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

The campaign is run from 2019/11/01 to 2019/11/09.

CURRENT UPDATE: Complete dataset as of 2019/11/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 ABC_2019_PRODUCTION.R from the stat1004 WMF statistics server, collecting the data as .tsv and .csv files, aggregating on the server with ABC_2019_Aggregation.R, copying manually and processing locally.

0.1 Daily Update

# !diagnostics off
### --- script: ABC_2019_PRODUCTION.R
### --- Data Acquisition for the Autumn Banner Campaign 2019
### --- run from stat1004
### --- /home/goransm/Analytics/NewEditors/Campaigns/2019AuBC/

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

### --- dir structure
campaignPath <- '/home/goransm/Analytics/NewEditors/Campaigns/2019AuBC/'
dataDir <- paste0(campaignPath, "_data/")
analyticsDir <- paste0(campaignPath, "_analytics/")

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

### ----------------------------------------------------------
### --- Banner Impressions
### ----------------------------------------------------------

# - function: wmde_collect_banner_impressions
wmde_collect_banner_impressions <- function(uri_host, 
                                            uri_path, 
                                            uri_query, 
                                            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_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, "'")
  }
  
  # - 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_query_condition
  if (length(uri_query) > 1) {
    uri_query_condition <- paste0("(",
                                  paste(
                                    paste0("uri_query LIKE '%", uri_query, "%'"),
                                    collapse = " OR ", sep = " "),
                                  ")"
    )
  } else {
    uri_query_condition = paste0("uri_query LIKE '%", uri_query, "%'")
  }
  
  # - compose HiveQL query
  hiveQuery <- paste0(
    "USE wmf;
    SELECT uri_query FROM webrequest
    WHERE (",
    uri_host_condition, " AND ",
    uri_path_condition, " AND ",
    uri_query_condition, " AND ",
    "(", datetimeCondition, ")",
    ");"
    )
  
  # - write hql
  write(hiveQuery, queryFile)
  # - execute hql script:
  hiveArgs <- '/usr/local/bin/beeline -f'
  hiveInput <- paste0(queryFile, ' > ', fileName)
  # - command:
  hiveCommand <- paste(hiveArgs, hiveInput)
  return(
    system(command = hiveCommand, wait = TRUE)
  )
}

# - set params to wmde_collect_banner_impressions
# - for the Autumn Banner Campaign 2019
uri_host <- c('de.wikipedia.org', 'de.m.wikipedia.org')
uri_path  <- '/beacon/impression'
uri_query <- c('WMDE_neweditors_autumn_2019')
queryFile <- 'abc2019_BannerImpressions.hql'
fileName <- paste0(dataDir, "bannerImpressions_", cetDay, ".tsv")

# - collect Banner Impression data
wmde_collect_banner_impressions(uri_host,
                                uri_path,
                                uri_query,
                                cetDay,
                                queryFile,
                                fileName,
                                dataDir)

# - function: wmde_process_banner_impressions
wmde_process_banner_impressions <- function(fileName,
                                            dataDir, 
                                            cetDay, 
                                            campaignName) {
  
  # - to dataDir
  setwd(dataDir)
  
  # - libraries
  library(stringr)
  library(dplyr)
  
  # - load
  bannerData <- read.delim(fileName, 
                           stringsAsFactors = F)
  colnames(bannerData) <- 'uri_query'
  
  # - clean
  wStart <- which(bannerData$uri_query == "uri_query")
  bannerData <- bannerData[(wStart + 1):(dim(bannerData)[1] - 2), ]
  
  # - split
  bannerData <- strsplit(bannerData, split = "&", fixed = T)
  # - extract relevant fields
  # - banner:
  banner <- sapply(bannerData, function(x) {
    x[which(grepl("^banner=", x))]
  })
  banner <- gsub("^banner=", "", banner)
  # - recordImpressionSampleRate:
  recordImpressionSampleRate <- sapply(bannerData, function(x) {
    x[which(grepl("^recordImpressionSampleRate=", x))]
  })
  recordImpressionSampleRate <- as.numeric(
    gsub("^recordImpressionSampleRate=", "", recordImpressionSampleRate)
  )
  # - result:
  result <- sapply(bannerData, function(x) {
    x[which(grepl("^result=", x))]
  })
  result <- gsub("^result=", "", result)
  
  # - compose table:
  bannerObservations <- data.frame(banner = banner, 
                                   recordImpressionSampleRate = recordImpressionSampleRate, 
                                   result = result, 
                                   stringsAsFactors = F)
  
  # - filter for result=show
  bannerObservations <- dplyr::filter(bannerObservations,
                                      result == "show")
  
  # - correction for recordImpressionSampleRate
  bannerObservations$recordImpressionSampleRate <- 
    1/bannerObservations$recordImpressionSampleRate
  
  # - aggregate:
  bannerObservations <- bannerObservations %>% 
    dplyr::select(banner, recordImpressionSampleRate) %>% 
    dplyr::group_by(banner) %>% 
    dplyr::summarise(impressions = sum(recordImpressionSampleRate))
  
  # - add cetDay, me
  bannerObservations$date <- cetDay
  bannerObservations$campaign <- campaignName
  
  # - store:
  write.csv(bannerObservations, 
            paste0("bannerImpressionsAggregated_",
                   cetDay,
                   ".csv"
            )
  )
  
}

# - wrangle Banner Impression data
campaignName <- "2019_AuBC"
wmde_process_banner_impressions(fileName = fileName, 
                                dataDir = dataDir, 
                                cetDay = cetDay,
                                campaignName = campaignName)

### ----------------------------------------------------------
### --- 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_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, "'")
  }
  
  # - 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, "'")
  }
  
  # - compose HiveQL query
  hiveQuery <- paste0(
    "USE wmf;
    SELECT 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:
  hiveArgs <- '/usr/local/bin/beeline -f'
  hiveInput <- paste0(queryFile, ' > ', fileName)
  # - command:
  hiveCommand <- paste(hiveArgs, hiveInput)
  return(
    system(command = hiveCommand, wait = TRUE)
  )
}

# - set params to wmde_collect_pageviews
# - for the Autumn Banner Campaign 2019
uri_host <- c('de.wikipedia.org', 'de.m.wikipedia.org')
uri_path  <- c(
  '/wiki/Wikipedia:Wikipedia_vor_Ort',
  '/wiki/Wikipedia:Kontor_Hamburg/Aktionstag_2019',
  '/wiki/Wikipedia:WikiWedding/Wikipedia_vor_Ort_2019',
  '/wiki/Wikipedia:WikiB%C3%A4r/Aktionstag_Wikipedia_2019',
  '/wiki/Wikipedia:Ruhrgebiet/Wikipedia_vor_Ort_2019',
  '/wiki/Wikipedia:K%C3%B6ln/Aktionstag_Wikipedia_2019',
  '/wiki/Wikipedia:Hannover/Aktionstag_Wikipedia_2019',
  '/wiki/Wikipedia:Frankfurt/Wikipedia_vor_Ort_2019',
  '/wiki/Wikipedia:Augsburg/Aktionstag_Wikipedia_2019',
  '/wiki/Wikipedia:WikiMUC/2019-11-10_Wikipedia_vor_Ort',
  '/wiki/Wikipedia:Freiburg_im_Breisgau/Wikipedia_vor_Ort_2019',
  '/wiki/Wikipedia:L%C3%B6rrach/Aktionstag_Wikipedia_2019',
  '/wiki/Wikipedia:Bodensee/Wikipedia_vor_Ort_2019',
  '/wiki/Wikipedia:Ober%C3%B6sterreich/Wikipedia_vor_Ort_2019',
  '/wiki/Wikipedia:Wien/Wikipedia_vor_Ort_2019',
  '/wiki/Wikipedia:Nieder%C3%B6sterreich/Aktionstag_Wikipedia_vor_Ort_2019',
  '/wiki/Wikipedia:Wikimedia_Deutschland/LerneWikipedia')

# - set params for wmde_collect_pageviews
queryFile <- 'abc2019_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, 
                                   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_path", pageviewsData))
  pageviewsData <- pageviewsData[(wStart + 2):(length(pageviewsData) - 2)]
  pageviewsData <- data.frame(dat = pageviewsData, 
                              stringsAsFactors = F)
  pageviewsData <- separate(pageviewsData,
                            dat,
                            into = c('uri_path', 'uri_query', 'referer'),
                            sep = "\t")
  # - apply uri_query_filter
  # - NOTE: Autumn 2019, looking in both: uri_query, referer
  w_uri_query <- which(grepl(uri_query_filter, pageviewsData$uri_query))
  w_uri_query_referer <- which(grepl(uri_query_filter, pageviewsData$referer))
  w_uri_query <- unique(c(w_uri_query, w_uri_query_referer))
  pageviewsData <- pageviewsData[w_uri_query, ]
  w_uri_query_referer <- which(grepl(uri_query_filter, pageviewsData$referer))
  w_uri_query_referer_delete <- setdiff(1:dim(pageviewsData)[1], w_uri_query_referer)
  pageviewsData$referer[w_uri_query_referer_delete] <- ''
  # - when there is no uri_query, use the query from the referer field if present there
  pageviewsData$referer <- str_extract(pageviewsData$referer, "\\?campaign=.*$")
  pageviewsData$referer[is.na(pageviewsData$referer)] <- ""
  pageviewsData$referer <- gsub("?campaign=", "", pageviewsData$referer, fixed = T)
  pageviewsData$uri_query <- gsub("?campaign=", "", pageviewsData$uri_query, fixed = T)
  pageviewsData$uri_query[pageviewsData$uri_query == ""] <- 
    pageviewsData$referer[pageviewsData$uri_query == ""]
  pageviewsData <- dplyr::filter(pageviewsData, 
                                 uri_query != "")
  pageviewsData$referer <- NULL
  # - clean up a bit:
  pageviewsData$uri_query <- gsub("/.*$", "", pageviewsData$uri_query)
  
  # - aggregate:
  pageviewsData <- pageviewsData %>% 
    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("pageviewsAggregated_",
                   strsplit(
                     strsplit(fileName, split = "_", fixed = T)[[1]][2],
                     split = ".", 
                     fixed = T)[[1]][1],
                   ".csv"
            )
  )
  
}

# - set params to wmde_process_pageviews
# - for the Autumn Banner Campaign 2019
uri_query_filter <- 'WMDE_neweditors_autumn_2019'

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

### ----------------------------------------------------------
### --- User Registrations
### --- HiveQL query: event.ServerSideAccountCreation table
### ----------------------------------------------------------
hiveQL <- 
  "SELECT year, month, day, hour,  event.campaign, event.userId, event.userName 
    FROM event.serversideaccountcreation 
    WHERE 
      year = 2019 
      AND month >= 11 
      AND day >= 1 
      AND (event.campaign LIKE '%WMDE_neweditors_autumn_2019%');"
# - write hql
write(hiveQL, paste0(dataDir, 'user_registrations.hql'))
### --- output filename
filename <- paste0(dataDir, 'user_registrations.tsv')
### --- execute hql script:
hiveArgs <- '/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)

0.2 Data Aggregation

NOTE: Not run from this report; the data were already pre-processed and aggregated by the following R script before being submitted to analytical procedures:

# !diagnostics off
### --- script: ABC_2019_Aggregation.R
### --- Data Aggregation for the Autumn Banner Campaign 2019
### --- run from stat1004
### --- /home/goransm/Analytics/NewEditors/Campaigns/2019AuBC/

### --- dir structure
campaignPath <- '/home/goransm/Analytics/NewEditors/Campaigns/2019AuBC/'
dataDir <- paste0(campaignPath, "_data/")
analyticsDir <- paste0(campaignPath, "_analytics/")

### --- Report Banner Impression Data

# - function: wmde_report_banner_impressions
wmde_report_banner_impressions <- function(dataDir) {
  
  # - Setup
  library(data.table)
  library(dplyr)
  
  # - list files:
  lF <- list.files(dataDir)
  
  # - filter aggregated banner impression data
  lF <- lF[grepl("bannerImpressionsAggregated_", lF, fixed = T)]
  
  # - load files and merge
  bannerData <- vector(mode = "list", length = length(lF))
  for (i in 1:length(lF)) {
    if (grepl("csv$|tsv$", lF[i])) {
      bannerData[[i]] <- fread(paste0(dataDir, lF[i]))
    } else {
      bannerData[[i]] <- NULL
    }
  }
  bannerData <- rbindlist(bannerData)
  bannerData$V1 <- NULL
  
  # - aggregates
  perBannerTotals <- bannerData %>% 
    select(banner, impressions) %>% 
    group_by(banner) %>% 
    summarise(totalImpressions = sum(impressions))
  perDayTotals <- bannerData %>% 
    select(date, impressions) %>% 
    group_by(date) %>% 
    summarise(totalImpressions = sum(impressions))
  
  # - output
  return(
    list(bannerImpressionsReport = bannerData, 
         perBannerTotals = perBannerTotals, 
         perDayTotals = perDayTotals)
  )
  
}

