library(knitr)

knitr::opts_chunk$set(echo = TRUE)
opts_chunk$set(tidy.opts=list(width.cutoff=100),tidy=TRUE, warning = FALSE, message = FALSE,comment = "#>", cache=TRUE, class.source=c("test"), class.output=c("test2"))
options(width = 100)
rgl::setupKnitr()



colorize <- function(x, color) {sprintf("<span style='color: %s;'>%s</span>", color, x) }
klippy::klippy(position = c('top', 'right'))
#klippy::klippy(color = 'darkred')
#klippy::klippy(tooltip_message = 'Click to copy', tooltip_success = 'Done')
#start with clean workspace 
rm(list=ls())
getwd()
#> [1] "C:/Users/ninab/OneDrive/Documenten/GitHub/labjournal"

1 packages

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

2 saving webpage

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

#head(soc_staff)
#class(soc_staff)

3 making data with names

3.1 selecting the table with names

soc_staff <- soc_staff %>%
    rvest::html_nodes("body") %>%
    xml2::xml_find_all("//td") %>%
    rvest::html_text()

3.2 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)] 
head(soc_names)

3.3 selecting only the even rowes with the expertise

soc_experts <- soc_staff[!fodd(1:nstaf)]
head(soc_experts)

3.4 combining names and expertise in soc_df

soc_df <- data.frame(cbind(soc_names, soc_experts)) 
save(soc_df, file = "names_experts.RData")
load("names_experts.RData")

4 Cleaning the dataset

4.1 delete rows without name info

# inspect again, and remove the rows we don't need (check for yourself to be certain!)

delrows <- which(soc_df$soc_names == "Staff:" | soc_df$soc_names == "PhD:" | soc_df$soc_names == "External PhD:" |
    soc_df$soc_names == "Guest researchers:" | soc_df$soc_names == "Other researchers:")

soc_df <- soc_df[-delrows, ]

4.2 cleaning the names

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

4.3 Loose double names

# trimws looses all spacing before and after (if you specify 'both') a character string
soc_df$last_name <- trimws(soc_df$last_name, which = c("both"), whitespace = "[ \t\r\n]")
soc_df$first_name <- trimws(soc_df$first_name, which = c("both"), whitespace = "[ \t\r\n]")

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

#removing strange characters
soc_df$first_name <- iconv(soc_df$first_name, from = 'UTF-8', to = 'ASCII//TRANSLIT')

soc_df$soc_experts <- trimws(soc_df$soc_experts, which = c("both"), whitespace = "[ \t\r\n]")
soc_df$soc_names <- trimws(soc_df$soc_names, which = c("both"), whitespace = "[ \t\r\n]")
soc_df$first_name
#>  [1] "ronald"     "katia"      "hidde"      "lonneke"    "lieselotte" "rob"        "maurice"   
#>  [8] "nella"      "saskia"     "margriet"   "remco"      "bas"        "judith"     "gerbert"   
#> [15] "roza"       "michael"    "peer"       "niels"      "jochem"     "ellen"      "mark"      
#> [22] "maarten"    "carlijn"    "rob"        "mustafa"    "aysegul"    "inge"       "thijmen"   
#> [29] "rachel"     "nik"        "renae"      "maikel"     "carly"      "anne"       "katrin"    
#> [36] "klara"      "marlou"     "sara"       "janos"      "jansje"     "elize"      "tijmen"    
#> [43] "elissa"     "carl"       "paul"       "malou"
save(soc_df, file="soc_df_s1.RData")

5 adding gender to dataset

5.1 fgender

fgender <- function(soc_df, me, file=NULL) {

####################################
# Author: Bas Hofstra, Anne Maaike Mulders, Jochem Tolsma
# DAte:   13-10-2021, last edit: 22-09-2022
# Tasks:  - assign gender baed on name
#         - Adapted from Rense Corten code April 2021
####################################


#Input: 
#  - firstname_df: a data.frame with a column named firstname  
#  - me: a character vector introducing yourself: e.g. "J Tolsma, Radboud University"
#  - file: location and name of file to be saved. 
  
#------------------------------------------------------------------------------------
# Load required packages

if (!require("tidyverse", character.only = TRUE)) {
  install.packages("tidyverse", dependencies = TRUE)
  library(tidyverse, character.only = TRUE)
}

if (!require("rvest", character.only = TRUE)) {
  install.packages("rvest", dependencies = TRUE)
  library(rvest, character.only = TRUE)
}

if (!require("polite", character.only = TRUE)) {
  install.packages("polite", dependencies = TRUE)
  library(polite, character.only = TRUE)
}



# make links to scrape
soc_df$name_url <- paste0("https://www.meertens.knaw.nl/nvb/naam/is/", soc_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()
soc_df$gender <- NA

  for (i in 1:nrow(soc_df)) {
    print(i)
    name_list[[i]] <- fnames(soc_df[i, c("name_url")])
    # extract name frequency table and gender info
    table_list[[i]] <- name_list[[i]] %>% html_table()
    
    table_list[[i]][[1]][table_list[[i]][[1]]=="--"] <- "0"
    
    if (as.numeric(table_list[[i]][[1]]$X3[2]) > as.numeric(table_list[[i]][[1]]$X3[6])) {
      soc_df$gender[i] <- "male" } else {
        soc_df$gender[i] <- "female"
      }
    
    if (!is.null(file)) (save(soc_df, file=file))
    
    }
  return(soc_df)
}

5.2 gender to soc_df

soc_df %>% mutate(firstname=first_name) -> soc_df

soc_df$firstname
soc_df <- fgender(soc_df, me="Jochem Tolsma, RU/RUG")
save(soc_df, file="soc_df_s2.RData")
load("soc_df_s2.RData")

5.3 affiliation

# set affiliation to radboud, comes in handy for querying google scholar
soc_df$affiliation <- "radboud university"

6 harvesting data from google scholar

#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)
}

6.1 scholars id.

Remove staff members without scholar ids.

soc_df$gs_id <- ""

for (i in 1:nrow(soc_df)) {
  print(i)
  time <- runif(1, 0, 1)
  Sys.sleep(time)

  tryCatch({
     soc_df[i,c("gs_id")] <- get_scholar_id_fix(last_name = soc_df[i, c("last_name")], # so search on last_name of staff (third column)
                                             first_name = soc_df[i, c("first_name")],  # search on first_name of staff (fourth column)
                                             affiliation = soc_df[i,c("affiliation")]) # search on affiliation of each staff (fifth column)

    }, error=function(e){cat("ERROR :", conditionMessage(e), "\n")}) # continue on error, but print the error
  }

# remove those without pubs from the df
# seems we're left with about 34 sociology staff members!
soc_df_copy <- soc_df #just to also have the data with staff without scholar_id
soc_df <- soc_df[!soc_df$gs_id == "", ]
save(soc_df, file="soc_df_s3.RData")
save(soc_df_copy, file="soc_df_copy.RData")
load("soc_df_s3.RData")

7 publications and profiles

We save the publications and info of the scholar profiles in new objects.

soc_list_profiles <- list()  # first we create an empty list that we then fill up with the for loop
soc_list_publications <- list()

time <- 1 # I placed the waiting time outside the loop
i <- 1 # Our loop iterator is now a variable. This means I can change it within a while loop. Using a for loop you cant change your iterator in the loop itself. 

while (i <= nrow(soc_df)) {
    print(i)
    Sys.sleep(time)

   
    tryCatch({
    #In this part of the tryCatch function you put all the stuff you want to do in the loop.
    # note how you call different elements in a list '[[]]', fill in the i-th element
    soc_list_profiles[[i]] <- get_profile(soc_df[i, c("gs_id")])  # Note how we call row i (remember how to call rows in a DF/Matrix) and then the associated scholar id
    soc_list_publications[[i]] <- get_publications(soc_df[i, c("gs_id")])
    soc_list_publications[[i]][, c("gs_id")] <- soc_df[i, c("gs_id")]  # note that we again attach an id
    # so both functions here call the entire profile and pubs for an author, based on google
    # scholar ids
    i <- i + 1 #IMPORTANT, YOU NEED TO TELL THE WHILE LOOP THAT YOUR ITERATOR HAS TO BE INCREASED
    },
      warning = function(w) {
        cat("WARNING:", conditionMessage(w), "\n") #WARNING message
        i <<- i + 1}, #BUT WE DO WANT TO CONTINUE. NOTE THE DOUBLE << THIS IS BECAUSE I WANT TO CHANGE A VARIABLE WHICH EXISTS OUTSIDE THE WARNING FUNCTION
      error =function(e) {
        time <<- min(time *10, 3600*2)
        cat("Error:", conditionMessage(e), "\n") #ERROR message
        cat("sleep time:", time,  "\n")
        cat("ik zit in loop", i)
        #AFTER THE NEW SLEEP TIME, WE TRY AGAIN, WE THEREFORE DO NOT UPDATE i. ideally you also want to have some break option. And maybe you also want to save your data when you hit a time out error. 
      })
   
 
}
# The 36 RU sociology scholars publish ~3000 papers
soc_df_publications <- bind_rows(soc_list_publications)
save(soc_df_publications, file="soc_df_publications.RData")
save(soc_list_profiles, file= "soc_list_profiles.RData")
load("soc_df_publications.RData")
load("soc_list_profiles.RData")

7.1 put the info of the profiles in our data set of staff members soc_df

soc_profiles_df <- list()


