We determine ethnic backgrounds based on last names. For this, we use
information from three sources:
Data on the origin of last names, web scraped from the Nederlandse
Familienamenbank
A list of common Moroccan names
Birth countries listed in dissertation PDFs
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)
}
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)
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
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)
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.
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:
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)
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”.
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
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)]
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)]
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))
References
LS0tDQp0aXRsZTogIkFjYWRlbWljIHB1Ymxpc2hpbmcgY2FyZWVycyBpbiBOTDogZ2F0aGVyaW5nIGV0aG5pY2l0eSINCmJpYmxpb2dyYXBoeTogcmVmZXJlbmNlcy5iaWINCmRhdGU6ICJMYXN0IGNvbXBpbGVkIG9uIGByIGZvcm1hdChTeXMudGltZSgpLCAnJUIsICVZJylgIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjc3M6IHR3ZWFrcy5jc3MNCiAgICB0b2M6ICB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgY29kZV9kb3dubG9hZDogeWVzDQotLS0NCg0KPCEtLS1wbGVhc2UgYmUgYXdhcmUgdGhhdCBjYWNoaW5nIGxhcmdlIG9iamVjdHMgaXMgcHJvYmxlbWF0aWMsIGhlbmNlIGNhY2hlLmxhenk9RkFMU0UgYW5kIHlvdSBtYXkgbmVlZCB0byB0dXJuIGNhY2hlPUZBTFNFIGZvciBjaHVuY2tzIGluIHdoaWNoIHlvdSBsb2FkIGxhcmdlIGRhdGFzZXRzIC0tLT4gDQoNCmBgYHtyLCBnbG9iYWxzZXR0aW5ncywgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0iaGlkZSJ9DQpsaWJyYXJ5KGtuaXRyKQ0Kb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGguY3V0b2ZmPTEwMCksdGlkeT1UUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSxjb21tZW50ID0gIiM+IiwgY2FjaGU9VFJVRSwgY2xhc3Muc291cmNlPWMoInRlc3QiKSwgY2xhc3Mub3V0cHV0PWMoInRlc3QyIiksIGNhY2hlLmxhenkgPSBGQUxTRSwgZXZhbD1GQUxTRSkNCm9wdGlvbnMod2lkdGggPSAxMDApDQpyZ2w6OnNldHVwS25pdHIoKQ0KDQpjb2xvcml6ZSA8LSBmdW5jdGlvbih4LCBjb2xvcikge3NwcmludGYoIjxzcGFuIHN0eWxlPSdjb2xvcjogJXM7Jz4lczwvc3Bhbj4iLCBjb2xvciwgeCkgfQ0KDQpgYGANCg0KYGBge3Iga2xpcHB5LCBlY2hvPUZBTFNFLCBpbmNsdWRlPVRSVUV9DQprbGlwcHk6OmtsaXBweShwb3NpdGlvbiA9IGMoJ3RvcCcsICdyaWdodCcpKQ0KI2tsaXBweTo6a2xpcHB5KGNvbG9yID0gJ2RhcmtyZWQnKQ0KI2tsaXBweTo6a2xpcHB5KHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgdG9vbHRpcF9zdWNjZXNzID0gJ0RvbmUnKQ0KYGBgDQoNCg0KDQoNCi0tLS0NCg0KDQpXZSBkZXRlcm1pbmUgZXRobmljIGJhY2tncm91bmRzIGJhc2VkIG9uIGxhc3QgbmFtZXMuIEZvciB0aGlzLCB3ZSB1c2UgaW5mb3JtYXRpb24gZnJvbSB0aHJlZSBzb3VyY2VzOg0KDQoxLiBEYXRhIG9uIHRoZSBvcmlnaW4gb2YgbGFzdCBuYW1lcywgd2ViIHNjcmFwZWQgZnJvbSB0aGUgW05lZGVybGFuZHNlIEZhbWlsaWVuYW1lbmJhbmtdKGh0dHBzOi8vd3d3LmNiZ2ZhbWlsaWVuYW1lbi5ubC9uZmIvKQ0KDQoyLiBBIGxpc3Qgb2YgY29tbW9uIE1vcm9jY2FuIG5hbWVzDQoNCjMuIEJpcnRoIGNvdW50cmllcyBsaXN0ZWQgaW4gZGlzc2VydGF0aW9uIFBERnMNCg0KDQogIA0KLS0tLQ0KDQojIEN1c3RvbSBmdW5jdGlvbnMNCg0KLSBgZnBhY2thZ2UuY2hlY2tgOiBDaGVjayBpZiBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkIChhbmQgaW5zdGFsbCBpZiBub3QpIGluIFIgKFtzb3VyY2VdKGh0dHBzOi8vdmJhbGlnYS5naXRodWIuaW8vdmVyaWZ5LXRoYXQtci1wYWNrYWdlcy1hcmUtaW5zdGFsbGVkLWFuZC1sb2FkZWQvKSkuICANCg0KDQpgYGB7ciwgcmVzdWx0cz0naGlkZSd9DQoNCnJtKGxpc3QgPSBscygpKQ0KDQoNCmZwYWNrYWdlLmNoZWNrIDwtIGZ1bmN0aW9uKHBhY2thZ2VzKSB7DQogIGxhcHBseShwYWNrYWdlcywgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgIGlmICghcmVxdWlyZSh4LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7DQogICAgICBpbnN0YWxsLnBhY2thZ2VzKHgsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICAgICBsaWJyYXJ5KHgsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCiAgICB9DQogIH0pDQp9DQoNCmZzYXZlIDwtIGZ1bmN0aW9uKHgsIGZpbGUsIGxvY2F0aW9uPSIuL2RhdGEvcHJvY2Vzc2VkLyIpIHsNCiAgZGF0ZW5hbWUgPC0gc3Vic3RyKGdzdWIoIls6LV0iLCAiIiwgU3lzLnRpbWUoKSksIDEsOCkgIA0KICB0b3RhbG5hbWUgPC0gcGFzdGUobG9jYXRpb24sIGRhdGVuYW1lLCBmaWxlLCBzZXA9IiIpDQogIHNhdmUoeCwgZmlsZSA9IHRvdGFsbmFtZSkgIA0KfQ0KDQpgYGANCg0KLS0tICANCg0KIyBQYWNrYWdlcw0KDQotIGB0aWR5dmVyc2VgOiBGb3IgZ2VuZXJhbCBkYXRhIG1hbmlwdWxhaW9uDQoNCi0gYHN0cmluZ3JgOiBmb3Igc3RyaW5nIG1hbmlwdWxhdGlvbnMNCg0KLSBgZHBseXJgOiBmb3IgZGF0YSBtYW5pcHVsYXRpb24NCg0KLSBgcnZlc3RgOiBmb3IgDQoNCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnfQ0KDQoNCnBhY2thZ2VzID0gYygidGlkeXZlcnNlIiwgInN0cmluZ3IiLCAiZHBseXIiLCAicnZlc3QiLCAic3RyaW5naSIsICJmb3JjYXRzIikNCg0KZnBhY2thZ2UuY2hlY2socGFja2FnZXMpDQoNCmBgYA0KDQotLS0gDQoNCg0KIyBJbnB1dA0KDQpXZSB1c2UgdHdvIHByb2Nlc3NlZCBkYXRhc2V0cw0KDQoqIFtwaGRuYW1lcy5yZGFdKGh0dHBzOi8vZ2l0aHViLmNvbS9hbW11bGRlcnMvYW1hdHRlcm9mdGltZS9kYXRhL3Byb2Nlc3NlZC9waGRuYW1lcy5yZGEpOiBleGFtcGxlIGRhdGFzZXQgb2YgOCAoZmljdGlvbmFsKSBQaERzIHdpdGggZmlyc3QgYW5kIGxhc3QgbmFtZXMNCiAgICAtIEZvciBjb25zdHJ1Y3Rpb24gb2YgdGhpcyBkYXRhc2V0IHNlZSBbSW5kZXBlbmRlbnQgdmFyaWFibGVzOiBuYW1lc10obmFtZXMuaHRtbCkgIA0KICAgIC0gbmFtZSBvZiBkYXRhc2V0OiBgcGhkbmFtZXNgIA0KDQoqIFtsYXN0bmFtZV9vcmlnaW4ucmRhXShodHRwczovL2dpdGh1Yi5jb20vYW1tdWxkZXJzL2FtYXR0ZXJvZnRpbWUvZGF0YS9wcm9jZXNzZWQvbGFzdG5hbWVfb3JpZ2luLnJkYSk6IHdlYiBzY3JhcGVkIGV0aG5pY2l0eSBkYXRhIGZvciB0aGUgOCBsYXN0IG5hbWVzIGluIHRoZSBleGFtcGxlIGRhdGEgZnJvbSBkZSBOZWRlcmxhbmRzZSBGYW1pbGllbmFtZW5iYW5rDQogICAgLSBuYW1lIG9mIGRhdGFzZXQ6IGBnZW5kZXJgIA0KDQpgYGB7ciBkYXRhc2V0cywgY2FjaGU9RkFMU0V9DQoNCmxvYWQoZmlsZSA9ICIuL2RhdGEvcHJvY2Vzc2VkL3BoZG5hbWVzLnJkYSIpDQoNCmxvYWQoZmlsZSA9ICIuL2RhdGEvcHJvY2Vzc2VkL2xhc3RuYW1lX29yaWdpbi5yZGEiKQ0KDQoNCmBgYA0KDQotLS0gIA0KDQojIE1ldGhvZCAxOiBGYW1pbGllbmFtZW5iYW5rIGxhc3QgbmFtZSBvcmlnaW4NCg0KVGhlIHByaW1hcnkgZGV0ZXJtaW5hbnQgZm9yIGV0aG5pY2l0eSBpcyBbZGUgTmVkZXJsYW5kc2UgRmFtaWxpZW5hbWVuYmFua10oaHR0cHM6Ly93d3cuY2JnZmFtaWxpZW5hbWVuLm5sL25mYi8pKGhlcmVhZnRlciAiRmFtaWxpZW5hbWVuYmFuayIpLiBUaGlzIHdlYnNpdGUsIGxpa2UgdGhlIE1lZXJ0ZW5zIFZvb3JuYW1lbmJhbmsgd2hpY2ggd2FzIHVzZWQgdG8gZGV0ZXJtaW5lIHJlc2VhcmNoZXJzJyBnZW5kZXIsIHVzZXMgcmVnaXN0ZXIgZGF0YSBmcm9tIER1dGNoIGluaGFiaXRhbnRzLiBTcGVjaWZpY2FsbHksIHRoZSB3ZWJzaXRlIGhhcmJvcnMgaW5mb3JtYXRpb24gb24gdGhlIGZyZXF1ZW5jeSwgZ2VvZ3JhcGhpY2FsIHNwcmVhZCwgYW5kIG9yaWdpbiBvZiAzMjAsMDAwIGxhc3QgbmFtZXMgd2hpY2ggb2NjdXIgaW4gdGhlIER1dGNoIHJlZ2lzdGVyLiANCg0KVGhlIG9yaWdpbiBpbmZvcm1hdGlvbiwgc3BlY2lmaWNhbGx5LCBpcyB1c2VkIHRvIGRldGVybWluZSByZXNlYXJjaGVycycgZXRobmljaXRpZXMuIFRoaXMgaW5mb3JtYXRpb24gaXMgZm91bmQgdW5kZXIgdGhlICdhbmFseXNlIGVuIHZlcmtsYXJpbmcnIHRhYiBvZiB0aGUgbmFtZSBwYWdlLiANClRoaXMgaW5mb3JtYXRpb24gaXMgc3RydWN0dXJlZCBzbyB0aGF0LCBpZiBuYW1lcyBhcmUgRHV0Y2gsIG1vcmUgZGV0YWlsZWQgb3JpZ2lucyBvZiB0aGUgbmFtZSBhcmUgbWVudGlvbmVkIChlLmcuIHdoZXRoZXIgdGhlIGxhc3QgbmFtZSBvcmlnaW5hdGVzIGZyb20gYSBjZXJ0YWluIHByb2Zlc3Npb24sIGdlb2dyYXBoaWNhbCByZWdpb24gaW4gdGhlIE5ldGhlcmxhbmRzIG9yIHdoZXRoZXIgaXQgaXMgZGVyaXZlZCBmcm9tIGZhdGhlcnMnIGZpcnN0IG5hbWVzKS4gSWYgbmFtZXMgYXJlIG5vdCBEdXRjaCwgaG93ZXZlciwgb25seSB0aGUgY291bnRyeSBvciBicm9hZGVyIHJlZ2lvbiBmcm9tIHdoaWNoIHRoZSBuYW1lIG9yaWdpbmF0ZXMgaXMgbWVudGlvbmVkLiBTZWUgYmVsb3cgZm9yIGV4YW1wbGVzIG9mIHRoZXNlIHBhZ2VzIGZvciBhIER1dGNoIGFuZCBhIG5vbi1EdXRjaCBsYXN0IG5hbWUuICANCg0KIVt2ZXJzY2h1dXJlbmNiZ10oLi9taXNjL3ZlcnNjaHV1cmVuX2NiZ2YucG5nKQ0KDQohW2thcmltaWNiZ10oLi9taXNjL2thcmltaV9jYmdmLnBuZykNCg0KV2Ugc2NyYXBlZCB0aGUgImFuYWx5c2UgZW4gdmVya2xhcmluZyIgc2VjdGlvbiBmb3IgZWFjaCBvZiB0aGUgbGFzdCBuYW1lcyBpbiBvdXIgc2FtcGxlLiBUaGUgbGF0dGVyIGltYWdlIHNob3dzIHRoYXQgdGhlIHN1YnNlY3Rpb24gInZlcmtsYXJpbmciIGFuZCAia2VubWVya2VuIiBwcm92aWRlIGluZm9ybWF0aW9uIG9uIHRoZSBjb3VudHJpZXMgYXNzb2NpYXRlZCB3aXRoIG5vbi1EdXRjaCBuYW1lcywgd2hpbGUgInNwZWNpZmlla2UgY29tcG9uZW50ZW4iIHByb3ZpZGVzIG1vcmUgaW5mb3JtYXRpb24gb24gY2VydGFpbiBwYXJ0cyBvZiB0aGUgbmFtZS4gVGhlIHJlc3VsdGluZyBkYXRhZnJhbWUgZm9yIG91ciBleGFtcGxlIGRhdGFzZXQgaXMgc2hvd24gYmVsb3cuDQoNCmBgYHtyfQ0KDQpwcmludChsYXN0bmFtZV9vcmlnaW4sIHJvdy5uYW1lcz1GQUxTRSkNCg0KYGBgDQoNCg0KIyMgU2VwYXJhdGluZyBuYW1lcyB3aXRoIER1dGNoICYgdW5rbm93biBvcmlnaW4NCg0KQXMgbWVudGlvbmVkIGVhcmxpZXIsIGl0IGlzIG5vdCBtZW50aW9uZWQgZXhwbGljaXRseSB0aGF0IER1dGNoIG5hbWVzIGFyZSBEdXRjaC4gVGhlcmVmb3JlLCB3ZSBuZWVkIHRvIGlkZW50aWZ5IER1dGNoIG5hbWVzIHNwZWNpZmljYWxseSwgYW5kIHNldCB0aGVtIGFwYXJ0IGZyb20gbm9uLUR1dGNoIG5hbWVzIGZvciB3aGljaCB0aGVyZSBpcyBubyBvcmlnaW4gaW5mb3JtYXRpb24gcHJlc2VudC4gDQoNCkluIHNob3J0LCB3ZSBkZXRlY3QgdGhlIHR3byBjYXRlZ29yaWVzIG9mIG5hbWVzIGFzIGZvbGxvd3M6DQotIER1dGNoIG5hbWVzOiBubyBsYWJlbCBpbmRpY2F0aW5nIHRoYXQgdGhlIG5hbWUgaXMgRHV0Y2gsIGJ1dCBzb21lIG90aGVyIGluZm9ybWF0aW9uIGF2YWlsYWJsZSBvbiBuYW1lIG9yaWdpbg0KLSBVbmtub3duIG5hbWVzOiB3ZWIgcGFnZSBjYW5ub3QgYmUgZm91bmQsIHNvIG9yaWdpbiBpbmZvcm1hdGlvbiBpcyBjb21wbGV0ZWx5IGVtcHR5LiANCg0KYGBge3Igb3JpZ2luLXVua25vd259DQoNCiMgSWRlbnRpZnkgbGFzdCBuYW1lcyB0aGF0IGNvdWxkIG5vdCBiZSBmb3VuZA0KbGFzdG5hbWVfb3JpZ2luIDwtIGxhc3RuYW1lX29yaWdpbiAlPiUNCiAgbXV0YXRlKHZlcmtsYXJpbmcgPSBpZmVsc2UodmVya2xhcmluZz09IiIsIDAsIHZlcmtsYXJpbmcpLCANCiAgICAgICAgIGtlbm1lcmtlbiA9IGlmZWxzZShrZW5tZXJrZW49PSJjaGFyYWN0ZXIoMCkiLCAwLCBrZW5tZXJrZW4pLA0KICAgICAgICAgc2MgPSBpZmVsc2Uoc2M9PSJjaGFyYWN0ZXIoMCkiLCAwLCBzYyksDQogICAgICAgICBub19pbmZvID0gbmNoYXIodmVya2xhcmluZykgKyBuY2hhcihrZW5tZXJrZW4pICsgbmNoYXIoc2MpKQ0KDQpsYXN0bmFtZV9vcmlnaW4gPC0gbGFzdG5hbWVfb3JpZ2luICU+JQ0KICBtdXRhdGUobm9faW5mbyA9IGlmZWxzZShub19pbmZvPT0zLCAxLCAwKSwgDQogICAgICAgICB2ZXJrbGFyaW5nID0gaWZlbHNlKHZlcmtsYXJpbmc9PTAsIE5BLCB2ZXJrbGFyaW5nKSwNCiAgICAgICAgIGtlbm1lcmtlbiA9IGlmZWxzZShrZW5tZXJrZW49PTAsIE5BLCBrZW5tZXJrZW4pKQ0KIyBJZiB0aGVyZSBpcyBubyB0ZXh0IGluIHZlcmtsYXJpbmcgb3Iga2VubWVya2VuLCB0aGUgbmFtZSBjb3VsZCBub3QgYmUgZm91bmQgaW4gdGhlIGRhdGFiYXNlcy4gDQoNCmBgYA0KDQoNCiMjIEV4dHJhY3Rpbmcgc3BlY2lmaWMgb3JpZ2luIGluZm9ybWF0aW9uDQpUaGVyZSBhcmUgdGhyZWUgbWFpbiB3YXlzIHRvIGdldCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgb3JpZ2luIG9mIGxhc3QgbmFtZXM6DQoNCjEpIFVuZGVyICJrZW5tZXJrZW4iLCBsYXN0IG5hbWVzIGFyZSBhc3NpZ25lZCBjbGlja2FibGUgdGFncy4gVGhlc2UgdGFncyBpbmNsdWRlIHVuc3BlY2lmaWVkIGZvcmVpZ24gbmFtZSB0YWdzICgiYW5kZXJlIHRhYWwiPSJub24tRHV0Y2ggbGFuZ3VhZ2UiKSwgYXMgd2VsbCBhcyBzcGVjaWZpYyBmb3JlaWduIG9yaWdpbnMgb2YgdGhlIG5hbWUgKCJGcmFuc2UgbmFhbSI9IkZyZW5jaCBuYW1lIiwgIkluZGlzY2hlIG5hYW0iPSJJbmRvbmVzaWFuIG5hbWUiKS4NCi0+IG9yaWdpbjEgKyBvcmlnaW40DQoNCjIpIFNldmVyYWwgbmFtZXMgaGF2ZSBtb3JlIGV4dGVuc2l2ZWx5IHdyaXR0ZW4gb3V0IGluZm9ybWF0aW9uIGFib3V0IHRoZSBuYW1lLCB1bmRlciAidmVya2xhcmluZyIuIEEgbnVtYmVyIG9mIG5hbWVzIGNvbnRhaW4gZGV0YWlsZWQgKGVpdGhlciBjb3VudHJ5LWxldmVsIG9yIHJlZ2lvbmFsKSBvcmlnaW5zLCB1c3VhbGx5IGluIHRoZSBmb3JtIG9mICJEZSBuYWFtIFt4eXpdIGlzIGFma29tc3RpZyB1aXQgW2NvdW50cnldIiAoPSJUaGUgbmFtZSBbeHl6XSBvcmlnaW5hdGVzIGZyb20gW2NvdW50cnldIikuIA0KLT4gb3JpZ2luMiANCg0KMykgU29tZSBuYW1lcyBoYXZlIG9yaWdpbiBpbmZvcm1hdGlvbiB1bmRlciAidmVya2xhcmluZyIgaW4gdGhlIGZvcm0gb2YgdGhlIGxpbmd1aXN0aWMgb3JpZ2lucyBvZiB0aGUgbmFtZS4gVGhpcyBjYW4gYmUgY291bnRyeSBzcGVjaWZpYyAoZS5nLiBDaGluZXNlIG5hbWUpLCBidXQgaXQgY2FuIGFsc28gYXBwbHkgdG8gbXVsdGlwbGUgY291bnRyaWVzIHdoZW4gdGhlIGxhbmd1YWdlIGlzIHNwb2tlbiBpbiBtb3JlIHRoYW4gMSBjb3VudHJpZXMgKGUuZy4gU3BhbmlzaCBuYW1lKS4gDQotPiBvcmlnaW4zIA0KDQpgYGB7ciBjb3VudHJpZXMtZXh0cmFjdH0NCg0KIyBTdGVwIDE6IGV4dHJhY3Rpbmcgb3JpZ2luIHRhZ3MgZnJvbSBrZW5tZXJrZW4NCmxhc3RuYW1lX29yaWdpbiA8LSBsYXN0bmFtZV9vcmlnaW4gJT4lDQogIG11dGF0ZShvcmlnaW4xID0gc3RyX2V4dHJhY3Qoa2VubWVya2VuLCAiWzp1cHBlcjpdKFs6bG93ZXI6XXsyLH0pIG5hYW0iKSkNCg0KIyBOb3RlOiBzb21ldGltZXMgbXVsdGlwbGUgb3JpZ2lucyBhcmUgbWVudGlvbmVkLiBDdXJyZW50bHksIEkgb25seSBleHRyYWN0IHRoZSBmaXJzdCBvbmUuIE90aGVyd2lzZSwgd2Ugc2hvdWxkIHVzZSBzdHJfZXh0cmFjdF9hbGwuIA0KDQoNCg0KIyBTdGVwIDI6IGV4dHJhY3Rpbmcgb3JpZ2luIGluZm8gZnJvbSB2ZXJrbGFyaW5nIA0KbGFzdG5hbWVfb3JpZ2luIDwtIGxhc3RuYW1lX29yaWdpbiAlPiUNCiAgbXV0YXRlKG9yaWdpbjIgPSBpZmVsc2UoYXMubnVtZXJpYyhzdHJfZGV0ZWN0KHZlcmtsYXJpbmcsICJhZmtvbXN0aWcgdWl0IikpID09IDEsIA0KICAgICAgICAgc3RyX3JlbW92ZSh2ZXJrbGFyaW5nLCAiLiphZmtvbXN0aWcgdWl0IiksIE5BKSkNCg0KDQojIFN0ZXAgMzogZXh0cmFjdGluZyBhZGRpdGlvbmFsIG9yaWdpbiBpbmZvIGZyb20gdmVya2xhcmluZw0KbGFzdG5hbWVfb3JpZ2luIDwtIGxhc3RuYW1lX29yaWdpbiAlPiUNCiAgbXV0YXRlKG9yaWdpbjMgPSBzdHJfZXh0cmFjdCh2ZXJrbGFyaW5nLCAiWzp1cHBlcjpdKFs6bG93ZXI6XXsyLH0pIChhY2h0ZXIpPyhmYW1pbGllKT8oYmVyb2Vwcyk/bmFhbSIpKQ0KDQoNCg0KIyBGaW5hbGx5LCB3ZSBjbGVhbiB1cCB0aGUgb3JpZ2luIGluZm9ybWF0aW9uIGV4dHJhY3RlZCBhYm92ZQ0KDQojIE9yaWdpbjE6IGFscmVhZHkgbmVhdA0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjEgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMSwgIkpvb2RzZSBuYWFtIikgIyBjYW4gYmUgRHV0Y2ggJiBub24tRHV0Y2gNCg0KIyBPcmlnaW4yOiBtZXNzeQ0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjIgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMiwgIlxcLi4qIikgIyByZW1vdmUgZXh0cmEgaW5mbyBpbiB0aGUgZm9sbG93aW5nIHNlbnRlbmNlIA0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjIgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMiwgIlxcOy4qIikgIyByZW1vdmUgZXh0cmEgaW5mbyBpbiB0aGUgZm9sbG93aW5nIHNlbnRlbmNlIA0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjIgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMiwgIlxcKC4qIikgIyByZW1vdmUgZXh0cmEgaW5mbyBpbiB0aGUgZm9sbG93aW5nIHNlbnRlbmNlIA0KDQoNCmxhc3RuYW1lX29yaWdpbiRkcGcgPC0gYXMubnVtZXJpYyhzdHJfZGV0ZWN0KGxhc3RuYW1lX29yaWdpbiRvcmlnaW4yLCAiKGRvcnApfChwbGFhdHMpfChnZW1lZW50ZSl8KGdyYWFmc2NoYXApfChzdGFkKXwoZGVlbCl8KEZyaWVzbGFuZCkiKSkgIyBvcmlnaW4gaW5mbyB0b28gcmVnaW9uYWwgDQpsYXN0bmFtZV9vcmlnaW4gPC0gbGFzdG5hbWVfb3JpZ2luICU+JSBtdXRhdGUob3JpZ2luMiA9IGlmZWxzZSgoZHBnPT0xKSwgTkEsIG9yaWdpbjIpKSAjIHJlbW92aW5nIHJlZ2lvbmFsIG9yaWdpbiBpbmZvDQpsYXN0bmFtZV9vcmlnaW4gPC0gc3Vic2V0KGxhc3RuYW1lX29yaWdpbiwgc2VsZWN0ID0gLWRwZykgIyByZW1vdmluZyBpbnRlcm1lZGlhdGUgdmFyaWFibGUNCg0KIyBTb21ldGltZXMsIHRoZXJlIHdlcmUgbXVsdGlwbGUgY291bnRyaWVzIG1lbnRpb25lZC4gVGFrZSBvbmx5IHRoZSBmaXJzdDoNCmxhc3RuYW1lX29yaWdpbiRvcmlnaW4yIDwtIHN0cl9yZW1vdmUobGFzdG5hbWVfb3JpZ2luJG9yaWdpbjIsICJcXCwuKiIpICMgT25seSBmaXJzdA0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjIgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMiwgIlxccyhlbikuKiIpICMgT25seSBmaXJzdCANCmxhc3RuYW1lX29yaWdpbiRvcmlnaW4yIDwtIHN0cl9yZW1vdmUobGFzdG5hbWVfb3JpZ2luJG9yaWdpbjIsICJcXHMob2YpLioiKSAjIE9ubHkgZmlyc3QgDQoNCg0KIyBPcmlnaW4zOiBwcmV0dHkgbmVhdA0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjMgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMywgIkQoaSk/ZSh6ZSk/IChmYW1pbGllKT8oYWNodGVyKT8oYmVyb2Vwcyk/bmFhbSIpICMgc2xpcHBlZCB0aHJvdWdoIHRoZSByZWdleA0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjMgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMywgIkVlbiAoZmFtaWxpZSk/KGFjaHRlcik/KGJlcm9lcHMpP25hYW0iKSAjIHNsaXBwZWQgdGhyb3VnaCB0aGUgcmVnZXgNCmxhc3RuYW1lX29yaWdpbiRvcmlnaW4zIDwtIHN0cl9yZW1vdmUobGFzdG5hbWVfb3JpZ2luJG9yaWdpbjMsICJaaWpuIChmYW1pbGllKT8oYWNodGVyKT8oYmVyb2Vwcyk/bmFhbSIpICMgc2xpcHBlZCB0aHJvdWdoIHRoZSByZWdleA0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjMgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMywgIkFscyAoZmFtaWxpZSk/KGFjaHRlcik/KGJlcm9lcHMpP25hYW0iKSAjIHNsaXBwZWQgdGhyb3VnaCB0aGUgcmVnZXgNCmxhc3RuYW1lX29yaWdpbiRvcmlnaW4zIDwtIHN0cl9yZW1vdmUobGFzdG5hbWVfb3JpZ2luJG9yaWdpbjMsICJKb29kc2UgKGZhbWlsaWUpPyhhY2h0ZXIpP25hYW0iKQ0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjMgPC0gc3RyX3JlbW92ZShsYXN0bmFtZV9vcmlnaW4kb3JpZ2luMywgIkJpamJlbHNlIChmYW1pbGllKT8oYWNodGVyKT9uYWFtIikNCg0KDQojIFNldHRpbmcgZW1wdHkgb3JpZ2luIHZhcmlhYmxlcyB0byBOQSAoRHV0Y2ggb3IgdW5mb3VuZCBmb3JlaWduKQ0KbGFzdG5hbWVfb3JpZ2luIDwtIGxhc3RuYW1lX29yaWdpbiAlPiUNCiAgbXV0YXRlKG9yaWdpbjEgPSBhcy5jaGFyYWN0ZXIoaWZlbHNlKG9yaWdpbjE9PSIifG9yaWdpbjE9PSJjaGFyYWN0ZXIoMCkiLCBOQSwgb3JpZ2luMSkpLA0KICAgICAgICAgb3JpZ2luMiA9IGFzLmNoYXJhY3RlcihpZmVsc2Uob3JpZ2luMj09IiJ8b3JpZ2luMj09ImNoYXJhY3RlcigwKSIsIE5BLCBvcmlnaW4yKSksDQogICAgICAgICBvcmlnaW4zID0gYXMuY2hhcmFjdGVyKGlmZWxzZShvcmlnaW4zPT0iInxvcmlnaW4zPT0iY2hhcmFjdGVyKDApIiwgTkEsIG9yaWdpbjMpKSkNCg0KDQoNCiMgRmluYWxseSwgdGhlIHRhZyAiYW5kZXJlIHRhYWwiIHdhcyB1c2VkIHRvIGRpc3Rpbmd1aXNoIGZvcmVpZ24gbmFtZXMgb2YgdW5rbm93biBvcmlnaW4gZnJvbSBrbm93biBEdXRjaCBuYW1lcy4gDQpsYXN0bmFtZV9vcmlnaW4gPC0gbGFzdG5hbWVfb3JpZ2luICU+JQ0KICBtdXRhdGUob3JpZ2luNCA9IGlmZWxzZSgoYXMubnVtZXJpYyhzdHJfZGV0ZWN0KGtlbm1lcmtlbiwgImFuZGVyZSB0YWFsIikpPT0xKSwgIm5vbi1EdXRjaCIsIE5BKSkNCg0KDQpgYGANCg0KDQpUaGUgcmVzdWx0IGlzIGFzIGZvbGxvd3M6DQoNCmBgYHtyfQ0KDQpsYXN0bmFtZV9vcmlnaW5bLGMoMSw2LDcsOCw5KV0NCg0KYGBgDQoNCg0KDQoNCiMjIENvbWJpbmluZyBvcmlnaW4gaW5mbyBmcm9tIGRpZmZlcmVudCBzb3VyY2VzDQoNCk5vdywgd2UgcG90ZW50aWFsbHkgaGF2ZSBvcmlnaW4gaW5mb3JtYXRpb24gZnJvbSB0aHJlZSBkaWZmZXJlbnQgc291cmNlcy4gVGhlIGZpbmFsIG9yaWdpbiBkZWNpc2lvbiBpcyBiYXNlZCBvbiBob3cgZGVwZW5kYWJsZSBhbmQgaG93IGluZm9ybWF0aXZlIHRoZSBsYWJlbCBpcy4gVGhlIGhldXJpc3RpYyBpcyBhcyBmb2xsb3dzOg0KDQoxKSBQcmlvcml0eSBpcyBnaXZlbiB0byBvcmlnaW4gaW5mb3JtYXRpb24gZnJvbSBzcGVjaWZpYyBjb3VudHJ5IHRhZ3MgKGkuZS4gb3JpZ2luMSksIGJlY2F1c2UgdGhpcyBpbmZvcm1hdGlvbiBpcyB0aGUgbW9zdCBuZWF0LiBJZiBubyBjb3VudHJ5IHRhZyBpcyBwcm92aWRlZCB1bmRlciBvcmlnaW4xLCB3ZSBsb29rIGF0IHRoZSBnZW9ncmFwaGljYWwgb3JpZ2lucyBpbmRpY2F0ZWQgaW4gdGhlIHJ1bm5pbmcgdGV4dCAob3JpZ2luIDIpLiBJZiB0aGlzIGlzIGFsc28gbm90IHByZXNlbnQsIHdlIHRha2UgdGhlIGxpbmd1aXN0aWMgb3JpZ2lucyBvZiB0aGUgbmFtZSAob3JpZ2luIDMpIA0KDQoyKSBJZiB0aGVyZSBpcyBubyBzcGVjaWZpYyBjb3VudHJ5IGluZm9ybWF0aW9uLCBidXQgdGhlcmUgaXMgYSB0YWcgZm9yIGEgZm9yZWlnbiBuYW1lIChvcmlnaW40KSwgdGhlbiB0aGUgbmFtZSBpcyBsYWJlbGxlZCBhcyAidW5rbm93biBub24tRHV0Y2giLiANCg0KMykgSWYgdGhlcmUgaXMgbm8gc3BlY2lmaWMgY291bnRyeSBpbmZvcm1hdGlvbiBBTkQgbm8gdGFnIGZvciBmb3JlaWduIG5hbWUgaW4gb3JpZ2luNCwgYnV0IHRoZXJlIGlzIG90aGVyIGluZm9ybWF0aW9uIGFib3V0IHRoZSBuYW1lLCB3ZSBhc3N1bWUgdGhlIHBlcnNvbiBiZWFyaW5nIHRoaXMgbmFtZSBpcyBEdXRjaC4gDQoNCkluIHN1bTogb3JpZ2luMSA+IG9yaWdpbjIgPiBvcmlnaW4zID4gb3JpZ2luNC4gDQoNCmBgYHtyIGNvbWJpbmluZy1vcmlnaW4xMjN9DQoNCiMgT3JpZ2luIDE6IHNwZWNpZmljIGNvdW50cnkgdGFncyANCmxhc3RuYW1lX29yaWdpbiA8LSBsYXN0bmFtZV9vcmlnaW4gJT4lDQogIG11dGF0ZShvcmlnaW4gPSBvcmlnaW4xKSANCg0KIyBPcmlnaW4gMjogbm8gc3BlY2lmaWMgY291bnRyeSB0YWcsIHRha2UgdGhlIGdlb2dyYXBoaWNhbCBvcmlnaW4gb2YgdGhlIG5hbWUNCmxhc3RuYW1lX29yaWdpbiA8LSBsYXN0bmFtZV9vcmlnaW4gJT4lDQogIG11dGF0ZShvcmlnaW4gPSBpZmVsc2UoKGlzLm5hKG9yaWdpbjEpJiFpcy5uYShvcmlnaW40KSksIG9yaWdpbjIsIG9yaWdpbikpDQoNCiMgT3JpZ2luIDM6IG5laXRoZXIgb2YgdGhlIGFib3ZlLCB0YWtlIHRoZSBsaW5ndWlzdGljIG9yaWdpbiBvZiB0aGUgbmFtZQ0KbGFzdG5hbWVfb3JpZ2luIDwtIGxhc3RuYW1lX29yaWdpbiAlPiUNCiAgIG11dGF0ZShvcmlnaW4gPSBpZmVsc2UoKGlzLm5hKG9yaWdpbjEpJmlzLm5hKG9yaWdpbjIpJiFpcy5uYShvcmlnaW40KSksIG9yaWdpbjMsIG9yaWdpbikpDQoNCiMgT3JpZ2luIDQ6IHNldCB0byB1bmtub3duIG5vbi1EdXRjaCBpZiB0aGUgbmFtZSBpcyBsYWJlbGxlZCBhcyBzdWNoIHdpdGggbm8gc3BlY2lmaWMgb3JpZ2luIGluZm8NCmxhc3RuYW1lX29yaWdpbiA8LSBsYXN0bmFtZV9vcmlnaW4gJT4lDQogICAgbXV0YXRlKG9yaWdpbiA9IGlmZWxzZSgoaXMubmEob3JpZ2luMSkmaXMubmEob3JpZ2luMikmaXMubmEob3JpZ2luMykpLCBvcmlnaW40LCBvcmlnaW4pKQ0KDQojIEZpbmFsIHN0ZXA6IGlmIHRoZXJlIGlzIG5vIG9yaWdpbiBpbmZvcm1hdGlvbiBwcmVzZW50LCBidXQgb3RoZXIgYmFja2dyb3VuZCBpbmZvcm1hdGlvbiBjb3VsZCBiZSBmb3VuZCwgdGhlIG5hbWUgaXMgbGlrZWx5IER1dGNoLg0KbGFzdG5hbWVfb3JpZ2luIDwtIGxhc3RuYW1lX29yaWdpbiAlPiUNCiAgbXV0YXRlKG9yaWdpbiA9IGlmZWxzZSgoaXMubmEob3JpZ2luKSZub19pbmZvPT0wKSwgImR1dGNoIiwgb3JpZ2luKSkNCg0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbiA8LSB0cmltd3MobGFzdG5hbWVfb3JpZ2luJG9yaWdpbiwgd2hpY2ggPSAiYm90aCIpDQoNCmBgYA0KDQoNCg0KVG8gY29uc3RydWN0IHRoZSBldGhuaWNpdHkgdmFyaWFibGUsIHdlIGRpc3Rpbmd1aXNoIGZpdmUgZGlmZmVyZW50IGJhY2tncm91bmRzOg0KMS4gRXRobmljIG1ham9yaXR5IG1lbWJlcnMgKGkuZS4gdGhvc2Ugd2l0aCBEdXRjaCBuYW1lcykNCjIuIFR1cmtpc2gtRHV0Y2ggaW5kaXZpZHVhbHMNCjMuIE1vcm9jY2FuLUR1dGNoIGluZGl2aWR1YWxzDQo0LiBDYXJpYmJlYW4tRHV0Y2ggaW5kaXZpZHVhbHMNCjUuIFRob3NlIHdpdGggYSBkaWZmZXJlbnQgZXRobmljaXR5IGZyb20gdGhvc2UgbGlzdGVkIGFib3ZlDQoNCg0KYGBge3Igb3JpZ2luNWNhdH0NCg0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjEgPC0gbGFzdG5hbWVfb3JpZ2luJG9yaWdpbg0KDQpsYXN0bmFtZV9vcmlnaW4gPC0gc3Vic2V0KGxhc3RuYW1lX29yaWdpbiwgc2VsZWN0ID0gYyhsYXN0bmFtZSwgb3JpZ2luMSkpDQoNCg0KIyB3ZSBkZWZpbmUgZGlmZmVyZW50IHdheXMgaW4gd2hpY2ggZXRobmljIG1pbm9yaXR5IG5hbWVzIGFyZSBpZGVudGlmaWVkIGluIHRoZSAiRmFtaWxpZW5hbWVuYmFuayINCmxhc3RuYW1lX29yaWdpbiA8LSBsYXN0bmFtZV9vcmlnaW4gJT4lDQogIG11dGF0ZShvcmlnaW4xID0gaWZlbHNlKGFzLm51bWVyaWMoc3RyX2RldGVjdChvcmlnaW4xLCBwYXN0ZSgiKFR1cmtpamUpfChUdXJrc2UgbmFhbSkiKSkpLCAidHVya2lzaCIsIG9yaWdpbjEpKQ0KDQpsYXN0bmFtZV9vcmlnaW4gPC0gbGFzdG5hbWVfb3JpZ2luICU+JQ0KICBtdXRhdGUob3JpZ2luMSA9IGlmZWxzZShhcy5udW1lcmljKHN0cl9kZXRlY3Qob3JpZ2luMSwgcGFzdGUoIihNYXJva2thYW5zZSBuYWFtKXwoTWFyb2trbyl8KGRlIE1hcm9ra28pIikpKSwgIm1vcm9jY2FuIiwgb3JpZ2luMSkpDQoNCmxhc3RuYW1lX29yaWdpbiA8LSBsYXN0bmFtZV9vcmlnaW4gJT4lDQogIG11dGF0ZShvcmlnaW4xID0gaWZlbHNlKGFzLm51bWVyaWMoc3RyX2RldGVjdChvcmlnaW4xLCBwYXN0ZSgiKGRlIE5lZGVybGFuZHNlIEFudGlsbGVuKXwoU3VyaW5hYW1zZSBuYWFtKXwoU3VyaW5hbWUpIikpKSwgImNhcnJpYmVhbiIsIG9yaWdpbjEpKQ0KDQpsYXN0bmFtZV9vcmlnaW4gPC0gbGFzdG5hbWVfb3JpZ2luICU+JQ0KICBtdXRhdGUob3JpZ2luMSA9IGlmZWxzZSgoIWlzLm5hKG9yaWdpbjEpJmFzLm51bWVyaWMoc3RyX2RldGVjdChvcmlnaW4xLCAidHVya2lzaHxtb3JvY2NhbnxjYXJyaWJlYW58ZHV0Y2giKSk9PTApLCAib3RoZXIiLCBvcmlnaW4xKSkNCg0KbGFzdG5hbWVfb3JpZ2luJG9yaWdpbjEgPC0gYXMuZmFjdG9yKGxhc3RuYW1lX29yaWdpbiRvcmlnaW4xKQ0KICAgICAgDQoNCiMgd2UgY3JlYXRlIGFuIGV4cGxpY2l0ICdtaXNzaW5nJyBjYXRlZ29yeSBmb3IgdGhvc2Ugd2hvIGFyZSBub3QgY2F0ZWdvcml6ZWQgdW5kZXIgZXRobmljIG1ham9yaXR5LCBtaW5vcml0eSBvciAnb3RoZXIgbGFzdG5hbWVfb3JpZ2luJw0KbGFzdG5hbWVfb3JpZ2luIDwtIGxhc3RuYW1lX29yaWdpbiAlPiUNCiAgbXV0YXRlKG9yaWdpbjEgPSBmY3RfZXhwbGljaXRfbmEob3JpZ2luMSwgbmFfbGV2ZWw9Im1pc3NpbmciKSkNCg0KDQojIHRodXMsIHdlIGdldCB0aGUgZm9sbG93aW5nIGRhdGFmcmFtZToNCmxhc3RuYW1lX29yaWdpbg0KDQpgYGANCg0KDQpXZSB0YWtlIHRoZSBsYXN0IG5hbWUgaW5mb3JtYXRpb24gZnJvbSBvcmlnaW4xIGFzIHRoZSBiYXNpcyBmb3Igb3VyIGV0aG5pY2l0eSB2YXJpYWJsZS4gSGVuY2UsIHdlIG1ha2UgYSBuZXcgZGF0YSBmcmFtZSwgaW4gd2hpY2ggd2UgY29weSB0aGUgaW5mb3JtYXRpb24gZnJvbSBvcmlnaW4xIHRvIGEgbmV3ICdldGhuaWNpdHknIG9iamVjdC4gTmV4dCwgd2UgZW5yaWNoIHRoaXMgZXRobmljaXR5IHZhcmlhYmxlIHVzaW5nIGluZm9ybWF0aW9uIGZyb20gZmlyc3QgbmFtZXMgYW5kIGJpcnRoIHBsYWNlcyBsaXN0ZWQgaW4gZGlzc2VydGF0aW9uIFBERnMuIA0KDQpgYGAge3J9DQoNCnBoZGV0aG5pY2l0eSA8LSBsYXN0bmFtZV9vcmlnaW4NCg0KcGhkZXRobmljaXR5JGV0aG5pY2l0eSA8LSBwaGRldGhuaWNpdHkkb3JpZ2luMQ0KDQpgYGANCg0KDQoNCg0KDQoNCiMgTWV0aG9kIDI6IE1vcm9jY2FuIG5hbWUgbGlzdA0KDQoNCkluIGFkZGl0aW9uIHRvIGRldGVybWluaW5nIGV0aG5pY2l0eSBiYXNlZCBvbiBsYXN0IG5hbWVzLCB3ZSBsb29rIGF0IGZpcnN0IG5hbWVzLiBTcGVjaWZpY2FsbHksIHdlIHVzZSBhIGxpc3Qgb2YgTW9yb2NjYW4gZmlyc3QgbmFtZXMgdG8gZGV0ZWN0IHdoZXRoZXIgYW4gaW5kaXZpZHVhbCBpcyBsaWtlbHkgb2YgTW9yb2NjYW4gZGVzY2VudC4gVW50aWxsIHZlcnkgcmVjZW50bHksIGZpcnN0IG5hbWVzIG9mIE1vcm9jY2FuIGluZGl2aWR1YWxzIHdlcmUgcmVndWxhdGVkIGJ5IE1vcm9jY2FuIGxhdyBbQGhhc2tvdXJpXS4gVGhpcyBpbXBsaWVzIHRoYXQgbWFueSBNb3JvY2NhbiBpbmRpdmlkdWFscyBjaG9vc2UgbmFtZXMgZm9yIHRoZWlyIGNoaWxkcmVuIGZyb20gYSBsaXN0IG9mIGFjY2VwdGVkIGdpcmxzIGFuZCBib3lzIG5hbWVzLiBXZSBsb2FkIGluIGEgbGlzdCBvZiBuYW1lcyBieSBAYE1vcm9jY28gR3VpZGVgIGFzIGFuIGFkZGl0aW9uYWwgaW5kaWNhdG9yIHRoYXQgYSBQaEQgaXMgTW9yb2NjYW4gYW5kIGFkZCB0aGlzIHRvIG91ciBleGFtcGxlIGRhdGEgZnJhbWUuIA0KDQoNCmBgYHtyfQ0KDQojIEFkZGluZyBmaXJzdCBuYW1lcyBiYWNrIGluDQpwaGRldGhuaWNpdHkgPC0gbGVmdF9qb2luKHBoZG5hbWVzLCBwaGRldGhuaWNpdHksIGJ5ID0gImxhc3RuYW1lIikNCg0KDQojIEFkZCB0aGUgbW9yb2NjYW4gbmFtZXMgdG8gdGhlIGV0aG5pY2l0eSBkYXRhDQpsb2FkKGZpbGUgPSAiZGF0YS9wcm9jZXNzZWQvbW9yb2NjYW5uYW1lcy5yZGEiKQ0KDQoNCnBoZGV0aG5pY2l0eSA8LSBsZWZ0X2pvaW4ocGhkZXRobmljaXR5LCBtb3JvY2Nhbm5hbWVzWyxjKDEsMyldLCBieSA9ICJmaXJzdG5hbWUiKQ0KDQoNCiMgQWRkIGV4cGxpY2l0IG1pc3NpbmcgY2F0ZWdvcnkgaW4gb3JpZ2luMg0KcGhkZXRobmljaXR5IDwtIHBoZGV0aG5pY2l0eSAlPiUNCiAgbXV0YXRlKG9yaWdpbjIgPSBmY3RfZXhwbGljaXRfbmEob3JpZ2luMiwgbmFfbGV2ZWw9Im1pc3NpbmciKSkNCg0KIyBXZSBvdmVyd3JpdGUgZXRobmljaXR5IHRvIG1pbm9yaXR5IGlmIHRoZSBmaXJzdCBuYW1lIGlzIE1vcm9jY2FuDQpmb3IgKGkgaW4gMTpucm93KHBoZGV0aG5pY2l0eSkpIHsNCiAgaWYgKHBoZGV0aG5pY2l0eSRvcmlnaW4xW2ldPT0ibWlzc2luZyIgJiBwaGRldGhuaWNpdHkkb3JpZ2luMltpXT09Im1vcm9jY2FuIikgew0KICAgIHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV0gPC0gIm1vcm9jY2FuIg0KICB9IGVsc2UgaWYgKHBoZGV0aG5pY2l0eSRvcmlnaW4xW2ldPT0iZHV0Y2giICYgcGhkZXRobmljaXR5JG9yaWdpbjJbaV09PSJtb3JvY2NhbiIpIHsNCiAgICBwaGRldGhuaWNpdHkkZXRobmljaXR5W2ldIDwtICJtb3JvY2NhbiINCiAgfSBlbHNlIGlmIChwaGRldGhuaWNpdHkkb3JpZ2luMVtpXT09Im90aGVyIiAmIHBoZGV0aG5pY2l0eSRvcmlnaW4yW2ldPT0ibW9yb2NjYW4iKSB7DQogICAgcGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXSA8LSAibW9yb2NjYW4iDQogIH0gDQp9DQoNCmBgYA0KDQoNCkxldCdzIHNlZSB3aGF0IHRoZSBkYXRhIGxvb2tzIGxpa2UuIEluIHRoaXMgY2FzZSwgd2Ugc2VlIHRoYXQgdGhlIHR3byBtZXRob2RzIGZvciBkZXRlcm1pbmluZyBldGhuaWNpdHkgKGF0IGxlYXN0IHdpdGggcmVnYXJkcyB0byBNb3JvY2NhbiB2cyBub24tTW9yb2NjYW4pIGFwcGVhciB0byBiZSBpbiBhZ3JlZW1lbnQuIA0KDQpgYGB7cn0NCg0KcGhkZXRobmljaXR5WyxjKDEsMiw1LDksMTEsNildDQoNCmBgYA0KDQoNCg0KIyBNZXRob2QgMzogUERGIGJpcnRoIHBsYWNlcw0KDQpCYXNlZCBvbiBsaXN0cyBvZiBEdXRjaCBtdW5pY2lwYWxpdGllcywgd2UgY2FuIGVzdGFibGlzaCB3aGV0aGVyIHNvbWVvbmUgd2FzIGJvcm4gaW4gdGhlIE5ldGhlcmxhbmRzLiBXZSB1c2UgbGlzdHMgb2YgbXVuaWNpcGFsaXRpZXMgcHJvdmlkZWQgYnkgU3RhdGlzdGljcyBOZXRoZXJsYW5kcyBmcm9tIHRoZSB5ZWFycyAyMDA2LzIwMTQvMjAxNS8yMDE2LzIwMTgvMjAxOS8yMDIwLzIwMjEsIGFzIHdlbGwgYXMgbXV0YXRpb25zIGluIG11bmljaXBhbGl0aWVzIG9yIG11bmljaXBhbGl0eSBuYW1lcyBkdXJpbmcgdGhlIHBlcmlvZCBvZiB0aGlzIHN0dWR5IFtAU3RhdGlzdGljcyBOZXRoZXJsYW5kc10uDQoNCklmIHRoZSBiaXJ0aCBwbGFjZSBpbmZvIGNvbnRhaW5zIGEgRHV0Y2ggbXVuaWNpcGFsaXR5IG9yIGEgcmVmZXJlbmNlIHRvIHRoZSBOZXRoZXJsYW5kcywgYSBwZXJzb24gaXMgYm9ybiBpbiB0aGUgTmV0aGVybGFuZHMuIElmIHRoZXJlIGlzIGEgcmVmZXJlbmNlIHRvIFR1cmtleSwgTW9yb2NjbywgdGhlDQoNCmBgYHtyfQ0KDQojIGxvYWRpbmcgaW4gdGhlIGxpc3Qgb2YgRHV0Y2ggbXVuaWNpcGFsaXRpZXMNCmxvYWQoZmlsZSA9ICJkYXRhL211bmljaXBhbGl0aWVzL2R1dGNoX211bl9hbGwucmRhIikNCg0KIyBjcmVhdGluZyBzdHJpbmdzIHdpdGggaW5kaWNhdG9ycyBmb3IgYmlydGggY291bnRyaWVzIChUaGUgTmV0aGVybGFuZHMsIENhcmliYmVhbiBOZXRoZXJsYW5kcywgVHVya2V5LCBNb3JvY2NvKQ0KZHV0Y2ggPC0gYyhhcy5jaGFyYWN0ZXIoZHV0Y2hfbXVuJG11bmljaXBhbGl0eSksICJuZXRoZXJsYW5kcyIsICJ0aGUgbmV0aGVybGFuZHMiLCAibmVkZXJsYW5kIiwiXFxzbmxcXHMiKQ0KDQpjYXJyaWJlYW4gPC0gYygic3VyaW5hbWUiLCAibmVkZXJsYW5kc2UgYW50aWxsZW4iLCAiY3VyYWNhbyIsICJjdXJhw6dhbyIsICJib25haXJlIiwgIlxcc3NzYWJhXFxzIiwgInNpbnQgZXVzdGF0aXVzIiwgInNpbnQgbWFhcnRlbiIsICJhcnViYSIpDQoNCnR1cmtpc2ggPC0gYygidHVya2lqZSIsICJ0dXJrZXkiKQ0KDQptb3JvY2NhbiA8LSBjKCJtYXJva2tvIiwgIm1vcm9jY28iKQ0KDQoNCg0KIyBPcmlnaW4zOiBjb3VudHJ5IG9mIGJpcnRoIHZhcmlhYmxlDQpwaGRldGhuaWNpdHkkb3JpZ2luMyA8LSBpZmVsc2UoYXMubnVtZXJpYyhzdHJfZGV0ZWN0KHBoZGV0aG5pY2l0eSRkaXNzX2JpcnRocGxhY2UsIHBhc3RlKGR1dGNoLCBjb2xsYXBzZSA9ICJ8IikpKT09MSwgImR1dGNoIiwgTkEpDQoNCnBoZGV0aG5pY2l0eSRvcmlnaW4zIDwtIGlmZWxzZShhcy5udW1lcmljKHN0cl9kZXRlY3QocGhkZXRobmljaXR5JGRpc3NfYmlydGhwbGFjZSwgcGFzdGUoY2FycmliZWFuLCBjb2xsYXBzZSA9ICJ8IikpKT09MSwgImNhcmliYmVhbiIsIHBoZGV0aG5pY2l0eSRvcmlnaW4zKQ0KDQpwaGRldGhuaWNpdHkkb3JpZ2luMyA8LSBpZmVsc2UoYXMubnVtZXJpYyhzdHJfZGV0ZWN0KHBoZGV0aG5pY2l0eSRkaXNzX2JpcnRocGxhY2UsIHBhc3RlKHR1cmtpc2gsIGNvbGxhcHNlID0gInwiKSkpPT0xLCAidHVya2lzaCIsIHBoZGV0aG5pY2l0eSRvcmlnaW4zKQ0KDQpwaGRldGhuaWNpdHkkb3JpZ2luMyA8LSBpZmVsc2UoYXMubnVtZXJpYyhzdHJfZGV0ZWN0KHBoZGV0aG5pY2l0eSRkaXNzX2JpcnRocGxhY2UsIHBhc3RlKG1vcm9jY2FuLCBjb2xsYXBzZSA9ICJ8IikpKT09MSwgIm1vcm9jY2FuIiwgcGhkZXRobmljaXR5JG9yaWdpbjMpDQoNCg0KcGhkZXRobmljaXR5JG9yaWdpbjMgPC0gZmFjdG9yKHBoZGV0aG5pY2l0eSRvcmlnaW4zLCBsZXZlbHM9YygiZHV0Y2giLCAiY2FyaWJiZWFuIiwgInR1cmtpc2giLCAibW9yb2NjYW4iKSkNCg0KIyBDcmVhdGUgZXhwbGljaXQgbWlzc2luZyBjYXRlZ29yeQ0KcGhkZXRobmljaXR5IDwtIHBoZGV0aG5pY2l0eSAlPiUNCiAgbXV0YXRlKG9yaWdpbjMgPSBmY3RfZXhwbGljaXRfbmEob3JpZ2luMywgbmFfbGV2ZWw9Im1pc3NpbmciKSkNCg0KDQojIFdlIHJlcGxhY2UgbWlzc2luZyBvciBvdGhlciB3aXRoICJkdXRjaCIgaWYgYSBwZXJzb24gaXMgYm9ybiBpbiBOTA0KIyBXZSByZXBsYWNlIG1pc3NpbmcsIG90aGVyLCBkdXRjaCBhbmQgbW9yb2NjYW4gd2l0aCAidHVya2lzaCIgaWYgYSBwZXJzb24gaXMgYm9ybiBpbiBUdXJrZXkNCiMgV2UgcmVwbGFjZSBtaXNzaW5nLCBvdGhlciwgZHV0Y2ggYW5kIHR1cmtpc2ggd2l0aCAibW9yb2NjYW4iIGlmIGEgcGVyc29uIGlzIGJvcm4gaW4gTW9yb2Njbw0KDQpmb3IgKGkgaW4gMTpucm93KHBoZGV0aG5pY2l0eSkpIHsNCiAgaWYgKHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV09PSJtaXNzaW5nIiAmIHBoZGV0aG5pY2l0eSRvcmlnaW4zW2ldPT0iZHV0Y2giKSB7DQogICAgcGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXSA8LSAiZHV0Y2giDQogIH0gZWxzZSBpZiAocGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXT09Im90aGVyIiAmIHBoZGV0aG5pY2l0eSRvcmlnaW4zW2ldPT0iZHV0Y2giKSB7DQogICAgcGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXSA8LSAiZHV0Y2giDQogICAgfSBlbHNlIGlmIChwaGRldGhuaWNpdHkkZXRobmljaXR5W2ldPT0ibWlzc2luZyIgJiBwaGRldGhuaWNpdHkkb3JpZ2luM1tpXT09Im1vcm9jY2FuIikgew0KICAgIHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV0gPC0gIm1vcm9jY2FuIg0KICB9IGVsc2UgaWYgKHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV09PSJkdXRjaCIgJiBwaGRldGhuaWNpdHkkb3JpZ2luM1tpXT09Im1vcm9jY2FuIikgew0KICAgIHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV0gPC0gIm1vcm9jY2FuIg0KICB9IGVsc2UgaWYgKHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV09PSJvdGhlciIgJiBwaGRldGhuaWNpdHkkb3JpZ2luM1tpXT09Im1vcm9jY2FuIikgew0KICAgIHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV0gPC0gIm1vcm9jY2FuIg0KICB9IGVsc2UgaWYgKHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV09PSJ0dXJraXNoIiAmIHBoZGV0aG5pY2l0eSRvcmlnaW4zW2ldPT0ibW9yb2NjYW4iKSB7DQogICAgcGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXSA8LSAibW9yb2NjYW4iDQogIH0gZWxzZSBpZiAocGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXT09Im1pc3NpbmciICYgcGhkZXRobmljaXR5JG9yaWdpbjNbaV09PSJ0dXJraXNoIikgew0KICAgIHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV0gPC0gInR1cmtpc2giDQogIH0gZWxzZSBpZiAocGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXT09ImR1dGNoIiAmIHBoZGV0aG5pY2l0eSRvcmlnaW4zW2ldPT0idHVya2lzaCIpIHsNCiAgICBwaGRldGhuaWNpdHkkZXRobmljaXR5W2ldIDwtICJ0dXJraXNoIg0KICB9IGVsc2UgaWYgKHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV09PSJvdGhlciIgJiBwaGRldGhuaWNpdHkkb3JpZ2luM1tpXT09InR1cmtpc2giKSB7DQogICAgcGhkZXRobmljaXR5JGV0aG5pY2l0eVtpXSA8LSAidHVya2lzaCINCiAgfSBlbHNlIGlmIChwaGRldGhuaWNpdHkkZXRobmljaXR5W2ldPT0ibW9yb2NjYW4iICYgcGhkZXRobmljaXR5JG9yaWdpbjNbaV09PSJ0dXJraXNoIikgew0KICAgIHBoZGV0aG5pY2l0eSRldGhuaWNpdHlbaV0gPC0gInR1cmtpc2giDQogIH0gDQp9DQoNCmBgYA0KDQoNCg0KQWZ0ZXIgYWxsIHRoZXNlIG9wZXJhdGlvbnMsIHdlIGFyZSBsZWZ0IHdpdGggYW4gZXRobmljaXR5IG9iamVjdCB3aGljaCBpcyBiYXNlZCBvbiBvcmlnaW5zIG9mIGxhc3QgbmFtZXMgYW5kIGZpcnN0IG5hbWVzLCBhbmQgb24gdGhlIGJpcnRoIHBsYWNlIGxpc3RlZCBpbiB0aGUgZGlzc2VydGF0aW9uLiANCg0KRm9yIHRoZXNlIGV4YW1wbGUgY2FzZXMsIHRoZSBsYWJlbHMgZ2l2ZW4gYmFzZWQgb24gdGhlc2UgZGlmZmVyZW50IGluZGljYXRvcnMgb3ZlcmxhcCBwZXJmZWN0bHksIGJ1dCB0aGUgY29kZSBhYm92ZSBnaXZlcyBpbnNpZ2h0IGluIHRoZSBoZXVyaXN0aWNzIHdlIHVzZSB0byBjb21iaW5lIHBvdGVudGlhbGx5IGNvbmZsaWN0aW5nIGV0aG5pY2l0eSBsYWJlbHMuIA0KDQpgYGB7cn0NCg0KcGhkZXRobmljaXR5WyxjKDIsNSw2LDEwLDksMTEsMTIpXQ0KDQpgYGANCg0KDQoNCiMgRXRobmljaXR5IDINCg0KQWx0aG91Z2ggd2UgbmVlZCB0aGUgc3BlY2lmaWMgZXRobmljaXR5IGxhYmVscyBmb3Igb3VyIG1lYXN1cmUgb2YgZ2VuZGVyLCB3ZSB1c2UgYSBsZXNzLWRldGFpbGVkIG1lYXN1cmUgb2YgZXRobmljIGJhY2tncm91bmQgZm9yIG91ciBhbmFseXNlcy4gSW4gdGhpcyBtZWFzdXJlIHdlIGRpc3Rpbmd1aXNoIGJldHdlZW4gaW5kaXZpZHVhbHMgd2l0aCBhbiBldGhuaWMgbWFqb3JpdHksIGV0aG5pYyBtaW5vcml0eSAgKFR1cmtpc2gtRHV0Y2gsIE1vcm9jY2FuLUR1dGNoLCBDYXJpYmJlYW4tRHV0Y2gpIG9yIG90aGVyIGV0aG5pYyBiYWNrZ3JvdW5kLiBUaGlzIGhhcyB0byBkbyB3aXRoIHNtYWxsIGdyb3VwIHNpemVzIGZvciBlYWNoIG9mIHRoZSBzcGVjaWZpYyBldGhuaWMgbWlub3JpdHkgYmFja2dyb3VuZHMuIA0KDQpXZSBzdWJzdW1lICJtaXNzaW5nIiBldGhuaWMgYmFja2dyb3VuZCB1bmRlciBvdGhlciBldGhuaWMgYmFja2dyb3VuZCwgYmVjYXVzZSB3ZSBiZWxpZXZlIHRoYXQgaXQgaXMgdW5saWtlbHkgdGhhdCBhIHBlcnNvbiB3aXRoIGEgRHV0Y2ggbWFqb3JpdHksIFR1cmtpc2gtRHV0Y2gsIE1vcm9jY2FuLUR1dGNoIG9yIENhcmliYmVhbi1EdXRjaCBiYWNrZ3JvdW5kIHdvdWxkIGhhdmUgZ29uZSB1bm5vdGljZWQgdXNpbmcgb3VyIGFycmF5IG9mIG1ldGhvZHMgdG8gZGV0ZXJtaW5lIGV0aG5pY2l0eS4gDQoNCmBgYHtyfQ0KDQpwaGRldGhuaWNpdHkkZXRobmljaXR5MiA8LSBwaGRldGhuaWNpdHkkZXRobmljaXR5DQoNCnBoZGV0aG5pY2l0eSRldGhuaWNpdHkyIDwtIGZjdF9jb2xsYXBzZShwaGRldGhuaWNpdHkkZXRobmljaXR5MiwNCiAgICAgICAgICAgICBtYWpvcml0eSA9ICJkdXRjaCIsDQogICAgICAgICAgICAgbWlub3JpdHkgPSBjKCJtb3JvY2NhbiIsICJ0dXJraXNoIiwgImNhcnJpYmVhbiIpLA0KICAgICAgICAgICAgIG90aGVyID0gYygib3RoZXIiLCAibWlzc2luZyIpKQ0KDQoNCg0KcGhkZXRobmljaXR5IDwtIHN1YnNldChwaGRldGhuaWNpdHksIHNlbGVjdD1jKGlkLCBmaXJzdG5hbWUsIG5wLCBsYXN0bmFtZSwgbGFzdG5hbWVfZnVsbCwgZGlzc19iaXJ0aHBsYWNlLCB1bmksIHBoZF95ZWFyLCBldGhuaWNpdHksIGV0aG5pY2l0eTIpKQ0KDQpgYGANCg0KDQoNCi0tLSANCg0KIyBPdXRwdXQNCg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCg0KDQpwaGRldGhuaWNpdHkNCg0KYGBgDQoNCg0KYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9DQoNCnNhdmUocGhkZXRobmljaXR5LCBmaWxlID0gIi4vZGF0YS9wcm9jZXNzZWQvcGhkZXRobmljaXR5LnJkYSIpDQoNCmBgYA0KDQoNCg0KLS0tICANCg0KIyBSZWZlcmVuY2VzDQoNCg0K
Copyright © 2023