# - Report banner impressions
bannerImpressionsData <- wmde_report_banner_impressions(dataDir)
bannerImpressionsFile <- bannerImpressionsData$bannerImpressionsReport
write.csv(bannerImpressionsFile, paste0(analyticsDir, "bannerImpressionsFile.csv"))
bannerTotals <- bannerImpressionsData$perBannerTotals
write.csv(bannerTotals, paste0(analyticsDir, "bannerTotals.csv"))
bannerDayTotals <- bannerImpressionsData$perDayTotals
write.csv(bannerDayTotals, paste0(analyticsDir, "bannerDayTotals.csv"))


### --- Report Pageviews Data

# - function: wmde_report_pageviews
wmde_report_pageviews <- function(dataDir) {
  
  # - Setup
  library(data.table)
  library(dplyr)
  
  # - list files:
  lF <- list.files(dataDir)
  
  # - filter aggregated banner impression data
  lF <- lF[grepl("pageviewsAggregated_", lF, fixed = T)]
  
  # - load files and merge
  pageviewsData <- vector(mode = "list", length = length(lF))
  for (i in 1:length(lF)) {
    if (grepl("csv$|tsv$", lF[i])) {
      pageviewsData[[i]] <- fread(paste0(dataDir, lF[i]))
    } else {
      pageviewsData[[i]] <- NULL
    }
  }
  pageviewsData <- rbindlist(pageviewsData)
  pageviewsData$V1 <- NULL
  
  # - aggregates
  perDayTotals <- pageviewsData %>% 
    select(date, Pageviews) %>% 
    group_by(date) %>% 
    summarise(totalPageviews = sum(Pageviews))
  perTagTotals <- pageviewsData %>% 
    select(Tag, Pageviews) %>% 
    group_by(Tag) %>% 
    summarise(totalPageviews = sum(Pageviews))
  perPageTotals <- pageviewsData %>% 
    select(Page, Pageviews) %>% 
    group_by(Page) %>% 
    summarise(totalPageviews = sum(Pageviews))
  perPageDayTotals <- pageviewsData %>% 
    select(Page, date, Pageviews) %>%
    group_by(Page, date) %>% 
    summarise(totalPageviews = sum(Pageviews))
  perTagDayTotals <- pageviewsData %>% 
    select(Tag, date, Pageviews) %>%
    group_by(Tag, date) %>% 
    summarise(totalPageviews = sum(Pageviews))
  perTagPageTotals <- pageviewsData %>% 
    select(Tag, Page, Pageviews) %>%
    group_by(Tag, Page) %>% 
    summarise(totalPageviews = sum(Pageviews))
  
  # - output
  return(
    list(pageviewsDataReport = pageviewsData, 
         perDayTotals = perDayTotals, 
         perTagTotals = perTagTotals, 
         perPageTotals = perPageTotals, 
         perPageDayTotals = perPageDayTotals, 
         perTagDayTotals = perTagDayTotals,
         perTagPageTotals = perTagPageTotals)
  )
  
}

# - Report pageviews:
pageviewsData <- wmde_report_pageviews(dataDir)
pageviewsReportFile <- pageviewsData$pageviewsDataReport
write.csv(pageviewsReportFile, paste0(analyticsDir, "pageviewsReportFile.csv"))
pageviews_perDayTotals <- pageviewsData$perDayTotals
write.csv(pageviews_perDayTotals, paste0(analyticsDir, "pageviews_perDayTotals.csv"))
pageviews_perTagTotals <- pageviewsData$perTagTotals
write.csv(pageviews_perTagTotals, paste0(analyticsDir, "pageviews_perTagTotals.csv"))
pageviews_perPageTotals <- pageviewsData$perPageTotals
write.csv(pageviews_perPageTotals, paste0(analyticsDir, "pageviews_perPageTotals.csv"))
pageviews_perPageDayTotals <- pageviewsData$perPageDayTotals
write.csv(pageviews_perPageDayTotals, paste0(analyticsDir, "pageviews_perPageDayTotals.csv"))
pageviews_perTagDayTotals <- pageviewsData$perTagDayTotals
write.csv(pageviews_perTagDayTotals, paste0(analyticsDir, "pageviews_perTagDayTotals.csv"))
pageviews_perTagPageTotals <- pageviewsData$perTagPageTotals
write.csv(pageviews_perTagPageTotals, paste0(analyticsDir, "perTagPageTotals.csv"))

### --- Report User Registrations

# - function: wmde_report_registrations_hive
wmde_report_registrations_hive <- function(dataDir, campaign) {
  
  # - Setup
  library(data.table)
  library(dplyr)
  
  # - list files:
  lF <- list.files(dataDir)
  
  # - filter aggregated user registration data
  lF <- lF[grepl("user_registrations.tsv", lF, fixed = T)]
  
  # - load file and wrangle columns
  registrationData <- fread(paste0(dataDir, lF), header = T)
  colnames(registrationData)[5] <- 'event_campaign'
  colnames(registrationData)[6] <- 'event_userId'
  registrationData$campaign <- campaign
  registrationData$day <- ifelse(nchar(registrationData$day) == 1,
                                 paste0("0", registrationData$day), 
                                 registrationData$day)
  registrationData$date <- paste(registrationData$year, 
                                 registrationData$month,
                                 registrationData$day,
                                 sep = "-")

  # - aggregates
  perDayTotals <- registrationData %>% 
    select(date) %>% 
    group_by(date) %>% 
    summarise(totalRegistrations = n())
  perTagTotals <- registrationData %>% 
    select(event_campaign) %>% 
    group_by(event_campaign) %>% 
    summarise(totalRegistrations = n())
  perTagDayTotals <- registrationData %>% 
    select(event_campaign, date) %>% 
    group_by(event_campaign, date) %>% 
    summarise(totalRegistrations = n())
  
  # - full registration dataset: non-aggregated registrations
  # - filter data:
  fullRegData <- registrationData %>% 
    select(event_userId, 
           event_campaign, 
           date, 
           campaign)
  
  # - output
  return(
    list(registrationsDataReport = registrationData, 
         perDayTotals = perDayTotals, 
         perTagTotals = perTagTotals, 
         perTagDayTotals = perTagDayTotals,
         fullRegistrationDataset = fullRegData)
  )
  
}

# - Report upon user registrations
campaign <- '2019_AuBC'
userRegData <- wmde_report_registrations_hive(dataDir, campaign)
userRegistrationsReportFile <- userRegData$registrationsDataReport
write.csv(userRegistrationsReportFile, paste0(analyticsDir, "userRegistrationsReportFile.csv"))
userRegistrations_perDayTotals <- userRegData$perDayTotals
write.csv(userRegistrations_perDayTotals, paste0(analyticsDir, "userRegistrations_perDayTotals.csv"))
userRegistrations_perTagTotals <- userRegData$perTagTotals
write.csv(userRegistrations_perTagTotals, paste0(analyticsDir, "userRegistrations_perTagTotals.csv"))
userRegistrations_perTagDayTotals <- userRegData$perTagDayTotals
write.csv(userRegistrations_perTagDayTotals, paste0(analyticsDir, "userRegistrations_perTagDayTotals.csv"))
fullUserRegistrations <- userRegData$fullRegistrationDataset
write.csv(fullUserRegistrations, paste0(analyticsDir, "fullRegistrationDataset.csv"))

1. Campaign Banners and Pages

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

1.2 Pageviews

1.2.1 Pageviews Overview

Chart 1.2.1. Pageviews Overview. Log scaling of the pageviews is necessary; the numbers reported in the data point labels are exact.

1.2.1 Pageviews Overview: Table

Table 1.2.1. Pageviews Overview

### --- Full Dataset (Table Report)
datatable(dataSet %>% arrange(desc(totalPageviews)))

1.2.2 Pageviews Overview: totals per Page

Chart 1.2.2. Pageviews Overview: totals per Page

1.2.3 Pageviews Overview: totals per day

Chart 1.2.3. Pageviews Overview: totals per day

1.2.3 Pageviews Overview: totals per Tag/Page

Chart 1.2.3. Pageviews Overview: totals per Tag/Page

1.2.4 Pageviews per Tag, daily totals

Chart 1.2.4. Pageviews per Tag, daily totals. Log scaling of the pageviews is necessary; the numbers reported in the data point labels are exact.

dataSet <- read.csv(
  '_analytics/pageviews_perTagDayTotals.csv',
                    header = T,
                    row.names = 1,
                    check.names = F,
                    stringsAsFactors = F)
campaignTags <- c('WMDE_neweditors_autumn_2019_nl_lp1',
                  'WMDE_neweditors_autumn_2019_nl_lp2',
                  'WMDE_neweditors_autumn_2019_flyer',
                  'WMDE_neweditors_autumn_2019_bnr')
dataSet <- filter(dataSet, 
                  grepl(campaignTags[1], dataSet$Tag)| 
                    grepl(campaignTags[2], dataSet$Tag)| 
                    grepl(campaignTags[3], dataSet$Tag)| 
                    grepl(campaignTags[4], dataSet$Tag))
dataSet$Tag[grepl(campaignTags[1], dataSet$Tag, fixed = T)] <- "nl_lp1"
dataSet$Tag[grepl(campaignTags[2], dataSet$Tag, fixed = T)] <- "nl_lp2"
dataSet$Tag[grepl(campaignTags[3], dataSet$Tag, fixed = T)] <- "flyer"
dataSet$Tag[grepl(campaignTags[4], dataSet$Tag, fixed = T)] <- "bnr"
dataSet <- dataSet %>% 
  group_by(Tag, date) %>% 
  summarise(totalPageviews = sum(totalPageviews))
ggplot(dataSet, aes(x = date,
                    y = log10(totalPageviews),
                    group = Tag,
                    color = Tag,
                    fill = Tag,
                    label = totalPageviews,
                    )) + 
  geom_path(size = .25) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, color = "white") +
  scale_y_continuous(labels = comma) +
  ggtitle('Autumn Banner Campaign 2019: Pageviews per Tag, daily totals') +
  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 = "top")

2. User Registrations

All data on user registrations are presented in this section.

2.1 Registrations per tag and day

Chart 2.1. Registrations per tag and day. Please note: points with no data labels signify 0 user registrations.