for (i in 1:length(soc_list_profiles)) {
    # soc_profiles_df[[i]] <- data.frame(t(unlist(soc_list_profiles[[i]][1:8]))) #some annyoing
    # data handling
  if (!is.null(soc_list_profiles[[i]])) {
    soc_profiles_df[[i]] <- unlist(soc_list_profiles[[i]][1:8])
    soc_profiles_df[[i]] <- data.frame(soc_profiles_df[[i]])
    soc_profiles_df[[i]] <- t(soc_profiles_df[[i]])
    row.names(soc_profiles_df[[i]]) <- NULL
    soc_profiles_df[[i]] <- data.frame(soc_profiles_df[[i]])
  }
}

#soc_profiles_df

soc_profiles_df2 <- bind_rows(soc_profiles_df)
soc_df <- left_join(soc_df, soc_profiles_df2, by = c(gs_id = "id"))  # merge data with soc_df
soc_df  # notice all the new information we were able to get from the scholar profiles!
save(soc_df, file="soc_df_s4.RData")
load("soc_df_s4.RData")

8 citation history

# get citation history of a scholar
soc_staff_cit <- list()


time <- 1 # I placed the waiting time outside the loop
i <- 1 # Our loop iterator is now a variable. This means I can change it within a while loop. Using a for loop you cant change your iterator in the loop itself. 

while (i <= nrow(soc_df)) {
    print(i)
    Sys.sleep(time)

    tryCatch({
      soc_staff_cit[[i]] <- get_citation_history(soc_df[i, c("gs_id")])
        if (nrow(soc_staff_cit[[i]]) > 0) {
          soc_staff_cit[[i]][, c("gs_id")] <- soc_df[i, c("gs_id")]  # again attach the gs_id as third column
        }
    i <- i + 1
    },
      warning = function(w) {
        cat("WARNING:", conditionMessage(w), "\n") #WARNING message
        i <<- i + 1}, #BUT WE DO WANT TO CONTINUE. NOTE THE DOUBLE << THIS IS BECAUSE I WANT TO CHANGE A VARIABLE WHICH EXISTS OUTSIDE THE WARNING FUNCTION
      error =function(e) {
        time <<- min(time *10, 3600*2)
        cat("Error:", conditionMessage(e), "\n") #ERROR message
        cat("sleep time:", time,  "\n")
        cat("ik zit in loop", i)
        #AFTER THE NEW SLEEP TIME, WE TRY AGAIN, WE THEREFORE DO NOT UPDATE i. ideally you also want to have some break option. And maybe you also want to save your data when you hit a time out error. 
      })

    
}
soc_staff_cit <- bind_rows(soc_staff_cit)
save(soc_staff_cit, file="soc_staff_cit.RData")
load("soc_staff_cit.RData")

9 collaborators

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)

}
# input a google scholar id and a 1 (if you want to find collabs) or 0 (only extracting names)

soc_collabs <- list()
for (i in 1:nrow(soc_df)) {
    print(i)
    time <- runif(1, 0, 1)
    Sys.sleep(time)

    soc_collabs[[i]] <- fcollabs(soc_df[i, c("gs_id")], 1)

}

soc_collabs <- bind_rows(soc_collabs)  # bind rows, get the unique ones!
soc_collabs_unique <- unique(soc_collabs[, 3]) 
soc_collabs_unique <- soc_collabs_unique[!is.na(soc_collabs_unique)]
save(soc_collabs, file = "soc_df_collabs1.RData")  # you notice this takes a while, so we save the data here

10 network based on publications.

Deze tijdsintervallen zijn waarschijnlijk te groot!

load("soc_df_collabs1.RData")
library(stringr)

#empty adjacency matrix for the years 2001-2007
network2001_2007 <- 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>2000 & year<2008)

save(pubs_sel, file = "soc_pubs_sel_2001_2007.RData")

#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? 
    network2001_2007[ego,alter] <- sum(str_detect(pubs_sel2$author, name_alter)) > 1 #did alter publish with ego
  }
}

save(network2001_2007, file = "soc_network2001_2007.RData")
library(stringr)

#empty adjacency matrix for the years 2008-2014
network2008_2014 <- 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>2007 & year<2015)

save(pubs_sel, file = "soc_pubs_sel_2008_2014.RData")

#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? 
    network2008_2014[ego,alter] <- sum(str_detect(pubs_sel2$author, name_alter)) > 1 #did alter publish with ego
  }
}

save(network2008_2014, file = "soc_network2008_2014.RData")
library(stringr)

#empty adjacency matrix for the years 2015-2021
network2015_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>2014 & year<2022)

save(pubs_sel, file = "soc_pubs_sel_2015_2021.RData")

#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? 
    network2015_2021[ego,alter] <- sum(str_detect(pubs_sel2$author, name_alter)) > 1 #did alter publish with ego
  }
}

