The campaign is run from 1. January 2018 to N January 2018.
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())
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=