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

The campaign is run from 2018/08/01 to 2018/08/08.

CURRENT UPDATE: Complete dataset as of 2018/08/09.

0. Data Acquisiton

NOTE: the Data Acquisition code chunk is not fully reproducible from this Report. The data are collected by running the script SummerBC2018_DailyUpdate_PRODUCTION.R on stat1005.eqiad.wmnet, collecting the data as .tsv and .csv files, copying manually, and processing locally.

0.1 Daily Update

# - wle_2018_de

### --- WMDE Spring Banner Campaign 2018
### --- Daily Update

rm(list = ls())

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

### --- Directories
outDir <- '/home/goransm/RScripts/NewEditors/2018_SummerBannerCampaign/'
setwd(outDir)

### --- General Info:
# First Banner (https://de.wikipedia.org/wiki/Wikipedia:Hauptseite?banner=B18WMDE_authors_02_180801_1&force=1)
# Registration Name: Tesssssttttt
# Created: Mo, 30.07.2018 around 15:42
# 
# Second Banner (https://de.wikipedia.org/wiki/Wikipedia:Hauptseite?banner=B18WMDE_authors_02_180801_2&force=1)
# Registration name: Tessssttttt
# Created: Mo, 30.07.2018 around 15:45
# 
# Third Banner (https://de.wikipedia.org/wiki/Wikipedia:Hauptseite?banner=B18WMDE_authors_02_180801_3&force=1)
# Registration name: Tttteesssstt
# Created: Mo, 30.07.2018 around 15:45

### --- banner names (/beacon/impression):
# - B18WMDE_authors_02_180801_1
# - B18WMDE_authors_02_180801_2
# - B18WMDE_authors_02_180801_3

### --- campaign tags
# - WMDE_neweditors_summer2018_1
# - WMDE_neweditors_summer2018_2
# - WMDE_neweditors_summer2018_3

### --- landingpage including the campaign tag and anchor
# - Banner 1: https://de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/Neue_Ehrenamtliche/FAQ?campaign=WMDE_neweditors_summer2018_1#Wer-darf-schreiben
# - Banner 2: https://de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/Neue_Ehrenamtliche/FAQ?campaign=WMDE_neweditors_summer2018_2#Wikipedia-Buero
# - Banner 3: https://de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/Neue_Ehrenamtliche/FAQ?campaign=WMDE_neweditors_summer2018_3#Schutz-vor-Missbrauch
# - Second landing page: https://de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/LerneWikipedia

# - Update on (Reminder: stat1005 time is UTC): today minus one day
today <- strsplit(
  strsplit(
    as.character(as.POSIXct(Sys.time()) - 24*60*60),
    split = " ",
    fixed = TRUE)[[1]][1],
  split = "-",
  fixed = TRUE)[[1]]
today <- as.list(today)
names(today) <- c('year', 'month', 'day')

# - construct wmf.webrequest year/month/day/hour condition
# - for today:
dateCondition <- paste0("((year = ", today$year,
                        " AND month = ", today$month,
                        " AND day = ", as.integer(today$day) - 1,
                        " AND (hour = 22 OR hour = 23))",
                        " OR ",
                        "(year = ", today$year,
                        " AND month = ", today$month,
                        " AND day = ", today$day,
                        " AND (hour >= 0 OR hour < 22)))"
                        )

### --- Collect Banner Impressions for Update: HiveQL
q <- paste(
  "USE wmf;
  SELECT uri_query FROM webrequest
  WHERE uri_host = 'de.wikipedia.org'
  AND uri_path = '/beacon/impression'
  AND ",
  dateCondition,
  " AND ((uri_query LIKE '%B18WMDE_authors_02_180801_1%') OR
    (uri_query LIKE '%B18WMDE_authors_02_180801_2%') OR
    (uri_query LIKE '%B18WMDE_authors_02_180801_3%'));",
  sep = "")
# - write hql
write(q, 'summerBC2018_BannerImpressionsDaily.hql')
# - prepare output file:
fileName <- 'sbc2018_BannerImpressionsDaily.tsv'
# - execute hql script:
hiveArgs <- 'beeline --verbose=true -f'
hiveInput <- paste0('summerBC2018_BannerImpressionsDaily.hql > ', fileName)
# - command:
hiveCommand <- paste(hiveArgs, hiveInput)
system(command = hiveCommand, wait = TRUE)

### --- Collect Pageviews == Banner clicks for Update: HiveQL
# https://de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/LerneWikipedia
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/Neue_Ehrenamtliche/FAQ' AND uri_query LIKE '%campaign=WMDE_neweditors_summer2018%')
  OR (uri_path = '/wiki/Wikipedia:Wikimedia_Deutschland/LerneWikipedia')) 
  AND ", dateCondition,
  " ;",
  sep = "")
# - write hql
write(q, 'summerBC2018_PageViewsDaily.hql')
# - prepare output file:
fileName <- 'sbc2018_PageViewsDaily.tsv'
# - execute hql script:
hiveArgs <- 'beeline --verbose=true -f'
hiveInput <- paste0('summerBC2018_PageViewsDaily.hql > ', fileName)
# - command:
hiveCommand <- paste(hiveArgs, hiveInput)
system(command = hiveCommand, wait = TRUE)

### --- Collect Use Registrations for Update: SQL
# - ServerSideAccountCreation_17719237 schema
qCommand <- "mysql --defaults-file=/etc/mysql/conf.d/analytics-research-client.cnf -h analytics-slave.eqiad.wmnet -A -e \"select * from log.ServerSideAccountCreation_17719237 where ((webHost = 'de.wikipedia.org') and (timestamp >= 20180729000000) and ((event_campaign like '%WMDE_neweditors_summer2018_1%') or (event_campaign like '%WMDE_neweditors_summer2018_2%') or (event_campaign like '%WMDE_neweditors_summer2018_3%')));\" > /home/goransm/RScripts/NewEditors/2018_SummerBannerCampaign/summerBC2018_userRegistrations.tsv"
system(command = qCommand, wait = TRUE)

### --- Collect Banner Events for Update: SQL
# - WMDEBannerEvents_18193948 schema
qCommand <- "mysql --defaults-file=/etc/mysql/conf.d/analytics-research-client.cnf -h analytics-slave.eqiad.wmnet -A -e \"select * from log.WMDEBannerEvents_18193948 where timestamp >= 20180729000000;\" > /home/goransm/RScripts/NewEditors/2018_SummerBannerCampaign/summerBC2018_bannerEvents.tsv"
system(command = qCommand, wait = TRUE)


### --- Prepare for repoting

# - Banner Impressions
bImpData <- readLines('sbc2018_BannerImpressionsDaily.tsv', n = -1)
bImpData <- bImpData[9:(length(bImpData) - 2)]
bImpData <- bImpData[-1]
bImpData <- data.frame(dat = bImpData, stringsAsFactors = F)
bannerA <- length(which(grepl("banner=B18WMDE_authors_02_180801_1", bImpData$dat, fixed = T)))
bannerB <- length(which(grepl("banner=B18WMDE_authors_02_180801_2", bImpData$dat, fixed = T)))
bannerC <- length(which(grepl("banner=B18WMDE_authors_02_180801_3", bImpData$dat, fixed = T)))
DailyBannerImpressions <- data.frame(
  Banner = c('B18WMDE_authors_1', 'B18WMDE_authors_2', 'B18WMDE_authors_3'),
  Count = c(bannerA, bannerB, bannerC),
  CampaignDay = rep(paste0(today, collapse = "-", sep = ""), 3)
)
DailyBannerImpressions$Count <- DailyBannerImpressions$Count/.01
# - DailyBannerImpressions
lF <- list.files()
if ('DailyBannerImpressions.csv' %in% lF) {
  dPS <- read.csv('DailyBannerImpressions.csv',
                  header = T,
                  row.names = 1)
  DailyBannerImpressions <- rbind(dPS, DailyBannerImpressions)
  write.csv(DailyBannerImpressions, 'DailyBannerImpressions.csv')
} else {
  write.csv(DailyBannerImpressions, 'DailyBannerImpressions.csv')
}

# - Pageviews
pageViews <- readLines('sbc2018_PageViewsDaily.tsv', n = -1)
pageViews <- pageViews[8:(length(pageViews) - 2)]
header = pageViews[1]
pageViews <- pageViews[-1]
header <- unlist(strsplit(header, "\t")[[1]])
pageViews <- data.frame(dat = pageViews, stringsAsFactors = F)
pageViews <- separate(pageViews,
                      dat,
                      into = c('uri_path', 'uri_query', 'referer'),
                      sep = "\t")
dailyPageviewsSet <- pageViews %>% 
  filter(grepl('campaign=WMDE_neweditors_summer2018', uri_query) | 
           grepl('/wiki/Wikipedia:Wikimedia_Deutschland/LerneWikipedia', uri_path)) %>%
  group_by(uri_query, uri_path) %>%
  summarise(Count = n())
dailyPageviewsSet$CampaignDay <- paste(unlist(today), collapse = "-", sep = "")
# - DailyPageviews
lF <- list.files()
if ('DailyPageviews.csv' %in% lF) {
  dPS <- read.csv('DailyPageviews.csv',
                  header = T,
                  row.names = 1)
  dailyPageviewsSet <- rbind(dPS, as.data.frame(dailyPageviewsSet))
  write.csv(dailyPageviewsSet, 'DailyPageviews.csv')
} else {
  write.csv(dailyPageviewsSet, 'DailyPageviews.csv')
}

# - User registrations
userRegistrations <- read.delim('summerBC2018_userRegistrations.tsv', 
                                sep = "\t",
                                header = T,
                                quote = "", 
                                stringsAsFactors = F)
userRegistrations <- userRegistrations[grepl('is_bot": false', userRegistrations$userAgent, fixed = T), ]
userRegistrations$timestamp <- as.character(userRegistrations$timestamp)
userRegistrations$timestamp <- as.POSIXct(userRegistrations$timestamp, tz = "UTC", format = "%Y%m%d%H%M%S") 
# - CEST correction on POSIXct class, +2hours:
userRegistrations$timestamp <- userRegistrations$timestamp + 2*60*60
# - wrangle
userRegistrations$CampaignDay <- sapply(userRegistrations$timestamp, function(x) {
  paste(c(
    substr(x, 1,4),
    substr(x, 6,7),
    substr(x, 9,10)
    ),
    collapse = "-",
    sep = "")
})
userRegistrations <- userRegistrations[which(userRegistrations$CampaignDay %in% paste(today, collapse = "-", sep = "")), ]
dailyUserRegSet <- userRegistrations %>%
  select(event_campaign) %>%
  group_by(event_campaign) %>%
  summarise(Count = n())
dailyUserRegSet$CampaignDay <- paste(unlist(today), collapse = "-", sep = "")
# - Daily User Registrations
lF <- list.files()
if ('dailyUserRegistrations.csv' %in% lF) {
  dPS <- read.csv('dailyUserRegistrations.csv', 
                  header = T, 
                  row.names = 1)
  dailyUserRegSet <- rbind(dPS, dailyUserRegSet)
  write.csv(dailyUserRegSet, 'dailyUserRegistrations.csv')
} else {
  write.csv(dailyUserRegSet, 'dailyUserRegistrations.csv')
}

0.1 Post-Campaign Analytics

NOTE: Training Module data are obtained directly from the application maintainer. The following code retrieves all user registrations and edits.

### --- User registrations

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

# - ServerSideAccountCreation_5487345
qCommand <- "mysql --defaults-file=/etc/mysql/conf.d/analytics-research-client.cnf -h analytics-slave.eqiad.wmnet -A -e \"select * from log.ServerSideAccountCreation_17719237 where ((webHost = 'de.wikipedia.org') and (timestamp >= 20180729000000) and ((event_campaign like '%WMDE_neweditors_summer2018_1%') or (event_campaign like '%WMDE_neweditors_summer2018_2%') or (event_campaign like '%WMDE_neweditors_summer2018_3%')));\" > /home/goransm/RScripts/NewEditors/2018_SummerBannerCampaign/summerBC2018_userRegistrations_FULL.tsv"
system(command = qCommand, wait = TRUE)

### --- User edits
# - get user IDs from registered:
userReg <- read.table('/home/goransm/RScripts/NewEditors/2018_SummerBannerCampaign/summerBC2018_userRegistrations_FULL.tsv', 
                      quote = "",
                      sep = "\t",
                      header = T,
                      check.names = F,
                      stringsAsFactors = F)
userReg <- userReg %>% 
  dplyr::select(event_userId, event_isSelfMade) %>% 
  filter(event_isSelfMade == 1)
# - 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 = ""),
                      '/home/goransm/RScripts/NewEditors/2018_SummerBannerCampaign/sBC2018_userEdits_FULL.tsv', sep = "")
system(command = mySqlCommand, 
       wait = TRUE)

1. Campaign Banners and Pages

This section presents all data and statistics on the campaign banners and pages.

1.4 User Registrations

Chart 1. 4. Daily user registrations per banner

dataSet <- read.delim('/home/goransm/Work/___DataKolektiv/Projects/WikimediaDEU/_WMDE_Projects/_misc/NewEditors_Team/2018_SummerBannerCampaign/_data/summerBC2018_userRegistrations_FULL.tsv',
                    header = T,
                    check.names = F,
                    sep = "\t",
                    quote = "",
                    stringsAsFactors = F)
dataSet <- dataSet[grepl('is_bot": false', dataSet$userAgent, fixed = T), ]
dataSet$timestamp <- as.character(dataSet$timestamp)
dataSet$timestamp <- as.POSIXct(dataSet$timestamp, tz = "UTC", format = "%Y%m%d%H%M%S") 
# - CEST correction on POSIXct class, +2hours:
dataSet$timestamp <- dataSet$timestamp + 2*60*60
# - Campaign day:
dataSet$CampaignDay <- sapply(dataSet$timestamp, function(x) {
  paste(c(
    substr(x, 1,4),
    substr(x, 6,7),
    substr(x, 9,10)
    ),
    collapse = "-",
    sep = "")
})
dataSet <- filter(dataSet,
                  CampaignDay %in% c("2018-08-01", 
                                     "2018-08-02",
                                     "2018-08-03",
                                     "2018-08-04",
                                     "2018-08-05",
                                     "2018-08-06",
                                     "2018-08-07",
                                     "2018-08-08"))
userReg <- dataSet %>% 
  select(event_campaign, CampaignDay) %>% 
  group_by(CampaignDay, event_campaign) %>% 
  summarise(Count = n())
