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).
- About 75% of works have more than a 1000 words, but only about 15% have more than 10000 words.
- Only about 50% of works have over a 1000 hits.
- Only about 25% of works have more than a 100 kudos.
- Only about 35% of works get more than 10 comments.
- Approximately 12% 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"),
get_legend(kudosPercentiles +
theme(legend.title=element_blank())))

rm(total, percentile, percentileData, wordsPercentiles, hitsPercentiles, kudosPercentiles, commentsPercentiles, bookmarksPercentiles)
Complete Work vs Work in Progress distributions
- There are approximately 3 times as many Complete Works as there are Works in Progress.
- Works in Progress are approximately 3 times longer than Complete ones.
- Both Complete Works and Works in Progress for LOK are longer than works for TDP.
- Works in Progress get approximately 30% more hits than Complete ones.
- Both Complete Works and Works in Progress get approximately the same amounts of 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).
- Works in Progress get slightly more bookmarks than Complete ones.
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
- Works rated G and T make up the majority of works, with M and E works only making approximately a third of those numbers.
- Works rated G are on average the shortest (~2500 words), followed by Not Rated works (>5000 words), T (~11000 words), E (~ 15000 words), and M (~22000 words). The trend of M rated works being the longest we observed in TDP holds up here as well.
- Not Rated works and works rated G get fewest hits (~1500). Number of hits rises with the rating. E rated works are most popular (~8500).
- Number of kudos doesn’t quite scale up with the number of hits, but unlike in TDP case E rated works also get the most kudos (~220). The difference in distribution might be due to the fact that LOK fandom on average skews older.
- Works rated M get the most comments (>50) which might correlate with them being the longest.
- Number of bookmarks doesn’t quite scale up with the number of hits, again, but it grows with the rating, reaching maximum for E rated works (~25 bookmarks).
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)
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=