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

The campaign is run from 1. January 2018 to N January 2018.

knitr::opts_knit$set(root.dir = '/home/goransm/Work/___DataKolektiv/Projects/WikimediaDEU/_WMDE_Projects/_misc/NewEditors_Team/Thank_You_Campaign_2018/_dailyUpdate/')

0. Data Acquisiton

NOTE: the Data Acquisition code chunk is not fully reproducible from this Report. The data are collected by running the script ThankYou_2018_Production_SQL.R on stat1005.eqiad.wmnet, collecting the data as .tsv and .csv files, copying manually, and processing locally. Run from stat1005 stat box by executing Rscript /home/goransm/RScripts/WMDE_Campaigns/ThankYou2018/ThankYou_2018_Production_SQL.R.

### --- from stat1005: Thank You 2018 Banner Campaign
### --- production script: fetch the campaign data sets

### --- Campaign Details: 
# - estimated start: 1st January 2018 (+/- 2 days)
# - estimated duration: 6 to 10 days
# - Reporting should start on 2nd January 2018. 
# - The report must include any activity from the beginning of the campaign. 
# - The estimated start will be 1st January 2018.

# - Guided Tour names
# - (The training modules include 2 new guided tours):
# - ?tour=diskutieren
# - ?tour=seimutig

### --- Training Modules Schema: 
### --- https://meta.wikimedia.org/wiki/User:Stefan_Schneider_(WMDE)/dashboard_libraries/wikipedia-kurse.json
### --- the slug field is relevant for tracking

### --- Setup
library(dplyr)
library(tidyr)
library(stringr)
library(data.table)

### --- Directories
bannerImpressionsDir <- '/home/goransm/RScripts/WMDE_Campaigns/ThankYou2018/BannerImpressions'
bannerClicksDir <- '/home/goransm/RScripts/WMDE_Campaigns/ThankYou2018/BannerClicks'
dailyUpdateDir <- '/home/goransm/RScripts/WMDE_Campaigns/ThankYou2018/ThankYou2018_DailyUpdate' 

### --- Campaign time range
startDate <- '2018-01-02'
endDate <- '2018-01-08'

### ------------------------------------------------------------
### --- S1. Banner Impression Data
### ------------------------------------------------------------

# - campaign tag
# - Name: bt1, ?campaign=wmde_etc2017_bt1

### --- loop over date range, create query, fetch, and store
dateRange <- seq.POSIXt(from = as.POSIXlt(startDate, tz = "CET"),
                        to = as.POSIXlt(endDate, tz = "CET"),
                        by = 'hour')
dateRange <- dateRange[-length(dateRange)]
cetDateRange <- as.character(dateRange)
cetDateRange <- sapply(cetDateRange, function(x) {
  strsplit(x, split = " ", fixed = T)[[1]][1]
})
names(dateRange) <- cetDateRange
dateRange <- as.POSIXlt(dateRange, tz = "UTC")
# - up to the campaign end:
endCampaign <- as.POSIXlt(endDate, tz = "UTC")
w <- which(dateRange > endCampaign)
if (length(w) > 0) {
  dateRange <- dateRange[-w]
}
dR <- list()
for (i in 1:length(dateRange)) {
  dR[[i]] <- data.frame(
    cetName = names(dateRange[i]),
    utcYear = year(dateRange[i]),
    utcMonth = month(dateRange[i]),
    utcDay = mday(dateRange[i]),
    utcHour = hour(dateRange[i])
  )
}
dR <- rbindlist(dR)
dR <- dR %>%
  group_by(cetName, utcYear, utcMonth, utcDay) %>%
  summarise(utcHour = paste("hour=", utcHour, collapse = " OR ", sep = ""))

### ------------------------------------------------------------
### --- S2. Banner Landing Page Data
### ------------------------------------------------------------

# - landing page link including the appropriate campaign tag
# - Link:https://de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/LerneWikipedia?campaign=wmde_etc2017_bt1

# - set bannerClicksDir
setwd(bannerClicksDir)

for (i in 1:length(unique(dR$cetName))) {
  
  wCetName <- which(dR$cetName %in% unique(dR$cetName)[i])
  
  for (j in 1:length(wCetName)) {
    
    # - construct HiveQL query:
    y <- dR$utcYear[wCetName[j]]
    m <- dR$utcMonth[wCetName[j]]
    d <- dR$utcDay[wCetName[j]]
    hour <- dR$utcHour[wCetName[j]]
    q <- paste(
      "USE wmf;
      SELECT uri_path, uri_query, referer FROM webrequest
      WHERE uri_host = 'de.wikipedia.org'
      AND uri_path = '/wiki/Wikipedia:Wikimedia_Deutschland/LerneWikipedia' 
      AND year = ", y,
      " AND month = ", m,
      " AND day = ", d,
      " AND (", hour, ");",
      sep = "")
    # - write hql
    write(q, 'thankyou2018_BannerClicks.hql')
    # - prepare output file:
    fileName <- "thankyou2018_BannerClicks_"
    fileName <- paste0(fileName,
                       as.character(unique(dR$cetName)[i]),
                       "_", j,
                       ".tsv")
    fileName <- paste0(bannerClicksDir, "/", fileName)
    # - execute hql script:
    hiveArgs <-
      'beeline -f'
    hiveInput <- paste0('thankyou2018_BannerClicks.hql > ',
                        fileName)
    # - command:
    hiveCommand <- paste(hiveArgs, hiveInput)
    system(command = hiveCommand, wait = TRUE)
    
  }
  
}

### --- Wrangle this dataset:

### --- Banner tags:
campaignBanner <- 'wmde_etc2017_bt1'

### --- Dataset:
# - count non-empty files:
c <- 0
lF <- list.files()
lF <- lF[grepl('.tsv', lF, fixed = T)]
lF <- lF[grepl('Clicks', lF, fixed = T)]
dataSet <- list()
for (i in 1:length(lF)) {
  dS <- readLines(lF[i], n = -1)
  timeStamp <- strsplit(lF[i], split = "_")[[1]][3]
  bannerClicks <- sum(grepl(campaignBanner, dS, fixed = T))
  dataSet[[i]] <- data.frame(timestamp = timeStamp,
                             bannerClicks = bannerClicks,
                             stringsAsFactors = F)
}
dataSet <- rbindlist(dataSet)

### --- store BannerClicksPageViews_Update.csv
setwd(dailyUpdateDir)
write.csv(dataSet, file = "thankyou2018_BannerClicksPageViews_Update.csv")

### --- SQL
startDate <- '2018-01-01'

### ------------------------------------------------------------
### --- S3. User Registration Data
### ------------------------------------------------------------

# - NOTE: UTC timestamps - adjustment for CE(S)T introduced. 
# - ServerSideAccountCreation_5487345
qCommand <- paste("mysql --defaults-file=/etc/mysql/conf.d/analytics-research-client.cnf -h analytics-slave.eqiad.wmnet -A -e \"select * from log.ServerSideAccountCreation_5487345 where ((webHost = 'de.wikipedia.org') and (timestamp >= ", gsub("-", "", startDate, fixed = T), "220000));\" > ",
            dailyUpdateDir, "/thankyou_2018_userRegistrations.tsv", sep = "")
system(command = qCommand, wait = TRUE)


### ------------------------------------------------------------
### --- S4. Guided Tours Data
### ------------------------------------------------------------

# - NOTE: UTC timestamps - adjustment for CE(S)T introduced. 
# - ServerSideAccountCreation_5487345
qCommand <- paste("mysql --defaults-file=/etc/mysql/conf.d/analytics-research-client.cnf -h analytics-slave.eqiad.wmnet -A -e \"select * from log.GuidedTourExited_8690566 where ((webHost = 'de.wikipedia.org') and (timestamp >= ", 
                  gsub("-", "", startDate, fixed = T), "220000));\" > ", 
                  dailyUpdateDir, "/thankyou_2018_guidedTours.tsv", sep = "")
system(command = qCommand, wait = TRUE)

### ------------------------------------------------------------
### --- S5. User Edit Data
### ------------------------------------------------------------

# - get user IDs from registered:
lF <- list.files()
lF <- lF[grepl('userRegistrations', lF, fixed = T)]
userReg <- read.table(lF, 
                      quote = "",
                      sep = "\t",
                      header = T,
                      check.names = F,
                      stringsAsFactors = F)
userReg <- userReg %>% 
  dplyr::select(event_userId, event_isSelfMade, event_campaign) %>% 
  filter(event_isSelfMade == 1) %>% 
  filter(event_campaign %in% "wmde_etc2017_bt1")
# - uids:
uid <- userReg$event_userId
# - sql query
sqlQuery <- paste('SELECT COUNT(*) as edits, rev_user FROM revision WHERE rev_user IN (',
                  paste(uid, collapse = ", "),
                  ') GROUP BY rev_user;',
                  sep = "")
mySqlCommand <- paste('mysql -h analytics-store.eqiad.wmnet dewiki -e ',
                      paste('"', sqlQuery, '" > ', sep = ""), 
                      dailyUpdateDir, '/thankyou_2018_userEdits.tsv', sep = "")
system(command = mySqlCommand, 
       wait = TRUE)

### ------------------------------------------------------------
### --- S6. Training Module Data
### ------------------------------------------------------------

1A. Campaign Banner Impressions

1A. 1 The data set

# - report: current update
print(paste0("Current update: ", as.character(Sys.time())))
[1] "Current update: 2018-01-25 15:51:16"
bannerImpressions <- read.csv('thankyouBannerImpressions.csv',
                              row.names = 1,
                              header = T,
                              stringsAsFactors = F) %>% 
  gather(key =  Banner,
         value = Views,
         B17WMDE_thankyou_authors:B17WMDE_thankyou_authors_mob_B) %>% 
  group_by(timeStamp, Banner) %>% 
  summarise(Views = sum(Views))
knitr::kable(bannerImpressions, format = "html") %>% 
  kable_styling(full_width = F, position = "left")
timeStamp Banner Views
2018-01-01 B17WMDE_thankyou_authors 257533
2018-01-01 B17WMDE_thankyou_authors_B 128423
2018-01-01 B17WMDE_thankyou_authors_mob_A 0
2018-01-01 B17WMDE_thankyou_authors_mob_B 0
2018-01-01 B17WMDE_thankyou_authors_pad_A 0
2018-01-01 B17WMDE_thankyou_authors_pad_B 0
2018-01-02 B17WMDE_thankyou_authors 741533
2018-01-02 B17WMDE_thankyou_authors_B 370737
2018-01-02 B17WMDE_thankyou_authors_mob_A 0
2018-01-02 B17WMDE_thankyou_authors_mob_B 3
2018-01-02 B17WMDE_thankyou_authors_pad_A 0
2018-01-02 B17WMDE_thankyou_authors_pad_B 0
2018-01-03 B17WMDE_thankyou_authors 824290
2018-01-03 B17WMDE_thankyou_authors_B 410722
2018-01-03 B17WMDE_thankyou_authors_mob_A 0
2018-01-03 B17WMDE_thankyou_authors_mob_B 0
2018-01-03 B17WMDE_thankyou_authors_pad_A 0
2018-01-03 B17WMDE_thankyou_authors_pad_B 0
2018-01-04 B17WMDE_thankyou_authors 818331
2018-01-04 B17WMDE_thankyou_authors_B 409420
2018-01-04 B17WMDE_thankyou_authors_mob_A 0
2018-01-04 B17WMDE_thankyou_authors_mob_B 0
2018-01-04 B17WMDE_thankyou_authors_pad_A 0
2018-01-04 B17WMDE_thankyou_authors_pad_B 0
2018-01-05 B17WMDE_thankyou_authors 750962
2018-01-05 B17WMDE_thankyou_authors_B 375077
2018-01-05 B17WMDE_thankyou_authors_mob_A 0
2018-01-05 B17WMDE_thankyou_authors_mob_B 2
2018-01-05 B17WMDE_thankyou_authors_pad_A 0
2018-01-05 B17WMDE_thankyou_authors_pad_B 0
2018-01-06 B17WMDE_thankyou_authors 586811
2018-01-06 B17WMDE_thankyou_authors_B 293237
2018-01-06 B17WMDE_thankyou_authors_mob_A 0
2018-01-06 B17WMDE_thankyou_authors_mob_B 0
2018-01-06 B17WMDE_thankyou_authors_pad_A 0
2018-01-06 B17WMDE_thankyou_authors_pad_B 0
2018-01-07 B17WMDE_thankyou_authors 706186
2018-01-07 B17WMDE_thankyou_authors_B 352536
2018-01-07 B17WMDE_thankyou_authors_mob_A 0
2018-01-07 B17WMDE_thankyou_authors_mob_B 0
2018-01-07 B17WMDE_thankyou_authors_pad_A 0
2018-01-07 B17WMDE_thankyou_authors_pad_B 0
2018-01-08 B17WMDE_thankyou_authors 920162
2018-01-08 B17WMDE_thankyou_authors_B 460469
2018-01-08 B17WMDE_thankyou_authors_mob_A 0
2018-01-08 B17WMDE_thankyou_authors_mob_B 0
2018-01-08 B17WMDE_thankyou_authors_pad_A 0
2018-01-08 B17WMDE_thankyou_authors_pad_B 0
2018-01-09 B17WMDE_thankyou_authors 988107
2018-01-09 B17WMDE_thankyou_authors_B 495139
2018-01-09 B17WMDE_thankyou_authors_mob_A 0
2018-01-09 B17WMDE_thankyou_authors_mob_B 0
2018-01-09 B17WMDE_thankyou_authors_pad_A 0
2018-01-09 B17WMDE_thankyou_authors_pad_B 0
2018-01-10 B17WMDE_thankyou_authors 990820
2018-01-10 B17WMDE_thankyou_authors_B 495235
2018-01-10 B17WMDE_thankyou_authors_mob_A 0
2018-01-10 B17WMDE_thankyou_authors_mob_B 0
2018-01-10 B17WMDE_thankyou_authors_pad_A 0
2018-01-10 B17WMDE_thankyou_authors_pad_B 0
2018-01-11 B17WMDE_thankyou_authors 986539
2018-01-11 B17WMDE_thankyou_authors_B 493215
2018-01-11 B17WMDE_thankyou_authors_mob_A 0
2018-01-11 B17WMDE_thankyou_authors_mob_B 1
2018-01-11 B17WMDE_thankyou_authors_pad_A 0
2018-01-11 B17WMDE_thankyou_authors_pad_B 0
2018-01-12 B17WMDE_thankyou_authors 852340
2018-01-12 B17WMDE_thankyou_authors_B 426957
2018-01-12 B17WMDE_thankyou_authors_mob_A 0
2018-01-12 B17WMDE_thankyou_authors_mob_B 0
2018-01-12 B17WMDE_thankyou_authors_pad_A 0
2018-01-12 B17WMDE_thankyou_authors_pad_B 0
2018-01-13 B17WMDE_thankyou_authors 628561
2018-01-13 B17WMDE_thankyou_authors_B 313426
2018-01-13 B17WMDE_thankyou_authors_mob_A 0
2018-01-13 B17WMDE_thankyou_authors_mob_B 0
2018-01-13 B17WMDE_thankyou_authors_pad_A 0
2018-01-13 B17WMDE_thankyou_authors_pad_B 0
2018-01-14 B17WMDE_thankyou_authors 779314
2018-01-14 B17WMDE_thankyou_authors_B 389017
2018-01-14 B17WMDE_thankyou_authors_mob_A 0
2018-01-14 B17WMDE_thankyou_authors_mob_B 0
2018-01-14 B17WMDE_thankyou_authors_pad_A 0
2018-01-14 B17WMDE_thankyou_authors_pad_B 0

1A. 2 Chart

ggplot(bannerImpressions, aes(x = timeStamp,
                    y = Views,
                    color = Banner,
                    label = Views)) +
  geom_line(aes(group = Banner), size = .25) +
  geom_point(size = 1.5) +
  geom_point(size = 1, color = "white") + 
  scale_y_continuous(labels = scales::comma) +
  ggtitle('Thank You 2018:\nOverview of BannerImpressions') +
  ylab("Impressions") +
  geom_text_repel(size = 3) +
  theme_minimal() + 
  theme(axis.text.x = element_text(angle = 90, size = 8, hjust = 1)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) +
  theme(panel.grid.major.x = element_blank()) +
  theme(panel.grid.minor.x = element_blank()) +
  theme(panel.background = element_blank())

1B. Campaign Pageviews (Banner Clicks)

1B. 1 The data set

pageviews <- read.csv('thankyou2018_BannerClicksPageViews_Update.csv',
                      row.names = 1,
                      header = T,
                      stringsAsFactors = F)
pageviews <- pageviews %>% 
  group_by(timestamp) %>% 
  summarise(Pageviews = sum(bannerClicks))
knitr::kable(pageviews, format = "html") %>% 
  kable_styling(full_width = F, position = "left")
timestamp Pageviews
2018-01-01 533
2018-01-02 1188
2018-01-03 1269
2018-01-04 1148
2018-01-05 1122
2018-01-06 1148
2018-01-07 1242
2018-01-08 1150
2018-01-09 1254
2018-01-10 1347
2018-01-11 1773
2018-01-12 1214
2018-01-13 1201
2018-01-14 1492

1B. 2 Chart

ggplot(pageviews, aes(x = timestamp,
                    y = Pageviews,
                    label = Pageviews)) +
  geom_bar(stat = "identity", 
           position = "dodge", 
           width = .15, 
           fill = "white", 
           color = "darkblue") +
  ggtitle('Thank You 2018:\nOverview of Landing Pageviews') +
  geom_label(size = 3) +
  theme_minimal() + 
  theme(axis.text.x = element_text(angle = 90, size = 8, hjust = 1)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) +
  theme(panel.grid.major.x = element_blank()) +
  theme(panel.grid.minor.x = element_blank()) +
  theme(panel.background = element_blank())

2. Campaign User Registrations

2. 1 The data set

userReg <- read.delim('thankyou_2018_userRegistrations.tsv',
                      sep = "\t",
                      quote = "",
                      header = T,
                      # row.names = 1,
                      stringsAsFactors = F)
userReg$timestamp <- as.character(userReg$timestamp)
userReg$timestamp <- sapply(userReg$timestamp, function(x) {
  y <- substr(x, 1, 4)
  m <- substr(x, 5, 6)
  d <- substr(x, 7, 8)
  part1Date <- paste(y, m, d, sep = "-")
  hr <- substr(x, 9, 10)
  mi <- substr(x, 11, 12)
  se <- substr(x, 13, 14)
  part2Date <- paste(hr, mi, se, sep = ":")
  paste(part1Date, part2Date, sep = " ")
})
userReg$timestamp <- as.POSIXct(userReg$timestamp, tz = "UTC")
timeDiff <- 
  as.POSIXct(as.character(Sys.time()), tz = "UTC") - as.POSIXct(as.character(Sys.time()), tz = "Europe/Berlin")
userReg$timestamp <- as.character(userReg$timestamp + timeDiff)
userReg$timestamp <- sapply(userReg$timestamp, function(x) {
  y <- substr(x, 1, 4)
  m <- substr(x, 6, 7)
  d <- substr(x, 9, 10) 
  paste(y, m, d, sep = "-")
})
userReg <- userReg %>%
  filter(event_campaign %in% 'wmde_etc2017_bt1') %>% 
  group_by(timestamp) %>% 
  summarise(Registrations = n())
knitr::kable(userReg, format = "html") %>% 
  kable_styling(full_width = F, position = "left")
timestamp Registrations
2018-01-01 1
2018-01-02 8
2018-01-03 15
2018-01-04 13
2018-01-05 5
2018-01-06 6
2018-01-07 12
2018-01-08 6
2018-01-09 9
2018-01-10 7
2018-01-11 15
2018-01-12 9
2018-01-13 3
2018-01-14 11
2018-01-15 1
print(paste0("Total number of registered users: ", sum(userReg$Registrations)))
[1] "Total number of registered users: 121"

2. 2 Chart

ggplot(userReg, aes(x = timestamp,
                    y = Registrations,
                    label = Registrations)) +
  geom_bar(stat = "identity", 
           position = "dodge", 
           width = .15, 
           fill = "white", 
           color = "darkblue") +
  ggtitle('Thank You 2018:\nOverview of User Registrations') +
  geom_label(size = 3) +
  theme_minimal() + 
  theme(axis.text.x = element_text(angle = 90, size = 8, hjust = 1)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) +
  theme(panel.grid.major.x = element_blank()) +
  theme(panel.grid.minor.x = element_blank()) +
  theme(panel.background = element_blank())

3. Campaign User Edits

3. 1 The data set

userEdits <- readLines('thankyou_2018_userEdits.tsv', n = -1)
if (length(userEdits) >= 1) {
  userEdits <- read.delim('thankyou_2018_userEdits.tsv',
                          sep = "\t",
                          header = T,
                          stringsAsFactors = F)
  # - report
  print(paste0(sum(userEdits$edits), " edits were made by the campaign registered users thus far."))
} else {
  print("There are currently no user edits from this campaign.")
}
[1] "135 edits were made by the campaign registered users thus far."

3. 2 Chart

userEditsDist <- as.data.frame(table(userEdits$edits))
colnames(userEditsDist) <- c('Number of Edits', 'Number of Users')
ggplot(userEditsDist, aes(x = `Number of Edits`,
                    y = `Number of Users`,
                    label = `Number of Users`)) +
  geom_bar(stat = "identity", 
           position = "dodge", 
           width = .15, 
           fill = "darkorange", 
           color = "darkorange") +
  ggtitle('Thank You 2018:\nHow many users made a particular number of edits') +
  geom_label(size = 3) +
  theme_minimal() + 
  theme(axis.text.x = element_text(angle = 0, size = 8, hjust = 1)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) +
  theme(panel.grid.major.x = element_blank()) +
  theme(panel.grid.minor.x = element_blank()) +
  theme(panel.background = element_blank())

4. Campaign Guided Tours

4. 1 The data set: Guided Tour Exit Steps

Campaign guided tours: diskutieren and seimutig.

tours <- c('diskutieren', 'seimutig')
# setwd('/home/goransm/Work/___DataKolektiv/Projects/WikimediaDEU/_WMDE_Projects/_misc/NewEditors_Team/Thank_You_Campaign_2018/_dailyUpdate/')
gTours <- read.delim('thankyou_2018_guidedTours.tsv',
                     sep = "\t",
                     header = T,
                     row.names = 1,
                     stringsAsFactors = F)
gTours$timestamp <- as.character(gTours$timestamp)
gTours$timestamp <- sapply(gTours$timestamp, function(x) {
  y <- substr(x, 1, 4)
  m <- substr(x, 5, 6)
  d <- substr(x, 7, 8)
  part1Date <- paste(y, m, d, sep = "-")
  hr <- substr(x, 9, 10)
  mi <- substr(x, 11, 12)
  se <- substr(x, 13, 14)
  part2Date <- paste(hr, mi, se, sep = ":")
  paste(part1Date, part2Date, sep = " ")
})
gTours$timestamp <- as.POSIXct(gTours$timestamp, tz = "UTC")
timeDiff <- 
  as.POSIXct(as.character(Sys.time()), tz = "UTC") - as.POSIXct(as.character(Sys.time()), tz = "Europe/Berlin")
