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

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

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

Data Acquisiton

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

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

Daily Update

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

### --- determine cetDay
cetDay <- Sys.time()
cetDay
attr(cetDay, "tzone") <- "Europe/Berlin"
# - one day behind for crontab
# - (i.e. waiting for wmf.webrequest to complete is data acquisition)
cetDay <- ymd(
  strsplit(as.character(cetDay), 
           split = " ", 
           fixed = T)[[1]][1]
) - 1

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

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

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

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

# - wrangle Banner Impression data
wmde_process_banner_impressions(fileName = fileName, 
                                dataDir = dataDir, 
                                cetDay = cetDay,
                                campaignName = campaignName)


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

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

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

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


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

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

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

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

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

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

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

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

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

startTimestamp <- '20201001000000'
# - get user ids
userRegistrations <- fread(paste0(dataDir,
                                  'user_registrations.tsv'))
dim(userRegistrations)
# - clean up from test accounts
userRegistrations <- filter(userRegistrations, 
                            !grepl("^test|^Test", 
                                   userRegistrations$username))
dim(userRegistrations)
head(userRegistrations)
rev_user <- userRegistrations$userid
# - iterate over rev_user
for (i in 1:length(rev_user)) {
  # - SQL query
  sqlQuery <- paste("\"SELECT actor.actor_id, 
                        actor.actor_user, 
                        actor.actor_name, 
                        revision_actor_temp.revactor_timestamp 
                    FROM actor 
                    LEFT JOIN revision_actor_temp ON (actor.actor_id = revision_actor_temp.revactor_actor) 
                    WHERE (revision_actor_temp.revactor_timestamp >= 20201001000000 
                      AND actor.actor_user = ", rev_user[i], ");\"");
  ### --- output filename
  filename <- paste(dataDir,'userEdits', "_", i, ".tsv", sep = "")
  ### --- execute sql script:
  sqlLogInPre <- paste0('/usr/local/bin/analytics-mysql dewiki -e ')
  sqlInput <- paste(sqlQuery,
                    " > ",
                    filename,
                    sep = "")
  # - command:
  sqlCommand <- paste(sqlLogInPre, sqlInput)
  system(command = sqlCommand, wait = TRUE)
  # - report
  print(paste0("DONE: user ", i, "."))
}
### --- END run SQL scripts
# - load user edits:
lF <- list.files(dataDir)
lF <- lF[grepl("^userEdits_", lF)]
userEdits <- lapply(paste0(dataDir, lF), fread)
userEdits <- rbindlist(userEdits)
# - store user edits:
write.csv(userEdits, paste0(analyticsDir, 'userEdits.csv'))

0. Campaign Banners

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

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

The following dataset presents the banner impressions data:

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

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

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

1. Campaign Pageviews

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

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

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

1.1 Pageviews Overview

Chart 1.1.1 Daily Pageviews, aggregated across the campaign channels.

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

Table 1.1.1 Pageviews totals

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

1.2 Pageviews: Campaign Channels

Chart 1.2.1 Pageviews, by channels

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

Chart 1.2.2 Total Pageviews, by channels

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

Table 2.2.1 Total pageviews, by channels and by pages.

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

2. User Registrations

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

2.1 User Registrations Overview

lF <- list.files("_dailyReporting")
lF <- lF[grepl("^user_registrations", lF)]
dataSet <- fread(paste0("_dailyReporting/", lF))
dataSet <- filter(dataSet, 
                  !grepl("^test|^Test", dataSet$username))
dataSet$date <- paste(dataSet$year, 
                      ifelse(nchar(dataSet$month) == 1, paste0("0", dataSet$month), dataSet$month),
                      ifelse(nchar(dataSet$day) == 1, paste0("0", dataSet$day), dataSet$day),
                      sep = "-")
regUsers <- select(dataSet,
                   userid,
                   username, 
                   campaign)
# - 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")

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

3.2 User Edits by Campaign Channels

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

3.3 Total User Edits by Campaign Channels

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

3.4 Edit Classes

3.4.1 Edit Classes: all users

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

3.4.2A Edit Classes per Campaign Channel: WMDE_2020_challenge_1

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

3.4.2B Edit Classes per Campaign Channel: WMDE_2020_challenge_3

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

3.4.2C Edit Classes per Campaign Channel: WMDE_2020_challenge_4

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

3.4.2D Edit Classes per Campaign Channel: WMDE_2020_challenge_5

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

3.4.2E Edit Classes per Campaign Channel: WMDE_2020_challenge_6

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

3.4.2F Edit Classes per Campaign Channel: WMDE_2020_challenge_12

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

3.4.2G Edit Classes per Campaign Channel: WMDE_2020_challenge_14

userClass <- dataSet %>% 
  filter(campaign == "WMDE_2020_challenge_14") %>% 
  select(username) %>% 
  group_by(username) %>% 
  summarise(edits = n())
editBoundaries <- list(
  c(0, 1), 
  c(2, 4),
  c(5, 9),
  c(10, 20),
  c(21, 50),
  c(51, 100)
)
userClass$editClass <- sapply(userClass$edits, function(x) {
  wEC <- sapply(editBoundaries, function(y) {
    x >= y[1] & x <= y[2]
  })
  if (sum(wEC) == 0) {
    return("> 100")
  } else {
    return(paste0("(",
                  editBoundaries[[which(wEC)]][1],
                  " - ",
                  editBoundaries[[which(wEC)]][2], 
                  ")"
                  )
    )
  }
})
editClass <- as.data.frame(table(userClass$editClass), 
                           stringsAsFactors = F)
colnames(editClass) <- c('Edit Class', 'Num.Users')
editClass$order <- as.numeric(sapply(editClass$`Edit Class`, function(x) {
  lower <- str_extract(x, '[[:digit:]]+')
}))
editClass <- arrange(editClass, order)
editClass$order <- NULL
datatable(editClass)
LS0tCnRpdGxlOiAnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAgKFByZWxpbWluYXJ5IFJlcG9ydCknCmF1dGhvcjogIkdvcmFuIFMuIE1pbG92YW5vdmljLCBEYXRhIFNjaWVudGlzdCwgV01ERSIKZGF0ZTogIk9jdG9iZXIgMjAsIDIwMjAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0aGVtZTogc2ltcGxleAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDUKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQotLS0KCioqRmVlZGJhY2sqKiBzaG91bGQgYmUgc2VuZCB0byBgZ29yYW4ubWlsb3Zhbm92aWNfZXh0QHdpa2ltZWRpYS5kZWAuIAoKVGhlIGNhbXBhaWduIHdhcyBydW4gZnJvbSAyMDIwLzEwLzAxIHRvIDIwMjAvMTAvMTEuCgoqKkNVUlJFTlQgVVBEQVRFOioqIERhdGFzZXQgYXMgb2YgMjAyMC8xMC8yNC4KCmBgYHtyLCBlY2hvID0gRiwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEYsIHJlc3VsdHMgPSAnaGlkZSd9CiMgIWRpYWdub3N0aWNzIG9mZgojIyMgLS0tIFNldHVwCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoRFQpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpgYGAKCiMjIERhdGEgQWNxdWlzaXRvbgoKKipOT1RFOioqIHRoZSBEYXRhIEFjcXVpc2l0aW9uIGNvZGUgY2h1bmsgaXMgbm90IGZ1bGx5IHJlcHJvZHVjaWJsZSBmcm9tIHRoaXMgUmVwb3J0LiBUaGUgZGF0YSBhcmUgY29sbGVjdGVkIGJ5IHJ1bm5pbmcgdGhlIHNjcmlwdCBgMjAyMF9XaWtpcGVkaWFDaGFsbGVuZ2VfT2N0b2Jlcl8yMDIwX0NhbXBhaWduX1BST0RVQ1RJT04uUmAgb24gc3RhdDEwMDcuZXFpYWQud21uZXQsIGNvbGxlY3RpbmcgdGhlIGRhdGEgYXMgYC50c3ZgIGFuZCBgLmNzdmAgZmlsZXMsIGNvcHlpbmcgbWFudWFsbHksIGFuZCBwcm9jZXNzaW5nIGxvY2FsbHkuIEEgZGFpbHkgY3JvbnRhYiBqb2Igd2FzIHJ1biBmcm9tIGAyMDIwLzEwLzAxYCB0byBgMjAyMC8xMC8xMWAgdG8gY29sbGVjdCB0aGUgZGF0YSBmb3IgZGFpbHkgcmVwb3J0aW5nLiBVc2VyIHJlZ2lzdHJhdGlvbnMgYXJlIHRoZW4gY29sbGVjdGVkIHVudGlsIE5vdmVtYmVyIDExdGggMjAyMCwgYW5kIHVzZXIgZWRpdHMgYXJlIGNvbGxlY3RlZCB1bnRpbCB0aGUgZW5kIG9mIHRoZSB5ZWFyLiBUaGUgZGF0YSB1c2VkIGluIHRoaXMgcmVwb3J0IGFyZSBhZ2dyZWdhdGVzIG9mIHRoZSBkYWlseSBkYXRhc2V0cywgc2FuaXRpemVkIGFuZCBhbm9ueW1pemVkLiAgIAoKKipEQVRBOioqICgxKSBUaGUgYGV2ZW50LndtZGViYW5uZXJpbnRlcmFjdGlvbnNgIHNjaGVtYSBpcyB1c2VkIGZvciBiYW5uZXIgY2xpY2tzL2Nsb3NpbmcgKHNlZTogW1BoYWJdKGh0dHBzOi8vcGhhYnJpY2F0b3Iud2lraW1lZGlhLm9yZy9UMjYyNTM0IzY1MDE3OTMpKTsgKDIpIHRoZSBgd21mLndlYnJlcXVlc3RgIHRhYmxlIGlzIGEgc291cmNlIG9mIGJhbm5lciBpbXByZXNzaW9ucyBkYXRhOyAoMykgcGFnZXZpZXdzLCB1c2VyIHJlZ2lzdHJhdGlvbnMsIGFuZCB1c2VyIGVkaXRzIGFyZSBjb2xsZWN0ZWQgYnkgZm9sbG93aW5nIHRoZSBzdGFuZGFyZCBwcm9jZWR1cmUuIAoKIyMjIERhaWx5IFVwZGF0ZQoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRn0KCiMjIyAtLS0gV01ERV9XaWtpcGVkaWFfQ2hhbGxlbmdlX09jdG9iZXJfMjAyMF9DYW1wYWlnbi5QUk9EVUNUSU9OLlIKIyMjIC0tLSBodHRwczovL3BoYWJyaWNhdG9yLndpa2ltZWRpYS5vcmcvVDI2MjUzNAoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBDYW1wYWlnbiBEZXNjcmlwdGlvbiBhbmQgUGFyYW1ldGVycwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIC0tLSBUcmFja2luZyBQYXJ0IDE6IGJhbm5lciBjYW1wYWlnbgojIC0gUGxlYXNlIHByb3ZpZGUgZGFpbHkgcmVwb3J0cyBkdXJpbmcgdGhlIGJhbm5lciBjYW1wYWlnbiAoT2N0b2JlciAxc3QgLSAxMXRoKS4KCiMjIyAtLS0gVHJhY2tpbmcgUGFydCAyOiBlbWFpbGluZyBjYW1wYWlnbiBXaWtpcGVkaWEgY2hhbGxlbmdlCiMgLSBQbGVhc2UgcHJvdmlkZSBhIHRyYWNraW5nIHJlcG9ydCBmw7xyIHRoZSBXaWtpcGVkaWEgY2hhbGxlbmdlIGVtYWlsIGNhbXBhaWduIAojIC0gYXMgYWxyZWFkeSBpbXBsZW1lbnRlZCBzZWUgaHR0cHM6Ly9waGFicmljYXRvci53aWtpbWVkaWEub3JnL1QyNTE1MjQgZnJvbSBPY3RvYmVyIHRvIERlY2VtYmVyLgoKIyMjIC0tLVRpbWVsaW5lIGZvciB0aGUgd2hvbGUgcHJvamVjdAojIC0gU3RhcnQgb2YgdGhlIGJhbm5lciBjYW1wYWlnbjogMS4gT2N0b2JlciAyMDIwCiMgLSBFbmQgb2YgdGhlIGJhbm5lciBjYW1wYWlnbjogMTEuIE9jdG9iZXIgMjAyMAojIC0gVHJhY2tpbmcgdGVzdDogRW5kIG9mIFNlcHRlbWJlcgojIC0gTGFzdCBVc2VyIHNpZ25pbmcgdXA6IDExLiBPY3RvYmVyCiMgLSBQcmVsaW1pbmFyeSByZXBvcnQgZm9yIHRyYWNraW5nIHBhcnQgMTogc29tZXRpbWUgdW50aWwgZW5kIG9mIE9jdG9iZXIKIyAtIExhc3QgVXNlciBmaW5pc2hpbmcgbWFpbGluZyBjYW1wYWlnbjogTm92ZW1iZXIgMTF0aAojIC0gVHJhY2sgZWRpdGluZyBiZWhhdmlvciBzaXggd2Vla3MgYWZ0ZXIgZW5kIG9mIGNhbXBhaWduOiBEZWNlbWJlciAyMwojIC0gZmluYWwgcmVwb3J0IGZvciB0cmFja2luZyBwYXJ0IDEgYW5kIDI6IGJlZ2lubmluZyBvZiBKYW51YXJ5CgojIyMgLS0tIEJhbm5lciBDYW1wYWlnbiBpbmZvcm1hdGlvbgojIC0gVGhlcmUgd2lsbCBiZSBvbmUgY2FtcGFpZ24gd2l0aCA2IGRpZmZlcmVudCBiYW5uZXJzIChpbmNsdWRpbmcgbW9iaWxlIHZlcnNpb25zKSAKIyAtIHdoaWNoIHRhcmdldCByZWFkZXJzIGluIHdpa2lwZWRpYS4gQmFubmVyIGNhbXBhaWduIGdvYWwgYXJlIHJlZ2lzdHJhdGlvbnMgCiMgLSBmb3IgdGhlIGVtYWlsaW5nIGNhbXBhaWduLgoKIyAtIEZpbmQgYWxsIGRldGFpbHMgaW4gdGhpcyBkb2N1bWVudDogCiMgLSBodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9kb2N1bWVudC9kLzFrVGxMZHN4OV9yRC1rOFhjcDJ5RnZiOWZ0SER4a01uNi1NSjEtNWlodFM4L2VkaXQKCiMjIyAtLS0gQ2FtcGFpZ24gVGFncyBhbmQgTGFuZGluZ3BhZ2VzCiMgLSBMYW5kaW5nIFBhZ2UgMTogaHR0cHM6Ly93d3cud2lraW1lZGlhLmRlL3dpa2lwZWRpYS1jaGFsbGVuZ2UKCiMgLSBCYW5uZXIgMQojIC0gP2NhbXBhaWduPVdNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX2N0cmwKIyAtIGh0dHBzOi8vd3d3Lndpa2ltZWRpYS5kZS93aWtpcGVkaWEtY2hhbGxlbmdlP2NhbXBhaWduPVdNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX2N0cmwKIyAtID9jYW1wYWlnbj1XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF9jdHJsX2lwYWQKIyAtIGh0dHBzOi8vd3d3Lndpa2ltZWRpYS5kZS93aWtpcGVkaWEtY2hhbGxlbmdlP2NhbXBhaWduPVdNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX2N0cmxfaXBhZAojIC0gP2NhbXBhaWduPVdNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX2N0cmxfbW9iaWxlCiMgLSBodHRwczovL3d3dy53aWtpbWVkaWEuZGUvd2lraXBlZGlhLWNoYWxsZW5nZT9jYW1wYWlnbj1XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF9jdHJsX21vYmlsZQoKIyAtIEJhbm5lciAyCiMgLSA/Y2FtcGFpZ249V01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfdmFyCiMgLSBodHRwczovL3d3dy53aWtpbWVkaWEuZGUvd2lraXBlZGlhLWNoYWxsZW5nZT9jYW1wYWlnbj1XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXIKIyAtID9jYW1wYWlnbj1XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXJfaXBhZAojIC0gaHR0cHM6Ly93d3cud2lraW1lZGlhLmRlL3dpa2lwZWRpYS1jaGFsbGVuZ2U/Y2FtcGFpZ249V01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfdmFyX2lwYWQKIyAtID9jYW1wYWlnbj1XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXJfbW9iaWxlCiMgLSBodHRwczovL3d3dy53aWtpbWVkaWEuZGUvd2lraXBlZGlhLWNoYWxsZW5nZT9jYW1wYWlnbj1XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXJfbW9iaWxlCgojIC0gdGFyZ2V0IGdyb3VwcwojIC0gQmFubmVycyB3aWxsIGJlIHRhcmdldGVkIHRvIG5vbi1sb2dnZWQgaW4gdXNlcnMgb25seS4KCiMgLSBUaGUgYS9iIHRlc3Qgc2NvcGUgaXMgY29tcGFyaW5nIHR3byBkaWZmZXJlbnQgY2xhaW1zIGluIHRoZSBiYW5uZXIgdGV4dAoKIyAtIFRyYWNraW5nIGlzIGRlcGVuZGVudCBvbiBldmVudGxvZ2dpbmcuIAojIC0gVGhlIHJlcXVpcmVkIGV2ZW50IHNjaGVtZSB3aWxsIGJlIHVzZWQgZm9yIGNsb3NpbmcgY2xpY2tzIG9ubHkuIAojIC0gaHR0cHM6Ly9waGFicmljYXRvci53aWtpbWVkaWEub3JnL1QyNTA3OTEKCiMjIyAtLS0gZGFpbHkgcmVwb3J0aW5nIGR1cmluZyBjYW1wYWlnbgojIC0gVGhlIGZvbGxvd2luZyBpbmZvcm1hdGlvbiBzaG91bGQgYmUgaW5jbHVkZWQgaW4gdGhlIGRhaWx5IHJlcG9ydGluZzoKIyAtIGltcHJlc3Npb25zIHBlciBiYW5uZXIKIyAtIGNsaWNrcyBwZXIgYmFubmVyLyBwYWdlIHZpZXdzIHBlciBsYW5kaW5nIHBhZ2UKIyAtIGNsb3NpbmcgcmF0ZSBvZiBiYW5uZXJzCgojIyMgLS0tIGxpYnJhcmllcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKCiMjIyAtLS0gZGlyIHN0cnVjdHVyZQpjYW1wYWlnblBhdGggPC0gJy9ob21lL2dvcmFuc20vQW5hbHl0aWNzL05ld0VkaXRvcnMvQ2FtcGFpZ25zLzIwMjBfV2lraXBlZGlhX0NoYWxsZW5nZV9PY3RvYmVyXzIwMjBfQ2FtcGFpZ24vJwpkYXRhRGlyIDwtIHBhc3RlMChjYW1wYWlnblBhdGgsICJfZGF0YS8iKQphbmFseXRpY3NEaXIgPC0gcGFzdGUwKGNhbXBhaWduUGF0aCwgIl9hbmFseXRpY3MvIikKIyMjIC0tLSBjYW1wYWlnbiBzcGVjaWZpY3MKY2FtcGFpZ25OYW1lIDwtICdXaWtpcGVkaWFfQ2hhbGxlbmdlX09jdG9iZXJfMjAyMCcKCiMjIyAtLS0gZGV0ZXJtaW5lIGNldERheQpjZXREYXkgPC0gU3lzLnRpbWUoKQpjZXREYXkKYXR0cihjZXREYXksICJ0em9uZSIpIDwtICJFdXJvcGUvQmVybGluIgojIC0gb25lIGRheSBiZWhpbmQgZm9yIGNyb250YWIKIyAtIChpLmUuIHdhaXRpbmcgZm9yIHdtZi53ZWJyZXF1ZXN0IHRvIGNvbXBsZXRlIGlzIGRhdGEgYWNxdWlzaXRpb24pCmNldERheSA8LSB5bWQoCiAgc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKGNldERheSksIAogICAgICAgICAgIHNwbGl0ID0gIiAiLCAKICAgICAgICAgICBmaXhlZCA9IFQpW1sxXV1bMV0KKSAtIDEKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gQmFubmVyIEltcHJlc3Npb25zCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgLSBmdW5jdGlvbjogd21kZV9jb2xsZWN0X2Jhbm5lcl9pbXByZXNzaW9ucwp3bWRlX2NvbGxlY3RfYmFubmVyX2ltcHJlc3Npb25zIDwtIGZ1bmN0aW9uKHVyaV9ob3N0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcGF0aCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIpIHsKICAKICAjIC0gTk9URToKICAjIC0gZXhwZWN0ZWQgZm9ybWF0IGZvciBjZXREYXkgaXM6IFlZWVktTU0tREQKICAKICAjIC0gdG8gZGF0YURpcgogIHNldHdkKGRhdGFEaXIpCiAgCiAgIyAtIGxpYnJhcmllcwogIGxpYnJhcnkoc3RyaW5ncikKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgZGF0ZXRpbWVfY29uZGl0aW9uCiAgY2V0X2NvbmRpdGlvbiA8LSBzZXEoCiAgICBmcm9tID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMDowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICB0byA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDIzOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIGJ5ID0gImhvdXIiCiAgKSAKICBhdHRyKGNldF9jb25kaXRpb24sICJ0em9uZSIpIDwtICJVVEMiCiAgY2V0X2NvbmRpdGlvbiA8LSBhcy5jaGFyYWN0ZXIoY2V0X2NvbmRpdGlvbikKICBjZXRfY29uZGl0aW9uIDwtIHVubGlzdChzdHJfZXh0cmFjdF9hbGwoY2V0X2NvbmRpdGlvbiwgIl4oW1s6ZGlnaXQ6XV18XFxzfC0pKiIpKQogIGNldF95ZWFycyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsxXQogICAgfSkKICBjZXRfbW9udGhzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzJdCiAgICB9KQogIGNldF9tb250aHMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X21vbnRocykKICBjZXRfZGF5cyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVszXQogICAgfSkKICBjZXRfZGF5cyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfZGF5cykKICBjZXRfaG91cnMgPC0gc2FwcGx5KHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCAKICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgeFsyXQogICAgICAgICAgICAgICAgICAgICAgfSkKICBjZXRfaG91cnMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2hvdXJzKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlMCgKICAgICJ5ZWFyID0gIiwgY2V0X3llYXJzLCAiIEFORCAiLAogICAgIm1vbnRoID0gIiwgY2V0X21vbnRocywgIiBBTkQgIiwKICAgICJkYXkgPSAiLCBjZXRfZGF5cywgIiBBTkQgIiwgCiAgICAiaG91ciA9ICIsIGNldF9ob3VycwogICkKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZSgiKCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGV0aW1lQ29uZGl0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSB1cmlfcGF0aF9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9wYXRoKSA+IDEpIHsKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX3BhdGhfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfcGF0aCA9ICciLCB1cmlfcGF0aCwgIiciKQogIH0KICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX2hvc3RfY29uZGl0aW9uCiAgaWYgKGxlbmd0aCh1cmlfaG9zdCkgPiAxKSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfaG9zdCA9ICciLCB1cmlfaG9zdCwgIiciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiA9IHBhc3RlMCgidXJpX2hvc3QgPSAnIiwgdXJpX2hvc3QsICInIikKICB9CiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9xdWVyeV9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9xdWVyeSkgPiAxKSB7CiAgICB1cmlfcXVlcnlfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfcXVlcnkgTElLRSAnJSIsIHVyaV9xdWVyeSwgIiUnIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9xdWVyeV9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9xdWVyeSBMSUtFICclIiwgdXJpX3F1ZXJ5LCAiJSciKQogIH0KICAKICAjIC0gY29tcG9zZSBIaXZlUUwgcXVlcnkKICBoaXZlUXVlcnkgPC0gcGFzdGUwKAogICAgIlVTRSB3bWY7CiAgICBTRUxFQ1QgdXJpX3F1ZXJ5IEZST00gd2VicmVxdWVzdAogICAgV0hFUkUgKCIsCiAgICB1cmlfaG9zdF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcGF0aF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcXVlcnlfY29uZGl0aW9uLCAiIEFORCAiLAogICAgIigiLCBkYXRldGltZUNvbmRpdGlvbiwgIikiLAogICAgIik7IgogICAgKQogIAogICMgLSB3cml0ZSBocWwKICB3cml0ZShoaXZlUXVlcnksIHF1ZXJ5RmlsZSkKICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0ICsgS2VyYmVyb3MgQXV0aAogIGhpdmVBcmdzIDwtICdzdWRvIC11IGFuYWx5dGljcy1wcml2YXRlZGF0YSBrZXJiZXJvcy1ydW4tY29tbWFuZCBhbmFseXRpY3MtcHJpdmF0ZWRhdGEgL3Vzci9sb2NhbC9iaW4vYmVlbGluZSAtZicKICBoaXZlSW5wdXQgPC0gcGFzdGUwKHF1ZXJ5RmlsZSwgJyA+ICcsIGZpbGVOYW1lKQogICMgLSBjb21tYW5kOgogIGhpdmVDb21tYW5kIDwtIHBhc3RlKGhpdmVBcmdzLCBoaXZlSW5wdXQpCiAgcmV0dXJuKAogICAgc3lzdGVtKGNvbW1hbmQgPSBoaXZlQ29tbWFuZCwgd2FpdCA9IFRSVUUpCiAgKQp9CgojIC0gc2V0IHBhcmFtcyB0byB3bWRlX2NvbGxlY3RfYmFubmVyX2ltcHJlc3Npb25zCiMgLSBmb3IgdGhlIEF1dHVtbiBCYW5uZXIgQ2FtcGFpZ24gMjAxOQp1cmlfaG9zdCA8LSBjKCdkZS53aWtpcGVkaWEub3JnJywgJ2RlLm0ud2lraXBlZGlhLm9yZycpCnVyaV9wYXRoICA8LSAnL2JlYWNvbi9pbXByZXNzaW9uJwp1cmlfcXVlcnkgPC0gYygnV01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfY3RybCcsICdXTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF9jdHJsX2lwYWQnLAogICAgICAgICAgICAgICAnV01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfY3RybF9tb2JpbGUnLCdXTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXInLAogICAgICAgICAgICAgICAnV01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfdmFyX2lwYWQnLCdXTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXJfbW9iaWxlJykKcXVlcnlGaWxlIDwtICd3aWtpcGVkaWFfY2hhbGxlbmdlX09jdDIwMjBfQmFubmVySW1wcmVzc2lvbnMuaHFsJwpmaWxlTmFtZSA8LSBwYXN0ZTAoZGF0YURpciwgImJhbm5lckltcHJlc3Npb25zXyIsIGNldERheSwgIi50c3YiKQoKIyAtIGNvbGxlY3QgQmFubmVyIEltcHJlc3Npb24gZGF0YQp3bWRlX2NvbGxlY3RfYmFubmVyX2ltcHJlc3Npb25zKHVyaV9ob3N0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9xdWVyeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIpCgojIC0gZnVuY3Rpb246IHdtZGVfcHJvY2Vzc19iYW5uZXJfaW1wcmVzc2lvbnMKd21kZV9wcm9jZXNzX2Jhbm5lcl9pbXByZXNzaW9ucyA8LSBmdW5jdGlvbihmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSkgewogIAogICMgLSB0byBkYXRhRGlyCiAgc2V0d2QoZGF0YURpcikKICAKICAjIC0gbGlicmFyaWVzCiAgbGlicmFyeShzdHJpbmdyKQogIGxpYnJhcnkoZHBseXIpCiAgCiAgIyAtIGxvYWQKICBiYW5uZXJEYXRhIDwtIHJlYWQuZGVsaW0oZmlsZU5hbWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICBjb2xuYW1lcyhiYW5uZXJEYXRhKSA8LSAndXJpX3F1ZXJ5JwogIAogICMgLSBjbGVhbgogIHdTdGFydCA8LSB3aGljaChiYW5uZXJEYXRhJHVyaV9xdWVyeSA9PSAidXJpX3F1ZXJ5IikKICBiYW5uZXJEYXRhIDwtIGJhbm5lckRhdGFbKHdTdGFydCArIDEpOihkaW0oYmFubmVyRGF0YSlbMV0gLSAyKSwgXQogIAogICMgLSBzcGxpdAogIGJhbm5lckRhdGEgPC0gc3Ryc3BsaXQoYmFubmVyRGF0YSwgc3BsaXQgPSAiJiIsIGZpeGVkID0gVCkKICAjIC0gZXh0cmFjdCByZWxldmFudCBmaWVsZHMKICAjIC0gYmFubmVyOgogIGJhbm5lciA8LSBzYXBwbHkoYmFubmVyRGF0YSwgZnVuY3Rpb24oeCkgewogICAgeFt3aGljaChncmVwbCgiXmJhbm5lcj0iLCB4KSldCiAgfSkKICBiYW5uZXIgPC0gZ3N1YigiXmJhbm5lcj0iLCAiIiwgYmFubmVyKQogICMgLSByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZToKICByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSA8LSBzYXBwbHkoYmFubmVyRGF0YSwgZnVuY3Rpb24oeCkgewogICAgeFt3aGljaChncmVwbCgiXnJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlPSIsIHgpKV0KICB9KQogIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlIDwtIGFzLm51bWVyaWMoCiAgICBnc3ViKCJecmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGU9IiwgIiIsIHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlKQogICkKICAjIC0gcmVzdWx0OgogIHJlc3VsdCA8LSBzYXBwbHkoYmFubmVyRGF0YSwgZnVuY3Rpb24oeCkgewogICAgeFt3aGljaChncmVwbCgiXnJlc3VsdD0iLCB4KSldCiAgfSkKICByZXN1bHQgPC0gZ3N1YigiXnJlc3VsdD0iLCAiIiwgcmVzdWx0KQogIAogICMgLSBjb21wb3NlIHRhYmxlOgogIGJhbm5lck9ic2VydmF0aW9ucyA8LSBkYXRhLmZyYW1lKGJhbm5lciA9IGJhbm5lciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUgPSByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gcmVzdWx0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICAKICAjIC0gZmlsdGVyIGZvciByZXN1bHQ9c2hvdwogIGJhbm5lck9ic2VydmF0aW9ucyA8LSBkcGx5cjo6ZmlsdGVyKGJhbm5lck9ic2VydmF0aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPT0gInNob3ciKQogIAogICMgLSBjb3JyZWN0aW9uIGZvciByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZQogIGJhbm5lck9ic2VydmF0aW9ucyRyZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSA8LSAKICAgIDEvYmFubmVyT2JzZXJ2YXRpb25zJHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlCiAgCiAgIyAtIGFnZ3JlZ2F0ZToKICBiYW5uZXJPYnNlcnZhdGlvbnMgPC0gYmFubmVyT2JzZXJ2YXRpb25zICU+JSAKICAgIGRwbHlyOjpzZWxlY3QoYmFubmVyLCByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSkgJT4lIAogICAgZHBseXI6Omdyb3VwX2J5KGJhbm5lcikgJT4lIAogICAgZHBseXI6OnN1bW1hcmlzZShpbXByZXNzaW9ucyA9IHN1bShyZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSkpCiAgCiAgIyAtIGFkZCBjZXREYXksIG1lCiAgYmFubmVyT2JzZXJ2YXRpb25zJGRhdGUgPC0gY2V0RGF5CiAgYmFubmVyT2JzZXJ2YXRpb25zJGNhbXBhaWduIDwtIGNhbXBhaWduTmFtZQogIAogICMgLSBzdG9yZToKICB3cml0ZS5jc3YoYmFubmVyT2JzZXJ2YXRpb25zLCAKICAgICAgICAgICAgcGFzdGUwKCJiYW5uZXJJbXByZXNzaW9uc0FnZ3JlZ2F0ZWRfIiwKICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICIuY3N2IgogICAgICAgICAgICApCiAgKQogIAp9CgojIC0gd3JhbmdsZSBCYW5uZXIgSW1wcmVzc2lvbiBkYXRhCndtZGVfcHJvY2Vzc19iYW5uZXJfaW1wcmVzc2lvbnMoZmlsZU5hbWUgPSBmaWxlTmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpciA9IGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUgPSBjYW1wYWlnbk5hbWUpCgoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBCYW5uZXIgSW50ZXJhY3Rpb25zOgojIyMgLS0tIHZpYSBldmVudC5XTURFQmFubmVySW50ZXJhY3Rpb25zCiMjIyAtLS0gaHR0cHM6Ly9tZXRhLndpa2ltZWRpYS5vcmcvd2lraS9TY2hlbWE6V01ERUJhbm5lckludGVyYWN0aW9ucwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIHNlbGVjdCBkdCwgZXZlbnQuYmFubmVyTmFtZSwgZXZlbnQuYmFubmVyQWN0aW9uLCBldmVudC5iYW5uZXJJbXByZXNzaW9ucywgZXZlbnQudXNlcklEIAojIC0gZnJvbSBldmVudC53bWRlYmFubmVyaW50ZXJhY3Rpb25zIHdoZXJlIHllYXI9MjAyMCBhbmQgbW9udGg9NSBhbmQgKGRheT0xMSBvciBkYXk9MTIgb3IgZGF5PTEzKTsKCiMgLSBmdW5jdGlvbjogd21kZV9jb2xsZWN0X3BhZ2V2aWV3cwp3bWRlX2Jhbm5lcl9hY3Rpb25zIDwtIGZ1bmN0aW9uKHVyaV9xdWVyeV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmFseXRpY3NEaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lKSB7CiAgCiAgIyAtIE5PVEU6CiAgIyAtIGV4cGVjdGVkIGZvcm1hdCBmb3IgY2V0RGF5IGlzOiBZWVlZLU1NLURECiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIGRhdGV0aW1lX2NvbmRpdGlvbgogIGNldF9jb25kaXRpb24gPC0gc2VxKAogICAgZnJvbSA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDA6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgdG8gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAyMzowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICBieSA9ICJob3VyIgogICkgCiAgYXR0cihjZXRfY29uZGl0aW9uLCAidHpvbmUiKSA8LSAiVVRDIgogIGNldF9jb25kaXRpb24gPC0gYXMuY2hhcmFjdGVyKGNldF9jb25kaXRpb24pCiAgY2V0X2NvbmRpdGlvbiA8LSB1bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKGNldF9jb25kaXRpb24sICJeKFtbOmRpZ2l0Ol1dfFxcc3wtKSoiKSkKICBjZXRfeWVhcnMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMV0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsyXQogICAgfSkKICBjZXRfbW9udGhzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9tb250aHMpCiAgY2V0X2RheXMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bM10KICAgIH0pCiAgY2V0X2RheXMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2RheXMpCiAgY2V0X2hvdXJzIDwtIHNhcHBseShzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgCiAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHhbMl0KICAgICAgICAgICAgICAgICAgICAgIH0pCiAgY2V0X2hvdXJzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9ob3VycykKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZTAoCiAgICAieWVhciA9ICIsIGNldF95ZWFycywgIiBBTkQgIiwKICAgICJtb250aCA9ICIsIGNldF9tb250aHMsICIgQU5EICIsCiAgICAiZGF5ID0gIiwgY2V0X2RheXMsICIgQU5EICIsIAogICAgImhvdXIgPSAiLCBjZXRfaG91cnMKICApCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUoIigiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZUNvbmRpdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgZXZlbnRCYW5uZXJOYW1lX2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX3F1ZXJ5KSA+IDEpIHsKICAgIGV2ZW50QmFubmVyTmFtZV9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoImV2ZW50LmJhbm5lck5hbWUgTElLRSAnJSIsIHVyaV9xdWVyeV9maWx0ZXIsICIlJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICBldmVudEJhbm5lck5hbWVfY29uZGl0aW9uID0gcGFzdGUwKCJldmVudC5iYW5uZXJOYW1lIExJS0UgJyUiLCB1cmlfcXVlcnlfZmlsdGVyLCAiJSciKQogIH0KICAKICAjIC0gY29tcG9zZSBIaXZlUUwgcXVlcnkKICBoaXZlUXVlcnkgPC0gcGFzdGUwKCAKICAgICJzZWxlY3QgZHQsIGV2ZW50LmJhbm5lck5hbWUsIGV2ZW50LmJhbm5lckFjdGlvbiwgZXZlbnQuYmFubmVySW1wcmVzc2lvbnMsIGV2ZW50LnVzZXJJRCBmcm9tIGV2ZW50LndtZGViYW5uZXJpbnRlcmFjdGlvbnMgCiAgICBXSEVSRSAoIiwKICAgIGV2ZW50QmFubmVyTmFtZV9jb25kaXRpb24sICIgQU5EICIsCiAgICAiKCIsIGRhdGV0aW1lQ29uZGl0aW9uLCAiKSIsCiAgICAiKTsiCiAgICApCiAgCiAgIyAtIHdyaXRlIGhxbAogIHdyaXRlKGhpdmVRdWVyeSwgcXVlcnlGaWxlKQogICMgLSBleGVjdXRlIGhxbCBzY3JpcHQ6CiAga2VyYmVyb3NQcmVmaXggPC0gCiAgICAnc3VkbyAtdSBhbmFseXRpY3MtcHJpdmF0ZWRhdGEga2VyYmVyb3MtcnVuLWNvbW1hbmQgYW5hbHl0aWNzLXByaXZhdGVkYXRhICcKICAjIC0gS2VyYmVyb3MgaW5pdAogIHN5c3RlbShjb21tYW5kID0gcGFzdGUwKGtlcmJlcm9zUHJlZml4LCAnIGhkZnMgZGZzIC1scycpLCAKICAgICAgICAgd2FpdCA9IFQpCiAgIyAtIFJ1biBxdWVyeQogIHF1ZXJ5IDwtIHN5c3RlbShjb21tYW5kID0gcGFzdGUoa2VyYmVyb3NQcmVmaXgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJy91c3IvbG9jYWwvYmluL2JlZWxpbmUgLS1pbmNyZW1lbnRhbD10cnVlIC0tc2lsZW50IC1mICInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKGRhdGFEaXIsIHF1ZXJ5RmlsZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnIiA+ICcsIGRhdGFEaXIsIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpLAogICAgICAgICAgICAgICAgICB3YWl0ID0gVFJVRSkKICAKICAjIC0gV3JhbmdsZSBCYW5uZXIgSW50ZXJhY3Rpb25zCiAgIyAtIGxvYWQKICBiYW5uZXJEYXRhIDwtIHRyeUNhdGNoKHsKICAgIGFzLmRhdGEuZnJhbWUoZnJlYWQocGFzdGUwKGRhdGFEaXIsIGZpbGVOYW1lKSkpCiAgfSwKICBlcnJvciA9IGZ1bmN0aW9uKGNvbmRpdGlvbikgewogICAgcmV0dXJuKEZBTFNFKQogIH0pCiAgCiAgIyAtIHByb2Nlc3MKICBpZiAoY2xhc3MoYmFubmVyRGF0YSkgPT0gJ2xvZ2ljYWwnKSB7CiAgICByZXR1cm4oRkFMU0UpIAogIH0gZWxzZSB7IAogICAgIyAtIGJhbm5lclNlZW4KICAgIGJhbm5lclNlZW4gPC0gYmFubmVyRGF0YSAlPiUgCiAgICAgIGRwbHlyOjpzZWxlY3QoYmFubmVybmFtZSwgdXNlcmlkKQogICAgYmFubmVyU2VlbiA8LSBiYW5uZXJTZWVuWyFkdXBsaWNhdGVkKGJhbm5lclNlZW4pLCBdCiAgICBiYW5uZXJTZWVuIDwtIGJhbm5lclNlZW4gJT4lIAogICAgICBkcGx5cjo6c2VsZWN0KGJhbm5lcm5hbWUpICU+JSAKICAgICAgZHBseXI6Omdyb3VwX2J5KGJhbm5lcm5hbWUpICU+JSAKICAgICAgZHBseXI6OnN1bW1hcmlzZShzZWVuX2J5ID0gbigpKQogICAgIyAtIGJhbm5lckNsb3NlZAogICAgYmFubmVyQ2xvc2VkIDwtIGJhbm5lckRhdGEgJT4lIAogICAgICBkcGx5cjo6ZmlsdGVyKGJhbm5lcmFjdGlvbiA9PSAiYmFubmVyLWNsb3NlZCIpICU+JSAKICAgICAgZHBseXI6OnNlbGVjdChiYW5uZXJuYW1lLCBiYW5uZXJpbXByZXNzaW9ucykgJT4lIAogICAgICBkcGx5cjo6Z3JvdXBfYnkoYmFubmVybmFtZSkgJT4lCiAgICAgIGRwbHlyOjpzdW1tYXJpc2UoY2xvc2VkX2J5ID0gbigpLCBtZWFuX2Nsb3NlX2ltcCA9IHJvdW5kKG1lYW4oYmFubmVyaW1wcmVzc2lvbnMpLCAyKSkKICAgICMgLSBiYW5uZXJDbGlja2VkCiAgICBiYW5uZXJDbGlja2VkIDwtIGJhbm5lckRhdGEgJT4lIAogICAgICBkcGx5cjo6ZmlsdGVyKGJhbm5lcmFjdGlvbiA9PSAiYmFubmVyLWNsaWNrZWQiKSAlPiUgCiAgICAgIGRwbHlyOjpzZWxlY3QoYmFubmVybmFtZSwgYmFubmVyaW1wcmVzc2lvbnMpICU+JSAKICAgICAgZHBseXI6Omdyb3VwX2J5KGJhbm5lcm5hbWUpICU+JQogICAgICBkcGx5cjo6c3VtbWFyaXNlKGNsaWNrZWRfYnkgPSBuKCksIG1lYW5fY2xpY2tfaW1wID0gcm91bmQobWVhbihiYW5uZXJpbXByZXNzaW9ucyksIDIpKQogICAgIyAtIHdob0NsaWNrZWQKICAgIHdob0NsaWNrZWQgPC0gYmFubmVyRGF0YSAlPiUgCiAgICAgIGRwbHlyOjpmaWx0ZXIoYmFubmVyYWN0aW9uID09ICJiYW5uZXItY2xpY2tlZCIpCiAgICB3aG9DbGlja2VkIDwtIGRhdGEuZnJhbWUodXNlcmlkID0gdW5pcXVlKHdob0NsaWNrZWQkdXNlcmlkKSkKICAgICMgLSBzdG9yZToKICAgIHdyaXRlLmNzdih3aG9DbGlja2VkLCAKICAgICAgICAgICAgICBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAKICAgICAgICAgICAgICAgICAgICAgIndob0NsaWNrZWRfIiwKICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoCiAgICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoZmlsZU5hbWUsIHNwbGl0ID0gIl8iLCBmaXhlZCA9IFQpW1sxXV1bMl0sCiAgICAgICAgICAgICAgICAgICAgICAgc3BsaXQgPSAiLiIsIAogICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXSwKICAgICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICAgKQogICAgKQogICAgIyAtIGpvaW4KICAgIGJhbm5lckRhdGEgPC0gYmFubmVyU2VlbiAlPiUgCiAgICAgIGRwbHlyOjpsZWZ0X2pvaW4oYmFubmVyQ2xvc2VkLCAnYmFubmVybmFtZScpICU+JSAKICAgICAgZHBseXI6OmxlZnRfam9pbihiYW5uZXJDbGlja2VkLCAnYmFubmVybmFtZScpCiAgICBiYW5uZXJEYXRhJGNsb3NlX3JhdGUgPC0gcm91bmQoYmFubmVyRGF0YSRjbG9zZWRfYnkvYmFubmVyRGF0YSRzZWVuX2J5LCAyKQogICAgYmFubmVyRGF0YSRjbGlja19yYXRlIDwtIHJvdW5kKGJhbm5lckRhdGEkY2xpY2tlZF9ieS9iYW5uZXJEYXRhJHNlZW5fYnksIDIpCiAgICAjIC0gZGF0ZSwgY2FtcGFpZ24KICAgIGJhbm5lckRhdGEkZGF5IDwtIGNldERheQogICAgYmFubmVyRGF0YSRjYW1wYWlnbiA8LSBjYW1wYWlnbk5hbWUKICAgIAogICAgIyAtIHN0b3JlOgogICAgd3JpdGUuY3N2KGJhbm5lckRhdGEsIAogICAgICAgICAgICAgIHBhc3RlMChhbmFseXRpY3NEaXIsIAogICAgICAgICAgICAgICAgICAgICAiYmFubmVySW50ZXJhY3Rpb25zQWdncmVnYXRlZF8iLAogICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdCgKICAgICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdChmaWxlTmFtZSwgc3BsaXQgPSAiXyIsIGZpeGVkID0gVClbWzFdXVsyXSwKICAgICAgICAgICAgICAgICAgICAgICBzcGxpdCA9ICIuIiwgCiAgICAgICAgICAgICAgICAgICAgICAgZml4ZWQgPSBUKVtbMV1dWzFdLAogICAgICAgICAgICAgICAgICAgICAiLmNzdiIKICAgICAgICAgICAgICApCiAgICApCiAgfQogIAp9CgojIC0gc2V0IHBhcmFtcyBmb3Igd21kZV9iYW5uZXJfYWN0aW9ucygpCnF1ZXJ5RmlsZSA8LSBwYXN0ZTAoY2FtcGFpZ25OYW1lLCAiX2Jhbm5lckludGVyYWN0aW9ucy5ocWwiKQpmaWxlTmFtZSA8LSBwYXN0ZTAoImJhbm5lckludGVyYWN0aW9uc18iLCBjZXREYXksICIudHN2IikKdXJpX3F1ZXJ5X2ZpbHRlciA8LSAnV01ERV9jaGFsbGVuZ2VibnJfZmFsbDIwMjBfJwpiYW5uZXJfc3RhdHVzIDwtIHdtZGVfYmFubmVyX2FjdGlvbnModXJpX3F1ZXJ5X2ZpbHRlciA9IHVyaV9xdWVyeV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXkgPSBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUgPSBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSA9IGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5hbHl0aWNzRGlyID0gYW5hbHl0aWNzRGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSA9IGNhbXBhaWduTmFtZSkKCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIFBhZ2V2aWV3cwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIGZ1bmN0aW9uOiB3bWRlX2NvbGxlY3RfcGFnZXZpZXdzCndtZGVfY29sbGVjdF9wYWdldmlld3MgPC0gZnVuY3Rpb24odXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyKSB7CiAgCiAgIyAtIE5PVEU6CiAgIyAtIGV4cGVjdGVkIGZvcm1hdCBmb3IgY2V0RGF5IGlzOiBZWVlZLU1NLURECiAgCiAgIyAtIHRvIGRhdGFEaXIKICBzZXR3ZChkYXRhRGlyKQogIAogICMgLSBsaWJyYXJpZXMKICBsaWJyYXJ5KHN0cmluZ3IpCiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIGRhdGV0aW1lX2NvbmRpdGlvbgogIGNldF9jb25kaXRpb24gPC0gc2VxKAogICAgZnJvbSA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDA6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgdG8gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAyMzowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICBieSA9ICJob3VyIgogICkgCiAgYXR0cihjZXRfY29uZGl0aW9uLCAidHpvbmUiKSA8LSAiVVRDIgogIGNldF9jb25kaXRpb24gPC0gYXMuY2hhcmFjdGVyKGNldF9jb25kaXRpb24pCiAgY2V0X2NvbmRpdGlvbiA8LSB1bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKGNldF9jb25kaXRpb24sICJeKFtbOmRpZ2l0Ol1dfFxcc3wtKSoiKSkKICBjZXRfeWVhcnMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMV0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsyXQogICAgfSkKICBjZXRfbW9udGhzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9tb250aHMpCiAgY2V0X2RheXMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bM10KICAgIH0pCiAgY2V0X2RheXMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2RheXMpCiAgY2V0X2hvdXJzIDwtIHNhcHBseShzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgCiAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHhbMl0KICAgICAgICAgICAgICAgICAgICAgIH0pCiAgY2V0X2hvdXJzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9ob3VycykKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZTAoCiAgICAieWVhciA9ICIsIGNldF95ZWFycywgIiBBTkQgIiwKICAgICJtb250aCA9ICIsIGNldF9tb250aHMsICIgQU5EICIsCiAgICAiZGF5ID0gIiwgY2V0X2RheXMsICIgQU5EICIsIAogICAgImhvdXIgPSAiLCBjZXRfaG91cnMKICApCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUoIigiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZUNvbmRpdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX2hvc3RfY29uZGl0aW9uCiAgaWYgKGxlbmd0aCh1cmlfaG9zdCkgPiAxKSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9ob3N0ID0gJyIsIHVyaV9ob3N0LCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9ob3N0ID0gJyIsIHVyaV9ob3N0LCAiJyIpCiAgfQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSB1cmlfcGF0aF9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9wYXRoKSA+IDEpIHsKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX3BhdGhfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfcGF0aCA9ICciLCB1cmlfcGF0aCwgIiciKQogIH0KICAKICAjIC0gY29tcG9zZSBIaXZlUUwgcXVlcnkKICBoaXZlUXVlcnkgPC0gcGFzdGUwKCAKICAgICJVU0Ugd21mOwogICAgU0VMRUNUIHVyaV9ob3N0LCB1cmlfcGF0aCwgdXJpX3F1ZXJ5LCByZWZlcmVyIEZST00gd2VicmVxdWVzdAogICAgV0hFUkUgKCIsCiAgICB1cmlfaG9zdF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcGF0aF9jb25kaXRpb24sICIgQU5EICIsCiAgICAiKCIsIGRhdGV0aW1lQ29uZGl0aW9uLCAiKSIsCiAgICAiKTsiCiAgICApCiAgCiAgIyAtIHdyaXRlIGhxbAogIHdyaXRlKGhpdmVRdWVyeSwgcXVlcnlGaWxlKQogICMgLSBleGVjdXRlIGhxbCBzY3JpcHQ6CiAga2VyYmVyb3NQcmVmaXggPC0gCiAgICAnc3VkbyAtdSBhbmFseXRpY3MtcHJpdmF0ZWRhdGEga2VyYmVyb3MtcnVuLWNvbW1hbmQgYW5hbHl0aWNzLXByaXZhdGVkYXRhICcKICAjIC0gS2VyYmVyb3MgaW5pdAogIHN5c3RlbShjb21tYW5kID0gcGFzdGUwKGtlcmJlcm9zUHJlZml4LCAnIGhkZnMgZGZzIC1scycpLCAKICAgICAgICAgd2FpdCA9IFQpCiAgIyAtIFJ1biBxdWVyeQogIHF1ZXJ5IDwtIHN5c3RlbShjb21tYW5kID0gcGFzdGUoa2VyYmVyb3NQcmVmaXgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJy91c3IvbG9jYWwvYmluL2JlZWxpbmUgLS1pbmNyZW1lbnRhbD10cnVlIC0tc2lsZW50IC1mICInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKGRhdGFEaXIsIHF1ZXJ5RmlsZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnIiA+ICcsIGRhdGFEaXIsIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpLAogICAgICAgICAgICAgICAgICB3YWl0ID0gVFJVRSkKfQoKIyAtIHNldCBwYXJhbXMgdG8gd21kZV9jb2xsZWN0X3BhZ2V2aWV3cwp1cmlfaG9zdCA8LSBjKCdkZS53aWtpcGVkaWEub3JnJywgJ2RlLm0ud2lraXBlZGlhLm9yZycpCnVyaV9wYXRoICA8LSBjKAogICcvd2lraS8oMjc0MzAxKV9XaWtpcGVkaWE/dG91cj1laW5mdWhydW5nJywKICAnL3dpa2kvU3BlemlhbDpCZW51dHplcmtvbnRvX2FubGVnZW4nLAogICcvd2lraS9TcGV6aWFsOkFubWVsZGVuJywKICAnL3dpa2kvU3BlY2lhbDpNeVBhZ2UvQXJ0aWtlbHdlcmtzdGF0dCcsCiAgJy93aWtpL0JlbnV0emVyOlRyYWluaW5nc2tvbnRvXyhXTURFKS9XaWtpcGVkaWFfVHJhaW5pbmcnLAogICcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0RlaW5FbmdhZ2VtZW50L0xpdGVyYXR1cicKICApCiMgdXJpX3F1ZXJ5IDwtIHBhc3RlMCgnV01ERV8yMDIwX2NoYWxsZW5nZV8nLCAxOjMwKQpxdWVyeUZpbGUgPC0gJ3dpa2ltZWRpYV9jaGFsbGVuZ2VfT2N0MjAyMF9QYWdldmlld3MuaHFsJwpmaWxlTmFtZSA8LSBwYXN0ZTAoInBhZ2V2aWV3c18iLCBjZXREYXksICIudHN2IikKCiMgLSBjb2xsZWN0IFBhZ2V2aWV3cyBkYXRhCndtZGVfY29sbGVjdF9wYWdldmlld3ModXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyKQoKIyMjIC0tLSBXcmFuZ2xlIFBhZ2V2aWV3cwojIC0gZnVuY3Rpb246IHdtZGVfcHJvY2Vzc19wYWdldmlld3MKd21kZV9wcm9jZXNzX3BhZ2V2aWV3cyA8LSBmdW5jdGlvbihmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcXVlcnlfZmlsdGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhZ2VfZmlsdGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUgPSBjYW1wYWlnbk5hbWUpIHsKICAKICAjIC0gdG8gZGF0YURpcgogIHNldHdkKGRhdGFEaXIpCiAgCiAgIyAtIGxpYnJhcmllcwogIGxpYnJhcnkoc3RyaW5ncikKICBsaWJyYXJ5KGRwbHlyKQogIGxpYnJhcnkodGlkeXIpCiAgbGlicmFyeShkYXRhLnRhYmxlKQogIAogICMgLSBsb2FkCiAgcGFnZXZpZXdzRGF0YSA8LSByZWFkTGluZXMoZmlsZU5hbWUpCiAgd1N0YXJ0IDwtIHdoaWNoKGdyZXBsKCJ1cmlfaG9zdCIsIHBhZ2V2aWV3c0RhdGEpKQogIHBhZ2V2aWV3c0RhdGEgPC0gcGFnZXZpZXdzRGF0YVsod1N0YXJ0ICsgMik6KGxlbmd0aChwYWdldmlld3NEYXRhKSAtIDIpXQogIHBhZ2V2aWV3c0RhdGEgPC0gZGF0YS5mcmFtZShkYXQgPSBwYWdldmlld3NEYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgcGFnZXZpZXdzRGF0YSA8LSBzZXBhcmF0ZShwYWdldmlld3NEYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50byA9IGMoJ3VyaV9ob3N0JywgJ3VyaV9wYXRoJywgJ3VyaV9xdWVyeScsICdyZWZlcmVyJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiKQogICMgLSBhcHBseSBwYWdlX2ZpbHRlcgogIHBhZ2V2aWV3c0RhdGEkcGFnZSA8LSBwYXN0ZTAocGFnZXZpZXdzRGF0YSR1cmlfaG9zdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWdldmlld3NEYXRhJHVyaV9wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkpCiAgd0ZpbHRlciA8LSBzYXBwbHkocGFnZXZpZXdzRGF0YSRwYWdlLCBmdW5jdGlvbih4KSB7CiAgICBzYXBwbHkocGFnZV9maWx0ZXIsIGZ1bmN0aW9uKHkpIHsKICAgICAgZ3JlcGwoeSwgeCkKICAgIH0pCiAgfSkKICB3RmlsdGVyIDwtIGNvbFN1bXMod0ZpbHRlcikKICB3RmlsdGVyIDwtIHdoaWNoKHdGaWx0ZXIgPiAwKQogIHBhZ2V2aWV3c0RhdGEgPC0gcGFnZXZpZXdzRGF0YVt3RmlsdGVyLCBdCiAgIyAtIGFwcGx5IHVyaV9xdWVyeV9maWx0ZXIKICAjIC0gTk9URTogbG9va2luZyBpbiBib3RoOiB1cmlfcXVlcnksIHJlZmVyZXIgCiAgd191cmlfcXVlcnkgPC0gd2hpY2goZ3JlcGwodXJpX3F1ZXJ5X2ZpbHRlciwgcGFnZXZpZXdzRGF0YSRwYWdlKSkKICAKICBpZiAobGVuZ3RoKHdfdXJpX3F1ZXJ5KSA+IDApIHsKICAgIAogICAgIyAtIGZpbHRlciBmb3Igd191cmlfcXVlcnkKICAgIHBhZ2V2aWV3c0RhdGEgPC0gcGFnZXZpZXdzRGF0YVt3X3VyaV9xdWVyeSwgXSAKICAgIAogICAgIyAtIGFnZ3JlZ2F0ZToKICAgIHBhZ2V2aWV3c0RhdGEkdXJpX3BhdGggPC0gcGFzdGUwKHBhZ2V2aWV3c0RhdGEkdXJpX2hvc3QsIHBhZ2V2aWV3c0RhdGEkdXJpX3BhdGgpCiAgICBwYWdldmlld3NEYXRhJHVyaV9ob3N0IDwtIE5VTEwKICAgIHBhZ2V2aWV3c0RhdGEkcGFnZSA8LSBOVUxMCiAgICBwYWdldmlld3NEYXRhJHJlZmVyZXIgPC0gTlVMTAogICAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhICU+JSAKICAgICAgZHBseXI6OnNlbGVjdCh1cmlfcXVlcnksIHVyaV9wYXRoKSAlPiUgCiAgICAgIGRwbHlyOjpncm91cF9ieSh1cmlfcXVlcnksIHVyaV9wYXRoKSAlPiUgCiAgICAgIGRwbHlyOjpzdW1tYXJpc2UocGFnZXZpZXdzID0gbigpKQogICAgY29sbmFtZXMocGFnZXZpZXdzRGF0YSkgPC0gYygnVGFnJywgJ1BhZ2UnLCAnUGFnZXZpZXdzJykKICAgIAogICAgIyAtIGFkZCBjZXREYXksIGNhbXBhaWduTmFtZQogICAgcGFnZXZpZXdzRGF0YSRkYXRlIDwtIGNldERheQogICAgcGFnZXZpZXdzRGF0YSRjYW1wYWlnbiA8LSBjYW1wYWlnbk5hbWUKICAgIAogICAgIyAtIHN0b3JlOgogICAgd3JpdGUuY3N2KHBhZ2V2aWV3c0RhdGEsIAogICAgICAgICAgICAgIHBhc3RlMChhbmFseXRpY3NEaXIsIAogICAgICAgICAgICAgICAgICAgICAicGFnZXZpZXdzQWdncmVnYXRlZF8iLAogICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdCgKICAgICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdChmaWxlTmFtZSwgc3BsaXQgPSAiXyIsIGZpeGVkID0gVClbWzFdXVsyXSwKICAgICAgICAgICAgICAgICAgICAgICBzcGxpdCA9ICIuIiwgCiAgICAgICAgICAgICAgICAgICAgICAgZml4ZWQgPSBUKVtbMV1dWzFdLAogICAgICAgICAgICAgICAgICAgICAiLmNzdiIKICAgICAgICAgICAgICApCiAgICApCiAgICAKICB9CiAgCn0KCiMgLSBzZXQgcGFyYW1zIHRvIHdtZGVfcHJvY2Vzc19wYWdldmlld3MKIyAtIGZvciB0aGUgV01ERSAyMDIwX0VtYWlsQ2FtcGFpZ25XaWtpcGVkaWFDaGFsbGVuZ2UKdXJpX3F1ZXJ5X2ZpbHRlciA8LSAnV01ERV8yMDIwX2NoYWxsZW5nZV8nCnBhZ2VfZmlsdGVyIDwtIGMoJ2RlLndpa2lwZWRpYS5vcmcvd2lraS8oMjc0MzAxKV9XaWtpcGVkaWE/dG91cj1laW5mdWhydW5nJywKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL1NwZXppYWw6QmVudXR6ZXJrb250b19hbmxlZ2VuJywKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL1NwZXppYWw6QW5tZWxkZW4nLAogICAgICAgICAgICAgICAgICdkZS53aWtpcGVkaWEub3JnL3dpa2kvU3BlY2lhbDpNeVBhZ2UvQXJ0aWtlbHdlcmtzdGF0dCcsCiAgICAgICAgICAgICAgICAgJ2RlLndpa2lwZWRpYS5vcmcvd2lraS9CZW51dHplcjpUcmFpbmluZ3Nrb250b18oV01ERSkvV2lraXBlZGlhX1RyYWluaW5nJywKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL1dpa2lwZWRpYTpXaWtpbWVkaWFfRGV1dHNjaGxhbmQvRGVpbkVuZ2FnZW1lbnQvTGl0ZXJhdHVyJykKCiMgLSB3cmFuZ2xlIHBhZ2V2aWV3cwp3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzKGZpbGVOYW1lID0gZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpciA9IGRhdGFEaXIsCiAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5X2ZpbHRlciA9IHVyaV9xdWVyeV9maWx0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgIHBhZ2VfZmlsdGVyID0gcGFnZV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5ID0gY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSA9IGNhbXBhaWduTmFtZSkKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gVXNlciBSZWdpc3RyYXRpb25zCiMjIyAtLS0gSGl2ZVFMIHF1ZXJ5OiBldmVudC5TZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uIHRhYmxlCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmhpdmVRTCA8LQogICJTRUxFQ1QgeWVhciwgbW9udGgsIGRheSwgaG91ciwgIGV2ZW50LmNhbXBhaWduLCBldmVudC51c2VySWQsIGV2ZW50LnVzZXJOYW1lLCB3ZWJob3N0LCB3aWtpCiAgICAgIEZST00gZXZlbnQuc2VydmVyc2lkZWFjY291bnRjcmVhdGlvbgogICAgICBXSEVSRQogICAgICAgIHllYXIgPSAyMDIwCiAgICAgICAgQU5EIChtb250aCA9IDEwIE9SIG1vbnRoID0gMTEpCiAgICAgICAgQU5EIChldmVudC5jYW1wYWlnbiBMSUtFICclV01ERV8yMDIwX2NoYWxsZW5nZV8lJyk7IgojIC0gd3JpdGUgaHFsCndyaXRlKGhpdmVRTCwgcGFzdGUwKGRhdGFEaXIsICd1c2VyX3JlZ2lzdHJhdGlvbnMuaHFsJykpCiMjIyAtLS0gb3V0cHV0IGZpbGVuYW1lCmZpbGVuYW1lIDwtIHBhc3RlMChkYXRhRGlyLCAndXNlcl9yZWdpc3RyYXRpb25zLnRzdicpCiMjIyAtLS0gZXhlY3V0ZSBocWwgc2NyaXB0OgpoaXZlQXJncyA8LSAnc3VkbyAtdSBhbmFseXRpY3MtcHJpdmF0ZWRhdGEga2VyYmVyb3MtcnVuLWNvbW1hbmQgYW5hbHl0aWNzLXByaXZhdGVkYXRhIC91c3IvbG9jYWwvYmluL2JlZWxpbmUgLS1zaWxlbnQgLS1pbmNyZW1lbnRhbD10cnVlIC0tdmVyYm9zZT1mYWxzZSAtZicKaGl2ZUlucHV0IDwtIHBhc3RlKHBhc3RlMChkYXRhRGlyLCAndXNlcl9yZWdpc3RyYXRpb25zLmhxbCcpLAogICAgICAgICAgICAgICAgICAgIiA+ICIsCiAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSwKICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQojIC0gY29tbWFuZDoKaGl2ZUNvbW1hbmQgPC0gcGFzdGUoaGl2ZUFyZ3MsIGhpdmVJbnB1dCkKc3lzdGVtKGNvbW1hbmQgPSBoaXZlQ29tbWFuZCwgd2FpdCA9IFRSVUUpCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIFVzZXIgRWRpdHMKIyMjIC0tLSBVc2VyIEVkaXRzIHZpYSByZXZpc2lvbl9hY3Rvcl90ZW1wCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpzdGFydFRpbWVzdGFtcCA8LSAnMjAyMDEwMDEwMDAwMDAnCiMgLSBnZXQgdXNlciBpZHMKdXNlclJlZ2lzdHJhdGlvbnMgPC0gZnJlYWQocGFzdGUwKGRhdGFEaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAndXNlcl9yZWdpc3RyYXRpb25zLnRzdicpKQpkaW0odXNlclJlZ2lzdHJhdGlvbnMpCiMgLSBjbGVhbiB1cCBmcm9tIHRlc3QgYWNjb3VudHMKdXNlclJlZ2lzdHJhdGlvbnMgPC0gZmlsdGVyKHVzZXJSZWdpc3RyYXRpb25zLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICFncmVwbCgiXnRlc3R8XlRlc3QiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2VyUmVnaXN0cmF0aW9ucyR1c2VybmFtZSkpCmRpbSh1c2VyUmVnaXN0cmF0aW9ucykKaGVhZCh1c2VyUmVnaXN0cmF0aW9ucykKcmV2X3VzZXIgPC0gdXNlclJlZ2lzdHJhdGlvbnMkdXNlcmlkCiMgLSBpdGVyYXRlIG92ZXIgcmV2X3VzZXIKZm9yIChpIGluIDE6bGVuZ3RoKHJldl91c2VyKSkgewogICMgLSBTUUwgcXVlcnkKICBzcWxRdWVyeSA8LSBwYXN0ZSgiXCJTRUxFQ1QgYWN0b3IuYWN0b3JfaWQsIAogICAgICAgICAgICAgICAgICAgICAgICBhY3Rvci5hY3Rvcl91c2VyLCAKICAgICAgICAgICAgICAgICAgICAgICAgYWN0b3IuYWN0b3JfbmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHJldmlzaW9uX2FjdG9yX3RlbXAucmV2YWN0b3JfdGltZXN0YW1wIAogICAgICAgICAgICAgICAgICAgIEZST00gYWN0b3IgCiAgICAgICAgICAgICAgICAgICAgTEVGVCBKT0lOIHJldmlzaW9uX2FjdG9yX3RlbXAgT04gKGFjdG9yLmFjdG9yX2lkID0gcmV2aXNpb25fYWN0b3JfdGVtcC5yZXZhY3Rvcl9hY3RvcikgCiAgICAgICAgICAgICAgICAgICAgV0hFUkUgKHJldmlzaW9uX2FjdG9yX3RlbXAucmV2YWN0b3JfdGltZXN0YW1wID49IDIwMjAxMDAxMDAwMDAwIAogICAgICAgICAgICAgICAgICAgICAgQU5EIGFjdG9yLmFjdG9yX3VzZXIgPSAiLCByZXZfdXNlcltpXSwgIik7XCIiKTsKICAjIyMgLS0tIG91dHB1dCBmaWxlbmFtZQogIGZpbGVuYW1lIDwtIHBhc3RlKGRhdGFEaXIsJ3VzZXJFZGl0cycsICJfIiwgaSwgIi50c3YiLCBzZXAgPSAiIikKICAjIyMgLS0tIGV4ZWN1dGUgc3FsIHNjcmlwdDoKICBzcWxMb2dJblByZSA8LSBwYXN0ZTAoJy91c3IvbG9jYWwvYmluL2FuYWx5dGljcy1teXNxbCBkZXdpa2kgLWUgJykKICBzcWxJbnB1dCA8LSBwYXN0ZShzcWxRdWVyeSwKICAgICAgICAgICAgICAgICAgICAiID4gIiwKICAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSwKICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAjIC0gY29tbWFuZDoKICBzcWxDb21tYW5kIDwtIHBhc3RlKHNxbExvZ0luUHJlLCBzcWxJbnB1dCkKICBzeXN0ZW0oY29tbWFuZCA9IHNxbENvbW1hbmQsIHdhaXQgPSBUUlVFKQogICMgLSByZXBvcnQKICBwcmludChwYXN0ZTAoIkRPTkU6IHVzZXIgIiwgaSwgIi4iKSkKfQojIyMgLS0tIEVORCBydW4gU1FMIHNjcmlwdHMKIyAtIGxvYWQgdXNlciBlZGl0czoKbEYgPC0gbGlzdC5maWxlcyhkYXRhRGlyKQpsRiA8LSBsRltncmVwbCgiXnVzZXJFZGl0c18iLCBsRildCnVzZXJFZGl0cyA8LSBsYXBwbHkocGFzdGUwKGRhdGFEaXIsIGxGKSwgZnJlYWQpCnVzZXJFZGl0cyA8LSByYmluZGxpc3QodXNlckVkaXRzKQojIC0gc3RvcmUgdXNlciBlZGl0czoKd3JpdGUuY3N2KHVzZXJFZGl0cywgcGFzdGUwKGFuYWx5dGljc0RpciwgJ3VzZXJFZGl0cy5jc3YnKSkKYGBgCgojIyAwLiBDYW1wYWlnbiBCYW5uZXJzCgpUaGlzIHNlY3Rpb24gcHJlc2VudHMgYWxsIGRhdGEgYW5kIHN0YXRpc3RpY3Mgb24gdGhlIGNhbXBhaWduIGJhbm5lcnMuClRoZSBmb2xsb3dpbmcgY2h1bmsgbG9hZHMgYW5kIHRoZW4gcmUtc3RydWN0dXJlcyB0aGUgYmFubmVycyBkYXRhc2V0IGEgYml0OgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmxGIDwtIGxpc3QuZmlsZXMoJ19kYWlseVJlcG9ydGluZycpCmxGIDwtIGxGW2dyZXBsKCJeYmFubmVySW50ZXJhY3Rpb25zIiwgbEYpXQpkYXRhU2V0IDwtIGxhcHBseShwYXN0ZTAoIl9kYWlseVJlcG9ydGluZy8iLCBsRiksIGZyZWFkKQpkYXRhU2V0IDwtIHJiaW5kbGlzdChkYXRhU2V0KQpkYXRhU2V0JFYxIDwtIE5VTEwKZGF0YVNldCRjYW1wYWlnbiA8LSBOVUxMCiMgLSBOQXMgdG8gemVybwpkYXRhU2V0W3doaWNoKGlzLm5hKGRhdGFTZXQpLCBhcnIuaW5kID0gVCldIDwtIDAKIyAtIGJhbm5lciBjb2RlcwpkYXRhU2V0JGJhbm5lcm5hbWUgPC0gc2FwcGx5KGRhdGFTZXQkYmFubmVybmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZCA8LSBzdHJzcGxpdCh4LCBzcGxpdCA9ICJfIilbWzFdXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZCA8LSBwYXN0ZShkWzE6NV0sIGNvbGxhcHNlID0gIl8iKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuKGQpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSkKZGF0YVNldCRiYW5uZXJuYW1lIDwtIGdzdWIoIk5BIiwgImRlc2t0b3AiLCBkYXRhU2V0JGJhbm5lcm5hbWUpCmRhdGFTZXQgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGJhbm5lcm5hbWUsIGRheSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5KSAlPiUKICBncm91cF9ieShiYW5uZXJuYW1lLCBkYXkpICU+JQogIHN1bW1hcmlzZShjbGlja2VkX2J5ID0gc3VtKGNsaWNrZWRfYnkpLCAKICAgICAgICAgICAgY2xvc2VkX2J5ID0gc3VtKGNsb3NlZF9ieSkpCmRhdGFTZXQkdmFyX2N0cmwgPC0gc2FwcGx5KGRhdGFTZXQkYmFubmVybmFtZSwgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgidmFyIiwgeCkpIHtyZXR1cm4oInZhciIpfSBlbHNlIHtyZXR1cm4oImN0cmwiKX0KfSkKZGF0YVNldCRkZXZpY2UgPC0gc2FwcGx5KGRhdGFTZXQkYmFubmVybmFtZSwgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgibW9iaWxlIiwgeCkpIHsKICAgIHJldHVybigibW9iaWxlIikKICB9IGVsc2UgaWYgKGdyZXBsKCJpcGFkIiwgeCkpIHsKICAgICAgcmV0dXJuKCJpcGFkIikKICB9IGVsc2UgewogICAgICByZXR1cm4oImRlc2t0b3AiKQogIH0KfSkKYGBgCgpUaGUgZm9sbG93aW5nIGRhdGFzZXQgcHJlc2VudHMgdGhlIGJhbm5lciBpbXByZXNzaW9ucyBkYXRhOgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmxGIDwtIGxpc3QuZmlsZXMoJ19kYWlseVJlcG9ydGluZycpCmxGIDwtIGxGW2dyZXBsKCJeYmFubmVySW1wcmVzc2lvbnNBZ2dyZWdhdGVkIiwgbEYpXQpiaV9kYXRhU2V0IDwtIGxhcHBseShwYXN0ZTAoIl9kYWlseVJlcG9ydGluZy8iLCBsRiksIGZyZWFkKQpiaV9kYXRhU2V0IDwtIHJiaW5kbGlzdChiaV9kYXRhU2V0KQpiaV9kYXRhU2V0JFYxIDwtIE5VTEwKYmlfZGF0YVNldCRiYW5uZXIgPC0gZ3N1YigiXldNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX2N0cmwkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIldNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX2N0cmxfZGVza3RvcCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYmlfZGF0YVNldCRiYW5uZXIpCmJpX2RhdGFTZXQkYmFubmVyIDwtIGdzdWIoIl5XTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF92YXIkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIldNREVfY2hhbGxlbmdlYm5yX2ZhbGwyMDIwX3Zhcl9kZXNrdG9wIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBiaV9kYXRhU2V0JGJhbm5lcikKYmlfZGF0YVNldCRjYW1wYWlnbiA8LSBOVUxMCmNvbG5hbWVzKGJpX2RhdGFTZXQpWzJdIDwtICdzZWVuX2J5JwpgYGAKCkpvaW4gYmFubmVyIGltcHJlc3Npb25zIGRhdGEgKGBiaV9kYXRhU2V0YCkgdG8gYmFubmVyIGludGVyYWN0aW9ucyBkYXRhIChgZGF0YVNldGApIHRvIHVzZSBgYmlfZGF0YVNldCRzZWVuX2J5YDoKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpkYXRhU2V0IDwtIGRwbHlyOjpsZWZ0X2pvaW4oZGF0YVNldCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBiaV9kYXRhU2V0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJkYXkiID0gImRhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJiYW5uZXJuYW1lIiA9ICJiYW5uZXIiKQopCnJtKGJpX2RhdGFTZXQpCmBgYAoKIyMjIDAuMSBCYW5uZXIgQWN0aW9ucyBPdmVydmlldwoKKipDaGFydCAwLjEuMSoqIERhaWx5IEJhbm5lciBJbXByZXNzaW9ucywgQ2xpY2tzLCBhbmQgQ2xvc3VyZXMsIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRheSwgc2Vlbl9ieSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5KSAlPiUgCiAgZ3JvdXBfYnkoZGF5KSAlPiUgCiAgc3VtbWFyaXNlKHNlZW5fYnkgPSBzdW0oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnkgPSBzdW0oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnkgPSBzdW0oY2xpY2tlZF9ieSkpCnBGcmFtZSRjbG9zZV9yYXRlID0gcm91bmQocEZyYW1lJGNsb3NlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lJGNsaWNrX3JhdGUgPSByb3VuZChwRnJhbWUkY2xpY2tlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXkpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRhaWx5OgpwRiA8LSBwRnJhbWUgJT4lIAogIHNlbGVjdChkYXksIHNlZW5fYnksIGNsb3NlZF9ieSwgY2xpY2tlZF9ieSkgJT4lIAogIHBpdm90X2xvbmdlcihjKCdzZWVuX2J5JywgJ2Nsb3NlZF9ieScsICdjbGlja2VkX2J5JyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBY3Rpb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlZhbHVlIikKZ2dwbG90KHBGLCBhZXMoeCA9IGRheSwKICAgICAgICAgICAgICAgeSA9IFZhbHVlLAogICAgICAgICAgICAgICBncm91cCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdXaWtpcGVkaWEgQ2hhbGxlbmdlIE9jdG9iZXIgMjAyMCcpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJBY3Rpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipDaGFydCAwLjEuMioqIFRvdGFsLCBNZWFuIHBlciBkYXksIGFuZCBNZWRpYW4gcGVyIGRheSBCYW5uZXIgSW1wcmVzc2lvbnMsIENsaWNrcywgYW5kIENsb3N1cmVzLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNX0KcEZyYW1lVG90YWwgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRheSwgc2Vlbl9ieSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5KSAlPiUgCiAgZ3JvdXBfYnkoZGF5KSAlPiUgCiAgc3VtbWFyaXNlKHNlZW5fYnkgPSBzdW0oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnkgPSBzdW0oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnkgPSBzdW0oY2xpY2tlZF9ieSkKICAgICAgICAgICAgKSAKcEZyYW1lVG90YWwgPC0gcEZyYW1lVG90YWwgJT4lIAogIHN1bW1hcmlzZShzZWVuX2J5X190b3RhbF9pbl9jYW1wYWlnbiA9IHN1bShzZWVuX2J5KSwgCiAgICAgICAgICAgIGNsb3NlZF9ieV9fdG90YWxfaW5fY2FtcGFpZ24gPSBzdW0oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnlfX3RvdGFsX2luX2NhbXBhaWduID0gc3VtKGNsaWNrZWRfYnkpLAogICAgICAgICAgICBzZWVuX2J5X19tZWFuX3Blcl9kYXkgPSBtZWFuKHNlZW5fYnkpLCAKICAgICAgICAgICAgY2xvc2VkX2J5X19tZWFuX3Blcl9kYXkgPSBtZWFuKGNsb3NlZF9ieSksIAogICAgICAgICAgICBjbGlja2VkX2J5X19tZWFuX3Blcl9kYXkgPSBtZWFuKGNsaWNrZWRfYnkpLAogICAgICAgICAgICBzZWVuX2J5X19tZWRpYW5fcGVyX2RheSA9IG1lZGlhbihzZWVuX2J5KSwgCiAgICAgICAgICAgIGNsb3NlZF9ieV9fbWVkaWFuX3Blcl9kYXkgPSBtZWRpYW4oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnlfX21lZGlhbl9wZXJfZGF5ID0gbWVkaWFuKGNsaWNrZWRfYnkpCiAgICAgICAgICAgICkgJT4lIAogIHQoKQpwRnJhbWVUb3RhbFssIDFdIDwtIHJvdW5kKHBGcmFtZVRvdGFsWywgMV0sIDIpCnBGcmFtZVRvdGFsIDwtIGFzLmRhdGEuZnJhbWUocEZyYW1lVG90YWwpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oJ01lYXN1cmUnKQpjb2xuYW1lcyhwRnJhbWVUb3RhbClbMl0gPC0gJ1ZhbHVlJwpwRnJhbWVUb3RhbCA8LXBGcmFtZVRvdGFsICU+JSAKICBzZXBhcmF0ZSgnTWVhc3VyZScsIAogICAgICAgICAgIHNlcCA9ICJfXyIsIAogICAgICAgICAgIGludG8gPSBjKCdBY3Rpb24nLCAnU3RhdGlzdGljJykpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRhaWx5OgpnZ3Bsb3QocEZyYW1lVG90YWwsIGFlcyh4ID0gQWN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICB5ID0gbG9nKFZhbHVlKSwKICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBTdGF0aXN0aWMsCiAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gU3RhdGlzdGljLAogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFZhbHVlLAogICAgICAgICAgICAgICAgICAgICkpICsKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsgIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ1dpa2lwZWRpYSBDaGFsbGVuZ2UgT2N0b2JlciAyMDIwJykgKwogIHhsYWIoIkFjdGlvbiIpICsgeWxhYigibG9nKFZhbHVlKSIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKKipDaGFydCAwLjEuMyoqIERhaWx5IEJhbm5lciBDbGljayBhbmQgQ2xvc2UgcmF0ZXMsIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfTogY2xvc2VfcmF0ZSwgY2xpY2tfcmF0ZSwgZGFpbHk6CnBGIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KGRheSwgY2xpY2tfcmF0ZSwgY2xvc2VfcmF0ZSkgJT4lIAogIHBpdm90X2xvbmdlcihjKCdjbGlja19yYXRlJywgJ2Nsb3NlX3JhdGUnKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkFjdGlvbiIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiVmFsdWUiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gVmFsdWUsCiAgICAgICAgICAgICAgIGdyb3VwID0gQWN0aW9uLAogICAgICAgICAgICAgICBjb2xvciA9IEFjdGlvbiwKICAgICAgICAgICAgICAgZmlsbCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgbGFiZWwgPSBWYWx1ZSwKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJzIwMjAgV01ERSBPY2Nhc2lvbmFsIEVkaXRvcnMgQmFubmVyIENhbXBhaWduJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIkFjdGlvbnMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKyAKICB5bGltKGMoMCwgLjI1KSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipUYWJsZSAwLjEuNCoqIE1lYW4gYW5kIE1lZGlhbiBDbGljayBhbmQgQ2xvc2UgcmF0ZXMgKGFjcm9zcyBkYXlzKQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnRGcmFtZSA8LSBkYXRhLmZyYW1lKGNsaWNrX3JhdGVfbWVhbiA9IHJvdW5kKG1lYW4ocEZyYW1lJGNsaWNrX3JhdGUpLCA1KSwgCiAgICAgICAgICAgICAgICAgICAgIGNsaWNrX3JhdGVfbWVkaWFuID0gcm91bmQobWVkaWFuKHBGcmFtZSRjbGlja19yYXRlKSwgNSksCiAgICAgICAgICAgICAgICAgICAgIGNsb3NlX3JhdGVfbWVhbiA9IHJvdW5kKG1lYW4ocEZyYW1lJGNsb3NlX3JhdGUpLCA1KSwKICAgICAgICAgICAgICAgICAgICAgY2xvc2VfcmF0ZV9tZWRpYW4gPSByb3VuZChtZWRpYW4ocEZyYW1lJGNsb3NlX3JhdGUpLCA1KQopCmRhdGF0YWJsZSh0RnJhbWUpCmBgYAoKKipUYWJsZSAwLjEuNSoqIERlIEZhY3RvIENsaWNrIGFuZCBDbG9zZSByYXRlcyBpbiBwZXJjZW50cwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnRGcmFtZSA8LSBkYXRhLmZyYW1lKGNsaWNrX3JhdGUgPSByb3VuZChzdW0ocEZyYW1lJGNsaWNrZWRfYnkpL3N1bShwRnJhbWUkc2Vlbl9ieSkqMTAwLCA1KSwgCiAgICAgICAgICAgICAgICAgICAgIGNsb3NlX3JhdGUgPSByb3VuZChzdW0ocEZyYW1lJGNsb3NlZF9ieSkvc3VtKHBGcmFtZSRzZWVuX2J5KSoxMDAsIDUpKQpkYXRhdGFibGUodEZyYW1lKQpgYGAKCioqQ2hhcnQgMC4xLjYqKiBEYWlseSBCYW5uZXIgQ2xpY2sgYW5kIENsb3NlIHJhdGVzLCBieSAqKmRldmljZXMqKiwgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDEwfQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRheSwgc2Vlbl9ieSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5LCBkZXZpY2UpICU+JSAKICBncm91cF9ieShkYXksIGRldmljZSkgJT4lIAogIHN1bW1hcmlzZShzZWVuX2J5ID0gc3VtKHNlZW5fYnkpLCAKICAgICAgICAgICAgY2xvc2VkX2J5ID0gc3VtKGNsb3NlZF9ieSksIAogICAgICAgICAgICBjbGlja2VkX2J5ID0gc3VtKGNsaWNrZWRfYnkpKQpwRnJhbWUkY2xvc2VfcmF0ZSA9IHJvdW5kKHBGcmFtZSRjbG9zZWRfYnkvcEZyYW1lJHNlZW5fYnksIDIpCnBGcmFtZSRjbGlja19yYXRlID0gcm91bmQocEZyYW1lJGNsaWNrZWRfYnkvcEZyYW1lJHNlZW5fYnksIDIpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZGF5KQojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfTogc2Vlbl9ieSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5LCBkYWlseToKcEYgPC0gcEZyYW1lICU+JSAKICBzZWxlY3QoZGF5LCBkZXZpY2UsIHNlZW5fYnksIGNsb3NlZF9ieSwgY2xpY2tlZF9ieSkgJT4lIAogIHBpdm90X2xvbmdlcihjKCdzZWVuX2J5JywgJ2Nsb3NlZF9ieScsICdjbGlja2VkX2J5JyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBY3Rpb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlZhbHVlIikKZ2dwbG90KHBGLCBhZXMoeCA9IGRheSwKICAgICAgICAgICAgICAgeSA9IFZhbHVlLAogICAgICAgICAgICAgICBncm91cCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBmYWNldF93cmFwKH5kZXZpY2UsIG5yb3cgPSAzLCBzY2FsZXMgPSAiZnJlZSIpICsgCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiQWN0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKKipDaGFydCAwLjEuNyoqIERhaWx5IEJhbm5lciBDbGljayBhbmQgQ2xvc2UgcmF0ZXMsIGJ5ICoqdmFyL2N0cmwqKiwgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuNX0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChkYXksIHNlZW5fYnksIGNsaWNrZWRfYnksIGNsb3NlZF9ieSwgdmFyX2N0cmwpICU+JSAKICBncm91cF9ieShkYXksIHZhcl9jdHJsKSAlPiUgCiAgc3VtbWFyaXNlKHNlZW5fYnkgPSBzdW0oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnkgPSBzdW0oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnkgPSBzdW0oY2xpY2tlZF9ieSkpCnBGcmFtZSRjbG9zZV9yYXRlID0gcm91bmQocEZyYW1lJGNsb3NlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lJGNsaWNrX3JhdGUgPSByb3VuZChwRnJhbWUkY2xpY2tlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXkpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRhaWx5OgpwRiA8LSBwRnJhbWUgJT4lIAogIHNlbGVjdChkYXksIHZhcl9jdHJsLCBzZWVuX2J5LCBjbG9zZWRfYnksIGNsaWNrZWRfYnkpICU+JSAKICBwaXZvdF9sb25nZXIoYygnc2Vlbl9ieScsICdjbG9zZWRfYnknLCAnY2xpY2tlZF9ieScpLCAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiQWN0aW9uIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJWYWx1ZSIpCmdncGxvdChwRiwgYWVzKHggPSBkYXksCiAgICAgICAgICAgICAgIHkgPSBWYWx1ZSwKICAgICAgICAgICAgICAgZ3JvdXAgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGNvbG9yID0gQWN0aW9uLAogICAgICAgICAgICAgICBmaWxsID0gQWN0aW9uLAogICAgICAgICAgICAgICBsYWJlbCA9IFZhbHVlLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZmFjZXRfd3JhcCh+dmFyX2N0cmwsIG5yb3cgPSAyLCBzY2FsZXMgPSAiZnJlZSIpICsgCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiQWN0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCioqVGFibGUgMC4xLjgqKiBEYWlseSBCYW5uZXIgSW1wcmVzc2lvbnM6IHNlZW4gYnksIGNsb3NlZCBieSwgY2xpY2tlZCBieSwgUGFnZXZpZXdzCgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMjIyAtLS0gRnVsbCBEYXRhc2V0IChUYWJsZSBSZXBvcnQpCmRTIDwtIGRhdGFTZXQKZFMkYmFubmVybmFtZSA8LSBnc3ViKCJXTURFX2NoYWxsZW5nZWJucl9mYWxsMjAyMF8iLCAiIiwgZFMkYmFubmVybmFtZSkKZFMgPC0gZFNbLAogICAgICAgICBjKCdiYW5uZXJuYW1lJywgJ2RheScsICdkZXZpY2UnLCAndmFyX2N0cmwnLCAKICAgICAgICAgICAnc2Vlbl9ieScsICdjbG9zZWRfYnknLCAnY2xpY2tlZF9ieScpXQpkYXRhdGFibGUoZFMsIAogICAgICAgICAgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDMwKQogICAgICAgICAgKQpgYGAKCiMjIDEuIENhbXBhaWduIFBhZ2V2aWV3cwoKVGhpcyBzZWN0aW9uIHByZXNlbnRzIGFsbCBkYXRhIGFuZCBzdGF0aXN0aWNzIG9uIHRoZSBjYW1wYWlnbiBwYWdlcy4KClRoZSBmb2xsb3dpbmcgY2h1bmsgbG9hZHMgYW5kIHRoZW4gcmUtc3RydWN0dXJlcyB0aGUgZGF0YXNldCBhIGJpdC4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpsRiA8LSBsaXN0LmZpbGVzKCdfZGFpbHlSZXBvcnRpbmcnKQpsRiA8LSBsRltncmVwbCgiXnBhZ2V2aWV3c0FnZ3JlZ2F0ZWQiLCBsRildCmRhdGFTZXQgPC0gbGFwcGx5KHBhc3RlMCgiX2RhaWx5UmVwb3J0aW5nLyIsIGxGKSwgZnJlYWQpCmRhdGFTZXQgPC0gcmJpbmRsaXN0KGRhdGFTZXQpCmRhdGFTZXQkVjEgPC0gTlVMTApkYXRhU2V0JGNhbXBhaWduIDwtIE5VTEwKIyAtIGV4cGFuZCBncmlkIHRvIGFjY291bnQgZm9yIG1pc3Npbmcgb2JzZXJ2YXRpb25zIHBlciBkYXkKZFMgPC0gZXhwYW5kLmdyaWQodW5pcXVlKGRhdGFTZXQkVGFnKSwgCiAgICAgICAgICAgICAgICAgIHVuaXF1ZShkYXRhU2V0JFBhZ2UpLCAKICAgICAgICAgICAgICAgICAgdW5pcXVlKGRhdGFTZXQkZGF0ZSksIAogICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXMoZFMpIDwtIGMoJ1RhZycsICdQYWdlJywgJ2RhdGUnKQpkUyA8LSBkUyAlPiUgCiAgbGVmdF9qb2luKGRhdGFTZXQsIAogICAgICAgICAgICBieSA9IGMoIlRhZyIsICJQYWdlIiwgImRhdGUiKSkKZGF0YVNldCA8LSBkUzsgcm0oZFMpCmRhdGFTZXQkUGFnZXZpZXdzW2lzLm5hKGRhdGFTZXQkUGFnZXZpZXdzKV0gPC0gMApkYXRhU2V0JFRhZyA8LSBnc3ViKCImLiskIiwgIiIsIGRhdGFTZXQkVGFnKQpkYXRhU2V0JFRhZyA8LSBnc3ViKCJcXD9jYW1wYWlnbj0iLCAiIiwgZGF0YVNldCRUYWcpCmRhdGFTZXQkZGF0ZSA8LSBzYXBwbHkoZGF0YVNldCRkYXRlLCBmdW5jdGlvbih4KSB7CiAgZCA8LSBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXQogIGlmIChuY2hhcihkWzNdKSA9PSAxKSB7CiAgICBkWzNdIDwtIHBhc3RlMCgiMCIsIGRbM10pCiAgfQogIHJldHVybihwYXN0ZShkLCBjb2xsYXBzZSA9ICItIikpCn0pCmBgYAoKIyMjIDEuMSBQYWdldmlld3MgT3ZlcnZpZXcKCioqQ2hhcnQgMS4xLjEqKiBEYWlseSBQYWdldmlld3MsIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBjaGFubmVscy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDZ9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF0ZSwgUGFnZSwgUGFnZXZpZXdzKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgUGFnZSkgJT4lIAogIHN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXRlKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41LCBncm91cCA9IDEpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAoflBhZ2UsIG5jb2wgPSAyKSArIAogIGdndGl0bGUoJ1dpa2lwZWRpYSBDaGFsbGVuZ2UgT2N0b2JlciAyMDIwJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIlBhZ2V2aWV3cyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgIyBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gMTApKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKKipUYWJsZSAxLjEuMSoqIFBhZ2V2aWV3cyB0b3RhbHMKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQp0RnJhbWUgPC0gcEZyYW1lICU+JSAKICBzZWxlY3QoUGFnZSwgUGFnZXZpZXdzKSAlPiUgCiAgZ3JvdXBfYnkoUGFnZSkgJT4lIAogIHN1bW1hcmlzZSh0b3RhbFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKSAlPiUgCiAgYXJyYW5nZShkZXNjKHRvdGFsUGFnZXZpZXdzKSkKZGF0YXRhYmxlKHRGcmFtZSkKYGBgCgojIyMgMS4yIFBhZ2V2aWV3czogQ2FtcGFpZ24gQ2hhbm5lbHMKCioqQ2hhcnQgMS4yLjEqKiBQYWdldmlld3MsIGJ5ICoqY2hhbm5lbHMqKgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNn0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChkYXRlLCBUYWcsIFBhZ2V2aWV3cykgJT4lIAogIGdyb3VwX2J5KGRhdGUsIFRhZykgJT4lIAogIHN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXRlKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41LCBncm91cCA9IDEpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAoflRhZywgbmNvbCA9IDIpICsgCiAgZ2d0aXRsZSgnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAnKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiUGFnZXZpZXdzIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICAjIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA2KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCioqQ2hhcnQgMS4yLjIqKiBUb3RhbCBQYWdldmlld3MsIGJ5ICoqY2hhbm5lbHMqKgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwRnJhbWUgPC0gcEZyYW1lICU+JSAKICBzZWxlY3QoVGFnLCBQYWdldmlld3MpICU+JSAKICBncm91cF9ieShUYWcpICU+JSAKICBzdW1tYXJpc2UoUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpICU+JSAKICBhcnJhbmdlKGRlc2MoUGFnZXZpZXdzKSkKcEZyYW1lJFRhZyA8LSBmYWN0b3IocEZyYW1lJFRhZywKICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gcEZyYW1lJFRhZywKICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFQpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gVGFnLAogICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsdWUiLCBmaWxsID0gIndoaXRlIikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdXaWtpcGVkaWEgQ2hhbGxlbmdlIE9jdG9iZXIgMjAyMCcpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJQYWdldmlld3MiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKKipUYWJsZSAyLjIuMSoqIFRvdGFsIHBhZ2V2aWV3cywgYnkgKipjaGFubmVscyoqIGFuZCBieSAqKnBhZ2VzKiouCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChUYWcsIFBhZ2UsIFBhZ2V2aWV3cykgJT4lIAogIGdyb3VwX2J5KFRhZywgUGFnZSkgJT4lIAogIHN1bW1hcmlzZSh0b3RhbFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKSAlPiUgCiAgYXJyYW5nZShkZXNjKHRvdGFsUGFnZXZpZXdzKSkKZGF0YXRhYmxlKHRGcmFtZSkKYGBgCgojIyAyLiBVc2VyIFJlZ2lzdHJhdGlvbnMKClRoaXMgc2VjdGlvbiBwcmVzZW50cyBhbGwgZGF0YSBhbmQgc3RhdGlzdGljcyBvbiB0aGUgdXNlciByZWdpc3RyYXRpb25zLgoKIyMjIDIuMSBVc2VyIFJlZ2lzdHJhdGlvbnMgT3ZlcnZpZXcKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpsRiA8LSBsaXN0LmZpbGVzKCJfZGFpbHlSZXBvcnRpbmciKQpsRiA8LSBsRltncmVwbCgiXnVzZXJfcmVnaXN0cmF0aW9ucyIsIGxGKV0KZGF0YVNldCA8LSBmcmVhZChwYXN0ZTAoIl9kYWlseVJlcG9ydGluZy8iLCBsRikpCmRhdGFTZXQgPC0gZmlsdGVyKGRhdGFTZXQsIAogICAgICAgICAgICAgICAgICAhZ3JlcGwoIl50ZXN0fF5UZXN0IiwgZGF0YVNldCR1c2VybmFtZSkpCmRhdGFTZXQkZGF0ZSA8LSBwYXN0ZShkYXRhU2V0JHllYXIsIAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKG5jaGFyKGRhdGFTZXQkbW9udGgpID09IDEsIHBhc3RlMCgiMCIsIGRhdGFTZXQkbW9udGgpLCBkYXRhU2V0JG1vbnRoKSwKICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShuY2hhcihkYXRhU2V0JGRheSkgPT0gMSwgcGFzdGUwKCIwIiwgZGF0YVNldCRkYXkpLCBkYXRhU2V0JGRheSksCiAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiLSIpCnJlZ1VzZXJzIDwtIHNlbGVjdChkYXRhU2V0LAogICAgICAgICAgICAgICAgICAgdXNlcmlkLAogICAgICAgICAgICAgICAgICAgdXNlcm5hbWUsIAogICAgICAgICAgICAgICAgICAgY2FtcGFpZ24pCiMgLSBleHBhbmQgZ3JpZCB0byBhY2NvdW50IGZvciBtaXNzaW5nIG9ic2VydmF0aW9ucyBwZXIgZGF5CmRhdGVTcGFuIDwtIHNlcShmcm9tID0gYXMuRGF0ZSgiMjAyMC0xMC0wMSIpLCAKICAgICAgICAgICAgICAgIHRvID0gYXMuRGF0ZSgiMjAyMC0xMS0xMSIpLCAKICAgICAgICAgICAgICAgIGJ5ID0gImRheSIpCmRhdGVTcGFuIDwtIGFzLmNoYXJhY3RlcihkYXRlU3BhbikKZFMgPC0gZXhwYW5kLmdyaWQoZGF0ZVNwYW4sIAogICAgICAgICAgICAgICAgICB1bmlxdWUoZGF0YVNldCRjYW1wYWlnbiksIAogICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXMoZFMpIDwtIGMoJ2RhdGUnLCAnY2FtcGFpZ24nKQpkUyA8LSBkUyAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChkYXRhU2V0LCBkYXRlLCBjYW1wYWlnbiwgdXNlcmlkKSwKICAgICAgICAgICAgYnkgPSBjKCJkYXRlIiwgImNhbXBhaWduIikpCmRTJHVzZXJpZCA8LSBpZmVsc2UoaXMubmEoZFMkdXNlcmlkKSwgMCwgMSkKcEZyYW1lIDwtIGRTICU+JSAKICBzZWxlY3QoZGF0ZSwgdXNlcmlkKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lIAogIHN1bW1hcmlzZShyZWdpc3RyYXRpb25zID0gc3VtKHVzZXJpZCkpICU+JSAKICBhcnJhbmdlKGRhdGUpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF0ZSwKICAgICAgICAgICAgICAgICAgIHkgPSByZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSByZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuMjUsIGdyb3VwID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAnKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiUmVnaXN0cmF0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyMgMi4yIFVzZXIgUmVnaXN0cmF0aW9ucyBwZXIgQ2FtcGFpZ24gQ2hhbm5lbAoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gOH0KcEZyYW1lIDwtIGRTICU+JSAKICBncm91cF9ieShkYXRlLCBjYW1wYWlnbikgJT4lIAogIHN1bW1hcmlzZShyZWdpc3RyYXRpb25zID0gc3VtKHVzZXJpZCkpICU+JSAKICBhcnJhbmdlKGRhdGUpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF0ZSwKICAgICAgICAgICAgICAgICAgIHkgPSByZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSByZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuMjUsIGdyb3VwID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgZmFjZXRfd3JhcCh+Y2FtcGFpZ24sIG5jb2wgPSAyKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ1dpa2lwZWRpYSBDaGFsbGVuZ2UgT2N0b2JlciAyMDIwJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIlJlZ2lzdHJhdGlvbnMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogICMgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNikpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyMgMi4zIFRvdGFsIFVzZXIgUmVnaXN0cmF0aW9ucyBwZXIgQ2FtcGFpZ24gQ2hhbm5lbAoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwRnJhbWUgPC0gZFMgJT4lIAogIHNlbGVjdChjYW1wYWlnbiwgdXNlcmlkKSAlPiUgCiAgZ3JvdXBfYnkoIGNhbXBhaWduKSAlPiUgCiAgc3VtbWFyaXNlKHJlZ2lzdHJhdGlvbnMgPSBzdW0odXNlcmlkKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhyZWdpc3RyYXRpb25zKSkKcEZyYW1lJGNhbXBhaWduIDwtIGZhY3RvcihwRnJhbWUkY2FtcGFpZ24sIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHBGcmFtZSRjYW1wYWlnbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFQpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gY2FtcGFpZ24sCiAgICAgICAgICAgICAgICAgICB5ID0gcmVnaXN0cmF0aW9ucywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gcmVnaXN0cmF0aW9ucywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvciA9ICJibHVlIiwgZmlsbCA9ICJ3aGl0ZSIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAnKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiUmVnaXN0cmF0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyAzLiBVc2VyIEVkaXRzCgpUaGlzIHNlY3Rpb24gcHJlc2VudHMgYWxsIGRhdGEgYW5kIHN0YXRpc3RpY3Mgb24gdGhlIHVzZXIgZWRpdHMuCgojIyMgMy4xIFVzZXIgRWRpdHMgT3ZlcnZpZXcKClRoZSBmb2xsb3dpbmcgY2h1bmsgbG9hZHMgdGhlIGRhdGFzZXQgb2YgdXNlciBpbnRlcmFjdGlvbnMgd2l0aCBjYW1wYWlnbiBjaGFubmVscyBhbmQgdGhlbiByZS1zdHJ1Y3R1cmVzIHRoZSBkYXRhc2V0IGEgYml0LgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmxGIDwtIGxpc3QuZmlsZXMoIl9kYWlseVJlcG9ydGluZyIpCmxGIDwtIGxGW2dyZXBsKCJedXNlckVkaXRzIiwgbEYpXQplZGl0U2V0IDwtIGZyZWFkKHBhc3RlMCgiX2RhaWx5UmVwb3J0aW5nLyIsIGxGKSkKZWRpdFNldCA8LSBzZWxlY3QoZWRpdFNldCwKICAgICAgICAgICAgICAgICAgYWN0b3JfbmFtZSwgCiAgICAgICAgICAgICAgICAgIHJldmFjdG9yX3RpbWVzdGFtcCkgICAgICAgICAgICAgICAgIApjb2xuYW1lcyhlZGl0U2V0KSA8LSBjKCd1c2VybmFtZScsICdyZXZfdGltZXN0YW1wJykKZGF0YVNldCA8LSBsZWZ0X2pvaW4oZWRpdFNldCwKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHJlZ1VzZXJzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZXJuYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ24pLAogICAgICAgICAgICAgICAgICAgICBieSA9ICJ1c2VybmFtZSIpCmRhdGFTZXQkeWVhciA8LSBzdWJzdHIoZGF0YVNldCRyZXZfdGltZXN0YW1wLCAxLCA0KQpkYXRhU2V0JG1vbnRoIDwtIHN1YnN0cihkYXRhU2V0JHJldl90aW1lc3RhbXAsIDUsIDYpCmRhdGFTZXQkZGF5IDwtIHN1YnN0cihkYXRhU2V0JHJldl90aW1lc3RhbXAsIDcsIDgpCmRhdGFTZXQkcmV2X3RpbWVzdGFtcCA8LSBOVUxMCmRhdGFTZXQkZGF0ZSA8LSBwYXN0ZShkYXRhU2V0JHllYXIsCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YVNldCRtb250aCwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhU2V0JGRheSwKICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiLSIpCmRhdGFTZXQgPC0gc2VsZWN0KGRhdGFTZXQsCiAgICAgICAgICAgICAgICAgIGRhdGUsCiAgICAgICAgICAgICAgICAgIHVzZXJuYW1lLAogICAgICAgICAgICAgICAgICBjYW1wYWlnbikKZGF0ZVNwYW4gPC0gc2VxKGZyb20gPSBhcy5EYXRlKCIyMDIwLTEwLTAxIiksIAogICAgICAgICAgICAgICAgdG8gPSBhcy5EYXRlKG1heChkYXRhU2V0JGRhdGUpKSwgCiAgICAgICAgICAgICAgICBieSA9ICJkYXkiKQpkYXRlU3BhbiA8LSBhcy5jaGFyYWN0ZXIoZGF0ZVNwYW4pCmRTIDwtIGV4cGFuZC5ncmlkKGRhdGVTcGFuLCAKICAgICAgICAgICAgICAgICAgdW5pcXVlKGRhdGFTZXQkY2FtcGFpZ24pLAogICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXMoZFMpIDwtIGMoJ2RhdGUnLCAnY2FtcGFpZ24nKQpkUyA8LSBkUyAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChkYXRhU2V0LCBkYXRlLCBjYW1wYWlnbiwgdXNlcm5hbWUpLAogICAgICAgICAgICBieSA9IGMoImRhdGUiLCAiY2FtcGFpZ24iKSkKZFMkdXNlcm5hbWUgPC0gaWZlbHNlKGlzLm5hKGRTJHVzZXJuYW1lKSwgMCwgMSkKcEZyYW1lIDwtIGRTICU+JQogIHNlbGVjdChkYXRlLCB1c2VybmFtZSkgJT4lIAogIGdyb3VwX2J5KGRhdGUpICU+JSAKICBzdW1tYXJpc2UoZWRpdHMgPSBzdW0odXNlcm5hbWUpKSAlPiUgCiAgYXJyYW5nZShkYXRlKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gZWRpdHMsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGVkaXRzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuMjUsIGdyb3VwID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnV2lraXBlZGlhIENoYWxsZW5nZSBPY3RvYmVyIDIwMjAnKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiRWRpdHMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKIyMjIDMuMiBVc2VyIEVkaXRzIGJ5IENhbXBhaWduIENoYW5uZWxzCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA4fQpwRnJhbWUgPC0gZFMgJT4lIAogIGdyb3VwX2J5KGRhdGUsIGNhbXBhaWduKSAlPiUgCiAgc3VtbWFyaXNlKGVkaXRzID0gc3VtKHVzZXJuYW1lKSkgJT4lIAogIGFycmFuZ2UoZGF0ZSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IGVkaXRzLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBlZGl0cywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjI1LCBncm91cCA9IDEpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIGZhY2V0X3dyYXAofmNhbXBhaWduLCBuY29sID0gMikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdXaWtpcGVkaWEgQ2hhbGxlbmdlIE9jdG9iZXIgMjAyMCcpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJFZGl0cyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgIyBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA1LjUpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKIyMjIDMuMyBUb3RhbCBVc2VyIEVkaXRzIGJ5IENhbXBhaWduIENoYW5uZWxzCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CnBGcmFtZSA8LSBkUyAlPiUgCiAgc2VsZWN0KGNhbXBhaWduLCB1c2VybmFtZSkgJT4lIAogIGdyb3VwX2J5KCBjYW1wYWlnbikgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IHN1bSh1c2VybmFtZSkpICU+JSAKICBhcnJhbmdlKGRlc2MoZWRpdHMpKQpwRnJhbWUkY2FtcGFpZ24gPC0gZmFjdG9yKHBGcmFtZSRjYW1wYWlnbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gcEZyYW1lJGNhbXBhaWduLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBjYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgIHkgPSBlZGl0cywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZWRpdHMsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgY29sb3IgPSAiYmx1ZSIsIGZpbGwgPSAid2hpdGUiKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ1dpa2lwZWRpYSBDaGFsbGVuZ2UgT2N0b2JlciAyMDIwJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIkVkaXRzIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCiMjIyAzLjQgRWRpdCBDbGFzc2VzCgojIyMjIDMuNC4xIEVkaXQgQ2xhc3NlczogYWxsIHVzZXJzCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuNX0KdXNlckNsYXNzIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdCh1c2VybmFtZSkgJT4lIAogIGdyb3VwX2J5KHVzZXJuYW1lKSAlPiUgCiAgc3VtbWFyaXNlKGVkaXRzID0gbigpKQplZGl0Qm91bmRhcmllcyA8LSBsaXN0KAogIGMoMCwgMSksIAogIGMoMiwgNCksCiAgYyg1LCA5KSwKICBjKDEwLCAyMCksCiAgYygyMSwgNTApLAogIGMoNTEsIDEwMCkKKQp1c2VyQ2xhc3MkZWRpdENsYXNzIDwtIHNhcHBseSh1c2VyQ2xhc3MkZWRpdHMsIGZ1bmN0aW9uKHgpIHsKICB3RUMgPC0gc2FwcGx5KGVkaXRCb3VuZGFyaWVzLCBmdW5jdGlvbih5KSB7CiAgICB4ID49IHlbMV0gJiB4IDw9IHlbMl0KICB9KQogIGlmIChzdW0od0VDKSA9PSAwKSB7CiAgICByZXR1cm4oIj4gMTAwIikKICB9IGVsc2UgewogICAgcmV0dXJuKHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMV0sCiAgICAgICAgICAgICAgICAgICIgLSAiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzJdLCAKICAgICAgICAgICAgICAgICAgIikiCiAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0pCmVkaXRDbGFzcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHVzZXJDbGFzcyRlZGl0Q2xhc3MpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGVkaXRDbGFzcykgPC0gYygnRWRpdCBDbGFzcycsICdOdW0uVXNlcnMnKQplZGl0Q2xhc3Mkb3JkZXIgPC0gYXMubnVtZXJpYyhzYXBwbHkoZWRpdENsYXNzJGBFZGl0IENsYXNzYCwgZnVuY3Rpb24oeCkgewogIGxvd2VyIDwtIHN0cl9leHRyYWN0KHgsICdbWzpkaWdpdDpdXSsnKQp9KSkKZWRpdENsYXNzIDwtIGFycmFuZ2UoZWRpdENsYXNzLCBvcmRlcikKZWRpdENsYXNzJG9yZGVyIDwtIE5VTEwKZGF0YXRhYmxlKGVkaXRDbGFzcykKYGBgCgojIyMjIDMuNC4yQSBFZGl0IENsYXNzZXMgcGVyIENhbXBhaWduIENoYW5uZWw6IGBXTURFXzIwMjBfY2hhbGxlbmdlXzFgCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuNX0KdXNlckNsYXNzIDwtIGRhdGFTZXQgJT4lIAogIGZpbHRlcihjYW1wYWlnbiA9PSAiV01ERV8yMDIwX2NoYWxsZW5nZV8xIikgJT4lIAogIHNlbGVjdCh1c2VybmFtZSkgJT4lIAogIGdyb3VwX2J5KHVzZXJuYW1lKSAlPiUgCiAgc3VtbWFyaXNlKGVkaXRzID0gbigpKQplZGl0Qm91bmRhcmllcyA8LSBsaXN0KAogIGMoMCwgMSksIAogIGMoMiwgNCksCiAgYyg1LCA5KSwKICBjKDEwLCAyMCksCiAgYygyMSwgNTApLAogIGMoNTEsIDEwMCkKKQp1c2VyQ2xhc3MkZWRpdENsYXNzIDwtIHNhcHBseSh1c2VyQ2xhc3MkZWRpdHMsIGZ1bmN0aW9uKHgpIHsKICB3RUMgPC0gc2FwcGx5KGVkaXRCb3VuZGFyaWVzLCBmdW5jdGlvbih5KSB7CiAgICB4ID49IHlbMV0gJiB4IDw9IHlbMl0KICB9KQogIGlmIChzdW0od0VDKSA9PSAwKSB7CiAgICByZXR1cm4oIj4gMTAwIikKICB9IGVsc2UgewogICAgcmV0dXJuKHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMV0sCiAgICAgICAgICAgICAgICAgICIgLSAiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzJdLCAKICAgICAgICAgICAgICAgICAgIikiCiAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0pCmVkaXRDbGFzcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHVzZXJDbGFzcyRlZGl0Q2xhc3MpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGVkaXRDbGFzcykgPC0gYygnRWRpdCBDbGFzcycsICdOdW0uVXNlcnMnKQplZGl0Q2xhc3Mkb3JkZXIgPC0gYXMubnVtZXJpYyhzYXBwbHkoZWRpdENsYXNzJGBFZGl0IENsYXNzYCwgZnVuY3Rpb24oeCkgewogIGxvd2VyIDwtIHN0cl9leHRyYWN0KHgsICdbWzpkaWdpdDpdXSsnKQp9KSkKZWRpdENsYXNzIDwtIGFycmFuZ2UoZWRpdENsYXNzLCBvcmRlcikKZWRpdENsYXNzJG9yZGVyIDwtIE5VTEwKZGF0YXRhYmxlKGVkaXRDbGFzcykKYGBgCgojIyMjIDMuNC4yQiBFZGl0IENsYXNzZXMgcGVyIENhbXBhaWduIENoYW5uZWw6IGBXTURFXzIwMjBfY2hhbGxlbmdlXzNgCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuNX0KdXNlckNsYXNzIDwtIGRhdGFTZXQgJT4lIAogIGZpbHRlcihjYW1wYWlnbiA9PSAiV01ERV8yMDIwX2NoYWxsZW5nZV8zIikgJT4lIAogIHNlbGVjdCh1c2VybmFtZSkgJT4lIAogIGdyb3VwX2J5KHVzZXJuYW1lKSAlPiUgCiAgc3VtbWFyaXNlKGVkaXRzID0gbigpKQplZGl0Qm91bmRhcmllcyA8LSBsaXN0KAogIGMoMCwgMSksIAogIGMoMiwgNCksCiAgYyg1LCA5KSwKICBjKDEwLCAyMCksCiAgYygyMSwgNTApLAogIGMoNTEsIDEwMCkKKQp1c2VyQ2xhc3MkZWRpdENsYXNzIDwtIHNhcHBseSh1c2VyQ2xhc3MkZWRpdHMsIGZ1bmN0aW9uKHgpIHsKICB3RUMgPC0gc2FwcGx5KGVkaXRCb3VuZGFyaWVzLCBmdW5jdGlvbih5KSB7CiAgICB4ID49IHlbMV0gJiB4IDw9IHlbMl0KICB9KQogIGlmIChzdW0od0VDKSA9PSAwKSB7CiAgICByZXR1cm4oIj4gMTAwIikKICB9IGVsc2UgewogICAgcmV0dXJuKHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMV0sCiAgICAgICAgICAgICAgICAgICIgLSAiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzJdLCAKICAgICAgICAgICAgICAgICAgIikiCiAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0pCmVkaXRDbGFzcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHVzZXJDbGFzcyRlZGl0Q2xhc3MpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGVkaXRDbGFzcykgPC0gYygnRWRpdCBDbGFzcycsICdOdW0uVXNlcnMnKQplZGl0Q2xhc3Mkb3JkZXIgPC0gYXMubnVtZXJpYyhzYXBwbHkoZWRpdENsYXNzJGBFZGl0IENsYXNzYCwgZnVuY3Rpb24oeCkgewogIGxvd2VyIDwtIHN0cl9leHRyYWN0KHgsICdbWzpkaWdpdDpdXSsnKQp9KSkKZWRpdENsYXNzIDwtIGFycmFuZ2UoZWRpdENsYXNzLCBvcmRlcikKZWRpdENsYXNzJG9yZGVyIDwtIE5VTEwKZGF0YXRhYmxlKGVkaXRDbGFzcykKYGBgCgojIyMjIDMuNC4yQyBFZGl0IENsYXNzZXMgcGVyIENhbXBhaWduIENoYW5uZWw6IGBXTURFXzIwMjBfY2hhbGxlbmdlXzRgCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuNX0KdXNlckNsYXNzIDwtIGRhdGFTZXQgJT4lIAogIGZpbHRlcihjYW1wYWlnbiA9PSAiV01ERV8yMDIwX2NoYWxsZW5nZV80IikgJT4lIAogIHNlbGVjdCh1c2VybmFtZSkgJT4lIAogIGdyb3VwX2J5KHVzZXJuYW1lKSAlPiUgCiAgc3VtbWFyaXNlKGVkaXRzID0gbigpKQplZGl0Qm91bmRhcmllcyA8LSBsaXN0KAogIGMoMCwgMSksIAogIGMoMiwgNCksCiAgYyg1LCA5KSwKICBjKDEwLCAyMCksCiAgYygyMSwgNTApLAogIGMoNTEsIDEwMCkKKQp1c2VyQ2xhc3MkZWRpdENsYXNzIDwtIHNhcHBseSh1c2VyQ2xhc3MkZWRpdHMsIGZ1bmN0aW9uKHgpIHsKICB3RUMgPC0gc2FwcGx5KGVkaXRCb3VuZGFyaWVzLCBmdW5jdGlvbih5KSB7CiAgICB4ID49IHlbMV0gJiB4IDw9IHlbMl0KICB9KQogIGlmIChzdW0od0VDKSA9PSAwKSB7CiAgICByZXR1cm4oIj4gMTAwIikKICB9IGVsc2UgewogICAgcmV0dXJuKHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMV0sCiAgICAgICAgICAgICAgICAgICIgLSAiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzJdLCAKICAgICAgICAgICAgICAgICAgIikiCiAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0pCmVkaXRDbGFzcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHVzZXJDbGFzcyRlZGl0Q2xhc3MpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGVkaXRDbGFzcykgPC0gYygnRWRpdCBDbGFzcycsICdOdW0uVXNlcnMnKQplZGl0Q2xhc3Mkb3JkZXIgPC0gYXMubnVtZXJpYyhzYXBwbHkoZWRpdENsYXNzJGBFZGl0IENsYXNzYCwgZnVuY3Rpb24oeCkgewogIGxvd2VyIDwtIHN0cl9leHRyYWN0KHgsICdbWzpkaWdpdDpdXSsnKQp9KSkKZWRpdENsYXNzIDwtIGFycmFuZ2UoZWRpdENsYXNzLCBvcmRlcikKZWRpdENsYXNzJG9yZGVyIDwtIE5VTEwKZGF0YXRhYmxlKGVkaXRDbGFzcykKYGBgCgoKIyMjIyAzLjQuMkQgRWRpdCBDbGFzc2VzIHBlciBDYW1wYWlnbiBDaGFubmVsOiBgV01ERV8yMDIwX2NoYWxsZW5nZV81YAoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnVzZXJDbGFzcyA8LSBkYXRhU2V0ICU+JSAKICBmaWx0ZXIoY2FtcGFpZ24gPT0gIldNREVfMjAyMF9jaGFsbGVuZ2VfNSIpICU+JSAKICBzZWxlY3QodXNlcm5hbWUpICU+JSAKICBncm91cF9ieSh1c2VybmFtZSkgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IG4oKSkKZWRpdEJvdW5kYXJpZXMgPC0gbGlzdCgKICBjKDAsIDEpLCAKICBjKDIsIDQpLAogIGMoNSwgOSksCiAgYygxMCwgMjApLAogIGMoMjEsIDUwKSwKICBjKDUxLCAxMDApCikKdXNlckNsYXNzJGVkaXRDbGFzcyA8LSBzYXBwbHkodXNlckNsYXNzJGVkaXRzLCBmdW5jdGlvbih4KSB7CiAgd0VDIDwtIHNhcHBseShlZGl0Qm91bmRhcmllcywgZnVuY3Rpb24oeSkgewogICAgeCA+PSB5WzFdICYgeCA8PSB5WzJdCiAgfSkKICBpZiAoc3VtKHdFQykgPT0gMCkgewogICAgcmV0dXJuKCI+IDEwMCIpCiAgfSBlbHNlIHsKICAgIHJldHVybihwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzFdLAogICAgICAgICAgICAgICAgICAiIC0gIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsyXSwgCiAgICAgICAgICAgICAgICAgICIpIgogICAgICAgICAgICAgICAgICApCiAgICApCiAgfQp9KQplZGl0Q2xhc3MgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZSh1c2VyQ2xhc3MkZWRpdENsYXNzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhlZGl0Q2xhc3MpIDwtIGMoJ0VkaXQgQ2xhc3MnLCAnTnVtLlVzZXJzJykKZWRpdENsYXNzJG9yZGVyIDwtIGFzLm51bWVyaWMoc2FwcGx5KGVkaXRDbGFzcyRgRWRpdCBDbGFzc2AsIGZ1bmN0aW9uKHgpIHsKICBsb3dlciA8LSBzdHJfZXh0cmFjdCh4LCAnW1s6ZGlnaXQ6XV0rJykKfSkpCmVkaXRDbGFzcyA8LSBhcnJhbmdlKGVkaXRDbGFzcywgb3JkZXIpCmVkaXRDbGFzcyRvcmRlciA8LSBOVUxMCmRhdGF0YWJsZShlZGl0Q2xhc3MpCmBgYAoKCiMjIyMgMy40LjJFIEVkaXQgQ2xhc3NlcyBwZXIgQ2FtcGFpZ24gQ2hhbm5lbDogYFdNREVfMjAyMF9jaGFsbGVuZ2VfNmAKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQp1c2VyQ2xhc3MgPC0gZGF0YVNldCAlPiUgCiAgZmlsdGVyKGNhbXBhaWduID09ICJXTURFXzIwMjBfY2hhbGxlbmdlXzYiKSAlPiUgCiAgc2VsZWN0KHVzZXJuYW1lKSAlPiUgCiAgZ3JvdXBfYnkodXNlcm5hbWUpICU+JSAKICBzdW1tYXJpc2UoZWRpdHMgPSBuKCkpCmVkaXRCb3VuZGFyaWVzIDwtIGxpc3QoCiAgYygwLCAxKSwgCiAgYygyLCA0KSwKICBjKDUsIDkpLAogIGMoMTAsIDIwKSwKICBjKDIxLCA1MCksCiAgYyg1MSwgMTAwKQopCnVzZXJDbGFzcyRlZGl0Q2xhc3MgPC0gc2FwcGx5KHVzZXJDbGFzcyRlZGl0cywgZnVuY3Rpb24oeCkgewogIHdFQyA8LSBzYXBwbHkoZWRpdEJvdW5kYXJpZXMsIGZ1bmN0aW9uKHkpIHsKICAgIHggPj0geVsxXSAmIHggPD0geVsyXQogIH0pCiAgaWYgKHN1bSh3RUMpID09IDApIHsKICAgIHJldHVybigiPiAxMDAiKQogIH0gZWxzZSB7CiAgICByZXR1cm4ocGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsxXSwKICAgICAgICAgICAgICAgICAgIiAtICIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMl0sIAogICAgICAgICAgICAgICAgICAiKSIKICAgICAgICAgICAgICAgICAgKQogICAgKQogIH0KfSkKZWRpdENsYXNzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUodXNlckNsYXNzJGVkaXRDbGFzcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXMoZWRpdENsYXNzKSA8LSBjKCdFZGl0IENsYXNzJywgJ051bS5Vc2VycycpCmVkaXRDbGFzcyRvcmRlciA8LSBhcy5udW1lcmljKHNhcHBseShlZGl0Q2xhc3MkYEVkaXQgQ2xhc3NgLCBmdW5jdGlvbih4KSB7CiAgbG93ZXIgPC0gc3RyX2V4dHJhY3QoeCwgJ1tbOmRpZ2l0Ol1dKycpCn0pKQplZGl0Q2xhc3MgPC0gYXJyYW5nZShlZGl0Q2xhc3MsIG9yZGVyKQplZGl0Q2xhc3Mkb3JkZXIgPC0gTlVMTApkYXRhdGFibGUoZWRpdENsYXNzKQpgYGAKCiMjIyMgMy40LjJGIEVkaXQgQ2xhc3NlcyBwZXIgQ2FtcGFpZ24gQ2hhbm5lbDogYFdNREVfMjAyMF9jaGFsbGVuZ2VfMTJgCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuNX0KdXNlckNsYXNzIDwtIGRhdGFTZXQgJT4lIAogIGZpbHRlcihjYW1wYWlnbiA9PSAiV01ERV8yMDIwX2NoYWxsZW5nZV8xMiIpICU+JSAKICBzZWxlY3QodXNlcm5hbWUpICU+JSAKICBncm91cF9ieSh1c2VybmFtZSkgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IG4oKSkKZWRpdEJvdW5kYXJpZXMgPC0gbGlzdCgKICBjKDAsIDEpLCAKICBjKDIsIDQpLAogIGMoNSwgOSksCiAgYygxMCwgMjApLAogIGMoMjEsIDUwKSwKICBjKDUxLCAxMDApCikKdXNlckNsYXNzJGVkaXRDbGFzcyA8LSBzYXBwbHkodXNlckNsYXNzJGVkaXRzLCBmdW5jdGlvbih4KSB7CiAgd0VDIDwtIHNhcHBseShlZGl0Qm91bmRhcmllcywgZnVuY3Rpb24oeSkgewogICAgeCA+PSB5WzFdICYgeCA8PSB5WzJdCiAgfSkKICBpZiAoc3VtKHdFQykgPT0gMCkgewogICAgcmV0dXJuKCI+IDEwMCIpCiAgfSBlbHNlIHsKICAgIHJldHVybihwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzFdLAogICAgICAgICAgICAgICAgICAiIC0gIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsyXSwgCiAgICAgICAgICAgICAgICAgICIpIgogICAgICAgICAgICAgICAgICApCiAgICApCiAgfQp9KQplZGl0Q2xhc3MgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZSh1c2VyQ2xhc3MkZWRpdENsYXNzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhlZGl0Q2xhc3MpIDwtIGMoJ0VkaXQgQ2xhc3MnLCAnTnVtLlVzZXJzJykKZWRpdENsYXNzJG9yZGVyIDwtIGFzLm51bWVyaWMoc2FwcGx5KGVkaXRDbGFzcyRgRWRpdCBDbGFzc2AsIGZ1bmN0aW9uKHgpIHsKICBsb3dlciA8LSBzdHJfZXh0cmFjdCh4LCAnW1s6ZGlnaXQ6XV0rJykKfSkpCmVkaXRDbGFzcyA8LSBhcnJhbmdlKGVkaXRDbGFzcywgb3JkZXIpCmVkaXRDbGFzcyRvcmRlciA8LSBOVUxMCmRhdGF0YWJsZShlZGl0Q2xhc3MpCmBgYAoKIyMjIyAzLjQuMkcgRWRpdCBDbGFzc2VzIHBlciBDYW1wYWlnbiBDaGFubmVsOiBgV01ERV8yMDIwX2NoYWxsZW5nZV8xNGAKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQp1c2VyQ2xhc3MgPC0gZGF0YVNldCAlPiUgCiAgZmlsdGVyKGNhbXBhaWduID09ICJXTURFXzIwMjBfY2hhbGxlbmdlXzE0IikgJT4lIAogIHNlbGVjdCh1c2VybmFtZSkgJT4lIAogIGdyb3VwX2J5KHVzZXJuYW1lKSAlPiUgCiAgc3VtbWFyaXNlKGVkaXRzID0gbigpKQplZGl0Qm91bmRhcmllcyA8LSBsaXN0KAogIGMoMCwgMSksIAogIGMoMiwgNCksCiAgYyg1LCA5KSwKICBjKDEwLCAyMCksCiAgYygyMSwgNTApLAogIGMoNTEsIDEwMCkKKQp1c2VyQ2xhc3MkZWRpdENsYXNzIDwtIHNhcHBseSh1c2VyQ2xhc3MkZWRpdHMsIGZ1bmN0aW9uKHgpIHsKICB3RUMgPC0gc2FwcGx5KGVkaXRCb3VuZGFyaWVzLCBmdW5jdGlvbih5KSB7CiAgICB4ID49IHlbMV0gJiB4IDw9IHlbMl0KICB9KQogIGlmIChzdW0od0VDKSA9PSAwKSB7CiAgICByZXR1cm4oIj4gMTAwIikKICB9IGVsc2UgewogICAgcmV0dXJuKHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMV0sCiAgICAgICAgICAgICAgICAgICIgLSAiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzJdLCAKICAgICAgICAgICAgICAgICAgIikiCiAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0pCmVkaXRDbGFzcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHVzZXJDbGFzcyRlZGl0Q2xhc3MpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGVkaXRDbGFzcykgPC0gYygnRWRpdCBDbGFzcycsICdOdW0uVXNlcnMnKQplZGl0Q2xhc3Mkb3JkZXIgPC0gYXMubnVtZXJpYyhzYXBwbHkoZWRpdENsYXNzJGBFZGl0IENsYXNzYCwgZnVuY3Rpb24oeCkgewogIGxvd2VyIDwtIHN0cl9leHRyYWN0KHgsICdbWzpkaWdpdDpdXSsnKQp9KSkKZWRpdENsYXNzIDwtIGFycmFuZ2UoZWRpdENsYXNzLCBvcmRlcikKZWRpdENsYXNzJG9yZGVyIDwtIE5VTEwKZGF0YXRhYmxlKGVkaXRDbGFzcykKYGBgCg==