1. Data Acquisition
The relevant data were obtained from the wmf.mediawiki_history table, which stores the denormalized edit history of WMF’s wikis. The following HiveQL queries were run within the R environment from the stat1005 statbox server on October 15, 2018:
1.1 Total revisions: users vs. namespaces
The following HiveQL query, run by a system()
call from within R, collects the counts of all event_user_id
(user IDs) and page_namespace
(page namespace of the page where a particular revision took place) combinations across all users and revisions on wikidatawiki
. Bots and anonymous users are filtered out, as well as page redirects and revisions that were subsequently deleted. The resulting table presents the number of revisions made by any user on any page, grouped by page namespaces.
filename <- "WD_revisions.tsv"
connect <- '/usr/local/bin/beeline --silent --incremental -e'
args <- '"USE wmf;
SELECT COUNT(*), event_user_id, page_namespace FROM mediawiki_history
WHERE (
event_entity = \'revision\' AND
event_type = \'create\' AND
wiki_db = \'wikidatawiki\' AND
event_user_is_bot_by_name = FALSE AND
event_user_is_anonymous = FALSE AND
NOT ARRAY_CONTAINS(event_user_groups, \'bot\') AND
NOT ARRAY_CONTAINS(event_user_groups_historical, \'bot\') AND
event_user_id != 0 AND
page_is_redirect = FALSE AND
revision_is_deleted = FALSE AND
snapshot = \'2018-09\'
)
GROUP BY event_user_id, page_namespace;"'
out <- paste0("> /home/goransm/RScripts/Wikidata/", filename)
qCommand <- paste(connect, args, out)
system(qCommand, wait = T)
1.2 Total revisions: users vs. namespaces in September 2018.
The following HiveQL query is exactly the same as the previous one, except for the fact that it filters out all revision older then Septmeber 2018. The nature of the wmf.mediawiki_history
table is such that a current table snapshot takes some time to compute, so that the latest table snapshot that was available is 2018-09
. In other words, the October 2018 dataset was not yet ready when we’ve run these analyses.
filename <- "WD_revisions_September.tsv"
connect <- '/usr/local/bin/beeline --silent --incremental -e'
args <- '"USE wmf;
SELECT COUNT(*), event_user_id, page_namespace FROM mediawiki_history
WHERE (
event_entity = \'revision\' AND
event_type = \'create\' AND
wiki_db = \'wikidatawiki\' AND
event_user_is_bot_by_name = FALSE AND
event_user_is_anonymous = FALSE AND
NOT ARRAY_CONTAINS(event_user_groups, \'bot\') AND
NOT ARRAY_CONTAINS(event_user_groups_historical, \'bot\') AND
event_user_id != 0 AND
page_is_redirect = FALSE AND
revision_is_deleted = FALSE AND
snapshot = \'2018-09\' AND
event_timestamp > \'2018-09-01 00:00:00.0\'
)
GROUP BY event_user_id, page_namespace;"'
out <- paste0("> /home/goransm/RScripts/Wikidata/WD_edits/", filename)
qCommand <- paste(connect, args, out)
system(qCommand, wait = T)
1.3 User registrations
The following HiveQL query collects all user registrations on wikidatawiki
. The documentation of the wmf.mediawiki_history
table is not clear on the following point: what is the exact meaning of the event_user_
type of fields in the context where event_entity = user
? In other words, it is uncertain whether filtering out bots and anonymous users should be done by refering to event_user_is_
type of fields or to user_is_
type of fileds. In order to prevent any inconsistencies in the data, both types of fields where used to filter out bots and anonymoys users. In spite of this precaution, the later results will demonstrate that it is highly likely that at least some bots have “survived” these filtering procedures. They were filtered out my statistical methods, as it will be demonstrated. The resulting table contains the user IDs and the timestamps of their registration with wikidatawiki
. Note. In spite of the fact that the Wikidata project started on October 30, 2012, many registration timestamps are as old as 2004. The only hypothesis in respect to this fact is that the older registration timestamps exist as a consequence of unifyed logins of those users who were already registered at other wikies. As we will see these user accounts were filtered out from some of the analyses.
filename <- "WD_users.tsv"
connect <- '/usr/local/bin/beeline --silent --incremental -e'
args <- '"USE wmf;
SELECT user_id, user_creation_timestamp FROM mediawiki_history
WHERE (
event_entity = \'user\' AND
event_type = \'create\' AND
wiki_db = \'wikidatawiki\' AND
event_user_is_bot_by_name = FALSE AND
event_user_is_anonymous = FALSE AND
user_is_bot_by_name = FALSE AND
user_is_anonymous = FALSE AND
NOT ARRAY_CONTAINS(event_user_groups, \'bot\') AND
NOT ARRAY_CONTAINS(event_user_groups_historical, \'bot\') AND
NOT ARRAY_CONTAINS(user_groups, \'bot\') AND
NOT ARRAY_CONTAINS(user_groups_historical, \'bot\') AND
snapshot = \'2018-09\'
);"'
out <- paste0("> /home/goransm/RScripts/Wikidata/WD_edits/", filename)
qCommand <- paste(connect, args, out)
system(qCommand, wait = T)
2. Data Wrangling
The following procedures load the datasets locally, impose consistent column names to obtain the desired matches, introduces the namespace names (e.g. “Main (Items)”, “Main (Items talk)”, and similar) beyond namespace codes, and creates Posix
timestamps for user registrations. Finally, the revision data are filtered out additionaly to keep only the user accounts that are found in the user registration data.
# - locally: revisions vs namespaces
library(data.table)
data.table 1.10.4
The fastest way to learn (by data.table authors): https://www.datacamp.com/courses/data-analysis-the-data-table-way
Documentation: ?data.table, example(data.table) and browseVignettes("data.table")
Release notes, videos and slides: http://r-datatable.com
setwd('/home/goransm/Work/___DataKolektiv/Projects/WikimediaDEU/_WMDE_Projects/Wikidata/WD_edits/')
wdRevisions <- fread('WD_revisions.tsv', sep = "\t")
colnames(wdRevisions) <- c('revisions', 'user', 'wd_namespace')
# - locally: revisions vs namespaces, September only
wdRevisionsSep <- fread('WD_revisions_September.tsv', sep = "\t")
colnames(wdRevisionsSep) <- c('revisions', 'user', 'wd_namespace')
# - locally: user registrations
wdUsers <- fread('WD_users.tsv', sep = "\t")
Read 43.4% of 2998702 rows
Read 49.7% of 2998702 rows
Read 65.4% of 2998702 rows
Read 83.0% of 2998702 rows
Read 2998702 rows and 2 (of 2) columns from 0.083 GB file in 00:00:06
colnames(wdUsers) <- c('user', 'creation_timestamp')
### --- Wrangling
library(dplyr)
Attaching package: ‘dplyr’
The following objects are masked from ‘package:data.table’:
between, first, last
The following objects are masked from ‘package:stats’:
filter, lag
The following objects are masked from ‘package:base’:
intersect, setdiff, setequal, union
# - WD namespace coding table;
# - add namespace name to wdRevisions, wdRevisionsSep
# - https://www.wikidata.org/wiki/Help:Namespaces
namespaces <- read.csv('namespace_coding_scheme.csv',
header = T,
check.names = F,
stringsAsFactors = F)
wdRevisions <- left_join(wdRevisions, namespaces,
by = c("wd_namespace" = "namespaceCode"))
wdRevisionsSep <- left_join(wdRevisionsSep, namespaces,
by = c("wd_namespace" = "namespaceCode"))
# - creation_timestamp in wdUsers to Posix
library(lubridate)
Attaching package: ‘lubridate’
The following objects are masked from ‘package:data.table’:
hour, isoweek, mday, minute, month, quarter, second, wday, week, yday, year
The following object is masked from ‘package:base’:
date
wdUsers$creation_timestamp <- sapply(wdUsers$creation_timestamp,
function(x) {
strsplit(
strsplit(x, split = ".", fixed = T)[[1]][1],
split = " ", fixed = T)[[1]][1]
})
wdUsers$creation_timestamp <- ymd(wdUsers$creation_timestamp)
wdUsers$YM <- sapply(as.character(wdUsers$creation_timestamp),
function(x) {
paste(strsplit(x, split = "-", fixed = T)[[1]][1:2], collapse = "-")
})
3. Data Analyses
### --- Analytics
library(ggplot2)
library(ggrepel)
library(stringr)
Q1. How is our user base composed in terms of edit count? (e.g. shown by an histogram, y= count, x=bins of edit count ranges)
First we group all user revisions and user IDs irrespective of the page namespace and sum up all revisions per user:
plotFrame <- wdRevisions %>%
select(user, revisions) %>%
group_by(user) %>%
summarise(revisions = sum(revisions)) %>%
arrange(desc(revisions))
Exploratory analysis has indicated that outlier removal is necessary in this case. Namely, account with millions of edits were discovered, indicating (as already observed) the possibility that some bots were not sucessfully removed while filtering out by respective database flags. It is also possible that we are observing the outcomes of automated imports to Wikidata that were not flagged as bots here.
# - APPROACH: For a given continuous variable, outliers are those observations
# - that lie outside 1.5 * IQR, where IQR, the ‘Inter Quartile Range’
# - is the difference between 75th and 25th quartiles.
outs <- which(
plotFrame$revisions %in% boxplot.stats(plotFrame$revisions)$out)
plotFrame <- plotFrame[-outs, ]
Q1.1 Checking for power-law behavior: log(Number of users) vs. log (Number of revisions) plot
We are doing this not to check for power-laws really and for some essential reasons, but rather because the skewness of the distribution prevents us from visualizing these data in an informative way.
# - Power-Law
userRevisions <- as.data.frame(table(plotFrame$revisions),
stringsAsFactors = F)
colnames(userRevisions) <- c('Revisions', 'Num.Users')
userRevisions$Revisions <- as.numeric(userRevisions$Revisions)
userRevisions <- arrange(userRevisions, desc(Revisions))
# - log-log
ggplot(data = userRevisions,
aes(x = log10(Revisions),
y = log10(`Num.Users`),
label = Revisions)) +
geom_point(size = .1) + geom_smooth(method = lm, size = .2) +
ggtitle("Revisions per user") +
geom_text_repel(size = 2.5) +
theme_minimal() +
theme(axis.text.x = element_blank()) +
theme(axis.text.y = element_text(size = 10, hjust = 1)) +
theme(axis.title.x = element_text(size = 10)) +
theme(axis.title.y = element_text(size = 10)) +
theme(plot.title = element_text(hjust = 0.5, size = 11)) +
ylab("log(Num.Users)")
Q1.2 Checking for power-law behavior: Number of users vs. Number of revisions plot
In absolute values:
# - absolute values
ggplot(data = userRevisions,
aes(x = Revisions,
y = `Num.Users`,
label = Revisions)) +
geom_point(size = .5) + geom_path(size = .2) +
ggtitle("Revisions per user") +
geom_text_repel(size = 2.5) +
theme_minimal() +
theme(axis.text.x = element_text(size = 10)) +
theme(axis.text.y = element_text(size = 10, hjust = 1)) +
theme(axis.title.x = element_text(size = 10)) +
theme(axis.title.y = element_text(size = 10)) +
theme(plot.title = element_text(hjust = 0.5, size = 11)) +
ylab("Num.Users")
Q1.3 Distribution of revisions per user: a Histogram
# - log histogram
ggplot(data = plotFrame,
aes(revisions)) +
geom_histogram(bins = 50, fill = "deepskyblue", color = "white") +
ggtitle("Distribution of revisions per user") +
ylab("Num.Users") + xlab("Revisions") +
theme_minimal() +
theme(axis.text.x = element_text(size = 10, hjust = 1)) +
theme(axis.text.y = element_text(size = 10, hjust = 1)) +
theme(axis.title.x = element_text(size = 10)) +
theme(axis.title.y = element_text(size = 10)) +
theme(plot.title = element_text(hjust = 0.5, size = 11))
Q2. How were our user base composed in terms of recent edits? How many account did, in the last month (or so) edited n times (e.g. shown by an histogram, y= count, x=bins of edit count ranges for a month)?
The procedures are the same as in Q2
, except for now we focus on September 2018 data only.
plotFrame <- wdRevisionsSep %>%
select(user, revisions) %>%
group_by(user) %>%
summarise(revisions = sum(revisions)) %>%
arrange(desc(revisions))
# - Outlier detection and removal
# - APPROACH: For a given continuous variable, outliers are those observations
# - that lie outside 1.5 * IQR, where IQR, the ‘Inter Quartile Range’
# - is the difference between 75th and 25th quartiles.
outs <- which(
plotFrame$revisions %in% boxplot.stats(plotFrame$revisions)$out)
plotFrame <- plotFrame[-outs, ]
Q2.1 Checking for power-law behavior: log(Number of users) vs. log (Number of revisions) plot
userRevisions <- as.data.frame(table(plotFrame$revisions),
stringsAsFactors = F)
colnames(userRevisions) <- c('Revisions', 'Num.Users')
userRevisions$Revisions <- as.numeric(userRevisions$Revisions)
userRevisions <- arrange(userRevisions, desc(Revisions))
ggplot(data = userRevisions,
aes(x = log10(Revisions),
y = log10(`Num.Users`),
label = Revisions)) +
geom_point(size = .1) + geom_smooth(method = lm, size = .2) +
ggtitle("Revisions per user: September 2018") +
geom_text_repel(size = 2.5) +
theme_minimal() +
theme(axis.text.x = element_blank()) +
theme(axis.text.y = element_text(size = 10, hjust = 1)) +
theme(axis.title.x = element_text(size = 10)) +
theme(axis.title.y = element_text(size = 10)) +
theme(plot.title = element_text(hjust = 0.5, size = 11)) +
ylab("log(Num.Users)")
Q2.2 Checking for power-law behavior: Number of users vs. Number of revisions plot
ggplot(data = userRevisions,
aes(x = Revisions,
y = `Num.Users`,
label = Revisions)) +
geom_point(size = .5) + geom_path(size = .2) +
ggtitle("Revisions per user: September 2018") +
geom_text_repel(size = 2.5) +
theme_minimal() +
theme(axis.text.x = element_text(size = 10)) +
theme(axis.text.y = element_text(size = 10, hjust = 1)) +
theme(axis.title.x = element_text(size = 10)) +
theme(axis.title.y = element_text(size = 10)) +
theme(plot.title = element_text(hjust = 0.5, size = 11)) +
ylab("Num.Users")
Q2.3 Distribution of revisions per user in September 2018: a Histogram
ggplot(data = plotFrame,
aes(revisions)) +
geom_histogram(bins = 50, fill = "deepskyblue", color = "white") +
ggtitle("Distribution of revisions per user: September 2018") +
ylab("Num.Users") + xlab("Revisions") +
theme_minimal() +
theme(axis.text.x = element_text(size = 10, hjust = 1)) +
theme(axis.text.y = element_text(size = 10, hjust = 1)) +
theme(axis.title.x = element_text(size = 10)) +
theme(axis.title.y = element_text(size = 10)) +
theme(plot.title = element_text(hjust = 0.5, size = 11))
Q3. How many of those users do participate in discussions? (mosaic plot, y = bins of edit count, x = bins of discussion participation)
I was not entirely sure if I have understood correctly the intention with the mosaic plot here when I’ve started working on this analysis. However, the following approach should be illustrative in respect to Q3
.
In the first step, we separate all talk (i.e. discussion) namespaces from the namespaces where “main” revisions take place.
wdRevisionsSep$talk <- ifelse(grepl("talk", wdRevisionsSep$namespace, fixed = T), "discussion", "edit")
plotFrame <- wdRevisionsSep %>%
select(user, revisions, talk) %>%
group_by(user, talk) %>%
summarise(revisions = sum(revisions))
Removing outliers, as in previous analyses:
# - APPROACH: For a given continuous variable, outliers are those observations
# - that lie outside 1.5 * IQR, where IQR, the ‘Inter Quartile Range’
# - is the difference between 75th and 25th quartiles.
outs <- which(
plotFrame$revisions %in% boxplot.stats(plotFrame$revisions)$out)
plotFrame <- plotFrame[-outs, ]
plotFrame <- arrange(plotFrame, user)
Now we cross-tabulate the number of revisions made on discussion and main namespaces for all users, and visualize the R table
object to obtain a mosaic plot:
# - number of users that have both edited and discussed
numEditTalkUsers <- sum(duplicated(plotFrame$user))
wEditTalkUsers <- which(duplicated(plotFrame$user))
wEditTalkUsers <- plotFrame$user[wEditTalkUsers]
plotFrame <- plotFrame[plotFrame$user %in% wEditTalkUsers, ]
pFrame <- data.frame(edits = plotFrame$revisions[plotFrame$talk == "edit"],
discussions = plotFrame$revisions[plotFrame$talk == "discussion"],
stringsAsFactors = F)
pFrame <- arrange(pFrame, edits)
# A "mosaic" plot
plot(table(pFrame$edits, pFrame$discussions),
xlab = "Edits", ylab = "Discussions",
main = "Edits vs. Discussions, Sep 2018",
color = "darkorange")
Another, more conservative and potentialy useful approach to visualize the same cross-tabulated data:
ggplot(data = pFrame,
aes(x = edits, y = discussions, label = discussions)) +
geom_point(size = 1.5, color = "blue") +
geom_point(size = 1, color = "white") +
ggtitle("Distribution of revisions per user: September 2018") +
ylab("Discussions") + xlab("Edits") +
theme_minimal() +
theme(axis.text.x = element_text(size = 10, hjust = 1)) +
theme(axis.text.y = element_text(size = 10, hjust = 1)) +
theme(axis.title.x = element_text(size = 10)) +
theme(axis.title.y = element_text(size = 10)) +
theme(plot.title = element_text(hjust = 0.5, size = 11))
Q3.1 Alternative approach
This is the number of users following the binning of the distributions of the number of edits (i.e. revisions made in any of the main namespaces) and the number of discussions (i.e. revisions made on talk pages):
pFrame$editIntervals <- cut(pFrame$edits,
breaks = 4)
pFrame$editIntervals <- sapply(pFrame$editIntervals, function(x) {
edges <- strsplit(as.character(x), split = ",")[[1]]
edges[1] <- floor(as.numeric(str_extract(edges[1], "([[:digit:]]|\\.)+"))) + 1
edges[2] <- floor(as.numeric(str_extract(edges[2], "([[:digit:]]|\\.)+")))
return(paste0("(", edges[1]," - ", edges[2], ")"))
})
pFrame$discussionIntervals <- cut(pFrame$discussions,
breaks = 4)
pFrame$discussionIntervals <- sapply(pFrame$discussionIntervals, function(x) {
edges <- strsplit(as.character(x), split = ",")[[1]]
edges[1] <- floor(as.numeric(str_extract(edges[1], "([[:digit:]]|\\.)+"))) + 1
edges[2] <- floor(as.numeric(str_extract(edges[2], "([[:digit:]]|\\.)+")))
return(paste0("(", edges[1]," - ", edges[2], ")"))
})
editIntervalsLevels <- sapply(unique(pFrame$editIntervals), function(x) {
lower <- str_extract(x, '[[:digit:]]+')
})
editIntervalsLevels <- names(editIntervalsLevels)[order(as.numeric(editIntervalsLevels))]
discussionIntervalsLevels <- sapply(unique(pFrame$discussionIntervals), function(x) {
lower <- str_extract(x, '[[:digit:]]+')
})
discussionIntervalsLevels <- names(discussionIntervalsLevels)[order(as.numeric(discussionIntervalsLevels))]
pFrame$editIntervals <- factor(pFrame$editIntervals,
levels = editIntervalsLevels)
pFrame$discussionIntervals <- factor(pFrame$discussionIntervals,
levels = discussionIntervalsLevels)
pFrame %>%
dplyr::select(editIntervals, discussionIntervals) %>%
dplyr::group_by(editIntervals, discussionIntervals) %>%
summarise(Count = n()) %>%
ggplot(aes(x = editIntervals,
y = discussionIntervals,
label = Count)) +
geom_point(aes(size = Count), color = "cadetblue4", shape = 19) +
geom_text(size = 3, nudge_x = .15) +
xlab("Edits") + ylab("Discussions") +
theme_bw() +
theme(panel.background = element_rect(fill = "lightblue"))
NOTE. This is (as requested on Phab) only for recent revisions (September 2018). We can have this produced from all available revisions of course.
Q4. How many new users do we have each month?
Note. My decision here was to encompass only those users who have registered later than or in November 2012 (since Wikidata started in October 2018).
Q4.1 User registrations per month
library(tidyr)
usersFrame <- wdUsers %>%
filter(YM >= "2012-11") %>%
arrange(YM)
usersRegistered <- usersFrame %>%
group_by(YM) %>%
summarise(registrations = n())
ggplot(data = usersRegistered,
aes(x = YM, y = registrations, label = registrations)) +
geom_path(size = .25, group = 1, color = "blue") +
geom_point(size = 1.5, color = "blue") +
geom_point(size = 1, color = "white") +
geom_text_repel(size = 2.5) +
ggtitle("Registrations on Wikidata since: November 2012") +
ylab("Year-Month") + xlab("Registrations") +
theme_minimal() +
theme(axis.text.x = element_text(size = 6, hjust = 1, angle = 90)) +
theme(axis.text.y = element_text(size = 10, hjust = 1)) +
theme(axis.title.x = element_text(size = 10)) +
theme(axis.title.y = element_text(size = 10)) +
theme(plot.title = element_text(hjust = 0.5, size = 11))
Q4.2 User registrations and revisions vs. participation in discussions
Note. Logarithmic scaling was used; edit
stands for revisions on the “main” namespaces (note: not only Items, but all namespaces that are not “talk” namespaces), discussion
for revisions on “talk” namespaces. Each point represents a count in the particular month since November 2012 in which the users whose data are taken into consideration have registered.
wdRevisions$talk <- ifelse(grepl("talk", wdRevisions$namespace, fixed = T), "discussion", "edit")
userRevs <- wdRevisions %>%
select(user, talk, revisions) %>%
group_by(user, talk) %>%
summarise(revisions = sum(revisions)) %>%
spread(key = talk,
value = revisions,
fill = 0)
usersFrame <- left_join(usersFrame, userRevs,
by = 'user')
usersFrame <- usersFrame[complete.cases(usersFrame), ]
uFrame <- usersFrame %>%
select(YM, discussion, edit) %>%
group_by(YM) %>%
summarise(edit = sum(edit), discussion = sum(discussion))
uFrame <- uFrame %>%
gather(key = "Activity",
value = "Revisions",
c('edit', 'discussion'))
ggplot(data = uFrame,
aes(x = YM, y = log10(Revisions),
color = Activity,
group = Activity,
label = Revisions)) +
geom_text_repel(size = 2, color = "black", segment.size = .2) +
geom_point(size = 1.5) + geom_path(size = .25) +
geom_point(size = 1, color = "white") +
ggtitle("log(Revisions) per Activity: since November 2012") +
xlab("Year-Month") + ylab("log10(Revisions)") +
theme_minimal() +
theme(axis.text.x = element_text(size = 6, hjust = 1, angle = 90)) +
theme(axis.text.y = element_text(size = 10, hjust = 1)) +
theme(axis.title.x = element_text(size = 10)) +
theme(axis.title.y = element_text(size = 10)) +
theme(plot.title = element_text(hjust = 0.5, size = 11)) +
theme(legend.position = "top")
Q4.3 The relationship between revisions and participations in discussions
By eye-balling only: there seem to be an approximate and weak linear relationship between the number of revisions made on the “main” and “talk” namespaces (once again: by “main” namespaces we mean not only Items, but all namespaces that are not “talk” namespaces). Again, each point represents a particular month since November 2012 in which the users whose data are taken into consideration have registered, except for the data are now represented on a scattergram.
pFrame <- uFrame %>%
spread(key = Activity,
value = Revisions)
ggplot(data = pFrame,
aes(x = edit, y = discussion)) +
geom_point(size = 1.5) +
geom_point(size = 1, color = "white") +
geom_smooth(method = lm, size = .25) +
ggtitle("Edits vs. Discussions: since November 2012") +
ylab("Discussions") + xlab("Edits") +
theme_minimal() +
theme(axis.text.x = element_text(size = 8, hjust = 1, angle = 90)) +
theme(axis.text.y = element_text(size = 10, hjust = 1)) +
theme(axis.title.x = element_text(size = 10)) +
theme(axis.title.y = element_text(size = 10)) +
theme(plot.title = element_text(hjust = 0.5, size = 11))
model <- lm(pFrame$discussion ~ pFrame$edit)
summary(model)
Call:
lm(formula = pFrame$discussion ~ pFrame$edit)
Residuals:
Min 1Q Median 3Q Max
-8395.3 -381.8 50.5 200.9 11101.8
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -3.594e+01 3.081e+02 -0.117 0.907
pFrame$edit 1.003e-03 6.718e-05 14.935 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 2300 on 69 degrees of freedom
Multiple R-squared: 0.7637, Adjusted R-squared: 0.7603
F-statistic: 223.1 on 1 and 69 DF, p-value: < 2.2e-16
We can explain some 76%
of variance in revisions made on “talk” namespaces from revisions made on the “main” namespaces. However, the assumptions of the linear model were not tested (not to mention that we are dealing with count data here, so Poisson would be more appropriate), neither any outliers (and there are some obvious outliers in this analysis, c.f. the scattergram) were removed, so this result should be taken with a grain of salt (at least).
LS0tCnRpdGxlOiAnQmFzaWMgRGF0YSBPbiBXaWtpZGF0YSBVc2UgUmVwb3J0JwphdXRob3I6ICJHb3JhbiBTLiBNaWxvdmFub3ZpYywgRGF0YSBTY2llbnRpc3QsIFdNREUiCmRhdGU6ICJPY3RvYmVyIDE1LCAyMDE4IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgdGhlbWU6IHNpbXBsZXgKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDMKLS0tCgoqKkZlZWRiYWNrKiogc2hvdWxkIGJlIHNlbmQgdG8gYGdvcmFuLm1pbG92YW5vdmljX2V4dEB3aWtpbWVkaWEuZGVgLiAKCmBgYHtyfQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gMTQsIGZpZy5oZWlnaHQgPSAxMCkgCmBgYAoKIyAwLiBUYXNrIERlc2NyaXB0aW9uCgpUaGUgdGFzayBpcyBkZXNjcmliZWQgaW4gdGhlIHJlc3BlY3RpdmUgW1BoYWJyaWNhdG9yIHRpY2tldF0oaHR0cHM6Ly9waGFicmljYXRvci53aWtpbWVkaWEub3JnL1QyMDYyMTQpIGJ5IEphbiBEaXR0cmljaDoKCioqVXNlciBTdG9yeSoqIDo6IEFzIGEgdXNlciByZXNlYXJjaGVyLCBJIHdhbnQgdG8ga25vdyBob3cgb3VyIHVzZXIgYmFzZSBpcyBjb21wb3NlZCBzbyBJIGNhbiBldmFsdWF0ZSBpZiBzdXJ2ZXkgYW5kIGludGVydmlldyBkYXRhIGhhcyBhIGJpYXMgY29tcGFyZWQgdG8gaXQgYW5kIHRvIHNlZSBiYXNpYyBwYXR0ZXJucyB0aGF0IEkgbWlnaHQgd2FudCB0byBmdXJ0aGVyIGV4cGxvcmUuCgpJbmZvcm1hdGlvbiBuZWVkczoKCkkgaW1hZ2luZSB0aGUgZm9sbG93aW5nIGRhdGEgdG8gYmUgdXNlZnVsIChhbGwgY2FuIGJlIGFnZ3JlZ2F0ZXMpOiAKSSBpbWFnaW5lIHRoaXMgc2hvdWxkIG5vdCBiZSBhIGdyYXBoYW5hIGJvYXJkIGJ1dCBhIHJlcG9ydCAoaWRlYWxseTogUk1hcmtkb3duIG9yIEp1cHl0ZXIgTm90ZWJvb2spCgotIEhvdyBpcyBvdXIgdXNlciBiYXNlIGNvbXBvc2VkIGluIHRlcm1zIG9mIGVkaXQgY291bnQ/IChlLmcuIHNob3duIGJ5IGFuIGhpc3RvZ3JhbSwgeT0gY291bnQsIHg9YmlucyBvZiBlZGl0IGNvdW50IHJhbmdlcykKLSBIb3cgd2VyZSBvdXIgdXNlciBiYXNlIGNvbXBvc2VkIGluIHRlcm1zIG9mIHJlY2VudCBlZGl0cz8gSG93IG1hbnkgYWNjb3VudCBkaWQsIGluIHRoZSBsYXN0IG1vbnRoIChvciBzbykgZWRpdGVkIG4gdGltZXMgKGUuZy4gc2hvd24gYnkgYW4gaGlzdG9ncmFtLCB5PSBjb3VudCwgeD1iaW5zIG9mIGVkaXQgY291bnQgcmFuZ2VzIGZvciBhIG1vbnRoKQotIEhvdyBtYW55IG9mIHRob3NlIHVzZXJzIGRvIHBhcnRpY2lwYXRlIGluIGRpc2N1c3Npb25zPyAobW9zYWljIHBsb3QsIHk9YmlucyBvZiBlZGl0IGNvdW50LCB4PSBiaW5zIG9mIGRpc2N1c3Npb24gcGFydGljaXBhdGlvbikKLSBIb3cgbWFueSBuZXcgdXNlcnMgZG8gd2UgaGF2ZSBlYWNoIG1vbnRoPwotLSBBbmQgb2YgdGhlbSwgaG93IG1hbnkgZG8gZWRpdCBob3cgbXVjaD8KLS0gQW5kIG9mIHRoZW0sIGhvdyBtYW55IGRvIHBhcnRpY2lwYXRlIGluIGRpc2N1c3Npb25zPwoKT3IgaW4gZ2VuZXJhbCwgaG93IGlzIHRoZSByZWxhdGlvbiBiZXR3ZWVuOgoKLSBFZGl0IGNvdW50IGFuZDogRGlzY3Vzc2lvbiBwYXJ0aWNpcGF0aW9uIChwb3NzaWJseSBzcGxpdCBieSBpdGVtIGFuZCBwcm9wZXJ0eSBkaXNjdXNzaW9ucyB2cy4gb3RoZXIgcHJvamVjdCBkaXNjdXNzaW9ucyksIGFkZGluZyB2YWx1ZXMsIGNvcnJlY3RpbmcgdmFsdWVzLCBhZGRpbmcgcmVmZXJlbmNlcywgYWRkaW5nIHF1YWxpZmllcnMKCiMgMS4gRGF0YSBBY3F1aXNpdGlvbgoKVGhlIHJlbGV2YW50IGRhdGEgd2VyZSBvYnRhaW5lZCBmcm9tIHRoZSBbd21mLm1lZGlhd2lraV9oaXN0b3J5XShodHRwczovL3dpa2l0ZWNoLndpa2ltZWRpYS5vcmcvd2lraS9BbmFseXRpY3MvRGF0YV9MYWtlL0VkaXRzL01lZGlhd2lraV9oaXN0b3J5KSB0YWJsZSwgd2hpY2ggc3RvcmVzIHRoZSBkZW5vcm1hbGl6ZWQgZWRpdCBoaXN0b3J5IG9mIFdNRidzIHdpa2lzLiBUaGUgZm9sbG93aW5nIEhpdmVRTCBxdWVyaWVzIHdlcmUgcnVuIHdpdGhpbiB0aGUgUiBlbnZpcm9ubWVudCBmcm9tIHRoZSBzdGF0MTAwNSBzdGF0Ym94IHNlcnZlciBvbiBPY3RvYmVyIDE1LCAyMDE4OgoKIyMgMS4xIFRvdGFsIHJldmlzaW9uczogdXNlcnMgdnMuIG5hbWVzcGFjZXMKClRoZSBmb2xsb3dpbmcgSGl2ZVFMIHF1ZXJ5LCBydW4gYnkgYSBgc3lzdGVtKClgIGNhbGwgZnJvbSB3aXRoaW4gUiwgY29sbGVjdHMgdGhlIGNvdW50cyBvZiBhbGwgYGV2ZW50X3VzZXJfaWRgICh1c2VyIElEcykgYW5kIGBwYWdlX25hbWVzcGFjZWAgKHBhZ2UgbmFtZXNwYWNlIG9mIHRoZSBwYWdlIHdoZXJlIGEgcGFydGljdWxhciByZXZpc2lvbiB0b29rIHBsYWNlKSBjb21iaW5hdGlvbnMgYWNyb3NzIGFsbCB1c2VycyBhbmQgcmV2aXNpb25zIG9uIGB3aWtpZGF0YXdpa2lgLiBCb3RzIGFuZCBhbm9ueW1vdXMgdXNlcnMgYXJlIGZpbHRlcmVkIG91dCwgYXMgd2VsbCBhcyBwYWdlIHJlZGlyZWN0cyBhbmQgcmV2aXNpb25zIHRoYXQgd2VyZSBzdWJzZXF1ZW50bHkgZGVsZXRlZC4gVGhlIHJlc3VsdGluZyB0YWJsZSBwcmVzZW50cyB0aGUgbnVtYmVyIG9mIHJldmlzaW9ucyBtYWRlIGJ5IGFueSB1c2VyIG9uIGFueSBwYWdlLCBncm91cGVkIGJ5IHBhZ2UgbmFtZXNwYWNlcy4KCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZ9CmZpbGVuYW1lIDwtICJXRF9yZXZpc2lvbnMudHN2Igpjb25uZWN0IDwtICcvdXNyL2xvY2FsL2Jpbi9iZWVsaW5lIC0tc2lsZW50IC0taW5jcmVtZW50YWwgLWUnCmFyZ3MgPC0gJyJVU0Ugd21mOyAKICAgICAgICAgIFNFTEVDVCBDT1VOVCgqKSwgZXZlbnRfdXNlcl9pZCwgcGFnZV9uYW1lc3BhY2UgRlJPTSBtZWRpYXdpa2lfaGlzdG9yeSAKICAgICAgICAgICAgV0hFUkUgKAogICAgICAgICAgICAgIGV2ZW50X2VudGl0eSA9IFwncmV2aXNpb25cJyBBTkQgCiAgICAgICAgICAgICAgZXZlbnRfdHlwZSA9IFwnY3JlYXRlXCcgQU5EIAogICAgICAgICAgICAgIHdpa2lfZGIgPSBcJ3dpa2lkYXRhd2lraVwnIEFORCAKICAgICAgICAgICAgICBldmVudF91c2VyX2lzX2JvdF9ieV9uYW1lID0gRkFMU0UgQU5EIAogICAgICAgICAgICAgIGV2ZW50X3VzZXJfaXNfYW5vbnltb3VzID0gRkFMU0UgQU5EIAogICAgICAgICAgICAgIE5PVCBBUlJBWV9DT05UQUlOUyhldmVudF91c2VyX2dyb3VwcywgXCdib3RcJykgQU5EIAogICAgICAgICAgICAgIE5PVCBBUlJBWV9DT05UQUlOUyhldmVudF91c2VyX2dyb3Vwc19oaXN0b3JpY2FsLCBcJ2JvdFwnKSBBTkQgCiAgICAgICAgICAgICAgZXZlbnRfdXNlcl9pZCAhPSAwIEFORCAKICAgICAgICAgICAgICBwYWdlX2lzX3JlZGlyZWN0ID0gRkFMU0UgQU5EICAKICAgICAgICAgICAgICByZXZpc2lvbl9pc19kZWxldGVkID0gRkFMU0UgQU5EIAogICAgICAgICAgICAgIHNuYXBzaG90ID0gXCcyMDE4LTA5XCcKICAgICAgICAgICAgKSAKICAgICAgICAgICAgR1JPVVAgQlkgZXZlbnRfdXNlcl9pZCwgcGFnZV9uYW1lc3BhY2U7IicKb3V0IDwtIHBhc3RlMCgiPiAvaG9tZS9nb3JhbnNtL1JTY3JpcHRzL1dpa2lkYXRhLyIsIGZpbGVuYW1lKQpxQ29tbWFuZCA8LSBwYXN0ZShjb25uZWN0LCBhcmdzLCBvdXQpCnN5c3RlbShxQ29tbWFuZCwgd2FpdCA9IFQpCmBgYAoKIyMgMS4yIFRvdGFsIHJldmlzaW9uczogdXNlcnMgdnMuIG5hbWVzcGFjZXMgaW4gU2VwdGVtYmVyIDIwMTguCgpUaGUgZm9sbG93aW5nIEhpdmVRTCBxdWVyeSBpcyBleGFjdGx5IHRoZSBzYW1lIGFzIHRoZSBwcmV2aW91cyBvbmUsIGV4Y2VwdCBmb3IgdGhlIGZhY3QgdGhhdCBpdCBmaWx0ZXJzIG91dCBhbGwgcmV2aXNpb24gb2xkZXIgdGhlbiBTZXB0bWViZXIgMjAxOC4gVGhlIG5hdHVyZSBvZiB0aGUgYHdtZi5tZWRpYXdpa2lfaGlzdG9yeWAgdGFibGUgaXMgc3VjaCB0aGF0IGEgY3VycmVudCB0YWJsZSBzbmFwc2hvdCB0YWtlcyBzb21lIHRpbWUgdG8gY29tcHV0ZSwgc28gdGhhdCB0aGUgbGF0ZXN0IHRhYmxlIHNuYXBzaG90IHRoYXQgd2FzIGF2YWlsYWJsZSBpcyBgMjAxOC0wOWAuIEluIG90aGVyIHdvcmRzLCB0aGUgT2N0b2JlciAyMDE4IGRhdGFzZXQgd2FzIG5vdCB5ZXQgcmVhZHkgd2hlbiB3ZSd2ZSBydW4gdGhlc2UgYW5hbHlzZXMuICAKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZ9CmZpbGVuYW1lIDwtICJXRF9yZXZpc2lvbnNfU2VwdGVtYmVyLnRzdiIKY29ubmVjdCA8LSAnL3Vzci9sb2NhbC9iaW4vYmVlbGluZSAtLXNpbGVudCAtLWluY3JlbWVudGFsIC1lJwphcmdzIDwtICciVVNFIHdtZjsgCiAgICAgICAgICBTRUxFQ1QgQ09VTlQoKiksIGV2ZW50X3VzZXJfaWQsIHBhZ2VfbmFtZXNwYWNlIEZST00gbWVkaWF3aWtpX2hpc3RvcnkgCiAgICAgICAgICAgIFdIRVJFICgKICAgICAgICAgICAgICBldmVudF9lbnRpdHkgPSBcJ3JldmlzaW9uXCcgQU5EIAogICAgICAgICAgICAgIGV2ZW50X3R5cGUgPSBcJ2NyZWF0ZVwnIEFORCAKICAgICAgICAgICAgICB3aWtpX2RiID0gXCd3aWtpZGF0YXdpa2lcJyBBTkQgCiAgICAgICAgICAgICAgZXZlbnRfdXNlcl9pc19ib3RfYnlfbmFtZSA9IEZBTFNFIEFORCAKICAgICAgICAgICAgICBldmVudF91c2VyX2lzX2Fub255bW91cyA9IEZBTFNFIEFORCAKICAgICAgICAgICAgICBOT1QgQVJSQVlfQ09OVEFJTlMoZXZlbnRfdXNlcl9ncm91cHMsIFwnYm90XCcpIEFORCAKICAgICAgICAgICAgICBOT1QgQVJSQVlfQ09OVEFJTlMoZXZlbnRfdXNlcl9ncm91cHNfaGlzdG9yaWNhbCwgXCdib3RcJykgQU5EIAogICAgICAgICAgICAgIGV2ZW50X3VzZXJfaWQgIT0gMCBBTkQgCiAgICAgICAgICAgICAgcGFnZV9pc19yZWRpcmVjdCA9IEZBTFNFIEFORCAgCiAgICAgICAgICAgICAgcmV2aXNpb25faXNfZGVsZXRlZCA9IEZBTFNFIEFORCAKICAgICAgICAgICAgICBzbmFwc2hvdCA9IFwnMjAxOC0wOVwnIEFORCAKICAgICAgICAgICAgICBldmVudF90aW1lc3RhbXAgPiBcJzIwMTgtMDktMDEgMDA6MDA6MDAuMFwnCiAgICAgICAgICAgICkgCiAgICAgICAgICAgIEdST1VQIEJZIGV2ZW50X3VzZXJfaWQsIHBhZ2VfbmFtZXNwYWNlOyInCm91dCA8LSBwYXN0ZTAoIj4gL2hvbWUvZ29yYW5zbS9SU2NyaXB0cy9XaWtpZGF0YS9XRF9lZGl0cy8iLCBmaWxlbmFtZSkKcUNvbW1hbmQgPC0gcGFzdGUoY29ubmVjdCwgYXJncywgb3V0KQpzeXN0ZW0ocUNvbW1hbmQsIHdhaXQgPSBUKQpgYGAKCiMjIDEuMyBVc2VyIHJlZ2lzdHJhdGlvbnMKClRoZSBmb2xsb3dpbmcgSGl2ZVFMIHF1ZXJ5IGNvbGxlY3RzIGFsbCB1c2VyIHJlZ2lzdHJhdGlvbnMgb24gYHdpa2lkYXRhd2lraWAuIFRoZSBkb2N1bWVudGF0aW9uIG9mIHRoZSBgd21mLm1lZGlhd2lraV9oaXN0b3J5YCB0YWJsZSBpcyBub3QgY2xlYXIgb24gdGhlIGZvbGxvd2luZyBwb2ludDogd2hhdCBpcyB0aGUgZXhhY3QgbWVhbmluZyBvZiB0aGUgYGV2ZW50X3VzZXJfYCB0eXBlIG9mIGZpZWxkcyBpbiB0aGUgY29udGV4dCB3aGVyZSBgZXZlbnRfZW50aXR5ID0gdXNlcmA/IEluIG90aGVyIHdvcmRzLCBpdCBpcyB1bmNlcnRhaW4gd2hldGhlciBmaWx0ZXJpbmcgb3V0IGJvdHMgYW5kIGFub255bW91cyB1c2VycyBzaG91bGQgYmUgZG9uZSBieSByZWZlcmluZyB0byBgZXZlbnRfdXNlcl9pc19gIHR5cGUgb2YgZmllbGRzIG9yIHRvIGB1c2VyX2lzX2AgdHlwZSBvZiBmaWxlZHMuIEluIG9yZGVyIHRvIHByZXZlbnQgYW55IGluY29uc2lzdGVuY2llcyBpbiB0aGUgZGF0YSwgYm90aCB0eXBlcyBvZiBmaWVsZHMgd2hlcmUgdXNlZCB0byBmaWx0ZXIgb3V0IGJvdHMgYW5kIGFub255bW95cyB1c2Vycy4gSW4gc3BpdGUgb2YgdGhpcyBwcmVjYXV0aW9uLCB0aGUgbGF0ZXIgcmVzdWx0cyB3aWxsIGRlbW9uc3RyYXRlIHRoYXQgaXQgaXMgaGlnaGx5IGxpa2VseSB0aGF0IGF0IGxlYXN0IHNvbWUgYm90cyBoYXZlICJzdXJ2aXZlZCIgdGhlc2UgZmlsdGVyaW5nIHByb2NlZHVyZXMuIFRoZXkgd2VyZSBmaWx0ZXJlZCBvdXQgbXkgc3RhdGlzdGljYWwgbWV0aG9kcywgYXMgaXQgd2lsbCBiZSBkZW1vbnN0cmF0ZWQuIFRoZSByZXN1bHRpbmcgdGFibGUgY29udGFpbnMgdGhlIHVzZXIgSURzIGFuZCB0aGUgdGltZXN0YW1wcyBvZiB0aGVpciByZWdpc3RyYXRpb24gd2l0aCBgd2lraWRhdGF3aWtpYC4KKipOb3RlLioqIEluIHNwaXRlIG9mIHRoZSBmYWN0IHRoYXQgdGhlIFdpa2lkYXRhIHByb2plY3Qgc3RhcnRlZCBvbiBPY3RvYmVyIDMwLCAyMDEyLCBtYW55IHJlZ2lzdHJhdGlvbiB0aW1lc3RhbXBzIGFyZSBhcyBvbGQgYXMgMjAwNC4gVGhlIG9ubHkgaHlwb3RoZXNpcyBpbiByZXNwZWN0IHRvIHRoaXMgZmFjdCBpcyB0aGF0IHRoZSBvbGRlciByZWdpc3RyYXRpb24gdGltZXN0YW1wcyBleGlzdCBhcyBhIGNvbnNlcXVlbmNlIG9mIHVuaWZ5ZWQgbG9naW5zIG9mIHRob3NlIHVzZXJzIHdobyB3ZXJlIGFscmVhZHkgcmVnaXN0ZXJlZCBhdCBvdGhlciB3aWtpZXMuIEFzIHdlIHdpbGwgc2VlIHRoZXNlIHVzZXIgYWNjb3VudHMgd2VyZSBmaWx0ZXJlZCBvdXQgZnJvbSBzb21lIG9mIHRoZSBhbmFseXNlcy4KCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZ9CmZpbGVuYW1lIDwtICJXRF91c2Vycy50c3YiCmNvbm5lY3QgPC0gJy91c3IvbG9jYWwvYmluL2JlZWxpbmUgLS1zaWxlbnQgLS1pbmNyZW1lbnRhbCAtZScKYXJncyA8LSAnIlVTRSB3bWY7IAogICAgICAgICAgU0VMRUNUIHVzZXJfaWQsIHVzZXJfY3JlYXRpb25fdGltZXN0YW1wIEZST00gbWVkaWF3aWtpX2hpc3RvcnkgCiAgICAgICAgICAgIFdIRVJFICgKICAgICAgICAgICAgICBldmVudF9lbnRpdHkgPSBcJ3VzZXJcJyBBTkQgCiAgICAgICAgICAgICAgZXZlbnRfdHlwZSA9IFwnY3JlYXRlXCcgQU5EIAogICAgICAgICAgICAgIHdpa2lfZGIgPSBcJ3dpa2lkYXRhd2lraVwnIEFORCAKICAgICAgICAgICAgICBldmVudF91c2VyX2lzX2JvdF9ieV9uYW1lID0gRkFMU0UgQU5EIAogICAgICAgICAgICAgIGV2ZW50X3VzZXJfaXNfYW5vbnltb3VzID0gRkFMU0UgQU5EIAogICAgICAgICAgICAgIHVzZXJfaXNfYm90X2J5X25hbWUgPSBGQUxTRSBBTkQgCiAgICAgICAgICAgICAgdXNlcl9pc19hbm9ueW1vdXMgPSBGQUxTRSBBTkQgCiAgICAgICAgICAgICAgTk9UIEFSUkFZX0NPTlRBSU5TKGV2ZW50X3VzZXJfZ3JvdXBzLCBcJ2JvdFwnKSBBTkQgCiAgICAgICAgICAgICAgTk9UIEFSUkFZX0NPTlRBSU5TKGV2ZW50X3VzZXJfZ3JvdXBzX2hpc3RvcmljYWwsIFwnYm90XCcpIEFORCAKICAgICAgICAgICAgICBOT1QgQVJSQVlfQ09OVEFJTlModXNlcl9ncm91cHMsIFwnYm90XCcpIEFORCAKICAgICAgICAgICAgICBOT1QgQVJSQVlfQ09OVEFJTlModXNlcl9ncm91cHNfaGlzdG9yaWNhbCwgXCdib3RcJykgQU5EIAogICAgICAgICAgICAgIHNuYXBzaG90ID0gXCcyMDE4LTA5XCcKICAgICAgICAgICAgKTsiJwpvdXQgPC0gcGFzdGUwKCI+IC9ob21lL2dvcmFuc20vUlNjcmlwdHMvV2lraWRhdGEvV0RfZWRpdHMvIiwgZmlsZW5hbWUpCnFDb21tYW5kIDwtIHBhc3RlKGNvbm5lY3QsIGFyZ3MsIG91dCkKc3lzdGVtKHFDb21tYW5kLCB3YWl0ID0gVCkKYGBgCgoKIyAyLiBEYXRhIFdyYW5nbGluZwoKVGhlIGZvbGxvd2luZyBwcm9jZWR1cmVzIGxvYWQgdGhlIGRhdGFzZXRzIGxvY2FsbHksIGltcG9zZSBjb25zaXN0ZW50IGNvbHVtbiBuYW1lcyB0byBvYnRhaW4gdGhlIGRlc2lyZWQgbWF0Y2hlcywgaW50cm9kdWNlcyB0aGUgbmFtZXNwYWNlIG5hbWVzIChlLmcuICJNYWluIChJdGVtcykiLCAiTWFpbiAoSXRlbXMgdGFsaykiLCBhbmQgc2ltaWxhcikgYmV5b25kIG5hbWVzcGFjZSBjb2RlcywgYW5kIGNyZWF0ZXMgYFBvc2l4YCB0aW1lc3RhbXBzIGZvciB1c2VyIHJlZ2lzdHJhdGlvbnMuIEZpbmFsbHksIHRoZSByZXZpc2lvbiBkYXRhIGFyZSBmaWx0ZXJlZCBvdXQgYWRkaXRpb25hbHkgdG8ga2VlcCBvbmx5IHRoZSB1c2VyIGFjY291bnRzIHRoYXQgYXJlIGZvdW5kIGluIHRoZSB1c2VyIHJlZ2lzdHJhdGlvbiBkYXRhLgoKYGBge3IgZWNobyA9IFR9CiMgLSBsb2NhbGx5OiByZXZpc2lvbnMgdnMgbmFtZXNwYWNlcwpsaWJyYXJ5KGRhdGEudGFibGUpCnNldHdkKCcvaG9tZS9nb3JhbnNtL1dvcmsvX19fRGF0YUtvbGVrdGl2L1Byb2plY3RzL1dpa2ltZWRpYURFVS9fV01ERV9Qcm9qZWN0cy9XaWtpZGF0YS9XRF9lZGl0cy8nKQp3ZFJldmlzaW9ucyA8LSBmcmVhZCgnV0RfcmV2aXNpb25zLnRzdicsIHNlcCA9ICJcdCIpCmNvbG5hbWVzKHdkUmV2aXNpb25zKSA8LSBjKCdyZXZpc2lvbnMnLCAndXNlcicsICd3ZF9uYW1lc3BhY2UnKQojIC0gbG9jYWxseTogcmV2aXNpb25zIHZzIG5hbWVzcGFjZXMsIFNlcHRlbWJlciBvbmx5CndkUmV2aXNpb25zU2VwIDwtIGZyZWFkKCdXRF9yZXZpc2lvbnNfU2VwdGVtYmVyLnRzdicsIHNlcCA9ICJcdCIpCmNvbG5hbWVzKHdkUmV2aXNpb25zU2VwKSA8LSBjKCdyZXZpc2lvbnMnLCAndXNlcicsICd3ZF9uYW1lc3BhY2UnKQojIC0gbG9jYWxseTogdXNlciByZWdpc3RyYXRpb25zCndkVXNlcnMgPC0gZnJlYWQoJ1dEX3VzZXJzLnRzdicsIHNlcCA9ICJcdCIpCmNvbG5hbWVzKHdkVXNlcnMpIDwtIGMoJ3VzZXInLCAnY3JlYXRpb25fdGltZXN0YW1wJykKCiMjIyAtLS0gV3JhbmdsaW5nCmxpYnJhcnkoZHBseXIpCiMgLSBXRCBuYW1lc3BhY2UgY29kaW5nIHRhYmxlOyAKIyAtIGFkZCBuYW1lc3BhY2UgbmFtZSB0byB3ZFJldmlzaW9ucywgd2RSZXZpc2lvbnNTZXAgCiMgLSBodHRwczovL3d3dy53aWtpZGF0YS5vcmcvd2lraS9IZWxwOk5hbWVzcGFjZXMKbmFtZXNwYWNlcyA8LSByZWFkLmNzdignbmFtZXNwYWNlX2NvZGluZ19zY2hlbWUuY3N2JywgCiAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgCiAgICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKd2RSZXZpc2lvbnMgPC0gbGVmdF9qb2luKHdkUmV2aXNpb25zLCBuYW1lc3BhY2VzLAogICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJ3ZF9uYW1lc3BhY2UiID0gIm5hbWVzcGFjZUNvZGUiKSkKd2RSZXZpc2lvbnNTZXAgPC0gbGVmdF9qb2luKHdkUmV2aXNpb25zU2VwLCBuYW1lc3BhY2VzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJ3ZF9uYW1lc3BhY2UiID0gIm5hbWVzcGFjZUNvZGUiKSkKIyAtIGNyZWF0aW9uX3RpbWVzdGFtcCBpbiB3ZFVzZXJzIHRvIFBvc2l4CmxpYnJhcnkobHVicmlkYXRlKQp3ZFVzZXJzJGNyZWF0aW9uX3RpbWVzdGFtcCA8LSBzYXBwbHkod2RVc2VycyRjcmVhdGlvbl90aW1lc3RhbXAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdCgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICIuIiwgZml4ZWQgPSBUKVtbMV1dWzFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKVtbMV1dWzFdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KQp3ZFVzZXJzJGNyZWF0aW9uX3RpbWVzdGFtcCA8LSB5bWQod2RVc2VycyRjcmVhdGlvbl90aW1lc3RhbXApCndkVXNlcnMkWU0gPC0gc2FwcGx5KGFzLmNoYXJhY3Rlcih3ZFVzZXJzJGNyZWF0aW9uX3RpbWVzdGFtcCksIAogICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIsIGZpeGVkID0gVClbWzFdXVsxOjJdLCBjb2xsYXBzZSA9ICItIikKICAgICAgICAgICAgICAgICAgICAgfSkKd2RVc2VycyRjcmVhdGlvbl90aW1lc3RhbXAgPC0gTlVMTAojIC0ga2VlcCBvbmx5IHJldmlzaW9ucyBpbiB3ZFJldmlzaW9ucywgd2RSZXZpc2lvbnNTZXAKIyAtIG1hZGUgb24gYmVoYWxmIG9mIHVzZXJzIGluIHdkVXNlcnMKdzEgPC0gd2hpY2god2RSZXZpc2lvbnMkdXNlciAlaW4lIHdkVXNlcnMkdXNlcikKd2RSZXZpc2lvbnMgPC0gd2RSZXZpc2lvbnNbdzEsIF0KdzIgPC0gd2hpY2god2RSZXZpc2lvbnNTZXAkdXNlciAlaW4lIHdkVXNlcnMkdXNlcikKd2RSZXZpc2lvbnNTZXAgPC0gd2RSZXZpc2lvbnNTZXBbdzIsIF0KYGBgCgoKIyAzLiBEYXRhIEFuYWx5c2VzCgoKYGBge3IgZWNobyA9IFR9CiMjIyAtLS0gQW5hbHl0aWNzCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHN0cmluZ3IpCmBgYAoKIyMgUTEuIEhvdyBpcyBvdXIgdXNlciBiYXNlIGNvbXBvc2VkIGluIHRlcm1zIG9mIGVkaXQgY291bnQ/IChlLmcuIHNob3duIGJ5IGFuIGhpc3RvZ3JhbSwgeT0gY291bnQsIHg9YmlucyBvZiBlZGl0IGNvdW50IHJhbmdlcykKCkZpcnN0IHdlIGdyb3VwIGFsbCB1c2VyIHJldmlzaW9ucyBhbmQgdXNlciBJRHMgaXJyZXNwZWN0aXZlIG9mIHRoZSBwYWdlIG5hbWVzcGFjZSBhbmQgc3VtIHVwIGFsbCByZXZpc2lvbnMgcGVyIHVzZXI6CgpgYGB7ciBlY2hvID0gVH0KcGxvdEZyYW1lIDwtIHdkUmV2aXNpb25zICU+JSAKICBzZWxlY3QodXNlciwgcmV2aXNpb25zKSAlPiUgCiAgZ3JvdXBfYnkodXNlcikgJT4lIAogIHN1bW1hcmlzZShyZXZpc2lvbnMgPSBzdW0ocmV2aXNpb25zKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhyZXZpc2lvbnMpKQpgYGAKCkV4cGxvcmF0b3J5IGFuYWx5c2lzIGhhcyBpbmRpY2F0ZWQgdGhhdCBvdXRsaWVyIHJlbW92YWwgaXMgbmVjZXNzYXJ5IGluIHRoaXMgY2FzZS4gTmFtZWx5LCBhY2NvdW50IHdpdGggbWlsbGlvbnMgb2YgZWRpdHMgd2VyZSBkaXNjb3ZlcmVkLCBpbmRpY2F0aW5nIChhcyBhbHJlYWR5IG9ic2VydmVkKSB0aGUgcG9zc2liaWxpdHkgdGhhdCBzb21lIGJvdHMgd2VyZSBub3Qgc3VjZXNzZnVsbHkgcmVtb3ZlZCB3aGlsZSBmaWx0ZXJpbmcgb3V0IGJ5IHJlc3BlY3RpdmUgZGF0YWJhc2UgZmxhZ3MuIEl0IGlzIGFsc28gcG9zc2libGUgdGhhdCB3ZSBhcmUgb2JzZXJ2aW5nIHRoZSBvdXRjb21lcyBvZiBhdXRvbWF0ZWQgaW1wb3J0cyB0byBXaWtpZGF0YSB0aGF0IHdlcmUgbm90IGZsYWdnZWQgYXMgYm90cyBoZXJlLgoKYGBge3IgZWNobyA9IFR9CiMgLSBBUFBST0FDSDogRm9yIGEgZ2l2ZW4gY29udGludW91cyB2YXJpYWJsZSwgb3V0bGllcnMgYXJlIHRob3NlIG9ic2VydmF0aW9ucyAKIyAtIHRoYXQgbGllIG91dHNpZGUgMS41ICogSVFSLCB3aGVyZSBJUVIsIHRoZSDigJhJbnRlciBRdWFydGlsZSBSYW5nZeKAmSAKIyAtIGlzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gNzV0aCBhbmQgMjV0aCBxdWFydGlsZXMuCm91dHMgPC0gd2hpY2goCiAgcGxvdEZyYW1lJHJldmlzaW9ucyAlaW4lIGJveHBsb3Quc3RhdHMocGxvdEZyYW1lJHJldmlzaW9ucykkb3V0KQpwbG90RnJhbWUgPC0gcGxvdEZyYW1lWy1vdXRzLCBdCmBgYAoKIyMgUTEuMSBDaGVja2luZyBmb3IgcG93ZXItbGF3IGJlaGF2aW9yOiBsb2coTnVtYmVyIG9mIHVzZXJzKSB2cy4gbG9nIChOdW1iZXIgb2YgcmV2aXNpb25zKSBwbG90CgpXZSBhcmUgZG9pbmcgdGhpcyBub3QgdG8gY2hlY2sgZm9yIHBvd2VyLWxhd3MgcmVhbGx5IGFuZCBmb3Igc29tZSBlc3NlbnRpYWwgcmVhc29ucywgYnV0IHJhdGhlciBiZWNhdXNlIHRoZSBza2V3bmVzcyBvZiB0aGUgZGlzdHJpYnV0aW9uIHByZXZlbnRzIHVzIGZyb20gdmlzdWFsaXppbmcgdGhlc2UgZGF0YSBpbiBhbiBpbmZvcm1hdGl2ZSB3YXkuIAoKYGBge3IgZWNobyA9IFR9CiMgLSBQb3dlci1MYXcKdXNlclJldmlzaW9ucyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHBsb3RGcmFtZSRyZXZpc2lvbnMpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyh1c2VyUmV2aXNpb25zKSA8LSBjKCdSZXZpc2lvbnMnLCAnTnVtLlVzZXJzJykKdXNlclJldmlzaW9ucyRSZXZpc2lvbnMgPC0gYXMubnVtZXJpYyh1c2VyUmV2aXNpb25zJFJldmlzaW9ucykKdXNlclJldmlzaW9ucyA8LSBhcnJhbmdlKHVzZXJSZXZpc2lvbnMsIGRlc2MoUmV2aXNpb25zKSkKIyAtIGxvZy1sb2cKZ2dwbG90KGRhdGEgPSB1c2VyUmV2aXNpb25zLCAKICAgICAgIGFlcyh4ID0gbG9nMTAoUmV2aXNpb25zKSwgCiAgICAgICAgICAgeSA9IGxvZzEwKGBOdW0uVXNlcnNgKSwKICAgICAgICAgICBsYWJlbCA9IFJldmlzaW9ucykpICsgCiAgZ2VvbV9wb2ludChzaXplID0gLjEpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0sIHNpemUgPSAuMikgKyAKICBnZ3RpdGxlKCJSZXZpc2lvbnMgcGVyIHVzZXIiKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMi41KSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGhqdXN0ID0gMSkpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxMSkpICsgCiAgeWxhYigibG9nKE51bS5Vc2VycykiKQpgYGAKCiMjIFExLjIgQ2hlY2tpbmcgZm9yIHBvd2VyLWxhdyBiZWhhdmlvcjogTnVtYmVyIG9mIHVzZXJzIHZzLiBOdW1iZXIgb2YgcmV2aXNpb25zIHBsb3QKCkluIGFic29sdXRlIHZhbHVlczoKCmBgYHtyIGVjaG8gPSBUfQojIC0gYWJzb2x1dGUgdmFsdWVzCmdncGxvdChkYXRhID0gdXNlclJldmlzaW9ucywgCiAgICAgICBhZXMoeCA9IFJldmlzaW9ucywgCiAgICAgICAgICAgeSA9IGBOdW0uVXNlcnNgLAogICAgICAgICAgIGxhYmVsID0gUmV2aXNpb25zKSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAuNSkgKyBnZW9tX3BhdGgoc2l6ZSA9IC4yKSArIAogIGdndGl0bGUoIlJldmlzaW9ucyBwZXIgdXNlciIpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAyLjUpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBoanVzdCA9IDEpKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTEpKSArIAogIHlsYWIoIk51bS5Vc2VycyIpCmBgYAoKIyMgUTEuMyBEaXN0cmlidXRpb24gb2YgcmV2aXNpb25zIHBlciB1c2VyOiBhIEhpc3RvZ3JhbQoKYGBge3IgZWNobyA9IFR9CiMgLSBsb2cgaGlzdG9ncmFtCmdncGxvdChkYXRhID0gcGxvdEZyYW1lLCAKICAgICAgIGFlcyhyZXZpc2lvbnMpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbnMgPSA1MCwgZmlsbCA9ICJkZWVwc2t5Ymx1ZSIsIGNvbG9yID0gIndoaXRlIikgKyAKICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgcmV2aXNpb25zIHBlciB1c2VyIikgKyAKICB5bGFiKCJOdW0uVXNlcnMiKSArIHhsYWIoIlJldmlzaW9ucyIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAxKSkgKyAKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGhqdXN0ID0gMSkpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxMSkpCmBgYAoKIyMgUTIuIEhvdyB3ZXJlIG91ciB1c2VyIGJhc2UgY29tcG9zZWQgaW4gdGVybXMgb2YgcmVjZW50IGVkaXRzPyBIb3cgbWFueSBhY2NvdW50IGRpZCwgaW4gdGhlIGxhc3QgbW9udGggKG9yIHNvKSBlZGl0ZWQgbiB0aW1lcyAoZS5nLiBzaG93biBieSBhbiBoaXN0b2dyYW0sIHk9IGNvdW50LCB4PWJpbnMgb2YgZWRpdCBjb3VudCByYW5nZXMgZm9yIGEgbW9udGgpPwoKVGhlIHByb2NlZHVyZXMgYXJlIHRoZSBzYW1lIGFzIGluIGBRMmAsIGV4Y2VwdCBmb3Igbm93IHdlIGZvY3VzIG9uIFNlcHRlbWJlciAyMDE4IGRhdGEgb25seS4KCmBgYHtyIGVjaG8gPSBUfQpwbG90RnJhbWUgPC0gd2RSZXZpc2lvbnNTZXAgJT4lIAogIHNlbGVjdCh1c2VyLCByZXZpc2lvbnMpICU+JSAKICBncm91cF9ieSh1c2VyKSAlPiUgCiAgc3VtbWFyaXNlKHJldmlzaW9ucyA9IHN1bShyZXZpc2lvbnMpKSAlPiUgCiAgYXJyYW5nZShkZXNjKHJldmlzaW9ucykpCiMgLSBPdXRsaWVyIGRldGVjdGlvbiBhbmQgcmVtb3ZhbAojIC0gQVBQUk9BQ0g6IEZvciBhIGdpdmVuIGNvbnRpbnVvdXMgdmFyaWFibGUsIG91dGxpZXJzIGFyZSB0aG9zZSBvYnNlcnZhdGlvbnMgCiMgLSB0aGF0IGxpZSBvdXRzaWRlIDEuNSAqIElRUiwgd2hlcmUgSVFSLCB0aGUg4oCYSW50ZXIgUXVhcnRpbGUgUmFuZ2XigJkgCiMgLSBpcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIDc1dGggYW5kIDI1dGggcXVhcnRpbGVzLgpvdXRzIDwtIHdoaWNoKAogIHBsb3RGcmFtZSRyZXZpc2lvbnMgJWluJSBib3hwbG90LnN0YXRzKHBsb3RGcmFtZSRyZXZpc2lvbnMpJG91dCkKcGxvdEZyYW1lIDwtIHBsb3RGcmFtZVstb3V0cywgXQpgYGAKCiMjIFEyLjEgQ2hlY2tpbmcgZm9yIHBvd2VyLWxhdyBiZWhhdmlvcjogbG9nKE51bWJlciBvZiB1c2VycykgdnMuIGxvZyAoTnVtYmVyIG9mIHJldmlzaW9ucykgcGxvdAoKYGBge3IgZWNobyA9IFR9CnVzZXJSZXZpc2lvbnMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShwbG90RnJhbWUkcmV2aXNpb25zKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXModXNlclJldmlzaW9ucykgPC0gYygnUmV2aXNpb25zJywgJ051bS5Vc2VycycpCnVzZXJSZXZpc2lvbnMkUmV2aXNpb25zIDwtIGFzLm51bWVyaWModXNlclJldmlzaW9ucyRSZXZpc2lvbnMpCnVzZXJSZXZpc2lvbnMgPC0gYXJyYW5nZSh1c2VyUmV2aXNpb25zLCBkZXNjKFJldmlzaW9ucykpCmdncGxvdChkYXRhID0gdXNlclJldmlzaW9ucywgCiAgICAgICBhZXMoeCA9IGxvZzEwKFJldmlzaW9ucyksIAogICAgICAgICAgIHkgPSBsb2cxMChgTnVtLlVzZXJzYCksCiAgICAgICAgICAgbGFiZWwgPSBSZXZpc2lvbnMpKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IC4xKSArIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBzaXplID0gLjIpICsgCiAgZ2d0aXRsZSgiUmV2aXNpb25zIHBlciB1c2VyOiBTZXB0ZW1iZXIgMjAxOCIpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAyLjUpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAxKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDExKSkgKyAKICB5bGFiKCJsb2coTnVtLlVzZXJzKSIpCmBgYAoKIyMgUTIuMiBDaGVja2luZyBmb3IgcG93ZXItbGF3IGJlaGF2aW9yOiBOdW1iZXIgb2YgdXNlcnMgdnMuIE51bWJlciBvZiByZXZpc2lvbnMgcGxvdAoKYGBge3IgZWNobyA9IFR9CmdncGxvdChkYXRhID0gdXNlclJldmlzaW9ucywgCiAgICAgICBhZXMoeCA9IFJldmlzaW9ucywgCiAgICAgICAgICAgeSA9IGBOdW0uVXNlcnNgLAogICAgICAgICAgIGxhYmVsID0gUmV2aXNpb25zKSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAuNSkgKyBnZW9tX3BhdGgoc2l6ZSA9IC4yKSArIAogIGdndGl0bGUoIlJldmlzaW9ucyBwZXIgdXNlcjogU2VwdGVtYmVyIDIwMTgiKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMi41KSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArIAogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAxKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDExKSkgKyAKICB5bGFiKCJOdW0uVXNlcnMiKQpgYGAKCiMjIFEyLjMgRGlzdHJpYnV0aW9uIG9mIHJldmlzaW9ucyBwZXIgdXNlciBpbiBTZXB0ZW1iZXIgMjAxODogYSBIaXN0b2dyYW0KCmBgYHtyIGVjaG8gPSBUfQpnZ3Bsb3QoZGF0YSA9IHBsb3RGcmFtZSwgCiAgICAgICBhZXMocmV2aXNpb25zKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTAsIGZpbGwgPSAiZGVlcHNreWJsdWUiLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIHJldmlzaW9ucyBwZXIgdXNlcjogU2VwdGVtYmVyIDIwMTgiKSArIAogIHlsYWIoIk51bS5Vc2VycyIpICsgeGxhYigiUmV2aXNpb25zIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBoanVzdCA9IDEpKSArIAogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAxKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDExKSkKYGBgCgojIyBRMy4gSG93IG1hbnkgb2YgdGhvc2UgdXNlcnMgZG8gcGFydGljaXBhdGUgaW4gZGlzY3Vzc2lvbnM/IChtb3NhaWMgcGxvdCwgeSA9IGJpbnMgb2YgZWRpdCBjb3VudCwgeCA9IGJpbnMgb2YgZGlzY3Vzc2lvbiBwYXJ0aWNpcGF0aW9uKQoKSSB3YXMgbm90IGVudGlyZWx5IHN1cmUgaWYgSSBoYXZlIHVuZGVyc3Rvb2QgY29ycmVjdGx5IHRoZSBpbnRlbnRpb24gd2l0aCB0aGUgbW9zYWljIHBsb3QgaGVyZSB3aGVuIEkndmUgc3RhcnRlZCB3b3JraW5nIG9uIHRoaXMgYW5hbHlzaXMuIEhvd2V2ZXIsIHRoZSBmb2xsb3dpbmcgYXBwcm9hY2ggc2hvdWxkIGJlIGlsbHVzdHJhdGl2ZSBpbiByZXNwZWN0IHRvIGBRM2AuCgpJbiB0aGUgZmlyc3Qgc3RlcCwgd2Ugc2VwYXJhdGUgYWxsIHRhbGsgKGkuZS4gZGlzY3Vzc2lvbikgbmFtZXNwYWNlcyBmcm9tIHRoZSBuYW1lc3BhY2VzIHdoZXJlICJtYWluIiByZXZpc2lvbnMgdGFrZSBwbGFjZS4KCmBgYHtyIGVjaG8gPSBUfQp3ZFJldmlzaW9uc1NlcCR0YWxrIDwtIGlmZWxzZShncmVwbCgidGFsayIsIHdkUmV2aXNpb25zU2VwJG5hbWVzcGFjZSwgZml4ZWQgPSBUKSwgImRpc2N1c3Npb24iLCAiZWRpdCIpCnBsb3RGcmFtZSA8LSB3ZFJldmlzaW9uc1NlcCAlPiUgCiAgc2VsZWN0KHVzZXIsIHJldmlzaW9ucywgdGFsaykgJT4lIAogIGdyb3VwX2J5KHVzZXIsIHRhbGspICU+JSAKICBzdW1tYXJpc2UocmV2aXNpb25zID0gc3VtKHJldmlzaW9ucykpCmBgYAoKUmVtb3Zpbmcgb3V0bGllcnMsIGFzIGluIHByZXZpb3VzIGFuYWx5c2VzOgoKYGBge3IgZWNobyA9IFR9CiMgLSBBUFBST0FDSDogRm9yIGEgZ2l2ZW4gY29udGludW91cyB2YXJpYWJsZSwgb3V0bGllcnMgYXJlIHRob3NlIG9ic2VydmF0aW9ucyAKIyAtIHRoYXQgbGllIG91dHNpZGUgMS41ICogSVFSLCB3aGVyZSBJUVIsIHRoZSDigJhJbnRlciBRdWFydGlsZSBSYW5nZeKAmSAKIyAtIGlzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gNzV0aCBhbmQgMjV0aCBxdWFydGlsZXMuCm91dHMgPC0gd2hpY2goCiAgcGxvdEZyYW1lJHJldmlzaW9ucyAlaW4lIGJveHBsb3Quc3RhdHMocGxvdEZyYW1lJHJldmlzaW9ucykkb3V0KQpwbG90RnJhbWUgPC0gcGxvdEZyYW1lWy1vdXRzLCBdCnBsb3RGcmFtZSA8LSBhcnJhbmdlKHBsb3RGcmFtZSwgdXNlcikKYGBgCgpOb3cgd2UgY3Jvc3MtdGFidWxhdGUgdGhlIG51bWJlciBvZiByZXZpc2lvbnMgbWFkZSBvbiBkaXNjdXNzaW9uIGFuZCBtYWluIG5hbWVzcGFjZXMgZm9yIGFsbCB1c2VycywgYW5kIHZpc3VhbGl6ZSB0aGUgUiBgdGFibGVgIG9iamVjdCB0byBvYnRhaW4gYSBtb3NhaWMgcGxvdDogCgpgYGB7ciBlY2hvID0gVH0KIyAtIG51bWJlciBvZiB1c2VycyB0aGF0IGhhdmUgYm90aCBlZGl0ZWQgYW5kIGRpc2N1c3NlZApudW1FZGl0VGFsa1VzZXJzIDwtIHN1bShkdXBsaWNhdGVkKHBsb3RGcmFtZSR1c2VyKSkKd0VkaXRUYWxrVXNlcnMgPC0gd2hpY2goZHVwbGljYXRlZChwbG90RnJhbWUkdXNlcikpCndFZGl0VGFsa1VzZXJzIDwtIHBsb3RGcmFtZSR1c2VyW3dFZGl0VGFsa1VzZXJzXQpwbG90RnJhbWUgPC0gcGxvdEZyYW1lW3Bsb3RGcmFtZSR1c2VyICVpbiUgd0VkaXRUYWxrVXNlcnMsIF0KcEZyYW1lIDwtIGRhdGEuZnJhbWUoZWRpdHMgPSBwbG90RnJhbWUkcmV2aXNpb25zW3Bsb3RGcmFtZSR0YWxrID09ICJlZGl0Il0sCiAgICAgICAgICAgICAgICAgICAgIGRpc2N1c3Npb25zID0gcGxvdEZyYW1lJHJldmlzaW9uc1twbG90RnJhbWUkdGFsayA9PSAiZGlzY3Vzc2lvbiJdLCAKICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZWRpdHMpCiMgQSAibW9zYWljIiBwbG90CnBsb3QodGFibGUocEZyYW1lJGVkaXRzLCBwRnJhbWUkZGlzY3Vzc2lvbnMpLAogICAgIHhsYWIgPSAiRWRpdHMiLCB5bGFiID0gIkRpc2N1c3Npb25zIiwgCiAgICAgbWFpbiA9ICJFZGl0cyB2cy4gRGlzY3Vzc2lvbnMsIFNlcCAyMDE4IiwgCiAgICAgY29sb3IgPSAiZGFya29yYW5nZSIpCmBgYAoKQW5vdGhlciwgbW9yZSBjb25zZXJ2YXRpdmUgYW5kIHBvdGVudGlhbHkgdXNlZnVsIGFwcHJvYWNoIHRvIHZpc3VhbGl6ZSB0aGUgc2FtZSBjcm9zcy10YWJ1bGF0ZWQgZGF0YToKCmBgYHtyIGVjaG8gPSBUfQpnZ3Bsb3QoZGF0YSA9IHBGcmFtZSwgCiAgICAgICBhZXMoeCA9IGVkaXRzLCB5ID0gZGlzY3Vzc2lvbnMsIGxhYmVsID0gZGlzY3Vzc2lvbnMpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMS41LCBjb2xvciA9ICJibHVlIikgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGNvbG9yID0gIndoaXRlIikgKwogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiByZXZpc2lvbnMgcGVyIHVzZXI6IFNlcHRlbWJlciAyMDE4IikgKwogIHlsYWIoIkRpc2N1c3Npb25zIikgKyB4bGFiKCJFZGl0cyIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAxKSkgKyAKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGhqdXN0ID0gMSkpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxMSkpCmBgYAoKCiMjIyBRMy4xIEFsdGVybmF0aXZlIGFwcHJvYWNoCgpUaGlzIGlzIHRoZSBudW1iZXIgb2YgdXNlcnMgZm9sbG93aW5nIHRoZSBiaW5uaW5nIG9mIHRoZSBkaXN0cmlidXRpb25zIG9mIHRoZSBudW1iZXIgb2YgZWRpdHMgKGkuZS4gcmV2aXNpb25zIG1hZGUgaW4gYW55IG9mIHRoZSBtYWluIG5hbWVzcGFjZXMpIGFuZCB0aGUgbnVtYmVyIG9mIGRpc2N1c3Npb25zIChpLmUuIHJldmlzaW9ucyBtYWRlIG9uIHRhbGsgcGFnZXMpOgoKYGBge3IgZWNobyA9IFR9CnBGcmFtZSRlZGl0SW50ZXJ2YWxzIDwtIGN1dChwRnJhbWUkZWRpdHMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gNCkKcEZyYW1lJGVkaXRJbnRlcnZhbHMgPC0gc2FwcGx5KHBGcmFtZSRlZGl0SW50ZXJ2YWxzLCBmdW5jdGlvbih4KSB7CiAgZWRnZXMgPC0gc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKHgpLCBzcGxpdCA9ICIsIilbWzFdXQogIGVkZ2VzWzFdIDwtIGZsb29yKGFzLm51bWVyaWMoc3RyX2V4dHJhY3QoZWRnZXNbMV0sICIoW1s6ZGlnaXQ6XV18XFwuKSsiKSkpICsgMQogIGVkZ2VzWzJdIDwtIGZsb29yKGFzLm51bWVyaWMoc3RyX2V4dHJhY3QoZWRnZXNbMl0sICIoW1s6ZGlnaXQ6XV18XFwuKSsiKSkpCiAgcmV0dXJuKHBhc3RlMCgiKCIsIGVkZ2VzWzFdLCIgLSAiLCBlZGdlc1syXSwgIikiKSkKfSkKcEZyYW1lJGRpc2N1c3Npb25JbnRlcnZhbHMgPC0gY3V0KHBGcmFtZSRkaXNjdXNzaW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IDQpCnBGcmFtZSRkaXNjdXNzaW9uSW50ZXJ2YWxzIDwtIHNhcHBseShwRnJhbWUkZGlzY3Vzc2lvbkludGVydmFscywgZnVuY3Rpb24oeCkgewogIGVkZ2VzIDwtIHN0cnNwbGl0KGFzLmNoYXJhY3Rlcih4KSwgc3BsaXQgPSAiLCIpW1sxXV0KICBlZGdlc1sxXSA8LSBmbG9vcihhcy5udW1lcmljKHN0cl9leHRyYWN0KGVkZ2VzWzFdLCAiKFtbOmRpZ2l0Ol1dfFxcLikrIikpKSArIDEKICBlZGdlc1syXSA8LSBmbG9vcihhcy5udW1lcmljKHN0cl9leHRyYWN0KGVkZ2VzWzJdLCAiKFtbOmRpZ2l0Ol1dfFxcLikrIikpKQogIHJldHVybihwYXN0ZTAoIigiLCBlZGdlc1sxXSwiIC0gIiwgZWRnZXNbMl0sICIpIikpCn0pCmVkaXRJbnRlcnZhbHNMZXZlbHMgPC0gc2FwcGx5KHVuaXF1ZShwRnJhbWUkZWRpdEludGVydmFscyksIGZ1bmN0aW9uKHgpIHsKICBsb3dlciA8LSBzdHJfZXh0cmFjdCh4LCAnW1s6ZGlnaXQ6XV0rJykKfSkKZWRpdEludGVydmFsc0xldmVscyA8LSBuYW1lcyhlZGl0SW50ZXJ2YWxzTGV2ZWxzKVtvcmRlcihhcy5udW1lcmljKGVkaXRJbnRlcnZhbHNMZXZlbHMpKV0KZGlzY3Vzc2lvbkludGVydmFsc0xldmVscyA8LSBzYXBwbHkodW5pcXVlKHBGcmFtZSRkaXNjdXNzaW9uSW50ZXJ2YWxzKSwgZnVuY3Rpb24oeCkgewogIGxvd2VyIDwtIHN0cl9leHRyYWN0KHgsICdbWzpkaWdpdDpdXSsnKQp9KQpkaXNjdXNzaW9uSW50ZXJ2YWxzTGV2ZWxzIDwtIG5hbWVzKGRpc2N1c3Npb25JbnRlcnZhbHNMZXZlbHMpW29yZGVyKGFzLm51bWVyaWMoZGlzY3Vzc2lvbkludGVydmFsc0xldmVscykpXQoKcEZyYW1lJGVkaXRJbnRlcnZhbHMgPC0gZmFjdG9yKHBGcmFtZSRlZGl0SW50ZXJ2YWxzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGVkaXRJbnRlcnZhbHNMZXZlbHMpCnBGcmFtZSRkaXNjdXNzaW9uSW50ZXJ2YWxzIDwtIGZhY3RvcihwRnJhbWUkZGlzY3Vzc2lvbkludGVydmFscywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBkaXNjdXNzaW9uSW50ZXJ2YWxzTGV2ZWxzKQpwRnJhbWUgJT4lIAogIGRwbHlyOjpzZWxlY3QoZWRpdEludGVydmFscywgZGlzY3Vzc2lvbkludGVydmFscykgJT4lIAogIGRwbHlyOjpncm91cF9ieShlZGl0SW50ZXJ2YWxzLCBkaXNjdXNzaW9uSW50ZXJ2YWxzKSAlPiUgCiAgc3VtbWFyaXNlKENvdW50ID0gbigpKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZWRpdEludGVydmFscywKICAgICAgICAgICAgIHkgPSBkaXNjdXNzaW9uSW50ZXJ2YWxzLAogICAgICAgICAgICAgbGFiZWwgPSBDb3VudCkpICsKICBnZW9tX3BvaW50KGFlcyhzaXplID0gQ291bnQpLCBjb2xvciA9ICJjYWRldGJsdWU0Iiwgc2hhcGUgPSAxOSkgKwogIGdlb21fdGV4dChzaXplID0gMywgbnVkZ2VfeCA9IC4xNSkgKyAKICB4bGFiKCJFZGl0cyIpICsgeWxhYigiRGlzY3Vzc2lvbnMiKSArIAogIHRoZW1lX2J3KCkgKyAKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAibGlnaHRibHVlIikpCmBgYAoKKipOT1RFLioqIFRoaXMgaXMgKGFzIHJlcXVlc3RlZCBvbiBQaGFiKSBvbmx5IGZvciByZWNlbnQgcmV2aXNpb25zIChTZXB0ZW1iZXIgMjAxOCkuIFdlIGNhbiBoYXZlIHRoaXMgcHJvZHVjZWQgZnJvbSBhbGwgYXZhaWxhYmxlIHJldmlzaW9ucyBvZiBjb3Vyc2UuCgojIyBRNC4gSG93IG1hbnkgbmV3IHVzZXJzIGRvIHdlIGhhdmUgZWFjaCBtb250aD8KCioqTm90ZS4qKiBNeSBkZWNpc2lvbiBoZXJlIHdhcyB0byBlbmNvbXBhc3Mgb25seSB0aG9zZSB1c2VycyB3aG8gaGF2ZSByZWdpc3RlcmVkIGxhdGVyIHRoYW4gb3IgaW4gTm92ZW1iZXIgMjAxMiAoc2luY2UgV2lraWRhdGEgc3RhcnRlZCBpbiBPY3RvYmVyIDIwMTgpLgoKIyMjIFE0LjEgVXNlciByZWdpc3RyYXRpb25zIHBlciBtb250aAoKYGBge3IgZWNobyA9IFR9CmxpYnJhcnkodGlkeXIpCnVzZXJzRnJhbWUgPC0gd2RVc2VycyAlPiUgCiAgZmlsdGVyKFlNID49ICIyMDEyLTExIikgJT4lIAogIGFycmFuZ2UoWU0pCnVzZXJzUmVnaXN0ZXJlZCA8LSB1c2Vyc0ZyYW1lICU+JSAKICBncm91cF9ieShZTSkgJT4lIAogIHN1bW1hcmlzZShyZWdpc3RyYXRpb25zID0gbigpKQpnZ3Bsb3QoZGF0YSA9IHVzZXJzUmVnaXN0ZXJlZCwgCiAgICAgICBhZXMoeCA9IFlNLCB5ID0gcmVnaXN0cmF0aW9ucywgbGFiZWwgPSByZWdpc3RyYXRpb25zKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSwgZ3JvdXAgPSAxLCBjb2xvciA9ICJibHVlIikgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUsIGNvbG9yID0gImJsdWUiKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGNvbG9yID0gIndoaXRlIikgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDIuNSkgKwogIGdndGl0bGUoIlJlZ2lzdHJhdGlvbnMgb24gV2lraWRhdGEgc2luY2U6IE5vdmVtYmVyIDIwMTIiKSArCiAgeWxhYigiWWVhci1Nb250aCIpICsgeGxhYigiUmVnaXN0cmF0aW9ucyIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA2LCBoanVzdCA9IDEsIGFuZ2xlID0gOTApKSArIAogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAxKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDExKSkKYGBgCgojIyMgUTQuMiBVc2VyIHJlZ2lzdHJhdGlvbnMgYW5kIHJldmlzaW9ucyB2cy4gcGFydGljaXBhdGlvbiBpbiBkaXNjdXNzaW9ucwoKKipOb3RlLioqIExvZ2FyaXRobWljIHNjYWxpbmcgd2FzIHVzZWQ7IGBlZGl0YCBzdGFuZHMgZm9yIHJldmlzaW9ucyBvbiB0aGUgIm1haW4iIG5hbWVzcGFjZXMgKG5vdGU6IG5vdCBvbmx5IEl0ZW1zLCBidXQgYWxsIG5hbWVzcGFjZXMgdGhhdCBhcmUgbm90ICJ0YWxrIiBuYW1lc3BhY2VzKSwgYGRpc2N1c3Npb25gIGZvciByZXZpc2lvbnMgb24gInRhbGsiIG5hbWVzcGFjZXMuIEVhY2ggcG9pbnQgcmVwcmVzZW50cyBhIGNvdW50IGluIHRoZSBwYXJ0aWN1bGFyIG1vbnRoIHNpbmNlIE5vdmVtYmVyIDIwMTIgaW4gd2hpY2ggdGhlIHVzZXJzIHdob3NlIGRhdGEgYXJlIHRha2VuIGludG8gY29uc2lkZXJhdGlvbiBoYXZlIHJlZ2lzdGVyZWQuCgpgYGB7ciBlY2hvID0gVH0Kd2RSZXZpc2lvbnMkdGFsayA8LSBpZmVsc2UoZ3JlcGwoInRhbGsiLCB3ZFJldmlzaW9ucyRuYW1lc3BhY2UsIGZpeGVkID0gVCksICJkaXNjdXNzaW9uIiwgImVkaXQiKQp1c2VyUmV2cyA8LSB3ZFJldmlzaW9ucyAlPiUgCiAgc2VsZWN0KHVzZXIsIHRhbGssIHJldmlzaW9ucykgJT4lIAogIGdyb3VwX2J5KHVzZXIsIHRhbGspICU+JSAKICBzdW1tYXJpc2UocmV2aXNpb25zID0gc3VtKHJldmlzaW9ucykpICU+JSAKICBzcHJlYWQoa2V5ID0gdGFsaywgCiAgICAgICAgIHZhbHVlID0gcmV2aXNpb25zLCAKICAgICAgICAgZmlsbCA9IDApCnVzZXJzRnJhbWUgPC0gbGVmdF9qb2luKHVzZXJzRnJhbWUsIHVzZXJSZXZzLCAKICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAndXNlcicpCnVzZXJzRnJhbWUgPC0gdXNlcnNGcmFtZVtjb21wbGV0ZS5jYXNlcyh1c2Vyc0ZyYW1lKSwgXQp1RnJhbWUgPC0gdXNlcnNGcmFtZSAlPiUgCiAgc2VsZWN0KFlNLCBkaXNjdXNzaW9uLCBlZGl0KSAlPiUgCiAgZ3JvdXBfYnkoWU0pICU+JSAKICBzdW1tYXJpc2UoZWRpdCA9IHN1bShlZGl0KSwgZGlzY3Vzc2lvbiA9IHN1bShkaXNjdXNzaW9uKSkKdUZyYW1lIDwtIHVGcmFtZSAlPiUgCiAgZ2F0aGVyKGtleSA9ICJBY3Rpdml0eSIsCiAgICAgICAgIHZhbHVlID0gIlJldmlzaW9ucyIsCiAgICAgICAgIGMoJ2VkaXQnLCAnZGlzY3Vzc2lvbicpKQpnZ3Bsb3QoZGF0YSA9IHVGcmFtZSwgCiAgICAgICBhZXMoeCA9IFlNLCB5ID0gbG9nMTAoUmV2aXNpb25zKSwgCiAgICAgICAgICAgY29sb3IgPSBBY3Rpdml0eSwgCiAgICAgICAgICAgZ3JvdXAgPSBBY3Rpdml0eSwgCiAgICAgICAgICAgbGFiZWwgPSBSZXZpc2lvbnMpKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMiwgY29sb3IgPSAiYmxhY2siLCBzZWdtZW50LnNpemUJPSAuMikgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKyBnZW9tX3BhdGgoc2l6ZSA9IC4yNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgZ2d0aXRsZSgibG9nKFJldmlzaW9ucykgcGVyIEFjdGl2aXR5OiBzaW5jZSBOb3ZlbWJlciAyMDEyIikgKwogIHhsYWIoIlllYXItTW9udGgiKSArIHlsYWIoImxvZzEwKFJldmlzaW9ucykiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gNiwgaGp1c3QgPSAxLCBhbmdsZSA9IDkwKSkgKyAKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGhqdXN0ID0gMSkpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxMSkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKIyMjIFE0LjMgVGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHJldmlzaW9ucyBhbmQgcGFydGljaXBhdGlvbnMgaW4gZGlzY3Vzc2lvbnMKCkJ5IGV5ZS1iYWxsaW5nIG9ubHk6IHRoZXJlIHNlZW0gdG8gYmUgYW4gYXBwcm94aW1hdGUgYW5kIHdlYWsgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBudW1iZXIgb2YgcmV2aXNpb25zIG1hZGUgb24gdGhlICJtYWluIiBhbmQgInRhbGsiIG5hbWVzcGFjZXMgKG9uY2UgYWdhaW46IGJ5ICJtYWluIiBuYW1lc3BhY2VzIHdlIG1lYW4gbm90IG9ubHkgSXRlbXMsIGJ1dCBhbGwgbmFtZXNwYWNlcyB0aGF0IGFyZSBub3QgInRhbGsiIG5hbWVzcGFjZXMpLiBBZ2FpbiwgZWFjaCBwb2ludCByZXByZXNlbnRzIGEgcGFydGljdWxhciBtb250aCBzaW5jZSBOb3ZlbWJlciAyMDEyIGluIHdoaWNoIHRoZSB1c2VycyB3aG9zZSBkYXRhIGFyZSB0YWtlbiBpbnRvIGNvbnNpZGVyYXRpb24gaGF2ZSByZWdpc3RlcmVkLCBleGNlcHQgZm9yIHRoZSBkYXRhIGFyZSBub3cgcmVwcmVzZW50ZWQgb24gYSBzY2F0dGVyZ3JhbS4KCmBgYHtyIGVjaG8gPSBUfQpwRnJhbWUgPC0gdUZyYW1lICU+JSAKICBzcHJlYWQoa2V5ID0gQWN0aXZpdHksIAogICAgICAgICB2YWx1ZSA9IFJldmlzaW9ucykKZ2dwbG90KGRhdGEgPSBwRnJhbWUsIAogICAgICAgYWVzKHggPSBlZGl0LCB5ID0gZGlzY3Vzc2lvbikpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArICAKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSwgc2l6ZSA9IC4yNSkgKyAKICBnZ3RpdGxlKCJFZGl0cyB2cy4gRGlzY3Vzc2lvbnM6IHNpbmNlIE5vdmVtYmVyIDIwMTIiKSArCiAgeWxhYigiRGlzY3Vzc2lvbnMiKSArIHhsYWIoIkVkaXRzIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGhqdXN0ID0gMSwgYW5nbGUgPSA5MCkpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBoanVzdCA9IDEpKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTEpKQpgYGAKCmBgYHtyIGVjaG8gPSBUfQptb2RlbCA8LSBsbShwRnJhbWUkZGlzY3Vzc2lvbiB+IHBGcmFtZSRlZGl0KQpzdW1tYXJ5KG1vZGVsKQpgYGAKCldlIGNhbiBleHBsYWluIHNvbWUgYDc2JWAgb2YgdmFyaWFuY2UgaW4gcmV2aXNpb25zIG1hZGUgb24gInRhbGsiIG5hbWVzcGFjZXMgZnJvbSByZXZpc2lvbnMgbWFkZSBvbiB0aGUgIm1haW4iIG5hbWVzcGFjZXMuIEhvd2V2ZXIsIHRoZSBhc3N1bXB0aW9ucyBvZiB0aGUgbGluZWFyIG1vZGVsIHdlcmUgbm90IHRlc3RlZCAobm90IHRvIG1lbnRpb24gdGhhdCB3ZSBhcmUgZGVhbGluZyB3aXRoIGNvdW50IGRhdGEgaGVyZSwgc28gUG9pc3NvbiB3b3VsZCBiZSBtb3JlIGFwcHJvcHJpYXRlKSwgbmVpdGhlciBhbnkgb3V0bGllcnMgKGFuZCB0aGVyZSBhcmUgc29tZSBvYnZpb3VzIG91dGxpZXJzIGluIHRoaXMgYW5hbHlzaXMsIGMuZi4gdGhlIHNjYXR0ZXJncmFtKSB3ZXJlIHJlbW92ZWQsIHNvIHRoaXMgcmVzdWx0IHNob3VsZCBiZSB0YWtlbiB3aXRoIGEgZ3JhaW4gb2Ygc2FsdCAoYXQgbGVhc3QpLgoKCgoKCgoK