gTours$timestamp <- as.character(gTours$timestamp + timeDiff)
gTours$timestamp <- sapply(gTours$timestamp, function(x) {
  y <- substr(x, 1, 4)
  m <- substr(x, 6, 7)
  d <- substr(x, 9, 10) 
  paste(y, m, d, sep = "-")
})
# - anonymize event_userId
eventUserId <- setdiff(unique(gTours$event_userId), 0)
an_userId <- character(length(eventUserId))
for (i in 1:length(an_userId)) {
  id <- round(runif(1, 1, 10e6))
  while (id %in% an_userId) {
    id <- round(runif(1, 1, 10e6))
  }
  an_userId[i] <- id
}
an_userId <- paste0("u_", an_userId)
gTours$an_userId <- sapply(gTours$event_userId, function(x) {
  w <- which(eventUserId %in% x)
  if (length(w) > 0) {
    an_userId[w]
  } else {
    x
  }
})
# - look up for the campaign guided tours
w <- which(gTours$event_tour %in% tours)
if (length(w) > 0) {
  gTours <- gTours[w, ]
  gTours <- gTours %>% 
    filter(event_userId != 0) %>% 
    select(timestamp, event_tour, event_step, an_userId)
  knitr::kable(gTours, format = "html") %>% 
  kable_styling(full_width = F, position = "left")
} else {
  print("There are currently no data on guided tours from this campaign.")
}
timestamp event_tour event_step an_userId
2018-01-03 diskutieren returnToTraining u_9719807
2018-01-03 seimutig returnToTraining u_9719807
2018-01-04 seimutig boldness u_126071
2018-01-04 seimutig positionCursor u_126071
2018-01-04 diskutieren returnToTraining u_126071
2018-01-05 diskutieren saveReply u_5424461
2018-01-04 seimutig returnToTraining u_126071
2018-01-04 seimutig editButtonCitation u_126071
2018-01-06 seimutig editButton u_514354
2018-01-06 seimutig boldness u_514354
2018-01-06 diskutieren firstMessage u_514354
2018-01-08 seimutig editBoldness u_4937020
2018-01-08 seimutig editSummary u_4937020
2018-01-08 diskutieren returnToTraining u_4937020
2018-01-10 seimutig editButton u_475981
2018-01-10 seimutig editButton u_475981
2018-01-10 seimutig editBoldness u_475981
2018-01-10 seimutig editSummary u_475981
2018-01-10 seimutig editButtonCitation u_475981
2018-01-10 seimutig saveCitation u_475981
2018-01-10 seimutig editButton u_475981
2018-01-10 diskutieren returnToTraining u_475981
2018-01-11 seimutig editBoldness u_445447
2018-01-11 seimutig boldness u_445447
2018-01-11 seimutig insertCitation u_445447
2018-01-11 seimutig anyEdit u_445447
2018-01-11 diskutieren returnToTraining u_445447
2018-01-11 seimutig anyEdit u_5703010

4. 2 Unique users in Guided Tours

Campaign guided tours: diskutieren and seimutig.

gTours %>% 
  select(event_tour) %>% 
  group_by(event_tour) %>% 
  summarise(`Unique users` = n()) %>% 
  knitr::kable(format = "html") %>% 
  kable_styling(full_width = F, position = "left")
event_tour Unique users
diskutieren 7
seimutig 21

5 Training Module

trainData <- read.csv('wmde_training_data.csv')
# - remove users: Stefan Schneider (WMDE), Sage (Wiki Ed)
wR <- which(trainData$username %in% c('Stefan Schneider (WMDE)', 'Sage (Wiki Ed)'))
trainData <- trainData[-wR, ]
# - get user IDs from registered:
lF <- list.files()
lF <- lF[grepl('userRegistrations', lF, fixed = T)]
userData <- read.table(lF, 
                      quote = "",
                      sep = "\t",
                      header = T,
                      check.names = F,
                      stringsAsFactors = F)
edData <- read.delim('thankyou_2018_userEdits.tsv',
                     sep = "\t",
                     header = T,
                     stringsAsFactors = F)
# - which users in the Training Module data set have registered via Thank You 2018:
wRegCampaign <- which(trainData$username %in% userData$event_userName)
trainData <- trainData[wRegCampaign, ]
completedTraining <- sapply(1:dim(trainData)[1], function(x) {
  w <- which(trainData$username %in% trainData$username[x])
  ct <- ifelse(
    trainData$last_slide_completed[w] %in% c('relevanz-quiz-fortsetzung', 'das-wars', 'fertig'), T, F)
  ifelse(sum(ct) >= 1, T, F)
})
trainData$completed_training <- completedTraining
userData <- userData %>% 
  filter(event_campaign %in% 'wmde_etc2017_bt1' & event_isSelfMade) %>% 
  select(event_userId, event_userName)
completedTrainingData <- trainData %>%
  select(username, completed_training)
completedTrainingData <- completedTrainingData[!duplicated(completedTrainingData), ]
userData <- left_join(userData, completedTrainingData, by = c("event_userName" = "username"))
Column `event_userName`/`username` joining character vector and factor, coercing into character vector
userData$module_completion_date <- NA
userData <- left_join(userData, edData,
                      by = c('event_userId' = 'rev_user'))
userData$started_training <- ifelse(is.na(userData$completed_training), F, T)

5.1 Training Module Overview

How many registered users have started the Training Module?

print(paste0("Number of users who have started the Training Module is ", 
             sum(userData$started_training),
             " , which is ",
             round(sum(userData$started_training)/sum(userReg$Registrations)*100, 2),
             "% of registered users."
               ))
[1] "Number of users who have started the Training Module is 16 , which is 13.22% of registered users."

How many registered users have completed the Training Module?

print(paste0("Number of users who have completed the Training Module is ", 
             sum(userData$completed_training, na.rm = T),
             " , which is ",
             round(sum(userData$completed_training, na.rm = T)/sum(userReg$Registrations)*100, 2),
             "% of registered users."
               ))
[1] "Number of users who have completed the Training Module is 13 , which is 10.74% of registered users."

What are the completed slides per user Training Module?

slidesData <- trainData %>% 
  select(training_module, last_slide_completed) %>% 
  group_by(training_module, last_slide_completed) %>% 
  summarise(Count = n()) %>% 
  arrange(training_module, desc(Count))
colnames(slidesData)[3] <- c('N. users completed')
knitr::kable(slidesData, format = "html") %>%
  kable_styling(full_width = F, position = "left")
training_module last_slide_completed N. users completed
artikel-bewerten fertig 3
editieren-basiswissen das-wars 2
editieren-basiswissen versuche-es 1
wikipedia-basiswissen relevanz-quiz-fortsetzung 14
wikipedia-basiswissen wikipedia-ist-frei 2
wikipedia-basiswissen urheberrecht-und-plagiate 1
ggplot(slidesData, aes(x = last_slide_completed,
                       y = `N. users completed`,
                       color = training_module,
                       label = `N. users completed`)) +
  geom_line(aes(group = 1), size = .25) +
  geom_point(size = 1.5) +
  geom_point(size = 1, color = "white") + 
  scale_y_continuous(labels = scales::comma) +
  ggtitle('Thank You 2018:\nOverview of Training Modules') +
  ylab("No. Users") +
  geom_text_repel(size = 3) +
  facet_wrap(~training_module) +
  theme_minimal() + 
  theme(axis.text.x = element_text(angle = 90, size = 6.5, hjust = 1)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) +
  theme(panel.grid.major.x = element_blank()) +
  theme(panel.grid.minor.x = element_blank()) +
  theme(panel.background = element_blank())

Do users start editing after training modules? Is there a difference in users with completed and not-completed or not at all having taken the modules?

How many edits were made on behalf of those users who have started the Training Module vs. those who did not?

tAn <- userData %>% 
  select(started_training, edits) %>% 
  group_by(started_training) %>% 
  summarise(Users = n(), Edits = sum(edits, na.rm = T)) %>% 
  arrange(started_training, desc(Edits))
knitr::kable(tAn, format = "html") %>%
  kable_styling(full_width = F, position = "left")
started_training Users Edits
FALSE 105 125
TRUE 16 10
print(paste0("In total, the users who have started the Training Module made ", 
             sum(tAn$Edits[2], na.rm = T),
             " edits, which is ",
             round(sum(tAn$Edits[2], na.rm = T)/sum(tAn$Edits, na.rm = T)*100, 2),
             "% of all edits."
               ))
[1] "In total, the users who have started the Training Module made 10 edits, which is 7.41% of all edits."
print(paste0("On the other hand, the users who did not take the Training Module made ", 
             tAn$Edits[1],
             " edits, which is ",
             round(tAn$Edits[1]/sum(tAn$Edits)*100, 2),
             "% of all edits."
               ))
[1] "On the other hand, the users who did not take the Training Module made 125 edits, which is 92.59% of all edits."

How many edits were made on behalf of those users who have completed the Training Module vs. those who did not?

tAn <- userData %>% 
  select(completed_training, edits) %>% 
  group_by(completed_training) %>% 
  summarise(Users = n(), Edits = sum(edits, na.rm = T)) %>% 
  arrange(completed_training, desc(Edits))
knitr::kable(tAn, format = "html") %>%
  kable_styling(full_width = F, position = "left")
completed_training Users Edits
FALSE 3 0
TRUE 13 10
NA 105 125

Completion of the Training Module vs. number of edits

# - final slide for: wikipedia-basiswissen = relevanz-quiz-fortsetzung
# - final slide for: editieren-basiswissen = das-wars
# - final slide for: artikel-bewerten = fertig
# finalSlide <- c('relevanz-quiz-fortsetzung', 'das', 'fertig')
# names(finalSlide) <- c('wikipedia-basiswissen', 'editieren-basiswissen', 'relevanz-quiz-fortsetzung')
completedTrainingEdits <- userData %>% 
  select(completed_training, edits) %>% 
  group_by(completed_training) %>% 
  summarise(Users = n(), Edits = sum(edits, na.rm = T))
knitr::kable(completedTrainingEdits, format = "html") %>%
  kable_styling(full_width = F, position = "left")
completed_training Users Edits
FALSE 3 0
TRUE 13 10
NA 105 125

Let’s now take a look at the number of users who (a) made any edits at all, or(b) have reached their 10th edit, in these two groups:

userData$Edits <- (userData$edits > 0)
userData$Edits10 <- (userData$edits >= 10)
completedTrainingEdits <- userData %>% 
  select(completed_training, Edits10) %>%
  group_by(completed_training) %>% 
  summarise(Users = n(), Edits10 = sum(Edits10, na.rm = T))
knitr::kable(completedTrainingEdits, format = "html") %>%
  kable_styling(full_width = F, position = "left")
