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

The campaign was run from 2020/10/01 to 2020/10/11.

CURRENT UPDATE: Dataset as of 2020/10/24.

Data Acquisiton

NOTE: the Data Acquisition code chunk is not fully reproducible from this Report. The data are collected by running the script 2020_WikipediaChallenge_October_2020_Campaign_PRODUCTION.R on stat1007.eqiad.wmnet, collecting the data as .tsv and .csv files, copying manually, and processing locally. A daily crontab job was run from 2020/10/01 to 2020/10/11 to collect the data for daily reporting. User registrations are then collected until November 11th 2020, and user edits are collected until the end of the year. The data used in this report are aggregates of the daily datasets, sanitized and anonymized.

DATA: (1) The event.wmdebannerinteractions schema is used for banner clicks/closing (see: Phab); (2) the wmf.webrequest table is a source of banner impressions data; (3) pageviews, user registrations, and user edits are collected by following the standard procedure.

Daily Update

### --- WMDE_Wikipedia_Challenge_October_2020_Campaign.PRODUCTION.R
### --- https://phabricator.wikimedia.org/T262534

### ----------------------------------------------------------------
### --- Campaign Description and Parameters
### ----------------------------------------------------------------

### --- Tracking Part 1: banner campaign
# - Please provide daily reports during the banner campaign (October 1st - 11th).

### --- Tracking Part 2: emailing campaign Wikipedia challenge
# - Please provide a tracking report für the Wikipedia challenge email campaign 
# - as already implemented see https://phabricator.wikimedia.org/T251524 from October to December.

### ---Timeline for the whole project
# - Start of the banner campaign: 1. October 2020
# - End of the banner campaign: 11. October 2020
# - Tracking test: End of September
# - Last User signing up: 11. October
# - Preliminary report for tracking part 1: sometime until end of October
# - Last User finishing mailing campaign: November 11th
# - Track editing behavior six weeks after end of campaign: December 23
# - final report for tracking part 1 and 2: beginning of January

### --- Banner Campaign information
# - There will be one campaign with 6 different banners (including mobile versions) 
# - which target readers in wikipedia. Banner campaign goal are registrations 
# - for the emailing campaign.

# - Find all details in this document: 
# - https://docs.google.com/document/d/1kTlLdsx9_rD-k8Xcp2yFvb9ftHDxkMn6-MJ1-5ihtS8/edit

### --- Campaign Tags and Landingpages
# - Landing Page 1: https://www.wikimedia.de/wikipedia-challenge

# - Banner 1
# - ?campaign=WMDE_challengebnr_fall2020_ctrl
# - https://www.wikimedia.de/wikipedia-challenge?campaign=WMDE_challengebnr_fall2020_ctrl
# - ?campaign=WMDE_challengebnr_fall2020_ctrl_ipad
# - https://www.wikimedia.de/wikipedia-challenge?campaign=WMDE_challengebnr_fall2020_ctrl_ipad
# - ?campaign=WMDE_challengebnr_fall2020_ctrl_mobile
# - https://www.wikimedia.de/wikipedia-challenge?campaign=WMDE_challengebnr_fall2020_ctrl_mobile

# - Banner 2
# - ?campaign=WMDE_challengebnr_fall2020_var
# - https://www.wikimedia.de/wikipedia-challenge?campaign=WMDE_challengebnr_fall2020_var
# - ?campaign=WMDE_challengebnr_fall2020_var_ipad
# - https://www.wikimedia.de/wikipedia-challenge?campaign=WMDE_challengebnr_fall2020_var_ipad
# - ?campaign=WMDE_challengebnr_fall2020_var_mobile
# - https://www.wikimedia.de/wikipedia-challenge?campaign=WMDE_challengebnr_fall2020_var_mobile

# - target groups
# - Banners will be targeted to non-logged in users only.

# - The a/b test scope is comparing two different claims in the banner text

# - Tracking is dependent on eventlogging. 
# - The required event scheme will be used for closing clicks only. 
# - https://phabricator.wikimedia.org/T250791

### --- daily reporting during campaign
# - The following information should be included in the daily reporting:
# - impressions per banner
# - clicks per banner/ page views per landing page
# - closing rate of banners

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

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

### --- 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 Autumn Banner Campaign 2019
uri_host <- c('de.wikipedia.org', 'de.m.wikipedia.org')
uri_path  <- '/beacon/impression'
uri_query <- c('WMDE_challengebnr_fall2020_ctrl', 'WMDE_challengebnr_fall2020_ctrl_ipad',
               'WMDE_challengebnr_fall2020_ctrl_mobile','WMDE_challengebnr_fall2020_var',
               'WMDE_challengebnr_fall2020_var_ipad','WMDE_challengebnr_fall2020_var_mobile')
queryFile <- 'wikipedia_challenge_Oct2020_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
wmde_process_banner_impressions(fileName = fileName, 
                                dataDir = dataDir, 
                                cetDay = cetDay,
                                campaignName = campaignName)


### ----------------------------------------------------------
### --- Banner Interactions:
### --- via event.WMDEBannerInteractions
### --- https://meta.wikimedia.org/wiki/Schema:WMDEBannerInteractions
### ----------------------------------------------------------

# - select dt, event.bannerName, event.bannerAction, event.bannerImpressions, event.userID 
# - from event.wmdebannerinteractions where year=2020 and month=5 and (day=11 or day=12 or day=13);

# - function: wmde_collect_pageviews
wmde_banner_actions <- function(uri_query_filter,
                                cetDay,
                                queryFile,
                                fileName,
                                analyticsDir,
                                campaignName) {
  
  # - NOTE:
  # - expected format for cetDay is: YYYY-MM-DD
  
  # - 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 eventBannerName_condition
  if (length(uri_query) > 1) {
    eventBannerName_condition <- paste0("(",
                                        paste(
                                          paste0("event.bannerName LIKE '%", uri_query_filter, "%'"),
                                          collapse = " OR ", sep = " "),
                                        ")"
    )
  } else {
    eventBannerName_condition = paste0("event.bannerName LIKE '%", uri_query_filter, "%'")
  }
  
  # - compose HiveQL query
  hiveQuery <- paste0( 
    "select dt, event.bannerName, event.bannerAction, event.bannerImpressions, event.userID from event.wmdebannerinteractions 
    WHERE (",
    eventBannerName_condition, " AND ",
    "(", datetimeCondition, ")",
    ");"
    )
  
  # - write hql
  write(hiveQuery, queryFile)
  # - execute hql script:
  kerberosPrefix <- 
    'sudo -u analytics-privatedata kerberos-run-command analytics-privatedata '
  # - Kerberos init
  system(command = paste0(kerberosPrefix, ' hdfs dfs -ls'), 
         wait = T)
  # - Run query
  query <- system(command = paste(kerberosPrefix, 
                                  '/usr/local/bin/beeline --incremental=true --silent -f "',
                                  paste0(dataDir, queryFile),
                                  '" > ', dataDir, fileName,
                                  sep = ""),
                  wait = TRUE)
  
  # - Wrangle Banner Interactions
  # - load
  bannerData <- tryCatch({
    as.data.frame(fread(paste0(dataDir, fileName)))
  },
  error = function(condition) {
    return(FALSE)
  })
  
  # - process
  if (class(bannerData) == 'logical') {
    return(FALSE) 
  } else { 
    # - bannerSeen
    bannerSeen <- bannerData %>% 
      dplyr::select(bannername, userid)
    bannerSeen <- bannerSeen[!duplicated(bannerSeen), ]
    bannerSeen <- bannerSeen %>% 
      dplyr::select(bannername) %>% 
      dplyr::group_by(bannername) %>% 
      dplyr::summarise(seen_by = n())
    # - bannerClosed
    bannerClosed <- bannerData %>% 
      dplyr::filter(banneraction == "banner-closed") %>% 
      dplyr::select(bannername, bannerimpressions) %>% 
      dplyr::group_by(bannername) %>%
      dplyr::summarise(closed_by = n(), mean_close_imp = round(mean(bannerimpressions), 2))
    # - bannerClicked
    bannerClicked <- bannerData %>% 
      dplyr::filter(banneraction == "banner-clicked") %>% 
      dplyr::select(bannername, bannerimpressions) %>% 
      dplyr::group_by(bannername) %>%
      dplyr::summarise(clicked_by = n(), mean_click_imp = round(mean(bannerimpressions), 2))
    # - whoClicked
    whoClicked <- bannerData %>% 
      dplyr::filter(banneraction == "banner-clicked")
    whoClicked <- data.frame(userid = unique(whoClicked$userid))
    # - store:
    write.csv(whoClicked, 
              paste0(analyticsDir, 
                     "whoClicked_",
                     strsplit(
                       strsplit(fileName, split = "_", fixed = T)[[1]][2],
                       split = ".", 
                       fixed = T)[[1]][1],
                     ".csv"
              )
    )
    # - join
    bannerData <- bannerSeen %>% 
      dplyr::left_join(bannerClosed, 'bannername') %>% 
      dplyr::left_join(bannerClicked, 'bannername')
    bannerData$close_rate <- round(bannerData$closed_by/bannerData$seen_by, 2)
    bannerData$click_rate <- round(bannerData$clicked_by/bannerData$seen_by, 2)
    # - date, campaign
    bannerData$day <- cetDay
    bannerData$campaign <- campaignName
    
    # - store:
    write.csv(bannerData, 
              paste0(analyticsDir, 
                     "bannerInteractionsAggregated_",
                     strsplit(
                       strsplit(fileName, split = "_", fixed = T)[[1]][2],
                       split = ".", 
                       fixed = T)[[1]][1],
                     ".csv"
              )
    )
  }
  
}

# - set params for wmde_banner_actions()
queryFile <- paste0(campaignName, "_bannerInteractions.hql")
fileName <- paste0("bannerInteractions_", cetDay, ".tsv")
uri_query_filter <- 'WMDE_challengebnr_fall2020_'
banner_status <- wmde_banner_actions(uri_query_filter = uri_query_filter,
                                     cetDay = cetDay,
                                     queryFile = queryFile,
                                     fileName = fileName,
                                     analyticsDir = analyticsDir, 
                                     campaignName = campaignName)


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

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

# - set params to wmde_collect_pageviews
uri_host <- c('de.wikipedia.org', 'de.m.wikipedia.org')
uri_path  <- c(
  '/wiki/(274301)_Wikipedia?tour=einfuhrung',
  '/wiki/Spezial:Benutzerkonto_anlegen',
  '/wiki/Spezial:Anmelden',
  '/wiki/Special:MyPage/Artikelwerkstatt',
  '/wiki/Benutzer:Trainingskonto_(WMDE)/Wikipedia_Training',
  '/wiki/Wikipedia:Wikimedia_Deutschland/DeinEngagement/Literatur'
  )
# uri_query <- paste0('WMDE_2020_challenge_', 1:30)
queryFile <- 'wikimedia_challenge_Oct2020_Pageviews.hql'
fileName <- paste0("pageviews_", cetDay, ".tsv")

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

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

# - set params to wmde_process_pageviews
# - for the WMDE 2020_EmailCampaignWikipediaChallenge
uri_query_filter <- 'WMDE_2020_challenge_'
page_filter <- c('de.wikipedia.org/wiki/(274301)_Wikipedia?tour=einfuhrung',
                 'de.wikipedia.org/wiki/Spezial:Benutzerkonto_anlegen',
                 'de.wikipedia.org/wiki/Spezial:Anmelden',
                 'de.wikipedia.org/wiki/Special:MyPage/Artikelwerkstatt',
                 'de.wikipedia.org/wiki/Benutzer:Trainingskonto_(WMDE)/Wikipedia_Training',
                 'de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/DeinEngagement/Literatur')

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

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

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

startTimestamp <- '20201001000000'
# - get user ids
userRegistrations <- fread(paste0(dataDir,
                                  'user_registrations.tsv'))
dim(userRegistrations)
# - clean up from test accounts
userRegistrations <- filter(userRegistrations, 
                            !grepl("^test|^Test", 
                                   userRegistrations$username))
dim(userRegistrations)
head(userRegistrations)
rev_user <- userRegistrations$userid
# - iterate over rev_user
for (i in 1:length(rev_user)) {
  # - SQL query
  sqlQuery <- paste("\"SELECT actor.actor_id, 
                        actor.actor_user, 
                        actor.actor_name, 
                        revision_actor_temp.revactor_timestamp 
                    FROM actor 
                    LEFT JOIN revision_actor_temp ON (actor.actor_id = revision_actor_temp.revactor_actor) 
                    WHERE (revision_actor_temp.revactor_timestamp >= 20201001000000 
                      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'))

0. Campaign Banners

This section presents all data and statistics on the campaign banners. The following chunk loads and then re-structures the banners dataset a bit:

lF <- list.files('_dailyReporting')
lF <- lF[grepl("^bannerInteractions", lF)]
dataSet <- lapply(paste0("_dailyReporting/", lF), fread)
dataSet <- rbindlist(dataSet)
dataSet$V1 <- NULL
dataSet$campaign <- NULL
# - NAs to zero
dataSet[which(is.na(dataSet), arr.ind = T)] <- 0
# - banner codes
dataSet$bannername <- sapply(dataSet$bannername, 
                             function(x) {
                               d <- strsplit(x, split = "_")[[1]]
                               d <- paste(d[1:5], collapse = "_")
                               return(d)
                             })
dataSet$bannername <- gsub("NA", "desktop", dataSet$bannername)
dataSet <- dataSet %>% 
  select(bannername, day, clicked_by, closed_by) %>%
  group_by(bannername, day) %>%
  summarise(clicked_by = sum(clicked_by), 
            closed_by = sum(closed_by))
dataSet$var_ctrl <- sapply(dataSet$bannername, function(x) {
  if (grepl("var", x)) {return("var")} else {return("ctrl")}
})
dataSet$device <- sapply(dataSet$bannername, function(x) {
  if (grepl("mobile", x)) {
    return("mobile")
  } else if (grepl("ipad", x)) {
      return("ipad")
  } else {
      return("desktop")
  }
})

The following dataset presents the banner impressions data:

lF <- list.files('_dailyReporting')
lF <- lF[grepl("^bannerImpressionsAggregated", lF)]
bi_dataSet <- lapply(paste0("_dailyReporting/", lF), fread)
bi_dataSet <- rbindlist(bi_dataSet)
bi_dataSet$V1 <- NULL
bi_dataSet$banner <- gsub("^WMDE_challengebnr_fall2020_ctrl$", 
                          "WMDE_challengebnr_fall2020_ctrl_desktop",
                          bi_dataSet$banner)
bi_dataSet$banner <- gsub("^WMDE_challengebnr_fall2020_var$", 
                          "WMDE_challengebnr_fall2020_var_desktop",
                          bi_dataSet$banner)
bi_dataSet$campaign <- NULL
colnames(bi_dataSet)[2] <- 'seen_by'

Join banner impressions data (bi_dataSet) to banner interactions data (dataSet) to use bi_dataSet$seen_by:

dataSet <- dplyr::left_join(dataSet, 
                            bi_dataSet,
                            by = c("day" = "date",
                                   "bannername" = "banner")
)
rm(bi_dataSet)

1. Campaign Pageviews

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

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

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

1.1 Pageviews Overview

Chart 1.1.1 Daily Pageviews, aggregated across the campaign channels.

pFrame <- dataSet %>% 
  select(date, Page, Pageviews) %>% 
  group_by(date, Page) %>% 
  summarise(Pageviews = sum(Pageviews))
pFrame <- arrange(pFrame, date)
ggplot(pFrame, aes(x = date,
                   y = Pageviews,
                   label = Pageviews,
                    )) + 
  geom_path(size = .5, group = 1) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  scale_y_continuous(labels = comma) +
  facet_wrap(~Page, ncol = 2) + 
  ggtitle('Wikipedia Challenge October 2020') +
  xlab("Date") + ylab("Pageviews") + 
  theme_minimal() + 
  # geom_text_repel(size = 3.5, show.legend = FALSE) + 
  scale_y_continuous(labels = comma) +
  theme(axis.text.x = element_text(angle = 90, size = 10)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "right")

Table 1.1.1 Pageviews totals

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

1.2 Pageviews: Campaign Channels

Chart 1.2.1 Pageviews, by channels

pFrame <- dataSet %>% 
  select(date, Tag, Pageviews) %>% 
  group_by(date, Tag) %>% 
  summarise(Pageviews = sum(Pageviews))
pFrame <- arrange(pFrame, date)
ggplot(pFrame, aes(x = date,
                   y = Pageviews,
                   label = Pageviews,
                    )) + 
  geom_path(size = .5, group = 1) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  scale_y_continuous(labels = comma) +
  facet_wrap(~Tag, ncol = 2) + 
  ggtitle('Wikipedia Challenge October 2020') +
  xlab("Date") + ylab("Pageviews") + 
  theme_minimal() + 
  # geom_text_repel(size = 3.5, show.legend = FALSE) + 
  scale_y_continuous(labels = comma) +
  theme(axis.text.x = element_text(angle = 90, size = 6)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "right")

Chart 1.2.2 Total Pageviews, by channels

pFrame <- pFrame %>% 
  select(Tag, Pageviews) %>% 
  group_by(Tag) %>% 
  summarise(Pageviews = sum(Pageviews)) %>% 
  arrange(desc(Pageviews))
pFrame$Tag <- factor(pFrame$Tag,
                     levels = pFrame$Tag,
                     ordered = T)
ggplot(pFrame, aes(x = Tag,
                   y = Pageviews,
                   label = Pageviews,
                    )) + 
  geom_bar(stat = "identity", color = "blue", fill = "white") + 
  scale_y_continuous(labels = comma) +
  ggtitle('Wikipedia Challenge October 2020') +
  xlab("Date") + ylab("Pageviews") + 
  theme_minimal() + 
  geom_text_repel(size = 3.5, show.legend = FALSE) +
  scale_y_continuous(labels = comma) +
  theme(axis.text.x = element_text(angle = 90, size = 8)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "right")

Table 2.2.1 Total pageviews, by channels and by pages.

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

2. User Registrations

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

2.1 User Registrations Overview

lF <- list.files("_dailyReporting")
lF <- lF[grepl("^user_registrations", lF)]
dataSet <- fread(paste0("_dailyReporting/", lF))
dataSet <- filter(dataSet, 
                  !grepl("^test|^Test", dataSet$username))
dataSet$date <- paste(dataSet$year, 
                      ifelse(nchar(dataSet$month) == 1, paste0("0", dataSet$month), dataSet$month),
                      ifelse(nchar(dataSet$day) == 1, paste0("0", dataSet$day), dataSet$day),
                      sep = "-")
regUsers <- select(dataSet,
                   userid,
                   username, 
                   campaign)
# - total number of registered users
total_registered <- dim(regUsers)[1]
# - expand grid to account for missing observations per day
dateSpan <- seq(from = as.Date("2020-10-01"), 
                to = as.Date("2020-11-11"), 
                by = "day")
dateSpan <- as.character(dateSpan)
dS <- expand.grid(dateSpan, 
                  unique(dataSet$campaign), 
                  stringsAsFactors = F)
colnames(dS) <- c('date', 'campaign')
dS <- dS %>% 
  left_join(select(dataSet, date, campaign, userid),
            by = c("date", "campaign"))
dS$userid <- ifelse(is.na(dS$userid), 0, 1)
pFrame <- dS %>% 
  select(date, userid) %>% 
  group_by(date) %>% 
  summarise(registrations = sum(userid)) %>% 
  arrange(date)
ggplot(pFrame, aes(x = date,
                   y = registrations,
                   label = registrations,
                    )) + 
  geom_path(size = .25, group = 1) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  scale_y_continuous(labels = comma) +
  ggtitle('Wikipedia Challenge October 2020') +
  xlab("Date") + ylab("Registrations") + 
  theme_minimal() + 
  geom_text_repel(size = 3.5, show.legend = FALSE) +
  scale_y_continuous(labels = comma) +
  theme(axis.text.x = element_text(angle = 90, size = 8)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "right")

The total number of users registered in this campaign is 346.

2.2 User Registrations per Campaign Channel

pFrame <- dS %>% 
  group_by(date, campaign) %>% 
  summarise(registrations = sum(userid)) %>% 
  arrange(date)
ggplot(pFrame, aes(x = date,
                   y = registrations,
                   label = registrations,
                    )) + 
  geom_path(size = .25, group = 1) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  facet_wrap(~campaign, ncol = 2) + 
  scale_y_continuous(labels = comma) +
  ggtitle('Wikipedia Challenge October 2020') +
  xlab("Date") + ylab("Registrations") + 
  theme_minimal() + 
  # geom_text_repel(size = 3.5, show.legend = FALSE) +
  scale_y_continuous(labels = comma) +
  theme(axis.text.x = element_text(angle = 90, size = 6)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "right")

2.3 Total User Registrations per Campaign Channel

pFrame <- dS %>% 
  select(campaign, userid) %>% 
  group_by( campaign) %>% 
  summarise(registrations = sum(userid)) %>% 
  arrange(desc(registrations))
pFrame$campaign <- factor(pFrame$campaign, 
                          levels = pFrame$campaign, 
                          ordered = T)
ggplot(pFrame, aes(x = campaign,
                   y = registrations,
                   label = registrations,
                    )) + 
  geom_bar(stat = "identity", color = "blue", fill = "white") + 
  scale_y_continuous(labels = comma) +
  ggtitle('Wikipedia Challenge October 2020') +
  xlab("Date") + ylab("Registrations") + 
  theme_minimal() + 
  geom_text_repel(size = 3.5, show.legend = FALSE) +
  scale_y_continuous(labels = comma) +
  theme(axis.text.x = element_text(angle = 90, size = 8)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "right")

3. User Edits

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

3.1 User Edits Overview

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

lF <- list.files("_dailyReporting")
lF <- lF[grepl("^userEdits", lF)]
editSet <- fread(paste0("_dailyReporting/", lF))
editSet <- select(editSet,
                  actor_name, 
                  revactor_timestamp)                 
colnames(editSet) <- c('username', 'rev_timestamp')
dataSet <- left_join(editSet,
                     select(regUsers, 
                            username,
                            campaign),
                     by = "username")
dataSet <- dataSet[complete.cases(dataSet), ]
dataSet$year <- substr(dataSet$rev_timestamp, 1, 4)
dataSet$month <- substr(dataSet$rev_timestamp, 5, 6)
dataSet$day <- substr(dataSet$rev_timestamp, 7, 8)
dataSet$rev_timestamp <- NULL
dataSet$date <- paste(dataSet$year,
                       dataSet$month,
                       dataSet$day,
                       sep = "-")
dataSet <- select(dataSet,
                  date,
                  username,
                  campaign)
dateSpan <- seq(from = as.Date("2020-10-01"), 
                to = as.Date(max(dataSet$date)), 
                by = "day")
dateSpan <- as.character(dateSpan)
dS <- expand.grid(dateSpan, 
                  unique(dataSet$campaign),
                  stringsAsFactors = F)
colnames(dS) <- c('date', 'campaign')
dS <- dS %>% 
  left_join(select(dataSet, date, campaign, username),
            by = c("date", "campaign"))
dS$username <- ifelse(is.na(dS$username), 0, 1)
pFrame <- dS %>%
  select(date, username) %>% 
  group_by(date) %>% 
  summarise(edits = sum(username)) %>% 
  arrange(date)
ggplot(pFrame, aes(x = date,
                   y = edits,
                   label = edits,
                    )) + 
  geom_path(size = .25, group = 1) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  scale_y_continuous(labels = comma) +
  ggtitle('Wikipedia Challenge October 2020') +
  xlab("Date") + ylab("Edits") + 
  theme_minimal() + 
  geom_text_repel(size = 3.5, show.legend = FALSE) +
  scale_y_continuous(labels = comma) +
  theme(axis.text.x = element_text(angle = 90, size = 8)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "right")

3.2 User Edits by Campaign Channels

pFrame <- dS %>% 
  group_by(date, campaign) %>% 
  summarise(edits = sum(username)) %>% 
  arrange(date)
ggplot(pFrame, aes(x = date,
                   y = edits,
                   label = edits,
                    )) + 
  geom_path(size = .25, group = 1) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  facet_wrap(~campaign, ncol = 2) + 
  scale_y_continuous(labels = comma) +
  ggtitle('Wikipedia Challenge October 2020') +
  xlab("Date") + ylab("Edits") + 
  theme_minimal() + 
  # geom_text_repel(size = 3.5, show.legend = FALSE) +
  scale_y_continuous(labels = comma) +
  theme(axis.text.x = element_text(angle = 90, size = 5.5)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "right")

3.3 Total User Edits by Campaign Channels

pFrame <- dS %>% 
  select(campaign, username) %>% 
  group_by( campaign) %>% 
  summarise(edits = sum(username)) %>% 
  arrange(desc(edits))
pFrame$campaign <- factor(pFrame$campaign, 
                          levels = pFrame$campaign, 
                          ordered = T)
ggplot(pFrame, aes(x = campaign,
                   y = edits,
                   label = edits,
                    )) + 
  geom_bar(stat = "identity", color = "blue", fill = "white") + 
  scale_y_continuous(labels = comma) +
  ggtitle('Wikipedia Challenge October 2020') +
  xlab("Date") + ylab("Edits") + 
  theme_minimal() + 
  geom_text_repel(size = 3.5, show.legend = FALSE) +
  scale_y_continuous(labels = comma) +
  theme(axis.text.x = element_text(angle = 90, size = 8)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "right")

3.4 Edit Classes

3.4.1 Edit Classes: all users

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

3.4.2 Edit Classes per Campaign Channels

edClasses <- lapply(unique(dataSet$campaign), function(x) {
  userClass <- dataSet %>% 
  filter(campaign == x) %>% 
  select(username) %>% 
  group_by(username) %>% 
  summarise(edits = n())
editBoundaries <- list(
  c(0, 1), 
  c(2, 4),
  c(5, 9),
  c(10, 20),
  c(21, 50),
  c(51, 100)
)
userClass$editClass <- sapply(userClass$edits, function(x) {
  wEC <- sapply(editBoundaries, function(y) {
    x >= y[1] & x <= y[2]
  })
  if (sum(wEC) == 0) {
    return("> 100")
  } else {
    return(paste0("(",
                  editBoundaries[[which(wEC)]][1],
                  " - ",
                  editBoundaries[[which(wEC)]][2], 
                  ")"
                  )
    )
  }
})
editClass <- as.data.frame(table(userClass$editClass), 
                           stringsAsFactors = F)
colnames(editClass) <- c('Edit Class', 'Num.Users')
editClass$order <- as.numeric(sapply(editClass$`Edit Class`, function(x) {
  lower <- str_extract(x, '[[:digit:]]+')
}))
editClass <- arrange(editClass, order)
editClass$order <- NULL
editClass$campaign <- x
return(editClass)
})
edClasses <- rbindlist(edClasses)
edClasses$campaign <- sapply(edClasses$campaign, function(x) {
  d <- strsplit(x, "_")[[1]]
  if (nchar(tail(d, 1)) == 1) {
    d[length(d)] <- paste0("0", d[length(d)])
  }
  d <- paste(d, collapse = "_")
  return(d)
  })
edClasses <- arrange(edClasses, campaign)
edClasses$`Edit Class`[edClasses$`Edit Class` == '(0 - 1)'] <- '(1)'
edClasses$`Edit Class` <- factor(edClasses$`Edit Class`, 
                                 levels = c('(1)',
                                            '(2 - 4)', 
                                            '(5 - 9)',
                                            '(10 - 20)',
                                            '(21 - 50)', 
                                            '(51 - 100)'),
                                 ordered = T
                                 )
ggplot(edClasses, 
       aes(x = `Edit Class`, 
           y = `Num.Users`, 
           label = `Num.Users`)) + 
  geom_bar(stat = "identity", color = "black", fill = "white") + 
  facet_wrap(~campaign) + 
  ggtitle('Wikipedia Challenge October 2020') +
  xlab("Edit Class") + ylab("Num.users") + 
  theme_minimal() + 
  geom_text_repel(size = 3.5, show.legend = FALSE) +
  scale_y_continuous(labels = comma) +
  theme(axis.text.x = element_text(angle = 90, size = 8)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "right")

LS0tCnRpdGxlOiAnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAgKFByZWxpbWluYXJ5IFJlcG9ydCknCmF1dGhvcjogIkdvcmFuIFMuIE1pbG92YW5vdmljLCBEYXRhIFNjaWVudGlzdCwgV01ERSIKZGF0ZTogIk9jdG9iZXIgMjAsIDIwMjAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0aGVtZTogc2ltcGxleAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDUKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQotLS0KCioqRmVlZGJhY2sqKiBzaG91bGQgYmUgc2VuZCB0byBgZ29yYW4ubWlsb3Zhbm92aWNfZXh0QHdpa2ltZWRpYS5kZWAuIAoKVGhlIGNhbXBhaWduIHdhcyBydW4gZnJvbSAyMDIwLzEwLzAxIHRvIDIwMjAvMTAvMTEuCgoqKkNVUlJFTlQgVVBEQVRFOioqIERhdGFzZXQgYXMgb2YgMjAyMC8xMC8yNC4KCmBgYHtyLCBlY2hvID0gRiwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEYsIHJlc3VsdHMgPSAnaGlkZSd9CiMgIWRpYWdub3N0aWNzIG9mZgojIyMgLS0tIFNldHVwCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoRFQpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpgYGAKCiMjIERhdGEgQWNxdWlzaXRvbgoKKipOT1RFOioqIHRoZSBEYXRhIEFjcXVpc2l0aW9uIGNvZGUgY2h1bmsgaXMgbm90IGZ1bGx5IHJlcHJvZHVjaWJsZSBmcm9tIHRoaXMgUmVwb3J0LiBUaGUgZGF0YSBhcmUgY29sbGVjdGVkIGJ5IHJ1bm5pbmcgdGhlIHNjcmlwdCBgMjAyMF9XaWtpcGVkaWFDaGFsbGVuZ2VfT2N0b2Jlcl8yMDIwX0NhbXBhaWduX1BST0RVQ1RJT04uUmAgb24gc3RhdDEwMDcuZXFpYWQud21uZXQsIGNvbGxlY3RpbmcgdGhlIGRhdGEgYXMgYC50c3ZgIGFuZCBgLmNzdmAgZmlsZXMsIGNvcHlpbmcgbWFudWFsbHksIGFuZCBwcm9jZXNzaW5nIGxvY2FsbHkuIEEgZGFpbHkgY3JvbnRhYiBqb2Igd2FzIHJ1biBmcm9tIGAyMDIwLzEwLzAxYCB0byBgMjAyMC8xMC8xMWAgdG8gY29sbGVjdCB0aGUgZGF0YSBmb3IgZGFpbHkgcmVwb3J0aW5nLiBVc2VyIHJlZ2lzdHJhdGlvbnMgYXJlIHRoZW4gY29sbGVjdGVkIHVudGlsIE5vdmVtYmVyIDExdGggMjAyMCwgYW5kIHVzZXIgZWRpdHMgYXJlIGNvbGxlY3RlZCB1bnRpbCB0aGUgZW5kIG9mIHRoZSB5ZWFyLiBUaGUgZGF0YSB1c2VkIGluIHRoaXMgcmVwb3J0IGFyZSBhZ2dyZWdhdGVzIG9mIHRoZSBkYWlseSBkYXRhc2V0cywgc2FuaXRpemVkIGFuZCBhbm9ueW1pemVkLiAgIAoKKipEQVRBOioqICgxKSBUaGUgYGV2ZW50LndtZGViYW5uZXJpbnRlcmFjdGlvbnNgIHNjaGVtYSBpcyB1c2VkIGZvciBiYW5uZXIgY2xpY2tzL2Nsb3NpbmcgKHNlZTogW1BoYWJdKGh0dHBzOi8vcGhhYnJpY2F0b3Iud2lraW1lZGlhLm9yZy9UMjYyNTM0IzY1MDE3OTMpKTsgKDIpIHRoZSBgd21mLndlYnJlcXVlc3RgIHRhYmxlIGlzIGEgc291cmNlIG9mIGJhbm5lciBpbXByZXNzaW9ucyBkYXRhOyAoMykgcGFnZXZpZXdzLCB1c2VyIHJlZ2lzdHJhdGlvbnMsIGFuZCB1c2VyIGVkaXRzIGFyZSBjb2xsZWN0ZWQgYnkgZm9sbG93aW5nIHRoZSBzdGFuZGFyZCBwcm9jZWR1cmUuIAoKIyMjIERhaWx5IFVwZGF0ZQoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRn0KCiMjIyAtLS0gV01ERV9XaWtpcGVkaWFfQ2hhbGxlbmdlX09jdG9iZXJfMjAyMF9DYW1wYWlnbi5QUk9EVUNUSU9OLlIKIyMjIC0tLSBodHRwczovL3BoYWJyaWNhdG9yLndpa2ltZWRpYS5vcmcvVDI2MjUzNAoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBDYW1wYWlnbiBEZXNjcmlwdGlvbiBhbmQgUGFyYW1ldGVycwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIC0tLSBUcmFja2luZyBQYXJ0IDE6IGJhbm5lciBjYW1wYWlnbgojIC0gUGxlYXNlIHByb3ZpZGUgZGFpbHkgcmVwb3J0cyBkdXJpbmcgdGhlIGJhbm5lciBjYW1wYWlnbiAoT2N0b2JlciAxc3QgLSAxMXRoKS4KCiMjIyAtLS0gVHJhY2tpbmcgUGFydCAyOiBlbWFpbGluZyBjYW1wYWlnbiBXaWtpcGVkaWEgY2hhbGxlbmdlCiMgLSBQbGVhc2UgcHJvdmlkZSBhIHRyYWNraW5nIHJlcG9ydCBmw7xyIHRoZSBXaWtpcGVkaWEgY2hhbGxlbmdlIGVtYWlsIGNhbXBhaWduIAojIC0gYXMgYWxyZWFkeSBpbXBsZW1lbnRlZCBzZWUgaHR0cHM6Ly9waGFicmljYXRvci53aWtpbWVkaWEub3JnL1QyNTE1MjQgZnJvbSBPY3RvYmVyIHRvIERlY2VtYmVyLgoKIyMjIC0tLVRpbWVsaW5lIGZvciB0aGUgd2hvbGUgcHJvamVjdAojIC0gU3RhcnQgb2YgdGhlIGJhbm5lciBjYW1wYWlnbjogMS4gT2N0b2JlciAyMDIwCiMgLSBFbmQgb2YgdGhlIGJhbm5lciBjYW1wYWlnbjogMTEuIE9jdG9iZXIgMjAyMAojIC0gVHJhY2tpbmcgdGVzdDogRW5kIG9mIFNlcHRlbWJlcgojIC0gTGFzdCBVc2VyIHNpZ25pbmcgdXA6IDExLiBPY3RvYmVyCiMgLSBQcmVsaW1pbmFyeSByZXBvcnQgZm9yIHRyYWNraW5nIHBhcnQgMTogc29tZXRpbWUgdW50aWwgZW5kIG9mIE9jdG9iZXIKIyAtIExhc3QgVXNlciBmaW5pc2hpbmcgbWFpbGluZyBjYW1wYWlnbjogTm92ZW1iZXIgMTF0aAojIC0gVHJhY2sgZWRpdGluZyBiZWhhdmlvciBzaXggd2Vla3MgYWZ0ZXIgZW5kIG9mIGNhbXBhaWduOiBEZWNlbWJlciAyMwojIC0gZmluYWwgcmVwb3J0IGZvciB0cmFja2luZyBwYXJ0IDEgYW5kIDI6IGJlZ2lubmluZyBvZiBKYW51YXJ5CgojIyMgLS0tIEJhbm5lciBDYW1wYWlnbiBpbmZvcm1hdGlvbgojIC0gVGhlcmUgd2lsbCBiZSBvbmUgY2FtcGFpZ24gd2l0aCA2IGRpZmZlcmVudCBiYW5uZXJzIChpbmNsdWRpbmcgbW9iaWxlIHZlcnNpb25zKSAKIyAtIHdoaWNoIHRhcmdldCByZWFkZXJzIGluIHdpa2lwZWRpYS4gQmFubmVyIGNhbXBhaWduIGdvYWwgYXJlIHJlZ2lzdHJhdGlvbnMgCiMgLSBmb3IgdGhlIGVtYWlsaW5nIGNhbXBhaWduLgoKIyAtIEZpbmQgYWxsIGRldGFpbHMgaW4gdGhpcyBkb2N1bWVudDogCiMgLSBodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9kb2N1bWVudC9kLzFrVGxMZHN4OV9yRC1rOFhjcDJ5RnZiOWZ0SER4a01uNi1NSjEtNWlodFM4L2VkaXQKCiMjIyAtLS0gQ2FtcGFpZ24gVGFncyBhbmQgTGFuZGluZ3BhZ2VzCiMgLSBMYW5kaW5nIFBhZ2UgMTogaHR0cHM6Ly93d3cud2lraW1lZGlhLmRlL3dpa2lwZWRpYS1jaGFsbGVuZ2UKCiMgLSBCYW5uZXIgMQojIC0gP2NhbXBhaWduPVdNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX2N0cmwKIyAtIGh0dHBzOi8vd3d3Lndpa2ltZWRpYS5kZS93aWtpcGVkaWEtY2hhbGxlbmdlP2NhbXBhaWduPVdNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX2N0cmwKIyAtID9jYW1wYWlnbj1XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF9jdHJsX2lwYWQKIyAtIGh0dHBzOi8vd3d3Lndpa2ltZWRpYS5kZS93aWtpcGVkaWEtY2hhbGxlbmdlP2NhbXBhaWduPVdNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX2N0cmxfaXBhZAojIC0gP2NhbXBhaWduPVdNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX2N0cmxfbW9iaWxlCiMgLSBodHRwczovL3d3dy53aWtpbWVkaWEuZGUvd2lraXBlZGlhLWNoYWxsZW5nZT9jYW1wYWlnbj1XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF9jdHJsX21vYmlsZQoKIyAtIEJhbm5lciAyCiMgLSA/Y2FtcGFpZ249V01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfdmFyCiMgLSBodHRwczovL3d3dy53aWtpbWVkaWEuZGUvd2lraXBlZGlhLWNoYWxsZW5nZT9jYW1wYWlnbj1XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXIKIyAtID9jYW1wYWlnbj1XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXJfaXBhZAojIC0gaHR0cHM6Ly93d3cud2lraW1lZGlhLmRlL3dpa2lwZWRpYS1jaGFsbGVuZ2U/Y2FtcGFpZ249V01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfdmFyX2lwYWQKIyAtID9jYW1wYWlnbj1XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXJfbW9iaWxlCiMgLSBodHRwczovL3d3dy53aWtpbWVkaWEuZGUvd2lraXBlZGlhLWNoYWxsZW5nZT9jYW1wYWlnbj1XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXJfbW9iaWxlCgojIC0gdGFyZ2V0IGdyb3VwcwojIC0gQmFubmVycyB3aWxsIGJlIHRhcmdldGVkIHRvIG5vbi1sb2dnZWQgaW4gdXNlcnMgb25seS4KCiMgLSBUaGUgYS9iIHRlc3Qgc2NvcGUgaXMgY29tcGFyaW5nIHR3byBkaWZmZXJlbnQgY2xhaW1zIGluIHRoZSBiYW5uZXIgdGV4dAoKIyAtIFRyYWNraW5nIGlzIGRlcGVuZGVudCBvbiBldmVudGxvZ2dpbmcuIAojIC0gVGhlIHJlcXVpcmVkIGV2ZW50IHNjaGVtZSB3aWxsIGJlIHVzZWQgZm9yIGNsb3NpbmcgY2xpY2tzIG9ubHkuIAojIC0gaHR0cHM6Ly9waGFicmljYXRvci53aWtpbWVkaWEub3JnL1QyNTA3OTEKCiMjIyAtLS0gZGFpbHkgcmVwb3J0aW5nIGR1cmluZyBjYW1wYWlnbgojIC0gVGhlIGZvbGxvd2luZyBpbmZvcm1hdGlvbiBzaG91bGQgYmUgaW5jbHVkZWQgaW4gdGhlIGRhaWx5IHJlcG9ydGluZzoKIyAtIGltcHJlc3Npb25zIHBlciBiYW5uZXIKIyAtIGNsaWNrcyBwZXIgYmFubmVyLyBwYWdlIHZpZXdzIHBlciBsYW5kaW5nIHBhZ2UKIyAtIGNsb3NpbmcgcmF0ZSBvZiBiYW5uZXJzCgojIyMgLS0tIGxpYnJhcmllcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKCiMjIyAtLS0gZGlyIHN0cnVjdHVyZQpjYW1wYWlnblBhdGggPC0gJy9ob21lL2dvcmFuc20vQW5hbHl0aWNzL05ld0VkaXRvcnMvQ2FtcGFpZ25zLzIwMjBfV2lraXBlZGlhX0NoYWxsZW5nZV9PY3RvYmVyXzIwMjBfQ2FtcGFpZ24vJwpkYXRhRGlyIDwtIHBhc3RlMChjYW1wYWlnblBhdGgsICJfZGF0YS8iKQphbmFseXRpY3NEaXIgPC0gcGFzdGUwKGNhbXBhaWduUGF0aCwgIl9hbmFseXRpY3MvIikKIyMjIC0tLSBjYW1wYWlnbiBzcGVjaWZpY3MKY2FtcGFpZ25OYW1lIDwtICdXaWtpcGVkaWFfQ2hhbGxlbmdlX09jdG9iZXJfMjAyMCcKCiMjIyAtLS0gZGV0ZXJtaW5lIGNldERheQpjZXREYXkgPC0gU3lzLnRpbWUoKQpjZXREYXkKYXR0cihjZXREYXksICJ0em9uZSIpIDwtICJFdXJvcGUvQmVybGluIgojIC0gb25lIGRheSBiZWhpbmQgZm9yIGNyb250YWIKIyAtIChpLmUuIHdhaXRpbmcgZm9yIHdtZi53ZWJyZXF1ZXN0IHRvIGNvbXBsZXRlIGlzIGRhdGEgYWNxdWlzaXRpb24pCmNldERheSA8LSB5bWQoCiAgc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKGNldERheSksIAogICAgICAgICAgIHNwbGl0ID0gIiAiLCAKICAgICAgICAgICBmaXhlZCA9IFQpW1sxXV1bMV0KKSAtIDEKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gQmFubmVyIEltcHJlc3Npb25zCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgLSBmdW5jdGlvbjogd21kZV9jb2xsZWN0X2Jhbm5lcl9pbXByZXNzaW9ucwp3bWRlX2NvbGxlY3RfYmFubmVyX2ltcHJlc3Npb25zIDwtIGZ1bmN0aW9uKHVyaV9ob3N0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcGF0aCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIpIHsKICAKICAjIC0gTk9URToKICAjIC0gZXhwZWN0ZWQgZm9ybWF0IGZvciBjZXREYXkgaXM6IFlZWVktTU0tREQKICAKICAjIC0gdG8gZGF0YURpcgogIHNldHdkKGRhdGFEaXIpCiAgCiAgIyAtIGxpYnJhcmllcwogIGxpYnJhcnkoc3RyaW5ncikKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgZGF0ZXRpbWVfY29uZGl0aW9uCiAgY2V0X2NvbmRpdGlvbiA8LSBzZXEoCiAgICBmcm9tID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMDowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICB0byA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDIzOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIGJ5ID0gImhvdXIiCiAgKSAKICBhdHRyKGNldF9jb25kaXRpb24sICJ0em9uZSIpIDwtICJVVEMiCiAgY2V0X2NvbmRpdGlvbiA8LSBhcy5jaGFyYWN0ZXIoY2V0X2NvbmRpdGlvbikKICBjZXRfY29uZGl0aW9uIDwtIHVubGlzdChzdHJfZXh0cmFjdF9hbGwoY2V0X2NvbmRpdGlvbiwgIl4oW1s6ZGlnaXQ6XV18XFxzfC0pKiIpKQogIGNldF95ZWFycyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsxXQogICAgfSkKICBjZXRfbW9udGhzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzJdCiAgICB9KQogIGNldF9tb250aHMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X21vbnRocykKICBjZXRfZGF5cyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVszXQogICAgfSkKICBjZXRfZGF5cyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfZGF5cykKICBjZXRfaG91cnMgPC0gc2FwcGx5KHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCAKICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgeFsyXQogICAgICAgICAgICAgICAgICAgICAgfSkKICBjZXRfaG91cnMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2hvdXJzKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlMCgKICAgICJ5ZWFyID0gIiwgY2V0X3llYXJzLCAiIEFORCAiLAogICAgIm1vbnRoID0gIiwgY2V0X21vbnRocywgIiBBTkQgIiwKICAgICJkYXkgPSAiLCBjZXRfZGF5cywgIiBBTkQgIiwgCiAgICAiaG91ciA9ICIsIGNldF9ob3VycwogICkKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZSgiKCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGV0aW1lQ29uZGl0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSB1cmlfcGF0aF9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9wYXRoKSA+IDEpIHsKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX3BhdGhfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfcGF0aCA9ICciLCB1cmlfcGF0aCwgIiciKQogIH0KICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX2hvc3RfY29uZGl0aW9uCiAgaWYgKGxlbmd0aCh1cmlfaG9zdCkgPiAxKSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfaG9zdCA9ICciLCB1cmlfaG9zdCwgIiciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiA9IHBhc3RlMCgidXJpX2hvc3QgPSAnIiwgdXJpX2hvc3QsICInIikKICB9CiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9xdWVyeV9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9xdWVyeSkgPiAxKSB7CiAgICB1cmlfcXVlcnlfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfcXVlcnkgTElLRSAnJSIsIHVyaV9xdWVyeSwgIiUnIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9xdWVyeV9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9xdWVyeSBMSUtFICclIiwgdXJpX3F1ZXJ5LCAiJSciKQogIH0KICAKICAjIC0gY29tcG9zZSBIaXZlUUwgcXVlcnkKICBoaXZlUXVlcnkgPC0gcGFzdGUwKAogICAgIlVTRSB3bWY7CiAgICBTRUxFQ1QgdXJpX3F1ZXJ5IEZST00gd2VicmVxdWVzdAogICAgV0hFUkUgKCIsCiAgICB1cmlfaG9zdF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcGF0aF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcXVlcnlfY29uZGl0aW9uLCAiIEFORCAiLAogICAgIigiLCBkYXRldGltZUNvbmRpdGlvbiwgIikiLAogICAgIik7IgogICAgKQogIAogICMgLSB3cml0ZSBocWwKICB3cml0ZShoaXZlUXVlcnksIHF1ZXJ5RmlsZSkKICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0ICsgS2VyYmVyb3MgQXV0aAogIGhpdmVBcmdzIDwtICdzdWRvIC11IGFuYWx5dGljcy1wcml2YXRlZGF0YSBrZXJiZXJvcy1ydW4tY29tbWFuZCBhbmFseXRpY3MtcHJpdmF0ZWRhdGEgL3Vzci9sb2NhbC9iaW4vYmVlbGluZSAtZicKICBoaXZlSW5wdXQgPC0gcGFzdGUwKHF1ZXJ5RmlsZSwgJyA+ICcsIGZpbGVOYW1lKQogICMgLSBjb21tYW5kOgogIGhpdmVDb21tYW5kIDwtIHBhc3RlKGhpdmVBcmdzLCBoaXZlSW5wdXQpCiAgcmV0dXJuKAogICAgc3lzdGVtKGNvbW1hbmQgPSBoaXZlQ29tbWFuZCwgd2FpdCA9IFRSVUUpCiAgKQp9CgojIC0gc2V0IHBhcmFtcyB0byB3bWRlX2NvbGxlY3RfYmFubmVyX2ltcHJlc3Npb25zCiMgLSBmb3IgdGhlIEF1dHVtbiBCYW5uZXIgQ2FtcGFpZ24gMjAxOQp1cmlfaG9zdCA8LSBjKCdkZS53aWtpcGVkaWEub3JnJywgJ2RlLm0ud2lraXBlZGlhLm9yZycpCnVyaV9wYXRoICA8LSAnL2JlYWNvbi9pbXByZXNzaW9uJwp1cmlfcXVlcnkgPC0gYygnV01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfY3RybCcsICdXTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF9jdHJsX2lwYWQnLAogICAgICAgICAgICAgICAnV01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfY3RybF9tb2JpbGUnLCdXTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXInLAogICAgICAgICAgICAgICAnV01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfdmFyX2lwYWQnLCdXTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXJfbW9iaWxlJykKcXVlcnlGaWxlIDwtICd3aWtpcGVkaWFfY2hhbGxlbmdlX09jdDIwMjBfQmFubmVySW1wcmVzc2lvbnMuaHFsJwpmaWxlTmFtZSA8LSBwYXN0ZTAoZGF0YURpciwgImJhbm5lckltcHJlc3Npb25zXyIsIGNldERheSwgIi50c3YiKQoKIyAtIGNvbGxlY3QgQmFubmVyIEltcHJlc3Npb24gZGF0YQp3bWRlX2NvbGxlY3RfYmFubmVyX2ltcHJlc3Npb25zKHVyaV9ob3N0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9xdWVyeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIpCgojIC0gZnVuY3Rpb246IHdtZGVfcHJvY2Vzc19iYW5uZXJfaW1wcmVzc2lvbnMKd21kZV9wcm9jZXNzX2Jhbm5lcl9pbXByZXNzaW9ucyA8LSBmdW5jdGlvbihmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSkgewogIAogICMgLSB0byBkYXRhRGlyCiAgc2V0d2QoZGF0YURpcikKICAKICAjIC0gbGlicmFyaWVzCiAgbGlicmFyeShzdHJpbmdyKQogIGxpYnJhcnkoZHBseXIpCiAgCiAgIyAtIGxvYWQKICBiYW5uZXJEYXRhIDwtIHJlYWQuZGVsaW0oZmlsZU5hbWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICBjb2xuYW1lcyhiYW5uZXJEYXRhKSA8LSAndXJpX3F1ZXJ5JwogIAogICMgLSBjbGVhbgogIHdTdGFydCA8LSB3aGljaChiYW5uZXJEYXRhJHVyaV9xdWVyeSA9PSAidXJpX3F1ZXJ5IikKICBiYW5uZXJEYXRhIDwtIGJhbm5lckRhdGFbKHdTdGFydCArIDEpOihkaW0oYmFubmVyRGF0YSlbMV0gLSAyKSwgXQogIAogICMgLSBzcGxpdAogIGJhbm5lckRhdGEgPC0gc3Ryc3BsaXQoYmFubmVyRGF0YSwgc3BsaXQgPSAiJiIsIGZpeGVkID0gVCkKICAjIC0gZXh0cmFjdCByZWxldmFudCBmaWVsZHMKICAjIC0gYmFubmVyOgogIGJhbm5lciA8LSBzYXBwbHkoYmFubmVyRGF0YSwgZnVuY3Rpb24oeCkgewogICAgeFt3aGljaChncmVwbCgiXmJhbm5lcj0iLCB4KSldCiAgfSkKICBiYW5uZXIgPC0gZ3N1YigiXmJhbm5lcj0iLCAiIiwgYmFubmVyKQogICMgLSByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZToKICByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSA8LSBzYXBwbHkoYmFubmVyRGF0YSwgZnVuY3Rpb24oeCkgewogICAgeFt3aGljaChncmVwbCgiXnJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlPSIsIHgpKV0KICB9KQogIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlIDwtIGFzLm51bWVyaWMoCiAgICBnc3ViKCJecmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGU9IiwgIiIsIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlKQogICkKICAjIC0gcmVzdWx0OgogIHJlc3VsdCA8LSBzYXBwbHkoYmFubmVyRGF0YSwgZnVuY3Rpb24oeCkgewogICAgeFt3aGljaChncmVwbCgiXnJlc3VsdD0iLCB4KSldCiAgfSkKICByZXN1bHQgPC0gZ3N1YigiXnJlc3VsdD0iLCAiIiwgcmVzdWx0KQogIAogICMgLSBjb21wb3NlIHRhYmxlOgogIGJhbm5lck9ic2VydmF0aW9ucyA8LSBkYXRhLmZyYW1lKGJhbm5lciA9IGJhbm5lciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUgPSByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gcmVzdWx0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICAKICAjIC0gZmlsdGVyIGZvciByZXN1bHQ9c2hvdwogIGJhbm5lck9ic2VydmF0aW9ucyA8LSBkcGx5cjo6ZmlsdGVyKGJhbm5lck9ic2VydmF0aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPT0gInNob3ciKQogIAogICMgLSBjb3JyZWN0aW9uIGZvciByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZQogIGJhbm5lck9ic2VydmF0aW9ucyRyZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSA8LSAKICAgIDEvYmFubmVyT2JzZXJ2YXRpb25zJHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlCiAgCiAgIyAtIGFnZ3JlZ2F0ZToKICBiYW5uZXJPYnNlcnZhdGlvbnMgPC0gYmFubmVyT2JzZXJ2YXRpb25zICU+JSAKICAgIGRwbHlyOjpzZWxlY3QoYmFubmVyLCByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSkgJT4lIAogICAgZHBseXI6Omdyb3VwX2J5KGJhbm5lcikgJT4lIAogICAgZHBseXI6OnN1bW1hcmlzZShpbXByZXNzaW9ucyA9IHN1bShyZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSkpCiAgCiAgIyAtIGFkZCBjZXREYXksIG1lCiAgYmFubmVyT2JzZXJ2YXRpb25zJGRhdGUgPC0gY2V0RGF5CiAgYmFubmVyT2JzZXJ2YXRpb25zJGNhbXBhaWduIDwtIGNhbXBhaWduTmFtZQogIAogICMgLSBzdG9yZToKICB3cml0ZS5jc3YoYmFubmVyT2JzZXJ2YXRpb25zLCAKICAgICAgICAgICAgcGFzdGUwKCJiYW5uZXJJbXByZXNzaW9uc0FnZ3JlZ2F0ZWRfIiwKICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICIuY3N2IgogICAgICAgICAgICApCiAgKQogIAp9CgojIC0gd3JhbmdsZSBCYW5uZXIgSW1wcmVzc2lvbiBkYXRhCndtZGVfcHJvY2Vzc19iYW5uZXJfaW1wcmVzc2lvbnMoZmlsZU5hbWUgPSBmaWxlTmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpciA9IGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUgPSBjYW1wYWlnbk5hbWUpCgoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBCYW5uZXIgSW50ZXJhY3Rpb25zOgojIyMgLS0tIHZpYSBldmVudC5XTURFQmFubmVySW50ZXJhY3Rpb25zCiMjIyAtLS0gaHR0cHM6Ly9tZXRhLndpa2ltZWRpYS5vcmcvd2lraS9TY2hlbWE6V01ERUJhbm5lckludGVyYWN0aW9ucwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIHNlbGVjdCBkdCwgZXZlbnQuYmFubmVyTmFtZSwgZXZlbnQuYmFubmVyQWN0aW9uLCBldmVudC5iYW5uZXJJbXByZXNzaW9ucywgZXZlbnQudXNlcklEIAojIC0gZnJvbSBldmVudC53bWRlYmFubmVyaW50ZXJhY3Rpb25zIHdoZXJlIHllYXI9MjAyMCBhbmQgbW9udGg9NSBhbmQgKGRheT0xMSBvciBkYXk9MTIgb3IgZGF5PTEzKTsKCiMgLSBmdW5jdGlvbjogd21kZV9jb2xsZWN0X3BhZ2V2aWV3cwp3bWRlX2Jhbm5lcl9hY3Rpb25zIDwtIGZ1bmN0aW9uKHVyaV9xdWVyeV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmFseXRpY3NEaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lKSB7CiAgCiAgIyAtIE5PVEU6CiAgIyAtIGV4cGVjdGVkIGZvcm1hdCBmb3IgY2V0RGF5IGlzOiBZWVlZLU1NLURECiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIGRhdGV0aW1lX2NvbmRpdGlvbgogIGNldF9jb25kaXRpb24gPC0gc2VxKAogICAgZnJvbSA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDA6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgdG8gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAyMzowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICBieSA9ICJob3VyIgogICkgCiAgYXR0cihjZXRfY29uZGl0aW9uLCAidHpvbmUiKSA8LSAiVVRDIgogIGNldF9jb25kaXRpb24gPC0gYXMuY2hhcmFjdGVyKGNldF9jb25kaXRpb24pCiAgY2V0X2NvbmRpdGlvbiA8LSB1bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKGNldF9jb25kaXRpb24sICJeKFtbOmRpZ2l0Ol1dfFxcc3wtKSoiKSkKICBjZXRfeWVhcnMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMV0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsyXQogICAgfSkKICBjZXRfbW9udGhzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9tb250aHMpCiAgY2V0X2RheXMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bM10KICAgIH0pCiAgY2V0X2RheXMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2RheXMpCiAgY2V0X2hvdXJzIDwtIHNhcHBseShzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgCiAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHhbMl0KICAgICAgICAgICAgICAgICAgICAgIH0pCiAgY2V0X2hvdXJzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9ob3VycykKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZTAoCiAgICAieWVhciA9ICIsIGNldF95ZWFycywgIiBBTkQgIiwKICAgICJtb250aCA9ICIsIGNldF9tb250aHMsICIgQU5EICIsCiAgICAiZGF5ID0gIiwgY2V0X2RheXMsICIgQU5EICIsIAogICAgImhvdXIgPSAiLCBjZXRfaG91cnMKICApCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUoIigiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZUNvbmRpdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgZXZlbnRCYW5uZXJOYW1lX2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX3F1ZXJ5KSA+IDEpIHsKICAgIGV2ZW50QmFubmVyTmFtZV9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoImV2ZW50LmJhbm5lck5hbWUgTElLRSAnJSIsIHVyaV9xdWVyeV9maWx0ZXIsICIlJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICBldmVudEJhbm5lck5hbWVfY29uZGl0aW9uID0gcGFzdGUwKCJldmVudC5iYW5uZXJOYW1lIExJS0UgJyUiLCB1cmlfcXVlcnlfZmlsdGVyLCAiJSciKQogIH0KICAKICAjIC0gY29tcG9zZSBIaXZlUUwgcXVlcnkKICBoaXZlUXVlcnkgPC0gcGFzdGUwKCAKICAgICJzZWxlY3QgZHQsIGV2ZW50LmJhbm5lck5hbWUsIGV2ZW50LmJhbm5lckFjdGlvbiwgZXZlbnQuYmFubmVySW1wcmVzc2lvbnMsIGV2ZW50LnVzZXJJRCBmcm9tIGV2ZW50LndtZGViYW5uZXJpbnRlcmFjdGlvbnMgCiAgICBXSEVSRSAoIiwKICAgIGV2ZW50QmFubmVyTmFtZV9jb25kaXRpb24sICIgQU5EICIsCiAgICAiKCIsIGRhdGV0aW1lQ29uZGl0aW9uLCAiKSIsCiAgICAiKTsiCiAgICApCiAgCiAgIyAtIHdyaXRlIGhxbAogIHdyaXRlKGhpdmVRdWVyeSwgcXVlcnlGaWxlKQogICMgLSBleGVjdXRlIGhxbCBzY3JpcHQ6CiAga2VyYmVyb3NQcmVmaXggPC0gCiAgICAnc3VkbyAtdSBhbmFseXRpY3MtcHJpdmF0ZWRhdGEga2VyYmVyb3MtcnVuLWNvbW1hbmQgYW5hbHl0aWNzLXByaXZhdGVkYXRhICcKICAjIC0gS2VyYmVyb3MgaW5pdAogIHN5c3RlbShjb21tYW5kID0gcGFzdGUwKGtlcmJlcm9zUHJlZml4LCAnIGhkZnMgZGZzIC1scycpLCAKICAgICAgICAgd2FpdCA9IFQpCiAgIyAtIFJ1biBxdWVyeQogIHF1ZXJ5IDwtIHN5c3RlbShjb21tYW5kID0gcGFzdGUoa2VyYmVyb3NQcmVmaXgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJy91c3IvbG9jYWwvYmluL2JlZWxpbmUgLS1pbmNyZW1lbnRhbD10cnVlIC0tc2lsZW50IC1mICInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKGRhdGFEaXIsIHF1ZXJ5RmlsZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnIiA+ICcsIGRhdGFEaXIsIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpLAogICAgICAgICAgICAgICAgICB3YWl0ID0gVFJVRSkKICAKICAjIC0gV3JhbmdsZSBCYW5uZXIgSW50ZXJhY3Rpb25zCiAgIyAtIGxvYWQKICBiYW5uZXJEYXRhIDwtIHRyeUNhdGNoKHsKICAgIGFzLmRhdGEuZnJhbWUoZnJlYWQocGFzdGUwKGRhdGFEaXIsIGZpbGVOYW1lKSkpCiAgfSwKICBlcnJvciA9IGZ1bmN0aW9uKGNvbmRpdGlvbikgewogICAgcmV0dXJuKEZBTFNFKQogIH0pCiAgCiAgIyAtIHByb2Nlc3MKICBpZiAoY2xhc3MoYmFubmVyRGF0YSkgPT0gJ2xvZ2ljYWwnKSB7CiAgICByZXR1cm4oRkFMU0UpIAogIH0gZWxzZSB7IAogICAgIyAtIGJhbm5lclNlZW4KICAgIGJhbm5lclNlZW4gPC0gYmFubmVyRGF0YSAlPiUgCiAgICAgIGRwbHlyOjpzZWxlY3QoYmFubmVybmFtZSwgdXNlcmlkKQogICAgYmFubmVyU2VlbiA8LSBiYW5uZXJTZWVuWyFkdXBsaWNhdGVkKGJhbm5lclNlZW4pLCBdCiAgICBiYW5uZXJTZWVuIDwtIGJhbm5lclNlZW4gJT4lIAogICAgICBkcGx5cjo6c2VsZWN0KGJhbm5lcm5hbWUpICU+JSAKICAgICAgZHBseXI6Omdyb3VwX2J5KGJhbm5lcm5hbWUpICU+JSAKICAgICAgZHBseXI6OnN1bW1hcmlzZShzZWVuX2J5ID0gbigpKQogICAgIyAtIGJhbm5lckNsb3NlZAogICAgYmFubmVyQ2xvc2VkIDwtIGJhbm5lckRhdGEgJT4lIAogICAgICBkcGx5cjo6ZmlsdGVyKGJhbm5lcmFjdGlvbiA9PSAiYmFubmVyLWNsb3NlZCIpICU+JSAKICAgICAgZHBseXI6OnNlbGVjdChiYW5uZXJuYW1lLCBiYW5uZXJpbXByZXNzaW9ucykgJT4lIAogICAgICBkcGx5cjo6Z3JvdXBfYnkoYmFubmVybmFtZSkgJT4lCiAgICAgIGRwbHlyOjpzdW1tYXJpc2UoY2xvc2VkX2J5ID0gbigpLCBtZWFuX2Nsb3NlX2ltcCA9IHJvdW5kKG1lYW4oYmFubmVyaW1wcmVzc2lvbnMpLCAyKSkKICAgICMgLSBiYW5uZXJDbGlja2VkCiAgICBiYW5uZXJDbGlja2VkIDwtIGJhbm5lckRhdGEgJT4lIAogICAgICBkcGx5cjo6ZmlsdGVyKGJhbm5lcmFjdGlvbiA9PSAiYmFubmVyLWNsaWNrZWQiKSAlPiUgCiAgICAgIGRwbHlyOjpzZWxlY3QoYmFubmVybmFtZSwgYmFubmVyaW1wcmVzc2lvbnMpICU+JSAKICAgICAgZHBseXI6Omdyb3VwX2J5KGJhbm5lcm5hbWUpICU+JQogICAgICBkcGx5cjo6c3VtbWFyaXNlKGNsaWNrZWRfYnkgPSBuKCksIG1lYW5fY2xpY2tfaW1wID0gcm91bmQobWVhbihiYW5uZXJpbXByZXNzaW9ucyksIDIpKQogICAgIyAtIHdob0NsaWNrZWQKICAgIHdob0NsaWNrZWQgPC0gYmFubmVyRGF0YSAlPiUgCiAgICAgIGRwbHlyOjpmaWx0ZXIoYmFubmVyYWN0aW9uID09ICJiYW5uZXItY2xpY2tlZCIpCiAgICB3aG9DbGlja2VkIDwtIGRhdGEuZnJhbWUodXNlcmlkID0gdW5pcXVlKHdob0NsaWNrZWQkdXNlcmlkKSkKICAgICMgLSBzdG9yZToKICAgIHdyaXRlLmNzdih3aG9DbGlja2VkLCAKICAgICAgICAgICAgICBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAKICAgICAgICAgICAgICAgICAgICAgIndob0NsaWNrZWRfIiwKICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoCiAgICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoZmlsZU5hbWUsIHNwbGl0ID0gIl8iLCBmaXhlZCA9IFQpW1sxXV1bMl0sCiAgICAgICAgICAgICAgICAgICAgICAgc3BsaXQgPSAiLiIsIAogICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXSwKICAgICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICAgKQogICAgKQogICAgIyAtIGpvaW4KICAgIGJhbm5lckRhdGEgPC0gYmFubmVyU2VlbiAlPiUgCiAgICAgIGRwbHlyOjpsZWZ0X2pvaW4oYmFubmVyQ2xvc2VkLCAnYmFubmVybmFtZScpICU+JSAKICAgICAgZHBseXI6OmxlZnRfam9pbihiYW5uZXJDbGlja2VkLCAnYmFubmVybmFtZScpCiAgICBiYW5uZXJEYXRhJGNsb3NlX3JhdGUgPC0gcm91bmQoYmFubmVyRGF0YSRjbG9zZWRfYnkvYmFubmVyRGF0YSRzZWVuX2J5LCAyKQogICAgYmFubmVyRGF0YSRjbGlja19yYXRlIDwtIHJvdW5kKGJhbm5lckRhdGEkY2xpY2tlZF9ieS9iYW5uZXJEYXRhJHNlZW5fYnksIDIpCiAgICAjIC0gZGF0ZSwgY2FtcGFpZ24KICAgIGJhbm5lckRhdGEkZGF5IDwtIGNldERheQogICAgYmFubmVyRGF0YSRjYW1wYWlnbiA8LSBjYW1wYWlnbk5hbWUKICAgIAogICAgIyAtIHN0b3JlOgogICAgd3JpdGUuY3N2KGJhbm5lckRhdGEsIAogICAgICAgICAgICAgIHBhc3RlMChhbmFseXRpY3NEaXIsIAogICAgICAgICAgICAgICAgICAgICAiYmFubmVySW50ZXJhY3Rpb25zQWdncmVnYXRlZF8iLAogICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdCgKICAgICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdChmaWxlTmFtZSwgc3BsaXQgPSAiXyIsIGZpeGVkID0gVClbWzFdXVsyXSwKICAgICAgICAgICAgICAgICAgICAgICBzcGxpdCA9ICIuIiwgCiAgICAgICAgICAgICAgICAgICAgICAgZml4ZWQgPSBUKVtbMV1dWzFdLAogICAgICAgICAgICAgICAgICAgICAiLmNzdiIKICAgICAgICAgICAgICApCiAgICApCiAgfQogIAp9CgojIC0gc2V0IHBhcmFtcyBmb3Igd21kZV9iYW5uZXJfYWN0aW9ucygpCnF1ZXJ5RmlsZSA8LSBwYXN0ZTAoY2FtcGFpZ25OYW1lLCAiX2Jhbm5lckludGVyYWN0aW9ucy5ocWwiKQpmaWxlTmFtZSA8LSBwYXN0ZTAoImJhbm5lckludGVyYWN0aW9uc18iLCBjZXREYXksICIudHN2IikKdXJpX3F1ZXJ5X2ZpbHRlciA8LSAnV01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfJwpiYW5uZXJfc3RhdHVzIDwtIHdtZGVfYmFubmVyX2FjdGlvbnModXJpX3F1ZXJ5X2ZpbHRlciA9IHVyaV9xdWVyeV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXkgPSBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUgPSBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSA9IGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5hbHl0aWNzRGlyID0gYW5hbHl0aWNzRGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSA9IGNhbXBhaWduTmFtZSkKCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIFBhZ2V2aWV3cwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIGZ1bmN0aW9uOiB3bWRlX2NvbGxlY3RfcGFnZXZpZXdzCndtZGVfY29sbGVjdF9wYWdldmlld3MgPC0gZnVuY3Rpb24odXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyKSB7CiAgCiAgIyAtIE5PVEU6CiAgIyAtIGV4cGVjdGVkIGZvcm1hdCBmb3IgY2V0RGF5IGlzOiBZWVlZLU1NLURECiAgCiAgIyAtIHRvIGRhdGFEaXIKICBzZXR3ZChkYXRhRGlyKQogIAogICMgLSBsaWJyYXJpZXMKICBsaWJyYXJ5KHN0cmluZ3IpCiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIGRhdGV0aW1lX2NvbmRpdGlvbgogIGNldF9jb25kaXRpb24gPC0gc2VxKAogICAgZnJvbSA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDA6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgdG8gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAyMzowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICBieSA9ICJob3VyIgogICkgCiAgYXR0cihjZXRfY29uZGl0aW9uLCAidHpvbmUiKSA8LSAiVVRDIgogIGNldF9jb25kaXRpb24gPC0gYXMuY2hhcmFjdGVyKGNldF9jb25kaXRpb24pCiAgY2V0X2NvbmRpdGlvbiA8LSB1bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKGNldF9jb25kaXRpb24sICJeKFtbOmRpZ2l0Ol1dfFxcc3wtKSoiKSkKICBjZXRfeWVhcnMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMV0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsyXQogICAgfSkKICBjZXRfbW9udGhzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9tb250aHMpCiAgY2V0X2RheXMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bM10KICAgIH0pCiAgY2V0X2RheXMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2RheXMpCiAgY2V0X2hvdXJzIDwtIHNhcHBseShzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgCiAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHhbMl0KICAgICAgICAgICAgICAgICAgICAgIH0pCiAgY2V0X2hvdXJzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9ob3VycykKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZTAoCiAgICAieWVhciA9ICIsIGNldF95ZWFycywgIiBBTkQgIiwKICAgICJtb250aCA9ICIsIGNldF9tb250aHMsICIgQU5EICIsCiAgICAiZGF5ID0gIiwgY2V0X2RheXMsICIgQU5EICIsIAogICAgImhvdXIgPSAiLCBjZXRfaG91cnMKICApCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUoIigiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZUNvbmRpdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX2hvc3RfY29uZGl0aW9uCiAgaWYgKGxlbmd0aCh1cmlfaG9zdCkgPiAxKSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9ob3N0ID0gJyIsIHVyaV9ob3N0LCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9ob3N0ID0gJyIsIHVyaV9ob3N0LCAiJyIpCiAgfQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSB1cmlfcGF0aF9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9wYXRoKSA+IDEpIHsKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX3BhdGhfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfcGF0aCA9ICciLCB1cmlfcGF0aCwgIiciKQogIH0KICAKICAjIC0gY29tcG9zZSBIaXZlUUwgcXVlcnkKICBoaXZlUXVlcnkgPC0gcGFzdGUwKCAKICAgICJVU0Ugd21mOwogICAgU0VMRUNUIHVyaV9ob3N0LCB1cmlfcGF0aCwgdXJpX3F1ZXJ5LCByZWZlcmVyIEZST00gd2VicmVxdWVzdAogICAgV0hFUkUgKCIsCiAgICB1cmlfaG9zdF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcGF0aF9jb25kaXRpb24sICIgQU5EICIsCiAgICAiKCIsIGRhdGV0aW1lQ29uZGl0aW9uLCAiKSIsCiAgICAiKTsiCiAgICApCiAgCiAgIyAtIHdyaXRlIGhxbAogIHdyaXRlKGhpdmVRdWVyeSwgcXVlcnlGaWxlKQogICMgLSBleGVjdXRlIGhxbCBzY3JpcHQ6CiAga2VyYmVyb3NQcmVmaXggPC0gCiAgICAnc3VkbyAtdSBhbmFseXRpY3MtcHJpdmF0ZWRhdGEga2VyYmVyb3MtcnVuLWNvbW1hbmQgYW5hbHl0aWNzLXByaXZhdGVkYXRhICcKICAjIC0gS2VyYmVyb3MgaW5pdAogIHN5c3RlbShjb21tYW5kID0gcGFzdGUwKGtlcmJlcm9zUHJlZml4LCAnIGhkZnMgZGZzIC1scycpLCAKICAgICAgICAgd2FpdCA9IFQpCiAgIyAtIFJ1biBxdWVyeQogIHF1ZXJ5IDwtIHN5c3RlbShjb21tYW5kID0gcGFzdGUoa2VyYmVyb3NQcmVmaXgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJy91c3IvbG9jYWwvYmluL2JlZWxpbmUgLS1pbmNyZW1lbnRhbD10cnVlIC0tc2lsZW50IC1mICInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKGRhdGFEaXIsIHF1ZXJ5RmlsZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnIiA+ICcsIGRhdGFEaXIsIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpLAogICAgICAgICAgICAgICAgICB3YWl0ID0gVFJVRSkKfQoKIyAtIHNldCBwYXJhbXMgdG8gd21kZV9jb2xsZWN0X3BhZ2V2aWV3cwp1cmlfaG9zdCA8LSBjKCdkZS53aWtpcGVkaWEub3JnJywgJ2RlLm0ud2lraXBlZGlhLm9yZycpCnVyaV9wYXRoICA8LSBjKAogICcvd2lraS8oMjc0MzAxKV9XaWtpcGVkaWE/dG91cj1laW5mdWhydW5nJywKICAnL3dpa2kvU3BlemlhbDpCZW51dHplcmtvbnRvX2FubGVnZW4nLAogICcvd2lraS9TcGV6aWFsOkFubWVsZGVuJywKICAnL3dpa2kvU3BlY2lhbDpNeVBhZ2UvQXJ0aWtlbHdlcmtzdGF0dCcsCiAgJy93aWtpL0JlbnV0emVyOlRyYWluaW5nc2tvbnRvXyhXTURFKS9XaWtpcGVkaWFfVHJhaW5pbmcnLAogICcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0RlaW5FbmdhZ2VtZW50L0xpdGVyYXR1cicKICApCiMgdXJpX3F1ZXJ5IDwtIHBhc3RlMCgnV01ERV8yMDIwX2NoYWxsZW5nZV8nLCAxOjMwKQpxdWVyeUZpbGUgPC0gJ3dpa2ltZWRpYV9jaGFsbGVuZ2VfT2N0MjAyMF9QYWdldmlld3MuaHFsJwpmaWxlTmFtZSA8LSBwYXN0ZTAoInBhZ2V2aWV3c18iLCBjZXREYXksICIudHN2IikKCiMgLSBjb2xsZWN0IFBhZ2V2aWV3cyBkYXRhCndtZGVfY29sbGVjdF9wYWdldmlld3ModXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyKQoKIyMjIC0tLSBXcmFuZ2xlIFBhZ2V2aWV3cwojIC0gZnVuY3Rpb246IHdtZGVfcHJvY2Vzc19wYWdldmlld3MKd21kZV9wcm9jZXNzX3BhZ2V2aWV3cyA8LSBmdW5jdGlvbihmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcXVlcnlfZmlsdGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhZ2VfZmlsdGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUgPSBjYW1wYWlnbk5hbWUpIHsKICAKICAjIC0gdG8gZGF0YURpcgogIHNldHdkKGRhdGFEaXIpCiAgCiAgIyAtIGxpYnJhcmllcwogIGxpYnJhcnkoc3RyaW5ncikKICBsaWJyYXJ5KGRwbHlyKQogIGxpYnJhcnkodGlkeXIpCiAgbGlicmFyeShkYXRhLnRhYmxlKQogIAogICMgLSBsb2FkCiAgcGFnZXZpZXdzRGF0YSA8LSByZWFkTGluZXMoZmlsZU5hbWUpCiAgd1N0YXJ0IDwtIHdoaWNoKGdyZXBsKCJ1cmlfaG9zdCIsIHBhZ2V2aWV3c0RhdGEpKQogIHBhZ2V2aWV3c0RhdGEgPC0gcGFnZXZpZXdzRGF0YVsod1N0YXJ0ICsgMik6KGxlbmd0aChwYWdldmlld3NEYXRhKSAtIDIpXQogIHBhZ2V2aWV3c0RhdGEgPC0gZGF0YS5mcmFtZShkYXQgPSBwYWdldmlld3NEYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgcGFnZXZpZXdzRGF0YSA8LSBzZXBhcmF0ZShwYWdldmlld3NEYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50byA9IGMoJ3VyaV9ob3N0JywgJ3VyaV9wYXRoJywgJ3VyaV9xdWVyeScsICdyZWZlcmVyJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiKQogICMgLSBhcHBseSBwYWdlX2ZpbHRlcgogIHBhZ2V2aWV3c0RhdGEkcGFnZSA8LSBwYXN0ZTAocGFnZXZpZXdzRGF0YSR1cmlfaG9zdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWdldmlld3NEYXRhJHVyaV9wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkpCiAgd0ZpbHRlciA8LSBzYXBwbHkocGFnZXZpZXdzRGF0YSRwYWdlLCBmdW5jdGlvbih4KSB7CiAgICBzYXBwbHkocGFnZV9maWx0ZXIsIGZ1bmN0aW9uKHkpIHsKICAgICAgZ3JlcGwoeSwgeCkKICAgIH0pCiAgfSkKICB3RmlsdGVyIDwtIGNvbFN1bXMod0ZpbHRlcikKICB3RmlsdGVyIDwtIHdoaWNoKHdGaWx0ZXIgPiAwKQogIHBhZ2V2aWV3c0RhdGEgPC0gcGFnZXZpZXdzRGF0YVt3RmlsdGVyLCBdCiAgIyAtIGFwcGx5IHVyaV9xdWVyeV9maWx0ZXIKICAjIC0gTk9URTogbG9va2luZyBpbiBib3RoOiB1cmlfcXVlcnksIHJlZmVyZXIgCiAgd191cmlfcXVlcnkgPC0gd2hpY2goZ3JlcGwodXJpX3F1ZXJ5X2ZpbHRlciwgcGFnZXZpZXdzRGF0YSRwYWdlKSkKICAKICBpZiAobGVuZ3RoKHdfdXJpX3F1ZXJ5KSA+IDApIHsKICAgIAogICAgIyAtIGZpbHRlciBmb3Igd191cmlfcXVlcnkKICAgIHBhZ2V2aWV3c0RhdGEgPC0gcGFnZXZpZXdzRGF0YVt3X3VyaV9xdWVyeSwgXSAKICAgIAogICAgIyAtIGFnZ3JlZ2F0ZToKICAgIHBhZ2V2aWV3c0RhdGEkdXJpX3BhdGggPC0gcGFzdGUwKHBhZ2V2aWV3c0RhdGEkdXJpX2hvc3QsIHBhZ2V2aWV3c0RhdGEkdXJpX3BhdGgpCiAgICBwYWdldmlld3NEYXRhJHVyaV9ob3N0IDwtIE5VTEwKICAgIHBhZ2V2aWV3c0RhdGEkcGFnZSA8LSBOVUxMCiAgICBwYWdldmlld3NEYXRhJHJlZmVyZXIgPC0gTlVMTAogICAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhICU+JSAKICAgICAgZHBseXI6OnNlbGVjdCh1cmlfcXVlcnksIHVyaV9wYXRoKSAlPiUgCiAgICAgIGRwbHlyOjpncm91cF9ieSh1cmlfcXVlcnksIHVyaV9wYXRoKSAlPiUgCiAgICAgIGRwbHlyOjpzdW1tYXJpc2UocGFnZXZpZXdzID0gbigpKQogICAgY29sbmFtZXMocGFnZXZpZXdzRGF0YSkgPC0gYygnVGFnJywgJ1BhZ2UnLCAnUGFnZXZpZXdzJykKICAgIAogICAgIyAtIGFkZCBjZXREYXksIGNhbXBhaWduTmFtZQogICAgcGFnZXZpZXdzRGF0YSRkYXRlIDwtIGNldERheQogICAgcGFnZXZpZXdzRGF0YSRjYW1wYWlnbiA8LSBjYW1wYWlnbk5hbWUKICAgIAogICAgIyAtIHN0b3JlOgogICAgd3JpdGUuY3N2KHBhZ2V2aWV3c0RhdGEsIAogICAgICAgICAgICAgIHBhc3RlMChhbmFseXRpY3NEaXIsIAogICAgICAgICAgICAgICAgICAgICAicGFnZXZpZXdzQWdncmVnYXRlZF8iLAogICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdCgKICAgICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdChmaWxlTmFtZSwgc3BsaXQgPSAiXyIsIGZpeGVkID0gVClbWzFdXVsyXSwKICAgICAgICAgICAgICAgICAgICAgICBzcGxpdCA9ICIuIiwgCiAgICAgICAgICAgICAgICAgICAgICAgZml4ZWQgPSBUKVtbMV1dWzFdLAogICAgICAgICAgICAgICAgICAgICAiLmNzdiIKICAgICAgICAgICAgICApCiAgICApCiAgICAKICB9CiAgCn0KCiMgLSBzZXQgcGFyYW1zIHRvIHdtZGVfcHJvY2Vzc19wYWdldmlld3MKIyAtIGZvciB0aGUgV01ERSAyMDIwX0VtYWlsQ2FtcGFpZ25XaWtpcGVkaWFDaGFsbGVuZ2UKdXJpX3F1ZXJ5X2ZpbHRlciA8LSAnV01ERV8yMDIwX2NoYWxsZW5nZV8nCnBhZ2VfZmlsdGVyIDwtIGMoJ2RlLndpa2lwZWRpYS5vcmcvd2lraS8oMjc0MzAxKV9XaWtpcGVkaWE/dG91cj1laW5mdWhydW5nJywKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL1NwZXppYWw6QmVudXR6ZXJrb250b19hbmxlZ2VuJywKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL1NwZXppYWw6QW5tZWxkZW4nLAogICAgICAgICAgICAgICAgICdkZS53aWtpcGVkaWEub3JnL3dpa2kvU3BlY2lhbDpNeVBhZ2UvQXJ0aWtlbHdlcmtzdGF0dCcsCiAgICAgICAgICAgICAgICAgJ2RlLndpa2lwZWRpYS5vcmcvd2lraS9CZW51dHplcjpUcmFpbmluZ3Nrb250b18oV01ERSkvV2lraXBlZGlhX1RyYWluaW5nJywKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL1dpa2lwZWRpYTpXaWtpbWVkaWFfRGV1dHNjaGxhbmQvRGVpbkVuZ2FnZW1lbnQvTGl0ZXJhdHVyJykKCiMgLSB3cmFuZ2xlIHBhZ2V2aWV3cwp3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzKGZpbGVOYW1lID0gZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpciA9IGRhdGFEaXIsCiAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5X2ZpbHRlciA9IHVyaV9xdWVyeV9maWx0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgIHBhZ2VfZmlsdGVyID0gcGFnZV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5ID0gY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSA9IGNhbXBhaWduTmFtZSkKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gVXNlciBSZWdpc3RyYXRpb25zCiMjIyAtLS0gSGl2ZVFMIHF1ZXJ5OiBldmVudC5TZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uIHRhYmxlCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmhpdmVRTCA8LQogICJTRUxFQ1QgeWVhciwgbW9udGgsIGRheSwgaG91ciwgIGV2ZW50LmNhbXBhaWduLCBldmVudC51c2VySWQsIGV2ZW50LnVzZXJOYW1lLCB3ZWJob3N0LCB3aWtpCiAgICAgIEZST00gZXZlbnQuc2VydmVyc2lkZWFjY291bnRjcmVhdGlvbgogICAgICBXSEVSRQogICAgICAgIHllYXIgPSAyMDIwCiAgICAgICAgQU5EIChtb250aCA9IDEwIE9SIG1vbnRoID0gMTEpCiAgICAgICAgQU5EIChldmVudC5jYW1wYWlnbiBMSUtFICclV01ERV8yMDIwX2NoYWxsZW5nZV8lJyk7IgojIC0gd3JpdGUgaHFsCndyaXRlKGhpdmVRTCwgcGFzdGUwKGRhdGFEaXIsICd1c2VyX3JlZ2lzdHJhdGlvbnMuaHFsJykpCiMjIyAtLS0gb3V0cHV0IGZpbGVuYW1lCmZpbGVuYW1lIDwtIHBhc3RlMChkYXRhRGlyLCAndXNlcl9yZWdpc3RyYXRpb25zLnRzdicpCiMjIyAtLS0gZXhlY3V0ZSBocWwgc2NyaXB0OgpoaXZlQXJncyA8LSAnc3VkbyAtdSBhbmFseXRpY3MtcHJpdmF0ZWRhdGEga2VyYmVyb3MtcnVuLWNvbW1hbmQgYW5hbHl0aWNzLXByaXZhdGVkYXRhIC91c3IvbG9jYWwvYmluL2JlZWxpbmUgLS1zaWxlbnQgLS1pbmNyZW1lbnRhbD10cnVlIC0tdmVyYm9zZT1mYWxzZSAtZicKaGl2ZUlucHV0IDwtIHBhc3RlKHBhc3RlMChkYXRhRGlyLCAndXNlcl9yZWdpc3RyYXRpb25zLmhxbCcpLAogICAgICAgICAgICAgICAgICAgIiA+ICIsCiAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSwKICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQojIC0gY29tbWFuZDoKaGl2ZUNvbW1hbmQgPC0gcGFzdGUoaGl2ZUFyZ3MsIGhpdmVJbnB1dCkKc3lzdGVtKGNvbW1hbmQgPSBoaXZlQ29tbWFuZCwgd2FpdCA9IFRSVUUpCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIFVzZXIgRWRpdHMKIyMjIC0tLSBVc2VyIEVkaXRzIHZpYSByZXZpc2lvbl9hY3Rvcl90ZW1wCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpzdGFydFRpbWVzdGFtcCA8LSAnMjAyMDEwMDEwMDAwMDAnCiMgLSBnZXQgdXNlciBpZHMKdXNlclJlZ2lzdHJhdGlvbnMgPC0gZnJlYWQocGFzdGUwKGRhdGFEaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAndXNlcl9yZWdpc3RyYXRpb25zLnRzdicpKQpkaW0odXNlclJlZ2lzdHJhdGlvbnMpCiMgLSBjbGVhbiB1cCBmcm9tIHRlc3QgYWNjb3VudHMKdXNlclJlZ2lzdHJhdGlvbnMgPC0gZmlsdGVyKHVzZXJSZWdpc3RyYXRpb25zLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICFncmVwbCgiXnRlc3R8XlRlc3QiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2VyUmVnaXN0cmF0aW9ucyR1c2VybmFtZSkpCmRpbSh1c2VyUmVnaXN0cmF0aW9ucykKaGVhZCh1c2VyUmVnaXN0cmF0aW9ucykKcmV2X3VzZXIgPC0gdXNlclJlZ2lzdHJhdGlvbnMkdXNlcmlkCiMgLSBpdGVyYXRlIG92ZXIgcmV2X3VzZXIKZm9yIChpIGluIDE6bGVuZ3RoKHJldl91c2VyKSkgewogICMgLSBTUUwgcXVlcnkKICBzcWxRdWVyeSA8LSBwYXN0ZSgiXCJTRUxFQ1QgYWN0b3IuYWN0b3JfaWQsIAogICAgICAgICAgICAgICAgICAgICAgICBhY3Rvci5hY3Rvcl91c2VyLCAKICAgICAgICAgICAgICAgICAgICAgICAgYWN0b3IuYWN0b3JfbmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHJldmlzaW9uX2FjdG9yX3RlbXAucmV2YWN0b3JfdGltZXN0YW1wIAogICAgICAgICAgICAgICAgICAgIEZST00gYWN0b3IgCiAgICAgICAgICAgICAgICAgICAgTEVGVCBKT0lOIHJldmlzaW9uX2FjdG9yX3RlbXAgT04gKGFjdG9yLmFjdG9yX2lkID0gcmV2aXNpb25fYWN0b3JfdGVtcC5yZXZhY3Rvcl9hY3RvcikgCiAgICAgICAgICAgICAgICAgICAgV0hFUkUgKHJldmlzaW9uX2FjdG9yX3RlbXAucmV2YWN0b3JfdGltZXN0YW1wID49IDIwMjAxMDAxMDAwMDAwIAogICAgICAgICAgICAgICAgICAgICAgQU5EIGFjdG9yLmFjdG9yX3VzZXIgPSAiLCByZXZfdXNlcltpXSwgIik7XCIiKTsKICAjIyMgLS0tIG91dHB1dCBmaWxlbmFtZQogIGZpbGVuYW1lIDwtIHBhc3RlKGRhdGFEaXIsJ3VzZXJFZGl0cycsICJfIiwgaSwgIi50c3YiLCBzZXAgPSAiIikKICAjIyMgLS0tIGV4ZWN1dGUgc3FsIHNjcmlwdDoKICBzcWxMb2dJblByZSA8LSBwYXN0ZTAoJy91c3IvbG9jYWwvYmluL2FuYWx5dGljcy1teXNxbCBkZXdpa2kgLWUgJykKICBzcWxJbnB1dCA8LSBwYXN0ZShzcWxRdWVyeSwKICAgICAgICAgICAgICAgICAgICAiID4gIiwKICAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSwKICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAjIC0gY29tbWFuZDoKICBzcWxDb21tYW5kIDwtIHBhc3RlKHNxbExvZ0luUHJlLCBzcWxJbnB1dCkKICBzeXN0ZW0oY29tbWFuZCA9IHNxbENvbW1hbmQsIHdhaXQgPSBUUlVFKQogICMgLSByZXBvcnQKICBwcmludChwYXN0ZTAoIkRPTkU6IHVzZXIgIiwgaSwgIi4iKSkKfQojIyMgLS0tIEVORCBydW4gU1FMIHNjcmlwdHMKIyAtIGxvYWQgdXNlciBlZGl0czoKbEYgPC0gbGlzdC5maWxlcyhkYXRhRGlyKQpsRiA8LSBsRltncmVwbCgiXnVzZXJFZGl0c18iLCBsRildCnVzZXJFZGl0cyA8LSBsYXBwbHkocGFzdGUwKGRhdGFEaXIsIGxGKSwgZnJlYWQpCnVzZXJFZGl0cyA8LSByYmluZGxpc3QodXNlckVkaXRzKQojIC0gc3RvcmUgdXNlciBlZGl0czoKd3JpdGUuY3N2KHVzZXJFZGl0cywgcGFzdGUwKGFuYWx5dGljc0RpciwgJ3VzZXJFZGl0cy5jc3YnKSkKYGBgCgojIyAwLiBDYW1wYWlnbiBCYW5uZXJzCgpUaGlzIHNlY3Rpb24gcHJlc2VudHMgYWxsIGRhdGEgYW5kIHN0YXRpc3RpY3Mgb24gdGhlIGNhbXBhaWduIGJhbm5lcnMuClRoZSBmb2xsb3dpbmcgY2h1bmsgbG9hZHMgYW5kIHRoZW4gcmUtc3RydWN0dXJlcyB0aGUgYmFubmVycyBkYXRhc2V0IGEgYml0OgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmxGIDwtIGxpc3QuZmlsZXMoJ19kYWlseVJlcG9ydGluZycpCmxGIDwtIGxGW2dyZXBsKCJeYmFubmVySW50ZXJhY3Rpb25zIiwgbEYpXQpkYXRhU2V0IDwtIGxhcHBseShwYXN0ZTAoIl9kYWlseVJlcG9ydGluZy8iLCBsRiksIGZyZWFkKQpkYXRhU2V0IDwtIHJiaW5kbGlzdChkYXRhU2V0KQpkYXRhU2V0JFYxIDwtIE5VTEwKZGF0YVNldCRjYW1wYWlnbiA8LSBOVUxMCiMgLSBOQXMgdG8gemVybwpkYXRhU2V0W3doaWNoKGlzLm5hKGRhdGFTZXQpLCBhcnIuaW5kID0gVCldIDwtIDAKIyAtIGJhbm5lciBjb2RlcwpkYXRhU2V0JGJhbm5lcm5hbWUgPC0gc2FwcGx5KGRhdGFTZXQkYmFubmVybmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZCA8LSBzdHJzcGxpdCh4LCBzcGxpdCA9ICJfIilbWzFdXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZCA8LSBwYXN0ZShkWzE6NV0sIGNvbGxhcHNlID0gIl8iKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuKGQpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSkKZGF0YVNldCRiYW5uZXJuYW1lIDwtIGdzdWIoIk5BIiwgImRlc2t0b3AiLCBkYXRhU2V0JGJhbm5lcm5hbWUpCmRhdGFTZXQgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGJhbm5lcm5hbWUsIGRheSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5KSAlPiUKICBncm91cF9ieShiYW5uZXJuYW1lLCBkYXkpICU+JQogIHN1bW1hcmlzZShjbGlja2VkX2J5ID0gc3VtKGNsaWNrZWRfYnkpLCAKICAgICAgICAgICAgY2xvc2VkX2J5ID0gc3VtKGNsb3NlZF9ieSkpCmRhdGFTZXQkdmFyX2N0cmwgPC0gc2FwcGx5KGRhdGFTZXQkYmFubmVybmFtZSwgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgidmFyIiwgeCkpIHtyZXR1cm4oInZhciIpfSBlbHNlIHtyZXR1cm4oImN0cmwiKX0KfSkKZGF0YVNldCRkZXZpY2UgPC0gc2FwcGx5KGRhdGFTZXQkYmFubmVybmFtZSwgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgibW9iaWxlIiwgeCkpIHsKICAgIHJldHVybigibW9iaWxlIikKICB9IGVsc2UgaWYgKGdyZXBsKCJpcGFkIiwgeCkpIHsKICAgICAgcmV0dXJuKCJpcGFkIikKICB9IGVsc2UgewogICAgICByZXR1cm4oImRlc2t0b3AiKQogIH0KfSkKYGBgCgpUaGUgZm9sbG93aW5nIGRhdGFzZXQgcHJlc2VudHMgdGhlIGJhbm5lciBpbXByZXNzaW9ucyBkYXRhOgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmxGIDwtIGxpc3QuZmlsZXMoJ19kYWlseVJlcG9ydGluZycpCmxGIDwtIGxGW2dyZXBsKCJeYmFubmVySW1wcmVzc2lvbnNBZ2dyZWdhdGVkIiwgbEYpXQpiaV9kYXRhU2V0IDwtIGxhcHBseShwYXN0ZTAoIl9kYWlseVJlcG9ydGluZy8iLCBsRiksIGZyZWFkKQpiaV9kYXRhU2V0IDwtIHJiaW5kbGlzdChiaV9kYXRhU2V0KQpiaV9kYXRhU2V0JFYxIDwtIE5VTEwKYmlfZGF0YVNldCRiYW5uZXIgPC0gZ3N1YigiXldNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX2N0cmwkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIldNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX2N0cmxfZGVza3RvcCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYmlfZGF0YVNldCRiYW5uZXIpCmJpX2RhdGFTZXQkYmFubmVyIDwtIGdzdWIoIl5XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXIkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIldNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX3Zhcl9kZXNrdG9wIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBiaV9kYXRhU2V0JGJhbm5lcikKYmlfZGF0YVNldCRjYW1wYWlnbiA8LSBOVUxMCmNvbG5hbWVzKGJpX2RhdGFTZXQpWzJdIDwtICdzZWVuX2J5JwpgYGAKCkpvaW4gYmFubmVyIGltcHJlc3Npb25zIGRhdGEgKGBiaV9kYXRhU2V0YCkgdG8gYmFubmVyIGludGVyYWN0aW9ucyBkYXRhIChgZGF0YVNldGApIHRvIHVzZSBgYmlfZGF0YVNldCRzZWVuX2J5YDoKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpkYXRhU2V0IDwtIGRwbHlyOjpsZWZ0X2pvaW4oZGF0YVNldCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBiaV9kYXRhU2V0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJkYXkiID0gImRhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJiYW5uZXJuYW1lIiA9ICJiYW5uZXIiKQopCnJtKGJpX2RhdGFTZXQpCmBgYAoKIyMjIDAuMSBCYW5uZXIgQWN0aW9ucyBPdmVydmlldwoKKipDaGFydCAwLjEuMSoqIERhaWx5IEJhbm5lciBJbXByZXNzaW9ucywgQ2xpY2tzLCBhbmQgQ2xvc3VyZXMsIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRheSwgc2Vlbl9ieSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5KSAlPiUgCiAgZ3JvdXBfYnkoZGF5KSAlPiUgCiAgc3VtbWFyaXNlKHNlZW5fYnkgPSBzdW0oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnkgPSBzdW0oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnkgPSBzdW0oY2xpY2tlZF9ieSkpCnBGcmFtZSRjbG9zZV9yYXRlID0gcm91bmQocEZyYW1lJGNsb3NlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lJGNsaWNrX3JhdGUgPSByb3VuZChwRnJhbWUkY2xpY2tlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXkpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRhaWx5OgpwRiA8LSBwRnJhbWUgJT4lIAogIHNlbGVjdChkYXksIHNlZW5fYnksIGNsb3NlZF9ieSwgY2xpY2tlZF9ieSkgJT4lIAogIHBpdm90X2xvbmdlcihjKCdzZWVuX2J5JywgJ2Nsb3NlZF9ieScsICdjbGlja2VkX2J5JyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBY3Rpb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlZhbHVlIikKZ2dwbG90KHBGLCBhZXMoeCA9IGRheSwKICAgICAgICAgICAgICAgeSA9IFZhbHVlLAogICAgICAgICAgICAgICBncm91cCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdXaWtpcGVkaWEgQ2hhbGxlbmdlIE9jdG9iZXIgMjAyMCcpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJBY3Rpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipDaGFydCAwLjEuMioqIFRvdGFsLCBNZWFuIHBlciBkYXksIGFuZCBNZWRpYW4gcGVyIGRheSBCYW5uZXIgSW1wcmVzc2lvbnMsIENsaWNrcywgYW5kIENsb3N1cmVzLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNX0KcEZyYW1lVG90YWwgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRheSwgc2Vlbl9ieSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5KSAlPiUgCiAgZ3JvdXBfYnkoZGF5KSAlPiUgCiAgc3VtbWFyaXNlKHNlZW5fYnkgPSBzdW0oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnkgPSBzdW0oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnkgPSBzdW0oY2xpY2tlZF9ieSkKICAgICAgICAgICAgKSAKcEZyYW1lVG90YWwgPC0gcEZyYW1lVG90YWwgJT4lIAogIHN1bW1hcmlzZShzZWVuX2J5X190b3RhbF9pbl9jYW1wYWlnbiA9IHN1bShzZWVuX2J5KSwgCiAgICAgICAgICAgIGNsb3NlZF9ieV9fdG90YWxfaW5fY2FtcGFpZ24gPSBzdW0oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnlfX3RvdGFsX2luX2NhbXBhaWduID0gc3VtKGNsaWNrZWRfYnkpLAogICAgICAgICAgICBzZWVuX2J5X19tZWFuX3Blcl9kYXkgPSBtZWFuKHNlZW5fYnkpLCAKICAgICAgICAgICAgY2xvc2VkX2J5X19tZWFuX3Blcl9kYXkgPSBtZWFuKGNsb3NlZF9ieSksIAogICAgICAgICAgICBjbGlja2VkX2J5X19tZWFuX3Blcl9kYXkgPSBtZWFuKGNsaWNrZWRfYnkpLAogICAgICAgICAgICBzZWVuX2J5X19tZWRpYW5fcGVyX2RheSA9IG1lZGlhbihzZWVuX2J5KSwgCiAgICAgICAgICAgIGNsb3NlZF9ieV9fbWVkaWFuX3Blcl9kYXkgPSBtZWRpYW4oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnlfX21lZGlhbl9wZXJfZGF5ID0gbWVkaWFuKGNsaWNrZWRfYnkpCiAgICAgICAgICAgICkgJT4lIAogIHQoKQpwRnJhbWVUb3RhbFssIDFdIDwtIHJvdW5kKHBGcmFtZVRvdGFsWywgMV0sIDIpCnBGcmFtZVRvdGFsIDwtIGFzLmRhdGEuZnJhbWUocEZyYW1lVG90YWwpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oJ01lYXN1cmUnKQpjb2xuYW1lcyhwRnJhbWVUb3RhbClbMl0gPC0gJ1ZhbHVlJwpwRnJhbWVUb3RhbCA8LXBGcmFtZVRvdGFsICU+JSAKICBzZXBhcmF0ZSgnTWVhc3VyZScsIAogICAgICAgICAgIHNlcCA9ICJfXyIsIAogICAgICAgICAgIGludG8gPSBjKCdBY3Rpb24nLCAnU3RhdGlzdGljJykpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRhaWx5OgpnZ3Bsb3QocEZyYW1lVG90YWwsIGFlcyh4ID0gQWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICB5ID0gbG9nKFZhbHVlKSwKICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBTdGF0aXN0aWMsCiAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gU3RhdGlzdGljLAogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFZhbHVlLAogICAgICAgICAgICAgICAgICAgICkpICsKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsgIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ1dpa2lwZWRpYSBDaGFsbGVuZ2UgT2N0b2JlciAyMDIwJykgKwogIHhsYWIoIkFjdGlvbiIpICsgeWxhYigibG9nKFZhbHVlKSIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKKipDaGFydCAwLjEuMyoqIERhaWx5IEJhbm5lciBDbGljayBhbmQgQ2xvc2UgcmF0ZXMsIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfTogY2xvc2VfcmF0ZSwgY2xpY2tfcmF0ZSwgZGFpbHk6CnBGIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KGRheSwgY2xpY2tfcmF0ZSwgY2xvc2VfcmF0ZSkgJT4lIAogIHBpdm90X2xvbmdlcihjKCdjbGlja19yYXRlJywgJ2Nsb3NlX3JhdGUnKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkFjdGlvbiIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiVmFsdWUiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gVmFsdWUsCiAgICAgICAgICAgICAgIGdyb3VwID0gQWN0aW9uLAogICAgICAgICAgICAgICBjb2xvciA9IEFjdGlvbiwKICAgICAgICAgICAgICAgZmlsbCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgbGFiZWwgPSBWYWx1ZSwKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJzIwMjAgV01ERSBPY2Nhc2lvbmFsIEVkaXRvcnMgQmFubmVyIENhbXBhaWduJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIkFjdGlvbnMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKyAKICB5bGltKGMoMCwgLjI1KSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipUYWJsZSAwLjEuNCoqIE1lYW4gYW5kIE1lZGlhbiBDbGljayBhbmQgQ2xvc2UgcmF0ZXMgKGFjcm9zcyBkYXlzKQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnRGcmFtZSA8LSBkYXRhLmZyYW1lKGNsaWNrX3JhdGVfbWVhbiA9IHJvdW5kKG1lYW4ocEZyYW1lJGNsaWNrX3JhdGUpLCA1KSwgCiAgICAgICAgICAgICAgICAgICAgIGNsaWNrX3JhdGVfbWVkaWFuID0gcm91bmQobWVkaWFuKHBGcmFtZSRjbGlja19yYXRlKSwgNSksCiAgICAgICAgICAgICAgICAgICAgIGNsb3NlX3JhdGVfbWVhbiA9IHJvdW5kKG1lYW4ocEZyYW1lJGNsb3NlX3JhdGUpLCA1KSwKICAgICAgICAgICAgICAgICAgICAgY2xvc2VfcmF0ZV9tZWRpYW4gPSByb3VuZChtZWRpYW4ocEZyYW1lJGNsb3NlX3JhdGUpLCA1KQopCmRhdGF0YWJsZSh0RnJhbWUpCmBgYAoKKipUYWJsZSAwLjEuNSoqIERlIEZhY3RvIENsaWNrIGFuZCBDbG9zZSByYXRlcyBpbiBwZXJjZW50cwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnRGcmFtZSA8LSBkYXRhLmZyYW1lKGNsaWNrX3JhdGUgPSByb3VuZChzdW0ocEZyYW1lJGNsaWNrZWRfYnkpL3N1bShwRnJhbWUkc2Vlbl9ieSkqMTAwLCA1KSwgCiAgICAgICAgICAgICAgICAgICAgIGNsb3NlX3JhdGUgPSByb3VuZChzdW0ocEZyYW1lJGNsb3NlZF9ieSkvc3VtKHBGcmFtZSRzZWVuX2J5KSoxMDAsIDUpKQpkYXRhdGFibGUodEZyYW1lKQpgYGAKCioqQ2hhcnQgMC4xLjYqKiBEYWlseSBCYW5uZXIgQ2xpY2sgYW5kIENsb3NlIHJhdGVzLCBieSAqKmRldmljZXMqKiwgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDEwfQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRheSwgc2Vlbl9ieSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5LCBkZXZpY2UpICU+JSAKICBncm91cF9ieShkYXksIGRldmljZSkgJT4lIAogIHN1bW1hcmlzZShzZWVuX2J5ID0gc3VtKHNlZW5fYnkpLCAKICAgICAgICAgICAgY2xvc2VkX2J5ID0gc3VtKGNsb3NlZF9ieSksIAogICAgICAgICAgICBjbGlja2VkX2J5ID0gc3VtKGNsaWNrZWRfYnkpKQpwRnJhbWUkY2xvc2VfcmF0ZSA9IHJvdW5kKHBGcmFtZSRjbG9zZWRfYnkvcEZyYW1lJHNlZW5fYnksIDIpCnBGcmFtZSRjbGlja19yYXRlID0gcm91bmQocEZyYW1lJGNsaWNrZWRfYnkvcEZyYW1lJHNlZW5fYnksIDIpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZGF5KQojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfTogc2Vlbl9ieSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5LCBkYWlseToKcEYgPC0gcEZyYW1lICU+JSAKICBzZWxlY3QoZGF5LCBkZXZpY2UsIHNlZW5fYnksIGNsb3NlZF9ieSwgY2xpY2tlZF9ieSkgJT4lIAogIHBpdm90X2xvbmdlcihjKCdzZWVuX2J5JywgJ2Nsb3NlZF9ieScsICdjbGlja2VkX2J5JyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBY3Rpb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlZhbHVlIikKZ2dwbG90KHBGLCBhZXMoeCA9IGRheSwKICAgICAgICAgICAgICAgeSA9IFZhbHVlLAogICAgICAgICAgICAgICBncm91cCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBmYWNldF93cmFwKH5kZXZpY2UsIG5yb3cgPSAzLCBzY2FsZXMgPSAiZnJlZSIpICsgCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiQWN0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCioqQ2hhcnQgMC4xLjZCKiogTWVhbiwgbWVkaWFuLCBhbmQgdG90YWwgSW1wcmVzc2lvbnMsIGNsaWNrcyBhbmQgY2xvc2luZyBjbGlja3MgKipwZXIgYmFubmVyKiogLCAqKnBlciBkZXZpY2UqKi4KKipOT1RFLioqIExvZyh5KSBzY2FsZXMgd2VyZSB1c2VkIHRvIGhlbHAgcmVhZGJpbGl0eTsgZGF0YSBsYWJlbHMgYXJlIGV4YWN0IHZhbHVlcy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gMTB9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6c2VsZWN0KHNlZW5fYnksIGNsaWNrZWRfYnksIGNsb3NlZF9ieSwgZGV2aWNlLCB2YXJfY3RybCkKcEZyYW1lJGJhbm5lcm5hbWUgPC0gTlVMTApwRnJhbWUgPC0gcEZyYW1lICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoZGV2aWNlLCB2YXJfY3RybCkgJT4lIAogIHN1bW1hcmlzZShtZWFuMHNlZW5fYnkgPSBtZWFuKHNlZW5fYnkpLAogICAgICAgICAgICBtZWFuMGNsaWNrZWRfYnkgPSBtZWFuKGNsaWNrZWRfYnkpLAogICAgICAgICAgICBtZWFuMGNsb3NlZF9ieSA9IG1lYW4oY2xvc2VkX2J5KSwKICAgICAgICAgICAgbWVkaWFuMHNlZW5fYnkgPSBtZWRpYW4oc2Vlbl9ieSksCiAgICAgICAgICAgIG1lZGlhbjBjbGlja2VkX2J5ID0gbWVkaWFuKGNsaWNrZWRfYnkpLAogICAgICAgICAgICBtZWRpYW4wY2xvc2VkX2J5ID0gbWVkaWFuKGNsb3NlZF9ieSksCiAgICAgICAgICAgIHRvdGFsMHNlZW5fYnkgPSBzdW0oc2Vlbl9ieSksCiAgICAgICAgICAgIHRvdGFsMGNsaWNrZWRfYnkgPSBzdW0oY2xpY2tlZF9ieSksCiAgICAgICAgICAgIHRvdGFsMGNsb3NlZF9ieSA9IHN1bShjbG9zZWRfYnkpCiAgICAgICAgICAgICkKcEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKCdtZWFuMHNlZW5fYnknLCAnbWVhbjBjbGlja2VkX2J5JywgJ21lYW4wY2xvc2VkX2J5JywKICAgICAgICAgICAgICAgICAgICAgICAgJ21lZGlhbjBzZWVuX2J5JywgJ21lZGlhbjBjbGlja2VkX2J5JywgJ21lZGlhbjBjbG9zZWRfYnknLAogICAgICAgICAgICAgICAgICAgICAgICAndG90YWwwc2Vlbl9ieScsICd0b3RhbDBjbGlja2VkX2J5JywgJ3RvdGFsMGNsb3NlZF9ieScpLCAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAnbWVhc3VyZScsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAndmFsdWUnKQpwRnJhbWUgPC0gcEZyYW1lICU+JSAKICBzZXBhcmF0ZShtZWFzdXJlLAogICAgICAgICAgIGludG8gPSBjKCdtZWFzdXJlJywgJ2FjdGlvbicpLAogICAgICAgICAgIHNlcCA9ICIwIikKcEZyYW1lJHZhbHVlIDwtIHJvdW5kKHBGcmFtZSR2YWx1ZSwgMSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBtZWFzdXJlLAogICAgICAgICAgICAgICB5ID0gbG9nKHZhbHVlKSwKICAgICAgICAgICAgICAgZ3JvdXAgPSBhY3Rpb24sCiAgICAgICAgICAgICAgIGNvbG9yID0gYWN0aW9uLAogICAgICAgICAgICAgICBmaWxsID0gYWN0aW9uLAogICAgICAgICAgICAgICBsYWJlbCA9IHZhbHVlLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZmFjZXRfd3JhcChkZXZpY2V+dmFyX2N0cmwsIG5yb3cgPSAzLCBzY2FsZXMgPSAiZnJlZSIpICsgCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiTWVhc3VyZSIpICsgeWxhYigibG9nKFZhbHVlKSIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCgoqKkNoYXJ0IDAuMS43KiogRGFpbHkgQmFubmVyIENsaWNrIGFuZCBDbG9zZSByYXRlcywgYnkgKip2YXIvY3RybCoqLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRheSwgc2Vlbl9ieSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5LCB2YXJfY3RybCkgJT4lIAogIGdyb3VwX2J5KGRheSwgdmFyX2N0cmwpICU+JSAKICBzdW1tYXJpc2Uoc2Vlbl9ieSA9IHN1bShzZWVuX2J5KSwgCiAgICAgICAgICAgIGNsb3NlZF9ieSA9IHN1bShjbG9zZWRfYnkpLCAKICAgICAgICAgICAgY2xpY2tlZF9ieSA9IHN1bShjbGlja2VkX2J5KSkKcEZyYW1lJGNsb3NlX3JhdGUgPSByb3VuZChwRnJhbWUkY2xvc2VkX2J5L3BGcmFtZSRzZWVuX2J5LCAyKQpwRnJhbWUkY2xpY2tfcmF0ZSA9IHJvdW5kKHBGcmFtZSRjbGlja2VkX2J5L3BGcmFtZSRzZWVuX2J5LCAyKQpwRnJhbWUgPC0gYXJyYW5nZShwRnJhbWUsIGRheSkKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn06IHNlZW5fYnksIGNsaWNrZWRfYnksIGNsb3NlZF9ieSwgZGFpbHk6CnBGIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KGRheSwgdmFyX2N0cmwsIHNlZW5fYnksIGNsb3NlZF9ieSwgY2xpY2tlZF9ieSkgJT4lIAogIHBpdm90X2xvbmdlcihjKCdzZWVuX2J5JywgJ2Nsb3NlZF9ieScsICdjbGlja2VkX2J5JyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBY3Rpb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlZhbHVlIikKZ2dwbG90KHBGLCBhZXMoeCA9IGRheSwKICAgICAgICAgICAgICAgeSA9IFZhbHVlLAogICAgICAgICAgICAgICBncm91cCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBmYWNldF93cmFwKH52YXJfY3RybCwgbnJvdyA9IDIsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJBY3Rpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipDaGFydCAwLjEuN0IqKiBNZWFuLCBtZWRpYW4sIGFuZCB0b3RhbCBJbXByZXNzaW9ucywgY2xpY2tzIGFuZCBjbG9zaW5nIGNsaWNrcyAqKnBlciBiYW5uZXIqKi4KKipOT1RFLioqIExvZyh5KSBzY2FsZXMgd2VyZSB1c2VkIHRvIGhlbHAgcmVhZGJpbGl0eTsgZGF0YSBsYWJlbHMgYXJlIGV4YWN0IHZhbHVlcy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OnNlbGVjdChzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIHZhcl9jdHJsKQpwRnJhbWUkYmFubmVybmFtZSA8LSBOVUxMCnBGcmFtZSA8LSBwRnJhbWUgJT4lIAogIGRwbHlyOjpncm91cF9ieSh2YXJfY3RybCkgJT4lIAogIHN1bW1hcmlzZShtZWFuMHNlZW5fYnkgPSBtZWFuKHNlZW5fYnkpLAogICAgICAgICAgICBtZWFuMGNsaWNrZWRfYnkgPSBtZWFuKGNsaWNrZWRfYnkpLAogICAgICAgICAgICBtZWFuMGNsb3NlZF9ieSA9IG1lYW4oY2xvc2VkX2J5KSwKICAgICAgICAgICAgbWVkaWFuMHNlZW5fYnkgPSBtZWRpYW4oc2Vlbl9ieSksCiAgICAgICAgICAgIG1lZGlhbjBjbGlja2VkX2J5ID0gbWVkaWFuKGNsaWNrZWRfYnkpLAogICAgICAgICAgICBtZWRpYW4wY2xvc2VkX2J5ID0gbWVkaWFuKGNsb3NlZF9ieSksCiAgICAgICAgICAgIHRvdGFsMHNlZW5fYnkgPSBzdW0oc2Vlbl9ieSksCiAgICAgICAgICAgIHRvdGFsMGNsaWNrZWRfYnkgPSBzdW0oY2xpY2tlZF9ieSksCiAgICAgICAgICAgIHRvdGFsMGNsb3NlZF9ieSA9IHN1bShjbG9zZWRfYnkpCiAgICAgICAgICAgICkKcEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKCdtZWFuMHNlZW5fYnknLCAnbWVhbjBjbGlja2VkX2J5JywgJ21lYW4wY2xvc2VkX2J5JywKICAgICAgICAgICAgICAgICAgICAgICAgJ21lZGlhbjBzZWVuX2J5JywgJ21lZGlhbjBjbGlja2VkX2J5JywgJ21lZGlhbjBjbG9zZWRfYnknLAogICAgICAgICAgICAgICAgICAgICAgICAndG90YWwwc2Vlbl9ieScsICd0b3RhbDBjbGlja2VkX2J5JywgJ3RvdGFsMGNsb3NlZF9ieScpLCAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAnbWVhc3VyZScsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAndmFsdWUnKQpwRnJhbWUgPC0gcEZyYW1lICU+JSAKICBzZXBhcmF0ZShtZWFzdXJlLAogICAgICAgICAgIGludG8gPSBjKCdtZWFzdXJlJywgJ2FjdGlvbicpLAogICAgICAgICAgIHNlcCA9ICIwIikKcEZyYW1lJHZhbHVlIDwtIHJvdW5kKHBGcmFtZSR2YWx1ZSwgMSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBtZWFzdXJlLAogICAgICAgICAgICAgICB5ID0gbG9nKHZhbHVlKSwKICAgICAgICAgICAgICAgZ3JvdXAgPSBhY3Rpb24sCiAgICAgICAgICAgICAgIGNvbG9yID0gYWN0aW9uLAogICAgICAgICAgICAgICBmaWxsID0gYWN0aW9uLAogICAgICAgICAgICAgICBsYWJlbCA9IHZhbHVlLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZmFjZXRfd3JhcCh+dmFyX2N0cmwsIG5yb3cgPSAzLCBzY2FsZXMgPSAiZnJlZSIpICsgCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiTWVhc3VyZSIpICsgeWxhYigibG9nKFZhbHVlKSIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCgoqKlRhYmxlIDAuMS44KiogRGFpbHkgQmFubmVyIEltcHJlc3Npb25zOiBzZWVuIGJ5LCBjbG9zZWQgYnksIGNsaWNrZWQgYnksIFBhZ2V2aWV3cwoKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIyMgLS0tIEZ1bGwgRGF0YXNldCAoVGFibGUgUmVwb3J0KQpkUyA8LSBkYXRhU2V0CmRTJGJhbm5lcm5hbWUgPC0gZ3N1YigiV01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfIiwgIiIsIGRTJGJhbm5lcm5hbWUpCmRTIDwtIGRTWywKICAgICAgICAgYygnYmFubmVybmFtZScsICdkYXknLCAnZGV2aWNlJywgJ3Zhcl9jdHJsJywgCiAgICAgICAgICAgJ3NlZW5fYnknLCAnY2xvc2VkX2J5JywgJ2NsaWNrZWRfYnknKV0KZGF0YXRhYmxlKGRTLCAKICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSAzMCkKICAgICAgICAgICkKYGBgCgojIyAxLiBDYW1wYWlnbiBQYWdldmlld3MKClRoaXMgc2VjdGlvbiBwcmVzZW50cyBhbGwgZGF0YSBhbmQgc3RhdGlzdGljcyBvbiB0aGUgY2FtcGFpZ24gcGFnZXMuCgpUaGUgZm9sbG93aW5nIGNodW5rIGxvYWRzIGFuZCB0aGVuIHJlLXN0cnVjdHVyZXMgdGhlIGRhdGFzZXQgYSBiaXQuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KbEYgPC0gbGlzdC5maWxlcygnX2RhaWx5UmVwb3J0aW5nJykKbEYgPC0gbEZbZ3JlcGwoIl5wYWdldmlld3NBZ2dyZWdhdGVkIiwgbEYpXQpkYXRhU2V0IDwtIGxhcHBseShwYXN0ZTAoIl9kYWlseVJlcG9ydGluZy8iLCBsRiksIGZyZWFkKQpkYXRhU2V0IDwtIHJiaW5kbGlzdChkYXRhU2V0KQpkYXRhU2V0JFYxIDwtIE5VTEwKZGF0YVNldCRjYW1wYWlnbiA8LSBOVUxMCiMgLSBleHBhbmQgZ3JpZCB0byBhY2NvdW50IGZvciBtaXNzaW5nIG9ic2VydmF0aW9ucyBwZXIgZGF5CmRTIDwtIGV4cGFuZC5ncmlkKHVuaXF1ZShkYXRhU2V0JFRhZyksIAogICAgICAgICAgICAgICAgICB1bmlxdWUoZGF0YVNldCRQYWdlKSwgCiAgICAgICAgICAgICAgICAgIHVuaXF1ZShkYXRhU2V0JGRhdGUpLCAKICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGRTKSA8LSBjKCdUYWcnLCAnUGFnZScsICdkYXRlJykKZFMgPC0gZFMgJT4lIAogIGxlZnRfam9pbihkYXRhU2V0LCAKICAgICAgICAgICAgYnkgPSBjKCJUYWciLCAiUGFnZSIsICJkYXRlIikpCmRhdGFTZXQgPC0gZFM7IHJtKGRTKQpkYXRhU2V0JFBhZ2V2aWV3c1tpcy5uYShkYXRhU2V0JFBhZ2V2aWV3cyldIDwtIDAKZGF0YVNldCRUYWcgPC0gZ3N1YigiJi4rJCIsICIiLCBkYXRhU2V0JFRhZykKZGF0YVNldCRUYWcgPC0gZ3N1YigiXFw/Y2FtcGFpZ249IiwgIiIsIGRhdGFTZXQkVGFnKQpkYXRhU2V0JGRhdGUgPC0gc2FwcGx5KGRhdGFTZXQkZGF0ZSwgZnVuY3Rpb24oeCkgewogIGQgPC0gc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV0KICBpZiAobmNoYXIoZFszXSkgPT0gMSkgewogICAgZFszXSA8LSBwYXN0ZTAoIjAiLCBkWzNdKQogIH0KICByZXR1cm4ocGFzdGUoZCwgY29sbGFwc2UgPSAiLSIpKQp9KQpgYGAKCiMjIyAxLjEgUGFnZXZpZXdzIE92ZXJ2aWV3CgoqKkNoYXJ0IDEuMS4xKiogRGFpbHkgUGFnZXZpZXdzLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gY2hhbm5lbHMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA2fQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRhdGUsIFBhZ2UsIFBhZ2V2aWV3cykgJT4lIAogIGdyb3VwX2J5KGRhdGUsIFBhZ2UpICU+JSAKICBzdW1tYXJpc2UoUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZGF0ZSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSwgZ3JvdXAgPSAxKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBmYWNldF93cmFwKH5QYWdlLCBuY29sID0gMikgKyAKICBnZ3RpdGxlKCdXaWtpcGVkaWEgQ2hhbGxlbmdlIE9jdG9iZXIgMjAyMCcpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJQYWdldmlld3MiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogICMgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDEwKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCioqVGFibGUgMS4xLjEqKiBQYWdldmlld3MgdG90YWxzCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KFBhZ2UsIFBhZ2V2aWV3cykgJT4lIAogIGdyb3VwX2J5KFBhZ2UpICU+JSAKICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkgJT4lIAogIGFycmFuZ2UoZGVzYyh0b3RhbFBhZ2V2aWV3cykpCmRhdGF0YWJsZSh0RnJhbWUpCmBgYAoKIyMjIDEuMiBQYWdldmlld3M6IENhbXBhaWduIENoYW5uZWxzCgoqKkNoYXJ0IDEuMi4xKiogUGFnZXZpZXdzLCBieSAqKmNoYW5uZWxzKioKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDZ9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF0ZSwgVGFnLCBQYWdldmlld3MpICU+JSAKICBncm91cF9ieShkYXRlLCBUYWcpICU+JSAKICBzdW1tYXJpc2UoUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZGF0ZSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSwgZ3JvdXAgPSAxKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBmYWNldF93cmFwKH5UYWcsIG5jb2wgPSAyKSArIAogIGdndGl0bGUoJ1dpa2lwZWRpYSBDaGFsbGVuZ2UgT2N0b2JlciAyMDIwJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIlBhZ2V2aWV3cyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgIyBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNikpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgoqKkNoYXJ0IDEuMi4yKiogVG90YWwgUGFnZXZpZXdzLCBieSAqKmNoYW5uZWxzKioKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNX0KcEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KFRhZywgUGFnZXZpZXdzKSAlPiUgCiAgZ3JvdXBfYnkoVGFnKSAlPiUgCiAgc3VtbWFyaXNlKFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKSAlPiUgCiAgYXJyYW5nZShkZXNjKFBhZ2V2aWV3cykpCnBGcmFtZSRUYWcgPC0gZmFjdG9yKHBGcmFtZSRUYWcsCiAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHBGcmFtZSRUYWcsCiAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBUKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IFRhZywKICAgICAgICAgICAgICAgICAgIHkgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvciA9ICJibHVlIiwgZmlsbCA9ICJ3aGl0ZSIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAnKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiUGFnZXZpZXdzIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCioqVGFibGUgMi4yLjEqKiBUb3RhbCBwYWdldmlld3MsIGJ5ICoqY2hhbm5lbHMqKiBhbmQgYnkgKipwYWdlcyoqLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnRGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoVGFnLCBQYWdlLCBQYWdldmlld3MpICU+JSAKICBncm91cF9ieShUYWcsIFBhZ2UpICU+JSAKICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkgJT4lIAogIGFycmFuZ2UoZGVzYyh0b3RhbFBhZ2V2aWV3cykpCmRhdGF0YWJsZSh0RnJhbWUpCmBgYAoKIyMgMi4gVXNlciBSZWdpc3RyYXRpb25zCgpUaGlzIHNlY3Rpb24gcHJlc2VudHMgYWxsIGRhdGEgYW5kIHN0YXRpc3RpY3Mgb24gdGhlIHVzZXIgcmVnaXN0cmF0aW9ucy4KCiMjIyAyLjEgVXNlciBSZWdpc3RyYXRpb25zIE92ZXJ2aWV3CgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KbEYgPC0gbGlzdC5maWxlcygiX2RhaWx5UmVwb3J0aW5nIikKbEYgPC0gbEZbZ3JlcGwoIl51c2VyX3JlZ2lzdHJhdGlvbnMiLCBsRildCmRhdGFTZXQgPC0gZnJlYWQocGFzdGUwKCJfZGFpbHlSZXBvcnRpbmcvIiwgbEYpKQpkYXRhU2V0IDwtIGZpbHRlcihkYXRhU2V0LCAKICAgICAgICAgICAgICAgICAgIWdyZXBsKCJedGVzdHxeVGVzdCIsIGRhdGFTZXQkdXNlcm5hbWUpKQpkYXRhU2V0JGRhdGUgPC0gcGFzdGUoZGF0YVNldCR5ZWFyLCAKICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShuY2hhcihkYXRhU2V0JG1vbnRoKSA9PSAxLCBwYXN0ZTAoIjAiLCBkYXRhU2V0JG1vbnRoKSwgZGF0YVNldCRtb250aCksCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UobmNoYXIoZGF0YVNldCRkYXkpID09IDEsIHBhc3RlMCgiMCIsIGRhdGFTZXQkZGF5KSwgZGF0YVNldCRkYXkpLAogICAgICAgICAgICAgICAgICAgICAgc2VwID0gIi0iKQpyZWdVc2VycyA8LSBzZWxlY3QoZGF0YVNldCwKICAgICAgICAgICAgICAgICAgIHVzZXJpZCwKICAgICAgICAgICAgICAgICAgIHVzZXJuYW1lLCAKICAgICAgICAgICAgICAgICAgIGNhbXBhaWduKQojIC0gdG90YWwgbnVtYmVyIG9mIHJlZ2lzdGVyZWQgdXNlcnMKdG90YWxfcmVnaXN0ZXJlZCA8LSBkaW0ocmVnVXNlcnMpWzFdCiMgLSBleHBhbmQgZ3JpZCB0byBhY2NvdW50IGZvciBtaXNzaW5nIG9ic2VydmF0aW9ucyBwZXIgZGF5CmRhdGVTcGFuIDwtIHNlcShmcm9tID0gYXMuRGF0ZSgiMjAyMC0xMC0wMSIpLCAKICAgICAgICAgICAgICAgIHRvID0gYXMuRGF0ZSgiMjAyMC0xMS0xMSIpLCAKICAgICAgICAgICAgICAgIGJ5ID0gImRheSIpCmRhdGVTcGFuIDwtIGFzLmNoYXJhY3RlcihkYXRlU3BhbikKZFMgPC0gZXhwYW5kLmdyaWQoZGF0ZVNwYW4sIAogICAgICAgICAgICAgICAgICB1bmlxdWUoZGF0YVNldCRjYW1wYWlnbiksIAogICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXMoZFMpIDwtIGMoJ2RhdGUnLCAnY2FtcGFpZ24nKQpkUyA8LSBkUyAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChkYXRhU2V0LCBkYXRlLCBjYW1wYWlnbiwgdXNlcmlkKSwKICAgICAgICAgICAgYnkgPSBjKCJkYXRlIiwgImNhbXBhaWduIikpCmRTJHVzZXJpZCA8LSBpZmVsc2UoaXMubmEoZFMkdXNlcmlkKSwgMCwgMSkKcEZyYW1lIDwtIGRTICU+JSAKICBzZWxlY3QoZGF0ZSwgdXNlcmlkKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lIAogIHN1bW1hcmlzZShyZWdpc3RyYXRpb25zID0gc3VtKHVzZXJpZCkpICU+JSAKICBhcnJhbmdlKGRhdGUpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF0ZSwKICAgICAgICAgICAgICAgICAgIHkgPSByZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSByZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuMjUsIGdyb3VwID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAnKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiUmVnaXN0cmF0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgClRoZSB0b3RhbCBudW1iZXIgb2YgdXNlcnMgcmVnaXN0ZXJlZCBpbiB0aGlzIGNhbXBhaWduIGlzIGByIHt0b3RhbF9yZWdpc3RlcmVkfWAuCgoKIyMjIDIuMiBVc2VyIFJlZ2lzdHJhdGlvbnMgcGVyIENhbXBhaWduIENoYW5uZWwKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDh9CnBGcmFtZSA8LSBkUyAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgY2FtcGFpZ24pICU+JSAKICBzdW1tYXJpc2UocmVnaXN0cmF0aW9ucyA9IHN1bSh1c2VyaWQpKSAlPiUgCiAgYXJyYW5nZShkYXRlKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gcmVnaXN0cmF0aW9ucywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gcmVnaXN0cmF0aW9ucywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjI1LCBncm91cCA9IDEpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIGZhY2V0X3dyYXAofmNhbXBhaWduLCBuY29sID0gMikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdXaWtpcGVkaWEgQ2hhbGxlbmdlIE9jdG9iZXIgMjAyMCcpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJSZWdpc3RyYXRpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICAjIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDYpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKIyMjIDIuMyBUb3RhbCBVc2VyIFJlZ2lzdHJhdGlvbnMgcGVyIENhbXBhaWduIENoYW5uZWwKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNX0KcEZyYW1lIDwtIGRTICU+JSAKICBzZWxlY3QoY2FtcGFpZ24sIHVzZXJpZCkgJT4lIAogIGdyb3VwX2J5KCBjYW1wYWlnbikgJT4lIAogIHN1bW1hcmlzZShyZWdpc3RyYXRpb25zID0gc3VtKHVzZXJpZCkpICU+JSAKICBhcnJhbmdlKGRlc2MocmVnaXN0cmF0aW9ucykpCnBGcmFtZSRjYW1wYWlnbiA8LSBmYWN0b3IocEZyYW1lJGNhbXBhaWduLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBwRnJhbWUkY2FtcGFpZ24sIAogICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBUKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGNhbXBhaWduLAogICAgICAgICAgICAgICAgICAgeSA9IHJlZ2lzdHJhdGlvbnMsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHJlZ2lzdHJhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgY29sb3IgPSAiYmx1ZSIsIGZpbGwgPSAid2hpdGUiKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ1dpa2lwZWRpYSBDaGFsbGVuZ2UgT2N0b2JlciAyMDIwJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIlJlZ2lzdHJhdGlvbnMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKIyMgMy4gVXNlciBFZGl0cwoKVGhpcyBzZWN0aW9uIHByZXNlbnRzIGFsbCBkYXRhIGFuZCBzdGF0aXN0aWNzIG9uIHRoZSB1c2VyIGVkaXRzLgoKIyMjIDMuMSBVc2VyIEVkaXRzIE92ZXJ2aWV3CgpUaGUgZm9sbG93aW5nIGNodW5rIGxvYWRzIHRoZSBkYXRhc2V0IG9mIHVzZXIgaW50ZXJhY3Rpb25zIHdpdGggY2FtcGFpZ24gY2hhbm5lbHMgYW5kIHRoZW4gcmUtc3RydWN0dXJlcyB0aGUgZGF0YXNldCBhIGJpdC4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpsRiA8LSBsaXN0LmZpbGVzKCJfZGFpbHlSZXBvcnRpbmciKQpsRiA8LSBsRltncmVwbCgiXnVzZXJFZGl0cyIsIGxGKV0KZWRpdFNldCA8LSBmcmVhZChwYXN0ZTAoIl9kYWlseVJlcG9ydGluZy8iLCBsRikpCmVkaXRTZXQgPC0gc2VsZWN0KGVkaXRTZXQsCiAgICAgICAgICAgICAgICAgIGFjdG9yX25hbWUsIAogICAgICAgICAgICAgICAgICByZXZhY3Rvcl90aW1lc3RhbXApICAgICAgICAgICAgICAgICAKY29sbmFtZXMoZWRpdFNldCkgPC0gYygndXNlcm5hbWUnLCAncmV2X3RpbWVzdGFtcCcpCmRhdGFTZXQgPC0gbGVmdF9qb2luKGVkaXRTZXQsCiAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChyZWdVc2VycywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2VybmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduKSwKICAgICAgICAgICAgICAgICAgICAgYnkgPSAidXNlcm5hbWUiKQpkYXRhU2V0IDwtIGRhdGFTZXRbY29tcGxldGUuY2FzZXMoZGF0YVNldCksIF0KZGF0YVNldCR5ZWFyIDwtIHN1YnN0cihkYXRhU2V0JHJldl90aW1lc3RhbXAsIDEsIDQpCmRhdGFTZXQkbW9udGggPC0gc3Vic3RyKGRhdGFTZXQkcmV2X3RpbWVzdGFtcCwgNSwgNikKZGF0YVNldCRkYXkgPC0gc3Vic3RyKGRhdGFTZXQkcmV2X3RpbWVzdGFtcCwgNywgOCkKZGF0YVNldCRyZXZfdGltZXN0YW1wIDwtIE5VTEwKZGF0YVNldCRkYXRlIDwtIHBhc3RlKGRhdGFTZXQkeWVhciwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhU2V0JG1vbnRoLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGFTZXQkZGF5LAogICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICItIikKZGF0YVNldCA8LSBzZWxlY3QoZGF0YVNldCwKICAgICAgICAgICAgICAgICAgZGF0ZSwKICAgICAgICAgICAgICAgICAgdXNlcm5hbWUsCiAgICAgICAgICAgICAgICAgIGNhbXBhaWduKQpkYXRlU3BhbiA8LSBzZXEoZnJvbSA9IGFzLkRhdGUoIjIwMjAtMTAtMDEiKSwgCiAgICAgICAgICAgICAgICB0byA9IGFzLkRhdGUobWF4KGRhdGFTZXQkZGF0ZSkpLCAKICAgICAgICAgICAgICAgIGJ5ID0gImRheSIpCmRhdGVTcGFuIDwtIGFzLmNoYXJhY3RlcihkYXRlU3BhbikKZFMgPC0gZXhwYW5kLmdyaWQoZGF0ZVNwYW4sIAogICAgICAgICAgICAgICAgICB1bmlxdWUoZGF0YVNldCRjYW1wYWlnbiksCiAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhkUykgPC0gYygnZGF0ZScsICdjYW1wYWlnbicpCmRTIDwtIGRTICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KGRhdGFTZXQsIGRhdGUsIGNhbXBhaWduLCB1c2VybmFtZSksCiAgICAgICAgICAgIGJ5ID0gYygiZGF0ZSIsICJjYW1wYWlnbiIpKQpkUyR1c2VybmFtZSA8LSBpZmVsc2UoaXMubmEoZFMkdXNlcm5hbWUpLCAwLCAxKQpwRnJhbWUgPC0gZFMgJT4lCiAgc2VsZWN0KGRhdGUsIHVzZXJuYW1lKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IHN1bSh1c2VybmFtZSkpICU+JSAKICBhcnJhbmdlKGRhdGUpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF0ZSwKICAgICAgICAgICAgICAgICAgIHkgPSBlZGl0cywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZWRpdHMsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSwgZ3JvdXAgPSAxKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdXaWtpcGVkaWEgQ2hhbGxlbmdlIE9jdG9iZXIgMjAyMCcpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJFZGl0cyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyMgMy4yIFVzZXIgRWRpdHMgYnkgQ2FtcGFpZ24gQ2hhbm5lbHMKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDh9CnBGcmFtZSA8LSBkUyAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgY2FtcGFpZ24pICU+JSAKICBzdW1tYXJpc2UoZWRpdHMgPSBzdW0odXNlcm5hbWUpKSAlPiUgCiAgYXJyYW5nZShkYXRlKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gZWRpdHMsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGVkaXRzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuMjUsIGdyb3VwID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgZmFjZXRfd3JhcCh+Y2FtcGFpZ24sIG5jb2wgPSAyKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ1dpa2lwZWRpYSBDaGFsbGVuZ2UgT2N0b2JlciAyMDIwJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIkVkaXRzIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICAjIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDUuNSkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyMgMy4zIFRvdGFsIFVzZXIgRWRpdHMgYnkgQ2FtcGFpZ24gQ2hhbm5lbHMKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNX0KcEZyYW1lIDwtIGRTICU+JSAKICBzZWxlY3QoY2FtcGFpZ24sIHVzZXJuYW1lKSAlPiUgCiAgZ3JvdXBfYnkoIGNhbXBhaWduKSAlPiUgCiAgc3VtbWFyaXNlKGVkaXRzID0gc3VtKHVzZXJuYW1lKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhlZGl0cykpCnBGcmFtZSRjYW1wYWlnbiA8LSBmYWN0b3IocEZyYW1lJGNhbXBhaWduLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBwRnJhbWUkY2FtcGFpZ24sIAogICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBUKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGNhbXBhaWduLAogICAgICAgICAgICAgICAgICAgeSA9IGVkaXRzLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBlZGl0cywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvciA9ICJibHVlIiwgZmlsbCA9ICJ3aGl0ZSIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAnKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiRWRpdHMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKIyMjIDMuNCBFZGl0IENsYXNzZXMKCiMjIyMgMy40LjEgRWRpdCBDbGFzc2VzOiBhbGwgdXNlcnMKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQp1c2VyQ2xhc3MgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KHVzZXJuYW1lKSAlPiUgCiAgZ3JvdXBfYnkodXNlcm5hbWUpICU+JSAKICBzdW1tYXJpc2UoZWRpdHMgPSBuKCkpCmVkaXRCb3VuZGFyaWVzIDwtIGxpc3QoCiAgYygwLCAxKSwgCiAgYygyLCA0KSwKICBjKDUsIDkpLAogIGMoMTAsIDIwKSwKICBjKDIxLCA1MCksCiAgYyg1MSwgMTAwKQopCnVzZXJDbGFzcyRlZGl0Q2xhc3MgPC0gc2FwcGx5KHVzZXJDbGFzcyRlZGl0cywgZnVuY3Rpb24oeCkgewogIHdFQyA8LSBzYXBwbHkoZWRpdEJvdW5kYXJpZXMsIGZ1bmN0aW9uKHkpIHsKICAgIHggPj0geVsxXSAmIHggPD0geVsyXQogIH0pCiAgaWYgKHN1bSh3RUMpID09IDApIHsKICAgIHJldHVybigiPiAxMDAiKQogIH0gZWxzZSB7CiAgICByZXR1cm4ocGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsxXSwKICAgICAgICAgICAgICAgICAgIiAtICIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMl0sIAogICAgICAgICAgICAgICAgICAiKSIKICAgICAgICAgICAgICAgICAgKQogICAgKQogIH0KfSkKZWRpdENsYXNzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUodXNlckNsYXNzJGVkaXRDbGFzcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXMoZWRpdENsYXNzKSA8LSBjKCdFZGl0IENsYXNzJywgJ051bS5Vc2VycycpCmVkaXRDbGFzcyRvcmRlciA8LSBhcy5udW1lcmljKHNhcHBseShlZGl0Q2xhc3MkYEVkaXQgQ2xhc3NgLCBmdW5jdGlvbih4KSB7CiAgbG93ZXIgPC0gc3RyX2V4dHJhY3QoeCwgJ1tbOmRpZ2l0Ol1dKycpCn0pKQplZGl0Q2xhc3MgPC0gYXJyYW5nZShlZGl0Q2xhc3MsIG9yZGVyKQplZGl0Q2xhc3Mkb3JkZXIgPC0gTlVMTAplZGl0Q2xhc3MkYEVkaXQgQ2xhc3NgW2VkaXRDbGFzcyRgRWRpdCBDbGFzc2AgPT0gJygwIC0gMSknXSA8LSAnKDEpJwpkYXRhdGFibGUoZWRpdENsYXNzKQpgYGAKCiMjIyMgMy40LjIgRWRpdCBDbGFzc2VzIHBlciBDYW1wYWlnbiBDaGFubmVscwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CmVkQ2xhc3NlcyA8LSBsYXBwbHkodW5pcXVlKGRhdGFTZXQkY2FtcGFpZ24pLCBmdW5jdGlvbih4KSB7CiAgdXNlckNsYXNzIDwtIGRhdGFTZXQgJT4lIAogIGZpbHRlcihjYW1wYWlnbiA9PSB4KSAlPiUgCiAgc2VsZWN0KHVzZXJuYW1lKSAlPiUgCiAgZ3JvdXBfYnkodXNlcm5hbWUpICU+JSAKICBzdW1tYXJpc2UoZWRpdHMgPSBuKCkpCmVkaXRCb3VuZGFyaWVzIDwtIGxpc3QoCiAgYygwLCAxKSwgCiAgYygyLCA0KSwKICBjKDUsIDkpLAogIGMoMTAsIDIwKSwKICBjKDIxLCA1MCksCiAgYyg1MSwgMTAwKQopCnVzZXJDbGFzcyRlZGl0Q2xhc3MgPC0gc2FwcGx5KHVzZXJDbGFzcyRlZGl0cywgZnVuY3Rpb24oeCkgewogIHdFQyA8LSBzYXBwbHkoZWRpdEJvdW5kYXJpZXMsIGZ1bmN0aW9uKHkpIHsKICAgIHggPj0geVsxXSAmIHggPD0geVsyXQogIH0pCiAgaWYgKHN1bSh3RUMpID09IDApIHsKICAgIHJldHVybigiPiAxMDAiKQogIH0gZWxzZSB7CiAgICByZXR1cm4ocGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsxXSwKICAgICAgICAgICAgICAgICAgIiAtICIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMl0sIAogICAgICAgICAgICAgICAgICAiKSIKICAgICAgICAgICAgICAgICAgKQogICAgKQogIH0KfSkKZWRpdENsYXNzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUodXNlckNsYXNzJGVkaXRDbGFzcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXMoZWRpdENsYXNzKSA8LSBjKCdFZGl0IENsYXNzJywgJ051bS5Vc2VycycpCmVkaXRDbGFzcyRvcmRlciA8LSBhcy5udW1lcmljKHNhcHBseShlZGl0Q2xhc3MkYEVkaXQgQ2xhc3NgLCBmdW5jdGlvbih4KSB7CiAgbG93ZXIgPC0gc3RyX2V4dHJhY3QoeCwgJ1tbOmRpZ2l0Ol1dKycpCn0pKQplZGl0Q2xhc3MgPC0gYXJyYW5nZShlZGl0Q2xhc3MsIG9yZGVyKQplZGl0Q2xhc3Mkb3JkZXIgPC0gTlVMTAplZGl0Q2xhc3MkY2FtcGFpZ24gPC0geApyZXR1cm4oZWRpdENsYXNzKQp9KQplZENsYXNzZXMgPC0gcmJpbmRsaXN0KGVkQ2xhc3NlcykKZWRDbGFzc2VzJGNhbXBhaWduIDwtIHNhcHBseShlZENsYXNzZXMkY2FtcGFpZ24sIGZ1bmN0aW9uKHgpIHsKICBkIDwtIHN0cnNwbGl0KHgsICJfIilbWzFdXQogIGlmIChuY2hhcih0YWlsKGQsIDEpKSA9PSAxKSB7CiAgICBkW2xlbmd0aChkKV0gPC0gcGFzdGUwKCIwIiwgZFtsZW5ndGgoZCldKQogIH0KICBkIDwtIHBhc3RlKGQsIGNvbGxhcHNlID0gIl8iKQogIHJldHVybihkKQogIH0pCmVkQ2xhc3NlcyA8LSBhcnJhbmdlKGVkQ2xhc3NlcywgY2FtcGFpZ24pCmVkQ2xhc3NlcyRgRWRpdCBDbGFzc2BbZWRDbGFzc2VzJGBFZGl0IENsYXNzYCA9PSAnKDAgLSAxKSddIDwtICcoMSknCmVkQ2xhc3NlcyRgRWRpdCBDbGFzc2AgPC0gZmFjdG9yKGVkQ2xhc3NlcyRgRWRpdCBDbGFzc2AsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCcoMSknLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcoMiAtIDQpJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyg1IC0gOSknLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcoMTAgLSAyMCknLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcoMjEgLSA1MCknLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnKDUxIC0gMTAwKScpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCmdncGxvdChlZENsYXNzZXMsIAogICAgICAgYWVzKHggPSBgRWRpdCBDbGFzc2AsIAogICAgICAgICAgIHkgPSBgTnVtLlVzZXJzYCwgCiAgICAgICAgICAgbGFiZWwgPSBgTnVtLlVzZXJzYCkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJ3aGl0ZSIpICsgCiAgZmFjZXRfd3JhcCh+Y2FtcGFpZ24pICsgCiAgZ2d0aXRsZSgnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAnKSArCiAgeGxhYigiRWRpdCBDbGFzcyIpICsgeWxhYigiTnVtLnVzZXJzIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCg==