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
### ------------------------------------------------------------

1. Campaign Pageviews (Banner Clicks)

1. 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 251
2018-01-04 0
2018-01-05 0
2018-01-06 0
2018-01-07 0

1. 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",
                      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 3

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] "4 edits were made by the campaign registered users thus far."

3. 2 Chart

4. Campaign Guided Tours

4. 1 The data set

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 = "-")
})
# - look up for the campaign guided tours
w <- which(gTours$event_tour %in% tours)
if (length(w) > 0) {
  gTours <- gTours[w, ]
  gTours <- gTours %>% 
    select(timestamp, event_step, event_userId)
  knitr::kable(gTours[, c(timestamp, "event_step")], format = "html") %>% 
  kable_styling(full_width = F, position = "left")
} else {
  print("There are currently no data on guided tours from this campaign.")
}
[1] "There are currently no data on guided tours from this campaign."

4. 2 Chart

LS0tCnRpdGxlOiAnVGhhbmsgWW91IENhbXBhaWduIDIwMTg6IFJlcG9ydCcKYXV0aG9yOiAiR29yYW4gUy4gTWlsb3Zhbm92aWMsIERhdGEgQW5hbHlzdCwgV01ERSIKZGF0ZTogIkphbnVhcnksIDIwMTgiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0aGVtZTogc2ltcGxleAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDUKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQotLS0KCgoqKkZlZWRiYWNrKiogc2hvdWxkIGJlIHNlbmQgdG8gYGdvcmFuLm1pbG92YW5vdmljX2V4dEB3aWtpbWVkaWEuZGVgLiAKClRoZSBjYW1wYWlnbiBpcyBydW4gZnJvbSAxLiBKYW51YXJ5IDIwMTggdG8gTiBKYW51YXJ5IDIwMTguCgpgYGB7ciBzZXR1cH0Ka25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAnL2hvbWUvZ29yYW5zbS9Xb3JrL19fX0RhdGFLb2xla3Rpdi9Qcm9qZWN0cy9XaWtpbWVkaWFERVUvX1dNREVfUHJvamVjdHMvX21pc2MvTmV3RWRpdG9yc19UZWFtL1RoYW5rX1lvdV9DYW1wYWlnbl8yMDE4L19kYWlseVVwZGF0ZS8nKQpgYGAKCmBgYHtyLCBlY2hvID0gRiwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCByZXN1bHRzID0gJ2hpZGUnfQojICFkaWFnbm9zdGljcyBvZmYKIyMjIC0tLSBTZXR1cAprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQgPSA4KSAKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoRFQpCmxpYnJhcnkocmVzaGFwZTIpCgojIC0gcmVwb3J0OiBjdXJyZW50IHVwZGF0ZQpwcmludChwYXN0ZTAoIkN1cnJlbnQgdXBkYXRlOiAiLCBhcy5jaGFyYWN0ZXIoU3lzLnRpbWUoKSkpKQpgYGAKCiMjIDAuIERhdGEgQWNxdWlzaXRvbgoKKipOT1RFOioqIHRoZSBEYXRhIEFjcXVpc2l0aW9uIGNvZGUgY2h1bmsgaXMgbm90IGZ1bGx5IHJlcHJvZHVjaWJsZSBmcm9tIHRoaXMgUmVwb3J0LiBUaGUgZGF0YSBhcmUgY29sbGVjdGVkIGJ5IHJ1bm5pbmcgdGhlIHNjcmlwdCBgVGhhbmtZb3VfMjAxOF9Qcm9kdWN0aW9uX1NRTC5SYCBvbiBzdGF0MTAwNS5lcWlhZC53bW5ldCwgY29sbGVjdGluZyB0aGUgZGF0YSBhcyBgLnRzdmAgYW5kIGAuY3N2YCBmaWxlcywgY29weWluZyBtYW51YWxseSwgYW5kIHByb2Nlc3NpbmcgbG9jYWxseS4gUnVuIGZyb20gc3RhdDEwMDUgc3RhdCBib3ggYnkgZXhlY3V0aW5nIGBSc2NyaXB0IC9ob21lL2dvcmFuc20vUlNjcmlwdHMvV01ERV9DYW1wYWlnbnMvVGhhbmtZb3UyMDE4L1RoYW5rWW91XzIwMThfUHJvZHVjdGlvbl9TUUwuUmAuCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGfQoKIyMjIC0tLSBmcm9tIHN0YXQxMDA1OiBUaGFuayBZb3UgMjAxOCBCYW5uZXIgQ2FtcGFpZ24KIyMjIC0tLSBwcm9kdWN0aW9uIHNjcmlwdDogZmV0Y2ggdGhlIGNhbXBhaWduIGRhdGEgc2V0cwoKIyMjIC0tLSBDYW1wYWlnbiBEZXRhaWxzOiAKIyAtIGVzdGltYXRlZCBzdGFydDogMXN0IEphbnVhcnkgMjAxOCAoKy8tIDIgZGF5cykKIyAtIGVzdGltYXRlZCBkdXJhdGlvbjogNiB0byAxMCBkYXlzCiMgLSBSZXBvcnRpbmcgc2hvdWxkIHN0YXJ0IG9uIDJuZCBKYW51YXJ5IDIwMTguIAojIC0gVGhlIHJlcG9ydCBtdXN0IGluY2x1ZGUgYW55IGFjdGl2aXR5IGZyb20gdGhlIGJlZ2lubmluZyBvZiB0aGUgY2FtcGFpZ24uIAojIC0gVGhlIGVzdGltYXRlZCBzdGFydCB3aWxsIGJlIDFzdCBKYW51YXJ5IDIwMTguCgojIC0gR3VpZGVkIFRvdXIgbmFtZXMKIyAtIChUaGUgdHJhaW5pbmcgbW9kdWxlcyBpbmNsdWRlIDIgbmV3IGd1aWRlZCB0b3Vycyk6CiMgLSA/dG91cj1kaXNrdXRpZXJlbgojIC0gP3RvdXI9c2VpbXV0aWcKCiMjIyAtLS0gVHJhaW5pbmcgTW9kdWxlcyBTY2hlbWE6IAojIyMgLS0tIGh0dHBzOi8vbWV0YS53aWtpbWVkaWEub3JnL3dpa2kvVXNlcjpTdGVmYW5fU2NobmVpZGVyXyhXTURFKS9kYXNoYm9hcmRfbGlicmFyaWVzL3dpa2lwZWRpYS1rdXJzZS5qc29uCiMjIyAtLS0gdGhlIHNsdWcgZmllbGQgaXMgcmVsZXZhbnQgZm9yIHRyYWNraW5nCgojIyMgLS0tIFNldHVwCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShkYXRhLnRhYmxlKQoKIyMjIC0tLSBEaXJlY3RvcmllcwpiYW5uZXJJbXByZXNzaW9uc0RpciA8LSAnL2hvbWUvZ29yYW5zbS9SU2NyaXB0cy9XTURFX0NhbXBhaWducy9UaGFua1lvdTIwMTgvQmFubmVySW1wcmVzc2lvbnMnCmJhbm5lckNsaWNrc0RpciA8LSAnL2hvbWUvZ29yYW5zbS9SU2NyaXB0cy9XTURFX0NhbXBhaWducy9UaGFua1lvdTIwMTgvQmFubmVyQ2xpY2tzJwpkYWlseVVwZGF0ZURpciA8LSAnL2hvbWUvZ29yYW5zbS9SU2NyaXB0cy9XTURFX0NhbXBhaWducy9UaGFua1lvdTIwMTgvVGhhbmtZb3UyMDE4X0RhaWx5VXBkYXRlJyAKCiMjIyAtLS0gQ2FtcGFpZ24gdGltZSByYW5nZQpzdGFydERhdGUgPC0gJzIwMTgtMDEtMDInCmVuZERhdGUgPC0gJzIwMTgtMDEtMDgnCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gUzEuIEJhbm5lciBJbXByZXNzaW9uIERhdGEKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIGNhbXBhaWduIHRhZwojIC0gTmFtZTogYnQxLCA/Y2FtcGFpZ249d21kZV9ldGMyMDE3X2J0MQoKIyMjIC0tLSBsb29wIG92ZXIgZGF0ZSByYW5nZSwgY3JlYXRlIHF1ZXJ5LCBmZXRjaCwgYW5kIHN0b3JlCmRhdGVSYW5nZSA8LSBzZXEuUE9TSVh0KGZyb20gPSBhcy5QT1NJWGx0KHN0YXJ0RGF0ZSwgdHogPSAiQ0VUIiksCiAgICAgICAgICAgICAgICAgICAgICAgIHRvID0gYXMuUE9TSVhsdChlbmREYXRlLCB0eiA9ICJDRVQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAnaG91cicpCmRhdGVSYW5nZSA8LSBkYXRlUmFuZ2VbLWxlbmd0aChkYXRlUmFuZ2UpXQpjZXREYXRlUmFuZ2UgPC0gYXMuY2hhcmFjdGVyKGRhdGVSYW5nZSkKY2V0RGF0ZVJhbmdlIDwtIHNhcHBseShjZXREYXRlUmFuZ2UsIGZ1bmN0aW9uKHgpIHsKICBzdHJzcGxpdCh4LCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKVtbMV1dWzFdCn0pCm5hbWVzKGRhdGVSYW5nZSkgPC0gY2V0RGF0ZVJhbmdlCmRhdGVSYW5nZSA8LSBhcy5QT1NJWGx0KGRhdGVSYW5nZSwgdHogPSAiVVRDIikKIyAtIHVwIHRvIHRoZSBjYW1wYWlnbiBlbmQ6CmVuZENhbXBhaWduIDwtIGFzLlBPU0lYbHQoZW5kRGF0ZSwgdHogPSAiVVRDIikKdyA8LSB3aGljaChkYXRlUmFuZ2UgPiBlbmRDYW1wYWlnbikKaWYgKGxlbmd0aCh3KSA+IDApIHsKICBkYXRlUmFuZ2UgPC0gZGF0ZVJhbmdlWy13XQp9CmRSIDwtIGxpc3QoKQpmb3IgKGkgaW4gMTpsZW5ndGgoZGF0ZVJhbmdlKSkgewogIGRSW1tpXV0gPC0gZGF0YS5mcmFtZSgKICAgIGNldE5hbWUgPSBuYW1lcyhkYXRlUmFuZ2VbaV0pLAogICAgdXRjWWVhciA9IHllYXIoZGF0ZVJhbmdlW2ldKSwKICAgIHV0Y01vbnRoID0gbW9udGgoZGF0ZVJhbmdlW2ldKSwKICAgIHV0Y0RheSA9IG1kYXkoZGF0ZVJhbmdlW2ldKSwKICAgIHV0Y0hvdXIgPSBob3VyKGRhdGVSYW5nZVtpXSkKICApCn0KZFIgPC0gcmJpbmRsaXN0KGRSKQpkUiA8LSBkUiAlPiUKICBncm91cF9ieShjZXROYW1lLCB1dGNZZWFyLCB1dGNNb250aCwgdXRjRGF5KSAlPiUKICBzdW1tYXJpc2UodXRjSG91ciA9IHBhc3RlKCJob3VyPSIsIHV0Y0hvdXIsIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiIikpCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gUzIuIEJhbm5lciBMYW5kaW5nIFBhZ2UgRGF0YQojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0gbGFuZGluZyBwYWdlIGxpbmsgaW5jbHVkaW5nIHRoZSBhcHByb3ByaWF0ZSBjYW1wYWlnbiB0YWcKIyAtIExpbms6aHR0cHM6Ly9kZS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9MZXJuZVdpa2lwZWRpYT9jYW1wYWlnbj13bWRlX2V0YzIwMTdfYnQxCgojIC0gc2V0IGJhbm5lckNsaWNrc0RpcgpzZXR3ZChiYW5uZXJDbGlja3NEaXIpCgpmb3IgKGkgaW4gMTpsZW5ndGgodW5pcXVlKGRSJGNldE5hbWUpKSkgewogIAogIHdDZXROYW1lIDwtIHdoaWNoKGRSJGNldE5hbWUgJWluJSB1bmlxdWUoZFIkY2V0TmFtZSlbaV0pCiAgCiAgZm9yIChqIGluIDE6bGVuZ3RoKHdDZXROYW1lKSkgewogICAgCiAgICAjIC0gY29uc3RydWN0IEhpdmVRTCBxdWVyeToKICAgIHkgPC0gZFIkdXRjWWVhclt3Q2V0TmFtZVtqXV0KICAgIG0gPC0gZFIkdXRjTW9udGhbd0NldE5hbWVbal1dCiAgICBkIDwtIGRSJHV0Y0RheVt3Q2V0TmFtZVtqXV0KICAgIGhvdXIgPC0gZFIkdXRjSG91clt3Q2V0TmFtZVtqXV0KICAgIHEgPC0gcGFzdGUoCiAgICAgICJVU0Ugd21mOwogICAgICBTRUxFQ1QgdXJpX3BhdGgsIHVyaV9xdWVyeSwgcmVmZXJlciBGUk9NIHdlYnJlcXVlc3QKICAgICAgV0hFUkUgdXJpX2hvc3QgPSAnZGUud2lraXBlZGlhLm9yZycKICAgICAgQU5EIHVyaV9wYXRoID0gJy93aWtpL1dpa2lwZWRpYTpXaWtpbWVkaWFfRGV1dHNjaGxhbmQvTGVybmVXaWtpcGVkaWEnIAogICAgICBBTkQgeWVhciA9ICIsIHksCiAgICAgICIgQU5EIG1vbnRoID0gIiwgbSwKICAgICAgIiBBTkQgZGF5ID0gIiwgZCwKICAgICAgIiBBTkQgKCIsIGhvdXIsICIpOyIsCiAgICAgIHNlcCA9ICIiKQogICAgIyAtIHdyaXRlIGhxbAogICAgd3JpdGUocSwgJ3RoYW5reW91MjAxOF9CYW5uZXJDbGlja3MuaHFsJykKICAgICMgLSBwcmVwYXJlIG91dHB1dCBmaWxlOgogICAgZmlsZU5hbWUgPC0gInRoYW5reW91MjAxOF9CYW5uZXJDbGlja3NfIgogICAgZmlsZU5hbWUgPC0gcGFzdGUwKGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3Rlcih1bmlxdWUoZFIkY2V0TmFtZSlbaV0pLAogICAgICAgICAgICAgICAgICAgICAgICJfIiwgaiwKICAgICAgICAgICAgICAgICAgICAgICAiLnRzdiIpCiAgICBmaWxlTmFtZSA8LSBwYXN0ZTAoYmFubmVyQ2xpY2tzRGlyLCAiLyIsIGZpbGVOYW1lKQogICAgIyAtIGV4ZWN1dGUgaHFsIHNjcmlwdDoKICAgIGhpdmVBcmdzIDwtCiAgICAgICdiZWVsaW5lIC1mJwogICAgaGl2ZUlucHV0IDwtIHBhc3RlMCgndGhhbmt5b3UyMDE4X0Jhbm5lckNsaWNrcy5ocWwgPiAnLAogICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSkKICAgICMgLSBjb21tYW5kOgogICAgaGl2ZUNvbW1hbmQgPC0gcGFzdGUoaGl2ZUFyZ3MsIGhpdmVJbnB1dCkKICAgIHN5c3RlbShjb21tYW5kID0gaGl2ZUNvbW1hbmQsIHdhaXQgPSBUUlVFKQogICAgCiAgfQogIAp9CgojIyMgLS0tIFdyYW5nbGUgdGhpcyBkYXRhc2V0OgoKIyMjIC0tLSBCYW5uZXIgdGFnczoKY2FtcGFpZ25CYW5uZXIgPC0gJ3dtZGVfZXRjMjAxN19idDEnCgojIyMgLS0tIERhdGFzZXQ6CiMgLSBjb3VudCBub24tZW1wdHkgZmlsZXM6CmMgPC0gMApsRiA8LSBsaXN0LmZpbGVzKCkKbEYgPC0gbEZbZ3JlcGwoJy50c3YnLCBsRiwgZml4ZWQgPSBUKV0KbEYgPC0gbEZbZ3JlcGwoJ0NsaWNrcycsIGxGLCBmaXhlZCA9IFQpXQpkYXRhU2V0IDwtIGxpc3QoKQpmb3IgKGkgaW4gMTpsZW5ndGgobEYpKSB7CiAgZFMgPC0gcmVhZExpbmVzKGxGW2ldLCBuID0gLTEpCiAgdGltZVN0YW1wIDwtIHN0cnNwbGl0KGxGW2ldLCBzcGxpdCA9ICJfIilbWzFdXVszXQogIGJhbm5lckNsaWNrcyA8LSBzdW0oZ3JlcGwoY2FtcGFpZ25CYW5uZXIsIGRTLCBmaXhlZCA9IFQpKQogIGRhdGFTZXRbW2ldXSA8LSBkYXRhLmZyYW1lKHRpbWVzdGFtcCA9IHRpbWVTdGFtcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYW5uZXJDbGlja3MgPSBiYW5uZXJDbGlja3MsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCn0KZGF0YVNldCA8LSByYmluZGxpc3QoZGF0YVNldCkKCiMjIyAtLS0gc3RvcmUgQmFubmVyQ2xpY2tzUGFnZVZpZXdzX1VwZGF0ZS5jc3YKc2V0d2QoZGFpbHlVcGRhdGVEaXIpCndyaXRlLmNzdihkYXRhU2V0LCBmaWxlID0gInRoYW5reW91MjAxOF9CYW5uZXJDbGlja3NQYWdlVmlld3NfVXBkYXRlLmNzdiIpCgojIyMgLS0tIFNRTApzdGFydERhdGUgPC0gJzIwMTgtMDEtMDEnCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gUzMuIFVzZXIgUmVnaXN0cmF0aW9uIERhdGEKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIE5PVEU6IFVUQyB0aW1lc3RhbXBzIC0gYWRqdXN0bWVudCBmb3IgQ0UoUylUIGludHJvZHVjZWQuIAojIC0gU2VydmVyU2lkZUFjY291bnRDcmVhdGlvbl81NDg3MzQ1CnFDb21tYW5kIDwtIHBhc3RlKCJteXNxbCAtLWRlZmF1bHRzLWZpbGU9L2V0Yy9teXNxbC9jb25mLmQvYW5hbHl0aWNzLXJlc2VhcmNoLWNsaWVudC5jbmYgLWggYW5hbHl0aWNzLXNsYXZlLmVxaWFkLndtbmV0IC1BIC1lIFwic2VsZWN0ICogZnJvbSBsb2cuU2VydmVyU2lkZUFjY291bnRDcmVhdGlvbl81NDg3MzQ1IHdoZXJlICgod2ViSG9zdCA9ICdkZS53aWtpcGVkaWEub3JnJykgYW5kICh0aW1lc3RhbXAgPj0gIiwgZ3N1YigiLSIsICIiLCBzdGFydERhdGUsIGZpeGVkID0gVCksICIyMjAwMDApKTtcIiA+ICIsCiAgICAgICAgICAgIGRhaWx5VXBkYXRlRGlyLCAiL3RoYW5reW91XzIwMThfdXNlclJlZ2lzdHJhdGlvbnMudHN2Iiwgc2VwID0gIiIpCnN5c3RlbShjb21tYW5kID0gcUNvbW1hbmQsIHdhaXQgPSBUUlVFKQoKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTNC4gR3VpZGVkIFRvdXJzIERhdGEKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIE5PVEU6IFVUQyB0aW1lc3RhbXBzIC0gYWRqdXN0bWVudCBmb3IgQ0UoUylUIGludHJvZHVjZWQuIAojIC0gU2VydmVyU2lkZUFjY291bnRDcmVhdGlvbl81NDg3MzQ1CnFDb21tYW5kIDwtIHBhc3RlKCJteXNxbCAtLWRlZmF1bHRzLWZpbGU9L2V0Yy9teXNxbC9jb25mLmQvYW5hbHl0aWNzLXJlc2VhcmNoLWNsaWVudC5jbmYgLWggYW5hbHl0aWNzLXNsYXZlLmVxaWFkLndtbmV0IC1BIC1lIFwic2VsZWN0ICogZnJvbSBsb2cuR3VpZGVkVG91ckV4aXRlZF84NjkwNTY2IHdoZXJlICgod2ViSG9zdCA9ICdkZS53aWtpcGVkaWEub3JnJykgYW5kICh0aW1lc3RhbXAgPj0gIiwgCiAgICAgICAgICAgICAgICAgIGdzdWIoIi0iLCAiIiwgc3RhcnREYXRlLCBmaXhlZCA9IFQpLCAiMjIwMDAwKSk7XCIgPiAiLCAKICAgICAgICAgICAgICAgICAgZGFpbHlVcGRhdGVEaXIsICIvdGhhbmt5b3VfMjAxOF9ndWlkZWRUb3Vycy50c3YiLCBzZXAgPSAiIikKc3lzdGVtKGNvbW1hbmQgPSBxQ29tbWFuZCwgd2FpdCA9IFRSVUUpCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gUzUuIFVzZXIgRWRpdCBEYXRhCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLSBnZXQgdXNlciBJRHMgZnJvbSByZWdpc3RlcmVkOgpsRiA8LSBsaXN0LmZpbGVzKCkKbEYgPC0gbEZbZ3JlcGwoJ3VzZXJSZWdpc3RyYXRpb25zJywgbEYsIGZpeGVkID0gVCldCnVzZXJSZWcgPC0gcmVhZC50YWJsZShsRiwgCiAgICAgICAgICAgICAgICAgICAgICBxdW90ZSA9ICIiLAogICAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IiwKICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKdXNlclJlZyA8LSB1c2VyUmVnICU+JSAKICBkcGx5cjo6c2VsZWN0KGV2ZW50X3VzZXJJZCwgZXZlbnRfaXNTZWxmTWFkZSwgZXZlbnRfY2FtcGFpZ24pICU+JSAKICBmaWx0ZXIoZXZlbnRfaXNTZWxmTWFkZSA9PSAxKSAlPiUgCiAgZmlsdGVyKGV2ZW50X2NhbXBhaWduICVpbiUgIndtZGVfZXRjMjAxN19idDEiKQojIC0gdWlkczoKdWlkIDwtIHVzZXJSZWckZXZlbnRfdXNlcklkCiMgLSBzcWwgcXVlcnkKc3FsUXVlcnkgPC0gcGFzdGUoJ1NFTEVDVCBDT1VOVCgqKSBhcyBlZGl0cywgcmV2X3VzZXIgRlJPTSByZXZpc2lvbiBXSEVSRSByZXZfdXNlciBJTiAoJywKICAgICAgICAgICAgICAgICAgcGFzdGUodWlkLCBjb2xsYXBzZSA9ICIsICIpLAogICAgICAgICAgICAgICAgICAnKSBHUk9VUCBCWSByZXZfdXNlcjsnLAogICAgICAgICAgICAgICAgICBzZXAgPSAiIikKbXlTcWxDb21tYW5kIDwtIHBhc3RlKCdteXNxbCAtaCBhbmFseXRpY3Mtc3RvcmUuZXFpYWQud21uZXQgZGV3aWtpIC1lICcsCiAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgnIicsIHNxbFF1ZXJ5LCAnIiA+ICcsIHNlcCA9ICIiKSwgCiAgICAgICAgICAgICAgICAgICAgICBkYWlseVVwZGF0ZURpciwgJy90aGFua3lvdV8yMDE4X3VzZXJFZGl0cy50c3YnLCBzZXAgPSAiIikKc3lzdGVtKGNvbW1hbmQgPSBteVNxbENvbW1hbmQsIAogICAgICAgd2FpdCA9IFRSVUUpCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gUzYuIFRyYWluaW5nIE1vZHVsZSBEYXRhCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmBgYAoKIyMgMS4gQ2FtcGFpZ24gUGFnZXZpZXdzIChCYW5uZXIgQ2xpY2tzKQoKIyMjIDEuIDEgVGhlIGRhdGEgc2V0CgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CnBhZ2V2aWV3cyA8LSByZWFkLmNzdigndGhhbmt5b3UyMDE4X0Jhbm5lckNsaWNrc1BhZ2VWaWV3c19VcGRhdGUuY3N2JywKICAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnBhZ2V2aWV3cyA8LSBwYWdldmlld3MgJT4lIAogIGdyb3VwX2J5KHRpbWVzdGFtcCkgJT4lIAogIHN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oYmFubmVyQ2xpY2tzKSkKa25pdHI6OmthYmxlKHBhZ2V2aWV3cywgZm9ybWF0ID0gImh0bWwiKSAlPiUgCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAibGVmdCIpCmBgYAoKIyMjIDEuIDIgQ2hhcnQKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KZ2dwbG90KHBhZ2V2aWV3cywgYWVzKHggPSB0aW1lc3RhbXAsCiAgICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFBhZ2V2aWV3cykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgCiAgICAgICAgICAgcG9zaXRpb24gPSAiZG9kZ2UiLCAKICAgICAgICAgICB3aWR0aCA9IC4xNSwgCiAgICAgICAgICAgZmlsbCA9ICJ3aGl0ZSIsIAogICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIikgKwogIGdndGl0bGUoJ1RoYW5rIFlvdSAyMDE4OlxuT3ZlcnZpZXcgb2YgTGFuZGluZyBQYWdldmlld3MnKSArCiAgZ2VvbV9sYWJlbChzaXplID0gMykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4LCBoanVzdCA9IDEpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgoKIyMgMi4gQ2FtcGFpZ24gVXNlciBSZWdpc3RyYXRpb25zCgojIyMgMi4gMSBUaGUgZGF0YSBzZXQKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KdXNlclJlZyA8LSByZWFkLmRlbGltKCd0aGFua3lvdV8yMDE4X3VzZXJSZWdpc3RyYXRpb25zLnRzdicsCiAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKdXNlclJlZyR0aW1lc3RhbXAgPC0gYXMuY2hhcmFjdGVyKHVzZXJSZWckdGltZXN0YW1wKQp1c2VyUmVnJHRpbWVzdGFtcCA8LSBzYXBwbHkodXNlclJlZyR0aW1lc3RhbXAsIGZ1bmN0aW9uKHgpIHsKICB5IDwtIHN1YnN0cih4LCAxLCA0KQogIG0gPC0gc3Vic3RyKHgsIDUsIDYpCiAgZCA8LSBzdWJzdHIoeCwgNywgOCkKICBwYXJ0MURhdGUgPC0gcGFzdGUoeSwgbSwgZCwgc2VwID0gIi0iKQogIGhyIDwtIHN1YnN0cih4LCA5LCAxMCkKICBtaSA8LSBzdWJzdHIoeCwgMTEsIDEyKQogIHNlIDwtIHN1YnN0cih4LCAxMywgMTQpCiAgcGFydDJEYXRlIDwtIHBhc3RlKGhyLCBtaSwgc2UsIHNlcCA9ICI6IikKICBwYXN0ZShwYXJ0MURhdGUsIHBhcnQyRGF0ZSwgc2VwID0gIiAiKQp9KQp1c2VyUmVnJHRpbWVzdGFtcCA8LSBhcy5QT1NJWGN0KHVzZXJSZWckdGltZXN0YW1wLCB0eiA9ICJVVEMiKQp0aW1lRGlmZiA8LSAKICBhcy5QT1NJWGN0KGFzLmNoYXJhY3RlcihTeXMudGltZSgpKSwgdHogPSAiVVRDIikgLSBhcy5QT1NJWGN0KGFzLmNoYXJhY3RlcihTeXMudGltZSgpKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpCnVzZXJSZWckdGltZXN0YW1wIDwtIGFzLmNoYXJhY3Rlcih1c2VyUmVnJHRpbWVzdGFtcCArIHRpbWVEaWZmKQp1c2VyUmVnJHRpbWVzdGFtcCA8LSBzYXBwbHkodXNlclJlZyR0aW1lc3RhbXAsIGZ1bmN0aW9uKHgpIHsKICB5IDwtIHN1YnN0cih4LCAxLCA0KQogIG0gPC0gc3Vic3RyKHgsIDYsIDcpCiAgZCA8LSBzdWJzdHIoeCwgOSwgMTApIAogIHBhc3RlKHksIG0sIGQsIHNlcCA9ICItIikKfSkKdXNlclJlZyA8LSB1c2VyUmVnICU+JQogIGZpbHRlcihldmVudF9jYW1wYWlnbiAlaW4lICd3bWRlX2V0YzIwMTdfYnQxJykgJT4lIAogIGdyb3VwX2J5KHRpbWVzdGFtcCkgJT4lIAogIHN1bW1hcmlzZShSZWdpc3RyYXRpb25zID0gbigpKQprbml0cjo6a2FibGUodXNlclJlZywgZm9ybWF0ID0gImh0bWwiKSAlPiUgCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAibGVmdCIpCmBgYAoKIyMjIDIuIDIgQ2hhcnQKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KZ2dwbG90KHVzZXJSZWcsIGFlcyh4ID0gdGltZXN0YW1wLAogICAgICAgICAgICAgICAgICAgIHkgPSBSZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUmVnaXN0cmF0aW9ucykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgCiAgICAgICAgICAgcG9zaXRpb24gPSAiZG9kZ2UiLCAKICAgICAgICAgICB3aWR0aCA9IC4xNSwgCiAgICAgICAgICAgZmlsbCA9ICJ3aGl0ZSIsIAogICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIikgKwogIGdndGl0bGUoJ1RoYW5rIFlvdSAyMDE4OlxuT3ZlcnZpZXcgb2YgVXNlciBSZWdpc3RyYXRpb25zJykgKwogIGdlb21fbGFiZWwoc2l6ZSA9IDMpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCwgaGp1c3QgPSAxKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMgMy4gQ2FtcGFpZ24gVXNlciBFZGl0cwoKIyMjIDMuIDEgVGhlIGRhdGEgc2V0CgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CnVzZXJFZGl0cyA8LSByZWFkTGluZXMoJ3RoYW5reW91XzIwMThfdXNlckVkaXRzLnRzdicsIG4gPSAtMSkKaWYgKGxlbmd0aCh1c2VyRWRpdHMpID49IDEpIHsKICB1c2VyRWRpdHMgPC0gcmVhZC5kZWxpbSgndGhhbmt5b3VfMjAxOF91c2VyRWRpdHMudHN2JywKICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgIyAtIHJlcG9ydAogIHByaW50KHBhc3RlMChzdW0odXNlckVkaXRzJGVkaXRzKSwgIiBlZGl0cyB3ZXJlIG1hZGUgYnkgdGhlIGNhbXBhaWduIHJlZ2lzdGVyZWQgdXNlcnMgdGh1cyBmYXIuIikpCn0gZWxzZSB7CiAgcHJpbnQoIlRoZXJlIGFyZSBjdXJyZW50bHkgbm8gdXNlciBlZGl0cyBmcm9tIHRoaXMgY2FtcGFpZ24uIikKfQpgYGAKCiMjIyAzLiAyIENoYXJ0CgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CmBgYAoKIyMgNC4gQ2FtcGFpZ24gR3VpZGVkIFRvdXJzCgojIyMgNC4gMSBUaGUgZGF0YSBzZXQKCkNhbXBhaWduIGd1aWRlZCB0b3VyczogYGRpc2t1dGllcmVuYCBhbmQgYHNlaW11dGlnYC4KCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KdG91cnMgPC0gYygnZGlza3V0aWVyZW4nLCAnc2VpbXV0aWcnKQojIHNldHdkKCcvaG9tZS9nb3JhbnNtL1dvcmsvX19fRGF0YUtvbGVrdGl2L1Byb2plY3RzL1dpa2ltZWRpYURFVS9fV01ERV9Qcm9qZWN0cy9fbWlzYy9OZXdFZGl0b3JzX1RlYW0vVGhhbmtfWW91X0NhbXBhaWduXzIwMTgvX2RhaWx5VXBkYXRlLycpCmdUb3VycyA8LSByZWFkLmRlbGltKCd0aGFua3lvdV8yMDE4X2d1aWRlZFRvdXJzLnRzdicsCiAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpnVG91cnMkdGltZXN0YW1wIDwtIGFzLmNoYXJhY3RlcihnVG91cnMkdGltZXN0YW1wKQpnVG91cnMkdGltZXN0YW1wIDwtIHNhcHBseShnVG91cnMkdGltZXN0YW1wLCBmdW5jdGlvbih4KSB7CiAgeSA8LSBzdWJzdHIoeCwgMSwgNCkKICBtIDwtIHN1YnN0cih4LCA1LCA2KQogIGQgPC0gc3Vic3RyKHgsIDcsIDgpCiAgcGFydDFEYXRlIDwtIHBhc3RlKHksIG0sIGQsIHNlcCA9ICItIikKICBociA8LSBzdWJzdHIoeCwgOSwgMTApCiAgbWkgPC0gc3Vic3RyKHgsIDExLCAxMikKICBzZSA8LSBzdWJzdHIoeCwgMTMsIDE0KQogIHBhcnQyRGF0ZSA8LSBwYXN0ZShociwgbWksIHNlLCBzZXAgPSAiOiIpCiAgcGFzdGUocGFydDFEYXRlLCBwYXJ0MkRhdGUsIHNlcCA9ICIgIikKfSkKZ1RvdXJzJHRpbWVzdGFtcCA8LSBhcy5QT1NJWGN0KGdUb3VycyR0aW1lc3RhbXAsIHR6ID0gIlVUQyIpCnRpbWVEaWZmIDwtIAogIGFzLlBPU0lYY3QoYXMuY2hhcmFjdGVyKFN5cy50aW1lKCkpLCB0eiA9ICJVVEMiKSAtIGFzLlBPU0lYY3QoYXMuY2hhcmFjdGVyKFN5cy50aW1lKCkpLCB0eiA9ICJFdXJvcGUvQmVybGluIikKZ1RvdXJzJHRpbWVzdGFtcCA8LSBhcy5jaGFyYWN0ZXIoZ1RvdXJzJHRpbWVzdGFtcCArIHRpbWVEaWZmKQpnVG91cnMkdGltZXN0YW1wIDwtIHNhcHBseShnVG91cnMkdGltZXN0YW1wLCBmdW5jdGlvbih4KSB7CiAgeSA8LSBzdWJzdHIoeCwgMSwgNCkKICBtIDwtIHN1YnN0cih4LCA2LCA3KQogIGQgPC0gc3Vic3RyKHgsIDksIDEwKSAKICBwYXN0ZSh5LCBtLCBkLCBzZXAgPSAiLSIpCn0pCiMgLSBsb29rIHVwIGZvciB0aGUgY2FtcGFpZ24gZ3VpZGVkIHRvdXJzCncgPC0gd2hpY2goZ1RvdXJzJGV2ZW50X3RvdXIgJWluJSB0b3VycykKaWYgKGxlbmd0aCh3KSA+IDApIHsKICBnVG91cnMgPC0gZ1RvdXJzW3csIF0KICBnVG91cnMgPC0gZ1RvdXJzICU+JSAKICAgIHNlbGVjdCh0aW1lc3RhbXAsIGV2ZW50X3N0ZXAsIGV2ZW50X3VzZXJJZCkKICBrbml0cjo6a2FibGUoZ1RvdXJzWywgYyh0aW1lc3RhbXAsICJldmVudF9zdGVwIildLCBmb3JtYXQgPSAiaHRtbCIpICU+JSAKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGLCBwb3NpdGlvbiA9ICJsZWZ0IikKfSBlbHNlIHsKICBwcmludCgiVGhlcmUgYXJlIGN1cnJlbnRseSBubyBkYXRhIG9uIGd1aWRlZCB0b3VycyBmcm9tIHRoaXMgY2FtcGFpZ24uIikKfQpgYGAKCiMjIyA0LiAyIENoYXJ0CgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CmBgYAo=