completed_training Users Edits10
FALSE 3 0
TRUE 13 0
NA 105 2
LS0tCnRpdGxlOiAnVGhhbmsgWW91IENhbXBhaWduIDIwMTg6IFJlcG9ydCcKYXV0aG9yOiAiR29yYW4gUy4gTWlsb3Zhbm92aWMsIERhdGEgQW5hbHlzdCwgV01ERSIKZGF0ZTogIkphbnVhcnksIDIwMTgiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0aGVtZTogc2ltcGxleAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDUKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQotLS0KCgoqKkZlZWRiYWNrKiogc2hvdWxkIGJlIHNlbmQgdG8gYGdvcmFuLm1pbG92YW5vdmljX2V4dEB3aWtpbWVkaWEuZGVgLiAKClRoZSBjYW1wYWlnbiBpcyBydW4gZnJvbSAxLiBKYW51YXJ5IDIwMTggdG8gTiBKYW51YXJ5IDIwMTguCgpgYGB7ciBzZXR1cH0Ka25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAnL2hvbWUvZ29yYW5zbS9Xb3JrL19fX0RhdGFLb2xla3Rpdi9Qcm9qZWN0cy9XaWtpbWVkaWFERVUvX1dNREVfUHJvamVjdHMvX21pc2MvTmV3RWRpdG9yc19UZWFtL1RoYW5rX1lvdV9DYW1wYWlnbl8yMDE4L19kYWlseVVwZGF0ZS8nKQpgYGAKCmBgYHtyLCBlY2hvID0gRiwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCByZXN1bHRzID0gJ2hpZGUnfQojICFkaWFnbm9zdGljcyBvZmYKIyMjIC0tLSBTZXR1cAprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQgPSA4KSAKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoRFQpCmxpYnJhcnkocmVzaGFwZTIpCmBgYAoKIyMgMC4gRGF0YSBBY3F1aXNpdG9uCgoqKk5PVEU6KiogdGhlIERhdGEgQWNxdWlzaXRpb24gY29kZSBjaHVuayBpcyBub3QgZnVsbHkgcmVwcm9kdWNpYmxlIGZyb20gdGhpcyBSZXBvcnQuIFRoZSBkYXRhIGFyZSBjb2xsZWN0ZWQgYnkgcnVubmluZyB0aGUgc2NyaXB0IGBUaGFua1lvdV8yMDE4X1Byb2R1Y3Rpb25fU1FMLlJgIG9uIHN0YXQxMDA1LmVxaWFkLndtbmV0LCBjb2xsZWN0aW5nIHRoZSBkYXRhIGFzIGAudHN2YCBhbmQgYC5jc3ZgIGZpbGVzLCBjb3B5aW5nIG1hbnVhbGx5LCBhbmQgcHJvY2Vzc2luZyBsb2NhbGx5LiBSdW4gZnJvbSBzdGF0MTAwNSBzdGF0IGJveCBieSBleGVjdXRpbmcgYFJzY3JpcHQgL2hvbWUvZ29yYW5zbS9SU2NyaXB0cy9XTURFX0NhbXBhaWducy9UaGFua1lvdTIwMTgvVGhhbmtZb3VfMjAxOF9Qcm9kdWN0aW9uX1NRTC5SYC4KCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZ9CgojIyMgLS0tIGZyb20gc3RhdDEwMDU6IFRoYW5rIFlvdSAyMDE4IEJhbm5lciBDYW1wYWlnbgojIyMgLS0tIHByb2R1Y3Rpb24gc2NyaXB0OiBmZXRjaCB0aGUgY2FtcGFpZ24gZGF0YSBzZXRzCgojIyMgLS0tIENhbXBhaWduIERldGFpbHM6IAojIC0gZXN0aW1hdGVkIHN0YXJ0OiAxc3QgSmFudWFyeSAyMDE4ICgrLy0gMiBkYXlzKQojIC0gZXN0aW1hdGVkIGR1cmF0aW9uOiA2IHRvIDEwIGRheXMKIyAtIFJlcG9ydGluZyBzaG91bGQgc3RhcnQgb24gMm5kIEphbnVhcnkgMjAxOC4gCiMgLSBUaGUgcmVwb3J0IG11c3QgaW5jbHVkZSBhbnkgYWN0aXZpdHkgZnJvbSB0aGUgYmVnaW5uaW5nIG9mIHRoZSBjYW1wYWlnbi4gCiMgLSBUaGUgZXN0aW1hdGVkIHN0YXJ0IHdpbGwgYmUgMXN0IEphbnVhcnkgMjAxOC4KCiMgLSBHdWlkZWQgVG91ciBuYW1lcwojIC0gKFRoZSB0cmFpbmluZyBtb2R1bGVzIGluY2x1ZGUgMiBuZXcgZ3VpZGVkIHRvdXJzKToKIyAtID90b3VyPWRpc2t1dGllcmVuCiMgLSA/dG91cj1zZWltdXRpZwoKIyMjIC0tLSBUcmFpbmluZyBNb2R1bGVzIFNjaGVtYTogCiMjIyAtLS0gaHR0cHM6Ly9tZXRhLndpa2ltZWRpYS5vcmcvd2lraS9Vc2VyOlN0ZWZhbl9TY2huZWlkZXJfKFdNREUpL2Rhc2hib2FyZF9saWJyYXJpZXMvd2lraXBlZGlhLWt1cnNlLmpzb24KIyMjIC0tLSB0aGUgc2x1ZyBmaWVsZCBpcyByZWxldmFudCBmb3IgdHJhY2tpbmcKCiMjIyAtLS0gU2V0dXAKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGRhdGEudGFibGUpCgojIyMgLS0tIERpcmVjdG9yaWVzCmJhbm5lckltcHJlc3Npb25zRGlyIDwtICcvaG9tZS9nb3JhbnNtL1JTY3JpcHRzL1dNREVfQ2FtcGFpZ25zL1RoYW5rWW91MjAxOC9CYW5uZXJJbXByZXNzaW9ucycKYmFubmVyQ2xpY2tzRGlyIDwtICcvaG9tZS9nb3JhbnNtL1JTY3JpcHRzL1dNREVfQ2FtcGFpZ25zL1RoYW5rWW91MjAxOC9CYW5uZXJDbGlja3MnCmRhaWx5VXBkYXRlRGlyIDwtICcvaG9tZS9nb3JhbnNtL1JTY3JpcHRzL1dNREVfQ2FtcGFpZ25zL1RoYW5rWW91MjAxOC9UaGFua1lvdTIwMThfRGFpbHlVcGRhdGUnIAoKIyMjIC0tLSBDYW1wYWlnbiB0aW1lIHJhbmdlCnN0YXJ0RGF0ZSA8LSAnMjAxOC0wMS0wMicKZW5kRGF0ZSA8LSAnMjAxOC0wMS0wOCcKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTMS4gQmFubmVyIEltcHJlc3Npb24gRGF0YQojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0gY2FtcGFpZ24gdGFnCiMgLSBOYW1lOiBidDEsID9jYW1wYWlnbj13bWRlX2V0YzIwMTdfYnQxCgojIyMgLS0tIGxvb3Agb3ZlciBkYXRlIHJhbmdlLCBjcmVhdGUgcXVlcnksIGZldGNoLCBhbmQgc3RvcmUKZGF0ZVJhbmdlIDwtIHNlcS5QT1NJWHQoZnJvbSA9IGFzLlBPU0lYbHQoc3RhcnREYXRlLCB0eiA9ICJDRVQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgdG8gPSBhcy5QT1NJWGx0KGVuZERhdGUsIHR6ID0gIkNFVCIpLAogICAgICAgICAgICAgICAgICAgICAgICBieSA9ICdob3VyJykKZGF0ZVJhbmdlIDwtIGRhdGVSYW5nZVstbGVuZ3RoKGRhdGVSYW5nZSldCmNldERhdGVSYW5nZSA8LSBhcy5jaGFyYWN0ZXIoZGF0ZVJhbmdlKQpjZXREYXRlUmFuZ2UgPC0gc2FwcGx5KGNldERhdGVSYW5nZSwgZnVuY3Rpb24oeCkgewogIHN0cnNwbGl0KHgsIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpW1sxXV1bMV0KfSkKbmFtZXMoZGF0ZVJhbmdlKSA8LSBjZXREYXRlUmFuZ2UKZGF0ZVJhbmdlIDwtIGFzLlBPU0lYbHQoZGF0ZVJhbmdlLCB0eiA9ICJVVEMiKQojIC0gdXAgdG8gdGhlIGNhbXBhaWduIGVuZDoKZW5kQ2FtcGFpZ24gPC0gYXMuUE9TSVhsdChlbmREYXRlLCB0eiA9ICJVVEMiKQp3IDwtIHdoaWNoKGRhdGVSYW5nZSA+IGVuZENhbXBhaWduKQppZiAobGVuZ3RoKHcpID4gMCkgewogIGRhdGVSYW5nZSA8LSBkYXRlUmFuZ2VbLXddCn0KZFIgPC0gbGlzdCgpCmZvciAoaSBpbiAxOmxlbmd0aChkYXRlUmFuZ2UpKSB7CiAgZFJbW2ldXSA8LSBkYXRhLmZyYW1lKAogICAgY2V0TmFtZSA9IG5hbWVzKGRhdGVSYW5nZVtpXSksCiAgICB1dGNZZWFyID0geWVhcihkYXRlUmFuZ2VbaV0pLAogICAgdXRjTW9udGggPSBtb250aChkYXRlUmFuZ2VbaV0pLAogICAgdXRjRGF5ID0gbWRheShkYXRlUmFuZ2VbaV0pLAogICAgdXRjSG91ciA9IGhvdXIoZGF0ZVJhbmdlW2ldKQogICkKfQpkUiA8LSByYmluZGxpc3QoZFIpCmRSIDwtIGRSICU+JQogIGdyb3VwX2J5KGNldE5hbWUsIHV0Y1llYXIsIHV0Y01vbnRoLCB1dGNEYXkpICU+JQogIHN1bW1hcmlzZSh1dGNIb3VyID0gcGFzdGUoImhvdXI9IiwgdXRjSG91ciwgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIiKSkKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTMi4gQmFubmVyIExhbmRpbmcgUGFnZSBEYXRhCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLSBsYW5kaW5nIHBhZ2UgbGluayBpbmNsdWRpbmcgdGhlIGFwcHJvcHJpYXRlIGNhbXBhaWduIHRhZwojIC0gTGluazpodHRwczovL2RlLndpa2lwZWRpYS5vcmcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0xlcm5lV2lraXBlZGlhP2NhbXBhaWduPXdtZGVfZXRjMjAxN19idDEKCiMgLSBzZXQgYmFubmVyQ2xpY2tzRGlyCnNldHdkKGJhbm5lckNsaWNrc0RpcikKCmZvciAoaSBpbiAxOmxlbmd0aCh1bmlxdWUoZFIkY2V0TmFtZSkpKSB7CiAgCiAgd0NldE5hbWUgPC0gd2hpY2goZFIkY2V0TmFtZSAlaW4lIHVuaXF1ZShkUiRjZXROYW1lKVtpXSkKICAKICBmb3IgKGogaW4gMTpsZW5ndGgod0NldE5hbWUpKSB7CiAgICAKICAgICMgLSBjb25zdHJ1Y3QgSGl2ZVFMIHF1ZXJ5OgogICAgeSA8LSBkUiR1dGNZZWFyW3dDZXROYW1lW2pdXQogICAgbSA8LSBkUiR1dGNNb250aFt3Q2V0TmFtZVtqXV0KICAgIGQgPC0gZFIkdXRjRGF5W3dDZXROYW1lW2pdXQogICAgaG91ciA8LSBkUiR1dGNIb3VyW3dDZXROYW1lW2pdXQogICAgcSA8LSBwYXN0ZSgKICAgICAgIlVTRSB3bWY7CiAgICAgIFNFTEVDVCB1cmlfcGF0aCwgdXJpX3F1ZXJ5LCByZWZlcmVyIEZST00gd2VicmVxdWVzdAogICAgICBXSEVSRSB1cmlfaG9zdCA9ICdkZS53aWtpcGVkaWEub3JnJwogICAgICBBTkQgdXJpX3BhdGggPSAnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9MZXJuZVdpa2lwZWRpYScgCiAgICAgIEFORCB5ZWFyID0gIiwgeSwKICAgICAgIiBBTkQgbW9udGggPSAiLCBtLAogICAgICAiIEFORCBkYXkgPSAiLCBkLAogICAgICAiIEFORCAoIiwgaG91ciwgIik7IiwKICAgICAgc2VwID0gIiIpCiAgICAjIC0gd3JpdGUgaHFsCiAgICB3cml0ZShxLCAndGhhbmt5b3UyMDE4X0Jhbm5lckNsaWNrcy5ocWwnKQogICAgIyAtIHByZXBhcmUgb3V0cHV0IGZpbGU6CiAgICBmaWxlTmFtZSA8LSAidGhhbmt5b3UyMDE4X0Jhbm5lckNsaWNrc18iCiAgICBmaWxlTmFtZSA8LSBwYXN0ZTAoZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKHVuaXF1ZShkUiRjZXROYW1lKVtpXSksCiAgICAgICAgICAgICAgICAgICAgICAgIl8iLCBqLAogICAgICAgICAgICAgICAgICAgICAgICIudHN2IikKICAgIGZpbGVOYW1lIDwtIHBhc3RlMChiYW5uZXJDbGlja3NEaXIsICIvIiwgZmlsZU5hbWUpCiAgICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0OgogICAgaGl2ZUFyZ3MgPC0KICAgICAgJ2JlZWxpbmUgLWYnCiAgICBoaXZlSW5wdXQgPC0gcGFzdGUwKCd0aGFua3lvdTIwMThfQmFubmVyQ2xpY2tzLmhxbCA+ICcsCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lKQogICAgIyAtIGNvbW1hbmQ6CiAgICBoaXZlQ29tbWFuZCA8LSBwYXN0ZShoaXZlQXJncywgaGl2ZUlucHV0KQogICAgc3lzdGVtKGNvbW1hbmQgPSBoaXZlQ29tbWFuZCwgd2FpdCA9IFRSVUUpCiAgICAKICB9CiAgCn0KCiMjIyAtLS0gV3JhbmdsZSB0aGlzIGRhdGFzZXQ6CgojIyMgLS0tIEJhbm5lciB0YWdzOgpjYW1wYWlnbkJhbm5lciA8LSAnd21kZV9ldGMyMDE3X2J0MScKCiMjIyAtLS0gRGF0YXNldDoKIyAtIGNvdW50IG5vbi1lbXB0eSBmaWxlczoKYyA8LSAwCmxGIDwtIGxpc3QuZmlsZXMoKQpsRiA8LSBsRltncmVwbCgnLnRzdicsIGxGLCBmaXhlZCA9IFQpXQpsRiA8LSBsRltncmVwbCgnQ2xpY2tzJywgbEYsIGZpeGVkID0gVCldCmRhdGFTZXQgPC0gbGlzdCgpCmZvciAoaSBpbiAxOmxlbmd0aChsRikpIHsKICBkUyA8LSByZWFkTGluZXMobEZbaV0sIG4gPSAtMSkKICB0aW1lU3RhbXAgPC0gc3Ryc3BsaXQobEZbaV0sIHNwbGl0ID0gIl8iKVtbMV1dWzNdCiAgYmFubmVyQ2xpY2tzIDwtIHN1bShncmVwbChjYW1wYWlnbkJhbm5lciwgZFMsIGZpeGVkID0gVCkpCiAgZGF0YVNldFtbaV1dIDwtIGRhdGEuZnJhbWUodGltZXN0YW1wID0gdGltZVN0YW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhbm5lckNsaWNrcyA9IGJhbm5lckNsaWNrcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKfQpkYXRhU2V0IDwtIHJiaW5kbGlzdChkYXRhU2V0KQoKIyMjIC0tLSBzdG9yZSBCYW5uZXJDbGlja3NQYWdlVmlld3NfVXBkYXRlLmNzdgpzZXR3ZChkYWlseVVwZGF0ZURpcikKd3JpdGUuY3N2KGRhdGFTZXQsIGZpbGUgPSAidGhhbmt5b3UyMDE4X0Jhbm5lckNsaWNrc1BhZ2VWaWV3c19VcGRhdGUuY3N2IikKCiMjIyAtLS0gU1FMCnN0YXJ0RGF0ZSA8LSAnMjAxOC0wMS0wMScKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTMy4gVXNlciBSZWdpc3RyYXRpb24gRGF0YQojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0gTk9URTogVVRDIHRpbWVzdGFtcHMgLSBhZGp1c3RtZW50IGZvciBDRShTKVQgaW50cm9kdWNlZC4gCiMgLSBTZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uXzU0ODczNDUKcUNvbW1hbmQgPC0gcGFzdGUoIm15c3FsIC0tZGVmYXVsdHMtZmlsZT0vZXRjL215c3FsL2NvbmYuZC9hbmFseXRpY3MtcmVzZWFyY2gtY2xpZW50LmNuZiAtaCBhbmFseXRpY3Mtc2xhdmUuZXFpYWQud21uZXQgLUEgLWUgXCJzZWxlY3QgKiBmcm9tIGxvZy5TZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uXzU0ODczNDUgd2hlcmUgKCh3ZWJIb3N0ID0gJ2RlLndpa2lwZWRpYS5vcmcnKSBhbmQgKHRpbWVzdGFtcCA+PSAiLCBnc3ViKCItIiwgIiIsIHN0YXJ0RGF0ZSwgZml4ZWQgPSBUKSwgIjIyMDAwMCkpO1wiID4gIiwKICAgICAgICAgICAgZGFpbHlVcGRhdGVEaXIsICIvdGhhbmt5b3VfMjAxOF91c2VyUmVnaXN0cmF0aW9ucy50c3YiLCBzZXAgPSAiIikKc3lzdGVtKGNvbW1hbmQgPSBxQ29tbWFuZCwgd2FpdCA9IFRSVUUpCgoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIFM0LiBHdWlkZWQgVG91cnMgRGF0YQojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0gTk9URTogVVRDIHRpbWVzdGFtcHMgLSBhZGp1c3RtZW50IGZvciBDRShTKVQgaW50cm9kdWNlZC4gCiMgLSBTZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uXzU0ODczNDUKcUNvbW1hbmQgPC0gcGFzdGUoIm15c3FsIC0tZGVmYXVsdHMtZmlsZT0vZXRjL215c3FsL2NvbmYuZC9hbmFseXRpY3MtcmVzZWFyY2gtY2xpZW50LmNuZiAtaCBhbmFseXRpY3Mtc2xhdmUuZXFpYWQud21uZXQgLUEgLWUgXCJzZWxlY3QgKiBmcm9tIGxvZy5HdWlkZWRUb3VyRXhpdGVkXzg2OTA1NjYgd2hlcmUgKCh3ZWJIb3N0ID0gJ2RlLndpa2lwZWRpYS5vcmcnKSBhbmQgKHRpbWVzdGFtcCA+PSAiLCAKICAgICAgICAgICAgICAgICAgZ3N1YigiLSIsICIiLCBzdGFydERhdGUsIGZpeGVkID0gVCksICIyMjAwMDApKTtcIiA+ICIsIAogICAgICAgICAgICAgICAgICBkYWlseVVwZGF0ZURpciwgIi90aGFua3lvdV8yMDE4X2d1aWRlZFRvdXJzLnRzdiIsIHNlcCA9ICIiKQpzeXN0ZW0oY29tbWFuZCA9IHFDb21tYW5kLCB3YWl0ID0gVFJVRSkKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTNS4gVXNlciBFZGl0IERhdGEKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIGdldCB1c2VyIElEcyBmcm9tIHJlZ2lzdGVyZWQ6CmxGIDwtIGxpc3QuZmlsZXMoKQpsRiA8LSBsRltncmVwbCgndXNlclJlZ2lzdHJhdGlvbnMnLCBsRiwgZml4ZWQgPSBUKV0KdXNlclJlZyA8LSByZWFkLnRhYmxlKGxGLCAKICAgICAgICAgICAgICAgICAgICAgIHF1b3RlID0gIiIsCiAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQp1c2VyUmVnIDwtIHVzZXJSZWcgJT4lIAogIGRwbHlyOjpzZWxlY3QoZXZlbnRfdXNlcklkLCBldmVudF9pc1NlbGZNYWRlLCBldmVudF9jYW1wYWlnbikgJT4lIAogIGZpbHRlcihldmVudF9pc1NlbGZNYWRlID09IDEpICU+JSAKICBmaWx0ZXIoZXZlbnRfY2FtcGFpZ24gJWluJSAid21kZV9ldGMyMDE3X2J0MSIpCiMgLSB1aWRzOgp1aWQgPC0gdXNlclJlZyRldmVudF91c2VySWQKIyAtIHNxbCBxdWVyeQpzcWxRdWVyeSA8LSBwYXN0ZSgnU0VMRUNUIENPVU5UKCopIGFzIGVkaXRzLCByZXZfdXNlciBGUk9NIHJldmlzaW9uIFdIRVJFIHJldl91c2VyIElOICgnLAogICAgICAgICAgICAgICAgICBwYXN0ZSh1aWQsIGNvbGxhcHNlID0gIiwgIiksCiAgICAgICAgICAgICAgICAgICcpIEdST1VQIEJZIHJldl91c2VyOycsCiAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQpteVNxbENvbW1hbmQgPC0gcGFzdGUoJ215c3FsIC1oIGFuYWx5dGljcy1zdG9yZS5lcWlhZC53bW5ldCBkZXdpa2kgLWUgJywKICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCciJywgc3FsUXVlcnksICciID4gJywgc2VwID0gIiIpLCAKICAgICAgICAgICAgICAgICAgICAgIGRhaWx5VXBkYXRlRGlyLCAnL3RoYW5reW91XzIwMThfdXNlckVkaXRzLnRzdicsIHNlcCA9ICIiKQpzeXN0ZW0oY29tbWFuZCA9IG15U3FsQ29tbWFuZCwgCiAgICAgICB3YWl0ID0gVFJVRSkKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTNi4gVHJhaW5pbmcgTW9kdWxlIERhdGEKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKYGBgCgojIyAxQS4gQ2FtcGFpZ24gQmFubmVyIEltcHJlc3Npb25zCgojIyMgMUEuIDEgVGhlIGRhdGEgc2V0CmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KIyAtIHJlcG9ydDogY3VycmVudCB1cGRhdGUKcHJpbnQocGFzdGUwKCJDdXJyZW50IHVwZGF0ZTogIiwgYXMuY2hhcmFjdGVyKFN5cy50aW1lKCkpKSkKYmFubmVySW1wcmVzc2lvbnMgPC0gcmVhZC5jc3YoJ3RoYW5reW91QmFubmVySW1wcmVzc2lvbnMuY3N2JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpICU+JSAKICBnYXRoZXIoa2V5ID0gIEJhbm5lciwKICAgICAgICAgdmFsdWUgPSBWaWV3cywKICAgICAgICAgQjE3V01ERV90aGFua3lvdV9hdXRob3JzOkIxN1dNREVfdGhhbmt5b3VfYXV0aG9yc19tb2JfQikgJT4lIAogIGdyb3VwX2J5KHRpbWVTdGFtcCwgQmFubmVyKSAlPiUgCiAgc3VtbWFyaXNlKFZpZXdzID0gc3VtKFZpZXdzKSkKa25pdHI6OmthYmxlKGJhbm5lckltcHJlc3Npb25zLCBmb3JtYXQgPSAiaHRtbCIpICU+JSAKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGLCBwb3NpdGlvbiA9ICJsZWZ0IikKYGBgCiMjIyAxQS4gMiBDaGFydAoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQpnZ3Bsb3QoYmFubmVySW1wcmVzc2lvbnMsIGFlcyh4ID0gdGltZVN0YW1wLAogICAgICAgICAgICAgICAgICAgIHkgPSBWaWV3cywKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IEJhbm5lciwKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFZpZXdzKSkgKwogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBCYW5uZXIpLCBzaXplID0gLjI1KSArCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAid2hpdGUiKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArCiAgZ2d0aXRsZSgnVGhhbmsgWW91IDIwMTg6XG5PdmVydmlldyBvZiBCYW5uZXJJbXByZXNzaW9ucycpICsKICB5bGFiKCJJbXByZXNzaW9ucyIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCwgaGp1c3QgPSAxKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMgMUIuIENhbXBhaWduIFBhZ2V2aWV3cyAoQmFubmVyIENsaWNrcykKCiMjIyAxQi4gMSBUaGUgZGF0YSBzZXQKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KcGFnZXZpZXdzIDwtIHJlYWQuY3N2KCd0aGFua3lvdTIwMThfQmFubmVyQ2xpY2tzUGFnZVZpZXdzX1VwZGF0ZS5jc3YnLAogICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKcGFnZXZpZXdzIDwtIHBhZ2V2aWV3cyAlPiUgCiAgZ3JvdXBfYnkodGltZXN0YW1wKSAlPiUgCiAgc3VtbWFyaXNlKFBhZ2V2aWV3cyA9IHN1bShiYW5uZXJDbGlja3MpKQprbml0cjo6a2FibGUocGFnZXZpZXdzLCBmb3JtYXQgPSAiaHRtbCIpICU+JSAKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGLCBwb3NpdGlvbiA9ICJsZWZ0IikKYGBgCgojIyMgMUIuIDIgQ2hhcnQKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KZ2dwbG90KHBhZ2V2aWV3cywgYWVzKHggPSB0aW1lc3RhbXAsCiAgICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFBhZ2V2aWV3cykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgCiAgICAgICAgICAgcG9zaXRpb24gPSAiZG9kZ2UiLCAKICAgICAgICAgICB3aWR0aCA9IC4xNSwgCiAgICAgICAgICAgZmlsbCA9ICJ3aGl0ZSIsIAogICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIikgKwogIGdndGl0bGUoJ1RoYW5rIFlvdSAyMDE4OlxuT3ZlcnZpZXcgb2YgTGFuZGluZyBQYWdldmlld3MnKSArCiAgZ2VvbV9sYWJlbChzaXplID0gMykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4LCBoanVzdCA9IDEpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgoKIyMgMi4gQ2FtcGFpZ24gVXNlciBSZWdpc3RyYXRpb25zCgojIyMgMi4gMSBUaGUgZGF0YSBzZXQKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KdXNlclJlZyA8LSByZWFkLmRlbGltKCd0aGFua3lvdV8yMDE4X3VzZXJSZWdpc3RyYXRpb25zLnRzdicsCiAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICAgICAgICAgICAgcXVvdGUgPSAiIiwKICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAjIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKdXNlclJlZyR0aW1lc3RhbXAgPC0gYXMuY2hhcmFjdGVyKHVzZXJSZWckdGltZXN0YW1wKQp1c2VyUmVnJHRpbWVzdGFtcCA8LSBzYXBwbHkodXNlclJlZyR0aW1lc3RhbXAsIGZ1bmN0aW9uKHgpIHsKICB5IDwtIHN1YnN0cih4LCAxLCA0KQogIG0gPC0gc3Vic3RyKHgsIDUsIDYpCiAgZCA8LSBzdWJzdHIoeCwgNywgOCkKICBwYXJ0MURhdGUgPC0gcGFzdGUoeSwgbSwgZCwgc2VwID0gIi0iKQogIGhyIDwtIHN1YnN0cih4LCA5LCAxMCkKICBtaSA8LSBzdWJzdHIoeCwgMTEsIDEyKQogIHNlIDwtIHN1YnN0cih4LCAxMywgMTQpCiAgcGFydDJEYXRlIDwtIHBhc3RlKGhyLCBtaSwgc2UsIHNlcCA9ICI6IikKICBwYXN0ZShwYXJ0MURhdGUsIHBhcnQyRGF0ZSwgc2VwID0gIiAiKQp9KQp1c2VyUmVnJHRpbWVzdGFtcCA8LSBhcy5QT1NJWGN0KHVzZXJSZWckdGltZXN0YW1wLCB0eiA9ICJVVEMiKQp0aW1lRGlmZiA8LSAKICBhcy5QT1NJWGN0KGFzLmNoYXJhY3RlcihTeXMudGltZSgpKSwgdHogPSAiVVRDIikgLSBhcy5QT1NJWGN0KGFzLmNoYXJhY3RlcihTeXMudGltZSgpKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpCnVzZXJSZWckdGltZXN0YW1wIDwtIGFzLmNoYXJhY3Rlcih1c2VyUmVnJHRpbWVzdGFtcCArIHRpbWVEaWZmKQp1c2VyUmVnJHRpbWVzdGFtcCA8LSBzYXBwbHkodXNlclJlZyR0aW1lc3RhbXAsIGZ1bmN0aW9uKHgpIHsKICB5IDwtIHN1YnN0cih4LCAxLCA0KQogIG0gPC0gc3Vic3RyKHgsIDYsIDcpCiAgZCA8LSBzdWJzdHIoeCwgOSwgMTApIAogIHBhc3RlKHksIG0sIGQsIHNlcCA9ICItIikKfSkKdXNlclJlZyA8LSB1c2VyUmVnICU+JQogIGZpbHRlcihldmVudF9jYW1wYWlnbiAlaW4lICd3bWRlX2V0YzIwMTdfYnQxJykgJT4lIAogIGdyb3VwX2J5KHRpbWVzdGFtcCkgJT4lIAogIHN1bW1hcmlzZShSZWdpc3RyYXRpb25zID0gbigpKQprbml0cjo6a2FibGUodXNlclJlZywgZm9ybWF0ID0gImh0bWwiKSAlPiUgCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAibGVmdCIpCmBgYAoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQpwcmludChwYXN0ZTAoIlRvdGFsIG51bWJlciBvZiByZWdpc3RlcmVkIHVzZXJzOiAiLCBzdW0odXNlclJlZyRSZWdpc3RyYXRpb25zKSkpCmBgYAoKIyMjIDIuIDIgQ2hhcnQKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KZ2dwbG90KHVzZXJSZWcsIGFlcyh4ID0gdGltZXN0YW1wLAogICAgICAgICAgICAgICAgICAgIHkgPSBSZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUmVnaXN0cmF0aW9ucykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgCiAgICAgICAgICAgcG9zaXRpb24gPSAiZG9kZ2UiLCAKICAgICAgICAgICB3aWR0aCA9IC4xNSwgCiAgICAgICAgICAgZmlsbCA9ICJ3aGl0ZSIsIAogICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIikgKwogIGdndGl0bGUoJ1RoYW5rIFlvdSAyMDE4OlxuT3ZlcnZpZXcgb2YgVXNlciBSZWdpc3RyYXRpb25zJykgKwogIGdlb21fbGFiZWwoc2l6ZSA9IDMpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCwgaGp1c3QgPSAxKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMgMy4gQ2FtcGFpZ24gVXNlciBFZGl0cwoKIyMjIDMuIDEgVGhlIGRhdGEgc2V0CgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CnVzZXJFZGl0cyA8LSByZWFkTGluZXMoJ3RoYW5reW91XzIwMThfdXNlckVkaXRzLnRzdicsIG4gPSAtMSkKaWYgKGxlbmd0aCh1c2VyRWRpdHMpID49IDEpIHsKICB1c2VyRWRpdHMgPC0gcmVhZC5kZWxpbSgndGhhbmt5b3VfMjAxOF91c2VyRWRpdHMudHN2JywKICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgIyAtIHJlcG9ydAogIHByaW50KHBhc3RlMChzdW0odXNlckVkaXRzJGVkaXRzKSwgIiBlZGl0cyB3ZXJlIG1hZGUgYnkgdGhlIGNhbXBhaWduIHJlZ2lzdGVyZWQgdXNlcnMgdGh1cyBmYXIuIikpCn0gZWxzZSB7CiAgcHJpbnQoIlRoZXJlIGFyZSBjdXJyZW50bHkgbm8gdXNlciBlZGl0cyBmcm9tIHRoaXMgY2FtcGFpZ24uIikKfQpgYGAKCiMjIyAzLiAyIENoYXJ0CgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CnVzZXJFZGl0c0Rpc3QgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZSh1c2VyRWRpdHMkZWRpdHMpKQpjb2xuYW1lcyh1c2VyRWRpdHNEaXN0KSA8LSBjKCdOdW1iZXIgb2YgRWRpdHMnLCAnTnVtYmVyIG9mIFVzZXJzJykKZ2dwbG90KHVzZXJFZGl0c0Rpc3QsIGFlcyh4ID0gYE51bWJlciBvZiBFZGl0c2AsCiAgICAgICAgICAgICAgICAgICAgeSA9IGBOdW1iZXIgb2YgVXNlcnNgLAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gYE51bWJlciBvZiBVc2Vyc2ApKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIAogICAgICAgICAgIHBvc2l0aW9uID0gImRvZGdlIiwgCiAgICAgICAgICAgd2lkdGggPSAuMTUsIAogICAgICAgICAgIGZpbGwgPSAiZGFya29yYW5nZSIsIAogICAgICAgICAgIGNvbG9yID0gImRhcmtvcmFuZ2UiKSArCiAgZ2d0aXRsZSgnVGhhbmsgWW91IDIwMTg6XG5Ib3cgbWFueSB1c2VycyBtYWRlIGEgcGFydGljdWxhciBudW1iZXIgb2YgZWRpdHMnKSArCiAgZ2VvbV9sYWJlbChzaXplID0gMykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgc2l6ZSA9IDgsIGhqdXN0ID0gMSkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMjIDQuIENhbXBhaWduIEd1aWRlZCBUb3VycwoKIyMjIDQuIDEgVGhlIGRhdGEgc2V0OiBHdWlkZWQgVG91ciBFeGl0IFN0ZXBzCgpDYW1wYWlnbiBndWlkZWQgdG91cnM6IGBkaXNrdXRpZXJlbmAgYW5kIGBzZWltdXRpZ2AuCgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CnRvdXJzIDwtIGMoJ2Rpc2t1dGllcmVuJywgJ3NlaW11dGlnJykKIyBzZXR3ZCgnL2hvbWUvZ29yYW5zbS9Xb3JrL19fX0RhdGFLb2xla3Rpdi9Qcm9qZWN0cy9XaWtpbWVkaWFERVUvX1dNREVfUHJvamVjdHMvX21pc2MvTmV3RWRpdG9yc19UZWFtL1RoYW5rX1lvdV9DYW1wYWlnbl8yMDE4L19kYWlseVVwZGF0ZS8nKQpnVG91cnMgPC0gcmVhZC5kZWxpbSgndGhhbmt5b3VfMjAxOF9ndWlkZWRUb3Vycy50c3YnLAogICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKZ1RvdXJzJHRpbWVzdGFtcCA8LSBhcy5jaGFyYWN0ZXIoZ1RvdXJzJHRpbWVzdGFtcCkKZ1RvdXJzJHRpbWVzdGFtcCA8LSBzYXBwbHkoZ1RvdXJzJHRpbWVzdGFtcCwgZnVuY3Rpb24oeCkgewogIHkgPC0gc3Vic3RyKHgsIDEsIDQpCiAgbSA8LSBzdWJzdHIoeCwgNSwgNikKICBkIDwtIHN1YnN0cih4LCA3LCA4KQogIHBhcnQxRGF0ZSA8LSBwYXN0ZSh5LCBtLCBkLCBzZXAgPSAiLSIpCiAgaHIgPC0gc3Vic3RyKHgsIDksIDEwKQogIG1pIDwtIHN1YnN0cih4LCAxMSwgMTIpCiAgc2UgPC0gc3Vic3RyKHgsIDEzLCAxNCkKICBwYXJ0MkRhdGUgPC0gcGFzdGUoaHIsIG1pLCBzZSwgc2VwID0gIjoiKQogIHBhc3RlKHBhcnQxRGF0ZSwgcGFydDJEYXRlLCBzZXAgPSAiICIpCn0pCmdUb3VycyR0aW1lc3RhbXAgPC0gYXMuUE9TSVhjdChnVG91cnMkdGltZXN0YW1wLCB0eiA9ICJVVEMiKQp0aW1lRGlmZiA8LSAKICBhcy5QT1NJWGN0KGFzLmNoYXJhY3RlcihTeXMudGltZSgpKSwgdHogPSAiVVRDIikgLSBhcy5QT1NJWGN0KGFzLmNoYXJhY3RlcihTeXMudGltZSgpKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpCmdUb3VycyR0aW1lc3RhbXAgPC0gYXMuY2hhcmFjdGVyKGdUb3VycyR0aW1lc3RhbXAgKyB0aW1lRGlmZikKZ1RvdXJzJHRpbWVzdGFtcCA8LSBzYXBwbHkoZ1RvdXJzJHRpbWVzdGFtcCwgZnVuY3Rpb24oeCkgewogIHkgPC0gc3Vic3RyKHgsIDEsIDQpCiAgbSA8LSBzdWJzdHIoeCwgNiwgNykKICBkIDwtIHN1YnN0cih4LCA5LCAxMCkgCiAgcGFzdGUoeSwgbSwgZCwgc2VwID0gIi0iKQp9KQojIC0gYW5vbnltaXplIGV2ZW50X3VzZXJJZApldmVudFVzZXJJZCA8LSBzZXRkaWZmKHVuaXF1ZShnVG91cnMkZXZlbnRfdXNlcklkKSwgMCkKYW5fdXNlcklkIDwtIGNoYXJhY3RlcihsZW5ndGgoZXZlbnRVc2VySWQpKQpmb3IgKGkgaW4gMTpsZW5ndGgoYW5fdXNlcklkKSkgewogIGlkIDwtIHJvdW5kKHJ1bmlmKDEsIDEsIDEwZTYpKQogIHdoaWxlIChpZCAlaW4lIGFuX3VzZXJJZCkgewogICAgaWQgPC0gcm91bmQocnVuaWYoMSwgMSwgMTBlNikpCiAgfQogIGFuX3VzZXJJZFtpXSA8LSBpZAp9CmFuX3VzZXJJZCA8LSBwYXN0ZTAoInVfIiwgYW5fdXNlcklkKQpnVG91cnMkYW5fdXNlcklkIDwtIHNhcHBseShnVG91cnMkZXZlbnRfdXNlcklkLCBmdW5jdGlvbih4KSB7CiAgdyA8LSB3aGljaChldmVudFVzZXJJZCAlaW4lIHgpCiAgaWYgKGxlbmd0aCh3KSA+IDApIHsKICAgIGFuX3VzZXJJZFt3XQogIH0gZWxzZSB7CiAgICB4CiAgfQp9KQojIC0gbG9vayB1cCBmb3IgdGhlIGNhbXBhaWduIGd1aWRlZCB0b3Vycwp3IDwtIHdoaWNoKGdUb3VycyRldmVudF90b3VyICVpbiUgdG91cnMpCmlmIChsZW5ndGgodykgPiAwKSB7CiAgZ1RvdXJzIDwtIGdUb3Vyc1t3LCBdCiAgZ1RvdXJzIDwtIGdUb3VycyAlPiUgCiAgICBmaWx0ZXIoZXZlbnRfdXNlcklkICE9IDApICU+JSAKICAgIHNlbGVjdCh0aW1lc3RhbXAsIGV2ZW50X3RvdXIsIGV2ZW50X3N0ZXAsIGFuX3VzZXJJZCkKICBrbml0cjo6a2FibGUoZ1RvdXJzLCBmb3JtYXQgPSAiaHRtbCIpICU+JSAKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGLCBwb3NpdGlvbiA9ICJsZWZ0IikKfSBlbHNlIHsKICBwcmludCgiVGhlcmUgYXJlIGN1cnJlbnRseSBubyBkYXRhIG9uIGd1aWRlZCB0b3VycyBmcm9tIHRoaXMgY2FtcGFpZ24uIikKfQpgYGAKIyMjIDQuIDIgVW5pcXVlIHVzZXJzIGluIEd1aWRlZCBUb3VycwoKQ2FtcGFpZ24gZ3VpZGVkIHRvdXJzOiBgZGlza3V0aWVyZW5gIGFuZCBgc2VpbXV0aWdgLgoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQpnVG91cnMgJT4lIAogIHNlbGVjdChldmVudF90b3VyKSAlPiUgCiAgZ3JvdXBfYnkoZXZlbnRfdG91cikgJT4lIAogIHN1bW1hcmlzZShgVW5pcXVlIHVzZXJzYCA9IG4oKSkgJT4lIAogIGtuaXRyOjprYWJsZShmb3JtYXQgPSAiaHRtbCIpICU+JSAKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGLCBwb3NpdGlvbiA9ICJsZWZ0IikKYGBgCgojIyA1IFRyYWluaW5nIE1vZHVsZQoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQp0cmFpbkRhdGEgPC0gcmVhZC5jc3YoJ3dtZGVfdHJhaW5pbmdfZGF0YS5jc3YnKQojIC0gcmVtb3ZlIHVzZXJzOiBTdGVmYW4gU2NobmVpZGVyIChXTURFKSwgU2FnZSAoV2lraSBFZCkKd1IgPC0gd2hpY2godHJhaW5EYXRhJHVzZXJuYW1lICVpbiUgYygnU3RlZmFuIFNjaG5laWRlciAoV01ERSknLCAnU2FnZSAoV2lraSBFZCknKSkKdHJhaW5EYXRhIDwtIHRyYWluRGF0YVstd1IsIF0KIyAtIGdldCB1c2VyIElEcyBmcm9tIHJlZ2lzdGVyZWQ6CmxGIDwtIGxpc3QuZmlsZXMoKQpsRiA8LSBsRltncmVwbCgndXNlclJlZ2lzdHJhdGlvbnMnLCBsRiwgZml4ZWQgPSBUKV0KdXNlckRhdGEgPC0gcmVhZC50YWJsZShsRiwgCiAgICAgICAgICAgICAgICAgICAgICBxdW90ZSA9ICIiLAogICAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IiwKICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKZWREYXRhIDwtIHJlYWQuZGVsaW0oJ3RoYW5reW91XzIwMThfdXNlckVkaXRzLnRzdicsCiAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQojIC0gd2hpY2ggdXNlcnMgaW4gdGhlIFRyYWluaW5nIE1vZHVsZSBkYXRhIHNldCBoYXZlIHJlZ2lzdGVyZWQgdmlhIFRoYW5rIFlvdSAyMDE4Ogp3UmVnQ2FtcGFpZ24gPC0gd2hpY2godHJhaW5EYXRhJHVzZXJuYW1lICVpbiUgdXNlckRhdGEkZXZlbnRfdXNlck5hbWUpCnRyYWluRGF0YSA8LSB0cmFpbkRhdGFbd1JlZ0NhbXBhaWduLCBdCmNvbXBsZXRlZFRyYWluaW5nIDwtIHNhcHBseSgxOmRpbSh0cmFpbkRhdGEpWzFdLCBmdW5jdGlvbih4KSB7CiAgdyA8LSB3aGljaCh0cmFpbkRhdGEkdXNlcm5hbWUgJWluJSB0cmFpbkRhdGEkdXNlcm5hbWVbeF0pCiAgY3QgPC0gaWZlbHNlKAogICAgdHJhaW5EYXRhJGxhc3Rfc2xpZGVfY29tcGxldGVkW3ddICVpbiUgYygncmVsZXZhbnotcXVpei1mb3J0c2V0enVuZycsICdkYXMtd2FycycsICdmZXJ0aWcnKSwgVCwgRikKICBpZmVsc2Uoc3VtKGN0KSA+PSAxLCBULCBGKQp9KQp0cmFpbkRhdGEkY29tcGxldGVkX3RyYWluaW5nIDwtIGNvbXBsZXRlZFRyYWluaW5nCnVzZXJEYXRhIDwtIHVzZXJEYXRhICU+JSAKICBmaWx0ZXIoZXZlbnRfY2FtcGFpZ24gJWluJSAnd21kZV9ldGMyMDE3X2J0MScgJiBldmVudF9pc1NlbGZNYWRlKSAlPiUgCiAgc2VsZWN0KGV2ZW50X3VzZXJJZCwgZXZlbnRfdXNlck5hbWUpCmNvbXBsZXRlZFRyYWluaW5nRGF0YSA8LSB0cmFpbkRhdGEgJT4lCiAgc2VsZWN0KHVzZXJuYW1lLCBjb21wbGV0ZWRfdHJhaW5pbmcpCmNvbXBsZXRlZFRyYWluaW5nRGF0YSA8LSBjb21wbGV0ZWRUcmFpbmluZ0RhdGFbIWR1cGxpY2F0ZWQoY29tcGxldGVkVHJhaW5pbmdEYXRhKSwgXQp1c2VyRGF0YSA8LSBsZWZ0X2pvaW4odXNlckRhdGEsIGNvbXBsZXRlZFRyYWluaW5nRGF0YSwgYnkgPSBjKCJldmVudF91c2VyTmFtZSIgPSAidXNlcm5hbWUiKSkKdXNlckRhdGEkbW9kdWxlX2NvbXBsZXRpb25fZGF0ZSA8LSBOQQp1c2VyRGF0YSA8LSBsZWZ0X2pvaW4odXNlckRhdGEsIGVkRGF0YSwKICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygnZXZlbnRfdXNlcklkJyA9ICdyZXZfdXNlcicpKQp1c2VyRGF0YSRzdGFydGVkX3RyYWluaW5nIDwtIGlmZWxzZShpcy5uYSh1c2VyRGF0YSRjb21wbGV0ZWRfdHJhaW5pbmcpLCBGLCBUKQpgYGAKCgojIyMgNS4xIFRyYWluaW5nIE1vZHVsZSBPdmVydmlldwoKSG93IG1hbnkgcmVnaXN0ZXJlZCB1c2VycyBoYXZlIHN0YXJ0ZWQgdGhlIFRyYWluaW5nIE1vZHVsZT8KCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KcHJpbnQocGFzdGUwKCJOdW1iZXIgb2YgdXNlcnMgd2hvIGhhdmUgc3RhcnRlZCB0aGUgVHJhaW5pbmcgTW9kdWxlIGlzICIsIAogICAgICAgICAgICAgc3VtKHVzZXJEYXRhJHN0YXJ0ZWRfdHJhaW5pbmcpLAogICAgICAgICAgICAgIiAsIHdoaWNoIGlzICIsCiAgICAgICAgICAgICByb3VuZChzdW0odXNlckRhdGEkc3RhcnRlZF90cmFpbmluZykvc3VtKHVzZXJSZWckUmVnaXN0cmF0aW9ucykqMTAwLCAyKSwKICAgICAgICAgICAgICIlIG9mIHJlZ2lzdGVyZWQgdXNlcnMuIgogICAgICAgICAgICAgICApKQpgYGAKCkhvdyBtYW55IHJlZ2lzdGVyZWQgdXNlcnMgaGF2ZSBjb21wbGV0ZWQgdGhlIFRyYWluaW5nIE1vZHVsZT8KCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KcHJpbnQocGFzdGUwKCJOdW1iZXIgb2YgdXNlcnMgd2hvIGhhdmUgY29tcGxldGVkIHRoZSBUcmFpbmluZyBNb2R1bGUgaXMgIiwgCiAgICAgICAgICAgICBzdW0odXNlckRhdGEkY29tcGxldGVkX3RyYWluaW5nLCBuYS5ybSA9IFQpLAogICAgICAgICAgICAgIiAsIHdoaWNoIGlzICIsCiAgICAgICAgICAgICByb3VuZChzdW0odXNlckRhdGEkY29tcGxldGVkX3RyYWluaW5nLCBuYS5ybSA9IFQpL3N1bSh1c2VyUmVnJFJlZ2lzdHJhdGlvbnMpKjEwMCwgMiksCiAgICAgICAgICAgICAiJSBvZiByZWdpc3RlcmVkIHVzZXJzLiIKICAgICAgICAgICAgICAgKSkKYGBgCgpXaGF0IGFyZSB0aGUgY29tcGxldGVkIHNsaWRlcyBwZXIgdXNlciBUcmFpbmluZyBNb2R1bGU/CgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CnNsaWRlc0RhdGEgPC0gdHJhaW5EYXRhICU+JSAKICBzZWxlY3QodHJhaW5pbmdfbW9kdWxlLCBsYXN0X3NsaWRlX2NvbXBsZXRlZCkgJT4lIAogIGdyb3VwX2J5KHRyYWluaW5nX21vZHVsZSwgbGFzdF9zbGlkZV9jb21wbGV0ZWQpICU+JSAKICBzdW1tYXJpc2UoQ291bnQgPSBuKCkpICU+JSAKICBhcnJhbmdlKHRyYWluaW5nX21vZHVsZSwgZGVzYyhDb3VudCkpCmNvbG5hbWVzKHNsaWRlc0RhdGEpWzNdIDwtIGMoJ04uIHVzZXJzIGNvbXBsZXRlZCcpCmtuaXRyOjprYWJsZShzbGlkZXNEYXRhLCBmb3JtYXQgPSAiaHRtbCIpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYsIHBvc2l0aW9uID0gImxlZnQiKQpgYGAKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KZ2dwbG90KHNsaWRlc0RhdGEsIGFlcyh4ID0gbGFzdF9zbGlkZV9jb21wbGV0ZWQsCiAgICAgICAgICAgICAgICAgICAgICAgeSA9IGBOLiB1c2VycyBjb21wbGV0ZWRgLAogICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gdHJhaW5pbmdfbW9kdWxlLAogICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gYE4uIHVzZXJzIGNvbXBsZXRlZGApKSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IDEpLCBzaXplID0gLjI1KSArCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAid2hpdGUiKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArCiAgZ2d0aXRsZSgnVGhhbmsgWW91IDIwMTg6XG5PdmVydmlldyBvZiBUcmFpbmluZyBNb2R1bGVzJykgKwogIHlsYWIoIk5vLiBVc2VycyIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMpICsKICBmYWNldF93cmFwKH50cmFpbmluZ19tb2R1bGUpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNi41LCBoanVzdCA9IDEpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpEbyB1c2VycyBzdGFydCBlZGl0aW5nIGFmdGVyIHRyYWluaW5nIG1vZHVsZXM/IElzIHRoZXJlIGEgZGlmZmVyZW5jZSBpbiB1c2VycyB3aXRoIGNvbXBsZXRlZCBhbmQgbm90LWNvbXBsZXRlZCBvciBub3QgYXQgYWxsIGhhdmluZyB0YWtlbiB0aGUgbW9kdWxlcz8KCkhvdyBtYW55IGVkaXRzIHdlcmUgbWFkZSBvbiBiZWhhbGYgb2YgdGhvc2UgdXNlcnMgd2hvIGhhdmUgc3RhcnRlZCB0aGUgVHJhaW5pbmcgTW9kdWxlIHZzLiB0aG9zZSB3aG8gZGlkIG5vdD8KCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KdEFuIDwtIHVzZXJEYXRhICU+JSAKICBzZWxlY3Qoc3RhcnRlZF90cmFpbmluZywgZWRpdHMpICU+JSAKICBncm91cF9ieShzdGFydGVkX3RyYWluaW5nKSAlPiUgCiAgc3VtbWFyaXNlKFVzZXJzID0gbigpLCBFZGl0cyA9IHN1bShlZGl0cywgbmEucm0gPSBUKSkgJT4lIAogIGFycmFuZ2Uoc3RhcnRlZF90cmFpbmluZywgZGVzYyhFZGl0cykpCmtuaXRyOjprYWJsZSh0QW4sIGZvcm1hdCA9ICJodG1sIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAibGVmdCIpCmBgYAoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQpwcmludChwYXN0ZTAoIkluIHRvdGFsLCB0aGUgdXNlcnMgd2hvIGhhdmUgc3RhcnRlZCB0aGUgVHJhaW5pbmcgTW9kdWxlIG1hZGUgIiwgCiAgICAgICAgICAgICBzdW0odEFuJEVkaXRzWzJdLCBuYS5ybSA9IFQpLAogICAgICAgICAgICAgIiBlZGl0cywgd2hpY2ggaXMgIiwKICAgICAgICAgICAgIHJvdW5kKHN1bSh0QW4kRWRpdHNbMl0sIG5hLnJtID0gVCkvc3VtKHRBbiRFZGl0cywgbmEucm0gPSBUKSoxMDAsIDIpLAogICAgICAgICAgICAgIiUgb2YgYWxsIGVkaXRzLiIKICAgICAgICAgICAgICAgKSkKcHJpbnQocGFzdGUwKCJPbiB0aGUgb3RoZXIgaGFuZCwgdGhlIHVzZXJzIHdobyBkaWQgbm90IHRha2UgdGhlIFRyYWluaW5nIE1vZHVsZSBtYWRlICIsIAogICAgICAgICAgICAgdEFuJEVkaXRzWzFdLAogICAgICAgICAgICAgIiBlZGl0cywgd2hpY2ggaXMgIiwKICAgICAgICAgICAgIHJvdW5kKHRBbiRFZGl0c1sxXS9zdW0odEFuJEVkaXRzKSoxMDAsIDIpLAogICAgICAgICAgICAgIiUgb2YgYWxsIGVkaXRzLiIKICAgICAgICAgICAgICAgKSkKYGBgCgpIb3cgbWFueSBlZGl0cyB3ZXJlIG1hZGUgb24gYmVoYWxmIG9mIHRob3NlIHVzZXJzIHdobyBoYXZlIGNvbXBsZXRlZCB0aGUgVHJhaW5pbmcgTW9kdWxlIHZzLiB0aG9zZSB3aG8gZGlkIG5vdD8KCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KdEFuIDwtIHVzZXJEYXRhICU+JSAKICBzZWxlY3QoY29tcGxldGVkX3RyYWluaW5nLCBlZGl0cykgJT4lIAogIGdyb3VwX2J5KGNvbXBsZXRlZF90cmFpbmluZykgJT4lIAogIHN1bW1hcmlzZShVc2VycyA9IG4oKSwgRWRpdHMgPSBzdW0oZWRpdHMsIG5hLnJtID0gVCkpICU+JSAKICBhcnJhbmdlKGNvbXBsZXRlZF90cmFpbmluZywgZGVzYyhFZGl0cykpCmtuaXRyOjprYWJsZSh0QW4sIGZvcm1hdCA9ICJodG1sIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAibGVmdCIpCmBgYAoKQ29tcGxldGlvbiBvZiB0aGUgVHJhaW5pbmcgTW9kdWxlIHZzLiBudW1iZXIgb2YgZWRpdHMKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KIyAtIGZpbmFsIHNsaWRlIGZvcjogd2lraXBlZGlhLWJhc2lzd2lzc2VuID0gcmVsZXZhbnotcXVpei1mb3J0c2V0enVuZwojIC0gZmluYWwgc2xpZGUgZm9yOiBlZGl0aWVyZW4tYmFzaXN3aXNzZW4gPSBkYXMtd2FycwojIC0gZmluYWwgc2xpZGUgZm9yOiBhcnRpa2VsLWJld2VydGVuID0gZmVydGlnCiMgZmluYWxTbGlkZSA8LSBjKCdyZWxldmFuei1xdWl6LWZvcnRzZXR6dW5nJywgJ2RhcycsICdmZXJ0aWcnKQojIG5hbWVzKGZpbmFsU2xpZGUpIDwtIGMoJ3dpa2lwZWRpYS1iYXNpc3dpc3NlbicsICdlZGl0aWVyZW4tYmFzaXN3aXNzZW4nLCAncmVsZXZhbnotcXVpei1mb3J0c2V0enVuZycpCmNvbXBsZXRlZFRyYWluaW5nRWRpdHMgPC0gdXNlckRhdGEgJT4lIAogIHNlbGVjdChjb21wbGV0ZWRfdHJhaW5pbmcsIGVkaXRzKSAlPiUgCiAgZ3JvdXBfYnkoY29tcGxldGVkX3RyYWluaW5nKSAlPiUgCiAgc3VtbWFyaXNlKFVzZXJzID0gbigpLCBFZGl0cyA9IHN1bShlZGl0cywgbmEucm0gPSBUKSkKa25pdHI6OmthYmxlKGNvbXBsZXRlZFRyYWluaW5nRWRpdHMsIGZvcm1hdCA9ICJodG1sIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAibGVmdCIpCmBgYAoKTGV0J3Mgbm93IHRha2UgYSBsb29rIGF0IHRoZSBudW1iZXIgb2YgdXNlcnMgd2hvIChhKSBtYWRlIGFueSBlZGl0cyBhdCBhbGwsIG9yKGIpIGhhdmUgcmVhY2hlZCB0aGVpciAxMHRoIGVkaXQsIGluIHRoZXNlIHR3byBncm91cHM6CgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CnVzZXJEYXRhJEVkaXRzIDwtICh1c2VyRGF0YSRlZGl0cyA+IDApCnVzZXJEYXRhJEVkaXRzMTAgPC0gKHVzZXJEYXRhJGVkaXRzID49IDEwKQpjb21wbGV0ZWRUcmFpbmluZ0VkaXRzIDwtIHVzZXJEYXRhICU+JSAKICBzZWxlY3QoY29tcGxldGVkX3RyYWluaW5nLCBFZGl0czEwKSAlPiUKICBncm91cF9ieShjb21wbGV0ZWRfdHJhaW5pbmcpICU+JSAKICBzdW1tYXJpc2UoVXNlcnMgPSBuKCksIEVkaXRzMTAgPSBzdW0oRWRpdHMxMCwgbmEucm0gPSBUKSkKa25pdHI6OmthYmxlKGNvbXBsZXRlZFRyYWluaW5nRWRpdHMsIGZvcm1hdCA9ICJodG1sIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAibGVmdCIpCmBgYA==