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

The campaign was run from 2020/05/14 to 2020/06/30.

CURRENT UPDATE: Complete dataset as of 2020/06/30.

0. Data Acquisiton

NOTE: the Data Acquisition code chunk is not fully reproducible from this Report. The data are collected by running the script 2020_EmailCampaignWikipediaChallenge_PRODUCTION.R on stat1004.eqiad.wmnet, collecting the data as .tsv and .csv files, copying manually, and processing locally. A daily crontab job was run from 2020/05/14 to 2020/06/30 to collect the data for daily reporting. The data used in this report are aggregates of the daily datasets, sanitized and anonymized.

0.1 Daily Update

### --- WMDE 2020_EmailCampaignWikipediaChallenge_PRODUCTION.R
### --- Campaign start: 2020/05/14
### --- Campaign end: 2020/06/30
### --- run from: stat1004
### --- path: /home/goransm/Analytics/NewEditors/Campaigns/2020_EmailCampaignWikipediaChallenge/

### --- libraries
library(tidyverse)
library(data.table)
library(lubridate)

### --- dir structure
campaignPath <- '/home/goransm/Analytics/NewEditors/Campaigns/2020_EmailCampaignWikipediaChallenge/'
dataDir <- paste0(campaignPath, "_data/")
analyticsDir <- paste0(campaignPath, "_analytics/")
### --- campaign specifics
campaignName <- 'Email_Campaign_Wikipedia_Challenge2020'

### --- determine cetDay
cetDay <- Sys.time()
cetDay
attr(cetDay, "tzone") <- "Europe/Berlin"
# - one day behind for crontab
# - (i.e. waiting for wmf.webrequest to complete is data acquisition)
cetDay <- ymd(
  strsplit(as.character(cetDay), 
           split = " ", 
           fixed = T)[[1]][1]
) - 1

cetDay <- "2020-06-30"

### ----------------------------------------------------------
### --- Pageviews
### ----------------------------------------------------------

# - function: wmde_collect_pageviews
wmde_collect_pageviews <- function(uri_host,
                                   uri_path,
                                   cetDay,
                                   queryFile,
                                   fileName,
                                   dataDir) {
  
  # - NOTE:
  # - expected format for cetDay is: YYYY-MM-DD
  
  # - to dataDir
  setwd(dataDir)
  
  # - libraries
  library(stringr)
  
  # - WHERE condition: create datetime_condition
  cet_condition <- seq(
    from = as.POSIXct(paste0(cetDay," 0:00"), tz = "Europe/Berlin"),
    to = as.POSIXct(paste0(cetDay," 23:00"), tz = "Europe/Berlin"),
    by = "hour"
  ) 
  attr(cet_condition, "tzone") <- "UTC"
  cet_condition <- as.character(cet_condition)
  cet_condition <- unlist(str_extract_all(cet_condition, "^([[:digit:]]|\\s|-)*"))
  cet_years <- sapply(
    strsplit(cet_condition, split = " ", fixed = T), function(x) {
      strsplit(x, split = "-")[[1]][1]
    })
  cet_months <- sapply(
    strsplit(cet_condition, split = " ", fixed = T), function(x) {
      strsplit(x, split = "-")[[1]][2]
    })
  cet_months <- gsub("^0", "", cet_months)
  cet_days <- sapply(
    strsplit(cet_condition, split = " ", fixed = T), function(x) {
      strsplit(x, split = "-")[[1]][3]
    })
  cet_days <- gsub("^0", "", cet_days)
  cet_hours <- sapply(strsplit(cet_condition, split = " ", fixed = T), 
                      function(x) {
                        x[2]
                      })
  cet_hours <- gsub("^0", "", cet_hours)
  datetimeCondition <- paste0(
    "year = ", cet_years, " AND ",
    "month = ", cet_months, " AND ",
    "day = ", cet_days, " AND ", 
    "hour = ", cet_hours
  )
  datetimeCondition <- paste("(", 
                             datetimeCondition, 
                             ")",
                             collapse = " OR ", 
                             sep = "")
  
  # - WHERE condition: create uri_host_condition
  if (length(uri_host) > 1) {
    uri_host_condition <- paste0("(",
                                  paste(
                                    paste0("uri_host = '", uri_host, "'"),
                                    collapse = " OR ", sep = " "),
                                  ")"
    )
  } else {
    uri_host_condition = paste0("uri_host = '", uri_host, "'")
  }
  
  # - WHERE condition: create uri_path_condition
  if (length(uri_path) > 1) {
    uri_path_condition <- paste0("(",
                                 paste(
                                   paste0("uri_path = '", uri_path, "'"),
                                   collapse = " OR ", sep = " "),
                                 ")"
    )
  } else {
    uri_path_condition = paste0("uri_path = '", uri_path, "'")
  }
  
  # - compose HiveQL query
  hiveQuery <- paste0( 
    "USE wmf;
    SELECT uri_host, uri_path, uri_query, referer FROM webrequest
    WHERE (",
    uri_host_condition, " AND ",
    uri_path_condition, " AND ",
    "(", datetimeCondition, ")",
    ");"
    )
  
  # - write hql
  write(hiveQuery, queryFile)
  # - execute hql script:
  kerberosPrefix <- 
    'sudo -u analytics-privatedata kerberos-run-command analytics-privatedata '
  # - Kerberos init
  system(command = paste0(kerberosPrefix, ' hdfs dfs -ls'), 
         wait = T)
  # - Run query
  query <- system(command = paste(kerberosPrefix, 
                                  '/usr/local/bin/beeline --incremental=true --silent -f "',
                                  paste0(dataDir, queryFile),
                                  '" > ', dataDir, fileName,
                                  sep = ""),
                  wait = TRUE)
}

# - set params to wmde_collect_pageviews
# - for the 2020_EmailCampaignWikipediaChallenge
uri_host <- c('de.wikipedia.org', 'de.m.wikipedia.org',
              'wikidata.org', 'm.wikidata.org',
              'commons.wikimedia.org', 'commons.m.wikimedia.org',
              'de.wikivoyage.org', 'de.m.wikivoyage.org',
              'de.wiktionary.org', 'de.m.wiktionary.org')
uri_path  <- c(
  '/wiki/Langer_Grund-Kohlberg?tour=einfuhrung',
  '/wiki/Spezial:Benutzerkonto_anlegen',
  '/wiki/Wikidata:Introduction/de',
  '/wiki/Spezial:Anmelden',
  '/wiki/Special:MyPage/Artikelwerkstatt',
  '/wiki/Benutzer:Trainingskonto_(WMDE)/Wikipedia_Training',
  '/wiki/Wikipedia:Wikimedia_Deutschland/DeinEngagement/Literatur',
  '/w/index.php?title=Q16943273&tour=wbitems&data=ok',
  '/w/index.php?title=Special:UploadWizard&uselang=de',
  '/wiki/Hauptseite',
  '/wiki/Wiktionary:Hauptseite',
  '/wiki/Commons:Willkommen')
# uri_query <- paste0('WMDE_2020_challenge_', 1:30)
queryFile <- 'emailFollowUp2019_Pageviews.hql'
fileName <- paste0("pageviews_", cetDay, ".tsv")

# - collect Pageviews data
wmde_collect_pageviews(uri_host,
                       uri_path,
                       cetDay,
                       queryFile,
                       fileName,
                       dataDir)

### --- Wrangle Pageviews
# - function: wmde_process_pageviews
wmde_process_pageviews <- function(fileName,
                                   dataDir, 
                                   uri_query_filter,
                                   page_filter,
                                   cetDay = cetDay,
                                   campaignName = campaignName) {
  
  # - to dataDir
  setwd(dataDir)
  
  # - libraries
  library(stringr)
  library(dplyr)
  library(tidyr)
  library(data.table)
  
  # - load
  pageviewsData <- readLines(fileName)
  wStart <- which(grepl("uri_host", pageviewsData))
  pageviewsData <- pageviewsData[(wStart + 2):(length(pageviewsData) - 2)]
  pageviewsData <- data.frame(dat = pageviewsData, 
                              stringsAsFactors = F)
  pageviewsData <- separate(pageviewsData,
                            dat,
                            into = c('uri_host', 'uri_path', 'uri_query', 'referer'),
                            sep = "\t")
  # - apply page_filter
  pageviewsData$page <- paste0(pageviewsData$uri_host, 
                               pageviewsData$uri_path,
                               pageviewsData$uri_query)
  wFilter <- sapply(pageviewsData$page, function(x) {
    sapply(page_filter, function(y) {
      grepl(y, x)
    })
  })
  wFilter <- colSums(wFilter)
  wFilter <- which(wFilter > 0)
  pageviewsData <- pageviewsData[wFilter, ]
  # - apply uri_query_filter
  # - NOTE: looking in both: uri_query, referer 
  w_uri_query <- which(grepl(uri_query_filter, pageviewsData$page))
  
  if (length(w_uri_query) > 0) {
    
    # - filter for w_uri_query
    pageviewsData <- pageviewsData[w_uri_query, ] 
    
    # - aggregate:
    pageviewsData$uri_path <- paste0(pageviewsData$uri_host, pageviewsData$uri_path)
    pageviewsData$uri_host <- NULL
    pageviewsData$page <- NULL
    pageviewsData$referer <- NULL
    pageviewsData <- pageviewsData %>% 
      dplyr::select(uri_query, uri_path) %>% 
      dplyr::group_by(uri_query, uri_path) %>% 
      dplyr::summarise(pageviews = n())
    colnames(pageviewsData) <- c('Tag', 'Page', 'Pageviews')
    
    # - add cetDay, campaignName
    pageviewsData$date <- cetDay
    pageviewsData$campaign <- campaignName
    
    # - store:
    write.csv(pageviewsData, 
              paste0(analyticsDir, 
                     "pageviewsAggregated_",
                     strsplit(
                       strsplit(fileName, split = "_", fixed = T)[[1]][2],
                       split = ".", 
                       fixed = T)[[1]][1],
                     ".csv"
              )
    )
    
  }
  
}

# - set params to wmde_process_pageviews
# - for the WMDE 2020_EmailCampaignWikipediaChallenge
uri_query_filter <- 'WMDE_2020_challenge_'
page_filter <- c('de.wikipedia.org/wiki/Langer_Grund-Kohlberg?tour=einfuhrung', 
                 'de.wikipedia.org/wiki/Spezial:Benutzerkonto_anlegen', 
                 'wikidata.org/wiki/Wikidata:Introduction/de', 
                 'de.wikipedia.org/wiki/Spezial:Anmelden', 
                 'de.wikipedia.org/wiki/Spezial:Anmelden', 
                 'de.wikipedia.org/wiki/Special:MyPage/Artikelwerkstatt', 
                 'de.wikipedia.org/wiki/Benutzer:Trainingskonto_(WMDE)/Wikipedia_Training', 
                 'de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/DeinEngagement/Literatur', 
                 'www.wikidata.org/w/index.php?title=Q16943273&tour=wbitems&data=ok', 
                 'commons.wikimedia.org/w/index.php?title=Special:UploadWizard&uselang=de', 
                 'commons.wikimedia.org/wiki/Commons:Willkommen',
                 'de.wikipedia.org/wiki/Benutzer:Trainingskonto_(WMDE)/Wikipedia_Training', 
                 'de.wikivoyage.org/wiki/Hauptseite', 
                 'de.wiktionary.org/wiki/Wiktionary:Hauptseite', 
                 'de.m.wikipedia.org/wiki/Langer_Grund-Kohlberg?tour=einfuhrung', 
                 'de.m.wikipedia.org/wiki/Spezial:Benutzerkonto_anlegen', 
                 'm.wikidata.org/wiki/Wikidata:Introduction/de', 
                 'de.m.wikipedia.org/wiki/Spezial:Anmelden', 
                 'de.m.wikipedia.org/wiki/Spezial:Anmelden', 
                 'de.m.wikipedia.org/wiki/Special:MyPage/Artikelwerkstatt', 
                 'de.m.wikipedia.org/wiki/Benutzer:Trainingskonto_(WMDE)/Wikipedia_Training', 
                 'de.m.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/DeinEngagement/Literatur', 
                 'www.wikidata.org/w/index.php?title=Q16943273&tour=wbitems&data=ok', 
                 'commons.m.wikimedia.org/w/index.php?title=Special:UploadWizard&uselang=de',
                 'commons.m.wikimedia.org/wiki/Commons:Willkommen',
                 'de.m.wikipedia.org/wiki/Benutzer:Trainingskonto_(WMDE)/Wikipedia_Training', 
                 'de.m.wikivoyage.org/wiki/Hauptseite', 
                 'de.m.wiktionary.org/wiki/Wiktionary:Hauptseite')

# - wrangle pageviews
wmde_process_pageviews(fileName = fileName,
                       dataDir = dataDir,
                       uri_query_filter = uri_query_filter, 
                       page_filter = page_filter,
                       cetDay = cetDay,
                       campaignName = campaignName)

### ----------------------------------------------------------
### --- User Registrations
### --- HiveQL query: event.ServerSideAccountCreation table
### ----------------------------------------------------------
hiveQL <-
  "SELECT year, month, day, hour,  event.campaign, event.userId, event.userName, webhost, wiki
      FROM event.serversideaccountcreation
      WHERE
        year = 2020
        AND (month = 5 OR month = 6)
        AND (event.campaign LIKE '%WMDE_2020_challenge_%');"
# - write hql
write(hiveQL, paste0(dataDir, 'user_registrations.hql'))
### --- output filename
filename <- paste0(dataDir, 'user_registrations.tsv')
### --- execute hql script:
hiveArgs <- 'sudo -u analytics-privatedata kerberos-run-command analytics-privatedata /usr/local/bin/beeline --silent --incremental=true --verbose=false -f'
hiveInput <- paste(paste0(dataDir, 'user_registrations.hql'),
                   " > ",
                   filename,
                   sep = "")
# - command:
hiveCommand <- paste(hiveArgs, hiveInput)
system(command = hiveCommand, wait = TRUE)

### ----------------------------------------------------------
### --- User Edits
### --- User Edits via revision_actor_temp
### ----------------------------------------------------------

startTimestamp <- '20200514000000'
# - get user ids
userRegistrations <- fread(paste0(dataDir,
                                  'user_registrations.tsv'))
dim(userRegistrations)
# - clean up from test accounts
userRegistrations <- filter(userRegistrations, 
                            !grepl("Test E-Mail Campaign 2020", 
                                   userRegistrations$username))
dim(userRegistrations)
head(userRegistrations)
rev_user <- userRegistrations$userid
# - iterate over rev_user
for (i in 1:length(rev_user)) {
  # - SQL query
  sqlQuery <- paste("\"SELECT actor.actor_id, 
                        actor.actor_user, 
                        actor.actor_name, 
                        revision_actor_temp.revactor_timestamp 
                    FROM actor 
                    LEFT JOIN revision_actor_temp ON (actor.actor_id = revision_actor_temp.revactor_actor) 
                    WHERE (revision_actor_temp.revactor_timestamp >= 20200511000000 
                      AND actor.actor_user = ", rev_user[i], ");\"");
  ### --- output filename
  filename <- paste(dataDir,'userEdits', "_", i, ".tsv", sep = "")
  ### --- execute sql script:
  sqlLogInPre <- paste0('/usr/local/bin/analytics-mysql dewiki -e ')
  sqlInput <- paste(sqlQuery,
                    " > ",
                    filename,
                    sep = "")
  # - command:
  sqlCommand <- paste(sqlLogInPre, sqlInput)
  system(command = sqlCommand, wait = TRUE)
  # - report
  print(paste0("DONE: user ", i, "."))
}
### --- END run SQL scripts
# - load user edits:
lF <- list.files(dataDir)
lF <- lF[grepl("^userEdits_", lF)]
userEdits <- lapply(paste0(dataDir, lF), fread)
userEdits <- rbindlist(userEdits)
# - store user edits:
write.csv(userEdits, paste0(analyticsDir, 'userEdits.csv'))

1. Campaign Pageviews

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

The following chunk loads and then re-structures the dataset a bit.

lF <- list.files('_analytics')
lF <- lF[grepl("^pageviewsAggregated", lF)]
dataSet <- lapply(paste0("_analytics/", lF), fread)
dataSet <- rbindlist(dataSet)
dataSet$V1 <- NULL
dataSet$campaign <- NULL
# - expand grid to account for missing observations per day
dS <- expand.grid(unique(dataSet$Tag), 
                  unique(dataSet$Page), 
                  unique(dataSet$date), 
                  stringsAsFactors = F)
colnames(dS) <- c('Tag', 'Page', 'date')
dS <- dS %>% 
  left_join(dataSet, 
            by = c("Tag", "Page", "date"))
dataSet <- dS; rm(dS)
dataSet$Pageviews[is.na(dataSet$Pageviews)] <- 0
dataSet$Tag <- gsub("&.+$", "", dataSet$Tag)
dataSet$Tag <- gsub("\\?campaign=", "", dataSet$Tag)
dataSet$date <- sapply(dataSet$date, function(x) {
  d <- strsplit(x, split = "-")[[1]]
  if (nchar(d[3]) == 1) {
    d[3] <- paste0("0", d[3])
  }
  return(paste(d, collapse = "-"))
})

1.1 Pageviews Overview

Chart 1.1.1 Daily Pageviews, aggregated across the campaign channels.

pFrame <- dataSet %>% 
  select(date, Page, Pageviews) %>% 
  group_by(date, Page) %>% 
  summarise(Pageviews = sum(Pageviews))
pFrame <- arrange(pFrame, date)
ggplot(pFrame, aes(x = date,
                   y = Pageviews,
                   label = Pageviews,
                    )) + 
  geom_path(size = .5, group = 1) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  scale_y_continuous(labels = comma) +
  facet_wrap(~Page, ncol = 2) + 
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Date") + ylab("Pageviews") + 
  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 = 6)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "right")

Table 1.1.1 Pageviews totals

tFrame <- pFrame %>% 
  select(Page, Pageviews) %>% 
  group_by(Page) %>% 
  summarise(totalPageviews = sum(Pageviews)) %>% 
  arrange(desc(totalPageviews))
datatable(tFrame)

1.2 Pageviews: Campaign Channels

Chart 1.2.1 Pageviews, by channels

pFrame <- dataSet %>% 
  select(date, Tag, Pageviews) %>% 
  group_by(date, Tag) %>% 
  summarise(Pageviews = sum(Pageviews))
pFrame <- arrange(pFrame, date)
ggplot(pFrame, aes(x = date,
                   y = Pageviews,
                   label = Pageviews,
                    )) + 
  geom_path(size = .5, group = 1) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  scale_y_continuous(labels = comma) +
  facet_wrap(~Tag, ncol = 2) + 
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Date") + ylab("Pageviews") + 
  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 = 6)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "right")

Chart 1.2.2 Total Pageviews, by channels

pFrame <- pFrame %>% 
  select(Tag, Pageviews) %>% 
  group_by(Tag) %>% 
  summarise(Pageviews = sum(Pageviews)) %>% 
  arrange(desc(Pageviews))
pFrame$Tag <- factor(pFrame$Tag,
                     levels = pFrame$Tag,
                     ordered = T)
ggplot(pFrame, aes(x = Tag,
                   y = Pageviews,
                   label = Pageviews,
                    )) + 
  geom_bar(stat = "identity", color = "blue", fill = "white") + 
  scale_y_continuous(labels = comma) +
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Date") + ylab("Pageviews") + 
  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()) + 
  theme(legend.position = "right")

Table 2.2.1 Total pageviews, by channels and by pages.

tFrame <- dataSet %>% 
  select(Tag, Page, Pageviews) %>% 
  group_by(Tag, Page) %>% 
  summarise(totalPageviews = sum(Pageviews)) %>% 
  arrange(desc(totalPageviews))
datatable(tFrame)

2. User Registrations

This section presents all data and statistics on the user registrations.

2.1 User Registrations Overview

lF <- list.files("_analytics")
lF <- lF[grepl("^user_registrations", lF)]
dataSet <- fread(paste0("_analytics/", lF))
dataSet <- filter(dataSet, 
                  !grepl("^Test E-Mail Campaign", dataSet$username))
dataSet$date <- paste(dataSet$year, 
                      ifelse(nchar(dataSet$month) == 1, paste0("0", dataSet$month), dataSet$month),
                      ifelse(nchar(dataSet$day) == 1, paste0("0", dataSet$day), dataSet$day),
                      sep = "-")
regUsers <- select(dataSet,
                   userid,
                   username, 
                   campaign)
# - expand grid to account for missing observations per day
dateSpan <- seq(from = as.Date("2020-05-14"), 
                to = as.Date("2020-06-30"), 
                by = "day")
dateSpan <- as.character(dateSpan)
dS <- expand.grid(dateSpan, 
                  unique(dataSet$campaign), 
                  stringsAsFactors = F)
colnames(dS) <- c('date', 'campaign')
dS <- dS %>% 
  left_join(select(dataSet, date, campaign, userid),
            by = c("date", "campaign"))
dS$userid <- ifelse(is.na(dS$userid), 0, 1)
pFrame <- dS %>% 
  select(date, userid) %>% 
  group_by(date) %>% 
  summarise(registrations = sum(userid)) %>% 
  arrange(date)
ggplot(pFrame, aes(x = date,
                   y = registrations,
                   label = registrations,
                    )) + 
  geom_path(size = .25, group = 1) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  scale_y_continuous(labels = comma) +
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Date") + ylab("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()) + 
  theme(legend.position = "right")

2.2 User Registrations per Campaign Channel

pFrame <- dS %>% 
  group_by(date, campaign) %>% 
  summarise(registrations = sum(userid)) %>% 
  arrange(date)
ggplot(pFrame, aes(x = date,
                   y = registrations,
                   label = registrations,
                    )) + 
  geom_path(size = .25, group = 1) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  facet_wrap(~campaign, ncol = 2) + 
  scale_y_continuous(labels = comma) +
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Date") + ylab("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 = 6)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "right")

2.3 Total User Registrations per Campaign Channel

pFrame <- dS %>% 
  select(campaign, userid) %>% 
  group_by( campaign) %>% 
  summarise(registrations = sum(userid)) %>% 
  arrange(desc(registrations))
pFrame$campaign <- factor(pFrame$campaign, 
                          levels = pFrame$campaign, 
                          ordered = T)
ggplot(pFrame, aes(x = campaign,
                   y = registrations,
                   label = registrations,
                    )) + 
  geom_bar(stat = "identity", color = "blue", fill = "white") + 
  scale_y_continuous(labels = comma) +
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Date") + ylab("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()) + 
  theme(legend.position = "right")

3. User Edits

This section presents all data and statistics on the user edits.

3.1 User Edits Overview

The following chunk loads the dataset of user interactions with campaign channels and then re-structures the dataset a bit.

lF <- list.files("_analytics")
lF <- lF[grepl("^userEdits", lF)]
editSet <- fread(paste0("_analytics/", lF))
editSet <- select(editSet,
                  actor_name, 
                  revactor_timestamp)                 
colnames(editSet) <- c('username', 'rev_timestamp')
dataSet <- left_join(editSet,
                     select(regUsers, 
                            username,
                            campaign),
                     by = "username")
dataSet$year <- substr(dataSet$rev_timestamp, 1, 4)
dataSet$month <- substr(dataSet$rev_timestamp, 5, 6)
dataSet$day <- substr(dataSet$rev_timestamp, 7, 8)
dataSet$rev_timestamp <- NULL
dataSet$date <- paste(dataSet$year,
                       dataSet$month,
                       dataSet$day,
                       sep = "-")
dataSet <- select(dataSet,
                  date,
                  username,
                  campaign)
dateSpan <- seq(from = as.Date("2020-05-14"), 
                to = as.Date(max(dataSet$date)), 
                by = "day")