colnames(userReg)[2] <- "Banner"
# - Visualize w. {ggplot2}
ggplot(userReg, aes(x = CampaignDay,
                    y = Count,
                    group = Banner,
                    color = Banner,
                    fill = Banner,
                    label = Count)) + 
  geom_path(size = .5) + 
  geom_point(size = 1.5) +
  scale_y_continuous(labels = comma) +
  ggtitle('Summer Banner Campaign 2018: User Registrations') +
  theme_minimal() + 
  geom_text_repel(size = 3.5, show.legend = FALSE) + 
  scale_y_continuous(labels = comma) +
  theme(axis.text.x = element_text(angle = 90, size = 8)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank())

Table 1. 4. Daily user registrations per banner

### --- Full Dataset (Table Report)
datatable(userReg)

Table 1. 4. 1 Banner Conversion Rates

### --- Banner Conversion Rates
totalRegs <- userReg[, 2:3] %>% 
  select(Banner, Count) %>%
  group_by(Banner) %>%
  summarise(totalReg = sum(Count))
totalRegs$Banner[grepl("WMDE_neweditors_summer2018_1", totalRegs$Banner)] <- 'B18WMDE_authors_1'
totalRegs$Banner[grepl("WMDE_neweditors_summer2018_2", totalRegs$Banner)] <- 'B18WMDE_authors_2'
totalRegs$Banner[grepl("WMDE_neweditors_summer2018_3", totalRegs$Banner)] <- 'B18WMDE_authors_3'
totalClicks$uri_query[grepl("WMDE_neweditors_summer2018_1", totalClicks$uri_query)] <- 'B18WMDE_authors_1'
totalClicks$uri_query[grepl("WMDE_neweditors_summer2018_2", totalClicks$uri_query)] <- 'B18WMDE_authors_2'
totalClicks$uri_query[grepl("WMDE_neweditors_summer2018_3", totalClicks$uri_query)] <- 'B18WMDE_authors_3'
totalClicks <- totalClicks %>% 
  group_by(uri_query) %>% 
  summarise(totalClicks = sum(totalClicks))
totalRegs <- left_join(totalRegs, totalClicks,
                       by = c("Banner" = "uri_query"))
totalRegs$`Registrations/Clicks` <- round(totalRegs$totalReg/totalRegs$totalClicks, 5)
datatable(totalRegs)

1.5 User Edits

Chart 1. 5. Edits of registered users per banner and campaign day of registration

NOTE: the Campaign Day here refers to the day when the user has registered, not the day in which the respective edits were made.

dataSet2 <- read.delim('/home/goransm/Work/___DataKolektiv/Projects/WikimediaDEU/_WMDE_Projects/_misc/NewEditors_Team/2018_SummerBannerCampaign/_data/sBC2018_userEdits_FULL.tsv',
                    header = T,
                    check.names = F,
                    sep = "\t",
                    quote = "",
                    stringsAsFactors = F)
dataSet <- left_join(dataSet, dataSet2, by = c("event_userId" = "rev_user"))
dataSet$edits[is.na(dataSet$edits)] <- 0
userEds <- dataSet %>% 
  select(event_campaign, CampaignDay, edits) %>% 
  group_by(CampaignDay, event_campaign) %>% 
  summarise(Edits = sum(edits))
colnames(userEds)[2] <- "Banner"
# - Visualize w. {ggplot2}
ggplot(userEds, aes(x = CampaignDay,
                    y = Edits,
                    group = Banner,
                    color = Banner,
                    fill = Banner,
                    label = Edits)) + 
  geom_path(size = .5) + 
  geom_point(size = 1.5) +
  scale_y_continuous(labels = comma) +
  ggtitle('Summer Banner Campaign 2018: User Edits') +
  theme_minimal() + 
  geom_text_repel(size = 3.5, show.legend = FALSE) + 
  scale_y_continuous(labels = comma) +
  theme(axis.text.x = element_text(angle = 90, size = 8)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank())

Table 1. 5. Edits of registered users per banner and campaign day of registration

### --- Full Dataset (Table Report)
datatable(userEds)

Table 1. 5. 1 Edits of registered users per banner and campaign day of registration

Total number of user edits per banner:

totalEdits <- userEds %>% 
  group_by(Banner) %>% 
  summarise(Edits = sum(Edits))
datatable(totalEdits)

Table 1. 5. 2 Users reaching their 10th edit per banner

edit10 <- dataSet %>% 
  filter(edits >= 10) %>% 
  select(event_campaign) %>% 
  group_by(event_campaign) %>%
  summarise(`edit10th` = n())
datatable(edit10)

Table 1. 5. 3 Full user edits distribution

### --- Full Dataset (Table Report)
pltEdits <- as.tbl(dataSet) %>% 
  dplyr::group_by(edits) %>% 
  count()
colnames(pltEdits) <- c('Edits', 'Num.users')
knitr::kable(pltEdits, format = "html") %>%
  kable_styling(full_width = F, position = "left")
Edits Num.users
0 78
1 11
2 9
3 5
4 3
5 2
8 1
9 1
48 1

Table 1. 5. 4 Users edit categories

edits0 <- pltEdits$`Num.users`[pltEdits$Edits == 0]
edits <- sum(pltEdits$`Num.users`[pltEdits$Edits > 0])
edits1 <- sum(pltEdits$`Num.users`[pltEdits$Edits == 1])
edits2_4 <- sum(pltEdits$`Num.users`[pltEdits$Edits >= 2 & pltEdits$Edits <= 4])
edits5_10 <- sum(pltEdits$`Num.users`[pltEdits$Edits >= 5 & pltEdits$Edits <= 10])
edits10 <- sum(pltEdits$`Num.users`[pltEdits$Edits > 10])
editClasses <- data.frame(`No edits` = edits0,
                          `Edited` = edits,
                          `1 edit` = edits1,
                          `2 - 4 edits` = edits2_4,
                          `5 - 10 edits` = edits5_10, 
                          `> 10 edits` = edits10,
                          check.names = F,
                          stringsAsFactors = F)
knitr::kable(editClasses, format = "html") %>%
  kable_styling(full_width = F, position = "left")
No edits Edited 1 edit 2 - 4 edits 5 - 10 edits > 10 edits
78 33 11 17 4 1

2 Training Modules

trainMod <- read.csv('/home/goransm/Work/___DataKolektiv/Projects/WikimediaDEU/_WMDE_Projects/_misc/NewEditors_Team/2018_SummerBannerCampaign/_data/wmde_training_data_2018-08.csv',
                    header = T,
                    check.names = F,
                    stringsAsFactors = F)
# - remove users Stefan Schneider (WMDE) and Sage (Wiki Ed)
trainMod <- trainMod %>% 
  filter(!(username %in% c('Stefan Schneider (WMDE)', 'Sage (Wiki Ed)')))
# - data types
trainMod$module_completion_date <- as.character(trainMod$module_completion_date)
trainMod$module_completion_date <- as.POSIXct(trainMod$module_completion_date, tz = "UTC", format = "%Y-%m-%d %H:%M:%S") 
# - CEST correction on POSIXct class, +2hours:
trainMod$module_completion_date <- trainMod$module_completion_date + 2*60*60
trainMod$started_at <- as.character(trainMod$started_at)
trainMod$started_at <- as.POSIXct(trainMod$started_at, tz = "UTC", format = "%Y-%m-%d %H:%M:%S") 
# - CEST correction on POSIXct class, +2hours:
trainMod$started_at <- trainMod$started_at + 2*60*60
trainMod$last_slide_completed_at <- as.character(trainMod$last_slide_completed_at)
trainMod$last_slide_completed_at <- as.POSIXct(trainMod$last_slide_completed_at, tz = "UTC", format = "%Y-%m-%d %H:%M:%S") 
# - CEST correction on POSIXct class, +2hours:
trainMod$last_slide_completed_at <- trainMod$last_slide_completed_at + 2*60*60
# - filter for campaign days only
trainMod <- filter(trainMod, 
                   started_at >= "2018-08-01 00:00:00" & started_at < "2018-08-09 00:00:00")
# - filter campaign training modules only
# trainMod <- filter(trainMod,
                   # training_module %in% c('diskutieren-basiswissen', 'editieren-basiswissen'))
# - left_join w. dataSet (user registrations + user edits)
# - to produce trainMod that has user registrations + edits
trainMod <- left_join(trainMod, dataSet,
                      by = c("username" = "event_userName"))
# - filter those who did not register with the campaign:
trainMod <- filter(trainMod, !is.na(CampaignDay))
# - trainMod aggregates 1: number of training modules started and completed per registered user
modulesStarted <- as.data.frame(table(trainMod$username))
colnames(modulesStarted) <- c('username', 'modulesStarted')
modulesCompleted <- filter(trainMod, !is.na(module_completion_date))
modulesCompleted <- as.data.frame(table(modulesCompleted$username))
colnames(modulesCompleted) <- c('username', 'modulesCompleted')
modulesData <- left_join(modulesStarted, modulesCompleted, by = 'username')
Column `username` joining factors with different levels, coercing to character vector
modulesData$modulesCompleted[is.na(modulesData$modulesCompleted)] <- 0
# - left_join dataSet w. modulesData
dataSet <- left_join(dataSet, modulesData, 
                     by = c("event_userName" = "username"))
dataSet$modulesStarted[is.na(dataSet$modulesStarted)] <- 0
dataSet$modulesCompleted[is.na(dataSet$modulesCompleted)] <- 0
# - produce aggregates for reporting on training modules from dataSet
# - agg1: tM_started
tM_started <- dataSet %>% 
  select(modulesStarted, edits) %>% 
  group_by(modulesStarted) %>% 
  summarise(sumEdits = sum(edits), users = n())
tM_started$editsPerUser <- round(tM_started$sumEdits/tM_started$users, 2)
# - agg1: tM_completed
tM_completed <- dataSet %>% 
  select(modulesCompleted, edits) %>% 
  group_by(modulesCompleted) %>% 
  summarise(sumEdits = sum(edits), users = n())
tM_completed$editsPerUser <- round(tM_completed$sumEdits/tM_completed$users, 2)
# - agg3: tm_started_perModule
tm_started_perModule <- trainMod %>% 
  select(training_module) %>% 
  group_by(training_module) %>% 
  summarise(Count = n())
# - agg4: tm_completed_perModule
tm_completed_perModule <- trainMod %>%
  filter(!is.na(module_completion_date)) %>%
  select(training_module) %>%
  group_by(training_module) %>% 
  summarise(Count = n())
# - agg5(1-2): exit slides per module
exitSlides1 <- data.frame(t(table(
  trainMod$training_module[trainMod$training_module %in% "diskutieren-basiswissen"], 
  trainMod$last_slide_completed[trainMod$training_module %in% "diskutieren-basiswissen"])))
colnames(exitSlides1) <- c('Slide', 'Module', 'Count')
exitSlides2 <- data.frame(t(table(
  trainMod$training_module[trainMod$training_module %in% "editieren-basiswissen"], 
  trainMod$last_slide_completed[trainMod$training_module %in% "editieren-basiswissen"])))
colnames(exitSlides2) <- c('Slide', 'Module', 'Count')
# - agg6(1-2): time spent in training
trainMod$Completed <- !is.na(trainMod$module_completion_date)
timeInTraining <- trainMod %>% 
  select(Completed, training_module, started_at, last_slide_completed_at) %>% 
  mutate(timeSpent = as.numeric((last_slide_completed_at - started_at)/60)) %>%
  select(Completed, training_module, timeSpent) %>%
  group_by(training_module, Completed) %>%
  summarise(`Time (mins)` = mean(round(timeSpent, 2)))
# - agg7: distribution of time spent in training
timeInTrainingDist <- trainMod %>%
  select(Completed, training_module, started_at, last_slide_completed_at) %>%
  mutate(timeSpent = as.numeric((last_slide_completed_at - started_at)/60))

Table 2. 1 Number of users who have started a particular number of training modules (modulesStarted), the total number of edits that they have made (sumEdits), number of users who took a particular number of training modules (users), and the edits per user ratio.

datatable(tM_started)

Table 2. 2 Number of users who have completed a particular number of training modules (modulesStarted), the total number of edits that they have made (sumEdits), number of users who took a particular number of training modules (users), and the edits per user ratio.

datatable(tM_completed)

Table 2. 3 Number of users who have started a particular training module.

datatable(tm_started_perModule)

Table 2. 4 Number of users who have completed a particular training module.

datatable(tm_completed_perModule)

Table 2. 5a Exit slides per training module and the number of users who have exited at a particular slide.

datatable(exitSlides1)

Table 2. 5b Exit slides per training module and the number of users who have exited at a particular slide.

datatable(exitSlides2)

Table 2. 6 Average Time spent in training per training module.

datatable(timeInTraining)

Chart 2. 7 The distribution of time spent in training per training module.

ggplot(data = timeInTrainingDist, 
       aes(x = Completed, y = timeSpent, color = Completed)) +
  geom_point() +
  geom_jitter() +
  facet_wrap(~training_module) + 
  theme_bw()

