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

The campaign is run from 2021/10/18 to 2021/10/31.

CURRENT UPDATE: Complete dataset as of 2021/10/31.

0. Data Acquisiton

NOTE: the Data Acquisition code chunk is not fully reproducible from this Report. The data are collected by running an R script on stat1007.eqiad.wmnet, collecting the data as .tsv and .csv files, copying manually, and processing locally.

0.1 Daily Update

### ----------------------------------------------------------------
### --- WMDE 2021 Ocassional Editors
### --- https://phabricator.wikimedia.org/T291635
### ----------------------------------------------------------------

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

# - Landing Pages:
# - Landing Page 1: https://de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/DeinEngagement
# - Landing Page 2a: https://tools.wmflabs.org/mitmachen/
# - Landing Page 2b: https://de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/LerneWikipedia
# - Landing Page 2c: https://de.wikipedia.org/wiki/Wikipedia:F%C3%B6rderung/F%C3%B6rderangebote
# - Campaign tags:
# - WMDE_oceditors_fall_2021

# - Start of the banner campaign: October 12th
# - End of the banner campaign: October 26 th
# - Tracking test: October 5 - 7
# - Preliminary report for tracking: beginning of November
# - Track editing behavior four weeks after end of campaign: November 23rd
# - final report for tracking: beginning of December

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

### --- campaign specifics
campaignName <- 'OccasionalEditors_2021'

### --- dir structure
campaignPath <- '/home/goransm/Analytics/NewEditors/2021_OccasionalEditors/'
dataDir <- paste0(campaignPath, "_data/")
analyticsDir <- paste0(campaignPath, "_analytics/")
publicDir <- '/srv/published/datasets/wmde-analytics-engineering/NewEditors/campaigns/2021_OccasionalEditors/'

### --- 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 { 
    
    # - 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"
              )
    )
    
    # - process bannerData
    bannerData <- t(table(bannerData$banneraction,
                          bannerData$bannername))
    bannerData <- as.data.frame(bannerData)
    colnames(bannerData) <- c('banner', 'action', 'count')
    bannerData$day <- cetDay
    bannerData$campaign <- campaignName
    
    # - store:
    write.csv(bannerData, 
              paste0(analyticsDir, 
                     "bannerInteractionsAggregated_",
                     strsplit(
                       strsplit(fileName, split = "_", fixed = T)[[1]][2],
                       split = ".", 
                       fixed = T)[[1]][1],
                     ".csv"
              ))
  }
  
}

# - set params for wmde_banner_actions()
queryFile <- paste0(campaignName, "_bannerInteractions.hql")
fileName <- paste0("bannerInteractions_", cetDay, ".tsv")
uri_query_filter <- 'WMDE_oceditors_fall_2021'
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:Wikimedia_Deutschland/DeinEngagement',
               '/wiki/Wikipedia:Wikimedia_Deutschland/LerneWikipedia', 
               '/wiki/Wikipedia:F%C3%B6rderung/F%C3%B6rderangebote')
queryFile <- 'WMDE_oceditors_fall_2021_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
  wFilter1 <- unique(unname(unlist(sapply(page_filter, function (x) {
    which(grepl(x, pageviewsData$uri_path))
  }))))
  pageviewsData <- pageviewsData[wFilter1, ]
  # - apply uri_query_filter
  w_uri_query_1 <- which(grepl(uri_query_filter, pageviewsData$uri_query))
  w_uri_query_2 <- which(grepl(uri_query_filter, pageviewsData$referer))
  w_uri_query <- unique(c(w_uri_query_1, w_uri_query_2))

  if (length(w_uri_query) > 0) {
    
    # - filter for w_uri_query
    pageviewsData <- pageviewsData[w_uri_query, ] 
    
    # - copy uri_query from referer where uri_query is empty:
    wCuriq <- which(pageviewsData$uri_query == "")
    curiq <- stringr::str_extract(pageviewsData$referer[wCuriq], 
                                  paste0(uri_query_filter, ".+$"))
    pageviewsData$uri_query[wCuriq] <- curiq
    # - remove ?campaign= from pageviewsData$uri_query
    pageviewsData$uri_query <- gsub("?campaign=", 
                                    "",
                                    pageviewsData$uri_query, 
                                    fixed = T)
    
    # - 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
uri_query_filter <- 'WMDE_oceditors_fall_2021'
page_filter <- c('/wiki/Wikipedia:Wikimedia_Deutschland/DeinEngagement',
                 '/wiki/Wikipedia:Wikimedia_Deutschland/LerneWikipedia', 
                 '/wiki/Wikipedia:F%C3%B6rderung/F%C3%B6rderangebote')

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


### ---------------------------------------------------------------------------
### --- Compose and copy to publicDir
### ---------------------------------------------------------------------------

lF <- list.files(analyticsDir)
lF <- lF[grepl("Aggregated", lF)]
lFp <- lF[grepl("pageviews", lF)]
lFp <- lapply(paste0(analyticsDir, lFp),
              data.table::fread)
lFp <- data.table::rbindlist(lFp)
write.csv(lFp, 
          paste0(publicDir, 
                 "2021_OccasionalEditors_Pageviews.csv"))
lFb <- lF[grepl("banner", lF)]
lFb <- lapply(paste0(analyticsDir, lFb),
              data.table::fread)
lFb <- data.table::rbindlist(lFb)
write.csv(lFb, 
          paste0(publicDir, 
                 "2021_OccasionalEditors_Banners.csv"))


### ----------------------------------------------------------
### --- User Edits
### ----------------------------------------------------------

1. Campaign Banners

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

2. Pageviews

2.1 Pageviews Overview

Chart 2.1.1. Pageviews Overview.

dataSet <- read.csv(
  '_reporting/2021_OccasionalEditors_Pageviews.csv',
                    header = T,
                    row.names = 1,
                    check.names = F,
                    stringsAsFactors = F)
dataSet$V1 <- NULL
dataSet <- dataSet %>% 
  dplyr::filter(!(grepl("2021-11", date)))
dataSet$Tag <- sapply(dataSet$Tag, function(x) {
  if (grepl("ipad$", x)) {
    "ipad"
  } else if (grepl("mobile$", x)) {
    "mobile"
  } else {
    "desktop"
  }
})
dataSet$Page <- sapply(dataSet$Page, function(x) {
  if (grepl("F%C3%B6rderangebote", x)) {
    return("Förderung/Förderangebote")
  } else if (grepl("DeinEngagement", x)) {
    return("DeinEngagement ")
  } else {
    return("LerneWikipedia")
  }
})
dataSet <- dataSet %>% 
  dplyr::select(-campaign) %>% 
  dplyr::group_by(date, Tag, Page) %>% 
  dplyr::summarise(Pageviews = sum(Pageviews))

# - Visualize w. {ggplot2}
ggplot(dataSet, aes(x = date,
                    y = log(Pageviews),
                    group = Page,
                    color = Page,
                    fill = Page,
                    label = Pageviews,
                    )) + 
  geom_path(size = .25) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, color = "white") +
  scale_y_continuous(labels = comma) +
  ggtitle('Occasional Editors 2021: Pageviews') +
  ylab("log(Pageviews)") + 
  theme_minimal() + 
  geom_text_repel(size = 6, show.legend = FALSE) + 
  facet_wrap(~Tag, ncol = 2) + 
  theme(axis.text.x = element_text(angle = 90, size = 12)) +
  theme(plot.title = element_text(size = 15)) +
  theme(legend.title = element_blank()) + 
  theme(legend.text = element_text(size = 15)) +
  theme(strip.text =  element_text(size = 15)) +
  theme(legend.position = "top")

Table 2.2.1. Pageviews Overview

### --- Full Dataset (Table Report)
datatable(dataSet %>% arrange(desc(Pageviews)))

2.2 Pageviews Daily

pFrame <- dataSet %>% 
  dplyr::select(date, Pageviews) %>% 
  dplyr::group_by(date) %>% 
  dplyr::summarise(Pageviews = sum(Pageviews))
Adding missing grouping variables: `Tag`
# - Visualize w. {ggplot2}
ggplot(pFrame, aes(x = date,
                    y = Pageviews,
                    label = Pageviews,
                    )) + 
  geom_line(size = .25, group = 1) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, color = "white") +
  scale_y_continuous(labels = comma) +
  ggtitle('Occasional Editors 2021: Pageviews') +
  theme_minimal() + 
  geom_text_repel(size = 6, show.legend = FALSE) + 
  theme(axis.text.x = element_text(angle = 90, size = 12)) +
  theme(plot.title = element_text(size = 15)) +
  theme(legend.title = element_blank()) + 
  theme(legend.text = element_text(size = 15)) +
  theme(legend.position = "top")

2.3 Pageviews per Tag

pFrame <- dataSet %>% 
  dplyr::select(Tag, Pageviews) %>% 
  dplyr::group_by(Tag) %>% 
  dplyr::summarise(Pageviews = sum(Pageviews))
Adding missing grouping variables: `date`
# - Visualize w. {ggplot2}
ggplot(pFrame, aes(x = Tag,
                   y = Pageviews,
                   label = Pageviews)) +
  geom_bar(stat = "identity", fill = "deepskyblue") +
  scale_y_continuous(labels = comma) +
  ggtitle('Occasional Editors 2021: Pageviews') +
  theme_minimal() + 
  geom_text_repel(size = 6, show.legend = FALSE) + 
  theme(axis.text.x = element_text(angle = 90, size = 12)) +
  theme(plot.title = element_text(size = 15)) +
  theme(legend.title = element_blank()) + 
  theme(legend.text = element_text(size = 15)) +
  theme(legend.position = "top")

2.3.1 Pageviews per Tag: pageviews per device - Landing Page 1 (DeinEngagement) only

pFrame <- dataSet %>% 
  ungroup() %>% 
  dplyr::filter(Page == "DeinEngagement ") %>% 
  dplyr::select(Tag, Pageviews) %>% 
  dplyr::group_by(Tag) %>% 
  dplyr::summarise(Pageviews = sum(Pageviews))

# - Visualize w. {ggplot2}
ggplot(pFrame, aes(x = Tag,
                   y = Pageviews,
                   label = Pageviews)) +
  geom_bar(stat = "identity", fill = "deepskyblue") +
  scale_y_continuous(labels = comma) +
  ggtitle('Occasional Editors 2021: Pageviews') +
  theme_minimal() + 
  geom_text_repel(size = 6, show.legend = FALSE) + 
  theme(axis.text.x = element_text(angle = 90, size = 12)) +
  theme(plot.title = element_text(size = 15)) +
  theme(legend.title = element_blank()) + 
  theme(legend.text = element_text(size = 15)) +
  theme(legend.position = "top")

2.3.2 Pageviews per Tag: pageviews per device - Landing Pages 2a and 2b

pFrame <- dataSet %>% 
  ungroup() %>% 
  dplyr::filter(Page != "DeinEngagement ") %>% 
  dplyr::select(Tag, Pageviews) %>% 
  dplyr::group_by(Tag) %>% 
  dplyr::summarise(Pageviews = sum(Pageviews))

# - Visualize w. {ggplot2}
ggplot(pFrame, aes(x = Tag,
                   y = Pageviews,
                   label = Pageviews)) +
  geom_bar(stat = "identity", fill = "deepskyblue") +
  scale_y_continuous(labels = comma) +
  ggtitle('Occasional Editors 2021: Pageviews') +
  theme_minimal() + 
  geom_text_repel(size = 6, show.legend = FALSE) + 
  theme(axis.text.x = element_text(angle = 90, size = 12)) +
  theme(plot.title = element_text(size = 15)) +
  theme(legend.title = element_blank()) + 
  theme(legend.text = element_text(size = 15)) +
  theme(legend.position = "top")

2.4 Pageviews per Page

pFrame <- dataSet %>% 
  dplyr::select(Page, Pageviews) %>% 
  dplyr::group_by(Page) %>% 
  dplyr::summarise(Pageviews = sum(Pageviews))
Adding missing grouping variables: `date`, `Tag`
# - Visualize w. {ggplot2}
ggplot(pFrame, aes(x = Page,
                   y = Pageviews,
                   label = Pageviews)) +
  geom_bar(stat = "identity", fill = "darkorange") +
  scale_y_continuous(labels = comma) +
  ggtitle('Occasional Editors 2021: Pageviews') +
  theme_minimal() + 
  geom_text_repel(size = 6, show.legend = FALSE) + 
  theme(axis.text.x = element_text(angle = 90, size = 12)) +
  theme(plot.title = element_text(size = 15)) +
  theme(legend.title = element_blank()) + 
  theme(legend.text = element_text(size = 15)) +
  theme(legend.position = "top")

3. Editing Behaviour

dataSet <- read.csv(
  '_analytics/userEdits_FINAL_ANON.csv',
                    header = T,
                    row.names = 1,
                    check.names = F,
                    stringsAsFactors = F)
dataSet$banner <- sapply(dataSet$banner, function(x) {
  if (grepl("ipad$", x)) {
    "ipad"
  } else if (grepl("mobile$", x)) {
    "mobile"
  } else {
    "desktop"
  }
})

3.1 Edits per tag

Edits per tag during the campaign:

3.2 Daily edits per tag

Daily edits per tag during the campaign:

3.4 Daily edits (total)

Daily edits (total) during the campaign:

3.4 Edit classes

The following overview of edit classes includes the 0/1 edits class split and

editorFrame <- expand.grid(anon_userid = unique(dataSet$anon_userid),
                           campaignDay = unique(dataSet$campaignDay))
editorFrame <- dplyr::filter(editorFrame, !is.na(campaignDay))
ds <- dataSet %>% 
  dplyr::select(anon_userid, campaignDay, ts_revision)
editorFrame <- editorFrame %>% 
  dplyr::left_join(ds, 
                   by = c("anon_userid", "campaignDay"))
editorFrame$ts_revision <- ifelse(is.na(editorFrame$ts_revision), 
                                  0, 
                                  1)
editorFrame <- editorFrame %>% 
  dplyr::select(anon_userid, campaignDay, ts_revision) %>% 
  dplyr::group_by(anon_userid, campaignDay) %>% 
  dplyr::summarise(edits = sum(ts_revision))

editorFrameTotal <- editorFrame %>% 
  dplyr::select(anon_userid, edits) %>% 
  dplyr::group_by(anon_userid) %>% 
  dplyr::summarise(edits = sum(edits))

