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('LOK_worksData.RData')

This is a document detailing analysis of Avatar: Legend of Korra Ao3 tag data, collected on the 10 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.

For The Dragon Prince (TDP) we saw a peak of activity after each season, but here it’s not quite the case. After season 1 there’s a peak, which lasts for almost a year, unlike the 2 months we saw in case of TDP. TDP is released via Netflix where the entire season is released all at once, while The Legend of Korra (LOK) has been steadily gaining momentum during its 2-month-long air period.

During and after season 2 release we observe a minor dip, but there’s a growth in the months after. While it was well critically acclaimed, season 2 was not as well recieved by the fandom, and it’s possible that after its conclusion many people took on writing their own personal interpretation.

Season 3 and 4, released in rapid succession fall onto that upward trend, building up to a massive peak which takes approximately 2 years to fall back to pre-season 3 levels.

At around 2017 the popularity steadies at the level of approximately season 1 peak, up until new recent upward growth. It’s hard to say for sure, but this new peak seems to fall approximately onto coronavirus lockdown and release of Avatar: the Last Airbender (ATLA) on US Netflix in May 2020.

Again, some works have been published even before the initial air date of season 1, which could possibly be attributed to ATLA popularity and trailer/Comic Cons hype.


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

seasonsStart <- c("2012-04-14", "2013-09-13", "2014-06-27", "2014-10-03")
seasonsStart <- as.Date(seasonsStart)
seasonsEnd <- c("2012-06-23", "2013-11-22", "2014-08-22", "2014-12-19")
seasonsEnd <- as.Date(seasonsEnd)

plotDatesDensityTotal <- ggplot(stats, aes(x = Date)) + 
                    geom_density(alpha = 0.1)+
                    geom_vline(xintercept=seasonsStart)+
                    geom_vline(xintercept=seasonsEnd, linetype ="longdash")+
                    scale_x_date(date_breaks="6 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)

I collect data from the Ao3 search page (rather than works pages, as it’s less disruptive to site’s function), so I don’t have access to initial postage dates, only the latest updates. This means that the upward trend in works over time can be an artifact of series getting more popular, but also could be attributed to multichapter works drifting further in time due to updates.

If we plot Complete Works and Works in Progress separately, we still observe an overall upward trend, but due to the structure of release dates it’s a bit difficult to draw any conclusions about multichapter drift. Interestingly, if we focus on the works in progress, we see that the current peak seems to start earlier, which may be attributed to 10 year anniversary of ATLA Bluray release in June 2018.


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="6 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"),
          get_legend(kudosPercentiles +
                     theme(legend.title=element_blank())))


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 8129 works tagged with a single category, and 1505 tagged with 2 or more (up until all 6).

‘F/F’ is the most popular category, followed by ‘F/M’, ‘Gen’, and ‘M/M’.

Multiple category fics strongly contribute towards ‘F/M’ count, then to ‘F/F’, ‘Gen’, and ‘M/M’, 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.

“Multi” seems to have most words, despite being a rather small 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.

Overall, “F/F” category works collect twice as many hits, kudos, and bookmarks as the next category, and more than thrice as many comments as “M/M” works.


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

Out of the 3 main shipping categories, in absolute numbers “F/F” has most E rated works, and “M/M” has the least. However, in relative amounts “M/M” category has more explicit works (26%) than either “F/F” or “F/M”. Overall, the distributions of ratings between the categories are much more balanced than in TDP, perhaps, reflecting a variety of highly plot relevant older female characters in the cast.


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

Seasons 3 and 4 of LOK brought a huge rise in ‘F/F’ category popularity. There’s a smaller peak around summer 2017 which may be due to release of “The Legend of Korra: Turf Wars – Part One” in July 2017.


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="6 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

Seasons 3 and 4 brought a peak of popularity to “Korra/Asami Sato”, but also coincided with the peaks for most of the other top ships, excluding perhaps only “Korra/Mako (Avatar)”, which experienced peaks around season 1 and 3. It’s perhaps worth noting “Lin Beifong/Kya II” experiencing a slightly later peak after seasons 3-4, and then another one starting around summer 2017, which is likely due to “The Legend of Korra: Turf Wars – Part One” reveal of Kya II as sapphic.


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="6 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 1901, but number of works tagged with more than 2 fandoms is 499 which seems to be due to works often being tagged with both “Avatar: Legend of Korra” and “Avatar: The Last Airbender”.

Number of works explicitly tagged as ‘crossover’ is just 246.

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
orphan_account 213
allywonderland 114
spockandawe 99
Writerleft 94
TurboNerdQueen 88
ObjectiveMistress 71
YennaWang 64
RaeDMagdon 55
Vampiric_Charms 55
iviscrit 54
Savorysavery 54
Cybercitizen 52
ItsaVikingThing 49
notgeorgelucas 49
Lion01 48
DraceDomino 44
ragnarok89 44
LarirenShadow 42
Nightworldlove 42
Angel_of_the_Starz 40
braigwen_s 40
Swani 39
slacktension 35
LissaBear 34
LizBee 34
AvatarAang7 33
gillywulf 33
TheWillowTree 33
kittymannequin 32
Carliro 29


rm(AuthorTable)

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

Authors by Words

Only 137 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
OurImpavidHeroine 1423345
Raven_Hallowryn 1230634
orphan_account 1152506
laurrayn 942054
WestOrEast 782403
RaeDMagdon 742536
duvarneya 705828
99nzhe 703828
sanctum_c 688005
Puffie 686658
YennaWang 672519
DimensionalLover 661214
kittymannequin 642239
AvatarAang7 636877
Apnsb 623536
Lion01 598358
Revans_Mask 564095
commandmetobewell 556755
MrMander 553751
DraceDomino 542456
SkyLynnx 531015
Destiny_Smasher 530580
Cuofeng 518607
AutyRose 507216
Writerleft 503703
chaisan 494807
Angel_of_the_Starz 485085
chelonianmobile 484384
MultiFanGirlWickedPony 484384
Writearoundchic 484384


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
Korra (Avatar) 5736
Asami Sato 4973
Mako (Avatar) 2544
Bolin (Avatar) 2302
Tenzin (Avatar) 1274
Lin Beifong 1206
Opal (Avatar) 942
Kuvira (Avatar) 904
Jinora (Avatar) 733
Katara (Avatar) 698
Kya II (Avatar) 648
Korra 624
Suyin Beifong 593
Pema (Avatar) 494
Aang (Avatar) 465
Zuko (Avatar) 458
Tonraq (Avatar) 409
Lin Bei Fong 408
Hiroshi Sato 393
Toph Beifong 393
Ikki (Avatar) 371
Senna (Avatar) 363
Original Characters 351
Amon (Avatar) 343
Sokka (Avatar) 329
Iroh II (Avatar) 322
Baatar Jr. (Avatar) 321
Bumi II (Avatar) 317
Varrick (Avatar) 305
Meelo (Avatar) 304


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
Korra/Asami Sato 4393
Korra/Mako (Avatar) 606
Bolin/Opal (Avatar) 486
Lin Beifong/Kya II 311
Baatar Jr./Kuvira (Avatar) 222
Aang/Katara (Avatar) 209
Korrasami 185
Jinora/Kai (Avatar) 174
Pema/Tenzin (Avatar) 168
Mako/Asami Sato 156
Lin Beifong/Tenzin 145
Mako/Prince Wu (Avatar) 142
Korra & Asami Sato 139
Bolin/Korra (Avatar) 138
Amon/Lieutenant (Avatar) 96
Korra/Kuvira (Avatar) 91
Mai/Zuko (Avatar) 86
Katara/Zuko (Avatar) 82
Amon/Korra (Avatar) 81
Varrick/Zhu Li Moon 81
Korra/Kuvira 75
Senna/Tonraq (Avatar) 75
Korra/Tahno (Avatar) 73
Bolin & Mako (Avatar) 70
Toph Beifong/Sokka 65
Lin Bei Fong/Tenzin 62
Sokka/Suki (Avatar) 61
Suyin Beifong/Kuvira 55
Baatar Sr./Suyin Beifong 53
Korra/Asami 52


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
Fluff 1229
Romance 803
Angst 703
Alternate Universe - Modern Setting 610
Korrasami - Freeform 430
Alternate Universe 389
Smut 389
Friendship 341
Canon Compliant 329
Hurt/Comfort 329
Humor 307
Alternate Universe - Canon Divergence 289
Family 288
Post-Canon 242
Established Relationship 226
One Shot 219
Oral Sex 194
Drama 191
Crossover 179
Slow Burn 178
Drabble 177
Fluff and Angst 173
Alternate Universe - College/University 170
AU 160
Friends to Lovers 153
Alpha/Beta/Omega Dynamics 151
Femslash 150
Love 148
Cross-Posted on FanFiction.Net 137
Emotional Hurt/Comfort 133


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 9445
Español 74
Français 71
<U+0420><U+0443><U+0441><U+0441><U+043A><U+0438><U+0439> 17
<U+4E2D><U+6587> 8
Português brasileiro 7
Italiano 6
Deutsch 5
Polski 1


#languagesList

#rm(LanguageTable)
LS0tDQp0aXRsZTogIkFvMyBkYXRhIGFuYWx5c2lzIGZvciBBdmF0YXI6IExlZ2VuZCBvZiBLb3JyYSINCmF1dGhvcjogImRhcnRoYWxpbmUiDQpkYXRlOiAiMTAgQXVnIDIwMjAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgY29kZV9mb2xkaW5nOiAiaGlkZSINCiAgICB0b2M6IHRydWUNCi0tLQ0KDQojIEFib3V0DQoNCmBgYHtyIHNldHVwLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNClN5cy5zZXRlbnYoTEFORyA9ICJlbiIpDQojbGlicmFyeSgicnN0dWRpb2FwaSIpICN0byBncmFiIGxvY2FsIHBvc2l0aW9uIG9mIHRoZSBzY3JpcHQNCiNzZXR3ZChkaXJuYW1lKHJzdHVkaW9hcGk6OmdldEFjdGl2ZURvY3VtZW50Q29udGV4dCgpJHBhdGgpKQ0Ka25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAnLicpDQoNCiNsaWJyYXJ5KCJydmVzdCIpICMgdG8gaGFuZGxlIGh0bWwgc3R1ZmYNCg0KbGlicmFyeShsdWJyaWRhdGUpICMgdG8gaGFuZGxlIGRhdGVzDQoNCmxpYnJhcnkoZ2dwbG90MikgIyBmb3IgcGxvdHRpbmcNCmxpYnJhcnkoY293cGxvdCkgIyBmb3IgcGxvdHRpbmcNCmxpYnJhcnkoUkNvbG9yQnJld2VyKSAjIGZvciBjaG9vc2luZyBjb2xvcnMNCg0KY3VzdG9tcGFsZXR0ZSA8LSBicmV3ZXIucGFsKG49OCwgbmFtZSA9ICdEYXJrMicpDQoNCmxpYnJhcnkoa25pdHIpICMgZm9yIHRhYmxlcw0KbGlicmFyeShrYWJsZUV4dHJhKSAjIGZvciB0YWJsZXMNCg0KbGlicmFyeShsdWJyaWRhdGUpICMgZm9yIGRhdGVzDQoNCmxpYnJhcnkocGx5cikgIyBkZHBseSwgdG8gc3VtbWFyaXplIG51bWJlciBvZiB3b3JkcyBieSBhdXRob3INCg0KbG9hZCgnTE9LX3dvcmtzRGF0YS5SRGF0YScpDQoNCmBgYA0KDQpUaGlzIGlzIGEgZG9jdW1lbnQgZGV0YWlsaW5nIGFuYWx5c2lzIG9mIFtgciB0YWdWYWx1ZWAgQW8zIHRhZ10oaHR0cHM6Ly9hcmNoaXZlb2ZvdXJvd24ub3JnL3RhZ3MvQXZhdGFyOiUyMExlZ2VuZCUyMG9mJTIwS29ycmEvd29ya3MpIGRhdGEsIGNvbGxlY3RlZCBvbiB0aGUgMTAgQXVnIDIwMjAuIEkgaGF2ZW4ndCBmaWd1cmVkIG91dCBhIHdheSB0byBnZXQgbXkgc2NyYXBwZXIgdG8gbG9nIGluIGludG8gQW8zICh5ZXQ/IHJ2ZXN0IHNlZW1zIHRvIGhhdmUgc29tZSB0cm91YmxlIHdpdGggcGFnZSByZWRpcmVjdHMpLCBzbyByZXN1bHRzIGhlcmUgYXJlIGJhc2VkIG9uIHRoZSB3b3JrcyB2aXNpYmxlIHdpdGhvdXQgYXV0aGVudGljYXRpb24sIHdoaWNoIGxpa2VseSBmaWx0ZXJzIG91dCBwcmVmZXJlbnRpYWxseSBleHBsaWNpdC9wcm9ibGVtYW50aWMgd29ya3MgZnJvbSB0aGUgc2VsZWN0aW9uLg0KDQpgYGB7ciBwbG90dGluZ0Z1bmN0aW9ucywgY29sbGFwc2U9VFJVRSwgd2FybmluZz1GQUxTRX0NCg0KcGxvdF9iYXIgPC0gZnVuY3Rpb24gKGRhdGEsIGNvbHVtblgsIGxlZ2VuZFBvc2l0aW9uKSB7DQogICAgZ2dwbG90KGRhdGEsIGFlc19zdHJpbmcoeCA9IGNvbHVtblgpKSArIA0KICAgIGdlb21fYmFyKGFscGhhPTEpKw0KICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAxLCBoanVzdD0xKSkrDQogICAgbGFicyh5PSJOdW1iZXIgb2Ygd29ya3MiKQ0KfQ0KDQpwbG90X2Jhcl9jb2xvciA8LSBmdW5jdGlvbiAoZGF0YSwgY29sdW1uWCwgY29sQ29sb3IsIGxlZ2VuZFBvc2l0aW9uKSB7DQogICAgZ2dwbG90KGRhdGEsIGFlc19zdHJpbmcoeCA9IGNvbHVtblgsIGZpbGw9Y29sQ29sb3IpKSArIA0KICAgIGdlb21fYmFyKGFscGhhPTAuNykrDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tcGFsZXR0ZSkgKw0KICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAxLCBoanVzdD0xKSkrDQogICAgbGFicyh5PSJOdW1iZXIgb2Ygd29ya3MiKQ0KfQ0KDQpwbG90X2NvbCA8LSBmdW5jdGlvbiAoZGF0YSwgY29sdW1uWCwgY29sdW1uWSwgbGVnZW5kUG9zaXRpb24pIHsNCiAgICBnZ3Bsb3QoZGF0YSwgYWVzX3N0cmluZyh4ID0gY29sdW1uWCwgeSA9IGNvbHVtblkpKSArIA0KICAgIGdlb21fY29sKGFscGhhPTEpKw0KICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAxLCBoanVzdD0xKSkrDQogICAgbGFicyh5PWdzdWIoJ1xcLicsICcgJywgY29sdW1uWSkpDQogIA0KfQ0KDQpwbG90X2NvbF9jb2xvciA8LSBmdW5jdGlvbiAoZGF0YSwgY29sdW1uWCwgY29sdW1uWSwgY29sQ29sb3IsIGxlZ2VuZFBvc2l0aW9uKSB7DQogICAgZ2dwbG90KGRhdGEsIGFlc19zdHJpbmcoeCA9IGNvbHVtblgsIHkgPSBjb2x1bW5ZLCBmaWxsPWNvbENvbG9yKSkgKyANCiAgICBnZW9tX2NvbChhbHBoYT0wLjcpKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbXBhbGV0dGUpICsNCiAgICB0aGVtZV9oYWxmX29wZW4oKSArDQogICAgYmFja2dyb3VuZF9ncmlkKCkgKw0KICAgIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMSwgaGp1c3Q9MSkpKw0KICAgIGxhYnMoeT1nc3ViKCdcXC4nLCAnICcsIGNvbHVtblkpKQ0KICANCn0NCg0KcGxvdF9wZXJjZW50aWxlcyA8LSBmdW5jdGlvbiAoZGF0YSwgY29sdW1uWCwgY29sdW1uWSwgbGVnZW5kUG9zaXRpb24pIHsNCiAgICBnZ3Bsb3QoZGF0YSwgYWVzX3N0cmluZyh4ID0gY29sdW1uWCwgeSA9IGNvbHVtblkpKSArIA0KICAgIGdlb21fcG9pbnQoYWxwaGE9MC4zKSsNCiAgICBzY2FsZV95X2xvZzEwKGJyZWFrcyA9IDEwXmMoMDoxNSkpKw0KICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKDAsIDI1LCA1MCwgNzUsIDEwMCkpKyAjc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoMDoxMCkqMTApKw0KICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSkrDQogICAgbGFicyh4PWdzdWIoJ1xcLicsICcgJywgY29sdW1uWCkpDQp9DQoNCmBgYA0KDQpgYGB7ciBmbGF0dGVuaW5nRGF0YSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI3RpdGxlIDwtIGxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JFRpdGxlfSkNCmF1dGhvciA8LSBsYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRBdXRob3J9KQ0KZmFuZG9tIDwtIGxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JEZhbmRvbX0pDQpyYXRpbmcgPC0gbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkUmF0aW5nfSkNCndhcm5pbmdzIDwtIGxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JFdhcm5pbmdzfSkNCmNhdGVnb3J5IDwtIGxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JENhdGVnb3J5fSkNCldJUCA8LSBsYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRXSVB9KQ0KZGF0ZSA8LWxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JERhdGV9KQ0KcmVsYXRpb25zaGlwcyA8LWxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JFJlbGF0aW9uc2hpcHN9KQ0KY2hhcmFjdGVyIDwtbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkQ2hhcmFjdGVyfSkNCmZyZWVmb3JtIDwtbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkRnJlZWZvcm19KQ0KbGFuZ3VhZ2UgPC1sYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRMYW5ndWFnZX0pDQp3b3JkcyA8LWxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JFdvcmRzfSkNCndvcmRzW2lzLm5hKHdvcmRzKV0gPC0gMA0Ka3Vkb3MgPC1sYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRLdWRvc30pDQprdWRvc1tpcy5uYShrdWRvcyldIDwtIDANCmNvbW1lbnRzIDwtbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkQ29tbWVudHN9KQ0KY29tbWVudHNbaXMubmEoY29tbWVudHMpXSA8LSAwDQpib29rbWFya3M8LWxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JEJvb2ttYXJrc30pDQpib29rbWFya3NbaXMubmEoYm9va21hcmtzKV0gPC0gMA0KaGl0cyA8LWxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JEhpdHN9KQ0KaGl0c1tpcy5uYShoaXRzKV0gPC0gMA0KDQpzdGF0cyA8LSBkYXRhLmZyYW1lKFdvcmRzID0gdW5saXN0KHdvcmRzLCByZWN1cnNpdmUgPSBGQUxTRSksDQogICAgICAgICAgICAgICAgICAgIENvbW1lbnRzPSBhcy5udW1lcmljKGFzLmNoYXJhY3Rlcihjb21tZW50cykpLA0KICAgICAgICAgICAgICAgICAgICBLdWRvcyA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGt1ZG9zKSksDQogICAgICAgICAgICAgICAgICAgIEJvb2ttYXJrcyA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGJvb2ttYXJrcykpLA0KICAgICAgICAgICAgICAgICAgICBIaXRzID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoaGl0cykpLA0KICAgICAgICAgICAgICAgICAgICBXSVAgPSB1bmxpc3QoV0lQLCByZWN1cnNpdmUgPSBGQUxTRSksDQogICAgICAgICAgICAgICAgICAgIFJhdGluZyA9IHVubGlzdChyYXRpbmcsIHJlY3Vyc2l2ZSA9IEZBTFNFKSwNCiAgICAgICAgICAgICAgICAgICAgRGF0ZSA9IGRvLmNhbGwoImMiLCBkYXRlKSkNCg0Kc3RhdHMkUmF0aW5nIDwtIGZhY3RvcihzdGF0cyRSYXRpbmcsIGxldmVscyA9IGMoIk5vdCBSYXRlZCIsICJHZW5lcmFsIEF1ZGllbmNlcyIsICJUZWVuIEFuZCBVcCBBdWRpZW5jZXMiLCAiTWF0dXJlIiwgIkV4cGxpY2l0IikpDQoNCnRvdGFsIDwtIDEwMDANCnBlcmNlbnRpbGUgPC0gYygxOnRvdGFsKQ0KcGVyY2VudGlsZURhdGEgPC0gZGF0YS5mcmFtZShXb3Jrcy5QZXJjZW50aWxlID0gMTAwKih0b3RhbCAtIHBlcmNlbnRpbGUpL3RvdGFsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXb3JkcyA9IHVubGlzdChsYXBwbHkocGVyY2VudGlsZS90b3RhbCwgcXVhbnRpbGUsIHggPSB1bmxpc3Qod29yZHMpICkpICsgMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSGl0cyA9IHVubGlzdChsYXBwbHkocGVyY2VudGlsZS90b3RhbCwgcXVhbnRpbGUsIHggPSB1bmxpc3QoaGl0cykgKSkgKyAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBLdWRvcyA9IHVubGlzdChsYXBwbHkocGVyY2VudGlsZS90b3RhbCwgcXVhbnRpbGUsIHggPSB1bmxpc3Qoa3Vkb3MpICkpICsgMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29tbWVudHMgPSB1bmxpc3QobGFwcGx5KHBlcmNlbnRpbGUvdG90YWwsIHF1YW50aWxlLCB4ID0gdW5saXN0KGNvbW1lbnRzKSApKSArIDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJvb2ttYXJrcyA9IHVubGlzdChsYXBwbHkocGVyY2VudGlsZS90b3RhbCwgcXVhbnRpbGUsIHggPSB1bmxpc3QoYm9va21hcmtzKSApKSArIDEgKQ0KDQpybShyYXRpbmcsIGt1ZG9zLCBjb21tZW50cywgYm9va21hcmtzLCBoaXRzKQ0KDQpgYGANCg0KIyBUaW1lbGluZQ0KDQpTb2xpZCB2ZXJ0aWNhbCBsaW5lcyBvbiB0aGUgZ3JhcGggaW5kaWNhdGUgaW5pdGlhbCBhaXIgZGF0ZXMsIGFuZCBkYXNoZWQgaW5kaWNhdGUgZmluYWwgYWlyIGRhdGVzLCBhY2NvcmRpbmcgdG8gW1dpa2kgYXJ0aWNsZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVGhlX0xlZ2VuZF9vZl9Lb3JyYSNTZXJpZXNfb3ZlcnZpZXcpLg0KDQpGb3IgVGhlIERyYWdvbiBQcmluY2UgKFREUCkgd2Ugc2F3IGEgcGVhayBvZiBhY3Rpdml0eSBhZnRlciBlYWNoIHNlYXNvbiwgYnV0IGhlcmUgaXQncyBub3QgcXVpdGUgdGhlIGNhc2UuIEFmdGVyIHNlYXNvbiAxIHRoZXJlJ3MgYSBwZWFrLCB3aGljaCBsYXN0cyBmb3IgYWxtb3N0IGEgeWVhciwgdW5saWtlIHRoZSAyIG1vbnRocyB3ZSBzYXcgaW4gY2FzZSBvZiBURFAuIFREUCBpcyByZWxlYXNlZCB2aWEgTmV0ZmxpeCB3aGVyZSB0aGUgZW50aXJlIHNlYXNvbiBpcyByZWxlYXNlZCBhbGwgYXQgb25jZSwgd2hpbGUgVGhlIExlZ2VuZCBvZiBLb3JyYSAoTE9LKSBoYXMgYmVlbiBzdGVhZGlseSBnYWluaW5nIG1vbWVudHVtIGR1cmluZyBpdHMgMi1tb250aC1sb25nIGFpciBwZXJpb2QuIA0KDQpEdXJpbmcgYW5kIGFmdGVyIHNlYXNvbiAyIHJlbGVhc2Ugd2Ugb2JzZXJ2ZSBhIG1pbm9yIGRpcCwgYnV0IHRoZXJlJ3MgYSBncm93dGggaW4gdGhlIG1vbnRocyBhZnRlci4gV2hpbGUgaXQgd2FzIHdlbGwgY3JpdGljYWxseSBhY2NsYWltZWQsIHNlYXNvbiAyIHdhcyBub3QgYXMgd2VsbCByZWNpZXZlZCBieSB0aGUgZmFuZG9tLCBhbmQgaXQncyBwb3NzaWJsZSB0aGF0IGFmdGVyIGl0cyBjb25jbHVzaW9uIG1hbnkgcGVvcGxlIHRvb2sgb24gd3JpdGluZyB0aGVpciBvd24gcGVyc29uYWwgaW50ZXJwcmV0YXRpb24uDQoNClNlYXNvbiAzIGFuZCA0LCByZWxlYXNlZCBpbiByYXBpZCBzdWNjZXNzaW9uIGZhbGwgb250byB0aGF0IHVwd2FyZCB0cmVuZCwgYnVpbGRpbmcgdXAgdG8gYSBtYXNzaXZlIHBlYWsgd2hpY2ggdGFrZXMgYXBwcm94aW1hdGVseSAyIHllYXJzIHRvIGZhbGwgYmFjayB0byBwcmUtc2Vhc29uIDMgbGV2ZWxzLg0KDQpBdCBhcm91bmQgMjAxNyB0aGUgcG9wdWxhcml0eSBzdGVhZGllcyBhdCB0aGUgbGV2ZWwgb2YgYXBwcm94aW1hdGVseSBzZWFzb24gMSBwZWFrLCB1cCB1bnRpbCBuZXcgcmVjZW50IHVwd2FyZCBncm93dGguIEl0J3MgaGFyZCB0byBzYXkgZm9yIHN1cmUsIGJ1dCB0aGlzIG5ldyBwZWFrIHNlZW1zIHRvIGZhbGwgYXBwcm94aW1hdGVseSBvbnRvIGNvcm9uYXZpcnVzIGxvY2tkb3duIGFuZCByZWxlYXNlIG9mIEF2YXRhcjogdGhlIExhc3QgQWlyYmVuZGVyIChBVExBKSBvbiBVUyBOZXRmbGl4IGluIE1heSAyMDIwLg0KDQpBZ2Fpbiwgc29tZSB3b3JrcyBoYXZlIGJlZW4gcHVibGlzaGVkIGV2ZW4gYmVmb3JlIHRoZSBpbml0aWFsIGFpciBkYXRlIG9mIHNlYXNvbiAxLCB3aGljaCBjb3VsZCBwb3NzaWJseSBiZSBhdHRyaWJ1dGVkIHRvIEFUTEEgcG9wdWxhcml0eSBhbmQgdHJhaWxlci9Db21pYyBDb25zIGh5cGUuDQoNCmBgYHtyIHRpbWVsaW5lVG90YWwsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTZ9DQoNCiNkYXRhJFRpbWVzdGFtcCA8LSBwYXJzZV9kYXRlX3RpbWUyKGFzLmNoYXJhY3RlcihkYXRhJFRpbWVzdGFtcCksIG9yZGVycyA9ICIlZC8lbS8lWSAlSDolTTolUyIpDQojZGF0YSRkYXkgPC0gYXMuRGF0ZShkYXRhJFRpbWVzdGFtcCkNCg0Kc2Vhc29uc1N0YXJ0IDwtIGMoIjIwMTItMDQtMTQiLCAiMjAxMy0wOS0xMyIsICIyMDE0LTA2LTI3IiwgIjIwMTQtMTAtMDMiKQ0Kc2Vhc29uc1N0YXJ0IDwtIGFzLkRhdGUoc2Vhc29uc1N0YXJ0KQ0Kc2Vhc29uc0VuZCA8LSBjKCIyMDEyLTA2LTIzIiwgIjIwMTMtMTEtMjIiLCAiMjAxNC0wOC0yMiIsICIyMDE0LTEyLTE5IikNCnNlYXNvbnNFbmQgPC0gYXMuRGF0ZShzZWFzb25zRW5kKQ0KDQpwbG90RGF0ZXNEZW5zaXR5VG90YWwgPC0gZ2dwbG90KHN0YXRzLCBhZXMoeCA9IERhdGUpKSArIA0KICAgICAgICAgICAgICAgICAgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjEpKw0KICAgICAgICAgICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9c2Vhc29uc1N0YXJ0KSsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PXNlYXNvbnNFbmQsIGxpbmV0eXBlID0ibG9uZ2Rhc2giKSsNCiAgICAgICAgICAgICAgICAgICAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzPSI2IG1vbnRocyIpKw0KICAgICAgICAgICAgICAgICAgICB0aGVtZV9oYWxmX29wZW4oKSArDQogICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmRfZ3JpZCgpICsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAncmlnaHQnKQ0KcGxvdERhdGVzRGVuc2l0eVRvdGFsDQoNCnJtKHBsb3REYXRlc0RlbnNpdHlUb3RhbCkNCmBgYA0KDQpJIGNvbGxlY3QgZGF0YSBmcm9tIHRoZSBBbzMgc2VhcmNoIHBhZ2UgKHJhdGhlciB0aGFuIHdvcmtzIHBhZ2VzLCBhcyBpdCdzIGxlc3MgZGlzcnVwdGl2ZSB0byBzaXRlJ3MgZnVuY3Rpb24pLCBzbyBJIGRvbid0IGhhdmUgYWNjZXNzIHRvIGluaXRpYWwgcG9zdGFnZSBkYXRlcywgb25seSB0aGUgbGF0ZXN0IHVwZGF0ZXMuIFRoaXMgbWVhbnMgdGhhdCB0aGUgdXB3YXJkIHRyZW5kIGluIHdvcmtzIG92ZXIgdGltZSBjYW4gYmUgYW4gYXJ0aWZhY3Qgb2Ygc2VyaWVzIGdldHRpbmcgbW9yZSBwb3B1bGFyLCBidXQgYWxzbyBjb3VsZCBiZSBhdHRyaWJ1dGVkIHRvIG11bHRpY2hhcHRlciB3b3JrcyBkcmlmdGluZyBmdXJ0aGVyIGluIHRpbWUgZHVlIHRvIHVwZGF0ZXMuDQoNCklmIHdlIHBsb3QgQ29tcGxldGUgV29ya3MgYW5kIFdvcmtzIGluIFByb2dyZXNzIHNlcGFyYXRlbHksIHdlIHN0aWxsIG9ic2VydmUgYW4gb3ZlcmFsbCB1cHdhcmQgdHJlbmQsIGJ1dCBkdWUgdG8gdGhlIHN0cnVjdHVyZSBvZiByZWxlYXNlIGRhdGVzIGl0J3MgYSBiaXQgZGlmZmljdWx0IHRvIGRyYXcgYW55IGNvbmNsdXNpb25zIGFib3V0IG11bHRpY2hhcHRlciBkcmlmdC4gSW50ZXJlc3RpbmdseSwgaWYgd2UgZm9jdXMgb24gdGhlIHdvcmtzIGluIHByb2dyZXNzLCB3ZSBzZWUgdGhhdCB0aGUgY3VycmVudCBwZWFrIHNlZW1zIHRvIHN0YXJ0IGVhcmxpZXIsIHdoaWNoIG1heSBiZSBhdHRyaWJ1dGVkIHRvIDEwIHllYXIgYW5uaXZlcnNhcnkgb2YgQVRMQSBCbHVyYXkgcmVsZWFzZSBpbiBKdW5lIDIwMTguDQoNCmBgYHtyIHRpbWVsaW5lV0lQLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQ0KDQpwbG90RGF0ZXNEZW5zaXR5IDwtIGdncGxvdChzdGF0cywgYWVzKHggPSBEYXRlLCBjb2w9V0lQKSkgKyANCiAgICAgICAgICAgICAgICAgICAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4xKSsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PXNlYXNvbnNTdGFydCkrDQogICAgICAgICAgICAgICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1zZWFzb25zRW5kLCBsaW5ldHlwZSA9ImxvbmdkYXNoIikrDQogICAgICAgICAgICAgICAgICAgIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcz0iNiBtb250aHMiKSsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICAgICAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgICAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gJ3JpZ2h0JykNCnBsb3REYXRlc0RlbnNpdHkNCg0Kcm0ocGxvdERhdGVzRGVuc2l0eSkNCg0KYGBgDQoNCg0KIyBFbmdhZ2VtZW50IHBlcmNlbnRpbGVzDQoNClNtYWxsIHBsb3R0aW5nIGNoZWF0OiBhbGwgdGhlIG51bWJlcnMgb24gdGhlIFkgYXhpcyBhcmUgaW5jcmVhc2VkIGJ5IDEgdG8gaW5jbHVkZSB0aGUgY2FzZSBvZiAwIGludG8gdGhlIHBsb3QgKG90aGVyd2lzZSBleGNsdWRlZCBiZWNhdXNlIG9mIGxvZyBzY2FsZSkuDQoNCiogQWJvdXQgNzUlIG9mIHdvcmtzIGhhdmUgbW9yZSB0aGFuIGEgMTAwMCB3b3JkcywgYnV0IG9ubHkgYWJvdXQgMTUlIGhhdmUgbW9yZSB0aGFuIDEwMDAwIHdvcmRzLg0KKiBPbmx5IGFib3V0IDUwJSBvZiB3b3JrcyBoYXZlIG92ZXIgYSAxMDAwIGhpdHMuDQoqIE9ubHkgYWJvdXQgMjUlIG9mIHdvcmtzIGhhdmUgbW9yZSB0aGFuIGEgMTAwIGt1ZG9zLg0KKiBPbmx5IGFib3V0IDM1JSBvZiB3b3JrcyBnZXQgbW9yZSB0aGFuIDEwIGNvbW1lbnRzLg0KKiBBcHByb3hpbWF0ZWx5IDEyJSBvZiB3b3JrcyBoYXZlIG5vIGNvbW1lbnRzICh0YWlsIGVuZCkuDQoqIE9ubHkgYXBwcm94aW1hdGVseSAzNSUgb2Ygd29ya3MgZ2V0IG1vcmUgdGhhbiAxMCBib29rbWFya3MuDQoqIEFwcHJveGltYXRlbHkgMTUlIG9mIHdvcmtzIGhhdmUgbm8gYm9va21hcmtzICh0YWlsIGVuZCkuDQoNCmBgYHtyIHBlcmNlbnRpbGVzLCBtZXNzYWdlID0gRkFMU0V9DQp3b3Jkc1BlcmNlbnRpbGVzIDwtIHBsb3RfcGVyY2VudGlsZXMocGVyY2VudGlsZURhdGEsICdXb3Jrcy5QZXJjZW50aWxlJywgJ1dvcmRzJywgJ3JpZ2h0JykNCmhpdHNQZXJjZW50aWxlcyA8LSBwbG90X3BlcmNlbnRpbGVzKHBlcmNlbnRpbGVEYXRhLCAnV29ya3MuUGVyY2VudGlsZScsICdIaXRzJywgJ3JpZ2h0JykNCmt1ZG9zUGVyY2VudGlsZXMgPC0gcGxvdF9wZXJjZW50aWxlcyhwZXJjZW50aWxlRGF0YSwgJ1dvcmtzLlBlcmNlbnRpbGUnLCAnS3Vkb3MnLCAncmlnaHQnKQ0KY29tbWVudHNQZXJjZW50aWxlcyA8LSBwbG90X3BlcmNlbnRpbGVzKHBlcmNlbnRpbGVEYXRhLCAnV29ya3MuUGVyY2VudGlsZScsICdDb21tZW50cycsICdyaWdodCcpDQpib29rbWFya3NQZXJjZW50aWxlcyA8LSBwbG90X3BlcmNlbnRpbGVzKHBlcmNlbnRpbGVEYXRhLCAnV29ya3MuUGVyY2VudGlsZScsICdCb29rbWFya3MnLCAncmlnaHQnKQ0KDQpwbG90X2dyaWQod29yZHNQZXJjZW50aWxlcyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgIGhpdHNQZXJjZW50aWxlcyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgIGt1ZG9zUGVyY2VudGlsZXMgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICBjb21tZW50c1BlcmNlbnRpbGVzICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgYm9va21hcmtzUGVyY2VudGlsZXMgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICBnZXRfbGVnZW5kKGt1ZG9zUGVyY2VudGlsZXMgKw0KICAgICAgICAgICAgICAgICAgICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSkpKQ0KDQpybSh0b3RhbCwgcGVyY2VudGlsZSwgcGVyY2VudGlsZURhdGEsIHdvcmRzUGVyY2VudGlsZXMsIGhpdHNQZXJjZW50aWxlcywga3Vkb3NQZXJjZW50aWxlcywgY29tbWVudHNQZXJjZW50aWxlcywgYm9va21hcmtzUGVyY2VudGlsZXMpDQoNCmBgYA0KDQojIENvbXBsZXRlIFdvcmsgdnMgV29yayBpbiBQcm9ncmVzcyBkaXN0cmlidXRpb25zDQoNCiogVGhlcmUgYXJlIGFwcHJveGltYXRlbHkgMyB0aW1lcyBhcyBtYW55IENvbXBsZXRlIFdvcmtzIGFzIHRoZXJlIGFyZSBXb3JrcyBpbiBQcm9ncmVzcy4NCiogV29ya3MgaW4gUHJvZ3Jlc3MgYXJlIGFwcHJveGltYXRlbHkgMyB0aW1lcyBsb25nZXIgdGhhbiBDb21wbGV0ZSBvbmVzLg0KKiBCb3RoIENvbXBsZXRlIFdvcmtzIGFuZCBXb3JrcyBpbiBQcm9ncmVzcyBmb3IgTE9LIGFyZSBsb25nZXIgdGhhbiB3b3JrcyBmb3IgVERQLg0KKiBXb3JrcyBpbiBQcm9ncmVzcyBnZXQgYXBwcm94aW1hdGVseSAzMCUgbW9yZSBoaXRzIHRoYW4gQ29tcGxldGUgb25lcy4NCiogQm90aCBDb21wbGV0ZSBXb3JrcyBhbmQgV29ya3MgaW4gUHJvZ3Jlc3MgZ2V0IGFwcHJveGltYXRlbHkgdGhlIHNhbWUgYW1vdW50cyBvZiBrdWRvcy4NCiogV29ya3MgaW4gUHJvZ3Jlc3MgZ2V0IGFwcHJveGltYXRlbHkgMiB0aW1lcyBhcyBtYW55IGNvbW1lbnRzIGFzIENvbXBsZXRlIG9uZXMgKGhvd2V2ZXIsIGFnYWluLCB0aGVyZSdzIG5vIHdheSB0byBmaWx0ZXIgb3V0IGF1dGhvcidzIGNvbW1lbnRzIGluIHRoZSBzZWFyY2ggc2VsZWN0aW9uKS4NCiogV29ya3MgaW4gUHJvZ3Jlc3MgZ2V0IHNsaWdodGx5IG1vcmUgYm9va21hcmtzIHRoYW4gQ29tcGxldGUgb25lcy4NCg0KYGBge3IgdG90YWxXb3Jrc1dJUCwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02fQ0KDQpzdGF0c1dJUCA8LSBzdGF0cw0Kc3RhdHNXSVAkRGl2aXNvciA8LSB1bmxpc3QobGFwcGx5KHN0YXRzV0lQJFdJUCwgZnVuY3Rpb24oeCkgc3VtbWFyeShzdGF0c1dJUCRXSVApW25hbWVzKHN1bW1hcnkoc3RhdHNXSVAkV0lQKSkgPT0geF0pKQ0Kc3RhdHNXSVAkV29yZHMucGVyLldvcmsgPC0gc3RhdHNXSVAkV29yZHMvc3RhdHNXSVAkRGl2aXNvcg0Kc3RhdHNXSVAkSGl0cy5wZXIuV29yayA8LSBzdGF0c1dJUCRIaXRzL3N0YXRzV0lQJERpdmlzb3INCnN0YXRzV0lQJEt1ZG9zLnBlci5Xb3JrIDwtIHN0YXRzV0lQJEt1ZG9zL3N0YXRzV0lQJERpdmlzb3INCnN0YXRzV0lQJENvbW1lbnRzLnBlci5Xb3JrIDwtIHN0YXRzV0lQJENvbW1lbnRzL3N0YXRzV0lQJERpdmlzb3INCnN0YXRzV0lQJEJvb2ttYXJrcy5wZXIuV29yayA8LSBzdGF0c1dJUCRCb29rbWFya3Mvc3RhdHNXSVAkRGl2aXNvcg0KDQpiYXJXb3Jrc1dJUCA8LSBwbG90X2JhcihzdGF0c1dJUCwgJ1dJUCcsICdyaWdodCcpDQpiYXJXb3Jkc1dJUCA8LSBwbG90X2NvbChzdGF0c1dJUCwgJ1dJUCcsICdXb3Jkcy5wZXIuV29yaycsICdyaWdodCcpDQpiYXJIaXRzV0lQIDwtIHBsb3RfY29sKHN0YXRzV0lQLCAnV0lQJywgJ0hpdHMucGVyLldvcmsnLCAncmlnaHQnKQ0KYmFyS3Vkb3NXSVAgPC0gcGxvdF9jb2woc3RhdHNXSVAsICdXSVAnLCAnS3Vkb3MucGVyLldvcmsnLCAncmlnaHQnKQ0KYmFyQ29tbWVudHNXSVAgPC0gcGxvdF9jb2woc3RhdHNXSVAsICdXSVAnLCAnQ29tbWVudHMucGVyLldvcmsnLCAncmlnaHQnKQ0KYmFyQm9va21hcmtzV0lQIDwtIHBsb3RfY29sKHN0YXRzV0lQLCAnV0lQJywgJ0Jvb2ttYXJrcy5wZXIuV29yaycsICdyaWdodCcpDQoNCiMgcGxvdF9ncmlkKHBsb3RfZ3JpZCggYmFyV29ya3NXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiMgICAgICAgICAgICAgICAgICAgICAgYmFyV29yZHNXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiMgICAgICAgICAgICAgICAgICAgICAgYmFySGl0c1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KIyAgICAgICAgICAgICAgICAgICAgICBiYXJLdWRvc1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KIyAgICAgICAgICAgICAgICAgICAgICBiYXJDb21tZW50c1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KIyAgICAgICAgICAgICAgICAgICAgICBiYXJCb29rbWFya3NXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiMgICAgICAgICAgICAgICAgICAgICAgYWxpZ24gPSAnaHYnKSwNCiMgICAgICAgICAgIGdldF9sZWdlbmQoYmFyV29ya3NXSVAgKyB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKSksDQojICAgICAgICAgICByZWxfd2lkdGhzID0gYyg0LDEpLA0KIyAgICAgICAgICAgYWxpZ24gPSAnaHYnKQ0KcGxvdF9ncmlkKCBiYXJXb3Jrc1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJXb3Jkc1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJIaXRzV0lQICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckt1ZG9zV0lQICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckNvbW1lbnRzV0lQICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckJvb2ttYXJrc1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBhbGlnbiA9ICdodicpDQoNCnJtKHN0YXRzV0lQLCBiYXJXb3Jrc1dJUCwgYmFyV29yZHNXSVAsIGJhckhpdHNXSVAsIGJhckt1ZG9zV0lQLCBiYXJDb21tZW50c1dJUCwgYmFyQm9va21hcmtzV0lQKQ0KYGBgDQoNCiMgUmF0aW5nIGRpc3RyaWJ1dGlvbnMNCg0KKiBXb3JrcyByYXRlZCBHIGFuZCBUIG1ha2UgdXAgdGhlIG1ham9yaXR5IG9mIHdvcmtzLCB3aXRoIE0gYW5kIEUgd29ya3Mgb25seSBtYWtpbmcgYXBwcm94aW1hdGVseSBhIHRoaXJkIG9mIHRob3NlIG51bWJlcnMuDQoqIFdvcmtzIHJhdGVkIEcgYXJlIG9uIGF2ZXJhZ2UgdGhlIHNob3J0ZXN0ICh+MjUwMCB3b3JkcyksIGZvbGxvd2VkIGJ5IE5vdCBSYXRlZCB3b3JrcyAoPjUwMDAgd29yZHMpLCBUICh+MTEwMDAgd29yZHMpLCBFICh+IDE1MDAwIHdvcmRzKSwgYW5kIE0gKH4yMjAwMCB3b3JkcykuIFRoZSB0cmVuZCBvZiBNIHJhdGVkIHdvcmtzIGJlaW5nIHRoZSBsb25nZXN0IHdlIG9ic2VydmVkIGluIFREUCBob2xkcyB1cCBoZXJlIGFzIHdlbGwuDQoqIE5vdCBSYXRlZCB3b3JrcyBhbmQgd29ya3MgcmF0ZWQgRyBnZXQgZmV3ZXN0IGhpdHMgKH4xNTAwKS4gTnVtYmVyIG9mIGhpdHMgcmlzZXMgd2l0aCB0aGUgcmF0aW5nLiBFIHJhdGVkIHdvcmtzIGFyZSBtb3N0IHBvcHVsYXIgKH44NTAwKS4NCiogTnVtYmVyIG9mIGt1ZG9zIGRvZXNuJ3QgcXVpdGUgc2NhbGUgdXAgd2l0aCB0aGUgbnVtYmVyIG9mIGhpdHMsIGJ1dCB1bmxpa2UgaW4gVERQIGNhc2UgRSByYXRlZCB3b3JrcyBhbHNvIGdldCB0aGUgbW9zdCBrdWRvcyAofjIyMCkuIFRoZSBkaWZmZXJlbmNlIGluIGRpc3RyaWJ1dGlvbiBtaWdodCBiZSBkdWUgdG8gdGhlIGZhY3QgdGhhdCBMT0sgZmFuZG9tIG9uIGF2ZXJhZ2Ugc2tld3Mgb2xkZXIuDQoqIFdvcmtzIHJhdGVkIE0gZ2V0IHRoZSBtb3N0IGNvbW1lbnRzICg+NTApIHdoaWNoIG1pZ2h0IGNvcnJlbGF0ZSB3aXRoIHRoZW0gYmVpbmcgdGhlIGxvbmdlc3QuDQoqIE51bWJlciBvZiBib29rbWFya3MgZG9lc24ndCBxdWl0ZSBzY2FsZSB1cCB3aXRoIHRoZSBudW1iZXIgb2YgaGl0cywgYWdhaW4sIGJ1dCBpdCBncm93cyB3aXRoIHRoZSByYXRpbmcsIHJlYWNoaW5nIG1heGltdW0gZm9yIEUgcmF0ZWQgd29ya3MgKH4yNSBib29rbWFya3MpLg0KDQoNCmBgYHtyIHRvdGFsV29ya3NSYXRpbmcsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0NCg0Kc3RhdHNSYXRpbmcgPC0gc3RhdHMNCnN0YXRzUmF0aW5nJERpdmlzb3IgPC0gdW5saXN0KGxhcHBseShzdGF0c1JhdGluZyRSYXRpbmcsIGZ1bmN0aW9uKHgpIHN1bW1hcnkoc3RhdHNSYXRpbmckUmF0aW5nKVtuYW1lcyhzdW1tYXJ5KHN0YXRzUmF0aW5nJFJhdGluZykpID09IHhdKSkNCnN0YXRzUmF0aW5nJFdvcmRzLnBlci5Xb3JrIDwtIHN0YXRzUmF0aW5nJFdvcmRzL3N0YXRzUmF0aW5nJERpdmlzb3INCnN0YXRzUmF0aW5nJEhpdHMucGVyLldvcmsgPC0gc3RhdHNSYXRpbmckSGl0cy9zdGF0c1JhdGluZyREaXZpc29yDQpzdGF0c1JhdGluZyRLdWRvcy5wZXIuV29yayA8LSBzdGF0c1JhdGluZyRLdWRvcy9zdGF0c1JhdGluZyREaXZpc29yDQpzdGF0c1JhdGluZyRDb21tZW50cy5wZXIuV29yayA8LSBzdGF0c1JhdGluZyRDb21tZW50cy9zdGF0c1JhdGluZyREaXZpc29yDQpzdGF0c1JhdGluZyRCb29rbWFya3MucGVyLldvcmsgPC0gc3RhdHNSYXRpbmckQm9va21hcmtzL3N0YXRzUmF0aW5nJERpdmlzb3INCg0KYmFyV29ya3NSYXRpbmcgPC0gcGxvdF9iYXIoc3RhdHNSYXRpbmcsICdSYXRpbmcnLCAncmlnaHQnKQ0KYmFyV29yZHNSYXRpbmcgPC0gcGxvdF9jb2woc3RhdHNSYXRpbmcsICdSYXRpbmcnLCAnV29yZHMucGVyLldvcmsnLCAncmlnaHQnKQ0KYmFySGl0c1JhdGluZyA8LSBwbG90X2NvbChzdGF0c1JhdGluZywgJ1JhdGluZycsICdIaXRzLnBlci5Xb3JrJywgJ3JpZ2h0JykNCmJhckt1ZG9zUmF0aW5nIDwtIHBsb3RfY29sKHN0YXRzUmF0aW5nLCAnUmF0aW5nJywgJ0t1ZG9zLnBlci5Xb3JrJywgJ3JpZ2h0JykNCmJhckNvbW1lbnRzUmF0aW5nIDwtIHBsb3RfY29sKHN0YXRzUmF0aW5nLCAnUmF0aW5nJywgJ0NvbW1lbnRzLnBlci5Xb3JrJywgJ3JpZ2h0JykNCmJhckJvb2ttYXJrc1JhdGluZyA8LSBwbG90X2NvbChzdGF0c1JhdGluZywgJ1JhdGluZycsICdCb29rbWFya3MucGVyLldvcmsnLCAncmlnaHQnKQ0KDQpwbG90X2dyaWQoIGJhcldvcmtzUmF0aW5nICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhcldvcmRzUmF0aW5nICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckhpdHNSYXRpbmcgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYmFyS3Vkb3NSYXRpbmcgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYmFyQ29tbWVudHNSYXRpbmcgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYmFyQm9va21hcmtzUmF0aW5nICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGFsaWduID0gJ2h2JykNCg0Kcm0oc3RhdHNSYXRpbmcsIGJhcldvcmtzUmF0aW5nLCBiYXJXb3Jkc1JhdGluZywgYmFySGl0c1JhdGluZywgYmFyS3Vkb3NSYXRpbmcsIGJhckNvbW1lbnRzUmF0aW5nLCBiYXJCb29rbWFya3NSYXRpbmcpDQpgYGANCg0KIyBDYXRlZ29yaWVzDQoNClRoZXJlIGFyZSBgciBsZW5ndGgoY2F0ZWdvcnlbdW5saXN0KGxhcHBseShjYXRlZ29yeSwgZnVuY3Rpb24oeCkgbGVuZ3RoKHgpKSkgPT0gMV0pYCB3b3JrcyB0YWdnZWQgd2l0aCBhIHNpbmdsZSBjYXRlZ29yeSwgYW5kIGByIGxlbmd0aChjYXRlZ29yeVt1bmxpc3QobGFwcGx5KGNhdGVnb3J5LCBmdW5jdGlvbih4KSBsZW5ndGgoeCkpKSA+IDFdKWAgdGFnZ2VkIHdpdGggMiBvciBtb3JlICh1cCB1bnRpbCBhbGwgNikuDQoNCidGL0YnIGlzIHRoZSBtb3N0IHBvcHVsYXIgY2F0ZWdvcnksIGZvbGxvd2VkIGJ5ICdGL00nLCAnR2VuJywgYW5kICdNL00nLg0KDQpNdWx0aXBsZSBjYXRlZ29yeSBmaWNzIHN0cm9uZ2x5IGNvbnRyaWJ1dGUgdG93YXJkcyAnRi9NJyBjb3VudCwgdGhlbiB0byAnRi9GJywgJ0dlbicsIGFuZCAnTS9NJywgYW5kIG9ubHkgbWFyZ2luYWxseSB0byAnTXVsdGknIGFuZCAnT3RoZXInLg0KDQpgYGB7ciBjYXRlZ29yaWVzQmFycywgbWVzc2FnZSA9IEZBTFNFfQ0KDQpzaW5nbGVDYXRlZ29yeVN1bW1hcnkgPC0gc3VtbWFyeShhcy5mYWN0b3IodW5saXN0KGNhdGVnb3J5W3VubGlzdChsYXBwbHkoY2F0ZWdvcnksIGZ1bmN0aW9uKHgpIGxlbmd0aCh4KSkpID09IDFdKSkpDQpzaW5nbGVDYXRlZ29yeVN1bW1hcnkgPC0gZGF0YS5mcmFtZShDYXRlZ29yeSA9IG5hbWVzKHNpbmdsZUNhdGVnb3J5U3VtbWFyeSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOdW1iZXIub2YuV29ya3MgPSBzaW5nbGVDYXRlZ29yeVN1bW1hcnkpDQpzaW5nbGVDYXRlZ29yeVN1bW1hcnkkU3BsaXQgPC0gIlNpbmdsZSBjYXRlZ29yeSINCg0KbXVsdGlwbGVDYXRlZ29yeVN1bW1hcnkgPC0gZGF0YS5mcmFtZShDYXRlZ29yeSA9IGMoJ0dlbicsICdGL0YnLCAnRi9NJywgJ00vTScsICdNdWx0aScsICdPdGhlcicsICdObyBjYXRlZ29yeScpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVtYmVyLm9mLldvcmtzID0gYyhzdW0oZ3JlcGwoJ0dlbicsY2F0ZWdvcnkpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGdyZXBsKCdGL0YnLGNhdGVnb3J5KSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgnRi9NJyxjYXRlZ29yeSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZ3JlcGwoJ00vTScsY2F0ZWdvcnkpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGdyZXBsKCdNdWx0aScsY2F0ZWdvcnkpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGdyZXBsKCdPdGhlcicsY2F0ZWdvcnkpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGdyZXBsKCdObyBjYXRlZ29yeScsY2F0ZWdvcnkpKSkgKQ0KbXVsdGlwbGVDYXRlZ29yeVN1bW1hcnkkU3BsaXQgPC0gIkFsbCB3b3JrcyINCg0KY2F0ZWdvcnlTdW1tYXJ5IDwtIHJiaW5kKHNpbmdsZUNhdGVnb3J5U3VtbWFyeSwgbXVsdGlwbGVDYXRlZ29yeVN1bW1hcnkpDQpjYXRlZ29yeVN1bW1hcnkkQ2F0ZWdvcnkgPC0gZmFjdG9yKGNhdGVnb3J5U3VtbWFyeSRDYXRlZ29yeSwgbGV2ZWxzID0gYygnR2VuJywgJ0YvRicsICdGL00nLCAnTS9NJywgJ011bHRpJywgJ090aGVyJywgJ05vIGNhdGVnb3J5JykpDQpjYXRlZ29yeVN1bW1hcnkkU3BsaXQgPC0gZmFjdG9yKGNhdGVnb3J5U3VtbWFyeSRTcGxpdCwgbGV2ZWxzID0gYygiU2luZ2xlIGNhdGVnb3J5IiwgIkFsbCB3b3JrcyIpKQ0KDQpwbG90Q2F0ZWdvcmllcyA8LSBnZ3Bsb3QoY2F0ZWdvcnlTdW1tYXJ5LCBhZXMoeCA9IENhdGVnb3J5LCB5ID0gTnVtYmVyLm9mLldvcmtzKSkgKyANCiAgICAgICAgICAgICAgICAgIGdlb21fY29sKGFscGhhPTEpKw0KICAgICAgICAgICAgICAgICAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICAgICAgICAgICAgICAgICAgYmFja2dyb3VuZF9ncmlkKCkgKw0KICAgICAgICAgICAgICAgICAgZmFjZXRfd3JhcCguflNwbGl0KSArDQogICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAxLCBoanVzdD0xKSkrDQogICAgICAgICAgICAgICAgICBsYWJzKHk9Ik51bWJlciBvZiBXb3JrcyIpDQpwbG90Q2F0ZWdvcmllcw0KDQpybShzaW5nbGVDYXRlZ29yeVN1bW1hcnksIG11bHRpcGxlQ2F0ZWdvcnlTdW1tYXJ5LCBjYXRlZ29yeVN1bW1hcnksIHBsb3RDYXRlZ29yaWVzKQ0KDQpgYGANCg0KIyBFbmdhZ2VtZW50IGJ5IGEgc2luZ2xlIGNhdGVnb3J5DQoNCkZvciBzaW1wbGljaXR5IEknbSBvbmx5IGxvb2tpbmcgYXQgd29ya3MgdGFnZ2VkIHdpdGggYSBzaW5nbGUgY2F0ZWdvcnkgaGVyZS4NCg0KIk11bHRpIiBzZWVtcyB0byBoYXZlIG1vc3Qgd29yZHMsIGRlc3BpdGUgYmVpbmcgYSByYXRoZXIgc21hbGwgY2F0ZWdvcnksIGFuZCBjb2xsZWN0cyBxdWl0ZSBhIGJpdCBvZiBIaXRzLCBLdWRvcywgQ29tbWVudHMgYW5kIEJvb2ttYXJrcy4gSXQncyBwb3NzaWJsZSB0aGF0IGEgbnVtYmVyIG9mIHRob3NlIHdvcmtzIGFyZSBjb2xsZWN0aW9ucyBvZiBzdG9yaWVzIGZvciBtYW55IGZhbmRvbXMsIHdoaWNoIGFtcGxpZmllcyB0aGUgZW5nYWdlbWVudCBudW1iZXJzLg0KDQpPdmVyYWxsLCAiRi9GIiBjYXRlZ29yeSB3b3JrcyBjb2xsZWN0IHR3aWNlIGFzIG1hbnkgaGl0cywga3Vkb3MsIGFuZCBib29rbWFya3MgYXMgdGhlIG5leHQgY2F0ZWdvcnksIGFuZCBtb3JlIHRoYW4gdGhyaWNlIGFzIG1hbnkgY29tbWVudHMgYXMgIk0vTSIgd29ya3MuDQoNCmBgYHtyIGNhdGVnb3JpZXNTaW5nbGVFbmdhZ2VtZW50LCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTZ9DQoNCnN0YXRzQ2F0ZWdvcnkgPC0gc3RhdHNbdW5saXN0KGxhcHBseShjYXRlZ29yeSwgZnVuY3Rpb24oeCkgbGVuZ3RoKHgpKSkgPT0gMSxdDQpzdGF0c0NhdGVnb3J5JENhdGVnb3J5IDwtIGFzLmZhY3Rvcih1bmxpc3QoY2F0ZWdvcnlbdW5saXN0KGxhcHBseShjYXRlZ29yeSwgZnVuY3Rpb24oeCkgbGVuZ3RoKHgpKSkgPT0gMV0pKQ0Kc3RhdHNDYXRlZ29yeSRDYXRlZ29yeSA8LSBmYWN0b3Ioc3RhdHNDYXRlZ29yeSRDYXRlZ29yeSwgbGV2ZWxzID0gYygnR2VuJywgJ0YvRicsICdGL00nLCAnTS9NJywgJ011bHRpJywgJ090aGVyJywgJ05vIGNhdGVnb3J5JykpDQpzdGF0c0NhdGVnb3J5JERpdmlzb3IgPC0gdW5saXN0KGxhcHBseShzdGF0c0NhdGVnb3J5JENhdGVnb3J5LCBmdW5jdGlvbih4KSBzdW1tYXJ5KHN0YXRzQ2F0ZWdvcnkkQ2F0ZWdvcnkpW25hbWVzKHN1bW1hcnkoc3RhdHNDYXRlZ29yeSRDYXRlZ29yeSkpID09IHhdKSkNCnN0YXRzQ2F0ZWdvcnkkV29yZHMucGVyLldvcmsgPC0gc3RhdHNDYXRlZ29yeSRXb3Jkcy9zdGF0c0NhdGVnb3J5JERpdmlzb3INCnN0YXRzQ2F0ZWdvcnkkSGl0cy5wZXIuV29yayA8LSBzdGF0c0NhdGVnb3J5JEhpdHMvc3RhdHNDYXRlZ29yeSREaXZpc29yDQpzdGF0c0NhdGVnb3J5JEt1ZG9zLnBlci5Xb3JrIDwtIHN0YXRzQ2F0ZWdvcnkkS3Vkb3Mvc3RhdHNDYXRlZ29yeSREaXZpc29yDQpzdGF0c0NhdGVnb3J5JENvbW1lbnRzLnBlci5Xb3JrIDwtIHN0YXRzQ2F0ZWdvcnkkQ29tbWVudHMvc3RhdHNDYXRlZ29yeSREaXZpc29yDQpzdGF0c0NhdGVnb3J5JEJvb2ttYXJrcy5wZXIuV29yayA8LSBzdGF0c0NhdGVnb3J5JEJvb2ttYXJrcy9zdGF0c0NhdGVnb3J5JERpdmlzb3INCnN0YXRzQ2F0ZWdvcnkkV29ya3MuUGVyY2VudCA8LSAxL3N0YXRzQ2F0ZWdvcnkkRGl2aXNvcg0KDQpiYXJXb3Jrc0NhdGVnb3J5IDwtIHBsb3RfYmFyX2NvbG9yKHN0YXRzQ2F0ZWdvcnksICdDYXRlZ29yeScsICdSYXRpbmcnLCAncmlnaHQnKQ0KYmFyV29yZHNDYXRlZ29yeSA8LSBwbG90X2NvbF9jb2xvcihzdGF0c0NhdGVnb3J5LCAnQ2F0ZWdvcnknLCAnV29yZHMucGVyLldvcmsnLCAnUmF0aW5nJywgJ3JpZ2h0JykNCmJhckhpdHNDYXRlZ29yeSA8LSBwbG90X2NvbF9jb2xvcihzdGF0c0NhdGVnb3J5LCAnQ2F0ZWdvcnknLCAnSGl0cy5wZXIuV29yaycsICdSYXRpbmcnLCAncmlnaHQnKQ0KYmFyS3Vkb3NDYXRlZ29yeSA8LSBwbG90X2NvbF9jb2xvcihzdGF0c0NhdGVnb3J5LCAnQ2F0ZWdvcnknLCAnS3Vkb3MucGVyLldvcmsnLCAnUmF0aW5nJywgJ3JpZ2h0JykNCmJhckNvbW1lbnRzQ2F0ZWdvcnkgPC0gcGxvdF9jb2xfY29sb3Ioc3RhdHNDYXRlZ29yeSwgJ0NhdGVnb3J5JywgJ0NvbW1lbnRzLnBlci5Xb3JrJywgJ1JhdGluZycsICdyaWdodCcpDQpiYXJCb29rbWFya3NDYXRlZ29yeSA8LSBwbG90X2NvbF9jb2xvcihzdGF0c0NhdGVnb3J5LCAnQ2F0ZWdvcnknLCAnQm9va21hcmtzLnBlci5Xb3JrJywnUmF0aW5nJywgJ3JpZ2h0JykNCg0KcGxvdF9ncmlkKHBsb3RfZ3JpZCggYmFyV29ya3NDYXRlZ29yeSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJXb3Jkc0NhdGVnb3J5ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckhpdHNDYXRlZ29yeSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJLdWRvc0NhdGVnb3J5ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckNvbW1lbnRzQ2F0ZWdvcnkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYmFyQm9va21hcmtzQ2F0ZWdvcnkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYWxpZ24gPSAnaHYnKSwNCiAgICAgICAgICBnZXRfbGVnZW5kKGJhcldvcmtzQ2F0ZWdvcnkgKyB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKSksDQogICAgICAgICAgcmVsX3dpZHRocyA9IGMoNCwxKSkNCg0KYGBgDQoNCiMgUmF0aW5ncyBwZXJjZW50YWdlcyBieSBhIHNpbmdsZSBjYXRlZ29yeQ0KDQpPdXQgb2YgdGhlIDMgbWFpbiBzaGlwcGluZyBjYXRlZ29yaWVzLCBpbiBhYnNvbHV0ZSBudW1iZXJzICJGL0YiIGhhcyBtb3N0IEUgcmF0ZWQgd29ya3MsIGFuZCAiTS9NIiBoYXMgdGhlIGxlYXN0LiBIb3dldmVyLCBpbiByZWxhdGl2ZSBhbW91bnRzICJNL00iIGNhdGVnb3J5IGhhcyBtb3JlIGV4cGxpY2l0IHdvcmtzIChgciByb3VuZCgxMDAqc3VtKHN0YXRzQ2F0ZWdvcnkkQ2F0ZWdvcnkgPT0gIk0vTSIgJiBzdGF0c0NhdGVnb3J5JFJhdGluZyA9PSAiRXhwbGljaXQiKS9zdW0oc3RhdHNDYXRlZ29yeSRDYXRlZ29yeSA9PSAiTS9NIikpYCUpIHRoYW4gZWl0aGVyICJGL0YiIG9yICJGL00iLiBPdmVyYWxsLCB0aGUgZGlzdHJpYnV0aW9ucyBvZiByYXRpbmdzIGJldHdlZW4gdGhlIGNhdGVnb3JpZXMgYXJlIG11Y2ggbW9yZSBiYWxhbmNlZCB0aGFuIGluIFREUCwgcGVyaGFwcywgcmVmbGVjdGluZyBhIHZhcmlldHkgb2YgaGlnaGx5IHBsb3QgcmVsZXZhbnQgb2xkZXIgZmVtYWxlIGNoYXJhY3RlcnMgaW4gdGhlIGNhc3QuDQoNCmBgYHtyIGNhdGVnb3JpZXNTaW5nbGVFbmdhZ2VtZW50UGVyY2VudCwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQ0KDQpwbG90V29ya3NDYXRlZ29yeU5vcm1hbGl6ZWQgPC0gcGxvdF9jb2xfY29sb3Ioc3RhdHNDYXRlZ29yeSwgJ1JhdGluZycsICdXb3Jrcy5QZXJjZW50JywgJ1JhdGluZycsICdub25lJykrDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2V0X3dyYXAoLn5DYXRlZ29yeSkNCnBsb3RXb3Jrc0NhdGVnb3J5Tm9ybWFsaXplZA0KDQpybShiYXJXb3Jrc0NhdGVnb3J5LCBiYXJXb3Jkc0NhdGVnb3J5LCBiYXJIaXRzQ2F0ZWdvcnksIGJhckt1ZG9zQ2F0ZWdvcnksIGJhckNvbW1lbnRzQ2F0ZWdvcnksIGJhckJvb2ttYXJrc0NhdGVnb3J5LCBwbG90V29ya3NDYXRlZ29yeU5vcm1hbGl6ZWQpDQoNCmBgYA0KDQojIFNpbmdsZSBDYXRlZ29yeSB0aHJvdWdoIHRpbWUNCg0KU2Vhc29ucyAzIGFuZCA0IG9mIExPSyBicm91Z2h0IGEgaHVnZSByaXNlIGluICdGL0YnIGNhdGVnb3J5IHBvcHVsYXJpdHkuIFRoZXJlJ3MgYSBzbWFsbGVyIHBlYWsgYXJvdW5kIHN1bW1lciAyMDE3IHdoaWNoIG1heSBiZSBkdWUgdG8gcmVsZWFzZSBvZiAiVGhlIExlZ2VuZCBvZiBLb3JyYTogVHVyZiBXYXJzIOKAkyBQYXJ0IE9uZSIgaW4gSnVseSAyMDE3Lg0KDQpgYGB7ciBzaW5nbGVSYXRpbmdUaW1lLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQ0KDQpwbG90RGF0ZXNSYXRpbmdEZW5zaXR5IDwtIGdncGxvdChzdGF0c0NhdGVnb3J5LCBhZXMoeCA9IERhdGUsIGNvbD1DYXRlZ29yeSkpICsgDQogICAgICAgICAgICAgICAgICAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMSkrDQogICAgICAgICAgICAgICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1zZWFzb25zU3RhcnQpKw0KICAgICAgICAgICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9c2Vhc29uc0VuZCwgbGluZXR5cGUgPSJsb25nZGFzaCIpKw0KICAgICAgICAgICAgICAgICAgICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3M9IjYgbW9udGhzIikrDQogICAgICAgICAgICAgICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21wYWxldHRlKSArDQogICAgICAgICAgICAgICAgICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICAgICAgICAgICAgICAgICAgYmFja2dyb3VuZF9ncmlkKCkgKw0KICAgICAgICAgICAgICAgICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICdyaWdodCcpDQpwbG90RGF0ZXNSYXRpbmdEZW5zaXR5DQoNCnJtKHBsb3REYXRlc1JhdGluZ0RlbnNpdHkpDQpgYGANCg0KIyBNb3N0IHBvcHVsYXIgc2hpcCB0YWdzDQoNCk92ZXJ3aGVsbWluZ2x5ICJLb3JyYS9Bc2FtaSBTYXRvIiBpcyB0aGUgbW9zdCBwb3B1bGFyIHNoaXAgaW4gTE9LLg0KDQpgYGB7ciBzaGlwc0hpc3RvZ3JhbSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02fQ0KDQp0b3BMaXN0IDwtIDgNCnRvcFJlbGF0aW9uc2hpcHNUYWJsZTwtIGRhdGEuZnJhbWUoJ1JlbGF0aW9uc2hpcCcgPSBuYW1lcyhzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QocmVsYXRpb25zaGlwcykpKVsxOnRvcExpc3RdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgJ051bWJlciBvZiBTdG9yaWVzJyA9IHN1bW1hcnkoYXMuZmFjdG9yKHVubGlzdChyZWxhdGlvbnNoaXBzKSkpWzE6dG9wTGlzdF0pDQpyb3cubmFtZXModG9wUmVsYXRpb25zaGlwc1RhYmxlKSA8LSBjKCkNCnRvcFJlbGF0aW9uc2hpcHNUYWJsZSA8LSB0b3BSZWxhdGlvbnNoaXBzVGFibGVbb3JkZXIodG9wUmVsYXRpb25zaGlwc1RhYmxlJE51bWJlci5vZi5TdG9yaWVzLCBkZWNyZWFzaW5nID0gVFJVRSksXQ0KdG9wUmVsYXRpb25zaGlwc1RhYmxlJFJlbGF0aW9uc2hpcCA8LSBmYWN0b3IoYXMuY2hhcmFjdGVyKHRvcFJlbGF0aW9uc2hpcHNUYWJsZSRSZWxhdGlvbnNoaXApLCBsZXZlbHM9YXMuY2hhcmFjdGVyKHRvcFJlbGF0aW9uc2hpcHNUYWJsZSRSZWxhdGlvbnNoaXApKQ0KDQpyZWxhdGlvbnNoaXBzU3RhdHMgPC0gZGF0YS5mcmFtZShEYXRlID0gc3RhdHMkRGF0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDEgPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDIgPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDMgPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDQgPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDUgPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDYgPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDcgPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDggPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSkNCmZvciAoaSBpbiAxOnRvcExpc3Qpew0KICBtYXRjaGluZ1ZlY3RvciA8LSBsYXBwbHkocmVsYXRpb25zaGlwcywgbWF0Y2gsIHRhYmxlPWFzLmNoYXJhY3Rlcih0b3BSZWxhdGlvbnNoaXBzVGFibGUkUmVsYXRpb25zaGlwW2ldKSkNCiAgbWF0Y2hpbmdWZWN0b3IgPC0gdW5saXN0KGxhcHBseShtYXRjaGluZ1ZlY3Rvciwgc3VtLCBuYS5ybT1UUlVFKSkNCiAgcmVsYXRpb25zaGlwc1N0YXRzW2krMV0gPC0gbWF0Y2hpbmdWZWN0b3INCn0NCg0KI2NvbG5hbWVzKHJlbGF0aW9uc2hpcHNTdGF0cylbMjo5XSA8LSBnc3ViKCcvJywgJ1xcLycsIHRvcFJlbGF0aW9uc2hpcHNUYWJsZSRSZWxhdGlvbnNoaXApDQoNCnBsb3RMZWdlbmRSZWxhdGlvbnNoaXBzIDwtIGdncGxvdCh0b3BSZWxhdGlvbnNoaXBzVGFibGUsIGFlcyh4PVJlbGF0aW9uc2hpcCwgeT1OdW1iZXIub2YuU3RvcmllcywgZmlsbD1SZWxhdGlvbnNoaXApKSsNCiAgZ2VvbV9jb2woYWxwaGE9MC43KSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tcGFsZXR0ZSkrDQogIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgYmFja2dyb3VuZF9ncmlkKCkgKw0KICBsYWJzKHg9IiIseT0nTnVtYmVyIG9mIFN0b3JpZXMnKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKQ0KcGxvdExlZ2VuZFJlbGF0aW9uc2hpcHMrdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKQ0KYGBgDQoNCiMgU2hpcCB0YWdzIHRocm91Z2ggdGltZQ0KDQpTZWFzb25zIDMgYW5kIDQgYnJvdWdodCBhIHBlYWsgb2YgcG9wdWxhcml0eSB0byAiS29ycmEvQXNhbWkgU2F0byIsIGJ1dCBhbHNvIGNvaW5jaWRlZCB3aXRoIHRoZSBwZWFrcyBmb3IgbW9zdCBvZiB0aGUgb3RoZXIgdG9wIHNoaXBzLCBleGNsdWRpbmcgcGVyaGFwcyBvbmx5ICJLb3JyYS9NYWtvIChBdmF0YXIpIiwgd2hpY2ggZXhwZXJpZW5jZWQgcGVha3MgYXJvdW5kIHNlYXNvbiAxIGFuZCAzLiBJdCdzIHBlcmhhcHMgd29ydGggbm90aW5nICJMaW4gQmVpZm9uZy9LeWEgSUkiIGV4cGVyaWVuY2luZyBhIHNsaWdodGx5IGxhdGVyIHBlYWsgYWZ0ZXIgc2Vhc29ucyAzLTQsIGFuZCB0aGVuIGFub3RoZXIgb25lIHN0YXJ0aW5nIGFyb3VuZCBzdW1tZXIgMjAxNywgd2hpY2ggaXMgbGlrZWx5IGR1ZSB0byAiVGhlIExlZ2VuZCBvZiBLb3JyYTogVHVyZiBXYXJzIOKAkyBQYXJ0IE9uZSIgcmV2ZWFsIG9mIEt5YSBJSSBhcyBzYXBwaGljLg0KDQpgYGB7ciBzaGlwVGFnc1RpbWUsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9DQoNCnBsb3RSZWxhdGlvbnNoaXBzIDwtIGdncGxvdCgpICsNCiAgICBnZW9tX2RlbnNpdHkoZGF0YSA9IHJlbGF0aW9uc2hpcHNTdGF0c1tyZWxhdGlvbnNoaXBzU3RhdHMkcmVsYXRpb25zaGlwMSA+IDAsXSwgbWFwcGluZz1hZXMoeCA9IERhdGUpLCBjb2xvdXI9Y3VzdG9tcGFsZXR0ZVsxXSkrDQogICAgZ2VvbV9kZW5zaXR5KGRhdGEgPSByZWxhdGlvbnNoaXBzU3RhdHNbcmVsYXRpb25zaGlwc1N0YXRzJHJlbGF0aW9uc2hpcDIgPiAwLF0sIG1hcHBpbmc9YWVzKHggPSBEYXRlKSwgY29sb3VyPWN1c3RvbXBhbGV0dGVbMl0pKw0KICAgIGdlb21fZGVuc2l0eShkYXRhID0gcmVsYXRpb25zaGlwc1N0YXRzW3JlbGF0aW9uc2hpcHNTdGF0cyRyZWxhdGlvbnNoaXAzID4gMCxdLCBtYXBwaW5nPWFlcyh4ID0gRGF0ZSksIGNvbG91cj1jdXN0b21wYWxldHRlWzNdKSsNCiAgICBnZW9tX2RlbnNpdHkoZGF0YSA9IHJlbGF0aW9uc2hpcHNTdGF0c1tyZWxhdGlvbnNoaXBzU3RhdHMkcmVsYXRpb25zaGlwNCA+IDAsXSwgbWFwcGluZz1hZXMoeCA9IERhdGUpLCBjb2xvdXI9Y3VzdG9tcGFsZXR0ZVs0XSkrDQogICAgZ2VvbV9kZW5zaXR5KGRhdGEgPSByZWxhdGlvbnNoaXBzU3RhdHNbcmVsYXRpb25zaGlwc1N0YXRzJHJlbGF0aW9uc2hpcDUgPiAwLF0sIG1hcHBpbmc9YWVzKHggPSBEYXRlKSwgY29sb3VyPWN1c3RvbXBhbGV0dGVbNV0pKw0KICAgIGdlb21fZGVuc2l0eShkYXRhID0gcmVsYXRpb25zaGlwc1N0YXRzW3JlbGF0aW9uc2hpcHNTdGF0cyRyZWxhdGlvbnNoaXA2ID4gMCxdLCBtYXBwaW5nPWFlcyh4ID0gRGF0ZSksIGNvbG91cj1jdXN0b21wYWxldHRlWzZdKSsNCiAgICBnZW9tX2RlbnNpdHkoZGF0YSA9IHJlbGF0aW9uc2hpcHNTdGF0c1tyZWxhdGlvbnNoaXBzU3RhdHMkcmVsYXRpb25zaGlwNyA+IDAsXSwgbWFwcGluZz1hZXMoeCA9IERhdGUpLCBjb2xvdXI9Y3VzdG9tcGFsZXR0ZVs3XSkrDQogICAgZ2VvbV9kZW5zaXR5KGRhdGEgPSByZWxhdGlvbnNoaXBzU3RhdHNbcmVsYXRpb25zaGlwc1N0YXRzJHJlbGF0aW9uc2hpcDggPiAwLF0sIG1hcHBpbmc9YWVzKHggPSBEYXRlKSwgY29sb3VyPWN1c3RvbXBhbGV0dGVbOF0pKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1zZWFzb25zU3RhcnQpKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1zZWFzb25zRW5kLCBsaW5ldHlwZSA9ImxvbmdkYXNoIikrDQogICAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzPSI2IG1vbnRocyIpKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21wYWxldHRlKSArDQogICAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICAgIGJhY2tncm91bmRfZ3JpZCgpICsNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpDQoNCm15bGVnZW5kIDwtIGdldF9sZWdlbmQocGxvdExlZ2VuZFJlbGF0aW9uc2hpcHMpDQoNCnBsb3RfZ3JpZChwbG90UmVsYXRpb25zaGlwcywgbXlsZWdlbmQsDQogICAgICAgICAgcmVsX3dpZHRocyA9IGMoMiwxKSwgbnJvdz0xKQ0KI3Bsb3RSZWxhdGlvbnNoaXBzDQoNCiNybShzZWFzb25zLCBwbG90RGF0ZXNSYXRpbmdEZW5zaXR5KQ0KYGBgDQoNCiMgQXJjaGl2ZSBXYXJuaW5ncw0KDQpNYWpvcml0eSBvZiB3b3JrcyBhcmUgdGFnZ2VkIHdpdGggIk5vIEFyY2hpdmUgV2FybmluZ3MgQXBwbHkiLCBmb2xsb3dlZCBieSBhIHNpemFibGUgZnJhY3Rpb24gb2YgIkNyZWF0b3IgQ2hvc2UgTm90IFRvIFVzZSBBcmNoaXZlIFdhcm5pbmdzIi4gSXQgc2VlbXMgdG8gYmUgYSBjb21tb24gbWF0dGVyIG9mIGNvbmZ1c2lvbiBiZXR3ZWVuIHRoZSB1c2FnZSBvZiB0aG9zZSB0d28gd2FybmluZ3MsIHNvIGl0J3MgcG9zc2libGUgdGhhdCBhIGxvdCBvZiAiQ3JlYXRvciBDaG9zZSBOb3QgVG8gVXNlIEFyY2hpdmUgV2FybmluZ3MiIGFyZSBtaXN0YWdnZWQgIk5vIEFyY2hpdmUgV2FybmluZ3MgQXBwbHkiLg0KDQpgYGB7ciB3YXJuaW5nQmFycywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD02fQ0KDQptdWx0aXBsZVdhcm5pbmdTdW1tYXJ5IDwtIGRhdGEuZnJhbWUoV2FybmluZyA9IGMoIk5vIEFyY2hpdmUgV2FybmluZ3MgQXBwbHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JhcGhpYyBEZXBpY3Rpb25zIE9mIFZpb2xlbmNlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1ham9yIENoYXJhY3RlciBEZWF0aCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSYXBlL05vbi1Db24iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVW5kZXJhZ2UiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ3JlYXRvciBDaG9zZSBOb3QgVG8gVXNlIEFyY2hpdmUgV2FybmluZ3MiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE51bWJlci5vZi5Xb3JrcyA9IGMoc3VtKGdyZXBsKCJObyBBcmNoaXZlIFdhcm5pbmdzIEFwcGx5Iix3YXJuaW5ncykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZ3JlcGwoIkdyYXBoaWMgRGVwaWN0aW9ucyBPZiBWaW9sZW5jZSIsd2FybmluZ3MpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGdyZXBsKCJNYWpvciBDaGFyYWN0ZXIgRGVhdGgiLHdhcm5pbmdzKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgiUmFwZS9Ob24tQ29uIix3YXJuaW5ncykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZ3JlcGwoIlVuZGVyYWdlIix3YXJuaW5ncykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZ3JlcGwoIkNyZWF0b3IgQ2hvc2UgTm90IFRvIFVzZSBBcmNoaXZlIFdhcm5pbmdzIix3YXJuaW5ncykpKSApDQoNCm11bHRpcGxlV2FybmluZ1N1bW1hcnkkV2FybmluZyA8LSBmYWN0b3IobXVsdGlwbGVXYXJuaW5nU3VtbWFyeSRXYXJuaW5nLCBsZXZlbHMgPSBjKCJObyBBcmNoaXZlIFdhcm5pbmdzIEFwcGx5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcmFwaGljIERlcGljdGlvbnMgT2YgVmlvbGVuY2UiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1ham9yIENoYXJhY3RlciBEZWF0aCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmFwZS9Ob24tQ29uIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJVbmRlcmFnZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ3JlYXRvciBDaG9zZSBOb3QgVG8gVXNlIEFyY2hpdmUgV2FybmluZ3MiKSkNCg0KcGxvdFdhcm5pbmdzIDwtIHBsb3RfY29sKG11bHRpcGxlV2FybmluZ1N1bW1hcnksICdXYXJuaW5nJywgJ051bWJlci5vZi5Xb3JrcycsICdyaWdodCcpDQpwbG90V2FybmluZ3MNCg0Kcm0obXVsdGlwbGVXYXJuaW5nU3VtbWFyeSwgcGxvdFdhcm5pbmdzKQ0KDQpgYGANCg0KIyBNdWx0aXBsZSBGYW5kb21zDQoNCk51bWJlciBvZiB3b3JrcyB0YWdnZWQgd2l0aCBtb3JlIHRoYW4gMSBmYW5kb20gaXMgYHIgbGVuZ3RoKGZhbmRvbVt1bmxpc3QobGFwcGx5KGZhbmRvbSwgbGVuZ3RoKSkgPiAxXSlgLCBidXQgbnVtYmVyIG9mIHdvcmtzIHRhZ2dlZCB3aXRoIG1vcmUgdGhhbiAyIGZhbmRvbXMgaXMgYHIgbGVuZ3RoKGZhbmRvbVt1bmxpc3QobGFwcGx5KGZhbmRvbSwgbGVuZ3RoKSkgPiAyXSlgIHdoaWNoIHNlZW1zIHRvIGJlIGR1ZSB0byB3b3JrcyBvZnRlbiBiZWluZyB0YWdnZWQgd2l0aCBib3RoICJBdmF0YXI6IExlZ2VuZCBvZiBLb3JyYSIgYW5kICJBdmF0YXI6IFRoZSBMYXN0IEFpcmJlbmRlciIuDQoNCk51bWJlciBvZiB3b3JrcyBleHBsaWNpdGx5IHRhZ2dlZCBhcyAnY3Jvc3NvdmVyJyBpcyBqdXN0IGByIGxlbmd0aChmYW5kb21bZ3JlcCgnY3Jvc3NvdmVyJyxmcmVlZm9ybSwgaWdub3JlLmNhc2U9VFJVRSldKWAuDQoNCiMgQXV0aG9ycyBieSBXb3Jrcw0KDQpUb3AgMzAgb2YgbW9zdCBwcm9saWZpYyBhdXRob3JzIGluIHRoZSB0YWcgYnkgdGhlIG51bWJlciBvZiBzdG9yaWVzIGFzIG9mIGRhdGEgY29sbGVjdGlvbiBkYXRlOg0KDQpgYGB7ciBhdXRob3JzV29ya3MsIG1lc3NhZ2UgPSBGQUxTRX0NCnRvcExpc3QgPC0gMzANCg0KQXV0aG9yVGFibGUgPC0gZGF0YS5mcmFtZSgnQXV0aG9yJyA9IG5hbWVzKHN1bW1hcnkoYXMuZmFjdG9yKHVubGlzdChhdXRob3IpKSlbMTp0b3BMaXN0XSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICdOdW1iZXIgb2YgU3RvcmllcycgPSBzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QoYXV0aG9yKSkpWzE6dG9wTGlzdF0pDQpyb3cubmFtZXMoQXV0aG9yVGFibGUpIDwtIGMoKQ0KDQprYWJsZShBdXRob3JUYWJsZSwNCiAgICAgIGNvbC5uYW1lcyA9IGMoJ0F1dGhvcicsICdOdW1iZXIgb2YgU3RvcmllcycpKQ0KDQpybShBdXRob3JUYWJsZSkNCmBgYA0KDQpUb3AgcGxhY2UgaXMgb2NjdXBpZWQgYnkgb3JwaGFuX2FjY291bnQsIHdoaWNoIGlzIGFuIGFydGlmYWN0IG9mIGFyY2hpdmUnIHdvcmtzIG9ycGhhbmluZyBmdW5jdGlvbi4NCg0KIyBBdXRob3JzIGJ5IFdvcmRzDQoNCk9ubHkgYHIgc3VtKHVubGlzdChsYXBwbHkoYXV0aG9yLCBsZW5ndGgpKT4xKWAgd29ya3MgaGF2ZSBtb3JlIHRoYW4gb25lIGF1dGhvci4gSW4gY2FzZXMgd2hlcmUgd29ya3MgaGFkIG1vcmUgdGhhbiBvbmUgYXV0aG9yLCBJIGFzc3VtZWQgdGhhdCBlYWNoIG9mIHRoZW0gY29udHJpYnV0ZWQgYW4gZXF1YWwgYW1vdW50cyBvZiB3b3Jkcy4NCg0KVG9wIDMwIG9mIG1vc3QgcHJvbGlmaWMgYXV0aG9ycyBpbiB0aGUgdGFnIGJ5IHRoZSBudW1iZXIgb2Ygd29yZHMgd3JpdHRlbiBhcyBvZiBkYXRhIGNvbGxlY3Rpb24gZGF0ZToNCg0KYGBge3IgYXV0aG9yc1dvcmRzLCBtZXNzYWdlID0gRkFMU0V9DQoNCndvcmRzQnlBdXRob3IgPC0gYygpDQoNCmZvciAoaSBpbiAxOmxlbmd0aCh3b3Jkcykpew0KICBpZiAobGVuZ3RoKGF1dGhvcltbaV1dKSA+IDEpIHsNCiAgICB3b3Jkc0J5QXV0aG9yIDwtIGMod29yZHNCeUF1dGhvciwgcmVwKHdvcmRzW1tpXV0vbGVuZ3RoKGF1dGhvcltbNV1dKSwgbGVuZ3RoKGF1dGhvcltbaV1dKSApICkNCiAgfSBlbHNlIHsNCiAgICB3b3Jkc0J5QXV0aG9yIDwtIGMod29yZHNCeUF1dGhvciwgd29yZHNbW2ldXSkNCiAgfQ0KfQ0KDQpBdXRob3JXb3Jkc1RhYmxlIDwtIGRhdGEuZnJhbWUoJ0F1dGhvcicgPSBhcy5mYWN0b3IodW5saXN0KGF1dGhvcikpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdXb3JkcycgPSB3b3Jkc0J5QXV0aG9yKQ0KDQpBdXRob3JXb3Jkc1N1bW1hcnkgPC0gZGRwbHkoQXV0aG9yV29yZHNUYWJsZSwgLihBdXRob3IpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpemUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRvdGFsLldvcmRzID0gc3VtKFdvcmRzKSkNCkF1dGhvcldvcmRzU3VtbWFyeSA8LSBBdXRob3JXb3Jkc1N1bW1hcnlbb3JkZXIoQXV0aG9yV29yZHNTdW1tYXJ5JFRvdGFsLldvcmRzLCBkZWNyZWFzaW5nID0gVFJVRSksXQ0Kcm93Lm5hbWVzKEF1dGhvcldvcmRzU3VtbWFyeSkgPC0gYygpDQoNCnRvcExpc3QgPC0gMzANCg0Ka2FibGUoQXV0aG9yV29yZHNTdW1tYXJ5WzE6dG9wTGlzdCxdLA0KICAgICAgY29sLm5hbWVzID0gYygnQXV0aG9yJywgJ1RvdGFsIFdvcmRzJykpDQoNCnJtKHdvcmRzQnlBdXRob3IsIGksIEF1dGhvcldvcmRzVGFibGUsIEF1dGhvcldvcmRzU3VtbWFyeSkNCmBgYA0KDQpJbnRlcmVzdGluZ2x5LCBvcnBoYW5fYWNjb3VudCBtYWRlIGl0IHRvIHRoZSB0b3AgYnkgdGhlIG51bWJlciBvZiB3b3JkcyB3cml0dGVuIGFzIHdlbGwuDQoNCiMgQ2hhcmFjdGVycw0KDQpUb3AgMzAgb2YgdGhlIG1vc3QgcG9wdWxhciBjaGFyYWN0ZXJzOg0KDQpgYGB7ciBjaGFyYWN0ZXJzLCBtZXNzYWdlID0gRkFMU0V9DQp0b3BMaXN0IDwtIDMwDQpDaGFyYWN0ZXJUYWJsZTwtIGRhdGEuZnJhbWUoJ0NoYXJhY3RlcicgPSBuYW1lcyhzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QoY2hhcmFjdGVyKSkpWzE6dG9wTGlzdF0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAnTnVtYmVyIG9mIFN0b3JpZXMnID0gc3VtbWFyeShhcy5mYWN0b3IodW5saXN0KGNoYXJhY3RlcikpKVsxOnRvcExpc3RdKQ0Kcm93Lm5hbWVzKENoYXJhY3RlclRhYmxlKSA8LSBjKCkNCg0Ka2FibGUoQ2hhcmFjdGVyVGFibGUsDQogICAgICBjb2wubmFtZXMgPSBjKCdDaGFyYWN0ZXInLCAnTnVtYmVyIG9mIFN0b3JpZXMnKSkNCg0Kcm0oQ2hhcmFjdGVyVGFibGUpDQpgYGANCg0KIyBSZWxhdGlvbnNoaXBzDQoNClRvcCAzMCBvZiB0aGUgbW9zdCBwb3B1bGFyIHJlbGF0aW9uc2hpcHM6DQoNCkkgZG9uJ3QgaGF2ZSBhY2Nlc3MgdG8gQW8zJ3Mgc3lzdGVtIG9mIHN5bm9ueW1vdXMgdGFncywgc28gYnkgdmlydHVlIG9mIHRleHQgcHJvY2Vzc2luZyBzb21lIHJlbGF0aW9uc2hpcCB0YWdzIGhlcmUgYXJlIHJlcGVhdGVkLg0KDQpPdmVyd2hlbG1pbmdseSwgIktvcnJhL0FzYW1pIFNhdG8iLyJLb3JyYXNhbWkiLyJLb3JyYS9Bc2FtaSIgaXMgdGhlIG1vc3QgcG9wdWxhciByZWxhdGlvbnNoaXAgaW4gTE9LLCBjb250cmlidXRpbmcgdG8gcG9wdWxhcml0eSBvZiAiRi9GIiBjYXRlZ29yeS4gVGhleSBhcmUgZm9sbG93ZWQgYnkgIktvcnJhL01ha28gKEF2YXRhcikiLCBhbmQgIkJvbGluL09wYWwgKEF2YXRhcikiLg0KDQpgYGB7ciByZWxhdGlvbnNoaXBzLCBtZXNzYWdlID0gRkFMU0V9DQp0b3BMaXN0IDwtIDMwDQpSZWxhdGlvbnNoaXBzVGFibGU8LSBkYXRhLmZyYW1lKCdSZWxhdGlvbnNoaXAnID0gbmFtZXMoc3VtbWFyeShhcy5mYWN0b3IodW5saXN0KHJlbGF0aW9uc2hpcHMpKSlbMTp0b3BMaXN0XSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICdOdW1iZXIgb2YgU3RvcmllcycgPSBzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QocmVsYXRpb25zaGlwcykpKVsxOnRvcExpc3RdKQ0Kcm93Lm5hbWVzKFJlbGF0aW9uc2hpcHNUYWJsZSkgPC0gYygpDQoNCmthYmxlKFJlbGF0aW9uc2hpcHNUYWJsZSwNCiAgICAgIGNvbC5uYW1lcyA9IGMoJ1JlbGF0aW9uc2hpcCcsICdOdW1iZXIgb2YgU3RvcmllcycpKQ0KDQpybShSZWxhdGlvbnNoaXBzVGFibGUpDQpgYGANCg0KIyBGcmVlZm9ybSB0YWdzDQoNClRvcCAzMCBvZiB0aGUgbW9zdCBwb3B1bGFyIGZyZWVmb3JtIHRhZ3M6DQoNCmBgYHtyIGZyZWVmb3JtLCBtZXNzYWdlID0gRkFMU0V9DQp0b3BMaXN0IDwtIDMwDQpGcmVlZm9ybVRhYmxlPC0gZGF0YS5mcmFtZSgnRnJlZWZvcm0nID0gbmFtZXMoc3VtbWFyeShhcy5mYWN0b3IodW5saXN0KGZyZWVmb3JtKSkpWzE6dG9wTGlzdF0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAnTnVtYmVyIG9mIFN0b3JpZXMnID0gc3VtbWFyeShhcy5mYWN0b3IodW5saXN0KGZyZWVmb3JtKSkpWzE6dG9wTGlzdF0pDQpyb3cubmFtZXMoRnJlZWZvcm1UYWJsZSkgPC0gYygpDQoNCmthYmxlKEZyZWVmb3JtVGFibGUsDQogICAgICBjb2wubmFtZXMgPSBjKCdGcmVlZm9ybSBUYWcnLCAnTnVtYmVyIG9mIFN0b3JpZXMnKSkNCg0Kcm0oRnJlZWZvcm1UYWJsZSkNCmBgYA0KDQojIExhbmd1YWdlcw0KDQpVbnN1cnByaXNpbmdseSwgbW9zdCB3b3JrcyBhcmUgd3JpdHRlbiBpbiBFbmdsaXNoLiBBcG9sb2dpZXMgZm9yIFUrLiBrYWJsZSBwYWNrYWdlIGZvciB3aGF0ZXZlciByZWFzb24gbXVyZGVycyB1bmljb2RlIGNoYXJhY3RlcnMuIFRoZSB0d28gbGFuZ3VhZ2VzIGluIHF1ZXN0aW9uIGFyZSBSdXNzaWFuICjQoNGD0YHRgdC60LjQuSkgYW5kIENoaW5lc2UgKOS4reaWhykuDQoNCmBgYHtyIGxhbmd1YWdlcywgbWVzc2FnZSA9IEZBTFNFfQ0KI3RvcExpc3QgPC0gMzANCg0KbGFuZ3VhZ2VzTGlzdCA8LSBzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QobGFuZ3VhZ2UpKSkNCg0KTGFuZ3VhZ2VUYWJsZSA8LSBkYXRhLmZyYW1lKCdMYW5ndWFnZScgPSBuYW1lcyhsYW5ndWFnZXNMaXN0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnTnVtYmVyIG9mIFN0b3JpZXMnID0gbGFuZ3VhZ2VzTGlzdCApDQpMYW5ndWFnZVRhYmxlIDwtIExhbmd1YWdlVGFibGVbb3JkZXIoTGFuZ3VhZ2VUYWJsZSROdW1iZXIub2YuU3RvcmllcywgZGVjcmVhc2luZz1UUlVFKSxdDQpyb3cubmFtZXMoTGFuZ3VhZ2VUYWJsZSkgPC0gYygpDQoNCmthYmxlKExhbmd1YWdlVGFibGUsDQogICAgICBjb2wubmFtZXMgPSBjKCdMYW5ndWFnZScsICdOdW1iZXIgb2YgU3RvcmllcycpKQ0KDQojbGFuZ3VhZ2VzTGlzdA0KDQojcm0oTGFuZ3VhZ2VUYWJsZSkNCmBgYA0KDQojIE90aGVyIGxpbmtzDQoNCltBbzMgZGF0YSBhbmFseXNpcyBmb3IgVGhlIERyYWdvbiBQcmluY2UgKENhcnRvb24pXShodHRwczovL2RhcnRoYWxpbmUuZ2l0aHViLmlvL0FvM1NlYXJjaEFuYWx5c2lzL2ZhbmRvbXMvVERQL1REUF9wcm9jZXNzaW5nX25vdGVib29rLm5iLmh0bWwpDQoNCkFvMyBkYXRhIGFuYWx5c2lzIGZvciBBdmF0YXI6IExlZ2VuZCBvZiBLb3JyYQ0KDQpbQW8zIGRhdGEgYW5hbHlzaXMgZm9yIEF2YXRhcjogVGhlIExhc3QgQWlyYmVuZGVyXShodHRwczovL2RhcnRoYWxpbmUuZ2l0aHViLmlvL0FvM1NlYXJjaEFuYWx5c2lzL2ZhbmRvbXMvQVRMQS9BVExBX3Byb2Nlc3Npbmdfbm90ZWJvb2submIuaHRtbCkNCg0KW0FvMyBkYXRhIGFuYWx5c2lzIGZvciBCbGFjayBTYWlsc10oaHR0cHM6Ly9kYXJ0aGFsaW5lLmdpdGh1Yi5pby9BbzNTZWFyY2hBbmFseXNpcy9mYW5kb21zL0JTYWlscy9CU2FpbHNfcHJvY2Vzc2luZ19ub3RlYm9vay5uYi5odG1sKQ0KDQpJZiB5b3UgZW5qb3llZCBteSBhbmFseXNpcywgcGxlYXNlLCBjb25zaWRlciBbYnV5aW5nIG1lIGEgY29mZmVlXShodHRwczovL2tvLWZpLmNvbS9EMUQ4UklHNSkgb3Igc29tZSBvdGhlciBiZXZlcmFnZS4=