Names and collabs
#########################################
# Title: Webscraping in R
# Author: Bas Hofstra
# Version: 29-07-2021
#########################################
#start with clean workspace
rm(list=ls())
# install.packages("data.table")
library(data.table) # mainly for faster data handling
library(tidyverse) # I assume you already installed this one!
# install.packages("httr") # we don't need this for now
# require(httr)
#install.packages("xml2")
require(xml2)
#install.packages("rvest")
require(rvest)
#install.packages("devtools")
require(devtools)
# Note we're doing something different here. We're installing a *latest* version directly from GitHub
# This is because the released version of this packages contains some errors!
#devtools::install_github("jkeirstead/scholar")
require(scholar)
#define workdirectory, note the double *backslashes* if you're on windows
# setwd("/yourpathhere)"
# Let's first get the staff page read_html is a function that simply extracts html webpages and
# puts them in xml format
soc_staff <- read_html("https://www.ru.nl/sociology/research/staff/")
head(soc_staff)
class(soc_staff)
soc_staff <- soc_staff %>%
rvest::html_nodes("body") %>%
xml2::xml_find_all("//td") %>%
rvest::html_text()
fodd <- function(x) {
#what is x, x is a vector
x%%2 != 0
}
nstaf <- length(soc_staff)
nstaf
fodd(1:nstaf)
soc_names <- soc_staff[fodd(1:nstaf)]
head(soc_names)
soc_names
soc_names
soc_experts <- soc_staff[!fodd(1:nstaf)] # in the 1 until 94st number, get the even elements
head(soc_experts)
soc_df <- data.frame(cbind(soc_names, soc_experts))
# inspect again, and remove the rows we don't need (check for yourself to be certain!)
delrows <- which(soc_df$soc_names == "Staff:" | soc_df$soc_names == "PhD:" | soc_df$soc_names == "External PhD:" |
soc_df$soc_names == "Guest researchers:" | soc_df$soc_names == "Other researchers:")
soc_df <- soc_df[-delrows, ]
# Last name seems to be everything before the comma
soc_df$last_name <- gsub(",.*$", "", soc_df$soc_names)
# first name is everything between brackets
soc_df$first_name <- str_extract_all(soc_df$soc_names, "(?<=\\().+?(?=\\))", simplify = TRUE)
soc_df$last_name <- gsub(" J. \\(Jansje\\) van MSc", "", soc_df$last_name)
soc_df$first_name <- tolower(soc_df$first_name) # everything to lower!
soc_df$last_name <- tolower(soc_df$last_name)
# trimws looses all spacing before and after (if you specify 'both') a character string
soc_df$last_name <- trimws(soc_df$last_name, which = c("both"), whitespace = "[ \t\r\n]")
soc_df$first_name <- trimws(soc_df$first_name, which = c("both"), whitespace = "[ \t\r\n]")
soc_df$soc_experts <- trimws(soc_df$soc_experts, which = c("both"), whitespace = "[ \t\r\n]")
soc_df$soc_names <- trimws(soc_df$soc_names, which = c("both"), whitespace = "[ \t\r\n]")
# set affiliation to radboud, comes in handy for querying google scholar
soc_df$affiliation <- "radboud university"
#require(scholar)
get_scholar_id_fix <- function (last_name = "", first_name = "", affiliation = NA)
{
if (!any(nzchar(c(first_name, last_name))))
stop("At least one of first and last name must be specified!")
site <- getOption("scholar_site")
url <- paste0(site, "/citations?view_op=search_authors&mauthors=",
first_name, "+", last_name, "&hl=en&oi=ao")
page <- get_scholar_resp(url)
if (is.null(page))
return(NA)
aa <- httr::content(page, as = "text")
# added by Bas Hofstra: bugfix for IDs that have a dash ("-")
ids <- substring(aa, regexpr(";user=", aa))
ids <- substr(ids, 1, 19) # error prone, but unsure how to solve otherwise
# if (nchar(stringr::str_extract_all(string = aa, pattern = ";user=[[:alnum:]]+[[:punct:]]")[[1]][1]) < 18) {
# ids <- stringr::str_extract_all(string = aa, pattern = ";user=[[:alnum:]]+[[:punct:]]+[[:alnum:]]+[[:punct:]]")
# } else {
# ids <- stringr::str_extract_all(string = aa, pattern = ";user=[[:alnum:]]+[[:punct:]]")
# }
if (length(unlist(ids)) == 0) {
message("No Scholar ID found.")
return(NA)
}
ids <- ids %>% unlist %>% gsub(";user=|[[:punct:]]$", "",
.) %>% unique
if (length(ids) > 1) {
profiles <- lapply(ids, scholar::get_profile)
if (is.na(affiliation)) {
x_profile <- profiles[[1]]
warning("Selecting first out of ", length(profiles),
" candidate matches.")
}
else {
which_profile <- sapply(profiles, function(x) {
stringr::str_count(string = x$affiliation, pattern = stringr::coll(affiliation,
ignore_case = TRUE))
})
if (all(which_profile == 0)) {
warning("No researcher found at the indicated affiliation.")
return(NA)
}
else {
x_profile <- profiles[[which(which_profile !=
0)]]
}
}
}
else {
x_profile <- scholar::get_profile(id = ids)
}
return(x_profile$id)
}
# Look throught get_scholar_id_fix(last_name, first_name, affiliation)
# if we can find google scholar profiles of sociology staff!
soc_df$gs_id <- ""
for (i in 1:nrow(soc_df)) {
print(i)
time <- runif(1, 0, 1)
Sys.sleep(time)
tryCatch({
soc_df[i,c("gs_id")] <- get_scholar_id_fix(last_name = soc_df[i, c("last_name")], # so search on last_name of staff (third column)
first_name = soc_df[i, c("first_name")], # search on first_name of staff (fourth column)
affiliation = soc_df[i,c("affiliation")]) # search on affiliation of each staff (fifth column)
}, error=function(e){cat("ERROR :", conditionMessage(e), "\n")}) # continue on error, but print the error
}
# remove those without pubs from the df
# seems we're left with about 34 sociology staff members!
soc_df <- soc_df[!soc_df$gs_id == "", ]
soc_df
soc_list_profiles <- list() # first we create an empty list that we then fill up with the for loop
soc_list_publications <- list()
for (i in 1:nrow(soc_df)) {
print(i)
time <- runif(1, 0, 1)
Sys.sleep(time)
# note how you call different elements in a list '[[]]', fill in the i-th element
soc_list_profiles[[i]] <- get_profile(soc_df[i, c("gs_id")]) # Note how we call row i (remember how to call rows in a DF/Matrix) and then the associated scholar id
soc_list_publications[[i]] <- get_publications(soc_df[i, c("gs_id")])
soc_list_publications[[i]][, c("gs_id")] <- soc_df[i, c("gs_id")] # note that we again attach an id
# so both functions here call the entire profile and pubs for an author, based on google
# scholar ids
}
# Notice how fast the data blow up! The 34 RU sociology scholars publish ~3000 papers
soc_df_publications <- bind_rows(soc_list_publications)
soc_profiles_df <- list()
for (i in 1:length(soc_list_profiles)) {
# soc_profiles_df[[i]] <- data.frame(t(unlist(soc_list_profiles[[i]][1:8]))) #some annyoing
# data handling
soc_profiles_df[[i]] <- unlist(soc_list_profiles[[i]][1:8])
soc_profiles_df[[i]] <- data.frame(soc_profiles_df[[i]])
soc_profiles_df[[i]] <- data.frame(t(soc_profiles_df[[i]]))
}
soc_profiles_df <- bind_rows(soc_profiles_df)
soc_df <- left_join(soc_df, soc_profiles_df, by = c(gs_id = "id")) # merge data with soc_df
soc_df # notice all the new information we were able to get from the scholar profiles!
# get citation history of a scholar
soc_staff_cit <- list()
for (i in 1:nrow(soc_df)) {
soc_staff_cit[[i]] <- get_citation_history(soc_df[i, c("gs_id")])
if (nrow(soc_staff_cit[[i]]) > 0) {
soc_staff_cit[[i]][, c("gs_id")] <- soc_df[i, c("gs_id")] # again attach the gs_id as third column
}
}
soc_staff_cit <- bind_rows(soc_staff_cit)
colnames(soc_staff_cit)[3] <- "gs_id"
require(rvest)
require(xml2)
require(tidyverse)
# function to get collaborators and names from GS profiles
fcollabs <- function(gsid, lookforcollabs) {
htmlpage1 <- read_html(paste0("https://scholar.google.nl/citations?user=", gsid, "&hl=en")) # so we paste the google scholar id
profilename <- htmlpage1 %>% html_nodes(xpath = "//*/div[@id='gsc_prf_in']") %>% html_text() # we extract the profile name of that google scholar page
profilecollabs1 <- as.data.frame(0) # empty df necessary for later
profilecollabs2 <- as.data.frame(0) # empty df necessary for later
if (lookforcollabs == 1) { # so if you want to look for collabs, set function to 1
htmlpage2 <- read_html(paste0("https://scholar.google.com/citations?view_op=list_colleagues&hl=en&user=", gsid)) # so we paste the google scholar id
profilecollabs1 <- htmlpage2 %>% html_nodes(css="h3") %>% html_text() # get names
profilecollabs1 <- as.data.frame(profilecollabs1)
profilecollabs2 <- htmlpage2 %>% html_nodes("a") %>% html_attr("href") # get the link
profilecollabs2 <- profilecollabs2[seq_along(profilecollabs2) %% 2 > 0]
profilecollabs2 <- substring(profilecollabs2, 23)
}
if (nrow(profilecollabs1)>1) { # if there ARE collabs
profilecollabs1 <- as.data.frame(profilecollabs1) # we want to...
profilecollabs2 <- as.data.frame(profilecollabs2)
profilecollabs1[,c("coauth_id")] <- profilecollabs2[,1]
profilecollabs1[,c("gs_id")] <- gsid #... add gs_ids of focal GS profile
profilecollabs1[,c("name")] <- profilename #...and the the profile name of GS profile attached
names(profilecollabs1)[1] <- "coauth"
} else {
profilecollabs1 <- as.data.frame(cbind(gsid, profilename)) # if NOT looking for collabs...
names(profilecollabs1) <- c("gs_id", "name") #...we only attach gs_id and profilename
}
return(profilecollabs1)
}
# first the soc collaborators note how we already build a function (fcollabs()) for you you need to
# input a google scholar id and a 1 (if you want to find collabs) or 0 (only extracting names)
# fcollabs --> you can check it out if you're interested
soc_collabs <- list()
for (i in 1:nrow(soc_df)) {
time <- runif(1, 0, 1)
Sys.sleep(time)
soc_collabs[[i]] <- fcollabs(soc_df[i, c("gs_id")], 1)
}
soc_collabs <- bind_rows(soc_collabs) # bind rows, get the unique ones!
soc_collabs_unique <- unique(soc_collabs[, "coauth_id"]) # so 229 unique collaborators for RU staff?
soc_collabs_unique <- soc_collabs_unique[!is.na(soc_collabs_unique)]
save(soc_collabs, file = "addfiles\\soc_df_collabs1.RData") # you notice this takes a while, so we save the data here.
# then the names of those collaborators plus THEIR collaborators understand that we don't have
# names of them yet from the code above?
collabs_1deep <- list()
for (i in 1:length(soc_collabs_unique)) {
time <- runif(1, 0, 3)
Sys.sleep(time)
if (!soc_collabs_unique[i] %in% soc_df$gs_id) {
collabs_1deep[[i]] <- fcollabs(soc_collabs_unique[i], 1)
}
}
collabs_1deep <- bind_rows(collabs_1deep)
collabs_1deep_unique <- unique(collabs_1deep[, 2])
collabs_1deep_unique <- collabs_1deep_unique[!is.na(collabs_1deep_unique)]
save(collabs_1deep, file = "addfiles\\soc_collabs2.RData") # you notice this takes a while, so we save the data here.
for (i in c("_ukytQYAAAAJ", "lkVq32sAAAAJ", "p3IwtT4AAAAJ")) {
# drop google scholar ids that look suspiciously productive
soc_df <- soc_df[!soc_df$gs_id == i, ]
soc_df_publications <- soc_df_publications[!(soc_df_publications$gs_id == i), ]
soc_staff_cit <- soc_staff_cit[!(soc_staff_cit$gs_id == i), ]
soc_collabs <- soc_collabs[!(soc_collabs$gs_id == i), ]
}
Gender
fgender <- function(firstname_df, me, file=NULL) {
####################################
# Author: Bas Hofstra, Anne Maaike Mulders, Jochem Tolsma
# DAte: 13-10-2021, last edit: 22-09-2022
# Tasks: - assign gender baed on name
# - Adapted from Rense Corten code April 2021
####################################
#Input:
# - firstname_df: a data.frame with a column named firstname
# - me: a character vector introducing yourself: e.g. "J Tolsma, Radboud University"
# - file: location and name of file to be saved.
#------------------------------------------------------------------------------------
# Load required packages
if (!require("tidyverse", character.only = TRUE)) {
install.packages("tidyverse", dependencies = TRUE)
library(tidyverse, character.only = TRUE)
}
if (!require("rvest", character.only = TRUE)) {
install.packages("rvest", dependencies = TRUE)
library(rvest, character.only = TRUE)
}
if (!require("polite", character.only = TRUE)) {
install.packages("polite", dependencies = TRUE)
library(polite, character.only = TRUE)
}
# make links to scrape
firstname_df$name_url <- paste0("https://www.meertens.knaw.nl/nvb/naam/is/", firstname_df[, c("firstname")])
#------------------------------------------------------------------------------------
### 2: introduce to server ###
# Introduce myself to the server
session <- bow("https://www.meertens.knaw.nl/nvb/naam/is", user_agent = me , delay = 1)
#------------------------------------------------------------------------------------
### 3: make function to get table from ###
fnames <- function(link){
name_session <-nod(session, path = link)
name_page <- scrape(name_session)
return(name_page)
}
name_list <- list()
table_list <- list()
firstname_df$gender <- NA
for (i in 1:nrow(firstname_df)) {
print(i)
name_list[[i]] <- fnames(firstname_df[i, c("name_url")])
# extract name frequency table and gender info
table_list[[i]] <- name_list[[i]] %>% html_table()
table_list[[i]][[1]][table_list[[i]][[1]]=="--"] <- "0"
if (as.numeric(table_list[[i]][[1]]$X3[2]) > as.numeric(table_list[[i]][[1]]$X3[6])) {
firstname_df$gender[i] <- "male" } else {
firstname_df$gender[i] <- "female"
}
if (!is.null(file)) (save(firstname_df, file=file))
}
return(firstname_df)
}
soc_df %>% mutate(firstname=first_name) -> soc_df
soc_df$firstname
soc_df <- fgender(soc_df, me="Jochem Tolsma, RU/RUG")
save(soc_df, file="soc_df_s2.RData")
Ethnicity
load("/Users/anuschka/Documents/labjournal/soc_df22.RData")
soc_df %>% mutate(lastname=last_name) -> soc_df
lastname_df <- soc_df
We moeten de namen in het juiste format zetten.
Dus niet “van der meer”, maar: “meer, van der”.
Dan verwijder ik dubbele achternamen.
#voorvoegsels correct zetten voor scraper
voorvoegsels <- c("'t ", "d' ", "de ", "de la ", "den ", "del ", "der ", "des ", "el ", "el- ", "in 't ", "la ", "le ", "les ", "op den ", "ten ", "ter ", "tes ", "van ", "van 't ", "van de " , "van der ", "van den ", "von der ", "op den ", "ul ")
for (i in 1: length(lastname_df$lastname)) {
if (sum(str_detect(lastname_df$lastname[i], voorvoegsels))>0) {
last <- as.character(str_split(lastname_df$lastname[i], pattern=" ", simplify = TRUE))
last <- last[length(last)]
first <- as.character(unlist(strsplit(lastname_df$lastname[i], split=last, fixed=TRUE)))
lastname_df$lastname[i] <- paste(last, ", ", first, sep="")
}
}
#dubbele namen verwijderen. let op dubbele namen met voorvoegsel worden niet gecleaned. TO DO
for (i in 1: length(lastname_df$lastname)) {
if (!sum(str_detect(lastname_df$lastname[i], voorvoegsels))>0) {
lastname_df$lastname[i] <- as.character(str_split(lastname_df$lastname[i], pattern=" ", n = 2, simplify = TRUE)[,1])
lastname_df$lastname[i] <- as.character(str_split(lastname_df$lastname[i], pattern="-", n = 2, simplify = TRUE)[,1])
}
}
lastname_df$lastname<- trimws(lastname_df$lastname, which = c("right"), whitespace = "[ \t\r\n]")
lastname_df$lastname <- str_replace_all(lastname_df$lastname, " ", "%20") #html links houden niet van spaties.
lastname_df$np <- ""
# Load required packages
if (!require("tidyverse", character.only = TRUE)) {
install.packages("tidyverse", dependencies = TRUE)
library(tidyverse, character.only = TRUE)
}
if (!require("rvest", character.only = TRUE)) {
install.packages("rvest", dependencies = TRUE)
library(rvest, character.only = TRUE)
}
# if (!require("polite", character.only = TRUE)) {
# install.packages("polite", dependencies = TRUE)
# library(polite, character.only = TRUE)
# }
#
# if (!require("xml2", character.only = TRUE)) {
# install.packages("xml2", dependencies = TRUE)
# library(polite, character.only = TRUE)
# }
hier maken we de links voor de website.
# creating URLs: origin
lastname_df$name_origin <- ifelse((lastname_df$np==""),
paste0("https://www.cbgfamilienamen.nl/nfb/detail_naam.php?gba_naam=",
stringr::str_to_title(lastname_df[, c("lastname")]),
"&gba_naam=",
stringr::str_to_title(lastname_df[, c("lastname")]),
"&nfd_naam=",
stringr::str_to_title(lastname_df[, c("lastname")]),
"&info=analyse+en+verklaring&operator=eq&taal="),
paste0("https://www.cbgfamilienamen.nl/nfb/detail_naam.php?gba_naam=",
lastname_df[, c("np2")],
stringr::str_to_title(lastname_df[, c("lastname")]),
"&gba_naam=",
lastname_df[, c("np2")],
stringr::str_to_title(lastname_df[, c("lastname")]),
"&nfd_naam=",
stringr::str_to_title(lastname_df[, c("lastname")]),
"%2C+",
lastname_df[, c("np")],
"&info=analyse+en+verklaring&operator=eq&taal="))
hier slaan we alles op
name_originl <- list()
table_originl <- list()
time <- 0.1
crucical scrape
loop
kom voorlopig geen fouten tegen, toch alles alvast in een trycatch
gezet. let op dat ik niet netjes scrape. dus zonder functie ‘polite’
gezien de kleine aantallen in onze cursus, mag dat wmb wel.
for (i in 1:nrow(lastname_df)) {
print(i)
Sys.sleep(time)
tryCatch({
name_originl[[i]] <- read_html(lastname_df[i, c("name_origin")])
table_originl[[i]] <- name_originl[[i]] %>% html_table()
},
warning = function(w) {
cat("WARNING:", conditionMessage(w), "\n") #WARNING message
},
error=function(e){
err <- conditionMessage(e)
cat("Error:", conditionMessage(e), "\n") #ERROR message
}
)
}
en vanaf hier is het eigenlijk alleen maar opschonen.
origin_txt <- list()
for (i in 1:length(name_originl)) {
origin_txt [[i]] <- name_originl[[i]] %>% html_text() %>% as.character()
}
# Get out the relevant origin information from the xml lists
origin_ln <- list()
for (i in 1:length(name_originl)) {
origin_ln[[i]] <- name_originl[[i]] %>% html_nodes("div") %>% rvest::html_text()
origin_ln[[i]] <- origin_ln[[i]][[3]]
}
# Remove mess
for (i in 1:length(origin_ln)) {
origin_ln[[i]] <- gsub("\\t", " ", origin_ln[[i]])
origin_ln[[i]] <- gsub("\\n", " ", origin_ln[[i]])
}
# Flatten nested structure of the origin information
#origin_ln <- rbind(flatten(origin_ln))
# Detaching the names and origin info for easier data handling
origin <- unlist(origin_ln)
origin <- str_extract_all(origin, "varianten(.*?)©")
# Origin information is usually mentioned after "verklaring" or "kenmerken"
origin <- str_remove_all(origin, "varianten")
origin <- str_remove_all(origin, "CBG Bronnen")
origin <- str_remove_all(origin, "catalogus")
origin <- str_remove_all(origin, "©")
verklaring <- str_remove_all(origin, "kenmerken:(.*?)$")
kenmerken <- str_extract_all(origin, "kenmerken:(.*?)$")
kenmerken <- str_remove_all(kenmerken, "specifieke componenten:(.*?)$")
sc <- str_extract_all(origin, "specifieke componenten:(.*?)$") # Not directly relevant to us, but does mean that the name has a webpage
# Make into a neat dataframe with the names attached
verklaring <- trimws(verklaring, which = "both")
kenmerken <- trimws(kenmerken, which = "both")
sc <- trimws(sc, which = "both")
vk <- data.frame(soc_df$lastname, verklaring, kenmerken, sc)
Separating names with
Dutch & unknown origin
Next, we identify those names for which no additional information was
found. This is important to distinguish Dutch names from names with
unknown origins.
- Dutch names: no label indicating that the name is Dutch, but some
other information available on name origin
- Unknown names: web page cannot be found, so origin information is
empty.
# Identify last names that could not be found
vk <- vk %>%
mutate(verklaring = ifelse(verklaring=="", 0, verklaring),
kenmerken = ifelse(kenmerken=="character(0)", 0, kenmerken),
sc = ifelse(sc=="character(0)", 0, sc),
no_info = nchar(verklaring) + nchar(kenmerken) + nchar(sc))
vk <- vk %>%
mutate(no_info = ifelse(no_info==3, 1, 0),
verklaring = ifelse(verklaring==0, NA, verklaring),
kenmerken = ifelse(kenmerken==0, NA, kenmerken))
# If there is no text in verklaring or kenmerken, the name could not be found in the databases.
RSiena dataframe
network based on
publications
NATURALLY, THIS IS JUST AN EXAMPLE. fOR rSIENA, YOU NEED AT LEAST 3
NETWORKS. tHUS YOU HAVE TO TWEAK THE PERIOD AS YOU DEEM FIT.
library(stringr)
#empty adjacency matrix for the years 2001-2010
network2010_2013 <- matrix(NA, nrow=nrow(soc_df), ncol=nrow(soc_df))
network2014_2018 <- matrix(NA, nrow=nrow(soc_df), ncol=nrow(soc_df))
network2019_2022 <- matrix(NA, nrow=nrow(soc_df), ncol=nrow(soc_df))
#select publications of the corresponding time era
pubs_sel <- soc_df_publications %>%
mutate(author = tolower(author)) %>%
filter(year>=2010 & year<=2013)
#fill the matrix
for (ego in 1: nrow(soc_df)) {
name_ego <- soc_df$last_name[ego] #which ego?
pubs_sel2 <- pubs_sel[str_detect(pubs_sel$author, name_ego),] #publications of ego
for (alter in 1:nrow(soc_df)){
name_alter <- soc_df$last_name[alter] #which alter?
network2010_2013[ego,alter] <- as.numeric(sum(str_detect(pubs_sel2$author, name_alter)) > 1) #did alter publish with ego
}
}
#select publications of the corresponding time era
pubs_sel <- soc_df_publications %>%
mutate(author = tolower(author)) %>%
filter(year>=2014 & year<=2018)
#fill the matrix
for (ego in 1: nrow(soc_df)) {
name_ego <- soc_df$last_name[ego] #which ego?
pubs_sel2 <- pubs_sel[str_detect(pubs_sel$author, name_ego),] #publications of ego
for (alter in 1:nrow(soc_df)){
name_alter <- soc_df$last_name[alter] #which alter?
network2014_2018[ego,alter] <- as.numeric(sum(str_detect(pubs_sel2$author, name_alter)) > 1) #did alter publish with ego
}
}
#select publications of the corresponding time era
pubs_sel <- soc_df_publications %>%
mutate(author = tolower(author)) %>%
filter(year>=2019 & year<=2022)
#fill the matrix
for (ego in 1: nrow(soc_df)) {
name_ego <- soc_df$last_name[ego] #which ego?
pubs_sel2 <- pubs_sel[str_detect(pubs_sel$author, name_ego),] #publications of ego
for (alter in 1:nrow(soc_df)){
name_alter <- soc_df$last_name[alter] #which alter?
network2019_2022[ego,alter] <- as.numeric(sum(str_detect(pubs_sel2$author, name_alter)) > 1) #did alter publish with ego
}
}
c(dim(network2010_2013),3)
net_array <- array(data = c(network2010_2013, network2014_2018, network2019_2022), dim=c(dim(network2010_2013),3))
net_array[1,1,1]
save(net_array, file="soc_net_array.RData")
I didn’t run the last part yet to estimate a model because it will
still be altered (more variables)
LS0tCnRpdGxlOiAiV2Vic2NyYXBpbmcgU29jaW9sb2d5IFN0YWZmIgphdXRob3I6ICJBbnVzY2hrYSBQZWVsZW4iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OiBodG1sX2RvY3VtZW50Ci0tLQoKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIGdsb2JhbHNldHRpbmdzLCBlY2hvPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KbGlicmFyeShrbml0cikKCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGguY3V0b2ZmPTEwMCksdGlkeT1UUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSxjb21tZW50ID0gIiM+IiwgY2FjaGU9VFJVRSwgY2xhc3Muc291cmNlPWMoInRlc3QiKSwgY2xhc3Mub3V0cHV0PWMoInRlc3QyIikpCm9wdGlvbnMod2lkdGggPSAxMDApCnJnbDo6c2V0dXBLbml0cigpCgoKCmNvbG9yaXplIDwtIGZ1bmN0aW9uKHgsIGNvbG9yKSB7c3ByaW50ZigiPHNwYW4gc3R5bGU9J2NvbG9yOiAlczsnPiVzPC9zcGFuPiIsIGNvbG9yLCB4KSB9CmBgYAoKYGBge3Iga2xpcHB5LCBlY2hvPUZBTFNFLCBpbmNsdWRlPVRSVUV9CmtsaXBweTo6a2xpcHB5KHBvc2l0aW9uID0gYygndG9wJywgJ3JpZ2h0JykpCiNrbGlwcHk6OmtsaXBweShjb2xvciA9ICdkYXJrcmVkJykKI2tsaXBweTo6a2xpcHB5KHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgdG9vbHRpcF9zdWNjZXNzID0gJ0RvbmUnKQpgYGAKCiMgTmFtZXMgYW5kIGNvbGxhYnMgCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBUaXRsZTogICAgV2Vic2NyYXBpbmcgaW4gUgojIEF1dGhvcjogICBCYXMgSG9mc3RyYQojIFZlcnNpb246ICAyOS0wNy0yMDIxCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojc3RhcnQgd2l0aCBjbGVhbiB3b3Jrc3BhY2UgCnJtKGxpc3Q9bHMoKSkKCiMgaW5zdGFsbC5wYWNrYWdlcygiZGF0YS50YWJsZSIpIApsaWJyYXJ5KGRhdGEudGFibGUpICMgbWFpbmx5IGZvciBmYXN0ZXIgZGF0YSBoYW5kbGluZwpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBJIGFzc3VtZSB5b3UgYWxyZWFkeSBpbnN0YWxsZWQgdGhpcyBvbmUhCiMgaW5zdGFsbC5wYWNrYWdlcygiaHR0ciIpICMgd2UgZG9uJ3QgbmVlZCB0aGlzIGZvciBub3cKIyByZXF1aXJlKGh0dHIpCiNpbnN0YWxsLnBhY2thZ2VzKCJ4bWwyIikKcmVxdWlyZSh4bWwyKQojaW5zdGFsbC5wYWNrYWdlcygicnZlc3QiKQpyZXF1aXJlKHJ2ZXN0KQojaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQpyZXF1aXJlKGRldnRvb2xzKQojIE5vdGUgd2UncmUgZG9pbmcgc29tZXRoaW5nIGRpZmZlcmVudCBoZXJlLiBXZSdyZSBpbnN0YWxsaW5nIGEgKmxhdGVzdCogdmVyc2lvbiBkaXJlY3RseSBmcm9tIEdpdEh1YgojIFRoaXMgaXMgYmVjYXVzZSB0aGUgcmVsZWFzZWQgdmVyc2lvbiBvZiB0aGlzIHBhY2thZ2VzIGNvbnRhaW5zIHNvbWUgZXJyb3JzIQojZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJqa2VpcnN0ZWFkL3NjaG9sYXIiKSAKCgpyZXF1aXJlKHNjaG9sYXIpCgojZGVmaW5lIHdvcmtkaXJlY3RvcnksIG5vdGUgdGhlIGRvdWJsZSAqYmFja3NsYXNoZXMqIGlmIHlvdSdyZSBvbiB3aW5kb3dzCiMgc2V0d2QoIi95b3VycGF0aGhlcmUpIgpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQojIExldCdzIGZpcnN0IGdldCB0aGUgc3RhZmYgcGFnZSByZWFkX2h0bWwgaXMgYSBmdW5jdGlvbiB0aGF0IHNpbXBseSBleHRyYWN0cyBodG1sIHdlYnBhZ2VzIGFuZAojIHB1dHMgdGhlbSBpbiB4bWwgZm9ybWF0CnNvY19zdGFmZiA8LSByZWFkX2h0bWwoImh0dHBzOi8vd3d3LnJ1Lm5sL3NvY2lvbG9neS9yZXNlYXJjaC9zdGFmZi8iKQoKaGVhZChzb2Nfc3RhZmYpCgpjbGFzcyhzb2Nfc3RhZmYpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CnNvY19zdGFmZiA8LSBzb2Nfc3RhZmYgJT4lCiAgICBydmVzdDo6aHRtbF9ub2RlcygiYm9keSIpICU+JQogICAgeG1sMjo6eG1sX2ZpbmRfYWxsKCIvL3RkIikgJT4lCiAgICBydmVzdDo6aHRtbF90ZXh0KCkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KZm9kZCA8LSBmdW5jdGlvbih4KSB7CiAgI3doYXQgaXMgeCwgeCBpcyBhIHZlY3RvcgogeCUlMiAhPSAwIAp9Cgpuc3RhZiA8LSBsZW5ndGgoc29jX3N0YWZmKQpuc3RhZgpmb2RkKDE6bnN0YWYpCgpzb2NfbmFtZXMgPC0gc29jX3N0YWZmW2ZvZGQoMTpuc3RhZildIApoZWFkKHNvY19uYW1lcykKc29jX25hbWVzCgpzb2NfbmFtZXMKCmBgYAoKYGBge3IsZXZhbD1GQUxTRX0Kc29jX2V4cGVydHMgPC0gc29jX3N0YWZmWyFmb2RkKDE6bnN0YWYpXSAgIyBpbiB0aGUgMSB1bnRpbCA5NHN0IG51bWJlciwgZ2V0IHRoZSBldmVuIGVsZW1lbnRzCmhlYWQoc29jX2V4cGVydHMpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CnNvY19kZiA8LSBkYXRhLmZyYW1lKGNiaW5kKHNvY19uYW1lcywgc29jX2V4cGVydHMpKSAgCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQojIGluc3BlY3QgYWdhaW4sIGFuZCByZW1vdmUgdGhlIHJvd3Mgd2UgZG9uJ3QgbmVlZCAoY2hlY2sgZm9yIHlvdXJzZWxmIHRvIGJlIGNlcnRhaW4hKQoKZGVscm93cyA8LSB3aGljaChzb2NfZGYkc29jX25hbWVzID09ICJTdGFmZjoiIHwgc29jX2RmJHNvY19uYW1lcyA9PSAiUGhEOiIgfCBzb2NfZGYkc29jX25hbWVzID09ICJFeHRlcm5hbCBQaEQ6IiB8CiAgICBzb2NfZGYkc29jX25hbWVzID09ICJHdWVzdCByZXNlYXJjaGVyczoiIHwgc29jX2RmJHNvY19uYW1lcyA9PSAiT3RoZXIgcmVzZWFyY2hlcnM6IikKCnNvY19kZiA8LSBzb2NfZGZbLWRlbHJvd3MsIF0KYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBMYXN0IG5hbWUgc2VlbXMgdG8gYmUgZXZlcnl0aGluZyBiZWZvcmUgdGhlIGNvbW1hCnNvY19kZiRsYXN0X25hbWUgPC0gZ3N1YigiLC4qJCIsICIiLCBzb2NfZGYkc29jX25hbWVzKQoKIyBmaXJzdCBuYW1lIGlzIGV2ZXJ5dGhpbmcgYmV0d2VlbiBicmFja2V0cwpzb2NfZGYkZmlyc3RfbmFtZSA8LSBzdHJfZXh0cmFjdF9hbGwoc29jX2RmJHNvY19uYW1lcywgIig/PD1cXCgpLis/KD89XFwpKSIsIHNpbXBsaWZ5ID0gVFJVRSkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0Kc29jX2RmJGxhc3RfbmFtZSA8LSBnc3ViKCIgSi4gXFwoSmFuc2plXFwpIHZhbiBNU2MiLCAiIiwgc29jX2RmJGxhc3RfbmFtZSkKc29jX2RmJGZpcnN0X25hbWUgPC0gdG9sb3dlcihzb2NfZGYkZmlyc3RfbmFtZSkgICMgZXZlcnl0aGluZyB0byBsb3dlciEKc29jX2RmJGxhc3RfbmFtZSA8LSB0b2xvd2VyKHNvY19kZiRsYXN0X25hbWUpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CiMgdHJpbXdzIGxvb3NlcyBhbGwgc3BhY2luZyBiZWZvcmUgYW5kIGFmdGVyIChpZiB5b3Ugc3BlY2lmeSAnYm90aCcpIGEgY2hhcmFjdGVyIHN0cmluZwpzb2NfZGYkbGFzdF9uYW1lIDwtIHRyaW13cyhzb2NfZGYkbGFzdF9uYW1lLCB3aGljaCA9IGMoImJvdGgiKSwgd2hpdGVzcGFjZSA9ICJbIFx0XHJcbl0iKQpzb2NfZGYkZmlyc3RfbmFtZSA8LSB0cmltd3Moc29jX2RmJGZpcnN0X25hbWUsIHdoaWNoID0gYygiYm90aCIpLCB3aGl0ZXNwYWNlID0gIlsgXHRcclxuXSIpCnNvY19kZiRzb2NfZXhwZXJ0cyA8LSB0cmltd3Moc29jX2RmJHNvY19leHBlcnRzLCB3aGljaCA9IGMoImJvdGgiKSwgd2hpdGVzcGFjZSA9ICJbIFx0XHJcbl0iKQpzb2NfZGYkc29jX25hbWVzIDwtIHRyaW13cyhzb2NfZGYkc29jX25hbWVzLCB3aGljaCA9IGMoImJvdGgiKSwgd2hpdGVzcGFjZSA9ICJbIFx0XHJcbl0iKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQojIHNldCBhZmZpbGlhdGlvbiB0byByYWRib3VkLCBjb21lcyBpbiBoYW5keSBmb3IgcXVlcnlpbmcgZ29vZ2xlIHNjaG9sYXIKc29jX2RmJGFmZmlsaWF0aW9uIDwtICJyYWRib3VkIHVuaXZlcnNpdHkiCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CiNyZXF1aXJlKHNjaG9sYXIpCgpnZXRfc2Nob2xhcl9pZF9maXggPC0gZnVuY3Rpb24gKGxhc3RfbmFtZSA9ICIiLCBmaXJzdF9uYW1lID0gIiIsIGFmZmlsaWF0aW9uID0gTkEpCnsKICBpZiAoIWFueShuemNoYXIoYyhmaXJzdF9uYW1lLCBsYXN0X25hbWUpKSkpCiAgICBzdG9wKCJBdCBsZWFzdCBvbmUgb2YgZmlyc3QgYW5kIGxhc3QgbmFtZSBtdXN0IGJlIHNwZWNpZmllZCEiKQogIHNpdGUgPC0gZ2V0T3B0aW9uKCJzY2hvbGFyX3NpdGUiKQogIHVybCA8LSBwYXN0ZTAoc2l0ZSwgIi9jaXRhdGlvbnM/dmlld19vcD1zZWFyY2hfYXV0aG9ycyZtYXV0aG9ycz0iLAogICAgICAgICAgICAgICAgZmlyc3RfbmFtZSwgIisiLCBsYXN0X25hbWUsICImaGw9ZW4mb2k9YW8iKQogIHBhZ2UgPC0gZ2V0X3NjaG9sYXJfcmVzcCh1cmwpCiAgaWYgKGlzLm51bGwocGFnZSkpCiAgICByZXR1cm4oTkEpCiAgYWEgPC0gaHR0cjo6Y29udGVudChwYWdlLCBhcyA9ICJ0ZXh0IikKICAjIGFkZGVkIGJ5IEJhcyBIb2ZzdHJhOiBidWdmaXggZm9yIElEcyB0aGF0IGhhdmUgYSBkYXNoICgiLSIpCiAgaWRzIDwtIHN1YnN0cmluZyhhYSwgcmVnZXhwcigiO3VzZXI9IiwgYWEpKQogIGlkcyA8LSBzdWJzdHIoaWRzLCAxLCAxOSkgIyBlcnJvciBwcm9uZSwgYnV0IHVuc3VyZSBob3cgdG8gc29sdmUgb3RoZXJ3aXNlCiAgIyBpZiAobmNoYXIoc3RyaW5ncjo6c3RyX2V4dHJhY3RfYWxsKHN0cmluZyA9IGFhLCBwYXR0ZXJuID0gIjt1c2VyPVtbOmFsbnVtOl1dK1tbOnB1bmN0Ol1dIilbWzFdXVsxXSkgPCAxOCkgewogICMgICBpZHMgPC0gc3RyaW5ncjo6c3RyX2V4dHJhY3RfYWxsKHN0cmluZyA9IGFhLCBwYXR0ZXJuID0gIjt1c2VyPVtbOmFsbnVtOl1dK1tbOnB1bmN0Ol1dK1tbOmFsbnVtOl1dK1tbOnB1bmN0Ol1dIikKICAjIH0gZWxzZSB7CiAgIyAgIGlkcyA8LSBzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwoc3RyaW5nID0gYWEsIHBhdHRlcm4gPSAiO3VzZXI9W1s6YWxudW06XV0rW1s6cHVuY3Q6XV0iKQogICMgfQogIGlmIChsZW5ndGgodW5saXN0KGlkcykpID09IDApIHsKICAgIG1lc3NhZ2UoIk5vIFNjaG9sYXIgSUQgZm91bmQuIikKICAgIHJldHVybihOQSkKICB9CiAgaWRzIDwtIGlkcyAlPiUgdW5saXN0ICU+JSBnc3ViKCI7dXNlcj18W1s6cHVuY3Q6XV0kIiwgIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4pICU+JSB1bmlxdWUKICBpZiAobGVuZ3RoKGlkcykgPiAxKSB7CiAgICBwcm9maWxlcyA8LSBsYXBwbHkoaWRzLCBzY2hvbGFyOjpnZXRfcHJvZmlsZSkKICAgIGlmIChpcy5uYShhZmZpbGlhdGlvbikpIHsKICAgICAgeF9wcm9maWxlIDwtIHByb2ZpbGVzW1sxXV0KICAgICAgd2FybmluZygiU2VsZWN0aW5nIGZpcnN0IG91dCBvZiAiLCBsZW5ndGgocHJvZmlsZXMpLAogICAgICAgICAgICAgICIgY2FuZGlkYXRlIG1hdGNoZXMuIikKICAgIH0KICAgIGVsc2UgewogICAgICB3aGljaF9wcm9maWxlIDwtIHNhcHBseShwcm9maWxlcywgZnVuY3Rpb24oeCkgewogICAgICAgIHN0cmluZ3I6OnN0cl9jb3VudChzdHJpbmcgPSB4JGFmZmlsaWF0aW9uLCBwYXR0ZXJuID0gc3RyaW5ncjo6Y29sbChhZmZpbGlhdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWdub3JlX2Nhc2UgPSBUUlVFKSkKICAgICAgfSkKICAgICAgaWYgKGFsbCh3aGljaF9wcm9maWxlID09IDApKSB7CiAgICAgICAgd2FybmluZygiTm8gcmVzZWFyY2hlciBmb3VuZCBhdCB0aGUgaW5kaWNhdGVkIGFmZmlsaWF0aW9uLiIpCiAgICAgICAgcmV0dXJuKE5BKQogICAgICB9CiAgICAgIGVsc2UgewogICAgICAgIHhfcHJvZmlsZSA8LSBwcm9maWxlc1tbd2hpY2god2hpY2hfcHJvZmlsZSAhPQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwKV1dCiAgICAgIH0KICAgIH0KICB9CiAgZWxzZSB7CiAgICB4X3Byb2ZpbGUgPC0gc2Nob2xhcjo6Z2V0X3Byb2ZpbGUoaWQgPSBpZHMpCiAgfQogIHJldHVybih4X3Byb2ZpbGUkaWQpCn0KYGBgCgoKYGBge3IsIGV2YWw9RkFMU0V9CiMgTG9vayB0aHJvdWdodCBnZXRfc2Nob2xhcl9pZF9maXgobGFzdF9uYW1lLCBmaXJzdF9uYW1lLCBhZmZpbGlhdGlvbikgCiMgaWYgd2UgY2FuIGZpbmQgZ29vZ2xlIHNjaG9sYXIgcHJvZmlsZXMgb2Ygc29jaW9sb2d5IHN0YWZmIQpzb2NfZGYkZ3NfaWQgPC0gIiIKCmZvciAoaSBpbiAxOm5yb3coc29jX2RmKSkgewogIHByaW50KGkpCiAgdGltZSA8LSBydW5pZigxLCAwLCAxKQogIFN5cy5zbGVlcCh0aW1lKQogIAogIHRyeUNhdGNoKHsKICAgICBzb2NfZGZbaSxjKCJnc19pZCIpXSA8LSBnZXRfc2Nob2xhcl9pZF9maXgobGFzdF9uYW1lID0gc29jX2RmW2ksIGMoImxhc3RfbmFtZSIpXSwgIyBzbyBzZWFyY2ggb24gbGFzdF9uYW1lIG9mIHN0YWZmICh0aGlyZCBjb2x1bW4pCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpcnN0X25hbWUgPSBzb2NfZGZbaSwgYygiZmlyc3RfbmFtZSIpXSwgICMgc2VhcmNoIG9uIGZpcnN0X25hbWUgb2Ygc3RhZmYgKGZvdXJ0aCBjb2x1bW4pCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFmZmlsaWF0aW9uID0gc29jX2RmW2ksYygiYWZmaWxpYXRpb24iKV0pICMgc2VhcmNoIG9uIGFmZmlsaWF0aW9uIG9mIGVhY2ggc3RhZmYgKGZpZnRoIGNvbHVtbikKCiAgICB9LCBlcnJvcj1mdW5jdGlvbihlKXtjYXQoIkVSUk9SIDoiLCBjb25kaXRpb25NZXNzYWdlKGUpLCAiXG4iKX0pICMgY29udGludWUgb24gZXJyb3IsIGJ1dCBwcmludCB0aGUgZXJyb3IKICB9CgojIHJlbW92ZSB0aG9zZSB3aXRob3V0IHB1YnMgZnJvbSB0aGUgZGYKIyBzZWVtcyB3ZSdyZSBsZWZ0IHdpdGggYWJvdXQgMzQgc29jaW9sb2d5IHN0YWZmIG1lbWJlcnMhCnNvY19kZiA8LSBzb2NfZGZbIXNvY19kZiRnc19pZCA9PSAiIiwgXQpzb2NfZGYKYGBgCgoKYGBge3IsIGV2YWw9RkFMU0V9CnNvY19saXN0X3Byb2ZpbGVzIDwtIGxpc3QoKSAgIyBmaXJzdCB3ZSBjcmVhdGUgYW4gZW1wdHkgbGlzdCB0aGF0IHdlIHRoZW4gZmlsbCB1cCB3aXRoIHRoZSBmb3IgbG9vcApzb2NfbGlzdF9wdWJsaWNhdGlvbnMgPC0gbGlzdCgpCgpmb3IgKGkgaW4gMTpucm93KHNvY19kZikpIHsKICAgIHByaW50KGkpCiAgICB0aW1lIDwtIHJ1bmlmKDEsIDAsIDEpCiAgICBTeXMuc2xlZXAodGltZSkKCiAgICAjIG5vdGUgaG93IHlvdSBjYWxsIGRpZmZlcmVudCBlbGVtZW50cyBpbiBhIGxpc3QgJ1tbXV0nLCBmaWxsIGluIHRoZSBpLXRoIGVsZW1lbnQKICAgIHNvY19saXN0X3Byb2ZpbGVzW1tpXV0gPC0gZ2V0X3Byb2ZpbGUoc29jX2RmW2ksIGMoImdzX2lkIildKSAgIyBOb3RlIGhvdyB3ZSBjYWxsIHJvdyBpIChyZW1lbWJlciBob3cgdG8gY2FsbCByb3dzIGluIGEgREYvTWF0cml4KSBhbmQgdGhlbiB0aGUgYXNzb2NpYXRlZCBzY2hvbGFyIGlkCiAgICBzb2NfbGlzdF9wdWJsaWNhdGlvbnNbW2ldXSA8LSBnZXRfcHVibGljYXRpb25zKHNvY19kZltpLCBjKCJnc19pZCIpXSkKICAgIHNvY19saXN0X3B1YmxpY2F0aW9uc1tbaV1dWywgYygiZ3NfaWQiKV0gPC0gc29jX2RmW2ksIGMoImdzX2lkIildICAjIG5vdGUgdGhhdCB3ZSBhZ2FpbiBhdHRhY2ggYW4gaWQKICAgICMgc28gYm90aCBmdW5jdGlvbnMgaGVyZSBjYWxsIHRoZSBlbnRpcmUgcHJvZmlsZSBhbmQgcHVicyBmb3IgYW4gYXV0aG9yLCBiYXNlZCBvbiBnb29nbGUKICAgICMgc2Nob2xhciBpZHMKCn0KIyBOb3RpY2UgaG93IGZhc3QgdGhlIGRhdGEgYmxvdyB1cCEgVGhlIDM0IFJVIHNvY2lvbG9neSBzY2hvbGFycyBwdWJsaXNoIH4zMDAwIHBhcGVycwpzb2NfZGZfcHVibGljYXRpb25zIDwtIGJpbmRfcm93cyhzb2NfbGlzdF9wdWJsaWNhdGlvbnMpCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQpzb2NfcHJvZmlsZXNfZGYgPC0gbGlzdCgpCmZvciAoaSBpbiAxOmxlbmd0aChzb2NfbGlzdF9wcm9maWxlcykpIHsKICAgICMgc29jX3Byb2ZpbGVzX2RmW1tpXV0gPC0gZGF0YS5mcmFtZSh0KHVubGlzdChzb2NfbGlzdF9wcm9maWxlc1tbaV1dWzE6OF0pKSkgI3NvbWUgYW5ueW9pbmcKICAgICMgZGF0YSBoYW5kbGluZwogICAgc29jX3Byb2ZpbGVzX2RmW1tpXV0gPC0gdW5saXN0KHNvY19saXN0X3Byb2ZpbGVzW1tpXV1bMTo4XSkKICAgIHNvY19wcm9maWxlc19kZltbaV1dIDwtIGRhdGEuZnJhbWUoc29jX3Byb2ZpbGVzX2RmW1tpXV0pCiAgICBzb2NfcHJvZmlsZXNfZGZbW2ldXSA8LSBkYXRhLmZyYW1lKHQoc29jX3Byb2ZpbGVzX2RmW1tpXV0pKQoKfQpzb2NfcHJvZmlsZXNfZGYgPC0gYmluZF9yb3dzKHNvY19wcm9maWxlc19kZikKc29jX2RmIDwtIGxlZnRfam9pbihzb2NfZGYsIHNvY19wcm9maWxlc19kZiwgYnkgPSBjKGdzX2lkID0gImlkIikpICAjIG1lcmdlIGRhdGEgd2l0aCBzb2NfZGYKc29jX2RmICAjIG5vdGljZSBhbGwgdGhlIG5ldyBpbmZvcm1hdGlvbiB3ZSB3ZXJlIGFibGUgdG8gZ2V0IGZyb20gdGhlIHNjaG9sYXIgcHJvZmlsZXMhCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CiMgZ2V0IGNpdGF0aW9uIGhpc3Rvcnkgb2YgYSBzY2hvbGFyCnNvY19zdGFmZl9jaXQgPC0gbGlzdCgpCmZvciAoaSBpbiAxOm5yb3coc29jX2RmKSkgewoKICAgIHNvY19zdGFmZl9jaXRbW2ldXSA8LSBnZXRfY2l0YXRpb25faGlzdG9yeShzb2NfZGZbaSwgYygiZ3NfaWQiKV0pCgogICAgaWYgKG5yb3coc29jX3N0YWZmX2NpdFtbaV1dKSA+IDApIHsKICAgICAgICBzb2Nfc3RhZmZfY2l0W1tpXV1bLCBjKCJnc19pZCIpXSA8LSBzb2NfZGZbaSwgYygiZ3NfaWQiKV0gICMgYWdhaW4gYXR0YWNoIHRoZSBnc19pZCBhcyB0aGlyZCBjb2x1bW4KICAgIH0KfQpzb2Nfc3RhZmZfY2l0IDwtIGJpbmRfcm93cyhzb2Nfc3RhZmZfY2l0KQpjb2xuYW1lcyhzb2Nfc3RhZmZfY2l0KVszXSA8LSAiZ3NfaWQiCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQpyZXF1aXJlKHJ2ZXN0KQpyZXF1aXJlKHhtbDIpCnJlcXVpcmUodGlkeXZlcnNlKQoKIyBmdW5jdGlvbiB0byBnZXQgY29sbGFib3JhdG9ycyBhbmQgbmFtZXMgZnJvbSBHUyBwcm9maWxlcwpmY29sbGFicyA8LSBmdW5jdGlvbihnc2lkLCBsb29rZm9yY29sbGFicykgewoKICBodG1scGFnZTEgPC0gcmVhZF9odG1sKHBhc3RlMCgiaHR0cHM6Ly9zY2hvbGFyLmdvb2dsZS5ubC9jaXRhdGlvbnM/dXNlcj0iLCBnc2lkLCAiJmhsPWVuIikpICMgc28gd2UgcGFzdGUgdGhlIGdvb2dsZSBzY2hvbGFyIGlkCiAgcHJvZmlsZW5hbWUgPC0gaHRtbHBhZ2UxICU+JSBodG1sX25vZGVzKHhwYXRoID0gIi8vKi9kaXZbQGlkPSdnc2NfcHJmX2luJ10iKSAlPiUgaHRtbF90ZXh0KCkgIyB3ZSBleHRyYWN0IHRoZSBwcm9maWxlIG5hbWUgb2YgdGhhdCBnb29nbGUgc2Nob2xhciBwYWdlCiAgcHJvZmlsZWNvbGxhYnMxIDwtIGFzLmRhdGEuZnJhbWUoMCkgIyBlbXB0eSBkZiBuZWNlc3NhcnkgZm9yIGxhdGVyCiAgcHJvZmlsZWNvbGxhYnMyIDwtIGFzLmRhdGEuZnJhbWUoMCkgIyBlbXB0eSBkZiBuZWNlc3NhcnkgZm9yIGxhdGVyCgogIGlmIChsb29rZm9yY29sbGFicyA9PSAxKSB7ICMgc28gaWYgeW91IHdhbnQgdG8gbG9vayBmb3IgY29sbGFicywgc2V0IGZ1bmN0aW9uIHRvIDEKCiAgICBodG1scGFnZTIgPC0gcmVhZF9odG1sKHBhc3RlMCgiaHR0cHM6Ly9zY2hvbGFyLmdvb2dsZS5jb20vY2l0YXRpb25zP3ZpZXdfb3A9bGlzdF9jb2xsZWFndWVzJmhsPWVuJnVzZXI9IiwgZ3NpZCkpICMgc28gd2UgcGFzdGUgdGhlIGdvb2dsZSBzY2hvbGFyIGlkCiAgICBwcm9maWxlY29sbGFiczEgPC0gIGh0bWxwYWdlMiAlPiUgaHRtbF9ub2Rlcyhjc3M9ImgzIikgJT4lIGh0bWxfdGV4dCgpICMgZ2V0IG5hbWVzCiAgICBwcm9maWxlY29sbGFiczEgPC0gIGFzLmRhdGEuZnJhbWUocHJvZmlsZWNvbGxhYnMxKQoKICAgIHByb2ZpbGVjb2xsYWJzMiA8LSBodG1scGFnZTIgJT4lIGh0bWxfbm9kZXMoImEiKSAlPiUgaHRtbF9hdHRyKCJocmVmIikgIyBnZXQgdGhlIGxpbmsKICAgIHByb2ZpbGVjb2xsYWJzMiA8LSBwcm9maWxlY29sbGFiczJbc2VxX2Fsb25nKHByb2ZpbGVjb2xsYWJzMikgJSUgMiA+IDBdCiAgICBwcm9maWxlY29sbGFiczIgPC0gc3Vic3RyaW5nKHByb2ZpbGVjb2xsYWJzMiwgMjMpCgogIH0KICBpZiAobnJvdyhwcm9maWxlY29sbGFiczEpPjEpIHsgIyBpZiB0aGVyZSBBUkUgY29sbGFicwoKICAgIHByb2ZpbGVjb2xsYWJzMSA8LSBhcy5kYXRhLmZyYW1lKHByb2ZpbGVjb2xsYWJzMSkgIyB3ZSB3YW50IHRvLi4uCiAgICBwcm9maWxlY29sbGFiczIgPC0gIGFzLmRhdGEuZnJhbWUocHJvZmlsZWNvbGxhYnMyKQogICAgcHJvZmlsZWNvbGxhYnMxWyxjKCJjb2F1dGhfaWQiKV0gPC0gcHJvZmlsZWNvbGxhYnMyWywxXQoKICAgIHByb2ZpbGVjb2xsYWJzMVssYygiZ3NfaWQiKV0gPC0gZ3NpZCAjLi4uIGFkZCBnc19pZHMgb2YgZm9jYWwgR1MgcHJvZmlsZQogICAgcHJvZmlsZWNvbGxhYnMxWyxjKCJuYW1lIildIDwtIHByb2ZpbGVuYW1lICMuLi5hbmQgdGhlIHRoZSBwcm9maWxlIG5hbWUgb2YgR1MgcHJvZmlsZSBhdHRhY2hlZAoKICAgIG5hbWVzKHByb2ZpbGVjb2xsYWJzMSlbMV0gPC0gImNvYXV0aCIKCiAgfSBlbHNlIHsKICAgIHByb2ZpbGVjb2xsYWJzMSA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKGdzaWQsIHByb2ZpbGVuYW1lKSkgIyBpZiBOT1QgbG9va2luZyBmb3IgY29sbGFicy4uLgogICAgbmFtZXMocHJvZmlsZWNvbGxhYnMxKSA8LSBjKCJnc19pZCIsICJuYW1lIikgIy4uLndlIG9ubHkgYXR0YWNoIGdzX2lkIGFuZCBwcm9maWxlbmFtZQoKICB9CiAgcmV0dXJuKHByb2ZpbGVjb2xsYWJzMSkKCn0KCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQojIGZpcnN0IHRoZSBzb2MgY29sbGFib3JhdG9ycyBub3RlIGhvdyB3ZSBhbHJlYWR5IGJ1aWxkIGEgZnVuY3Rpb24gKGZjb2xsYWJzKCkpIGZvciB5b3UgeW91IG5lZWQgdG8KIyBpbnB1dCBhIGdvb2dsZSBzY2hvbGFyIGlkIGFuZCBhIDEgKGlmIHlvdSB3YW50IHRvIGZpbmQgY29sbGFicykgb3IgMCAob25seSBleHRyYWN0aW5nIG5hbWVzKQojIGZjb2xsYWJzIC0tPiB5b3UgY2FuIGNoZWNrIGl0IG91dCBpZiB5b3UncmUgaW50ZXJlc3RlZApzb2NfY29sbGFicyA8LSBsaXN0KCkKZm9yIChpIGluIDE6bnJvdyhzb2NfZGYpKSB7CgogICAgdGltZSA8LSBydW5pZigxLCAwLCAxKQogICAgU3lzLnNsZWVwKHRpbWUpCgogICAgc29jX2NvbGxhYnNbW2ldXSA8LSBmY29sbGFicyhzb2NfZGZbaSwgYygiZ3NfaWQiKV0sIDEpCgp9CnNvY19jb2xsYWJzIDwtIGJpbmRfcm93cyhzb2NfY29sbGFicykgICMgYmluZCByb3dzLCBnZXQgdGhlIHVuaXF1ZSBvbmVzIQpzb2NfY29sbGFic191bmlxdWUgPC0gdW5pcXVlKHNvY19jb2xsYWJzWywgImNvYXV0aF9pZCJdKSAgIyBzbyAyMjkgdW5pcXVlIGNvbGxhYm9yYXRvcnMgZm9yIFJVIHN0YWZmPwpzb2NfY29sbGFic191bmlxdWUgPC0gc29jX2NvbGxhYnNfdW5pcXVlWyFpcy5uYShzb2NfY29sbGFic191bmlxdWUpXQpzYXZlKHNvY19jb2xsYWJzLCBmaWxlID0gImFkZGZpbGVzXFxzb2NfZGZfY29sbGFiczEuUkRhdGEiKSAgIyB5b3Ugbm90aWNlIHRoaXMgdGFrZXMgYSB3aGlsZSwgc28gd2Ugc2F2ZSB0aGUgZGF0YSBoZXJlLgpgYGAKCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyB0aGVuIHRoZSBuYW1lcyBvZiB0aG9zZSBjb2xsYWJvcmF0b3JzIHBsdXMgVEhFSVIgY29sbGFib3JhdG9ycyB1bmRlcnN0YW5kIHRoYXQgd2UgZG9uJ3QgaGF2ZQojIG5hbWVzIG9mIHRoZW0geWV0IGZyb20gdGhlIGNvZGUgYWJvdmU/CmNvbGxhYnNfMWRlZXAgPC0gbGlzdCgpCmZvciAoaSBpbiAxOmxlbmd0aChzb2NfY29sbGFic191bmlxdWUpKSB7CgogICAgdGltZSA8LSBydW5pZigxLCAwLCAzKQogICAgU3lzLnNsZWVwKHRpbWUpCgogICAgaWYgKCFzb2NfY29sbGFic191bmlxdWVbaV0gJWluJSBzb2NfZGYkZ3NfaWQpIHsKICAgICAgICBjb2xsYWJzXzFkZWVwW1tpXV0gPC0gZmNvbGxhYnMoc29jX2NvbGxhYnNfdW5pcXVlW2ldLCAxKQoKICAgIH0KfQpjb2xsYWJzXzFkZWVwIDwtIGJpbmRfcm93cyhjb2xsYWJzXzFkZWVwKQpjb2xsYWJzXzFkZWVwX3VuaXF1ZSA8LSB1bmlxdWUoY29sbGFic18xZGVlcFssIDJdKQpjb2xsYWJzXzFkZWVwX3VuaXF1ZSA8LSBjb2xsYWJzXzFkZWVwX3VuaXF1ZVshaXMubmEoY29sbGFic18xZGVlcF91bmlxdWUpXQpzYXZlKGNvbGxhYnNfMWRlZXAsIGZpbGUgPSAiYWRkZmlsZXNcXHNvY19jb2xsYWJzMi5SRGF0YSIpICAjIHlvdSBub3RpY2UgdGhpcyB0YWtlcyBhIHdoaWxlLCBzbyB3ZSBzYXZlIHRoZSBkYXRhIGhlcmUuCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CmZvciAoaSBpbiBjKCJfdWt5dFFZQUFBQUoiLCAibGtWcTMyc0FBQUFKIiwgInAzSXd0VDRBQUFBSiIpKSB7CiAgICAjIGRyb3AgZ29vZ2xlIHNjaG9sYXIgaWRzIHRoYXQgbG9vayBzdXNwaWNpb3VzbHkgcHJvZHVjdGl2ZQogICAgc29jX2RmIDwtIHNvY19kZlshc29jX2RmJGdzX2lkID09IGksIF0KICAgIHNvY19kZl9wdWJsaWNhdGlvbnMgPC0gc29jX2RmX3B1YmxpY2F0aW9uc1shKHNvY19kZl9wdWJsaWNhdGlvbnMkZ3NfaWQgPT0gaSksIF0KICAgIHNvY19zdGFmZl9jaXQgPC0gc29jX3N0YWZmX2NpdFshKHNvY19zdGFmZl9jaXQkZ3NfaWQgPT0gaSksIF0KICAgIHNvY19jb2xsYWJzIDwtIHNvY19jb2xsYWJzWyEoc29jX2NvbGxhYnMkZ3NfaWQgPT0gaSksIF0KfQpgYGAKCiMgR2VuZGVyIAoKCmBgYHtyLCBldmFsPUZBTFNFfQpmZ2VuZGVyIDwtIGZ1bmN0aW9uKGZpcnN0bmFtZV9kZiwgbWUsIGZpbGU9TlVMTCkgewoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgQXV0aG9yOiBCYXMgSG9mc3RyYSwgQW5uZSBNYWFpa2UgTXVsZGVycywgSm9jaGVtIFRvbHNtYQojIERBdGU6ICAgMTMtMTAtMjAyMSwgbGFzdCBlZGl0OiAyMi0wOS0yMDIyCiMgVGFza3M6ICAtIGFzc2lnbiBnZW5kZXIgYmFlZCBvbiBuYW1lCiMgICAgICAgICAtIEFkYXB0ZWQgZnJvbSBSZW5zZSBDb3J0ZW4gY29kZSBBcHJpbCAyMDIxCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKCiNJbnB1dDogCiMgIC0gZmlyc3RuYW1lX2RmOiBhIGRhdGEuZnJhbWUgd2l0aCBhIGNvbHVtbiBuYW1lZCBmaXJzdG5hbWUgIAojICAtIG1lOiBhIGNoYXJhY3RlciB2ZWN0b3IgaW50cm9kdWNpbmcgeW91cnNlbGY6IGUuZy4gIkogVG9sc21hLCBSYWRib3VkIFVuaXZlcnNpdHkiCiMgIC0gZmlsZTogbG9jYXRpb24gYW5kIG5hbWUgb2YgZmlsZSB0byBiZSBzYXZlZC4gCiAgCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBMb2FkIHJlcXVpcmVkIHBhY2thZ2VzCgppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQogIGxpYnJhcnkodGlkeXZlcnNlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCn0KCmlmICghcmVxdWlyZSgicnZlc3QiLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygicnZlc3QiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQogIGxpYnJhcnkocnZlc3QsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkKfQoKaWYgKCFyZXF1aXJlKCJwb2xpdGUiLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygicG9saXRlIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICBsaWJyYXJ5KHBvbGl0ZSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQp9CgoKCiMgbWFrZSBsaW5rcyB0byBzY3JhcGUKZmlyc3RuYW1lX2RmJG5hbWVfdXJsIDwtIHBhc3RlMCgiaHR0cHM6Ly93d3cubWVlcnRlbnMua25hdy5ubC9udmIvbmFhbS9pcy8iLCBmaXJzdG5hbWVfZGZbLCBjKCJmaXJzdG5hbWUiKV0pCgoKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIDI6IGludHJvZHVjZSB0byBzZXJ2ZXIgIyMjCgojIEludHJvZHVjZSBteXNlbGYgdG8gdGhlIHNlcnZlcgpzZXNzaW9uIDwtIGJvdygiaHR0cHM6Ly93d3cubWVlcnRlbnMua25hdy5ubC9udmIvbmFhbS9pcyIsIHVzZXJfYWdlbnQgPSBtZSAsIGRlbGF5ID0gMSkKCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIyAzOiBtYWtlIGZ1bmN0aW9uIHRvIGdldCB0YWJsZSBmcm9tICMjIwogIGZuYW1lcyA8LSBmdW5jdGlvbihsaW5rKXsgCiAgICBuYW1lX3Nlc3Npb24gPC1ub2Qoc2Vzc2lvbiwgcGF0aCA9IGxpbmspCiAgICBuYW1lX3BhZ2UgPC0gc2NyYXBlKG5hbWVfc2Vzc2lvbikgCiAgICByZXR1cm4obmFtZV9wYWdlKQogIH0KICAKbmFtZV9saXN0IDwtIGxpc3QoKQp0YWJsZV9saXN0IDwtIGxpc3QoKQpmaXJzdG5hbWVfZGYkZ2VuZGVyIDwtIE5BCgogIGZvciAoaSBpbiAxOm5yb3coZmlyc3RuYW1lX2RmKSkgewogICAgcHJpbnQoaSkKICAgIG5hbWVfbGlzdFtbaV1dIDwtIGZuYW1lcyhmaXJzdG5hbWVfZGZbaSwgYygibmFtZV91cmwiKV0pCiAgICAjIGV4dHJhY3QgbmFtZSBmcmVxdWVuY3kgdGFibGUgYW5kIGdlbmRlciBpbmZvCiAgICB0YWJsZV9saXN0W1tpXV0gPC0gbmFtZV9saXN0W1tpXV0gJT4lIGh0bWxfdGFibGUoKQogICAgCiAgICB0YWJsZV9saXN0W1tpXV1bWzFdXVt0YWJsZV9saXN0W1tpXV1bWzFdXT09Ii0tIl0gPC0gIjAiCiAgICAKICAgIGlmIChhcy5udW1lcmljKHRhYmxlX2xpc3RbW2ldXVtbMV1dJFgzWzJdKSA+IGFzLm51bWVyaWModGFibGVfbGlzdFtbaV1dW1sxXV0kWDNbNl0pKSB7CiAgICAgIGZpcnN0bmFtZV9kZiRnZW5kZXJbaV0gPC0gIm1hbGUiIH0gZWxzZSB7CiAgICAgICAgZmlyc3RuYW1lX2RmJGdlbmRlcltpXSA8LSAiZmVtYWxlIgogICAgICB9CiAgICAKICAgIGlmICghaXMubnVsbChmaWxlKSkgKHNhdmUoZmlyc3RuYW1lX2RmLCBmaWxlPWZpbGUpKQogICAgCiAgICB9CiAgcmV0dXJuKGZpcnN0bmFtZV9kZikKfQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpzb2NfZGYgJT4lIG11dGF0ZShmaXJzdG5hbWU9Zmlyc3RfbmFtZSkgLT4gc29jX2RmCgpzb2NfZGYkZmlyc3RuYW1lCnNvY19kZiA8LSBmZ2VuZGVyKHNvY19kZiwgbWU9IkpvY2hlbSBUb2xzbWEsIFJVL1JVRyIpCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQpzYXZlKHNvY19kZiwgZmlsZT0ic29jX2RmX3MyLlJEYXRhIikKYGBgCgojIEV0aG5pY2l0eQoKYGBge3IsIGV2YWw9RkFMU0V9CmxvYWQoIi9Vc2Vycy9hbnVzY2hrYS9Eb2N1bWVudHMvbGFiam91cm5hbC9zb2NfZGYyMi5SRGF0YSIpCnNvY19kZiAlPiUgbXV0YXRlKGxhc3RuYW1lPWxhc3RfbmFtZSkgLT4gc29jX2RmCmxhc3RuYW1lX2RmIDwtIHNvY19kZgpgYGAKCldlIG1vZXRlbiBkZSBuYW1lbiBpbiBoZXQganVpc3RlIGZvcm1hdCB6ZXR0ZW4uIAoKRHVzIG5pZXQgInZhbiBkZXIgbWVlciIsIG1hYXI6ICJtZWVyLCB2YW4gZGVyIi4gCgpEYW4gdmVyd2lqZGVyIGlrIGR1YmJlbGUgYWNodGVybmFtZW4uIApgYGB7ciwgZXZhbD1GQUxTRX0KI3Zvb3J2b2Vnc2VscyBjb3JyZWN0IHpldHRlbiB2b29yIHNjcmFwZXIKdm9vcnZvZWdzZWxzIDwtIGMoIid0ICIsICJkJyAiLCAiZGUgIiwgImRlIGxhICIsICJkZW4gIiwgImRlbCAiLCAiZGVyICIsICJkZXMgIiwgImVsICIsICJlbC0gIiwgImluICd0ICIsICJsYSAiLCAibGUgIiwgImxlcyAiLCAib3AgZGVuICIsICJ0ZW4gIiwgInRlciAiLCAidGVzICIsICJ2YW4gIiwgInZhbiAndCAiLCAidmFuIGRlICIgLCAidmFuIGRlciAiLCAidmFuIGRlbiAiLCAidm9uIGRlciAiLCAib3AgZGVuICIsICJ1bCAiKSAKCmZvciAoaSBpbiAxOiBsZW5ndGgobGFzdG5hbWVfZGYkbGFzdG5hbWUpKSB7CiAgaWYgKHN1bShzdHJfZGV0ZWN0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCB2b29ydm9lZ3NlbHMpKT4wKSB7CiAgICBsYXN0IDwtICBhcy5jaGFyYWN0ZXIoc3RyX3NwbGl0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCBwYXR0ZXJuPSIgIiwgc2ltcGxpZnkgPSBUUlVFKSkKICAgIGxhc3QgPC0gbGFzdFtsZW5ndGgobGFzdCldCiAgICBmaXJzdCA8LSBhcy5jaGFyYWN0ZXIodW5saXN0KHN0cnNwbGl0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCBzcGxpdD1sYXN0LCBmaXhlZD1UUlVFKSkpCiAgICBsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSA8LSBwYXN0ZShsYXN0LCAiLCAiLCBmaXJzdCwgc2VwPSIiKQogIH0KfQoKI2R1YmJlbGUgbmFtZW4gdmVyd2lqZGVyZW4uIGxldCBvcCBkdWJiZWxlIG5hbWVuIG1ldCB2b29ydm9lZ3NlbCB3b3JkZW4gbmlldCBnZWNsZWFuZWQuIFRPIERPIApmb3IgKGkgaW4gMTogbGVuZ3RoKGxhc3RuYW1lX2RmJGxhc3RuYW1lKSkgewogIGlmICghc3VtKHN0cl9kZXRlY3QobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHZvb3J2b2Vnc2VscykpPjApIHsKICAgIGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldIDwtIGFzLmNoYXJhY3RlcihzdHJfc3BsaXQobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHBhdHRlcm49IiAiLCBuID0gMiwgc2ltcGxpZnkgPSBUUlVFKVssMV0pCiAgICBsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSA8LSBhcy5jaGFyYWN0ZXIoc3RyX3NwbGl0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCBwYXR0ZXJuPSItIiwgbiA9IDIsIHNpbXBsaWZ5ID0gVFJVRSlbLDFdKQogIH0KfQoKbGFzdG5hbWVfZGYkbGFzdG5hbWU8LSB0cmltd3MobGFzdG5hbWVfZGYkbGFzdG5hbWUsIHdoaWNoID0gYygicmlnaHQiKSwgd2hpdGVzcGFjZSA9ICJbIFx0XHJcbl0iKQpsYXN0bmFtZV9kZiRsYXN0bmFtZSA8LSBzdHJfcmVwbGFjZV9hbGwobGFzdG5hbWVfZGYkbGFzdG5hbWUsICIgIiwgIiUyMCIpICNodG1sIGxpbmtzIGhvdWRlbiBuaWV0IHZhbiBzcGF0aWVzLiAKCmxhc3RuYW1lX2RmJG5wIDwtICIiCmBgYAogCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBMb2FkIHJlcXVpcmVkIHBhY2thZ2VzCgppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQogIGxpYnJhcnkodGlkeXZlcnNlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCn0KCmlmICghcmVxdWlyZSgicnZlc3QiLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygicnZlc3QiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQogIGxpYnJhcnkocnZlc3QsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkKfQoKIyBpZiAoIXJlcXVpcmUoInBvbGl0ZSIsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsKIyAgIGluc3RhbGwucGFja2FnZXMoInBvbGl0ZSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiMgICBsaWJyYXJ5KHBvbGl0ZSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQojIH0KIyAgIAojIGlmICghcmVxdWlyZSgieG1sMiIsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsKIyAgIGluc3RhbGwucGFja2FnZXMoInhtbDIiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQojICAgbGlicmFyeShwb2xpdGUsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkKIyB9CmBgYAoKaGllciBtYWtlbiB3ZSBkZSBsaW5rcyB2b29yIGRlIHdlYnNpdGUuIApgYGB7ciwgZXZhbD1GQUxTRX0KIyBjcmVhdGluZyBVUkxzOiBvcmlnaW4KbGFzdG5hbWVfZGYkbmFtZV9vcmlnaW4gPC0gaWZlbHNlKChsYXN0bmFtZV9kZiRucD09IiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiaHR0cHM6Ly93d3cuY2JnZmFtaWxpZW5hbWVuLm5sL25mYi9kZXRhaWxfbmFhbS5waHA/Z2JhX25hYW09IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5ncjo6c3RyX3RvX3RpdGxlKGxhc3RuYW1lX2RmWywgYygibGFzdG5hbWUiKV0pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiZnYmFfbmFhbT0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdyOjpzdHJfdG9fdGl0bGUobGFzdG5hbWVfZGZbLCBjKCJsYXN0bmFtZSIpXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICImbmZkX25hYW09IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5ncjo6c3RyX3RvX3RpdGxlKGxhc3RuYW1lX2RmWywgYygibGFzdG5hbWUiKV0pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiZpbmZvPWFuYWx5c2UrZW4rdmVya2xhcmluZyZvcGVyYXRvcj1lcSZ0YWFsPSIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoImh0dHBzOi8vd3d3LmNiZ2ZhbWlsaWVuYW1lbi5ubC9uZmIvZGV0YWlsX25hYW0ucGhwP2diYV9uYWFtPSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RuYW1lX2RmWywgYygibnAyIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdyOjpzdHJfdG9fdGl0bGUobGFzdG5hbWVfZGZbLCBjKCJsYXN0bmFtZSIpXSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiJmdiYV9uYWFtPSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RuYW1lX2RmWywgYygibnAyIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdyOjpzdHJfdG9fdGl0bGUobGFzdG5hbWVfZGZbLCBjKCJsYXN0bmFtZSIpXSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiJm5mZF9uYWFtPSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3I6OnN0cl90b190aXRsZShsYXN0bmFtZV9kZlssIGMoImxhc3RuYW1lIildKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiUyQysiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXN0bmFtZV9kZlssIGMoIm5wIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiJmluZm89YW5hbHlzZStlbit2ZXJrbGFyaW5nJm9wZXJhdG9yPWVxJnRhYWw9IikpCmBgYAoKaGllciBzbGFhbiB3ZSBhbGxlcyBvcApgYGB7ciwgZXZhbD1GQUxTRX0KbmFtZV9vcmlnaW5sIDwtIGxpc3QoKQp0YWJsZV9vcmlnaW5sIDwtIGxpc3QoKQp0aW1lIDwtIDAuMQpgYGAKCiMgY3J1Y2ljYWwgc2NyYXBlIGxvb3AKa29tIHZvb3Jsb3BpZyBnZWVuIGZvdXRlbiB0ZWdlbiwgdG9jaCBhbGxlcyBhbHZhc3QgaW4gZWVuIHRyeWNhdGNoIGdlemV0LiAKbGV0IG9wIGRhdCBpayBuaWV0IG5ldGplcyBzY3JhcGUuIGR1cyB6b25kZXIgZnVuY3RpZSAncG9saXRlJyBnZXppZW4gZGUga2xlaW5lIGFhbnRhbGxlbiBpbiBvbnplIGN1cnN1cywgbWFnIGRhdCB3bWIgd2VsLiAKYGBge3IsIGV2YWw9RkFMU0V9Cgpmb3IgKGkgaW4gMTpucm93KGxhc3RuYW1lX2RmKSkgewogIHByaW50KGkpCiAgU3lzLnNsZWVwKHRpbWUpCiAgdHJ5Q2F0Y2goeyAKICAgIG5hbWVfb3JpZ2lubFtbaV1dICA8LSByZWFkX2h0bWwobGFzdG5hbWVfZGZbaSwgYygibmFtZV9vcmlnaW4iKV0pCiAgICB0YWJsZV9vcmlnaW5sW1tpXV0gPC0gbmFtZV9vcmlnaW5sW1tpXV0gJT4lIGh0bWxfdGFibGUoKQogIH0sIAogICAgd2FybmluZyA9IGZ1bmN0aW9uKHcpIHsKICAgICAgICBjYXQoIldBUk5JTkc6IiwgY29uZGl0aW9uTWVzc2FnZSh3KSwgIlxuIikgI1dBUk5JTkcgbWVzc2FnZQogICAgfSwKICAgIGVycm9yPWZ1bmN0aW9uKGUpewogICAgICBlcnIgPC0gY29uZGl0aW9uTWVzc2FnZShlKQogICAgICBjYXQoIkVycm9yOiIsIGNvbmRpdGlvbk1lc3NhZ2UoZSksICJcbiIpICNFUlJPUiBtZXNzYWdlCiAgICB9ICAKICApCn0KYGBgCgplbiB2YW5hZiBoaWVyIGlzIGhldCBlaWdlbmxpamsgYWxsZWVuIG1hYXIgb3BzY2hvbmVuLiAKYGBge3IsIGV2YWw9RkFMU0V9Cm9yaWdpbl90eHQgPC0gbGlzdCgpCmZvciAoaSBpbiAxOmxlbmd0aChuYW1lX29yaWdpbmwpKSB7CiAgICBvcmlnaW5fdHh0IFtbaV1dIDwtIG5hbWVfb3JpZ2lubFtbaV1dICU+JSBodG1sX3RleHQoKSAlPiUgYXMuY2hhcmFjdGVyKCkKfQoKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBHZXQgb3V0IHRoZSByZWxldmFudCBvcmlnaW4gaW5mb3JtYXRpb24gZnJvbSB0aGUgeG1sIGxpc3RzCm9yaWdpbl9sbiA8LSBsaXN0KCkKCmZvciAoaSBpbiAxOmxlbmd0aChuYW1lX29yaWdpbmwpKSB7CiAgb3JpZ2luX2xuW1tpXV0gPC0gbmFtZV9vcmlnaW5sW1tpXV0gJT4lIGh0bWxfbm9kZXMoImRpdiIpICU+JSBydmVzdDo6aHRtbF90ZXh0KCkKICBvcmlnaW5fbG5bW2ldXSA8LSBvcmlnaW5fbG5bW2ldXVtbM11dCn0KCiMgUmVtb3ZlIG1lc3MKZm9yIChpIGluIDE6bGVuZ3RoKG9yaWdpbl9sbikpIHsKICBvcmlnaW5fbG5bW2ldXSA8LSBnc3ViKCJcXHQiLCAiICIsIG9yaWdpbl9sbltbaV1dKQogIG9yaWdpbl9sbltbaV1dIDwtIGdzdWIoIlxcbiIsICIgIiwgb3JpZ2luX2xuW1tpXV0pCn0KCiMgRmxhdHRlbiBuZXN0ZWQgc3RydWN0dXJlIG9mIHRoZSBvcmlnaW4gaW5mb3JtYXRpb24KI29yaWdpbl9sbiA8LSByYmluZChmbGF0dGVuKG9yaWdpbl9sbikpCgpgYGAKCgpgYGB7ciBleHRyYWN0aW5nLXZlcmtsYXJpbmcta2VubWVya2VuLCBldmFsPUZBTFNFfQoKIyBEZXRhY2hpbmcgdGhlIG5hbWVzIGFuZCBvcmlnaW4gaW5mbyBmb3IgZWFzaWVyIGRhdGEgaGFuZGxpbmcKb3JpZ2luIDwtIHVubGlzdChvcmlnaW5fbG4pCgoKCm9yaWdpbiA8LSBzdHJfZXh0cmFjdF9hbGwob3JpZ2luLCAidmFyaWFudGVuKC4qPynCqSIpCgojIE9yaWdpbiBpbmZvcm1hdGlvbiBpcyB1c3VhbGx5IG1lbnRpb25lZCBhZnRlciAidmVya2xhcmluZyIgb3IgImtlbm1lcmtlbiIKb3JpZ2luIDwtIHN0cl9yZW1vdmVfYWxsKG9yaWdpbiwgInZhcmlhbnRlbiIpCm9yaWdpbiA8LSBzdHJfcmVtb3ZlX2FsbChvcmlnaW4sICJDQkcgQnJvbm5lbiIpCm9yaWdpbiA8LSBzdHJfcmVtb3ZlX2FsbChvcmlnaW4sICJjYXRhbG9ndXMiKQpvcmlnaW4gPC0gc3RyX3JlbW92ZV9hbGwob3JpZ2luLCAiwqkiKQoKCnZlcmtsYXJpbmcgPC0gc3RyX3JlbW92ZV9hbGwob3JpZ2luLCAia2VubWVya2VuOiguKj8pJCIpCmtlbm1lcmtlbiA8LSBzdHJfZXh0cmFjdF9hbGwob3JpZ2luLCAia2VubWVya2VuOiguKj8pJCIpCmtlbm1lcmtlbiA8LSBzdHJfcmVtb3ZlX2FsbChrZW5tZXJrZW4sICJzcGVjaWZpZWtlIGNvbXBvbmVudGVuOiguKj8pJCIpCnNjIDwtIHN0cl9leHRyYWN0X2FsbChvcmlnaW4sICJzcGVjaWZpZWtlIGNvbXBvbmVudGVuOiguKj8pJCIpICMgTm90IGRpcmVjdGx5IHJlbGV2YW50IHRvIHVzLCBidXQgZG9lcyBtZWFuIHRoYXQgdGhlIG5hbWUgaGFzIGEgd2VicGFnZQoKCiMgTWFrZSBpbnRvIGEgbmVhdCBkYXRhZnJhbWUgd2l0aCB0aGUgbmFtZXMgYXR0YWNoZWQKdmVya2xhcmluZyA8LSB0cmltd3ModmVya2xhcmluZywgd2hpY2ggPSAiYm90aCIpCmtlbm1lcmtlbiA8LSB0cmltd3Moa2VubWVya2VuLCB3aGljaCA9ICJib3RoIikKc2MgPC0gdHJpbXdzKHNjLCB3aGljaCA9ICJib3RoIikKdmsgPC0gZGF0YS5mcmFtZShzb2NfZGYkbGFzdG5hbWUsIHZlcmtsYXJpbmcsIGtlbm1lcmtlbiwgc2MpCgoKYGBgCgoKIyBTZXBhcmF0aW5nIG5hbWVzIHdpdGggRHV0Y2ggJiB1bmtub3duIG9yaWdpbgpOZXh0LCB3ZSBpZGVudGlmeSB0aG9zZSBuYW1lcyBmb3Igd2hpY2ggbm8gYWRkaXRpb25hbCBpbmZvcm1hdGlvbiB3YXMgZm91bmQuIFRoaXMgaXMgaW1wb3J0YW50IHRvIGRpc3Rpbmd1aXNoIER1dGNoIG5hbWVzIGZyb20gbmFtZXMgd2l0aCB1bmtub3duIG9yaWdpbnMuCgotIER1dGNoIG5hbWVzOiBubyBsYWJlbCBpbmRpY2F0aW5nIHRoYXQgdGhlIG5hbWUgaXMgRHV0Y2gsIGJ1dCBzb21lIG90aGVyIGluZm9ybWF0aW9uIGF2YWlsYWJsZSBvbiBuYW1lIG9yaWdpbgotIFVua25vd24gbmFtZXM6IHdlYiBwYWdlIGNhbm5vdCBiZSBmb3VuZCwgc28gb3JpZ2luIGluZm9ybWF0aW9uIGlzIGVtcHR5LiAKCmBgYHtyIG9yaWdpbi11bmtub3duLCBldmFsPUZBTFNFfQoKIyBJZGVudGlmeSBsYXN0IG5hbWVzIHRoYXQgY291bGQgbm90IGJlIGZvdW5kCnZrIDwtIHZrICU+JQogIG11dGF0ZSh2ZXJrbGFyaW5nID0gaWZlbHNlKHZlcmtsYXJpbmc9PSIiLCAwLCB2ZXJrbGFyaW5nKSwgCiAgICAgICAgIGtlbm1lcmtlbiA9IGlmZWxzZShrZW5tZXJrZW49PSJjaGFyYWN0ZXIoMCkiLCAwLCBrZW5tZXJrZW4pLAogICAgICAgICBzYyA9IGlmZWxzZShzYz09ImNoYXJhY3RlcigwKSIsIDAsIHNjKSwKICAgICAgICAgbm9faW5mbyA9IG5jaGFyKHZlcmtsYXJpbmcpICsgbmNoYXIoa2VubWVya2VuKSArIG5jaGFyKHNjKSkKCnZrIDwtIHZrICU+JQogIG11dGF0ZShub19pbmZvID0gaWZlbHNlKG5vX2luZm89PTMsIDEsIDApLCAKICAgICAgICAgdmVya2xhcmluZyA9IGlmZWxzZSh2ZXJrbGFyaW5nPT0wLCBOQSwgdmVya2xhcmluZyksCiAgICAgICAgIGtlbm1lcmtlbiA9IGlmZWxzZShrZW5tZXJrZW49PTAsIE5BLCBrZW5tZXJrZW4pKQojIElmIHRoZXJlIGlzIG5vIHRleHQgaW4gdmVya2xhcmluZyBvciBrZW5tZXJrZW4sIHRoZSBuYW1lIGNvdWxkIG5vdCBiZSBmb3VuZCBpbiB0aGUgZGF0YWJhc2VzLiAKYGBgCgoKIyBFeHRyYWN0aW5nIHNwZWNpZmljIG9yaWdpbiBpbmZvcm1hdGlvbgpUaGVyZSBhcmUgdGhyZWUgbWFpbiB3YXlzIHRvIGdldCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgb3JpZ2luIG9mIGxhc3QgbmFtZXM6CgoxKSBVbmRlciAia2VubWVya2VuIiwgbGFzdCBuYW1lcyBhcmUgYXNzaWduZWQgY2xpY2thYmxlIHRhZ3MuIFRoZXNlIHRhZ3MgaW5jbHVkZSB1bnNwZWNpZmllZCBmb3JlaWduIG5hbWUgdGFncyAoImFuZGVyZSB0YWFsIiksIGFzIHdlbGwgYXMgc3BlY2lmaWMgZm9yZWlnbiBvcmlnaW5zIG9mIHRoZSBuYW1lICgiRnJhbnNlIG5hYW0iLCAiSW5kaXNjaGUgbmFhbSIpLgotPiBvcmlnaW4xICsgb3JpZ2luNAoKMikgU2V2ZXJhbCBuYW1lcyBoYXZlIG1vcmUgZXh0ZW5zaXZlbHkgd3JpdHRlbiBvdXQgc3RvcmllcyBiZWhpbmQgdGhlIG5hbWUsIHVuZGVyICJ2ZXJrbGFyaW5nIi4gQSBudW1iZXIgb2YgbmFtZXMgY29udGFpbiBkZXRhaWxlZCAoZWl0aGVyIGNvdW50cnktbGV2ZWwgb3IgcmVnaW9uYWwpIG9yaWdpbnMsIHVzdWFsbHkgaW4gdGhlIGZvcm0gb2YgIkRlIG5hYW0gW3h5el0gaXMgYWZrb21zdGlnIHVpdCBbY291bnRyeV0iLiAKLT4gb3JpZ2luMiAKCjMpIFNvbWUgbmFtZXMgaGF2ZSBvcmlnaW4gaW5mb3JtYXRpb24gdW5kZXIgInZlcmtsYXJpbmciIGluIHRoZSBmb3JtIG9mIHRoZSBsaW5ndWlzdGljIG9yaWdpbnMgb2YgdGhlIG5hbWUuIFRoaXMgY2FuIGJlIGNvdW50cnkgc3BlY2lmaWMgKGUuZy4gQ2hpbmVzZSBuYW1lKSwgYnV0IGl0IGNhbiBhbHNvIGFwcGx5IHRvIG11bHRpcGxlIGNvdW50cmllcyB3aGVuIHRoZSBsYW5ndWFnZSBpcyBzcG9rZW4gaW4gbW9yZSB0aGFuIDEgY291bnRyaWVzIChlLmcuIFNwYW5pc2ggbmFtZSkuIAotPiBvcmlnaW4zIAoKYGBge3IgY291bnRyaWVzLWV4dHJhY3QsIGV2YWw9RkFMU0V9CgojIFN0ZXAgMTogZXh0cmFjdGluZyBvcmlnaW4gdGFncyBmcm9tIGtlbm1lcmtlbgp2ayA8LSB2ayAlPiUKICBtdXRhdGUob3JpZ2luMSA9IHN0cl9leHRyYWN0KGtlbm1lcmtlbiwgIls6dXBwZXI6XShbOmxvd2VyOl17Mix9KSBuYWFtIikpCgojIE5vdGU6IHNvbWV0aW1lcyBtdWx0aXBsZSBvcmlnaW5zIGFyZSBtZW50aW9uZWQuIEN1cnJlbnRseSwgSSBvbmx5IGV4dHJhY3QgdGhlIGZpcnN0IG9uZS4gT3RoZXJ3aXNlLCB3ZSBzaG91bGQgdXNlIHN0cl9leHRyYWN0X2FsbC4gCgoKCiMgU3RlcCAyOiBleHRyYWN0aW5nIG9yaWdpbiBpbmZvIGZyb20gdmVya2xhcmluZyAKdmsgPC0gdmsgJT4lCiAgbXV0YXRlKG9yaWdpbjIgPSBpZmVsc2UoYXMubnVtZXJpYyhzdHJfZGV0ZWN0KHZlcmtsYXJpbmcsICJhZmtvbXN0aWcgdWl0IikpID09IDEsIAogICAgICAgICBzdHJfcmVtb3ZlKHZlcmtsYXJpbmcsICIuKmFma29tc3RpZyB1aXQiKSwgTkEpKQoKCiMgU3RlcCAzOiBleHRyYWN0aW5nIGFkZGl0aW9uYWwgb3JpZ2luIGluZm8gZnJvbSB2ZXJrbGFyaW5nCnZrIDwtIHZrICU+JQogIG11dGF0ZShvcmlnaW4zID0gc3RyX2V4dHJhY3QodmVya2xhcmluZywgIls6dXBwZXI6XShbOmxvd2VyOl17Mix9KSAoYWNodGVyKT8oZmFtaWxpZSk/KGJlcm9lcHMpP25hYW0iKSkKCgoKIyBGaW5hbGx5LCB3ZSBjbGVhbiB1cCB0aGUgb3JpZ2luIGluZm9ybWF0aW9uIGV4dHJhY3RlZCBhYm92ZQoKIyBPcmlnaW4xOiBhbHJlYWR5IG5lYXQKdmskb3JpZ2luMSA8LSBzdHJfcmVtb3ZlKHZrJG9yaWdpbjEsICJKb29kc2UgbmFhbSIpICMgY2FuIGJlIER1dGNoICYgbm9uLUR1dGNoCgojIE9yaWdpbjI6IG1lc3N5CnZrJG9yaWdpbjIgPC0gc3RyX3JlbW92ZSh2ayRvcmlnaW4yLCAiXFwuLioiKSAjIHJlbW92ZSBleHRyYSBpbmZvIGluIHRoZSBmb2xsb3dpbmcgc2VudGVuY2UgCnZrJG9yaWdpbjIgPC0gc3RyX3JlbW92ZSh2ayRvcmlnaW4yLCAiXFw7LioiKSAjIHJlbW92ZSBleHRyYSBpbmZvIGluIHRoZSBmb2xsb3dpbmcgc2VudGVuY2UgCnZrJG9yaWdpbjIgPC0gc3RyX3JlbW92ZSh2ayRvcmlnaW4yLCAiXFwoLioiKSAjIHJlbW92ZSBleHRyYSBpbmZvIGluIHRoZSBmb2xsb3dpbmcgc2VudGVuY2UgCgoKdmskZHBnIDwtIGFzLm51bWVyaWMoc3RyX2RldGVjdCh2ayRvcmlnaW4yLCAiKGRvcnApfChwbGFhdHMpfChnZW1lZW50ZSl8KGdyYWFmc2NoYXApfChzdGFkKXwoZGVlbCl8KEZyaWVzbGFuZCkiKSkgIyBvcmlnaW4gaW5mbyB0b28gcmVnaW9uYWwgCnZrIDwtIHZrICU+JSBtdXRhdGUob3JpZ2luMiA9IGlmZWxzZSgoZHBnPT0xKSwgTkEsIG9yaWdpbjIpKSAjIHJlbW92aW5nIHJlZ2lvbmFsIG9yaWdpbiBpbmZvCnZrIDwtIHN1YnNldCh2aywgc2VsZWN0ID0gLWRwZykgIyByZW1vdmluZyBpbnRlcm1lZGlhdGUgdmFyaWFibGUKCiMgU29tZXRpbWVzLCB0aGVyZSB3ZXJlIG11bHRpcGxlIGNvdW50cmllcyBtZW50aW9uZWQuIFRha2Ugb25seSB0aGUgZmlyc3Q6CnZrJG9yaWdpbjIgPC0gc3RyX3JlbW92ZSh2ayRvcmlnaW4yLCAiXFwsLioiKSAjIE9ubHkgZmlyc3QKdmskb3JpZ2luMiA8LSBzdHJfcmVtb3ZlKHZrJG9yaWdpbjIsICJcXHMoZW4pLioiKSAjIE9ubHkgZmlyc3QgCnZrJG9yaWdpbjIgPC0gc3RyX3JlbW92ZSh2ayRvcmlnaW4yLCAiXFxzKG9mKS4qIikgIyBPbmx5IGZpcnN0IAoKCiMgT3JpZ2luMzogcHJldHR5IG5lYXQKdmskb3JpZ2luMyA8LSBzdHJfcmVtb3ZlKHZrJG9yaWdpbjMsICJEKGkpP2UoemUpPyAoZmFtaWxpZSk/KGFjaHRlcik/KGJlcm9lcHMpP25hYW0iKSAjIHNsaXBwZWQgdGhyb3VnaCB0aGUgcmVnZXgKdmskb3JpZ2luMyA8LSBzdHJfcmVtb3ZlKHZrJG9yaWdpbjMsICJFZW4gKGZhbWlsaWUpPyhhY2h0ZXIpPyhiZXJvZXBzKT9uYWFtIikgIyBzbGlwcGVkIHRocm91Z2ggdGhlIHJlZ2V4CnZrJG9yaWdpbjMgPC0gc3RyX3JlbW92ZSh2ayRvcmlnaW4zLCAiWmlqbiAoZmFtaWxpZSk/KGFjaHRlcik/KGJlcm9lcHMpP25hYW0iKSAjIHNsaXBwZWQgdGhyb3VnaCB0aGUgcmVnZXgKdmskb3JpZ2luMyA8LSBzdHJfcmVtb3ZlKHZrJG9yaWdpbjMsICJBbHMgKGZhbWlsaWUpPyhhY2h0ZXIpPyhiZXJvZXBzKT9uYWFtIikgIyBzbGlwcGVkIHRocm91Z2ggdGhlIHJlZ2V4CnZrJG9yaWdpbjMgPC0gc3RyX3JlbW92ZSh2ayRvcmlnaW4zLCAiSm9vZHNlIChmYW1pbGllKT8oYWNodGVyKT9uYWFtIikKdmskb3JpZ2luMyA8LSBzdHJfcmVtb3ZlKHZrJG9yaWdpbjMsICJCaWpiZWxzZSAoZmFtaWxpZSk/KGFjaHRlcik/bmFhbSIpCgoKIyBTZXR0aW5nIGVtcHR5IG9yaWdpbiB2YXJpYWJsZXMgdG8gTkEgKER1dGNoIG9yIHVuZm91bmQgZm9yZWlnbikKdmsgPC0gdmsgJT4lCiAgbXV0YXRlKG9yaWdpbjEgPSBhcy5jaGFyYWN0ZXIoaWZlbHNlKG9yaWdpbjE9PSIifG9yaWdpbjE9PSJjaGFyYWN0ZXIoMCkiLCBOQSwgb3JpZ2luMSkpLAogICAgICAgICBvcmlnaW4yID0gYXMuY2hhcmFjdGVyKGlmZWxzZShvcmlnaW4yPT0iInxvcmlnaW4yPT0iY2hhcmFjdGVyKDApIiwgTkEsIG9yaWdpbjIpKSwKICAgICAgICAgb3JpZ2luMyA9IGFzLmNoYXJhY3RlcihpZmVsc2Uob3JpZ2luMz09IiJ8b3JpZ2luMz09ImNoYXJhY3RlcigwKSIsIE5BLCBvcmlnaW4zKSkpCgoKCiMgRmluYWxseSwgdGhlIHRhZyAiYW5kZXJlIHRhYWwiIHdhcyB1c2VkIHRvIGRpc3Rpbmd1aXNoIGZvcmVpZ24gbmFtZXMgb2YgdW5rbm93biBvcmlnaW4gZnJvbSBrbm93biBEdXRjaCBuYW1lcy4gCnZrIDwtIHZrICU+JQogIG11dGF0ZShvcmlnaW40ID0gaWZlbHNlKChhcy5udW1lcmljKHN0cl9kZXRlY3Qoa2VubWVya2VuLCAiYW5kZXJlIHRhYWwiKSk9PTEpLCAibm9uLUR1dGNoIiwgTkEpKQoKCmBgYAoKSWsgem91IGFsbGVzIHdhYXIgYG5vX2luZm9gIG9wIDEgc3RhYXQgb2Ygd2FhciBgb3JpZ2luNGAgb3AgIm5vbi1EdXRjaCIgc3RhYXQgY29kZXJlbiBhbHMgYnVpdGVubGFuZHMhIAoKCmBgYHtyLCBldmFsPUZBTFNFfQpzb2NfZGYgPC0gY2JpbmQoc29jX2RmLCB2aykKc2F2ZShzb2NfZGYsIGZpbGU9InNvY19kZjIyIikKYGBgCgoKIyBSU2llbmEgZGF0YWZyYW1lCgojIG5ldHdvcmsgYmFzZWQgb24gcHVibGljYXRpb25zCgpOQVRVUkFMTFksIFRISVMgSVMgSlVTVCBBTiBFWEFNUExFLiBmT1IgclNJRU5BLCBZT1UgTkVFRCBBVCBMRUFTVCAzIE5FVFdPUktTLiB0SFVTIFlPVSBIQVZFIFRPIFRXRUFLIFRIRSBQRVJJT0QgQVMgWU9VIERFRU0gRklULiAKCmBgYHtyLCBldmFsPUZBTFNFfQpsaWJyYXJ5KHN0cmluZ3IpCgojZW1wdHkgYWRqYWNlbmN5IG1hdHJpeCBmb3IgdGhlIHllYXJzIDIwMDEtMjAxMApuZXR3b3JrMjAxMF8yMDEzIDwtIG1hdHJpeChOQSwgbnJvdz1ucm93KHNvY19kZiksIG5jb2w9bnJvdyhzb2NfZGYpKQpuZXR3b3JrMjAxNF8yMDE4IDwtIG1hdHJpeChOQSwgbnJvdz1ucm93KHNvY19kZiksIG5jb2w9bnJvdyhzb2NfZGYpKQpuZXR3b3JrMjAxOV8yMDIyIDwtIG1hdHJpeChOQSwgbnJvdz1ucm93KHNvY19kZiksIG5jb2w9bnJvdyhzb2NfZGYpKQoKCiNzZWxlY3QgcHVibGljYXRpb25zIG9mIHRoZSBjb3JyZXNwb25kaW5nIHRpbWUgZXJhCnB1YnNfc2VsIDwtIHNvY19kZl9wdWJsaWNhdGlvbnMgJT4lCiAgICAgICAgICAgICAgbXV0YXRlKGF1dGhvciA9IHRvbG93ZXIoYXV0aG9yKSkgJT4lCiAgICAgICAgICAgICAgZmlsdGVyKHllYXI+PTIwMTAgJiB5ZWFyPD0yMDEzKQojZmlsbCB0aGUgbWF0cml4CmZvciAoZWdvIGluIDE6IG5yb3coc29jX2RmKSkgewogIG5hbWVfZWdvIDwtIHNvY19kZiRsYXN0X25hbWVbZWdvXSAjd2hpY2ggZWdvPyAKICBwdWJzX3NlbDIgPC0gcHVic19zZWxbc3RyX2RldGVjdChwdWJzX3NlbCRhdXRob3IsIG5hbWVfZWdvKSxdICNwdWJsaWNhdGlvbnMgb2YgZWdvCiAgZm9yIChhbHRlciBpbiAxOm5yb3coc29jX2RmKSl7CiAgICBuYW1lX2FsdGVyIDwtIHNvY19kZiRsYXN0X25hbWVbYWx0ZXJdICN3aGljaCBhbHRlcj8gCiAgICBuZXR3b3JrMjAxMF8yMDEzW2VnbyxhbHRlcl0gPC0gYXMubnVtZXJpYyhzdW0oc3RyX2RldGVjdChwdWJzX3NlbDIkYXV0aG9yLCBuYW1lX2FsdGVyKSkgPiAxKSAgI2RpZCBhbHRlciBwdWJsaXNoIHdpdGggZWdvCiAgfQp9Cgojc2VsZWN0IHB1YmxpY2F0aW9ucyBvZiB0aGUgY29ycmVzcG9uZGluZyB0aW1lIGVyYQpwdWJzX3NlbCA8LSBzb2NfZGZfcHVibGljYXRpb25zICU+JQogICAgICAgICAgICAgIG11dGF0ZShhdXRob3IgPSB0b2xvd2VyKGF1dGhvcikpICU+JQogICAgICAgICAgICAgIGZpbHRlcih5ZWFyPj0yMDE0ICYgeWVhcjw9MjAxOCkKI2ZpbGwgdGhlIG1hdHJpeApmb3IgKGVnbyBpbiAxOiBucm93KHNvY19kZikpIHsKICBuYW1lX2VnbyA8LSBzb2NfZGYkbGFzdF9uYW1lW2Vnb10gI3doaWNoIGVnbz8gCiAgcHVic19zZWwyIDwtIHB1YnNfc2VsW3N0cl9kZXRlY3QocHVic19zZWwkYXV0aG9yLCBuYW1lX2VnbyksXSAjcHVibGljYXRpb25zIG9mIGVnbwogIGZvciAoYWx0ZXIgaW4gMTpucm93KHNvY19kZikpewogICAgbmFtZV9hbHRlciA8LSBzb2NfZGYkbGFzdF9uYW1lW2FsdGVyXSAjd2hpY2ggYWx0ZXI/IAogICAgbmV0d29yazIwMTRfMjAxOFtlZ28sYWx0ZXJdIDwtIGFzLm51bWVyaWMoc3VtKHN0cl9kZXRlY3QocHVic19zZWwyJGF1dGhvciwgbmFtZV9hbHRlcikpID4gMSkgI2RpZCBhbHRlciBwdWJsaXNoIHdpdGggZWdvCiAgfQp9Cgojc2VsZWN0IHB1YmxpY2F0aW9ucyBvZiB0aGUgY29ycmVzcG9uZGluZyB0aW1lIGVyYQpwdWJzX3NlbCA8LSBzb2NfZGZfcHVibGljYXRpb25zICU+JQogICAgICAgICAgICAgIG11dGF0ZShhdXRob3IgPSB0b2xvd2VyKGF1dGhvcikpICU+JQogICAgICAgICAgICAgIGZpbHRlcih5ZWFyPj0yMDE5ICYgeWVhcjw9MjAyMikKI2ZpbGwgdGhlIG1hdHJpeApmb3IgKGVnbyBpbiAxOiBucm93KHNvY19kZikpIHsKICBuYW1lX2VnbyA8LSBzb2NfZGYkbGFzdF9uYW1lW2Vnb10gI3doaWNoIGVnbz8gCiAgcHVic19zZWwyIDwtIHB1YnNfc2VsW3N0cl9kZXRlY3QocHVic19zZWwkYXV0aG9yLCBuYW1lX2VnbyksXSAjcHVibGljYXRpb25zIG9mIGVnbwogIGZvciAoYWx0ZXIgaW4gMTpucm93KHNvY19kZikpewogICAgbmFtZV9hbHRlciA8LSBzb2NfZGYkbGFzdF9uYW1lW2FsdGVyXSAjd2hpY2ggYWx0ZXI/IAogICAgbmV0d29yazIwMTlfMjAyMltlZ28sYWx0ZXJdIDwtIGFzLm51bWVyaWMoc3VtKHN0cl9kZXRlY3QocHVic19zZWwyJGF1dGhvciwgbmFtZV9hbHRlcikpID4gMSkgI2RpZCBhbHRlciBwdWJsaXNoIHdpdGggZWdvCiAgfQp9CgpjKGRpbShuZXR3b3JrMjAxMF8yMDEzKSwzKQpuZXRfYXJyYXkgPC0gYXJyYXkoZGF0YSA9IGMobmV0d29yazIwMTBfMjAxMywgbmV0d29yazIwMTRfMjAxOCwgbmV0d29yazIwMTlfMjAyMiksIGRpbT1jKGRpbShuZXR3b3JrMjAxMF8yMDEzKSwzKSkKCm5ldF9hcnJheVsxLDEsMV0KYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0Kc2F2ZShuZXRfYXJyYXksIGZpbGU9InNvY19uZXRfYXJyYXkuUkRhdGEiKQpgYGAKCkkgZGlkbid0IHJ1biB0aGUgbGFzdCBwYXJ0IHlldCB0byBlc3RpbWF0ZSBhIG1vZGVsIGJlY2F1c2UgaXQgd2lsbCBzdGlsbCBiZSBhbHRlcmVkIChtb3JlIHZhcmlhYmxlcykKCg==