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
### ------------------------------------------------------------
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
|
1284
|
2018-01-11
|
0
|
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",
header = T,
row.names = 1,
stringsAsFactors = F)
EOF within quoted string
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
|
4
|
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: 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_638086
|
2018-01-03
|
seimutig
|
returnToTraining
|
u_638086
|
2018-01-04
|
seimutig
|
boldness
|
u_2937302
|
2018-01-04
|
seimutig
|
positionCursor
|
u_2937302
|
2018-01-04
|
diskutieren
|
returnToTraining
|
u_2937302
|
2018-01-05
|
diskutieren
|
saveReply
|
u_7828514
|
2018-01-04
|
seimutig
|
returnToTraining
|
u_2937302
|
2018-01-04
|
seimutig
|
editButtonCitation
|
u_2937302
|
2018-01-06
|
seimutig
|
editButton
|
u_6495795
|
2018-01-06
|
seimutig
|
boldness
|
u_6495795
|
2018-01-06
|
diskutieren
|
firstMessage
|
u_6495795
|
2018-01-08
|
seimutig
|
editBoldness
|
u_4296245
|
2018-01-08
|
seimutig
|
editSummary
|
u_4296245
|
2018-01-08
|
diskutieren
|
returnToTraining
|
u_4296245
|
2018-01-10
|
seimutig
|
editButton
|
u_4773138
|
2018-01-10
|
seimutig
|
editButton
|
u_4773138
|
2018-01-10
|
seimutig
|
editBoldness
|
u_4773138
|
2018-01-10
|
seimutig
|
editSummary
|
u_4773138
|
2018-01-10
|
seimutig
|
editButtonCitation
|
u_4773138
|
2018-01-10
|
seimutig
|
saveCitation
|
u_4773138
|
2018-01-10
|
seimutig
|
editButton
|
u_4773138
|
2018-01-10
|
diskutieren
|
returnToTraining
|
u_4773138
|
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
|
6
|
seimutig
|
16
|
LS0tCnRpdGxlOiAnVGhhbmsgWW91IENhbXBhaWduIDIwMTg6IFJlcG9ydCcKYXV0aG9yOiAiR29yYW4gUy4gTWlsb3Zhbm92aWMsIERhdGEgQW5hbHlzdCwgV01ERSIKZGF0ZTogIkphbnVhcnksIDIwMTgiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0aGVtZTogc2ltcGxleAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDUKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQotLS0KCgoqKkZlZWRiYWNrKiogc2hvdWxkIGJlIHNlbmQgdG8gYGdvcmFuLm1pbG92YW5vdmljX2V4dEB3aWtpbWVkaWEuZGVgLiAKClRoZSBjYW1wYWlnbiBpcyBydW4gZnJvbSAxLiBKYW51YXJ5IDIwMTggdG8gTiBKYW51YXJ5IDIwMTguCgpgYGB7ciBzZXR1cH0Ka25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAnL2hvbWUvZ29yYW5zbS9Xb3JrL19fX0RhdGFLb2xla3Rpdi9Qcm9qZWN0cy9XaWtpbWVkaWFERVUvX1dNREVfUHJvamVjdHMvX21pc2MvTmV3RWRpdG9yc19UZWFtL1RoYW5rX1lvdV9DYW1wYWlnbl8yMDE4L19kYWlseVVwZGF0ZS8nKQpgYGAKCmBgYHtyLCBlY2hvID0gRiwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCByZXN1bHRzID0gJ2hpZGUnfQojICFkaWFnbm9zdGljcyBvZmYKIyMjIC0tLSBTZXR1cAprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQgPSA4KSAKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoRFQpCmxpYnJhcnkocmVzaGFwZTIpCmBgYAoKIyMgMC4gRGF0YSBBY3F1aXNpdG9uCgoqKk5PVEU6KiogdGhlIERhdGEgQWNxdWlzaXRpb24gY29kZSBjaHVuayBpcyBub3QgZnVsbHkgcmVwcm9kdWNpYmxlIGZyb20gdGhpcyBSZXBvcnQuIFRoZSBkYXRhIGFyZSBjb2xsZWN0ZWQgYnkgcnVubmluZyB0aGUgc2NyaXB0IGBUaGFua1lvdV8yMDE4X1Byb2R1Y3Rpb25fU1FMLlJgIG9uIHN0YXQxMDA1LmVxaWFkLndtbmV0LCBjb2xsZWN0aW5nIHRoZSBkYXRhIGFzIGAudHN2YCBhbmQgYC5jc3ZgIGZpbGVzLCBjb3B5aW5nIG1hbnVhbGx5LCBhbmQgcHJvY2Vzc2luZyBsb2NhbGx5LiBSdW4gZnJvbSBzdGF0MTAwNSBzdGF0IGJveCBieSBleGVjdXRpbmcgYFJzY3JpcHQgL2hvbWUvZ29yYW5zbS9SU2NyaXB0cy9XTURFX0NhbXBhaWducy9UaGFua1lvdTIwMTgvVGhhbmtZb3VfMjAxOF9Qcm9kdWN0aW9uX1NRTC5SYC4KCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZ9CgojIyMgLS0tIGZyb20gc3RhdDEwMDU6IFRoYW5rIFlvdSAyMDE4IEJhbm5lciBDYW1wYWlnbgojIyMgLS0tIHByb2R1Y3Rpb24gc2NyaXB0OiBmZXRjaCB0aGUgY2FtcGFpZ24gZGF0YSBzZXRzCgojIyMgLS0tIENhbXBhaWduIERldGFpbHM6IAojIC0gZXN0aW1hdGVkIHN0YXJ0OiAxc3QgSmFudWFyeSAyMDE4ICgrLy0gMiBkYXlzKQojIC0gZXN0aW1hdGVkIGR1cmF0aW9uOiA2IHRvIDEwIGRheXMKIyAtIFJlcG9ydGluZyBzaG91bGQgc3RhcnQgb24gMm5kIEphbnVhcnkgMjAxOC4gCiMgLSBUaGUgcmVwb3J0IG11c3QgaW5jbHVkZSBhbnkgYWN0aXZpdHkgZnJvbSB0aGUgYmVnaW5uaW5nIG9mIHRoZSBjYW1wYWlnbi4gCiMgLSBUaGUgZXN0aW1hdGVkIHN0YXJ0IHdpbGwgYmUgMXN0IEphbnVhcnkgMjAxOC4KCiMgLSBHdWlkZWQgVG91ciBuYW1lcwojIC0gKFRoZSB0cmFpbmluZyBtb2R1bGVzIGluY2x1ZGUgMiBuZXcgZ3VpZGVkIHRvdXJzKToKIyAtID90b3VyPWRpc2t1dGllcmVuCiMgLSA/dG91cj1zZWltdXRpZwoKIyMjIC0tLSBUcmFpbmluZyBNb2R1bGVzIFNjaGVtYTogCiMjIyAtLS0gaHR0cHM6Ly9tZXRhLndpa2ltZWRpYS5vcmcvd2lraS9Vc2VyOlN0ZWZhbl9TY2huZWlkZXJfKFdNREUpL2Rhc2hib2FyZF9saWJyYXJpZXMvd2lraXBlZGlhLWt1cnNlLmpzb24KIyMjIC0tLSB0aGUgc2x1ZyBmaWVsZCBpcyByZWxldmFudCBmb3IgdHJhY2tpbmcKCiMjIyAtLS0gU2V0dXAKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGRhdGEudGFibGUpCgojIyMgLS0tIERpcmVjdG9yaWVzCmJhbm5lckltcHJlc3Npb25zRGlyIDwtICcvaG9tZS9nb3JhbnNtL1JTY3JpcHRzL1dNREVfQ2FtcGFpZ25zL1RoYW5rWW91MjAxOC9CYW5uZXJJbXByZXNzaW9ucycKYmFubmVyQ2xpY2tzRGlyIDwtICcvaG9tZS9nb3JhbnNtL1JTY3JpcHRzL1dNREVfQ2FtcGFpZ25zL1RoYW5rWW91MjAxOC9CYW5uZXJDbGlja3MnCmRhaWx5VXBkYXRlRGlyIDwtICcvaG9tZS9nb3JhbnNtL1JTY3JpcHRzL1dNREVfQ2FtcGFpZ25zL1RoYW5rWW91MjAxOC9UaGFua1lvdTIwMThfRGFpbHlVcGRhdGUnIAoKIyMjIC0tLSBDYW1wYWlnbiB0aW1lIHJhbmdlCnN0YXJ0RGF0ZSA8LSAnMjAxOC0wMS0wMicKZW5kRGF0ZSA8LSAnMjAxOC0wMS0wOCcKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTMS4gQmFubmVyIEltcHJlc3Npb24gRGF0YQojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0gY2FtcGFpZ24gdGFnCiMgLSBOYW1lOiBidDEsID9jYW1wYWlnbj13bWRlX2V0YzIwMTdfYnQxCgojIyMgLS0tIGxvb3Agb3ZlciBkYXRlIHJhbmdlLCBjcmVhdGUgcXVlcnksIGZldGNoLCBhbmQgc3RvcmUKZGF0ZVJhbmdlIDwtIHNlcS5QT1NJWHQoZnJvbSA9IGFzLlBPU0lYbHQoc3RhcnREYXRlLCB0eiA9ICJDRVQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgdG8gPSBhcy5QT1NJWGx0KGVuZERhdGUsIHR6ID0gIkNFVCIpLAogICAgICAgICAgICAgICAgICAgICAgICBieSA9ICdob3VyJykKZGF0ZVJhbmdlIDwtIGRhdGVSYW5nZVstbGVuZ3RoKGRhdGVSYW5nZSldCmNldERhdGVSYW5nZSA8LSBhcy5jaGFyYWN0ZXIoZGF0ZVJhbmdlKQpjZXREYXRlUmFuZ2UgPC0gc2FwcGx5KGNldERhdGVSYW5nZSwgZnVuY3Rpb24oeCkgewogIHN0cnNwbGl0KHgsIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpW1sxXV1bMV0KfSkKbmFtZXMoZGF0ZVJhbmdlKSA8LSBjZXREYXRlUmFuZ2UKZGF0ZVJhbmdlIDwtIGFzLlBPU0lYbHQoZGF0ZVJhbmdlLCB0eiA9ICJVVEMiKQojIC0gdXAgdG8gdGhlIGNhbXBhaWduIGVuZDoKZW5kQ2FtcGFpZ24gPC0gYXMuUE9TSVhsdChlbmREYXRlLCB0eiA9ICJVVEMiKQp3IDwtIHdoaWNoKGRhdGVSYW5nZSA+IGVuZENhbXBhaWduKQppZiAobGVuZ3RoKHcpID4gMCkgewogIGRhdGVSYW5nZSA8LSBkYXRlUmFuZ2VbLXddCn0KZFIgPC0gbGlzdCgpCmZvciAoaSBpbiAxOmxlbmd0aChkYXRlUmFuZ2UpKSB7CiAgZFJbW2ldXSA8LSBkYXRhLmZyYW1lKAogICAgY2V0TmFtZSA9IG5hbWVzKGRhdGVSYW5nZVtpXSksCiAgICB1dGNZZWFyID0geWVhcihkYXRlUmFuZ2VbaV0pLAogICAgdXRjTW9udGggPSBtb250aChkYXRlUmFuZ2VbaV0pLAogICAgdXRjRGF5ID0gbWRheShkYXRlUmFuZ2VbaV0pLAogICAgdXRjSG91ciA9IGhvdXIoZGF0ZVJhbmdlW2ldKQogICkKfQpkUiA8LSByYmluZGxpc3QoZFIpCmRSIDwtIGRSICU+JQogIGdyb3VwX2J5KGNldE5hbWUsIHV0Y1llYXIsIHV0Y01vbnRoLCB1dGNEYXkpICU+JQogIHN1bW1hcmlzZSh1dGNIb3VyID0gcGFzdGUoImhvdXI9IiwgdXRjSG91ciwgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIiKSkKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTMi4gQmFubmVyIExhbmRpbmcgUGFnZSBEYXRhCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLSBsYW5kaW5nIHBhZ2UgbGluayBpbmNsdWRpbmcgdGhlIGFwcHJvcHJpYXRlIGNhbXBhaWduIHRhZwojIC0gTGluazpodHRwczovL2RlLndpa2lwZWRpYS5vcmcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0xlcm5lV2lraXBlZGlhP2NhbXBhaWduPXdtZGVfZXRjMjAxN19idDEKCiMgLSBzZXQgYmFubmVyQ2xpY2tzRGlyCnNldHdkKGJhbm5lckNsaWNrc0RpcikKCmZvciAoaSBpbiAxOmxlbmd0aCh1bmlxdWUoZFIkY2V0TmFtZSkpKSB7CiAgCiAgd0NldE5hbWUgPC0gd2hpY2goZFIkY2V0TmFtZSAlaW4lIHVuaXF1ZShkUiRjZXROYW1lKVtpXSkKICAKICBmb3IgKGogaW4gMTpsZW5ndGgod0NldE5hbWUpKSB7CiAgICAKICAgICMgLSBjb25zdHJ1Y3QgSGl2ZVFMIHF1ZXJ5OgogICAgeSA8LSBkUiR1dGNZZWFyW3dDZXROYW1lW2pdXQogICAgbSA8LSBkUiR1dGNNb250aFt3Q2V0TmFtZVtqXV0KICAgIGQgPC0gZFIkdXRjRGF5W3dDZXROYW1lW2pdXQogICAgaG91ciA8LSBkUiR1dGNIb3VyW3dDZXROYW1lW2pdXQogICAgcSA8LSBwYXN0ZSgKICAgICAgIlVTRSB3bWY7CiAgICAgIFNFTEVDVCB1cmlfcGF0aCwgdXJpX3F1ZXJ5LCByZWZlcmVyIEZST00gd2VicmVxdWVzdAogICAgICBXSEVSRSB1cmlfaG9zdCA9ICdkZS53aWtpcGVkaWEub3JnJwogICAgICBBTkQgdXJpX3BhdGggPSAnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9MZXJuZVdpa2lwZWRpYScgCiAgICAgIEFORCB5ZWFyID0gIiwgeSwKICAgICAgIiBBTkQgbW9udGggPSAiLCBtLAogICAgICAiIEFORCBkYXkgPSAiLCBkLAogICAgICAiIEFORCAoIiwgaG91ciwgIik7IiwKICAgICAgc2VwID0gIiIpCiAgICAjIC0gd3JpdGUgaHFsCiAgICB3cml0ZShxLCAndGhhbmt5b3UyMDE4X0Jhbm5lckNsaWNrcy5ocWwnKQogICAgIyAtIHByZXBhcmUgb3V0cHV0IGZpbGU6CiAgICBmaWxlTmFtZSA8LSAidGhhbmt5b3UyMDE4X0Jhbm5lckNsaWNrc18iCiAgICBmaWxlTmFtZSA8LSBwYXN0ZTAoZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKHVuaXF1ZShkUiRjZXROYW1lKVtpXSksCiAgICAgICAgICAgICAgICAgICAgICAgIl8iLCBqLAogICAgICAgICAgICAgICAgICAgICAgICIudHN2IikKICAgIGZpbGVOYW1lIDwtIHBhc3RlMChiYW5uZXJDbGlja3NEaXIsICIvIiwgZmlsZU5hbWUpCiAgICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0OgogICAgaGl2ZUFyZ3MgPC0KICAgICAgJ2JlZWxpbmUgLWYnCiAgICBoaXZlSW5wdXQgPC0gcGFzdGUwKCd0aGFua3lvdTIwMThfQmFubmVyQ2xpY2tzLmhxbCA+ICcsCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lKQogICAgIyAtIGNvbW1hbmQ6CiAgICBoaXZlQ29tbWFuZCA8LSBwYXN0ZShoaXZlQXJncywgaGl2ZUlucHV0KQogICAgc3lzdGVtKGNvbW1hbmQgPSBoaXZlQ29tbWFuZCwgd2FpdCA9IFRSVUUpCiAgICAKICB9CiAgCn0KCiMjIyAtLS0gV3JhbmdsZSB0aGlzIGRhdGFzZXQ6CgojIyMgLS0tIEJhbm5lciB0YWdzOgpjYW1wYWlnbkJhbm5lciA8LSAnd21kZV9ldGMyMDE3X2J0MScKCiMjIyAtLS0gRGF0YXNldDoKIyAtIGNvdW50IG5vbi1lbXB0eSBmaWxlczoKYyA8LSAwCmxGIDwtIGxpc3QuZmlsZXMoKQpsRiA8LSBsRltncmVwbCgnLnRzdicsIGxGLCBmaXhlZCA9IFQpXQpsRiA8LSBsRltncmVwbCgnQ2xpY2tzJywgbEYsIGZpeGVkID0gVCldCmRhdGFTZXQgPC0gbGlzdCgpCmZvciAoaSBpbiAxOmxlbmd0aChsRikpIHsKICBkUyA8LSByZWFkTGluZXMobEZbaV0sIG4gPSAtMSkKICB0aW1lU3RhbXAgPC0gc3Ryc3BsaXQobEZbaV0sIHNwbGl0ID0gIl8iKVtbMV1dWzNdCiAgYmFubmVyQ2xpY2tzIDwtIHN1bShncmVwbChjYW1wYWlnbkJhbm5lciwgZFMsIGZpeGVkID0gVCkpCiAgZGF0YVNldFtbaV1dIDwtIGRhdGEuZnJhbWUodGltZXN0YW1wID0gdGltZVN0YW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhbm5lckNsaWNrcyA9IGJhbm5lckNsaWNrcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKfQpkYXRhU2V0IDwtIHJiaW5kbGlzdChkYXRhU2V0KQoKIyMjIC0tLSBzdG9yZSBCYW5uZXJDbGlja3NQYWdlVmlld3NfVXBkYXRlLmNzdgpzZXR3ZChkYWlseVVwZGF0ZURpcikKd3JpdGUuY3N2KGRhdGFTZXQsIGZpbGUgPSAidGhhbmt5b3UyMDE4X0Jhbm5lckNsaWNrc1BhZ2VWaWV3c19VcGRhdGUuY3N2IikKCiMjIyAtLS0gU1FMCnN0YXJ0RGF0ZSA8LSAnMjAxOC0wMS0wMScKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTMy4gVXNlciBSZWdpc3RyYXRpb24gRGF0YQojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0gTk9URTogVVRDIHRpbWVzdGFtcHMgLSBhZGp1c3RtZW50IGZvciBDRShTKVQgaW50cm9kdWNlZC4gCiMgLSBTZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uXzU0ODczNDUKcUNvbW1hbmQgPC0gcGFzdGUoIm15c3FsIC0tZGVmYXVsdHMtZmlsZT0vZXRjL215c3FsL2NvbmYuZC9hbmFseXRpY3MtcmVzZWFyY2gtY2xpZW50LmNuZiAtaCBhbmFseXRpY3Mtc2xhdmUuZXFpYWQud21uZXQgLUEgLWUgXCJzZWxlY3QgKiBmcm9tIGxvZy5TZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uXzU0ODczNDUgd2hlcmUgKCh3ZWJIb3N0ID0gJ2RlLndpa2lwZWRpYS5vcmcnKSBhbmQgKHRpbWVzdGFtcCA+PSAiLCBnc3ViKCItIiwgIiIsIHN0YXJ0RGF0ZSwgZml4ZWQgPSBUKSwgIjIyMDAwMCkpO1wiID4gIiwKICAgICAgICAgICAgZGFpbHlVcGRhdGVEaXIsICIvdGhhbmt5b3VfMjAxOF91c2VyUmVnaXN0cmF0aW9ucy50c3YiLCBzZXAgPSAiIikKc3lzdGVtKGNvbW1hbmQgPSBxQ29tbWFuZCwgd2FpdCA9IFRSVUUpCgoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIFM0LiBHdWlkZWQgVG91cnMgRGF0YQojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0gTk9URTogVVRDIHRpbWVzdGFtcHMgLSBhZGp1c3RtZW50IGZvciBDRShTKVQgaW50cm9kdWNlZC4gCiMgLSBTZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uXzU0ODczNDUKcUNvbW1hbmQgPC0gcGFzdGUoIm15c3FsIC0tZGVmYXVsdHMtZmlsZT0vZXRjL215c3FsL2NvbmYuZC9hbmFseXRpY3MtcmVzZWFyY2gtY2xpZW50LmNuZiAtaCBhbmFseXRpY3Mtc2xhdmUuZXFpYWQud21uZXQgLUEgLWUgXCJzZWxlY3QgKiBmcm9tIGxvZy5HdWlkZWRUb3VyRXhpdGVkXzg2OTA1NjYgd2hlcmUgKCh3ZWJIb3N0ID0gJ2RlLndpa2lwZWRpYS5vcmcnKSBhbmQgKHRpbWVzdGFtcCA+PSAiLCAKICAgICAgICAgICAgICAgICAgZ3N1YigiLSIsICIiLCBzdGFydERhdGUsIGZpeGVkID0gVCksICIyMjAwMDApKTtcIiA+ICIsIAogICAgICAgICAgICAgICAgICBkYWlseVVwZGF0ZURpciwgIi90aGFua3lvdV8yMDE4X2d1aWRlZFRvdXJzLnRzdiIsIHNlcCA9ICIiKQpzeXN0ZW0oY29tbWFuZCA9IHFDb21tYW5kLCB3YWl0ID0gVFJVRSkKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTNS4gVXNlciBFZGl0IERhdGEKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIGdldCB1c2VyIElEcyBmcm9tIHJlZ2lzdGVyZWQ6CmxGIDwtIGxpc3QuZmlsZXMoKQpsRiA8LSBsRltncmVwbCgndXNlclJlZ2lzdHJhdGlvbnMnLCBsRiwgZml4ZWQgPSBUKV0KdXNlclJlZyA8LSByZWFkLnRhYmxlKGxGLCAKICAgICAgICAgICAgICAgICAgICAgIHF1b3RlID0gIiIsCiAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQp1c2VyUmVnIDwtIHVzZXJSZWcgJT4lIAogIGRwbHlyOjpzZWxlY3QoZXZlbnRfdXNlcklkLCBldmVudF9pc1NlbGZNYWRlLCBldmVudF9jYW1wYWlnbikgJT4lIAogIGZpbHRlcihldmVudF9pc1NlbGZNYWRlID09IDEpICU+JSAKICBmaWx0ZXIoZXZlbnRfY2FtcGFpZ24gJWluJSAid21kZV9ldGMyMDE3X2J0MSIpCiMgLSB1aWRzOgp1aWQgPC0gdXNlclJlZyRldmVudF91c2VySWQKIyAtIHNxbCBxdWVyeQpzcWxRdWVyeSA8LSBwYXN0ZSgnU0VMRUNUIENPVU5UKCopIGFzIGVkaXRzLCByZXZfdXNlciBGUk9NIHJldmlzaW9uIFdIRVJFIHJldl91c2VyIElOICgnLAogICAgICAgICAgICAgICAgICBwYXN0ZSh1aWQsIGNvbGxhcHNlID0gIiwgIiksCiAgICAgICAgICAgICAgICAgICcpIEdST1VQIEJZIHJldl91c2VyOycsCiAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQpteVNxbENvbW1hbmQgPC0gcGFzdGUoJ215c3FsIC1oIGFuYWx5dGljcy1zdG9yZS5lcWlhZC53bW5ldCBkZXdpa2kgLWUgJywKICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCciJywgc3FsUXVlcnksICciID4gJywgc2VwID0gIiIpLCAKICAgICAgICAgICAgICAgICAgICAgIGRhaWx5VXBkYXRlRGlyLCAnL3RoYW5reW91XzIwMThfdXNlckVkaXRzLnRzdicsIHNlcCA9ICIiKQpzeXN0ZW0oY29tbWFuZCA9IG15U3FsQ29tbWFuZCwgCiAgICAgICB3YWl0ID0gVFJVRSkKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTNi4gVHJhaW5pbmcgTW9kdWxlIERhdGEKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKYGBgCgojIyAxQS4gQ2FtcGFpZ24gQmFubmVyIEltcHJlc3Npb25zCgojIyMgMUEuIDEgVGhlIGRhdGEgc2V0CmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KIyAtIHJlcG9ydDogY3VycmVudCB1cGRhdGUKcHJpbnQocGFzdGUwKCJDdXJyZW50IHVwZGF0ZTogIiwgYXMuY2hhcmFjdGVyKFN5cy50aW1lKCkpKSkKYmFubmVySW1wcmVzc2lvbnMgPC0gcmVhZC5jc3YoJ3RoYW5reW91QmFubmVySW1wcmVzc2lvbnMuY3N2JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpICU+JSAKICBnYXRoZXIoa2V5ID0gIEJhbm5lciwKICAgICAgICAgdmFsdWUgPSBWaWV3cywKICAgICAgICAgQjE3V01ERV90aGFua3lvdV9hdXRob3JzOkIxN1dNREVfdGhhbmt5b3VfYXV0aG9yc19tb2JfQikgJT4lIAogIGdyb3VwX2J5KHRpbWVTdGFtcCwgQmFubmVyKSAlPiUgCiAgc3VtbWFyaXNlKFZpZXdzID0gc3VtKFZpZXdzKSkKa25pdHI6OmthYmxlKGJhbm5lckltcHJlc3Npb25zLCBmb3JtYXQgPSAiaHRtbCIpICU+JSAKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGLCBwb3NpdGlvbiA9ICJsZWZ0IikKYGBgCiMjIyAxQS4gMiBDaGFydAoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQpnZ3Bsb3QoYmFubmVySW1wcmVzc2lvbnMsIGFlcyh4ID0gdGltZVN0YW1wLAogICAgICAgICAgICAgICAgICAgIHkgPSBWaWV3cywKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IEJhbm5lciwKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFZpZXdzKSkgKwogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBCYW5uZXIpLCBzaXplID0gLjI1KSArCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAid2hpdGUiKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArCiAgZ2d0aXRsZSgnVGhhbmsgWW91IDIwMTg6XG5PdmVydmlldyBvZiBCYW5uZXJJbXByZXNzaW9ucycpICsKICB5bGFiKCJJbXByZXNzaW9ucyIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCwgaGp1c3QgPSAxKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMgMUIuIENhbXBhaWduIFBhZ2V2aWV3cyAoQmFubmVyIENsaWNrcykKCiMjIyAxQi4gMSBUaGUgZGF0YSBzZXQKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KcGFnZXZpZXdzIDwtIHJlYWQuY3N2KCd0aGFua3lvdTIwMThfQmFubmVyQ2xpY2tzUGFnZVZpZXdzX1VwZGF0ZS5jc3YnLAogICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKcGFnZXZpZXdzIDwtIHBhZ2V2aWV3cyAlPiUgCiAgZ3JvdXBfYnkodGltZXN0YW1wKSAlPiUgCiAgc3VtbWFyaXNlKFBhZ2V2aWV3cyA9IHN1bShiYW5uZXJDbGlja3MpKQprbml0cjo6a2FibGUocGFnZXZpZXdzLCBmb3JtYXQgPSAiaHRtbCIpICU+JSAKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGLCBwb3NpdGlvbiA9ICJsZWZ0IikKYGBgCgojIyMgMUIuIDIgQ2hhcnQKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KZ2dwbG90KHBhZ2V2aWV3cywgYWVzKHggPSB0aW1lc3RhbXAsCiAgICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFBhZ2V2aWV3cykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgCiAgICAgICAgICAgcG9zaXRpb24gPSAiZG9kZ2UiLCAKICAgICAgICAgICB3aWR0aCA9IC4xNSwgCiAgICAgICAgICAgZmlsbCA9ICJ3aGl0ZSIsIAogICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIikgKwogIGdndGl0bGUoJ1RoYW5rIFlvdSAyMDE4OlxuT3ZlcnZpZXcgb2YgTGFuZGluZyBQYWdldmlld3MnKSArCiAgZ2VvbV9sYWJlbChzaXplID0gMykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4LCBoanVzdCA9IDEpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgoKIyMgMi4gQ2FtcGFpZ24gVXNlciBSZWdpc3RyYXRpb25zCgojIyMgMi4gMSBUaGUgZGF0YSBzZXQKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KdXNlclJlZyA8LSByZWFkLmRlbGltKCd0aGFua3lvdV8yMDE4X3VzZXJSZWdpc3RyYXRpb25zLnRzdicsCiAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKdXNlclJlZyR0aW1lc3RhbXAgPC0gYXMuY2hhcmFjdGVyKHVzZXJSZWckdGltZXN0YW1wKQp1c2VyUmVnJHRpbWVzdGFtcCA8LSBzYXBwbHkodXNlclJlZyR0aW1lc3RhbXAsIGZ1bmN0aW9uKHgpIHsKICB5IDwtIHN1YnN0cih4LCAxLCA0KQogIG0gPC0gc3Vic3RyKHgsIDUsIDYpCiAgZCA8LSBzdWJzdHIoeCwgNywgOCkKICBwYXJ0MURhdGUgPC0gcGFzdGUoeSwgbSwgZCwgc2VwID0gIi0iKQogIGhyIDwtIHN1YnN0cih4LCA5LCAxMCkKICBtaSA8LSBzdWJzdHIoeCwgMTEsIDEyKQogIHNlIDwtIHN1YnN0cih4LCAxMywgMTQpCiAgcGFydDJEYXRlIDwtIHBhc3RlKGhyLCBtaSwgc2UsIHNlcCA9ICI6IikKICBwYXN0ZShwYXJ0MURhdGUsIHBhcnQyRGF0ZSwgc2VwID0gIiAiKQp9KQp1c2VyUmVnJHRpbWVzdGFtcCA8LSBhcy5QT1NJWGN0KHVzZXJSZWckdGltZXN0YW1wLCB0eiA9ICJVVEMiKQp0aW1lRGlmZiA8LSAKICBhcy5QT1NJWGN0KGFzLmNoYXJhY3RlcihTeXMudGltZSgpKSwgdHogPSAiVVRDIikgLSBhcy5QT1NJWGN0KGFzLmNoYXJhY3RlcihTeXMudGltZSgpKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpCnVzZXJSZWckdGltZXN0YW1wIDwtIGFzLmNoYXJhY3Rlcih1c2VyUmVnJHRpbWVzdGFtcCArIHRpbWVEaWZmKQp1c2VyUmVnJHRpbWVzdGFtcCA8LSBzYXBwbHkodXNlclJlZyR0aW1lc3RhbXAsIGZ1bmN0aW9uKHgpIHsKICB5IDwtIHN1YnN0cih4LCAxLCA0KQogIG0gPC0gc3Vic3RyKHgsIDYsIDcpCiAgZCA8LSBzdWJzdHIoeCwgOSwgMTApIAogIHBhc3RlKHksIG0sIGQsIHNlcCA9ICItIikKfSkKdXNlclJlZyA8LSB1c2VyUmVnICU+JQogIGZpbHRlcihldmVudF9jYW1wYWlnbiAlaW4lICd3bWRlX2V0YzIwMTdfYnQxJykgJT4lIAogIGdyb3VwX2J5KHRpbWVzdGFtcCkgJT4lIAogIHN1bW1hcmlzZShSZWdpc3RyYXRpb25zID0gbigpKQprbml0cjo6a2FibGUodXNlclJlZywgZm9ybWF0ID0gImh0bWwiKSAlPiUgCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAibGVmdCIpCmBgYAoKIyMjIDIuIDIgQ2hhcnQKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KZ2dwbG90KHVzZXJSZWcsIGFlcyh4ID0gdGltZXN0YW1wLAogICAgICAgICAgICAgICAgICAgIHkgPSBSZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUmVnaXN0cmF0aW9ucykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgCiAgICAgICAgICAgcG9zaXRpb24gPSAiZG9kZ2UiLCAKICAgICAgICAgICB3aWR0aCA9IC4xNSwgCiAgICAgICAgICAgZmlsbCA9ICJ3aGl0ZSIsIAogICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIikgKwogIGdndGl0bGUoJ1RoYW5rIFlvdSAyMDE4OlxuT3ZlcnZpZXcgb2YgVXNlciBSZWdpc3RyYXRpb25zJykgKwogIGdlb21fbGFiZWwoc2l6ZSA9IDMpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCwgaGp1c3QgPSAxKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMgMy4gQ2FtcGFpZ24gVXNlciBFZGl0cwoKIyMjIDMuIDEgVGhlIGRhdGEgc2V0CgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CnVzZXJFZGl0cyA8LSByZWFkTGluZXMoJ3RoYW5reW91XzIwMThfdXNlckVkaXRzLnRzdicsIG4gPSAtMSkKaWYgKGxlbmd0aCh1c2VyRWRpdHMpID49IDEpIHsKICB1c2VyRWRpdHMgPC0gcmVhZC5kZWxpbSgndGhhbmt5b3VfMjAxOF91c2VyRWRpdHMudHN2JywKICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgIyAtIHJlcG9ydAogIHByaW50KHBhc3RlMChzdW0odXNlckVkaXRzJGVkaXRzKSwgIiBlZGl0cyB3ZXJlIG1hZGUgYnkgdGhlIGNhbXBhaWduIHJlZ2lzdGVyZWQgdXNlcnMgdGh1cyBmYXIuIikpCn0gZWxzZSB7CiAgcHJpbnQoIlRoZXJlIGFyZSBjdXJyZW50bHkgbm8gdXNlciBlZGl0cyBmcm9tIHRoaXMgY2FtcGFpZ24uIikKfQpgYGAKCiMjIyAzLiAyIENoYXJ0CgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CmBgYAoKIyMgNC4gQ2FtcGFpZ24gR3VpZGVkIFRvdXJzCgojIyMgNC4gMSBUaGUgZGF0YSBzZXQ6IEd1aWRlZCBUb3VyIEV4aXQgU3RlcHMKCkNhbXBhaWduIGd1aWRlZCB0b3VyczogYGRpc2t1dGllcmVuYCBhbmQgYHNlaW11dGlnYC4KCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KdG91cnMgPC0gYygnZGlza3V0aWVyZW4nLCAnc2VpbXV0aWcnKQojIHNldHdkKCcvaG9tZS9nb3JhbnNtL1dvcmsvX19fRGF0YUtvbGVrdGl2L1Byb2plY3RzL1dpa2ltZWRpYURFVS9fV01ERV9Qcm9qZWN0cy9fbWlzYy9OZXdFZGl0b3JzX1RlYW0vVGhhbmtfWW91X0NhbXBhaWduXzIwMTgvX2RhaWx5VXBkYXRlLycpCmdUb3VycyA8LSByZWFkLmRlbGltKCd0aGFua3lvdV8yMDE4X2d1aWRlZFRvdXJzLnRzdicsCiAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpnVG91cnMkdGltZXN0YW1wIDwtIGFzLmNoYXJhY3RlcihnVG91cnMkdGltZXN0YW1wKQpnVG91cnMkdGltZXN0YW1wIDwtIHNhcHBseShnVG91cnMkdGltZXN0YW1wLCBmdW5jdGlvbih4KSB7CiAgeSA8LSBzdWJzdHIoeCwgMSwgNCkKICBtIDwtIHN1YnN0cih4LCA1LCA2KQogIGQgPC0gc3Vic3RyKHgsIDcsIDgpCiAgcGFydDFEYXRlIDwtIHBhc3RlKHksIG0sIGQsIHNlcCA9ICItIikKICBociA8LSBzdWJzdHIoeCwgOSwgMTApCiAgbWkgPC0gc3Vic3RyKHgsIDExLCAxMikKICBzZSA8LSBzdWJzdHIoeCwgMTMsIDE0KQogIHBhcnQyRGF0ZSA8LSBwYXN0ZShociwgbWksIHNlLCBzZXAgPSAiOiIpCiAgcGFzdGUocGFydDFEYXRlLCBwYXJ0MkRhdGUsIHNlcCA9ICIgIikKfSkKZ1RvdXJzJHRpbWVzdGFtcCA8LSBhcy5QT1NJWGN0KGdUb3VycyR0aW1lc3RhbXAsIHR6ID0gIlVUQyIpCnRpbWVEaWZmIDwtIAogIGFzLlBPU0lYY3QoYXMuY2hhcmFjdGVyKFN5cy50aW1lKCkpLCB0eiA9ICJVVEMiKSAtIGFzLlBPU0lYY3QoYXMuY2hhcmFjdGVyKFN5cy50aW1lKCkpLCB0eiA9ICJFdXJvcGUvQmVybGluIikKZ1RvdXJzJHRpbWVzdGFtcCA8LSBhcy5jaGFyYWN0ZXIoZ1RvdXJzJHRpbWVzdGFtcCArIHRpbWVEaWZmKQpnVG91cnMkdGltZXN0YW1wIDwtIHNhcHBseShnVG91cnMkdGltZXN0YW1wLCBmdW5jdGlvbih4KSB7CiAgeSA8LSBzdWJzdHIoeCwgMSwgNCkKICBtIDwtIHN1YnN0cih4LCA2LCA3KQogIGQgPC0gc3Vic3RyKHgsIDksIDEwKSAKICBwYXN0ZSh5LCBtLCBkLCBzZXAgPSAiLSIpCn0pCiMgLSBhbm9ueW1pemUgZXZlbnRfdXNlcklkCmV2ZW50VXNlcklkIDwtIHNldGRpZmYodW5pcXVlKGdUb3VycyRldmVudF91c2VySWQpLCAwKQphbl91c2VySWQgPC0gY2hhcmFjdGVyKGxlbmd0aChldmVudFVzZXJJZCkpCmZvciAoaSBpbiAxOmxlbmd0aChhbl91c2VySWQpKSB7CiAgaWQgPC0gcm91bmQocnVuaWYoMSwgMSwgMTBlNikpCiAgd2hpbGUgKGlkICVpbiUgYW5fdXNlcklkKSB7CiAgICBpZCA8LSByb3VuZChydW5pZigxLCAxLCAxMGU2KSkKICB9CiAgYW5fdXNlcklkW2ldIDwtIGlkCn0KYW5fdXNlcklkIDwtIHBhc3RlMCgidV8iLCBhbl91c2VySWQpCmdUb3VycyRhbl91c2VySWQgPC0gc2FwcGx5KGdUb3VycyRldmVudF91c2VySWQsIGZ1bmN0aW9uKHgpIHsKICB3IDwtIHdoaWNoKGV2ZW50VXNlcklkICVpbiUgeCkKICBpZiAobGVuZ3RoKHcpID4gMCkgewogICAgYW5fdXNlcklkW3ddCiAgfSBlbHNlIHsKICAgIHgKICB9Cn0pCiMgLSBsb29rIHVwIGZvciB0aGUgY2FtcGFpZ24gZ3VpZGVkIHRvdXJzCncgPC0gd2hpY2goZ1RvdXJzJGV2ZW50X3RvdXIgJWluJSB0b3VycykKaWYgKGxlbmd0aCh3KSA+IDApIHsKICBnVG91cnMgPC0gZ1RvdXJzW3csIF0KICBnVG91cnMgPC0gZ1RvdXJzICU+JSAKICAgIGZpbHRlcihldmVudF91c2VySWQgIT0gMCkgJT4lIAogICAgc2VsZWN0KHRpbWVzdGFtcCwgZXZlbnRfdG91ciwgZXZlbnRfc3RlcCwgYW5fdXNlcklkKQogIGtuaXRyOjprYWJsZShnVG91cnMsIGZvcm1hdCA9ICJodG1sIikgJT4lIAogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYsIHBvc2l0aW9uID0gImxlZnQiKQp9IGVsc2UgewogIHByaW50KCJUaGVyZSBhcmUgY3VycmVudGx5IG5vIGRhdGEgb24gZ3VpZGVkIHRvdXJzIGZyb20gdGhpcyBjYW1wYWlnbi4iKQp9CmBgYAojIyMgNC4gMiBVbmlxdWUgdXNlcnMgaW4gR3VpZGVkIFRvdXJzCgpDYW1wYWlnbiBndWlkZWQgdG91cnM6IGBkaXNrdXRpZXJlbmAgYW5kIGBzZWltdXRpZ2AuCgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CmdUb3VycyAlPiUgCiAgc2VsZWN0KGV2ZW50X3RvdXIpICU+JSAKICBncm91cF9ieShldmVudF90b3VyKSAlPiUgCiAgc3VtbWFyaXNlKGBVbmlxdWUgdXNlcnNgID0gbigpKSAlPiUgCiAga25pdHI6OmthYmxlKGZvcm1hdCA9ICJodG1sIikgJT4lIAogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYsIHBvc2l0aW9uID0gImxlZnQiKQpgYGAK