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"))

0.3 User Edits

0.3.1 User Edits via dewiki.revision - FAILED

library(data.table)
# !diagnostics off
### --- script: ABC_2019_UserEdits.R
### --- User Edits Acquisition for the Autumn Banner Campaign 2019
### --- run from stat1004
### --- /home/goransm/Analytics/NewEditors/Campaigns/2019AuBC/
### -----------------------------------------------------------------------
### --- User Edits
### -----------------------------------------------------------------------
### --- dir structure
campaignPath <- '/home/goransm/Analytics/NewEditors/Campaigns/2019AuBC/'
dataDir <- paste0(campaignPath, "_data/")
analyticsDir <- paste0(campaignPath, "_analytics/")
startTimestamp <- '20191101000000'
# - get user ids
userRegistrations <- read.csv(paste0(analyticsDir, 'userRegistrationsReportFile.csv'))
rev_user <- userRegistrations$event_userId
rev_user_text <- as.character(userRegistrations$username)
# - iterate over rev_user
for (i in 1:length(rev_user)) {
  # - check username
  if (grepl("'", rev_user_text[i], fixed = T)) {
    rev_user_text[i] <- gsub("'", "\\'", rev_user_text[i], fixed = T)
  }
  # - SQL query
  sqlQuery <- paste("\"SELECT rev_user, rev_timestamp FROM dewiki.revision WHERE (rev_timestamp >= ", 
                    startTimestamp, " AND rev_user_text = '", rev_user_text[i], "');\"", sep = "")
  ### --- output filename
  filename <- paste(dataDir,'userEdits', "_", rev_user[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'))

### --- Check user edits
# - select all non-anonymous user edits on dewiki where timestamp > 20191101000000
# - SQL query
sqlQuery <- paste("\"SELECT rev_actor FROM dewiki.revision WHERE (rev_timestamp >= ", 
                  startTimestamp, ");\"", sep = "")
### --- output filename
filename <- paste0(dataDir, 'userEditsALL.tsv')
### --- 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)
# - load userEdits
userEdits <- fread(paste0(dataDir, 'userEditsALL.tsv'))
tEdits <- table(as.numeric(userEdits$rev_user))

0.3.2 User Edits via revision_actor_temp

library(data.table)
# !diagnostics off
### --- script: ABC_2019_UserEdits_wmfMediaWikiHistory.R
### --- User Edits Acquisition for the Autumn Banner Campaign 2019
### --- run from stat1004
### --- /home/goransm/Analytics/NewEditors/Campaigns/2019AuBC/
### -----------------------------------------------------------------------
### --- User Edits
### -----------------------------------------------------------------------
### --- dir structure
campaignPath <- '/home/goransm/Analytics/NewEditors/Campaigns/2019AuBC/'
dataDir <- paste0(campaignPath, "_data/")
analyticsDir <- paste0(campaignPath, "_analytics/")
startTimestamp <- '20191101000000'
# - get user ids
userRegistrations <- read.csv(paste0(analyticsDir, 
                                     'userRegistrationsReportFile.csv'))
rev_user <- userRegistrations$event_userId
rev_user_text <- as.character(userRegistrations$username)
# - 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 >= 20191101000000 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'))

### --- Check user edits
# - select all non-anonymous user edits on dewiki where timestamp > 20191101000000
# - SQL query
sqlQuery <- paste("\"SELECT revactor_actor, revactor_timestamp FROM revision_actor_temp WHERE (revactor_timestamp >= ", 
                    startTimestamp, ");\"", sep = "")
### --- output filename
filename <- paste0(dataDir, 'userEditsALL.tsv')
### --- 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)
# - load userEdits
userEdits <- fread(paste0(dataDir, 'userEditsALL.tsv'))
tEdits <- table(as.numeric(userEdits$revactor_actor))
# - campaign registered users?
wCU <- which(rev_user %in% names(tEdits))
rev_user[wCU]

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.

### --- Full Dataset (Table Report)
datatable(dplyr::arrange(dataSet, event_campaign, date, desc(Registrations)))

2.2 Total registrations per tag

Chart 2.2. Total registrations per tag.

dataSet <- dataSet %>% 
  group_by(event_campaign) %>% 
  summarise(totalRegistrations = sum(Registrations))
ggplot(dataSet, aes(x = event_campaign, 
                    y = totalRegistrations, 
                    color = event_campaign,
                    fill = event_campaign,
                    label = totalRegistrations)) + 
  geom_bar(width = .5, stat = "identity") + 
  scale_y_continuous(labels = comma) +
  ggtitle('Autumn Banner Campaign 2019: Total Registrations per Tag') +
  theme_minimal() + 
  geom_label_repel(size = 3.5, color = "white", show.legend = FALSE) + 
  scale_y_continuous(labels = comma) + 
  ylab("Registrations") +
  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

All data on user edits are presented in this section.

3.1 User edits: distribution

userEdits <- read.csv(
  '_analytics/userEdits.csv',
                    header = T,
                    row.names = 1,
                    check.names = F,
                    stringsAsFactors = F)
dataSet <- userEdits %>%
  dplyr::select(actor_user) %>%
  dplyr::group_by(actor_user) %>% 
  dplyr::summarise(edits = n())
# - Edit | 1 | 2-4 | 5-9 | 10-49 | >50
editBoundaries <- list(
  c(0, 1), 
  c(2, 4),
  c(5, 9),
  c(10, 49)
)
dataSet$editClass <- sapply(dataSet$edits, function(x) {
  wEC <- sapply(editBoundaries, function(y) {
    x >= y[1] & x <= y[2]
  })
  if (sum(wEC) == 0) {
    return(">= 50")
  } else {
    return(paste0("(",
                  editBoundaries[[which(wEC)]][1],
                  " - ",
                  editBoundaries[[which(wEC)]][2], 
                  ")"
                  )
    )
  }
})
editClass <- as.data.frame(table(dataSet$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)

4. Training Modules

All data on training modules are presented in this section.

4.1 Started Training Modules

tModules <- read.csv('_analytics/wmde_training_data_2019-11.csv',
                     header = T,
                     check.names = F,
                     stringsAsFactors = F)
userReg <- read.csv(
  '_analytics/userRegistrationsReportFile.csv',
                    header = T,
                    row.names = 1,
                    check.names = F,
                    stringsAsFactors = F)
tModules <- tModules %>% 
  dplyr::filter(tModules$username %in% userReg$username)
tModules_Overview <- tModules %>%
  dplyr::select(training_module) %>% 
  dplyr::group_by(training_module) %>% 
  dplyr::summarise(num_users = n())
datatable(tModules_Overview)

4.2 Completed Training Modules

The following table presents the statistics on completed training modules.

tModules <- read.csv('_analytics/wmde_training_data_2019-11.csv',
                     header = T,
                     check.names = F,
                     stringsAsFactors = F)
userReg <- read.csv(
  '_analytics/userRegistrationsReportFile.csv',
                    header = T,
                    row.names = 1,
                    check.names = F,
                    stringsAsFactors = F)
tModules <- tModules %>% 
  dplyr::filter(tModules$username %in% userReg$username)
tModules <- tModules %>%
  dplyr::filter(nchar(module_completion_date) > 0)
tModules_Overview <- tModules %>%
  dplyr::select(training_module) %>% 
  dplyr::group_by(training_module) %>% 
  dplyr::summarise(num_users = n())
datatable(tModules_Overview)

4.3 Completed Training Modules and User Edits

The following table presents the statistics on completed training modules and the respective users’ edits.

tModules <- read.csv('_analytics/wmde_training_data_2019-11.csv',
                     header = T,
                     check.names = F,
                     stringsAsFactors = F)
userReg <- read.csv(
  '_analytics/userRegistrationsReportFile.csv',
                    header = T,
                    row.names = 1,
                    check.names = F,
                    stringsAsFactors = F)
tModules <- tModules %>% 
  dplyr::filter(tModules$username %in% userReg$username)
tModules <- tModules %>%
  dplyr::filter(nchar(module_completion_date) > 0)
tModules_Edits <- tModules %>%
  dplyr::left_join(userEdits, 
                   by = c("username" = "actor_name")) %>% 
  dplyr::filter(!is.na(revactor_timestamp)) %>% 
  dplyr::group_by(training_module) %>% 
  dplyr::summarise(edits = n())
datatable(tModules_Edits)
LS0tCnRpdGxlOiAnV01ERSBBdXR1bW4gQmFubmVyIENhbXBhaWduIDIwMTk6IEludGVyaW0gUmVwb3J0JwphdXRob3I6ICJHb3JhbiBTLiBNaWxvdmFub3ZpYywgRGF0YSBTY2llbnRpc3QsIFdNREUiCmRhdGU6ICJOb3ZlbWJlciAxMCwgMjAxOSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRoZW1lOiBzaW1wbGV4CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogNQogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA1Ci0tLQoKKipGZWVkYmFjayoqIHNob3VsZCBiZSBzZW5kIHRvIGBnb3Jhbi5taWxvdmFub3ZpY19leHRAd2lraW1lZGlhLmRlYC4gCgpUaGUgY2FtcGFpZ24gaXMgcnVuIGZyb20gMjAxOS8xMS8wMSB0byAyMDE5LzExLzA5LgoKKipDVVJSRU5UIFVQREFURToqKiBDb21wbGV0ZSBkYXRhc2V0IGFzIG9mIDIwMTkvMTEvMDkuCgpgYGB7ciwgZWNobyA9IEYsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGLCByZXN1bHRzID0gJ2hpZGUnfQojICFkaWFnbm9zdGljcyBvZmYKIyMjIC0tLSBTZXR1cAprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQgPSA4KSAKcm0obGlzdCA9IGxzKCkpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkocm1hcmtkb3duKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KHJlc2hhcGUyKQpgYGAKCiMjIDAuIERhdGEgQWNxdWlzaXRvbgoKKipOT1RFOioqIHRoZSBEYXRhIEFjcXVpc2l0aW9uIGNvZGUgY2h1bmsgaXMgbm90IGZ1bGx5IHJlcHJvZHVjaWJsZSBmcm9tIHRoaXMgUmVwb3J0LiBUaGUgZGF0YSBhcmUgY29sbGVjdGVkIGJ5IHJ1bm5pbmcgdGhlIHNjcmlwdCBgQUJDXzIwMTlfUFJPRFVDVElPTi5SYCBmcm9tIHRoZSBgc3RhdDEwMDRgIFdNRiBzdGF0aXN0aWNzIHNlcnZlciwgY29sbGVjdGluZyB0aGUgZGF0YSBhcyBgLnRzdmAgYW5kIGAuY3N2YCBmaWxlcywgYWdncmVnYXRpbmcgb24gdGhlIHNlcnZlciB3aXRoIGBBQkNfMjAxOV9BZ2dyZWdhdGlvbi5SYCwgY29weWluZyBtYW51YWxseSBhbmQgcHJvY2Vzc2luZyBsb2NhbGx5LiAKCiMjIyAwLjEgRGFpbHkgVXBkYXRlCgpgYGB7ciwgZXZhbCA9IEZ9CiMgIWRpYWdub3N0aWNzIG9mZgojIyMgLS0tIHNjcmlwdDogQUJDXzIwMTlfUFJPRFVDVElPTi5SCiMjIyAtLS0gRGF0YSBBY3F1aXNpdGlvbiBmb3IgdGhlIEF1dHVtbiBCYW5uZXIgQ2FtcGFpZ24gMjAxOQojIyMgLS0tIHJ1biBmcm9tIHN0YXQxMDA0CiMjIyAtLS0gL2hvbWUvZ29yYW5zbS9BbmFseXRpY3MvTmV3RWRpdG9ycy9DYW1wYWlnbnMvMjAxOUF1QkMvCgojIyMgLS0tIGxpYnJhcmllcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKCiMjIyAtLS0gZGlyIHN0cnVjdHVyZQpjYW1wYWlnblBhdGggPC0gJy9ob21lL2dvcmFuc20vQW5hbHl0aWNzL05ld0VkaXRvcnMvQ2FtcGFpZ25zLzIwMTlBdUJDLycKZGF0YURpciA8LSBwYXN0ZTAoY2FtcGFpZ25QYXRoLCAiX2RhdGEvIikKYW5hbHl0aWNzRGlyIDwtIHBhc3RlMChjYW1wYWlnblBhdGgsICJfYW5hbHl0aWNzLyIpCgojIyMgLS0tIGRldGVybWluZSBjZXREYXkKY2V0RGF5IDwtIFN5cy50aW1lKCkKY2V0RGF5CmF0dHIoY2V0RGF5LCAidHpvbmUiKSA8LSAiRXVyb3BlL0JlcmxpbiIKIyAtIG9uZSBkYXkgYmVoaW5kIGZvciBjcm9udGFiCiMgLSAoaS5lLiB3YWl0aW5nIGZvciB3bWYud2VicmVxdWVzdCB0byBjb21wbGV0ZSBpcyBkYXRhIGFjcXVpc2l0aW9uKQpjZXREYXkgPC0geW1kKAogIHN0cnNwbGl0KGFzLmNoYXJhY3RlcihjZXREYXkpLCAKICAgICAgICAgICBzcGxpdCA9ICIgIiwgCiAgICAgICAgICAgZml4ZWQgPSBUKVtbMV1dWzFdCikgLSAxCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIEJhbm5lciBJbXByZXNzaW9ucwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIGZ1bmN0aW9uOiB3bWRlX2NvbGxlY3RfYmFubmVyX2ltcHJlc3Npb25zCndtZGVfY29sbGVjdF9iYW5uZXJfaW1wcmVzc2lvbnMgPC0gZnVuY3Rpb24odXJpX2hvc3QsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9wYXRoLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcXVlcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpcikgewogIAogICMgLSBOT1RFOgogICMgLSBleHBlY3RlZCBmb3JtYXQgZm9yIGNldERheSBpczogWVlZWS1NTS1ERAogIAogICMgLSB0byBkYXRhRGlyCiAgc2V0d2QoZGF0YURpcikKICAKICAjIC0gbGlicmFyaWVzCiAgbGlicmFyeShzdHJpbmdyKQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSBkYXRldGltZV9jb25kaXRpb24KICBjZXRfY29uZGl0aW9uIDwtIHNlcSgKICAgIGZyb20gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAwOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIHRvID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMjM6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgYnkgPSAiaG91ciIKICApIAogIGF0dHIoY2V0X2NvbmRpdGlvbiwgInR6b25lIikgPC0gIlVUQyIKICBjZXRfY29uZGl0aW9uIDwtIGFzLmNoYXJhY3RlcihjZXRfY29uZGl0aW9uKQogIGNldF9jb25kaXRpb24gPC0gdW5saXN0KHN0cl9leHRyYWN0X2FsbChjZXRfY29uZGl0aW9uLCAiXihbWzpkaWdpdDpdXXxcXHN8LSkqIikpCiAgY2V0X3llYXJzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzFdCiAgICB9KQogIGNldF9tb250aHMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMl0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfbW9udGhzKQogIGNldF9kYXlzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzNdCiAgICB9KQogIGNldF9kYXlzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9kYXlzKQogIGNldF9ob3VycyA8LSBzYXBwbHkoc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIAogICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICB4WzJdCiAgICAgICAgICAgICAgICAgICAgICB9KQogIGNldF9ob3VycyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfaG91cnMpCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUwKAogICAgInllYXIgPSAiLCBjZXRfeWVhcnMsICIgQU5EICIsCiAgICAibW9udGggPSAiLCBjZXRfbW9udGhzLCAiIEFORCAiLAogICAgImRheSA9ICIsIGNldF9kYXlzLCAiIEFORCAiLCAKICAgICJob3VyID0gIiwgY2V0X2hvdXJzCiAgKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlKCIoIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZXRpbWVDb25kaXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpCiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9wYXRoX2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX3BhdGgpID4gMSkgewogICAgdXJpX3BhdGhfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgidXJpX3BhdGggPSAnIiwgdXJpX3BhdGgsICInIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICB1cmlfcGF0aF9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpCiAgfQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSB1cmlfaG9zdF9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9ob3N0KSA+IDEpIHsKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9ob3N0ID0gJyIsIHVyaV9ob3N0LCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX2hvc3RfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfaG9zdCA9ICciLCB1cmlfaG9zdCwgIiciKQogIH0KICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX3F1ZXJ5X2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX3F1ZXJ5KSA+IDEpIHsKICAgIHVyaV9xdWVyeV9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9xdWVyeSBMSUtFICclIiwgdXJpX3F1ZXJ5LCAiJSciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX3F1ZXJ5X2NvbmRpdGlvbiA9IHBhc3RlMCgidXJpX3F1ZXJ5IExJS0UgJyUiLCB1cmlfcXVlcnksICIlJyIpCiAgfQogIAogICMgLSBjb21wb3NlIEhpdmVRTCBxdWVyeQogIGhpdmVRdWVyeSA8LSBwYXN0ZTAoCiAgICAiVVNFIHdtZjsKICAgIFNFTEVDVCB1cmlfcXVlcnkgRlJPTSB3ZWJyZXF1ZXN0CiAgICBXSEVSRSAoIiwKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiwgIiBBTkQgIiwKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiwgIiBBTkQgIiwKICAgIHVyaV9xdWVyeV9jb25kaXRpb24sICIgQU5EICIsCiAgICAiKCIsIGRhdGV0aW1lQ29uZGl0aW9uLCAiKSIsCiAgICAiKTsiCiAgICApCiAgCiAgIyAtIHdyaXRlIGhxbAogIHdyaXRlKGhpdmVRdWVyeSwgcXVlcnlGaWxlKQogICMgLSBleGVjdXRlIGhxbCBzY3JpcHQ6CiAgaGl2ZUFyZ3MgPC0gJy91c3IvbG9jYWwvYmluL2JlZWxpbmUgLWYnCiAgaGl2ZUlucHV0IDwtIHBhc3RlMChxdWVyeUZpbGUsICcgPiAnLCBmaWxlTmFtZSkKICAjIC0gY29tbWFuZDoKICBoaXZlQ29tbWFuZCA8LSBwYXN0ZShoaXZlQXJncywgaGl2ZUlucHV0KQogIHJldHVybigKICAgIHN5c3RlbShjb21tYW5kID0gaGl2ZUNvbW1hbmQsIHdhaXQgPSBUUlVFKQogICkKfQoKIyAtIHNldCBwYXJhbXMgdG8gd21kZV9jb2xsZWN0X2Jhbm5lcl9pbXByZXNzaW9ucwojIC0gZm9yIHRoZSBBdXR1bW4gQmFubmVyIENhbXBhaWduIDIwMTkKdXJpX2hvc3QgPC0gYygnZGUud2lraXBlZGlhLm9yZycsICdkZS5tLndpa2lwZWRpYS5vcmcnKQp1cmlfcGF0aCAgPC0gJy9iZWFjb24vaW1wcmVzc2lvbicKdXJpX3F1ZXJ5IDwtIGMoJ1dNREVfbmV3ZWRpdG9yc19hdXR1bW5fMjAxOScpCnF1ZXJ5RmlsZSA8LSAnYWJjMjAxOV9CYW5uZXJJbXByZXNzaW9ucy5ocWwnCmZpbGVOYW1lIDwtIHBhc3RlMChkYXRhRGlyLCAiYmFubmVySW1wcmVzc2lvbnNfIiwgY2V0RGF5LCAiLnRzdiIpCgojIC0gY29sbGVjdCBCYW5uZXIgSW1wcmVzc2lvbiBkYXRhCndtZGVfY29sbGVjdF9iYW5uZXJfaW1wcmVzc2lvbnModXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpcikKCiMgLSBmdW5jdGlvbjogd21kZV9wcm9jZXNzX2Jhbm5lcl9pbXByZXNzaW9ucwp3bWRlX3Byb2Nlc3NfYmFubmVyX2ltcHJlc3Npb25zIDwtIGZ1bmN0aW9uKGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lKSB7CiAgCiAgIyAtIHRvIGRhdGFEaXIKICBzZXR3ZChkYXRhRGlyKQogIAogICMgLSBsaWJyYXJpZXMKICBsaWJyYXJ5KHN0cmluZ3IpCiAgbGlicmFyeShkcGx5cikKICAKICAjIC0gbG9hZAogIGJhbm5lckRhdGEgPC0gcmVhZC5kZWxpbShmaWxlTmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIGNvbG5hbWVzKGJhbm5lckRhdGEpIDwtICd1cmlfcXVlcnknCiAgCiAgIyAtIGNsZWFuCiAgd1N0YXJ0IDwtIHdoaWNoKGJhbm5lckRhdGEkdXJpX3F1ZXJ5ID09ICJ1cmlfcXVlcnkiKQogIGJhbm5lckRhdGEgPC0gYmFubmVyRGF0YVsod1N0YXJ0ICsgMSk6KGRpbShiYW5uZXJEYXRhKVsxXSAtIDIpLCBdCiAgCiAgIyAtIHNwbGl0CiAgYmFubmVyRGF0YSA8LSBzdHJzcGxpdChiYW5uZXJEYXRhLCBzcGxpdCA9ICImIiwgZml4ZWQgPSBUKQogICMgLSBleHRyYWN0IHJlbGV2YW50IGZpZWxkcwogICMgLSBiYW5uZXI6CiAgYmFubmVyIDwtIHNhcHBseShiYW5uZXJEYXRhLCBmdW5jdGlvbih4KSB7CiAgICB4W3doaWNoKGdyZXBsKCJeYmFubmVyPSIsIHgpKV0KICB9KQogIGJhbm5lciA8LSBnc3ViKCJeYmFubmVyPSIsICIiLCBiYW5uZXIpCiAgIyAtIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlOgogIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlIDwtIHNhcHBseShiYW5uZXJEYXRhLCBmdW5jdGlvbih4KSB7CiAgICB4W3doaWNoKGdyZXBsKCJecmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGU9IiwgeCkpXQogIH0pCiAgcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUgPC0gYXMubnVtZXJpYygKICAgIGdzdWIoIl5yZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZT0iLCAiIiwgcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUpCiAgKQogICMgLSByZXN1bHQ6CiAgcmVzdWx0IDwtIHNhcHBseShiYW5uZXJEYXRhLCBmdW5jdGlvbih4KSB7CiAgICB4W3doaWNoKGdyZXBsKCJecmVzdWx0PSIsIHgpKV0KICB9KQogIHJlc3VsdCA8LSBnc3ViKCJecmVzdWx0PSIsICIiLCByZXN1bHQpCiAgCiAgIyAtIGNvbXBvc2UgdGFibGU6CiAgYmFubmVyT2JzZXJ2YXRpb25zIDwtIGRhdGEuZnJhbWUoYmFubmVyID0gYmFubmVyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSA9IHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSByZXN1bHQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIAogICMgLSBmaWx0ZXIgZm9yIHJlc3VsdD1zaG93CiAgYmFubmVyT2JzZXJ2YXRpb25zIDwtIGRwbHlyOjpmaWx0ZXIoYmFubmVyT2JzZXJ2YXRpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9PSAic2hvdyIpCiAgCiAgIyAtIGNvcnJlY3Rpb24gZm9yIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlCiAgYmFubmVyT2JzZXJ2YXRpb25zJHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlIDwtIAogICAgMS9iYW5uZXJPYnNlcnZhdGlvbnMkcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUKICAKICAjIC0gYWdncmVnYXRlOgogIGJhbm5lck9ic2VydmF0aW9ucyA8LSBiYW5uZXJPYnNlcnZhdGlvbnMgJT4lIAogICAgZHBseXI6OnNlbGVjdChiYW5uZXIsIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlKSAlPiUgCiAgICBkcGx5cjo6Z3JvdXBfYnkoYmFubmVyKSAlPiUgCiAgICBkcGx5cjo6c3VtbWFyaXNlKGltcHJlc3Npb25zID0gc3VtKHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlKSkKICAKICAjIC0gYWRkIGNldERheSwgbWUKICBiYW5uZXJPYnNlcnZhdGlvbnMkZGF0ZSA8LSBjZXREYXkKICBiYW5uZXJPYnNlcnZhdGlvbnMkY2FtcGFpZ24gPC0gY2FtcGFpZ25OYW1lCiAgCiAgIyAtIHN0b3JlOgogIHdyaXRlLmNzdihiYW5uZXJPYnNlcnZhdGlvbnMsIAogICAgICAgICAgICBwYXN0ZTAoImJhbm5lckltcHJlc3Npb25zQWdncmVnYXRlZF8iLAogICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICkKICApCiAgCn0KCiMgLSB3cmFuZ2xlIEJhbm5lciBJbXByZXNzaW9uIGRhdGEKY2FtcGFpZ25OYW1lIDwtICIyMDE5X0F1QkMiCndtZGVfcHJvY2Vzc19iYW5uZXJfaW1wcmVzc2lvbnMoZmlsZU5hbWUgPSBmaWxlTmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpciA9IGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUgPSBjYW1wYWlnbk5hbWUpCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIFBhZ2V2aWV3cwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIGZ1bmN0aW9uOiB3bWRlX2NvbGxlY3RfcGFnZXZpZXdzCndtZGVfY29sbGVjdF9wYWdldmlld3MgPC0gZnVuY3Rpb24odXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyKSB7CiAgCiAgIyAtIE5PVEU6CiAgIyAtIGV4cGVjdGVkIGZvcm1hdCBmb3IgY2V0RGF5IGlzOiBZWVlZLU1NLURECiAgCiAgIyAtIHRvIGRhdGFEaXIKICBzZXR3ZChkYXRhRGlyKQogIAogICMgLSBsaWJyYXJpZXMKICBsaWJyYXJ5KHN0cmluZ3IpCiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIGRhdGV0aW1lX2NvbmRpdGlvbgogIGNldF9jb25kaXRpb24gPC0gc2VxKAogICAgZnJvbSA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDA6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgdG8gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAyMzowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICBieSA9ICJob3VyIgogICkgCiAgYXR0cihjZXRfY29uZGl0aW9uLCAidHpvbmUiKSA8LSAiVVRDIgogIGNldF9jb25kaXRpb24gPC0gYXMuY2hhcmFjdGVyKGNldF9jb25kaXRpb24pCiAgY2V0X2NvbmRpdGlvbiA8LSB1bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKGNldF9jb25kaXRpb24sICJeKFtbOmRpZ2l0Ol1dfFxcc3wtKSoiKSkKICBjZXRfeWVhcnMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMV0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsyXQogICAgfSkKICBjZXRfbW9udGhzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9tb250aHMpCiAgY2V0X2RheXMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bM10KICAgIH0pCiAgY2V0X2RheXMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2RheXMpCiAgY2V0X2hvdXJzIDwtIHNhcHBseShzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgCiAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHhbMl0KICAgICAgICAgICAgICAgICAgICAgIH0pCiAgY2V0X2hvdXJzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9ob3VycykKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZTAoCiAgICAieWVhciA9ICIsIGNldF95ZWFycywgIiBBTkQgIiwKICAgICJtb250aCA9ICIsIGNldF9tb250aHMsICIgQU5EICIsCiAgICAiZGF5ID0gIiwgY2V0X2RheXMsICIgQU5EICIsIAogICAgImhvdXIgPSAiLCBjZXRfaG91cnMKICApCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUoIigiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZUNvbmRpdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX3BhdGhfY29uZGl0aW9uCiAgaWYgKGxlbmd0aCh1cmlfcGF0aCkgPiAxKSB7CiAgICB1cmlfcGF0aF9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfcGF0aCA9ICciLCB1cmlfcGF0aCwgIiciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiA9IHBhc3RlMCgidXJpX3BhdGggPSAnIiwgdXJpX3BhdGgsICInIikKICB9CiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9ob3N0X2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX2hvc3QpID4gMSkgewogICAgdXJpX2hvc3RfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgidXJpX2hvc3QgPSAnIiwgdXJpX2hvc3QsICInIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9ob3N0ID0gJyIsIHVyaV9ob3N0LCAiJyIpCiAgfQogIAogICMgLSBjb21wb3NlIEhpdmVRTCBxdWVyeQogIGhpdmVRdWVyeSA8LSBwYXN0ZTAoCiAgICAiVVNFIHdtZjsKICAgIFNFTEVDVCB1cmlfcGF0aCwgdXJpX3F1ZXJ5LCByZWZlcmVyIEZST00gd2VicmVxdWVzdAogICAgV0hFUkUgKCIsCiAgICB1cmlfaG9zdF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcGF0aF9jb25kaXRpb24sICIgQU5EICIsCiAgICAiKCIsIGRhdGV0aW1lQ29uZGl0aW9uLCAiKSIsCiAgICAiKTsiCiAgICApCiAgCiAgIyAtIHdyaXRlIGhxbAogIHdyaXRlKGhpdmVRdWVyeSwgcXVlcnlGaWxlKQogICMgLSBleGVjdXRlIGhxbCBzY3JpcHQ6CiAgaGl2ZUFyZ3MgPC0gJy91c3IvbG9jYWwvYmluL2JlZWxpbmUgLWYnCiAgaGl2ZUlucHV0IDwtIHBhc3RlMChxdWVyeUZpbGUsICcgPiAnLCBmaWxlTmFtZSkKICAjIC0gY29tbWFuZDoKICBoaXZlQ29tbWFuZCA8LSBwYXN0ZShoaXZlQXJncywgaGl2ZUlucHV0KQogIHJldHVybigKICAgIHN5c3RlbShjb21tYW5kID0gaGl2ZUNvbW1hbmQsIHdhaXQgPSBUUlVFKQogICkKfQoKIyAtIHNldCBwYXJhbXMgdG8gd21kZV9jb2xsZWN0X3BhZ2V2aWV3cwojIC0gZm9yIHRoZSBBdXR1bW4gQmFubmVyIENhbXBhaWduIDIwMTkKdXJpX2hvc3QgPC0gYygnZGUud2lraXBlZGlhLm9yZycsICdkZS5tLndpa2lwZWRpYS5vcmcnKQp1cmlfcGF0aCAgPC0gYygKICAnL3dpa2kvV2lraXBlZGlhOldpa2lwZWRpYV92b3JfT3J0JywKICAnL3dpa2kvV2lraXBlZGlhOktvbnRvcl9IYW1idXJnL0FrdGlvbnN0YWdfMjAxOScsCiAgJy93aWtpL1dpa2lwZWRpYTpXaWtpV2VkZGluZy9XaWtpcGVkaWFfdm9yX09ydF8yMDE5JywKICAnL3dpa2kvV2lraXBlZGlhOldpa2lCJUMzJUE0ci9Ba3Rpb25zdGFnX1dpa2lwZWRpYV8yMDE5JywKICAnL3dpa2kvV2lraXBlZGlhOlJ1aHJnZWJpZXQvV2lraXBlZGlhX3Zvcl9PcnRfMjAxOScsCiAgJy93aWtpL1dpa2lwZWRpYTpLJUMzJUI2bG4vQWt0aW9uc3RhZ19XaWtpcGVkaWFfMjAxOScsCiAgJy93aWtpL1dpa2lwZWRpYTpIYW5ub3Zlci9Ba3Rpb25zdGFnX1dpa2lwZWRpYV8yMDE5JywKICAnL3dpa2kvV2lraXBlZGlhOkZyYW5rZnVydC9XaWtpcGVkaWFfdm9yX09ydF8yMDE5JywKICAnL3dpa2kvV2lraXBlZGlhOkF1Z3NidXJnL0FrdGlvbnN0YWdfV2lraXBlZGlhXzIwMTknLAogICcvd2lraS9XaWtpcGVkaWE6V2lraU1VQy8yMDE5LTExLTEwX1dpa2lwZWRpYV92b3JfT3J0JywKICAnL3dpa2kvV2lraXBlZGlhOkZyZWlidXJnX2ltX0JyZWlzZ2F1L1dpa2lwZWRpYV92b3JfT3J0XzIwMTknLAogICcvd2lraS9XaWtpcGVkaWE6TCVDMyVCNnJyYWNoL0FrdGlvbnN0YWdfV2lraXBlZGlhXzIwMTknLAogICcvd2lraS9XaWtpcGVkaWE6Qm9kZW5zZWUvV2lraXBlZGlhX3Zvcl9PcnRfMjAxOScsCiAgJy93aWtpL1dpa2lwZWRpYTpPYmVyJUMzJUI2c3RlcnJlaWNoL1dpa2lwZWRpYV92b3JfT3J0XzIwMTknLAogICcvd2lraS9XaWtpcGVkaWE6V2llbi9XaWtpcGVkaWFfdm9yX09ydF8yMDE5JywKICAnL3dpa2kvV2lraXBlZGlhOk5pZWRlciVDMyVCNnN0ZXJyZWljaC9Ba3Rpb25zdGFnX1dpa2lwZWRpYV92b3JfT3J0XzIwMTknLAogICcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0xlcm5lV2lraXBlZGlhJykKCiMgLSBzZXQgcGFyYW1zIGZvciB3bWRlX2NvbGxlY3RfcGFnZXZpZXdzCnF1ZXJ5RmlsZSA8LSAnYWJjMjAxOV9QYWdldmlld3MuaHFsJwpmaWxlTmFtZSA8LSBwYXN0ZTAoInBhZ2V2aWV3c18iLCBjZXREYXksICIudHN2IikKCiMgLSBjb2xsZWN0IFBhZ2V2aWV3cyBkYXRhCndtZGVfY29sbGVjdF9wYWdldmlld3ModXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyKQoKIyMjIC0tLSBXcmFuZ2xlIFBhZ2V2aWV3cwojIC0gZnVuY3Rpb246IHdtZGVfcHJvY2Vzc19wYWdldmlld3MKd21kZV9wcm9jZXNzX3BhZ2V2aWV3cyA8LSBmdW5jdGlvbihmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcXVlcnlfZmlsdGVyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXkgPSBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lID0gY2FtcGFpZ25OYW1lKSB7CiAgCiAgIyAtIHRvIGRhdGFEaXIKICBzZXR3ZChkYXRhRGlyKQogIAogICMgLSBsaWJyYXJpZXMKICBsaWJyYXJ5KHN0cmluZ3IpCiAgbGlicmFyeShkcGx5cikKICBsaWJyYXJ5KHRpZHlyKQogIGxpYnJhcnkoZGF0YS50YWJsZSkKICAKICAjIC0gbG9hZAogIHBhZ2V2aWV3c0RhdGEgPC0gcmVhZExpbmVzKGZpbGVOYW1lKQogIHdTdGFydCA8LSB3aGljaChncmVwbCgiXnVyaV9wYXRoIiwgcGFnZXZpZXdzRGF0YSkpCiAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhWyh3U3RhcnQgKyAyKToobGVuZ3RoKHBhZ2V2aWV3c0RhdGEpIC0gMildCiAgcGFnZXZpZXdzRGF0YSA8LSBkYXRhLmZyYW1lKGRhdCA9IHBhZ2V2aWV3c0RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICBwYWdldmlld3NEYXRhIDwtIHNlcGFyYXRlKHBhZ2V2aWV3c0RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRvID0gYygndXJpX3BhdGgnLCAndXJpX3F1ZXJ5JywgJ3JlZmVyZXInKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIpCiAgIyAtIGFwcGx5IHVyaV9xdWVyeV9maWx0ZXIKICAjIC0gTk9URTogQXV0dW1uIDIwMTksIGxvb2tpbmcgaW4gYm90aDogdXJpX3F1ZXJ5LCByZWZlcmVyCiAgd191cmlfcXVlcnkgPC0gd2hpY2goZ3JlcGwodXJpX3F1ZXJ5X2ZpbHRlciwgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkpKQogIHdfdXJpX3F1ZXJ5X3JlZmVyZXIgPC0gd2hpY2goZ3JlcGwodXJpX3F1ZXJ5X2ZpbHRlciwgcGFnZXZpZXdzRGF0YSRyZWZlcmVyKSkKICB3X3VyaV9xdWVyeSA8LSB1bmlxdWUoYyh3X3VyaV9xdWVyeSwgd191cmlfcXVlcnlfcmVmZXJlcikpCiAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhW3dfdXJpX3F1ZXJ5LCBdCiAgd191cmlfcXVlcnlfcmVmZXJlciA8LSB3aGljaChncmVwbCh1cmlfcXVlcnlfZmlsdGVyLCBwYWdldmlld3NEYXRhJHJlZmVyZXIpKQogIHdfdXJpX3F1ZXJ5X3JlZmVyZXJfZGVsZXRlIDwtIHNldGRpZmYoMTpkaW0ocGFnZXZpZXdzRGF0YSlbMV0sIHdfdXJpX3F1ZXJ5X3JlZmVyZXIpCiAgcGFnZXZpZXdzRGF0YSRyZWZlcmVyW3dfdXJpX3F1ZXJ5X3JlZmVyZXJfZGVsZXRlXSA8LSAnJwogICMgLSB3aGVuIHRoZXJlIGlzIG5vIHVyaV9xdWVyeSwgdXNlIHRoZSBxdWVyeSBmcm9tIHRoZSByZWZlcmVyIGZpZWxkIGlmIHByZXNlbnQgdGhlcmUKICBwYWdldmlld3NEYXRhJHJlZmVyZXIgPC0gc3RyX2V4dHJhY3QocGFnZXZpZXdzRGF0YSRyZWZlcmVyLCAiXFw/Y2FtcGFpZ249LiokIikKICBwYWdldmlld3NEYXRhJHJlZmVyZXJbaXMubmEocGFnZXZpZXdzRGF0YSRyZWZlcmVyKV0gPC0gIiIKICBwYWdldmlld3NEYXRhJHJlZmVyZXIgPC0gZ3N1YigiP2NhbXBhaWduPSIsICIiLCBwYWdldmlld3NEYXRhJHJlZmVyZXIsIGZpeGVkID0gVCkKICBwYWdldmlld3NEYXRhJHVyaV9xdWVyeSA8LSBnc3ViKCI/Y2FtcGFpZ249IiwgIiIsIHBhZ2V2aWV3c0RhdGEkdXJpX3F1ZXJ5LCBmaXhlZCA9IFQpCiAgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnlbcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkgPT0gIiJdIDwtIAogICAgcGFnZXZpZXdzRGF0YSRyZWZlcmVyW3BhZ2V2aWV3c0RhdGEkdXJpX3F1ZXJ5ID09ICIiXQogIHBhZ2V2aWV3c0RhdGEgPC0gZHBseXI6OmZpbHRlcihwYWdldmlld3NEYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5ICE9ICIiKQogIHBhZ2V2aWV3c0RhdGEkcmVmZXJlciA8LSBOVUxMCiAgIyAtIGNsZWFuIHVwIGEgYml0OgogIHBhZ2V2aWV3c0RhdGEkdXJpX3F1ZXJ5IDwtIGdzdWIoIi8uKiQiLCAiIiwgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkpCiAgCiAgIyAtIGFnZ3JlZ2F0ZToKICBwYWdldmlld3NEYXRhIDwtIHBhZ2V2aWV3c0RhdGEgJT4lIAogICAgZHBseXI6Omdyb3VwX2J5KHVyaV9xdWVyeSwgdXJpX3BhdGgpICU+JSAKICAgIGRwbHlyOjpzdW1tYXJpc2UocGFnZXZpZXdzID0gbigpKQogIGNvbG5hbWVzKHBhZ2V2aWV3c0RhdGEpIDwtIGMoJ1RhZycsICdQYWdlJywgJ1BhZ2V2aWV3cycpCiAgCiAgIyAtIGFkZCBjZXREYXksIGNhbXBhaWduTmFtZQogIHBhZ2V2aWV3c0RhdGEkZGF0ZSA8LSBjZXREYXkKICBwYWdldmlld3NEYXRhJGNhbXBhaWduIDwtIGNhbXBhaWduTmFtZQogIAogICMgLSBzdG9yZToKICB3cml0ZS5jc3YocGFnZXZpZXdzRGF0YSwgCiAgICAgICAgICAgIHBhc3RlMCgicGFnZXZpZXdzQWdncmVnYXRlZF8iLAogICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoCiAgICAgICAgICAgICAgICAgICAgIHN0cnNwbGl0KGZpbGVOYW1lLCBzcGxpdCA9ICJfIiwgZml4ZWQgPSBUKVtbMV1dWzJdLAogICAgICAgICAgICAgICAgICAgICBzcGxpdCA9ICIuIiwgCiAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXSwKICAgICAgICAgICAgICAgICAgICIuY3N2IgogICAgICAgICAgICApCiAgKQogIAp9CgojIC0gc2V0IHBhcmFtcyB0byB3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzCiMgLSBmb3IgdGhlIEF1dHVtbiBCYW5uZXIgQ2FtcGFpZ24gMjAxOQp1cmlfcXVlcnlfZmlsdGVyIDwtICdXTURFX25ld2VkaXRvcnNfYXV0dW1uXzIwMTknCgojIC0gd3JhbmdsZSBwYWdldmlld3MKd21kZV9wcm9jZXNzX3BhZ2V2aWV3cyhmaWxlTmFtZSA9IGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIgPSBkYXRhRGlyLAogICAgICAgICAgICAgICAgICAgICAgIHVyaV9xdWVyeV9maWx0ZXIgPSB1cmlfcXVlcnlfZmlsdGVyLCAKICAgICAgICAgICAgICAgICAgICAgICBjZXREYXkgPSBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lID0gY2FtcGFpZ25OYW1lKSAKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gVXNlciBSZWdpc3RyYXRpb25zCiMjIyAtLS0gSGl2ZVFMIHF1ZXJ5OiBldmVudC5TZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uIHRhYmxlCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmhpdmVRTCA8LSAKICAiU0VMRUNUIHllYXIsIG1vbnRoLCBkYXksIGhvdXIsICBldmVudC5jYW1wYWlnbiwgZXZlbnQudXNlcklkLCBldmVudC51c2VyTmFtZSAKICAgIEZST00gZXZlbnQuc2VydmVyc2lkZWFjY291bnRjcmVhdGlvbiAKICAgIFdIRVJFIAogICAgICB5ZWFyID0gMjAxOSAKICAgICAgQU5EIG1vbnRoID49IDExIAogICAgICBBTkQgZGF5ID49IDEgCiAgICAgIEFORCAoZXZlbnQuY2FtcGFpZ24gTElLRSAnJVdNREVfbmV3ZWRpdG9yc19hdXR1bW5fMjAxOSUnKTsiCiMgLSB3cml0ZSBocWwKd3JpdGUoaGl2ZVFMLCBwYXN0ZTAoZGF0YURpciwgJ3VzZXJfcmVnaXN0cmF0aW9ucy5ocWwnKSkKIyMjIC0tLSBvdXRwdXQgZmlsZW5hbWUKZmlsZW5hbWUgPC0gcGFzdGUwKGRhdGFEaXIsICd1c2VyX3JlZ2lzdHJhdGlvbnMudHN2JykKIyMjIC0tLSBleGVjdXRlIGhxbCBzY3JpcHQ6CmhpdmVBcmdzIDwtICcvdXNyL2xvY2FsL2Jpbi9iZWVsaW5lIC0tc2lsZW50IC0taW5jcmVtZW50YWw9dHJ1ZSAtLXZlcmJvc2U9ZmFsc2UgLWYnCmhpdmVJbnB1dCA8LSBwYXN0ZShwYXN0ZTAoZGF0YURpciwgJ3VzZXJfcmVnaXN0cmF0aW9ucy5ocWwnKSwKICAgICAgICAgICAgICAgICAgICIgPiAiLAogICAgICAgICAgICAgICAgICAgZmlsZW5hbWUsCiAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKIyAtIGNvbW1hbmQ6CmhpdmVDb21tYW5kIDwtIHBhc3RlKGhpdmVBcmdzLCBoaXZlSW5wdXQpCnN5c3RlbShjb21tYW5kID0gaGl2ZUNvbW1hbmQsIHdhaXQgPSBUUlVFKQpgYGAKCiMjIyAwLjIgRGF0YSBBZ2dyZWdhdGlvbgoKKipOT1RFOioqIE5vdCBydW4gZnJvbSB0aGlzIHJlcG9ydDsgdGhlIGRhdGEgd2VyZSBhbHJlYWR5IHByZS1wcm9jZXNzZWQgYW5kIGFnZ3JlZ2F0ZWQgYnkgdGhlIGZvbGxvd2luZyBgUmAgc2NyaXB0IGJlZm9yZSBiZWluZyBzdWJtaXR0ZWQgdG8gYW5hbHl0aWNhbCBwcm9jZWR1cmVzOgoKYGBge3IsIGV2YWwgPSBGfQojICFkaWFnbm9zdGljcyBvZmYKIyMjIC0tLSBzY3JpcHQ6IEFCQ18yMDE5X0FnZ3JlZ2F0aW9uLlIKIyMjIC0tLSBEYXRhIEFnZ3JlZ2F0aW9uIGZvciB0aGUgQXV0dW1uIEJhbm5lciBDYW1wYWlnbiAyMDE5CiMjIyAtLS0gcnVuIGZyb20gc3RhdDEwMDQKIyMjIC0tLSAvaG9tZS9nb3JhbnNtL0FuYWx5dGljcy9OZXdFZGl0b3JzL0NhbXBhaWducy8yMDE5QXVCQy8KCiMjIyAtLS0gZGlyIHN0cnVjdHVyZQpjYW1wYWlnblBhdGggPC0gJy9ob21lL2dvcmFuc20vQW5hbHl0aWNzL05ld0VkaXRvcnMvQ2FtcGFpZ25zLzIwMTlBdUJDLycKZGF0YURpciA8LSBwYXN0ZTAoY2FtcGFpZ25QYXRoLCAiX2RhdGEvIikKYW5hbHl0aWNzRGlyIDwtIHBhc3RlMChjYW1wYWlnblBhdGgsICJfYW5hbHl0aWNzLyIpCgojIyMgLS0tIFJlcG9ydCBCYW5uZXIgSW1wcmVzc2lvbiBEYXRhCgojIC0gZnVuY3Rpb246IHdtZGVfcmVwb3J0X2Jhbm5lcl9pbXByZXNzaW9ucwp3bWRlX3JlcG9ydF9iYW5uZXJfaW1wcmVzc2lvbnMgPC0gZnVuY3Rpb24oZGF0YURpcikgewogIAogICMgLSBTZXR1cAogIGxpYnJhcnkoZGF0YS50YWJsZSkKICBsaWJyYXJ5KGRwbHlyKQogIAogICMgLSBsaXN0IGZpbGVzOgogIGxGIDwtIGxpc3QuZmlsZXMoZGF0YURpcikKICAKICAjIC0gZmlsdGVyIGFnZ3JlZ2F0ZWQgYmFubmVyIGltcHJlc3Npb24gZGF0YQogIGxGIDwtIGxGW2dyZXBsKCJiYW5uZXJJbXByZXNzaW9uc0FnZ3JlZ2F0ZWRfIiwgbEYsIGZpeGVkID0gVCldCiAgCiAgIyAtIGxvYWQgZmlsZXMgYW5kIG1lcmdlCiAgYmFubmVyRGF0YSA8LSB2ZWN0b3IobW9kZSA9ICJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKGxGKSkKICBmb3IgKGkgaW4gMTpsZW5ndGgobEYpKSB7CiAgICBpZiAoZ3JlcGwoImNzdiR8dHN2JCIsIGxGW2ldKSkgewogICAgICBiYW5uZXJEYXRhW1tpXV0gPC0gZnJlYWQocGFzdGUwKGRhdGFEaXIsIGxGW2ldKSkKICAgIH0gZWxzZSB7CiAgICAgIGJhbm5lckRhdGFbW2ldXSA8LSBOVUxMCiAgICB9CiAgfQogIGJhbm5lckRhdGEgPC0gcmJpbmRsaXN0KGJhbm5lckRhdGEpCiAgYmFubmVyRGF0YSRWMSA8LSBOVUxMCiAgCiAgIyAtIGFnZ3JlZ2F0ZXMKICBwZXJCYW5uZXJUb3RhbHMgPC0gYmFubmVyRGF0YSAlPiUgCiAgICBzZWxlY3QoYmFubmVyLCBpbXByZXNzaW9ucykgJT4lIAogICAgZ3JvdXBfYnkoYmFubmVyKSAlPiUgCiAgICBzdW1tYXJpc2UodG90YWxJbXByZXNzaW9ucyA9IHN1bShpbXByZXNzaW9ucykpCiAgcGVyRGF5VG90YWxzIDwtIGJhbm5lckRhdGEgJT4lIAogICAgc2VsZWN0KGRhdGUsIGltcHJlc3Npb25zKSAlPiUgCiAgICBncm91cF9ieShkYXRlKSAlPiUgCiAgICBzdW1tYXJpc2UodG90YWxJbXByZXNzaW9ucyA9IHN1bShpbXByZXNzaW9ucykpCiAgCiAgIyAtIG91dHB1dAogIHJldHVybigKICAgIGxpc3QoYmFubmVySW1wcmVzc2lvbnNSZXBvcnQgPSBiYW5uZXJEYXRhLCAKICAgICAgICAgcGVyQmFubmVyVG90YWxzID0gcGVyQmFubmVyVG90YWxzLCAKICAgICAgICAgcGVyRGF5VG90YWxzID0gcGVyRGF5VG90YWxzKQogICkKICAKfQoKIyAtIFJlcG9ydCBiYW5uZXIgaW1wcmVzc2lvbnMKYmFubmVySW1wcmVzc2lvbnNEYXRhIDwtIHdtZGVfcmVwb3J0X2Jhbm5lcl9pbXByZXNzaW9ucyhkYXRhRGlyKQpiYW5uZXJJbXByZXNzaW9uc0ZpbGUgPC0gYmFubmVySW1wcmVzc2lvbnNEYXRhJGJhbm5lckltcHJlc3Npb25zUmVwb3J0CndyaXRlLmNzdihiYW5uZXJJbXByZXNzaW9uc0ZpbGUsIHBhc3RlMChhbmFseXRpY3NEaXIsICJiYW5uZXJJbXByZXNzaW9uc0ZpbGUuY3N2IikpCmJhbm5lclRvdGFscyA8LSBiYW5uZXJJbXByZXNzaW9uc0RhdGEkcGVyQmFubmVyVG90YWxzCndyaXRlLmNzdihiYW5uZXJUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJiYW5uZXJUb3RhbHMuY3N2IikpCmJhbm5lckRheVRvdGFscyA8LSBiYW5uZXJJbXByZXNzaW9uc0RhdGEkcGVyRGF5VG90YWxzCndyaXRlLmNzdihiYW5uZXJEYXlUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJiYW5uZXJEYXlUb3RhbHMuY3N2IikpCgoKIyMjIC0tLSBSZXBvcnQgUGFnZXZpZXdzIERhdGEKCiMgLSBmdW5jdGlvbjogd21kZV9yZXBvcnRfcGFnZXZpZXdzCndtZGVfcmVwb3J0X3BhZ2V2aWV3cyA8LSBmdW5jdGlvbihkYXRhRGlyKSB7CiAgCiAgIyAtIFNldHVwCiAgbGlicmFyeShkYXRhLnRhYmxlKQogIGxpYnJhcnkoZHBseXIpCiAgCiAgIyAtIGxpc3QgZmlsZXM6CiAgbEYgPC0gbGlzdC5maWxlcyhkYXRhRGlyKQogIAogICMgLSBmaWx0ZXIgYWdncmVnYXRlZCBiYW5uZXIgaW1wcmVzc2lvbiBkYXRhCiAgbEYgPC0gbEZbZ3JlcGwoInBhZ2V2aWV3c0FnZ3JlZ2F0ZWRfIiwgbEYsIGZpeGVkID0gVCldCiAgCiAgIyAtIGxvYWQgZmlsZXMgYW5kIG1lcmdlCiAgcGFnZXZpZXdzRGF0YSA8LSB2ZWN0b3IobW9kZSA9ICJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKGxGKSkKICBmb3IgKGkgaW4gMTpsZW5ndGgobEYpKSB7CiAgICBpZiAoZ3JlcGwoImNzdiR8dHN2JCIsIGxGW2ldKSkgewogICAgICBwYWdldmlld3NEYXRhW1tpXV0gPC0gZnJlYWQocGFzdGUwKGRhdGFEaXIsIGxGW2ldKSkKICAgIH0gZWxzZSB7CiAgICAgIHBhZ2V2aWV3c0RhdGFbW2ldXSA8LSBOVUxMCiAgICB9CiAgfQogIHBhZ2V2aWV3c0RhdGEgPC0gcmJpbmRsaXN0KHBhZ2V2aWV3c0RhdGEpCiAgcGFnZXZpZXdzRGF0YSRWMSA8LSBOVUxMCiAgCiAgIyAtIGFnZ3JlZ2F0ZXMKICBwZXJEYXlUb3RhbHMgPC0gcGFnZXZpZXdzRGF0YSAlPiUgCiAgICBzZWxlY3QoZGF0ZSwgUGFnZXZpZXdzKSAlPiUgCiAgICBncm91cF9ieShkYXRlKSAlPiUgCiAgICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKICBwZXJUYWdUb3RhbHMgPC0gcGFnZXZpZXdzRGF0YSAlPiUgCiAgICBzZWxlY3QoVGFnLCBQYWdldmlld3MpICU+JSAKICAgIGdyb3VwX2J5KFRhZykgJT4lIAogICAgc3VtbWFyaXNlKHRvdGFsUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCiAgcGVyUGFnZVRvdGFscyA8LSBwYWdldmlld3NEYXRhICU+JSAKICAgIHNlbGVjdChQYWdlLCBQYWdldmlld3MpICU+JSAKICAgIGdyb3VwX2J5KFBhZ2UpICU+JSAKICAgIHN1bW1hcmlzZSh0b3RhbFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQogIHBlclBhZ2VEYXlUb3RhbHMgPC0gcGFnZXZpZXdzRGF0YSAlPiUgCiAgICBzZWxlY3QoUGFnZSwgZGF0ZSwgUGFnZXZpZXdzKSAlPiUKICAgIGdyb3VwX2J5KFBhZ2UsIGRhdGUpICU+JSAKICAgIHN1bW1hcmlzZSh0b3RhbFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQogIHBlclRhZ0RheVRvdGFscyA8LSBwYWdldmlld3NEYXRhICU+JSAKICAgIHNlbGVjdChUYWcsIGRhdGUsIFBhZ2V2aWV3cykgJT4lCiAgICBncm91cF9ieShUYWcsIGRhdGUpICU+JSAKICAgIHN1bW1hcmlzZSh0b3RhbFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQogIHBlclRhZ1BhZ2VUb3RhbHMgPC0gcGFnZXZpZXdzRGF0YSAlPiUgCiAgICBzZWxlY3QoVGFnLCBQYWdlLCBQYWdldmlld3MpICU+JQogICAgZ3JvdXBfYnkoVGFnLCBQYWdlKSAlPiUgCiAgICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKICAKICAjIC0gb3V0cHV0CiAgcmV0dXJuKAogICAgbGlzdChwYWdldmlld3NEYXRhUmVwb3J0ID0gcGFnZXZpZXdzRGF0YSwgCiAgICAgICAgIHBlckRheVRvdGFscyA9IHBlckRheVRvdGFscywgCiAgICAgICAgIHBlclRhZ1RvdGFscyA9IHBlclRhZ1RvdGFscywgCiAgICAgICAgIHBlclBhZ2VUb3RhbHMgPSBwZXJQYWdlVG90YWxzLCAKICAgICAgICAgcGVyUGFnZURheVRvdGFscyA9IHBlclBhZ2VEYXlUb3RhbHMsIAogICAgICAgICBwZXJUYWdEYXlUb3RhbHMgPSBwZXJUYWdEYXlUb3RhbHMsCiAgICAgICAgIHBlclRhZ1BhZ2VUb3RhbHMgPSBwZXJUYWdQYWdlVG90YWxzKQogICkKICAKfQoKIyAtIFJlcG9ydCBwYWdldmlld3M6CnBhZ2V2aWV3c0RhdGEgPC0gd21kZV9yZXBvcnRfcGFnZXZpZXdzKGRhdGFEaXIpCnBhZ2V2aWV3c1JlcG9ydEZpbGUgPC0gcGFnZXZpZXdzRGF0YSRwYWdldmlld3NEYXRhUmVwb3J0CndyaXRlLmNzdihwYWdldmlld3NSZXBvcnRGaWxlLCBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAicGFnZXZpZXdzUmVwb3J0RmlsZS5jc3YiKSkKcGFnZXZpZXdzX3BlckRheVRvdGFscyA8LSBwYWdldmlld3NEYXRhJHBlckRheVRvdGFscwp3cml0ZS5jc3YocGFnZXZpZXdzX3BlckRheVRvdGFscywgcGFzdGUwKGFuYWx5dGljc0RpciwgInBhZ2V2aWV3c19wZXJEYXlUb3RhbHMuY3N2IikpCnBhZ2V2aWV3c19wZXJUYWdUb3RhbHMgPC0gcGFnZXZpZXdzRGF0YSRwZXJUYWdUb3RhbHMKd3JpdGUuY3N2KHBhZ2V2aWV3c19wZXJUYWdUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJwYWdldmlld3NfcGVyVGFnVG90YWxzLmNzdiIpKQpwYWdldmlld3NfcGVyUGFnZVRvdGFscyA8LSBwYWdldmlld3NEYXRhJHBlclBhZ2VUb3RhbHMKd3JpdGUuY3N2KHBhZ2V2aWV3c19wZXJQYWdlVG90YWxzLCBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAicGFnZXZpZXdzX3BlclBhZ2VUb3RhbHMuY3N2IikpCnBhZ2V2aWV3c19wZXJQYWdlRGF5VG90YWxzIDwtIHBhZ2V2aWV3c0RhdGEkcGVyUGFnZURheVRvdGFscwp3cml0ZS5jc3YocGFnZXZpZXdzX3BlclBhZ2VEYXlUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJwYWdldmlld3NfcGVyUGFnZURheVRvdGFscy5jc3YiKSkKcGFnZXZpZXdzX3BlclRhZ0RheVRvdGFscyA8LSBwYWdldmlld3NEYXRhJHBlclRhZ0RheVRvdGFscwp3cml0ZS5jc3YocGFnZXZpZXdzX3BlclRhZ0RheVRvdGFscywgcGFzdGUwKGFuYWx5dGljc0RpciwgInBhZ2V2aWV3c19wZXJUYWdEYXlUb3RhbHMuY3N2IikpCnBhZ2V2aWV3c19wZXJUYWdQYWdlVG90YWxzIDwtIHBhZ2V2aWV3c0RhdGEkcGVyVGFnUGFnZVRvdGFscwp3cml0ZS5jc3YocGFnZXZpZXdzX3BlclRhZ1BhZ2VUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJwZXJUYWdQYWdlVG90YWxzLmNzdiIpKQoKIyMjIC0tLSBSZXBvcnQgVXNlciBSZWdpc3RyYXRpb25zCgojIC0gZnVuY3Rpb246IHdtZGVfcmVwb3J0X3JlZ2lzdHJhdGlvbnNfaGl2ZQp3bWRlX3JlcG9ydF9yZWdpc3RyYXRpb25zX2hpdmUgPC0gZnVuY3Rpb24oZGF0YURpciwgY2FtcGFpZ24pIHsKICAKICAjIC0gU2V0dXAKICBsaWJyYXJ5KGRhdGEudGFibGUpCiAgbGlicmFyeShkcGx5cikKICAKICAjIC0gbGlzdCBmaWxlczoKICBsRiA8LSBsaXN0LmZpbGVzKGRhdGFEaXIpCiAgCiAgIyAtIGZpbHRlciBhZ2dyZWdhdGVkIHVzZXIgcmVnaXN0cmF0aW9uIGRhdGEKICBsRiA8LSBsRltncmVwbCgidXNlcl9yZWdpc3RyYXRpb25zLnRzdiIsIGxGLCBmaXhlZCA9IFQpXQogIAogICMgLSBsb2FkIGZpbGUgYW5kIHdyYW5nbGUgY29sdW1ucwogIHJlZ2lzdHJhdGlvbkRhdGEgPC0gZnJlYWQocGFzdGUwKGRhdGFEaXIsIGxGKSwgaGVhZGVyID0gVCkKICBjb2xuYW1lcyhyZWdpc3RyYXRpb25EYXRhKVs1XSA8LSAnZXZlbnRfY2FtcGFpZ24nCiAgY29sbmFtZXMocmVnaXN0cmF0aW9uRGF0YSlbNl0gPC0gJ2V2ZW50X3VzZXJJZCcKICByZWdpc3RyYXRpb25EYXRhJGNhbXBhaWduIDwtIGNhbXBhaWduCiAgcmVnaXN0cmF0aW9uRGF0YSRkYXkgPC0gaWZlbHNlKG5jaGFyKHJlZ2lzdHJhdGlvbkRhdGEkZGF5KSA9PSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIjAiLCByZWdpc3RyYXRpb25EYXRhJGRheSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWdpc3RyYXRpb25EYXRhJGRheSkKICByZWdpc3RyYXRpb25EYXRhJGRhdGUgPC0gcGFzdGUocmVnaXN0cmF0aW9uRGF0YSR5ZWFyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVnaXN0cmF0aW9uRGF0YSRtb250aCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVnaXN0cmF0aW9uRGF0YSRkYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICItIikKCiAgIyAtIGFnZ3JlZ2F0ZXMKICBwZXJEYXlUb3RhbHMgPC0gcmVnaXN0cmF0aW9uRGF0YSAlPiUgCiAgICBzZWxlY3QoZGF0ZSkgJT4lIAogICAgZ3JvdXBfYnkoZGF0ZSkgJT4lIAogICAgc3VtbWFyaXNlKHRvdGFsUmVnaXN0cmF0aW9ucyA9IG4oKSkKICBwZXJUYWdUb3RhbHMgPC0gcmVnaXN0cmF0aW9uRGF0YSAlPiUgCiAgICBzZWxlY3QoZXZlbnRfY2FtcGFpZ24pICU+JSAKICAgIGdyb3VwX2J5KGV2ZW50X2NhbXBhaWduKSAlPiUgCiAgICBzdW1tYXJpc2UodG90YWxSZWdpc3RyYXRpb25zID0gbigpKQogIHBlclRhZ0RheVRvdGFscyA8LSByZWdpc3RyYXRpb25EYXRhICU+JSAKICAgIHNlbGVjdChldmVudF9jYW1wYWlnbiwgZGF0ZSkgJT4lIAogICAgZ3JvdXBfYnkoZXZlbnRfY2FtcGFpZ24sIGRhdGUpICU+JSAKICAgIHN1bW1hcmlzZSh0b3RhbFJlZ2lzdHJhdGlvbnMgPSBuKCkpCiAgCiAgIyAtIGZ1bGwgcmVnaXN0cmF0aW9uIGRhdGFzZXQ6IG5vbi1hZ2dyZWdhdGVkIHJlZ2lzdHJhdGlvbnMKICAjIC0gZmlsdGVyIGRhdGE6CiAgZnVsbFJlZ0RhdGEgPC0gcmVnaXN0cmF0aW9uRGF0YSAlPiUgCiAgICBzZWxlY3QoZXZlbnRfdXNlcklkLCAKICAgICAgICAgICBldmVudF9jYW1wYWlnbiwgCiAgICAgICAgICAgZGF0ZSwgCiAgICAgICAgICAgY2FtcGFpZ24pCiAgCiAgIyAtIG91dHB1dAogIHJldHVybigKICAgIGxpc3QocmVnaXN0cmF0aW9uc0RhdGFSZXBvcnQgPSByZWdpc3RyYXRpb25EYXRhLCAKICAgICAgICAgcGVyRGF5VG90YWxzID0gcGVyRGF5VG90YWxzLCAKICAgICAgICAgcGVyVGFnVG90YWxzID0gcGVyVGFnVG90YWxzLCAKICAgICAgICAgcGVyVGFnRGF5VG90YWxzID0gcGVyVGFnRGF5VG90YWxzLAogICAgICAgICBmdWxsUmVnaXN0cmF0aW9uRGF0YXNldCA9IGZ1bGxSZWdEYXRhKQogICkKICAKfQoKIyAtIFJlcG9ydCB1cG9uIHVzZXIgcmVnaXN0cmF0aW9ucwpjYW1wYWlnbiA8LSAnMjAxOV9BdUJDJwp1c2VyUmVnRGF0YSA8LSB3bWRlX3JlcG9ydF9yZWdpc3RyYXRpb25zX2hpdmUoZGF0YURpciwgY2FtcGFpZ24pCnVzZXJSZWdpc3RyYXRpb25zUmVwb3J0RmlsZSA8LSB1c2VyUmVnRGF0YSRyZWdpc3RyYXRpb25zRGF0YVJlcG9ydAp3cml0ZS5jc3YodXNlclJlZ2lzdHJhdGlvbnNSZXBvcnRGaWxlLCBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAidXNlclJlZ2lzdHJhdGlvbnNSZXBvcnRGaWxlLmNzdiIpKQp1c2VyUmVnaXN0cmF0aW9uc19wZXJEYXlUb3RhbHMgPC0gdXNlclJlZ0RhdGEkcGVyRGF5VG90YWxzCndyaXRlLmNzdih1c2VyUmVnaXN0cmF0aW9uc19wZXJEYXlUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJ1c2VyUmVnaXN0cmF0aW9uc19wZXJEYXlUb3RhbHMuY3N2IikpCnVzZXJSZWdpc3RyYXRpb25zX3BlclRhZ1RvdGFscyA8LSB1c2VyUmVnRGF0YSRwZXJUYWdUb3RhbHMKd3JpdGUuY3N2KHVzZXJSZWdpc3RyYXRpb25zX3BlclRhZ1RvdGFscywgcGFzdGUwKGFuYWx5dGljc0RpciwgInVzZXJSZWdpc3RyYXRpb25zX3BlclRhZ1RvdGFscy5jc3YiKSkKdXNlclJlZ2lzdHJhdGlvbnNfcGVyVGFnRGF5VG90YWxzIDwtIHVzZXJSZWdEYXRhJHBlclRhZ0RheVRvdGFscwp3cml0ZS5jc3YodXNlclJlZ2lzdHJhdGlvbnNfcGVyVGFnRGF5VG90YWxzLCBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAidXNlclJlZ2lzdHJhdGlvbnNfcGVyVGFnRGF5VG90YWxzLmNzdiIpKQpmdWxsVXNlclJlZ2lzdHJhdGlvbnMgPC0gdXNlclJlZ0RhdGEkZnVsbFJlZ2lzdHJhdGlvbkRhdGFzZXQKd3JpdGUuY3N2KGZ1bGxVc2VyUmVnaXN0cmF0aW9ucywgcGFzdGUwKGFuYWx5dGljc0RpciwgImZ1bGxSZWdpc3RyYXRpb25EYXRhc2V0LmNzdiIpKQoKCmBgYAoKIyMjIDAuMyBVc2VyIEVkaXRzCgojIyMjIDAuMy4xIFVzZXIgRWRpdHMgdmlhIGRld2lraS5yZXZpc2lvbiAtIEZBSUxFRAoKYGBge3IsIGV2YWwgPSBGfQpsaWJyYXJ5KGRhdGEudGFibGUpCiMgIWRpYWdub3N0aWNzIG9mZgojIyMgLS0tIHNjcmlwdDogQUJDXzIwMTlfVXNlckVkaXRzLlIKIyMjIC0tLSBVc2VyIEVkaXRzIEFjcXVpc2l0aW9uIGZvciB0aGUgQXV0dW1uIEJhbm5lciBDYW1wYWlnbiAyMDE5CiMjIyAtLS0gcnVuIGZyb20gc3RhdDEwMDQKIyMjIC0tLSAvaG9tZS9nb3JhbnNtL0FuYWx5dGljcy9OZXdFZGl0b3JzL0NhbXBhaWducy8yMDE5QXVCQy8KIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gVXNlciBFZGl0cwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBkaXIgc3RydWN0dXJlCmNhbXBhaWduUGF0aCA8LSAnL2hvbWUvZ29yYW5zbS9BbmFseXRpY3MvTmV3RWRpdG9ycy9DYW1wYWlnbnMvMjAxOUF1QkMvJwpkYXRhRGlyIDwtIHBhc3RlMChjYW1wYWlnblBhdGgsICJfZGF0YS8iKQphbmFseXRpY3NEaXIgPC0gcGFzdGUwKGNhbXBhaWduUGF0aCwgIl9hbmFseXRpY3MvIikKc3RhcnRUaW1lc3RhbXAgPC0gJzIwMTkxMTAxMDAwMDAwJwojIC0gZ2V0IHVzZXIgaWRzCnVzZXJSZWdpc3RyYXRpb25zIDwtIHJlYWQuY3N2KHBhc3RlMChhbmFseXRpY3NEaXIsICd1c2VyUmVnaXN0cmF0aW9uc1JlcG9ydEZpbGUuY3N2JykpCnJldl91c2VyIDwtIHVzZXJSZWdpc3RyYXRpb25zJGV2ZW50X3VzZXJJZApyZXZfdXNlcl90ZXh0IDwtIGFzLmNoYXJhY3Rlcih1c2VyUmVnaXN0cmF0aW9ucyR1c2VybmFtZSkKIyAtIGl0ZXJhdGUgb3ZlciByZXZfdXNlcgpmb3IgKGkgaW4gMTpsZW5ndGgocmV2X3VzZXIpKSB7CiAgIyAtIGNoZWNrIHVzZXJuYW1lCiAgaWYgKGdyZXBsKCInIiwgcmV2X3VzZXJfdGV4dFtpXSwgZml4ZWQgPSBUKSkgewogICAgcmV2X3VzZXJfdGV4dFtpXSA8LSBnc3ViKCInIiwgIlxcJyIsIHJldl91c2VyX3RleHRbaV0sIGZpeGVkID0gVCkKICB9CiAgIyAtIFNRTCBxdWVyeQogIHNxbFF1ZXJ5IDwtIHBhc3RlKCJcIlNFTEVDVCByZXZfdXNlciwgcmV2X3RpbWVzdGFtcCBGUk9NIGRld2lraS5yZXZpc2lvbiBXSEVSRSAocmV2X3RpbWVzdGFtcCA+PSAiLCAKICAgICAgICAgICAgICAgICAgICBzdGFydFRpbWVzdGFtcCwgIiBBTkQgcmV2X3VzZXJfdGV4dCA9ICciLCByZXZfdXNlcl90ZXh0W2ldLCAiJyk7XCIiLCBzZXAgPSAiIikKICAjIyMgLS0tIG91dHB1dCBmaWxlbmFtZQogIGZpbGVuYW1lIDwtIHBhc3RlKGRhdGFEaXIsJ3VzZXJFZGl0cycsICJfIiwgcmV2X3VzZXJbaV0sICIudHN2Iiwgc2VwID0gIiIpCiAgIyMjIC0tLSBleGVjdXRlIHNxbCBzY3JpcHQ6CiAgc3FsTG9nSW5QcmUgPC0gcGFzdGUwKCcvdXNyL2xvY2FsL2Jpbi9hbmFseXRpY3MtbXlzcWwgZGV3aWtpIC1lICcpCiAgc3FsSW5wdXQgPC0gcGFzdGUoc3FsUXVlcnksCiAgICAgICAgICAgICAgICAgICAgIiA+ICIsCiAgICAgICAgICAgICAgICAgICAgZmlsZW5hbWUsCiAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpCiAgIyAtIGNvbW1hbmQ6CiAgc3FsQ29tbWFuZCA8LSBwYXN0ZShzcWxMb2dJblByZSwgc3FsSW5wdXQpCiAgc3lzdGVtKGNvbW1hbmQgPSBzcWxDb21tYW5kLCB3YWl0ID0gVFJVRSkKICAjIC0gcmVwb3J0CiAgcHJpbnQocGFzdGUwKCJET05FOiB1c2VyICIsIGksICIuIikpCn0KIyMjIC0tLSBFTkQgcnVuIFNRTCBzY3JpcHRzCiMgLSBsb2FkIHVzZXIgZWRpdHM6CmxGIDwtIGxpc3QuZmlsZXMoZGF0YURpcikKbEYgPC0gbEZbZ3JlcGwoIl51c2VyRWRpdHNfIiwgbEYpXQp1c2VyRWRpdHMgPC0gbGFwcGx5KHBhc3RlMChkYXRhRGlyLCBsRiksIGZyZWFkKQp1c2VyRWRpdHMgPC0gcmJpbmRsaXN0KHVzZXJFZGl0cykKIyAtIHN0b3JlIHVzZXIgZWRpdHM6CndyaXRlLmNzdih1c2VyRWRpdHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICd1c2VyRWRpdHMuY3N2JykpCgojIyMgLS0tIENoZWNrIHVzZXIgZWRpdHMKIyAtIHNlbGVjdCBhbGwgbm9uLWFub255bW91cyB1c2VyIGVkaXRzIG9uIGRld2lraSB3aGVyZSB0aW1lc3RhbXAgPiAyMDE5MTEwMTAwMDAwMAojIC0gU1FMIHF1ZXJ5CnNxbFF1ZXJ5IDwtIHBhc3RlKCJcIlNFTEVDVCByZXZfYWN0b3IgRlJPTSBkZXdpa2kucmV2aXNpb24gV0hFUkUgKHJldl90aW1lc3RhbXAgPj0gIiwgCiAgICAgICAgICAgICAgICAgIHN0YXJ0VGltZXN0YW1wLCAiKTtcIiIsIHNlcCA9ICIiKQojIyMgLS0tIG91dHB1dCBmaWxlbmFtZQpmaWxlbmFtZSA8LSBwYXN0ZTAoZGF0YURpciwgJ3VzZXJFZGl0c0FMTC50c3YnKQojIyMgLS0tIGV4ZWN1dGUgc3FsIHNjcmlwdDoKc3FsTG9nSW5QcmUgPC0gcGFzdGUwKCcvdXNyL2xvY2FsL2Jpbi9hbmFseXRpY3MtbXlzcWwgZGV3aWtpIC1lICcpCnNxbElucHV0IDwtIHBhc3RlKHNxbFF1ZXJ5LAogICAgICAgICAgICAgICAgICAiID4gIiwKICAgICAgICAgICAgICAgICAgZmlsZW5hbWUsCiAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQojIC0gY29tbWFuZDoKc3FsQ29tbWFuZCA8LSBwYXN0ZShzcWxMb2dJblByZSwgc3FsSW5wdXQpCnN5c3RlbShjb21tYW5kID0gc3FsQ29tbWFuZCwgd2FpdCA9IFRSVUUpCiMgLSBsb2FkIHVzZXJFZGl0cwp1c2VyRWRpdHMgPC0gZnJlYWQocGFzdGUwKGRhdGFEaXIsICd1c2VyRWRpdHNBTEwudHN2JykpCnRFZGl0cyA8LSB0YWJsZShhcy5udW1lcmljKHVzZXJFZGl0cyRyZXZfdXNlcikpCmBgYAoKIyMjIyAwLjMuMiBVc2VyIEVkaXRzIHZpYSByZXZpc2lvbl9hY3Rvcl90ZW1wCgpgYGB7ciwgZXZhbCA9IEZ9CmxpYnJhcnkoZGF0YS50YWJsZSkKIyAhZGlhZ25vc3RpY3Mgb2ZmCiMjIyAtLS0gc2NyaXB0OiBBQkNfMjAxOV9Vc2VyRWRpdHNfd21mTWVkaWFXaWtpSGlzdG9yeS5SCiMjIyAtLS0gVXNlciBFZGl0cyBBY3F1aXNpdGlvbiBmb3IgdGhlIEF1dHVtbiBCYW5uZXIgQ2FtcGFpZ24gMjAxOQojIyMgLS0tIHJ1biBmcm9tIHN0YXQxMDA0CiMjIyAtLS0gL2hvbWUvZ29yYW5zbS9BbmFseXRpY3MvTmV3RWRpdG9ycy9DYW1wYWlnbnMvMjAxOUF1QkMvCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIFVzZXIgRWRpdHMKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gZGlyIHN0cnVjdHVyZQpjYW1wYWlnblBhdGggPC0gJy9ob21lL2dvcmFuc20vQW5hbHl0aWNzL05ld0VkaXRvcnMvQ2FtcGFpZ25zLzIwMTlBdUJDLycKZGF0YURpciA8LSBwYXN0ZTAoY2FtcGFpZ25QYXRoLCAiX2RhdGEvIikKYW5hbHl0aWNzRGlyIDwtIHBhc3RlMChjYW1wYWlnblBhdGgsICJfYW5hbHl0aWNzLyIpCnN0YXJ0VGltZXN0YW1wIDwtICcyMDE5MTEwMTAwMDAwMCcKIyAtIGdldCB1c2VyIGlkcwp1c2VyUmVnaXN0cmF0aW9ucyA8LSByZWFkLmNzdihwYXN0ZTAoYW5hbHl0aWNzRGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICd1c2VyUmVnaXN0cmF0aW9uc1JlcG9ydEZpbGUuY3N2JykpCnJldl91c2VyIDwtIHVzZXJSZWdpc3RyYXRpb25zJGV2ZW50X3VzZXJJZApyZXZfdXNlcl90ZXh0IDwtIGFzLmNoYXJhY3Rlcih1c2VyUmVnaXN0cmF0aW9ucyR1c2VybmFtZSkKIyAtIGl0ZXJhdGUgb3ZlciByZXZfdXNlcgpmb3IgKGkgaW4gMTpsZW5ndGgocmV2X3VzZXIpKSB7CiAgIyAtIFNRTCBxdWVyeQogIHNxbFF1ZXJ5IDwtIHBhc3RlKCJcIlNFTEVDVCBhY3Rvci5hY3Rvcl9pZCwgYWN0b3IuYWN0b3JfdXNlciwgYWN0b3IuYWN0b3JfbmFtZSwgcmV2aXNpb25fYWN0b3JfdGVtcC5yZXZhY3Rvcl90aW1lc3RhbXAgRlJPTSBhY3RvciBMRUZUIEpPSU4gcmV2aXNpb25fYWN0b3JfdGVtcCBPTiAoYWN0b3IuYWN0b3JfaWQgPSByZXZpc2lvbl9hY3Rvcl90ZW1wLnJldmFjdG9yX2FjdG9yKSBXSEVSRSAocmV2aXNpb25fYWN0b3JfdGVtcC5yZXZhY3Rvcl90aW1lc3RhbXAgPj0gMjAxOTExMDEwMDAwMDAgQU5EIGFjdG9yLmFjdG9yX3VzZXIgPSAiLCByZXZfdXNlcltpXSwgIik7XCIiKTsKICAjIyMgLS0tIG91dHB1dCBmaWxlbmFtZQogIGZpbGVuYW1lIDwtIHBhc3RlKGRhdGFEaXIsJ3VzZXJFZGl0cycsICJfIiwgaSwgIi50c3YiLCBzZXAgPSAiIikKICAjIyMgLS0tIGV4ZWN1dGUgc3FsIHNjcmlwdDoKICBzcWxMb2dJblByZSA8LSBwYXN0ZTAoJy91c3IvbG9jYWwvYmluL2FuYWx5dGljcy1teXNxbCBkZXdpa2kgLWUgJykKICBzcWxJbnB1dCA8LSBwYXN0ZShzcWxRdWVyeSwKICAgICAgICAgICAgICAgICAgICAiID4gIiwKICAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSwKICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAjIC0gY29tbWFuZDoKICBzcWxDb21tYW5kIDwtIHBhc3RlKHNxbExvZ0luUHJlLCBzcWxJbnB1dCkKICBzeXN0ZW0oY29tbWFuZCA9IHNxbENvbW1hbmQsIHdhaXQgPSBUUlVFKQogICMgLSByZXBvcnQKICBwcmludChwYXN0ZTAoIkRPTkU6IHVzZXIgIiwgaSwgIi4iKSkKfQojIyMgLS0tIEVORCBydW4gU1FMIHNjcmlwdHMKIyAtIGxvYWQgdXNlciBlZGl0czoKbEYgPC0gbGlzdC5maWxlcyhkYXRhRGlyKQpsRiA8LSBsRltncmVwbCgiXnVzZXJFZGl0c18iLCBsRildCnVzZXJFZGl0cyA8LSBsYXBwbHkocGFzdGUwKGRhdGFEaXIsIGxGKSwgZnJlYWQpCnVzZXJFZGl0cyA8LSByYmluZGxpc3QodXNlckVkaXRzKQojIC0gc3RvcmUgdXNlciBlZGl0czoKd3JpdGUuY3N2KHVzZXJFZGl0cywgcGFzdGUwKGFuYWx5dGljc0RpciwgJ3VzZXJFZGl0cy5jc3YnKSkKCiMjIyAtLS0gQ2hlY2sgdXNlciBlZGl0cwojIC0gc2VsZWN0IGFsbCBub24tYW5vbnltb3VzIHVzZXIgZWRpdHMgb24gZGV3aWtpIHdoZXJlIHRpbWVzdGFtcCA+IDIwMTkxMTAxMDAwMDAwCiMgLSBTUUwgcXVlcnkKc3FsUXVlcnkgPC0gcGFzdGUoIlwiU0VMRUNUIHJldmFjdG9yX2FjdG9yLCByZXZhY3Rvcl90aW1lc3RhbXAgRlJPTSByZXZpc2lvbl9hY3Rvcl90ZW1wIFdIRVJFIChyZXZhY3Rvcl90aW1lc3RhbXAgPj0gIiwgCiAgICAgICAgICAgICAgICAgICAgc3RhcnRUaW1lc3RhbXAsICIpO1wiIiwgc2VwID0gIiIpCiMjIyAtLS0gb3V0cHV0IGZpbGVuYW1lCmZpbGVuYW1lIDwtIHBhc3RlMChkYXRhRGlyLCAndXNlckVkaXRzQUxMLnRzdicpCiMjIyAtLS0gZXhlY3V0ZSBzcWwgc2NyaXB0OgpzcWxMb2dJblByZSA8LSBwYXN0ZTAoJy91c3IvbG9jYWwvYmluL2FuYWx5dGljcy1teXNxbCBkZXdpa2kgLWUgJykKc3FsSW5wdXQgPC0gcGFzdGUoc3FsUXVlcnksCiAgICAgICAgICAgICAgICAgICIgPiAiLAogICAgICAgICAgICAgICAgICBmaWxlbmFtZSwKICAgICAgICAgICAgICAgICAgc2VwID0gIiIpCiMgLSBjb21tYW5kOgpzcWxDb21tYW5kIDwtIHBhc3RlKHNxbExvZ0luUHJlLCBzcWxJbnB1dCkKc3lzdGVtKGNvbW1hbmQgPSBzcWxDb21tYW5kLCB3YWl0ID0gVFJVRSkKIyAtIGxvYWQgdXNlckVkaXRzCnVzZXJFZGl0cyA8LSBmcmVhZChwYXN0ZTAoZGF0YURpciwgJ3VzZXJFZGl0c0FMTC50c3YnKSkKdEVkaXRzIDwtIHRhYmxlKGFzLm51bWVyaWModXNlckVkaXRzJHJldmFjdG9yX2FjdG9yKSkKIyAtIGNhbXBhaWduIHJlZ2lzdGVyZWQgdXNlcnM/CndDVSA8LSB3aGljaChyZXZfdXNlciAlaW4lIG5hbWVzKHRFZGl0cykpCnJldl91c2VyW3dDVV0KYGBgCgoKIyMgMS4gQ2FtcGFpZ24gQmFubmVycyBhbmQgUGFnZXMKClRoaXMgc2VjdGlvbiBwcmVzZW50cyBhbGwgZGF0YSBhbmQgc3RhdGlzdGljcyBvbiB0aGUgY2FtcGFpZ24gYmFubmVycyBhbmQgcGFnZXMuCgojIyMgMS4xIEJhbm5lciBJbXByZXNzaW9ucwojIyMjIDEuMS4xIEJhbm5lciBJbXByZXNzaW9ucyBPdmVydmlldwoKKipDaGFydCAxLjEuMSoqIERhaWx5IEJhbm5lciBJbXByZXNzaW9ucwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmRhdGFTZXQgPC0gcmVhZC5jc3YoCiAgJ19hbmFseXRpY3MvYmFubmVySW1wcmVzc2lvbnNGaWxlLmNzdicsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICAKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn0KZ2dwbG90KGRhdGFTZXQsIGFlcyh4ID0gZGF0ZSwKICAgICAgICAgICAgICAgICAgICB5ID0gaW1wcmVzc2lvbnMsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGJhbm5lciwKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGltcHJlc3Npb25zLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdBdXR1bW4gQmFubmVyIENhbXBhaWduIDIwMTk6IEJhbm5lciBJbXByZXNzaW9ucycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKIyMjIyAxLjEuMSBCYW5uZXIgSW1wcmVzc2lvbnMgT3ZlcnZpZXc6IFRhYmxlCgoqKlRhYmxlIDEuMS4xLioqIERhaWx5IEJhbm5lciBJbXByZXNzaW9ucwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMjIyAtLS0gRnVsbCBEYXRhc2V0IChUYWJsZSBSZXBvcnQpCmRhdGF0YWJsZShkYXRhU2V0KQpgYGAKCiMjIyMgMS4xLjIgVG90YWwgQmFubmVyIEltcHJlc3Npb25zCioqQ2hhcnQgMS4xLjIuKiogVG90YWwgQmFubmVyIEltcHJlc3Npb25zCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KZGF0YVNldCA8LSByZWFkLmNzdigKICAnX2FuYWx5dGljcy9iYW5uZXJUb3RhbHMuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpnZ3Bsb3QoZGF0YVNldCwgYWVzKHggPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgIHkgPSB0b3RhbEltcHJlc3Npb25zLCAKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGJhbm5lciwgCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGJhbm5lciwgCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB0b3RhbEltcHJlc3Npb25zKSkgKyAKICBnZW9tX2Jhcih3aWR0aCA9IC41LCBzdGF0ID0gImlkZW50aXR5IikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdBdXR1bW4gQmFubmVyIENhbXBhaWduIDIwMTk6IEJhbm5lciBJbXByZXNzaW9ucycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX2xhYmVsX3JlcGVsKHNpemUgPSAzLjUsIGNvbG9yID0gIndoaXRlIiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIyMjIDEuMS4zIEJhbm5lciBJbXByZXNzaW9ucyBwZXIgRGF5CgoqKkNoYXJ0IDEuMS4zLioqIEJhbm5lciBJbXByZXNzaW9ucyBwZXIgRGF5CgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KZGF0YVNldCA8LSByZWFkLmNzdigKICAnX2FuYWx5dGljcy9iYW5uZXJEYXlUb3RhbHMuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpnZ3Bsb3QoZGF0YVNldCwgYWVzKHggPSBkYXRlLCAKICAgICAgICAgICAgICAgICAgICB5ID0gdG90YWxJbXByZXNzaW9ucywgCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB0b3RhbEltcHJlc3Npb25zKSkgKwogIGdlb21fcGF0aChzaXplID0gLjI1LCBncm91cCA9IDEsIGNvbG9yID0gImRhcmtibHVlIikgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgY29sb3IgPSAiZGFya2JsdWUiKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGNvbG9yID0gIndoaXRlIikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdBdXR1bW4gQmFubmVyIENhbXBhaWduIDIwMTk6IEJhbm5lciBJbXByZXNzaW9ucycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX2xhYmVsX3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpCmBgYAoKIyMjIDEuMiBQYWdldmlld3MKCiMjIyMgMS4yLjEgUGFnZXZpZXdzIE92ZXJ2aWV3CgoqKkNoYXJ0IDEuMi4xLioqIFBhZ2V2aWV3cyBPdmVydmlldy4gTG9nIHNjYWxpbmcgb2YgdGhlIHBhZ2V2aWV3cyBpcyBuZWNlc3Nhcnk7IHRoZSBudW1iZXJzIHJlcG9ydGVkIGluIHRoZSBkYXRhIHBvaW50IGxhYmVscyBhcmUgZXhhY3QuCgpgYGB7ciBldmFsPVQsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMiwgZWNobz1GQUxTRX0KZGF0YVNldCA8LSByZWFkLmNzdigKICAnX2FuYWx5dGljcy9wYWdldmlld3NfcGVyUGFnZURheVRvdGFscy5jc3YnLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmRhdGFTZXQkUGFnZSA8LSBnc3ViKCIvd2lraS9XaWtpcGVkaWE6IiwgIiIsIGRhdGFTZXQkUGFnZSkKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn0KZ2dwbG90KGRhdGFTZXQsIGFlcyh4ID0gZGF0ZSwKICAgICAgICAgICAgICAgICAgICB5ID0gbG9nMTAodG90YWxQYWdldmlld3MpLAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gUGFnZSwKICAgICAgICAgICAgICAgICAgICBmaWxsID0gUGFnZSwKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHRvdGFsUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuMjUsIGdyb3VwID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdBdXR1bW4gQmFubmVyIENhbXBhaWduIDIwMTk6IFBhZ2V2aWV3cycpICsKICBmYWNldF93cmFwKH5QYWdlLCBuY29sID0gMykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgoKIyMjIyAxLjIuMSBQYWdldmlld3MgT3ZlcnZpZXc6IFRhYmxlCgoqKlRhYmxlIDEuMi4xLioqIFBhZ2V2aWV3cyBPdmVydmlldwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMjIyAtLS0gRnVsbCBEYXRhc2V0IChUYWJsZSBSZXBvcnQpCmRhdGF0YWJsZShkYXRhU2V0ICU+JSBhcnJhbmdlKGRlc2ModG90YWxQYWdldmlld3MpKSkKYGBgCgojIyMjIDEuMi4yIFBhZ2V2aWV3cyBPdmVydmlldzogdG90YWxzIHBlciBQYWdlCioqQ2hhcnQgMS4yLjIuKiogUGFnZXZpZXdzIE92ZXJ2aWV3OiB0b3RhbHMgcGVyIFBhZ2UKCmBgYHtyIGV2YWw9VCwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyLCBlY2hvPUZBTFNFfQpkYXRhU2V0IDwtIHJlYWQuY3N2KAogICdfYW5hbHl0aWNzL3BhZ2V2aWV3c19wZXJQYWdlVG90YWxzLmNzdicsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKZGF0YVNldCRQYWdlIDwtIGdzdWIoIi93aWtpL1dpa2lwZWRpYToiLCAiIiwgZGF0YVNldCRQYWdlKQpkYXRhU2V0JFBhZ2UgPC0gZmFjdG9yKGRhdGFTZXQkUGFnZSwgCiAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gZGF0YVNldCRQYWdlW29yZGVyKC1kYXRhU2V0JHRvdGFsUGFnZXZpZXdzKV0pCmdncGxvdChkYXRhU2V0LCBhZXMoeCA9IFBhZ2UsIAogICAgICAgICAgICAgICAgICAgIHkgPSBsb2codG90YWxQYWdldmlld3MpLCAKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFBhZ2UsCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFBhZ2UsCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB0b3RhbFBhZ2V2aWV3cykpICsgCiAgZ2VvbV9iYXIod2lkdGggPSAuNSwgc3RhdCA9ICJpZGVudGl0eSIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnQXV0dW1uIEJhbm5lciBDYW1wYWlnbiAyMDE5OiBUb3RhbCBQYWdldmlld3MgcGVyIFBhZ2UnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV9sYWJlbF9yZXBlbChzaXplID0gMy41LCBjb2xvciA9ICJ3aGl0ZSIsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKCiMjIyMgMS4yLjMgUGFnZXZpZXdzIE92ZXJ2aWV3OiB0b3RhbHMgcGVyIGRheQoKKipDaGFydCAxLjIuMy4qKiBQYWdldmlld3MgT3ZlcnZpZXc6IHRvdGFscyBwZXIgZGF5CgpgYGB7ciBldmFsPVQsIGZpZy53aWR0aD02LCBlY2hvPUZBTFNFfQpkYXRhU2V0IDwtIHJlYWQuY3N2KAogICdfYW5hbHl0aWNzL3BhZ2V2aWV3c19wZXJEYXlUb3RhbHMuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpnZ3Bsb3QoZGF0YVNldCwgYWVzKHggPSBkYXRlLCAKICAgICAgICAgICAgICAgICAgICB5ID0gdG90YWxQYWdldmlld3MsIAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdG90YWxQYWdldmlld3MpKSArCiAgZ2VvbV9wYXRoKHNpemUgPSAuMjUsIGdyb3VwID0gMSwgY29sb3IgPSAiZGFya2JsdWUiKSArCiAgZ2VvbV9wb2ludChzaXplID0gMS41LCBjb2xvciA9ICJkYXJrYmx1ZSIpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAid2hpdGUiKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ0F1dHVtbiBCYW5uZXIgQ2FtcGFpZ24gMjAxOTogUGFnZXZpZXdzJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fbGFiZWxfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkKYGBgCgoKIyMjIyAxLjIuMyBQYWdldmlld3MgT3ZlcnZpZXc6IHRvdGFscyBwZXIgVGFnL1BhZ2UKCioqQ2hhcnQgMS4yLjMuKiogUGFnZXZpZXdzIE92ZXJ2aWV3OiB0b3RhbHMgcGVyIFRhZy9QYWdlCgpgYGB7ciBldmFsPVQsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMiwgZWNobz1GQUxTRX0KZGF0YVNldCA8LSByZWFkLmNzdigKICAnX2FuYWx5dGljcy9wZXJUYWdQYWdlVG90YWxzLmNzdicsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY2FtcGFpZ25UYWdzIDwtIGMoJ1dNREVfbmV3ZWRpdG9yc19hdXR1bW5fMjAxOV9ubF9scDEnLAogICAgICAgICAgICAgICAgICAnV01ERV9uZXdlZGl0b3JzX2F1dHVtbl8yMDE5X25sX2xwMicsCiAgICAgICAgICAgICAgICAgICdXTURFX25ld2VkaXRvcnNfYXV0dW1uXzIwMTlfZmx5ZXInLAogICAgICAgICAgICAgICAgICAnV01ERV9uZXdlZGl0b3JzX2F1dHVtbl8yMDE5X2JucicpCmRhdGFTZXQgPC0gZmlsdGVyKGRhdGFTZXQsIAogICAgICAgICAgICAgICAgICBncmVwbChjYW1wYWlnblRhZ3NbMV0sIGRhdGFTZXQkVGFnKXwgCiAgICAgICAgICAgICAgICAgICAgZ3JlcGwoY2FtcGFpZ25UYWdzWzJdLCBkYXRhU2V0JFRhZyl8IAogICAgICAgICAgICAgICAgICAgIGdyZXBsKGNhbXBhaWduVGFnc1szXSwgZGF0YVNldCRUYWcpfCAKICAgICAgICAgICAgICAgICAgICBncmVwbChjYW1wYWlnblRhZ3NbNF0sIGRhdGFTZXQkVGFnKSkKZGF0YVNldCRUYWdbZ3JlcGwoY2FtcGFpZ25UYWdzWzFdLCBkYXRhU2V0JFRhZywgZml4ZWQgPSBUKV0gPC0gIm5sX2xwMSIKZGF0YVNldCRUYWdbZ3JlcGwoY2FtcGFpZ25UYWdzWzJdLCBkYXRhU2V0JFRhZywgZml4ZWQgPSBUKV0gPC0gIm5sX2xwMiIKZGF0YVNldCRUYWdbZ3JlcGwoY2FtcGFpZ25UYWdzWzNdLCBkYXRhU2V0JFRhZywgZml4ZWQgPSBUKV0gPC0gImZseWVyIgpkYXRhU2V0JFRhZ1tncmVwbChjYW1wYWlnblRhZ3NbNF0sIGRhdGFTZXQkVGFnLCBmaXhlZCA9IFQpXSA8LSAiYm5yIgpkYXRhU2V0JFBhZ2UgPC0gZ3N1YigiL3dpa2kvV2lraXBlZGlhOiIsICIiLCBkYXRhU2V0JFBhZ2UpCmRhdGFTZXQgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KFRhZywgUGFnZSwgdG90YWxQYWdldmlld3MpICU+JSAKICBncm91cF9ieShUYWcsIFBhZ2UpICU+JSAKICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0odG90YWxQYWdldmlld3MpKQpkYXRhU2V0ICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBUYWcsCiAgICAgICAgICAgICB5ID0gUGFnZSwKICAgICAgICAgICAgIGNvbG9yID0gVGFnLAogICAgICAgICAgICAgbGFiZWwgPSB0b3RhbFBhZ2V2aWV3cykpICsgCiAgZ2d0aXRsZSgnQXV0dW1uIEJhbm5lciBDYW1wYWlnbiAyMDE5OiBQYWdldmlld3MgcGVyIFRhZycpICsKICBnZW9tX3BvaW50KGFlcyhzaXplID0gdG90YWxQYWdldmlld3MpLCBzaGFwZSA9IDE5KSArCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLCBudWRnZV94ID0gLjMsIHNob3cubGVnZW5kID0gRikgKyAKICB4bGFiKCJUYWdzIikgKyB5bGFiKCJQYWdlcyIpICsgCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpKQpgYGAKCiMjIyMgMS4yLjQgUGFnZXZpZXdzIHBlciBUYWcsIGRhaWx5IHRvdGFscwoqKkNoYXJ0IDEuMi40LiBQYWdldmlld3MgcGVyIFRhZywgZGFpbHkgdG90YWxzLioqIExvZyBzY2FsaW5nIG9mIHRoZSBwYWdldmlld3MgaXMgbmVjZXNzYXJ5OyB0aGUgbnVtYmVycyByZXBvcnRlZCBpbiB0aGUgZGF0YSBwb2ludCBsYWJlbHMgYXJlIGV4YWN0LgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmRhdGFTZXQgPC0gcmVhZC5jc3YoCiAgJ19hbmFseXRpY3MvcGFnZXZpZXdzX3BlclRhZ0RheVRvdGFscy5jc3YnLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNhbXBhaWduVGFncyA8LSBjKCdXTURFX25ld2VkaXRvcnNfYXV0dW1uXzIwMTlfbmxfbHAxJywKICAgICAgICAgICAgICAgICAgJ1dNREVfbmV3ZWRpdG9yc19hdXR1bW5fMjAxOV9ubF9scDInLAogICAgICAgICAgICAgICAgICAnV01ERV9uZXdlZGl0b3JzX2F1dHVtbl8yMDE5X2ZseWVyJywKICAgICAgICAgICAgICAgICAgJ1dNREVfbmV3ZWRpdG9yc19hdXR1bW5fMjAxOV9ibnInKQpkYXRhU2V0IDwtIGZpbHRlcihkYXRhU2V0LCAKICAgICAgICAgICAgICAgICAgZ3JlcGwoY2FtcGFpZ25UYWdzWzFdLCBkYXRhU2V0JFRhZyl8IAogICAgICAgICAgICAgICAgICAgIGdyZXBsKGNhbXBhaWduVGFnc1syXSwgZGF0YVNldCRUYWcpfCAKICAgICAgICAgICAgICAgICAgICBncmVwbChjYW1wYWlnblRhZ3NbM10sIGRhdGFTZXQkVGFnKXwgCiAgICAgICAgICAgICAgICAgICAgZ3JlcGwoY2FtcGFpZ25UYWdzWzRdLCBkYXRhU2V0JFRhZykpCmRhdGFTZXQkVGFnW2dyZXBsKGNhbXBhaWduVGFnc1sxXSwgZGF0YVNldCRUYWcsIGZpeGVkID0gVCldIDwtICJubF9scDEiCmRhdGFTZXQkVGFnW2dyZXBsKGNhbXBhaWduVGFnc1syXSwgZGF0YVNldCRUYWcsIGZpeGVkID0gVCldIDwtICJubF9scDIiCmRhdGFTZXQkVGFnW2dyZXBsKGNhbXBhaWduVGFnc1szXSwgZGF0YVNldCRUYWcsIGZpeGVkID0gVCldIDwtICJmbHllciIKZGF0YVNldCRUYWdbZ3JlcGwoY2FtcGFpZ25UYWdzWzRdLCBkYXRhU2V0JFRhZywgZml4ZWQgPSBUKV0gPC0gImJuciIKZGF0YVNldCA8LSBkYXRhU2V0ICU+JSAKICBncm91cF9ieShUYWcsIGRhdGUpICU+JSAKICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0odG90YWxQYWdldmlld3MpKQpnZ3Bsb3QoZGF0YVNldCwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgIHkgPSBsb2cxMCh0b3RhbFBhZ2V2aWV3cyksCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBUYWcsCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBUYWcsCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFRhZywKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHRvdGFsUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuMjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnQXV0dW1uIEJhbm5lciBDYW1wYWlnbiAyMDE5OiBQYWdldmlld3MgcGVyIFRhZywgZGFpbHkgdG90YWxzJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgojIyAyLiBVc2VyIFJlZ2lzdHJhdGlvbnMKCkFsbCBkYXRhIG9uIHVzZXIgcmVnaXN0cmF0aW9ucyBhcmUgcHJlc2VudGVkIGluIHRoaXMgc2VjdGlvbi4KCiMjIyAyLjEgUmVnaXN0cmF0aW9ucyBwZXIgdGFnIGFuZCBkYXkKCioqQ2hhcnQgMi4xLioqIFJlZ2lzdHJhdGlvbnMgcGVyIHRhZyBhbmQgZGF5LiBQbGVhc2Ugbm90ZTogcG9pbnRzIHdpdGggbm8gZGF0YSBsYWJlbHMgc2lnbmlmeSAwIHVzZXIgcmVnaXN0cmF0aW9ucy4KCmBgYHtyIGV2YWw9VCwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTcsIGVjaG89RkFMU0V9CiMgLSBTdGFuZGFyZCByZWdpc3RyYXRpb25zCmRhdGFTZXQgPC0gcmVhZC5jc3YoCiAgJ19hbmFseXRpY3MvdXNlclJlZ2lzdHJhdGlvbnNSZXBvcnRGaWxlLmNzdicsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKZGF0YVNldCA8LSBkYXRhU2V0ICU+JQogIHNlbGVjdChkYXRlLCBldmVudF9jYW1wYWlnbikgJT4lIAogIGdyb3VwX2J5KGV2ZW50X2NhbXBhaWduLCBkYXRlKSAlPiUgCiAgc3VtbWFyaXNlKFJlZ2lzdHJhdGlvbnMgPSBuKCkpCmdncGxvdChkYXRhU2V0LCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICAgeSA9IFJlZ2lzdHJhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBldmVudF9jYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGV2ZW50X2NhbXBhaWduLAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSBldmVudF9jYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFJlZ2lzdHJhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdBdXR1bW4gQmFubmVyIENhbXBhaWduIDIwMTk6IFJlZ2lzdHJhdGlvbnMgcGVyIFRhZycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIyMgLS0tIEZ1bGwgRGF0YXNldCAoVGFibGUgUmVwb3J0KQpkYXRhdGFibGUoZHBseXI6OmFycmFuZ2UoZGF0YVNldCwgZXZlbnRfY2FtcGFpZ24sIGRhdGUsIGRlc2MoUmVnaXN0cmF0aW9ucykpKQpgYGAKCiMjIyAyLjIgVG90YWwgcmVnaXN0cmF0aW9ucyBwZXIgdGFnCioqQ2hhcnQgMi4yLioqIFRvdGFsIHJlZ2lzdHJhdGlvbnMgcGVyIHRhZy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpkYXRhU2V0IDwtIGRhdGFTZXQgJT4lIAogIGdyb3VwX2J5KGV2ZW50X2NhbXBhaWduKSAlPiUgCiAgc3VtbWFyaXNlKHRvdGFsUmVnaXN0cmF0aW9ucyA9IHN1bShSZWdpc3RyYXRpb25zKSkKZ2dwbG90KGRhdGFTZXQsIGFlcyh4ID0gZXZlbnRfY2FtcGFpZ24sIAogICAgICAgICAgICAgICAgICAgIHkgPSB0b3RhbFJlZ2lzdHJhdGlvbnMsIAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gZXZlbnRfY2FtcGFpZ24sCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGV2ZW50X2NhbXBhaWduLAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdG90YWxSZWdpc3RyYXRpb25zKSkgKyAKICBnZW9tX2Jhcih3aWR0aCA9IC41LCBzdGF0ID0gImlkZW50aXR5IikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdBdXR1bW4gQmFubmVyIENhbXBhaWduIDIwMTk6IFRvdGFsIFJlZ2lzdHJhdGlvbnMgcGVyIFRhZycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX2xhYmVsX3JlcGVsKHNpemUgPSAzLjUsIGNvbG9yID0gIndoaXRlIiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsgCiAgeWxhYigiUmVnaXN0cmF0aW9ucyIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyAzLiBVc2VyIEVkaXRzCgpBbGwgZGF0YSBvbiB1c2VyIGVkaXRzIGFyZSBwcmVzZW50ZWQgaW4gdGhpcyBzZWN0aW9uLgoKIyMjIDMuMSBVc2VyIGVkaXRzOiBkaXN0cmlidXRpb24KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQp1c2VyRWRpdHMgPC0gcmVhZC5jc3YoCiAgJ19hbmFseXRpY3MvdXNlckVkaXRzLmNzdicsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKZGF0YVNldCA8LSB1c2VyRWRpdHMgJT4lCiAgZHBseXI6OnNlbGVjdChhY3Rvcl91c2VyKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoYWN0b3JfdXNlcikgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoZWRpdHMgPSBuKCkpCiMgLSBFZGl0IHwgMSB8IDItNCB8IDUtOSB8IDEwLTQ5IHwgPjUwCmVkaXRCb3VuZGFyaWVzIDwtIGxpc3QoCiAgYygwLCAxKSwgCiAgYygyLCA0KSwKICBjKDUsIDkpLAogIGMoMTAsIDQ5KQopCmRhdGFTZXQkZWRpdENsYXNzIDwtIHNhcHBseShkYXRhU2V0JGVkaXRzLCBmdW5jdGlvbih4KSB7CiAgd0VDIDwtIHNhcHBseShlZGl0Qm91bmRhcmllcywgZnVuY3Rpb24oeSkgewogICAgeCA+PSB5WzFdICYgeCA8PSB5WzJdCiAgfSkKICBpZiAoc3VtKHdFQykgPT0gMCkgewogICAgcmV0dXJuKCI+PSA1MCIpCiAgfSBlbHNlIHsKICAgIHJldHVybihwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzFdLAogICAgICAgICAgICAgICAgICAiIC0gIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsyXSwgCiAgICAgICAgICAgICAgICAgICIpIgogICAgICAgICAgICAgICAgICApCiAgICApCiAgfQp9KQplZGl0Q2xhc3MgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShkYXRhU2V0JGVkaXRDbGFzcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXMoZWRpdENsYXNzKSA8LSBjKCdFZGl0IENsYXNzJywgJ051bS5Vc2VycycpCmVkaXRDbGFzcyRvcmRlciA8LSBhcy5udW1lcmljKHNhcHBseShlZGl0Q2xhc3MkYEVkaXQgQ2xhc3NgLCBmdW5jdGlvbih4KSB7CiAgbG93ZXIgPC0gc3RyX2V4dHJhY3QoeCwgJ1tbOmRpZ2l0Ol1dKycpCn0pKQplZGl0Q2xhc3MgPC0gYXJyYW5nZShlZGl0Q2xhc3MsIG9yZGVyKQplZGl0Q2xhc3Mkb3JkZXIgPC0gTlVMTApkYXRhdGFibGUoZWRpdENsYXNzKQpgYGAKCiMjIDQuIFRyYWluaW5nIE1vZHVsZXMKCkFsbCBkYXRhIG9uIHRyYWluaW5nIG1vZHVsZXMgYXJlIHByZXNlbnRlZCBpbiB0aGlzIHNlY3Rpb24uCgojIyMgNC4xIFN0YXJ0ZWQgVHJhaW5pbmcgTW9kdWxlcwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnRNb2R1bGVzIDwtIHJlYWQuY3N2KCdfYW5hbHl0aWNzL3dtZGVfdHJhaW5pbmdfZGF0YV8yMDE5LTExLmNzdicsCiAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnVzZXJSZWcgPC0gcmVhZC5jc3YoCiAgJ19hbmFseXRpY3MvdXNlclJlZ2lzdHJhdGlvbnNSZXBvcnRGaWxlLmNzdicsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKdE1vZHVsZXMgPC0gdE1vZHVsZXMgJT4lIAogIGRwbHlyOjpmaWx0ZXIodE1vZHVsZXMkdXNlcm5hbWUgJWluJSB1c2VyUmVnJHVzZXJuYW1lKQp0TW9kdWxlc19PdmVydmlldyA8LSB0TW9kdWxlcyAlPiUKICBkcGx5cjo6c2VsZWN0KHRyYWluaW5nX21vZHVsZSkgJT4lIAogIGRwbHlyOjpncm91cF9ieSh0cmFpbmluZ19tb2R1bGUpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKG51bV91c2VycyA9IG4oKSkKZGF0YXRhYmxlKHRNb2R1bGVzX092ZXJ2aWV3KQpgYGAKCgojIyMgNC4yIENvbXBsZXRlZCBUcmFpbmluZyBNb2R1bGVzCgpUaGUgZm9sbG93aW5nIHRhYmxlIHByZXNlbnRzIHRoZSBzdGF0aXN0aWNzIG9uIGNvbXBsZXRlZCB0cmFpbmluZyBtb2R1bGVzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnRNb2R1bGVzIDwtIHJlYWQuY3N2KCdfYW5hbHl0aWNzL3dtZGVfdHJhaW5pbmdfZGF0YV8yMDE5LTExLmNzdicsCiAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnVzZXJSZWcgPC0gcmVhZC5jc3YoCiAgJ19hbmFseXRpY3MvdXNlclJlZ2lzdHJhdGlvbnNSZXBvcnRGaWxlLmNzdicsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKdE1vZHVsZXMgPC0gdE1vZHVsZXMgJT4lIAogIGRwbHlyOjpmaWx0ZXIodE1vZHVsZXMkdXNlcm5hbWUgJWluJSB1c2VyUmVnJHVzZXJuYW1lKQp0TW9kdWxlcyA8LSB0TW9kdWxlcyAlPiUKICBkcGx5cjo6ZmlsdGVyKG5jaGFyKG1vZHVsZV9jb21wbGV0aW9uX2RhdGUpID4gMCkKdE1vZHVsZXNfT3ZlcnZpZXcgPC0gdE1vZHVsZXMgJT4lCiAgZHBseXI6OnNlbGVjdCh0cmFpbmluZ19tb2R1bGUpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkodHJhaW5pbmdfbW9kdWxlKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShudW1fdXNlcnMgPSBuKCkpCmRhdGF0YWJsZSh0TW9kdWxlc19PdmVydmlldykKYGBgCgojIyMgNC4zIENvbXBsZXRlZCBUcmFpbmluZyBNb2R1bGVzIGFuZCBVc2VyIEVkaXRzCgpUaGUgZm9sbG93aW5nIHRhYmxlIHByZXNlbnRzIHRoZSBzdGF0aXN0aWNzIG9uIGNvbXBsZXRlZCB0cmFpbmluZyBtb2R1bGVzIGFuZCB0aGUgcmVzcGVjdGl2ZSB1c2VycycgZWRpdHMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdE1vZHVsZXMgPC0gcmVhZC5jc3YoJ19hbmFseXRpY3Mvd21kZV90cmFpbmluZ19kYXRhXzIwMTktMTEuY3N2JywKICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKdXNlclJlZyA8LSByZWFkLmNzdigKICAnX2FuYWx5dGljcy91c2VyUmVnaXN0cmF0aW9uc1JlcG9ydEZpbGUuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQp0TW9kdWxlcyA8LSB0TW9kdWxlcyAlPiUgCiAgZHBseXI6OmZpbHRlcih0TW9kdWxlcyR1c2VybmFtZSAlaW4lIHVzZXJSZWckdXNlcm5hbWUpCnRNb2R1bGVzIDwtIHRNb2R1bGVzICU+JQogIGRwbHlyOjpmaWx0ZXIobmNoYXIobW9kdWxlX2NvbXBsZXRpb25fZGF0ZSkgPiAwKQp0TW9kdWxlc19FZGl0cyA8LSB0TW9kdWxlcyAlPiUKICBkcGx5cjo6bGVmdF9qb2luKHVzZXJFZGl0cywgCiAgICAgICAgICAgICAgICAgICBieSA9IGMoInVzZXJuYW1lIiA9ICJhY3Rvcl9uYW1lIikpICU+JSAKICBkcGx5cjo6ZmlsdGVyKCFpcy5uYShyZXZhY3Rvcl90aW1lc3RhbXApKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHRyYWluaW5nX21vZHVsZSkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoZWRpdHMgPSBuKCkpCmRhdGF0YWJsZSh0TW9kdWxlc19FZGl0cykKYGBgCgoKCg==