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

The campaign was run from 2021/03/08 to 2021/03/21.

CURRENT UPDATE: Dataset as of 2021/03/21; Report updated w. user edits data on 2021/03/29.

Data Acquisiton

NOTE: the Data Acquisition code chunk is not fully reproducible from this Report. The data are collected by running the script 2021_Actionweek_PRODUCTION.R on stat1007.eqiad.wmnet Analytics Client, collecting the data as .tsv and .csv files, copying manually, and processing locally. A daily crontab job was run from 2021/03/08 to 2021/03/21 to collect the data for daily reporting. User edits are then collected until 2021/05/02. The data used in this report are aggregates of the daily datasets, sanitized and anonymized.

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

Daily Update

### ----------------------------------------------------------------
### --- WMDE 2021_Actionweek
### --- https://phabricator.wikimedia.org/T274563
### ----------------------------------------------------------------

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

### --- Tracking Part 1: banner campaign
### --- Please provide daily reports during the banner campaign (08th of March to 21th of March).

# - Tracking Part 2: Landingpage links to Wikipedia
# - Please provide a tracking report for the Links from the Landingpage of the campaign 
# - until 8 weeks after the end of the campaign.

### --- Timeline for the whole project
# - Start of the banner campaign: 08. March 2020
# - End of the banner campaign: 21. March 2020
# - Tracking test: first week of March
# - Last User signing up: 21.March 2020
# - Preliminary report for tracking part 1: sometime till end of March
# - Track editing behavior eight weeks after end of campaign: 02. May 2020
# - final report for tracking part 1 and 2: sometime till end of May

### --- Tracking Part 1: banner campaign
# - Campaign Tag used for Links
# -------- ?campaign=WMDE_aw21
# -------- this is only for pageviews, this tag is picked-up at our WMDE landing page
# - the Banners can carry one of the two following tags:
# -------- ?banner=WMDE_aw2021_var
# -------- ?banner=WMDE_aw2021_ctrl

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

### --- Daily Tracking
# - daily reporting during campaign
# - The following information should be included in the daily reporting:
# --- impressions per banner
# --- clicks per banner/ page views per landing page
# --- closing rate of banners
# --- If possible with the distinction whether the person was logged in or not.

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

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

### --- 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 Interactions:
### --- via event.WMDEBannerInteractions
### --- https://meta.wikimedia.org/wiki/Schema:WMDEBannerInteractions
### ----------------------------------------------------------

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

# - function: wmde_banner_actions
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_filter) > 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, paste0(dataDir, 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 <- '_wp20_aw_'
banner_status <- wmde_banner_actions(uri_query_filter = uri_query_filter,
                                     cetDay = cetDay,
                                     queryFile = queryFile,
                                     fileName = fileName,
                                     analyticsDir = analyticsDir, 
                                     campaignName = campaignName)

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

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

# - set params to wmde_collect_pageviews
uri_host <- c('de.wikipedia.org', 'de.m.wikipedia.org')
uri_path  <- c('/wiki/Wikipedia:Edit-a-thon/Wirtschaftsuniversit%C3%A4t_Wien_zum_Internationalen_Frauentag_2021',
               '/wiki/Wikipedia:GLAM',
               '/wiki/Wikipedia:Wikimedia_Deutschland/LerneWikipedia',
               '/wiki/Wikipedia:WikiProjekt_Frauen/Frauen_in_Rot',
               '/wiki/Wikipedia:Redaktionen',
               '/wiki/Wikipedia:Stuttgart#Workshop_Frauen_in_der_Politik',
               '/wiki/Wikipedia:WomenEdit',
               '/wiki/Wikipedia:FemNetz2021',
               '/wiki/Wikipedia:60_Minuten',
               '/wiki/Wikipedia%3AWho_writes_his_tory%3F',
               '/wiki/Wikipedia:Stuttgart',
               '/wiki/Benutzer:Ykskwadrat',
               '/wiki/Benutzer:Matthias_S%C3%BC%C3%9Fen',
               '/wiki/Benutzer:Cirdan',
               '/wiki/Benutzer:Mellebga',
               '/wiki/Benutzer:DerHexer',
               '/wiki/Benutzer:Nexo20')

queryFile <- 'Actionweek2021_Pageviews.hql'
fileName <- paste0("pageviews_", cetDay, ".tsv")

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

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

# - set params to wmde_process_pageviews
# - for the WMDE 2020_EmailCampaignWikipediaChallenge
uri_query_filter <- 'WMDE_aw21'
page_filter <- c('de.wikipedia.org/wiki/Wikipedia:Edit-a-thon/Wirtschaftsuniversit%C3%A4t_Wien_zum_Internationalen_Frauentag_2021',
                 'de.wikipedia.org/wiki/Wikipedia:GLAM',
                 'de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/LerneWikipedia',
                 'de.wikipedia.org/wiki/Wikipedia:WikiProjekt_Frauen/Frauen_in_Rot',
                 'de.wikipedia.org/wiki/Wikipedia:Redaktionen',
                 'de.wikipedia.org/wiki/Wikipedia:Stuttgart#Workshop_Frauen_in_der_Politik',
                 'de.wikipedia.org/wiki/Wikipedia:WomenEdit',
                 'de.wikipedia.org/wiki/Wikipedia:FemNetz2021',
                 'de.wikipedia.org/wiki/Wikipedia:60_Minuten',
                 'de.wikipedia.org/wiki/Wikipedia%3AWho_writes_his_tory%3F',
                 'de.wikipedia.org/wiki/Wikipedia:Stuttgart',
                 'de.wikipedia.org/wiki/Benutzer:Ykskwadrat',
                 'de.wikipedia.org/wiki/Benutzer:Matthias_S%C3%BC%C3%9Fen',
                 'de.wikipedia.org/wiki/Benutzer:Cirdan',
                 'de.wikipedia.org/wiki/Benutzer:Mellebga',
                 'de.wikipedia.org/wiki/Benutzer:DerHexer',
                 'de.wikipedia.org/wiki/Benutzer:Nexo20')

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

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

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

library(bit64)

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

0. Campaign Banners

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

lF <- list.files('_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
# - Design
dataSet$banner <- sapply(dataSet$bannername, function(x) {
  if (grepl("b1", x)) {return("b1")} else {return("b2")}
})
dataSet$device <- gsub("WMDE_wp20_aw_b1_|WMDE_wp20_aw_b2_", "", dataSet$bannername)

1. Campaign Pageviews

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

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

lF <- list.files('_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
dataSet$Tag <- gsub("&.+$", "", dataSet$Tag)
dataSet$Tag <- gsub("\\?campaign=", "", dataSet$Tag)
dataSet$date <- sapply(dataSet$date, function(x) {
  d <- strsplit(x, split = "-")[[1]]
  if (nchar(d[3]) == 1) {
    d[3] <- paste0("0", d[3])
  }
  return(paste(d, collapse = "-"))
})
dataSet <- dataSet %>% 
  filter(!(grepl("^%", dataSet$Tag)))

1.1 Pageviews Overview

Chart 1.1.1 Daily Pageviews, aggregated across the campaign channels.

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

Table 1.1.1 Pageviews totals

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

1.2 Pageviews: Campaign Channels

Chart 1.2.1 Pageviews, by channels

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

Chart 1.2.2 Total Pageviews, by channels

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

Table 2.2.1 Total pageviews, by channels and by pages.

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

2. User Registrations

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

2.1 User Registrations Overview

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

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

2.2 User Registrations per Campaign Channel

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

2.3 Total User Registrations per Campaign Channel

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

3. User Edits

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

3.1 User Edits Overview

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

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

3.2 User Edits by Campaign Channels

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

3.3 Total User Edits by Campaign Channels

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

3.4 Edit Classes

3.4.1 Edit Classes: all users

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

3.4.2 Edit Classes per Campaign Channels

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

LS0tCnRpdGxlOiAnMjAyMSBBY3Rpb253ZWVrIChQcmVsaW1pbmFyeSBSZXBvcnQpJwphdXRob3I6ICJHb3JhbiBTLiBNaWxvdmFub3ZpYywgRGF0YSBTY2llbnRpc3QsIFdNREUiCmRhdGU6ICJNYXJjaCAyOSwgMjAyMSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRoZW1lOiBzaW1wbGV4CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogNQogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA1Ci0tLQoKKipGZWVkYmFjayoqIHNob3VsZCBiZSBzZW5kIHRvIGBnb3Jhbi5taWxvdmFub3ZpY19leHRAd2lraW1lZGlhLmRlYC4gCgpUaGUgY2FtcGFpZ24gd2FzIHJ1biBmcm9tIDIwMjEvMDMvMDggdG8gMjAyMS8wMy8yMS4KCioqQ1VSUkVOVCBVUERBVEU6KiogRGF0YXNldCBhcyBvZiAyMDIxLzAzLzIxOyBSZXBvcnQgdXBkYXRlZCB3LiB1c2VyIGVkaXRzIGRhdGEgb24gMjAyMS8wMy8yOS4gCgpgYGB7ciwgZWNobyA9IEYsIG1lc3NhZ2UgPSBGLCB3YXJuaW5nID0gRn0KIyAhZGlhZ25vc3RpY3Mgb2ZmCiMjIyAtLS0gU2V0dXAKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KHJtYXJrZG93bikKbGlicmFyeShrbml0cikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShEVCkKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKIyMgRGF0YSBBY3F1aXNpdG9uCgoqKk5PVEU6KiogdGhlIERhdGEgQWNxdWlzaXRpb24gY29kZSBjaHVuayBpcyBub3QgZnVsbHkgcmVwcm9kdWNpYmxlIGZyb20gdGhpcyBSZXBvcnQuIFRoZSBkYXRhIGFyZSBjb2xsZWN0ZWQgYnkgcnVubmluZyB0aGUgc2NyaXB0IGAyMDIxX0FjdGlvbndlZWtfUFJPRFVDVElPTi5SYCBvbiBgc3RhdDEwMDcuZXFpYWQud21uZXRgIEFuYWx5dGljcyBDbGllbnQsIGNvbGxlY3RpbmcgdGhlIGRhdGEgYXMgYC50c3ZgIGFuZCBgLmNzdmAgZmlsZXMsIGNvcHlpbmcgbWFudWFsbHksIGFuZCBwcm9jZXNzaW5nIGxvY2FsbHkuIEEgZGFpbHkgY3JvbnRhYiBqb2Igd2FzIHJ1biBmcm9tIGAyMDIxLzAzLzA4YCB0byBgMjAyMS8wMy8yMWAgdG8gY29sbGVjdCB0aGUgZGF0YSBmb3IgZGFpbHkgcmVwb3J0aW5nLiBVc2VyIGVkaXRzIGFyZSB0aGVuIGNvbGxlY3RlZCB1bnRpbCAyMDIxLzA1LzAyLiBUaGUgZGF0YSB1c2VkIGluIHRoaXMgcmVwb3J0IGFyZSBhZ2dyZWdhdGVzIG9mIHRoZSBkYWlseSBkYXRhc2V0cywgc2FuaXRpemVkIGFuZCBhbm9ueW1pemVkLiAgIAoKKipEQVRBOioqICgxKSBUaGUgYGV2ZW50LndtZGViYW5uZXJpbnRlcmFjdGlvbnNgIHNjaGVtYSBpcyB1c2VkIGZvciBiYW5uZXIgY2xpY2tzL2Nsb3NpbmcgKHNlZTogW1BoYWJdKGh0dHBzOi8vcGhhYnJpY2F0b3Iud2lraW1lZGlhLm9yZy9UMjYyNTM0IzY1MDE3OTMpKTsgKDIpIHRoZSBgd21mLndlYnJlcXVlc3RgIHRhYmxlIGlzIGEgc291cmNlIG9mIGJhbm5lciBpbXByZXNzaW9ucyBkYXRhOyAoMykgcGFnZXZpZXdzLCB1c2VyIHJlZ2lzdHJhdGlvbnMsIGFuZCB1c2VyIGVkaXRzIGFyZSBjb2xsZWN0ZWQgYnkgZm9sbG93aW5nIHRoZSBzdGFuZGFyZCBwcm9jZWR1cmUuIAoKIyMjIERhaWx5IFVwZGF0ZQoKYGBge3IsIGVjaG8gPSBULCBldmFsID0gRn0KIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBXTURFIDIwMjFfQWN0aW9ud2VlawojIyMgLS0tIGh0dHBzOi8vcGhhYnJpY2F0b3Iud2lraW1lZGlhLm9yZy9UMjc0NTYzCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIENhbXBhaWduIERlc2NyaXB0aW9uIGFuZCBQYXJhbWV0ZXJzCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMgLS0tIFRyYWNraW5nIFBhcnQgMTogYmFubmVyIGNhbXBhaWduCiMjIyAtLS0gUGxlYXNlIHByb3ZpZGUgZGFpbHkgcmVwb3J0cyBkdXJpbmcgdGhlIGJhbm5lciBjYW1wYWlnbiAoMDh0aCBvZiBNYXJjaCB0byAyMXRoIG9mIE1hcmNoKS4KCiMgLSBUcmFja2luZyBQYXJ0IDI6IExhbmRpbmdwYWdlIGxpbmtzIHRvIFdpa2lwZWRpYQojIC0gUGxlYXNlIHByb3ZpZGUgYSB0cmFja2luZyByZXBvcnQgZm9yIHRoZSBMaW5rcyBmcm9tIHRoZSBMYW5kaW5ncGFnZSBvZiB0aGUgY2FtcGFpZ24gCiMgLSB1bnRpbCA4IHdlZWtzIGFmdGVyIHRoZSBlbmQgb2YgdGhlIGNhbXBhaWduLgoKIyMjIC0tLSBUaW1lbGluZSBmb3IgdGhlIHdob2xlIHByb2plY3QKIyAtIFN0YXJ0IG9mIHRoZSBiYW5uZXIgY2FtcGFpZ246IDA4LiBNYXJjaCAyMDIwCiMgLSBFbmQgb2YgdGhlIGJhbm5lciBjYW1wYWlnbjogMjEuIE1hcmNoIDIwMjAKIyAtIFRyYWNraW5nIHRlc3Q6IGZpcnN0IHdlZWsgb2YgTWFyY2gKIyAtIExhc3QgVXNlciBzaWduaW5nIHVwOiAyMS5NYXJjaCAyMDIwCiMgLSBQcmVsaW1pbmFyeSByZXBvcnQgZm9yIHRyYWNraW5nIHBhcnQgMTogc29tZXRpbWUgdGlsbCBlbmQgb2YgTWFyY2gKIyAtIFRyYWNrIGVkaXRpbmcgYmVoYXZpb3IgZWlnaHQgd2Vla3MgYWZ0ZXIgZW5kIG9mIGNhbXBhaWduOiAwMi4gTWF5IDIwMjAKIyAtIGZpbmFsIHJlcG9ydCBmb3IgdHJhY2tpbmcgcGFydCAxIGFuZCAyOiBzb21ldGltZSB0aWxsIGVuZCBvZiBNYXkKCiMjIyAtLS0gVHJhY2tpbmcgUGFydCAxOiBiYW5uZXIgY2FtcGFpZ24KIyAtIENhbXBhaWduIFRhZyB1c2VkIGZvciBMaW5rcwojIC0tLS0tLS0tID9jYW1wYWlnbj1XTURFX2F3MjEKIyAtLS0tLS0tLSB0aGlzIGlzIG9ubHkgZm9yIHBhZ2V2aWV3cywgdGhpcyB0YWcgaXMgcGlja2VkLXVwIGF0IG91ciBXTURFIGxhbmRpbmcgcGFnZQojIC0gdGhlIEJhbm5lcnMgY2FuIGNhcnJ5IG9uZSBvZiB0aGUgdHdvIGZvbGxvd2luZyB0YWdzOgojIC0tLS0tLS0tID9iYW5uZXI9V01ERV9hdzIwMjFfdmFyCiMgLS0tLS0tLS0gP2Jhbm5lcj1XTURFX2F3MjAyMV9jdHJsCgojIyMgLS0tIFRpbWVsaW5lIGZvciB0aGUgd2hvbGUgcHJvamVjdAojIC0gU3RhcnQgb2YgdGhlIGJhbm5lciBjYW1wYWlnbjogMS4gT2N0b2JlciAyMDIwCiMgLSBFbmQgb2YgdGhlIGJhbm5lciBjYW1wYWlnbjogMTEuIE9jdG9iZXIgMjAyMAojIC0gVHJhY2tpbmcgdGVzdDogRW5kIG9mIFNlcHRlbWJlcgojIC0gTGFzdCBVc2VyIHNpZ25pbmcgdXA6IDExLiBPY3RvYmVyCiMgLSBQcmVsaW1pbmFyeSByZXBvcnQgZm9yIHRyYWNraW5nIHBhcnQgMTogc29tZXRpbWUgdW50aWwgZW5kIG9mIE9jdG9iZXIKIyAtIExhc3QgVXNlciBmaW5pc2hpbmcgbWFpbGluZyBjYW1wYWlnbjogTm92ZW1iZXIgMTF0aAojIC0gVHJhY2sgZWRpdGluZyBiZWhhdmlvciBzaXggd2Vla3MgYWZ0ZXIgZW5kIG9mIGNhbXBhaWduOiBEZWNlbWJlciAyMwojIC0gZmluYWwgcmVwb3J0IGZvciB0cmFja2luZyBwYXJ0IDEgYW5kIDI6IGJlZ2lubmluZyBvZiBKYW51YXJ5CgojIyMgLS0tIERhaWx5IFRyYWNraW5nCiMgLSBkYWlseSByZXBvcnRpbmcgZHVyaW5nIGNhbXBhaWduCiMgLSBUaGUgZm9sbG93aW5nIGluZm9ybWF0aW9uIHNob3VsZCBiZSBpbmNsdWRlZCBpbiB0aGUgZGFpbHkgcmVwb3J0aW5nOgojIC0tLSBpbXByZXNzaW9ucyBwZXIgYmFubmVyCiMgLS0tIGNsaWNrcyBwZXIgYmFubmVyLyBwYWdlIHZpZXdzIHBlciBsYW5kaW5nIHBhZ2UKIyAtLS0gY2xvc2luZyByYXRlIG9mIGJhbm5lcnMKIyAtLS0gSWYgcG9zc2libGUgd2l0aCB0aGUgZGlzdGluY3Rpb24gd2hldGhlciB0aGUgcGVyc29uIHdhcyBsb2dnZWQgaW4gb3Igbm90LgoKIyMjIC0tLSBsaWJyYXJpZXMKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShsdWJyaWRhdGUpCgojIyMgLS0tIGRpciBzdHJ1Y3R1cmUKY2FtcGFpZ25QYXRoIDwtICcvaG9tZS9nb3JhbnNtL0FuYWx5dGljcy9OZXdFZGl0b3JzL0NhbXBhaWducy8yMDIxX0FjdGlvbndlZWsvJwpkYXRhRGlyIDwtIHBhc3RlMChjYW1wYWlnblBhdGgsICJfZGF0YS8iKQphbmFseXRpY3NEaXIgPC0gcGFzdGUwKGNhbXBhaWduUGF0aCwgIl9hbmFseXRpY3MvIikKIyMjIC0tLSBjYW1wYWlnbiBzcGVjaWZpY3MKY2FtcGFpZ25OYW1lIDwtICdBY3Rpb253ZWVrXzIwMjEnCgojIyMgLS0tIGRldGVybWluZSBjZXREYXkKY2V0RGF5IDwtIFN5cy50aW1lKCkKY2V0RGF5CmF0dHIoY2V0RGF5LCAidHpvbmUiKSA8LSAiRXVyb3BlL0JlcmxpbiIKIyAtIG9uZSBkYXkgYmVoaW5kIGZvciBjcm9udGFiCiMgLSAoaS5lLiB3YWl0aW5nIGZvciB3bWYud2VicmVxdWVzdCB0byBjb21wbGV0ZSBpcyBkYXRhIGFjcXVpc2l0aW9uKQpjZXREYXkgPC0geW1kKAogIHN0cnNwbGl0KGFzLmNoYXJhY3RlcihjZXREYXkpLCAKICAgICAgICAgICBzcGxpdCA9ICIgIiwgCiAgICAgICAgICAgZml4ZWQgPSBUKVtbMV1dWzFdCikgLSAxCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIEJhbm5lciBJbnRlcmFjdGlvbnM6CiMjIyAtLS0gdmlhIGV2ZW50LldNREVCYW5uZXJJbnRlcmFjdGlvbnMKIyMjIC0tLSBodHRwczovL21ldGEud2lraW1lZGlhLm9yZy93aWtpL1NjaGVtYTpXTURFQmFubmVySW50ZXJhY3Rpb25zCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0gc2VsZWN0IGR0LCBldmVudC5iYW5uZXJOYW1lLCBldmVudC5iYW5uZXJBY3Rpb24sIGV2ZW50LmJhbm5lckltcHJlc3Npb25zLCBldmVudC51c2VySUQgCiMgLSBmcm9tIGV2ZW50LndtZGViYW5uZXJpbnRlcmFjdGlvbnMgd2hlcmUgeWVhcj0yMDIwIGFuZCBtb250aD01IGFuZCAoZGF5PTExIG9yIGRheT0xMiBvciBkYXk9MTMpOwoKIyAtIGZ1bmN0aW9uOiB3bWRlX2Jhbm5lcl9hY3Rpb25zCndtZGVfYmFubmVyX2FjdGlvbnMgPC0gZnVuY3Rpb24odXJpX3F1ZXJ5X2ZpbHRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuYWx5dGljc0RpciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUpIHsKICAKICAjIC0gTk9URToKICAjIC0gZXhwZWN0ZWQgZm9ybWF0IGZvciBjZXREYXkgaXM6IFlZWVktTU0tREQKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgZGF0ZXRpbWVfY29uZGl0aW9uCiAgY2V0X2NvbmRpdGlvbiA8LSBzZXEoCiAgICBmcm9tID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMDowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICB0byA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDIzOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIGJ5ID0gImhvdXIiCiAgKSAKICBhdHRyKGNldF9jb25kaXRpb24sICJ0em9uZSIpIDwtICJVVEMiCiAgY2V0X2NvbmRpdGlvbiA8LSBhcy5jaGFyYWN0ZXIoY2V0X2NvbmRpdGlvbikKICBjZXRfY29uZGl0aW9uIDwtIHVubGlzdChzdHJfZXh0cmFjdF9hbGwoY2V0X2NvbmRpdGlvbiwgIl4oW1s6ZGlnaXQ6XV18XFxzfC0pKiIpKQogIGNldF95ZWFycyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsxXQogICAgfSkKICBjZXRfbW9udGhzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzJdCiAgICB9KQogIGNldF9tb250aHMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X21vbnRocykKICBjZXRfZGF5cyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVszXQogICAgfSkKICBjZXRfZGF5cyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfZGF5cykKICBjZXRfaG91cnMgPC0gc2FwcGx5KHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCAKICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgeFsyXQogICAgICAgICAgICAgICAgICAgICAgfSkKICBjZXRfaG91cnMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2hvdXJzKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlMCgKICAgICJ5ZWFyID0gIiwgY2V0X3llYXJzLCAiIEFORCAiLAogICAgIm1vbnRoID0gIiwgY2V0X21vbnRocywgIiBBTkQgIiwKICAgICJkYXkgPSAiLCBjZXRfZGF5cywgIiBBTkQgIiwgCiAgICAiaG91ciA9ICIsIGNldF9ob3VycwogICkKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZSgiKCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGV0aW1lQ29uZGl0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQogIAogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSBldmVudEJhbm5lck5hbWVfY29uZGl0aW9uCiAgaWYgKGxlbmd0aCh1cmlfcXVlcnlfZmlsdGVyKSA+IDEpIHsKICAgIGV2ZW50QmFubmVyTmFtZV9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoImV2ZW50LmJhbm5lck5hbWUgTElLRSAnJSIsIHVyaV9xdWVyeV9maWx0ZXIsICIlJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICBldmVudEJhbm5lck5hbWVfY29uZGl0aW9uID0gcGFzdGUwKCJldmVudC5iYW5uZXJOYW1lIExJS0UgJyUiLCB1cmlfcXVlcnlfZmlsdGVyLCAiJSciKQogIH0KICAKICAjIC0gY29tcG9zZSBIaXZlUUwgcXVlcnkKICBoaXZlUXVlcnkgPC0gcGFzdGUwKCAKICAgICJzZWxlY3QgZHQsIGV2ZW50LmJhbm5lck5hbWUsIGV2ZW50LmJhbm5lckFjdGlvbiwgZXZlbnQuYmFubmVySW1wcmVzc2lvbnMsIGV2ZW50LnVzZXJJRCBmcm9tIGV2ZW50LndtZGViYW5uZXJpbnRlcmFjdGlvbnMgCiAgICBXSEVSRSAoIiwKICAgIGV2ZW50QmFubmVyTmFtZV9jb25kaXRpb24sICIgQU5EICIsCiAgICAiKCIsIGRhdGV0aW1lQ29uZGl0aW9uLCAiKSIsCiAgICAiKTsiCiAgKQogIAogICMgLSB3cml0ZSBocWwKICB3cml0ZShoaXZlUXVlcnksIHBhc3RlMChkYXRhRGlyLCBxdWVyeUZpbGUpKQogICMgLSBleGVjdXRlIGhxbCBzY3JpcHQ6CiAga2VyYmVyb3NQcmVmaXggPC0gCiAgICAnc3VkbyAtdSBhbmFseXRpY3MtcHJpdmF0ZWRhdGEga2VyYmVyb3MtcnVuLWNvbW1hbmQgYW5hbHl0aWNzLXByaXZhdGVkYXRhICcKICAjIC0gS2VyYmVyb3MgaW5pdAogIHN5c3RlbShjb21tYW5kID0gcGFzdGUwKGtlcmJlcm9zUHJlZml4LCAnIGhkZnMgZGZzIC1scycpLCAKICAgICAgICAgd2FpdCA9IFQpCiAgIyAtIFJ1biBxdWVyeQogIHF1ZXJ5IDwtIHN5c3RlbShjb21tYW5kID0gcGFzdGUoa2VyYmVyb3NQcmVmaXgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJy91c3IvbG9jYWwvYmluL2JlZWxpbmUgLS1pbmNyZW1lbnRhbD10cnVlIC0tc2lsZW50IC1mICInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKGRhdGFEaXIsIHF1ZXJ5RmlsZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnIiA+ICcsIGRhdGFEaXIsIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpLAogICAgICAgICAgICAgICAgICB3YWl0ID0gVFJVRSkKICAKICAjIC0gV3JhbmdsZSBCYW5uZXIgSW50ZXJhY3Rpb25zCiAgIyAtIGxvYWQKICBiYW5uZXJEYXRhIDwtIHRyeUNhdGNoKHsKICAgIGFzLmRhdGEuZnJhbWUoZnJlYWQocGFzdGUwKGRhdGFEaXIsIGZpbGVOYW1lKSkpCiAgfSwKICBlcnJvciA9IGZ1bmN0aW9uKGNvbmRpdGlvbikgewogICAgcmV0dXJuKEZBTFNFKQogIH0pCiAgCiAgIyAtIHByb2Nlc3MKICBpZiAoY2xhc3MoYmFubmVyRGF0YSkgPT0gJ2xvZ2ljYWwnKSB7CiAgICByZXR1cm4oRkFMU0UpIAogIH0gZWxzZSB7IAogICAgIyAtIGJhbm5lclNlZW4KICAgIGJhbm5lclNlZW4gPC0gYmFubmVyRGF0YSAlPiUgCiAgICAgIGRwbHlyOjpzZWxlY3QoYmFubmVybmFtZSwgdXNlcmlkKQogICAgIyAtIGJhbm5lclNlZW4gPC0gYmFubmVyU2VlblshZHVwbGljYXRlZChiYW5uZXJTZWVuKSwgXQogICAgYmFubmVyU2VlbiA8LSBiYW5uZXJTZWVuICU+JSAKICAgICAgZHBseXI6OnNlbGVjdChiYW5uZXJuYW1lKSAlPiUgCiAgICAgIGRwbHlyOjpncm91cF9ieShiYW5uZXJuYW1lKSAlPiUgCiAgICAgIGRwbHlyOjpzdW1tYXJpc2Uoc2Vlbl9ieSA9IG4oKSkKICAgICMgLSBiYW5uZXJDbG9zZWQKICAgIGJhbm5lckNsb3NlZCA8LSBiYW5uZXJEYXRhICU+JSAKICAgICAgZHBseXI6OmZpbHRlcihiYW5uZXJhY3Rpb24gPT0gImJhbm5lci1jbG9zZWQiKSAlPiUgCiAgICAgIGRwbHlyOjpzZWxlY3QoYmFubmVybmFtZSwgYmFubmVyaW1wcmVzc2lvbnMpICU+JSAKICAgICAgZHBseXI6Omdyb3VwX2J5KGJhbm5lcm5hbWUpICU+JQogICAgICBkcGx5cjo6c3VtbWFyaXNlKGNsb3NlZF9ieSA9IG4oKSwgCiAgICAgICAgICAgICAgICAgICAgICAgbWVhbl9jbG9zZV9pbXAgPSByb3VuZChtZWFuKGJhbm5lcmltcHJlc3Npb25zKSwgMikpCiAgICAjIC0gYmFubmVyQ2xpY2tlZAogICAgYmFubmVyQ2xpY2tlZCA8LSBiYW5uZXJEYXRhICU+JSAKICAgICAgZHBseXI6OmZpbHRlcihiYW5uZXJhY3Rpb24gPT0gImJhbm5lci1jbGlja2VkIikgJT4lIAogICAgICBkcGx5cjo6c2VsZWN0KGJhbm5lcm5hbWUsIGJhbm5lcmltcHJlc3Npb25zKSAlPiUgCiAgICAgIGRwbHlyOjpncm91cF9ieShiYW5uZXJuYW1lKSAlPiUKICAgICAgZHBseXI6OnN1bW1hcmlzZShjbGlja2VkX2J5ID0gbigpLCAKICAgICAgICAgICAgICAgICAgICAgICBtZWFuX2NsaWNrX2ltcCA9IHJvdW5kKG1lYW4oYmFubmVyaW1wcmVzc2lvbnMpLCAyKSkKICAgICMgLSB3aG9DbGlja2VkCiAgICB3aG9DbGlja2VkIDwtIGJhbm5lckRhdGEgJT4lIAogICAgICBkcGx5cjo6ZmlsdGVyKGJhbm5lcmFjdGlvbiA9PSAiYmFubmVyLWNsaWNrZWQiKQogICAgd2hvQ2xpY2tlZCA8LSBkYXRhLmZyYW1lKHVzZXJpZCA9IHVuaXF1ZSh3aG9DbGlja2VkJHVzZXJpZCkpCiAgICAjIC0gc3RvcmU6CiAgICB3cml0ZS5jc3Yod2hvQ2xpY2tlZCwgCiAgICAgICAgICAgICAgcGFzdGUwKGFuYWx5dGljc0RpciwgCiAgICAgICAgICAgICAgICAgICAgICJ3aG9DbGlja2VkXyIsCiAgICAgICAgICAgICAgICAgICAgIHN0cnNwbGl0KAogICAgICAgICAgICAgICAgICAgICAgIHN0cnNwbGl0KGZpbGVOYW1lLCBzcGxpdCA9ICJfIiwgZml4ZWQgPSBUKVtbMV1dWzJdLAogICAgICAgICAgICAgICAgICAgICAgIHNwbGl0ID0gIi4iLCAKICAgICAgICAgICAgICAgICAgICAgICBmaXhlZCA9IFQpW1sxXV1bMV0sCiAgICAgICAgICAgICAgICAgICAgICIuY3N2IgogICAgICAgICAgICAgICkKICAgICkKICAgICMgLSBqb2luCiAgICBiYW5uZXJEYXRhIDwtIGJhbm5lclNlZW4gJT4lIAogICAgICBkcGx5cjo6bGVmdF9qb2luKGJhbm5lckNsb3NlZCwgJ2Jhbm5lcm5hbWUnKSAlPiUgCiAgICAgIGRwbHlyOjpsZWZ0X2pvaW4oYmFubmVyQ2xpY2tlZCwgJ2Jhbm5lcm5hbWUnKQogICAgYmFubmVyRGF0YSRjbG9zZV9yYXRlIDwtIHJvdW5kKGJhbm5lckRhdGEkY2xvc2VkX2J5L2Jhbm5lckRhdGEkc2Vlbl9ieSwgMikKICAgIGJhbm5lckRhdGEkY2xpY2tfcmF0ZSA8LSByb3VuZChiYW5uZXJEYXRhJGNsaWNrZWRfYnkvYmFubmVyRGF0YSRzZWVuX2J5LCAyKQogICAgIyAtIGRhdGUsIGNhbXBhaWduCiAgICBiYW5uZXJEYXRhJGRheSA8LSBjZXREYXkKICAgIGJhbm5lckRhdGEkY2FtcGFpZ24gPC0gY2FtcGFpZ25OYW1lCiAgICAKICAgICMgLSBzdG9yZToKICAgIHdyaXRlLmNzdihiYW5uZXJEYXRhLCAKICAgICAgICAgICAgICBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAKICAgICAgICAgICAgICAgICAgICAgImJhbm5lckludGVyYWN0aW9uc0FnZ3JlZ2F0ZWRfIiwKICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoCiAgICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoZmlsZU5hbWUsIHNwbGl0ID0gIl8iLCBmaXhlZCA9IFQpW1sxXV1bMl0sCiAgICAgICAgICAgICAgICAgICAgICAgc3BsaXQgPSAiLiIsIAogICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXSwKICAgICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICAgKQogICAgKQogIH0KICAKfQoKIyAtIHNldCBwYXJhbXMgZm9yIHdtZGVfYmFubmVyX2FjdGlvbnMoKQpxdWVyeUZpbGUgPC0gcGFzdGUwKGNhbXBhaWduTmFtZSwgIl9iYW5uZXJJbnRlcmFjdGlvbnMuaHFsIikKZmlsZU5hbWUgPC0gcGFzdGUwKCJiYW5uZXJJbnRlcmFjdGlvbnNfIiwgY2V0RGF5LCAiLnRzdiIpCnVyaV9xdWVyeV9maWx0ZXIgPC0gJ193cDIwX2F3XycKYmFubmVyX3N0YXR1cyA8LSB3bWRlX2Jhbm5lcl9hY3Rpb25zKHVyaV9xdWVyeV9maWx0ZXIgPSB1cmlfcXVlcnlfZmlsdGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5ID0gY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlID0gcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUgPSBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuYWx5dGljc0RpciA9IGFuYWx5dGljc0RpciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUgPSBjYW1wYWlnbk5hbWUpCgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIFBhZ2V2aWV3cwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIGZ1bmN0aW9uOiB3bWRlX2NvbGxlY3RfcGFnZXZpZXdzCndtZGVfY29sbGVjdF9wYWdldmlld3MgPC0gZnVuY3Rpb24odXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyKSB7CiAgCiAgIyAtIE5PVEU6CiAgIyAtIGV4cGVjdGVkIGZvcm1hdCBmb3IgY2V0RGF5IGlzOiBZWVlZLU1NLURECiAgCiAgIyAtIHRvIGRhdGFEaXIKICBzZXR3ZChkYXRhRGlyKQogIAogICMgLSBsaWJyYXJpZXMKICBsaWJyYXJ5KHN0cmluZ3IpCiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIGRhdGV0aW1lX2NvbmRpdGlvbgogIGNldF9jb25kaXRpb24gPC0gc2VxKAogICAgZnJvbSA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDA6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgdG8gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAyMzowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICBieSA9ICJob3VyIgogICkgCiAgYXR0cihjZXRfY29uZGl0aW9uLCAidHpvbmUiKSA8LSAiVVRDIgogIGNldF9jb25kaXRpb24gPC0gYXMuY2hhcmFjdGVyKGNldF9jb25kaXRpb24pCiAgY2V0X2NvbmRpdGlvbiA8LSB1bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKGNldF9jb25kaXRpb24sICJeKFtbOmRpZ2l0Ol1dfFxcc3wtKSoiKSkKICBjZXRfeWVhcnMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMV0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsyXQogICAgfSkKICBjZXRfbW9udGhzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9tb250aHMpCiAgY2V0X2RheXMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bM10KICAgIH0pCiAgY2V0X2RheXMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2RheXMpCiAgY2V0X2hvdXJzIDwtIHNhcHBseShzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgCiAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHhbMl0KICAgICAgICAgICAgICAgICAgICAgIH0pCiAgY2V0X2hvdXJzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9ob3VycykKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZTAoCiAgICAieWVhciA9ICIsIGNldF95ZWFycywgIiBBTkQgIiwKICAgICJtb250aCA9ICIsIGNldF9tb250aHMsICIgQU5EICIsCiAgICAiZGF5ID0gIiwgY2V0X2RheXMsICIgQU5EICIsIAogICAgImhvdXIgPSAiLCBjZXRfaG91cnMKICApCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUoIigiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZUNvbmRpdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgdXJpX2hvc3RfY29uZGl0aW9uCiAgaWYgKGxlbmd0aCh1cmlfaG9zdCkgPiAxKSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPC0gcGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJ1cmlfaG9zdCA9ICciLCB1cmlfaG9zdCwgIiciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgc2VwID0gIiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiCiAgICApCiAgfSBlbHNlIHsKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiA9IHBhc3RlMCgidXJpX2hvc3QgPSAnIiwgdXJpX2hvc3QsICInIikKICB9CiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9wYXRoX2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX3BhdGgpID4gMSkgewogICAgdXJpX3BhdGhfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgidXJpX3BhdGggPSAnIiwgdXJpX3BhdGgsICInIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICB1cmlfcGF0aF9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpCiAgfQogIAogICMgLSBjb21wb3NlIEhpdmVRTCBxdWVyeQogIGhpdmVRdWVyeSA8LSBwYXN0ZTAoIAogICAgIlVTRSB3bWY7CiAgICBTRUxFQ1QgdXJpX2hvc3QsIHVyaV9wYXRoLCB1cmlfcXVlcnksIHJlZmVyZXIgRlJPTSB3ZWJyZXF1ZXN0CiAgICBXSEVSRSAoIiwKICAgIHVyaV9ob3N0X2NvbmRpdGlvbiwgIiBBTkQgIiwKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiwgIiBBTkQgIiwKICAgICIoIiwgZGF0ZXRpbWVDb25kaXRpb24sICIpIiwKICAgICIpOyIKICApCiAgCiAgIyAtIHdyaXRlIGhxbAogIHdyaXRlKGhpdmVRdWVyeSwgcXVlcnlGaWxlKQogICMgLSBleGVjdXRlIGhxbCBzY3JpcHQ6CiAga2VyYmVyb3NQcmVmaXggPC0gCiAgICAnc3VkbyAtdSBhbmFseXRpY3MtcHJpdmF0ZWRhdGEga2VyYmVyb3MtcnVuLWNvbW1hbmQgYW5hbHl0aWNzLXByaXZhdGVkYXRhICcKICAjIC0gS2VyYmVyb3MgaW5pdAogIHN5c3RlbShjb21tYW5kID0gcGFzdGUwKGtlcmJlcm9zUHJlZml4LCAnIGhkZnMgZGZzIC1scycpLCAKICAgICAgICAgd2FpdCA9IFQpCiAgIyAtIFJ1biBxdWVyeQogIHF1ZXJ5IDwtIHN5c3RlbShjb21tYW5kID0gcGFzdGUoa2VyYmVyb3NQcmVmaXgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJy91c3IvbG9jYWwvYmluL2JlZWxpbmUgLS1pbmNyZW1lbnRhbD10cnVlIC0tc2lsZW50IC1mICInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKGRhdGFEaXIsIHF1ZXJ5RmlsZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnIiA+ICcsIGRhdGFEaXIsIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpLAogICAgICAgICAgICAgICAgICB3YWl0ID0gVFJVRSkKfQoKIyAtIHNldCBwYXJhbXMgdG8gd21kZV9jb2xsZWN0X3BhZ2V2aWV3cwp1cmlfaG9zdCA8LSBjKCdkZS53aWtpcGVkaWEub3JnJywgJ2RlLm0ud2lraXBlZGlhLm9yZycpCnVyaV9wYXRoICA8LSBjKCcvd2lraS9XaWtpcGVkaWE6RWRpdC1hLXRob24vV2lydHNjaGFmdHN1bml2ZXJzaXQlQzMlQTR0X1dpZW5fenVtX0ludGVybmF0aW9uYWxlbl9GcmF1ZW50YWdfMjAyMScsCiAgICAgICAgICAgICAgICcvd2lraS9XaWtpcGVkaWE6R0xBTScsCiAgICAgICAgICAgICAgICcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0xlcm5lV2lraXBlZGlhJywKICAgICAgICAgICAgICAgJy93aWtpL1dpa2lwZWRpYTpXaWtpUHJvamVrdF9GcmF1ZW4vRnJhdWVuX2luX1JvdCcsCiAgICAgICAgICAgICAgICcvd2lraS9XaWtpcGVkaWE6UmVkYWt0aW9uZW4nLAogICAgICAgICAgICAgICAnL3dpa2kvV2lraXBlZGlhOlN0dXR0Z2FydCNXb3Jrc2hvcF9GcmF1ZW5faW5fZGVyX1BvbGl0aWsnLAogICAgICAgICAgICAgICAnL3dpa2kvV2lraXBlZGlhOldvbWVuRWRpdCcsCiAgICAgICAgICAgICAgICcvd2lraS9XaWtpcGVkaWE6RmVtTmV0ejIwMjEnLAogICAgICAgICAgICAgICAnL3dpa2kvV2lraXBlZGlhOjYwX01pbnV0ZW4nLAogICAgICAgICAgICAgICAnL3dpa2kvV2lraXBlZGlhJTNBV2hvX3dyaXRlc19oaXNfdG9yeSUzRicsCiAgICAgICAgICAgICAgICcvd2lraS9XaWtpcGVkaWE6U3R1dHRnYXJ0JywKICAgICAgICAgICAgICAgJy93aWtpL0JlbnV0emVyOllrc2t3YWRyYXQnLAogICAgICAgICAgICAgICAnL3dpa2kvQmVudXR6ZXI6TWF0dGhpYXNfUyVDMyVCQyVDMyU5RmVuJywKICAgICAgICAgICAgICAgJy93aWtpL0JlbnV0emVyOkNpcmRhbicsCiAgICAgICAgICAgICAgICcvd2lraS9CZW51dHplcjpNZWxsZWJnYScsCiAgICAgICAgICAgICAgICcvd2lraS9CZW51dHplcjpEZXJIZXhlcicsCiAgICAgICAgICAgICAgICcvd2lraS9CZW51dHplcjpOZXhvMjAnKQoKcXVlcnlGaWxlIDwtICdBY3Rpb253ZWVrMjAyMV9QYWdldmlld3MuaHFsJwpmaWxlTmFtZSA8LSBwYXN0ZTAoInBhZ2V2aWV3c18iLCBjZXREYXksICIudHN2IikKCiMgLSBjb2xsZWN0IFBhZ2V2aWV3cyBkYXRhCndtZGVfY29sbGVjdF9wYWdldmlld3ModXJpX2hvc3QsCiAgICAgICAgICAgICAgICAgICAgICAgdXJpX3BhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyKQoKIyMjIC0tLSBXcmFuZ2xlIFBhZ2V2aWV3cwojIC0gZnVuY3Rpb246IHdtZGVfcHJvY2Vzc19wYWdldmlld3MKd21kZV9wcm9jZXNzX3BhZ2V2aWV3cyA8LSBmdW5jdGlvbihmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhRGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmlfcXVlcnlfZmlsdGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhZ2VfZmlsdGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUgPSBjYW1wYWlnbk5hbWUpIHsKICAKICAjIC0gdG8gZGF0YURpcgogIHNldHdkKGRhdGFEaXIpCiAgCiAgIyAtIGxpYnJhcmllcwogIGxpYnJhcnkoc3RyaW5ncikKICBsaWJyYXJ5KGRwbHlyKQogIGxpYnJhcnkodGlkeXIpCiAgbGlicmFyeShkYXRhLnRhYmxlKQogIAogICMgLSBsb2FkCiAgcGFnZXZpZXdzRGF0YSA8LSByZWFkTGluZXMoZmlsZU5hbWUpCiAgd1N0YXJ0IDwtIHdoaWNoKGdyZXBsKCJ1cmlfaG9zdCIsIHBhZ2V2aWV3c0RhdGEpKQogIHBhZ2V2aWV3c0RhdGEgPC0gcGFnZXZpZXdzRGF0YVsod1N0YXJ0ICsgMSk6KGxlbmd0aChwYWdldmlld3NEYXRhKSAtIDEpXQogIHBhZ2V2aWV3c0RhdGEgPC0gZGF0YS5mcmFtZShkYXQgPSBwYWdldmlld3NEYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgcGFnZXZpZXdzRGF0YSA8LSBzZXBhcmF0ZShwYWdldmlld3NEYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaW50byA9IGMoJ3VyaV9ob3N0JywgJ3VyaV9wYXRoJywgJ3VyaV9xdWVyeScsICdyZWZlcmVyJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiKQogICMgLSBhcHBseSBwYWdlX2ZpbHRlcgogIHBhZ2V2aWV3c0RhdGEkcGFnZSA8LSBwYXN0ZTAocGFnZXZpZXdzRGF0YSR1cmlfaG9zdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWdldmlld3NEYXRhJHVyaV9wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkpCiAgd0ZpbHRlciA8LSBzYXBwbHkocGFnZXZpZXdzRGF0YSRwYWdlLCBmdW5jdGlvbih4KSB7CiAgICBzYXBwbHkocGFnZV9maWx0ZXIsIGZ1bmN0aW9uKHkpIHsKICAgICAgZ3JlcGwoeSwgeCkKICAgIH0pCiAgfSkKICB3RmlsdGVyIDwtIGNvbFN1bXMod0ZpbHRlcikKICB3RmlsdGVyIDwtIHdoaWNoKHdGaWx0ZXIgPiAwKQogIHBhZ2V2aWV3c0RhdGEgPC0gcGFnZXZpZXdzRGF0YVt3RmlsdGVyLCBdCiAgIyAtIGFwcGx5IHVyaV9xdWVyeV9maWx0ZXIKICAjIC0gTk9URTogbG9va2luZyBpbiBib3RoOiB1cmlfcXVlcnksIHJlZmVyZXIgCiAgd191cmlfcXVlcnkgPC0gd2hpY2goZ3JlcGwodXJpX3F1ZXJ5X2ZpbHRlciwgcGFnZXZpZXdzRGF0YSRwYWdlKSkKICAKICBpZiAobGVuZ3RoKHdfdXJpX3F1ZXJ5KSA+IDApIHsKICAgIAogICAgIyAtIGZpbHRlciBmb3Igd191cmlfcXVlcnkKICAgIHBhZ2V2aWV3c0RhdGEgPC0gcGFnZXZpZXdzRGF0YVt3X3VyaV9xdWVyeSwgXSAKICAgIAogICAgIyAtIGFnZ3JlZ2F0ZToKICAgIHBhZ2V2aWV3c0RhdGEkdXJpX3BhdGggPC0gcGFzdGUwKHBhZ2V2aWV3c0RhdGEkdXJpX2hvc3QsIHBhZ2V2aWV3c0RhdGEkdXJpX3BhdGgpCiAgICBwYWdldmlld3NEYXRhJHVyaV9ob3N0IDwtIE5VTEwKICAgIHBhZ2V2aWV3c0RhdGEkcGFnZSA8LSBOVUxMCiAgICBwYWdldmlld3NEYXRhJHJlZmVyZXIgPC0gTlVMTAogICAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhICU+JSAKICAgICAgZHBseXI6OnNlbGVjdCh1cmlfcXVlcnksIHVyaV9wYXRoKSAlPiUgCiAgICAgIGRwbHlyOjpncm91cF9ieSh1cmlfcXVlcnksIHVyaV9wYXRoKSAlPiUgCiAgICAgIGRwbHlyOjpzdW1tYXJpc2UocGFnZXZpZXdzID0gbigpKQogICAgY29sbmFtZXMocGFnZXZpZXdzRGF0YSkgPC0gYygnVGFnJywgJ1BhZ2UnLCAnUGFnZXZpZXdzJykKICAgIAogICAgIyAtIGFkZCBjZXREYXksIGNhbXBhaWduTmFtZQogICAgcGFnZXZpZXdzRGF0YSRkYXRlIDwtIGNldERheQogICAgcGFnZXZpZXdzRGF0YSRjYW1wYWlnbiA8LSBjYW1wYWlnbk5hbWUKICAgIAogICAgIyAtIHN0b3JlOgogICAgd3JpdGUuY3N2KHBhZ2V2aWV3c0RhdGEsIAogICAgICAgICAgICAgIHBhc3RlMChhbmFseXRpY3NEaXIsIAogICAgICAgICAgICAgICAgICAgICAicGFnZXZpZXdzQWdncmVnYXRlZF8iLAogICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdCgKICAgICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdChmaWxlTmFtZSwgc3BsaXQgPSAiXyIsIGZpeGVkID0gVClbWzFdXVsyXSwKICAgICAgICAgICAgICAgICAgICAgICBzcGxpdCA9ICIuIiwgCiAgICAgICAgICAgICAgICAgICAgICAgZml4ZWQgPSBUKVtbMV1dWzFdLAogICAgICAgICAgICAgICAgICAgICAiLmNzdiIKICAgICAgICAgICAgICApCiAgICApCiAgICAKICB9CiAgCn0KCiMgLSBzZXQgcGFyYW1zIHRvIHdtZGVfcHJvY2Vzc19wYWdldmlld3MKIyAtIGZvciB0aGUgV01ERSAyMDIwX0VtYWlsQ2FtcGFpZ25XaWtpcGVkaWFDaGFsbGVuZ2UKdXJpX3F1ZXJ5X2ZpbHRlciA8LSAnV01ERV9hdzIxJwpwYWdlX2ZpbHRlciA8LSBjKCdkZS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOkVkaXQtYS10aG9uL1dpcnRzY2hhZnRzdW5pdmVyc2l0JUMzJUE0dF9XaWVuX3p1bV9JbnRlcm5hdGlvbmFsZW5fRnJhdWVudGFnXzIwMjEnLAogICAgICAgICAgICAgICAgICdkZS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOkdMQU0nLAogICAgICAgICAgICAgICAgICdkZS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9MZXJuZVdpa2lwZWRpYScsCiAgICAgICAgICAgICAgICAgJ2RlLndpa2lwZWRpYS5vcmcvd2lraS9XaWtpcGVkaWE6V2lraVByb2pla3RfRnJhdWVuL0ZyYXVlbl9pbl9Sb3QnLAogICAgICAgICAgICAgICAgICdkZS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOlJlZGFrdGlvbmVuJywKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL1dpa2lwZWRpYTpTdHV0dGdhcnQjV29ya3Nob3BfRnJhdWVuX2luX2Rlcl9Qb2xpdGlrJywKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL1dpa2lwZWRpYTpXb21lbkVkaXQnLAogICAgICAgICAgICAgICAgICdkZS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOkZlbU5ldHoyMDIxJywKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL1dpa2lwZWRpYTo2MF9NaW51dGVuJywKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL1dpa2lwZWRpYSUzQVdob193cml0ZXNfaGlzX3RvcnklM0YnLAogICAgICAgICAgICAgICAgICdkZS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOlN0dXR0Z2FydCcsCiAgICAgICAgICAgICAgICAgJ2RlLndpa2lwZWRpYS5vcmcvd2lraS9CZW51dHplcjpZa3Nrd2FkcmF0JywKICAgICAgICAgICAgICAgICAnZGUud2lraXBlZGlhLm9yZy93aWtpL0JlbnV0emVyOk1hdHRoaWFzX1MlQzMlQkMlQzMlOUZlbicsCiAgICAgICAgICAgICAgICAgJ2RlLndpa2lwZWRpYS5vcmcvd2lraS9CZW51dHplcjpDaXJkYW4nLAogICAgICAgICAgICAgICAgICdkZS53aWtpcGVkaWEub3JnL3dpa2kvQmVudXR6ZXI6TWVsbGViZ2EnLAogICAgICAgICAgICAgICAgICdkZS53aWtpcGVkaWEub3JnL3dpa2kvQmVudXR6ZXI6RGVySGV4ZXInLAogICAgICAgICAgICAgICAgICdkZS53aWtpcGVkaWEub3JnL3dpa2kvQmVudXR6ZXI6TmV4bzIwJykKCiMgLSB3cmFuZ2xlIHBhZ2V2aWV3cwp3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzKGZpbGVOYW1lID0gZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpciA9IGRhdGFEaXIsCiAgICAgICAgICAgICAgICAgICAgICAgdXJpX3F1ZXJ5X2ZpbHRlciA9IHVyaV9xdWVyeV9maWx0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgIHBhZ2VfZmlsdGVyID0gcGFnZV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5ID0gY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSA9IGNhbXBhaWduTmFtZSkKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gVXNlciBSZWdpc3RyYXRpb25zCiMjIyAtLS0gSGl2ZVFMIHF1ZXJ5OiBldmVudC5TZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uIHRhYmxlCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmhpdmVRTCA8LQogICJTRUxFQ1QgeWVhciwgbW9udGgsIGRheSwgaG91ciwgIGV2ZW50LmNhbXBhaWduLCBldmVudC51c2VySWQsIGV2ZW50LnVzZXJOYW1lLCB3ZWJob3N0LCB3aWtpCiAgICAgIEZST00gZXZlbnQuc2VydmVyc2lkZWFjY291bnRjcmVhdGlvbgogICAgICBXSEVSRQogICAgICAgIFlFQVIgPSAyMDIxCiAgICAgICAgQU5EIG1vbnRoID0gMwogICAgICAgIEFORCBldmVudC5jYW1wYWlnbiBMSUtFICclV01ERV9hdzIxJScgCiAgICAgICAgQU5EIHdpa2kgPSAnZGV3aWtpJzsiCiMgLSB3cml0ZSBocWwKd3JpdGUoaGl2ZVFMLCBwYXN0ZTAoZGF0YURpciwgJ3VzZXJfcmVnaXN0cmF0aW9ucy5ocWwnKSkKIyMjIC0tLSBvdXRwdXQgZmlsZW5hbWUKZmlsZW5hbWUgPC0gcGFzdGUwKGFuYWx5dGljc0RpciwgJ3VzZXJfcmVnaXN0cmF0aW9ucy50c3YnKQojIyMgLS0tIGV4ZWN1dGUgaHFsIHNjcmlwdDoKaGl2ZUFyZ3MgPC0gJ3N1ZG8gLXUgYW5hbHl0aWNzLXByaXZhdGVkYXRhIGtlcmJlcm9zLXJ1bi1jb21tYW5kIGFuYWx5dGljcy1wcml2YXRlZGF0YSAvdXNyL2xvY2FsL2Jpbi9iZWVsaW5lIC0tc2lsZW50IC0taW5jcmVtZW50YWw9dHJ1ZSAtLXZlcmJvc2U9ZmFsc2UgLWYnCmhpdmVJbnB1dCA8LSBwYXN0ZShwYXN0ZTAoZGF0YURpciwgJ3VzZXJfcmVnaXN0cmF0aW9ucy5ocWwnKSwKICAgICAgICAgICAgICAgICAgICIgPiAiLAogICAgICAgICAgICAgICAgICAgZmlsZW5hbWUsCiAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKIyAtIGNvbW1hbmQ6CmhpdmVDb21tYW5kIDwtIHBhc3RlKGhpdmVBcmdzLCBoaXZlSW5wdXQpCnN5c3RlbShjb21tYW5kID0gaGl2ZUNvbW1hbmQsIHdhaXQgPSBUUlVFKQoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBVc2VyIEVkaXRzCiMjIyAtLS0gVXNlciBFZGl0cyB2aWEgcmV2aXNpb25fYWN0b3JfdGVtcAojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKbGlicmFyeShiaXQ2NCkKCnN0YXJ0VGltZXN0YW1wIDwtICcyMDIxMDMwODAwMDAwMCcKIyAtIGdldCB1c2VyIGlkcwp1c2VyUmVnaXN0cmF0aW9ucyA8LSBmcmVhZChwYXN0ZTAoYW5hbHl0aWNzRGlyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3VzZXJfcmVnaXN0cmF0aW9ucy50c3YnKSkKZGltKHVzZXJSZWdpc3RyYXRpb25zKQojIC0gY2xlYW4gdXAgZnJvbSB0ZXN0IGFjY291bnRzCnVzZXJSZWdpc3RyYXRpb25zIDwtIGZpbHRlcih1c2VyUmVnaXN0cmF0aW9ucywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAhZ3JlcGwoIl50ZXN0fF5UZXN0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlclJlZ2lzdHJhdGlvbnMkdXNlcm5hbWUpKQpkaW0odXNlclJlZ2lzdHJhdGlvbnMpCmhlYWQodXNlclJlZ2lzdHJhdGlvbnMpCnJldl91c2VyIDwtIHVzZXJSZWdpc3RyYXRpb25zJHVzZXJpZAojIC0gaXRlcmF0ZSBvdmVyIHJldl91c2VyCmZvciAoaSBpbiAxOmxlbmd0aChyZXZfdXNlcikpIHsKICAjIC0gU1FMIHF1ZXJ5CiAgc3FsUXVlcnkgPC0gcGFzdGUoIlwiU0VMRUNUIGFjdG9yLmFjdG9yX2lkLCAKICAgICAgICAgICAgICAgICAgICAgICAgYWN0b3IuYWN0b3JfdXNlciwgCiAgICAgICAgICAgICAgICAgICAgICAgIGFjdG9yLmFjdG9yX25hbWUsIAogICAgICAgICAgICAgICAgICAgICAgICByZXZpc2lvbl9hY3Rvcl90ZW1wLnJldmFjdG9yX3RpbWVzdGFtcCAKICAgICAgICAgICAgICAgICAgICBGUk9NIGFjdG9yIAogICAgICAgICAgICAgICAgICAgIExFRlQgSk9JTiByZXZpc2lvbl9hY3Rvcl90ZW1wIE9OIChhY3Rvci5hY3Rvcl9pZCA9IHJldmlzaW9uX2FjdG9yX3RlbXAucmV2YWN0b3JfYWN0b3IpIAogICAgICAgICAgICAgICAgICAgIFdIRVJFIChyZXZpc2lvbl9hY3Rvcl90ZW1wLnJldmFjdG9yX3RpbWVzdGFtcCA+PSAyMDIxMDMwODAwMDAwMCBBTkQgYWN0b3IuYWN0b3JfdXNlciA9ICIsIHJldl91c2VyW2ldLCAiKTtcIiIpOwogICMjIyAtLS0gb3V0cHV0IGZpbGVuYW1lCiAgZmlsZW5hbWUgPC0gcGFzdGUoZGF0YURpciwndXNlckVkaXRzJywgIl8iLCBpLCAiLnRzdiIsIHNlcCA9ICIiKQogICMjIyAtLS0gZXhlY3V0ZSBzcWwgc2NyaXB0OgogIHNxbExvZ0luUHJlIDwtIHBhc3RlMCgnL3Vzci9sb2NhbC9iaW4vYW5hbHl0aWNzLW15c3FsIGRld2lraSAtZSAnKQogIHNxbElucHV0IDwtIHBhc3RlKHNxbFF1ZXJ5LAogICAgICAgICAgICAgICAgICAgICIgPiAiLAogICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lLAogICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQogICMgLSBjb21tYW5kOgogIHNxbENvbW1hbmQgPC0gcGFzdGUoc3FsTG9nSW5QcmUsIHNxbElucHV0KQogIHN5c3RlbShjb21tYW5kID0gc3FsQ29tbWFuZCwgd2FpdCA9IFRSVUUpCiAgIyAtIHJlcG9ydAogIHByaW50KHBhc3RlMCgiRE9ORTogdXNlciAiLCBpLCAiLiIpKQp9CiMjIyAtLS0gRU5EIHJ1biBTUUwgc2NyaXB0cwojIC0gbG9hZCB1c2VyIGVkaXRzOgpsRiA8LSBsaXN0LmZpbGVzKGRhdGFEaXIpCmxGIDwtIGxGW2dyZXBsKCJedXNlckVkaXRzXyIsIGxGKV0KdXNlckVkaXRzIDwtIGxhcHBseShwYXN0ZTAoZGF0YURpciwgbEYpLCBmcmVhZCkKZWRpdGVkIDwtIHNhcHBseSh1c2VyRWRpdHMsIGZ1bmN0aW9uKHgpe2RpbSh4KVsxXSA+IDB9KQp1c2VyRWRpdHMgPC0gdXNlckVkaXRzW2VkaXRlZF0KdXNlckVkaXRzIDwtIHJiaW5kbGlzdCh1c2VyRWRpdHMpCiMgLSBzdG9yZSB1c2VyIGVkaXRzOgp3cml0ZS5jc3YodXNlckVkaXRzLCBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAndXNlckVkaXRzLmNzdicpKQpgYGAKCiMjIDAuIENhbXBhaWduIEJhbm5lcnMKClRoaXMgc2VjdGlvbiBwcmVzZW50cyBhbGwgZGF0YSBhbmQgc3RhdGlzdGljcyBvbiB0aGUgY2FtcGFpZ24gYmFubmVycy4gVGhlIGZvbGxvd2luZyBjaHVuayBsb2FkcyBhbmQgdGhlbiByZS1zdHJ1Y3R1cmVzIHRoZSBiYW5uZXJzIGRhdGFzZXQgYSBiaXQ6CgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KbEYgPC0gbGlzdC5maWxlcygnX2FuYWx5dGljcycpCmxGIDwtIGxGW2dyZXBsKCJeYmFubmVySW50ZXJhY3Rpb25zIiwgbEYpXQpkYXRhU2V0IDwtIGxhcHBseShwYXN0ZTAoIl9hbmFseXRpY3MvIiwgbEYpLCBmcmVhZCkKZGF0YVNldCA8LSByYmluZGxpc3QoZGF0YVNldCkKZGF0YVNldCRWMSA8LSBOVUxMCmRhdGFTZXQkY2FtcGFpZ24gPC0gTlVMTAojIC0gTkFzIHRvIHplcm8KZGF0YVNldFt3aGljaChpcy5uYShkYXRhU2V0KSwgYXJyLmluZCA9IFQpXSA8LSAwCiMgLSBEZXNpZ24KZGF0YVNldCRiYW5uZXIgPC0gc2FwcGx5KGRhdGFTZXQkYmFubmVybmFtZSwgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgiYjEiLCB4KSkge3JldHVybigiYjEiKX0gZWxzZSB7cmV0dXJuKCJiMiIpfQp9KQpkYXRhU2V0JGRldmljZSA8LSBnc3ViKCJXTURFX3dwMjBfYXdfYjFffFdNREVfd3AyMF9hd19iMl8iLCAiIiwgZGF0YVNldCRiYW5uZXJuYW1lKQpgYGAKCiMjIyAwLjEgQmFubmVyIEFjdGlvbnMgT3ZlcnZpZXcKCioqQ2hhcnQgMC4xLjEqKiBEYWlseSBCYW5uZXIgSW1wcmVzc2lvbnMsIENsaWNrcywgYW5kIENsb3N1cmVzLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KKipOT1RFLioqIExvZyh5KSBzY2FsZXMgd2VyZSB1c2VkIHRvIGhlbHAgcmVhZGFiaWxpdHk7IGRhdGEgbGFiZWxzIGFyZSBleGFjdCB2YWx1ZXMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF5LCBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnkpICU+JSAKICBncm91cF9ieShkYXkpICU+JSAKICBzdW1tYXJpc2Uoc2Vlbl9ieSA9IHN1bShzZWVuX2J5KSwgCiAgICAgICAgICAgIGNsb3NlZF9ieSA9IHN1bShjbG9zZWRfYnkpLCAKICAgICAgICAgICAgY2xpY2tlZF9ieSA9IHN1bShjbGlja2VkX2J5KSkKcEZyYW1lJGNsb3NlX3JhdGUgPSByb3VuZChwRnJhbWUkY2xvc2VkX2J5L3BGcmFtZSRzZWVuX2J5LCAyKQpwRnJhbWUkY2xpY2tfcmF0ZSA9IHJvdW5kKHBGcmFtZSRjbGlja2VkX2J5L3BGcmFtZSRzZWVuX2J5LCAyKQpwRnJhbWUgPC0gYXJyYW5nZShwRnJhbWUsIGRheSkKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn06IHNlZW5fYnksIGNsaWNrZWRfYnksIGNsb3NlZF9ieSwgZGFpbHk6CnBGIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KGRheSwgc2Vlbl9ieSwgY2xvc2VkX2J5LCBjbGlja2VkX2J5KSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ3NlZW5fYnknLCAnY2xvc2VkX2J5JywgJ2NsaWNrZWRfYnknKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkFjdGlvbiIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiVmFsdWUiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gbG9nKFZhbHVlKSwKICAgICAgICAgICAgICAgZ3JvdXAgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGNvbG9yID0gQWN0aW9uLAogICAgICAgICAgICAgICBmaWxsID0gQWN0aW9uLAogICAgICAgICAgICAgICBsYWJlbCA9IFZhbHVlLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnMjAyMSBBY3Rpb253ZWVrJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoImxvZyhBY3Rpb25zKSIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCioqQ2hhcnQgMC4xLjIqKiBUb3RhbCwgTWVhbiBwZXIgZGF5LCBhbmQgTWVkaWFuIHBlciBkYXkgQmFubmVyIEltcHJlc3Npb25zLCBDbGlja3MsIGFuZCBDbG9zdXJlcywgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CnBGcmFtZVRvdGFsIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChkYXksIHNlZW5fYnksIGNsaWNrZWRfYnksIGNsb3NlZF9ieSkgJT4lIAogIGdyb3VwX2J5KGRheSkgJT4lIAogIHN1bW1hcmlzZShzZWVuX2J5ID0gc3VtKHNlZW5fYnkpLCAKICAgICAgICAgICAgY2xvc2VkX2J5ID0gc3VtKGNsb3NlZF9ieSksIAogICAgICAgICAgICBjbGlja2VkX2J5ID0gc3VtKGNsaWNrZWRfYnkpCiAgICAgICAgICAgICkgCnBGcmFtZVRvdGFsIDwtIHBGcmFtZVRvdGFsICU+JSAKICBzdW1tYXJpc2Uoc2Vlbl9ieV9fdG90YWxfaW5fY2FtcGFpZ24gPSBzdW0oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnlfX3RvdGFsX2luX2NhbXBhaWduID0gc3VtKGNsb3NlZF9ieSksIAogICAgICAgICAgICBjbGlja2VkX2J5X190b3RhbF9pbl9jYW1wYWlnbiA9IHN1bShjbGlja2VkX2J5KSwKICAgICAgICAgICAgc2Vlbl9ieV9fbWVhbl9wZXJfZGF5ID0gbWVhbihzZWVuX2J5KSwgCiAgICAgICAgICAgIGNsb3NlZF9ieV9fbWVhbl9wZXJfZGF5ID0gbWVhbihjbG9zZWRfYnkpLCAKICAgICAgICAgICAgY2xpY2tlZF9ieV9fbWVhbl9wZXJfZGF5ID0gbWVhbihjbGlja2VkX2J5KSwKICAgICAgICAgICAgc2Vlbl9ieV9fbWVkaWFuX3Blcl9kYXkgPSBtZWRpYW4oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnlfX21lZGlhbl9wZXJfZGF5ID0gbWVkaWFuKGNsb3NlZF9ieSksIAogICAgICAgICAgICBjbGlja2VkX2J5X19tZWRpYW5fcGVyX2RheSA9IG1lZGlhbihjbGlja2VkX2J5KQogICAgICAgICAgICApICU+JSAKICB0KCkKcEZyYW1lVG90YWxbLCAxXSA8LSByb3VuZChwRnJhbWVUb3RhbFssIDFdLCAyKQpwRnJhbWVUb3RhbCA8LSBhcy5kYXRhLmZyYW1lKHBGcmFtZVRvdGFsKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKCdNZWFzdXJlJykKY29sbmFtZXMocEZyYW1lVG90YWwpWzJdIDwtICdWYWx1ZScKcEZyYW1lVG90YWwgPC1wRnJhbWVUb3RhbCAlPiUgCiAgc2VwYXJhdGUoJ01lYXN1cmUnLCAKICAgICAgICAgICBzZXAgPSAiX18iLCAKICAgICAgICAgICBpbnRvID0gYygnQWN0aW9uJywgJ1N0YXRpc3RpYycpKQojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfTogc2Vlbl9ieSwgY2xpY2tlZF9ieSwgY2xvc2VkX2J5LCBkYWlseToKZ2dwbG90KHBGcmFtZVRvdGFsLCBhZXMoeCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGxvZyhWYWx1ZSksCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gU3RhdGlzdGljLAogICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IFN0YXRpc3RpYywKICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBWYWx1ZSwKICAgICAgICAgICAgICAgICAgICApKSArCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArICAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIxIEFjdGlvbndlZWsnKSArCiAgeGxhYigiQWN0aW9uIikgKyB5bGFiKCJsb2coVmFsdWUpIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgoqKkNoYXJ0IDAuMS4zKiogRGFpbHkgQmFubmVyIENsaWNrIGFuZCBDbG9zZSByYXRlcywgYWdncmVnYXRlZCBhY3Jvc3MgdGhlIGNhbXBhaWduIGJhbm5lcnMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBjbG9zZV9yYXRlLCBjbGlja19yYXRlLCBkYWlseToKcEYgPC0gcEZyYW1lICU+JSAKICBzZWxlY3QoZGF5LCBjbGlja19yYXRlLCBjbG9zZV9yYXRlKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ2NsaWNrX3JhdGUnLCAnY2xvc2VfcmF0ZScpLCAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiQWN0aW9uIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJWYWx1ZSIpCmdncGxvdChwRiwgYWVzKHggPSBkYXksCiAgICAgICAgICAgICAgIHkgPSBWYWx1ZSwKICAgICAgICAgICAgICAgZ3JvdXAgPSBBY3Rpb24sCiAgICAgICAgICAgICAgIGNvbG9yID0gQWN0aW9uLAogICAgICAgICAgICAgICBmaWxsID0gQWN0aW9uLAogICAgICAgICAgICAgICBsYWJlbCA9IFZhbHVlLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnMjAyMSBBY3Rpb253ZWVrJykgKwogIHhsYWIoIkRhdGUiKSArIHlsYWIoIkFjdGlvbnMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKyAKICB5bGltKGMoMCwgLjI1KSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKKipUYWJsZSAwLjEuNCoqIE1lYW4gYW5kIE1lZGlhbiBDbGljayBhbmQgQ2xvc2UgcmF0ZXMgKGFjcm9zcyBkYXlzKQoqKk5vdGUuKiogVGhlIGRhdGEgYXJlIHByb2R1Y2UgYnkgY29tcHV0aW5nIG1lYW5zL21lZGlhbnMgYWNyb3NzIHRoZSBkYWlseSBjbGljay9jbG9zZSByYXRlcy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQp0RnJhbWUgPC0gZGF0YS5mcmFtZShjbGlja19yYXRlX21lYW4gPSByb3VuZChtZWFuKHBGcmFtZSRjbGlja19yYXRlKSwgNSksIAogICAgICAgICAgICAgICAgICAgICBjbGlja19yYXRlX21lZGlhbiA9IHJvdW5kKG1lZGlhbihwRnJhbWUkY2xpY2tfcmF0ZSksIDUpLAogICAgICAgICAgICAgICAgICAgICBjbG9zZV9yYXRlX21lYW4gPSByb3VuZChtZWFuKHBGcmFtZSRjbG9zZV9yYXRlKSwgNSksCiAgICAgICAgICAgICAgICAgICAgIGNsb3NlX3JhdGVfbWVkaWFuID0gcm91bmQobWVkaWFuKHBGcmFtZSRjbG9zZV9yYXRlKSwgNSkKKQpkYXRhdGFibGUodEZyYW1lKQpgYGAKCioqVGFibGUgMC4xLjUqKiBEZSBmYWN0byBDbGljayBhbmQgQ2xvc2UgcmF0ZXMgaW4gcGVyY2VudHMKKipOb3RlLioqIFRoZSBkYXRhIGFyZSBwcm9kdWNlIGJ5IGNvbXB1dGluZyBtZWFucy9tZWRpYW5zIGFjcm9zcyB0aGUgb3ZlcmFsbCBzdW1zIG9mIGNsaWNrcyBhbmQgY2xvc3VyZXMgaW4gdGhlIGNhbXBhaWduIChpLmUuIGJ5IGFnZ3JlZ2F0aW5nIGFjcm9zcyB0aGUgY2FtcGFpZ24gZGF5cykuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdEZyYW1lIDwtIGRhdGEuZnJhbWUoY2xpY2tfcmF0ZSA9IHJvdW5kKHN1bShwRnJhbWUkY2xpY2tlZF9ieSkvc3VtKHBGcmFtZSRzZWVuX2J5KSoxMDAsIDUpLCAKICAgICAgICAgICAgICAgICAgICAgY2xvc2VfcmF0ZSA9IHJvdW5kKHN1bShwRnJhbWUkY2xvc2VkX2J5KS9zdW0ocEZyYW1lJHNlZW5fYnkpKjEwMCwgNSkpCmRhdGF0YWJsZSh0RnJhbWUpCmBgYAoKKipDaGFydCAwLjEuNioqIERhaWx5IEJhbm5lciBDbGljayBhbmQgQ2xvc2UgcmF0ZXMsIGJ5ICoqZGV2aWNlcyoqLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gMTB9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF5LCBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRldmljZSkgJT4lIAogIGdyb3VwX2J5KGRheSwgZGV2aWNlKSAlPiUgCiAgc3VtbWFyaXNlKHNlZW5fYnkgPSBzdW0oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnkgPSBzdW0oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnkgPSBzdW0oY2xpY2tlZF9ieSkpCnBGcmFtZSRjbG9zZV9yYXRlID0gcm91bmQocEZyYW1lJGNsb3NlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lJGNsaWNrX3JhdGUgPSByb3VuZChwRnJhbWUkY2xpY2tlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXkpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRhaWx5OgpwRiA8LSBwRnJhbWUgJT4lIAogIHNlbGVjdChkYXksIGRldmljZSwgc2Vlbl9ieSwgY2xvc2VkX2J5LCBjbGlja2VkX2J5KSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ3NlZW5fYnknLCAnY2xvc2VkX2J5JywgJ2NsaWNrZWRfYnknKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkFjdGlvbiIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiVmFsdWUiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gVmFsdWUsCiAgICAgICAgICAgICAgIGdyb3VwID0gQWN0aW9uLAogICAgICAgICAgICAgICBjb2xvciA9IEFjdGlvbiwKICAgICAgICAgICAgICAgZmlsbCA9IEFjdGlvbgogICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAofmRldmljZSwgbnJvdyA9IDMsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZ3RpdGxlKCcyMDIxIEFjdGlvbndlZWsnKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiQWN0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCioqQ2hhcnQgMC4xLjZCKiogTWVhbiwgbWVkaWFuLCBhbmQgdG90YWwgSW1wcmVzc2lvbnMsIGNsaWNrcyBhbmQgY2xvc2luZyBjbGlja3MgKipwZXIgYmFubmVyKiogLCAqKnBlciBkZXZpY2UqKi4KKipOT1RFLioqIExvZyh5KSBzY2FsZXMgd2VyZSB1c2VkIHRvIGhlbHAgcmVhZGFiaWxpdHk7IGRhdGEgbGFiZWxzIGFyZSBleGFjdCB2YWx1ZXMuCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDMwfQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OnNlbGVjdChzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRldmljZSwgYmFubmVyKQpwRnJhbWUgPC0gcEZyYW1lICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoZGV2aWNlLCBiYW5uZXIpICU+JSAKICBzdW1tYXJpc2UobWVhbjBzZWVuX2J5ID0gbWVhbihzZWVuX2J5KSwKICAgICAgICAgICAgbWVhbjBjbGlja2VkX2J5ID0gbWVhbihjbGlja2VkX2J5KSwKICAgICAgICAgICAgbWVhbjBjbG9zZWRfYnkgPSBtZWFuKGNsb3NlZF9ieSksCiAgICAgICAgICAgIG1lZGlhbjBzZWVuX2J5ID0gbWVkaWFuKHNlZW5fYnkpLAogICAgICAgICAgICBtZWRpYW4wY2xpY2tlZF9ieSA9IG1lZGlhbihjbGlja2VkX2J5KSwKICAgICAgICAgICAgbWVkaWFuMGNsb3NlZF9ieSA9IG1lZGlhbihjbG9zZWRfYnkpLAogICAgICAgICAgICB0b3RhbDBzZWVuX2J5ID0gc3VtKHNlZW5fYnkpLAogICAgICAgICAgICB0b3RhbDBjbGlja2VkX2J5ID0gc3VtKGNsaWNrZWRfYnkpLAogICAgICAgICAgICB0b3RhbDBjbG9zZWRfYnkgPSBzdW0oY2xvc2VkX2J5KQogICAgICAgICAgICApCnBGcmFtZSA8LSBwRnJhbWUgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gYygnbWVhbjBzZWVuX2J5JywgJ21lYW4wY2xpY2tlZF9ieScsICdtZWFuMGNsb3NlZF9ieScsCiAgICAgICAgICAgICAgICAgICAgICAgICdtZWRpYW4wc2Vlbl9ieScsICdtZWRpYW4wY2xpY2tlZF9ieScsICdtZWRpYW4wY2xvc2VkX2J5JywKICAgICAgICAgICAgICAgICAgICAgICAgJ3RvdGFsMHNlZW5fYnknLCAndG90YWwwY2xpY2tlZF9ieScsICd0b3RhbDBjbG9zZWRfYnknKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gJ21lYXN1cmUnLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gJ3ZhbHVlJykKcEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgc2VwYXJhdGUobWVhc3VyZSwKICAgICAgICAgICBpbnRvID0gYygnbWVhc3VyZScsICdhY3Rpb24nKSwKICAgICAgICAgICBzZXAgPSAiMCIpCnBGcmFtZSR2YWx1ZSA8LSByb3VuZChwRnJhbWUkdmFsdWUsIDEpCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gbWVhc3VyZSwKICAgICAgICAgICAgICAgeSA9IGxvZyh2YWx1ZSksCiAgICAgICAgICAgICAgIGdyb3VwID0gYWN0aW9uLAogICAgICAgICAgICAgICBjb2xvciA9IGFjdGlvbiwKICAgICAgICAgICAgICAgZmlsbCA9IGFjdGlvbiwKICAgICAgICAgICAgICAgbGFiZWwgPSB2YWx1ZSwKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAoZGV2aWNlfmJhbm5lciwgbmNvbCA9IDIsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZ3RpdGxlKCcyMDIxIEFjdGlvbndlZWsnKSArCiAgeGxhYigiTWVhc3VyZSIpICsgeWxhYigibG9nKFZhbHVlKSIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCgoqKkNoYXJ0IDAuMS43KiogRGFpbHkgQmFubmVyIENsaWNrIGFuZCBDbG9zZSByYXRlcywgYnkgKipiMS9iMioqLCBhZ2dyZWdhdGVkIGFjcm9zcyB0aGUgY2FtcGFpZ24gYmFubmVycy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gMTJ9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF5LCBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGJhbm5lcikgJT4lIAogIGdyb3VwX2J5KGRheSwgYmFubmVyKSAlPiUgCiAgc3VtbWFyaXNlKHNlZW5fYnkgPSBzdW0oc2Vlbl9ieSksIAogICAgICAgICAgICBjbG9zZWRfYnkgPSBzdW0oY2xvc2VkX2J5KSwgCiAgICAgICAgICAgIGNsaWNrZWRfYnkgPSBzdW0oY2xpY2tlZF9ieSkpCnBGcmFtZSRjbG9zZV9yYXRlID0gcm91bmQocEZyYW1lJGNsb3NlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lJGNsaWNrX3JhdGUgPSByb3VuZChwRnJhbWUkY2xpY2tlZF9ieS9wRnJhbWUkc2Vlbl9ieSwgMikKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXkpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9OiBzZWVuX2J5LCBjbGlja2VkX2J5LCBjbG9zZWRfYnksIGRhaWx5OgpwRiA8LSBwRnJhbWUgJT4lIAogIHNlbGVjdChkYXksIGJhbm5lciwgc2Vlbl9ieSwgY2xvc2VkX2J5LCBjbGlja2VkX2J5KSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoJ3NlZW5fYnknLCAnY2xvc2VkX2J5JywgJ2NsaWNrZWRfYnknKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkFjdGlvbiIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiVmFsdWUiKQpnZ3Bsb3QocEYsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICB5ID0gVmFsdWUsCiAgICAgICAgICAgICAgIGdyb3VwID0gQWN0aW9uLAogICAgICAgICAgICAgICBjb2xvciA9IEFjdGlvbiwKICAgICAgICAgICAgICAgZmlsbCA9IEFjdGlvbiwKICAgICAgICAgICAgICAgbGFiZWwgPSBWYWx1ZSwKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAofmJhbm5lciwgbnJvdyA9IDIsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZ3RpdGxlKCcyMDIxIEFjdGlvbndlZWsnKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiQWN0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCioqQ2hhcnQgMC4xLjdCKiogTWVhbiwgbWVkaWFuLCBhbmQgdG90YWwgSW1wcmVzc2lvbnMsIGNsaWNrcyBhbmQgY2xvc2luZyBjbGlja3MgKipwZXIgYmFubmVyKiouCioqTk9URS4qKiBMb2coeSkgc2NhbGVzIHdlcmUgdXNlZCB0byBoZWxwIHJlYWRhYmlsaXR5OyBkYXRhIGxhYmVscyBhcmUgZXhhY3QgdmFsdWVzLgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6c2VsZWN0KHNlZW5fYnksIGNsaWNrZWRfYnksIGNsb3NlZF9ieSwgYmFubmVyKQpwRnJhbWUgPC0gcEZyYW1lICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoYmFubmVyKSAlPiUgCiAgc3VtbWFyaXNlKG1lYW4wc2Vlbl9ieSA9IG1lYW4oc2Vlbl9ieSksCiAgICAgICAgICAgIG1lYW4wY2xpY2tlZF9ieSA9IG1lYW4oY2xpY2tlZF9ieSksCiAgICAgICAgICAgIG1lYW4wY2xvc2VkX2J5ID0gbWVhbihjbG9zZWRfYnkpLAogICAgICAgICAgICBtZWRpYW4wc2Vlbl9ieSA9IG1lZGlhbihzZWVuX2J5KSwKICAgICAgICAgICAgbWVkaWFuMGNsaWNrZWRfYnkgPSBtZWRpYW4oY2xpY2tlZF9ieSksCiAgICAgICAgICAgIG1lZGlhbjBjbG9zZWRfYnkgPSBtZWRpYW4oY2xvc2VkX2J5KSwKICAgICAgICAgICAgdG90YWwwc2Vlbl9ieSA9IHN1bShzZWVuX2J5KSwKICAgICAgICAgICAgdG90YWwwY2xpY2tlZF9ieSA9IHN1bShjbGlja2VkX2J5KSwKICAgICAgICAgICAgdG90YWwwY2xvc2VkX2J5ID0gc3VtKGNsb3NlZF9ieSkKICAgICAgICAgICAgKQpwRnJhbWUgPC0gcEZyYW1lICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9IGMoJ21lYW4wc2Vlbl9ieScsICdtZWFuMGNsaWNrZWRfYnknLCAnbWVhbjBjbG9zZWRfYnknLAogICAgICAgICAgICAgICAgICAgICAgICAnbWVkaWFuMHNlZW5fYnknLCAnbWVkaWFuMGNsaWNrZWRfYnknLCAnbWVkaWFuMGNsb3NlZF9ieScsCiAgICAgICAgICAgICAgICAgICAgICAgICd0b3RhbDBzZWVuX2J5JywgJ3RvdGFsMGNsaWNrZWRfYnknLCAndG90YWwwY2xvc2VkX2J5JyksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICdtZWFzdXJlJywgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICd2YWx1ZScpCnBGcmFtZSA8LSBwRnJhbWUgJT4lIAogIHNlcGFyYXRlKG1lYXN1cmUsCiAgICAgICAgICAgaW50byA9IGMoJ21lYXN1cmUnLCAnYWN0aW9uJyksCiAgICAgICAgICAgc2VwID0gIjAiKQpwRnJhbWUkdmFsdWUgPC0gcm91bmQocEZyYW1lJHZhbHVlLCAxKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IG1lYXN1cmUsCiAgICAgICAgICAgICAgIHkgPSBsb2codmFsdWUpLAogICAgICAgICAgICAgICBncm91cCA9IGFjdGlvbiwKICAgICAgICAgICAgICAgY29sb3IgPSBhY3Rpb24sCiAgICAgICAgICAgICAgIGZpbGwgPSBhY3Rpb24sCiAgICAgICAgICAgICAgIGxhYmVsID0gdmFsdWUsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBmYWNldF93cmFwKH5iYW5uZXIsIG5yb3cgPSAzLCBzY2FsZXMgPSAiZnJlZSIpICsgCiAgZ2d0aXRsZSgnMjAyMSBBY3Rpb253ZWVrJykgKwogIHhsYWIoIk1lYXN1cmUiKSArIHlsYWIoImxvZyhWYWx1ZSkiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgoKKipUYWJsZSAwLjEuOCoqIERhaWx5IEJhbm5lciBJbXByZXNzaW9uczogc2VlbiBieSwgY2xvc2VkIGJ5LCBjbGlja2VkIGJ5IC0gZnVsbCBkYXRhc2V0LgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMjIyAtLS0gRnVsbCBEYXRhc2V0IChUYWJsZSBSZXBvcnQpCmRTIDwtIGRhdGFTZXQKZFMkYmFubmVybmFtZSA8LSBnc3ViKCJXTURFX3dwMjBfYXdfIiwgIiIsIGRTJGJhbm5lcm5hbWUpCmRTIDwtIGRTWywKICAgICAgICAgYygnYmFubmVybmFtZScsICdkYXknLCAnZGV2aWNlJywgJ2Jhbm5lcicsIAogICAgICAgICAgICdzZWVuX2J5JywgJ2Nsb3NlZF9ieScsICdjbGlja2VkX2J5JyldCmRhdGF0YWJsZShkUywgCiAgICAgICAgICBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gMzApCiAgICAgICAgICApCmBgYAoKIyMgMS4gQ2FtcGFpZ24gUGFnZXZpZXdzCgpUaGlzIHNlY3Rpb24gcHJlc2VudHMgYWxsIGRhdGEgYW5kIHN0YXRpc3RpY3Mgb24gdGhlIGNhbXBhaWduIHBhZ2VzLgoKVGhlIGZvbGxvd2luZyBjaHVuayBsb2FkcyBhbmQgdGhlbiByZS1zdHJ1Y3R1cmVzIHRoZSBkYXRhc2V0IGEgYml0LgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmxGIDwtIGxpc3QuZmlsZXMoJ19hbmFseXRpY3MnKQpsRiA8LSBsRltncmVwbCgiXnBhZ2V2aWV3c0FnZ3JlZ2F0ZWQiLCBsRildCmRhdGFTZXQgPC0gbGFwcGx5KHBhc3RlMCgiX2FuYWx5dGljcy8iLCBsRiksIGZyZWFkKQpkYXRhU2V0IDwtIHJiaW5kbGlzdChkYXRhU2V0KQpkYXRhU2V0JFYxIDwtIE5VTEwKZGF0YVNldCRjYW1wYWlnbiA8LSBOVUxMCiMgLSBleHBhbmQgZ3JpZCB0byBhY2NvdW50IGZvciBtaXNzaW5nIG9ic2VydmF0aW9ucyBwZXIgZGF5CmRTIDwtIGV4cGFuZC5ncmlkKHVuaXF1ZShkYXRhU2V0JFRhZyksIAogICAgICAgICAgICAgICAgICB1bmlxdWUoZGF0YVNldCRQYWdlKSwgCiAgICAgICAgICAgICAgICAgIHVuaXF1ZShkYXRhU2V0JGRhdGUpLCAKICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGRTKSA8LSBjKCdUYWcnLCAnUGFnZScsICdkYXRlJykKZFMgPC0gZFMgJT4lIAogIGxlZnRfam9pbihkYXRhU2V0LCAKICAgICAgICAgICAgYnkgPSBjKCJUYWciLCAiUGFnZSIsICJkYXRlIikpCmRhdGFTZXQgPC0gZFM7IHJtKGRTKQpkYXRhU2V0JFBhZ2V2aWV3c1tpcy5uYShkYXRhU2V0JFBhZ2V2aWV3cyldIDwtIDAKZGF0YVNldCRUYWcgPC0gZ3N1YigiJi4rJCIsICIiLCBkYXRhU2V0JFRhZykKZGF0YVNldCRUYWcgPC0gZ3N1YigiXFw/Y2FtcGFpZ249IiwgIiIsIGRhdGFTZXQkVGFnKQpkYXRhU2V0JGRhdGUgPC0gc2FwcGx5KGRhdGFTZXQkZGF0ZSwgZnVuY3Rpb24oeCkgewogIGQgPC0gc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV0KICBpZiAobmNoYXIoZFszXSkgPT0gMSkgewogICAgZFszXSA8LSBwYXN0ZTAoIjAiLCBkWzNdKQogIH0KICByZXR1cm4ocGFzdGUoZCwgY29sbGFwc2UgPSAiLSIpKQp9KQpkYXRhU2V0IDwtIGRhdGFTZXQgJT4lIAogIGZpbHRlcighKGdyZXBsKCJeJSIsIGRhdGFTZXQkVGFnKSkpCmBgYAoKIyMjIDEuMSBQYWdldmlld3MgT3ZlcnZpZXcKCioqQ2hhcnQgMS4xLjEqKiBEYWlseSBQYWdldmlld3MsIGFnZ3JlZ2F0ZWQgYWNyb3NzIHRoZSBjYW1wYWlnbiBjaGFubmVscy4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDZ9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF0ZSwgUGFnZSwgUGFnZXZpZXdzKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgUGFnZSkgJT4lIAogIHN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKcEZyYW1lIDwtIGFycmFuZ2UocEZyYW1lLCBkYXRlKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41LCBncm91cCA9IDEpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGZhY2V0X3dyYXAoflBhZ2UsIG5jb2wgPSAyKSArIAogIGdndGl0bGUoJzIwMjEgQWN0aW9ud2VlaycpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJQYWdldmlld3MiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogICMgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDEwKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCioqVGFibGUgMS4xLjEqKiBQYWdldmlld3MgdG90YWxzCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgc2VsZWN0KFBhZ2UsIFBhZ2V2aWV3cykgJT4lIAogIGdyb3VwX2J5KFBhZ2UpICU+JSAKICBzdW1tYXJpc2UodG90YWxQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkgJT4lIAogIGFycmFuZ2UoZGVzYyh0b3RhbFBhZ2V2aWV3cykpCmRhdGF0YWJsZSh0RnJhbWUpCmBgYAoKIyMjIDEuMiBQYWdldmlld3M6IENhbXBhaWduIENoYW5uZWxzCgoqKkNoYXJ0IDEuMi4xKiogUGFnZXZpZXdzLCBieSAqKmNoYW5uZWxzKioKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDZ9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QoZGF0ZSwgVGFnLCBQYWdldmlld3MpICU+JSAKICBncm91cF9ieShkYXRlLCBUYWcpICU+JSAKICBzdW1tYXJpc2UoUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZGF0ZSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSwgZ3JvdXAgPSAxKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBmYWNldF93cmFwKH5UYWcsIG5jb2wgPSAyKSArIAogIGdndGl0bGUoJzIwMjEgQWN0aW9ud2VlaycpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJQYWdldmlld3MiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogICMgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDYpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKKipDaGFydCAxLjIuMioqIFRvdGFsIFBhZ2V2aWV3cywgYnkgKipjaGFubmVscyoqCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CnBGcmFtZSA8LSBwRnJhbWUgJT4lIAogIHNlbGVjdChUYWcsIFBhZ2V2aWV3cykgJT4lIAogIGdyb3VwX2J5KFRhZykgJT4lIAogIHN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhQYWdldmlld3MpKQpwRnJhbWUkVGFnIDwtIGZhY3RvcihwRnJhbWUkVGFnLAogICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBwRnJhbWUkVGFnLAogICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBUYWcsCiAgICAgICAgICAgICAgICAgICB5ID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgY29sb3IgPSAiYmx1ZSIsIGZpbGwgPSAid2hpdGUiKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJzIwMjEgQWN0aW9ud2VlaycpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJQYWdldmlld3MiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMy41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKKipUYWJsZSAyLjIuMSoqIFRvdGFsIHBhZ2V2aWV3cywgYnkgKipjaGFubmVscyoqIGFuZCBieSAqKnBhZ2VzKiouCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHNlbGVjdChUYWcsIFBhZ2UsIFBhZ2V2aWV3cykgJT4lIAogIGdyb3VwX2J5KFRhZywgUGFnZSkgJT4lIAogIHN1bW1hcmlzZSh0b3RhbFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKSAlPiUgCiAgYXJyYW5nZShkZXNjKHRvdGFsUGFnZXZpZXdzKSkKZGF0YXRhYmxlKHRGcmFtZSkKYGBgCgojIyAyLiBVc2VyIFJlZ2lzdHJhdGlvbnMKClRoaXMgc2VjdGlvbiBwcmVzZW50cyBhbGwgZGF0YSBhbmQgc3RhdGlzdGljcyBvbiB0aGUgdXNlciByZWdpc3RyYXRpb25zLgoKIyMjIDIuMSBVc2VyIFJlZ2lzdHJhdGlvbnMgT3ZlcnZpZXcKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpsRiA8LSBsaXN0LmZpbGVzKCJfYW5hbHl0aWNzIikKbEYgPC0gbEZbZ3JlcGwoIl51c2VyX3JlZ2lzdHJhdGlvbnMiLCBsRildCmRhdGFTZXQgPC0gZnJlYWQocGFzdGUwKCJfYW5hbHl0aWNzLyIsIGxGKSkKZGF0YVNldCA8LSBmaWx0ZXIoZGF0YVNldCwgCiAgICAgICAgICAgICAgICAgICFncmVwbCgiXnRlc3R8XlRlc3QiLCBkYXRhU2V0JHVzZXJuYW1lKSkKZGF0YVNldCRkYXRlIDwtIHBhc3RlKGRhdGFTZXQkeWVhciwgCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UobmNoYXIoZGF0YVNldCRtb250aCkgPT0gMSwgcGFzdGUwKCIwIiwgZGF0YVNldCRtb250aCksIGRhdGFTZXQkbW9udGgpLAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKG5jaGFyKGRhdGFTZXQkZGF5KSA9PSAxLCBwYXN0ZTAoIjAiLCBkYXRhU2V0JGRheSksIGRhdGFTZXQkZGF5KSwKICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICItIikKcmVnVXNlcnMgPC0gc2VsZWN0KGRhdGFTZXQsCiAgICAgICAgICAgICAgICAgICB1c2VyaWQsCiAgICAgICAgICAgICAgICAgICB1c2VybmFtZSwgCiAgICAgICAgICAgICAgICAgICBjYW1wYWlnbikKIyAtIHRvdGFsIG51bWJlciBvZiByZWdpc3RlcmVkIHVzZXJzCiMgIC0gdG90YWxfcmVnaXN0ZXJlZCA8LSBkaW0ocmVnVXNlcnMpWzFdCiMgLSBleHBhbmQgZ3JpZCB0byBhY2NvdW50IGZvciBtaXNzaW5nIG9ic2VydmF0aW9ucyBwZXIgZGF5CmRhdGVTcGFuIDwtIHNlcShmcm9tID0gYXMuRGF0ZSgiMjAyMS0wMy0wOCIpLCAKICAgICAgICAgICAgICAgIHRvID0gYXMuRGF0ZSgiMjAyMS0wMy0yMSIpLCAKICAgICAgICAgICAgICAgIGJ5ID0gImRheSIpCmRhdGVTcGFuIDwtIGFzLmNoYXJhY3RlcihkYXRlU3BhbikKZFMgPC0gZXhwYW5kLmdyaWQoZGF0ZVNwYW4sIAogICAgICAgICAgICAgICAgICB1bmlxdWUoZGF0YVNldCRjYW1wYWlnbiksIAogICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXMoZFMpIDwtIGMoJ2RhdGUnLCAnY2FtcGFpZ24nKQpkUyA8LSBkUyAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChkYXRhU2V0LCBkYXRlLCBjYW1wYWlnbiwgdXNlcmlkKSwKICAgICAgICAgICAgYnkgPSBjKCJkYXRlIiwgImNhbXBhaWduIikpCmRTJHVzZXJpZCA8LSBpZmVsc2UoaXMubmEoZFMkdXNlcmlkKSwgMCwgMSkKdG90YWxfcmVnaXN0ZXJlZCA8LSBzdW0oZFMkdXNlcmlkKQpwRnJhbWUgPC0gZFMgJT4lIAogIHNlbGVjdChkYXRlLCB1c2VyaWQpICU+JSAKICBncm91cF9ieShkYXRlKSAlPiUgCiAgc3VtbWFyaXNlKHJlZ2lzdHJhdGlvbnMgPSBzdW0odXNlcmlkKSkgJT4lIAogIGFycmFuZ2UoZGF0ZSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IHJlZ2lzdHJhdGlvbnMsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHJlZ2lzdHJhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSwgZ3JvdXAgPSAxKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIxIEFjdGlvbndlZWsnKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiUmVnaXN0cmF0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNykpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgClRoZSB0b3RhbCBudW1iZXIgb2YgdXNlcnMgcmVnaXN0ZXJlZCBpbiB0aGlzIGNhbXBhaWduIGlzIGByIHt0b3RhbF9yZWdpc3RlcmVkfWAuCgoKIyMjIDIuMiBVc2VyIFJlZ2lzdHJhdGlvbnMgcGVyIENhbXBhaWduIENoYW5uZWwKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDh9CnBGcmFtZSA8LSBkUyAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgY2FtcGFpZ24pICU+JSAKICBzdW1tYXJpc2UocmVnaXN0cmF0aW9ucyA9IHN1bSh1c2VyaWQpKSAlPiUgCiAgYXJyYW5nZShkYXRlKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gcmVnaXN0cmF0aW9ucywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gcmVnaXN0cmF0aW9ucywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjI1LCBncm91cCA9IDEpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIGZhY2V0X3dyYXAofmNhbXBhaWduLCBuY29sID0gMikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIxIEFjdGlvbndlZWsnKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiUmVnaXN0cmF0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgIyBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA2KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCiMjIyAyLjMgVG90YWwgVXNlciBSZWdpc3RyYXRpb25zIHBlciBDYW1wYWlnbiBDaGFubmVsCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CnBGcmFtZSA8LSBkUyAlPiUgCiAgc2VsZWN0KGNhbXBhaWduLCB1c2VyaWQpICU+JSAKICBncm91cF9ieSggY2FtcGFpZ24pICU+JSAKICBzdW1tYXJpc2UocmVnaXN0cmF0aW9ucyA9IHN1bSh1c2VyaWQpKSAlPiUgCiAgYXJyYW5nZShkZXNjKHJlZ2lzdHJhdGlvbnMpKQpwRnJhbWUkY2FtcGFpZ24gPC0gZmFjdG9yKHBGcmFtZSRjYW1wYWlnbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gcEZyYW1lJGNhbXBhaWduLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBjYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgIHkgPSByZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSByZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsdWUiLCBmaWxsID0gIndoaXRlIikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIxIEFjdGlvbndlZWsnKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiUmVnaXN0cmF0aW9ucyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyAzLiBVc2VyIEVkaXRzCgpUaGlzIHNlY3Rpb24gcHJlc2VudHMgYWxsIGRhdGEgYW5kIHN0YXRpc3RpY3Mgb24gdGhlIHVzZXIgZWRpdHMuCgojIyMgMy4xIFVzZXIgRWRpdHMgT3ZlcnZpZXcKClRoZSBmb2xsb3dpbmcgY2h1bmsgbG9hZHMgdGhlIGRhdGFzZXQgb2YgdXNlciBpbnRlcmFjdGlvbnMgd2l0aCBjYW1wYWlnbiBjaGFubmVscyBhbmQgdGhlbiByZS1zdHJ1Y3R1cmVzIHRoZSBkYXRhc2V0IGEgYml0LgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CmxGIDwtIGxpc3QuZmlsZXMoIl9hbmFseXRpY3MvIikKbEYgPC0gbEZbZ3JlcGwoIl51c2VyRWRpdHMiLCBsRildCmVkaXRTZXQgPC0gZnJlYWQocGFzdGUwKCJfYW5hbHl0aWNzLyIsIGxGKSkKZWRpdFNldCA8LSBzZWxlY3QoZWRpdFNldCwKICAgICAgICAgICAgICAgICAgYWN0b3JfbmFtZSwgCiAgICAgICAgICAgICAgICAgIHJldmFjdG9yX3RpbWVzdGFtcCkgICAgICAgICAgICAgICAgIApjb2xuYW1lcyhlZGl0U2V0KSA8LSBjKCd1c2VybmFtZScsICdyZXZfdGltZXN0YW1wJykKZGF0YVNldCA8LSBsZWZ0X2pvaW4oZWRpdFNldCwKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHJlZ1VzZXJzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZXJuYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ24pLAogICAgICAgICAgICAgICAgICAgICBieSA9ICJ1c2VybmFtZSIpCmRhdGFTZXQgPC0gZGF0YVNldFtjb21wbGV0ZS5jYXNlcyhkYXRhU2V0KSwgXQpkYXRhU2V0JHllYXIgPC0gc3Vic3RyKGRhdGFTZXQkcmV2X3RpbWVzdGFtcCwgMSwgNCkKZGF0YVNldCRtb250aCA8LSBzdWJzdHIoZGF0YVNldCRyZXZfdGltZXN0YW1wLCA1LCA2KQpkYXRhU2V0JGRheSA8LSBzdWJzdHIoZGF0YVNldCRyZXZfdGltZXN0YW1wLCA3LCA4KQpkYXRhU2V0JHJldl90aW1lc3RhbXAgPC0gTlVMTApkYXRhU2V0JGRhdGUgPC0gcGFzdGUoZGF0YVNldCR5ZWFyLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGFTZXQkbW9udGgsCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YVNldCRkYXksCiAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIi0iKQpkYXRhU2V0IDwtIHNlbGVjdChkYXRhU2V0LAogICAgICAgICAgICAgICAgICBkYXRlLAogICAgICAgICAgICAgICAgICB1c2VybmFtZSwKICAgICAgICAgICAgICAgICAgY2FtcGFpZ24pCmRhdGVTcGFuIDwtIHNlcShmcm9tID0gYXMuRGF0ZSgiMjAyMS0wMy0wOCIpLCAKICAgICAgICAgICAgICAgIHRvID0gYXMuRGF0ZShtYXgoZGF0YVNldCRkYXRlKSksIAogICAgICAgICAgICAgICAgYnkgPSAiZGF5IikKZGF0ZVNwYW4gPC0gYXMuY2hhcmFjdGVyKGRhdGVTcGFuKQpkUyA8LSBleHBhbmQuZ3JpZChkYXRlU3BhbiwgCiAgICAgICAgICAgICAgICAgIHVuaXF1ZShkYXRhU2V0JGNhbXBhaWduKSwKICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGRTKSA8LSBjKCdkYXRlJywgJ2NhbXBhaWduJykKZFMgPC0gZFMgJT4lIAogIGxlZnRfam9pbihzZWxlY3QoZGF0YVNldCwgZGF0ZSwgY2FtcGFpZ24sIHVzZXJuYW1lKSwKICAgICAgICAgICAgYnkgPSBjKCJkYXRlIiwgImNhbXBhaWduIikpCmRTJHVzZXJuYW1lIDwtIGlmZWxzZShpcy5uYShkUyR1c2VybmFtZSksIDAsIDEpCnBGcmFtZSA8LSBkUyAlPiUKICBzZWxlY3QoZGF0ZSwgdXNlcm5hbWUpICU+JSAKICBncm91cF9ieShkYXRlKSAlPiUgCiAgc3VtbWFyaXNlKGVkaXRzID0gc3VtKHVzZXJuYW1lKSkgJT4lIAogIGFycmFuZ2UoZGF0ZSkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IGVkaXRzLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBlZGl0cywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fcGF0aChzaXplID0gLjI1LCBncm91cCA9IDEpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJzIwMjEgQWN0aW9ud2VlaycpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJFZGl0cyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNykpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyMgMy4yIFVzZXIgRWRpdHMgYnkgQ2FtcGFpZ24gQ2hhbm5lbHMKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDh9CnBGcmFtZSA8LSBkUyAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgY2FtcGFpZ24pICU+JSAKICBzdW1tYXJpc2UoZWRpdHMgPSBzdW0odXNlcm5hbWUpKSAlPiUgCiAgYXJyYW5nZShkYXRlKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gZWRpdHMsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGVkaXRzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuMjUsIGdyb3VwID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgZmFjZXRfd3JhcCh+Y2FtcGFpZ24sIG5jb2wgPSAyKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJzIwMjEgQWN0aW9ud2VlaycpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJFZGl0cyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgIyBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA1LjUpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKIyMjIDMuMyBUb3RhbCBVc2VyIEVkaXRzIGJ5IENhbXBhaWduIENoYW5uZWxzCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDV9CnBGcmFtZSA8LSBkUyAlPiUgCiAgc2VsZWN0KGNhbXBhaWduLCB1c2VybmFtZSkgJT4lIAogIGdyb3VwX2J5KCBjYW1wYWlnbikgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IHN1bSh1c2VybmFtZSkpICU+JSAKICBhcnJhbmdlKGRlc2MoZWRpdHMpKQpwRnJhbWUkY2FtcGFpZ24gPC0gZmFjdG9yKHBGcmFtZSRjYW1wYWlnbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gcEZyYW1lJGNhbXBhaWduLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCkKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBjYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgIHkgPSBlZGl0cywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZWRpdHMsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgY29sb3IgPSAiYmx1ZSIsIGZpbGwgPSAid2hpdGUiKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJzIwMjEgQWN0aW9ud2VlaycpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJFZGl0cyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojIyMgMy40IEVkaXQgQ2xhc3NlcwoKIyMjIyAzLjQuMSBFZGl0IENsYXNzZXM6IGFsbCB1c2VycwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CnVzZXJDbGFzcyA8LSBkYXRhU2V0ICU+JSAKICBzZWxlY3QodXNlcm5hbWUpICU+JSAKICBncm91cF9ieSh1c2VybmFtZSkgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IG4oKSkKZWRpdEJvdW5kYXJpZXMgPC0gbGlzdCgKICBjKDAsIDEpLCAKICBjKDIsIDQpLAogIGMoNSwgOSksCiAgYygxMCwgMjApLAogIGMoMjEsIDUwKSwKICBjKDUxLCAxMDApLCAKICBjKDEwMSwgMTAwMDAwMCkKKQp1c2VyQ2xhc3MkZWRpdENsYXNzIDwtIHNhcHBseSh1c2VyQ2xhc3MkZWRpdHMsIGZ1bmN0aW9uKHgpIHsKICB3RUMgPC0gc2FwcGx5KGVkaXRCb3VuZGFyaWVzLCBmdW5jdGlvbih5KSB7CiAgICB4ID49IHlbMV0gJiB4IDw9IHlbMl0KICB9KQogIGlmIChzdW0od0VDKSA9PSAwKSB7CiAgICByZXR1cm4oIj4gMTAwIikKICB9IGVsc2UgewogICAgcmV0dXJuKHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMV0sCiAgICAgICAgICAgICAgICAgICIgLSAiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzJdLCAKICAgICAgICAgICAgICAgICAgIikiCiAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0pCmVkaXRDbGFzcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHVzZXJDbGFzcyRlZGl0Q2xhc3MpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmNvbG5hbWVzKGVkaXRDbGFzcykgPC0gYygnRWRpdCBDbGFzcycsICdOdW0uVXNlcnMnKQplZGl0Q2xhc3Mkb3JkZXIgPC0gYXMubnVtZXJpYyhzYXBwbHkoZWRpdENsYXNzJGBFZGl0IENsYXNzYCwgZnVuY3Rpb24oeCkgewogIGxvd2VyIDwtIHN0cl9leHRyYWN0KHgsICdbWzpkaWdpdDpdXSsnKQp9KSkKZWRpdENsYXNzIDwtIGFycmFuZ2UoZWRpdENsYXNzLCBvcmRlcikKZWRpdENsYXNzJG9yZGVyIDwtIE5VTEwKZWRpdENsYXNzJGBFZGl0IENsYXNzYFtlZGl0Q2xhc3MkYEVkaXQgQ2xhc3NgID09ICcoMCAtIDEpJ10gPC0gJygxKScKZWRpdENsYXNzJGBFZGl0IENsYXNzYFtlZGl0Q2xhc3MkYEVkaXQgQ2xhc3NgID09ICcoMTAxIC0gMWUrMDYpJ10gPC0gJyg+IDEwMCknCmRhdGF0YWJsZShlZGl0Q2xhc3MpCmBgYAoKIyMjIyAzLjQuMiBFZGl0IENsYXNzZXMgcGVyIENhbXBhaWduIENoYW5uZWxzCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuNX0KZWRDbGFzc2VzIDwtIGxhcHBseSh1bmlxdWUoZGF0YVNldCRjYW1wYWlnbiksIGZ1bmN0aW9uKHgpIHsKICB1c2VyQ2xhc3MgPC0gZGF0YVNldCAlPiUgCiAgZmlsdGVyKGNhbXBhaWduID09IHgpICU+JSAKICBzZWxlY3QodXNlcm5hbWUpICU+JSAKICBncm91cF9ieSh1c2VybmFtZSkgJT4lIAogIHN1bW1hcmlzZShlZGl0cyA9IG4oKSkKZWRpdEJvdW5kYXJpZXMgPC0gbGlzdCgKICBjKDAsIDEpLCAKICBjKDIsIDQpLAogIGMoNSwgOSksCiAgYygxMCwgMjApLAogIGMoMjEsIDUwKSwKICBjKDUxLCAxMDApLCAKICBjKDEwMSwgMTAwMDAwMCkKKQp1c2VyQ2xhc3MkZWRpdENsYXNzIDwtIHNhcHBseSh1c2VyQ2xhc3MkZWRpdHMsIGZ1bmN0aW9uKHgpIHsKICB3RUMgPC0gc2FwcGx5KGVkaXRCb3VuZGFyaWVzLCBmdW5jdGlvbih5KSB7CiAgICB4ID49IHlbMV0gJiB4IDw9IHlbMl0KICB9KQogIGlmIChzdW0od0VDKSA9PSAwKSB7CiAgICByZXR1cm4oIj4gMTAwIikKICB9IGVsc2UgewogICAgcmV0dXJuKHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMV0sCiAgICAgICAgICAgICAgICAgICIgLSAiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzJdLCAKICAgICAgICAgICAgICAgICAgIikiCiAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0pCnVzZXJDbGFzcyRlZGl0Q2xhc3NbdXNlckNsYXNzJGVkaXRDbGFzcyA9PSAnKDAgLSAxKSddIDwtICcoMSknCnVzZXJDbGFzcyRlZGl0Q2xhc3NbdXNlckNsYXNzJGVkaXRDbGFzcyA9PSAnKDEwMSAtIDFlKzA2KSddIDwtICcoPiAxMDApJwplZGl0Q2xhc3MgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZSh1c2VyQ2xhc3MkZWRpdENsYXNzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhlZGl0Q2xhc3MpIDwtIGMoJ0VkaXQgQ2xhc3MnLCAnTnVtLlVzZXJzJykKZWRpdENsYXNzJG9yZGVyIDwtIGFzLm51bWVyaWMoc2FwcGx5KGVkaXRDbGFzcyRgRWRpdCBDbGFzc2AsIGZ1bmN0aW9uKHgpIHsKICBsb3dlciA8LSBzdHJfZXh0cmFjdCh4LCAnW1s6ZGlnaXQ6XV0rJykKfSkpCmVkaXRDbGFzcyA8LSBhcnJhbmdlKGVkaXRDbGFzcywgb3JkZXIpCmVkaXRDbGFzcyRvcmRlciA8LSBOVUxMCmVkaXRDbGFzcyRjYW1wYWlnbiA8LSB4CnJldHVybihlZGl0Q2xhc3MpCn0pCmVkQ2xhc3NlcyA8LSByYmluZGxpc3QoZWRDbGFzc2VzKQplZENsYXNzZXMkY2FtcGFpZ24gPC0gc2FwcGx5KGVkQ2xhc3NlcyRjYW1wYWlnbiwgZnVuY3Rpb24oeCkgewogIGQgPC0gc3Ryc3BsaXQoeCwgIl8iKVtbMV1dCiAgaWYgKG5jaGFyKHRhaWwoZCwgMSkpID09IDEpIHsKICAgIGRbbGVuZ3RoKGQpXSA8LSBwYXN0ZTAoIjAiLCBkW2xlbmd0aChkKV0pCiAgfQogIGQgPC0gcGFzdGUoZCwgY29sbGFwc2UgPSAiXyIpCiAgcmV0dXJuKGQpCiAgfSkKZWRDbGFzc2VzIDwtIGFycmFuZ2UoZWRDbGFzc2VzLCBjYW1wYWlnbikKZWRDbGFzc2VzJGBFZGl0IENsYXNzYFtlZENsYXNzZXMkYEVkaXQgQ2xhc3NgID09ICcoMCAtIDEpJ10gPC0gJygxKScKZWRDbGFzc2VzJGBFZGl0IENsYXNzYCA8LSBmYWN0b3IoZWRDbGFzc2VzJGBFZGl0IENsYXNzYCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoJygxKScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJygyIC0gNCknLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnKDUgLSA5KScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJygxMCAtIDIwKScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJygyMSAtIDUwKScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcoNTEgLSAxMDApJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnKD4gMTAwKScpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCmdncGxvdChlZENsYXNzZXMsIAogICAgICAgYWVzKHggPSBgRWRpdCBDbGFzc2AsIAogICAgICAgICAgIHkgPSBgTnVtLlVzZXJzYCwgCiAgICAgICAgICAgbGFiZWwgPSBgTnVtLlVzZXJzYCkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJ3aGl0ZSIpICsgCiAgZmFjZXRfd3JhcCh+Y2FtcGFpZ24pICsgCiAgZ2d0aXRsZSgnMjAyMSBBY3Rpb253ZWVrJykgKwogIHhsYWIoIkVkaXQgQ2xhc3MiKSArIHlsYWIoIk51bS51c2VycyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgo=