Feedback should be send to goran.milovanovic_ext@wikimedia.de.

knitr::opts_chunk$set(fig.width = 14, fig.height = 10) 

0. Task Description

The task is described in the respective Phabricator ticket by Jan Dittrich:

User Story :: As a user researcher, I want to know how our user base is composed so I can evaluate if survey and interview data has a bias compared to it and to see basic patterns that I might want to further explore.

Information needs:

I imagine the following data to be useful (all can be aggregates): I imagine this should not be a graphana board but a report (ideally: RMarkdown or Jupyter Notebook)

  • 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)
  • 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)
  • How many of those users do participate in discussions? (mosaic plot, y=bins of edit count, x= bins of discussion participation)
  • How many new users do we have each month? – And of them, how many do edit how much? – And of them, how many do participate in discussions?

Or in general, how is the relation between:

  • Edit count and: Discussion participation (possibly split by item and property discussions vs. other project discussions), adding values, correcting values, adding references, adding qualifiers

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