editBoundaries <- list(
  c(0, 0),
  c(1, 1), 
  c(2, 4),
  c(5, 9),
  c(10, 20),
  c(21, 50),
  c(51, 100)
)

editorFrameTotal$editClass <- sapply(editorFrameTotal$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(editorFrameTotal$editClass), 
                           stringsAsFactors = F)
editClass[editClass == "(0 - 0)"] <- "0"
editClass[editClass == "(1 - 1)"] <- "1"
colnames(editClass) <- c('Edit Class', 'Num.Users')
editClass$order <- as.numeric(sapply(editClass$`Edit Class`, function(x) {
  lower <- str_extract(x, '[[:digit:]]+')
}))
editClass <- arrange(editClass, order)
editClass$order <- NULL
datatable(editClass)

3.5 Daily user edits before, during, and after campaign

pFrame <- dataSet %>% 
  dplyr::filter(!is.na(ts_revision)) %>% 
  dplyr::select(ts_revision, campaignDay) %>% 
  dplyr::group_by(ts_revision, campaignDay) %>% 
  dplyr::summarise(Edits = n())
colnames(pFrame) <- c("date", "campaign", "Edits")
ggplot(pFrame, aes(x = date,
                   y = Edits,
                   group = campaign,
                   color = campaign,
                   fill = campaign,
                   label = Edits,
                    )) + 
  geom_path(size = .5) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  geom_text_repel(size = 3.5, show.legend = FALSE) + 
  scale_y_continuous(labels = comma) +
  ggtitle('2021 WMDE Occasional Editors Banner Campaign') +
  xlab("Date") + ylab("Edits") + 
  theme_minimal() + 
  theme(axis.text.x = element_text(angle = 90, size = 6)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) + 
  theme(legend.position = "top")

tFrame <- pFrame %>% 
  dplyr::select(campaign, Edits) %>% 
  dplyr::group_by(campaign) %>% 
  dplyr::summarise(totalEdits = sum(Edits),
                   avgEdits = round(mean(Edits), 2))
Adding missing grouping variables: `date`
DT::datatable(tFrame)

Edit Classes: Before Campaign

editorFrame <- expand.grid(anon_userid = unique(dataSet$anon_userid),
                           campaignDay = unique(dataSet$campaignDay))
editorFrame <- dplyr::filter(editorFrame, !is.na(campaignDay))
ds <- dataSet %>% 
  dplyr::select(anon_userid, campaignDay, ts_revision)
editorFrame <- editorFrame %>% 
  dplyr::left_join(ds, 
                   by = c("anon_userid", "campaignDay"))
editorFrame$ts_revision <- ifelse(is.na(editorFrame$ts_revision), 
                                  0, 
                                  1)
editorFrame <- editorFrame %>% 
  dplyr::select(anon_userid, campaignDay, ts_revision) %>% 
  dplyr::group_by(anon_userid, campaignDay) %>% 
  dplyr::summarise(edits = sum(ts_revision)) %>% 
  dplyr::filter(campaignDay == "beforeCampaign")

editBoundaries <- list(
  c(0, 0),
  c(1, 1), 
  c(2, 4),
  c(5, 9),
  c(10, 20),
  c(21, 50),
  c(51, 100)
)

editorFrame$editClass <- sapply(editorFrame$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(editorFrame$editClass), 
                           stringsAsFactors = F)
editClass[editClass == "(0 - 0)"] <- "0"
editClass[editClass == "(1 - 1)"] <- "1"
colnames(editClass) <- c('Edit Class', 'Num.Users')
editClass$order <- as.numeric(sapply(editClass$`Edit Class`, function(x) {
  lower <- str_extract(x, '[[:digit:]]+')
}))
editClass <- arrange(editClass, order)
editClass$order <- NULL
datatable(editClass)

Edit Classes: During Campaign

editorFrame <- expand.grid(anon_userid = unique(dataSet$anon_userid),
                           campaignDay = unique(dataSet$campaignDay))
editorFrame <- dplyr::filter(editorFrame, !is.na(campaignDay))
ds <- dataSet %>% 
  dplyr::select(anon_userid, campaignDay, ts_revision)
editorFrame <- editorFrame %>% 
  dplyr::left_join(ds, 
                   by = c("anon_userid", "campaignDay"))
editorFrame$ts_revision <- ifelse(is.na(editorFrame$ts_revision), 
                                  0, 
                                  1)
editorFrame <- editorFrame %>% 
  dplyr::select(anon_userid, campaignDay, ts_revision) %>% 
  dplyr::group_by(anon_userid, campaignDay) %>% 
  dplyr::summarise(edits = sum(ts_revision)) %>% 
  dplyr::filter(campaignDay == "Campaign")

editBoundaries <- list(
  c(0, 0),
  c(1, 1), 
  c(2, 4),
  c(5, 9),
  c(10, 20),
  c(21, 50),
  c(51, 100)
)

editorFrame$editClass <- sapply(editorFrame$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(editorFrame$editClass), 
                           stringsAsFactors = F)
editClass[editClass == "(0 - 0)"] <- "0"
editClass[editClass == "(1 - 1)"] <- "1"
colnames(editClass) <- c('Edit Class', 'Num.Users')
editClass$order <- as.numeric(sapply(editClass$`Edit Class`, function(x) {
  lower <- str_extract(x, '[[:digit:]]+')
}))
editClass <- arrange(editClass, order)
editClass$order <- NULL
datatable(editClass)

Edit Classes: After Campaign

editorFrame <- expand.grid(anon_userid = unique(dataSet$anon_userid),
                           campaignDay = unique(dataSet$campaignDay))
editorFrame <- dplyr::filter(editorFrame, !is.na(campaignDay))
ds <- dataSet %>% 
  dplyr::select(anon_userid, campaignDay, ts_revision)
editorFrame <- editorFrame %>% 
  dplyr::left_join(ds, 
                   by = c("anon_userid", "campaignDay"))
editorFrame$ts_revision <- ifelse(is.na(editorFrame$ts_revision), 
                                  0, 
                                  1)
editorFrame <- editorFrame %>% 
  dplyr::select(anon_userid, campaignDay, ts_revision) %>% 
  dplyr::group_by(anon_userid, campaignDay) %>% 
  dplyr::summarise(edits = sum(ts_revision)) %>% 
  dplyr::filter(campaignDay == "afterCampaign")

editBoundaries <- list(
  c(0, 0),
  c(1, 1), 
  c(2, 4),
  c(5, 9),
  c(10, 20),
  c(21, 50),
  c(51, 100)
)

editorFrame$editClass <- sapply(editorFrame$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(editorFrame$editClass), 
                           stringsAsFactors = F)
editClass[editClass == "(0 - 0)"] <- "0"
editClass[editClass == "(1 - 1)"] <- "1"
colnames(editClass) <- c('Edit Class', 'Num.Users')
editClass$order <- as.numeric(sapply(editClass$`Edit Class`, function(x) {
  lower <- str_extract(x, '[[:digit:]]+')
}))
editClass <- arrange(editClass, order)
editClass$order <- NULL
datatable(editClass)
dewikiEdits <- read.csv(paste0(getwd(),"/_reporting/", "avg_edits_dewiki.csv"),
                      header = TRUE, 
                      check.names = FALSE,
                      row.names = 1,
                      stringsAsFactors = FALSE)
dewikiEdits$dateType <- as.character(dewikiEdits$revactor_timestamp)
dewikiEdits$dateType <- substr(dewikiEdits$dateType, 1, 8)
dewikiEdits$dateType <- sapply(dewikiEdits$dateType, function(x) {
  d <- paste0(
      substr(x, 1, 4), 
      "-",
      substr(x, 5, 6),
      "-",
      substr(x, 7, 8)
    )
  return(d)
})
dewikiEdits <- dplyr::arrange(dewikiEdits, dateType) %>% 
  dplyr::select(user_id, dateType)

campaignDays <- c("2021-10-18", "2021-10-19", "2021-10-20", "2021-10-21", "2021-10-22", "2021-10-23",
                  "2021-10-24", "2021-10-25", "2021-10-26", "2021-10-27", "2021-10-28", "2021-10-29",
                  "2021-10-30", "2021-10-31")

dewikiEdits <- dewikiEdits %>% 
  select(dateType) %>% 
  group_by(dateType) %>% 
  summarise(Edits = n())
dewikiEdits <- arrange(dewikiEdits, dateType)
dewikiEdits$campaign <- sapply(dewikiEdits$dateType, function(x) {
  if (x < "2021-10-18") {
    return("Before Campaign")
  } else if (x > "2021-10-31") {
    return("After Campaign") 
  } else {
    return("Campaign")
  }
})

campaignBefore <- mean(pFrame$Edits[pFrame$campaign == "beforeCampaign"])
campaign <- mean(pFrame$Edits[pFrame$campaign == "Campaign"])
campaignAfter <- mean(pFrame$Edits[pFrame$campaign == "afterCampaign"])

dewikiBefore <- mean(dewikiEdits$Edits[dewikiEdits$campaign == "Before Campaign"])
dewiki <- mean(dewikiEdits$Edits[dewikiEdits$campaign == "Campaign"])
dewikiAfter <- mean(dewikiEdits$Edits[dewikiEdits$campaign == "After Campaign"])

In comparison to the overall edits in dewiki, in the same time span as in the chart above:

  • before the campaign, the campaign users made 99.89 edits on the average daily, while in dewiki we find 1234.18 edits;
  • during the campaign, the campaign users made 192.64 edits on the average daily, while in dewiki we find 1256.5 edits;
  • and after the campaign, the campaign users made 159.11 edits on the average daily, while in dewiki we find 1350.32 edits;