save(network2015_2021, file = "soc_network2015_2021.RData")
LS0tDQp0aXRsZTogIldlYnNjcmFwaW5nIGRhdGEgc29jaW9sb2d5IFJhZGJvdWQgVW5pdmVyc2l0eSINCiNiaWJsaW9ncmFwaHk6IHJlZmVyZW5jZXMuYmliDQphdXRob3I6ICJOaW5hIEJyYW50ZW4iDQotLS0NCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQpsaWJyYXJ5KGtuaXRyKQ0KDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpvcHRzX2NodW5rJHNldCh0aWR5Lm9wdHM9bGlzdCh3aWR0aC5jdXRvZmY9MTAwKSx0aWR5PVRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLGNvbW1lbnQgPSAiIz4iLCBjYWNoZT1UUlVFLCBjbGFzcy5zb3VyY2U9YygidGVzdCIpLCBjbGFzcy5vdXRwdXQ9YygidGVzdDIiKSkNCm9wdGlvbnMod2lkdGggPSAxMDApDQpyZ2w6OnNldHVwS25pdHIoKQ0KDQoNCg0KY29sb3JpemUgPC0gZnVuY3Rpb24oeCwgY29sb3IpIHtzcHJpbnRmKCI8c3BhbiBzdHlsZT0nY29sb3I6ICVzOyc+JXM8L3NwYW4+IiwgY29sb3IsIHgpIH0NCg0KYGBgDQoNCmBgYHtyIGtsaXBweSwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQprbGlwcHk6OmtsaXBweShwb3NpdGlvbiA9IGMoJ3RvcCcsICdyaWdodCcpKQ0KI2tsaXBweTo6a2xpcHB5KGNvbG9yID0gJ2RhcmtyZWQnKQ0KI2tsaXBweTo6a2xpcHB5KHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgdG9vbHRpcF9zdWNjZXNzID0gJ0RvbmUnKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiNzdGFydCB3aXRoIGNsZWFuIHdvcmtzcGFjZSANCnJtKGxpc3Q9bHMoKSkNCmdldHdkKCkNCmBgYA0KDQojIHBhY2thZ2VzDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJkYXRhLnRhYmxlIikgDQpsaWJyYXJ5KGRhdGEudGFibGUpICMgbWFpbmx5IGZvciBmYXN0ZXIgZGF0YSBoYW5kbGluZw0KbGlicmFyeSh0aWR5dmVyc2UpICMgSSBhc3N1bWUgeW91IGFscmVhZHkgaW5zdGFsbGVkIHRoaXMgb25lIQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJodHRyIikgIyB3ZSBkb24ndCBuZWVkIHRoaXMgZm9yIG5vdw0KIyByZXF1aXJlKGh0dHIpDQojaW5zdGFsbC5wYWNrYWdlcygieG1sMiIpDQpyZXF1aXJlKHhtbDIpDQojaW5zdGFsbC5wYWNrYWdlcygicnZlc3QiKQ0KcmVxdWlyZShydmVzdCkNCiNpbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpDQpyZXF1aXJlKGRldnRvb2xzKQ0KIyBOb3RlIHdlJ3JlIGRvaW5nIHNvbWV0aGluZyBkaWZmZXJlbnQgaGVyZS4gV2UncmUgaW5zdGFsbGluZyBhICpsYXRlc3QqIHZlcnNpb24gZGlyZWN0bHkgZnJvbSBHaXRIdWINCiMgVGhpcyBpcyBiZWNhdXNlIHRoZSByZWxlYXNlZCB2ZXJzaW9uIG9mIHRoaXMgcGFja2FnZXMgY29udGFpbnMgc29tZSBlcnJvcnMhDQojZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJqa2VpcnN0ZWFkL3NjaG9sYXIiKSANCg0KDQpyZXF1aXJlKHNjaG9sYXIpDQoNCiNkZWZpbmUgd29ya2RpcmVjdG9yeSwgbm90ZSB0aGUgZG91YmxlICpiYWNrc2xhc2hlcyogaWYgeW91J3JlIG9uIHdpbmRvd3MNCiMgc2V0d2QoIi95b3VycGF0aGhlcmUpIg0KYGBgDQoNCiMgc2F2aW5nIHdlYnBhZ2UNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQojIExldCdzIGZpcnN0IGdldCB0aGUgc3RhZmYgcGFnZSByZWFkX2h0bWwgaXMgYSBmdW5jdGlvbiB0aGF0IHNpbXBseSBleHRyYWN0cyBodG1sIHdlYnBhZ2VzIGFuZA0KIyBwdXRzIHRoZW0gaW4geG1sIGZvcm1hdA0Kc29jX3N0YWZmIDwtIHJlYWRfaHRtbCgiaHR0cHM6Ly93d3cucnUubmwvc29jaW9sb2d5L3Jlc2VhcmNoL3N0YWZmLyIpDQoNCiNoZWFkKHNvY19zdGFmZikNCiNjbGFzcyhzb2Nfc3RhZmYpDQpgYGANCg0KIyBtYWtpbmcgZGF0YSB3aXRoIG5hbWVzDQoNCiMjIHNlbGVjdGluZyB0aGUgdGFibGUgd2l0aCBuYW1lcw0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnNvY19zdGFmZiA8LSBzb2Nfc3RhZmYgJT4lDQogICAgcnZlc3Q6Omh0bWxfbm9kZXMoImJvZHkiKSAlPiUNCiAgICB4bWwyOjp4bWxfZmluZF9hbGwoIi8vdGQiKSAlPiUNCiAgICBydmVzdDo6aHRtbF90ZXh0KCkNCmBgYA0KDQojIyBzZWxlY3Rpbmcgb25seSB0aGUgb2RkIHJvd2VzIHdpdGggdGhlIG5hbWVzDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmZvZGQgPC0gZnVuY3Rpb24oeCkgew0KICAjd2hhdCBpcyB4LCB4IGlzIGEgdmVjdG9yDQogeCUlMiAhPSAwIA0KfQ0KDQpuc3RhZiA8LSBsZW5ndGgoc29jX3N0YWZmKQ0KDQpzb2NfbmFtZXMgPC0gc29jX3N0YWZmW2ZvZGQoMTpuc3RhZildIA0KaGVhZChzb2NfbmFtZXMpDQoNCmBgYA0KDQojIyBzZWxlY3Rpbmcgb25seSB0aGUgZXZlbiByb3dlcyB3aXRoIHRoZSBleHBlcnRpc2UNCmBgYHtyLCBldmFsPUZBTFNFfQ0Kc29jX2V4cGVydHMgPC0gc29jX3N0YWZmWyFmb2RkKDE6bnN0YWYpXQ0KaGVhZChzb2NfZXhwZXJ0cykNCmBgYA0KDQojIyBjb21iaW5pbmcgbmFtZXMgYW5kIGV4cGVydGlzZSBpbiBzb2NfZGYgDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnNvY19kZiA8LSBkYXRhLmZyYW1lKGNiaW5kKHNvY19uYW1lcywgc29jX2V4cGVydHMpKSANCnNhdmUoc29jX2RmLCBmaWxlID0gIm5hbWVzX2V4cGVydHMuUkRhdGEiKQ0KYGBgDQoNCg0KYGBge3J9DQpsb2FkKCJuYW1lc19leHBlcnRzLlJEYXRhIikNCmBgYA0KDQojIENsZWFuaW5nIHRoZSBkYXRhc2V0ICANCg0KIyMgZGVsZXRlIHJvd3Mgd2l0aG91dCBuYW1lIGluZm8NCmBgYHtyfQ0KIyBpbnNwZWN0IGFnYWluLCBhbmQgcmVtb3ZlIHRoZSByb3dzIHdlIGRvbid0IG5lZWQgKGNoZWNrIGZvciB5b3Vyc2VsZiB0byBiZSBjZXJ0YWluISkNCg0KZGVscm93cyA8LSB3aGljaChzb2NfZGYkc29jX25hbWVzID09ICJTdGFmZjoiIHwgc29jX2RmJHNvY19uYW1lcyA9PSAiUGhEOiIgfCBzb2NfZGYkc29jX25hbWVzID09ICJFeHRlcm5hbCBQaEQ6IiB8DQogICAgc29jX2RmJHNvY19uYW1lcyA9PSAiR3Vlc3QgcmVzZWFyY2hlcnM6IiB8IHNvY19kZiRzb2NfbmFtZXMgPT0gIk90aGVyIHJlc2VhcmNoZXJzOiIpDQoNCnNvY19kZiA8LSBzb2NfZGZbLWRlbHJvd3MsIF0NCmBgYA0KDQojIyBjbGVhbmluZyB0aGUgbmFtZXMgIA0KYGBge3J9DQojIExhc3QgbmFtZSBzZWVtcyB0byBiZSBldmVyeXRoaW5nIGJlZm9yZSB0aGUgY29tbWENCnNvY19kZiRsYXN0X25hbWUgPC0gZ3N1YigiLC4qJCIsICIiLCBzb2NfZGYkc29jX25hbWVzKQ0KDQojIGZpcnN0IG5hbWUgaXMgZXZlcnl0aGluZyBiZXR3ZWVuIGJyYWNrZXRzDQpzb2NfZGYkZmlyc3RfbmFtZSA8LSBhcy5jaGFyYWN0ZXIoc3RyX2V4dHJhY3RfYWxsKHNvY19kZiRzb2NfbmFtZXMsICIoPzw9XFwoKS4rPyg/PVxcKSkiLCBzaW1wbGlmeSA9IFRSVUUpKQ0KYGBgDQoNCmBgYHtyfQ0Kc29jX2RmJGxhc3RfbmFtZSA8LSBnc3ViKCIgSi4gXFwoSmFuc2plXFwpIHZhbiBNU2MiLCAiIiwgc29jX2RmJGxhc3RfbmFtZSkNCnNvY19kZiRmaXJzdF9uYW1lIDwtIHRvbG93ZXIoc29jX2RmJGZpcnN0X25hbWUpICAjIGV2ZXJ5dGhpbmcgdG8gbG93ZXIhDQpzb2NfZGYkbGFzdF9uYW1lIDwtIHRvbG93ZXIoc29jX2RmJGxhc3RfbmFtZSkNCmBgYA0KDQojIyBMb29zZSBkb3VibGUgbmFtZXMNCmBgYHtyfQ0KIyB0cmltd3MgbG9vc2VzIGFsbCBzcGFjaW5nIGJlZm9yZSBhbmQgYWZ0ZXIgKGlmIHlvdSBzcGVjaWZ5ICdib3RoJykgYSBjaGFyYWN0ZXIgc3RyaW5nDQpzb2NfZGYkbGFzdF9uYW1lIDwtIHRyaW13cyhzb2NfZGYkbGFzdF9uYW1lLCB3aGljaCA9IGMoImJvdGgiKSwgd2hpdGVzcGFjZSA9ICJbIFx0XHJcbl0iKQ0Kc29jX2RmJGZpcnN0X25hbWUgPC0gdHJpbXdzKHNvY19kZiRmaXJzdF9uYW1lLCB3aGljaCA9IGMoImJvdGgiKSwgd2hpdGVzcGFjZSA9ICJbIFx0XHJcbl0iKQ0KDQpzb2NfZGYkZmlyc3RfbmFtZSA8LSBhcy5jaGFyYWN0ZXIoc3RyX3NwbGl0KHNvY19kZiRmaXJzdF9uYW1lLCBwYXR0ZXJuPSIgIiwgbiA9IDIsIHNpbXBsaWZ5ID0gVFJVRSlbLDFdKQ0Kc29jX2RmJGZpcnN0X25hbWUgPC0gYXMuY2hhcmFjdGVyKHN0cl9zcGxpdChzb2NfZGYkZmlyc3RfbmFtZSwgcGF0dGVybj0iLSIsIG4gPSAyLCBzaW1wbGlmeSA9IFRSVUUpWywxXSkNCg0KI3JlbW92aW5nIHN0cmFuZ2UgY2hhcmFjdGVycw0Kc29jX2RmJGZpcnN0X25hbWUgPC0gaWNvbnYoc29jX2RmJGZpcnN0X25hbWUsIGZyb20gPSAnVVRGLTgnLCB0byA9ICdBU0NJSS8vVFJBTlNMSVQnKQ0KDQpzb2NfZGYkc29jX2V4cGVydHMgPC0gdHJpbXdzKHNvY19kZiRzb2NfZXhwZXJ0cywgd2hpY2ggPSBjKCJib3RoIiksIHdoaXRlc3BhY2UgPSAiWyBcdFxyXG5dIikNCnNvY19kZiRzb2NfbmFtZXMgPC0gdHJpbXdzKHNvY19kZiRzb2NfbmFtZXMsIHdoaWNoID0gYygiYm90aCIpLCB3aGl0ZXNwYWNlID0gIlsgXHRcclxuXSIpDQpzb2NfZGYkZmlyc3RfbmFtZQ0KYGBgDQpgYGB7cn0NCnNhdmUoc29jX2RmLCBmaWxlPSJzb2NfZGZfczEuUkRhdGEiKQ0KYGBgDQoNCg0KIyBhZGRpbmcgZ2VuZGVyIHRvIGRhdGFzZXQNCg0KIyMgZmdlbmRlcg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmZnZW5kZXIgPC0gZnVuY3Rpb24oc29jX2RmLCBtZSwgZmlsZT1OVUxMKSB7DQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBBdXRob3I6IEJhcyBIb2ZzdHJhLCBBbm5lIE1hYWlrZSBNdWxkZXJzLCBKb2NoZW0gVG9sc21hDQojIERBdGU6ICAgMTMtMTAtMjAyMSwgbGFzdCBlZGl0OiAyMi0wOS0yMDIyDQojIFRhc2tzOiAgLSBhc3NpZ24gZ2VuZGVyIGJhZWQgb24gbmFtZQ0KIyAgICAgICAgIC0gQWRhcHRlZCBmcm9tIFJlbnNlIENvcnRlbiBjb2RlIEFwcmlsIDIwMjENCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQoNCiNJbnB1dDogDQojICAtIGZpcnN0bmFtZV9kZjogYSBkYXRhLmZyYW1lIHdpdGggYSBjb2x1bW4gbmFtZWQgZmlyc3RuYW1lICANCiMgIC0gbWU6IGEgY2hhcmFjdGVyIHZlY3RvciBpbnRyb2R1Y2luZyB5b3Vyc2VsZjogZS5nLiAiSiBUb2xzbWEsIFJhZGJvdWQgVW5pdmVyc2l0eSINCiMgIC0gZmlsZTogbG9jYXRpb24gYW5kIG5hbWUgb2YgZmlsZSB0byBiZSBzYXZlZC4gDQogIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBMb2FkIHJlcXVpcmVkIHBhY2thZ2VzDQoNCmlmICghcmVxdWlyZSgidGlkeXZlcnNlIiwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICBsaWJyYXJ5KHRpZHl2ZXJzZSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQ0KfQ0KDQppZiAoIXJlcXVpcmUoInJ2ZXN0IiwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJydmVzdCIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogIGxpYnJhcnkocnZlc3QsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCn0NCg0KaWYgKCFyZXF1aXJlKCJwb2xpdGUiLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7DQogIGluc3RhbGwucGFja2FnZXMoInBvbGl0ZSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogIGxpYnJhcnkocG9saXRlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQp9DQoNCg0KDQojIG1ha2UgbGlua3MgdG8gc2NyYXBlDQpzb2NfZGYkbmFtZV91cmwgPC0gcGFzdGUwKCJodHRwczovL3d3dy5tZWVydGVucy5rbmF3Lm5sL252Yi9uYWFtL2lzLyIsIHNvY19kZlssIGMoImZpcnN0bmFtZSIpXSkNCg0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMjIyAyOiBpbnRyb2R1Y2UgdG8gc2VydmVyICMjIw0KDQojIEludHJvZHVjZSBteXNlbGYgdG8gdGhlIHNlcnZlcg0Kc2Vzc2lvbiA8LSBib3coImh0dHBzOi8vd3d3Lm1lZXJ0ZW5zLmtuYXcubmwvbnZiL25hYW0vaXMiLCB1c2VyX2FnZW50ID0gbWUgLCBkZWxheSA9IDEpDQoNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyMjIDM6IG1ha2UgZnVuY3Rpb24gdG8gZ2V0IHRhYmxlIGZyb20gIyMjDQogIGZuYW1lcyA8LSBmdW5jdGlvbihsaW5rKXsgDQogICAgbmFtZV9zZXNzaW9uIDwtbm9kKHNlc3Npb24sIHBhdGggPSBsaW5rKQ0KICAgIG5hbWVfcGFnZSA8LSBzY3JhcGUobmFtZV9zZXNzaW9uKSANCiAgICByZXR1cm4obmFtZV9wYWdlKQ0KICB9DQogIA0KbmFtZV9saXN0IDwtIGxpc3QoKQ0KdGFibGVfbGlzdCA8LSBsaXN0KCkNCnNvY19kZiRnZW5kZXIgPC0gTkENCg0KICBmb3IgKGkgaW4gMTpucm93KHNvY19kZikpIHsNCiAgICBwcmludChpKQ0KICAgIG5hbWVfbGlzdFtbaV1dIDwtIGZuYW1lcyhzb2NfZGZbaSwgYygibmFtZV91cmwiKV0pDQogICAgIyBleHRyYWN0IG5hbWUgZnJlcXVlbmN5IHRhYmxlIGFuZCBnZW5kZXIgaW5mbw0KICAgIHRhYmxlX2xpc3RbW2ldXSA8LSBuYW1lX2xpc3RbW2ldXSAlPiUgaHRtbF90YWJsZSgpDQogICAgDQogICAgdGFibGVfbGlzdFtbaV1dW1sxXV1bdGFibGVfbGlzdFtbaV1dW1sxXV09PSItLSJdIDwtICIwIg0KICAgIA0KICAgIGlmIChhcy5udW1lcmljKHRhYmxlX2xpc3RbW2ldXVtbMV1dJFgzWzJdKSA+IGFzLm51bWVyaWModGFibGVfbGlzdFtbaV1dW1sxXV0kWDNbNl0pKSB7DQogICAgICBzb2NfZGYkZ2VuZGVyW2ldIDwtICJtYWxlIiB9IGVsc2Ugew0KICAgICAgICBzb2NfZGYkZ2VuZGVyW2ldIDwtICJmZW1hbGUiDQogICAgICB9DQogICAgDQogICAgaWYgKCFpcy5udWxsKGZpbGUpKSAoc2F2ZShzb2NfZGYsIGZpbGU9ZmlsZSkpDQogICAgDQogICAgfQ0KICByZXR1cm4oc29jX2RmKQ0KfQ0KYGBgDQoNCiMjIGdlbmRlciB0byBzb2NfZGYNCmBgYHtyLCBldmFsPUZBTFNFfQ0Kc29jX2RmICU+JSBtdXRhdGUoZmlyc3RuYW1lPWZpcnN0X25hbWUpIC0+IHNvY19kZg0KDQpzb2NfZGYkZmlyc3RuYW1lDQpzb2NfZGYgPC0gZmdlbmRlcihzb2NfZGYsIG1lPSJKb2NoZW0gVG9sc21hLCBSVS9SVUciKQ0KYGBgDQoNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpzYXZlKHNvY19kZiwgZmlsZT0ic29jX2RmX3MyLlJEYXRhIikNCmBgYA0KDQpgYGB7cn0NCmxvYWQoInNvY19kZl9zMi5SRGF0YSIpDQpgYGANCg0KDQojIyBhZmZpbGlhdGlvbg0KYGBge3J9DQojIHNldCBhZmZpbGlhdGlvbiB0byByYWRib3VkLCBjb21lcyBpbiBoYW5keSBmb3IgcXVlcnlpbmcgZ29vZ2xlIHNjaG9sYXINCnNvY19kZiRhZmZpbGlhdGlvbiA8LSAicmFkYm91ZCB1bml2ZXJzaXR5Ig0KYGBgDQoNCiMgaGFydmVzdGluZyBkYXRhIGZyb20gZ29vZ2xlIHNjaG9sYXINCg0KYGBge3IsIGV2YWw9RkFMU0V9DQojcmVxdWlyZShzY2hvbGFyKQ0KDQpnZXRfc2Nob2xhcl9pZF9maXggPC0gZnVuY3Rpb24gKGxhc3RfbmFtZSA9ICIiLCBmaXJzdF9uYW1lID0gIiIsIGFmZmlsaWF0aW9uID0gTkEpDQp7DQogIGlmICghYW55KG56Y2hhcihjKGZpcnN0X25hbWUsIGxhc3RfbmFtZSkpKSkNCiAgICBzdG9wKCJBdCBsZWFzdCBvbmUgb2YgZmlyc3QgYW5kIGxhc3QgbmFtZSBtdXN0IGJlIHNwZWNpZmllZCEiKQ0KICBzaXRlIDwtIGdldE9wdGlvbigic2Nob2xhcl9zaXRlIikNCiAgdXJsIDwtIHBhc3RlMChzaXRlLCAiL2NpdGF0aW9ucz92aWV3X29wPXNlYXJjaF9hdXRob3JzJm1hdXRob3JzPSIsDQogICAgICAgICAgICAgICAgZmlyc3RfbmFtZSwgIisiLCBsYXN0X25hbWUsICImaGw9ZW4mb2k9YW8iKQ0KICBwYWdlIDwtIGdldF9zY2hvbGFyX3Jlc3AodXJsKQ0KICBpZiAoaXMubnVsbChwYWdlKSkNCiAgICByZXR1cm4oTkEpDQogIGFhIDwtIGh0dHI6OmNvbnRlbnQocGFnZSwgYXMgPSAidGV4dCIpDQogICMgYWRkZWQgYnkgQmFzIEhvZnN0cmE6IGJ1Z2ZpeCBmb3IgSURzIHRoYXQgaGF2ZSBhIGRhc2ggKCItIikNCiAgaWRzIDwtIHN1YnN0cmluZyhhYSwgcmVnZXhwcigiO3VzZXI9IiwgYWEpKQ0KICBpZHMgPC0gc3Vic3RyKGlkcywgMSwgMTkpICMgZXJyb3IgcHJvbmUsIGJ1dCB1bnN1cmUgaG93IHRvIHNvbHZlIG90aGVyd2lzZQ0KICAjIGlmIChuY2hhcihzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwoc3RyaW5nID0gYWEsIHBhdHRlcm4gPSAiO3VzZXI9W1s6YWxudW06XV0rW1s6cHVuY3Q6XV0iKVtbMV1dWzFdKSA8IDE4KSB7DQogICMgICBpZHMgPC0gc3RyaW5ncjo6c3RyX2V4dHJhY3RfYWxsKHN0cmluZyA9IGFhLCBwYXR0ZXJuID0gIjt1c2VyPVtbOmFsbnVtOl1dK1tbOnB1bmN0Ol1dK1tbOmFsbnVtOl1dK1tbOnB1bmN0Ol1dIikNCiAgIyB9IGVsc2Ugew0KICAjICAgaWRzIDwtIHN0cmluZ3I6OnN0cl9leHRyYWN0X2FsbChzdHJpbmcgPSBhYSwgcGF0dGVybiA9ICI7dXNlcj1bWzphbG51bTpdXStbWzpwdW5jdDpdXSIpDQogICMgfQ0KICBpZiAobGVuZ3RoKHVubGlzdChpZHMpKSA9PSAwKSB7DQogICAgbWVzc2FnZSgiTm8gU2Nob2xhciBJRCBmb3VuZC4iKQ0KICAgIHJldHVybihOQSkNCiAgfQ0KICBpZHMgPC0gaWRzICU+JSB1bmxpc3QgJT4lIGdzdWIoIjt1c2VyPXxbWzpwdW5jdDpdXSQiLCAiIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4pICU+JSB1bmlxdWUNCiAgaWYgKGxlbmd0aChpZHMpID4gMSkgew0KICAgIHByb2ZpbGVzIDwtIGxhcHBseShpZHMsIHNjaG9sYXI6OmdldF9wcm9maWxlKQ0KICAgIGlmIChpcy5uYShhZmZpbGlhdGlvbikpIHsNCiAgICAgIHhfcHJvZmlsZSA8LSBwcm9maWxlc1tbMV1dDQogICAgICB3YXJuaW5nKCJTZWxlY3RpbmcgZmlyc3Qgb3V0IG9mICIsIGxlbmd0aChwcm9maWxlcyksDQogICAgICAgICAgICAgICIgY2FuZGlkYXRlIG1hdGNoZXMuIikNCiAgICB9DQogICAgZWxzZSB7DQogICAgICB3aGljaF9wcm9maWxlIDwtIHNhcHBseShwcm9maWxlcywgZnVuY3Rpb24oeCkgew0KICAgICAgICBzdHJpbmdyOjpzdHJfY291bnQoc3RyaW5nID0geCRhZmZpbGlhdGlvbiwgcGF0dGVybiA9IHN0cmluZ3I6OmNvbGwoYWZmaWxpYXRpb24sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IFRSVUUpKQ0KICAgICAgfSkNCiAgICAgIGlmIChhbGwod2hpY2hfcHJvZmlsZSA9PSAwKSkgew0KICAgICAgICB3YXJuaW5nKCJObyByZXNlYXJjaGVyIGZvdW5kIGF0IHRoZSBpbmRpY2F0ZWQgYWZmaWxpYXRpb24uIikNCiAgICAgICAgcmV0dXJuKE5BKQ0KICAgICAgfQ0KICAgICAgZWxzZSB7DQogICAgICAgIHhfcHJvZmlsZSA8LSBwcm9maWxlc1tbd2hpY2god2hpY2hfcHJvZmlsZSAhPQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMCldXQ0KICAgICAgfQ0KICAgIH0NCiAgfQ0KICBlbHNlIHsNCiAgICB4X3Byb2ZpbGUgPC0gc2Nob2xhcjo6Z2V0X3Byb2ZpbGUoaWQgPSBpZHMpDQogIH0NCiAgcmV0dXJuKHhfcHJvZmlsZSRpZCkNCn0NCmBgYA0KDQojIyBzY2hvbGFycyBpZC4gDQoNClJlbW92ZSBzdGFmZiBtZW1iZXJzIHdpdGhvdXQgc2Nob2xhciBpZHMuIA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCg0Kc29jX2RmJGdzX2lkIDwtICIiDQoNCmZvciAoaSBpbiAxOm5yb3coc29jX2RmKSkgew0KICBwcmludChpKQ0KICB0aW1lIDwtIHJ1bmlmKDEsIDAsIDEpDQogIFN5cy5zbGVlcCh0aW1lKQ0KDQogIHRyeUNhdGNoKHsNCiAgICAgc29jX2RmW2ksYygiZ3NfaWQiKV0gPC0gZ2V0X3NjaG9sYXJfaWRfZml4KGxhc3RfbmFtZSA9IHNvY19kZltpLCBjKCJsYXN0X25hbWUiKV0sICMgc28gc2VhcmNoIG9uIGxhc3RfbmFtZSBvZiBzdGFmZiAodGhpcmQgY29sdW1uKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3RfbmFtZSA9IHNvY19kZltpLCBjKCJmaXJzdF9uYW1lIildLCAgIyBzZWFyY2ggb24gZmlyc3RfbmFtZSBvZiBzdGFmZiAoZm91cnRoIGNvbHVtbikNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFmZmlsaWF0aW9uID0gc29jX2RmW2ksYygiYWZmaWxpYXRpb24iKV0pICMgc2VhcmNoIG9uIGFmZmlsaWF0aW9uIG9mIGVhY2ggc3RhZmYgKGZpZnRoIGNvbHVtbikNCg0KICAgIH0sIGVycm9yPWZ1bmN0aW9uKGUpe2NhdCgiRVJST1IgOiIsIGNvbmRpdGlvbk1lc3NhZ2UoZSksICJcbiIpfSkgIyBjb250aW51ZSBvbiBlcnJvciwgYnV0IHByaW50IHRoZSBlcnJvcg0KICB9DQoNCiMgcmVtb3ZlIHRob3NlIHdpdGhvdXQgcHVicyBmcm9tIHRoZSBkZg0KIyBzZWVtcyB3ZSdyZSBsZWZ0IHdpdGggYWJvdXQgMzQgc29jaW9sb2d5IHN0YWZmIG1lbWJlcnMhDQpzb2NfZGZfY29weSA8LSBzb2NfZGYgI2p1c3QgdG8gYWxzbyBoYXZlIHRoZSBkYXRhIHdpdGggc3RhZmYgd2l0aG91dCBzY2hvbGFyX2lkDQpzb2NfZGYgPC0gc29jX2RmWyFzb2NfZGYkZ3NfaWQgPT0gIiIsIF0NCg0KYGBgDQoNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpzYXZlKHNvY19kZiwgZmlsZT0ic29jX2RmX3MzLlJEYXRhIikNCnNhdmUoc29jX2RmX2NvcHksIGZpbGU9InNvY19kZl9jb3B5LlJEYXRhIikNCg0KYGBgDQoNCmBgYHtyfQ0KbG9hZCgic29jX2RmX3MzLlJEYXRhIikNCmBgYA0KDQoNCiMgcHVibGljYXRpb25zIGFuZCBwcm9maWxlcw0KDQpXZSBzYXZlIHRoZSBwdWJsaWNhdGlvbnMgYW5kIGluZm8gb2YgdGhlIHNjaG9sYXIgcHJvZmlsZXMgaW4gbmV3IG9iamVjdHMuIA0KDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0Kc29jX2xpc3RfcHJvZmlsZXMgPC0gbGlzdCgpICAjIGZpcnN0IHdlIGNyZWF0ZSBhbiBlbXB0eSBsaXN0IHRoYXQgd2UgdGhlbiBmaWxsIHVwIHdpdGggdGhlIGZvciBsb29wDQpzb2NfbGlzdF9wdWJsaWNhdGlvbnMgPC0gbGlzdCgpDQoNCnRpbWUgPC0gMSAjIEkgcGxhY2VkIHRoZSB3YWl0aW5nIHRpbWUgb3V0c2lkZSB0aGUgbG9vcA0KaSA8LSAxICMgT3VyIGxvb3AgaXRlcmF0b3IgaXMgbm93IGEgdmFyaWFibGUuIFRoaXMgbWVhbnMgSSBjYW4gY2hhbmdlIGl0IHdpdGhpbiBhIHdoaWxlIGxvb3AuIFVzaW5nIGEgZm9yIGxvb3AgeW91IGNhbnQgY2hhbmdlIHlvdXIgaXRlcmF0b3IgaW4gdGhlIGxvb3AgaXRzZWxmLiANCg0Kd2hpbGUgKGkgPD0gbnJvdyhzb2NfZGYpKSB7DQogICAgcHJpbnQoaSkNCiAgICBTeXMuc2xlZXAodGltZSkNCg0KICAgDQogICAgdHJ5Q2F0Y2goew0KICAgICNJbiB0aGlzIHBhcnQgb2YgdGhlIHRyeUNhdGNoIGZ1bmN0aW9uIHlvdSBwdXQgYWxsIHRoZSBzdHVmZiB5b3Ugd2FudCB0byBkbyBpbiB0aGUgbG9vcC4NCiAgICAjIG5vdGUgaG93IHlvdSBjYWxsIGRpZmZlcmVudCBlbGVtZW50cyBpbiBhIGxpc3QgJ1tbXV0nLCBmaWxsIGluIHRoZSBpLXRoIGVsZW1lbnQNCiAgICBzb2NfbGlzdF9wcm9maWxlc1tbaV1dIDwtIGdldF9wcm9maWxlKHNvY19kZltpLCBjKCJnc19pZCIpXSkgICMgTm90ZSBob3cgd2UgY2FsbCByb3cgaSAocmVtZW1iZXIgaG93IHRvIGNhbGwgcm93cyBpbiBhIERGL01hdHJpeCkgYW5kIHRoZW4gdGhlIGFzc29jaWF0ZWQgc2Nob2xhciBpZA0KICAgIHNvY19saXN0X3B1YmxpY2F0aW9uc1tbaV1dIDwtIGdldF9wdWJsaWNhdGlvbnMoc29jX2RmW2ksIGMoImdzX2lkIildKQ0KICAgIHNvY19saXN0X3B1YmxpY2F0aW9uc1tbaV1dWywgYygiZ3NfaWQiKV0gPC0gc29jX2RmW2ksIGMoImdzX2lkIildICAjIG5vdGUgdGhhdCB3ZSBhZ2FpbiBhdHRhY2ggYW4gaWQNCiAgICAjIHNvIGJvdGggZnVuY3Rpb25zIGhlcmUgY2FsbCB0aGUgZW50aXJlIHByb2ZpbGUgYW5kIHB1YnMgZm9yIGFuIGF1dGhvciwgYmFzZWQgb24gZ29vZ2xlDQogICAgIyBzY2hvbGFyIGlkcw0KICAgIGkgPC0gaSArIDEgI0lNUE9SVEFOVCwgWU9VIE5FRUQgVE8gVEVMTCBUSEUgV0hJTEUgTE9PUCBUSEFUIFlPVVIgSVRFUkFUT1IgSEFTIFRPIEJFIElOQ1JFQVNFRA0KICAgIH0sDQogICAgICB3YXJuaW5nID0gZnVuY3Rpb24odykgew0KICAgICAgICBjYXQoIldBUk5JTkc6IiwgY29uZGl0aW9uTWVzc2FnZSh3KSwgIlxuIikgI1dBUk5JTkcgbWVzc2FnZQ0KICAgICAgICBpIDw8LSBpICsgMX0sICNCVVQgV0UgRE8gV0FOVCBUTyBDT05USU5VRS4gTk9URSBUSEUgRE9VQkxFIDw8IFRISVMgSVMgQkVDQVVTRSBJIFdBTlQgVE8gQ0hBTkdFIEEgVkFSSUFCTEUgV0hJQ0ggRVhJU1RTIE9VVFNJREUgVEhFIFdBUk5JTkcgRlVOQ1RJT04NCiAgICAgIGVycm9yID1mdW5jdGlvbihlKSB7DQogICAgICAgIHRpbWUgPDwtIG1pbih0aW1lICoxMCwgMzYwMCoyKQ0KICAgICAgICBjYXQoIkVycm9yOiIsIGNvbmRpdGlvbk1lc3NhZ2UoZSksICJcbiIpICNFUlJPUiBtZXNzYWdlDQogICAgICAgIGNhdCgic2xlZXAgdGltZToiLCB0aW1lLCAgIlxuIikNCiAgICAgICAgY2F0KCJpayB6aXQgaW4gbG9vcCIsIGkpDQogICAgICAgICNBRlRFUiBUSEUgTkVXIFNMRUVQIFRJTUUsIFdFIFRSWSBBR0FJTiwgV0UgVEhFUkVGT1JFIERPIE5PVCBVUERBVEUgaS4gaWRlYWxseSB5b3UgYWxzbyB3YW50IHRvIGhhdmUgc29tZSBicmVhayBvcHRpb24uIEFuZCBtYXliZSB5b3UgYWxzbyB3YW50IHRvIHNhdmUgeW91ciBkYXRhIHdoZW4geW91IGhpdCBhIHRpbWUgb3V0IGVycm9yLiANCiAgICAgIH0pDQogICANCiANCn0NCmBgYA0KDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KIyBUaGUgMzYgUlUgc29jaW9sb2d5IHNjaG9sYXJzIHB1Ymxpc2ggfjMwMDAgcGFwZXJzDQpzb2NfZGZfcHVibGljYXRpb25zIDwtIGJpbmRfcm93cyhzb2NfbGlzdF9wdWJsaWNhdGlvbnMpDQpgYGANCg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnNhdmUoc29jX2RmX3B1YmxpY2F0aW9ucywgZmlsZT0ic29jX2RmX3B1YmxpY2F0aW9ucy5SRGF0YSIpDQpzYXZlKHNvY19saXN0X3Byb2ZpbGVzLCBmaWxlPSAic29jX2xpc3RfcHJvZmlsZXMuUkRhdGEiKQ0KYGBgDQoNCmBgYHtyfQ0KbG9hZCgic29jX2RmX3B1YmxpY2F0aW9ucy5SRGF0YSIpDQpsb2FkKCJzb2NfbGlzdF9wcm9maWxlcy5SRGF0YSIpDQpgYGANCg0KDQojIyBwdXQgdGhlIGluZm8gb2YgdGhlIHByb2ZpbGVzIGluIG91ciBkYXRhIHNldCBvZiBzdGFmZiBtZW1iZXJzIHNvY19kZg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnNvY19wcm9maWxlc19kZiA8LSBsaXN0KCkNCg0KDQpmb3IgKGkgaW4gMTpsZW5ndGgoc29jX2xpc3RfcHJvZmlsZXMpKSB7DQogICAgIyBzb2NfcHJvZmlsZXNfZGZbW2ldXSA8LSBkYXRhLmZyYW1lKHQodW5saXN0KHNvY19saXN0X3Byb2ZpbGVzW1tpXV1bMTo4XSkpKSAjc29tZSBhbm55b2luZw0KICAgICMgZGF0YSBoYW5kbGluZw0KICBpZiAoIWlzLm51bGwoc29jX2xpc3RfcHJvZmlsZXNbW2ldXSkpIHsNCiAgICBzb2NfcHJvZmlsZXNfZGZbW2ldXSA8LSB1bmxpc3Qoc29jX2xpc3RfcHJvZmlsZXNbW2ldXVsxOjhdKQ0KICAgIHNvY19wcm9maWxlc19kZltbaV1dIDwtIGRhdGEuZnJhbWUoc29jX3Byb2ZpbGVzX2RmW1tpXV0pDQogICAgc29jX3Byb2ZpbGVzX2RmW1tpXV0gPC0gdChzb2NfcHJvZmlsZXNfZGZbW2ldXSkNCiAgICByb3cubmFtZXMoc29jX3Byb2ZpbGVzX2RmW1tpXV0pIDwtIE5VTEwNCiAgICBzb2NfcHJvZmlsZXNfZGZbW2ldXSA8LSBkYXRhLmZyYW1lKHNvY19wcm9maWxlc19kZltbaV1dKQ0KICB9DQp9DQoNCiNzb2NfcHJvZmlsZXNfZGYNCg0Kc29jX3Byb2ZpbGVzX2RmMiA8LSBiaW5kX3Jvd3Moc29jX3Byb2ZpbGVzX2RmKQ0Kc29jX2RmIDwtIGxlZnRfam9pbihzb2NfZGYsIHNvY19wcm9maWxlc19kZjIsIGJ5ID0gYyhnc19pZCA9ICJpZCIpKSAgIyBtZXJnZSBkYXRhIHdpdGggc29jX2RmDQpzb2NfZGYgICMgbm90aWNlIGFsbCB0aGUgbmV3IGluZm9ybWF0aW9uIHdlIHdlcmUgYWJsZSB0byBnZXQgZnJvbSB0aGUgc2Nob2xhciBwcm9maWxlcyENCg0KYGBgDQoNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpzYXZlKHNvY19kZiwgZmlsZT0ic29jX2RmX3M0LlJEYXRhIikNCg0KYGBgDQoNCmBgYHtyfQ0KbG9hZCgic29jX2RmX3M0LlJEYXRhIikNCmBgYA0KDQoNCiMgY2l0YXRpb24gaGlzdG9yeQ0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiMgZ2V0IGNpdGF0aW9uIGhpc3Rvcnkgb2YgYSBzY2hvbGFyDQpzb2Nfc3RhZmZfY2l0IDwtIGxpc3QoKQ0KDQoNCnRpbWUgPC0gMSAjIEkgcGxhY2VkIHRoZSB3YWl0aW5nIHRpbWUgb3V0c2lkZSB0aGUgbG9vcA0KaSA8LSAxICMgT3VyIGxvb3AgaXRlcmF0b3IgaXMgbm93IGEgdmFyaWFibGUuIFRoaXMgbWVhbnMgSSBjYW4gY2hhbmdlIGl0IHdpdGhpbiBhIHdoaWxlIGxvb3AuIFVzaW5nIGEgZm9yIGxvb3AgeW91IGNhbnQgY2hhbmdlIHlvdXIgaXRlcmF0b3IgaW4gdGhlIGxvb3AgaXRzZWxmLiANCg0Kd2hpbGUgKGkgPD0gbnJvdyhzb2NfZGYpKSB7DQogICAgcHJpbnQoaSkNCiAgICBTeXMuc2xlZXAodGltZSkNCg0KICAgIHRyeUNhdGNoKHsNCiAgICAgIHNvY19zdGFmZl9jaXRbW2ldXSA8LSBnZXRfY2l0YXRpb25faGlzdG9yeShzb2NfZGZbaSwgYygiZ3NfaWQiKV0pDQogICAgICAgIGlmIChucm93KHNvY19zdGFmZl9jaXRbW2ldXSkgPiAwKSB7DQogICAgICAgICAgc29jX3N0YWZmX2NpdFtbaV1dWywgYygiZ3NfaWQiKV0gPC0gc29jX2RmW2ksIGMoImdzX2lkIildICAjIGFnYWluIGF0dGFjaCB0aGUgZ3NfaWQgYXMgdGhpcmQgY29sdW1uDQogICAgICAgIH0NCiAgICBpIDwtIGkgKyAxDQogICAgfSwNCiAgICAgIHdhcm5pbmcgPSBmdW5jdGlvbih3KSB7DQogICAgICAgIGNhdCgiV0FSTklORzoiLCBjb25kaXRpb25NZXNzYWdlKHcpLCAiXG4iKSAjV0FSTklORyBtZXNzYWdlDQogICAgICAgIGkgPDwtIGkgKyAxfSwgI0JVVCBXRSBETyBXQU5UIFRPIENPTlRJTlVFLiBOT1RFIFRIRSBET1VCTEUgPDwgVEhJUyBJUyBCRUNBVVNFIEkgV0FOVCBUTyBDSEFOR0UgQSBWQVJJQUJMRSBXSElDSCBFWElTVFMgT1VUU0lERSBUSEUgV0FSTklORyBGVU5DVElPTg0KICAgICAgZXJyb3IgPWZ1bmN0aW9uKGUpIHsNCiAgICAgICAgdGltZSA8PC0gbWluKHRpbWUgKjEwLCAzNjAwKjIpDQogICAgICAgIGNhdCgiRXJyb3I6IiwgY29uZGl0aW9uTWVzc2FnZShlKSwgIlxuIikgI0VSUk9SIG1lc3NhZ2UNCiAgICAgICAgY2F0KCJzbGVlcCB0aW1lOiIsIHRpbWUsICAiXG4iKQ0KICAgICAgICBjYXQoImlrIHppdCBpbiBsb29wIiwgaSkNCiAgICAgICAgI0FGVEVSIFRIRSBORVcgU0xFRVAgVElNRSwgV0UgVFJZIEFHQUlOLCBXRSBUSEVSRUZPUkUgRE8gTk9UIFVQREFURSBpLiBpZGVhbGx5IHlvdSBhbHNvIHdhbnQgdG8gaGF2ZSBzb21lIGJyZWFrIG9wdGlvbi4gQW5kIG1heWJlIHlvdSBhbHNvIHdhbnQgdG8gc2F2ZSB5b3VyIGRhdGEgd2hlbiB5b3UgaGl0IGEgdGltZSBvdXQgZXJyb3IuIA0KICAgICAgfSkNCg0KICAgIA0KfQ0Kc29jX3N0YWZmX2NpdCA8LSBiaW5kX3Jvd3Moc29jX3N0YWZmX2NpdCkNCmBgYA0KDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0Kc2F2ZShzb2Nfc3RhZmZfY2l0LCBmaWxlPSJzb2Nfc3RhZmZfY2l0LlJEYXRhIikNCg0KYGBgDQoNCmBgYHtyfQ0KbG9hZCgic29jX3N0YWZmX2NpdC5SRGF0YSIpDQpgYGANCg0KDQojIGNvbGxhYm9yYXRvcnMNCmBgYHtyLCBldmFsPUZBTFNFfQ0KcmVxdWlyZShydmVzdCkNCnJlcXVpcmUoeG1sMikNCnJlcXVpcmUodGlkeXZlcnNlKQ0KDQojIGZ1bmN0aW9uIHRvIGdldCBjb2xsYWJvcmF0b3JzIGFuZCBuYW1lcyBmcm9tIEdTIHByb2ZpbGVzDQpmY29sbGFicyA8LSBmdW5jdGlvbihnc2lkLCBsb29rZm9yY29sbGFicykgew0KDQogIGh0bWxwYWdlMSA8LSByZWFkX2h0bWwocGFzdGUwKCJodHRwczovL3NjaG9sYXIuZ29vZ2xlLm5sL2NpdGF0aW9ucz91c2VyPSIsIGdzaWQsICImaGw9ZW4iKSkgIyBzbyB3ZSBwYXN0ZSB0aGUgZ29vZ2xlIHNjaG9sYXIgaWQNCiAgcHJvZmlsZW5hbWUgPC0gaHRtbHBhZ2UxICU+JSBodG1sX25vZGVzKHhwYXRoID0gIi8vKi9kaXZbQGlkPSdnc2NfcHJmX2luJ10iKSAlPiUgaHRtbF90ZXh0KCkgIyB3ZSBleHRyYWN0IHRoZSBwcm9maWxlIG5hbWUgb2YgdGhhdCBnb29nbGUgc2Nob2xhciBwYWdlDQogIHByb2ZpbGVjb2xsYWJzMSA8LSBhcy5kYXRhLmZyYW1lKDApICMgZW1wdHkgZGYgbmVjZXNzYXJ5IGZvciBsYXRlcg0KICBwcm9maWxlY29sbGFiczIgPC0gYXMuZGF0YS5mcmFtZSgwKSAjIGVtcHR5IGRmIG5lY2Vzc2FyeSBmb3IgbGF0ZXINCg0KICBpZiAobG9va2ZvcmNvbGxhYnMgPT0gMSkgeyAjIHNvIGlmIHlvdSB3YW50IHRvIGxvb2sgZm9yIGNvbGxhYnMsIHNldCBmdW5jdGlvbiB0byAxDQoNCiAgICBodG1scGFnZTIgPC0gcmVhZF9odG1sKHBhc3RlMCgiaHR0cHM6Ly9zY2hvbGFyLmdvb2dsZS5jb20vY2l0YXRpb25zP3ZpZXdfb3A9bGlzdF9jb2xsZWFndWVzJmhsPWVuJnVzZXI9IiwgZ3NpZCkpICMgc28gd2UgcGFzdGUgdGhlIGdvb2dsZSBzY2hvbGFyIGlkDQogICAgcHJvZmlsZWNvbGxhYnMxIDwtICBodG1scGFnZTIgJT4lIGh0bWxfbm9kZXMoY3NzPSJoMyIpICU+JSBodG1sX3RleHQoKSAjIGdldCBuYW1lcw0KICAgIHByb2ZpbGVjb2xsYWJzMSA8LSAgYXMuZGF0YS5mcmFtZShwcm9maWxlY29sbGFiczEpDQoNCiAgICBwcm9maWxlY29sbGFiczIgPC0gaHRtbHBhZ2UyICU+JSBodG1sX25vZGVzKCJhIikgJT4lIGh0bWxfYXR0cigiaHJlZiIpICMgZ2V0IHRoZSBsaW5rDQogICAgcHJvZmlsZWNvbGxhYnMyIDwtIHByb2ZpbGVjb2xsYWJzMltzZXFfYWxvbmcocHJvZmlsZWNvbGxhYnMyKSAlJSAyID4gMF0NCiAgICBwcm9maWxlY29sbGFiczIgPC0gc3Vic3RyaW5nKHByb2ZpbGVjb2xsYWJzMiwgMjMpDQoNCiAgfQ0KICBpZiAobnJvdyhwcm9maWxlY29sbGFiczEpPjEpIHsgIyBpZiB0aGVyZSBBUkUgY29sbGFicw0KDQogICAgcHJvZmlsZWNvbGxhYnMxIDwtIGFzLmRhdGEuZnJhbWUocHJvZmlsZWNvbGxhYnMxKSAjIHdlIHdhbnQgdG8uLi4NCiAgICBwcm9maWxlY29sbGFiczIgPC0gIGFzLmRhdGEuZnJhbWUocHJvZmlsZWNvbGxhYnMyKQ0KICAgIHByb2ZpbGVjb2xsYWJzMVssYygiY29hdXRoX2lkIildIDwtIHByb2ZpbGVjb2xsYWJzMlssMV0NCg0KICAgIHByb2ZpbGVjb2xsYWJzMVssYygiZ3NfaWQiKV0gPC0gZ3NpZCAjLi4uIGFkZCBnc19pZHMgb2YgZm9jYWwgR1MgcHJvZmlsZQ0KICAgIHByb2ZpbGVjb2xsYWJzMVssYygibmFtZSIpXSA8LSBwcm9maWxlbmFtZSAjLi4uYW5kIHRoZSB0aGUgcHJvZmlsZSBuYW1lIG9mIEdTIHByb2ZpbGUgYXR0YWNoZWQNCg0KICAgIG5hbWVzKHByb2ZpbGVjb2xsYWJzMSlbMV0gPC0gImNvYXV0aCINCg0KICB9IGVsc2Ugew0KICAgIHByb2ZpbGVjb2xsYWJzMSA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKGdzaWQsIHByb2ZpbGVuYW1lKSkgIyBpZiBOT1QgbG9va2luZyBmb3IgY29sbGFicy4uLg0KICAgIG5hbWVzKHByb2ZpbGVjb2xsYWJzMSkgPC0gYygiZ3NfaWQiLCAibmFtZSIpICMuLi53ZSBvbmx5IGF0dGFjaCBnc19pZCBhbmQgcHJvZmlsZW5hbWUNCg0KICB9DQogIHJldHVybihwcm9maWxlY29sbGFiczEpDQoNCn0NCmBgYA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiMgaW5wdXQgYSBnb29nbGUgc2Nob2xhciBpZCBhbmQgYSAxIChpZiB5b3Ugd2FudCB0byBmaW5kIGNvbGxhYnMpIG9yIDAgKG9ubHkgZXh0cmFjdGluZyBuYW1lcykNCg0Kc29jX2NvbGxhYnMgPC0gbGlzdCgpDQpmb3IgKGkgaW4gMTpucm93KHNvY19kZikpIHsNCiAgICBwcmludChpKQ0KICAgIHRpbWUgPC0gcnVuaWYoMSwgMCwgMSkNCiAgICBTeXMuc2xlZXAodGltZSkNCg0KICAgIHNvY19jb2xsYWJzW1tpXV0gPC0gZmNvbGxhYnMoc29jX2RmW2ksIGMoImdzX2lkIildLCAxKQ0KDQp9DQoNCnNvY19jb2xsYWJzIDwtIGJpbmRfcm93cyhzb2NfY29sbGFicykgICMgYmluZCByb3dzLCBnZXQgdGhlIHVuaXF1ZSBvbmVzIQ0Kc29jX2NvbGxhYnNfdW5pcXVlIDwtIHVuaXF1ZShzb2NfY29sbGFic1ssIDNdKSANCnNvY19jb2xsYWJzX3VuaXF1ZSA8LSBzb2NfY29sbGFic191bmlxdWVbIWlzLm5hKHNvY19jb2xsYWJzX3VuaXF1ZSldDQpzYXZlKHNvY19jb2xsYWJzLCBmaWxlID0gInNvY19kZl9jb2xsYWJzMS5SRGF0YSIpICAjIHlvdSBub3RpY2UgdGhpcyB0YWtlcyBhIHdoaWxlLCBzbyB3ZSBzYXZlIHRoZSBkYXRhIGhlcmUNCmBgYA0KIyBuZXR3b3JrIGJhc2VkIG9uIHB1YmxpY2F0aW9ucy4gDQoNCkRlemUgdGlqZHNpbnRlcnZhbGxlbiB6aWpuIHdhYXJzY2hpam5saWprIHRlIGdyb290IQ0KDQpgYGB7cn0NCmxvYWQoInNvY19kZl9jb2xsYWJzMS5SRGF0YSIpDQpgYGANCg0KDQpgYGB7cn0NCmxpYnJhcnkoc3RyaW5ncikNCg0KI2VtcHR5IGFkamFjZW5jeSBtYXRyaXggZm9yIHRoZSB5ZWFycyAyMDAxLTIwMDcNCm5ldHdvcmsyMDAxXzIwMDcgPC0gbWF0cml4KE5BLCBucm93PW5yb3coc29jX2RmKSwgbmNvbD1ucm93KHNvY19kZikpDQoNCiNzZWxlY3QgcHVibGljYXRpb25zIG9mIHRoZSBjb3JyZXNwb25kaW5nIHRpbWUgZXJhDQpwdWJzX3NlbCA8LSBzb2NfZGZfcHVibGljYXRpb25zICU+JQ0KICAgICAgICAgICAgICBtdXRhdGUoYXV0aG9yID0gdG9sb3dlcihhdXRob3IpKSAlPiUNCiAgICAgICAgICAgICAgZmlsdGVyKHllYXI+MjAwMCAmIHllYXI8MjAwOCkNCg0Kc2F2ZShwdWJzX3NlbCwgZmlsZSA9ICJzb2NfcHVic19zZWxfMjAwMV8yMDA3LlJEYXRhIikNCg0KI2ZpbGwgdGhlIG1hdHJpeA0KZm9yIChlZ28gaW4gMTogbnJvdyhzb2NfZGYpKSB7DQogIG5hbWVfZWdvIDwtIHNvY19kZiRsYXN0X25hbWVbZWdvXSAjd2hpY2ggZWdvPyANCiAgcHVic19zZWwyIDwtIHB1YnNfc2VsW3N0cl9kZXRlY3QocHVic19zZWwkYXV0aG9yLCBuYW1lX2VnbyksXSAjcHVibGljYXRpb25zIG9mIGVnbw0KICBmb3IgKGFsdGVyIGluIDE6bnJvdyhzb2NfZGYpKXsNCiAgICBuYW1lX2FsdGVyIDwtIHNvY19kZiRsYXN0X25hbWVbYWx0ZXJdICN3aGljaCBhbHRlcj8gDQogICAgbmV0d29yazIwMDFfMjAwN1tlZ28sYWx0ZXJdIDwtIHN1bShzdHJfZGV0ZWN0KHB1YnNfc2VsMiRhdXRob3IsIG5hbWVfYWx0ZXIpKSA+IDEgI2RpZCBhbHRlciBwdWJsaXNoIHdpdGggZWdvDQogIH0NCn0NCg0Kc2F2ZShuZXR3b3JrMjAwMV8yMDA3LCBmaWxlID0gInNvY19uZXR3b3JrMjAwMV8yMDA3LlJEYXRhIikNCg0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShzdHJpbmdyKQ0KDQojZW1wdHkgYWRqYWNlbmN5IG1hdHJpeCBmb3IgdGhlIHllYXJzIDIwMDgtMjAxNA0KbmV0d29yazIwMDhfMjAxNCA8LSBtYXRyaXgoTkEsIG5yb3c9bnJvdyhzb2NfZGYpLCBuY29sPW5yb3coc29jX2RmKSkNCg0KI3NlbGVjdCBwdWJsaWNhdGlvbnMgb2YgdGhlIGNvcnJlc3BvbmRpbmcgdGltZSBlcmENCnB1YnNfc2VsIDwtIHNvY19kZl9wdWJsaWNhdGlvbnMgJT4lDQogICAgICAgICAgICAgIG11dGF0ZShhdXRob3IgPSB0b2xvd2VyKGF1dGhvcikpICU+JQ0KICAgICAgICAgICAgICBmaWx0ZXIoeWVhcj4yMDA3ICYgeWVhcjwyMDE1KQ0KDQpzYXZlKHB1YnNfc2VsLCBmaWxlID0gInNvY19wdWJzX3NlbF8yMDA4XzIwMTQuUkRhdGEiKQ0KDQojZmlsbCB0aGUgbWF0cml4DQpmb3IgKGVnbyBpbiAxOiBucm93KHNvY19kZikpIHsNCiAgbmFtZV9lZ28gPC0gc29jX2RmJGxhc3RfbmFtZVtlZ29dICN3aGljaCBlZ28/IA0KICBwdWJzX3NlbDIgPC0gcHVic19zZWxbc3RyX2RldGVjdChwdWJzX3NlbCRhdXRob3IsIG5hbWVfZWdvKSxdICNwdWJsaWNhdGlvbnMgb2YgZWdvDQogIGZvciAoYWx0ZXIgaW4gMTpucm93KHNvY19kZikpew0KICAgIG5hbWVfYWx0ZXIgPC0gc29jX2RmJGxhc3RfbmFtZVthbHRlcl0gI3doaWNoIGFsdGVyPyANCiAgICBuZXR3b3JrMjAwOF8yMDE0W2VnbyxhbHRlcl0gPC0gc3VtKHN0cl9kZXRlY3QocHVic19zZWwyJGF1dGhvciwgbmFtZV9hbHRlcikpID4gMSAjZGlkIGFsdGVyIHB1Ymxpc2ggd2l0aCBlZ28NCiAgfQ0KfQ0KDQpzYXZlKG5ldHdvcmsyMDA4XzIwMTQsIGZpbGUgPSAic29jX25ldHdvcmsyMDA4XzIwMTQuUkRhdGEiKQ0KDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KHN0cmluZ3IpDQoNCiNlbXB0eSBhZGphY2VuY3kgbWF0cml4IGZvciB0aGUgeWVhcnMgMjAxNS0yMDIxDQpuZXR3b3JrMjAxNV8yMDIxIDwtIG1hdHJpeChOQSwgbnJvdz1ucm93KHNvY19kZiksIG5jb2w9bnJvdyhzb2NfZGYpKQ0KDQojc2VsZWN0IHB1YmxpY2F0aW9ucyBvZiB0aGUgY29ycmVzcG9uZGluZyB0aW1lIGVyYQ0KcHVic19zZWwgPC0gc29jX2RmX3B1YmxpY2F0aW9ucyAlPiUNCiAgICAgICAgICAgICAgbXV0YXRlKGF1dGhvciA9IHRvbG93ZXIoYXV0aG9yKSkgJT4lDQogICAgICAgICAgICAgIGZpbHRlcih5ZWFyPjIwMTQgJiB5ZWFyPDIwMjIpDQoNCnNhdmUocHVic19zZWwsIGZpbGUgPSAic29jX3B1YnNfc2VsXzIwMTVfMjAyMS5SRGF0YSIpDQoNCiNmaWxsIHRoZSBtYXRyaXgNCmZvciAoZWdvIGluIDE6IG5yb3coc29jX2RmKSkgew0KICBuYW1lX2VnbyA8LSBzb2NfZGYkbGFzdF9uYW1lW2Vnb10gI3doaWNoIGVnbz8gDQogIHB1YnNfc2VsMiA8LSBwdWJzX3NlbFtzdHJfZGV0ZWN0KHB1YnNfc2VsJGF1dGhvciwgbmFtZV9lZ28pLF0gI3B1YmxpY2F0aW9ucyBvZiBlZ28NCiAgZm9yIChhbHRlciBpbiAxOm5yb3coc29jX2RmKSl7DQogICAgbmFtZV9hbHRlciA8LSBzb2NfZGYkbGFzdF9uYW1lW2FsdGVyXSAjd2hpY2ggYWx0ZXI/IA0KICAgIG5ldHdvcmsyMDE1XzIwMjFbZWdvLGFsdGVyXSA8LSBzdW0oc3RyX2RldGVjdChwdWJzX3NlbDIkYXV0aG9yLCBuYW1lX2FsdGVyKSkgPiAxICNkaWQgYWx0ZXIgcHVibGlzaCB3aXRoIGVnbw0KICB9DQp9DQoNCnNhdmUobmV0d29yazIwMTVfMjAyMSwgZmlsZSA9ICJzb2NfbmV0d29yazIwMTVfMjAyMS5SRGF0YSIpDQpgYGANCg0K