We determine ethnic backgrounds based on last names. For this, we use information from three sources:

  1. Data on the origin of last names, web scraped from the Nederlandse Familienamenbank

  2. A list of common Moroccan names

  3. Birth countries listed in dissertation PDFs


1 Custom functions

  • fpackage.check: Check if packages are installed (and install if not) in R (source).
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, location = "./data/processed/") {
    datename <- substr(gsub("[:-]", "", Sys.time()), 1, 8)
    totalname <- paste(location, datename, file, sep = "")
    save(x, file = totalname)
}

2 Packages

  • tidyverse: For general data manipulaion

  • stringr: for string manipulations

  • dplyr: for data manipulation

  • rvest: for

packages = c("tidyverse", "stringr", "dplyr", "rvest", "stringi", "forcats")

fpackage.check(packages)

3 Input

We use two processed datasets

  • phdnames.rda: example dataset of 8 (fictional) PhDs with first and last names
  • lastname_origin.rda: web scraped ethnicity data for the 8 last names in the example data from de Nederlandse Familienamenbank
    • name of dataset: gender
load(file = "./data/processed/phdnames.rda")

load(file = "./data/processed/lastname_origin.rda")

4 Method 1: Familienamenbank last name origin

The primary determinant for ethnicity is de Nederlandse Familienamenbank(hereafter “Familienamenbank”). This website, like the Meertens Voornamenbank which was used to determine researchers’ gender, uses register data from Dutch inhabitants. Specifically, the website harbors information on the frequency, geographical spread, and origin of 320,000 last names which occur in the Dutch register.

The origin information, specifically, is used to determine researchers’ ethnicities. This information is found under the ‘analyse en verklaring’ tab of the name page. This information is structured so that, if names are Dutch, more detailed origins of the name are mentioned (e.g. whether the last name originates from a certain profession, geographical region in the Netherlands or whether it is derived from fathers’ first names). If names are not Dutch, however, only the country or broader region from which the name originates is mentioned. See below for examples of these pages for a Dutch and a non-Dutch last name.

verschuurencbg
verschuurencbg
karimicbg
karimicbg

We scraped the “analyse en verklaring” section for each of the last names in our sample. The latter image shows that the subsection “verklaring” and “kenmerken” provide information on the countries associated with non-Dutch names, while “specifieke componenten” provides more information on certain parts of the name. The resulting dataframe for our example dataset is shown below.

print(lastname_origin, row.names = FALSE)

4.1 Separating names with Dutch & unknown origin

As mentioned earlier, it is not mentioned explicitly that Dutch names are Dutch. Therefore, we need to identify Dutch names specifically, and set them apart from non-Dutch names for which there is no origin information present.

In short, we detect the two categories of names as follows: - Dutch names: no label indicating that the name is Dutch, but some other information available on name origin - Unknown names: web page cannot be found, so origin information is completely empty.

# Identify last names that could not be found
lastname_origin <- lastname_origin %>%
    mutate(verklaring = ifelse(verklaring == "", 0, verklaring), kenmerken = ifelse(kenmerken == "character(0)",
        0, kenmerken), sc = ifelse(sc == "character(0)", 0, sc), no_info = nchar(verklaring) + nchar(kenmerken) +
        nchar(sc))

lastname_origin <- lastname_origin %>%
    mutate(no_info = ifelse(no_info == 3, 1, 0), verklaring = ifelse(verklaring == 0, NA, verklaring),
        kenmerken = ifelse(kenmerken == 0, NA, kenmerken))
# If there is no text in verklaring or kenmerken, the name could not be found in the databases.

4.2 Extracting specific origin information

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

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

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

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

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

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



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


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



# Finally, we clean up the origin information extracted above

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

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


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

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


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


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



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

The result is as follows:

lastname_origin[, c(1, 6, 7, 8, 9)]

4.3 Combining origin info from different sources

Now, we potentially have origin information from three different sources. The final origin decision is based on how dependable and how informative the label is. The heuristic is as follows:

  1. Priority is given to origin information from specific country tags (i.e. origin1), because this information is the most neat. If no country tag is provided under origin1, we look at the geographical origins indicated in the running text (origin 2). If this is also not present, we take the linguistic origins of the name (origin 3)

  2. If there is no specific country information, but there is a tag for a foreign name (origin4), then the name is labelled as “unknown non-Dutch”.

  3. If there is no specific country information AND no tag for foreign name in origin4, but there is other information about the name, we assume the person bearing this name is Dutch.

In sum: origin1 > origin2 > origin3 > origin4.

# Origin 1: specific country tags
lastname_origin <- lastname_origin %>%
    mutate(origin = origin1)

# Origin 2: no specific country tag, take the geographical origin of the name
lastname_origin <- lastname_origin %>%
    mutate(origin = ifelse((is.na(origin1) & !is.na(origin4)), origin2, origin))

# Origin 3: neither of the above, take the linguistic origin of the name
lastname_origin <- lastname_origin %>%
    mutate(origin = ifelse((is.na(origin1) & is.na(origin2) & !is.na(origin4)), origin3, origin))

# Origin 4: set to unknown non-Dutch if the name is labelled as such with no specific origin info
lastname_origin <- lastname_origin %>%
    mutate(origin = ifelse((is.na(origin1) & is.na(origin2) & is.na(origin3)), origin4, origin))

# Final step: if there is no origin information present, but other background information could be
# found, the name is likely Dutch.
lastname_origin <- lastname_origin %>%
    mutate(origin = ifelse((is.na(origin) & no_info == 0), "dutch", origin))

lastname_origin$origin <- trimws(lastname_origin$origin, which = "both")

To construct the ethnicity variable, we distinguish five different backgrounds: 1. Ethnic majority members (i.e. those with Dutch names) 2. Turkish-Dutch individuals 3. Moroccan-Dutch individuals 4. Caribbean-Dutch individuals 5. Those with a different ethnicity from those listed above

lastname_origin$origin1 <- lastname_origin$origin

lastname_origin <- subset(lastname_origin, select = c(lastname, origin1))


# we define different ways in which ethnic minority names are identified in the 'Familienamenbank'
lastname_origin <- lastname_origin %>%
    mutate(origin1 = ifelse(as.numeric(str_detect(origin1, paste("(Turkije)|(Turkse naam)"))), "turkish",
        origin1))

lastname_origin <- lastname_origin %>%
    mutate(origin1 = ifelse(as.numeric(str_detect(origin1, paste("(Marokkaanse naam)|(Marokko)|(de Marokko)"))),
        "moroccan", origin1))

lastname_origin <- lastname_origin %>%
    mutate(origin1 = ifelse(as.numeric(str_detect(origin1, paste("(de Nederlandse Antillen)|(Surinaamse naam)|(Suriname)"))),
        "carribean", origin1))

lastname_origin <- lastname_origin %>%
    mutate(origin1 = ifelse((!is.na(origin1) & as.numeric(str_detect(origin1, "turkish|moroccan|carribean|dutch")) ==
        0), "other", origin1))

lastname_origin$origin1 <- as.factor(lastname_origin$origin1)


# we create an explicit 'missing' category for those who are not categorized under ethnic majority,
# minority or 'other lastname_origin'
lastname_origin <- lastname_origin %>%
    mutate(origin1 = fct_explicit_na(origin1, na_level = "missing"))


# thus, we get the following dataframe:
lastname_origin

We take the last name information from origin1 as the basis for our ethnicity variable. Hence, we make a new data frame, in which we copy the information from origin1 to a new ‘ethnicity’ object. Next, we enrich this ethnicity variable using information from first names and birth places listed in dissertation PDFs.

phdethnicity <- lastname_origin

phdethnicity$ethnicity <- phdethnicity$origin1

5 Method 2: Moroccan name list

In addition to determining ethnicity based on last names, we look at first names. Specifically, we use a list of Moroccan first names to detect whether an individual is likely of Moroccan descent. Untill very recently, first names of Moroccan individuals were regulated by Moroccan law (haskouri?). This implies that many Moroccan individuals choose names for their children from a list of accepted girls and boys names. We load in a list of names by @Morocco Guide as an additional indicator that a PhD is Moroccan and add this to our example data frame.

# Adding first names back in
phdethnicity <- left_join(phdnames, phdethnicity, by = "lastname")


# Add the moroccan names to the ethnicity data
load(file = "data/processed/moroccannames.rda")


phdethnicity <- left_join(phdethnicity, moroccannames[, c(1, 3)], by = "firstname")


# Add explicit missing category in origin2
phdethnicity <- phdethnicity %>%
    mutate(origin2 = fct_explicit_na(origin2, na_level = "missing"))

# We overwrite ethnicity to minority if the first name is Moroccan
for (i in 1:nrow(phdethnicity)) {
    if (phdethnicity$origin1[i] == "missing" & phdethnicity$origin2[i] == "moroccan") {
        phdethnicity$ethnicity[i] <- "moroccan"
    } else if (phdethnicity$origin1[i] == "dutch" & phdethnicity$origin2[i] == "moroccan") {
        phdethnicity$ethnicity[i] <- "moroccan"
    } else if (phdethnicity$origin1[i] == "other" & phdethnicity$origin2[i] == "moroccan") {
        phdethnicity$ethnicity[i] <- "moroccan"
    }
}

Let’s see what the data looks like. In this case, we see that the two methods for determining ethnicity (at least with regards to Moroccan vs non-Moroccan) appear to be in agreement.

phdethnicity[, c(1, 2, 5, 9, 11, 6)]

6 Method 3: PDF birth places

Based on lists of Dutch municipalities, we can establish whether someone was born in the Netherlands. We use lists of municipalities provided by Statistics Netherlands from the years 2006/2014/2015/2016/2018/2019/2020/2021, as well as mutations in municipalities or municipality names during the period of this study (Statistics? Netherlands).

If the birth place info contains a Dutch municipality or a reference to the Netherlands, a person is born in the Netherlands. If there is a reference to Turkey, Morocco, the

# loading in the list of Dutch municipalities
load(file = "data/municipalities/dutch_mun_all.rda")

# creating strings with indicators for birth countries (The Netherlands, Caribbean Netherlands,
# Turkey, Morocco)
dutch <- c(as.character(dutch_mun$municipality), "netherlands", "the netherlands", "nederland", "\\snl\\s")

carribean <- c("suriname", "nederlandse antillen", "curacao", "curaçao", "bonaire", "\\sssaba\\s", "sint eustatius",
    "sint maarten", "aruba")

turkish <- c("turkije", "turkey")

moroccan <- c("marokko", "morocco")



# Origin3: country of birth variable
phdethnicity$origin3 <- ifelse(as.numeric(str_detect(phdethnicity$diss_birthplace, paste(dutch, collapse = "|"))) ==
    1, "dutch", NA)

phdethnicity$origin3 <- ifelse(as.numeric(str_detect(phdethnicity$diss_birthplace, paste(carribean, collapse = "|"))) ==
    1, "caribbean", phdethnicity$origin3)

phdethnicity$origin3 <- ifelse(as.numeric(str_detect(phdethnicity$diss_birthplace, paste(turkish, collapse = "|"))) ==
    1, "turkish", phdethnicity$origin3)

phdethnicity$origin3 <- ifelse(as.numeric(str_detect(phdethnicity$diss_birthplace, paste(moroccan, collapse = "|"))) ==
    1, "moroccan", phdethnicity$origin3)


phdethnicity$origin3 <- factor(phdethnicity$origin3, levels = c("dutch", "caribbean", "turkish", "moroccan"))

# Create explicit missing category
phdethnicity <- phdethnicity %>%
    mutate(origin3 = fct_explicit_na(origin3, na_level = "missing"))


# We replace missing or other with 'dutch' if a person is born in NL We replace missing, other,
# dutch and moroccan with 'turkish' if a person is born in Turkey We replace missing, other, dutch
# and turkish with 'moroccan' if a person is born in Morocco

for (i in 1:nrow(phdethnicity)) {
    if (phdethnicity$ethnicity[i] == "missing" & phdethnicity$origin3[i] == "dutch") {
        phdethnicity$ethnicity[i] <- "dutch"
    } else if (phdethnicity$ethnicity[i] == "other" & phdethnicity$origin3[i] == "dutch") {
        phdethnicity$ethnicity[i] <- "dutch"
    } else if (phdethnicity$ethnicity[i] == "missing" & phdethnicity$origin3[i] == "moroccan") {
        phdethnicity$ethnicity[i] <- "moroccan"
    } else if (phdethnicity$ethnicity[i] == "dutch" & phdethnicity$origin3[i] == "moroccan") {
        phdethnicity$ethnicity[i] <- "moroccan"
    } else if (phdethnicity$ethnicity[i] == "other" & phdethnicity$origin3[i] == "moroccan") {
        phdethnicity$ethnicity[i] <- "moroccan"
    } else if (phdethnicity$ethnicity[i] == "turkish" & phdethnicity$origin3[i] == "moroccan") {
        phdethnicity$ethnicity[i] <- "moroccan"
    } else if (phdethnicity$ethnicity[i] == "missing" & phdethnicity$origin3[i] == "turkish") {
        phdethnicity$ethnicity[i] <- "turkish"
    } else if (phdethnicity$ethnicity[i] == "dutch" & phdethnicity$origin3[i] == "turkish") {
        phdethnicity$ethnicity[i] <- "turkish"
    } else if (phdethnicity$ethnicity[i] == "other" & phdethnicity$origin3[i] == "turkish") {
        phdethnicity$ethnicity[i] <- "turkish"
    } else if (phdethnicity$ethnicity[i] == "moroccan" & phdethnicity$origin3[i] == "turkish") {
        phdethnicity$ethnicity[i] <- "turkish"
    }
}

After all these operations, we are left with an ethnicity object which is based on origins of last names and first names, and on the birth place listed in the dissertation.

For these example cases, the labels given based on these different indicators overlap perfectly, but the code above gives insight in the heuristics we use to combine potentially conflicting ethnicity labels.

phdethnicity[, c(2, 5, 6, 10, 9, 11, 12)]

7 Ethnicity 2

Although we need the specific ethnicity labels for our measure of gender, we use a less-detailed measure of ethnic background for our analyses. In this measure we distinguish between individuals with an ethnic majority, ethnic minority (Turkish-Dutch, Moroccan-Dutch, Caribbean-Dutch) or other ethnic background. This has to do with small group sizes for each of the specific ethnic minority backgrounds.

We subsume “missing” ethnic background under other ethnic background, because we believe that it is unlikely that a person with a Dutch majority, Turkish-Dutch, Moroccan-Dutch or Caribbean-Dutch background would have gone unnoticed using our array of methods to determine ethnicity.

phdethnicity$ethnicity2 <- phdethnicity$ethnicity

phdethnicity$ethnicity2 <- fct_collapse(phdethnicity$ethnicity2, majority = "dutch", minority = c("moroccan",
    "turkish", "carribean"), other = c("other", "missing"))



phdethnicity <- subset(phdethnicity, select = c(id, firstname, np, lastname, lastname_full, diss_birthplace,
    uni, phd_year, ethnicity, ethnicity2))

8 Output

phdethnicity

9 References

