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; Report updated w. user edits data on 2020/11/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 352.

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), 
  c(101, 1000000)
)
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)'
editClass$`Edit Class`[editClass$`Edit Class` == '(101 - 1e+06)'] <- '(> 100)'
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), 
  c(101, 1000000)
)
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], 
                  ")"
                  )
    )
  }
})
userClass$editClass[userClass$editClass == '(0 - 1)'] <- '(1)'
userClass$editClass[userClass$editClass == '(101 - 1e+06)'] <- '(> 100)'
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)',
                                            '(> 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")

LS0tCnRpdGxlOiAnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAgKFByZWxpbWluYXJ5IFJlcG9ydCknCmF1dGhvcjogIkdvcmFuIFMuIE1pbG92YW5vdmljLCBEYXRhIFNjaWVudGlzdCwgV01ERSIKZGF0ZTogIk9jdG9iZXIgMjAsIDIwMjAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0aGVtZTogc2ltcGxleAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDUKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQotLS0KCioqRmVlZGJhY2sqKiBzaG91bGQgYmUgc2VuZCB0byBgZ29yYW4ubWlsb3Zhbm92aWNfZXh0QHdpa2ltZWRpYS5kZWAuIAoKVGhlIGNhbXBhaWduIHdhcyBydW4gZnJvbSAyMDIwLzEwLzAxIHRvIDIwMjAvMTAvMTEuCgoqKkNVUlJFTlQgVVBEQVRFOioqIERhdGFzZXQgYXMgb2YgMjAyMC8xMC8yNDsgUmVwb3J0IHVwZGF0ZWQgdy4gdXNlciBlZGl0cyBkYXRhIG9uIDIwMjAvMTEvMjQuIAoKYGBge3IsIGVjaG8gPSBGLCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRiwgcmVzdWx0cyA9ICdoaWRlJ30KIyAhZGlhZ25vc3RpY3Mgb2ZmCiMjIyAtLS0gU2V0dXAKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KHJtYXJrZG93bikKbGlicmFyeShrbml0cikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShEVCkKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKIyMgRGF0YSBBY3F1aXNpdG9uCgoqKk5PVEU6KiogdGhlIERhdGEgQWNxdWlzaXRpb24gY29kZSBjaHVuayBpcyBub3QgZnVsbHkgcmVwcm9kdWNpYmxlIGZyb20gdGhpcyBSZXBvcnQuIFRoZSBkYXRhIGFyZSBjb2xsZWN0ZWQgYnkgcnVubmluZyB0aGUgc2NyaXB0IGAyMDIwX1dpa2lwZWRpYUNoYWxsZW5nZV9PY3RvYmVyXzIwMjBfQ2FtcGFpZ25fUFJPRFVDVElPTi5SYCBvbiBzdGF0MTAwNy5lcWlhZC53bW5ldCwgY29sbGVjdGluZyB0aGUgZGF0YSBhcyBgLnRzdmAgYW5kIGAuY3N2YCBmaWxlcywgY29weWluZyBtYW51YWxseSwgYW5kIHByb2Nlc3NpbmcgbG9jYWxseS4gQSBkYWlseSBjcm9udGFiIGpvYiB3YXMgcnVuIGZyb20gYDIwMjAvMTAvMDFgIHRvIGAyMDIwLzEwLzExYCB0byBjb2xsZWN0IHRoZSBkYXRhIGZvciBkYWlseSByZXBvcnRpbmcuIFVzZXIgcmVnaXN0cmF0aW9ucyBhcmUgdGhlbiBjb2xsZWN0ZWQgdW50aWwgTm92ZW1iZXIgMTF0aCAyMDIwLCBhbmQgdXNlciBlZGl0cyBhcmUgY29sbGVjdGVkIHVudGlsIHRoZSBlbmQgb2YgdGhlIHllYXIuIFRoZSBkYXRhIHVzZWQgaW4gdGhpcyByZXBvcnQgYXJlIGFnZ3JlZ2F0ZXMgb2YgdGhlIGRhaWx5IGRhdGFzZXRzLCBzYW5pdGl6ZWQgYW5kIGFub255bWl6ZWQuICAgCgoqKkRBVEE6KiogKDEpIFRoZSBgZXZlbnQud21kZWJhbm5lcmludGVyYWN0aW9uc2Agc2NoZW1hIGlzIHVzZWQgZm9yIGJhbm5lciBjbGlja3MvY2xvc2luZyAoc2VlOiBbUGhhYl0oaHR0cHM6Ly9waGFicmljYXRvci53aWtpbWVkaWEub3JnL1QyNjI1MzQjNjUwMTc5MykpOyAoMikgdGhlIGB3bWYud2VicmVxdWVzdGAgdGFibGUgaXMgYSBzb3VyY2Ugb2YgYmFubmVyIGltcHJlc3Npb25zIGRhdGE7ICgzKSBwYWdldmlld3MsIHVzZXIgcmVnaXN0cmF0aW9ucywgYW5kIHVzZXIgZWRpdHMgYXJlIGNvbGxlY3RlZCBieSBmb2xsb3dpbmcgdGhlIHN0YW5kYXJkIHByb2NlZHVyZS4gCgojIyMgRGFpbHkgVXBkYXRlCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGfQoKIyMjIC0tLSBXTURFX1dpa2lwZWRpYV9DaGFsbGVuZ2VfT2N0b2Jlcl8yMDIwX0NhbXBhaWduLlBST0RVQ1RJT04uUgojIyMgLS0tIGh0dHBzOi8vcGhhYnJpY2F0b3Iud2lraW1lZGlhLm9yZy9UMjYyNTM0CgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIENhbXBhaWduIERlc2NyaXB0aW9uIGFuZCBQYXJhbWV0ZXJzCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMgLS0tIFRyYWNraW5nIFBhcnQgMTogYmFubmVyIGNhbXBhaWduCiMgLSBQbGVhc2UgcHJvdmlkZSBkYWlseSByZXBvcnRzIGR1cmluZyB0aGUgYmFubmVyIGNhbXBhaWduIChPY3RvYmVyIDFzdCAtIDExdGgpLgoKIyMjIC0tLSBUcmFja2luZyBQYXJ0IDI6IGVtYWlsaW5nIGNhbXBhaWduIFdpa2lwZWRpYSBjaGFsbGVuZ2UKIyAtIFBsZWFzZSBwcm92aWRlIGEgdHJhY2tpbmcgcmVwb3J0IGbDvHIgdGhlIFdpa2lwZWRpYSBjaGFsbGVuZ2UgZW1haWwgY2FtcGFpZ24gCiMgLSBhcyBhbHJlYWR5IGltcGxlbWVudGVkIHNlZSBodHRwczovL3BoYWJyaWNhdG9yLndpa2ltZWRpYS5vcmcvVDI1MTUyNCBmcm9tIE9jdG9iZXIgdG8gRGVjZW1iZXIuCgojIyMgLS0tVGltZWxpbmUgZm9yIHRoZSB3aG9sZSBwcm9qZWN0CiMgLSBTdGFydCBvZiB0aGUgYmFubmVyIGNhbXBhaWduOiAxLiBPY3RvYmVyIDIwMjAKIyAtIEVuZCBvZiB0aGUgYmFubmVyIGNhbXBhaWduOiAxMS4gT2N0b2JlciAyMDIwCiMgLSBUcmFja2luZyB0ZXN0OiBFbmQgb2YgU2VwdGVtYmVyCiMgLSBMYXN0IFVzZXIgc2lnbmluZyB1cDogMTEuIE9jdG9iZXIKIyAtIFByZWxpbWluYXJ5IHJlcG9ydCBmb3IgdHJhY2tpbmcgcGFydCAxOiBzb21ldGltZSB1bnRpbCBlbmQgb2YgT2N0b2JlcgojIC0gTGFzdCBVc2VyIGZpbmlzaGluZyBtYWlsaW5nIGNhbXBhaWduOiBOb3ZlbWJlciAxMXRoCiMgLSBUcmFjayBlZGl0aW5nIGJlaGF2aW9yIHNpeCB3ZWVrcyBhZnRlciBlbmQgb2YgY2FtcGFpZ246IERlY2VtYmVyIDIzCiMgLSBmaW5hbCByZXBvcnQgZm9yIHRyYWNraW5nIHBhcnQgMSBhbmQgMjogYmVnaW5uaW5nIG9mIEphbnVhcnkKCiMjIyAtLS0gQmFubmVyIENhbXBhaWduIGluZm9ybWF0aW9uCiMgLSBUaGVyZSB3aWxsIGJlIG9uZSBjYW1wYWlnbiB3aXRoIDYgZGlmZmVyZW50IGJhbm5lcnMgKGluY2x1ZGluZyBtb2JpbGUgdmVyc2lvbnMpIAojIC0gd2hpY2ggdGFyZ2V0IHJlYWRlcnMgaW4gd2lraXBlZGlhLiBCYW5uZXIgY2FtcGFpZ24gZ29hbCBhcmUgcmVnaXN0cmF0aW9ucyAKIyAtIGZvciB0aGUgZW1haWxpbmcgY2FtcGFpZ24uCgojIC0gRmluZCBhbGwgZGV0YWlscyBpbiB0aGlzIGRvY3VtZW50OiAKIyAtIGh0dHBzOi8vZG9jcy5nb29nbGUuY29tL2RvY3VtZW50L2QvMWtUbExkc3g5X3JELWs4WGNwMnlGdmI5ZnRIRHhrTW42LU1KMS01aWh0UzgvZWRpdAoKIyMjIC0tLSBDYW1wYWlnbiBUYWdzIGFuZCBMYW5kaW5ncGFnZXMKIyAtIExhbmRpbmcgUGFnZSAxOiBodHRwczovL3d3dy53aWtpbWVkaWEuZGUvd2lraXBlZGlhLWNoYWxsZW5nZQoKIyAtIEJhbm5lciAxCiMgLSA/Y2FtcGFpZ249V01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfY3RybAojIC0gaHR0cHM6Ly93d3cud2lraW1lZGlhLmRlL3dpa2lwZWRpYS1jaGFsbGVuZ2U/Y2FtcGFpZ249V01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfY3RybAojIC0gP2NhbXBhaWduPVdNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX2N0cmxfaXBhZAojIC0gaHR0cHM6Ly93d3cud2lraW1lZGlhLmRlL3dpa2lwZWRpYS1jaGFsbGVuZ2U/Y2FtcGFpZ249V01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfY3RybF9pcGFkCiMgLSA/Y2FtcGFpZ249V01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfY3RybF9tb2JpbGUKIyAtIGh0dHBzOi8vd3d3Lndpa2ltZWRpYS5kZS93aWtpcGVkaWEtY2hhbGxlbmdlP2NhbXBhaWduPVdNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX2N0cmxfbW9iaWxlCgojIC0gQmFubmVyIDIKIyAtID9jYW1wYWlnbj1XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXIKIyAtIGh0dHBzOi8vd3d3Lndpa2ltZWRpYS5kZS93aWtpcGVkaWEtY2hhbGxlbmdlP2NhbXBhaWduPVdNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX3ZhcgojIC0gP2NhbXBhaWduPVdNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX3Zhcl9pcGFkCiMgLSBodHRwczovL3d3dy53aWtpbWVkaWEuZGUvd2lraXBlZGlhLWNoYWxsZW5nZT9jYW1wYWlnbj1XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXJfaXBhZAojIC0gP2NhbXBhaWduPVdNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX3Zhcl9tb2JpbGUKIyAtIGh0dHBzOi8vd3d3Lndpa2ltZWRpYS5kZS93aWtpcGVkaWEtY2hhbGxlbmdlP2NhbXBhaWduPVdNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX3Zhcl9tb2JpbGUKCiMgLSB0YXJnZXQgZ3JvdXBzCiMgLSBCYW5uZXJzIHdpbGwgYmUgdGFyZ2V0ZWQgdG8gbm9uLWxvZ2dlZCBpbiB1c2VycyBvbmx5LgoKIyAtIFRoZSBhL2IgdGVzdCBzY29wZSBpcyBjb21wYXJpbmcgdHdvIGRpZmZlcmVudCBjbGFpbXMgaW4gdGhlIGJhbm5lciB0ZXh0CgojIC0gVHJhY2tpbmcgaXMgZGVwZW5kZW50IG9uIGV2ZW50bG9nZ2luZy4gCiMgLSBUaGUgcmVxdWlyZWQgZXZlbnQgc2NoZW1lIHdpbGwgYmUgdXNlZCBmb3IgY2xvc2luZyBjbGlja3Mgb25seS4gCiMgLSBodHRwczovL3BoYWJyaWNhdG9yLndpa2ltZWRpYS5vcmcvVDI1MDc5MQoKIyMjIC0tLSBkYWlseSByZXBvcnRpbmcgZHVyaW5nIGNhbXBhaWduCiMgLSBUaGUgZm9sbG93aW5nIGluZm9ybWF0aW9uIHNob3VsZCBiZSBpbmNsdWRlZCBpbiB0aGUgZGFpbHkgcmVwb3J0aW5nOgojIC0gaW1wcmVzc2lvbnMgcGVyIGJhbm5lcgojIC0gY2xpY2tzIHBlciBiYW5uZXIvIHBhZ2Ugdmlld3MgcGVyIGxhbmRpbmcgcGFnZQojIC0gY2xvc2luZyByYXRlIG9mIGJhbm5lcnMKCiMjIyAtLS0gbGlicmFyaWVzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkobHVicmlkYXRlKQoKIyMjIC0tLSBkaXIgc3RydWN0dXJlCmNhbXBhaWduUGF0aCA8LSAnL2hvbWUvZ29yYW5zbS9BbmFseXRpY3MvTmV3RWRpdG9ycy9DYW1wYWlnbnMvMjAyMF9XaWtpcGVkaWFfQ2hhbGxlbmdlX09jdG9iZXJfMjAyMF9DYW1wYWlnbi8nCmRhdGFEaXIgPC0gcGFzdGUwKGNhbXBhaWduUGF0aCwgIl9kYXRhLyIpCmFuYWx5dGljc0RpciA8LSBwYXN0ZTAoY2FtcGFpZ25QYXRoLCAiX2FuYWx5dGljcy8iKQojIyMgLS0tIGNhbXBhaWduIHNwZWNpZmljcwpjYW1wYWlnbk5hbWUgPC0gJ1dpa2lwZWRpYV9DaGFsbGVuZ2VfT2N0b2Jlcl8yMDIwJwoKIyMjIC0tLSBkZXRlcm1pbmUgY2V0RGF5CmNldERheSA8LSBTeXMudGltZSgpCmNldERheQphdHRyKGNldERheSwgInR6b25lIikgPC0gIkV1cm9wZS9CZXJsaW4iCiMgLSBvbmUgZGF5IGJlaGluZCBmb3IgY3JvbnRhYgojIC0gKGkuZS4gd2FpdGluZyBmb3Igd21mLndlYnJlcXVlc3QgdG8gY29tcGxldGUgaXMgZGF0YSBhY3F1aXNpdGlvbikKY2V0RGF5IDwtIHltZCgKICBzdHJzcGxpdChhcy5jaGFyYWN0ZXIoY2V0RGF5KSwgCiAgICAgICAgICAgc3BsaXQgPSAiICIsIAogICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXQopIC0gMQoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBCYW5uZXIgSW1wcmVzc2lvbnMKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAtIGZ1bmN0aW9uOiB3bWRlX2NvbGxlY3RfYmFubmVyX2ltcHJlc3Npb25zCndtZGVfY29sbGVjdF9iYW5uZXJfaW1wcmVzc2lvbnMgPC0gZnVuY3Rpb24odXJpX2hvc3QsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9wYXRoLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcXVlcnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpcikgewogIAogICMgLSBOT1RFOgogICMgLSBleHBlY3RlZCBmb3JtYXQgZm9yIGNldERheSBpczogWVlZWS1NTS1ERAogIAogICMgLSB0byBkYXRhRGlyCiAgc2V0d2QoZGF0YURpcikKICAKICAjIC0gbGlicmFyaWVzCiAgbGlicmFyeShzdHJpbmdyKQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSBkYXRldGltZV9jb25kaXRpb24KICBjZXRfY29uZGl0aW9uIDwtIHNlcSgKICAgIGZyb20gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAwOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIHRvID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMjM6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgYnkgPSAiaG91ciIKICApIAogIGF0dHIoY2V0X2NvbmRpdGlvbiwgInR6b25lIikgPC0gIlVUQyIKICBjZXRfY29uZGl0aW9uIDwtIGFzLmNoYXJhY3RlcihjZXRfY29uZGl0aW9uKQogIGNldF9jb25kaXRpb24gPC0gdW5saXN0KHN0cl9leHRyYWN0X2FsbChjZXRfY29uZGl0aW9uLCAiXihbWzpkaWdpdDpdXXxcXHN8LSkqIikpCiAgY2V0X3llYXJzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzFdCiAgICB9KQogIGNldF9tb250aHMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMl0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfbW9udGhzKQogIGNldF9kYXlzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzNdCiAgICB9KQogIGNldF9kYXlzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9kYXlzKQogIGNldF9ob3VycyA8LSBzYXBwbHkoc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIAogICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICB4WzJdCiAgICAgICAgICAgICAgICAgICAgICB9KQogIGNldF9ob3VycyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfaG91cnMpCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUwKAogICAgInllYXIgPSAiLCBjZXRfeWVhcnMsICIgQU5EICIsCiAgICAibW9udGggPSAiLCBjZXRfbW9udGhzLCAiIEFORCAiLAogICAgImRheSA9ICIsIGNldF9kYXlzLCAiIEFORCAiLCAKICAgICJob3VyID0gIiwgY2V0X2hvdXJzCiAgKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlKCIoIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZXRpbWVDb25kaXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpCiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9wYXRoX2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX3BhdGgpID4gMSkgewogICAgdXJpX3BhdGhfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgidXJpX3BhdGggPSAnIiwgdXJpX3BhdGgsICInIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICB1cmlfcGF0aF9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpCiAgfQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSB1cmlfaG9zdF9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9ob3N0KSA+IDEpIHsKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9ob3N0ID0gJyIsIHVyaV9ob3N0LCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX2hvc3RfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfaG9zdCA9ICciLCB1cmlfaG9zdCwgIiciKQogIH0KICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX3F1ZXJ5X2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX3F1ZXJ5KSA+IDEpIHsKICAgIHVyaV9xdWVyeV9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9xdWVyeSBMSUtFICclIiwgdXJpX3F1ZXJ5LCAiJSciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX3F1ZXJ5X2NvbmRpdGlvbiA9IHBhc3RlMCgidXJpX3F1ZXJ5IExJS0UgJyUiLCB1cmlfcXVlcnksICIlJyIpCiAgfQogIAogICMgLSBjb21wb3NlIEhpdmVRTCBxdWVyeQogIGhpdmVRdWVyeSA8LSBwYXN0ZTAoCiAgICAiVVNFIHdtZjsKICAgIFNFTEVDVCB1cmlfcXVlcnkgRlJPTSB3ZWJyZXF1ZXN0CiAgICBXSEVSRSAoIiwKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiwgIiBBTkQgIiwKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiwgIiBBTkQgIiwKICAgIHVyaV9xdWVyeV9jb25kaXRpb24sICIgQU5EICIsCiAgICAiKCIsIGRhdGV0aW1lQ29uZGl0aW9uLCAiKSIsCiAgICAiKTsiCiAgICApCiAgCiAgIyAtIHdyaXRlIGhxbAogIHdyaXRlKGhpdmVRdWVyeSwgcXVlcnlGaWxlKQogICMgLSBleGVjdXRlIGhxbCBzY3JpcHQgKyBLZXJiZXJvcyBBdXRoCiAgaGl2ZUFyZ3MgPC0gJ3N1ZG8gLXUgYW5hbHl0aWNzLXByaXZhdGVkYXRhIGtlcmJlcm9zLXJ1bi1jb21tYW5kIGFuYWx5dGljcy1wcml2YXRlZGF0YSAvdXNyL2xvY2FsL2Jpbi9iZWVsaW5lIC1mJwogIGhpdmVJbnB1dCA8LSBwYXN0ZTAocXVlcnlGaWxlLCAnID4gJywgZmlsZU5hbWUpCiAgIyAtIGNvbW1hbmQ6CiAgaGl2ZUNvbW1hbmQgPC0gcGFzdGUoaGl2ZUFyZ3MsIGhpdmVJbnB1dCkKICByZXR1cm4oCiAgICBzeXN0ZW0oY29tbWFuZCA9IGhpdmVDb21tYW5kLCB3YWl0ID0gVFJVRSkKICApCn0KCiMgLSBzZXQgcGFyYW1zIHRvIHdtZGVfY29sbGVjdF9iYW5uZXJfaW1wcmVzc2lvbnMKIyAtIGZvciB0aGUgQXV0dW1uIEJhbm5lciBDYW1wYWlnbiAyMDE5CnVyaV9ob3N0IDwtIGMoJ2RlLndpa2lwZWRpYS5vcmcnLCAnZGUubS53aWtpcGVkaWEub3JnJykKdXJpX3BhdGggIDwtICcvYmVhY29uL2ltcHJlc3Npb24nCnVyaV9xdWVyeSA8LSBjKCdXTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF9jdHJsJywgJ1dNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX2N0cmxfaXBhZCcsCiAgICAgICAgICAgICAgICdXTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF9jdHJsX21vYmlsZScsJ1dNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX3ZhcicsCiAgICAgICAgICAgICAgICdXTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXJfaXBhZCcsJ1dNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX3Zhcl9tb2JpbGUnKQpxdWVyeUZpbGUgPC0gJ3dpa2lwZWRpYV9jaGFsbGVuZ2VfT2N0MjAyMF9CYW5uZXJJbXByZXNzaW9ucy5ocWwnCmZpbGVOYW1lIDwtIHBhc3RlMChkYXRhRGlyLCAiYmFubmVySW1wcmVzc2lvbnNfIiwgY2V0RGF5LCAiLnRzdiIpCgojIC0gY29sbGVjdCBCYW5uZXIgSW1wcmVzc2lvbiBkYXRhCndtZGVfY29sbGVjdF9iYW5uZXJfaW1wcmVzc2lvbnModXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpcikKCiMgLSBmdW5jdGlvbjogd21kZV9wcm9jZXNzX2Jhbm5lcl9pbXByZXNzaW9ucwp3bWRlX3Byb2Nlc3NfYmFubmVyX2ltcHJlc3Npb25zIDwtIGZ1bmN0aW9uKGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lKSB7CiAgCiAgIyAtIHRvIGRhdGFEaXIKICBzZXR3ZChkYXRhRGlyKQogIAogICMgLSBsaWJyYXJpZXMKICBsaWJyYXJ5KHN0cmluZ3IpCiAgbGlicmFyeShkcGx5cikKICAKICAjIC0gbG9hZAogIGJhbm5lckRhdGEgPC0gcmVhZC5kZWxpbShmaWxlTmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIGNvbG5hbWVzKGJhbm5lckRhdGEpIDwtICd1cmlfcXVlcnknCiAgCiAgIyAtIGNsZWFuCiAgd1N0YXJ0IDwtIHdoaWNoKGJhbm5lckRhdGEkdXJpX3F1ZXJ5ID09ICJ1cmlfcXVlcnkiKQogIGJhbm5lckRhdGEgPC0gYmFubmVyRGF0YVsod1N0YXJ0ICsgMSk6KGRpbShiYW5uZXJEYXRhKVsxXSAtIDIpLCBdCiAgCiAgIyAtIHNwbGl0CiAgYmFubmVyRGF0YSA8LSBzdHJzcGxpdChiYW5uZXJEYXRhLCBzcGxpdCA9ICImIiwgZml4ZWQgPSBUKQogICMgLSBleHRyYWN0IHJlbGV2YW50IGZpZWxkcwogICMgLSBiYW5uZXI6CiAgYmFubmVyIDwtIHNhcHBseShiYW5uZXJEYXRhLCBmdW5jdGlvbih4KSB7CiAgICB4W3doaWNoKGdyZXBsKCJeYmFubmVyPSIsIHgpKV0KICB9KQogIGJhbm5lciA8LSBnc3ViKCJeYmFubmVyPSIsICIiLCBiYW5uZXIpCiAgIyAtIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlOgogIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlIDwtIHNhcHBseShiYW5uZXJEYXRhLCBmdW5jdGlvbih4KSB7CiAgICB4W3doaWNoKGdyZXBsKCJecmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGU9IiwgeCkpXQogIH0pCiAgcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUgPC0gYXMubnVtZXJpYygKICAgIGdzdWIoIl5yZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZT0iLCAiIiwgcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUpCiAgKQogICMgLSByZXN1bHQ6CiAgcmVzdWx0IDwtIHNhcHBseShiYW5uZXJEYXRhLCBmdW5jdGlvbih4KSB7CiAgICB4W3doaWNoKGdyZXBsKCJecmVzdWx0PSIsIHgpKV0KICB9KQogIHJlc3VsdCA8LSBnc3ViKCJecmVzdWx0PSIsICIiLCByZXN1bHQpCiAgCiAgIyAtIGNvbXBvc2UgdGFibGU6CiAgYmFubmVyT2JzZXJ2YXRpb25zIDwtIGRhdGEuZnJhbWUoYmFubmVyID0gYmFubmVyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSA9IHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSByZXN1bHQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIAogICMgLSBmaWx0ZXIgZm9yIHJlc3VsdD1zaG93CiAgYmFubmVyT2JzZXJ2YXRpb25zIDwtIGRwbHlyOjpmaWx0ZXIoYmFubmVyT2JzZXJ2YXRpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9PSAic2hvdyIpCiAgCiAgIyAtIGNvcnJlY3Rpb24gZm9yIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlCiAgYmFubmVyT2JzZXJ2YXRpb25zJHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlIDwtIAogICAgMS9iYW5uZXJPYnNlcnZhdGlvbnMkcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUKICAKICAjIC0gYWdncmVnYXRlOgogIGJhbm5lck9ic2VydmF0aW9ucyA8LSBiYW5uZXJPYnNlcnZhdGlvbnMgJT4lIAogICAgZHBseXI6OnNlbGVjdChiYW5uZXIsIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlKSAlPiUgCiAgICBkcGx5cjo6Z3JvdXBfYnkoYmFubmVyKSAlPiUgCiAgICBkcGx5cjo6c3VtbWFyaXNlKGltcHJlc3Npb25zID0gc3VtKHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlKSkKICAKICAjIC0gYWRkIGNldERheSwgbWUKICBiYW5uZXJPYnNlcnZhdGlvbnMkZGF0ZSA8LSBjZXREYXkKICBiYW5uZXJPYnNlcnZhdGlvbnMkY2FtcGFpZ24gPC0gY2FtcGFpZ25OYW1lCiAgCiAgIyAtIHN0b3JlOgogIHdyaXRlLmNzdihiYW5uZXJPYnNlcnZhdGlvbnMsIAogICAgICAgICAgICBwYXN0ZTAoImJhbm5lckltcHJlc3Npb25zQWdncmVnYXRlZF8iLAogICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICkKICApCiAgCn0KCiMgLSB3cmFuZ2xlIEJhbm5lciBJbXByZXNzaW9uIGRhdGEKd21kZV9wcm9jZXNzX2Jhbm5lcl9pbXByZXNzaW9ucyhmaWxlTmFtZSA9IGZpbGVOYW1lLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyID0gZGF0YURpciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5ID0gY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSA9IGNhbXBhaWduTmFtZSkKCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIEJhbm5lciBJbnRlcmFjdGlvbnM6CiMjIyAtLS0gdmlhIGV2ZW50LldNREVCYW5uZXJJbnRlcmFjdGlvbnMKIyMjIC0tLSBodHRwczovL21ldGEud2lraW1lZGlhLm9yZy93aWtpL1NjaGVtYTpXTURFQmFubmVySW50ZXJhY3Rpb25zCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0gc2VsZWN0IGR0LCBldmVudC5iYW5uZXJOYW1lLCBldmVudC5iYW5uZXJBY3Rpb24sIGV2ZW50LmJhbm5lckltcHJlc3Npb25zLCBldmVudC51c2VySUQgCiMgLSBmcm9tIGV2ZW50LndtZGViYW5uZXJpbnRlcmFjdGlvbnMgd2hlcmUgeWVhcj0yMDIwIGFuZCBtb250aD01IGFuZCAoZGF5PTExIG9yIGRheT0xMiBvciBkYXk9MTMpOwoKIyAtIGZ1bmN0aW9uOiB3bWRlX2NvbGxlY3RfcGFnZXZpZXdzCndtZGVfYmFubmVyX2FjdGlvbnMgPC0gZnVuY3Rpb24odXJpX3F1ZXJ5X2ZpbHRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuYWx5dGljc0RpciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUpIHsKICAKICAjIC0gTk9URToKICAjIC0gZXhwZWN0ZWQgZm9ybWF0IGZvciBjZXREYXkgaXM6IFlZWVktTU0tREQKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgZGF0ZXRpbWVfY29uZGl0aW9uCiAgY2V0X2NvbmRpdGlvbiA8LSBzZXEoCiAgICBmcm9tID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMDowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICB0byA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDIzOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIGJ5ID0gImhvdXIiCiAgKSAKICBhdHRyKGNldF9jb25kaXRpb24sICJ0em9uZSIpIDwtICJVVEMiCiAgY2V0X2NvbmRpdGlvbiA8LSBhcy5jaGFyYWN0ZXIoY2V0X2NvbmRpdGlvbikKICBjZXRfY29uZGl0aW9uIDwtIHVubGlzdChzdHJfZXh0cmFjdF9hbGwoY2V0X2NvbmRpdGlvbiwgIl4oW1s6ZGlnaXQ6XV18XFxzfC0pKiIpKQogIGNldF95ZWFycyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsxXQogICAgfSkKICBjZXRfbW9udGhzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzJdCiAgICB9KQogIGNldF9tb250aHMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X21vbnRocykKICBjZXRfZGF5cyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVszXQogICAgfSkKICBjZXRfZGF5cyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfZGF5cykKICBjZXRfaG91cnMgPC0gc2FwcGx5KHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCAKICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgeFsyXQogICAgICAgICAgICAgICAgICAgICAgfSkKICBjZXRfaG91cnMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2hvdXJzKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlMCgKICAgICJ5ZWFyID0gIiwgY2V0X3llYXJzLCAiIEFORCAiLAogICAgIm1vbnRoID0gIiwgY2V0X21vbnRocywgIiBBTkQgIiwKICAgICJkYXkgPSAiLCBjZXRfZGF5cywgIiBBTkQgIiwgCiAgICAiaG91ciA9ICIsIGNldF9ob3VycwogICkKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZSgiKCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGV0aW1lQ29uZGl0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQogIAogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSBldmVudEJhbm5lck5hbWVfY29uZGl0aW9uCiAgaWYgKGxlbmd0aCh1cmlfcXVlcnkpID4gMSkgewogICAgZXZlbnRCYW5uZXJOYW1lX2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiZXZlbnQuYmFubmVyTmFtZSBMSUtFICclIiwgdXJpX3F1ZXJ5X2ZpbHRlciwgIiUnIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIGV2ZW50QmFubmVyTmFtZV9jb25kaXRpb24gPSBwYXN0ZTAoImV2ZW50LmJhbm5lck5hbWUgTElLRSAnJSIsIHVyaV9xdWVyeV9maWx0ZXIsICIlJyIpCiAgfQogIAogICMgLSBjb21wb3NlIEhpdmVRTCBxdWVyeQogIGhpdmVRdWVyeSA8LSBwYXN0ZTAoIAogICAgInNlbGVjdCBkdCwgZXZlbnQuYmFubmVyTmFtZSwgZXZlbnQuYmFubmVyQWN0aW9uLCBldmVudC5iYW5uZXJJbXByZXNzaW9ucywgZXZlbnQudXNlcklEIGZyb20gZXZlbnQud21kZWJhbm5lcmludGVyYWN0aW9ucyAKICAgIFdIRVJFICgiLAogICAgZXZlbnRCYW5uZXJOYW1lX2NvbmRpdGlvbiwgIiBBTkQgIiwKICAgICIoIiwgZGF0ZXRpbWVDb25kaXRpb24sICIpIiwKICAgICIpOyIKICAgICkKICAKICAjIC0gd3JpdGUgaHFsCiAgd3JpdGUoaGl2ZVF1ZXJ5LCBxdWVyeUZpbGUpCiAgIyAtIGV4ZWN1dGUgaHFsIHNjcmlwdDoKICBrZXJiZXJvc1ByZWZpeCA8LSAKICAgICdzdWRvIC11IGFuYWx5dGljcy1wcml2YXRlZGF0YSBrZXJiZXJvcy1ydW4tY29tbWFuZCBhbmFseXRpY3MtcHJpdmF0ZWRhdGEgJwogICMgLSBLZXJiZXJvcyBpbml0CiAgc3lzdGVtKGNvbW1hbmQgPSBwYXN0ZTAoa2VyYmVyb3NQcmVmaXgsICcgaGRmcyBkZnMgLWxzJyksIAogICAgICAgICB3YWl0ID0gVCkKICAjIC0gUnVuIHF1ZXJ5CiAgcXVlcnkgPC0gc3lzdGVtKGNvbW1hbmQgPSBwYXN0ZShrZXJiZXJvc1ByZWZpeCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnL3Vzci9sb2NhbC9iaW4vYmVlbGluZSAtLWluY3JlbWVudGFsPXRydWUgLS1zaWxlbnQgLWYgIicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoZGF0YURpciwgcXVlcnlGaWxlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICciID4gJywgZGF0YURpciwgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIiksCiAgICAgICAgICAgICAgICAgIHdhaXQgPSBUUlVFKQogIAogICMgLSBXcmFuZ2xlIEJhbm5lciBJbnRlcmFjdGlvbnMKICAjIC0gbG9hZAogIGJhbm5lckRhdGEgPC0gdHJ5Q2F0Y2goewogICAgYXMuZGF0YS5mcmFtZShmcmVhZChwYXN0ZTAoZGF0YURpciwgZmlsZU5hbWUpKSkKICB9LAogIGVycm9yID0gZnVuY3Rpb24oY29uZGl0aW9uKSB7CiAgICByZXR1cm4oRkFMU0UpCiAgfSkKICAKICAjIC0gcHJvY2VzcwogIGlmIChjbGFzcyhiYW5uZXJEYXRhKSA9PSAnbG9naWNhbCcpIHsKICAgIHJldHVybihGQUxTRSkgCiAgfSBlbHNlIHsgCiAgICAjIC0gYmFubmVyU2VlbgogICAgYmFubmVyU2VlbiA8LSBiYW5uZXJEYXRhICU+JSAKICAgICAgZHBseXI6OnNlbGVjdChiYW5uZXJuYW1lLCB1c2VyaWQpCiAgICBiYW5uZXJTZWVuIDwtIGJhbm5lclNlZW5bIWR1cGxpY2F0ZWQoYmFubmVyU2VlbiksIF0KICAgIGJhbm5lclNlZW4gPC0gYmFubmVyU2VlbiAlPiUgCiAgICAgIGRwbHlyOjpzZWxlY3QoYmFubmVybmFtZSkgJT4lIAogICAgICBkcGx5cjo6Z3JvdXBfYnkoYmFubmVybmFtZSkgJT4lIAogICAgICBkcGx5cjo6c3VtbWFyaXNlKHNlZW5fYnkgPSBuKCkpCiAgICAjIC0gYmFubmVyQ2xvc2VkCiAgICBiYW5uZXJDbG9zZWQgPC0gYmFubmVyRGF0YSAlPiUgCiAgICAgIGRwbHlyOjpmaWx0ZXIoYmFubmVyYWN0aW9uID09ICJiYW5uZXItY2xvc2VkIikgJT4lIAogICAgICBkcGx5cjo6c2VsZWN0KGJhbm5lcm5hbWUsIGJhbm5lcmltcHJlc3Npb25zKSAlPiUgCiAgICAgIGRwbHlyOjpncm91cF9ieShiYW5uZXJuYW1lKSAlPiUKICAgICAgZHBseXI6OnN1bW1hcmlzZShjbG9zZWRfYnkgPSBuKCksIG1lYW5fY2xvc2VfaW1wID0gcm91bmQobWVhbihiYW5uZXJpbXByZXNzaW9ucyksIDIpKQogICAgIyAtIGJhbm5lckNsaWNrZWQKICAgIGJhbm5lckNsaWNrZWQgPC0gYmFubmVyRGF0YSAlPiUgCiAgICAgIGRwbHlyOjpmaWx0ZXIoYmFubmVyYWN0aW9uID09ICJiYW5uZXItY2xpY2tlZCIpICU+JSAKICAgICAgZHBseXI6OnNlbGVjdChiYW5uZXJuYW1lLCBiYW5uZXJpbXByZXNzaW9ucykgJT4lIAogICAgICBkcGx5cjo6Z3JvdXBfYnkoYmFubmVybmFtZSkgJT4lCiAgICAgIGRwbHlyOjpzdW1tYXJpc2UoY2xpY2tlZF9ieSA9IG4oKSwgbWVhbl9jbGlja19pbXAgPSByb3VuZChtZWFuKGJhbm5lcmltcHJlc3Npb25zKSwgMikpCiAgICAjIC0gd2hvQ2xpY2tlZAogICAgd2hvQ2xpY2tlZCA8LSBiYW5uZXJEYXRhICU+JSAKICAgICAgZHBseXI6OmZpbHRlcihiYW5uZXJhY3Rpb24gPT0gImJhbm5lci1jbGlja2VkIikKICAgIHdob0NsaWNrZWQgPC0gZGF0YS5mcmFtZSh1c2VyaWQgPSB1bmlxdWUod2hvQ2xpY2tlZCR1c2VyaWQpKQogICAgIyAtIHN0b3JlOgogICAgd3JpdGUuY3N2KHdob0NsaWNrZWQsIAogICAgICAgICAgICAgIHBhc3RlMChhbmFseXRpY3NEaXIsIAogICAgICAgICAgICAgICAgICAgICAid2hvQ2xpY2tlZF8iLAogICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdCgKICAgICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdChmaWxlTmFtZSwgc3BsaXQgPSAiXyIsIGZpeGVkID0gVClbWzFdXVsyXSwKICAgICAgICAgICAgICAgICAgICAgICBzcGxpdCA9ICIuIiwgCiAgICAgICAgICAgICAgICAgICAgICAgZml4ZWQgPSBUKVtbMV1dWzFdLAogICAgICAgICAgICAgICAgICAgICAiLmNzdiIKICAgICAgICAgICAgICApCiAgICApCiAgICAjIC0gam9pbgogICAgYmFubmVyRGF0YSA8LSBiYW5uZXJTZWVuICU+JSAKICAgICAgZHBseXI6OmxlZnRfam9pbihiYW5uZXJDbG9zZWQsICdiYW5uZXJuYW1lJykgJT4lIAogICAgICBkcGx5cjo6bGVmdF9qb2luKGJhbm5lckNsaWNrZWQsICdiYW5uZXJuYW1lJykKICAgIGJhbm5lckRhdGEkY2xvc2VfcmF0ZSA8LSByb3VuZChiYW5uZXJEYXRhJGNsb3NlZF9ieS9iYW5uZXJEYXRhJHNlZW5fYnksIDIpCiAgICBiYW5uZXJEYXRhJGNsaWNrX3JhdGUgPC0gcm91bmQoYmFubmVyRGF0YSRjbGlja2VkX2J5L2Jhbm5lckRhdGEkc2Vlbl9ieSwgMikKICAgICMgLSBkYXRlLCBjYW1wYWlnbgogICAgYmFubmVyRGF0YSRkYXkgPC0gY2V0RGF5CiAgICBiYW5uZXJEYXRhJGNhbXBhaWduIDwtIGNhbXBhaWduTmFtZQogICAgCiAgICAjIC0gc3RvcmU6CiAgICB3cml0ZS5jc3YoYmFubmVyRGF0YSwgCiAgICAgICAgICAgICAgcGFzdGUwKGFuYWx5dGljc0RpciwgCiAgICAgICAgICAgICAgICAgICAgICJiYW5uZXJJbnRlcmFjdGlvbnNBZ2dyZWdhdGVkXyIsCiAgICAgICAgICAgICAgICAgICAgIHN0cnNwbGl0KAogICAgICAgICAgICAgICAgICAgICAgIHN0cnNwbGl0KGZpbGVOYW1lLCBzcGxpdCA9ICJfIiwgZml4ZWQgPSBUKVtbMV1dWzJdLAogICAgICAgICAgICAgICAgICAgICAgIHNwbGl0ID0gIi4iLCAKICAgICAgICAgICAgICAgICAgICAgICBmaXhlZCA9IFQpW1sxXV1bMV0sCiAgICAgICAgICAgICAgICAgICAgICIuY3N2IgogICAgICAgICAgICAgICkKICAgICkKICB9CiAgCn0KCiMgLSBzZXQgcGFyYW1zIGZvciB3bWRlX2Jhbm5lcl9hY3Rpb25zKCkKcXVlcnlGaWxlIDwtIHBhc3RlMChjYW1wYWlnbk5hbWUsICJfYmFubmVySW50ZXJhY3Rpb25zLmhxbCIpCmZpbGVOYW1lIDwtIHBhc3RlMCgiYmFubmVySW50ZXJhY3Rpb25zXyIsIGNldERheSwgIi50c3YiKQp1cmlfcXVlcnlfZmlsdGVyIDwtICdXTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF8nCmJhbm5lcl9zdGF0dXMgPC0gd21kZV9iYW5uZXJfYWN0aW9ucyh1cmlfcXVlcnlfZmlsdGVyID0gdXJpX3F1ZXJ5X2ZpbHRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSA9IHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lID0gZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmFseXRpY3NEaXIgPSBhbmFseXRpY3NEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lID0gY2FtcGFpZ25OYW1lKQoKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gUGFnZXZpZXdzCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0gZnVuY3Rpb246IHdtZGVfY29sbGVjdF9wYWdldmlld3MKd21kZV9jb2xsZWN0X3BhZ2V2aWV3cyA8LSBmdW5jdGlvbih1cmlfaG9zdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcGF0aCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIpIHsKICAKICAjIC0gTk9URToKICAjIC0gZXhwZWN0ZWQgZm9ybWF0IGZvciBjZXREYXkgaXM6IFlZWVktTU0tREQKICAKICAjIC0gdG8gZGF0YURpcgogIHNldHdkKGRhdGFEaXIpCiAgCiAgIyAtIGxpYnJhcmllcwogIGxpYnJhcnkoc3RyaW5ncikKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgZGF0ZXRpbWVfY29uZGl0aW9uCiAgY2V0X2NvbmRpdGlvbiA8LSBzZXEoCiAgICBmcm9tID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMDowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICB0byA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDIzOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIGJ5ID0gImhvdXIiCiAgKSAKICBhdHRyKGNldF9jb25kaXRpb24sICJ0em9uZSIpIDwtICJVVEMiCiAgY2V0X2NvbmRpdGlvbiA8LSBhcy5jaGFyYWN0ZXIoY2V0X2NvbmRpdGlvbikKICBjZXRfY29uZGl0aW9uIDwtIHVubGlzdChzdHJfZXh0cmFjdF9hbGwoY2V0X2NvbmRpdGlvbiwgIl4oW1s6ZGlnaXQ6XV18XFxzfC0pKiIpKQogIGNldF95ZWFycyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsxXQogICAgfSkKICBjZXRfbW9udGhzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzJdCiAgICB9KQogIGNldF9tb250aHMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X21vbnRocykKICBjZXRfZGF5cyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVszXQogICAgfSkKICBjZXRfZGF5cyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfZGF5cykKICBjZXRfaG91cnMgPC0gc2FwcGx5KHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCAKICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgeFsyXQogICAgICAgICAgICAgICAgICAgICAgfSkKICBjZXRfaG91cnMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2hvdXJzKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlMCgKICAgICJ5ZWFyID0gIiwgY2V0X3llYXJzLCAiIEFORCAiLAogICAgIm1vbnRoID0gIiwgY2V0X21vbnRocywgIiBBTkQgIiwKICAgICJkYXkgPSAiLCBjZXRfZGF5cywgIiBBTkQgIiwgCiAgICAiaG91ciA9ICIsIGNldF9ob3VycwogICkKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZSgiKCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGV0aW1lQ29uZGl0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSB1cmlfaG9zdF9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9ob3N0KSA+IDEpIHsKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgidXJpX2hvc3QgPSAnIiwgdXJpX2hvc3QsICInIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiA9IHBhc3RlMCgidXJpX2hvc3QgPSAnIiwgdXJpX2hvc3QsICInIikKICB9CiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9wYXRoX2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX3BhdGgpID4gMSkgewogICAgdXJpX3BhdGhfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgidXJpX3BhdGggPSAnIiwgdXJpX3BhdGgsICInIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICB1cmlfcGF0aF9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpCiAgfQogIAogICMgLSBjb21wb3NlIEhpdmVRTCBxdWVyeQogIGhpdmVRdWVyeSA8LSBwYXN0ZTAoIAogICAgIlVTRSB3bWY7CiAgICBTRUxFQ1QgdXJpX2hvc3QsIHVyaV9wYXRoLCB1cmlfcXVlcnksIHJlZmVyZXIgRlJPTSB3ZWJyZXF1ZXN0CiAgICBXSEVSRSAoIiwKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiwgIiBBTkQgIiwKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiwgIiBBTkQgIiwKICAgICIoIiwgZGF0ZXRpbWVDb25kaXRpb24sICIpIiwKICAgICIpOyIKICAgICkKICAKICAjIC0gd3JpdGUgaHFsCiAgd3JpdGUoaGl2ZVF1ZXJ5LCBxdWVyeUZpbGUpCiAgIyAtIGV4ZWN1dGUgaHFsIHNjcmlwdDoKICBrZXJiZXJvc1ByZWZpeCA8LSAKICAgICdzdWRvIC11IGFuYWx5dGljcy1wcml2YXRlZGF0YSBrZXJiZXJvcy1ydW4tY29tbWFuZCBhbmFseXRpY3MtcHJpdmF0ZWRhdGEgJwogICMgLSBLZXJiZXJvcyBpbml0CiAgc3lzdGVtKGNvbW1hbmQgPSBwYXN0ZTAoa2VyYmVyb3NQcmVmaXgsICcgaGRmcyBkZnMgLWxzJyksIAogICAgICAgICB3YWl0ID0gVCkKICAjIC0gUnVuIHF1ZXJ5CiAgcXVlcnkgPC0gc3lzdGVtKGNvbW1hbmQgPSBwYXN0ZShrZXJiZXJvc1ByZWZpeCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnL3Vzci9sb2NhbC9iaW4vYmVlbGluZSAtLWluY3JlbWVudGFsPXRydWUgLS1zaWxlbnQgLWYgIicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoZGF0YURpciwgcXVlcnlGaWxlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICciID4gJywgZGF0YURpciwgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIiksCiAgICAgICAgICAgICAgICAgIHdhaXQgPSBUUlVFKQp9CgojIC0gc2V0IHBhcmFtcyB0byB3bWRlX2NvbGxlY3RfcGFnZXZpZXdzCnVyaV9ob3N0IDwtIGMoJ2RlLndpa2lwZWRpYS5vcmcnLCAnZGUubS53aWtpcGVkaWEub3JnJykKdXJpX3BhdGggIDwtIGMoCiAgJy93aWtpLygyNzQzMDEpX1dpa2lwZWRpYT90b3VyPWVpbmZ1aHJ1bmcnLAogICcvd2lraS9TcGV6aWFsOkJlbnV0emVya29udG9fYW5sZWdlbicsCiAgJy93aWtpL1NwZXppYWw6QW5tZWxkZW4nLAogICcvd2lraS9TcGVjaWFsOk15UGFnZS9BcnRpa2Vsd2Vya3N0YXR0JywKICAnL3dpa2kvQmVudXR6ZXI6VHJhaW5pbmdza29udG9fKFdNREUpL1dpa2lwZWRpYV9UcmFpbmluZycsCiAgJy93aWtpL1dpa2lwZWRpYTpXaWtpbWVkaWFfRGV1dHNjaGxhbmQvRGVpbkVuZ2FnZW1lbnQvTGl0ZXJhdHVyJwogICkKIyB1cmlfcXVlcnkgPC0gcGFzdGUwKCdXTURFXzIwMjBfY2hhbGxlbmdlXycsIDE6MzApCnF1ZXJ5RmlsZSA8LSAnd2lraW1lZGlhX2NoYWxsZW5nZV9PY3QyMDIwX1BhZ2V2aWV3cy5ocWwnCmZpbGVOYW1lIDwtIHBhc3RlMCgicGFnZXZpZXdzXyIsIGNldERheSwgIi50c3YiKQoKIyAtIGNvbGxlY3QgUGFnZXZpZXdzIGRhdGEKd21kZV9jb2xsZWN0X3BhZ2V2aWV3cyh1cmlfaG9zdCwKICAgICAgICAgICAgICAgICAgICAgICB1cmlfcGF0aCwKICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIpCgojIyMgLS0tIFdyYW5nbGUgUGFnZXZpZXdzCiMgLSBmdW5jdGlvbjogd21kZV9wcm9jZXNzX3BhZ2V2aWV3cwp3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzIDwtIGZ1bmN0aW9uKGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9xdWVyeV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFnZV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5ID0gY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSA9IGNhbXBhaWduTmFtZSkgewogIAogICMgLSB0byBkYXRhRGlyCiAgc2V0d2QoZGF0YURpcikKICAKICAjIC0gbGlicmFyaWVzCiAgbGlicmFyeShzdHJpbmdyKQogIGxpYnJhcnkoZHBseXIpCiAgbGlicmFyeSh0aWR5cikKICBsaWJyYXJ5KGRhdGEudGFibGUpCiAgCiAgIyAtIGxvYWQKICBwYWdldmlld3NEYXRhIDwtIHJlYWRMaW5lcyhmaWxlTmFtZSkKICB3U3RhcnQgPC0gd2hpY2goZ3JlcGwoInVyaV9ob3N0IiwgcGFnZXZpZXdzRGF0YSkpCiAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhWyh3U3RhcnQgKyAyKToobGVuZ3RoKHBhZ2V2aWV3c0RhdGEpIC0gMildCiAgcGFnZXZpZXdzRGF0YSA8LSBkYXRhLmZyYW1lKGRhdCA9IHBhZ2V2aWV3c0RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICBwYWdldmlld3NEYXRhIDwtIHNlcGFyYXRlKHBhZ2V2aWV3c0RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRvID0gYygndXJpX2hvc3QnLCAndXJpX3BhdGgnLCAndXJpX3F1ZXJ5JywgJ3JlZmVyZXInKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIpCiAgIyAtIGFwcGx5IHBhZ2VfZmlsdGVyCiAgcGFnZXZpZXdzRGF0YSRwYWdlIDwtIHBhc3RlMChwYWdldmlld3NEYXRhJHVyaV9ob3N0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhZ2V2aWV3c0RhdGEkdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWdldmlld3NEYXRhJHVyaV9xdWVyeSkKICB3RmlsdGVyIDwtIHNhcHBseShwYWdldmlld3NEYXRhJHBhZ2UsIGZ1bmN0aW9uKHgpIHsKICAgIHNhcHBseShwYWdlX2ZpbHRlciwgZnVuY3Rpb24oeSkgewogICAgICBncmVwbCh5LCB4KQogICAgfSkKICB9KQogIHdGaWx0ZXIgPC0gY29sU3Vtcyh3RmlsdGVyKQogIHdGaWx0ZXIgPC0gd2hpY2god0ZpbHRlciA+IDApCiAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhW3dGaWx0ZXIsIF0KICAjIC0gYXBwbHkgdXJpX3F1ZXJ5X2ZpbHRlcgogICMgLSBOT1RFOiBsb29raW5nIGluIGJvdGg6IHVyaV9xdWVyeSwgcmVmZXJlciAKICB3X3VyaV9xdWVyeSA8LSB3aGljaChncmVwbCh1cmlfcXVlcnlfZmlsdGVyLCBwYWdldmlld3NEYXRhJHBhZ2UpKQogIAogIGlmIChsZW5ndGgod191cmlfcXVlcnkpID4gMCkgewogICAgCiAgICAjIC0gZmlsdGVyIGZvciB3X3VyaV9xdWVyeQogICAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhW3dfdXJpX3F1ZXJ5LCBdIAogICAgCiAgICAjIC0gYWdncmVnYXRlOgogICAgcGFnZXZpZXdzRGF0YSR1cmlfcGF0aCA8LSBwYXN0ZTAocGFnZXZpZXdzRGF0YSR1cmlfaG9zdCwgcGFnZXZpZXdzRGF0YSR1cmlfcGF0aCkKICAgIHBhZ2V2aWV3c0RhdGEkdXJpX2hvc3QgPC0gTlVMTAogICAgcGFnZXZpZXdzRGF0YSRwYWdlIDwtIE5VTEwKICAgIHBhZ2V2aWV3c0RhdGEkcmVmZXJlciA8LSBOVUxMCiAgICBwYWdldmlld3NEYXRhIDwtIHBhZ2V2aWV3c0RhdGEgJT4lIAogICAgICBkcGx5cjo6c2VsZWN0KHVyaV9xdWVyeSwgdXJpX3BhdGgpICU+JSAKICAgICAgZHBseXI6Omdyb3VwX2J5KHVyaV9xdWVyeSwgdXJpX3BhdGgpICU+JSAKICAgICAgZHBseXI6OnN1bW1hcmlzZShwYWdldmlld3MgPSBuKCkpCiAgICBjb2xuYW1lcyhwYWdldmlld3NEYXRhKSA8LSBjKCdUYWcnLCAnUGFnZScsICdQYWdldmlld3MnKQogICAgCiAgICAjIC0gYWRkIGNldERheSwgY2FtcGFpZ25OYW1lCiAgICBwYWdldmlld3NEYXRhJGRhdGUgPC0gY2V0RGF5CiAgICBwYWdldmlld3NEYXRhJGNhbXBhaWduIDwtIGNhbXBhaWduTmFtZQogICAgCiAgICAjIC0gc3RvcmU6CiAgICB3cml0ZS5jc3YocGFnZXZpZXdzRGF0YSwgCiAgICAgICAgICAgICAgcGFzdGUwKGFuYWx5dGljc0RpciwgCiAgICAgICAgICAgICAgICAgICAgICJwYWdldmlld3NBZ2dyZWdhdGVkXyIsCiAgICAgICAgICAgICAgICAgICAgIHN0cnNwbGl0KAogICAgICAgICAgICAgICAgICAgICAgIHN0cnNwbGl0KGZpbGVOYW1lLCBzcGxpdCA9ICJfIiwgZml4ZWQgPSBUKVtbMV1dWzJdLAogICAgICAgICAgICAgICAgICAgICAgIHNwbGl0ID0gIi4iLCAKICAgICAgICAgICAgICAgICAgICAgICBmaXhlZCA9IFQpW1sxXV1bMV0sCiAgICAgICAgICAgICAgICAgICAgICIuY3N2IgogICAgICAgICAgICAgICkKICAgICkKICAgIAogIH0KICAKfQoKIyAtIHNldCBwYXJhbXMgdG8gd21kZV9wcm9jZXNzX3BhZ2V2aWV3cwojIC0gZm9yIHRoZSBXTURFIDIwMjBfRW1haWxDYW1wYWlnbldpa2lwZWRpYUNoYWxsZW5nZQp1cmlfcXVlcnlfZmlsdGVyIDwtICdXTURFXzIwMjBfY2hhbGxlbmdlXycKcGFnZV9maWx0ZXIgPC0gYygnZGUud2lraXBlZGlhLm9yZy93aWtpLygyNzQzMDEpX1dpa2lwZWRpYT90b3VyPWVpbmZ1aHJ1bmcnLAogICAgICAgICAgICAgICAgICdkZS53aWtpcGVkaWEub3JnL3dpa2kvU3BlemlhbDpCZW51dHplcmtvbnRvX2FubGVnZW4nLAogICAgICAgICAgICAgICAgICdkZS53aWtpcGVkaWEub3JnL3dpa2kvU3BlemlhbDpBbm1lbGRlbicsCiAgICAgICAgICAgICAgICAgJ2RlLndpa2lwZWRpYS5vcmcvd2lraS9TcGVjaWFsOk15UGFnZS9BcnRpa2Vsd2Vya3N0YXR0JywKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL0JlbnV0emVyOlRyYWluaW5nc2tvbnRvXyhXTURFKS9XaWtpcGVkaWFfVHJhaW5pbmcnLAogICAgICAgICAgICAgICAgICdkZS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9EZWluRW5nYWdlbWVudC9MaXRlcmF0dXInKQoKIyAtIHdyYW5nbGUgcGFnZXZpZXdzCndtZGVfcHJvY2Vzc19wYWdldmlld3MoZmlsZU5hbWUgPSBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyID0gZGF0YURpciwKICAgICAgICAgICAgICAgICAgICAgICB1cmlfcXVlcnlfZmlsdGVyID0gdXJpX3F1ZXJ5X2ZpbHRlciwgCiAgICAgICAgICAgICAgICAgICAgICAgcGFnZV9maWx0ZXIgPSBwYWdlX2ZpbHRlciwKICAgICAgICAgICAgICAgICAgICAgICBjZXREYXkgPSBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lID0gY2FtcGFpZ25OYW1lKQoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBVc2VyIFJlZ2lzdHJhdGlvbnMKIyMjIC0tLSBIaXZlUUwgcXVlcnk6IGV2ZW50LlNlcnZlclNpZGVBY2NvdW50Q3JlYXRpb24gdGFibGUKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KaGl2ZVFMIDwtCiAgIlNFTEVDVCB5ZWFyLCBtb250aCwgZGF5LCBob3VyLCAgZXZlbnQuY2FtcGFpZ24sIGV2ZW50LnVzZXJJZCwgZXZlbnQudXNlck5hbWUsIHdlYmhvc3QsIHdpa2kKICAgICAgRlJPTSBldmVudC5zZXJ2ZXJzaWRlYWNjb3VudGNyZWF0aW9uCiAgICAgIFdIRVJFCiAgICAgICAgeWVhciA9IDIwMjAKICAgICAgICBBTkQgKG1vbnRoID0gMTAgT1IgbW9udGggPSAxMSkKICAgICAgICBBTkQgKGV2ZW50LmNhbXBhaWduIExJS0UgJyVXTURFXzIwMjBfY2hhbGxlbmdlXyUnKTsiCiMgLSB3cml0ZSBocWwKd3JpdGUoaGl2ZVFMLCBwYXN0ZTAoZGF0YURpciwgJ3VzZXJfcmVnaXN0cmF0aW9ucy5ocWwnKSkKIyMjIC0tLSBvdXRwdXQgZmlsZW5hbWUKZmlsZW5hbWUgPC0gcGFzdGUwKGRhdGFEaXIsICd1c2VyX3JlZ2lzdHJhdGlvbnMudHN2JykKIyMjIC0tLSBleGVjdXRlIGhxbCBzY3JpcHQ6CmhpdmVBcmdzIDwtICdzdWRvIC11IGFuYWx5dGljcy1wcml2YXRlZGF0YSBrZXJiZXJvcy1ydW4tY29tbWFuZCBhbmFseXRpY3MtcHJpdmF0ZWRhdGEgL3Vzci9sb2NhbC9iaW4vYmVlbGluZSAtLXNpbGVudCAtLWluY3JlbWVudGFsPXRydWUgLS12ZXJib3NlPWZhbHNlIC1mJwpoaXZlSW5wdXQgPC0gcGFzdGUocGFzdGUwKGRhdGFEaXIsICd1c2VyX3JlZ2lzdHJhdGlvbnMuaHFsJyksCiAgICAgICAgICAgICAgICAgICAiID4gIiwKICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lLAogICAgICAgICAgICAgICAgICAgc2VwID0gIiIpCiMgLSBjb21tYW5kOgpoaXZlQ29tbWFuZCA8LSBwYXN0ZShoaXZlQXJncywgaGl2ZUlucHV0KQpzeXN0ZW0oY29tbWFuZCA9IGhpdmVDb21tYW5kLCB3YWl0ID0gVFJVRSkKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gVXNlciBFZGl0cwojIyMgLS0tIFVzZXIgRWRpdHMgdmlhIHJldmlzaW9uX2FjdG9yX3RlbXAKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCnN0YXJ0VGltZXN0YW1wIDwtICcyMDIwMTAwMTAwMDAwMCcKIyAtIGdldCB1c2VyIGlkcwp1c2VyUmVnaXN0cmF0aW9ucyA8LSBmcmVhZChwYXN0ZTAoZGF0YURpciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICd1c2VyX3JlZ2lzdHJhdGlvbnMudHN2JykpCmRpbSh1c2VyUmVnaXN0cmF0aW9ucykKIyAtIGNsZWFuIHVwIGZyb20gdGVzdCBhY2NvdW50cwp1c2VyUmVnaXN0cmF0aW9ucyA8LSBmaWx0ZXIodXNlclJlZ2lzdHJhdGlvbnMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIWdyZXBsKCJedGVzdHxeVGVzdCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZXJSZWdpc3RyYXRpb25zJHVzZXJuYW1lKSkKZGltKHVzZXJSZWdpc3RyYXRpb25zKQpoZWFkKHVzZXJSZWdpc3RyYXRpb25zKQpyZXZfdXNlciA8LSB1c2VyUmVnaXN0cmF0aW9ucyR1c2VyaWQKIyAtIGl0ZXJhdGUgb3ZlciByZXZfdXNlcgpmb3IgKGkgaW4gMTpsZW5ndGgocmV2X3VzZXIpKSB7CiAgIyAtIFNRTCBxdWVyeQogIHNxbFF1ZXJ5IDwtIHBhc3RlKCJcIlNFTEVDVCBhY3Rvci5hY3Rvcl9pZCwgCiAgICAgICAgICAgICAgICAgICAgICAgIGFjdG9yLmFjdG9yX3VzZXIsIAogICAgICAgICAgICAgICAgICAgICAgICBhY3Rvci5hY3Rvcl9uYW1lLCAKICAgICAgICAgICAgICAgICAgICAgICAgcmV2aXNpb25fYWN0b3JfdGVtcC5yZXZhY3Rvcl90aW1lc3RhbXAgCiAgICAgICAgICAgICAgICAgICAgRlJPTSBhY3RvciAKICAgICAgICAgICAgICAgICAgICBMRUZUIEpPSU4gcmV2aXNpb25fYWN0b3JfdGVtcCBPTiAoYWN0b3IuYWN0b3JfaWQgPSByZXZpc2lvbl9hY3Rvcl90ZW1wLnJldmFjdG9yX2FjdG9yKSAKICAgICAgICAgICAgICAgICAgICBXSEVSRSAocmV2aXNpb25fYWN0b3JfdGVtcC5yZXZhY3Rvcl90aW1lc3RhbXAgPj0gMjAyMDEwMDEwMDAwMDAgCiAgICAgICAgICAgICAgICAgICAgICBBTkQgYWN0b3IuYWN0b3JfdXNlciA9ICIsIHJldl91c2VyW2ldLCAiKTtcIiIpOwogICMjIyAtLS0gb3V0cHV0IGZpbGVuYW1lCiAgZmlsZW5hbWUgPC0gcGFzdGUoZGF0YURpciwndXNlckVkaXRzJywgIl8iLCBpLCAiLnRzdiIsIHNlcCA9ICIiKQogICMjIyAtLS0gZXhlY3V0ZSBzcWwgc2NyaXB0OgogIHNxbExvZ0luUHJlIDwtIHBhc3RlMCgnL3Vzci9sb2NhbC9iaW4vYW5hbHl0aWNzLW15c3FsIGRld2lraSAtZSAnKQogIHNxbElucHV0IDwtIHBhc3RlKHNxbFF1ZXJ5LAogICAgICAgICAgICAgICAgICAgICIgPiAiLAogICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lLAogICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQogICMgLSBjb21tYW5kOgogIHNxbENvbW1hbmQgPC0gcGFzdGUoc3FsTG9nSW5QcmUsIHNxbElucHV0KQogIHN5c3RlbShjb21tYW5kID0gc3FsQ29tbWFuZCwgd2FpdCA9IFRSVUUpCiAgIyAtIHJlcG9ydAogIHByaW50KHBhc3RlMCgiRE9ORTogdXNlciAiLCBpLCAiLiIpKQp9CiMjIyAtLS0gRU5EIHJ1biBTUUwgc2NyaXB0cwojIC0gbG9hZCB1c2VyIGVkaXRzOgpsRiA8LSBsaXN0LmZpbGVzKGRhdGFEaXIpCmxGIDwtIGxGW2dyZXBsKCJedXNlckVkaXRzXyIsIGxGKV0KdXNlckVkaXRzIDwtIGxhcHBseShwYXN0ZTAoZGF0YURpciwgbEYpLCBmcmVhZCkKdXNlckVkaXRzIDwtIHJiaW5kbGlzdCh1c2VyRWRpdHMpCiMgLSBzdG9yZSB1c2VyIGVkaXRzOgp3cml0ZS5jc3YodXNlckVkaXRzLCBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAndXNlckVkaXRzLmNzdicpKQpgYGAKCiMjIDAuIENhbXBhaWduIEJhbm5lcnMKClRoaXMgc2VjdGlvbiBwcmVzZW50cyBhbGwgZGF0YSBhbmQgc3RhdGlzdGljcyBvbiB0aGUgY2FtcGFpZ24gYmFubmVycy4KVGhlIGZvbGxvd2luZyBjaHVuayBsb2FkcyBhbmQgdGhlbiByZS1zdHJ1Y3R1cmVzIHRoZSBiYW5uZXJzIGRhdGFzZXQgYSBiaXQ6CgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KbEYgPC0gbGlzdC5maWxlcygnX2RhaWx5UmVwb3J0aW5nJykKbEYgPC0gbEZbZ3JlcGwoIl5iYW5uZXJJbnRlcmFjdGlvbnMiLCBsRildCmRhdGFTZXQgPC0gbGFwcGx5KHBhc3RlMCgiX2RhaWx5UmVwb3J0aW5nLyIsIGxGKSwgZnJlYWQpCmRhdGFTZXQgPC0gcmJpbmRsaXN0KGRhdGFTZXQpCmRhdGFTZXQkVjEgPC0gTlVMTApkYXRhU2V0JGNhbXBhaWduIDwtIE5VTEwKIyAtIE5BcyB0byB6ZXJvCmRhdGFTZXRbd2hpY2goaXMubmEoZGF0YVNldCksIGFyci5pbmQgPSBUKV0gPC0gMAojIC0gYmFubmVyIGNvZGVzCmRhdGFTZXQkYmFubmVybmFtZSA8LSBzYXBwbHkoZGF0YVNldCRiYW5uZXJuYW1lLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkIDwtIHN0cnNwbGl0KHgsIHNwbGl0ID0gIl8iKVtbMV1dCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkIDwtIHBhc3RlKGRbMTo1XSwgY29sbGFwc2UgPSAiXyIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4oZCkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KQpkYXRhU2V0JGJhbm5lcm5hbWUgPC0gZ3N1YigiTkEiLCAiZGVza3RvcCIsIGRhdGFTZXQkYmFubmVybmFtZSkKZGF0YVNldCA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoYmFubmVybmFtZSwgZGF5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnkpICU+JQogIGdyb3VwX2J5KGJhbm5lcm5hbWUsIGRheSkgJT4lCiAgc3VtbWFyaXNlKGNsaWNrZWRfYnkgPSBzdW0oY2xpY2tlZF9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnkgPSBzdW0oY2xvc2VkX2J5KSkKZGF0YVNldCR2YXJfY3RybCA8LSBzYXBwbHkoZGF0YVNldCRiYW5uZXJuYW1lLCBmdW5jdGlvbih4KSB7CiAgaWYgKGdyZXBsKCJ2YXIiLCB4KSkge3JldHVybigidmFyIil9IGVsc2Uge3JldHVybigiY3RybCIpfQp9KQpkYXRhU2V0JGRldmljZSA8LSBzYXBwbHkoZGF0YVNldCRiYW5uZXJuYW1lLCBmdW5jdGlvbih4KSB7CiAgaWYgKGdyZXBsKCJtb2JpbGUiLCB4KSkgewogICAgcmV0dXJuKCJtb2JpbGUiKQogIH0gZWxzZSBpZiAoZ3JlcGwoImlwYWQiLCB4KSkgewogICAgICByZXR1cm4oImlwYWQiKQogIH0gZWxzZSB7CiAgICAgIHJldHVybigiZGVza3RvcCIpCiAgfQp9KQpgYGAKClRoZSBmb2xsb3dpbmcgZGF0YXNldCBwcmVzZW50cyB0aGUgYmFubmVyIGltcHJlc3Npb25zIGRhdGE6CgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KbEYgPC0gbGlzdC5maWxlcygnX2RhaWx5UmVwb3J0aW5nJykKbEYgPC0gbEZbZ3JlcGwoIl5iYW5uZXJJbXByZXNzaW9uc0FnZ3JlZ2F0ZWQiLCBsRildCmJpX2RhdGFTZXQgPC0gbGFwcGx5KHBhc3RlMCgiX2RhaWx5UmVwb3J0aW5nLyIsIGxGKSwgZnJlYWQpCmJpX2RhdGFTZXQgPC0gcmJpbmRsaXN0KGJpX2RhdGFTZXQpCmJpX2RhdGFTZXQkVjEgPC0gTlVMTApiaV9kYXRhU2V0JGJhbm5lciA8LSBnc3ViKCJeV01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfY3RybCQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiV01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfY3RybF9kZXNrdG9wIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBiaV9kYXRhU2V0JGJhbm5lcikKYmlfZGF0YVNldCRiYW5uZXIgPC0gZ3N1YigiXldNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX3ZhciQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiV01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfdmFyX2Rlc2t0b3AiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJpX2RhdGFTZXQkYmFubmVyKQpiaV9kYXRhU2V0JGNhbXBhaWduIDwtIE5VTEwKY29sbmFtZXMoYmlfZGF0YVNldClbMl0gPC0gJ3NlZW5fYnknCmBgYAoKSm9pbiBiYW5uZXIgaW1wcmVzc2lvbnMgZGF0YSAoYGJpX2RhdGFTZXRgKSB0byBiYW5uZXIgaW50ZXJhY3Rpb25zIGRhdGEgKGBkYXRhU2V0YCkgdG8gdXNlIGBiaV9kYXRhU2V0JHNlZW5fYnlgOgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmRhdGFTZXQgPC0gZHBseXI6OmxlZnRfam9pbihkYXRhU2V0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJpX2RhdGFTZXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoImRheSIgPSAiZGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImJhbm5lcm5hbWUiID0gImJhbm5lciIpCikKcm0oYmlfZGF0YVNldCkKYGBgCgojIyMgMC4xIEJhbm5lciBBY3Rpb25zIE92ZXJ2aWV3CgoqKkNoYXJ0IDAuMS4xKiogRGFpbHkgQmFubmVyIEltcHJlc3Npb25zLCBDbGlja3MsIGFuZCBDbG9zdXJlcywgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF5LCBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnkpICU+JSAKICBncm91cF9ieShkYXkpICU+JSAKICBzdW1tYXJpc2Uoc2Vlbl9ieSA9IHN1bShzZWVuX2J5KSwgCiAgICAgICAgICAgIGNsb3NlZF9ieSA9IHN1bShjbG9zZWRfYnkpLCAKICAgICAgICAgICAgY2xpY2tlZF9ieSA9IHN1bShjbGlja2VkX2J5KSkKcEZyYW1lJGNsb3NlX3JhdGUgPSByb3VuZChwRnJhbWUkY2xvc2VkX2J5L3BGcmFtZSRzZWVuX2J5LCAyKQpwRnJhbWUkY2xpY2tfcmF0ZSA9IHJvdW5kKHBGcmFtZSRjbGlja2VkX2J5L3BGcmFtZSRzZWVuX2J5LCAyKQpwRnJhbWUgPC0gYXJyYW5nZShwRnJhbWUsIGRheSkKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn06IHNlZW5fYnksIGNsaWNrZWRfYnksIGNsb3NlZF9ieSwgZGFpbHk6CnBGIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KGRheSwgc2Vlbl9ieSwgY2xvc2VkX2J5LCBjbGlja2VkX2J5KSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ3NlZW5fYnknLCAnY2xvc2VkX2J5JywgJ2NsaWNrZWRfYnknKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkFjdGlvbiIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiVmFsdWUiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gVmFsdWUsCiAgICAgICAgICAgICAgIGdyb3VwID0gQWN0aW9uLAogICAgICAgICAgICAgICBjb2xvciA9IEFjdGlvbiwKICAgICAgICAgICAgICAgZmlsbCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgbGFiZWwgPSBWYWx1ZSwKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ1dpa2lwZWRpYSBDaGFsbGVuZ2UgT2N0b2JlciAyMDIwJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIkFjdGlvbnMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgoqKkNoYXJ0IDAuMS4yKiogVG90YWwsIE1lYW4gcGVyIGRheSwgYW5kIE1lZGlhbiBwZXIgZGF5IEJhbm5lciBJbXByZXNzaW9ucywgQ2xpY2tzLCBhbmQgQ2xvc3VyZXMsIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwRnJhbWVUb3RhbCA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF5LCBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnkpICU+JSAKICBncm91cF9ieShkYXkpICU+JSAKICBzdW1tYXJpc2Uoc2Vlbl9ieSA9IHN1bShzZWVuX2J5KSwgCiAgICAgICAgICAgIGNsb3NlZF9ieSA9IHN1bShjbG9zZWRfYnkpLCAKICAgICAgICAgICAgY2xpY2tlZF9ieSA9IHN1bShjbGlja2VkX2J5KQogICAgICAgICAgICApIApwRnJhbWVUb3RhbCA8LSBwRnJhbWVUb3RhbCAlPiUgCiAgc3VtbWFyaXNlKHNlZW5fYnlfX3RvdGFsX2luX2NhbXBhaWduID0gc3VtKHNlZW5fYnkpLCAKICAgICAgICAgICAgY2xvc2VkX2J5X190b3RhbF9pbl9jYW1wYWlnbiA9IHN1bShjbG9zZWRfYnkpLCAKICAgICAgICAgICAgY2xpY2tlZF9ieV9fdG90YWxfaW5fY2FtcGFpZ24gPSBzdW0oY2xpY2tlZF9ieSksCiAgICAgICAgICAgIHNlZW5fYnlfX21lYW5fcGVyX2RheSA9IG1lYW4oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnlfX21lYW5fcGVyX2RheSA9IG1lYW4oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnlfX21lYW5fcGVyX2RheSA9IG1lYW4oY2xpY2tlZF9ieSksCiAgICAgICAgICAgIHNlZW5fYnlfX21lZGlhbl9wZXJfZGF5ID0gbWVkaWFuKHNlZW5fYnkpLCAKICAgICAgICAgICAgY2xvc2VkX2J5X19tZWRpYW5fcGVyX2RheSA9IG1lZGlhbihjbG9zZWRfYnkpLCAKICAgICAgICAgICAgY2xpY2tlZF9ieV9fbWVkaWFuX3Blcl9kYXkgPSBtZWRpYW4oY2xpY2tlZF9ieSkKICAgICAgICAgICAgKSAlPiUgCiAgdCgpCnBGcmFtZVRvdGFsWywgMV0gPC0gcm91bmQocEZyYW1lVG90YWxbLCAxXSwgMikKcEZyYW1lVG90YWwgPC0gYXMuZGF0YS5mcmFtZShwRnJhbWVUb3RhbCkgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbignTWVhc3VyZScpCmNvbG5hbWVzKHBGcmFtZVRvdGFsKVsyXSA8LSAnVmFsdWUnCnBGcmFtZVRvdGFsIDwtcEZyYW1lVG90YWwgJT4lIAogIHNlcGFyYXRlKCdNZWFzdXJlJywgCiAgICAgICAgICAgc2VwID0gIl9fIiwgCiAgICAgICAgICAgaW50byA9IGMoJ0FjdGlvbicsICdTdGF0aXN0aWMnKSkKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn06IHNlZW5fYnksIGNsaWNrZWRfYnksIGNsb3NlZF9ieSwgZGFpbHk6CmdncGxvdChwRnJhbWVUb3RhbCwgYWVzKHggPSBBY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBsb2coVmFsdWUpLAogICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFN0YXRpc3RpYywKICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBTdGF0aXN0aWMsCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKwogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKyAgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAnKSArCiAgeGxhYigiQWN0aW9uIikgKyB5bGFiKCJsb2coVmFsdWUpIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgoqKkNoYXJ0IDAuMS4zKiogRGFpbHkgQmFubmVyIENsaWNrIGFuZCBDbG9zZSByYXRlcywgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBjbG9zZV9yYXRlLCBjbGlja19yYXRlLCBkYWlseToKcEYgPC0gcEZyYW1lICU+JSAKICBzZWxlY3QoZGF5LCBjbGlja19yYXRlLCBjbG9zZV9yYXRlKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ2NsaWNrX3JhdGUnLCAnY2xvc2VfcmF0ZScpLCAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiQWN0aW9uIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJWYWx1ZSIpCmdncGxvdChwRiwgYWVzKHggPSBkYXksCiAgICAgICAgICAgICAgIHkgPSBWYWx1ZSwKICAgICAgICAgICAgICAgZ3JvdXAgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGNvbG9yID0gQWN0aW9uLAogICAgICAgICAgICAgICBmaWxsID0gQWN0aW9uLAogICAgICAgICAgICAgICBsYWJlbCA9IFZhbHVlLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiQWN0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArIAogIHlsaW0oYygwLCAuMjUpKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgoqKlRhYmxlIDAuMS40KiogTWVhbiBhbmQgTWVkaWFuIENsaWNrIGFuZCBDbG9zZSByYXRlcyAoYWNyb3NzIGRheXMpCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdEZyYW1lIDwtIGRhdGEuZnJhbWUoY2xpY2tfcmF0ZV9tZWFuID0gcm91bmQobWVhbihwRnJhbWUkY2xpY2tfcmF0ZSksIDUpLCAKICAgICAgICAgICAgICAgICAgICAgY2xpY2tfcmF0ZV9tZWRpYW4gPSByb3VuZChtZWRpYW4ocEZyYW1lJGNsaWNrX3JhdGUpLCA1KSwKICAgICAgICAgICAgICAgICAgICAgY2xvc2VfcmF0ZV9tZWFuID0gcm91bmQobWVhbihwRnJhbWUkY2xvc2VfcmF0ZSksIDUpLAogICAgICAgICAgICAgICAgICAgICBjbG9zZV9yYXRlX21lZGlhbiA9IHJvdW5kKG1lZGlhbihwRnJhbWUkY2xvc2VfcmF0ZSksIDUpCikKZGF0YXRhYmxlKHRGcmFtZSkKYGBgCgoqKlRhYmxlIDAuMS41KiogRGUgRmFjdG8gQ2xpY2sgYW5kIENsb3NlIHJhdGVzIGluIHBlcmNlbnRzCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdEZyYW1lIDwtIGRhdGEuZnJhbWUoY2xpY2tfcmF0ZSA9IHJvdW5kKHN1bShwRnJhbWUkY2xpY2tlZF9ieSkvc3VtKHBGcmFtZSRzZWVuX2J5KSoxMDAsIDUpLCAKICAgICAgICAgICAgICAgICAgICAgY2xvc2VfcmF0ZSA9IHJvdW5kKHN1bShwRnJhbWUkY2xvc2VkX2J5KS9zdW0ocEZyYW1lJHNlZW5fYnkpKjEwMCwgNSkpCmRhdGF0YWJsZSh0RnJhbWUpCmBgYAoKKipDaGFydCAwLjEuNioqIERhaWx5IEJhbm5lciBDbGljayBhbmQgQ2xvc2UgcmF0ZXMsIGJ5ICoqZGV2aWNlcyoqLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gMTB9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF5LCBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRldmljZSkgJT4lIAogIGdyb3VwX2J5KGRheSwgZGV2aWNlKSAlPiUgCiAgc3VtbWFyaXNlKHNlZW5fYnkgPSBzdW0oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnkgPSBzdW0oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnkgPSBzdW0oY2xpY2tlZF9ieSkpCnBGcmFtZSRjbG9zZV9yYXRlID0gcm91bmQocEZyYW1lJGNsb3NlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lJGNsaWNrX3JhdGUgPSByb3VuZChwRnJhbWUkY2xpY2tlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXkpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRhaWx5OgpwRiA8LSBwRnJhbWUgJT4lIAogIHNlbGVjdChkYXksIGRldmljZSwgc2Vlbl9ieSwgY2xvc2VkX2J5LCBjbGlja2VkX2J5KSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ3NlZW5fYnknLCAnY2xvc2VkX2J5JywgJ2NsaWNrZWRfYnknKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkFjdGlvbiIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiVmFsdWUiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gVmFsdWUsCiAgICAgICAgICAgICAgIGdyb3VwID0gQWN0aW9uLAogICAgICAgICAgICAgICBjb2xvciA9IEFjdGlvbiwKICAgICAgICAgICAgICAgZmlsbCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgbGFiZWwgPSBWYWx1ZSwKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAofmRldmljZSwgbnJvdyA9IDMsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJBY3Rpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipDaGFydCAwLjEuNkIqKiBNZWFuLCBtZWRpYW4sIGFuZCB0b3RhbCBJbXByZXNzaW9ucywgY2xpY2tzIGFuZCBjbG9zaW5nIGNsaWNrcyAqKnBlciBiYW5uZXIqKiAsICoqcGVyIGRldmljZSoqLgoqKk5PVEUuKiogTG9nKHkpIHNjYWxlcyB3ZXJlIHVzZWQgdG8gaGVscCByZWFkYmlsaXR5OyBkYXRhIGxhYmVscyBhcmUgZXhhY3QgdmFsdWVzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSAxMH0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpzZWxlY3Qoc2Vlbl9ieSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5LCBkZXZpY2UsIHZhcl9jdHJsKQpwRnJhbWUkYmFubmVybmFtZSA8LSBOVUxMCnBGcmFtZSA8LSBwRnJhbWUgJT4lIAogIGRwbHlyOjpncm91cF9ieShkZXZpY2UsIHZhcl9jdHJsKSAlPiUgCiAgc3VtbWFyaXNlKG1lYW4wc2Vlbl9ieSA9IG1lYW4oc2Vlbl9ieSksCiAgICAgICAgICAgIG1lYW4wY2xpY2tlZF9ieSA9IG1lYW4oY2xpY2tlZF9ieSksCiAgICAgICAgICAgIG1lYW4wY2xvc2VkX2J5ID0gbWVhbihjbG9zZWRfYnkpLAogICAgICAgICAgICBtZWRpYW4wc2Vlbl9ieSA9IG1lZGlhbihzZWVuX2J5KSwKICAgICAgICAgICAgbWVkaWFuMGNsaWNrZWRfYnkgPSBtZWRpYW4oY2xpY2tlZF9ieSksCiAgICAgICAgICAgIG1lZGlhbjBjbG9zZWRfYnkgPSBtZWRpYW4oY2xvc2VkX2J5KSwKICAgICAgICAgICAgdG90YWwwc2Vlbl9ieSA9IHN1bShzZWVuX2J5KSwKICAgICAgICAgICAgdG90YWwwY2xpY2tlZF9ieSA9IHN1bShjbGlja2VkX2J5KSwKICAgICAgICAgICAgdG90YWwwY2xvc2VkX2J5ID0gc3VtKGNsb3NlZF9ieSkKICAgICAgICAgICAgKQpwRnJhbWUgPC0gcEZyYW1lICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9IGMoJ21lYW4wc2Vlbl9ieScsICdtZWFuMGNsaWNrZWRfYnknLCAnbWVhbjBjbG9zZWRfYnknLAogICAgICAgICAgICAgICAgICAgICAgICAnbWVkaWFuMHNlZW5fYnknLCAnbWVkaWFuMGNsaWNrZWRfYnknLCAnbWVkaWFuMGNsb3NlZF9ieScsCiAgICAgICAgICAgICAgICAgICAgICAgICd0b3RhbDBzZWVuX2J5JywgJ3RvdGFsMGNsaWNrZWRfYnknLCAndG90YWwwY2xvc2VkX2J5JyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICdtZWFzdXJlJywgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICd2YWx1ZScpCnBGcmFtZSA8LSBwRnJhbWUgJT4lIAogIHNlcGFyYXRlKG1lYXN1cmUsCiAgICAgICAgICAgaW50byA9IGMoJ21lYXN1cmUnLCAnYWN0aW9uJyksCiAgICAgICAgICAgc2VwID0gIjAiKQpwRnJhbWUkdmFsdWUgPC0gcm91bmQocEZyYW1lJHZhbHVlLCAxKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IG1lYXN1cmUsCiAgICAgICAgICAgICAgIHkgPSBsb2codmFsdWUpLAogICAgICAgICAgICAgICBncm91cCA9IGFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBhY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBhY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gdmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBmYWNldF93cmFwKGRldmljZX52YXJfY3RybCwgbnJvdyA9IDMsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJNZWFzdXJlIikgKyB5bGFiKCJsb2coVmFsdWUpIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKCioqQ2hhcnQgMC4xLjcqKiBEYWlseSBCYW5uZXIgQ2xpY2sgYW5kIENsb3NlIHJhdGVzLCBieSAqKnZhci9jdHJsKiosIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF5LCBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIHZhcl9jdHJsKSAlPiUgCiAgZ3JvdXBfYnkoZGF5LCB2YXJfY3RybCkgJT4lIAogIHN1bW1hcmlzZShzZWVuX2J5ID0gc3VtKHNlZW5fYnkpLCAKICAgICAgICAgICAgY2xvc2VkX2J5ID0gc3VtKGNsb3NlZF9ieSksIAogICAgICAgICAgICBjbGlja2VkX2J5ID0gc3VtKGNsaWNrZWRfYnkpKQpwRnJhbWUkY2xvc2VfcmF0ZSA9IHJvdW5kKHBGcmFtZSRjbG9zZWRfYnkvcEZyYW1lJHNlZW5fYnksIDIpCnBGcmFtZSRjbGlja19yYXRlID0gcm91bmQocEZyYW1lJGNsaWNrZWRfYnkvcEZyYW1lJHNlZW5fYnksIDIpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZGF5KQojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfTogc2Vlbl9ieSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5LCBkYWlseToKcEYgPC0gcEZyYW1lICU+JSAKICBzZWxlY3QoZGF5LCB2YXJfY3RybCwgc2Vlbl9ieSwgY2xvc2VkX2J5LCBjbGlja2VkX2J5KSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ3NlZW5fYnknLCAnY2xvc2VkX2J5JywgJ2NsaWNrZWRfYnknKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkFjdGlvbiIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiVmFsdWUiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gVmFsdWUsCiAgICAgICAgICAgICAgIGdyb3VwID0gQWN0aW9uLAogICAgICAgICAgICAgICBjb2xvciA9IEFjdGlvbiwKICAgICAgICAgICAgICAgZmlsbCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgbGFiZWwgPSBWYWx1ZSwKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAofnZhcl9jdHJsLCBucm93ID0gMiwgc2NhbGVzID0gImZyZWUiKSArIAogIGdndGl0bGUoJzIwMjAgV01ERSBPY2Nhc2lvbmFsIEVkaXRvcnMgQmFubmVyIENhbXBhaWduJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIkFjdGlvbnMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgoqKkNoYXJ0IDAuMS43QioqIE1lYW4sIG1lZGlhbiwgYW5kIHRvdGFsIEltcHJlc3Npb25zLCBjbGlja3MgYW5kIGNsb3NpbmcgY2xpY2tzICoqcGVyIGJhbm5lcioqLgoqKk5PVEUuKiogTG9nKHkpIHNjYWxlcyB3ZXJlIHVzZWQgdG8gaGVscCByZWFkYmlsaXR5OyBkYXRhIGxhYmVscyBhcmUgZXhhY3QgdmFsdWVzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6c2VsZWN0KHNlZW5fYnksIGNsaWNrZWRfYnksIGNsb3NlZF9ieSwgdmFyX2N0cmwpCnBGcmFtZSRiYW5uZXJuYW1lIDwtIE5VTEwKcEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHZhcl9jdHJsKSAlPiUgCiAgc3VtbWFyaXNlKG1lYW4wc2Vlbl9ieSA9IG1lYW4oc2Vlbl9ieSksCiAgICAgICAgICAgIG1lYW4wY2xpY2tlZF9ieSA9IG1lYW4oY2xpY2tlZF9ieSksCiAgICAgICAgICAgIG1lYW4wY2xvc2VkX2J5ID0gbWVhbihjbG9zZWRfYnkpLAogICAgICAgICAgICBtZWRpYW4wc2Vlbl9ieSA9IG1lZGlhbihzZWVuX2J5KSwKICAgICAgICAgICAgbWVkaWFuMGNsaWNrZWRfYnkgPSBtZWRpYW4oY2xpY2tlZF9ieSksCiAgICAgICAgICAgIG1lZGlhbjBjbG9zZWRfYnkgPSBtZWRpYW4oY2xvc2VkX2J5KSwKICAgICAgICAgICAgdG90YWwwc2Vlbl9ieSA9IHN1bShzZWVuX2J5KSwKICAgICAgICAgICAgdG90YWwwY2xpY2tlZF9ieSA9IHN1bShjbGlja2VkX2J5KSwKICAgICAgICAgICAgdG90YWwwY2xvc2VkX2J5ID0gc3VtKGNsb3NlZF9ieSkKICAgICAgICAgICAgKQpwRnJhbWUgPC0gcEZyYW1lICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9IGMoJ21lYW4wc2Vlbl9ieScsICdtZWFuMGNsaWNrZWRfYnknLCAnbWVhbjBjbG9zZWRfYnknLAogICAgICAgICAgICAgICAgICAgICAgICAnbWVkaWFuMHNlZW5fYnknLCAnbWVkaWFuMGNsaWNrZWRfYnknLCAnbWVkaWFuMGNsb3NlZF9ieScsCiAgICAgICAgICAgICAgICAgICAgICAgICd0b3RhbDBzZWVuX2J5JywgJ3RvdGFsMGNsaWNrZWRfYnknLCAndG90YWwwY2xvc2VkX2J5JyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICdtZWFzdXJlJywgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICd2YWx1ZScpCnBGcmFtZSA8LSBwRnJhbWUgJT4lIAogIHNlcGFyYXRlKG1lYXN1cmUsCiAgICAgICAgICAgaW50byA9IGMoJ21lYXN1cmUnLCAnYWN0aW9uJyksCiAgICAgICAgICAgc2VwID0gIjAiKQpwRnJhbWUkdmFsdWUgPC0gcm91bmQocEZyYW1lJHZhbHVlLCAxKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IG1lYXN1cmUsCiAgICAgICAgICAgICAgIHkgPSBsb2codmFsdWUpLAogICAgICAgICAgICAgICBncm91cCA9IGFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBhY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBhY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gdmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBmYWNldF93cmFwKH52YXJfY3RybCwgbnJvdyA9IDMsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJNZWFzdXJlIikgKyB5bGFiKCJsb2coVmFsdWUpIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKCioqVGFibGUgMC4xLjgqKiBEYWlseSBCYW5uZXIgSW1wcmVzc2lvbnM6IHNlZW4gYnksIGNsb3NlZCBieSwgY2xpY2tlZCBieSwgUGFnZXZpZXdzCgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMjIyAtLS0gRnVsbCBEYXRhc2V0IChUYWJsZSBSZXBvcnQpCmRTIDwtIGRhdGFTZXQKZFMkYmFubmVybmFtZSA8LSBnc3ViKCJXTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF8iLCAiIiwgZFMkYmFubmVybmFtZSkKZFMgPC0gZFNbLAogICAgICAgICBjKCdiYW5uZXJuYW1lJywgJ2RheScsICdkZXZpY2UnLCAndmFyX2N0cmwnLCAKICAgICAgICAgICAnc2Vlbl9ieScsICdjbG9zZWRfYnknLCAnY2xpY2tlZF9ieScpXQpkYXRhdGFibGUoZFMsIAogICAgICAgICAgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDMwKQogICAgICAgICAgKQpgYGAKCiMjIDEuIENhbXBhaWduIFBhZ2V2aWV3cwoKVGhpcyBzZWN0aW9uIHByZXNlbnRzIGFsbCBkYXRhIGFuZCBzdGF0aXN0aWNzIG9uIHRoZSBjYW1wYWlnbiBwYWdlcy4KClRoZSBmb2xsb3dpbmcgY2h1bmsgbG9hZHMgYW5kIHRoZW4gcmUtc3RydWN0dXJlcyB0aGUgZGF0YXNldCBhIGJpdC4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpsRiA8LSBsaXN0LmZpbGVzKCdfZGFpbHlSZXBvcnRpbmcnKQpsRiA8LSBsRltncmVwbCgiXnBhZ2V2aWV3c0FnZ3JlZ2F0ZWQiLCBsRildCmRhdGFTZXQgPC0gbGFwcGx5KHBhc3RlMCgiX2RhaWx5UmVwb3J0aW5nLyIsIGxGKSwgZnJlYWQpCmRhdGFTZXQgPC0gcmJpbmRsaXN0KGRhdGFTZXQpCmRhdGFTZXQkVjEgPC0gTlVMTApkYXRhU2V0JGNhbXBhaWduIDwtIE5VTEwKIyAtIGV4cGFuZCBncmlkIHRvIGFjY291bnQgZm9yIG1pc3Npbmcgb2JzZXJ2YXRpb25zIHBlciBkYXkKZFMgPC0gZXhwYW5kLmdyaWQodW5pcXVlKGRhdGFTZXQkVGFnKSwgCiAgICAgICAgICAgICAgICAgIHVuaXF1ZShkYXRhU2V0JFBhZ2UpLCAKICAgICAgICAgICAgICAgICAgdW5pcXVlKGRhdGFTZXQkZGF0ZSksIAogICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXMoZFMpIDwtIGMoJ1RhZycsICdQYWdlJywgJ2RhdGUnKQpkUyA8LSBkUyAlPiUgCiAgbGVmdF9qb2luKGRhdGFTZXQsIAogICAgICAgICAgICBieSA9IGMoIlRhZyIsICJQYWdlIiwgImRhdGUiKSkKZGF0YVNldCA8LSBkUzsgcm0oZFMpCmRhdGFTZXQkUGFnZXZpZXdzW2lzLm5hKGRhdGFTZXQkUGFnZXZpZXdzKV0gPC0gMApkYXRhU2V0JFRhZyA8LSBnc3ViKCImLiskIiwgIiIsIGRhdGFTZXQkVGFnKQpkYXRhU2V0JFRhZyA8LSBnc3ViKCJcXD9jYW1wYWlnbj0iLCAiIiwgZGF0YVNldCRUYWcpCmRhdGFTZXQkZGF0ZSA8LSBzYXBwbHkoZGF0YVNldCRkYXRlLCBmdW5jdGlvbih4KSB7CiAgZCA8LSBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXQogIGlmIChuY2hhcihkWzNdKSA9PSAxKSB7CiAgICBkWzNdIDwtIHBhc3RlMCgiMCIsIGRbM10pCiAgfQogIHJldHVybihwYXN0ZShkLCBjb2xsYXBzZSA9ICItIikpCn0pCmBgYAoKIyMjIDEuMSBQYWdldmlld3MgT3ZlcnZpZXcKCioqQ2hhcnQgMS4xLjEqKiBEYWlseSBQYWdldmlld3MsIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBjaGFubmVscy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDZ9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF0ZSwgUGFnZSwgUGFnZXZpZXdzKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgUGFnZSkgJT4lIAogIHN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXRlKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41LCBncm91cCA9IDEpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAoflBhZ2UsIG5jb2wgPSAyKSArIAogIGdndGl0bGUoJ1dpa2lwZWRpYSBDaGFsbGVuZ2UgT2N0b2JlciAyMDIwJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIlBhZ2V2aWV3cyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgIyBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gMTApKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKKipUYWJsZSAxLjEuMSoqIFBhZ2V2aWV3cyB0b3RhbHMKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQp0RnJhbWUgPC0gcEZyYW1lICU+JSAKICBzZWxlY3QoUGFnZSwgUGFnZXZpZXdzKSAlPiUgCiAgZ3JvdXBfYnkoUGFnZSkgJT4lIAogIHN1bW1hcmlzZSh0b3RhbFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKSAlPiUgCiAgYXJyYW5nZShkZXNjKHRvdGFsUGFnZXZpZXdzKSkKZGF0YXRhYmxlKHRGcmFtZSkKYGBgCgojIyMgMS4yIFBhZ2V2aWV3czogQ2FtcGFpZ24gQ2hhbm5lbHMKCioqQ2hhcnQgMS4yLjEqKiBQYWdldmlld3MsIGJ5ICoqY2hhbm5lbHMqKgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNn0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChkYXRlLCBUYWcsIFBhZ2V2aWV3cykgJT4lIAogIGdyb3VwX2J5KGRhdGUsIFRhZykgJT4lIAogIHN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXRlKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41LCBncm91cCA9IDEpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAoflRhZywgbmNvbCA9IDIpICsgCiAgZ2d0aXRsZSgnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAnKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiUGFnZXZpZXdzIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICAjIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA2KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCioqQ2hhcnQgMS4yLjIqKiBUb3RhbCBQYWdldmlld3MsIGJ5ICoqY2hhbm5lbHMqKgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwRnJhbWUgPC0gcEZyYW1lICU+JSAKICBzZWxlY3QoVGFnLCBQYWdldmlld3MpICU+JSAKICBncm91cF9ieShUYWcpICU+JSAKICBzdW1tYXJpc2UoUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpICU+JSAKICBhcnJhbmdlKGRlc2MoUGFnZXZpZXdzKSkKcEZyYW1lJFRhZyA8LSBmYWN0b3IocEZyYW1lJFRhZywKICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gcEZyYW1lJFRhZywKICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFQpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gVGFnLAogICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsdWUiLCBmaWxsID0gIndoaXRlIikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdXaWtpcGVkaWEgQ2hhbGxlbmdlIE9jdG9iZXIgMjAyMCcpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJQYWdldmlld3MiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKKipUYWJsZSAyLjIuMSoqIFRvdGFsIHBhZ2V2aWV3cywgYnkgKipjaGFubmVscyoqIGFuZCBieSAqKnBhZ2VzKiouCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChUYWcsIFBhZ2UsIFBhZ2V2aWV3cykgJT4lIAogIGdyb3VwX2J5KFRhZywgUGFnZSkgJT4lIAogIHN1bW1hcmlzZSh0b3RhbFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKSAlPiUgCiAgYXJyYW5nZShkZXNjKHRvdGFsUGFnZXZpZXdzKSkKZGF0YXRhYmxlKHRGcmFtZSkKYGBgCgojIyAyLiBVc2VyIFJlZ2lzdHJhdGlvbnMKClRoaXMgc2VjdGlvbiBwcmVzZW50cyBhbGwgZGF0YSBhbmQgc3RhdGlzdGljcyBvbiB0aGUgdXNlciByZWdpc3RyYXRpb25zLgoKIyMjIDIuMSBVc2VyIFJlZ2lzdHJhdGlvbnMgT3ZlcnZpZXcKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpsRiA8LSBsaXN0LmZpbGVzKCJfZGFpbHlSZXBvcnRpbmciKQpsRiA8LSBsRltncmVwbCgiXnVzZXJfcmVnaXN0cmF0aW9ucyIsIGxGKV0KZGF0YVNldCA8LSBmcmVhZChwYXN0ZTAoIl9kYWlseVJlcG9ydGluZy8iLCBsRikpCmRhdGFTZXQgPC0gZmlsdGVyKGRhdGFTZXQsIAogICAgICAgICAgICAgICAgICAhZ3JlcGwoIl50ZXN0fF5UZXN0IiwgZGF0YVNldCR1c2VybmFtZSkpCmRhdGFTZXQkZGF0ZSA8LSBwYXN0ZShkYXRhU2V0JHllYXIsIAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKG5jaGFyKGRhdGFTZXQkbW9udGgpID09IDEsIHBhc3RlMCgiMCIsIGRhdGFTZXQkbW9udGgpLCBkYXRhU2V0JG1vbnRoKSwKICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShuY2hhcihkYXRhU2V0JGRheSkgPT0gMSwgcGFzdGUwKCIwIiwgZGF0YVNldCRkYXkpLCBkYXRhU2V0JGRheSksCiAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiLSIpCnJlZ1VzZXJzIDwtIHNlbGVjdChkYXRhU2V0LAogICAgICAgICAgICAgICAgICAgdXNlcmlkLAogICAgICAgICAgICAgICAgICAgdXNlcm5hbWUsIAogICAgICAgICAgICAgICAgICAgY2FtcGFpZ24pCiMgLSB0b3RhbCBudW1iZXIgb2YgcmVnaXN0ZXJlZCB1c2Vycwp0b3RhbF9yZWdpc3RlcmVkIDwtIGRpbShyZWdVc2VycylbMV0KIyAtIGV4cGFuZCBncmlkIHRvIGFjY291bnQgZm9yIG1pc3Npbmcgb2JzZXJ2YXRpb25zIHBlciBkYXkKZGF0ZVNwYW4gPC0gc2VxKGZyb20gPSBhcy5EYXRlKCIyMDIwLTEwLTAxIiksIAogICAgICAgICAgICAgICAgdG8gPSBhcy5EYXRlKCIyMDIwLTExLTExIiksIAogICAgICAgICAgICAgICAgYnkgPSAiZGF5IikKZGF0ZVNwYW4gPC0gYXMuY2hhcmFjdGVyKGRhdGVTcGFuKQpkUyA8LSBleHBhbmQuZ3JpZChkYXRlU3BhbiwgCiAgICAgICAgICAgICAgICAgIHVuaXF1ZShkYXRhU2V0JGNhbXBhaWduKSwgCiAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhkUykgPC0gYygnZGF0ZScsICdjYW1wYWlnbicpCmRTIDwtIGRTICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KGRhdGFTZXQsIGRhdGUsIGNhbXBhaWduLCB1c2VyaWQpLAogICAgICAgICAgICBieSA9IGMoImRhdGUiLCAiY2FtcGFpZ24iKSkKZFMkdXNlcmlkIDwtIGlmZWxzZShpcy5uYShkUyR1c2VyaWQpLCAwLCAxKQpwRnJhbWUgPC0gZFMgJT4lIAogIHNlbGVjdChkYXRlLCB1c2VyaWQpICU+JSAKICBncm91cF9ieShkYXRlKSAlPiUgCiAgc3VtbWFyaXNlKHJlZ2lzdHJhdGlvbnMgPSBzdW0odXNlcmlkKSkgJT4lIAogIGFycmFuZ2UoZGF0ZSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IHJlZ2lzdHJhdGlvbnMsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHJlZ2lzdHJhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSwgZ3JvdXAgPSAxKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdXaWtpcGVkaWEgQ2hhbGxlbmdlIE9jdG9iZXIgMjAyMCcpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJSZWdpc3RyYXRpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKVGhlIHRvdGFsIG51bWJlciBvZiB1c2VycyByZWdpc3RlcmVkIGluIHRoaXMgY2FtcGFpZ24gaXMgYHIge3RvdGFsX3JlZ2lzdGVyZWR9YC4KCgojIyMgMi4yIFVzZXIgUmVnaXN0cmF0aW9ucyBwZXIgQ2FtcGFpZ24gQ2hhbm5lbAoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gOH0KcEZyYW1lIDwtIGRTICU+JSAKICBncm91cF9ieShkYXRlLCBjYW1wYWlnbikgJT4lIAogIHN1bW1hcmlzZShyZWdpc3RyYXRpb25zID0gc3VtKHVzZXJpZCkpICU+JSAKICBhcnJhbmdlKGRhdGUpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF0ZSwKICAgICAgICAgICAgICAgICAgIHkgPSByZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSByZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuMjUsIGdyb3VwID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgZmFjZXRfd3JhcCh+Y2FtcGFpZ24sIG5jb2wgPSAyKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ1dpa2lwZWRpYSBDaGFsbGVuZ2UgT2N0b2JlciAyMDIwJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIlJlZ2lzdHJhdGlvbnMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogICMgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNikpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyMgMi4zIFRvdGFsIFVzZXIgUmVnaXN0cmF0aW9ucyBwZXIgQ2FtcGFpZ24gQ2hhbm5lbAoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwRnJhbWUgPC0gZFMgJT4lIAogIHNlbGVjdChjYW1wYWlnbiwgdXNlcmlkKSAlPiUgCiAgZ3JvdXBfYnkoIGNhbXBhaWduKSAlPiUgCiAgc3VtbWFyaXNlKHJlZ2lzdHJhdGlvbnMgPSBzdW0odXNlcmlkKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhyZWdpc3RyYXRpb25zKSkKcEZyYW1lJGNhbXBhaWduIDwtIGZhY3RvcihwRnJhbWUkY2FtcGFpZ24sIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHBGcmFtZSRjYW1wYWlnbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFQpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gY2FtcGFpZ24sCiAgICAgICAgICAgICAgICAgICB5ID0gcmVnaXN0cmF0aW9ucywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gcmVnaXN0cmF0aW9ucywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvciA9ICJibHVlIiwgZmlsbCA9ICJ3aGl0ZSIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAnKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiUmVnaXN0cmF0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyAzLiBVc2VyIEVkaXRzCgpUaGlzIHNlY3Rpb24gcHJlc2VudHMgYWxsIGRhdGEgYW5kIHN0YXRpc3RpY3Mgb24gdGhlIHVzZXIgZWRpdHMuCgojIyMgMy4xIFVzZXIgRWRpdHMgT3ZlcnZpZXcKClRoZSBmb2xsb3dpbmcgY2h1bmsgbG9hZHMgdGhlIGRhdGFzZXQgb2YgdXNlciBpbnRlcmFjdGlvbnMgd2l0aCBjYW1wYWlnbiBjaGFubmVscyBhbmQgdGhlbiByZS1zdHJ1Y3R1cmVzIHRoZSBkYXRhc2V0IGEgYml0LgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmxGIDwtIGxpc3QuZmlsZXMoIl9kYWlseVJlcG9ydGluZyIpCmxGIDwtIGxGW2dyZXBsKCJedXNlckVkaXRzIiwgbEYpXQplZGl0U2V0IDwtIGZyZWFkKHBhc3RlMCgiX2RhaWx5UmVwb3J0aW5nLyIsIGxGKSkKZWRpdFNldCA8LSBzZWxlY3QoZWRpdFNldCwKICAgICAgICAgICAgICAgICAgYWN0b3JfbmFtZSwgCiAgICAgICAgICAgICAgICAgIHJldmFjdG9yX3RpbWVzdGFtcCkgICAgICAgICAgICAgICAgIApjb2xuYW1lcyhlZGl0U2V0KSA8LSBjKCd1c2VybmFtZScsICdyZXZfdGltZXN0YW1wJykKZGF0YVNldCA8LSBsZWZ0X2pvaW4oZWRpdFNldCwKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHJlZ1VzZXJzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZXJuYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ24pLAogICAgICAgICAgICAgICAgICAgICBieSA9ICJ1c2VybmFtZSIpCmRhdGFTZXQgPC0gZGF0YVNldFtjb21wbGV0ZS5jYXNlcyhkYXRhU2V0KSwgXQpkYXRhU2V0JHllYXIgPC0gc3Vic3RyKGRhdGFTZXQkcmV2X3RpbWVzdGFtcCwgMSwgNCkKZGF0YVNldCRtb250aCA8LSBzdWJzdHIoZGF0YVNldCRyZXZfdGltZXN0YW1wLCA1LCA2KQpkYXRhU2V0JGRheSA8LSBzdWJzdHIoZGF0YVNldCRyZXZfdGltZXN0YW1wLCA3LCA4KQpkYXRhU2V0JHJldl90aW1lc3RhbXAgPC0gTlVMTApkYXRhU2V0JGRhdGUgPC0gcGFzdGUoZGF0YVNldCR5ZWFyLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGFTZXQkbW9udGgsCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YVNldCRkYXksCiAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIi0iKQpkYXRhU2V0IDwtIHNlbGVjdChkYXRhU2V0LAogICAgICAgICAgICAgICAgICBkYXRlLAogICAgICAgICAgICAgICAgICB1c2VybmFtZSwKICAgICAgICAgICAgICAgICAgY2FtcGFpZ24pCmRhdGVTcGFuIDwtIHNlcShmcm9tID0gYXMuRGF0ZSgiMjAyMC0xMC0wMSIpLCAKICAgICAgICAgICAgICAgIHRvID0gYXMuRGF0ZShtYXgoZGF0YVNldCRkYXRlKSksIAogICAgICAgICAgICAgICAgYnkgPSAiZGF5IikKZGF0ZVNwYW4gPC0gYXMuY2hhcmFjdGVyKGRhdGVTcGFuKQpkUyA8LSBleHBhbmQuZ3JpZChkYXRlU3BhbiwgCiAgICAgICAgICAgICAgICAgIHVuaXF1ZShkYXRhU2V0JGNhbXBhaWduKSwKICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGRTKSA8LSBjKCdkYXRlJywgJ2NhbXBhaWduJykKZFMgPC0gZFMgJT4lIAogIGxlZnRfam9pbihzZWxlY3QoZGF0YVNldCwgZGF0ZSwgY2FtcGFpZ24sIHVzZXJuYW1lKSwKICAgICAgICAgICAgYnkgPSBjKCJkYXRlIiwgImNhbXBhaWduIikpCmRTJHVzZXJuYW1lIDwtIGlmZWxzZShpcy5uYShkUyR1c2VybmFtZSksIDAsIDEpCnBGcmFtZSA8LSBkUyAlPiUKICBzZWxlY3QoZGF0ZSwgdXNlcm5hbWUpICU+JSAKICBncm91cF9ieShkYXRlKSAlPiUgCiAgc3VtbWFyaXNlKGVkaXRzID0gc3VtKHVzZXJuYW1lKSkgJT4lIAogIGFycmFuZ2UoZGF0ZSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IGVkaXRzLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBlZGl0cywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjI1LCBncm91cCA9IDEpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ1dpa2lwZWRpYSBDaGFsbGVuZ2UgT2N0b2JlciAyMDIwJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIkVkaXRzIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCiMjIyAzLjIgVXNlciBFZGl0cyBieSBDYW1wYWlnbiBDaGFubmVscwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gOH0KcEZyYW1lIDwtIGRTICU+JSAKICBncm91cF9ieShkYXRlLCBjYW1wYWlnbikgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IHN1bSh1c2VybmFtZSkpICU+JSAKICBhcnJhbmdlKGRhdGUpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF0ZSwKICAgICAgICAgICAgICAgICAgIHkgPSBlZGl0cywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZWRpdHMsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSwgZ3JvdXAgPSAxKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBmYWNldF93cmFwKH5jYW1wYWlnbiwgbmNvbCA9IDIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAnKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiRWRpdHMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogICMgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNS41KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCiMjIyAzLjMgVG90YWwgVXNlciBFZGl0cyBieSBDYW1wYWlnbiBDaGFubmVscwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwRnJhbWUgPC0gZFMgJT4lIAogIHNlbGVjdChjYW1wYWlnbiwgdXNlcm5hbWUpICU+JSAKICBncm91cF9ieSggY2FtcGFpZ24pICU+JSAKICBzdW1tYXJpc2UoZWRpdHMgPSBzdW0odXNlcm5hbWUpKSAlPiUgCiAgYXJyYW5nZShkZXNjKGVkaXRzKSkKcEZyYW1lJGNhbXBhaWduIDwtIGZhY3RvcihwRnJhbWUkY2FtcGFpZ24sIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHBGcmFtZSRjYW1wYWlnbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFQpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gY2FtcGFpZ24sCiAgICAgICAgICAgICAgICAgICB5ID0gZWRpdHMsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGVkaXRzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsdWUiLCBmaWxsID0gIndoaXRlIikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdXaWtpcGVkaWEgQ2hhbGxlbmdlIE9jdG9iZXIgMjAyMCcpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJFZGl0cyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyMgMy40IEVkaXQgQ2xhc3NlcwoKIyMjIyAzLjQuMSBFZGl0IENsYXNzZXM6IGFsbCB1c2VycwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnVzZXJDbGFzcyA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QodXNlcm5hbWUpICU+JSAKICBncm91cF9ieSh1c2VybmFtZSkgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IG4oKSkKZWRpdEJvdW5kYXJpZXMgPC0gbGlzdCgKICBjKDAsIDEpLCAKICBjKDIsIDQpLAogIGMoNSwgOSksCiAgYygxMCwgMjApLAogIGMoMjEsIDUwKSwKICBjKDUxLCAxMDApLCAKICBjKDEwMSwgMTAwMDAwMCkKKQp1c2VyQ2xhc3MkZWRpdENsYXNzIDwtIHNhcHBseSh1c2VyQ2xhc3MkZWRpdHMsIGZ1bmN0aW9uKHgpIHsKICB3RUMgPC0gc2FwcGx5KGVkaXRCb3VuZGFyaWVzLCBmdW5jdGlvbih5KSB7CiAgICB4ID49IHlbMV0gJiB4IDw9IHlbMl0KICB9KQogIGlmIChzdW0od0VDKSA9PSAwKSB7CiAgICByZXR1cm4oIj4gMTAwIikKICB9IGVsc2UgewogICAgcmV0dXJuKHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMV0sCiAgICAgICAgICAgICAgICAgICIgLSAiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzJdLCAKICAgICAgICAgICAgICAgICAgIikiCiAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0pCmVkaXRDbGFzcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHVzZXJDbGFzcyRlZGl0Q2xhc3MpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGVkaXRDbGFzcykgPC0gYygnRWRpdCBDbGFzcycsICdOdW0uVXNlcnMnKQplZGl0Q2xhc3Mkb3JkZXIgPC0gYXMubnVtZXJpYyhzYXBwbHkoZWRpdENsYXNzJGBFZGl0IENsYXNzYCwgZnVuY3Rpb24oeCkgewogIGxvd2VyIDwtIHN0cl9leHRyYWN0KHgsICdbWzpkaWdpdDpdXSsnKQp9KSkKZWRpdENsYXNzIDwtIGFycmFuZ2UoZWRpdENsYXNzLCBvcmRlcikKZWRpdENsYXNzJG9yZGVyIDwtIE5VTEwKZWRpdENsYXNzJGBFZGl0IENsYXNzYFtlZGl0Q2xhc3MkYEVkaXQgQ2xhc3NgID09ICcoMCAtIDEpJ10gPC0gJygxKScKZWRpdENsYXNzJGBFZGl0IENsYXNzYFtlZGl0Q2xhc3MkYEVkaXQgQ2xhc3NgID09ICcoMTAxIC0gMWUrMDYpJ10gPC0gJyg+IDEwMCknCmRhdGF0YWJsZShlZGl0Q2xhc3MpCmBgYAoKIyMjIyAzLjQuMiBFZGl0IENsYXNzZXMgcGVyIENhbXBhaWduIENoYW5uZWxzCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuNX0KZWRDbGFzc2VzIDwtIGxhcHBseSh1bmlxdWUoZGF0YVNldCRjYW1wYWlnbiksIGZ1bmN0aW9uKHgpIHsKICB1c2VyQ2xhc3MgPC0gZGF0YVNldCAlPiUgCiAgZmlsdGVyKGNhbXBhaWduID09IHgpICU+JSAKICBzZWxlY3QodXNlcm5hbWUpICU+JSAKICBncm91cF9ieSh1c2VybmFtZSkgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IG4oKSkKZWRpdEJvdW5kYXJpZXMgPC0gbGlzdCgKICBjKDAsIDEpLCAKICBjKDIsIDQpLAogIGMoNSwgOSksCiAgYygxMCwgMjApLAogIGMoMjEsIDUwKSwKICBjKDUxLCAxMDApLCAKICBjKDEwMSwgMTAwMDAwMCkKKQp1c2VyQ2xhc3MkZWRpdENsYXNzIDwtIHNhcHBseSh1c2VyQ2xhc3MkZWRpdHMsIGZ1bmN0aW9uKHgpIHsKICB3RUMgPC0gc2FwcGx5KGVkaXRCb3VuZGFyaWVzLCBmdW5jdGlvbih5KSB7CiAgICB4ID49IHlbMV0gJiB4IDw9IHlbMl0KICB9KQogIGlmIChzdW0od0VDKSA9PSAwKSB7CiAgICByZXR1cm4oIj4gMTAwIikKICB9IGVsc2UgewogICAgcmV0dXJuKHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMV0sCiAgICAgICAgICAgICAgICAgICIgLSAiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzJdLCAKICAgICAgICAgICAgICAgICAgIikiCiAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0pCnVzZXJDbGFzcyRlZGl0Q2xhc3NbdXNlckNsYXNzJGVkaXRDbGFzcyA9PSAnKDAgLSAxKSddIDwtICcoMSknCnVzZXJDbGFzcyRlZGl0Q2xhc3NbdXNlckNsYXNzJGVkaXRDbGFzcyA9PSAnKDEwMSAtIDFlKzA2KSddIDwtICcoPiAxMDApJwplZGl0Q2xhc3MgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZSh1c2VyQ2xhc3MkZWRpdENsYXNzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhlZGl0Q2xhc3MpIDwtIGMoJ0VkaXQgQ2xhc3MnLCAnTnVtLlVzZXJzJykKZWRpdENsYXNzJG9yZGVyIDwtIGFzLm51bWVyaWMoc2FwcGx5KGVkaXRDbGFzcyRgRWRpdCBDbGFzc2AsIGZ1bmN0aW9uKHgpIHsKICBsb3dlciA8LSBzdHJfZXh0cmFjdCh4LCAnW1s6ZGlnaXQ6XV0rJykKfSkpCmVkaXRDbGFzcyA8LSBhcnJhbmdlKGVkaXRDbGFzcywgb3JkZXIpCmVkaXRDbGFzcyRvcmRlciA8LSBOVUxMCmVkaXRDbGFzcyRjYW1wYWlnbiA8LSB4CnJldHVybihlZGl0Q2xhc3MpCn0pCmVkQ2xhc3NlcyA8LSByYmluZGxpc3QoZWRDbGFzc2VzKQplZENsYXNzZXMkY2FtcGFpZ24gPC0gc2FwcGx5KGVkQ2xhc3NlcyRjYW1wYWlnbiwgZnVuY3Rpb24oeCkgewogIGQgPC0gc3Ryc3BsaXQoeCwgIl8iKVtbMV1dCiAgaWYgKG5jaGFyKHRhaWwoZCwgMSkpID09IDEpIHsKICAgIGRbbGVuZ3RoKGQpXSA8LSBwYXN0ZTAoIjAiLCBkW2xlbmd0aChkKV0pCiAgfQogIGQgPC0gcGFzdGUoZCwgY29sbGFwc2UgPSAiXyIpCiAgcmV0dXJuKGQpCiAgfSkKZWRDbGFzc2VzIDwtIGFycmFuZ2UoZWRDbGFzc2VzLCBjYW1wYWlnbikKZWRDbGFzc2VzJGBFZGl0IENsYXNzYFtlZENsYXNzZXMkYEVkaXQgQ2xhc3NgID09ICcoMCAtIDEpJ10gPC0gJygxKScKZWRDbGFzc2VzJGBFZGl0IENsYXNzYCA8LSBmYWN0b3IoZWRDbGFzc2VzJGBFZGl0IENsYXNzYCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoJygxKScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJygyIC0gNCknLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnKDUgLSA5KScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJygxMCAtIDIwKScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJygyMSAtIDUwKScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcoNTEgLSAxMDApJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnKD4gMTAwKScpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCmdncGxvdChlZENsYXNzZXMsIAogICAgICAgYWVzKHggPSBgRWRpdCBDbGFzc2AsIAogICAgICAgICAgIHkgPSBgTnVtLlVzZXJzYCwgCiAgICAgICAgICAgbGFiZWwgPSBgTnVtLlVzZXJzYCkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJ3aGl0ZSIpICsgCiAgZmFjZXRfd3JhcCh+Y2FtcGFpZ24pICsgCiAgZ2d0aXRsZSgnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAnKSArCiAgeGxhYigiRWRpdCBDbGFzcyIpICsgeWxhYigiTnVtLnVzZXJzIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCg==