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

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(
  '_reporting/userEdits_Complete.csv',
                    header = T,
                    row.names = 1,
                    check.names = F,
                    stringsAsFactors = F)
dataSet$ts_revision <- as.character(dataSet$ts_revision)
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

pFrame <- dataSet %>% 
  dplyr::filter(!is.na(ts_revision)) %>% 
  dplyr::select(banner) %>% 
  dplyr::group_by(banner) %>% 
  dplyr::summarise(edits = n())
# - Visualize w. {ggplot2}
ggplot(pFrame, aes(x = banner,
                   y = edits,
                   label = edits)) +
  geom_bar(stat = "identity", fill = "darkRed") +
  scale_y_continuous(labels = comma) +
  ggtitle('Occasional Editors 2021: Edits') +
  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.2 How many banner impressions before the first edit?

Note. The chart shows how many users (vertical, y-axis -> log scale, the data point labels are absolute counts) made their first edit following how many banner impressions (horizontal, x-axis) per campaign channel (desktop, mobile, ipad).

pFrame <- dataSet %>% 
  dplyr::filter(!is.na(ts_revision)) %>% 
  dplyr::select(banner, banner_impressions) %>% 
  dplyr::group_by(banner, banner_impressions) %>% 
  dplyr::summarise(users = n()) %>% 
  dplyr::filter(banner_impressions != 0)
pFrame$banner_impressions <- as.factor(pFrame$banner_impressions)

