1 getting started

#start with clean workspace 
rm(list=ls())
getwd()
#> [1] "C:/Users/ninab/OneDrive/Documenten/GitHub/labjournal"

2 packages

library(data.table) 
library(tidyverse) 
require(xml2)
require(rvest)
require(devtools)
require(scholar)
require(stringi)

3 collect names

3.1 Computer Science!

# Let's first get the staff page read_html is a function that simply extracts html webpages and
# puts them in xml format
cs_staff <- read_html("https://www.cs.ru.nl/staff/index.html")

#head(soc_staff)
#class(soc_staff)
cs_staff <- cs_staff %>%
    rvest::html_nodes("body") %>%
    xml2::xml_find_all("//a") %>%
    rvest::html_text()

3.1.1 Cleaning

Encoding(cs_staff) <- "UTF-8"
cs_staff <- iconv(cs_staff, from="UTF-8", to="LATIN1")

cs_staff <- stri_trans_general(cs_staff, id = "Latin-ASCII")

check <- NA

for (i in 1:length(cs_staff)) {
  check[i] <- str_length(cs_staff[i]) > 1
}

cs_staff2 <- cs_staff[check]
cs_staff <- cs_staff2
cs_df <- data.frame(cs_staff)  
# Last name seems to be everything after ) 
cs_df$last_name <-as.character(str_split(cs_df$cs_staff, pattern="\\)", n = 2, simplify = TRUE)[,2])

# first name is everything between brackets
cs_df$first_name <- as.character(str_extract_all(cs_df$cs_staff, "(?<=\\().+?(?=\\))", simplify = TRUE))
cs_df$first_name <- tolower(cs_df$first_name)  # everything to lower!
cs_df$last_name <- tolower(cs_df$last_name)

cs_df$last_name <- trimws(cs_df$last_name, which = c("both"), whitespace = "[ \t\r\n]")
cs_df$first_name <- trimws(cs_df$first_name, which = c("both"), whitespace = "[ \t\r\n]")

cs_df$first_name <- as.character(str_split(cs_df$first_name, pattern=" ", n = 2, simplify = TRUE)[,1])
cs_df$first_name <- as.character(str_split(cs_df$first_name, pattern="-", n = 2, simplify = TRUE)[,1])

cs_df$last_name
cs_df$last_name <- trimws(cs_df$last_name, which = c("both"), whitespace = "[ \t\r\n]")
cs_df %>% mutate(lastname=last_name) -> cs_df
lastname_df <- cs_df
#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) {
    
    
    if (sum(str_detect(lastname_df$lastname[i], " "))>0) {
      lastname_df$lastname[i] <- tail(as.character(str_split(lastname_df$lastname[i], pattern=" ", n = 2, simplify = TRUE)), 1)
    }
    
    if (sum(str_detect(lastname_df$lastname[i], "-"))>0) {
      lastname_df$lastname[i] <- tail(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]")
cs_df <- lastname_df
cs_df$affiliation <- "RU"
cs_df$field <- "computer science"
save(cs_df, file="./data/cs_df_v20221005.RData")

3.2 Sociology-RU

rm(list=ls())
soc_staff <- read_html("https://www.ru.nl/sociology/research/staff/")
soc_staff <- soc_staff %>%
    rvest::html_nodes("body") %>%
    xml2::xml_find_all("//td") %>%
    rvest::html_text()

3.3 selecting only the odd rowes with the names

fodd <- function(x) {
  #what is x, x is a vector
 x%%2 != 0 
}

nstaf <- length(soc_staff)

soc_names <- soc_staff[fodd(1:nstaf)] 
soc_names <- stri_trans_general(soc_names, id = "Latin-ASCII")
soc_df <- data.frame(soc_names)  

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 <- data.frame(soc_df[-delrows, ])
colnames(soc_df) <- "soc_names"

3.3.1 cleaning

# 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 <- as.character(str_extract_all(soc_df$soc_names, "(?<=\\().+?(?=\\))", simplify = TRUE))

# tussenvoegsel is evertying between last . and first bracket
test <- gsub("\\(.*$", "", soc_df$soc_names)
test <- substr(test, start= regexpr("\\.[^\\.]*$", test) + 2, length(test) ) 
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)
cs_df <- soc_df
cs_df$first_name <- tolower(cs_df$first_name)  # everything to lower!
cs_df$last_name <- tolower(cs_df$last_name)

cs_df$last_name <- trimws(cs_df$last_name, which = c("both"), whitespace = "[ \t\r\n]")
cs_df$first_name <- trimws(cs_df$first_name, which = c("both"), whitespace = "[ \t\r\n]")

cs_df$first_name <- as.character(str_split(cs_df$first_name, pattern=" ", n = 2, simplify = TRUE)[,1])
cs_df$first_name <- as.character(str_split(cs_df$first_name, pattern="-", n = 2, simplify = TRUE)[,1])

cs_df$last_name
cs_df$last_name <- trimws(cs_df$last_name, which = c("both"), whitespace = "[ \t\r\n]")

cs_df$last_name <- paste(test, cs_df$last_name, sep="" )
cs_df %>% mutate(lastname=last_name) -> cs_df
lastname_df <- cs_df
#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 de ", "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) {
    
    
    if (sum(str_detect(lastname_df$lastname[i], " "))>0) {
      lastname_df$lastname[i] <- tail(as.character(str_split(lastname_df$lastname[i], pattern=" ", n = 2, simplify = TRUE)), 1)
    }
    
    if (sum(str_detect(lastname_df$lastname[i], "-"))>0) {
      lastname_df$lastname[i] <- tail(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]")
soc_df <- lastname_df
soc_df$affiliation <- "RU" 
soc_df$field <- "sociology"
colnames(soc_df)[1] <- "names"
save(soc_df, file="./data/soc_df_v20221005.RData")

3.4 Data Science

rm(list=ls())
library(V8)
link <- 'https://www.cs.ru.nl/das/staff/index.html'
namesjs <- read_html(link)  %>% html_nodes('script') %>% html_text()
names <- as.character(namesjs[4])


# first name is everything between brackets
names <- as.character(str_extract_all(names, "\\[(.*?)\\]", simplify = TRUE))

names <- as.character(str_extract_all(names, "\\'(.*?)\\'", simplify = TRUE))

names <- gsub("'", "", names)

names <- names[1:95]

3.4.1 cleaning

names <- stri_trans_general(names, id = "Latin-ASCII")

cs_df <- data.frame(names)

cs_df$first_name <- str_split(cs_df$names, pattern=" ", n = 2, simplify = TRUE)[,1]
cs_df$last_name <- str_split(cs_df$names, pattern=" ", n = 2, simplify = TRUE)[,2]
cs_df$first_name <- tolower(cs_df$first_name)  # everything to lower!
cs_df$last_name <- tolower(cs_df$last_name)

cs_df$last_name <- trimws(cs_df$last_name, which = c("both"), whitespace = "[ \t\r\n]")
cs_df$first_name <- trimws(cs_df$first_name, which = c("both"), whitespace = "[ \t\r\n]")
cs_df %>% mutate(lastname=last_name) -> cs_df
lastname_df <- cs_df
lastname_df$lastname
#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) {
    
    
    if (sum(str_detect(lastname_df$lastname[i], " "))>0) {
      lastname_df$lastname[i] <- tail(as.character(str_split(lastname_df$lastname[i], pattern=" ", n = 2, simplify = TRUE)), 1)
    }
    
    if (sum(str_detect(lastname_df$lastname[i], "-"))>0) {
      lastname_df$lastname[i] <- tail(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]")
ds_df <- lastname_df
ds_df$affiliation <- "RU"
ds_df$field <- "data science"
save(ds_df, file="./data/ds_df_v20221005.RData")

3.5 Sociology - UU

rm(list=ls())
library(RSelenium) # note that this is something new you need to install yourself first 
pjs <- wdman::phantomjs() # never mind this

# Open a connection to browse
dr <- rsDriver(browser = "phantomjs")

# get a browser ready
remdr <- dr[['client']] 

# Go to the site we want to scrape that uses javascript tables
remdr$navigate("https://www.uu.nl/organisatie/sociologie/medewerkers")

tables <- remdr$findElements(using = "xpath", '//*[contains(concat( " ", @class, " " ), concat( " ", "profile", " " ))]')

# now we have the javascript loaded tables and we want to extract some information from that
persons <- list()
for (i in 1:length(tables)) { # so for all the elements (18 persons)
  
  persons[[i]] <- tables[[i]]$getElementText()[[1]] # we want to simply extract the text
  persons[[i]] <- strsplit(persons[[i]], split = "\\\n") # split that string on the "\n" substring, note the escape
  persons[[i]] <- data.frame(t(data.frame(persons[[i]]))) # do some data crunching, nevermind this
  
}
persons <- bind_rows(persons) # bind the rows out of that list
rownames(persons) <- 1:nrow(persons) # rename rows

names <- persons[,1]

names <- gsub("([A-Z]\\.)*", "", names)

names <- tolower(names)
removes <- c("\\(", "\\)", "prof. ", "dr. ", "drs. ", "msc", "bsc", "ir. ", "drs. ")

for (i in 1:length(names)) {
  for (j in 1:length(removes)) {
    names[i] <- gsub(removes[j], "", names[i])
  }
}

names <- stri_trans_general(names, id = "Latin-ASCII")

names <- trimws(names, which = c("both"), whitespace = "[ \t\r\n]")
cs_df <- data.frame(names)

cs_df$first_name <- str_split(cs_df$names, pattern=" ", n = 2, simplify = TRUE)[,1]
cs_df$last_name <- str_split(cs_df$names, pattern=" ", n = 2, simplify = TRUE)[,2]
cs_df$first_name <- tolower(cs_df$first_name)  # everything to lower!
cs_df$last_name <- tolower(cs_df$last_name)

cs_df$last_name <- trimws(cs_df$last_name, which = c("both"), whitespace = "[ \t\r\n]")
cs_df$first_name <- trimws(cs_df$first_name, which = c("both"), whitespace = "[ \t\r\n]")
cs_df %>% mutate(lastname=last_name) -> cs_df
lastname_df <- cs_df
lastname_df$lastname
#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 ", "op de ") 

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) {
    
    
    if (sum(str_detect(lastname_df$lastname[i], " "))>0) {
      lastname_df$lastname[i] <- tail(as.character(str_split(lastname_df$lastname[i], pattern=" ", n = 2, simplify = TRUE)), 1)
    }
    
    if (sum(str_detect(lastname_df$lastname[i], "-"))>0) {
      lastname_df$lastname[i] <- tail(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$first_name <- str_split(lastname_df$first_name, pattern=" ", n = 2, simplify = TRUE)[,1]
lastname_df$first_name <- str_split(lastname_df$first_name, pattern="-", n = 2, simplify = TRUE)[,1]
socuu_df <- lastname_df
socuu_df$affiliation <- "UU"
socuu_df$field <- "sociology"
save(socuu_df, file="./data/socuu_df_v20221005.RData")

4 combine

rm(list=ls())
load("./data/soc_df_v20221005.RData")
load("./data/cs_df_v20221005.RData")
names(cs_df)[1] <- "names"
load("./data/ds_df_v20221005.RData")
load("./data/socuu_df_v20221005.RData")
names_df <- rbind(soc_df, cs_df, ds_df, socuu_df)
id <- 1:nrow(names_df)
names_df <- cbind(id, names_df)
save(names_df, file="./data/names_df_v20221005.RData")
LS0tDQp0aXRsZTogIjEuIG5hbWVzIg0KYXV0aG9yOiAiYnk6IE5pbmEgQnJhbnRlbiINCmJpYmxpb2dyYXBoeTogcmVmZXJlbmNlcy5iaWINCi0tLQ0KDQoNCg0KYGBge3IsIGluc3RhbGwgcmVtb3RlbHksIGdsb2JhbHNldHRpbmdzLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJywgZXZhbD1GQUxTRX0NCmluc3RhbGwucGFja2FnZXMoInJlbW90ZXMiKQ0KcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoInJsZXN1ci9rbGlwcHkiKQ0KYGBgIA0KDQpgYGB7ciwgZ2xvYmFsc2V0dGluZ3MsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkocmdsKQ0KDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpvcHRzX2NodW5rJHNldCh0aWR5Lm9wdHM9bGlzdCh3aWR0aC5jdXRvZmY9MTAwKSx0aWR5PVRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLGNvbW1lbnQgPSAiIz4iLCBjYWNoZT1UUlVFLCBjbGFzcy5zb3VyY2U9YygidGVzdCIpLCBjbGFzcy5vdXRwdXQ9YygidGVzdDIiKSkNCm9wdGlvbnMod2lkdGggPSAxMDApDQpyZ2w6OnNldHVwS25pdHIoKQ0KDQoNCg0KY29sb3JpemUgPC0gZnVuY3Rpb24oeCwgY29sb3IpIHtzcHJpbnRmKCI8c3BhbiBzdHlsZT0nY29sb3I6ICVzOyc+JXM8L3NwYW4+IiwgY29sb3IsIHgpIH0NCg0KYGBgDQoNCmBgYHtyIGtsaXBweSwgZWNobz1GQUxTRSwgaW5jbHVkZT1UUlVFfQ0Ka2xpcHB5OjprbGlwcHkocG9zaXRpb24gPSBjKCd0b3AnLCAncmlnaHQnKSkNCiNrbGlwcHk6OmtsaXBweShjb2xvciA9ICdkYXJrcmVkJykNCiNrbGlwcHk6OmtsaXBweSh0b29sdGlwX21lc3NhZ2UgPSAnQ2xpY2sgdG8gY29weScsIHRvb2x0aXBfc3VjY2VzcyA9ICdEb25lJykNCmBgYA0KDQoNCiMgZ2V0dGluZyBzdGFydGVkDQoNCmBgYHtyfQ0KI3N0YXJ0IHdpdGggY2xlYW4gd29ya3NwYWNlIA0Kcm0obGlzdD1scygpKQ0KZ2V0d2QoKQ0KYGBgDQoNCiMgcGFja2FnZXMNCg0KYGBge3J9DQpsaWJyYXJ5KGRhdGEudGFibGUpIA0KbGlicmFyeSh0aWR5dmVyc2UpIA0KcmVxdWlyZSh4bWwyKQ0KcmVxdWlyZShydmVzdCkNCnJlcXVpcmUoZGV2dG9vbHMpDQpyZXF1aXJlKHNjaG9sYXIpDQpyZXF1aXJlKHN0cmluZ2kpDQoNCmBgYA0KDQojIGNvbGxlY3QgbmFtZXMgIA0KDQojIyBDb21wdXRlciBTY2llbmNlISANCmBgYHtyLCBldmFsPUZBTFNFfQ0KIyBMZXQncyBmaXJzdCBnZXQgdGhlIHN0YWZmIHBhZ2UgcmVhZF9odG1sIGlzIGEgZnVuY3Rpb24gdGhhdCBzaW1wbHkgZXh0cmFjdHMgaHRtbCB3ZWJwYWdlcyBhbmQNCiMgcHV0cyB0aGVtIGluIHhtbCBmb3JtYXQNCmNzX3N0YWZmIDwtIHJlYWRfaHRtbCgiaHR0cHM6Ly93d3cuY3MucnUubmwvc3RhZmYvaW5kZXguaHRtbCIpDQoNCiNoZWFkKHNvY19zdGFmZikNCiNjbGFzcyhzb2Nfc3RhZmYpDQpgYGANCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpjc19zdGFmZiA8LSBjc19zdGFmZiAlPiUNCiAgICBydmVzdDo6aHRtbF9ub2RlcygiYm9keSIpICU+JQ0KICAgIHhtbDI6OnhtbF9maW5kX2FsbCgiLy9hIikgJT4lDQogICAgcnZlc3Q6Omh0bWxfdGV4dCgpDQpgYGANCg0KIyMjIENsZWFuaW5nDQpgYGB7ciwgZXZhbD1GQUxTRX0NCkVuY29kaW5nKGNzX3N0YWZmKSA8LSAiVVRGLTgiDQpjc19zdGFmZiA8LSBpY29udihjc19zdGFmZiwgZnJvbT0iVVRGLTgiLCB0bz0iTEFUSU4xIikNCg0KY3Nfc3RhZmYgPC0gc3RyaV90cmFuc19nZW5lcmFsKGNzX3N0YWZmLCBpZCA9ICJMYXRpbi1BU0NJSSIpDQoNCmNoZWNrIDwtIE5BDQoNCmZvciAoaSBpbiAxOmxlbmd0aChjc19zdGFmZikpIHsNCiAgY2hlY2tbaV0gPC0gc3RyX2xlbmd0aChjc19zdGFmZltpXSkgPiAxDQp9DQoNCmNzX3N0YWZmMiA8LSBjc19zdGFmZltjaGVja10NCmNzX3N0YWZmIDwtIGNzX3N0YWZmMg0KYGBgDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KY3NfZGYgPC0gZGF0YS5mcmFtZShjc19zdGFmZikgIA0KYGBgDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KIyBMYXN0IG5hbWUgc2VlbXMgdG8gYmUgZXZlcnl0aGluZyBhZnRlciApIA0KY3NfZGYkbGFzdF9uYW1lIDwtYXMuY2hhcmFjdGVyKHN0cl9zcGxpdChjc19kZiRjc19zdGFmZiwgcGF0dGVybj0iXFwpIiwgbiA9IDIsIHNpbXBsaWZ5ID0gVFJVRSlbLDJdKQ0KDQojIGZpcnN0IG5hbWUgaXMgZXZlcnl0aGluZyBiZXR3ZWVuIGJyYWNrZXRzDQpjc19kZiRmaXJzdF9uYW1lIDwtIGFzLmNoYXJhY3RlcihzdHJfZXh0cmFjdF9hbGwoY3NfZGYkY3Nfc3RhZmYsICIoPzw9XFwoKS4rPyg/PVxcKSkiLCBzaW1wbGlmeSA9IFRSVUUpKQ0KYGBgDQoNCg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmNzX2RmJGZpcnN0X25hbWUgPC0gdG9sb3dlcihjc19kZiRmaXJzdF9uYW1lKSAgIyBldmVyeXRoaW5nIHRvIGxvd2VyIQ0KY3NfZGYkbGFzdF9uYW1lIDwtIHRvbG93ZXIoY3NfZGYkbGFzdF9uYW1lKQ0KDQpjc19kZiRsYXN0X25hbWUgPC0gdHJpbXdzKGNzX2RmJGxhc3RfbmFtZSwgd2hpY2ggPSBjKCJib3RoIiksIHdoaXRlc3BhY2UgPSAiWyBcdFxyXG5dIikNCmNzX2RmJGZpcnN0X25hbWUgPC0gdHJpbXdzKGNzX2RmJGZpcnN0X25hbWUsIHdoaWNoID0gYygiYm90aCIpLCB3aGl0ZXNwYWNlID0gIlsgXHRcclxuXSIpDQoNCmNzX2RmJGZpcnN0X25hbWUgPC0gYXMuY2hhcmFjdGVyKHN0cl9zcGxpdChjc19kZiRmaXJzdF9uYW1lLCBwYXR0ZXJuPSIgIiwgbiA9IDIsIHNpbXBsaWZ5ID0gVFJVRSlbLDFdKQ0KY3NfZGYkZmlyc3RfbmFtZSA8LSBhcy5jaGFyYWN0ZXIoc3RyX3NwbGl0KGNzX2RmJGZpcnN0X25hbWUsIHBhdHRlcm49Ii0iLCBuID0gMiwgc2ltcGxpZnkgPSBUUlVFKVssMV0pDQoNCmNzX2RmJGxhc3RfbmFtZQ0KY3NfZGYkbGFzdF9uYW1lIDwtIHRyaW13cyhjc19kZiRsYXN0X25hbWUsIHdoaWNoID0gYygiYm90aCIpLCB3aGl0ZXNwYWNlID0gIlsgXHRcclxuXSIpDQpgYGANCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpjc19kZiAlPiUgbXV0YXRlKGxhc3RuYW1lPWxhc3RfbmFtZSkgLT4gY3NfZGYNCmxhc3RuYW1lX2RmIDwtIGNzX2RmDQojdm9vcnZvZWdzZWxzIGNvcnJlY3QgemV0dGVuIHZvb3Igc2NyYXBlcg0Kdm9vcnZvZWdzZWxzIDwtIGMoIid0ICIsICJkJyAiLCAiZGUgIiwgImRlIGxhICIsICJkZW4gIiwgImRlbCAiLCAiZGVyICIsICJkZXMgIiwgImVsICIsICJlbC0gIiwgImluICd0ICIsICJsYSAiLCAibGUgIiwgImxlcyAiLCAib3AgZGVuICIsICJ0ZW4gIiwgInRlciAiLCAidGVzICIsICJ2YW4gIiwgInZhbiAndCAiLCAidmFuIGRlICIgLCAidmFuIGRlciAiLCAidmFuIGRlbiAiLCAidm9uIGRlciAiLCAib3AgZGVuICIsICJ1bCAiKSANCg0KZm9yIChpIGluIDE6IGxlbmd0aChsYXN0bmFtZV9kZiRsYXN0bmFtZSkpIHsNCiAgaWYgKHN1bShzdHJfZGV0ZWN0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCB2b29ydm9lZ3NlbHMpKT4wKSB7DQogICAgbGFzdCA8LSAgYXMuY2hhcmFjdGVyKHN0cl9zcGxpdChsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSwgcGF0dGVybj0iICIsIHNpbXBsaWZ5ID0gVFJVRSkpDQogICAgbGFzdCA8LSBsYXN0W2xlbmd0aChsYXN0KV0NCiAgICBmaXJzdCA8LSBhcy5jaGFyYWN0ZXIodW5saXN0KHN0cnNwbGl0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCBzcGxpdD1sYXN0LCBmaXhlZD1UUlVFKSkpDQogICAgbGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0gPC0gcGFzdGUobGFzdCwgIiwgIiwgZmlyc3QsIHNlcD0iIikNCiAgfQ0KfQ0KDQojZHViYmVsZSBuYW1lbiB2ZXJ3aWpkZXJlbi4gbGV0IG9wIGR1YmJlbGUgbmFtZW4gbWV0IHZvb3J2b2Vnc2VsIHdvcmRlbiBuaWV0IGdlY2xlYW5lZC4gVE8gRE8gDQpmb3IgKGkgaW4gMTogbGVuZ3RoKGxhc3RuYW1lX2RmJGxhc3RuYW1lKSkgew0KICBpZiAoIXN1bShzdHJfZGV0ZWN0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCB2b29ydm9lZ3NlbHMpKT4wKSB7DQogICAgDQogICAgDQogICAgaWYgKHN1bShzdHJfZGV0ZWN0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCAiICIpKT4wKSB7DQogICAgICBsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSA8LSB0YWlsKGFzLmNoYXJhY3RlcihzdHJfc3BsaXQobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHBhdHRlcm49IiAiLCBuID0gMiwgc2ltcGxpZnkgPSBUUlVFKSksIDEpDQogICAgfQ0KICAgIA0KICAgIGlmIChzdW0oc3RyX2RldGVjdChsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSwgIi0iKSk+MCkgew0KICAgICAgbGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0gPC0gdGFpbChhcy5jaGFyYWN0ZXIoc3RyX3NwbGl0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCBwYXR0ZXJuPSItIiwgbiA9IDIsIHNpbXBsaWZ5ID0gVFJVRSkpLCAxKQ0KICAgIH0NCiAgICANCiAgfQ0KfQ0KDQpsYXN0bmFtZV9kZiRsYXN0bmFtZTwtIHRyaW13cyhsYXN0bmFtZV9kZiRsYXN0bmFtZSwgd2hpY2ggPSBjKCJyaWdodCIpLCB3aGl0ZXNwYWNlID0gIlsgXHRcclxuXSIpDQpgYGANCg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmNzX2RmIDwtIGxhc3RuYW1lX2RmDQpjc19kZiRhZmZpbGlhdGlvbiA8LSAiUlUiDQpjc19kZiRmaWVsZCA8LSAiY29tcHV0ZXIgc2NpZW5jZSINCnNhdmUoY3NfZGYsIGZpbGU9Ii4vZGF0YS9jc19kZl92MjAyMjEwMDUuUkRhdGEiKQ0KYGBgDQoNCg0KIyMgU29jaW9sb2d5LVJVDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0Kcm0obGlzdD1scygpKQ0KYGBgDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0Kc29jX3N0YWZmIDwtIHJlYWRfaHRtbCgiaHR0cHM6Ly93d3cucnUubmwvc29jaW9sb2d5L3Jlc2VhcmNoL3N0YWZmLyIpDQpgYGANCg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnNvY19zdGFmZiA8LSBzb2Nfc3RhZmYgJT4lDQogICAgcnZlc3Q6Omh0bWxfbm9kZXMoImJvZHkiKSAlPiUNCiAgICB4bWwyOjp4bWxfZmluZF9hbGwoIi8vdGQiKSAlPiUNCiAgICBydmVzdDo6aHRtbF90ZXh0KCkNCmBgYA0KDQojIyBzZWxlY3Rpbmcgb25seSB0aGUgb2RkIHJvd2VzIHdpdGggdGhlIG5hbWVzDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmZvZGQgPC0gZnVuY3Rpb24oeCkgew0KICAjd2hhdCBpcyB4LCB4IGlzIGEgdmVjdG9yDQogeCUlMiAhPSAwIA0KfQ0KDQpuc3RhZiA8LSBsZW5ndGgoc29jX3N0YWZmKQ0KDQpzb2NfbmFtZXMgPC0gc29jX3N0YWZmW2ZvZGQoMTpuc3RhZildIA0KDQoNCmBgYA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCg0Kc29jX25hbWVzIDwtIHN0cmlfdHJhbnNfZ2VuZXJhbChzb2NfbmFtZXMsIGlkID0gIkxhdGluLUFTQ0lJIikNCg0KYGBgDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0Kc29jX2RmIDwtIGRhdGEuZnJhbWUoc29jX25hbWVzKSAgDQoNCmRlbHJvd3MgPC0gd2hpY2goc29jX2RmJHNvY19uYW1lcyA9PSAiU3RhZmY6IiB8IHNvY19kZiRzb2NfbmFtZXMgPT0gIlBoRDoiIHwgc29jX2RmJHNvY19uYW1lcyA9PSAiRXh0ZXJuYWwgUGhEOiIgfA0KICAgIHNvY19kZiRzb2NfbmFtZXMgPT0gIkd1ZXN0IHJlc2VhcmNoZXJzOiIgfCBzb2NfZGYkc29jX25hbWVzID09ICJPdGhlciByZXNlYXJjaGVyczoiKQ0KDQpzb2NfZGYgPC0gZGF0YS5mcmFtZShzb2NfZGZbLWRlbHJvd3MsIF0pDQpjb2xuYW1lcyhzb2NfZGYpIDwtICJzb2NfbmFtZXMiDQpgYGANCg0KDQojIyMgY2xlYW5pbmcgIA0KYGBge3IsIGV2YWw9RkFMU0V9DQojIExhc3QgbmFtZSBzZWVtcyB0byBiZSBldmVyeXRoaW5nIGJlZm9yZSB0aGUgY29tbWENCnNvY19kZiRsYXN0X25hbWUgPC0gZ3N1YigiLC4qJCIsICIiLCBzb2NfZGYkc29jX25hbWVzKQ0KDQojIGZpcnN0IG5hbWUgaXMgZXZlcnl0aGluZyBiZXR3ZWVuIGJyYWNrZXRzDQpzb2NfZGYkZmlyc3RfbmFtZSA8LSBhcy5jaGFyYWN0ZXIoc3RyX2V4dHJhY3RfYWxsKHNvY19kZiRzb2NfbmFtZXMsICIoPzw9XFwoKS4rPyg/PVxcKSkiLCBzaW1wbGlmeSA9IFRSVUUpKQ0KDQojIHR1c3NlbnZvZWdzZWwgaXMgZXZlcnR5aW5nIGJldHdlZW4gbGFzdCAuIGFuZCBmaXJzdCBicmFja2V0DQp0ZXN0IDwtIGdzdWIoIlxcKC4qJCIsICIiLCBzb2NfZGYkc29jX25hbWVzKQ0KdGVzdCA8LSBzdWJzdHIodGVzdCwgc3RhcnQ9IHJlZ2V4cHIoIlxcLlteXFwuXSokIiwgdGVzdCkgKyAyLCBsZW5ndGgodGVzdCkgKSANCg0KYGBgDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0Kc29jX2RmJGxhc3RfbmFtZSA8LSBnc3ViKCIgSi4gXFwoSmFuc2plXFwpIHZhbiBNU2MiLCAiIiwgc29jX2RmJGxhc3RfbmFtZSkNCnNvY19kZiRmaXJzdF9uYW1lIDwtIHRvbG93ZXIoc29jX2RmJGZpcnN0X25hbWUpICAjIGV2ZXJ5dGhpbmcgdG8gbG93ZXIhDQpzb2NfZGYkbGFzdF9uYW1lIDwtIHRvbG93ZXIoc29jX2RmJGxhc3RfbmFtZSkNCmBgYA0KDQoNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpjc19kZiA8LSBzb2NfZGYNCmNzX2RmJGZpcnN0X25hbWUgPC0gdG9sb3dlcihjc19kZiRmaXJzdF9uYW1lKSAgIyBldmVyeXRoaW5nIHRvIGxvd2VyIQ0KY3NfZGYkbGFzdF9uYW1lIDwtIHRvbG93ZXIoY3NfZGYkbGFzdF9uYW1lKQ0KDQpjc19kZiRsYXN0X25hbWUgPC0gdHJpbXdzKGNzX2RmJGxhc3RfbmFtZSwgd2hpY2ggPSBjKCJib3RoIiksIHdoaXRlc3BhY2UgPSAiWyBcdFxyXG5dIikNCmNzX2RmJGZpcnN0X25hbWUgPC0gdHJpbXdzKGNzX2RmJGZpcnN0X25hbWUsIHdoaWNoID0gYygiYm90aCIpLCB3aGl0ZXNwYWNlID0gIlsgXHRcclxuXSIpDQoNCmNzX2RmJGZpcnN0X25hbWUgPC0gYXMuY2hhcmFjdGVyKHN0cl9zcGxpdChjc19kZiRmaXJzdF9uYW1lLCBwYXR0ZXJuPSIgIiwgbiA9IDIsIHNpbXBsaWZ5ID0gVFJVRSlbLDFdKQ0KY3NfZGYkZmlyc3RfbmFtZSA8LSBhcy5jaGFyYWN0ZXIoc3RyX3NwbGl0KGNzX2RmJGZpcnN0X25hbWUsIHBhdHRlcm49Ii0iLCBuID0gMiwgc2ltcGxpZnkgPSBUUlVFKVssMV0pDQoNCmNzX2RmJGxhc3RfbmFtZQ0KY3NfZGYkbGFzdF9uYW1lIDwtIHRyaW13cyhjc19kZiRsYXN0X25hbWUsIHdoaWNoID0gYygiYm90aCIpLCB3aGl0ZXNwYWNlID0gIlsgXHRcclxuXSIpDQoNCmNzX2RmJGxhc3RfbmFtZSA8LSBwYXN0ZSh0ZXN0LCBjc19kZiRsYXN0X25hbWUsIHNlcD0iIiApDQpgYGANCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpjc19kZiAlPiUgbXV0YXRlKGxhc3RuYW1lPWxhc3RfbmFtZSkgLT4gY3NfZGYNCmxhc3RuYW1lX2RmIDwtIGNzX2RmDQojdm9vcnZvZWdzZWxzIGNvcnJlY3QgemV0dGVuIHZvb3Igc2NyYXBlcg0Kdm9vcnZvZWdzZWxzIDwtIGMoIid0ICIsICJkJyAiLCAiZGUgIiwgImRlIGxhICIsICJkZW4gIiwgImRlbCAiLCAiZGVyICIsICJkZXMgIiwgImVsICIsICJlbC0gIiwgImluICd0ICIsICJsYSAiLCAibGUgIiwgImxlcyAiLCAib3AgZGVuICIsICJ0ZW4gIiwgInRlciAiLCAidGVzICIsICJ2YW4gIiwgInZhbiAndCAiLCAidmFuIGRlICIgLCAidmFuIGRlciAiLCAidmFuIGRlbiAiLCAidm9uIGRlciAiLCAib3AgZGUgIiwgInVsICIpIA0KDQpmb3IgKGkgaW4gMTogbGVuZ3RoKGxhc3RuYW1lX2RmJGxhc3RuYW1lKSkgew0KICBpZiAoc3VtKHN0cl9kZXRlY3QobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHZvb3J2b2Vnc2VscykpPjApIHsNCiAgICBsYXN0IDwtICBhcy5jaGFyYWN0ZXIoc3RyX3NwbGl0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCBwYXR0ZXJuPSIgIiwgc2ltcGxpZnkgPSBUUlVFKSkNCiAgICBsYXN0IDwtIGxhc3RbbGVuZ3RoKGxhc3QpXQ0KICAgIGZpcnN0IDwtIGFzLmNoYXJhY3Rlcih1bmxpc3Qoc3Ryc3BsaXQobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHNwbGl0PWxhc3QsIGZpeGVkPVRSVUUpKSkNCiAgICBsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSA8LSBwYXN0ZShsYXN0LCAiLCAiLCBmaXJzdCwgc2VwPSIiKQ0KICB9DQp9DQoNCiNkdWJiZWxlIG5hbWVuIHZlcndpamRlcmVuLiBsZXQgb3AgZHViYmVsZSBuYW1lbiBtZXQgdm9vcnZvZWdzZWwgd29yZGVuIG5pZXQgZ2VjbGVhbmVkLiBUTyBETyANCmZvciAoaSBpbiAxOiBsZW5ndGgobGFzdG5hbWVfZGYkbGFzdG5hbWUpKSB7DQogIGlmICghc3VtKHN0cl9kZXRlY3QobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHZvb3J2b2Vnc2VscykpPjApIHsNCiAgICANCiAgICANCiAgICBpZiAoc3VtKHN0cl9kZXRlY3QobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sICIgIikpPjApIHsNCiAgICAgIGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldIDwtIHRhaWwoYXMuY2hhcmFjdGVyKHN0cl9zcGxpdChsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSwgcGF0dGVybj0iICIsIG4gPSAyLCBzaW1wbGlmeSA9IFRSVUUpKSwgMSkNCiAgICB9DQogICAgDQogICAgaWYgKHN1bShzdHJfZGV0ZWN0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCAiLSIpKT4wKSB7DQogICAgICBsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSA8LSB0YWlsKGFzLmNoYXJhY3RlcihzdHJfc3BsaXQobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHBhdHRlcm49Ii0iLCBuID0gMiwgc2ltcGxpZnkgPSBUUlVFKSksIDEpDQogICAgfQ0KICAgIA0KICB9DQp9DQoNCmxhc3RuYW1lX2RmJGxhc3RuYW1lPC0gdHJpbXdzKGxhc3RuYW1lX2RmJGxhc3RuYW1lLCB3aGljaCA9IGMoInJpZ2h0IiksIHdoaXRlc3BhY2UgPSAiWyBcdFxyXG5dIikNCmBgYA0KDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0Kc29jX2RmIDwtIGxhc3RuYW1lX2RmDQpzb2NfZGYkYWZmaWxpYXRpb24gPC0gIlJVIiANCnNvY19kZiRmaWVsZCA8LSAic29jaW9sb2d5Ig0KY29sbmFtZXMoc29jX2RmKVsxXSA8LSAibmFtZXMiDQpzYXZlKHNvY19kZiwgZmlsZT0iLi9kYXRhL3NvY19kZl92MjAyMjEwMDUuUkRhdGEiKQ0KYGBgDQoNCiMjIERhdGEgU2NpZW5jZQ0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnJtKGxpc3Q9bHMoKSkNCmBgYA0KDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KbGlicmFyeShWOCkNCmxpbmsgPC0gJ2h0dHBzOi8vd3d3LmNzLnJ1Lm5sL2Rhcy9zdGFmZi9pbmRleC5odG1sJw0KbmFtZXNqcyA8LSByZWFkX2h0bWwobGluaykgICU+JSBodG1sX25vZGVzKCdzY3JpcHQnKSAlPiUgaHRtbF90ZXh0KCkNCm5hbWVzIDwtIGFzLmNoYXJhY3RlcihuYW1lc2pzWzRdKQ0KDQoNCiMgZmlyc3QgbmFtZSBpcyBldmVyeXRoaW5nIGJldHdlZW4gYnJhY2tldHMNCm5hbWVzIDwtIGFzLmNoYXJhY3RlcihzdHJfZXh0cmFjdF9hbGwobmFtZXMsICJcXFsoLio/KVxcXSIsIHNpbXBsaWZ5ID0gVFJVRSkpDQoNCm5hbWVzIDwtIGFzLmNoYXJhY3RlcihzdHJfZXh0cmFjdF9hbGwobmFtZXMsICJcXCcoLio/KVxcJyIsIHNpbXBsaWZ5ID0gVFJVRSkpDQoNCm5hbWVzIDwtIGdzdWIoIiciLCAiIiwgbmFtZXMpDQoNCm5hbWVzIDwtIG5hbWVzWzE6OTVdDQoNCmBgYA0KDQojIyMgY2xlYW5pbmcNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpuYW1lcyA8LSBzdHJpX3RyYW5zX2dlbmVyYWwobmFtZXMsIGlkID0gIkxhdGluLUFTQ0lJIikNCg0KY3NfZGYgPC0gZGF0YS5mcmFtZShuYW1lcykNCg0KY3NfZGYkZmlyc3RfbmFtZSA8LSBzdHJfc3BsaXQoY3NfZGYkbmFtZXMsIHBhdHRlcm49IiAiLCBuID0gMiwgc2ltcGxpZnkgPSBUUlVFKVssMV0NCmNzX2RmJGxhc3RfbmFtZSA8LSBzdHJfc3BsaXQoY3NfZGYkbmFtZXMsIHBhdHRlcm49IiAiLCBuID0gMiwgc2ltcGxpZnkgPSBUUlVFKVssMl0NCmBgYA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmNzX2RmJGZpcnN0X25hbWUgPC0gdG9sb3dlcihjc19kZiRmaXJzdF9uYW1lKSAgIyBldmVyeXRoaW5nIHRvIGxvd2VyIQ0KY3NfZGYkbGFzdF9uYW1lIDwtIHRvbG93ZXIoY3NfZGYkbGFzdF9uYW1lKQ0KDQpjc19kZiRsYXN0X25hbWUgPC0gdHJpbXdzKGNzX2RmJGxhc3RfbmFtZSwgd2hpY2ggPSBjKCJib3RoIiksIHdoaXRlc3BhY2UgPSAiWyBcdFxyXG5dIikNCmNzX2RmJGZpcnN0X25hbWUgPC0gdHJpbXdzKGNzX2RmJGZpcnN0X25hbWUsIHdoaWNoID0gYygiYm90aCIpLCB3aGl0ZXNwYWNlID0gIlsgXHRcclxuXSIpDQoNCmBgYA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmNzX2RmICU+JSBtdXRhdGUobGFzdG5hbWU9bGFzdF9uYW1lKSAtPiBjc19kZg0KbGFzdG5hbWVfZGYgPC0gY3NfZGYNCmxhc3RuYW1lX2RmJGxhc3RuYW1lDQojdm9vcnZvZWdzZWxzIGNvcnJlY3QgemV0dGVuIHZvb3Igc2NyYXBlcg0Kdm9vcnZvZWdzZWxzIDwtIGMoIid0ICIsICJkJyAiLCAiZGUgIiwgImRlIGxhICIsICJkZW4gIiwgImRlbCAiLCAiZGVyICIsICJkZXMgIiwgImVsICIsICJlbC0gIiwgImluICd0ICIsICJsYSAiLCAibGUgIiwgImxlcyAiLCAib3AgZGVuICIsICJ0ZW4gIiwgInRlciAiLCAidGVzICIsICJ2YW4gIiwgInZhbiAndCAiLCAidmFuIGRlICIgLCAidmFuIGRlciAiLCAidmFuIGRlbiAiLCAidm9uIGRlciAiLCAib3AgZGVuICIsICJ1bCAiKSANCg0KZm9yIChpIGluIDE6IGxlbmd0aChsYXN0bmFtZV9kZiRsYXN0bmFtZSkpIHsNCiAgaWYgKHN1bShzdHJfZGV0ZWN0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCB2b29ydm9lZ3NlbHMpKT4wKSB7DQogICAgbGFzdCA8LSAgYXMuY2hhcmFjdGVyKHN0cl9zcGxpdChsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSwgcGF0dGVybj0iICIsIHNpbXBsaWZ5ID0gVFJVRSkpDQogICAgbGFzdCA8LSBsYXN0W2xlbmd0aChsYXN0KV0NCiAgICBmaXJzdCA8LSBhcy5jaGFyYWN0ZXIodW5saXN0KHN0cnNwbGl0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCBzcGxpdD1sYXN0LCBmaXhlZD1UUlVFKSkpDQogICAgbGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0gPC0gcGFzdGUobGFzdCwgIiwgIiwgZmlyc3QsIHNlcD0iIikNCiAgfQ0KfQ0KDQojZHViYmVsZSBuYW1lbiB2ZXJ3aWpkZXJlbi4gbGV0IG9wIGR1YmJlbGUgbmFtZW4gbWV0IHZvb3J2b2Vnc2VsIHdvcmRlbiBuaWV0IGdlY2xlYW5lZC4gVE8gRE8gDQpmb3IgKGkgaW4gMTogbGVuZ3RoKGxhc3RuYW1lX2RmJGxhc3RuYW1lKSkgew0KICBpZiAoIXN1bShzdHJfZGV0ZWN0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCB2b29ydm9lZ3NlbHMpKT4wKSB7DQogICAgDQogICAgDQogICAgaWYgKHN1bShzdHJfZGV0ZWN0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCAiICIpKT4wKSB7DQogICAgICBsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSA8LSB0YWlsKGFzLmNoYXJhY3RlcihzdHJfc3BsaXQobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHBhdHRlcm49IiAiLCBuID0gMiwgc2ltcGxpZnkgPSBUUlVFKSksIDEpDQogICAgfQ0KICAgIA0KICAgIGlmIChzdW0oc3RyX2RldGVjdChsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSwgIi0iKSk+MCkgew0KICAgICAgbGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0gPC0gdGFpbChhcy5jaGFyYWN0ZXIoc3RyX3NwbGl0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCBwYXR0ZXJuPSItIiwgbiA9IDIsIHNpbXBsaWZ5ID0gVFJVRSkpLCAxKQ0KICAgIH0NCiAgICANCiAgfQ0KfQ0KDQpsYXN0bmFtZV9kZiRsYXN0bmFtZTwtIHRyaW13cyhsYXN0bmFtZV9kZiRsYXN0bmFtZSwgd2hpY2ggPSBjKCJyaWdodCIpLCB3aGl0ZXNwYWNlID0gIlsgXHRcclxuXSIpDQpgYGANCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpkc19kZiA8LSBsYXN0bmFtZV9kZg0KZHNfZGYkYWZmaWxpYXRpb24gPC0gIlJVIg0KZHNfZGYkZmllbGQgPC0gImRhdGEgc2NpZW5jZSINCnNhdmUoZHNfZGYsIGZpbGU9Ii4vZGF0YS9kc19kZl92MjAyMjEwMDUuUkRhdGEiKQ0KYGBgDQoNCiMjIFNvY2lvbG9neSAtIFVVDQoNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpybShsaXN0PWxzKCkpDQpgYGANCg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCg0KbGlicmFyeShSU2VsZW5pdW0pICMgbm90ZSB0aGF0IHRoaXMgaXMgc29tZXRoaW5nIG5ldyB5b3UgbmVlZCB0byBpbnN0YWxsIHlvdXJzZWxmIGZpcnN0IA0KcGpzIDwtIHdkbWFuOjpwaGFudG9tanMoKSAjIG5ldmVyIG1pbmQgdGhpcw0KDQojIE9wZW4gYSBjb25uZWN0aW9uIHRvIGJyb3dzZQ0KZHIgPC0gcnNEcml2ZXIoYnJvd3NlciA9ICJwaGFudG9tanMiKQ0KDQojIGdldCBhIGJyb3dzZXIgcmVhZHkNCnJlbWRyIDwtIGRyW1snY2xpZW50J11dIA0KDQojIEdvIHRvIHRoZSBzaXRlIHdlIHdhbnQgdG8gc2NyYXBlIHRoYXQgdXNlcyBqYXZhc2NyaXB0IHRhYmxlcw0KcmVtZHIkbmF2aWdhdGUoImh0dHBzOi8vd3d3LnV1Lm5sL29yZ2FuaXNhdGllL3NvY2lvbG9naWUvbWVkZXdlcmtlcnMiKQ0KDQp0YWJsZXMgPC0gcmVtZHIkZmluZEVsZW1lbnRzKHVzaW5nID0gInhwYXRoIiwgJy8vKltjb250YWlucyhjb25jYXQoICIgIiwgQGNsYXNzLCAiICIgKSwgY29uY2F0KCAiICIsICJwcm9maWxlIiwgIiAiICkpXScpDQoNCiMgbm93IHdlIGhhdmUgdGhlIGphdmFzY3JpcHQgbG9hZGVkIHRhYmxlcyBhbmQgd2Ugd2FudCB0byBleHRyYWN0IHNvbWUgaW5mb3JtYXRpb24gZnJvbSB0aGF0DQpwZXJzb25zIDwtIGxpc3QoKQ0KZm9yIChpIGluIDE6bGVuZ3RoKHRhYmxlcykpIHsgIyBzbyBmb3IgYWxsIHRoZSBlbGVtZW50cyAoMTggcGVyc29ucykNCiAgDQogIHBlcnNvbnNbW2ldXSA8LSB0YWJsZXNbW2ldXSRnZXRFbGVtZW50VGV4dCgpW1sxXV0gIyB3ZSB3YW50IHRvIHNpbXBseSBleHRyYWN0IHRoZSB0ZXh0DQogIHBlcnNvbnNbW2ldXSA8LSBzdHJzcGxpdChwZXJzb25zW1tpXV0sIHNwbGl0ID0gIlxcXG4iKSAjIHNwbGl0IHRoYXQgc3RyaW5nIG9uIHRoZSAiXG4iIHN1YnN0cmluZywgbm90ZSB0aGUgZXNjYXBlDQogIHBlcnNvbnNbW2ldXSA8LSBkYXRhLmZyYW1lKHQoZGF0YS5mcmFtZShwZXJzb25zW1tpXV0pKSkgIyBkbyBzb21lIGRhdGEgY3J1bmNoaW5nLCBuZXZlcm1pbmQgdGhpcw0KICANCn0NCnBlcnNvbnMgPC0gYmluZF9yb3dzKHBlcnNvbnMpICMgYmluZCB0aGUgcm93cyBvdXQgb2YgdGhhdCBsaXN0DQpyb3duYW1lcyhwZXJzb25zKSA8LSAxOm5yb3cocGVyc29ucykgIyByZW5hbWUgcm93cw0KDQpuYW1lcyA8LSBwZXJzb25zWywxXQ0KDQpuYW1lcyA8LSBnc3ViKCIoW0EtWl1cXC4pKiIsICIiLCBuYW1lcykNCg0KbmFtZXMgPC0gdG9sb3dlcihuYW1lcykNCg0KYGBgDQoNCg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCg0KcmVtb3ZlcyA8LSBjKCJcXCgiLCAiXFwpIiwgInByb2YuICIsICJkci4gIiwgImRycy4gIiwgIm1zYyIsICJic2MiLCAiaXIuICIsICJkcnMuICIpDQoNCmZvciAoaSBpbiAxOmxlbmd0aChuYW1lcykpIHsNCiAgZm9yIChqIGluIDE6bGVuZ3RoKHJlbW92ZXMpKSB7DQogICAgbmFtZXNbaV0gPC0gZ3N1YihyZW1vdmVzW2pdLCAiIiwgbmFtZXNbaV0pDQogIH0NCn0NCg0KbmFtZXMgPC0gc3RyaV90cmFuc19nZW5lcmFsKG5hbWVzLCBpZCA9ICJMYXRpbi1BU0NJSSIpDQoNCm5hbWVzIDwtIHRyaW13cyhuYW1lcywgd2hpY2ggPSBjKCJib3RoIiksIHdoaXRlc3BhY2UgPSAiWyBcdFxyXG5dIikNCmBgYA0KDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KDQpjc19kZiA8LSBkYXRhLmZyYW1lKG5hbWVzKQ0KDQpjc19kZiRmaXJzdF9uYW1lIDwtIHN0cl9zcGxpdChjc19kZiRuYW1lcywgcGF0dGVybj0iICIsIG4gPSAyLCBzaW1wbGlmeSA9IFRSVUUpWywxXQ0KY3NfZGYkbGFzdF9uYW1lIDwtIHN0cl9zcGxpdChjc19kZiRuYW1lcywgcGF0dGVybj0iICIsIG4gPSAyLCBzaW1wbGlmeSA9IFRSVUUpWywyXQ0KDQpgYGANCg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCg0KY3NfZGYkZmlyc3RfbmFtZSA8LSB0b2xvd2VyKGNzX2RmJGZpcnN0X25hbWUpICAjIGV2ZXJ5dGhpbmcgdG8gbG93ZXIhDQpjc19kZiRsYXN0X25hbWUgPC0gdG9sb3dlcihjc19kZiRsYXN0X25hbWUpDQoNCmNzX2RmJGxhc3RfbmFtZSA8LSB0cmltd3MoY3NfZGYkbGFzdF9uYW1lLCB3aGljaCA9IGMoImJvdGgiKSwgd2hpdGVzcGFjZSA9ICJbIFx0XHJcbl0iKQ0KY3NfZGYkZmlyc3RfbmFtZSA8LSB0cmltd3MoY3NfZGYkZmlyc3RfbmFtZSwgd2hpY2ggPSBjKCJib3RoIiksIHdoaXRlc3BhY2UgPSAiWyBcdFxyXG5dIikNCg0KYGBgDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KDQpjc19kZiAlPiUgbXV0YXRlKGxhc3RuYW1lPWxhc3RfbmFtZSkgLT4gY3NfZGYNCmxhc3RuYW1lX2RmIDwtIGNzX2RmDQpsYXN0bmFtZV9kZiRsYXN0bmFtZQ0KI3Zvb3J2b2Vnc2VscyBjb3JyZWN0IHpldHRlbiB2b29yIHNjcmFwZXINCnZvb3J2b2Vnc2VscyA8LSBjKCIndCAiLCAiZCcgIiwgImRlICIsICJkZSBsYSAiLCAiZGVuICIsICJkZWwgIiwgImRlciAiLCAiZGVzICIsICJlbCAiLCAiZWwtICIsICJpbiAndCAiLCAibGEgIiwgImxlICIsICJsZXMgIiwgIm9wIGRlbiAiLCAidGVuICIsICJ0ZXIgIiwgInRlcyAiLCAidmFuICIsICJ2YW4gJ3QgIiwgInZhbiBkZSAiICwgInZhbiBkZXIgIiwgInZhbiBkZW4gIiwgInZvbiBkZXIgIiwgIm9wIGRlbiAiLCAidWwgIiwgIm9wIGRlICIpIA0KDQpmb3IgKGkgaW4gMTogbGVuZ3RoKGxhc3RuYW1lX2RmJGxhc3RuYW1lKSkgew0KICBpZiAoc3VtKHN0cl9kZXRlY3QobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHZvb3J2b2Vnc2VscykpPjApIHsNCiAgICBsYXN0IDwtICBhcy5jaGFyYWN0ZXIoc3RyX3NwbGl0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCBwYXR0ZXJuPSIgIiwgc2ltcGxpZnkgPSBUUlVFKSkNCiAgICBsYXN0IDwtIGxhc3RbbGVuZ3RoKGxhc3QpXQ0KICAgIGZpcnN0IDwtIGFzLmNoYXJhY3Rlcih1bmxpc3Qoc3Ryc3BsaXQobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHNwbGl0PWxhc3QsIGZpeGVkPVRSVUUpKSkNCiAgICBsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSA8LSBwYXN0ZShsYXN0LCAiLCAiLCBmaXJzdCwgc2VwPSIiKQ0KICB9DQp9DQoNCiNkdWJiZWxlIG5hbWVuIHZlcndpamRlcmVuLiBsZXQgb3AgZHViYmVsZSBuYW1lbiBtZXQgdm9vcnZvZWdzZWwgd29yZGVuIG5pZXQgZ2VjbGVhbmVkLiBUTyBETyANCmZvciAoaSBpbiAxOiBsZW5ndGgobGFzdG5hbWVfZGYkbGFzdG5hbWUpKSB7DQogIGlmICghc3VtKHN0cl9kZXRlY3QobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHZvb3J2b2Vnc2VscykpPjApIHsNCiAgICANCiAgICANCiAgICBpZiAoc3VtKHN0cl9kZXRlY3QobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sICIgIikpPjApIHsNCiAgICAgIGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldIDwtIHRhaWwoYXMuY2hhcmFjdGVyKHN0cl9zcGxpdChsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSwgcGF0dGVybj0iICIsIG4gPSAyLCBzaW1wbGlmeSA9IFRSVUUpKSwgMSkNCiAgICB9DQogICAgDQogICAgaWYgKHN1bShzdHJfZGV0ZWN0KGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldLCAiLSIpKT4wKSB7DQogICAgICBsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSA8LSB0YWlsKGFzLmNoYXJhY3RlcihzdHJfc3BsaXQobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHBhdHRlcm49Ii0iLCBuID0gMiwgc2ltcGxpZnkgPSBUUlVFKSksIDEpDQogICAgfQ0KICAgIA0KICB9DQp9DQoNCmxhc3RuYW1lX2RmJGxhc3RuYW1lPC0gdHJpbXdzKGxhc3RuYW1lX2RmJGxhc3RuYW1lLCB3aGljaCA9IGMoInJpZ2h0IiksIHdoaXRlc3BhY2UgPSAiWyBcdFxyXG5dIikNCmBgYA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmxhc3RuYW1lX2RmJGZpcnN0X25hbWUgPC0gc3RyX3NwbGl0KGxhc3RuYW1lX2RmJGZpcnN0X25hbWUsIHBhdHRlcm49IiAiLCBuID0gMiwgc2ltcGxpZnkgPSBUUlVFKVssMV0NCmxhc3RuYW1lX2RmJGZpcnN0X25hbWUgPC0gc3RyX3NwbGl0KGxhc3RuYW1lX2RmJGZpcnN0X25hbWUsIHBhdHRlcm49Ii0iLCBuID0gMiwgc2ltcGxpZnkgPSBUUlVFKVssMV0NCg0KDQpgYGANCg0KDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0Kc29jdXVfZGYgPC0gbGFzdG5hbWVfZGYNCnNvY3V1X2RmJGFmZmlsaWF0aW9uIDwtICJVVSINCnNvY3V1X2RmJGZpZWxkIDwtICJzb2Npb2xvZ3kiDQpzYXZlKHNvY3V1X2RmLCBmaWxlPSIuL2RhdGEvc29jdXVfZGZfdjIwMjIxMDA1LlJEYXRhIikNCmBgYA0KDQojIGNvbWJpbmUNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpybShsaXN0PWxzKCkpDQpsb2FkKCIuL2RhdGEvc29jX2RmX3YyMDIyMTAwNS5SRGF0YSIpDQpsb2FkKCIuL2RhdGEvY3NfZGZfdjIwMjIxMDA1LlJEYXRhIikNCm5hbWVzKGNzX2RmKVsxXSA8LSAibmFtZXMiDQpsb2FkKCIuL2RhdGEvZHNfZGZfdjIwMjIxMDA1LlJEYXRhIikNCmxvYWQoIi4vZGF0YS9zb2N1dV9kZl92MjAyMjEwMDUuUkRhdGEiKQ0KbmFtZXNfZGYgPC0gcmJpbmQoc29jX2RmLCBjc19kZiwgZHNfZGYsIHNvY3V1X2RmKQ0KaWQgPC0gMTpucm93KG5hbWVzX2RmKQ0KbmFtZXNfZGYgPC0gY2JpbmQoaWQsIG5hbWVzX2RmKQ0Kc2F2ZShuYW1lc19kZiwgZmlsZT0iLi9kYXRhL25hbWVzX2RmX3YyMDIyMTAwNS5SRGF0YSIpDQpgYGANCg0KDQo=