LS0tCnRpdGxlOiAnV01ERSBBdXR1bW4gQmFubmVyIENhbXBhaWduIDIwMTk6IEludGVyaW0gUmVwb3J0JwphdXRob3I6ICJHb3JhbiBTLiBNaWxvdmFub3ZpYywgRGF0YSBTY2llbnRpc3QsIFdNREUiCmRhdGU6ICJOb3ZlbWJlciAxMCwgMjAxOSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRoZW1lOiBzaW1wbGV4CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogNQogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA1Ci0tLQoKKipGZWVkYmFjayoqIHNob3VsZCBiZSBzZW5kIHRvIGBnb3Jhbi5taWxvdmFub3ZpY19leHRAd2lraW1lZGlhLmRlYC4gCgpUaGUgY2FtcGFpZ24gaXMgcnVuIGZyb20gMjAxOS8xMS8wMSB0byAyMDE5LzExLzA5LgoKKipDVVJSRU5UIFVQREFURToqKiBDb21wbGV0ZSBkYXRhc2V0IGFzIG9mIDIwMTkvMTEvMDkuCgpgYGB7ciwgZWNobyA9IEYsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGLCByZXN1bHRzID0gJ2hpZGUnfQojICFkaWFnbm9zdGljcyBvZmYKIyMjIC0tLSBTZXR1cAprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQgPSA4KSAKcm0obGlzdCA9IGxzKCkpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkocm1hcmtkb3duKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KHJlc2hhcGUyKQpgYGAKCiMjIDAuIERhdGEgQWNxdWlzaXRvbgoKKipOT1RFOioqIHRoZSBEYXRhIEFjcXVpc2l0aW9uIGNvZGUgY2h1bmsgaXMgbm90IGZ1bGx5IHJlcHJvZHVjaWJsZSBmcm9tIHRoaXMgUmVwb3J0LiBUaGUgZGF0YSBhcmUgY29sbGVjdGVkIGJ5IHJ1bm5pbmcgdGhlIHNjcmlwdCBgQUJDXzIwMTlfUFJPRFVDVElPTi5SYCBmcm9tIHRoZSBgc3RhdDEwMDRgIFdNRiBzdGF0aXN0aWNzIHNlcnZlciwgY29sbGVjdGluZyB0aGUgZGF0YSBhcyBgLnRzdmAgYW5kIGAuY3N2YCBmaWxlcywgYWdncmVnYXRpbmcgb24gdGhlIHNlcnZlciB3aXRoIGBBQkNfMjAxOV9BZ2dyZWdhdGlvbi5SYCwgY29weWluZyBtYW51YWxseSBhbmQgcHJvY2Vzc2luZyBsb2NhbGx5LiAKCiMjIyAwLjEgRGFpbHkgVXBkYXRlCgpgYGB7ciwgZXZhbCA9IEZ9CiMgIWRpYWdub3N0aWNzIG9mZgojIyMgLS0tIHNjcmlwdDogQUJDXzIwMTlfUFJPRFVDVElPTi5SCiMjIyAtLS0gRGF0YSBBY3F1aXNpdGlvbiBmb3IgdGhlIEF1dHVtbiBCYW5uZXIgQ2FtcGFpZ24gMjAxOQojIyMgLS0tIHJ1biBmcm9tIHN0YXQxMDA0CiMjIyAtLS0gL2hvbWUvZ29yYW5zbS9BbmFseXRpY3MvTmV3RWRpdG9ycy9DYW1wYWlnbnMvMjAxOUF1QkMvCgojIyMgLS0tIGxpYnJhcmllcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKCiMjIyAtLS0gZGlyIHN0cnVjdHVyZQpjYW1wYWlnblBhdGggPC0gJy9ob21lL2dvcmFuc20vQW5hbHl0aWNzL05ld0VkaXRvcnMvQ2FtcGFpZ25zLzIwMTlBdUJDLycKZGF0YURpciA8LSBwYXN0ZTAoY2FtcGFpZ25QYXRoLCAiX2RhdGEvIikKYW5hbHl0aWNzRGlyIDwtIHBhc3RlMChjYW1wYWlnblBhdGgsICJfYW5hbHl0aWNzLyIpCgojIyMgLS0tIGRldGVybWluZSBjZXREYXkKY2V0RGF5IDwtIFN5cy50aW1lKCkKY2V0RGF5CmF0dHIoY2V0RGF5LCAidHpvbmUiKSA8LSAiRXVyb3BlL0JlcmxpbiIKIyAtIG9uZSBkYXkgYmVoaW5kIGZvciBjcm9udGFiCiMgLSAoaS5lLiB3YWl0aW5nIGZvciB3bWYud2VicmVxdWVzdCB0byBjb21wbGV0ZSBpcyBkYXRhIGFjcXVpc2l0aW9uKQpjZXREYXkgPC0geW1kKAogIHN0cnNwbGl0KGFzLmNoYXJhY3RlcihjZXREYXkpLCAKICAgICAgICAgICBzcGxpdCA9ICIgIiwgCiAgICAgICAgICAgZml4ZWQgPSBUKVtbMV1dWzFdCikgLSAxCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIEJhbm5lciBJbXByZXNzaW9ucwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIGZ1bmN0aW9uOiB3bWRlX2NvbGxlY3RfYmFubmVyX2ltcHJlc3Npb25zCndtZGVfY29sbGVjdF9iYW5uZXJfaW1wcmVzc2lvbnMgPC0gZnVuY3Rpb24odXJpX2hvc3QsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9wYXRoLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcXVlcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpcikgewogIAogICMgLSBOT1RFOgogICMgLSBleHBlY3RlZCBmb3JtYXQgZm9yIGNldERheSBpczogWVlZWS1NTS1ERAogIAogICMgLSB0byBkYXRhRGlyCiAgc2V0d2QoZGF0YURpcikKICAKICAjIC0gbGlicmFyaWVzCiAgbGlicmFyeShzdHJpbmdyKQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSBkYXRldGltZV9jb25kaXRpb24KICBjZXRfY29uZGl0aW9uIDwtIHNlcSgKICAgIGZyb20gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAwOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIHRvID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMjM6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgYnkgPSAiaG91ciIKICApIAogIGF0dHIoY2V0X2NvbmRpdGlvbiwgInR6b25lIikgPC0gIlVUQyIKICBjZXRfY29uZGl0aW9uIDwtIGFzLmNoYXJhY3RlcihjZXRfY29uZGl0aW9uKQogIGNldF9jb25kaXRpb24gPC0gdW5saXN0KHN0cl9leHRyYWN0X2FsbChjZXRfY29uZGl0aW9uLCAiXihbWzpkaWdpdDpdXXxcXHN8LSkqIikpCiAgY2V0X3llYXJzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzFdCiAgICB9KQogIGNldF9tb250aHMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMl0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfbW9udGhzKQogIGNldF9kYXlzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzNdCiAgICB9KQogIGNldF9kYXlzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9kYXlzKQogIGNldF9ob3VycyA8LSBzYXBwbHkoc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIAogICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICB4WzJdCiAgICAgICAgICAgICAgICAgICAgICB9KQogIGNldF9ob3VycyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfaG91cnMpCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUwKAogICAgInllYXIgPSAiLCBjZXRfeWVhcnMsICIgQU5EICIsCiAgICAibW9udGggPSAiLCBjZXRfbW9udGhzLCAiIEFORCAiLAogICAgImRheSA9ICIsIGNldF9kYXlzLCAiIEFORCAiLCAKICAgICJob3VyID0gIiwgY2V0X2hvdXJzCiAgKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlKCIoIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZXRpbWVDb25kaXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpCiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9wYXRoX2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX3BhdGgpID4gMSkgewogICAgdXJpX3BhdGhfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgidXJpX3BhdGggPSAnIiwgdXJpX3BhdGgsICInIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICB1cmlfcGF0aF9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpCiAgfQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSB1cmlfaG9zdF9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9ob3N0KSA+IDEpIHsKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9ob3N0ID0gJyIsIHVyaV9ob3N0LCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX2hvc3RfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfaG9zdCA9ICciLCB1cmlfaG9zdCwgIiciKQogIH0KICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX3F1ZXJ5X2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX3F1ZXJ5KSA+IDEpIHsKICAgIHVyaV9xdWVyeV9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9xdWVyeSBMSUtFICclIiwgdXJpX3F1ZXJ5LCAiJSciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX3F1ZXJ5X2NvbmRpdGlvbiA9IHBhc3RlMCgidXJpX3F1ZXJ5IExJS0UgJyUiLCB1cmlfcXVlcnksICIlJyIpCiAgfQogIAogICMgLSBjb21wb3NlIEhpdmVRTCBxdWVyeQogIGhpdmVRdWVyeSA8LSBwYXN0ZTAoCiAgICAiVVNFIHdtZjsKICAgIFNFTEVDVCB1cmlfcXVlcnkgRlJPTSB3ZWJyZXF1ZXN0CiAgICBXSEVSRSAoIiwKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiwgIiBBTkQgIiwKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiwgIiBBTkQgIiwKICAgIHVyaV9xdWVyeV9jb25kaXRpb24sICIgQU5EICIsCiAgICAiKCIsIGRhdGV0aW1lQ29uZGl0aW9uLCAiKSIsCiAgICAiKTsiCiAgICApCiAgCiAgIyAtIHdyaXRlIGhxbAogIHdyaXRlKGhpdmVRdWVyeSwgcXVlcnlGaWxlKQogICMgLSBleGVjdXRlIGhxbCBzY3JpcHQ6CiAgaGl2ZUFyZ3MgPC0gJy91c3IvbG9jYWwvYmluL2JlZWxpbmUgLWYnCiAgaGl2ZUlucHV0IDwtIHBhc3RlMChxdWVyeUZpbGUsICcgPiAnLCBmaWxlTmFtZSkKICAjIC0gY29tbWFuZDoKICBoaXZlQ29tbWFuZCA8LSBwYXN0ZShoaXZlQXJncywgaGl2ZUlucHV0KQogIHJldHVybigKICAgIHN5c3RlbShjb21tYW5kID0gaGl2ZUNvbW1hbmQsIHdhaXQgPSBUUlVFKQogICkKfQoKIyAtIHNldCBwYXJhbXMgdG8gd21kZV9jb2xsZWN0X2Jhbm5lcl9pbXByZXNzaW9ucwojIC0gZm9yIHRoZSBBdXR1bW4gQmFubmVyIENhbXBhaWduIDIwMTkKdXJpX2hvc3QgPC0gYygnZGUud2lraXBlZGlhLm9yZycsICdkZS5tLndpa2lwZWRpYS5vcmcnKQp1cmlfcGF0aCAgPC0gJy9iZWFjb24vaW1wcmVzc2lvbicKdXJpX3F1ZXJ5IDwtIGMoJ1dNREVfbmV3ZWRpdG9yc19hdXR1bW5fMjAxOScpCnF1ZXJ5RmlsZSA8LSAnYWJjMjAxOV9CYW5uZXJJbXByZXNzaW9ucy5ocWwnCmZpbGVOYW1lIDwtIHBhc3RlMChkYXRhRGlyLCAiYmFubmVySW1wcmVzc2lvbnNfIiwgY2V0RGF5LCAiLnRzdiIpCgojIC0gY29sbGVjdCBCYW5uZXIgSW1wcmVzc2lvbiBkYXRhCndtZGVfY29sbGVjdF9iYW5uZXJfaW1wcmVzc2lvbnModXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpcikKCiMgLSBmdW5jdGlvbjogd21kZV9wcm9jZXNzX2Jhbm5lcl9pbXByZXNzaW9ucwp3bWRlX3Byb2Nlc3NfYmFubmVyX2ltcHJlc3Npb25zIDwtIGZ1bmN0aW9uKGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lKSB7CiAgCiAgIyAtIHRvIGRhdGFEaXIKICBzZXR3ZChkYXRhRGlyKQogIAogICMgLSBsaWJyYXJpZXMKICBsaWJyYXJ5KHN0cmluZ3IpCiAgbGlicmFyeShkcGx5cikKICAKICAjIC0gbG9hZAogIGJhbm5lckRhdGEgPC0gcmVhZC5kZWxpbShmaWxlTmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIGNvbG5hbWVzKGJhbm5lckRhdGEpIDwtICd1cmlfcXVlcnknCiAgCiAgIyAtIGNsZWFuCiAgd1N0YXJ0IDwtIHdoaWNoKGJhbm5lckRhdGEkdXJpX3F1ZXJ5ID09ICJ1cmlfcXVlcnkiKQogIGJhbm5lckRhdGEgPC0gYmFubmVyRGF0YVsod1N0YXJ0ICsgMSk6KGRpbShiYW5uZXJEYXRhKVsxXSAtIDIpLCBdCiAgCiAgIyAtIHNwbGl0CiAgYmFubmVyRGF0YSA8LSBzdHJzcGxpdChiYW5uZXJEYXRhLCBzcGxpdCA9ICImIiwgZml4ZWQgPSBUKQogICMgLSBleHRyYWN0IHJlbGV2YW50IGZpZWxkcwogICMgLSBiYW5uZXI6CiAgYmFubmVyIDwtIHNhcHBseShiYW5uZXJEYXRhLCBmdW5jdGlvbih4KSB7CiAgICB4W3doaWNoKGdyZXBsKCJeYmFubmVyPSIsIHgpKV0KICB9KQogIGJhbm5lciA8LSBnc3ViKCJeYmFubmVyPSIsICIiLCBiYW5uZXIpCiAgIyAtIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlOgogIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlIDwtIHNhcHBseShiYW5uZXJEYXRhLCBmdW5jdGlvbih4KSB7CiAgICB4W3doaWNoKGdyZXBsKCJecmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGU9IiwgeCkpXQogIH0pCiAgcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUgPC0gYXMubnVtZXJpYygKICAgIGdzdWIoIl5yZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZT0iLCAiIiwgcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUpCiAgKQogICMgLSByZXN1bHQ6CiAgcmVzdWx0IDwtIHNhcHBseShiYW5uZXJEYXRhLCBmdW5jdGlvbih4KSB7CiAgICB4W3doaWNoKGdyZXBsKCJecmVzdWx0PSIsIHgpKV0KICB9KQogIHJlc3VsdCA8LSBnc3ViKCJecmVzdWx0PSIsICIiLCByZXN1bHQpCiAgCiAgIyAtIGNvbXBvc2UgdGFibGU6CiAgYmFubmVyT2JzZXJ2YXRpb25zIDwtIGRhdGEuZnJhbWUoYmFubmVyID0gYmFubmVyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSA9IHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSByZXN1bHQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIAogICMgLSBmaWx0ZXIgZm9yIHJlc3VsdD1zaG93CiAgYmFubmVyT2JzZXJ2YXRpb25zIDwtIGRwbHlyOjpmaWx0ZXIoYmFubmVyT2JzZXJ2YXRpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9PSAic2hvdyIpCiAgCiAgIyAtIGNvcnJlY3Rpb24gZm9yIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlCiAgYmFubmVyT2JzZXJ2YXRpb25zJHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlIDwtIAogICAgMS9iYW5uZXJPYnNlcnZhdGlvbnMkcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUKICAKICAjIC0gYWdncmVnYXRlOgogIGJhbm5lck9ic2VydmF0aW9ucyA8LSBiYW5uZXJPYnNlcnZhdGlvbnMgJT4lIAogICAgZHBseXI6OnNlbGVjdChiYW5uZXIsIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlKSAlPiUgCiAgICBkcGx5cjo6Z3JvdXBfYnkoYmFubmVyKSAlPiUgCiAgICBkcGx5cjo6c3VtbWFyaXNlKGltcHJlc3Npb25zID0gc3VtKHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlKSkKICAKICAjIC0gYWRkIGNldERheSwgbWUKICBiYW5uZXJPYnNlcnZhdGlvbnMkZGF0ZSA8LSBjZXREYXkKICBiYW5uZXJPYnNlcnZhdGlvbnMkY2FtcGFpZ24gPC0gY2FtcGFpZ25OYW1lCiAgCiAgIyAtIHN0b3JlOgogIHdyaXRlLmNzdihiYW5uZXJPYnNlcnZhdGlvbnMsIAogICAgICAgICAgICBwYXN0ZTAoImJhbm5lckltcHJlc3Npb25zQWdncmVnYXRlZF8iLAogICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICkKICApCiAgCn0KCiMgLSB3cmFuZ2xlIEJhbm5lciBJbXByZXNzaW9uIGRhdGEKY2FtcGFpZ25OYW1lIDwtICIyMDE5X0F1QkMiCndtZGVfcHJvY2Vzc19iYW5uZXJfaW1wcmVzc2lvbnMoZmlsZU5hbWUgPSBmaWxlTmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpciA9IGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUgPSBjYW1wYWlnbk5hbWUpCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIFBhZ2V2aWV3cwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIGZ1bmN0aW9uOiB3bWRlX2NvbGxlY3RfcGFnZXZpZXdzCndtZGVfY29sbGVjdF9wYWdldmlld3MgPC0gZnVuY3Rpb24odXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyKSB7CiAgCiAgIyAtIE5PVEU6CiAgIyAtIGV4cGVjdGVkIGZvcm1hdCBmb3IgY2V0RGF5IGlzOiBZWVlZLU1NLURECiAgCiAgIyAtIHRvIGRhdGFEaXIKICBzZXR3ZChkYXRhRGlyKQogIAogICMgLSBsaWJyYXJpZXMKICBsaWJyYXJ5KHN0cmluZ3IpCiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIGRhdGV0aW1lX2NvbmRpdGlvbgogIGNldF9jb25kaXRpb24gPC0gc2VxKAogICAgZnJvbSA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDA6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgdG8gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAyMzowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICBieSA9ICJob3VyIgogICkgCiAgYXR0cihjZXRfY29uZGl0aW9uLCAidHpvbmUiKSA8LSAiVVRDIgogIGNldF9jb25kaXRpb24gPC0gYXMuY2hhcmFjdGVyKGNldF9jb25kaXRpb24pCiAgY2V0X2NvbmRpdGlvbiA8LSB1bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKGNldF9jb25kaXRpb24sICJeKFtbOmRpZ2l0Ol1dfFxcc3wtKSoiKSkKICBjZXRfeWVhcnMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMV0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsyXQogICAgfSkKICBjZXRfbW9udGhzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9tb250aHMpCiAgY2V0X2RheXMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bM10KICAgIH0pCiAgY2V0X2RheXMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2RheXMpCiAgY2V0X2hvdXJzIDwtIHNhcHBseShzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgCiAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHhbMl0KICAgICAgICAgICAgICAgICAgICAgIH0pCiAgY2V0X2hvdXJzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9ob3VycykKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZTAoCiAgICAieWVhciA9ICIsIGNldF95ZWFycywgIiBBTkQgIiwKICAgICJtb250aCA9ICIsIGNldF9tb250aHMsICIgQU5EICIsCiAgICAiZGF5ID0gIiwgY2V0X2RheXMsICIgQU5EICIsIAogICAgImhvdXIgPSAiLCBjZXRfaG91cnMKICApCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUoIigiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZUNvbmRpdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX3BhdGhfY29uZGl0aW9uCiAgaWYgKGxlbmd0aCh1cmlfcGF0aCkgPiAxKSB7CiAgICB1cmlfcGF0aF9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfcGF0aCA9ICciLCB1cmlfcGF0aCwgIiciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiA9IHBhc3RlMCgidXJpX3BhdGggPSAnIiwgdXJpX3BhdGgsICInIikKICB9CiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9ob3N0X2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX2hvc3QpID4gMSkgewogICAgdXJpX2hvc3RfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgidXJpX2hvc3QgPSAnIiwgdXJpX2hvc3QsICInIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9ob3N0ID0gJyIsIHVyaV9ob3N0LCAiJyIpCiAgfQogIAogICMgLSBjb21wb3NlIEhpdmVRTCBxdWVyeQogIGhpdmVRdWVyeSA8LSBwYXN0ZTAoCiAgICAiVVNFIHdtZjsKICAgIFNFTEVDVCB1cmlfcGF0aCwgdXJpX3F1ZXJ5LCByZWZlcmVyIEZST00gd2VicmVxdWVzdAogICAgV0hFUkUgKCIsCiAgICB1cmlfaG9zdF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcGF0aF9jb25kaXRpb24sICIgQU5EICIsCiAgICAiKCIsIGRhdGV0aW1lQ29uZGl0aW9uLCAiKSIsCiAgICAiKTsiCiAgICApCiAgCiAgIyAtIHdyaXRlIGhxbAogIHdyaXRlKGhpdmVRdWVyeSwgcXVlcnlGaWxlKQogICMgLSBleGVjdXRlIGhxbCBzY3JpcHQ6CiAgaGl2ZUFyZ3MgPC0gJy91c3IvbG9jYWwvYmluL2JlZWxpbmUgLWYnCiAgaGl2ZUlucHV0IDwtIHBhc3RlMChxdWVyeUZpbGUsICcgPiAnLCBmaWxlTmFtZSkKICAjIC0gY29tbWFuZDoKICBoaXZlQ29tbWFuZCA8LSBwYXN0ZShoaXZlQXJncywgaGl2ZUlucHV0KQogIHJldHVybigKICAgIHN5c3RlbShjb21tYW5kID0gaGl2ZUNvbW1hbmQsIHdhaXQgPSBUUlVFKQogICkKfQoKIyAtIHNldCBwYXJhbXMgdG8gd21kZV9jb2xsZWN0X3BhZ2V2aWV3cwojIC0gZm9yIHRoZSBBdXR1bW4gQmFubmVyIENhbXBhaWduIDIwMTkKdXJpX2hvc3QgPC0gYygnZGUud2lraXBlZGlhLm9yZycsICdkZS5tLndpa2lwZWRpYS5vcmcnKQp1cmlfcGF0aCAgPC0gYygKICAnL3dpa2kvV2lraXBlZGlhOldpa2lwZWRpYV92b3JfT3J0JywKICAnL3dpa2kvV2lraXBlZGlhOktvbnRvcl9IYW1idXJnL0FrdGlvbnN0YWdfMjAxOScsCiAgJy93aWtpL1dpa2lwZWRpYTpXaWtpV2VkZGluZy9XaWtpcGVkaWFfdm9yX09ydF8yMDE5JywKICAnL3dpa2kvV2lraXBlZGlhOldpa2lCJUMzJUE0ci9Ba3Rpb25zdGFnX1dpa2lwZWRpYV8yMDE5JywKICAnL3dpa2kvV2lraXBlZGlhOlJ1aHJnZWJpZXQvV2lraXBlZGlhX3Zvcl9PcnRfMjAxOScsCiAgJy93aWtpL1dpa2lwZWRpYTpLJUMzJUI2bG4vQWt0aW9uc3RhZ19XaWtpcGVkaWFfMjAxOScsCiAgJy93aWtpL1dpa2lwZWRpYTpIYW5ub3Zlci9Ba3Rpb25zdGFnX1dpa2lwZWRpYV8yMDE5JywKICAnL3dpa2kvV2lraXBlZGlhOkZyYW5rZnVydC9XaWtpcGVkaWFfdm9yX09ydF8yMDE5JywKICAnL3dpa2kvV2lraXBlZGlhOkF1Z3NidXJnL0FrdGlvbnN0YWdfV2lraXBlZGlhXzIwMTknLAogICcvd2lraS9XaWtpcGVkaWE6V2lraU1VQy8yMDE5LTExLTEwX1dpa2lwZWRpYV92b3JfT3J0JywKICAnL3dpa2kvV2lraXBlZGlhOkZyZWlidXJnX2ltX0JyZWlzZ2F1L1dpa2lwZWRpYV92b3JfT3J0XzIwMTknLAogICcvd2lraS9XaWtpcGVkaWE6TCVDMyVCNnJyYWNoL0FrdGlvbnN0YWdfV2lraXBlZGlhXzIwMTknLAogICcvd2lraS9XaWtpcGVkaWE6Qm9kZW5zZWUvV2lraXBlZGlhX3Zvcl9PcnRfMjAxOScsCiAgJy93aWtpL1dpa2lwZWRpYTpPYmVyJUMzJUI2c3RlcnJlaWNoL1dpa2lwZWRpYV92b3JfT3J0XzIwMTknLAogICcvd2lraS9XaWtpcGVkaWE6V2llbi9XaWtpcGVkaWFfdm9yX09ydF8yMDE5JywKICAnL3dpa2kvV2lraXBlZGlhOk5pZWRlciVDMyVCNnN0ZXJyZWljaC9Ba3Rpb25zdGFnX1dpa2lwZWRpYV92b3JfT3J0XzIwMTknLAogICcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0xlcm5lV2lraXBlZGlhJykKCiMgLSBzZXQgcGFyYW1zIGZvciB3bWRlX2NvbGxlY3RfcGFnZXZpZXdzCnF1ZXJ5RmlsZSA8LSAnYWJjMjAxOV9QYWdldmlld3MuaHFsJwpmaWxlTmFtZSA8LSBwYXN0ZTAoInBhZ2V2aWV3c18iLCBjZXREYXksICIudHN2IikKCiMgLSBjb2xsZWN0IFBhZ2V2aWV3cyBkYXRhCndtZGVfY29sbGVjdF9wYWdldmlld3ModXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyKQoKIyMjIC0tLSBXcmFuZ2xlIFBhZ2V2aWV3cwojIC0gZnVuY3Rpb246IHdtZGVfcHJvY2Vzc19wYWdldmlld3MKd21kZV9wcm9jZXNzX3BhZ2V2aWV3cyA8LSBmdW5jdGlvbihmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcXVlcnlfZmlsdGVyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXkgPSBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lID0gY2FtcGFpZ25OYW1lKSB7CiAgCiAgIyAtIHRvIGRhdGFEaXIKICBzZXR3ZChkYXRhRGlyKQogIAogICMgLSBsaWJyYXJpZXMKICBsaWJyYXJ5KHN0cmluZ3IpCiAgbGlicmFyeShkcGx5cikKICBsaWJyYXJ5KHRpZHlyKQogIGxpYnJhcnkoZGF0YS50YWJsZSkKICAKICAjIC0gbG9hZAogIHBhZ2V2aWV3c0RhdGEgPC0gcmVhZExpbmVzKGZpbGVOYW1lKQogIHdTdGFydCA8LSB3aGljaChncmVwbCgiXnVyaV9wYXRoIiwgcGFnZXZpZXdzRGF0YSkpCiAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhWyh3U3RhcnQgKyAyKToobGVuZ3RoKHBhZ2V2aWV3c0RhdGEpIC0gMildCiAgcGFnZXZpZXdzRGF0YSA8LSBkYXRhLmZyYW1lKGRhdCA9IHBhZ2V2aWV3c0RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICBwYWdldmlld3NEYXRhIDwtIHNlcGFyYXRlKHBhZ2V2aWV3c0RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRvID0gYygndXJpX3BhdGgnLCAndXJpX3F1ZXJ5JywgJ3JlZmVyZXInKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIpCiAgIyAtIGFwcGx5IHVyaV9xdWVyeV9maWx0ZXIKICAjIC0gTk9URTogQXV0dW1uIDIwMTksIGxvb2tpbmcgaW4gYm90aDogdXJpX3F1ZXJ5LCByZWZlcmVyCiAgd191cmlfcXVlcnkgPC0gd2hpY2goZ3JlcGwodXJpX3F1ZXJ5X2ZpbHRlciwgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkpKQogIHdfdXJpX3F1ZXJ5X3JlZmVyZXIgPC0gd2hpY2goZ3JlcGwodXJpX3F1ZXJ5X2ZpbHRlciwgcGFnZXZpZXdzRGF0YSRyZWZlcmVyKSkKICB3X3VyaV9xdWVyeSA8LSB1bmlxdWUoYyh3X3VyaV9xdWVyeSwgd191cmlfcXVlcnlfcmVmZXJlcikpCiAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhW3dfdXJpX3F1ZXJ5LCBdCiAgd191cmlfcXVlcnlfcmVmZXJlciA8LSB3aGljaChncmVwbCh1cmlfcXVlcnlfZmlsdGVyLCBwYWdldmlld3NEYXRhJHJlZmVyZXIpKQogIHdfdXJpX3F1ZXJ5X3JlZmVyZXJfZGVsZXRlIDwtIHNldGRpZmYoMTpkaW0ocGFnZXZpZXdzRGF0YSlbMV0sIHdfdXJpX3F1ZXJ5X3JlZmVyZXIpCiAgcGFnZXZpZXdzRGF0YSRyZWZlcmVyW3dfdXJpX3F1ZXJ5X3JlZmVyZXJfZGVsZXRlXSA8LSAnJwogICMgLSB3aGVuIHRoZXJlIGlzIG5vIHVyaV9xdWVyeSwgdXNlIHRoZSBxdWVyeSBmcm9tIHRoZSByZWZlcmVyIGZpZWxkIGlmIHByZXNlbnQgdGhlcmUKICBwYWdldmlld3NEYXRhJHJlZmVyZXIgPC0gc3RyX2V4dHJhY3QocGFnZXZpZXdzRGF0YSRyZWZlcmVyLCAiXFw/Y2FtcGFpZ249LiokIikKICBwYWdldmlld3NEYXRhJHJlZmVyZXJbaXMubmEocGFnZXZpZXdzRGF0YSRyZWZlcmVyKV0gPC0gIiIKICBwYWdldmlld3NEYXRhJHJlZmVyZXIgPC0gZ3N1YigiP2NhbXBhaWduPSIsICIiLCBwYWdldmlld3NEYXRhJHJlZmVyZXIsIGZpeGVkID0gVCkKICBwYWdldmlld3NEYXRhJHVyaV9xdWVyeSA8LSBnc3ViKCI/Y2FtcGFpZ249IiwgIiIsIHBhZ2V2aWV3c0RhdGEkdXJpX3F1ZXJ5LCBmaXhlZCA9IFQpCiAgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnlbcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkgPT0gIiJdIDwtIAogICAgcGFnZXZpZXdzRGF0YSRyZWZlcmVyW3BhZ2V2aWV3c0RhdGEkdXJpX3F1ZXJ5ID09ICIiXQogIHBhZ2V2aWV3c0RhdGEgPC0gZHBseXI6OmZpbHRlcihwYWdldmlld3NEYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5ICE9ICIiKQogIHBhZ2V2aWV3c0RhdGEkcmVmZXJlciA8LSBOVUxMCiAgIyAtIGNsZWFuIHVwIGEgYml0OgogIHBhZ2V2aWV3c0RhdGEkdXJpX3F1ZXJ5IDwtIGdzdWIoIi8uKiQiLCAiIiwgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkpCiAgCiAgIyAtIGFnZ3JlZ2F0ZToKICBwYWdldmlld3NEYXRhIDwtIHBhZ2V2aWV3c0RhdGEgJT4lIAogICAgZHBseXI6Omdyb3VwX2J5KHVyaV9xdWVyeSwgdXJpX3BhdGgpICU+JSAKICAgIGRwbHlyOjpzdW1tYXJpc2UocGFnZXZpZXdzID0gbigpKQogIGNvbG5hbWVzKHBhZ2V2aWV3c0RhdGEpIDwtIGMoJ1RhZycsICdQYWdlJywgJ1BhZ2V2aWV3cycpCiAgCiAgIyAtIGFkZCBjZXREYXksIGNhbXBhaWduTmFtZQogIHBhZ2V2aWV3c0RhdGEkZGF0ZSA8LSBjZXREYXkKICBwYWdldmlld3NEYXRhJGNhbXBhaWduIDwtIGNhbXBhaWduTmFtZQogIAogICMgLSBzdG9yZToKICB3cml0ZS5jc3YocGFnZXZpZXdzRGF0YSwgCiAgICAgICAgICAgIHBhc3RlMCgicGFnZXZpZXdzQWdncmVnYXRlZF8iLAogICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoCiAgICAgICAgICAgICAgICAgICAgIHN0cnNwbGl0KGZpbGVOYW1lLCBzcGxpdCA9ICJfIiwgZml4ZWQgPSBUKVtbMV1dWzJdLAogICAgICAgICAgICAgICAgICAgICBzcGxpdCA9ICIuIiwgCiAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXSwKICAgICAgICAgICAgICAgICAgICIuY3N2IgogICAgICAgICAgICApCiAgKQogIAp9CgojIC0gc2V0IHBhcmFtcyB0byB3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzCiMgLSBmb3IgdGhlIEF1dHVtbiBCYW5uZXIgQ2FtcGFpZ24gMjAxOQp1cmlfcXVlcnlfZmlsdGVyIDwtICdXTURFX25ld2VkaXRvcnNfYXV0dW1uXzIwMTknCgojIC0gd3JhbmdsZSBwYWdldmlld3MKd21kZV9wcm9jZXNzX3BhZ2V2aWV3cyhmaWxlTmFtZSA9IGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIgPSBkYXRhRGlyLAogICAgICAgICAgICAgICAgICAgICAgIHVyaV9xdWVyeV9maWx0ZXIgPSB1cmlfcXVlcnlfZmlsdGVyLCAKICAgICAgICAgICAgICAgICAgICAgICBjZXREYXkgPSBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lID0gY2FtcGFpZ25OYW1lKSAKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gVXNlciBSZWdpc3RyYXRpb25zCiMjIyAtLS0gSGl2ZVFMIHF1ZXJ5OiBldmVudC5TZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uIHRhYmxlCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmhpdmVRTCA8LSAKICAiU0VMRUNUIHllYXIsIG1vbnRoLCBkYXksIGhvdXIsICBldmVudC5jYW1wYWlnbiwgZXZlbnQudXNlcklkLCBldmVudC51c2VyTmFtZSAKICAgIEZST00gZXZlbnQuc2VydmVyc2lkZWFjY291bnRjcmVhdGlvbiAKICAgIFdIRVJFIAogICAgICB5ZWFyID0gMjAxOSAKICAgICAgQU5EIG1vbnRoID49IDExIAogICAgICBBTkQgZGF5ID49IDEgCiAgICAgIEFORCAoZXZlbnQuY2FtcGFpZ24gTElLRSAnJVdNREVfbmV3ZWRpdG9yc19hdXR1bW5fMjAxOSUnKTsiCiMgLSB3cml0ZSBocWwKd3JpdGUoaGl2ZVFMLCBwYXN0ZTAoZGF0YURpciwgJ3VzZXJfcmVnaXN0cmF0aW9ucy5ocWwnKSkKIyMjIC0tLSBvdXRwdXQgZmlsZW5hbWUKZmlsZW5hbWUgPC0gcGFzdGUwKGRhdGFEaXIsICd1c2VyX3JlZ2lzdHJhdGlvbnMudHN2JykKIyMjIC0tLSBleGVjdXRlIGhxbCBzY3JpcHQ6CmhpdmVBcmdzIDwtICcvdXNyL2xvY2FsL2Jpbi9iZWVsaW5lIC0tc2lsZW50IC0taW5jcmVtZW50YWw9dHJ1ZSAtLXZlcmJvc2U9ZmFsc2UgLWYnCmhpdmVJbnB1dCA8LSBwYXN0ZShwYXN0ZTAoZGF0YURpciwgJ3VzZXJfcmVnaXN0cmF0aW9ucy5ocWwnKSwKICAgICAgICAgICAgICAgICAgICIgPiAiLAogICAgICAgICAgICAgICAgICAgZmlsZW5hbWUsCiAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKIyAtIGNvbW1hbmQ6CmhpdmVDb21tYW5kIDwtIHBhc3RlKGhpdmVBcmdzLCBoaXZlSW5wdXQpCnN5c3RlbShjb21tYW5kID0gaGl2ZUNvbW1hbmQsIHdhaXQgPSBUUlVFKQpgYGAKCiMjIyAwLjIgRGF0YSBBZ2dyZWdhdGlvbgoKKipOT1RFOioqIE5vdCBydW4gZnJvbSB0aGlzIHJlcG9ydDsgdGhlIGRhdGEgd2VyZSBhbHJlYWR5IHByZS1wcm9jZXNzZWQgYW5kIGFnZ3JlZ2F0ZWQgYnkgdGhlIGZvbGxvd2luZyBgUmAgc2NyaXB0IGJlZm9yZSBiZWluZyBzdWJtaXR0ZWQgdG8gYW5hbHl0aWNhbCBwcm9jZWR1cmVzOgoKYGBge3IsIGV2YWwgPSBGfQojICFkaWFnbm9zdGljcyBvZmYKIyMjIC0tLSBzY3JpcHQ6IEFCQ18yMDE5X0FnZ3JlZ2F0aW9uLlIKIyMjIC0tLSBEYXRhIEFnZ3JlZ2F0aW9uIGZvciB0aGUgQXV0dW1uIEJhbm5lciBDYW1wYWlnbiAyMDE5CiMjIyAtLS0gcnVuIGZyb20gc3RhdDEwMDQKIyMjIC0tLSAvaG9tZS9nb3JhbnNtL0FuYWx5dGljcy9OZXdFZGl0b3JzL0NhbXBhaWducy8yMDE5QXVCQy8KCiMjIyAtLS0gZGlyIHN0cnVjdHVyZQpjYW1wYWlnblBhdGggPC0gJy9ob21lL2dvcmFuc20vQW5hbHl0aWNzL05ld0VkaXRvcnMvQ2FtcGFpZ25zLzIwMTlBdUJDLycKZGF0YURpciA8LSBwYXN0ZTAoY2FtcGFpZ25QYXRoLCAiX2RhdGEvIikKYW5hbHl0aWNzRGlyIDwtIHBhc3RlMChjYW1wYWlnblBhdGgsICJfYW5hbHl0aWNzLyIpCgojIyMgLS0tIFJlcG9ydCBCYW5uZXIgSW1wcmVzc2lvbiBEYXRhCgojIC0gZnVuY3Rpb246IHdtZGVfcmVwb3J0X2Jhbm5lcl9pbXByZXNzaW9ucwp3bWRlX3JlcG9ydF9iYW5uZXJfaW1wcmVzc2lvbnMgPC0gZnVuY3Rpb24oZGF0YURpcikgewogIAogICMgLSBTZXR1cAogIGxpYnJhcnkoZGF0YS50YWJsZSkKICBsaWJyYXJ5KGRwbHlyKQogIAogICMgLSBsaXN0IGZpbGVzOgogIGxGIDwtIGxpc3QuZmlsZXMoZGF0YURpcikKICAKICAjIC0gZmlsdGVyIGFnZ3JlZ2F0ZWQgYmFubmVyIGltcHJlc3Npb24gZGF0YQogIGxGIDwtIGxGW2dyZXBsKCJiYW5uZXJJbXByZXNzaW9uc0FnZ3JlZ2F0ZWRfIiwgbEYsIGZpeGVkID0gVCldCiAgCiAgIyAtIGxvYWQgZmlsZXMgYW5kIG1lcmdlCiAgYmFubmVyRGF0YSA8LSB2ZWN0b3IobW9kZSA9ICJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKGxGKSkKICBmb3IgKGkgaW4gMTpsZW5ndGgobEYpKSB7CiAgICBpZiAoZ3JlcGwoImNzdiR8dHN2JCIsIGxGW2ldKSkgewogICAgICBiYW5uZXJEYXRhW1tpXV0gPC0gZnJlYWQocGFzdGUwKGRhdGFEaXIsIGxGW2ldKSkKICAgIH0gZWxzZSB7CiAgICAgIGJhbm5lckRhdGFbW2ldXSA8LSBOVUxMCiAgICB9CiAgfQogIGJhbm5lckRhdGEgPC0gcmJpbmRsaXN0KGJhbm5lckRhdGEpCiAgYmFubmVyRGF0YSRWMSA8LSBOVUxMCiAgCiAgIyAtIGFnZ3JlZ2F0ZXMKICBwZXJCYW5uZXJUb3RhbHMgPC0gYmFubmVyRGF0YSAlPiUgCiAgICBzZWxlY3QoYmFubmVyLCBpbXByZXNzaW9ucykgJT4lIAogICAgZ3JvdXBfYnkoYmFubmVyKSAlPiUgCiAgICBzdW1tYXJpc2UodG90YWxJbXByZXNzaW9ucyA9IHN1bShpbXByZXNzaW9ucykpCiAgcGVyRGF5VG90YWxzIDwtIGJhbm5lckRhdGEgJT4lIAogICAgc2VsZWN0KGRhdGUsIGltcHJlc3Npb25zKSAlPiUgCiAgICBncm91cF9ieShkYXRlKSAlPiUgCiAgICBzdW1tYXJpc2UodG90YWxJbXByZXNzaW9ucyA9IHN1bShpbXByZXNzaW9ucykpCiAgCiAgIyAtIG91dHB1dAogIHJldHVybigKICAgIGxpc3QoYmFubmVySW1wcmVzc2lvbnNSZXBvcnQgPSBiYW5uZXJEYXRhLCAKICAgICAgICAgcGVyQmFubmVyVG90YWxzID0gcGVyQmFubmVyVG90YWxzLCAKICAgICAgICAgcGVyRGF5VG90YWxzID0gcGVyRGF5VG90YWxzKQogICkKICAKfQoKIyAtIFJlcG9ydCBiYW5uZXIgaW1wcmVzc2lvbnMKYmFubmVySW1wcmVzc2lvbnNEYXRhIDwtIHdtZGVfcmVwb3J0X2Jhbm5lcl9pbXByZXNzaW9ucyhkYXRhRGlyKQpiYW5uZXJJbXByZXNzaW9uc0ZpbGUgPC0gYmFubmVySW1wcmVzc2lvbnNEYXRhJGJhbm5lckltcHJlc3Npb25zUmVwb3J0CndyaXRlLmNzdihiYW5uZXJJbXByZXNzaW9uc0ZpbGUsIHBhc3RlMChhbmFseXRpY3NEaXIsICJiYW5uZXJJbXByZXNzaW9uc0ZpbGUuY3N2IikpCmJhbm5lclRvdGFscyA8LSBiYW5uZXJJbXByZXNzaW9uc0RhdGEkcGVyQmFubmVyVG90YWxzCndyaXRlLmNzdihiYW5uZXJUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJiYW5uZXJUb3RhbHMuY3N2IikpCmJhbm5lckRheVRvdGFscyA8LSBiYW5uZXJJbXByZXNzaW9uc0RhdGEkcGVyRGF5VG90YWxzCndyaXRlLmNzdihiYW5uZXJEYXlUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJiYW5uZXJEYXlUb3RhbHMuY3N2IikpCgoKIyMjIC0tLSBSZXBvcnQgUGFnZXZpZXdzIERhdGEKCiMgLSBmdW5jdGlvbjogd21kZV9yZXBvcnRfcGFnZXZpZXdzCndtZGVfcmVwb3J0X3BhZ2V2aWV3cyA8LSBmdW5jdGlvbihkYXRhRGlyKSB7CiAgCiAgIyAtIFNldHVwCiAgbGlicmFyeShkYXRhLnRhYmxlKQogIGxpYnJhcnkoZHBseXIpCiAgCiAgIyAtIGxpc3QgZmlsZXM6CiAgbEYgPC0gbGlzdC5maWxlcyhkYXRhRGlyKQogIAogICMgLSBmaWx0ZXIgYWdncmVnYXRlZCBiYW5uZXIgaW1wcmVzc2lvbiBkYXRhCiAgbEYgPC0gbEZbZ3JlcGwoInBhZ2V2aWV3c0FnZ3JlZ2F0ZWRfIiwgbEYsIGZpeGVkID0gVCldCiAgCiAgIyAtIGxvYWQgZmlsZXMgYW5kIG1lcmdlCiAgcGFnZXZpZXdzRGF0YSA8LSB2ZWN0b3IobW9kZSA9ICJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKGxGKSkKICBmb3IgKGkgaW4gMTpsZW5ndGgobEYpKSB7CiAgICBpZiAoZ3JlcGwoImNzdiR8dHN2JCIsIGxGW2ldKSkgewogICAgICBwYWdldmlld3NEYXRhW1tpXV0gPC0gZnJlYWQocGFzdGUwKGRhdGFEaXIsIGxGW2ldKSkKICAgIH0gZWxzZSB7CiAgICAgIHBhZ2V2aWV3c0RhdGFbW2ldXSA8LSBOVUxMCiAgICB9CiAgfQogIHBhZ2V2aWV3c0RhdGEgPC0gcmJpbmRsaXN0KHBhZ2V2aWV3c0RhdGEpCiAgcGFnZXZpZXdzRGF0YSRWMSA8LSBOVUxMCiAgCiAgIyAtIGFnZ3JlZ2F0ZXMKICBwZXJEYXlUb3RhbHMgPC0gcGFnZXZpZXdzRGF0YSAlPiUgCiAgICBzZWxlY3QoZGF0ZSwgUGFnZXZpZXdzKSAlPiUgCiAgICBncm91cF9ieShkYXRlKSAlPiUgCiAgICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKICBwZXJUYWdUb3RhbHMgPC0gcGFnZXZpZXdzRGF0YSAlPiUgCiAgICBzZWxlY3QoVGFnLCBQYWdldmlld3MpICU+JSAKICAgIGdyb3VwX2J5KFRhZykgJT4lIAogICAgc3VtbWFyaXNlKHRvdGFsUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCiAgcGVyUGFnZVRvdGFscyA8LSBwYWdldmlld3NEYXRhICU+JSAKICAgIHNlbGVjdChQYWdlLCBQYWdldmlld3MpICU+JSAKICAgIGdyb3VwX2J5KFBhZ2UpICU+JSAKICAgIHN1bW1hcmlzZSh0b3RhbFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQogIHBlclBhZ2VEYXlUb3RhbHMgPC0gcGFnZXZpZXdzRGF0YSAlPiUgCiAgICBzZWxlY3QoUGFnZSwgZGF0ZSwgUGFnZXZpZXdzKSAlPiUKICAgIGdyb3VwX2J5KFBhZ2UsIGRhdGUpICU+JSAKICAgIHN1bW1hcmlzZSh0b3RhbFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQogIHBlclRhZ0RheVRvdGFscyA8LSBwYWdldmlld3NEYXRhICU+JSAKICAgIHNlbGVjdChUYWcsIGRhdGUsIFBhZ2V2aWV3cykgJT4lCiAgICBncm91cF9ieShUYWcsIGRhdGUpICU+JSAKICAgIHN1bW1hcmlzZSh0b3RhbFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQogIHBlclRhZ1BhZ2VUb3RhbHMgPC0gcGFnZXZpZXdzRGF0YSAlPiUgCiAgICBzZWxlY3QoVGFnLCBQYWdlLCBQYWdldmlld3MpICU+JQogICAgZ3JvdXBfYnkoVGFnLCBQYWdlKSAlPiUgCiAgICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKICAKICAjIC0gb3V0cHV0CiAgcmV0dXJuKAogICAgbGlzdChwYWdldmlld3NEYXRhUmVwb3J0ID0gcGFnZXZpZXdzRGF0YSwgCiAgICAgICAgIHBlckRheVRvdGFscyA9IHBlckRheVRvdGFscywgCiAgICAgICAgIHBlclRhZ1RvdGFscyA9IHBlclRhZ1RvdGFscywgCiAgICAgICAgIHBlclBhZ2VUb3RhbHMgPSBwZXJQYWdlVG90YWxzLCAKICAgICAgICAgcGVyUGFnZURheVRvdGFscyA9IHBlclBhZ2VEYXlUb3RhbHMsIAogICAgICAgICBwZXJUYWdEYXlUb3RhbHMgPSBwZXJUYWdEYXlUb3RhbHMsCiAgICAgICAgIHBlclRhZ1BhZ2VUb3RhbHMgPSBwZXJUYWdQYWdlVG90YWxzKQogICkKICAKfQoKIyAtIFJlcG9ydCBwYWdldmlld3M6CnBhZ2V2aWV3c0RhdGEgPC0gd21kZV9yZXBvcnRfcGFnZXZpZXdzKGRhdGFEaXIpCnBhZ2V2aWV3c1JlcG9ydEZpbGUgPC0gcGFnZXZpZXdzRGF0YSRwYWdldmlld3NEYXRhUmVwb3J0CndyaXRlLmNzdihwYWdldmlld3NSZXBvcnRGaWxlLCBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAicGFnZXZpZXdzUmVwb3J0RmlsZS5jc3YiKSkKcGFnZXZpZXdzX3BlckRheVRvdGFscyA8LSBwYWdldmlld3NEYXRhJHBlckRheVRvdGFscwp3cml0ZS5jc3YocGFnZXZpZXdzX3BlckRheVRvdGFscywgcGFzdGUwKGFuYWx5dGljc0RpciwgInBhZ2V2aWV3c19wZXJEYXlUb3RhbHMuY3N2IikpCnBhZ2V2aWV3c19wZXJUYWdUb3RhbHMgPC0gcGFnZXZpZXdzRGF0YSRwZXJUYWdUb3RhbHMKd3JpdGUuY3N2KHBhZ2V2aWV3c19wZXJUYWdUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJwYWdldmlld3NfcGVyVGFnVG90YWxzLmNzdiIpKQpwYWdldmlld3NfcGVyUGFnZVRvdGFscyA8LSBwYWdldmlld3NEYXRhJHBlclBhZ2VUb3RhbHMKd3JpdGUuY3N2KHBhZ2V2aWV3c19wZXJQYWdlVG90YWxzLCBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAicGFnZXZpZXdzX3BlclBhZ2VUb3RhbHMuY3N2IikpCnBhZ2V2aWV3c19wZXJQYWdlRGF5VG90YWxzIDwtIHBhZ2V2aWV3c0RhdGEkcGVyUGFnZURheVRvdGFscwp3cml0ZS5jc3YocGFnZXZpZXdzX3BlclBhZ2VEYXlUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJwYWdldmlld3NfcGVyUGFnZURheVRvdGFscy5jc3YiKSkKcGFnZXZpZXdzX3BlclRhZ0RheVRvdGFscyA8LSBwYWdldmlld3NEYXRhJHBlclRhZ0RheVRvdGFscwp3cml0ZS5jc3YocGFnZXZpZXdzX3BlclRhZ0RheVRvdGFscywgcGFzdGUwKGFuYWx5dGljc0RpciwgInBhZ2V2aWV3c19wZXJUYWdEYXlUb3RhbHMuY3N2IikpCnBhZ2V2aWV3c19wZXJUYWdQYWdlVG90YWxzIDwtIHBhZ2V2aWV3c0RhdGEkcGVyVGFnUGFnZVRvdGFscwp3cml0ZS5jc3YocGFnZXZpZXdzX3BlclRhZ1BhZ2VUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJwZXJUYWdQYWdlVG90YWxzLmNzdiIpKQoKIyMjIC0tLSBSZXBvcnQgVXNlciBSZWdpc3RyYXRpb25zCgojIC0gZnVuY3Rpb246IHdtZGVfcmVwb3J0X3JlZ2lzdHJhdGlvbnNfaGl2ZQp3bWRlX3JlcG9ydF9yZWdpc3RyYXRpb25zX2hpdmUgPC0gZnVuY3Rpb24oZGF0YURpciwgY2FtcGFpZ24pIHsKICAKICAjIC0gU2V0dXAKICBsaWJyYXJ5KGRhdGEudGFibGUpCiAgbGlicmFyeShkcGx5cikKICAKICAjIC0gbGlzdCBmaWxlczoKICBsRiA8LSBsaXN0LmZpbGVzKGRhdGFEaXIpCiAgCiAgIyAtIGZpbHRlciBhZ2dyZWdhdGVkIHVzZXIgcmVnaXN0cmF0aW9uIGRhdGEKICBsRiA8LSBsRltncmVwbCgidXNlcl9yZWdpc3RyYXRpb25zLnRzdiIsIGxGLCBmaXhlZCA9IFQpXQogIAogICMgLSBsb2FkIGZpbGUgYW5kIHdyYW5nbGUgY29sdW1ucwogIHJlZ2lzdHJhdGlvbkRhdGEgPC0gZnJlYWQocGFzdGUwKGRhdGFEaXIsIGxGKSwgaGVhZGVyID0gVCkKICBjb2xuYW1lcyhyZWdpc3RyYXRpb25EYXRhKVs1XSA8LSAnZXZlbnRfY2FtcGFpZ24nCiAgY29sbmFtZXMocmVnaXN0cmF0aW9uRGF0YSlbNl0gPC0gJ2V2ZW50X3VzZXJJZCcKICByZWdpc3RyYXRpb25EYXRhJGNhbXBhaWduIDwtIGNhbXBhaWduCiAgcmVnaXN0cmF0aW9uRGF0YSRkYXkgPC0gaWZlbHNlKG5jaGFyKHJlZ2lzdHJhdGlvbkRhdGEkZGF5KSA9PSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIjAiLCByZWdpc3RyYXRpb25EYXRhJGRheSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWdpc3RyYXRpb25EYXRhJGRheSkKICByZWdpc3RyYXRpb25EYXRhJGRhdGUgPC0gcGFzdGUocmVnaXN0cmF0aW9uRGF0YSR5ZWFyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVnaXN0cmF0aW9uRGF0YSRtb250aCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVnaXN0cmF0aW9uRGF0YSRkYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICItIikKCiAgIyAtIGFnZ3JlZ2F0ZXMKICBwZXJEYXlUb3RhbHMgPC0gcmVnaXN0cmF0aW9uRGF0YSAlPiUgCiAgICBzZWxlY3QoZGF0ZSkgJT4lIAogICAgZ3JvdXBfYnkoZGF0ZSkgJT4lIAogICAgc3VtbWFyaXNlKHRvdGFsUmVnaXN0cmF0aW9ucyA9IG4oKSkKICBwZXJUYWdUb3RhbHMgPC0gcmVnaXN0cmF0aW9uRGF0YSAlPiUgCiAgICBzZWxlY3QoZXZlbnRfY2FtcGFpZ24pICU+JSAKICAgIGdyb3VwX2J5KGV2ZW50X2NhbXBhaWduKSAlPiUgCiAgICBzdW1tYXJpc2UodG90YWxSZWdpc3RyYXRpb25zID0gbigpKQogIHBlclRhZ0RheVRvdGFscyA8LSByZWdpc3RyYXRpb25EYXRhICU+JSAKICAgIHNlbGVjdChldmVudF9jYW1wYWlnbiwgZGF0ZSkgJT4lIAogICAgZ3JvdXBfYnkoZXZlbnRfY2FtcGFpZ24sIGRhdGUpICU+JSAKICAgIHN1bW1hcmlzZSh0b3RhbFJlZ2lzdHJhdGlvbnMgPSBuKCkpCiAgCiAgIyAtIGZ1bGwgcmVnaXN0cmF0aW9uIGRhdGFzZXQ6IG5vbi1hZ2dyZWdhdGVkIHJlZ2lzdHJhdGlvbnMKICAjIC0gZmlsdGVyIGRhdGE6CiAgZnVsbFJlZ0RhdGEgPC0gcmVnaXN0cmF0aW9uRGF0YSAlPiUgCiAgICBzZWxlY3QoZXZlbnRfdXNlcklkLCAKICAgICAgICAgICBldmVudF9jYW1wYWlnbiwgCiAgICAgICAgICAgZGF0ZSwgCiAgICAgICAgICAgY2FtcGFpZ24pCiAgCiAgIyAtIG91dHB1dAogIHJldHVybigKICAgIGxpc3QocmVnaXN0cmF0aW9uc0RhdGFSZXBvcnQgPSByZWdpc3RyYXRpb25EYXRhLCAKICAgICAgICAgcGVyRGF5VG90YWxzID0gcGVyRGF5VG90YWxzLCAKICAgICAgICAgcGVyVGFnVG90YWxzID0gcGVyVGFnVG90YWxzLCAKICAgICAgICAgcGVyVGFnRGF5VG90YWxzID0gcGVyVGFnRGF5VG90YWxzLAogICAgICAgICBmdWxsUmVnaXN0cmF0aW9uRGF0YXNldCA9IGZ1bGxSZWdEYXRhKQogICkKICAKfQoKIyAtIFJlcG9ydCB1cG9uIHVzZXIgcmVnaXN0cmF0aW9ucwpjYW1wYWlnbiA8LSAnMjAxOV9BdUJDJwp1c2VyUmVnRGF0YSA8LSB3bWRlX3JlcG9ydF9yZWdpc3RyYXRpb25zX2hpdmUoZGF0YURpciwgY2FtcGFpZ24pCnVzZXJSZWdpc3RyYXRpb25zUmVwb3J0RmlsZSA8LSB1c2VyUmVnRGF0YSRyZWdpc3RyYXRpb25zRGF0YVJlcG9ydAp3cml0ZS5jc3YodXNlclJlZ2lzdHJhdGlvbnNSZXBvcnRGaWxlLCBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAidXNlclJlZ2lzdHJhdGlvbnNSZXBvcnRGaWxlLmNzdiIpKQp1c2VyUmVnaXN0cmF0aW9uc19wZXJEYXlUb3RhbHMgPC0gdXNlclJlZ0RhdGEkcGVyRGF5VG90YWxzCndyaXRlLmNzdih1c2VyUmVnaXN0cmF0aW9uc19wZXJEYXlUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJ1c2VyUmVnaXN0cmF0aW9uc19wZXJEYXlUb3RhbHMuY3N2IikpCnVzZXJSZWdpc3RyYXRpb25zX3BlclRhZ1RvdGFscyA8LSB1c2VyUmVnRGF0YSRwZXJUYWdUb3RhbHMKd3JpdGUuY3N2KHVzZXJSZWdpc3RyYXRpb25zX3BlclRhZ1RvdGFscywgcGFzdGUwKGFuYWx5dGljc0RpciwgInVzZXJSZWdpc3RyYXRpb25zX3BlclRhZ1RvdGFscy5jc3YiKSkKdXNlclJlZ2lzdHJhdGlvbnNfcGVyVGFnRGF5VG90YWxzIDwtIHVzZXJSZWdEYXRhJHBlclRhZ0RheVRvdGFscwp3cml0ZS5jc3YodXNlclJlZ2lzdHJhdGlvbnNfcGVyVGFnRGF5VG90YWxzLCBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAidXNlclJlZ2lzdHJhdGlvbnNfcGVyVGFnRGF5VG90YWxzLmNzdiIpKQpmdWxsVXNlclJlZ2lzdHJhdGlvbnMgPC0gdXNlclJlZ0RhdGEkZnVsbFJlZ2lzdHJhdGlvbkRhdGFzZXQKd3JpdGUuY3N2KGZ1bGxVc2VyUmVnaXN0cmF0aW9ucywgcGFzdGUwKGFuYWx5dGljc0RpciwgImZ1bGxSZWdpc3RyYXRpb25EYXRhc2V0LmNzdiIpKQoKCmBgYAoKCiMjIDEuIENhbXBhaWduIEJhbm5lcnMgYW5kIFBhZ2VzCgpUaGlzIHNlY3Rpb24gcHJlc2VudHMgYWxsIGRhdGEgYW5kIHN0YXRpc3RpY3Mgb24gdGhlIGNhbXBhaWduIGJhbm5lcnMgYW5kIHBhZ2VzLgoKIyMjIDEuMSBCYW5uZXIgSW1wcmVzc2lvbnMKIyMjIyAxLjEuMSBCYW5uZXIgSW1wcmVzc2lvbnMgT3ZlcnZpZXcKCioqQ2hhcnQgMS4xLjEqKiBEYWlseSBCYW5uZXIgSW1wcmVzc2lvbnMKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpkYXRhU2V0IDwtIHJlYWQuY3N2KAogICdfYW5hbHl0aWNzL2Jhbm5lckltcHJlc3Npb25zRmlsZS5jc3YnLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChkYXRhU2V0LCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICAgeSA9IGltcHJlc3Npb25zLAogICAgICAgICAgICAgICAgICAgIGdyb3VwID0gYmFubmVyLAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gYmFubmVyLAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBpbXByZXNzaW9ucywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnQXV0dW1uIEJhbm5lciBDYW1wYWlnbiAyMDE5OiBCYW5uZXIgSW1wcmVzc2lvbnMnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCiMjIyMgMS4xLjEgQmFubmVyIEltcHJlc3Npb25zIE92ZXJ2aWV3OiBUYWJsZQoKKipUYWJsZSAxLjEuMS4qKiBEYWlseSBCYW5uZXIgSW1wcmVzc2lvbnMKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIyMgLS0tIEZ1bGwgRGF0YXNldCAoVGFibGUgUmVwb3J0KQpkYXRhdGFibGUoZGF0YVNldCkKYGBgCgojIyMjIDEuMS4yIFRvdGFsIEJhbm5lciBJbXByZXNzaW9ucwoqKkNoYXJ0IDEuMS4yLioqIFRvdGFsIEJhbm5lciBJbXByZXNzaW9ucwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmRhdGFTZXQgPC0gcmVhZC5jc3YoCiAgJ19hbmFseXRpY3MvYmFubmVyVG90YWxzLmNzdicsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKZ2dwbG90KGRhdGFTZXQsIGFlcyh4ID0gYmFubmVyLCAKICAgICAgICAgICAgICAgICAgICB5ID0gdG90YWxJbXByZXNzaW9ucywgCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdG90YWxJbXByZXNzaW9ucykpICsgCiAgZ2VvbV9iYXIod2lkdGggPSAuNSwgc3RhdCA9ICJpZGVudGl0eSIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnQXV0dW1uIEJhbm5lciBDYW1wYWlnbiAyMDE5OiBCYW5uZXIgSW1wcmVzc2lvbnMnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV9sYWJlbF9yZXBlbChzaXplID0gMy41LCBjb2xvciA9ICJ3aGl0ZSIsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMjIyAxLjEuMyBCYW5uZXIgSW1wcmVzc2lvbnMgcGVyIERheQoKKipDaGFydCAxLjEuMy4qKiBCYW5uZXIgSW1wcmVzc2lvbnMgcGVyIERheQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmRhdGFTZXQgPC0gcmVhZC5jc3YoCiAgJ19hbmFseXRpY3MvYmFubmVyRGF5VG90YWxzLmNzdicsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKZ2dwbG90KGRhdGFTZXQsIGFlcyh4ID0gZGF0ZSwgCiAgICAgICAgICAgICAgICAgICAgeSA9IHRvdGFsSW1wcmVzc2lvbnMsIAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdG90YWxJbXByZXNzaW9ucykpICsKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSwgZ3JvdXAgPSAxLCBjb2xvciA9ICJkYXJrYmx1ZSIpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLjUsIGNvbG9yID0gImRhcmtibHVlIikgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnQXV0dW1uIEJhbm5lciBDYW1wYWlnbiAyMDE5OiBCYW5uZXIgSW1wcmVzc2lvbnMnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV9sYWJlbF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQpgYGAKCiMjIyAxLjIgUGFnZXZpZXdzCgojIyMjIDEuMi4xIFBhZ2V2aWV3cyBPdmVydmlldwoKKipDaGFydCAxLjIuMS4qKiBQYWdldmlld3MgT3ZlcnZpZXcuIExvZyBzY2FsaW5nIG9mIHRoZSBwYWdldmlld3MgaXMgbmVjZXNzYXJ5OyB0aGUgbnVtYmVycyByZXBvcnRlZCBpbiB0aGUgZGF0YSBwb2ludCBsYWJlbHMgYXJlIGV4YWN0LgoKYGBge3IgZXZhbD1ULCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTIsIGVjaG89RkFMU0V9CmRhdGFTZXQgPC0gcmVhZC5jc3YoCiAgJ19hbmFseXRpY3MvcGFnZXZpZXdzX3BlclBhZ2VEYXlUb3RhbHMuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpkYXRhU2V0JFBhZ2UgPC0gZ3N1YigiL3dpa2kvV2lraXBlZGlhOiIsICIiLCBkYXRhU2V0JFBhZ2UpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChkYXRhU2V0LCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICAgeSA9IGxvZzEwKHRvdGFsUGFnZXZpZXdzKSwKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFBhZ2UsCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFBhZ2UsCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB0b3RhbFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjI1LCBncm91cCA9IDEpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnQXV0dW1uIEJhbm5lciBDYW1wYWlnbiAyMDE5OiBQYWdldmlld3MnKSArCiAgZmFjZXRfd3JhcCh+UGFnZSwgbmNvbCA9IDMpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKCiMjIyMgMS4yLjEgUGFnZXZpZXdzIE92ZXJ2aWV3OiBUYWJsZQoKKipUYWJsZSAxLjIuMS4qKiBQYWdldmlld3MgT3ZlcnZpZXcKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIyMgLS0tIEZ1bGwgRGF0YXNldCAoVGFibGUgUmVwb3J0KQpkYXRhdGFibGUoZGF0YVNldCAlPiUgYXJyYW5nZShkZXNjKHRvdGFsUGFnZXZpZXdzKSkpCmBgYAoKIyMjIyAxLjIuMiBQYWdldmlld3MgT3ZlcnZpZXc6IHRvdGFscyBwZXIgUGFnZQoqKkNoYXJ0IDEuMi4yLioqIFBhZ2V2aWV3cyBPdmVydmlldzogdG90YWxzIHBlciBQYWdlCgpgYGB7ciBldmFsPVQsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMiwgZWNobz1GQUxTRX0KZGF0YVNldCA8LSByZWFkLmNzdigKICAnX2FuYWx5dGljcy9wYWdldmlld3NfcGVyUGFnZVRvdGFscy5jc3YnLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmRhdGFTZXQkUGFnZSA8LSBnc3ViKCIvd2lraS9XaWtpcGVkaWE6IiwgIiIsIGRhdGFTZXQkUGFnZSkKZGF0YVNldCRQYWdlIDwtIGZhY3RvcihkYXRhU2V0JFBhZ2UsIAogICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGRhdGFTZXQkUGFnZVtvcmRlcigtZGF0YVNldCR0b3RhbFBhZ2V2aWV3cyldKQpnZ3Bsb3QoZGF0YVNldCwgYWVzKHggPSBQYWdlLCAKICAgICAgICAgICAgICAgICAgICB5ID0gbG9nKHRvdGFsUGFnZXZpZXdzKSwgCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBQYWdlLAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSBQYWdlLAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdG90YWxQYWdldmlld3MpKSArIAogIGdlb21fYmFyKHdpZHRoID0gLjUsIHN0YXQgPSAiaWRlbnRpdHkiKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ0F1dHVtbiBCYW5uZXIgQ2FtcGFpZ24gMjAxOTogVG90YWwgUGFnZXZpZXdzIHBlciBQYWdlJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fbGFiZWxfcmVwZWwoc2l6ZSA9IDMuNSwgY29sb3IgPSAid2hpdGUiLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCgojIyMjIDEuMi4zIFBhZ2V2aWV3cyBPdmVydmlldzogdG90YWxzIHBlciBkYXkKCioqQ2hhcnQgMS4yLjMuKiogUGFnZXZpZXdzIE92ZXJ2aWV3OiB0b3RhbHMgcGVyIGRheQoKYGBge3IgZXZhbD1ULCBmaWcud2lkdGg9NiwgZWNobz1GQUxTRX0KZGF0YVNldCA8LSByZWFkLmNzdigKICAnX2FuYWx5dGljcy9wYWdldmlld3NfcGVyRGF5VG90YWxzLmNzdicsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKZ2dwbG90KGRhdGFTZXQsIGFlcyh4ID0gZGF0ZSwgCiAgICAgICAgICAgICAgICAgICAgeSA9IHRvdGFsUGFnZXZpZXdzLCAKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHRvdGFsUGFnZXZpZXdzKSkgKwogIGdlb21fcGF0aChzaXplID0gLjI1LCBncm91cCA9IDEsIGNvbG9yID0gImRhcmtibHVlIikgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgY29sb3IgPSAiZGFya2JsdWUiKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGNvbG9yID0gIndoaXRlIikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdBdXR1bW4gQmFubmVyIENhbXBhaWduIDIwMTk6IFBhZ2V2aWV3cycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX2xhYmVsX3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpCmBgYAoKCiMjIyMgMS4yLjMgUGFnZXZpZXdzIE92ZXJ2aWV3OiB0b3RhbHMgcGVyIFRhZy9QYWdlCgoqKkNoYXJ0IDEuMi4zLioqIFBhZ2V2aWV3cyBPdmVydmlldzogdG90YWxzIHBlciBUYWcvUGFnZQoKYGBge3IgZXZhbD1ULCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTIsIGVjaG89RkFMU0V9CmRhdGFTZXQgPC0gcmVhZC5jc3YoCiAgJ19hbmFseXRpY3MvcGVyVGFnUGFnZVRvdGFscy5jc3YnLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNhbXBhaWduVGFncyA8LSBjKCdXTURFX25ld2VkaXRvcnNfYXV0dW1uXzIwMTlfbmxfbHAxJywKICAgICAgICAgICAgICAgICAgJ1dNREVfbmV3ZWRpdG9yc19hdXR1bW5fMjAxOV9ubF9scDInLAogICAgICAgICAgICAgICAgICAnV01ERV9uZXdlZGl0b3JzX2F1dHVtbl8yMDE5X2ZseWVyJywKICAgICAgICAgICAgICAgICAgJ1dNREVfbmV3ZWRpdG9yc19hdXR1bW5fMjAxOV9ibnInKQpkYXRhU2V0IDwtIGZpbHRlcihkYXRhU2V0LCAKICAgICAgICAgICAgICAgICAgZ3JlcGwoY2FtcGFpZ25UYWdzWzFdLCBkYXRhU2V0JFRhZyl8IAogICAgICAgICAgICAgICAgICAgIGdyZXBsKGNhbXBhaWduVGFnc1syXSwgZGF0YVNldCRUYWcpfCAKICAgICAgICAgICAgICAgICAgICBncmVwbChjYW1wYWlnblRhZ3NbM10sIGRhdGFTZXQkVGFnKXwgCiAgICAgICAgICAgICAgICAgICAgZ3JlcGwoY2FtcGFpZ25UYWdzWzRdLCBkYXRhU2V0JFRhZykpCmRhdGFTZXQkVGFnW2dyZXBsKGNhbXBhaWduVGFnc1sxXSwgZGF0YVNldCRUYWcsIGZpeGVkID0gVCldIDwtICJubF9scDEiCmRhdGFTZXQkVGFnW2dyZXBsKGNhbXBhaWduVGFnc1syXSwgZGF0YVNldCRUYWcsIGZpeGVkID0gVCldIDwtICJubF9scDIiCmRhdGFTZXQkVGFnW2dyZXBsKGNhbXBhaWduVGFnc1szXSwgZGF0YVNldCRUYWcsIGZpeGVkID0gVCldIDwtICJmbHllciIKZGF0YVNldCRUYWdbZ3JlcGwoY2FtcGFpZ25UYWdzWzRdLCBkYXRhU2V0JFRhZywgZml4ZWQgPSBUKV0gPC0gImJuciIKZGF0YVNldCRQYWdlIDwtIGdzdWIoIi93aWtpL1dpa2lwZWRpYToiLCAiIiwgZGF0YVNldCRQYWdlKQpkYXRhU2V0IDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChUYWcsIFBhZ2UsIHRvdGFsUGFnZXZpZXdzKSAlPiUgCiAgZ3JvdXBfYnkoVGFnLCBQYWdlKSAlPiUgCiAgc3VtbWFyaXNlKHRvdGFsUGFnZXZpZXdzID0gc3VtKHRvdGFsUGFnZXZpZXdzKSkKZGF0YVNldCAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gVGFnLAogICAgICAgICAgICAgeSA9IFBhZ2UsCiAgICAgICAgICAgICBjb2xvciA9IFRhZywKICAgICAgICAgICAgIGxhYmVsID0gdG90YWxQYWdldmlld3MpKSArIAogIGdndGl0bGUoJ0F1dHVtbiBCYW5uZXIgQ2FtcGFpZ24gMjAxOTogUGFnZXZpZXdzIHBlciBUYWcnKSArCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IHRvdGFsUGFnZXZpZXdzKSwgc2hhcGUgPSAxOSkgKwogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMywgbnVkZ2VfeCA9IC4zLCBzaG93LmxlZ2VuZCA9IEYpICsgCiAgeGxhYigiVGFncyIpICsgeWxhYigiUGFnZXMiKSArIAogIHRoZW1lX2J3KCkgKyAKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSkKYGBgCgojIyMjIDEuMi40IFBhZ2V2aWV3cyBwZXIgVGFnLCBkYWlseSB0b3RhbHMKKipDaGFydCAxLjIuNC4gUGFnZXZpZXdzIHBlciBUYWcsIGRhaWx5IHRvdGFscy4qKiBMb2cgc2NhbGluZyBvZiB0aGUgcGFnZXZpZXdzIGlzIG5lY2Vzc2FyeTsgdGhlIG51bWJlcnMgcmVwb3J0ZWQgaW4gdGhlIGRhdGEgcG9pbnQgbGFiZWxzIGFyZSBleGFjdC4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpkYXRhU2V0IDwtIHJlYWQuY3N2KAogICdfYW5hbHl0aWNzL3BhZ2V2aWV3c19wZXJUYWdEYXlUb3RhbHMuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjYW1wYWlnblRhZ3MgPC0gYygnV01ERV9uZXdlZGl0b3JzX2F1dHVtbl8yMDE5X25sX2xwMScsCiAgICAgICAgICAgICAgICAgICdXTURFX25ld2VkaXRvcnNfYXV0dW1uXzIwMTlfbmxfbHAyJywKICAgICAgICAgICAgICAgICAgJ1dNREVfbmV3ZWRpdG9yc19hdXR1bW5fMjAxOV9mbHllcicsCiAgICAgICAgICAgICAgICAgICdXTURFX25ld2VkaXRvcnNfYXV0dW1uXzIwMTlfYm5yJykKZGF0YVNldCA8LSBmaWx0ZXIoZGF0YVNldCwgCiAgICAgICAgICAgICAgICAgIGdyZXBsKGNhbXBhaWduVGFnc1sxXSwgZGF0YVNldCRUYWcpfCAKICAgICAgICAgICAgICAgICAgICBncmVwbChjYW1wYWlnblRhZ3NbMl0sIGRhdGFTZXQkVGFnKXwgCiAgICAgICAgICAgICAgICAgICAgZ3JlcGwoY2FtcGFpZ25UYWdzWzNdLCBkYXRhU2V0JFRhZyl8IAogICAgICAgICAgICAgICAgICAgIGdyZXBsKGNhbXBhaWduVGFnc1s0XSwgZGF0YVNldCRUYWcpKQpkYXRhU2V0JFRhZ1tncmVwbChjYW1wYWlnblRhZ3NbMV0sIGRhdGFTZXQkVGFnLCBmaXhlZCA9IFQpXSA8LSAibmxfbHAxIgpkYXRhU2V0JFRhZ1tncmVwbChjYW1wYWlnblRhZ3NbMl0sIGRhdGFTZXQkVGFnLCBmaXhlZCA9IFQpXSA8LSAibmxfbHAyIgpkYXRhU2V0JFRhZ1tncmVwbChjYW1wYWlnblRhZ3NbM10sIGRhdGFTZXQkVGFnLCBmaXhlZCA9IFQpXSA8LSAiZmx5ZXIiCmRhdGFTZXQkVGFnW2dyZXBsKGNhbXBhaWduVGFnc1s0XSwgZGF0YVNldCRUYWcsIGZpeGVkID0gVCldIDwtICJibnIiCmRhdGFTZXQgPC0gZGF0YVNldCAlPiUgCiAgZ3JvdXBfYnkoVGFnLCBkYXRlKSAlPiUgCiAgc3VtbWFyaXNlKHRvdGFsUGFnZXZpZXdzID0gc3VtKHRvdGFsUGFnZXZpZXdzKSkKZ2dwbG90KGRhdGFTZXQsIGFlcyh4ID0gZGF0ZSwKICAgICAgICAgICAgICAgICAgICB5ID0gbG9nMTAodG90YWxQYWdldmlld3MpLAogICAgICAgICAgICAgICAgICAgIGdyb3VwID0gVGFnLAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gVGFnLAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSBUYWcsCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB0b3RhbFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjI1KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ0F1dHVtbiBCYW5uZXIgQ2FtcGFpZ24gMjAxOTogUGFnZXZpZXdzIHBlciBUYWcsIGRhaWx5IHRvdGFscycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKIyMgMi4gVXNlciBSZWdpc3RyYXRpb25zCgpBbGwgZGF0YSBvbiB1c2VyIHJlZ2lzdHJhdGlvbnMgYXJlIHByZXNlbnRlZCBpbiB0aGlzIHNlY3Rpb24uCgojIyMgMi4xIFJlZ2lzdHJhdGlvbnMgcGVyIHRhZyBhbmQgZGF5CgoqKkNoYXJ0IDIuMS4qKiBSZWdpc3RyYXRpb25zIHBlciB0YWcgYW5kIGRheS4gUGxlYXNlIG5vdGU6IHBvaW50cyB3aXRoIG5vIGRhdGEgbGFiZWxzIHNpZ25pZnkgMCB1c2VyIHJlZ2lzdHJhdGlvbnMuCgpgYGB7ciBldmFsPVQsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD03LCBlY2hvPUZBTFNFfQojIC0gU3RhbmRhcmQgcmVnaXN0cmF0aW9ucwpkYXRhU2V0IDwtIHJlYWQuY3N2KAogICdfYW5hbHl0aWNzL3VzZXJSZWdpc3RyYXRpb25zUmVwb3J0RmlsZS5jc3YnLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmRhdGFTZXQgPC0gZGF0YVNldCAlPiUKICBzZWxlY3QoZGF0ZSwgZXZlbnRfY2FtcGFpZ24pICU+JSAKICBncm91cF9ieShldmVudF9jYW1wYWlnbiwgZGF0ZSkgJT4lIAogIHN1bW1hcmlzZShSZWdpc3RyYXRpb25zID0gbigpKQpnZ3Bsb3QoZGF0YVNldCwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgIHkgPSBSZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgIGdyb3VwID0gZXZlbnRfY2FtcGFpZ24sCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBldmVudF9jYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgICBmaWxsID0gZXZlbnRfY2FtcGFpZ24sCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBSZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuMjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnQXV0dW1uIEJhbm5lciBDYW1wYWlnbiAyMDE5OiBSZWdpc3RyYXRpb25zIHBlciBUYWcnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgo=