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

The campaign is run from 2020/01/01 to 2020/01/19.

CURRENT UPDATE: Complete dataset as of 2020/01/19; no data on training modules, banner expansion clicks, and banner closing quotes are available yet.

0. Data Acquisiton

NOTE: the Data Acquisition code chunk is not fully reproducible from this Report. The data are collected by running the script ThankYou_2020_PRODUCTION.R from the stat1004 WMF statistics server, collecting the data as .tsv and .csv files, aggregating on the server with ThankYou_2020_Aggregation.R locally, copying manually, and processing locally.

0.1 Daily Update

# !diagnostics off
### --- WMDE Thank You Campaign 2020 PRODUCTION
### --- Dec 23, 2019.
### --- run from: stat1004
### --- CAMPAIGN START: January 1, 2020.
### --- Daily reporting starts on January 2, 2020.

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

### --- dir structure
campaignPath <- '/home/goransm/Analytics/NewEditors/Campaigns/2020_ThankYou/'
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 + Kerberos Auth
  hiveArgs <- 'sudo -u analytics-privatedata kerberos-run-command analytics-privatedata /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 Thank You Campaign 2019/2020
uri_host <- c('de.wikipedia.org', 'de.m.wikipedia.org')
uri_path  <- '/beacon/impression'
uri_query <- c('WMDE_2019_2020_thx_')
queryFile <- 'thankyou2020_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 <- "2020_ThankYou"
wmde_process_banner_impressions(fileName = fileName, 
                                dataDir = dataDir, 
                                cetDay = cetDay,
                                campaignName = campaignName)

### ----------------------------------------------------------
### --- Banner Expansion Clicks + Banner Closing Quote
### --- Schema:WMDEBannerEvents
### --- https://meta.wikimedia.org/wiki/Schema:WMDEBannerEvents
### ----------------------------------------------------------
# - Schema:WMDEBannerEvents is empty

### ----------------------------------------------------------
### --- 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 <- 'sudo -u analytics-privatedata kerberos-run-command analytics-privatedata /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 2018
uri_host <- c('de.wikipedia.org', 'de.m.wikipedia.org')
uri_path  <- c('/wiki/Wikipedia:Wikimedia_Deutschland/LerneWikipedia')

# - set params for wmde_collect_pageviews
queryFile <- 'thankyou2020_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 2018, 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 2018
uri_query_filter <- 'WMDE_2019_2020_thx_'

# - 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 = 2020
      AND month = 1 
      AND day >= 1
      AND (event.campaign LIKE '%WMDE_2019_2020_thx_%');"
# - write hql
write(hiveQL, paste0(dataDir, 'user_registrations.hql'))
### --- output filename
filename <- paste0(dataDir, 'user_registrations.tsv')
### --- execute hql script:
hiveArgs <- 'sudo -u analytics-privatedata kerberos-run-command analytics-privatedata /usr/local/bin/beeline --silent --incremental=true --verbose=false -f'
hiveInput <- paste(paste0(dataDir, 'user_registrations.hql'),
                   " > ",
                   filename,
                   sep = "")
# - command:
hiveCommand <- paste(hiveArgs, hiveInput)
system(command = hiveCommand, wait = TRUE)

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
### --- Report Generation for the Thank You 2019/2020 Campaign
### --- run locally

### --- dir structure
campaignPath <- '/home/goransm/Work/___DataKolektiv/Projects/WikimediaDEU/_WMDE_Projects/_misc/NewEditors_Team/2020_ThankYou/'
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 <- '2020_ThankYou'
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.4 User Edits

0.4.1 User Edits via revision_actor_temp

library(data.table)
# !diagnostics off
### --- script: ThankYou2020_UserEdits.R
### --- User Edits Acquisition for the WMDE Thank You Campaign 2019/2020
### --- run from stat1004
### -----------------------------------------------------------------------
### --- User Edits
### -----------------------------------------------------------------------
### --- dir structure
campaignPath <- '/home/goransm/Analytics/NewEditors/Campaigns/2020_ThankYou/'
dataDir <- paste0(campaignPath, "_data/")
analyticsDir <- paste0(campaignPath, "_analytics/")
startTimestamp <- '20200101000000'
# - 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'))

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.

1.2.1 Pageviews Overview: Table

Table 1.2.1. Pageviews Overview

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

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. The numbers reported in the data point labels are exact. The erroneous WPDE tag (see: Phab T240351) is marked separately.

dataSet <- read.csv(
  '_analytics/pageviews_perTagDayTotals.csv',
                    header = T,
                    row.names = 1,
                    check.names = F,
                    stringsAsFactors = F)
dataSet <- filter(dataSet, 
                  !(grepl("?&piwik_", dataSet$Tag)))
dataSet$Tag <- gsub("\\?&campaign=WMDE_2019_2020_", "", dataSet$Tag)
dataSet$Tag <- gsub("\\?&campaign=WPDE_2019_2020_", "WPDE_", dataSet$Tag)
dataSet <- dataSet %>% 
  group_by(Tag, date) %>% 
  summarise(totalPageviews = sum(totalPageviews))
dataSet$platform <- sapply(dataSet$Tag, function(x) {
  if (grepl("dskt", x)) {
    return('dskt')
  } else if (grepl("mob", x)) {
    return('mob')
  } else if (grepl("ipad", x)) {
    return('ipad')
  } else {
    return(NA)
  }
})
ggplot(dataSet, aes(x = date,
                    y = 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) +
  geom_text_repel(size = 5, show.legend = FALSE) +
  facet_wrap(~platform, ncol = 1) +
  ggtitle('Thank You Campaign 2019/2020: Pageviews per Tag, daily totals') +
  theme_minimal() + 
  theme(axis.text.x = element_text(angle = 90, size = 18)) +
  theme(axis.text.y = element_text(size = 18)) +
  theme(axis.title.x = element_text(size = 18)) +
  theme(axis.title.y = element_text(size = 18)) +
  theme(plot.title = element_text(size = 20)) + 
  theme(strip.text.x = element_text(size = 18)) + 
  theme(legend.title = element_blank()) +
  theme(legend.text=element_text(size = 18)) + 
  theme(legend.position = "right")

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('Thank You Campaign 2019/2020: 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.text=element_text(size = 14)) + 
  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)
userEdits$revactor_timestamp <- as.character(userEdits$revactor_timestamp)
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

Only one campaign registered user started a training module.

tModules <- read.csv('_analytics/wmde_training_data_2020-01.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. NOTE. Only one user has completed a training module.

tModules <- read.csv('_analytics/wmde_training_data_2020-01.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. NOTE. The one and the only user who completed any training modules made 6 edits.

tModules <- read.csv('_analytics/wmde_training_data_2020-01.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)
LS0tCnRpdGxlOiAnV01ERSBUaGFuayBZb3UgQ2FtcGFpZ24gMjAxOS8yMDIwOiBJbnRlcmltIFJlcG9ydCcKYXV0aG9yOiAiR29yYW4gUy4gTWlsb3Zhbm92aWMsIERhdGEgU2NpZW50aXN0LCBXTURFIgpkYXRlOiAiSmFuYXVyeSAyMSwgMjAyMCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRoZW1lOiBzaW1wbGV4CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogNQogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA1Ci0tLQoKKipGZWVkYmFjayoqIHNob3VsZCBiZSBzZW5kIHRvIGBnb3Jhbi5taWxvdmFub3ZpY19leHRAd2lraW1lZGlhLmRlYC4gCgpUaGUgY2FtcGFpZ24gaXMgcnVuIGZyb20gMjAyMC8wMS8wMSB0byAyMDIwLzAxLzE5LgoKKipDVVJSRU5UIFVQREFURToqKiBDb21wbGV0ZSBkYXRhc2V0IGFzIG9mIDIwMjAvMDEvMTk7IG5vIGRhdGEgb24gdHJhaW5pbmcgbW9kdWxlcywgYmFubmVyIGV4cGFuc2lvbiBjbGlja3MsIGFuZCBiYW5uZXIgY2xvc2luZyBxdW90ZXMgYXJlIGF2YWlsYWJsZSB5ZXQuCgpgYGB7ciwgZWNobyA9IEYsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGLCByZXN1bHRzID0gJ2hpZGUnfQojICFkaWFnbm9zdGljcyBvZmYKIyMjIC0tLSBTZXR1cAprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQgPSA4KSAKcm0obGlzdCA9IGxzKCkpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkocm1hcmtkb3duKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KHJlc2hhcGUyKQpgYGAKCiMjIDAuIERhdGEgQWNxdWlzaXRvbgoKKipOT1RFOioqIHRoZSBEYXRhIEFjcXVpc2l0aW9uIGNvZGUgY2h1bmsgaXMgbm90IGZ1bGx5IHJlcHJvZHVjaWJsZSBmcm9tIHRoaXMgUmVwb3J0LiBUaGUgZGF0YSBhcmUgY29sbGVjdGVkIGJ5IHJ1bm5pbmcgdGhlIHNjcmlwdCBgVGhhbmtZb3VfMjAyMF9QUk9EVUNUSU9OLlJgIGZyb20gdGhlIGBzdGF0MTAwNGAgV01GIHN0YXRpc3RpY3Mgc2VydmVyLCBjb2xsZWN0aW5nIHRoZSBkYXRhIGFzIGAudHN2YCBhbmQgYC5jc3ZgIGZpbGVzLCBhZ2dyZWdhdGluZyBvbiB0aGUgc2VydmVyIHdpdGggYFRoYW5rWW91XzIwMjBfQWdncmVnYXRpb24uUmAgbG9jYWxseSwgY29weWluZyBtYW51YWxseSwgYW5kIHByb2Nlc3NpbmcgbG9jYWxseS4gCgojIyMgMC4xIERhaWx5IFVwZGF0ZQoKYGBge3IsIGV2YWwgPSBGfQojICFkaWFnbm9zdGljcyBvZmYKIyMjIC0tLSBXTURFIFRoYW5rIFlvdSBDYW1wYWlnbiAyMDIwIFBST0RVQ1RJT04KIyMjIC0tLSBEZWMgMjMsIDIwMTkuCiMjIyAtLS0gcnVuIGZyb206IHN0YXQxMDA0CiMjIyAtLS0gQ0FNUEFJR04gU1RBUlQ6IEphbnVhcnkgMSwgMjAyMC4KIyMjIC0tLSBEYWlseSByZXBvcnRpbmcgc3RhcnRzIG9uIEphbnVhcnkgMiwgMjAyMC4KCiMjIyAtLS0gbGlicmFyaWVzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkobHVicmlkYXRlKQoKIyMjIC0tLSBkaXIgc3RydWN0dXJlCmNhbXBhaWduUGF0aCA8LSAnL2hvbWUvZ29yYW5zbS9BbmFseXRpY3MvTmV3RWRpdG9ycy9DYW1wYWlnbnMvMjAyMF9UaGFua1lvdS8nCmRhdGFEaXIgPC0gcGFzdGUwKGNhbXBhaWduUGF0aCwgIl9kYXRhLyIpCmFuYWx5dGljc0RpciA8LSBwYXN0ZTAoY2FtcGFpZ25QYXRoLCAiX2FuYWx5dGljcy8iKQoKIyMjIC0tLSBkZXRlcm1pbmUgY2V0RGF5CmNldERheSA8LSBTeXMudGltZSgpCmNldERheQphdHRyKGNldERheSwgInR6b25lIikgPC0gIkV1cm9wZS9CZXJsaW4iCiMgLSBvbmUgZGF5IGJlaGluZCBmb3IgY3JvbnRhYgojIC0gKGkuZS4gd2FpdGluZyBmb3Igd21mLndlYnJlcXVlc3QgdG8gY29tcGxldGUgaXMgZGF0YSBhY3F1aXNpdGlvbikKY2V0RGF5IDwtIHltZCgKICBzdHJzcGxpdChhcy5jaGFyYWN0ZXIoY2V0RGF5KSwgCiAgICAgICAgICAgc3BsaXQgPSAiICIsIAogICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXQopIC0gMQoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBCYW5uZXIgSW1wcmVzc2lvbnMKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLSBmdW5jdGlvbjogd21kZV9jb2xsZWN0X2Jhbm5lcl9pbXByZXNzaW9ucwp3bWRlX2NvbGxlY3RfYmFubmVyX2ltcHJlc3Npb25zIDwtIGZ1bmN0aW9uKHVyaV9ob3N0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcGF0aCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIpIHsKICAKICAjIC0gTk9URToKICAjIC0gZXhwZWN0ZWQgZm9ybWF0IGZvciBjZXREYXkgaXM6IFlZWVktTU0tREQKICAKICAjIC0gdG8gZGF0YURpcgogIHNldHdkKGRhdGFEaXIpCiAgCiAgIyAtIGxpYnJhcmllcwogIGxpYnJhcnkoc3RyaW5ncikKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgZGF0ZXRpbWVfY29uZGl0aW9uCiAgY2V0X2NvbmRpdGlvbiA8LSBzZXEoCiAgICBmcm9tID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMDowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICB0byA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDIzOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIGJ5ID0gImhvdXIiCiAgKSAKICBhdHRyKGNldF9jb25kaXRpb24sICJ0em9uZSIpIDwtICJVVEMiCiAgY2V0X2NvbmRpdGlvbiA8LSBhcy5jaGFyYWN0ZXIoY2V0X2NvbmRpdGlvbikKICBjZXRfY29uZGl0aW9uIDwtIHVubGlzdChzdHJfZXh0cmFjdF9hbGwoY2V0X2NvbmRpdGlvbiwgIl4oW1s6ZGlnaXQ6XV18XFxzfC0pKiIpKQogIGNldF95ZWFycyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsxXQogICAgfSkKICBjZXRfbW9udGhzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzJdCiAgICB9KQogIGNldF9tb250aHMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X21vbnRocykKICBjZXRfZGF5cyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVszXQogICAgfSkKICBjZXRfZGF5cyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfZGF5cykKICBjZXRfaG91cnMgPC0gc2FwcGx5KHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCAKICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgeFsyXQogICAgICAgICAgICAgICAgICAgICAgfSkKICBjZXRfaG91cnMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2hvdXJzKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlMCgKICAgICJ5ZWFyID0gIiwgY2V0X3llYXJzLCAiIEFORCAiLAogICAgIm1vbnRoID0gIiwgY2V0X21vbnRocywgIiBBTkQgIiwKICAgICJkYXkgPSAiLCBjZXRfZGF5cywgIiBBTkQgIiwgCiAgICAiaG91ciA9ICIsIGNldF9ob3VycwogICkKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZSgiKCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGV0aW1lQ29uZGl0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSB1cmlfcGF0aF9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9wYXRoKSA+IDEpIHsKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX3BhdGhfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfcGF0aCA9ICciLCB1cmlfcGF0aCwgIiciKQogIH0KICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX2hvc3RfY29uZGl0aW9uCiAgaWYgKGxlbmd0aCh1cmlfaG9zdCkgPiAxKSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfaG9zdCA9ICciLCB1cmlfaG9zdCwgIiciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiA9IHBhc3RlMCgidXJpX2hvc3QgPSAnIiwgdXJpX2hvc3QsICInIikKICB9CiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9xdWVyeV9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9xdWVyeSkgPiAxKSB7CiAgICB1cmlfcXVlcnlfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfcXVlcnkgTElLRSAnJSIsIHVyaV9xdWVyeSwgIiUnIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9xdWVyeV9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9xdWVyeSBMSUtFICclIiwgdXJpX3F1ZXJ5LCAiJSciKQogIH0KICAKICAjIC0gY29tcG9zZSBIaXZlUUwgcXVlcnkKICBoaXZlUXVlcnkgPC0gcGFzdGUwKAogICAgIlVTRSB3bWY7CiAgICBTRUxFQ1QgdXJpX3F1ZXJ5IEZST00gd2VicmVxdWVzdAogICAgV0hFUkUgKCIsCiAgICB1cmlfaG9zdF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcGF0aF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcXVlcnlfY29uZGl0aW9uLCAiIEFORCAiLAogICAgIigiLCBkYXRldGltZUNvbmRpdGlvbiwgIikiLAogICAgIik7IgogICAgKQogIAogICMgLSB3cml0ZSBocWwKICB3cml0ZShoaXZlUXVlcnksIHF1ZXJ5RmlsZSkKICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0ICsgS2VyYmVyb3MgQXV0aAogIGhpdmVBcmdzIDwtICdzdWRvIC11IGFuYWx5dGljcy1wcml2YXRlZGF0YSBrZXJiZXJvcy1ydW4tY29tbWFuZCBhbmFseXRpY3MtcHJpdmF0ZWRhdGEgL3Vzci9sb2NhbC9iaW4vYmVlbGluZSAtZicKICBoaXZlSW5wdXQgPC0gcGFzdGUwKHF1ZXJ5RmlsZSwgJyA+ICcsIGZpbGVOYW1lKQogICMgLSBjb21tYW5kOgogIGhpdmVDb21tYW5kIDwtIHBhc3RlKGhpdmVBcmdzLCBoaXZlSW5wdXQpCiAgcmV0dXJuKAogICAgc3lzdGVtKGNvbW1hbmQgPSBoaXZlQ29tbWFuZCwgd2FpdCA9IFRSVUUpCiAgKQp9CgojIC0gc2V0IHBhcmFtcyB0byB3bWRlX2NvbGxlY3RfYmFubmVyX2ltcHJlc3Npb25zCiMgLSBmb3IgdGhlIFRoYW5rIFlvdSBDYW1wYWlnbiAyMDE5LzIwMjAKdXJpX2hvc3QgPC0gYygnZGUud2lraXBlZGlhLm9yZycsICdkZS5tLndpa2lwZWRpYS5vcmcnKQp1cmlfcGF0aCAgPC0gJy9iZWFjb24vaW1wcmVzc2lvbicKdXJpX3F1ZXJ5IDwtIGMoJ1dNREVfMjAxOV8yMDIwX3RoeF8nKQpxdWVyeUZpbGUgPC0gJ3RoYW5reW91MjAyMF9CYW5uZXJJbXByZXNzaW9ucy5ocWwnCmZpbGVOYW1lIDwtIHBhc3RlMChkYXRhRGlyLCAiYmFubmVySW1wcmVzc2lvbnNfIiwgY2V0RGF5LCAiLnRzdiIpCgojIC0gY29sbGVjdCBCYW5uZXIgSW1wcmVzc2lvbiBkYXRhCndtZGVfY29sbGVjdF9iYW5uZXJfaW1wcmVzc2lvbnModXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpcikKCiMgLSBmdW5jdGlvbjogd21kZV9wcm9jZXNzX2Jhbm5lcl9pbXByZXNzaW9ucwp3bWRlX3Byb2Nlc3NfYmFubmVyX2ltcHJlc3Npb25zIDwtIGZ1bmN0aW9uKGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lKSB7CiAgCiAgIyAtIHRvIGRhdGFEaXIKICBzZXR3ZChkYXRhRGlyKQogIAogICMgLSBsaWJyYXJpZXMKICBsaWJyYXJ5KHN0cmluZ3IpCiAgbGlicmFyeShkcGx5cikKICAKICAjIC0gbG9hZAogIGJhbm5lckRhdGEgPC0gcmVhZC5kZWxpbShmaWxlTmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIGNvbG5hbWVzKGJhbm5lckRhdGEpIDwtICd1cmlfcXVlcnknCiAgCiAgIyAtIGNsZWFuCiAgd1N0YXJ0IDwtIHdoaWNoKGJhbm5lckRhdGEkdXJpX3F1ZXJ5ID09ICJ1cmlfcXVlcnkiKQogIGJhbm5lckRhdGEgPC0gYmFubmVyRGF0YVsod1N0YXJ0ICsgMSk6KGRpbShiYW5uZXJEYXRhKVsxXSAtIDIpLCBdCiAgCiAgIyAtIHNwbGl0CiAgYmFubmVyRGF0YSA8LSBzdHJzcGxpdChiYW5uZXJEYXRhLCBzcGxpdCA9ICImIiwgZml4ZWQgPSBUKQogICMgLSBleHRyYWN0IHJlbGV2YW50IGZpZWxkcwogICMgLSBiYW5uZXI6CiAgYmFubmVyIDwtIHNhcHBseShiYW5uZXJEYXRhLCBmdW5jdGlvbih4KSB7CiAgICB4W3doaWNoKGdyZXBsKCJeYmFubmVyPSIsIHgpKV0KICB9KQogIGJhbm5lciA8LSBnc3ViKCJeYmFubmVyPSIsICIiLCBiYW5uZXIpCiAgIyAtIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlOgogIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlIDwtIHNhcHBseShiYW5uZXJEYXRhLCBmdW5jdGlvbih4KSB7CiAgICB4W3doaWNoKGdyZXBsKCJecmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGU9IiwgeCkpXQogIH0pCiAgcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUgPC0gYXMubnVtZXJpYygKICAgIGdzdWIoIl5yZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZT0iLCAiIiwgcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUpCiAgKQogICMgLSByZXN1bHQ6CiAgcmVzdWx0IDwtIHNhcHBseShiYW5uZXJEYXRhLCBmdW5jdGlvbih4KSB7CiAgICB4W3doaWNoKGdyZXBsKCJecmVzdWx0PSIsIHgpKV0KICB9KQogIHJlc3VsdCA8LSBnc3ViKCJecmVzdWx0PSIsICIiLCByZXN1bHQpCiAgCiAgIyAtIGNvbXBvc2UgdGFibGU6CiAgYmFubmVyT2JzZXJ2YXRpb25zIDwtIGRhdGEuZnJhbWUoYmFubmVyID0gYmFubmVyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSA9IHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSByZXN1bHQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIAogICMgLSBmaWx0ZXIgZm9yIHJlc3VsdD1zaG93CiAgYmFubmVyT2JzZXJ2YXRpb25zIDwtIGRwbHlyOjpmaWx0ZXIoYmFubmVyT2JzZXJ2YXRpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9PSAic2hvdyIpCiAgCiAgIyAtIGNvcnJlY3Rpb24gZm9yIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlCiAgYmFubmVyT2JzZXJ2YXRpb25zJHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlIDwtIAogICAgMS9iYW5uZXJPYnNlcnZhdGlvbnMkcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUKICAKICAjIC0gYWdncmVnYXRlOgogIGJhbm5lck9ic2VydmF0aW9ucyA8LSBiYW5uZXJPYnNlcnZhdGlvbnMgJT4lIAogICAgZHBseXI6OnNlbGVjdChiYW5uZXIsIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlKSAlPiUgCiAgICBkcGx5cjo6Z3JvdXBfYnkoYmFubmVyKSAlPiUgCiAgICBkcGx5cjo6c3VtbWFyaXNlKGltcHJlc3Npb25zID0gc3VtKHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlKSkKICAKICAjIC0gYWRkIGNldERheSwgbWUKICBiYW5uZXJPYnNlcnZhdGlvbnMkZGF0ZSA8LSBjZXREYXkKICBiYW5uZXJPYnNlcnZhdGlvbnMkY2FtcGFpZ24gPC0gY2FtcGFpZ25OYW1lCiAgCiAgIyAtIHN0b3JlOgogIHdyaXRlLmNzdihiYW5uZXJPYnNlcnZhdGlvbnMsIAogICAgICAgICAgICBwYXN0ZTAoImJhbm5lckltcHJlc3Npb25zQWdncmVnYXRlZF8iLAogICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICkKICApCiAgCn0KCiMgLSB3cmFuZ2xlIEJhbm5lciBJbXByZXNzaW9uIGRhdGEKY2FtcGFpZ25OYW1lIDwtICIyMDIwX1RoYW5rWW91Igp3bWRlX3Byb2Nlc3NfYmFubmVyX2ltcHJlc3Npb25zKGZpbGVOYW1lID0gZmlsZU5hbWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIgPSBkYXRhRGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXkgPSBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lID0gY2FtcGFpZ25OYW1lKQoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBCYW5uZXIgRXhwYW5zaW9uIENsaWNrcyArIEJhbm5lciBDbG9zaW5nIFF1b3RlCiMjIyAtLS0gU2NoZW1hOldNREVCYW5uZXJFdmVudHMKIyMjIC0tLSBodHRwczovL21ldGEud2lraW1lZGlhLm9yZy93aWtpL1NjaGVtYTpXTURFQmFubmVyRXZlbnRzCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgLSBTY2hlbWE6V01ERUJhbm5lckV2ZW50cyBpcyBlbXB0eQoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBQYWdldmlld3MKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLSBmdW5jdGlvbjogd21kZV9jb2xsZWN0X3BhZ2V2aWV3cwp3bWRlX2NvbGxlY3RfcGFnZXZpZXdzIDwtIGZ1bmN0aW9uKHVyaV9ob3N0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpcikgewogIAogICMgLSBOT1RFOgogICMgLSBleHBlY3RlZCBmb3JtYXQgZm9yIGNldERheSBpczogWVlZWS1NTS1ERAogIAogICMgLSB0byBkYXRhRGlyCiAgc2V0d2QoZGF0YURpcikKICAKICAjIC0gbGlicmFyaWVzCiAgbGlicmFyeShzdHJpbmdyKQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSBkYXRldGltZV9jb25kaXRpb24KICBjZXRfY29uZGl0aW9uIDwtIHNlcSgKICAgIGZyb20gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAwOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIHRvID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMjM6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgYnkgPSAiaG91ciIKICApIAogIGF0dHIoY2V0X2NvbmRpdGlvbiwgInR6b25lIikgPC0gIlVUQyIKICBjZXRfY29uZGl0aW9uIDwtIGFzLmNoYXJhY3RlcihjZXRfY29uZGl0aW9uKQogIGNldF9jb25kaXRpb24gPC0gdW5saXN0KHN0cl9leHRyYWN0X2FsbChjZXRfY29uZGl0aW9uLCAiXihbWzpkaWdpdDpdXXxcXHN8LSkqIikpCiAgY2V0X3llYXJzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzFdCiAgICB9KQogIGNldF9tb250aHMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMl0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfbW9udGhzKQogIGNldF9kYXlzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzNdCiAgICB9KQogIGNldF9kYXlzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9kYXlzKQogIGNldF9ob3VycyA8LSBzYXBwbHkoc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIAogICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICB4WzJdCiAgICAgICAgICAgICAgICAgICAgICB9KQogIGNldF9ob3VycyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfaG91cnMpCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUwKAogICAgInllYXIgPSAiLCBjZXRfeWVhcnMsICIgQU5EICIsCiAgICAibW9udGggPSAiLCBjZXRfbW9udGhzLCAiIEFORCAiLAogICAgImRheSA9ICIsIGNldF9kYXlzLCAiIEFORCAiLCAKICAgICJob3VyID0gIiwgY2V0X2hvdXJzCiAgKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlKCIoIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZXRpbWVDb25kaXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpCiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9wYXRoX2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX3BhdGgpID4gMSkgewogICAgdXJpX3BhdGhfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgidXJpX3BhdGggPSAnIiwgdXJpX3BhdGgsICInIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICB1cmlfcGF0aF9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpCiAgfQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSB1cmlfaG9zdF9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9ob3N0KSA+IDEpIHsKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9ob3N0ID0gJyIsIHVyaV9ob3N0LCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX2hvc3RfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfaG9zdCA9ICciLCB1cmlfaG9zdCwgIiciKQogIH0KICAKICAjIC0gY29tcG9zZSBIaXZlUUwgcXVlcnkKICBoaXZlUXVlcnkgPC0gcGFzdGUwKAogICAgIlVTRSB3bWY7CiAgICBTRUxFQ1QgdXJpX3BhdGgsIHVyaV9xdWVyeSwgcmVmZXJlciBGUk9NIHdlYnJlcXVlc3QKICAgIFdIRVJFICgiLAogICAgdXJpX2hvc3RfY29uZGl0aW9uLCAiIEFORCAiLAogICAgdXJpX3BhdGhfY29uZGl0aW9uLCAiIEFORCAiLAogICAgIigiLCBkYXRldGltZUNvbmRpdGlvbiwgIikiLAogICAgIik7IgogICAgKQogIAogICMgLSB3cml0ZSBocWwKICB3cml0ZShoaXZlUXVlcnksIHF1ZXJ5RmlsZSkKICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0OgogIGhpdmVBcmdzIDwtICdzdWRvIC11IGFuYWx5dGljcy1wcml2YXRlZGF0YSBrZXJiZXJvcy1ydW4tY29tbWFuZCBhbmFseXRpY3MtcHJpdmF0ZWRhdGEgL3Vzci9sb2NhbC9iaW4vYmVlbGluZSAtZicKICBoaXZlSW5wdXQgPC0gcGFzdGUwKHF1ZXJ5RmlsZSwgJyA+ICcsIGZpbGVOYW1lKQogICMgLSBjb21tYW5kOgogIGhpdmVDb21tYW5kIDwtIHBhc3RlKGhpdmVBcmdzLCBoaXZlSW5wdXQpCiAgcmV0dXJuKAogICAgc3lzdGVtKGNvbW1hbmQgPSBoaXZlQ29tbWFuZCwgd2FpdCA9IFRSVUUpCiAgKQp9CgojIC0gc2V0IHBhcmFtcyB0byB3bWRlX2NvbGxlY3RfcGFnZXZpZXdzCiMgLSBmb3IgdGhlIEF1dHVtbiBCYW5uZXIgQ2FtcGFpZ24gMjAxOAp1cmlfaG9zdCA8LSBjKCdkZS53aWtpcGVkaWEub3JnJywgJ2RlLm0ud2lraXBlZGlhLm9yZycpCnVyaV9wYXRoICA8LSBjKCcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0xlcm5lV2lraXBlZGlhJykKCiMgLSBzZXQgcGFyYW1zIGZvciB3bWRlX2NvbGxlY3RfcGFnZXZpZXdzCnF1ZXJ5RmlsZSA8LSAndGhhbmt5b3UyMDIwX1BhZ2V2aWV3cy5ocWwnCmZpbGVOYW1lIDwtIHBhc3RlMCgicGFnZXZpZXdzXyIsIGNldERheSwgIi50c3YiKQoKIyAtIGNvbGxlY3QgUGFnZXZpZXdzIGRhdGEKd21kZV9jb2xsZWN0X3BhZ2V2aWV3cyh1cmlfaG9zdCwKICAgICAgICAgICAgICAgICAgICAgICB1cmlfcGF0aCwKICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIpCgojIyMgLS0tIFdyYW5nbGUgUGFnZXZpZXdzCiMgLSBmdW5jdGlvbjogd21kZV9wcm9jZXNzX3BhZ2V2aWV3cwp3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzIDwtIGZ1bmN0aW9uKGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9xdWVyeV9maWx0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUgPSBjYW1wYWlnbk5hbWUpIHsKICAKICAjIC0gdG8gZGF0YURpcgogIHNldHdkKGRhdGFEaXIpCiAgCiAgIyAtIGxpYnJhcmllcwogIGxpYnJhcnkoc3RyaW5ncikKICBsaWJyYXJ5KGRwbHlyKQogIGxpYnJhcnkodGlkeXIpCiAgbGlicmFyeShkYXRhLnRhYmxlKQogIAogICMgLSBsb2FkCiAgcGFnZXZpZXdzRGF0YSA8LSByZWFkTGluZXMoZmlsZU5hbWUpCiAgd1N0YXJ0IDwtIHdoaWNoKGdyZXBsKCJedXJpX3BhdGgiLCBwYWdldmlld3NEYXRhKSkKICBwYWdldmlld3NEYXRhIDwtIHBhZ2V2aWV3c0RhdGFbKHdTdGFydCArIDIpOihsZW5ndGgocGFnZXZpZXdzRGF0YSkgLSAyKV0KICBwYWdldmlld3NEYXRhIDwtIGRhdGEuZnJhbWUoZGF0ID0gcGFnZXZpZXdzRGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIHBhZ2V2aWV3c0RhdGEgPC0gc2VwYXJhdGUocGFnZXZpZXdzRGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGludG8gPSBjKCd1cmlfcGF0aCcsICd1cmlfcXVlcnknLCAncmVmZXJlcicpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IikKICAjIC0gYXBwbHkgdXJpX3F1ZXJ5X2ZpbHRlcgogICMgLSBOT1RFOiBBdXR1bW4gMjAxOCwgbG9va2luZyBpbiBib3RoOiB1cmlfcXVlcnksIHJlZmVyZXIKICB3X3VyaV9xdWVyeSA8LSB3aGljaChncmVwbCh1cmlfcXVlcnlfZmlsdGVyLCBwYWdldmlld3NEYXRhJHVyaV9xdWVyeSkpCiAgd191cmlfcXVlcnlfcmVmZXJlciA8LSB3aGljaChncmVwbCh1cmlfcXVlcnlfZmlsdGVyLCBwYWdldmlld3NEYXRhJHJlZmVyZXIpKQogIHdfdXJpX3F1ZXJ5IDwtIHVuaXF1ZShjKHdfdXJpX3F1ZXJ5LCB3X3VyaV9xdWVyeV9yZWZlcmVyKSkKICBwYWdldmlld3NEYXRhIDwtIHBhZ2V2aWV3c0RhdGFbd191cmlfcXVlcnksIF0KICB3X3VyaV9xdWVyeV9yZWZlcmVyIDwtIHdoaWNoKGdyZXBsKHVyaV9xdWVyeV9maWx0ZXIsIHBhZ2V2aWV3c0RhdGEkcmVmZXJlcikpCiAgd191cmlfcXVlcnlfcmVmZXJlcl9kZWxldGUgPC0gc2V0ZGlmZigxOmRpbShwYWdldmlld3NEYXRhKVsxXSwgd191cmlfcXVlcnlfcmVmZXJlcikKICBwYWdldmlld3NEYXRhJHJlZmVyZXJbd191cmlfcXVlcnlfcmVmZXJlcl9kZWxldGVdIDwtICcnCiAgIyAtIHdoZW4gdGhlcmUgaXMgbm8gdXJpX3F1ZXJ5LCB1c2UgdGhlIHF1ZXJ5IGZyb20gdGhlIHJlZmVyZXIgZmllbGQgaWYgcHJlc2VudCB0aGVyZQogIHBhZ2V2aWV3c0RhdGEkcmVmZXJlciA8LSBzdHJfZXh0cmFjdChwYWdldmlld3NEYXRhJHJlZmVyZXIsICJcXD9jYW1wYWlnbj0uKiQiKQogIHBhZ2V2aWV3c0RhdGEkcmVmZXJlcltpcy5uYShwYWdldmlld3NEYXRhJHJlZmVyZXIpXSA8LSAiIgogIHBhZ2V2aWV3c0RhdGEkcmVmZXJlciA8LSBnc3ViKCI/Y2FtcGFpZ249IiwgIiIsIHBhZ2V2aWV3c0RhdGEkcmVmZXJlciwgZml4ZWQgPSBUKQogIHBhZ2V2aWV3c0RhdGEkdXJpX3F1ZXJ5IDwtIGdzdWIoIj9jYW1wYWlnbj0iLCAiIiwgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnksIGZpeGVkID0gVCkKICBwYWdldmlld3NEYXRhJHVyaV9xdWVyeVtwYWdldmlld3NEYXRhJHVyaV9xdWVyeSA9PSAiIl0gPC0gCiAgICBwYWdldmlld3NEYXRhJHJlZmVyZXJbcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkgPT0gIiJdCiAgcGFnZXZpZXdzRGF0YSA8LSBkcGx5cjo6ZmlsdGVyKHBhZ2V2aWV3c0RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcXVlcnkgIT0gIiIpCiAgcGFnZXZpZXdzRGF0YSRyZWZlcmVyIDwtIE5VTEwKICAjIC0gY2xlYW4gdXAgYSBiaXQ6CiAgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkgPC0gZ3N1YigiLy4qJCIsICIiLCBwYWdldmlld3NEYXRhJHVyaV9xdWVyeSkKICAKICAjIC0gYWdncmVnYXRlOgogIHBhZ2V2aWV3c0RhdGEgPC0gcGFnZXZpZXdzRGF0YSAlPiUgCiAgICBkcGx5cjo6Z3JvdXBfYnkodXJpX3F1ZXJ5LCB1cmlfcGF0aCkgJT4lIAogICAgZHBseXI6OnN1bW1hcmlzZShwYWdldmlld3MgPSBuKCkpCiAgY29sbmFtZXMocGFnZXZpZXdzRGF0YSkgPC0gYygnVGFnJywgJ1BhZ2UnLCAnUGFnZXZpZXdzJykKICAKICAjIC0gYWRkIGNldERheSwgY2FtcGFpZ25OYW1lCiAgcGFnZXZpZXdzRGF0YSRkYXRlIDwtIGNldERheQogIHBhZ2V2aWV3c0RhdGEkY2FtcGFpZ24gPC0gY2FtcGFpZ25OYW1lCiAgCiAgIyAtIHN0b3JlOgogIHdyaXRlLmNzdihwYWdldmlld3NEYXRhLCAKICAgICAgICAgICAgcGFzdGUwKCJwYWdldmlld3NBZ2dyZWdhdGVkXyIsCiAgICAgICAgICAgICAgICAgICBzdHJzcGxpdCgKICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoZmlsZU5hbWUsIHNwbGl0ID0gIl8iLCBmaXhlZCA9IFQpW1sxXV1bMl0sCiAgICAgICAgICAgICAgICAgICAgIHNwbGl0ID0gIi4iLCAKICAgICAgICAgICAgICAgICAgICAgZml4ZWQgPSBUKVtbMV1dWzFdLAogICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICkKICApCiAgCn0KCiMgLSBzZXQgcGFyYW1zIHRvIHdtZGVfcHJvY2Vzc19wYWdldmlld3MKIyAtIGZvciB0aGUgQXV0dW1uIEJhbm5lciBDYW1wYWlnbiAyMDE4CnVyaV9xdWVyeV9maWx0ZXIgPC0gJ1dNREVfMjAxOV8yMDIwX3RoeF8nCgojIC0gd3JhbmdsZSBwYWdldmlld3MKd21kZV9wcm9jZXNzX3BhZ2V2aWV3cyhmaWxlTmFtZSA9IGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIgPSBkYXRhRGlyLAogICAgICAgICAgICAgICAgICAgICAgIHVyaV9xdWVyeV9maWx0ZXIgPSB1cmlfcXVlcnlfZmlsdGVyLCAKICAgICAgICAgICAgICAgICAgICAgICBjZXREYXkgPSBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lID0gY2FtcGFpZ25OYW1lKSAKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gVXNlciBSZWdpc3RyYXRpb25zCiMjIyAtLS0gSGl2ZVFMIHF1ZXJ5OiBldmVudC5TZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uIHRhYmxlCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmhpdmVRTCA8LSAKICAiU0VMRUNUIHllYXIsIG1vbnRoLCBkYXksIGhvdXIsICBldmVudC5jYW1wYWlnbiwgZXZlbnQudXNlcklkLCBldmVudC51c2VyTmFtZSAKICAgIEZST00gZXZlbnQuc2VydmVyc2lkZWFjY291bnRjcmVhdGlvbiAKICAgIFdIRVJFIAogICAgICB5ZWFyID0gMjAyMAogICAgICBBTkQgbW9udGggPSAxIAogICAgICBBTkQgZGF5ID49IDEKICAgICAgQU5EIChldmVudC5jYW1wYWlnbiBMSUtFICclV01ERV8yMDE5XzIwMjBfdGh4XyUnKTsiCiMgLSB3cml0ZSBocWwKd3JpdGUoaGl2ZVFMLCBwYXN0ZTAoZGF0YURpciwgJ3VzZXJfcmVnaXN0cmF0aW9ucy5ocWwnKSkKIyMjIC0tLSBvdXRwdXQgZmlsZW5hbWUKZmlsZW5hbWUgPC0gcGFzdGUwKGRhdGFEaXIsICd1c2VyX3JlZ2lzdHJhdGlvbnMudHN2JykKIyMjIC0tLSBleGVjdXRlIGhxbCBzY3JpcHQ6CmhpdmVBcmdzIDwtICdzdWRvIC11IGFuYWx5dGljcy1wcml2YXRlZGF0YSBrZXJiZXJvcy1ydW4tY29tbWFuZCBhbmFseXRpY3MtcHJpdmF0ZWRhdGEgL3Vzci9sb2NhbC9iaW4vYmVlbGluZSAtLXNpbGVudCAtLWluY3JlbWVudGFsPXRydWUgLS12ZXJib3NlPWZhbHNlIC1mJwpoaXZlSW5wdXQgPC0gcGFzdGUocGFzdGUwKGRhdGFEaXIsICd1c2VyX3JlZ2lzdHJhdGlvbnMuaHFsJyksCiAgICAgICAgICAgICAgICAgICAiID4gIiwKICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lLAogICAgICAgICAgICAgICAgICAgc2VwID0gIiIpCiMgLSBjb21tYW5kOgpoaXZlQ29tbWFuZCA8LSBwYXN0ZShoaXZlQXJncywgaGl2ZUlucHV0KQpzeXN0ZW0oY29tbWFuZCA9IGhpdmVDb21tYW5kLCB3YWl0ID0gVFJVRSkKYGBgCgojIyMgMC4yIERhdGEgQWdncmVnYXRpb24KCioqTk9URToqKiBOb3QgcnVuIGZyb20gdGhpcyByZXBvcnQ7IHRoZSBkYXRhIHdlcmUgYWxyZWFkeSBwcmUtcHJvY2Vzc2VkIGFuZCBhZ2dyZWdhdGVkIGJ5IHRoZSBmb2xsb3dpbmcgYFJgIHNjcmlwdCBiZWZvcmUgYmVpbmcgc3VibWl0dGVkIHRvIGFuYWx5dGljYWwgcHJvY2VkdXJlczoKCmBgYHtyLCBldmFsID0gRn0KIyAhZGlhZ25vc3RpY3Mgb2ZmCiMjIyAtLS0gUmVwb3J0IEdlbmVyYXRpb24gZm9yIHRoZSBUaGFuayBZb3UgMjAxOS8yMDIwIENhbXBhaWduCiMjIyAtLS0gcnVuIGxvY2FsbHkKCiMjIyAtLS0gZGlyIHN0cnVjdHVyZQpjYW1wYWlnblBhdGggPC0gJy9ob21lL2dvcmFuc20vV29yay9fX19EYXRhS29sZWt0aXYvUHJvamVjdHMvV2lraW1lZGlhREVVL19XTURFX1Byb2plY3RzL19taXNjL05ld0VkaXRvcnNfVGVhbS8yMDIwX1RoYW5rWW91LycKZGF0YURpciA8LSBwYXN0ZTAoY2FtcGFpZ25QYXRoLCAiX2RhdGEvIikKYW5hbHl0aWNzRGlyIDwtIHBhc3RlMChjYW1wYWlnblBhdGgsICJfYW5hbHl0aWNzLyIpCgojIyMgLS0tIFJlcG9ydCBCYW5uZXIgSW1wcmVzc2lvbiBEYXRhCgojIC0gZnVuY3Rpb246IHdtZGVfcmVwb3J0X2Jhbm5lcl9pbXByZXNzaW9ucwp3bWRlX3JlcG9ydF9iYW5uZXJfaW1wcmVzc2lvbnMgPC0gZnVuY3Rpb24oZGF0YURpcikgewogIAogICMgLSBTZXR1cAogIGxpYnJhcnkoZGF0YS50YWJsZSkKICBsaWJyYXJ5KGRwbHlyKQogIAogICMgLSBsaXN0IGZpbGVzOgogIGxGIDwtIGxpc3QuZmlsZXMoZGF0YURpcikKICAKICAjIC0gZmlsdGVyIGFnZ3JlZ2F0ZWQgYmFubmVyIGltcHJlc3Npb24gZGF0YQogIGxGIDwtIGxGW2dyZXBsKCJiYW5uZXJJbXByZXNzaW9uc0FnZ3JlZ2F0ZWRfIiwgbEYsIGZpeGVkID0gVCldCiAgCiAgIyAtIGxvYWQgZmlsZXMgYW5kIG1lcmdlCiAgYmFubmVyRGF0YSA8LSB2ZWN0b3IobW9kZSA9ICJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKGxGKSkKICBmb3IgKGkgaW4gMTpsZW5ndGgobEYpKSB7CiAgICBpZiAoZ3JlcGwoImNzdiR8dHN2JCIsIGxGW2ldKSkgewogICAgICBiYW5uZXJEYXRhW1tpXV0gPC0gZnJlYWQocGFzdGUwKGRhdGFEaXIsIGxGW2ldKSkKICAgIH0gZWxzZSB7CiAgICAgIGJhbm5lckRhdGFbW2ldXSA8LSBOVUxMCiAgICB9CiAgfQogIGJhbm5lckRhdGEgPC0gcmJpbmRsaXN0KGJhbm5lckRhdGEpCiAgYmFubmVyRGF0YSRWMSA8LSBOVUxMCiAgCiAgIyAtIGFnZ3JlZ2F0ZXMKICBwZXJCYW5uZXJUb3RhbHMgPC0gYmFubmVyRGF0YSAlPiUgCiAgICBzZWxlY3QoYmFubmVyLCBpbXByZXNzaW9ucykgJT4lIAogICAgZ3JvdXBfYnkoYmFubmVyKSAlPiUgCiAgICBzdW1tYXJpc2UodG90YWxJbXByZXNzaW9ucyA9IHN1bShpbXByZXNzaW9ucykpCiAgcGVyRGF5VG90YWxzIDwtIGJhbm5lckRhdGEgJT4lIAogICAgc2VsZWN0KGRhdGUsIGltcHJlc3Npb25zKSAlPiUgCiAgICBncm91cF9ieShkYXRlKSAlPiUgCiAgICBzdW1tYXJpc2UodG90YWxJbXByZXNzaW9ucyA9IHN1bShpbXByZXNzaW9ucykpCiAgCiAgIyAtIG91dHB1dAogIHJldHVybigKICAgIGxpc3QoYmFubmVySW1wcmVzc2lvbnNSZXBvcnQgPSBiYW5uZXJEYXRhLCAKICAgICAgICAgcGVyQmFubmVyVG90YWxzID0gcGVyQmFubmVyVG90YWxzLCAKICAgICAgICAgcGVyRGF5VG90YWxzID0gcGVyRGF5VG90YWxzKQogICkKICAKfQoKIyAtIFJlcG9ydCBiYW5uZXIgaW1wcmVzc2lvbnMKYmFubmVySW1wcmVzc2lvbnNEYXRhIDwtIHdtZGVfcmVwb3J0X2Jhbm5lcl9pbXByZXNzaW9ucyhkYXRhRGlyKQpiYW5uZXJJbXByZXNzaW9uc0ZpbGUgPC0gYmFubmVySW1wcmVzc2lvbnNEYXRhJGJhbm5lckltcHJlc3Npb25zUmVwb3J0CndyaXRlLmNzdihiYW5uZXJJbXByZXNzaW9uc0ZpbGUsIHBhc3RlMChhbmFseXRpY3NEaXIsICJiYW5uZXJJbXByZXNzaW9uc0ZpbGUuY3N2IikpCmJhbm5lclRvdGFscyA8LSBiYW5uZXJJbXByZXNzaW9uc0RhdGEkcGVyQmFubmVyVG90YWxzCndyaXRlLmNzdihiYW5uZXJUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJiYW5uZXJUb3RhbHMuY3N2IikpCmJhbm5lckRheVRvdGFscyA8LSBiYW5uZXJJbXByZXNzaW9uc0RhdGEkcGVyRGF5VG90YWxzCndyaXRlLmNzdihiYW5uZXJEYXlUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJiYW5uZXJEYXlUb3RhbHMuY3N2IikpCgoKIyMjIC0tLSBSZXBvcnQgUGFnZXZpZXdzIERhdGEKCiMgLSBmdW5jdGlvbjogd21kZV9yZXBvcnRfcGFnZXZpZXdzCndtZGVfcmVwb3J0X3BhZ2V2aWV3cyA8LSBmdW5jdGlvbihkYXRhRGlyKSB7CiAgCiAgIyAtIFNldHVwCiAgbGlicmFyeShkYXRhLnRhYmxlKQogIGxpYnJhcnkoZHBseXIpCiAgCiAgIyAtIGxpc3QgZmlsZXM6CiAgbEYgPC0gbGlzdC5maWxlcyhkYXRhRGlyKQogIAogICMgLSBmaWx0ZXIgYWdncmVnYXRlZCBiYW5uZXIgaW1wcmVzc2lvbiBkYXRhCiAgbEYgPC0gbEZbZ3JlcGwoInBhZ2V2aWV3c0FnZ3JlZ2F0ZWRfIiwgbEYsIGZpeGVkID0gVCldCiAgCiAgIyAtIGxvYWQgZmlsZXMgYW5kIG1lcmdlCiAgcGFnZXZpZXdzRGF0YSA8LSB2ZWN0b3IobW9kZSA9ICJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKGxGKSkKICBmb3IgKGkgaW4gMTpsZW5ndGgobEYpKSB7CiAgICBpZiAoZ3JlcGwoImNzdiR8dHN2JCIsIGxGW2ldKSkgewogICAgICBwYWdldmlld3NEYXRhW1tpXV0gPC0gZnJlYWQocGFzdGUwKGRhdGFEaXIsIGxGW2ldKSkKICAgIH0gZWxzZSB7CiAgICAgIHBhZ2V2aWV3c0RhdGFbW2ldXSA8LSBOVUxMCiAgICB9CiAgfQogIHBhZ2V2aWV3c0RhdGEgPC0gcmJpbmRsaXN0KHBhZ2V2aWV3c0RhdGEpCiAgcGFnZXZpZXdzRGF0YSRWMSA8LSBOVUxMCiAgCiAgIyAtIGFnZ3JlZ2F0ZXMKICBwZXJEYXlUb3RhbHMgPC0gcGFnZXZpZXdzRGF0YSAlPiUgCiAgICBzZWxlY3QoZGF0ZSwgUGFnZXZpZXdzKSAlPiUgCiAgICBncm91cF9ieShkYXRlKSAlPiUgCiAgICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKICBwZXJUYWdUb3RhbHMgPC0gcGFnZXZpZXdzRGF0YSAlPiUgCiAgICBzZWxlY3QoVGFnLCBQYWdldmlld3MpICU+JSAKICAgIGdyb3VwX2J5KFRhZykgJT4lIAogICAgc3VtbWFyaXNlKHRvdGFsUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCiAgcGVyUGFnZVRvdGFscyA8LSBwYWdldmlld3NEYXRhICU+JSAKICAgIHNlbGVjdChQYWdlLCBQYWdldmlld3MpICU+JSAKICAgIGdyb3VwX2J5KFBhZ2UpICU+JSAKICAgIHN1bW1hcmlzZSh0b3RhbFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQogIHBlclBhZ2VEYXlUb3RhbHMgPC0gcGFnZXZpZXdzRGF0YSAlPiUgCiAgICBzZWxlY3QoUGFnZSwgZGF0ZSwgUGFnZXZpZXdzKSAlPiUKICAgIGdyb3VwX2J5KFBhZ2UsIGRhdGUpICU+JSAKICAgIHN1bW1hcmlzZSh0b3RhbFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQogIHBlclRhZ0RheVRvdGFscyA8LSBwYWdldmlld3NEYXRhICU+JSAKICAgIHNlbGVjdChUYWcsIGRhdGUsIFBhZ2V2aWV3cykgJT4lCiAgICBncm91cF9ieShUYWcsIGRhdGUpICU+JSAKICAgIHN1bW1hcmlzZSh0b3RhbFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQogIHBlclRhZ1BhZ2VUb3RhbHMgPC0gcGFnZXZpZXdzRGF0YSAlPiUgCiAgICBzZWxlY3QoVGFnLCBQYWdlLCBQYWdldmlld3MpICU+JQogICAgZ3JvdXBfYnkoVGFnLCBQYWdlKSAlPiUgCiAgICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKICAKICAjIC0gb3V0cHV0CiAgcmV0dXJuKAogICAgbGlzdChwYWdldmlld3NEYXRhUmVwb3J0ID0gcGFnZXZpZXdzRGF0YSwgCiAgICAgICAgIHBlckRheVRvdGFscyA9IHBlckRheVRvdGFscywgCiAgICAgICAgIHBlclRhZ1RvdGFscyA9IHBlclRhZ1RvdGFscywgCiAgICAgICAgIHBlclBhZ2VUb3RhbHMgPSBwZXJQYWdlVG90YWxzLCAKICAgICAgICAgcGVyUGFnZURheVRvdGFscyA9IHBlclBhZ2VEYXlUb3RhbHMsIAogICAgICAgICBwZXJUYWdEYXlUb3RhbHMgPSBwZXJUYWdEYXlUb3RhbHMsCiAgICAgICAgIHBlclRhZ1BhZ2VUb3RhbHMgPSBwZXJUYWdQYWdlVG90YWxzKQogICkKICAKfQoKIyAtIFJlcG9ydCBwYWdldmlld3M6CnBhZ2V2aWV3c0RhdGEgPC0gd21kZV9yZXBvcnRfcGFnZXZpZXdzKGRhdGFEaXIpCnBhZ2V2aWV3c1JlcG9ydEZpbGUgPC0gcGFnZXZpZXdzRGF0YSRwYWdldmlld3NEYXRhUmVwb3J0CndyaXRlLmNzdihwYWdldmlld3NSZXBvcnRGaWxlLCBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAicGFnZXZpZXdzUmVwb3J0RmlsZS5jc3YiKSkKcGFnZXZpZXdzX3BlckRheVRvdGFscyA8LSBwYWdldmlld3NEYXRhJHBlckRheVRvdGFscwp3cml0ZS5jc3YocGFnZXZpZXdzX3BlckRheVRvdGFscywgcGFzdGUwKGFuYWx5dGljc0RpciwgInBhZ2V2aWV3c19wZXJEYXlUb3RhbHMuY3N2IikpCnBhZ2V2aWV3c19wZXJUYWdUb3RhbHMgPC0gcGFnZXZpZXdzRGF0YSRwZXJUYWdUb3RhbHMKd3JpdGUuY3N2KHBhZ2V2aWV3c19wZXJUYWdUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJwYWdldmlld3NfcGVyVGFnVG90YWxzLmNzdiIpKQpwYWdldmlld3NfcGVyUGFnZVRvdGFscyA8LSBwYWdldmlld3NEYXRhJHBlclBhZ2VUb3RhbHMKd3JpdGUuY3N2KHBhZ2V2aWV3c19wZXJQYWdlVG90YWxzLCBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAicGFnZXZpZXdzX3BlclBhZ2VUb3RhbHMuY3N2IikpCnBhZ2V2aWV3c19wZXJQYWdlRGF5VG90YWxzIDwtIHBhZ2V2aWV3c0RhdGEkcGVyUGFnZURheVRvdGFscwp3cml0ZS5jc3YocGFnZXZpZXdzX3BlclBhZ2VEYXlUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJwYWdldmlld3NfcGVyUGFnZURheVRvdGFscy5jc3YiKSkKcGFnZXZpZXdzX3BlclRhZ0RheVRvdGFscyA8LSBwYWdldmlld3NEYXRhJHBlclRhZ0RheVRvdGFscwp3cml0ZS5jc3YocGFnZXZpZXdzX3BlclRhZ0RheVRvdGFscywgcGFzdGUwKGFuYWx5dGljc0RpciwgInBhZ2V2aWV3c19wZXJUYWdEYXlUb3RhbHMuY3N2IikpCnBhZ2V2aWV3c19wZXJUYWdQYWdlVG90YWxzIDwtIHBhZ2V2aWV3c0RhdGEkcGVyVGFnUGFnZVRvdGFscwp3cml0ZS5jc3YocGFnZXZpZXdzX3BlclRhZ1BhZ2VUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJwZXJUYWdQYWdlVG90YWxzLmNzdiIpKQoKIyMjIC0tLSBSZXBvcnQgVXNlciBSZWdpc3RyYXRpb25zCgojIC0gZnVuY3Rpb246IHdtZGVfcmVwb3J0X3JlZ2lzdHJhdGlvbnNfaGl2ZQp3bWRlX3JlcG9ydF9yZWdpc3RyYXRpb25zX2hpdmUgPC0gZnVuY3Rpb24oZGF0YURpciwgY2FtcGFpZ24pIHsKICAKICAjIC0gU2V0dXAKICBsaWJyYXJ5KGRhdGEudGFibGUpCiAgbGlicmFyeShkcGx5cikKICAKICAjIC0gbGlzdCBmaWxlczoKICBsRiA8LSBsaXN0LmZpbGVzKGRhdGFEaXIpCiAgCiAgIyAtIGZpbHRlciBhZ2dyZWdhdGVkIHVzZXIgcmVnaXN0cmF0aW9uIGRhdGEKICBsRiA8LSBsRltncmVwbCgidXNlcl9yZWdpc3RyYXRpb25zLnRzdiIsIGxGLCBmaXhlZCA9IFQpXQogIAogICMgLSBsb2FkIGZpbGUgYW5kIHdyYW5nbGUgY29sdW1ucwogIHJlZ2lzdHJhdGlvbkRhdGEgPC0gZnJlYWQocGFzdGUwKGRhdGFEaXIsIGxGKSwgaGVhZGVyID0gVCkKICBjb2xuYW1lcyhyZWdpc3RyYXRpb25EYXRhKVs1XSA8LSAnZXZlbnRfY2FtcGFpZ24nCiAgY29sbmFtZXMocmVnaXN0cmF0aW9uRGF0YSlbNl0gPC0gJ2V2ZW50X3VzZXJJZCcKICByZWdpc3RyYXRpb25EYXRhJGNhbXBhaWduIDwtIGNhbXBhaWduCiAgcmVnaXN0cmF0aW9uRGF0YSRkYXkgPC0gaWZlbHNlKG5jaGFyKHJlZ2lzdHJhdGlvbkRhdGEkZGF5KSA9PSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIjAiLCByZWdpc3RyYXRpb25EYXRhJGRheSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWdpc3RyYXRpb25EYXRhJGRheSkKICByZWdpc3RyYXRpb25EYXRhJGRhdGUgPC0gcGFzdGUocmVnaXN0cmF0aW9uRGF0YSR5ZWFyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVnaXN0cmF0aW9uRGF0YSRtb250aCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVnaXN0cmF0aW9uRGF0YSRkYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICItIikKCiAgIyAtIGFnZ3JlZ2F0ZXMKICBwZXJEYXlUb3RhbHMgPC0gcmVnaXN0cmF0aW9uRGF0YSAlPiUgCiAgICBzZWxlY3QoZGF0ZSkgJT4lIAogICAgZ3JvdXBfYnkoZGF0ZSkgJT4lIAogICAgc3VtbWFyaXNlKHRvdGFsUmVnaXN0cmF0aW9ucyA9IG4oKSkKICBwZXJUYWdUb3RhbHMgPC0gcmVnaXN0cmF0aW9uRGF0YSAlPiUgCiAgICBzZWxlY3QoZXZlbnRfY2FtcGFpZ24pICU+JSAKICAgIGdyb3VwX2J5KGV2ZW50X2NhbXBhaWduKSAlPiUgCiAgICBzdW1tYXJpc2UodG90YWxSZWdpc3RyYXRpb25zID0gbigpKQogIHBlclRhZ0RheVRvdGFscyA8LSByZWdpc3RyYXRpb25EYXRhICU+JSAKICAgIHNlbGVjdChldmVudF9jYW1wYWlnbiwgZGF0ZSkgJT4lIAogICAgZ3JvdXBfYnkoZXZlbnRfY2FtcGFpZ24sIGRhdGUpICU+JSAKICAgIHN1bW1hcmlzZSh0b3RhbFJlZ2lzdHJhdGlvbnMgPSBuKCkpCiAgCiAgIyAtIGZ1bGwgcmVnaXN0cmF0aW9uIGRhdGFzZXQ6IG5vbi1hZ2dyZWdhdGVkIHJlZ2lzdHJhdGlvbnMKICAjIC0gZmlsdGVyIGRhdGE6CiAgZnVsbFJlZ0RhdGEgPC0gcmVnaXN0cmF0aW9uRGF0YSAlPiUgCiAgICBzZWxlY3QoZXZlbnRfdXNlcklkLCAKICAgICAgICAgICBldmVudF9jYW1wYWlnbiwgCiAgICAgICAgICAgZGF0ZSwgCiAgICAgICAgICAgY2FtcGFpZ24pCiAgCiAgIyAtIG91dHB1dAogIHJldHVybigKICAgIGxpc3QocmVnaXN0cmF0aW9uc0RhdGFSZXBvcnQgPSByZWdpc3RyYXRpb25EYXRhLCAKICAgICAgICAgcGVyRGF5VG90YWxzID0gcGVyRGF5VG90YWxzLCAKICAgICAgICAgcGVyVGFnVG90YWxzID0gcGVyVGFnVG90YWxzLCAKICAgICAgICAgcGVyVGFnRGF5VG90YWxzID0gcGVyVGFnRGF5VG90YWxzLAogICAgICAgICBmdWxsUmVnaXN0cmF0aW9uRGF0YXNldCA9IGZ1bGxSZWdEYXRhKQogICkKICAKfQoKIyAtIFJlcG9ydCB1cG9uIHVzZXIgcmVnaXN0cmF0aW9ucwpjYW1wYWlnbiA8LSAnMjAyMF9UaGFua1lvdScKdXNlclJlZ0RhdGEgPC0gd21kZV9yZXBvcnRfcmVnaXN0cmF0aW9uc19oaXZlKGRhdGFEaXIsIGNhbXBhaWduKQp1c2VyUmVnaXN0cmF0aW9uc1JlcG9ydEZpbGUgPC0gdXNlclJlZ0RhdGEkcmVnaXN0cmF0aW9uc0RhdGFSZXBvcnQKd3JpdGUuY3N2KHVzZXJSZWdpc3RyYXRpb25zUmVwb3J0RmlsZSwgcGFzdGUwKGFuYWx5dGljc0RpciwgInVzZXJSZWdpc3RyYXRpb25zUmVwb3J0RmlsZS5jc3YiKSkKdXNlclJlZ2lzdHJhdGlvbnNfcGVyRGF5VG90YWxzIDwtIHVzZXJSZWdEYXRhJHBlckRheVRvdGFscwp3cml0ZS5jc3YodXNlclJlZ2lzdHJhdGlvbnNfcGVyRGF5VG90YWxzLCBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAidXNlclJlZ2lzdHJhdGlvbnNfcGVyRGF5VG90YWxzLmNzdiIpKQp1c2VyUmVnaXN0cmF0aW9uc19wZXJUYWdUb3RhbHMgPC0gdXNlclJlZ0RhdGEkcGVyVGFnVG90YWxzCndyaXRlLmNzdih1c2VyUmVnaXN0cmF0aW9uc19wZXJUYWdUb3RhbHMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJ1c2VyUmVnaXN0cmF0aW9uc19wZXJUYWdUb3RhbHMuY3N2IikpCnVzZXJSZWdpc3RyYXRpb25zX3BlclRhZ0RheVRvdGFscyA8LSB1c2VyUmVnRGF0YSRwZXJUYWdEYXlUb3RhbHMKd3JpdGUuY3N2KHVzZXJSZWdpc3RyYXRpb25zX3BlclRhZ0RheVRvdGFscywgcGFzdGUwKGFuYWx5dGljc0RpciwgInVzZXJSZWdpc3RyYXRpb25zX3BlclRhZ0RheVRvdGFscy5jc3YiKSkKZnVsbFVzZXJSZWdpc3RyYXRpb25zIDwtIHVzZXJSZWdEYXRhJGZ1bGxSZWdpc3RyYXRpb25EYXRhc2V0CndyaXRlLmNzdihmdWxsVXNlclJlZ2lzdHJhdGlvbnMsIHBhc3RlMChhbmFseXRpY3NEaXIsICJmdWxsUmVnaXN0cmF0aW9uRGF0YXNldC5jc3YiKSkKYGBgCgoKIyMjIDAuMyBCYW5uZXIgQWN0aW9ucyBEYXRhIFNldAoKKipOT1RFOioqIE5vdCBydW4gZnJvbSB0aGlzIHJlcG9ydDsgdGhlIGRhdGEgd2VyZSBhbHJlYWR5IHByZS1wcm9jZXNzZWQgYW5kIGFnZ3JlZ2F0ZWQgYnkgdGhlIGZvbGxvd2luZyBgUmAgc2NyaXB0IGJlZm9yZSBiZWluZyBzdWJtaXR0ZWQgdG8gYW5hbHl0aWNhbCBwcm9jZWR1cmVzOgoKYGBge3IsIGV2YWwgPSBGfQojICFkaWFnbm9zdGljcyBvZmYKIyMjIC0tLSBSZXBvcnQgR2VuZXJhdGlvbiBmb3IgdGhlIFRoYW5rIFlvdSAyMDE5LzIwMjAgQ2FtcGFpZ24KIyMjIC0tLSBydW4gbG9jYWxseQoKIyMjIC0tLSBkaXIgc3RydWN0dXJlCmNhbXBhaWduUGF0aCA8LSAnL2hvbWUvZ29yYW5zbS9BbmFseXRpY3MvTmV3RWRpdG9ycy9DYW1wYWlnbnMvMjAyMF9UaGFua1lvdS8nCmRhdGFEaXIgPC0gcGFzdGUwKGNhbXBhaWduUGF0aCwgIl9kYXRhLyIpCmFuYWx5dGljc0RpciA8LSBwYXN0ZTAoY2FtcGFpZ25QYXRoLCAiX2FuYWx5dGljcy8iKQojIC0gY29tcG9zZSBIaXZlUUwgc2NyaXB0CmhpdmVRTCA8LQogICJVU0UgZXZlbnQ7IAogIFNFTEVDVCB5ZWFyLCBtb250aCwgZGF5LCBldmVudC5iYW5uZXJOYW1lIGFzIGJhbm5lciwgZXZlbnQuYmFubmVyQWN0aW9uIGFzIGFjdGlvbiwgQ09VTlQoKikgYXMgY291bnQgCiAgRlJPTSB3bWRlYmFubmVyZXZlbnRzIAogIFdIRVJFIHllYXIgPSAyMDIwIEFORCBtb250aCA9IDEgQU5EIAogICAgKGV2ZW50LmJhbm5lck5hbWUgTElLRSBcIldNREUlXCIgT1IgZXZlbnQuYmFubmVyTmFtZSBMSUtFIFwiV1BERSVcIikgCiAgR1JPVVAgQlkgeWVhciwgbW9udGgsIGRheSwgZXZlbnQuYmFubmVyTmFtZSwgZXZlbnQuYmFubmVyQWN0aW9uOyIKIyAtIHdyaXRlIGhxbAp3cml0ZShoaXZlUUwsIHBhc3RlMChkYXRhRGlyLCAnYmFubmVyX2FjdGlvbnMuaHFsJykpCiMjIyAtLS0gZXhlY3V0ZSBocWwgc2NyaXB0OgpmaWxlbmFtZSA8LSBwYXN0ZTAoZGF0YURpciwgJ2Jhbm5lcl9hY3Rpb25zX1dNREVfVGhhbmtZb3UyMDIwLnRzdicpCmhpdmVBcmdzIDwtICdzdWRvIC11IGFuYWx5dGljcy1wcml2YXRlZGF0YSBrZXJiZXJvcy1ydW4tY29tbWFuZCBhbmFseXRpY3MtcHJpdmF0ZWRhdGEgL3Vzci9sb2NhbC9iaW4vYmVlbGluZSAtLXNpbGVudCAtLWluY3JlbWVudGFsPXRydWUgLS12ZXJib3NlPWZhbHNlIC1mJwpoaXZlSW5wdXQgPC0gcGFzdGUocGFzdGUwKGRhdGFEaXIsICdiYW5uZXJfYWN0aW9ucy5ocWwnKSwKICAgICAgICAgICAgICAgICAgICIgPiAiLAogICAgICAgICAgICAgICAgICAgZmlsZW5hbWUsCiAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKIyAtIGNvbW1hbmQ6CmhpdmVDb21tYW5kIDwtIHBhc3RlKGhpdmVBcmdzLCBoaXZlSW5wdXQpCnN5c3RlbShjb21tYW5kID0gaGl2ZUNvbW1hbmQsIHdhaXQgPSBUUlVFKQpgYGAKCgojIyMgMC40IFVzZXIgRWRpdHMKCiMjIyMgMC40LjEgVXNlciBFZGl0cyB2aWEgcmV2aXNpb25fYWN0b3JfdGVtcAoKYGBge3IsIGV2YWwgPSBGfQpsaWJyYXJ5KGRhdGEudGFibGUpCiMgIWRpYWdub3N0aWNzIG9mZgojIyMgLS0tIHNjcmlwdDogVGhhbmtZb3UyMDIwX1VzZXJFZGl0cy5SCiMjIyAtLS0gVXNlciBFZGl0cyBBY3F1aXNpdGlvbiBmb3IgdGhlIFdNREUgVGhhbmsgWW91IENhbXBhaWduIDIwMTkvMjAyMAojIyMgLS0tIHJ1biBmcm9tIHN0YXQxMDA0CiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIFVzZXIgRWRpdHMKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gZGlyIHN0cnVjdHVyZQpjYW1wYWlnblBhdGggPC0gJy9ob21lL2dvcmFuc20vQW5hbHl0aWNzL05ld0VkaXRvcnMvQ2FtcGFpZ25zLzIwMjBfVGhhbmtZb3UvJwpkYXRhRGlyIDwtIHBhc3RlMChjYW1wYWlnblBhdGgsICJfZGF0YS8iKQphbmFseXRpY3NEaXIgPC0gcGFzdGUwKGNhbXBhaWduUGF0aCwgIl9hbmFseXRpY3MvIikKc3RhcnRUaW1lc3RhbXAgPC0gJzIwMjAwMTAxMDAwMDAwJwojIC0gZ2V0IHVzZXIgaWRzCnVzZXJSZWdpc3RyYXRpb25zIDwtIHJlYWQuY3N2KHBhc3RlMChhbmFseXRpY3NEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3VzZXJSZWdpc3RyYXRpb25zUmVwb3J0RmlsZS5jc3YnKSkKcmV2X3VzZXIgPC0gdXNlclJlZ2lzdHJhdGlvbnMkZXZlbnRfdXNlcklkCnJldl91c2VyX3RleHQgPC0gYXMuY2hhcmFjdGVyKHVzZXJSZWdpc3RyYXRpb25zJHVzZXJuYW1lKQojIC0gaXRlcmF0ZSBvdmVyIHJldl91c2VyCmZvciAoaSBpbiAxOmxlbmd0aChyZXZfdXNlcikpIHsKICAjIC0gU1FMIHF1ZXJ5CiAgc3FsUXVlcnkgPC0gcGFzdGUoIlwiU0VMRUNUIGFjdG9yLmFjdG9yX2lkLCBhY3Rvci5hY3Rvcl91c2VyLCBhY3Rvci5hY3Rvcl9uYW1lLCByZXZpc2lvbl9hY3Rvcl90ZW1wLnJldmFjdG9yX3RpbWVzdGFtcCBGUk9NIGFjdG9yIExFRlQgSk9JTiByZXZpc2lvbl9hY3Rvcl90ZW1wIE9OIChhY3Rvci5hY3Rvcl9pZCA9IHJldmlzaW9uX2FjdG9yX3RlbXAucmV2YWN0b3JfYWN0b3IpIFdIRVJFIChyZXZpc2lvbl9hY3Rvcl90ZW1wLnJldmFjdG9yX3RpbWVzdGFtcCA+PSAyMDE5MTEwMTAwMDAwMCBBTkQgYWN0b3IuYWN0b3JfdXNlciA9ICIsIHJldl91c2VyW2ldLCAiKTtcIiIpOwogICMjIyAtLS0gb3V0cHV0IGZpbGVuYW1lCiAgZmlsZW5hbWUgPC0gcGFzdGUoZGF0YURpciwndXNlckVkaXRzJywgIl8iLCBpLCAiLnRzdiIsIHNlcCA9ICIiKQogICMjIyAtLS0gZXhlY3V0ZSBzcWwgc2NyaXB0OgogIHNxbExvZ0luUHJlIDwtIHBhc3RlMCgnL3Vzci9sb2NhbC9iaW4vYW5hbHl0aWNzLW15c3FsIGRld2lraSAtZSAnKQogIHNxbElucHV0IDwtIHBhc3RlKHNxbFF1ZXJ5LAogICAgICAgICAgICAgICAgICAgICIgPiAiLAogICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lLAogICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQogICMgLSBjb21tYW5kOgogIHNxbENvbW1hbmQgPC0gcGFzdGUoc3FsTG9nSW5QcmUsIHNxbElucHV0KQogIHN5c3RlbShjb21tYW5kID0gc3FsQ29tbWFuZCwgd2FpdCA9IFRSVUUpCiAgIyAtIHJlcG9ydAogIHByaW50KHBhc3RlMCgiRE9ORTogdXNlciAiLCBpLCAiLiIpKQp9CiMjIyAtLS0gRU5EIHJ1biBTUUwgc2NyaXB0cwojIC0gbG9hZCB1c2VyIGVkaXRzOgpsRiA8LSBsaXN0LmZpbGVzKGRhdGFEaXIpCmxGIDwtIGxGW2dyZXBsKCJedXNlckVkaXRzXyIsIGxGKV0KdXNlckVkaXRzIDwtIGxhcHBseShwYXN0ZTAoZGF0YURpciwgbEYpLCBmcmVhZCkKdXNlckVkaXRzIDwtIHJiaW5kbGlzdCh1c2VyRWRpdHMpCiMgLSBzdG9yZSB1c2VyIGVkaXRzOgp3cml0ZS5jc3YodXNlckVkaXRzLCBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAndXNlckVkaXRzLmNzdicpKQpgYGAKCgojIyAxLiBDYW1wYWlnbiBCYW5uZXJzIGFuZCBQYWdlcwoKVGhpcyBzZWN0aW9uIHByZXNlbnRzIGFsbCBkYXRhIGFuZCBzdGF0aXN0aWNzIG9uIHRoZSBjYW1wYWlnbiBiYW5uZXJzIGFuZCBwYWdlcy4KCiMjIyAxLjEgQmFubmVyIEltcHJlc3Npb25zCiMjIyMgMS4xLjEgQmFubmVyIEltcHJlc3Npb25zIE92ZXJ2aWV3CgoqKkNoYXJ0IDEuMS4xKiogRGFpbHkgQmFubmVyIEltcHJlc3Npb25zCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCA9IDIwLCBmaWcud2lkdGggPSAxNX0KZGF0YVNldCA8LSByZWFkLmNzdigKICAnX2FuYWx5dGljcy9iYW5uZXJJbXByZXNzaW9uc0ZpbGUuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpkYXRhU2V0JHBsYXRmb3JtIDwtIHNhcHBseShkYXRhU2V0JGJhbm5lciwgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgiZHNrdCIsIHgpKSB7CiAgICByZXR1cm4oJ2Rza3QnKQogIH0gZWxzZSBpZiAoZ3JlcGwoIm1vYiIsIHgpKSB7CiAgICByZXR1cm4oJ21vYicpCiAgfSBlbHNlIGlmIChncmVwbCgiaXBhZCIsIHgpKSB7CiAgICByZXR1cm4oJ2lwYWQnKQogIH0gZWxzZSB7CiAgICByZXR1cm4oTkEpCiAgfQp9KQpkYXRhU2V0JGJhbm5lciA8LSBnc3ViKCJXTURFXzIwMTlfMjAyMF90aHhfIiwgIiIsIGRhdGFTZXQkYmFubmVyKSAgCmRhdGFTZXQkYmFubmVyIDwtIGdzdWIoImRza3R8bW9ifGlwYWQiLCAiIiwgZGF0YVNldCRiYW5uZXIpICAKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn0KZ2dwbG90KGRhdGFTZXQsIGFlcyh4ID0gZGF0ZSwKICAgICAgICAgICAgICAgICAgICB5ID0gaW1wcmVzc2lvbnMsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGJhbm5lciwKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGltcHJlc3Npb25zLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDYsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgZmFjZXRfd3JhcCh+cGxhdGZvcm0sIG5yb3cgPSBsZW5ndGgodW5pcXVlKGRhdGFTZXQkcGxhdGZvcm0pKSkgKyAKICBnZ3RpdGxlKCdUaGFuayBZb3UgQ2FtcGFpZ24gMjAxOS8yMDIwOiBCYW5uZXIgSW1wcmVzc2lvbnMnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDE4KSkgKwogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgKwogIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArIAogIHRoZW1lKHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKSArIAogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplID0gMTgpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKIyMjIyAxLjEuMSBCYW5uZXIgSW1wcmVzc2lvbnMgT3ZlcnZpZXc6IFRhYmxlCgoqKlRhYmxlIDEuMS4xLioqIERhaWx5IEJhbm5lciBJbXByZXNzaW9ucwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMjIyAtLS0gRnVsbCBEYXRhc2V0IChUYWJsZSBSZXBvcnQpCmRhdGFTZXQgPC0gZHBseXI6OnNlbGVjdChkYXRhU2V0LAogICAgICAgICAgICAgICAgICAgICAgICAgcGxhdGZvcm0sIGJhbm5lciwgZGF0ZSwgaW1wcmVzc2lvbnMpCmRhdGF0YWJsZShkYXRhU2V0KQpgYGAKCiMjIyMgMS4xLjIgVG90YWwgQmFubmVyIEltcHJlc3Npb25zCioqQ2hhcnQgMS4xLjIuKiogVG90YWwgQmFubmVyIEltcHJlc3Npb25zCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KZGF0YVNldCA8LSByZWFkLmNzdigKICAnX2FuYWx5dGljcy9iYW5uZXJUb3RhbHMuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpkYXRhU2V0JGJhbm5lciA8LSBnc3ViKCJXTURFXzIwMTlfMjAyMF90aHhfIiwgIiIsIGRhdGFTZXQkYmFubmVyKQpnZ3Bsb3QoZGF0YVNldCwgYWVzKHggPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgIHkgPSB0b3RhbEltcHJlc3Npb25zLCAKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGJhbm5lciwgCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGJhbm5lciwgCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB0b3RhbEltcHJlc3Npb25zKSkgKyAKICBnZW9tX2Jhcih3aWR0aCA9IC41LCBzdGF0ID0gImlkZW50aXR5IikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdUaGFuayBZb3UgQ2FtcGFpZ24gMjAxOS8yMDIwOiBCYW5uZXIgSW1wcmVzc2lvbnMnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV9sYWJlbF9yZXBlbChzaXplID0gMy41LCBjb2xvciA9ICJ3aGl0ZSIsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMjIyAxLjEuMyBCYW5uZXIgSW1wcmVzc2lvbnMgcGVyIERheQoKKipDaGFydCAxLjEuMy4qKiBCYW5uZXIgSW1wcmVzc2lvbnMgcGVyIERheQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmRhdGFTZXQgPC0gcmVhZC5jc3YoCiAgJ19hbmFseXRpY3MvYmFubmVyRGF5VG90YWxzLmNzdicsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKZGF0YVNldCR5ZWFyIDwtIHN1YnN0cihkYXRhU2V0JGRhdGUsIDEsIDQpCmRhdGFTZXQkbW9udGggPC0gc3Vic3RyKGRhdGFTZXQkZGF0ZSwgNiwgNykKZGF0YVNldCRkYXkgPC0gc3Vic3RyKGRhdGFTZXQkZGF0ZSwgOSwgMTApCmRhdGFTZXQkZGF5IDwtIGlmZWxzZShuY2hhcihkYXRhU2V0JGRheSkgPT0gMSwgCiAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIjAiLCBkYXRhU2V0JGRheSksCiAgICAgICAgICAgICAgICAgICAgICBkYXRhU2V0JGRheSkKZGF0YVNldCRkYXRlIDwtIHBhc3RlKGRhdGFTZXQkeWVhciwgZGF0YVNldCRtb250aCwgZGF0YVNldCRkYXksIHNlcCA9ICItIikKZ2dwbG90KGRhdGFTZXQsIGFlcyh4ID0gZGF0ZSwgCiAgICAgICAgICAgICAgICAgICAgeSA9IHRvdGFsSW1wcmVzc2lvbnMsIAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdG90YWxJbXByZXNzaW9ucykpICsKICBnZW9tX2xpbmUoc2l6ZSA9IC4yNSwgZ3JvdXAgPSAxLCBjb2xvciA9ICJkYXJrYmx1ZSIpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLjUsIGNvbG9yID0gImRhcmtibHVlIikgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnVGhhbmsgWW91IENhbXBhaWduIDIwMTkvMjAyMDogQmFubmVyIEltcHJlc3Npb25zJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fbGFiZWxfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gMTMpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMpKSArCiAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMykpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpICsgCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUobGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemUgPSAxMykpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyMgMS4xLjQgQmFubmVyIEFjdGlvbnM6IGZ1bGwgZGF0YSBzZXQKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gMjAsIGZpZy53aWR0aCA9IDE1fQpiYW5uZXJJbXByZXNzaW9ucyA8LSByZWFkLmNzdigKICAnX2FuYWx5dGljcy9iYW5uZXJJbXByZXNzaW9uc0ZpbGUuY3N2JywKICBoZWFkZXIgPSBULAogIHJvdy5uYW1lcyA9IDEsCiAgY2hlY2submFtZXMgPSBGLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpiYW5uZXJBY3Rpb25zIDwtIHJlYWQudGFibGUoCiAgJ19hbmFseXRpY3MvYmFubmVyX2FjdGlvbnNfV01ERV9UaGFua1lvdTIwMjAudHN2JywKICBzZXAgPSAiXHQiLAogIGhlYWRlciA9IFQsCiAgY2hlY2submFtZXMgPSBGLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpiYW5uZXJBY3Rpb25zJGJhbm5lciA8LSBnc3ViKCJXUERFIiwgIldNREUiLCBiYW5uZXJBY3Rpb25zJGJhbm5lciwgZml4ZWQgPSBUKQpiYW5uZXJBY3Rpb25zJG1vbnRoIDwtIGlmZWxzZShuY2hhcihiYW5uZXJBY3Rpb25zJG1vbnRoKSA9PSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIjAiLCBiYW5uZXJBY3Rpb25zJG1vbnRoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhbm5lckFjdGlvbnMkbW9udGgpCiMgYmFubmVyQWN0aW9ucyRkYXkgPC0gaWZlbHNlKG5jaGFyKGJhbm5lckFjdGlvbnMkZGF5KSA9PSAxLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiMCIsIGJhbm5lckFjdGlvbnMkZGF5KSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmFubmVyQWN0aW9ucyRkYXkpCmJhbm5lckFjdGlvbnMkZGF0ZSA8LSBwYXN0ZShiYW5uZXJBY3Rpb25zJHllYXIsIGJhbm5lckFjdGlvbnMkbW9udGgsIGJhbm5lckFjdGlvbnMkZGF5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICItIikKYmFubmVyQWN0aW9ucyA8LSBkcGx5cjo6c2VsZWN0KGJhbm5lckFjdGlvbnMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmFubmVyLCBhY3Rpb24sIGRhdGUsIGNvdW50KQpkYXRhU2V0IDwtIGRwbHlyOjpsZWZ0X2pvaW4oYmFubmVyQWN0aW9ucywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KGJhbm5lckltcHJlc3Npb25zLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmFubmVyLCBkYXRlLCBpbXByZXNzaW9ucyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoImJhbm5lciIgPSAiYmFubmVyIiwgImRhdGUiID0gImRhdGUiKQopCmRhdGFTZXQgPC0gZHBseXI6OmFycmFuZ2UoZGF0YVNldCwgZGF0ZSwgYmFubmVyKQoKCmRhdGFTZXQkcGxhdGZvcm0gPC0gc2FwcGx5KGRhdGFTZXQkYmFubmVyLCBmdW5jdGlvbih4KSB7CiAgaWYgKGdyZXBsKCJkc2t0IiwgeCkpIHsKICAgIHJldHVybignZHNrdCcpCiAgfSBlbHNlIGlmIChncmVwbCgibW9iIiwgeCkpIHsKICAgIHJldHVybignbW9iJykKICB9IGVsc2UgaWYgKGdyZXBsKCJpcGFkIiwgeCkpIHsKICAgIHJldHVybignaXBhZCcpCiAgfSBlbHNlIHsKICAgIHJldHVybihOQSkKICB9Cn0pCmRhdGFTZXQkYmFubmVyIDwtIGdzdWIoIldNREVfMjAxOV8yMDIwX3RoeF98V1BERV8yMDE5XzIwMjBfdGh4XyIsICIiLCBkYXRhU2V0JGJhbm5lcikgIApkYXRhU2V0JGJhbm5lciA8LSBnc3ViKCJkc2t0fG1vYnxpcGFkIiwgIiIsIGRhdGFTZXQkYmFubmVyKQpkYXRhU2V0IDwtIGRwbHlyOjpmaWx0ZXIoZGF0YVNldCwKICAgICAgICAgICAgICAgICAgICAgICAgICEoZGF0YVNldCRkYXRlICVpbiUgYygiMjAyMC0wMS0yMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDEtMjEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMS0yMiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAxLTIzIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkKZGF0YVNldCR5ZWFyIDwtIHN1YnN0cihkYXRhU2V0JGRhdGUsIDEsIDQpCmRhdGFTZXQkbW9udGggPC0gc3Vic3RyKGRhdGFTZXQkZGF0ZSwgNiwgNykKZGF0YVNldCRkYXkgPC0gc3Vic3RyKGRhdGFTZXQkZGF0ZSwgOSwgMTApCmRhdGFTZXQkZGF5IDwtIGlmZWxzZShuY2hhcihkYXRhU2V0JGRheSkgPT0gMSwgCiAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIjAiLCBkYXRhU2V0JGRheSksCiAgICAgICAgICAgICAgICAgICAgICBkYXRhU2V0JGRheSkKZGF0YVNldCRkYXRlIDwtIHBhc3RlKGRhdGFTZXQkeWVhciwgZGF0YVNldCRtb250aCwgZGF0YVNldCRkYXksIHNlcCA9ICItIikKZGF0YVNldCA8LSBkYXRhU2V0WywgYygncGxhdGZvcm0nLCAnYmFubmVyJywgJ2FjdGlvbicsICdjb3VudCcsICdpbXByZXNzaW9ucycsICdkYXRlJyldCmRhdGF0YWJsZShkYXRhU2V0KQoKYGBgCgoKIyMjIDEuMS40IEJhbm5lciBBY3Rpb25zOiBtaW5pLWJhbm5lci1leHBhbmRlZAoKVGhlIGNoYXJ0cyBhcmUgb3JnYW5pemVkIGJ5IHBsYXRmb3JtIChgZHNrdGAsIGBtb2JgLCBvciBgaXBhZGApIGFuZCBgX2N0cmxgIHZzLiBgX3ZhcmAgYmFubmVycy4gVGhlIHZlcnRpY2FsIGF4ZXMgcmVwcmVzZW50ICUgb2YgYmFubmVyIGFjdGlvbnMgcmVsYXRpdmUgdG8gdGhlIG51bWJlciBvZiBiYW5uZXIgaW1wcmVzc2lvbnMgc2VydmVkIGZyb20gdGhhdCBiYW5uZXIuIFRoZSBkYXRhIHBvaW50cyBhcmUgbGFiZWxlZCBieSBleGFjdCBjb3VudHMgb2YgYmFubmVyIGFjdGlvbnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCA9IDIwLCBmaWcud2lkdGggPSAxNX0KcEZyYW1lIDwtIGRwbHlyOjpmaWx0ZXIoZGF0YVNldCwKICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoImV4cGFuZGVkIiwgZGF0YVNldCRhY3Rpb24pKQpwRnJhbWUkYWN0aW9uIDwtIE5VTEwKcEZyYW1lJHBlcmNlbnQgPC0gcm91bmQocEZyYW1lJGNvdW50L3BGcmFtZSRpbXByZXNzaW9ucywgMikgKiAxMDAKcEZyYW1lJGltcHJlc3Npb25zIDwtIE5VTEwKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn0KZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IHBlcmNlbnQsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IGJhbm5lciwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGNvdW50LAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9saW5lKHNpemUgPSAuMjUsIGdyb3VwID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdUaGFuayBZb3UgQ2FtcGFpZ24gMjAxOS8yMDIwOiBCYW5uZXIgRXhwYW5kJykgKwogIGZhY2V0X3dyYXAofnBsYXRmb3JtICsgYmFubmVyLCBuY29sID0gMikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gNiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gMTMpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMpKSArCiAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMykpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsgCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUobGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemUgPSAxMykpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKIyMjIDEuMS41IEJhbm5lciBBY3Rpb25zOiBiYW5uZXItY2xvc2VkCgpUaGUgY2hhcnRzIGFyZSBvcmdhbml6ZWQgYnkgcGxhdGZvcm0gKGBkc2t0YCwgYG1vYmAsIG9yIGBpcGFkYCkgYW5kIGBfY3RybGAgdnMuIGBfdmFyYCBiYW5uZXJzLiBUaGUgdmVydGljYWwgYXhlcyByZXByZXNlbnQgJSBvZiBiYW5uZXIgYWN0aW9ucyByZWxhdGl2ZSB0byB0aGUgbnVtYmVyIG9mIGJhbm5lciBpbXByZXNzaW9ucyBzZXJ2ZWQgZnJvbSB0aGF0IGJhbm5lci4gVGhlIGRhdGEgcG9pbnRzIGFyZSBsYWJlbGVkIGJ5IGV4YWN0IGNvdW50cyBvZiBiYW5uZXIgYWN0aW9ucy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gMjAsIGZpZy53aWR0aCA9IDE1fQpwRnJhbWUgPC0gZHBseXI6OmZpbHRlcihkYXRhU2V0LAogICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiY2xvc2VkIiwgZGF0YVNldCRhY3Rpb24pKQpwRnJhbWUkYWN0aW9uIDwtIE5VTEwKcEZyYW1lJHBlcmNlbnQgPC0gcm91bmQocEZyYW1lJGNvdW50L3BGcmFtZSRpbXByZXNzaW9ucywgMikgKiAxMDAKcEZyYW1lJGltcHJlc3Npb25zIDwtIE5VTEwKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn0KZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IHBlcmNlbnQsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IGJhbm5lciwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGNvdW50LAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9saW5lKHNpemUgPSAuMjUsIGdyb3VwID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdUaGFuayBZb3UgQ2FtcGFpZ24gMjAxOS8yMDIwOiBCYW5uZXIgQ2xvc2UnKSArCiAgZmFjZXRfd3JhcCh+cGxhdGZvcm0gKyBiYW5uZXIsIG5jb2wgPSAyKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSA2LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSAxMykpICsKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMpKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMykpICsKICB0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgKyAKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZShsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZSA9IDEzKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgojIyMgMS4yIFBhZ2V2aWV3cwoKIyMjIyAxLjIuMSBQYWdldmlld3MgT3ZlcnZpZXcKCioqQ2hhcnQgMS4yLjEuKiogUGFnZXZpZXdzIE92ZXJ2aWV3LiAKCmBgYHtyIGV2YWw9VCwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTksIGVjaG89RkFMU0V9CmRhdGFTZXQgPC0gcmVhZC5jc3YoCiAgJ19hbmFseXRpY3MvcGFnZXZpZXdzX3BlclBhZ2VEYXlUb3RhbHMuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpkYXRhU2V0JFBhZ2UgPC0gZ3N1YigiL3dpa2kvV2lraXBlZGlhOiIsICIiLCBkYXRhU2V0JFBhZ2UpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChkYXRhU2V0LCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICAgeSA9IHRvdGFsUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gUGFnZSwKICAgICAgICAgICAgICAgICAgICBmaWxsID0gUGFnZSwKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHRvdGFsUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuMjUsIGdyb3VwID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdUaGFuayBZb3UgQ2FtcGFpZ24gMjAxOS8yMDIwOiBQYWdldmlld3MnKSArCiAgZmFjZXRfd3JhcCh+UGFnZSwgbmNvbCA9IDMpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDYsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDEzKSkgKwogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMykpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKSArIAogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplID0gMTMpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCgojIyMjIDEuMi4xIFBhZ2V2aWV3cyBPdmVydmlldzogVGFibGUKCioqVGFibGUgMS4yLjEuKiogUGFnZXZpZXdzIE92ZXJ2aWV3CgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KIyMjIC0tLSBGdWxsIERhdGFzZXQgKFRhYmxlIFJlcG9ydCkKZGF0YXRhYmxlKGRhdGFTZXQgJT4lIGFycmFuZ2UoZGVzYyh0b3RhbFBhZ2V2aWV3cykpKQpgYGAKCgoKIyMjIyAxLjIuMyBQYWdldmlld3MgT3ZlcnZpZXc6IHRvdGFscyBwZXIgVGFnL1BhZ2UKCioqQ2hhcnQgMS4yLjMuKiogUGFnZXZpZXdzIE92ZXJ2aWV3OiB0b3RhbHMgcGVyIFRhZy9QYWdlCgpgYGB7ciBldmFsPVQsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD04LCBlY2hvPUZBTFNFfQpkYXRhU2V0IDwtIHJlYWQuY3N2KAogICdfYW5hbHl0aWNzL3BlclRhZ1BhZ2VUb3RhbHMuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpkYXRhU2V0IDwtIGZpbHRlcihkYXRhU2V0LCAKICAgICAgICAgICAgICAgICAgIShncmVwbCgiPyZwaXdpa18iLCBkYXRhU2V0JFRhZykpKQpkYXRhU2V0JFRhZyA8LSBnc3ViKCJcXD8mY2FtcGFpZ249V01ERV8yMDE5XzIwMjBfIiwgIiIsIGRhdGFTZXQkVGFnKQpkYXRhU2V0JFRhZyA8LSBnc3ViKCJcXD8mY2FtcGFpZ249V1BERV8yMDE5XzIwMjBfIiwgIldQREVfIiwgZGF0YVNldCRUYWcpCmRhdGFTZXQgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KFRhZywgUGFnZSwgdG90YWxQYWdldmlld3MpICU+JSAKICBncm91cF9ieShUYWcsIFBhZ2UpICU+JSAKICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0odG90YWxQYWdldmlld3MpKQpnZ3Bsb3QoZGF0YVNldCwgYWVzKHggPSBUYWcsIAogICAgICAgICAgICAgICAgICAgIHkgPSB0b3RhbFBhZ2V2aWV3cywgCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB0b3RhbFBhZ2V2aWV3cykpICsKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSwgZ3JvdXAgPSAxLCBjb2xvciA9ICJkYXJrYmx1ZSIpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLjUsIGNvbG9yID0gImRhcmtibHVlIikgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnVGhhbmsgWW91IENhbXBhaWduIDIwMTkvMjAyMDogUGFnZXZpZXdzIHBlciBUYWcnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV9sYWJlbF9yZXBlbChzaXplID0gNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gMTUpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMpKSArCiAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMykpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpICsgCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUobGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemUgPSAxMykpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyMjIDEuMi40IFBhZ2V2aWV3cyBwZXIgVGFnLCBkYWlseSB0b3RhbHMKKipDaGFydCAxLjIuNC4gUGFnZXZpZXdzIHBlciBUYWcsIGRhaWx5IHRvdGFscy4qKiBUaGUgbnVtYmVycyByZXBvcnRlZCBpbiB0aGUgZGF0YSBwb2ludCBsYWJlbHMgYXJlIGV4YWN0LiBUaGUgZXJyb25lb3VzIGBXUERFYCB0YWcgKHNlZTogW1BoYWIgVDI0MDM1MV0oaHR0cHM6Ly9waGFicmljYXRvci53aWtpbWVkaWEub3JnL1QyNDAzNTEjNTgyMDIzMSkpIGlzIG1hcmtlZCBzZXBhcmF0ZWx5LgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xNSx9CmRhdGFTZXQgPC0gcmVhZC5jc3YoCiAgJ19hbmFseXRpY3MvcGFnZXZpZXdzX3BlclRhZ0RheVRvdGFscy5jc3YnLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmRhdGFTZXQgPC0gZmlsdGVyKGRhdGFTZXQsIAogICAgICAgICAgICAgICAgICAhKGdyZXBsKCI/JnBpd2lrXyIsIGRhdGFTZXQkVGFnKSkpCmRhdGFTZXQkVGFnIDwtIGdzdWIoIlxcPyZjYW1wYWlnbj1XTURFXzIwMTlfMjAyMF8iLCAiIiwgZGF0YVNldCRUYWcpCmRhdGFTZXQkVGFnIDwtIGdzdWIoIlxcPyZjYW1wYWlnbj1XUERFXzIwMTlfMjAyMF8iLCAiV1BERV8iLCBkYXRhU2V0JFRhZykKZGF0YVNldCA8LSBkYXRhU2V0ICU+JSAKICBncm91cF9ieShUYWcsIGRhdGUpICU+JSAKICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0odG90YWxQYWdldmlld3MpKQpkYXRhU2V0JHBsYXRmb3JtIDwtIHNhcHBseShkYXRhU2V0JFRhZywgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgiZHNrdCIsIHgpKSB7CiAgICByZXR1cm4oJ2Rza3QnKQogIH0gZWxzZSBpZiAoZ3JlcGwoIm1vYiIsIHgpKSB7CiAgICByZXR1cm4oJ21vYicpCiAgfSBlbHNlIGlmIChncmVwbCgiaXBhZCIsIHgpKSB7CiAgICByZXR1cm4oJ2lwYWQnKQogIH0gZWxzZSB7CiAgICByZXR1cm4oTkEpCiAgfQp9KQpnZ3Bsb3QoZGF0YVNldCwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgIHkgPSB0b3RhbFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICBncm91cCA9IFRhZywKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFRhZywKICAgICAgICAgICAgICAgICAgICBmaWxsID0gVGFnLAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdG90YWxQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBmYWNldF93cmFwKH5wbGF0Zm9ybSwgbmNvbCA9IDEpICsKICBnZ3RpdGxlKCdUaGFuayBZb3UgQ2FtcGFpZ24gMjAxOS8yMDIwOiBQYWdldmlld3MgcGVyIFRhZywgZGFpbHkgdG90YWxzJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSAxOCkpICsKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICB0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKyAKICB0aGVtZShzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgKyAKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZShsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCiMjIDIuIFVzZXIgUmVnaXN0cmF0aW9ucwoKQWxsIGRhdGEgb24gdXNlciByZWdpc3RyYXRpb25zIGFyZSBwcmVzZW50ZWQgaW4gdGhpcyBzZWN0aW9uLgoKIyMjIDIuMSBSZWdpc3RyYXRpb25zIHBlciB0YWcgYW5kIGRheQoKKipDaGFydCAyLjEuKiogUmVnaXN0cmF0aW9ucyBwZXIgdGFnIGFuZCBkYXkuIFBsZWFzZSBub3RlOiBwb2ludHMgd2l0aCBubyBkYXRhIGxhYmVscyBzaWduaWZ5IDAgdXNlciByZWdpc3RyYXRpb25zLgoKYGBge3IgZXZhbD1ULCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NywgZWNobz1GQUxTRX0KIyAtIFN0YW5kYXJkIHJlZ2lzdHJhdGlvbnMKZGF0YVNldCA8LSByZWFkLmNzdigKICAnX2FuYWx5dGljcy91c2VyUmVnaXN0cmF0aW9uc1JlcG9ydEZpbGUuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpkYXRhU2V0JGV2ZW50X2NhbXBhaWduIDwtIGdzdWIoIldNREVfMjAxOV8yMDIwX3RoeF8iLCAiIiwgZGF0YVNldCRldmVudF9jYW1wYWlnbikKZGF0YVNldCA8LSBkYXRhU2V0ICU+JQogIHNlbGVjdChkYXRlLCBldmVudF9jYW1wYWlnbikgJT4lIAogIGdyb3VwX2J5KGV2ZW50X2NhbXBhaWduLCBkYXRlKSAlPiUgCiAgc3VtbWFyaXNlKFJlZ2lzdHJhdGlvbnMgPSBuKCkpCmdncGxvdChkYXRhU2V0LCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICAgeSA9IFJlZ2lzdHJhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBldmVudF9jYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGV2ZW50X2NhbXBhaWduLAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSBldmVudF9jYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFJlZ2lzdHJhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdUaGFuayBZb3UgQ2FtcGFpZ24gMjAxOS8yMDIwOiBSZWdpc3RyYXRpb25zIHBlciBUYWcnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSA1LCBzaG93LmxlZ2VuZCA9IEYpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDEzKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplID0gMTgpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMjIyAtLS0gRnVsbCBEYXRhc2V0IChUYWJsZSBSZXBvcnQpCmRhdGF0YWJsZShkcGx5cjo6YXJyYW5nZShkYXRhU2V0LCBldmVudF9jYW1wYWlnbiwgZGF0ZSwgZGVzYyhSZWdpc3RyYXRpb25zKSkpCmBgYAoKIyMjIDIuMiBUb3RhbCByZWdpc3RyYXRpb25zIHBlciB0YWcKKipDaGFydCAyLjIuKiogVG90YWwgcmVnaXN0cmF0aW9ucyBwZXIgdGFnLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmRhdGFTZXQgPC0gZGF0YVNldCAlPiUgCiAgZ3JvdXBfYnkoZXZlbnRfY2FtcGFpZ24pICU+JSAKICBzdW1tYXJpc2UodG90YWxSZWdpc3RyYXRpb25zID0gc3VtKFJlZ2lzdHJhdGlvbnMpKQpnZ3Bsb3QoZGF0YVNldCwgYWVzKHggPSBldmVudF9jYW1wYWlnbiwgCiAgICAgICAgICAgICAgICAgICAgeSA9IHRvdGFsUmVnaXN0cmF0aW9ucywgCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBldmVudF9jYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgICBmaWxsID0gZXZlbnRfY2FtcGFpZ24sCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB0b3RhbFJlZ2lzdHJhdGlvbnMpKSArIAogIGdlb21fYmFyKHdpZHRoID0gLjUsIHN0YXQgPSAiaWRlbnRpdHkiKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ1RoYW5rIFlvdSBDYW1wYWlnbiAyMDE5LzIwMjA6IFRvdGFsIFJlZ2lzdHJhdGlvbnMgcGVyIFRhZycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX2xhYmVsX3JlcGVsKHNpemUgPSAzLjUsIGNvbG9yID0gIndoaXRlIiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsgCiAgeWxhYigiUmVnaXN0cmF0aW9ucyIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZShsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZSA9IDE0KSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCiMjIDMuIFVzZXIgRWRpdHMKCkFsbCBkYXRhIG9uIHVzZXIgZWRpdHMgYXJlIHByZXNlbnRlZCBpbiB0aGlzIHNlY3Rpb24uCgojIyMgMy4xIFVzZXIgZWRpdHM6IGRpc3RyaWJ1dGlvbgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnVzZXJFZGl0cyA8LSByZWFkLmNzdigKICAnX2FuYWx5dGljcy91c2VyRWRpdHMuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQp1c2VyRWRpdHMkcmV2YWN0b3JfdGltZXN0YW1wIDwtIGFzLmNoYXJhY3Rlcih1c2VyRWRpdHMkcmV2YWN0b3JfdGltZXN0YW1wKQpkYXRhU2V0IDwtIHVzZXJFZGl0cyAlPiUKICBkcGx5cjo6c2VsZWN0KGFjdG9yX3VzZXIpICU+JQogIGRwbHlyOjpncm91cF9ieShhY3Rvcl91c2VyKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShlZGl0cyA9IG4oKSkKIyAtIEVkaXQgfCAxIHwgMi00IHwgNS05IHwgMTAtNDkgfCA+NTAKZWRpdEJvdW5kYXJpZXMgPC0gbGlzdCgKICBjKDAsIDEpLCAKICBjKDIsIDQpLAogIGMoNSwgOSksCiAgYygxMCwgNDkpCikKZGF0YVNldCRlZGl0Q2xhc3MgPC0gc2FwcGx5KGRhdGFTZXQkZWRpdHMsIGZ1bmN0aW9uKHgpIHsKICB3RUMgPC0gc2FwcGx5KGVkaXRCb3VuZGFyaWVzLCBmdW5jdGlvbih5KSB7CiAgICB4ID49IHlbMV0gJiB4IDw9IHlbMl0KICB9KQogIGlmIChzdW0od0VDKSA9PSAwKSB7CiAgICByZXR1cm4oIj49IDUwIikKICB9IGVsc2UgewogICAgcmV0dXJuKHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMV0sCiAgICAgICAgICAgICAgICAgICIgLSAiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzJdLCAKICAgICAgICAgICAgICAgICAgIikiCiAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0pCmVkaXRDbGFzcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKGRhdGFTZXQkZWRpdENsYXNzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhlZGl0Q2xhc3MpIDwtIGMoJ0VkaXQgQ2xhc3MnLCAnTnVtLlVzZXJzJykKZWRpdENsYXNzJG9yZGVyIDwtIGFzLm51bWVyaWMoc2FwcGx5KGVkaXRDbGFzcyRgRWRpdCBDbGFzc2AsIGZ1bmN0aW9uKHgpIHsKICBsb3dlciA8LSBzdHJfZXh0cmFjdCh4LCAnW1s6ZGlnaXQ6XV0rJykKfSkpCmVkaXRDbGFzcyA8LSBhcnJhbmdlKGVkaXRDbGFzcywgb3JkZXIpCmVkaXRDbGFzcyRvcmRlciA8LSBOVUxMCmRhdGF0YWJsZShlZGl0Q2xhc3MpCmBgYAoKIyMgNC4gVHJhaW5pbmcgTW9kdWxlcwoKQWxsIGRhdGEgb24gdHJhaW5pbmcgbW9kdWxlcyBhcmUgcHJlc2VudGVkIGluIHRoaXMgc2VjdGlvbi4KCiMjIyA0LjEgU3RhcnRlZCBUcmFpbmluZyBNb2R1bGVzCgpPbmx5IG9uZSBjYW1wYWlnbiByZWdpc3RlcmVkIHVzZXIgc3RhcnRlZCBhIHRyYWluaW5nIG1vZHVsZS4gCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdE1vZHVsZXMgPC0gcmVhZC5jc3YoJ19hbmFseXRpY3Mvd21kZV90cmFpbmluZ19kYXRhXzIwMjAtMDEuY3N2JywKICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKdXNlclJlZyA8LSByZWFkLmNzdigKICAnX2FuYWx5dGljcy91c2VyUmVnaXN0cmF0aW9uc1JlcG9ydEZpbGUuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQp0TW9kdWxlcyA8LSB0TW9kdWxlcyAlPiUgCiAgZHBseXI6OmZpbHRlcih0TW9kdWxlcyR1c2VybmFtZSAlaW4lIHVzZXJSZWckdXNlcm5hbWUpCnRNb2R1bGVzX092ZXJ2aWV3IDwtIHRNb2R1bGVzICU+JQogIGRwbHlyOjpzZWxlY3QodHJhaW5pbmdfbW9kdWxlKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHRyYWluaW5nX21vZHVsZSkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UobnVtX3VzZXJzID0gbigpKQpkYXRhdGFibGUodE1vZHVsZXNfT3ZlcnZpZXcpCmBgYAoKCiMjIyA0LjIgQ29tcGxldGVkIFRyYWluaW5nIE1vZHVsZXMKClRoZSBmb2xsb3dpbmcgdGFibGUgcHJlc2VudHMgdGhlIHN0YXRpc3RpY3Mgb24gY29tcGxldGVkIHRyYWluaW5nIG1vZHVsZXMuCioqTk9URS4qKiBPbmx5IG9uZSB1c2VyIGhhcyBjb21wbGV0ZWQgYSB0cmFpbmluZyBtb2R1bGUuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdE1vZHVsZXMgPC0gcmVhZC5jc3YoJ19hbmFseXRpY3Mvd21kZV90cmFpbmluZ19kYXRhXzIwMjAtMDEuY3N2JywKICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKdXNlclJlZyA8LSByZWFkLmNzdigKICAnX2FuYWx5dGljcy91c2VyUmVnaXN0cmF0aW9uc1JlcG9ydEZpbGUuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQp0TW9kdWxlcyA8LSB0TW9kdWxlcyAlPiUgCiAgZHBseXI6OmZpbHRlcih0TW9kdWxlcyR1c2VybmFtZSAlaW4lIHVzZXJSZWckdXNlcm5hbWUpCnRNb2R1bGVzIDwtIHRNb2R1bGVzICU+JQogIGRwbHlyOjpmaWx0ZXIobmNoYXIobW9kdWxlX2NvbXBsZXRpb25fZGF0ZSkgPiAwKQp0TW9kdWxlc19PdmVydmlldyA8LSB0TW9kdWxlcyAlPiUKICBkcGx5cjo6c2VsZWN0KHRyYWluaW5nX21vZHVsZSkgJT4lIAogIGRwbHlyOjpncm91cF9ieSh0cmFpbmluZ19tb2R1bGUpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKG51bV91c2VycyA9IG4oKSkKZGF0YXRhYmxlKHRNb2R1bGVzX092ZXJ2aWV3KQpgYGAKCiMjIyA0LjMgQ29tcGxldGVkIFRyYWluaW5nIE1vZHVsZXMgYW5kIFVzZXIgRWRpdHMKClRoZSBmb2xsb3dpbmcgdGFibGUgcHJlc2VudHMgdGhlIHN0YXRpc3RpY3Mgb24gY29tcGxldGVkIHRyYWluaW5nIG1vZHVsZXMgYW5kIHRoZSByZXNwZWN0aXZlIHVzZXJzJyBlZGl0cy4KKipOT1RFLioqIFRoZSBvbmUgYW5kIHRoZSBvbmx5IHVzZXIgd2hvIGNvbXBsZXRlZCBhbnkgdHJhaW5pbmcgbW9kdWxlcyBtYWRlIGA2YCBlZGl0cy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQp0TW9kdWxlcyA8LSByZWFkLmNzdignX2FuYWx5dGljcy93bWRlX3RyYWluaW5nX2RhdGFfMjAyMC0wMS5jc3YnLAogICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQp1c2VyUmVnIDwtIHJlYWQuY3N2KAogICdfYW5hbHl0aWNzL3VzZXJSZWdpc3RyYXRpb25zUmVwb3J0RmlsZS5jc3YnLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnRNb2R1bGVzIDwtIHRNb2R1bGVzICU+JSAKICBkcGx5cjo6ZmlsdGVyKHRNb2R1bGVzJHVzZXJuYW1lICVpbiUgdXNlclJlZyR1c2VybmFtZSkKdE1vZHVsZXMgPC0gdE1vZHVsZXMgJT4lCiAgZHBseXI6OmZpbHRlcihuY2hhcihtb2R1bGVfY29tcGxldGlvbl9kYXRlKSA+IDApCnRNb2R1bGVzX0VkaXRzIDwtIHRNb2R1bGVzICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4odXNlckVkaXRzLCAKICAgICAgICAgICAgICAgICAgIGJ5ID0gYygidXNlcm5hbWUiID0gImFjdG9yX25hbWUiKSkgJT4lIAogIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKHJldmFjdG9yX3RpbWVzdGFtcCkpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkodHJhaW5pbmdfbW9kdWxlKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShlZGl0cyA9IG4oKSkKZGF0YXRhYmxlKHRNb2R1bGVzX0VkaXRzKQpgYGAKCgoK