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

The campaign was run from 2020/05/14 to 2020/05/27.

CURRENT UPDATE: Complete dataset as of 2020/05/27.

0. Data Acquisiton

NOTE: the Data Acquisition code chunk is not fully reproducible from this Report. The data are collected by running the script 2020_OccasionalEditors_PRODUCTION.R on stat1004.eqiad.wmnet, collecting the data as .tsv and .csv files, copying manually, and processing locally. A daily crontab job was run from 2020/05/14 to 2020/05/27 to collect the data for daily reporting. The data used in this report are aggregates of the daily datasets, sanitized and anonymized.

0.1 Daily Update

### --- WMDE 2020_OccasionalEditors_PRODUCTION.R
# - Campaign start: 2020/05/14
# - Campaign end: 2020/05/27
# - run from: stat1004
# - path: 

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

### --- dir structure
campaignPath <- paste0(getwd(), "/")
dataDir <- paste0(campaignPath, "_data/")
analyticsDir <- paste0(campaignPath, "_analytics/")
### --- campaign specifics
campaignName <- 'OccasionalEditors2020'

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

### ----------------------------------------------------------
### --- Banner Impressions
### ----------------------------------------------------------

# - function: wmde_collect_banner_impressions
wmde_collect_banner_impressions <- function(uri_host, 
                                            uri_path, 
                                            uri_query, 
                                            cetDay,
                                            queryFile,
                                            fileName,
                                            dataDir) {
  
  # - NOTE:
  # - expected format for cetDay is: YYYY-MM-DD
  
  # - to dataDir
  setwd(dataDir)
  
  # - libraries
  library(stringr)
  
  # - WHERE condition: create datetime_condition
  cet_condition <- seq(
    from = as.POSIXct(paste0(cetDay," 0:00"), tz = "Europe/Berlin"),
    to = as.POSIXct(paste0(cetDay," 23:00"), tz = "Europe/Berlin"),
    by = "hour"
  ) 
  attr(cet_condition, "tzone") <- "UTC"
  cet_condition <- as.character(cet_condition)
  cet_condition <- unlist(str_extract_all(cet_condition, "^([[:digit:]]|\\s|-)*"))
  cet_years <- sapply(
    strsplit(cet_condition, split = " ", fixed = T), function(x) {
      strsplit(x, split = "-")[[1]][1]
    })
  cet_months <- sapply(
    strsplit(cet_condition, split = " ", fixed = T), function(x) {
      strsplit(x, split = "-")[[1]][2]
    })
  cet_months <- gsub("^0", "", cet_months)
  cet_days <- sapply(
    strsplit(cet_condition, split = " ", fixed = T), function(x) {
      strsplit(x, split = "-")[[1]][3]
    })
  cet_days <- gsub("^0", "", cet_days)
  cet_hours <- sapply(strsplit(cet_condition, split = " ", fixed = T), 
                      function(x) {
                        x[2]
                      })
  cet_hours <- gsub("^0", "", cet_hours)
  datetimeCondition <- paste0(
    "year = ", cet_years, " AND ",
    "month = ", cet_months, " AND ",
    "day = ", cet_days, " AND ", 
    "hour = ", cet_hours
  )
  datetimeCondition <- paste("(", 
                             datetimeCondition, 
                             ")",
                             collapse = " OR ", 
                             sep = "")
  
  # - WHERE condition: create uri_path_condition
  if (length(uri_path) > 1) {
    uri_path_condition <- paste0("(",
                                 paste(
                                   paste0("uri_path = '", uri_path, "'"),
                                   collapse = " OR ", sep = " "),
                                 ")"
    )
  } else {
    uri_path_condition = paste0("uri_path = '", uri_path, "'")
  }
  
  # - WHERE condition: create uri_host_condition
  if (length(uri_host) > 1) {
    uri_host_condition <- paste0("(",
                                 paste(
                                   paste0("uri_host = '", uri_host, "'"),
                                   collapse = " OR ", sep = " "),
                                 ")"
    )
  } else {
    uri_host_condition = paste0("uri_host = '", uri_host, "'")
  }
  
  # - WHERE condition: create uri_query_condition
  if (length(uri_query) > 1) {
    uri_query_condition <- paste0("(",
                                  paste(
                                    paste0("uri_query LIKE '%", uri_query, "%'"),
                                    collapse = " OR ", sep = " "),
                                  ")"
    )
  } else {
    uri_query_condition = paste0("uri_query LIKE '%", uri_query, "%'")
  }
  
  # - compose HiveQL query
  hiveQuery <- paste0(
    "USE wmf;
    SELECT uri_query FROM webrequest
    WHERE (",
    uri_host_condition, " AND ",
    uri_path_condition, " AND ",
    uri_query_condition, " AND ",
    "(", datetimeCondition, ")",
    ");"
    )
  
  # - write hql
  write(hiveQuery, queryFile)
  # - execute hql script:
  hiveArgs <- 'sudo -u analytics-privatedata kerberos-run-command analytics-privatedata /usr/local/bin/beeline --silent --incremental=true --verbose=false -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 OccasionalEditors2020
uri_host <- c('de.wikipedia.org', 'de.m.wikipedia.org')
uri_path  <- '/beacon/impression'
uri_query <- c('WMDE_oceditors_spring_2020_')
queryFile <- 'OccasionalEditors2020_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, 
                                            uri_query) {
  
  # - to dataDir
  setwd(dataDir)
  
  # - libraries
  library(stringr)
  library(dplyr)
  
  # - load
  bannerData <- tryCatch({
    as.data.frame(fread(fileName))
    },
    error = function(condition) {
      return(FALSE)
  })
  # - process
  if (class(bannerData) == 'logical') {
    return(FALSE) 
  } else {
    # - clean
    bannerData <- dplyr::filter(bannerData,
                                uri_query != "")
    # - split
    bannerData <- tidyr::separate(bannerData, 
                                  col = uri_query, 
                                  into = c('country', 
                                           'region',
                                           'anonymous',
                                           'project',
                                           'db',
                                           'uselang', 
                                           'device', 
                                           'debug', 
                                           'randomcampaign',
                                           'randombanner',
                                           'recordImpressionSampleRate',
                                           'impressionEventSampleRate',
                                           'campaignStatuses',
                                           'status',
                                           'statusCode',
                                           'campaign',
                                           'campaignCategory',
                                           'campaignCategoryUsesLegacy',
                                           'bucket',
                                           'banner',
                                           'bannerCategory', 
                                           'result'),
                                           sep = "&") %>% 
      dplyr::select(banner, device, recordImpressionSampleRate, result)
    # - filter for uri_query
    bannerData <- bannerData[grepl(uri_query, bannerData$banner), ]
    # - clean relevant fields
    # - banner:
    bannerData$banner <- gsub("^banner=", "", bannerData$banner)
    # - recordImpressionSampleRate:
    bannerData$recordImpressionSampleRate <- as.numeric(
      gsub("^recordImpressionSampleRate=", "", bannerData$recordImpressionSampleRate)
    )
    # - device:
    bannerData$device <- gsub("^device=", "", bannerData$device)
    # - result:
    bannerData$result <- gsub("^result=", "", bannerData$result)
    # - filter for result=show
    bannerData <- dplyr::filter(bannerData,
                                result == "show")
    # - correction for recordImpressionSampleRate
    bannerData$recordImpressionSampleRate <- 
      1/bannerData$recordImpressionSampleRate
    
    # - aggregate:
    bannerData <- bannerData %>% 
      dplyr::select(banner, device, recordImpressionSampleRate) %>% 
      dplyr::group_by(banner, device) %>% 
      dplyr::summarise(impressions = sum(recordImpressionSampleRate))
    
    # - add cetDay, me
    bannerData$date <- cetDay
    bannerData$campaign <- campaignName
    
    # - store:
    write.csv(bannerData, 
              paste0(analyticsDir, "bannerImpressions",
                     cetDay,
                     ".csv"
              )
    )
    
    # - return
    return(TRUE)
  }
}

# - wrangle Banner Impression data
campaignName <- "OccasionalEditors2020"
uri_query <- c('WMDE_oceditors_spring_2020_')
bannerProcess <- wmde_process_banner_impressions(fileName = fileName,
                                                 dataDir = dataDir,
                                                 cetDay = cetDay,
                                                 campaignName = campaignName,
                                                 uri_query = uri_query)



### ----------------------------------------------------------
### --- 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
# - for the 2020_EmailCampaignWikipediaChallenge
uri_host <- c('de.wikipedia.org', 'de.m.wikipedia.org')
uri_path  <- c(
  '/wiki/Wikipedia:Wikimedia_Deutschland/DeinEngagement',
  '/wiki/Wikipedia:Wikimedia_Deutschland/DeinEngagement/Literatur',
  '/wiki/Wikipedia:Mentorenprogramm')
# uri_query <- paste0('WMDE_2020_challenge_', 1:30)
queryFile <- 'OccasionalEditors2020_Pageviews.hql'
fileName <- paste0("pageviews_", cetDay, ".tsv")

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

### --- Wrangle Pageviews
# - function: wmde_process_pageviews
wmde_process_pageviews <- function(fileName,
                                   dataDir, 
                                   uri_query_filter,
                                   cetDay = cetDay,
                                   campaignName = campaignName) {
  
  # - to dataDir
  setwd(dataDir)
  
  # - libraries
  library(stringr)
  library(dplyr)
  library(tidyr)
  library(data.table)
  
  # - load
  pageviewsData <- readLines(fileName)
  wStart <- which(grepl("uri_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 uri_query_filter
  # - NOTE: looking in both: uri_query, referer 
  # - NOTE: hack for the 2020_OccasionalEditors Campaign
  # - include pageviews for 
  # - '/wiki/Wikipedia:Wikimedia_Deutschland/DeinEngagement/Literatur',
  # - '/wiki/Wikipedia:Mentorenprogramm'
  # - on dewiki where grepl(w_uri_query, referer)
  w_uri_query_referer <- which(grepl(uri_query_filter, pageviewsData$referer) & 
                                 !grepl(uri_query_filter, pageviewsData$uri_query))
  if (length(w_uri_query_referer) > 0) {
    refererTags <- strsplit(pageviewsData$referer[w_uri_query_referer], 
                            split = "?",
                            fixed = T)
    refererTags <- sapply(refererTags, function(x) {x[2]})
    refererTags <- paste0("?", refererTags)
    pageviewsData$uri_query[w_uri_query_referer] <- refererTags 
  }
  w_uri_query <- which(grepl(uri_query_filter, pageviewsData$uri_query))
  
  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$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_oceditors_spring_2020_'

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

### ----------------------------------------------------------
### --- Banner Actions
### --- via event.WMDEBannerActions
### ----------------------------------------------------------

### ___ NOTE:
# - Suffix explanation:
# - ctrl is the banner that dynamically displays text depending on the target group
# - var is the banner that shows the same text for both target groups
# - ipad/mobile does not represent the actual device type, but the display mode of the banner (small desktop screens may be reported as ipad)
# - cs/nt indicates, which target group the user belongs to
# - Kai Nissen in https://phabricator.wikimedia.org/T251535#6132468

# - 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_oceditors_spring_2020_'
banner_status <- wmde_banner_actions(uri_query_filter = uri_query_filter,
                                     cetDay = cetDay,
                                     queryFile = queryFile,
                                     fileName = fileName,
                                     analyticsDir = analyticsDir, 
                                     campaignName = campaignName)

1. Campaign Banners

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

lF <- list.files('_analytics')
lF <- lF[grepl("^bannerInteractions", lF)]
dataSet <- lapply(paste0("_analytics/", 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$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")
  }
})
dataSet$cs_nt <- sapply(dataSet$bannername, function(x) {
  if (grepl("cs", x)) {return("cs")} else {return("nt")}
})

2. 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. NOTE. The 3all campaign tag was observed only once, on 2020/05/14, and is removed from analysis.

lF <- list.files('_analytics')
lF <- lF[grepl("^pageviewsAggregated", lF)]
dataSet <- lapply(paste0("_analytics/", 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
# - banner codes
dataSet$var_ctrl <- sapply(dataSet$Tag, function(x) {
  if (grepl("var", x)) {return("var")} else {return("ctrl")}
})
dataSet$device <- sapply(dataSet$Tag, function(x) {
  if (grepl("mobile", x)) {
    return("mobile")
  } else if (grepl("ipad", x)) {
      return("ipad")
  } else {
      return("desktop")
  }
})
dataSet$cs_nt <- sapply(dataSet$Tag, function(x) {
  if (grepl("cs", x)) {return("cs")} else {return("nt")}
})
dataSet$Tag <- gsub("\\?campaign=WMDE_oceditors_spring_2020_", "", dataSet$Tag)
dataSet$Page <- gsub("de\\.wikipedia\\.org/wiki/Wikipedia:|de\\.m\\.wikipedia\\.org/wiki/Wikipedia:", "", dataSet$Page)
# - remove 3all tag:
dataSet <- filter(dataSet, Tag != "3all")

2.1 Pageviews Overview

Chart 2.1.1 Daily Pageviews, aggregated across the campaign banners.

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,
                   group = Page,
                   color = Page,
                   fill = Page,
                   label = Pageviews,
                    )) + 
  geom_path(size = .5) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  scale_y_continuous(labels = comma) +
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  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")

2.2 Pageviews: Devices

Reminder. From the Campaign Tracking Concept:

ipad/mobile does not represent the actual device type, but the display mode of the banner (small desktop screens may be reported as ipad)

Chart 2.2.1 Pageviews, by devices, aggregated across the campaign banners.

pFrame <- dataSet %>% 
  select(date, device, Page, Pageviews) %>% 
  group_by(date, device, Page) %>% 
  summarise(Pageviews = sum(Pageviews))
pFrame <- arrange(pFrame, date)
ggplot(pFrame, aes(x = date,
                   y = Pageviews,
                   group = Page,
                   color = Page,
                   fill = Page,
                   label = Pageviews,
                    )) + 
  geom_path(size = .5) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  geom_text_repel(size = 3.5, show.legend = FALSE) + 
  scale_y_continuous(labels = comma) +
  facet_wrap(~device, nrow = 3, scales = "free") + 
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Date") + ylab("Actions") + 
  theme_minimal() + 
  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.3 Pageviews: Var vs. Ctrl

Reminder. From the Campaign Tracking Concept:

ctrl is the banner that dynamically displays text depending on the target group

var is the banner that shows the same text for both target groups

Chart 1.3.1 Pageviews, by var/ctrl, aggregated across the campaign banners.

pFrame <- dataSet %>% 
  select(date, var_ctrl, Page, Pageviews) %>% 
  group_by(date, var_ctrl, Page) %>% 
  summarise(Pageviews = sum(Pageviews))
pFrame <- arrange(pFrame, date)
ggplot(pFrame, aes(x = date,
                   y = Pageviews,
                   group = Page,
                   color = Page,
                   fill = Page,
                   label = Pageviews,
                    )) + 
  geom_path(size = .5) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  geom_text_repel(size = 3.5, show.legend = FALSE) + 
  scale_y_continuous(labels = comma) +
  facet_wrap(~var_ctrl, nrow = 3, scales = "free") + 
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Date") + ylab("Actions") + 
  theme_minimal() + 
  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.4 Pageviews: nt vs. cs

Reminder. From the Campaign Tracking Concept:

cs/nt indicates, which target group the user belongs to (cs = community support, nt = new tasks)

Chart 1.4.1 Pageviews, by nt/cs, aggregated across the campaign banners.

pFrame <- dataSet %>% 
  select(date, cs_nt, Page, Pageviews) %>% 
  group_by(date, cs_nt, Page) %>% 
  summarise(Pageviews = sum(Pageviews))
pFrame <- arrange(pFrame, date)
ggplot(pFrame, aes(x = date,
                   y = Pageviews,
                   group = Page,
                   color = Page,
                   fill = Page,
                   label = Pageviews,
                    )) + 
  geom_path(size = .5) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  geom_text_repel(size = 3.5, show.legend = FALSE) + 
  scale_y_continuous(labels = comma) +
  facet_wrap(~cs_nt, nrow = 3, scales = "free") + 
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Date") + ylab("Actions") + 
  theme_minimal() + 
  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.5 Pageviews: Full Dataset

Table 2.5.1 Pageviews dataset

### --- Full Dataset (Table Report)
datatable(dataSet, 
          options = list(pageLength = 30)
          )

3. User Edits

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

3.1 The Assignment of Users to Campaign Banners

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

lF <- list.files("_data")
lF <- lF[grepl("^bannerInteractions", lF)]
dataSet <- lapply(paste0("_data/", lF), fread)
dataSet <- rbindlist(dataSet)
dataSet <- filter(dataSet, banneraction == "banner-clicked")
dataSet <- select(dataSet, bannername, userid, dt)
dataSet <- arrange(dataSet, userid, dt)
dataSet <- dataSet[!duplicated(dataSet$userid), ]
dataSet$dt <- NULL
dataSet$bannername <- gsub("WMDE_oceditors_spring_2020_", "", dataSet$bannername)
dataSet$bannername <- gsub("mobile_|ipad_", "", dataSet$bannername)
# - banner codes
dataSet$var_ctrl <- sapply(dataSet$bannername, function(x) {
  if (grepl("var", x)) {return("var")} else {return("ctrl")}
})
dataSet$cs_nt <- sapply(dataSet$bannername, function(x) {
  if (grepl("cs", x)) {return("cs")} else {return("nt")}
})
users <- select(dataSet, userid, cs_nt)
rm(dataSet)

Because the assingment of users to cs (community support) vs. nt (new tasks) was unique, we will focus on this variable in the campaign design in the analysis of user edits.

userEdits <- read.csv("_analytics/userEdits.csv", 
                      header = T,
                      row.names = 1,
                      check.names = F,
                      stringsAsFactors = F)
userEdits$revactor_timestamp <- as.character(userEdits$revactor_timestamp)
userEdits <- left_join(userEdits,
                       users,
                       by = c("actor_user" = "userid"))
userEdits$actor_id <- NULL
rm(users)
userEdits$date <- paste0(
  substr(userEdits$revactor_timestamp, 1, 4),
  "-",
  substr(userEdits$revactor_timestamp, 5, 6),
  "-",
  substr(userEdits$revactor_timestamp, 7, 8)
)

3.2 Daily user edits since the campaign onset

pFrame <- userEdits %>% 
  select(date) %>% 
  group_by(date) %>% 
  summarise(Edits = n())
pFrame <- arrange(pFrame, date)
campaignDays <- c("2020-05-14", "2020-05-15", "2020-05-16", "2020-05-17", "2020-05-18", "2020-05-19", 
                  "2020-05-20", "2020-05-21", "2020-05-22", "2020-05-23", "2020-05-24", "2020-05-25", 
                  "2020-05-26", "2020-05-27")
pFrame$campaign <- ifelse(pFrame$date %in% campaignDays, 
                          "Campaign", 
                          "After Campaign")
ggplot(pFrame, aes(x = date,
                   y = Edits,
                   group = campaign,
                   color = campaign,
                   fill = campaign,
                   label = Edits,
                    )) + 
  geom_path(size = .5) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  geom_text_repel(size = 3.5, show.legend = FALSE) + 
  scale_y_continuous(labels = comma) +
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Date") + ylab("Edits") + 
  theme_minimal() + 
  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.3 Daily user edits since the campaign onset by cs/nt

pFrame <- userEdits %>% 
  select(date, cs_nt) %>% 
  group_by(date, cs_nt) %>% 
  summarise(Edits = n())
pFrame <- arrange(pFrame, date)
campaignDays <- c("2020-05-14", "2020-05-15", "2020-05-16", "2020-05-17", "2020-05-18", "2020-05-19", 
                  "2020-05-20", "2020-05-21", "2020-05-22", "2020-05-23", "2020-05-24", "2020-05-25", 
                  "2020-05-26", "2020-05-27")
pFrame$campaign <- ifelse(pFrame$date %in% campaignDays, 
                          "Campaign", 
                          "After Campaign")
ggplot(pFrame, aes(x = date,
                   y = Edits,
                   group = campaign,
                   color = campaign,
                   fill = campaign,
                   label = Edits,
                    )) + 
  geom_path(size = .5) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  geom_text_repel(size = 3.5, show.legend = FALSE) + 
  facet_wrap(~cs_nt, ncol = 1, scales = "free") + 
  scale_y_continuous(labels = comma) +
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Date") + ylab("Edits") + 
  theme_minimal() + 
  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 <- userEdits %>% 
  select(actor_user) %>% 
  group_by(actor_user) %>% 
  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.2 Edit Classes: cs (community support) users

userClass <- userEdits %>% 
  filter(cs_nt == "cs") %>% 
  select(actor_user) %>% 
  group_by(actor_user) %>% 
  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.2 Edit Classes: nt (new tasks) users

userClass <- userEdits %>% 
  filter(cs_nt == "nt") %>% 
  select(actor_user) %>% 
  group_by(actor_user) %>% 
  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)
LS0tCnRpdGxlOiAnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nCmF1dGhvcjogIkdvcmFuIFMuIE1pbG92YW5vdmljLCBEYXRhIFNjaWVudGlzdCwgV01ERSIKZGF0ZTogIkp1bmUgMTIsIDIwMjAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0aGVtZTogc2ltcGxleAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDUKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQotLS0KCioqRmVlZGJhY2sqKiBzaG91bGQgYmUgc2VuZCB0byBgZ29yYW4ubWlsb3Zhbm92aWNfZXh0QHdpa2ltZWRpYS5kZWAuIAoKVGhlIGNhbXBhaWduIHdhcyBydW4gZnJvbSAyMDIwLzA1LzE0IHRvIDIwMjAvMDUvMjcuCgoqKkNVUlJFTlQgVVBEQVRFOioqIENvbXBsZXRlIGRhdGFzZXQgYXMgb2YgMjAyMC8wNS8yNy4KCmBgYHtyLCBlY2hvID0gRiwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEYsIHJlc3VsdHMgPSAnaGlkZSd9CiMgIWRpYWdub3N0aWNzIG9mZgojIyMgLS0tIFNldHVwCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoRFQpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpgYGAKCiMjIDAuIERhdGEgQWNxdWlzaXRvbgoKKipOT1RFOioqIHRoZSBEYXRhIEFjcXVpc2l0aW9uIGNvZGUgY2h1bmsgaXMgbm90IGZ1bGx5IHJlcHJvZHVjaWJsZSBmcm9tIHRoaXMgUmVwb3J0LiBUaGUgZGF0YSBhcmUgY29sbGVjdGVkIGJ5IHJ1bm5pbmcgdGhlIHNjcmlwdCBgMjAyMF9PY2Nhc2lvbmFsRWRpdG9yc19QUk9EVUNUSU9OLlJgIG9uIHN0YXQxMDA0LmVxaWFkLndtbmV0LCBjb2xsZWN0aW5nIHRoZSBkYXRhIGFzIGAudHN2YCBhbmQgYC5jc3ZgIGZpbGVzLCBjb3B5aW5nIG1hbnVhbGx5LCBhbmQgcHJvY2Vzc2luZyBsb2NhbGx5LiBBIGRhaWx5IGNyb250YWIgam9iIHdhcyBydW4gZnJvbSBgMjAyMC8wNS8xNGAgdG8gYDIwMjAvMDUvMjdgIHRvIGNvbGxlY3QgdGhlIGRhdGEgZm9yIGRhaWx5IHJlcG9ydGluZy4gVGhlIGRhdGEgdXNlZCBpbiB0aGlzIHJlcG9ydCBhcmUgYWdncmVnYXRlcyBvZiB0aGUgZGFpbHkgZGF0YXNldHMsIHNhbml0aXplZCBhbmQgYW5vbnltaXplZC4gICAKCiMjIyAwLjEgRGFpbHkgVXBkYXRlCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGfQojIyMgLS0tIFdNREUgMjAyMF9PY2Nhc2lvbmFsRWRpdG9yc19QUk9EVUNUSU9OLlIKIyAtIENhbXBhaWduIHN0YXJ0OiAyMDIwLzA1LzE0CiMgLSBDYW1wYWlnbiBlbmQ6IDIwMjAvMDUvMjcKIyAtIHJ1biBmcm9tOiBzdGF0MTAwNAojIC0gcGF0aDogCgojIyMgLS0tIGxpYnJhcmllcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKCiMjIyAtLS0gZGlyIHN0cnVjdHVyZQpjYW1wYWlnblBhdGggPC0gcGFzdGUwKGdldHdkKCksICIvIikKZGF0YURpciA8LSBwYXN0ZTAoY2FtcGFpZ25QYXRoLCAiX2RhdGEvIikKYW5hbHl0aWNzRGlyIDwtIHBhc3RlMChjYW1wYWlnblBhdGgsICJfYW5hbHl0aWNzLyIpCiMjIyAtLS0gY2FtcGFpZ24gc3BlY2lmaWNzCmNhbXBhaWduTmFtZSA8LSAnT2NjYXNpb25hbEVkaXRvcnMyMDIwJwoKIyMjIC0tLSBkZXRlcm1pbmUgY2V0RGF5CmNldERheSA8LSBTeXMudGltZSgpCmNldERheQphdHRyKGNldERheSwgInR6b25lIikgPC0gIkV1cm9wZS9CZXJsaW4iCiMgLSBvbmUgZGF5IGJlaGluZCBmb3IgY3JvbnRhYgojIC0gKGkuZS4gd2FpdGluZyBmb3Igd21mLndlYnJlcXVlc3QgdG8gY29tcGxldGUgaXMgZGF0YSBhY3F1aXNpdGlvbikKY2V0RGF5IDwtIHltZCgKICBzdHJzcGxpdChhcy5jaGFyYWN0ZXIoY2V0RGF5KSwgCiAgICAgICAgICAgc3BsaXQgPSAiICIsIAogICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXQopIC0gMQoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBCYW5uZXIgSW1wcmVzc2lvbnMKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLSBmdW5jdGlvbjogd21kZV9jb2xsZWN0X2Jhbm5lcl9pbXByZXNzaW9ucwp3bWRlX2NvbGxlY3RfYmFubmVyX2ltcHJlc3Npb25zIDwtIGZ1bmN0aW9uKHVyaV9ob3N0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcGF0aCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIpIHsKICAKICAjIC0gTk9URToKICAjIC0gZXhwZWN0ZWQgZm9ybWF0IGZvciBjZXREYXkgaXM6IFlZWVktTU0tREQKICAKICAjIC0gdG8gZGF0YURpcgogIHNldHdkKGRhdGFEaXIpCiAgCiAgIyAtIGxpYnJhcmllcwogIGxpYnJhcnkoc3RyaW5ncikKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgZGF0ZXRpbWVfY29uZGl0aW9uCiAgY2V0X2NvbmRpdGlvbiA8LSBzZXEoCiAgICBmcm9tID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMDowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICB0byA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDIzOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIGJ5ID0gImhvdXIiCiAgKSAKICBhdHRyKGNldF9jb25kaXRpb24sICJ0em9uZSIpIDwtICJVVEMiCiAgY2V0X2NvbmRpdGlvbiA8LSBhcy5jaGFyYWN0ZXIoY2V0X2NvbmRpdGlvbikKICBjZXRfY29uZGl0aW9uIDwtIHVubGlzdChzdHJfZXh0cmFjdF9hbGwoY2V0X2NvbmRpdGlvbiwgIl4oW1s6ZGlnaXQ6XV18XFxzfC0pKiIpKQogIGNldF95ZWFycyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsxXQogICAgfSkKICBjZXRfbW9udGhzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzJdCiAgICB9KQogIGNldF9tb250aHMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X21vbnRocykKICBjZXRfZGF5cyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVszXQogICAgfSkKICBjZXRfZGF5cyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfZGF5cykKICBjZXRfaG91cnMgPC0gc2FwcGx5KHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCAKICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgeFsyXQogICAgICAgICAgICAgICAgICAgICAgfSkKICBjZXRfaG91cnMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2hvdXJzKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlMCgKICAgICJ5ZWFyID0gIiwgY2V0X3llYXJzLCAiIEFORCAiLAogICAgIm1vbnRoID0gIiwgY2V0X21vbnRocywgIiBBTkQgIiwKICAgICJkYXkgPSAiLCBjZXRfZGF5cywgIiBBTkQgIiwgCiAgICAiaG91ciA9ICIsIGNldF9ob3VycwogICkKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZSgiKCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGV0aW1lQ29uZGl0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSB1cmlfcGF0aF9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9wYXRoKSA+IDEpIHsKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX3BhdGhfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfcGF0aCA9ICciLCB1cmlfcGF0aCwgIiciKQogIH0KICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX2hvc3RfY29uZGl0aW9uCiAgaWYgKGxlbmd0aCh1cmlfaG9zdCkgPiAxKSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfaG9zdCA9ICciLCB1cmlfaG9zdCwgIiciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiA9IHBhc3RlMCgidXJpX2hvc3QgPSAnIiwgdXJpX2hvc3QsICInIikKICB9CiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9xdWVyeV9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9xdWVyeSkgPiAxKSB7CiAgICB1cmlfcXVlcnlfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfcXVlcnkgTElLRSAnJSIsIHVyaV9xdWVyeSwgIiUnIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9xdWVyeV9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9xdWVyeSBMSUtFICclIiwgdXJpX3F1ZXJ5LCAiJSciKQogIH0KICAKICAjIC0gY29tcG9zZSBIaXZlUUwgcXVlcnkKICBoaXZlUXVlcnkgPC0gcGFzdGUwKAogICAgIlVTRSB3bWY7CiAgICBTRUxFQ1QgdXJpX3F1ZXJ5IEZST00gd2VicmVxdWVzdAogICAgV0hFUkUgKCIsCiAgICB1cmlfaG9zdF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcGF0aF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcXVlcnlfY29uZGl0aW9uLCAiIEFORCAiLAogICAgIigiLCBkYXRldGltZUNvbmRpdGlvbiwgIikiLAogICAgIik7IgogICAgKQogIAogICMgLSB3cml0ZSBocWwKICB3cml0ZShoaXZlUXVlcnksIHF1ZXJ5RmlsZSkKICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0OgogIGhpdmVBcmdzIDwtICdzdWRvIC11IGFuYWx5dGljcy1wcml2YXRlZGF0YSBrZXJiZXJvcy1ydW4tY29tbWFuZCBhbmFseXRpY3MtcHJpdmF0ZWRhdGEgL3Vzci9sb2NhbC9iaW4vYmVlbGluZSAtLXNpbGVudCAtLWluY3JlbWVudGFsPXRydWUgLS12ZXJib3NlPWZhbHNlIC1mJwogIGhpdmVJbnB1dCA8LSBwYXN0ZTAocXVlcnlGaWxlLCAnID4gJywgZmlsZU5hbWUpCiAgIyAtIGNvbW1hbmQ6CiAgaGl2ZUNvbW1hbmQgPC0gcGFzdGUoaGl2ZUFyZ3MsIGhpdmVJbnB1dCkKICByZXR1cm4oCiAgICBzeXN0ZW0oY29tbWFuZCA9IGhpdmVDb21tYW5kLCB3YWl0ID0gVFJVRSkKICApCn0KCiMgLSBzZXQgcGFyYW1zIHRvIHdtZGVfY29sbGVjdF9iYW5uZXJfaW1wcmVzc2lvbnMKIyAtIGZvciB0aGUgT2NjYXNpb25hbEVkaXRvcnMyMDIwCnVyaV9ob3N0IDwtIGMoJ2RlLndpa2lwZWRpYS5vcmcnLCAnZGUubS53aWtpcGVkaWEub3JnJykKdXJpX3BhdGggIDwtICcvYmVhY29uL2ltcHJlc3Npb24nCnVyaV9xdWVyeSA8LSBjKCdXTURFX29jZWRpdG9yc19zcHJpbmdfMjAyMF8nKQpxdWVyeUZpbGUgPC0gJ09jY2FzaW9uYWxFZGl0b3JzMjAyMF9CYW5uZXJJbXByZXNzaW9ucy5ocWwnCmZpbGVOYW1lIDwtIHBhc3RlMChkYXRhRGlyLCAiYmFubmVySW1wcmVzc2lvbnNfIiwgY2V0RGF5LCAiLnRzdiIpCgojIC0gY29sbGVjdCBCYW5uZXIgSW1wcmVzc2lvbiBkYXRhCndtZGVfY29sbGVjdF9iYW5uZXJfaW1wcmVzc2lvbnModXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpcikKCiMgLSBmdW5jdGlvbjogd21kZV9wcm9jZXNzX2Jhbm5lcl9pbXByZXNzaW9ucwp3bWRlX3Byb2Nlc3NfYmFubmVyX2ltcHJlc3Npb25zIDwtIGZ1bmN0aW9uKGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcXVlcnkpIHsKICAKICAjIC0gdG8gZGF0YURpcgogIHNldHdkKGRhdGFEaXIpCiAgCiAgIyAtIGxpYnJhcmllcwogIGxpYnJhcnkoc3RyaW5ncikKICBsaWJyYXJ5KGRwbHlyKQogIAogICMgLSBsb2FkCiAgYmFubmVyRGF0YSA8LSB0cnlDYXRjaCh7CiAgICBhcy5kYXRhLmZyYW1lKGZyZWFkKGZpbGVOYW1lKSkKICAgIH0sCiAgICBlcnJvciA9IGZ1bmN0aW9uKGNvbmRpdGlvbikgewogICAgICByZXR1cm4oRkFMU0UpCiAgfSkKICAjIC0gcHJvY2VzcwogIGlmIChjbGFzcyhiYW5uZXJEYXRhKSA9PSAnbG9naWNhbCcpIHsKICAgIHJldHVybihGQUxTRSkgCiAgfSBlbHNlIHsKICAgICMgLSBjbGVhbgogICAgYmFubmVyRGF0YSA8LSBkcGx5cjo6ZmlsdGVyKGJhbm5lckRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5ICE9ICIiKQogICAgIyAtIHNwbGl0CiAgICBiYW5uZXJEYXRhIDwtIHRpZHlyOjpzZXBhcmF0ZShiYW5uZXJEYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IHVyaV9xdWVyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRvID0gYygnY291bnRyeScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3JlZ2lvbicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYW5vbnltb3VzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdwcm9qZWN0JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdkYicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAndXNlbGFuZycsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2RldmljZScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2RlYnVnJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAncmFuZG9tY2FtcGFpZ24nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3JhbmRvbWJhbm5lcicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAncmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2ltcHJlc3Npb25FdmVudFNhbXBsZVJhdGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2NhbXBhaWduU3RhdHVzZXMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3N0YXR1cycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnc3RhdHVzQ29kZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnY2FtcGFpZ24nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2NhbXBhaWduQ2F0ZWdvcnknLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2NhbXBhaWduQ2F0ZWdvcnlVc2VzTGVnYWN5JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdidWNrZXQnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2Jhbm5lcicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYmFubmVyQ2F0ZWdvcnknLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdyZXN1bHQnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICImIikgJT4lIAogICAgICBkcGx5cjo6c2VsZWN0KGJhbm5lciwgZGV2aWNlLCByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSwgcmVzdWx0KQogICAgIyAtIGZpbHRlciBmb3IgdXJpX3F1ZXJ5CiAgICBiYW5uZXJEYXRhIDwtIGJhbm5lckRhdGFbZ3JlcGwodXJpX3F1ZXJ5LCBiYW5uZXJEYXRhJGJhbm5lciksIF0KICAgICMgLSBjbGVhbiByZWxldmFudCBmaWVsZHMKICAgICMgLSBiYW5uZXI6CiAgICBiYW5uZXJEYXRhJGJhbm5lciA8LSBnc3ViKCJeYmFubmVyPSIsICIiLCBiYW5uZXJEYXRhJGJhbm5lcikKICAgICMgLSByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZToKICAgIGJhbm5lckRhdGEkcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUgPC0gYXMubnVtZXJpYygKICAgICAgZ3N1YigiXnJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlPSIsICIiLCBiYW5uZXJEYXRhJHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlKQogICAgKQogICAgIyAtIGRldmljZToKICAgIGJhbm5lckRhdGEkZGV2aWNlIDwtIGdzdWIoIl5kZXZpY2U9IiwgIiIsIGJhbm5lckRhdGEkZGV2aWNlKQogICAgIyAtIHJlc3VsdDoKICAgIGJhbm5lckRhdGEkcmVzdWx0IDwtIGdzdWIoIl5yZXN1bHQ9IiwgIiIsIGJhbm5lckRhdGEkcmVzdWx0KQogICAgIyAtIGZpbHRlciBmb3IgcmVzdWx0PXNob3cKICAgIGJhbm5lckRhdGEgPC0gZHBseXI6OmZpbHRlcihiYW5uZXJEYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9PSAic2hvdyIpCiAgICAjIC0gY29ycmVjdGlvbiBmb3IgcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUKICAgIGJhbm5lckRhdGEkcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUgPC0gCiAgICAgIDEvYmFubmVyRGF0YSRyZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZQogICAgCiAgICAjIC0gYWdncmVnYXRlOgogICAgYmFubmVyRGF0YSA8LSBiYW5uZXJEYXRhICU+JSAKICAgICAgZHBseXI6OnNlbGVjdChiYW5uZXIsIGRldmljZSwgcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUpICU+JSAKICAgICAgZHBseXI6Omdyb3VwX2J5KGJhbm5lciwgZGV2aWNlKSAlPiUgCiAgICAgIGRwbHlyOjpzdW1tYXJpc2UoaW1wcmVzc2lvbnMgPSBzdW0ocmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUpKQogICAgCiAgICAjIC0gYWRkIGNldERheSwgbWUKICAgIGJhbm5lckRhdGEkZGF0ZSA8LSBjZXREYXkKICAgIGJhbm5lckRhdGEkY2FtcGFpZ24gPC0gY2FtcGFpZ25OYW1lCiAgICAKICAgICMgLSBzdG9yZToKICAgIHdyaXRlLmNzdihiYW5uZXJEYXRhLCAKICAgICAgICAgICAgICBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAiYmFubmVySW1wcmVzc2lvbnMiLAogICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICIuY3N2IgogICAgICAgICAgICAgICkKICAgICkKICAgIAogICAgIyAtIHJldHVybgogICAgcmV0dXJuKFRSVUUpCiAgfQp9CgojIC0gd3JhbmdsZSBCYW5uZXIgSW1wcmVzc2lvbiBkYXRhCmNhbXBhaWduTmFtZSA8LSAiT2NjYXNpb25hbEVkaXRvcnMyMDIwIgp1cmlfcXVlcnkgPC0gYygnV01ERV9vY2VkaXRvcnNfc3ByaW5nXzIwMjBfJykKYmFubmVyUHJvY2VzcyA8LSB3bWRlX3Byb2Nlc3NfYmFubmVyX2ltcHJlc3Npb25zKGZpbGVOYW1lID0gZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyID0gZGF0YURpciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSA9IGNhbXBhaWduTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9xdWVyeSA9IHVyaV9xdWVyeSkKCgoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBQYWdldmlld3MKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLSBmdW5jdGlvbjogd21kZV9jb2xsZWN0X3BhZ2V2aWV3cwp3bWRlX2NvbGxlY3RfcGFnZXZpZXdzIDwtIGZ1bmN0aW9uKHVyaV9ob3N0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpcikgewogIAogICMgLSBOT1RFOgogICMgLSBleHBlY3RlZCBmb3JtYXQgZm9yIGNldERheSBpczogWVlZWS1NTS1ERAogIAogICMgLSB0byBkYXRhRGlyCiAgc2V0d2QoZGF0YURpcikKICAKICAjIC0gbGlicmFyaWVzCiAgbGlicmFyeShzdHJpbmdyKQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSBkYXRldGltZV9jb25kaXRpb24KICBjZXRfY29uZGl0aW9uIDwtIHNlcSgKICAgIGZyb20gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAwOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIHRvID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMjM6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgYnkgPSAiaG91ciIKICApIAogIGF0dHIoY2V0X2NvbmRpdGlvbiwgInR6b25lIikgPC0gIlVUQyIKICBjZXRfY29uZGl0aW9uIDwtIGFzLmNoYXJhY3RlcihjZXRfY29uZGl0aW9uKQogIGNldF9jb25kaXRpb24gPC0gdW5saXN0KHN0cl9leHRyYWN0X2FsbChjZXRfY29uZGl0aW9uLCAiXihbWzpkaWdpdDpdXXxcXHN8LSkqIikpCiAgY2V0X3llYXJzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzFdCiAgICB9KQogIGNldF9tb250aHMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMl0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfbW9udGhzKQogIGNldF9kYXlzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzNdCiAgICB9KQogIGNldF9kYXlzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9kYXlzKQogIGNldF9ob3VycyA8LSBzYXBwbHkoc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIAogICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICB4WzJdCiAgICAgICAgICAgICAgICAgICAgICB9KQogIGNldF9ob3VycyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfaG91cnMpCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUwKAogICAgInllYXIgPSAiLCBjZXRfeWVhcnMsICIgQU5EICIsCiAgICAibW9udGggPSAiLCBjZXRfbW9udGhzLCAiIEFORCAiLAogICAgImRheSA9ICIsIGNldF9kYXlzLCAiIEFORCAiLCAKICAgICJob3VyID0gIiwgY2V0X2hvdXJzCiAgKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlKCIoIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZXRpbWVDb25kaXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpCiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9ob3N0X2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX2hvc3QpID4gMSkgewogICAgdXJpX2hvc3RfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfaG9zdCA9ICciLCB1cmlfaG9zdCwgIiciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX2hvc3RfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfaG9zdCA9ICciLCB1cmlfaG9zdCwgIiciKQogIH0KICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX3BhdGhfY29uZGl0aW9uCiAgaWYgKGxlbmd0aCh1cmlfcGF0aCkgPiAxKSB7CiAgICB1cmlfcGF0aF9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfcGF0aCA9ICciLCB1cmlfcGF0aCwgIiciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiA9IHBhc3RlMCgidXJpX3BhdGggPSAnIiwgdXJpX3BhdGgsICInIikKICB9CiAgCiAgIyAtIGNvbXBvc2UgSGl2ZVFMIHF1ZXJ5CiAgaGl2ZVF1ZXJ5IDwtIHBhc3RlMCggCiAgICAiVVNFIHdtZjsKICAgIFNFTEVDVCB1cmlfaG9zdCwgdXJpX3BhdGgsIHVyaV9xdWVyeSwgcmVmZXJlciBGUk9NIHdlYnJlcXVlc3QKICAgIFdIRVJFICgiLAogICAgdXJpX2hvc3RfY29uZGl0aW9uLCAiIEFORCAiLAogICAgdXJpX3BhdGhfY29uZGl0aW9uLCAiIEFORCAiLAogICAgIigiLCBkYXRldGltZUNvbmRpdGlvbiwgIikiLAogICAgIik7IgogICAgKQogIAogICMgLSB3cml0ZSBocWwKICB3cml0ZShoaXZlUXVlcnksIHF1ZXJ5RmlsZSkKICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0OgogIGtlcmJlcm9zUHJlZml4IDwtIAogICAgJ3N1ZG8gLXUgYW5hbHl0aWNzLXByaXZhdGVkYXRhIGtlcmJlcm9zLXJ1bi1jb21tYW5kIGFuYWx5dGljcy1wcml2YXRlZGF0YSAnCiAgIyAtIEtlcmJlcm9zIGluaXQKICBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlMChrZXJiZXJvc1ByZWZpeCwgJyBoZGZzIGRmcyAtbHMnKSwgCiAgICAgICAgIHdhaXQgPSBUKQogICMgLSBSdW4gcXVlcnkKICBxdWVyeSA8LSBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlKGtlcmJlcm9zUHJlZml4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcvdXNyL2xvY2FsL2Jpbi9iZWVsaW5lIC0taW5jcmVtZW50YWw9dHJ1ZSAtLXNpbGVudCAtZiAiJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMChkYXRhRGlyLCBxdWVyeUZpbGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIgPiAnLCBkYXRhRGlyLCBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKSwKICAgICAgICAgICAgICAgICAgd2FpdCA9IFRSVUUpCn0KCiMgLSBzZXQgcGFyYW1zIHRvIHdtZGVfY29sbGVjdF9wYWdldmlld3MKIyAtIGZvciB0aGUgMjAyMF9FbWFpbENhbXBhaWduV2lraXBlZGlhQ2hhbGxlbmdlCnVyaV9ob3N0IDwtIGMoJ2RlLndpa2lwZWRpYS5vcmcnLCAnZGUubS53aWtpcGVkaWEub3JnJykKdXJpX3BhdGggIDwtIGMoCiAgJy93aWtpL1dpa2lwZWRpYTpXaWtpbWVkaWFfRGV1dHNjaGxhbmQvRGVpbkVuZ2FnZW1lbnQnLAogICcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0RlaW5FbmdhZ2VtZW50L0xpdGVyYXR1cicsCiAgJy93aWtpL1dpa2lwZWRpYTpNZW50b3JlbnByb2dyYW1tJykKIyB1cmlfcXVlcnkgPC0gcGFzdGUwKCdXTURFXzIwMjBfY2hhbGxlbmdlXycsIDE6MzApCnF1ZXJ5RmlsZSA8LSAnT2NjYXNpb25hbEVkaXRvcnMyMDIwX1BhZ2V2aWV3cy5ocWwnCmZpbGVOYW1lIDwtIHBhc3RlMCgicGFnZXZpZXdzXyIsIGNldERheSwgIi50c3YiKQoKIyAtIGNvbGxlY3QgUGFnZXZpZXdzIGRhdGEKd21kZV9jb2xsZWN0X3BhZ2V2aWV3cyh1cmlfaG9zdCwKICAgICAgICAgICAgICAgICAgICAgICB1cmlfcGF0aCwKICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIpCgojIyMgLS0tIFdyYW5nbGUgUGFnZXZpZXdzCiMgLSBmdW5jdGlvbjogd21kZV9wcm9jZXNzX3BhZ2V2aWV3cwp3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzIDwtIGZ1bmN0aW9uKGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9xdWVyeV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5ID0gY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSA9IGNhbXBhaWduTmFtZSkgewogIAogICMgLSB0byBkYXRhRGlyCiAgc2V0d2QoZGF0YURpcikKICAKICAjIC0gbGlicmFyaWVzCiAgbGlicmFyeShzdHJpbmdyKQogIGxpYnJhcnkoZHBseXIpCiAgbGlicmFyeSh0aWR5cikKICBsaWJyYXJ5KGRhdGEudGFibGUpCiAgCiAgIyAtIGxvYWQKICBwYWdldmlld3NEYXRhIDwtIHJlYWRMaW5lcyhmaWxlTmFtZSkKICB3U3RhcnQgPC0gd2hpY2goZ3JlcGwoInVyaV9ob3N0IiwgcGFnZXZpZXdzRGF0YSkpCiAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhWyh3U3RhcnQgKyAyKToobGVuZ3RoKHBhZ2V2aWV3c0RhdGEpIC0gMildCiAgcGFnZXZpZXdzRGF0YSA8LSBkYXRhLmZyYW1lKGRhdCA9IHBhZ2V2aWV3c0RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICBwYWdldmlld3NEYXRhIDwtIHNlcGFyYXRlKHBhZ2V2aWV3c0RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRvID0gYygndXJpX2hvc3QnLCAndXJpX3BhdGgnLCAndXJpX3F1ZXJ5JywgJ3JlZmVyZXInKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIpCiAgCiAgIyAtIGFwcGx5IHVyaV9xdWVyeV9maWx0ZXIKICAjIC0gTk9URTogbG9va2luZyBpbiBib3RoOiB1cmlfcXVlcnksIHJlZmVyZXIgCiAgIyAtIE5PVEU6IGhhY2sgZm9yIHRoZSAyMDIwX09jY2FzaW9uYWxFZGl0b3JzIENhbXBhaWduCiAgIyAtIGluY2x1ZGUgcGFnZXZpZXdzIGZvciAKICAjIC0gJy93aWtpL1dpa2lwZWRpYTpXaWtpbWVkaWFfRGV1dHNjaGxhbmQvRGVpbkVuZ2FnZW1lbnQvTGl0ZXJhdHVyJywKICAjIC0gJy93aWtpL1dpa2lwZWRpYTpNZW50b3JlbnByb2dyYW1tJwogICMgLSBvbiBkZXdpa2kgd2hlcmUgZ3JlcGwod191cmlfcXVlcnksIHJlZmVyZXIpCiAgd191cmlfcXVlcnlfcmVmZXJlciA8LSB3aGljaChncmVwbCh1cmlfcXVlcnlfZmlsdGVyLCBwYWdldmlld3NEYXRhJHJlZmVyZXIpICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFncmVwbCh1cmlfcXVlcnlfZmlsdGVyLCBwYWdldmlld3NEYXRhJHVyaV9xdWVyeSkpCiAgaWYgKGxlbmd0aCh3X3VyaV9xdWVyeV9yZWZlcmVyKSA+IDApIHsKICAgIHJlZmVyZXJUYWdzIDwtIHN0cnNwbGl0KHBhZ2V2aWV3c0RhdGEkcmVmZXJlclt3X3VyaV9xdWVyeV9yZWZlcmVyXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGxpdCA9ICI/IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVCkKICAgIHJlZmVyZXJUYWdzIDwtIHNhcHBseShyZWZlcmVyVGFncywgZnVuY3Rpb24oeCkge3hbMl19KQogICAgcmVmZXJlclRhZ3MgPC0gcGFzdGUwKCI/IiwgcmVmZXJlclRhZ3MpCiAgICBwYWdldmlld3NEYXRhJHVyaV9xdWVyeVt3X3VyaV9xdWVyeV9yZWZlcmVyXSA8LSByZWZlcmVyVGFncyAKICB9CiAgd191cmlfcXVlcnkgPC0gd2hpY2goZ3JlcGwodXJpX3F1ZXJ5X2ZpbHRlciwgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkpKQogIAogIGlmIChsZW5ndGgod191cmlfcXVlcnkpID4gMCkgewogICAgCiAgICAjIC0gZmlsdGVyIGZvciB3X3VyaV9xdWVyeQogICAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhW3dfdXJpX3F1ZXJ5LCBdIAogICAgCiAgICAjIC0gYWdncmVnYXRlOgogICAgcGFnZXZpZXdzRGF0YSR1cmlfcGF0aCA8LSBwYXN0ZTAocGFnZXZpZXdzRGF0YSR1cmlfaG9zdCwgcGFnZXZpZXdzRGF0YSR1cmlfcGF0aCkKICAgIHBhZ2V2aWV3c0RhdGEkdXJpX2hvc3QgPC0gTlVMTAogICAgcGFnZXZpZXdzRGF0YSRyZWZlcmVyIDwtIE5VTEwKICAgIHBhZ2V2aWV3c0RhdGEgPC0gcGFnZXZpZXdzRGF0YSAlPiUgCiAgICAgIGRwbHlyOjpzZWxlY3QodXJpX3F1ZXJ5LCB1cmlfcGF0aCkgJT4lIAogICAgICBkcGx5cjo6Z3JvdXBfYnkodXJpX3F1ZXJ5LCB1cmlfcGF0aCkgJT4lIAogICAgICBkcGx5cjo6c3VtbWFyaXNlKHBhZ2V2aWV3cyA9IG4oKSkKICAgIGNvbG5hbWVzKHBhZ2V2aWV3c0RhdGEpIDwtIGMoJ1RhZycsICdQYWdlJywgJ1BhZ2V2aWV3cycpCiAgICAKICAgICMgLSBhZGQgY2V0RGF5LCBjYW1wYWlnbk5hbWUKICAgIHBhZ2V2aWV3c0RhdGEkZGF0ZSA8LSBjZXREYXkKICAgIHBhZ2V2aWV3c0RhdGEkY2FtcGFpZ24gPC0gY2FtcGFpZ25OYW1lCiAgICAKICAgICMgLSBzdG9yZToKICAgIHdyaXRlLmNzdihwYWdldmlld3NEYXRhLCAKICAgICAgICAgICAgICBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAKICAgICAgICAgICAgICAgICAgICAgInBhZ2V2aWV3c0FnZ3JlZ2F0ZWRfIiwKICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoCiAgICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoZmlsZU5hbWUsIHNwbGl0ID0gIl8iLCBmaXhlZCA9IFQpW1sxXV1bMl0sCiAgICAgICAgICAgICAgICAgICAgICAgc3BsaXQgPSAiLiIsIAogICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXSwKICAgICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICAgKQogICAgKQogICAgCiAgfQogIAp9CgojIC0gc2V0IHBhcmFtcyB0byB3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzCiMgLSBmb3IgdGhlIFdNREUgMjAyMF9FbWFpbENhbXBhaWduV2lraXBlZGlhQ2hhbGxlbmdlCnVyaV9xdWVyeV9maWx0ZXIgPC0gJ1dNREVfb2NlZGl0b3JzX3NwcmluZ18yMDIwXycKCiMgLSB3cmFuZ2xlIHBhZ2V2aWV3cwp3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzKGZpbGVOYW1lID0gZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpciA9IGRhdGFEaXIsCiAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5X2ZpbHRlciA9IHVyaV9xdWVyeV9maWx0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUgPSBjYW1wYWlnbk5hbWUpCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIEJhbm5lciBBY3Rpb25zCiMjIyAtLS0gdmlhIGV2ZW50LldNREVCYW5uZXJBY3Rpb25zCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMgX19fIE5PVEU6CiMgLSBTdWZmaXggZXhwbGFuYXRpb246CiMgLSBjdHJsIGlzIHRoZSBiYW5uZXIgdGhhdCBkeW5hbWljYWxseSBkaXNwbGF5cyB0ZXh0IGRlcGVuZGluZyBvbiB0aGUgdGFyZ2V0IGdyb3VwCiMgLSB2YXIgaXMgdGhlIGJhbm5lciB0aGF0IHNob3dzIHRoZSBzYW1lIHRleHQgZm9yIGJvdGggdGFyZ2V0IGdyb3VwcwojIC0gaXBhZC9tb2JpbGUgZG9lcyBub3QgcmVwcmVzZW50IHRoZSBhY3R1YWwgZGV2aWNlIHR5cGUsIGJ1dCB0aGUgZGlzcGxheSBtb2RlIG9mIHRoZSBiYW5uZXIgKHNtYWxsIGRlc2t0b3Agc2NyZWVucyBtYXkgYmUgcmVwb3J0ZWQgYXMgaXBhZCkKIyAtIGNzL250IGluZGljYXRlcywgd2hpY2ggdGFyZ2V0IGdyb3VwIHRoZSB1c2VyIGJlbG9uZ3MgdG8KIyAtIEthaSBOaXNzZW4gaW4gaHR0cHM6Ly9waGFicmljYXRvci53aWtpbWVkaWEub3JnL1QyNTE1MzUjNjEzMjQ2OAoKIyAtIHNlbGVjdCBkdCwgZXZlbnQuYmFubmVyTmFtZSwgZXZlbnQuYmFubmVyQWN0aW9uLCBldmVudC5iYW5uZXJJbXByZXNzaW9ucywgZXZlbnQudXNlcklEIAojIC0gZnJvbSBldmVudC53bWRlYmFubmVyaW50ZXJhY3Rpb25zIHdoZXJlIHllYXI9MjAyMCBhbmQgbW9udGg9NSBhbmQgKGRheT0xMSBvciBkYXk9MTIgb3IgZGF5PTEzKTsKCiMgLSBmdW5jdGlvbjogd21kZV9jb2xsZWN0X3BhZ2V2aWV3cwp3bWRlX2Jhbm5lcl9hY3Rpb25zIDwtIGZ1bmN0aW9uKHVyaV9xdWVyeV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmFseXRpY3NEaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lKSB7CiAgCiAgIyAtIE5PVEU6CiAgIyAtIGV4cGVjdGVkIGZvcm1hdCBmb3IgY2V0RGF5IGlzOiBZWVlZLU1NLURECiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIGRhdGV0aW1lX2NvbmRpdGlvbgogIGNldF9jb25kaXRpb24gPC0gc2VxKAogICAgZnJvbSA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDA6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgdG8gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAyMzowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICBieSA9ICJob3VyIgogICkgCiAgYXR0cihjZXRfY29uZGl0aW9uLCAidHpvbmUiKSA8LSAiVVRDIgogIGNldF9jb25kaXRpb24gPC0gYXMuY2hhcmFjdGVyKGNldF9jb25kaXRpb24pCiAgY2V0X2NvbmRpdGlvbiA8LSB1bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKGNldF9jb25kaXRpb24sICJeKFtbOmRpZ2l0Ol1dfFxcc3wtKSoiKSkKICBjZXRfeWVhcnMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMV0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsyXQogICAgfSkKICBjZXRfbW9udGhzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9tb250aHMpCiAgY2V0X2RheXMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bM10KICAgIH0pCiAgY2V0X2RheXMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2RheXMpCiAgY2V0X2hvdXJzIDwtIHNhcHBseShzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgCiAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHhbMl0KICAgICAgICAgICAgICAgICAgICAgIH0pCiAgY2V0X2hvdXJzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9ob3VycykKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZTAoCiAgICAieWVhciA9ICIsIGNldF95ZWFycywgIiBBTkQgIiwKICAgICJtb250aCA9ICIsIGNldF9tb250aHMsICIgQU5EICIsCiAgICAiZGF5ID0gIiwgY2V0X2RheXMsICIgQU5EICIsIAogICAgImhvdXIgPSAiLCBjZXRfaG91cnMKICApCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUoIigiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZUNvbmRpdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAKCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIGV2ZW50QmFubmVyTmFtZV9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9xdWVyeSkgPiAxKSB7CiAgICBldmVudEJhbm5lck5hbWVfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJldmVudC5iYW5uZXJOYW1lIExJS0UgJyUiLCB1cmlfcXVlcnlfZmlsdGVyLCAiJSciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgZXZlbnRCYW5uZXJOYW1lX2NvbmRpdGlvbiA9IHBhc3RlMCgiZXZlbnQuYmFubmVyTmFtZSBMSUtFICclIiwgdXJpX3F1ZXJ5X2ZpbHRlciwgIiUnIikKICB9CiAgCiAgIyAtIGNvbXBvc2UgSGl2ZVFMIHF1ZXJ5CiAgaGl2ZVF1ZXJ5IDwtIHBhc3RlMCggCiAgICAic2VsZWN0IGR0LCBldmVudC5iYW5uZXJOYW1lLCBldmVudC5iYW5uZXJBY3Rpb24sIGV2ZW50LmJhbm5lckltcHJlc3Npb25zLCBldmVudC51c2VySUQgZnJvbSBldmVudC53bWRlYmFubmVyaW50ZXJhY3Rpb25zIAogICAgV0hFUkUgKCIsCiAgICBldmVudEJhbm5lck5hbWVfY29uZGl0aW9uLCAiIEFORCAiLAogICAgIigiLCBkYXRldGltZUNvbmRpdGlvbiwgIikiLAogICAgIik7IgogICAgKQogIAogICMgLSB3cml0ZSBocWwKICB3cml0ZShoaXZlUXVlcnksIHF1ZXJ5RmlsZSkKICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0OgogIGtlcmJlcm9zUHJlZml4IDwtIAogICAgJ3N1ZG8gLXUgYW5hbHl0aWNzLXByaXZhdGVkYXRhIGtlcmJlcm9zLXJ1bi1jb21tYW5kIGFuYWx5dGljcy1wcml2YXRlZGF0YSAnCiAgIyAtIEtlcmJlcm9zIGluaXQKICBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlMChrZXJiZXJvc1ByZWZpeCwgJyBoZGZzIGRmcyAtbHMnKSwgCiAgICAgICAgIHdhaXQgPSBUKQogICMgLSBSdW4gcXVlcnkKICBxdWVyeSA8LSBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlKGtlcmJlcm9zUHJlZml4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcvdXNyL2xvY2FsL2Jpbi9iZWVsaW5lIC0taW5jcmVtZW50YWw9dHJ1ZSAtLXNpbGVudCAtZiAiJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMChkYXRhRGlyLCBxdWVyeUZpbGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIgPiAnLCBkYXRhRGlyLCBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKSwKICAgICAgICAgICAgICAgICAgd2FpdCA9IFRSVUUpCiAgCiAgIyAtIFdyYW5nbGUgQmFubmVyIEludGVyYWN0aW9ucwogICMgLSBsb2FkCiAgYmFubmVyRGF0YSA8LSB0cnlDYXRjaCh7CiAgICBhcy5kYXRhLmZyYW1lKGZyZWFkKHBhc3RlMChkYXRhRGlyLCBmaWxlTmFtZSkpKQogIH0sCiAgZXJyb3IgPSBmdW5jdGlvbihjb25kaXRpb24pIHsKICAgIHJldHVybihGQUxTRSkKICB9KQogIAogICMgLSBwcm9jZXNzCiAgaWYgKGNsYXNzKGJhbm5lckRhdGEpID09ICdsb2dpY2FsJykgewogICAgcmV0dXJuKEZBTFNFKSAKICB9IGVsc2UgeyAKICAgICMgLSBiYW5uZXJTZWVuCiAgICBiYW5uZXJTZWVuIDwtIGJhbm5lckRhdGEgJT4lIAogICAgICBkcGx5cjo6c2VsZWN0KGJhbm5lcm5hbWUsIHVzZXJpZCkKICAgIGJhbm5lclNlZW4gPC0gYmFubmVyU2VlblshZHVwbGljYXRlZChiYW5uZXJTZWVuKSwgXQogICAgYmFubmVyU2VlbiA8LSBiYW5uZXJTZWVuICU+JSAKICAgICAgZHBseXI6OnNlbGVjdChiYW5uZXJuYW1lKSAlPiUgCiAgICAgIGRwbHlyOjpncm91cF9ieShiYW5uZXJuYW1lKSAlPiUgCiAgICAgIGRwbHlyOjpzdW1tYXJpc2Uoc2Vlbl9ieSA9IG4oKSkKICAgICMgLSBiYW5uZXJDbG9zZWQKICAgIGJhbm5lckNsb3NlZCA8LSBiYW5uZXJEYXRhICU+JSAKICAgICAgZHBseXI6OmZpbHRlcihiYW5uZXJhY3Rpb24gPT0gImJhbm5lci1jbG9zZWQiKSAlPiUgCiAgICAgIGRwbHlyOjpzZWxlY3QoYmFubmVybmFtZSwgYmFubmVyaW1wcmVzc2lvbnMpICU+JSAKICAgICAgZHBseXI6Omdyb3VwX2J5KGJhbm5lcm5hbWUpICU+JQogICAgICBkcGx5cjo6c3VtbWFyaXNlKGNsb3NlZF9ieSA9IG4oKSwgbWVhbl9jbG9zZV9pbXAgPSByb3VuZChtZWFuKGJhbm5lcmltcHJlc3Npb25zKSwgMikpCiAgICAjIC0gYmFubmVyQ2xpY2tlZAogICAgYmFubmVyQ2xpY2tlZCA8LSBiYW5uZXJEYXRhICU+JSAKICAgICAgZHBseXI6OmZpbHRlcihiYW5uZXJhY3Rpb24gPT0gImJhbm5lci1jbGlja2VkIikgJT4lIAogICAgICBkcGx5cjo6c2VsZWN0KGJhbm5lcm5hbWUsIGJhbm5lcmltcHJlc3Npb25zKSAlPiUgCiAgICAgIGRwbHlyOjpncm91cF9ieShiYW5uZXJuYW1lKSAlPiUKICAgICAgZHBseXI6OnN1bW1hcmlzZShjbGlja2VkX2J5ID0gbigpLCBtZWFuX2NsaWNrX2ltcCA9IHJvdW5kKG1lYW4oYmFubmVyaW1wcmVzc2lvbnMpLCAyKSkKICAgICMgLSB3aG9DbGlja2VkCiAgICB3aG9DbGlja2VkIDwtIGJhbm5lckRhdGEgJT4lIAogICAgICBkcGx5cjo6ZmlsdGVyKGJhbm5lcmFjdGlvbiA9PSAiYmFubmVyLWNsaWNrZWQiKQogICAgd2hvQ2xpY2tlZCA8LSBkYXRhLmZyYW1lKHVzZXJpZCA9IHVuaXF1ZSh3aG9DbGlja2VkJHVzZXJpZCkpCiAgICAjIC0gc3RvcmU6CiAgICB3cml0ZS5jc3Yod2hvQ2xpY2tlZCwgCiAgICAgICAgICAgICAgcGFzdGUwKGFuYWx5dGljc0RpciwgCiAgICAgICAgICAgICAgICAgICAgICJ3aG9DbGlja2VkXyIsCiAgICAgICAgICAgICAgICAgICAgIHN0cnNwbGl0KAogICAgICAgICAgICAgICAgICAgICAgIHN0cnNwbGl0KGZpbGVOYW1lLCBzcGxpdCA9ICJfIiwgZml4ZWQgPSBUKVtbMV1dWzJdLAogICAgICAgICAgICAgICAgICAgICAgIHNwbGl0ID0gIi4iLCAKICAgICAgICAgICAgICAgICAgICAgICBmaXhlZCA9IFQpW1sxXV1bMV0sCiAgICAgICAgICAgICAgICAgICAgICIuY3N2IgogICAgICAgICAgICAgICkKICAgICkKICAgICMgLSBqb2luCiAgICBiYW5uZXJEYXRhIDwtIGJhbm5lclNlZW4gJT4lIAogICAgICBkcGx5cjo6bGVmdF9qb2luKGJhbm5lckNsb3NlZCwgJ2Jhbm5lcm5hbWUnKSAlPiUgCiAgICAgIGRwbHlyOjpsZWZ0X2pvaW4oYmFubmVyQ2xpY2tlZCwgJ2Jhbm5lcm5hbWUnKQogICAgYmFubmVyRGF0YSRjbG9zZV9yYXRlIDwtIHJvdW5kKGJhbm5lckRhdGEkY2xvc2VkX2J5L2Jhbm5lckRhdGEkc2Vlbl9ieSwgMikKICAgIGJhbm5lckRhdGEkY2xpY2tfcmF0ZSA8LSByb3VuZChiYW5uZXJEYXRhJGNsaWNrZWRfYnkvYmFubmVyRGF0YSRzZWVuX2J5LCAyKQogICAgIyAtIGRhdGUsIGNhbXBhaWduCiAgICBiYW5uZXJEYXRhJGRheSA8LSBjZXREYXkKICAgIGJhbm5lckRhdGEkY2FtcGFpZ24gPC0gY2FtcGFpZ25OYW1lCiAgICAKICAgICMgLSBzdG9yZToKICAgIHdyaXRlLmNzdihiYW5uZXJEYXRhLCAKICAgICAgICAgICAgICBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAKICAgICAgICAgICAgICAgICAgICAgImJhbm5lckludGVyYWN0aW9uc0FnZ3JlZ2F0ZWRfIiwKICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoCiAgICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoZmlsZU5hbWUsIHNwbGl0ID0gIl8iLCBmaXhlZCA9IFQpW1sxXV1bMl0sCiAgICAgICAgICAgICAgICAgICAgICAgc3BsaXQgPSAiLiIsIAogICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXSwKICAgICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICAgKQogICAgKQogIH0KCn0KCiMgLSBzZXQgcGFyYW1zIGZvciB3bWRlX2Jhbm5lcl9hY3Rpb25zKCkKcXVlcnlGaWxlIDwtIHBhc3RlMChjYW1wYWlnbk5hbWUsICJfYmFubmVySW50ZXJhY3Rpb25zLmhxbCIpCmZpbGVOYW1lIDwtIHBhc3RlMCgiYmFubmVySW50ZXJhY3Rpb25zXyIsIGNldERheSwgIi50c3YiKQp1cmlfcXVlcnlfZmlsdGVyIDwtICdXTURFX29jZWRpdG9yc19zcHJpbmdfMjAyMF8nCmJhbm5lcl9zdGF0dXMgPC0gd21kZV9iYW5uZXJfYWN0aW9ucyh1cmlfcXVlcnlfZmlsdGVyID0gdXJpX3F1ZXJ5X2ZpbHRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSA9IHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lID0gZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmFseXRpY3NEaXIgPSBhbmFseXRpY3NEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lID0gY2FtcGFpZ25OYW1lKQpgYGAKCiMjIDEuIENhbXBhaWduIEJhbm5lcnMKClRoaXMgc2VjdGlvbiBwcmVzZW50cyBhbGwgZGF0YSBhbmQgc3RhdGlzdGljcyBvbiB0aGUgY2FtcGFpZ24gYmFubmVycy4KVGhlIGZvbGxvd2luZyBjaHVuayBsb2FkcyBhbmQgdGhlbiByZS1zdHJ1Y3R1cmVzIHRoZSBkYXRhc2V0IGEgYml0OgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmxGIDwtIGxpc3QuZmlsZXMoJ19hbmFseXRpY3MnKQpsRiA8LSBsRltncmVwbCgiXmJhbm5lckludGVyYWN0aW9ucyIsIGxGKV0KZGF0YVNldCA8LSBsYXBwbHkocGFzdGUwKCJfYW5hbHl0aWNzLyIsIGxGKSwgZnJlYWQpCmRhdGFTZXQgPC0gcmJpbmRsaXN0KGRhdGFTZXQpCmRhdGFTZXQkVjEgPC0gTlVMTApkYXRhU2V0JGNhbXBhaWduIDwtIE5VTEwKIyAtIE5BcyB0byB6ZXJvCmRhdGFTZXRbd2hpY2goaXMubmEoZGF0YVNldCksIGFyci5pbmQgPSBUKV0gPC0gMAojIC0gYmFubmVyIGNvZGVzCmRhdGFTZXQkdmFyX2N0cmwgPC0gc2FwcGx5KGRhdGFTZXQkYmFubmVybmFtZSwgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgidmFyIiwgeCkpIHtyZXR1cm4oInZhciIpfSBlbHNlIHtyZXR1cm4oImN0cmwiKX0KfSkKZGF0YVNldCRkZXZpY2UgPC0gc2FwcGx5KGRhdGFTZXQkYmFubmVybmFtZSwgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgibW9iaWxlIiwgeCkpIHsKICAgIHJldHVybigibW9iaWxlIikKICB9IGVsc2UgaWYgKGdyZXBsKCJpcGFkIiwgeCkpIHsKICAgICAgcmV0dXJuKCJpcGFkIikKICB9IGVsc2UgewogICAgICByZXR1cm4oImRlc2t0b3AiKQogIH0KfSkKZGF0YVNldCRjc19udCA8LSBzYXBwbHkoZGF0YVNldCRiYW5uZXJuYW1lLCBmdW5jdGlvbih4KSB7CiAgaWYgKGdyZXBsKCJjcyIsIHgpKSB7cmV0dXJuKCJjcyIpfSBlbHNlIHtyZXR1cm4oIm50Iil9Cn0pCmBgYAoKIyMjIDEuMSBCYW5uZXIgQWN0aW9ucyBPdmVydmlldwoKKipDaGFydCAxLjEuMSoqIERhaWx5IEJhbm5lciBJbXByZXNzaW9ucywgQ2xpY2tzLCBhbmQgQ2xvc3VyZXMsIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRheSwgc2Vlbl9ieSwgY2xvc2VkX2J5LCBjbGlja2VkX2J5KSAlPiUgCiAgZ3JvdXBfYnkoZGF5KSAlPiUgCiAgc3VtbWFyaXNlKHNlZW5fYnkgPSBzdW0oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnkgPSBzdW0oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnkgPSBzdW0oY2xpY2tlZF9ieSkpCnBGcmFtZSRjbG9zZV9yYXRlID0gcm91bmQocEZyYW1lJGNsb3NlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lJGNsaWNrX3JhdGUgPSByb3VuZChwRnJhbWUkY2xpY2tlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXkpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRhaWx5OgpwRiA8LSBwRnJhbWUgJT4lIAogIHNlbGVjdChkYXksIHNlZW5fYnksIGNsb3NlZF9ieSwgY2xpY2tlZF9ieSkgJT4lIAogIHBpdm90X2xvbmdlcihjKCdzZWVuX2J5JywgJ2Nsb3NlZF9ieScsICdjbGlja2VkX2J5JyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBY3Rpb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlZhbHVlIikKZ2dwbG90KHBGLCBhZXMoeCA9IGRheSwKICAgICAgICAgICAgICAgeSA9IFZhbHVlLAogICAgICAgICAgICAgICBncm91cCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJBY3Rpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipDaGFydCAxLjEuMioqIERhaWx5IEJhbm5lciBDbGljayBhbmQgQ2xvc2UgcmF0ZXMsIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfTogY2xvc2VfcmF0ZSwgY2xpY2tfcmF0ZSwgZGFpbHk6CnBGIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KGRheSwgY2xpY2tfcmF0ZSwgY2xvc2VfcmF0ZSkgJT4lIAogIHBpdm90X2xvbmdlcihjKCdjbGlja19yYXRlJywgJ2Nsb3NlX3JhdGUnKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkFjdGlvbiIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiVmFsdWUiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gVmFsdWUsCiAgICAgICAgICAgICAgIGdyb3VwID0gQWN0aW9uLAogICAgICAgICAgICAgICBjb2xvciA9IEFjdGlvbiwKICAgICAgICAgICAgICAgZmlsbCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgbGFiZWwgPSBWYWx1ZSwKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJzIwMjAgV01ERSBPY2Nhc2lvbmFsIEVkaXRvcnMgQmFubmVyIENhbXBhaWduJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIkFjdGlvbnMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKyAKICB5bGltKGMoMCwgMSkpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCioqQ2hhcnQgMS4xLjMqKiBEYWlseTogSG93IE1hbnkgSW1wcmVzc2lvbnMgdG8gQWN0aW9uIChDbGljaywgQ2xvc2UpLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNX0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChkYXksIG1lYW5fY2xpY2tfaW1wLCBtZWFuX2Nsb3NlX2ltcCkgJT4lIAogIGdyb3VwX2J5KGRheSkgJT4lIAogIHN1bW1hcmlzZShtZWFuX2NsaWNrX2ltcCA9IHJvdW5kKG1lYW4obWVhbl9jbGlja19pbXApLCAyKSwgCiAgICAgICAgICAgIG1lYW5fY2xvc2VfaW1wID0gcm91bmQobWVhbihtZWFuX2Nsb3NlX2ltcCksIDIpKSAlPiUgCiAgYXJyYW5nZShkYXkpCmNvbG5hbWVzKHBGcmFtZSkgPC0gYygnZGF5JywgJ2NsaWNrJywgJ2Nsb3NlJykKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn06IGNsaWNrLCBjbG9zZSwgZGFpbHk6CnBGIDwtIHBGcmFtZSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ2NsaWNrJywgJ2Nsb3NlJyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBY3Rpb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIkF2ZXJhZ2UgSW1wcmVzc2lvbnMiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gYEF2ZXJhZ2UgSW1wcmVzc2lvbnNgLAogICAgICAgICAgICAgICBncm91cCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gYEF2ZXJhZ2UgSW1wcmVzc2lvbnNgLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiQXZnLiBJbXByZXNzaW9ucyBiZWZvcmUgQWN0aW9uIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKIyMjIDEuMiBCYW5uZXIgQWN0aW9uczogRGV2aWNlcwoKKipSZW1pbmRlci4qKiBGcm9tIHRoZSBbQ2FtcGFpZ24gVHJhY2tpbmcgQ29uY2VwdF0oaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vZG9jdW1lbnQvZC8xN2s3Z3FEaHJ4Q0RwU1R0a2dZX180Qk8wUXk1R0staWJEZDFjb3RhU1otRS9lZGl0Iyk6Cgo+IGlwYWQvbW9iaWxlIGRvZXMgbm90IHJlcHJlc2VudCB0aGUgYWN0dWFsIGRldmljZSB0eXBlLCBidXQgdGhlIGRpc3BsYXkgbW9kZSBvZiB0aGUgYmFubmVyIChzbWFsbCBkZXNrdG9wIHNjcmVlbnMgbWF5IGJlIHJlcG9ydGVkIGFzIGlwYWQpCgoKKipDaGFydCAxLjIuMSoqIERhaWx5IEJhbm5lciBDbGljayBhbmQgQ2xvc2UgcmF0ZXMsIGJ5ICoqZGV2aWNlcyoqLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gMTB9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF5LCBzZWVuX2J5LCBjbG9zZWRfYnksIGNsaWNrZWRfYnksIGRldmljZSkgJT4lIAogIGdyb3VwX2J5KGRheSwgZGV2aWNlKSAlPiUgCiAgc3VtbWFyaXNlKHNlZW5fYnkgPSBzdW0oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnkgPSBzdW0oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnkgPSBzdW0oY2xpY2tlZF9ieSkpCnBGcmFtZSRjbG9zZV9yYXRlID0gcm91bmQocEZyYW1lJGNsb3NlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lJGNsaWNrX3JhdGUgPSByb3VuZChwRnJhbWUkY2xpY2tlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXkpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRhaWx5OgpwRiA8LSBwRnJhbWUgJT4lIAogIHNlbGVjdChkYXksIGRldmljZSwgc2Vlbl9ieSwgY2xvc2VkX2J5LCBjbGlja2VkX2J5KSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ3NlZW5fYnknLCAnY2xvc2VkX2J5JywgJ2NsaWNrZWRfYnknKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkFjdGlvbiIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiVmFsdWUiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gVmFsdWUsCiAgICAgICAgICAgICAgIGdyb3VwID0gQWN0aW9uLAogICAgICAgICAgICAgICBjb2xvciA9IEFjdGlvbiwKICAgICAgICAgICAgICAgZmlsbCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgbGFiZWwgPSBWYWx1ZSwKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAofmRldmljZSwgbnJvdyA9IDMsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJBY3Rpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipDaGFydCAxLjIuMioqIERhaWx5OiBIb3cgTWVhbiBJbXByZXNzaW9ucyB0byBBY3Rpb24gKENsaWNrLCBDbG9zZSksIGJ5ICoqZGV2aWNlcyoqLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gMTB9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF5LCBkZXZpY2UsIG1lYW5fY2xpY2tfaW1wLCBtZWFuX2Nsb3NlX2ltcCkgJT4lIAogIGdyb3VwX2J5KGRheSwgZGV2aWNlKSAlPiUgCiAgc3VtbWFyaXNlKG1lYW5fY2xpY2tfaW1wID0gcm91bmQobWVhbihtZWFuX2NsaWNrX2ltcCksIDIpLCAKICAgICAgICAgICAgbWVhbl9jbG9zZV9pbXAgPSByb3VuZChtZWFuKG1lYW5fY2xvc2VfaW1wKSwgMikpICU+JSAKICBhcnJhbmdlKGRheSkKY29sbmFtZXMocEZyYW1lKSA8LSBjKCdkYXknLCAnZGV2aWNlJywgJ2NsaWNrJywgJ2Nsb3NlJykKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn06IGNsaWNrLCBjbG9zZSwgZGFpbHk6CnBGIDwtIHBGcmFtZSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ2NsaWNrJywgJ2Nsb3NlJyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBY3Rpb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIkF2ZXJhZ2UgSW1wcmVzc2lvbnMiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gYEF2ZXJhZ2UgSW1wcmVzc2lvbnNgLAogICAgICAgICAgICAgICBncm91cCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gYEF2ZXJhZ2UgSW1wcmVzc2lvbnNgLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZmFjZXRfd3JhcCh+ZGV2aWNlLCBucm93ID0gMywgc2NhbGVzID0gImZyZWUiKSArCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArIAogIGdndGl0bGUoJzIwMjAgV01ERSBPY2Nhc2lvbmFsIEVkaXRvcnMgQmFubmVyIENhbXBhaWduJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIkF2Zy4gSW1wcmVzc2lvbnMgYmVmb3JlIEFjdGlvbiIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCiMjIyAxLjMgQmFubmVyIEFjdGlvbnM6IFZhciB2cy4gQ3RybAoKKipSZW1pbmRlci4qKiBGcm9tIHRoZSBbQ2FtcGFpZ24gVHJhY2tpbmcgQ29uY2VwdF0oaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vZG9jdW1lbnQvZC8xN2s3Z3FEaHJ4Q0RwU1R0a2dZX180Qk8wUXk1R0staWJEZDFjb3RhU1otRS9lZGl0Iyk6Cgo+IGN0cmwgaXMgdGhlIGJhbm5lciB0aGF0IGR5bmFtaWNhbGx5IGRpc3BsYXlzIHRleHQgZGVwZW5kaW5nIG9uIHRoZSB0YXJnZXQgZ3JvdXAKCj4gdmFyIGlzIHRoZSBiYW5uZXIgdGhhdCBzaG93cyB0aGUgc2FtZSB0ZXh0IGZvciBib3RoIHRhcmdldCBncm91cHMKCioqQ2hhcnQgMS4zLjEqKiBEYWlseSBCYW5uZXIgQ2xpY2sgYW5kIENsb3NlIHJhdGVzLCBieSAqKnZhci9jdHJsKiosIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF5LCBzZWVuX2J5LCBjbG9zZWRfYnksIGNsaWNrZWRfYnksIHZhcl9jdHJsKSAlPiUgCiAgZ3JvdXBfYnkoZGF5LCB2YXJfY3RybCkgJT4lIAogIHN1bW1hcmlzZShzZWVuX2J5ID0gc3VtKHNlZW5fYnkpLCAKICAgICAgICAgICAgY2xvc2VkX2J5ID0gc3VtKGNsb3NlZF9ieSksIAogICAgICAgICAgICBjbGlja2VkX2J5ID0gc3VtKGNsaWNrZWRfYnkpKQpwRnJhbWUkY2xvc2VfcmF0ZSA9IHJvdW5kKHBGcmFtZSRjbG9zZWRfYnkvcEZyYW1lJHNlZW5fYnksIDIpCnBGcmFtZSRjbGlja19yYXRlID0gcm91bmQocEZyYW1lJGNsaWNrZWRfYnkvcEZyYW1lJHNlZW5fYnksIDIpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZGF5KQojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfTogc2Vlbl9ieSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5LCBkYWlseToKcEYgPC0gcEZyYW1lICU+JSAKICBzZWxlY3QoZGF5LCB2YXJfY3RybCwgc2Vlbl9ieSwgY2xvc2VkX2J5LCBjbGlja2VkX2J5KSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ3NlZW5fYnknLCAnY2xvc2VkX2J5JywgJ2NsaWNrZWRfYnknKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkFjdGlvbiIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiVmFsdWUiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gVmFsdWUsCiAgICAgICAgICAgICAgIGdyb3VwID0gQWN0aW9uLAogICAgICAgICAgICAgICBjb2xvciA9IEFjdGlvbiwKICAgICAgICAgICAgICAgZmlsbCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgbGFiZWwgPSBWYWx1ZSwKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAofnZhcl9jdHJsLCBucm93ID0gMiwgc2NhbGVzID0gImZyZWUiKSArIAogIGdndGl0bGUoJzIwMjAgV01ERSBPY2Nhc2lvbmFsIEVkaXRvcnMgQmFubmVyIENhbXBhaWduJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIkFjdGlvbnMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgoqKkNoYXJ0IDEuMy4yKiogRGFpbHk6IEhvdyBNZWFuIEltcHJlc3Npb25zIHRvIEFjdGlvbiAoQ2xpY2ssIENsb3NlKSwgYnkgKip2YXIvY3RybCoqLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRheSwgdmFyX2N0cmwsIG1lYW5fY2xpY2tfaW1wLCBtZWFuX2Nsb3NlX2ltcCkgJT4lIAogIGdyb3VwX2J5KGRheSwgdmFyX2N0cmwpICU+JSAKICBzdW1tYXJpc2UobWVhbl9jbGlja19pbXAgPSByb3VuZChtZWFuKG1lYW5fY2xpY2tfaW1wKSwgMiksIAogICAgICAgICAgICBtZWFuX2Nsb3NlX2ltcCA9IHJvdW5kKG1lYW4obWVhbl9jbG9zZV9pbXApLCAyKSkgJT4lIAogIGFycmFuZ2UoZGF5KQpjb2xuYW1lcyhwRnJhbWUpIDwtIGMoJ2RheScsICd2YXJfY3RybCcsICdjbGljaycsICdjbG9zZScpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBjbGljaywgY2xvc2UsIGRhaWx5OgpwRiA8LSBwRnJhbWUgJT4lIAogIHBpdm90X2xvbmdlcihjKCdjbGljaycsICdjbG9zZScpLCAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiQWN0aW9uIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJBdmVyYWdlIEltcHJlc3Npb25zIikKZ2dwbG90KHBGLCBhZXMoeCA9IGRheSwKICAgICAgICAgICAgICAgeSA9IGBBdmVyYWdlIEltcHJlc3Npb25zYCwKICAgICAgICAgICAgICAgZ3JvdXAgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGNvbG9yID0gQWN0aW9uLAogICAgICAgICAgICAgICBmaWxsID0gQWN0aW9uLAogICAgICAgICAgICAgICBsYWJlbCA9IGBBdmVyYWdlIEltcHJlc3Npb25zYCwKICAgICAgICAgICAgICAgICAgICApKSArIAogIGZhY2V0X3dyYXAofnZhcl9jdHJsLCBucm93ID0gMiwgc2NhbGVzID0gImZyZWUiKSArCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArIAogIGdndGl0bGUoJzIwMjAgV01ERSBPY2Nhc2lvbmFsIEVkaXRvcnMgQmFubmVyIENhbXBhaWduJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIkF2Zy4gSW1wcmVzc2lvbnMgYmVmb3JlIEFjdGlvbiIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCiMjIyAxLjQgQmFubmVyIEFjdGlvbnM6IG50IHZzLiBjcwoKKipSZW1pbmRlci4qKiBGcm9tIHRoZSBbQ2FtcGFpZ24gVHJhY2tpbmcgQ29uY2VwdF0oaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vZG9jdW1lbnQvZC8xN2s3Z3FEaHJ4Q0RwU1R0a2dZX180Qk8wUXk1R0staWJEZDFjb3RhU1otRS9lZGl0Iyk6Cgo+IGNzL250IGluZGljYXRlcywgd2hpY2ggdGFyZ2V0IGdyb3VwIHRoZSB1c2VyIGJlbG9uZ3MgdG8gKGNzID0gY29tbXVuaXR5IHN1cHBvcnQsIG50ID0gbmV3IHRhc2tzKQoKKipDaGFydCAxLjQuMSoqIERhaWx5IEJhbm5lciBDbGljayBhbmQgQ2xvc2UgcmF0ZXMsIGJ5ICoqbnQvY3MqKiwgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuNX0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChkYXksIHNlZW5fYnksIGNsb3NlZF9ieSwgY2xpY2tlZF9ieSwgY3NfbnQpICU+JSAKICBncm91cF9ieShkYXksIGNzX250KSAlPiUgCiAgc3VtbWFyaXNlKHNlZW5fYnkgPSBzdW0oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnkgPSBzdW0oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnkgPSBzdW0oY2xpY2tlZF9ieSkpCnBGcmFtZSRjbG9zZV9yYXRlID0gcm91bmQocEZyYW1lJGNsb3NlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lJGNsaWNrX3JhdGUgPSByb3VuZChwRnJhbWUkY2xpY2tlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXkpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRhaWx5OgpwRiA8LSBwRnJhbWUgJT4lIAogIHNlbGVjdChkYXksIGNzX250LCBzZWVuX2J5LCBjbG9zZWRfYnksIGNsaWNrZWRfYnkpICU+JSAKICBwaXZvdF9sb25nZXIoYygnc2Vlbl9ieScsICdjbG9zZWRfYnknLCAnY2xpY2tlZF9ieScpLCAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiQWN0aW9uIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJWYWx1ZSIpCmdncGxvdChwRiwgYWVzKHggPSBkYXksCiAgICAgICAgICAgICAgIHkgPSBWYWx1ZSwKICAgICAgICAgICAgICAgZ3JvdXAgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGNvbG9yID0gQWN0aW9uLAogICAgICAgICAgICAgICBmaWxsID0gQWN0aW9uLAogICAgICAgICAgICAgICBsYWJlbCA9IFZhbHVlLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZmFjZXRfd3JhcCh+Y3NfbnQsIG5yb3cgPSAyLCBzY2FsZXMgPSAiZnJlZSIpICsgCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiQWN0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCioqQ2hhcnQgMS40LjIqKiBEYWlseTogSG93IE1lYW4gSW1wcmVzc2lvbnMgdG8gQWN0aW9uIChDbGljaywgQ2xvc2UpLCBieSAqKm50L2NzKiosIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF5LCBjc19udCwgbWVhbl9jbGlja19pbXAsIG1lYW5fY2xvc2VfaW1wKSAlPiUgCiAgZ3JvdXBfYnkoZGF5LCBjc19udCkgJT4lIAogIHN1bW1hcmlzZShtZWFuX2NsaWNrX2ltcCA9IHJvdW5kKG1lYW4obWVhbl9jbGlja19pbXApLCAyKSwgCiAgICAgICAgICAgIG1lYW5fY2xvc2VfaW1wID0gcm91bmQobWVhbihtZWFuX2Nsb3NlX2ltcCksIDIpKSAlPiUgCiAgYXJyYW5nZShkYXkpCmNvbG5hbWVzKHBGcmFtZSkgPC0gYygnZGF5JywgJ2NzX250JywgJ2NsaWNrJywgJ2Nsb3NlJykKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn06IGNsaWNrLCBjbG9zZSwgZGFpbHk6CnBGIDwtIHBGcmFtZSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ2NsaWNrJywgJ2Nsb3NlJyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBY3Rpb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIkF2ZXJhZ2UgSW1wcmVzc2lvbnMiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gYEF2ZXJhZ2UgSW1wcmVzc2lvbnNgLAogICAgICAgICAgICAgICBncm91cCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gYEF2ZXJhZ2UgSW1wcmVzc2lvbnNgLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZmFjZXRfd3JhcCh+Y3NfbnQsIG5yb3cgPSAyLCBzY2FsZXMgPSAiZnJlZSIpICsKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiQXZnLiBJbXByZXNzaW9ucyBiZWZvcmUgQWN0aW9uIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKIyMjIDEuNSBCYW5uZXIgQWN0aW9uczogRnVsbCBEYXRhc2V0CgoqKlRhYmxlIDEuNS4xKiogRGFpbHkgQmFubmVyIEltcHJlc3Npb25zOiBzZWVuIGJ5LCBjbG9zZWQgYnksIGNsaWNrZWQgYnkKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIyMgLS0tIEZ1bGwgRGF0YXNldCAoVGFibGUgUmVwb3J0KQpkUyA8LSBkYXRhU2V0CmRTJGJhbm5lcm5hbWUgPC0gZ3N1YigiV01ERV9vY2VkaXRvcnNfc3ByaW5nXzIwMjBfIiwgIiIsIGRTJGJhbm5lcm5hbWUpCmRTIDwtIGRTWywKICAgICAgICAgYygnYmFubmVybmFtZScsICdkZXZpY2UnLCAndmFyX2N0cmwnLCAnY3NfbnQnLAogICAgICAgICAgICdzZWVuX2J5JywgJ2Nsb3NlZF9ieScsICdjbGlja2VkX2J5JyldCmRhdGF0YWJsZShkUywgCiAgICAgICAgICBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gMzApCiAgICAgICAgICApCmBgYAoKKipUYWJsZSAxLjUuMioqIERhaWx5IEJhbm5lciBBY3Rpb25zOiBtZWFuIGltcHJlc3Npb25zIGJlZm9yZSBhY3Rpb24KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIyMgLS0tIEZ1bGwgRGF0YXNldCAoVGFibGUgUmVwb3J0KQpkUyA8LSBkYXRhU2V0CmRTJGJhbm5lcm5hbWUgPC0gZ3N1YigiV01ERV9vY2VkaXRvcnNfc3ByaW5nXzIwMjBfIiwgIiIsIGRTJGJhbm5lcm5hbWUpCmRTIDwtIGRTWywKICAgICAgICAgYygnYmFubmVybmFtZScsICdkZXZpY2UnLCAndmFyX2N0cmwnLCAnY3NfbnQnLAogICAgICAgICAgICdtZWFuX2NsaWNrX2ltcCcsICdtZWFuX2Nsb3NlX2ltcCcpXQpkYXRhdGFibGUoZFMsIAogICAgICAgICAgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDMwKQogICAgICAgICAgKQpgYGAKCiMjIDIuIENhbXBhaWduIFBhZ2V2aWV3cwoKVGhpcyBzZWN0aW9uIHByZXNlbnRzIGFsbCBkYXRhIGFuZCBzdGF0aXN0aWNzIG9uIHRoZSBjYW1wYWlnbiBwYWdlcy4KClRoZSBmb2xsb3dpbmcgY2h1bmsgbG9hZHMgYW5kIHRoZW4gcmUtc3RydWN0dXJlcyB0aGUgZGF0YXNldCBhIGJpdC4KKipOT1RFLioqIFRoZSBgM2FsbGAgY2FtcGFpZ24gdGFnIHdhcyBvYnNlcnZlZCBvbmx5IG9uY2UsIG9uIGAyMDIwLzA1LzE0YCwgYW5kIGlzIHJlbW92ZWQgZnJvbSBhbmFseXNpcy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpsRiA8LSBsaXN0LmZpbGVzKCdfYW5hbHl0aWNzJykKbEYgPC0gbEZbZ3JlcGwoIl5wYWdldmlld3NBZ2dyZWdhdGVkIiwgbEYpXQpkYXRhU2V0IDwtIGxhcHBseShwYXN0ZTAoIl9hbmFseXRpY3MvIiwgbEYpLCBmcmVhZCkKZGF0YVNldCA8LSByYmluZGxpc3QoZGF0YVNldCkKZGF0YVNldCRWMSA8LSBOVUxMCmRhdGFTZXQkY2FtcGFpZ24gPC0gTlVMTAojIC0gZXhwYW5kIGdyaWQgdG8gYWNjb3VudCBmb3IgbWlzc2luZyBvYnNlcnZhdGlvbnMgcGVyIGRheQpkUyA8LSBleHBhbmQuZ3JpZCh1bmlxdWUoZGF0YVNldCRUYWcpLCAKICAgICAgICAgICAgICAgICAgdW5pcXVlKGRhdGFTZXQkUGFnZSksIAogICAgICAgICAgICAgICAgICB1bmlxdWUoZGF0YVNldCRkYXRlKSwgCiAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhkUykgPC0gYygnVGFnJywgJ1BhZ2UnLCAnZGF0ZScpCmRTIDwtIGRTICU+JSAKICBsZWZ0X2pvaW4oZGF0YVNldCwgCiAgICAgICAgICAgIGJ5ID0gYygiVGFnIiwgIlBhZ2UiLCAiZGF0ZSIpKQpkYXRhU2V0IDwtIGRTOyBybShkUykKZGF0YVNldCRQYWdldmlld3NbaXMubmEoZGF0YVNldCRQYWdldmlld3MpXSA8LSAwCiMgLSBiYW5uZXIgY29kZXMKZGF0YVNldCR2YXJfY3RybCA8LSBzYXBwbHkoZGF0YVNldCRUYWcsIGZ1bmN0aW9uKHgpIHsKICBpZiAoZ3JlcGwoInZhciIsIHgpKSB7cmV0dXJuKCJ2YXIiKX0gZWxzZSB7cmV0dXJuKCJjdHJsIil9Cn0pCmRhdGFTZXQkZGV2aWNlIDwtIHNhcHBseShkYXRhU2V0JFRhZywgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgibW9iaWxlIiwgeCkpIHsKICAgIHJldHVybigibW9iaWxlIikKICB9IGVsc2UgaWYgKGdyZXBsKCJpcGFkIiwgeCkpIHsKICAgICAgcmV0dXJuKCJpcGFkIikKICB9IGVsc2UgewogICAgICByZXR1cm4oImRlc2t0b3AiKQogIH0KfSkKZGF0YVNldCRjc19udCA8LSBzYXBwbHkoZGF0YVNldCRUYWcsIGZ1bmN0aW9uKHgpIHsKICBpZiAoZ3JlcGwoImNzIiwgeCkpIHtyZXR1cm4oImNzIil9IGVsc2Uge3JldHVybigibnQiKX0KfSkKZGF0YVNldCRUYWcgPC0gZ3N1YigiXFw/Y2FtcGFpZ249V01ERV9vY2VkaXRvcnNfc3ByaW5nXzIwMjBfIiwgIiIsIGRhdGFTZXQkVGFnKQpkYXRhU2V0JFBhZ2UgPC0gZ3N1YigiZGVcXC53aWtpcGVkaWFcXC5vcmcvd2lraS9XaWtpcGVkaWE6fGRlXFwubVxcLndpa2lwZWRpYVxcLm9yZy93aWtpL1dpa2lwZWRpYToiLCAiIiwgZGF0YVNldCRQYWdlKQojIC0gcmVtb3ZlIDNhbGwgdGFnOgpkYXRhU2V0IDwtIGZpbHRlcihkYXRhU2V0LCBUYWcgIT0gIjNhbGwiKQpgYGAKCiMjIyAyLjEgUGFnZXZpZXdzIE92ZXJ2aWV3CgoqKkNoYXJ0IDIuMS4xKiogRGFpbHkgUGFnZXZpZXdzLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNX0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChkYXRlLCBQYWdlLCBQYWdldmlld3MpICU+JSAKICBncm91cF9ieShkYXRlLCBQYWdlKSAlPiUgCiAgc3VtbWFyaXNlKFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQpwRnJhbWUgPC0gYXJyYW5nZShwRnJhbWUsIGRhdGUpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF0ZSwKICAgICAgICAgICAgICAgICAgIHkgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICBncm91cCA9IFBhZ2UsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IFBhZ2UsCiAgICAgICAgICAgICAgICAgICBmaWxsID0gUGFnZSwKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiUGFnZXZpZXdzIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyMgMi4yIFBhZ2V2aWV3czogRGV2aWNlcwoKKipSZW1pbmRlci4qKiBGcm9tIHRoZSBbQ2FtcGFpZ24gVHJhY2tpbmcgQ29uY2VwdF0oaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vZG9jdW1lbnQvZC8xN2s3Z3FEaHJ4Q0RwU1R0a2dZX180Qk8wUXk1R0staWJEZDFjb3RhU1otRS9lZGl0Iyk6Cgo+IGlwYWQvbW9iaWxlIGRvZXMgbm90IHJlcHJlc2VudCB0aGUgYWN0dWFsIGRldmljZSB0eXBlLCBidXQgdGhlIGRpc3BsYXkgbW9kZSBvZiB0aGUgYmFubmVyIChzbWFsbCBkZXNrdG9wIHNjcmVlbnMgbWF5IGJlIHJlcG9ydGVkIGFzIGlwYWQpCgoKKipDaGFydCAyLjIuMSoqIFBhZ2V2aWV3cywgYnkgKipkZXZpY2VzKiosIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSAxMH0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChkYXRlLCBkZXZpY2UsIFBhZ2UsIFBhZ2V2aWV3cykgJT4lIAogIGdyb3VwX2J5KGRhdGUsIGRldmljZSwgUGFnZSkgJT4lIAogIHN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXRlKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBQYWdlLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBQYWdlLAogICAgICAgICAgICAgICAgICAgZmlsbCA9IFBhZ2UsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAofmRldmljZSwgbnJvdyA9IDMsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJBY3Rpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgoKIyMjIDIuMyBQYWdldmlld3M6IFZhciB2cy4gQ3RybAoKKipSZW1pbmRlci4qKiBGcm9tIHRoZSBbQ2FtcGFpZ24gVHJhY2tpbmcgQ29uY2VwdF0oaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vZG9jdW1lbnQvZC8xN2s3Z3FEaHJ4Q0RwU1R0a2dZX180Qk8wUXk1R0staWJEZDFjb3RhU1otRS9lZGl0Iyk6Cgo+IGN0cmwgaXMgdGhlIGJhbm5lciB0aGF0IGR5bmFtaWNhbGx5IGRpc3BsYXlzIHRleHQgZGVwZW5kaW5nIG9uIHRoZSB0YXJnZXQgZ3JvdXAKCj4gdmFyIGlzIHRoZSBiYW5uZXIgdGhhdCBzaG93cyB0aGUgc2FtZSB0ZXh0IGZvciBib3RoIHRhcmdldCBncm91cHMKCioqQ2hhcnQgMS4zLjEqKiBQYWdldmlld3MsIGJ5ICoqdmFyL2N0cmwqKiwgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuNX0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChkYXRlLCB2YXJfY3RybCwgUGFnZSwgUGFnZXZpZXdzKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgdmFyX2N0cmwsIFBhZ2UpICU+JSAKICBzdW1tYXJpc2UoUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZGF0ZSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgIGdyb3VwID0gUGFnZSwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gUGFnZSwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBQYWdlLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBmYWNldF93cmFwKH52YXJfY3RybCwgbnJvdyA9IDMsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJBY3Rpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgoKIyMjIDIuNCBQYWdldmlld3M6IG50IHZzLiBjcwoKKipSZW1pbmRlci4qKiBGcm9tIHRoZSBbQ2FtcGFpZ24gVHJhY2tpbmcgQ29uY2VwdF0oaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vZG9jdW1lbnQvZC8xN2s3Z3FEaHJ4Q0RwU1R0a2dZX180Qk8wUXk1R0staWJEZDFjb3RhU1otRS9lZGl0Iyk6Cgo+IGNzL250IGluZGljYXRlcywgd2hpY2ggdGFyZ2V0IGdyb3VwIHRoZSB1c2VyIGJlbG9uZ3MgdG8gKGNzID0gY29tbXVuaXR5IHN1cHBvcnQsIG50ID0gbmV3IHRhc2tzKQoKKipDaGFydCAxLjQuMSoqIFBhZ2V2aWV3cywgYnkgKipudC9jcyoqLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRhdGUsIGNzX250LCBQYWdlLCBQYWdldmlld3MpICU+JSAKICBncm91cF9ieShkYXRlLCBjc19udCwgUGFnZSkgJT4lIAogIHN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXRlKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBQYWdlLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBQYWdlLAogICAgICAgICAgICAgICAgICAgZmlsbCA9IFBhZ2UsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAofmNzX250LCBucm93ID0gMywgc2NhbGVzID0gImZyZWUiKSArIAogIGdndGl0bGUoJzIwMjAgV01ERSBPY2Nhc2lvbmFsIEVkaXRvcnMgQmFubmVyIENhbXBhaWduJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIkFjdGlvbnMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCiMjIyAyLjUgUGFnZXZpZXdzOiBGdWxsIERhdGFzZXQKCioqVGFibGUgMi41LjEqKiBQYWdldmlld3MgZGF0YXNldAoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMjIyAtLS0gRnVsbCBEYXRhc2V0IChUYWJsZSBSZXBvcnQpCmRhdGF0YWJsZShkYXRhU2V0LCAKICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSAzMCkKICAgICAgICAgICkKYGBgCgojIyAzLiBVc2VyIEVkaXRzCgpUaGlzIHNlY3Rpb24gcHJlc2VudHMgYWxsIGRhdGEgYW5kIHN0YXRpc3RpY3Mgb24gdGhlIHVzZXIgZWRpdHMuCgojIyMgMy4xIFRoZSBBc3NpZ25tZW50IG9mIFVzZXJzIHRvIENhbXBhaWduIEJhbm5lcnMKClRoZSBmb2xsb3dpbmcgY2h1bmsgbG9hZHMgdGhlIGRhdGFzZXQgb2YgdXNlciBpbnRlcmFjdGlvbnMgd2l0aCBjYW1wYWlnbiBiYW5uZXJzIGFuZCB0aGVuIHJlLXN0cnVjdHVyZXMgdGhlIGRhdGFzZXQgYSBiaXQuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KbEYgPC0gbGlzdC5maWxlcygiX2RhdGEiKQpsRiA8LSBsRltncmVwbCgiXmJhbm5lckludGVyYWN0aW9ucyIsIGxGKV0KZGF0YVNldCA8LSBsYXBwbHkocGFzdGUwKCJfZGF0YS8iLCBsRiksIGZyZWFkKQpkYXRhU2V0IDwtIHJiaW5kbGlzdChkYXRhU2V0KQpkYXRhU2V0IDwtIGZpbHRlcihkYXRhU2V0LCBiYW5uZXJhY3Rpb24gPT0gImJhbm5lci1jbGlja2VkIikKZGF0YVNldCA8LSBzZWxlY3QoZGF0YVNldCwgYmFubmVybmFtZSwgdXNlcmlkLCBkdCkKZGF0YVNldCA8LSBhcnJhbmdlKGRhdGFTZXQsIHVzZXJpZCwgZHQpCmRhdGFTZXQgPC0gZGF0YVNldFshZHVwbGljYXRlZChkYXRhU2V0JHVzZXJpZCksIF0KZGF0YVNldCRkdCA8LSBOVUxMCmRhdGFTZXQkYmFubmVybmFtZSA8LSBnc3ViKCJXTURFX29jZWRpdG9yc19zcHJpbmdfMjAyMF8iLCAiIiwgZGF0YVNldCRiYW5uZXJuYW1lKQpkYXRhU2V0JGJhbm5lcm5hbWUgPC0gZ3N1YigibW9iaWxlX3xpcGFkXyIsICIiLCBkYXRhU2V0JGJhbm5lcm5hbWUpCiMgLSBiYW5uZXIgY29kZXMKZGF0YVNldCR2YXJfY3RybCA8LSBzYXBwbHkoZGF0YVNldCRiYW5uZXJuYW1lLCBmdW5jdGlvbih4KSB7CiAgaWYgKGdyZXBsKCJ2YXIiLCB4KSkge3JldHVybigidmFyIil9IGVsc2Uge3JldHVybigiY3RybCIpfQp9KQpkYXRhU2V0JGNzX250IDwtIHNhcHBseShkYXRhU2V0JGJhbm5lcm5hbWUsIGZ1bmN0aW9uKHgpIHsKICBpZiAoZ3JlcGwoImNzIiwgeCkpIHtyZXR1cm4oImNzIil9IGVsc2Uge3JldHVybigibnQiKX0KfSkKdXNlcnMgPC0gc2VsZWN0KGRhdGFTZXQsIHVzZXJpZCwgY3NfbnQpCnJtKGRhdGFTZXQpCmBgYAoKQmVjYXVzZSB0aGUgYXNzaW5nbWVudCBvZiB1c2VycyB0byAqKmNzIChjb21tdW5pdHkgc3VwcG9ydCkqKiB2cy4gKipudCAobmV3IHRhc2tzKSoqIHdhcyB1bmlxdWUsIHdlIHdpbGwgZm9jdXMgb24gdGhpcyB2YXJpYWJsZSBpbiB0aGUgY2FtcGFpZ24gZGVzaWduIGluIHRoZSBhbmFseXNpcyBvZiB1c2VyIGVkaXRzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnVzZXJFZGl0cyA8LSByZWFkLmNzdigiX2FuYWx5dGljcy91c2VyRWRpdHMuY3N2IiwgCiAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQp1c2VyRWRpdHMkcmV2YWN0b3JfdGltZXN0YW1wIDwtIGFzLmNoYXJhY3Rlcih1c2VyRWRpdHMkcmV2YWN0b3JfdGltZXN0YW1wKQp1c2VyRWRpdHMgPC0gbGVmdF9qb2luKHVzZXJFZGl0cywKICAgICAgICAgICAgICAgICAgICAgICB1c2VycywKICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoImFjdG9yX3VzZXIiID0gInVzZXJpZCIpKQp1c2VyRWRpdHMkYWN0b3JfaWQgPC0gTlVMTApybSh1c2VycykKdXNlckVkaXRzJGRhdGUgPC0gcGFzdGUwKAogIHN1YnN0cih1c2VyRWRpdHMkcmV2YWN0b3JfdGltZXN0YW1wLCAxLCA0KSwKICAiLSIsCiAgc3Vic3RyKHVzZXJFZGl0cyRyZXZhY3Rvcl90aW1lc3RhbXAsIDUsIDYpLAogICItIiwKICBzdWJzdHIodXNlckVkaXRzJHJldmFjdG9yX3RpbWVzdGFtcCwgNywgOCkKKQpgYGAKCiMjIyAzLjIgRGFpbHkgdXNlciBlZGl0cyBzaW5jZSB0aGUgY2FtcGFpZ24gb25zZXQKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQpwRnJhbWUgPC0gdXNlckVkaXRzICU+JSAKICBzZWxlY3QoZGF0ZSkgJT4lIAogIGdyb3VwX2J5KGRhdGUpICU+JSAKICBzdW1tYXJpc2UoRWRpdHMgPSBuKCkpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZGF0ZSkKY2FtcGFpZ25EYXlzIDwtIGMoIjIwMjAtMDUtMTQiLCAiMjAyMC0wNS0xNSIsICIyMDIwLTA1LTE2IiwgIjIwMjAtMDUtMTciLCAiMjAyMC0wNS0xOCIsICIyMDIwLTA1LTE5IiwgCiAgICAgICAgICAgICAgICAgICIyMDIwLTA1LTIwIiwgIjIwMjAtMDUtMjEiLCAiMjAyMC0wNS0yMiIsICIyMDIwLTA1LTIzIiwgIjIwMjAtMDUtMjQiLCAiMjAyMC0wNS0yNSIsIAogICAgICAgICAgICAgICAgICAiMjAyMC0wNS0yNiIsICIyMDIwLTA1LTI3IikKcEZyYW1lJGNhbXBhaWduIDwtIGlmZWxzZShwRnJhbWUkZGF0ZSAlaW4lIGNhbXBhaWduRGF5cywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIkNhbXBhaWduIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIkFmdGVyIENhbXBhaWduIikKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IEVkaXRzLAogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBjYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY2FtcGFpZ24sCiAgICAgICAgICAgICAgICAgICBmaWxsID0gY2FtcGFpZ24sCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IEVkaXRzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiRWRpdHMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCiMjIyAzLjMgRGFpbHkgdXNlciBlZGl0cyBzaW5jZSB0aGUgY2FtcGFpZ24gb25zZXQgYnkgY3MvbnQKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQpwRnJhbWUgPC0gdXNlckVkaXRzICU+JSAKICBzZWxlY3QoZGF0ZSwgY3NfbnQpICU+JSAKICBncm91cF9ieShkYXRlLCBjc19udCkgJT4lIAogIHN1bW1hcmlzZShFZGl0cyA9IG4oKSkKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXRlKQpjYW1wYWlnbkRheXMgPC0gYygiMjAyMC0wNS0xNCIsICIyMDIwLTA1LTE1IiwgIjIwMjAtMDUtMTYiLCAiMjAyMC0wNS0xNyIsICIyMDIwLTA1LTE4IiwgIjIwMjAtMDUtMTkiLCAKICAgICAgICAgICAgICAgICAgIjIwMjAtMDUtMjAiLCAiMjAyMC0wNS0yMSIsICIyMDIwLTA1LTIyIiwgIjIwMjAtMDUtMjMiLCAiMjAyMC0wNS0yNCIsICIyMDIwLTA1LTI1IiwgCiAgICAgICAgICAgICAgICAgICIyMDIwLTA1LTI2IiwgIjIwMjAtMDUtMjciKQpwRnJhbWUkY2FtcGFpZ24gPC0gaWZlbHNlKHBGcmFtZSRkYXRlICVpbiUgY2FtcGFpZ25EYXlzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2FtcGFpZ24iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiQWZ0ZXIgQ2FtcGFpZ24iKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gRWRpdHMsCiAgICAgICAgICAgICAgICAgICBncm91cCA9IGNhbXBhaWduLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBjYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBjYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gRWRpdHMsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBmYWNldF93cmFwKH5jc19udCwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJFZGl0cyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKIyMjIDMuNCBFZGl0IENsYXNzZXMKCiMjIyMgMy40LjEgRWRpdCBDbGFzc2VzOiBhbGwgdXNlcnMKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQp1c2VyQ2xhc3MgPC0gdXNlckVkaXRzICU+JSAKICBzZWxlY3QoYWN0b3JfdXNlcikgJT4lIAogIGdyb3VwX2J5KGFjdG9yX3VzZXIpICU+JSAKICBzdW1tYXJpc2UoZWRpdHMgPSBuKCkpCmVkaXRCb3VuZGFyaWVzIDwtIGxpc3QoCiAgYygwLCAxKSwgCiAgYygyLCA0KSwKICBjKDUsIDkpLAogIGMoMTAsIDIwKSwKICBjKDIxLCA1MCksCiAgYyg1MSwgMTAwKQopCnVzZXJDbGFzcyRlZGl0Q2xhc3MgPC0gc2FwcGx5KHVzZXJDbGFzcyRlZGl0cywgZnVuY3Rpb24oeCkgewogIHdFQyA8LSBzYXBwbHkoZWRpdEJvdW5kYXJpZXMsIGZ1bmN0aW9uKHkpIHsKICAgIHggPj0geVsxXSAmIHggPD0geVsyXQogIH0pCiAgaWYgKHN1bSh3RUMpID09IDApIHsKICAgIHJldHVybigiPiAxMDAiKQogIH0gZWxzZSB7CiAgICByZXR1cm4ocGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsxXSwKICAgICAgICAgICAgICAgICAgIiAtICIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMl0sIAogICAgICAgICAgICAgICAgICAiKSIKICAgICAgICAgICAgICAgICAgKQogICAgKQogIH0KfSkKZWRpdENsYXNzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUodXNlckNsYXNzJGVkaXRDbGFzcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXMoZWRpdENsYXNzKSA8LSBjKCdFZGl0IENsYXNzJywgJ051bS5Vc2VycycpCmVkaXRDbGFzcyRvcmRlciA8LSBhcy5udW1lcmljKHNhcHBseShlZGl0Q2xhc3MkYEVkaXQgQ2xhc3NgLCBmdW5jdGlvbih4KSB7CiAgbG93ZXIgPC0gc3RyX2V4dHJhY3QoeCwgJ1tbOmRpZ2l0Ol1dKycpCn0pKQplZGl0Q2xhc3MgPC0gYXJyYW5nZShlZGl0Q2xhc3MsIG9yZGVyKQplZGl0Q2xhc3Mkb3JkZXIgPC0gTlVMTApkYXRhdGFibGUoZWRpdENsYXNzKQpgYGAKCiMjIyMgMy40LjIgRWRpdCBDbGFzc2VzOiBjcyAoY29tbXVuaXR5IHN1cHBvcnQpIHVzZXJzCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuNX0KdXNlckNsYXNzIDwtIHVzZXJFZGl0cyAlPiUgCiAgZmlsdGVyKGNzX250ID09ICJjcyIpICU+JSAKICBzZWxlY3QoYWN0b3JfdXNlcikgJT4lIAogIGdyb3VwX2J5KGFjdG9yX3VzZXIpICU+JSAKICBzdW1tYXJpc2UoZWRpdHMgPSBuKCkpCmVkaXRCb3VuZGFyaWVzIDwtIGxpc3QoCiAgYygwLCAxKSwgCiAgYygyLCA0KSwKICBjKDUsIDkpLAogIGMoMTAsIDIwKSwKICBjKDIxLCA1MCksCiAgYyg1MSwgMTAwKQopCnVzZXJDbGFzcyRlZGl0Q2xhc3MgPC0gc2FwcGx5KHVzZXJDbGFzcyRlZGl0cywgZnVuY3Rpb24oeCkgewogIHdFQyA8LSBzYXBwbHkoZWRpdEJvdW5kYXJpZXMsIGZ1bmN0aW9uKHkpIHsKICAgIHggPj0geVsxXSAmIHggPD0geVsyXQogIH0pCiAgaWYgKHN1bSh3RUMpID09IDApIHsKICAgIHJldHVybigiPiAxMDAiKQogIH0gZWxzZSB7CiAgICByZXR1cm4ocGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsxXSwKICAgICAgICAgICAgICAgICAgIiAtICIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMl0sIAogICAgICAgICAgICAgICAgICAiKSIKICAgICAgICAgICAgICAgICAgKQogICAgKQogIH0KfSkKZWRpdENsYXNzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUodXNlckNsYXNzJGVkaXRDbGFzcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXMoZWRpdENsYXNzKSA8LSBjKCdFZGl0IENsYXNzJywgJ051bS5Vc2VycycpCmVkaXRDbGFzcyRvcmRlciA8LSBhcy5udW1lcmljKHNhcHBseShlZGl0Q2xhc3MkYEVkaXQgQ2xhc3NgLCBmdW5jdGlvbih4KSB7CiAgbG93ZXIgPC0gc3RyX2V4dHJhY3QoeCwgJ1tbOmRpZ2l0Ol1dKycpCn0pKQplZGl0Q2xhc3MgPC0gYXJyYW5nZShlZGl0Q2xhc3MsIG9yZGVyKQplZGl0Q2xhc3Mkb3JkZXIgPC0gTlVMTApkYXRhdGFibGUoZWRpdENsYXNzKQpgYGAKCiMjIyMgMy40LjIgRWRpdCBDbGFzc2VzOiBudCAobmV3IHRhc2tzKSB1c2VycwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnVzZXJDbGFzcyA8LSB1c2VyRWRpdHMgJT4lIAogIGZpbHRlcihjc19udCA9PSAibnQiKSAlPiUgCiAgc2VsZWN0KGFjdG9yX3VzZXIpICU+JSAKICBncm91cF9ieShhY3Rvcl91c2VyKSAlPiUgCiAgc3VtbWFyaXNlKGVkaXRzID0gbigpKQplZGl0Qm91bmRhcmllcyA8LSBsaXN0KAogIGMoMCwgMSksIAogIGMoMiwgNCksCiAgYyg1LCA5KSwKICBjKDEwLCAyMCksCiAgYygyMSwgNTApLAogIGMoNTEsIDEwMCkKKQp1c2VyQ2xhc3MkZWRpdENsYXNzIDwtIHNhcHBseSh1c2VyQ2xhc3MkZWRpdHMsIGZ1bmN0aW9uKHgpIHsKICB3RUMgPC0gc2FwcGx5KGVkaXRCb3VuZGFyaWVzLCBmdW5jdGlvbih5KSB7CiAgICB4ID49IHlbMV0gJiB4IDw9IHlbMl0KICB9KQogIGlmIChzdW0od0VDKSA9PSAwKSB7CiAgICByZXR1cm4oIj4gMTAwIikKICB9IGVsc2UgewogICAgcmV0dXJuKHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMV0sCiAgICAgICAgICAgICAgICAgICIgLSAiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzJdLCAKICAgICAgICAgICAgICAgICAgIikiCiAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0pCmVkaXRDbGFzcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHVzZXJDbGFzcyRlZGl0Q2xhc3MpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGVkaXRDbGFzcykgPC0gYygnRWRpdCBDbGFzcycsICdOdW0uVXNlcnMnKQplZGl0Q2xhc3Mkb3JkZXIgPC0gYXMubnVtZXJpYyhzYXBwbHkoZWRpdENsYXNzJGBFZGl0IENsYXNzYCwgZnVuY3Rpb24oeCkgewogIGxvd2VyIDwtIHN0cl9leHRyYWN0KHgsICdbWzpkaWdpdDpdXSsnKQp9KSkKZWRpdENsYXNzIDwtIGFycmFuZ2UoZWRpdENsYXNzLCBvcmRlcikKZWRpdENsYXNzJG9yZGVyIDwtIE5VTEwKZGF0YXRhYmxlKGVkaXRDbGFzcykKYGBgCg==