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).

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

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


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

Complete Work vs Work in Progress distributions


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

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

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


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

Rating distributions


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

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

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


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

Categories

There are 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)

Freeform tags

Top 30 of the most popular freeform tags:

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

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

Freeform Tag Number of Stories
Angst 1644
Fluff 1559
Alternate Universe - Canon Divergence 1345
Hurt/Comfort 978
Romance 934
Zuko is an Awkward Turtleduck 915
Alternate Universe - Modern Setting 841
Friendship 796
Post-Canon 786
Alternate Universe 678
Humor 581
Implied/Referenced Child Abuse 552
Ozai (Avatar) Being a Terrible Parent 517
Family 511
Slow Burn 511
Zutara 501
Fluff and Angst 468
Canon Compliant 466
Emotional Hurt/Comfort 404
One Shot 384
Iroh (Avatar) is a Good Uncle 367
Zuko (Avatar)-centric 351
Crossover 348
Ozai (Avatar) is an Asshole 334
Established Relationship 328
Angst with a Happy Ending 322
Friends to Lovers 315
Drabble 310
Smut 300
Drama 288


rm(FreeformTable)

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=