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

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

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

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/28 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")

Table 2.1.1 Pageviews totals

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

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")

Table 2.2.1 Total pageviews, by devices, aggregated across the campaign banners.

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

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 2.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")

Table 2.3.1 Total pageviews, by devices, aggregated across the campaign banners.

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

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 2.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")

Table 2.4.2 Total pageviews, by nt/cs, aggregated across the campaign banners.

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

2.5 Pageviews: Var/Ctrl vs. cs/nt

Chart 2.5.1 Pageviews, by nt/cs vs. ctrl/var.

pFrame <- dataSet %>% 
  select(var_ctrl, cs_nt, Pageviews) %>% 
  group_by(var_ctrl, cs_nt) %>% 
  summarise(pageviews = sum(Pageviews))
ggplot(pFrame, aes(x = var_ctrl,
                   y = pageviews,
                   group = cs_nt,
                   color = cs_nt,
                   fill = cs_nt,
                   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) +
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Banner") + ylab("Pageviews") + 
  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")

Chart 2.5.2 Pageviews, by nt/cs vs. ctrl/var, across the campaign pages.

pFrame <- dataSet %>% 
  select(var_ctrl, cs_nt, Page, Pageviews) %>% 
  group_by(var_ctrl, cs_nt, Page) %>% 
  summarise(pageviews = sum(Pageviews))
ggplot(pFrame, aes(x = var_ctrl,
                   y = pageviews,
                   group = cs_nt,
                   color = cs_nt,
                   fill = cs_nt,
                   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(~Page, nrow = 3, scales = "free") + 
  ggtitle('2020 WMDE Occasional Editors Banner Campaign') +
  xlab("Banner") + ylab("Pageviews") + 
  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.6 Pageviews: Full Dataset

Table 2.6.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)
)
userEdits$dateType <- as.Date(userEdits$date) 

In total, 681 users made edits.

3.2 Daily user edits since the campaign onset

Chart 3.2.1 daily user edits

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 <- userEdits %>% 
  select(dateType) %>% 
  group_by(dateType) %>% 
  summarise(Edits = n())
pFrame <- arrange(pFrame, dateType)
pFrame$campaign <- sapply(pFrame$dateType, function(x) {
  if (x < "2020-05-14") {
    return("Before Campaign")
  } else if (x > "2020-05-28") {
    return("After Campaign") 
  } else {
    return("Campaign")
  }
})
pFrame$date <- as.character(pFrame$dateType)
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 = 6)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "top")

Table 3.2.1 Mean user edits per day: before campaign, during the campaign, and after the campaign

tFrame <- pFrame %>% 
  select(campaign, Edits) %>% 
  group_by(campaign) %>% 
  summarise(totalEdits = sum(Edits), 
            meanEdits = round(mean(Edits), 2)
            )
datatable(tFrame)

3.3 Daily user edits since the campaign onset by cs/nt

Chart 3.3.1 Mean user edits per day, per cs/nt: before campaign, during the campaign, and after the campaign

pFrame <- userEdits %>% 
  select(date, cs_nt) %>% 
  group_by(date, cs_nt) %>% 
  summarise(Edits = n())
pFrame$dateType <- as.Date(pFrame$date) 
pFrame$campaign <- sapply(pFrame$dateType, function(x) {
  if (x < "2020-05-14") {
    return("Before Campaign")
  } else if (x > "2020-05-28") {
    return("After Campaign") 
  } else {
    return("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 = 6)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "top")

Table 3.3.1 Mean user edits per day, per cs/nt: before campaign, during the campaign, and after the campaign

tFrame <- pFrame %>% 
  select(campaign, cs_nt, Edits) %>% 
  group_by(campaign, cs_nt) %>% 
  summarise(totalEdits = sum(Edits), 
            meanEdits = round(mean(Edits), 2)
            )
datatable(tFrame)

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.2A 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.2B 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)

3.4.3 Edit Classes: before, during, and after the campaign

userEdits$campaign <- sapply(userEdits$dateType, function(x) {
  if (x < "2020-05-14") {
    return("Before Campaign")
  } else if (x > "2020-05-28") {
    return("After Campaign") 
  } else {
    return("Campaign")
  }
})
userClass <- userEdits %>% 
  select(campaign, actor_user) %>% 
  group_by(campaign, 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], 
                  ")"
                  )
    )
  }
})
userClassFull <- expand.grid(actor_user = unique(userClass$actor_user), 
                             campaign = unique(userClass$campaign))
userClass <- left_join(userClassFull, 
                       userClass, 
                       by = c('actor_user', 'campaign'))
userClass$edits[is.na(userClass$edits)] <- 0
userClass$editClass[is.na(userClass$editClass)] <- '(0 - 1)'
editClass <- as.data.frame(table(userClass$editClass, userClass$campaign), 
                           stringsAsFactors = F)
