1 Scrape names & 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
data_staff <- read_html("https://www.cs.ru.nl/das/staff/index.html")

head(data_staff)

class(data_staff)

The website of the data science department looks different. It turns out that the names, emails and pictures are written as javascript, so this is not possible to scrape how we learnt it. After a lot of googling and trying, the following script seemed to work.

#Here I need to install the extra package V8 which is able to read java data. 
#Loading both the required libraries
#library(rvest)
#install.packages("V8")
library(V8)
#URL with js-rendered content to be scraped
link <- 'https://www.cs.ru.nl/das/staff/index.html'

#Read the html page content and extract all javascript codes that are inside a list
#The names are stored after the bit of html that ends with javascript.
namesjs <- read_html(link)  %>% html_nodes('script') %>% html_text()

# Create a new v8 context
ct <- v8()
#This part gave me an error, something about undefined is not found. However, I saw that some data was stored in namesjs (the javascript names) already. So I moved on to the next chunk. 
#parse the html content from the js output and print it as text
read_html(ct$eval(gsub('document.write', namesjs))) %>% 
 html_text()
#This is to clear the data a little bit more. 
data_staff <- namesjs %>% 
gsub("[\n\t]", "", .) %>%
stringr::str_trim() %>%
gsub("\\s+", " ", .) %>%
strsplit(" ") %>% 
unlist()
#Then I made data_staff (based on the javascript names) a dataframe. 
data_staff <- as.data.frame(data_staff)

#Now I only have one column and everything is stored underneath each other. Now I have to see how I can make that normal-looking data. The first 15 can be just deleted as these were almost empty columns. 
data_staff <- data_staff[-c (1:15), , drop=FALSE]

#Now I can look whether there are certain patterns I detect in the data. Now I also have all the email-adresses stored and I don't need those. 
data_staff1 <- data_staff[!grepl("@|jpg|png", data_staff$data_staff), ]
data_staff1 <- as.data.frame(data_staff1)
data_staff <- data_staff1[c(1:160), , drop=FALSE]

All the above code didn’t help me, so I had to just put the names in a dataframe.

#Okay for the sake of time I have to create a dataframe with their names by hand. (h for handcoded). I wont include guest researchers as they may not have Radboud as affiliation and for sociology those are excluded as well.
first_name <- c("tom", "djoerd", "martha", "david", "elena", "arjen", "nico", "theo", "johannes", "tom", "perry", "faegheh", "twan", "harrie", "marieke", "gabriel", "parisa", "yuliya", "gijs", "inge", "jacopo", "ankur", "zaheer", "roel", "franka", "kai", "koen", "mirthe", "luc", "emma", "negin", "hideaki", "chris", "alex", "norman", "simone", "zhuoran","konrad", "marvin", "gido", "bob", "shabaz", "wieske", "yao", "nik", "ivan", "feri", "errol", "zhengyu")
last_name <- c("heskes", "hiemstra", "larson", "van leeuwen", "marchiori", "de vries", "karssemeijer", "van der weide", "textor", "claassen", "groot", "hasibi", "van laarhoven", "oosterhuis", "de vries", "bucur", "naseri", "shapovalova", "van tulder", "wortel", "acquarelli", "ankan", "babar", "bouman", "buytenhuijs", "chen", "dercksen", "van diepen", "evers", "gerritse", "ghasemitaheri", "joko", "kamphuis", "kolmus", "knyazev", "lederer", "liu", "mielke", "oeben", "schoenmacker", "stienen", "sultan", "de swart", "tong", "vaessen", "veul", "wijayanto", "zalmijn", "zhao")
data_staffh <- data.frame(first_name, last_name)
# set affiliation to radboud, comes in handy for querying google scholar
data_staffh$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!
#Turns out 17 people data science do not have a google scholar id. Leaves us with 32 people. 
data_staffh$gs_id <- ""
for (i in 1:nrow(data_staffh)) {
  print(i)
  time <- runif(1, 0, 1)
  Sys.sleep(time)
  
  tryCatch({
     data_staffh[i,c("gs_id")] <- get_scholar_id_fix(last_name = data_staffh[i, c("last_name")], # so search on last_name of staff (third column)
                                             first_name = data_staffh[i, c("first_name")],  # search on first_name of staff (fourth column)
                                             affiliation = data_staffh[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
data_staffh <- data_staffh[!data_staffh$gs_id == "", ]
data_staffh
data_list_profiles <- list()  # first we create an empty list that we then fill up with the for loop
data_list_publications <- list()
for (i in 1:nrow(data_staffh)) {
    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
    data_list_profiles[[i]] <- get_profile(data_staffh[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
    data_list_publications[[i]] <- get_publications(data_staffh[i, c("gs_id")])
    data_list_publications[[i]][, c("gs_id")] <- data_staffh[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
data_df_publications <- bind_rows(data_list_publications)
data_profiles_df <- list()
for (i in 1:length(data_list_profiles)) {
    # soc_profiles_df[[i]] <- data.frame(t(unlist(soc_list_profiles[[i]][1:8]))) #some annyoing
    # data handling
    data_profiles_df[[i]] <- unlist(data_list_profiles[[i]][1:8])
    data_profiles_df[[i]] <- data.frame(data_profiles_df[[i]])
    data_profiles_df[[i]] <- data.frame(t(data_profiles_df[[i]]))
}
data_profiles_df <- bind_rows(data_profiles_df)
data_df <- left_join(data_staffh, data_profiles_df, by = c(gs_id = "id"))  # merge data with soc_df
data_df  # notice all the new information we were able to get from the scholar profiles!
# get citation history of a scholar
data_staff_cit <- list()
for (i in 1:nrow(datadef_df)) {
    data_staff_cit[[i]] <- get_citation_history(datadef_df[i, c("gs_id")])
    if (nrow(data_staff_cit[[i]]) > 0) {
        data_staff_cit[[i]][, c("gs_id")] <- datadef_df[i, c("gs_id")]  # again attach the gs_id as third column
    }
}
data_staff_cit <- bind_rows(data_staff_cit)
colnames(data_staff_cit)[3] <- "gs_id"
save(data_staff_cit, file="/Users/anuschka/Documents/labjournal/data/data_staff_cit.RData")
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)
}
save(data_df, file = "addfiles\\data_df.RData") 
# 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
data_collabs <- list()
for (i in 1:nrow(data_df)) {
    time <- runif(1, 0, 1)
    Sys.sleep(time)
    data_collabs[[i]] <- fcollabs(data_df[i, c("gs_id")], 1)
}
data_collabs <- bind_rows(data_collabs)  # bind rows, get the unique ones!
data_collabs_unique <- unique(data_collabs[, "coauth_id"])  # so 229 unique collaborators for RU staff?
data_collabs_unique <- data_collabs_unique[!is.na(data_collabs_unique)]
save(data_collabs, file = "addfiles\\data_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?
collabsdata_1deep <- list()
for (i in 1:length(data_collabs_unique)) {
    time <- runif(1, 0, 3)
    Sys.sleep(time)
    if (!data_collabs_unique[i] %in% data_df$gs_id) {
        collabsdata_1deep[[i]] <- fcollabs(data_collabs_unique[i], 1)
    }
}
collabsdata_1deep <- bind_rows(collabsdata_1deep)
collabsdata_1deep_unique <- unique(collabsdata_1deep[, 2])
collabsdata_1deep_unique <- collabsdata_1deep_unique[!is.na(collabsdata_1deep_unique)]
save(collabsdata_1deep, file = "addfiles\\data_collabs2.RData")  # you notice this takes a while, so we save the data here.
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  and gender!
#  - 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()


  for (i in 1:nrow(firstname_df)) {
    print(i)
    if (!(is.na(firstname_df$gender))) next
    name_list[[i]] <- fnames(firstname_df[i, c("name_url")])
    # extract name frequency table and gender info
    if (length(name_list[[i]] %>% html_table())>0) {
      
      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)
}
data_df %>% mutate(firstname=first_name) -> data_df
data_df$gender <- NA
#GIVES ERROR
data_df$firstname
data_df <- fgender(data_df, me="Jochem Tolsma, RU/RUG", file="tempgender_data_d2.RData")
save(cs_df, file="cs_df_s2b.RData") #genderized last. 

2 Ethnicity

data_df %>% mutate(lastname=last_name) -> data_df
lastname_df <- data_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) {
    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

3 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(data_df$lastname, verklaring, kenmerken, sc)

4 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. 

5 Extracting specific origin information

There are three main ways to get information about the origin of last names:

  1. Under “kenmerken”, last names are assigned clickable tags. These tags include unspecified foreign name tags (“andere taal”), as well as specific foreign origins of the name (“Franse naam”, “Indische naam”). -> origin1 + origin4

  2. Several names have more extensively written out stories behind the name, under “verklaring”. A number of names contain detailed (either country-level or regional) origins, usually in the form of “De naam [xyz] is afkomstig uit [country]”. -> origin2

  3. Some names have origin information under “verklaring” in the form of the linguistic origins of the name. This can be country specific (e.g. Chinese name), but it can also apply to multiple countries when the language is spoken in more than 1 countries (e.g. Spanish name). -> origin3

# Step 1: extracting origin tags from kenmerken
vk <- vk %>%
  mutate(origin1 = str_extract(kenmerken, "[:upper:]([:lower:]{2,}) naam"))

# Note: sometimes multiple origins are mentioned. Currently, I only extract the first one. Otherwise, we should use str_extract_all. 



# Step 2: extracting origin info from verklaring 
vk <- vk %>%
  mutate(origin2 = ifelse(as.numeric(str_detect(verklaring, "afkomstig uit")) == 1, 
         str_remove(verklaring, ".*afkomstig uit"), NA))


# Step 3: extracting additional origin info from verklaring
vk <- vk %>%
  mutate(origin3 = str_extract(verklaring, "[:upper:]([:lower:]{2,}) (achter)?(familie)?(beroeps)?naam"))



# Finally, we clean up the origin information extracted above

# Origin1: already neat
vk$origin1 <- str_remove(vk$origin1, "Joodse naam") # can be Dutch & non-Dutch

# Origin2: messy
vk$origin2 <- str_remove(vk$origin2, "\\..*") # remove extra info in the following sentence 
vk$origin2 <- str_remove(vk$origin2, "\\;.*") # remove extra info in the following sentence 
vk$origin2 <- str_remove(vk$origin2, "\\(.*") # remove extra info in the following sentence 


vk$dpg <- as.numeric(str_detect(vk$origin2, "(dorp)|(plaats)|(gemeente)|(graafschap)|(stad)|(deel)|(Friesland)")) # origin info too regional 
vk <- vk %>% mutate(origin2 = ifelse((dpg==1), NA, origin2)) # removing regional origin info
vk <- subset(vk, select = -dpg) # removing intermediate variable

# Sometimes, there were multiple countries mentioned. Take only the first:
vk$origin2 <- str_remove(vk$origin2, "\\,.*") # Only first
vk$origin2 <- str_remove(vk$origin2, "\\s(en).*") # Only first 
vk$origin2 <- str_remove(vk$origin2, "\\s(of).*") # Only first 


# Origin3: pretty neat
vk$origin3 <- str_remove(vk$origin3, "D(i)?e(ze)? (familie)?(achter)?(beroeps)?naam") # slipped through the regex
vk$origin3 <- str_remove(vk$origin3, "Een (familie)?(achter)?(beroeps)?naam") # slipped through the regex
vk$origin3 <- str_remove(vk$origin3, "Zijn (familie)?(achter)?(beroeps)?naam") # slipped through the regex
vk$origin3 <- str_remove(vk$origin3, "Als (familie)?(achter)?(beroeps)?naam") # slipped through the regex
vk$origin3 <- str_remove(vk$origin3, "Joodse (familie)?(achter)?naam")
vk$origin3 <- str_remove(vk$origin3, "Bijbelse (familie)?(achter)?naam")


# Setting empty origin variables to NA (Dutch or unfound foreign)
vk <- vk %>%
  mutate(origin1 = as.character(ifelse(origin1==""|origin1=="character(0)", NA, origin1)),
         origin2 = as.character(ifelse(origin2==""|origin2=="character(0)", NA, origin2)),
         origin3 = as.character(ifelse(origin3==""|origin3=="character(0)", NA, origin3)))



# Finally, the tag "andere taal" was used to distinguish foreign names of unknown origin from known Dutch names. 
vk <- vk %>%
  mutate(origin4 = ifelse((as.numeric(str_detect(kenmerken, "andere taal"))==1), "non-Dutch", NA))

Ik zou alles waar no_info op 1 staat of waar origin4 op “non-Dutch” staat coderen als buitenlands!

save(vk, file="vk_data.RData")

6 Making RSiena frame

I see that i dont have the data saved for the publications from data science, but I do not have time to fix that now. So I will start in class # 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_2012 <- matrix(NA, nrow=nrow(soc_df), ncol=nrow(soc_df))
network2013_2015 <- matrix(NA, nrow=nrow(soc_df), ncol=nrow(soc_df))
network2016_2018 <- matrix(NA, nrow=nrow(soc_df), ncol=nrow(soc_df))
network2019_2021 <- 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<=2012)
#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_2012[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>=2013 & year<=2015)
#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? 
    network2013_2015[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>=2016 & 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? 
    network2016_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<=2021)
#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_2021[ego,alter] <- as.numeric(sum(str_detect(pubs_sel2$author, name_alter)) > 1) #did alter publish with ego
  }
}

c(dim(network2010_2012),4)
net_array <- array(data = c(network2010_2012, network2013_2015, network2016_2018, network2019_2021), dim=c(dim(network2010_2012),4))

net_array[1,1,1]
save(net_array, file="soc_net_array.RData")
LS0tCnRpdGxlOiAiV2Vic2NyYXBpbmcgRGF0YSBTY2llbmNlIgphdXRob3I6ICJBbnVzY2hrYSBQZWVsZW4iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OiBodG1sX2RvY3VtZW50Ci0tLQoKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIGdsb2JhbHNldHRpbmdzLCBlY2hvPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KbGlicmFyeShrbml0cikKCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGguY3V0b2ZmPTEwMCksdGlkeT1UUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSxjb21tZW50ID0gIiM+IiwgY2FjaGU9VFJVRSwgY2xhc3Muc291cmNlPWMoInRlc3QiKSwgY2xhc3Mub3V0cHV0PWMoInRlc3QyIikpCm9wdGlvbnMod2lkdGggPSAxMDApCnJnbDo6c2V0dXBLbml0cigpCgoKCmNvbG9yaXplIDwtIGZ1bmN0aW9uKHgsIGNvbG9yKSB7c3ByaW50ZigiPHNwYW4gc3R5bGU9J2NvbG9yOiAlczsnPiVzPC9zcGFuPiIsIGNvbG9yLCB4KSB9CmBgYAoKYGBge3Iga2xpcHB5LCBlY2hvPUZBTFNFLCBpbmNsdWRlPVRSVUV9CmtsaXBweTo6a2xpcHB5KHBvc2l0aW9uID0gYygndG9wJywgJ3JpZ2h0JykpCiNrbGlwcHk6OmtsaXBweShjb2xvciA9ICdkYXJrcmVkJykKI2tsaXBweTo6a2xpcHB5KHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgdG9vbHRpcF9zdWNjZXNzID0gJ0RvbmUnKQpgYGAKCiMgU2NyYXBlIG5hbWVzICYgY29sbGFicwpgYGB7ciwgZXZhbD1GQUxTRX0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBUaXRsZTogICAgV2Vic2NyYXBpbmcgaW4gUgojIEF1dGhvcjogICBCYXMgSG9mc3RyYQojIFZlcnNpb246ICAyOS0wNy0yMDIxCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgojc3RhcnQgd2l0aCBjbGVhbiB3b3Jrc3BhY2UgCnJtKGxpc3Q9bHMoKSkKCiMgaW5zdGFsbC5wYWNrYWdlcygiZGF0YS50YWJsZSIpIApsaWJyYXJ5KGRhdGEudGFibGUpICMgbWFpbmx5IGZvciBmYXN0ZXIgZGF0YSBoYW5kbGluZwpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBJIGFzc3VtZSB5b3UgYWxyZWFkeSBpbnN0YWxsZWQgdGhpcyBvbmUhCiMgaW5zdGFsbC5wYWNrYWdlcygiaHR0ciIpICMgd2UgZG9uJ3QgbmVlZCB0aGlzIGZvciBub3cKIyByZXF1aXJlKGh0dHIpCiNpbnN0YWxsLnBhY2thZ2VzKCJ4bWwyIikKcmVxdWlyZSh4bWwyKQojaW5zdGFsbC5wYWNrYWdlcygicnZlc3QiKQpyZXF1aXJlKHJ2ZXN0KQojaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQpyZXF1aXJlKGRldnRvb2xzKQojIE5vdGUgd2UncmUgZG9pbmcgc29tZXRoaW5nIGRpZmZlcmVudCBoZXJlLiBXZSdyZSBpbnN0YWxsaW5nIGEgKmxhdGVzdCogdmVyc2lvbiBkaXJlY3RseSBmcm9tIEdpdEh1YgojIFRoaXMgaXMgYmVjYXVzZSB0aGUgcmVsZWFzZWQgdmVyc2lvbiBvZiB0aGlzIHBhY2thZ2VzIGNvbnRhaW5zIHNvbWUgZXJyb3JzIQojZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJqa2VpcnN0ZWFkL3NjaG9sYXIiKSAKCgpyZXF1aXJlKHNjaG9sYXIpCgojZGVmaW5lIHdvcmtkaXJlY3RvcnksIG5vdGUgdGhlIGRvdWJsZSAqYmFja3NsYXNoZXMqIGlmIHlvdSdyZSBvbiB3aW5kb3dzCiMgc2V0d2QoIi95b3VycGF0aGhlcmUpIgpgYGAKCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBMZXQncyBmaXJzdCBnZXQgdGhlIHN0YWZmIHBhZ2UgcmVhZF9odG1sIGlzIGEgZnVuY3Rpb24gdGhhdCBzaW1wbHkgZXh0cmFjdHMgaHRtbCB3ZWJwYWdlcyBhbmQKIyBwdXRzIHRoZW0gaW4geG1sIGZvcm1hdApkYXRhX3N0YWZmIDwtIHJlYWRfaHRtbCgiaHR0cHM6Ly93d3cuY3MucnUubmwvZGFzL3N0YWZmL2luZGV4Lmh0bWwiKQoKaGVhZChkYXRhX3N0YWZmKQoKY2xhc3MoZGF0YV9zdGFmZikKYGBgCgpUaGUgd2Vic2l0ZSBvZiB0aGUgZGF0YSBzY2llbmNlIGRlcGFydG1lbnQgbG9va3MgZGlmZmVyZW50LiBJdCB0dXJucyBvdXQgdGhhdCB0aGUgbmFtZXMsIGVtYWlscyBhbmQgcGljdHVyZXMgYXJlIHdyaXR0ZW4gYXMgamF2YXNjcmlwdCwgc28gdGhpcyBpcyBub3QgcG9zc2libGUgdG8gc2NyYXBlIGhvdyB3ZSBsZWFybnQgaXQuIEFmdGVyIGEgbG90IG9mIGdvb2dsaW5nIGFuZCB0cnlpbmcsIHRoZSBmb2xsb3dpbmcgc2NyaXB0IHNlZW1lZCB0byB3b3JrLiAKCmBgYHtyLCBldmFsPUZBTFNFfQojSGVyZSBJIG5lZWQgdG8gaW5zdGFsbCB0aGUgZXh0cmEgcGFja2FnZSBWOCB3aGljaCBpcyBhYmxlIHRvIHJlYWQgamF2YSBkYXRhLiAKI0xvYWRpbmcgYm90aCB0aGUgcmVxdWlyZWQgbGlicmFyaWVzCiNsaWJyYXJ5KHJ2ZXN0KQojaW5zdGFsbC5wYWNrYWdlcygiVjgiKQpsaWJyYXJ5KFY4KQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQojVVJMIHdpdGgganMtcmVuZGVyZWQgY29udGVudCB0byBiZSBzY3JhcGVkCmxpbmsgPC0gJ2h0dHBzOi8vd3d3LmNzLnJ1Lm5sL2Rhcy9zdGFmZi9pbmRleC5odG1sJwoKI1JlYWQgdGhlIGh0bWwgcGFnZSBjb250ZW50IGFuZCBleHRyYWN0IGFsbCBqYXZhc2NyaXB0IGNvZGVzIHRoYXQgYXJlIGluc2lkZSBhIGxpc3QKI1RoZSBuYW1lcyBhcmUgc3RvcmVkIGFmdGVyIHRoZSBiaXQgb2YgaHRtbCB0aGF0IGVuZHMgd2l0aCBqYXZhc2NyaXB0LgpuYW1lc2pzIDwtIHJlYWRfaHRtbChsaW5rKSAgJT4lIGh0bWxfbm9kZXMoJ3NjcmlwdCcpICU+JSBodG1sX3RleHQoKQoKIyBDcmVhdGUgYSBuZXcgdjggY29udGV4dApjdCA8LSB2OCgpCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQojVGhpcyBwYXJ0IGdhdmUgbWUgYW4gZXJyb3IsIHNvbWV0aGluZyBhYm91dCB1bmRlZmluZWQgaXMgbm90IGZvdW5kLiBIb3dldmVyLCBJIHNhdyB0aGF0IHNvbWUgZGF0YSB3YXMgc3RvcmVkIGluIG5hbWVzanMgKHRoZSBqYXZhc2NyaXB0IG5hbWVzKSBhbHJlYWR5LiBTbyBJIG1vdmVkIG9uIHRvIHRoZSBuZXh0IGNodW5rLiAKI3BhcnNlIHRoZSBodG1sIGNvbnRlbnQgZnJvbSB0aGUganMgb3V0cHV0IGFuZCBwcmludCBpdCBhcyB0ZXh0CnJlYWRfaHRtbChjdCRldmFsKGdzdWIoJ2RvY3VtZW50LndyaXRlJywgbmFtZXNqcykpKSAlPiUgCiBodG1sX3RleHQoKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQojVGhpcyBpcyB0byBjbGVhciB0aGUgZGF0YSBhIGxpdHRsZSBiaXQgbW9yZS4gCmRhdGFfc3RhZmYgPC0gbmFtZXNqcyAlPiUgCmdzdWIoIltcblx0XSIsICIiLCAuKSAlPiUKc3RyaW5ncjo6c3RyX3RyaW0oKSAlPiUKZ3N1YigiXFxzKyIsICIgIiwgLikgJT4lCnN0cnNwbGl0KCIgIikgJT4lIAp1bmxpc3QoKQoKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KI1RoZW4gSSBtYWRlIGRhdGFfc3RhZmYgKGJhc2VkIG9uIHRoZSBqYXZhc2NyaXB0IG5hbWVzKSBhIGRhdGFmcmFtZS4gCmRhdGFfc3RhZmYgPC0gYXMuZGF0YS5mcmFtZShkYXRhX3N0YWZmKQoKI05vdyBJIG9ubHkgaGF2ZSBvbmUgY29sdW1uIGFuZCBldmVyeXRoaW5nIGlzIHN0b3JlZCB1bmRlcm5lYXRoIGVhY2ggb3RoZXIuIE5vdyBJIGhhdmUgdG8gc2VlIGhvdyBJIGNhbiBtYWtlIHRoYXQgbm9ybWFsLWxvb2tpbmcgZGF0YS4gVGhlIGZpcnN0IDE1IGNhbiBiZSBqdXN0IGRlbGV0ZWQgYXMgdGhlc2Ugd2VyZSBhbG1vc3QgZW1wdHkgY29sdW1ucy4gCmRhdGFfc3RhZmYgPC0gZGF0YV9zdGFmZlstYyAoMToxNSksICwgZHJvcD1GQUxTRV0KCiNOb3cgSSBjYW4gbG9vayB3aGV0aGVyIHRoZXJlIGFyZSBjZXJ0YWluIHBhdHRlcm5zIEkgZGV0ZWN0IGluIHRoZSBkYXRhLiBOb3cgSSBhbHNvIGhhdmUgYWxsIHRoZSBlbWFpbC1hZHJlc3NlcyBzdG9yZWQgYW5kIEkgZG9uJ3QgbmVlZCB0aG9zZS4gCmRhdGFfc3RhZmYxIDwtIGRhdGFfc3RhZmZbIWdyZXBsKCJAfGpwZ3xwbmciLCBkYXRhX3N0YWZmJGRhdGFfc3RhZmYpLCBdCmRhdGFfc3RhZmYxIDwtIGFzLmRhdGEuZnJhbWUoZGF0YV9zdGFmZjEpCmRhdGFfc3RhZmYgPC0gZGF0YV9zdGFmZjFbYygxOjE2MCksICwgZHJvcD1GQUxTRV0KCgpgYGAKCkFsbCB0aGUgYWJvdmUgY29kZSBkaWRuJ3QgaGVscCBtZSwgc28gSSBoYWQgdG8ganVzdCBwdXQgdGhlIG5hbWVzIGluIGEgZGF0YWZyYW1lLiAKCmBgYHtyLCBldmFsPUZBTFNFfQojT2theSBmb3IgdGhlIHNha2Ugb2YgdGltZSBJIGhhdmUgdG8gY3JlYXRlIGEgZGF0YWZyYW1lIHdpdGggdGhlaXIgbmFtZXMgYnkgaGFuZC4gKGggZm9yIGhhbmRjb2RlZCkuIEkgd29udCBpbmNsdWRlIGd1ZXN0IHJlc2VhcmNoZXJzIGFzIHRoZXkgbWF5IG5vdCBoYXZlIFJhZGJvdWQgYXMgYWZmaWxpYXRpb24gYW5kIGZvciBzb2Npb2xvZ3kgdGhvc2UgYXJlIGV4Y2x1ZGVkIGFzIHdlbGwuCmZpcnN0X25hbWUgPC0gYygidG9tIiwgImRqb2VyZCIsICJtYXJ0aGEiLCAiZGF2aWQiLCAiZWxlbmEiLCAiYXJqZW4iLCAibmljbyIsICJ0aGVvIiwgImpvaGFubmVzIiwgInRvbSIsICJwZXJyeSIsICJmYWVnaGVoIiwgInR3YW4iLCAiaGFycmllIiwgIm1hcmlla2UiLCAiZ2FicmllbCIsICJwYXJpc2EiLCAieXVsaXlhIiwgImdpanMiLCAiaW5nZSIsICJqYWNvcG8iLCAiYW5rdXIiLCAiemFoZWVyIiwgInJvZWwiLCAiZnJhbmthIiwgImthaSIsICJrb2VuIiwgIm1pcnRoZSIsICJsdWMiLCAiZW1tYSIsICJuZWdpbiIsICJoaWRlYWtpIiwgImNocmlzIiwgImFsZXgiLCAibm9ybWFuIiwgInNpbW9uZSIsICJ6aHVvcmFuIiwia29ucmFkIiwgIm1hcnZpbiIsICJnaWRvIiwgImJvYiIsICJzaGFiYXoiLCAid2llc2tlIiwgInlhbyIsICJuaWsiLCAiaXZhbiIsICJmZXJpIiwgImVycm9sIiwgInpoZW5neXUiKQpsYXN0X25hbWUgPC0gYygiaGVza2VzIiwgImhpZW1zdHJhIiwgImxhcnNvbiIsICJ2YW4gbGVldXdlbiIsICJtYXJjaGlvcmkiLCAiZGUgdnJpZXMiLCAia2Fyc3NlbWVpamVyIiwgInZhbiBkZXIgd2VpZGUiLCAidGV4dG9yIiwgImNsYWFzc2VuIiwgImdyb290IiwgImhhc2liaSIsICJ2YW4gbGFhcmhvdmVuIiwgIm9vc3Rlcmh1aXMiLCAiZGUgdnJpZXMiLCAiYnVjdXIiLCAibmFzZXJpIiwgInNoYXBvdmFsb3ZhIiwgInZhbiB0dWxkZXIiLCAid29ydGVsIiwgImFjcXVhcmVsbGkiLCAiYW5rYW4iLCAiYmFiYXIiLCAiYm91bWFuIiwgImJ1eXRlbmh1aWpzIiwgImNoZW4iLCAiZGVyY2tzZW4iLCAidmFuIGRpZXBlbiIsICJldmVycyIsICJnZXJyaXRzZSIsICJnaGFzZW1pdGFoZXJpIiwgImpva28iLCAia2FtcGh1aXMiLCAia29sbXVzIiwgImtueWF6ZXYiLCAibGVkZXJlciIsICJsaXUiLCAibWllbGtlIiwgIm9lYmVuIiwgInNjaG9lbm1hY2tlciIsICJzdGllbmVuIiwgInN1bHRhbiIsICJkZSBzd2FydCIsICJ0b25nIiwgInZhZXNzZW4iLCAidmV1bCIsICJ3aWpheWFudG8iLCAiemFsbWlqbiIsICJ6aGFvIikKZGF0YV9zdGFmZmggPC0gZGF0YS5mcmFtZShmaXJzdF9uYW1lLCBsYXN0X25hbWUpCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQojIHNldCBhZmZpbGlhdGlvbiB0byByYWRib3VkLCBjb21lcyBpbiBoYW5keSBmb3IgcXVlcnlpbmcgZ29vZ2xlIHNjaG9sYXIKZGF0YV9zdGFmZmgkYWZmaWxpYXRpb24gPC0gInJhZGJvdWQgdW5pdmVyc2l0eSIKYGBgCgoKYGBge3IsIGV2YWw9RkFMU0V9CiNyZXF1aXJlKHNjaG9sYXIpCmdldF9zY2hvbGFyX2lkX2ZpeCA8LSBmdW5jdGlvbiAobGFzdF9uYW1lID0gIiIsIGZpcnN0X25hbWUgPSAiIiwgYWZmaWxpYXRpb24gPSBOQSkKewogIGlmICghYW55KG56Y2hhcihjKGZpcnN0X25hbWUsIGxhc3RfbmFtZSkpKSkKICAgIHN0b3AoIkF0IGxlYXN0IG9uZSBvZiBmaXJzdCBhbmQgbGFzdCBuYW1lIG11c3QgYmUgc3BlY2lmaWVkISIpCiAgc2l0ZSA8LSBnZXRPcHRpb24oInNjaG9sYXJfc2l0ZSIpCiAgdXJsIDwtIHBhc3RlMChzaXRlLCAiL2NpdGF0aW9ucz92aWV3X29wPXNlYXJjaF9hdXRob3JzJm1hdXRob3JzPSIsCiAgICAgICAgICAgICAgICBmaXJzdF9uYW1lLCAiKyIsIGxhc3RfbmFtZSwgIiZobD1lbiZvaT1hbyIpCiAgcGFnZSA8LSBnZXRfc2Nob2xhcl9yZXNwKHVybCkKICBpZiAoaXMubnVsbChwYWdlKSkKICAgIHJldHVybihOQSkKICBhYSA8LSBodHRyOjpjb250ZW50KHBhZ2UsIGFzID0gInRleHQiKQogICMgYWRkZWQgYnkgQmFzIEhvZnN0cmE6IGJ1Z2ZpeCBmb3IgSURzIHRoYXQgaGF2ZSBhIGRhc2ggKCItIikKICBpZHMgPC0gc3Vic3RyaW5nKGFhLCByZWdleHByKCI7dXNlcj0iLCBhYSkpCiAgaWRzIDwtIHN1YnN0cihpZHMsIDEsIDE5KSAjIGVycm9yIHByb25lLCBidXQgdW5zdXJlIGhvdyB0byBzb2x2ZSBvdGhlcndpc2UKICAjIGlmIChuY2hhcihzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwoc3RyaW5nID0gYWEsIHBhdHRlcm4gPSAiO3VzZXI9W1s6YWxudW06XV0rW1s6cHVuY3Q6XV0iKVtbMV1dWzFdKSA8IDE4KSB7CiAgIyAgIGlkcyA8LSBzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwoc3RyaW5nID0gYWEsIHBhdHRlcm4gPSAiO3VzZXI9W1s6YWxudW06XV0rW1s6cHVuY3Q6XV0rW1s6YWxudW06XV0rW1s6cHVuY3Q6XV0iKQogICMgfSBlbHNlIHsKICAjICAgaWRzIDwtIHN0cmluZ3I6OnN0cl9leHRyYWN0X2FsbChzdHJpbmcgPSBhYSwgcGF0dGVybiA9ICI7dXNlcj1bWzphbG51bTpdXStbWzpwdW5jdDpdXSIpCiAgIyB9CiAgaWYgKGxlbmd0aCh1bmxpc3QoaWRzKSkgPT0gMCkgewogICAgbWVzc2FnZSgiTm8gU2Nob2xhciBJRCBmb3VuZC4iKQogICAgcmV0dXJuKE5BKQogIH0KICBpZHMgPC0gaWRzICU+JSB1bmxpc3QgJT4lIGdzdWIoIjt1c2VyPXxbWzpwdW5jdDpdXSQiLCAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLikgJT4lIHVuaXF1ZQogIGlmIChsZW5ndGgoaWRzKSA+IDEpIHsKICAgIHByb2ZpbGVzIDwtIGxhcHBseShpZHMsIHNjaG9sYXI6OmdldF9wcm9maWxlKQogICAgaWYgKGlzLm5hKGFmZmlsaWF0aW9uKSkgewogICAgICB4X3Byb2ZpbGUgPC0gcHJvZmlsZXNbWzFdXQogICAgICB3YXJuaW5nKCJTZWxlY3RpbmcgZmlyc3Qgb3V0IG9mICIsIGxlbmd0aChwcm9maWxlcyksCiAgICAgICAgICAgICAgIiBjYW5kaWRhdGUgbWF0Y2hlcy4iKQogICAgfQogICAgZWxzZSB7CiAgICAgIHdoaWNoX3Byb2ZpbGUgPC0gc2FwcGx5KHByb2ZpbGVzLCBmdW5jdGlvbih4KSB7CiAgICAgICAgc3RyaW5ncjo6c3RyX2NvdW50KHN0cmluZyA9IHgkYWZmaWxpYXRpb24sIHBhdHRlcm4gPSBzdHJpbmdyOjpjb2xsKGFmZmlsaWF0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IFRSVUUpKQogICAgICB9KQogICAgICBpZiAoYWxsKHdoaWNoX3Byb2ZpbGUgPT0gMCkpIHsKICAgICAgICB3YXJuaW5nKCJObyByZXNlYXJjaGVyIGZvdW5kIGF0IHRoZSBpbmRpY2F0ZWQgYWZmaWxpYXRpb24uIikKICAgICAgICByZXR1cm4oTkEpCiAgICAgIH0KICAgICAgZWxzZSB7CiAgICAgICAgeF9wcm9maWxlIDwtIHByb2ZpbGVzW1t3aGljaCh3aGljaF9wcm9maWxlICE9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDApXV0KICAgICAgfQogICAgfQogIH0KICBlbHNlIHsKICAgIHhfcHJvZmlsZSA8LSBzY2hvbGFyOjpnZXRfcHJvZmlsZShpZCA9IGlkcykKICB9CiAgcmV0dXJuKHhfcHJvZmlsZSRpZCkKfQpgYGAKCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBMb29rIHRocm91Z2h0IGdldF9zY2hvbGFyX2lkX2ZpeChsYXN0X25hbWUsIGZpcnN0X25hbWUsIGFmZmlsaWF0aW9uKSAKIyBpZiB3ZSBjYW4gZmluZCBnb29nbGUgc2Nob2xhciBwcm9maWxlcyBvZiBzb2Npb2xvZ3kgc3RhZmYhCiNUdXJucyBvdXQgMTcgcGVvcGxlIGRhdGEgc2NpZW5jZSBkbyBub3QgaGF2ZSBhIGdvb2dsZSBzY2hvbGFyIGlkLiBMZWF2ZXMgdXMgd2l0aCAzMiBwZW9wbGUuIApkYXRhX3N0YWZmaCRnc19pZCA8LSAiIgpmb3IgKGkgaW4gMTpucm93KGRhdGFfc3RhZmZoKSkgewogIHByaW50KGkpCiAgdGltZSA8LSBydW5pZigxLCAwLCAxKQogIFN5cy5zbGVlcCh0aW1lKQogIAogIHRyeUNhdGNoKHsKICAgICBkYXRhX3N0YWZmaFtpLGMoImdzX2lkIildIDwtIGdldF9zY2hvbGFyX2lkX2ZpeChsYXN0X25hbWUgPSBkYXRhX3N0YWZmaFtpLCBjKCJsYXN0X25hbWUiKV0sICMgc28gc2VhcmNoIG9uIGxhc3RfbmFtZSBvZiBzdGFmZiAodGhpcmQgY29sdW1uKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaXJzdF9uYW1lID0gZGF0YV9zdGFmZmhbaSwgYygiZmlyc3RfbmFtZSIpXSwgICMgc2VhcmNoIG9uIGZpcnN0X25hbWUgb2Ygc3RhZmYgKGZvdXJ0aCBjb2x1bW4pCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFmZmlsaWF0aW9uID0gZGF0YV9zdGFmZmhbaSxjKCJhZmZpbGlhdGlvbiIpXSkgIyBzZWFyY2ggb24gYWZmaWxpYXRpb24gb2YgZWFjaCBzdGFmZiAoZmlmdGggY29sdW1uKQogICAgfSwgZXJyb3I9ZnVuY3Rpb24oZSl7Y2F0KCJFUlJPUiA6IiwgY29uZGl0aW9uTWVzc2FnZShlKSwgIlxuIil9KSAjIGNvbnRpbnVlIG9uIGVycm9yLCBidXQgcHJpbnQgdGhlIGVycm9yCiAgfQojIHJlbW92ZSB0aG9zZSB3aXRob3V0IHB1YnMgZnJvbSB0aGUgZGYKZGF0YV9zdGFmZmggPC0gZGF0YV9zdGFmZmhbIWRhdGFfc3RhZmZoJGdzX2lkID09ICIiLCBdCmRhdGFfc3RhZmZoCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQpkYXRhX2xpc3RfcHJvZmlsZXMgPC0gbGlzdCgpICAjIGZpcnN0IHdlIGNyZWF0ZSBhbiBlbXB0eSBsaXN0IHRoYXQgd2UgdGhlbiBmaWxsIHVwIHdpdGggdGhlIGZvciBsb29wCmRhdGFfbGlzdF9wdWJsaWNhdGlvbnMgPC0gbGlzdCgpCmZvciAoaSBpbiAxOm5yb3coZGF0YV9zdGFmZmgpKSB7CiAgICBwcmludChpKQogICAgdGltZSA8LSBydW5pZigxLCAwLCAxKQogICAgU3lzLnNsZWVwKHRpbWUpCiAgICAjIG5vdGUgaG93IHlvdSBjYWxsIGRpZmZlcmVudCBlbGVtZW50cyBpbiBhIGxpc3QgJ1tbXV0nLCBmaWxsIGluIHRoZSBpLXRoIGVsZW1lbnQKICAgIGRhdGFfbGlzdF9wcm9maWxlc1tbaV1dIDwtIGdldF9wcm9maWxlKGRhdGFfc3RhZmZoW2ksIGMoImdzX2lkIildKSAgIyBOb3RlIGhvdyB3ZSBjYWxsIHJvdyBpIChyZW1lbWJlciBob3cgdG8gY2FsbCByb3dzIGluIGEgREYvTWF0cml4KSBhbmQgdGhlbiB0aGUgYXNzb2NpYXRlZCBzY2hvbGFyIGlkCiAgICBkYXRhX2xpc3RfcHVibGljYXRpb25zW1tpXV0gPC0gZ2V0X3B1YmxpY2F0aW9ucyhkYXRhX3N0YWZmaFtpLCBjKCJnc19pZCIpXSkKICAgIGRhdGFfbGlzdF9wdWJsaWNhdGlvbnNbW2ldXVssIGMoImdzX2lkIildIDwtIGRhdGFfc3RhZmZoW2ksIGMoImdzX2lkIildICAjIG5vdGUgdGhhdCB3ZSBhZ2FpbiBhdHRhY2ggYW4gaWQKICAgICMgc28gYm90aCBmdW5jdGlvbnMgaGVyZSBjYWxsIHRoZSBlbnRpcmUgcHJvZmlsZSBhbmQgcHVicyBmb3IgYW4gYXV0aG9yLCBiYXNlZCBvbiBnb29nbGUKICAgICMgc2Nob2xhciBpZHMKfQojIE5vdGljZSBob3cgZmFzdCB0aGUgZGF0YSBibG93IHVwISBUaGUgMzQgUlUgc29jaW9sb2d5IHNjaG9sYXJzIHB1Ymxpc2ggfjMwMDAgcGFwZXJzCmRhdGFfZGZfcHVibGljYXRpb25zIDwtIGJpbmRfcm93cyhkYXRhX2xpc3RfcHVibGljYXRpb25zKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpkYXRhX3Byb2ZpbGVzX2RmIDwtIGxpc3QoKQpmb3IgKGkgaW4gMTpsZW5ndGgoZGF0YV9saXN0X3Byb2ZpbGVzKSkgewogICAgIyBzb2NfcHJvZmlsZXNfZGZbW2ldXSA8LSBkYXRhLmZyYW1lKHQodW5saXN0KHNvY19saXN0X3Byb2ZpbGVzW1tpXV1bMTo4XSkpKSAjc29tZSBhbm55b2luZwogICAgIyBkYXRhIGhhbmRsaW5nCiAgICBkYXRhX3Byb2ZpbGVzX2RmW1tpXV0gPC0gdW5saXN0KGRhdGFfbGlzdF9wcm9maWxlc1tbaV1dWzE6OF0pCiAgICBkYXRhX3Byb2ZpbGVzX2RmW1tpXV0gPC0gZGF0YS5mcmFtZShkYXRhX3Byb2ZpbGVzX2RmW1tpXV0pCiAgICBkYXRhX3Byb2ZpbGVzX2RmW1tpXV0gPC0gZGF0YS5mcmFtZSh0KGRhdGFfcHJvZmlsZXNfZGZbW2ldXSkpCn0KZGF0YV9wcm9maWxlc19kZiA8LSBiaW5kX3Jvd3MoZGF0YV9wcm9maWxlc19kZikKZGF0YV9kZiA8LSBsZWZ0X2pvaW4oZGF0YV9zdGFmZmgsIGRhdGFfcHJvZmlsZXNfZGYsIGJ5ID0gYyhnc19pZCA9ICJpZCIpKSAgIyBtZXJnZSBkYXRhIHdpdGggc29jX2RmCmRhdGFfZGYgICMgbm90aWNlIGFsbCB0aGUgbmV3IGluZm9ybWF0aW9uIHdlIHdlcmUgYWJsZSB0byBnZXQgZnJvbSB0aGUgc2Nob2xhciBwcm9maWxlcyEKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBnZXQgY2l0YXRpb24gaGlzdG9yeSBvZiBhIHNjaG9sYXIKZGF0YV9zdGFmZl9jaXQgPC0gbGlzdCgpCmZvciAoaSBpbiAxOm5yb3coZGF0YWRlZl9kZikpIHsKICAgIGRhdGFfc3RhZmZfY2l0W1tpXV0gPC0gZ2V0X2NpdGF0aW9uX2hpc3RvcnkoZGF0YWRlZl9kZltpLCBjKCJnc19pZCIpXSkKICAgIGlmIChucm93KGRhdGFfc3RhZmZfY2l0W1tpXV0pID4gMCkgewogICAgICAgIGRhdGFfc3RhZmZfY2l0W1tpXV1bLCBjKCJnc19pZCIpXSA8LSBkYXRhZGVmX2RmW2ksIGMoImdzX2lkIildICAjIGFnYWluIGF0dGFjaCB0aGUgZ3NfaWQgYXMgdGhpcmQgY29sdW1uCiAgICB9Cn0KZGF0YV9zdGFmZl9jaXQgPC0gYmluZF9yb3dzKGRhdGFfc3RhZmZfY2l0KQpjb2xuYW1lcyhkYXRhX3N0YWZmX2NpdClbM10gPC0gImdzX2lkIgpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpzYXZlKGRhdGFfc3RhZmZfY2l0LCBmaWxlPSIvVXNlcnMvYW51c2Noa2EvRG9jdW1lbnRzL2xhYmpvdXJuYWwvZGF0YS9kYXRhX3N0YWZmX2NpdC5SRGF0YSIpCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQpyZXF1aXJlKHJ2ZXN0KQpyZXF1aXJlKHhtbDIpCnJlcXVpcmUodGlkeXZlcnNlKQojIGZ1bmN0aW9uIHRvIGdldCBjb2xsYWJvcmF0b3JzIGFuZCBuYW1lcyBmcm9tIEdTIHByb2ZpbGVzCmZjb2xsYWJzIDwtIGZ1bmN0aW9uKGdzaWQsIGxvb2tmb3Jjb2xsYWJzKSB7CiAgaHRtbHBhZ2UxIDwtIHJlYWRfaHRtbChwYXN0ZTAoImh0dHBzOi8vc2Nob2xhci5nb29nbGUubmwvY2l0YXRpb25zP3VzZXI9IiwgZ3NpZCwgIiZobD1lbiIpKSAjIHNvIHdlIHBhc3RlIHRoZSBnb29nbGUgc2Nob2xhciBpZAogIHByb2ZpbGVuYW1lIDwtIGh0bWxwYWdlMSAlPiUgaHRtbF9ub2Rlcyh4cGF0aCA9ICIvLyovZGl2W0BpZD0nZ3NjX3ByZl9pbiddIikgJT4lIGh0bWxfdGV4dCgpICMgd2UgZXh0cmFjdCB0aGUgcHJvZmlsZSBuYW1lIG9mIHRoYXQgZ29vZ2xlIHNjaG9sYXIgcGFnZQogIHByb2ZpbGVjb2xsYWJzMSA8LSBhcy5kYXRhLmZyYW1lKDApICMgZW1wdHkgZGYgbmVjZXNzYXJ5IGZvciBsYXRlcgogIHByb2ZpbGVjb2xsYWJzMiA8LSBhcy5kYXRhLmZyYW1lKDApICMgZW1wdHkgZGYgbmVjZXNzYXJ5IGZvciBsYXRlcgogIGlmIChsb29rZm9yY29sbGFicyA9PSAxKSB7ICMgc28gaWYgeW91IHdhbnQgdG8gbG9vayBmb3IgY29sbGFicywgc2V0IGZ1bmN0aW9uIHRvIDEKICAgIGh0bWxwYWdlMiA8LSByZWFkX2h0bWwocGFzdGUwKCJodHRwczovL3NjaG9sYXIuZ29vZ2xlLmNvbS9jaXRhdGlvbnM/dmlld19vcD1saXN0X2NvbGxlYWd1ZXMmaGw9ZW4mdXNlcj0iLCBnc2lkKSkgIyBzbyB3ZSBwYXN0ZSB0aGUgZ29vZ2xlIHNjaG9sYXIgaWQKICAgIHByb2ZpbGVjb2xsYWJzMSA8LSAgaHRtbHBhZ2UyICU+JSBodG1sX25vZGVzKGNzcz0iaDMiKSAlPiUgaHRtbF90ZXh0KCkgIyBnZXQgbmFtZXMKICAgIHByb2ZpbGVjb2xsYWJzMSA8LSAgYXMuZGF0YS5mcmFtZShwcm9maWxlY29sbGFiczEpCiAgICBwcm9maWxlY29sbGFiczIgPC0gaHRtbHBhZ2UyICU+JSBodG1sX25vZGVzKCJhIikgJT4lIGh0bWxfYXR0cigiaHJlZiIpICMgZ2V0IHRoZSBsaW5rCiAgICBwcm9maWxlY29sbGFiczIgPC0gcHJvZmlsZWNvbGxhYnMyW3NlcV9hbG9uZyhwcm9maWxlY29sbGFiczIpICUlIDIgPiAwXQogICAgcHJvZmlsZWNvbGxhYnMyIDwtIHN1YnN0cmluZyhwcm9maWxlY29sbGFiczIsIDIzKQogIH0KICBpZiAobnJvdyhwcm9maWxlY29sbGFiczEpPjEpIHsgIyBpZiB0aGVyZSBBUkUgY29sbGFicwogICAgcHJvZmlsZWNvbGxhYnMxIDwtIGFzLmRhdGEuZnJhbWUocHJvZmlsZWNvbGxhYnMxKSAjIHdlIHdhbnQgdG8uLi4KICAgIHByb2ZpbGVjb2xsYWJzMiA8LSAgYXMuZGF0YS5mcmFtZShwcm9maWxlY29sbGFiczIpCiAgICBwcm9maWxlY29sbGFiczFbLGMoImNvYXV0aF9pZCIpXSA8LSBwcm9maWxlY29sbGFiczJbLDFdCiAgICBwcm9maWxlY29sbGFiczFbLGMoImdzX2lkIildIDwtIGdzaWQgIy4uLiBhZGQgZ3NfaWRzIG9mIGZvY2FsIEdTIHByb2ZpbGUKICAgIHByb2ZpbGVjb2xsYWJzMVssYygibmFtZSIpXSA8LSBwcm9maWxlbmFtZSAjLi4uYW5kIHRoZSB0aGUgcHJvZmlsZSBuYW1lIG9mIEdTIHByb2ZpbGUgYXR0YWNoZWQKICAgIG5hbWVzKHByb2ZpbGVjb2xsYWJzMSlbMV0gPC0gImNvYXV0aCIKICB9IGVsc2UgewogICAgcHJvZmlsZWNvbGxhYnMxIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQoZ3NpZCwgcHJvZmlsZW5hbWUpKSAjIGlmIE5PVCBsb29raW5nIGZvciBjb2xsYWJzLi4uCiAgICBuYW1lcyhwcm9maWxlY29sbGFiczEpIDwtIGMoImdzX2lkIiwgIm5hbWUiKSAjLi4ud2Ugb25seSBhdHRhY2ggZ3NfaWQgYW5kIHByb2ZpbGVuYW1lCiAgfQogIHJldHVybihwcm9maWxlY29sbGFiczEpCn0KYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0Kc2F2ZShkYXRhX2RmLCBmaWxlID0gImFkZGZpbGVzXFxkYXRhX2RmLlJEYXRhIikgCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQojIGZpcnN0IHRoZSBzb2MgY29sbGFib3JhdG9ycyBub3RlIGhvdyB3ZSBhbHJlYWR5IGJ1aWxkIGEgZnVuY3Rpb24gKGZjb2xsYWJzKCkpIGZvciB5b3UgeW91IG5lZWQgdG8KIyBpbnB1dCBhIGdvb2dsZSBzY2hvbGFyIGlkIGFuZCBhIDEgKGlmIHlvdSB3YW50IHRvIGZpbmQgY29sbGFicykgb3IgMCAob25seSBleHRyYWN0aW5nIG5hbWVzKQojIGZjb2xsYWJzIC0tPiB5b3UgY2FuIGNoZWNrIGl0IG91dCBpZiB5b3UncmUgaW50ZXJlc3RlZApkYXRhX2NvbGxhYnMgPC0gbGlzdCgpCmZvciAoaSBpbiAxOm5yb3coZGF0YV9kZikpIHsKICAgIHRpbWUgPC0gcnVuaWYoMSwgMCwgMSkKICAgIFN5cy5zbGVlcCh0aW1lKQogICAgZGF0YV9jb2xsYWJzW1tpXV0gPC0gZmNvbGxhYnMoZGF0YV9kZltpLCBjKCJnc19pZCIpXSwgMSkKfQpkYXRhX2NvbGxhYnMgPC0gYmluZF9yb3dzKGRhdGFfY29sbGFicykgICMgYmluZCByb3dzLCBnZXQgdGhlIHVuaXF1ZSBvbmVzIQpkYXRhX2NvbGxhYnNfdW5pcXVlIDwtIHVuaXF1ZShkYXRhX2NvbGxhYnNbLCAiY29hdXRoX2lkIl0pICAjIHNvIDIyOSB1bmlxdWUgY29sbGFib3JhdG9ycyBmb3IgUlUgc3RhZmY/CmRhdGFfY29sbGFic191bmlxdWUgPC0gZGF0YV9jb2xsYWJzX3VuaXF1ZVshaXMubmEoZGF0YV9jb2xsYWJzX3VuaXF1ZSldCnNhdmUoZGF0YV9jb2xsYWJzLCBmaWxlID0gImFkZGZpbGVzXFxkYXRhX2RmX2NvbGxhYnMxLlJEYXRhIikgICMgeW91IG5vdGljZSB0aGlzIHRha2VzIGEgd2hpbGUsIHNvIHdlIHNhdmUgdGhlIGRhdGEgaGVyZS4KYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyB0aGVuIHRoZSBuYW1lcyBvZiB0aG9zZSBjb2xsYWJvcmF0b3JzIHBsdXMgVEhFSVIgY29sbGFib3JhdG9ycyB1bmRlcnN0YW5kIHRoYXQgd2UgZG9uJ3QgaGF2ZQojIG5hbWVzIG9mIHRoZW0geWV0IGZyb20gdGhlIGNvZGUgYWJvdmU/CmNvbGxhYnNkYXRhXzFkZWVwIDwtIGxpc3QoKQpmb3IgKGkgaW4gMTpsZW5ndGgoZGF0YV9jb2xsYWJzX3VuaXF1ZSkpIHsKICAgIHRpbWUgPC0gcnVuaWYoMSwgMCwgMykKICAgIFN5cy5zbGVlcCh0aW1lKQogICAgaWYgKCFkYXRhX2NvbGxhYnNfdW5pcXVlW2ldICVpbiUgZGF0YV9kZiRnc19pZCkgewogICAgICAgIGNvbGxhYnNkYXRhXzFkZWVwW1tpXV0gPC0gZmNvbGxhYnMoZGF0YV9jb2xsYWJzX3VuaXF1ZVtpXSwgMSkKICAgIH0KfQpjb2xsYWJzZGF0YV8xZGVlcCA8LSBiaW5kX3Jvd3MoY29sbGFic2RhdGFfMWRlZXApCmNvbGxhYnNkYXRhXzFkZWVwX3VuaXF1ZSA8LSB1bmlxdWUoY29sbGFic2RhdGFfMWRlZXBbLCAyXSkKY29sbGFic2RhdGFfMWRlZXBfdW5pcXVlIDwtIGNvbGxhYnNkYXRhXzFkZWVwX3VuaXF1ZVshaXMubmEoY29sbGFic2RhdGFfMWRlZXBfdW5pcXVlKV0Kc2F2ZShjb2xsYWJzZGF0YV8xZGVlcCwgZmlsZSA9ICJhZGRmaWxlc1xcZGF0YV9jb2xsYWJzMi5SRGF0YSIpICAjIHlvdSBub3RpY2UgdGhpcyB0YWtlcyBhIHdoaWxlLCBzbyB3ZSBzYXZlIHRoZSBkYXRhIGhlcmUuCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQpmZ2VuZGVyIDwtIGZ1bmN0aW9uKGZpcnN0bmFtZV9kZiwgbWUsIGZpbGU9TlVMTCkgewoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgQXV0aG9yOiBCYXMgSG9mc3RyYSwgQW5uZSBNYWFpa2UgTXVsZGVycywgSm9jaGVtIFRvbHNtYQojIERBdGU6ICAgMTMtMTAtMjAyMSwgbGFzdCBlZGl0OiAyMi0wOS0yMDIyCiMgVGFza3M6ICAtIGFzc2lnbiBnZW5kZXIgYmFlZCBvbiBuYW1lCiMgICAgICAgICAtIEFkYXB0ZWQgZnJvbSBSZW5zZSBDb3J0ZW4gY29kZSBBcHJpbCAyMDIxCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKCiNJbnB1dDogCiMgIC0gZmlyc3RuYW1lX2RmOiBhIGRhdGEuZnJhbWUgd2l0aCBhIGNvbHVtbiBuYW1lZCBmaXJzdG5hbWUgIGFuZCBnZW5kZXIhCiMgIC0gbWU6IGEgY2hhcmFjdGVyIHZlY3RvciBpbnRyb2R1Y2luZyB5b3Vyc2VsZjogZS5nLiAiSiBUb2xzbWEsIFJhZGJvdWQgVW5pdmVyc2l0eSIKIyAgLSBmaWxlOiBsb2NhdGlvbiBhbmQgbmFtZSBvZiBmaWxlIHRvIGJlIHNhdmVkLiAKICAKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIExvYWQgcmVxdWlyZWQgcGFja2FnZXMKCmlmICghcmVxdWlyZSgidGlkeXZlcnNlIiwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgewogIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiAgbGlicmFyeSh0aWR5dmVyc2UsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkKfQoKaWYgKCFyZXF1aXJlKCJydmVzdCIsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJydmVzdCIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiAgbGlicmFyeShydmVzdCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQp9CgppZiAoIXJlcXVpcmUoInBvbGl0ZSIsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJwb2xpdGUiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQogIGxpYnJhcnkocG9saXRlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCn0KCgoKIyBtYWtlIGxpbmtzIHRvIHNjcmFwZQpmaXJzdG5hbWVfZGYkbmFtZV91cmwgPC0gcGFzdGUwKCJodHRwczovL3d3dy5tZWVydGVucy5rbmF3Lm5sL252Yi9uYWFtL2lzLyIsIGZpcnN0bmFtZV9kZlssIGMoImZpcnN0bmFtZSIpXSkKCgoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgMjogaW50cm9kdWNlIHRvIHNlcnZlciAjIyMKCiMgSW50cm9kdWNlIG15c2VsZiB0byB0aGUgc2VydmVyCnNlc3Npb24gPC0gYm93KCJodHRwczovL3d3dy5tZWVydGVucy5rbmF3Lm5sL252Yi9uYWFtL2lzIiwgdXNlcl9hZ2VudCA9IG1lICwgZGVsYXkgPSAxKQoKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIDM6IG1ha2UgZnVuY3Rpb24gdG8gZ2V0IHRhYmxlIGZyb20gIyMjCiAgZm5hbWVzIDwtIGZ1bmN0aW9uKGxpbmspeyAKICAgIG5hbWVfc2Vzc2lvbiA8LW5vZChzZXNzaW9uLCBwYXRoID0gbGluaykKICAgIG5hbWVfcGFnZSA8LSBzY3JhcGUobmFtZV9zZXNzaW9uKSAKICAgIHJldHVybihuYW1lX3BhZ2UpCiAgfQogIApuYW1lX2xpc3QgPC0gbGlzdCgpCnRhYmxlX2xpc3QgPC0gbGlzdCgpCgoKICBmb3IgKGkgaW4gMTpucm93KGZpcnN0bmFtZV9kZikpIHsKICAgIHByaW50KGkpCiAgICBpZiAoIShpcy5uYShmaXJzdG5hbWVfZGYkZ2VuZGVyKSkpIG5leHQKICAgIG5hbWVfbGlzdFtbaV1dIDwtIGZuYW1lcyhmaXJzdG5hbWVfZGZbaSwgYygibmFtZV91cmwiKV0pCiAgICAjIGV4dHJhY3QgbmFtZSBmcmVxdWVuY3kgdGFibGUgYW5kIGdlbmRlciBpbmZvCiAgICBpZiAobGVuZ3RoKG5hbWVfbGlzdFtbaV1dICU+JSBodG1sX3RhYmxlKCkpPjApIHsKICAgICAgCiAgICAgIHRhYmxlX2xpc3RbW2ldXSA8LSBuYW1lX2xpc3RbW2ldXSAlPiUgaHRtbF90YWJsZSgpCiAgICAgIHRhYmxlX2xpc3RbW2ldXVtbMV1dW3RhYmxlX2xpc3RbW2ldXVtbMV1dPT0iLS0iXSA8LSAiMCIKICAgICAgaWYgKGFzLm51bWVyaWModGFibGVfbGlzdFtbaV1dW1sxXV0kWDNbMl0pID4gYXMubnVtZXJpYyh0YWJsZV9saXN0W1tpXV1bWzFdXSRYM1s2XSkpIHsKICAgICAgICBmaXJzdG5hbWVfZGYkZ2VuZGVyW2ldIDwtICJtYWxlIiB9IGVsc2UgewogICAgICAgICAgZmlyc3RuYW1lX2RmJGdlbmRlcltpXSA8LSAiZmVtYWxlIgogICAgICAgIH0KICAgIH0KICAgIGlmICghaXMubnVsbChmaWxlKSkgKHNhdmUoZmlyc3RuYW1lX2RmLCBmaWxlPWZpbGUpKQogICAgCiAgICB9CiAgcmV0dXJuKGZpcnN0bmFtZV9kZikKfQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpkYXRhX2RmICU+JSBtdXRhdGUoZmlyc3RuYW1lPWZpcnN0X25hbWUpIC0+IGRhdGFfZGYKZGF0YV9kZiRnZW5kZXIgPC0gTkEKYGBgCgoKYGBge3IsIGV2YWw9RkFMU0V9CiNHSVZFUyBFUlJPUgpkYXRhX2RmJGZpcnN0bmFtZQpkYXRhX2RmIDwtIGZnZW5kZXIoZGF0YV9kZiwgbWU9IkpvY2hlbSBUb2xzbWEsIFJVL1JVRyIsIGZpbGU9InRlbXBnZW5kZXJfZGF0YV9kMi5SRGF0YSIpCgpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpzYXZlKGNzX2RmLCBmaWxlPSJjc19kZl9zMmIuUkRhdGEiKSAjZ2VuZGVyaXplZCBsYXN0LiAKYGBgCgoKIyBFdGhuaWNpdHkgCgpgYGB7ciwgZXZhbD1GQUxTRX0KZGF0YV9kZiAlPiUgbXV0YXRlKGxhc3RuYW1lPWxhc3RfbmFtZSkgLT4gZGF0YV9kZgpsYXN0bmFtZV9kZiA8LSBkYXRhX2RmCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQojdm9vcnZvZWdzZWxzIGNvcnJlY3QgemV0dGVuIHZvb3Igc2NyYXBlcgp2b29ydm9lZ3NlbHMgPC0gYygiJ3QgIiwgImQnICIsICJkZSAiLCAiZGUgbGEgIiwgImRlbiAiLCAiZGVsICIsICJkZXIgIiwgImRlcyAiLCAiZWwgIiwgImVsLSAiLCAiaW4gJ3QgIiwgImxhICIsICJsZSAiLCAibGVzICIsICJvcCBkZW4gIiwgInRlbiAiLCAidGVyICIsICJ0ZXMgIiwgInZhbiAiLCAidmFuICd0ICIsICJ2YW4gZGUgIiAsICJ2YW4gZGVyICIsICJ2YW4gZGVuICIsICJ2b24gZGVyICIsICJvcCBkZW4gIiwgInVsICIpIAoKZm9yIChpIGluIDE6IGxlbmd0aChsYXN0bmFtZV9kZiRsYXN0bmFtZSkpIHsKICBpZiAoc3VtKHN0cl9kZXRlY3QobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHZvb3J2b2Vnc2VscykpPjApIHsKICAgIGxhc3QgPC0gIGFzLmNoYXJhY3RlcihzdHJfc3BsaXQobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHBhdHRlcm49IiAiLCBzaW1wbGlmeSA9IFRSVUUpKQogICAgbGFzdCA8LSBsYXN0W2xlbmd0aChsYXN0KV0KICAgIGZpcnN0IDwtIGFzLmNoYXJhY3Rlcih1bmxpc3Qoc3Ryc3BsaXQobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHNwbGl0PWxhc3QsIGZpeGVkPVRSVUUpKSkKICAgIGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldIDwtIHBhc3RlKGxhc3QsICIsICIsIGZpcnN0LCBzZXA9IiIpCiAgfQp9CgojZHViYmVsZSBuYW1lbiB2ZXJ3aWpkZXJlbi4gbGV0IG9wIGR1YmJlbGUgbmFtZW4gbWV0IHZvb3J2b2Vnc2VsIHdvcmRlbiBuaWV0IGdlY2xlYW5lZC4gVE8gRE8gCmZvciAoaSBpbiAxOiBsZW5ndGgobGFzdG5hbWVfZGYkbGFzdG5hbWUpKSB7CiAgaWYgKCFzdW0oc3RyX2RldGVjdChsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSwgdm9vcnZvZWdzZWxzKSk+MCkgewogICAgbGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0gPC0gYXMuY2hhcmFjdGVyKHN0cl9zcGxpdChsYXN0bmFtZV9kZiRsYXN0bmFtZVtpXSwgcGF0dGVybj0iICIsIG4gPSAyLCBzaW1wbGlmeSA9IFRSVUUpWywxXSkKICAgIGxhc3RuYW1lX2RmJGxhc3RuYW1lW2ldIDwtIGFzLmNoYXJhY3RlcihzdHJfc3BsaXQobGFzdG5hbWVfZGYkbGFzdG5hbWVbaV0sIHBhdHRlcm49Ii0iLCBuID0gMiwgc2ltcGxpZnkgPSBUUlVFKVssMV0pCiAgfQp9CgpsYXN0bmFtZV9kZiRsYXN0bmFtZTwtIHRyaW13cyhsYXN0bmFtZV9kZiRsYXN0bmFtZSwgd2hpY2ggPSBjKCJyaWdodCIpLCB3aGl0ZXNwYWNlID0gIlsgXHRcclxuXSIpCmxhc3RuYW1lX2RmJGxhc3RuYW1lIDwtIHN0cl9yZXBsYWNlX2FsbChsYXN0bmFtZV9kZiRsYXN0bmFtZSwgIiAiLCAiJTIwIikgI2h0bWwgbGlua3MgaG91ZGVuIG5pZXQgdmFuIHNwYXRpZXMuIAoKbGFzdG5hbWVfZGYkbnAgPC0gIiIKYGBgCgoKYGBge3IsIGV2YWw9RkFMU0V9CiMgTG9hZCByZXF1aXJlZCBwYWNrYWdlcwoKaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICBsaWJyYXJ5KHRpZHl2ZXJzZSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQp9CgppZiAoIXJlcXVpcmUoInJ2ZXN0IiwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgewogIGluc3RhbGwucGFja2FnZXMoInJ2ZXN0IiwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICBsaWJyYXJ5KHJ2ZXN0LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCn0KCiMgaWYgKCFyZXF1aXJlKCJwb2xpdGUiLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7CiMgICBpbnN0YWxsLnBhY2thZ2VzKCJwb2xpdGUiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQojICAgbGlicmFyeShwb2xpdGUsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkKIyB9CiMgICAKIyBpZiAoIXJlcXVpcmUoInhtbDIiLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7CiMgICBpbnN0YWxsLnBhY2thZ2VzKCJ4bWwyIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkKIyAgIGxpYnJhcnkocG9saXRlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCiMgfQpgYGAKCmhpZXIgbWFrZW4gd2UgZGUgbGlua3Mgdm9vciBkZSB3ZWJzaXRlLiAKYGBge3IsIGV2YWw9RkFMU0V9CiMgY3JlYXRpbmcgVVJMczogb3JpZ2luCmxhc3RuYW1lX2RmJG5hbWVfb3JpZ2luIDwtIGlmZWxzZSgobGFzdG5hbWVfZGYkbnA9PSIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoImh0dHBzOi8vd3d3LmNiZ2ZhbWlsaWVuYW1lbi5ubC9uZmIvZGV0YWlsX25hYW0ucGhwP2diYV9uYWFtPSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3I6OnN0cl90b190aXRsZShsYXN0bmFtZV9kZlssIGMoImxhc3RuYW1lIildKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICImZ2JhX25hYW09IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5ncjo6c3RyX3RvX3RpdGxlKGxhc3RuYW1lX2RmWywgYygibGFzdG5hbWUiKV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiJm5mZF9uYWFtPSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3I6OnN0cl90b190aXRsZShsYXN0bmFtZV9kZlssIGMoImxhc3RuYW1lIildKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICImaW5mbz1hbmFseXNlK2VuK3ZlcmtsYXJpbmcmb3BlcmF0b3I9ZXEmdGFhbD0iKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJodHRwczovL3d3dy5jYmdmYW1pbGllbmFtZW4ubmwvbmZiL2RldGFpbF9uYWFtLnBocD9nYmFfbmFhbT0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXN0bmFtZV9kZlssIGMoIm5wMiIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5ncjo6c3RyX3RvX3RpdGxlKGxhc3RuYW1lX2RmWywgYygibGFzdG5hbWUiKV0pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiZnYmFfbmFhbT0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXN0bmFtZV9kZlssIGMoIm5wMiIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5ncjo6c3RyX3RvX3RpdGxlKGxhc3RuYW1lX2RmWywgYygibGFzdG5hbWUiKV0pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiZuZmRfbmFhbT0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdyOjpzdHJfdG9fdGl0bGUobGFzdG5hbWVfZGZbLCBjKCJsYXN0bmFtZSIpXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIlMkMrIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFzdG5hbWVfZGZbLCBjKCJucCIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiZpbmZvPWFuYWx5c2UrZW4rdmVya2xhcmluZyZvcGVyYXRvcj1lcSZ0YWFsPSIpKQpgYGAKCmhpZXIgc2xhYW4gd2UgYWxsZXMgb3AKYGBge3IsIGV2YWw9RkFMU0V9Cm5hbWVfb3JpZ2lubCA8LSBsaXN0KCkKdGFibGVfb3JpZ2lubCA8LSBsaXN0KCkKdGltZSA8LSAwLjEKYGBgCgojIGNydWNpY2FsIHNjcmFwZSBsb29wCmtvbSB2b29ybG9waWcgZ2VlbiBmb3V0ZW4gdGVnZW4sIHRvY2ggYWxsZXMgYWx2YXN0IGluIGVlbiB0cnljYXRjaCBnZXpldC4gCmxldCBvcCBkYXQgaWsgbmlldCBuZXRqZXMgc2NyYXBlLiBkdXMgem9uZGVyIGZ1bmN0aWUgJ3BvbGl0ZScgZ2V6aWVuIGRlIGtsZWluZSBhYW50YWxsZW4gaW4gb256ZSBjdXJzdXMsIG1hZyBkYXQgd21iIHdlbC4gCmBgYHtyLCBldmFsPUZBTFNFfQoKZm9yIChpIGluIDE6bnJvdyhsYXN0bmFtZV9kZikpIHsKICBwcmludChpKQogIFN5cy5zbGVlcCh0aW1lKQogIHRyeUNhdGNoKHsgCiAgICBuYW1lX29yaWdpbmxbW2ldXSAgPC0gcmVhZF9odG1sKGxhc3RuYW1lX2RmW2ksIGMoIm5hbWVfb3JpZ2luIildKQogICAgdGFibGVfb3JpZ2lubFtbaV1dIDwtIG5hbWVfb3JpZ2lubFtbaV1dICU+JSBodG1sX3RhYmxlKCkKICB9LCAKICAgIHdhcm5pbmcgPSBmdW5jdGlvbih3KSB7CiAgICAgICAgY2F0KCJXQVJOSU5HOiIsIGNvbmRpdGlvbk1lc3NhZ2UodyksICJcbiIpICNXQVJOSU5HIG1lc3NhZ2UKICAgIH0sCiAgICBlcnJvcj1mdW5jdGlvbihlKXsKICAgICAgZXJyIDwtIGNvbmRpdGlvbk1lc3NhZ2UoZSkKICAgICAgY2F0KCJFcnJvcjoiLCBjb25kaXRpb25NZXNzYWdlKGUpLCAiXG4iKSAjRVJST1IgbWVzc2FnZQogICAgfSAgCiAgKQp9CmBgYAoKZW4gdmFuYWYgaGllciBpcyBoZXQgZWlnZW5saWprIGFsbGVlbiBtYWFyIG9wc2Nob25lbi4gCmBgYHtyLGV2YWw9RkFMU0V9Cm9yaWdpbl90eHQgPC0gbGlzdCgpCmZvciAoaSBpbiAxOmxlbmd0aChuYW1lX29yaWdpbmwpKSB7CiAgICBvcmlnaW5fdHh0IFtbaV1dIDwtIG5hbWVfb3JpZ2lubFtbaV1dICU+JSBodG1sX3RleHQoKSAlPiUgYXMuY2hhcmFjdGVyKCkKfQoKYGBgCgoKYGBge3IsIGV2YWw9RkFMU0V9CiMgR2V0IG91dCB0aGUgcmVsZXZhbnQgb3JpZ2luIGluZm9ybWF0aW9uIGZyb20gdGhlIHhtbCBsaXN0cwpvcmlnaW5fbG4gPC0gbGlzdCgpCgpmb3IgKGkgaW4gMTpsZW5ndGgobmFtZV9vcmlnaW5sKSkgewogIG9yaWdpbl9sbltbaV1dIDwtIG5hbWVfb3JpZ2lubFtbaV1dICU+JSBodG1sX25vZGVzKCJkaXYiKSAlPiUgcnZlc3Q6Omh0bWxfdGV4dCgpCiAgb3JpZ2luX2xuW1tpXV0gPC0gb3JpZ2luX2xuW1tpXV1bWzNdXQp9CgojIFJlbW92ZSBtZXNzCmZvciAoaSBpbiAxOmxlbmd0aChvcmlnaW5fbG4pKSB7CiAgb3JpZ2luX2xuW1tpXV0gPC0gZ3N1YigiXFx0IiwgIiAiLCBvcmlnaW5fbG5bW2ldXSkKICBvcmlnaW5fbG5bW2ldXSA8LSBnc3ViKCJcXG4iLCAiICIsIG9yaWdpbl9sbltbaV1dKQp9CgojIEZsYXR0ZW4gbmVzdGVkIHN0cnVjdHVyZSBvZiB0aGUgb3JpZ2luIGluZm9ybWF0aW9uCiNvcmlnaW5fbG4gPC0gcmJpbmQoZmxhdHRlbihvcmlnaW5fbG4pKQoKYGBgCgoKYGBge3IgZXh0cmFjdGluZy12ZXJrbGFyaW5nLWtlbm1lcmtlbiwgZXZhbD1GQUxTRX0KCiMgRGV0YWNoaW5nIHRoZSBuYW1lcyBhbmQgb3JpZ2luIGluZm8gZm9yIGVhc2llciBkYXRhIGhhbmRsaW5nCm9yaWdpbiA8LSB1bmxpc3Qob3JpZ2luX2xuKQoKCgpvcmlnaW4gPC0gc3RyX2V4dHJhY3RfYWxsKG9yaWdpbiwgInZhcmlhbnRlbiguKj8pwqkiKQoKIyBPcmlnaW4gaW5mb3JtYXRpb24gaXMgdXN1YWxseSBtZW50aW9uZWQgYWZ0ZXIgInZlcmtsYXJpbmciIG9yICJrZW5tZXJrZW4iCm9yaWdpbiA8LSBzdHJfcmVtb3ZlX2FsbChvcmlnaW4sICJ2YXJpYW50ZW4iKQpvcmlnaW4gPC0gc3RyX3JlbW92ZV9hbGwob3JpZ2luLCAiQ0JHIEJyb25uZW4iKQpvcmlnaW4gPC0gc3RyX3JlbW92ZV9hbGwob3JpZ2luLCAiY2F0YWxvZ3VzIikKb3JpZ2luIDwtIHN0cl9yZW1vdmVfYWxsKG9yaWdpbiwgIsKpIikKCgp2ZXJrbGFyaW5nIDwtIHN0cl9yZW1vdmVfYWxsKG9yaWdpbiwgImtlbm1lcmtlbjooLio/KSQiKQprZW5tZXJrZW4gPC0gc3RyX2V4dHJhY3RfYWxsKG9yaWdpbiwgImtlbm1lcmtlbjooLio/KSQiKQprZW5tZXJrZW4gPC0gc3RyX3JlbW92ZV9hbGwoa2VubWVya2VuLCAic3BlY2lmaWVrZSBjb21wb25lbnRlbjooLio/KSQiKQpzYyA8LSBzdHJfZXh0cmFjdF9hbGwob3JpZ2luLCAic3BlY2lmaWVrZSBjb21wb25lbnRlbjooLio/KSQiKSAjIE5vdCBkaXJlY3RseSByZWxldmFudCB0byB1cywgYnV0IGRvZXMgbWVhbiB0aGF0IHRoZSBuYW1lIGhhcyBhIHdlYnBhZ2UKCgojIE1ha2UgaW50byBhIG5lYXQgZGF0YWZyYW1lIHdpdGggdGhlIG5hbWVzIGF0dGFjaGVkCnZlcmtsYXJpbmcgPC0gdHJpbXdzKHZlcmtsYXJpbmcsIHdoaWNoID0gImJvdGgiKQprZW5tZXJrZW4gPC0gdHJpbXdzKGtlbm1lcmtlbiwgd2hpY2ggPSAiYm90aCIpCnNjIDwtIHRyaW13cyhzYywgd2hpY2ggPSAiYm90aCIpCnZrIDwtIGRhdGEuZnJhbWUoZGF0YV9kZiRsYXN0bmFtZSwgdmVya2xhcmluZywga2VubWVya2VuLCBzYykKCgpgYGAKCgoKIyBTZXBhcmF0aW5nIG5hbWVzIHdpdGggRHV0Y2ggJiB1bmtub3duIG9yaWdpbgpOZXh0LCB3ZSBpZGVudGlmeSB0aG9zZSBuYW1lcyBmb3Igd2hpY2ggbm8gYWRkaXRpb25hbCBpbmZvcm1hdGlvbiB3YXMgZm91bmQuIFRoaXMgaXMgaW1wb3J0YW50IHRvIGRpc3Rpbmd1aXNoIER1dGNoIG5hbWVzIGZyb20gbmFtZXMgd2l0aCB1bmtub3duIG9yaWdpbnMuCgotIER1dGNoIG5hbWVzOiBubyBsYWJlbCBpbmRpY2F0aW5nIHRoYXQgdGhlIG5hbWUgaXMgRHV0Y2gsIGJ1dCBzb21lIG90aGVyIGluZm9ybWF0aW9uIGF2YWlsYWJsZSBvbiBuYW1lIG9yaWdpbgotIFVua25vd24gbmFtZXM6IHdlYiBwYWdlIGNhbm5vdCBiZSBmb3VuZCwgc28gb3JpZ2luIGluZm9ybWF0aW9uIGlzIGVtcHR5LiAKCmBgYHtyIG9yaWdpbi11bmtub3duLCBldmFsPUZBTFNFfQoKIyBJZGVudGlmeSBsYXN0IG5hbWVzIHRoYXQgY291bGQgbm90IGJlIGZvdW5kCnZrIDwtIHZrICU+JQogIG11dGF0ZSh2ZXJrbGFyaW5nID0gaWZlbHNlKHZlcmtsYXJpbmc9PSIiLCAwLCB2ZXJrbGFyaW5nKSwgCiAgICAgICAgIGtlbm1lcmtlbiA9IGlmZWxzZShrZW5tZXJrZW49PSJjaGFyYWN0ZXIoMCkiLCAwLCBrZW5tZXJrZW4pLAogICAgICAgICBzYyA9IGlmZWxzZShzYz09ImNoYXJhY3RlcigwKSIsIDAsIHNjKSwKICAgICAgICAgbm9faW5mbyA9IG5jaGFyKHZlcmtsYXJpbmcpICsgbmNoYXIoa2VubWVya2VuKSArIG5jaGFyKHNjKSkKCnZrIDwtIHZrICU+JQogIG11dGF0ZShub19pbmZvID0gaWZlbHNlKG5vX2luZm89PTMsIDEsIDApLCAKICAgICAgICAgdmVya2xhcmluZyA9IGlmZWxzZSh2ZXJrbGFyaW5nPT0wLCBOQSwgdmVya2xhcmluZyksCiAgICAgICAgIGtlbm1lcmtlbiA9IGlmZWxzZShrZW5tZXJrZW49PTAsIE5BLCBrZW5tZXJrZW4pKQojIElmIHRoZXJlIGlzIG5vIHRleHQgaW4gdmVya2xhcmluZyBvciBrZW5tZXJrZW4sIHRoZSBuYW1lIGNvdWxkIG5vdCBiZSBmb3VuZCBpbiB0aGUgZGF0YWJhc2VzLiAKYGBgCgoKIyBFeHRyYWN0aW5nIHNwZWNpZmljIG9yaWdpbiBpbmZvcm1hdGlvbgpUaGVyZSBhcmUgdGhyZWUgbWFpbiB3YXlzIHRvIGdldCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgb3JpZ2luIG9mIGxhc3QgbmFtZXM6CgoxKSBVbmRlciAia2VubWVya2VuIiwgbGFzdCBuYW1lcyBhcmUgYXNzaWduZWQgY2xpY2thYmxlIHRhZ3MuIFRoZXNlIHRhZ3MgaW5jbHVkZSB1bnNwZWNpZmllZCBmb3JlaWduIG5hbWUgdGFncyAoImFuZGVyZSB0YWFsIiksIGFzIHdlbGwgYXMgc3BlY2lmaWMgZm9yZWlnbiBvcmlnaW5zIG9mIHRoZSBuYW1lICgiRnJhbnNlIG5hYW0iLCAiSW5kaXNjaGUgbmFhbSIpLgotPiBvcmlnaW4xICsgb3JpZ2luNAoKMikgU2V2ZXJhbCBuYW1lcyBoYXZlIG1vcmUgZXh0ZW5zaXZlbHkgd3JpdHRlbiBvdXQgc3RvcmllcyBiZWhpbmQgdGhlIG5hbWUsIHVuZGVyICJ2ZXJrbGFyaW5nIi4gQSBudW1iZXIgb2YgbmFtZXMgY29udGFpbiBkZXRhaWxlZCAoZWl0aGVyIGNvdW50cnktbGV2ZWwgb3IgcmVnaW9uYWwpIG9yaWdpbnMsIHVzdWFsbHkgaW4gdGhlIGZvcm0gb2YgIkRlIG5hYW0gW3h5el0gaXMgYWZrb21zdGlnIHVpdCBbY291bnRyeV0iLiAKLT4gb3JpZ2luMiAKCjMpIFNvbWUgbmFtZXMgaGF2ZSBvcmlnaW4gaW5mb3JtYXRpb24gdW5kZXIgInZlcmtsYXJpbmciIGluIHRoZSBmb3JtIG9mIHRoZSBsaW5ndWlzdGljIG9yaWdpbnMgb2YgdGhlIG5hbWUuIFRoaXMgY2FuIGJlIGNvdW50cnkgc3BlY2lmaWMgKGUuZy4gQ2hpbmVzZSBuYW1lKSwgYnV0IGl0IGNhbiBhbHNvIGFwcGx5IHRvIG11bHRpcGxlIGNvdW50cmllcyB3aGVuIHRoZSBsYW5ndWFnZSBpcyBzcG9rZW4gaW4gbW9yZSB0aGFuIDEgY291bnRyaWVzIChlLmcuIFNwYW5pc2ggbmFtZSkuIAotPiBvcmlnaW4zIAoKYGBge3IgY291bnRyaWVzLWV4dHJhY3QsIGV2YWw9RkFMU0V9CgojIFN0ZXAgMTogZXh0cmFjdGluZyBvcmlnaW4gdGFncyBmcm9tIGtlbm1lcmtlbgp2ayA8LSB2ayAlPiUKICBtdXRhdGUob3JpZ2luMSA9IHN0cl9leHRyYWN0KGtlbm1lcmtlbiwgIls6dXBwZXI6XShbOmxvd2VyOl17Mix9KSBuYWFtIikpCgojIE5vdGU6IHNvbWV0aW1lcyBtdWx0aXBsZSBvcmlnaW5zIGFyZSBtZW50aW9uZWQuIEN1cnJlbnRseSwgSSBvbmx5IGV4dHJhY3QgdGhlIGZpcnN0IG9uZS4gT3RoZXJ3aXNlLCB3ZSBzaG91bGQgdXNlIHN0cl9leHRyYWN0X2FsbC4gCgoKCiMgU3RlcCAyOiBleHRyYWN0aW5nIG9yaWdpbiBpbmZvIGZyb20gdmVya2xhcmluZyAKdmsgPC0gdmsgJT4lCiAgbXV0YXRlKG9yaWdpbjIgPSBpZmVsc2UoYXMubnVtZXJpYyhzdHJfZGV0ZWN0KHZlcmtsYXJpbmcsICJhZmtvbXN0aWcgdWl0IikpID09IDEsIAogICAgICAgICBzdHJfcmVtb3ZlKHZlcmtsYXJpbmcsICIuKmFma29tc3RpZyB1aXQiKSwgTkEpKQoKCiMgU3RlcCAzOiBleHRyYWN0aW5nIGFkZGl0aW9uYWwgb3JpZ2luIGluZm8gZnJvbSB2ZXJrbGFyaW5nCnZrIDwtIHZrICU+JQogIG11dGF0ZShvcmlnaW4zID0gc3RyX2V4dHJhY3QodmVya2xhcmluZywgIls6dXBwZXI6XShbOmxvd2VyOl17Mix9KSAoYWNodGVyKT8oZmFtaWxpZSk/KGJlcm9lcHMpP25hYW0iKSkKCgoKIyBGaW5hbGx5LCB3ZSBjbGVhbiB1cCB0aGUgb3JpZ2luIGluZm9ybWF0aW9uIGV4dHJhY3RlZCBhYm92ZQoKIyBPcmlnaW4xOiBhbHJlYWR5IG5lYXQKdmskb3JpZ2luMSA8LSBzdHJfcmVtb3ZlKHZrJG9yaWdpbjEsICJKb29kc2UgbmFhbSIpICMgY2FuIGJlIER1dGNoICYgbm9uLUR1dGNoCgojIE9yaWdpbjI6IG1lc3N5CnZrJG9yaWdpbjIgPC0gc3RyX3JlbW92ZSh2ayRvcmlnaW4yLCAiXFwuLioiKSAjIHJlbW92ZSBleHRyYSBpbmZvIGluIHRoZSBmb2xsb3dpbmcgc2VudGVuY2UgCnZrJG9yaWdpbjIgPC0gc3RyX3JlbW92ZSh2ayRvcmlnaW4yLCAiXFw7LioiKSAjIHJlbW92ZSBleHRyYSBpbmZvIGluIHRoZSBmb2xsb3dpbmcgc2VudGVuY2UgCnZrJG9yaWdpbjIgPC0gc3RyX3JlbW92ZSh2ayRvcmlnaW4yLCAiXFwoLioiKSAjIHJlbW92ZSBleHRyYSBpbmZvIGluIHRoZSBmb2xsb3dpbmcgc2VudGVuY2UgCgoKdmskZHBnIDwtIGFzLm51bWVyaWMoc3RyX2RldGVjdCh2ayRvcmlnaW4yLCAiKGRvcnApfChwbGFhdHMpfChnZW1lZW50ZSl8KGdyYWFmc2NoYXApfChzdGFkKXwoZGVlbCl8KEZyaWVzbGFuZCkiKSkgIyBvcmlnaW4gaW5mbyB0b28gcmVnaW9uYWwgCnZrIDwtIHZrICU+JSBtdXRhdGUob3JpZ2luMiA9IGlmZWxzZSgoZHBnPT0xKSwgTkEsIG9yaWdpbjIpKSAjIHJlbW92aW5nIHJlZ2lvbmFsIG9yaWdpbiBpbmZvCnZrIDwtIHN1YnNldCh2aywgc2VsZWN0ID0gLWRwZykgIyByZW1vdmluZyBpbnRlcm1lZGlhdGUgdmFyaWFibGUKCiMgU29tZXRpbWVzLCB0aGVyZSB3ZXJlIG11bHRpcGxlIGNvdW50cmllcyBtZW50aW9uZWQuIFRha2Ugb25seSB0aGUgZmlyc3Q6CnZrJG9yaWdpbjIgPC0gc3RyX3JlbW92ZSh2ayRvcmlnaW4yLCAiXFwsLioiKSAjIE9ubHkgZmlyc3QKdmskb3JpZ2luMiA8LSBzdHJfcmVtb3ZlKHZrJG9yaWdpbjIsICJcXHMoZW4pLioiKSAjIE9ubHkgZmlyc3QgCnZrJG9yaWdpbjIgPC0gc3RyX3JlbW92ZSh2ayRvcmlnaW4yLCAiXFxzKG9mKS4qIikgIyBPbmx5IGZpcnN0IAoKCiMgT3JpZ2luMzogcHJldHR5IG5lYXQKdmskb3JpZ2luMyA8LSBzdHJfcmVtb3ZlKHZrJG9yaWdpbjMsICJEKGkpP2UoemUpPyAoZmFtaWxpZSk/KGFjaHRlcik/KGJlcm9lcHMpP25hYW0iKSAjIHNsaXBwZWQgdGhyb3VnaCB0aGUgcmVnZXgKdmskb3JpZ2luMyA8LSBzdHJfcmVtb3ZlKHZrJG9yaWdpbjMsICJFZW4gKGZhbWlsaWUpPyhhY2h0ZXIpPyhiZXJvZXBzKT9uYWFtIikgIyBzbGlwcGVkIHRocm91Z2ggdGhlIHJlZ2V4CnZrJG9yaWdpbjMgPC0gc3RyX3JlbW92ZSh2ayRvcmlnaW4zLCAiWmlqbiAoZmFtaWxpZSk/KGFjaHRlcik/KGJlcm9lcHMpP25hYW0iKSAjIHNsaXBwZWQgdGhyb3VnaCB0aGUgcmVnZXgKdmskb3JpZ2luMyA8LSBzdHJfcmVtb3ZlKHZrJG9yaWdpbjMsICJBbHMgKGZhbWlsaWUpPyhhY2h0ZXIpPyhiZXJvZXBzKT9uYWFtIikgIyBzbGlwcGVkIHRocm91Z2ggdGhlIHJlZ2V4CnZrJG9yaWdpbjMgPC0gc3RyX3JlbW92ZSh2ayRvcmlnaW4zLCAiSm9vZHNlIChmYW1pbGllKT8oYWNodGVyKT9uYWFtIikKdmskb3JpZ2luMyA8LSBzdHJfcmVtb3ZlKHZrJG9yaWdpbjMsICJCaWpiZWxzZSAoZmFtaWxpZSk/KGFjaHRlcik/bmFhbSIpCgoKIyBTZXR0aW5nIGVtcHR5IG9yaWdpbiB2YXJpYWJsZXMgdG8gTkEgKER1dGNoIG9yIHVuZm91bmQgZm9yZWlnbikKdmsgPC0gdmsgJT4lCiAgbXV0YXRlKG9yaWdpbjEgPSBhcy5jaGFyYWN0ZXIoaWZlbHNlKG9yaWdpbjE9PSIifG9yaWdpbjE9PSJjaGFyYWN0ZXIoMCkiLCBOQSwgb3JpZ2luMSkpLAogICAgICAgICBvcmlnaW4yID0gYXMuY2hhcmFjdGVyKGlmZWxzZShvcmlnaW4yPT0iInxvcmlnaW4yPT0iY2hhcmFjdGVyKDApIiwgTkEsIG9yaWdpbjIpKSwKICAgICAgICAgb3JpZ2luMyA9IGFzLmNoYXJhY3RlcihpZmVsc2Uob3JpZ2luMz09IiJ8b3JpZ2luMz09ImNoYXJhY3RlcigwKSIsIE5BLCBvcmlnaW4zKSkpCgoKCiMgRmluYWxseSwgdGhlIHRhZyAiYW5kZXJlIHRhYWwiIHdhcyB1c2VkIHRvIGRpc3Rpbmd1aXNoIGZvcmVpZ24gbmFtZXMgb2YgdW5rbm93biBvcmlnaW4gZnJvbSBrbm93biBEdXRjaCBuYW1lcy4gCnZrIDwtIHZrICU+JQogIG11dGF0ZShvcmlnaW40ID0gaWZlbHNlKChhcy5udW1lcmljKHN0cl9kZXRlY3Qoa2VubWVya2VuLCAiYW5kZXJlIHRhYWwiKSk9PTEpLCAibm9uLUR1dGNoIiwgTkEpKQoKCmBgYAoKSWsgem91IGFsbGVzIHdhYXIgYG5vX2luZm9gIG9wIDEgc3RhYXQgb2Ygd2FhciBgb3JpZ2luNGAgb3AgIm5vbi1EdXRjaCIgc3RhYXQgY29kZXJlbiBhbHMgYnVpdGVubGFuZHMhIAoKYGBge3IsIGV2YWw9RkFMU0V9CnNhdmUodmssIGZpbGU9InZrX2RhdGEuUkRhdGEiKQpgYGAKCiMgTWFraW5nIFJTaWVuYSBmcmFtZQoKSSBzZWUgdGhhdCBpIGRvbnQgaGF2ZSB0aGUgZGF0YSBzYXZlZCBmb3IgdGhlIHB1YmxpY2F0aW9ucyBmcm9tIGRhdGEgc2NpZW5jZSwgYnV0IEkgZG8gbm90IGhhdmUgdGltZSB0byBmaXggdGhhdCBub3cuIFNvIEkgd2lsbCBzdGFydCBpbiBjbGFzcyAKIyBuZXR3b3JrIGJhc2VkIG9uIHB1YmxpY2F0aW9ucwoKTkFUVVJBTExZLCBUSElTIElTIEpVU1QgQU4gRVhBTVBMRS4gZk9SIHJTSUVOQSwgWU9VIE5FRUQgQVQgTEVBU1QgMyBORVRXT1JLUy4gdEhVUyBZT1UgSEFWRSBUTyBUV0VBSyBUSEUgUEVSSU9EIEFTIFlPVSBERUVNIEZJVC4gCgpgYGB7ciwgZXZhbD1GQUxTRX0KbGlicmFyeShzdHJpbmdyKQoKI2VtcHR5IGFkamFjZW5jeSBtYXRyaXggZm9yIHRoZSB5ZWFycyAyMDAxLTIwMTAKbmV0d29yazIwMTBfMjAxMiA8LSBtYXRyaXgoTkEsIG5yb3c9bnJvdyhzb2NfZGYpLCBuY29sPW5yb3coc29jX2RmKSkKbmV0d29yazIwMTNfMjAxNSA8LSBtYXRyaXgoTkEsIG5yb3c9bnJvdyhzb2NfZGYpLCBuY29sPW5yb3coc29jX2RmKSkKbmV0d29yazIwMTZfMjAxOCA8LSBtYXRyaXgoTkEsIG5yb3c9bnJvdyhzb2NfZGYpLCBuY29sPW5yb3coc29jX2RmKSkKbmV0d29yazIwMTlfMjAyMSA8LSBtYXRyaXgoTkEsIG5yb3c9bnJvdyhzb2NfZGYpLCBuY29sPW5yb3coc29jX2RmKSkKCgojc2VsZWN0IHB1YmxpY2F0aW9ucyBvZiB0aGUgY29ycmVzcG9uZGluZyB0aW1lIGVyYQpwdWJzX3NlbCA8LSBzb2NfZGZfcHVibGljYXRpb25zICU+JQogICAgICAgICAgICAgIG11dGF0ZShhdXRob3IgPSB0b2xvd2VyKGF1dGhvcikpICU+JQogICAgICAgICAgICAgIGZpbHRlcih5ZWFyPj0yMDEwICYgeWVhcjw9MjAxMikKI2ZpbGwgdGhlIG1hdHJpeApmb3IgKGVnbyBpbiAxOiBucm93KHNvY19kZikpIHsKICBuYW1lX2VnbyA8LSBzb2NfZGYkbGFzdF9uYW1lW2Vnb10gI3doaWNoIGVnbz8gCiAgcHVic19zZWwyIDwtIHB1YnNfc2VsW3N0cl9kZXRlY3QocHVic19zZWwkYXV0aG9yLCBuYW1lX2VnbyksXSAjcHVibGljYXRpb25zIG9mIGVnbwogIGZvciAoYWx0ZXIgaW4gMTpucm93KHNvY19kZikpewogICAgbmFtZV9hbHRlciA8LSBzb2NfZGYkbGFzdF9uYW1lW2FsdGVyXSAjd2hpY2ggYWx0ZXI/IAogICAgbmV0d29yazIwMTBfMjAxMltlZ28sYWx0ZXJdIDwtIGFzLm51bWVyaWMoc3VtKHN0cl9kZXRlY3QocHVic19zZWwyJGF1dGhvciwgbmFtZV9hbHRlcikpID4gMSkgICNkaWQgYWx0ZXIgcHVibGlzaCB3aXRoIGVnbwogIH0KfQoKI3NlbGVjdCBwdWJsaWNhdGlvbnMgb2YgdGhlIGNvcnJlc3BvbmRpbmcgdGltZSBlcmEKcHVic19zZWwgPC0gc29jX2RmX3B1YmxpY2F0aW9ucyAlPiUKICAgICAgICAgICAgICBtdXRhdGUoYXV0aG9yID0gdG9sb3dlcihhdXRob3IpKSAlPiUKICAgICAgICAgICAgICBmaWx0ZXIoeWVhcj49MjAxMyAmIHllYXI8PTIwMTUpCiNmaWxsIHRoZSBtYXRyaXgKZm9yIChlZ28gaW4gMTogbnJvdyhzb2NfZGYpKSB7CiAgbmFtZV9lZ28gPC0gc29jX2RmJGxhc3RfbmFtZVtlZ29dICN3aGljaCBlZ28/IAogIHB1YnNfc2VsMiA8LSBwdWJzX3NlbFtzdHJfZGV0ZWN0KHB1YnNfc2VsJGF1dGhvciwgbmFtZV9lZ28pLF0gI3B1YmxpY2F0aW9ucyBvZiBlZ28KICBmb3IgKGFsdGVyIGluIDE6bnJvdyhzb2NfZGYpKXsKICAgIG5hbWVfYWx0ZXIgPC0gc29jX2RmJGxhc3RfbmFtZVthbHRlcl0gI3doaWNoIGFsdGVyPyAKICAgIG5ldHdvcmsyMDEzXzIwMTVbZWdvLGFsdGVyXSA8LSBhcy5udW1lcmljKHN1bShzdHJfZGV0ZWN0KHB1YnNfc2VsMiRhdXRob3IsIG5hbWVfYWx0ZXIpKSA+IDEpICNkaWQgYWx0ZXIgcHVibGlzaCB3aXRoIGVnbwogIH0KfQoKI3NlbGVjdCBwdWJsaWNhdGlvbnMgb2YgdGhlIGNvcnJlc3BvbmRpbmcgdGltZSBlcmEKcHVic19zZWwgPC0gc29jX2RmX3B1YmxpY2F0aW9ucyAlPiUKICAgICAgICAgICAgICBtdXRhdGUoYXV0aG9yID0gdG9sb3dlcihhdXRob3IpKSAlPiUKICAgICAgICAgICAgICBmaWx0ZXIoeWVhcj49MjAxNiAmIHllYXI8PTIwMTgpCiNmaWxsIHRoZSBtYXRyaXgKZm9yIChlZ28gaW4gMTogbnJvdyhzb2NfZGYpKSB7CiAgbmFtZV9lZ28gPC0gc29jX2RmJGxhc3RfbmFtZVtlZ29dICN3aGljaCBlZ28/IAogIHB1YnNfc2VsMiA8LSBwdWJzX3NlbFtzdHJfZGV0ZWN0KHB1YnNfc2VsJGF1dGhvciwgbmFtZV9lZ28pLF0gI3B1YmxpY2F0aW9ucyBvZiBlZ28KICBmb3IgKGFsdGVyIGluIDE6bnJvdyhzb2NfZGYpKXsKICAgIG5hbWVfYWx0ZXIgPC0gc29jX2RmJGxhc3RfbmFtZVthbHRlcl0gI3doaWNoIGFsdGVyPyAKICAgIG5ldHdvcmsyMDE2XzIwMThbZWdvLGFsdGVyXSA8LSBhcy5udW1lcmljKHN1bShzdHJfZGV0ZWN0KHB1YnNfc2VsMiRhdXRob3IsIG5hbWVfYWx0ZXIpKSA+IDEpICNkaWQgYWx0ZXIgcHVibGlzaCB3aXRoIGVnbwogIH0KfQoKI3NlbGVjdCBwdWJsaWNhdGlvbnMgb2YgdGhlIGNvcnJlc3BvbmRpbmcgdGltZSBlcmEKcHVic19zZWwgPC0gc29jX2RmX3B1YmxpY2F0aW9ucyAlPiUKICAgICAgICAgICAgICBtdXRhdGUoYXV0aG9yID0gdG9sb3dlcihhdXRob3IpKSAlPiUKICAgICAgICAgICAgICBmaWx0ZXIoeWVhcj49MjAxOSAmIHllYXI8PTIwMjEpCiNmaWxsIHRoZSBtYXRyaXgKZm9yIChlZ28gaW4gMTogbnJvdyhzb2NfZGYpKSB7CiAgbmFtZV9lZ28gPC0gc29jX2RmJGxhc3RfbmFtZVtlZ29dICN3aGljaCBlZ28/IAogIHB1YnNfc2VsMiA8LSBwdWJzX3NlbFtzdHJfZGV0ZWN0KHB1YnNfc2VsJGF1dGhvciwgbmFtZV9lZ28pLF0gI3B1YmxpY2F0aW9ucyBvZiBlZ28KICBmb3IgKGFsdGVyIGluIDE6bnJvdyhzb2NfZGYpKXsKICAgIG5hbWVfYWx0ZXIgPC0gc29jX2RmJGxhc3RfbmFtZVthbHRlcl0gI3doaWNoIGFsdGVyPyAKICAgIG5ldHdvcmsyMDE5XzIwMjFbZWdvLGFsdGVyXSA8LSBhcy5udW1lcmljKHN1bShzdHJfZGV0ZWN0KHB1YnNfc2VsMiRhdXRob3IsIG5hbWVfYWx0ZXIpKSA+IDEpICNkaWQgYWx0ZXIgcHVibGlzaCB3aXRoIGVnbwogIH0KfQoKYyhkaW0obmV0d29yazIwMTBfMjAxMiksNCkKbmV0X2FycmF5IDwtIGFycmF5KGRhdGEgPSBjKG5ldHdvcmsyMDEwXzIwMTIsIG5ldHdvcmsyMDEzXzIwMTUsIG5ldHdvcmsyMDE2XzIwMTgsIG5ldHdvcmsyMDE5XzIwMjEpLCBkaW09YyhkaW0obmV0d29yazIwMTBfMjAxMiksNCkpCgpuZXRfYXJyYXlbMSwxLDFdCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQpzYXZlKG5ldF9hcnJheSwgZmlsZT0ic29jX25ldF9hcnJheS5SRGF0YSIpCmBgYAoKCg==