Analysis
1. Overlap between the three queries?
What’s the extent of overlap between the blastp hits using the three
different queries?
Hits with <50% query coverage are removed.
refseq %>% filter(qcovs < 50)
Venn diagram showing the extent of overlap and set specific hits:
set.ovlp <- refseq %>%
filter(qcovs >= 50) %>%
group_by(sid) %>%
summarize(
Caur = any(query == "Caur_Hil1"),
Calb = any(query == "Calb_Hyr1"),
Cgla = any(query == "Cgla_Hyr1")
)
ggvenn(set.ovlp)

Which genes are found by the C. albicans and/or C.
glabrata Hyr1 query blast but not by the C. auris Hil1
query blast?
Calb.not.Caur.hits <- set.ovlp %>% filter(!Caur) %>% pull(sid)
refseq %>% filter(sid %in% Calb.not.Caur.hits) %>%
group_by(sid) %>% filter(evalue == min(evalue)) %>% ungroup() %>%
select(query, qcovs, sid, slen, pident, evalue, species) %>%
mutate(species = paste(str_sub(species, 1, 1), word(species, 2, 2), sep = ". ")) %>%
arrange(species)
- The majority of the combined set is already in the subset identified
using C. auris Hil1 as the query.
- Of the ones that are specific to the C. albicans Hyr1
query, I found the three C. parasilosis hits are already
included in the 104 homologs via the FungiDB blast. Whatever the reason
that exluded them from the ncbi blastp search using C. auris
Hil1 domain as the query, they are already in our dataset.
- Same is true for XP_452176.1 from K. lactis, this time
through the GRYC search.
Among the species included previously, how many of the “new”
sequences were not part of the 104 homologs list?
| XP_002999585 |
CAGL0L00227g |
C. glabrata |
Yes |
GRYC |
| XP_002615218 |
CLUG_05233 |
C. lusitaniae |
Yes |
FungiDB |
| XP_002620045 |
CLUG_01204 |
C. lusitaniae |
No^ |
NA |
| XP_002614121 |
CLUG_05607 |
C. lusitaniae |
No |
NA |
| XP_036665262 |
CPAR2_806390 |
C. parapsilosis |
Yes |
FungiDB |
| XP_036662815 |
CPAR2_600430 |
C. parapsilosis |
Yes |
FungiDB |
| XP_036665263 |
CPAR2_806400 |
C. parapsilosis |
No |
NA |
| XP_452176 |
KLLA0_B14498g |
K. lactis |
Yes |
GRYC |
| XP_001527307 |
LELG_02136 |
L. elongisporus |
No |
NA |
| XP_001385683 |
PICST_3402 |
S. stipitis |
No^ |
NA |
| XP_001383953 |
PICST_31095 |
S. stipitis |
Yes* |
NCBI |
*this hit was originally included when the N560 aa was used as query.
In later analysis where N360 aa (of C. auris Hil1) was used as
query, this hit dropped off the list due to E-value being above the 1e-5
cutoff. So it is a legit, but borderline hit.
^shorter than 500 a.a.
Five hits are new, among which two are shorter than 500 a.a. and
would have been excluded from the list (although they were not
identified at all with the C. auris query).
2. Species to include
The new criteria is whether the species is among the 332 included in
the large-scale phylogeny of Shen et al. 2018. I downloaded the
tree from https://github.com/JLSteenwyk/treehouse/blob/master/Data/Saccharomycotina_fig2_Shen_etal_2018.tre
and manually converted it into a list of species names.
shen2018 <- scan("../../data/yeast-phylogeny/Saccharomycotina_fig2_Shen_etal_2018.txt", what = "character", sep = "\n")
Any species in the refseq hit list not in the Shen 2018
phylogeny?
setdiff(unique(refseq$species), shen2018)
[1] "Candida haemuloni" "Candida duobushaemulonis" "Candida pseudohaemulonii"
[4] "Suhomyces tanzawaensis" "Pseudomonas syringae;Pseudomonas" "Diutina rugosa"
[7] "Yamadazyma tenuis" "Schizosaccharomyces cryophilus" "Kazachstania barnettii"
[10] "Artibeus jamaicensis"
| Candida haemuloni |
45357 |
No |
NA |
| Candida duobushaemulonis |
1231522 |
No |
NA |
| Candida pseudohaemulonii |
418784 |
No |
NA |
| Suhomyces tanzawaensis |
984487 |
Yes |
Candida tanzawaensis |
| Pseudomonas syringae:Pseudomonas |
317;59510 |
No |
NA |
| Diutina rugosa |
5481 |
No |
NA |
| Yamadazyma tenuis |
590646 |
Yes |
Candida tenuis |
| Schizosaccharomyces cryophilus |
653667 |
No |
NA |
| Kazachstania barnettii |
61262 |
No |
NA |
| Artibeus jamaicensis |
9417 |
No |
NA |
Manually figure out species synonyms and record both names for the
species to use. update: downloaded the species name
convertion table from Shen et al 2018 supplementary material.
spsNameConvert <- read_tsv("../../data/yeast-phylogeny/343taxa_speicies-name_clade-name_color-code.txt", col_types = cols()) %>%
mutate(species = word(`Species name`, 1, 2))
spsNameSyn <- tibble(
ncbi.name = c(intersect(refseq$species, shen2018), "Suhomyces tanzawaensis", "Yamadazyma tenuis", "Lachancea kluyveri"),
shen.name = c(intersect(refseq$species, shen2018), "Candida tanzawaensis", "Candida tenuis", "Lachancea kluyveri")
)
3. Check protein completeness
Our goal is to create a table documenting the number of putative
homologs per species. We used to apply a length cutoff of 500 aa.
However, the specific value of the cutoff is not based on any
comprehensive studies. This combined with the uncertainty in some
putative homolog’s length due to assembly issues led us to reconsider
this filter. The solution we came up with is to include all blast hits
based on the PF11765 domain alone in the family expansion analysis (CAFE
and phylogenetic reconstruction). Later we will selectively include some
of them for the protein feature analysis. Since the latter analysis is
mainly to demonstrate the fast evolution of the central domain features,
it doesn’t rely on the completeness of the Hil family or the inclusion
of all species for which we have records for.
In curating the blast hits, we found that some (protein) blast hits
are annotated as missing the carboxy terminus. This would prevent us
from properly assessing the length of the protein. Note that
“incomplete” means different things for the protein vs mRNA record. For
the latter, “incomplete” could mean missing 5’ and/or 3’UTR while the
CDS may be complete. A large number of the mRNA records corresponding to
the protein hits in the list are “partial”. That’s not reason for
exclusion.
For C. tropicalis, I found a new, 2019
assembly that uses both PacBio and Illumina. The assembly did use
the reference genome assembly as a guide.
To identify all partial mRNA, we will use the rentrez
package to query the ncbi database.
# define a function for retrieving the mRNA information from the nucleotide databased, based on protein ID
retrieve_mrna_info <- function(ids){
nuc.lnk <- entrez_link(dbfrom = "protein", db = "nucleotide", id = ids, by_id = TRUE)
nucID <- sapply(nuc.lnk, function(x){
lnk = x$links$protein_nuccore_mrna
return(ifelse(is.null(lnk), NA, lnk))
})
nucSum <- entrez_summary(db = "nuccore", id = na.omit(nucID))
names(nucSum) <- ids[!is.na(nucID)]
return(nucSum)
}
retrieve_protein_info <- function(ids){
pSum <- entrez_summary(db = "protein", id = ids)
names(pSum) <- ids
return(pSum)
}
# all refseq protein IDs to get the mRNA information for
pid <- unique(refseq$sid)
# posting too many queries can hit the rate-limit. split into multiples of 50s
iter.pid <- split(x = pid, f = ceiling(seq_along(pid)/50))
pSum <- sapply(iter.pid, retrieve_protein_info)
nucSum <- sapply(iter.pid, retrieve_mrna_info)
# extract the mRNA info
mrnaInfo <- lapply(nucSum, function(x) {
extract_from_esummary(x, elements = c("caption", "completeness"), simplify = FALSE) %>%
bind_rows(.id = "sid") %>%
rename(tID = caption, tComplete = completeness)
}) %>% bind_rows()
# extract the protein completeness info
pInfo <- lapply(pSum, function(x) {
extract_from_esummary(x, elements = c("completeness"), simplify = FALSE) %>%
bind_rows(.id = "sid") %>%
rename(pComplete = completeness)
}) %>% bind_rows()
Now remove duplicates (resulting from the different queries) and
filter by species (those that overlap the Shen 2018 list plus the MDR
clade)
sps.include <- c(spsNameSyn$ncbi.name, "Candida haemuloni", "Candida duobushaemulonis", "Candida pseudohaemulonii")
dedup.ref <- refseq %>%
filter(species %in% sps.include, qcovs >= 50) %>%
group_by(sid) %>%
filter(evalue == min(evalue)) %>%
left_join(mrnaInfo, by = c("sid" = "sid")) %>%
left_join(pInfo, by = c("sid" = "sid")) %>%
select(query, sid, slen, pComplete, tID, tComplete, species, strain, everything())
Summarize the deduplicated list - for each species, record the total
number of hits, those that are labeled as incomplete in the protein
record, those that are above 500 aa and the number of hits below 500 aa
that are labeled as incomplete
sum.ref <- dedup.ref %>%
group_by(species, strain) %>%
summarize(
total = n(),
incompl = sum(pComplete != ""),
gt500 = sum(slen >= 500),
shortIncompl = sum(pComplete != "" & slen < 500)
)
`summarise()` has grouped output by 'species'. You can override using the `.groups` argument.
sum.ref %>% arrange(desc(shortIncompl))
- S. stipitis and M. bicuspidata had a large number
of incomplete proteins, likely a reflection of the genome assembly
status. For M. bicuspidata, a newer Illumina + Oxford Nanopore
assembly
is available for this species but is only assembled to the contig level,
with no gene annotation. I tried blasting the refseq hits from the
species against the new assembly but the match was poor for most of the
hits - instead of getting a long, contiguous HSP, I got mostly broken
pieces with lots of mismatches. I suspect this is due to a combination
of tandem gene arrays causing difficulty for assembly around the Hil
homologs loci and the sequencing depth and assembly itself. Based on
these observations, I plan to include the species in the CAFE analysis
and potentially in the phylogenetic reconstruction as well, but exclude
it from the homolog protein trait analyses.
- For S. stipitis, I identified a newer
assembly for a different strain (NRRL Y-7124) and performed local
blast against the annotated protein sequences. I identified a total of
13 hits, of which seven are above the 50% query coverage threshold. This
is quite different from the 18 hits identified in the refseq assembly.
When I blasted the refseq hits against the new assembly, some of them
had poor matches. Without further investigation, it is not clear what is
the cause of the discrepancy. I plan to use the new assembly hits for
downstream analyses given that it is more recently sequenced.
- For the same reasons as above, I plan to include Y. tenuis,
S. tanzawaensis and C. jadinii in the CAFE analysis
but exclude them from the protein feature analyses. Given that they have
relatively few hits, I could consider manually searching for the full
length ORF, but that’s optional.
- For D. fabryi, the Y-1000 plus project has sequenced
another strain from this species. I blasted against that assembly and
identified two matches. But the two hits appear too similar to each
other, leaving me wonder if they are truly distinct loci, or are they
partially duplicate contigs.
Hits based on the new S. stipitis assembly
s.stipitis <- read_tsv("../../data/long-read-assemblies/s_stipitis_NRRL_Y7124/S-stipitis-NRRL-Y7124-Hil-homologs-blast.txt",
comment = "#", col_names = header[1:10], col_types = cols()) %>%
mutate(species = "Scheffersomyces stipitis", strain = "NRRL Y-7124", staxid = "913138",
pident = num(pident, digits = 1)) %>%
relocate(species:strain, .after = pident) %>%
group_by(sid) %>%
filter(evalue == min(evalue))
4. Candida glabrata hits
The Cormack lab released new assemblies for a couple of C.
glabrata strains in Xu 2020 and Xu 2021 (PMIDs: 32068314,
33713372). These assemblies were based on long-red sequencing
technologies supplemented with short reads for polishing the sequences.
They also paid special attention to the subtelomeres, using various
means to verify that their newly assembled subtelomeric regions are
correct. Performing blastp searches against these assemblies (I tested
two of them: ATTC2001=CBS138 and BG2) recovered 13 instead of 3 hits (in
the new refseq hit list). I verified that all of them are credible.
Actually, Xu et al. 2020 supplementary table specifically
listed these 13 as predicted adhesins belonging to cluster V, which also
included Awp2 studied by Reithofer et al 2021. Including them in the
homologs list would properly reflect the family size in the species, but
will also highlight the issue that incompletely assembled subtelomeres
may have led to many false negatives in the gene family inventory. I
think it is worth including it and commenting on the caveats.
c.glabrata <- read_tsv("../../data/long-read-assemblies/c_glabrata_CBS138/C-glabrata-CBS138-Hil-homologs-blast.txt",
comment = "#", col_names = header[1:10], col_types = cols()) %>%
mutate(species = "Candida glabrata", strain = "CBS 138", staxid = "5478",
pident = num(pident, digits = 1)) %>%
relocate(species:strain, .after = pident) %>%
group_by(sid) %>%
filter(evalue == min(evalue))# %>%
#rename(pid = sid)
c.glabrata
Note that I also checked three other species for which I can find a
recent assembly that used long-read technlogoies. None of them led to so
many more hits as the C. glabrata assembly did. In fact, only
the C. nivariensis assembly yielded one more hit than the
older, short-read based assembly. There may be two possibilities: 1) not
all new assemblies are created equal. there are some special properties
of the C. glabrata assembly that made its subtelomeric regions
especially well assembled; 2) there is something special about C.
glabrata in that the Hil family expanded more significantly than
even the closely related Nakaseomyces species. As of now we cannot
distinguish between these two.
5. FungiDB hits
FungiDB doesn’t include any species not already in the refseq
database. The hits that were added to the refseq hits from the earlier
analysis are
prev.fdb.add <- tribble(
~gid, ~pid, ~species,
"CLUG_05233", "XP_002615218.1", "Clavispora lusitaniae",
"CPAR2_600430", "XP_036662815.1", "Candida parapsilosis",
"CPAR2_806390", "XP_036665262.1", "Candida parapsilosis",
"CPAR2_806420", "XP_036665265.1", "Candida parapsilosis",
"B9J08_004098", "PIS52481.1", "Candida auris"
)
prev.fdb.add %>% filter(!pid %in% dedup.ref$sid)
The first four are not missing, just a difference in the ID format.
The missing C. auris sequence is known to us and will be added
regardless. Based on the above, we will no longer consult the FungiDB to
supplement the refseq hits.
c.auris <- read_tsv("../../output/XP_028889033-Cauris-five-strains-blast.txt",
comment = "#", col_types = cols()) %>%
filter(qseqid == "XP_028889033.1_PF11765", sseqid == "PIS52481.1") %>%
rename(sid = sseqid) %>%
mutate(
pid = sid,
query = "Caur_Hil1",
# the blast result from the above file was done using the first 325 amino acids as query
# I manually did two sequence blast with the PF11765 from Hil1 as the query and entered
# the query and subject start and end positions below
q.start = 12, q.end = 326, s.start = 11, s.end = 325,
qcovs = (q.end - q.start + 1)/325*100,
slen = 1247,
species = "Candida auris",
staxid = "498019"
) %>%
select(query, sid, slen, species, qcovs, pident, q.start, q.end, s.start, s.end, evalue, staxid, pid)
I now consider the refseq list complete, we will gather the final
list of IDs and retrieve their sequences.
Retrieve protein info (no mRNA information is available)
id.add <- c(c.glabrata$sid, s.stipitis$sid, c.auris$sid)
pSum1 <- retrieve_protein_info(id.add)
# extract the protein completeness info
pInfo1 <- extract_from_esummary(pSum1, elements = c("completeness"), simplify = FALSE) %>%
bind_rows(.id = "sid") %>%
rename(pComplete = completeness)
Replace C. glabrata and S. stipitis in refseq hit
list. Add the C. auris Hil4 from B8441.
dedup.ref.cg <- dedup.ref %>%
filter(!species %in% c("Candida glabrata", "Scheffersomyces stipitis")) %>%
bind_rows(filter(s.stipitis, qcovs >= 50)) %>%
bind_rows(filter(c.glabrata, qcovs >= 50)) %>%
bind_rows(filter(c.auris, qcovs >= 50)) %>%
rows_update(pInfo1, by = "sid", unmatched = "ignore") %>%
mutate(pid = gsub(".*\\|(.*)\\|.*", "\\1", sid))
Retrieve sequences
iter.pid1 <- split(x = dedup.ref.cg$pid, f = ceiling(seq_along(dedup.ref.cg$pid)/50))
ref.seq <- lapply(iter.pid1, function(x){
entrez_fetch(db = "protein", id = x, rettype = "fasta")
})
# merge all the sequences and write to file
ref.seq %>% paste0(collapse = "") %>%
write(file = "../../output/20220503-expanded-blast-refseq-homologs.faa")
6. GRYC hits
GRYC did include a few more species, especially in the Nakaseomyces
and Lachancea genera. While the same assembly data appear to exist in
the ncbi database, they are certainly not included in the refseq
database. Moreover, even when the assemblies are present in the ncbi
database, there are only the genomic sequences, not translated CDS,
which likely make them unavailable to blastp searches. Therefore I
downloaded the five non-glabrata Nakaseomyces genomes plus one Lachancea
species, L. kluyveri and performed local tblastn
searches. Below I will process the results and decide which ones to
include in the final list.
gryc.sps <- tribble(
~sps.abr, ~species, ~strain, ~staxid,
"CANI", "Candida nivariensis", "CBS 9983", "1279115",
"NADE", "Nakaseomyces delphensis", "CBS 2170", "1279113",
"NABA", "Nakaseomyces bacillisporus", "CBS 7720", "1279116",
"CACA", "Candida castellii", "CBS 4332", "1279086",
"CABR", "Candida bracarensis", "CBS 10154", "1279114",
"SAKL", "Lachancea kluyveri", "CBS 3082", "4934"
)
gryc <- read_tsv("../../data/Nakaseomyces_GRYC/Nakaseomyces-Hil-homologs-blast.txt",
comment = "#", col_names = header[1:10], col_types = cols()) %>%
mutate(sps.abr = gsub("BN[0-9]*_", "", sid) %>% str_sub(1,4), pident = num(pident, digits = 1)) %>%
left_join(gryc.sps, by = "sps.abr") %>%
relocate(species:strain, .after = pident) %>%
select(-sps.abr)
dedup.gryc <- gryc %>%
group_by(sid) %>%
filter(evalue == min(evalue)) %>%
mutate(pid = gsub("BN[0-9]*_", "", sid) %>% gsub("1_1$", "", .)) %>%
filter(qcovs >= 50) %>%
ungroup()
Extract the protein sequences from the combined fasta
cat(dedup.gryc$sid, file = "20220506-gryc-deduplicated-id-for-extracting-fasta.txt", sep = "\n")
Run the following at the command line assuming the current working
directory is 02-blast
python3 script/extract_fasta_gz.py \
data/Nakaseomyces_GRYC/20220503-gryc-nakaseomyces-protein.faa.gz \
analysis/expanded-blast/20220506-gryc-deduplicated-id-for-extracting-fasta.txt \
output/20220506-expanded-blast-gryc-homologs.faa
Merge the GRYC sequences with the refseq sequences - run the
following at the command line
cat 20220503-expanded-blast-refseq-homologs.faa \
20220506-expanded-blast-gryc-homologs.faa \
> 20220506-expanded-blast-combined-homologs.faa
Check to make sure that no duplicates exist.
bioawk -c fastx '{print $name}' 20220506-expanded-blast-combined-homologs.faa | sort | uniq -d
If there is no output, then there is no duplicates. The command will
output one line per sequence that occcurs more than once in the
file.
7. Merge all the sequences
Export the combined homologs list
dedup.merge <- bind_rows(
RefSeq = dedup.ref.cg,
GRYC = dedup.gryc,
.id = "source"
) %>%
mutate(name = paste(pid, gsub(" ", "_", species), sep = "_")) %>%
select(-q.start, -q.end, -s.start, -s.end) %>%
ungroup()
write_tsv(dedup.merge, file = "../../output/20220506-expanded-blast-combined-homologs.tsv")
9. Identify the PF11765 domain
Use HMMER to identify the location of the PF11765 domain. The hmm
model was downloaded from the pfam database on 2022-05-04. Run the
following command in 02-blast directory.
hmmscan --noali --domE 1e-3 \
-o 20220504-hmmscan.log \
--domtblout output/20220506-expanded-blast-combined-PF11765-hmmscan.txt \
data/HMM-profile/Hyphal_reg_CWP.hmm \
output/20220506-expanded-blast-combined-homologs.faa
Read the result
hmm.header <- c("target", "tacc", "tlen", "pid", "pacc", "slen", "full.evalue", "full.score", "full.bias",
"num", "of", "c.evalue", "i.evalue", "domain.score", "domain.bias",
"hmm.from", "hmm.to", "ali.from", "ali.to", "env.from", "env.to", "acc")
hmm <- read_table("../../output/20220506-expanded-blast-combined-PF11765-hmmscan.txt",
comment = "#", col_names = hmm.header, col_types = cols()) %>%
mutate(sid = pid, pid = gsub("BN[0-9]*_", "", pid) %>% gsub("1_1$", "", .)) %>%
select(sid, pid, num, of, i.evalue:env.to)
dedup.merge %>% filter(!pid %in% hmm$pid)
Curious why hmmscan didn’t identify the PF11765 domain
in the two sequences, I submitted the same sequence file to Batch CD-search,
which uses the RPS-BLAST to search a collection of protein sequences for
matches to a set of profile matrices. It is the reverse of the
PSI-BLAST.
cdsearch <- read_tsv("../../output/20220509-expanded-blast-combined-pfam-cd-search-output.txt", skip = 7,
col_types = cols()) %>%
filter(grepl("Hyphal", `Short name`)) %>%
mutate(sid = str_match(Query, ">([a-zA-Z0-9_.]*) ")[,2],
pid = gsub("BN[0-9]*_", "", sid) %>% gsub("1_1$", "", .)) %>%
select(sid, pid, everything(), -Query)
cdsearch %>% filter(pid %in% setdiff(dedup.merge$pid, hmm$pid)) %>% select(-sid)
Both sequences did have a match, although one of them has a
“superfamily” level match, suggesting that no hits exceed the domain
level E-value, but multiple hits met the “non-specific” criteria. Not
sure why hmmscan didn’t pick up the domain in the C. glabrata
sequence though
Merge the CD-Search and HMMSCAM results into one. I’m using the
envelope coordinates from hmmscan rather than the alignment
coordinates. According to PFam
documentation, the envelope coordinates reflect the region where the
match has been probabilistically determined to lie. It will be
slightly wider than the alignment.
hmm.cd <- cdsearch %>% filter(grepl("Hyphal_reg_CWP", `Short name`)) %>%
select(sid, pid, cd.type = `Hit type`, cd.incom = Incomplete, cd.from = From, cd.to = To) %>%
full_join(select(hmm, sid, pid, ali.from, ali.to, env.from, env.to)) %>%
mutate(cd.len = cd.to - cd.from + 1,
ali.len = ali.to - ali.from + 1,
env.len = env.to - env.from + 1)
Joining, by = c("sid", "pid")
hmm.cd %>% group_by(pid) %>% filter(n() > 1) %>% select(-sid, -ends_with("to"))
- Above are the sequences with more than one entries from either
search. Note that with the parameters used for CD-Search, it appears
that the algorithm only reports the strongest match (specific). This can
probably be changed by switching to the full report mode. But it’s not
important as I plan to use the HMM envelope coordinates whenever
available.
- The second match in XP_003671892.1 is very short. Remove it.
- The second match in NADE0s03e00110g largely overlaps with the first
one. Remove.
domain.final <- hmm.cd %>%
# remove the short hit and duplicates due to the join operation, but save the rows where hmmscan didn't find a match
filter(is.na(ali.from) | (abs(ali.from - cd.from) < 1000 & ali.len > 100)) %>%
filter(!(pid == "NADE0s03e00110g" & ali.from == 39)) # remove the overlapping entry
domain.final %>%
pivot_longer(c(cd.len, env.len), values_to = "length", names_to = "method") %>%
mutate(method = factor(method, levels = c("cd.len", "env.len"), labels = c("CD-Search hits", "HMMSCAN hits"))) %>%
ggplot(aes(x = length)) +
geom_histogram(aes(fill = cd.incom != "-"), bins = 30) +
geom_vline(xintercept = 328, linetype = 2) +
scale_fill_brewer("Incomplete", type = "qual") +
facet_wrap(~method, nrow = 2, ) + theme_cowplot()
Warning: Removed 2 rows containing non-finite values (stat_bin).

