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('ATLA_worksData.RData')
This is a document detailing analysis of Avatar: The Last Airbender 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(kudos, comments, bookmarks, hits)
Timeline
Solid vertical lines on the graph indicate initial air dates, and dashed ones indicate final air dates, according to Wiki article. Blue lines, similarly indicate air dates of Avatar: Legend of Korra (LOK) series according to Wiki article. Red line indicates opening of Ao3’s beta.
Avatar: the Last Airbender (ATLA) is an interesting case, because the entire show has been aired before the Ao3 was founded and open to the public. However, 373 works are posted before A03 beta was open, indicating that those were likely added to AO3 via Import tool from other fanfiction sites/archives.
After Ao3 beta opening there was a steady upward trend in popularity. After LOK release the trend vaguely follows the shape of LOK distribution. Finally, the recent high peak starts slowly at around 2018, possibly due to Blueray release of the series in June 2018, and continues up until now. It’s possible that particular sharp increase in 2020 could be related both to coronavirus social distancing measures and US Netflix release in May 2020.
#data$Timestamp <- parse_date_time2(as.character(data$Timestamp), orders = "%d/%m/%Y %H:%M:%S")
#data$day <- as.Date(data$Timestamp)
seasonsStart <- c("2005-02-21", "2006-03-17", "2007-09-21")
seasonsStart <- as.Date(seasonsStart)
seasonsEnd <- c("2005-12-02", "2006-12-01", "2008-07-18")
seasonsEnd <- as.Date(seasonsEnd)
ao3birth <- "2009-11-14"
ao3birth <- as.Date(ao3birth)
seasonsStartLOK <- c("2012-04-14", "2013-09-13", "2014-06-27", "2014-10-03")
seasonsStartLOK <- as.Date(seasonsStartLOK)
seasonsEndLOK <- c("2012-06-23", "2013-11-22", "2014-08-22", "2014-12-19")
seasonsEndLOK <- as.Date(seasonsEndLOK)
plotDatesDensityTotal <- ggplot(stats, aes(x = Date)) +
geom_density(alpha = 0.1)+
geom_vline(xintercept=seasonsStart)+
geom_vline(xintercept=seasonsEnd, linetype ="longdash")+
geom_vline(xintercept=ao3birth, col='red')+
geom_vline(xintercept=seasonsStartLOK, col='blue')+
geom_vline(xintercept=seasonsEndLOK, linetype ="longdash", col='blue')+
scale_x_date(date_breaks="12 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.
Plotting Complete Works and Works in Progress gives are similar overall shape to the total distribution, but with flatter bump around season 1 of LOK release and sharper new peak for Works in Progress. Speculatively, it’s possible that Works in Progress which were started a while back, are now updating due to social distancing, contributing to the dramatic 2020 peak.
plotDatesDensity <- ggplot(stats, aes(x = Date, col=WIP)) +
geom_density(alpha = 0.1)+
geom_vline(xintercept=seasonsStart)+
geom_vline(xintercept=seasonsEnd, linetype ="longdash")+
geom_vline(xintercept=ao3birth, col='red')+
geom_vline(xintercept=seasonsStartLOK, col='blue')+
geom_vline(xintercept=seasonsEndLOK, linetype ="longdash", col='blue')+
scale_x_date(date_breaks="12 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 40% of works have more than a 100 kudos.
- Only about 40% of works get more than 10 comments.
- Approximately 10% of works have no comments (tail end).
- Only approximately 40% of works get more than 10 bookmarks.
- Approximately 12% 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.
- Works in Progress get marginally more hits than Complete ones.
- Works in Progress get approximately 30% less kudos than Complete ones.
- Works in Progress get more than 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.
- Works rated G are on average the shortest (~3000 words), followed by Not Rated works (~7000 words), T (~12000 words), E (~ 15000 words), and M (~22000 words). The trend of M rated works being the longest holds up here as well.
- Not Rated works and works rated G get fewest hits (~2000). Number of hits rises with the rating. E rated works are most popular (~8000).
- Not Rated, G and M rated works get on average 200 kudos, while T and E rated works get ~ 300.
- T, M and E rated works get over 30 comments on average, while G rated works get only half of that.
- T rated works get the most bookmarks (>40) and G rated works get the least (<30).
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 13531 works tagged with a single category, and 2458 tagged with 2 or more (up until all 6).
‘F/M’ is the most popular category, closely followed by ‘Gen’, and then by and ‘M/M’ and ‘F/F’, with ‘No category’ works being close to ‘F/F’ numbers.
Multiple category fics strongly contribute towards ‘F/M’ count, then to ‘Gen’, ‘M/M’, and ‘F/F’, and only marginally to ‘Multi’ and ‘Other’.
singleCategorySummary <- summary(as.factor(unlist(category[unlist(lapply(category, function(x) length(x))) == 1])))
singleCategorySummary <- data.frame(Category = names(singleCategorySummary),
Number.of.Works = singleCategorySummary)
singleCategorySummary$Split <- "Single category"
multipleCategorySummary <- data.frame(Category = c('Gen', 'F/F', 'F/M', 'M/M', 'Multi', 'Other', 'No category'),
Number.of.Works = c(sum(grepl('Gen',category)),
sum(grepl('F/F',category)),
sum(grepl('F/M',category)),
sum(grepl('M/M',category)),
sum(grepl('Multi',category)),
sum(grepl('Other',category)),
sum(grepl('No category',category))) )
multipleCategorySummary$Split <- "All works"
categorySummary <- rbind(singleCategorySummary, multipleCategorySummary)
categorySummary$Category <- factor(categorySummary$Category, levels = c('Gen', 'F/F', 'F/M', 'M/M', 'Multi', 'Other', 'No category'))
categorySummary$Split <- factor(categorySummary$Split, levels = c("Single category", "All works"))
plotCategories <- ggplot(categorySummary, aes(x = Category, y = Number.of.Works)) +
geom_col(alpha=1)+
theme_half_open() +
background_grid() +
facet_wrap(.~Split) +
theme(legend.title=element_blank(),
axis.title.x = element_blank(),
axis.text.x = element_text(angle = 90, vjust = 1, hjust=1))+
labs(y="Number of Works")
plotCategories

rm(singleCategorySummary, multipleCategorySummary, categorySummary, plotCategories)
Engagement by a single category
For simplicity I’m only looking at works tagged with a single category here.
“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, “M/M” category works collect at least a third as as many hits as all others. It also yeilds most kudos and comments, closely followed by ‘Gen’, with all others being significantly less popular. ‘Gen’ and ‘M/M’ also get the most bookmarks.
statsCategory <- stats[unlist(lapply(category, function(x) length(x))) == 1,]
statsCategory$Category <- as.factor(unlist(category[unlist(lapply(category, function(x) length(x))) == 1]))
statsCategory$Category <- factor(statsCategory$Category, levels = c('Gen', 'F/F', 'F/M', 'M/M', 'Multi', 'Other', 'No category'))
statsCategory$Divisor <- unlist(lapply(statsCategory$Category, function(x) summary(statsCategory$Category)[names(summary(statsCategory$Category)) == x]))
statsCategory$Words.per.Work <- statsCategory$Words/statsCategory$Divisor
statsCategory$Hits.per.Work <- statsCategory$Hits/statsCategory$Divisor
statsCategory$Kudos.per.Work <- statsCategory$Kudos/statsCategory$Divisor
statsCategory$Comments.per.Work <- statsCategory$Comments/statsCategory$Divisor
statsCategory$Bookmarks.per.Work <- statsCategory$Bookmarks/statsCategory$Divisor
statsCategory$Works.Percent <- 1/statsCategory$Divisor
barWorksCategory <- plot_bar_color(statsCategory, 'Category', 'Rating', 'right')
barWordsCategory <- plot_col_color(statsCategory, 'Category', 'Words.per.Work', 'Rating', 'right')
barHitsCategory <- plot_col_color(statsCategory, 'Category', 'Hits.per.Work', 'Rating', 'right')
barKudosCategory <- plot_col_color(statsCategory, 'Category', 'Kudos.per.Work', 'Rating', 'right')
barCommentsCategory <- plot_col_color(statsCategory, 'Category', 'Comments.per.Work', 'Rating', 'right')
barBookmarksCategory <- plot_col_color(statsCategory, 'Category', 'Bookmarks.per.Work','Rating', 'right')
plot_grid(plot_grid( barWorksCategory + theme(legend.position="none"),
barWordsCategory + theme(legend.position="none"),
barHitsCategory + theme(legend.position="none"),
barKudosCategory + theme(legend.position="none"),
barCommentsCategory + theme(legend.position="none"),
barBookmarksCategory + theme(legend.position="none"),
align = 'hv'),
get_legend(barWorksCategory + theme(legend.title=element_blank())),
rel_widths = c(4,1))

Ratings percentages by a single category
Out of the 3 main shipping categories, in absolute numbers “F/M” has most E rated works, and “F/F” has the least. However, in relative amounts “M/M” category has more explicit works (16%) than either “F/F” or “F/M”, but not overwhelmingly so. Overall, the distributions of ratings between the categories for ATLA seem very close in ratings, unlike for LOK and especially TDP.
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
Interestingly, seasons 3 and 4 of LOK brought increase of ‘F/F’ category popularity in ATLA, likely due to works being tagged with both fandom tags. Recent peak shows high number of ‘M/M’ works, which may be due to recent rise in popularity of Zukka - ‘Sokka/Zuko (Avatar)’ relationship.
plotDatesRatingDensity <- ggplot(statsCategory, aes(x = Date, col=Category)) +
geom_density(alpha = 0.1)+
geom_vline(xintercept=seasonsStart)+
geom_vline(xintercept=seasonsEnd, linetype ="longdash")+
geom_vline(xintercept=ao3birth, col='red')+
geom_vline(xintercept=seasonsStartLOK, col='blue')+
geom_vline(xintercept=seasonsEndLOK, linetype ="longdash", col='blue')+
scale_x_date(date_breaks="12 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
Due to ATLA being an old fandom which recently got more popular it’s a little difficult to see the actual ship dynamics, however it’s clear that all of the most popular ships are recieving more attention recently, and that “Katara & Zuko (Avatar)”, “Iroh & Zuko (Avatar)”, and most noticably “Sokka/Zuko (Avatar)” are getting a lot of new works.
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")+
geom_vline(xintercept=ao3birth, col='red')+
geom_vline(xintercept=seasonsStartLOK, col='blue')+
geom_vline(xintercept=seasonsEndLOK, linetype ="longdash", col='blue')+
scale_x_date(date_breaks="12 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 2806, but number of works tagged with more than 2 fandoms is 710 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 483.
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
|
222
|
madamebomb
|
104
|
TalesOfOnyxBats
|
97
|
thesometimeswarrior
|
87
|
ArtemisRae
|
82
|
Alabaster86
|
74
|
AzarDarkstar
|
65
|
sangi
|
59
|
spiralicious
|
58
|
attackfish
|
57
|
Anonymous
|
53
|
gemsofformenos
|
53
|
Abraxas (Qlippoth)
|
47
|
Kalira
|
43
|
allywonderland
|
41
|
Caelum_Blue
|
40
|
Haicrescendo
|
40
|
Harlow R (harlowrd)
|
36
|
hopscotch_11
|
34
|
LizBee
|
34
|
silkinsilence
|
33
|
DaFishi
|
32
|
SaraJaye
|
32
|
theadamantdaughter
|
32
|
Loopy
|
31
|
FeatherQuilt88
|
30
|
Nuwiel
|
30
|
terajk
|
30
|
BetterThanCoffee
|
28
|
IrisPlumeria
|
28
|
rm(AuthorTable)
Top place is occupied by orphan_account, which is an artifact of archive’ works orphaning function.
Authors by Words
Only 208 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
|
Seyary_Minamoto
|
2401130
|
JCMorrigan
|
2038531
|
GMBlackjack
|
2027047
|
Loopy
|
1365724
|
tubendo
|
1275057
|
alwaysZutarian
|
1244271
|
madamebomb
|
1118775
|
Mr_A_Firebender
|
997375
|
TalesOfOnyxBats
|
962793
|
damagectrl
|
938451
|
orphan_account
|
932871
|
WaterLily95
|
907944
|
serendipitymadness
|
898939
|
Boogum
|
861621
|
WestOrEast
|
836567
|
grither55
|
799614
|
Gamewizard2008
|
741253
|
Vathara
|
727174
|
duvarneya
|
705828
|
Depthcharge2030
|
674775
|
Morkhan
|
670368
|
CanadaCowboy
|
662911
|
Destiny_Smasher
|
647170
|
Kimberly_T
|
613607
|
Kelseyalicia
|
580850
|
99nzhe
|
574902
|
mad_fairy
|
543098
|
the_cloud_whisperer
|
531593
|
penpaninu
|
526879
|
AvocadoLove
|
506877
|
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
|
Zuko (Avatar)
|
10046
|
Katara (Avatar)
|
6684
|
Sokka (Avatar)
|
5824
|
Aang (Avatar)
|
5123
|
Iroh (Avatar)
|
3578
|
Toph Beifong
|
3516
|
Azula (Avatar)
|
3478
|
Suki (Avatar)
|
2433
|
Mai (Avatar)
|
2318
|
Ty Lee (Avatar)
|
1809
|
Ozai (Avatar)
|
1549
|
The Gaang (Avatar)
|
1236
|
Hakoda (Avatar)
|
1024
|
Ursa (Avatar)
|
981
|
Jet (Avatar)
|
733
|
Original Characters
|
673
|
Zuko
|
633
|
Toph Bei Fong
|
624
|
Katara
|
499
|
Yue (Avatar)
|
492
|
Appa (Avatar)
|
434
|
Korra (Avatar)
|
426
|
Sokka
|
408
|
Original Female Character(s)
|
398
|
Aang
|
331
|
Lu Ten
|
327
|
Zhao (Avatar)
|
322
|
Momo (Avatar)
|
307
|
Azula
|
270
|
Asami Sato
|
269
|
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.
“Katara/Zuko (Avatar)” is the most popular relationship in ATLA. They are followed by “Sokka/Zuko (Avatar)”, and “Aang/Katara (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
|
Katara/Zuko (Avatar)
|
2966
|
Sokka/Zuko (Avatar)
|
1925
|
Aang/Katara (Avatar)
|
1536
|
Sokka/Suki (Avatar)
|
1059
|
Mai/Zuko (Avatar)
|
1040
|
Iroh & Zuko (Avatar)
|
792
|
Azula/Ty Lee (Avatar)
|
504
|
Katara & Zuko (Avatar)
|
488
|
Azula & Zuko (Avatar)
|
482
|
The Gaang & Zuko (Avatar)
|
465
|
Aang & Zuko (Avatar)
|
412
|
Sokka & Zuko (Avatar)
|
409
|
Toph Beifong & Zuko
|
360
|
Aang/Zuko (Avatar)
|
347
|
Jet/Zuko (Avatar)
|
253
|
Toph Beifong/Sokka
|
233
|
Mai/Ty Lee (Avatar)
|
221
|
Korra/Asami Sato
|
209
|
Katara & Sokka (Avatar)
|
204
|
Suki/Zuko (Avatar)
|
189
|
Aang & Katara (Avatar)
|
178
|
Sokka/Yue (Avatar)
|
176
|
Ozai/Ursa (Avatar)
|
160
|
Azula/Sokka (Avatar)
|
137
|
Aang/Toph Beifong
|
135
|
Bato/Hakoda (Avatar)
|
126
|
Ursa & Zuko (Avatar)
|
118
|
Minor or Background Relationship(s)
|
117
|
Toph Beifong & Sokka
|
117
|
Ozai & Zuko (Avatar)
|
113
|
rm(RelationshipsTable)
Languages
Unsurprisingly, most works are written in English. Apologies for U+. kable package for whatever reason murders unicode characters. The languages in question are Russian (Русский),Chinese (中文), Hebrew(עברית), and Vietnamese (Tiếng Việt).
#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
|
15661
|
<U+0420><U+0443><U+0441><U+0441><U+043A><U+0438><U+0439>
|
95
|
Français
|
80
|
Español
|
73
|
Português brasileiro
|
24
|
<U+4E2D><U+6587>
|
15
|
Italiano
|
12
|
Polski
|
12
|
Deutsch
|
5
|
Nederlands
|
5
|
Türkçe
|
3
|
<U+05E2><U+05D1><U+05E8><U+05D9><U+05EA>
|
1
|
Bahasa Indonesia
|
1
|
Magyar
|
1
|
Ti<U+1EBF>ng Vi<U+1EC7>t
|
1
|
#languagesList
#rm(LanguageTable)
LS0tDQp0aXRsZTogIkFvMyBkYXRhIGFuYWx5c2lzIGZvciBBdmF0YXI6IFRoZSBMYXN0IEFpcmJlbmRlciINCmF1dGhvcjogImRhcnRoYWxpbmUiDQpkYXRlOiAiMTAgQXVnIDIwMjAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgY29kZV9mb2xkaW5nOiAiaGlkZSINCiAgICB0b2M6IHRydWUNCi0tLQ0KDQojIEFib3V0DQoNCmBgYHtyIHNldHVwLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNClN5cy5zZXRlbnYoTEFORyA9ICJlbiIpDQojbGlicmFyeSgicnN0dWRpb2FwaSIpICN0byBncmFiIGxvY2FsIHBvc2l0aW9uIG9mIHRoZSBzY3JpcHQNCiNzZXR3ZChkaXJuYW1lKHJzdHVkaW9hcGk6OmdldEFjdGl2ZURvY3VtZW50Q29udGV4dCgpJHBhdGgpKQ0Ka25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAnLicpDQoNCiNsaWJyYXJ5KCJydmVzdCIpICMgdG8gaGFuZGxlIGh0bWwgc3R1ZmYNCg0KbGlicmFyeShsdWJyaWRhdGUpICMgdG8gaGFuZGxlIGRhdGVzDQoNCmxpYnJhcnkoZ2dwbG90MikgIyBmb3IgcGxvdHRpbmcNCmxpYnJhcnkoY293cGxvdCkgIyBmb3IgcGxvdHRpbmcNCmxpYnJhcnkoUkNvbG9yQnJld2VyKSAjIGZvciBjaG9vc2luZyBjb2xvcnMNCg0KY3VzdG9tcGFsZXR0ZSA8LSBicmV3ZXIucGFsKG49OCwgbmFtZSA9ICdEYXJrMicpDQoNCmxpYnJhcnkoa25pdHIpICMgZm9yIHRhYmxlcw0KbGlicmFyeShrYWJsZUV4dHJhKSAjIGZvciB0YWJsZXMNCg0KbGlicmFyeShsdWJyaWRhdGUpICMgZm9yIGRhdGVzDQoNCmxpYnJhcnkocGx5cikgIyBkZHBseSwgdG8gc3VtbWFyaXplIG51bWJlciBvZiB3b3JkcyBieSBhdXRob3INCg0KbG9hZCgnQVRMQV93b3Jrc0RhdGEuUkRhdGEnKQ0KDQpgYGANCg0KVGhpcyBpcyBhIGRvY3VtZW50IGRldGFpbGluZyBhbmFseXNpcyBvZiBbYHIgdGFnVmFsdWVgIEFvMyB0YWddKGh0dHBzOi8vYXJjaGl2ZW9mb3Vyb3duLm9yZy90YWdzL0F2YXRhcjolMjBUaGUlMjBMYXN0JTIwQWlyYmVuZGVyL3dvcmtzKSBkYXRhLCBjb2xsZWN0ZWQgb24gdGhlIDEwIEF1ZyAyMDIwLiBJIGhhdmVuJ3QgZmlndXJlZCBvdXQgYSB3YXkgdG8gZ2V0IG15IHNjcmFwcGVyIHRvIGxvZyBpbiBpbnRvIEFvMyAoeWV0PyBydmVzdCBzZWVtcyB0byBoYXZlIHNvbWUgdHJvdWJsZSB3aXRoIHBhZ2UgcmVkaXJlY3RzKSwgc28gcmVzdWx0cyBoZXJlIGFyZSBiYXNlZCBvbiB0aGUgd29ya3MgdmlzaWJsZSB3aXRob3V0IGF1dGhlbnRpY2F0aW9uLCB3aGljaCBsaWtlbHkgZmlsdGVycyBvdXQgcHJlZmVyZW50aWFsbHkgZXhwbGljaXQvcHJvYmxlbWFudGljIHdvcmtzIGZyb20gdGhlIHNlbGVjdGlvbi4NCg0KYGBge3IgcGxvdHRpbmdGdW5jdGlvbnMsIGNvbGxhcHNlPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQoNCnBsb3RfYmFyIDwtIGZ1bmN0aW9uIChkYXRhLCBjb2x1bW5YLCBsZWdlbmRQb3NpdGlvbikgew0KICAgIGdncGxvdChkYXRhLCBhZXNfc3RyaW5nKHggPSBjb2x1bW5YKSkgKyANCiAgICBnZW9tX2JhcihhbHBoYT0xKSsNCiAgICB0aGVtZV9oYWxmX29wZW4oKSArDQogICAgYmFja2dyb3VuZF9ncmlkKCkgKw0KICAgIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMSwgaGp1c3Q9MSkpKw0KICAgIGxhYnMoeT0iTnVtYmVyIG9mIHdvcmtzIikNCn0NCg0KcGxvdF9iYXJfY29sb3IgPC0gZnVuY3Rpb24gKGRhdGEsIGNvbHVtblgsIGNvbENvbG9yLCBsZWdlbmRQb3NpdGlvbikgew0KICAgIGdncGxvdChkYXRhLCBhZXNfc3RyaW5nKHggPSBjb2x1bW5YLCBmaWxsPWNvbENvbG9yKSkgKyANCiAgICBnZW9tX2JhcihhbHBoYT0wLjcpKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbXBhbGV0dGUpICsNCiAgICB0aGVtZV9oYWxmX29wZW4oKSArDQogICAgYmFja2dyb3VuZF9ncmlkKCkgKw0KICAgIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMSwgaGp1c3Q9MSkpKw0KICAgIGxhYnMoeT0iTnVtYmVyIG9mIHdvcmtzIikNCn0NCg0KcGxvdF9jb2wgPC0gZnVuY3Rpb24gKGRhdGEsIGNvbHVtblgsIGNvbHVtblksIGxlZ2VuZFBvc2l0aW9uKSB7DQogICAgZ2dwbG90KGRhdGEsIGFlc19zdHJpbmcoeCA9IGNvbHVtblgsIHkgPSBjb2x1bW5ZKSkgKyANCiAgICBnZW9tX2NvbChhbHBoYT0xKSsNCiAgICB0aGVtZV9oYWxmX29wZW4oKSArDQogICAgYmFja2dyb3VuZF9ncmlkKCkgKw0KICAgIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMSwgaGp1c3Q9MSkpKw0KICAgIGxhYnMoeT1nc3ViKCdcXC4nLCAnICcsIGNvbHVtblkpKQ0KICANCn0NCg0KcGxvdF9jb2xfY29sb3IgPC0gZnVuY3Rpb24gKGRhdGEsIGNvbHVtblgsIGNvbHVtblksIGNvbENvbG9yLCBsZWdlbmRQb3NpdGlvbikgew0KICAgIGdncGxvdChkYXRhLCBhZXNfc3RyaW5nKHggPSBjb2x1bW5YLCB5ID0gY29sdW1uWSwgZmlsbD1jb2xDb2xvcikpICsgDQogICAgZ2VvbV9jb2woYWxwaGE9MC43KSsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21wYWxldHRlKSArDQogICAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICAgIGJhY2tncm91bmRfZ3JpZCgpICsNCiAgICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDEsIGhqdXN0PTEpKSsNCiAgICBsYWJzKHk9Z3N1YignXFwuJywgJyAnLCBjb2x1bW5ZKSkNCiAgDQp9DQoNCnBsb3RfcGVyY2VudGlsZXMgPC0gZnVuY3Rpb24gKGRhdGEsIGNvbHVtblgsIGNvbHVtblksIGxlZ2VuZFBvc2l0aW9uKSB7DQogICAgZ2dwbG90KGRhdGEsIGFlc19zdHJpbmcoeCA9IGNvbHVtblgsIHkgPSBjb2x1bW5ZKSkgKyANCiAgICBnZW9tX3BvaW50KGFscGhhPTAuMykrDQogICAgc2NhbGVfeV9sb2cxMChicmVha3MgPSAxMF5jKDA6MTUpKSsNCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygwLCAyNSwgNTAsIDc1LCAxMDApKSsgI3NjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKDA6MTApKjEwKSsNCiAgICB0aGVtZV9oYWxmX29wZW4oKSArDQogICAgYmFja2dyb3VuZF9ncmlkKCkgKw0KICAgIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCkpKw0KICAgIGxhYnMoeD1nc3ViKCdcXC4nLCAnICcsIGNvbHVtblgpKQ0KfQ0KDQpgYGANCg0KYGBge3IgZmxhdHRlbmluZ0RhdGEsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZz1GQUxTRX0NCiN0aXRsZSA8LSBsYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRUaXRsZX0pDQphdXRob3IgPC0gbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkQXV0aG9yfSkNCmZhbmRvbSA8LSBsYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRGYW5kb219KQ0KcmF0aW5nIDwtIGxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JFJhdGluZ30pDQp3YXJuaW5ncyA8LSBsYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRXYXJuaW5nc30pDQpjYXRlZ29yeSA8LSBsYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRDYXRlZ29yeX0pDQpXSVAgPC0gbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkV0lQfSkNCmRhdGUgPC1sYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCREYXRlfSkNCnJlbGF0aW9uc2hpcHMgPC1sYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRSZWxhdGlvbnNoaXBzfSkNCmNoYXJhY3RlciA8LWxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JENoYXJhY3Rlcn0pDQpmcmVlZm9ybSA8LWxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JEZyZWVmb3JtfSkNCmxhbmd1YWdlIDwtbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkTGFuZ3VhZ2V9KQ0Kd29yZHMgPC1sYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRXb3Jkc30pDQp3b3Jkc1tpcy5uYSh3b3JkcyldIDwtIDANCmt1ZG9zIDwtbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkS3Vkb3N9KQ0Ka3Vkb3NbaXMubmEoa3Vkb3MpXSA8LSAwDQpjb21tZW50cyA8LWxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JENvbW1lbnRzfSkNCmNvbW1lbnRzW2lzLm5hKGNvbW1lbnRzKV0gPC0gMA0KYm9va21hcmtzPC1sYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRCb29rbWFya3N9KQ0KYm9va21hcmtzW2lzLm5hKGJvb2ttYXJrcyldIDwtIDANCmhpdHMgPC1sYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRIaXRzfSkNCmhpdHNbaXMubmEoaGl0cyldIDwtIDANCg0Kc3RhdHMgPC0gZGF0YS5mcmFtZShXb3JkcyA9IHVubGlzdCh3b3JkcywgcmVjdXJzaXZlID0gRkFMU0UpLA0KICAgICAgICAgICAgICAgICAgICBDb21tZW50cz0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoY29tbWVudHMpKSwNCiAgICAgICAgICAgICAgICAgICAgS3Vkb3MgPSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihrdWRvcykpLA0KICAgICAgICAgICAgICAgICAgICBCb29rbWFya3MgPSBhcy5udW1lcmljKGFzLmNoYXJhY3Rlcihib29rbWFya3MpKSwNCiAgICAgICAgICAgICAgICAgICAgSGl0cyA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGhpdHMpKSwNCiAgICAgICAgICAgICAgICAgICAgV0lQID0gdW5saXN0KFdJUCwgcmVjdXJzaXZlID0gRkFMU0UpLA0KICAgICAgICAgICAgICAgICAgICBSYXRpbmcgPSB1bmxpc3QocmF0aW5nLCByZWN1cnNpdmUgPSBGQUxTRSksDQogICAgICAgICAgICAgICAgICAgIERhdGUgPSBkby5jYWxsKCJjIiwgZGF0ZSkpDQoNCnN0YXRzJFJhdGluZyA8LSBmYWN0b3Ioc3RhdHMkUmF0aW5nLCBsZXZlbHMgPSBjKCJOb3QgUmF0ZWQiLCAiR2VuZXJhbCBBdWRpZW5jZXMiLCAiVGVlbiBBbmQgVXAgQXVkaWVuY2VzIiwgIk1hdHVyZSIsICJFeHBsaWNpdCIpKQ0KDQp0b3RhbCA8LSAxMDAwDQpwZXJjZW50aWxlIDwtIGMoMTp0b3RhbCkNCnBlcmNlbnRpbGVEYXRhIDwtIGRhdGEuZnJhbWUoV29ya3MuUGVyY2VudGlsZSA9IDEwMCoodG90YWwgLSBwZXJjZW50aWxlKS90b3RhbCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV29yZHMgPSB1bmxpc3QobGFwcGx5KHBlcmNlbnRpbGUvdG90YWwsIHF1YW50aWxlLCB4ID0gdW5saXN0KHdvcmRzKSApKSArIDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhpdHMgPSB1bmxpc3QobGFwcGx5KHBlcmNlbnRpbGUvdG90YWwsIHF1YW50aWxlLCB4ID0gdW5saXN0KGhpdHMpICkpICsgMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgS3Vkb3MgPSB1bmxpc3QobGFwcGx5KHBlcmNlbnRpbGUvdG90YWwsIHF1YW50aWxlLCB4ID0gdW5saXN0KGt1ZG9zKSApKSArIDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIENvbW1lbnRzID0gdW5saXN0KGxhcHBseShwZXJjZW50aWxlL3RvdGFsLCBxdWFudGlsZSwgeCA9IHVubGlzdChjb21tZW50cykgKSkgKyAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCb29rbWFya3MgPSB1bmxpc3QobGFwcGx5KHBlcmNlbnRpbGUvdG90YWwsIHF1YW50aWxlLCB4ID0gdW5saXN0KGJvb2ttYXJrcykgKSkgKyAxICkNCg0Kcm0oa3Vkb3MsIGNvbW1lbnRzLCBib29rbWFya3MsIGhpdHMpDQoNCmBgYA0KDQojIFRpbWVsaW5lDQoNClNvbGlkIHZlcnRpY2FsIGxpbmVzIG9uIHRoZSBncmFwaCBpbmRpY2F0ZSBpbml0aWFsIGFpciBkYXRlcywgYW5kIGRhc2hlZCBvbmVzIGluZGljYXRlIGZpbmFsIGFpciBkYXRlcywgYWNjb3JkaW5nIHRvIFtXaWtpIGFydGljbGVdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0F2YXRhcjpfVGhlX0xhc3RfQWlyYmVuZGVyI0VwaXNvZGVzKS4gQmx1ZSBsaW5lcywgc2ltaWxhcmx5IGluZGljYXRlIGFpciBkYXRlcyBvZiBBdmF0YXI6IExlZ2VuZCBvZiBLb3JyYSAoTE9LKSBzZXJpZXMgYWNjb3JkaW5nIHRvIFtXaWtpIGFydGljbGVdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1RoZV9MZWdlbmRfb2ZfS29ycmEjU2VyaWVzX292ZXJ2aWV3KS4gUmVkIGxpbmUgaW5kaWNhdGVzIG9wZW5pbmcgb2YgW0FvM10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQXJjaGl2ZV9vZl9PdXJfT3duKSdzIGJldGEuDQoNCkF2YXRhcjogdGhlIExhc3QgQWlyYmVuZGVyIChBVExBKSBpcyBhbiBpbnRlcmVzdGluZyBjYXNlLCBiZWNhdXNlIHRoZSBlbnRpcmUgc2hvdyBoYXMgYmVlbiBhaXJlZCBiZWZvcmUgdGhlIEFvMyB3YXMgZm91bmRlZCBhbmQgb3BlbiB0byB0aGUgcHVibGljLiBIb3dldmVyLCBgciBzdW0odW5saXN0KGRhdGUpIDwgYW8zYmlydGgpYCB3b3JrcyBhcmUgcG9zdGVkIGJlZm9yZSBBMDMgYmV0YSB3YXMgb3BlbiwgaW5kaWNhdGluZyB0aGF0IHRob3NlIHdlcmUgbGlrZWx5IGFkZGVkIHRvIEFPMyB2aWEgSW1wb3J0IHRvb2wgZnJvbSBvdGhlciBmYW5maWN0aW9uIHNpdGVzL2FyY2hpdmVzLg0KDQpBZnRlciBBbzMgYmV0YSBvcGVuaW5nIHRoZXJlIHdhcyBhIHN0ZWFkeSB1cHdhcmQgdHJlbmQgaW4gcG9wdWxhcml0eS4gQWZ0ZXIgTE9LIHJlbGVhc2UgdGhlIHRyZW5kIHZhZ3VlbHkgZm9sbG93cyB0aGUgc2hhcGUgb2YgTE9LIGRpc3RyaWJ1dGlvbi4gRmluYWxseSwgdGhlIHJlY2VudCBoaWdoIHBlYWsgc3RhcnRzIHNsb3dseSBhdCBhcm91bmQgMjAxOCwgcG9zc2libHkgZHVlIHRvIEJsdWVyYXkgcmVsZWFzZSBvZiB0aGUgc2VyaWVzIGluIEp1bmUgMjAxOCwgYW5kIGNvbnRpbnVlcyB1cCB1bnRpbCBub3cuIEl0J3MgcG9zc2libGUgdGhhdCBwYXJ0aWN1bGFyIHNoYXJwIGluY3JlYXNlIGluIDIwMjAgY291bGQgYmUgcmVsYXRlZCBib3RoIHRvIGNvcm9uYXZpcnVzIHNvY2lhbCBkaXN0YW5jaW5nIG1lYXN1cmVzIGFuZCBVUyBOZXRmbGl4IHJlbGVhc2UgaW4gTWF5IDIwMjAuDQoNCmBgYHtyIHRpbWVsaW5lVG90YWwsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTZ9DQoNCiNkYXRhJFRpbWVzdGFtcCA8LSBwYXJzZV9kYXRlX3RpbWUyKGFzLmNoYXJhY3RlcihkYXRhJFRpbWVzdGFtcCksIG9yZGVycyA9ICIlZC8lbS8lWSAlSDolTTolUyIpDQojZGF0YSRkYXkgPC0gYXMuRGF0ZShkYXRhJFRpbWVzdGFtcCkNCg0Kc2Vhc29uc1N0YXJ0IDwtIGMoIjIwMDUtMDItMjEiLCAiMjAwNi0wMy0xNyIsICIyMDA3LTA5LTIxIikNCnNlYXNvbnNTdGFydCA8LSBhcy5EYXRlKHNlYXNvbnNTdGFydCkNCnNlYXNvbnNFbmQgPC0gYygiMjAwNS0xMi0wMiIsICIyMDA2LTEyLTAxIiwgIjIwMDgtMDctMTgiKQ0Kc2Vhc29uc0VuZCA8LSBhcy5EYXRlKHNlYXNvbnNFbmQpDQoNCmFvM2JpcnRoIDwtICIyMDA5LTExLTE0Ig0KYW8zYmlydGggPC0gYXMuRGF0ZShhbzNiaXJ0aCkNCg0Kc2Vhc29uc1N0YXJ0TE9LIDwtIGMoIjIwMTItMDQtMTQiLCAiMjAxMy0wOS0xMyIsICIyMDE0LTA2LTI3IiwgIjIwMTQtMTAtMDMiKQ0Kc2Vhc29uc1N0YXJ0TE9LIDwtIGFzLkRhdGUoc2Vhc29uc1N0YXJ0TE9LKQ0Kc2Vhc29uc0VuZExPSyA8LSBjKCIyMDEyLTA2LTIzIiwgIjIwMTMtMTEtMjIiLCAiMjAxNC0wOC0yMiIsICIyMDE0LTEyLTE5IikNCnNlYXNvbnNFbmRMT0sgPC0gYXMuRGF0ZShzZWFzb25zRW5kTE9LKQ0KDQpwbG90RGF0ZXNEZW5zaXR5VG90YWwgPC0gZ2dwbG90KHN0YXRzLCBhZXMoeCA9IERhdGUpKSArIA0KICAgICAgICAgICAgICAgICAgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjEpKw0KICAgICAgICAgICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9c2Vhc29uc1N0YXJ0KSsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PXNlYXNvbnNFbmQsIGxpbmV0eXBlID0ibG9uZ2Rhc2giKSsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PWFvM2JpcnRoLCBjb2w9J3JlZCcpKw0KICAgICAgICAgICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9c2Vhc29uc1N0YXJ0TE9LLCBjb2w9J2JsdWUnKSsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PXNlYXNvbnNFbmRMT0ssIGxpbmV0eXBlID0ibG9uZ2Rhc2giLCBjb2w9J2JsdWUnKSsNCiAgICAgICAgICAgICAgICAgICAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzPSIxMiBtb250aHMiKSsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICAgICAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgICAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gJ3JpZ2h0JykNCnBsb3REYXRlc0RlbnNpdHlUb3RhbA0KDQpybShwbG90RGF0ZXNEZW5zaXR5VG90YWwpDQpgYGANCg0KSSBjb2xsZWN0IGRhdGEgZnJvbSB0aGUgQW8zIHNlYXJjaCBwYWdlIChyYXRoZXIgdGhhbiB3b3JrcyBwYWdlcywgYXMgaXQncyBsZXNzIGRpc3J1cHRpdmUgdG8gc2l0ZSdzIGZ1bmN0aW9uKSwgc28gSSBkb24ndCBoYXZlIGFjY2VzcyB0byBpbml0aWFsIHBvc3RhZ2UgZGF0ZXMsIG9ubHkgdGhlIGxhdGVzdCB1cGRhdGVzLiBUaGlzIG1lYW5zIHRoYXQgdGhlIHVwd2FyZCB0cmVuZCBpbiB3b3JrcyBvdmVyIHRpbWUgY2FuIGJlIGFuIGFydGlmYWN0IG9mIHNlcmllcyBnZXR0aW5nIG1vcmUgcG9wdWxhciwgYnV0IGFsc28gY291bGQgYmUgYXR0cmlidXRlZCB0byBtdWx0aWNoYXB0ZXIgd29ya3MgZHJpZnRpbmcgZnVydGhlciBpbiB0aW1lIGR1ZSB0byB1cGRhdGVzLg0KDQpQbG90dGluZyBDb21wbGV0ZSBXb3JrcyBhbmQgV29ya3MgaW4gUHJvZ3Jlc3MgZ2l2ZXMgYXJlIHNpbWlsYXIgb3ZlcmFsbCBzaGFwZSB0byB0aGUgdG90YWwgZGlzdHJpYnV0aW9uLCBidXQgd2l0aCBmbGF0dGVyIGJ1bXAgYXJvdW5kIHNlYXNvbiAxIG9mIExPSyByZWxlYXNlIGFuZCBzaGFycGVyIG5ldyBwZWFrIGZvciBXb3JrcyBpbiBQcm9ncmVzcy4gU3BlY3VsYXRpdmVseSwgaXQncyBwb3NzaWJsZSB0aGF0IFdvcmtzIGluIFByb2dyZXNzIHdoaWNoIHdlcmUgc3RhcnRlZCBhIHdoaWxlIGJhY2ssIGFyZSBub3cgdXBkYXRpbmcgZHVlIHRvIHNvY2lhbCBkaXN0YW5jaW5nLCBjb250cmlidXRpbmcgdG8gdGhlIGRyYW1hdGljIDIwMjAgcGVhay4NCg0KYGBge3IgdGltZWxpbmVXSVAsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTZ9DQoNCnBsb3REYXRlc0RlbnNpdHkgPC0gZ2dwbG90KHN0YXRzLCBhZXMoeCA9IERhdGUsIGNvbD1XSVApKSArIA0KICAgICAgICAgICAgICAgICAgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjEpKw0KICAgICAgICAgICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9c2Vhc29uc1N0YXJ0KSsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PXNlYXNvbnNFbmQsIGxpbmV0eXBlID0ibG9uZ2Rhc2giKSsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PWFvM2JpcnRoLCBjb2w9J3JlZCcpKw0KICAgICAgICAgICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9c2Vhc29uc1N0YXJ0TE9LLCBjb2w9J2JsdWUnKSsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PXNlYXNvbnNFbmRMT0ssIGxpbmV0eXBlID0ibG9uZ2Rhc2giLCBjb2w9J2JsdWUnKSsNCiAgICAgICAgICAgICAgICAgICAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzPSIxMiBtb250aHMiKSsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICAgICAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgICAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gJ3JpZ2h0JykNCnBsb3REYXRlc0RlbnNpdHkNCg0Kcm0ocGxvdERhdGVzRGVuc2l0eSkNCg0KYGBgDQoNCiMgRW5nYWdlbWVudCBwZXJjZW50aWxlcw0KDQpTbWFsbCBwbG90dGluZyBjaGVhdDogYWxsIHRoZSBudW1iZXJzIG9uIHRoZSBZIGF4aXMgYXJlIGluY3JlYXNlZCBieSAxIHRvIGluY2x1ZGUgdGhlIGNhc2Ugb2YgMCBpbnRvIHRoZSBwbG90IChvdGhlcndpc2UgZXhjbHVkZWQgYmVjYXVzZSBvZiBsb2cgc2NhbGUpLg0KDQoqIEFib3V0IDc1JSBvZiB3b3JrcyBoYXZlIG1vcmUgdGhhbiBhIDEwMDAgd29yZHMsIGJ1dCBvbmx5IGFib3V0IDE1JSBoYXZlIG1vcmUgdGhhbiAxMDAwMCB3b3Jkcy4NCiogT25seSBhYm91dCA1MCUgb2Ygd29ya3MgaGF2ZSBvdmVyIGEgMTAwMCBoaXRzLg0KKiBPbmx5IGFib3V0IDQwJSBvZiB3b3JrcyBoYXZlIG1vcmUgdGhhbiBhIDEwMCBrdWRvcy4NCiogT25seSBhYm91dCA0MCUgb2Ygd29ya3MgZ2V0IG1vcmUgdGhhbiAxMCBjb21tZW50cy4NCiogQXBwcm94aW1hdGVseSAxMCUgb2Ygd29ya3MgaGF2ZSBubyBjb21tZW50cyAodGFpbCBlbmQpLg0KKiBPbmx5IGFwcHJveGltYXRlbHkgNDAlIG9mIHdvcmtzIGdldCBtb3JlIHRoYW4gMTAgYm9va21hcmtzLg0KKiBBcHByb3hpbWF0ZWx5IDEyJSBvZiB3b3JrcyBoYXZlIG5vIGJvb2ttYXJrcyAodGFpbCBlbmQpLg0KDQpgYGB7ciBwZXJjZW50aWxlcywgbWVzc2FnZSA9IEZBTFNFfQ0Kd29yZHNQZXJjZW50aWxlcyA8LSBwbG90X3BlcmNlbnRpbGVzKHBlcmNlbnRpbGVEYXRhLCAnV29ya3MuUGVyY2VudGlsZScsICdXb3JkcycsICdyaWdodCcpDQpoaXRzUGVyY2VudGlsZXMgPC0gcGxvdF9wZXJjZW50aWxlcyhwZXJjZW50aWxlRGF0YSwgJ1dvcmtzLlBlcmNlbnRpbGUnLCAnSGl0cycsICdyaWdodCcpDQprdWRvc1BlcmNlbnRpbGVzIDwtIHBsb3RfcGVyY2VudGlsZXMocGVyY2VudGlsZURhdGEsICdXb3Jrcy5QZXJjZW50aWxlJywgJ0t1ZG9zJywgJ3JpZ2h0JykNCmNvbW1lbnRzUGVyY2VudGlsZXMgPC0gcGxvdF9wZXJjZW50aWxlcyhwZXJjZW50aWxlRGF0YSwgJ1dvcmtzLlBlcmNlbnRpbGUnLCAnQ29tbWVudHMnLCAncmlnaHQnKQ0KYm9va21hcmtzUGVyY2VudGlsZXMgPC0gcGxvdF9wZXJjZW50aWxlcyhwZXJjZW50aWxlRGF0YSwgJ1dvcmtzLlBlcmNlbnRpbGUnLCAnQm9va21hcmtzJywgJ3JpZ2h0JykNCg0KcGxvdF9ncmlkKHdvcmRzUGVyY2VudGlsZXMgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICBoaXRzUGVyY2VudGlsZXMgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICBrdWRvc1BlcmNlbnRpbGVzICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgY29tbWVudHNQZXJjZW50aWxlcyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgIGJvb2ttYXJrc1BlcmNlbnRpbGVzICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgZ2V0X2xlZ2VuZChrdWRvc1BlcmNlbnRpbGVzICsNCiAgICAgICAgICAgICAgICAgICAgIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCkpKSkNCg0Kcm0odG90YWwsIHBlcmNlbnRpbGUsIHBlcmNlbnRpbGVEYXRhLCB3b3Jkc1BlcmNlbnRpbGVzLCBoaXRzUGVyY2VudGlsZXMsIGt1ZG9zUGVyY2VudGlsZXMsIGNvbW1lbnRzUGVyY2VudGlsZXMsIGJvb2ttYXJrc1BlcmNlbnRpbGVzKQ0KDQpgYGANCg0KIyBDb21wbGV0ZSBXb3JrIHZzIFdvcmsgaW4gUHJvZ3Jlc3MgZGlzdHJpYnV0aW9ucw0KDQoqIFRoZXJlIGFyZSBhcHByb3hpbWF0ZWx5IDMgdGltZXMgYXMgbWFueSBDb21wbGV0ZSBXb3JrcyBhcyB0aGVyZSBhcmUgV29ya3MgaW4gUHJvZ3Jlc3MuDQoqIFdvcmtzIGluIFByb2dyZXNzIGFyZSBhcHByb3hpbWF0ZWx5IDMgdGltZXMgbG9uZ2VyIHRoYW4gQ29tcGxldGUgb25lcy4NCiogV29ya3MgaW4gUHJvZ3Jlc3MgZ2V0IG1hcmdpbmFsbHkgbW9yZSBoaXRzIHRoYW4gQ29tcGxldGUgb25lcy4NCiogV29ya3MgaW4gUHJvZ3Jlc3MgZ2V0IGFwcHJveGltYXRlbHkgMzAlIGxlc3Mga3Vkb3MgdGhhbiBDb21wbGV0ZSBvbmVzLg0KKiBXb3JrcyBpbiBQcm9ncmVzcyBnZXQgbW9yZSB0aGFuIDIgdGltZXMgYXMgbWFueSBjb21tZW50cyBhcyBDb21wbGV0ZSBvbmVzIChob3dldmVyLCBhZ2FpbiwgdGhlcmUncyBubyB3YXkgdG8gZmlsdGVyIG91dCBhdXRob3IncyBjb21tZW50cyBpbiB0aGUgc2VhcmNoIHNlbGVjdGlvbikuDQoqIFdvcmtzIGluIFByb2dyZXNzIGdldCBzbGlnaHRseSBtb3JlIGJvb2ttYXJrcyB0aGFuIENvbXBsZXRlIG9uZXMuDQoNCmBgYHtyIHRvdGFsV29ya3NXSVAsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Nn0NCg0Kc3RhdHNXSVAgPC0gc3RhdHMNCnN0YXRzV0lQJERpdmlzb3IgPC0gdW5saXN0KGxhcHBseShzdGF0c1dJUCRXSVAsIGZ1bmN0aW9uKHgpIHN1bW1hcnkoc3RhdHNXSVAkV0lQKVtuYW1lcyhzdW1tYXJ5KHN0YXRzV0lQJFdJUCkpID09IHhdKSkNCnN0YXRzV0lQJFdvcmRzLnBlci5Xb3JrIDwtIHN0YXRzV0lQJFdvcmRzL3N0YXRzV0lQJERpdmlzb3INCnN0YXRzV0lQJEhpdHMucGVyLldvcmsgPC0gc3RhdHNXSVAkSGl0cy9zdGF0c1dJUCREaXZpc29yDQpzdGF0c1dJUCRLdWRvcy5wZXIuV29yayA8LSBzdGF0c1dJUCRLdWRvcy9zdGF0c1dJUCREaXZpc29yDQpzdGF0c1dJUCRDb21tZW50cy5wZXIuV29yayA8LSBzdGF0c1dJUCRDb21tZW50cy9zdGF0c1dJUCREaXZpc29yDQpzdGF0c1dJUCRCb29rbWFya3MucGVyLldvcmsgPC0gc3RhdHNXSVAkQm9va21hcmtzL3N0YXRzV0lQJERpdmlzb3INCg0KYmFyV29ya3NXSVAgPC0gcGxvdF9iYXIoc3RhdHNXSVAsICdXSVAnLCAncmlnaHQnKQ0KYmFyV29yZHNXSVAgPC0gcGxvdF9jb2woc3RhdHNXSVAsICdXSVAnLCAnV29yZHMucGVyLldvcmsnLCAncmlnaHQnKQ0KYmFySGl0c1dJUCA8LSBwbG90X2NvbChzdGF0c1dJUCwgJ1dJUCcsICdIaXRzLnBlci5Xb3JrJywgJ3JpZ2h0JykNCmJhckt1ZG9zV0lQIDwtIHBsb3RfY29sKHN0YXRzV0lQLCAnV0lQJywgJ0t1ZG9zLnBlci5Xb3JrJywgJ3JpZ2h0JykNCmJhckNvbW1lbnRzV0lQIDwtIHBsb3RfY29sKHN0YXRzV0lQLCAnV0lQJywgJ0NvbW1lbnRzLnBlci5Xb3JrJywgJ3JpZ2h0JykNCmJhckJvb2ttYXJrc1dJUCA8LSBwbG90X2NvbChzdGF0c1dJUCwgJ1dJUCcsICdCb29rbWFya3MucGVyLldvcmsnLCAncmlnaHQnKQ0KDQojIHBsb3RfZ3JpZChwbG90X2dyaWQoIGJhcldvcmtzV0lQICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQojICAgICAgICAgICAgICAgICAgICAgIGJhcldvcmRzV0lQICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQojICAgICAgICAgICAgICAgICAgICAgIGJhckhpdHNXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiMgICAgICAgICAgICAgICAgICAgICAgYmFyS3Vkb3NXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiMgICAgICAgICAgICAgICAgICAgICAgYmFyQ29tbWVudHNXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiMgICAgICAgICAgICAgICAgICAgICAgYmFyQm9va21hcmtzV0lQICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQojICAgICAgICAgICAgICAgICAgICAgIGFsaWduID0gJ2h2JyksDQojICAgICAgICAgICBnZXRfbGVnZW5kKGJhcldvcmtzV0lQICsgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSkpLA0KIyAgICAgICAgICAgcmVsX3dpZHRocyA9IGMoNCwxKSwNCiMgICAgICAgICAgIGFsaWduID0gJ2h2JykNCnBsb3RfZ3JpZCggYmFyV29ya3NXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYmFyV29yZHNXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYmFySGl0c1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJLdWRvc1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJDb21tZW50c1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJCb29rbWFya3NXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYWxpZ24gPSAnaHYnKQ0KDQpybShzdGF0c1dJUCwgYmFyV29ya3NXSVAsIGJhcldvcmRzV0lQLCBiYXJIaXRzV0lQLCBiYXJLdWRvc1dJUCwgYmFyQ29tbWVudHNXSVAsIGJhckJvb2ttYXJrc1dJUCkNCmBgYA0KDQojIFJhdGluZyBkaXN0cmlidXRpb25zDQoNCiogV29ya3MgcmF0ZWQgRyBhbmQgVCBtYWtlIHVwIHRoZSBtYWpvcml0eSBvZiB3b3Jrcy4NCiogV29ya3MgcmF0ZWQgRyBhcmUgb24gYXZlcmFnZSB0aGUgc2hvcnRlc3QgKH4zMDAwIHdvcmRzKSwgZm9sbG93ZWQgYnkgTm90IFJhdGVkIHdvcmtzICh+NzAwMCB3b3JkcyksIFQgKH4xMjAwMCB3b3JkcyksIEUgKH4gMTUwMDAgd29yZHMpLCBhbmQgTSAofjIyMDAwIHdvcmRzKS4gVGhlIHRyZW5kIG9mIE0gcmF0ZWQgd29ya3MgYmVpbmcgdGhlIGxvbmdlc3QgaG9sZHMgdXAgaGVyZSBhcyB3ZWxsLg0KKiBOb3QgUmF0ZWQgd29ya3MgYW5kIHdvcmtzIHJhdGVkIEcgZ2V0IGZld2VzdCBoaXRzICh+MjAwMCkuIE51bWJlciBvZiBoaXRzIHJpc2VzIHdpdGggdGhlIHJhdGluZy4gRSByYXRlZCB3b3JrcyBhcmUgbW9zdCBwb3B1bGFyICh+ODAwMCkuDQoqIE5vdCBSYXRlZCwgRyBhbmQgTSByYXRlZCB3b3JrcyBnZXQgb24gYXZlcmFnZSAyMDAga3Vkb3MsIHdoaWxlIFQgYW5kIEUgcmF0ZWQgd29ya3MgZ2V0IH4gMzAwLg0KKiBULCBNIGFuZCBFIHJhdGVkIHdvcmtzIGdldCBvdmVyIDMwIGNvbW1lbnRzIG9uIGF2ZXJhZ2UsIHdoaWxlIEcgcmF0ZWQgd29ya3MgZ2V0IG9ubHkgaGFsZiBvZiB0aGF0Lg0KKiBUIHJhdGVkIHdvcmtzIGdldCB0aGUgbW9zdCBib29rbWFya3MgKD40MCkgYW5kIEcgcmF0ZWQgd29ya3MgZ2V0IHRoZSBsZWFzdCAoPDMwKS4NCg0KDQpgYGB7ciB0b3RhbFdvcmtzUmF0aW5nLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9DQoNCnN0YXRzUmF0aW5nIDwtIHN0YXRzDQpzdGF0c1JhdGluZyREaXZpc29yIDwtIHVubGlzdChsYXBwbHkoc3RhdHNSYXRpbmckUmF0aW5nLCBmdW5jdGlvbih4KSBzdW1tYXJ5KHN0YXRzUmF0aW5nJFJhdGluZylbbmFtZXMoc3VtbWFyeShzdGF0c1JhdGluZyRSYXRpbmcpKSA9PSB4XSkpDQpzdGF0c1JhdGluZyRXb3Jkcy5wZXIuV29yayA8LSBzdGF0c1JhdGluZyRXb3Jkcy9zdGF0c1JhdGluZyREaXZpc29yDQpzdGF0c1JhdGluZyRIaXRzLnBlci5Xb3JrIDwtIHN0YXRzUmF0aW5nJEhpdHMvc3RhdHNSYXRpbmckRGl2aXNvcg0Kc3RhdHNSYXRpbmckS3Vkb3MucGVyLldvcmsgPC0gc3RhdHNSYXRpbmckS3Vkb3Mvc3RhdHNSYXRpbmckRGl2aXNvcg0Kc3RhdHNSYXRpbmckQ29tbWVudHMucGVyLldvcmsgPC0gc3RhdHNSYXRpbmckQ29tbWVudHMvc3RhdHNSYXRpbmckRGl2aXNvcg0Kc3RhdHNSYXRpbmckQm9va21hcmtzLnBlci5Xb3JrIDwtIHN0YXRzUmF0aW5nJEJvb2ttYXJrcy9zdGF0c1JhdGluZyREaXZpc29yDQoNCmJhcldvcmtzUmF0aW5nIDwtIHBsb3RfYmFyKHN0YXRzUmF0aW5nLCAnUmF0aW5nJywgJ3JpZ2h0JykNCmJhcldvcmRzUmF0aW5nIDwtIHBsb3RfY29sKHN0YXRzUmF0aW5nLCAnUmF0aW5nJywgJ1dvcmRzLnBlci5Xb3JrJywgJ3JpZ2h0JykNCmJhckhpdHNSYXRpbmcgPC0gcGxvdF9jb2woc3RhdHNSYXRpbmcsICdSYXRpbmcnLCAnSGl0cy5wZXIuV29yaycsICdyaWdodCcpDQpiYXJLdWRvc1JhdGluZyA8LSBwbG90X2NvbChzdGF0c1JhdGluZywgJ1JhdGluZycsICdLdWRvcy5wZXIuV29yaycsICdyaWdodCcpDQpiYXJDb21tZW50c1JhdGluZyA8LSBwbG90X2NvbChzdGF0c1JhdGluZywgJ1JhdGluZycsICdDb21tZW50cy5wZXIuV29yaycsICdyaWdodCcpDQpiYXJCb29rbWFya3NSYXRpbmcgPC0gcGxvdF9jb2woc3RhdHNSYXRpbmcsICdSYXRpbmcnLCAnQm9va21hcmtzLnBlci5Xb3JrJywgJ3JpZ2h0JykNCg0KcGxvdF9ncmlkKCBiYXJXb3Jrc1JhdGluZyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJXb3Jkc1JhdGluZyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJIaXRzUmF0aW5nICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckt1ZG9zUmF0aW5nICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckNvbW1lbnRzUmF0aW5nICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckJvb2ttYXJrc1JhdGluZyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBhbGlnbiA9ICdodicpDQoNCnJtKHN0YXRzUmF0aW5nLCBiYXJXb3Jrc1JhdGluZywgYmFyV29yZHNSYXRpbmcsIGJhckhpdHNSYXRpbmcsIGJhckt1ZG9zUmF0aW5nLCBiYXJDb21tZW50c1JhdGluZywgYmFyQm9va21hcmtzUmF0aW5nKQ0KYGBgDQoNCiMgQ2F0ZWdvcmllcw0KDQpUaGVyZSBhcmUgYHIgbGVuZ3RoKGNhdGVnb3J5W3VubGlzdChsYXBwbHkoY2F0ZWdvcnksIGZ1bmN0aW9uKHgpIGxlbmd0aCh4KSkpID09IDFdKWAgd29ya3MgdGFnZ2VkIHdpdGggYSBzaW5nbGUgY2F0ZWdvcnksIGFuZCBgciBsZW5ndGgoY2F0ZWdvcnlbdW5saXN0KGxhcHBseShjYXRlZ29yeSwgZnVuY3Rpb24oeCkgbGVuZ3RoKHgpKSkgPiAxXSlgIHRhZ2dlZCB3aXRoIDIgb3IgbW9yZSAodXAgdW50aWwgYWxsIDYpLg0KDQonRi9NJyBpcyB0aGUgbW9zdCBwb3B1bGFyIGNhdGVnb3J5LCBjbG9zZWx5IGZvbGxvd2VkIGJ5ICdHZW4nLCBhbmQgdGhlbiBieSBhbmQgJ00vTScgYW5kICdGL0YnLCB3aXRoICdObyBjYXRlZ29yeScgd29ya3MgYmVpbmcgY2xvc2UgdG8gJ0YvRicgbnVtYmVycy4NCg0KTXVsdGlwbGUgY2F0ZWdvcnkgZmljcyBzdHJvbmdseSBjb250cmlidXRlIHRvd2FyZHMgJ0YvTScgY291bnQsIHRoZW4gdG8gJ0dlbicsICdNL00nLCBhbmQgJ0YvRicsIGFuZCBvbmx5IG1hcmdpbmFsbHkgdG8gJ011bHRpJyBhbmQgJ090aGVyJy4NCg0KYGBge3IgY2F0ZWdvcmllc0JhcnMsIG1lc3NhZ2UgPSBGQUxTRX0NCg0Kc2luZ2xlQ2F0ZWdvcnlTdW1tYXJ5IDwtIHN1bW1hcnkoYXMuZmFjdG9yKHVubGlzdChjYXRlZ29yeVt1bmxpc3QobGFwcGx5KGNhdGVnb3J5LCBmdW5jdGlvbih4KSBsZW5ndGgoeCkpKSA9PSAxXSkpKQ0Kc2luZ2xlQ2F0ZWdvcnlTdW1tYXJ5IDwtIGRhdGEuZnJhbWUoQ2F0ZWdvcnkgPSBuYW1lcyhzaW5nbGVDYXRlZ29yeVN1bW1hcnkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVtYmVyLm9mLldvcmtzID0gc2luZ2xlQ2F0ZWdvcnlTdW1tYXJ5KQ0Kc2luZ2xlQ2F0ZWdvcnlTdW1tYXJ5JFNwbGl0IDwtICJTaW5nbGUgY2F0ZWdvcnkiDQoNCm11bHRpcGxlQ2F0ZWdvcnlTdW1tYXJ5IDwtIGRhdGEuZnJhbWUoQ2F0ZWdvcnkgPSBjKCdHZW4nLCAnRi9GJywgJ0YvTScsICdNL00nLCAnTXVsdGknLCAnT3RoZXInLCAnTm8gY2F0ZWdvcnknKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE51bWJlci5vZi5Xb3JrcyA9IGMoc3VtKGdyZXBsKCdHZW4nLGNhdGVnb3J5KSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgnRi9GJyxjYXRlZ29yeSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZ3JlcGwoJ0YvTScsY2F0ZWdvcnkpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGdyZXBsKCdNL00nLGNhdGVnb3J5KSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgnTXVsdGknLGNhdGVnb3J5KSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgnT3RoZXInLGNhdGVnb3J5KSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgnTm8gY2F0ZWdvcnknLGNhdGVnb3J5KSkpICkNCm11bHRpcGxlQ2F0ZWdvcnlTdW1tYXJ5JFNwbGl0IDwtICJBbGwgd29ya3MiDQoNCmNhdGVnb3J5U3VtbWFyeSA8LSByYmluZChzaW5nbGVDYXRlZ29yeVN1bW1hcnksIG11bHRpcGxlQ2F0ZWdvcnlTdW1tYXJ5KQ0KY2F0ZWdvcnlTdW1tYXJ5JENhdGVnb3J5IDwtIGZhY3RvcihjYXRlZ29yeVN1bW1hcnkkQ2F0ZWdvcnksIGxldmVscyA9IGMoJ0dlbicsICdGL0YnLCAnRi9NJywgJ00vTScsICdNdWx0aScsICdPdGhlcicsICdObyBjYXRlZ29yeScpKQ0KY2F0ZWdvcnlTdW1tYXJ5JFNwbGl0IDwtIGZhY3RvcihjYXRlZ29yeVN1bW1hcnkkU3BsaXQsIGxldmVscyA9IGMoIlNpbmdsZSBjYXRlZ29yeSIsICJBbGwgd29ya3MiKSkNCg0KcGxvdENhdGVnb3JpZXMgPC0gZ2dwbG90KGNhdGVnb3J5U3VtbWFyeSwgYWVzKHggPSBDYXRlZ29yeSwgeSA9IE51bWJlci5vZi5Xb3JrcykpICsgDQogICAgICAgICAgICAgICAgICBnZW9tX2NvbChhbHBoYT0xKSsNCiAgICAgICAgICAgICAgICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICAgICAgICAgICAgICAgIGJhY2tncm91bmRfZ3JpZCgpICsNCiAgICAgICAgICAgICAgICAgIGZhY2V0X3dyYXAoLn5TcGxpdCkgKw0KICAgICAgICAgICAgICAgICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMSwgaGp1c3Q9MSkpKw0KICAgICAgICAgICAgICAgICAgbGFicyh5PSJOdW1iZXIgb2YgV29ya3MiKQ0KcGxvdENhdGVnb3JpZXMNCg0Kcm0oc2luZ2xlQ2F0ZWdvcnlTdW1tYXJ5LCBtdWx0aXBsZUNhdGVnb3J5U3VtbWFyeSwgY2F0ZWdvcnlTdW1tYXJ5LCBwbG90Q2F0ZWdvcmllcykNCg0KYGBgDQoNCiMgRW5nYWdlbWVudCBieSBhIHNpbmdsZSBjYXRlZ29yeQ0KDQpGb3Igc2ltcGxpY2l0eSBJJ20gb25seSBsb29raW5nIGF0IHdvcmtzIHRhZ2dlZCB3aXRoIGEgc2luZ2xlIGNhdGVnb3J5IGhlcmUuDQoNCiJNdWx0aSIgc2VlbXMgdG8gaGF2ZSBtb3N0IHdvcmRzLCBkZXNwaXRlIGJlaW5nIGEgcmF0aGVyIHNtYWxsIGNhdGVnb3J5LCBhbmQgY29sbGVjdHMgcXVpdGUgYSBiaXQgb2YgSGl0cywgS3Vkb3MsIENvbW1lbnRzIGFuZCBCb29rbWFya3MuIEl0J3MgcG9zc2libGUgdGhhdCBhIG51bWJlciBvZiB0aG9zZSB3b3JrcyBhcmUgY29sbGVjdGlvbnMgb2Ygc3RvcmllcyBmb3IgbWFueSBmYW5kb21zLCB3aGljaCBhbXBsaWZpZXMgdGhlIGVuZ2FnZW1lbnQgbnVtYmVycy4NCg0KT3ZlcmFsbCwgIk0vTSIgY2F0ZWdvcnkgd29ya3MgY29sbGVjdCBhdCBsZWFzdCBhIHRoaXJkIGFzIGFzIG1hbnkgaGl0cyBhcyBhbGwgb3RoZXJzLiBJdCBhbHNvIHllaWxkcyBtb3N0IGt1ZG9zIGFuZCBjb21tZW50cywgY2xvc2VseSBmb2xsb3dlZCBieSAnR2VuJywgd2l0aCBhbGwgb3RoZXJzIGJlaW5nIHNpZ25pZmljYW50bHkgbGVzcyBwb3B1bGFyLiAnR2VuJyBhbmQgJ00vTScgYWxzbyBnZXQgdGhlIG1vc3QgYm9va21hcmtzLg0KDQpgYGB7ciBjYXRlZ29yaWVzU2luZ2xlRW5nYWdlbWVudCwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQ0KDQpzdGF0c0NhdGVnb3J5IDwtIHN0YXRzW3VubGlzdChsYXBwbHkoY2F0ZWdvcnksIGZ1bmN0aW9uKHgpIGxlbmd0aCh4KSkpID09IDEsXQ0Kc3RhdHNDYXRlZ29yeSRDYXRlZ29yeSA8LSBhcy5mYWN0b3IodW5saXN0KGNhdGVnb3J5W3VubGlzdChsYXBwbHkoY2F0ZWdvcnksIGZ1bmN0aW9uKHgpIGxlbmd0aCh4KSkpID09IDFdKSkNCnN0YXRzQ2F0ZWdvcnkkQ2F0ZWdvcnkgPC0gZmFjdG9yKHN0YXRzQ2F0ZWdvcnkkQ2F0ZWdvcnksIGxldmVscyA9IGMoJ0dlbicsICdGL0YnLCAnRi9NJywgJ00vTScsICdNdWx0aScsICdPdGhlcicsICdObyBjYXRlZ29yeScpKQ0Kc3RhdHNDYXRlZ29yeSREaXZpc29yIDwtIHVubGlzdChsYXBwbHkoc3RhdHNDYXRlZ29yeSRDYXRlZ29yeSwgZnVuY3Rpb24oeCkgc3VtbWFyeShzdGF0c0NhdGVnb3J5JENhdGVnb3J5KVtuYW1lcyhzdW1tYXJ5KHN0YXRzQ2F0ZWdvcnkkQ2F0ZWdvcnkpKSA9PSB4XSkpDQpzdGF0c0NhdGVnb3J5JFdvcmRzLnBlci5Xb3JrIDwtIHN0YXRzQ2F0ZWdvcnkkV29yZHMvc3RhdHNDYXRlZ29yeSREaXZpc29yDQpzdGF0c0NhdGVnb3J5JEhpdHMucGVyLldvcmsgPC0gc3RhdHNDYXRlZ29yeSRIaXRzL3N0YXRzQ2F0ZWdvcnkkRGl2aXNvcg0Kc3RhdHNDYXRlZ29yeSRLdWRvcy5wZXIuV29yayA8LSBzdGF0c0NhdGVnb3J5JEt1ZG9zL3N0YXRzQ2F0ZWdvcnkkRGl2aXNvcg0Kc3RhdHNDYXRlZ29yeSRDb21tZW50cy5wZXIuV29yayA8LSBzdGF0c0NhdGVnb3J5JENvbW1lbnRzL3N0YXRzQ2F0ZWdvcnkkRGl2aXNvcg0Kc3RhdHNDYXRlZ29yeSRCb29rbWFya3MucGVyLldvcmsgPC0gc3RhdHNDYXRlZ29yeSRCb29rbWFya3Mvc3RhdHNDYXRlZ29yeSREaXZpc29yDQpzdGF0c0NhdGVnb3J5JFdvcmtzLlBlcmNlbnQgPC0gMS9zdGF0c0NhdGVnb3J5JERpdmlzb3INCg0KYmFyV29ya3NDYXRlZ29yeSA8LSBwbG90X2Jhcl9jb2xvcihzdGF0c0NhdGVnb3J5LCAnQ2F0ZWdvcnknLCAnUmF0aW5nJywgJ3JpZ2h0JykNCmJhcldvcmRzQ2F0ZWdvcnkgPC0gcGxvdF9jb2xfY29sb3Ioc3RhdHNDYXRlZ29yeSwgJ0NhdGVnb3J5JywgJ1dvcmRzLnBlci5Xb3JrJywgJ1JhdGluZycsICdyaWdodCcpDQpiYXJIaXRzQ2F0ZWdvcnkgPC0gcGxvdF9jb2xfY29sb3Ioc3RhdHNDYXRlZ29yeSwgJ0NhdGVnb3J5JywgJ0hpdHMucGVyLldvcmsnLCAnUmF0aW5nJywgJ3JpZ2h0JykNCmJhckt1ZG9zQ2F0ZWdvcnkgPC0gcGxvdF9jb2xfY29sb3Ioc3RhdHNDYXRlZ29yeSwgJ0NhdGVnb3J5JywgJ0t1ZG9zLnBlci5Xb3JrJywgJ1JhdGluZycsICdyaWdodCcpDQpiYXJDb21tZW50c0NhdGVnb3J5IDwtIHBsb3RfY29sX2NvbG9yKHN0YXRzQ2F0ZWdvcnksICdDYXRlZ29yeScsICdDb21tZW50cy5wZXIuV29yaycsICdSYXRpbmcnLCAncmlnaHQnKQ0KYmFyQm9va21hcmtzQ2F0ZWdvcnkgPC0gcGxvdF9jb2xfY29sb3Ioc3RhdHNDYXRlZ29yeSwgJ0NhdGVnb3J5JywgJ0Jvb2ttYXJrcy5wZXIuV29yaycsJ1JhdGluZycsICdyaWdodCcpDQoNCnBsb3RfZ3JpZChwbG90X2dyaWQoIGJhcldvcmtzQ2F0ZWdvcnkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYmFyV29yZHNDYXRlZ29yeSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJIaXRzQ2F0ZWdvcnkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYmFyS3Vkb3NDYXRlZ29yeSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJDb21tZW50c0NhdGVnb3J5ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckJvb2ttYXJrc0NhdGVnb3J5ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGFsaWduID0gJ2h2JyksDQogICAgICAgICAgZ2V0X2xlZ2VuZChiYXJXb3Jrc0NhdGVnb3J5ICsgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSkpLA0KICAgICAgICAgIHJlbF93aWR0aHMgPSBjKDQsMSkpDQoNCmBgYA0KDQojIFJhdGluZ3MgcGVyY2VudGFnZXMgYnkgYSBzaW5nbGUgY2F0ZWdvcnkNCg0KT3V0IG9mIHRoZSAzIG1haW4gc2hpcHBpbmcgY2F0ZWdvcmllcywgaW4gYWJzb2x1dGUgbnVtYmVycyAiRi9NIiBoYXMgbW9zdCBFIHJhdGVkIHdvcmtzLCBhbmQgIkYvRiIgaGFzIHRoZSBsZWFzdC4gSG93ZXZlciwgaW4gcmVsYXRpdmUgYW1vdW50cyAiTS9NIiBjYXRlZ29yeSBoYXMgbW9yZSBleHBsaWNpdCB3b3JrcyAoYHIgcm91bmQoMTAwKnN1bShzdGF0c0NhdGVnb3J5JENhdGVnb3J5ID09ICJNL00iICYgc3RhdHNDYXRlZ29yeSRSYXRpbmcgPT0gIkV4cGxpY2l0Iikvc3VtKHN0YXRzQ2F0ZWdvcnkkQ2F0ZWdvcnkgPT0gIk0vTSIpKWAlKSB0aGFuIGVpdGhlciAiRi9GIiBvciAiRi9NIiwgYnV0IG5vdCBvdmVyd2hlbG1pbmdseSBzby4gT3ZlcmFsbCwgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgcmF0aW5ncyBiZXR3ZWVuIHRoZSBjYXRlZ29yaWVzIGZvciBBVExBIHNlZW0gdmVyeSBjbG9zZSBpbiByYXRpbmdzLCB1bmxpa2UgZm9yIExPSyBhbmQgZXNwZWNpYWxseSBURFAuDQoNCmBgYHtyIGNhdGVnb3JpZXNTaW5nbGVFbmdhZ2VtZW50UGVyY2VudCwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQ0KDQpwbG90V29ya3NDYXRlZ29yeU5vcm1hbGl6ZWQgPC0gcGxvdF9jb2xfY29sb3Ioc3RhdHNDYXRlZ29yeSwgJ1JhdGluZycsICdXb3Jrcy5QZXJjZW50JywgJ1JhdGluZycsICdub25lJykrDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2V0X3dyYXAoLn5DYXRlZ29yeSkNCnBsb3RXb3Jrc0NhdGVnb3J5Tm9ybWFsaXplZA0KDQpybShiYXJXb3Jrc0NhdGVnb3J5LCBiYXJXb3Jkc0NhdGVnb3J5LCBiYXJIaXRzQ2F0ZWdvcnksIGJhckt1ZG9zQ2F0ZWdvcnksIGJhckNvbW1lbnRzQ2F0ZWdvcnksIGJhckJvb2ttYXJrc0NhdGVnb3J5LCBwbG90V29ya3NDYXRlZ29yeU5vcm1hbGl6ZWQpDQoNCmBgYA0KDQojIFNpbmdsZSBDYXRlZ29yeSB0aHJvdWdoIHRpbWUNCg0KSW50ZXJlc3RpbmdseSwgc2Vhc29ucyAzIGFuZCA0IG9mIExPSyBicm91Z2h0IGluY3JlYXNlIG9mICdGL0YnIGNhdGVnb3J5IHBvcHVsYXJpdHkgaW4gQVRMQSwgbGlrZWx5IGR1ZSB0byB3b3JrcyBiZWluZyB0YWdnZWQgd2l0aCBib3RoIGZhbmRvbSB0YWdzLiBSZWNlbnQgcGVhayBzaG93cyBoaWdoIG51bWJlciBvZiAnTS9NJyB3b3Jrcywgd2hpY2ggbWF5IGJlIGR1ZSB0byByZWNlbnQgcmlzZSBpbiBwb3B1bGFyaXR5IG9mIFp1a2thIC0gJ1Nva2thL1p1a28gKEF2YXRhciknIHJlbGF0aW9uc2hpcC4NCg0KYGBge3Igc2luZ2xlUmF0aW5nVGltZSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0NCg0KcGxvdERhdGVzUmF0aW5nRGVuc2l0eSA8LSBnZ3Bsb3Qoc3RhdHNDYXRlZ29yeSwgYWVzKHggPSBEYXRlLCBjb2w9Q2F0ZWdvcnkpKSArIA0KICAgICAgICAgICAgICAgICAgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjEpKw0KICAgICAgICAgICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9c2Vhc29uc1N0YXJ0KSsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PXNlYXNvbnNFbmQsIGxpbmV0eXBlID0ibG9uZ2Rhc2giKSsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PWFvM2JpcnRoLCBjb2w9J3JlZCcpKw0KICAgICAgICAgICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9c2Vhc29uc1N0YXJ0TE9LLCBjb2w9J2JsdWUnKSsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PXNlYXNvbnNFbmRMT0ssIGxpbmV0eXBlID0ibG9uZ2Rhc2giLCBjb2w9J2JsdWUnKSsNCiAgICAgICAgICAgICAgICAgICAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzPSIxMiBtb250aHMiKSsNCiAgICAgICAgICAgICAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGN1c3RvbXBhbGV0dGUpICsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICAgICAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgICAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gJ3JpZ2h0JykNCnBsb3REYXRlc1JhdGluZ0RlbnNpdHkNCg0Kcm0ocGxvdERhdGVzUmF0aW5nRGVuc2l0eSkNCmBgYA0KDQojIE1vc3QgcG9wdWxhciBzaGlwIHRhZ3MNCg0KQ3VycmVudGx5ICJLYXRhcmEvWnVrbyAoQXZhdGFyKSIgaXMgdGhlIG1vc3QgcG9wdWxhciBzaGlwLCB3aXRoICJTb2trYS9adWtvIChBdmF0YXIpIiBmb2xsb3dpbmcgYXQgYWJvdXQgMi8zIG9mIHRoZSBudW1iZXIgb2Ygd29ya3MuDQoNCmBgYHtyIHNoaXBzSGlzdG9ncmFtLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTZ9DQoNCnRvcExpc3QgPC0gOA0KdG9wUmVsYXRpb25zaGlwc1RhYmxlPC0gZGF0YS5mcmFtZSgnUmVsYXRpb25zaGlwJyA9IG5hbWVzKHN1bW1hcnkoYXMuZmFjdG9yKHVubGlzdChyZWxhdGlvbnNoaXBzKSkpWzE6dG9wTGlzdF0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAnTnVtYmVyIG9mIFN0b3JpZXMnID0gc3VtbWFyeShhcy5mYWN0b3IodW5saXN0KHJlbGF0aW9uc2hpcHMpKSlbMTp0b3BMaXN0XSkNCnJvdy5uYW1lcyh0b3BSZWxhdGlvbnNoaXBzVGFibGUpIDwtIGMoKQ0KdG9wUmVsYXRpb25zaGlwc1RhYmxlIDwtIHRvcFJlbGF0aW9uc2hpcHNUYWJsZVtvcmRlcih0b3BSZWxhdGlvbnNoaXBzVGFibGUkTnVtYmVyLm9mLlN0b3JpZXMsIGRlY3JlYXNpbmcgPSBUUlVFKSxdDQp0b3BSZWxhdGlvbnNoaXBzVGFibGUkUmVsYXRpb25zaGlwIDwtIGZhY3Rvcihhcy5jaGFyYWN0ZXIodG9wUmVsYXRpb25zaGlwc1RhYmxlJFJlbGF0aW9uc2hpcCksIGxldmVscz1hcy5jaGFyYWN0ZXIodG9wUmVsYXRpb25zaGlwc1RhYmxlJFJlbGF0aW9uc2hpcCkpDQoNCnJlbGF0aW9uc2hpcHNTdGF0cyA8LSBkYXRhLmZyYW1lKERhdGUgPSBzdGF0cyREYXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVsYXRpb25zaGlwMSA9IHJlcCgwLCBsZW5ndGgoc3RhdHMkRGF0ZSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVsYXRpb25zaGlwMiA9IHJlcCgwLCBsZW5ndGgoc3RhdHMkRGF0ZSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVsYXRpb25zaGlwMyA9IHJlcCgwLCBsZW5ndGgoc3RhdHMkRGF0ZSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVsYXRpb25zaGlwNCA9IHJlcCgwLCBsZW5ndGgoc3RhdHMkRGF0ZSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVsYXRpb25zaGlwNSA9IHJlcCgwLCBsZW5ndGgoc3RhdHMkRGF0ZSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVsYXRpb25zaGlwNiA9IHJlcCgwLCBsZW5ndGgoc3RhdHMkRGF0ZSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVsYXRpb25zaGlwNyA9IHJlcCgwLCBsZW5ndGgoc3RhdHMkRGF0ZSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVsYXRpb25zaGlwOCA9IHJlcCgwLCBsZW5ndGgoc3RhdHMkRGF0ZSkpKQ0KZm9yIChpIGluIDE6dG9wTGlzdCl7DQogIG1hdGNoaW5nVmVjdG9yIDwtIGxhcHBseShyZWxhdGlvbnNoaXBzLCBtYXRjaCwgdGFibGU9YXMuY2hhcmFjdGVyKHRvcFJlbGF0aW9uc2hpcHNUYWJsZSRSZWxhdGlvbnNoaXBbaV0pKQ0KICBtYXRjaGluZ1ZlY3RvciA8LSB1bmxpc3QobGFwcGx5KG1hdGNoaW5nVmVjdG9yLCBzdW0sIG5hLnJtPVRSVUUpKQ0KICByZWxhdGlvbnNoaXBzU3RhdHNbaSsxXSA8LSBtYXRjaGluZ1ZlY3Rvcg0KfQ0KDQojY29sbmFtZXMocmVsYXRpb25zaGlwc1N0YXRzKVsyOjldIDwtIGdzdWIoJy8nLCAnXFwvJywgdG9wUmVsYXRpb25zaGlwc1RhYmxlJFJlbGF0aW9uc2hpcCkNCg0KcGxvdExlZ2VuZFJlbGF0aW9uc2hpcHMgPC0gZ2dwbG90KHRvcFJlbGF0aW9uc2hpcHNUYWJsZSwgYWVzKHg9UmVsYXRpb25zaGlwLCB5PU51bWJlci5vZi5TdG9yaWVzLCBmaWxsPVJlbGF0aW9uc2hpcCkpKw0KICBnZW9tX2NvbChhbHBoYT0wLjcpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21wYWxldHRlKSsNCiAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogIGxhYnMoeD0iIix5PSdOdW1iZXIgb2YgU3RvcmllcycpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpDQpwbG90TGVnZW5kUmVsYXRpb25zaGlwcyt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpDQpgYGANCg0KIyBTaGlwIHRhZ3MgdGhyb3VnaCB0aW1lDQoNCkR1ZSB0byBBVExBIGJlaW5nIGFuIG9sZCBmYW5kb20gd2hpY2ggcmVjZW50bHkgZ290IG1vcmUgcG9wdWxhciBpdCdzIGEgbGl0dGxlIGRpZmZpY3VsdCB0byBzZWUgdGhlIGFjdHVhbCBzaGlwIGR5bmFtaWNzLCBob3dldmVyIGl0J3MgY2xlYXIgdGhhdCBhbGwgb2YgdGhlIG1vc3QgcG9wdWxhciBzaGlwcyBhcmUgcmVjaWV2aW5nIG1vcmUgYXR0ZW50aW9uIHJlY2VudGx5LCBhbmQgdGhhdCAiS2F0YXJhICYgWnVrbyAoQXZhdGFyKSIsICJJcm9oICYgWnVrbyAoQXZhdGFyKSIsIGFuZCBtb3N0IG5vdGljYWJseSAiU29ra2EvWnVrbyAoQXZhdGFyKSIgYXJlIGdldHRpbmcgYSBsb3Qgb2YgbmV3IHdvcmtzLg0KDQpgYGB7ciBzaGlwVGFnc1RpbWUsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9DQoNCnBsb3RSZWxhdGlvbnNoaXBzIDwtIGdncGxvdCgpICsNCiAgICBnZW9tX2RlbnNpdHkoZGF0YSA9IHJlbGF0aW9uc2hpcHNTdGF0c1tyZWxhdGlvbnNoaXBzU3RhdHMkcmVsYXRpb25zaGlwMSA+IDAsXSwgbWFwcGluZz1hZXMoeCA9IERhdGUpLCBjb2xvdXI9Y3VzdG9tcGFsZXR0ZVsxXSkrDQogICAgZ2VvbV9kZW5zaXR5KGRhdGEgPSByZWxhdGlvbnNoaXBzU3RhdHNbcmVsYXRpb25zaGlwc1N0YXRzJHJlbGF0aW9uc2hpcDIgPiAwLF0sIG1hcHBpbmc9YWVzKHggPSBEYXRlKSwgY29sb3VyPWN1c3RvbXBhbGV0dGVbMl0pKw0KICAgIGdlb21fZGVuc2l0eShkYXRhID0gcmVsYXRpb25zaGlwc1N0YXRzW3JlbGF0aW9uc2hpcHNTdGF0cyRyZWxhdGlvbnNoaXAzID4gMCxdLCBtYXBwaW5nPWFlcyh4ID0gRGF0ZSksIGNvbG91cj1jdXN0b21wYWxldHRlWzNdKSsNCiAgICBnZW9tX2RlbnNpdHkoZGF0YSA9IHJlbGF0aW9uc2hpcHNTdGF0c1tyZWxhdGlvbnNoaXBzU3RhdHMkcmVsYXRpb25zaGlwNCA+IDAsXSwgbWFwcGluZz1hZXMoeCA9IERhdGUpLCBjb2xvdXI9Y3VzdG9tcGFsZXR0ZVs0XSkrDQogICAgZ2VvbV9kZW5zaXR5KGRhdGEgPSByZWxhdGlvbnNoaXBzU3RhdHNbcmVsYXRpb25zaGlwc1N0YXRzJHJlbGF0aW9uc2hpcDUgPiAwLF0sIG1hcHBpbmc9YWVzKHggPSBEYXRlKSwgY29sb3VyPWN1c3RvbXBhbGV0dGVbNV0pKw0KICAgIGdlb21fZGVuc2l0eShkYXRhID0gcmVsYXRpb25zaGlwc1N0YXRzW3JlbGF0aW9uc2hpcHNTdGF0cyRyZWxhdGlvbnNoaXA2ID4gMCxdLCBtYXBwaW5nPWFlcyh4ID0gRGF0ZSksIGNvbG91cj1jdXN0b21wYWxldHRlWzZdKSsNCiAgICBnZW9tX2RlbnNpdHkoZGF0YSA9IHJlbGF0aW9uc2hpcHNTdGF0c1tyZWxhdGlvbnNoaXBzU3RhdHMkcmVsYXRpb25zaGlwNyA+IDAsXSwgbWFwcGluZz1hZXMoeCA9IERhdGUpLCBjb2xvdXI9Y3VzdG9tcGFsZXR0ZVs3XSkrDQogICAgZ2VvbV9kZW5zaXR5KGRhdGEgPSByZWxhdGlvbnNoaXBzU3RhdHNbcmVsYXRpb25zaGlwc1N0YXRzJHJlbGF0aW9uc2hpcDggPiAwLF0sIG1hcHBpbmc9YWVzKHggPSBEYXRlKSwgY29sb3VyPWN1c3RvbXBhbGV0dGVbOF0pKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1zZWFzb25zU3RhcnQpKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1zZWFzb25zRW5kLCBsaW5ldHlwZSA9ImxvbmdkYXNoIikrDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PWFvM2JpcnRoLCBjb2w9J3JlZCcpKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1zZWFzb25zU3RhcnRMT0ssIGNvbD0nYmx1ZScpKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1zZWFzb25zRW5kTE9LLCBsaW5ldHlwZSA9ImxvbmdkYXNoIiwgY29sPSdibHVlJykrDQogICAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzPSIxMiBtb250aHMiKSsNCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY3VzdG9tcGFsZXR0ZSkgKw0KICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKQ0KDQpteWxlZ2VuZCA8LSBnZXRfbGVnZW5kKHBsb3RMZWdlbmRSZWxhdGlvbnNoaXBzKQ0KDQpwbG90X2dyaWQocGxvdFJlbGF0aW9uc2hpcHMsIG15bGVnZW5kLA0KICAgICAgICAgIHJlbF93aWR0aHMgPSBjKDIsMSksIG5yb3c9MSkNCiNwbG90UmVsYXRpb25zaGlwcw0KDQojcm0oc2Vhc29ucywgcGxvdERhdGVzUmF0aW5nRGVuc2l0eSkNCmBgYA0KDQoNCiMgQXJjaGl2ZSBXYXJuaW5ncw0KDQpNYWpvcml0eSBvZiB3b3JrcyBhcmUgdGFnZ2VkIHdpdGggIk5vIEFyY2hpdmUgV2FybmluZ3MgQXBwbHkiLCBmb2xsb3dlZCBieSBhIHNpemFibGUgZnJhY3Rpb24gb2YgIkNyZWF0b3IgQ2hvc2UgTm90IFRvIFVzZSBBcmNoaXZlIFdhcm5pbmdzIi4gSXQgc2VlbXMgdG8gYmUgYSBjb21tb24gbWF0dGVyIG9mIGNvbmZ1c2lvbiBiZXR3ZWVuIHRoZSB1c2FnZSBvZiB0aG9zZSB0d28gd2FybmluZ3MsIHNvIGl0J3MgcG9zc2libGUgdGhhdCBhIGxvdCBvZiAiQ3JlYXRvciBDaG9zZSBOb3QgVG8gVXNlIEFyY2hpdmUgV2FybmluZ3MiIGFyZSBtaXN0YWdnZWQgIk5vIEFyY2hpdmUgV2FybmluZ3MgQXBwbHkiLg0KDQpgYGB7ciB3YXJuaW5nQmFycywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD02fQ0KDQptdWx0aXBsZVdhcm5pbmdTdW1tYXJ5IDwtIGRhdGEuZnJhbWUoV2FybmluZyA9IGMoIk5vIEFyY2hpdmUgV2FybmluZ3MgQXBwbHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JhcGhpYyBEZXBpY3Rpb25zIE9mIFZpb2xlbmNlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1ham9yIENoYXJhY3RlciBEZWF0aCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSYXBlL05vbi1Db24iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVW5kZXJhZ2UiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ3JlYXRvciBDaG9zZSBOb3QgVG8gVXNlIEFyY2hpdmUgV2FybmluZ3MiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE51bWJlci5vZi5Xb3JrcyA9IGMoc3VtKGdyZXBsKCJObyBBcmNoaXZlIFdhcm5pbmdzIEFwcGx5Iix3YXJuaW5ncykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZ3JlcGwoIkdyYXBoaWMgRGVwaWN0aW9ucyBPZiBWaW9sZW5jZSIsd2FybmluZ3MpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGdyZXBsKCJNYWpvciBDaGFyYWN0ZXIgRGVhdGgiLHdhcm5pbmdzKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgiUmFwZS9Ob24tQ29uIix3YXJuaW5ncykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZ3JlcGwoIlVuZGVyYWdlIix3YXJuaW5ncykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZ3JlcGwoIkNyZWF0b3IgQ2hvc2UgTm90IFRvIFVzZSBBcmNoaXZlIFdhcm5pbmdzIix3YXJuaW5ncykpKSApDQoNCm11bHRpcGxlV2FybmluZ1N1bW1hcnkkV2FybmluZyA8LSBmYWN0b3IobXVsdGlwbGVXYXJuaW5nU3VtbWFyeSRXYXJuaW5nLCBsZXZlbHMgPSBjKCJObyBBcmNoaXZlIFdhcm5pbmdzIEFwcGx5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcmFwaGljIERlcGljdGlvbnMgT2YgVmlvbGVuY2UiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1ham9yIENoYXJhY3RlciBEZWF0aCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmFwZS9Ob24tQ29uIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJVbmRlcmFnZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ3JlYXRvciBDaG9zZSBOb3QgVG8gVXNlIEFyY2hpdmUgV2FybmluZ3MiKSkNCg0KcGxvdFdhcm5pbmdzIDwtIHBsb3RfY29sKG11bHRpcGxlV2FybmluZ1N1bW1hcnksICdXYXJuaW5nJywgJ051bWJlci5vZi5Xb3JrcycsICdyaWdodCcpDQpwbG90V2FybmluZ3MNCg0Kcm0obXVsdGlwbGVXYXJuaW5nU3VtbWFyeSwgcGxvdFdhcm5pbmdzKQ0KDQpgYGANCg0KIyBNdWx0aXBsZSBGYW5kb21zDQoNCk51bWJlciBvZiB3b3JrcyB0YWdnZWQgd2l0aCBtb3JlIHRoYW4gMSBmYW5kb20gaXMgYHIgbGVuZ3RoKGZhbmRvbVt1bmxpc3QobGFwcGx5KGZhbmRvbSwgbGVuZ3RoKSkgPiAxXSlgLCBidXQgbnVtYmVyIG9mIHdvcmtzIHRhZ2dlZCB3aXRoIG1vcmUgdGhhbiAyIGZhbmRvbXMgaXMgYHIgbGVuZ3RoKGZhbmRvbVt1bmxpc3QobGFwcGx5KGZhbmRvbSwgbGVuZ3RoKSkgPiAyXSlgIHdoaWNoIHNlZW1zIHRvIGJlIGR1ZSB0byB3b3JrcyBvZnRlbiBiZWluZyB0YWdnZWQgd2l0aCBib3RoICJBdmF0YXI6IExlZ2VuZCBvZiBLb3JyYSIgYW5kICJBdmF0YXI6IFRoZSBMYXN0IEFpcmJlbmRlciIuDQoNCk51bWJlciBvZiB3b3JrcyBleHBsaWNpdGx5IHRhZ2dlZCBhcyAnY3Jvc3NvdmVyJyBpcyBqdXN0IGByIGxlbmd0aChmYW5kb21bZ3JlcCgnY3Jvc3NvdmVyJyxmcmVlZm9ybSwgaWdub3JlLmNhc2U9VFJVRSldKWAuDQoNCiMgQXV0aG9ycyBieSBXb3Jrcw0KDQpUb3AgMzAgb2YgbW9zdCBwcm9saWZpYyBhdXRob3JzIGluIHRoZSB0YWcgYnkgdGhlIG51bWJlciBvZiBzdG9yaWVzIGFzIG9mIGRhdGEgY29sbGVjdGlvbiBkYXRlOg0KDQpgYGB7ciBhdXRob3JzV29ya3MsIG1lc3NhZ2UgPSBGQUxTRX0NCnRvcExpc3QgPC0gMzANCg0KQXV0aG9yVGFibGUgPC0gZGF0YS5mcmFtZSgnQXV0aG9yJyA9IG5hbWVzKHN1bW1hcnkoYXMuZmFjdG9yKHVubGlzdChhdXRob3IpKSlbMTp0b3BMaXN0XSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICdOdW1iZXIgb2YgU3RvcmllcycgPSBzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QoYXV0aG9yKSkpWzE6dG9wTGlzdF0pDQpyb3cubmFtZXMoQXV0aG9yVGFibGUpIDwtIGMoKQ0KDQprYWJsZShBdXRob3JUYWJsZSwNCiAgICAgIGNvbC5uYW1lcyA9IGMoJ0F1dGhvcicsICdOdW1iZXIgb2YgU3RvcmllcycpKQ0KDQpybShBdXRob3JUYWJsZSkNCmBgYA0KDQpUb3AgcGxhY2UgaXMgb2NjdXBpZWQgYnkgb3JwaGFuX2FjY291bnQsIHdoaWNoIGlzIGFuIGFydGlmYWN0IG9mIGFyY2hpdmUnIHdvcmtzIG9ycGhhbmluZyBmdW5jdGlvbi4NCg0KIyBBdXRob3JzIGJ5IFdvcmRzDQoNCk9ubHkgYHIgc3VtKHVubGlzdChsYXBwbHkoYXV0aG9yLCBsZW5ndGgpKT4xKWAgd29ya3MgaGF2ZSBtb3JlIHRoYW4gb25lIGF1dGhvci4gSW4gY2FzZXMgd2hlcmUgd29ya3MgaGFkIG1vcmUgdGhhbiBvbmUgYXV0aG9yLCBJIGFzc3VtZWQgdGhhdCBlYWNoIG9mIHRoZW0gY29udHJpYnV0ZWQgYW4gZXF1YWwgYW1vdW50cyBvZiB3b3Jkcy4NCg0KVG9wIDMwIG9mIG1vc3QgcHJvbGlmaWMgYXV0aG9ycyBpbiB0aGUgdGFnIGJ5IHRoZSBudW1iZXIgb2Ygd29yZHMgd3JpdHRlbiBhcyBvZiBkYXRhIGNvbGxlY3Rpb24gZGF0ZToNCg0KYGBge3IgYXV0aG9yc1dvcmRzLCBtZXNzYWdlID0gRkFMU0V9DQoNCndvcmRzQnlBdXRob3IgPC0gYygpDQoNCmZvciAoaSBpbiAxOmxlbmd0aCh3b3Jkcykpew0KICBpZiAobGVuZ3RoKGF1dGhvcltbaV1dKSA+IDEpIHsNCiAgICB3b3Jkc0J5QXV0aG9yIDwtIGMod29yZHNCeUF1dGhvciwgcmVwKHdvcmRzW1tpXV0vbGVuZ3RoKGF1dGhvcltbNV1dKSwgbGVuZ3RoKGF1dGhvcltbaV1dKSApICkNCiAgfSBlbHNlIHsNCiAgICB3b3Jkc0J5QXV0aG9yIDwtIGMod29yZHNCeUF1dGhvciwgd29yZHNbW2ldXSkNCiAgfQ0KfQ0KDQpBdXRob3JXb3Jkc1RhYmxlIDwtIGRhdGEuZnJhbWUoJ0F1dGhvcicgPSBhcy5mYWN0b3IodW5saXN0KGF1dGhvcikpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdXb3JkcycgPSB3b3Jkc0J5QXV0aG9yKQ0KDQpBdXRob3JXb3Jkc1N1bW1hcnkgPC0gZGRwbHkoQXV0aG9yV29yZHNUYWJsZSwgLihBdXRob3IpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpemUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRvdGFsLldvcmRzID0gc3VtKFdvcmRzKSkNCkF1dGhvcldvcmRzU3VtbWFyeSA8LSBBdXRob3JXb3Jkc1N1bW1hcnlbb3JkZXIoQXV0aG9yV29yZHNTdW1tYXJ5JFRvdGFsLldvcmRzLCBkZWNyZWFzaW5nID0gVFJVRSksXQ0Kcm93Lm5hbWVzKEF1dGhvcldvcmRzU3VtbWFyeSkgPC0gYygpDQoNCnRvcExpc3QgPC0gMzANCg0Ka2FibGUoQXV0aG9yV29yZHNTdW1tYXJ5WzE6dG9wTGlzdCxdLA0KICAgICAgY29sLm5hbWVzID0gYygnQXV0aG9yJywgJ1RvdGFsIFdvcmRzJykpDQoNCnJtKHdvcmRzQnlBdXRob3IsIGksIEF1dGhvcldvcmRzVGFibGUsIEF1dGhvcldvcmRzU3VtbWFyeSkNCmBgYA0KDQpJbnRlcmVzdGluZ2x5LCBvcnBoYW5fYWNjb3VudCBtYWRlIGl0IHRvIHRoZSB0b3AgYnkgdGhlIG51bWJlciBvZiB3b3JkcyB3cml0dGVuIGFzIHdlbGwuDQoNCiMgQ2hhcmFjdGVycw0KDQpUb3AgMzAgb2YgdGhlIG1vc3QgcG9wdWxhciBjaGFyYWN0ZXJzOg0KDQpgYGB7ciBjaGFyYWN0ZXJzLCBtZXNzYWdlID0gRkFMU0V9DQp0b3BMaXN0IDwtIDMwDQpDaGFyYWN0ZXJUYWJsZTwtIGRhdGEuZnJhbWUoJ0NoYXJhY3RlcicgPSBuYW1lcyhzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QoY2hhcmFjdGVyKSkpWzE6dG9wTGlzdF0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAnTnVtYmVyIG9mIFN0b3JpZXMnID0gc3VtbWFyeShhcy5mYWN0b3IodW5saXN0KGNoYXJhY3RlcikpKVsxOnRvcExpc3RdKQ0Kcm93Lm5hbWVzKENoYXJhY3RlclRhYmxlKSA8LSBjKCkNCg0Ka2FibGUoQ2hhcmFjdGVyVGFibGUsDQogICAgICBjb2wubmFtZXMgPSBjKCdDaGFyYWN0ZXInLCAnTnVtYmVyIG9mIFN0b3JpZXMnKSkNCg0Kcm0oQ2hhcmFjdGVyVGFibGUpDQpgYGANCg0KIyBSZWxhdGlvbnNoaXBzDQoNClRvcCAzMCBvZiB0aGUgbW9zdCBwb3B1bGFyIHJlbGF0aW9uc2hpcHM6DQoNCkkgZG9uJ3QgaGF2ZSBhY2Nlc3MgdG8gQW8zJ3Mgc3lzdGVtIG9mIHN5bm9ueW1vdXMgdGFncywgc28gYnkgdmlydHVlIG9mIHRleHQgcHJvY2Vzc2luZyBzb21lIHJlbGF0aW9uc2hpcCB0YWdzIGhlcmUgYXJlIHJlcGVhdGVkLg0KDQoiS2F0YXJhL1p1a28gKEF2YXRhcikiIGlzIHRoZSBtb3N0IHBvcHVsYXIgcmVsYXRpb25zaGlwIGluIEFUTEEuIFRoZXkgYXJlIGZvbGxvd2VkIGJ5ICJTb2trYS9adWtvIChBdmF0YXIpIiwgYW5kICJBYW5nL0thdGFyYSAoQXZhdGFyKSIuDQoNCmBgYHtyIHJlbGF0aW9uc2hpcHMsIG1lc3NhZ2UgPSBGQUxTRX0NCnRvcExpc3QgPC0gMzANClJlbGF0aW9uc2hpcHNUYWJsZTwtIGRhdGEuZnJhbWUoJ1JlbGF0aW9uc2hpcCcgPSBuYW1lcyhzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QocmVsYXRpb25zaGlwcykpKVsxOnRvcExpc3RdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgJ051bWJlciBvZiBTdG9yaWVzJyA9IHN1bW1hcnkoYXMuZmFjdG9yKHVubGlzdChyZWxhdGlvbnNoaXBzKSkpWzE6dG9wTGlzdF0pDQpyb3cubmFtZXMoUmVsYXRpb25zaGlwc1RhYmxlKSA8LSBjKCkNCg0Ka2FibGUoUmVsYXRpb25zaGlwc1RhYmxlLA0KICAgICAgY29sLm5hbWVzID0gYygnUmVsYXRpb25zaGlwJywgJ051bWJlciBvZiBTdG9yaWVzJykpDQoNCnJtKFJlbGF0aW9uc2hpcHNUYWJsZSkNCmBgYA0KDQojIEZyZWVmb3JtIHRhZ3MNCg0KVG9wIDMwIG9mIHRoZSBtb3N0IHBvcHVsYXIgZnJlZWZvcm0gdGFnczoNCg0KYGBge3IgZnJlZWZvcm0sIG1lc3NhZ2UgPSBGQUxTRX0NCnRvcExpc3QgPC0gMzANCkZyZWVmb3JtVGFibGU8LSBkYXRhLmZyYW1lKCdGcmVlZm9ybScgPSBuYW1lcyhzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QoZnJlZWZvcm0pKSlbMTp0b3BMaXN0XSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICdOdW1iZXIgb2YgU3RvcmllcycgPSBzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QoZnJlZWZvcm0pKSlbMTp0b3BMaXN0XSkNCnJvdy5uYW1lcyhGcmVlZm9ybVRhYmxlKSA8LSBjKCkNCg0Ka2FibGUoRnJlZWZvcm1UYWJsZSwNCiAgICAgIGNvbC5uYW1lcyA9IGMoJ0ZyZWVmb3JtIFRhZycsICdOdW1iZXIgb2YgU3RvcmllcycpKQ0KDQpybShGcmVlZm9ybVRhYmxlKQ0KYGBgDQoNCiMgTGFuZ3VhZ2VzDQoNClVuc3VycHJpc2luZ2x5LCBtb3N0IHdvcmtzIGFyZSB3cml0dGVuIGluIEVuZ2xpc2guIEFwb2xvZ2llcyBmb3IgVSsuIGthYmxlIHBhY2thZ2UgZm9yIHdoYXRldmVyIHJlYXNvbiBtdXJkZXJzIHVuaWNvZGUgY2hhcmFjdGVycy4gVGhlIGxhbmd1YWdlcyBpbiBxdWVzdGlvbiBhcmUgUnVzc2lhbiAo0KDRg9GB0YHQutC40LkpLENoaW5lc2UgKOS4reaWhyksIEhlYnJldyjXoteR16jXmdeqKSwgYW5kIFZpZXRuYW1lc2UgKFRp4bq/bmcgVmnhu4d0KS4NCg0KYGBge3IgbGFuZ3VhZ2VzLCBtZXNzYWdlID0gRkFMU0V9DQojdG9wTGlzdCA8LSAzMA0KDQpsYW5ndWFnZXNMaXN0IDwtIHN1bW1hcnkoYXMuZmFjdG9yKHVubGlzdChsYW5ndWFnZSkpKQ0KDQpMYW5ndWFnZVRhYmxlIDwtIGRhdGEuZnJhbWUoJ0xhbmd1YWdlJyA9IG5hbWVzKGxhbmd1YWdlc0xpc3QpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICdOdW1iZXIgb2YgU3RvcmllcycgPSBsYW5ndWFnZXNMaXN0ICkNCkxhbmd1YWdlVGFibGUgPC0gTGFuZ3VhZ2VUYWJsZVtvcmRlcihMYW5ndWFnZVRhYmxlJE51bWJlci5vZi5TdG9yaWVzLCBkZWNyZWFzaW5nPVRSVUUpLF0NCnJvdy5uYW1lcyhMYW5ndWFnZVRhYmxlKSA8LSBjKCkNCg0Ka2FibGUoTGFuZ3VhZ2VUYWJsZSwNCiAgICAgIGNvbC5uYW1lcyA9IGMoJ0xhbmd1YWdlJywgJ051bWJlciBvZiBTdG9yaWVzJykpDQoNCiNsYW5ndWFnZXNMaXN0DQoNCiNybShMYW5ndWFnZVRhYmxlKQ0KYGBgDQoNCiMgT3RoZXIgbGlua3MNCg0KW0FvMyBkYXRhIGFuYWx5c2lzIGZvciBUaGUgRHJhZ29uIFByaW5jZSAoQ2FydG9vbildKGh0dHBzOi8vZGFydGhhbGluZS5naXRodWIuaW8vQW8zU2VhcmNoQW5hbHlzaXMvZmFuZG9tcy9URFAvVERQX3Byb2Nlc3Npbmdfbm90ZWJvb2submIuaHRtbCkNCg0KW0FvMyBkYXRhIGFuYWx5c2lzIGZvciBBdmF0YXI6IExlZ2VuZCBvZiBLb3JyYV0oaHR0cHM6Ly9kYXJ0aGFsaW5lLmdpdGh1Yi5pby9BbzNTZWFyY2hBbmFseXNpcy9mYW5kb21zL0xPSy9MT0tfcHJvY2Vzc2luZ19ub3RlYm9vay5uYi5odG1sKQ0KDQpBbzMgZGF0YSBhbmFseXNpcyBmb3IgQXZhdGFyOiBUaGUgTGFzdCBBaXJiZW5kZXINCg0KW0FvMyBkYXRhIGFuYWx5c2lzIGZvciBCbGFjayBTYWlsc10oaHR0cHM6Ly9kYXJ0aGFsaW5lLmdpdGh1Yi5pby9BbzNTZWFyY2hBbmFseXNpcy9mYW5kb21zL0JTYWlscy9CU2FpbHNfcHJvY2Vzc2luZ19ub3RlYm9vay5uYi5odG1sKQ0KDQpJZiB5b3UgZW5qb3llZCBteSBhbmFseXNpcywgcGxlYXNlLCBjb25zaWRlciBbYnV5aW5nIG1lIGEgY29mZmVlXShodHRwczovL2tvLWZpLmNvbS9EMUQ4UklHNSkgb3Igc29tZSBvdGhlciBiZXZlcmFnZS4=