colnames(editClass) <- c('Edit Class', 'Campaign', '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)
LS0tCnRpdGxlOiAnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nCmF1dGhvcjogIkdvcmFuIFMuIE1pbG92YW5vdmljLCBEYXRhIFNjaWVudGlzdCwgV01ERSIKZGF0ZTogIkp1bmUgMjEsIDIwMjAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0aGVtZTogc2ltcGxleAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDUKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQotLS0KCioqRmVlZGJhY2sqKiBzaG91bGQgYmUgc2VuZCB0byBgZ29yYW4ubWlsb3Zhbm92aWNfZXh0QHdpa2ltZWRpYS5kZWAuIAoKVGhlIGNhbXBhaWduIHdhcyBydW4gZnJvbSAyMDIwLzA1LzE0IHRvIDIwMjAvMDUvMjguCgoqKkNVUlJFTlQgVVBEQVRFOioqIENvbXBsZXRlIGRhdGFzZXQgYXMgb2YgMjAyMC8wNS8yOC4KCmBgYHtyLCBlY2hvID0gRiwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEYsIHJlc3VsdHMgPSAnaGlkZSd9CiMgIWRpYWdub3N0aWNzIG9mZgojIyMgLS0tIFNldHVwCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoRFQpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpgYGAKCiMjIDAuIERhdGEgQWNxdWlzaXRvbgoKKipOT1RFOioqIHRoZSBEYXRhIEFjcXVpc2l0aW9uIGNvZGUgY2h1bmsgaXMgbm90IGZ1bGx5IHJlcHJvZHVjaWJsZSBmcm9tIHRoaXMgUmVwb3J0LiBUaGUgZGF0YSBhcmUgY29sbGVjdGVkIGJ5IHJ1bm5pbmcgdGhlIHNjcmlwdCBgMjAyMF9PY2Nhc2lvbmFsRWRpdG9yc19QUk9EVUNUSU9OLlJgIG9uIHN0YXQxMDA0LmVxaWFkLndtbmV0LCBjb2xsZWN0aW5nIHRoZSBkYXRhIGFzIGAudHN2YCBhbmQgYC5jc3ZgIGZpbGVzLCBjb3B5aW5nIG1hbnVhbGx5LCBhbmQgcHJvY2Vzc2luZyBsb2NhbGx5LiBBIGRhaWx5IGNyb250YWIgam9iIHdhcyBydW4gZnJvbSBgMjAyMC8wNS8xNGAgdG8gYDIwMjAvMDUvMjhgIHRvIGNvbGxlY3QgdGhlIGRhdGEgZm9yIGRhaWx5IHJlcG9ydGluZy4gVGhlIGRhdGEgdXNlZCBpbiB0aGlzIHJlcG9ydCBhcmUgYWdncmVnYXRlcyBvZiB0aGUgZGFpbHkgZGF0YXNldHMsIHNhbml0aXplZCBhbmQgYW5vbnltaXplZC4gICAKCiMjIyAwLjEgRGFpbHkgVXBkYXRlCgpgYGB7ciwgZWNobyA9IFQsIGV2YWwgPSBGfQojIyMgLS0tIFdNREUgMjAyMF9PY2Nhc2lvbmFsRWRpdG9yc19QUk9EVUNUSU9OLlIKIyAtIENhbXBhaWduIHN0YXJ0OiAyMDIwLzA1LzE0CiMgLSBDYW1wYWlnbiBlbmQ6IDIwMjAvMDUvMjcKIyAtIHJ1biBmcm9tOiBzdGF0MTAwNAojIC0gcGF0aDogCgojIyMgLS0tIGxpYnJhcmllcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKCiMjIyAtLS0gZGlyIHN0cnVjdHVyZQpjYW1wYWlnblBhdGggPC0gcGFzdGUwKGdldHdkKCksICIvIikKZGF0YURpciA8LSBwYXN0ZTAoY2FtcGFpZ25QYXRoLCAiX2RhdGEvIikKYW5hbHl0aWNzRGlyIDwtIHBhc3RlMChjYW1wYWlnblBhdGgsICJfYW5hbHl0aWNzLyIpCiMjIyAtLS0gY2FtcGFpZ24gc3BlY2lmaWNzCmNhbXBhaWduTmFtZSA8LSAnT2NjYXNpb25hbEVkaXRvcnMyMDIwJwoKIyMjIC0tLSBkZXRlcm1pbmUgY2V0RGF5CmNldERheSA8LSBTeXMudGltZSgpCmNldERheQphdHRyKGNldERheSwgInR6b25lIikgPC0gIkV1cm9wZS9CZXJsaW4iCiMgLSBvbmUgZGF5IGJlaGluZCBmb3IgY3JvbnRhYgojIC0gKGkuZS4gd2FpdGluZyBmb3Igd21mLndlYnJlcXVlc3QgdG8gY29tcGxldGUgaXMgZGF0YSBhY3F1aXNpdGlvbikKY2V0RGF5IDwtIHltZCgKICBzdHJzcGxpdChhcy5jaGFyYWN0ZXIoY2V0RGF5KSwgCiAgICAgICAgICAgc3BsaXQgPSAiICIsIAogICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXQopIC0gMQoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBCYW5uZXIgSW1wcmVzc2lvbnMKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLSBmdW5jdGlvbjogd21kZV9jb2xsZWN0X2Jhbm5lcl9pbXByZXNzaW9ucwp3bWRlX2NvbGxlY3RfYmFubmVyX2ltcHJlc3Npb25zIDwtIGZ1bmN0aW9uKHVyaV9ob3N0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcGF0aCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIpIHsKICAKICAjIC0gTk9URToKICAjIC0gZXhwZWN0ZWQgZm9ybWF0IGZvciBjZXREYXkgaXM6IFlZWVktTU0tREQKICAKICAjIC0gdG8gZGF0YURpcgogIHNldHdkKGRhdGFEaXIpCiAgCiAgIyAtIGxpYnJhcmllcwogIGxpYnJhcnkoc3RyaW5ncikKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgZGF0ZXRpbWVfY29uZGl0aW9uCiAgY2V0X2NvbmRpdGlvbiA8LSBzZXEoCiAgICBmcm9tID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMDowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICB0byA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDIzOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIGJ5ID0gImhvdXIiCiAgKSAKICBhdHRyKGNldF9jb25kaXRpb24sICJ0em9uZSIpIDwtICJVVEMiCiAgY2V0X2NvbmRpdGlvbiA8LSBhcy5jaGFyYWN0ZXIoY2V0X2NvbmRpdGlvbikKICBjZXRfY29uZGl0aW9uIDwtIHVubGlzdChzdHJfZXh0cmFjdF9hbGwoY2V0X2NvbmRpdGlvbiwgIl4oW1s6ZGlnaXQ6XV18XFxzfC0pKiIpKQogIGNldF95ZWFycyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsxXQogICAgfSkKICBjZXRfbW9udGhzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzJdCiAgICB9KQogIGNldF9tb250aHMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X21vbnRocykKICBjZXRfZGF5cyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVszXQogICAgfSkKICBjZXRfZGF5cyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfZGF5cykKICBjZXRfaG91cnMgPC0gc2FwcGx5KHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCAKICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgeFsyXQogICAgICAgICAgICAgICAgICAgICAgfSkKICBjZXRfaG91cnMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2hvdXJzKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlMCgKICAgICJ5ZWFyID0gIiwgY2V0X3llYXJzLCAiIEFORCAiLAogICAgIm1vbnRoID0gIiwgY2V0X21vbnRocywgIiBBTkQgIiwKICAgICJkYXkgPSAiLCBjZXRfZGF5cywgIiBBTkQgIiwgCiAgICAiaG91ciA9ICIsIGNldF9ob3VycwogICkKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZSgiKCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGV0aW1lQ29uZGl0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSB1cmlfcGF0aF9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9wYXRoKSA+IDEpIHsKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX3BhdGhfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfcGF0aCA9ICciLCB1cmlfcGF0aCwgIiciKQogIH0KICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX2hvc3RfY29uZGl0aW9uCiAgaWYgKGxlbmd0aCh1cmlfaG9zdCkgPiAxKSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfaG9zdCA9ICciLCB1cmlfaG9zdCwgIiciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiA9IHBhc3RlMCgidXJpX2hvc3QgPSAnIiwgdXJpX2hvc3QsICInIikKICB9CiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9xdWVyeV9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9xdWVyeSkgPiAxKSB7CiAgICB1cmlfcXVlcnlfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfcXVlcnkgTElLRSAnJSIsIHVyaV9xdWVyeSwgIiUnIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9xdWVyeV9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9xdWVyeSBMSUtFICclIiwgdXJpX3F1ZXJ5LCAiJSciKQogIH0KICAKICAjIC0gY29tcG9zZSBIaXZlUUwgcXVlcnkKICBoaXZlUXVlcnkgPC0gcGFzdGUwKAogICAgIlVTRSB3bWY7CiAgICBTRUxFQ1QgdXJpX3F1ZXJ5IEZST00gd2VicmVxdWVzdAogICAgV0hFUkUgKCIsCiAgICB1cmlfaG9zdF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcGF0aF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcXVlcnlfY29uZGl0aW9uLCAiIEFORCAiLAogICAgIigiLCBkYXRldGltZUNvbmRpdGlvbiwgIikiLAogICAgIik7IgogICAgKQogIAogICMgLSB3cml0ZSBocWwKICB3cml0ZShoaXZlUXVlcnksIHF1ZXJ5RmlsZSkKICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0OgogIGhpdmVBcmdzIDwtICdzdWRvIC11IGFuYWx5dGljcy1wcml2YXRlZGF0YSBrZXJiZXJvcy1ydW4tY29tbWFuZCBhbmFseXRpY3MtcHJpdmF0ZWRhdGEgL3Vzci9sb2NhbC9iaW4vYmVlbGluZSAtLXNpbGVudCAtLWluY3JlbWVudGFsPXRydWUgLS12ZXJib3NlPWZhbHNlIC1mJwogIGhpdmVJbnB1dCA8LSBwYXN0ZTAocXVlcnlGaWxlLCAnID4gJywgZmlsZU5hbWUpCiAgIyAtIGNvbW1hbmQ6CiAgaGl2ZUNvbW1hbmQgPC0gcGFzdGUoaGl2ZUFyZ3MsIGhpdmVJbnB1dCkKICByZXR1cm4oCiAgICBzeXN0ZW0oY29tbWFuZCA9IGhpdmVDb21tYW5kLCB3YWl0ID0gVFJVRSkKICApCn0KCiMgLSBzZXQgcGFyYW1zIHRvIHdtZGVfY29sbGVjdF9iYW5uZXJfaW1wcmVzc2lvbnMKIyAtIGZvciB0aGUgT2NjYXNpb25hbEVkaXRvcnMyMDIwCnVyaV9ob3N0IDwtIGMoJ2RlLndpa2lwZWRpYS5vcmcnLCAnZGUubS53aWtpcGVkaWEub3JnJykKdXJpX3BhdGggIDwtICcvYmVhY29uL2ltcHJlc3Npb24nCnVyaV9xdWVyeSA8LSBjKCdXTURFX29jZWRpdG9yc19zcHJpbmdfMjAyMF8nKQpxdWVyeUZpbGUgPC0gJ09jY2FzaW9uYWxFZGl0b3JzMjAyMF9CYW5uZXJJbXByZXNzaW9ucy5ocWwnCmZpbGVOYW1lIDwtIHBhc3RlMChkYXRhRGlyLCAiYmFubmVySW1wcmVzc2lvbnNfIiwgY2V0RGF5LCAiLnRzdiIpCgojIC0gY29sbGVjdCBCYW5uZXIgSW1wcmVzc2lvbiBkYXRhCndtZGVfY29sbGVjdF9iYW5uZXJfaW1wcmVzc2lvbnModXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpcikKCiMgLSBmdW5jdGlvbjogd21kZV9wcm9jZXNzX2Jhbm5lcl9pbXByZXNzaW9ucwp3bWRlX3Byb2Nlc3NfYmFubmVyX2ltcHJlc3Npb25zIDwtIGZ1bmN0aW9uKGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcXVlcnkpIHsKICAKICAjIC0gdG8gZGF0YURpcgogIHNldHdkKGRhdGFEaXIpCiAgCiAgIyAtIGxpYnJhcmllcwogIGxpYnJhcnkoc3RyaW5ncikKICBsaWJyYXJ5KGRwbHlyKQogIAogICMgLSBsb2FkCiAgYmFubmVyRGF0YSA8LSB0cnlDYXRjaCh7CiAgICBhcy5kYXRhLmZyYW1lKGZyZWFkKGZpbGVOYW1lKSkKICAgIH0sCiAgICBlcnJvciA9IGZ1bmN0aW9uKGNvbmRpdGlvbikgewogICAgICByZXR1cm4oRkFMU0UpCiAgfSkKICAjIC0gcHJvY2VzcwogIGlmIChjbGFzcyhiYW5uZXJEYXRhKSA9PSAnbG9naWNhbCcpIHsKICAgIHJldHVybihGQUxTRSkgCiAgfSBlbHNlIHsKICAgICMgLSBjbGVhbgogICAgYmFubmVyRGF0YSA8LSBkcGx5cjo6ZmlsdGVyKGJhbm5lckRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5ICE9ICIiKQogICAgIyAtIHNwbGl0CiAgICBiYW5uZXJEYXRhIDwtIHRpZHlyOjpzZXBhcmF0ZShiYW5uZXJEYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IHVyaV9xdWVyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRvID0gYygnY291bnRyeScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3JlZ2lvbicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYW5vbnltb3VzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdwcm9qZWN0JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdkYicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAndXNlbGFuZycsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2RldmljZScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2RlYnVnJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAncmFuZG9tY2FtcGFpZ24nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3JhbmRvbWJhbm5lcicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAncmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2ltcHJlc3Npb25FdmVudFNhbXBsZVJhdGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2NhbXBhaWduU3RhdHVzZXMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3N0YXR1cycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnc3RhdHVzQ29kZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnY2FtcGFpZ24nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2NhbXBhaWduQ2F0ZWdvcnknLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2NhbXBhaWduQ2F0ZWdvcnlVc2VzTGVnYWN5JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdidWNrZXQnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2Jhbm5lcicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnYmFubmVyQ2F0ZWdvcnknLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdyZXN1bHQnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICImIikgJT4lIAogICAgICBkcGx5cjo6c2VsZWN0KGJhbm5lciwgZGV2aWNlLCByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZSwgcmVzdWx0KQogICAgIyAtIGZpbHRlciBmb3IgdXJpX3F1ZXJ5CiAgICBiYW5uZXJEYXRhIDwtIGJhbm5lckRhdGFbZ3JlcGwodXJpX3F1ZXJ5LCBiYW5uZXJEYXRhJGJhbm5lciksIF0KICAgICMgLSBjbGVhbiByZWxldmFudCBmaWVsZHMKICAgICMgLSBiYW5uZXI6CiAgICBiYW5uZXJEYXRhJGJhbm5lciA8LSBnc3ViKCJeYmFubmVyPSIsICIiLCBiYW5uZXJEYXRhJGJhbm5lcikKICAgICMgLSByZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZToKICAgIGJhbm5lckRhdGEkcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUgPC0gYXMubnVtZXJpYygKICAgICAgZ3N1YigiXnJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlPSIsICIiLCBiYW5uZXJEYXRhJHJlY29yZEltcHJlc3Npb25TYW1wbGVSYXRlKQogICAgKQogICAgIyAtIGRldmljZToKICAgIGJhbm5lckRhdGEkZGV2aWNlIDwtIGdzdWIoIl5kZXZpY2U9IiwgIiIsIGJhbm5lckRhdGEkZGV2aWNlKQogICAgIyAtIHJlc3VsdDoKICAgIGJhbm5lckRhdGEkcmVzdWx0IDwtIGdzdWIoIl5yZXN1bHQ9IiwgIiIsIGJhbm5lckRhdGEkcmVzdWx0KQogICAgIyAtIGZpbHRlciBmb3IgcmVzdWx0PXNob3cKICAgIGJhbm5lckRhdGEgPC0gZHBseXI6OmZpbHRlcihiYW5uZXJEYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9PSAic2hvdyIpCiAgICAjIC0gY29ycmVjdGlvbiBmb3IgcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUKICAgIGJhbm5lckRhdGEkcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUgPC0gCiAgICAgIDEvYmFubmVyRGF0YSRyZWNvcmRJbXByZXNzaW9uU2FtcGxlUmF0ZQogICAgCiAgICAjIC0gYWdncmVnYXRlOgogICAgYmFubmVyRGF0YSA8LSBiYW5uZXJEYXRhICU+JSAKICAgICAgZHBseXI6OnNlbGVjdChiYW5uZXIsIGRldmljZSwgcmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUpICU+JSAKICAgICAgZHBseXI6Omdyb3VwX2J5KGJhbm5lciwgZGV2aWNlKSAlPiUgCiAgICAgIGRwbHlyOjpzdW1tYXJpc2UoaW1wcmVzc2lvbnMgPSBzdW0ocmVjb3JkSW1wcmVzc2lvblNhbXBsZVJhdGUpKQogICAgCiAgICAjIC0gYWRkIGNldERheSwgbWUKICAgIGJhbm5lckRhdGEkZGF0ZSA8LSBjZXREYXkKICAgIGJhbm5lckRhdGEkY2FtcGFpZ24gPC0gY2FtcGFpZ25OYW1lCiAgICAKICAgICMgLSBzdG9yZToKICAgIHdyaXRlLmNzdihiYW5uZXJEYXRhLCAKICAgICAgICAgICAgICBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAiYmFubmVySW1wcmVzc2lvbnMiLAogICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICIuY3N2IgogICAgICAgICAgICAgICkKICAgICkKICAgIAogICAgIyAtIHJldHVybgogICAgcmV0dXJuKFRSVUUpCiAgfQp9CgojIC0gd3JhbmdsZSBCYW5uZXIgSW1wcmVzc2lvbiBkYXRhCmNhbXBhaWduTmFtZSA8LSAiT2NjYXNpb25hbEVkaXRvcnMyMDIwIgp1cmlfcXVlcnkgPC0gYygnV01ERV9vY2VkaXRvcnNfc3ByaW5nXzIwMjBfJykKYmFubmVyUHJvY2VzcyA8LSB3bWRlX3Byb2Nlc3NfYmFubmVyX2ltcHJlc3Npb25zKGZpbGVOYW1lID0gZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyID0gZGF0YURpciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSA9IGNhbXBhaWduTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9xdWVyeSA9IHVyaV9xdWVyeSkKCgoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBQYWdldmlld3MKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLSBmdW5jdGlvbjogd21kZV9jb2xsZWN0X3BhZ2V2aWV3cwp3bWRlX2NvbGxlY3RfcGFnZXZpZXdzIDwtIGZ1bmN0aW9uKHVyaV9ob3N0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpcikgewogIAogICMgLSBOT1RFOgogICMgLSBleHBlY3RlZCBmb3JtYXQgZm9yIGNldERheSBpczogWVlZWS1NTS1ERAogIAogICMgLSB0byBkYXRhRGlyCiAgc2V0d2QoZGF0YURpcikKICAKICAjIC0gbGlicmFyaWVzCiAgbGlicmFyeShzdHJpbmdyKQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSBkYXRldGltZV9jb25kaXRpb24KICBjZXRfY29uZGl0aW9uIDwtIHNlcSgKICAgIGZyb20gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAwOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIHRvID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMjM6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgYnkgPSAiaG91ciIKICApIAogIGF0dHIoY2V0X2NvbmRpdGlvbiwgInR6b25lIikgPC0gIlVUQyIKICBjZXRfY29uZGl0aW9uIDwtIGFzLmNoYXJhY3RlcihjZXRfY29uZGl0aW9uKQogIGNldF9jb25kaXRpb24gPC0gdW5saXN0KHN0cl9leHRyYWN0X2FsbChjZXRfY29uZGl0aW9uLCAiXihbWzpkaWdpdDpdXXxcXHN8LSkqIikpCiAgY2V0X3llYXJzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzFdCiAgICB9KQogIGNldF9tb250aHMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMl0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfbW9udGhzKQogIGNldF9kYXlzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzNdCiAgICB9KQogIGNldF9kYXlzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9kYXlzKQogIGNldF9ob3VycyA8LSBzYXBwbHkoc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIAogICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICB4WzJdCiAgICAgICAgICAgICAgICAgICAgICB9KQogIGNldF9ob3VycyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfaG91cnMpCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUwKAogICAgInllYXIgPSAiLCBjZXRfeWVhcnMsICIgQU5EICIsCiAgICAibW9udGggPSAiLCBjZXRfbW9udGhzLCAiIEFORCAiLAogICAgImRheSA9ICIsIGNldF9kYXlzLCAiIEFORCAiLCAKICAgICJob3VyID0gIiwgY2V0X2hvdXJzCiAgKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlKCIoIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZXRpbWVDb25kaXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpCiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9ob3N0X2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX2hvc3QpID4gMSkgewogICAgdXJpX2hvc3RfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfaG9zdCA9ICciLCB1cmlfaG9zdCwgIiciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX2hvc3RfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfaG9zdCA9ICciLCB1cmlfaG9zdCwgIiciKQogIH0KICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX3BhdGhfY29uZGl0aW9uCiAgaWYgKGxlbmd0aCh1cmlfcGF0aCkgPiAxKSB7CiAgICB1cmlfcGF0aF9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfcGF0aCA9ICciLCB1cmlfcGF0aCwgIiciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiA9IHBhc3RlMCgidXJpX3BhdGggPSAnIiwgdXJpX3BhdGgsICInIikKICB9CiAgCiAgIyAtIGNvbXBvc2UgSGl2ZVFMIHF1ZXJ5CiAgaGl2ZVF1ZXJ5IDwtIHBhc3RlMCggCiAgICAiVVNFIHdtZjsKICAgIFNFTEVDVCB1cmlfaG9zdCwgdXJpX3BhdGgsIHVyaV9xdWVyeSwgcmVmZXJlciBGUk9NIHdlYnJlcXVlc3QKICAgIFdIRVJFICgiLAogICAgdXJpX2hvc3RfY29uZGl0aW9uLCAiIEFORCAiLAogICAgdXJpX3BhdGhfY29uZGl0aW9uLCAiIEFORCAiLAogICAgIigiLCBkYXRldGltZUNvbmRpdGlvbiwgIikiLAogICAgIik7IgogICAgKQogIAogICMgLSB3cml0ZSBocWwKICB3cml0ZShoaXZlUXVlcnksIHF1ZXJ5RmlsZSkKICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0OgogIGtlcmJlcm9zUHJlZml4IDwtIAogICAgJ3N1ZG8gLXUgYW5hbHl0aWNzLXByaXZhdGVkYXRhIGtlcmJlcm9zLXJ1bi1jb21tYW5kIGFuYWx5dGljcy1wcml2YXRlZGF0YSAnCiAgIyAtIEtlcmJlcm9zIGluaXQKICBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlMChrZXJiZXJvc1ByZWZpeCwgJyBoZGZzIGRmcyAtbHMnKSwgCiAgICAgICAgIHdhaXQgPSBUKQogICMgLSBSdW4gcXVlcnkKICBxdWVyeSA8LSBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlKGtlcmJlcm9zUHJlZml4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcvdXNyL2xvY2FsL2Jpbi9iZWVsaW5lIC0taW5jcmVtZW50YWw9dHJ1ZSAtLXNpbGVudCAtZiAiJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMChkYXRhRGlyLCBxdWVyeUZpbGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIgPiAnLCBkYXRhRGlyLCBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKSwKICAgICAgICAgICAgICAgICAgd2FpdCA9IFRSVUUpCn0KCiMgLSBzZXQgcGFyYW1zIHRvIHdtZGVfY29sbGVjdF9wYWdldmlld3MKIyAtIGZvciB0aGUgMjAyMF9FbWFpbENhbXBhaWduV2lraXBlZGlhQ2hhbGxlbmdlCnVyaV9ob3N0IDwtIGMoJ2RlLndpa2lwZWRpYS5vcmcnLCAnZGUubS53aWtpcGVkaWEub3JnJykKdXJpX3BhdGggIDwtIGMoCiAgJy93aWtpL1dpa2lwZWRpYTpXaWtpbWVkaWFfRGV1dHNjaGxhbmQvRGVpbkVuZ2FnZW1lbnQnLAogICcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0RlaW5FbmdhZ2VtZW50L0xpdGVyYXR1cicsCiAgJy93aWtpL1dpa2lwZWRpYTpNZW50b3JlbnByb2dyYW1tJykKIyB1cmlfcXVlcnkgPC0gcGFzdGUwKCdXTURFXzIwMjBfY2hhbGxlbmdlXycsIDE6MzApCnF1ZXJ5RmlsZSA8LSAnT2NjYXNpb25hbEVkaXRvcnMyMDIwX1BhZ2V2aWV3cy5ocWwnCmZpbGVOYW1lIDwtIHBhc3RlMCgicGFnZXZpZXdzXyIsIGNldERheSwgIi50c3YiKQoKIyAtIGNvbGxlY3QgUGFnZXZpZXdzIGRhdGEKd21kZV9jb2xsZWN0X3BhZ2V2aWV3cyh1cmlfaG9zdCwKICAgICAgICAgICAgICAgICAgICAgICB1cmlfcGF0aCwKICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIpCgojIyMgLS0tIFdyYW5nbGUgUGFnZXZpZXdzCiMgLSBmdW5jdGlvbjogd21kZV9wcm9jZXNzX3BhZ2V2aWV3cwp3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzIDwtIGZ1bmN0aW9uKGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9xdWVyeV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5ID0gY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSA9IGNhbXBhaWduTmFtZSkgewogIAogICMgLSB0byBkYXRhRGlyCiAgc2V0d2QoZGF0YURpcikKICAKICAjIC0gbGlicmFyaWVzCiAgbGlicmFyeShzdHJpbmdyKQogIGxpYnJhcnkoZHBseXIpCiAgbGlicmFyeSh0aWR5cikKICBsaWJyYXJ5KGRhdGEudGFibGUpCiAgCiAgIyAtIGxvYWQKICBwYWdldmlld3NEYXRhIDwtIHJlYWRMaW5lcyhmaWxlTmFtZSkKICB3U3RhcnQgPC0gd2hpY2goZ3JlcGwoInVyaV9ob3N0IiwgcGFnZXZpZXdzRGF0YSkpCiAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhWyh3U3RhcnQgKyAyKToobGVuZ3RoKHBhZ2V2aWV3c0RhdGEpIC0gMildCiAgcGFnZXZpZXdzRGF0YSA8LSBkYXRhLmZyYW1lKGRhdCA9IHBhZ2V2aWV3c0RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICBwYWdldmlld3NEYXRhIDwtIHNlcGFyYXRlKHBhZ2V2aWV3c0RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRvID0gYygndXJpX2hvc3QnLCAndXJpX3BhdGgnLCAndXJpX3F1ZXJ5JywgJ3JlZmVyZXInKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIpCiAgCiAgIyAtIGFwcGx5IHVyaV9xdWVyeV9maWx0ZXIKICAjIC0gTk9URTogbG9va2luZyBpbiBib3RoOiB1cmlfcXVlcnksIHJlZmVyZXIgCiAgIyAtIE5PVEU6IGhhY2sgZm9yIHRoZSAyMDIwX09jY2FzaW9uYWxFZGl0b3JzIENhbXBhaWduCiAgIyAtIGluY2x1ZGUgcGFnZXZpZXdzIGZvciAKICAjIC0gJy93aWtpL1dpa2lwZWRpYTpXaWtpbWVkaWFfRGV1dHNjaGxhbmQvRGVpbkVuZ2FnZW1lbnQvTGl0ZXJhdHVyJywKICAjIC0gJy93aWtpL1dpa2lwZWRpYTpNZW50b3JlbnByb2dyYW1tJwogICMgLSBvbiBkZXdpa2kgd2hlcmUgZ3JlcGwod191cmlfcXVlcnksIHJlZmVyZXIpCiAgd191cmlfcXVlcnlfcmVmZXJlciA8LSB3aGljaChncmVwbCh1cmlfcXVlcnlfZmlsdGVyLCBwYWdldmlld3NEYXRhJHJlZmVyZXIpICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFncmVwbCh1cmlfcXVlcnlfZmlsdGVyLCBwYWdldmlld3NEYXRhJHVyaV9xdWVyeSkpCiAgaWYgKGxlbmd0aCh3X3VyaV9xdWVyeV9yZWZlcmVyKSA+IDApIHsKICAgIHJlZmVyZXJUYWdzIDwtIHN0cnNwbGl0KHBhZ2V2aWV3c0RhdGEkcmVmZXJlclt3X3VyaV9xdWVyeV9yZWZlcmVyXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGxpdCA9ICI/IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVCkKICAgIHJlZmVyZXJUYWdzIDwtIHNhcHBseShyZWZlcmVyVGFncywgZnVuY3Rpb24oeCkge3hbMl19KQogICAgcmVmZXJlclRhZ3MgPC0gcGFzdGUwKCI/IiwgcmVmZXJlclRhZ3MpCiAgICBwYWdldmlld3NEYXRhJHVyaV9xdWVyeVt3X3VyaV9xdWVyeV9yZWZlcmVyXSA8LSByZWZlcmVyVGFncyAKICB9CiAgd191cmlfcXVlcnkgPC0gd2hpY2goZ3JlcGwodXJpX3F1ZXJ5X2ZpbHRlciwgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkpKQogIAogIGlmIChsZW5ndGgod191cmlfcXVlcnkpID4gMCkgewogICAgCiAgICAjIC0gZmlsdGVyIGZvciB3X3VyaV9xdWVyeQogICAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhW3dfdXJpX3F1ZXJ5LCBdIAogICAgCiAgICAjIC0gYWdncmVnYXRlOgogICAgcGFnZXZpZXdzRGF0YSR1cmlfcGF0aCA8LSBwYXN0ZTAocGFnZXZpZXdzRGF0YSR1cmlfaG9zdCwgcGFnZXZpZXdzRGF0YSR1cmlfcGF0aCkKICAgIHBhZ2V2aWV3c0RhdGEkdXJpX2hvc3QgPC0gTlVMTAogICAgcGFnZXZpZXdzRGF0YSRyZWZlcmVyIDwtIE5VTEwKICAgIHBhZ2V2aWV3c0RhdGEgPC0gcGFnZXZpZXdzRGF0YSAlPiUgCiAgICAgIGRwbHlyOjpzZWxlY3QodXJpX3F1ZXJ5LCB1cmlfcGF0aCkgJT4lIAogICAgICBkcGx5cjo6Z3JvdXBfYnkodXJpX3F1ZXJ5LCB1cmlfcGF0aCkgJT4lIAogICAgICBkcGx5cjo6c3VtbWFyaXNlKHBhZ2V2aWV3cyA9IG4oKSkKICAgIGNvbG5hbWVzKHBhZ2V2aWV3c0RhdGEpIDwtIGMoJ1RhZycsICdQYWdlJywgJ1BhZ2V2aWV3cycpCiAgICAKICAgICMgLSBhZGQgY2V0RGF5LCBjYW1wYWlnbk5hbWUKICAgIHBhZ2V2aWV3c0RhdGEkZGF0ZSA8LSBjZXREYXkKICAgIHBhZ2V2aWV3c0RhdGEkY2FtcGFpZ24gPC0gY2FtcGFpZ25OYW1lCiAgICAKICAgICMgLSBzdG9yZToKICAgIHdyaXRlLmNzdihwYWdldmlld3NEYXRhLCAKICAgICAgICAgICAgICBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAKICAgICAgICAgICAgICAgICAgICAgInBhZ2V2aWV3c0FnZ3JlZ2F0ZWRfIiwKICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoCiAgICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoZmlsZU5hbWUsIHNwbGl0ID0gIl8iLCBmaXhlZCA9IFQpW1sxXV1bMl0sCiAgICAgICAgICAgICAgICAgICAgICAgc3BsaXQgPSAiLiIsIAogICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXSwKICAgICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICAgKQogICAgKQogICAgCiAgfQogIAp9CgojIC0gc2V0IHBhcmFtcyB0byB3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzCiMgLSBmb3IgdGhlIFdNREUgMjAyMF9FbWFpbENhbXBhaWduV2lraXBlZGlhQ2hhbGxlbmdlCnVyaV9xdWVyeV9maWx0ZXIgPC0gJ1dNREVfb2NlZGl0b3JzX3NwcmluZ18yMDIwXycKCiMgLSB3cmFuZ2xlIHBhZ2V2aWV3cwp3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzKGZpbGVOYW1lID0gZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpciA9IGRhdGFEaXIsCiAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5X2ZpbHRlciA9IHVyaV9xdWVyeV9maWx0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUgPSBjYW1wYWlnbk5hbWUpCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIEJhbm5lciBBY3Rpb25zCiMjIyAtLS0gdmlhIGV2ZW50LldNREVCYW5uZXJBY3Rpb25zCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMgX19fIE5PVEU6CiMgLSBTdWZmaXggZXhwbGFuYXRpb246CiMgLSBjdHJsIGlzIHRoZSBiYW5uZXIgdGhhdCBkeW5hbWljYWxseSBkaXNwbGF5cyB0ZXh0IGRlcGVuZGluZyBvbiB0aGUgdGFyZ2V0IGdyb3VwCiMgLSB2YXIgaXMgdGhlIGJhbm5lciB0aGF0IHNob3dzIHRoZSBzYW1lIHRleHQgZm9yIGJvdGggdGFyZ2V0IGdyb3VwcwojIC0gaXBhZC9tb2JpbGUgZG9lcyBub3QgcmVwcmVzZW50IHRoZSBhY3R1YWwgZGV2aWNlIHR5cGUsIGJ1dCB0aGUgZGlzcGxheSBtb2RlIG9mIHRoZSBiYW5uZXIgKHNtYWxsIGRlc2t0b3Agc2NyZWVucyBtYXkgYmUgcmVwb3J0ZWQgYXMgaXBhZCkKIyAtIGNzL250IGluZGljYXRlcywgd2hpY2ggdGFyZ2V0IGdyb3VwIHRoZSB1c2VyIGJlbG9uZ3MgdG8KIyAtIEthaSBOaXNzZW4gaW4gaHR0cHM6Ly9waGFicmljYXRvci53aWtpbWVkaWEub3JnL1QyNTE1MzUjNjEzMjQ2OAoKIyAtIHNlbGVjdCBkdCwgZXZlbnQuYmFubmVyTmFtZSwgZXZlbnQuYmFubmVyQWN0aW9uLCBldmVudC5iYW5uZXJJbXByZXNzaW9ucywgZXZlbnQudXNlcklEIAojIC0gZnJvbSBldmVudC53bWRlYmFubmVyaW50ZXJhY3Rpb25zIHdoZXJlIHllYXI9MjAyMCBhbmQgbW9udGg9NSBhbmQgKGRheT0xMSBvciBkYXk9MTIgb3IgZGF5PTEzKTsKCiMgLSBmdW5jdGlvbjogd21kZV9jb2xsZWN0X3BhZ2V2aWV3cwp3bWRlX2Jhbm5lcl9hY3Rpb25zIDwtIGZ1bmN0aW9uKHVyaV9xdWVyeV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmFseXRpY3NEaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lKSB7CiAgCiAgIyAtIE5PVEU6CiAgIyAtIGV4cGVjdGVkIGZvcm1hdCBmb3IgY2V0RGF5IGlzOiBZWVlZLU1NLURECiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIGRhdGV0aW1lX2NvbmRpdGlvbgogIGNldF9jb25kaXRpb24gPC0gc2VxKAogICAgZnJvbSA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDA6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgdG8gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAyMzowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICBieSA9ICJob3VyIgogICkgCiAgYXR0cihjZXRfY29uZGl0aW9uLCAidHpvbmUiKSA8LSAiVVRDIgogIGNldF9jb25kaXRpb24gPC0gYXMuY2hhcmFjdGVyKGNldF9jb25kaXRpb24pCiAgY2V0X2NvbmRpdGlvbiA8LSB1bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKGNldF9jb25kaXRpb24sICJeKFtbOmRpZ2l0Ol1dfFxcc3wtKSoiKSkKICBjZXRfeWVhcnMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMV0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsyXQogICAgfSkKICBjZXRfbW9udGhzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9tb250aHMpCiAgY2V0X2RheXMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bM10KICAgIH0pCiAgY2V0X2RheXMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2RheXMpCiAgY2V0X2hvdXJzIDwtIHNhcHBseShzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgCiAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHhbMl0KICAgICAgICAgICAgICAgICAgICAgIH0pCiAgY2V0X2hvdXJzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9ob3VycykKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZTAoCiAgICAieWVhciA9ICIsIGNldF95ZWFycywgIiBBTkQgIiwKICAgICJtb250aCA9ICIsIGNldF9tb250aHMsICIgQU5EICIsCiAgICAiZGF5ID0gIiwgY2V0X2RheXMsICIgQU5EICIsIAogICAgImhvdXIgPSAiLCBjZXRfaG91cnMKICApCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUoIigiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZUNvbmRpdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAKCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIGV2ZW50QmFubmVyTmFtZV9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9xdWVyeSkgPiAxKSB7CiAgICBldmVudEJhbm5lck5hbWVfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJldmVudC5iYW5uZXJOYW1lIExJS0UgJyUiLCB1cmlfcXVlcnlfZmlsdGVyLCAiJSciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgZXZlbnRCYW5uZXJOYW1lX2NvbmRpdGlvbiA9IHBhc3RlMCgiZXZlbnQuYmFubmVyTmFtZSBMSUtFICclIiwgdXJpX3F1ZXJ5X2ZpbHRlciwgIiUnIikKICB9CiAgCiAgIyAtIGNvbXBvc2UgSGl2ZVFMIHF1ZXJ5CiAgaGl2ZVF1ZXJ5IDwtIHBhc3RlMCggCiAgICAic2VsZWN0IGR0LCBldmVudC5iYW5uZXJOYW1lLCBldmVudC5iYW5uZXJBY3Rpb24sIGV2ZW50LmJhbm5lckltcHJlc3Npb25zLCBldmVudC51c2VySUQgZnJvbSBldmVudC53bWRlYmFubmVyaW50ZXJhY3Rpb25zIAogICAgV0hFUkUgKCIsCiAgICBldmVudEJhbm5lck5hbWVfY29uZGl0aW9uLCAiIEFORCAiLAogICAgIigiLCBkYXRldGltZUNvbmRpdGlvbiwgIikiLAogICAgIik7IgogICAgKQogIAogICMgLSB3cml0ZSBocWwKICB3cml0ZShoaXZlUXVlcnksIHF1ZXJ5RmlsZSkKICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0OgogIGtlcmJlcm9zUHJlZml4IDwtIAogICAgJ3N1ZG8gLXUgYW5hbHl0aWNzLXByaXZhdGVkYXRhIGtlcmJlcm9zLXJ1bi1jb21tYW5kIGFuYWx5dGljcy1wcml2YXRlZGF0YSAnCiAgIyAtIEtlcmJlcm9zIGluaXQKICBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlMChrZXJiZXJvc1ByZWZpeCwgJyBoZGZzIGRmcyAtbHMnKSwgCiAgICAgICAgIHdhaXQgPSBUKQogICMgLSBSdW4gcXVlcnkKICBxdWVyeSA8LSBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlKGtlcmJlcm9zUHJlZml4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcvdXNyL2xvY2FsL2Jpbi9iZWVsaW5lIC0taW5jcmVtZW50YWw9dHJ1ZSAtLXNpbGVudCAtZiAiJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMChkYXRhRGlyLCBxdWVyeUZpbGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIgPiAnLCBkYXRhRGlyLCBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKSwKICAgICAgICAgICAgICAgICAgd2FpdCA9IFRSVUUpCiAgCiAgIyAtIFdyYW5nbGUgQmFubmVyIEludGVyYWN0aW9ucwogICMgLSBsb2FkCiAgYmFubmVyRGF0YSA8LSB0cnlDYXRjaCh7CiAgICBhcy5kYXRhLmZyYW1lKGZyZWFkKHBhc3RlMChkYXRhRGlyLCBmaWxlTmFtZSkpKQogIH0sCiAgZXJyb3IgPSBmdW5jdGlvbihjb25kaXRpb24pIHsKICAgIHJldHVybihGQUxTRSkKICB9KQogIAogICMgLSBwcm9jZXNzCiAgaWYgKGNsYXNzKGJhbm5lckRhdGEpID09ICdsb2dpY2FsJykgewogICAgcmV0dXJuKEZBTFNFKSAKICB9IGVsc2UgeyAKICAgICMgLSBiYW5uZXJTZWVuCiAgICBiYW5uZXJTZWVuIDwtIGJhbm5lckRhdGEgJT4lIAogICAgICBkcGx5cjo6c2VsZWN0KGJhbm5lcm5hbWUsIHVzZXJpZCkKICAgIGJhbm5lclNlZW4gPC0gYmFubmVyU2VlblshZHVwbGljYXRlZChiYW5uZXJTZWVuKSwgXQogICAgYmFubmVyU2VlbiA8LSBiYW5uZXJTZWVuICU+JSAKICAgICAgZHBseXI6OnNlbGVjdChiYW5uZXJuYW1lKSAlPiUgCiAgICAgIGRwbHlyOjpncm91cF9ieShiYW5uZXJuYW1lKSAlPiUgCiAgICAgIGRwbHlyOjpzdW1tYXJpc2Uoc2Vlbl9ieSA9IG4oKSkKICAgICMgLSBiYW5uZXJDbG9zZWQKICAgIGJhbm5lckNsb3NlZCA8LSBiYW5uZXJEYXRhICU+JSAKICAgICAgZHBseXI6OmZpbHRlcihiYW5uZXJhY3Rpb24gPT0gImJhbm5lci1jbG9zZWQiKSAlPiUgCiAgICAgIGRwbHlyOjpzZWxlY3QoYmFubmVybmFtZSwgYmFubmVyaW1wcmVzc2lvbnMpICU+JSAKICAgICAgZHBseXI6Omdyb3VwX2J5KGJhbm5lcm5hbWUpICU+JQogICAgICBkcGx5cjo6c3VtbWFyaXNlKGNsb3NlZF9ieSA9IG4oKSwgbWVhbl9jbG9zZV9pbXAgPSByb3VuZChtZWFuKGJhbm5lcmltcHJlc3Npb25zKSwgMikpCiAgICAjIC0gYmFubmVyQ2xpY2tlZAogICAgYmFubmVyQ2xpY2tlZCA8LSBiYW5uZXJEYXRhICU+JSAKICAgICAgZHBseXI6OmZpbHRlcihiYW5uZXJhY3Rpb24gPT0gImJhbm5lci1jbGlja2VkIikgJT4lIAogICAgICBkcGx5cjo6c2VsZWN0KGJhbm5lcm5hbWUsIGJhbm5lcmltcHJlc3Npb25zKSAlPiUgCiAgICAgIGRwbHlyOjpncm91cF9ieShiYW5uZXJuYW1lKSAlPiUKICAgICAgZHBseXI6OnN1bW1hcmlzZShjbGlja2VkX2J5ID0gbigpLCBtZWFuX2NsaWNrX2ltcCA9IHJvdW5kKG1lYW4oYmFubmVyaW1wcmVzc2lvbnMpLCAyKSkKICAgICMgLSB3aG9DbGlja2VkCiAgICB3aG9DbGlja2VkIDwtIGJhbm5lckRhdGEgJT4lIAogICAgICBkcGx5cjo6ZmlsdGVyKGJhbm5lcmFjdGlvbiA9PSAiYmFubmVyLWNsaWNrZWQiKQogICAgd2hvQ2xpY2tlZCA8LSBkYXRhLmZyYW1lKHVzZXJpZCA9IHVuaXF1ZSh3aG9DbGlja2VkJHVzZXJpZCkpCiAgICAjIC0gc3RvcmU6CiAgICB3cml0ZS5jc3Yod2hvQ2xpY2tlZCwgCiAgICAgICAgICAgICAgcGFzdGUwKGFuYWx5dGljc0RpciwgCiAgICAgICAgICAgICAgICAgICAgICJ3aG9DbGlja2VkXyIsCiAgICAgICAgICAgICAgICAgICAgIHN0cnNwbGl0KAogICAgICAgICAgICAgICAgICAgICAgIHN0cnNwbGl0KGZpbGVOYW1lLCBzcGxpdCA9ICJfIiwgZml4ZWQgPSBUKVtbMV1dWzJdLAogICAgICAgICAgICAgICAgICAgICAgIHNwbGl0ID0gIi4iLCAKICAgICAgICAgICAgICAgICAgICAgICBmaXhlZCA9IFQpW1sxXV1bMV0sCiAgICAgICAgICAgICAgICAgICAgICIuY3N2IgogICAgICAgICAgICAgICkKICAgICkKICAgICMgLSBqb2luCiAgICBiYW5uZXJEYXRhIDwtIGJhbm5lclNlZW4gJT4lIAogICAgICBkcGx5cjo6bGVmdF9qb2luKGJhbm5lckNsb3NlZCwgJ2Jhbm5lcm5hbWUnKSAlPiUgCiAgICAgIGRwbHlyOjpsZWZ0X2pvaW4oYmFubmVyQ2xpY2tlZCwgJ2Jhbm5lcm5hbWUnKQogICAgYmFubmVyRGF0YSRjbG9zZV9yYXRlIDwtIHJvdW5kKGJhbm5lckRhdGEkY2xvc2VkX2J5L2Jhbm5lckRhdGEkc2Vlbl9ieSwgMikKICAgIGJhbm5lckRhdGEkY2xpY2tfcmF0ZSA8LSByb3VuZChiYW5uZXJEYXRhJGNsaWNrZWRfYnkvYmFubmVyRGF0YSRzZWVuX2J5LCAyKQogICAgIyAtIGRhdGUsIGNhbXBhaWduCiAgICBiYW5uZXJEYXRhJGRheSA8LSBjZXREYXkKICAgIGJhbm5lckRhdGEkY2FtcGFpZ24gPC0gY2FtcGFpZ25OYW1lCiAgICAKICAgICMgLSBzdG9yZToKICAgIHdyaXRlLmNzdihiYW5uZXJEYXRhLCAKICAgICAgICAgICAgICBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAKICAgICAgICAgICAgICAgICAgICAgImJhbm5lckludGVyYWN0aW9uc0FnZ3JlZ2F0ZWRfIiwKICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoCiAgICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoZmlsZU5hbWUsIHNwbGl0ID0gIl8iLCBmaXhlZCA9IFQpW1sxXV1bMl0sCiAgICAgICAgICAgICAgICAgICAgICAgc3BsaXQgPSAiLiIsIAogICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXSwKICAgICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICAgKQogICAgKQogIH0KCn0KCiMgLSBzZXQgcGFyYW1zIGZvciB3bWRlX2Jhbm5lcl9hY3Rpb25zKCkKcXVlcnlGaWxlIDwtIHBhc3RlMChjYW1wYWlnbk5hbWUsICJfYmFubmVySW50ZXJhY3Rpb25zLmhxbCIpCmZpbGVOYW1lIDwtIHBhc3RlMCgiYmFubmVySW50ZXJhY3Rpb25zXyIsIGNldERheSwgIi50c3YiKQp1cmlfcXVlcnlfZmlsdGVyIDwtICdXTURFX29jZWRpdG9yc19zcHJpbmdfMjAyMF8nCmJhbm5lcl9zdGF0dXMgPC0gd21kZV9iYW5uZXJfYWN0aW9ucyh1cmlfcXVlcnlfZmlsdGVyID0gdXJpX3F1ZXJ5X2ZpbHRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSA9IHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lID0gZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmFseXRpY3NEaXIgPSBhbmFseXRpY3NEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lID0gY2FtcGFpZ25OYW1lKQpgYGAKCgoKIyMgMS4gQ2FtcGFpZ24gQmFubmVycwoKVGhpcyBzZWN0aW9uIHByZXNlbnRzIGFsbCBkYXRhIGFuZCBzdGF0aXN0aWNzIG9uIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgpUaGUgZm9sbG93aW5nIGNodW5rIGxvYWRzIGFuZCB0aGVuIHJlLXN0cnVjdHVyZXMgdGhlIGRhdGFzZXQgYSBiaXQ6CgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KbEYgPC0gbGlzdC5maWxlcygnX2FuYWx5dGljcycpCmxGIDwtIGxGW2dyZXBsKCJeYmFubmVySW50ZXJhY3Rpb25zIiwgbEYpXQpkYXRhU2V0IDwtIGxhcHBseShwYXN0ZTAoIl9hbmFseXRpY3MvIiwgbEYpLCBmcmVhZCkKZGF0YVNldCA8LSByYmluZGxpc3QoZGF0YVNldCkKZGF0YVNldCRWMSA8LSBOVUxMCmRhdGFTZXQkY2FtcGFpZ24gPC0gTlVMTAojIC0gTkFzIHRvIHplcm8KZGF0YVNldFt3aGljaChpcy5uYShkYXRhU2V0KSwgYXJyLmluZCA9IFQpXSA8LSAwCiMgLSBiYW5uZXIgY29kZXMKZGF0YVNldCR2YXJfY3RybCA8LSBzYXBwbHkoZGF0YVNldCRiYW5uZXJuYW1lLCBmdW5jdGlvbih4KSB7CiAgaWYgKGdyZXBsKCJ2YXIiLCB4KSkge3JldHVybigidmFyIil9IGVsc2Uge3JldHVybigiY3RybCIpfQp9KQpkYXRhU2V0JGRldmljZSA8LSBzYXBwbHkoZGF0YVNldCRiYW5uZXJuYW1lLCBmdW5jdGlvbih4KSB7CiAgaWYgKGdyZXBsKCJtb2JpbGUiLCB4KSkgewogICAgcmV0dXJuKCJtb2JpbGUiKQogIH0gZWxzZSBpZiAoZ3JlcGwoImlwYWQiLCB4KSkgewogICAgICByZXR1cm4oImlwYWQiKQogIH0gZWxzZSB7CiAgICAgIHJldHVybigiZGVza3RvcCIpCiAgfQp9KQpkYXRhU2V0JGNzX250IDwtIHNhcHBseShkYXRhU2V0JGJhbm5lcm5hbWUsIGZ1bmN0aW9uKHgpIHsKICBpZiAoZ3JlcGwoImNzIiwgeCkpIHtyZXR1cm4oImNzIil9IGVsc2Uge3JldHVybigibnQiKX0KfSkKYGBgCgojIyMgMS4xIEJhbm5lciBBY3Rpb25zIE92ZXJ2aWV3CgoqKkNoYXJ0IDEuMS4xQSoqIERhaWx5IEJhbm5lciBJbXByZXNzaW9ucywgQ2xpY2tzLCBhbmQgQ2xvc3VyZXMsIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRheSwgc2Vlbl9ieSwgY2xvc2VkX2J5LCBjbGlja2VkX2J5KSAlPiUgCiAgZ3JvdXBfYnkoZGF5KSAlPiUgCiAgc3VtbWFyaXNlKHNlZW5fYnkgPSBzdW0oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnkgPSBzdW0oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnkgPSBzdW0oY2xpY2tlZF9ieSkpCnBGcmFtZSRjbG9zZV9yYXRlID0gcm91bmQocEZyYW1lJGNsb3NlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lJGNsaWNrX3JhdGUgPSByb3VuZChwRnJhbWUkY2xpY2tlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXkpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRhaWx5OgpwRiA8LSBwRnJhbWUgJT4lIAogIHNlbGVjdChkYXksIHNlZW5fYnksIGNsb3NlZF9ieSwgY2xpY2tlZF9ieSkgJT4lIAogIHBpdm90X2xvbmdlcihjKCdzZWVuX2J5JywgJ2Nsb3NlZF9ieScsICdjbGlja2VkX2J5JyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBY3Rpb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlZhbHVlIikKZ2dwbG90KHBGLCBhZXMoeCA9IGRheSwKICAgICAgICAgICAgICAgeSA9IFZhbHVlLAogICAgICAgICAgICAgICBncm91cCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJBY3Rpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipDaGFydCAxLjEuMUIqKiBUb3RhbCwgTWVhbiBwZXIgZGF5LCBhbmQgTWVkaWFuIHBlciBkYXkgQmFubmVyIEltcHJlc3Npb25zLCBDbGlja3MsIGFuZCBDbG9zdXJlcywgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CnBGcmFtZVRvdGFsIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChkYXksIHNlZW5fYnksIGNsb3NlZF9ieSwgY2xpY2tlZF9ieSkgJT4lIAogIGdyb3VwX2J5KGRheSkgJT4lIAogIHN1bW1hcmlzZShzZWVuX2J5ID0gc3VtKHNlZW5fYnkpLCAKICAgICAgICAgICAgY2xvc2VkX2J5ID0gc3VtKGNsb3NlZF9ieSksIAogICAgICAgICAgICBjbGlja2VkX2J5ID0gc3VtKGNsaWNrZWRfYnkpCiAgICAgICAgICAgICkgCnBGcmFtZVRvdGFsIDwtIHBGcmFtZVRvdGFsICU+JSAKICBzdW1tYXJpc2Uoc2Vlbl9ieV9fdG90YWxfaW5fY2FtcGFpZ24gPSBzdW0oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnlfX3RvdGFsX2luX2NhbXBhaWduID0gc3VtKGNsb3NlZF9ieSksIAogICAgICAgICAgICBjbGlja2VkX2J5X190b3RhbF9pbl9jYW1wYWlnbiA9IHN1bShjbGlja2VkX2J5KSwKICAgICAgICAgICAgc2Vlbl9ieV9fbWVhbl9wZXJfZGF5ID0gbWVhbihzZWVuX2J5KSwgCiAgICAgICAgICAgIGNsb3NlZF9ieV9fbWVhbl9wZXJfZGF5ID0gbWVhbihjbG9zZWRfYnkpLCAKICAgICAgICAgICAgY2xpY2tlZF9ieV9fbWVhbl9wZXJfZGF5ID0gbWVhbihjbGlja2VkX2J5KSwKICAgICAgICAgICAgc2Vlbl9ieV9fbWVkaWFuX3Blcl9kYXkgPSBtZWRpYW4oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnlfX21lZGlhbl9wZXJfZGF5ID0gbWVkaWFuKGNsb3NlZF9ieSksIAogICAgICAgICAgICBjbGlja2VkX2J5X19tZWRpYW5fcGVyX2RheSA9IG1lZGlhbihjbGlja2VkX2J5KQogICAgICAgICAgICApICU+JSAKICB0KCkKcEZyYW1lVG90YWxbLCAxXSA8LSByb3VuZChwRnJhbWVUb3RhbFssIDFdLCAyKQpwRnJhbWVUb3RhbCA8LSBhcy5kYXRhLmZyYW1lKHBGcmFtZVRvdGFsKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKCdNZWFzdXJlJykKY29sbmFtZXMocEZyYW1lVG90YWwpWzJdIDwtICdWYWx1ZScKcEZyYW1lVG90YWwgPC1wRnJhbWVUb3RhbCAlPiUgCiAgc2VwYXJhdGUoJ01lYXN1cmUnLCAKICAgICAgICAgICBzZXAgPSAiX18iLCAKICAgICAgICAgICBpbnRvID0gYygnQWN0aW9uJywgJ1N0YXRpc3RpYycpKQojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfTogc2Vlbl9ieSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5LCBkYWlseToKZ2dwbG90KHBGcmFtZVRvdGFsLCBhZXMoeCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGxvZyhWYWx1ZSksCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gU3RhdGlzdGljLAogICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IFN0YXRpc3RpYywKICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBWYWx1ZSwKICAgICAgICAgICAgICAgICAgICApKSArCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArICAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJBY3Rpb24iKSArIHlsYWIoImxvZyhWYWx1ZSkiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCgoqKkNoYXJ0IDEuMS4yKiogRGFpbHkgQmFubmVyIENsaWNrIGFuZCBDbG9zZSByYXRlcywgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBjbG9zZV9yYXRlLCBjbGlja19yYXRlLCBkYWlseToKcEYgPC0gcEZyYW1lICU+JSAKICBzZWxlY3QoZGF5LCBjbGlja19yYXRlLCBjbG9zZV9yYXRlKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ2NsaWNrX3JhdGUnLCAnY2xvc2VfcmF0ZScpLCAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiQWN0aW9uIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJWYWx1ZSIpCmdncGxvdChwRiwgYWVzKHggPSBkYXksCiAgICAgICAgICAgICAgIHkgPSBWYWx1ZSwKICAgICAgICAgICAgICAgZ3JvdXAgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGNvbG9yID0gQWN0aW9uLAogICAgICAgICAgICAgICBmaWxsID0gQWN0aW9uLAogICAgICAgICAgICAgICBsYWJlbCA9IFZhbHVlLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiQWN0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArIAogIHlsaW0oYygwLCAxKSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipUYWJsZSAxLjEuMioqIE1lYW4gYW5kIE1lZGlhbiBDbGljayBhbmQgQ2xvc2UgcmF0ZXMgKGFjcm9zcyBkYXlzKQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnRGcmFtZSA8LSBkYXRhLmZyYW1lKGNsaWNrX3JhdGVfbWVhbiA9IHJvdW5kKG1lYW4ocEZyYW1lJGNsaWNrX3JhdGUpLCA1KSwgCiAgICAgICAgICAgICAgICAgICAgIGNsaWNrX3JhdGVfbWVkaWFuID0gcm91bmQobWVkaWFuKHBGcmFtZSRjbGlja19yYXRlKSwgNSksCiAgICAgICAgICAgICAgICAgICAgIGNsb3NlX3JhdGVfbWVhbiA9IHJvdW5kKG1lYW4ocEZyYW1lJGNsb3NlX3JhdGUpLCA1KSwKICAgICAgICAgICAgICAgICAgICAgY2xvc2VfcmF0ZV9tZWRpYW4gPSByb3VuZChtZWRpYW4ocEZyYW1lJGNsb3NlX3JhdGUpLCA1KQopCmRhdGF0YWJsZSh0RnJhbWUpCmBgYAoKKipDaGFydCAxLjEuMyoqIERhaWx5OiBIb3cgTWFueSBJbXByZXNzaW9ucyB0byBBY3Rpb24gKENsaWNrLCBDbG9zZSksIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRheSwgbWVhbl9jbGlja19pbXAsIG1lYW5fY2xvc2VfaW1wKSAlPiUgCiAgZ3JvdXBfYnkoZGF5KSAlPiUgCiAgc3VtbWFyaXNlKG1lYW5fY2xpY2tfaW1wID0gcm91bmQobWVhbihtZWFuX2NsaWNrX2ltcCksIDIpLCAKICAgICAgICAgICAgbWVhbl9jbG9zZV9pbXAgPSByb3VuZChtZWFuKG1lYW5fY2xvc2VfaW1wKSwgMikpICU+JSAKICBhcnJhbmdlKGRheSkKY29sbmFtZXMocEZyYW1lKSA8LSBjKCdkYXknLCAnY2xpY2snLCAnY2xvc2UnKQojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfTogY2xpY2ssIGNsb3NlLCBkYWlseToKcEYgPC0gcEZyYW1lICU+JSAKICBwaXZvdF9sb25nZXIoYygnY2xpY2snLCAnY2xvc2UnKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkFjdGlvbiIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiQXZlcmFnZSBJbXByZXNzaW9ucyIpCmdncGxvdChwRiwgYWVzKHggPSBkYXksCiAgICAgICAgICAgICAgIHkgPSBgQXZlcmFnZSBJbXByZXNzaW9uc2AsCiAgICAgICAgICAgICAgIGdyb3VwID0gQWN0aW9uLAogICAgICAgICAgICAgICBjb2xvciA9IEFjdGlvbiwKICAgICAgICAgICAgICAgZmlsbCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgbGFiZWwgPSBgQXZlcmFnZSBJbXByZXNzaW9uc2AsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJBdmcuIEltcHJlc3Npb25zIGJlZm9yZSBBY3Rpb24iKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgoqKlRhYmxlIDEuMS4zKiogTWVhbiBpbXByZXNzaW9ucyBiZWZvcmUgQ2xpY2sgYW5kIENsb3NlIGFjdGlvbnMgKGFjcm9zcyBkYXlzKQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnRGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QobWVhbl9jbGlja19pbXAsIG1lYW5fY2xvc2VfaW1wKSAlPiUgCiAgc3VtbWFyaXNlKG1lYW5fY2xpY2tfaW1wID0gcm91bmQobWVhbihtZWFuX2NsaWNrX2ltcCksIDIpLCAKICAgICAgICAgICAgbWVhbl9jbG9zZV9pbXAgPSByb3VuZChtZWFuKG1lYW5fY2xvc2VfaW1wKSwgMikpCmRhdGF0YWJsZSh0RnJhbWUpCmBgYAoKIyMjIDEuMiBCYW5uZXIgQWN0aW9uczogRGV2aWNlcwoKKipSZW1pbmRlci4qKiBGcm9tIHRoZSBbQ2FtcGFpZ24gVHJhY2tpbmcgQ29uY2VwdF0oaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vZG9jdW1lbnQvZC8xN2s3Z3FEaHJ4Q0RwU1R0a2dZX180Qk8wUXk1R0staWJEZDFjb3RhU1otRS9lZGl0Iyk6Cgo+IGlwYWQvbW9iaWxlIGRvZXMgbm90IHJlcHJlc2VudCB0aGUgYWN0dWFsIGRldmljZSB0eXBlLCBidXQgdGhlIGRpc3BsYXkgbW9kZSBvZiB0aGUgYmFubmVyIChzbWFsbCBkZXNrdG9wIHNjcmVlbnMgbWF5IGJlIHJlcG9ydGVkIGFzIGlwYWQpCgoKKipDaGFydCAxLjIuMUEqKiBEYWlseSBCYW5uZXIgQ2xpY2sgYW5kIENsb3NlIHJhdGVzLCBieSAqKmRldmljZXMqKiwgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDEwfQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRheSwgc2Vlbl9ieSwgY2xvc2VkX2J5LCBjbGlja2VkX2J5LCBkZXZpY2UpICU+JSAKICBncm91cF9ieShkYXksIGRldmljZSkgJT4lIAogIHN1bW1hcmlzZShzZWVuX2J5ID0gc3VtKHNlZW5fYnkpLCAKICAgICAgICAgICAgY2xvc2VkX2J5ID0gc3VtKGNsb3NlZF9ieSksIAogICAgICAgICAgICBjbGlja2VkX2J5ID0gc3VtKGNsaWNrZWRfYnkpKQpwRnJhbWUkY2xvc2VfcmF0ZSA9IHJvdW5kKHBGcmFtZSRjbG9zZWRfYnkvcEZyYW1lJHNlZW5fYnksIDIpCnBGcmFtZSRjbGlja19yYXRlID0gcm91bmQocEZyYW1lJGNsaWNrZWRfYnkvcEZyYW1lJHNlZW5fYnksIDIpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZGF5KQojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfTogc2Vlbl9ieSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5LCBkYWlseToKcEYgPC0gcEZyYW1lICU+JSAKICBzZWxlY3QoZGF5LCBkZXZpY2UsIHNlZW5fYnksIGNsb3NlZF9ieSwgY2xpY2tlZF9ieSkgJT4lIAogIHBpdm90X2xvbmdlcihjKCdzZWVuX2J5JywgJ2Nsb3NlZF9ieScsICdjbGlja2VkX2J5JyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBY3Rpb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlZhbHVlIikKZ2dwbG90KHBGLCBhZXMoeCA9IGRheSwKICAgICAgICAgICAgICAgeSA9IFZhbHVlLAogICAgICAgICAgICAgICBncm91cCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBmYWNldF93cmFwKH5kZXZpY2UsIG5yb3cgPSAzLCBzY2FsZXMgPSAiZnJlZSIpICsgCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiQWN0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCioqVGFibGUgMS4yLjIqKiBNZWFuIGFuZCBNZWRpYW4gQ2xpY2sgYW5kIENsb3NlIHJhdGVzIChhY3Jvc3MgZGF5cykKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQp0RnJhbWUgPC0gcEYgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb209ICdBY3Rpb24nLCAKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9ICdWYWx1ZScpCnRGcmFtZSRjbGlja19yYXRlIDwtIHRGcmFtZSRjbGlja2VkX2J5L3RGcmFtZSRzZWVuX2J5CnRGcmFtZSRjbG9zZV9yYXRlIDwtIHRGcmFtZSRjbG9zZWRfYnkvdEZyYW1lJHNlZW5fYnkKdEZyYW1lIDwtIHRGcmFtZSAlPiUgCiAgc2VsZWN0KGRldmljZSwgY2xpY2tfcmF0ZSwgY2xvc2VfcmF0ZSkgJT4lIAogIGdyb3VwX2J5KGRldmljZSkgJT4lIAogIHN1bW1hcmlzZShjbGlja19yYXRlX21lYW4gPSByb3VuZChtZWFuKGNsaWNrX3JhdGUpLCA1KSwKICAgICAgICAgICAgY2xpY2tfcmF0ZV9tZWRpYW4gPSByb3VuZChtZWRpYW4oY2xpY2tfcmF0ZSksIDUpLAogICAgICAgICAgICBjbG9zZV9yYXRlX21lYW4gPSByb3VuZChtZWFuKGNsb3NlX3JhdGUpLCA1KSwKICAgICAgICAgICAgY2xvc2VfcmF0ZV9tZWRpYW4gPSByb3VuZChtZWRpYW4oY2xvc2VfcmF0ZSksIDUpCiAgKQpkYXRhdGFibGUodEZyYW1lKQpgYGAKCgoqKkNoYXJ0IDEuMi4xQioqIERhaWx5IEJhbm5lciBDbGljayBhbmQgQ2xvc2UgcmF0ZXMsIGJ5ICoqZGV2aWNlcyoqLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNX0KcEZUb3RhbCA8LSBwRiAlPiUgCiAgc2VsZWN0KGRldmljZSwgQWN0aW9uLCBWYWx1ZSkgJT4lIAogIGdyb3VwX2J5KGRldmljZSwgQWN0aW9uKSAlPiUgCiAgc3VtbWFyaXNlKHRvdGFsID0gc3VtKFZhbHVlKSwgCiAgICAgICAgICAgIG1lYW4gPSByb3VuZChtZWFuKFZhbHVlKSwgMiksCiAgICAgICAgICAgIG1lZGlhbiA9IHJvdW5kKG1lZGlhbihWYWx1ZSksIDIpCiAgICAgICAgICAgICkgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gYygndG90YWwnLCAnbWVhbicsICdtZWRpYW4nKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiTWVhc3VyZSIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJWYWx1ZSIpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRhaWx5OgpnZ3Bsb3QocEZUb3RhbCwgYWVzKHggPSBBY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgeSA9IGxvZyhWYWx1ZSksCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBNZWFzdXJlLAogICAgICAgICAgICAgICAgICAgIGdyb3VwID0gTWVhc3VyZSwKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFZhbHVlLAogICAgICAgICAgICAgICAgICAgICkpICsKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsgIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIGdndGl0bGUoJzIwMjAgV01ERSBPY2Nhc2lvbmFsIEVkaXRvcnMgQmFubmVyIENhbXBhaWduJykgKwogIHhsYWIoIkFjdGlvbiIpICsgeWxhYigibG9nKFZhbHVlKSIpICsgCiAgZmFjZXRfd3JhcCh+IGRldmljZSkgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipDaGFydCAxLjIuMioqIERhaWx5OiBNZWFuIEltcHJlc3Npb25zIHRvIEFjdGlvbiAoQ2xpY2ssIENsb3NlKSwgYnkgKipkZXZpY2VzKiosIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSAxMH0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChkYXksIGRldmljZSwgbWVhbl9jbGlja19pbXAsIG1lYW5fY2xvc2VfaW1wKSAlPiUgCiAgZ3JvdXBfYnkoZGF5LCBkZXZpY2UpICU+JSAKICBzdW1tYXJpc2UobWVhbl9jbGlja19pbXAgPSByb3VuZChtZWFuKG1lYW5fY2xpY2tfaW1wKSwgMiksIAogICAgICAgICAgICBtZWFuX2Nsb3NlX2ltcCA9IHJvdW5kKG1lYW4obWVhbl9jbG9zZV9pbXApLCAyKSkgJT4lIAogIGFycmFuZ2UoZGF5KQpjb2xuYW1lcyhwRnJhbWUpIDwtIGMoJ2RheScsICdkZXZpY2UnLCAnY2xpY2snLCAnY2xvc2UnKQojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfTogY2xpY2ssIGNsb3NlLCBkYWlseToKcEYgPC0gcEZyYW1lICU+JSAKICBwaXZvdF9sb25nZXIoYygnY2xpY2snLCAnY2xvc2UnKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkFjdGlvbiIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiQXZlcmFnZSBJbXByZXNzaW9ucyIpCmdncGxvdChwRiwgYWVzKHggPSBkYXksCiAgICAgICAgICAgICAgIHkgPSBgQXZlcmFnZSBJbXByZXNzaW9uc2AsCiAgICAgICAgICAgICAgIGdyb3VwID0gQWN0aW9uLAogICAgICAgICAgICAgICBjb2xvciA9IEFjdGlvbiwKICAgICAgICAgICAgICAgZmlsbCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgbGFiZWwgPSBgQXZlcmFnZSBJbXByZXNzaW9uc2AsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBmYWNldF93cmFwKH5kZXZpY2UsIG5yb3cgPSAzLCBzY2FsZXMgPSAiZnJlZSIpICsKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiQXZnLiBJbXByZXNzaW9ucyBiZWZvcmUgQWN0aW9uIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipUYWJsZSAxLjIuMioqIE1lYW4gSW1wcmVzc2lvbnMgdG8gQWN0aW9uIChDbGljaywgQ2xvc2UpLCBieSAqKmRldmljZXMqKiwgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMgYW5kIGNhbXBhaWduIGRheXMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KGRldmljZSwgY2xpY2ssIGNsb3NlKSAlPiUgCiAgZ3JvdXBfYnkoZGV2aWNlKSAlPiUgCiAgc3VtbWFyaXNlKG1lYW5fY2xpY2sgPSByb3VuZChtZWFuKGNsaWNrKSwgMiksCiAgICAgICAgICAgIG1lYW5fY2xvc2UgPSByb3VuZChtZWFuKGNsb3NlKSwgMikKICApCmRhdGF0YWJsZSh0RnJhbWUpCmBgYAoKCiMjIyAxLjMgQmFubmVyIEFjdGlvbnM6IFZhciB2cy4gQ3RybAoKKipSZW1pbmRlci4qKiBGcm9tIHRoZSBbQ2FtcGFpZ24gVHJhY2tpbmcgQ29uY2VwdF0oaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vZG9jdW1lbnQvZC8xN2s3Z3FEaHJ4Q0RwU1R0a2dZX180Qk8wUXk1R0staWJEZDFjb3RhU1otRS9lZGl0Iyk6Cgo+IGN0cmwgaXMgdGhlIGJhbm5lciB0aGF0IGR5bmFtaWNhbGx5IGRpc3BsYXlzIHRleHQgZGVwZW5kaW5nIG9uIHRoZSB0YXJnZXQgZ3JvdXAKCj4gdmFyIGlzIHRoZSBiYW5uZXIgdGhhdCBzaG93cyB0aGUgc2FtZSB0ZXh0IGZvciBib3RoIHRhcmdldCBncm91cHMKCioqQ2hhcnQgMS4zLjFBKiogRGFpbHkgQmFubmVyIENsaWNrIGFuZCBDbG9zZSByYXRlcywgYnkgKip2YXIvY3RybCoqLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRheSwgc2Vlbl9ieSwgY2xvc2VkX2J5LCBjbGlja2VkX2J5LCB2YXJfY3RybCkgJT4lIAogIGdyb3VwX2J5KGRheSwgdmFyX2N0cmwpICU+JSAKICBzdW1tYXJpc2Uoc2Vlbl9ieSA9IHN1bShzZWVuX2J5KSwgCiAgICAgICAgICAgIGNsb3NlZF9ieSA9IHN1bShjbG9zZWRfYnkpLCAKICAgICAgICAgICAgY2xpY2tlZF9ieSA9IHN1bShjbGlja2VkX2J5KSkKcEZyYW1lJGNsb3NlX3JhdGUgPSByb3VuZChwRnJhbWUkY2xvc2VkX2J5L3BGcmFtZSRzZWVuX2J5LCAyKQpwRnJhbWUkY2xpY2tfcmF0ZSA9IHJvdW5kKHBGcmFtZSRjbGlja2VkX2J5L3BGcmFtZSRzZWVuX2J5LCAyKQpwRnJhbWUgPC0gYXJyYW5nZShwRnJhbWUsIGRheSkKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn06IHNlZW5fYnksIGNsaWNrZWRfYnksIGNsb3NlZF9ieSwgZGFpbHk6CnBGIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KGRheSwgdmFyX2N0cmwsIHNlZW5fYnksIGNsb3NlZF9ieSwgY2xpY2tlZF9ieSkgJT4lIAogIHBpdm90X2xvbmdlcihjKCdzZWVuX2J5JywgJ2Nsb3NlZF9ieScsICdjbGlja2VkX2J5JyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBY3Rpb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlZhbHVlIikKZ2dwbG90KHBGLCBhZXMoeCA9IGRheSwKICAgICAgICAgICAgICAgeSA9IFZhbHVlLAogICAgICAgICAgICAgICBncm91cCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBmYWNldF93cmFwKH52YXJfY3RybCwgbnJvdyA9IDIsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJBY3Rpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipUYWJsZSAxLjMuMioqIE1lYW4gYW5kIE1lZGlhbiBDbGljayBhbmQgQ2xvc2UgcmF0ZXMgKGFjcm9zcyBkYXlzKQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnRGcmFtZSA8LSBwRiAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICdBY3Rpb24nLCAKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9ICdWYWx1ZScpCnRGcmFtZSRjbGlja19yYXRlIDwtIHRGcmFtZSRjbGlja2VkX2J5L3RGcmFtZSRzZWVuX2J5CnRGcmFtZSRjbG9zZV9yYXRlIDwtIHRGcmFtZSRjbG9zZWRfYnkvdEZyYW1lJHNlZW5fYnkKdEZyYW1lIDwtIHRGcmFtZSAlPiUgCiAgc2VsZWN0KHZhcl9jdHJsLCBjbGlja19yYXRlLCBjbG9zZV9yYXRlKSAlPiUgCiAgZ3JvdXBfYnkodmFyX2N0cmwpICU+JSAKICBzdW1tYXJpc2UoY2xpY2tfcmF0ZV9tZWFuID0gcm91bmQobWVhbihjbGlja19yYXRlKSwgNSksCiAgICAgICAgICAgIGNsaWNrX3JhdGVfbWVkaWFuID0gcm91bmQobWVkaWFuKGNsaWNrX3JhdGUpLCA1KSwKICAgICAgICAgICAgY2xvc2VfcmF0ZV9tZWFuID0gcm91bmQobWVhbihjbG9zZV9yYXRlKSwgNSksCiAgICAgICAgICAgIGNsb3NlX3JhdGVfbWVkaWFuID0gcm91bmQobWVkaWFuKGNsb3NlX3JhdGUpLCA1KQogICkKZGF0YXRhYmxlKHRGcmFtZSkKYGBgCgoqKkNoYXJ0IDEuMy4xQioqIFRvdGFsLCBtZWFuLCBhbmQgbWVkaWFuIGRhaWx5IEJhbm5lciBDbGljayBhbmQgQ2xvc2UgcmF0ZXMsIGJ5ICoqdmFyL2N0cmwqKiwgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CnBGVG90YWwgPC0gcEYgJT4lIAogIHNlbGVjdCh2YXJfY3RybCwgQWN0aW9uLCBWYWx1ZSkgJT4lIAogIGdyb3VwX2J5KHZhcl9jdHJsLCBBY3Rpb24pICU+JSAKICBzdW1tYXJpc2UodG90YWwgPSBzdW0oVmFsdWUpLCAKICAgICAgICAgICAgbWVhbiA9IHJvdW5kKG1lYW4oVmFsdWUpLCAyKSwKICAgICAgICAgICAgbWVkaWFuID0gcm91bmQobWVkaWFuKFZhbHVlKSwgMikKICAgICAgICAgICAgKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKCd0b3RhbCcsICdtZWFuJywgJ21lZGlhbicpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJNZWFzdXJlIiwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlZhbHVlIikKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn06IHNlZW5fYnksIGNsaWNrZWRfYnksIGNsb3NlZF9ieSwgZGFpbHk6CmdncGxvdChwRlRvdGFsLCBhZXMoeCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgICAgICB5ID0gbG9nKFZhbHVlKSwKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IE1lYXN1cmUsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBNZWFzdXJlLAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKwogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKyAgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiQWN0aW9uIikgKyB5bGFiKCJsb2coVmFsdWUpIikgKyAKICBmYWNldF93cmFwKH4gdmFyX2N0cmwpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCioqQ2hhcnQgMS4zLjIqKiBEYWlseTogSG93IE1lYW4gSW1wcmVzc2lvbnMgdG8gQWN0aW9uIChDbGljaywgQ2xvc2UpLCBieSAqKnZhci9jdHJsKiosIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF5LCB2YXJfY3RybCwgbWVhbl9jbGlja19pbXAsIG1lYW5fY2xvc2VfaW1wKSAlPiUgCiAgZ3JvdXBfYnkoZGF5LCB2YXJfY3RybCkgJT4lIAogIHN1bW1hcmlzZShtZWFuX2NsaWNrX2ltcCA9IHJvdW5kKG1lYW4obWVhbl9jbGlja19pbXApLCAyKSwgCiAgICAgICAgICAgIG1lYW5fY2xvc2VfaW1wID0gcm91bmQobWVhbihtZWFuX2Nsb3NlX2ltcCksIDIpKSAlPiUgCiAgYXJyYW5nZShkYXkpCmNvbG5hbWVzKHBGcmFtZSkgPC0gYygnZGF5JywgJ3Zhcl9jdHJsJywgJ2NsaWNrJywgJ2Nsb3NlJykKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn06IGNsaWNrLCBjbG9zZSwgZGFpbHk6CnBGIDwtIHBGcmFtZSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ2NsaWNrJywgJ2Nsb3NlJyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBY3Rpb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIkF2ZXJhZ2UgSW1wcmVzc2lvbnMiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gYEF2ZXJhZ2UgSW1wcmVzc2lvbnNgLAogICAgICAgICAgICAgICBncm91cCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gYEF2ZXJhZ2UgSW1wcmVzc2lvbnNgLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZmFjZXRfd3JhcCh+dmFyX2N0cmwsIG5yb3cgPSAyLCBzY2FsZXMgPSAiZnJlZSIpICsKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiQXZnLiBJbXByZXNzaW9ucyBiZWZvcmUgQWN0aW9uIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipUYWJsZSAxLjMuMioqIE1lYW4gSW1wcmVzc2lvbnMgdG8gQWN0aW9uIChDbGljaywgQ2xvc2UpLCBieSAqKnZhci9jdHJsKiosIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzIGFuZCBjYW1wYWlnbiBkYXlzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnRGcmFtZSA8LSBwRnJhbWUgJT4lIAogIHNlbGVjdCh2YXJfY3RybCwgY2xpY2ssIGNsb3NlKSAlPiUgCiAgZ3JvdXBfYnkodmFyX2N0cmwpICU+JSAKICBzdW1tYXJpc2UobWVhbl9jbGljayA9IHJvdW5kKG1lYW4oY2xpY2spLCAyKSwKICAgICAgICAgICAgbWVhbl9jbG9zZSA9IHJvdW5kKG1lYW4oY2xvc2UpLCAyKQogICkKZGF0YXRhYmxlKHRGcmFtZSkKYGBgCgojIyMgMS40IEJhbm5lciBBY3Rpb25zOiBudCB2cy4gY3MKCioqUmVtaW5kZXIuKiogRnJvbSB0aGUgW0NhbXBhaWduIFRyYWNraW5nIENvbmNlcHRdKGh0dHBzOi8vZG9jcy5nb29nbGUuY29tL2RvY3VtZW50L2QvMTdrN2dxRGhyeENEcFNUdGtnWV9fNEJPMFF5NUdLLWliRGQxY290YVNaLUUvZWRpdCMpOgoKPiBjcy9udCBpbmRpY2F0ZXMsIHdoaWNoIHRhcmdldCBncm91cCB0aGUgdXNlciBiZWxvbmdzIHRvIChjcyA9IGNvbW11bml0eSBzdXBwb3J0LCBudCA9IG5ldyB0YXNrcykKCioqQ2hhcnQgMS40LjFBKiogRGFpbHkgQmFubmVyIENsaWNrIGFuZCBDbG9zZSByYXRlcywgYnkgKipudC9jcyoqLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRheSwgc2Vlbl9ieSwgY2xvc2VkX2J5LCBjbGlja2VkX2J5LCBjc19udCkgJT4lIAogIGdyb3VwX2J5KGRheSwgY3NfbnQpICU+JSAKICBzdW1tYXJpc2Uoc2Vlbl9ieSA9IHN1bShzZWVuX2J5KSwgCiAgICAgICAgICAgIGNsb3NlZF9ieSA9IHN1bShjbG9zZWRfYnkpLCAKICAgICAgICAgICAgY2xpY2tlZF9ieSA9IHN1bShjbGlja2VkX2J5KSkKcEZyYW1lJGNsb3NlX3JhdGUgPSByb3VuZChwRnJhbWUkY2xvc2VkX2J5L3BGcmFtZSRzZWVuX2J5LCAyKQpwRnJhbWUkY2xpY2tfcmF0ZSA9IHJvdW5kKHBGcmFtZSRjbGlja2VkX2J5L3BGcmFtZSRzZWVuX2J5LCAyKQpwRnJhbWUgPC0gYXJyYW5nZShwRnJhbWUsIGRheSkKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn06IHNlZW5fYnksIGNsaWNrZWRfYnksIGNsb3NlZF9ieSwgZGFpbHk6CnBGIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KGRheSwgY3NfbnQsIHNlZW5fYnksIGNsb3NlZF9ieSwgY2xpY2tlZF9ieSkgJT4lIAogIHBpdm90X2xvbmdlcihjKCdzZWVuX2J5JywgJ2Nsb3NlZF9ieScsICdjbGlja2VkX2J5JyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBY3Rpb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlZhbHVlIikKZ2dwbG90KHBGLCBhZXMoeCA9IGRheSwKICAgICAgICAgICAgICAgeSA9IFZhbHVlLAogICAgICAgICAgICAgICBncm91cCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBmYWNldF93cmFwKH5jc19udCwgbnJvdyA9IDIsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJBY3Rpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipUYWJsZSAxLjQuMioqIE1lYW4gYW5kIE1lZGlhbiBDbGljayBhbmQgQ2xvc2UgcmF0ZXMgKGFjcm9zcyBkYXlzKQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnRGcmFtZSA8LSBwRiAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICdBY3Rpb24nLCAKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9ICdWYWx1ZScpCnRGcmFtZSRjbGlja19yYXRlIDwtIHRGcmFtZSRjbGlja2VkX2J5L3RGcmFtZSRzZWVuX2J5CnRGcmFtZSRjbG9zZV9yYXRlIDwtIHRGcmFtZSRjbG9zZWRfYnkvdEZyYW1lJHNlZW5fYnkKdEZyYW1lIDwtIHRGcmFtZSAlPiUgCiAgc2VsZWN0KGNzX250LCBjbGlja19yYXRlLCBjbG9zZV9yYXRlKSAlPiUgCiAgZ3JvdXBfYnkoY3NfbnQpICU+JSAKICBzdW1tYXJpc2UoY2xpY2tfcmF0ZV9tZWFuID0gcm91bmQobWVhbihjbGlja19yYXRlKSwgNSksCiAgICAgICAgICAgIGNsaWNrX3JhdGVfbWVkaWFuID0gcm91bmQobWVkaWFuKGNsaWNrX3JhdGUpLCA1KSwKICAgICAgICAgICAgY2xvc2VfcmF0ZV9tZWFuID0gcm91bmQobWVhbihjbG9zZV9yYXRlKSwgNSksCiAgICAgICAgICAgIGNsb3NlX3JhdGVfbWVkaWFuID0gcm91bmQobWVkaWFuKGNsb3NlX3JhdGUpLCA1KQogICkKZGF0YXRhYmxlKHRGcmFtZSkKYGBgCgoqKkNoYXJ0IDEuNC4xQioqIFRvdGFsLCBtZWFuLCBhbmQgbWVkaWFuIGRhaWx5IGJhbm5lciBDbGljayBhbmQgQ2xvc2UgcmF0ZXMsIGJ5ICoqbnQvY3MqKiwgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMgYW5kIGNhbXBhaWduIGRheXMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CnBGVG90YWwgPC0gcEYgJT4lIAogIHNlbGVjdChjc19udCwgQWN0aW9uLCBWYWx1ZSkgJT4lIAogIGdyb3VwX2J5KGNzX250LCBBY3Rpb24pICU+JSAKICBzdW1tYXJpc2UodG90YWwgPSBzdW0oVmFsdWUpLCAKICAgICAgICAgICAgbWVhbiA9IHJvdW5kKG1lYW4oVmFsdWUpLCAyKSwKICAgICAgICAgICAgbWVkaWFuID0gcm91bmQobWVkaWFuKFZhbHVlKSwgMikKICAgICAgICAgICAgKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKCd0b3RhbCcsICdtZWFuJywgJ21lZGlhbicpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJNZWFzdXJlIiwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlZhbHVlIikKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn06IHNlZW5fYnksIGNsaWNrZWRfYnksIGNsb3NlZF9ieSwgZGFpbHk6CmdncGxvdChwRlRvdGFsLCBhZXMoeCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgICAgICB5ID0gbG9nKFZhbHVlKSwKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IE1lYXN1cmUsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBNZWFzdXJlLAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gVmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKwogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKyAgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiQWN0aW9uIikgKyB5bGFiKCJsb2coVmFsdWUpIikgKyAKICBmYWNldF93cmFwKH4gY3NfbnQpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCioqQ2hhcnQgMS40LjIqKiBEYWlseTogSG93IE1lYW4gSW1wcmVzc2lvbnMgdG8gQWN0aW9uIChDbGljaywgQ2xvc2UpLCBieSAqKm50L2NzKiosIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF5LCBjc19udCwgbWVhbl9jbGlja19pbXAsIG1lYW5fY2xvc2VfaW1wKSAlPiUgCiAgZ3JvdXBfYnkoZGF5LCBjc19udCkgJT4lIAogIHN1bW1hcmlzZShtZWFuX2NsaWNrX2ltcCA9IHJvdW5kKG1lYW4obWVhbl9jbGlja19pbXApLCAyKSwgCiAgICAgICAgICAgIG1lYW5fY2xvc2VfaW1wID0gcm91bmQobWVhbihtZWFuX2Nsb3NlX2ltcCksIDIpKSAlPiUgCiAgYXJyYW5nZShkYXkpCmNvbG5hbWVzKHBGcmFtZSkgPC0gYygnZGF5JywgJ2NzX250JywgJ2NsaWNrJywgJ2Nsb3NlJykKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn06IGNsaWNrLCBjbG9zZSwgZGFpbHk6CnBGIDwtIHBGcmFtZSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ2NsaWNrJywgJ2Nsb3NlJyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBY3Rpb24iLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIkF2ZXJhZ2UgSW1wcmVzc2lvbnMiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gYEF2ZXJhZ2UgSW1wcmVzc2lvbnNgLAogICAgICAgICAgICAgICBncm91cCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gYEF2ZXJhZ2UgSW1wcmVzc2lvbnNgLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZmFjZXRfd3JhcCh+Y3NfbnQsIG5yb3cgPSAyLCBzY2FsZXMgPSAiZnJlZSIpICsKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiQXZnLiBJbXByZXNzaW9ucyBiZWZvcmUgQWN0aW9uIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipUYWJsZSAxLjQuMioqIE1lYW4gSW1wcmVzc2lvbnMgdG8gQWN0aW9uIChDbGljaywgQ2xvc2UpLCBieSAqKnZhci9jdHJsKiosIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzIGFuZCBjYW1wYWlnbiBkYXlzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnRGcmFtZSA8LSBwRnJhbWUgJT4lIAogIHNlbGVjdChjc19udCwgY2xpY2ssIGNsb3NlKSAlPiUgCiAgZ3JvdXBfYnkoY3NfbnQpICU+JSAKICBzdW1tYXJpc2UobWVhbl9jbGljayA9IHJvdW5kKG1lYW4oY2xpY2spLCAyKSwKICAgICAgICAgICAgbWVhbl9jbG9zZSA9IHJvdW5kKG1lYW4oY2xvc2UpLCAyKQogICkKZGF0YXRhYmxlKHRGcmFtZSkKYGBgCgojIyMgMS41IEJhbm5lciBBY3Rpb25zOiBGdWxsIERhdGFzZXQKCioqVGFibGUgMS41LjEqKiBEYWlseSBCYW5uZXIgSW1wcmVzc2lvbnM6IHNlZW4gYnksIGNsb3NlZCBieSwgY2xpY2tlZCBieQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMjIyAtLS0gRnVsbCBEYXRhc2V0IChUYWJsZSBSZXBvcnQpCmRTIDwtIGRhdGFTZXQKZFMkYmFubmVybmFtZSA8LSBnc3ViKCJXTURFX29jZWRpdG9yc19zcHJpbmdfMjAyMF8iLCAiIiwgZFMkYmFubmVybmFtZSkKZFMgPC0gZFNbLAogICAgICAgICBjKCdiYW5uZXJuYW1lJywgJ2RldmljZScsICd2YXJfY3RybCcsICdjc19udCcsCiAgICAgICAgICAgJ3NlZW5fYnknLCAnY2xvc2VkX2J5JywgJ2NsaWNrZWRfYnknKV0KZGF0YXRhYmxlKGRTLCAKICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSAzMCkKICAgICAgICAgICkKYGBgCgoqKlRhYmxlIDEuNS4yKiogRGFpbHkgQmFubmVyIEFjdGlvbnM6IG1lYW4gaW1wcmVzc2lvbnMgYmVmb3JlIGFjdGlvbgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMjIyAtLS0gRnVsbCBEYXRhc2V0IChUYWJsZSBSZXBvcnQpCmRTIDwtIGRhdGFTZXQKZFMkYmFubmVybmFtZSA8LSBnc3ViKCJXTURFX29jZWRpdG9yc19zcHJpbmdfMjAyMF8iLCAiIiwgZFMkYmFubmVybmFtZSkKZFMgPC0gZFNbLAogICAgICAgICBjKCdiYW5uZXJuYW1lJywgJ2RldmljZScsICd2YXJfY3RybCcsICdjc19udCcsCiAgICAgICAgICAgJ21lYW5fY2xpY2tfaW1wJywgJ21lYW5fY2xvc2VfaW1wJyldCmRhdGF0YWJsZShkUywgCiAgICAgICAgICBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gMzApCiAgICAgICAgICApCmBgYAoKIyMgMi4gQ2FtcGFpZ24gUGFnZXZpZXdzCgpUaGlzIHNlY3Rpb24gcHJlc2VudHMgYWxsIGRhdGEgYW5kIHN0YXRpc3RpY3Mgb24gdGhlIGNhbXBhaWduIHBhZ2VzLgoKVGhlIGZvbGxvd2luZyBjaHVuayBsb2FkcyBhbmQgdGhlbiByZS1zdHJ1Y3R1cmVzIHRoZSBkYXRhc2V0IGEgYml0LgoqKk5PVEUuKiogVGhlIGAzYWxsYCBjYW1wYWlnbiB0YWcgd2FzIG9ic2VydmVkIG9ubHkgb25jZSwgb24gYDIwMjAvMDUvMTRgLCBhbmQgaXMgcmVtb3ZlZCBmcm9tIGFuYWx5c2lzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmxGIDwtIGxpc3QuZmlsZXMoJ19hbmFseXRpY3MnKQpsRiA8LSBsRltncmVwbCgiXnBhZ2V2aWV3c0FnZ3JlZ2F0ZWQiLCBsRildCmRhdGFTZXQgPC0gbGFwcGx5KHBhc3RlMCgiX2FuYWx5dGljcy8iLCBsRiksIGZyZWFkKQpkYXRhU2V0IDwtIHJiaW5kbGlzdChkYXRhU2V0KQpkYXRhU2V0JFYxIDwtIE5VTEwKZGF0YVNldCRjYW1wYWlnbiA8LSBOVUxMCiMgLSBleHBhbmQgZ3JpZCB0byBhY2NvdW50IGZvciBtaXNzaW5nIG9ic2VydmF0aW9ucyBwZXIgZGF5CmRTIDwtIGV4cGFuZC5ncmlkKHVuaXF1ZShkYXRhU2V0JFRhZyksIAogICAgICAgICAgICAgICAgICB1bmlxdWUoZGF0YVNldCRQYWdlKSwgCiAgICAgICAgICAgICAgICAgIHVuaXF1ZShkYXRhU2V0JGRhdGUpLCAKICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGRTKSA8LSBjKCdUYWcnLCAnUGFnZScsICdkYXRlJykKZFMgPC0gZFMgJT4lIAogIGxlZnRfam9pbihkYXRhU2V0LCAKICAgICAgICAgICAgYnkgPSBjKCJUYWciLCAiUGFnZSIsICJkYXRlIikpCmRhdGFTZXQgPC0gZFM7IHJtKGRTKQpkYXRhU2V0JFBhZ2V2aWV3c1tpcy5uYShkYXRhU2V0JFBhZ2V2aWV3cyldIDwtIDAKIyAtIGJhbm5lciBjb2RlcwpkYXRhU2V0JHZhcl9jdHJsIDwtIHNhcHBseShkYXRhU2V0JFRhZywgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgidmFyIiwgeCkpIHtyZXR1cm4oInZhciIpfSBlbHNlIHtyZXR1cm4oImN0cmwiKX0KfSkKZGF0YVNldCRkZXZpY2UgPC0gc2FwcGx5KGRhdGFTZXQkVGFnLCBmdW5jdGlvbih4KSB7CiAgaWYgKGdyZXBsKCJtb2JpbGUiLCB4KSkgewogICAgcmV0dXJuKCJtb2JpbGUiKQogIH0gZWxzZSBpZiAoZ3JlcGwoImlwYWQiLCB4KSkgewogICAgICByZXR1cm4oImlwYWQiKQogIH0gZWxzZSB7CiAgICAgIHJldHVybigiZGVza3RvcCIpCiAgfQp9KQpkYXRhU2V0JGNzX250IDwtIHNhcHBseShkYXRhU2V0JFRhZywgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgiY3MiLCB4KSkge3JldHVybigiY3MiKX0gZWxzZSB7cmV0dXJuKCJudCIpfQp9KQpkYXRhU2V0JFRhZyA8LSBnc3ViKCJcXD9jYW1wYWlnbj1XTURFX29jZWRpdG9yc19zcHJpbmdfMjAyMF8iLCAiIiwgZGF0YVNldCRUYWcpCmRhdGFTZXQkUGFnZSA8LSBnc3ViKCJkZVxcLndpa2lwZWRpYVxcLm9yZy93aWtpL1dpa2lwZWRpYTp8ZGVcXC5tXFwud2lraXBlZGlhXFwub3JnL3dpa2kvV2lraXBlZGlhOiIsICIiLCBkYXRhU2V0JFBhZ2UpCiMgLSByZW1vdmUgM2FsbCB0YWc6CmRhdGFTZXQgPC0gZmlsdGVyKGRhdGFTZXQsIFRhZyAhPSAiM2FsbCIpCmBgYAoKIyMjIDIuMSBQYWdldmlld3MgT3ZlcnZpZXcKCioqQ2hhcnQgMi4xLjEqKiBEYWlseSBQYWdldmlld3MsIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KGRhdGUsIFBhZ2UsIFBhZ2V2aWV3cykgJT4lIAogIGdyb3VwX2J5KGRhdGUsIFBhZ2UpICU+JSAKICBzdW1tYXJpc2UoUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZGF0ZSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgIGdyb3VwID0gUGFnZSwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gUGFnZSwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBQYWdlLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJQYWdldmlld3MiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCioqVGFibGUgMi4xLjEqKiBQYWdldmlld3MgdG90YWxzCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KFBhZ2UsIFBhZ2V2aWV3cykgJT4lIAogIGdyb3VwX2J5KFBhZ2UpICU+JSAKICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKZGF0YXRhYmxlKHRGcmFtZSkKYGBgCgojIyMgMi4yIFBhZ2V2aWV3czogRGV2aWNlcwoKKipSZW1pbmRlci4qKiBGcm9tIHRoZSBbQ2FtcGFpZ24gVHJhY2tpbmcgQ29uY2VwdF0oaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vZG9jdW1lbnQvZC8xN2s3Z3FEaHJ4Q0RwU1R0a2dZX180Qk8wUXk1R0staWJEZDFjb3RhU1otRS9lZGl0Iyk6Cgo+IGlwYWQvbW9iaWxlIGRvZXMgbm90IHJlcHJlc2VudCB0aGUgYWN0dWFsIGRldmljZSB0eXBlLCBidXQgdGhlIGRpc3BsYXkgbW9kZSBvZiB0aGUgYmFubmVyIChzbWFsbCBkZXNrdG9wIHNjcmVlbnMgbWF5IGJlIHJlcG9ydGVkIGFzIGlwYWQpCgoKKipDaGFydCAyLjIuMSoqIFBhZ2V2aWV3cywgYnkgKipkZXZpY2VzKiosIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSAxMH0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChkYXRlLCBkZXZpY2UsIFBhZ2UsIFBhZ2V2aWV3cykgJT4lIAogIGdyb3VwX2J5KGRhdGUsIGRldmljZSwgUGFnZSkgJT4lIAogIHN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXRlKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBQYWdlLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBQYWdlLAogICAgICAgICAgICAgICAgICAgZmlsbCA9IFBhZ2UsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAofmRldmljZSwgbnJvdyA9IDMsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJBY3Rpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgoqKlRhYmxlIDIuMi4xKiogVG90YWwgcGFnZXZpZXdzLCBieSAqKmRldmljZXMqKiwgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KGRldmljZSwgUGFnZSwgUGFnZXZpZXdzKSAlPiUgCiAgZ3JvdXBfYnkoZGV2aWNlLCBQYWdlKSAlPiUgCiAgc3VtbWFyaXNlKHRvdGFsUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCmRhdGF0YWJsZSh0RnJhbWUpCmBgYAoKIyMjIDIuMyBQYWdldmlld3M6IFZhciB2cy4gQ3RybAoKKipSZW1pbmRlci4qKiBGcm9tIHRoZSBbQ2FtcGFpZ24gVHJhY2tpbmcgQ29uY2VwdF0oaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vZG9jdW1lbnQvZC8xN2s3Z3FEaHJ4Q0RwU1R0a2dZX180Qk8wUXk1R0staWJEZDFjb3RhU1otRS9lZGl0Iyk6Cgo+IGN0cmwgaXMgdGhlIGJhbm5lciB0aGF0IGR5bmFtaWNhbGx5IGRpc3BsYXlzIHRleHQgZGVwZW5kaW5nIG9uIHRoZSB0YXJnZXQgZ3JvdXAKCj4gdmFyIGlzIHRoZSBiYW5uZXIgdGhhdCBzaG93cyB0aGUgc2FtZSB0ZXh0IGZvciBib3RoIHRhcmdldCBncm91cHMKCioqQ2hhcnQgMi4zLjEqKiBQYWdldmlld3MsIGJ5ICoqdmFyL2N0cmwqKiwgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuNX0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChkYXRlLCB2YXJfY3RybCwgUGFnZSwgUGFnZXZpZXdzKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgdmFyX2N0cmwsIFBhZ2UpICU+JSAKICBzdW1tYXJpc2UoUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZGF0ZSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgIGdyb3VwID0gUGFnZSwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gUGFnZSwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBQYWdlLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBmYWNldF93cmFwKH52YXJfY3RybCwgbnJvdyA9IDMsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJBY3Rpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgoqKlRhYmxlIDIuMy4xKiogVG90YWwgcGFnZXZpZXdzLCBieSAqKmRldmljZXMqKiwgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KHZhcl9jdHJsLCBQYWdlLCBQYWdldmlld3MpICU+JSAKICBncm91cF9ieSh2YXJfY3RybCwgUGFnZSkgJT4lIAogIHN1bW1hcmlzZSh0b3RhbFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQpkYXRhdGFibGUodEZyYW1lKQpgYGAKCiMjIyAyLjQgUGFnZXZpZXdzOiBudCB2cy4gY3MKCioqUmVtaW5kZXIuKiogRnJvbSB0aGUgW0NhbXBhaWduIFRyYWNraW5nIENvbmNlcHRdKGh0dHBzOi8vZG9jcy5nb29nbGUuY29tL2RvY3VtZW50L2QvMTdrN2dxRGhyeENEcFNUdGtnWV9fNEJPMFF5NUdLLWliRGQxY290YVNaLUUvZWRpdCMpOgoKPiBjcy9udCBpbmRpY2F0ZXMsIHdoaWNoIHRhcmdldCBncm91cCB0aGUgdXNlciBiZWxvbmdzIHRvIChjcyA9IGNvbW11bml0eSBzdXBwb3J0LCBudCA9IG5ldyB0YXNrcykKCioqQ2hhcnQgMi40LjEqKiBQYWdldmlld3MsIGJ5ICoqbnQvY3MqKiwgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuNX0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChkYXRlLCBjc19udCwgUGFnZSwgUGFnZXZpZXdzKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgY3NfbnQsIFBhZ2UpICU+JSAKICBzdW1tYXJpc2UoUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZGF0ZSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgIGdyb3VwID0gUGFnZSwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gUGFnZSwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBQYWdlLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBmYWNldF93cmFwKH5jc19udCwgbnJvdyA9IDMsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJBY3Rpb25zIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgoqKlRhYmxlIDIuNC4yKiogVG90YWwgcGFnZXZpZXdzLCBieSAqKm50L2NzKiosIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBiYW5uZXJzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnRGcmFtZSA8LSBwRnJhbWUgJT4lIAogIHNlbGVjdChjc19udCwgUGFnZSwgUGFnZXZpZXdzKSAlPiUgCiAgZ3JvdXBfYnkoY3NfbnQsIFBhZ2UpICU+JSAKICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKZGF0YXRhYmxlKHRGcmFtZSkKYGBgCgojIyMgMi41IFBhZ2V2aWV3czogVmFyL0N0cmwgdnMuIGNzL250CgoqKkNoYXJ0IDIuNS4xKiogUGFnZXZpZXdzLCBieSAqKm50L2NzKiogdnMuICoqY3RybC92YXIqKi4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgc2VsZWN0KHZhcl9jdHJsLCBjc19udCwgUGFnZXZpZXdzKSAlPiUgCiAgZ3JvdXBfYnkodmFyX2N0cmwsIGNzX250KSAlPiUgCiAgc3VtbWFyaXNlKHBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IHZhcl9jdHJsLAogICAgICAgICAgICAgICAgICAgeSA9IHBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgIGdyb3VwID0gY3NfbnQsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IGNzX250LAogICAgICAgICAgICAgICAgICAgZmlsbCA9IGNzX250LAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBwYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJCYW5uZXIiKSArIHlsYWIoIlBhZ2V2aWV3cyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKKipDaGFydCAyLjUuMioqIFBhZ2V2aWV3cywgYnkgKipudC9jcyoqIHZzLiAqKmN0cmwvdmFyKiosIGFjcm9zcyB0aGUgY2FtcGFpZ24gcGFnZXMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA2LjV9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QodmFyX2N0cmwsIGNzX250LCBQYWdlLCBQYWdldmlld3MpICU+JSAKICBncm91cF9ieSh2YXJfY3RybCwgY3NfbnQsIFBhZ2UpICU+JSAKICBzdW1tYXJpc2UocGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gdmFyX2N0cmwsCiAgICAgICAgICAgICAgICAgICB5ID0gcGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBjc19udCwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY3NfbnQsCiAgICAgICAgICAgICAgICAgICBmaWxsID0gY3NfbnQsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKyAKICBmYWNldF93cmFwKH5QYWdlLCBucm93ID0gMywgc2NhbGVzID0gImZyZWUiKSArIAogIGdndGl0bGUoJzIwMjAgV01ERSBPY2Nhc2lvbmFsIEVkaXRvcnMgQmFubmVyIENhbXBhaWduJykgKwogIHhsYWIoIkJhbm5lciIpICsgeWxhYigiUGFnZXZpZXdzIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgoKIyMjIDIuNiBQYWdldmlld3M6IEZ1bGwgRGF0YXNldAoKKipUYWJsZSAyLjYuMSoqIFBhZ2V2aWV3cyBkYXRhc2V0CgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KIyMjIC0tLSBGdWxsIERhdGFzZXQgKFRhYmxlIFJlcG9ydCkKZGF0YXRhYmxlKGRhdGFTZXQsIAogICAgICAgICAgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDMwKQogICAgICAgICAgKQpgYGAKCiMjIDMuIFVzZXIgRWRpdHMKClRoaXMgc2VjdGlvbiBwcmVzZW50cyBhbGwgZGF0YSBhbmQgc3RhdGlzdGljcyBvbiB0aGUgdXNlciBlZGl0cy4KCiMjIyAzLjEgVGhlIEFzc2lnbm1lbnQgb2YgVXNlcnMgdG8gQ2FtcGFpZ24gQmFubmVycwoKVGhlIGZvbGxvd2luZyBjaHVuayBsb2FkcyB0aGUgZGF0YXNldCBvZiB1c2VyIGludGVyYWN0aW9ucyB3aXRoIGNhbXBhaWduIGJhbm5lcnMgYW5kIHRoZW4gcmUtc3RydWN0dXJlcyB0aGUgZGF0YXNldCBhIGJpdC4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpsRiA8LSBsaXN0LmZpbGVzKCJfZGF0YSIpCmxGIDwtIGxGW2dyZXBsKCJeYmFubmVySW50ZXJhY3Rpb25zIiwgbEYpXQpkYXRhU2V0IDwtIGxhcHBseShwYXN0ZTAoIl9kYXRhLyIsIGxGKSwgZnJlYWQpCmRhdGFTZXQgPC0gcmJpbmRsaXN0KGRhdGFTZXQpCmRhdGFTZXQgPC0gZmlsdGVyKGRhdGFTZXQsIGJhbm5lcmFjdGlvbiA9PSAiYmFubmVyLWNsaWNrZWQiKQpkYXRhU2V0IDwtIHNlbGVjdChkYXRhU2V0LCBiYW5uZXJuYW1lLCB1c2VyaWQsIGR0KQpkYXRhU2V0IDwtIGFycmFuZ2UoZGF0YVNldCwgdXNlcmlkLCBkdCkKZGF0YVNldCA8LSBkYXRhU2V0WyFkdXBsaWNhdGVkKGRhdGFTZXQkdXNlcmlkKSwgXQpkYXRhU2V0JGR0IDwtIE5VTEwKZGF0YVNldCRiYW5uZXJuYW1lIDwtIGdzdWIoIldNREVfb2NlZGl0b3JzX3NwcmluZ18yMDIwXyIsICIiLCBkYXRhU2V0JGJhbm5lcm5hbWUpCmRhdGFTZXQkYmFubmVybmFtZSA8LSBnc3ViKCJtb2JpbGVffGlwYWRfIiwgIiIsIGRhdGFTZXQkYmFubmVybmFtZSkKIyAtIGJhbm5lciBjb2RlcwpkYXRhU2V0JHZhcl9jdHJsIDwtIHNhcHBseShkYXRhU2V0JGJhbm5lcm5hbWUsIGZ1bmN0aW9uKHgpIHsKICBpZiAoZ3JlcGwoInZhciIsIHgpKSB7cmV0dXJuKCJ2YXIiKX0gZWxzZSB7cmV0dXJuKCJjdHJsIil9Cn0pCmRhdGFTZXQkY3NfbnQgPC0gc2FwcGx5KGRhdGFTZXQkYmFubmVybmFtZSwgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgiY3MiLCB4KSkge3JldHVybigiY3MiKX0gZWxzZSB7cmV0dXJuKCJudCIpfQp9KQp1c2VycyA8LSBzZWxlY3QoZGF0YVNldCwgdXNlcmlkLCBjc19udCkKcm0oZGF0YVNldCkKYGBgCgpCZWNhdXNlIHRoZSBhc3NpbmdtZW50IG9mIHVzZXJzIHRvICoqY3MgKGNvbW11bml0eSBzdXBwb3J0KSoqIHZzLiAqKm50IChuZXcgdGFza3MpKiogd2FzIHVuaXF1ZSwgd2Ugd2lsbCBmb2N1cyBvbiB0aGlzIHZhcmlhYmxlIGluIHRoZSBjYW1wYWlnbiBkZXNpZ24gaW4gdGhlIGFuYWx5c2lzIG9mIHVzZXIgZWRpdHMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuNX0KdXNlckVkaXRzIDwtIHJlYWQuY3N2KCJfYW5hbHl0aWNzL3VzZXJFZGl0cy5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnVzZXJFZGl0cyRyZXZhY3Rvcl90aW1lc3RhbXAgPC0gYXMuY2hhcmFjdGVyKHVzZXJFZGl0cyRyZXZhY3Rvcl90aW1lc3RhbXApCnVzZXJFZGl0cyA8LSBsZWZ0X2pvaW4odXNlckVkaXRzLAogICAgICAgICAgICAgICAgICAgICAgIHVzZXJzLAogICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiYWN0b3JfdXNlciIgPSAidXNlcmlkIikpCnVzZXJFZGl0cyRhY3Rvcl9pZCA8LSBOVUxMCnJtKHVzZXJzKQp1c2VyRWRpdHMkZGF0ZSA8LSBwYXN0ZTAoCiAgc3Vic3RyKHVzZXJFZGl0cyRyZXZhY3Rvcl90aW1lc3RhbXAsIDEsIDQpLAogICItIiwKICBzdWJzdHIodXNlckVkaXRzJHJldmFjdG9yX3RpbWVzdGFtcCwgNSwgNiksCiAgIi0iLAogIHN1YnN0cih1c2VyRWRpdHMkcmV2YWN0b3JfdGltZXN0YW1wLCA3LCA4KQopCnVzZXJFZGl0cyRkYXRlVHlwZSA8LSBhcy5EYXRlKHVzZXJFZGl0cyRkYXRlKSAKYGBgCgpJbiB0b3RhbCwgYHIgbGVuZ3RoKHVuaXF1ZSh1c2VyRWRpdHMkYWN0b3JfdXNlcikpYCB1c2VycyBtYWRlIGVkaXRzLgoKIyMjIDMuMiBEYWlseSB1c2VyIGVkaXRzIHNpbmNlIHRoZSBjYW1wYWlnbiBvbnNldAoKKipDaGFydCAzLjIuMSoqIGRhaWx5IHVzZXIgZWRpdHMKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQpjYW1wYWlnbkRheXMgPC0gYygiMjAyMC0wNS0xNCIsICIyMDIwLTA1LTE1IiwgIjIwMjAtMDUtMTYiLCAiMjAyMC0wNS0xNyIsICIyMDIwLTA1LTE4IiwgIjIwMjAtMDUtMTkiLCAKICAgICAgICAgICAgICAgICAgIjIwMjAtMDUtMjAiLCAiMjAyMC0wNS0yMSIsICIyMDIwLTA1LTIyIiwgIjIwMjAtMDUtMjMiLCAiMjAyMC0wNS0yNCIsICIyMDIwLTA1LTI1IiwgCiAgICAgICAgICAgICAgICAgICIyMDIwLTA1LTI2IiwgIjIwMjAtMDUtMjciKQoKcEZyYW1lIDwtIHVzZXJFZGl0cyAlPiUgCiAgc2VsZWN0KGRhdGVUeXBlKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZVR5cGUpICU+JSAKICBzdW1tYXJpc2UoRWRpdHMgPSBuKCkpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZGF0ZVR5cGUpCnBGcmFtZSRjYW1wYWlnbiA8LSBzYXBwbHkocEZyYW1lJGRhdGVUeXBlLCBmdW5jdGlvbih4KSB7CiAgaWYgKHggPCAiMjAyMC0wNS0xNCIpIHsKICAgIHJldHVybigiQmVmb3JlIENhbXBhaWduIikKICB9IGVsc2UgaWYgKHggPiAiMjAyMC0wNS0yOCIpIHsKICAgIHJldHVybigiQWZ0ZXIgQ2FtcGFpZ24iKSAKICB9IGVsc2UgewogICAgcmV0dXJuKCJDYW1wYWlnbiIpCiAgfQp9KQpwRnJhbWUkZGF0ZSA8LSBhcy5jaGFyYWN0ZXIocEZyYW1lJGRhdGVUeXBlKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gRWRpdHMsCiAgICAgICAgICAgICAgICAgICBncm91cCA9IGNhbXBhaWduLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBjYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBjYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gRWRpdHMsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIwIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJFZGl0cyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDYpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCioqVGFibGUgMy4yLjEqKiBNZWFuIHVzZXIgZWRpdHMgcGVyIGRheTogYmVmb3JlIGNhbXBhaWduLCBkdXJpbmcgdGhlIGNhbXBhaWduLCBhbmQgYWZ0ZXIgdGhlIGNhbXBhaWduCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KGNhbXBhaWduLCBFZGl0cykgJT4lIAogIGdyb3VwX2J5KGNhbXBhaWduKSAlPiUgCiAgc3VtbWFyaXNlKHRvdGFsRWRpdHMgPSBzdW0oRWRpdHMpLCAKICAgICAgICAgICAgbWVhbkVkaXRzID0gcm91bmQobWVhbihFZGl0cyksIDIpCiAgICAgICAgICAgICkKZGF0YXRhYmxlKHRGcmFtZSkKYGBgCgojIyMgMy4zIERhaWx5IHVzZXIgZWRpdHMgc2luY2UgdGhlIGNhbXBhaWduIG9uc2V0IGJ5IGNzL250CgoqKkNoYXJ0IDMuMy4xKiogTWVhbiB1c2VyIGVkaXRzIHBlciBkYXksIHBlciBgY3MvbnRgOiBiZWZvcmUgY2FtcGFpZ24sIGR1cmluZyB0aGUgY2FtcGFpZ24sIGFuZCBhZnRlciB0aGUgY2FtcGFpZ24KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQpwRnJhbWUgPC0gdXNlckVkaXRzICU+JSAKICBzZWxlY3QoZGF0ZSwgY3NfbnQpICU+JSAKICBncm91cF9ieShkYXRlLCBjc19udCkgJT4lIAogIHN1bW1hcmlzZShFZGl0cyA9IG4oKSkKcEZyYW1lJGRhdGVUeXBlIDwtIGFzLkRhdGUocEZyYW1lJGRhdGUpIApwRnJhbWUkY2FtcGFpZ24gPC0gc2FwcGx5KHBGcmFtZSRkYXRlVHlwZSwgZnVuY3Rpb24oeCkgewogIGlmICh4IDwgIjIwMjAtMDUtMTQiKSB7CiAgICByZXR1cm4oIkJlZm9yZSBDYW1wYWlnbiIpCiAgfSBlbHNlIGlmICh4ID4gIjIwMjAtMDUtMjgiKSB7CiAgICByZXR1cm4oIkFmdGVyIENhbXBhaWduIikgCiAgfSBlbHNlIHsKICAgIHJldHVybigiQ2FtcGFpZ24iKQogIH0KfSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IEVkaXRzLAogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBjYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY2FtcGFpZ24sCiAgICAgICAgICAgICAgICAgICBmaWxsID0gY2FtcGFpZ24sCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IEVkaXRzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgZmFjZXRfd3JhcCh+Y3NfbnQsIG5jb2wgPSAxLCBzY2FsZXMgPSAiZnJlZSIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnMjAyMCBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiRWRpdHMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA2KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgoqKlRhYmxlIDMuMy4xKiogTWVhbiB1c2VyIGVkaXRzIHBlciBkYXksIHBlciBgY3MvbnRgOiBiZWZvcmUgY2FtcGFpZ24sIGR1cmluZyB0aGUgY2FtcGFpZ24sIGFuZCBhZnRlciB0aGUgY2FtcGFpZ24KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQp0RnJhbWUgPC0gcEZyYW1lICU+JSAKICBzZWxlY3QoY2FtcGFpZ24sIGNzX250LCBFZGl0cykgJT4lIAogIGdyb3VwX2J5KGNhbXBhaWduLCBjc19udCkgJT4lIAogIHN1bW1hcmlzZSh0b3RhbEVkaXRzID0gc3VtKEVkaXRzKSwgCiAgICAgICAgICAgIG1lYW5FZGl0cyA9IHJvdW5kKG1lYW4oRWRpdHMpLCAyKQogICAgICAgICAgICApCmRhdGF0YWJsZSh0RnJhbWUpCmBgYAoKIyMjIDMuNCBFZGl0IENsYXNzZXMKCiMjIyMgMy40LjEgRWRpdCBDbGFzc2VzOiBhbGwgdXNlcnMKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQp1c2VyQ2xhc3MgPC0gdXNlckVkaXRzICU+JSAKICBzZWxlY3QoYWN0b3JfdXNlcikgJT4lIAogIGdyb3VwX2J5KGFjdG9yX3VzZXIpICU+JSAKICBzdW1tYXJpc2UoZWRpdHMgPSBuKCkpCmVkaXRCb3VuZGFyaWVzIDwtIGxpc3QoCiAgYygwLCAxKSwgCiAgYygyLCA0KSwKICBjKDUsIDkpLAogIGMoMTAsIDIwKSwKICBjKDIxLCA1MCksCiAgYyg1MSwgMTAwKQopCnVzZXJDbGFzcyRlZGl0Q2xhc3MgPC0gc2FwcGx5KHVzZXJDbGFzcyRlZGl0cywgZnVuY3Rpb24oeCkgewogIHdFQyA8LSBzYXBwbHkoZWRpdEJvdW5kYXJpZXMsIGZ1bmN0aW9uKHkpIHsKICAgIHggPj0geVsxXSAmIHggPD0geVsyXQogIH0pCiAgaWYgKHN1bSh3RUMpID09IDApIHsKICAgIHJldHVybigiPiAxMDAiKQogIH0gZWxzZSB7CiAgICByZXR1cm4ocGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsxXSwKICAgICAgICAgICAgICAgICAgIiAtICIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMl0sIAogICAgICAgICAgICAgICAgICAiKSIKICAgICAgICAgICAgICAgICAgKQogICAgKQogIH0KfSkKZWRpdENsYXNzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUodXNlckNsYXNzJGVkaXRDbGFzcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXMoZWRpdENsYXNzKSA8LSBjKCdFZGl0IENsYXNzJywgJ051bS5Vc2VycycpCmVkaXRDbGFzcyRvcmRlciA8LSBhcy5udW1lcmljKHNhcHBseShlZGl0Q2xhc3MkYEVkaXQgQ2xhc3NgLCBmdW5jdGlvbih4KSB7CiAgbG93ZXIgPC0gc3RyX2V4dHJhY3QoeCwgJ1tbOmRpZ2l0Ol1dKycpCn0pKQplZGl0Q2xhc3MgPC0gYXJyYW5nZShlZGl0Q2xhc3MsIG9yZGVyKQplZGl0Q2xhc3Mkb3JkZXIgPC0gTlVMTApkYXRhdGFibGUoZWRpdENsYXNzKQpgYGAKCiMjIyMgMy40LjJBIEVkaXQgQ2xhc3NlczogY3MgKGNvbW11bml0eSBzdXBwb3J0KSB1c2VycwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnVzZXJDbGFzcyA8LSB1c2VyRWRpdHMgJT4lIAogIGZpbHRlcihjc19udCA9PSAiY3MiKSAlPiUgCiAgc2VsZWN0KGFjdG9yX3VzZXIpICU+JSAKICBncm91cF9ieShhY3Rvcl91c2VyKSAlPiUgCiAgc3VtbWFyaXNlKGVkaXRzID0gbigpKQplZGl0Qm91bmRhcmllcyA8LSBsaXN0KAogIGMoMCwgMSksIAogIGMoMiwgNCksCiAgYyg1LCA5KSwKICBjKDEwLCAyMCksCiAgYygyMSwgNTApLAogIGMoNTEsIDEwMCkKKQp1c2VyQ2xhc3MkZWRpdENsYXNzIDwtIHNhcHBseSh1c2VyQ2xhc3MkZWRpdHMsIGZ1bmN0aW9uKHgpIHsKICB3RUMgPC0gc2FwcGx5KGVkaXRCb3VuZGFyaWVzLCBmdW5jdGlvbih5KSB7CiAgICB4ID49IHlbMV0gJiB4IDw9IHlbMl0KICB9KQogIGlmIChzdW0od0VDKSA9PSAwKSB7CiAgICByZXR1cm4oIj4gMTAwIikKICB9IGVsc2UgewogICAgcmV0dXJuKHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMV0sCiAgICAgICAgICAgICAgICAgICIgLSAiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzJdLCAKICAgICAgICAgICAgICAgICAgIikiCiAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0pCmVkaXRDbGFzcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHVzZXJDbGFzcyRlZGl0Q2xhc3MpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGVkaXRDbGFzcykgPC0gYygnRWRpdCBDbGFzcycsICdOdW0uVXNlcnMnKQplZGl0Q2xhc3Mkb3JkZXIgPC0gYXMubnVtZXJpYyhzYXBwbHkoZWRpdENsYXNzJGBFZGl0IENsYXNzYCwgZnVuY3Rpb24oeCkgewogIGxvd2VyIDwtIHN0cl9leHRyYWN0KHgsICdbWzpkaWdpdDpdXSsnKQp9KSkKZWRpdENsYXNzIDwtIGFycmFuZ2UoZWRpdENsYXNzLCBvcmRlcikKZWRpdENsYXNzJG9yZGVyIDwtIE5VTEwKZGF0YXRhYmxlKGVkaXRDbGFzcykKYGBgCgojIyMjIDMuNC4yQiBFZGl0IENsYXNzZXM6IG50IChuZXcgdGFza3MpIHVzZXJzCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuNX0KdXNlckNsYXNzIDwtIHVzZXJFZGl0cyAlPiUgCiAgZmlsdGVyKGNzX250ID09ICJudCIpICU+JSAKICBzZWxlY3QoYWN0b3JfdXNlcikgJT4lIAogIGdyb3VwX2J5KGFjdG9yX3VzZXIpICU+JSAKICBzdW1tYXJpc2UoZWRpdHMgPSBuKCkpCmVkaXRCb3VuZGFyaWVzIDwtIGxpc3QoCiAgYygwLCAxKSwgCiAgYygyLCA0KSwKICBjKDUsIDkpLAogIGMoMTAsIDIwKSwKICBjKDIxLCA1MCksCiAgYyg1MSwgMTAwKQopCnVzZXJDbGFzcyRlZGl0Q2xhc3MgPC0gc2FwcGx5KHVzZXJDbGFzcyRlZGl0cywgZnVuY3Rpb24oeCkgewogIHdFQyA8LSBzYXBwbHkoZWRpdEJvdW5kYXJpZXMsIGZ1bmN0aW9uKHkpIHsKICAgIHggPj0geVsxXSAmIHggPD0geVsyXQogIH0pCiAgaWYgKHN1bSh3RUMpID09IDApIHsKICAgIHJldHVybigiPiAxMDAiKQogIH0gZWxzZSB7CiAgICByZXR1cm4ocGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsxXSwKICAgICAgICAgICAgICAgICAgIiAtICIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMl0sIAogICAgICAgICAgICAgICAgICAiKSIKICAgICAgICAgICAgICAgICAgKQogICAgKQogIH0KfSkKZWRpdENsYXNzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUodXNlckNsYXNzJGVkaXRDbGFzcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXMoZWRpdENsYXNzKSA8LSBjKCdFZGl0IENsYXNzJywgJ051bS5Vc2VycycpCmVkaXRDbGFzcyRvcmRlciA8LSBhcy5udW1lcmljKHNhcHBseShlZGl0Q2xhc3MkYEVkaXQgQ2xhc3NgLCBmdW5jdGlvbih4KSB7CiAgbG93ZXIgPC0gc3RyX2V4dHJhY3QoeCwgJ1tbOmRpZ2l0Ol1dKycpCn0pKQplZGl0Q2xhc3MgPC0gYXJyYW5nZShlZGl0Q2xhc3MsIG9yZGVyKQplZGl0Q2xhc3Mkb3JkZXIgPC0gTlVMTApkYXRhdGFibGUoZWRpdENsYXNzKQpgYGAKCiMjIyMgMy40LjMgRWRpdCBDbGFzc2VzOiBiZWZvcmUsIGR1cmluZywgYW5kIGFmdGVyIHRoZSBjYW1wYWlnbgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnVzZXJFZGl0cyRjYW1wYWlnbiA8LSBzYXBwbHkodXNlckVkaXRzJGRhdGVUeXBlLCBmdW5jdGlvbih4KSB7CiAgaWYgKHggPCAiMjAyMC0wNS0xNCIpIHsKICAgIHJldHVybigiQmVmb3JlIENhbXBhaWduIikKICB9IGVsc2UgaWYgKHggPiAiMjAyMC0wNS0yOCIpIHsKICAgIHJldHVybigiQWZ0ZXIgQ2FtcGFpZ24iKSAKICB9IGVsc2UgewogICAgcmV0dXJuKCJDYW1wYWlnbiIpCiAgfQp9KQp1c2VyQ2xhc3MgPC0gdXNlckVkaXRzICU+JSAKICBzZWxlY3QoY2FtcGFpZ24sIGFjdG9yX3VzZXIpICU+JSAKICBncm91cF9ieShjYW1wYWlnbiwgYWN0b3JfdXNlcikgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IG4oKSkKZWRpdEJvdW5kYXJpZXMgPC0gbGlzdCgKICBjKDAsIDEpLCAKICBjKDIsIDQpLAogIGMoNSwgOSksCiAgYygxMCwgMjApLAogIGMoMjEsIDUwKSwKICBjKDUxLCAxMDApCikKdXNlckNsYXNzJGVkaXRDbGFzcyA8LSBzYXBwbHkodXNlckNsYXNzJGVkaXRzLCBmdW5jdGlvbih4KSB7CiAgd0VDIDwtIHNhcHBseShlZGl0Qm91bmRhcmllcywgZnVuY3Rpb24oeSkgewogICAgeCA+PSB5WzFdICYgeCA8PSB5WzJdCiAgfSkKICBpZiAoc3VtKHdFQykgPT0gMCkgewogICAgcmV0dXJuKCI+IDEwMCIpCiAgfSBlbHNlIHsKICAgIHJldHVybihwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzFdLAogICAgICAgICAgICAgICAgICAiIC0gIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsyXSwgCiAgICAgICAgICAgICAgICAgICIpIgogICAgICAgICAgICAgICAgICApCiAgICApCiAgfQp9KQoKdXNlckNsYXNzRnVsbCA8LSBleHBhbmQuZ3JpZChhY3Rvcl91c2VyID0gdW5pcXVlKHVzZXJDbGFzcyRhY3Rvcl91c2VyKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ24gPSB1bmlxdWUodXNlckNsYXNzJGNhbXBhaWduKSkKdXNlckNsYXNzIDwtIGxlZnRfam9pbih1c2VyQ2xhc3NGdWxsLCAKICAgICAgICAgICAgICAgICAgICAgICB1c2VyQ2xhc3MsIAogICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygnYWN0b3JfdXNlcicsICdjYW1wYWlnbicpKQp1c2VyQ2xhc3MkZWRpdHNbaXMubmEodXNlckNsYXNzJGVkaXRzKV0gPC0gMAp1c2VyQ2xhc3MkZWRpdENsYXNzW2lzLm5hKHVzZXJDbGFzcyRlZGl0Q2xhc3MpXSA8LSAnKDAgLSAxKScKZWRpdENsYXNzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUodXNlckNsYXNzJGVkaXRDbGFzcywgdXNlckNsYXNzJGNhbXBhaWduKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhlZGl0Q2xhc3MpIDwtIGMoJ0VkaXQgQ2xhc3MnLCAnQ2FtcGFpZ24nLCAnTnVtLlVzZXJzJykKZWRpdENsYXNzJG9yZGVyIDwtIGFzLm51bWVyaWMoc2FwcGx5KGVkaXRDbGFzcyRgRWRpdCBDbGFzc2AsIGZ1bmN0aW9uKHgpIHsKICBsb3dlciA8LSBzdHJfZXh0cmFjdCh4LCAnW1s6ZGlnaXQ6XV0rJykKfSkpCmVkaXRDbGFzcyA8LSBhcnJhbmdlKGVkaXRDbGFzcywgb3JkZXIpCmVkaXRDbGFzcyRvcmRlciA8LSBOVUxMCmRhdGF0YWJsZShlZGl0Q2xhc3MpCmBgYAoKCg==