Some are incomplete matches.
Write the coordinates into a BED formatted file for extracting the
sequences.
domain.final %>%
mutate(
# if hmm hits are not available, use cd-search hit coordinates, with 3 bp expansion on both sides
# note that BED format is 0-based for the start position
from = ifelse(is.na(env.from), cd.from - 3 - 1, env.from - 1),
to = ifelse(is.na(env.to), cd.to + 3, env.to)) %>%
select(seq = sid, from, to) %>%
# the following statement is to remove the second PF11765 domain from one protein for the purpose of gene tree
group_by(seq) %>%
filter(from == min(from)) %>%
write_tsv(file = "../../output/20220512-expanded-blast-combined-homologs-PF11765.BED", col_names = FALSE)
Also write a version with the long name and not removing the second
domain from the one sequence, for the purpose of plotting and
calculating sequence length - PF11765
domain.final %>%
mutate(
from = ifelse(is.na(env.from), cd.from - 3 - 1, env.from - 1),
to = ifelse(is.na(env.to), cd.to + 3, env.to)) %>%
select(seq = pid, from, to) %>% # select pid for merging with dedup.merge
left_join(select(dedup.merge, seq = pid, name), by = "seq") %>%
ungroup %>% select(name, from, to) %>%
write_tsv(file = "../../output/20220623-expanded-blast-combined-homologs-PF11765-longname.BED", col_names = FALSE)
To extract the sequence matches to the PF11765 profile, execute the
following shell command
seqtk subseq 20220506-expanded-blast-combined-homologs.faa \
20220512-expanded-blast-combined-homologs-PF11765.BED > \
20220512-expanded-blast-combined-homologs-PF11765.faa
10. Rename the sequences for gene tree reconstruction
To facilitate gene tree reconstruction, rename the sequence names in
the form of ID_species_name, and get rid of the description
renamer <- dedup.merge %>%
# a bit complicated: in the sequence file, sequence names from Refseq sequences were modified
# while sequences from GRYC were not
mutate(id = ifelse(source == "RefSeq", pid, sid)) %>%
select(id, name) %>%
deframe()
myFastaRename <- function(file, out){
input <- read.fasta(file, seqtype = "AA", as.string = TRUE)
if(any(grepl(":", names(input)))){
# deal with the seqtk output
names(input) <- gsub(":.*$", "", names(input))
}
if(!all(names(input) %in% names(renamer))){
warning("The following sequences were not found in the rename list:")
print(setdiff(names(input), names(renamer)))
}
write.fasta(input, names = renamer[names(input)], file.out = out, nbchar = 5000)
}
myFastaRename(file = "../../output/20220506-expanded-blast-combined-homologs.faa",
out = "../../output/20220506-expanded-blast-combined-homologs-longname.faa")
myFastaRename(file = "../../output/20220512-expanded-blast-combined-homologs-PF11765.faa",
out = "../../output/20220512-expanded-blast-combined-homologs-PF11765-longname.faa")
11. Obtain subtrees for the species included
This is for the latter gene tree reconcilation and plotting.
Output the species used as a single column text file:
# after adding the GRYC species, we need to expand the sps.include list
# add S. cerevisiae for species tree illustrations
sps.final <- c(unique(dedup.merge$species), "Saccharomyces cerevisiae")
# convert the species names to the Shen 2018 name
sps.final.shen <- spsNameConvert %>%
mutate(ncbi.name = species,
shen2018 = word(gsub("_", " ", old_speceis_names), -2, -1)) %>%
select(ncbi.name, shen2018, `Major clade`, Family, Genus) %>%
filter(ncbi.name %in% sps.final)
cat("The following species are not present in the Shen 2018 list and need to be added later.")
The following species are not present in the Shen 2018 list and need to be added later.
setdiff(sps.final, spsNameConvert$species)
[1] "Candida haemuloni" "Candida duobushaemulonis" "Candida pseudohaemulonii"
sps.final.shen$shen2018 %>% na.omit() %>% gsub(" ", "_", .) %>%
write(file = paste0("data/", gsub("-", "", Sys.Date()), "-expanded-blast-species-list.txt"))
Get the species tree using TreeHouse
# first install shiny and phytools package
shiny::runGitHub("treehouse", "JLSteenwyk")
I then manually added the missing species by editing the Newick file
(without branch length version). The final result is shown below: 
Also export the gene-to-species mapping for generax
gene2sps.map <- dedup.merge %>%
select(name, species) %>%
# modify the two species names that differ between the gene names and species tree
mutate(treeName = gsub(" ", "_", species))#treeName = case_when(
#species == "Yamadazyma tenuis" ~ "Candida tenuis",
#species == "Suhomyces tanzawaensis" ~ "Candida tanzawaensis",
#TRUE ~ species),
#treeName = gsub(" ", "_", treeName))
gene2sps.map %>%
select(name, treeName) %>%
write_tsv("data/20220516-expanded-blast-genes-to-species-map.tsv", col_names = FALSE)
Append the species list with additional information
sps.patho <- read_tsv("data/20220518-expanded-blast-species-pathogen-info.txt", col_types = cols())
Warning: One or more parsing issues, see `problems()` for details
sps.summary <- dedup.merge %>%
group_by(species, staxid) %>%
summarize(
total = n(),
short = sum(slen < 500),
shortIncompl = ifelse(
any(is.na(pComplete)), NA,
sum(pComplete %in% c("no-ends", "no-left", "no-right") & slen < 500)
),
.groups = "drop"
) %>%
add_row(species = "Saccharomyces cerevisiae",
staxid = "559292",
total = 0, short = 0, shortIncompl = NA) %>%
left_join(sps.patho, by = "species") %>%
arrange(desc(total))
sps.summary %>% select(-staxid)
Query the taxonomy database for lineage information for each
species
taxon <- entrez_fetch(db = "taxonomy", id = sps.summary$staxid, rettype = "xml") %>%
XML::xmlToList()
names(taxon) <- sps.summary$species
Parse the taxonomy information
sps.lin <- lapply(taxon, function(x) {
bind_rows(x$LineageEx) %>% tail(-10) %>% filter(Rank != "species")
}) %>%
bind_rows(.id = "species")
write_tsv(sps.lin, "data/20220518-expanded-blast-species-taxonomy.tsv")
Write species info to file
spsInfo <- sps.lin %>%
filter(Rank == "family") %>%
select(species, family = ScientificName) %>%
mutate(treeName = gsub(" ", "_", species)) %>%
#treeName = case_when(
#species == "Yamadazyma tenuis" ~ "Candida tenuis",
#species == "Suhomyces tanzawaensis" ~ "Candida tanzawaensis",
#TRUE ~ species),
#treeName = gsub(" ", "_", treeName)) %>%
right_join(sps.summary, by = "species")
write_tsv(spsInfo, file = "data/20220518-expanded-blast-species-info.tsv")
12. Genome assembly info
Use species taxid to fetch the links to the taxonomy database. Note
that some of the taxids are for specific strains while others are at the
species level. As a result, there may be different number of assemblies
being fetched.
staxid <- sps.summary %>% select(species, staxid) %>% deframe()
staxid["Scheffersomyces stipitis"] <- "4924" # use the species general ID
assembly.lnk <- entrez_link(dbfrom = "taxonomy", db = "assembly", id = staxid, by_id = TRUE)
assembly.lnk <- lapply(assembly.lnk, function(x) x$links$taxonomy_assembly)
names(assembly.lnk) <- sps.summary$species
assembly.lnk["Lachancea kluyveri"] <- "9527871"
Get the assembly summary info from ncbi
assembly.info <- lapply(assembly.lnk, function(i){
if(!is_empty(i))
entrez_summary(db = "assembly", id = i)
})
Extract the information into a tibble
fields <- c("assemblyaccession", "lastmajorreleaseaccession", "taxid", "speciestaxid",
"speciesname", "assemblytype", "assemblystatus", "coverage", "submissiondate",
"lastupdatedate", "refseq_category", "contign50", "scaffoldn50")
assembly.tb <- lapply(assembly.info, function(x) {
tb <- extract_from_esummary(x, elements = fields, simplify = FALSE)
bind_rows(tb)
}) %>% bind_rows() %>%
mutate(species = gsub("\\[|\\]", "", speciesname)) %>%
arrange(desc(refseq_category), species) %>%
select(species, assemblystatus:scaffoldn50, everything())
#select(species = speciesname, everything())
Select the refseq or one alternative assembly
assembly.tbl <- assembly.tb %>%
mutate(assemblystatus = factor(assemblystatus, levels = c("Complete Genome", "Chromosome", "Scaffold", "Contig"))) %>%
group_by(species) %>%
filter(refseq_category != "na" | scaffoldn50 == max(scaffoldn50) | assemblystatus %in% c("Complete Genome", "Chromosome")) %>%
group_by(species, assemblystatus) %>%
filter(refseq_category != "na" | scaffoldn50 == max(scaffoldn50)) %>%
ungroup()
write_tsv(assembly.tbl, file = "../../output/20220525-expanded-blast-species-assembly-info.tsv")
13. Chromosomal locations
The goal is to fetch the chromosomal locations for the BLAST hits. We
are only going to use the assemblies that are at a chromosomal or
complete genome level. Note that C. auris refseq genome is
complete even though the record says it is scaffold.
# Get the list of species whose genome assembly are at a chromosomal or complete genome level
list.genome.compl <- assembly.tbl %>%
filter(assemblystatus %in% c("Complete Genome", "Chromosome"), refseq_category != "na") %>%
pull(species) %>% unique()
chr.sps <- c(list.genome.compl, "Scheffersomyces stipitis", "Candida auris")
chr.sid <- dedup.merge %>% filter(species %in% chr.sps, grepl("^ref", sid)) %>% pull(sid)
# Get the protein's gene ID (not refseq ID)
tmp.gene.lnk <- entrez_link(dbfrom = "protein", id = chr.sid, db = "gene", by_id = TRUE)
# extract gene ID
tmp.gene.id <- sapply(tmp.gene.lnk, function(x) x$links$protein_gene)
# fetch summary info from the gene database
tmp.gene <- entrez_summary(db = "gene", id = tmp.gene.id)
# extract gene name
tmp.gene.name <- extract_from_esummary(tmp.gene, elements = "name")
# extract systematic ID (stored in the otheralias field)
tmp.gene.alias <- extract_from_esummary(tmp.gene, elements = "otheraliases")
# extract chromosomal locations, only available for fully assembled genomes
tmp.gene.loc <- bind_rows(extract_from_esummary(tmp.gene, elements = "genomicinfo"), .id = "gene_uid")
# pool the unique chromosome sequence IDs to find out their length
tmp.chr.id <- unique(tmp.gene.loc$chraccver)
# obtain the chromosome length information from the nuccore database
tmp.chr <- entrez_summary("nuccore", id = tmp.chr.id)
tmp.chr.len <- extract_from_esummary(tmp.chr, elements = "slen");
names(tmp.chr.len) = tmp.chr.id
# assemble the chromosomal location dataset
chr.loc <- tibble(sid = chr.sid, gene_uid = names(tmp.gene.name),
gene_name = tmp.gene.name, gene_alias = tmp.gene.alias) %>%
left_join(tmp.gene.loc, by = "gene_uid") %>%
mutate(chrL = tmp.chr.len[chraccver], relLoc = num(chrstart/chrL, digits = 2)) %>%
left_join(select(dedup.merge, sid, pid, species, strain), by = "sid") %>%
relocate(pid:strain, .after = sid) %>% relocate(sid, .after = last_col()) %>%
select(-exoncount) %>%
arrange(species, chrloc)
write_tsv(chr.loc, file = "../../output/20220609-expanded-blast-chromosomal-locations.tsv")
LS0tCnRpdGxlOiAiQzAzNyBFeHBhbmRpbmcgTkNCSSBibGFzdHAgYW5hbHlzaXMiCmF1dGhvcjogQmluIEhlCmRhdGU6ICIyMDIyLTA0LTA4IHVwZGF0ZWQgYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKLS0tCgpgYGB7cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHJlcXVpcmUodGlkeXZlcnNlKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHJlcXVpcmUoY293cGxvdCkpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhyZXF1aXJlKGdndmVubikpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhyZXF1aXJlKHNlcWlucikpCiMgc2V0IE5DQkkgRW50cmV6IEFQSSBrZXkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHJlcXVpcmUocmVudHJleikpCnNldF9lbnRyZXpfa2V5KCJmNGYwMWMyMjBmM2EwYTE2MmJhZGE1YjBiZDVkNGIxMzE5MDgiKQpgYGAKCiMjIEJhY2tncm91bmQKVGhpcyBhbmFseXNpcyBpcyBpbiByZXNwb25zZSB0byByZXZpZXdlciAxJ3MgcXVlc3Rpb25zOgoKMS4gVGhlIHN1Ym1pdHRlZCB2ZXJzaW9uIHVzZWQgX0MuIGF1cmlzXyBIaWwxJ3MgUEYxMTc2NSBkb21haW4gYXMgdGhlIHF1ZXJ5IChiYWl0KS4gV2hhdCBpZiBvdGhlciBob21vbG9ncyB3ZXJlIHVzZWQ/IFdvdWxkIHRoZSBudW1iZXIgYW5kIGlkZW50aXR5IG9mIHRoZSBibGFzdCByZXN1bHRzIGNoYW5nZT8KMS4gV2h5IHJlbW92ZSBzcGVjaWVzIGZyb20gdGhlIGJsYXN0IG91dHB1dD8KClRvIGFkZHJlc3MgdGhlIGZpcnN0IHByb2JsZW0sIEkgZGlkIGJsYXN0cCB3aXRoIHR3byBhZGRpdGlvbmFsIHF1ZXJpZXM6IHRoZSBQRjExNzY1IGRvbWFpbnMgaW4gX0MuIGFsYmljYW5zXyBIeXIxIGFuZCBfQy4gZ2xhYnJhdGFfIEh5cjEuCgpBcyBmb3IgdGhlIHNlY29uZCBxdWVzdGlvbiwgbXkgcmVzcG9uc2UgYW5kIHNvbHV0aW9ucyBhcmUgdG8gaW5jbHVkZSBhbnkgc3BlY2llcyB0aGF0IHdlcmUgYW1vbmcgdGhlIFkxMDAwIHRyZWUgaW4gU2hlbiBldCBhbC4gMjAxOC4gVGhlIG1vcmUgY29tcGxldGUgc2V0IGNhbiBiZSB1c2VkIGZvciB0aGUgZmFtaWx5IGV4cGFuc2lvbiBhbmFseXNpcy4gSSBwbGFuIHRvIGtlZXAgdGhlIGhvbW9sb2dzIGFuYWx5c2lzIHJlc3RyaWN0ZWQgdG8gdGhlIDEwNCBob21vbG9ncywgYXMgYWRkaW5nIG1vcmUgc3BlY2llcyBhcmUgbm90IGV4cGVjdGVkIHRvIGNoYW5nZSB0aGUgbWFpbiBjb25jbHVzaW9ucy4KCiMjIERhdGEKSSBwZXJmb3JtZWQgYmxhc3RwIGFuYWx5c2lzIHVzaW5nIHRoZSB0aHJlZSBxdWVyaWVzIGFnYWluc3QgZWl0aGVyIHRoZSByZWZzZXFfcHJvdCwgdGhlIG5ldyBjbHVzdGVyZWQgbnIgZGF0YWJhc2VzIG9yIGluIEZ1bmdpREIuIEZvciB0aGUgZm9ybWVyIHR3byAocGVyZm9ybWVkIHRocm91Z2ggd2ViIGludGVyZmFjZSBvbiBOQ0JJIHdlYnNpdGUpLCBJIGRvd25sb2FkZWQgdGhlIHJlc3VsdHMgdXNpbmcgdGhlIFJlcXVlc3QgSUQgKFJJRCkgd2l0aCB0aGUgYGJsYXN0X2Zvcm1hdHRlcmAgcHJvZ3JhbSBhbmQgdGhlbiB1c2VkIHRoZSBzYW1lIHRvb2wgdG8gcmVmb3JtYXQgdGhlIG91dHB1dCB0byBjb250YWluIHRoZSBkZXNpcmVkIGNvbHVtbnMuIEZvciB0aGUgRnVuZ2lEQiByZXN1bHQsIEkgdHJpZWQgZG93bmxvYWRpbmcgdGhlIHJlc3VsdHMgaW4gQVNOLjEgZm9ybWF0LCBidXQgd2Fzbid0IGFibGUgdG8gdXNlIGBibGFzdF9mb3JtYXR0ZXJgIHRvIHJlZm9ybWF0IGl0LiBUaGVyZWZvcmUgSSBkb3dubG9hZGVkIHRoZSB0YWJsZSBhcyBzaG93biBvbiB0aGUgZGVmYXVsdCByZXN1bHQgcGFnZS4KYGBge3J9CiMgaGVhZGVyIGZvciBuY2JpIG91dHB1dApoZWFkZXIgPC0gYygicXVlcnkiLCAicWNvdnMiLCAic2lkIiwgInNsZW4iLCAicGlkZW50IiwgInEuc3RhcnQiLCAicS5lbmQiLCAicy5zdGFydCIsICJzLmVuZCIsICJldmFsdWUiLCAic3RheGlkIiwgInNzY2luYW1lcyIpCiMgcmVmc2VxIHJlc3VsdHMKcmVmc2VxIDwtIHJlYWRfdHN2KCJkYXRhLzIwMjIwNDA2LWV4cGFuZGVkLVBGMTE3NjUtYmxhc3RwLXJlZnNlcS50eHQiLCBjb21tZW50ID0gIiMiLCBjb2xfdHlwZXMgPSBjb2xzKCksIGNvbF9uYW1lcyA9IGhlYWRlcikgJT4lIAogIG11dGF0ZShzcGVjaWVzID0gd29yZChzc2NpbmFtZXMsIDEsIDIpICU+JSBnc3ViKCJcXFt8XFxdIiwgIiIsIHggPSAuKSwgc3RyYWluID0gd29yZChzc2NpbmFtZXMsIDMsIC0xKSwKICAgICAgICAgcGlkZW50ID0gbnVtKHBpZGVudCwgZGlnaXRzID0gMSkpICU+JSAKICBzZWxlY3QoLXNzY2luYW1lcykgJT4lIAogIHJlbG9jYXRlKHNwZWNpZXM6c3RyYWluLCAuYWZ0ZXIgPSBwaWRlbnQpCiMgbnIgcmVzdWx0cwpuciA8LSByZWFkX3RzdigiZGF0YS8yMDIyMDQwNi1leHBhbmRlZC1QRjExNzY1LWJsYXN0cC1jbHVzdGVybnIudHh0IiwgY29tbWVudCA9ICIjIiwgY29sX3R5cGVzID0gY29scygpLCBjb2xfbmFtZXMgPSBoZWFkZXIpICU+JSAKICBtdXRhdGUoc3BlY2llcyA9IHdvcmQoc3NjaW5hbWVzLCAxLCAyKSAlPiUgZ3N1YigiXFxbfFxcXSIsICIiLCB4ID0gLiksIHN0cmFpbiA9IHdvcmQoc3NjaW5hbWVzLCAzLCAtMSksCiAgICAgICAgIHBpZGVudCA9IG51bShwaWRlbnQsIGRpZ2l0cyA9IDEpKSAlPiUgCiAgc2VsZWN0KC1zc2NpbmFtZXMpICU+JSAKICByZWxvY2F0ZShzcGVjaWVzOnN0cmFpbiwgLmFmdGVyID0gcGlkZW50KQojIGZ1bmdpZGIgcmVzdWx0cwpmZGIgPC0gcmVhZF9jc3YoImRhdGEvMjAyMjA0MjctZXhwYW5kZWQtYmxhc3QtZnVuZ2lkYi5jc3YiKSAlPiUgCiAgcmVuYW1lKHNpZCA9IEFjY2Vzc2lvbiwgZXZhbHVlID0gYEUtVmFsdWVgLCBzY29yZSA9IFNjb3JlLCBhbGVuID0gYEFsaWduIExlbmd0aGAsIHFjb3ZzID0gYFF1ZXJ5IENvdmVyYWdlYCkgJT4lIAogIG11dGF0ZSgKICAgIHNwZWNpZXMgPSB3b3JkKE9yZ2FuaXNtLCAxLCAyKSAlPiUgZ3N1YigiXFxbfFxcXSIsICIiLCB4ID0gLiksIHN0cmFpbiA9IHdvcmQoT3JnYW5pc20sIDMsIC0xKSwKICAgIHF1ZXJ5ID0gd29yZChRdWVyeSwgMSwgMSksCiAgICBwaWRlbnQgPSBudW0oSWRlbnRpdHkqMTAwLCBkaWdpdHMgPSAxKSwKICAgIHFjb3ZzID0gbnVtKHFjb3ZzKjEwMCwgZGlnaXRzID0gMCksCiAgICBldmFsdWUgPSBzaWduaWYoZXZhbHVlLCBkaWdpdHMgPSAxKSwKICAgIHNjb3JlID0gc2lnbmlmKHNjb3JlLCBkaWdpdHMgPSAzKSwKICAgIC5rZWVwID0gInVudXNlZCIKICApICU+JSAKICAjIG1ha2Ugc3BlbGxpbmcgY29uc2lzdGVudCB3aXRoIG5jYmkKICBtdXRhdGUoc3BlY2llcyA9IGdzdWIoIkNhbmRpZGEgaGFlbXVsb25pcyIsICJDYW5kaWRhIGhhZW11bG9uaSIsIHNwZWNpZXMpKSAlPiUKICBzZWxlY3QocXVlcnksIHFjb3ZzLCBzaWQsIHBpZGVudCwgc3BlY2llcywgc3RyYWluLCBzY29yZSwgZXZhbHVlLCBgUmFuayBQZXIgUXVlcnlgLCBgUmFuayBQZXIgU3ViamVjdGApCmBgYAoKIyMgQW5hbHlzaXMKIyMjIDEuIE92ZXJsYXAgYmV0d2VlbiB0aGUgdGhyZWUgcXVlcmllcz8KV2hhdCdzIHRoZSBleHRlbnQgb2Ygb3ZlcmxhcCBiZXR3ZWVuIHRoZSBibGFzdHAgaGl0cyB1c2luZyB0aGUgdGhyZWUgZGlmZmVyZW50IHF1ZXJpZXM/CgpIaXRzIHdpdGggPDUwJSBxdWVyeSBjb3ZlcmFnZSBhcmUgcmVtb3ZlZC4KYGBge3J9CnJlZnNlcSAlPiUgZmlsdGVyKHFjb3ZzIDwgNTApCmBgYApWZW5uIGRpYWdyYW0gc2hvd2luZyB0aGUgZXh0ZW50IG9mIG92ZXJsYXAgYW5kIHNldCBzcGVjaWZpYyBoaXRzOgpgYGB7cn0Kc2V0Lm92bHAgPC0gcmVmc2VxICU+JSAKICBmaWx0ZXIocWNvdnMgPj0gNTApICU+JSAKICBncm91cF9ieShzaWQpICU+JSAKICBzdW1tYXJpemUoCiAgICBDYXVyID0gYW55KHF1ZXJ5ID09ICJDYXVyX0hpbDEiKSwKICAgIENhbGIgPSBhbnkocXVlcnkgPT0gIkNhbGJfSHlyMSIpLAogICAgQ2dsYSA9IGFueShxdWVyeSA9PSAiQ2dsYV9IeXIxIikKICApCmdndmVubihzZXQub3ZscCkKYGBgCgpXaGljaCBnZW5lcyBhcmUgZm91bmQgYnkgdGhlIF9DLiBhbGJpY2Fuc18gYW5kL29yIF9DLiBnbGFicmF0YV8gSHlyMSBxdWVyeSBibGFzdCBidXQgbm90IGJ5IHRoZSBfQy4gYXVyaXNfIEhpbDEgcXVlcnkgYmxhc3Q/CmBgYHtyfQpDYWxiLm5vdC5DYXVyLmhpdHMgPC0gc2V0Lm92bHAgJT4lIGZpbHRlcighQ2F1cikgJT4lIHB1bGwoc2lkKQpyZWZzZXEgJT4lIGZpbHRlcihzaWQgJWluJSBDYWxiLm5vdC5DYXVyLmhpdHMpICU+JSAKICBncm91cF9ieShzaWQpICU+JSBmaWx0ZXIoZXZhbHVlID09IG1pbihldmFsdWUpKSAlPiUgdW5ncm91cCgpICU+JSAKICBzZWxlY3QocXVlcnksIHFjb3ZzLCBzaWQsIHNsZW4sIHBpZGVudCwgZXZhbHVlLCBzcGVjaWVzKSAlPiUgCiAgbXV0YXRlKHNwZWNpZXMgPSBwYXN0ZShzdHJfc3ViKHNwZWNpZXMsIDEsIDEpLCB3b3JkKHNwZWNpZXMsIDIsIDIpLCBzZXAgPSAiLiAiKSkgJT4lIAogIGFycmFuZ2Uoc3BlY2llcykKYGBgCj4gLSBUaGUgbWFqb3JpdHkgb2YgdGhlIGNvbWJpbmVkIHNldCBpcyBhbHJlYWR5IGluIHRoZSBzdWJzZXQgaWRlbnRpZmllZCB1c2luZyBfQy4gYXVyaXNfIEhpbDEgYXMgdGhlIHF1ZXJ5Lgo+IC0gT2YgdGhlIG9uZXMgdGhhdCBhcmUgc3BlY2lmaWMgdG8gdGhlIF9DLiBhbGJpY2Fuc18gSHlyMSBxdWVyeSwgSSBmb3VuZCB0aGUgdGhyZWUgX0MuIHBhcmFzaWxvc2lzXyBoaXRzIGFyZSBhbHJlYWR5IGluY2x1ZGVkIGluIHRoZSAxMDQgaG9tb2xvZ3MgdmlhIHRoZSBGdW5naURCIGJsYXN0LiBXaGF0ZXZlciB0aGUgcmVhc29uIHRoYXQgZXhsdWRlZCB0aGVtIGZyb20gdGhlIG5jYmkgYmxhc3RwIHNlYXJjaCB1c2luZyBfQy4gYXVyaXNfIEhpbDEgZG9tYWluIGFzIHRoZSBxdWVyeSwgdGhleSBhcmUgYWxyZWFkeSBpbiBvdXIgZGF0YXNldC4KPiAtIFNhbWUgaXMgdHJ1ZSBmb3IgWFBfNDUyMTc2LjEgZnJvbSBfSy4gbGFjdGlzXywgdGhpcyB0aW1lIHRocm91Z2ggdGhlIEdSWUMgc2VhcmNoLgoKQW1vbmcgdGhlIHNwZWNpZXMgaW5jbHVkZWQgcHJldmlvdXNseSwgaG93IG1hbnkgb2YgdGhlICJuZXciIHNlcXVlbmNlcyB3ZXJlIG5vdCBwYXJ0IG9mIHRoZSAxMDQgaG9tb2xvZ3MgbGlzdD8KCnwgc2lkIHwgZ2lkIHwgc3BlY2llcyB8IGluX2Vhcmx5X3ZlcmlvbiB8IHNvdXJjZSB8Cnw6LS18Oi0tfDotLXw6LS18Oi0tfAp8IFhQXzAwMjk5OTU4NSB8IENBR0wwTDAwMjI3ZyB8IEMuIGdsYWJyYXRhIHwgWWVzIHwgR1JZQyB8CnwgWFBfMDAyNjE1MjE4IHwgQ0xVR18wNTIzMyB8IEMuIGx1c2l0YW5pYWUgfCBZZXMgfCBGdW5naURCIHwKfCBYUF8wMDI2MjAwNDUgfCBDTFVHXzAxMjA0IHwgQy4gbHVzaXRhbmlhZSB8IE5vXiB8IE5BIHwKfCBYUF8wMDI2MTQxMjEgfCBDTFVHXzA1NjA3IHwgQy4gbHVzaXRhbmlhZSB8IE5vIHwgTkEgfAp8IFhQXzAzNjY2NTI2MiB8IENQQVIyXzgwNjM5MCB8IEMuIHBhcmFwc2lsb3NpcyB8IFllcyB8IEZ1bmdpREIgfAp8IFhQXzAzNjY2MjgxNSB8IENQQVIyXzYwMDQzMCB8IEMuIHBhcmFwc2lsb3NpcyB8IFllcyB8IEZ1bmdpREIgfAp8IFhQXzAzNjY2NTI2MyB8IENQQVIyXzgwNjQwMCB8IEMuIHBhcmFwc2lsb3NpcyB8IE5vIHwgTkEgfAp8IFhQXzQ1MjE3NiB8IEtMTEEwX0IxNDQ5OGcgfCBLLiBsYWN0aXMgfCBZZXMgfCBHUllDIHwKfCBYUF8wMDE1MjczMDcgfCBMRUxHXzAyMTM2IHwgTC4gZWxvbmdpc3BvcnVzIHwgTm8gfCBOQSB8CnwgWFBfMDAxMzg1NjgzIHwgUElDU1RfMzQwMiB8IFMuIHN0aXBpdGlzIHwgTm9eIHwgTkEgfAp8IFhQXzAwMTM4Mzk1MyB8IFBJQ1NUXzMxMDk1IHwgUy4gc3RpcGl0aXMgfCBZZXMqIHwgTkNCSSB8CgoqdGhpcyBoaXQgd2FzIG9yaWdpbmFsbHkgaW5jbHVkZWQgd2hlbiB0aGUgTjU2MCBhYSB3YXMgdXNlZCBhcyBxdWVyeS4gSW4gbGF0ZXIgYW5hbHlzaXMgd2hlcmUgTjM2MCBhYSAob2YgX0MuIGF1cmlzXyBIaWwxKSB3YXMgdXNlZCBhcyBxdWVyeSwgdGhpcyBoaXQgZHJvcHBlZCBvZmYgdGhlIGxpc3QgZHVlIHRvIEUtdmFsdWUgYmVpbmcgYWJvdmUgdGhlIDFlLTUgY3V0b2ZmLiBTbyBpdCBpcyBhIGxlZ2l0LCBidXQgYm9yZGVybGluZSBoaXQuCgpec2hvcnRlciB0aGFuIDUwMCBhLmEuCgo+IEZpdmUgaGl0cyBhcmUgbmV3LCBhbW9uZyB3aGljaCB0d28gYXJlIHNob3J0ZXIgdGhhbiA1MDAgYS5hLiBhbmQgd291bGQgaGF2ZSBiZWVuIGV4Y2x1ZGVkIGZyb20gdGhlIGxpc3QgKGFsdGhvdWdoIHRoZXkgd2VyZSBub3QgaWRlbnRpZmllZCBhdCBhbGwgd2l0aCB0aGUgX0MuIGF1cmlzXyBxdWVyeSkuCgoKIyMjIDIuIFNwZWNpZXMgdG8gaW5jbHVkZQpUaGUgbmV3IGNyaXRlcmlhIGlzIHdoZXRoZXIgdGhlIHNwZWNpZXMgaXMgYW1vbmcgdGhlIDMzMiBpbmNsdWRlZCBpbiB0aGUgbGFyZ2Utc2NhbGUgcGh5bG9nZW55IG9mIFNoZW4gX2V0IGFsLl8gMjAxOC4gSSBkb3dubG9hZGVkIHRoZSB0cmVlIGZyb20gaHR0cHM6Ly9naXRodWIuY29tL0pMU3RlZW53eWsvdHJlZWhvdXNlL2Jsb2IvbWFzdGVyL0RhdGEvU2FjY2hhcm9teWNvdGluYV9maWcyX1NoZW5fZXRhbF8yMDE4LnRyZSBhbmQgbWFudWFsbHkgY29udmVydGVkIGl0IGludG8gYSBsaXN0IG9mIHNwZWNpZXMgbmFtZXMuCmBgYHtyfQpzaGVuMjAxOCA8LSBzY2FuKCIuLi8uLi9kYXRhL3llYXN0LXBoeWxvZ2VueS9TYWNjaGFyb215Y290aW5hX2ZpZzJfU2hlbl9ldGFsXzIwMTgudHh0Iiwgd2hhdCA9ICJjaGFyYWN0ZXIiLCBzZXAgPSAiXG4iKQpgYGAKCkFueSBzcGVjaWVzIGluIHRoZSByZWZzZXEgaGl0IGxpc3Qgbm90IGluIHRoZSBTaGVuIDIwMTggcGh5bG9nZW55PwpgYGB7cn0Kc2V0ZGlmZih1bmlxdWUocmVmc2VxJHNwZWNpZXMpLCBzaGVuMjAxOCkKYGBgCgp8IHJlZnNlcV9uYW1lIHwgdGF4aWQgfCBpbl9TaGVuXzIwMTggfCBzaGVuMjAxOF9uYW1lIHwKfDotLXw6LS18Oi0tfDotLXwKfCBDYW5kaWRhIGhhZW11bG9uaSB8IDQ1MzU3IHwgTm8gfCBOQSB8CnwgQ2FuZGlkYSBkdW9idXNoYWVtdWxvbmlzIHwgMTIzMTUyMiB8IE5vIHwgTkEgfAp8IENhbmRpZGEgcHNldWRvaGFlbXVsb25paSB8IDQxODc4NCB8IE5vIHwgTkEgfAp8IFN1aG9teWNlcyB0YW56YXdhZW5zaXMgfCA5ODQ0ODcgfCBZZXMgfCBDYW5kaWRhIHRhbnphd2FlbnNpcyB8CnwgUHNldWRvbW9uYXMgc3lyaW5nYWU6UHNldWRvbW9uYXMgfCAzMTc7NTk1MTAgfCBObyB8IE5BIHwKfCBEaXV0aW5hIHJ1Z29zYSB8IDU0ODEgfCBObyB8IE5BIHwKfCBZYW1hZGF6eW1hIHRlbnVpcyB8IDU5MDY0NiB8IFllcyB8IENhbmRpZGEgdGVudWlzIHwKfCBTY2hpem9zYWNjaGFyb215Y2VzIGNyeW9waGlsdXMgfCA2NTM2NjcgfCBObyB8IE5BIHwKfCBLYXphY2hzdGFuaWEgYmFybmV0dGlpIHwgNjEyNjIgfCBObyB8IE5BIHwKfCBBcnRpYmV1cyBqYW1haWNlbnNpcyB8IDk0MTcgfCBObyB8IE5BIHwKCk1hbnVhbGx5IGZpZ3VyZSBvdXQgc3BlY2llcyBzeW5vbnltcyBhbmQgcmVjb3JkIGJvdGggbmFtZXMgZm9yIHRoZSBzcGVjaWVzIHRvIHVzZS4gKip1cGRhdGUqKjogZG93bmxvYWRlZCB0aGUgc3BlY2llcyBuYW1lIGNvbnZlcnRpb24gdGFibGUgZnJvbSBTaGVuIGV0IGFsIDIwMTggc3VwcGxlbWVudGFyeSBtYXRlcmlhbC4KYGBge3J9CnNwc05hbWVDb252ZXJ0IDwtIHJlYWRfdHN2KCIuLi8uLi9kYXRhL3llYXN0LXBoeWxvZ2VueS8zNDN0YXhhX3NwZWljaWVzLW5hbWVfY2xhZGUtbmFtZV9jb2xvci1jb2RlLnR4dCIsIGNvbF90eXBlcyA9IGNvbHMoKSkgJT4lIAogIG11dGF0ZShzcGVjaWVzID0gd29yZChgU3BlY2llcyBuYW1lYCwgMSwgMikpCnNwc05hbWVTeW4gPC0gdGliYmxlKAogIG5jYmkubmFtZSA9IGMoaW50ZXJzZWN0KHJlZnNlcSRzcGVjaWVzLCBzaGVuMjAxOCksICJTdWhvbXljZXMgdGFuemF3YWVuc2lzIiwgIllhbWFkYXp5bWEgdGVudWlzIiwgIkxhY2hhbmNlYSBrbHV5dmVyaSIpLAogIHNoZW4ubmFtZSA9IGMoaW50ZXJzZWN0KHJlZnNlcSRzcGVjaWVzLCBzaGVuMjAxOCksICJDYW5kaWRhIHRhbnphd2FlbnNpcyIsICJDYW5kaWRhIHRlbnVpcyIsICJMYWNoYW5jZWEga2x1eXZlcmkiKQopCmBgYAoKIyMjIDMuIENoZWNrIHByb3RlaW4gY29tcGxldGVuZXNzCgpPdXIgZ29hbCBpcyB0byBjcmVhdGUgYSB0YWJsZSBkb2N1bWVudGluZyB0aGUgbnVtYmVyIG9mIHB1dGF0aXZlIGhvbW9sb2dzIHBlciBzcGVjaWVzLiBXZSB1c2VkIHRvIGFwcGx5IGEgbGVuZ3RoIGN1dG9mZiBvZiA1MDAgYWEuIEhvd2V2ZXIsIHRoZSBzcGVjaWZpYyB2YWx1ZSBvZiB0aGUgY3V0b2ZmIGlzIG5vdCBiYXNlZCBvbiBhbnkgY29tcHJlaGVuc2l2ZSBzdHVkaWVzLiBUaGlzIGNvbWJpbmVkIHdpdGggdGhlIHVuY2VydGFpbnR5IGluIHNvbWUgcHV0YXRpdmUgaG9tb2xvZydzIGxlbmd0aCBkdWUgdG8gYXNzZW1ibHkgaXNzdWVzIGxlZCB1cyB0byByZWNvbnNpZGVyIHRoaXMgZmlsdGVyLiBUaGUgc29sdXRpb24gd2UgY2FtZSB1cCB3aXRoIGlzIHRvIGluY2x1ZGUgYWxsIGJsYXN0IGhpdHMgYmFzZWQgb24gdGhlIFBGMTE3NjUgZG9tYWluIGFsb25lIGluIHRoZSBmYW1pbHkgZXhwYW5zaW9uIGFuYWx5c2lzIChDQUZFIGFuZCBwaHlsb2dlbmV0aWMgcmVjb25zdHJ1Y3Rpb24pLiBMYXRlciB3ZSB3aWxsIHNlbGVjdGl2ZWx5IGluY2x1ZGUgc29tZSBvZiB0aGVtIGZvciB0aGUgcHJvdGVpbiBmZWF0dXJlIGFuYWx5c2lzLiBTaW5jZSB0aGUgbGF0dGVyIGFuYWx5c2lzIGlzIG1haW5seSB0byBkZW1vbnN0cmF0ZSB0aGUgZmFzdCBldm9sdXRpb24gb2YgdGhlIGNlbnRyYWwgZG9tYWluIGZlYXR1cmVzLCBpdCBkb2Vzbid0IHJlbHkgb24gdGhlIGNvbXBsZXRlbmVzcyBvZiB0aGUgSGlsIGZhbWlseSBvciB0aGUgaW5jbHVzaW9uIG9mIGFsbCBzcGVjaWVzIGZvciB3aGljaCB3ZSBoYXZlIHJlY29yZHMgZm9yLgoKSW4gY3VyYXRpbmcgdGhlIGJsYXN0IGhpdHMsIHdlIGZvdW5kIHRoYXQgc29tZSAocHJvdGVpbikgYmxhc3QgaGl0cyBhcmUgYW5ub3RhdGVkIGFzIG1pc3NpbmcgdGhlIGNhcmJveHkgdGVybWludXMuIFRoaXMgd291bGQgcHJldmVudCB1cyBmcm9tIHByb3Blcmx5IGFzc2Vzc2luZyB0aGUgbGVuZ3RoIG9mIHRoZSBwcm90ZWluLiBOb3RlIHRoYXQgImluY29tcGxldGUiIG1lYW5zIGRpZmZlcmVudCB0aGluZ3MgZm9yIHRoZSBwcm90ZWluIHZzIG1STkEgcmVjb3JkLiBGb3IgdGhlIGxhdHRlciwgImluY29tcGxldGUiIGNvdWxkIG1lYW4gbWlzc2luZyA1JyBhbmQvb3IgMydVVFIgd2hpbGUgdGhlIENEUyBtYXkgYmUgY29tcGxldGUuIEEgbGFyZ2UgbnVtYmVyIG9mIHRoZSBtUk5BIHJlY29yZHMgY29ycmVzcG9uZGluZyB0byB0aGUgcHJvdGVpbiBoaXRzIGluIHRoZSBsaXN0IGFyZSAicGFydGlhbCIuIFRoYXQncyBub3QgcmVhc29uIGZvciBleGNsdXNpb24uCgpGb3IgX0MuIHRyb3BpY2FsaXNfLCBJIGZvdW5kIGEgbmV3LCBbMjAxOSBhc3NlbWJseV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9hc3NlbWJseS9HQ0FfMDEzMTc3NTU1LjEpIHRoYXQgdXNlcyBib3RoIFBhY0JpbyBhbmQgSWxsdW1pbmEuIFRoZSBhc3NlbWJseSBkaWQgdXNlIHRoZSByZWZlcmVuY2UgZ2Vub21lIGFzc2VtYmx5IGFzIGEgZ3VpZGUuCgpUbyBpZGVudGlmeSBhbGwgcGFydGlhbCBtUk5BLCB3ZSB3aWxsIHVzZSB0aGUgYHJlbnRyZXpgIHBhY2thZ2UgdG8gcXVlcnkgdGhlIG5jYmkgZGF0YWJhc2UuCmBgYHtyfQojIGRlZmluZSBhIGZ1bmN0aW9uIGZvciByZXRyaWV2aW5nIHRoZSBtUk5BIGluZm9ybWF0aW9uIGZyb20gdGhlIG51Y2xlb3RpZGUgZGF0YWJhc2VkLCBiYXNlZCBvbiBwcm90ZWluIElECnJldHJpZXZlX21ybmFfaW5mbyA8LSBmdW5jdGlvbihpZHMpewogIG51Yy5sbmsgPC0gZW50cmV6X2xpbmsoZGJmcm9tID0gInByb3RlaW4iLCBkYiA9ICJudWNsZW90aWRlIiwgaWQgPSBpZHMsIGJ5X2lkID0gVFJVRSkKICBudWNJRCA8LSBzYXBwbHkobnVjLmxuaywgZnVuY3Rpb24oeCl7CiAgICBsbmsgPSB4JGxpbmtzJHByb3RlaW5fbnVjY29yZV9tcm5hCiAgICByZXR1cm4oaWZlbHNlKGlzLm51bGwobG5rKSwgTkEsIGxuaykpCiAgfSkKICBudWNTdW0gPC0gZW50cmV6X3N1bW1hcnkoZGIgPSAibnVjY29yZSIsIGlkID0gbmEub21pdChudWNJRCkpCiAgbmFtZXMobnVjU3VtKSA8LSBpZHNbIWlzLm5hKG51Y0lEKV0KICByZXR1cm4obnVjU3VtKQp9CgpyZXRyaWV2ZV9wcm90ZWluX2luZm8gPC0gZnVuY3Rpb24oaWRzKXsKICBwU3VtIDwtIGVudHJlel9zdW1tYXJ5KGRiID0gInByb3RlaW4iLCBpZCA9IGlkcykKICBuYW1lcyhwU3VtKSA8LSBpZHMKICByZXR1cm4ocFN1bSkKfQpgYGAKCmBgYHtyfQojIGFsbCByZWZzZXEgcHJvdGVpbiBJRHMgdG8gZ2V0IHRoZSBtUk5BIGluZm9ybWF0aW9uIGZvcgpwaWQgPC0gdW5pcXVlKHJlZnNlcSRzaWQpCiMgcG9zdGluZyB0b28gbWFueSBxdWVyaWVzIGNhbiBoaXQgdGhlIHJhdGUtbGltaXQuIHNwbGl0IGludG8gbXVsdGlwbGVzIG9mIDUwcwppdGVyLnBpZCA8LSBzcGxpdCh4ID0gcGlkLCBmID0gY2VpbGluZyhzZXFfYWxvbmcocGlkKS81MCkpCnBTdW0gPC0gc2FwcGx5KGl0ZXIucGlkLCByZXRyaWV2ZV9wcm90ZWluX2luZm8pCm51Y1N1bSA8LSBzYXBwbHkoaXRlci5waWQsIHJldHJpZXZlX21ybmFfaW5mbykKYGBgCgpgYGB7cn0KIyBleHRyYWN0IHRoZSBtUk5BIGluZm8KbXJuYUluZm8gPC0gbGFwcGx5KG51Y1N1bSwgZnVuY3Rpb24oeCkgewogIGV4dHJhY3RfZnJvbV9lc3VtbWFyeSh4LCBlbGVtZW50cyA9IGMoImNhcHRpb24iLCAiY29tcGxldGVuZXNzIiksIHNpbXBsaWZ5ID0gRkFMU0UpICU+JSAKICAgIGJpbmRfcm93cyguaWQgPSAic2lkIikgJT4lIAogICAgcmVuYW1lKHRJRCA9IGNhcHRpb24sIHRDb21wbGV0ZSA9IGNvbXBsZXRlbmVzcykKfSkgJT4lIGJpbmRfcm93cygpCiMgZXh0cmFjdCB0aGUgcHJvdGVpbiBjb21wbGV0ZW5lc3MgaW5mbwpwSW5mbyA8LSBsYXBwbHkocFN1bSwgZnVuY3Rpb24oeCkgewogIGV4dHJhY3RfZnJvbV9lc3VtbWFyeSh4LCBlbGVtZW50cyA9IGMoImNvbXBsZXRlbmVzcyIpLCBzaW1wbGlmeSA9IEZBTFNFKSAlPiUgCiAgICBiaW5kX3Jvd3MoLmlkID0gInNpZCIpICU+JSAKICAgIHJlbmFtZShwQ29tcGxldGUgPSBjb21wbGV0ZW5lc3MpCn0pICU+JSBiaW5kX3Jvd3MoKQpgYGAKCk5vdyByZW1vdmUgZHVwbGljYXRlcyAocmVzdWx0aW5nIGZyb20gdGhlIGRpZmZlcmVudCBxdWVyaWVzKSBhbmQgZmlsdGVyIGJ5IHNwZWNpZXMgKHRob3NlIHRoYXQgb3ZlcmxhcCB0aGUgU2hlbiAyMDE4IGxpc3QgcGx1cyB0aGUgTURSIGNsYWRlKQpgYGB7cn0Kc3BzLmluY2x1ZGUgPC0gYyhzcHNOYW1lU3luJG5jYmkubmFtZSwgIkNhbmRpZGEgaGFlbXVsb25pIiwgIkNhbmRpZGEgZHVvYnVzaGFlbXVsb25pcyIsICJDYW5kaWRhIHBzZXVkb2hhZW11bG9uaWkiKQpkZWR1cC5yZWYgPC0gcmVmc2VxICU+JSAKICBmaWx0ZXIoc3BlY2llcyAlaW4lIHNwcy5pbmNsdWRlLCBxY292cyA+PSA1MCkgJT4lIAogIGdyb3VwX2J5KHNpZCkgJT4lIAogIGZpbHRlcihldmFsdWUgPT0gbWluKGV2YWx1ZSkpICU+JSAKICBsZWZ0X2pvaW4obXJuYUluZm8sIGJ5ID0gYygic2lkIiA9ICJzaWQiKSkgJT4lIAogIGxlZnRfam9pbihwSW5mbywgYnkgPSBjKCJzaWQiID0gInNpZCIpKSAlPiUgCiAgc2VsZWN0KHF1ZXJ5LCBzaWQsIHNsZW4sIHBDb21wbGV0ZSwgdElELCB0Q29tcGxldGUsIHNwZWNpZXMsIHN0cmFpbiwgZXZlcnl0aGluZygpKQpgYGAKCgpTdW1tYXJpemUgdGhlIGRlZHVwbGljYXRlZCBsaXN0IC0gZm9yIGVhY2ggc3BlY2llcywgcmVjb3JkIHRoZSB0b3RhbCBudW1iZXIgb2YgaGl0cywgdGhvc2UgdGhhdCBhcmUgbGFiZWxlZCBhcyBpbmNvbXBsZXRlIGluIHRoZSBwcm90ZWluIHJlY29yZCwgdGhvc2UgdGhhdCBhcmUgYWJvdmUgNTAwIGFhIGFuZCB0aGUgbnVtYmVyIG9mIGhpdHMgYmVsb3cgNTAwIGFhIHRoYXQgYXJlIGxhYmVsZWQgYXMgaW5jb21wbGV0ZQpgYGB7cn0Kc3VtLnJlZiA8LSBkZWR1cC5yZWYgJT4lIAogIGdyb3VwX2J5KHNwZWNpZXMsIHN0cmFpbikgJT4lIAogIHN1bW1hcml6ZSgKICAgIHRvdGFsID0gbigpLAogICAgaW5jb21wbCA9IHN1bShwQ29tcGxldGUgIT0gIiIpLAogICAgZ3Q1MDAgPSBzdW0oc2xlbiA+PSA1MDApLAogICAgc2hvcnRJbmNvbXBsID0gc3VtKHBDb21wbGV0ZSAhPSAiIiAmIHNsZW4gPCA1MDApCiAgKQpzdW0ucmVmICU+JSBhcnJhbmdlKGRlc2Moc2hvcnRJbmNvbXBsKSkKYGBgCj4gLSBfUy4gc3RpcGl0aXNfIGFuZCBfTS4gYmljdXNwaWRhdGFfIGhhZCBhIGxhcmdlIG51bWJlciBvZiBpbmNvbXBsZXRlIHByb3RlaW5zLCBsaWtlbHkgYSByZWZsZWN0aW9uIG9mIHRoZSBnZW5vbWUgYXNzZW1ibHkgc3RhdHVzLiBGb3IgX00uIGJpY3VzcGlkYXRhXywgYSBuZXdlciBJbGx1bWluYSArIE94Zm9yZCBOYW5vcG9yZSBbYXNzZW1ibHldKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvYXNzZW1ibHkvR0NBXzAyMjU3NTk1NS4xLykgaXMgYXZhaWxhYmxlIGZvciB0aGlzIHNwZWNpZXMgYnV0IGlzIG9ubHkgYXNzZW1ibGVkIHRvIHRoZSBjb250aWcgbGV2ZWwsIHdpdGggbm8gZ2VuZSBhbm5vdGF0aW9uLiBJIHRyaWVkIGJsYXN0aW5nIHRoZSByZWZzZXEgaGl0cyBmcm9tIHRoZSBzcGVjaWVzIGFnYWluc3QgdGhlIG5ldyBhc3NlbWJseSBidXQgdGhlIG1hdGNoIHdhcyBwb29yIGZvciBtb3N0IG9mIHRoZSBoaXRzIC0gaW5zdGVhZCBvZiBnZXR0aW5nIGEgbG9uZywgY29udGlndW91cyBIU1AsIEkgZ290IG1vc3RseSBicm9rZW4gcGllY2VzIHdpdGggbG90cyBvZiBtaXNtYXRjaGVzLiBJIHN1c3BlY3QgdGhpcyBpcyBkdWUgdG8gYSBjb21iaW5hdGlvbiBvZiB0YW5kZW0gZ2VuZSBhcnJheXMgY2F1c2luZyBkaWZmaWN1bHR5IGZvciBhc3NlbWJseSBhcm91bmQgdGhlIEhpbCBob21vbG9ncyBsb2NpIGFuZCB0aGUgc2VxdWVuY2luZyBkZXB0aCBhbmQgYXNzZW1ibHkgaXRzZWxmLiBCYXNlZCBvbiB0aGVzZSBvYnNlcnZhdGlvbnMsIEkgcGxhbiB0byBpbmNsdWRlIHRoZSBzcGVjaWVzIGluIHRoZSBDQUZFIGFuYWx5c2lzIGFuZCBwb3RlbnRpYWxseSBpbiB0aGUgcGh5bG9nZW5ldGljIHJlY29uc3RydWN0aW9uIGFzIHdlbGwsIGJ1dCBleGNsdWRlIGl0IGZyb20gdGhlIGhvbW9sb2cgcHJvdGVpbiB0cmFpdCBhbmFseXNlcy4KPiAtIEZvciBfUy4gc3RpcGl0aXNfLCBJIGlkZW50aWZpZWQgYSBbbmV3ZXIgYXNzZW1ibHldKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvYXNzZW1ibHkvR0NBXzAxNjg1OTI5NS4xLykgZm9yIGEgZGlmZmVyZW50IHN0cmFpbiAoTlJSTCBZLTcxMjQpIGFuZCBwZXJmb3JtZWQgbG9jYWwgYmxhc3QgYWdhaW5zdCB0aGUgYW5ub3RhdGVkIHByb3RlaW4gc2VxdWVuY2VzLiBJIGlkZW50aWZpZWQgYSB0b3RhbCBvZiAxMyBoaXRzLCBvZiB3aGljaCBzZXZlbiBhcmUgYWJvdmUgdGhlIDUwJSBxdWVyeSBjb3ZlcmFnZSB0aHJlc2hvbGQuIFRoaXMgaXMgcXVpdGUgZGlmZmVyZW50IGZyb20gdGhlIDE4IGhpdHMgaWRlbnRpZmllZCBpbiB0aGUgcmVmc2VxIGFzc2VtYmx5LiBXaGVuIEkgYmxhc3RlZCB0aGUgcmVmc2VxIGhpdHMgYWdhaW5zdCB0aGUgbmV3IGFzc2VtYmx5LCBzb21lIG9mIHRoZW0gaGFkIHBvb3IgbWF0Y2hlcy4gV2l0aG91dCBmdXJ0aGVyIGludmVzdGlnYXRpb24sIGl0IGlzIG5vdCBjbGVhciB3aGF0IGlzIHRoZSBjYXVzZSBvZiB0aGUgZGlzY3JlcGFuY3kuIEkgcGxhbiB0byB1c2UgdGhlIG5ldyBhc3NlbWJseSBoaXRzIGZvciBkb3duc3RyZWFtIGFuYWx5c2VzIGdpdmVuIHRoYXQgaXQgaXMgbW9yZSByZWNlbnRseSBzZXF1ZW5jZWQuCj4gLSBGb3IgdGhlIHNhbWUgcmVhc29ucyBhcyBhYm92ZSwgSSBwbGFuIHRvIGluY2x1ZGUgX1kuIHRlbnVpc18sIF9TLiB0YW56YXdhZW5zaXNfIGFuZCBfQy4gamFkaW5paV8gaW4gdGhlIENBRkUgYW5hbHlzaXMgYnV0IGV4Y2x1ZGUgdGhlbSBmcm9tIHRoZSBwcm90ZWluIGZlYXR1cmUgYW5hbHlzZXMuIEdpdmVuIHRoYXQgdGhleSBoYXZlIHJlbGF0aXZlbHkgZmV3IGhpdHMsIEkgY291bGQgY29uc2lkZXIgbWFudWFsbHkgc2VhcmNoaW5nIGZvciB0aGUgZnVsbCBsZW5ndGggT1JGLCBidXQgdGhhdCdzIG9wdGlvbmFsLgo+IC0gRm9yIF9ELiBmYWJyeWlfLCB0aGUgWS0xMDAwIHBsdXMgcHJvamVjdCBoYXMgc2VxdWVuY2VkIGFub3RoZXIgc3RyYWluIGZyb20gdGhpcyBzcGVjaWVzLiBJIGJsYXN0ZWQgYWdhaW5zdCB0aGF0IGFzc2VtYmx5IGFuZCBpZGVudGlmaWVkIHR3byBtYXRjaGVzLiBCdXQgdGhlIHR3byBoaXRzIGFwcGVhciB0b28gc2ltaWxhciB0byBlYWNoIG90aGVyLCBsZWF2aW5nIG1lIHdvbmRlciBpZiB0aGV5IGFyZSB0cnVseSBkaXN0aW5jdCBsb2NpLCBvciBhcmUgdGhleSBwYXJ0aWFsbHkgZHVwbGljYXRlIGNvbnRpZ3MuCgpIaXRzIGJhc2VkIG9uIHRoZSBuZXcgX1MuIHN0aXBpdGlzXyBhc3NlbWJseQpgYGB7cn0Kcy5zdGlwaXRpcyA8LSByZWFkX3RzdigiLi4vLi4vZGF0YS9sb25nLXJlYWQtYXNzZW1ibGllcy9zX3N0aXBpdGlzX05SUkxfWTcxMjQvUy1zdGlwaXRpcy1OUlJMLVk3MTI0LUhpbC1ob21vbG9ncy1ibGFzdC50eHQiLAogICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSAiIyIsIGNvbF9uYW1lcyA9IGhlYWRlclsxOjEwXSwgY29sX3R5cGVzID0gY29scygpKSAlPiUgCiAgbXV0YXRlKHNwZWNpZXMgPSAiU2NoZWZmZXJzb215Y2VzIHN0aXBpdGlzIiwgc3RyYWluID0gIk5SUkwgWS03MTI0Iiwgc3RheGlkID0gIjkxMzEzOCIsCiAgICAgICAgIHBpZGVudCA9IG51bShwaWRlbnQsIGRpZ2l0cyA9IDEpKSAlPiUgCiAgcmVsb2NhdGUoc3BlY2llczpzdHJhaW4sIC5hZnRlciA9IHBpZGVudCkgJT4lIAogIGdyb3VwX2J5KHNpZCkgJT4lIAogIGZpbHRlcihldmFsdWUgPT0gbWluKGV2YWx1ZSkpCmBgYAoKIyMjIDQuIF9DYW5kaWRhIGdsYWJyYXRhXyBoaXRzClRoZSBDb3JtYWNrIGxhYiByZWxlYXNlZCBuZXcgYXNzZW1ibGllcyBmb3IgYSBjb3VwbGUgb2YgX0MuIGdsYWJyYXRhXyBzdHJhaW5zIGluIFh1IDIwMjAgYW5kIFh1IDIwMjEgKFBNSURzOiAzMjA2ODMxNCwgMzM3MTMzNzIpLiBUaGVzZSBhc3NlbWJsaWVzIHdlcmUgYmFzZWQgb24gbG9uZy1yZWQgc2VxdWVuY2luZyB0ZWNobm9sb2dpZXMgc3VwcGxlbWVudGVkIHdpdGggc2hvcnQgcmVhZHMgZm9yIHBvbGlzaGluZyB0aGUgc2VxdWVuY2VzLiBUaGV5IGFsc28gcGFpZCBzcGVjaWFsIGF0dGVudGlvbiB0byB0aGUgc3VidGVsb21lcmVzLCB1c2luZyB2YXJpb3VzIG1lYW5zIHRvIHZlcmlmeSB0aGF0IHRoZWlyIG5ld2x5IGFzc2VtYmxlZCBzdWJ0ZWxvbWVyaWMgcmVnaW9ucyBhcmUgY29ycmVjdC4gUGVyZm9ybWluZyBibGFzdHAgc2VhcmNoZXMgYWdhaW5zdCB0aGVzZSBhc3NlbWJsaWVzIChJIHRlc3RlZCB0d28gb2YgdGhlbTogQVRUQzIwMDE9Q0JTMTM4IGFuZCBCRzIpIHJlY292ZXJlZCAxMyBpbnN0ZWFkIG9mIDMgaGl0cyAoaW4gdGhlIG5ldyByZWZzZXEgaGl0IGxpc3QpLiBJIHZlcmlmaWVkIHRoYXQgYWxsIG9mIHRoZW0gYXJlIGNyZWRpYmxlLiBBY3R1YWxseSwgWHUgX2V0IGFsLl8gMjAyMCBzdXBwbGVtZW50YXJ5IHRhYmxlIHNwZWNpZmljYWxseSBsaXN0ZWQgdGhlc2UgMTMgYXMgcHJlZGljdGVkIGFkaGVzaW5zIGJlbG9uZ2luZyB0byBjbHVzdGVyIFYsIHdoaWNoIGFsc28gaW5jbHVkZWQgQXdwMiBzdHVkaWVkIGJ5IFJlaXRob2ZlciBldCBhbCAyMDIxLiBJbmNsdWRpbmcgdGhlbSBpbiB0aGUgaG9tb2xvZ3MgbGlzdCB3b3VsZCBwcm9wZXJseSByZWZsZWN0IHRoZSBmYW1pbHkgc2l6ZSBpbiB0aGUgc3BlY2llcywgYnV0IHdpbGwgYWxzbyBoaWdobGlnaHQgdGhlIGlzc3VlIHRoYXQgaW5jb21wbGV0ZWx5IGFzc2VtYmxlZCBzdWJ0ZWxvbWVyZXMgbWF5IGhhdmUgbGVkIHRvIG1hbnkgZmFsc2UgbmVnYXRpdmVzIGluIHRoZSBnZW5lIGZhbWlseSBpbnZlbnRvcnkuIEkgdGhpbmsgaXQgaXMgd29ydGggaW5jbHVkaW5nIGl0IGFuZCBjb21tZW50aW5nIG9uIHRoZSBjYXZlYXRzLgoKYGBge3J9CmMuZ2xhYnJhdGEgPC0gcmVhZF90c3YoIi4uLy4uL2RhdGEvbG9uZy1yZWFkLWFzc2VtYmxpZXMvY19nbGFicmF0YV9DQlMxMzgvQy1nbGFicmF0YS1DQlMxMzgtSGlsLWhvbW9sb2dzLWJsYXN0LnR4dCIsCiAgICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9ICIjIiwgY29sX25hbWVzID0gaGVhZGVyWzE6MTBdLCBjb2xfdHlwZXMgPSBjb2xzKCkpICU+JSAKICBtdXRhdGUoc3BlY2llcyA9ICJDYW5kaWRhIGdsYWJyYXRhIiwgc3RyYWluID0gIkNCUyAxMzgiLCBzdGF4aWQgPSAiNTQ3OCIsCiAgICAgICAgIHBpZGVudCA9IG51bShwaWRlbnQsIGRpZ2l0cyA9IDEpKSAlPiUgCiAgcmVsb2NhdGUoc3BlY2llczpzdHJhaW4sIC5hZnRlciA9IHBpZGVudCkgJT4lIAogIGdyb3VwX2J5KHNpZCkgJT4lIAogIGZpbHRlcihldmFsdWUgPT0gbWluKGV2YWx1ZSkpIyAlPiUgCiAgI3JlbmFtZShwaWQgPSBzaWQpCgpjLmdsYWJyYXRhCmBgYAoKTm90ZSB0aGF0IEkgYWxzbyBjaGVja2VkIHRocmVlIG90aGVyIHNwZWNpZXMgZm9yIHdoaWNoIEkgY2FuIGZpbmQgYSByZWNlbnQgYXNzZW1ibHkgdGhhdCB1c2VkIGxvbmctcmVhZCB0ZWNobmxvZ29pZXMuIE5vbmUgb2YgdGhlbSBsZWQgdG8gc28gbWFueSBtb3JlIGhpdHMgYXMgdGhlIF9DLiBnbGFicmF0YV8gYXNzZW1ibHkgZGlkLiBJbiBmYWN0LCBvbmx5IHRoZSBfQy4gbml2YXJpZW5zaXNfIGFzc2VtYmx5IHlpZWxkZWQgb25lIG1vcmUgaGl0IHRoYW4gdGhlIG9sZGVyLCBzaG9ydC1yZWFkIGJhc2VkIGFzc2VtYmx5LiBUaGVyZSBtYXkgYmUgdHdvIHBvc3NpYmlsaXRpZXM6IDEpIG5vdCBhbGwgbmV3IGFzc2VtYmxpZXMgYXJlIGNyZWF0ZWQgZXF1YWwuIHRoZXJlIGFyZSBzb21lIHNwZWNpYWwgcHJvcGVydGllcyBvZiB0aGUgX0MuIGdsYWJyYXRhXyBhc3NlbWJseSB0aGF0IG1hZGUgaXRzIHN1YnRlbG9tZXJpYyByZWdpb25zIGVzcGVjaWFsbHkgd2VsbCBhc3NlbWJsZWQ7IDIpIHRoZXJlIGlzIHNvbWV0aGluZyBzcGVjaWFsIGFib3V0IF9DLiBnbGFicmF0YV8gaW4gdGhhdCB0aGUgSGlsIGZhbWlseSBleHBhbmRlZCBtb3JlIHNpZ25pZmljYW50bHkgdGhhbiBldmVuIHRoZSBjbG9zZWx5IHJlbGF0ZWQgTmFrYXNlb215Y2VzIHNwZWNpZXMuIEFzIG9mIG5vdyB3ZSBjYW5ub3QgZGlzdGluZ3Vpc2ggYmV0d2VlbiB0aGVzZSB0d28uCgojIyMgNS4gRnVuZ2lEQiBoaXRzCkZ1bmdpREIgZG9lc24ndCBpbmNsdWRlIGFueSBzcGVjaWVzIG5vdCBhbHJlYWR5IGluIHRoZSByZWZzZXEgZGF0YWJhc2UuIFRoZSBoaXRzIHRoYXQgd2VyZSBhZGRlZCB0byB0aGUgcmVmc2VxIGhpdHMgZnJvbSB0aGUgZWFybGllciBhbmFseXNpcyBhcmUKCmBgYHtyfQpwcmV2LmZkYi5hZGQgPC0gdHJpYmJsZSgKICB+Z2lkLCB+cGlkLCB+c3BlY2llcywKICAiQ0xVR18wNTIzMyIsICJYUF8wMDI2MTUyMTguMSIsICJDbGF2aXNwb3JhIGx1c2l0YW5pYWUiLAogICJDUEFSMl82MDA0MzAiLCAiWFBfMDM2NjYyODE1LjEiLCAiQ2FuZGlkYSBwYXJhcHNpbG9zaXMiLAogICJDUEFSMl84MDYzOTAiLCAiWFBfMDM2NjY1MjYyLjEiLCAiQ2FuZGlkYSBwYXJhcHNpbG9zaXMiLAogICJDUEFSMl84MDY0MjAiLCAiWFBfMDM2NjY1MjY1LjEiLCAiQ2FuZGlkYSBwYXJhcHNpbG9zaXMiLAogICJCOUowOF8wMDQwOTgiLCAiUElTNTI0ODEuMSIsICJDYW5kaWRhIGF1cmlzIgopCnByZXYuZmRiLmFkZCAlPiUgZmlsdGVyKCFwaWQgJWluJSBkZWR1cC5yZWYkc2lkKQpgYGAKPiBUaGUgZmlyc3QgZm91ciBhcmUgbm90IG1pc3NpbmcsIGp1c3QgYSBkaWZmZXJlbmNlIGluIHRoZSBJRCBmb3JtYXQuCj4gVGhlIG1pc3NpbmcgX0MuIGF1cmlzXyBzZXF1ZW5jZSBpcyBrbm93biB0byB1cyBhbmQgd2lsbCBiZSBhZGRlZCByZWdhcmRsZXNzLgo+IEJhc2VkIG9uIHRoZSBhYm92ZSwgd2Ugd2lsbCBubyBsb25nZXIgY29uc3VsdCB0aGUgRnVuZ2lEQiB0byBzdXBwbGVtZW50IHRoZSByZWZzZXEgaGl0cy4KCmBgYHtyfQpjLmF1cmlzIDwtIHJlYWRfdHN2KCIuLi8uLi9vdXRwdXQvWFBfMDI4ODg5MDMzLUNhdXJpcy1maXZlLXN0cmFpbnMtYmxhc3QudHh0IiwKICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gIiMiLCBjb2xfdHlwZXMgPSBjb2xzKCkpICU+JSAKICBmaWx0ZXIocXNlcWlkID09ICJYUF8wMjg4ODkwMzMuMV9QRjExNzY1Iiwgc3NlcWlkID09ICJQSVM1MjQ4MS4xIikgJT4lIAogIHJlbmFtZShzaWQgPSBzc2VxaWQpICU+JSAKICBtdXRhdGUoCiAgICBwaWQgPSBzaWQsCiAgICBxdWVyeSA9ICJDYXVyX0hpbDEiLCAKICAgICMgdGhlIGJsYXN0IHJlc3VsdCBmcm9tIHRoZSBhYm92ZSBmaWxlIHdhcyBkb25lIHVzaW5nIHRoZSBmaXJzdCAzMjUgYW1pbm8gYWNpZHMgYXMgcXVlcnkKICAgICMgSSBtYW51YWxseSBkaWQgdHdvIHNlcXVlbmNlIGJsYXN0IHdpdGggdGhlIFBGMTE3NjUgZnJvbSBIaWwxIGFzIHRoZSBxdWVyeSBhbmQgZW50ZXJlZCAKICAgICMgdGhlIHF1ZXJ5IGFuZCBzdWJqZWN0IHN0YXJ0IGFuZCBlbmQgcG9zaXRpb25zIGJlbG93CiAgICBxLnN0YXJ0ID0gMTIsIHEuZW5kID0gMzI2LCBzLnN0YXJ0ID0gMTEsIHMuZW5kID0gMzI1LAogICAgcWNvdnMgPSAocS5lbmQgLSBxLnN0YXJ0ICsgMSkvMzI1KjEwMCwKICAgIHNsZW4gPSAxMjQ3LAogICAgc3BlY2llcyA9ICJDYW5kaWRhIGF1cmlzIiwKICAgIHN0YXhpZCA9ICI0OTgwMTkiCiAgKSAlPiUgCiAgc2VsZWN0KHF1ZXJ5LCBzaWQsIHNsZW4sIHNwZWNpZXMsIHFjb3ZzLCBwaWRlbnQsIHEuc3RhcnQsIHEuZW5kLCBzLnN0YXJ0LCBzLmVuZCwgZXZhbHVlLCBzdGF4aWQsIHBpZCkKYGBgCgpJIG5vdyBjb25zaWRlciB0aGUgcmVmc2VxIGxpc3QgY29tcGxldGUsIHdlIHdpbGwgZ2F0aGVyIHRoZSBmaW5hbCBsaXN0IG9mIElEcyBhbmQgcmV0cmlldmUgdGhlaXIgc2VxdWVuY2VzLgoKUmV0cmlldmUgcHJvdGVpbiBpbmZvIChubyBtUk5BIGluZm9ybWF0aW9uIGlzIGF2YWlsYWJsZSkKYGBge3J9CmlkLmFkZCA8LSBjKGMuZ2xhYnJhdGEkc2lkLCBzLnN0aXBpdGlzJHNpZCwgYy5hdXJpcyRzaWQpCnBTdW0xIDwtIHJldHJpZXZlX3Byb3RlaW5faW5mbyhpZC5hZGQpCiMgZXh0cmFjdCB0aGUgcHJvdGVpbiBjb21wbGV0ZW5lc3MgaW5mbwpwSW5mbzEgPC0gZXh0cmFjdF9mcm9tX2VzdW1tYXJ5KHBTdW0xLCBlbGVtZW50cyA9IGMoImNvbXBsZXRlbmVzcyIpLCBzaW1wbGlmeSA9IEZBTFNFKSAlPiUgCiAgICBiaW5kX3Jvd3MoLmlkID0gInNpZCIpICU+JSAKICAgIHJlbmFtZShwQ29tcGxldGUgPSBjb21wbGV0ZW5lc3MpCmBgYAoKUmVwbGFjZSBfQy4gZ2xhYnJhdGFfIGFuZCBfUy4gc3RpcGl0aXNfIGluIHJlZnNlcSBoaXQgbGlzdC4gQWRkIHRoZSBfQy4gYXVyaXNfIEhpbDQgZnJvbSBCODQ0MS4KYGBge3J9CmRlZHVwLnJlZi5jZyA8LSBkZWR1cC5yZWYgJT4lIAogIGZpbHRlcighc3BlY2llcyAlaW4lIGMoIkNhbmRpZGEgZ2xhYnJhdGEiLCAiU2NoZWZmZXJzb215Y2VzIHN0aXBpdGlzIikpICU+JSAKICBiaW5kX3Jvd3MoZmlsdGVyKHMuc3RpcGl0aXMsIHFjb3ZzID49IDUwKSkgJT4lIAogIGJpbmRfcm93cyhmaWx0ZXIoYy5nbGFicmF0YSwgcWNvdnMgPj0gNTApKSAlPiUgCiAgYmluZF9yb3dzKGZpbHRlcihjLmF1cmlzLCBxY292cyA+PSA1MCkpICU+JSAKICByb3dzX3VwZGF0ZShwSW5mbzEsIGJ5ID0gInNpZCIsIHVubWF0Y2hlZCA9ICJpZ25vcmUiKSAlPiUgCiAgbXV0YXRlKHBpZCA9IGdzdWIoIi4qXFx8KC4qKVxcfC4qIiwgIlxcMSIsIHNpZCkpCmBgYAoKClJldHJpZXZlIHNlcXVlbmNlcwpgYGB7cn0KaXRlci5waWQxIDwtIHNwbGl0KHggPSBkZWR1cC5yZWYuY2ckcGlkLCBmID0gY2VpbGluZyhzZXFfYWxvbmcoZGVkdXAucmVmLmNnJHBpZCkvNTApKQpyZWYuc2VxIDwtIGxhcHBseShpdGVyLnBpZDEsIGZ1bmN0aW9uKHgpewogIGVudHJlel9mZXRjaChkYiA9ICJwcm90ZWluIiwgaWQgPSB4LCByZXR0eXBlID0gImZhc3RhIikKfSkKYGBgCgpgYGB7cn0KIyBtZXJnZSBhbGwgdGhlIHNlcXVlbmNlcyBhbmQgd3JpdGUgdG8gZmlsZQpyZWYuc2VxICU+JSBwYXN0ZTAoY29sbGFwc2UgPSAiIikgJT4lIAogIHdyaXRlKGZpbGUgPSAiLi4vLi4vb3V0cHV0LzIwMjIwNTAzLWV4cGFuZGVkLWJsYXN0LXJlZnNlcS1ob21vbG9ncy5mYWEiKQpgYGAKCiMjIyA2LiBHUllDIGhpdHMKR1JZQyBkaWQgaW5jbHVkZSBhIGZldyBtb3JlIHNwZWNpZXMsIGVzcGVjaWFsbHkgaW4gdGhlIE5ha2FzZW9teWNlcyBhbmQgTGFjaGFuY2VhIGdlbmVyYS4gV2hpbGUgdGhlIHNhbWUgYXNzZW1ibHkgZGF0YSBhcHBlYXIgdG8gZXhpc3QgaW4gdGhlIG5jYmkgZGF0YWJhc2UsIHRoZXkgYXJlIGNlcnRhaW5seSBub3QgaW5jbHVkZWQgaW4gdGhlIHJlZnNlcSBkYXRhYmFzZS4gTW9yZW92ZXIsIGV2ZW4gd2hlbiB0aGUgYXNzZW1ibGllcyBhcmUgcHJlc2VudCBpbiB0aGUgbmNiaSBkYXRhYmFzZSwgdGhlcmUgYXJlIG9ubHkgdGhlIGdlbm9taWMgc2VxdWVuY2VzLCBub3QgdHJhbnNsYXRlZCBDRFMsIHdoaWNoIGxpa2VseSBtYWtlIHRoZW0gdW5hdmFpbGFibGUgdG8gYmxhc3RwIHNlYXJjaGVzLiBUaGVyZWZvcmUgSSBkb3dubG9hZGVkIHRoZSBmaXZlIG5vbi1nbGFicmF0YSBOYWthc2VvbXljZXMgZ2Vub21lcyBwbHVzIG9uZSBMYWNoYW5jZWEgc3BlY2llcywgX0wuIGtsdXl2ZXJpXyBhbmQgcGVyZm9ybWVkIGxvY2FsIGB0Ymxhc3RuYCBzZWFyY2hlcy4gQmVsb3cgSSB3aWxsIHByb2Nlc3MgdGhlIHJlc3VsdHMgYW5kIGRlY2lkZSB3aGljaCBvbmVzIHRvIGluY2x1ZGUgaW4gdGhlIGZpbmFsIGxpc3QuCmBgYHtyfQpncnljLnNwcyA8LSB0cmliYmxlKAogIH5zcHMuYWJyLCB+c3BlY2llcywgfnN0cmFpbiwgfnN0YXhpZCwKICAiQ0FOSSIsICJDYW5kaWRhIG5pdmFyaWVuc2lzIiwgIkNCUyA5OTgzIiwgIjEyNzkxMTUiLAogICJOQURFIiwgIk5ha2FzZW9teWNlcyBkZWxwaGVuc2lzIiwgIkNCUyAyMTcwIiwgIjEyNzkxMTMiLAogICJOQUJBIiwgIk5ha2FzZW9teWNlcyBiYWNpbGxpc3BvcnVzIiwgIkNCUyA3NzIwIiwgIjEyNzkxMTYiLAogICJDQUNBIiwgIkNhbmRpZGEgY2FzdGVsbGlpIiwgIkNCUyA0MzMyIiwgIjEyNzkwODYiLAogICJDQUJSIiwgIkNhbmRpZGEgYnJhY2FyZW5zaXMiLCAiQ0JTIDEwMTU0IiwgIjEyNzkxMTQiLAogICJTQUtMIiwgIkxhY2hhbmNlYSBrbHV5dmVyaSIsICJDQlMgMzA4MiIsICI0OTM0IgopCgpncnljIDwtIHJlYWRfdHN2KCIuLi8uLi9kYXRhL05ha2FzZW9teWNlc19HUllDL05ha2FzZW9teWNlcy1IaWwtaG9tb2xvZ3MtYmxhc3QudHh0IiwKICAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gIiMiLCBjb2xfbmFtZXMgPSBoZWFkZXJbMToxMF0sIGNvbF90eXBlcyA9IGNvbHMoKSkgJT4lIAogIG11dGF0ZShzcHMuYWJyID0gZ3N1YigiQk5bMC05XSpfIiwgIiIsIHNpZCkgJT4lIHN0cl9zdWIoMSw0KSwgcGlkZW50ID0gbnVtKHBpZGVudCwgZGlnaXRzID0gMSkpICU+JSAKICBsZWZ0X2pvaW4oZ3J5Yy5zcHMsIGJ5ID0gInNwcy5hYnIiKSAlPiUgCiAgcmVsb2NhdGUoc3BlY2llczpzdHJhaW4sIC5hZnRlciA9IHBpZGVudCkgJT4lIAogIHNlbGVjdCgtc3BzLmFicikKCmRlZHVwLmdyeWMgPC0gZ3J5YyAlPiUgCiAgZ3JvdXBfYnkoc2lkKSAlPiUgCiAgZmlsdGVyKGV2YWx1ZSA9PSBtaW4oZXZhbHVlKSkgJT4lIAogIG11dGF0ZShwaWQgPSBnc3ViKCJCTlswLTldKl8iLCAiIiwgc2lkKSAlPiUgZ3N1YigiMV8xJCIsICIiLCAuKSkgJT4lIAogIGZpbHRlcihxY292cyA+PSA1MCkgJT4lIAogIHVuZ3JvdXAoKQpgYGAKCkV4dHJhY3QgdGhlIHByb3RlaW4gc2VxdWVuY2VzIGZyb20gdGhlIGNvbWJpbmVkIGZhc3RhCmBgYHtyfQpjYXQoZGVkdXAuZ3J5YyRzaWQsIGZpbGUgPSAiMjAyMjA1MDYtZ3J5Yy1kZWR1cGxpY2F0ZWQtaWQtZm9yLWV4dHJhY3RpbmctZmFzdGEudHh0Iiwgc2VwID0gIlxuIikKYGBgCgpSdW4gdGhlIGZvbGxvd2luZyBhdCB0aGUgY29tbWFuZCBsaW5lIGFzc3VtaW5nIHRoZSBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5IGlzIGAwMi1ibGFzdGAKYGBgYmFzaApweXRob24zIHNjcmlwdC9leHRyYWN0X2Zhc3RhX2d6LnB5IFwKICBkYXRhL05ha2FzZW9teWNlc19HUllDLzIwMjIwNTAzLWdyeWMtbmFrYXNlb215Y2VzLXByb3RlaW4uZmFhLmd6IFwKICBhbmFseXNpcy9leHBhbmRlZC1ibGFzdC8yMDIyMDUwNi1ncnljLWRlZHVwbGljYXRlZC1pZC1mb3ItZXh0cmFjdGluZy1mYXN0YS50eHQgXAogIG91dHB1dC8yMDIyMDUwNi1leHBhbmRlZC1ibGFzdC1ncnljLWhvbW9sb2dzLmZhYQpgYGAKCk1lcmdlIHRoZSBHUllDIHNlcXVlbmNlcyB3aXRoIHRoZSByZWZzZXEgc2VxdWVuY2VzIC0gcnVuIHRoZSBmb2xsb3dpbmcgYXQgdGhlIGNvbW1hbmQgbGluZQpgYGBiYXNoCmNhdCAyMDIyMDUwMy1leHBhbmRlZC1ibGFzdC1yZWZzZXEtaG9tb2xvZ3MuZmFhIFwKICAyMDIyMDUwNi1leHBhbmRlZC1ibGFzdC1ncnljLWhvbW9sb2dzLmZhYSBcCiAgPiAyMDIyMDUwNi1leHBhbmRlZC1ibGFzdC1jb21iaW5lZC1ob21vbG9ncy5mYWEKYGBgCgpDaGVjayB0byBtYWtlIHN1cmUgdGhhdCBubyBkdXBsaWNhdGVzIGV4aXN0LgoKYGBgCmJpb2F3ayAtYyBmYXN0eCAne3ByaW50ICRuYW1lfScgMjAyMjA1MDYtZXhwYW5kZWQtYmxhc3QtY29tYmluZWQtaG9tb2xvZ3MuZmFhIHwgc29ydCB8IHVuaXEgLWQKYGBgCgpJZiB0aGVyZSBpcyBubyBvdXRwdXQsIHRoZW4gdGhlcmUgaXMgbm8gZHVwbGljYXRlcy4gVGhlIGNvbW1hbmQgd2lsbCBvdXRwdXQgb25lIGxpbmUgcGVyIHNlcXVlbmNlIHRoYXQgb2NjY3VycyBtb3JlIHRoYW4gb25jZSBpbiB0aGUgZmlsZS4KCiMjIyA3LiBNZXJnZSBhbGwgdGhlIHNlcXVlbmNlcwpFeHBvcnQgdGhlIGNvbWJpbmVkIGhvbW9sb2dzIGxpc3QKYGBge3J9CmRlZHVwLm1lcmdlIDwtIGJpbmRfcm93cygKICBSZWZTZXEgPSBkZWR1cC5yZWYuY2csCiAgR1JZQyA9IGRlZHVwLmdyeWMsCiAgLmlkID0gInNvdXJjZSIKICApICU+JSAKICBtdXRhdGUobmFtZSA9IHBhc3RlKHBpZCwgZ3N1YigiICIsICJfIiwgc3BlY2llcyksIHNlcCA9ICJfIikpICU+JSAKICBzZWxlY3QoLXEuc3RhcnQsIC1xLmVuZCwgLXMuc3RhcnQsIC1zLmVuZCkgJT4lIAogIHVuZ3JvdXAoKQp3cml0ZV90c3YoZGVkdXAubWVyZ2UsIGZpbGUgPSAiLi4vLi4vb3V0cHV0LzIwMjIwNTA2LWV4cGFuZGVkLWJsYXN0LWNvbWJpbmVkLWhvbW9sb2dzLnRzdiIpCmBgYAoKIyMjIDguIENvbXBhcmUgd2l0aCB0aGUgZm9ybWVyIDEwNCBsaXN0CkhpdHMgaW4gdGhlIHByZXZpb3VzIHJlZnNlcSBibGFzdCBsaXN0IGJ1dCBub3QgaW4gdGhlIGN1cnJlbnQgb25lOgpgYGB7cn0KcDEwNCA8LSByZWFkX3RzdigiLi4vLi4vb3V0cHV0L1hQXzAyODg4OTAzM19ob21vbG9nc19jb21iaW5lX2JsYXN0X3N0YXQudHN2IiwgY29sX3R5cGVzID0gY29scygpKQpnZ3Zlbm4obGlzdChleHBhbmRlZF9ibGFzdCA9IGRlZHVwLm1lcmdlJHBpZCwgcHJldmlvdXNfMTA0ID0gcDEwNCRpZCkpCnAxMDQgJT4lIGZpbHRlcighaWQgJWluJSBnc3ViKCJCTlswLTldKl8iLCAiIiwgZGVkdXAubWVyZ2UkcGlkKSkgJT4lIGFycmFuZ2Uoc3BlY2llcykKYGBgCj4gLSBUaGUgZm91ciBfUy4gc3RpcGl0aXNfIHNlcXVlbmNlcyBoYXZlIGJlZW4gcmVwbGFjZWQgYnkgdGhlIG5ldyBhc3NlbWJseQo+IC0gVGhlIF9DLiBhdXJpc18sIF9DLiBnbGFicmF0YV8sIF9DLiBwYXJhc2lsb3Npc18gYW5kIF9DbGF2aXNwb3JhIGx1c2l0YW5pYWVfIHNlcXVlbmNlcyBhcmUgaW4gZmFjdCBhbGwgcHJlc2VudCBpbiB0aGUgbmV3IGxpc3QsIGp1c3QgdW5kZXIgZGlmZmVyZW50IElEcwoKIyMjIDkuIElkZW50aWZ5IHRoZSBQRjExNzY1IGRvbWFpbgpVc2UgSE1NRVIgdG8gaWRlbnRpZnkgdGhlIGxvY2F0aW9uIG9mIHRoZSBQRjExNzY1IGRvbWFpbi4gVGhlIGhtbSBtb2RlbCB3YXMgZG93bmxvYWRlZCBmcm9tIHRoZSBwZmFtIGRhdGFiYXNlIG9uIDIwMjItMDUtMDQuIFJ1biB0aGUgZm9sbG93aW5nIGNvbW1hbmQgaW4gYDAyLWJsYXN0YCBkaXJlY3RvcnkuCmBgYApobW1zY2FuIC0tbm9hbGkgLS1kb21FIDFlLTMgXAogIC1vIDIwMjIwNTA0LWhtbXNjYW4ubG9nIFwKICAtLWRvbXRibG91dCBvdXRwdXQvMjAyMjA1MDYtZXhwYW5kZWQtYmxhc3QtY29tYmluZWQtUEYxMTc2NS1obW1zY2FuLnR4dCBcCiAgZGF0YS9ITU0tcHJvZmlsZS9IeXBoYWxfcmVnX0NXUC5obW0gXAogIG91dHB1dC8yMDIyMDUwNi1leHBhbmRlZC1ibGFzdC1jb21iaW5lZC1ob21vbG9ncy5mYWEKYGBgCgpSZWFkIHRoZSByZXN1bHQKYGBge3Igd2FybmluZz1GQUxTRX0KaG1tLmhlYWRlciA8LSBjKCJ0YXJnZXQiLCAidGFjYyIsICJ0bGVuIiwgInBpZCIsICJwYWNjIiwgInNsZW4iLCAiZnVsbC5ldmFsdWUiLCAiZnVsbC5zY29yZSIsICJmdWxsLmJpYXMiLAogICAgICAgICAgICAgICAgIm51bSIsICJvZiIsICJjLmV2YWx1ZSIsICJpLmV2YWx1ZSIsICJkb21haW4uc2NvcmUiLCAiZG9tYWluLmJpYXMiLAogICAgICAgICAgICAgICAgImhtbS5mcm9tIiwgImhtbS50byIsICJhbGkuZnJvbSIsICJhbGkudG8iLCAiZW52LmZyb20iLCAiZW52LnRvIiwgImFjYyIpCmhtbSA8LSByZWFkX3RhYmxlKCIuLi8uLi9vdXRwdXQvMjAyMjA1MDYtZXhwYW5kZWQtYmxhc3QtY29tYmluZWQtUEYxMTc2NS1obW1zY2FuLnR4dCIsIAogICAgICAgICAgICAgICAgICBjb21tZW50ID0gIiMiLCBjb2xfbmFtZXMgPSBobW0uaGVhZGVyLCBjb2xfdHlwZXMgPSBjb2xzKCkpICU+JSAKICBtdXRhdGUoc2lkID0gcGlkLCBwaWQgPSBnc3ViKCJCTlswLTldKl8iLCAiIiwgcGlkKSAlPiUgZ3N1YigiMV8xJCIsICIiLCAuKSkgJT4lIAogIHNlbGVjdChzaWQsIHBpZCwgbnVtLCBvZiwgaS5ldmFsdWU6ZW52LnRvKQpkZWR1cC5tZXJnZSAlPiUgZmlsdGVyKCFwaWQgJWluJSBobW0kcGlkKQpgYGAKCkN1cmlvdXMgd2h5IGBobW1zY2FuYCBkaWRuJ3QgaWRlbnRpZnkgdGhlIFBGMTE3NjUgZG9tYWluIGluIHRoZSB0d28gc2VxdWVuY2VzLCBJIHN1Ym1pdHRlZCB0aGUgc2FtZSBzZXF1ZW5jZSBmaWxlIHRvIFtgQmF0Y2ggQ0Qtc2VhcmNoYF0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9TdHJ1Y3R1cmUvYndycHNiL2J3cnBzYi5jZ2kpLCB3aGljaCB1c2VzIHRoZSBSUFMtQkxBU1QgdG8gc2VhcmNoIGEgY29sbGVjdGlvbiBvZiBwcm90ZWluIHNlcXVlbmNlcyBmb3IgbWF0Y2hlcyB0byBhIHNldCBvZiBwcm9maWxlIG1hdHJpY2VzLiBJdCBpcyB0aGUgcmV2ZXJzZSBvZiB0aGUgUFNJLUJMQVNULgpgYGB7cn0KY2RzZWFyY2ggPC0gcmVhZF90c3YoIi4uLy4uL291dHB1dC8yMDIyMDUwOS1leHBhbmRlZC1ibGFzdC1jb21iaW5lZC1wZmFtLWNkLXNlYXJjaC1vdXRwdXQudHh0Iiwgc2tpcCA9IDcsCiAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IGNvbHMoKSkgJT4lIAogIGZpbHRlcihncmVwbCgiSHlwaGFsIiwgYFNob3J0IG5hbWVgKSkgJT4lCiAgbXV0YXRlKHNpZCA9IHN0cl9tYXRjaChRdWVyeSwgIj4oW2EtekEtWjAtOV8uXSopICIpWywyXSwKICAgICAgICAgcGlkID0gZ3N1YigiQk5bMC05XSpfIiwgIiIsIHNpZCkgJT4lIGdzdWIoIjFfMSQiLCAiIiwgLikpICU+JSAKICBzZWxlY3Qoc2lkLCBwaWQsIGV2ZXJ5dGhpbmcoKSwgLVF1ZXJ5KQpjZHNlYXJjaCAlPiUgZmlsdGVyKHBpZCAlaW4lIHNldGRpZmYoZGVkdXAubWVyZ2UkcGlkLCBobW0kcGlkKSkgJT4lIHNlbGVjdCgtc2lkKQpgYGAKPiBCb3RoIHNlcXVlbmNlcyBkaWQgaGF2ZSBhIG1hdGNoLCBhbHRob3VnaCBvbmUgb2YgdGhlbSBoYXMgYSAic3VwZXJmYW1pbHkiIGxldmVsIG1hdGNoLCBzdWdnZXN0aW5nIHRoYXQgbm8gaGl0cyBleGNlZWQgdGhlIGRvbWFpbiBsZXZlbCBFLXZhbHVlLCBidXQgbXVsdGlwbGUgaGl0cyBtZXQgdGhlICJub24tc3BlY2lmaWMiIGNyaXRlcmlhLiBOb3Qgc3VyZSB3aHkgaG1tc2NhbiBkaWRuJ3QgcGljayB1cCB0aGUgZG9tYWluIGluIHRoZSBfQy4gZ2xhYnJhdGFfIHNlcXVlbmNlIHRob3VnaAoKTWVyZ2UgdGhlIENELVNlYXJjaCBhbmQgSE1NU0NBTSByZXN1bHRzIGludG8gb25lLiBJJ20gdXNpbmcgdGhlIGVudmVsb3BlIGNvb3JkaW5hdGVzIGZyb20gYGhtbXNjYW5gIHJhdGhlciB0aGFuIHRoZSBhbGlnbm1lbnQgY29vcmRpbmF0ZXMuIEFjY29yZGluZyB0byBbUEZhbSBkb2N1bWVudGF0aW9uXShodHRwczovL3BmYW0tZG9jcy5yZWFkdGhlZG9jcy5pby9lbi9sYXRlc3QvZ2xvc3NhcnkuaHRtbCNhbGlnbm1lbnQtY29vcmRpbmF0ZXMpLCB0aGUgZW52ZWxvcGUgY29vcmRpbmF0ZXMgcmVmbGVjdCB0aGUgcmVnaW9uIHdoZXJlIHRoZSBtYXRjaCBoYXMgYmVlbiAqcHJvYmFiaWxpc3RpY2FsbHkqIGRldGVybWluZWQgdG8gbGllLiBJdCB3aWxsIGJlIHNsaWdodGx5IHdpZGVyIHRoYW4gdGhlIGFsaWdubWVudC4KYGBge3J9CmhtbS5jZCA8LSBjZHNlYXJjaCAlPiUgZmlsdGVyKGdyZXBsKCJIeXBoYWxfcmVnX0NXUCIsIGBTaG9ydCBuYW1lYCkpICU+JSAKICBzZWxlY3Qoc2lkLCBwaWQsIGNkLnR5cGUgPSBgSGl0IHR5cGVgLCBjZC5pbmNvbSA9IEluY29tcGxldGUsIGNkLmZyb20gPSBGcm9tLCBjZC50byA9IFRvKSAlPiUgCiAgZnVsbF9qb2luKHNlbGVjdChobW0sIHNpZCwgcGlkLCBhbGkuZnJvbSwgYWxpLnRvLCBlbnYuZnJvbSwgZW52LnRvKSkgJT4lIAogIG11dGF0ZShjZC5sZW4gPSBjZC50byAtIGNkLmZyb20gKyAxLAogICAgICAgICBhbGkubGVuID0gYWxpLnRvIC0gYWxpLmZyb20gKyAxLAogICAgICAgICBlbnYubGVuID0gZW52LnRvIC0gZW52LmZyb20gKyAxKQpobW0uY2QgJT4lIGdyb3VwX2J5KHBpZCkgJT4lIGZpbHRlcihuKCkgPiAxKSAlPiUgc2VsZWN0KC1zaWQsIC1lbmRzX3dpdGgoInRvIikpCmBgYAoKPiAtIEFib3ZlIGFyZSB0aGUgc2VxdWVuY2VzIHdpdGggbW9yZSB0aGFuIG9uZSBlbnRyaWVzIGZyb20gZWl0aGVyIHNlYXJjaC4gTm90ZSB0aGF0IHdpdGggdGhlIHBhcmFtZXRlcnMgdXNlZCBmb3IgQ0QtU2VhcmNoLCBpdCBhcHBlYXJzIHRoYXQgdGhlIGFsZ29yaXRobSBvbmx5IHJlcG9ydHMgdGhlIHN0cm9uZ2VzdCBtYXRjaCAoc3BlY2lmaWMpLiBUaGlzIGNhbiBwcm9iYWJseSBiZSBjaGFuZ2VkIGJ5IHN3aXRjaGluZyB0byB0aGUgZnVsbCByZXBvcnQgbW9kZS4gQnV0IGl0J3Mgbm90IGltcG9ydGFudCBhcyBJIHBsYW4gdG8gdXNlIHRoZSBITU0gZW52ZWxvcGUgY29vcmRpbmF0ZXMgd2hlbmV2ZXIgYXZhaWxhYmxlLgo+IC0gVGhlIHNlY29uZCBtYXRjaCBpbiBYUF8wMDM2NzE4OTIuMSBpcyB2ZXJ5IHNob3J0LiBSZW1vdmUgaXQuCj4gLSBUaGUgc2Vjb25kIG1hdGNoIGluIE5BREUwczAzZTAwMTEwZyBsYXJnZWx5IG92ZXJsYXBzIHdpdGggdGhlIGZpcnN0IG9uZS4gUmVtb3ZlLgoKYGBge3J9CmRvbWFpbi5maW5hbCA8LSBobW0uY2QgJT4lIAogICMgcmVtb3ZlIHRoZSBzaG9ydCBoaXQgYW5kIGR1cGxpY2F0ZXMgZHVlIHRvIHRoZSBqb2luIG9wZXJhdGlvbiwgYnV0IHNhdmUgdGhlIHJvd3Mgd2hlcmUgaG1tc2NhbiBkaWRuJ3QgZmluZCBhIG1hdGNoCiAgZmlsdGVyKGlzLm5hKGFsaS5mcm9tKSB8IChhYnMoYWxpLmZyb20gLSBjZC5mcm9tKSA8IDEwMDAgJiBhbGkubGVuID4gMTAwKSkgJT4lIAogIGZpbHRlcighKHBpZCA9PSAiTkFERTBzMDNlMDAxMTBnIiAmIGFsaS5mcm9tID09IDM5KSkgIyByZW1vdmUgdGhlIG92ZXJsYXBwaW5nIGVudHJ5CmRvbWFpbi5maW5hbCAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoY2QubGVuLCBlbnYubGVuKSwgdmFsdWVzX3RvID0gImxlbmd0aCIsIG5hbWVzX3RvID0gIm1ldGhvZCIpICU+JQogIG11dGF0ZShtZXRob2QgPSBmYWN0b3IobWV0aG9kLCBsZXZlbHMgPSBjKCJjZC5sZW4iLCAiZW52LmxlbiIpLCBsYWJlbHMgPSBjKCJDRC1TZWFyY2ggaGl0cyIsICJITU1TQ0FOIGhpdHMiKSkpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBsZW5ndGgpKSArIAogIGdlb21faGlzdG9ncmFtKGFlcyhmaWxsID0gY2QuaW5jb20gIT0gIi0iKSwgYmlucyA9IDMwKSArIAogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDMyOCwgbGluZXR5cGUgPSAyKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIoIkluY29tcGxldGUiLCB0eXBlID0gInF1YWwiKSArCiAgZmFjZXRfd3JhcCh+bWV0aG9kLCBucm93ID0gMiwgKSArIHRoZW1lX2Nvd3Bsb3QoKQpgYGAKPiBTb21lIGFyZSBpbmNvbXBsZXRlIG1hdGNoZXMuCgpgciBkb21haW4uZmluYWwgJT4lIGZpbHRlcihjZC5pbmNvbSAhPSAiLSIpYAoKV3JpdGUgdGhlIGNvb3JkaW5hdGVzIGludG8gYSBCRUQgZm9ybWF0dGVkIGZpbGUgZm9yIGV4dHJhY3RpbmcgdGhlIHNlcXVlbmNlcy4KYGBge3J9CmRvbWFpbi5maW5hbCAlPiUgCiAgbXV0YXRlKAogICAgIyBpZiBobW0gaGl0cyBhcmUgbm90IGF2YWlsYWJsZSwgdXNlIGNkLXNlYXJjaCBoaXQgY29vcmRpbmF0ZXMsIHdpdGggMyBicCBleHBhbnNpb24gb24gYm90aCBzaWRlcwogICAgIyBub3RlIHRoYXQgQkVEIGZvcm1hdCBpcyAwLWJhc2VkIGZvciB0aGUgc3RhcnQgcG9zaXRpb24KICAgIGZyb20gPSBpZmVsc2UoaXMubmEoZW52LmZyb20pLCBjZC5mcm9tIC0gMyAtIDEsIGVudi5mcm9tIC0gMSksIAogICAgdG8gPSBpZmVsc2UoaXMubmEoZW52LnRvKSwgY2QudG8gKyAzLCBlbnYudG8pKSAlPiUgCiAgc2VsZWN0KHNlcSA9IHNpZCwgZnJvbSwgdG8pICU+JSAKICAjIHRoZSBmb2xsb3dpbmcgc3RhdGVtZW50IGlzIHRvIHJlbW92ZSB0aGUgc2Vjb25kIFBGMTE3NjUgZG9tYWluIGZyb20gb25lIHByb3RlaW4gZm9yIHRoZSBwdXJwb3NlIG9mIGdlbmUgdHJlZQogIGdyb3VwX2J5KHNlcSkgJT4lIAogIGZpbHRlcihmcm9tID09IG1pbihmcm9tKSkgJT4lIAogIHdyaXRlX3RzdihmaWxlID0gIi4uLy4uL291dHB1dC8yMDIyMDUxMi1leHBhbmRlZC1ibGFzdC1jb21iaW5lZC1ob21vbG9ncy1QRjExNzY1LkJFRCIsIGNvbF9uYW1lcyA9IEZBTFNFKQpgYGAKCkFsc28gd3JpdGUgYSB2ZXJzaW9uIHdpdGggdGhlIGxvbmcgbmFtZSBhbmQgbm90IHJlbW92aW5nIHRoZSBzZWNvbmQgZG9tYWluIGZyb20gdGhlIG9uZSBzZXF1ZW5jZSwgZm9yIHRoZSBwdXJwb3NlIG9mIHBsb3R0aW5nIGFuZCBjYWxjdWxhdGluZyBzZXF1ZW5jZSBsZW5ndGggLSBQRjExNzY1CmBgYHtyfQpkb21haW4uZmluYWwgJT4lIAogIG11dGF0ZSgKICAgIGZyb20gPSBpZmVsc2UoaXMubmEoZW52LmZyb20pLCBjZC5mcm9tIC0gMyAtIDEsIGVudi5mcm9tIC0gMSksIAogICAgdG8gPSBpZmVsc2UoaXMubmEoZW52LnRvKSwgY2QudG8gKyAzLCBlbnYudG8pKSAlPiUgCiAgc2VsZWN0KHNlcSA9IHBpZCwgZnJvbSwgdG8pICU+JSAjIHNlbGVjdCBwaWQgZm9yIG1lcmdpbmcgd2l0aCBkZWR1cC5tZXJnZQogIGxlZnRfam9pbihzZWxlY3QoZGVkdXAubWVyZ2UsIHNlcSA9IHBpZCwgbmFtZSksIGJ5ID0gInNlcSIpICU+JSAKICB1bmdyb3VwICU+JSBzZWxlY3QobmFtZSwgZnJvbSwgdG8pICU+JSAKICB3cml0ZV90c3YoZmlsZSA9ICIuLi8uLi9vdXRwdXQvMjAyMjA2MjMtZXhwYW5kZWQtYmxhc3QtY29tYmluZWQtaG9tb2xvZ3MtUEYxMTc2NS1sb25nbmFtZS5CRUQiLCBjb2xfbmFtZXMgPSBGQUxTRSkKYGBgCgpUbyBleHRyYWN0IHRoZSBzZXF1ZW5jZSBtYXRjaGVzIHRvIHRoZSBQRjExNzY1IHByb2ZpbGUsIGV4ZWN1dGUgdGhlIGZvbGxvd2luZyBzaGVsbCBjb21tYW5kCmBgYApzZXF0ayBzdWJzZXEgMjAyMjA1MDYtZXhwYW5kZWQtYmxhc3QtY29tYmluZWQtaG9tb2xvZ3MuZmFhIFwKICAgICAgICAgICAgIDIwMjIwNTEyLWV4cGFuZGVkLWJsYXN0LWNvbWJpbmVkLWhvbW9sb2dzLVBGMTE3NjUuQkVEICA+IFwKICAgICAgICAgICAgIDIwMjIwNTEyLWV4cGFuZGVkLWJsYXN0LWNvbWJpbmVkLWhvbW9sb2dzLVBGMTE3NjUuZmFhCmBgYAoKIyMjIDEwLiBSZW5hbWUgdGhlIHNlcXVlbmNlcyBmb3IgZ2VuZSB0cmVlIHJlY29uc3RydWN0aW9uClRvIGZhY2lsaXRhdGUgZ2VuZSB0cmVlIHJlY29uc3RydWN0aW9uLCByZW5hbWUgdGhlIHNlcXVlbmNlIG5hbWVzIGluIHRoZSBmb3JtIG9mIElEX3NwZWNpZXNfbmFtZSwgYW5kIGdldCByaWQgb2YgdGhlIGRlc2NyaXB0aW9uCgpgYGB7cn0KcmVuYW1lciA8LSBkZWR1cC5tZXJnZSAlPiUgCiAgIyBhIGJpdCBjb21wbGljYXRlZDogaW4gdGhlIHNlcXVlbmNlIGZpbGUsIHNlcXVlbmNlIG5hbWVzIGZyb20gUmVmc2VxIHNlcXVlbmNlcyB3ZXJlIG1vZGlmaWVkIAogICMgd2hpbGUgc2VxdWVuY2VzIGZyb20gR1JZQyB3ZXJlIG5vdAogIG11dGF0ZShpZCA9IGlmZWxzZShzb3VyY2UgPT0gIlJlZlNlcSIsIHBpZCwgc2lkKSkgJT4lIAogIHNlbGVjdChpZCwgbmFtZSkgJT4lIAogIGRlZnJhbWUoKQpteUZhc3RhUmVuYW1lIDwtIGZ1bmN0aW9uKGZpbGUsIG91dCl7CiAgaW5wdXQgPC0gcmVhZC5mYXN0YShmaWxlLCBzZXF0eXBlID0gIkFBIiwgYXMuc3RyaW5nID0gVFJVRSkKICBpZihhbnkoZ3JlcGwoIjoiLCBuYW1lcyhpbnB1dCkpKSl7CiAgICAjIGRlYWwgd2l0aCB0aGUgc2VxdGsgb3V0cHV0CiAgICBuYW1lcyhpbnB1dCkgPC0gZ3N1YigiOi4qJCIsICIiLCBuYW1lcyhpbnB1dCkpCiAgfQogIGlmKCFhbGwobmFtZXMoaW5wdXQpICVpbiUgbmFtZXMocmVuYW1lcikpKXsKICAgIHdhcm5pbmcoIlRoZSBmb2xsb3dpbmcgc2VxdWVuY2VzIHdlcmUgbm90IGZvdW5kIGluIHRoZSByZW5hbWUgbGlzdDoiKQogICAgcHJpbnQoc2V0ZGlmZihuYW1lcyhpbnB1dCksIG5hbWVzKHJlbmFtZXIpKSkKICB9CiAgd3JpdGUuZmFzdGEoaW5wdXQsIG5hbWVzID0gcmVuYW1lcltuYW1lcyhpbnB1dCldLCBmaWxlLm91dCA9IG91dCwgbmJjaGFyID0gNTAwMCkKfQpgYGAKYGBge3J9Cm15RmFzdGFSZW5hbWUoZmlsZSA9ICIuLi8uLi9vdXRwdXQvMjAyMjA1MDYtZXhwYW5kZWQtYmxhc3QtY29tYmluZWQtaG9tb2xvZ3MuZmFhIiwgCiAgICAgICAgICAgICAgb3V0ID0gIi4uLy4uL291dHB1dC8yMDIyMDUwNi1leHBhbmRlZC1ibGFzdC1jb21iaW5lZC1ob21vbG9ncy1sb25nbmFtZS5mYWEiKQpteUZhc3RhUmVuYW1lKGZpbGUgPSAiLi4vLi4vb3V0cHV0LzIwMjIwNTEyLWV4cGFuZGVkLWJsYXN0LWNvbWJpbmVkLWhvbW9sb2dzLVBGMTE3NjUuZmFhIiwgCiAgICAgICAgICAgICAgb3V0ID0gIi4uLy4uL291dHB1dC8yMDIyMDUxMi1leHBhbmRlZC1ibGFzdC1jb21iaW5lZC1ob21vbG9ncy1QRjExNzY1LWxvbmduYW1lLmZhYSIpCmBgYAoKIyMjIDExLiBPYnRhaW4gc3VidHJlZXMgZm9yIHRoZSBzcGVjaWVzIGluY2x1ZGVkClRoaXMgaXMgZm9yIHRoZSBsYXR0ZXIgZ2VuZSB0cmVlIHJlY29uY2lsYXRpb24gYW5kIHBsb3R0aW5nLiAKCk91dHB1dCB0aGUgc3BlY2llcyB1c2VkIGFzIGEgc2luZ2xlIGNvbHVtbiB0ZXh0IGZpbGU6CmBgYHtyfQojIGFmdGVyIGFkZGluZyB0aGUgR1JZQyBzcGVjaWVzLCB3ZSBuZWVkIHRvIGV4cGFuZCB0aGUgc3BzLmluY2x1ZGUgbGlzdAojIGFkZCBTLiBjZXJldmlzaWFlIGZvciBzcGVjaWVzIHRyZWUgaWxsdXN0cmF0aW9ucwpzcHMuZmluYWwgPC0gYyh1bmlxdWUoZGVkdXAubWVyZ2Ukc3BlY2llcyksICJTYWNjaGFyb215Y2VzIGNlcmV2aXNpYWUiKQojIGNvbnZlcnQgdGhlIHNwZWNpZXMgbmFtZXMgdG8gdGhlIFNoZW4gMjAxOCBuYW1lCnNwcy5maW5hbC5zaGVuIDwtIHNwc05hbWVDb252ZXJ0ICU+JSAKICBtdXRhdGUobmNiaS5uYW1lID0gc3BlY2llcywKICAgICAgICAgc2hlbjIwMTggPSB3b3JkKGdzdWIoIl8iLCAiICIsIG9sZF9zcGVjZWlzX25hbWVzKSwgLTIsIC0xKSkgJT4lIAogIHNlbGVjdChuY2JpLm5hbWUsIHNoZW4yMDE4LCBgTWFqb3IgY2xhZGVgLCBGYW1pbHksIEdlbnVzKSAlPiUgCiAgZmlsdGVyKG5jYmkubmFtZSAlaW4lIHNwcy5maW5hbCkKY2F0KCJUaGUgZm9sbG93aW5nIHNwZWNpZXMgYXJlIG5vdCBwcmVzZW50IGluIHRoZSBTaGVuIDIwMTggbGlzdCBhbmQgbmVlZCB0byBiZSBhZGRlZCBsYXRlci4iKQpzZXRkaWZmKHNwcy5maW5hbCwgc3BzTmFtZUNvbnZlcnQkc3BlY2llcykKYGBgCgpgYGB7cn0Kc3BzLmZpbmFsLnNoZW4kc2hlbjIwMTggJT4lIG5hLm9taXQoKSAlPiUgZ3N1YigiICIsICJfIiwgLikgJT4lCiAgd3JpdGUoZmlsZSA9IHBhc3RlMCgiZGF0YS8iLCBnc3ViKCItIiwgIiIsIFN5cy5EYXRlKCkpLCAiLWV4cGFuZGVkLWJsYXN0LXNwZWNpZXMtbGlzdC50eHQiKSkKYGBgCgpHZXQgdGhlIHNwZWNpZXMgdHJlZSB1c2luZyBbVHJlZUhvdXNlXShodHRwczovL2dpdGh1Yi5jb20vSkxTdGVlbnd5ay90cmVlaG91c2UpCmBgYHtyfQojIGZpcnN0IGluc3RhbGwgc2hpbnkgYW5kIHBoeXRvb2xzIHBhY2thZ2UKc2hpbnk6OnJ1bkdpdEh1YigidHJlZWhvdXNlIiwgIkpMU3RlZW53eWsiKQpgYGAKCkkgdGhlbiBtYW51YWxseSBhZGRlZCB0aGUgbWlzc2luZyBzcGVjaWVzIGJ5IGVkaXRpbmcgdGhlIE5ld2ljayBmaWxlICh3aXRob3V0IGJyYW5jaCBsZW5ndGggdmVyc2lvbikuIFRoZSBmaW5hbCByZXN1bHQgaXMgc2hvd24gYmVsb3c6CiFbc3BlY2llcyB0cmVlXShkYXRhLzIwMjIwNTE1LWV4cGFuZGVkLWJsYXN0LXNwZWNpZXMtdHJlZS5wbmcpCgpBbHNvIGV4cG9ydCB0aGUgZ2VuZS10by1zcGVjaWVzIG1hcHBpbmcgZm9yIGBnZW5lcmF4YApgYGB7cn0KZ2VuZTJzcHMubWFwIDwtIGRlZHVwLm1lcmdlICU+JSAKICBzZWxlY3QobmFtZSwgc3BlY2llcykgJT4lIAogICMgbW9kaWZ5IHRoZSB0d28gc3BlY2llcyBuYW1lcyB0aGF0IGRpZmZlciBiZXR3ZWVuIHRoZSBnZW5lIG5hbWVzIGFuZCBzcGVjaWVzIHRyZWUKICBtdXRhdGUodHJlZU5hbWUgPSBnc3ViKCIgIiwgIl8iLCBzcGVjaWVzKSkjdHJlZU5hbWUgPSBjYXNlX3doZW4oCiAgICAjc3BlY2llcyA9PSAiWWFtYWRhenltYSB0ZW51aXMiIH4gIkNhbmRpZGEgdGVudWlzIiwKICAgICNzcGVjaWVzID09ICJTdWhvbXljZXMgdGFuemF3YWVuc2lzIiB+ICJDYW5kaWRhIHRhbnphd2FlbnNpcyIsCiAgICAjVFJVRSB+IHNwZWNpZXMpLAogICAgI3RyZWVOYW1lID0gZ3N1YigiICIsICJfIiwgdHJlZU5hbWUpKQogICAgCmdlbmUyc3BzLm1hcCAlPiUgCiAgc2VsZWN0KG5hbWUsIHRyZWVOYW1lKSAlPiUgCiAgd3JpdGVfdHN2KCJkYXRhLzIwMjIwNTE2LWV4cGFuZGVkLWJsYXN0LWdlbmVzLXRvLXNwZWNpZXMtbWFwLnRzdiIsIGNvbF9uYW1lcyA9IEZBTFNFKQpgYGAKCkFwcGVuZCB0aGUgc3BlY2llcyBsaXN0IHdpdGggYWRkaXRpb25hbCBpbmZvcm1hdGlvbgpgYGB7cn0Kc3BzLnBhdGhvIDwtIHJlYWRfdHN2KCJkYXRhLzIwMjIwNTE4LWV4cGFuZGVkLWJsYXN0LXNwZWNpZXMtcGF0aG9nZW4taW5mby50eHQiLCBjb2xfdHlwZXMgPSBjb2xzKCkpCnNwcy5zdW1tYXJ5IDwtIGRlZHVwLm1lcmdlICU+JSAKICBncm91cF9ieShzcGVjaWVzLCBzdGF4aWQpICU+JSAKICBzdW1tYXJpemUoCiAgICB0b3RhbCA9IG4oKSwKICAgIHNob3J0ID0gc3VtKHNsZW4gPCA1MDApLAogICAgc2hvcnRJbmNvbXBsID0gaWZlbHNlKAogICAgICBhbnkoaXMubmEocENvbXBsZXRlKSksIE5BLAogICAgICBzdW0ocENvbXBsZXRlICVpbiUgYygibm8tZW5kcyIsICJuby1sZWZ0IiwgIm5vLXJpZ2h0IikgJiBzbGVuIDwgNTAwKQogICAgKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApICU+JSAKICBhZGRfcm93KHNwZWNpZXMgPSAiU2FjY2hhcm9teWNlcyBjZXJldmlzaWFlIiwKICAgICAgICAgIHN0YXhpZCA9ICI1NTkyOTIiLAogICAgICAgICAgdG90YWwgPSAwLCBzaG9ydCA9IDAsIHNob3J0SW5jb21wbCA9IE5BKSAlPiUgCiAgbGVmdF9qb2luKHNwcy5wYXRobywgYnkgPSAic3BlY2llcyIpICU+JSAKICBhcnJhbmdlKGRlc2ModG90YWwpKQpzcHMuc3VtbWFyeSAlPiUgc2VsZWN0KC1zdGF4aWQpCmBgYAoKUXVlcnkgdGhlIHRheG9ub215IGRhdGFiYXNlIGZvciBsaW5lYWdlIGluZm9ybWF0aW9uIGZvciBlYWNoIHNwZWNpZXMKYGBge3J9CnRheG9uIDwtIGVudHJlel9mZXRjaChkYiA9ICJ0YXhvbm9teSIsIGlkID0gc3BzLnN1bW1hcnkkc3RheGlkLCByZXR0eXBlID0gInhtbCIpICU+JSAKICBYTUw6OnhtbFRvTGlzdCgpCm5hbWVzKHRheG9uKSA8LSBzcHMuc3VtbWFyeSRzcGVjaWVzCmBgYAoKUGFyc2UgdGhlIHRheG9ub215IGluZm9ybWF0aW9uCmBgYHtyfQpzcHMubGluIDwtIGxhcHBseSh0YXhvbiwgZnVuY3Rpb24oeCkgewogIGJpbmRfcm93cyh4JExpbmVhZ2VFeCkgJT4lIHRhaWwoLTEwKSAlPiUgZmlsdGVyKFJhbmsgIT0gInNwZWNpZXMiKQogIH0pICU+JSAKICBiaW5kX3Jvd3MoLmlkID0gInNwZWNpZXMiKQp3cml0ZV90c3Yoc3BzLmxpbiwgImRhdGEvMjAyMjA1MTgtZXhwYW5kZWQtYmxhc3Qtc3BlY2llcy10YXhvbm9teS50c3YiKQpgYGAKCgpXcml0ZSBzcGVjaWVzIGluZm8gdG8gZmlsZQpgYGB7cn0Kc3BzSW5mbyA8LSBzcHMubGluICU+JSAKICBmaWx0ZXIoUmFuayA9PSAiZmFtaWx5IikgJT4lIAogIHNlbGVjdChzcGVjaWVzLCBmYW1pbHkgPSBTY2llbnRpZmljTmFtZSkgJT4lIAogIG11dGF0ZSh0cmVlTmFtZSA9IGdzdWIoIiAiLCAiXyIsIHNwZWNpZXMpKSAlPiUgCiAgICAjdHJlZU5hbWUgPSBjYXNlX3doZW4oCiAgICAjc3BlY2llcyA9PSAiWWFtYWRhenltYSB0ZW51aXMiIH4gIkNhbmRpZGEgdGVudWlzIiwKICAgICNzcGVjaWVzID09ICJTdWhvbXljZXMgdGFuemF3YWVuc2lzIiB+ICJDYW5kaWRhIHRhbnphd2FlbnNpcyIsCiAgICAjVFJVRSB+IHNwZWNpZXMpLAogICAgI3RyZWVOYW1lID0gZ3N1YigiICIsICJfIiwgdHJlZU5hbWUpKSAlPiUgCiAgcmlnaHRfam9pbihzcHMuc3VtbWFyeSwgYnkgPSAic3BlY2llcyIpCgp3cml0ZV90c3Yoc3BzSW5mbywgZmlsZSA9ICJkYXRhLzIwMjIwNTE4LWV4cGFuZGVkLWJsYXN0LXNwZWNpZXMtaW5mby50c3YiKQpgYGAKCiMjIyAxMi4gR2Vub21lIGFzc2VtYmx5IGluZm8KVXNlIHNwZWNpZXMgdGF4aWQgdG8gZmV0Y2ggdGhlIGxpbmtzIHRvIHRoZSB0YXhvbm9teSBkYXRhYmFzZS4gTm90ZSB0aGF0IHNvbWUgb2YgdGhlIHRheGlkcyBhcmUgZm9yIHNwZWNpZmljIHN0cmFpbnMgd2hpbGUgb3RoZXJzIGFyZSBhdCB0aGUgc3BlY2llcyBsZXZlbC4gQXMgYSByZXN1bHQsIHRoZXJlIG1heSBiZSBkaWZmZXJlbnQgbnVtYmVyIG9mIGFzc2VtYmxpZXMgYmVpbmcgZmV0Y2hlZC4KYGBge3IgZ2Vub21lX2Fzc2VtYmx5fQpzdGF4aWQgPC0gc3BzLnN1bW1hcnkgJT4lIHNlbGVjdChzcGVjaWVzLCBzdGF4aWQpICU+JSBkZWZyYW1lKCkKc3RheGlkWyJTY2hlZmZlcnNvbXljZXMgc3RpcGl0aXMiXSA8LSAiNDkyNCIgIyB1c2UgdGhlIHNwZWNpZXMgZ2VuZXJhbCBJRAphc3NlbWJseS5sbmsgPC0gZW50cmV6X2xpbmsoZGJmcm9tID0gInRheG9ub215IiwgZGIgPSAiYXNzZW1ibHkiLCBpZCA9IHN0YXhpZCwgYnlfaWQgPSBUUlVFKQphc3NlbWJseS5sbmsgPC0gbGFwcGx5KGFzc2VtYmx5LmxuaywgZnVuY3Rpb24oeCkgeCRsaW5rcyR0YXhvbm9teV9hc3NlbWJseSkKbmFtZXMoYXNzZW1ibHkubG5rKSA8LSBzcHMuc3VtbWFyeSRzcGVjaWVzCmFzc2VtYmx5Lmxua1siTGFjaGFuY2VhIGtsdXl2ZXJpIl0gPC0gIjk1Mjc4NzEiCmBgYApHZXQgdGhlIGFzc2VtYmx5IHN1bW1hcnkgaW5mbyBmcm9tIG5jYmkKYGBge3J9CmFzc2VtYmx5LmluZm8gPC0gbGFwcGx5KGFzc2VtYmx5LmxuaywgZnVuY3Rpb24oaSl7CiAgaWYoIWlzX2VtcHR5KGkpKQogICAgZW50cmV6X3N1bW1hcnkoZGIgPSAiYXNzZW1ibHkiLCBpZCA9IGkpCn0pCmBgYApFeHRyYWN0IHRoZSBpbmZvcm1hdGlvbiBpbnRvIGEgdGliYmxlCmBgYHtyfQpmaWVsZHMgPC0gYygiYXNzZW1ibHlhY2Nlc3Npb24iLCAibGFzdG1ham9ycmVsZWFzZWFjY2Vzc2lvbiIsICJ0YXhpZCIsICJzcGVjaWVzdGF4aWQiLAogICAgICAgICAgICAic3BlY2llc25hbWUiLCAiYXNzZW1ibHl0eXBlIiwgImFzc2VtYmx5c3RhdHVzIiwgImNvdmVyYWdlIiwgInN1Ym1pc3Npb25kYXRlIiwKICAgICAgICAgICAgImxhc3R1cGRhdGVkYXRlIiwgInJlZnNlcV9jYXRlZ29yeSIsICJjb250aWduNTAiLCAic2NhZmZvbGRuNTAiKQphc3NlbWJseS50YiA8LSBsYXBwbHkoYXNzZW1ibHkuaW5mbywgZnVuY3Rpb24oeCkgewogIHRiIDwtIGV4dHJhY3RfZnJvbV9lc3VtbWFyeSh4LCBlbGVtZW50cyA9IGZpZWxkcywgc2ltcGxpZnkgPSBGQUxTRSkKICBiaW5kX3Jvd3ModGIpCn0pICU+JSBiaW5kX3Jvd3MoKSAlPiUKICBtdXRhdGUoc3BlY2llcyA9IGdzdWIoIlxcW3xcXF0iLCAiIiwgc3BlY2llc25hbWUpKSAlPiUgCiAgYXJyYW5nZShkZXNjKHJlZnNlcV9jYXRlZ29yeSksIHNwZWNpZXMpICU+JSAKICBzZWxlY3Qoc3BlY2llcywgYXNzZW1ibHlzdGF0dXM6c2NhZmZvbGRuNTAsIGV2ZXJ5dGhpbmcoKSkKICAjc2VsZWN0KHNwZWNpZXMgPSBzcGVjaWVzbmFtZSwgZXZlcnl0aGluZygpKQpgYGAKU2VsZWN0IHRoZSByZWZzZXEgb3Igb25lIGFsdGVybmF0aXZlIGFzc2VtYmx5CmBgYHtyfQphc3NlbWJseS50YmwgPC0gYXNzZW1ibHkudGIgJT4lIAogIG11dGF0ZShhc3NlbWJseXN0YXR1cyA9IGZhY3Rvcihhc3NlbWJseXN0YXR1cywgbGV2ZWxzID0gYygiQ29tcGxldGUgR2Vub21lIiwgIkNocm9tb3NvbWUiLCAiU2NhZmZvbGQiLCAiQ29udGlnIikpKSAlPiUgCiAgZ3JvdXBfYnkoc3BlY2llcykgJT4lIAogIGZpbHRlcihyZWZzZXFfY2F0ZWdvcnkgIT0gIm5hIiB8IHNjYWZmb2xkbjUwID09IG1heChzY2FmZm9sZG41MCkgfCBhc3NlbWJseXN0YXR1cyAlaW4lIGMoIkNvbXBsZXRlIEdlbm9tZSIsICJDaHJvbW9zb21lIikpICU+JSAKICBncm91cF9ieShzcGVjaWVzLCBhc3NlbWJseXN0YXR1cykgJT4lIAogIGZpbHRlcihyZWZzZXFfY2F0ZWdvcnkgIT0gIm5hIiB8IHNjYWZmb2xkbjUwID09IG1heChzY2FmZm9sZG41MCkpICU+JSAKICB1bmdyb3VwKCkKd3JpdGVfdHN2KGFzc2VtYmx5LnRibCwgZmlsZSA9ICIuLi8uLi9vdXRwdXQvMjAyMjA1MjUtZXhwYW5kZWQtYmxhc3Qtc3BlY2llcy1hc3NlbWJseS1pbmZvLnRzdiIpCmBgYAoKIyMjIDEzLiBDaHJvbW9zb21hbCBsb2NhdGlvbnMKVGhlIGdvYWwgaXMgdG8gZmV0Y2ggdGhlIGNocm9tb3NvbWFsIGxvY2F0aW9ucyBmb3IgdGhlIEJMQVNUIGhpdHMuIFdlIGFyZSBvbmx5IGdvaW5nIHRvIHVzZSB0aGUgYXNzZW1ibGllcyB0aGF0IGFyZSBhdCBhIGNocm9tb3NvbWFsIG9yIGNvbXBsZXRlIGdlbm9tZSBsZXZlbC4gTm90ZSB0aGF0IF9DLiBhdXJpc18gcmVmc2VxIGdlbm9tZSBpcyBjb21wbGV0ZSBldmVuIHRob3VnaCB0aGUgcmVjb3JkIHNheXMgaXQgaXMgc2NhZmZvbGQuCgpgYGB7ciBjaHJfbG9jMX0KIyBHZXQgdGhlIGxpc3Qgb2Ygc3BlY2llcyB3aG9zZSBnZW5vbWUgYXNzZW1ibHkgYXJlIGF0IGEgY2hyb21vc29tYWwgb3IgY29tcGxldGUgZ2Vub21lIGxldmVsCmxpc3QuZ2Vub21lLmNvbXBsIDwtIGFzc2VtYmx5LnRibCAlPiUgCiAgZmlsdGVyKGFzc2VtYmx5c3RhdHVzICVpbiUgYygiQ29tcGxldGUgR2Vub21lIiwgIkNocm9tb3NvbWUiKSwgcmVmc2VxX2NhdGVnb3J5ICE9ICJuYSIpICU+JSAKICBwdWxsKHNwZWNpZXMpICU+JSB1bmlxdWUoKQpjaHIuc3BzIDwtIGMobGlzdC5nZW5vbWUuY29tcGwsICJTY2hlZmZlcnNvbXljZXMgc3RpcGl0aXMiLCAiQ2FuZGlkYSBhdXJpcyIpCmNoci5zaWQgPC0gZGVkdXAubWVyZ2UgJT4lIGZpbHRlcihzcGVjaWVzICVpbiUgY2hyLnNwcywgZ3JlcGwoIl5yZWYiLCBzaWQpKSAlPiUgcHVsbChzaWQpIAojIEdldCB0aGUgcHJvdGVpbidzIGdlbmUgSUQgKG5vdCByZWZzZXEgSUQpCnRtcC5nZW5lLmxuayA8LSBlbnRyZXpfbGluayhkYmZyb20gPSAicHJvdGVpbiIsIGlkID0gY2hyLnNpZCwgZGIgPSAiZ2VuZSIsIGJ5X2lkID0gVFJVRSkKIyBleHRyYWN0IGdlbmUgSUQKdG1wLmdlbmUuaWQgPC0gc2FwcGx5KHRtcC5nZW5lLmxuaywgZnVuY3Rpb24oeCkgeCRsaW5rcyRwcm90ZWluX2dlbmUpCiMgZmV0Y2ggc3VtbWFyeSBpbmZvIGZyb20gdGhlIGdlbmUgZGF0YWJhc2UKdG1wLmdlbmUgPC0gZW50cmV6X3N1bW1hcnkoZGIgPSAiZ2VuZSIsIGlkID0gdG1wLmdlbmUuaWQpCiMgZXh0cmFjdCBnZW5lIG5hbWUKdG1wLmdlbmUubmFtZSA8LSBleHRyYWN0X2Zyb21fZXN1bW1hcnkodG1wLmdlbmUsIGVsZW1lbnRzID0gIm5hbWUiKQojIGV4dHJhY3Qgc3lzdGVtYXRpYyBJRCAoc3RvcmVkIGluIHRoZSBvdGhlcmFsaWFzIGZpZWxkKQp0bXAuZ2VuZS5hbGlhcyA8LSBleHRyYWN0X2Zyb21fZXN1bW1hcnkodG1wLmdlbmUsIGVsZW1lbnRzID0gIm90aGVyYWxpYXNlcyIpCiMgZXh0cmFjdCBjaHJvbW9zb21hbCBsb2NhdGlvbnMsIG9ubHkgYXZhaWxhYmxlIGZvciBmdWxseSBhc3NlbWJsZWQgZ2Vub21lcwp0bXAuZ2VuZS5sb2MgPC0gYmluZF9yb3dzKGV4dHJhY3RfZnJvbV9lc3VtbWFyeSh0bXAuZ2VuZSwgZWxlbWVudHMgPSAiZ2Vub21pY2luZm8iKSwgLmlkID0gImdlbmVfdWlkIikKIyBwb29sIHRoZSB1bmlxdWUgY2hyb21vc29tZSBzZXF1ZW5jZSBJRHMgdG8gZmluZCBvdXQgdGhlaXIgbGVuZ3RoCnRtcC5jaHIuaWQgPC0gdW5pcXVlKHRtcC5nZW5lLmxvYyRjaHJhY2N2ZXIpCiMgb2J0YWluIHRoZSBjaHJvbW9zb21lIGxlbmd0aCBpbmZvcm1hdGlvbiBmcm9tIHRoZSBudWNjb3JlIGRhdGFiYXNlCnRtcC5jaHIgPC0gZW50cmV6X3N1bW1hcnkoIm51Y2NvcmUiLCBpZCA9IHRtcC5jaHIuaWQpCnRtcC5jaHIubGVuIDwtIGV4dHJhY3RfZnJvbV9lc3VtbWFyeSh0bXAuY2hyLCBlbGVtZW50cyA9ICJzbGVuIik7IApuYW1lcyh0bXAuY2hyLmxlbikgPSB0bXAuY2hyLmlkCiMgYXNzZW1ibGUgdGhlIGNocm9tb3NvbWFsIGxvY2F0aW9uIGRhdGFzZXQKY2hyLmxvYyA8LSB0aWJibGUoc2lkID0gY2hyLnNpZCwgZ2VuZV91aWQgPSBuYW1lcyh0bXAuZ2VuZS5uYW1lKSwgCiAgICAgICAgICAgICAgICAgIGdlbmVfbmFtZSA9IHRtcC5nZW5lLm5hbWUsIGdlbmVfYWxpYXMgPSB0bXAuZ2VuZS5hbGlhcykgJT4lIAogIGxlZnRfam9pbih0bXAuZ2VuZS5sb2MsIGJ5ID0gImdlbmVfdWlkIikgJT4lIAogIG11dGF0ZShjaHJMID0gdG1wLmNoci5sZW5bY2hyYWNjdmVyXSwgcmVsTG9jID0gbnVtKGNocnN0YXJ0L2NockwsIGRpZ2l0cyA9IDIpKSAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChkZWR1cC5tZXJnZSwgc2lkLCBwaWQsIHNwZWNpZXMsIHN0cmFpbiksIGJ5ID0gInNpZCIpICU+JSAKICByZWxvY2F0ZShwaWQ6c3RyYWluLCAuYWZ0ZXIgPSBzaWQpICU+JSByZWxvY2F0ZShzaWQsIC5hZnRlciA9IGxhc3RfY29sKCkpICU+JSAKICBzZWxlY3QoLWV4b25jb3VudCkgJT4lIAogIGFycmFuZ2Uoc3BlY2llcywgY2hybG9jKQpgYGAKCmBgYHtyfQp3cml0ZV90c3YoY2hyLmxvYywgZmlsZSA9ICIuLi8uLi9vdXRwdXQvMjAyMjA2MDktZXhwYW5kZWQtYmxhc3QtY2hyb21vc29tYWwtbG9jYXRpb25zLnRzdiIpCmBgYAo=