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('TDP_worksData.RData')
This is a document detailing analysis of The Dragon Prince (Cartoon) Ao3 tag data, collected on the 7 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))
}
plot_density <- function (data, column, color_column, legendPosition) {
ggplot(data, aes_string(x = column, col=color_column)) +
geom_density(alpha = 0.1)+
scale_x_log10()+
theme_half_open() +
background_grid() +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1),
legend.position = legendPosition)
}
plot_points <- function (data, columnX, columnY, color_column, legendPosition) {
ggplot(data, aes_string(x = columnX, y = columnY, col=color_column)) +
geom_point(alpha=0.3)+
scale_x_log10()+
scale_y_log10()+
facet_wrap(color_column)+
theme_half_open() +
background_grid() +
theme(legend.title=element_blank(),
axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
}
#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})
kudos <-lapply(worksData, function(x) {x$Kudos})
comments <-lapply(worksData, function(x) {x$Comments})
bookmarks<-lapply(worksData, function(x) {x$Bookmarks})
hits <-lapply(worksData, function(x) {x$Hits})
stats <- data.frame(Words = unlist(words, recursive = FALSE),
Comments= as.numeric(as.character(comments)),
Kudos = as.numeric(as.character(kudos)),
Bookmarks = as.numeric(as.character(bookmarks)),
Hits = as.numeric(as.character(hits)),
WIP = unlist(WIP, recursive = FALSE),
Rating = unlist(rating, recursive = FALSE),
Date = do.call("c", date))
stats$Rating <- factor(stats$Rating, levels = c("Not Rated", "General Audiences", "Teen And Up Audiences", "Mature", "Explicit"))
total <- 1000
percentile <- c(1:total)
percentileData <- data.frame(Works.Percentile = 100*(total - percentile)/total,
Words = unlist(lapply(percentile/total, quantile, x = unlist(words))) + 1,
Hits = unlist(lapply(percentile/total, quantile, x = unlist(hits))) + 1,
Kudos = unlist(lapply(percentile/total, quantile, x = unlist(kudos))) + 1,
Comments = unlist(lapply(percentile/total, quantile, x = unlist(comments))) + 1,
Bookmarks = unlist(lapply(percentile/total, quantile, x = unlist(bookmarks))) + 1 )
rm(rating, kudos, comments, bookmarks, hits)
Timeline
Vertical lines on the graph indicate season release dates according to Wiki article. As expected, after each new season, there’s a peak of activity which fades in about 2 months. Curiously, a few works were posted before official season 1 release. This may be attributed to the series trailer drop in July 2018 at the San Diego Comic-Con.
#data$Timestamp <- parse_date_time2(as.character(data$Timestamp), orders = "%d/%m/%Y %H:%M:%S")
#data$day <- as.Date(data$Timestamp)
seasons <- c("2018-09-14", "2019-02-15", "2019-11-22")
seasons <- as.Date(seasons)
dates <- data.frame(date = do.call("c", date),
WIP = unlist(WIP))
plotDatesDensityTotal <- ggplot(stats, aes(x = Date)) +
geom_density(alpha = 0.1)+
geom_vline(xintercept=seasons)+
scale_x_date(date_breaks="2 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)
It’s important to note, that I collect data from the Ao3 search page (rather than works pages, as it’s less disruptive), so I don’t have access to initial postage dates, only the latest updates. This means that the upward trend in works over time can be an artifact of series getting more popular, but also could be attributed to multichapter works drifting further in time due to updates.
If we plot Complete Works and Works in Progress separately, we still see an upward trend in both, but the slope characterising the growth of Work In progress peaks seems steeper than for Complete Works, which to me indicates at least partial effect of the multichapter drift.
plotDatesDensity <- ggplot(stats, aes(x = Date, col=WIP)) +
geom_density(alpha = 0.1)+
geom_vline(xintercept=seasons)+
scale_x_date(date_breaks="2 months")+
theme_half_open() +
background_grid() +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1),
legend.position = 'right')
plotDatesDensity

rm(plotDatesDensity)
Engagement percentiles
Small plotting cheat: all the numbers on the Y axis are increased by 1 to include the case of 0 into the plot (otherwise excluded because of log scale).
- About 75% of works have more than a 1000 words, but only about 10% have more than 10000 words.
- Only about 30% of works have over a 1000 hits.
- Only about 25% 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 25% of works get more than 10 bookmarks.
- Approximately 20% 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 4 times as many Complete Works as there are Works in Progress.
- Works in Progress are approximately 4 times longer than Complete ones.
- Works in Progress get approximately twice as many hits as Complete ones.
- Works in Progress get approximately 25% more kudos than Complete ones.
- Works in Progress get approximately 3 times as many comments as Complete ones (however there’s no way to filter out author’s comments in the search. selection, so this statistic should be taken with a grain of salt).
- Works in Progress get approximately twice as many bookmarks as 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)
wordsDensityWIP <- plot_density(stats, 'Words', 'WIP', 'right')
hitsDensityWIP <- plot_density(stats, 'Hits', 'WIP', 'right')
kudosDensityWIP <- plot_density(stats, 'Kudos', 'WIP', 'right')
commentsDensityWIP <- plot_density(stats, 'Comments', 'WIP', 'right')
bookmarksDensityWIP <- plot_density(stats, 'Bookmarks', 'WIP', 'right')
plot_grid(wordsDensityWIP + theme(legend.position="none"),
hitsDensityWIP + theme(legend.position="none"),
kudosDensityWIP + theme(legend.position="none"),
commentsDensityWIP + theme(legend.position="none"),
bookmarksDensityWIP + theme(legend.position="none"),
get_legend(kudosDensityWIP +
theme(legend.title=element_blank())))
rm(wordsDensityWIP, hitsDensityWIP, kudosDensityWIP, commentsDensityWIP, bookmarksDensityWIP)
wordsHitsWIP <- plot_points(stats, 'Words', 'Hits', 'WIP', 'right')
wordsHitsWIP
wordsKudosWIP <- plot_points(stats, 'Words', 'Kudos', 'WIP', 'right')
wordsKudosWIP
wordsCommentsWIP <- plot_points(stats, 'Words', 'Comments', 'WIP', 'right')
wordsCommentsWIP
wordsBookmarksWIP <- plot_points(stats, 'Words', 'Bookmarks', 'WIP', 'right')
wordsBookmarksWIP
rm(wordsHitsWIP, wordsKudosWIP, wordsCommentsWIP, wordsBookmarksWIP)
Rating distributions
- Works rated G make up the most numerous category (~1300), but they are on average the shortest (~2500 words) and get fewest hits(>1000), and fewest comments (~10).
- Works rated T follow in numbers (~900), but are significantly longer (~9000 words).
- Works rated M make for the smallest category (tied with Not Rated, at ~ 200), but are the longest (~13k words).
- Works rated E are few (~350), longer than G but shorter than T rated works (~6000 words), and get the most hits, but fewer kudos and significantly less comments than T and M rated works.
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)
wordsDensityRating <- plot_density(stats, 'Words', 'Rating', 'right')
hitsDensityRating <- plot_density(stats, 'Hits', 'Rating', 'right')
kudosDensityRating <- plot_density(stats, 'Kudos', 'Rating', 'right')
commentsDensityRating <- plot_density(stats, 'Comments', 'Rating', 'right')
bookmarksDensityRating <- plot_density(stats, 'Bookmarks', 'Rating', 'right')
plot_grid(wordsDensityRating + theme(legend.position="none"),
hitsDensityRating + theme(legend.position="none"),
kudosDensityRating + theme(legend.position="none"),
commentsDensityRating + theme(legend.position="none"),
bookmarksDensityRating + theme(legend.position="none"),
get_legend(kudosDensityRating +
theme(legend.title=element_blank())))
rm(wordsDensityRating, hitsDensityRating, kudosDensityRating, commentsDensityRating, bookmarksDensityRating)
wordsHitsRating <- plot_points(stats, 'Words', 'Hits', 'Rating', 'right')
wordsHitsRating
wordsKudosRating <- plot_points(stats, 'Words', 'Kudos', 'Rating', 'right')
wordsKudosRating
wordsCommentsRating <- plot_points(stats, 'Words', 'Comments', 'Rating', 'right')
wordsCommentsRating
wordsBookmarksRating <- plot_points(stats, 'Words', 'Bookmarks', 'Rating', 'right')
wordsBookmarksRating
rm(wordsHitsRating, wordsKudosRating, wordsCommentsRating, wordsBookmarksRating)
Categories
There are 2604 works tagged with a single category, and 444 tagged with 2 or more (up until all 6).
‘F/M’ is the most popular category, followed by ‘M/M’, ‘Gen’, and ‘F/F’.
Multiple category fics strongly contribute towards ‘F/M’ count, then to ‘M/M’, ‘Gen’, and ‘F/F’, and only marginally to ‘Multi’ and ‘Other’.
singleCategorySummary <- summary(as.factor(unlist(category[unlist(lapply(category, function(x) length(x))) == 1])))
singleCategorySummary <- data.frame(Category = names(singleCategorySummary),
Number.of.Works = singleCategorySummary)
singleCategorySummary$Split <- "Single category"
multipleCategorySummary <- data.frame(Category = c('Gen', 'F/F', 'F/M', 'M/M', 'Multi', 'Other', 'No category'),
Number.of.Works = c(sum(grepl('Gen',category)),
sum(grepl('F/F',category)),
sum(grepl('F/M',category)),
sum(grepl('M/M',category)),
sum(grepl('Multi',category)),
sum(grepl('Other',category)),
sum(grepl('No category',category))) )
multipleCategorySummary$Split <- "All works"
categorySummary <- rbind(singleCategorySummary, multipleCategorySummary)
categorySummary$Category <- factor(categorySummary$Category, levels = c('Gen', 'F/F', 'F/M', 'M/M', 'Multi', 'Other', 'No category'))
categorySummary$Split <- factor(categorySummary$Split, levels = c("Single category", "All works"))
plotCategories <- ggplot(categorySummary, aes(x = Category, y = Number.of.Works)) +
geom_col(alpha=1)+
theme_half_open() +
background_grid() +
facet_wrap(.~Split) +
theme(legend.title=element_blank(),
axis.title.x = element_blank(),
axis.text.x = element_text(angle = 90, vjust = 1, hjust=1))+
labs(y="Number of Works")
plotCategories

rm(singleCategorySummary, multipleCategorySummary, categorySummary, plotCategories)
Engagement by a single category
For simplicity I’m only looking at works tagged with a single category here.
“Multi” seems to have most words, despite being a rather small category, and collects quite a bit of Hits and Comments. It’s possible that a number of those works are collections of stories for many fandoms, which amplifies the number of Hits and Comments, but that requires further investigation.
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 “M/M” has most E rated works, and “F/F” has the least, but in percentages of total works “F/F” and “F/M” are distributed almost identically, while “M/M” skews more towards M and E rated works.
plotWorksCategoryNormalized <- plot_col_color(statsCategory, 'Rating', 'Works.Percent', 'Rating', 'none')+
scale_y_continuous(labels=scales::percent)+
facet_wrap(.~Category)
plotWorksCategoryNormalized

rm(barWorksCategory, barWordsCategory, barHitsCategory, barKudosCategory, barCommentsCategory, barBookmarksCategory, plotWorksCategoryNormalized)
Single Category through time
Interestingly, season 1 sees a peak of ‘Gen’ category. Season 2 gives higher rise to ‘M/M’ (possibly related to Aaravos reveal and ‘Aaravos/Viren (The Dragon Prince)’ shipping) and ‘F/M’ (the rise of ‘Callum/Rayla (The Dragon Prince)’?), and season 3 is followed by a high rise of ‘F/F’ (following the development of ‘Amaya/Janai (The Dragon Prince)’ relationship) and a modest ‘M/M’ peak (‘Ethari/Runaan (The Dragon Prince)’ due to Ethari finally getting official name?).
plotDatesCategoryDensity <- ggplot(statsCategory, aes(x = Date, col=Category)) +
geom_density(alpha = 0.1)+
geom_vline(xintercept=seasons)+
scale_x_date(date_breaks="2 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')
plotDatesCategoryDensity

rm(plotDatesCategoryDensity)
Ship tags through time
“Amaya/Janai (The Dragon Prince)” sharply took of in popularity after season 3. Ethari’s name hasn’t been revealed until season 3, so authors were using “Runaan/Tinker | Necklace Elf (The Dragon Prince)” instead of “Ethari/Runaan (The Dragon Prince)”, but Ao3 would consider them synonymous. Romance tag “Callum/Rayla (The Dragon Prince)” is significantly more popular than platonic/frienship tag “Callum & Rayla (The Dragon Prince)”, however it’s worth noting that out of 230 stories tagged with a friendship/platonic one 178 are tagged with both, possibly making specifically friendship/platonic content more difficult to find.
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=seasons)+
scale_x_date(date_breaks="2 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
multiFandoms <- fandom[category == "Multi"]
multiFandomsAll <- fandom[grepl('Multi', category)]
severalFandoms <- fandom[unlist(lapply(fandom, length)) > 1]
crossovers <- fandom[grep('crossover',freeform, ignore.case=TRUE)]
Total number of works tagged only as ‘Multi’ is 63, but only 7 are tagged with more than one fandom. Among these, median number of fandoms tagged is 6, and at least one work is tagged with 15.
Number of works tagged with ‘Multi’ and/or other categories is 139, but only 21 are tagged with more than one fandom. Among these, median number of fandoms tagged is 14, and at least one work is tagged with 56.
Number of works tagged with more than 1 fandom is 133, however in some cases fandom tags used by the author are synonymous with The Dragon Prince (Cartoon), for example “The Dragon Prince”, “rayllum - Fandom”, “rayla x callum”, “TDP - Fandom”, “callum x rayla”.
Number of works explicitly tagged as ‘crossover’ is lower: 30. Out of multiple fandom tag works a significant amount are tagged with 2: 80, which, upon inspection, don’t seem overwhelmingly synonymous, so perhaps some authors simply don’t tag for crossovers.
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
|
planetundersiege
|
244
|
6Husbandos
|
40
|
Lady_Talla_Doe
|
37
|
orphan_account
|
33
|
brightsmoon
|
31
|
HootHalycon
|
30
|
beautifulterriblequeen
|
26
|
Lonespark_the_friendly_kraken
|
26
|
Symphoenae
|
26
|
propheticfire
|
23
|
Jelly
|
22
|
DelicateDragons
|
21
|
wordswithdragons
|
21
|
Porg_Master
|
19
|
Midnightdragon2
|
18
|
CuddlyCookie1360
|
17
|
UnsubstantiatedAssertion
|
17
|
AWillfullDroll
|
16
|
zuppi
|
16
|
Nightworldlove
|
15
|
im_the_king_of_the_ocean
|
14
|
Khaleesi_0f_Trolls
|
14
|
Mydarlingwriter
|
14
|
poetroe
|
14
|
spiritypowers
|
14
|
Aaravosa (Lokiiwood)
|
13
|
Anima_princess_1
|
13
|
Anonymous
|
13
|
Erratus
|
13
|
his_valentine
|
13
|
rm(AuthorTable)
Authors by Words
Only 48 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
|
Decorated
|
500506.5
|
beautifulterriblequeen
|
313169.0
|
spontaneite
|
294914.0
|
Lodke
|
248405.0
|
Jelly
|
225759.0
|
DeeTheTeaDrinkingDragon
|
217848.2
|
NumptyPylon
|
184380.0
|
nonameforhire
|
175498.0
|
Khaleesi_0f_Trolls
|
166320.0
|
genericfanatic
|
162468.5
|
enbyred
|
158300.0
|
MagiesHeartLove
|
156268.0
|
prolixdreams
|
156264.0
|
The_Eternal_Winter
|
154539.0
|
wordswithdragons
|
144711.5
|
Symphoenae
|
144698.2
|
Kuno
|
144402.0
|
AWillfullDroll
|
139935.0
|
CuddlyCookie1360
|
137388.0
|
nautiscarader
|
134518.0
|
DelphinusDelphis
|
134126.0
|
Erratus
|
127890.0
|
Captain_Azoren
|
126415.0
|
Ocaj
|
125066.0
|
iwillhaveamoonbase
|
122299.0
|
DelicateDragons
|
118327.0
|
assassiinikissa
|
114339.0
|
BlehBlahBluh
|
113585.0
|
zuppi
|
112691.0
|
RosettaStarlight
|
109028.0
|
rm(wordsByAuthor, i, AuthorWordsTable, AuthorWordsSummary)
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
|
Rayla (The Dragon Prince)
|
1455
|
Callum (The Dragon Prince)
|
1434
|
Viren (The Dragon Prince)
|
782
|
Ezran (The Dragon Prince)
|
735
|
Soren (The Dragon Prince)
|
687
|
Claudia (The Dragon Prince)
|
665
|
Runaan (The Dragon Prince)
|
601
|
Amaya (The Dragon Prince)
|
558
|
Aaravos (The Dragon Prince)
|
472
|
Harrow (The Dragon Prince)
|
399
|
Gren (The Dragon Prince)
|
340
|
Ethari (The Dragon Prince)
|
334
|
Janai (The Dragon Prince)
|
333
|
Azymondias (The Dragon Prince)
|
297
|
Sarai (The Dragon Prince)
|
179
|
Bait (The Dragon Prince)
|
160
|
Tinker | Necklace Elf (The Dragon Prince)
|
135
|
Corvus (The Dragon Prince)
|
121
|
Opeli (The Dragon Prince)
|
107
|
Original Characters
|
102
|
Marcos (The Dragon Prince)
|
85
|
Lain (The Dragon Prince)
|
75
|
Tiadrin (The Dragon Prince)
|
75
|
Original Female Character(s)
|
74
|
Kazi (The Dragon Prince)
|
71
|
Lujanne (The Dragon Prince)
|
70
|
Ibis (The Dragon Prince)
|
60
|
Reader
|
57
|
Ellis (The Dragon Prince)
|
53
|
Aanya (The Dragon Prince)
|
51
|
rm(CharacterTable)
Relationships
Top 30 of the most popular relationships:
I don’t have access to Ao3’s system of synonymous tags, so by virtue of text processing some relationship tags here are repeated.
Overwhelmingly, “Callum/Rayla (The Dragon Prince)” is the most popular relationship in TDP. They are followed by “Ethari/Runaan (The Dragon Prince)”, which is not immediately obvious due to common use of synonymous tags such as “Runaan/Tinker | Necklace Elf (The Dragon Prince)” and “Runaan/Ethari”. Third most popular relationship is “Amaya/Janai (The Dragon Prince)”.
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
|
Callum/Rayla (The Dragon Prince)
|
1009
|
Amaya/Janai (The Dragon Prince)
|
273
|
Callum & Rayla (The Dragon Prince)
|
230
|
Ethari/Runaan (The Dragon Prince)
|
222
|
Aaravos/Viren (The Dragon Prince)
|
182
|
Runaan/Tinker | Necklace Elf (The Dragon Prince)
|
155
|
Harrow/Viren (The Dragon Prince)
|
119
|
Claudia/Rayla (The Dragon Prince)
|
113
|
Callum & Ezran & Rayla (The Dragon Prince)
|
100
|
Harrow/Sarai (The Dragon Prince)
|
87
|
Claudia & Soren (The Dragon Prince)
|
82
|
Callum & Ezran (The Dragon Prince)
|
77
|
Rayla & Runaan (The Dragon Prince)
|
67
|
Aaravos (The Dragon Prince)/Reader
|
55
|
Lain/Tiadrin (The Dragon Prince)
|
52
|
Aaravos & Viren (The Dragon Prince)
|
47
|
Amaya/Gren (The Dragon Prince)
|
47
|
Amaya & Gren (The Dragon Prince)
|
46
|
Callum & Soren (The Dragon Prince)
|
45
|
Callum & Claudia (The Dragon Prince)
|
40
|
Marcos/Soren (The Dragon Prince)
|
40
|
Ezran & Rayla (The Dragon Prince)
|
36
|
Rayllum - Relationship
|
32
|
Runaan/Ethari (The Dragon Prince)
|
32
|
Claudia & Rayla (The Dragon Prince)
|
31
|
Claudia & Viren (The Dragon Prince)
|
31
|
Callum & King Harrow (The Dragon Prince)
|
30
|
Gren & Runaan (The Dragon Prince)
|
29
|
Runaan/Ethari
|
29
|
Callum & Ezran & King Harrow (The Dragon Prince)
|
28
|
rm(RelationshipsTable)
Languages
Unsurprisingly, most works are written in English. Apologies for U+. kable package for whatever reason murders unicode characters. The two languages in question are Russian (Русский) and Chinese (中文).
#topList <- 30
languagesList <- summary(as.factor(unlist(language)))
LanguageTable <- data.frame('Language' = names(languagesList),
'Number of Stories' = languagesList )
LanguageTable <- LanguageTable[order(LanguageTable$Number.of.Stories, decreasing=TRUE),]
row.names(LanguageTable) <- c()
kable(LanguageTable,
col.names = c('Language', 'Number of Stories'))
Language
|
Number of Stories
|
English
|
2998
|
<U+0420><U+0443><U+0441><U+0441><U+043A><U+0438><U+0439>
|
12
|
Español
|
10
|
<U+4E2D><U+6587>
|
8
|
Français
|
8
|
Polski
|
6
|
Português brasileiro
|
3
|
Deutsch
|
1
|
Italiano
|
1
|
Suomi
|
1
|
#languagesList
#rm(LanguageTable)
LS0tDQp0aXRsZTogIkFvMyBkYXRhIGFuYWx5c2lzIGZvciBUaGUgRHJhZ29uIFByaW5jZSAoQ2FydG9vbikiDQphdXRob3I6ICJkYXJ0aGFsaW5lIg0KZGF0ZTogIjcgQXVnIDIwMjAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgY29kZV9mb2xkaW5nOiAiaGlkZSINCiAgICB0b2M6IHRydWUNCi0tLQ0KDQojIEFib3V0DQoNCmBgYHtyIHNldHVwLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNClN5cy5zZXRlbnYoTEFORyA9ICJlbiIpDQojbGlicmFyeSgicnN0dWRpb2FwaSIpICN0byBncmFiIGxvY2FsIHBvc2l0aW9uIG9mIHRoZSBzY3JpcHQNCiNzZXR3ZChkaXJuYW1lKHJzdHVkaW9hcGk6OmdldEFjdGl2ZURvY3VtZW50Q29udGV4dCgpJHBhdGgpKQ0Ka25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAnLicpDQoNCiNsaWJyYXJ5KCJydmVzdCIpICMgdG8gaGFuZGxlIGh0bWwgc3R1ZmYNCg0KbGlicmFyeShsdWJyaWRhdGUpICMgdG8gaGFuZGxlIGRhdGVzDQoNCmxpYnJhcnkoZ2dwbG90MikgIyBmb3IgcGxvdHRpbmcNCmxpYnJhcnkoY293cGxvdCkgIyBmb3IgcGxvdHRpbmcNCmxpYnJhcnkoUkNvbG9yQnJld2VyKSAjIGZvciBjaG9vc2luZyBjb2xvcnMNCg0KY3VzdG9tcGFsZXR0ZSA8LSBicmV3ZXIucGFsKG49OCwgbmFtZSA9ICdEYXJrMicpDQoNCmxpYnJhcnkoa25pdHIpICMgZm9yIHRhYmxlcw0KbGlicmFyeShrYWJsZUV4dHJhKSAjIGZvciB0YWJsZXMNCg0KbGlicmFyeShsdWJyaWRhdGUpICMgZm9yIGRhdGVzDQoNCmxpYnJhcnkocGx5cikgIyBkZHBseSwgdG8gc3VtbWFyaXplIG51bWJlciBvZiB3b3JkcyBieSBhdXRob3INCg0KbG9hZCgnVERQX3dvcmtzRGF0YS5SRGF0YScpDQoNCmBgYA0KDQpUaGlzIGlzIGEgZG9jdW1lbnQgZGV0YWlsaW5nIGFuYWx5c2lzIG9mIFtgciB0YWdWYWx1ZWAgQW8zIHRhZ10oaHR0cHM6Ly9hcmNoaXZlb2ZvdXJvd24ub3JnL3RhZ3MvVGhlJTIwRHJhZ29uJTIwUHJpbmNlJTIwKENhcnRvb24pL3dvcmtzKSBkYXRhLCBjb2xsZWN0ZWQgb24gdGhlIDcgQXVnIDIwMjAuIEkgaGF2ZW4ndCBmaWd1cmVkIG91dCBhIHdheSB0byBnZXQgbXkgc2NyYXBwZXIgdG8gbG9nIGluIGludG8gQW8zICh5ZXQ/IHJ2ZXN0IHNlZW1zIHRvIGhhdmUgc29tZSB0cm91YmxlIHdpdGggcGFnZSByZWRpcmVjdHMpLCBzbyByZXN1bHRzIGhlcmUgYXJlIGJhc2VkIG9uIHRoZSB3b3JrcyB2aXNpYmxlIHdpdGhvdXQgYXV0aGVudGljYXRpb24sIHdoaWNoIGxpa2VseSBmaWx0ZXJzIG91dCBwcmVmZXJlbnRpYWxseSBleHBsaWNpdC9wcm9ibGVtYW50aWMgd29ya3MgZnJvbSB0aGUgc2VsZWN0aW9uLg0KDQpgYGB7ciBwbG90dGluZ0Z1bmN0aW9ucywgY29sbGFwc2U9VFJVRSwgd2FybmluZz1GQUxTRX0NCg0KcGxvdF9iYXIgPC0gZnVuY3Rpb24gKGRhdGEsIGNvbHVtblgsIGxlZ2VuZFBvc2l0aW9uKSB7DQogICAgZ2dwbG90KGRhdGEsIGFlc19zdHJpbmcoeCA9IGNvbHVtblgpKSArIA0KICAgIGdlb21fYmFyKGFscGhhPTEpKw0KICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAxLCBoanVzdD0xKSkrDQogICAgbGFicyh5PSJOdW1iZXIgb2Ygd29ya3MiKQ0KfQ0KDQpwbG90X2Jhcl9jb2xvciA8LSBmdW5jdGlvbiAoZGF0YSwgY29sdW1uWCwgY29sQ29sb3IsIGxlZ2VuZFBvc2l0aW9uKSB7DQogICAgZ2dwbG90KGRhdGEsIGFlc19zdHJpbmcoeCA9IGNvbHVtblgsIGZpbGw9Y29sQ29sb3IpKSArIA0KICAgIGdlb21fYmFyKGFscGhhPTAuNykrDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tcGFsZXR0ZSkgKw0KICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAxLCBoanVzdD0xKSkrDQogICAgbGFicyh5PSJOdW1iZXIgb2Ygd29ya3MiKQ0KfQ0KDQpwbG90X2NvbCA8LSBmdW5jdGlvbiAoZGF0YSwgY29sdW1uWCwgY29sdW1uWSwgbGVnZW5kUG9zaXRpb24pIHsNCiAgICBnZ3Bsb3QoZGF0YSwgYWVzX3N0cmluZyh4ID0gY29sdW1uWCwgeSA9IGNvbHVtblkpKSArIA0KICAgIGdlb21fY29sKGFscGhhPTEpKw0KICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAxLCBoanVzdD0xKSkrDQogICAgbGFicyh5PWdzdWIoJ1xcLicsICcgJywgY29sdW1uWSkpDQogIA0KfQ0KDQpwbG90X2NvbF9jb2xvciA8LSBmdW5jdGlvbiAoZGF0YSwgY29sdW1uWCwgY29sdW1uWSwgY29sQ29sb3IsIGxlZ2VuZFBvc2l0aW9uKSB7DQogICAgZ2dwbG90KGRhdGEsIGFlc19zdHJpbmcoeCA9IGNvbHVtblgsIHkgPSBjb2x1bW5ZLCBmaWxsPWNvbENvbG9yKSkgKyANCiAgICBnZW9tX2NvbChhbHBoYT0wLjcpKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbXBhbGV0dGUpICsNCiAgICB0aGVtZV9oYWxmX29wZW4oKSArDQogICAgYmFja2dyb3VuZF9ncmlkKCkgKw0KICAgIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMSwgaGp1c3Q9MSkpKw0KICAgIGxhYnMoeT1nc3ViKCdcXC4nLCAnICcsIGNvbHVtblkpKQ0KICANCn0NCg0KcGxvdF9wZXJjZW50aWxlcyA8LSBmdW5jdGlvbiAoZGF0YSwgY29sdW1uWCwgY29sdW1uWSwgbGVnZW5kUG9zaXRpb24pIHsNCiAgICBnZ3Bsb3QoZGF0YSwgYWVzX3N0cmluZyh4ID0gY29sdW1uWCwgeSA9IGNvbHVtblkpKSArIA0KICAgIGdlb21fcG9pbnQoYWxwaGE9MC4zKSsNCiAgICBzY2FsZV95X2xvZzEwKGJyZWFrcyA9IDEwXmMoMDoxNSkpKw0KICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKDAsIDI1LCA1MCwgNzUsIDEwMCkpKyAjc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoMDoxMCkqMTApKw0KICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSkrDQogICAgbGFicyh4PWdzdWIoJ1xcLicsICcgJywgY29sdW1uWCkpDQp9DQoNCmBgYA0KDQpgYGB7ciBwbG90dGluZ0Z1bmN0aW9uc1VudXNlZCwgY29sbGFwc2U9VFJVRSwgd2FybmluZz1GQUxTRSwgZXZhbCA9IEZBTFNFfQ0KDQpwbG90X2RlbnNpdHkgPC0gZnVuY3Rpb24gKGRhdGEsIGNvbHVtbiwgY29sb3JfY29sdW1uLCBsZWdlbmRQb3NpdGlvbikgew0KICAgIGdncGxvdChkYXRhLCBhZXNfc3RyaW5nKHggPSBjb2x1bW4sIGNvbD1jb2xvcl9jb2x1bW4pKSArIA0KICAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMSkrDQogICAgc2NhbGVfeF9sb2cxMCgpKw0KICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpLA0KICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGxlZ2VuZFBvc2l0aW9uKQ0KfQ0KDQpwbG90X3BvaW50cyA8LSBmdW5jdGlvbiAoZGF0YSwgY29sdW1uWCwgY29sdW1uWSwgY29sb3JfY29sdW1uLCBsZWdlbmRQb3NpdGlvbikgew0KICAgIGdncGxvdChkYXRhLCBhZXNfc3RyaW5nKHggPSBjb2x1bW5YLCB5ID0gY29sdW1uWSwgY29sPWNvbG9yX2NvbHVtbikpICsgDQogICAgZ2VvbV9wb2ludChhbHBoYT0wLjMpKw0KICAgIHNjYWxlX3hfbG9nMTAoKSsNCiAgICBzY2FsZV95X2xvZzEwKCkrDQogICAgZmFjZXRfd3JhcChjb2xvcl9jb2x1bW4pKw0KICAgIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpDQp9DQoNCmBgYA0KDQpgYGB7ciBmbGF0dGVuaW5nRGF0YSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI3RpdGxlIDwtIGxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JFRpdGxlfSkNCmF1dGhvciA8LSBsYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRBdXRob3J9KQ0KZmFuZG9tIDwtIGxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JEZhbmRvbX0pDQpyYXRpbmcgPC0gbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkUmF0aW5nfSkNCndhcm5pbmdzIDwtIGxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JFdhcm5pbmdzfSkNCmNhdGVnb3J5IDwtIGxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JENhdGVnb3J5fSkNCldJUCA8LSBsYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRXSVB9KQ0KZGF0ZSA8LWxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JERhdGV9KQ0KcmVsYXRpb25zaGlwcyA8LWxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JFJlbGF0aW9uc2hpcHN9KQ0KY2hhcmFjdGVyIDwtbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkQ2hhcmFjdGVyfSkNCmZyZWVmb3JtIDwtbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkRnJlZWZvcm19KQ0KbGFuZ3VhZ2UgPC1sYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRMYW5ndWFnZX0pDQp3b3JkcyA8LWxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JFdvcmRzfSkNCmt1ZG9zIDwtbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkS3Vkb3N9KQ0KY29tbWVudHMgPC1sYXBwbHkod29ya3NEYXRhLCBmdW5jdGlvbih4KSB7eCRDb21tZW50c30pDQpib29rbWFya3M8LWxhcHBseSh3b3Jrc0RhdGEsIGZ1bmN0aW9uKHgpIHt4JEJvb2ttYXJrc30pDQpoaXRzIDwtbGFwcGx5KHdvcmtzRGF0YSwgZnVuY3Rpb24oeCkge3gkSGl0c30pDQoNCnN0YXRzIDwtIGRhdGEuZnJhbWUoV29yZHMgPSB1bmxpc3Qod29yZHMsIHJlY3Vyc2l2ZSA9IEZBTFNFKSwNCiAgICAgICAgICAgICAgICAgICAgQ29tbWVudHM9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGNvbW1lbnRzKSksDQogICAgICAgICAgICAgICAgICAgIEt1ZG9zID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoa3Vkb3MpKSwNCiAgICAgICAgICAgICAgICAgICAgQm9va21hcmtzID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoYm9va21hcmtzKSksDQogICAgICAgICAgICAgICAgICAgIEhpdHMgPSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihoaXRzKSksDQogICAgICAgICAgICAgICAgICAgIFdJUCA9IHVubGlzdChXSVAsIHJlY3Vyc2l2ZSA9IEZBTFNFKSwNCiAgICAgICAgICAgICAgICAgICAgUmF0aW5nID0gdW5saXN0KHJhdGluZywgcmVjdXJzaXZlID0gRkFMU0UpLA0KICAgICAgICAgICAgICAgICAgICBEYXRlID0gZG8uY2FsbCgiYyIsIGRhdGUpKQ0KDQpzdGF0cyRSYXRpbmcgPC0gZmFjdG9yKHN0YXRzJFJhdGluZywgbGV2ZWxzID0gYygiTm90IFJhdGVkIiwgIkdlbmVyYWwgQXVkaWVuY2VzIiwgIlRlZW4gQW5kIFVwIEF1ZGllbmNlcyIsICJNYXR1cmUiLCAiRXhwbGljaXQiKSkNCg0KdG90YWwgPC0gMTAwMA0KcGVyY2VudGlsZSA8LSBjKDE6dG90YWwpDQpwZXJjZW50aWxlRGF0YSA8LSBkYXRhLmZyYW1lKFdvcmtzLlBlcmNlbnRpbGUgPSAxMDAqKHRvdGFsIC0gcGVyY2VudGlsZSkvdG90YWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdvcmRzID0gdW5saXN0KGxhcHBseShwZXJjZW50aWxlL3RvdGFsLCBxdWFudGlsZSwgeCA9IHVubGlzdCh3b3JkcykpKSArIDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhpdHMgPSB1bmxpc3QobGFwcGx5KHBlcmNlbnRpbGUvdG90YWwsIHF1YW50aWxlLCB4ID0gdW5saXN0KGhpdHMpKSkgKyAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBLdWRvcyA9IHVubGlzdChsYXBwbHkocGVyY2VudGlsZS90b3RhbCwgcXVhbnRpbGUsIHggPSB1bmxpc3Qoa3Vkb3MpKSkgKyAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDb21tZW50cyA9IHVubGlzdChsYXBwbHkocGVyY2VudGlsZS90b3RhbCwgcXVhbnRpbGUsIHggPSB1bmxpc3QoY29tbWVudHMpKSkgKyAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCb29rbWFya3MgPSB1bmxpc3QobGFwcGx5KHBlcmNlbnRpbGUvdG90YWwsIHF1YW50aWxlLCB4ID0gdW5saXN0KGJvb2ttYXJrcykpKSArIDEgKQ0KDQpybShyYXRpbmcsIGt1ZG9zLCBjb21tZW50cywgYm9va21hcmtzLCBoaXRzKQ0KDQpgYGANCg0KIyBUaW1lbGluZQ0KDQpWZXJ0aWNhbCBsaW5lcyBvbiB0aGUgZ3JhcGggaW5kaWNhdGUgc2Vhc29uIHJlbGVhc2UgZGF0ZXMgYWNjb3JkaW5nIHRvIFtXaWtpIGFydGljbGVdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1RoZV9EcmFnb25fUHJpbmNlI0VwaXNvZGVzKS4gQXMgZXhwZWN0ZWQsIGFmdGVyIGVhY2ggbmV3IHNlYXNvbiwgdGhlcmUncyBhIHBlYWsgb2YgYWN0aXZpdHkgd2hpY2ggZmFkZXMgaW4gYWJvdXQgMiBtb250aHMuIEN1cmlvdXNseSwgYSBmZXcgd29ya3Mgd2VyZSBwb3N0ZWQgYmVmb3JlIG9mZmljaWFsIHNlYXNvbiAxIHJlbGVhc2UuIFRoaXMgbWF5IGJlIGF0dHJpYnV0ZWQgdG8gdGhlIHNlcmllcyB0cmFpbGVyIGRyb3AgaW4gSnVseSAyMDE4IGF0IHRoZSBTYW4gRGllZ28gQ29taWMtQ29uLg0KDQpgYGB7ciB0aW1lbGluZVRvdGFsLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQ0KDQojZGF0YSRUaW1lc3RhbXAgPC0gcGFyc2VfZGF0ZV90aW1lMihhcy5jaGFyYWN0ZXIoZGF0YSRUaW1lc3RhbXApLCBvcmRlcnMgPSAiJWQvJW0vJVkgJUg6JU06JVMiKQ0KI2RhdGEkZGF5IDwtIGFzLkRhdGUoZGF0YSRUaW1lc3RhbXApDQoNCnNlYXNvbnMgPC0gYygiMjAxOC0wOS0xNCIsICIyMDE5LTAyLTE1IiwgIjIwMTktMTEtMjIiKQ0Kc2Vhc29ucyA8LSBhcy5EYXRlKHNlYXNvbnMpDQoNCmRhdGVzIDwtIGRhdGEuZnJhbWUoZGF0ZSA9IGRvLmNhbGwoImMiLCBkYXRlKSwNCiAgICAgICAgICAgICAgICAgICAgV0lQID0gdW5saXN0KFdJUCkpDQoNCnBsb3REYXRlc0RlbnNpdHlUb3RhbCA8LSBnZ3Bsb3Qoc3RhdHMsIGFlcyh4ID0gRGF0ZSkpICsgDQogICAgICAgICAgICAgICAgICAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMSkrDQogICAgICAgICAgICAgICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1zZWFzb25zKSsNCiAgICAgICAgICAgICAgICAgICAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzPSIyIG1vbnRocyIpKw0KICAgICAgICAgICAgICAgICAgICB0aGVtZV9oYWxmX29wZW4oKSArDQogICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmRfZ3JpZCgpICsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAncmlnaHQnKQ0KcGxvdERhdGVzRGVuc2l0eVRvdGFsDQoNCnJtKHBsb3REYXRlc0RlbnNpdHlUb3RhbCkNCmBgYA0KDQpJdCdzIGltcG9ydGFudCB0byBub3RlLCB0aGF0IEkgY29sbGVjdCBkYXRhIGZyb20gdGhlIEFvMyBzZWFyY2ggcGFnZSAocmF0aGVyIHRoYW4gd29ya3MgcGFnZXMsIGFzIGl0J3MgbGVzcyBkaXNydXB0aXZlKSwgc28gSSBkb24ndCBoYXZlIGFjY2VzcyB0byBpbml0aWFsIHBvc3RhZ2UgZGF0ZXMsIG9ubHkgdGhlIGxhdGVzdCB1cGRhdGVzLiBUaGlzIG1lYW5zIHRoYXQgdGhlIHVwd2FyZCB0cmVuZCBpbiB3b3JrcyBvdmVyIHRpbWUgY2FuIGJlIGFuIGFydGlmYWN0IG9mIHNlcmllcyBnZXR0aW5nIG1vcmUgcG9wdWxhciwgYnV0IGFsc28gY291bGQgYmUgYXR0cmlidXRlZCB0byBtdWx0aWNoYXB0ZXIgd29ya3MgZHJpZnRpbmcgZnVydGhlciBpbiB0aW1lIGR1ZSB0byB1cGRhdGVzLg0KDQpJZiB3ZSBwbG90IENvbXBsZXRlIFdvcmtzIGFuZCBXb3JrcyBpbiBQcm9ncmVzcyBzZXBhcmF0ZWx5LCB3ZSBzdGlsbCBzZWUgYW4gdXB3YXJkIHRyZW5kIGluIGJvdGgsIGJ1dCB0aGUgc2xvcGUgY2hhcmFjdGVyaXNpbmcgdGhlIGdyb3d0aCBvZiBXb3JrIEluIHByb2dyZXNzIHBlYWtzIHNlZW1zIHN0ZWVwZXIgdGhhbiBmb3IgQ29tcGxldGUgV29ya3MsIHdoaWNoIHRvIG1lIGluZGljYXRlcyBhdCBsZWFzdCBwYXJ0aWFsIGVmZmVjdCBvZiB0aGUgbXVsdGljaGFwdGVyIGRyaWZ0Lg0KDQpgYGB7ciB0aW1lbGluZVdJUCwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0NCg0KcGxvdERhdGVzRGVuc2l0eSA8LSBnZ3Bsb3Qoc3RhdHMsIGFlcyh4ID0gRGF0ZSwgY29sPVdJUCkpICsgDQogICAgICAgICAgICAgICAgICAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMSkrDQogICAgICAgICAgICAgICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1zZWFzb25zKSsNCiAgICAgICAgICAgICAgICAgICAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzPSIyIG1vbnRocyIpKw0KICAgICAgICAgICAgICAgICAgICB0aGVtZV9oYWxmX29wZW4oKSArDQogICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmRfZ3JpZCgpICsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAncmlnaHQnKQ0KcGxvdERhdGVzRGVuc2l0eQ0KDQpybShwbG90RGF0ZXNEZW5zaXR5KQ0KDQpgYGANCg0KDQojIEVuZ2FnZW1lbnQgcGVyY2VudGlsZXMNCg0KU21hbGwgcGxvdHRpbmcgY2hlYXQ6IGFsbCB0aGUgbnVtYmVycyBvbiB0aGUgWSBheGlzIGFyZSBpbmNyZWFzZWQgYnkgMSB0byBpbmNsdWRlIHRoZSBjYXNlIG9mIDAgaW50byB0aGUgcGxvdCAob3RoZXJ3aXNlIGV4Y2x1ZGVkIGJlY2F1c2Ugb2YgbG9nIHNjYWxlKS4NCg0KKiBBYm91dCA3NSUgb2Ygd29ya3MgaGF2ZSBtb3JlIHRoYW4gYSAxMDAwIHdvcmRzLCBidXQgb25seSBhYm91dCAxMCUgaGF2ZSBtb3JlIHRoYW4gMTAwMDAgd29yZHMuDQoqIE9ubHkgYWJvdXQgMzAlIG9mIHdvcmtzIGhhdmUgb3ZlciBhIDEwMDAgaGl0cy4NCiogT25seSBhYm91dCAyNSUgb2Ygd29ya3MgaGF2ZSBtb3JlIHRoYW4gYSAxMDAga3Vkb3MuDQoqIE9ubHkgYWJvdXQgNDAlIG9mIHdvcmtzIGdldCBtb3JlIHRoYW4gMTAgY29tbWVudHMuDQoqIEFwcHJveGltYXRlbHkgMTAlIG9mIHdvcmtzIGhhdmUgbm8gY29tbWVudHMgKHRhaWwgZW5kKS4NCiogT25seSBhcHByb3hpbWF0ZWx5IDI1JSBvZiB3b3JrcyBnZXQgbW9yZSB0aGFuIDEwIGJvb2ttYXJrcy4NCiogQXBwcm94aW1hdGVseSAyMCUgb2Ygd29ya3MgaGF2ZSBubyBib29rbWFya3MgKHRhaWwgZW5kKS4NCg0KYGBge3IgcGVyY2VudGlsZXMsIG1lc3NhZ2UgPSBGQUxTRX0NCndvcmRzUGVyY2VudGlsZXMgPC0gcGxvdF9wZXJjZW50aWxlcyhwZXJjZW50aWxlRGF0YSwgJ1dvcmtzLlBlcmNlbnRpbGUnLCAnV29yZHMnLCAncmlnaHQnKQ0KaGl0c1BlcmNlbnRpbGVzIDwtIHBsb3RfcGVyY2VudGlsZXMocGVyY2VudGlsZURhdGEsICdXb3Jrcy5QZXJjZW50aWxlJywgJ0hpdHMnLCAncmlnaHQnKQ0Ka3Vkb3NQZXJjZW50aWxlcyA8LSBwbG90X3BlcmNlbnRpbGVzKHBlcmNlbnRpbGVEYXRhLCAnV29ya3MuUGVyY2VudGlsZScsICdLdWRvcycsICdyaWdodCcpDQpjb21tZW50c1BlcmNlbnRpbGVzIDwtIHBsb3RfcGVyY2VudGlsZXMocGVyY2VudGlsZURhdGEsICdXb3Jrcy5QZXJjZW50aWxlJywgJ0NvbW1lbnRzJywgJ3JpZ2h0JykNCmJvb2ttYXJrc1BlcmNlbnRpbGVzIDwtIHBsb3RfcGVyY2VudGlsZXMocGVyY2VudGlsZURhdGEsICdXb3Jrcy5QZXJjZW50aWxlJywgJ0Jvb2ttYXJrcycsICdyaWdodCcpDQoNCnBsb3RfZ3JpZCh3b3Jkc1BlcmNlbnRpbGVzICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgaGl0c1BlcmNlbnRpbGVzICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAga3Vkb3NQZXJjZW50aWxlcyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgIGNvbW1lbnRzUGVyY2VudGlsZXMgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICBib29rbWFya3NQZXJjZW50aWxlcyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgIGdldF9sZWdlbmQoa3Vkb3NQZXJjZW50aWxlcyArDQogICAgICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKSkpDQoNCnJtKHRvdGFsLCBwZXJjZW50aWxlLCBwZXJjZW50aWxlRGF0YSwgd29yZHNQZXJjZW50aWxlcywgaGl0c1BlcmNlbnRpbGVzLCBrdWRvc1BlcmNlbnRpbGVzLCBjb21tZW50c1BlcmNlbnRpbGVzLCBib29rbWFya3NQZXJjZW50aWxlcykNCg0KYGBgDQoNCiMgQ29tcGxldGUgV29yayB2cyBXb3JrIGluIFByb2dyZXNzIGRpc3RyaWJ1dGlvbnMNCg0KKiBUaGVyZSBhcmUgYXBwcm94aW1hdGVseSA0IHRpbWVzIGFzIG1hbnkgQ29tcGxldGUgV29ya3MgYXMgdGhlcmUgYXJlIFdvcmtzIGluIFByb2dyZXNzLg0KKiBXb3JrcyBpbiBQcm9ncmVzcyBhcmUgYXBwcm94aW1hdGVseSA0IHRpbWVzIGxvbmdlciB0aGFuIENvbXBsZXRlIG9uZXMuDQoqIFdvcmtzIGluIFByb2dyZXNzIGdldCBhcHByb3hpbWF0ZWx5IHR3aWNlIGFzIG1hbnkgaGl0cyBhcyBDb21wbGV0ZSBvbmVzLg0KKiBXb3JrcyBpbiBQcm9ncmVzcyBnZXQgYXBwcm94aW1hdGVseSAyNSUgbW9yZSBrdWRvcyB0aGFuIENvbXBsZXRlIG9uZXMuDQoqIFdvcmtzIGluIFByb2dyZXNzIGdldCBhcHByb3hpbWF0ZWx5IDMgdGltZXMgYXMgbWFueSBjb21tZW50cyBhcyBDb21wbGV0ZSBvbmVzIChob3dldmVyIHRoZXJlJ3Mgbm8gd2F5IHRvIGZpbHRlciBvdXQgYXV0aG9yJ3MgY29tbWVudHMgaW4gdGhlIHNlYXJjaC4gc2VsZWN0aW9uLCBzbyB0aGlzIHN0YXRpc3RpYyBzaG91bGQgYmUgdGFrZW4gd2l0aCBhIGdyYWluIG9mIHNhbHQpLg0KKiBXb3JrcyBpbiBQcm9ncmVzcyBnZXQgYXBwcm94aW1hdGVseSB0d2ljZSBhcyBtYW55IGJvb2ttYXJrcyBhcyBDb21wbGV0ZSBvbmVzLg0KDQpgYGB7ciB0b3RhbFdvcmtzV0lQLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTZ9DQoNCnN0YXRzV0lQIDwtIHN0YXRzDQpzdGF0c1dJUCREaXZpc29yIDwtIHVubGlzdChsYXBwbHkoc3RhdHNXSVAkV0lQLCBmdW5jdGlvbih4KSBzdW1tYXJ5KHN0YXRzV0lQJFdJUClbbmFtZXMoc3VtbWFyeShzdGF0c1dJUCRXSVApKSA9PSB4XSkpDQpzdGF0c1dJUCRXb3Jkcy5wZXIuV29yayA8LSBzdGF0c1dJUCRXb3Jkcy9zdGF0c1dJUCREaXZpc29yDQpzdGF0c1dJUCRIaXRzLnBlci5Xb3JrIDwtIHN0YXRzV0lQJEhpdHMvc3RhdHNXSVAkRGl2aXNvcg0Kc3RhdHNXSVAkS3Vkb3MucGVyLldvcmsgPC0gc3RhdHNXSVAkS3Vkb3Mvc3RhdHNXSVAkRGl2aXNvcg0Kc3RhdHNXSVAkQ29tbWVudHMucGVyLldvcmsgPC0gc3RhdHNXSVAkQ29tbWVudHMvc3RhdHNXSVAkRGl2aXNvcg0Kc3RhdHNXSVAkQm9va21hcmtzLnBlci5Xb3JrIDwtIHN0YXRzV0lQJEJvb2ttYXJrcy9zdGF0c1dJUCREaXZpc29yDQoNCmJhcldvcmtzV0lQIDwtIHBsb3RfYmFyKHN0YXRzV0lQLCAnV0lQJywgJ3JpZ2h0JykNCmJhcldvcmRzV0lQIDwtIHBsb3RfY29sKHN0YXRzV0lQLCAnV0lQJywgJ1dvcmRzLnBlci5Xb3JrJywgJ3JpZ2h0JykNCmJhckhpdHNXSVAgPC0gcGxvdF9jb2woc3RhdHNXSVAsICdXSVAnLCAnSGl0cy5wZXIuV29yaycsICdyaWdodCcpDQpiYXJLdWRvc1dJUCA8LSBwbG90X2NvbChzdGF0c1dJUCwgJ1dJUCcsICdLdWRvcy5wZXIuV29yaycsICdyaWdodCcpDQpiYXJDb21tZW50c1dJUCA8LSBwbG90X2NvbChzdGF0c1dJUCwgJ1dJUCcsICdDb21tZW50cy5wZXIuV29yaycsICdyaWdodCcpDQpiYXJCb29rbWFya3NXSVAgPC0gcGxvdF9jb2woc3RhdHNXSVAsICdXSVAnLCAnQm9va21hcmtzLnBlci5Xb3JrJywgJ3JpZ2h0JykNCg0KIyBwbG90X2dyaWQocGxvdF9ncmlkKCBiYXJXb3Jrc1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KIyAgICAgICAgICAgICAgICAgICAgICBiYXJXb3Jkc1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KIyAgICAgICAgICAgICAgICAgICAgICBiYXJIaXRzV0lQICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQojICAgICAgICAgICAgICAgICAgICAgIGJhckt1ZG9zV0lQICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQojICAgICAgICAgICAgICAgICAgICAgIGJhckNvbW1lbnRzV0lQICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQojICAgICAgICAgICAgICAgICAgICAgIGJhckJvb2ttYXJrc1dJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KIyAgICAgICAgICAgICAgICAgICAgICBhbGlnbiA9ICdodicpLA0KIyAgICAgICAgICAgZ2V0X2xlZ2VuZChiYXJXb3Jrc1dJUCArIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCkpKSwNCiMgICAgICAgICAgIHJlbF93aWR0aHMgPSBjKDQsMSksDQojICAgICAgICAgICBhbGlnbiA9ICdodicpDQpwbG90X2dyaWQoIGJhcldvcmtzV0lQICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhcldvcmRzV0lQICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckhpdHNXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYmFyS3Vkb3NXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYmFyQ29tbWVudHNXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYmFyQm9va21hcmtzV0lQICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGFsaWduID0gJ2h2JykNCg0Kcm0oc3RhdHNXSVAsIGJhcldvcmtzV0lQLCBiYXJXb3Jkc1dJUCwgYmFySGl0c1dJUCwgYmFyS3Vkb3NXSVAsIGJhckNvbW1lbnRzV0lQLCBiYXJCb29rbWFya3NXSVApDQpgYGANCg0KDQpgYGB7ciBzdGF0c0RlbnNpdGllc1dJUCwgbWVzc2FnZSA9IEZBTFNFLCBldmFsPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQp3b3Jkc0RlbnNpdHlXSVAgPC0gcGxvdF9kZW5zaXR5KHN0YXRzLCAnV29yZHMnLCAnV0lQJywgJ3JpZ2h0JykNCmhpdHNEZW5zaXR5V0lQIDwtIHBsb3RfZGVuc2l0eShzdGF0cywgJ0hpdHMnLCAnV0lQJywgJ3JpZ2h0JykNCmt1ZG9zRGVuc2l0eVdJUCA8LSBwbG90X2RlbnNpdHkoc3RhdHMsICdLdWRvcycsICdXSVAnLCAncmlnaHQnKQ0KY29tbWVudHNEZW5zaXR5V0lQIDwtIHBsb3RfZGVuc2l0eShzdGF0cywgJ0NvbW1lbnRzJywgJ1dJUCcsICdyaWdodCcpDQpib29rbWFya3NEZW5zaXR5V0lQIDwtIHBsb3RfZGVuc2l0eShzdGF0cywgJ0Jvb2ttYXJrcycsICdXSVAnLCAncmlnaHQnKQ0KICANCnBsb3RfZ3JpZCh3b3Jkc0RlbnNpdHlXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICBoaXRzRGVuc2l0eVdJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgIGt1ZG9zRGVuc2l0eVdJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgIGNvbW1lbnRzRGVuc2l0eVdJUCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgIGJvb2ttYXJrc0RlbnNpdHlXSVAgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICBnZXRfbGVnZW5kKGt1ZG9zRGVuc2l0eVdJUCArDQogICAgICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKSkpDQoNCnJtKHdvcmRzRGVuc2l0eVdJUCwgaGl0c0RlbnNpdHlXSVAsIGt1ZG9zRGVuc2l0eVdJUCwgY29tbWVudHNEZW5zaXR5V0lQLCBib29rbWFya3NEZW5zaXR5V0lQKQ0KYGBgDQoNCmBgYHtyIHN0YXRzV29yZHNXSVAsIG1lc3NhZ2UgPSBGQUxTRSwgZXZhbD1GQUxTRSwgd2FybmluZz1GQUxTRX0NCndvcmRzSGl0c1dJUCA8LSBwbG90X3BvaW50cyhzdGF0cywgJ1dvcmRzJywgJ0hpdHMnLCAnV0lQJywgJ3JpZ2h0JykNCndvcmRzSGl0c1dJUA0Kd29yZHNLdWRvc1dJUCA8LSBwbG90X3BvaW50cyhzdGF0cywgJ1dvcmRzJywgJ0t1ZG9zJywgJ1dJUCcsICdyaWdodCcpDQp3b3Jkc0t1ZG9zV0lQDQp3b3Jkc0NvbW1lbnRzV0lQIDwtIHBsb3RfcG9pbnRzKHN0YXRzLCAnV29yZHMnLCAnQ29tbWVudHMnLCAnV0lQJywgJ3JpZ2h0JykNCndvcmRzQ29tbWVudHNXSVANCndvcmRzQm9va21hcmtzV0lQIDwtIHBsb3RfcG9pbnRzKHN0YXRzLCAnV29yZHMnLCAnQm9va21hcmtzJywgJ1dJUCcsICdyaWdodCcpDQp3b3Jkc0Jvb2ttYXJrc1dJUA0KDQpybSh3b3Jkc0hpdHNXSVAsIHdvcmRzS3Vkb3NXSVAsIHdvcmRzQ29tbWVudHNXSVAsIHdvcmRzQm9va21hcmtzV0lQKQ0KYGBgDQoNCiMgUmF0aW5nIGRpc3RyaWJ1dGlvbnMNCg0KKiBXb3JrcyByYXRlZCBHIG1ha2UgdXAgdGhlIG1vc3QgbnVtZXJvdXMgY2F0ZWdvcnkgKH4xMzAwKSwgYnV0IHRoZXkgYXJlIG9uIGF2ZXJhZ2UgdGhlIHNob3J0ZXN0ICh+MjUwMCB3b3JkcykgYW5kIGdldCBmZXdlc3QgaGl0cyg+MTAwMCksIGFuZCBmZXdlc3QgY29tbWVudHMgKH4xMCkuDQoqIFdvcmtzIHJhdGVkIFQgZm9sbG93IGluIG51bWJlcnMgKH45MDApLCBidXQgYXJlIHNpZ25pZmljYW50bHkgbG9uZ2VyICh+OTAwMCB3b3JkcykuDQoqIFdvcmtzIHJhdGVkIE0gbWFrZSBmb3IgdGhlIHNtYWxsZXN0IGNhdGVnb3J5ICh0aWVkIHdpdGggTm90IFJhdGVkLCBhdCB+IDIwMCksIGJ1dCBhcmUgdGhlIGxvbmdlc3QgKH4xM2sgd29yZHMpLg0KKiBXb3JrcyByYXRlZCBFIGFyZSBmZXcgKH4zNTApLCBsb25nZXIgdGhhbiBHIGJ1dCBzaG9ydGVyIHRoYW4gVCByYXRlZCB3b3JrcyAofjYwMDAgd29yZHMpLCBhbmQgZ2V0IHRoZSBtb3N0IGhpdHMsIGJ1dCBmZXdlciBrdWRvcyBhbmQgc2lnbmlmaWNhbnRseSBsZXNzIGNvbW1lbnRzIHRoYW4gVCBhbmQgTSByYXRlZCB3b3Jrcy4NCg0KDQpgYGB7ciB0b3RhbFdvcmtzUmF0aW5nLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9DQoNCnN0YXRzUmF0aW5nIDwtIHN0YXRzDQpzdGF0c1JhdGluZyREaXZpc29yIDwtIHVubGlzdChsYXBwbHkoc3RhdHNSYXRpbmckUmF0aW5nLCBmdW5jdGlvbih4KSBzdW1tYXJ5KHN0YXRzUmF0aW5nJFJhdGluZylbbmFtZXMoc3VtbWFyeShzdGF0c1JhdGluZyRSYXRpbmcpKSA9PSB4XSkpDQpzdGF0c1JhdGluZyRXb3Jkcy5wZXIuV29yayA8LSBzdGF0c1JhdGluZyRXb3Jkcy9zdGF0c1JhdGluZyREaXZpc29yDQpzdGF0c1JhdGluZyRIaXRzLnBlci5Xb3JrIDwtIHN0YXRzUmF0aW5nJEhpdHMvc3RhdHNSYXRpbmckRGl2aXNvcg0Kc3RhdHNSYXRpbmckS3Vkb3MucGVyLldvcmsgPC0gc3RhdHNSYXRpbmckS3Vkb3Mvc3RhdHNSYXRpbmckRGl2aXNvcg0Kc3RhdHNSYXRpbmckQ29tbWVudHMucGVyLldvcmsgPC0gc3RhdHNSYXRpbmckQ29tbWVudHMvc3RhdHNSYXRpbmckRGl2aXNvcg0Kc3RhdHNSYXRpbmckQm9va21hcmtzLnBlci5Xb3JrIDwtIHN0YXRzUmF0aW5nJEJvb2ttYXJrcy9zdGF0c1JhdGluZyREaXZpc29yDQoNCmJhcldvcmtzUmF0aW5nIDwtIHBsb3RfYmFyKHN0YXRzUmF0aW5nLCAnUmF0aW5nJywgJ3JpZ2h0JykNCmJhcldvcmRzUmF0aW5nIDwtIHBsb3RfY29sKHN0YXRzUmF0aW5nLCAnUmF0aW5nJywgJ1dvcmRzLnBlci5Xb3JrJywgJ3JpZ2h0JykNCmJhckhpdHNSYXRpbmcgPC0gcGxvdF9jb2woc3RhdHNSYXRpbmcsICdSYXRpbmcnLCAnSGl0cy5wZXIuV29yaycsICdyaWdodCcpDQpiYXJLdWRvc1JhdGluZyA8LSBwbG90X2NvbChzdGF0c1JhdGluZywgJ1JhdGluZycsICdLdWRvcy5wZXIuV29yaycsICdyaWdodCcpDQpiYXJDb21tZW50c1JhdGluZyA8LSBwbG90X2NvbChzdGF0c1JhdGluZywgJ1JhdGluZycsICdDb21tZW50cy5wZXIuV29yaycsICdyaWdodCcpDQpiYXJCb29rbWFya3NSYXRpbmcgPC0gcGxvdF9jb2woc3RhdHNSYXRpbmcsICdSYXRpbmcnLCAnQm9va21hcmtzLnBlci5Xb3JrJywgJ3JpZ2h0JykNCg0KcGxvdF9ncmlkKCBiYXJXb3Jrc1JhdGluZyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJXb3Jkc1JhdGluZyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJIaXRzUmF0aW5nICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckt1ZG9zUmF0aW5nICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckNvbW1lbnRzUmF0aW5nICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckJvb2ttYXJrc1JhdGluZyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBhbGlnbiA9ICdodicpDQoNCnJtKHN0YXRzUmF0aW5nLCBiYXJXb3Jrc1JhdGluZywgYmFyV29yZHNSYXRpbmcsIGJhckhpdHNSYXRpbmcsIGJhckt1ZG9zUmF0aW5nLCBiYXJDb21tZW50c1JhdGluZywgYmFyQm9va21hcmtzUmF0aW5nKQ0KYGBgDQoNCmBgYHtyIHN0YXRzRGVuc2l0aWVzUmF0aW5nLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9RkFMU0V9DQp3b3Jkc0RlbnNpdHlSYXRpbmcgPC0gcGxvdF9kZW5zaXR5KHN0YXRzLCAnV29yZHMnLCAnUmF0aW5nJywgJ3JpZ2h0JykNCmhpdHNEZW5zaXR5UmF0aW5nIDwtIHBsb3RfZGVuc2l0eShzdGF0cywgJ0hpdHMnLCAnUmF0aW5nJywgJ3JpZ2h0JykNCmt1ZG9zRGVuc2l0eVJhdGluZyA8LSBwbG90X2RlbnNpdHkoc3RhdHMsICdLdWRvcycsICdSYXRpbmcnLCAncmlnaHQnKQ0KY29tbWVudHNEZW5zaXR5UmF0aW5nIDwtIHBsb3RfZGVuc2l0eShzdGF0cywgJ0NvbW1lbnRzJywgJ1JhdGluZycsICdyaWdodCcpDQpib29rbWFya3NEZW5zaXR5UmF0aW5nIDwtIHBsb3RfZGVuc2l0eShzdGF0cywgJ0Jvb2ttYXJrcycsICdSYXRpbmcnLCAncmlnaHQnKQ0KDQpwbG90X2dyaWQod29yZHNEZW5zaXR5UmF0aW5nICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgaGl0c0RlbnNpdHlSYXRpbmcgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICBrdWRvc0RlbnNpdHlSYXRpbmcgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICBjb21tZW50c0RlbnNpdHlSYXRpbmcgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICBib29rbWFya3NEZW5zaXR5UmF0aW5nICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgZ2V0X2xlZ2VuZChrdWRvc0RlbnNpdHlSYXRpbmcgKw0KICAgICAgICAgICAgICAgICAgICAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSkpKQ0KDQpybSh3b3Jkc0RlbnNpdHlSYXRpbmcsIGhpdHNEZW5zaXR5UmF0aW5nLCBrdWRvc0RlbnNpdHlSYXRpbmcsIGNvbW1lbnRzRGVuc2l0eVJhdGluZywgYm9va21hcmtzRGVuc2l0eVJhdGluZykNCmBgYA0KDQoNCmBgYHtyIHN0YXRzV29yZHNSYXRpbmcsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1GQUxTRX0NCndvcmRzSGl0c1JhdGluZyA8LSBwbG90X3BvaW50cyhzdGF0cywgJ1dvcmRzJywgJ0hpdHMnLCAnUmF0aW5nJywgJ3JpZ2h0JykNCndvcmRzSGl0c1JhdGluZw0Kd29yZHNLdWRvc1JhdGluZyA8LSBwbG90X3BvaW50cyhzdGF0cywgJ1dvcmRzJywgJ0t1ZG9zJywgJ1JhdGluZycsICdyaWdodCcpDQp3b3Jkc0t1ZG9zUmF0aW5nDQp3b3Jkc0NvbW1lbnRzUmF0aW5nIDwtIHBsb3RfcG9pbnRzKHN0YXRzLCAnV29yZHMnLCAnQ29tbWVudHMnLCAnUmF0aW5nJywgJ3JpZ2h0JykNCndvcmRzQ29tbWVudHNSYXRpbmcNCndvcmRzQm9va21hcmtzUmF0aW5nIDwtIHBsb3RfcG9pbnRzKHN0YXRzLCAnV29yZHMnLCAnQm9va21hcmtzJywgJ1JhdGluZycsICdyaWdodCcpDQp3b3Jkc0Jvb2ttYXJrc1JhdGluZw0KDQpybSh3b3Jkc0hpdHNSYXRpbmcsIHdvcmRzS3Vkb3NSYXRpbmcsIHdvcmRzQ29tbWVudHNSYXRpbmcsIHdvcmRzQm9va21hcmtzUmF0aW5nKQ0KDQpgYGANCg0KIyBDYXRlZ29yaWVzDQoNClRoZXJlIGFyZSBgciBsZW5ndGgoY2F0ZWdvcnlbdW5saXN0KGxhcHBseShjYXRlZ29yeSwgZnVuY3Rpb24oeCkgbGVuZ3RoKHgpKSkgPT0gMV0pYCB3b3JrcyB0YWdnZWQgd2l0aCBhIHNpbmdsZSBjYXRlZ29yeSwgYW5kIGByIGxlbmd0aChjYXRlZ29yeVt1bmxpc3QobGFwcGx5KGNhdGVnb3J5LCBmdW5jdGlvbih4KSBsZW5ndGgoeCkpKSA+IDFdKWAgdGFnZ2VkIHdpdGggMiBvciBtb3JlICh1cCB1bnRpbCBhbGwgNikuDQoNCidGL00nIGlzIHRoZSBtb3N0IHBvcHVsYXIgY2F0ZWdvcnksIGZvbGxvd2VkIGJ5ICdNL00nLCAnR2VuJywgYW5kICdGL0YnLg0KDQpNdWx0aXBsZSBjYXRlZ29yeSBmaWNzIHN0cm9uZ2x5IGNvbnRyaWJ1dGUgdG93YXJkcyAnRi9NJyBjb3VudCwgdGhlbiB0byAnTS9NJywgJ0dlbicsIGFuZCAnRi9GJywgYW5kIG9ubHkgbWFyZ2luYWxseSB0byAnTXVsdGknIGFuZCAnT3RoZXInLg0KDQpgYGB7ciBjYXRlZ29yaWVzQmFycywgbWVzc2FnZSA9IEZBTFNFfQ0KDQpzaW5nbGVDYXRlZ29yeVN1bW1hcnkgPC0gc3VtbWFyeShhcy5mYWN0b3IodW5saXN0KGNhdGVnb3J5W3VubGlzdChsYXBwbHkoY2F0ZWdvcnksIGZ1bmN0aW9uKHgpIGxlbmd0aCh4KSkpID09IDFdKSkpDQpzaW5nbGVDYXRlZ29yeVN1bW1hcnkgPC0gZGF0YS5mcmFtZShDYXRlZ29yeSA9IG5hbWVzKHNpbmdsZUNhdGVnb3J5U3VtbWFyeSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOdW1iZXIub2YuV29ya3MgPSBzaW5nbGVDYXRlZ29yeVN1bW1hcnkpDQpzaW5nbGVDYXRlZ29yeVN1bW1hcnkkU3BsaXQgPC0gIlNpbmdsZSBjYXRlZ29yeSINCg0KbXVsdGlwbGVDYXRlZ29yeVN1bW1hcnkgPC0gZGF0YS5mcmFtZShDYXRlZ29yeSA9IGMoJ0dlbicsICdGL0YnLCAnRi9NJywgJ00vTScsICdNdWx0aScsICdPdGhlcicsICdObyBjYXRlZ29yeScpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVtYmVyLm9mLldvcmtzID0gYyhzdW0oZ3JlcGwoJ0dlbicsY2F0ZWdvcnkpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGdyZXBsKCdGL0YnLGNhdGVnb3J5KSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgnRi9NJyxjYXRlZ29yeSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZ3JlcGwoJ00vTScsY2F0ZWdvcnkpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGdyZXBsKCdNdWx0aScsY2F0ZWdvcnkpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGdyZXBsKCdPdGhlcicsY2F0ZWdvcnkpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGdyZXBsKCdObyBjYXRlZ29yeScsY2F0ZWdvcnkpKSkgKQ0KbXVsdGlwbGVDYXRlZ29yeVN1bW1hcnkkU3BsaXQgPC0gIkFsbCB3b3JrcyINCg0KY2F0ZWdvcnlTdW1tYXJ5IDwtIHJiaW5kKHNpbmdsZUNhdGVnb3J5U3VtbWFyeSwgbXVsdGlwbGVDYXRlZ29yeVN1bW1hcnkpDQpjYXRlZ29yeVN1bW1hcnkkQ2F0ZWdvcnkgPC0gZmFjdG9yKGNhdGVnb3J5U3VtbWFyeSRDYXRlZ29yeSwgbGV2ZWxzID0gYygnR2VuJywgJ0YvRicsICdGL00nLCAnTS9NJywgJ011bHRpJywgJ090aGVyJywgJ05vIGNhdGVnb3J5JykpDQpjYXRlZ29yeVN1bW1hcnkkU3BsaXQgPC0gZmFjdG9yKGNhdGVnb3J5U3VtbWFyeSRTcGxpdCwgbGV2ZWxzID0gYygiU2luZ2xlIGNhdGVnb3J5IiwgIkFsbCB3b3JrcyIpKQ0KDQpwbG90Q2F0ZWdvcmllcyA8LSBnZ3Bsb3QoY2F0ZWdvcnlTdW1tYXJ5LCBhZXMoeCA9IENhdGVnb3J5LCB5ID0gTnVtYmVyLm9mLldvcmtzKSkgKyANCiAgICAgICAgICAgICAgICAgIGdlb21fY29sKGFscGhhPTEpKw0KICAgICAgICAgICAgICAgICAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICAgICAgICAgICAgICAgICAgYmFja2dyb3VuZF9ncmlkKCkgKw0KICAgICAgICAgICAgICAgICAgZmFjZXRfd3JhcCguflNwbGl0KSArDQogICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAxLCBoanVzdD0xKSkrDQogICAgICAgICAgICAgICAgICBsYWJzKHk9Ik51bWJlciBvZiBXb3JrcyIpDQpwbG90Q2F0ZWdvcmllcw0KDQpybShzaW5nbGVDYXRlZ29yeVN1bW1hcnksIG11bHRpcGxlQ2F0ZWdvcnlTdW1tYXJ5LCBjYXRlZ29yeVN1bW1hcnksIHBsb3RDYXRlZ29yaWVzKQ0KDQpgYGANCg0KIyBFbmdhZ2VtZW50IGJ5IGEgc2luZ2xlIGNhdGVnb3J5DQoNCkZvciBzaW1wbGljaXR5IEknbSBvbmx5IGxvb2tpbmcgYXQgd29ya3MgdGFnZ2VkIHdpdGggYSBzaW5nbGUgY2F0ZWdvcnkgaGVyZS4NCg0KIk11bHRpIiBzZWVtcyB0byBoYXZlIG1vc3Qgd29yZHMsIGRlc3BpdGUgYmVpbmcgYSByYXRoZXIgc21hbGwgY2F0ZWdvcnksIGFuZCBjb2xsZWN0cyBxdWl0ZSBhIGJpdCBvZiBIaXRzIGFuZCBDb21tZW50cy4gSXQncyBwb3NzaWJsZSB0aGF0IGEgbnVtYmVyIG9mIHRob3NlIHdvcmtzIGFyZSBjb2xsZWN0aW9ucyBvZiBzdG9yaWVzIGZvciBtYW55IGZhbmRvbXMsIHdoaWNoIGFtcGxpZmllcyB0aGUgbnVtYmVyIG9mIEhpdHMgYW5kIENvbW1lbnRzLCBidXQgdGhhdCByZXF1aXJlcyBmdXJ0aGVyIGludmVzdGlnYXRpb24uDQoNCmBgYHtyIGNhdGVnb3JpZXNTaW5nbGVFbmdhZ2VtZW50LCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTZ9DQoNCnN0YXRzQ2F0ZWdvcnkgPC0gc3RhdHNbdW5saXN0KGxhcHBseShjYXRlZ29yeSwgZnVuY3Rpb24oeCkgbGVuZ3RoKHgpKSkgPT0gMSxdDQpzdGF0c0NhdGVnb3J5JENhdGVnb3J5IDwtIGFzLmZhY3Rvcih1bmxpc3QoY2F0ZWdvcnlbdW5saXN0KGxhcHBseShjYXRlZ29yeSwgZnVuY3Rpb24oeCkgbGVuZ3RoKHgpKSkgPT0gMV0pKQ0Kc3RhdHNDYXRlZ29yeSRDYXRlZ29yeSA8LSBmYWN0b3Ioc3RhdHNDYXRlZ29yeSRDYXRlZ29yeSwgbGV2ZWxzID0gYygnR2VuJywgJ0YvRicsICdGL00nLCAnTS9NJywgJ011bHRpJywgJ090aGVyJywgJ05vIGNhdGVnb3J5JykpDQpzdGF0c0NhdGVnb3J5JERpdmlzb3IgPC0gdW5saXN0KGxhcHBseShzdGF0c0NhdGVnb3J5JENhdGVnb3J5LCBmdW5jdGlvbih4KSBzdW1tYXJ5KHN0YXRzQ2F0ZWdvcnkkQ2F0ZWdvcnkpW25hbWVzKHN1bW1hcnkoc3RhdHNDYXRlZ29yeSRDYXRlZ29yeSkpID09IHhdKSkNCnN0YXRzQ2F0ZWdvcnkkV29yZHMucGVyLldvcmsgPC0gc3RhdHNDYXRlZ29yeSRXb3Jkcy9zdGF0c0NhdGVnb3J5JERpdmlzb3INCnN0YXRzQ2F0ZWdvcnkkSGl0cy5wZXIuV29yayA8LSBzdGF0c0NhdGVnb3J5JEhpdHMvc3RhdHNDYXRlZ29yeSREaXZpc29yDQpzdGF0c0NhdGVnb3J5JEt1ZG9zLnBlci5Xb3JrIDwtIHN0YXRzQ2F0ZWdvcnkkS3Vkb3Mvc3RhdHNDYXRlZ29yeSREaXZpc29yDQpzdGF0c0NhdGVnb3J5JENvbW1lbnRzLnBlci5Xb3JrIDwtIHN0YXRzQ2F0ZWdvcnkkQ29tbWVudHMvc3RhdHNDYXRlZ29yeSREaXZpc29yDQpzdGF0c0NhdGVnb3J5JEJvb2ttYXJrcy5wZXIuV29yayA8LSBzdGF0c0NhdGVnb3J5JEJvb2ttYXJrcy9zdGF0c0NhdGVnb3J5JERpdmlzb3INCnN0YXRzQ2F0ZWdvcnkkV29ya3MuUGVyY2VudCA8LSAxL3N0YXRzQ2F0ZWdvcnkkRGl2aXNvcg0KDQpiYXJXb3Jrc0NhdGVnb3J5IDwtIHBsb3RfYmFyX2NvbG9yKHN0YXRzQ2F0ZWdvcnksICdDYXRlZ29yeScsICdSYXRpbmcnLCAncmlnaHQnKQ0KYmFyV29yZHNDYXRlZ29yeSA8LSBwbG90X2NvbF9jb2xvcihzdGF0c0NhdGVnb3J5LCAnQ2F0ZWdvcnknLCAnV29yZHMucGVyLldvcmsnLCAnUmF0aW5nJywgJ3JpZ2h0JykNCmJhckhpdHNDYXRlZ29yeSA8LSBwbG90X2NvbF9jb2xvcihzdGF0c0NhdGVnb3J5LCAnQ2F0ZWdvcnknLCAnSGl0cy5wZXIuV29yaycsICdSYXRpbmcnLCAncmlnaHQnKQ0KYmFyS3Vkb3NDYXRlZ29yeSA8LSBwbG90X2NvbF9jb2xvcihzdGF0c0NhdGVnb3J5LCAnQ2F0ZWdvcnknLCAnS3Vkb3MucGVyLldvcmsnLCAnUmF0aW5nJywgJ3JpZ2h0JykNCmJhckNvbW1lbnRzQ2F0ZWdvcnkgPC0gcGxvdF9jb2xfY29sb3Ioc3RhdHNDYXRlZ29yeSwgJ0NhdGVnb3J5JywgJ0NvbW1lbnRzLnBlci5Xb3JrJywgJ1JhdGluZycsICdyaWdodCcpDQpiYXJCb29rbWFya3NDYXRlZ29yeSA8LSBwbG90X2NvbF9jb2xvcihzdGF0c0NhdGVnb3J5LCAnQ2F0ZWdvcnknLCAnQm9va21hcmtzLnBlci5Xb3JrJywnUmF0aW5nJywgJ3JpZ2h0JykNCg0KcGxvdF9ncmlkKHBsb3RfZ3JpZCggYmFyV29ya3NDYXRlZ29yeSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJXb3Jkc0NhdGVnb3J5ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckhpdHNDYXRlZ29yeSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLA0KICAgICAgICAgICBiYXJLdWRvc0NhdGVnb3J5ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksDQogICAgICAgICAgIGJhckNvbW1lbnRzQ2F0ZWdvcnkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYmFyQm9va21hcmtzQ2F0ZWdvcnkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwNCiAgICAgICAgICAgYWxpZ24gPSAnaHYnKSwNCiAgICAgICAgICBnZXRfbGVnZW5kKGJhcldvcmtzQ2F0ZWdvcnkgKyB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKSksDQogICAgICAgICAgcmVsX3dpZHRocyA9IGMoNCwxKSkNCg0KYGBgDQoNCiMgUmF0aW5ncyBwZXJjZW50YWdlcyBieSBhIHNpbmdsZSBjYXRlZ29yeQ0KDQpPdXQgb2YgdGhlIDMgbWFpbiBzaGlwcGluZyBjYXRlZ29yaWVzIGluIGFic29sdXRlIG51bWJlcnMgIk0vTSIgaGFzIG1vc3QgRSByYXRlZCB3b3JrcywgYW5kICJGL0YiIGhhcyB0aGUgbGVhc3QsIGJ1dCBpbiBwZXJjZW50YWdlcyBvZiB0b3RhbCB3b3JrcyAiRi9GIiBhbmQgIkYvTSIgYXJlIGRpc3RyaWJ1dGVkIGFsbW9zdCBpZGVudGljYWxseSwgd2hpbGUgIk0vTSIgc2tld3MgbW9yZSB0b3dhcmRzIE0gYW5kIEUgcmF0ZWQgd29ya3MuDQoNCmBgYHtyIGNhdGVnb3JpZXNTaW5nbGVFbmdhZ2VtZW50UGVyY2VudCwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQ0KDQpwbG90V29ya3NDYXRlZ29yeU5vcm1hbGl6ZWQgPC0gcGxvdF9jb2xfY29sb3Ioc3RhdHNDYXRlZ29yeSwgJ1JhdGluZycsICdXb3Jrcy5QZXJjZW50JywgJ1JhdGluZycsICdub25lJykrDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2V0X3dyYXAoLn5DYXRlZ29yeSkNCnBsb3RXb3Jrc0NhdGVnb3J5Tm9ybWFsaXplZA0KDQpybShiYXJXb3Jrc0NhdGVnb3J5LCBiYXJXb3Jkc0NhdGVnb3J5LCBiYXJIaXRzQ2F0ZWdvcnksIGJhckt1ZG9zQ2F0ZWdvcnksIGJhckNvbW1lbnRzQ2F0ZWdvcnksIGJhckJvb2ttYXJrc0NhdGVnb3J5LCBwbG90V29ya3NDYXRlZ29yeU5vcm1hbGl6ZWQpDQoNCmBgYA0KDQojIFNpbmdsZSBDYXRlZ29yeSB0aHJvdWdoIHRpbWUNCg0KSW50ZXJlc3RpbmdseSwgc2Vhc29uIDEgc2VlcyBhIHBlYWsgb2YgJ0dlbicgY2F0ZWdvcnkuIFNlYXNvbiAyIGdpdmVzIGhpZ2hlciByaXNlIHRvICdNL00nIChwb3NzaWJseSByZWxhdGVkIHRvIEFhcmF2b3MgcmV2ZWFsIGFuZCAnQWFyYXZvcy9WaXJlbiAoVGhlIERyYWdvbiBQcmluY2UpJyBzaGlwcGluZykgYW5kICdGL00nICh0aGUgcmlzZSBvZiAnQ2FsbHVtL1JheWxhIChUaGUgRHJhZ29uIFByaW5jZSknPyksIGFuZCBzZWFzb24gMyBpcyBmb2xsb3dlZCBieSBhIGhpZ2ggcmlzZSBvZiAnRi9GJyAoZm9sbG93aW5nIHRoZSBkZXZlbG9wbWVudCBvZiAnQW1heWEvSmFuYWkgKFRoZSBEcmFnb24gUHJpbmNlKScgcmVsYXRpb25zaGlwKSBhbmQgYSBtb2Rlc3QgJ00vTScgcGVhayAoJ0V0aGFyaS9SdW5hYW4gKFRoZSBEcmFnb24gUHJpbmNlKScgZHVlIHRvIEV0aGFyaSBmaW5hbGx5IGdldHRpbmcgb2ZmaWNpYWwgbmFtZT8pLg0KDQpgYGB7ciBzaW5nbGVDYXRlZ29yeVRpbWUsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTZ9DQoNCnBsb3REYXRlc0NhdGVnb3J5RGVuc2l0eSA8LSBnZ3Bsb3Qoc3RhdHNDYXRlZ29yeSwgYWVzKHggPSBEYXRlLCBjb2w9Q2F0ZWdvcnkpKSArIA0KICAgICAgICAgICAgICAgICAgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjEpKw0KICAgICAgICAgICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9c2Vhc29ucykrDQogICAgICAgICAgICAgICAgICAgIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcz0iMiBtb250aHMiKSsNCiAgICAgICAgICAgICAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGN1c3RvbXBhbGV0dGUpICsNCiAgICAgICAgICAgICAgICAgICAgdGhlbWVfaGFsZl9vcGVuKCkgKw0KICAgICAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kX2dyaWQoKSArDQogICAgICAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gJ3JpZ2h0JykNCnBsb3REYXRlc0NhdGVnb3J5RGVuc2l0eQ0KDQpybShwbG90RGF0ZXNDYXRlZ29yeURlbnNpdHkpDQpgYGANCg0KIyBNb3N0IHBvcHVsYXIgc2hpcCB0YWdzDQoNClVuZGVuaWFibHkgdGhlIG1vc3QgcG9wdWxhciBzaGlwIHRhZyBpcyAiQ2FsbHVtL1JheWxhIChUaGUgRHJhZ29uIFByaW5jZSkiLCBzY29yaW5nIGFsbW9zdCA0IHRpbWVzIGFzIG1hbnkgc3RvcmllcyBhcyB0aGUgbmV4dCBtb3N0IHBvcHVsYXIgc2hpcCB0YWcgIkFtYXlhL0phbmFpIChUaGUgRHJhZ29uIFByaW5jZSkiLg0KDQpgYGB7ciBzaGlwc0hpc3RvZ3JhbSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02fQ0KDQp0b3BMaXN0IDwtIDgNCnRvcFJlbGF0aW9uc2hpcHNUYWJsZTwtIGRhdGEuZnJhbWUoJ1JlbGF0aW9uc2hpcCcgPSBuYW1lcyhzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QocmVsYXRpb25zaGlwcykpKVsxOnRvcExpc3RdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgJ051bWJlciBvZiBTdG9yaWVzJyA9IHN1bW1hcnkoYXMuZmFjdG9yKHVubGlzdChyZWxhdGlvbnNoaXBzKSkpWzE6dG9wTGlzdF0pDQpyb3cubmFtZXModG9wUmVsYXRpb25zaGlwc1RhYmxlKSA8LSBjKCkNCnRvcFJlbGF0aW9uc2hpcHNUYWJsZSA8LSB0b3BSZWxhdGlvbnNoaXBzVGFibGVbb3JkZXIodG9wUmVsYXRpb25zaGlwc1RhYmxlJE51bWJlci5vZi5TdG9yaWVzLCBkZWNyZWFzaW5nID0gVFJVRSksXQ0KdG9wUmVsYXRpb25zaGlwc1RhYmxlJFJlbGF0aW9uc2hpcCA8LSBmYWN0b3IoYXMuY2hhcmFjdGVyKHRvcFJlbGF0aW9uc2hpcHNUYWJsZSRSZWxhdGlvbnNoaXApLCBsZXZlbHM9YXMuY2hhcmFjdGVyKHRvcFJlbGF0aW9uc2hpcHNUYWJsZSRSZWxhdGlvbnNoaXApKQ0KDQpyZWxhdGlvbnNoaXBzU3RhdHMgPC0gZGF0YS5mcmFtZShEYXRlID0gc3RhdHMkRGF0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDEgPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDIgPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDMgPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDQgPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDUgPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDYgPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDcgPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbGF0aW9uc2hpcDggPSByZXAoMCwgbGVuZ3RoKHN0YXRzJERhdGUpKSkNCmZvciAoaSBpbiAxOnRvcExpc3Qpew0KICBtYXRjaGluZ1ZlY3RvciA8LSBsYXBwbHkocmVsYXRpb25zaGlwcywgbWF0Y2gsIHRhYmxlPWFzLmNoYXJhY3Rlcih0b3BSZWxhdGlvbnNoaXBzVGFibGUkUmVsYXRpb25zaGlwW2ldKSkNCiAgbWF0Y2hpbmdWZWN0b3IgPC0gdW5saXN0KGxhcHBseShtYXRjaGluZ1ZlY3Rvciwgc3VtLCBuYS5ybT1UUlVFKSkNCiAgcmVsYXRpb25zaGlwc1N0YXRzW2krMV0gPC0gbWF0Y2hpbmdWZWN0b3INCn0NCg0KI2NvbG5hbWVzKHJlbGF0aW9uc2hpcHNTdGF0cylbMjo5XSA8LSBnc3ViKCcvJywgJ1xcLycsIHRvcFJlbGF0aW9uc2hpcHNUYWJsZSRSZWxhdGlvbnNoaXApDQoNCnBsb3RMZWdlbmRSZWxhdGlvbnNoaXBzIDwtIGdncGxvdCh0b3BSZWxhdGlvbnNoaXBzVGFibGUsIGFlcyh4PVJlbGF0aW9uc2hpcCwgeT1OdW1iZXIub2YuU3RvcmllcywgZmlsbD1SZWxhdGlvbnNoaXApKSsNCiAgZ2VvbV9jb2woYWxwaGE9MC43KSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tcGFsZXR0ZSkrDQogIHRoZW1lX2hhbGZfb3BlbigpICsNCiAgYmFja2dyb3VuZF9ncmlkKCkgKw0KICBsYWJzKHg9IiIseT0nTnVtYmVyIG9mIFN0b3JpZXMnKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKQ0KcGxvdExlZ2VuZFJlbGF0aW9uc2hpcHMrdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKQ0KYGBgDQoNCiMgU2hpcCB0YWdzIHRocm91Z2ggdGltZQ0KDQoiQW1heWEvSmFuYWkgKFRoZSBEcmFnb24gUHJpbmNlKSIgc2hhcnBseSB0b29rIG9mIGluIHBvcHVsYXJpdHkgYWZ0ZXIgc2Vhc29uIDMuIEV0aGFyaSdzIG5hbWUgaGFzbid0IGJlZW4gcmV2ZWFsZWQgdW50aWwgc2Vhc29uIDMsIHNvIGF1dGhvcnMgd2VyZSB1c2luZyAiUnVuYWFuL1RpbmtlciB8IE5lY2tsYWNlIEVsZiAoVGhlIERyYWdvbiBQcmluY2UpIiBpbnN0ZWFkIG9mICJFdGhhcmkvUnVuYWFuIChUaGUgRHJhZ29uIFByaW5jZSkiLCBidXQgQW8zIHdvdWxkIGNvbnNpZGVyIHRoZW0gc3lub255bW91cy4gUm9tYW5jZSB0YWcgIkNhbGx1bS9SYXlsYSAoVGhlIERyYWdvbiBQcmluY2UpIiBpcyBzaWduaWZpY2FudGx5IG1vcmUgcG9wdWxhciB0aGFuIHBsYXRvbmljL2ZyaWVuc2hpcCB0YWcgIkNhbGx1bSAmIFJheWxhIChUaGUgRHJhZ29uIFByaW5jZSkiLCBob3dldmVyIGl0J3Mgd29ydGggbm90aW5nIHRoYXQgb3V0IG9mIGByIHRvcFJlbGF0aW9uc2hpcHNUYWJsZVt0b3BSZWxhdGlvbnNoaXBzVGFibGUkUmVsYXRpb25zaGlwID09ICJDYWxsdW0gJiBSYXlsYSAoVGhlIERyYWdvbiBQcmluY2UpIixdJE51bWJlci5vZi5TdG9yaWVzYCBzdG9yaWVzIHRhZ2dlZCB3aXRoIGEgZnJpZW5kc2hpcC9wbGF0b25pYyBvbmUgYHIgc3VtKChyZWxhdGlvbnNoaXBzU3RhdHMkcmVsYXRpb25zaGlwMStyZWxhdGlvbnNoaXBzU3RhdHMkcmVsYXRpb25zaGlwMyk+MSlgIGFyZSB0YWdnZWQgd2l0aCBib3RoLCBwb3NzaWJseSBtYWtpbmcgc3BlY2lmaWNhbGx5IGZyaWVuZHNoaXAvcGxhdG9uaWMgY29udGVudCBtb3JlIGRpZmZpY3VsdCB0byBmaW5kLg0KDQpgYGB7ciBzaGlwVGFnc1RpbWUsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9DQoNCnBsb3RSZWxhdGlvbnNoaXBzIDwtIGdncGxvdCgpICsNCiAgICBnZW9tX2RlbnNpdHkoZGF0YSA9IHJlbGF0aW9uc2hpcHNTdGF0c1tyZWxhdGlvbnNoaXBzU3RhdHMkcmVsYXRpb25zaGlwMSA+IDAsXSwgbWFwcGluZz1hZXMoeCA9IERhdGUpLCBjb2xvdXI9Y3VzdG9tcGFsZXR0ZVsxXSkrDQogICAgZ2VvbV9kZW5zaXR5KGRhdGEgPSByZWxhdGlvbnNoaXBzU3RhdHNbcmVsYXRpb25zaGlwc1N0YXRzJHJlbGF0aW9uc2hpcDIgPiAwLF0sIG1hcHBpbmc9YWVzKHggPSBEYXRlKSwgY29sb3VyPWN1c3RvbXBhbGV0dGVbMl0pKw0KICAgIGdlb21fZGVuc2l0eShkYXRhID0gcmVsYXRpb25zaGlwc1N0YXRzW3JlbGF0aW9uc2hpcHNTdGF0cyRyZWxhdGlvbnNoaXAzID4gMCxdLCBtYXBwaW5nPWFlcyh4ID0gRGF0ZSksIGNvbG91cj1jdXN0b21wYWxldHRlWzNdKSsNCiAgICBnZW9tX2RlbnNpdHkoZGF0YSA9IHJlbGF0aW9uc2hpcHNTdGF0c1tyZWxhdGlvbnNoaXBzU3RhdHMkcmVsYXRpb25zaGlwNCA+IDAsXSwgbWFwcGluZz1hZXMoeCA9IERhdGUpLCBjb2xvdXI9Y3VzdG9tcGFsZXR0ZVs0XSkrDQogICAgZ2VvbV9kZW5zaXR5KGRhdGEgPSByZWxhdGlvbnNoaXBzU3RhdHNbcmVsYXRpb25zaGlwc1N0YXRzJHJlbGF0aW9uc2hpcDUgPiAwLF0sIG1hcHBpbmc9YWVzKHggPSBEYXRlKSwgY29sb3VyPWN1c3RvbXBhbGV0dGVbNV0pKw0KICAgIGdlb21fZGVuc2l0eShkYXRhID0gcmVsYXRpb25zaGlwc1N0YXRzW3JlbGF0aW9uc2hpcHNTdGF0cyRyZWxhdGlvbnNoaXA2ID4gMCxdLCBtYXBwaW5nPWFlcyh4ID0gRGF0ZSksIGNvbG91cj1jdXN0b21wYWxldHRlWzZdKSsNCiAgICBnZW9tX2RlbnNpdHkoZGF0YSA9IHJlbGF0aW9uc2hpcHNTdGF0c1tyZWxhdGlvbnNoaXBzU3RhdHMkcmVsYXRpb25zaGlwNyA+IDAsXSwgbWFwcGluZz1hZXMoeCA9IERhdGUpLCBjb2xvdXI9Y3VzdG9tcGFsZXR0ZVs3XSkrDQogICAgZ2VvbV9kZW5zaXR5KGRhdGEgPSByZWxhdGlvbnNoaXBzU3RhdHNbcmVsYXRpb25zaGlwc1N0YXRzJHJlbGF0aW9uc2hpcDggPiAwLF0sIG1hcHBpbmc9YWVzKHggPSBEYXRlKSwgY29sb3VyPWN1c3RvbXBhbGV0dGVbOF0pKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1zZWFzb25zKSsNCiAgICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3M9IjIgbW9udGhzIikrDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGN1c3RvbXBhbGV0dGUpICsNCiAgICB0aGVtZV9oYWxmX29wZW4oKSArDQogICAgYmFja2dyb3VuZF9ncmlkKCkgKw0KICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkNCg0KbXlsZWdlbmQgPC0gZ2V0X2xlZ2VuZChwbG90TGVnZW5kUmVsYXRpb25zaGlwcykNCg0KcGxvdF9ncmlkKHBsb3RSZWxhdGlvbnNoaXBzLCBteWxlZ2VuZCwNCiAgICAgICAgICByZWxfd2lkdGhzID0gYygyLDEpLCBucm93PTEpDQojcGxvdFJlbGF0aW9uc2hpcHMNCg0KI3JtKHNlYXNvbnMsIHBsb3REYXRlc1JhdGluZ0RlbnNpdHkpDQpgYGANCg0KIyBBcmNoaXZlIFdhcm5pbmdzDQoNCk1ham9yaXR5IG9mIHdvcmtzIGFyZSB0YWdnZWQgd2l0aCAiTm8gQXJjaGl2ZSBXYXJuaW5ncyBBcHBseSIsIGZvbGxvd2VkIGJ5IGEgc2l6YWJsZSBmcmFjdGlvbiBvZiAiQ3JlYXRvciBDaG9zZSBOb3QgVG8gVXNlIEFyY2hpdmUgV2FybmluZ3MiLiBJdCBzZWVtcyB0byBiZSBhIGNvbW1vbiBtYXR0ZXIgb2YgY29uZnVzaW9uIGJldHdlZW4gdGhlIHVzYWdlIG9mIHRob3NlIHR3byB3YXJuaW5ncywgc28gaXQncyBwb3NzaWJsZSB0aGF0IGEgbG90IG9mICJDcmVhdG9yIENob3NlIE5vdCBUbyBVc2UgQXJjaGl2ZSBXYXJuaW5ncyIgYXJlIG1pc3RhZ2dlZCAiTm8gQXJjaGl2ZSBXYXJuaW5ncyBBcHBseSIuDQoNCmBgYHtyIHdhcm5pbmdCYXJzLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTZ9DQoNCm11bHRpcGxlV2FybmluZ1N1bW1hcnkgPC0gZGF0YS5mcmFtZShXYXJuaW5nID0gYygiTm8gQXJjaGl2ZSBXYXJuaW5ncyBBcHBseSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcmFwaGljIERlcGljdGlvbnMgT2YgVmlvbGVuY2UiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTWFqb3IgQ2hhcmFjdGVyIERlYXRoIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJhcGUvTm9uLUNvbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJVbmRlcmFnZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDcmVhdG9yIENob3NlIE5vdCBUbyBVc2UgQXJjaGl2ZSBXYXJuaW5ncyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVtYmVyLm9mLldvcmtzID0gYyhzdW0oZ3JlcGwoIk5vIEFyY2hpdmUgV2FybmluZ3MgQXBwbHkiLHdhcm5pbmdzKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgiR3JhcGhpYyBEZXBpY3Rpb25zIE9mIFZpb2xlbmNlIix3YXJuaW5ncykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oZ3JlcGwoIk1ham9yIENoYXJhY3RlciBEZWF0aCIsd2FybmluZ3MpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGdyZXBsKCJSYXBlL05vbi1Db24iLHdhcm5pbmdzKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgiVW5kZXJhZ2UiLHdhcm5pbmdzKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShncmVwbCgiQ3JlYXRvciBDaG9zZSBOb3QgVG8gVXNlIEFyY2hpdmUgV2FybmluZ3MiLHdhcm5pbmdzKSkpICkNCg0KbXVsdGlwbGVXYXJuaW5nU3VtbWFyeSRXYXJuaW5nIDwtIGZhY3RvcihtdWx0aXBsZVdhcm5pbmdTdW1tYXJ5JFdhcm5pbmcsIGxldmVscyA9IGMoIk5vIEFyY2hpdmUgV2FybmluZ3MgQXBwbHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyYXBoaWMgRGVwaWN0aW9ucyBPZiBWaW9sZW5jZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTWFqb3IgQ2hhcmFjdGVyIERlYXRoIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSYXBlL05vbi1Db24iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlVuZGVyYWdlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDcmVhdG9yIENob3NlIE5vdCBUbyBVc2UgQXJjaGl2ZSBXYXJuaW5ncyIpKQ0KDQpwbG90V2FybmluZ3MgPC0gcGxvdF9jb2wobXVsdGlwbGVXYXJuaW5nU3VtbWFyeSwgJ1dhcm5pbmcnLCAnTnVtYmVyLm9mLldvcmtzJywgJ3JpZ2h0JykNCnBsb3RXYXJuaW5ncw0KDQpybShtdWx0aXBsZVdhcm5pbmdTdW1tYXJ5LCBwbG90V2FybmluZ3MpDQoNCmBgYA0KDQojIE11bHRpcGxlIEZhbmRvbXMNCg0KYGBge3IgbXVsdGlwbGVGYW5kb21zLCBtZXNzYWdlID0gRkFMU0V9DQoNCm11bHRpRmFuZG9tcyA8LSBmYW5kb21bY2F0ZWdvcnkgPT0gIk11bHRpIl0NCg0KbXVsdGlGYW5kb21zQWxsIDwtIGZhbmRvbVtncmVwbCgnTXVsdGknLCBjYXRlZ29yeSldDQoNCnNldmVyYWxGYW5kb21zIDwtIGZhbmRvbVt1bmxpc3QobGFwcGx5KGZhbmRvbSwgbGVuZ3RoKSkgPiAxXQ0KDQpjcm9zc292ZXJzIDwtIGZhbmRvbVtncmVwKCdjcm9zc292ZXInLGZyZWVmb3JtLCBpZ25vcmUuY2FzZT1UUlVFKV0NCg0KYGBgDQoNClRvdGFsIG51bWJlciBvZiB3b3JrcyB0YWdnZWQgb25seSBhcyAnTXVsdGknIGlzIGByIGxlbmd0aChtdWx0aUZhbmRvbXMpYCwgYnV0IG9ubHkgYHIgc3VtKGxhcHBseShtdWx0aUZhbmRvbXMsIGxlbmd0aCkgPiAxKWAgYXJlIHRhZ2dlZCB3aXRoIG1vcmUgdGhhbiBvbmUgZmFuZG9tLiBBbW9uZyB0aGVzZSwgbWVkaWFuIG51bWJlciBvZiBmYW5kb21zIHRhZ2dlZCBpcyBgciBzdW1tYXJ5KCB1bmxpc3QobGFwcGx5KG11bHRpRmFuZG9tc1tsYXBwbHkobXVsdGlGYW5kb21zLCBsZW5ndGgpID4gMV0sIGxlbmd0aCkpIClbM11gLCBhbmQgYXQgbGVhc3Qgb25lIHdvcmsgaXMgdGFnZ2VkIHdpdGggYHIgc3VtbWFyeSggdW5saXN0KGxhcHBseShtdWx0aUZhbmRvbXNbbGFwcGx5KG11bHRpRmFuZG9tcywgbGVuZ3RoKSA+IDFdLCBsZW5ndGgpKSApWzZdYC4NCg0KTnVtYmVyIG9mIHdvcmtzIHRhZ2dlZCB3aXRoICdNdWx0aScgYW5kL29yIG90aGVyIGNhdGVnb3JpZXMgaXMgYHIgbGVuZ3RoKG11bHRpRmFuZG9tc0FsbClgLCBidXQgb25seSBgciBzdW0obGFwcGx5KG11bHRpRmFuZG9tc0FsbCwgbGVuZ3RoKSA+IDEpYCBhcmUgdGFnZ2VkIHdpdGggbW9yZSB0aGFuIG9uZSBmYW5kb20uIEFtb25nIHRoZXNlLCBtZWRpYW4gbnVtYmVyIG9mIGZhbmRvbXMgdGFnZ2VkIGlzIGByIHN1bW1hcnkoIHVubGlzdChsYXBwbHkobXVsdGlGYW5kb21zQWxsW2xhcHBseShtdWx0aUZhbmRvbXNBbGwsIGxlbmd0aCkgPiAxXSwgbGVuZ3RoKSkgKVszXWAsIGFuZCBhdCBsZWFzdCBvbmUgd29yayBpcyB0YWdnZWQgd2l0aCBgciBzdW1tYXJ5KCB1bmxpc3QobGFwcGx5KG11bHRpRmFuZG9tc0FsbFtsYXBwbHkobXVsdGlGYW5kb21zQWxsLCBsZW5ndGgpID4gMV0sIGxlbmd0aCkpIClbNl1gLg0KDQpOdW1iZXIgb2Ygd29ya3MgdGFnZ2VkIHdpdGggbW9yZSB0aGFuIDEgZmFuZG9tIGlzIGByIGxlbmd0aChzZXZlcmFsRmFuZG9tcylgLCBob3dldmVyIGluIHNvbWUgY2FzZXMgZmFuZG9tIHRhZ3MgdXNlZCBieSB0aGUgYXV0aG9yIGFyZSBzeW5vbnltb3VzIHdpdGggYHIgdGFnVmFsdWVgLCBmb3IgZXhhbXBsZSAiVGhlIERyYWdvbiBQcmluY2UiLCAicmF5bGx1bSAtIEZhbmRvbSIsICJyYXlsYSB4IGNhbGx1bSIsICJURFAgLSBGYW5kb20iLCAiY2FsbHVtIHggcmF5bGEiLg0KDQpOdW1iZXIgb2Ygd29ya3MgZXhwbGljaXRseSB0YWdnZWQgYXMgJ2Nyb3Nzb3ZlcicgaXMgbG93ZXI6IGByIGxlbmd0aChjcm9zc292ZXJzKWAuIE91dCBvZiBtdWx0aXBsZSBmYW5kb20gdGFnIHdvcmtzIGEgc2lnbmlmaWNhbnQgYW1vdW50IGFyZSB0YWdnZWQgd2l0aCAyOiBgciBsZW5ndGgoZmFuZG9tW3VubGlzdChsYXBwbHkoZmFuZG9tLCBsZW5ndGgpKSA9PSAyXSlgLCB3aGljaCwgdXBvbiBpbnNwZWN0aW9uLCBkb24ndCBzZWVtIG92ZXJ3aGVsbWluZ2x5IHN5bm9ueW1vdXMsIHNvIHBlcmhhcHMgc29tZSBhdXRob3JzIHNpbXBseSBkb24ndCB0YWcgZm9yIGNyb3Nzb3ZlcnMuDQoNCiMgQXV0aG9ycyBieSBXb3Jrcw0KDQpUb3AgMzAgb2YgbW9zdCBwcm9saWZpYyBhdXRob3JzIGluIHRoZSB0YWcgYnkgdGhlIG51bWJlciBvZiBzdG9yaWVzIGFzIG9mIGRhdGEgY29sbGVjdGlvbiBkYXRlOg0KDQpgYGB7ciBhdXRob3JzV29ya3MsIG1lc3NhZ2UgPSBGQUxTRX0NCnRvcExpc3QgPC0gMzANCg0KQXV0aG9yVGFibGUgPC0gZGF0YS5mcmFtZSgnQXV0aG9yJyA9IG5hbWVzKHN1bW1hcnkoYXMuZmFjdG9yKHVubGlzdChhdXRob3IpKSlbMTp0b3BMaXN0XSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICdOdW1iZXIgb2YgU3RvcmllcycgPSBzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QoYXV0aG9yKSkpWzE6dG9wTGlzdF0pDQpyb3cubmFtZXMoQXV0aG9yVGFibGUpIDwtIGMoKQ0KDQprYWJsZShBdXRob3JUYWJsZSwNCiAgICAgIGNvbC5uYW1lcyA9IGMoJ0F1dGhvcicsICdOdW1iZXIgb2YgU3RvcmllcycpKQ0KDQpybShBdXRob3JUYWJsZSkNCmBgYA0KDQojIEF1dGhvcnMgYnkgV29yZHMNCg0KT25seSBgciBzdW0odW5saXN0KGxhcHBseShhdXRob3IsIGxlbmd0aCkpPjEpYCB3b3JrcyBoYXZlIG1vcmUgdGhhbiBvbmUgYXV0aG9yLiBJbiBjYXNlcyB3aGVyZSB3b3JrcyBoYWQgbW9yZSB0aGFuIG9uZSBhdXRob3IsIEkgYXNzdW1lZCB0aGF0IGVhY2ggb2YgdGhlbSBjb250cmlidXRlZCBhbiBlcXVhbCBhbW91bnRzIG9mIHdvcmRzLg0KDQpUb3AgMzAgb2YgbW9zdCBwcm9saWZpYyBhdXRob3JzIGluIHRoZSB0YWcgYnkgdGhlIG51bWJlciBvZiB3b3JkcyB3cml0dGVuIGFzIG9mIGRhdGEgY29sbGVjdGlvbiBkYXRlOg0KDQpgYGB7ciBhdXRob3JzV29yZHMsIG1lc3NhZ2UgPSBGQUxTRX0NCg0Kd29yZHNCeUF1dGhvciA8LSBjKCkNCg0KZm9yIChpIGluIDE6bGVuZ3RoKHdvcmRzKSl7DQogIGlmIChsZW5ndGgoYXV0aG9yW1tpXV0pID4gMSkgew0KICAgIHdvcmRzQnlBdXRob3IgPC0gYyh3b3Jkc0J5QXV0aG9yLCByZXAod29yZHNbW2ldXS9sZW5ndGgoYXV0aG9yW1s1XV0pLCBsZW5ndGgoYXV0aG9yW1tpXV0pICkgKQ0KICB9IGVsc2Ugew0KICAgIHdvcmRzQnlBdXRob3IgPC0gYyh3b3Jkc0J5QXV0aG9yLCB3b3Jkc1tbaV1dKQ0KICB9DQp9DQoNCkF1dGhvcldvcmRzVGFibGUgPC0gZGF0YS5mcmFtZSgnQXV0aG9yJyA9IGFzLmZhY3Rvcih1bmxpc3QoYXV0aG9yKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1dvcmRzJyA9IHdvcmRzQnlBdXRob3IpDQoNCkF1dGhvcldvcmRzU3VtbWFyeSA8LSBkZHBseShBdXRob3JXb3Jkc1RhYmxlLCAuKEF1dGhvciksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcml6ZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgVG90YWwuV29yZHMgPSBzdW0oV29yZHMpKQ0KQXV0aG9yV29yZHNTdW1tYXJ5IDwtIEF1dGhvcldvcmRzU3VtbWFyeVtvcmRlcihBdXRob3JXb3Jkc1N1bW1hcnkkVG90YWwuV29yZHMsIGRlY3JlYXNpbmcgPSBUUlVFKSxdDQpyb3cubmFtZXMoQXV0aG9yV29yZHNTdW1tYXJ5KSA8LSBjKCkNCg0KdG9wTGlzdCA8LSAzMA0KDQprYWJsZShBdXRob3JXb3Jkc1N1bW1hcnlbMTp0b3BMaXN0LF0sDQogICAgICBjb2wubmFtZXMgPSBjKCdBdXRob3InLCAnVG90YWwgV29yZHMnKSkNCg0Kcm0od29yZHNCeUF1dGhvciwgaSwgQXV0aG9yV29yZHNUYWJsZSwgQXV0aG9yV29yZHNTdW1tYXJ5KQ0KYGBgDQoNCiMgQ2hhcmFjdGVycw0KDQpUb3AgMzAgb2YgdGhlIG1vc3QgcG9wdWxhciBjaGFyYWN0ZXJzOg0KDQpgYGB7ciBjaGFyYWN0ZXJzLCBtZXNzYWdlID0gRkFMU0V9DQp0b3BMaXN0IDwtIDMwDQpDaGFyYWN0ZXJUYWJsZTwtIGRhdGEuZnJhbWUoJ0NoYXJhY3RlcicgPSBuYW1lcyhzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QoY2hhcmFjdGVyKSkpWzE6dG9wTGlzdF0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAnTnVtYmVyIG9mIFN0b3JpZXMnID0gc3VtbWFyeShhcy5mYWN0b3IodW5saXN0KGNoYXJhY3RlcikpKVsxOnRvcExpc3RdKQ0Kcm93Lm5hbWVzKENoYXJhY3RlclRhYmxlKSA8LSBjKCkNCg0Ka2FibGUoQ2hhcmFjdGVyVGFibGUsDQogICAgICBjb2wubmFtZXMgPSBjKCdDaGFyYWN0ZXInLCAnTnVtYmVyIG9mIFN0b3JpZXMnKSkNCg0Kcm0oQ2hhcmFjdGVyVGFibGUpDQpgYGANCg0KIyBSZWxhdGlvbnNoaXBzDQoNClRvcCAzMCBvZiB0aGUgbW9zdCBwb3B1bGFyIHJlbGF0aW9uc2hpcHM6DQoNCkkgZG9uJ3QgaGF2ZSBhY2Nlc3MgdG8gQW8zJ3Mgc3lzdGVtIG9mIHN5bm9ueW1vdXMgdGFncywgc28gYnkgdmlydHVlIG9mIHRleHQgcHJvY2Vzc2luZyBzb21lIHJlbGF0aW9uc2hpcCB0YWdzIGhlcmUgYXJlIHJlcGVhdGVkLiANCg0KT3ZlcndoZWxtaW5nbHksICJDYWxsdW0vUmF5bGEgKFRoZSBEcmFnb24gUHJpbmNlKSIgaXMgdGhlIG1vc3QgcG9wdWxhciByZWxhdGlvbnNoaXAgaW4gVERQLiBUaGV5IGFyZSBmb2xsb3dlZCBieSAiRXRoYXJpL1J1bmFhbiAoVGhlIERyYWdvbiBQcmluY2UpIiwgd2hpY2ggaXMgbm90IGltbWVkaWF0ZWx5IG9idmlvdXMgZHVlIHRvIGNvbW1vbiB1c2Ugb2Ygc3lub255bW91cyB0YWdzIHN1Y2ggYXMgIlJ1bmFhbi9UaW5rZXIgfCBOZWNrbGFjZSBFbGYgKFRoZSBEcmFnb24gUHJpbmNlKSIgYW5kICJSdW5hYW4vRXRoYXJpIi4gVGhpcmQgbW9zdCBwb3B1bGFyIHJlbGF0aW9uc2hpcCBpcyAiQW1heWEvSmFuYWkgKFRoZSBEcmFnb24gUHJpbmNlKSIuDQoNCmBgYHtyIHJlbGF0aW9uc2hpcHMsIG1lc3NhZ2UgPSBGQUxTRX0NCnRvcExpc3QgPC0gMzANClJlbGF0aW9uc2hpcHNUYWJsZTwtIGRhdGEuZnJhbWUoJ1JlbGF0aW9uc2hpcCcgPSBuYW1lcyhzdW1tYXJ5KGFzLmZhY3Rvcih1bmxpc3QocmVsYXRpb25zaGlwcykpKVsxOnRvcExpc3RdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgJ051bWJlciBvZiBTdG9yaWVzJyA9IHN1bW1hcnkoYXMuZmFjdG9yKHVubGlzdChyZWxhdGlvbnNoaXBzKSkpWzE6dG9wTGlzdF0pDQpyb3cubmFtZXMoUmVsYXRpb25zaGlwc1RhYmxlKSA8LSBjKCkNCg0Ka2FibGUoUmVsYXRpb25zaGlwc1RhYmxlLA0KICAgICAgY29sLm5hbWVzID0gYygnUmVsYXRpb25zaGlwJywgJ051bWJlciBvZiBTdG9yaWVzJykpDQoNCnJtKFJlbGF0aW9uc2hpcHNUYWJsZSkNCmBgYA0KDQojIEZyZWVmb3JtIHRhZ3MNCg0KVG9wIDMwIG9mIHRoZSBtb3N0IHBvcHVsYXIgZnJlZWZvcm0gdGFncw0KDQpgYGB7ciBmcmVlZm9ybSwgbWVzc2FnZSA9IEZBTFNFfQ0KdG9wTGlzdCA8LSAzMA0KRnJlZWZvcm1UYWJsZTwtIGRhdGEuZnJhbWUoJ0ZyZWVmb3JtJyA9IG5hbWVzKHN1bW1hcnkoYXMuZmFjdG9yKHVubGlzdChmcmVlZm9ybSkpKVsxOnRvcExpc3RdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgJ051bWJlciBvZiBTdG9yaWVzJyA9IHN1bW1hcnkoYXMuZmFjdG9yKHVubGlzdChmcmVlZm9ybSkpKVsxOnRvcExpc3RdKQ0Kcm93Lm5hbWVzKEZyZWVmb3JtVGFibGUpIDwtIGMoKQ0KDQprYWJsZShGcmVlZm9ybVRhYmxlLA0KICAgICAgY29sLm5hbWVzID0gYygnRnJlZWZvcm0gVGFnJywgJ051bWJlciBvZiBTdG9yaWVzJykpDQoNCnJtKEZyZWVmb3JtVGFibGUpDQpgYGANCg0KIyBMYW5ndWFnZXMNCg0KVW5zdXJwcmlzaW5nbHksIG1vc3Qgd29ya3MgYXJlIHdyaXR0ZW4gaW4gRW5nbGlzaC4gQXBvbG9naWVzIGZvciBVKy4ga2FibGUgcGFja2FnZSBmb3Igd2hhdGV2ZXIgcmVhc29uIG11cmRlcnMgdW5pY29kZSBjaGFyYWN0ZXJzLiBUaGUgdHdvIGxhbmd1YWdlcyBpbiBxdWVzdGlvbiBhcmUgUnVzc2lhbiAo0KDRg9GB0YHQutC40LkpIGFuZCBDaGluZXNlICjkuK3mlocpLg0KDQpgYGB7ciBsYW5ndWFnZXMsIG1lc3NhZ2UgPSBGQUxTRX0NCiN0b3BMaXN0IDwtIDMwDQoNCmxhbmd1YWdlc0xpc3QgPC0gc3VtbWFyeShhcy5mYWN0b3IodW5saXN0KGxhbmd1YWdlKSkpDQoNCkxhbmd1YWdlVGFibGUgPC0gZGF0YS5mcmFtZSgnTGFuZ3VhZ2UnID0gbmFtZXMobGFuZ3VhZ2VzTGlzdCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgJ051bWJlciBvZiBTdG9yaWVzJyA9IGxhbmd1YWdlc0xpc3QgKQ0KTGFuZ3VhZ2VUYWJsZSA8LSBMYW5ndWFnZVRhYmxlW29yZGVyKExhbmd1YWdlVGFibGUkTnVtYmVyLm9mLlN0b3JpZXMsIGRlY3JlYXNpbmc9VFJVRSksXQ0Kcm93Lm5hbWVzKExhbmd1YWdlVGFibGUpIDwtIGMoKQ0KDQprYWJsZShMYW5ndWFnZVRhYmxlLA0KICAgICAgY29sLm5hbWVzID0gYygnTGFuZ3VhZ2UnLCAnTnVtYmVyIG9mIFN0b3JpZXMnKSkNCg0KI2xhbmd1YWdlc0xpc3QNCg0KI3JtKExhbmd1YWdlVGFibGUpDQpgYGANCg0KIyBPdGhlciBsaW5rcw0KDQpBbzMgZGF0YSBhbmFseXNpcyBmb3IgVGhlIERyYWdvbiBQcmluY2UgKENhcnRvb24pDQoNCltBbzMgZGF0YSBhbmFseXNpcyBmb3IgQXZhdGFyOiBMZWdlbmQgb2YgS29ycmFdKGh0dHBzOi8vZGFydGhhbGluZS5naXRodWIuaW8vQW8zU2VhcmNoQW5hbHlzaXMvZmFuZG9tcy9MT0svTE9LX3Byb2Nlc3Npbmdfbm90ZWJvb2submIuaHRtbCkNCg0KW0FvMyBkYXRhIGFuYWx5c2lzIGZvciBBdmF0YXI6IFRoZSBMYXN0IEFpcmJlbmRlcl0oaHR0cHM6Ly9kYXJ0aGFsaW5lLmdpdGh1Yi5pby9BbzNTZWFyY2hBbmFseXNpcy9mYW5kb21zL0FUTEEvQVRMQV9wcm9jZXNzaW5nX25vdGVib29rLm5iLmh0bWwpDQoNCltBbzMgZGF0YSBhbmFseXNpcyBmb3IgQmxhY2sgU2FpbHNdKGh0dHBzOi8vZGFydGhhbGluZS5naXRodWIuaW8vQW8zU2VhcmNoQW5hbHlzaXMvZmFuZG9tcy9CU2FpbHMvQlNhaWxzX3Byb2Nlc3Npbmdfbm90ZWJvb2submIuaHRtbCkNCg0KSWYgeW91IGVuam95ZWQgbXkgYW5hbHlzaXMsIHBsZWFzZSwgY29uc2lkZXIgW2J1eWluZyBtZSBhIGNvZmZlZV0oaHR0cHM6Ly9rby1maS5jb20vRDFEOFJJRzUpIG9yIHNvbWUgb3RoZXIgYmV2ZXJhZ2Uu