This lab journal is created for the paper
Tolsma, J., Hofstra, B. and Mulders, AM (2025). How COVID-19
Exacerbated Gender Inequalities in Dutch Academia.
Scientometrics.
In this file we show how we went from the raw data sets scraped from
NARCIS to the working samples on which all analysis are based. Note that
the raw data files are not shared but please email jochem.tolsma@ru.nl
if you are interested in working with these raw data files. The working
sample(s) can be downloaded here.
Custom functions
rm(list = ls())
fpackage.check <- function(packages) {
lapply(packages, FUN = function(x) {
if (!require(x, character.only = TRUE)) {
install.packages(x, dependencies = TRUE)
library(x, character.only = TRUE)
}
})
}
fsave <- function(x, file = NULL, location = "./data/processed/") {
ifelse(!dir.exists("data"), dir.create("data"), FALSE)
ifelse(!dir.exists("data/processed"), dir.create("data/processed"), FALSE)
if (is.null(file))
file = deparse(substitute(x))
datename <- substr(gsub("[:-]", "", Sys.time()), 1, 8)
totalname <- paste(location, file, "_", datename, ".rda", sep = "")
save(x, file = totalname) #need to fix if file is reloaded as input name, not as x.
}
fload <- function(filename) {
load(filename)
get(ls()[ls() != "filename"])
}
fshowdf <- function(x, ...) {
knitr::kable(x, digits = 2, "html", ...) %>%
kableExtra::kable_styling(bootstrap_options = c("striped", "hover")) %>%
kableExtra::scroll_box(width = "100%", height = "300px")
}
colorize <- function(x, color) {sprintf("<span style='color: %s;'>%s</span>", color, x) }
Packages
packages = c("ggplot2", "tidyverse", "RColorBrewer", "dplyr", "stringdist", "stringi", "future.apply")
fpackage.check(packages)
Publications (part
1)
Scraped publications via NARCIS. These raw data sets are not shared
publicly but please email jochem.tolsma@ru.nl if you want to work with these raw
data.
# publications raw datafiles
pubs1 <- fload(file = "./data/pubs_meta_df.rda")
pubs2 <- fload(file = "./data/pubs_meta_df_2020_2021.rda")
pubs3 <- fload(file = "./data/pubs_meta_df_2020_2021_adden.rda")
pubs4 <- fload(file = "./data/pubs_meta_df_2022_adden.rda")
# Different variables in each publication dataset, align this
pubs1 <- pubs1[, c(1:4, 7, 231, 13)]
pubs2 <- pubs2[, c(1:3, 5, 9, 136, 16)]
pubs3 <- pubs3[, c(1:3, 5, 9, 141, 16)]
pubs4 <- pubs4[, c(1:4, 6, 113, 12)]
# combine
pubs12 <- rbind.data.frame(pubs1, pubs2)
pubs123 <- rbind.data.frame(pubs12, pubs3)
pubs <- rbind.data.frame(pubs123, pubs4)
Selection of publications
table(pubs$Type, useNA = "always")
pubs$Type <- as.factor(pubs$Type)
pubs <- pubs[(pubs$Type == "Artikel") | (pubs$Type == "Boek" | (pubs$Type == "Boekdeel") | (pubs$Type ==
"Conference Paper") | (pubs$Type == "Conference Proceedings") | (pubs$Type == "Conferentiebijdrage") |
(pubs$Type == "Patent") | (pubs$Type == "Rapport") | (pubs$Type == "Dataset") | (pubs$Type == "Review")),
] #845256
Removing duplicates
pubs %>%
filter(!is.na(Type)) %>%
mutate(Titel_ori = Titel, Titel = tolower(Titel), Titel = str_sub(Titel, 1, 40), Titel = str_replace_all(Titel,
"\\s", ""), Titel = str_replace_all(Titel, intToUtf8(8217), intToUtf8(39)), Titel = str_replace_all(Titel,
" :", ":"), Titel = str_replace_all(Titel, " \\(", "\\("), Titel = str_replace_all(Titel, "\\( ",
"\\("), Titel = str_replace_all(Titel, "\\) ", "\\)"), Titel = str_replace_all(Titel, " \\)",
"\\)")) %>%
group_by(person_id) %>%
distinct(Titel, .keep_all = TRUE) %>%
ungroup -> df_pubs
Removing pubs without pubyear
colnames(df_pubs) <- make.names(colnames(df_pubs), unique = TRUE)
# derive publication year from variable Data.issued
df_pubs %>%
mutate(pub_year = as.character(Date.issued), pub_year = substr(pub_year, 1, 4), pub_year = as.numeric(pub_year)) ->
df_pubs
df_pubs %>%
filter(!is.na(pub_year)) -> df_pubs #697695
Set pubs published in 2023 (16) to 2022
df_pubs %>%
mutate(pub_year = ifelse(pub_year > 2022, 2022, pub_year)) -> df_pubs
length(unique(df_pubs$Titel)) #478761
length(unique(df_pubs$person_id)) #27285
Saving the data
fsave(df_pubs, file = "df_covid_pubs", location = "./data/processed/")
PhDs
We use three processed datasets.
phds_variables.rda: dataset of Dutch PhDs between
1990-2021
This data set has been prepared by Anne Maaike Mulders, see (Mulders, Hofstra, and Tolsma 2024).
url_to_unique_id.rda: used to match different scraped
info from NARCIS and where different id variables are used.
Also load our publication dataset.
df_phd <- fload(file = "./data/processed/phds_variables.rda")
df_pubs <- fload(file = "./data/processed/20240701df_covid_pubs.rda")
df_id <- fload(file = "./data/url_to_unique_id.rda")
how many unique phds do we start with
length(unique(df_phd$id))
table(df_phd$start_pub)
add diss_url variable to df_phd dataframe
df_id$id <- paste0("i", df_id$id)
df_phd2 <- left_join(df_phd, df_id, by = c("id")) #we created some duplicates
add df_phd to publications
df_pubs2 <- left_join(df_pubs, df_phd2, by = c(person_id = "person_id1")) #we created some duplicates
Publications (part
2)
Delete publications
without phd info
df_pubs2 <- df_pubs2 %>%
filter(!is.na(id)) %>%
filter(dupid == 0) #6277728
so we lost approximately 70.000 publication. probably of scholars who
did publish but do not have a profile.
Removing
duplicates
This time use the DOI.
df_pubs2 %>%
group_by(person_id) %>%
mutate(duplicateDOI = (duplicated(DOI) & !is.na(DOI))) %>%
filter(!(duplicateDOI)) -> df_pubs2 #624143
We are only interested in publications after the Phd.
df_pubs2 <- df_pubs2 %>%
filter(phd_year < pub_year) #465283
We add three variables to the publications data:
- first year of publication after their PhD
- last year of publication after their PhD
- how many publications written 1-3 years prior to covid
df_pubs2 %>%
mutate(covidpub = as.numeric(pub_year > 2016 & pub_year < 2020)) %>%
group_by(id) %>%
dplyr::summarize(firstpub = min(pub_year, na.rm = T), lastpub = max(pub_year, na.rm = T), covidpub = sum(covidpub)) ->
flpubs
df_pubs2 %>%
left_join(flpubs) -> df_pubs2
Remove publications
and PhDs, given inclusion criteria
How many phds do we now have publications for:
length(unique(df_pubs2$id)) #19368
Only select Phds who published at least one publication in the three
years before COVID-19.
df_pubs2 %>%
filter(covidpub > 0) -> df_pubs2
length(unique(df_pubs2$id)) #10001
And only select Phds who have a publication career of at least three
years.
df_pubs2 %>%
filter(lastpub > firstpub + 1) -> df_pubs2
length(unique(df_pubs2$id)) #8303
Intermediate save to
construct our research domain variable
select only papers published after 2014
df_pubs_topic <- df_pubs2 %>%
filter(pub_year > 2014)
# fsave(df_pubs_topic, 'df_pubs_topic')
Based on these pubs we determine the field in which the authors are
active via OpenAlex. See here.
Author position of PhD
in publication
Prepare author-list
of each publication
CAUTION: We use multisession.
plan(multisession, workers = 10) ## Parallelize
# make a list for each publication of autors, cleaned a bit
str_split(df_pubs2$Auteur, ";") %>%
# lowercase
future_lapply(tolower) %>%
# Removing diacritics
future_lapply(stri_trans_general, id = "latin-ascii") %>%
# remove first names
future_lapply(sub, pattern = "\\(.*\\)", replacement = "") %>%
future_lapply(sub, pattern = ",.*", replacement = "") %>%
# remove van de etc.
future_lapply(sub, pattern = "'t ", replacement = "") %>%
future_lapply(sub, pattern = "d' ", replacement = "") %>%
future_lapply(sub, pattern = "de ", replacement = "") %>%
future_lapply(sub, pattern = "de la ", replacement = "") %>%
future_lapply(sub, pattern = "den ", replacement = "") %>%
future_lapply(sub, pattern = "del ", replacement = "") %>%
future_lapply(sub, pattern = "der ", replacement = "") %>%
future_lapply(sub, pattern = "des ", replacement = "") %>%
future_lapply(sub, pattern = "el ", replacement = "") %>%
future_lapply(sub, pattern = "el- ", replacement = "") %>%
future_lapply(sub, pattern = "in 't ", replacement = "") %>%
future_lapply(sub, pattern = "la ", replacement = "") %>%
future_lapply(sub, pattern = "le ", replacement = "") %>%
future_lapply(sub, pattern = "les ", replacement = "") %>%
future_lapply(sub, pattern = "op den ", replacement = "") %>%
future_lapply(sub, pattern = "ten ", replacement = "") %>%
future_lapply(sub, pattern = "ter ", replacement = "") %>%
future_lapply(sub, pattern = "tes ", replacement = "") %>%
future_lapply(sub, pattern = "van ", replacement = "") %>%
future_lapply(sub, pattern = "van 't ", replacement = "") %>%
future_lapply(sub, pattern = "van de ", replacement = "") %>%
future_lapply(sub, pattern = "van der ", replacement = "") %>%
future_lapply(sub, pattern = "van den ", replacement = "") %>%
future_lapply(sub, pattern = "von der ", replacement = "") %>%
future_lapply(sub, pattern = "op den ", replacement = "") %>%
future_lapply(sub, pattern = "ul ", replacement = "") %>%
# remove white spaces
future_lapply(str_squish) %>%
# remove initials
future_lapply(gsub, pattern = ".*\\.", replacement = "") %>%
# Remove double names separated by a dash THINK WE NEED TO KEEP NOT THE FIRST BUT LAST
future_lapply(sub, pattern = "\\-.*", replacement = "") %>%
# remove white spaces again
future_lapply(str_squish) %>%
# remove double names sperated by a space WE KEEP TEH FIRST
future_lapply(gsub, pattern = " .*$", replacement = "") -> pubsautors
Author position
We determine the position of the PhD in the author-list of each
publication.
We add two variables to the publicatin dataset pubs:
aut_max: the number of authors of each
publication
aut_pos: the position of the PhD in the
publication
Flow plan:
- step -1: check to see if we have a name.
- Stap 0: check to see if it is a sole-authored paper.
- Stap 1: check to see if we have one or more exact matches.
- 1.1: one exact match, determine position,
done.
- 1.2: two or more exact mathces, pick first done.
- Stap 2: check whether last name consists of one letter.
- 2.1: Yes, set position to
NA, done
- 2.2 No
- Stap 3: Determine distance between last name and the names on the
publication.
- 3.1: No minimial distance observed, set position to
NA,
done
- 3.2: One minimum, set position, done.
- Stap 4: when multiple minima, check to see if it is the same last
name.
- 4.1: Yes, pick first (arbitrary), done
- 4.2: No, set position to
NA, done
warnings <- NA
aut_pot <- aut_max <- code <- rep(NA, length(df_pubs2$lastname))
table(is.na(df_pubs2$Auteur), useNA = "always")
# loop over each publication
for (i in 1:length(df_pubs2$lastname)) {
tryCatch({
# step -1
if (is.na(df_pubs2$Auteur[i])) {
code[i] <- "(-1) no name" #probably have to set to 0 later
next
}
aut_max[i] <- length(pubsautors[[i]]) #number of authors
# step 0:
if (aut_max[i] == 1) {
aut_pot[i] <- 1
code[i] <- "(0) publication with 1 author"
next
}
# step 1:
if (df_pubs2$lastname[i] %in% pubsautors[[i]]) {
aut_pot[i] <- which.min(pubsautors[[i]] %in% df_pubs2$lastname[i]) #########pasop. which.min vindt eerste false, niet eerste true!!!!!!!!!!!!
if (sum(pubsautors[[i]] %in% df_pubs2$lastname[i]) == 1) {
code[i] <- "(1.1) one exact match"
} else {
code[i] <- "(1.2) >1 exact matches, position of 1st"
}
next
}
# step 2:
if (nchar(df_pubs2$lastname[i]) == 1) {
aut_pot[i] <- NA
code[i] <- "(2) lastname just 1 character, set to NA"
next
}
# step 3:
naut1 <- stringdist(df_pubs2$lastname[i], pubsautors[[i]], method = "jaccard")
naut2 <- stringdist(df_pubs2$lastname[i], pubsautors[[i]], method = "lv")
naut3 <- stringdist(df_pubs2$lastname[i], pubsautors[[i]], method = "jw", p = 0) #this one helps with double last names 'tolsma berkhof'
naut <- naut1 + naut2 + naut3
# step 3.1
if (is.na(min(naut)) | is.nan(min(naut))) {
aut_pot[i] <- NA
code[i] <- "(3.1) no minimum via matching, set to NA"
next
}
# step 3.2
if (sum(naut == min(naut)) == 1) {
aut_pot[i] <- which(naut == min(naut))
code[i] <- "(3.2) 1 minimum via matching"
next
}
# step 4.1
if (pubsautors[[i]][which(naut == min(naut))[1]] == pubsautors[[i]][which(naut == min(naut))[2]]) {
aut_pot[i] <- which.min(naut) #take the first, use this one to run everything.
code[i] <- "(4.1) >1 minima after matching of same lastname, position of 1st"
} else {
aut_pot[i] <- NA
code[i] <- "(4.2) >1 minima after matching of different lastnames, set to NA"
}
}, error = function(e) e, warning = function(w) {
print(i)
w
warnings <- append(warnings, i)
# print(j) stop('converted from warning: ', conditionMessage(w))
})
}
table(code, useNA = "always")
table(aut_pot, useNA = "always")
table(aut_max, useNA = "always")
df_pubs2$aut_pot <- aut_pot
df_pubs2$aut_max <- aut_max
df_pubs2$aut_code <- code
So we can play a little later by filtering on specific codes. or by
recoding the NAs of author positions via the code variable. For example,
setting all publications without author names as first author
publications. etc.
person period file
pubs per year per
author
I count the:
- total number of publications
- all first authored publications (inclusive sole authored
publications)
- all last authored publications (exclusive sole authored
publications)
- all sole authored publications
- all first authored articles (inclusive sole authored articles)
- all last authored articles (exclusive sole authored articles)
- all sole authored articles
pubs <- df_pubs2
# publication year
pubs %>%
mutate(pub_a = as.numeric(Type == "Artikel"), pub_first = as.numeric(aut_pot == 1), pub_last = as.numeric(aut_pot ==
aut_max & aut_pot != 1), pub_sole = as.numeric(aut_max == 1), pub_first_a = as.numeric(aut_pot ==
1 & Type == "Artikel"), pub_last_a = as.numeric(aut_pot == aut_max & aut_pot != 1 & Type == "Artikel"),
pub_sole_a = as.numeric(aut_max == 1 & Type == "Artikel")) -> pubs
# aggregate by id and pub_year. this is what we want in the person period file.
pubs %>%
group_by(id, pub_year) %>%
summarize(npubs = n(), npubs_a = sum(pub_a, na.rm = TRUE), firstpub = first(firstpub), lastpub = first(lastpub),
npubs_first = sum(pub_first, na.rm = TRUE), npubs_last = sum(pub_last, na.rm = TRUE), npubs_sole = sum(pub_sole,
na.rm = TRUE), npubs_first_a = sum(pub_first_a, na.rm = TRUE), npubs_last_a = sum(pub_last_a,
na.rm = TRUE), npubs_sole_a = sum(pub_sole_a, na.rm = TRUE), phd_year = first(phd_year),
gender = first(gender_2), field = first(field), ethnicity = first(ethnicity3), uni = first(uni),
id = first(id)) -> pubs2
# we aggregate also by id. this will help to find us the total publications over the career. but
# also to fill in the empty rows of the df_ppp later on.
pubs %>%
group_by(id) %>%
summarize(npubs_t = n(), npubs_a_t = sum(pub_a, na.rm = TRUE), firstpub = first(firstpub), lastpub = first(lastpub),
npubs_first_t = sum(pub_first, na.rm = TRUE), npubs_last_t = sum(pub_last, na.rm = TRUE), npubs_sole_t = sum(pub_sole,
na.rm = TRUE), npubs_first_a_t = sum(pub_first_a, na.rm = TRUE), npubs_last_a_t = sum(pub_last_a,
na.rm = TRUE), npubs_sole_a_t = sum(pub_sole_a, na.rm = TRUE), phd_year = first(phd_year),
gender = first(gender_2), field = first(field), ethnicity = first(ethnicity3), uni = first(uni),
id = first(id)) -> pubs3
empty ppf
# first make an empty dataset
pub_year <- c(1990:2022)
npubs_zero <- rep(0, length(pub_year))
id <- unique(df_pubs2$id)
nid <- length(id)
pub_year <- rep(pub_year, nid)
npubs_zero <- rep(npubs_zero, nid)
id <- rep(id, each = length(c(1990:2022)))
df <- data.frame(id, pub_year)
df %>%
arrange(id, pub_year) -> df
fill the ppf
data_ppf <- left_join(df, pubs3, by = c("id")) %>%
filter(pub_year >= firstpub) %>%
left_join(pubs2, by = c("id", "pub_year")) %>%
mutate(npubs = replace_na(npubs, 0), npubs_a = replace_na(npubs_a, 0), npubs_first = replace_na(npubs_first,
0), npubs_last = replace_na(npubs_last, 0), npubs_sole = replace_na(npubs_sole, 0), npubs_first_a = replace_na(npubs_first_a,
0), npubs_last_a = replace_na(npubs_last_a, 0), npubs_sole_a = replace_na(npubs_sole_a, 0)) %>%
rename_with(~gsub(".x", "", .x, fixed = TRUE)) %>%
dplyr::select(c("id", "pub_year", "phd_year", "gender", "field", "ethnicity", "uni", "npubs", "npubs_a",
"npubs_first", "npubs_last", "npubs_sole", "npubs_first_a", "npubs_last_a", "npubs_sole_a", "npubs_t",
"npubs_a_t", "npubs_first_t", "npubs_last_t", "npubs_sole_t", "npubs_first_a_t", "npubs_last_a_t",
"npubs_sole_a_t"))
saving
fsave(data_ppf, "df_ppf") #the person period file dataset for the analysis
fsave(df_pubs2, "df_pubs") #publications included in the ppf (with all info on phds attached as well)
Mulders, Anne Maaike, Bas Hofstra, and Jochem Tolsma. 2024.
“A
Matter of Time? Gender and Ethnic Inequality in the Academic Publishing
Careers of Dutch PhDs.” Quantitative Science Studies,
May, 1–29.
https://doi.org/10.1162/qss_a_00306.
LS0tDQp0aXRsZTogIkRhdGF3cmFuZ2xpbmciDQpiaWJsaW9ncmFwaHk6IHJlZmVyZW5jZXMuYmliDQotLS0NCg0KPCEtLS1wbGVhc2UgYmUgYXdhcmUgdGhhdCBjYWNoaW5nIGxhcmdlIG9iamVjdHMgaXMgcHJvYmxlbWF0aWMsIGhlbmNlIGNhY2hlLmxhenk9RkFMU0UgYW5kIHlvdSBtYXkgbmVlZCB0byB0dXJuIGNhY2hlPUZBTFNFIGZvciBjaHVuY2tzIGluIHdoaWNoIHlvdSBsb2FkIGxhcmdlIGRhdGFzZXRzIC0tLT4NCg0KYGBge3IsIGdsb2JhbHNldHRpbmdzLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQpsaWJyYXJ5KGtuaXRyKQ0KI2xpYnJhcnkocmdsKQ0Kb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGguY3V0b2ZmPTEwMCksdGlkeT1UUlVFLCB3YXJuaW5nID0gRkFMU0UsIGV2YWwgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLGNvbW1lbnQgPSAiIz4iLCBjYWNoZT1UUlVFLCByZXN1bHRzPSdob2xkJywgY2xhc3Muc291cmNlPWMoInRlc3QiKSwgY2xhc3Mub3V0cHV0PWMoInRlc3QyIiksIGNhY2hlLmxhenkgPSBGQUxTRSkNCm9wdGlvbnMod2lkdGggPSAxMDApDQpyZ2w6OnNldHVwS25pdHIoKQ0KDQprbml0cjo6b3B0c19jaHVuayRzZXQodGFiLmNhcC5wcmUgPSAiIiwgdGFiLmNhcC5zZXAgPSAiIikNCg0KY29sb3JpemUgPC0gZnVuY3Rpb24oeCwgY29sb3IpIHtzcHJpbnRmKCI8c3BhbiBzdHlsZT0nY29sb3I6ICVzOyc+JXM8L3NwYW4+IiwgY29sb3IsIHgpIH0NCg0KYGBgDQoNCmBgYHtyIGtsaXBweSwgZWNobz1GQUxTRSwgaW5jbHVkZT1UUlVFLCBldmFsPVRSVUV9DQprbGlwcHk6OmtsaXBweShwb3NpdGlvbiA9IGMoJ3RvcCcsICdyaWdodCcpKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJyZW1vdGVzIikNCiNyZW1vdGVzOjppbnN0YWxsX2dpdGh1Yigicmxlc3VyL2tsaXBweSIpDQoja2xpcHB5OjprbGlwcHkoY29sb3IgPSAnZGFya3JlZCcpDQoja2xpcHB5OjprbGlwcHkodG9vbHRpcF9tZXNzYWdlID0gJ0NsaWNrIHRvIGNvcHknLCB0b29sdGlwX3N1Y2Nlc3MgPSAnRG9uZScpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNClRoaXMgbGFiIGpvdXJuYWwgaXMgY3JlYXRlZCBmb3IgdGhlIHBhcGVyIA0KDQoqKlRvbHNtYSwgSi4sIEhvZnN0cmEsIEIuIGFuZCBNdWxkZXJzLCBBTSAoMjAyNSkuIEhvdyBDT1ZJRC0xOSBFeGFjZXJiYXRlZCBHZW5kZXIgSW5lcXVhbGl0aWVzIGluIER1dGNoIEFjYWRlbWlhLiAqU2NpZW50b21ldHJpY3MqLioqDQoNCkluIHRoaXMgZmlsZSB3ZSBzaG93IGhvdyB3ZSB3ZW50IGZyb20gdGhlIHJhdyBkYXRhIHNldHMgc2NyYXBlZCBmcm9tIE5BUkNJUyB0byB0aGUgd29ya2luZyBzYW1wbGVzIG9uIHdoaWNoIGFsbCBhbmFseXNpcyBhcmUgYmFzZWQuIE5vdGUgdGhhdCB0aGUgcmF3IGRhdGEgZmlsZXMgYXJlIG5vdCBzaGFyZWQgYnV0IHBsZWFzZSBlbWFpbCBqb2NoZW0udG9sc21hQHJ1Lm5sIGlmIHlvdSBhcmUgaW50ZXJlc3RlZCBpbiB3b3JraW5nIHdpdGggdGhlc2UgcmF3IGRhdGEgZmlsZXMuIFRoZSB3b3JraW5nIHNhbXBsZShzKSBjYW4gYmUgZG93bmxvYWRlZCBbaGVyZV0oaHR0cHM6Ly9jb3ZpZGR1dGNoYWNhZGVtaWEubmV0bGlmeS5hcHAvYW5hbHlzaXMpLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBDdXN0b20gZnVuY3Rpb25zDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJywgZXZhbCA9IFRSVUV9DQpybShsaXN0ID0gbHMoKSkNCg0KZnBhY2thZ2UuY2hlY2sgPC0gZnVuY3Rpb24ocGFja2FnZXMpIHsNCiAgICBsYXBwbHkocGFja2FnZXMsIEZVTiA9IGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgaWYgKCFyZXF1aXJlKHgsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsNCiAgICAgICAgICAgIGluc3RhbGwucGFja2FnZXMoeCwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgICAgICAgICAgIGxpYnJhcnkoeCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQ0KICAgICAgICB9DQogICAgfSkNCn0NCg0KZnNhdmUgPC0gZnVuY3Rpb24oeCwgZmlsZSA9IE5VTEwsIGxvY2F0aW9uID0gIi4vZGF0YS9wcm9jZXNzZWQvIikgew0KICAgIGlmZWxzZSghZGlyLmV4aXN0cygiZGF0YSIpLCBkaXIuY3JlYXRlKCJkYXRhIiksIEZBTFNFKQ0KICAgIGlmZWxzZSghZGlyLmV4aXN0cygiZGF0YS9wcm9jZXNzZWQiKSwgZGlyLmNyZWF0ZSgiZGF0YS9wcm9jZXNzZWQiKSwgRkFMU0UpDQogICAgaWYgKGlzLm51bGwoZmlsZSkpDQogICAgICAgIGZpbGUgPSBkZXBhcnNlKHN1YnN0aXR1dGUoeCkpDQogICAgZGF0ZW5hbWUgPC0gc3Vic3RyKGdzdWIoIls6LV0iLCAiIiwgU3lzLnRpbWUoKSksIDEsIDgpDQogICAgdG90YWxuYW1lIDwtIHBhc3RlKGxvY2F0aW9uLCBmaWxlLCAiXyIsIGRhdGVuYW1lLCAgIi5yZGEiLCBzZXAgPSAiIikNCiAgICBzYXZlKHgsIGZpbGUgPSB0b3RhbG5hbWUpICAjbmVlZCB0byBmaXggaWYgZmlsZSBpcyByZWxvYWRlZCBhcyBpbnB1dCBuYW1lLCBub3QgYXMgeC4gDQp9DQoNCmZsb2FkIDwtIGZ1bmN0aW9uKGZpbGVuYW1lKSB7DQogICAgbG9hZChmaWxlbmFtZSkNCiAgICBnZXQobHMoKVtscygpICE9ICJmaWxlbmFtZSJdKQ0KfQ0KDQpmc2hvd2RmIDwtIGZ1bmN0aW9uKHgsIC4uLikgew0KICAgIGtuaXRyOjprYWJsZSh4LCBkaWdpdHMgPSAyLCAiaHRtbCIsIC4uLikgJT4lDQogICAgICAgIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpICU+JQ0KICAgICAgICBrYWJsZUV4dHJhOjpzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiLCBoZWlnaHQgPSAiMzAwcHgiKQ0KfQ0KDQpjb2xvcml6ZSA8LSBmdW5jdGlvbih4LCBjb2xvcikge3NwcmludGYoIjxzcGFuIHN0eWxlPSdjb2xvcjogJXM7Jz4lczwvc3Bhbj4iLCBjb2xvciwgeCkgfQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIFBhY2thZ2VzDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJywgZXZhbCA9IFRSVUV9DQpwYWNrYWdlcyA9IGMoImdncGxvdDIiLCAidGlkeXZlcnNlIiwgIlJDb2xvckJyZXdlciIsICJkcGx5ciIsICJzdHJpbmdkaXN0IiwgInN0cmluZ2kiLCAiZnV0dXJlLmFwcGx5IikNCg0KZnBhY2thZ2UuY2hlY2socGFja2FnZXMpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgUHVibGljYXRpb25zIChwYXJ0IDEpDQoNClNjcmFwZWQgcHVibGljYXRpb25zIHZpYSBOQVJDSVMuIFRoZXNlIHJhdyBkYXRhIHNldHMgYXJlIG5vdCBzaGFyZWQgcHVibGljbHkgYnV0IHBsZWFzZSBlbWFpbCBqb2NoZW0udG9sc21hQHJ1Lm5sIGlmIHlvdSB3YW50IHRvIHdvcmsgd2l0aCB0aGVzZSByYXcgZGF0YS4gDQoNCmBgYHtyfQ0KIyBwdWJsaWNhdGlvbnMgcmF3IGRhdGFmaWxlcw0KcHViczEgPC0gZmxvYWQoZmlsZSA9ICIuL2RhdGEvcHVic19tZXRhX2RmLnJkYSIpDQpwdWJzMiA8LSBmbG9hZChmaWxlID0gIi4vZGF0YS9wdWJzX21ldGFfZGZfMjAyMF8yMDIxLnJkYSIpDQpwdWJzMyA8LSBmbG9hZChmaWxlID0gIi4vZGF0YS9wdWJzX21ldGFfZGZfMjAyMF8yMDIxX2FkZGVuLnJkYSIpDQpwdWJzNCA8LSBmbG9hZChmaWxlID0gIi4vZGF0YS9wdWJzX21ldGFfZGZfMjAyMl9hZGRlbi5yZGEiKQ0KYGBgDQoNCmBgYHtyIGRhdGF9DQojIERpZmZlcmVudCB2YXJpYWJsZXMgaW4gZWFjaCBwdWJsaWNhdGlvbiBkYXRhc2V0LCBhbGlnbiB0aGlzDQogcHViczEgPC0gcHViczFbLGMoMTo0LDcsMjMxLDEzKV0NCnB1YnMyIDwtIHB1YnMyWyxjKDE6Myw1LDksMTM2LCAxNildDQogcHViczMgPC0gcHViczNbLGMoMTozLDUsOSwxNDEsMTYpXQ0KIHB1YnM0IDwtIHB1YnM0WyxjKDE6NCw2LDExMywxMildDQoNCiMgY29tYmluZQ0KcHViczEyIDwtIHJiaW5kLmRhdGEuZnJhbWUocHViczEsIHB1YnMyKQ0KcHViczEyMyA8LSByYmluZC5kYXRhLmZyYW1lKHB1YnMxMiwgcHViczMpDQpwdWJzIDwtIHJiaW5kLmRhdGEuZnJhbWUocHViczEyMywgcHViczQpDQpgYGANCg0KU2VsZWN0aW9uIG9mIHB1YmxpY2F0aW9ucw0KDQpgYGB7ciB9DQp0YWJsZShwdWJzJFR5cGUsIHVzZU5BID0gImFsd2F5cyIpDQpwdWJzJFR5cGUgPC0gYXMuZmFjdG9yKHB1YnMkVHlwZSkNCiBwdWJzIDwtIHB1YnNbKHB1YnMkVHlwZT09IkFydGlrZWwiKSB8IChwdWJzJFR5cGU9PSJCb2VrIiB8IChwdWJzJFR5cGU9PSJCb2VrZGVlbCIpIHwgKHB1YnMkVHlwZT09IkNvbmZlcmVuY2UgUGFwZXIiKSB8IChwdWJzJFR5cGU9PSJDb25mZXJlbmNlIFByb2NlZWRpbmdzIikgfCAocHVicyRUeXBlPT0iQ29uZmVyZW50aWViaWpkcmFnZSIpIHwgKHB1YnMkVHlwZT09IlBhdGVudCIpIHwgKHB1YnMkVHlwZT09IlJhcHBvcnQiKSB8IChwdWJzJFR5cGU9PSJEYXRhc2V0IikgfCAocHVicyRUeXBlPT0iUmV2aWV3IikpLCBdICM4NDUyNTYNCmBgYA0KDQpSZW1vdmluZyBkdXBsaWNhdGVzDQoNCmBgYHtyfQ0KcHVicyAlPiUNCiAgZmlsdGVyKCFpcy5uYShUeXBlKSkgJT4lDQogIG11dGF0ZShUaXRlbF9vcmkgPSBUaXRlbCwgDQogICAgICAgICBUaXRlbCA9IHRvbG93ZXIoVGl0ZWwpLA0KICAgICAgICAgVGl0ZWwgPSBzdHJfc3ViKFRpdGVsLCAxLCA0MCksDQogICAgICAgICBUaXRlbCA9IHN0cl9yZXBsYWNlX2FsbChUaXRlbCwgIlxccyIsICIiKSwNCiAgICAgICAgIFRpdGVsID0gc3RyX3JlcGxhY2VfYWxsKFRpdGVsLCBpbnRUb1V0ZjgoODIxNyksIGludFRvVXRmOCgzOSkgKSwgDQogICAgICAgICBUaXRlbCA9IHN0cl9yZXBsYWNlX2FsbChUaXRlbCwgIiA6IiwgIjoiICksIA0KICAgICAgICAgVGl0ZWwgPSBzdHJfcmVwbGFjZV9hbGwoVGl0ZWwsICIgXFwoIiwgIlxcKCIgKSwNCiAgICAgICAgIFRpdGVsID0gc3RyX3JlcGxhY2VfYWxsKFRpdGVsLCAiXFwoICIsICJcXCgiICksDQogICAgICAgICBUaXRlbCA9IHN0cl9yZXBsYWNlX2FsbChUaXRlbCwgIlxcKSAiLCAiXFwpIiApLA0KICAgICAgICAgVGl0ZWwgPSBzdHJfcmVwbGFjZV9hbGwoVGl0ZWwsICIgXFwpIiwgIlxcKSIgKSkgJT4lDQogIGdyb3VwX2J5KHBlcnNvbl9pZCkgJT4lDQogIGRpc3RpbmN0KFRpdGVsLCAua2VlcF9hbGw9VFJVRSkgJT4lDQogIHVuZ3JvdXAgLT4gZGZfcHVicw0KYGBgDQoNClJlbW92aW5nIHB1YnMgd2l0aG91dCBwdWJ5ZWFyDQoNCmBgYHtyfQ0KY29sbmFtZXMoZGZfcHVicykgPC0gbWFrZS5uYW1lcyhjb2xuYW1lcyhkZl9wdWJzKSwgdW5pcXVlID0gVFJVRSkNCg0KIyBkZXJpdmUgcHVibGljYXRpb24geWVhciBmcm9tIHZhcmlhYmxlIERhdGEuaXNzdWVkDQpkZl9wdWJzICU+JSANCiAgbXV0YXRlKHB1Yl95ZWFyID0gYXMuY2hhcmFjdGVyKERhdGUuaXNzdWVkKSwNCiAgICAgICAgIHB1Yl95ZWFyID0gc3Vic3RyKHB1Yl95ZWFyLCAxLDQpLA0KICAgICAgICAgcHViX3llYXIgPSBhcy5udW1lcmljKHB1Yl95ZWFyKSkgLT4gZGZfcHVicw0KDQpkZl9wdWJzICU+JQ0KICBmaWx0ZXIoIWlzLm5hKHB1Yl95ZWFyKSkgLT4gZGZfcHVicyAjNjk3Njk1DQoNCmBgYA0KDQpTZXQgcHVicyBwdWJsaXNoZWQgaW4gMjAyMyAoMTYpIHRvIDIwMjINCg0KYGBge3J9DQpkZl9wdWJzICU+JSANCiAgbXV0YXRlKHB1Yl95ZWFyID0gaWZlbHNlKHB1Yl95ZWFyPjIwMjIsIDIwMjIsIHB1Yl95ZWFyKSkgLT4gZGZfcHVicw0KYGBgDQoNCmBgYHtyfQ0KbGVuZ3RoKHVuaXF1ZShkZl9wdWJzJFRpdGVsKSkgIzQ3ODc2MQ0KbGVuZ3RoKHVuaXF1ZShkZl9wdWJzJHBlcnNvbl9pZCkpICMyNzI4NQ0KDQpgYGANCg0KU2F2aW5nIHRoZSBkYXRhDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KZnNhdmUoZGZfcHVicywgZmlsZSA9ICJkZl9jb3ZpZF9wdWJzIiwgbG9jYXRpb24gPSAiLi9kYXRhL3Byb2Nlc3NlZC8iKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIFBoRHMNCg0KV2UgdXNlIHRocmVlIHByb2Nlc3NlZCBkYXRhc2V0cy4NCg0KLSAgIGBwaGRzX3ZhcmlhYmxlcy5yZGFgOiBkYXRhc2V0IG9mIER1dGNoIFBoRHMgYmV0d2VlbiAxOTkwLTIwMjENCg0KVGhpcyBkYXRhIHNldCBoYXMgYmVlbiBwcmVwYXJlZCBieSBBbm5lIE1hYWlrZSBNdWxkZXJzLCBzZWUgW0BtdWxkZXJzMjAyNF0uIA0KDQotICAgYHVybF90b191bmlxdWVfaWQucmRhYDogdXNlZCB0byBtYXRjaCBkaWZmZXJlbnQgc2NyYXBlZCBpbmZvIGZyb20gTkFSQ0lTIGFuZCB3aGVyZSBkaWZmZXJlbnQgaWQgdmFyaWFibGVzIGFyZSB1c2VkLg0KDQpBbHNvIGxvYWQgb3VyIHB1YmxpY2F0aW9uIGRhdGFzZXQuDQoNCmBgYHtyIH0NCmRmX3BoZCA8LSBmbG9hZChmaWxlID0gIi4vZGF0YS9wcm9jZXNzZWQvcGhkc192YXJpYWJsZXMucmRhIikNCg0KZGZfcHVicyA8LSBmbG9hZChmaWxlID0gIi4vZGF0YS9wcm9jZXNzZWQvMjAyNDA3MDFkZl9jb3ZpZF9wdWJzLnJkYSIpDQoNCmRmX2lkIDwtIGZsb2FkKGZpbGUgPSAiLi9kYXRhL3VybF90b191bmlxdWVfaWQucmRhIikNCmBgYA0KDQpob3cgbWFueSB1bmlxdWUgcGhkcyBkbyB3ZSBzdGFydCB3aXRoDQoNCmBgYHtyfQ0KbGVuZ3RoKHVuaXF1ZShkZl9waGQkaWQpKQ0KdGFibGUoZGZfcGhkJHN0YXJ0X3B1YikNCmBgYA0KDQphZGQgZGlzc191cmwgdmFyaWFibGUgdG8gZGZfcGhkIGRhdGFmcmFtZQ0KDQpgYGB7cn0NCmRmX2lkJGlkIDwtIHBhc3RlMCgiaSIsIGRmX2lkJGlkKQ0KZGZfcGhkMiA8LSBsZWZ0X2pvaW4oZGZfcGhkLCBkZl9pZCwgYnk9YygiaWQiKSkgI3dlIGNyZWF0ZWQgc29tZSBkdXBsaWNhdGVzDQpgYGANCg0KYWRkIGRmX3BoZCB0byBwdWJsaWNhdGlvbnMNCg0KYGBge3J9DQpkZl9wdWJzMiA8LSBsZWZ0X2pvaW4oZGZfcHVicywgZGZfcGhkMiwgYnk9YygicGVyc29uX2lkIiA9ICJwZXJzb25faWQxIikpICN3ZSBjcmVhdGVkIHNvbWUgZHVwbGljYXRlcw0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIFB1YmxpY2F0aW9ucyAocGFydCAyKQ0KDQojIyBEZWxldGUgcHVibGljYXRpb25zIHdpdGhvdXQgcGhkIGluZm8NCg0KYGBge3J9DQpkZl9wdWJzMiA8LSBkZl9wdWJzMiAlPiUgDQogICAgICAgICAgICBmaWx0ZXIoIWlzLm5hKGlkKSkgJT4lDQogICAgICAgICAgICBmaWx0ZXIoZHVwaWQgPT0gMCkgIzYyNzc3MjgNCmBgYA0KDQpzbyB3ZSBsb3N0IGFwcHJveGltYXRlbHkgNzAuMDAwIHB1YmxpY2F0aW9uLiBwcm9iYWJseSBvZiBzY2hvbGFycyB3aG8gZGlkIHB1Ymxpc2ggYnV0IGRvIG5vdCBoYXZlIGEgcHJvZmlsZS4NCg0KIyMgUmVtb3ZpbmcgZHVwbGljYXRlcw0KDQpUaGlzIHRpbWUgdXNlIHRoZSBET0kuDQoNCmBgYHtyfQ0KZGZfcHViczIgJT4lDQogIGdyb3VwX2J5KHBlcnNvbl9pZCkgJT4lDQogIG11dGF0ZShkdXBsaWNhdGVET0kgPSAoZHVwbGljYXRlZChET0kpICYgIWlzLm5hKERPSSkpKSAlPiUNCiAgZmlsdGVyKCEoZHVwbGljYXRlRE9JKSkgLT4gZGZfcHViczIgIzYyNDE0Mw0KDQpgYGANCg0KV2UgYXJlIG9ubHkgaW50ZXJlc3RlZCBpbiBwdWJsaWNhdGlvbnMgYWZ0ZXIgdGhlIFBoZC4NCg0KYGBge3J9DQpkZl9wdWJzMiA8LSBkZl9wdWJzMiAlPiUgDQogICAgICAgICAgICBmaWx0ZXIocGhkX3llYXI8cHViX3llYXIpICM0NjUyODMNCmBgYA0KDQpXZSBhZGQgdGhyZWUgdmFyaWFibGVzIHRvIHRoZSBwdWJsaWNhdGlvbnMgZGF0YToNCg0KLSAgIGZpcnN0IHllYXIgb2YgcHVibGljYXRpb24gYWZ0ZXIgdGhlaXIgUGhEXA0KLSAgIGxhc3QgeWVhciBvZiBwdWJsaWNhdGlvbiBhZnRlciB0aGVpciBQaERcDQotICAgaG93IG1hbnkgcHVibGljYXRpb25zIHdyaXR0ZW4gMS0zIHllYXJzIHByaW9yIHRvIGNvdmlkDQoNCmBgYHtyfQ0KZGZfcHViczIgJT4lIA0KICBtdXRhdGUoY292aWRwdWIgPSBhcy5udW1lcmljKHB1Yl95ZWFyPjIwMTYgJiBwdWJfeWVhcjwyMDIwKSkgJT4lDQogIGdyb3VwX2J5KGlkKSAlPiUNCiAgZHBseXI6OnN1bW1hcml6ZSgNCiAgICAgICAgICAgIGZpcnN0cHViID0gbWluKHB1Yl95ZWFyLCBuYS5ybT1UKSwNCiAgICAgICAgICAgIGxhc3RwdWIgPSBtYXgocHViX3llYXIsIG5hLnJtPVQpLCANCiAgICAgICAgICAgIGNvdmlkcHViPSBzdW0oY292aWRwdWIpKSAtPiBmbHB1YnMNCg0KZGZfcHViczIgJT4lIGxlZnRfam9pbihmbHB1YnMpIC0+IGRmX3B1YnMyDQpgYGANCg0KIyMgUmVtb3ZlIHB1YmxpY2F0aW9ucyBhbmQgUGhEcywgZ2l2ZW4gaW5jbHVzaW9uIGNyaXRlcmlhDQoNCkhvdyBtYW55IHBoZHMgZG8gd2Ugbm93IGhhdmUgcHVibGljYXRpb25zIGZvcjoNCg0KYGBge3J9DQpsZW5ndGgodW5pcXVlKGRmX3B1YnMyJGlkKSkgIzE5MzY4DQpgYGANCg0KT25seSBzZWxlY3QgUGhkcyB3aG8gcHVibGlzaGVkIGF0IGxlYXN0IG9uZSBwdWJsaWNhdGlvbiBpbiB0aGUgdGhyZWUgeWVhcnMgYmVmb3JlIENPVklELTE5Lg0KDQpgYGB7cn0NCmRmX3B1YnMyICU+JSBmaWx0ZXIoY292aWRwdWIgPiAwKSAtPiBkZl9wdWJzMg0KbGVuZ3RoKHVuaXF1ZShkZl9wdWJzMiRpZCkpICMxMDAwMQ0KYGBgDQoNCkFuZCBvbmx5IHNlbGVjdCBQaGRzIHdobyBoYXZlIGEgcHVibGljYXRpb24gY2FyZWVyIG9mIGF0IGxlYXN0IHRocmVlIHllYXJzLg0KDQpgYGB7cn0NCmRmX3B1YnMyICU+JSBmaWx0ZXIobGFzdHB1YiA+IGZpcnN0cHViICsgMSkgLT4gZGZfcHViczINCmxlbmd0aCh1bmlxdWUoZGZfcHViczIkaWQpKSAjODMwMw0KYGBgDQoNCiMjIEludGVybWVkaWF0ZSBzYXZlIHRvIGNvbnN0cnVjdCBvdXIgcmVzZWFyY2ggZG9tYWluIHZhcmlhYmxlDQoNCnNlbGVjdCBvbmx5IHBhcGVycyBwdWJsaXNoZWQgYWZ0ZXIgMjAxNA0KDQpgYGB7cn0NCmRmX3B1YnNfdG9waWMgPC0gZGZfcHViczIgJT4lIA0KICAgICAgICAgICAgZmlsdGVyKHB1Yl95ZWFyID4gMjAxNCkgDQojZnNhdmUoZGZfcHVic190b3BpYywgImRmX3B1YnNfdG9waWMiKQ0KYGBgDQoNCkJhc2VkIG9uIHRoZXNlIHB1YnMgd2UgZGV0ZXJtaW5lIHRoZSBmaWVsZCBpbiB3aGljaCB0aGUgYXV0aG9ycyBhcmUgYWN0aXZlIHZpYSBPcGVuQWxleC4gU2VlIFtoZXJlXShodHRwczovL2NvdmlkZHV0Y2hhY2FkZW1pYS5uZXRsaWZ5LmFwcC9vYV90b3BpY3NfZmV0Y2gpLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBBdXRob3IgcG9zaXRpb24gb2YgUGhEIGluIHB1YmxpY2F0aW9uDQoNCiMjIFByZXBhcmUgYXV0aG9yLWxpc3Qgb2YgZWFjaCBwdWJsaWNhdGlvbg0KDQpgciBjb2xvcml6ZSgiQ0FVVElPTjogV2UgdXNlIG11bHRpc2Vzc2lvbi4iLCAicmVkIilgDQoNCmBgYHtyfQ0KcGxhbihtdWx0aXNlc3Npb24sIHdvcmtlcnMgPSAxMCkgIyMgUGFyYWxsZWxpemUgDQoNCiNtYWtlIGEgbGlzdCBmb3IgZWFjaCBwdWJsaWNhdGlvbiBvZiBhdXRvcnMsIGNsZWFuZWQgYSBiaXQNCnN0cl9zcGxpdChkZl9wdWJzMiRBdXRldXIsICI7IikgJT4lDQogICNsb3dlcmNhc2UNCiAgZnV0dXJlX2xhcHBseSh0b2xvd2VyKSAlPiUgDQogICMgUmVtb3ZpbmcgZGlhY3JpdGljcw0KICBmdXR1cmVfbGFwcGx5KHN0cmlfdHJhbnNfZ2VuZXJhbCwgaWQgPSAibGF0aW4tYXNjaWkiKSAlPiUNCiAgI3JlbW92ZSBmaXJzdCBuYW1lcw0KICBmdXR1cmVfbGFwcGx5KHN1YiwgcGF0dGVybj0iXFwoLipcXCkiLCByZXBsYWNlbWVudD0iIikgJT4lDQogIGZ1dHVyZV9sYXBwbHkoc3ViLCBwYXR0ZXJuPSIsLioiLCByZXBsYWNlbWVudD0iIikgJT4lDQogICNyZW1vdmUgdmFuIGRlIGV0Yy4gDQoNCiAgZnV0dXJlX2xhcHBseShzdWIsIHBhdHRlcm49Iid0ICIsIHJlcGxhY2VtZW50PSIiKSAlPiUNCiAgZnV0dXJlX2xhcHBseShzdWIsIHBhdHRlcm49ImQnICIsIHJlcGxhY2VtZW50PSIiKSAlPiUNCiAgZnV0dXJlX2xhcHBseShzdWIsIHBhdHRlcm49ImRlICIsIHJlcGxhY2VtZW50PSIiKSAlPiUNCiAgZnV0dXJlX2xhcHBseShzdWIsIHBhdHRlcm49ImRlIGxhICIsIHJlcGxhY2VtZW50PSIiKSAlPiUNCiAgZnV0dXJlX2xhcHBseShzdWIsIHBhdHRlcm49ImRlbiAiLCByZXBsYWNlbWVudD0iIikgJT4lDQogIGZ1dHVyZV9sYXBwbHkoc3ViLCBwYXR0ZXJuPSJkZWwgIiwgcmVwbGFjZW1lbnQ9IiIpICU+JQ0KICBmdXR1cmVfbGFwcGx5KHN1YiwgcGF0dGVybj0iZGVyICIsIHJlcGxhY2VtZW50PSIiKSAlPiUNCiAgZnV0dXJlX2xhcHBseShzdWIsIHBhdHRlcm49ImRlcyAiLCByZXBsYWNlbWVudD0iIikgJT4lDQogIGZ1dHVyZV9sYXBwbHkoc3ViLCBwYXR0ZXJuPSJlbCAiLCByZXBsYWNlbWVudD0iIikgJT4lDQogIGZ1dHVyZV9sYXBwbHkoc3ViLCBwYXR0ZXJuPSJlbC0gIiwgcmVwbGFjZW1lbnQ9IiIpICU+JQ0KICBmdXR1cmVfbGFwcGx5KHN1YiwgcGF0dGVybj0iaW4gJ3QgIiwgcmVwbGFjZW1lbnQ9IiIpICU+JQ0KICBmdXR1cmVfbGFwcGx5KHN1YiwgcGF0dGVybj0ibGEgIiwgcmVwbGFjZW1lbnQ9IiIpICU+JQ0KICBmdXR1cmVfbGFwcGx5KHN1YiwgcGF0dGVybj0ibGUgIiwgcmVwbGFjZW1lbnQ9IiIpICU+JQ0KICBmdXR1cmVfbGFwcGx5KHN1YiwgcGF0dGVybj0ibGVzICIsIHJlcGxhY2VtZW50PSIiKSAlPiUNCiAgZnV0dXJlX2xhcHBseShzdWIsIHBhdHRlcm49Im9wIGRlbiAiLCByZXBsYWNlbWVudD0iIikgJT4lDQogIGZ1dHVyZV9sYXBwbHkoc3ViLCBwYXR0ZXJuPSJ0ZW4gIiwgcmVwbGFjZW1lbnQ9IiIpICU+JQ0KICBmdXR1cmVfbGFwcGx5KHN1YiwgcGF0dGVybj0idGVyICIsIHJlcGxhY2VtZW50PSIiKSAlPiUNCiAgZnV0dXJlX2xhcHBseShzdWIsIHBhdHRlcm49InRlcyAiLCByZXBsYWNlbWVudD0iIikgJT4lDQogIGZ1dHVyZV9sYXBwbHkoc3ViLCBwYXR0ZXJuPSJ2YW4gIiwgcmVwbGFjZW1lbnQ9IiIpICU+JQ0KICBmdXR1cmVfbGFwcGx5KHN1YiwgcGF0dGVybj0idmFuICd0ICIsIHJlcGxhY2VtZW50PSIiKSAlPiUNCiAgZnV0dXJlX2xhcHBseShzdWIsIHBhdHRlcm49InZhbiBkZSAiLCByZXBsYWNlbWVudD0iIikgJT4lDQogIGZ1dHVyZV9sYXBwbHkoc3ViLCBwYXR0ZXJuPSJ2YW4gZGVyICIsIHJlcGxhY2VtZW50PSIiKSAlPiUNCiAgZnV0dXJlX2xhcHBseShzdWIsIHBhdHRlcm49InZhbiBkZW4gIiwgcmVwbGFjZW1lbnQ9IiIpICU+JQ0KICBmdXR1cmVfbGFwcGx5KHN1YiwgcGF0dGVybj0idm9uIGRlciAiLCByZXBsYWNlbWVudD0iIikgJT4lDQogIGZ1dHVyZV9sYXBwbHkoc3ViLCBwYXR0ZXJuPSJvcCBkZW4gIiwgcmVwbGFjZW1lbnQ9IiIpICU+JQ0KICBmdXR1cmVfbGFwcGx5KHN1YiwgcGF0dGVybj0idWwgIiwgcmVwbGFjZW1lbnQ9IiIpICU+JQ0KICAjcmVtb3ZlIHdoaXRlIHNwYWNlcw0KICBmdXR1cmVfbGFwcGx5KHN0cl9zcXVpc2gpICU+JQ0KICAjIHJlbW92ZSBpbml0aWFscw0KICBmdXR1cmVfbGFwcGx5KGdzdWIsIHBhdHRlcm49ICIuKlxcLiIsIHJlcGxhY2VtZW50PSIiKSAlPiUNCiAgIyBSZW1vdmUgZG91YmxlIG5hbWVzIHNlcGFyYXRlZCBieSBhIGRhc2ggVEhJTksgV0UgTkVFRCBUTyBLRUVQIE5PVCBUSEUgRklSU1QgQlVUIExBU1QNCiAgZnV0dXJlX2xhcHBseShzdWIsIHBhdHRlcm49ICJcXC0uKiIsIHJlcGxhY2VtZW50PSIiKSAlPiUNCiAgI3JlbW92ZSB3aGl0ZSBzcGFjZXMgYWdhaW4NCiAgZnV0dXJlX2xhcHBseShzdHJfc3F1aXNoKSAlPiUNCiAgI3JlbW92ZSBkb3VibGUgbmFtZXMgc3BlcmF0ZWQgYnkgYSBzcGFjZSBXRSBLRUVQIFRFSCBGSVJTVA0KICBmdXR1cmVfbGFwcGx5KGdzdWIsIHBhdHRlcm49ICIgLiokIiwgcmVwbGFjZW1lbnQ9IiIpIC0+ICBwdWJzYXV0b3JzDQoNCg0KYGBgDQoNCiMjIEF1dGhvciBwb3NpdGlvbg0KDQo8IS0tLXdvdWxkIGJlIGdyZWF0IGlmIHdlIGxvb2sgdG9nZXRoZXIgYXQgdGhlIGZsb3cgcGxhbiBhbmQgdGhlIGNvZGUtLS0+DQoNCldlIGRldGVybWluZSB0aGUgcG9zaXRpb24gb2YgdGhlIFBoRCBpbiB0aGUgYXV0aG9yLWxpc3Qgb2YgZWFjaCBwdWJsaWNhdGlvbi4NCg0KV2UgYWRkIHR3byB2YXJpYWJsZXMgdG8gdGhlIHB1YmxpY2F0aW4gZGF0YXNldCBgcHVic2A6DQoNCi0gICBgYXV0X21heGA6IHRoZSBudW1iZXIgb2YgYXV0aG9ycyBvZiBlYWNoIHB1YmxpY2F0aW9uXA0KLSAgIGBhdXRfcG9zYDogdGhlIHBvc2l0aW9uIG9mIHRoZSBQaEQgaW4gdGhlIHB1YmxpY2F0aW9uDQoNCioqRmxvdyBwbGFuOioqDQoNCi0gICBzdGVwIC0xOiBjaGVjayB0byBzZWUgaWYgd2UgaGF2ZSBhIG5hbWUuDQogICAgLSAgIDEuMTogTm8sICoqZG9uZSoqXA0KLSAgIFN0YXAgMDogY2hlY2sgdG8gc2VlIGlmIGl0IGlzIGEgc29sZS1hdXRob3JlZCBwYXBlci4NCiAgICAtICAgMC4xOiBZZXMsICoqZG9uZSoqXA0KICAgIC0gICAwLjI6IE5vXA0KLSAgIFN0YXAgMTogY2hlY2sgdG8gc2VlIGlmIHdlIGhhdmUgb25lIG9yIG1vcmUgZXhhY3QgbWF0Y2hlcy4NCiAgICAtICAgMS4xOiBvbmUgZXhhY3QgbWF0Y2gsIGRldGVybWluZSBwb3NpdGlvbiwgKipkb25lKiouXA0KICAgIC0gICAxLjI6IHR3byBvciBtb3JlIGV4YWN0IG1hdGhjZXMsIHBpY2sgZmlyc3QgKipkb25lKiouW14xXVwNCi0gICBTdGFwIDI6IGNoZWNrIHdoZXRoZXIgbGFzdCBuYW1lIGNvbnNpc3RzIG9mIG9uZSBsZXR0ZXIuDQogICAgLSAgIDIuMTogWWVzLCBzZXQgcG9zaXRpb24gdG8gYE5BYCwgZG9uZVwNCiAgICAtICAgMi4yIE5vXA0KLSAgIFN0YXAgMzogRGV0ZXJtaW5lIGRpc3RhbmNlIGJldHdlZW4gbGFzdCBuYW1lIGFuZCB0aGUgbmFtZXMgb24gdGhlIHB1YmxpY2F0aW9uLg0KICAgIC0gICAzLjE6IE5vIG1pbmltaWFsIGRpc3RhbmNlIG9ic2VydmVkLCBzZXQgcG9zaXRpb24gdG8gYE5BYCwgKipkb25lKipcDQogICAgLSAgIDMuMjogT25lIG1pbmltdW0sIHNldCBwb3NpdGlvbiwgKipkb25lKiouXA0KLSAgIFN0YXAgNDogd2hlbiBtdWx0aXBsZSBtaW5pbWEsIGNoZWNrIHRvIHNlZSBpZiBpdCBpcyB0aGUgc2FtZSBsYXN0IG5hbWUuDQogICAgLSAgIDQuMTogWWVzLCBwaWNrIGZpcnN0IChhcmJpdHJhcnkpLCAqKmRvbmUqKlwNCiAgICAtICAgNC4yOiBObywgc2V0IHBvc2l0aW9uIHRvIGBOQWAsICoqZG9uZSoqDQoNClteMV06IFdlIHBpY2sgdGhlIGZpcnN0IGJlY2F1c2UgYW4gaW5zcGVjdGlvbiBvZiBOYXJjaXMgc2VlbXMgdG8gaW5kaWNhdGUgdGhhdCB0aGUgbGVhZCBhdXRob3IgaXMgYXQgdGltZXMgYWRkaXRpb25hbGx5IG1lbnRpb25lZCBhcyBsYXN0IGF1dGhvci4NCg0KYGBge3J9DQp3YXJuaW5ncyA8LSBOQQ0KYXV0X3BvdCA8LSBhdXRfbWF4IDwtIGNvZGUgPC0gcmVwKE5BLCBsZW5ndGgoZGZfcHViczIkbGFzdG5hbWUpKQ0KDQp0YWJsZShpcy5uYShkZl9wdWJzMiRBdXRldXIpLCB1c2VOQSA9ICJhbHdheXMiKQ0KDQojbG9vcCBvdmVyIGVhY2ggcHVibGljYXRpb24NCmZvciAoaSBpbiAxOmxlbmd0aChkZl9wdWJzMiRsYXN0bmFtZSkpIHsNCnRyeUNhdGNoKHsNCiAgI3N0ZXAgLTEgDQogIGlmKGlzLm5hKGRmX3B1YnMyJEF1dGV1cltpXSkpIHsNCiAgICBjb2RlW2ldIDwtICIoLTEpIG5vIG5hbWUiICNwcm9iYWJseSBoYXZlIHRvIHNldCB0byAwIGxhdGVyDQogICAgbmV4dA0KICB9DQogIGF1dF9tYXhbaV0gPC0gbGVuZ3RoKHB1YnNhdXRvcnNbW2ldXSkgI251bWJlciBvZiBhdXRob3JzIA0KICAjc3RlcCAwOiANCiAgaWYgKGF1dF9tYXhbaV0gPT0gMSkgew0KICAgIGF1dF9wb3RbaV0gPC0gMQ0KICAgIGNvZGVbaV0gPC0gIigwKSBwdWJsaWNhdGlvbiB3aXRoIDEgYXV0aG9yIg0KICAgIG5leHQgDQogIH0NCiAgI3N0ZXAgMTogDQogIGlmIChkZl9wdWJzMiRsYXN0bmFtZVtpXSAlaW4lIHB1YnNhdXRvcnNbW2ldXSkgew0KICAgIGF1dF9wb3RbaV0gPC0gd2hpY2gubWluKHB1YnNhdXRvcnNbW2ldXSAlaW4lIGRmX3B1YnMyJGxhc3RuYW1lW2ldKSAjIyMjIyMjIyNwYXNvcC4gd2hpY2gubWluIHZpbmR0IGVlcnN0ZSBmYWxzZSwgbmlldCBlZXJzdGUgdHJ1ZSEhISEhISEhISEhIQ0KICAgIGlmIChzdW0ocHVic2F1dG9yc1tbaV1dICVpbiUgZGZfcHViczIkbGFzdG5hbWVbaV0pID09IDEpIHsNCiAgICBjb2RlW2ldIDwtICIoMS4xKSBvbmUgZXhhY3QgbWF0Y2giICANCiAgICB9IGVsc2Ugew0KICAgICAgY29kZVtpXSA8LSAiKDEuMikgPjEgZXhhY3QgbWF0Y2hlcywgcG9zaXRpb24gb2YgMXN0IiAgDQogICAgICB9DQogICAgbmV4dA0KICB9DQogICNzdGVwIDI6DQogIGlmIChuY2hhcihkZl9wdWJzMiRsYXN0bmFtZVtpXSk9PTEpIHsNCiAgICBhdXRfcG90W2ldIDwtIE5BDQogICAgY29kZVtpXSA8LSAiKDIpIGxhc3RuYW1lIGp1c3QgMSBjaGFyYWN0ZXIsIHNldCB0byBOQSINCiAgICBuZXh0DQogIH0NCiAgI3N0ZXAgMzogDQogIG5hdXQxIDwtIHN0cmluZ2Rpc3QoZGZfcHViczIkbGFzdG5hbWVbaV0sIHB1YnNhdXRvcnNbW2ldXSwgbWV0aG9kPSJqYWNjYXJkIikNCiAgbmF1dDIgPC0gc3RyaW5nZGlzdChkZl9wdWJzMiRsYXN0bmFtZVtpXSwgcHVic2F1dG9yc1tbaV1dLCBtZXRob2Q9Imx2IikNCiAgbmF1dDMgPC0gc3RyaW5nZGlzdChkZl9wdWJzMiRsYXN0bmFtZVtpXSwgcHVic2F1dG9yc1tbaV1dLCBtZXRob2Q9Imp3IiwgcD0wKSAjdGhpcyBvbmUgaGVscHMgd2l0aCBkb3VibGUgbGFzdCBuYW1lcyAidG9sc21hIGJlcmtob2YiDQogIG5hdXQgPC0gbmF1dDEgKyBuYXV0MiArIG5hdXQzDQogICNzdGVwIDMuMQ0KICBpZiAoaXMubmEobWluKG5hdXQpKSB8IGlzLm5hbihtaW4obmF1dCkpKSB7DQogICAgYXV0X3BvdFtpXSA8LSBOQQ0KICAgIGNvZGVbaV0gPC0gIigzLjEpIG5vIG1pbmltdW0gdmlhIG1hdGNoaW5nLCBzZXQgdG8gTkEiDQogICAgbmV4dA0KICB9DQogICNzdGVwIDMuMiANCiAgaWYgKHN1bShuYXV0PT1taW4obmF1dCkpID09IDEpIHsNCiAgICBhdXRfcG90W2ldIDwtIHdoaWNoKG5hdXQ9PW1pbihuYXV0KSkNCiAgICBjb2RlW2ldIDwtICIoMy4yKSAxIG1pbmltdW0gdmlhIG1hdGNoaW5nIg0KICAgIG5leHQNCiAgfQ0KICAjc3RlcCA0LjENCiAgaWYgKHB1YnNhdXRvcnNbW2ldXVt3aGljaChuYXV0PT1taW4obmF1dCkpWzFdXSA9PSAgcHVic2F1dG9yc1tbaV1dW3doaWNoKG5hdXQ9PW1pbihuYXV0KSlbMl1dKSB7DQogICAgYXV0X3BvdFtpXSA8LSB3aGljaC5taW4obmF1dCkgICAjdGFrZSB0aGUgZmlyc3QsIHVzZSB0aGlzIG9uZSB0byBydW4gZXZlcnl0aGluZy4NCiAgICBjb2RlW2ldIDwtICIoNC4xKSA+MSBtaW5pbWEgYWZ0ZXIgbWF0Y2hpbmcgb2Ygc2FtZSBsYXN0bmFtZSwgcG9zaXRpb24gb2YgMXN0Ig0KICB9IGVsc2Ugew0KICAgIGF1dF9wb3RbaV0gPC0gTkENCiAgICBjb2RlW2ldIDwtICIoNC4yKSA+MSBtaW5pbWEgYWZ0ZXIgbWF0Y2hpbmcgb2YgZGlmZmVyZW50IGxhc3RuYW1lcywgc2V0IHRvIE5BIg0KICB9DQp9ICwgZXJyb3IgPSBmdW5jdGlvbihlKSBlLCB3YXJuaW5nID0gZnVuY3Rpb24odykgew0KICBwcmludChpKQ0KICB3DQogIHdhcm5pbmdzIDwtIGFwcGVuZCh3YXJuaW5ncywgaSkNCiAgI3ByaW50KGopDQogICNzdG9wKCJjb252ZXJ0ZWQgZnJvbSB3YXJuaW5nOiAiLCBjb25kaXRpb25NZXNzYWdlKHcpKQ0KICB9DQopDQp9DQoNCnRhYmxlKGNvZGUsIHVzZU5BID0gImFsd2F5cyIpDQp0YWJsZShhdXRfcG90LCB1c2VOQSA9ICJhbHdheXMiKQ0KdGFibGUoYXV0X21heCwgdXNlTkEgPSAiYWx3YXlzIikNCg0KZGZfcHViczIkYXV0X3BvdCA8LSBhdXRfcG90DQpkZl9wdWJzMiRhdXRfbWF4IDwtIGF1dF9tYXgNCmRmX3B1YnMyJGF1dF9jb2RlIDwtIGNvZGUNCg0KDQoNCmBgYA0KDQpTbyB3ZSBjYW4gcGxheSBhIGxpdHRsZSBsYXRlciBieSBmaWx0ZXJpbmcgb24gc3BlY2lmaWMgY29kZXMuIG9yIGJ5IHJlY29kaW5nIHRoZSBOQXMgb2YgYXV0aG9yIHBvc2l0aW9ucyB2aWEgdGhlIGNvZGUgdmFyaWFibGUuIEZvciBleGFtcGxlLCBzZXR0aW5nIGFsbCBwdWJsaWNhdGlvbnMgd2l0aG91dCBhdXRob3IgbmFtZXMgYXMgZmlyc3QgYXV0aG9yIHB1YmxpY2F0aW9ucy4gZXRjLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBwZXJzb24gcGVyaW9kIGZpbGUNCg0KIyMgcHVicyBwZXIgeWVhciBwZXIgYXV0aG9yDQoNCkkgY291bnQgdGhlOg0KDQotICAgdG90YWwgbnVtYmVyIG9mIHB1YmxpY2F0aW9uc1wNCi0gICBhbGwgZmlyc3QgYXV0aG9yZWQgcHVibGljYXRpb25zIChpbmNsdXNpdmUgc29sZSBhdXRob3JlZCBwdWJsaWNhdGlvbnMpDQotICAgYWxsIGxhc3QgYXV0aG9yZWQgcHVibGljYXRpb25zIChleGNsdXNpdmUgc29sZSBhdXRob3JlZCBwdWJsaWNhdGlvbnMpXA0KLSAgIGFsbCBzb2xlIGF1dGhvcmVkIHB1YmxpY2F0aW9uc1wNCi0gICBhbGwgZmlyc3QgYXV0aG9yZWQgYXJ0aWNsZXMgKGluY2x1c2l2ZSBzb2xlIGF1dGhvcmVkIGFydGljbGVzKQ0KLSAgIGFsbCBsYXN0IGF1dGhvcmVkIGFydGljbGVzIChleGNsdXNpdmUgc29sZSBhdXRob3JlZCBhcnRpY2xlcylcDQotICAgYWxsIHNvbGUgYXV0aG9yZWQgYXJ0aWNsZXMNCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCnRhYmxlKGRmX3B1YnMyJGdlbmRlciwgdXNlTkE9ImFsd2F5cyIpDQp0YWJsZShkZl9wdWJzMiRnZW5kZXJfMiwgdXNlTkE9ImFsd2F5cyIpDQp0YWJsZShkZl9wdWJzMiRldGhuaWNpdHksIHVzZU5BPSJhbHdheXMiKQ0KdGFibGUoZGZfcHViczIkZXRobmljaXR5MiwgdXNlTkE9ImFsd2F5cyIpDQp0YWJsZShkZl9wdWJzMiRldGhuaWNpdHkzLCB1c2VOQT0iYWx3YXlzIikNCmBgYA0KDQpgYGB7cn0NCnB1YnMgPC0gZGZfcHViczINCg0KI3B1YmxpY2F0aW9uIHllYXINCnB1YnMgJT4lIA0KICBtdXRhdGUocHViX2EgPSBhcy5udW1lcmljKFR5cGU9PSJBcnRpa2VsIiksDQogICAgICAgICAgcHViX2ZpcnN0ID0gYXMubnVtZXJpYyhhdXRfcG90PT0xKSwNCiAgICAgICAgIHB1Yl9sYXN0ID0gYXMubnVtZXJpYyhhdXRfcG90PT1hdXRfbWF4ICYgYXV0X3BvdCE9MSksDQogICAgICAgICBwdWJfc29sZSA9IGFzLm51bWVyaWMoYXV0X21heD09MSksDQogICAgICAgICBwdWJfZmlyc3RfYSA9IGFzLm51bWVyaWMoYXV0X3BvdD09MSAmIFR5cGU9PSJBcnRpa2VsIiksDQogICAgICAgICBwdWJfbGFzdF9hID0gYXMubnVtZXJpYyhhdXRfcG90PT1hdXRfbWF4ICYgYXV0X3BvdCE9MSAmIFR5cGU9PSJBcnRpa2VsIiksIA0KICAgICAgICAgcHViX3NvbGVfYSA9IGFzLm51bWVyaWMoYXV0X21heD09MSAmIFR5cGU9PSJBcnRpa2VsIikpIC0+IHB1YnMNCg0KDQojYWdncmVnYXRlIGJ5IGlkIGFuZCBwdWJfeWVhci4gdGhpcyBpcyB3aGF0IHdlIHdhbnQgaW4gdGhlIHBlcnNvbiBwZXJpb2QgZmlsZS4gDQpwdWJzICU+JSANCiAgZ3JvdXBfYnkoaWQsIHB1Yl95ZWFyKSAlPiUNCiAgc3VtbWFyaXplKG5wdWJzID0gbigpLA0KICAgICAgICAgICAgbnB1YnNfYSA9IHN1bShwdWJfYSwgbmEucm09VFJVRSksDQogICAgICAgICAgICBmaXJzdHB1YiA9IGZpcnN0KGZpcnN0cHViKSwNCiAgICAgICAgICAgIGxhc3RwdWIgPSBmaXJzdChsYXN0cHViKSwNCiAgICAgICAgICAgIG5wdWJzX2ZpcnN0ID0gc3VtKHB1Yl9maXJzdCwgbmEucm09VFJVRSksDQogICAgICAgICAgICBucHVic19sYXN0ID0gc3VtKHB1Yl9sYXN0LCBuYS5ybT1UUlVFKSwNCiAgICAgICAgICAgIG5wdWJzX3NvbGUgPSBzdW0ocHViX3NvbGUsIG5hLnJtPVRSVUUpLA0KICAgICAgICAgICAgbnB1YnNfZmlyc3RfYSA9IHN1bShwdWJfZmlyc3RfYSwgbmEucm09VFJVRSksDQogICAgICAgICAgICBucHVic19sYXN0X2EgPSBzdW0ocHViX2xhc3RfYSwgbmEucm09VFJVRSksDQogICAgICAgICAgICBucHVic19zb2xlX2EgPSBzdW0ocHViX3NvbGVfYSwgbmEucm09VFJVRSksDQogICAgICAgICAgICBwaGRfeWVhciA9IGZpcnN0KHBoZF95ZWFyKSwNCiAgICAgICAgICAgIGdlbmRlciA9IGZpcnN0KGdlbmRlcl8yKSwNCiAgICAgICAgICAgIGZpZWxkID0gZmlyc3QoZmllbGQpLA0KICAgICAgICAgICAgZXRobmljaXR5ID0gZmlyc3QoZXRobmljaXR5MyksDQogICAgICAgICAgICB1bmkgPSBmaXJzdCh1bmkpLA0KICAgICAgICAgICAgaWQgPSBmaXJzdChpZCkpIC0+IHB1YnMyDQoNCiN3ZSBhZ2dyZWdhdGUgYWxzbyBieSBpZC4gdGhpcyB3aWxsIGhlbHAgdG8gZmluZCB1cyB0aGUgdG90YWwgcHVibGljYXRpb25zIG92ZXIgdGhlIGNhcmVlci4gYnV0IGFsc28gdG8gZmlsbCBpbiB0aGUgZW1wdHkgcm93cyBvZiB0aGUgZGZfcHBwIGxhdGVyIG9uLiANCnB1YnMgJT4lIA0KICBncm91cF9ieShpZCkgJT4lDQogIHN1bW1hcml6ZShucHVic190ID0gbigpLA0KICAgICAgICAgICAgbnB1YnNfYV90ID0gc3VtKHB1Yl9hLCBuYS5ybT1UUlVFKSwNCiAgICAgICAgICAgIGZpcnN0cHViID0gZmlyc3QoZmlyc3RwdWIpLA0KICAgICAgICAgICAgbGFzdHB1YiA9IGZpcnN0KGxhc3RwdWIpLA0KICAgICAgICAgICAgbnB1YnNfZmlyc3RfdCA9IHN1bShwdWJfZmlyc3QsIG5hLnJtPVRSVUUpLA0KICAgICAgICAgICAgbnB1YnNfbGFzdF90ID0gc3VtKHB1Yl9sYXN0LCBuYS5ybT1UUlVFKSwNCiAgICAgICAgICAgIG5wdWJzX3NvbGVfdCA9IHN1bShwdWJfc29sZSwgbmEucm09VFJVRSksDQogICAgICAgICAgICBucHVic19maXJzdF9hX3QgPSBzdW0ocHViX2ZpcnN0X2EsIG5hLnJtPVRSVUUpLA0KICAgICAgICAgICAgbnB1YnNfbGFzdF9hX3QgPSBzdW0ocHViX2xhc3RfYSwgbmEucm09VFJVRSksDQogICAgICAgICAgICBucHVic19zb2xlX2FfdCA9IHN1bShwdWJfc29sZV9hLCBuYS5ybT1UUlVFKSwNCiAgICAgICAgICAgIHBoZF95ZWFyID0gZmlyc3QocGhkX3llYXIpLA0KICAgICAgICAgICAgZ2VuZGVyID0gZmlyc3QoZ2VuZGVyXzIpLA0KICAgICAgICAgICAgZmllbGQgPSBmaXJzdChmaWVsZCksDQogICAgICAgICAgICBldGhuaWNpdHkgPSBmaXJzdChldGhuaWNpdHkzKSwNCiAgICAgICAgICAgIHVuaSA9IGZpcnN0KHVuaSksDQogICAgICAgICAgICBpZCA9IGZpcnN0KGlkKSkgLT4gcHViczMNCg0KDQpgYGANCg0KIyMgZW1wdHkgcHBmDQoNCmBgYHtyfQ0KI2ZpcnN0IG1ha2UgYW4gZW1wdHkgZGF0YXNldA0KDQpwdWJfeWVhciA8LSBjKDE5OTA6MjAyMikNCm5wdWJzX3plcm8gPC0gcmVwKDAsIGxlbmd0aChwdWJfeWVhcikpDQppZCA8LSB1bmlxdWUoZGZfcHViczIkaWQpDQpuaWQgPC0gbGVuZ3RoKGlkKQ0KcHViX3llYXIgPC0gcmVwKHB1Yl95ZWFyLCBuaWQpDQpucHVic196ZXJvIDwtIHJlcChucHVic196ZXJvLCBuaWQpDQppZCA8LSByZXAoaWQsIGVhY2g9bGVuZ3RoKGMoMTk5MDoyMDIyKSkpICANCg0KZGYgPC0gZGF0YS5mcmFtZShpZCwgcHViX3llYXIpDQpkZiAlPiUgDQogIGFycmFuZ2UoaWQsIHB1Yl95ZWFyKSAtPiBkZg0KYGBgDQoNCiMjIGZpbGwgdGhlIHBwZg0KDQpgYGB7cn0NCg0KZGF0YV9wcGYgPC0gbGVmdF9qb2luKGRmLCBwdWJzMywgYnk9YygiaWQiKSkgJT4lDQogIGZpbHRlcihwdWJfeWVhcj49Zmlyc3RwdWIpICU+JQ0KICBsZWZ0X2pvaW4ocHViczIsIGJ5PWMoImlkIiwgInB1Yl95ZWFyIikpICU+JQ0KICBtdXRhdGUobnB1YnMgPSByZXBsYWNlX25hKG5wdWJzLCAwICksDQogICAgICAgICBucHVic19hID0gcmVwbGFjZV9uYShucHVic19hLCAwICksDQogICAgICAgICBucHVic19maXJzdCA9IHJlcGxhY2VfbmEobnB1YnNfZmlyc3QsIDApLA0KICAgICAgICAgbnB1YnNfbGFzdCA9IHJlcGxhY2VfbmEobnB1YnNfbGFzdCwgMCksDQogICAgICAgICBucHVic19zb2xlID0gcmVwbGFjZV9uYShucHVic19zb2xlLCAwKSwNCiAgICAgICAgIG5wdWJzX2ZpcnN0X2EgPSByZXBsYWNlX25hKG5wdWJzX2ZpcnN0X2EsIDApLA0KICAgICAgICAgbnB1YnNfbGFzdF9hID0gcmVwbGFjZV9uYShucHVic19sYXN0X2EsIDApLCANCiAgICAgICAgIG5wdWJzX3NvbGVfYSA9IHJlcGxhY2VfbmEobnB1YnNfc29sZV9hLCAwKSkgJT4lDQogIHJlbmFtZV93aXRoKH5nc3ViKCIueCIsICIiLCAueCwgZml4ZWQgPSBUUlVFKSkgJT4lIA0KICBkcGx5cjo6c2VsZWN0KGMoImlkIiwgInB1Yl95ZWFyIiwgInBoZF95ZWFyIiwgImdlbmRlciIsICJmaWVsZCIsICJldGhuaWNpdHkiLCAidW5pIiwgIm5wdWJzIiwgIm5wdWJzX2EiLCAibnB1YnNfZmlyc3QiLCAibnB1YnNfbGFzdCIsICJucHVic19zb2xlIiwibnB1YnNfZmlyc3RfYSIsICJucHVic19sYXN0X2EiLCAibnB1YnNfc29sZV9hIiwgIm5wdWJzX3QiLCAibnB1YnNfYV90IiwgIm5wdWJzX2ZpcnN0X3QiLCAibnB1YnNfbGFzdF90IiwgIm5wdWJzX3NvbGVfdCIsIm5wdWJzX2ZpcnN0X2FfdCIsICJucHVic19sYXN0X2FfdCIsICJucHVic19zb2xlX2FfdCIgKSkNCg0KYGBgDQoNCiMjIHNhdmluZw0KDQpgYGB7cn0NCmZzYXZlKGRhdGFfcHBmLCAiZGZfcHBmIikgI3RoZSBwZXJzb24gcGVyaW9kIGZpbGUgZGF0YXNldCBmb3IgdGhlIGFuYWx5c2lzDQpmc2F2ZShkZl9wdWJzMiwgImRmX3B1YnMiKSAjcHVibGljYXRpb25zIGluY2x1ZGVkIGluIHRoZSBwcGYgKHdpdGggYWxsIGluZm8gb24gcGhkcyBhdHRhY2hlZCBhcyB3ZWxsKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0K
Copyright © 2024- Jochem Tolsma