dateSpan <- as.character(dateSpan)
dS <- expand.grid(dateSpan, 
                  unique(dataSet$campaign),
                  stringsAsFactors = F)
colnames(dS) <- c('date', 'campaign')
dS <- dS %>% 
  left_join(select(dataSet, date, campaign, username),
            by = c("date", "campaign"))
dS$username <- ifelse(is.na(dS$username), 0, 1)
pFrame <- dS %>%
  select(date, username) %>% 
  group_by(date) %>% 
  summarise(edits = sum(username)) %>% 
  arrange(date)
ggplot(pFrame, aes(x = date,
                   y = edits,
                   label = edits,
                    )) + 
  geom_path(size = .25, group = 1) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  scale_y_continuous(labels = comma) +
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Date") + ylab("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()) + 
  theme(legend.position = "right")

3.2 User Edits by Campaign Channels

pFrame <- dS %>% 
  group_by(date, campaign) %>% 
  summarise(edits = sum(username)) %>% 
  arrange(date)
ggplot(pFrame, aes(x = date,
                   y = edits,
                   label = edits,
                    )) + 
  geom_path(size = .25, group = 1) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  facet_wrap(~campaign, ncol = 2) + 
  scale_y_continuous(labels = comma) +
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Date") + ylab("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 = 5.5)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "right")

3.3 Total User Edits by Campaign Channels

pFrame <- dS %>% 
  select(campaign, username) %>% 
  group_by( campaign) %>% 
  summarise(edits = sum(username)) %>% 
  arrange(desc(edits))
pFrame$campaign <- factor(pFrame$campaign, 
                          levels = pFrame$campaign, 
                          ordered = T)
ggplot(pFrame, aes(x = campaign,
                   y = edits,
                   label = edits,
                    )) + 
  geom_bar(stat = "identity", color = "blue", fill = "white") + 
  scale_y_continuous(labels = comma) +
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Date") + ylab("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()) + 
  theme(legend.position = "right")

3.4 Edit Classes

3.4.1 Edit Classes: all users

userClass <- dataSet %>% 
  select(username) %>% 
  group_by(username) %>% 
  summarise(edits = n())
editBoundaries <- list(
  c(0, 1), 
  c(2, 4),
  c(5, 9),
  c(10, 20),
  c(21, 50),
  c(51, 100)
)
userClass$editClass <- sapply(userClass$edits, function(x) {
  wEC <- sapply(editBoundaries, function(y) {
    x >= y[1] & x <= y[2]
  })
  if (sum(wEC) == 0) {
    return("> 100")
  } else {
    return(paste0("(",
                  editBoundaries[[which(wEC)]][1],
                  " - ",
                  editBoundaries[[which(wEC)]][2], 
                  ")"
                  )
    )
  }
})
editClass <- as.data.frame(table(userClass$editClass), 
                           stringsAsFactors = F)
colnames(editClass) <- c('Edit Class', 'Num.Users')
editClass$order <- as.numeric(sapply(editClass$`Edit Class`, function(x) {
  lower <- str_extract(x, '[[:digit:]]+')
}))
editClass <- arrange(editClass, order)
editClass$order <- NULL
datatable(editClass)

3.4.2A Edit Classes per Campaign Channel: WMDE_2020_challenge_1

userClass <- dataSet %>% 
  filter(campaign == "WMDE_2020_challenge_1") %>% 
  select(username) %>% 
  group_by(username) %>% 
  summarise(edits = n())
editBoundaries <- list(
  c(0, 1), 
  c(2, 4),
  c(5, 9),
  c(10, 20),
  c(21, 50),
  c(51, 100)
)
userClass$editClass <- sapply(userClass$edits, function(x) {
  wEC <- sapply(editBoundaries, function(y) {
    x >= y[1] & x <= y[2]
  })
  if (sum(wEC) == 0) {
    return("> 100")
  } else {
    return(paste0("(",
                  editBoundaries[[which(wEC)]][1],
                  " - ",
                  editBoundaries[[which(wEC)]][2], 
                  ")"
                  )
    )
  }
})
editClass <- as.data.frame(table(userClass$editClass), 
                           stringsAsFactors = F)
colnames(editClass) <- c('Edit Class', 'Num.Users')
editClass$order <- as.numeric(sapply(editClass$`Edit Class`, function(x) {
  lower <- str_extract(x, '[[:digit:]]+')
}))
editClass <- arrange(editClass, order)
editClass$order <- NULL
datatable(editClass)

3.4.2B Edit Classes per Campaign Channel: WMDE_2020_challenge_4

userClass <- dataSet %>% 
  filter(campaign == "WMDE_2020_challenge_4") %>% 
  select(username) %>% 
  group_by(username) %>% 
  summarise(edits = n())
editBoundaries <- list(
  c(0, 1), 
  c(2, 4),
  c(5, 9),
  c(10, 20),
  c(21, 50),
  c(51, 100)
)
userClass$editClass <- sapply(userClass$edits, function(x) {
  wEC <- sapply(editBoundaries, function(y) {
    x >= y[1] & x <= y[2]
  })
  if (sum(wEC) == 0) {
    return("> 100")
  } else {
    return(paste0("(",
                  editBoundaries[[which(wEC)]][1],
                  " - ",
                  editBoundaries[[which(wEC)]][2], 
                  ")"
                  )
    )
  }
})
editClass <- as.data.frame(table(userClass$editClass), 
                           stringsAsFactors = F)
colnames(editClass) <- c('Edit Class', 'Num.Users')
editClass$order <- as.numeric(sapply(editClass$`Edit Class`, function(x) {
  lower <- str_extract(x, '[[:digit:]]+')
}))
editClass <- arrange(editClass, order)
editClass$order <- NULL
datatable(editClass)

3.4.2C Edit Classes per Campaign Channel: WMDE_2020_challenge_5

userClass <- dataSet %>% 
  filter(campaign == "WMDE_2020_challenge_5") %>% 
  select(username) %>% 
  group_by(username) %>% 
  summarise(edits = n())
editBoundaries <- list(
  c(0, 1), 
  c(2, 4),
  c(5, 9),
  c(10, 20),
  c(21, 50),
  c(51, 100)
)
userClass$editClass <- sapply(userClass$edits, function(x) {
  wEC <- sapply(editBoundaries, function(y) {
    x >= y[1] & x <= y[2]
  })
  if (sum(wEC) == 0) {
    return("> 100")
  } else {
    return(paste0("(",
                  editBoundaries[[which(wEC)]][1],
                  " - ",
                  editBoundaries[[which(wEC)]][2], 
                  ")"
                  )
    )
  }
})
editClass <- as.data.frame(table(userClass$editClass), 
                           stringsAsFactors = F)