LS0tCnRpdGxlOiAnT2NjYXNpb25hbCBFZGl0b3JzIDIwMjE6IEludGVyaW0gUmVwb3J0JwphdXRob3I6ICJHb3JhbiBTLiBNaWxvdmFub3ZpYywgRGF0YSBTY2llbnRpc3QsIFdNREUiCmRhdGU6ICJOb3ZlbWJlciAzLCAyMDIxIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdGhlbWU6IGNvc21vCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogNQogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA1Ci0tLQoKKipGZWVkYmFjayoqIHNob3VsZCBiZSBzZW5kIHRvIGBnb3Jhbi5taWxvdmFub3ZpY19leHRAd2lraW1lZGlhLmRlYC4gCgpUaGUgY2FtcGFpZ24gaXMgcnVuIGZyb20gMjAyMS8xMC8xOCB0byAyMDIxLzEwLzMxLgoKKipDVVJSRU5UIFVQREFURToqKiBDb21wbGV0ZSBkYXRhc2V0IGFzIG9mIDIwMjEvMTAvMzEuCgpgYGB7ciwgZWNobyA9IEYsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGLCByZXN1bHRzID0gJ2hpZGUnfQojICFkaWFnbm9zdGljcyBvZmYKIyMjIC0tLSBTZXR1cApvcHRpb25zKGRwbHlyLnN1bW1hcmlzZS5pbmZvcm0gPSBGQUxTRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy53aWR0aCA9IDE1LCBmaWcuaGVpZ2h0ID0gOCkgCnJtKGxpc3QgPSBscygpKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KHJtYXJrZG93bikKbGlicmFyeShrbml0cikKbGlicmFyeShEVCkKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShwdXJycikKYGBgCgojIyAwLiBEYXRhIEFjcXVpc2l0b24KCioqTk9URToqKiB0aGUgRGF0YSBBY3F1aXNpdGlvbiBjb2RlIGNodW5rIGlzIG5vdCBmdWxseSByZXByb2R1Y2libGUgZnJvbSB0aGlzIFJlcG9ydC4gVGhlIGRhdGEgYXJlIGNvbGxlY3RlZCBieSBydW5uaW5nIGFuIFIgc2NyaXB0IG9uIGBzdGF0MTAwNy5lcWlhZC53bW5ldGAsIGNvbGxlY3RpbmcgdGhlIGRhdGEgYXMgYC50c3ZgIGFuZCBgLmNzdmAgZmlsZXMsIGNvcHlpbmcgbWFudWFsbHksIGFuZCBwcm9jZXNzaW5nIGxvY2FsbHkuIAoKIyMjIDAuMSBEYWlseSBVcGRhdGUKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZ9CgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIFdNREUgMjAyMSBPY2Fzc2lvbmFsIEVkaXRvcnMKIyMjIC0tLSBodHRwczovL3BoYWJyaWNhdG9yLndpa2ltZWRpYS5vcmcvVDI5MTYzNQojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBDYW1wYWlnbiBEZXNjcmlwdGlvbiBhbmQgUGFyYW1ldGVycwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIExhbmRpbmcgUGFnZXM6CiMgLSBMYW5kaW5nIFBhZ2UgMTogaHR0cHM6Ly9kZS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9EZWluRW5nYWdlbWVudAojIC0gTGFuZGluZyBQYWdlIDJhOiBodHRwczovL3Rvb2xzLndtZmxhYnMub3JnL21pdG1hY2hlbi8KIyAtIExhbmRpbmcgUGFnZSAyYjogaHR0cHM6Ly9kZS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9MZXJuZVdpa2lwZWRpYQojIC0gTGFuZGluZyBQYWdlIDJjOiBodHRwczovL2RlLndpa2lwZWRpYS5vcmcvd2lraS9XaWtpcGVkaWE6RiVDMyVCNnJkZXJ1bmcvRiVDMyVCNnJkZXJhbmdlYm90ZQojIC0gQ2FtcGFpZ24gdGFnczoKIyAtIFdNREVfb2NlZGl0b3JzX2ZhbGxfMjAyMQoKIyAtIFN0YXJ0IG9mIHRoZSBiYW5uZXIgY2FtcGFpZ246IE9jdG9iZXIgMTJ0aAojIC0gRW5kIG9mIHRoZSBiYW5uZXIgY2FtcGFpZ246IE9jdG9iZXIgMjYgdGgKIyAtIFRyYWNraW5nIHRlc3Q6IE9jdG9iZXIgNSAtIDcKIyAtIFByZWxpbWluYXJ5IHJlcG9ydCBmb3IgdHJhY2tpbmc6IGJlZ2lubmluZyBvZiBOb3ZlbWJlcgojIC0gVHJhY2sgZWRpdGluZyBiZWhhdmlvciBmb3VyIHdlZWtzIGFmdGVyIGVuZCBvZiBjYW1wYWlnbjogTm92ZW1iZXIgMjNyZAojIC0gZmluYWwgcmVwb3J0IGZvciB0cmFja2luZzogYmVnaW5uaW5nIG9mIERlY2VtYmVyCgojIyMgLS0tIGxpYnJhcmllcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKCiMjIyAtLS0gY2FtcGFpZ24gc3BlY2lmaWNzCmNhbXBhaWduTmFtZSA8LSAnT2NjYXNpb25hbEVkaXRvcnNfMjAyMScKCiMjIyAtLS0gZGlyIHN0cnVjdHVyZQpjYW1wYWlnblBhdGggPC0gJy9ob21lL2dvcmFuc20vQW5hbHl0aWNzL05ld0VkaXRvcnMvMjAyMV9PY2Nhc2lvbmFsRWRpdG9ycy8nCmRhdGFEaXIgPC0gcGFzdGUwKGNhbXBhaWduUGF0aCwgIl9kYXRhLyIpCmFuYWx5dGljc0RpciA8LSBwYXN0ZTAoY2FtcGFpZ25QYXRoLCAiX2FuYWx5dGljcy8iKQpwdWJsaWNEaXIgPC0gJy9zcnYvcHVibGlzaGVkL2RhdGFzZXRzL3dtZGUtYW5hbHl0aWNzLWVuZ2luZWVyaW5nL05ld0VkaXRvcnMvY2FtcGFpZ25zLzIwMjFfT2NjYXNpb25hbEVkaXRvcnMvJwoKIyMjIC0tLSBkZXRlcm1pbmUgY2V0RGF5CmNldERheSA8LSBTeXMudGltZSgpCmNldERheQphdHRyKGNldERheSwgInR6b25lIikgPC0gIkV1cm9wZS9CZXJsaW4iCiMgLSBvbmUgZGF5IGJlaGluZCBmb3IgY3JvbnRhYgojIC0gKGkuZS4gd2FpdGluZyBmb3Igd21mLndlYnJlcXVlc3QgdG8gY29tcGxldGUgaXMgZGF0YSBhY3F1aXNpdGlvbikKY2V0RGF5IDwtIHltZCgKICBzdHJzcGxpdChhcy5jaGFyYWN0ZXIoY2V0RGF5KSwgCiAgICAgICAgICAgc3BsaXQgPSAiICIsIAogICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXQopIC0gMQoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBCYW5uZXIgSW50ZXJhY3Rpb25zOgojIyMgLS0tIHZpYSBldmVudC5XTURFQmFubmVySW50ZXJhY3Rpb25zCiMjIyAtLS0gaHR0cHM6Ly9tZXRhLndpa2ltZWRpYS5vcmcvd2lraS9TY2hlbWE6V01ERUJhbm5lckludGVyYWN0aW9ucwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIHNlbGVjdCBkdCwgZXZlbnQuYmFubmVyTmFtZSwgZXZlbnQuYmFubmVyQWN0aW9uLCBldmVudC5iYW5uZXJJbXByZXNzaW9ucywgZXZlbnQudXNlcklEIAojIC0gZnJvbSBldmVudC53bWRlYmFubmVyaW50ZXJhY3Rpb25zIHdoZXJlIHllYXI9MjAyMCBhbmQgbW9udGg9NSBhbmQgKGRheT0xMSBvciBkYXk9MTIgb3IgZGF5PTEzKTsKCiMgLSBmdW5jdGlvbjogd21kZV9iYW5uZXJfYWN0aW9ucwp3bWRlX2Jhbm5lcl9hY3Rpb25zIDwtIGZ1bmN0aW9uKHVyaV9xdWVyeV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmFseXRpY3NEaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lKSB7CiAgCiAgIyAtIE5PVEU6CiAgIyAtIGV4cGVjdGVkIGZvcm1hdCBmb3IgY2V0RGF5IGlzOiBZWVlZLU1NLURECiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIGRhdGV0aW1lX2NvbmRpdGlvbgogIGNldF9jb25kaXRpb24gPC0gc2VxKAogICAgZnJvbSA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDA6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgdG8gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAyMzowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICBieSA9ICJob3VyIgogICkgCiAgYXR0cihjZXRfY29uZGl0aW9uLCAidHpvbmUiKSA8LSAiVVRDIgogIGNldF9jb25kaXRpb24gPC0gYXMuY2hhcmFjdGVyKGNldF9jb25kaXRpb24pCiAgY2V0X2NvbmRpdGlvbiA8LSB1bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKGNldF9jb25kaXRpb24sICJeKFtbOmRpZ2l0Ol1dfFxcc3wtKSoiKSkKICBjZXRfeWVhcnMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMV0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsyXQogICAgfSkKICBjZXRfbW9udGhzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9tb250aHMpCiAgY2V0X2RheXMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bM10KICAgIH0pCiAgY2V0X2RheXMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2RheXMpCiAgY2V0X2hvdXJzIDwtIHNhcHBseShzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgCiAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHhbMl0KICAgICAgICAgICAgICAgICAgICAgIH0pCiAgY2V0X2hvdXJzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9ob3VycykKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZTAoCiAgICAieWVhciA9ICIsIGNldF95ZWFycywgIiBBTkQgIiwKICAgICJtb250aCA9ICIsIGNldF9tb250aHMsICIgQU5EICIsCiAgICAiZGF5ID0gIiwgY2V0X2RheXMsICIgQU5EICIsIAogICAgImhvdXIgPSAiLCBjZXRfaG91cnMKICApCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUoIigiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZUNvbmRpdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgZXZlbnRCYW5uZXJOYW1lX2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX3F1ZXJ5X2ZpbHRlcikgPiAxKSB7CiAgICBldmVudEJhbm5lck5hbWVfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJldmVudC5iYW5uZXJOYW1lIExJS0UgJyUiLCB1cmlfcXVlcnlfZmlsdGVyLCAiJSciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgZXZlbnRCYW5uZXJOYW1lX2NvbmRpdGlvbiA9IHBhc3RlMCgiZXZlbnQuYmFubmVyTmFtZSBMSUtFICclIiwgdXJpX3F1ZXJ5X2ZpbHRlciwgIiUnIikKICB9CiAgCiAgIyAtIGNvbXBvc2UgSGl2ZVFMIHF1ZXJ5CiAgaGl2ZVF1ZXJ5IDwtIHBhc3RlMCggCiAgICAic2VsZWN0IGR0LCBldmVudC5iYW5uZXJOYW1lLCBldmVudC5iYW5uZXJBY3Rpb24sIGV2ZW50LmJhbm5lckltcHJlc3Npb25zLCBldmVudC51c2VySUQgZnJvbSBldmVudC53bWRlYmFubmVyaW50ZXJhY3Rpb25zIAogICAgV0hFUkUgKCIsCiAgICBldmVudEJhbm5lck5hbWVfY29uZGl0aW9uLCAiIEFORCAiLAogICAgIigiLCBkYXRldGltZUNvbmRpdGlvbiwgIikiLAogICAgIik7IgogICkKICAKICAjIC0gd3JpdGUgaHFsCiAgd3JpdGUoaGl2ZVF1ZXJ5LCBwYXN0ZTAoZGF0YURpciwgcXVlcnlGaWxlKSkKICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0OgogIGtlcmJlcm9zUHJlZml4IDwtIAogICAgJ3N1ZG8gLXUgYW5hbHl0aWNzLXByaXZhdGVkYXRhIGtlcmJlcm9zLXJ1bi1jb21tYW5kIGFuYWx5dGljcy1wcml2YXRlZGF0YSAnCiAgIyAtIEtlcmJlcm9zIGluaXQKICBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlMChrZXJiZXJvc1ByZWZpeCwgJyBoZGZzIGRmcyAtbHMnKSwgCiAgICAgICAgIHdhaXQgPSBUKQogICMgLSBSdW4gcXVlcnkKICBxdWVyeSA8LSBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlKGtlcmJlcm9zUHJlZml4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcvdXNyL2xvY2FsL2Jpbi9iZWVsaW5lIC0taW5jcmVtZW50YWw9dHJ1ZSAtLXNpbGVudCAtZiAiJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMChkYXRhRGlyLCBxdWVyeUZpbGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIgPiAnLCBkYXRhRGlyLCBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKSwKICAgICAgICAgICAgICAgICAgd2FpdCA9IFRSVUUpCiAgCiAgIyAtIFdyYW5nbGUgQmFubmVyIEludGVyYWN0aW9ucwogICMgLSBsb2FkCiAgYmFubmVyRGF0YSA8LSB0cnlDYXRjaCh7CiAgICBhcy5kYXRhLmZyYW1lKGZyZWFkKHBhc3RlMChkYXRhRGlyLCBmaWxlTmFtZSkpKQogIH0sCiAgZXJyb3IgPSBmdW5jdGlvbihjb25kaXRpb24pIHsKICAgIHJldHVybihGQUxTRSkKICB9KQogIAogICMgLSBwcm9jZXNzCiAgaWYgKGNsYXNzKGJhbm5lckRhdGEpID09ICdsb2dpY2FsJykgewogICAgCiAgICByZXR1cm4oRkFMU0UpIAogICAgCiAgfSBlbHNlIHsgCiAgICAKICAgICMgLSB3aG9DbGlja2VkCiAgICB3aG9DbGlja2VkIDwtIGJhbm5lckRhdGEgJT4lIAogICAgICBkcGx5cjo6ZmlsdGVyKGJhbm5lcmFjdGlvbiA9PSAiYmFubmVyLWNsaWNrZWQiKQogICAgd2hvQ2xpY2tlZCA8LSBkYXRhLmZyYW1lKHVzZXJpZCA9IHVuaXF1ZSh3aG9DbGlja2VkJHVzZXJpZCkpCiAgICAKICAgICMgLSBzdG9yZToKICAgIHdyaXRlLmNzdih3aG9DbGlja2VkLCAKICAgICAgICAgICAgICBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAKICAgICAgICAgICAgICAgICAgICAgIndob0NsaWNrZWRfIiwKICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoCiAgICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoZmlsZU5hbWUsIHNwbGl0ID0gIl8iLCBmaXhlZCA9IFQpW1sxXV1bMl0sCiAgICAgICAgICAgICAgICAgICAgICAgc3BsaXQgPSAiLiIsIAogICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXSwKICAgICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICAgKQogICAgKQogICAgCiAgICAjIC0gcHJvY2VzcyBiYW5uZXJEYXRhCiAgICBiYW5uZXJEYXRhIDwtIHQodGFibGUoYmFubmVyRGF0YSRiYW5uZXJhY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgYmFubmVyRGF0YSRiYW5uZXJuYW1lKSkKICAgIGJhbm5lckRhdGEgPC0gYXMuZGF0YS5mcmFtZShiYW5uZXJEYXRhKQogICAgY29sbmFtZXMoYmFubmVyRGF0YSkgPC0gYygnYmFubmVyJywgJ2FjdGlvbicsICdjb3VudCcpCiAgICBiYW5uZXJEYXRhJGRheSA8LSBjZXREYXkKICAgIGJhbm5lckRhdGEkY2FtcGFpZ24gPC0gY2FtcGFpZ25OYW1lCiAgICAKICAgICMgLSBzdG9yZToKICAgIHdyaXRlLmNzdihiYW5uZXJEYXRhLCAKICAgICAgICAgICAgICBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAKICAgICAgICAgICAgICAgICAgICAgImJhbm5lckludGVyYWN0aW9uc0FnZ3JlZ2F0ZWRfIiwKICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoCiAgICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoZmlsZU5hbWUsIHNwbGl0ID0gIl8iLCBmaXhlZCA9IFQpW1sxXV1bMl0sCiAgICAgICAgICAgICAgICAgICAgICAgc3BsaXQgPSAiLiIsIAogICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXSwKICAgICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICAgKSkKICB9CiAgCn0KCiMgLSBzZXQgcGFyYW1zIGZvciB3bWRlX2Jhbm5lcl9hY3Rpb25zKCkKcXVlcnlGaWxlIDwtIHBhc3RlMChjYW1wYWlnbk5hbWUsICJfYmFubmVySW50ZXJhY3Rpb25zLmhxbCIpCmZpbGVOYW1lIDwtIHBhc3RlMCgiYmFubmVySW50ZXJhY3Rpb25zXyIsIGNldERheSwgIi50c3YiKQp1cmlfcXVlcnlfZmlsdGVyIDwtICdXTURFX29jZWRpdG9yc19mYWxsXzIwMjEnCmJhbm5lcl9zdGF0dXMgPC0gd21kZV9iYW5uZXJfYWN0aW9ucyh1cmlfcXVlcnlfZmlsdGVyID0gdXJpX3F1ZXJ5X2ZpbHRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSA9IHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lID0gZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmFseXRpY3NEaXIgPSBhbmFseXRpY3NEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lID0gY2FtcGFpZ25OYW1lKQoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBQYWdldmlld3MKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLSBmdW5jdGlvbjogd21kZV9jb2xsZWN0X3BhZ2V2aWV3cwp3bWRlX2NvbGxlY3RfcGFnZXZpZXdzIDwtIGZ1bmN0aW9uKHVyaV9ob3N0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpcikgewogIAogICMgLSBOT1RFOgogICMgLSBleHBlY3RlZCBmb3JtYXQgZm9yIGNldERheSBpczogWVlZWS1NTS1ERAogIAogICMgLSB0byBkYXRhRGlyCiAgc2V0d2QoZGF0YURpcikKICAKICAjIC0gbGlicmFyaWVzCiAgbGlicmFyeShzdHJpbmdyKQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSBkYXRldGltZV9jb25kaXRpb24KICBjZXRfY29uZGl0aW9uIDwtIHNlcSgKICAgIGZyb20gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAwOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIHRvID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMjM6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgYnkgPSAiaG91ciIKICApIAogIGF0dHIoY2V0X2NvbmRpdGlvbiwgInR6b25lIikgPC0gIlVUQyIKICBjZXRfY29uZGl0aW9uIDwtIGFzLmNoYXJhY3RlcihjZXRfY29uZGl0aW9uKQogIGNldF9jb25kaXRpb24gPC0gdW5saXN0KHN0cl9leHRyYWN0X2FsbChjZXRfY29uZGl0aW9uLCAiXihbWzpkaWdpdDpdXXxcXHN8LSkqIikpCiAgY2V0X3llYXJzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzFdCiAgICB9KQogIGNldF9tb250aHMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMl0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfbW9udGhzKQogIGNldF9kYXlzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzNdCiAgICB9KQogIGNldF9kYXlzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9kYXlzKQogIGNldF9ob3VycyA8LSBzYXBwbHkoc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIAogICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICB4WzJdCiAgICAgICAgICAgICAgICAgICAgICB9KQogIGNldF9ob3VycyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfaG91cnMpCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUwKAogICAgInllYXIgPSAiLCBjZXRfeWVhcnMsICIgQU5EICIsCiAgICAibW9udGggPSAiLCBjZXRfbW9udGhzLCAiIEFORCAiLAogICAgImRheSA9ICIsIGNldF9kYXlzLCAiIEFORCAiLCAKICAgICJob3VyID0gIiwgY2V0X2hvdXJzCiAgKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlKCIoIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZXRpbWVDb25kaXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpCiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9ob3N0X2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX2hvc3QpID4gMSkgewogICAgdXJpX2hvc3RfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgidXJpX2hvc3QgPSAnIiwgdXJpX2hvc3QsICInIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9ob3N0ID0gJyIsIHVyaV9ob3N0LCAiJyIpCiAgfQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSB1cmlfcGF0aF9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9wYXRoKSA+IDEpIHsKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX3BhdGhfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfcGF0aCA9ICciLCB1cmlfcGF0aCwgIiciKQogIH0KICAKICAjIC0gY29tcG9zZSBIaXZlUUwgcXVlcnkKICBoaXZlUXVlcnkgPC0gcGFzdGUwKCAKICAgICJVU0Ugd21mOwogICAgU0VMRUNUIHVyaV9ob3N0LCB1cmlfcGF0aCwgdXJpX3F1ZXJ5LCByZWZlcmVyIEZST00gd2VicmVxdWVzdAogICAgV0hFUkUgKCIsCiAgICB1cmlfaG9zdF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcGF0aF9jb25kaXRpb24sICIgQU5EICIsCiAgICAiKCIsIGRhdGV0aW1lQ29uZGl0aW9uLCAiKSIsCiAgICAiKTsiCiAgKQogIAogICMgLSB3cml0ZSBocWwKICB3cml0ZShoaXZlUXVlcnksIHF1ZXJ5RmlsZSkKICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0OgogIGtlcmJlcm9zUHJlZml4IDwtIAogICAgJ3N1ZG8gLXUgYW5hbHl0aWNzLXByaXZhdGVkYXRhIGtlcmJlcm9zLXJ1bi1jb21tYW5kIGFuYWx5dGljcy1wcml2YXRlZGF0YSAnCiAgIyAtIEtlcmJlcm9zIGluaXQKICBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlMChrZXJiZXJvc1ByZWZpeCwgJyBoZGZzIGRmcyAtbHMnKSwgCiAgICAgICAgIHdhaXQgPSBUKQogICMgLSBSdW4gcXVlcnkKICBxdWVyeSA8LSBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlKGtlcmJlcm9zUHJlZml4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcvdXNyL2xvY2FsL2Jpbi9iZWVsaW5lIC0taW5jcmVtZW50YWw9dHJ1ZSAtLXNpbGVudCAtZiAiJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMChkYXRhRGlyLCBxdWVyeUZpbGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIgPiAnLCBkYXRhRGlyLCBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKSwKICAgICAgICAgICAgICAgICAgd2FpdCA9IFRSVUUpCn0KCiMgLSBzZXQgcGFyYW1zIHRvIHdtZGVfY29sbGVjdF9wYWdldmlld3MKdXJpX2hvc3QgPC0gYygnZGUud2lraXBlZGlhLm9yZycsICdkZS5tLndpa2lwZWRpYS5vcmcnKQp1cmlfcGF0aCAgPC0gYygnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9EZWluRW5nYWdlbWVudCcsCiAgICAgICAgICAgICAgICcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0xlcm5lV2lraXBlZGlhJywgCiAgICAgICAgICAgICAgICcvd2lraS9XaWtpcGVkaWE6RiVDMyVCNnJkZXJ1bmcvRiVDMyVCNnJkZXJhbmdlYm90ZScpCnF1ZXJ5RmlsZSA8LSAnV01ERV9vY2VkaXRvcnNfZmFsbF8yMDIxX1BhZ2V2aWV3cy5ocWwnCmZpbGVOYW1lIDwtIHBhc3RlMCgicGFnZXZpZXdzXyIsIGNldERheSwgIi50c3YiKQoKIyAtIGNvbGxlY3QgUGFnZXZpZXdzIGRhdGEKd21kZV9jb2xsZWN0X3BhZ2V2aWV3cyh1cmlfaG9zdCwKICAgICAgICAgICAgICAgICAgICAgICB1cmlfcGF0aCwKICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIpCgojIyMgLS0tIFdyYW5nbGUgUGFnZXZpZXdzCiMgLSBmdW5jdGlvbjogd21kZV9wcm9jZXNzX3BhZ2V2aWV3cwp3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzIDwtIGZ1bmN0aW9uKGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9xdWVyeV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFnZV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5ID0gY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSA9IGNhbXBhaWduTmFtZSkgewogIAogICMgLSB0byBkYXRhRGlyCiAgc2V0d2QoZGF0YURpcikKICAKICAjIC0gbGlicmFyaWVzCiAgbGlicmFyeShzdHJpbmdyKQogIGxpYnJhcnkoZHBseXIpCiAgbGlicmFyeSh0aWR5cikKICBsaWJyYXJ5KGRhdGEudGFibGUpCiAgCiAgIyAtIGxvYWQKICBwYWdldmlld3NEYXRhIDwtIHJlYWRMaW5lcyhmaWxlTmFtZSkKICB3U3RhcnQgPC0gd2hpY2goZ3JlcGwoInVyaV9ob3N0IiwgcGFnZXZpZXdzRGF0YSkpCiAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhWyh3U3RhcnQgKyAxKToobGVuZ3RoKHBhZ2V2aWV3c0RhdGEpIC0gMSldCiAgcGFnZXZpZXdzRGF0YSA8LSBkYXRhLmZyYW1lKGRhdCA9IHBhZ2V2aWV3c0RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICBwYWdldmlld3NEYXRhIDwtIHNlcGFyYXRlKHBhZ2V2aWV3c0RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRvID0gYygndXJpX2hvc3QnLCAndXJpX3BhdGgnLCAndXJpX3F1ZXJ5JywgJ3JlZmVyZXInKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIpCiAgIyAtIGFwcGx5IHBhZ2VfZmlsdGVyCiAgd0ZpbHRlcjEgPC0gdW5pcXVlKHVubmFtZSh1bmxpc3Qoc2FwcGx5KHBhZ2VfZmlsdGVyLCBmdW5jdGlvbiAoeCkgewogICAgd2hpY2goZ3JlcGwoeCwgcGFnZXZpZXdzRGF0YSR1cmlfcGF0aCkpCiAgfSkpKSkKICBwYWdldmlld3NEYXRhIDwtIHBhZ2V2aWV3c0RhdGFbd0ZpbHRlcjEsIF0KICAjIC0gYXBwbHkgdXJpX3F1ZXJ5X2ZpbHRlcgogIHdfdXJpX3F1ZXJ5XzEgPC0gd2hpY2goZ3JlcGwodXJpX3F1ZXJ5X2ZpbHRlciwgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkpKQogIHdfdXJpX3F1ZXJ5XzIgPC0gd2hpY2goZ3JlcGwodXJpX3F1ZXJ5X2ZpbHRlciwgcGFnZXZpZXdzRGF0YSRyZWZlcmVyKSkKICB3X3VyaV9xdWVyeSA8LSB1bmlxdWUoYyh3X3VyaV9xdWVyeV8xLCB3X3VyaV9xdWVyeV8yKSkKCiAgaWYgKGxlbmd0aCh3X3VyaV9xdWVyeSkgPiAwKSB7CiAgICAKICAgICMgLSBmaWx0ZXIgZm9yIHdfdXJpX3F1ZXJ5CiAgICBwYWdldmlld3NEYXRhIDwtIHBhZ2V2aWV3c0RhdGFbd191cmlfcXVlcnksIF0gCiAgICAKICAgICMgLSBjb3B5IHVyaV9xdWVyeSBmcm9tIHJlZmVyZXIgd2hlcmUgdXJpX3F1ZXJ5IGlzIGVtcHR5OgogICAgd0N1cmlxIDwtIHdoaWNoKHBhZ2V2aWV3c0RhdGEkdXJpX3F1ZXJ5ID09ICIiKQogICAgY3VyaXEgPC0gc3RyaW5ncjo6c3RyX2V4dHJhY3QocGFnZXZpZXdzRGF0YSRyZWZlcmVyW3dDdXJpcV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKHVyaV9xdWVyeV9maWx0ZXIsICIuKyQiKSkKICAgIHBhZ2V2aWV3c0RhdGEkdXJpX3F1ZXJ5W3dDdXJpcV0gPC0gY3VyaXEKICAgICMgLSByZW1vdmUgP2NhbXBhaWduPSBmcm9tIHBhZ2V2aWV3c0RhdGEkdXJpX3F1ZXJ5CiAgICBwYWdldmlld3NEYXRhJHVyaV9xdWVyeSA8LSBnc3ViKCI/Y2FtcGFpZ249IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWdldmlld3NEYXRhJHVyaV9xdWVyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVCkKICAgIAogICAgIyAtIGFnZ3JlZ2F0ZToKICAgIHBhZ2V2aWV3c0RhdGEkdXJpX3BhdGggPC0gcGFzdGUwKHBhZ2V2aWV3c0RhdGEkdXJpX2hvc3QsIHBhZ2V2aWV3c0RhdGEkdXJpX3BhdGgpCiAgICBwYWdldmlld3NEYXRhJHVyaV9ob3N0IDwtIE5VTEwKICAgIHBhZ2V2aWV3c0RhdGEkcGFnZSA8LSBOVUxMCiAgICBwYWdldmlld3NEYXRhJHJlZmVyZXIgPC0gTlVMTAogICAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhICU+JSAKICAgICAgZHBseXI6OnNlbGVjdCh1cmlfcXVlcnksIHVyaV9wYXRoKSAlPiUgCiAgICAgIGRwbHlyOjpncm91cF9ieSh1cmlfcXVlcnksIHVyaV9wYXRoKSAlPiUgCiAgICAgIGRwbHlyOjpzdW1tYXJpc2UocGFnZXZpZXdzID0gbigpKQogICAgY29sbmFtZXMocGFnZXZpZXdzRGF0YSkgPC0gYygnVGFnJywgJ1BhZ2UnLCAnUGFnZXZpZXdzJykKICAgIAogICAgIyAtIGFkZCBjZXREYXksIGNhbXBhaWduTmFtZQogICAgcGFnZXZpZXdzRGF0YSRkYXRlIDwtIGNldERheQogICAgcGFnZXZpZXdzRGF0YSRjYW1wYWlnbiA8LSBjYW1wYWlnbk5hbWUKICAgIAogICAgIyAtIHN0b3JlOgogICAgd3JpdGUuY3N2KHBhZ2V2aWV3c0RhdGEsIAogICAgICAgICAgICAgIHBhc3RlMChhbmFseXRpY3NEaXIsIAogICAgICAgICAgICAgICAgICAgICAicGFnZXZpZXdzQWdncmVnYXRlZF8iLAogICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdCgKICAgICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdChmaWxlTmFtZSwgc3BsaXQgPSAiXyIsIGZpeGVkID0gVClbWzFdXVsyXSwKICAgICAgICAgICAgICAgICAgICAgICBzcGxpdCA9ICIuIiwgCiAgICAgICAgICAgICAgICAgICAgICAgZml4ZWQgPSBUKVtbMV1dWzFdLAogICAgICAgICAgICAgICAgICAgICAiLmNzdiIKICAgICAgICAgICAgICApCiAgICApCiAgICAKICB9CiAgCn0KCiMgLSBzZXQgcGFyYW1zIHRvIHdtZGVfcHJvY2Vzc19wYWdldmlld3MKdXJpX3F1ZXJ5X2ZpbHRlciA8LSAnV01ERV9vY2VkaXRvcnNfZmFsbF8yMDIxJwpwYWdlX2ZpbHRlciA8LSBjKCcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0RlaW5FbmdhZ2VtZW50JywKICAgICAgICAgICAgICAgICAnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9MZXJuZVdpa2lwZWRpYScsIAogICAgICAgICAgICAgICAgICcvd2lraS9XaWtpcGVkaWE6RiVDMyVCNnJkZXJ1bmcvRiVDMyVCNnJkZXJhbmdlYm90ZScpCgojIC0gd3JhbmdsZSBwYWdldmlld3MKd21kZV9wcm9jZXNzX3BhZ2V2aWV3cyhmaWxlTmFtZSA9IGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIgPSBkYXRhRGlyLAogICAgICAgICAgICAgICAgICAgICAgIHVyaV9xdWVyeV9maWx0ZXIgPSB1cmlfcXVlcnlfZmlsdGVyLCAKICAgICAgICAgICAgICAgICAgICAgICBwYWdlX2ZpbHRlciA9IHBhZ2VfZmlsdGVyLAogICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUgPSBjYW1wYWlnbk5hbWUpCgoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIENvbXBvc2UgYW5kIGNvcHkgdG8gcHVibGljRGlyCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmxGIDwtIGxpc3QuZmlsZXMoYW5hbHl0aWNzRGlyKQpsRiA8LSBsRltncmVwbCgiQWdncmVnYXRlZCIsIGxGKV0KbEZwIDwtIGxGW2dyZXBsKCJwYWdldmlld3MiLCBsRildCmxGcCA8LSBsYXBwbHkocGFzdGUwKGFuYWx5dGljc0RpciwgbEZwKSwKICAgICAgICAgICAgICBkYXRhLnRhYmxlOjpmcmVhZCkKbEZwIDwtIGRhdGEudGFibGU6OnJiaW5kbGlzdChsRnApCndyaXRlLmNzdihsRnAsIAogICAgICAgICAgcGFzdGUwKHB1YmxpY0RpciwgCiAgICAgICAgICAgICAgICAgIjIwMjFfT2NjYXNpb25hbEVkaXRvcnNfUGFnZXZpZXdzLmNzdiIpKQpsRmIgPC0gbEZbZ3JlcGwoImJhbm5lciIsIGxGKV0KbEZiIDwtIGxhcHBseShwYXN0ZTAoYW5hbHl0aWNzRGlyLCBsRmIpLAogICAgICAgICAgICAgIGRhdGEudGFibGU6OmZyZWFkKQpsRmIgPC0gZGF0YS50YWJsZTo6cmJpbmRsaXN0KGxGYikKd3JpdGUuY3N2KGxGYiwgCiAgICAgICAgICBwYXN0ZTAocHVibGljRGlyLCAKICAgICAgICAgICAgICAgICAiMjAyMV9PY2Nhc2lvbmFsRWRpdG9yc19CYW5uZXJzLmNzdiIpKQoKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAtLS0gVXNlciBFZGl0cwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCgoKCmBgYAoKCiMjIDEuIENhbXBhaWduIEJhbm5lcnMKClRoaXMgc2VjdGlvbiBwcmVzZW50cyBhbGwgZGF0YSBhbmQgc3RhdGlzdGljcyBvbiB0aGUgY2FtcGFpZ24gYmFubmVycy4KCiMjIyAxLjEgQmFubmVyIEltcHJlc3Npb25zCiMjIyMgMS4xLjEgQmFubmVyIEltcHJlc3Npb25zIE92ZXJ2aWV3CgoqKkNoYXJ0IDEuMS4xKiogRGFpbHkgQmFubmVyIEltcHJlc3Npb25zCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KIyAtIHdyYW5nbGUgZGF0YVNldApkYXRhU2V0IDwtIHJlYWQuY3N2KAogICdfcmVwb3J0aW5nLzIwMjFfT2NjYXNpb25hbEVkaXRvcnNfQmFubmVycy5jc3YnLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmRhdGFTZXQkVjEgPC0gTlVMTApkYXRhU2V0IDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpmaWx0ZXIoIWdyZXBsKCIyMDIxLTExIiwgZGF0YVNldCRkYXkpKQpkYXRhU2V0JGJhbm5lciA8LSBnc3ViKCJfY3RybCIsICIiLCBkYXRhU2V0JGJhbm5lciwgZml4ZWQgPSBUKQpkYXRhU2V0JGJhbm5lciA8LSBzYXBwbHkoZGF0YVNldCRiYW5uZXIsIGZ1bmN0aW9uKHgpIHsKICBpZiAoZ3JlcGwoImlwYWQkIiwgeCkpIHsKICAgICJpcGFkIgogIH0gZWxzZSBpZiAoZ3JlcGwoIm1vYmlsZSQiLCB4KSkgewogICAgIm1vYmlsZSIKICB9IGVsc2UgewogICAgImRlc2t0b3AiCiAgfQp9KQoKIyAtIHBGcmFtZQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OnNlbGVjdCgtY2FtcGFpZ24pICU+JSAKICBkcGx5cjo6ZmlsdGVyKGFjdGlvbiA9PSAiYmFubmVyLXNlZW4iKSAlPiUgCiAgZHBseXI6OnNlbGVjdCgtYWN0aW9uKQoKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn0KZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXksCiAgICAgICAgICAgICAgICAgICB5ID0gY291bnQsCiAgICAgICAgICAgICAgICAgICBncm91cCA9IGJhbm5lciwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gYmFubmVyLAogICAgICAgICAgICAgICAgICAgZmlsbCA9IGJhbm5lciwKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gY291bnQpKSArIAogIGdlb21fbGluZShzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArIAogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChzaXplID0gNCwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGdndGl0bGUoJ09jY2FzaW9uYWwgRWRpdG9ycyAyMDIxOiBEYWlseSBCYW5uZXIgSW1wcmVzc2lvbnMnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCiMjIyMgMS4xLjEgQmFubmVyIEltcHJlc3Npb25zIE92ZXJ2aWV3OiBUYWJsZQoKKipUYWJsZSAxLjEuMS4qKiBEYWlseSBCYW5uZXIgSW1wcmVzc2lvbnMKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIyMgLS0tIEZ1bGwgRGF0YXNldCAoVGFibGUgUmVwb3J0KQpkYXRhdGFibGUocEZyYW1lKQpgYGAKCiMjIyMgMS4xLjIgVG90YWwgQmFubmVyIEltcHJlc3Npb25zCgoqKkNoYXJ0IDEuMS4yLioqIFRvdGFsIEJhbm5lciBJbXByZXNzaW9ucwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMgLSBwRnJhbWUKcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWNhbXBhaWduKSAlPiUgCiAgZHBseXI6OmZpbHRlcihhY3Rpb24gPT0gImJhbm5lci1zZWVuIikgJT4lCiAgZHBseXI6OnNlbGVjdCgtYWN0aW9uLCAtIGRheSkgJT4lIAogIGRwbHlyOjpncm91cF9ieShiYW5uZXIpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKHRvdGFsSW1wcmVzc2lvbnMgPSBzdW0oY291bnQpKQoKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgIHkgPSB0b3RhbEltcHJlc3Npb25zLCAKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGJhbm5lciwgCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGJhbm5lciwgCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB0b3RhbEltcHJlc3Npb25zKSkgKyAKICBnZW9tX2Jhcih3aWR0aCA9IC41LCBzdGF0ID0gImlkZW50aXR5IikgKyAKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogQmFubmVyIEltcHJlc3Npb25zJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fbGFiZWxfcmVwZWwoc2l6ZSA9IDMuNSwgY29sb3IgPSAid2hpdGUiLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMjIyMgMS4xLjMgQmFubmVyIEltcHJlc3Npb25zIHBlciBEYXkKCioqQ2hhcnQgMS4xLjMuKiogQmFubmVyIEltcHJlc3Npb25zIHBlciBEYXkKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIC0gcEZyYW1lCnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6c2VsZWN0KC1jYW1wYWlnbikgJT4lIAogIGRwbHlyOjpmaWx0ZXIoYWN0aW9uID09ICJiYW5uZXItc2VlbiIpICU+JQogIGRwbHlyOjpzZWxlY3QoLWFjdGlvbiwgLWJhbm5lcikgJT4lIAogIGRwbHlyOjpncm91cF9ieShkYXkpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKHRvdGFsSW1wcmVzc2lvbnMgPSBzdW0oY291bnQpKQoKCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF5LCAKICAgICAgICAgICAgICAgICAgICB5ID0gdG90YWxJbXByZXNzaW9ucywgCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB0b3RhbEltcHJlc3Npb25zKSkgKwogIGdlb21fcGF0aChzaXplID0gLjI1LCBncm91cCA9IDEsIGNvbG9yID0gImRhcmtibHVlIikgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgY29sb3IgPSAiZGFya2JsdWUiKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGNvbG9yID0gIndoaXRlIikgKyAKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogQmFubmVyIEltcHJlc3Npb25zJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fbGFiZWxfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkKYGBgCgojIyMgMS4yIEJhbm5lciBDbGlja3MKIyMjIyAxLjIuMSBCYW5uZXIgQ2xpY2tzIE92ZXJ2aWV3CgoqKkNoYXJ0IDEuMi4xKiogRGFpbHkgQmFubmVyIENsaWNrcwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMgLSBwRnJhbWUKcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWNhbXBhaWduKSAlPiUgCiAgZHBseXI6OmZpbHRlcihhY3Rpb24gPT0gImJhbm5lci1jbGlja2VkIikgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWFjdGlvbikKCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICAgICAgeSA9IGNvdW50LAogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IGJhbm5lciwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGNvdW50CiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX2xpbmUoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChzaXplID0gNCkgKwogIGdndGl0bGUoJ09jY2FzaW9uYWwgRWRpdG9ycyAyMDIxOiBEYWlseSBCYW5uZXIgQ2xpY2tzJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgojIyMjIDEuMi4xIEJhbm5lciBDbGlja3MgT3ZlcnZpZXc6IFRhYmxlCgoqKlRhYmxlIDEuMi4xLioqIERhaWx5IEJhbm5lciBDbGlja3MKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIyMgLS0tIEZ1bGwgRGF0YXNldCAoVGFibGUgUmVwb3J0KQpkYXRhdGFibGUocEZyYW1lKQpgYGAKCiMjIyMgMS4yLjIgVG90YWwgQmFubmVyIENsaWNrcwoKKipDaGFydCAxLjIuMi4qKiBUb3RhbCBCYW5uZXIgQ2xpY2tzCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KIyAtIHBGcmFtZQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OnNlbGVjdCgtY2FtcGFpZ24pICU+JSAKICBkcGx5cjo6ZmlsdGVyKGFjdGlvbiA9PSAiYmFubmVyLWNsaWNrZWQiKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1hY3Rpb24sIC0gZGF5KSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGJhbm5lcikgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UodG90YWxDbGlja3MgPSBzdW0oY291bnQpKQoKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgIHkgPSB0b3RhbENsaWNrcywgCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdG90YWxDbGlja3MpKSArIAogIGdlb21fYmFyKHdpZHRoID0gLjUsIHN0YXQgPSAiaWRlbnRpdHkiKSArIAogIGdndGl0bGUoJ09jY2FzaW9uYWwgRWRpdG9ycyAyMDIxOiBCYW5uZXIgQ2xpY2tzJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fbGFiZWxfcmVwZWwoc2l6ZSA9IDMuNSwgY29sb3IgPSAid2hpdGUiLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMjIyMgMS4yLjMgQmFubmVyIENsaWNrcyBwZXIgRGF5CgoqKkNoYXJ0IDEuMi4zLioqIEJhbm5lciBDbGlja3MgcGVyIERheQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMgLSBwRnJhbWUKcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWNhbXBhaWduKSAlPiUgCiAgZHBseXI6OmZpbHRlcihhY3Rpb24gPT0gImJhbm5lci1jbGlja2VkIikgJT4lCiAgZHBseXI6OnNlbGVjdCgtYWN0aW9uLCAtYmFubmVyKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGRheSkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UodG90YWxDbGlja3MgPSBzdW0oY291bnQpKQoKCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF5LCAKICAgICAgICAgICAgICAgICAgICB5ID0gdG90YWxDbGlja3MsIAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdG90YWxDbGlja3MpKSArCiAgZ2VvbV9wYXRoKHNpemUgPSAuMjUsIGdyb3VwID0gMSwgY29sb3IgPSAiZGFya2JsdWUiKSArCiAgZ2VvbV9wb2ludChzaXplID0gMS41LCBjb2xvciA9ICJkYXJrYmx1ZSIpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAid2hpdGUiKSArIAogIGdndGl0bGUoJ09jY2FzaW9uYWwgRWRpdG9ycyAyMDIxOiBCYW5uZXIgQ2xpY2tzJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fbGFiZWxfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkKYGBgCgojIyMgMS4zIEJhbm5lciBDbG9zZQojIyMjIDEuMy4xIEJhbm5lciBDbG9zZSBPdmVydmlldwoKKipDaGFydCAxLjMuMSoqIERhaWx5IEJhbm5lciBDbG9zZQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMgLSBwRnJhbWUKcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWNhbXBhaWduKSAlPiUgCiAgZHBseXI6OmZpbHRlcihhY3Rpb24gPT0gImJhbm5lci1jbG9zZWQiKSAlPiUgCiAgZHBseXI6OnNlbGVjdCgtYWN0aW9uKQoKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn0KZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXksCiAgICAgICAgICAgICAgICAgICB5ID0gY291bnQsCiAgICAgICAgICAgICAgICAgICBncm91cCA9IGJhbm5lciwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gYmFubmVyLAogICAgICAgICAgICAgICAgICAgZmlsbCA9IGJhbm5lciwKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gY291bnQpKSArIAogIGdlb21fbGluZShzaXplID0gLjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2d0aXRsZSgnT2NjYXNpb25hbCBFZGl0b3JzIDIwMjE6IERhaWx5IEJhbm5lciBDbG9zZScpICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDQpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKIyMjIyAxLjMuMSBCYW5uZXIgQ2xvc2UgT3ZlcnZpZXc6IFRhYmxlCgoqKlRhYmxlIDEuMy4xLioqIERhaWx5IEJhbm5lciBDbG9zZQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMjIyAtLS0gRnVsbCBEYXRhc2V0IChUYWJsZSBSZXBvcnQpCmRhdGF0YWJsZShwRnJhbWUpCmBgYAoKIyMjIyAxLjMuMiBUb3RhbCBCYW5uZXIgQ2xvc2UKCioqQ2hhcnQgMS4zLjIuKiogVG90YWwgQmFubmVyIENsb3NlCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KIyAtIHBGcmFtZQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OnNlbGVjdCgtY2FtcGFpZ24pICU+JSAKICBkcGx5cjo6ZmlsdGVyKGFjdGlvbiA9PSAiYmFubmVyLWNsb3NlZCIpICU+JQogIGRwbHlyOjpzZWxlY3QoLWFjdGlvbiwgLSBkYXkpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoYmFubmVyKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZSh0b3RhbENsb3NlID0gc3VtKGNvdW50KSkKCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gYmFubmVyLCAKICAgICAgICAgICAgICAgICAgICB5ID0gdG90YWxDbG9zZSwgCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdG90YWxDbG9zZSkpICsgCiAgZ2VvbV9iYXIod2lkdGggPSAuNSwgc3RhdCA9ICJpZGVudGl0eSIpICsgCiAgZ2d0aXRsZSgnT2NjYXNpb25hbCBFZGl0b3JzIDIwMjE6IEJhbm5lciBDbG9zZScpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX2xhYmVsX3JlcGVsKHNpemUgPSAzLjUsIGNvbG9yID0gIndoaXRlIiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIyMjIDEuMy4zIEJhbm5lciBDbG9zZSBwZXIgRGF5CgoqKkNoYXJ0IDEuMy4zLioqIEJhbm5lciBDbG9zZSBwZXIgRGF5CgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KIyAtIHBGcmFtZQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OnNlbGVjdCgtY2FtcGFpZ24pICU+JSAKICBkcGx5cjo6ZmlsdGVyKGFjdGlvbiA9PSAiYmFubmVyLWNsb3NlZCIpICU+JQogIGRwbHlyOjpzZWxlY3QoLWFjdGlvbiwgLWJhbm5lcikgJT4lIAogIGRwbHlyOjpncm91cF9ieShkYXkpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKHRvdGFsQ2xvc2UgPSBzdW0oY291bnQpKQoKCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF5LCAKICAgICAgICAgICAgICAgICAgICB5ID0gdG90YWxDbG9zZSwgCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB0b3RhbENsb3NlKSkgKwogIGdlb21fcGF0aChzaXplID0gLjI1LCBncm91cCA9IDEsIGNvbG9yID0gImRhcmtibHVlIikgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgY29sb3IgPSAiZGFya2JsdWUiKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGNvbG9yID0gIndoaXRlIikgKyAKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogQmFubmVyIENsb3NlJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fbGFiZWxfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkKYGBgCgojIyMgMS40IEJhbm5lciBDbGljayBSYXRlCiMjIyMgMS40LjEgQmFubmVyIENsaWNrIFJhdGUgT3ZlcnZpZXcKCioqQ2hhcnQgMS40LjEqKiBEYWlseSBCYW5uZXIgQ2xvc2UKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIC0gcEZyYW1lCnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6c2VsZWN0KC1jYW1wYWlnbikgJT4lIAogIGRwbHlyOjpmaWx0ZXIoYWN0aW9uICE9ICJiYW5uZXItY2xvc2VkIikgJT4lIAogIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tID0gImFjdGlvbiIsIAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9ICJjb3VudCIsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlc19maWxsID0gMCkKcEZyYW1lJGNsaWNrX3JhdGUgPC0gcEZyYW1lJGBiYW5uZXItY2xpY2tlZGAvcEZyYW1lJGBiYW5uZXItc2VlbmAKcEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgZHBseXI6OnNlbGVjdChiYW5uZXIsIGRheSwgY2xpY2tfcmF0ZSkKCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICAgICAgeSA9IGNsaWNrX3JhdGUsCiAgICAgICAgICAgICAgICAgICBncm91cCA9IGJhbm5lciwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gYmFubmVyLAogICAgICAgICAgICAgICAgICAgZmlsbCA9IGJhbm5lciwgCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHJvdW5kKGNsaWNrX3JhdGUsIDIpKSkgKyAKICBnZW9tX2xpbmUoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChzaXplID0gNCkgKwogIGdndGl0bGUoJ09jY2FzaW9uYWwgRWRpdG9ycyAyMDIxOiBEYWlseSBCYW5uZXIgQ2xpY2sgUmF0ZScpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKIyMjIyAxLjQuMSBCYW5uZXIgQ2xvc2UgT3ZlcnZpZXc6IFRhYmxlCgoqKlRhYmxlIDEuNC4xLioqIERhaWx5IEJhbm5lciBDbG9zZQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMjIyAtLS0gRnVsbCBEYXRhc2V0IChUYWJsZSBSZXBvcnQpCmRhdGF0YWJsZShwRnJhbWUpCmBgYAoKIyMjIyAxLjQuMiBBdmVyYWdlIEJhbm5lciBDbGljayBSYXRlCgoqKkNoYXJ0IDEuNC4yLioqIEF2ZXJhZ2UgQmFubmVyIENsaWNrIFJhdGUKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIC0gcEZyYW1lCnBwRnJhbWUgPC0gcEZyYW1lICU+JSAKICBkcGx5cjo6c2VsZWN0KGJhbm5lciwgY2xpY2tfcmF0ZSkgJT4lIAogIGRwbHlyOjpncm91cF9ieShiYW5uZXIpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKGF2Z19jbGlja19yYXRlID0gbWVhbihjbGlja19yYXRlKSkKCmdncGxvdChwcEZyYW1lLCBhZXMoeCA9IGJhbm5lciwgCiAgICAgICAgICAgICAgICAgICAgeSA9IGF2Z19jbGlja19yYXRlLCAKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGJhbm5lciwgCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGJhbm5lciwgCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSByb3VuZChhdmdfY2xpY2tfcmF0ZSwgMikpKSArIAogIGdlb21fYmFyKHdpZHRoID0gLjUsIHN0YXQgPSAiaWRlbnRpdHkiKSArIAogIGdndGl0bGUoJ09jY2FzaW9uYWwgRWRpdG9ycyAyMDIxOiBBdmVyYWdlIEJhbm5lciBDbGljayBSYXRlJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fbGFiZWxfcmVwZWwoc2l6ZSA9IDMuNSwgY29sb3IgPSAid2hpdGUiLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMjIyMgMS40LjMgQXZlcmFnZSBCYW5uZXIgQ2xpY2sgUmF0ZSBwZXIgRGF5CgoqKkNoYXJ0IDEuMy4zLioqIEF2ZXJhZ2UgQmFubmVyIENsaWNrIFJhdGUgcGVyIERheQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMgLSBwRnJhbWUKcHBGcmFtZSA8LSBwRnJhbWUgJT4lIAogIGRwbHlyOjpzZWxlY3QoZGF5LCBjbGlja19yYXRlKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGRheSkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoYXZnX2NsaWNrX3JhdGUgPSBtZWFuKGNsaWNrX3JhdGUpKQogIApnZ3Bsb3QocHBGcmFtZSwgYWVzKHggPSBkYXksIAogICAgICAgICAgICAgICAgICAgIHkgPSBhdmdfY2xpY2tfcmF0ZSwgCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSByb3VuZChhdmdfY2xpY2tfcmF0ZSwgMikpKSArCiAgZ2VvbV9wYXRoKHNpemUgPSAuMjUsIGdyb3VwID0gMSwgY29sb3IgPSAiZGFya2JsdWUiKSArCiAgZ2VvbV9wb2ludChzaXplID0gMS41LCBjb2xvciA9ICJkYXJrYmx1ZSIpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAid2hpdGUiKSArIAogIGdndGl0bGUoJ09jY2FzaW9uYWwgRWRpdG9ycyAyMDIxOiBBdmVyYWdlIEJhbm5lciBDbGljayBSYXRlJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fbGFiZWxfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkKYGBgCgojIyAyLiBQYWdldmlld3MKCiMjIyAyLjEgUGFnZXZpZXdzIE92ZXJ2aWV3CgoqKkNoYXJ0IDIuMS4xLioqIFBhZ2V2aWV3cyBPdmVydmlldy4gCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCAgPSAxMH0KZGF0YVNldCA8LSByZWFkLmNzdigKICAnX3JlcG9ydGluZy8yMDIxX09jY2FzaW9uYWxFZGl0b3JzX1BhZ2V2aWV3cy5jc3YnLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmRhdGFTZXQkVjEgPC0gTlVMTApkYXRhU2V0IDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpmaWx0ZXIoIShncmVwbCgiMjAyMS0xMSIsIGRhdGUpKSkKZGF0YVNldCRUYWcgPC0gc2FwcGx5KGRhdGFTZXQkVGFnLCBmdW5jdGlvbih4KSB7CiAgaWYgKGdyZXBsKCJpcGFkJCIsIHgpKSB7CiAgICAiaXBhZCIKICB9IGVsc2UgaWYgKGdyZXBsKCJtb2JpbGUkIiwgeCkpIHsKICAgICJtb2JpbGUiCiAgfSBlbHNlIHsKICAgICJkZXNrdG9wIgogIH0KfSkKZGF0YVNldCRQYWdlIDwtIHNhcHBseShkYXRhU2V0JFBhZ2UsIGZ1bmN0aW9uKHgpIHsKICBpZiAoZ3JlcGwoIkYlQzMlQjZyZGVyYW5nZWJvdGUiLCB4KSkgewogICAgcmV0dXJuKCJGw7ZyZGVydW5nL0bDtnJkZXJhbmdlYm90ZSIpCiAgfSBlbHNlIGlmIChncmVwbCgiRGVpbkVuZ2FnZW1lbnQiLCB4KSkgewogICAgcmV0dXJuKCJEZWluRW5nYWdlbWVudCAiKQogIH0gZWxzZSB7CiAgICByZXR1cm4oIkxlcm5lV2lraXBlZGlhIikKICB9Cn0pCmRhdGFTZXQgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OnNlbGVjdCgtY2FtcGFpZ24pICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoZGF0ZSwgVGFnLCBQYWdlKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChkYXRhU2V0LCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICAgeSA9IGxvZyhQYWdldmlld3MpLAogICAgICAgICAgICAgICAgICAgIGdyb3VwID0gUGFnZSwKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFBhZ2UsCiAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFBhZ2UsCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogUGFnZXZpZXdzJykgKwogIHlsYWIoImxvZyhQYWdldmlld3MpIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDYsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgZmFjZXRfd3JhcCh+VGFnLCBuY29sID0gMikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gMTIpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpICsKICB0aGVtZShzdHJpcC50ZXh0ID0gIGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKCioqVGFibGUgMi4yLjEuKiogUGFnZXZpZXdzIE92ZXJ2aWV3CgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KIyMjIC0tLSBGdWxsIERhdGFzZXQgKFRhYmxlIFJlcG9ydCkKZGF0YXRhYmxlKGRhdGFTZXQgJT4lIGFycmFuZ2UoZGVzYyhQYWdldmlld3MpKSkKYGBgCgojIyMgMi4yIFBhZ2V2aWV3cyBEYWlseQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6c2VsZWN0KGRhdGUsIFBhZ2V2aWV3cykgJT4lIAogIGRwbHlyOjpncm91cF9ieShkYXRlKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF0ZSwKICAgICAgICAgICAgICAgICAgICB5ID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9saW5lKHNpemUgPSAuMjUsIGdyb3VwID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogUGFnZXZpZXdzJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gNiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gMTIpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgojIyMgMi4zIFBhZ2V2aWV3cyBwZXIgVGFnCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpzZWxlY3QoVGFnLCBQYWdldmlld3MpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVGFnKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChwRnJhbWUsIGFlcyh4ID0gVGFnLAogICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUGFnZXZpZXdzKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImRlZXBza3libHVlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ09jY2FzaW9uYWwgRWRpdG9ycyAyMDIxOiBQYWdldmlld3MnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSA2LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSAxMikpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCiMjIyMgMi4zLjEgUGFnZXZpZXdzIHBlciBUYWc6IHBhZ2V2aWV3cyBwZXIgZGV2aWNlIC0gTGFuZGluZyBQYWdlIDEgKERlaW5FbmdhZ2VtZW50KSBvbmx5CgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgZHBseXI6OmZpbHRlcihQYWdlID09ICJEZWluRW5nYWdlbWVudCAiKSAlPiUgCiAgZHBseXI6OnNlbGVjdChUYWcsIFBhZ2V2aWV3cykgJT4lIAogIGRwbHlyOjpncm91cF9ieShUYWcpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQoKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn0KZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBUYWcsCiAgICAgICAgICAgICAgICAgICB5ID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBQYWdldmlld3MpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiZGVlcHNreWJsdWUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnT2NjYXNpb25hbCBFZGl0b3JzIDIwMjE6IFBhZ2V2aWV3cycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDYsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDEyKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKIyMjIyAyLjMuMiBQYWdldmlld3MgcGVyIFRhZzogcGFnZXZpZXdzIHBlciBkZXZpY2UgLSBMYW5kaW5nIFBhZ2VzIDJhIGFuZCAyYgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGRwbHlyOjpmaWx0ZXIoUGFnZSAhPSAiRGVpbkVuZ2FnZW1lbnQgIikgJT4lIAogIGRwbHlyOjpzZWxlY3QoVGFnLCBQYWdldmlld3MpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVGFnKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChwRnJhbWUsIGFlcyh4ID0gVGFnLAogICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUGFnZXZpZXdzKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImRlZXBza3libHVlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ09jY2FzaW9uYWwgRWRpdG9ycyAyMDIxOiBQYWdldmlld3MnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSA2LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSAxMikpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCiMjIyAyLjQgUGFnZXZpZXdzIHBlciBQYWdlCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpzZWxlY3QoUGFnZSwgUGFnZXZpZXdzKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFBhZ2UpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKFBhZ2V2aWV3cyA9IHN1bShQYWdldmlld3MpKQoKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn0KZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBQYWdlLAogICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUGFnZXZpZXdzKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnT2NjYXNpb25hbCBFZGl0b3JzIDIwMjE6IFBhZ2V2aWV3cycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDYsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDEyKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKCiMjIDMuIEVkaXRpbmcgQmVoYXZpb3VyCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCAgPSAxMH0KZGF0YVNldCA8LSByZWFkLmNzdigKICAnX2FuYWx5dGljcy91c2VyRWRpdHNfRklOQUxfQU5PTi5jc3YnLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmRhdGFTZXQkYmFubmVyIDwtIHNhcHBseShkYXRhU2V0JGJhbm5lciwgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgiaXBhZCQiLCB4KSkgewogICAgImlwYWQiCiAgfSBlbHNlIGlmIChncmVwbCgibW9iaWxlJCIsIHgpKSB7CiAgICAibW9iaWxlIgogIH0gZWxzZSB7CiAgICAiZGVza3RvcCIKICB9Cn0pCmBgYAoKIyMjIDMuMSBFZGl0cyBwZXIgdGFnCgpFZGl0cyBwZXIgdGFnICoqZHVyaW5nIHRoZSBjYW1wYWlnbioqOgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5oZWlnaHQgID0gMTB9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6ZmlsdGVyKCFpcy5uYSh0c19yZXZpc2lvbikpICU+JSAKICBkcGx5cjo6ZmlsdGVyKGNhbXBhaWduRGF5ID09ICJDYW1wYWlnbiIpICU+JQogIGRwbHlyOjpzZWxlY3QoYmFubmVyKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGJhbm5lcikgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoZWRpdHMgPSBuKCkpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChwRnJhbWUsIGFlcyh4ID0gYmFubmVyLAogICAgICAgICAgICAgICAgICAgeSA9IGVkaXRzLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBlZGl0cykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJkYXJrUmVkIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ09jY2FzaW9uYWwgRWRpdG9ycyAyMDIxOiBFZGl0cycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDYsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDEyKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKCiMjIyAzLjIgRGFpbHkgZWRpdHMgcGVyIHRhZwoKRGFpbHkgZWRpdHMgcGVyIHRhZyAqKmR1cmluZyB0aGUgY2FtcGFpZ24qKjoKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcuaGVpZ2h0ICA9IDEwfQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OmZpbHRlcighaXMubmEodHNfcmV2aXNpb24pKSAlPiUKICBkcGx5cjo6ZmlsdGVyKGNhbXBhaWduRGF5ID09ICJDYW1wYWlnbiIpICU+JQogIGRwbHlyOjpzZWxlY3QoYmFubmVyLCB0c19yZXZpc2lvbikKcEZyYW1lIDwtIHBGcmFtZSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoYmFubmVyLCB0c19yZXZpc2lvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShlZGl0cyA9IG4oKSkKcEZyYW1lJHRzX3JldmlzaW9uIDwtIGFzLmZhY3RvcihwRnJhbWUkdHNfcmV2aXNpb24pCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChwRnJhbWUsIGFlcyh4ID0gdHNfcmV2aXNpb24sCiAgICAgICAgICAgICAgICAgICB5ID0gbG9nKGVkaXRzKSwKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZWRpdHMsIAogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICBmaWxsID0gYmFubmVyKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgZ2VvbV9saW5lKHNpemUgPSAuMjUpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogRWRpdHMnKSArCiAgeGxhYigiRGF5IikgKyB5bGFiKCJMb2coZWRpdHMpIikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gNiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gMTIpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgojIyMgMy40IERhaWx5IGVkaXRzICh0b3RhbCkKCkRhaWx5IGVkaXRzICh0b3RhbCkgKipkdXJpbmcgdGhlIGNhbXBhaWduKio6CgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCAgPSAxMH0KcEZyYW1lIDwtIHBGcmFtZSAlPiUKICBkcGx5cjo6c2VsZWN0KHRzX3JldmlzaW9uLCBlZGl0cykgJT4lIAogIGRwbHlyOjpncm91cF9ieSh0c19yZXZpc2lvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShlZGl0cyA9IHN1bShlZGl0cykpCnBGcmFtZSR0c19yZXZpc2lvbiA8LSBhcy5mYWN0b3IocEZyYW1lJHRzX3JldmlzaW9uKQojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IHRzX3JldmlzaW9uLAogICAgICAgICAgICAgICAgICAgeSA9IGxvZyhlZGl0cyksCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGVkaXRzKSkgKwogIGdlb21fbGluZShzaXplID0gLjI1LCBncm91cCA9IDEsIGNvbG9yID0gImRhcmtyZWQiKSArCiAgZ2VvbV9wb2ludChzaXplID0gMS41LCBjb2xvciA9ICJkYXJrcmVkIikgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnT2NjYXNpb25hbCBFZGl0b3JzIDIwMjE6IEVkaXRzJykgKwogIHhsYWIoIkRheSIpICsgeWxhYigiTG9nKGVkaXRzKSIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDYsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDEyKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKIyMjIDMuNCBFZGl0IGNsYXNzZXMKClRoZSBmb2xsb3dpbmcgb3ZlcnZpZXcgb2YgZWRpdCBjbGFzc2VzIGluY2x1ZGVzIHRoZSBgMC8xYCBlZGl0cyBjbGFzcyBzcGxpdCBhbmQgCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCAgPSAxMH0KZWRpdG9yRnJhbWUgPC0gZXhwYW5kLmdyaWQoYW5vbl91c2VyaWQgPSB1bmlxdWUoZGF0YVNldCRhbm9uX3VzZXJpZCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduRGF5ID0gdW5pcXVlKGRhdGFTZXQkY2FtcGFpZ25EYXkpKQplZGl0b3JGcmFtZSA8LSBkcGx5cjo6ZmlsdGVyKGVkaXRvckZyYW1lLCAhaXMubmEoY2FtcGFpZ25EYXkpKQpkcyA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6c2VsZWN0KGFub25fdXNlcmlkLCBjYW1wYWlnbkRheSwgdHNfcmV2aXNpb24pCmVkaXRvckZyYW1lIDwtIGVkaXRvckZyYW1lICU+JSAKICBkcGx5cjo6bGVmdF9qb2luKGRzLCAKICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiYW5vbl91c2VyaWQiLCAiY2FtcGFpZ25EYXkiKSkKZWRpdG9yRnJhbWUkdHNfcmV2aXNpb24gPC0gaWZlbHNlKGlzLm5hKGVkaXRvckZyYW1lJHRzX3JldmlzaW9uKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEpCmVkaXRvckZyYW1lIDwtIGVkaXRvckZyYW1lICU+JSAKICBkcGx5cjo6c2VsZWN0KGFub25fdXNlcmlkLCBjYW1wYWlnbkRheSwgdHNfcmV2aXNpb24pICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoYW5vbl91c2VyaWQsIGNhbXBhaWduRGF5KSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShlZGl0cyA9IHN1bSh0c19yZXZpc2lvbikpCgplZGl0b3JGcmFtZVRvdGFsIDwtIGVkaXRvckZyYW1lICU+JSAKICBkcGx5cjo6c2VsZWN0KGFub25fdXNlcmlkLCBlZGl0cykgJT4lIAogIGRwbHlyOjpncm91cF9ieShhbm9uX3VzZXJpZCkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoZWRpdHMgPSBzdW0oZWRpdHMpKQoKZWRpdEJvdW5kYXJpZXMgPC0gbGlzdCgKICBjKDAsIDApLAogIGMoMSwgMSksIAogIGMoMiwgNCksCiAgYyg1LCA5KSwKICBjKDEwLCAyMCksCiAgYygyMSwgNTApLAogIGMoNTEsIDEwMCkKKQoKZWRpdG9yRnJhbWVUb3RhbCRlZGl0Q2xhc3MgPC0gc2FwcGx5KGVkaXRvckZyYW1lVG90YWwkZWRpdHMsIGZ1bmN0aW9uKHgpIHsKICB3RUMgPC0gc2FwcGx5KGVkaXRCb3VuZGFyaWVzLCBmdW5jdGlvbih5KSB7CiAgICB4ID49IHlbMV0gJiB4IDw9IHlbMl0KICB9KQogIGlmIChzdW0od0VDKSA9PSAwKSB7CiAgICByZXR1cm4oIj49IDEwMCIpCiAgfSBlbHNlIHsKICAgIHJldHVybihwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzFdLAogICAgICAgICAgICAgICAgICAiIC0gIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsyXSwgCiAgICAgICAgICAgICAgICAgICIpIgogICAgICAgICAgICAgICAgICApCiAgICApCiAgfQp9KQoKZWRpdENsYXNzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoZWRpdG9yRnJhbWVUb3RhbCRlZGl0Q2xhc3MpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmVkaXRDbGFzc1tlZGl0Q2xhc3MgPT0gIigwIC0gMCkiXSA8LSAiMCIKZWRpdENsYXNzW2VkaXRDbGFzcyA9PSAiKDEgLSAxKSJdIDwtICIxIgpjb2xuYW1lcyhlZGl0Q2xhc3MpIDwtIGMoJ0VkaXQgQ2xhc3MnLCAnTnVtLlVzZXJzJykKZWRpdENsYXNzJG9yZGVyIDwtIGFzLm51bWVyaWMoc2FwcGx5KGVkaXRDbGFzcyRgRWRpdCBDbGFzc2AsIGZ1bmN0aW9uKHgpIHsKICBsb3dlciA8LSBzdHJfZXh0cmFjdCh4LCAnW1s6ZGlnaXQ6XV0rJykKfSkpCmVkaXRDbGFzcyA8LSBhcnJhbmdlKGVkaXRDbGFzcywgb3JkZXIpCmVkaXRDbGFzcyRvcmRlciA8LSBOVUxMCmRhdGF0YWJsZShlZGl0Q2xhc3MpCmBgYAoKIyMjIDMuNSBEYWlseSB1c2VyIGVkaXRzIGJlZm9yZSwgZHVyaW5nLCBhbmQgYWZ0ZXIgY2FtcGFpZ24KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OmZpbHRlcighaXMubmEodHNfcmV2aXNpb24pKSAlPiUgCiAgZHBseXI6OnNlbGVjdCh0c19yZXZpc2lvbiwgY2FtcGFpZ25EYXkpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkodHNfcmV2aXNpb24sIGNhbXBhaWduRGF5KSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShFZGl0cyA9IG4oKSkKY29sbmFtZXMocEZyYW1lKSA8LSBjKCJkYXRlIiwgImNhbXBhaWduIiwgIkVkaXRzIikKZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgeSA9IEVkaXRzLAogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBjYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY2FtcGFpZ24sCiAgICAgICAgICAgICAgICAgICBmaWxsID0gY2FtcGFpZ24sCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IEVkaXRzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiKSArCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnMjAyMSBXTURFIE9jY2FzaW9uYWwgRWRpdG9ycyBCYW5uZXIgQ2FtcGFpZ24nKSArCiAgeGxhYigiRGF0ZSIpICsgeWxhYigiRWRpdHMiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA2KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KdEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgZHBseXI6OnNlbGVjdChjYW1wYWlnbiwgRWRpdHMpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoY2FtcGFpZ24pICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKHRvdGFsRWRpdHMgPSBzdW0oRWRpdHMpLAogICAgICAgICAgICAgICAgICAgYXZnRWRpdHMgPSByb3VuZChtZWFuKEVkaXRzKSwgMikpCkRUOjpkYXRhdGFibGUodEZyYW1lKQpgYGAKCioqRWRpdCBDbGFzc2VzOiBCZWZvcmUgQ2FtcGFpZ24qKgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5oZWlnaHQgID0gMTB9CmVkaXRvckZyYW1lIDwtIGV4cGFuZC5ncmlkKGFub25fdXNlcmlkID0gdW5pcXVlKGRhdGFTZXQkYW5vbl91c2VyaWQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbkRheSA9IHVuaXF1ZShkYXRhU2V0JGNhbXBhaWduRGF5KSkKZWRpdG9yRnJhbWUgPC0gZHBseXI6OmZpbHRlcihlZGl0b3JGcmFtZSwgIWlzLm5hKGNhbXBhaWduRGF5KSkKZHMgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OnNlbGVjdChhbm9uX3VzZXJpZCwgY2FtcGFpZ25EYXksIHRzX3JldmlzaW9uKQplZGl0b3JGcmFtZSA8LSBlZGl0b3JGcmFtZSAlPiUgCiAgZHBseXI6OmxlZnRfam9pbihkcywgCiAgICAgICAgICAgICAgICAgICBieSA9IGMoImFub25fdXNlcmlkIiwgImNhbXBhaWduRGF5IikpCmVkaXRvckZyYW1lJHRzX3JldmlzaW9uIDwtIGlmZWxzZShpcy5uYShlZGl0b3JGcmFtZSR0c19yZXZpc2lvbiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxKQplZGl0b3JGcmFtZSA8LSBlZGl0b3JGcmFtZSAlPiUgCiAgZHBseXI6OnNlbGVjdChhbm9uX3VzZXJpZCwgY2FtcGFpZ25EYXksIHRzX3JldmlzaW9uKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGFub25fdXNlcmlkLCBjYW1wYWlnbkRheSkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoZWRpdHMgPSBzdW0odHNfcmV2aXNpb24pKSAlPiUgCiAgZHBseXI6OmZpbHRlcihjYW1wYWlnbkRheSA9PSAiYmVmb3JlQ2FtcGFpZ24iKQoKZWRpdEJvdW5kYXJpZXMgPC0gbGlzdCgKICBjKDAsIDApLAogIGMoMSwgMSksIAogIGMoMiwgNCksCiAgYyg1LCA5KSwKICBjKDEwLCAyMCksCiAgYygyMSwgNTApLAogIGMoNTEsIDEwMCkKKQoKZWRpdG9yRnJhbWUkZWRpdENsYXNzIDwtIHNhcHBseShlZGl0b3JGcmFtZSRlZGl0cywgZnVuY3Rpb24oeCkgewogIHdFQyA8LSBzYXBwbHkoZWRpdEJvdW5kYXJpZXMsIGZ1bmN0aW9uKHkpIHsKICAgIHggPj0geVsxXSAmIHggPD0geVsyXQogIH0pCiAgaWYgKHN1bSh3RUMpID09IDApIHsKICAgIHJldHVybigiPj0gMTAwIikKICB9IGVsc2UgewogICAgcmV0dXJuKHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMV0sCiAgICAgICAgICAgICAgICAgICIgLSAiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzJdLCAKICAgICAgICAgICAgICAgICAgIikiCiAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0pCgplZGl0Q2xhc3MgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShlZGl0b3JGcmFtZSRlZGl0Q2xhc3MpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmVkaXRDbGFzc1tlZGl0Q2xhc3MgPT0gIigwIC0gMCkiXSA8LSAiMCIKZWRpdENsYXNzW2VkaXRDbGFzcyA9PSAiKDEgLSAxKSJdIDwtICIxIgpjb2xuYW1lcyhlZGl0Q2xhc3MpIDwtIGMoJ0VkaXQgQ2xhc3MnLCAnTnVtLlVzZXJzJykKZWRpdENsYXNzJG9yZGVyIDwtIGFzLm51bWVyaWMoc2FwcGx5KGVkaXRDbGFzcyRgRWRpdCBDbGFzc2AsIGZ1bmN0aW9uKHgpIHsKICBsb3dlciA8LSBzdHJfZXh0cmFjdCh4LCAnW1s6ZGlnaXQ6XV0rJykKfSkpCmVkaXRDbGFzcyA8LSBhcnJhbmdlKGVkaXRDbGFzcywgb3JkZXIpCmVkaXRDbGFzcyRvcmRlciA8LSBOVUxMCmRhdGF0YWJsZShlZGl0Q2xhc3MpCmBgYAoKKipFZGl0IENsYXNzZXM6IER1cmluZyBDYW1wYWlnbioqCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCAgPSAxMH0KZWRpdG9yRnJhbWUgPC0gZXhwYW5kLmdyaWQoYW5vbl91c2VyaWQgPSB1bmlxdWUoZGF0YVNldCRhbm9uX3VzZXJpZCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduRGF5ID0gdW5pcXVlKGRhdGFTZXQkY2FtcGFpZ25EYXkpKQplZGl0b3JGcmFtZSA8LSBkcGx5cjo6ZmlsdGVyKGVkaXRvckZyYW1lLCAhaXMubmEoY2FtcGFpZ25EYXkpKQpkcyA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6c2VsZWN0KGFub25fdXNlcmlkLCBjYW1wYWlnbkRheSwgdHNfcmV2aXNpb24pCmVkaXRvckZyYW1lIDwtIGVkaXRvckZyYW1lICU+JSAKICBkcGx5cjo6bGVmdF9qb2luKGRzLCAKICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiYW5vbl91c2VyaWQiLCAiY2FtcGFpZ25EYXkiKSkKZWRpdG9yRnJhbWUkdHNfcmV2aXNpb24gPC0gaWZlbHNlKGlzLm5hKGVkaXRvckZyYW1lJHRzX3JldmlzaW9uKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEpCmVkaXRvckZyYW1lIDwtIGVkaXRvckZyYW1lICU+JSAKICBkcGx5cjo6c2VsZWN0KGFub25fdXNlcmlkLCBjYW1wYWlnbkRheSwgdHNfcmV2aXNpb24pICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoYW5vbl91c2VyaWQsIGNhbXBhaWduRGF5KSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShlZGl0cyA9IHN1bSh0c19yZXZpc2lvbikpICU+JSAKICBkcGx5cjo6ZmlsdGVyKGNhbXBhaWduRGF5ID09ICJDYW1wYWlnbiIpCgplZGl0Qm91bmRhcmllcyA8LSBsaXN0KAogIGMoMCwgMCksCiAgYygxLCAxKSwgCiAgYygyLCA0KSwKICBjKDUsIDkpLAogIGMoMTAsIDIwKSwKICBjKDIxLCA1MCksCiAgYyg1MSwgMTAwKQopCgplZGl0b3JGcmFtZSRlZGl0Q2xhc3MgPC0gc2FwcGx5KGVkaXRvckZyYW1lJGVkaXRzLCBmdW5jdGlvbih4KSB7CiAgd0VDIDwtIHNhcHBseShlZGl0Qm91bmRhcmllcywgZnVuY3Rpb24oeSkgewogICAgeCA+PSB5WzFdICYgeCA8PSB5WzJdCiAgfSkKICBpZiAoc3VtKHdFQykgPT0gMCkgewogICAgcmV0dXJuKCI+PSAxMDAiKQogIH0gZWxzZSB7CiAgICByZXR1cm4ocGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsxXSwKICAgICAgICAgICAgICAgICAgIiAtICIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMl0sIAogICAgICAgICAgICAgICAgICAiKSIKICAgICAgICAgICAgICAgICAgKQogICAgKQogIH0KfSkKCmVkaXRDbGFzcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKGVkaXRvckZyYW1lJGVkaXRDbGFzcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKZWRpdENsYXNzW2VkaXRDbGFzcyA9PSAiKDAgLSAwKSJdIDwtICIwIgplZGl0Q2xhc3NbZWRpdENsYXNzID09ICIoMSAtIDEpIl0gPC0gIjEiCmNvbG5hbWVzKGVkaXRDbGFzcykgPC0gYygnRWRpdCBDbGFzcycsICdOdW0uVXNlcnMnKQplZGl0Q2xhc3Mkb3JkZXIgPC0gYXMubnVtZXJpYyhzYXBwbHkoZWRpdENsYXNzJGBFZGl0IENsYXNzYCwgZnVuY3Rpb24oeCkgewogIGxvd2VyIDwtIHN0cl9leHRyYWN0KHgsICdbWzpkaWdpdDpdXSsnKQp9KSkKZWRpdENsYXNzIDwtIGFycmFuZ2UoZWRpdENsYXNzLCBvcmRlcikKZWRpdENsYXNzJG9yZGVyIDwtIE5VTEwKZGF0YXRhYmxlKGVkaXRDbGFzcykKYGBgCgoqKkVkaXQgQ2xhc3NlczogQWZ0ZXIgQ2FtcGFpZ24qKgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5oZWlnaHQgID0gMTB9CmVkaXRvckZyYW1lIDwtIGV4cGFuZC5ncmlkKGFub25fdXNlcmlkID0gdW5pcXVlKGRhdGFTZXQkYW5vbl91c2VyaWQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbkRheSA9IHVuaXF1ZShkYXRhU2V0JGNhbXBhaWduRGF5KSkKZWRpdG9yRnJhbWUgPC0gZHBseXI6OmZpbHRlcihlZGl0b3JGcmFtZSwgIWlzLm5hKGNhbXBhaWduRGF5KSkKZHMgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OnNlbGVjdChhbm9uX3VzZXJpZCwgY2FtcGFpZ25EYXksIHRzX3JldmlzaW9uKQplZGl0b3JGcmFtZSA8LSBlZGl0b3JGcmFtZSAlPiUgCiAgZHBseXI6OmxlZnRfam9pbihkcywgCiAgICAgICAgICAgICAgICAgICBieSA9IGMoImFub25fdXNlcmlkIiwgImNhbXBhaWduRGF5IikpCmVkaXRvckZyYW1lJHRzX3JldmlzaW9uIDwtIGlmZWxzZShpcy5uYShlZGl0b3JGcmFtZSR0c19yZXZpc2lvbiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxKQplZGl0b3JGcmFtZSA8LSBlZGl0b3JGcmFtZSAlPiUgCiAgZHBseXI6OnNlbGVjdChhbm9uX3VzZXJpZCwgY2FtcGFpZ25EYXksIHRzX3JldmlzaW9uKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGFub25fdXNlcmlkLCBjYW1wYWlnbkRheSkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoZWRpdHMgPSBzdW0odHNfcmV2aXNpb24pKSAlPiUgCiAgZHBseXI6OmZpbHRlcihjYW1wYWlnbkRheSA9PSAiYWZ0ZXJDYW1wYWlnbiIpCgplZGl0Qm91bmRhcmllcyA8LSBsaXN0KAogIGMoMCwgMCksCiAgYygxLCAxKSwgCiAgYygyLCA0KSwKICBjKDUsIDkpLAogIGMoMTAsIDIwKSwKICBjKDIxLCA1MCksCiAgYyg1MSwgMTAwKQopCgplZGl0b3JGcmFtZSRlZGl0Q2xhc3MgPC0gc2FwcGx5KGVkaXRvckZyYW1lJGVkaXRzLCBmdW5jdGlvbih4KSB7CiAgd0VDIDwtIHNhcHBseShlZGl0Qm91bmRhcmllcywgZnVuY3Rpb24oeSkgewogICAgeCA+PSB5WzFdICYgeCA8PSB5WzJdCiAgfSkKICBpZiAoc3VtKHdFQykgPT0gMCkgewogICAgcmV0dXJuKCI+PSAxMDAiKQogIH0gZWxzZSB7CiAgICByZXR1cm4ocGFzdGUwKCIoIiwKICAgICAgICAgICAgICAgICAgZWRpdEJvdW5kYXJpZXNbW3doaWNoKHdFQyldXVsxXSwKICAgICAgICAgICAgICAgICAgIiAtICIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMl0sIAogICAgICAgICAgICAgICAgICAiKSIKICAgICAgICAgICAgICAgICAgKQogICAgKQogIH0KfSkKCmVkaXRDbGFzcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKGVkaXRvckZyYW1lJGVkaXRDbGFzcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKZWRpdENsYXNzW2VkaXRDbGFzcyA9PSAiKDAgLSAwKSJdIDwtICIwIgplZGl0Q2xhc3NbZWRpdENsYXNzID09ICIoMSAtIDEpIl0gPC0gIjEiCmNvbG5hbWVzKGVkaXRDbGFzcykgPC0gYygnRWRpdCBDbGFzcycsICdOdW0uVXNlcnMnKQplZGl0Q2xhc3Mkb3JkZXIgPC0gYXMubnVtZXJpYyhzYXBwbHkoZWRpdENsYXNzJGBFZGl0IENsYXNzYCwgZnVuY3Rpb24oeCkgewogIGxvd2VyIDwtIHN0cl9leHRyYWN0KHgsICdbWzpkaWdpdDpdXSsnKQp9KSkKZWRpdENsYXNzIDwtIGFycmFuZ2UoZWRpdENsYXNzLCBvcmRlcikKZWRpdENsYXNzJG9yZGVyIDwtIE5VTEwKZGF0YXRhYmxlKGVkaXRDbGFzcykKYGBgCgoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CmRld2lraUVkaXRzIDwtIHJlYWQuY3N2KHBhc3RlMChnZXR3ZCgpLCIvX3JlcG9ydGluZy8iLCAiYXZnX2VkaXRzX2Rld2lraS5jc3YiKSwKICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmRld2lraUVkaXRzJGRhdGVUeXBlIDwtIGFzLmNoYXJhY3RlcihkZXdpa2lFZGl0cyRyZXZhY3Rvcl90aW1lc3RhbXApCmRld2lraUVkaXRzJGRhdGVUeXBlIDwtIHN1YnN0cihkZXdpa2lFZGl0cyRkYXRlVHlwZSwgMSwgOCkKZGV3aWtpRWRpdHMkZGF0ZVR5cGUgPC0gc2FwcGx5KGRld2lraUVkaXRzJGRhdGVUeXBlLCBmdW5jdGlvbih4KSB7CiAgZCA8LSBwYXN0ZTAoCiAgICAgIHN1YnN0cih4LCAxLCA0KSwgCiAgICAgICItIiwKICAgICAgc3Vic3RyKHgsIDUsIDYpLAogICAgICAiLSIsCiAgICAgIHN1YnN0cih4LCA3LCA4KQogICAgKQogIHJldHVybihkKQp9KQpkZXdpa2lFZGl0cyA8LSBkcGx5cjo6YXJyYW5nZShkZXdpa2lFZGl0cywgZGF0ZVR5cGUpICU+JSAKICBkcGx5cjo6c2VsZWN0KHVzZXJfaWQsIGRhdGVUeXBlKQoKY2FtcGFpZ25EYXlzIDwtIGMoIjIwMjEtMTAtMTgiLCAiMjAyMS0xMC0xOSIsICIyMDIxLTEwLTIwIiwgIjIwMjEtMTAtMjEiLCAiMjAyMS0xMC0yMiIsICIyMDIxLTEwLTIzIiwKICAgICAgICAgICAgICAgICAgIjIwMjEtMTAtMjQiLCAiMjAyMS0xMC0yNSIsICIyMDIxLTEwLTI2IiwgIjIwMjEtMTAtMjciLCAiMjAyMS0xMC0yOCIsICIyMDIxLTEwLTI5IiwKICAgICAgICAgICAgICAgICAgIjIwMjEtMTAtMzAiLCAiMjAyMS0xMC0zMSIpCgpkZXdpa2lFZGl0cyA8LSBkZXdpa2lFZGl0cyAlPiUgCiAgc2VsZWN0KGRhdGVUeXBlKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZVR5cGUpICU+JSAKICBzdW1tYXJpc2UoRWRpdHMgPSBuKCkpCmRld2lraUVkaXRzIDwtIGFycmFuZ2UoZGV3aWtpRWRpdHMsIGRhdGVUeXBlKQpkZXdpa2lFZGl0cyRjYW1wYWlnbiA8LSBzYXBwbHkoZGV3aWtpRWRpdHMkZGF0ZVR5cGUsIGZ1bmN0aW9uKHgpIHsKICBpZiAoeCA8ICIyMDIxLTEwLTE4IikgewogICAgcmV0dXJuKCJCZWZvcmUgQ2FtcGFpZ24iKQogIH0gZWxzZSBpZiAoeCA+ICIyMDIxLTEwLTMxIikgewogICAgcmV0dXJuKCJBZnRlciBDYW1wYWlnbiIpIAogIH0gZWxzZSB7CiAgICByZXR1cm4oIkNhbXBhaWduIikKICB9Cn0pCgpjYW1wYWlnbkJlZm9yZSA8LSBtZWFuKHBGcmFtZSRFZGl0c1twRnJhbWUkY2FtcGFpZ24gPT0gImJlZm9yZUNhbXBhaWduIl0pCmNhbXBhaWduIDwtIG1lYW4ocEZyYW1lJEVkaXRzW3BGcmFtZSRjYW1wYWlnbiA9PSAiQ2FtcGFpZ24iXSkKY2FtcGFpZ25BZnRlciA8LSBtZWFuKHBGcmFtZSRFZGl0c1twRnJhbWUkY2FtcGFpZ24gPT0gImFmdGVyQ2FtcGFpZ24iXSkKCmRld2lraUJlZm9yZSA8LSBtZWFuKGRld2lraUVkaXRzJEVkaXRzW2Rld2lraUVkaXRzJGNhbXBhaWduID09ICJCZWZvcmUgQ2FtcGFpZ24iXSkKZGV3aWtpIDwtIG1lYW4oZGV3aWtpRWRpdHMkRWRpdHNbZGV3aWtpRWRpdHMkY2FtcGFpZ24gPT0gIkNhbXBhaWduIl0pCmRld2lraUFmdGVyIDwtIG1lYW4oZGV3aWtpRWRpdHMkRWRpdHNbZGV3aWtpRWRpdHMkY2FtcGFpZ24gPT0gIkFmdGVyIENhbXBhaWduIl0pCmBgYAoKSW4gY29tcGFyaXNvbiB0byB0aGUgb3ZlcmFsbCBlZGl0cyBpbiBgZGV3aWtpYCwgaW4gdGhlIHNhbWUgdGltZSBzcGFuIGFzIGluIHRoZSBjaGFydCBhYm92ZToKCi0gYmVmb3JlIHRoZSBjYW1wYWlnbiwgdGhlIGNhbXBhaWduIHVzZXJzIG1hZGUgYHIgcm91bmQoY2FtcGFpZ25CZWZvcmUsMilgIGVkaXRzIG9uIHRoZSBhdmVyYWdlIGRhaWx5LCB3aGlsZSBpbiBgZGV3aWtpYCB3ZSBmaW5kIGByIHJvdW5kKGRld2lraUJlZm9yZSwgMilgIGVkaXRzOwotIGR1cmluZyB0aGUgY2FtcGFpZ24sIHRoZSBjYW1wYWlnbiB1c2VycyBtYWRlIGByIHJvdW5kKGNhbXBhaWduLCAyKWAgZWRpdHMgb24gdGhlIGF2ZXJhZ2UgZGFpbHksIHdoaWxlIGluIGBkZXdpa2lgIHdlIGZpbmQgYHIgcm91bmQoZGV3aWtpLCAyKWAgZWRpdHM7Ci0gYW5kIGFmdGVyIHRoZSBjYW1wYWlnbiwgdGhlIGNhbXBhaWduIHVzZXJzIG1hZGUgYHIgcm91bmQoY2FtcGFpZ25BZnRlciwgMilgIGVkaXRzIG9uIHRoZSBhdmVyYWdlIGRhaWx5LCB3aGlsZSBpbiBgZGV3aWtpYCB3ZSBmaW5kIGByIHJvdW5kKGRld2lraUFmdGVyLCAyKWAgZWRpdHM7Cg==