LS0tCnRpdGxlOiAnU3VtbWVyIEJhbm5lciBDYW1wYWlnbiAyMDE4OiBSZXBvcnQnCmF1dGhvcjogIkdvcmFuIFMuIE1pbG92YW5vdmljLCBEYXRhIFNjaWVudGlzdCwgV01ERSIKZGF0ZTogIkF1Z3VzdCAwOSwgMjAxOCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRoZW1lOiBzaW1wbGV4CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogNQogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA1Ci0tLQoKCioqRmVlZGJhY2sqKiBzaG91bGQgYmUgc2VuZCB0byBgZ29yYW4ubWlsb3Zhbm92aWNfZXh0QHdpa2ltZWRpYS5kZWAuIAoKVGhlIGNhbXBhaWduIGlzIHJ1biBmcm9tIDIwMTgvMDgvMDEgdG8gMjAxOC8wOC8wOC4KCioqQ1VSUkVOVCBVUERBVEU6KiogQ29tcGxldGUgZGF0YXNldCBhcyBvZiAyMDE4LzA4LzA5LgoKYGBge3IsIGVjaG8gPSBGLCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRiwgcmVzdWx0cyA9ICdoaWRlJ30KIyAhZGlhZ25vc3RpY3Mgb2ZmCiMjIyAtLS0gU2V0dXAKa25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy53aWR0aCA9IDE1LCBmaWcuaGVpZ2h0ID0gOCkgCnJtKGxpc3QgPSBscygpKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KHJtYXJrZG93bikKbGlicmFyeShrbml0cikKbGlicmFyeShEVCkKbGlicmFyeShyZXNoYXBlMikKYGBgCgojIyAwLiBEYXRhIEFjcXVpc2l0b24KCioqTk9URToqKiB0aGUgRGF0YSBBY3F1aXNpdGlvbiBjb2RlIGNodW5rIGlzIG5vdCBmdWxseSByZXByb2R1Y2libGUgZnJvbSB0aGlzIFJlcG9ydC4gVGhlIGRhdGEgYXJlIGNvbGxlY3RlZCBieSBydW5uaW5nIHRoZSBzY3JpcHQgYFN1bW1lckJDMjAxOF9EYWlseVVwZGF0ZV9QUk9EVUNUSU9OLlJgIG9uIHN0YXQxMDA1LmVxaWFkLndtbmV0LCBjb2xsZWN0aW5nIHRoZSBkYXRhIGFzIGAudHN2YCBhbmQgYC5jc3ZgIGZpbGVzLCBjb3B5aW5nIG1hbnVhbGx5LCBhbmQgcHJvY2Vzc2luZyBsb2NhbGx5LiAKCiMjIyAwLjEgRGFpbHkgVXBkYXRlCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGfQoKIyAtIHdsZV8yMDE4X2RlCgojIyMgLS0tIFdNREUgU3ByaW5nIEJhbm5lciBDYW1wYWlnbiAyMDE4CiMjIyAtLS0gRGFpbHkgVXBkYXRlCgpybShsaXN0ID0gbHMoKSkKCiMjIyAtLS0gTGlicmFyaWVzCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShkYXRhLnRhYmxlKQoKIyMjIC0tLSBEaXJlY3RvcmllcwpvdXREaXIgPC0gJy9ob21lL2dvcmFuc20vUlNjcmlwdHMvTmV3RWRpdG9ycy8yMDE4X1N1bW1lckJhbm5lckNhbXBhaWduLycKc2V0d2Qob3V0RGlyKQoKIyMjIC0tLSBHZW5lcmFsIEluZm86CiMgRmlyc3QgQmFubmVyIChodHRwczovL2RlLndpa2lwZWRpYS5vcmcvd2lraS9XaWtpcGVkaWE6SGF1cHRzZWl0ZT9iYW5uZXI9QjE4V01ERV9hdXRob3JzXzAyXzE4MDgwMV8xJmZvcmNlPTEpCiMgUmVnaXN0cmF0aW9uIE5hbWU6IFRlc3Nzc3N0dHR0dAojIENyZWF0ZWQ6IE1vLCAzMC4wNy4yMDE4IGFyb3VuZCAxNTo0MgojIAojIFNlY29uZCBCYW5uZXIgKGh0dHBzOi8vZGUud2lraXBlZGlhLm9yZy93aWtpL1dpa2lwZWRpYTpIYXVwdHNlaXRlP2Jhbm5lcj1CMThXTURFX2F1dGhvcnNfMDJfMTgwODAxXzImZm9yY2U9MSkKIyBSZWdpc3RyYXRpb24gbmFtZTogVGVzc3NzdHR0dHQKIyBDcmVhdGVkOiBNbywgMzAuMDcuMjAxOCBhcm91bmQgMTU6NDUKIyAKIyBUaGlyZCBCYW5uZXIgKGh0dHBzOi8vZGUud2lraXBlZGlhLm9yZy93aWtpL1dpa2lwZWRpYTpIYXVwdHNlaXRlP2Jhbm5lcj1CMThXTURFX2F1dGhvcnNfMDJfMTgwODAxXzMmZm9yY2U9MSkKIyBSZWdpc3RyYXRpb24gbmFtZTogVHR0dGVlc3Nzc3R0CiMgQ3JlYXRlZDogTW8sIDMwLjA3LjIwMTggYXJvdW5kIDE1OjQ1CgojIyMgLS0tIGJhbm5lciBuYW1lcyAoL2JlYWNvbi9pbXByZXNzaW9uKToKIyAtIEIxOFdNREVfYXV0aG9yc18wMl8xODA4MDFfMQojIC0gQjE4V01ERV9hdXRob3JzXzAyXzE4MDgwMV8yCiMgLSBCMThXTURFX2F1dGhvcnNfMDJfMTgwODAxXzMKCiMjIyAtLS0gY2FtcGFpZ24gdGFncwojIC0gV01ERV9uZXdlZGl0b3JzX3N1bW1lcjIwMThfMQojIC0gV01ERV9uZXdlZGl0b3JzX3N1bW1lcjIwMThfMgojIC0gV01ERV9uZXdlZGl0b3JzX3N1bW1lcjIwMThfMwoKIyMjIC0tLSBsYW5kaW5ncGFnZSBpbmNsdWRpbmcgdGhlIGNhbXBhaWduIHRhZyBhbmQgYW5jaG9yCiMgLSBCYW5uZXIgMTogaHR0cHM6Ly9kZS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9OZXVlX0VocmVuYW10bGljaGUvRkFRP2NhbXBhaWduPVdNREVfbmV3ZWRpdG9yc19zdW1tZXIyMDE4XzEjV2VyLWRhcmYtc2NocmVpYmVuCiMgLSBCYW5uZXIgMjogaHR0cHM6Ly9kZS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9OZXVlX0VocmVuYW10bGljaGUvRkFRP2NhbXBhaWduPVdNREVfbmV3ZWRpdG9yc19zdW1tZXIyMDE4XzIjV2lraXBlZGlhLUJ1ZXJvCiMgLSBCYW5uZXIgMzogaHR0cHM6Ly9kZS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9OZXVlX0VocmVuYW10bGljaGUvRkFRP2NhbXBhaWduPVdNREVfbmV3ZWRpdG9yc19zdW1tZXIyMDE4XzMjU2NodXR6LXZvci1NaXNzYnJhdWNoCiMgLSBTZWNvbmQgbGFuZGluZyBwYWdlOiBodHRwczovL2RlLndpa2lwZWRpYS5vcmcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0xlcm5lV2lraXBlZGlhCgojIC0gVXBkYXRlIG9uIChSZW1pbmRlcjogc3RhdDEwMDUgdGltZSBpcyBVVEMpOiB0b2RheSBtaW51cyBvbmUgZGF5CnRvZGF5IDwtIHN0cnNwbGl0KAogIHN0cnNwbGl0KAogICAgYXMuY2hhcmFjdGVyKGFzLlBPU0lYY3QoU3lzLnRpbWUoKSkgLSAyNCo2MCo2MCksCiAgICBzcGxpdCA9ICIgIiwKICAgIGZpeGVkID0gVFJVRSlbWzFdXVsxXSwKICBzcGxpdCA9ICItIiwKICBmaXhlZCA9IFRSVUUpW1sxXV0KdG9kYXkgPC0gYXMubGlzdCh0b2RheSkKbmFtZXModG9kYXkpIDwtIGMoJ3llYXInLCAnbW9udGgnLCAnZGF5JykKCiMgLSBjb25zdHJ1Y3Qgd21mLndlYnJlcXVlc3QgeWVhci9tb250aC9kYXkvaG91ciBjb25kaXRpb24KIyAtIGZvciB0b2RheToKZGF0ZUNvbmRpdGlvbiA8LSBwYXN0ZTAoIigoeWVhciA9ICIsIHRvZGF5JHllYXIsCiAgICAgICAgICAgICAgICAgICAgICAgICIgQU5EIG1vbnRoID0gIiwgdG9kYXkkbW9udGgsCiAgICAgICAgICAgICAgICAgICAgICAgICIgQU5EIGRheSA9ICIsIGFzLmludGVnZXIodG9kYXkkZGF5KSAtIDEsCiAgICAgICAgICAgICAgICAgICAgICAgICIgQU5EIChob3VyID0gMjIgT1IgaG91ciA9IDIzKSkiLAogICAgICAgICAgICAgICAgICAgICAgICAiIE9SICIsCiAgICAgICAgICAgICAgICAgICAgICAgICIoeWVhciA9ICIsIHRvZGF5JHllYXIsCiAgICAgICAgICAgICAgICAgICAgICAgICIgQU5EIG1vbnRoID0gIiwgdG9kYXkkbW9udGgsCiAgICAgICAgICAgICAgICAgICAgICAgICIgQU5EIGRheSA9ICIsIHRvZGF5JGRheSwKICAgICAgICAgICAgICAgICAgICAgICAgIiBBTkQgKGhvdXIgPj0gMCBPUiBob3VyIDwgMjIpKSkiCiAgICAgICAgICAgICAgICAgICAgICAgICkKCiMjIyAtLS0gQ29sbGVjdCBCYW5uZXIgSW1wcmVzc2lvbnMgZm9yIFVwZGF0ZTogSGl2ZVFMCnEgPC0gcGFzdGUoCiAgIlVTRSB3bWY7CiAgU0VMRUNUIHVyaV9xdWVyeSBGUk9NIHdlYnJlcXVlc3QKICBXSEVSRSB1cmlfaG9zdCA9ICdkZS53aWtpcGVkaWEub3JnJwogIEFORCB1cmlfcGF0aCA9ICcvYmVhY29uL2ltcHJlc3Npb24nCiAgQU5EICIsCiAgZGF0ZUNvbmRpdGlvbiwKICAiIEFORCAoKHVyaV9xdWVyeSBMSUtFICclQjE4V01ERV9hdXRob3JzXzAyXzE4MDgwMV8xJScpIE9SCiAgICAodXJpX3F1ZXJ5IExJS0UgJyVCMThXTURFX2F1dGhvcnNfMDJfMTgwODAxXzIlJykgT1IKICAgICh1cmlfcXVlcnkgTElLRSAnJUIxOFdNREVfYXV0aG9yc18wMl8xODA4MDFfMyUnKSk7IiwKICBzZXAgPSAiIikKIyAtIHdyaXRlIGhxbAp3cml0ZShxLCAnc3VtbWVyQkMyMDE4X0Jhbm5lckltcHJlc3Npb25zRGFpbHkuaHFsJykKIyAtIHByZXBhcmUgb3V0cHV0IGZpbGU6CmZpbGVOYW1lIDwtICdzYmMyMDE4X0Jhbm5lckltcHJlc3Npb25zRGFpbHkudHN2JwojIC0gZXhlY3V0ZSBocWwgc2NyaXB0OgpoaXZlQXJncyA8LSAnYmVlbGluZSAtLXZlcmJvc2U9dHJ1ZSAtZicKaGl2ZUlucHV0IDwtIHBhc3RlMCgnc3VtbWVyQkMyMDE4X0Jhbm5lckltcHJlc3Npb25zRGFpbHkuaHFsID4gJywgZmlsZU5hbWUpCiMgLSBjb21tYW5kOgpoaXZlQ29tbWFuZCA8LSBwYXN0ZShoaXZlQXJncywgaGl2ZUlucHV0KQpzeXN0ZW0oY29tbWFuZCA9IGhpdmVDb21tYW5kLCB3YWl0ID0gVFJVRSkKCiMjIyAtLS0gQ29sbGVjdCBQYWdldmlld3MgPT0gQmFubmVyIGNsaWNrcyBmb3IgVXBkYXRlOiBIaXZlUUwKIyBodHRwczovL2RlLndpa2lwZWRpYS5vcmcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0xlcm5lV2lraXBlZGlhCnEgPC0gcGFzdGUoCiAgIlVTRSB3bWY7CiAgU0VMRUNUIHVyaV9wYXRoLCB1cmlfcXVlcnksIHJlZmVyZXIgRlJPTSB3ZWJyZXF1ZXN0CiAgV0hFUkUgdXJpX2hvc3QgPSAnZGUud2lraXBlZGlhLm9yZycKICBBTkQgKCh1cmlfcGF0aCA9ICcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL05ldWVfRWhyZW5hbXRsaWNoZS9GQVEnIEFORCB1cmlfcXVlcnkgTElLRSAnJWNhbXBhaWduPVdNREVfbmV3ZWRpdG9yc19zdW1tZXIyMDE4JScpCiAgT1IgKHVyaV9wYXRoID0gJy93aWtpL1dpa2lwZWRpYTpXaWtpbWVkaWFfRGV1dHNjaGxhbmQvTGVybmVXaWtpcGVkaWEnKSkgCiAgQU5EICIsIGRhdGVDb25kaXRpb24sCiAgIiA7IiwKICBzZXAgPSAiIikKIyAtIHdyaXRlIGhxbAp3cml0ZShxLCAnc3VtbWVyQkMyMDE4X1BhZ2VWaWV3c0RhaWx5LmhxbCcpCiMgLSBwcmVwYXJlIG91dHB1dCBmaWxlOgpmaWxlTmFtZSA8LSAnc2JjMjAxOF9QYWdlVmlld3NEYWlseS50c3YnCiMgLSBleGVjdXRlIGhxbCBzY3JpcHQ6CmhpdmVBcmdzIDwtICdiZWVsaW5lIC0tdmVyYm9zZT10cnVlIC1mJwpoaXZlSW5wdXQgPC0gcGFzdGUwKCdzdW1tZXJCQzIwMThfUGFnZVZpZXdzRGFpbHkuaHFsID4gJywgZmlsZU5hbWUpCiMgLSBjb21tYW5kOgpoaXZlQ29tbWFuZCA8LSBwYXN0ZShoaXZlQXJncywgaGl2ZUlucHV0KQpzeXN0ZW0oY29tbWFuZCA9IGhpdmVDb21tYW5kLCB3YWl0ID0gVFJVRSkKCiMjIyAtLS0gQ29sbGVjdCBVc2UgUmVnaXN0cmF0aW9ucyBmb3IgVXBkYXRlOiBTUUwKIyAtIFNlcnZlclNpZGVBY2NvdW50Q3JlYXRpb25fMTc3MTkyMzcgc2NoZW1hCnFDb21tYW5kIDwtICJteXNxbCAtLWRlZmF1bHRzLWZpbGU9L2V0Yy9teXNxbC9jb25mLmQvYW5hbHl0aWNzLXJlc2VhcmNoLWNsaWVudC5jbmYgLWggYW5hbHl0aWNzLXNsYXZlLmVxaWFkLndtbmV0IC1BIC1lIFwic2VsZWN0ICogZnJvbSBsb2cuU2VydmVyU2lkZUFjY291bnRDcmVhdGlvbl8xNzcxOTIzNyB3aGVyZSAoKHdlYkhvc3QgPSAnZGUud2lraXBlZGlhLm9yZycpIGFuZCAodGltZXN0YW1wID49IDIwMTgwNzI5MDAwMDAwKSBhbmQgKChldmVudF9jYW1wYWlnbiBsaWtlICclV01ERV9uZXdlZGl0b3JzX3N1bW1lcjIwMThfMSUnKSBvciAoZXZlbnRfY2FtcGFpZ24gbGlrZSAnJVdNREVfbmV3ZWRpdG9yc19zdW1tZXIyMDE4XzIlJykgb3IgKGV2ZW50X2NhbXBhaWduIGxpa2UgJyVXTURFX25ld2VkaXRvcnNfc3VtbWVyMjAxOF8zJScpKSk7XCIgPiAvaG9tZS9nb3JhbnNtL1JTY3JpcHRzL05ld0VkaXRvcnMvMjAxOF9TdW1tZXJCYW5uZXJDYW1wYWlnbi9zdW1tZXJCQzIwMThfdXNlclJlZ2lzdHJhdGlvbnMudHN2IgpzeXN0ZW0oY29tbWFuZCA9IHFDb21tYW5kLCB3YWl0ID0gVFJVRSkKCiMjIyAtLS0gQ29sbGVjdCBCYW5uZXIgRXZlbnRzIGZvciBVcGRhdGU6IFNRTAojIC0gV01ERUJhbm5lckV2ZW50c18xODE5Mzk0OCBzY2hlbWEKcUNvbW1hbmQgPC0gIm15c3FsIC0tZGVmYXVsdHMtZmlsZT0vZXRjL215c3FsL2NvbmYuZC9hbmFseXRpY3MtcmVzZWFyY2gtY2xpZW50LmNuZiAtaCBhbmFseXRpY3Mtc2xhdmUuZXFpYWQud21uZXQgLUEgLWUgXCJzZWxlY3QgKiBmcm9tIGxvZy5XTURFQmFubmVyRXZlbnRzXzE4MTkzOTQ4IHdoZXJlIHRpbWVzdGFtcCA+PSAyMDE4MDcyOTAwMDAwMDtcIiA+IC9ob21lL2dvcmFuc20vUlNjcmlwdHMvTmV3RWRpdG9ycy8yMDE4X1N1bW1lckJhbm5lckNhbXBhaWduL3N1bW1lckJDMjAxOF9iYW5uZXJFdmVudHMudHN2IgpzeXN0ZW0oY29tbWFuZCA9IHFDb21tYW5kLCB3YWl0ID0gVFJVRSkKCgojIyMgLS0tIFByZXBhcmUgZm9yIHJlcG90aW5nCgojIC0gQmFubmVyIEltcHJlc3Npb25zCmJJbXBEYXRhIDwtIHJlYWRMaW5lcygnc2JjMjAxOF9CYW5uZXJJbXByZXNzaW9uc0RhaWx5LnRzdicsIG4gPSAtMSkKYkltcERhdGEgPC0gYkltcERhdGFbOToobGVuZ3RoKGJJbXBEYXRhKSAtIDIpXQpiSW1wRGF0YSA8LSBiSW1wRGF0YVstMV0KYkltcERhdGEgPC0gZGF0YS5mcmFtZShkYXQgPSBiSW1wRGF0YSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmJhbm5lckEgPC0gbGVuZ3RoKHdoaWNoKGdyZXBsKCJiYW5uZXI9QjE4V01ERV9hdXRob3JzXzAyXzE4MDgwMV8xIiwgYkltcERhdGEkZGF0LCBmaXhlZCA9IFQpKSkKYmFubmVyQiA8LSBsZW5ndGgod2hpY2goZ3JlcGwoImJhbm5lcj1CMThXTURFX2F1dGhvcnNfMDJfMTgwODAxXzIiLCBiSW1wRGF0YSRkYXQsIGZpeGVkID0gVCkpKQpiYW5uZXJDIDwtIGxlbmd0aCh3aGljaChncmVwbCgiYmFubmVyPUIxOFdNREVfYXV0aG9yc18wMl8xODA4MDFfMyIsIGJJbXBEYXRhJGRhdCwgZml4ZWQgPSBUKSkpCkRhaWx5QmFubmVySW1wcmVzc2lvbnMgPC0gZGF0YS5mcmFtZSgKICBCYW5uZXIgPSBjKCdCMThXTURFX2F1dGhvcnNfMScsICdCMThXTURFX2F1dGhvcnNfMicsICdCMThXTURFX2F1dGhvcnNfMycpLAogIENvdW50ID0gYyhiYW5uZXJBLCBiYW5uZXJCLCBiYW5uZXJDKSwKICBDYW1wYWlnbkRheSA9IHJlcChwYXN0ZTAodG9kYXksIGNvbGxhcHNlID0gIi0iLCBzZXAgPSAiIiksIDMpCikKRGFpbHlCYW5uZXJJbXByZXNzaW9ucyRDb3VudCA8LSBEYWlseUJhbm5lckltcHJlc3Npb25zJENvdW50Ly4wMQojIC0gRGFpbHlCYW5uZXJJbXByZXNzaW9ucwpsRiA8LSBsaXN0LmZpbGVzKCkKaWYgKCdEYWlseUJhbm5lckltcHJlc3Npb25zLmNzdicgJWluJSBsRikgewogIGRQUyA8LSByZWFkLmNzdignRGFpbHlCYW5uZXJJbXByZXNzaW9ucy5jc3YnLAogICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxKQogIERhaWx5QmFubmVySW1wcmVzc2lvbnMgPC0gcmJpbmQoZFBTLCBEYWlseUJhbm5lckltcHJlc3Npb25zKQogIHdyaXRlLmNzdihEYWlseUJhbm5lckltcHJlc3Npb25zLCAnRGFpbHlCYW5uZXJJbXByZXNzaW9ucy5jc3YnKQp9IGVsc2UgewogIHdyaXRlLmNzdihEYWlseUJhbm5lckltcHJlc3Npb25zLCAnRGFpbHlCYW5uZXJJbXByZXNzaW9ucy5jc3YnKQp9CgojIC0gUGFnZXZpZXdzCnBhZ2VWaWV3cyA8LSByZWFkTGluZXMoJ3NiYzIwMThfUGFnZVZpZXdzRGFpbHkudHN2JywgbiA9IC0xKQpwYWdlVmlld3MgPC0gcGFnZVZpZXdzWzg6KGxlbmd0aChwYWdlVmlld3MpIC0gMildCmhlYWRlciA9IHBhZ2VWaWV3c1sxXQpwYWdlVmlld3MgPC0gcGFnZVZpZXdzWy0xXQpoZWFkZXIgPC0gdW5saXN0KHN0cnNwbGl0KGhlYWRlciwgIlx0IilbWzFdXSkKcGFnZVZpZXdzIDwtIGRhdGEuZnJhbWUoZGF0ID0gcGFnZVZpZXdzLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKcGFnZVZpZXdzIDwtIHNlcGFyYXRlKHBhZ2VWaWV3cywKICAgICAgICAgICAgICAgICAgICAgIGRhdCwKICAgICAgICAgICAgICAgICAgICAgIGludG8gPSBjKCd1cmlfcGF0aCcsICd1cmlfcXVlcnknLCAncmVmZXJlcicpLAogICAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IikKZGFpbHlQYWdldmlld3NTZXQgPC0gcGFnZVZpZXdzICU+JSAKICBmaWx0ZXIoZ3JlcGwoJ2NhbXBhaWduPVdNREVfbmV3ZWRpdG9yc19zdW1tZXIyMDE4JywgdXJpX3F1ZXJ5KSB8IAogICAgICAgICAgIGdyZXBsKCcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0xlcm5lV2lraXBlZGlhJywgdXJpX3BhdGgpKSAlPiUKICBncm91cF9ieSh1cmlfcXVlcnksIHVyaV9wYXRoKSAlPiUKICBzdW1tYXJpc2UoQ291bnQgPSBuKCkpCmRhaWx5UGFnZXZpZXdzU2V0JENhbXBhaWduRGF5IDwtIHBhc3RlKHVubGlzdCh0b2RheSksIGNvbGxhcHNlID0gIi0iLCBzZXAgPSAiIikKIyAtIERhaWx5UGFnZXZpZXdzCmxGIDwtIGxpc3QuZmlsZXMoKQppZiAoJ0RhaWx5UGFnZXZpZXdzLmNzdicgJWluJSBsRikgewogIGRQUyA8LSByZWFkLmNzdignRGFpbHlQYWdldmlld3MuY3N2JywKICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSkKICBkYWlseVBhZ2V2aWV3c1NldCA8LSByYmluZChkUFMsIGFzLmRhdGEuZnJhbWUoZGFpbHlQYWdldmlld3NTZXQpKQogIHdyaXRlLmNzdihkYWlseVBhZ2V2aWV3c1NldCwgJ0RhaWx5UGFnZXZpZXdzLmNzdicpCn0gZWxzZSB7CiAgd3JpdGUuY3N2KGRhaWx5UGFnZXZpZXdzU2V0LCAnRGFpbHlQYWdldmlld3MuY3N2JykKfQoKIyAtIFVzZXIgcmVnaXN0cmF0aW9ucwp1c2VyUmVnaXN0cmF0aW9ucyA8LSByZWFkLmRlbGltKCdzdW1tZXJCQzIwMThfdXNlclJlZ2lzdHJhdGlvbnMudHN2JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1b3RlID0gIiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQp1c2VyUmVnaXN0cmF0aW9ucyA8LSB1c2VyUmVnaXN0cmF0aW9uc1tncmVwbCgnaXNfYm90IjogZmFsc2UnLCB1c2VyUmVnaXN0cmF0aW9ucyR1c2VyQWdlbnQsIGZpeGVkID0gVCksIF0KdXNlclJlZ2lzdHJhdGlvbnMkdGltZXN0YW1wIDwtIGFzLmNoYXJhY3Rlcih1c2VyUmVnaXN0cmF0aW9ucyR0aW1lc3RhbXApCnVzZXJSZWdpc3RyYXRpb25zJHRpbWVzdGFtcCA8LSBhcy5QT1NJWGN0KHVzZXJSZWdpc3RyYXRpb25zJHRpbWVzdGFtcCwgdHogPSAiVVRDIiwgZm9ybWF0ID0gIiVZJW0lZCVIJU0lUyIpIAojIC0gQ0VTVCBjb3JyZWN0aW9uIG9uIFBPU0lYY3QgY2xhc3MsICsyaG91cnM6CnVzZXJSZWdpc3RyYXRpb25zJHRpbWVzdGFtcCA8LSB1c2VyUmVnaXN0cmF0aW9ucyR0aW1lc3RhbXAgKyAyKjYwKjYwCiMgLSB3cmFuZ2xlCnVzZXJSZWdpc3RyYXRpb25zJENhbXBhaWduRGF5IDwtIHNhcHBseSh1c2VyUmVnaXN0cmF0aW9ucyR0aW1lc3RhbXAsIGZ1bmN0aW9uKHgpIHsKICBwYXN0ZShjKAogICAgc3Vic3RyKHgsIDEsNCksCiAgICBzdWJzdHIoeCwgNiw3KSwKICAgIHN1YnN0cih4LCA5LDEwKQogICAgKSwKICAgIGNvbGxhcHNlID0gIi0iLAogICAgc2VwID0gIiIpCn0pCnVzZXJSZWdpc3RyYXRpb25zIDwtIHVzZXJSZWdpc3RyYXRpb25zW3doaWNoKHVzZXJSZWdpc3RyYXRpb25zJENhbXBhaWduRGF5ICVpbiUgcGFzdGUodG9kYXksIGNvbGxhcHNlID0gIi0iLCBzZXAgPSAiIikpLCBdCmRhaWx5VXNlclJlZ1NldCA8LSB1c2VyUmVnaXN0cmF0aW9ucyAlPiUKICBzZWxlY3QoZXZlbnRfY2FtcGFpZ24pICU+JQogIGdyb3VwX2J5KGV2ZW50X2NhbXBhaWduKSAlPiUKICBzdW1tYXJpc2UoQ291bnQgPSBuKCkpCmRhaWx5VXNlclJlZ1NldCRDYW1wYWlnbkRheSA8LSBwYXN0ZSh1bmxpc3QodG9kYXkpLCBjb2xsYXBzZSA9ICItIiwgc2VwID0gIiIpCiMgLSBEYWlseSBVc2VyIFJlZ2lzdHJhdGlvbnMKbEYgPC0gbGlzdC5maWxlcygpCmlmICgnZGFpbHlVc2VyUmVnaXN0cmF0aW9ucy5jc3YnICVpbiUgbEYpIHsKICBkUFMgPC0gcmVhZC5jc3YoJ2RhaWx5VXNlclJlZ2lzdHJhdGlvbnMuY3N2JywgCiAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsIAogICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxKQogIGRhaWx5VXNlclJlZ1NldCA8LSByYmluZChkUFMsIGRhaWx5VXNlclJlZ1NldCkKICB3cml0ZS5jc3YoZGFpbHlVc2VyUmVnU2V0LCAnZGFpbHlVc2VyUmVnaXN0cmF0aW9ucy5jc3YnKQp9IGVsc2UgewogIHdyaXRlLmNzdihkYWlseVVzZXJSZWdTZXQsICdkYWlseVVzZXJSZWdpc3RyYXRpb25zLmNzdicpCn0KYGBgCgojIyMgMC4xIFBvc3QtQ2FtcGFpZ24gQW5hbHl0aWNzCgoqKk5PVEU6KiogVHJhaW5pbmcgTW9kdWxlIGRhdGEgYXJlIG9idGFpbmVkIGRpcmVjdGx5IGZyb20gdGhlIGFwcGxpY2F0aW9uIG1haW50YWluZXIuIFRoZSBmb2xsb3dpbmcgY29kZSByZXRyaWV2ZXMgYWxsIHVzZXIgcmVnaXN0cmF0aW9ucyBhbmQgZWRpdHMuCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGfQojIyMgLS0tIFVzZXIgcmVnaXN0cmF0aW9ucwoKIyMjIC0tLSBMaWJyYXJpZXMKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGRhdGEudGFibGUpCgojIC0gU2VydmVyU2lkZUFjY291bnRDcmVhdGlvbl81NDg3MzQ1CnFDb21tYW5kIDwtICJteXNxbCAtLWRlZmF1bHRzLWZpbGU9L2V0Yy9teXNxbC9jb25mLmQvYW5hbHl0aWNzLXJlc2VhcmNoLWNsaWVudC5jbmYgLWggYW5hbHl0aWNzLXNsYXZlLmVxaWFkLndtbmV0IC1BIC1lIFwic2VsZWN0ICogZnJvbSBsb2cuU2VydmVyU2lkZUFjY291bnRDcmVhdGlvbl8xNzcxOTIzNyB3aGVyZSAoKHdlYkhvc3QgPSAnZGUud2lraXBlZGlhLm9yZycpIGFuZCAodGltZXN0YW1wID49IDIwMTgwNzI5MDAwMDAwKSBhbmQgKChldmVudF9jYW1wYWlnbiBsaWtlICclV01ERV9uZXdlZGl0b3JzX3N1bW1lcjIwMThfMSUnKSBvciAoZXZlbnRfY2FtcGFpZ24gbGlrZSAnJVdNREVfbmV3ZWRpdG9yc19zdW1tZXIyMDE4XzIlJykgb3IgKGV2ZW50X2NhbXBhaWduIGxpa2UgJyVXTURFX25ld2VkaXRvcnNfc3VtbWVyMjAxOF8zJScpKSk7XCIgPiAvaG9tZS9nb3JhbnNtL1JTY3JpcHRzL05ld0VkaXRvcnMvMjAxOF9TdW1tZXJCYW5uZXJDYW1wYWlnbi9zdW1tZXJCQzIwMThfdXNlclJlZ2lzdHJhdGlvbnNfRlVMTC50c3YiCnN5c3RlbShjb21tYW5kID0gcUNvbW1hbmQsIHdhaXQgPSBUUlVFKQoKIyMjIC0tLSBVc2VyIGVkaXRzCiMgLSBnZXQgdXNlciBJRHMgZnJvbSByZWdpc3RlcmVkOgp1c2VyUmVnIDwtIHJlYWQudGFibGUoJy9ob21lL2dvcmFuc20vUlNjcmlwdHMvTmV3RWRpdG9ycy8yMDE4X1N1bW1lckJhbm5lckNhbXBhaWduL3N1bW1lckJDMjAxOF91c2VyUmVnaXN0cmF0aW9uc19GVUxMLnRzdicsIAogICAgICAgICAgICAgICAgICAgICAgcXVvdGUgPSAiIiwKICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnVzZXJSZWcgPC0gdXNlclJlZyAlPiUgCiAgZHBseXI6OnNlbGVjdChldmVudF91c2VySWQsIGV2ZW50X2lzU2VsZk1hZGUpICU+JSAKICBmaWx0ZXIoZXZlbnRfaXNTZWxmTWFkZSA9PSAxKQojIC0gdWlkczoKdWlkIDwtIHVzZXJSZWckZXZlbnRfdXNlcklkCiMgLSBzcWwgcXVlcnkKc3FsUXVlcnkgPC0gcGFzdGUoJ1NFTEVDVCBDT1VOVCgqKSBhcyBlZGl0cywgcmV2X3VzZXIgRlJPTSByZXZpc2lvbiBXSEVSRSByZXZfdXNlciBJTiAoJywKICAgICAgICAgICAgICAgICAgcGFzdGUodWlkLCBjb2xsYXBzZSA9ICIsICIpLAogICAgICAgICAgICAgICAgICAnKSBHUk9VUCBCWSByZXZfdXNlcjsnLAogICAgICAgICAgICAgICAgICBzZXAgPSAiIikKbXlTcWxDb21tYW5kIDwtIHBhc3RlKCdteXNxbCAtaCBhbmFseXRpY3Mtc3RvcmUuZXFpYWQud21uZXQgZGV3aWtpIC1lICcsCiAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgnIicsIHNxbFF1ZXJ5LCAnIiA+ICcsIHNlcCA9ICIiKSwKICAgICAgICAgICAgICAgICAgICAgICcvaG9tZS9nb3JhbnNtL1JTY3JpcHRzL05ld0VkaXRvcnMvMjAxOF9TdW1tZXJCYW5uZXJDYW1wYWlnbi9zQkMyMDE4X3VzZXJFZGl0c19GVUxMLnRzdicsIHNlcCA9ICIiKQpzeXN0ZW0oY29tbWFuZCA9IG15U3FsQ29tbWFuZCwgCiAgICAgICB3YWl0ID0gVFJVRSkKYGBgCgojIyAxLiBDYW1wYWlnbiBCYW5uZXJzIGFuZCBQYWdlcwoKVGhpcyBzZWN0aW9uIHByZXNlbnRzIGFsbCBkYXRhIGFuZCBzdGF0aXN0aWNzIG9uIHRoZSBjYW1wYWlnbiBiYW5uZXJzIGFuZCBwYWdlcy4KCiMjIyAxLjEgQmFubmVyIEltcHJlc3Npb25zCgoqKkNoYXJ0IDEuIDEuIERhaWx5IEJhbm5lciBJbXByZXNzaW9ucyAqKgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmRhdGFTZXQgPC0gcmVhZC5jc3YoJy9ob21lL2dvcmFuc20vV29yay9fX19EYXRhS29sZWt0aXYvUHJvamVjdHMvV2lraW1lZGlhREVVL19XTURFX1Byb2plY3RzL19taXNjL05ld0VkaXRvcnNfVGVhbS8yMDE4X1N1bW1lckJhbm5lckNhbXBhaWduL19kYXRhL0RhaWx5QmFubmVySW1wcmVzc2lvbnMuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKIyAtIGJhbm5lciBpbXByZXNzaW9uIHJhdGUgPT0gLjAxCiMgLSBkYXRhU2V0JENvdW50IDwtIGRhdGFTZXQkQ291bnQgKiAoMS8uMDEpCgojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfQpnZ3Bsb3QoZGF0YVNldCwgYWVzKHggPSBDYW1wYWlnbkRheSwKICAgICAgICAgICAgICAgICAgICB5ID0gQ291bnQsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBCYW5uZXIsCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBCYW5uZXIsCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9IEJhbm5lciwKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IENvdW50LAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdTdW1tZXIgQmFubmVyIENhbXBhaWduIDIwMTg6IEJhbm5lciBJbXByZXNzaW9ucycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKKipUYWJsZSAxLiAxLiBEYWlseSBCYW5uZXIgSW1wcmVzc2lvbnMgKioKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KIyMjIC0tLSBGdWxsIERhdGFzZXQgKFRhYmxlIFJlcG9ydCkKZGF0YXRhYmxlKGRhdGFTZXQpCmBgYAoKKipUYWJsZSAxLiAxLiAxIFRvdGFsIEJhbm5lciBJbXByZXNzaW9ucyBwZXIgQmFubmVyICoqCgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CiMjIyAtLS0gU3VtbWFyeSBTdGF0cyBmb3IgQmFubmVyIEltcHJlc3Npb25zCnRvdGFsSW1wcmVzc2lvbnMgPC0gZGF0YVNldCAlPiUgCiAgZ3JvdXBfYnkoQmFubmVyKSAlPiUgCiAgc3VtbWFyaXNlKHRvdGFsSW1wcmVzc2lvbnMgPSBzdW0oQ291bnQpKQpkYXRhdGFibGUodG90YWxJbXByZXNzaW9ucykKYGBgCgojIyMgMS4yIEJhbm5lciBDbGlja3MgKFBhZ2V2aWV3cykKCioqQ2hhcnQgMS4gMi4gRGFpbHkgQmFubmVyIENsaWNrcyAoTGFuZGluZyBQYWdlIFZpZXdzKSAqKgoKVGhlIGB1cmlfcXVlcnlgIGZpZWxkIGRlZmluZXMgdGhlIHNvdXJjZSBvZiB0aGUgcmVzcGVjdGl2ZSBwYWdldmlldy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpkYXRhU2V0IDwtIHJlYWQuY3N2KHBhc3RlKCcvaG9tZS9nb3JhbnNtL1dvcmsvX19fRGF0YUtvbGVrdGl2L1Byb2plY3RzL1dpa2ltZWRpYURFVS9fV01ERV9Qcm9qZWN0cy9fbWlzYy9OZXdFZGl0b3JzX1RlYW0vMjAxOF9TdW1tZXJCYW5uZXJDYW1wYWlnbi9fZGF0YS9EYWlseVBhZ2V2aWV3cy5jc3YnKSwKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpkYXRhU2V0JHVyaV9wYXRoIDwtIGdzdWIoIi93aWtpL1dpa2lwZWRpYTpXaWtpbWVkaWFfRGV1dHNjaGxhbmQvIiwgIiIsIGRhdGFTZXQkdXJpX3BhdGgpCmRhdGFTZXQkdXJpX3F1ZXJ5IDwtIGdzdWIoIj9jYW1wYWlnbj0iLCAiIiwgZGF0YVNldCR1cmlfcXVlcnksIGZpeGVkID0gVCkKZGF0YVNldCA8LSBkYXRhU2V0ICU+JSAKICBmaWx0ZXIoIShncmVwbCgiV01ERV8yMDE4X3NwcmJ0MXxXTURFXzIwMThfc3ByYnQyfHdtZGVfZXRjMjAxN19idDEiLCB1cmlfcXVlcnkpKSkKZGF0YVNldCR1cmlfcXVlcnlbd2hpY2goZGF0YVNldCR1cmlfcXVlcnkgPT0gIiIpXSA8LSAiTm8gcmVmZXJlciIKZGF0YVNldCR1cmlfcXVlcnlbd2hpY2goZ3JlcGwoIl5XTURFX25ld2VkaXRvcnNfc3VtbWVyMjAxOF8xIiwgZGF0YVNldCR1cmlfcXVlcnkpKV0gPC0gIldNREVfbmV3ZWRpdG9yc19zdW1tZXIyMDE4XzEiCmRhdGFTZXQkdXJpX3F1ZXJ5W3doaWNoKGdyZXBsKCJeV01ERV9uZXdlZGl0b3JzX3N1bW1lcjIwMThfMiIsIGRhdGFTZXQkdXJpX3F1ZXJ5KSldIDwtICJXTURFX25ld2VkaXRvcnNfc3VtbWVyMjAxOF8yIgpkYXRhU2V0JHVyaV9xdWVyeVt3aGljaChncmVwbCgiXldNREVfbmV3ZWRpdG9yc19zdW1tZXIyMDE4XzMiLCBkYXRhU2V0JHVyaV9xdWVyeSkpXSA8LSAiV01ERV9uZXdlZGl0b3JzX3N1bW1lcjIwMThfMyIKZGF0YVNldCA8LSBkYXRhU2V0ICU+JSAKICBncm91cF9ieSh1cmlfcXVlcnksIHVyaV9wYXRoLCBDYW1wYWlnbkRheSkgJT4lIAogIHN1bW1hcmlzZShDb3VudCA9IHN1bShDb3VudCkpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChkYXRhU2V0LCBhZXMoeCA9IENhbXBhaWduRGF5LAogICAgICAgICAgICAgICAgICAgIHkgPSBDb3VudCwKICAgICAgICAgICAgICAgICAgICBncm91cCA9IHVyaV9xdWVyeSwKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHVyaV9xdWVyeSwKICAgICAgICAgICAgICAgICAgICBmaWxsID0gdXJpX3F1ZXJ5LAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gQ291bnQpKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArIAogIGZhY2V0X3dyYXAofiB1cmlfcGF0aCwgc2NhbGVzID0gImZyZWUiKSArIAogIGdndGl0bGUoJ1N1bW1lciBCYW5uZXIgQ2FtcGFpZ24gMjAxODogQmFubmVyIENsaWtzIChQYWdldmlld3MpJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICAncmlnaHQnKSArIAogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gIGVsZW1lbnRfdGV4dChzaXplID0gNykpCmBgYAoKCioqVGFibGUgMS4gMi4gRGFpbHkgQmFubmVyIENsaWNrcyAoTGFuZGluZyBQYWdlIFZpZXdzKSAqKgoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQojIyMgLS0tIEZ1bGwgRGF0YXNldCAoVGFibGUgUmVwb3J0KQpkYXRhU2V0IDwtIHJlYWQuY3N2KHBhc3RlKCcvaG9tZS9nb3JhbnNtL1dvcmsvX19fRGF0YUtvbGVrdGl2L1Byb2plY3RzL1dpa2ltZWRpYURFVS9fV01ERV9Qcm9qZWN0cy9fbWlzYy9OZXdFZGl0b3JzX1RlYW0vMjAxOF9TdW1tZXJCYW5uZXJDYW1wYWlnbi9fZGF0YS9EYWlseVBhZ2V2aWV3cy5jc3YnKSwKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpkYXRhU2V0JHVyaV9wYXRoIDwtIGdzdWIoIi93aWtpL1dpa2lwZWRpYTpXaWtpbWVkaWFfRGV1dHNjaGxhbmQvIiwgIiIsIGRhdGFTZXQkdXJpX3BhdGgpCmRhdGFTZXQkdXJpX3F1ZXJ5IDwtIGdzdWIoIj9jYW1wYWlnbj0iLCAiIiwgZGF0YVNldCR1cmlfcXVlcnksIGZpeGVkID0gVCkKZGF0YVNldCA8LSBkYXRhU2V0ICU+JSAKICBmaWx0ZXIoIShncmVwbCgiV01ERV8yMDE4X3NwcmJ0MXxXTURFXzIwMThfc3ByYnQyfHdtZGVfZXRjMjAxN19idDEiLCB1cmlfcXVlcnkpKSkKZGF0YVNldCR1cmlfcXVlcnlbd2hpY2goZGF0YVNldCR1cmlfcXVlcnkgPT0gIiIpXSA8LSAiTm8gcmVmZXJlciIKZGF0YVNldCR1cmlfcXVlcnlbd2hpY2goZ3JlcGwoIl5XTURFX25ld2VkaXRvcnNfc3VtbWVyMjAxOF8xIiwgZGF0YVNldCR1cmlfcXVlcnkpKV0gPC0gIldNREVfbmV3ZWRpdG9yc19zdW1tZXIyMDE4XzEiCmRhdGFTZXQkdXJpX3F1ZXJ5W3doaWNoKGdyZXBsKCJeV01ERV9uZXdlZGl0b3JzX3N1bW1lcjIwMThfMiIsIGRhdGFTZXQkdXJpX3F1ZXJ5KSldIDwtICJXTURFX25ld2VkaXRvcnNfc3VtbWVyMjAxOF8yIgpkYXRhU2V0JHVyaV9xdWVyeVt3aGljaChncmVwbCgiXldNREVfbmV3ZWRpdG9yc19zdW1tZXIyMDE4XzMiLCBkYXRhU2V0JHVyaV9xdWVyeSkpXSA8LSAiV01ERV9uZXdlZGl0b3JzX3N1bW1lcjIwMThfMyIKZGF0YVNldCA8LSBkYXRhU2V0ICU+JSAKICBncm91cF9ieSh1cmlfcXVlcnksIHVyaV9wYXRoLCBDYW1wYWlnbkRheSkgJT4lIAogIHN1bW1hcmlzZShDb3VudCA9IHN1bShDb3VudCkpCmRhdGF0YWJsZShkYXRhU2V0KQpgYGAKCioqVGFibGUgMS4gMi4gMSBUb3RhbCBCYW5uZXIgQ2xpY2tzL1BhZ2V2aWV3cyBwZXIgQmFubmVyL1NvdXJjZSAqKgoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQojIyMgLS0tIFN1bW1hcnkgU3RhdHMgZm9yIEJhbm5lciBJbXByZXNzaW9ucwp0b3RhbENsaWNrcyA8LSBkYXRhU2V0ICU+JSAKICBncm91cF9ieSh1cmlfcXVlcnkpICU+JSAKICBzdW1tYXJpc2UodG90YWxDbGlja3MgPSBzdW0oQ291bnQpKSAlPiUgCiAgYXJyYW5nZShkZXNjKHRvdGFsQ2xpY2tzKSkKZGF0YXRhYmxlKHRvdGFsQ2xpY2tzKQpgYGAKCiMjIyAxLjMgQmFubmVyIENsaWNrcy9JbXByZXNzaW9ucyBDbGlja3MgKFBhZ2V2aWV3cykKCioqQ2hhcnQgMS4gMy4gVGhlIENsaWNrcy9JbXByZXNzaW9ucyByYXRpbyBwZXIgYmFubmVyKioKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQppbXByZXNzaW9ucyA8LSByZWFkLmNzdignL2hvbWUvZ29yYW5zbS9Xb3JrL19fX0RhdGFLb2xla3Rpdi9Qcm9qZWN0cy9XaWtpbWVkaWFERVUvX1dNREVfUHJvamVjdHMvX21pc2MvTmV3RWRpdG9yc19UZWFtLzIwMThfU3VtbWVyQmFubmVyQ2FtcGFpZ24vX2RhdGEvRGFpbHlCYW5uZXJJbXByZXNzaW9ucy5jc3YnLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiMgLSBhZGp1c3QgZm9yIGJhbm5lciBpbXByZXNzaW9ucyBzYW1wbGUgcmF0ZQpjbGlja3MgPC0gcmVhZC5jc3YocGFzdGUoJy9ob21lL2dvcmFuc20vV29yay9fX19EYXRhS29sZWt0aXYvUHJvamVjdHMvV2lraW1lZGlhREVVL19XTURFX1Byb2plY3RzL19taXNjL05ld0VkaXRvcnNfVGVhbS8yMDE4X1N1bW1lckJhbm5lckNhbXBhaWduL19kYXRhL0RhaWx5UGFnZXZpZXdzLmNzdicpLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNsaWNrcyR1cmlfcXVlcnlbd2hpY2goY2xpY2tzJHVyaV9xdWVyeSA9PSAiIildIDwtICJObyByZWZlcmVyIgpjbGlja3MkdXJpX3F1ZXJ5IDwtIGdzdWIoIj9jYW1wYWlnbj0iLCAiIiwgY2xpY2tzJHVyaV9xdWVyeSwgZml4ZWQgPSBUKQpjbGlja3MgPC0gY2xpY2tzICU+JSAKICBmaWx0ZXIoIShncmVwbCgiV01ERV8yMDE4X3NwcmJ0MXxXTURFXzIwMThfc3ByYnQyfHdtZGVfZXRjMjAxN19idDEiLCB1cmlfcXVlcnkpKSkKY2xpY2tzJHVyaV9xdWVyeVtncmVwbCgiXldNREVfbmV3ZWRpdG9yc19zdW1tZXIyMDE4XzEiLCBjbGlja3MkdXJpX3F1ZXJ5KV0gPC0gJ0IxOFdNREVfYXV0aG9yc18xJwpjbGlja3MkdXJpX3F1ZXJ5W2dyZXBsKCJeV01ERV9uZXdlZGl0b3JzX3N1bW1lcjIwMThfMiIsIGNsaWNrcyR1cmlfcXVlcnkpXSA8LSAnQjE4V01ERV9hdXRob3JzXzInCmNsaWNrcyR1cmlfcXVlcnlbZ3JlcGwoIl5XTURFX25ld2VkaXRvcnNfc3VtbWVyMjAxOF8zIiwgY2xpY2tzJHVyaV9xdWVyeSldIDwtICdCMThXTURFX2F1dGhvcnNfMycKY2xpY2tzIDwtIGNsaWNrcyAlPiUgCiAgZ3JvdXBfYnkodXJpX3F1ZXJ5LCBDYW1wYWlnbkRheSkgJT4lIAogIHN1bW1hcmlzZShDb3VudCA9IHN1bShDb3VudCkpCiMgLSBqb2luIGltcHJlc3Npb25zIGFuZCBjbGlja3M6ICAKZGF0YVNldCA8LSBsZWZ0X2pvaW4oaW1wcmVzc2lvbnMsIGNsaWNrcywgCiAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiQmFubmVyIiA9ICJ1cmlfcXVlcnkiLCAiQ2FtcGFpZ25EYXkiKSkKZGF0YVNldCRgQ2xpY2tzL0ltcHJlc3Npb25zYCA8LSByb3VuZChkYXRhU2V0JENvdW50LnkvZGF0YVNldCRDb3VudC54LCA1KQojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfQpnZ3Bsb3QoZGF0YVNldCwgYWVzKHggPSBDYW1wYWlnbkRheSwKICAgICAgICAgICAgICAgICAgICB5ID0gYENsaWNrcy9JbXByZXNzaW9uc2AsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBCYW5uZXIsCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBCYW5uZXIsCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9IEJhbm5lciwKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGBDbGlja3MvSW1wcmVzc2lvbnNgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ1N1bW1lciBCYW5uZXIgQ2FtcGFpZ24gMjAxODogQmFubmVyIENsaWNrcy9JbXByZXNzaW9ucycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKKipUYWJsZSAxLiAzLiBUaGUgQ2xpY2tzL0ltcHJlc3Npb25zIHJhdGlvIHBlciBiYW5uZXIqKgoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQojIyMgLS0tIEZ1bGwgRGF0YXNldCAoVGFibGUgUmVwb3J0KQpkYXRhdGFibGUoZGF0YVNldFssIGMoMSwgMywgNSldKQpgYGAKCioqVGFibGUgMS4gMy4gMSBUb3RhbCBDbGlja3MvSW1wcmVzc2lvbnMgcmF0aW8gcGVyIGJhbm5lcioqCgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CiMjIyAtLS0gRnVsbCBEYXRhc2V0IChUYWJsZSBSZXBvcnQpCnRvdGFsSW1wQ2xpY2tzIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChCYW5uZXIsIENvdW50LngsIENvdW50LnkpICU+JSAKICBncm91cF9ieShCYW5uZXIpICU+JSAKICBzdW1tYXJpc2UoSW1wcmVzc2lvbnMgPSBzdW0oQ291bnQueCksIENsaWNrcyA9IHN1bShDb3VudC55KSkKdG90YWxJbXBDbGlja3MkYENsaWNrcy9JbXByZXNzaW9uc2AgPC0gcm91bmQodG90YWxJbXBDbGlja3MkQ2xpY2tzL3RvdGFsSW1wQ2xpY2tzJEltcHJlc3Npb25zLCA1KQpkYXRhdGFibGUodG90YWxJbXBDbGlja3MpCmBgYAoKIyMjIDEuNCBVc2VyIFJlZ2lzdHJhdGlvbnMKCioqQ2hhcnQgMS4gNC4gRGFpbHkgdXNlciByZWdpc3RyYXRpb25zIHBlciBiYW5uZXIqKgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmRhdGFTZXQgPC0gcmVhZC5kZWxpbSgnL2hvbWUvZ29yYW5zbS9Xb3JrL19fX0RhdGFLb2xla3Rpdi9Qcm9qZWN0cy9XaWtpbWVkaWFERVUvX1dNREVfUHJvamVjdHMvX21pc2MvTmV3RWRpdG9yc19UZWFtLzIwMThfU3VtbWVyQmFubmVyQ2FtcGFpZ24vX2RhdGEvc3VtbWVyQkMyMDE4X3VzZXJSZWdpc3RyYXRpb25zX0ZVTEwudHN2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICAgICAgICAgIHF1b3RlID0gIiIsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmRhdGFTZXQgPC0gZGF0YVNldFtncmVwbCgnaXNfYm90IjogZmFsc2UnLCBkYXRhU2V0JHVzZXJBZ2VudCwgZml4ZWQgPSBUKSwgXQpkYXRhU2V0JHRpbWVzdGFtcCA8LSBhcy5jaGFyYWN0ZXIoZGF0YVNldCR0aW1lc3RhbXApCmRhdGFTZXQkdGltZXN0YW1wIDwtIGFzLlBPU0lYY3QoZGF0YVNldCR0aW1lc3RhbXAsIHR6ID0gIlVUQyIsIGZvcm1hdCA9ICIlWSVtJWQlSCVNJVMiKSAKIyAtIENFU1QgY29ycmVjdGlvbiBvbiBQT1NJWGN0IGNsYXNzLCArMmhvdXJzOgpkYXRhU2V0JHRpbWVzdGFtcCA8LSBkYXRhU2V0JHRpbWVzdGFtcCArIDIqNjAqNjAKIyAtIENhbXBhaWduIGRheToKZGF0YVNldCRDYW1wYWlnbkRheSA8LSBzYXBwbHkoZGF0YVNldCR0aW1lc3RhbXAsIGZ1bmN0aW9uKHgpIHsKICBwYXN0ZShjKAogICAgc3Vic3RyKHgsIDEsNCksCiAgICBzdWJzdHIoeCwgNiw3KSwKICAgIHN1YnN0cih4LCA5LDEwKQogICAgKSwKICAgIGNvbGxhcHNlID0gIi0iLAogICAgc2VwID0gIiIpCn0pCmRhdGFTZXQgPC0gZmlsdGVyKGRhdGFTZXQsCiAgICAgICAgICAgICAgICAgIENhbXBhaWduRGF5ICVpbiUgYygiMjAxOC0wOC0wMSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIwMTgtMDgtMDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIwMTgtMDgtMDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIwMTgtMDgtMDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIwMTgtMDgtMDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIwMTgtMDgtMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIwMTgtMDgtMDciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIwMTgtMDgtMDgiKSkKdXNlclJlZyA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZXZlbnRfY2FtcGFpZ24sIENhbXBhaWduRGF5KSAlPiUgCiAgZ3JvdXBfYnkoQ2FtcGFpZ25EYXksIGV2ZW50X2NhbXBhaWduKSAlPiUgCiAgc3VtbWFyaXNlKENvdW50ID0gbigpKQpjb2xuYW1lcyh1c2VyUmVnKVsyXSA8LSAiQmFubmVyIgoKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn0KZ2dwbG90KHVzZXJSZWcsIGFlcyh4ID0gQ2FtcGFpZ25EYXksCiAgICAgICAgICAgICAgICAgICAgeSA9IENvdW50LAogICAgICAgICAgICAgICAgICAgIGdyb3VwID0gQmFubmVyLAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gQmFubmVyLAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSBCYW5uZXIsCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBDb3VudCkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdTdW1tZXIgQmFubmVyIENhbXBhaWduIDIwMTg6IFVzZXIgUmVnaXN0cmF0aW9ucycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKKipUYWJsZSAxLiA0LiBEYWlseSB1c2VyIHJlZ2lzdHJhdGlvbnMgcGVyIGJhbm5lcioqCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KIyMjIC0tLSBGdWxsIERhdGFzZXQgKFRhYmxlIFJlcG9ydCkKZGF0YXRhYmxlKHVzZXJSZWcpCmBgYAoKKipUYWJsZSAxLiA0LiAxIEJhbm5lciBDb252ZXJzaW9uIFJhdGVzKioKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CiMjIyAtLS0gQmFubmVyIENvbnZlcnNpb24gUmF0ZXMKdG90YWxSZWdzIDwtIHVzZXJSZWdbLCAyOjNdICU+JSAKICBzZWxlY3QoQmFubmVyLCBDb3VudCkgJT4lCiAgZ3JvdXBfYnkoQmFubmVyKSAlPiUKICBzdW1tYXJpc2UodG90YWxSZWcgPSBzdW0oQ291bnQpKQp0b3RhbFJlZ3MkQmFubmVyW2dyZXBsKCJXTURFX25ld2VkaXRvcnNfc3VtbWVyMjAxOF8xIiwgdG90YWxSZWdzJEJhbm5lcildIDwtICdCMThXTURFX2F1dGhvcnNfMScKdG90YWxSZWdzJEJhbm5lcltncmVwbCgiV01ERV9uZXdlZGl0b3JzX3N1bW1lcjIwMThfMiIsIHRvdGFsUmVncyRCYW5uZXIpXSA8LSAnQjE4V01ERV9hdXRob3JzXzInCnRvdGFsUmVncyRCYW5uZXJbZ3JlcGwoIldNREVfbmV3ZWRpdG9yc19zdW1tZXIyMDE4XzMiLCB0b3RhbFJlZ3MkQmFubmVyKV0gPC0gJ0IxOFdNREVfYXV0aG9yc18zJwp0b3RhbENsaWNrcyR1cmlfcXVlcnlbZ3JlcGwoIldNREVfbmV3ZWRpdG9yc19zdW1tZXIyMDE4XzEiLCB0b3RhbENsaWNrcyR1cmlfcXVlcnkpXSA8LSAnQjE4V01ERV9hdXRob3JzXzEnCnRvdGFsQ2xpY2tzJHVyaV9xdWVyeVtncmVwbCgiV01ERV9uZXdlZGl0b3JzX3N1bW1lcjIwMThfMiIsIHRvdGFsQ2xpY2tzJHVyaV9xdWVyeSldIDwtICdCMThXTURFX2F1dGhvcnNfMicKdG90YWxDbGlja3MkdXJpX3F1ZXJ5W2dyZXBsKCJXTURFX25ld2VkaXRvcnNfc3VtbWVyMjAxOF8zIiwgdG90YWxDbGlja3MkdXJpX3F1ZXJ5KV0gPC0gJ0IxOFdNREVfYXV0aG9yc18zJwp0b3RhbENsaWNrcyA8LSB0b3RhbENsaWNrcyAlPiUgCiAgZ3JvdXBfYnkodXJpX3F1ZXJ5KSAlPiUgCiAgc3VtbWFyaXNlKHRvdGFsQ2xpY2tzID0gc3VtKHRvdGFsQ2xpY2tzKSkKdG90YWxSZWdzIDwtIGxlZnRfam9pbih0b3RhbFJlZ3MsIHRvdGFsQ2xpY2tzLAogICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiQmFubmVyIiA9ICJ1cmlfcXVlcnkiKSkKdG90YWxSZWdzJGBSZWdpc3RyYXRpb25zL0NsaWNrc2AgPC0gcm91bmQodG90YWxSZWdzJHRvdGFsUmVnL3RvdGFsUmVncyR0b3RhbENsaWNrcywgNSkKZGF0YXRhYmxlKHRvdGFsUmVncykKYGBgCgojIyMgMS41IFVzZXIgRWRpdHMKCioqQ2hhcnQgMS4gNS4gRWRpdHMgb2YgcmVnaXN0ZXJlZCB1c2VycyBwZXIgYmFubmVyIGFuZCBjYW1wYWlnbiBkYXkgb2YgcmVnaXN0cmF0aW9uKioKCioqTk9URToqKiB0aGUgQ2FtcGFpZ24gRGF5IGhlcmUgcmVmZXJzIHRvIHRoZSBkYXkgd2hlbiB0aGUgdXNlciBoYXMgcmVnaXN0ZXJlZCwgbm90IHRoZSBkYXkgaW4gd2hpY2ggdGhlIHJlc3BlY3RpdmUgZWRpdHMgd2VyZSBtYWRlLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmRhdGFTZXQyIDwtIHJlYWQuZGVsaW0oJy9ob21lL2dvcmFuc20vV29yay9fX19EYXRhS29sZWt0aXYvUHJvamVjdHMvV2lraW1lZGlhREVVL19XTURFX1Byb2plY3RzL19taXNjL05ld0VkaXRvcnNfVGVhbS8yMDE4X1N1bW1lckJhbm5lckNhbXBhaWduL19kYXRhL3NCQzIwMThfdXNlckVkaXRzX0ZVTEwudHN2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICAgICAgICAgIHF1b3RlID0gIiIsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmRhdGFTZXQgPC0gbGVmdF9qb2luKGRhdGFTZXQsIGRhdGFTZXQyLCBieSA9IGMoImV2ZW50X3VzZXJJZCIgPSAicmV2X3VzZXIiKSkKZGF0YVNldCRlZGl0c1tpcy5uYShkYXRhU2V0JGVkaXRzKV0gPC0gMAoKdXNlckVkcyA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZXZlbnRfY2FtcGFpZ24sIENhbXBhaWduRGF5LCBlZGl0cykgJT4lIAogIGdyb3VwX2J5KENhbXBhaWduRGF5LCBldmVudF9jYW1wYWlnbikgJT4lIAogIHN1bW1hcmlzZShFZGl0cyA9IHN1bShlZGl0cykpCmNvbG5hbWVzKHVzZXJFZHMpWzJdIDwtICJCYW5uZXIiCgojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfQpnZ3Bsb3QodXNlckVkcywgYWVzKHggPSBDYW1wYWlnbkRheSwKICAgICAgICAgICAgICAgICAgICB5ID0gRWRpdHMsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBCYW5uZXIsCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBCYW5uZXIsCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9IEJhbm5lciwKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IEVkaXRzKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ1N1bW1lciBCYW5uZXIgQ2FtcGFpZ24gMjAxODogVXNlciBFZGl0cycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKKipUYWJsZSAxLiA1LiBFZGl0cyBvZiByZWdpc3RlcmVkIHVzZXJzIHBlciBiYW5uZXIgYW5kIGNhbXBhaWduIGRheSBvZiByZWdpc3RyYXRpb24qKgoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQojIyMgLS0tIEZ1bGwgRGF0YXNldCAoVGFibGUgUmVwb3J0KQpkYXRhdGFibGUodXNlckVkcykKYGBgCgoKKipUYWJsZSAxLiA1LiAxIEVkaXRzIG9mIHJlZ2lzdGVyZWQgdXNlcnMgcGVyIGJhbm5lciBhbmQgY2FtcGFpZ24gZGF5IG9mIHJlZ2lzdHJhdGlvbioqCgpUb3RhbCBudW1iZXIgb2YgdXNlciBlZGl0cyBwZXIgYmFubmVyOgoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQp0b3RhbEVkaXRzIDwtIHVzZXJFZHMgJT4lIAogIGdyb3VwX2J5KEJhbm5lcikgJT4lIAogIHN1bW1hcmlzZShFZGl0cyA9IHN1bShFZGl0cykpCmRhdGF0YWJsZSh0b3RhbEVkaXRzKQpgYGAKCioqVGFibGUgMS4gNS4gMiBVc2VycyByZWFjaGluZyB0aGVpciAxMHRoIGVkaXQgcGVyIGJhbm5lcioqCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQplZGl0MTAgPC0gZGF0YVNldCAlPiUgCiAgZmlsdGVyKGVkaXRzID49IDEwKSAlPiUgCiAgc2VsZWN0KGV2ZW50X2NhbXBhaWduKSAlPiUgCiAgZ3JvdXBfYnkoZXZlbnRfY2FtcGFpZ24pICU+JQogIHN1bW1hcmlzZShgZWRpdDEwdGhgID0gbigpKQpkYXRhdGFibGUoZWRpdDEwKQpgYGAKCioqVGFibGUgMS4gNS4gMyBGdWxsIHVzZXIgZWRpdHMgZGlzdHJpYnV0aW9uKioKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CiMjIyAtLS0gRnVsbCBEYXRhc2V0IChUYWJsZSBSZXBvcnQpCnBsdEVkaXRzIDwtIGFzLnRibChkYXRhU2V0KSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGVkaXRzKSAlPiUgCiAgY291bnQoKQpjb2xuYW1lcyhwbHRFZGl0cykgPC0gYygnRWRpdHMnLCAnTnVtLnVzZXJzJykKa25pdHI6OmthYmxlKHBsdEVkaXRzLCBmb3JtYXQgPSAiaHRtbCIpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYsIHBvc2l0aW9uID0gImxlZnQiKQpgYGAKCgoqKlRhYmxlIDEuIDUuIDQgVXNlcnMgZWRpdCBjYXRlZ29yaWVzKioKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CmVkaXRzMCA8LSBwbHRFZGl0cyRgTnVtLnVzZXJzYFtwbHRFZGl0cyRFZGl0cyA9PSAwXQplZGl0cyA8LSBzdW0ocGx0RWRpdHMkYE51bS51c2Vyc2BbcGx0RWRpdHMkRWRpdHMgPiAwXSkKZWRpdHMxIDwtIHN1bShwbHRFZGl0cyRgTnVtLnVzZXJzYFtwbHRFZGl0cyRFZGl0cyA9PSAxXSkKZWRpdHMyXzQgPC0gc3VtKHBsdEVkaXRzJGBOdW0udXNlcnNgW3BsdEVkaXRzJEVkaXRzID49IDIgJiBwbHRFZGl0cyRFZGl0cyA8PSA0XSkKZWRpdHM1XzEwIDwtIHN1bShwbHRFZGl0cyRgTnVtLnVzZXJzYFtwbHRFZGl0cyRFZGl0cyA+PSA1ICYgcGx0RWRpdHMkRWRpdHMgPD0gMTBdKQplZGl0czEwIDwtIHN1bShwbHRFZGl0cyRgTnVtLnVzZXJzYFtwbHRFZGl0cyRFZGl0cyA+IDEwXSkKZWRpdENsYXNzZXMgPC0gZGF0YS5mcmFtZShgTm8gZWRpdHNgID0gZWRpdHMwLAogICAgICAgICAgICAgICAgICAgICAgICAgIGBFZGl0ZWRgID0gZWRpdHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYDEgZWRpdGAgPSBlZGl0czEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYDIgLSA0IGVkaXRzYCA9IGVkaXRzMl80LAogICAgICAgICAgICAgICAgICAgICAgICAgIGA1IC0gMTAgZWRpdHNgID0gZWRpdHM1XzEwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBgPiAxMCBlZGl0c2AgPSBlZGl0czEwLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKa25pdHI6OmthYmxlKGVkaXRDbGFzc2VzLCBmb3JtYXQgPSAiaHRtbCIpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYsIHBvc2l0aW9uID0gImxlZnQiKQpgYGAKCiMjIDIgVHJhaW5pbmcgTW9kdWxlcwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KdHJhaW5Nb2QgPC0gcmVhZC5jc3YoJy9ob21lL2dvcmFuc20vV29yay9fX19EYXRhS29sZWt0aXYvUHJvamVjdHMvV2lraW1lZGlhREVVL19XTURFX1Byb2plY3RzL19taXNjL05ld0VkaXRvcnNfVGVhbS8yMDE4X1N1bW1lckJhbm5lckNhbXBhaWduL19kYXRhL3dtZGVfdHJhaW5pbmdfZGF0YV8yMDE4LTA4LmNzdicsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiMgLSByZW1vdmUgdXNlcnMgU3RlZmFuIFNjaG5laWRlciAoV01ERSkgYW5kIFNhZ2UgKFdpa2kgRWQpCnRyYWluTW9kIDwtIHRyYWluTW9kICU+JSAKICBmaWx0ZXIoISh1c2VybmFtZSAlaW4lIGMoJ1N0ZWZhbiBTY2huZWlkZXIgKFdNREUpJywgJ1NhZ2UgKFdpa2kgRWQpJykpKQojIC0gZGF0YSB0eXBlcwp0cmFpbk1vZCRtb2R1bGVfY29tcGxldGlvbl9kYXRlIDwtIGFzLmNoYXJhY3Rlcih0cmFpbk1vZCRtb2R1bGVfY29tcGxldGlvbl9kYXRlKQp0cmFpbk1vZCRtb2R1bGVfY29tcGxldGlvbl9kYXRlIDwtIGFzLlBPU0lYY3QodHJhaW5Nb2QkbW9kdWxlX2NvbXBsZXRpb25fZGF0ZSwgdHogPSAiVVRDIiwgZm9ybWF0ID0gIiVZLSVtLSVkICVIOiVNOiVTIikgCiMgLSBDRVNUIGNvcnJlY3Rpb24gb24gUE9TSVhjdCBjbGFzcywgKzJob3VyczoKdHJhaW5Nb2QkbW9kdWxlX2NvbXBsZXRpb25fZGF0ZSA8LSB0cmFpbk1vZCRtb2R1bGVfY29tcGxldGlvbl9kYXRlICsgMio2MCo2MAoKdHJhaW5Nb2Qkc3RhcnRlZF9hdCA8LSBhcy5jaGFyYWN0ZXIodHJhaW5Nb2Qkc3RhcnRlZF9hdCkKdHJhaW5Nb2Qkc3RhcnRlZF9hdCA8LSBhcy5QT1NJWGN0KHRyYWluTW9kJHN0YXJ0ZWRfYXQsIHR6ID0gIlVUQyIsIGZvcm1hdCA9ICIlWS0lbS0lZCAlSDolTTolUyIpIAojIC0gQ0VTVCBjb3JyZWN0aW9uIG9uIFBPU0lYY3QgY2xhc3MsICsyaG91cnM6CnRyYWluTW9kJHN0YXJ0ZWRfYXQgPC0gdHJhaW5Nb2Qkc3RhcnRlZF9hdCArIDIqNjAqNjAKCnRyYWluTW9kJGxhc3Rfc2xpZGVfY29tcGxldGVkX2F0IDwtIGFzLmNoYXJhY3Rlcih0cmFpbk1vZCRsYXN0X3NsaWRlX2NvbXBsZXRlZF9hdCkKdHJhaW5Nb2QkbGFzdF9zbGlkZV9jb21wbGV0ZWRfYXQgPC0gYXMuUE9TSVhjdCh0cmFpbk1vZCRsYXN0X3NsaWRlX2NvbXBsZXRlZF9hdCwgdHogPSAiVVRDIiwgZm9ybWF0ID0gIiVZLSVtLSVkICVIOiVNOiVTIikgCiMgLSBDRVNUIGNvcnJlY3Rpb24gb24gUE9TSVhjdCBjbGFzcywgKzJob3VyczoKdHJhaW5Nb2QkbGFzdF9zbGlkZV9jb21wbGV0ZWRfYXQgPC0gdHJhaW5Nb2QkbGFzdF9zbGlkZV9jb21wbGV0ZWRfYXQgKyAyKjYwKjYwCgojIC0gZmlsdGVyIGZvciBjYW1wYWlnbiBkYXlzIG9ubHkKdHJhaW5Nb2QgPC0gZmlsdGVyKHRyYWluTW9kLCAKICAgICAgICAgICAgICAgICAgIHN0YXJ0ZWRfYXQgPj0gIjIwMTgtMDgtMDEgMDA6MDA6MDAiICYgc3RhcnRlZF9hdCA8ICIyMDE4LTA4LTA5IDAwOjAwOjAwIikKCiMgLSBmaWx0ZXIgY2FtcGFpZ24gdHJhaW5pbmcgbW9kdWxlcyBvbmx5CiMgdHJhaW5Nb2QgPC0gZmlsdGVyKHRyYWluTW9kLAogICAgICAgICAgICAgICAgICAgIyB0cmFpbmluZ19tb2R1bGUgJWluJSBjKCdkaXNrdXRpZXJlbi1iYXNpc3dpc3NlbicsICdlZGl0aWVyZW4tYmFzaXN3aXNzZW4nKSkKCgojIC0gbGVmdF9qb2luIHcuIGRhdGFTZXQgKHVzZXIgcmVnaXN0cmF0aW9ucyArIHVzZXIgZWRpdHMpCiMgLSB0byBwcm9kdWNlIHRyYWluTW9kIHRoYXQgaGFzIHVzZXIgcmVnaXN0cmF0aW9ucyArIGVkaXRzCnRyYWluTW9kIDwtIGxlZnRfam9pbih0cmFpbk1vZCwgZGF0YVNldCwKICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygidXNlcm5hbWUiID0gImV2ZW50X3VzZXJOYW1lIikpCiMgLSBmaWx0ZXIgdGhvc2Ugd2hvIGRpZCBub3QgcmVnaXN0ZXIgd2l0aCB0aGUgY2FtcGFpZ246CnRyYWluTW9kIDwtIGZpbHRlcih0cmFpbk1vZCwgIWlzLm5hKENhbXBhaWduRGF5KSkKCiMgLSB0cmFpbk1vZCBhZ2dyZWdhdGVzIDE6IG51bWJlciBvZiB0cmFpbmluZyBtb2R1bGVzIHN0YXJ0ZWQgYW5kIGNvbXBsZXRlZCBwZXIgcmVnaXN0ZXJlZCB1c2VyCm1vZHVsZXNTdGFydGVkIDwtIGFzLmRhdGEuZnJhbWUodGFibGUodHJhaW5Nb2QkdXNlcm5hbWUpKQpjb2xuYW1lcyhtb2R1bGVzU3RhcnRlZCkgPC0gYygndXNlcm5hbWUnLCAnbW9kdWxlc1N0YXJ0ZWQnKQptb2R1bGVzQ29tcGxldGVkIDwtIGZpbHRlcih0cmFpbk1vZCwgIWlzLm5hKG1vZHVsZV9jb21wbGV0aW9uX2RhdGUpKQptb2R1bGVzQ29tcGxldGVkIDwtIGFzLmRhdGEuZnJhbWUodGFibGUobW9kdWxlc0NvbXBsZXRlZCR1c2VybmFtZSkpCmNvbG5hbWVzKG1vZHVsZXNDb21wbGV0ZWQpIDwtIGMoJ3VzZXJuYW1lJywgJ21vZHVsZXNDb21wbGV0ZWQnKQptb2R1bGVzRGF0YSA8LSBsZWZ0X2pvaW4obW9kdWxlc1N0YXJ0ZWQsIG1vZHVsZXNDb21wbGV0ZWQsIGJ5ID0gJ3VzZXJuYW1lJykKbW9kdWxlc0RhdGEkbW9kdWxlc0NvbXBsZXRlZFtpcy5uYShtb2R1bGVzRGF0YSRtb2R1bGVzQ29tcGxldGVkKV0gPC0gMAoKIyAtIGxlZnRfam9pbiBkYXRhU2V0IHcuIG1vZHVsZXNEYXRhCmRhdGFTZXQgPC0gbGVmdF9qb2luKGRhdGFTZXQsIG1vZHVsZXNEYXRhLCAKICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJldmVudF91c2VyTmFtZSIgPSAidXNlcm5hbWUiKSkKZGF0YVNldCRtb2R1bGVzU3RhcnRlZFtpcy5uYShkYXRhU2V0JG1vZHVsZXNTdGFydGVkKV0gPC0gMApkYXRhU2V0JG1vZHVsZXNDb21wbGV0ZWRbaXMubmEoZGF0YVNldCRtb2R1bGVzQ29tcGxldGVkKV0gPC0gMAoKIyAtIHByb2R1Y2UgYWdncmVnYXRlcyBmb3IgcmVwb3J0aW5nIG9uIHRyYWluaW5nIG1vZHVsZXMgZnJvbSBkYXRhU2V0CiMgLSBhZ2cxOiB0TV9zdGFydGVkCnRNX3N0YXJ0ZWQgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KG1vZHVsZXNTdGFydGVkLCBlZGl0cykgJT4lIAogIGdyb3VwX2J5KG1vZHVsZXNTdGFydGVkKSAlPiUgCiAgc3VtbWFyaXNlKHN1bUVkaXRzID0gc3VtKGVkaXRzKSwgdXNlcnMgPSBuKCkpCnRNX3N0YXJ0ZWQkZWRpdHNQZXJVc2VyIDwtIHJvdW5kKHRNX3N0YXJ0ZWQkc3VtRWRpdHMvdE1fc3RhcnRlZCR1c2VycywgMikKCiMgLSBhZ2cxOiB0TV9jb21wbGV0ZWQKdE1fY29tcGxldGVkIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChtb2R1bGVzQ29tcGxldGVkLCBlZGl0cykgJT4lIAogIGdyb3VwX2J5KG1vZHVsZXNDb21wbGV0ZWQpICU+JSAKICBzdW1tYXJpc2Uoc3VtRWRpdHMgPSBzdW0oZWRpdHMpLCB1c2VycyA9IG4oKSkKdE1fY29tcGxldGVkJGVkaXRzUGVyVXNlciA8LSByb3VuZCh0TV9jb21wbGV0ZWQkc3VtRWRpdHMvdE1fY29tcGxldGVkJHVzZXJzLCAyKQoKIyAtIGFnZzM6IHRtX3N0YXJ0ZWRfcGVyTW9kdWxlCnRtX3N0YXJ0ZWRfcGVyTW9kdWxlIDwtIHRyYWluTW9kICU+JSAKICBzZWxlY3QodHJhaW5pbmdfbW9kdWxlKSAlPiUgCiAgZ3JvdXBfYnkodHJhaW5pbmdfbW9kdWxlKSAlPiUgCiAgc3VtbWFyaXNlKENvdW50ID0gbigpKQoKIyAtIGFnZzQ6IHRtX2NvbXBsZXRlZF9wZXJNb2R1bGUKdG1fY29tcGxldGVkX3Blck1vZHVsZSA8LSB0cmFpbk1vZCAlPiUKICBmaWx0ZXIoIWlzLm5hKG1vZHVsZV9jb21wbGV0aW9uX2RhdGUpKSAlPiUKICBzZWxlY3QodHJhaW5pbmdfbW9kdWxlKSAlPiUKICBncm91cF9ieSh0cmFpbmluZ19tb2R1bGUpICU+JSAKICBzdW1tYXJpc2UoQ291bnQgPSBuKCkpCgojIC0gYWdnNSgxLTIpOiBleGl0IHNsaWRlcyBwZXIgbW9kdWxlCmV4aXRTbGlkZXMxIDwtIGRhdGEuZnJhbWUodCh0YWJsZSgKICB0cmFpbk1vZCR0cmFpbmluZ19tb2R1bGVbdHJhaW5Nb2QkdHJhaW5pbmdfbW9kdWxlICVpbiUgImRpc2t1dGllcmVuLWJhc2lzd2lzc2VuIl0sIAogIHRyYWluTW9kJGxhc3Rfc2xpZGVfY29tcGxldGVkW3RyYWluTW9kJHRyYWluaW5nX21vZHVsZSAlaW4lICJkaXNrdXRpZXJlbi1iYXNpc3dpc3NlbiJdKSkpCmNvbG5hbWVzKGV4aXRTbGlkZXMxKSA8LSBjKCdTbGlkZScsICdNb2R1bGUnLCAnQ291bnQnKQpleGl0U2xpZGVzMiA8LSBkYXRhLmZyYW1lKHQodGFibGUoCiAgdHJhaW5Nb2QkdHJhaW5pbmdfbW9kdWxlW3RyYWluTW9kJHRyYWluaW5nX21vZHVsZSAlaW4lICJlZGl0aWVyZW4tYmFzaXN3aXNzZW4iXSwgCiAgdHJhaW5Nb2QkbGFzdF9zbGlkZV9jb21wbGV0ZWRbdHJhaW5Nb2QkdHJhaW5pbmdfbW9kdWxlICVpbiUgImVkaXRpZXJlbi1iYXNpc3dpc3NlbiJdKSkpCmNvbG5hbWVzKGV4aXRTbGlkZXMyKSA8LSBjKCdTbGlkZScsICdNb2R1bGUnLCAnQ291bnQnKQoKIyAtIGFnZzYoMS0yKTogdGltZSBzcGVudCBpbiB0cmFpbmluZwp0cmFpbk1vZCRDb21wbGV0ZWQgPC0gIWlzLm5hKHRyYWluTW9kJG1vZHVsZV9jb21wbGV0aW9uX2RhdGUpCnRpbWVJblRyYWluaW5nIDwtIHRyYWluTW9kICU+JSAKICBzZWxlY3QoQ29tcGxldGVkLCB0cmFpbmluZ19tb2R1bGUsIHN0YXJ0ZWRfYXQsIGxhc3Rfc2xpZGVfY29tcGxldGVkX2F0KSAlPiUgCiAgbXV0YXRlKHRpbWVTcGVudCA9IGFzLm51bWVyaWMoKGxhc3Rfc2xpZGVfY29tcGxldGVkX2F0IC0gc3RhcnRlZF9hdCkvNjApKSAlPiUKICBzZWxlY3QoQ29tcGxldGVkLCB0cmFpbmluZ19tb2R1bGUsIHRpbWVTcGVudCkgJT4lCiAgZ3JvdXBfYnkodHJhaW5pbmdfbW9kdWxlLCBDb21wbGV0ZWQpICU+JQogIHN1bW1hcmlzZShgVGltZSAobWlucylgID0gbWVhbihyb3VuZCh0aW1lU3BlbnQsIDIpKSkKCiMgLSBhZ2c3OiBkaXN0cmlidXRpb24gb2YgdGltZSBzcGVudCBpbiB0cmFpbmluZwp0aW1lSW5UcmFpbmluZ0Rpc3QgPC0gdHJhaW5Nb2QgJT4lCiAgc2VsZWN0KENvbXBsZXRlZCwgdHJhaW5pbmdfbW9kdWxlLCBzdGFydGVkX2F0LCBsYXN0X3NsaWRlX2NvbXBsZXRlZF9hdCkgJT4lCiAgbXV0YXRlKHRpbWVTcGVudCA9IGFzLm51bWVyaWMoKGxhc3Rfc2xpZGVfY29tcGxldGVkX2F0IC0gc3RhcnRlZF9hdCkvNjApKQpgYGAKCgoqKlRhYmxlIDIuIDEgTnVtYmVyIG9mIHVzZXJzIHdobyBoYXZlIHN0YXJ0ZWQgYSBwYXJ0aWN1bGFyIG51bWJlciBvZiB0cmFpbmluZyBtb2R1bGVzIChtb2R1bGVzU3RhcnRlZCksIHRoZSB0b3RhbCBudW1iZXIgb2YgZWRpdHMgdGhhdCB0aGV5IGhhdmUgbWFkZSAoc3VtRWRpdHMpLCBudW1iZXIgb2YgdXNlcnMgd2hvIHRvb2sgYSBwYXJ0aWN1bGFyIG51bWJlciBvZiB0cmFpbmluZyBtb2R1bGVzICh1c2VycyksIGFuZCB0aGUgZWRpdHMgcGVyIHVzZXIgcmF0aW8uICoqCgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CmRhdGF0YWJsZSh0TV9zdGFydGVkKQpgYGAKCioqVGFibGUgMi4gMiBOdW1iZXIgb2YgdXNlcnMgd2hvIGhhdmUgY29tcGxldGVkIGEgcGFydGljdWxhciBudW1iZXIgb2YgdHJhaW5pbmcgbW9kdWxlcyAobW9kdWxlc1N0YXJ0ZWQpLCB0aGUgdG90YWwgbnVtYmVyIG9mIGVkaXRzIHRoYXQgdGhleSBoYXZlIG1hZGUgKHN1bUVkaXRzKSwgbnVtYmVyIG9mIHVzZXJzIHdobyB0b29rIGEgcGFydGljdWxhciBudW1iZXIgb2YgdHJhaW5pbmcgbW9kdWxlcyAodXNlcnMpLCBhbmQgdGhlIGVkaXRzIHBlciB1c2VyIHJhdGlvLiAqKgoKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KZGF0YXRhYmxlKHRNX2NvbXBsZXRlZCkKYGBgCgoqKlRhYmxlIDIuIDMgTnVtYmVyIG9mIHVzZXJzIHdobyBoYXZlIHN0YXJ0ZWQgYSBwYXJ0aWN1bGFyIHRyYWluaW5nIG1vZHVsZS4gKioKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KZGF0YXRhYmxlKHRtX3N0YXJ0ZWRfcGVyTW9kdWxlKQpgYGAKCioqVGFibGUgMi4gNCBOdW1iZXIgb2YgdXNlcnMgd2hvIGhhdmUgY29tcGxldGVkIGEgcGFydGljdWxhciB0cmFpbmluZyBtb2R1bGUuICoqCgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CmRhdGF0YWJsZSh0bV9jb21wbGV0ZWRfcGVyTW9kdWxlKQpgYGAKCioqVGFibGUgMi4gNWEgRXhpdCBzbGlkZXMgcGVyIHRyYWluaW5nIG1vZHVsZSBhbmQgdGhlIG51bWJlciBvZiB1c2VycyB3aG8gaGF2ZSBleGl0ZWQgYXQgYSBwYXJ0aWN1bGFyIHNsaWRlLiAqKgoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQpkYXRhdGFibGUoZXhpdFNsaWRlczEpCmBgYAoqKlRhYmxlIDIuIDViIEV4aXQgc2xpZGVzIHBlciB0cmFpbmluZyBtb2R1bGUgYW5kIHRoZSBudW1iZXIgb2YgdXNlcnMgd2hvIGhhdmUgZXhpdGVkIGF0IGEgcGFydGljdWxhciBzbGlkZS4gKioKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KZGF0YXRhYmxlKGV4aXRTbGlkZXMyKQpgYGAKKipUYWJsZSAyLiA2IEF2ZXJhZ2UgVGltZSBzcGVudCBpbiB0cmFpbmluZyBwZXIgdHJhaW5pbmcgbW9kdWxlLiAqKgoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQpkYXRhdGFibGUodGltZUluVHJhaW5pbmcpCmBgYAoqKkNoYXJ0IDIuIDcgVGhlIGRpc3RyaWJ1dGlvbiBvZiB0aW1lIHNwZW50IGluIHRyYWluaW5nIHBlciB0cmFpbmluZyBtb2R1bGUuICoqCgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CmdncGxvdChkYXRhID0gdGltZUluVHJhaW5pbmdEaXN0LCAKICAgICAgIGFlcyh4ID0gQ29tcGxldGVkLCB5ID0gdGltZVNwZW50LCBjb2xvciA9IENvbXBsZXRlZCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faml0dGVyKCkgKwogIGZhY2V0X3dyYXAofnRyYWluaW5nX21vZHVsZSkgKyAKICB0aGVtZV9idygpCmBgYAoK