LS0tDQp0aXRsZTogIkFjYWRlbWljIHB1Ymxpc2hpbmcgY2FyZWVycyBpbiBOTDogZ2F0aGVyaW5nIGV0aG5pY2l0eSINCmJpYmxpb2dyYXBoeTogcmVmZXJlbmNlcy5iaWINCmRhdGU6ICJMYXN0IGNvbXBpbGVkIG9uIGByIGZvcm1hdChTeXMudGltZSgpLCAnJUIsICVZJylgIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjc3M6IHR3ZWFrcy5jc3MNCiAgICB0b2M6ICB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgY29kZV9kb3dubG9hZDogeWVzDQotLS0NCg0KPCEtLS1wbGVhc2UgYmUgYXdhcmUgdGhhdCBjYWNoaW5nIGxhcmdlIG9iamVjdHMgaXMgcHJvYmxlbWF0aWMsIGhlbmNlIGNhY2hlLmxhenk9RkFMU0UgYW5kIHlvdSBtYXkgbmVlZCB0byB0dXJuIGNhY2hlPUZBTFNFIGZvciBjaHVuY2tzIGluIHdoaWNoIHlvdSBsb2FkIGxhcmdlIGRhdGFzZXRzIC0tLT4gDQoNCmBgYHtyLCBnbG9iYWxzZXR0aW5ncywgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0iaGlkZSJ9DQpsaWJyYXJ5KGtuaXRyKQ0Kb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGguY3V0b2ZmPTEwMCksdGlkeT1UUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSxjb21tZW50ID0gIiM+IiwgY2FjaGU9VFJVRSwgY2xhc3Muc291cmNlPWMoInRlc3QiKSwgY2xhc3Mub3V0cHV0PWMoInRlc3QyIiksIGNhY2hlLmxhenkgPSBGQUxTRSwgZXZhbD1GQUxTRSkNCm9wdGlvbnMod2lkdGggPSAxMDApDQpyZ2w6OnNldHVwS25pdHIoKQ0KDQpjb2xvcml6ZSA8LSBmdW5jdGlvbih4LCBjb2xvcikge3NwcmludGYoIjxzcGFuIHN0eWxlPSdjb2xvcjogJXM7Jz4lczwvc3Bhbj4iLCBjb2xvciwgeCkgfQ0KDQpgYGANCg0KYGBge3Iga2xpcHB5LCBlY2hvPUZBTFNFLCBpbmNsdWRlPVRSVUV9DQprbGlwcHk6OmtsaXBweShwb3NpdGlvbiA9IGMoJ3RvcCcsICdyaWdodCcpKQ0KI2tsaXBweTo6a2xpcHB5KGNvbG9yID0gJ2RhcmtyZWQnKQ0KI2tsaXBweTo6a2xpcHB5KHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgdG9vbHRpcF9zdWNjZXNzID0gJ0RvbmUnKQ0KYGBgDQoNCg0KDQoNCi0tLS0NCg0KDQpXZSBkZXRlcm1pbmUgZXRobmljIGJhY2tncm91bmRzIGJhc2VkIG9uIGxhc3QgbmFtZXMuIEZvciB0aGlzLCB3ZSB1c2UgaW5mb3JtYXRpb24gZnJvbSB0aHJlZSBzb3VyY2VzOg0KDQoxLiBEYXRhIG9uIHRoZSBvcmlnaW4gb2YgbGFzdCBuYW1lcywgd2ViIHNjcmFwZWQgZnJvbSB0aGUgW05lZGVybGFuZHNlIEZhbWlsaWVuYW1lbmJhbmtdKGh0dHBzOi8vd3d3LmNiZ2ZhbWlsaWVuYW1lbi5ubC9uZmIvKQ0KDQoyLiBBIGxpc3Qgb2YgY29tbW9uIE1vcm9jY2FuIG5hbWVzDQoNCjMuIEJpcnRoIGNvdW50cmllcyBsaXN0ZWQgaW4gZGlzc2VydGF0aW9uIFBERnMNCg0KDQogIA0KLS0tLQ0KDQojIEN1c3RvbSBmdW5jdGlvbnMNCg0KLSBgZnBhY2thZ2UuY2hlY2tgOiBDaGVjayBpZiBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkIChhbmQgaW5zdGFsbCBpZiBub3QpIGluIFIgKFtzb3VyY2VdKGh0dHBzOi8vdmJhbGlnYS5naXRodWIuaW8vdmVyaWZ5LXRoYXQtci1wYWNrYWdlcy1hcmUtaW5zdGFsbGVkLWFuZC1sb2FkZWQvKSkuICANCg0KDQpgYGB7ciwgcmVzdWx0cz0naGlkZSd9DQoNCnJtKGxpc3QgPSBscygpKQ0KDQoNCmZwYWNrYWdlLmNoZWNrIDwtIGZ1bmN0aW9uKHBhY2thZ2VzKSB7DQogIGxhcHBseShwYWNrYWdlcywgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgIGlmICghcmVxdWlyZSh4LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7DQogICAgICBpbnN0YWxsLnBhY2thZ2VzKHgsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICAgICBsaWJyYXJ5KHgsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCiAgICB9DQogIH0pDQp9DQoNCmZzYXZlIDwtIGZ1bmN0aW9uKHgsIGZpbGUsIGxvY2F0aW9uPSIuL2RhdGEvcHJvY2Vzc2VkLyIpIHsNCiAgZGF0ZW5hbWUgPC0gc3Vic3RyKGdzdWIoIls6LV0iLCAiIiwgU3lzLnRpbWUoKSksIDEsOCkgIA0KICB0b3RhbG5hbWUgPC0gcGFzdGUobG9jYXRpb24sIGRhdGVuYW1lLCBmaWxlLCBzZXA9IiIpDQogIHNhdmUoeCwgZmlsZSA9IHRvdGFsbmFtZSkgIA0KfQ0KDQpgYGANCg0KLS0tICANCg0KIyBQYWNrYWdlcw0KDQotIGB0aWR5dmVyc2VgOiBGb3IgZ2VuZXJhbCBkYXRhIG1hbmlwdWxhaW9uDQoNCi0gYHN0cmluZ3JgOiBmb3Igc3RyaW5nIG1hbmlwdWxhdGlvbnMNCg0KLSBgZHBseXJgOiBmb3IgZGF0YSBtYW5pcHVsYXRpb24NCg0KLSBgcnZlc3RgOiBmb3IgDQoNCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnfQ0KDQoNCnBhY2thZ2VzID0gYygidGlkeXZlcnNlIiwgInN0cmluZ3IiLCAiZHBseXIiLCAicnZlc3QiLCAic3RyaW5naSIsICJmb3JjYXRzIikNCg0KZnBhY2thZ2UuY2hlY2socGFja2FnZXMpDQoNCmBgYA0KDQotLS0gDQoNCg0KIyBJbnB1dA0KDQpXZSB1c2UgdHdvIHByb2Nlc3NlZCBkYXRhc2V0cw0KDQoqIFtwaGRuYW1lcy5yZGFdKGh0dHBzOi8vZ2l0aHViLmNvbS9hbW11bGRlcnMvYW1hdHRlcm9mdGltZS9kYXRhL3Byb2Nlc3NlZC9waGRuYW1lcy5yZGEpOiBleGFtcGxlIGRhdGFzZXQgb2YgOCAoZmljdGlvbmFsKSBQaERzIHdpdGggZmlyc3QgYW5kIGxhc3QgbmFtZXMNCiAgICAtIEZvciBjb25zdHJ1Y3Rpb24gb2YgdGhpcyBkYXRhc2V0IHNlZSBbSW5kZXBlbmRlbnQgdmFyaWFibGVzOiBuYW1lc10obmFtZXMuaHRtbCkgIA0KICAgIC0gbmFtZSBvZiBkYXRhc2V0OiBgcGhkbmFtZXNgIA0KDQoqIFtsYXN0bmFtZV9vcmlnaW4ucmRhXShodHRwczovL2dpdGh1Yi5jb20vYW1tdWxkZXJzL2FtYXR0ZXJvZnRpbWUvZGF0YS9wcm9jZXNzZWQvbGFzdG5hbWVfb3JpZ2luLnJkYSk6IHdlYiBzY3JhcGVkIGV0aG5pY2l0eSBkYXRhIGZvciB0aGUgOCBsYXN0IG5hbWVzIGluIHRoZSBleGFtcGxlIGRhdGEgZnJvbSBkZSBOZWRlcmxhbmRzZSBGYW1pbGllbmFtZW5iYW5rDQogICAgLSBuYW1lIG9mIGRhdGFzZXQ6IGBnZW5kZXJgIA0KDQpgYGB7ciBkYXRhc2V0cywgY2FjaGU9RkFMU0V9DQoNCmxvYWQoZmlsZSA9ICIuL2RhdGEvcHJvY2Vzc2VkL3BoZG5hbWVzLnJkYSIpDQoNCmxvYWQoZmlsZSA9ICIuL2RhdGEvcHJvY2Vzc2VkL2xhc3RuYW1lX29yaWdpbi5yZGEiKQ0KDQoNCmBgYA0KDQotLS0gIA0KDQojIE1ldGhvZCAxOiBGYW1pbGllbmFtZW5iYW5rIGxhc3QgbmFtZSBvcmlnaW4NCg0KVGhlIHByaW1hcnkgZGV0ZXJtaW5hbnQgZm9yIGV0aG5pY2l0eSBpcyBbZGUgTmVkZXJsYW5kc2UgRmFtaWxpZW5hbWVuYmFua10oaHR0cHM6Ly93d3cuY2JnZmFtaWxpZW5hbWVuLm5sL25mYi8pKGhlcmVhZnRlciAiRmFtaWxpZW5hbWVuYmFuayIpLiBUaGlzIHdlYnNpdGUsIGxpa2UgdGhlIE1lZXJ0ZW5zIFZvb3JuYW1lbmJhbmsgd2hpY2ggd2FzIHVzZWQgdG8gZGV0ZXJtaW5lIHJlc2VhcmNoZXJzJyBnZW5kZXIsIHVzZXMgcmVnaXN0ZXIgZGF0YSBmcm9tIER1dGNoIGluaGFiaXRhbnRzLiBTcGVjaWZpY2FsbHksIHRoZSB3ZWJzaXRlIGhhcmJvcnMgaW5mb3JtYXRpb24gb24gdGhlIGZyZXF1ZW5jeSwgZ2VvZ3JhcGhpY2FsIHNwcmVhZCwgYW5kIG9yaWdpbiBvZiAzMjAsMDAwIGxhc3QgbmFtZXMgd2hpY2ggb2NjdXIgaW4gdGhlIER1dGNoIHJlZ2lzdGVyLiANCg0KVGhlIG9yaWdpbiBpbmZvcm1hdGlvbiwgc3BlY2lmaWNhbGx5LCBpcyB1c2VkIHRvIGRldGVybWluZSByZXNlYXJjaGVycycgZXRobmljaXRpZXMuIFRoaXMgaW5mb3JtYXRpb24gaXMgZm91bmQgdW5kZXIgdGhlICdhbmFseXNlIGVuIHZlcmtsYXJpbmcnIHRhYiBvZiB0aGUgbmFtZSBwYWdlLiANClRoaXMgaW5mb3JtYXRpb24gaXMgc3RydWN0dXJlZCBzbyB0aGF0LCBpZiBuYW1lcyBhcmUgRHV0Y2gsIG1vcmUgZGV0YWlsZWQgb3JpZ2lucyBvZiB0aGUgbmFtZSBhcmUgbWVudGlvbmVkIChlLmcuIHdoZXRoZXIgdGhlIGxhc3QgbmFtZSBvcmlnaW5hdGVzIGZyb20gYSBjZXJ0YWluIHByb2Zlc3Npb24sIGdlb2dyYXBoaWNhbCByZWdpb24gaW4gdGhlIE5ldGhlcmxhbmRzIG9yIHdoZXRoZXIgaXQgaXMgZGVyaXZlZCBmcm9tIGZhdGhlcnMnIGZpcnN0IG5hbWVzKS4gSWYgbmFtZXMgYXJlIG5vdCBEdXRjaCwgaG93ZXZlciwgb25seSB0aGUgY291bnRyeSBvciBicm9hZGVyIHJlZ2lvbiBmcm9tIHdoaWNoIHRoZSBuYW1lIG9yaWdpbmF0ZXMgaXMgbWVudGlvbmVkLiBTZWUgYmVsb3cgZm9yIGV4YW1wbGVzIG9mIHRoZXNlIHBhZ2VzIGZvciBhIER1dGNoIGFuZCBhIG5vbi1EdXRjaCBsYXN0IG5hbWUuICANCg0KIVt2ZXJzY2h1dXJlbmNiZ10oLi9taXNjL3ZlcnNjaHV1cmVuX2NiZ2YucG5nKQ0KDQohW2thcmltaWNiZ10oLi9taXNjL2thcmltaV9jYmdmLnBuZykNCg0KV2Ugc2NyYXBlZCB0aGUgImFuYWx5c2UgZW4gdmVya2xhcmluZyIgc2VjdGlvbiBmb3IgZWFjaCBvZiB0aGUgbGFzdCBuYW1lcyBpbiBvdXIgc2FtcGxlLiBUaGUgbGF0dGVyIGltYWdlIHNob3dzIHRoYXQgdGhlIHN1YnNlY3Rpb24gInZlcmtsYXJpbmciIGFuZCAia2VubWVya2VuIiBwcm92aWRlIGluZm9ybWF0aW9uIG9uIHRoZSBjb3VudHJpZXMgYXNzb2NpYXRlZCB3aXRoIG5vbi1EdXRjaCBuYW1lcywgd2hpbGUgInNwZWNpZmlla2UgY29tcG9uZW50ZW4iIHByb3ZpZGVzIG1vcmUgaW5mb3JtYXRpb24gb24gY2VydGFpbiBwYXJ0cyBvZiB0aGUgbmFtZS4gVGhlIHJlc3VsdGluZyBkYXRhZnJhbWUgZm9yIG91ciBleGFtcGxlIGRhdGFzZXQgaXMgc2hvd24gYmVsb3cuDQoNCmBgYHtyfQ0KDQpwcmludChsYXN0bmFtZV9vcmlnaW4sIHJvdy5uYW1lcz1GQUxTRSkNCg0KYGBgDQoNCg0KIyMgU2VwYXJhdGluZyBuYW1lcyB3aXRoIER1dGNoICYgdW5rbm93biBvcmlnaW4NCg0KQXMgbWVudGlvbmVkIGVhcmxpZXIsIGl0IGlzIG5vdCBtZW50aW9uZWQgZXhwbGljaXRseSB0aGF0IER1dGNoIG5hbWVzIGFyZSBEdXRjaC4gVGhlcmVmb3JlLCB3ZSBuZWVkIHRvIGlkZW50aWZ5IER1dGNoIG5hbWVzIHNwZWNpZmljYWxseSwgYW5kIHNldCB0aGVtIGFwYXJ0IGZyb20gbm9uLUR1dGNoIG5hbWVzIGZvciB3aGljaCB0aGVyZSBpcyBubyBvcmlnaW4gaW5mb3JtYXRpb24gcHJlc2VudC4gDQoNCkluIHNob3J0LCB3ZSBkZXRlY3QgdGhlIHR3byBjYXRlZ29yaWVzIG9mIG5hbWVzIGFzIGZvbGxvd3M6DQotIER1dGNoIG5hbWVzOiBubyBsYWJlbCBpbmRpY2F0aW5nIHRoYXQgdGhlIG5hbWUgaXMgRHV0Y2gsIGJ1dCBzb21lIG90aGVyIGluZm9ybWF0aW9uIGF2YWlsYWJsZSBvbiBuYW1lIG9yaWdpbg0KLSBVbmtub3duIG5hbWVzOiB3ZWIgcGFnZSBjYW5ub3QgYmUgZm91bmQsIHNvIG9yaWdpbiBpbmZvcm1hdGlvbiBpcyBjb21wbGV0ZWx5IGVtcHR5LiANCg0KYGBge3Igb3JpZ2luLXVua25vd259DQoNCiMgSWRlbnRpZnkgbGFzdCBuYW1lcyB0aGF0IGNvdWxkIG5vdCBiZSBmb3VuZA0KbGFzdG5hbWVfb3JpZ2luIDwtIGxhc3RuYW1lX29yaWdpbiAlPiUNCiAgbXV0YXRlKHZlcmtsYXJpbmcgPSBpZmVsc2UodmVya2xhcmluZz09IiIsIDAsIHZlcmtsYXJpbmcpLCANCiAgICAgICAgIGtlbm1lcmtlbiA9IGlmZWxzZShrZW5tZXJrZW49PSJjaGFyYWN0ZXIoMCkiLCAwLCBrZW5tZXJrZW4pLA0KICAgICAgICAgc2MgPSBpZmVsc2Uoc2M9PSJjaGFyYWN0ZXIoMCkiLCAwLCBzYyksDQogICAgICAgICBub19pbmZvID0gbmNoYXIodmVya2xhcmluZykgKyBuY2hhcihrZW5tZXJrZW4pICsgbmNoYXIoc2MpKQ0KDQpsYXN0bmFtZV9vcmlnaW4gPC0gbGFzdG5hbWVfb3JpZ2luICU+JQ0KICBtdXRhdGUobm9faW5mbyA9IGlmZWxzZShub19pbmZvPT0zLCAxLCAwKSwgDQogICAgICAgICB2ZXJrbGFyaW5nID0gaWZlbHNlKHZlcmtsYXJpbmc9PTAsIE5BLCB2ZXJrbGFyaW5nKSwNCiAgICAgICAgIGtlbm1lcmtlbiA9IGlmZWxzZShrZW5tZXJrZW49PTAsIE5BLCBrZW5tZXJrZW4pKQ0KIyBJZiB0aGVyZSBpcyBubyB0ZXh0IGluIHZlcmtsYXJpbmcgb3Iga2VubWVya2VuLCB0aGUgbmFtZSBjb3VsZCBub3QgYmUgZm91bmQgaW4gdGhlIGRhdGFiYXNlcy4gDQoNCmBgYA0KDQoNCiMjIEV4dHJhY3Rpbmcgc3BlY2lmaWMgb3JpZ2luIGluZm9ybWF0aW9uDQpUaGVyZSBhcmUgdGhyZWUgbWFpbiB3YXlzIHRvIGdldCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgb3JpZ2luIG9mIGxhc3QgbmFtZXM6DQoNCjEpIFVuZGVyICJrZW5tZXJrZW4iLCBsYXN0IG5hbWVzIGFyZSBhc3NpZ25lZCBjbGlja2FibGUgdGFncy4gVGhlc2UgdGFncyBpbmNsdWRlIHVuc3BlY2lmaWVkIGZvcmVpZ24gbmFtZSB0YWdzICgiYW5kZXJlIHRhYWwiPSJub24tRHV0Y2ggbGFuZ3VhZ2UiKSwgYXMgd2VsbCBhcyBzcGVjaWZpYyBmb3JlaWduIG9yaWdpbnMgb2YgdGhlIG5hbWUgKCJGcmFuc2UgbmFhbSI9IkZyZW5jaCBuYW1lIiwgIkluZGlzY2hlIG5hYW0iPSJJbmRvbmVzaWFuIG5hbWUiKS4NCi0+IG9yaWdpbjEgKyBvcmlnaW40DQoNCjIpIFNldmVyYWwgbmFtZXMgaGF2ZSBtb3JlIGV4dGVuc2l2ZWx5IHdyaXR0ZW4gb3V0IGluZm9ybWF0aW9uIGFib3V0IHRoZSBuYW1lLCB1bmRlciAidmVya2xhcmluZyIuIEEgbnVtYmVyIG9mIG5hbWVzIGNvbnRhaW4gZGV0YWlsZWQgKGVpdGhlciBjb3VudHJ5LWxldmVsIG9yIHJlZ2lvbmFsKSBvcmlnaW5zLCB1c3VhbGx5IGluIHRoZSBmb3JtIG9mICJEZSBuYWFtIFt4eXpdIGlzIGFma29tc3RpZyB1aXQgW2NvdW50cnldIiAoPSJUaGUgbmFtZSBbeHl6XSBvcmlnaW5hdGVzIGZyb20gW2NvdW50cnldIikuIA0KLT4gb3JpZ2luMiANCg0KMykgU29tZSBuYW1lcyBoYXZlIG9yaWdpbiBpbmZvcm1hdGlvbiB1bmRlciAidmVya2xhcmluZyIgaW4gdGhlIGZvcm0gb2YgdGhlIGxpbmd1aXN0aWMgb3JpZ2lucyBvZiB0aGUgbmFtZS4gVGhpcyBjYW4gYmUgY291bnRyeSBzcGVjaWZpYyAoZS5nLiBDaGluZXNlIG5hbWUpLCBidXQgaXQgY2FuIGFsc28gYXBwbHkgdG8gbXVsdGlwbGUgY291bnRyaWVzIHdoZW4gdGhlIGxhbmd1YWdlIGlzIHNwb2tlbiBpbiBtb3JlIHRoYW4gMSBjb3VudHJpZXMgKGUuZy4gU3BhbmlzaCBuYW1lKS4gDQotPiBvcmlnaW4zIA0KDQpgYGB7ciBjb3VudHJpZXMtZXh0cmFjdH0NCg0KIyBTdGVwIDE6IGV4dHJhY3Rpbmcgb3JpZ2luIHRhZ3MgZnJvbSBrZW5tZXJrZW4NCmxhc3RuYW1lX29yaWdpbiA8LSBsYXN0bmFtZV9vcmlnaW4gJT4lDQogIG11dGF0ZShvcmlnaW4xID0gc3RyX2V4dHJhY3Qoa2VubWVya2VuLCAiWzp1cHBlcjpdKFs6bG93ZXI6XXsyLH0pIG5hYW0iKSkNCg0KIyBOb3RlOiBzb21ldGltZXMgbXVsdGlwbGUgb3JpZ2lucyBhcmUgbWVudGlvbmVkLiBDdXJyZW50bHksIEkgb25seSBleHRyYWN0IHRoZSBmaXJzdCBvbmUuIE90aGVyd2lzZSwgd2Ugc2hvdWxkIHVzZSBzdHJfZXh0cmFjdF9hbGwuIA0KDQoNCg0KIyBTdGVwIDI6IGV4dHJhY3Rpbmcgb3JpZ2luIGluZm8gZnJvbSB2ZXJrbGFyaW5nIA0KbGFzdG5hbWVfb3JpZ2luIDwtIGxhc3RuYW1lX29yaWdpbiAlPiUNCiAgbXV0YXRlKG9yaWdpbjIgPSBpZmVsc2UoYXMubnVtZXJpYyhzdHJfZGV0ZWN0KHZlcmtsYXJpbmcsICJhZmtvbXN0aWcgdWl0IikpID09IDEsIA0KICAgICAgICAgc3RyX3JlbW92ZSh2ZXJrbGFyaW5nLCAiLiphZmtvbXN0aWcgdWl0IiksIE5BKSkNCg0KDQojIFN0ZXAgMzogZXh0cmFjdGluZyBhZGRpdGlvbmFsIG9yaWdpbiBpbmZvIGZyb20gdmVya2xhcmluZw0KbGFzdG5hbWVfb3JpZ2luIDwtIGxhc3RuYW1lX29yaWdpbiAlPiUNCiAgbXV0YXRlKG9yaWdpbjMgPSBzdHJfZXh0cmFjdCh2ZXJrbGFyaW5nLCAiWzp1cHBlcjpdKFs6bG93ZXI6XXsyLH0pIChhY2h0ZXIpPyhmYW1pbGllKT8oYmVyb2Vwcyk/bmFhbSIpKQ0KDQoNCg0KIyBGaW5hbGx5LCB3ZSBjbGVhbiB1cCB0aGUgb3JpZ2luIGluZm9ybWF0aW9uIGV4dHJhY3RlZCBhYm92ZQ0KDQojIE9yaWdpbjE6IGFscmVhZHkgbmVhdA0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjEgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMSwgIkpvb2RzZSBuYWFtIikgIyBjYW4gYmUgRHV0Y2ggJiBub24tRHV0Y2gNCg0KIyBPcmlnaW4yOiBtZXNzeQ0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjIgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMiwgIlxcLi4qIikgIyByZW1vdmUgZXh0cmEgaW5mbyBpbiB0aGUgZm9sbG93aW5nIHNlbnRlbmNlIA0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjIgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMiwgIlxcOy4qIikgIyByZW1vdmUgZXh0cmEgaW5mbyBpbiB0aGUgZm9sbG93aW5nIHNlbnRlbmNlIA0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjIgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMiwgIlxcKC4qIikgIyByZW1vdmUgZXh0cmEgaW5mbyBpbiB0aGUgZm9sbG93aW5nIHNlbnRlbmNlIA0KDQoNCmxhc3RuYW1lX29yaWdpbiRkcGcgPC0gYXMubnVtZXJpYyhzdHJfZGV0ZWN0KGxhc3RuYW1lX29yaWdpbiRvcmlnaW4yLCAiKGRvcnApfChwbGFhdHMpfChnZW1lZW50ZSl8KGdyYWFmc2NoYXApfChzdGFkKXwoZGVlbCl8KEZyaWVzbGFuZCkiKSkgIyBvcmlnaW4gaW5mbyB0b28gcmVnaW9uYWwgDQpsYXN0bmFtZV9vcmlnaW4gPC0gbGFzdG5hbWVfb3JpZ2luICU+JSBtdXRhdGUob3JpZ2luMiA9IGlmZWxzZSgoZHBnPT0xKSwgTkEsIG9yaWdpbjIpKSAjIHJlbW92aW5nIHJlZ2lvbmFsIG9yaWdpbiBpbmZvDQpsYXN0bmFtZV9vcmlnaW4gPC0gc3Vic2V0KGxhc3RuYW1lX29yaWdpbiwgc2VsZWN0ID0gLWRwZykgIyByZW1vdmluZyBpbnRlcm1lZGlhdGUgdmFyaWFibGUNCg0KIyBTb21ldGltZXMsIHRoZXJlIHdlcmUgbXVsdGlwbGUgY291bnRyaWVzIG1lbnRpb25lZC4gVGFrZSBvbmx5IHRoZSBmaXJzdDoNCmxhc3RuYW1lX29yaWdpbiRvcmlnaW4yIDwtIHN0cl9yZW1vdmUobGFzdG5hbWVfb3JpZ2luJG9yaWdpbjIsICJcXCwuKiIpICMgT25seSBmaXJzdA0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjIgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMiwgIlxccyhlbikuKiIpICMgT25seSBmaXJzdCANCmxhc3RuYW1lX29yaWdpbiRvcmlnaW4yIDwtIHN0cl9yZW1vdmUobGFzdG5hbWVfb3JpZ2luJG9yaWdpbjIsICJcXHMob2YpLioiKSAjIE9ubHkgZmlyc3QgDQoNCg0KIyBPcmlnaW4zOiBwcmV0dHkgbmVhdA0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjMgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMywgIkQoaSk/ZSh6ZSk/IChmYW1pbGllKT8oYWNodGVyKT8oYmVyb2Vwcyk/bmFhbSIpICMgc2xpcHBlZCB0aHJvdWdoIHRoZSByZWdleA0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjMgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMywgIkVlbiAoZmFtaWxpZSk/KGFjaHRlcik/KGJlcm9lcHMpP25hYW0iKSAjIHNsaXBwZWQgdGhyb3VnaCB0aGUgcmVnZXgNCmxhc3RuYW1lX29yaWdpbiRvcmlnaW4zIDwtIHN0cl9yZW1vdmUobGFzdG5hbWVfb3JpZ2luJG9yaWdpbjMsICJaaWpuIChmYW1pbGllKT8oYWNodGVyKT8oYmVyb2Vwcyk/bmFhbSIpICMgc2xpcHBlZCB0aHJvdWdoIHRoZSByZWdleA0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjMgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMywgIkFscyAoZmFtaWxpZSk/KGFjaHRlcik/KGJlcm9lcHMpP25hYW0iKSAjIHNsaXBwZWQgdGhyb3VnaCB0aGUgcmVnZXgNCmxhc3RuYW1lX29yaWdpbiRvcmlnaW4zIDwtIHN0cl9yZW1vdmUobGFzdG5hbWVfb3JpZ2luJG9yaWdpbjMsICJKb29kc2UgKGZhbWlsaWUpPyhhY2h0ZXIpP25hYW0iKQ0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjMgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMywgIkJpamJlbHNlIChmYW1pbGllKT8oYWNodGVyKT9uYWFtIikNCg0KDQojIFNldHRpbmcgZW1wdHkgb3JpZ2luIHZhcmlhYmxlcyB0byBOQSAoRHV0Y2ggb3IgdW5mb3VuZCBmb3JlaWduKQ0KbGFzdG5hbWVfb3JpZ2luIDwtIGxhc3RuYW1lX29yaWdpbiAlPiUNCiAgbXV0YXRlKG9yaWdpbjEgPSBhcy5jaGFyYWN0ZXIoaWZlbHNlKG9yaWdpbjE9PSIifG9yaWdpbjE9PSJjaGFyYWN0ZXIoMCkiLCBOQSwgb3JpZ2luMSkpLA0KICAgICAgICAgb3JpZ2luMiA9IGFzLmNoYXJhY3RlcihpZmVsc2Uob3JpZ2luMj09IiJ8b3JpZ2luMj09ImNoYXJhY3RlcigwKSIsIE5BLCBvcmlnaW4yKSksDQogICAgICAgICBvcmlnaW4zID0gYXMuY2hhcmFjdGVyKGlmZWxzZShvcmlnaW4zPT0iInxvcmlnaW4zPT0iY2hhcmFjdGVyKDApIiwgTkEsIG9yaWdpbjMpKSkNCg0KDQoNCiMgRmluYWxseSwgdGhlIHRhZyAiYW5kZXJlIHRhYWwiIHdhcyB1c2VkIHRvIGRpc3Rpbmd1aXNoIGZvcmVpZ24gbmFtZXMgb2YgdW5rbm93biBvcmlnaW4gZnJvbSBrbm93biBEdXRjaCBuYW1lcy4gDQpsYXN0bmFtZV9vcmlnaW4gPC0gbGFzdG5hbWVfb3JpZ2luICU+JQ0KICBtdXRhdGUob3JpZ2luNCA9IGlmZWxzZSgoYXMubnVtZXJpYyhzdHJfZGV0ZWN0KGtlbm1lcmtlbiwgImFuZGVyZSB0YWFsIikpPT0xKSwgIm5vbi1EdXRjaCIsIE5BKSkNCg0KDQpgYGANCg0KDQpUaGUgcmVzdWx0IGlzIGFzIGZvbGxvd3M6DQoNCmBgYHtyfQ0KDQpsYXN0bmFtZV9vcmlnaW5bLGMoMSw2LDcsOCw5KV0NCg0KYGBgDQoNCg0KDQoNCiMjIENvbWJpbmluZyBvcmlnaW4gaW5mbyBmcm9tIGRpZmZlcmVudCBzb3VyY2VzDQoNCk5vdywgd2UgcG90ZW50aWFsbHkgaGF2ZSBvcmlnaW4gaW5mb3JtYXRpb24gZnJvbSB0aHJlZSBkaWZmZXJlbnQgc291cmNlcy4gVGhlIGZpbmFsIG9yaWdpbiBkZWNpc2lvbiBpcyBiYXNlZCBvbiBob3cgZGVwZW5kYWJsZSBhbmQgaG93IGluZm9ybWF0aXZlIHRoZSBsYWJlbCBpcy4gVGhlIGhldXJpc3RpYyBpcyBhcyBmb2xsb3dzOg0KDQoxKSBQcmlvcml0eSBpcyBnaXZlbiB0byBvcmlnaW4gaW5mb3JtYXRpb24gZnJvbSBzcGVjaWZpYyBjb3VudHJ5IHRhZ3MgKGkuZS4gb3JpZ2luMSksIGJlY2F1c2UgdGhpcyBpbmZvcm1hdGlvbiBpcyB0aGUgbW9zdCBuZWF0LiBJZiBubyBjb3VudHJ5IHRhZyBpcyBwcm92aWRlZCB1bmRlciBvcmlnaW4xLCB3ZSBsb29rIGF0IHRoZSBnZW9ncmFwaGljYWwgb3JpZ2lucyBpbmRpY2F0ZWQgaW4gdGhlIHJ1bm5pbmcgdGV4dCAob3JpZ2luIDIpLiBJZiB0aGlzIGlzIGFsc28gbm90IHByZXNlbnQsIHdlIHRha2UgdGhlIGxpbmd1aXN0aWMgb3JpZ2lucyBvZiB0aGUgbmFtZSAob3JpZ2luIDMpIA0KDQoyKSBJZiB0aGVyZSBpcyBubyBzcGVjaWZpYyBjb3VudHJ5IGluZm9ybWF0aW9uLCBidXQgdGhlcmUgaXMgYSB0YWcgZm9yIGEgZm9yZWlnbiBuYW1lIChvcmlnaW40KSwgdGhlbiB0aGUgbmFtZSBpcyBsYWJlbGxlZCBhcyAidW5rbm93biBub24tRHV0Y2giLiANCg0KMykgSWYgdGhlcmUgaXMgbm8gc3BlY2lmaWMgY291bnRyeSBpbmZvcm1hdGlvbiBBTkQgbm8gdGFnIGZvciBmb3JlaWduIG5hbWUgaW4gb3JpZ2luNCwgYnV0IHRoZXJlIGlzIG90aGVyIGluZm9ybWF0aW9uIGFib3V0IHRoZSBuYW1lLCB3ZSBhc3N1bWUgdGhlIHBlcnNvbiBiZWFyaW5nIHRoaXMgbmFtZSBpcyBEdXRjaC4gDQoNCkluIHN1bTogb3JpZ2luMSA+IG9yaWdpbjIgPiBvcmlnaW4zID4gb3JpZ2luNC4gDQoNCmBgYHtyIGNvbWJpbmluZy1vcmlnaW4xMjN9DQoNCiMgT3JpZ2luIDE6IHNwZWNpZmljIGNvdW50cnkgdGFncyANCmxhc3RuYW1lX29yaWdpbiA8LSBsYXN0bmFtZV9vcmlnaW4gJT4lDQogIG11dGF0ZShvcmlnaW4gPSBvcmlnaW4xKSANCg0KIyBPcmlnaW4gMjogbm8gc3BlY2lmaWMgY291bnRyeSB0YWcsIHRha2UgdGhlIGdlb2dyYXBoaWNhbCBvcmlnaW4gb2YgdGhlIG5hbWUNCmxhc3RuYW1lX29yaWdpbiA8LSBsYXN0bmFtZV9vcmlnaW4gJT4lDQogIG11dGF0ZShvcmlnaW4gPSBpZmVsc2UoKGlzLm5hKG9yaWdpbjEpJiFpcy5uYShvcmlnaW40KSksIG9yaWdpbjIsIG9yaWdpbikpDQoNCiMgT3JpZ2luIDM6IG5laXRoZXIgb2YgdGhlIGFib3ZlLCB0YWtlIHRoZSBsaW5ndWlzdGljIG9yaWdpbiBvZiB0aGUgbmFtZQ0KbGFzdG5hbWVfb3JpZ2luIDwtIGxhc3RuYW1lX29yaWdpbiAlPiUNCiAgIG11dGF0ZShvcmlnaW4gPSBpZmVsc2UoKGlzLm5hKG9yaWdpbjEpJmlzLm5hKG9yaWdpbjIpJiFpcy5uYShvcmlnaW40KSksIG9yaWdpbjMsIG9yaWdpbikpDQoNCiMgT3JpZ2luIDQ6IHNldCB0byB1bmtub3duIG5vbi1EdXRjaCBpZiB0aGUgbmFtZSBpcyBsYWJlbGxlZCBhcyBzdWNoIHdpdGggbm8gc3BlY2lmaWMgb3JpZ2luIGluZm8NCmxhc3RuYW1lX29yaWdpbiA8LSBsYXN0bmFtZV9vcmlnaW4gJT4lDQogICAgbXV0YXRlKG9yaWdpbiA9IGlmZWxzZSgoaXMubmEob3JpZ2luMSkmaXMubmEob3JpZ2luMikmaXMubmEob3JpZ2luMykpLCBvcmlnaW40LCBvcmlnaW4pKQ0KDQojIEZpbmFsIHN0ZXA6IGlmIHRoZXJlIGlzIG5vIG9yaWdpbiBpbmZvcm1hdGlvbiBwcmVzZW50LCBidXQgb3RoZXIgYmFja2dyb3VuZCBpbmZvcm1hdGlvbiBjb3VsZCBiZSBmb3VuZCwgdGhlIG5hbWUgaXMgbGlrZWx5IER1dGNoLg0KbGFzdG5hbWVfb3JpZ2luIDwtIGxhc3RuYW1lX29yaWdpbiAlPiUNCiAgbXV0YXRlKG9yaWdpbiA9IGlmZWxzZSgoaXMubmEob3JpZ2luKSZub19pbmZvPT0wKSwgImR1dGNoIiwgb3JpZ2luKSkNCg0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbiA8LSB0cmltd3MobGFzdG5hbWVfb3JpZ2luJG9yaWdpbiwgd2hpY2ggPSAiYm90aCIpDQoNCmBgYA0KDQoNCg0KVG8gY29uc3RydWN0IHRoZSBldGhuaWNpdHkgdmFyaWFibGUsIHdlIGRpc3Rpbmd1aXNoIGZpdmUgZGlmZmVyZW50IGJhY2tncm91bmRzOg0KMS4gRXRobmljIG1ham9yaXR5IG1lbWJlcnMgKGkuZS4gdGhvc2Ugd2l0aCBEdXRjaCBuYW1lcykNCjIuIFR1cmtpc2gtRHV0Y2ggaW5kaXZpZHVhbHMNCjMuIE1vcm9jY2FuLUR1dGNoIGluZGl2aWR1YWxzDQo0LiBDYXJpYmJlYW4tRHV0Y2ggaW5kaXZpZHVhbHMNCjUuIFRob3NlIHdpdGggYSBkaWZmZXJlbnQgZXRobmljaXR5IGZyb20gdGhvc2UgbGlzdGVkIGFib3ZlDQoNCg0KYGBge3Igb3JpZ2luNWNhdH0NCg0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjEgPC0gbGFzdG5hbWVfb3JpZ2luJG9yaWdpbg0KDQpsYXN0bmFtZV9vcmlnaW4gPC0gc3Vic2V0KGxhc3RuYW1lX29yaWdpbiwgc2VsZWN0ID0gYyhsYXN0bmFtZSwgb3JpZ2luMSkpDQoNCg0KIyB3ZSBkZWZpbmUgZGlmZmVyZW50IHdheXMgaW4gd2hpY2ggZXRobmljIG1pbm9yaXR5IG5hbWVzIGFyZSBpZGVudGlmaWVkIGluIHRoZSAiRmFtaWxpZW5hbWVuYmFuayINCmxhc3RuYW1lX29yaWdpbiA8LSBsYXN0bmFtZV9vcmlnaW4gJT4lDQogIG11dGF0ZShvcmlnaW4xID0gaWZlbHNlKGFzLm51bWVyaWMoc3RyX2RldGVjdChvcmlnaW4xLCBwYXN0ZSgiKFR1cmtpamUpfChUdXJrc2UgbmFhbSkiKSkpLCAidHVya2lzaCIsIG9yaWdpbjEpKQ0KDQpsYXN0bmFtZV9vcmlnaW4gPC0gbGFzdG5hbWVfb3JpZ2luICU+JQ0KICBtdXRhdGUob3JpZ2luMSA9IGlmZWxzZShhcy5udW1lcmljKHN0cl9kZXRlY3Qob3JpZ2luMSwgcGFzdGUoIihNYXJva2thYW5zZSBuYWFtKXwoTWFyb2trbyl8KGRlIE1hcm9ra28pIikpKSwgIm1vcm9jY2FuIiwgb3JpZ2luMSkpDQoNCmxhc3RuYW1lX29yaWdpbiA8LSBsYXN0bmFtZV9vcmlnaW4gJT4lDQogIG11dGF0ZShvcmlnaW4xID0gaWZlbHNlKGFzLm51bWVyaWMoc3RyX2RldGVjdChvcmlnaW4xLCBwYXN0ZSgiKGRlIE5lZGVybGFuZHNlIEFudGlsbGVuKXwoU3VyaW5hYW1zZSBuYWFtKXwoU3VyaW5hbWUpIikpKSwgImNhcnJpYmVhbiIsIG9yaWdpbjEpKQ0KDQpsYXN0bmFtZV9vcmlnaW4gPC0gbGFzdG5hbWVfb3JpZ2luICU+JQ0KICBtdXRhdGUob3JpZ2luMSA9IGlmZWxzZSgoIWlzLm5hKG9yaWdpbjEpJmFzLm51bWVyaWMoc3RyX2RldGVjdChvcmlnaW4xLCAidHVya2lzaHxtb3JvY2NhbnxjYXJyaWJlYW58ZHV0Y2giKSk9PTApLCAib3RoZXIiLCBvcmlnaW4xKSkNCg0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjEgPC0gYXMuZmFjdG9yKGxhc3RuYW1lX29yaWdpbiRvcmlnaW4xKQ0KICAgICAgDQoNCiMgd2UgY3JlYXRlIGFuIGV4cGxpY2l0ICdtaXNzaW5nJyBjYXRlZ29yeSBmb3IgdGhvc2Ugd2hvIGFyZSBub3QgY2F0ZWdvcml6ZWQgdW5kZXIgZXRobmljIG1ham9yaXR5LCBtaW5vcml0eSBvciAnb3RoZXIgbGFzdG5hbWVfb3JpZ2luJw0KbGFzdG5hbWVfb3JpZ2luIDwtIGxhc3RuYW1lX29yaWdpbiAlPiUNCiAgbXV0YXRlKG9yaWdpbjEgPSBmY3RfZXhwbGljaXRfbmEob3JpZ2luMSwgbmFfbGV2ZWw9Im1pc3NpbmciKSkNCg0KDQojIHRodXMsIHdlIGdldCB0aGUgZm9sbG93aW5nIGRhdGFmcmFtZToNCmxhc3RuYW1lX29yaWdpbg0KDQpgYGANCg0KDQpXZSB0YWtlIHRoZSBsYXN0IG5hbWUgaW5mb3JtYXRpb24gZnJvbSBvcmlnaW4xIGFzIHRoZSBiYXNpcyBmb3Igb3VyIGV0aG5pY2l0eSB2YXJpYWJsZS4gSGVuY2UsIHdlIG1ha2UgYSBuZXcgZGF0YSBmcmFtZSwgaW4gd2hpY2ggd2UgY29weSB0aGUgaW5mb3JtYXRpb24gZnJvbSBvcmlnaW4xIHRvIGEgbmV3ICdldGhuaWNpdHknIG9iamVjdC4gTmV4dCwgd2UgZW5yaWNoIHRoaXMgZXRobmljaXR5IHZhcmlhYmxlIHVzaW5nIGluZm9ybWF0aW9uIGZyb20gZmlyc3QgbmFtZXMgYW5kIGJpcnRoIHBsYWNlcyBsaXN0ZWQgaW4gZGlzc2VydGF0aW9uIFBERnMuIA0KDQpgYGAge3J9DQoNCnBoZGV0aG5pY2l0eSA8LSBsYXN0bmFtZV9vcmlnaW4NCg0KcGhkZXRobmljaXR5JGV0aG5pY2l0eSA8LSBwaGRldGhuaWNpdHkkb3JpZ2luMQ0KDQpgYGANCg0KDQoNCg0KDQoNCiMgTWV0aG9kIDI6IE1vcm9jY2FuIG5hbWUgbGlzdA0KDQoNCkluIGFkZGl0aW9uIHRvIGRldGVybWluaW5nIGV0aG5pY2l0eSBiYXNlZCBvbiBsYXN0IG5hbWVzLCB3ZSBsb29rIGF0IGZpcnN0IG5hbWVzLiBTcGVjaWZpY2FsbHksIHdlIHVzZSBhIGxpc3Qgb2YgTW9yb2NjYW4gZmlyc3QgbmFtZXMgdG8gZGV0ZWN0IHdoZXRoZXIgYW4gaW5kaXZpZHVhbCBpcyBsaWtlbHkgb2YgTW9yb2NjYW4gZGVzY2VudC4gVW50aWxsIHZlcnkgcmVjZW50bHksIGZpcnN0IG5hbWVzIG9mIE1vcm9jY2FuIGluZGl2aWR1YWxzIHdlcmUgcmVndWxhdGVkIGJ5IE1vcm9jY2FuIGxhdyBbQGhhc2tvdXJpXS4gVGhpcyBpbXBsaWVzIHRoYXQgbWFueSBNb3JvY2NhbiBpbmRpdmlkdWFscyBjaG9vc2UgbmFtZXMgZm9yIHRoZWlyIGNoaWxkcmVuIGZyb20gYSBsaXN0IG9mIGFjY2VwdGVkIGdpcmxzIGFuZCBib3lzIG5hbWVzLiBXZSBsb2FkIGluIGEgbGlzdCBvZiBuYW1lcyBieSBAYE1vcm9jY28gR3VpZGVgIGFzIGFuIGFkZGl0aW9uYWwgaW5kaWNhdG9yIHRoYXQgYSBQaEQgaXMgTW9yb2NjYW4gYW5kIGFkZCB0aGlzIHRvIG91ciBleGFtcGxlIGRhdGEgZnJhbWUuIA0KDQoNCmBgYHtyfQ0KDQojIEFkZGluZyBmaXJzdCBuYW1lcyBiYWNrIGluDQpwaGRldGhuaWNpdHkgPC0gbGVmdF9qb2luKHBoZG5hbWVzLCBwaGRldGhuaWNpdHksIGJ5ID0gImxhc3RuYW1lIikNCg0KDQojIEFkZCB0aGUgbW9yb2NjYW4gbmFtZXMgdG8gdGhlIGV0aG5pY2l0eSBkYXRhDQpsb2FkKGZpbGUgPSAiZGF0YS9wcm9jZXNzZWQvbW9yb2NjYW5uYW1lcy5yZGEiKQ0KDQoNCnBoZGV0aG5pY2l0eSA8LSBsZWZ0X2pvaW4ocGhkZXRobmljaXR5LCBtb3JvY2Nhbm5hbWVzWyxjKDEsMyldLCBieSA9ICJmaXJzdG5hbWUiKQ0KDQoNCiMgQWRkIGV4cGxpY2l0IG1pc3NpbmcgY2F0ZWdvcnkgaW4gb3JpZ2luMg0KcGhkZXRobmljaXR5IDwtIHBoZGV0aG5pY2l0eSAlPiUNCiAgbXV0YXRlKG9yaWdpbjIgPSBmY3RfZXhwbGljaXRfbmEob3JpZ2luMiwgbmFfbGV2ZWw9Im1pc3NpbmciKSkNCg0KIyBXZSBvdmVyd3JpdGUgZXRobmljaXR5IHRvIG1pbm9yaXR5IGlmIHRoZSBmaXJzdCBuYW1lIGlzIE1vcm9jY2FuDQpmb3IgKGkgaW4gMTpucm93KHBoZGV0aG5pY2l0eSkpIHsNCiAgaWYgKHBoZGV0aG5pY2l0eSRvcmlnaW4xW2ldPT0ibWlzc2luZyIgJiBwaGRldGhuaWNpdHkkb3JpZ2luMltpXT09Im1vcm9jY2FuIikgew0KICAgIHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV0gPC0gIm1vcm9jY2FuIg0KICB9IGVsc2UgaWYgKHBoZGV0aG5pY2l0eSRvcmlnaW4xW2ldPT0iZHV0Y2giICYgcGhkZXRobmljaXR5JG9yaWdpbjJbaV09PSJtb3JvY2NhbiIpIHsNCiAgICBwaGRldGhuaWNpdHkkZXRobmljaXR5W2ldIDwtICJtb3JvY2NhbiINCiAgfSBlbHNlIGlmIChwaGRldGhuaWNpdHkkb3JpZ2luMVtpXT09Im90aGVyIiAmIHBoZGV0aG5pY2l0eSRvcmlnaW4yW2ldPT0ibW9yb2NjYW4iKSB7DQogICAgcGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXSA8LSAibW9yb2NjYW4iDQogIH0gDQp9DQoNCmBgYA0KDQoNCkxldCdzIHNlZSB3aGF0IHRoZSBkYXRhIGxvb2tzIGxpa2UuIEluIHRoaXMgY2FzZSwgd2Ugc2VlIHRoYXQgdGhlIHR3byBtZXRob2RzIGZvciBkZXRlcm1pbmluZyBldGhuaWNpdHkgKGF0IGxlYXN0IHdpdGggcmVnYXJkcyB0byBNb3JvY2NhbiB2cyBub24tTW9yb2NjYW4pIGFwcGVhciB0byBiZSBpbiBhZ3JlZW1lbnQuIA0KDQpgYGB7cn0NCg0KcGhkZXRobmljaXR5WyxjKDEsMiw1LDksMTEsNildDQoNCmBgYA0KDQoNCg0KIyBNZXRob2QgMzogUERGIGJpcnRoIHBsYWNlcw0KDQpCYXNlZCBvbiBsaXN0cyBvZiBEdXRjaCBtdW5pY2lwYWxpdGllcywgd2UgY2FuIGVzdGFibGlzaCB3aGV0aGVyIHNvbWVvbmUgd2FzIGJvcm4gaW4gdGhlIE5ldGhlcmxhbmRzLiBXZSB1c2UgbGlzdHMgb2YgbXVuaWNpcGFsaXRpZXMgcHJvdmlkZWQgYnkgU3RhdGlzdGljcyBOZXRoZXJsYW5kcyBmcm9tIHRoZSB5ZWFycyAyMDA2LzIwMTQvMjAxNS8yMDE2LzIwMTgvMjAxOS8yMDIwLzIwMjEsIGFzIHdlbGwgYXMgbXV0YXRpb25zIGluIG11bmljaXBhbGl0aWVzIG9yIG11bmljaXBhbGl0eSBuYW1lcyBkdXJpbmcgdGhlIHBlcmlvZCBvZiB0aGlzIHN0dWR5IFtAU3RhdGlzdGljcyBOZXRoZXJsYW5kc10uDQoNCklmIHRoZSBiaXJ0aCBwbGFjZSBpbmZvIGNvbnRhaW5zIGEgRHV0Y2ggbXVuaWNpcGFsaXR5IG9yIGEgcmVmZXJlbmNlIHRvIHRoZSBOZXRoZXJsYW5kcywgYSBwZXJzb24gaXMgYm9ybiBpbiB0aGUgTmV0aGVybGFuZHMuIElmIHRoZXJlIGlzIGEgcmVmZXJlbmNlIHRvIFR1cmtleSwgTW9yb2NjbywgdGhlDQoNCmBgYHtyfQ0KDQojIGxvYWRpbmcgaW4gdGhlIGxpc3Qgb2YgRHV0Y2ggbXVuaWNpcGFsaXRpZXMNCmxvYWQoZmlsZSA9ICJkYXRhL211bmljaXBhbGl0aWVzL2R1dGNoX211bl9hbGwucmRhIikNCg0KIyBjcmVhdGluZyBzdHJpbmdzIHdpdGggaW5kaWNhdG9ycyBmb3IgYmlydGggY291bnRyaWVzIChUaGUgTmV0aGVybGFuZHMsIENhcmliYmVhbiBOZXRoZXJsYW5kcywgVHVya2V5LCBNb3JvY2NvKQ0KZHV0Y2ggPC0gYyhhcy5jaGFyYWN0ZXIoZHV0Y2hfbXVuJG11bmljaXBhbGl0eSksICJuZXRoZXJsYW5kcyIsICJ0aGUgbmV0aGVybGFuZHMiLCAibmVkZXJsYW5kIiwiXFxzbmxcXHMiKQ0KDQpjYXJyaWJlYW4gPC0gYygic3VyaW5hbWUiLCAibmVkZXJsYW5kc2UgYW50aWxsZW4iLCAiY3VyYWNhbyIsICJjdXJhw6dhbyIsICJib25haXJlIiwgIlxcc3NzYWJhXFxzIiwgInNpbnQgZXVzdGF0aXVzIiwgInNpbnQgbWFhcnRlbiIsICJhcnViYSIpDQoNCnR1cmtpc2ggPC0gYygidHVya2lqZSIsICJ0dXJrZXkiKQ0KDQptb3JvY2NhbiA8LSBjKCJtYXJva2tvIiwgIm1vcm9jY28iKQ0KDQoNCg0KIyBPcmlnaW4zOiBjb3VudHJ5IG9mIGJpcnRoIHZhcmlhYmxlDQpwaGRldGhuaWNpdHkkb3JpZ2luMyA8LSBpZmVsc2UoYXMubnVtZXJpYyhzdHJfZGV0ZWN0KHBoZGV0aG5pY2l0eSRkaXNzX2JpcnRocGxhY2UsIHBhc3RlKGR1dGNoLCBjb2xsYXBzZSA9ICJ8IikpKT09MSwgImR1dGNoIiwgTkEpDQoNCnBoZGV0aG5pY2l0eSRvcmlnaW4zIDwtIGlmZWxzZShhcy5udW1lcmljKHN0cl9kZXRlY3QocGhkZXRobmljaXR5JGRpc3NfYmlydGhwbGFjZSwgcGFzdGUoY2FycmliZWFuLCBjb2xsYXBzZSA9ICJ8IikpKT09MSwgImNhcmliYmVhbiIsIHBoZGV0aG5pY2l0eSRvcmlnaW4zKQ0KDQpwaGRldGhuaWNpdHkkb3JpZ2luMyA8LSBpZmVsc2UoYXMubnVtZXJpYyhzdHJfZGV0ZWN0KHBoZGV0aG5pY2l0eSRkaXNzX2JpcnRocGxhY2UsIHBhc3RlKHR1cmtpc2gsIGNvbGxhcHNlID0gInwiKSkpPT0xLCAidHVya2lzaCIsIHBoZGV0aG5pY2l0eSRvcmlnaW4zKQ0KDQpwaGRldGhuaWNpdHkkb3JpZ2luMyA8LSBpZmVsc2UoYXMubnVtZXJpYyhzdHJfZGV0ZWN0KHBoZGV0aG5pY2l0eSRkaXNzX2JpcnRocGxhY2UsIHBhc3RlKG1vcm9jY2FuLCBjb2xsYXBzZSA9ICJ8IikpKT09MSwgIm1vcm9jY2FuIiwgcGhkZXRobmljaXR5JG9yaWdpbjMpDQoNCg0KcGhkZXRobmljaXR5JG9yaWdpbjMgPC0gZmFjdG9yKHBoZGV0aG5pY2l0eSRvcmlnaW4zLCBsZXZlbHM9YygiZHV0Y2giLCAiY2FyaWJiZWFuIiwgInR1cmtpc2giLCAibW9yb2NjYW4iKSkNCg0KIyBDcmVhdGUgZXhwbGljaXQgbWlzc2luZyBjYXRlZ29yeQ0KcGhkZXRobmljaXR5IDwtIHBoZGV0aG5pY2l0eSAlPiUNCiAgbXV0YXRlKG9yaWdpbjMgPSBmY3RfZXhwbGljaXRfbmEob3JpZ2luMywgbmFfbGV2ZWw9Im1pc3NpbmciKSkNCg0KDQojIFdlIHJlcGxhY2UgbWlzc2luZyBvciBvdGhlciB3aXRoICJkdXRjaCIgaWYgYSBwZXJzb24gaXMgYm9ybiBpbiBOTA0KIyBXZSByZXBsYWNlIG1pc3NpbmcsIG90aGVyLCBkdXRjaCBhbmQgbW9yb2NjYW4gd2l0aCAidHVya2lzaCIgaWYgYSBwZXJzb24gaXMgYm9ybiBpbiBUdXJrZXkNCiMgV2UgcmVwbGFjZSBtaXNzaW5nLCBvdGhlciwgZHV0Y2ggYW5kIHR1cmtpc2ggd2l0aCAibW9yb2NjYW4iIGlmIGEgcGVyc29uIGlzIGJvcm4gaW4gTW9yb2Njbw0KDQpmb3IgKGkgaW4gMTpucm93KHBoZGV0aG5pY2l0eSkpIHsNCiAgaWYgKHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV09PSJtaXNzaW5nIiAmIHBoZGV0aG5pY2l0eSRvcmlnaW4zW2ldPT0iZHV0Y2giKSB7DQogICAgcGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXSA8LSAiZHV0Y2giDQogIH0gZWxzZSBpZiAocGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXT09Im90aGVyIiAmIHBoZGV0aG5pY2l0eSRvcmlnaW4zW2ldPT0iZHV0Y2giKSB7DQogICAgcGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXSA8LSAiZHV0Y2giDQogICAgfSBlbHNlIGlmIChwaGRldGhuaWNpdHkkZXRobmljaXR5W2ldPT0ibWlzc2luZyIgJiBwaGRldGhuaWNpdHkkb3JpZ2luM1tpXT09Im1vcm9jY2FuIikgew0KICAgIHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV0gPC0gIm1vcm9jY2FuIg0KICB9IGVsc2UgaWYgKHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV09PSJkdXRjaCIgJiBwaGRldGhuaWNpdHkkb3JpZ2luM1tpXT09Im1vcm9jY2FuIikgew0KICAgIHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV0gPC0gIm1vcm9jY2FuIg0KICB9IGVsc2UgaWYgKHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV09PSJvdGhlciIgJiBwaGRldGhuaWNpdHkkb3JpZ2luM1tpXT09Im1vcm9jY2FuIikgew0KICAgIHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV0gPC0gIm1vcm9jY2FuIg0KICB9IGVsc2UgaWYgKHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV09PSJ0dXJraXNoIiAmIHBoZGV0aG5pY2l0eSRvcmlnaW4zW2ldPT0ibW9yb2NjYW4iKSB7DQogICAgcGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXSA8LSAibW9yb2NjYW4iDQogIH0gZWxzZSBpZiAocGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXT09Im1pc3NpbmciICYgcGhkZXRobmljaXR5JG9yaWdpbjNbaV09PSJ0dXJraXNoIikgew0KICAgIHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV0gPC0gInR1cmtpc2giDQogIH0gZWxzZSBpZiAocGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXT09ImR1dGNoIiAmIHBoZGV0aG5pY2l0eSRvcmlnaW4zW2ldPT0idHVya2lzaCIpIHsNCiAgICBwaGRldGhuaWNpdHkkZXRobmljaXR5W2ldIDwtICJ0dXJraXNoIg0KICB9IGVsc2UgaWYgKHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV09PSJvdGhlciIgJiBwaGRldGhuaWNpdHkkb3JpZ2luM1tpXT09InR1cmtpc2giKSB7DQogICAgcGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXSA8LSAidHVya2lzaCINCiAgfSBlbHNlIGlmIChwaGRldGhuaWNpdHkkZXRobmljaXR5W2ldPT0ibW9yb2NjYW4iICYgcGhkZXRobmljaXR5JG9yaWdpbjNbaV09PSJ0dXJraXNoIikgew0KICAgIHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV0gPC0gInR1cmtpc2giDQogIH0gDQp9DQoNCmBgYA0KDQoNCg0KQWZ0ZXIgYWxsIHRoZXNlIG9wZXJhdGlvbnMsIHdlIGFyZSBsZWZ0IHdpdGggYW4gZXRobmljaXR5IG9iamVjdCB3aGljaCBpcyBiYXNlZCBvbiBvcmlnaW5zIG9mIGxhc3QgbmFtZXMgYW5kIGZpcnN0IG5hbWVzLCBhbmQgb24gdGhlIGJpcnRoIHBsYWNlIGxpc3RlZCBpbiB0aGUgZGlzc2VydGF0aW9uLiANCg0KRm9yIHRoZXNlIGV4YW1wbGUgY2FzZXMsIHRoZSBsYWJlbHMgZ2l2ZW4gYmFzZWQgb24gdGhlc2UgZGlmZmVyZW50IGluZGljYXRvcnMgb3ZlcmxhcCBwZXJmZWN0bHksIGJ1dCB0aGUgY29kZSBhYm92ZSBnaXZlcyBpbnNpZ2h0IGluIHRoZSBoZXVyaXN0aWNzIHdlIHVzZSB0byBjb21iaW5lIHBvdGVudGlhbGx5IGNvbmZsaWN0aW5nIGV0aG5pY2l0eSBsYWJlbHMuIA0KDQpgYGB7cn0NCg0KcGhkZXRobmljaXR5WyxjKDIsNSw2LDEwLDksMTEsMTIpXQ0KDQpgYGANCg0KDQoNCiMgRXRobmljaXR5IDINCg0KQWx0aG91Z2ggd2UgbmVlZCB0aGUgc3BlY2lmaWMgZXRobmljaXR5IGxhYmVscyBmb3Igb3VyIG1lYXN1cmUgb2YgZ2VuZGVyLCB3ZSB1c2UgYSBsZXNzLWRldGFpbGVkIG1lYXN1cmUgb2YgZXRobmljIGJhY2tncm91bmQgZm9yIG91ciBhbmFseXNlcy4gSW4gdGhpcyBtZWFzdXJlIHdlIGRpc3Rpbmd1aXNoIGJldHdlZW4gaW5kaXZpZHVhbHMgd2l0aCBhbiBldGhuaWMgbWFqb3JpdHksIGV0aG5pYyBtaW5vcml0eSAgKFR1cmtpc2gtRHV0Y2gsIE1vcm9jY2FuLUR1dGNoLCBDYXJpYmJlYW4tRHV0Y2gpIG9yIG90aGVyIGV0aG5pYyBiYWNrZ3JvdW5kLiBUaGlzIGhhcyB0byBkbyB3aXRoIHNtYWxsIGdyb3VwIHNpemVzIGZvciBlYWNoIG9mIHRoZSBzcGVjaWZpYyBldGhuaWMgbWlub3JpdHkgYmFja2dyb3VuZHMuIA0KDQpXZSBzdWJzdW1lICJtaXNzaW5nIiBldGhuaWMgYmFja2dyb3VuZCB1bmRlciBvdGhlciBldGhuaWMgYmFja2dyb3VuZCwgYmVjYXVzZSB3ZSBiZWxpZXZlIHRoYXQgaXQgaXMgdW5saWtlbHkgdGhhdCBhIHBlcnNvbiB3aXRoIGEgRHV0Y2ggbWFqb3JpdHksIFR1cmtpc2gtRHV0Y2gsIE1vcm9jY2FuLUR1dGNoIG9yIENhcmliYmVhbi1EdXRjaCBiYWNrZ3JvdW5kIHdvdWxkIGhhdmUgZ29uZSB1bm5vdGljZWQgdXNpbmcgb3VyIGFycmF5IG9mIG1ldGhvZHMgdG8gZGV0ZXJtaW5lIGV0aG5pY2l0eS4gDQoNCmBgYHtyfQ0KDQpwaGRldGhuaWNpdHkkZXRobmljaXR5MiA8LSBwaGRldGhuaWNpdHkkZXRobmljaXR5DQoNCnBoZGV0aG5pY2l0eSRldGhuaWNpdHkyIDwtIGZjdF9jb2xsYXBzZShwaGRldGhuaWNpdHkkZXRobmljaXR5MiwNCiAgICAgICAgICAgICBtYWpvcml0eSA9ICJkdXRjaCIsDQogICAgICAgICAgICAgbWlub3JpdHkgPSBjKCJtb3JvY2NhbiIsICJ0dXJraXNoIiwgImNhcnJpYmVhbiIpLA0KICAgICAgICAgICAgIG90aGVyID0gYygib3RoZXIiLCAibWlzc2luZyIpKQ0KDQoNCg0KcGhkZXRobmljaXR5IDwtIHN1YnNldChwaGRldGhuaWNpdHksIHNlbGVjdD1jKGlkLCBmaXJzdG5hbWUsIG5wLCBsYXN0bmFtZSwgbGFzdG5hbWVfZnVsbCwgZGlzc19iaXJ0aHBsYWNlLCB1bmksIHBoZF95ZWFyLCBldGhuaWNpdHksIGV0aG5pY2l0eTIpKQ0KDQpgYGANCg0KDQoNCi0tLSANCg0KIyBPdXRwdXQNCg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCg0KDQpwaGRldGhuaWNpdHkNCg0KYGBgDQoNCg0KYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9DQoNCnNhdmUocGhkZXRobmljaXR5LCBmaWxlID0gIi4vZGF0YS9wcm9jZXNzZWQvcGhkZXRobmljaXR5LnJkYSIpDQoNCmBgYA0KDQoNCg0KLS0tICANCg0KIyBSZWZlcmVuY2VzDQoNCg0K


Copyright © 2023