# - Visualize w. {ggplot2}
ggplot(pFrame, aes(x = banner_impressions,
                   y = log(users),
                   label = users, 
                   group = banner, 
                   color = banner,
                   fill = banner)) +
  geom_point(size = 1.5) + 
  geom_point(size = 1, color = "white") + 
  geom_line(size = .25) +
  scale_y_continuous(labels = comma) +
  ggtitle('Occasional Editors 2021: Edits') +
  theme_minimal() + 
  geom_text_repel(size = 6, show.legend = FALSE) + 
  theme(axis.text.x = element_text(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.3 Daily edits per tag

pFrame <- dataSet %>% 
  dplyr::filter(!is.na(ts_revision)) %>% 
  dplyr::select(banner, ts_revision)
pFrame$ts_revision <- substr(pFrame$ts_revision, 5, 8)
pFrame$ts_revision <- sapply(pFrame$ts_revision, function(x) {
  first <- substr(x, 1, 2)
  second <- substr(x, 3, 4)
  return(
    paste0(first, "-", second)
  )
})
pFrame <- pFrame %>%
  dplyr::group_by(banner, ts_revision) %>%
  dplyr::summarise(edits = n())
pFrame$ts_revision <- as.factor(pFrame$ts_revision)
# - Visualize w. {ggplot2}
ggplot(pFrame, aes(x = ts_revision,
                   y = log(edits),
                   label = edits, 
                   group = banner, 
                   color = banner,
                   fill = banner)) +
  geom_point(size = 1.5) + 
  geom_point(size = 1, color = "white") + 
  geom_line(size = .25) +
  scale_y_continuous(labels = comma) +
  ggtitle('Occasional Editors 2021: Edits') +
  xlab("Day") + ylab("Log(edits)") +
  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.4 Daily edits (total)

pFrame <- pFrame %>%
  dplyr::select(ts_revision, edits) %>% 
  dplyr::group_by(ts_revision) %>%
  dplyr::summarise(edits = sum(edits))
Adding missing grouping variables: `banner`
pFrame$ts_revision <- as.factor(pFrame$ts_revision)
# - Visualize w. {ggplot2}
ggplot(pFrame, aes(x = ts_revision,
                   y = log(edits),
                   label = edits)) +
  geom_line(size = .25, group = 1, color = "darkred") +
  geom_point(size = 1.5, color = "darkred") + 
  geom_point(size = 1, color = "white") + 
  scale_y_continuous(labels = comma) +
  ggtitle('Occasional Editors 2021: Edits') +
  xlab("Day") + ylab("Log(edits)") +
  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.4 Edit classes

pFrame <- dataSet %>% 
  dplyr::filter(!is.na(ts_revision)) %>% 
  dplyr::select(userid) %>% 
  dplyr::group_by(userid) %>% 
  dplyr::summarise(edits = n())

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

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

3.5 Daily user edits before, during, and after campaign

userEdits <- read.csv(paste0(getwd(),"/_analytics/", "userEdits_History.csv"),
                      header = TRUE, 
                      check.names = FALSE,
                      row.names = 1,
                      stringsAsFactors = FALSE)
userEdits$dateType <- as.character(userEdits$revactor_timestamp)
userEdits$dateType <- substr(userEdits$dateType, 1, 8)
userEdits$dateType <- sapply(userEdits$dateType, function(x) {
  d <- paste0(
      substr(x, 1, 4), 
      "-",
      substr(x, 5, 6),
      "-",
      substr(x, 7, 8)
    )
  return(d)
})
userEdits <- dplyr::arrange(userEdits, dateType) %>% 
  select(actor_user, 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")

pFrame <- userEdits %>% 
  select(dateType) %>% 
  group_by(dateType) %>% 
  summarise(Edits = n())
pFrame <- arrange(pFrame, dateType)
pFrame$campaign <- sapply(pFrame$dateType, function(x) {
  if (x < "2021-10-18") {
    return("Before Campaign")
  } else if (x > "2021-10-31") {
    return("After Campaign") 
  } else {
    return("Campaign")
  }
})
pFrame$date <- as.character(pFrame$dateType)
ggplot(pFrame, aes(x = date,
                   y = Edits,
                   group = campaign,
                   color = campaign,
                   fill = campaign,
                   label = Edits,
                    )) + 
  geom_path(size = .5) + 
  geom_point(size = 1.5) +
  geom_point(size = 1, fill = "white", color = "white") +
  geom_text_repel(size = 3.5, show.legend = FALSE) + 
  scale_y_continuous(labels = comma) +
  ggtitle('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")

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 == "Before Campaign"])
campaign <- mean(pFrame$Edits[pFrame$campaign == "Campaign"])
campaignAfter <- mean(pFrame$Edits[pFrame$campaign == "After Campaign"])

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 100.46 edits on the average daily, while in dewiki we find 1234.18 edits;
  • during the campaign, the campaign users made 195.86 edits on the average daily, while in dewiki we find 1256.5 edits;
  • and after the campaign, the campaign users made 159.25 edits on the average daily, while in dewiki we find 1350.32 edits;
LS0tCnRpdGxlOiAnT2NjYXNpb25hbCBFZGl0b3JzIDIwMjE6IEludGVyaW0gUmVwb3J0JwphdXRob3I6ICJHb3JhbiBTLiBNaWxvdmFub3ZpYywgRGF0YSBTY2llbnRpc3QsIFdNREUiCmRhdGU6ICJOb3ZlbWJlciAzLCAyMDIxIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdGhlbWU6IGNvc21vCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogNQogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA1Ci0tLQoKKipGZWVkYmFjayoqIHNob3VsZCBiZSBzZW5kIHRvIGBnb3Jhbi5taWxvdmFub3ZpY19leHRAd2lraW1lZGlhLmRlYC4gCgpUaGUgY2FtcGFpZ24gaXMgcnVuIGZyb20gMjAyMS8xMC8xOCB0byAyMDIxLzEwLzMxLgoKKipDVVJSRU5UIFVQREFURToqKiBDb21wbGV0ZSBkYXRhc2V0IGFzIG9mIDIwMjEvMTAvMzEuCgpgYGB7ciwgZWNobyA9IEYsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGLCByZXN1bHRzID0gJ2hpZGUnfQojICFkaWFnbm9zdGljcyBvZmYKIyMjIC0tLSBTZXR1cApvcHRpb25zKGRwbHlyLnN1bW1hcmlzZS5pbmZvcm0gPSBGQUxTRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy53aWR0aCA9IDE1LCBmaWcuaGVpZ2h0ID0gOCkgCnJtKGxpc3QgPSBscygpKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KHJtYXJrZG93bikKbGlicmFyeShrbml0cikKbGlicmFyeShEVCkKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShwdXJycikKYGBgCgojIyAwLiBEYXRhIEFjcXVpc2l0b24KCioqTk9URToqKiB0aGUgRGF0YSBBY3F1aXNpdGlvbiBjb2RlIGNodW5rIGlzIG5vdCBmdWxseSByZXByb2R1Y2libGUgZnJvbSB0aGlzIFJlcG9ydC4gVGhlIGRhdGEgYXJlIGNvbGxlY3RlZCBieSBydW5uaW5nIGFuIFIgc2NyaXB0IG9uIGBzdGF0MTAwNy5lcWlhZC53bW5ldGAsIGNvbGxlY3RpbmcgdGhlIGRhdGEgYXMgYC50c3ZgIGFuZCBgLmNzdmAgZmlsZXMsIGNvcHlpbmcgbWFudWFsbHksIGFuZCBwcm9jZXNzaW5nIGxvY2FsbHkuIAoKIyMjIDAuMSBEYWlseSBVcGRhdGUKCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZ9CgojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIFdNREUgMjAyMSBPY2Fzc2lvbmFsIEVkaXRvcnMKIyMjIC0tLSBodHRwczovL3BoYWJyaWNhdG9yLndpa2ltZWRpYS5vcmcvVDI5MTYzNQojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBDYW1wYWlnbiBEZXNjcmlwdGlvbiBhbmQgUGFyYW1ldGVycwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIExhbmRpbmcgUGFnZXM6CiMgLSBMYW5kaW5nIFBhZ2UgMTogaHR0cHM6Ly9kZS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9EZWluRW5nYWdlbWVudAojIC0gTGFuZGluZyBQYWdlIDJhOiBodHRwczovL3Rvb2xzLndtZmxhYnMub3JnL21pdG1hY2hlbi8KIyAtIExhbmRpbmcgUGFnZSAyYjogaHR0cHM6Ly9kZS53aWtpcGVkaWEub3JnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9MZXJuZVdpa2lwZWRpYQojIC0gTGFuZGluZyBQYWdlIDJjOiBodHRwczovL2RlLndpa2lwZWRpYS5vcmcvd2lraS9XaWtpcGVkaWE6RiVDMyVCNnJkZXJ1bmcvRiVDMyVCNnJkZXJhbmdlYm90ZQojIC0gQ2FtcGFpZ24gdGFnczoKIyAtIFdNREVfb2NlZGl0b3JzX2ZhbGxfMjAyMQoKIyAtIFN0YXJ0IG9mIHRoZSBiYW5uZXIgY2FtcGFpZ246IE9jdG9iZXIgMTJ0aAojIC0gRW5kIG9mIHRoZSBiYW5uZXIgY2FtcGFpZ246IE9jdG9iZXIgMjYgdGgKIyAtIFRyYWNraW5nIHRlc3Q6IE9jdG9iZXIgNSAtIDcKIyAtIFByZWxpbWluYXJ5IHJlcG9ydCBmb3IgdHJhY2tpbmc6IGJlZ2lubmluZyBvZiBOb3ZlbWJlcgojIC0gVHJhY2sgZWRpdGluZyBiZWhhdmlvciBmb3VyIHdlZWtzIGFmdGVyIGVuZCBvZiBjYW1wYWlnbjogTm92ZW1iZXIgMjNyZAojIC0gZmluYWwgcmVwb3J0IGZvciB0cmFja2luZzogYmVnaW5uaW5nIG9mIERlY2VtYmVyCgojIyMgLS0tIGxpYnJhcmllcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKCiMjIyAtLS0gY2FtcGFpZ24gc3BlY2lmaWNzCmNhbXBhaWduTmFtZSA8LSAnT2NjYXNpb25hbEVkaXRvcnNfMjAyMScKCiMjIyAtLS0gZGlyIHN0cnVjdHVyZQpjYW1wYWlnblBhdGggPC0gJy9ob21lL2dvcmFuc20vQW5hbHl0aWNzL05ld0VkaXRvcnMvMjAyMV9PY2Nhc2lvbmFsRWRpdG9ycy8nCmRhdGFEaXIgPC0gcGFzdGUwKGNhbXBhaWduUGF0aCwgIl9kYXRhLyIpCmFuYWx5dGljc0RpciA8LSBwYXN0ZTAoY2FtcGFpZ25QYXRoLCAiX2FuYWx5dGljcy8iKQpwdWJsaWNEaXIgPC0gJy9zcnYvcHVibGlzaGVkL2RhdGFzZXRzL3dtZGUtYW5hbHl0aWNzLWVuZ2luZWVyaW5nL05ld0VkaXRvcnMvY2FtcGFpZ25zLzIwMjFfT2NjYXNpb25hbEVkaXRvcnMvJwoKIyMjIC0tLSBkZXRlcm1pbmUgY2V0RGF5CmNldERheSA8LSBTeXMudGltZSgpCmNldERheQphdHRyKGNldERheSwgInR6b25lIikgPC0gIkV1cm9wZS9CZXJsaW4iCiMgLSBvbmUgZGF5IGJlaGluZCBmb3IgY3JvbnRhYgojIC0gKGkuZS4gd2FpdGluZyBmb3Igd21mLndlYnJlcXVlc3QgdG8gY29tcGxldGUgaXMgZGF0YSBhY3F1aXNpdGlvbikKY2V0RGF5IDwtIHltZCgKICBzdHJzcGxpdChhcy5jaGFyYWN0ZXIoY2V0RGF5KSwgCiAgICAgICAgICAgc3BsaXQgPSAiICIsIAogICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXQopIC0gMQoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBCYW5uZXIgSW50ZXJhY3Rpb25zOgojIyMgLS0tIHZpYSBldmVudC5XTURFQmFubmVySW50ZXJhY3Rpb25zCiMjIyAtLS0gaHR0cHM6Ly9tZXRhLndpa2ltZWRpYS5vcmcvd2lraS9TY2hlbWE6V01ERUJhbm5lckludGVyYWN0aW9ucwojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIHNlbGVjdCBkdCwgZXZlbnQuYmFubmVyTmFtZSwgZXZlbnQuYmFubmVyQWN0aW9uLCBldmVudC5iYW5uZXJJbXByZXNzaW9ucywgZXZlbnQudXNlcklEIAojIC0gZnJvbSBldmVudC53bWRlYmFubmVyaW50ZXJhY3Rpb25zIHdoZXJlIHllYXI9MjAyMCBhbmQgbW9udGg9NSBhbmQgKGRheT0xMSBvciBkYXk9MTIgb3IgZGF5PTEzKTsKCiMgLSBmdW5jdGlvbjogd21kZV9iYW5uZXJfYWN0aW9ucwp3bWRlX2Jhbm5lcl9hY3Rpb25zIDwtIGZ1bmN0aW9uKHVyaV9xdWVyeV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmFseXRpY3NEaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lKSB7CiAgCiAgIyAtIE5PVEU6CiAgIyAtIGV4cGVjdGVkIGZvcm1hdCBmb3IgY2V0RGF5IGlzOiBZWVlZLU1NLURECiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIGRhdGV0aW1lX2NvbmRpdGlvbgogIGNldF9jb25kaXRpb24gPC0gc2VxKAogICAgZnJvbSA9IGFzLlBPU0lYY3QocGFzdGUwKGNldERheSwiIDA6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgdG8gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAyMzowMCIpLCB0eiA9ICJFdXJvcGUvQmVybGluIiksCiAgICBieSA9ICJob3VyIgogICkgCiAgYXR0cihjZXRfY29uZGl0aW9uLCAidHpvbmUiKSA8LSAiVVRDIgogIGNldF9jb25kaXRpb24gPC0gYXMuY2hhcmFjdGVyKGNldF9jb25kaXRpb24pCiAgY2V0X2NvbmRpdGlvbiA8LSB1bmxpc3Qoc3RyX2V4dHJhY3RfYWxsKGNldF9jb25kaXRpb24sICJeKFtbOmRpZ2l0Ol1dfFxcc3wtKSoiKSkKICBjZXRfeWVhcnMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMV0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBzYXBwbHkoCiAgICBzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgewogICAgICBzdHJzcGxpdCh4LCBzcGxpdCA9ICItIilbWzFdXVsyXQogICAgfSkKICBjZXRfbW9udGhzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9tb250aHMpCiAgY2V0X2RheXMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bM10KICAgIH0pCiAgY2V0X2RheXMgPC0gZ3N1YigiXjAiLCAiIiwgY2V0X2RheXMpCiAgY2V0X2hvdXJzIDwtIHNhcHBseShzdHJzcGxpdChjZXRfY29uZGl0aW9uLCBzcGxpdCA9ICIgIiwgZml4ZWQgPSBUKSwgCiAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHhbMl0KICAgICAgICAgICAgICAgICAgICAgIH0pCiAgY2V0X2hvdXJzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9ob3VycykKICBkYXRldGltZUNvbmRpdGlvbiA8LSBwYXN0ZTAoCiAgICAieWVhciA9ICIsIGNldF95ZWFycywgIiBBTkQgIiwKICAgICJtb250aCA9ICIsIGNldF9tb250aHMsICIgQU5EICIsCiAgICAiZGF5ID0gIiwgY2V0X2RheXMsICIgQU5EICIsIAogICAgImhvdXIgPSAiLCBjZXRfaG91cnMKICApCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUoIigiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRldGltZUNvbmRpdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiIikKICAKICAKICAjIC0gV0hFUkUgY29uZGl0aW9uOiBjcmVhdGUgZXZlbnRCYW5uZXJOYW1lX2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX3F1ZXJ5X2ZpbHRlcikgPiAxKSB7CiAgICBldmVudEJhbm5lck5hbWVfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJldmVudC5iYW5uZXJOYW1lIExJS0UgJyUiLCB1cmlfcXVlcnlfZmlsdGVyLCAiJSciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgZXZlbnRCYW5uZXJOYW1lX2NvbmRpdGlvbiA9IHBhc3RlMCgiZXZlbnQuYmFubmVyTmFtZSBMSUtFICclIiwgdXJpX3F1ZXJ5X2ZpbHRlciwgIiUnIikKICB9CiAgCiAgIyAtIGNvbXBvc2UgSGl2ZVFMIHF1ZXJ5CiAgaGl2ZVF1ZXJ5IDwtIHBhc3RlMCggCiAgICAic2VsZWN0IGR0LCBldmVudC5iYW5uZXJOYW1lLCBldmVudC5iYW5uZXJBY3Rpb24sIGV2ZW50LmJhbm5lckltcHJlc3Npb25zLCBldmVudC51c2VySUQgZnJvbSBldmVudC53bWRlYmFubmVyaW50ZXJhY3Rpb25zIAogICAgV0hFUkUgKCIsCiAgICBldmVudEJhbm5lck5hbWVfY29uZGl0aW9uLCAiIEFORCAiLAogICAgIigiLCBkYXRldGltZUNvbmRpdGlvbiwgIikiLAogICAgIik7IgogICkKICAKICAjIC0gd3JpdGUgaHFsCiAgd3JpdGUoaGl2ZVF1ZXJ5LCBwYXN0ZTAoZGF0YURpciwgcXVlcnlGaWxlKSkKICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0OgogIGtlcmJlcm9zUHJlZml4IDwtIAogICAgJ3N1ZG8gLXUgYW5hbHl0aWNzLXByaXZhdGVkYXRhIGtlcmJlcm9zLXJ1bi1jb21tYW5kIGFuYWx5dGljcy1wcml2YXRlZGF0YSAnCiAgIyAtIEtlcmJlcm9zIGluaXQKICBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlMChrZXJiZXJvc1ByZWZpeCwgJyBoZGZzIGRmcyAtbHMnKSwgCiAgICAgICAgIHdhaXQgPSBUKQogICMgLSBSdW4gcXVlcnkKICBxdWVyeSA8LSBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlKGtlcmJlcm9zUHJlZml4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcvdXNyL2xvY2FsL2Jpbi9iZWVsaW5lIC0taW5jcmVtZW50YWw9dHJ1ZSAtLXNpbGVudCAtZiAiJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMChkYXRhRGlyLCBxdWVyeUZpbGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIgPiAnLCBkYXRhRGlyLCBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKSwKICAgICAgICAgICAgICAgICAgd2FpdCA9IFRSVUUpCiAgCiAgIyAtIFdyYW5nbGUgQmFubmVyIEludGVyYWN0aW9ucwogICMgLSBsb2FkCiAgYmFubmVyRGF0YSA8LSB0cnlDYXRjaCh7CiAgICBhcy5kYXRhLmZyYW1lKGZyZWFkKHBhc3RlMChkYXRhRGlyLCBmaWxlTmFtZSkpKQogIH0sCiAgZXJyb3IgPSBmdW5jdGlvbihjb25kaXRpb24pIHsKICAgIHJldHVybihGQUxTRSkKICB9KQogIAogICMgLSBwcm9jZXNzCiAgaWYgKGNsYXNzKGJhbm5lckRhdGEpID09ICdsb2dpY2FsJykgewogICAgCiAgICByZXR1cm4oRkFMU0UpIAogICAgCiAgfSBlbHNlIHsgCiAgICAKICAgICMgLSB3aG9DbGlja2VkCiAgICB3aG9DbGlja2VkIDwtIGJhbm5lckRhdGEgJT4lIAogICAgICBkcGx5cjo6ZmlsdGVyKGJhbm5lcmFjdGlvbiA9PSAiYmFubmVyLWNsaWNrZWQiKQogICAgd2hvQ2xpY2tlZCA8LSBkYXRhLmZyYW1lKHVzZXJpZCA9IHVuaXF1ZSh3aG9DbGlja2VkJHVzZXJpZCkpCiAgICAKICAgICMgLSBzdG9yZToKICAgIHdyaXRlLmNzdih3aG9DbGlja2VkLCAKICAgICAgICAgICAgICBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAKICAgICAgICAgICAgICAgICAgICAgIndob0NsaWNrZWRfIiwKICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoCiAgICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoZmlsZU5hbWUsIHNwbGl0ID0gIl8iLCBmaXhlZCA9IFQpW1sxXV1bMl0sCiAgICAgICAgICAgICAgICAgICAgICAgc3BsaXQgPSAiLiIsIAogICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXSwKICAgICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICAgKQogICAgKQogICAgCiAgICAjIC0gcHJvY2VzcyBiYW5uZXJEYXRhCiAgICBiYW5uZXJEYXRhIDwtIHQodGFibGUoYmFubmVyRGF0YSRiYW5uZXJhY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgYmFubmVyRGF0YSRiYW5uZXJuYW1lKSkKICAgIGJhbm5lckRhdGEgPC0gYXMuZGF0YS5mcmFtZShiYW5uZXJEYXRhKQogICAgY29sbmFtZXMoYmFubmVyRGF0YSkgPC0gYygnYmFubmVyJywgJ2FjdGlvbicsICdjb3VudCcpCiAgICBiYW5uZXJEYXRhJGRheSA8LSBjZXREYXkKICAgIGJhbm5lckRhdGEkY2FtcGFpZ24gPC0gY2FtcGFpZ25OYW1lCiAgICAKICAgICMgLSBzdG9yZToKICAgIHdyaXRlLmNzdihiYW5uZXJEYXRhLCAKICAgICAgICAgICAgICBwYXN0ZTAoYW5hbHl0aWNzRGlyLCAKICAgICAgICAgICAgICAgICAgICAgImJhbm5lckludGVyYWN0aW9uc0FnZ3JlZ2F0ZWRfIiwKICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoCiAgICAgICAgICAgICAgICAgICAgICAgc3Ryc3BsaXQoZmlsZU5hbWUsIHNwbGl0ID0gIl8iLCBmaXhlZCA9IFQpW1sxXV1bMl0sCiAgICAgICAgICAgICAgICAgICAgICAgc3BsaXQgPSAiLiIsIAogICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVClbWzFdXVsxXSwKICAgICAgICAgICAgICAgICAgICAgIi5jc3YiCiAgICAgICAgICAgICAgKSkKICB9CiAgCn0KCiMgLSBzZXQgcGFyYW1zIGZvciB3bWRlX2Jhbm5lcl9hY3Rpb25zKCkKcXVlcnlGaWxlIDwtIHBhc3RlMChjYW1wYWlnbk5hbWUsICJfYmFubmVySW50ZXJhY3Rpb25zLmhxbCIpCmZpbGVOYW1lIDwtIHBhc3RlMCgiYmFubmVySW50ZXJhY3Rpb25zXyIsIGNldERheSwgIi50c3YiKQp1cmlfcXVlcnlfZmlsdGVyIDwtICdXTURFX29jZWRpdG9yc19mYWxsXzIwMjEnCmJhbm5lcl9zdGF0dXMgPC0gd21kZV9iYW5uZXJfYWN0aW9ucyh1cmlfcXVlcnlfZmlsdGVyID0gdXJpX3F1ZXJ5X2ZpbHRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5RmlsZSA9IHF1ZXJ5RmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lID0gZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmFseXRpY3NEaXIgPSBhbmFseXRpY3NEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FtcGFpZ25OYW1lID0gY2FtcGFpZ25OYW1lKQoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBQYWdldmlld3MKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLSBmdW5jdGlvbjogd21kZV9jb2xsZWN0X3BhZ2V2aWV3cwp3bWRlX2NvbGxlY3RfcGFnZXZpZXdzIDwtIGZ1bmN0aW9uKHVyaV9ob3N0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVyeUZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YURpcikgewogIAogICMgLSBOT1RFOgogICMgLSBleHBlY3RlZCBmb3JtYXQgZm9yIGNldERheSBpczogWVlZWS1NTS1ERAogIAogICMgLSB0byBkYXRhRGlyCiAgc2V0d2QoZGF0YURpcikKICAKICAjIC0gbGlicmFyaWVzCiAgbGlicmFyeShzdHJpbmdyKQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSBkYXRldGltZV9jb25kaXRpb24KICBjZXRfY29uZGl0aW9uIDwtIHNlcSgKICAgIGZyb20gPSBhcy5QT1NJWGN0KHBhc3RlMChjZXREYXksIiAwOjAwIiksIHR6ID0gIkV1cm9wZS9CZXJsaW4iKSwKICAgIHRvID0gYXMuUE9TSVhjdChwYXN0ZTAoY2V0RGF5LCIgMjM6MDAiKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpLAogICAgYnkgPSAiaG91ciIKICApIAogIGF0dHIoY2V0X2NvbmRpdGlvbiwgInR6b25lIikgPC0gIlVUQyIKICBjZXRfY29uZGl0aW9uIDwtIGFzLmNoYXJhY3RlcihjZXRfY29uZGl0aW9uKQogIGNldF9jb25kaXRpb24gPC0gdW5saXN0KHN0cl9leHRyYWN0X2FsbChjZXRfY29uZGl0aW9uLCAiXihbWzpkaWdpdDpdXXxcXHN8LSkqIikpCiAgY2V0X3llYXJzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzFdCiAgICB9KQogIGNldF9tb250aHMgPC0gc2FwcGx5KAogICAgc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIGZ1bmN0aW9uKHgpIHsKICAgICAgc3Ryc3BsaXQoeCwgc3BsaXQgPSAiLSIpW1sxXV1bMl0KICAgIH0pCiAgY2V0X21vbnRocyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfbW9udGhzKQogIGNldF9kYXlzIDwtIHNhcHBseSgKICAgIHN0cnNwbGl0KGNldF9jb25kaXRpb24sIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB7CiAgICAgIHN0cnNwbGl0KHgsIHNwbGl0ID0gIi0iKVtbMV1dWzNdCiAgICB9KQogIGNldF9kYXlzIDwtIGdzdWIoIl4wIiwgIiIsIGNldF9kYXlzKQogIGNldF9ob3VycyA8LSBzYXBwbHkoc3Ryc3BsaXQoY2V0X2NvbmRpdGlvbiwgc3BsaXQgPSAiICIsIGZpeGVkID0gVCksIAogICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICB4WzJdCiAgICAgICAgICAgICAgICAgICAgICB9KQogIGNldF9ob3VycyA8LSBnc3ViKCJeMCIsICIiLCBjZXRfaG91cnMpCiAgZGF0ZXRpbWVDb25kaXRpb24gPC0gcGFzdGUwKAogICAgInllYXIgPSAiLCBjZXRfeWVhcnMsICIgQU5EICIsCiAgICAibW9udGggPSAiLCBjZXRfbW9udGhzLCAiIEFORCAiLAogICAgImRheSA9ICIsIGNldF9kYXlzLCAiIEFORCAiLCAKICAgICJob3VyID0gIiwgY2V0X2hvdXJzCiAgKQogIGRhdGV0aW1lQ29uZGl0aW9uIDwtIHBhc3RlKCIoIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0ZXRpbWVDb25kaXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIgT1IgIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIiIpCiAgCiAgIyAtIFdIRVJFIGNvbmRpdGlvbjogY3JlYXRlIHVyaV9ob3N0X2NvbmRpdGlvbgogIGlmIChsZW5ndGgodXJpX2hvc3QpID4gMSkgewogICAgdXJpX2hvc3RfY29uZGl0aW9uIDwtIHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgidXJpX2hvc3QgPSAnIiwgdXJpX2hvc3QsICInIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIgIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIpIgogICAgKQogIH0gZWxzZSB7CiAgICB1cmlfaG9zdF9jb25kaXRpb24gPSBwYXN0ZTAoInVyaV9ob3N0ID0gJyIsIHVyaV9ob3N0LCAiJyIpCiAgfQogIAogICMgLSBXSEVSRSBjb25kaXRpb246IGNyZWF0ZSB1cmlfcGF0aF9jb25kaXRpb24KICBpZiAobGVuZ3RoKHVyaV9wYXRoKSA+IDEpIHsKICAgIHVyaV9wYXRoX2NvbmRpdGlvbiA8LSBwYXN0ZTAoIigiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoInVyaV9wYXRoID0gJyIsIHVyaV9wYXRoLCAiJyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiBPUiAiLCBzZXAgPSAiICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKSIKICAgICkKICB9IGVsc2UgewogICAgdXJpX3BhdGhfY29uZGl0aW9uID0gcGFzdGUwKCJ1cmlfcGF0aCA9ICciLCB1cmlfcGF0aCwgIiciKQogIH0KICAKICAjIC0gY29tcG9zZSBIaXZlUUwgcXVlcnkKICBoaXZlUXVlcnkgPC0gcGFzdGUwKCAKICAgICJVU0Ugd21mOwogICAgU0VMRUNUIHVyaV9ob3N0LCB1cmlfcGF0aCwgdXJpX3F1ZXJ5LCByZWZlcmVyIEZST00gd2VicmVxdWVzdAogICAgV0hFUkUgKCIsCiAgICB1cmlfaG9zdF9jb25kaXRpb24sICIgQU5EICIsCiAgICB1cmlfcGF0aF9jb25kaXRpb24sICIgQU5EICIsCiAgICAiKCIsIGRhdGV0aW1lQ29uZGl0aW9uLCAiKSIsCiAgICAiKTsiCiAgKQogIAogICMgLSB3cml0ZSBocWwKICB3cml0ZShoaXZlUXVlcnksIHF1ZXJ5RmlsZSkKICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0OgogIGtlcmJlcm9zUHJlZml4IDwtIAogICAgJ3N1ZG8gLXUgYW5hbHl0aWNzLXByaXZhdGVkYXRhIGtlcmJlcm9zLXJ1bi1jb21tYW5kIGFuYWx5dGljcy1wcml2YXRlZGF0YSAnCiAgIyAtIEtlcmJlcm9zIGluaXQKICBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlMChrZXJiZXJvc1ByZWZpeCwgJyBoZGZzIGRmcyAtbHMnKSwgCiAgICAgICAgIHdhaXQgPSBUKQogICMgLSBSdW4gcXVlcnkKICBxdWVyeSA8LSBzeXN0ZW0oY29tbWFuZCA9IHBhc3RlKGtlcmJlcm9zUHJlZml4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcvdXNyL2xvY2FsL2Jpbi9iZWVsaW5lIC0taW5jcmVtZW50YWw9dHJ1ZSAtLXNpbGVudCAtZiAiJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMChkYXRhRGlyLCBxdWVyeUZpbGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJyIgPiAnLCBkYXRhRGlyLCBmaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKSwKICAgICAgICAgICAgICAgICAgd2FpdCA9IFRSVUUpCn0KCiMgLSBzZXQgcGFyYW1zIHRvIHdtZGVfY29sbGVjdF9wYWdldmlld3MKdXJpX2hvc3QgPC0gYygnZGUud2lraXBlZGlhLm9yZycsICdkZS5tLndpa2lwZWRpYS5vcmcnKQp1cmlfcGF0aCAgPC0gYygnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9EZWluRW5nYWdlbWVudCcsCiAgICAgICAgICAgICAgICcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0xlcm5lV2lraXBlZGlhJywgCiAgICAgICAgICAgICAgICcvd2lraS9XaWtpcGVkaWE6RiVDMyVCNnJkZXJ1bmcvRiVDMyVCNnJkZXJhbmdlYm90ZScpCnF1ZXJ5RmlsZSA8LSAnV01ERV9vY2VkaXRvcnNfZmFsbF8yMDIxX1BhZ2V2aWV3cy5ocWwnCmZpbGVOYW1lIDwtIHBhc3RlMCgicGFnZXZpZXdzXyIsIGNldERheSwgIi50c3YiKQoKIyAtIGNvbGxlY3QgUGFnZXZpZXdzIGRhdGEKd21kZV9jb2xsZWN0X3BhZ2V2aWV3cyh1cmlfaG9zdCwKICAgICAgICAgICAgICAgICAgICAgICB1cmlfcGF0aCwKICAgICAgICAgICAgICAgICAgICAgICBjZXREYXksCiAgICAgICAgICAgICAgICAgICAgICAgcXVlcnlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIpCgojIyMgLS0tIFdyYW5nbGUgUGFnZXZpZXdzCiMgLSBmdW5jdGlvbjogd21kZV9wcm9jZXNzX3BhZ2V2aWV3cwp3bWRlX3Byb2Nlc3NfcGFnZXZpZXdzIDwtIGZ1bmN0aW9uKGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVyaV9xdWVyeV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFnZV9maWx0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V0RGF5ID0gY2V0RGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbXBhaWduTmFtZSA9IGNhbXBhaWduTmFtZSkgewogIAogICMgLSB0byBkYXRhRGlyCiAgc2V0d2QoZGF0YURpcikKICAKICAjIC0gbGlicmFyaWVzCiAgbGlicmFyeShzdHJpbmdyKQogIGxpYnJhcnkoZHBseXIpCiAgbGlicmFyeSh0aWR5cikKICBsaWJyYXJ5KGRhdGEudGFibGUpCiAgCiAgIyAtIGxvYWQKICBwYWdldmlld3NEYXRhIDwtIHJlYWRMaW5lcyhmaWxlTmFtZSkKICB3U3RhcnQgPC0gd2hpY2goZ3JlcGwoInVyaV9ob3N0IiwgcGFnZXZpZXdzRGF0YSkpCiAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhWyh3U3RhcnQgKyAxKToobGVuZ3RoKHBhZ2V2aWV3c0RhdGEpIC0gMSldCiAgcGFnZXZpZXdzRGF0YSA8LSBkYXRhLmZyYW1lKGRhdCA9IHBhZ2V2aWV3c0RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICBwYWdldmlld3NEYXRhIDwtIHNlcGFyYXRlKHBhZ2V2aWV3c0RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRvID0gYygndXJpX2hvc3QnLCAndXJpX3BhdGgnLCAndXJpX3F1ZXJ5JywgJ3JlZmVyZXInKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIpCiAgIyAtIGFwcGx5IHBhZ2VfZmlsdGVyCiAgd0ZpbHRlcjEgPC0gdW5pcXVlKHVubmFtZSh1bmxpc3Qoc2FwcGx5KHBhZ2VfZmlsdGVyLCBmdW5jdGlvbiAoeCkgewogICAgd2hpY2goZ3JlcGwoeCwgcGFnZXZpZXdzRGF0YSR1cmlfcGF0aCkpCiAgfSkpKSkKICBwYWdldmlld3NEYXRhIDwtIHBhZ2V2aWV3c0RhdGFbd0ZpbHRlcjEsIF0KICAjIC0gYXBwbHkgdXJpX3F1ZXJ5X2ZpbHRlcgogIHdfdXJpX3F1ZXJ5XzEgPC0gd2hpY2goZ3JlcGwodXJpX3F1ZXJ5X2ZpbHRlciwgcGFnZXZpZXdzRGF0YSR1cmlfcXVlcnkpKQogIHdfdXJpX3F1ZXJ5XzIgPC0gd2hpY2goZ3JlcGwodXJpX3F1ZXJ5X2ZpbHRlciwgcGFnZXZpZXdzRGF0YSRyZWZlcmVyKSkKICB3X3VyaV9xdWVyeSA8LSB1bmlxdWUoYyh3X3VyaV9xdWVyeV8xLCB3X3VyaV9xdWVyeV8yKSkKCiAgaWYgKGxlbmd0aCh3X3VyaV9xdWVyeSkgPiAwKSB7CiAgICAKICAgICMgLSBmaWx0ZXIgZm9yIHdfdXJpX3F1ZXJ5CiAgICBwYWdldmlld3NEYXRhIDwtIHBhZ2V2aWV3c0RhdGFbd191cmlfcXVlcnksIF0gCiAgICAKICAgICMgLSBjb3B5IHVyaV9xdWVyeSBmcm9tIHJlZmVyZXIgd2hlcmUgdXJpX3F1ZXJ5IGlzIGVtcHR5OgogICAgd0N1cmlxIDwtIHdoaWNoKHBhZ2V2aWV3c0RhdGEkdXJpX3F1ZXJ5ID09ICIiKQogICAgY3VyaXEgPC0gc3RyaW5ncjo6c3RyX2V4dHJhY3QocGFnZXZpZXdzRGF0YSRyZWZlcmVyW3dDdXJpcV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKHVyaV9xdWVyeV9maWx0ZXIsICIuKyQiKSkKICAgIHBhZ2V2aWV3c0RhdGEkdXJpX3F1ZXJ5W3dDdXJpcV0gPC0gY3VyaXEKICAgICMgLSByZW1vdmUgP2NhbXBhaWduPSBmcm9tIHBhZ2V2aWV3c0RhdGEkdXJpX3F1ZXJ5CiAgICBwYWdldmlld3NEYXRhJHVyaV9xdWVyeSA8LSBnc3ViKCI/Y2FtcGFpZ249IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWdldmlld3NEYXRhJHVyaV9xdWVyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpeGVkID0gVCkKICAgIAogICAgIyAtIGFnZ3JlZ2F0ZToKICAgIHBhZ2V2aWV3c0RhdGEkdXJpX3BhdGggPC0gcGFzdGUwKHBhZ2V2aWV3c0RhdGEkdXJpX2hvc3QsIHBhZ2V2aWV3c0RhdGEkdXJpX3BhdGgpCiAgICBwYWdldmlld3NEYXRhJHVyaV9ob3N0IDwtIE5VTEwKICAgIHBhZ2V2aWV3c0RhdGEkcGFnZSA8LSBOVUxMCiAgICBwYWdldmlld3NEYXRhJHJlZmVyZXIgPC0gTlVMTAogICAgcGFnZXZpZXdzRGF0YSA8LSBwYWdldmlld3NEYXRhICU+JSAKICAgICAgZHBseXI6OnNlbGVjdCh1cmlfcXVlcnksIHVyaV9wYXRoKSAlPiUgCiAgICAgIGRwbHlyOjpncm91cF9ieSh1cmlfcXVlcnksIHVyaV9wYXRoKSAlPiUgCiAgICAgIGRwbHlyOjpzdW1tYXJpc2UocGFnZXZpZXdzID0gbigpKQogICAgY29sbmFtZXMocGFnZXZpZXdzRGF0YSkgPC0gYygnVGFnJywgJ1BhZ2UnLCAnUGFnZXZpZXdzJykKICAgIAogICAgIyAtIGFkZCBjZXREYXksIGNhbXBhaWduTmFtZQogICAgcGFnZXZpZXdzRGF0YSRkYXRlIDwtIGNldERheQogICAgcGFnZXZpZXdzRGF0YSRjYW1wYWlnbiA8LSBjYW1wYWlnbk5hbWUKICAgIAogICAgIyAtIHN0b3JlOgogICAgd3JpdGUuY3N2KHBhZ2V2aWV3c0RhdGEsIAogICAgICAgICAgICAgIHBhc3RlMChhbmFseXRpY3NEaXIsIAogICAgICAgICAgICAgICAgICAgICAicGFnZXZpZXdzQWdncmVnYXRlZF8iLAogICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdCgKICAgICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdChmaWxlTmFtZSwgc3BsaXQgPSAiXyIsIGZpeGVkID0gVClbWzFdXVsyXSwKICAgICAgICAgICAgICAgICAgICAgICBzcGxpdCA9ICIuIiwgCiAgICAgICAgICAgICAgICAgICAgICAgZml4ZWQgPSBUKVtbMV1dWzFdLAogICAgICAgICAgICAgICAgICAgICAiLmNzdiIKICAgICAgICAgICAgICApCiAgICApCiAgICAKICB9CiAgCn0KCiMgLSBzZXQgcGFyYW1zIHRvIHdtZGVfcHJvY2Vzc19wYWdldmlld3MKdXJpX3F1ZXJ5X2ZpbHRlciA8LSAnV01ERV9vY2VkaXRvcnNfZmFsbF8yMDIxJwpwYWdlX2ZpbHRlciA8LSBjKCcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0RlaW5FbmdhZ2VtZW50JywKICAgICAgICAgICAgICAgICAnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9MZXJuZVdpa2lwZWRpYScsIAogICAgICAgICAgICAgICAgICcvd2lraS9XaWtpcGVkaWE6RiVDMyVCNnJkZXJ1bmcvRiVDMyVCNnJkZXJhbmdlYm90ZScpCgojIC0gd3JhbmdsZSBwYWdldmlld3MKd21kZV9wcm9jZXNzX3BhZ2V2aWV3cyhmaWxlTmFtZSA9IGZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGFEaXIgPSBkYXRhRGlyLAogICAgICAgICAgICAgICAgICAgICAgIHVyaV9xdWVyeV9maWx0ZXIgPSB1cmlfcXVlcnlfZmlsdGVyLCAKICAgICAgICAgICAgICAgICAgICAgICBwYWdlX2ZpbHRlciA9IHBhZ2VfZmlsdGVyLAogICAgICAgICAgICAgICAgICAgICAgIGNldERheSA9IGNldERheSwKICAgICAgICAgICAgICAgICAgICAgICBjYW1wYWlnbk5hbWUgPSBjYW1wYWlnbk5hbWUpCgoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIENvbXBvc2UgYW5kIGNvcHkgdG8gcHVibGljRGlyCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmxGIDwtIGxpc3QuZmlsZXMoYW5hbHl0aWNzRGlyKQpsRiA8LSBsRltncmVwbCgiQWdncmVnYXRlZCIsIGxGKV0KbEZwIDwtIGxGW2dyZXBsKCJwYWdldmlld3MiLCBsRildCmxGcCA8LSBsYXBwbHkocGFzdGUwKGFuYWx5dGljc0RpciwgbEZwKSwKICAgICAgICAgICAgICBkYXRhLnRhYmxlOjpmcmVhZCkKbEZwIDwtIGRhdGEudGFibGU6OnJiaW5kbGlzdChsRnApCndyaXRlLmNzdihsRnAsIAogICAgICAgICAgcGFzdGUwKHB1YmxpY0RpciwgCiAgICAgICAgICAgICAgICAgIjIwMjFfT2NjYXNpb25hbEVkaXRvcnNfUGFnZXZpZXdzLmNzdiIpKQpsRmIgPC0gbEZbZ3JlcGwoImJhbm5lciIsIGxGKV0KbEZiIDwtIGxhcHBseShwYXN0ZTAoYW5hbHl0aWNzRGlyLCBsRmIpLAogICAgICAgICAgICAgIGRhdGEudGFibGU6OmZyZWFkKQpsRmIgPC0gZGF0YS50YWJsZTo6cmJpbmRsaXN0KGxGYikKd3JpdGUuY3N2KGxGYiwgCiAgICAgICAgICBwYXN0ZTAocHVibGljRGlyLCAKICAgICAgICAgICAgICAgICAiMjAyMV9PY2Nhc2lvbmFsRWRpdG9yc19CYW5uZXJzLmNzdiIpKQoKCgpgYGAKCgojIyAxLiBDYW1wYWlnbiBCYW5uZXJzCgpUaGlzIHNlY3Rpb24gcHJlc2VudHMgYWxsIGRhdGEgYW5kIHN0YXRpc3RpY3Mgb24gdGhlIGNhbXBhaWduIGJhbm5lcnMuCgojIyMgMS4xIEJhbm5lciBJbXByZXNzaW9ucwojIyMjIDEuMS4xIEJhbm5lciBJbXByZXNzaW9ucyBPdmVydmlldwoKKipDaGFydCAxLjEuMSoqIERhaWx5IEJhbm5lciBJbXByZXNzaW9ucwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMgLSB3cmFuZ2xlIGRhdGFTZXQKZGF0YVNldCA8LSByZWFkLmNzdigKICAnX3JlcG9ydGluZy8yMDIxX09jY2FzaW9uYWxFZGl0b3JzX0Jhbm5lcnMuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpkYXRhU2V0JFYxIDwtIE5VTEwKZGF0YVNldCA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6ZmlsdGVyKCFncmVwbCgiMjAyMS0xMSIsIGRhdGFTZXQkZGF5KSkKZGF0YVNldCRiYW5uZXIgPC0gZ3N1YigiX2N0cmwiLCAiIiwgZGF0YVNldCRiYW5uZXIsIGZpeGVkID0gVCkKZGF0YVNldCRiYW5uZXIgPC0gc2FwcGx5KGRhdGFTZXQkYmFubmVyLCBmdW5jdGlvbih4KSB7CiAgaWYgKGdyZXBsKCJpcGFkJCIsIHgpKSB7CiAgICAiaXBhZCIKICB9IGVsc2UgaWYgKGdyZXBsKCJtb2JpbGUkIiwgeCkpIHsKICAgICJtb2JpbGUiCiAgfSBlbHNlIHsKICAgICJkZXNrdG9wIgogIH0KfSkKCiMgLSBwRnJhbWUKcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWNhbXBhaWduKSAlPiUgCiAgZHBseXI6OmZpbHRlcihhY3Rpb24gPT0gImJhbm5lci1zZWVuIikgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWFjdGlvbikKCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICAgICAgeSA9IGNvdW50LAogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IGJhbm5lciwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGNvdW50KSkgKyAKICBnZW9tX2xpbmUoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKyAKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDQsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogRGFpbHkgQmFubmVyIEltcHJlc3Npb25zJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4KSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgojIyMjIDEuMS4xIEJhbm5lciBJbXByZXNzaW9ucyBPdmVydmlldzogVGFibGUKCioqVGFibGUgMS4xLjEuKiogRGFpbHkgQmFubmVyIEltcHJlc3Npb25zCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KIyMjIC0tLSBGdWxsIERhdGFzZXQgKFRhYmxlIFJlcG9ydCkKZGF0YXRhYmxlKHBGcmFtZSkKYGBgCgojIyMjIDEuMS4yIFRvdGFsIEJhbm5lciBJbXByZXNzaW9ucwoKKipDaGFydCAxLjEuMi4qKiBUb3RhbCBCYW5uZXIgSW1wcmVzc2lvbnMKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIC0gcEZyYW1lCnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6c2VsZWN0KC1jYW1wYWlnbikgJT4lIAogIGRwbHlyOjpmaWx0ZXIoYWN0aW9uID09ICJiYW5uZXItc2VlbiIpICU+JQogIGRwbHlyOjpzZWxlY3QoLWFjdGlvbiwgLSBkYXkpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoYmFubmVyKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZSh0b3RhbEltcHJlc3Npb25zID0gc3VtKGNvdW50KSkKCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gYmFubmVyLCAKICAgICAgICAgICAgICAgICAgICB5ID0gdG90YWxJbXByZXNzaW9ucywgCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdG90YWxJbXByZXNzaW9ucykpICsgCiAgZ2VvbV9iYXIod2lkdGggPSAuNSwgc3RhdCA9ICJpZGVudGl0eSIpICsgCiAgZ2d0aXRsZSgnT2NjYXNpb25hbCBFZGl0b3JzIDIwMjE6IEJhbm5lciBJbXByZXNzaW9ucycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX2xhYmVsX3JlcGVsKHNpemUgPSAzLjUsIGNvbG9yID0gIndoaXRlIiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIyMjIDEuMS4zIEJhbm5lciBJbXByZXNzaW9ucyBwZXIgRGF5CgoqKkNoYXJ0IDEuMS4zLioqIEJhbm5lciBJbXByZXNzaW9ucyBwZXIgRGF5CgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KIyAtIHBGcmFtZQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OnNlbGVjdCgtY2FtcGFpZ24pICU+JSAKICBkcGx5cjo6ZmlsdGVyKGFjdGlvbiA9PSAiYmFubmVyLXNlZW4iKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1hY3Rpb24sIC1iYW5uZXIpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoZGF5KSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZSh0b3RhbEltcHJlc3Npb25zID0gc3VtKGNvdW50KSkKCgpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRheSwgCiAgICAgICAgICAgICAgICAgICAgeSA9IHRvdGFsSW1wcmVzc2lvbnMsIAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdG90YWxJbXByZXNzaW9ucykpICsKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSwgZ3JvdXAgPSAxLCBjb2xvciA9ICJkYXJrYmx1ZSIpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLjUsIGNvbG9yID0gImRhcmtibHVlIikgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgZ2d0aXRsZSgnT2NjYXNpb25hbCBFZGl0b3JzIDIwMjE6IEJhbm5lciBJbXByZXNzaW9ucycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX2xhYmVsX3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpCmBgYAoKIyMjIDEuMiBCYW5uZXIgQ2xpY2tzCiMjIyMgMS4yLjEgQmFubmVyIENsaWNrcyBPdmVydmlldwoKKipDaGFydCAxLjIuMSoqIERhaWx5IEJhbm5lciBDbGlja3MKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIC0gcEZyYW1lCnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6c2VsZWN0KC1jYW1wYWlnbikgJT4lIAogIGRwbHlyOjpmaWx0ZXIoYWN0aW9uID09ICJiYW5uZXItY2xpY2tlZCIpICU+JSAKICBkcGx5cjo6c2VsZWN0KC1hY3Rpb24pCgojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRheSwKICAgICAgICAgICAgICAgICAgIHkgPSBjb3VudCwKICAgICAgICAgICAgICAgICAgIGdyb3VwID0gYmFubmVyLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICBmaWxsID0gYmFubmVyLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBjb3VudAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9saW5lKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDQpICsKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogRGFpbHkgQmFubmVyIENsaWNrcycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKIyMjIyAxLjIuMSBCYW5uZXIgQ2xpY2tzIE92ZXJ2aWV3OiBUYWJsZQoKKipUYWJsZSAxLjIuMS4qKiBEYWlseSBCYW5uZXIgQ2xpY2tzCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KIyMjIC0tLSBGdWxsIERhdGFzZXQgKFRhYmxlIFJlcG9ydCkKZGF0YXRhYmxlKHBGcmFtZSkKYGBgCgojIyMjIDEuMi4yIFRvdGFsIEJhbm5lciBDbGlja3MKCioqQ2hhcnQgMS4yLjIuKiogVG90YWwgQmFubmVyIENsaWNrcwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMgLSBwRnJhbWUKcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWNhbXBhaWduKSAlPiUgCiAgZHBseXI6OmZpbHRlcihhY3Rpb24gPT0gImJhbm5lci1jbGlja2VkIikgJT4lCiAgZHBseXI6OnNlbGVjdCgtYWN0aW9uLCAtIGRheSkgJT4lIAogIGRwbHlyOjpncm91cF9ieShiYW5uZXIpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKHRvdGFsQ2xpY2tzID0gc3VtKGNvdW50KSkKCmdncGxvdChwRnJhbWUsIGFlcyh4ID0gYmFubmVyLCAKICAgICAgICAgICAgICAgICAgICB5ID0gdG90YWxDbGlja3MsIAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gYmFubmVyLCAKICAgICAgICAgICAgICAgICAgICBmaWxsID0gYmFubmVyLCAKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHRvdGFsQ2xpY2tzKSkgKyAKICBnZW9tX2Jhcih3aWR0aCA9IC41LCBzdGF0ID0gImlkZW50aXR5IikgKyAKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogQmFubmVyIENsaWNrcycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX2xhYmVsX3JlcGVsKHNpemUgPSAzLjUsIGNvbG9yID0gIndoaXRlIiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIyMjIDEuMi4zIEJhbm5lciBDbGlja3MgcGVyIERheQoKKipDaGFydCAxLjIuMy4qKiBCYW5uZXIgQ2xpY2tzIHBlciBEYXkKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIC0gcEZyYW1lCnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6c2VsZWN0KC1jYW1wYWlnbikgJT4lIAogIGRwbHlyOjpmaWx0ZXIoYWN0aW9uID09ICJiYW5uZXItY2xpY2tlZCIpICU+JQogIGRwbHlyOjpzZWxlY3QoLWFjdGlvbiwgLWJhbm5lcikgJT4lIAogIGRwbHlyOjpncm91cF9ieShkYXkpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKHRvdGFsQ2xpY2tzID0gc3VtKGNvdW50KSkKCgpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRheSwgCiAgICAgICAgICAgICAgICAgICAgeSA9IHRvdGFsQ2xpY2tzLCAKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHRvdGFsQ2xpY2tzKSkgKwogIGdlb21fcGF0aChzaXplID0gLjI1LCBncm91cCA9IDEsIGNvbG9yID0gImRhcmtibHVlIikgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgY29sb3IgPSAiZGFya2JsdWUiKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGNvbG9yID0gIndoaXRlIikgKyAKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogQmFubmVyIENsaWNrcycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX2xhYmVsX3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpCmBgYAoKIyMjIDEuMyBCYW5uZXIgQ2xvc2UKIyMjIyAxLjMuMSBCYW5uZXIgQ2xvc2UgT3ZlcnZpZXcKCioqQ2hhcnQgMS4zLjEqKiBEYWlseSBCYW5uZXIgQ2xvc2UKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIC0gcEZyYW1lCnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6c2VsZWN0KC1jYW1wYWlnbikgJT4lIAogIGRwbHlyOjpmaWx0ZXIoYWN0aW9uID09ICJiYW5uZXItY2xvc2VkIikgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWFjdGlvbikKCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChwRnJhbWUsIGFlcyh4ID0gZGF5LAogICAgICAgICAgICAgICAgICAgeSA9IGNvdW50LAogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IGJhbm5lciwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGNvdW50KSkgKyAKICBnZW9tX2xpbmUoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdndGl0bGUoJ09jY2FzaW9uYWwgRWRpdG9ycyAyMDIxOiBEYWlseSBCYW5uZXIgQ2xvc2UnKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKHNpemUgPSA0KSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCiMjIyMgMS4zLjEgQmFubmVyIENsb3NlIE92ZXJ2aWV3OiBUYWJsZQoKKipUYWJsZSAxLjMuMS4qKiBEYWlseSBCYW5uZXIgQ2xvc2UKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIyMgLS0tIEZ1bGwgRGF0YXNldCAoVGFibGUgUmVwb3J0KQpkYXRhdGFibGUocEZyYW1lKQpgYGAKCiMjIyMgMS4zLjIgVG90YWwgQmFubmVyIENsb3NlCgoqKkNoYXJ0IDEuMy4yLioqIFRvdGFsIEJhbm5lciBDbG9zZQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMgLSBwRnJhbWUKcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWNhbXBhaWduKSAlPiUgCiAgZHBseXI6OmZpbHRlcihhY3Rpb24gPT0gImJhbm5lci1jbG9zZWQiKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1hY3Rpb24sIC0gZGF5KSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGJhbm5lcikgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UodG90YWxDbG9zZSA9IHN1bShjb3VudCkpCgpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGJhbm5lciwgCiAgICAgICAgICAgICAgICAgICAgeSA9IHRvdGFsQ2xvc2UsIAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gYmFubmVyLCAKICAgICAgICAgICAgICAgICAgICBmaWxsID0gYmFubmVyLCAKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHRvdGFsQ2xvc2UpKSArIAogIGdlb21fYmFyKHdpZHRoID0gLjUsIHN0YXQgPSAiaWRlbnRpdHkiKSArIAogIGdndGl0bGUoJ09jY2FzaW9uYWwgRWRpdG9ycyAyMDIxOiBCYW5uZXIgQ2xvc2UnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV9sYWJlbF9yZXBlbChzaXplID0gMy41LCBjb2xvciA9ICJ3aGl0ZSIsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMjIyAxLjMuMyBCYW5uZXIgQ2xvc2UgcGVyIERheQoKKipDaGFydCAxLjMuMy4qKiBCYW5uZXIgQ2xvc2UgcGVyIERheQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMgLSBwRnJhbWUKcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWNhbXBhaWduKSAlPiUgCiAgZHBseXI6OmZpbHRlcihhY3Rpb24gPT0gImJhbm5lci1jbG9zZWQiKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1hY3Rpb24sIC1iYW5uZXIpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoZGF5KSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZSh0b3RhbENsb3NlID0gc3VtKGNvdW50KSkKCgpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRheSwgCiAgICAgICAgICAgICAgICAgICAgeSA9IHRvdGFsQ2xvc2UsIAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdG90YWxDbG9zZSkpICsKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSwgZ3JvdXAgPSAxLCBjb2xvciA9ICJkYXJrYmx1ZSIpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLjUsIGNvbG9yID0gImRhcmtibHVlIikgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgZ2d0aXRsZSgnT2NjYXNpb25hbCBFZGl0b3JzIDIwMjE6IEJhbm5lciBDbG9zZScpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX2xhYmVsX3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpCmBgYAoKIyMjIDEuNCBCYW5uZXIgQ2xpY2sgUmF0ZQojIyMjIDEuNC4xIEJhbm5lciBDbGljayBSYXRlIE92ZXJ2aWV3CgoqKkNoYXJ0IDEuNC4xKiogRGFpbHkgQmFubmVyIENsb3NlCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KIyAtIHBGcmFtZQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OnNlbGVjdCgtY2FtcGFpZ24pICU+JSAKICBkcGx5cjo6ZmlsdGVyKGFjdGlvbiAhPSAiYmFubmVyLWNsb3NlZCIpICU+JSAKICB0aWR5cjo6cGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJhY3Rpb24iLCAKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSAiY291bnQiLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfZmlsbCA9IDApCnBGcmFtZSRjbGlja19yYXRlIDwtIHBGcmFtZSRgYmFubmVyLWNsaWNrZWRgL3BGcmFtZSRgYmFubmVyLXNlZW5gCnBGcmFtZSA8LSBwRnJhbWUgJT4lIAogIGRwbHlyOjpzZWxlY3QoYmFubmVyLCBkYXksIGNsaWNrX3JhdGUpCgojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRheSwKICAgICAgICAgICAgICAgICAgIHkgPSBjbGlja19yYXRlLAogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IGJhbm5lciwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSByb3VuZChjbGlja19yYXRlLCAyKSkpICsgCiAgZ2VvbV9saW5lKHNpemUgPSAuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDQpICsKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogRGFpbHkgQmFubmVyIENsaWNrIFJhdGUnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCiMjIyMgMS40LjEgQmFubmVyIENsb3NlIE92ZXJ2aWV3OiBUYWJsZQoKKipUYWJsZSAxLjQuMS4qKiBEYWlseSBCYW5uZXIgQ2xvc2UKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIyMgLS0tIEZ1bGwgRGF0YXNldCAoVGFibGUgUmVwb3J0KQpkYXRhdGFibGUocEZyYW1lKQpgYGAKCiMjIyMgMS40LjIgQXZlcmFnZSBCYW5uZXIgQ2xpY2sgUmF0ZQoKKipDaGFydCAxLjQuMi4qKiBBdmVyYWdlIEJhbm5lciBDbGljayBSYXRlCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRX0KIyAtIHBGcmFtZQpwcEZyYW1lIDwtIHBGcmFtZSAlPiUgCiAgZHBseXI6OnNlbGVjdChiYW5uZXIsIGNsaWNrX3JhdGUpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoYmFubmVyKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShhdmdfY2xpY2tfcmF0ZSA9IG1lYW4oY2xpY2tfcmF0ZSkpCgpnZ3Bsb3QocHBGcmFtZSwgYWVzKHggPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgIHkgPSBhdmdfY2xpY2tfcmF0ZSwgCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gcm91bmQoYXZnX2NsaWNrX3JhdGUsIDIpKSkgKyAKICBnZW9tX2Jhcih3aWR0aCA9IC41LCBzdGF0ID0gImlkZW50aXR5IikgKyAKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogQXZlcmFnZSBCYW5uZXIgQ2xpY2sgUmF0ZScpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX2xhYmVsX3JlcGVsKHNpemUgPSAzLjUsIGNvbG9yID0gIndoaXRlIiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIyMjIDEuNC4zIEF2ZXJhZ2UgQmFubmVyIENsaWNrIFJhdGUgcGVyIERheQoKKipDaGFydCAxLjMuMy4qKiBBdmVyYWdlIEJhbm5lciBDbGljayBSYXRlIHBlciBEYXkKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQojIC0gcEZyYW1lCnBwRnJhbWUgPC0gcEZyYW1lICU+JSAKICBkcGx5cjo6c2VsZWN0KGRheSwgY2xpY2tfcmF0ZSkgJT4lIAogIGRwbHlyOjpncm91cF9ieShkYXkpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKGF2Z19jbGlja19yYXRlID0gbWVhbihjbGlja19yYXRlKSkKICAKZ2dwbG90KHBwRnJhbWUsIGFlcyh4ID0gZGF5LCAKICAgICAgICAgICAgICAgICAgICB5ID0gYXZnX2NsaWNrX3JhdGUsIAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gcm91bmQoYXZnX2NsaWNrX3JhdGUsIDIpKSkgKwogIGdlb21fcGF0aChzaXplID0gLjI1LCBncm91cCA9IDEsIGNvbG9yID0gImRhcmtibHVlIikgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgY29sb3IgPSAiZGFya2JsdWUiKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGNvbG9yID0gIndoaXRlIikgKyAKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogQXZlcmFnZSBCYW5uZXIgQ2xpY2sgUmF0ZScpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX2xhYmVsX3JlcGVsKHNpemUgPSAzLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDgpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpCmBgYAoKIyMgMi4gUGFnZXZpZXdzCgojIyMgMi4xIFBhZ2V2aWV3cyBPdmVydmlldwoKKipDaGFydCAyLjEuMS4qKiBQYWdldmlld3MgT3ZlcnZpZXcuIAoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5oZWlnaHQgID0gMTB9CmRhdGFTZXQgPC0gcmVhZC5jc3YoCiAgJ19yZXBvcnRpbmcvMjAyMV9PY2Nhc2lvbmFsRWRpdG9yc19QYWdldmlld3MuY3N2JywKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpkYXRhU2V0JFYxIDwtIE5VTEwKZGF0YVNldCA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6ZmlsdGVyKCEoZ3JlcGwoIjIwMjEtMTEiLCBkYXRlKSkpCmRhdGFTZXQkVGFnIDwtIHNhcHBseShkYXRhU2V0JFRhZywgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgiaXBhZCQiLCB4KSkgewogICAgImlwYWQiCiAgfSBlbHNlIGlmIChncmVwbCgibW9iaWxlJCIsIHgpKSB7CiAgICAibW9iaWxlIgogIH0gZWxzZSB7CiAgICAiZGVza3RvcCIKICB9Cn0pCmRhdGFTZXQkUGFnZSA8LSBzYXBwbHkoZGF0YVNldCRQYWdlLCBmdW5jdGlvbih4KSB7CiAgaWYgKGdyZXBsKCJGJUMzJUI2cmRlcmFuZ2Vib3RlIiwgeCkpIHsKICAgIHJldHVybigiRsO2cmRlcnVuZy9Gw7ZyZGVyYW5nZWJvdGUiKQogIH0gZWxzZSBpZiAoZ3JlcGwoIkRlaW5FbmdhZ2VtZW50IiwgeCkpIHsKICAgIHJldHVybigiRGVpbkVuZ2FnZW1lbnQgIikKICB9IGVsc2UgewogICAgcmV0dXJuKCJMZXJuZVdpa2lwZWRpYSIpCiAgfQp9KQpkYXRhU2V0IDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWNhbXBhaWduKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGRhdGUsIFRhZywgUGFnZSkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCgojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfQpnZ3Bsb3QoZGF0YVNldCwgYWVzKHggPSBkYXRlLAogICAgICAgICAgICAgICAgICAgIHkgPSBsb2coUGFnZXZpZXdzKSwKICAgICAgICAgICAgICAgICAgICBncm91cCA9IFBhZ2UsCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBQYWdlLAogICAgICAgICAgICAgICAgICAgIGZpbGwgPSBQYWdlLAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUGFnZXZpZXdzLAogICAgICAgICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wYXRoKHNpemUgPSAuMjUpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnT2NjYXNpb25hbCBFZGl0b3JzIDIwMjE6IFBhZ2V2aWV3cycpICsKICB5bGFiKCJsb2coUGFnZXZpZXdzKSIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSA2LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIGZhY2V0X3dyYXAoflRhZywgbmNvbCA9IDIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDEyKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgdGhlbWUoc3RyaXAudGV4dCA9ICBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCgoqKlRhYmxlIDIuMi4xLioqIFBhZ2V2aWV3cyBPdmVydmlldwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CiMjIyAtLS0gRnVsbCBEYXRhc2V0IChUYWJsZSBSZXBvcnQpCmRhdGF0YWJsZShkYXRhU2V0ICU+JSBhcnJhbmdlKGRlc2MoUGFnZXZpZXdzKSkpCmBgYAoKIyMjIDIuMiBQYWdldmlld3MgRGFpbHkKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OnNlbGVjdChkYXRlLCBQYWdldmlld3MpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoZGF0ZSkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCgojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICApKSArIAogIGdlb21fbGluZShzaXplID0gLjI1LCBncm91cCA9IDEpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgZ2d0aXRsZSgnT2NjYXNpb25hbCBFZGl0b3JzIDIwMjE6IFBhZ2V2aWV3cycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDYsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDEyKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKIyMjIDIuMyBQYWdldmlld3MgcGVyIFRhZwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6c2VsZWN0KFRhZywgUGFnZXZpZXdzKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRhZykgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCgojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IFRhZywKICAgICAgICAgICAgICAgICAgIHkgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFBhZ2V2aWV3cykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJkZWVwc2t5Ymx1ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogUGFnZXZpZXdzJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gNiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gMTIpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgojIyMjIDIuMy4xIFBhZ2V2aWV3cyBwZXIgVGFnOiBwYWdldmlld3MgcGVyIGRldmljZSAtIExhbmRpbmcgUGFnZSAxIChEZWluRW5nYWdlbWVudCkgb25seQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGRwbHlyOjpmaWx0ZXIoUGFnZSA9PSAiRGVpbkVuZ2FnZW1lbnQgIikgJT4lIAogIGRwbHlyOjpzZWxlY3QoVGFnLCBQYWdldmlld3MpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVGFnKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChwRnJhbWUsIGFlcyh4ID0gVGFnLAogICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUGFnZXZpZXdzKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImRlZXBza3libHVlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ09jY2FzaW9uYWwgRWRpdG9ycyAyMDIxOiBQYWdldmlld3MnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSA2LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSAxMikpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCiMjIyMgMi4zLjIgUGFnZXZpZXdzIHBlciBUYWc6IHBhZ2V2aWV3cyBwZXIgZGV2aWNlIC0gTGFuZGluZyBQYWdlcyAyYSBhbmQgMmIKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFfQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBkcGx5cjo6ZmlsdGVyKFBhZ2UgIT0gIkRlaW5FbmdhZ2VtZW50ICIpICU+JSAKICBkcGx5cjo6c2VsZWN0KFRhZywgUGFnZXZpZXdzKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRhZykgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoUGFnZXZpZXdzID0gc3VtKFBhZ2V2aWV3cykpCgojIC0gVmlzdWFsaXplIHcuIHtnZ3Bsb3QyfQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IFRhZywKICAgICAgICAgICAgICAgICAgIHkgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFBhZ2V2aWV3cykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJkZWVwc2t5Ymx1ZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogUGFnZXZpZXdzJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gNiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gMTIpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgojIyMgMi40IFBhZ2V2aWV3cyBwZXIgUGFnZQoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6c2VsZWN0KFBhZ2UsIFBhZ2V2aWV3cykgJT4lIAogIGRwbHlyOjpncm91cF9ieShQYWdlKSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShQYWdldmlld3MgPSBzdW0oUGFnZXZpZXdzKSkKCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChwRnJhbWUsIGFlcyh4ID0gUGFnZSwKICAgICAgICAgICAgICAgICAgIHkgPSBQYWdldmlld3MsCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFBhZ2V2aWV3cykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ09jY2FzaW9uYWwgRWRpdG9ycyAyMDIxOiBQYWdldmlld3MnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSA2LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSAxMikpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKIyMgMy4gRWRpdGluZyBCZWhhdmlvdXIKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcuaGVpZ2h0ICA9IDEwfQpkYXRhU2V0IDwtIHJlYWQuY3N2KAogICdfcmVwb3J0aW5nL3VzZXJFZGl0c19Db21wbGV0ZS5jc3YnLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmRhdGFTZXQkdHNfcmV2aXNpb24gPC0gYXMuY2hhcmFjdGVyKGRhdGFTZXQkdHNfcmV2aXNpb24pCmRhdGFTZXQkYmFubmVyIDwtIHNhcHBseShkYXRhU2V0JGJhbm5lciwgZnVuY3Rpb24oeCkgewogIGlmIChncmVwbCgiaXBhZCQiLCB4KSkgewogICAgImlwYWQiCiAgfSBlbHNlIGlmIChncmVwbCgibW9iaWxlJCIsIHgpKSB7CiAgICAibW9iaWxlIgogIH0gZWxzZSB7CiAgICAiZGVza3RvcCIKICB9Cn0pCmBgYAoKIyMjIDMuMSBFZGl0cyBwZXIgdGFnCgpgYGB7ciBlY2hvID0gVCwgZXZhbCA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCAgPSAxMH0KcEZyYW1lIDwtIGRhdGFTZXQgJT4lIAogIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKHRzX3JldmlzaW9uKSkgJT4lIAogIGRwbHlyOjpzZWxlY3QoYmFubmVyKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGJhbm5lcikgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoZWRpdHMgPSBuKCkpCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChwRnJhbWUsIGFlcyh4ID0gYmFubmVyLAogICAgICAgICAgICAgICAgICAgeSA9IGVkaXRzLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBlZGl0cykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJkYXJrUmVkIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogIGdndGl0bGUoJ09jY2FzaW9uYWwgRWRpdG9ycyAyMDIxOiBFZGl0cycpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDYsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDEyKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKIyMjIDMuMiBIb3cgbWFueSBiYW5uZXIgaW1wcmVzc2lvbnMgYmVmb3JlIHRoZSBmaXJzdCBlZGl0PwoKKipOb3RlLioqIFRoZSBjaGFydCBzaG93cyBob3cgbWFueSB1c2VycyAodmVydGljYWwsIHktYXhpcyAtPiBsb2cgc2NhbGUsIHRoZSBkYXRhIHBvaW50IGxhYmVscyBhcmUgYWJzb2x1dGUgY291bnRzKSBtYWRlIHRoZWlyIGZpcnN0IGVkaXQgZm9sbG93aW5nIGhvdyBtYW55IGJhbm5lciBpbXByZXNzaW9ucyAoaG9yaXpvbnRhbCwgeC1heGlzKSBwZXIgY2FtcGFpZ24gY2hhbm5lbCAoZGVza3RvcCwgbW9iaWxlLCBpcGFkKS4KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcuaGVpZ2h0ICA9IDEwfQpwRnJhbWUgPC0gZGF0YVNldCAlPiUgCiAgZHBseXI6OmZpbHRlcighaXMubmEodHNfcmV2aXNpb24pKSAlPiUgCiAgZHBseXI6OnNlbGVjdChiYW5uZXIsIGJhbm5lcl9pbXByZXNzaW9ucykgJT4lIAogIGRwbHlyOjpncm91cF9ieShiYW5uZXIsIGJhbm5lcl9pbXByZXNzaW9ucykgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UodXNlcnMgPSBuKCkpICU+JSAKICBkcGx5cjo6ZmlsdGVyKGJhbm5lcl9pbXByZXNzaW9ucyAhPSAwKQpwRnJhbWUkYmFubmVyX2ltcHJlc3Npb25zIDwtIGFzLmZhY3RvcihwRnJhbWUkYmFubmVyX2ltcHJlc3Npb25zKQoKIyAtIFZpc3VhbGl6ZSB3LiB7Z2dwbG90Mn0KZ2dwbG90KHBGcmFtZSwgYWVzKHggPSBiYW5uZXJfaW1wcmVzc2lvbnMsCiAgICAgICAgICAgICAgICAgICB5ID0gbG9nKHVzZXJzKSwKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdXNlcnMsIAogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICBmaWxsID0gYmFubmVyKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgZ2VvbV9saW5lKHNpemUgPSAuMjUpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogRWRpdHMnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSA2LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCiMjIyAzLjMgRGFpbHkgZWRpdHMgcGVyIHRhZwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5oZWlnaHQgID0gMTB9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6ZmlsdGVyKCFpcy5uYSh0c19yZXZpc2lvbikpICU+JSAKICBkcGx5cjo6c2VsZWN0KGJhbm5lciwgdHNfcmV2aXNpb24pCnBGcmFtZSR0c19yZXZpc2lvbiA8LSBzdWJzdHIocEZyYW1lJHRzX3JldmlzaW9uLCA1LCA4KQpwRnJhbWUkdHNfcmV2aXNpb24gPC0gc2FwcGx5KHBGcmFtZSR0c19yZXZpc2lvbiwgZnVuY3Rpb24oeCkgewogIGZpcnN0IDwtIHN1YnN0cih4LCAxLCAyKQogIHNlY29uZCA8LSBzdWJzdHIoeCwgMywgNCkKICByZXR1cm4oCiAgICBwYXN0ZTAoZmlyc3QsICItIiwgc2Vjb25kKQogICkKfSkKcEZyYW1lIDwtIHBGcmFtZSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoYmFubmVyLCB0c19yZXZpc2lvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShlZGl0cyA9IG4oKSkKcEZyYW1lJHRzX3JldmlzaW9uIDwtIGFzLmZhY3RvcihwRnJhbWUkdHNfcmV2aXNpb24pCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChwRnJhbWUsIGFlcyh4ID0gdHNfcmV2aXNpb24sCiAgICAgICAgICAgICAgICAgICB5ID0gbG9nKGVkaXRzKSwKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZWRpdHMsIAogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBiYW5uZXIsIAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBiYW5uZXIsCiAgICAgICAgICAgICAgICAgICBmaWxsID0gYmFubmVyKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgZ2VvbV9saW5lKHNpemUgPSAuMjUpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogRWRpdHMnKSArCiAgeGxhYigiRGF5IikgKyB5bGFiKCJMb2coZWRpdHMpIikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gNiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gMTIpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgojIyMgMy40IERhaWx5IGVkaXRzICh0b3RhbCkKCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcuaGVpZ2h0ICA9IDEwfQpwRnJhbWUgPC0gcEZyYW1lICU+JQogIGRwbHlyOjpzZWxlY3QodHNfcmV2aXNpb24sIGVkaXRzKSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHRzX3JldmlzaW9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGVkaXRzID0gc3VtKGVkaXRzKSkKcEZyYW1lJHRzX3JldmlzaW9uIDwtIGFzLmZhY3RvcihwRnJhbWUkdHNfcmV2aXNpb24pCiMgLSBWaXN1YWxpemUgdy4ge2dncGxvdDJ9CmdncGxvdChwRnJhbWUsIGFlcyh4ID0gdHNfcmV2aXNpb24sCiAgICAgICAgICAgICAgICAgICB5ID0gbG9nKGVkaXRzKSwKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZWRpdHMpKSArCiAgZ2VvbV9saW5lKHNpemUgPSAuMjUsIGdyb3VwID0gMSwgY29sb3IgPSAiZGFya3JlZCIpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLjUsIGNvbG9yID0gImRhcmtyZWQiKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGNvbG9yID0gIndoaXRlIikgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCdPY2Nhc2lvbmFsIEVkaXRvcnMgMjAyMTogRWRpdHMnKSArCiAgeGxhYigiRGF5IikgKyB5bGFiKCJMb2coZWRpdHMpIikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIGdlb21fdGV4dF9yZXBlbChzaXplID0gNiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gMTIpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgojIyMgMy40IEVkaXQgY2xhc3NlcwoKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5oZWlnaHQgID0gMTB9CnBGcmFtZSA8LSBkYXRhU2V0ICU+JSAKICBkcGx5cjo6ZmlsdGVyKCFpcy5uYSh0c19yZXZpc2lvbikpICU+JSAKICBkcGx5cjo6c2VsZWN0KHVzZXJpZCkgJT4lIAogIGRwbHlyOjpncm91cF9ieSh1c2VyaWQpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKGVkaXRzID0gbigpKQoKZWRpdEJvdW5kYXJpZXMgPC0gbGlzdCgKICBjKDAsIDEpLCAKICBjKDIsIDQpLAogIGMoNSwgOSksCiAgYygxMCwgMjApLAogIGMoMjEsIDUwKSwKICBjKDUxLCAxMDApCikKCnBGcmFtZSRlZGl0Q2xhc3MgPC0gc2FwcGx5KHBGcmFtZSRlZGl0cywgZnVuY3Rpb24oeCkgewogIHdFQyA8LSBzYXBwbHkoZWRpdEJvdW5kYXJpZXMsIGZ1bmN0aW9uKHkpIHsKICAgIHggPj0geVsxXSAmIHggPD0geVsyXQogIH0pCiAgaWYgKHN1bSh3RUMpID09IDApIHsKICAgIHJldHVybigiPj0gMTAwIikKICB9IGVsc2UgewogICAgcmV0dXJuKHBhc3RlMCgiKCIsCiAgICAgICAgICAgICAgICAgIGVkaXRCb3VuZGFyaWVzW1t3aGljaCh3RUMpXV1bMV0sCiAgICAgICAgICAgICAgICAgICIgLSAiLAogICAgICAgICAgICAgICAgICBlZGl0Qm91bmRhcmllc1tbd2hpY2god0VDKV1dWzJdLCAKICAgICAgICAgICAgICAgICAgIikiCiAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0pCgplZGl0Q2xhc3MgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShwRnJhbWUkZWRpdENsYXNzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhlZGl0Q2xhc3MpIDwtIGMoJ0VkaXQgQ2xhc3MnLCAnTnVtLlVzZXJzJykKZWRpdENsYXNzJG9yZGVyIDwtIGFzLm51bWVyaWMoc2FwcGx5KGVkaXRDbGFzcyRgRWRpdCBDbGFzc2AsIGZ1bmN0aW9uKHgpIHsKICBsb3dlciA8LSBzdHJfZXh0cmFjdCh4LCAnW1s6ZGlnaXQ6XV0rJykKfSkpCmVkaXRDbGFzcyA8LSBhcnJhbmdlKGVkaXRDbGFzcywgb3JkZXIpCmVkaXRDbGFzcyRvcmRlciA8LSBOVUxMCmRhdGF0YWJsZShlZGl0Q2xhc3MpCmBgYAoKIyMjIDMuNSBEYWlseSB1c2VyIGVkaXRzIGJlZm9yZSwgZHVyaW5nLCBhbmQgYWZ0ZXIgY2FtcGFpZ24KCmBgYHtyIGVjaG8gPSBULCBldmFsID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNi41fQp1c2VyRWRpdHMgPC0gcmVhZC5jc3YocGFzdGUwKGdldHdkKCksIi9fYW5hbHl0aWNzLyIsICJ1c2VyRWRpdHNfSGlzdG9yeS5jc3YiKSwKICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCnVzZXJFZGl0cyRkYXRlVHlwZSA8LSBhcy5jaGFyYWN0ZXIodXNlckVkaXRzJHJldmFjdG9yX3RpbWVzdGFtcCkKdXNlckVkaXRzJGRhdGVUeXBlIDwtIHN1YnN0cih1c2VyRWRpdHMkZGF0ZVR5cGUsIDEsIDgpCnVzZXJFZGl0cyRkYXRlVHlwZSA8LSBzYXBwbHkodXNlckVkaXRzJGRhdGVUeXBlLCBmdW5jdGlvbih4KSB7CiAgZCA8LSBwYXN0ZTAoCiAgICAgIHN1YnN0cih4LCAxLCA0KSwgCiAgICAgICItIiwKICAgICAgc3Vic3RyKHgsIDUsIDYpLAogICAgICAiLSIsCiAgICAgIHN1YnN0cih4LCA3LCA4KQogICAgKQogIHJldHVybihkKQp9KQp1c2VyRWRpdHMgPC0gZHBseXI6OmFycmFuZ2UodXNlckVkaXRzLCBkYXRlVHlwZSkgJT4lIAogIHNlbGVjdChhY3Rvcl91c2VyLCBkYXRlVHlwZSkKCmNhbXBhaWduRGF5cyA8LSBjKCIyMDIxLTEwLTE4IiwgIjIwMjEtMTAtMTkiLCAiMjAyMS0xMC0yMCIsICIyMDIxLTEwLTIxIiwgIjIwMjEtMTAtMjIiLCAiMjAyMS0xMC0yMyIsCiAgICAgICAgICAgICAgICAgICIyMDIxLTEwLTI0IiwgIjIwMjEtMTAtMjUiLCAiMjAyMS0xMC0yNiIsICIyMDIxLTEwLTI3IiwgIjIwMjEtMTAtMjgiLCAiMjAyMS0xMC0yOSIsCiAgICAgICAgICAgICAgICAgICIyMDIxLTEwLTMwIiwgIjIwMjEtMTAtMzEiKQoKcEZyYW1lIDwtIHVzZXJFZGl0cyAlPiUgCiAgc2VsZWN0KGRhdGVUeXBlKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZVR5cGUpICU+JSAKICBzdW1tYXJpc2UoRWRpdHMgPSBuKCkpCnBGcmFtZSA8LSBhcnJhbmdlKHBGcmFtZSwgZGF0ZVR5cGUpCnBGcmFtZSRjYW1wYWlnbiA8LSBzYXBwbHkocEZyYW1lJGRhdGVUeXBlLCBmdW5jdGlvbih4KSB7CiAgaWYgKHggPCAiMjAyMS0xMC0xOCIpIHsKICAgIHJldHVybigiQmVmb3JlIENhbXBhaWduIikKICB9IGVsc2UgaWYgKHggPiAiMjAyMS0xMC0zMSIpIHsKICAgIHJldHVybigiQWZ0ZXIgQ2FtcGFpZ24iKSAKICB9IGVsc2UgewogICAgcmV0dXJuKCJDYW1wYWlnbiIpCiAgfQp9KQpwRnJhbWUkZGF0ZSA8LSBhcy5jaGFyYWN0ZXIocEZyYW1lJGRhdGVUeXBlKQpnZ3Bsb3QocEZyYW1lLCBhZXMoeCA9IGRhdGUsCiAgICAgICAgICAgICAgICAgICB5ID0gRWRpdHMsCiAgICAgICAgICAgICAgICAgICBncm91cCA9IGNhbXBhaWduLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBjYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBjYW1wYWlnbiwKICAgICAgICAgICAgICAgICAgIGxhYmVsID0gRWRpdHMsCiAgICAgICAgICAgICAgICAgICAgKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC41KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMuNSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBnZ3RpdGxlKCcyMDIxIFdNREUgT2NjYXNpb25hbCBFZGl0b3JzIEJhbm5lciBDYW1wYWlnbicpICsKICB4bGFiKCJEYXRlIikgKyB5bGFiKCJFZGl0cyIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDYpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKYGBge3IgZWNobyA9IFQsIGV2YWwgPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2LjV9CmRld2lraUVkaXRzIDwtIHJlYWQuY3N2KHBhc3RlMChnZXR3ZCgpLCIvX3JlcG9ydGluZy8iLCAiYXZnX2VkaXRzX2Rld2lraS5jc3YiKSwKICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmRld2lraUVkaXRzJGRhdGVUeXBlIDwtIGFzLmNoYXJhY3RlcihkZXdpa2lFZGl0cyRyZXZhY3Rvcl90aW1lc3RhbXApCmRld2lraUVkaXRzJGRhdGVUeXBlIDwtIHN1YnN0cihkZXdpa2lFZGl0cyRkYXRlVHlwZSwgMSwgOCkKZGV3aWtpRWRpdHMkZGF0ZVR5cGUgPC0gc2FwcGx5KGRld2lraUVkaXRzJGRhdGVUeXBlLCBmdW5jdGlvbih4KSB7CiAgZCA8LSBwYXN0ZTAoCiAgICAgIHN1YnN0cih4LCAxLCA0KSwgCiAgICAgICItIiwKICAgICAgc3Vic3RyKHgsIDUsIDYpLAogICAgICAiLSIsCiAgICAgIHN1YnN0cih4LCA3LCA4KQogICAgKQogIHJldHVybihkKQp9KQpkZXdpa2lFZGl0cyA8LSBkcGx5cjo6YXJyYW5nZShkZXdpa2lFZGl0cywgZGF0ZVR5cGUpICU+JSAKICBkcGx5cjo6c2VsZWN0KHVzZXJfaWQsIGRhdGVUeXBlKQoKY2FtcGFpZ25EYXlzIDwtIGMoIjIwMjEtMTAtMTgiLCAiMjAyMS0xMC0xOSIsICIyMDIxLTEwLTIwIiwgIjIwMjEtMTAtMjEiLCAiMjAyMS0xMC0yMiIsICIyMDIxLTEwLTIzIiwKICAgICAgICAgICAgICAgICAgIjIwMjEtMTAtMjQiLCAiMjAyMS0xMC0yNSIsICIyMDIxLTEwLTI2IiwgIjIwMjEtMTAtMjciLCAiMjAyMS0xMC0yOCIsICIyMDIxLTEwLTI5IiwKICAgICAgICAgICAgICAgICAgIjIwMjEtMTAtMzAiLCAiMjAyMS0xMC0zMSIpCgpkZXdpa2lFZGl0cyA8LSBkZXdpa2lFZGl0cyAlPiUgCiAgc2VsZWN0KGRhdGVUeXBlKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZVR5cGUpICU+JSAKICBzdW1tYXJpc2UoRWRpdHMgPSBuKCkpCmRld2lraUVkaXRzIDwtIGFycmFuZ2UoZGV3aWtpRWRpdHMsIGRhdGVUeXBlKQpkZXdpa2lFZGl0cyRjYW1wYWlnbiA8LSBzYXBwbHkoZGV3aWtpRWRpdHMkZGF0ZVR5cGUsIGZ1bmN0aW9uKHgpIHsKICBpZiAoeCA8ICIyMDIxLTEwLTE4IikgewogICAgcmV0dXJuKCJCZWZvcmUgQ2FtcGFpZ24iKQogIH0gZWxzZSBpZiAoeCA+ICIyMDIxLTEwLTMxIikgewogICAgcmV0dXJuKCJBZnRlciBDYW1wYWlnbiIpIAogIH0gZWxzZSB7CiAgICByZXR1cm4oIkNhbXBhaWduIikKICB9Cn0pCgpjYW1wYWlnbkJlZm9yZSA8LSBtZWFuKHBGcmFtZSRFZGl0c1twRnJhbWUkY2FtcGFpZ24gPT0gIkJlZm9yZSBDYW1wYWlnbiJdKQpjYW1wYWlnbiA8LSBtZWFuKHBGcmFtZSRFZGl0c1twRnJhbWUkY2FtcGFpZ24gPT0gIkNhbXBhaWduIl0pCmNhbXBhaWduQWZ0ZXIgPC0gbWVhbihwRnJhbWUkRWRpdHNbcEZyYW1lJGNhbXBhaWduID09ICJBZnRlciBDYW1wYWlnbiJdKQoKZGV3aWtpQmVmb3JlIDwtIG1lYW4oZGV3aWtpRWRpdHMkRWRpdHNbZGV3aWtpRWRpdHMkY2FtcGFpZ24gPT0gIkJlZm9yZSBDYW1wYWlnbiJdKQpkZXdpa2kgPC0gbWVhbihkZXdpa2lFZGl0cyRFZGl0c1tkZXdpa2lFZGl0cyRjYW1wYWlnbiA9PSAiQ2FtcGFpZ24iXSkKZGV3aWtpQWZ0ZXIgPC0gbWVhbihkZXdpa2lFZGl0cyRFZGl0c1tkZXdpa2lFZGl0cyRjYW1wYWlnbiA9PSAiQWZ0ZXIgQ2FtcGFpZ24iXSkKYGBgCgpJbiBjb21wYXJpc29uIHRvIHRoZSBvdmVyYWxsIGVkaXRzIGluIGBkZXdpa2lgLCBpbiB0aGUgc2FtZSB0aW1lIHNwYW4gYXMgaW4gdGhlIGNoYXJ0IGFib3ZlOgoKLSBiZWZvcmUgdGhlIGNhbXBhaWduLCB0aGUgY2FtcGFpZ24gdXNlcnMgbWFkZSBgciByb3VuZChjYW1wYWlnbkJlZm9yZSwyKWAgZWRpdHMgb24gdGhlIGF2ZXJhZ2UgZGFpbHksIHdoaWxlIGluIGBkZXdpa2lgIHdlIGZpbmQgYHIgcm91bmQoZGV3aWtpQmVmb3JlLCAyKWAgZWRpdHM7Ci0gZHVyaW5nIHRoZSBjYW1wYWlnbiwgdGhlIGNhbXBhaWduIHVzZXJzIG1hZGUgYHIgcm91bmQoY2FtcGFpZ24sIDIpYCBlZGl0cyBvbiB0aGUgYXZlcmFnZSBkYWlseSwgd2hpbGUgaW4gYGRld2lraWAgd2UgZmluZCBgciByb3VuZChkZXdpa2ksIDIpYCBlZGl0czsKLSBhbmQgYWZ0ZXIgdGhlIGNhbXBhaWduLCB0aGUgY2FtcGFpZ24gdXNlcnMgbWFkZSBgciByb3VuZChjYW1wYWlnbkFmdGVyLCAyKWAgZWRpdHMgb24gdGhlIGF2ZXJhZ2UgZGFpbHksIHdoaWxlIGluIGBkZXdpa2lgIHdlIGZpbmQgYHIgcm91bmQoZGV3aWtpQWZ0ZXIsIDIpYCBlZGl0czsK