• 1 Custom functions
  • 2 Packages
  • 3 Publications (part 1)
  • 4 PhDs
  • 5 Publications (part 2)
    • 5.1 Delete publications without phd info
    • 5.2 Removing duplicates
    • 5.3 Remove publications and PhDs, given inclusion criteria
    • 5.4 Intermediate save to construct our research domain variable
  • 6 Author position of PhD in publication
    • 6.1 Prepare author-list of each publication
    • 6.2 Author position
  • 7 person period file
    • 7.1 pubs per year per author
    • 7.2 empty ppf
    • 7.3 fill the ppf
    • 7.4 saving

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 if you are interested in working with these raw data files. The working sample(s) can be downloaded here.


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

2 Packages

packages = c("ggplot2", "tidyverse", "RColorBrewer", "dplyr", "stringdist", "stringi", "future.apply")

fpackage.check(packages)

3 Publications (part 1)

Scraped publications via NARCIS. These raw data sets are not shared publicly but please email 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/")

4 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

5 Publications (part 2)

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

5.2 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

5.3 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

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


6 Author position of PhD in publication

6.1 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

6.2 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.
    • 1.1: No, done
  • Stap 0: check to see if it is a sole-authored paper.
    • 0.1: Yes, done
    • 0.2: No
  • 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.1
  • 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.


7 person period file

7.1 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

7.2 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

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

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

  1. We pick the first because an inspection of Narcis seems to indicate that the lead author is at times additionally mentioned as last author.↩︎

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