colnames(editClass) <- c('Edit Class', 'Num.Users')
editClass$order <- as.numeric(sapply(editClass$`Edit Class`, function(x) {
  lower <- str_extract(x, '[[:digit:]]+')
}))
editClass <- arrange(editClass, order)
editClass$order <- NULL
datatable(editClass)
LS0tCnRpdGxlOiAnMjAyMCBXTURFIEVtYWlsIENhbXBhaWduIFdpa2lwZWRpYSBDaGFsbGVuZ2UgMjAyMCcKYXV0aG9yOiAiR29yYW4gUy4gTWlsb3Zhbm92aWMsIERhdGEgU2NpZW50aXN0LCBXTURFIgpkYXRlOiAiSnVseSA3LCAyMDIwIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdGhlbWU6IHNpbXBsZXgKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2RlcHRoOiA1CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDUKLS0tCgoqKkZlZWRiYWNrKiogc2hvdWxkIGJlIHNlbmQgdG8gYGdvcmFuLm1pbG92YW5vdmljX2V4dEB3aWtpbWVkaWEuZGVgLiAKClRoZSBjYW1wYWlnbiB3YXMgcnVuIGZyb20gMjAyMC8wNS8xNCB0byAyMDIwLzA2LzMwLgoKKipDVVJSRU5UIFVQREFURToqKiBDb21wbGV0ZSBkYXRhc2V0IGFzIG9mIDIwMjAvMDYvMzAuCgpgYGB7ciwgZWNobyA9IEYsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGLCByZXN1bHRzID0gJ2hpZGUnfQojICFkaWFnbm9zdGljcyBvZmYKIyMjIC0tLSBTZXR1cApsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkocm1hcmtkb3duKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKYGBgCgojIyAwLiBEYXRhIEFjcXVpc2l0b24KCioqTk9URToqKiB0aGUgRGF0YSBBY3F1aXNpdGlvbiBjb2RlIGNodW5rIGlzIG5vdCBmdWxseSByZXByb2R1Y2libGUgZnJvbSB0aGlzIFJlcG9ydC4gVGhlIGRhdGEgYXJlIGNvbGxlY3RlZCBieSBydW5uaW5nIHRoZSBzY3JpcHQgYDIwMjBfRW1haWxDYW1wYWlnbldpa2lwZWRpYUNoYWxsZW5nZV9QUk9EVUNUSU9OLlJgIG9uIHN0YXQxMDA0LmVxaWFkLndtbmV0LCBjb2xsZWN0aW5nIHRoZSBkYXRhIGFzIGAudHN2YCBhbmQgYC5jc3ZgIGZpbGVzLCBjb3B5aW5nIG1hbnVhbGx5LCBhbmQgcHJvY2Vzc2luZyBsb2NhbGx5LiBBIGRhaWx5IGNyb250YWIgam9iIHdhcyBydW4gZnJvbSBgMjAyMC8wNS8xNGAgdG8gYDIwMjAvMDYvMzBgIHRvIGNvbGxlY3QgdGhlIGRhdGEgZm9yIGRhaWx5IHJlcG9ydGluZy4gVGhlIGRhdGEgdXNlZCBpbiB0aGlzIHJlcG9ydCBhcmUgYWdncmVnYXRlcyBvZiB0aGUgZGFpbHkgZGF0YXNldHMsIHNhbml0aXplZCBhbmQgYW5vbnltaXplZC4gICAKCiMjIyAwLjEgRGFpbHkgVXBkYXRlCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGfQoKIyMjIC0tLSBXTURFIDIwMjBfRW1haWxDYW1wYWlnbldpa2lwZWRpYUNoYWxsZW5nZV9QUk9EVUNUSU9OLlIKIyMjIC0tLSBDYW1wYWlnbiBzdGFydDogMjAyMC8wNS8xNAojIyMgLS0tIENhbXBhaWduIGVuZDogMjAyMC8wNi8zMAojIyMgLS0tIHJ1biBmcm9tOiBzdGF0MTAwNAojIyMgLS0tIHBhdGg6IC9ob21lL2dvcmFuc20vQW5hbHl0aWNzL05ld0VkaXRvcnMvQ2FtcGFpZ25zLzIwMjBfRW1haWxDYW1wYWlnbldpa2lwZWRpYUNoYWxsZW5nZS8KCiMjIyAtLS0gbGlicmFyaWVzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkobHVicmlkYXRlKQoKIyMjIC0tLSBkaXIgc3RydWN0dXJlCmNhbXBhaWduUGF0aCA8LSAnL2hvbWUvZ29yYW5zbS9BbmFseXRpY3MvTmV3RWRpdG9ycy9DYW1wYWlnbnMvMjAyMF9FbWFpbENhbXBhaWduV2lraXBlZGlhQ2hhbGxlbmdlLycKZGF0YURpciA8LSBwYXN0ZTAoY2FtcGFpZ25QYXRoLCAiX2RhdGEvIikKYW5hbHl0aWNzRGlyIDwtIHBhc3RlMChjYW1wYWlnblBhdGgsICJfYW5hbHl0aWNzLyIpCiMjIyAtLS0gY2FtcGFpZ24gc3BlY2lmaWNzCmNhbXBhaWduTmFtZSA8LSAnRW1haWxfQ2FtcGFpZ25fV2lraXBlZGlhX0NoYWxsZW5nZTIwMjAnCgojIyMgLS0tIGRldGVybWluZSBjZXREYXkKY2V0RGF5IDwtIFN5cy50aW1lKCkKY2V0RGF5CmF0dHIoY2V0RGF5LCAidHpvbmUiKSA8LSAiRXVyb3BlL0JlcmxpbiIKIyAtIG9uZSBkYXkgYmVoaW5kIGZvciBjcm9udGFiCiMgLSAoaS5lLiB3YWl0aW5nIGZvciB3bWYud2VicmVxdWVzdCB0byBjb21wbGV0ZSBpcyBkYXRhIGFjcXVpc2l0aW9uKQpjZXREYXkgPC0geW1kKAogIHN0cnNwbGl0KGFzLmNoYXJhY3RlcihjZXREYXkpLCAKICAgICAgICAgICBzcGxpdCA9ICIgIiwgCiAgICAgICAgICAgZml4ZWQgPSBUKVtbMV1dWzFdCikgLSAxCgpjZXREYXkgPC0gIjIwMjAtMDYtMzAiCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIFBhZ2V2aWV3cwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIGZ1bmN0aW9uOiB3bWRlX2NvbGxlY3RfcGFnZXZpZXdzCndtZGVfY29sbGVjdF9wYWdldmlld3MgPC0gZnVuY3Rpb24odXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyKSB7CiAgCiAgIyAtIE5PVEU6CiAgIyAtIGV4cGVjdGVkIGZvcm1hdCBmb3IgY2V0RGF5IGlzOiBZWVlZLU1NLURECiAgCiAgIyAtIHRvIGRhdGFEaXIKICBzZXR3ZChkYXRhRGlyKQogIAogICMgLSBsaWJyYXJpZXMKICBsaWJyYXJ5KHN0cmluZ3IpCiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIGRhdGV0aW1lX2NvbmRpdGlvbgogIGNldF9jb25kaXRpb24gPC0gc2VxKAogICAgZnJvbSA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDA6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgdG8gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAyMzowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICBieSA9ICJob3VyIgogICkgCiAgYXR0cihjZXRfY29uZGl0aW9uLCAidHpvbmUiKSA8LSAiVVRDIgogIGNldF9jb25kaXRpb24gPC0gYXMuY2hhcmFjdGVyKGNldF9jb25kaXRpb24pCiAgY2V0X2NvbmRpdGlvbiA8LSB1bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKGNldF9jb25kaXRpb24sICJeKFtbOmRpZ2l0Ol1dfFxcc3wtKSoiKSkKICBjZXRfeWVhcnMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMV0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsyXQogICAgfSkKICBjZXRfbW9udGhzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9tb250aHMpCiAgY2V0X2RheXMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bM10KICAgIH0pCiAgY2V0X2RheXMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2RheXMpCiAgY2V0X2hvdXJzIDwtIHNhcHBseShzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgCiAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHhbMl0KICAgICAgICAgICAgICAgICAgICAgIH0pCiAgY2V0X2hvdXJzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9ob3VycykKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZTAoCiAgICAieWVhciA9ICIsIGNldF95ZWFycywgIiBBTkQgIiwKICAgICJtb250aCA9ICIsIGNldF9tb250aHMsICIgQU5EICIsCiAgICAiZGF5ID0gIiwgY2V0X2RheXMsICIgQU5EICIsIAogICAgImhvdXIgPSAiLCBjZXRfaG91cnMKICApCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUoIigiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZUNvbmRpdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX2hvc3RfY29uZGl0aW9uCiAgaWYgKGxlbmd0aCh1cmlfaG9zdCkgPiAxKSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9ob3N0ID0gJyIsIHVyaV9ob3N0LCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9ob3N0ID0gJyIsIHVyaV9ob3N0LCAiJyIpCiAgfQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSB1cmlfcGF0aF9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9wYXRoKSA+IDEpIHsKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX3BhdGhfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfcGF0aCA9ICciLCB1cmlfcGF0aCwgIiciKQogIH0KICAKICAjIC0gY29tcG9zZSBIaXZlUUwgcXVlcnkKICBoaXZlUXVlcnkgPC0gcGFzdGUwKCAKICAgICJVU0Ugd21mOwogICAgU0VMRUNUIHVyaV9ob3N0LCB1cmlfcGF0aCwgdXJpX3F1ZXJ5LCByZWZlcmVyIEZST00gd2VicmVxdWVzdAogICAgV0hFUkUgKCIsCiAgICB1cmlfaG9zdF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcGF0aF9jb25kaXRpb24sICIgQU5EICIsCiAgICAiKCIsIGRhdGV0aW1lQ29uZGl0aW9uLCAiKSIsCiAgICAiKTsiCiAgICApCiAgCiAgIyAtIHdyaXRlIGhxbAogIHdyaXRlKGhpdmVRdWVyeSwgcXVlcnlGaWxlKQogICMgLSBleGVjdXRlIGhxbCBzY3JpcHQ6CiAga2VyYmVyb3NQcmVmaXggPC0gCiAgICAnc3VkbyAtdSBhbmFseXRpY3MtcHJpdmF0ZWRhdGEga2VyYmVyb3MtcnVuLWNvbW1hbmQgYW5hbHl0aWNzLXByaXZhdGVkYXRhICcKICAjIC0gS2VyYmVyb3MgaW5pdAogIHN5c3RlbShjb21tYW5kID0gcGFzdGUwKGtlcmJlcm9zUHJlZml4LCAnIGhkZnMgZGZzIC1scycpLCAKICAgICAgICAgd2FpdCA9IFQpCiAgIyAtIFJ1biBxdWVyeQogIHF1ZXJ5IDwtIHN5c3RlbShjb21tYW5kID0gcGFzdGUoa2VyYmVyb3NQcmVmaXgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJy91c3IvbG9jYWwvYmluL2JlZWxpbmUgLS1pbmNyZW1lbnRhbD10cnVlIC0tc2lsZW50IC1mICInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKGRhdGFEaXIsIHF1ZXJ5RmlsZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnIiA+ICcsIGRhdGFEaXIsIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpLAogICAgICAgICAgICAgICAgICB3YWl0ID0gVFJVRSkKfQoKIyAtIHNldCBwYXJhbXMgdG8gd21kZV9jb2xsZWN0X3BhZ2V2aWV3cwojIC0gZm9yIHRoZSAyMDIwX0VtYWlsQ2FtcGFpZ25XaWtpcGVkaWFDaGFsbGVuZ2UKdXJpX2hvc3QgPC0gYygnZGUud2lraXBlZGlhLm9yZycsICdkZS5tLndpa2lwZWRpYS5vcmcnLAogICAgICAgICAgICAgICd3aWtpZGF0YS5vcmcnLCAnbS53aWtpZGF0YS5vcmcnLAogICAgICAgICAgICAgICdjb21tb25zLndpa2ltZWRpYS5vcmcnLCAnY29tbW9ucy5tLndpa2ltZWRpYS5vcmcnLAogICAgICAgICAgICAgICdkZS53aWtpdm95YWdlLm9yZycsICdkZS5tLndpa2l2b3lhZ2Uub3JnJywKICAgICAgICAgICAgICAnZGUud2lrdGlvbmFyeS5vcmcnLCAnZGUubS53aWt0aW9uYXJ5Lm9yZycpCnVyaV9wYXRoICA8LSBjKAogICcvd2lraS9MYW5nZXJfR3J1bmQtS29obGJlcmc/dG91cj1laW5mdWhydW5nJywKICAnL3dpa2kvU3BlemlhbDpCZW51dHplcmtvbnRvX2FubGVnZW4nLAogICcvd2lraS9XaWtpZGF0YTpJbnRyb2R1Y3Rpb24vZGUnLAogICcvd2lraS9TcGV6aWFsOkFubWVsZGVuJywKICAnL3dpa2kvU3BlY2lhbDpNeVBhZ2UvQXJ0aWtlbHdlcmtzdGF0dCcsCiAgJy93aWtpL0JlbnV0emVyOlRyYWluaW5nc2tvbnRvXyhXTURFKS9XaWtpcGVkaWFfVHJhaW5pbmcnLAogICcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0RlaW5FbmdhZ2VtZW50L0xpdGVyYXR1cicsCiAgJy93L2luZGV4LnBocD90aXRsZT1RMTY5NDMyNzMmdG91cj13Yml0ZW1zJmRhdGE9b2snLAogICcvdy9pbmRleC5waHA/dGl0bGU9U3BlY2lhbDpVcGxvYWRXaXphcmQmdXNlbGFuZz1kZScsCiAgJy93aWtpL0hhdXB0c2VpdGUnLAogICcvd2lraS9XaWt0aW9uYXJ5OkhhdXB0c2VpdGUnLAogICcvd2lraS9Db21tb25zOldpbGxrb21tZW4nKQojIHVyaV9xdWVyeSA8LSBwYXN0ZTAoJ1dNREVfMjAyMF9jaGFsbGVuZ2VfJywgMTozMCkKcXVlcnlGaWxlIDwtICdlbWFpbEZvbGxvd1VwMjAxOV9QYWdldmlld3MuaHFsJwpmaWxlTmFtZSA8LSBwYXN0ZTAoInBhZ2V2aWV3c18iLCBjZXREYXksICIudHN2IikKCiMgLSBjb2xsZWN0IFBhZ2V2aWV3cyBkYXRhCndtZGVfY29sbGVjdF9wYWdldmlld3ModXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyKQoKIyMjIC0tLSBXcmFuZ2xlIFBhZ2V2aWV3cwojIC0gZnVuY3Rpb246IHdtZGVfcHJvY2Vzc19wYWdldmlld3MKd21kZV9wcm9jZXNzX3BhZ2V2aWV3cyA8LSBmdW5jdGlvbihmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcXVlcnlfZmlsdGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhZ2VfZmlsdGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUgPSBjYW1wYWlnbk5hbWUpIHsKICAKICAjIC0gdG8gZGF0YURpcgogIHNldHdkKGRhdGFEaXIpCiAgCiAgIyAtIGxpYnJhcmllcwogIGxpYnJhcnkoc3RyaW5ncikKICBsaWJyYXJ5KGRwbHlyKQogIGxpYnJhcnkodGlkeXIpCiAgbGlicmFyeShkYXRhLnRhYmxlKQogIAogICMgLSBsb2FkCiAgcGFnZXZpZXdzRGF0YSA8LSByZWFkTGluZXMoZmlsZU5hbWUpCiAgd1N0YXJ0IDwtIHdoaWNoKGdyZXBsKCJ1cmlfaG9zdCIsIHBhZ2V2aWV3c0RhdGEpKQogIHBhZ2V2aWV3c0RhdGEgPC0gcGFnZXZpZXdzRGF0YVsod1N0YXJ0ICsgMik6KGxlbmd0aChwYWdldmlld3NEYXRhKSAtIDIpXQogIHBhZ2V2aWV3c0RhdGEgPC0gZGF0YS5mcmFtZShkYXQgPSBwYWdldmlld3NEYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgcGFnZXZpZXdzRGF0YSA8LSBzZXBhcmF0ZShwYWdldmlld3NEYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50byA9IGMoJ3VyaV9ob3N0JywgJ3VyaV9wYXRoJywgJ3VyaV9xdWVyeScsICdyZWZlcmVyJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiKQogICMgLSBhcHBseSBwYWdlX2ZpbHRlcgogIHBhZ2V2aWV3c0RhdGEkcGFnZSA8LSBwYXN0ZTAocGFnZXZpZXdzRGF0YSR1cmlfaG9zdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWdldmlld3NEYXRhJHVyaV9wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkpCiAgd0ZpbHRlciA8LSBzYXBwbHkocGFnZXZpZXdzRGF0YSRwYWdlLCBmdW5jdGlvbih4KSB7CiAgICBzYXBwbHkocGFnZV9maWx0ZXIsIGZ1bmN0aW9uKHkpIHsKICAgICAgZ3JlcGwoeSwgeCkKICAgIH0pCiAgfSkKICB3RmlsdGVyIDwtIGNvbFN1bXMod0ZpbHRlcikKICB3RmlsdGVyIDwtIHdoaWNoKHdGaWx0ZXIgPiAwKQogIHBhZ2V2aWV3c0RhdGEgPC0gcGFnZXZpZXdzRGF0YVt3RmlsdGVyLCBdCiAgIyAtIGFwcGx5IHVyaV9xdWVyeV9maWx0ZXIKICAjIC0gTk9URTogbG9va2luZyBpbiBib3RoOiB1cmlfcXVlcnksIHJlZmVyZXIgCiAgd191cmlfcXVlcnkgPC0gd2hpY2goZ3JlcGwodXJpX3F1ZXJ5X2ZpbHRlciwgcGFnZXZpZXdzRGF0YSRwYWdlKSkKICAKICBpZiAobGVuZ3RoKHdfdXJpX3F1ZXJ5KSA+IDApIHsKICAgIAogICAgIyAtIGZpbHRlciBmb3Igd191cmlfcXVlcnkKICAgIHBhZ2V2aWV3c0RhdGEgPC0gcGFnZXZpZXdzRGF0YVt3X3VyaV9xdWVyeSwgXSAKICAgIAogICAgIyAtIGFnZ3JlZ2F0ZToKICAgIHBhZ2V2aWV3c0RhdGEkdXJpX3BhdGggPC0gcGFzdGUwKHBhZ2V2aWV3c0RhdGEkdXJpX2hvc3QsIHBhZ2V2aWV3c0RhdGEkdXJpX3BhdGgpCiAgICBwYWdldmlld3NEYXRhJHVyaV9ob3N0IDwtIE5VTEwKICAgIHBhZ2V2aWV3c0RhdGEkcGFnZSA8LSBOVUxMCiAgICBwYWdldmlld3NEYXRhJHJlZmVyZXIgPC0gTlVMTAogICAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhICU+JSAKICAgICAgZHBseXI6OnNlbGVjdCh1cmlfcXVlcnksIHVyaV9wYXRoKSAlPiUgCiAgICAgIGRwbHlyOjpncm91cF9ieSh1cmlfcXVlcnksIHVyaV9wYXRoKSAlPiUgCiAgICAgIGRwbHlyOjpzdW1tYXJpc2UocGFnZXZpZXdzID0gbigpKQogICAgY29sbmFtZXMocGFnZXZpZXdzRGF0YSkgPC0gYygnVGFnJywgJ1BhZ2UnLCAnUGFnZXZpZXdzJykKICAgIAogICAgIyAtIGFkZCBjZXREYXksIGNhbXBhaWduTmFtZQogICAgcGFnZXZpZXdzRGF0YSRkYXRlIDwtIGNldERheQogICAgcGFnZXZpZXdzRGF0YSRjYW1wYWlnbiA8LSBjYW1wYWlnbk5hbWUKICAgIAogICAgIyAtIHN0b3JlOgogICAgd3JpdGUuY3N2KHBhZ2V2aWV3c0RhdGEsIAogICAgICAgICAgICAgIHBhc3RlMChhbmFseXRpY3NEaXIsIAogICAgICAgICAgICAgICAgICAgICAicGFnZXZpZXdzQWdncmVnYXRlZF8iLAogICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdCgKICAgICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdChmaWxlTmFtZSwgc3BsaXQgPSAiXyIsIGZpeGVkID0gVClbWzFdXVsyXSwKICAgICAgICAgICAgICAgICAgICAgICBzcGxpdCA9ICIuIiwgCiAgICAgICAgICAgICAgICAgICAgICAgZml4ZWQgPSBUKVtbMV1dWzFdLAogICAgICAgICAgICAgICAgICAgICAiLmNzdiIKICAgICAgICAgICAgICApCiAgICApCiAgICAKICB9CiAgCn0KCiMgLSBzZXQgcGFyYW1zIHRvIHdtZGVfcHJvY2Vzc19wYWdldmlld3MKIyAtIGZvciB0aGUgV01ERSAyMDIwX0VtYWlsQ2FtcGFpZ25XaWtpcGVkaWFDaGFsbGVuZ2UKdXJpX3F1ZXJ5X2ZpbHRlciA8LSAnV01ERV8yMDIwX2NoYWxsZW5nZV8nCnBhZ2VfZmlsdGVyIDwtIGMoJ2RlLndpa2lwZWRpYS5vcmcvd2lraS9MYW5nZXJfR3J1bmQtS29obGJlcmc/dG91cj1laW5mdWhydW5nJywgCiAgICAgICAgICAgICAgICAgJ2RlLndpa2lwZWRpYS5vcmcvd2lraS9TcGV6aWFsOkJlbnV0emVya29udG9fYW5sZWdlbicsIAogICAgICAgICAgICAgICAgICd3aWtpZGF0YS5vcmcvd2lraS9XaWtpZGF0YTpJbnRyb2R1Y3Rpb24vZGUnLCAKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL1NwZXppYWw6QW5tZWxkZW4nLCAKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL1NwZXppYWw6QW5tZWxkZW4nLCAKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL1NwZWNpYWw6TXlQYWdlL0FydGlrZWx3ZXJrc3RhdHQnLCAKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL0JlbnV0emVyOlRyYWluaW5nc2tvbnRvXyhXTURFKS9XaWtpcGVkaWFfVHJhaW5pbmcnLCAKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL1dpa2lwZWRpYTpXaWtpbWVkaWFfRGV1dHNjaGxhbmQvRGVpbkVuZ2FnZW1lbnQvTGl0ZXJhdHVyJywgCiAgICAgICAgICAgICAgICAgJ3d3dy53aWtpZGF0YS5vcmcvdy9pbmRleC5waHA/dGl0bGU9UTE2OTQzMjczJnRvdXI9d2JpdGVtcyZkYXRhPW9rJywgCiAgICAgICAgICAgICAgICAgJ2NvbW1vbnMud2lraW1lZGlhLm9yZy93L2luZGV4LnBocD90aXRsZT1TcGVjaWFsOlVwbG9hZFdpemFyZCZ1c2VsYW5nPWRlJywgCiAgICAgICAgICAgICAgICAgJ2NvbW1vbnMud2lraW1lZGlhLm9yZy93aWtpL0NvbW1vbnM6V2lsbGtvbW1lbicsCiAgICAgICAgICAgICAgICAgJ2RlLndpa2lwZWRpYS5vcmcvd2lraS9CZW51dHplcjpUcmFpbmluZ3Nrb250b18oV01ERSkvV2lraXBlZGlhX1RyYWluaW5nJywgCiAgICAgICAgICAgICAgICAgJ2RlLndpa2l2b3lhZ2Uub3JnL3dpa2kvSGF1cHRzZWl0ZScsIAogICAgICAgICAgICAgICAgICdkZS53aWt0aW9uYXJ5Lm9yZy93aWtpL1dpa3Rpb25hcnk6SGF1cHRzZWl0ZScsIAogICAgICAgICAgICAgICAgICdkZS5tLndpa2lwZWRpYS5vcmcvd2lraS9MYW5nZXJfR3J1bmQtS29obGJlcmc/dG91cj1laW5mdWhydW5nJywgCiAgICAgICAgICAgICAgICAgJ2RlLm0ud2lraXBlZGlhLm9yZy93aWtpL1NwZXppYWw6QmVudXR6ZXJrb250b19hbmxlZ2VuJywgCiAgICAgICAgICAgICAgICAgJ20ud2lraWRhdGEub3JnL3dpa2kvV2lraWRhdGE6SW50cm9kdWN0aW9uL2RlJywgCiAgICAgICAgICAgICAgICAgJ2RlLm0ud2lraXBlZGlhLm9yZy93aWtpL1NwZXppYWw6QW5tZWxkZW4nLCAKICAgICAgICAgICAgICAgICAnZGUubS53aWtpcGVkaWEub3JnL3dpa2kvU3BlemlhbDpBbm1lbGRlbicsIAogICAgICAgICAgICAgICAgICdkZS5tLndpa2lwZWRpYS5vcmcvd2lraS9TcGVjaWFsOk15UGFnZS9BcnRpa2Vsd2Vya3N0YXR0JywgCiAgICAgICAgICAgICAgICAgJ2RlLm0ud2lraXBlZGlhLm9yZy93aWtpL0JlbnV0emVyOlRyYWluaW5nc2tvbnRvXyhXTURFKS9XaWtpcGVkaWFfVHJhaW5pbmcnLCAKICAgICAgICAgICAgICAgICAnZGUubS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9EZWluRW5nYWdlbWVudC9MaXRlcmF0dXInLCAKICAgICAgICAgICAgICAgICAnd3d3Lndpa2lkYXRhLm9yZy93L2luZGV4LnBocD90aXRsZT1RMTY5NDMyNzMmdG91cj13Yml0ZW1zJmRhdGE9b2snLCAKICAgICAgICAgICAgICAgICAnY29tbW9ucy5tLndpa2ltZWRpYS5vcmcvdy9pbmRleC5waHA/dGl0bGU9U3BlY2lhbDpVcGxvYWRXaXphcmQmdXNlbGFuZz1kZScsCiAgICAgICAgICAgICAgICAgJ2NvbW1vbnMubS53aWtpbWVkaWEub3JnL3dpa2kvQ29tbW9uczpXaWxsa29tbWVuJywKICAgICAgICAgICAgICAgICAnZGUubS53aWtpcGVkaWEub3JnL3dpa2kvQmVudXR6ZXI6VHJhaW5pbmdza29udG9fKFdNREUpL1dpa2lwZWRpYV9UcmFpbmluZycsIAogICAgICAgICAgICAgICAgICdkZS5tLndpa2l2b3lhZ2Uub3JnL3dpa2kvSGF1cHRzZWl0ZScsIAogICAgICAgICAgICAgICAgICdkZS5tLndpa3Rpb25hcnkub3JnL3dpa2kvV2lrdGlvbmFyeTpIYXVwdHNlaXRlJykKCiMgLSB3cmFuZ2xlIHBhZ2V2aWV3cwp3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzKGZpbGVOYW1lID0gZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpciA9IGRhdGFEaXIsCiAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5X2ZpbHRlciA9IHVyaV9xdWVyeV9maWx0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgIHBhZ2VfZmlsdGVyID0gcGFnZV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5ID0gY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSA9IGNhbXBhaWduTmFtZSkKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gVXNlciBSZWdpc3RyYXRpb25zCiMjIyAtLS0gSGl2ZVFMIHF1ZXJ5OiBldmVudC5TZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uIHRhYmxlCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmhpdmVRTCA8LQogICJTRUxFQ1QgeWVhciwgbW9udGgsIGRheSwgaG91ciwgIGV2ZW50LmNhbXBhaWduLCBldmVudC51c2VySWQsIGV2ZW50LnVzZXJOYW1lLCB3ZWJob3N0LCB3aWtpCiAgICAgIEZST00gZXZlbnQuc2VydmVyc2lkZWFjY291bnRjcmVhdGlvbgogICAgICBXSEVSRQogICAgICAgIHllYXIgPSAyMDIwCiAgICAgICAgQU5EIChtb250aCA9IDUgT1IgbW9udGggPSA2KQogICAgICAgIEFORCAoZXZlbnQuY2FtcGFpZ24gTElLRSAnJVdNREVfMjAyMF9jaGFsbGVuZ2VfJScpOyIKIyAtIHdyaXRlIGhxbAp3cml0ZShoaXZlUUwsIHBhc3RlMChkYXRhRGlyLCAndXNlcl9yZWdpc3RyYXRpb25zLmhxbCcpKQojIyMgLS0tIG91dHB1dCBmaWxlbmFtZQpmaWxlbmFtZSA8LSBwYXN0ZTAoZGF0YURpciwgJ3VzZXJfcmVnaXN0cmF0aW9ucy50c3YnKQojIyMgLS0tIGV4ZWN1dGUgaHFsIHNjcmlwdDoKaGl2ZUFyZ3MgPC0gJ3N1ZG8gLXUgYW5hbHl0aWNzLXByaXZhdGVkYXRhIGtlcmJlcm9zLXJ1bi1jb21tYW5kIGFuYWx5dGljcy1wcml2YXRlZGF0YSAvdXNyL2xvY2FsL2Jpbi9iZWVsaW5lIC0tc2lsZW50IC0taW5jcmVtZW50YWw9dHJ1ZSAtLXZlcmJvc2U9ZmFsc2UgLWYnCmhpdmVJbnB1dCA8LSBwYXN0ZShwYXN0ZTAoZGF0YURpciwgJ3VzZXJfcmVnaXN0cmF0aW9ucy5ocWwnKSwKICAgICAgICAgICAgICAgICAgICIgPiAiLAogICAgICAgICAgICAgICAgICAgZmlsZW5hbWUsCiAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKIyAtIGNvbW1hbmQ6CmhpdmVDb21tYW5kIDwtIHBhc3RlKGhpdmVBcmdzLCBoaXZlSW5wdXQpCnN5c3RlbShjb21tYW5kID0gaGl2ZUNvbW1hbmQsIHdhaXQgPSBUUlVFKQoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBVc2VyIEVkaXRzCiMjIyAtLS0gVXNlciBFZGl0cyB2aWEgcmV2aXNpb25fYWN0b3JfdGVtcAojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKc3RhcnRUaW1lc3RhbXAgPC0gJzIwMjAwNTE0MDAwMDAwJwojIC0gZ2V0IHVzZXIgaWRzCnVzZXJSZWdpc3RyYXRpb25zIDwtIGZyZWFkKHBhc3RlMChkYXRhRGlyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3VzZXJfcmVnaXN0cmF0aW9ucy50c3YnKSkKZGltKHVzZXJSZWdpc3RyYXRpb25zKQojIC0gY2xlYW4gdXAgZnJvbSB0ZXN0IGFjY291bnRzCnVzZXJSZWdpc3RyYXRpb25zIDwtIGZpbHRlcih1c2VyUmVnaXN0cmF0aW9ucywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAhZ3JlcGwoIlRlc3QgRS1NYWlsIENhbXBhaWduIDIwMjAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2VyUmVnaXN0cmF0aW9ucyR1c2VybmFtZSkpCmRpbSh1c2VyUmVnaXN0cmF0aW9ucykKaGVhZCh1c2VyUmVnaXN0cmF0aW9ucykKcmV2X3VzZXIgPC0gdXNlclJlZ2lzdHJhdGlvbnMkdXNlcmlkCiMgLSBpdGVyYXRlIG92ZXIgcmV2X3VzZXIKZm9yIChpIGluIDE6bGVuZ3RoKHJldl91c2VyKSkgewogICMgLSBTUUwgcXVlcnkKICBzcWxRdWVyeSA8LSBwYXN0ZSgiXCJTRUxFQ1QgYWN0b3IuYWN0b3JfaWQsIAogICAgICAgICAgICAgICAgICAgICAgICBhY3Rvci5hY3Rvcl91c2VyLCAKICAgICAgICAgICAgICAgICAgICAgICAgYWN0b3IuYWN0b3JfbmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHJldmlzaW9uX2FjdG9yX3RlbXAucmV2YWN0b3JfdGltZXN0YW1wIAogICAgICAgICAgICAgICAgICAgIEZST00gYWN0b3IgCiAgICAgICAgICAgICAgICAgICAgTEVGVCBKT0lOIHJldmlzaW9uX2FjdG9yX3RlbXAgT04gKGFjdG9yLmFjdG9yX2lkID0gcmV2aXNpb25fYWN0b3JfdGVtcC5yZXZhY3Rvcl9hY3RvcikgCiAgICAgICAgICAgICAgICAgICAgV0hFUkUgKHJldmlzaW9uX2FjdG9yX3RlbXAucmV2YWN0b3JfdGltZXN0YW1wID49IDIwMjAwNTExMDAwMDAwIAogICAgICAgICAgICAgICAgICAgICAgQU5EIGFjdG9yLmFjdG9yX3VzZXIgPSAiLCByZXZfdXNlcltpXSwgIik7XCIiKTsKICAjIyMgLS0tIG91dHB1dCBmaWxlbmFtZQogIGZpbGVuYW1lIDwtIHBhc3RlKGRhdGFEaXIsJ3VzZXJFZGl0cycsICJfIiwgaSwgIi50c3YiLCBzZXAgPSAiIikKICAjIyMgLS0tIGV4ZWN1dGUgc3FsIHNjcmlwdDoKICBzcWxMb2dJblByZSA8LSBwYXN0ZTAoJy91c3IvbG9jYWwvYmluL2FuYWx5dGljcy1teXNxbCBkZXdpa2kgLWUgJykKICBzcWxJbnB1dCA8LSBwYXN0ZShzcWxRdWVyeSwKICAgICAgICAgICAgICAgICAgICAiID4gIiwKICAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSwKICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAjIC0gY29tbWFuZDoKICBzcWxDb21tYW5kIDwtIHBhc3RlKHNxbExvZ0luUHJlLCBzcWxJbnB1dCkKICBzeXN0ZW0oY29tbWFuZCA9IHNxbENvbW1hbmQsIHdhaXQgPSBUUlVFKQogICMgLSByZXBvcnQKICBwcmludChwYXN0ZTAoIkRPTkU6IHVzZXIgIiwgaSwgIi4iKSkKfQojIyMgLS0tIEVORCBydW4gU1FMIHNjcmlwdHMKIyAtIGxvYWQgdXNlciBlZGl0czoKbEYgPC0gbGlzdC5maWxlcyhkYXRhRGlyKQpsRiA8LSBsRltncmVwbCgiXnVzZXJFZGl0c18iLCBsRildCnVzZXJFZGl0cyA8LSBsYXBwbHkocGFzdGUwKGRhdGFEaXIsIGxGKSwgZnJlYWQpCnVzZXJFZGl0cyA8LSByYmluZGxpc3QodXNlckVkaXRzKQojIC0gc3RvcmUgdXNlciBlZGl0czoKd3JpdGUuY3N2KHVzZXJFZGl0cywgcGFzdGUwKGFuYWx5dGljc0RpciwgJ3VzZXJFZGl0cy5jc3YnKSkKCmBgYAoKIyMgMS4gQ2FtcGFpZ24gUGFnZXZpZXdzCgpUaGlzIHNlY3Rpb24gcHJlc2VudHMgYWxsIGRhdGEgYW5kIHN0YXRpc3RpY3Mgb24gdGhlIGNhbXBhaWduIHBhZ2VzLgoKVGhlIGZvbGxvd2luZyBjaHVuayBsb2FkcyBhbmQgdGhlbiByZS1zdHJ1Y3R1cmVzIHRoZSBkYXRhc2V0IGEgYml0LgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmxGIDwtIGxpc3QuZmlsZXMoJ19hbmFseXRpY3MnKQpsRiA8LSBsRltncmVwbCgiXnBhZ2V2aWV3c0FnZ3JlZ2F0ZWQiLCBsRildCmRhdGFTZXQgPC0gbGFwcGx5KHBhc3RlMCgiX2FuYWx5dGljcy8iLCBsRiksIGZyZWFkKQpkYXRhU2V0IDwtIHJiaW5kbGlzdChkYXRhU2V0KQpkYXRhU2V0JFYxIDwtIE5VTEwKZGF0YVNldCRjYW1wYWlnbiA8LSBOVUxMCiMgLSBleHBhbmQgZ3JpZCB0byBhY2NvdW50IGZvciBtaXNzaW5nIG9ic2VydmF0aW9ucyBwZXIgZGF5CmRTIDwtIGV4cGFuZC5ncmlkKHVuaXF1ZShkYXRhU2V0JFRhZyksIAogICAgICAgICAgICAgICAgICB1bmlxdWUoZGF0YVNldCRQYWdlKSwgCiAgICAgICAgICAgICAgICAgIHVuaXF1ZShkYXRhU2V0JGRhdGUpLCAKICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGRTKSA8LSBjKCdUYWcnLCAnUGFnZScsICdkYXRlJykKZFMgPC0gZFMgJT4lIAogIGxlZnRfam9pbihkYXRhU2V0LCAKICAgICAgICAgICAgYnkgPSBjKCJUYWciLCAiUGFnZSIsICJkYXRlIikpCmRhdGFTZXQgPC0gZFM7IHJtKGRTKQpkYXRhU2V0JFBhZ2V2aWV3c1tpcy5uYShkYXRhU2V0JFBhZ2V2aWV3cyldIDwtIDAKZGF0YVNldCRUYWcgPC0gZ3N1YigiJi4rJCIsICIiLCBkYXRhU2V0JFRhZykKZGF0YVNldCRUYWcgPC0gZ3N1YigiXFw/Y2FtcGFpZ249IiwgIiIsIGRhdGFTZXQkVGFnKQpkYXRhU2V0JGRhdGUgPC0gc2FwcGx5KGRhdGFTZXQkZGF0ZSwgZnVuY3Rpb24oeCkgewogIGQgPC0gc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV0KICBpZiAobmNoYXIoZFszXSkgPT0gMSkgewogICAgZFszXSA8LSBwYXN0ZTAoIjAiLCBkWzNdKQogIH0KICByZXR1cm4ocGFzdGUoZCwgY29sbGFwc2UgPSAiLSIpKQp9KQpgYGAKCiMjIyAxLjEgUGFnZXZpZXdzIE92ZXJ2aWV3CgoqKkNoYXJ0IDEuMS4xKiogRGFpbHkgUGFnZXZpZXdzLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gY2hhbm5lbHMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMH0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChkYXRlLCBQYWdlLCBQYWdldmlld3MpICU+JSAKICBncm91cF9ieShkYXRlLCBQYWdlKSAlPiUgCiAgc3VtbWFyaXNlKFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQpwRnJhbWUgPC0gYXJyYW5nZShwRnJhbWUsIGRhdGUpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF0ZSwKICAgICAgICAgICAgICAgICAgIHkgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUsIGdyb3VwID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZmFjZXRfd3JhcCh+UGFnZSwgbmNvbCA9IDIpICsgCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiUGFnZXZpZXdzIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICAjIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA2KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCioqVGFibGUgMS4xLjEqKiBQYWdldmlld3MgdG90YWxzCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KFBhZ2UsIFBhZ2V2aWV3cykgJT4lIAogIGdyb3VwX2J5KFBhZ2UpICU+JSAKICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkgJT4lIAogIGFycmFuZ2UoZGVzYyh0b3RhbFBhZ2V2aWV3cykpCmRhdGF0YWJsZSh0RnJhbWUpCmBgYAoKIyMjIDEuMiBQYWdldmlld3M6IENhbXBhaWduIENoYW5uZWxzCgoqKkNoYXJ0IDEuMi4xKiogUGFnZXZpZXdzLCBieSAqKmNoYW5uZWxzKioKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEwfQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRhdGUsIFRhZywgUGFnZXZpZXdzKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgVGFnKSAlPiUgCiAgc3VtbWFyaXNlKFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQpwRnJhbWUgPC0gYXJyYW5nZShwRnJhbWUsIGRhdGUpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF0ZSwKICAgICAgICAgICAgICAgICAgIHkgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUsIGdyb3VwID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZmFjZXRfd3JhcCh+VGFnLCBuY29sID0gMikgKyAKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJQYWdldmlld3MiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogICMgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDYpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKKipDaGFydCAxLjIuMioqIFRvdGFsIFBhZ2V2aWV3cywgYnkgKipjaGFubmVscyoqCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CnBGcmFtZSA8LSBwRnJhbWUgJT4lIAogIHNlbGVjdChUYWcsIFBhZ2V2aWV3cykgJT4lIAogIGdyb3VwX2J5KFRhZykgJT4lIAogIHN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhQYWdldmlld3MpKQpwRnJhbWUkVGFnIDwtIGZhY3RvcihwRnJhbWUkVGFnLAogICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBwRnJhbWUkVGFnLAogICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBUYWcsCiAgICAgICAgICAgICAgICAgICB5ID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgY29sb3IgPSAiYmx1ZSIsIGZpbGwgPSAid2hpdGUiKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJzIwMjAgV01ERSBPY2Nhc2lvbmFsIEVkaXRvcnMgQmFubmVyIENhbXBhaWduJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIlBhZ2V2aWV3cyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgoqKlRhYmxlIDIuMi4xKiogVG90YWwgcGFnZXZpZXdzLCBieSAqKmNoYW5uZWxzKiogYW5kIGJ5ICoqcGFnZXMqKi4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQp0RnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KFRhZywgUGFnZSwgUGFnZXZpZXdzKSAlPiUgCiAgZ3JvdXBfYnkoVGFnLCBQYWdlKSAlPiUgCiAgc3VtbWFyaXNlKHRvdGFsUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpICU+JSAKICBhcnJhbmdlKGRlc2ModG90YWxQYWdldmlld3MpKQpkYXRhdGFibGUodEZyYW1lKQpgYGAKCiMjIDIuIFVzZXIgUmVnaXN0cmF0aW9ucwoKVGhpcyBzZWN0aW9uIHByZXNlbnRzIGFsbCBkYXRhIGFuZCBzdGF0aXN0aWNzIG9uIHRoZSB1c2VyIHJlZ2lzdHJhdGlvbnMuCgojIyMgMi4xIFVzZXIgUmVnaXN0cmF0aW9ucyBPdmVydmlldwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmxGIDwtIGxpc3QuZmlsZXMoIl9hbmFseXRpY3MiKQpsRiA8LSBsRltncmVwbCgiXnVzZXJfcmVnaXN0cmF0aW9ucyIsIGxGKV0KZGF0YVNldCA8LSBmcmVhZChwYXN0ZTAoIl9hbmFseXRpY3MvIiwgbEYpKQpkYXRhU2V0IDwtIGZpbHRlcihkYXRhU2V0LCAKICAgICAgICAgICAgICAgICAgIWdyZXBsKCJeVGVzdCBFLU1haWwgQ2FtcGFpZ24iLCBkYXRhU2V0JHVzZXJuYW1lKSkKZGF0YVNldCRkYXRlIDwtIHBhc3RlKGRhdGFTZXQkeWVhciwgCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UobmNoYXIoZGF0YVNldCRtb250aCkgPT0gMSwgcGFzdGUwKCIwIiwgZGF0YVNldCRtb250aCksIGRhdGFTZXQkbW9udGgpLAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKG5jaGFyKGRhdGFTZXQkZGF5KSA9PSAxLCBwYXN0ZTAoIjAiLCBkYXRhU2V0JGRheSksIGRhdGFTZXQkZGF5KSwKICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICItIikKcmVnVXNlcnMgPC0gc2VsZWN0KGRhdGFTZXQsCiAgICAgICAgICAgICAgICAgICB1c2VyaWQsCiAgICAgICAgICAgICAgICAgICB1c2VybmFtZSwgCiAgICAgICAgICAgICAgICAgICBjYW1wYWlnbikKIyAtIGV4cGFuZCBncmlkIHRvIGFjY291bnQgZm9yIG1pc3Npbmcgb2JzZXJ2YXRpb25zIHBlciBkYXkKZGF0ZVNwYW4gPC0gc2VxKGZyb20gPSBhcy5EYXRlKCIyMDIwLTA1LTE0IiksIAogICAgICAgICAgICAgICAgdG8gPSBhcy5EYXRlKCIyMDIwLTA2LTMwIiksIAogICAgICAgICAgICAgICAgYnkgPSAiZGF5IikKZGF0ZVNwYW4gPC0gYXMuY2hhcmFjdGVyKGRhdGVTcGFuKQpkUyA8LSBleHBhbmQuZ3JpZChkYXRlU3BhbiwgCiAgICAgICAgICAgICAgICAgIHVuaXF1ZShkYXRhU2V0JGNhbXBhaWduKSwgCiAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhkUykgPC0gYygnZGF0ZScsICdjYW1wYWlnbicpCmRTIDwtIGRTICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KGRhdGFTZXQsIGRhdGUsIGNhbXBhaWduLCB1c2VyaWQpLAogICAgICAgICAgICBieSA9IGMoImRhdGUiLCAiY2FtcGFpZ24iKSkKZFMkdXNlcmlkIDwtIGlmZWxzZShpcy5uYShkUyR1c2VyaWQpLCAwLCAxKQpwRnJhbWUgPC0gZFMgJT4lIAogIHNlbGVjdChkYXRlLCB1c2VyaWQpICU+JSAKICBncm91cF9ieShkYXRlKSAlPiUgCiAgc3VtbWFyaXNlKHJlZ2lzdHJhdGlvbnMgPSBzdW0odXNlcmlkKSkgJT4lIAogIGFycmFuZ2UoZGF0ZSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IHJlZ2lzdHJhdGlvbnMsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHJlZ2lzdHJhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSwgZ3JvdXAgPSAxKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJSZWdpc3RyYXRpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCiMjIyAyLjIgVXNlciBSZWdpc3RyYXRpb25zIHBlciBDYW1wYWlnbiBDaGFubmVsCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA4fQpwRnJhbWUgPC0gZFMgJT4lIAogIGdyb3VwX2J5KGRhdGUsIGNhbXBhaWduKSAlPiUgCiAgc3VtbWFyaXNlKHJlZ2lzdHJhdGlvbnMgPSBzdW0odXNlcmlkKSkgJT4lIAogIGFycmFuZ2UoZGF0ZSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IHJlZ2lzdHJhdGlvbnMsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHJlZ2lzdHJhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSwgZ3JvdXAgPSAxKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBmYWNldF93cmFwKH5jYW1wYWlnbiwgbmNvbCA9IDIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiUmVnaXN0cmF0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgIyBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA2KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCiMjIyAyLjMgVG90YWwgVXNlciBSZWdpc3RyYXRpb25zIHBlciBDYW1wYWlnbiBDaGFubmVsCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CnBGcmFtZSA8LSBkUyAlPiUgCiAgc2VsZWN0KGNhbXBhaWduLCB1c2VyaWQpICU+JSAKICBncm91cF9ieSggY2FtcGFpZ24pICU+JSAKICBzdW1tYXJpc2UocmVnaXN0cmF0aW9ucyA9IHN1bSh1c2VyaWQpKSAlPiUgCiAgYXJyYW5nZShkZXNjKHJlZ2lzdHJhdGlvbnMpKQpwRnJhbWUkY2FtcGFpZ24gPC0gZmFjdG9yKHBGcmFtZSRjYW1wYWlnbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gcEZyYW1lJGNhbXBhaWduLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBjYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgIHkgPSByZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSByZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsdWUiLCBmaWxsID0gIndoaXRlIikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJSZWdpc3RyYXRpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCiMjIDMuIFVzZXIgRWRpdHMKClRoaXMgc2VjdGlvbiBwcmVzZW50cyBhbGwgZGF0YSBhbmQgc3RhdGlzdGljcyBvbiB0aGUgdXNlciBlZGl0cy4KCiMjIyAzLjEgVXNlciBFZGl0cyBPdmVydmlldwoKVGhlIGZvbGxvd2luZyBjaHVuayBsb2FkcyB0aGUgZGF0YXNldCBvZiB1c2VyIGludGVyYWN0aW9ucyB3aXRoIGNhbXBhaWduIGNoYW5uZWxzIGFuZCB0aGVuIHJlLXN0cnVjdHVyZXMgdGhlIGRhdGFzZXQgYSBiaXQuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KbEYgPC0gbGlzdC5maWxlcygiX2FuYWx5dGljcyIpCmxGIDwtIGxGW2dyZXBsKCJedXNlckVkaXRzIiwgbEYpXQplZGl0U2V0IDwtIGZyZWFkKHBhc3RlMCgiX2FuYWx5dGljcy8iLCBsRikpCmVkaXRTZXQgPC0gc2VsZWN0KGVkaXRTZXQsCiAgICAgICAgICAgICAgICAgIGFjdG9yX25hbWUsIAogICAgICAgICAgICAgICAgICByZXZhY3Rvcl90aW1lc3RhbXApICAgICAgICAgICAgICAgICAKY29sbmFtZXMoZWRpdFNldCkgPC0gYygndXNlcm5hbWUnLCAncmV2X3RpbWVzdGFtcCcpCmRhdGFTZXQgPC0gbGVmdF9qb2luKGVkaXRTZXQsCiAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChyZWdVc2VycywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2VybmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduKSwKICAgICAgICAgICAgICAgICAgICAgYnkgPSAidXNlcm5hbWUiKQpkYXRhU2V0JHllYXIgPC0gc3Vic3RyKGRhdGFTZXQkcmV2X3RpbWVzdGFtcCwgMSwgNCkKZGF0YVNldCRtb250aCA8LSBzdWJzdHIoZGF0YVNldCRyZXZfdGltZXN0YW1wLCA1LCA2KQpkYXRhU2V0JGRheSA8LSBzdWJzdHIoZGF0YVNldCRyZXZfdGltZXN0YW1wLCA3LCA4KQpkYXRhU2V0JHJldl90aW1lc3RhbXAgPC0gTlVMTApkYXRhU2V0JGRhdGUgPC0gcGFzdGUoZGF0YVNldCR5ZWFyLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGFTZXQkbW9udGgsCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YVNldCRkYXksCiAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIi0iKQpkYXRhU2V0IDwtIHNlbGVjdChkYXRhU2V0LAogICAgICAgICAgICAgICAgICBkYXRlLAogICAgICAgICAgICAgICAgICB1c2VybmFtZSwKICAgICAgICAgICAgICAgICAgY2FtcGFpZ24pCmRhdGVTcGFuIDwtIHNlcShmcm9tID0gYXMuRGF0ZSgiMjAyMC0wNS0xNCIpLCAKICAgICAgICAgICAgICAgIHRvID0gYXMuRGF0ZShtYXgoZGF0YVNldCRkYXRlKSksIAogICAgICAgICAgICAgICAgYnkgPSAiZGF5IikKZGF0ZVNwYW4gPC0gYXMuY2hhcmFjdGVyKGRhdGVTcGFuKQpkUyA8LSBleHBhbmQuZ3JpZChkYXRlU3BhbiwgCiAgICAgICAgICAgICAgICAgIHVuaXF1ZShkYXRhU2V0JGNhbXBhaWduKSwKICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGRTKSA8LSBjKCdkYXRlJywgJ2NhbXBhaWduJykKZFMgPC0gZFMgJT4lIAogIGxlZnRfam9pbihzZWxlY3QoZGF0YVNldCwgZGF0ZSwgY2FtcGFpZ24sIHVzZXJuYW1lKSwKICAgICAgICAgICAgYnkgPSBjKCJkYXRlIiwgImNhbXBhaWduIikpCmRTJHVzZXJuYW1lIDwtIGlmZWxzZShpcy5uYShkUyR1c2VybmFtZSksIDAsIDEpCnBGcmFtZSA8LSBkUyAlPiUKICBzZWxlY3QoZGF0ZSwgdXNlcm5hbWUpICU+JSAKICBncm91cF9ieShkYXRlKSAlPiUgCiAgc3VtbWFyaXNlKGVkaXRzID0gc3VtKHVzZXJuYW1lKSkgJT4lIAogIGFycmFuZ2UoZGF0ZSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IGVkaXRzLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBlZGl0cywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjI1LCBncm91cCA9IDEpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJzIwMjAgV01ERSBPY2Nhc2lvbmFsIEVkaXRvcnMgQmFubmVyIENhbXBhaWduJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIkVkaXRzIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCiMjIyAzLjIgVXNlciBFZGl0cyBieSBDYW1wYWlnbiBDaGFubmVscwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gOH0KcEZyYW1lIDwtIGRTICU+JSAKICBncm91cF9ieShkYXRlLCBjYW1wYWlnbikgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IHN1bSh1c2VybmFtZSkpICU+JSAKICBhcnJhbmdlKGRhdGUpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF0ZSwKICAgICAgICAgICAgICAgICAgIHkgPSBlZGl0cywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZWRpdHMsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSwgZ3JvdXAgPSAxKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBmYWNldF93cmFwKH5jYW1wYWlnbiwgbmNvbCA9IDIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiRWRpdHMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogICMgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNS41KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCiMjIyAzLjMgVG90YWwgVXNlciBFZGl0cyBieSBDYW1wYWlnbiBDaGFubmVscwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwRnJhbWUgPC0gZFMgJT4lIAogIHNlbGVjdChjYW1wYWlnbiwgdXNlcm5hbWUpICU+JSAKICBncm91cF9ieSggY2FtcGFpZ24pICU+JSAKICBzdW1tYXJpc2UoZWRpdHMgPSBzdW0odXNlcm5hbWUpKSAlPiUgCiAgYXJyYW5nZShkZXNjKGVkaXRzKSkKcEZyYW1lJGNhbXBhaWduIDwtIGZhY3RvcihwRnJhbWUkY2FtcGFpZ24sIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHBGcmFtZSRjYW1wYWlnbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFQpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gY2FtcGFpZ24sCiAgICAgICAgICAgICAgICAgICB5ID0gZWRpdHMsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGVkaXRzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsdWUiLCBmaWxsID0gIndoaXRlIikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJFZGl0cyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyMgMy40IEVkaXQgQ2xhc3NlcwoKIyMjIyAzLjQuMSBFZGl0IENsYXNzZXM6IGFsbCB1c2VycwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnVzZXJDbGFzcyA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QodXNlcm5hbWUpICU+JSAKICBncm91cF9ieSh1c2VybmFtZSkgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IG4oKSkKZWRpdEJvdW5kYXJpZXMgPC0gbGlzdCgKICBjKDAsIDEpLCAKICBjKDIsIDQpLAogIGMoNSwgOSksCiAgYygxMCwgMjApLAogIGMoMjEsIDUwKSwKICBjKDUxLCAxMDApCikKdXNlckNsYXNzJGVkaXRDbGFzcyA8LSBzYXBwbHkodXNlckNsYXNzJGVkaXRzLCBmdW5jdGlvbih4KSB7CiAgd0VDIDwtIHNhcHBseShlZGl0Qm91bmRhcmllcywgZnVuY3Rpb24oeSkgewogICAgeCA+PSB5WzFdICYgeCA8PSB5WzJdCiAgfSkKICBpZiAoc3VtKHdFQykgPT0gMCkgewogICAgcmV0dXJuKCI+IDEwMCIpCiAgfSBlbHNlIHsKICAgIHJldHVybihwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzFdLAogICAgICAgICAgICAgICAgICAiIC0gIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsyXSwgCiAgICAgICAgICAgICAgICAgICIpIgogICAgICAgICAgICAgICAgICApCiAgICApCiAgfQp9KQplZGl0Q2xhc3MgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZSh1c2VyQ2xhc3MkZWRpdENsYXNzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhlZGl0Q2xhc3MpIDwtIGMoJ0VkaXQgQ2xhc3MnLCAnTnVtLlVzZXJzJykKZWRpdENsYXNzJG9yZGVyIDwtIGFzLm51bWVyaWMoc2FwcGx5KGVkaXRDbGFzcyRgRWRpdCBDbGFzc2AsIGZ1bmN0aW9uKHgpIHsKICBsb3dlciA8LSBzdHJfZXh0cmFjdCh4LCAnW1s6ZGlnaXQ6XV0rJykKfSkpCmVkaXRDbGFzcyA8LSBhcnJhbmdlKGVkaXRDbGFzcywgb3JkZXIpCmVkaXRDbGFzcyRvcmRlciA8LSBOVUxMCmRhdGF0YWJsZShlZGl0Q2xhc3MpCmBgYAoKIyMjIyAzLjQuMkEgRWRpdCBDbGFzc2VzIHBlciBDYW1wYWlnbiBDaGFubmVsOiBgV01ERV8yMDIwX2NoYWxsZW5nZV8xYAoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnVzZXJDbGFzcyA8LSBkYXRhU2V0ICU+JSAKICBmaWx0ZXIoY2FtcGFpZ24gPT0gIldNREVfMjAyMF9jaGFsbGVuZ2VfMSIpICU+JSAKICBzZWxlY3QodXNlcm5hbWUpICU+JSAKICBncm91cF9ieSh1c2VybmFtZSkgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IG4oKSkKZWRpdEJvdW5kYXJpZXMgPC0gbGlzdCgKICBjKDAsIDEpLCAKICBjKDIsIDQpLAogIGMoNSwgOSksCiAgYygxMCwgMjApLAogIGMoMjEsIDUwKSwKICBjKDUxLCAxMDApCikKdXNlckNsYXNzJGVkaXRDbGFzcyA8LSBzYXBwbHkodXNlckNsYXNzJGVkaXRzLCBmdW5jdGlvbih4KSB7CiAgd0VDIDwtIHNhcHBseShlZGl0Qm91bmRhcmllcywgZnVuY3Rpb24oeSkgewogICAgeCA+PSB5WzFdICYgeCA8PSB5WzJdCiAgfSkKICBpZiAoc3VtKHdFQykgPT0gMCkgewogICAgcmV0dXJuKCI+IDEwMCIpCiAgfSBlbHNlIHsKICAgIHJldHVybihwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzFdLAogICAgICAgICAgICAgICAgICAiIC0gIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsyXSwgCiAgICAgICAgICAgICAgICAgICIpIgogICAgICAgICAgICAgICAgICApCiAgICApCiAgfQp9KQplZGl0Q2xhc3MgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZSh1c2VyQ2xhc3MkZWRpdENsYXNzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhlZGl0Q2xhc3MpIDwtIGMoJ0VkaXQgQ2xhc3MnLCAnTnVtLlVzZXJzJykKZWRpdENsYXNzJG9yZGVyIDwtIGFzLm51bWVyaWMoc2FwcGx5KGVkaXRDbGFzcyRgRWRpdCBDbGFzc2AsIGZ1bmN0aW9uKHgpIHsKICBsb3dlciA8LSBzdHJfZXh0cmFjdCh4LCAnW1s6ZGlnaXQ6XV0rJykKfSkpCmVkaXRDbGFzcyA8LSBhcnJhbmdlKGVkaXRDbGFzcywgb3JkZXIpCmVkaXRDbGFzcyRvcmRlciA8LSBOVUxMCmRhdGF0YWJsZShlZGl0Q2xhc3MpCmBgYAoKIyMjIyAzLjQuMkIgRWRpdCBDbGFzc2VzIHBlciBDYW1wYWlnbiBDaGFubmVsOiBgV01ERV8yMDIwX2NoYWxsZW5nZV80YAoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnVzZXJDbGFzcyA8LSBkYXRhU2V0ICU+JSAKICBmaWx0ZXIoY2FtcGFpZ24gPT0gIldNREVfMjAyMF9jaGFsbGVuZ2VfNCIpICU+JSAKICBzZWxlY3QodXNlcm5hbWUpICU+JSAKICBncm91cF9ieSh1c2VybmFtZSkgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IG4oKSkKZWRpdEJvdW5kYXJpZXMgPC0gbGlzdCgKICBjKDAsIDEpLCAKICBjKDIsIDQpLAogIGMoNSwgOSksCiAgYygxMCwgMjApLAogIGMoMjEsIDUwKSwKICBjKDUxLCAxMDApCikKdXNlckNsYXNzJGVkaXRDbGFzcyA8LSBzYXBwbHkodXNlckNsYXNzJGVkaXRzLCBmdW5jdGlvbih4KSB7CiAgd0VDIDwtIHNhcHBseShlZGl0Qm91bmRhcmllcywgZnVuY3Rpb24oeSkgewogICAgeCA+PSB5WzFdICYgeCA8PSB5WzJdCiAgfSkKICBpZiAoc3VtKHdFQykgPT0gMCkgewogICAgcmV0dXJuKCI+IDEwMCIpCiAgfSBlbHNlIHsKICAgIHJldHVybihwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzFdLAogICAgICAgICAgICAgICAgICAiIC0gIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsyXSwgCiAgICAgICAgICAgICAgICAgICIpIgogICAgICAgICAgICAgICAgICApCiAgICApCiAgfQp9KQplZGl0Q2xhc3MgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZSh1c2VyQ2xhc3MkZWRpdENsYXNzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhlZGl0Q2xhc3MpIDwtIGMoJ0VkaXQgQ2xhc3MnLCAnTnVtLlVzZXJzJykKZWRpdENsYXNzJG9yZGVyIDwtIGFzLm51bWVyaWMoc2FwcGx5KGVkaXRDbGFzcyRgRWRpdCBDbGFzc2AsIGZ1bmN0aW9uKHgpIHsKICBsb3dlciA8LSBzdHJfZXh0cmFjdCh4LCAnW1s6ZGlnaXQ6XV0rJykKfSkpCmVkaXRDbGFzcyA8LSBhcnJhbmdlKGVkaXRDbGFzcywgb3JkZXIpCmVkaXRDbGFzcyRvcmRlciA8LSBOVUxMCmRhdGF0YWJsZShlZGl0Q2xhc3MpCmBgYAoKIyMjIyAzLjQuMkMgRWRpdCBDbGFzc2VzIHBlciBDYW1wYWlnbiBDaGFubmVsOiBgV01ERV8yMDIwX2NoYWxsZW5nZV81YAoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnVzZXJDbGFzcyA8LSBkYXRhU2V0ICU+JSAKICBmaWx0ZXIoY2FtcGFpZ24gPT0gIldNREVfMjAyMF9jaGFsbGVuZ2VfNSIpICU+JSAKICBzZWxlY3QodXNlcm5hbWUpICU+JSAKICBncm91cF9ieSh1c2VybmFtZSkgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IG4oKSkKZWRpdEJvdW5kYXJpZXMgPC0gbGlzdCgKICBjKDAsIDEpLCAKICBjKDIsIDQpLAogIGMoNSwgOSksCiAgYygxMCwgMjApLAogIGMoMjEsIDUwKSwKICBjKDUxLCAxMDApCikKdXNlckNsYXNzJGVkaXRDbGFzcyA8LSBzYXBwbHkodXNlckNsYXNzJGVkaXRzLCBmdW5jdGlvbih4KSB7CiAgd0VDIDwtIHNhcHBseShlZGl0Qm91bmRhcmllcywgZnVuY3Rpb24oeSkgewogICAgeCA+PSB5WzFdICYgeCA8PSB5WzJdCiAgfSkKICBpZiAoc3VtKHdFQykgPT0gMCkgewogICAgcmV0dXJuKCI+IDEwMCIpCiAgfSBlbHNlIHsKICAgIHJldHVybihwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzFdLAogICAgICAgICAgICAgICAgICAiIC0gIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsyXSwgCiAgICAgICAgICAgICAgICAgICIpIgogICAgICAgICAgICAgICAgICApCiAgICApCiAgfQp9KQplZGl0Q2xhc3MgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZSh1c2VyQ2xhc3MkZWRpdENsYXNzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhlZGl0Q2xhc3MpIDwtIGMoJ0VkaXQgQ2xhc3MnLCAnTnVtLlVzZXJzJykKZWRpdENsYXNzJG9yZGVyIDwtIGFzLm51bWVyaWMoc2FwcGx5KGVkaXRDbGFzcyRgRWRpdCBDbGFzc2AsIGZ1bmN0aW9uKHgpIHsKICBsb3dlciA8LSBzdHJfZXh0cmFjdCh4LCAnW1s6ZGlnaXQ6XV0rJykKfSkpCmVkaXRDbGFzcyA8LSBhcnJhbmdlKGVkaXRDbGFzcywgb3JkZXIpCmVkaXRDbGFzcyRvcmRlciA8LSBOVUxMCmRhdGF0YWJsZShlZGl0Q2xhc3MpCmBgYA==