Refseq_protein
I used blastp to search the N-terminal 560 residues in XP_028889033 against the refseq_protein database. (See README.md for details). The taxonomy distribution is shown below: 
# load table
refseq <- read_csv("data/ncbi-refseq/XP_028889033_homologs_refprot_N560_select.csv", col_types = "cddnnnc")
refseq.length <- read_tsv("data/ncbi-refseq/XP_028889033_homologs_refprot_N560_select_length.txt", col_names = c("accession","length"), col_types = "ci")
refseq <- left_join(refseq, refseq.length) %>% select(accession, everything())
Joining, by = "accession"
# Do the refseq hits show similar relationship between e-value and protein length?
refseq %>%
ggplot(aes(x = -log10(e.value), y = log2(length), col = (length > 500))) + geom_point() +
theme(legend.position = "bottom")
ggsave("img/20200723-XP_028889033-refseq-hits-e-value-by-length.png", width = 3, height = 3)

# The answer is yes. So let's filter these out and show what they are
refseq0 <- refseq %>%
filter(length > 500, !grepl("rugosa", description))
refseq %>% filter(length <= 500) %>% select(accession, query.cover, e.value, perc.ident, length, description)
# the above script uses a strange way to move a column to the last, by first removing it and then adding it back
# thanks to https://github.com/tidyverse/dplyr/issues/3051
Discussion
- We see that the excluded sequences not only have less significant e-values, but also have barely above 50% query coverage. In terms of species distribution, most of them are from S. stipitis and D. rugosa. The second species will be removed anyways (see notes in the
03-gene-tree folder)
- Even though a couple of the S. stipitis sequences were annotated as “fungal hyphal regulated cell wall proteins”, with less than 500 a.a., they are unlikely to encode the stalk and in a separate analysis, I also found them to not possess the GPI-anchor. All in all, I think these sequences are unlikely to be candidate adhesins.
NCBI blastp with N360 query
I repeated the NCBI blastp search with the first 360 amino acid residues instead of the first 560. Because the 560 aa sequence contains a copy of the Hyr1 domain in addition to the PF11765 domain, it is possible that some of the hits bear similarity to the Hyr1 and not the Hyphal_reg_CWP domain. Also, when I applied the >50% query coverage filter on the resulting homolog list, the effective cutoff for the PF11765 domain would be higher, since most of the hits don’t share the Hyr1 domain.
Below is the taxonomy distribution of the hits:
To determine the effect of changing the query sequence from N560 to N360 amino acids, I downloaded the hits description and the fasta sequences from both searches. Now let’s compare them.
hitTabColNames <- c("description","sci.name","common.name","taxid","max.score",
"total.score","query.cover","e.value","perc.ident","length","acc")
refseq.N360 <- read_csv("data/ncbi-refseq/XP_028889033_homologs_refprot_N360.csv", col_types = "ccccddcddic", col_names = hitTabColNames, skip = 1) %>%
mutate(query.cover = parse_number(query.cover),
accession = gsub('[")]','',str_split(acc, ",", simplify = T)[,2]))
cat("**Sequences only in the N360 set after applying the 50% query coverage filter**")
Sequences only in the N360 set after applying the 50% query coverage filter
all.N360 <- refseq.N360 %>% filter(query.cover >= 50, length > 500) %>% pull(accession)
only.in.N360 <- setdiff(all.N360, refseq0$accession)
refseq.N360 %>% filter(accession %in% only.in.N360) %>%
select(accession, qcov = query.cover, e.value, pident = perc.ident, length, sci.name) %>%
arrange(sci.name)
cat("**Sequences only in the N560 set after applying the 50% query coverage filter**")
Sequences only in the N560 set after applying the 50% query coverage filter
only.in.N560 <- setdiff(refseq0$accession, all.N360)
refseq0 %>% filter(accession %in% only.in.N560) %>%
select(accession, qcov = query.cover, e.value, pident = perc.ident, length)
Discussion
I would include 7 of the first 10 sequences in the table above, except for those from C. orthopsilosis, into the final list. The reason to include them is because the 50% query coverage cutoff was unfairly applied to them, since using the N360 amino acid as query, all of them passed the cutoff. The reason to not include C. orthopsilosis is to limit the total number of species and sequences in the various plots. The main contribution of C. orthopsilosis, if I include it, is to enhance the conclusion that the number of homologs in C. parapsilosis and L. elongatus are low. The remaining 15 sequences are from species I’m not going to include.
One sequence from the original blast search, XP_001383953.2 from S. stipitis didn’t make the cut in the new search. When I lowered the e-value cutoff to 1e-3, it appeared at the bottom of the hist list. For consistency’s sake, I decided to exclude it from the new analysis.
At the same time, I found that relaxing the e-value cutoff from 1e-5 to 1e-3 only resulted in four new sequences. This suggests that the list of homologs is not too sensitive to the arbitrary choice of the e-value cutoff. Looking at the three other sequences making the cut at 1e-3, one is from a species I am not including anyway. The other two were actually added from the FungiDB search result (see notes and analysis below). This made me realize that e-value cutoff is database-size-specific and thus using the same cutoff doesn’t mean the same thing between refseq_db, fungidb and GRYC. But the resulting differences in the number of hits included or excluded are relatively small and thus don’t affect the overall conclusions.

refseq.add <- c("XP_024716365.1", "XP_717775.2", "XP_036665264.1", "XP_002547616.1", "XP_002545510.1", "XP_002614115.1", "XP_002770240.1")
# remove one seq, see notes above
refseq0 <- refseq0 %>% filter(accession != "XP_001383953.2")
refseq.combined <- refseq.N360 %>%
filter(accession %in% refseq.add) %>%
select(accession, description, max.score, total.score, query.cover, e.value, perc.ident, length) %>%
bind_rows(refseq0)
Missing homolog in C. auris B11221
Update 2021-03-01
Muñoz et al 2021 reported 8 members in the Iff/Hyr1 family in C. auris clades I and III.
My own blastp only identified 7 members in the refseq_prot database, which includes B11221 but not B8441. A second blastp search against the nr_prot database, with taxonomy restricted to C. auris confirmed that while B8441 showed 8 hits, B11221 only showed 7. This is inconsistent with the Muñoz 2021 report. The discrepancy could be due to the protein fasta file in the B11221 refseq assembly being incomplete. To keep our results consistent with the recent publication, I’ll add this one B8441 sequence to the homolog database manually so we would have 8 members, which is the max size of the family among all know C. auris strains. Although, note that the 8 genes do not all come from the same strain any more. I don’t expect this to change any of our downstream analyses, since the two strains are closely related within the same clade in C. auris.
Merge
First we need to find out which sequences were duplicates. To do so we will blast the fungidb and gryc hits against the refseq hits.
update 2021-01-25 Added new sequences from the refseq_prot search.
FungiDB
Translate fungiDB IDs to refseq_protein IDs
The first approach I took was to directly blast the fungidb hits to the NCBI refseq_prot database and see how many of them had a match there. Note that due to the way e-values are calculated, a fungidb hit that had an identical match in the refseq_prot database may not actually be among the refseq hits, as we will see below.
See README.md for details on how I did BLAST.
# load blast table
fdb.hits.blast <- read_tsv("data/fungidb/fungidb_blast_refseq_protein.txt", col_names = c("query_id","subject_id","query_length","subject_length","perc_identity","mismatch","score","bitscore","e_value"), col_types = "ccnndnnnn")
# goal: for each fungidb hit, identify the refseq ID that has the highest Score
fdb.hits.blast.filtered <- fdb.hits.blast %>%
group_by(query_id) %>%
filter(score == max(score), str_sub(query_id, -2, -1) != "_B") # this blast was done before I removed the _B alleles
# ID mapping for identical sequences (% identity = 100%)
fdb.refseqID <- fdb.hits.blast.filtered %>% filter(perc_identity == 100) %>% pull(subject_id)
names(fdb.refseqID) <- fdb.hits.blast.filtered %>% filter(perc_identity == 100) %>% pull(query_id)
cat("**Hits not mapped to refseq_prot:**")
Hits not mapped to refseq_prot:
#fdb.filtered %>% filter(!gene_id %in% names(fdb.refseqID))
fdb.hits.blast.filtered %>%
filter(perc_identity != 100) %>%
select(query_id, subject_id, qlen = query_length, slen = subject_length, pident = perc_identity, mismatch, e_value)
Discussion
Of the 70 sequence, 60 are present in the refseq database.
Among the sequences that didn’t map, five belong to the C. auris B8441 strain, which is the only strain of C. auris present in the FungiDB, but apparently is not in the refseq_protein database. Another three sequences in B8441 apparently are identical to sequences from the other C. auris strains included in the refseq database.
Each species in the homologs table is represented by one strain, which corresponds to the refseq assembly and is included in the refseq_prot database. To keep with this convention, I will use B11221 as the strain for C. auris and leave B8441 out. In a separate analysis, I will specifically investigate within C. auris polymorphism and will include B8441 and other strains.
The remaining five sequences belong to C. paradoxus (4) and C. tropicalis (1). See the Merge section for choices to include some additional sequences.
Search the fungidb hits against a database made of the refseq_prot hits
The above search only identifies fungidb hits that are don’t even have a match in the refseq_prot database. Here we are being more specific and ask which FungiDB hits do not already exist among the refseq_prot hits.
The bash script below is to make a blast database from the NCBI blast results and search the FungiDB hits in it to identify matching sequences. Note that we are using all of the N360 hits, including the ones that were filtered out. This is because we are only interested in those FungiDB hits that don’t already exist in the refseq hits at all. So if a hit matches a refseq hit that was later filtered, it would still be excluded.
# make blast database from the NCBI blast results
cd data
mkdir blastdb
makeblastdb -in ncbi-refseq/XP_028889033_homologs_refprot_N360.fasta -parse_seqids -dbtype prot -title XP_028889033_refprot -out blastdb/XP_028889033_refprot
# blastp
# blastp -db ./blastdb/XP_028889033_refprot -query fungidb/XP_028889033_homologs_fungidb_N360_use.fasta -outfmt "6 qseqid sseqid qlen slen pident mismatch score bitscore evalue" -max_target_seqs 1 -num_threads 4 -out fungidb/XP_028889033_fungidb-refprot-N360-blast.txt
# blastp -db ./blastdb/XP_028889033_refprot -query GRYC/XP_028889033_homologs_gryc.fasta -outfmt "6 qseqid sseqid qlen slen pident mismatch score bitscore evalue" -max_target_seqs 1 -num_threads 4 -out GRYC/XP_028889033_gryc-refprot-N360-blast.txt
blastp -db ./blastdb/XP_028889033_refprot -query fungidb/XP_028889033_homologs_fungidb_N360_use.fasta -outfmt "6 qseqid sseqid qlen slen pident mismatch score bitscore evalue" -evalue 1e-180 -num_threads 4 -out fungidb/XP_028889033_fungidb-refprot-N360-blast-allhits.txt
blastp -db ./blastdb/XP_028889033_refprot -query GRYC/XP_028889033_homologs_gryc.fasta -outfmt "6 qseqid sseqid qlen slen pident mismatch score bitscore evalue" -evalue 1e-180 -num_threads 4 -out GRYC/XP_028889033_gryc-refprot-N360-blast_allhits.txt
The typical protocol for identifying orthologs is to perform reciprocol blast. But our goal here is to identify identical sequences – more like what the CDD hits program does – and thus one sided blast should be fine. The analysis below is meant to identify the shared hits as well as the unique hits in each set.
===== Update [HB] 2021-06-12 =====
The -max_target_seqs 1 argument to blastp, when run locally, led to unexpected behavior – instead of returning the “best” hit ranked by e-value, as one would intuitive expect, it returns the FIRST hit that is above the threshold, and thus the result will also depend on the order of the sequences in the database. This issue is widely documented on the web if one knows what question to search for. See Shah et al. 2019
I therefore repeated the above searches, replacing -max_target_seqs 1 with -evalue 1e-180, since what we want to identify is the perfect match. I also changed the -outfmt from 6 to 7 to include the comment lines, and named the output files with the suffix _allhits to distinguish them from the previous results.
===== End Update =====
# local blast the fungiDB hits against the library made from the refseq hits
blast.header <- c("query_id","subject_id","query_length","subject_length","perc_identity","mismatch","score","bitscore","e_value")
fdb2refseq <- read_tsv("data/fungidb/XP_028889033_fungidb-refprot-N360-blast-allhits.txt", col_types = "ccnndnnnn", col_names = blast.header, comment = "#")
# for each query sequence, retain the top hit with the best score
fdb2refseq.best.hit <- fdb2refseq %>%
group_by(query_id) %>%
filter(score == max(score))
# if perc_identity is 100, then we had a match
fdb2refseq.match <- fdb2refseq.best.hit %>%
filter(perc_identity == 100) %>%
pull(query_id)
# print out some stats
sprintf("When searching %i FungiDB hits in the %i refseq_prot hits, %i got at least one hit.",
nrow(fdb.filtered), nrow(refseq0), nrow(fdb2refseq.best.hit))
[1] “When searching 70 FungiDB hits in the 88 refseq_prot hits, 67 got at least one hit.”
sprintf("Among them, %i FungiDB hits were found to have an identical match in the refseq hits.",
length(fdb2refseq.match))
[1] “Among them, 56 FungiDB hits were found to have an identical match in the refseq hits.”
cat("Below are the FungiDB hits that either had no or non-identical matches in the refseq hits...")
Below are the FungiDB hits that either had no or non-identical matches in the refseq hits…
fdb.filtered %>%
filter(!gene_id %in% fdb2refseq.match) %>%
select(gene_id, organism, `FDB e_value` = e_value, length) %>%
left_join(select(fdb2refseq.best.hit, gene_id = query_id, sid = subject_id,
slen = subject_length, pident = perc_identity))
Joining, by = “gene_id”
Discussion
- The top 8 are from B8441 of C. auris. As noted above, the refseq strain for the species is B11221. Looking at the perc_identity of the matches, we can see that B11221 misses one of the eight hits in B8441, i.e. B9J08_004098.
- CAGL0L00227g and CLUG_05233 are both “provisional” refseq sequences, meaning that their records have not been submitted to NCBI for final review.
- I will use the refseq hits as the base and add C. auris B9J08_004098, CLUG_05233 and the C. parapsilosis sequences (for some reason this species is not included in refseq). To do so, I’ll use the
read.fasta() function from the seqinr package to extract their sequences, and use the write.fasta() function to output the combined fasta.
Update 2020-10-18 I realized that by including the one sequence from C. auris B8441, the final dataset would contain two C. auris strains, making it distinct from the other species, each of which is represented by one strain only. This wouldn’t change the broader conclusions. But if there is a need to modify the homologs list and repeat the downstream analysis, I would like to remove this sequence (B9J08_004098)
Update 2021-03-01 I will actually include B9J08_004098. See notes in the fungidb blast section above and also README.md in this folder for reasons.
GRYC
# local blast the gryc hits against the library made from the refseq hits
gryc2refseq <- read_tsv("data/GRYC/XP_028889033_gryc-refprot-N360-blast-allhits.txt", col_names = c("query_id","subject_id","query_length","subject_length","perc_identity","mismatch","score","bitscore","e_value"), col_types = "ccnndnnnn", comment = "#")
match.gryc <- gryc2refseq %>% filter(perc_identity > 90) %>% select(-score, -bitscore, -e_value)
Among the 15 queries searched, 4 got at least one hit among the refseq hits with the evalue cutoff, among which 3 are redundant with the refseq hits. Here are all those that got a hit
gryc1 <- gryc %>%
filter((!ori_id %in% match.gryc$query_id & species_id != "Lfermentati") &
(query_cover > 50 & length >= 500) |
(id %in% c("NADE0s05e04136g"))) %>%
# the second criterion is to remove the one sequence with length smaller than 500 a.a.
# the last criterion is to add back the Ndelphensis sequence
select(ori_id, id, species, species_id)
gryc1
In the end we added four sequences to the list.
Merge
In order to make it easier for the downstream tree building and reconciliation, I would like to programmatically reformat the sequence names to conform to the Notung post-script standard, i.e. SeqID_SpeciesName.
Now let’s do the renaming and gather some species/strain information as well.
# use rentrez function to get the organism information
tmp <- entrez_summary(db = "protein", id = refseq.combined$accession)
refseq.orgn.extract <- extract_from_esummary(tmp, elements = c("organism","strain"), simplify = FALSE)
refseq.orgn <- do.call(rbind, lapply(refseq.orgn.extract, unlist))
# Rename the refseq names. The goal is to have SeqID_Species, like XP_028889033_Cauris
# what we have now is a two word species name, which we will shorten to a capital first letter of
# the first word followed by the entire second word
refseq1 <- cbind(refseq.orgn, refseq.combined) %>%
mutate(organism = word(str_replace_all(organism, "[\\x5B\\x5D]", ""), 1, 2),
# \\x5B\\x5D are Hexadecimal representations of the square brackets
species = gsub("pseudohaemulonii", "pseudohaemulonis", organism),
# the new ncbi database renamed the species, but my old results used the "s"
# therefore I left the organism field alone and created a new variable to
# store the "old" name
species_id = paste0(str_sub(species, 1, 1), word(species, 2,2))) %>%
filter(species_id != "Drugosa") %>% # remove D. rugosa sequences
select(accession, species, species_id, everything())
Merge the three datasets and export the fasta sequences and blast results.
# refseq hit sequences and their names
refseqdb.seq <- read.fasta("data/ncbi-refseq/XP_028889033_homologs_refprot_N360.fasta", seqtype = "AA", as.string = TRUE, strip.desc = TRUE)
refseqdb.list <- refseq1$accession
refseqdb.names <- with(refseq1, paste(accession, species_id, sep = "_"))
# sequences to be added to the refseq list from fungidb hits
fdb.list <- c("CLUG_05233", "B9J08_004098", paste("CPAR2",c(600430,806390,806420),sep = "_"))
fdb.species <- c("Clusitaniae", "Cauris", rep("Cparapsilosis",times = 3))
fdb.names <- paste(fdb.list, fdb.species, sep = "_")
fungidb.seq <- read.fasta("data/fungidb/XP_028889033_homologs_fungidb_N360_use.fasta", seqtype = "AA", as.string = TRUE, strip.desc = TRUE)
# sequences to be added to the refseq list from gryc hits
gryc.seq <- read.fasta("data/GRYC/XP_028889033_homologs_gryc.fasta", seqtype = "AA", as.string = TRUE, strip.desc = FALSE)
gryc.list <- gryc1$ori_id
gryc.names <- gryc1 %>% mutate(name = paste(id, species_id, sep = "_")) %>% pull(name)
# sequences to be added to the
combined.seq <- c(refseqdb.seq[refseqdb.list], fungidb.seq[fdb.list], gryc.seq[gryc.list])
combined.names <- c(refseqdb.names, fdb.names, gryc.names)
write.fasta(combined.seq, names = combined.names, file.out = "output/XP_028889033_homologs_combine.fasta")
# in the "output/gene-tree/README.md", I documented an issue with a particular
# sequence, XP_025344407.1, which has a long N-terminal part that is not aligned
# to any other sequences. the solution is to remove the first 900 a.a. of it
# and output a different version of fasta file just for gene tree analysis
truncated <- str_sub(refseqdb.seq$XP_025344407.1, start = 901)
attributes(truncated) <- attributes(refseqdb.seq$XP_025344407.1)
combined.seq$XP_025344407.1 <- truncated
write.fasta(combined.seq, names = combined.names, file.out = "output/XP_028889033_homologs_combine_for_gene_tree.fasta")
# merge blast hit table
refseq.stat <- refseq1 %>%
select(id = accession, species, strain, e_value = e.value, query_cover = query.cover, perc_ident = perc.ident, length)
fdb.stat <- fdb.filtered %>%
filter(gene_id %in% fdb.list) %>%
mutate(organism = gsub("strain ", "", organism),
species = word(organism, 1, 2),
strain = word(organism, 3, -1),
query_cover = NA, perc_ident = NA) %>%
select(id = gene_id, species, strain, e_value, query_cover, perc_ident, length)
gryc.stat <- gryc %>%
filter(ori_id %in% gryc.list) %>%
mutate(organism = species,
species = word(organism, 1, 2),
strain = word(organism, 3, -1)) %>%
select(id, species, strain, e_value = min_e_value, query_cover, perc_ident = max_ident, length)
combined.blast <- bind_rows(list(refseq = refseq.stat, FungiDB = fdb.stat, GRYC = gryc.stat), .id = "source")
write_tsv(combined.blast, "output/XP_028889033_homologs_combine_blast_stat.tsv")
# also export the rejected list from refseq
refseq.rejected <- refseq.N360 %>%
filter(length < 500, sci.name %in% c("Candida parapsilosis", "Candida tropicalis MYA-3404", "Clavispora lusitaniae ATCC 42720",
"Lodderomyces elongisporus NRRL YB-4239", "Scheffersomyces stipitis CBS 6054")) %>%
select(id = accession, species = sci.name, e_value = e.value, query_cover = query.cover, perc_ident = perc.ident, length)
write_tsv(refseq.rejected, "output/XP_028889033_homologs_length_filtered_blast_stat.tsv")
# in the "output/gene-tree/README.md", I documented an issue with a particular
# sequence, XP_025344407.1, which has a long N-terminal part that is not aligned
# to any other sequences. the solution is to remove the first 900 a.a. of it
# and output a different version of fasta file just for gene tree analysis
truncated <- str_sub(refseqdb.seq$XP_025344407.1, start = 901)
attributes(truncated) <- attributes(refseqdb.seq$XP_025344407.1)
combined.seq$XP_025344407.1 <- truncated
write.fasta(combined.seq, names = combined.names, file.out = "output/XP_028889033_homologs_combine_for_gene_tree.fasta")
Chromosomal locations
The goal of this analysis is to extract the chromosomal locations for all homologs identified. Notice that not all genomes from which these sequences came are well assembled. If the genome is not fully assembled to the chromosome level, the locations would not be meaningful.
Update 2021-06-17 The C. auris refseq strain, B11221, was assembled to the scaffold level on the NCBI Assembly database page. But according to Muñoz et al. 2021, the genome was basically assembled to the chromosomal level – the first 7 scaffolds are chromosomal length, with the remaining scaffolds being short and unassembled fragments. This means I can use the location information for this strain if the gene is located on the first seven scaffolds.
To determine the location on the chromosome, we need to know 1) the coordinate of the gene encoding the protein and 2) the length of the chromosome. Our approach to obtain these data depends upon the source of the sequences. For proteins identified through FungiDB and GRYC, since there are only a small number of them, I just manually searched and entered the information. For Refseq proteins, since it represents the majority of the data, and since the rentrez package allowed me to cross-reference information among the NCBI databases, I’ll attempt to gather the information programmatically.
- Collect genome assembly information for all species/strains included in the Refseq dataset.
# For all sequences with refseq IDs
# First get the genome assembly ID using the protein ID as query
refseq.strain <- unique(with(refseq1, paste(organism, strain, sep = " ")))
names(refseq.strain) <- word(refseq.strain, 1, 2)
refseq.strain["Naumovozyma castellii"] <- "Naumovozyma castellii CBS 4309"
refseq.strain["Candida glabrata"] <- "Candida glabrata 138" # for some reason the original query wouldn't retrieve the result
refseq.strain["Candida parapsilosis"] <- "ASM18276v2" # using the species and strain name combo didn't retrieve the refseq assembly
# fungidb didn't add any new species that refseq hits didn't already have
gryc.strain <- unique(gryc1$species)
names(gryc.strain) <- word(gryc.strain, 1, 2)
gryc.strain["Kluyveromyces lactis"] <- "ASM251v1" # this is the NCBI Assembly for K. lactis NRRL Y-1140, not the GRYC CBS 2359, but we only had one sequence from this species and the sequence is identical to the refseq version from the NRRL strain
all.strain <- c(refseq.strain, gryc.strain)
tmp <- sapply(all.strain, function(i){
#Sys.sleep(1) # added to avoid the API rate limitation error
entrez_search(db = "assembly", term = i, use_history = T)$ids
})
assembly.info <- sapply(tmp, function(i){
if(!is_empty(i))
entrez_summary(db = "assembly", id = i)
})
- For species with chromosomal level assemblies, we can use the protein ID to fish out the chromosomal location and chromosome length information.
# Get the protein's gene ID (not refseq ID)
tmp.gene.lnk <- entrez_link(dbfrom = "protein", id = refseq1$accession, 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")
Warning: Outer names are only allowed for unnamed scalar atomic inputs
# 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
# also get the assembly id
tmp.assembly.lnk <- entrez_link(dbfrom = "nuccore", id = tmp.chr.id, db = "assembly", by_id = T)
# some sequences map to more than one assembly. use the first one
tmp.assembly.id <- sapply(tmp.assembly.lnk, function(i) i$links$nuccore_assembly[1])
# 2021-06-18 for unknown reasons, one of the chromosomes failed to get a link even though
# searches on the NCBI website gives the expected result. Manually add it in
# tmp.assembly.id[[8]] <- "277478"
tmp.assembly <- map_chr(tmp.assembly.id, function(x)
entrez_summary(db = "assembly", id = x)$assemblyaccession)
names(tmp.assembly) <- tmp.chr.id
# construct a new tibble to hold the chromosomal location info
refseq2 <- refseq1 %>%
select(accession, species, organism) %>%
mutate(gene_uid = names(tmp.gene.name), gene_id = tmp.gene.name, alias = tmp.gene.alias) %>%
left_join(tmp.gene.loc, by = c("gene_uid" = "gene_uid")) %>%
mutate(assembly = tmp.assembly[chraccver], chrL = tmp.chr.len[chraccver],
relLoc = round(chrstart/chrL,2)) %>%
select(-exoncount)
# since some species are fully assembled while others are not, let's split them into two and deal with the latter separately
refseq2s <- group_split(refseq2, is.na(chraccver), .keep = FALSE)
# print the ones successfully assembled
refseq2s[[1]] %>% select(-gene_uid, -chraccver) %>% arrange(species, relLoc)
Above are the ones that we can find the chromosomal information for.
- Next let’s deal with the partially assembled genomes. The idea is to first download the genome annotation files for the species/strains and then extract the genomic information from them. Because some species/strain could have multiple assemblies, we need to collect all of them and then identify the latest / representative one. I found that each species/strain has only one assembly that has the “refseq” status. Therefore I chose that as the representative assembly.
Below are all the species in our dataset, followed by the corresponding refseq assembly.
# 1. List the unique species in the dataset
sps.list <- names(all.strain)
sps.list
[1] "Candida pseudohaemulonii" "Candida parapsilosis" "Candida tropicalis"
[4] "Debaryomyces hansenii" "Candida albicans" "Clavispora lusitaniae"
[7] "Candida auris" "Candida haemuloni" "Candida duobushaemulonis"
[10] "Scheffersomyces stipitis" "Candida dubliniensis" "Lodderomyces elongisporus"
[13] "Naumovozyma castellii" "Candida glabrata" "Nakaseomyces delphensis"
[16] "Kluyveromyces lactis" "Nakaseomyces bracarensis" "Nakaseomyces nivariensis"
# 2. Obtain assembly information
## explanation for the fields from the assembly record: https://www.ncbi.nlm.nih.gov/assembly/help/
## to parse information from the "meta" field, which is in XML format, I used the "XML" package
## and to access particular nodes from the object, I used the syntax as specified for XPatH
## reference: https://www.w3schools.com/xml/xpath_syntax.asp
sps.stat <- tibble(species = character(), strain = character(), assemblyaccession = character(),
assemblyname = character(), assemblystatus = character(),
n_chr = integer(), n_scaffold = integer(), n_contig = integer(),
lastupdate = character(), submitter = character(),
refseq = character(), ftpreport = character())
for(s in sps.list){
ss <- assembly.info[[s]]
if(class(ss)[1] == "esummary"){
# some strains have just one assembly
# most of the information are easily accessible as elements of the list
# for the # of contigs and # of scaffolds, however, I need to parse the XML formatted "extra" field
f = newXMLNode("assembly")
parseXMLAndAdd(ss$meta, f)
n.chr = getNodeSet(f, "//Stat[@category='chromosome_count']") %>% xmlValue %>% as.integer
n.scaffold = getNodeSet(f, "//Stat[@category='scaffold_count' and @sequence_tag='all']") %>% xmlValue %>% as.integer
n.contig = getNodeSet(f, "//Stat[@category='contig_count']") %>% xmlValue %>% as.integer
tmp.strain = ss$biosource$infraspecieslist$sub_value
sps.stat <- sps.stat %>%
add_row(species = s, strain = ifelse(is.null(tmp.strain), ss$biosource$isolate, tmp.strain),
assemblyaccession = ss$assemblyaccession, assemblyname = ss$assemblyname, assemblystatus = ss$assemblystatus,
n_chr = n.chr, n_scaffold = n.scaffold, n_contig = n.contig,
lastupdate = ss$lastupdatedate, submitter = ss$submitterorganization,
refseq = ss$refseq_category, ftpreport = ss$ftppath_assembly_rpt)
}else if(class(ss)[1] == "esummary_list"){
# some strains have more than one assembly and we iterate through them
ss0 = ss
for(ss in ss0){
f = newXMLNode("assembly")
parseXMLAndAdd(ss$meta, f)
n.chr = getNodeSet(f, "//Stat[@category='chromosome_count']") %>% xmlValue %>% as.integer
n.scaffold = getNodeSet(f, "//Stat[@category='scaffold_count' and @sequence_tag='all']") %>% xmlValue %>% as.integer
n.contig = getNodeSet(f, "//Stat[@category='contig_count']") %>% xmlValue %>% as.integer
sps.stat <- sps.stat %>%
add_row(species = s, strain = ss$biosource$infraspecieslist$sub_value,
assemblyaccession = ss$assemblyaccession, assemblyname = ss$assemblyname, assemblystatus = ss$assemblystatus,
n_chr = n.chr, n_scaffold = n.scaffold, n_contig = n.contig,
lastupdate = ss$lastupdatedate, submitter = ss$submitterorganization,
refseq = ss$refseq_category, ftpreport = ss$ftppath_assembly_rpt)
}
}
}
sps.stat <- sps.stat %>%
mutate(n_chr = ifelse(n_chr <= 1, NA, n_chr),
n_scaffold = ifelse(n_scaffold <= 1, NA, n_scaffold),
n_contig = ifelse(n_contig <= 0, NA, n_contig))
sps.stat %>% filter(refseq != "na") %>% select(-assemblyaccession, -assemblyname, -ftpreport, -refseq) %>% arrange(assemblystatus)
Now that we have got the representative genome assemblies, we can use the ftppath to download the assembly information.
ftpreport <- sps.stat %>% filter(refseq != "na") %>% pull(ftpreport)
ftpfeature <- gsub("_assembly_report.txt", "_feature_table.txt.gz", ftpreport)
for(f in c(ftpreport, ftpfeature)){
destfile = paste0("data/assembly-info/",last(str_split(f, "/")[[1]]))
# https://stackoverflow.com/questions/14904983/how-do-i-check-the-existence-of-a-downloaded-file
if(!file.exists(destfile)){
tryCatch(download.file(f, destfile), error = function(e) 1)
}
}
Note that three genomes do not have feature tables at the time of downloading (2021-02-04). They are: Nakaseomyces delphensis CBS 2170, Nakaseomyces bracarensis CBS 10154, Nakaseomyces nivariensis CBS 9983.
Instead of loading all the tables, I found it is easier to just search through them using grep. The idea is to iterate through the refseq2s[[2]], which contains all the proteins with undetermined locations. For each entry, search the gene ID against the feature tables using zgrep and capture the output.
search_chr_loc <- function(gene){
cmd <- sprintf("zgrep %s data/assembly-info/*feature_table.txt.gz | grep ':gene'", gene)
res <- system(cmd, intern = TRUE)
if(length(res) > 1){
stop(sprintf('More than one record found for "%s"', gene))
}else if(length(res) == 0){
return(NA)
}
gInfo <- str_split(res, "\t")[[1]]
# 1: file name; 3: assembly name; 7: "chromosome" name; 8: start; 9: end; 10: strand; 17: gene ID; 18: gene length
assembly.file <- gsub("feature_table.txt.gz:gene", "assembly_report.txt", gInfo[1])
res1 <- system(paste("grep",gInfo[7],assembly.file), intern = TRUE)
if(length(res1) > 1)
stop(sprintf('More than one record found for "%s"', gene[7]))
chr <- str_split(res1, "\t")[[1]]
# 1: seq_name; 7: refseq_acc; 9: length
final <- tibble(gene_id = gene, assembly = gInfo[3], chrloc = chr[1], chraccver = gInfo[7],
chrstart = gInfo[8], chrstop = gInfo[9], chrL = chr[9])
return(final)
}
tmp1 <- map_dfr(refseq2s[[2]]$gene_id, search_chr_loc)
tmp1 <- tmp1 %>%
mutate(chrstart = as.integer(chrstart), chrstop = as.integer(chrstop), chrL = as.integer(chrL),
relLoc = round(chrstart/chrL, 2))
Update the original table
refseq2s[[2]] <- rows_update(refseq2s[[2]], tmp1, by = "gene_id")
refseq3 <- bind_rows(refseq2s)
- Let’s do the same search with the FungiDB hits. GRYC has no matches since none of the GRYC strains are in the NCBI assembly database.
tmp2 <- map_dfr(fdb.list[-2], search_chr_loc) # the B9J08 sequence is ignored since its genome is not the refseq assembly
tmp2 <- tmp2 %>%
rename(chr_name = chrloc) %>%
mutate(chrstart = as.integer(chrstart), chrstop = as.integer(chrstop),
chrL = as.integer(chrL), relLoc = round(chrstart/chrL, 2))
The B9J08 sequence is the missing homolog from B11221. As shown in the README.md of this blast folder, I found that the “missing homolog” is actually present, but was broken into two fragments due to incomplete assembly. The C-terminus of the B9J08 sequence aligns to a fragment NW_021640165.1, which corresponds to scaffold00001. Its length is 4,303,397 bp, which is nearly the same as the reported chromosome 1 length from Muñoz et al. 2021 (Fig. 1). I therefore believe that this homolog (Hil4) is located at the 3’ tip of chromosome 1 of B11221.
- Finally, we can combine the above results with the manually entered GRYC results
non.refseq <- read_tsv("data/GRYC/XP_028889033_homologs_chr_loc_fungidb_gryc.txt", comment = "#") %>%
rows_update(tmp2, by = "gene_id") # update the list with the search results above for the fungidb ones
── Column specification ──────────────────────────────────────────────────────────────────
cols(
source = col_character(),
accession = col_character(),
species = col_character(),
species_id = col_character(),
strain = col_character(),
assembly = col_character(),
assemblystatus = col_character(),
n_seqs = col_double(),
gene_id = col_character(),
gene_name = col_logical(),
chraccver = col_character(),
chr_name = col_character(),
chrstart = col_number(),
chrstop = col_number(),
chrL = col_number(),
relLoc = col_double()
)
# decide the tibble structure:
# accession (for protein from source); source: GRYC/FungiDB/Refseq; species; species_id; strain
# assembly; assemblystatus; n_seqs; gene_id; gene_name; chraccver; chr_name; chrstart, chrstop;
# chrL; relLoc
refseq4 <- refseq3 %>%
rename(gene_id = alias, gene_name = gene_id, chr_name = chrloc) %>%
left_join(sps.stat %>% filter(refseq != "na") %>%
select(organism = species, strain, assembly = assemblyaccession, assemblystatus, starts_with("n_")),
by = c("organism", "assembly")) %>%
mutate(source = "Refseq", species_id = paste0(substr(species,1,1),word(species, 2, 2)),
# https://stackoverflow.com/questions/28070878/r-use-min-within-dplyrmutate
n_seqs = pmin(n_chr, n_scaffold, n_contig, na.rm = T)) %>%
select(source, accession, species, species_id, strain, assembly, assemblystatus, n_seqs, gene_id, gene_name,
chraccver, chr_name, chrstart, chrstop, chrL, relLoc)
all.genes <- bind_rows(refseq4, non.refseq)
write_tsv(all.genes, "output/XP_028889033_homologs_chr_loc.tsv")
LS0tCnRpdGxlOiAiQkxBU1QiCmF1dGhvcjogIkJpbiBIZSIKZGF0ZTogIjA3LzAxLzIwMjAgICh1cGRhdGVkOiBgciBTeXMuRGF0ZSgpYCkiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2RlcHRoOiA1CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKLS0tCgpUaGlzIGFuYWx5c2lzIGFjY29tcGFuaWVzIHRoZSBCTEFTVCBzZWFyY2ggdG8gaWRlbnRpZnkgaG9tb2xvZ3MgZm9yIFhQXzAyODg4OTAzMwoKYGBge3IgbG9hZF9saWJyYXJpZXN9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KHRpZHl2ZXJzZSkpCiMgaW5zdGFsbCByZW50cmV6IHBhY2thZ2UgaWYgbm90IGFscmVhZHkgaW5zdGFsbGVkCmlmICghcmVxdWlyZU5hbWVzcGFjZSgicmVudHJleiIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIGluc3RhbGwucGFja2FnZXMoInJlbnRyZXoiKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShyZW50cmV6KSkKIyBzZXQgTkNCSSBFbnRyZXogQVBJIGtleQpzZXRfZW50cmV6X2tleSgiZjRmMDFjMjIwZjNhMGExNjJiYWRhNWIwYmQ1ZDRiMTMxOTA4IikKIyBpbnN0YWxsIHNlcWluciBwYWNrYWdlIGlmIG5vdCBhbHJlYWR5IGluc3RhbGxlZAppZiAoIXJlcXVpcmVOYW1lc3BhY2UoInNlcWluciIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIGluc3RhbGwucGFja2FnZXMoInNlcWluciIpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KHNlcWlucikpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KFhNTCkpCmBgYAoKIyMgUmVmc2VxX3Byb3RlaW4KSSB1c2VkIGJsYXN0cCB0byBzZWFyY2ggdGhlIE4tdGVybWluYWwgNTYwIHJlc2lkdWVzIGluIFhQXzAyODg4OTAzMyBhZ2FpbnN0IHRoZSByZWZzZXFfcHJvdGVpbiBkYXRhYmFzZS4gKFNlZSBSRUFETUUubWQgZm9yIGRldGFpbHMpLiBUaGUgdGF4b25vbXkgZGlzdHJpYnV0aW9uIGlzIHNob3duIGJlbG93OgohW3RheG9ub215IG9mIHJlZnNlcV9wcm90ZWluIGJsYXN0XShpbWcvMjAyMDA3MDQtbmNiaS1ibGFzdHAtWFBfMDI4ODg5MDMzLXRheG9ubXktZGlzdHJpYnV0aW9uLnBuZykKYGBge3IgcmVmc2VxX3Byb3RlaW5fYmxhc3R9CiMgbG9hZCB0YWJsZQpyZWZzZXEgPC0gcmVhZF9jc3YoImRhdGEvbmNiaS1yZWZzZXEvWFBfMDI4ODg5MDMzX2hvbW9sb2dzX3JlZnByb3RfTjU2MF9zZWxlY3QuY3N2IiwgY29sX3R5cGVzID0gImNkZG5ubmMiKQpyZWZzZXEubGVuZ3RoIDwtIHJlYWRfdHN2KCJkYXRhL25jYmktcmVmc2VxL1hQXzAyODg4OTAzM19ob21vbG9nc19yZWZwcm90X041NjBfc2VsZWN0X2xlbmd0aC50eHQiLCBjb2xfbmFtZXMgPSBjKCJhY2Nlc3Npb24iLCJsZW5ndGgiKSwgY29sX3R5cGVzID0gImNpIikKcmVmc2VxIDwtIGxlZnRfam9pbihyZWZzZXEsIHJlZnNlcS5sZW5ndGgpICU+JSBzZWxlY3QoYWNjZXNzaW9uLCBldmVyeXRoaW5nKCkpCiMgRG8gdGhlIHJlZnNlcSBoaXRzIHNob3cgc2ltaWxhciByZWxhdGlvbnNoaXAgYmV0d2VlbiBlLXZhbHVlIGFuZCBwcm90ZWluIGxlbmd0aD8KcmVmc2VxICU+JSAKICBnZ3Bsb3QoYWVzKHggPSAtbG9nMTAoZS52YWx1ZSksIHkgPSBsb2cyKGxlbmd0aCksIGNvbCA9IChsZW5ndGggPiA1MDApKSkgKyBnZW9tX3BvaW50KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpnZ3NhdmUoImltZy8yMDIwMDcyMy1YUF8wMjg4ODkwMzMtcmVmc2VxLWhpdHMtZS12YWx1ZS1ieS1sZW5ndGgucG5nIiwgd2lkdGggPSAzLCBoZWlnaHQgPSAzKQojIFRoZSBhbnN3ZXIgaXMgeWVzLiBTbyBsZXQncyBmaWx0ZXIgdGhlc2Ugb3V0IGFuZCBzaG93IHdoYXQgdGhleSBhcmUKcmVmc2VxMCA8LSByZWZzZXEgJT4lIAogIGZpbHRlcihsZW5ndGggPiA1MDAsICFncmVwbCgicnVnb3NhIiwgZGVzY3JpcHRpb24pKQpyZWZzZXEgJT4lIGZpbHRlcihsZW5ndGggPD0gNTAwKSAlPiUgc2VsZWN0KGFjY2Vzc2lvbiwgcXVlcnkuY292ZXIsIGUudmFsdWUsIHBlcmMuaWRlbnQsIGxlbmd0aCwgZGVzY3JpcHRpb24pCiMgdGhlIGFib3ZlIHNjcmlwdCB1c2VzIGEgc3RyYW5nZSB3YXkgdG8gbW92ZSBhIGNvbHVtbiB0byB0aGUgbGFzdCwgYnkgZmlyc3QgcmVtb3ZpbmcgaXQgYW5kIHRoZW4gYWRkaW5nIGl0IGJhY2sKIyB0aGFua3MgdG8gaHR0cHM6Ly9naXRodWIuY29tL3RpZHl2ZXJzZS9kcGx5ci9pc3N1ZXMvMzA1MQpgYGAKX0Rpc2N1c3Npb25fCgoxLiBXZSBzZWUgdGhhdCB0aGUgZXhjbHVkZWQgc2VxdWVuY2VzIG5vdCBvbmx5IGhhdmUgbGVzcyBzaWduaWZpY2FudCBlLXZhbHVlcywgYnV0IGFsc28gaGF2ZSBiYXJlbHkgYWJvdmUgNTAlIHF1ZXJ5IGNvdmVyYWdlLiBJbiB0ZXJtcyBvZiBzcGVjaWVzIGRpc3RyaWJ1dGlvbiwgbW9zdCBvZiB0aGVtIGFyZSBmcm9tIF9TLiBzdGlwaXRpc18gYW5kIF9ELiBydWdvc2FfLiBUaGUgc2Vjb25kIHNwZWNpZXMgd2lsbCBiZSByZW1vdmVkIGFueXdheXMgKHNlZSBub3RlcyBpbiB0aGUgYDAzLWdlbmUtdHJlZWAgZm9sZGVyKQoxLiBFdmVuIHRob3VnaCBhIGNvdXBsZSBvZiB0aGUgX1MuIHN0aXBpdGlzXyBzZXF1ZW5jZXMgd2VyZSBhbm5vdGF0ZWQgYXMgImZ1bmdhbCBoeXBoYWwgcmVndWxhdGVkIGNlbGwgd2FsbCBwcm90ZWlucyIsIHdpdGggbGVzcyB0aGFuIDUwMCBhLmEuLCB0aGV5IGFyZSB1bmxpa2VseSB0byBlbmNvZGUgdGhlIHN0YWxrIGFuZCBpbiBhIHNlcGFyYXRlIGFuYWx5c2lzLCBJIGFsc28gZm91bmQgdGhlbSB0byBub3QgcG9zc2VzcyB0aGUgR1BJLWFuY2hvci4gQWxsIGluIGFsbCwgSSB0aGluayB0aGVzZSBzZXF1ZW5jZXMgYXJlIHVubGlrZWx5IHRvIGJlIGNhbmRpZGF0ZSBhZGhlc2lucy4KCiMjIyBOQ0JJIGJsYXN0cCB3aXRoIE4zNjAgcXVlcnkKSSByZXBlYXRlZCB0aGUgTkNCSSBibGFzdHAgc2VhcmNoIHdpdGggdGhlIGZpcnN0IDM2MCBhbWlubyBhY2lkIHJlc2lkdWVzIGluc3RlYWQgb2YgdGhlIGZpcnN0IDU2MC4gQmVjYXVzZSB0aGUgNTYwIGFhIHNlcXVlbmNlIGNvbnRhaW5zIGEgY29weSBvZiB0aGUgSHlyMSBkb21haW4gaW4gYWRkaXRpb24gdG8gdGhlIFBGMTE3NjUgZG9tYWluLCBpdCBpcyBwb3NzaWJsZSB0aGF0IHNvbWUgb2YgdGhlIGhpdHMgYmVhciBzaW1pbGFyaXR5IHRvIHRoZSBIeXIxIGFuZCBub3QgdGhlIEh5cGhhbF9yZWdfQ1dQIGRvbWFpbi4gQWxzbywgd2hlbiBJIGFwcGxpZWQgdGhlID41MCUgcXVlcnkgY292ZXJhZ2UgZmlsdGVyIG9uIHRoZSByZXN1bHRpbmcgaG9tb2xvZyBsaXN0LCB0aGUgZWZmZWN0aXZlIGN1dG9mZiBmb3IgdGhlIFBGMTE3NjUgZG9tYWluIHdvdWxkIGJlIGhpZ2hlciwgc2luY2UgbW9zdCBvZiB0aGUgaGl0cyBkb24ndCBzaGFyZSB0aGUgSHlyMSBkb21haW4uCgpCZWxvdyBpcyB0aGUgdGF4b25vbXkgZGlzdHJpYnV0aW9uIG9mIHRoZSBoaXRzOgohW3RheG9ub215IG9mIHJlZnNlcV9wcm90ZWluIGJsYXN0XShpbWcvMjAyMTAxMjQtYmxhc3RwLU4zNjAtcmVmc2VxLWFsbC1zcGVjaWVzLXBoeWxvZ2VueS5wbmcpClRvIGRldGVybWluZSB0aGUgZWZmZWN0IG9mIGNoYW5naW5nIHRoZSBxdWVyeSBzZXF1ZW5jZSBmcm9tIE41NjAgdG8gTjM2MCBhbWlubyBhY2lkcywgSSBkb3dubG9hZGVkIHRoZSBoaXRzIGRlc2NyaXB0aW9uIGFuZCB0aGUgZmFzdGEgc2VxdWVuY2VzIGZyb20gYm90aCBzZWFyY2hlcy4gTm93IGxldCdzIGNvbXBhcmUgdGhlbS4KCmBgYHtyLCByZXN1bHRzPSJhc2lzIn0KaGl0VGFiQ29sTmFtZXMgPC0gYygiZGVzY3JpcHRpb24iLCJzY2kubmFtZSIsImNvbW1vbi5uYW1lIiwidGF4aWQiLCJtYXguc2NvcmUiLAogICAgICAgICAgICAgICAgICAgICJ0b3RhbC5zY29yZSIsInF1ZXJ5LmNvdmVyIiwiZS52YWx1ZSIsInBlcmMuaWRlbnQiLCJsZW5ndGgiLCJhY2MiKQpyZWZzZXEuTjM2MCA8LSByZWFkX2NzdigiZGF0YS9uY2JpLXJlZnNlcS9YUF8wMjg4ODkwMzNfaG9tb2xvZ3NfcmVmcHJvdF9OMzYwLmNzdiIsICBjb2xfdHlwZXMgPSAiY2NjY2RkY2RkaWMiLCBjb2xfbmFtZXMgPSBoaXRUYWJDb2xOYW1lcywgc2tpcCA9IDEpICU+JSAKICBtdXRhdGUocXVlcnkuY292ZXIgPSBwYXJzZV9udW1iZXIocXVlcnkuY292ZXIpLCAKICAgICAgICAgYWNjZXNzaW9uID0gZ3N1YignWyIpXScsJycsc3RyX3NwbGl0KGFjYywgIiwiLCBzaW1wbGlmeSA9IFQpWywyXSkpCgpjYXQoIioqU2VxdWVuY2VzIG9ubHkgaW4gdGhlIE4zNjAgc2V0IGFmdGVyIGFwcGx5aW5nIHRoZSA1MCUgcXVlcnkgY292ZXJhZ2UgZmlsdGVyKioiKQphbGwuTjM2MCA8LSByZWZzZXEuTjM2MCAlPiUgZmlsdGVyKHF1ZXJ5LmNvdmVyID49IDUwLCBsZW5ndGggPiA1MDApICU+JSBwdWxsKGFjY2Vzc2lvbikKb25seS5pbi5OMzYwIDwtIHNldGRpZmYoYWxsLk4zNjAsIHJlZnNlcTAkYWNjZXNzaW9uKQpyZWZzZXEuTjM2MCAlPiUgZmlsdGVyKGFjY2Vzc2lvbiAlaW4lIG9ubHkuaW4uTjM2MCkgJT4lIAogIHNlbGVjdChhY2Nlc3Npb24sIHFjb3YgPSBxdWVyeS5jb3ZlciwgZS52YWx1ZSwgcGlkZW50ID0gcGVyYy5pZGVudCwgbGVuZ3RoLCBzY2kubmFtZSkgJT4lIAogIGFycmFuZ2Uoc2NpLm5hbWUpCgpjYXQoIioqU2VxdWVuY2VzIG9ubHkgaW4gdGhlIE41NjAgc2V0IGFmdGVyIGFwcGx5aW5nIHRoZSA1MCUgcXVlcnkgY292ZXJhZ2UgZmlsdGVyKioiKQpvbmx5LmluLk41NjAgPC0gc2V0ZGlmZihyZWZzZXEwJGFjY2Vzc2lvbiwgYWxsLk4zNjApCnJlZnNlcTAgJT4lIGZpbHRlcihhY2Nlc3Npb24gJWluJSBvbmx5LmluLk41NjApICU+JSAKICBzZWxlY3QoYWNjZXNzaW9uLCBxY292ID0gcXVlcnkuY292ZXIsIGUudmFsdWUsIHBpZGVudCA9IHBlcmMuaWRlbnQsIGxlbmd0aCkKYGBgCgpfRGlzY3Vzc2lvbl8KCkkgd291bGQgaW5jbHVkZSA3IG9mIHRoZSBmaXJzdCAxMCBzZXF1ZW5jZXMgaW4gdGhlIHRhYmxlIGFib3ZlLCBleGNlcHQgZm9yIHRob3NlIGZyb20gX0MuIG9ydGhvcHNpbG9zaXNfLCBpbnRvIHRoZSBmaW5hbCBsaXN0LiBUaGUgcmVhc29uIHRvIGluY2x1ZGUgdGhlbSBpcyBiZWNhdXNlIHRoZSA1MCUgcXVlcnkgY292ZXJhZ2UgY3V0b2ZmIHdhcyB1bmZhaXJseSBhcHBsaWVkIHRvIHRoZW0sIHNpbmNlIHVzaW5nIHRoZSBOMzYwIGFtaW5vIGFjaWQgYXMgcXVlcnksIGFsbCBvZiB0aGVtIHBhc3NlZCB0aGUgY3V0b2ZmLiBUaGUgcmVhc29uIHRvIG5vdCBpbmNsdWRlIF9DLiBvcnRob3BzaWxvc2lzXyBpcyB0byBsaW1pdCB0aGUgdG90YWwgbnVtYmVyIG9mIHNwZWNpZXMgYW5kIHNlcXVlbmNlcyBpbiB0aGUgdmFyaW91cyBwbG90cy4gVGhlIG1haW4gY29udHJpYnV0aW9uIG9mIF9DLiBvcnRob3BzaWxvc2lzXywgaWYgSSBpbmNsdWRlIGl0LCBpcyB0byBlbmhhbmNlIHRoZSBjb25jbHVzaW9uIHRoYXQgdGhlIG51bWJlciBvZiBob21vbG9ncyBpbiBfQy4gcGFyYXBzaWxvc2lzXyBhbmQgX0wuIGVsb25nYXR1c18gYXJlIGxvdy4gVGhlIHJlbWFpbmluZyAxNSBzZXF1ZW5jZXMgYXJlIGZyb20gc3BlY2llcyBJJ20gbm90IGdvaW5nIHRvIGluY2x1ZGUuCgpPbmUgc2VxdWVuY2UgZnJvbSB0aGUgb3JpZ2luYWwgYmxhc3Qgc2VhcmNoLCBYUF8wMDEzODM5NTMuMiBmcm9tIF9TLiBzdGlwaXRpc18gZGlkbid0IG1ha2UgdGhlIGN1dCBpbiB0aGUgbmV3IHNlYXJjaC4gV2hlbiBJIGxvd2VyZWQgdGhlIGUtdmFsdWUgY3V0b2ZmIHRvIDFlLTMsIGl0IGFwcGVhcmVkIGF0IHRoZSBib3R0b20gb2YgdGhlIGhpc3QgbGlzdC4gRm9yIGNvbnNpc3RlbmN5J3Mgc2FrZSwgSSBkZWNpZGVkIHRvIGV4Y2x1ZGUgaXQgZnJvbSB0aGUgbmV3IGFuYWx5c2lzLgoKQXQgdGhlIHNhbWUgdGltZSwgSSBmb3VuZCB0aGF0ICoqcmVsYXhpbmcgdGhlIGUtdmFsdWUgY3V0b2ZmIGZyb20gMWUtNSB0byAxZS0zIG9ubHkgcmVzdWx0ZWQgaW4gZm91ciBuZXcgc2VxdWVuY2VzKiouIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGUgbGlzdCBvZiBob21vbG9ncyBpcyBub3QgdG9vIHNlbnNpdGl2ZSB0byB0aGUgYXJiaXRyYXJ5IGNob2ljZSBvZiB0aGUgZS12YWx1ZSBjdXRvZmYuIExvb2tpbmcgYXQgdGhlIHRocmVlIG90aGVyIHNlcXVlbmNlcyBtYWtpbmcgdGhlIGN1dCBhdCAxZS0zLCBvbmUgaXMgZnJvbSBhIHNwZWNpZXMgSSBhbSBub3QgaW5jbHVkaW5nIGFueXdheS4gVGhlIG90aGVyIHR3byB3ZXJlIGFjdHVhbGx5IGFkZGVkIGZyb20gdGhlIEZ1bmdpREIgc2VhcmNoIHJlc3VsdCAoc2VlIG5vdGVzIGFuZCBhbmFseXNpcyBiZWxvdykuIFRoaXMgbWFkZSBtZSByZWFsaXplIHRoYXQgKiplLXZhbHVlIGN1dG9mZiBpcyBkYXRhYmFzZS1zaXplLXNwZWNpZmljKiogYW5kIHRodXMgdXNpbmcgdGhlIHNhbWUgY3V0b2ZmIGRvZXNuJ3QgbWVhbiB0aGUgc2FtZSB0aGluZyBiZXR3ZWVuIHJlZnNlcV9kYiwgZnVuZ2lkYiBhbmQgR1JZQy4gQnV0IHRoZSByZXN1bHRpbmcgZGlmZmVyZW5jZXMgaW4gdGhlIG51bWJlciBvZiBoaXRzIGluY2x1ZGVkIG9yIGV4Y2x1ZGVkIGFyZSByZWxhdGl2ZWx5IHNtYWxsIGFuZCB0aHVzIGRvbid0IGFmZmVjdCB0aGUgb3ZlcmFsbCBjb25jbHVzaW9ucy4KCiFbXShpbWcvMjAyMTAxMzEtYmxhc3RwLWUtdmFsdWUtMWUtMy1zZXFzLnBuZykKYGBge3IgaW5jbHVkZV9hZGRpdGlvbmFfc2Vxc19yZWZzZXF9CnJlZnNlcS5hZGQgPC0gYygiWFBfMDI0NzE2MzY1LjEiLCAiWFBfNzE3Nzc1LjIiLCAiWFBfMDM2NjY1MjY0LjEiLCAiWFBfMDAyNTQ3NjE2LjEiLCAiWFBfMDAyNTQ1NTEwLjEiLCAiWFBfMDAyNjE0MTE1LjEiLCAiWFBfMDAyNzcwMjQwLjEiKQojIHJlbW92ZSBvbmUgc2VxLCBzZWUgbm90ZXMgYWJvdmUKcmVmc2VxMCA8LSByZWZzZXEwICU+JSBmaWx0ZXIoYWNjZXNzaW9uICE9ICJYUF8wMDEzODM5NTMuMiIpCnJlZnNlcS5jb21iaW5lZCA8LSByZWZzZXEuTjM2MCAlPiUgCiAgZmlsdGVyKGFjY2Vzc2lvbiAlaW4lIHJlZnNlcS5hZGQpICU+JSAKICBzZWxlY3QoYWNjZXNzaW9uLCBkZXNjcmlwdGlvbiwgbWF4LnNjb3JlLCB0b3RhbC5zY29yZSwgcXVlcnkuY292ZXIsIGUudmFsdWUsIHBlcmMuaWRlbnQsIGxlbmd0aCkgJT4lIAogIGJpbmRfcm93cyhyZWZzZXEwKQpgYGAKCiMjIyBNaXNzaW5nIGhvbW9sb2cgaW4gX0MuIGF1cmlzXyBCMTEyMjEKKipfVXBkYXRlIDIwMjEtMDMtMDFfKioKCk11w7FveiBldCBhbCAyMDIxIHJlcG9ydGVkIDggbWVtYmVycyBpbiB0aGUgSWZmL0h5cjEgZmFtaWx5IGluIF9DLiBhdXJpc18gY2xhZGVzIEkgYW5kIElJSS4KCiFbTXXDsW96IGV0IGFsIDIwMjEgRmlnLiA1YSwgQy4gYXVyaXMgY2xhZGUgSSBhbmQgSUlJIGhhdmUgOCBtZW1iZXJzXSguL2ltZy8yMDIxMDMwMS1tdW5vei0yMDIxLWdlbmV0aWNzLWlmZi1mYW1pbHktaW4tY2F1cmlzLnBuZykKCk15IG93biBibGFzdHAgb25seSBpZGVudGlmaWVkIDcgbWVtYmVycyBpbiB0aGUgcmVmc2VxX3Byb3QgZGF0YWJhc2UsIHdoaWNoIGluY2x1ZGVzIEIxMTIyMSBidXQgbm90IEI4NDQxLiBBIHNlY29uZCBibGFzdHAgc2VhcmNoIGFnYWluc3QgdGhlIG5yX3Byb3QgZGF0YWJhc2UsIHdpdGggdGF4b25vbXkgcmVzdHJpY3RlZCB0byBfQy4gYXVyaXNfIGNvbmZpcm1lZCB0aGF0IHdoaWxlIEI4NDQxIHNob3dlZCA4IGhpdHMsIEIxMTIyMSBvbmx5IHNob3dlZCA3LiBUaGlzIGlzIGluY29uc2lzdGVudCB3aXRoIHRoZSBNdcOxb3ogMjAyMSByZXBvcnQuIFRoZSBkaXNjcmVwYW5jeSBjb3VsZCBiZSBkdWUgdG8gdGhlIHByb3RlaW4gZmFzdGEgZmlsZSBpbiB0aGUgQjExMjIxIHJlZnNlcSBhc3NlbWJseSBiZWluZyBpbmNvbXBsZXRlLiBUbyBrZWVwIG91ciByZXN1bHRzIGNvbnNpc3RlbnQgd2l0aCB0aGUgcmVjZW50IHB1YmxpY2F0aW9uLCBJJ2xsIGFkZCB0aGlzIG9uZSBCODQ0MSBzZXF1ZW5jZSB0byB0aGUgaG9tb2xvZyBkYXRhYmFzZSBtYW51YWxseSBzbyB3ZSB3b3VsZCBoYXZlIDggbWVtYmVycywgd2hpY2ggaXMgdGhlIG1heCBzaXplIG9mIHRoZSBmYW1pbHkgYW1vbmcgYWxsIGtub3cgX0MuIGF1cmlzXyBzdHJhaW5zLiBBbHRob3VnaCwgbm90ZSB0aGF0IHRoZSA4IGdlbmVzIGRvIG5vdCBhbGwgY29tZSBmcm9tIHRoZSBzYW1lIHN0cmFpbiBhbnkgbW9yZS4gSSBkb24ndCBleHBlY3QgdGhpcyB0byBjaGFuZ2UgYW55IG9mIG91ciBkb3duc3RyZWFtIGFuYWx5c2VzLCBzaW5jZSB0aGUgdHdvIHN0cmFpbnMgYXJlIGNsb3NlbHkgcmVsYXRlZCB3aXRoaW4gdGhlIHNhbWUgY2xhZGUgaW4gX0MuIGF1cmlzXy4KCiFbQjExMjIxIGlzIG1pc3Npbmcgb25lIHNlcXVlbmNlXSguL2ltZy8yMDIxMDMwMS1ibGFzdHAtbnItaWRlbnRpZnktQjg0NDEtOHRoLW1lbWJlci5wbmcpCgoKCiMjIEZ1bmdpZGIKCiMjIyBVcGRhdGUgMjAyMS0wMS0yNCBbSEJdIG5ldyBzZWFyY2ggd2l0aCBOMzYwIGFhCgpJIHJlcGVhdGVkIHRoZSBzZWFyY2ggd2l0aCBlaXRoZXIgTjM2MCBvciBONTYwIGFhLCB3aXRoIGUtdmFsdWUgMWUtNSBjdXRvZmYgYW5kIGxvdyBjb21wbGV4aXR5IGZpbHRlciBvbi4gVGhlIGZpcnN0IHNlYXJjaCB5aWVsZGVkIDEwNCBoaXRzIGFuZCB0aGUgc2Vjb25kIDEwNS4gSSBkb3dubG9hZGVkIHRoZSByZXN1bHRzIGZyb20gdGhlIGZpcnN0IHNlYXJjaCBhbmQgbmFtZWQgdGhlbSB3aXRoICJOMzYwIiBpbiB0aGVpciBmaWxlbmFtZXMuIEJlbG93IEkgd2lsbCBjb21wYXJlIHRoZSBsaXN0IHRvIG15IHByZXZpb3VzIGxpc3QuCgpgYGB7ciBmdW5naWRiX29sZF92c19uZXcsIHJlc3VsdHM9ImFzaXMifQojIG9sZDogbG9hZCB0YWJsZQpmdW5naWRiIDwtIHJlYWRfdHN2KCJkYXRhL2Z1bmdpZGIvWFBfMDI4ODg5MDMzX2hvbW9sb2dzX2Z1bmdpZGJfTjU2MF90YWJsZS50c3YiLCBjb2xfdHlwZXMgPSAiY2NjZGRjY2NkY2MiKQpmdW5naWRiJHNwZWNpZXNfaWQgPC0gbWFwX2NocihzdHJfc3BsaXQoZnVuZ2lkYiRvcmdhbmlzbSwgIiAiKSwgZnVuY3Rpb24oeCkgcGFzdGUwKHN1YnN0cih4WzFdLDEsMSksIHhbMl0pKQojIG5ldzogbG9hZCB0YWJsZQpmdW5naWRiLm5ldyA8LSByZWFkX3RzdigiZGF0YS9mdW5naWRiL1hQXzAyODg4OTAzM19ob21vbG9nc19mdW5naWRiX04zNjBfdGFibGUudHN2IiwgY29sX3R5cGVzID0gImNjY2RkY2NjY2NjY2QiKQpmdW5naWRiLm5ldyRzcGVjaWVzX2lkIDwtIG1hcF9jaHIoc3RyX3NwbGl0KGZ1bmdpZGIubmV3JG9yZ2FuaXNtLCAiICIpLCBmdW5jdGlvbih4KSBwYXN0ZTAoc3Vic3RyKHhbMV0sMSwxKSwgeFsyXSkpCiMgcmV2ZWFsIGRpZmZlcmVuY2VzCmNhdCgiKipzZXF1ZW5jZXMgaW4gdGhlIG9yaWdpbmFsIGxpc3QgYnV0IG5vdCBpbiB0aGUgbmV3IG9uZSoqIikKc2V0ZGlmZihmdW5naWRiJGdlbmVfaWQsIGZ1bmdpZGIubmV3JGdlbmVfaWQpCmNhdCgiKipzZXF1ZW5jZXMgaW4gdGhlIG5ldyBsaXN0IGJ1dCBub3QgaW4gdGhlIG9sZCBvbmUqKiIpCnNldGRpZmYoZnVuZ2lkYi5uZXckZ2VuZV9pZCwgZnVuZ2lkYiRnZW5lX2lkKQpgYGAKCioqX0Rpc2N1c3Npb25fKioKClVzaW5nIHRoZSBOMzYwIGFzIHF1ZXJ5IHJlc3VsdGVkIGluIHR3byBmZXdlciBoaXRzLCBvbmUgb2Ygd2hpY2ggd2FzIGV4Y2x1ZGVkIGFueXdheSBsYXRlciwgaS5lLiBDQUdMMEwwMDIyN2csIHdoaWxlIHRoZSBvdGhlciBvbmUgcHJvYmFibHkganVzdCB3ZW50IHNsaWdodGx5IGJlbG93IHRoZSBlLXZhbHVlIGN1dG9mZi4gQnkgY29udHJhc3QsIHVzaW5nIHRoZSBOMzYwIGRpZG4ndCByZXN1bHQgaW4gYW55IG5ldyBoaXRzICh0aGUgQ0FXRyBvbmVzIGFyZSBmcm9tIHRoZSBXTy0xIHN0cmFpbiBvZiBfQy4gYWxiaWNhbnNfKS4gSG93ZXZlciwgSSBzaG91bGQgcGF5IGF0dGVudGlvbiB0byB0aGUgcXVlcnkgY292ZXJhZ2UgZmlsdGVyIHVzZWQgYmVsb3cuCgojIyMgRmlsdGVyaW5nIGZ1bmdpZGIgaGl0cyBieSBsZW5ndGgKU29tZSBvZiB0aGUgaWRlbnRpZmllZCBob21vbG9ncyBoYXZlIGxlc3MgdGhhbiA1MDAgYW1pbm8gYWNpZHMuIEkgd2FudCB0byBrbm93IGlmIHRoZXNlIGhpdHMgYWxzbyB0ZW5kIHRvIGhhdmUgbGVzcyBzaWduaWZpY2FudCBfZS12YWx1ZXNfLiBJZiBzbywgSSdsbCBleGNsdWRlIHRoZW0gZnJvbSBmdXJ0aGVyIGFuYWx5c2VzLgoKYGBge3IgZnVuZ2lkYl9wbG90fQojIHBsb3QgcHJvdGVpbiBsZW5ndGggYXMgYSBmdW5jdGlvbiBvZiBlLXZhbHVlCmZ1bmdpZGIgJT4lIAogIGdncGxvdChhZXMoeCA9IC1sb2cxMChlX3ZhbHVlKSwgeSA9IGxvZzIobGVuZ3RoKSwgY29sID0gKGxlbmd0aCA+IDUwMCkpKSArIGdlb21fcG9pbnQoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCmdnc2F2ZSgiaW1nLzIwMjAwNzAxLVhQXzAyODg4OTAzMy1mdW5naWRiLWhpdHMtZS12YWx1ZS1ieS1sZW5ndGgucG5nIiwgd2lkdGggPSAzLCBoZWlnaHQgPSAzKQpgYGAKQmFzZWQgb24gdGhlIHJlc3VsdHMsIEkgd2lsbCByZW1vdmUgdGhlIHNlcXVlbmNlcyB0aGF0IGFyZSBzaG9ydGVyIHRoYW4gNTAwIGFtaW5vIGFjaWRzLiBOb3RlIHRoYXQgdGhlIDUwJSBxdWVyeSBjb3ZlcmFnZSBmaWx0ZXIgYXBwbGllZCB0byB0aGUgTkNCSSBoaXRzIHdhcyBub3QgYXBwbGllZCBoZXJlLCBhcyBxdWVyeSBjb3ZlcmFnZSB3YXMgbm90IGV2ZW4gcmVwb3J0ZWQgaW4gdGhlIG9yaWdpbmFsIGJsYXN0IHNlYXJjaCBmcm9tIGZ1bmdpZGIuCgpgYGB7ciBmdW5naWRiX2ZpbHRlcn0KIyBvdXRwdXQgYSBsaXN0IG9mIElEcyB0aGF0IGNvcnJlc3BvbmQgdG8gc2VxdWVuY2VzIGdyZWF0ZXIgdGhhbiA1MDAgYS5hLgpmZGIuZmlsdGVyZWQgPC0gZnVuZ2lkYiAlPiUgCiAgZmlsdGVyKHN0cl9zdWIoZ2VuZV9pZCwgLTIsIC0xKSAhPSAiX0IiKSAlPiUgICMgcmVtb3ZlIHRoZSBCIGFsbGVsZSBmcm9tIEMuIGFsYmljYW5zCiAgZmlsdGVyKGxlbmd0aCA+IDUwMCkgICAgICAgICAgICAgICAgICAgICAgICAgICMgcmVxdWlyZSBsZW5ndGggZ3JlYXRlciB0aGFuIDUwMCBhbWlubyBhY2lkcwoKY2F0KGZkYi5maWx0ZXJlZCRnZW5lX2lkLCBmaWxlID0gImRhdGEvZnVuZ2lkYi9mdW5naWRiX2ZpbHRlcl9saXN0LnR4dCIsIHNlcCA9ICJcbiIpCmBgYApgciBucm93KGZkYi5maWx0ZXJlZClgIHNlcXVlbmNlcyBsZWZ0CgoqKlRoZSBweXRob24gc2NyaXB0IGJlbG93IHdpbGwgZXh0cmFjdCB0aG9zZSA3MCBzZXF1ZW5jZXMgaW50byBhIHNlcGFyYXRlIGZpbGUgZm9yIGxhdGVyLioqCgpgYGBiYXNoCmNkIGRhdGEvZnVuZ2lkYgpweXRob24gLi4vLi4vc2NyaXB0L2V4dHJhY3RfZmFzdGEucHkgWFBfMDI4ODg5MDMzX2hvbW9sb2dzX2Z1bmdpZGJfTjM2MC5mYXN0YSBmdW5naWRiX2ZpbHRlcl9saXN0LnR4dAptdiBmdW5naWRiX2ZpbHRlcl9saXN0LmZhYSBYUF8wMjg4ODkwMzNfaG9tb2xvZ3NfZnVuZ2lkYl9OMzYwX3VzZS5mYXN0YQpgYGAKCiMjIEdSWUMKPiAqKlVwZGF0ZSAyMDIwLTA3LTIyKioKPiBUaGUgb3JpZ2luYWwgYmxhc3QgdG8gYm90aCB0aGUgcmVmc2VxX3Byb3RlaW4gYW5kIGZ1bmdpREIgZGF0YWJhc2VzIHlpZWxkZWQgbm8gaGl0cyBpbiB0aGUgd2VsbCBzdHVkaWVkIF9TLiBjZXJldmlzaWFlXyBfc2Vuc3Ugc3RyaWN0b18gb3IgX3NlbnN1IGxhdG9fIGNsYWRlLiBUaGUgb25seSBoaXRzIHdlcmUgaW4gX0MuIGdsYWJyYXRhXyBhbmQgX04uIGNhc3RlbGxpaV8uIEknbSBwYXJ0aWN1bGFybHkgY3VyaW91cyB3aHkgdGhlIG90aGVyIE5ha2FzZW9teWNlcyBncm91cCBzcGVjaWVzLCBlLmcuIF9DLiBicmFjYXJlbnNpc18sIF9OLiBkZXBoZW5zaXNfIGFuZCBfQy4gbml2YXJpZW5zaXNfIGhhZCBubyBoaXRzLiBUdXJucyBvdXQgZXZlbiB0aGUgTkNCSSBucl9wcm90ZWluIGRhdGFiYXNlIGRvZXNuJ3QgY29udGFpbiBhbnkgcHJvdGVpbiBlbnRyaWVzIGZvciB0aGUgTmFrYXNlb215Y2VzIC0tIEkgdmVyaWZpZWQgdGhpcyBieSBibGFzdCdpbmcgUGhvNCBwcm90ZWluIHNlcXVlbmNlIGFnYWluc3QgdGhlIG5yX3Byb3RlaW4gYW5kIGxpbWl0ZWQgdGhlIG9yZ2FuaXNtcyB0byBOYWthc2VvbXljZXMuIEkgdGhlbiBmb3VuZCAvIHJlbWVtYmVyZWQgdGhhdCB0aGUgW0dlbm9tZSBSZXNvdXJjZSBmb3IgWWVhc3QgQ2hyb21vc29tZXNdKGh0dHA6Ly9ncnljLmlucmEuZnIvaW5kZXgucGhwKSBzaXRlIGNvbnRhaW5zIHRoZSBOYWthc2VvbXljZXMgZ2Vub21lcy4gSSB2ZXJpZmllZCB0aGlzIGJ5IHJlcGVhdGluZyB0aGUgUGhvNHAgYmxhc3QuIEkgdGhlbiBibGFzdCdlZCB0aGUgZmlyc3QgNTAwIGEuYS4gb2YgWFBfMDI4ODg5MDMzIGluIEdSWUMsIHNlbGVjdGluZyB0aGUgTmFrYXNlb215Y2VzICg2IHNwcylhcyB3ZWxsIGFzIF9TLiBjZXJldmlzaWFlXyAoMSksIExhY2hhbmNlYSAoMTIpLCBOYXVtb3ZvenltYSAoMSwgX04uIGNhc3RlbGxpaV8pLCBZYXJyb3dpYSAoMyBfWS4gbGlwb2x5dGljYV8gc3RyYWlucykuIAoKIVtncnljIGhpdHNdKC4vaW1nLzIwMjAwNzIzLUdSWUMtQkxBU1QtaGl0cy5wbmcpCgpgYGB7ciBncnljX2luZm99CiMgbG9hZCB0aGUgbWFudWFsbHkgZWRpdGVkIGJsYXN0IHRhYmxlCmdyeWMgPC0gcmVhZF90c3YoImRhdGEvR1JZQy9YUF8wMjg4ODkwMzNfaG9tb2xvZ3NfZ3J5Y190YWJsZS50eHQiLCBjb2xfdHlwZXMgPSBjb2xzKCkpCmdyeWMgPC0gZ3J5YyAlPiUgCiAgbXV0YXRlKHNwZWNpZXNfaWQgPSBwYXN0ZTAoc3RyX3N1YihzcGVjaWVzLCAxLCAxKSwgd29yZChzcGVjaWVzLCAyLCAyKSkpCmdyeWMgJT4lIHNlbGVjdChpZCwgc3BlY2llcywgbGVuZ3RoLCBxY292ID0gcXVlcnlfY292ZXIsIGVfdmFsdWUgPSBtaW5fZV92YWx1ZSwgbWF4X2lkZW50LCBkZXNjcmlwdGlvbikKYGBgCl9EaXNjdXNzaW9uXwoKMS4gRm9yIHRoZSBSZWZfc2VxIGFuZCBmdW5naURCIGhpdHMsIHdlIGFwcGxpZWQgdHdvIGFkZGl0aW9uYWwgY3JpdGVyaWEgb24gdG9wIG9mIHRoZSBFLXZhbHVlIGN1dG9mZiBvZiAxZS01LCB3aGljaCBhcmUgPjUwJSBxdWVyeSBjb3ZlcmFnZSAoMjgwIGEuYS4pIGFuZCB0aGUgcHJvdGVpbiBsZW5ndGggPiA1MDAgYS5hLiBMZXQncyBzZWUgd2hpY2ggb2YgdGhlIGhpdHMgd2lsbCBiZSBleGNsdWRlZCBpZiB3ZSBhcHBseSB0aGUgc2FtZSBjcml0ZXJpYSBoZXJlLiBgciBncnljICU+JSBmaWx0ZXIocXVlcnlfY292ZXIgPCA1MCB8IGxlbmd0aCA8IDUwMCkgJT4lIHNlbGVjdCgtb3JpX2lkLCAtbWF4X2JpdF9zY29yZSwgLWRlc2NyaXB0aW9uLCBkZXNjcmlwdGlvbilgCgoxLiBBbW9uZyB0aGUgcG90ZW50aWFsbHkgZXhjbHVkZWQgc2VxdWVuY2VzLCBJIHdvdWxkIHJlc2N1ZSAqKk5BREUwczA1ZTA0MTM2ZyoqIG91dCBvZiB0aGUgcGlsZSBiZWNhdXNlIGl0cyBzaG9ydCBsZW5ndGggY291bGQgYmUgZHVlIHRvIHRoZSBzZXF1ZW5jZSBiZWluZyBhICJwYXJ0aWFsIENEUyIuIEFub3RoZXIgc2VxdWVuY2UgcG90ZW50aWFsbHkgb2YgaW50ZXJlc3QgaXMgKipDQUdMMEwwMDIyN2cqKiwgd2hpY2ggaGFzIDQ3JSBxdWVyeSBjb3ZlcmFnZSwgYW5kIGlzIG9mIHNpbWlsYXIgbGVuZ3RoIGFzIFhQXzAyODg4OTAzMy4gQnV0IGxhdHRlciBwaHlsb2dlbmV0aWMgYW5hbHlzaXMgc2hvd2VkIHRoYXQgaXQgYXBwZWFycyB0byBoYXZlIGRpdmVyZ2VkIGZyb20gdGhlIGhvbW9sb2dzIHdpdGggPjUwJSBjb3ZlcmFnZSBzaWduaWZpY2FudGx5LiBJIHRoZXJlZm9yZSBkZWNpZGVkIHRvIG1haW50YWluIHRoZSBjcml0ZXJpb24gb2YgcXVlcnkgY292ZXJhZ2Ugb3ZlciA1MCUgYW5kIG5vdCBpbmNsdWRlIGl0LgoKMS4gTGFzdGx5LCBJJ2xsIHJlbW92ZSB0aGUgaGl0cyBmcm9tIF9MLiBmZXJtZW50YXRpXy4gVGhpcyBpcyBhcmJpdHJhcnkgYW5kIHRoZSByYXRpb25hbGUgaXMgdGhhdCBJIGp1c3Qgd2FudCB0byBnZXQgc29tZSByZXByZXNlbnRhdGlvbiBmcm9tIHRoZSBMYW5jaGFuY2VhIGdlbnVzLCBhbmQgSSBhbHJlYWR5IGhhdmUgX0wuIGtsdXl2ZXJpXy4KCiMjIE1lcmdlIHsjbWVyZ2V9CkZpcnN0IHdlIG5lZWQgdG8gZmluZCBvdXQgd2hpY2ggc2VxdWVuY2VzIHdlcmUgZHVwbGljYXRlcy4gVG8gZG8gc28gd2Ugd2lsbCBibGFzdCB0aGUgZnVuZ2lkYiBhbmQgZ3J5YyBoaXRzIGFnYWluc3QgdGhlIHJlZnNlcSBoaXRzLgoKPiAqKl91cGRhdGUgMjAyMS0wMS0yNV8qKiBBZGRlZCBuZXcgc2VxdWVuY2VzIGZyb20gdGhlIHJlZnNlcV9wcm90IHNlYXJjaC4KCiMjIyBGdW5naURCCiMjIyMgVHJhbnNsYXRlIGZ1bmdpREIgSURzIHRvIHJlZnNlcV9wcm90ZWluIElEcwpUaGUgZmlyc3QgYXBwcm9hY2ggSSB0b29rIHdhcyB0byBkaXJlY3RseSBibGFzdCB0aGUgZnVuZ2lkYiBoaXRzIHRvIHRoZSBOQ0JJIHJlZnNlcV9wcm90IGRhdGFiYXNlIGFuZCBzZWUgaG93IG1hbnkgb2YgdGhlbSBoYWQgYSBtYXRjaCB0aGVyZS4gTm90ZSB0aGF0IGR1ZSB0byB0aGUgd2F5IGUtdmFsdWVzIGFyZSBjYWxjdWxhdGVkLCBhIGZ1bmdpZGIgaGl0IHRoYXQgaGFkIGFuIGlkZW50aWNhbCBtYXRjaCBpbiB0aGUgcmVmc2VxX3Byb3QgZGF0YWJhc2UgbWF5IG5vdCBhY3R1YWxseSBiZSBhbW9uZyB0aGUgcmVmc2VxIGhpdHMsIGFzIHdlIHdpbGwgc2VlIGJlbG93LgoKU2VlIFJFQURNRS5tZCBmb3IgZGV0YWlscyBvbiBob3cgSSBkaWQgQkxBU1QuCgpgYGB7ciByZWZzZXFfaWRfZm9yX2Z1bmdpZGJfaGl0cywgcmVzdWx0cz0iYXNpcyJ9CiMgbG9hZCBibGFzdCB0YWJsZQpmZGIuaGl0cy5ibGFzdCA8LSByZWFkX3RzdigiZGF0YS9mdW5naWRiL2Z1bmdpZGJfYmxhc3RfcmVmc2VxX3Byb3RlaW4udHh0IiwgY29sX25hbWVzID0gYygicXVlcnlfaWQiLCJzdWJqZWN0X2lkIiwicXVlcnlfbGVuZ3RoIiwic3ViamVjdF9sZW5ndGgiLCJwZXJjX2lkZW50aXR5IiwibWlzbWF0Y2giLCJzY29yZSIsImJpdHNjb3JlIiwiZV92YWx1ZSIpLCBjb2xfdHlwZXMgPSAiY2NubmRubm5uIikKCiMgZ29hbDogZm9yIGVhY2ggZnVuZ2lkYiBoaXQsIGlkZW50aWZ5IHRoZSByZWZzZXEgSUQgdGhhdCBoYXMgdGhlIGhpZ2hlc3QgU2NvcmUKZmRiLmhpdHMuYmxhc3QuZmlsdGVyZWQgPC0gIGZkYi5oaXRzLmJsYXN0ICU+JSAKICBncm91cF9ieShxdWVyeV9pZCkgJT4lIAogIGZpbHRlcihzY29yZSA9PSBtYXgoc2NvcmUpLCBzdHJfc3ViKHF1ZXJ5X2lkLCAtMiwgLTEpICE9ICJfQiIpICMgdGhpcyBibGFzdCB3YXMgZG9uZSBiZWZvcmUgSSByZW1vdmVkIHRoZSBfQiBhbGxlbGVzCgojIElEIG1hcHBpbmcgZm9yIGlkZW50aWNhbCBzZXF1ZW5jZXMgKCUgaWRlbnRpdHkgPSAxMDAlKQpmZGIucmVmc2VxSUQgPC0gZmRiLmhpdHMuYmxhc3QuZmlsdGVyZWQgJT4lIGZpbHRlcihwZXJjX2lkZW50aXR5ID09IDEwMCkgJT4lIHB1bGwoc3ViamVjdF9pZCkKbmFtZXMoZmRiLnJlZnNlcUlEKSA8LSBmZGIuaGl0cy5ibGFzdC5maWx0ZXJlZCAlPiUgZmlsdGVyKHBlcmNfaWRlbnRpdHkgPT0gMTAwKSAlPiUgcHVsbChxdWVyeV9pZCkKCmNhdCgiKipIaXRzIG5vdCBtYXBwZWQgdG8gcmVmc2VxX3Byb3Q6KioiKQojZmRiLmZpbHRlcmVkICU+JSBmaWx0ZXIoIWdlbmVfaWQgJWluJSBuYW1lcyhmZGIucmVmc2VxSUQpKQpmZGIuaGl0cy5ibGFzdC5maWx0ZXJlZCAlPiUgCiAgZmlsdGVyKHBlcmNfaWRlbnRpdHkgIT0gMTAwKSAlPiUgCiAgc2VsZWN0KHF1ZXJ5X2lkLCBzdWJqZWN0X2lkLCBxbGVuID0gcXVlcnlfbGVuZ3RoLCBzbGVuID0gc3ViamVjdF9sZW5ndGgsIHBpZGVudCA9IHBlcmNfaWRlbnRpdHksIG1pc21hdGNoLCBlX3ZhbHVlKQpgYGAKCl9EaXNjdXNzaW9uXwoKMS4gT2YgdGhlIGByIG5yb3coZmRiLmZpbHRlcmVkKWAgc2VxdWVuY2UsIGByIGxlbmd0aChmZGIucmVmc2VxSUQpYCBhcmUgcHJlc2VudCBpbiB0aGUgcmVmc2VxIGRhdGFiYXNlLgoxLiBBbW9uZyB0aGUgc2VxdWVuY2VzIHRoYXQgZGlkbid0IG1hcCwgZml2ZSBiZWxvbmcgdG8gdGhlIF9DLiBhdXJpc18gQjg0NDEgc3RyYWluLCB3aGljaCBpcyB0aGUgb25seSBzdHJhaW4gb2YgX0MuIGF1cmlzXyBwcmVzZW50IGluIHRoZSBGdW5naURCLCBidXQgYXBwYXJlbnRseSBpcyBub3QgaW4gdGhlIHJlZnNlcV9wcm90ZWluIGRhdGFiYXNlLiBBbm90aGVyIHRocmVlIHNlcXVlbmNlcyBpbiBCODQ0MSBhcHBhcmVudGx5IGFyZSBpZGVudGljYWwgdG8gc2VxdWVuY2VzIGZyb20gdGhlIG90aGVyIF9DLiBhdXJpc18gc3RyYWlucyBpbmNsdWRlZCBpbiB0aGUgcmVmc2VxIGRhdGFiYXNlLgoKICAgID4gRWFjaCBzcGVjaWVzIGluIHRoZSBob21vbG9ncyB0YWJsZSBpcyByZXByZXNlbnRlZCBieSBvbmUgc3RyYWluLCB3aGljaCBjb3JyZXNwb25kcyB0byB0aGUgcmVmc2VxIGFzc2VtYmx5IGFuZCBpcyBpbmNsdWRlZCBpbiB0aGUgcmVmc2VxX3Byb3QgZGF0YWJhc2UuIFRvIGtlZXAgd2l0aCB0aGlzIGNvbnZlbnRpb24sIEkgd2lsbCB1c2UgQjExMjIxIGFzIHRoZSBzdHJhaW4gZm9yIF9DLiBhdXJpc18gYW5kIGxlYXZlIEI4NDQxIG91dC4gSW4gYSBzZXBhcmF0ZSBhbmFseXNpcywgSSB3aWxsIHNwZWNpZmljYWxseSBpbnZlc3RpZ2F0ZSB3aXRoaW4gX0MuIGF1cmlzXyBwb2x5bW9ycGhpc20gYW5kIHdpbGwgaW5jbHVkZSBCODQ0MSBhbmQgb3RoZXIgc3RyYWlucy4KICAgICAgICAKMS4gVGhlIHJlbWFpbmluZyBmaXZlIHNlcXVlbmNlcyBiZWxvbmcgdG8gX0MuIHBhcmFkb3h1c18gKDQpIGFuZCBfQy4gdHJvcGljYWxpc18gKDEpLiBTZWUgdGhlIFtNZXJnZV0oI21lcmdlKSBzZWN0aW9uIGZvciBjaG9pY2VzIHRvIGluY2x1ZGUgc29tZSBhZGRpdGlvbmFsIHNlcXVlbmNlcy4KCiMjIyMgU2VhcmNoIHRoZSBmdW5naWRiIGhpdHMgYWdhaW5zdCBhIGRhdGFiYXNlIG1hZGUgb2YgdGhlIHJlZnNlcV9wcm90IGhpdHMKVGhlIGFib3ZlIHNlYXJjaCBvbmx5IGlkZW50aWZpZXMgZnVuZ2lkYiBoaXRzIHRoYXQgYXJlIGRvbid0IGV2ZW4gaGF2ZSBhIG1hdGNoIGluIHRoZSByZWZzZXFfcHJvdCBkYXRhYmFzZS4gSGVyZSB3ZSBhcmUgYmVpbmcgbW9yZSBzcGVjaWZpYyBhbmQgYXNrIHdoaWNoIEZ1bmdpREIgaGl0cyBkbyBub3QgYWxyZWFkeSBleGlzdCBhbW9uZyB0aGUgcmVmc2VxX3Byb3QgaGl0cy4KClRoZSBgYmFzaGAgc2NyaXB0IGJlbG93IGlzIHRvIG1ha2UgYSBibGFzdCBkYXRhYmFzZSBmcm9tIHRoZSBOQ0JJIGJsYXN0IHJlc3VsdHMgYW5kIHNlYXJjaCB0aGUgRnVuZ2lEQiBoaXRzIGluIGl0IHRvIGlkZW50aWZ5IG1hdGNoaW5nIHNlcXVlbmNlcy4gTm90ZSB0aGF0IHdlIGFyZSB1c2luZyBhbGwgb2YgdGhlIE4zNjAgaGl0cywgaW5jbHVkaW5nIHRoZSBvbmVzIHRoYXQgd2VyZSBmaWx0ZXJlZCBvdXQuIFRoaXMgaXMgYmVjYXVzZSB3ZSBhcmUgb25seSBpbnRlcmVzdGVkIGluIHRob3NlIEZ1bmdpREIgaGl0cyB0aGF0IGRvbid0IGFscmVhZHkgZXhpc3QgaW4gdGhlIHJlZnNlcSBoaXRzIGF0IGFsbC4gU28gaWYgYSBoaXQgbWF0Y2hlcyBhIHJlZnNlcSBoaXQgdGhhdCB3YXMgbGF0ZXIgZmlsdGVyZWQsIGl0IHdvdWxkIHN0aWxsIGJlIGV4Y2x1ZGVkLgpgYGBiYXNoCiMgbWFrZSBibGFzdCBkYXRhYmFzZSBmcm9tIHRoZSBOQ0JJIGJsYXN0IHJlc3VsdHMKY2QgZGF0YQpta2RpciBibGFzdGRiCm1ha2VibGFzdGRiIC1pbiBuY2JpLXJlZnNlcS9YUF8wMjg4ODkwMzNfaG9tb2xvZ3NfcmVmcHJvdF9OMzYwLmZhc3RhIC1wYXJzZV9zZXFpZHMgLWRidHlwZSBwcm90IC10aXRsZSBYUF8wMjg4ODkwMzNfcmVmcHJvdCAtb3V0IGJsYXN0ZGIvWFBfMDI4ODg5MDMzX3JlZnByb3QKIyBibGFzdHAKIyBibGFzdHAgLWRiIC4vYmxhc3RkYi9YUF8wMjg4ODkwMzNfcmVmcHJvdCAtcXVlcnkgZnVuZ2lkYi9YUF8wMjg4ODkwMzNfaG9tb2xvZ3NfZnVuZ2lkYl9OMzYwX3VzZS5mYXN0YSAtb3V0Zm10ICI2IHFzZXFpZCBzc2VxaWQgcWxlbiBzbGVuIHBpZGVudCBtaXNtYXRjaCBzY29yZSBiaXRzY29yZSBldmFsdWUiIC1tYXhfdGFyZ2V0X3NlcXMgMSAtbnVtX3RocmVhZHMgNCAtb3V0IGZ1bmdpZGIvWFBfMDI4ODg5MDMzX2Z1bmdpZGItcmVmcHJvdC1OMzYwLWJsYXN0LnR4dAojIGJsYXN0cCAtZGIgLi9ibGFzdGRiL1hQXzAyODg4OTAzM19yZWZwcm90IC1xdWVyeSBHUllDL1hQXzAyODg4OTAzM19ob21vbG9nc19ncnljLmZhc3RhIC1vdXRmbXQgIjYgcXNlcWlkIHNzZXFpZCBxbGVuIHNsZW4gcGlkZW50IG1pc21hdGNoIHNjb3JlIGJpdHNjb3JlIGV2YWx1ZSIgLW1heF90YXJnZXRfc2VxcyAxIC1udW1fdGhyZWFkcyA0IC1vdXQgR1JZQy9YUF8wMjg4ODkwMzNfZ3J5Yy1yZWZwcm90LU4zNjAtYmxhc3QudHh0CmJsYXN0cCAtZGIgLi9ibGFzdGRiL1hQXzAyODg4OTAzM19yZWZwcm90IC1xdWVyeSBmdW5naWRiL1hQXzAyODg4OTAzM19ob21vbG9nc19mdW5naWRiX04zNjBfdXNlLmZhc3RhIC1vdXRmbXQgIjYgcXNlcWlkIHNzZXFpZCBxbGVuIHNsZW4gcGlkZW50IG1pc21hdGNoIHNjb3JlIGJpdHNjb3JlIGV2YWx1ZSIgLWV2YWx1ZSAxZS0xODAgLW51bV90aHJlYWRzIDQgLW91dCBmdW5naWRiL1hQXzAyODg4OTAzM19mdW5naWRiLXJlZnByb3QtTjM2MC1ibGFzdC1hbGxoaXRzLnR4dAoKYmxhc3RwIC1kYiAuL2JsYXN0ZGIvWFBfMDI4ODg5MDMzX3JlZnByb3QgLXF1ZXJ5IEdSWUMvWFBfMDI4ODg5MDMzX2hvbW9sb2dzX2dyeWMuZmFzdGEgLW91dGZtdCAiNiBxc2VxaWQgc3NlcWlkIHFsZW4gc2xlbiBwaWRlbnQgbWlzbWF0Y2ggc2NvcmUgYml0c2NvcmUgZXZhbHVlIiAtZXZhbHVlIDFlLTE4MCAtbnVtX3RocmVhZHMgNCAtb3V0IEdSWUMvWFBfMDI4ODg5MDMzX2dyeWMtcmVmcHJvdC1OMzYwLWJsYXN0X2FsbGhpdHMudHh0CmBgYAoKVGhlIHR5cGljYWwgcHJvdG9jb2wgZm9yIGlkZW50aWZ5aW5nIG9ydGhvbG9ncyBpcyB0byBwZXJmb3JtIHJlY2lwcm9jb2wgYmxhc3QuIEJ1dCBvdXIgZ29hbCBoZXJlIGlzIHRvIGlkZW50aWZ5IGlkZW50aWNhbCBzZXF1ZW5jZXMgLS0gbW9yZSBsaWtlIHdoYXQgdGhlIENERCBoaXRzIHByb2dyYW0gZG9lcyAtLSBhbmQgdGh1cyBvbmUgc2lkZWQgYmxhc3Qgc2hvdWxkIGJlIGZpbmUuIFRoZSBhbmFseXNpcyBiZWxvdyBpcyBtZWFudCB0byBpZGVudGlmeSB0aGUgc2hhcmVkIGhpdHMgYXMgd2VsbCBhcyB0aGUgdW5pcXVlIGhpdHMgaW4gZWFjaCBzZXQuCgo+Kio9PT09PSBVcGRhdGUgW0hCXSAyMDIxLTA2LTEyID09PT09KioKPgo+VGhlIGAtbWF4X3RhcmdldF9zZXFzIDFgIGFyZ3VtZW50IHRvIGBibGFzdHBgLCB3aGVuIHJ1biBsb2NhbGx5LCBsZWQgdG8gdW5leHBlY3RlZCBiZWhhdmlvciAtLSBpbnN0ZWFkIG9mIHJldHVybmluZyB0aGUgImJlc3QiIGhpdCByYW5rZWQgYnkgZS12YWx1ZSwgYXMgb25lIHdvdWxkIGludHVpdGl2ZSBleHBlY3QsIGl0IHJldHVybnMgdGhlIEZJUlNUIGhpdCB0aGF0IGlzIGFib3ZlIHRoZSB0aHJlc2hvbGQsIGFuZCB0aHVzIHRoZSByZXN1bHQgd2lsbCBhbHNvIGRlcGVuZCBvbiB0aGUgb3JkZXIgb2YgdGhlIHNlcXVlbmNlcyBpbiB0aGUgZGF0YWJhc2UuIFRoaXMgaXNzdWUgaXMgd2lkZWx5IGRvY3VtZW50ZWQgb24gdGhlIHdlYiBpZiBvbmUga25vd3Mgd2hhdCBxdWVzdGlvbiB0byBzZWFyY2ggZm9yLiBTZWUgW1NoYWggX2V0IGFsLl8gMjAxOV0oaHR0cHM6Ly9hY2FkZW1pYy5vdXAuY29tL2Jpb2luZm9ybWF0aWNzL2FydGljbGUvMzUvOS8xNjEzLzUxMDYxNjYpCj4KPkkgdGhlcmVmb3JlIHJlcGVhdGVkIHRoZSBhYm92ZSBzZWFyY2hlcywgcmVwbGFjaW5nIGAtbWF4X3RhcmdldF9zZXFzIDFgIHdpdGggYC1ldmFsdWUgMWUtMTgwYCwgc2luY2Ugd2hhdCB3ZSB3YW50IHRvIGlkZW50aWZ5IGlzIHRoZSBwZXJmZWN0IG1hdGNoLiBJIGFsc28gY2hhbmdlZCB0aGUgYC1vdXRmbXRgIGZyb20gNiB0byA3IHRvIGluY2x1ZGUgdGhlIGNvbW1lbnQgbGluZXMsIGFuZCBuYW1lZCB0aGUgb3V0cHV0IGZpbGVzIHdpdGggdGhlIHN1ZmZpeCBgX2FsbGhpdHNgIHRvIGRpc3Rpbmd1aXNoIHRoZW0gZnJvbSB0aGUgcHJldmlvdXMgcmVzdWx0cy4KPgo+Kio9PT09PSBFbmQgVXBkYXRlID09PT09KioKCmBgYHtyIGZ1bmdpZGJfbWF0Y2gsIHJlc3VsdHM9J2FzaXMnfQojIGxvY2FsIGJsYXN0IHRoZSBmdW5naURCIGhpdHMgYWdhaW5zdCB0aGUgbGlicmFyeSBtYWRlIGZyb20gdGhlIHJlZnNlcSBoaXRzCmJsYXN0LmhlYWRlciA8LSBjKCJxdWVyeV9pZCIsInN1YmplY3RfaWQiLCJxdWVyeV9sZW5ndGgiLCJzdWJqZWN0X2xlbmd0aCIsInBlcmNfaWRlbnRpdHkiLCJtaXNtYXRjaCIsInNjb3JlIiwiYml0c2NvcmUiLCJlX3ZhbHVlIikKZmRiMnJlZnNlcSA8LSByZWFkX3RzdigiZGF0YS9mdW5naWRiL1hQXzAyODg4OTAzM19mdW5naWRiLXJlZnByb3QtTjM2MC1ibGFzdC1hbGxoaXRzLnR4dCIsIGNvbF90eXBlcyA9ICJjY25uZG5ubm4iLCBjb2xfbmFtZXMgPSBibGFzdC5oZWFkZXIsIGNvbW1lbnQgPSAiIyIpCgojIGZvciBlYWNoIHF1ZXJ5IHNlcXVlbmNlLCByZXRhaW4gdGhlIHRvcCBoaXQgd2l0aCB0aGUgYmVzdCBzY29yZQpmZGIycmVmc2VxLmJlc3QuaGl0IDwtIGZkYjJyZWZzZXEgJT4lIAogIGdyb3VwX2J5KHF1ZXJ5X2lkKSAlPiUgCiAgZmlsdGVyKHNjb3JlID09IG1heChzY29yZSkpCgojIGlmIHBlcmNfaWRlbnRpdHkgaXMgMTAwLCB0aGVuIHdlIGhhZCBhIG1hdGNoCmZkYjJyZWZzZXEubWF0Y2ggPC0gZmRiMnJlZnNlcS5iZXN0LmhpdCAlPiUgCiAgZmlsdGVyKHBlcmNfaWRlbnRpdHkgPT0gMTAwKSAlPiUKICBwdWxsKHF1ZXJ5X2lkKQoKIyBwcmludCBvdXQgc29tZSBzdGF0cwpzcHJpbnRmKCJXaGVuIHNlYXJjaGluZyAlaSBGdW5naURCIGhpdHMgaW4gdGhlICVpIHJlZnNlcV9wcm90IGhpdHMsICVpIGdvdCBhdCBsZWFzdCBvbmUgaGl0LiIsCiAgICAgICAgbnJvdyhmZGIuZmlsdGVyZWQpLCBucm93KHJlZnNlcTApLCBucm93KGZkYjJyZWZzZXEuYmVzdC5oaXQpKQpzcHJpbnRmKCJBbW9uZyB0aGVtLCAlaSBGdW5naURCIGhpdHMgd2VyZSBmb3VuZCB0byBoYXZlIGFuIGlkZW50aWNhbCBtYXRjaCBpbiB0aGUgcmVmc2VxIGhpdHMuIiwKICAgICAgICBsZW5ndGgoZmRiMnJlZnNlcS5tYXRjaCkpCgpjYXQoIkJlbG93IGFyZSB0aGUgRnVuZ2lEQiBoaXRzIHRoYXQgZWl0aGVyIGhhZCBubyBvciBub24taWRlbnRpY2FsIG1hdGNoZXMgaW4gdGhlIHJlZnNlcSBoaXRzLi4uIikKZmRiLmZpbHRlcmVkICU+JSAKICBmaWx0ZXIoIWdlbmVfaWQgJWluJSBmZGIycmVmc2VxLm1hdGNoKSAlPiUgCiAgc2VsZWN0KGdlbmVfaWQsIG9yZ2FuaXNtLCBgRkRCIGVfdmFsdWVgID0gZV92YWx1ZSwgbGVuZ3RoKSAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChmZGIycmVmc2VxLmJlc3QuaGl0LCBnZW5lX2lkID0gcXVlcnlfaWQsIHNpZCA9IHN1YmplY3RfaWQsIAogICAgICAgICAgICAgICAgICAgc2xlbiA9IHN1YmplY3RfbGVuZ3RoLCBwaWRlbnQgPSBwZXJjX2lkZW50aXR5KSkKYGBgCgoqKl9EaXNjdXNzaW9uXyoqCgoxLiBUaGUgdG9wIDggYXJlIGZyb20gQjg0NDEgb2YgX0MuIGF1cmlzXy4gQXMgbm90ZWQgYWJvdmUsIHRoZSByZWZzZXEgc3RyYWluIGZvciB0aGUgc3BlY2llcyBpcyBCMTEyMjEuIExvb2tpbmcgYXQgdGhlIHBlcmNfaWRlbnRpdHkgb2YgdGhlIG1hdGNoZXMsIHdlIGNhbiBzZWUgdGhhdCBCMTEyMjEgbWlzc2VzIG9uZSBvZiB0aGUgZWlnaHQgaGl0cyBpbiBCODQ0MSwgaS5lLiBCOUowOF8wMDQwOTguCjEuIENBR0wwTDAwMjI3ZyBhbmQgQ0xVR18wNTIzMyBhcmUgYm90aCAicHJvdmlzaW9uYWwiIHJlZnNlcSBzZXF1ZW5jZXMsIG1lYW5pbmcgdGhhdCB0aGVpciByZWNvcmRzIGhhdmUgbm90IGJlZW4gc3VibWl0dGVkIHRvIE5DQkkgZm9yIGZpbmFsIHJldmlldy4KMS4gSSB3aWxsIHVzZSB0aGUgcmVmc2VxIGhpdHMgYXMgdGhlIGJhc2UgYW5kIGFkZCBfQy4gYXVyaXNfIEI5SjA4XzAwNDA5OCwgQ0xVR18wNTIzMyBhbmQgdGhlIF9DLiBwYXJhcHNpbG9zaXNfIHNlcXVlbmNlcyAoZm9yIHNvbWUgcmVhc29uIHRoaXMgc3BlY2llcyBpcyBub3QgaW5jbHVkZWQgaW4gcmVmc2VxKS4gVG8gZG8gc28sIEknbGwgdXNlIHRoZSBgcmVhZC5mYXN0YSgpYCBmdW5jdGlvbiBmcm9tIHRoZSBgc2VxaW5yYCBwYWNrYWdlIHRvIGV4dHJhY3QgdGhlaXIgc2VxdWVuY2VzLCBhbmQgdXNlIHRoZSBgd3JpdGUuZmFzdGEoKWAgZnVuY3Rpb24gdG8gb3V0cHV0IHRoZSBjb21iaW5lZCBmYXN0YS4KCj4qKlVwZGF0ZSAyMDIwLTEwLTE4KioKPiBJIHJlYWxpemVkIHRoYXQgYnkgaW5jbHVkaW5nIHRoZSBvbmUgc2VxdWVuY2UgZnJvbSBfQy4gYXVyaXNfIEI4NDQxLCB0aGUgZmluYWwgZGF0YXNldCB3b3VsZCBjb250YWluIHR3byBfQy4gYXVyaXNfIHN0cmFpbnMsIG1ha2luZyBpdCBkaXN0aW5jdCBmcm9tIHRoZSBvdGhlciBzcGVjaWVzLCBlYWNoIG9mIHdoaWNoIGlzIHJlcHJlc2VudGVkIGJ5IG9uZSBzdHJhaW4gb25seS4gVGhpcyB3b3VsZG4ndCBjaGFuZ2UgdGhlIGJyb2FkZXIgY29uY2x1c2lvbnMuIEJ1dCBpZiB0aGVyZSBpcyBhIG5lZWQgdG8gbW9kaWZ5IHRoZSBob21vbG9ncyBsaXN0IGFuZCByZXBlYXQgdGhlIGRvd25zdHJlYW0gYW5hbHlzaXMsIEkgd291bGQgbGlrZSB0byByZW1vdmUgdGhpcyBzZXF1ZW5jZSAoQjlKMDhfMDA0MDk4KQoKPioqVXBkYXRlIDIwMjEtMDMtMDEqKgo+IEkgd2lsbCBhY3R1YWxseSBpbmNsdWRlIEI5SjA4XzAwNDA5OC4gU2VlIG5vdGVzIGluIHRoZSBmdW5naWRiIGJsYXN0IHNlY3Rpb24gYWJvdmUgYW5kIGFsc28gUkVBRE1FLm1kIGluIHRoaXMgZm9sZGVyIGZvciByZWFzb25zLgoKIyMjIEdSWUMKYGBge3IgZ3J5Y19tYXRjaH0KIyBsb2NhbCBibGFzdCB0aGUgZ3J5YyBoaXRzIGFnYWluc3QgdGhlIGxpYnJhcnkgbWFkZSBmcm9tIHRoZSByZWZzZXEgaGl0cwpncnljMnJlZnNlcSA8LSByZWFkX3RzdigiZGF0YS9HUllDL1hQXzAyODg4OTAzM19ncnljLXJlZnByb3QtTjM2MC1ibGFzdC1hbGxoaXRzLnR4dCIsIGNvbF9uYW1lcyA9IGMoInF1ZXJ5X2lkIiwic3ViamVjdF9pZCIsInF1ZXJ5X2xlbmd0aCIsInN1YmplY3RfbGVuZ3RoIiwicGVyY19pZGVudGl0eSIsIm1pc21hdGNoIiwic2NvcmUiLCJiaXRzY29yZSIsImVfdmFsdWUiKSwgY29sX3R5cGVzID0gImNjbm5kbm5ubiIsIGNvbW1lbnQgPSAiIyIpCm1hdGNoLmdyeWMgPC0gZ3J5YzJyZWZzZXEgJT4lIGZpbHRlcihwZXJjX2lkZW50aXR5ID4gOTApICU+JSBzZWxlY3QoLXNjb3JlLCAtYml0c2NvcmUsIC1lX3ZhbHVlKQpgYGAKQW1vbmcgdGhlIDE1IHF1ZXJpZXMgc2VhcmNoZWQsIGByIGxlbmd0aCh1bmlxdWUoZ3J5YzJyZWZzZXEkcXVlcnlfaWQpKWAgZ290IGF0IGxlYXN0IG9uZSBoaXQgYW1vbmcgdGhlIHJlZnNlcSBoaXRzIHdpdGggdGhlIGV2YWx1ZSBjdXRvZmYsIGFtb25nIHdoaWNoIGByIG5yb3cobWF0Y2guZ3J5YylgIGFyZSByZWR1bmRhbnQgd2l0aCB0aGUgcmVmc2VxIGhpdHMuIEhlcmUgYXJlIGFsbCB0aG9zZSB0aGF0IGdvdCBhIGhpdCBgciBncnljMnJlZnNlcSAlPiUgc2VsZWN0KC1zY29yZSwgLWJpdHNjb3JlLCAtZV92YWx1ZSlgCgpgYGB7ciBncnljX2ZpbHRlcn0KZ3J5YzEgPC0gZ3J5YyAlPiUgCiAgZmlsdGVyKCghb3JpX2lkICVpbiUgbWF0Y2guZ3J5YyRxdWVyeV9pZCAmIHNwZWNpZXNfaWQgIT0gIkxmZXJtZW50YXRpIikgJiAKICAgICAgICAgICAocXVlcnlfY292ZXIgPiA1MCAmIGxlbmd0aCA+PSA1MDApIHwKICAgICAgICAgICAoaWQgJWluJSBjKCJOQURFMHMwNWUwNDEzNmciKSkpICU+JSAKICAjIHRoZSBzZWNvbmQgY3JpdGVyaW9uIGlzIHRvIHJlbW92ZSB0aGUgb25lIHNlcXVlbmNlIHdpdGggbGVuZ3RoIHNtYWxsZXIgdGhhbiA1MDAgYS5hLgogICMgdGhlIGxhc3QgY3JpdGVyaW9uIGlzIHRvIGFkZCBiYWNrIHRoZSBOZGVscGhlbnNpcyBzZXF1ZW5jZQogIHNlbGVjdChvcmlfaWQsIGlkLCBzcGVjaWVzLCBzcGVjaWVzX2lkKQpncnljMQpgYGAKCkluIHRoZSBlbmQgd2UgYWRkZWQgZm91ciBzZXF1ZW5jZXMgdG8gdGhlIGxpc3QuCgojIyMgTWVyZ2UKSW4gb3JkZXIgdG8gbWFrZSBpdCBlYXNpZXIgZm9yIHRoZSBkb3duc3RyZWFtIHRyZWUgYnVpbGRpbmcgYW5kIHJlY29uY2lsaWF0aW9uLCBJIHdvdWxkIGxpa2UgdG8gcHJvZ3JhbW1hdGljYWxseSByZWZvcm1hdCB0aGUgc2VxdWVuY2UgbmFtZXMgdG8gY29uZm9ybSB0byB0aGUgTm90dW5nIHBvc3Qtc2NyaXB0IHN0YW5kYXJkLCBpLmUuIFNlcUlEX1NwZWNpZXNOYW1lLgoKTm93IGxldCdzIGRvIHRoZSByZW5hbWluZyBhbmQgZ2F0aGVyIHNvbWUgc3BlY2llcy9zdHJhaW4gaW5mb3JtYXRpb24gYXMgd2VsbC4KYGBge3IgcmVmc2VxX3NwZWNpZXN9CiMgdXNlIHJlbnRyZXogZnVuY3Rpb24gdG8gZ2V0IHRoZSBvcmdhbmlzbSBpbmZvcm1hdGlvbgp0bXAgPC0gZW50cmV6X3N1bW1hcnkoZGIgPSAicHJvdGVpbiIsIGlkID0gcmVmc2VxLmNvbWJpbmVkJGFjY2Vzc2lvbikKcmVmc2VxLm9yZ24uZXh0cmFjdCA8LSBleHRyYWN0X2Zyb21fZXN1bW1hcnkodG1wLCBlbGVtZW50cyA9IGMoIm9yZ2FuaXNtIiwic3RyYWluIiksIHNpbXBsaWZ5ID0gRkFMU0UpCnJlZnNlcS5vcmduIDwtIGRvLmNhbGwocmJpbmQsIGxhcHBseShyZWZzZXEub3Jnbi5leHRyYWN0LCB1bmxpc3QpKQojIFJlbmFtZSB0aGUgcmVmc2VxIG5hbWVzLiBUaGUgZ29hbCBpcyB0byBoYXZlIFNlcUlEX1NwZWNpZXMsIGxpa2UgWFBfMDI4ODg5MDMzX0NhdXJpcwojIHdoYXQgd2UgaGF2ZSBub3cgaXMgYSB0d28gd29yZCBzcGVjaWVzIG5hbWUsIHdoaWNoIHdlIHdpbGwgc2hvcnRlbiB0byBhIGNhcGl0YWwgZmlyc3QgbGV0dGVyIG9mIAojIHRoZSBmaXJzdCB3b3JkIGZvbGxvd2VkIGJ5IHRoZSBlbnRpcmUgc2Vjb25kIHdvcmQKcmVmc2VxMSA8LSBjYmluZChyZWZzZXEub3JnbiwgcmVmc2VxLmNvbWJpbmVkKSAlPiUgCiAgbXV0YXRlKG9yZ2FuaXNtID0gd29yZChzdHJfcmVwbGFjZV9hbGwob3JnYW5pc20sICJbXFx4NUJcXHg1RF0iLCAiIiksIDEsIDIpLAogICAgICAgICAjIFxceDVCXFx4NUQgYXJlIEhleGFkZWNpbWFsIHJlcHJlc2VudGF0aW9ucyBvZiB0aGUgc3F1YXJlIGJyYWNrZXRzCiAgICAgICAgIHNwZWNpZXMgPSBnc3ViKCJwc2V1ZG9oYWVtdWxvbmlpIiwgInBzZXVkb2hhZW11bG9uaXMiLCBvcmdhbmlzbSksCiAgICAgICAgICMgdGhlIG5ldyBuY2JpIGRhdGFiYXNlIHJlbmFtZWQgdGhlIHNwZWNpZXMsIGJ1dCBteSBvbGQgcmVzdWx0cyB1c2VkIHRoZSAicyIKICAgICAgICAgIyB0aGVyZWZvcmUgSSBsZWZ0IHRoZSBvcmdhbmlzbSBmaWVsZCBhbG9uZSBhbmQgY3JlYXRlZCBhIG5ldyB2YXJpYWJsZSB0byAKICAgICAgICAgIyBzdG9yZSB0aGUgIm9sZCIgbmFtZQogICAgICAgICBzcGVjaWVzX2lkID0gcGFzdGUwKHN0cl9zdWIoc3BlY2llcywgMSwgMSksIHdvcmQoc3BlY2llcywgMiwyKSkpICU+JQogIGZpbHRlcihzcGVjaWVzX2lkICE9ICJEcnVnb3NhIikgJT4lICMgcmVtb3ZlIEQuIHJ1Z29zYSBzZXF1ZW5jZXMKICBzZWxlY3QoYWNjZXNzaW9uLCBzcGVjaWVzLCBzcGVjaWVzX2lkLCBldmVyeXRoaW5nKCkpCmBgYAoKTWVyZ2UgdGhlIHRocmVlIGRhdGFzZXRzIGFuZCBleHBvcnQgdGhlIGZhc3RhIHNlcXVlbmNlcyBhbmQgYmxhc3QgcmVzdWx0cy4KYGBge3IgbWVyZ2V9CiMgcmVmc2VxIGhpdCBzZXF1ZW5jZXMgYW5kIHRoZWlyIG5hbWVzCnJlZnNlcWRiLnNlcSA8LSByZWFkLmZhc3RhKCJkYXRhL25jYmktcmVmc2VxL1hQXzAyODg4OTAzM19ob21vbG9nc19yZWZwcm90X04zNjAuZmFzdGEiLCBzZXF0eXBlID0gIkFBIiwgYXMuc3RyaW5nID0gVFJVRSwgc3RyaXAuZGVzYyA9IFRSVUUpCnJlZnNlcWRiLmxpc3QgPC0gcmVmc2VxMSRhY2Nlc3Npb24KcmVmc2VxZGIubmFtZXMgPC0gd2l0aChyZWZzZXExLCBwYXN0ZShhY2Nlc3Npb24sIHNwZWNpZXNfaWQsIHNlcCA9ICJfIikpCgojIHNlcXVlbmNlcyB0byBiZSBhZGRlZCB0byB0aGUgcmVmc2VxIGxpc3QgZnJvbSBmdW5naWRiIGhpdHMKZmRiLmxpc3QgPC0gYygiQ0xVR18wNTIzMyIsICJCOUowOF8wMDQwOTgiLCBwYXN0ZSgiQ1BBUjIiLGMoNjAwNDMwLDgwNjM5MCw4MDY0MjApLHNlcCA9ICJfIikpCmZkYi5zcGVjaWVzIDwtIGMoIkNsdXNpdGFuaWFlIiwgIkNhdXJpcyIsIHJlcCgiQ3BhcmFwc2lsb3NpcyIsdGltZXMgPSAzKSkKZmRiLm5hbWVzIDwtIHBhc3RlKGZkYi5saXN0LCBmZGIuc3BlY2llcywgc2VwID0gIl8iKQpmdW5naWRiLnNlcSA8LSByZWFkLmZhc3RhKCJkYXRhL2Z1bmdpZGIvWFBfMDI4ODg5MDMzX2hvbW9sb2dzX2Z1bmdpZGJfTjM2MF91c2UuZmFzdGEiLCBzZXF0eXBlID0gIkFBIiwgYXMuc3RyaW5nID0gVFJVRSwgc3RyaXAuZGVzYyA9IFRSVUUpCgojIHNlcXVlbmNlcyB0byBiZSBhZGRlZCB0byB0aGUgcmVmc2VxIGxpc3QgZnJvbSBncnljIGhpdHMKZ3J5Yy5zZXEgPC0gcmVhZC5mYXN0YSgiZGF0YS9HUllDL1hQXzAyODg4OTAzM19ob21vbG9nc19ncnljLmZhc3RhIiwgc2VxdHlwZSA9ICJBQSIsIGFzLnN0cmluZyA9IFRSVUUsIHN0cmlwLmRlc2MgPSBGQUxTRSkKZ3J5Yy5saXN0IDwtIGdyeWMxJG9yaV9pZApncnljLm5hbWVzIDwtIGdyeWMxICU+JSBtdXRhdGUobmFtZSA9IHBhc3RlKGlkLCBzcGVjaWVzX2lkLCBzZXAgPSAiXyIpKSAlPiUgcHVsbChuYW1lKQoKIyBzZXF1ZW5jZXMgdG8gYmUgYWRkZWQgdG8gdGhlIApjb21iaW5lZC5zZXEgPC0gYyhyZWZzZXFkYi5zZXFbcmVmc2VxZGIubGlzdF0sIGZ1bmdpZGIuc2VxW2ZkYi5saXN0XSwgZ3J5Yy5zZXFbZ3J5Yy5saXN0XSkKY29tYmluZWQubmFtZXMgPC0gYyhyZWZzZXFkYi5uYW1lcywgZmRiLm5hbWVzLCBncnljLm5hbWVzKQp3cml0ZS5mYXN0YShjb21iaW5lZC5zZXEsIG5hbWVzID0gY29tYmluZWQubmFtZXMsIGZpbGUub3V0ID0gIm91dHB1dC9YUF8wMjg4ODkwMzNfaG9tb2xvZ3NfY29tYmluZS5mYXN0YSIpCmBgYAoKYGBge3J9CiMgaW4gdGhlICJvdXRwdXQvZ2VuZS10cmVlL1JFQURNRS5tZCIsIEkgZG9jdW1lbnRlZCBhbiBpc3N1ZSB3aXRoIGEgcGFydGljdWxhcgojIHNlcXVlbmNlLCBYUF8wMjUzNDQ0MDcuMSwgd2hpY2ggaGFzIGEgbG9uZyBOLXRlcm1pbmFsIHBhcnQgdGhhdCBpcyBub3QgYWxpZ25lZCAKIyB0byBhbnkgb3RoZXIgc2VxdWVuY2VzLiB0aGUgc29sdXRpb24gaXMgdG8gcmVtb3ZlIHRoZSBmaXJzdCA5MDAgYS5hLiBvZiBpdAojIGFuZCBvdXRwdXQgYSBkaWZmZXJlbnQgdmVyc2lvbiBvZiBmYXN0YSBmaWxlIGp1c3QgZm9yIGdlbmUgdHJlZSBhbmFseXNpcwp0cnVuY2F0ZWQgPC0gc3RyX3N1YihyZWZzZXFkYi5zZXEkWFBfMDI1MzQ0NDA3LjEsIHN0YXJ0ID0gOTAxKQphdHRyaWJ1dGVzKHRydW5jYXRlZCkgPC0gYXR0cmlidXRlcyhyZWZzZXFkYi5zZXEkWFBfMDI1MzQ0NDA3LjEpCmNvbWJpbmVkLnNlcSRYUF8wMjUzNDQ0MDcuMSA8LSB0cnVuY2F0ZWQKd3JpdGUuZmFzdGEoY29tYmluZWQuc2VxLCBuYW1lcyA9IGNvbWJpbmVkLm5hbWVzLCBmaWxlLm91dCA9ICJvdXRwdXQvWFBfMDI4ODg5MDMzX2hvbW9sb2dzX2NvbWJpbmVfZm9yX2dlbmVfdHJlZS5mYXN0YSIpCmBgYAoKYGBge3J9CiMgbWVyZ2UgYmxhc3QgaGl0IHRhYmxlCnJlZnNlcS5zdGF0IDwtICByZWZzZXExICU+JSAKICBzZWxlY3QoaWQgPSBhY2Nlc3Npb24sIHNwZWNpZXMsIHN0cmFpbiwgZV92YWx1ZSA9IGUudmFsdWUsIHF1ZXJ5X2NvdmVyID0gcXVlcnkuY292ZXIsIHBlcmNfaWRlbnQgPSBwZXJjLmlkZW50LCBsZW5ndGgpCmZkYi5zdGF0IDwtIGZkYi5maWx0ZXJlZCAlPiUgCiAgZmlsdGVyKGdlbmVfaWQgJWluJSBmZGIubGlzdCkgJT4lIAogIG11dGF0ZShvcmdhbmlzbSA9IGdzdWIoInN0cmFpbiAiLCAiIiwgb3JnYW5pc20pLAogICAgICAgICBzcGVjaWVzID0gd29yZChvcmdhbmlzbSwgMSwgMiksCiAgICAgICAgIHN0cmFpbiA9IHdvcmQob3JnYW5pc20sIDMsIC0xKSwKICAgICAgICAgcXVlcnlfY292ZXIgPSBOQSwgcGVyY19pZGVudCA9IE5BKSAlPiUgCiAgc2VsZWN0KGlkID0gZ2VuZV9pZCwgc3BlY2llcywgc3RyYWluLCBlX3ZhbHVlLCBxdWVyeV9jb3ZlciwgcGVyY19pZGVudCwgbGVuZ3RoKQpncnljLnN0YXQgPC0gZ3J5YyAlPiUgCiAgZmlsdGVyKG9yaV9pZCAlaW4lIGdyeWMubGlzdCkgJT4lIAogIG11dGF0ZShvcmdhbmlzbSA9IHNwZWNpZXMsCiAgICAgICAgIHNwZWNpZXMgPSB3b3JkKG9yZ2FuaXNtLCAxLCAyKSwKICAgICAgICAgc3RyYWluID0gd29yZChvcmdhbmlzbSwgMywgLTEpKSAlPiUgCiAgc2VsZWN0KGlkLCBzcGVjaWVzLCBzdHJhaW4sIGVfdmFsdWUgPSBtaW5fZV92YWx1ZSwgcXVlcnlfY292ZXIsIHBlcmNfaWRlbnQgPSBtYXhfaWRlbnQsIGxlbmd0aCkKY29tYmluZWQuYmxhc3QgPC0gYmluZF9yb3dzKGxpc3QocmVmc2VxID0gcmVmc2VxLnN0YXQsIEZ1bmdpREIgPSBmZGIuc3RhdCwgR1JZQyA9IGdyeWMuc3RhdCksIC5pZCA9ICJzb3VyY2UiKQp3cml0ZV90c3YoY29tYmluZWQuYmxhc3QsICJvdXRwdXQvWFBfMDI4ODg5MDMzX2hvbW9sb2dzX2NvbWJpbmVfYmxhc3Rfc3RhdC50c3YiKQoKIyBhbHNvIGV4cG9ydCB0aGUgcmVqZWN0ZWQgbGlzdCBmcm9tIHJlZnNlcQpyZWZzZXEucmVqZWN0ZWQgPC0gcmVmc2VxLk4zNjAgJT4lIAogIGZpbHRlcihsZW5ndGggPCA1MDAsIHNjaS5uYW1lICVpbiUgYygiQ2FuZGlkYSBwYXJhcHNpbG9zaXMiLCAiQ2FuZGlkYSB0cm9waWNhbGlzIE1ZQS0zNDA0IiwgIkNsYXZpc3BvcmEgbHVzaXRhbmlhZSBBVENDIDQyNzIwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxvZGRlcm9teWNlcyBlbG9uZ2lzcG9ydXMgTlJSTCBZQi00MjM5IiwgIlNjaGVmZmVyc29teWNlcyBzdGlwaXRpcyBDQlMgNjA1NCIpKSAlPiUgCiAgc2VsZWN0KGlkID0gYWNjZXNzaW9uLCBzcGVjaWVzID0gc2NpLm5hbWUsIGVfdmFsdWUgPSBlLnZhbHVlLCBxdWVyeV9jb3ZlciA9IHF1ZXJ5LmNvdmVyLCBwZXJjX2lkZW50ID0gcGVyYy5pZGVudCwgbGVuZ3RoKQp3cml0ZV90c3YocmVmc2VxLnJlamVjdGVkLCAib3V0cHV0L1hQXzAyODg4OTAzM19ob21vbG9nc19sZW5ndGhfZmlsdGVyZWRfYmxhc3Rfc3RhdC50c3YiKQpgYGAKCgpgYGB7cn0KIyBpbiB0aGUgIm91dHB1dC9nZW5lLXRyZWUvUkVBRE1FLm1kIiwgSSBkb2N1bWVudGVkIGFuIGlzc3VlIHdpdGggYSBwYXJ0aWN1bGFyCiMgc2VxdWVuY2UsIFhQXzAyNTM0NDQwNy4xLCB3aGljaCBoYXMgYSBsb25nIE4tdGVybWluYWwgcGFydCB0aGF0IGlzIG5vdCBhbGlnbmVkIAojIHRvIGFueSBvdGhlciBzZXF1ZW5jZXMuIHRoZSBzb2x1dGlvbiBpcyB0byByZW1vdmUgdGhlIGZpcnN0IDkwMCBhLmEuIG9mIGl0CiMgYW5kIG91dHB1dCBhIGRpZmZlcmVudCB2ZXJzaW9uIG9mIGZhc3RhIGZpbGUganVzdCBmb3IgZ2VuZSB0cmVlIGFuYWx5c2lzCnRydW5jYXRlZCA8LSBzdHJfc3ViKHJlZnNlcWRiLnNlcSRYUF8wMjUzNDQ0MDcuMSwgc3RhcnQgPSA5MDEpCmF0dHJpYnV0ZXModHJ1bmNhdGVkKSA8LSBhdHRyaWJ1dGVzKHJlZnNlcWRiLnNlcSRYUF8wMjUzNDQ0MDcuMSkKY29tYmluZWQuc2VxJFhQXzAyNTM0NDQwNy4xIDwtIHRydW5jYXRlZAp3cml0ZS5mYXN0YShjb21iaW5lZC5zZXEsIG5hbWVzID0gY29tYmluZWQubmFtZXMsIGZpbGUub3V0ID0gIm91dHB1dC9YUF8wMjg4ODkwMzNfaG9tb2xvZ3NfY29tYmluZV9mb3JfZ2VuZV90cmVlLmZhc3RhIikKYGBgCgojIyBDaHJvbW9zb21hbCBsb2NhdGlvbnMKVGhlIGdvYWwgb2YgdGhpcyBhbmFseXNpcyBpcyB0byBleHRyYWN0IHRoZSBjaHJvbW9zb21hbCBsb2NhdGlvbnMgZm9yIGFsbCBob21vbG9ncyBpZGVudGlmaWVkLiBOb3RpY2UgdGhhdCBub3QgYWxsIGdlbm9tZXMgZnJvbSB3aGljaCB0aGVzZSBzZXF1ZW5jZXMgY2FtZSBhcmUgd2VsbCBhc3NlbWJsZWQuIElmIHRoZSBnZW5vbWUgaXMgbm90IGZ1bGx5IGFzc2VtYmxlZCB0byB0aGUgY2hyb21vc29tZSBsZXZlbCwgdGhlIGxvY2F0aW9ucyB3b3VsZCBub3QgYmUgbWVhbmluZ2Z1bC4KCj4qKlVwZGF0ZSAyMDIxLTA2LTE3KiogVGhlIF9DLiBhdXJpc18gcmVmc2VxIHN0cmFpbiwgQjExMjIxLCB3YXMgYXNzZW1ibGVkIHRvIHRoZSBzY2FmZm9sZCBsZXZlbCBvbiB0aGUgTkNCSSBBc3NlbWJseSBkYXRhYmFzZSBwYWdlLiBCdXQgYWNjb3JkaW5nIHRvIE11w7FveiBfZXQgYWwuXyAyMDIxLCB0aGUgZ2Vub21lIHdhcyBiYXNpY2FsbHkgYXNzZW1ibGVkIHRvIHRoZSBjaHJvbW9zb21hbCBsZXZlbCAtLSB0aGUgZmlyc3QgNyBzY2FmZm9sZHMgYXJlIGNocm9tb3NvbWFsIGxlbmd0aCwgd2l0aCB0aGUgcmVtYWluaW5nIHNjYWZmb2xkcyBiZWluZyBzaG9ydCBhbmQgdW5hc3NlbWJsZWQgZnJhZ21lbnRzLiBUaGlzIG1lYW5zIEkgY2FuIHVzZSB0aGUgbG9jYXRpb24gaW5mb3JtYXRpb24gZm9yIHRoaXMgc3RyYWluIGlmIHRoZSBnZW5lIGlzIGxvY2F0ZWQgb24gdGhlIGZpcnN0IHNldmVuIHNjYWZmb2xkcy4KClRvIGRldGVybWluZSB0aGUgbG9jYXRpb24gb24gdGhlIGNocm9tb3NvbWUsIHdlIG5lZWQgdG8ga25vdyAxKSB0aGUgY29vcmRpbmF0ZSBvZiB0aGUgZ2VuZSBlbmNvZGluZyB0aGUgcHJvdGVpbiBhbmQgMikgdGhlIGxlbmd0aCBvZiB0aGUgY2hyb21vc29tZS4gT3VyIGFwcHJvYWNoIHRvIG9idGFpbiB0aGVzZSBkYXRhIGRlcGVuZHMgdXBvbiB0aGUgc291cmNlIG9mIHRoZSBzZXF1ZW5jZXMuIEZvciBwcm90ZWlucyBpZGVudGlmaWVkIHRocm91Z2ggRnVuZ2lEQiBhbmQgR1JZQywgc2luY2UgdGhlcmUgYXJlIG9ubHkgYSBzbWFsbCBudW1iZXIgb2YgdGhlbSwgSSBqdXN0IG1hbnVhbGx5IHNlYXJjaGVkIGFuZCBlbnRlcmVkIHRoZSBpbmZvcm1hdGlvbi4gRm9yIFJlZnNlcSBwcm90ZWlucywgc2luY2UgaXQgcmVwcmVzZW50cyB0aGUgbWFqb3JpdHkgb2YgdGhlIGRhdGEsIGFuZCBzaW5jZSB0aGUgYHJlbnRyZXpgIHBhY2thZ2UgYWxsb3dlZCBtZSB0byBjcm9zcy1yZWZlcmVuY2UgaW5mb3JtYXRpb24gYW1vbmcgdGhlIE5DQkkgZGF0YWJhc2VzLCBJJ2xsIGF0dGVtcHQgdG8gZ2F0aGVyIHRoZSBpbmZvcm1hdGlvbiBwcm9ncmFtbWF0aWNhbGx5LgoKMS4gQ29sbGVjdCBnZW5vbWUgYXNzZW1ibHkgaW5mb3JtYXRpb24gZm9yIGFsbCBzcGVjaWVzL3N0cmFpbnMgaW5jbHVkZWQgaW4gdGhlIFJlZnNlcSBkYXRhc2V0LgpgYGB7ciBnZW5vbWVfYXNzZW1ibHl9CiMgRm9yIGFsbCBzZXF1ZW5jZXMgd2l0aCByZWZzZXEgSURzCiMgRmlyc3QgZ2V0IHRoZSBnZW5vbWUgYXNzZW1ibHkgSUQgdXNpbmcgdGhlIHByb3RlaW4gSUQgYXMgcXVlcnkKcmVmc2VxLnN0cmFpbiA8LSB1bmlxdWUod2l0aChyZWZzZXExLCBwYXN0ZShvcmdhbmlzbSwgc3RyYWluLCBzZXAgPSAiICIpKSkKbmFtZXMocmVmc2VxLnN0cmFpbikgPC0gd29yZChyZWZzZXEuc3RyYWluLCAxLCAyKQpyZWZzZXEuc3RyYWluWyJOYXVtb3ZvenltYSBjYXN0ZWxsaWkiXSA8LSAiTmF1bW92b3p5bWEgY2FzdGVsbGlpIENCUyA0MzA5IgpyZWZzZXEuc3RyYWluWyJDYW5kaWRhIGdsYWJyYXRhIl0gPC0gIkNhbmRpZGEgZ2xhYnJhdGEgMTM4IiAjIGZvciBzb21lIHJlYXNvbiB0aGUgb3JpZ2luYWwgcXVlcnkgd291bGRuJ3QgcmV0cmlldmUgdGhlIHJlc3VsdApyZWZzZXEuc3RyYWluWyJDYW5kaWRhIHBhcmFwc2lsb3NpcyJdIDwtICJBU00xODI3NnYyIiAgICAgICAjIHVzaW5nIHRoZSBzcGVjaWVzIGFuZCBzdHJhaW4gbmFtZSBjb21ibyBkaWRuJ3QgcmV0cmlldmUgdGhlIHJlZnNlcSBhc3NlbWJseQojIGZ1bmdpZGIgZGlkbid0IGFkZCBhbnkgbmV3IHNwZWNpZXMgdGhhdCByZWZzZXEgaGl0cyBkaWRuJ3QgYWxyZWFkeSBoYXZlCmdyeWMuc3RyYWluIDwtIHVuaXF1ZShncnljMSRzcGVjaWVzKQpuYW1lcyhncnljLnN0cmFpbikgPC0gd29yZChncnljLnN0cmFpbiwgMSwgMikKZ3J5Yy5zdHJhaW5bIktsdXl2ZXJvbXljZXMgbGFjdGlzIl0gPC0gIkFTTTI1MXYxIiAgICAgICAgICAgIyB0aGlzIGlzIHRoZSBOQ0JJIEFzc2VtYmx5IGZvciBLLiBsYWN0aXMgTlJSTCBZLTExNDAsIG5vdCB0aGUgR1JZQyBDQlMgMjM1OSwgYnV0IHdlIG9ubHkgaGFkIG9uZSBzZXF1ZW5jZSBmcm9tIHRoaXMgc3BlY2llcyBhbmQgdGhlIHNlcXVlbmNlIGlzIGlkZW50aWNhbCB0byB0aGUgcmVmc2VxIHZlcnNpb24gZnJvbSB0aGUgTlJSTCBzdHJhaW4KYWxsLnN0cmFpbiA8LSBjKHJlZnNlcS5zdHJhaW4sIGdyeWMuc3RyYWluKQp0bXAgPC0gc2FwcGx5KGFsbC5zdHJhaW4sIGZ1bmN0aW9uKGkpewogICNTeXMuc2xlZXAoMSkgIyBhZGRlZCB0byBhdm9pZCB0aGUgQVBJIHJhdGUgbGltaXRhdGlvbiBlcnJvcgogIGVudHJlel9zZWFyY2goZGIgPSAiYXNzZW1ibHkiLCB0ZXJtID0gaSwgdXNlX2hpc3RvcnkgPSBUKSRpZHMKfSkKYXNzZW1ibHkuaW5mbyA8LSBzYXBwbHkodG1wLCBmdW5jdGlvbihpKXsKICBpZighaXNfZW1wdHkoaSkpCiAgICBlbnRyZXpfc3VtbWFyeShkYiA9ICJhc3NlbWJseSIsIGlkID0gaSkKfSkKYGBgCgpgYGB7ciB0ZXN0LCBpbmNsdWRlPUZBTFNFLCBlY2hvPUZBTFNFfQp0ZXN0IDwtICJYUF83MTg2MzAuMSIKI3Rlc3QgPC0gIlhQXzAyODg4OTAzMy4xIgp0bXAucHJvdCA8LSBlbnRyZXpfc3VtbWFyeShkYiA9ICJwcm90ZWluIiwgaWQgPSB0ZXN0KQp0bXAucHJvLmxuayA8LSBlbnRyZXpfbGluayhkYmZyb20gPSAicHJvdGVpbiIsIGlkID0gdGVzdCwgZGIgPSAicHJvdGVpbiIpClN5cy5zbGVlcCgxKQp0bXAubnVjLmxuayA8LSBlbnRyZXpfbGluayhkYmZyb20gPSAicHJvdGVpbiIsIGlkID0gdGVzdCwgZGIgPSAibnVjY29yZSIpClN5cy5zbGVlcCgxKQp0bXAuZ2VuLmxuayA8LSBlbnRyZXpfbGluayhkYmZyb20gPSAicHJvdGVpbiIsIGlkID0gdGVzdCwgZGIgPSAiZ2VuZSIpClN5cy5zbGVlcCgxKQp0bXAuZ2Vub21lLmxuayA8LSBlbnRyZXpfbGluayhkYmZyb20gPSAicHJvdGVpbiIsIGlkID0gdGVzdCwgZGIgPSAiZ2Vub21lIikKU3lzLnNsZWVwKDEpCnRtcC5udWMgPC0gZW50cmV6X3N1bW1hcnkoZGIgPSAibnVjY29yZSIsIGlkID0gdG1wLm51Yy5sbmskbGlua3MkcHJvdGVpbl9udWNjb3JlKQpTeXMuc2xlZXAoMSkKdG1wLmdlbiA8LSBlbnRyZXpfc3VtbWFyeShkYiA9ICJnZW5lIiwgaWQgPSB0bXAuZ2VuLmxuayRsaW5rcyRwcm90ZWluX2dlbmUpClN5cy5zbGVlcCgxKQp0bXAubnVjLmFsbCA8LSBlbnRyZXpfZmV0Y2goZGIgPSAibnVjY29yZSIsIGlkID0gdG1wLm51Yy5sbmskbGlua3MkcHJvdGVpbl9udWNjb3JlLCByZXR0eXBlID0gInNlcWlkIiwgcmV0bW9kZSA9ICJ0ZXh0IikKYGBgCgoyLiBGb3Igc3BlY2llcyB3aXRoIGNocm9tb3NvbWFsIGxldmVsIGFzc2VtYmxpZXMsIHdlIGNhbiB1c2UgdGhlIHByb3RlaW4gSUQgdG8gZmlzaCBvdXQgdGhlIGNocm9tb3NvbWFsIGxvY2F0aW9uIGFuZCBjaHJvbW9zb21lIGxlbmd0aCBpbmZvcm1hdGlvbi4KCmBgYHtyIGNocl9sb2MxfQojIEdldCB0aGUgcHJvdGVpbidzIGdlbmUgSUQgKG5vdCByZWZzZXEgSUQpCnRtcC5nZW5lLmxuayA8LSBlbnRyZXpfbGluayhkYmZyb20gPSAicHJvdGVpbiIsIGlkID0gcmVmc2VxMSRhY2Nlc3Npb24sIGRiID0gImdlbmUiLCBieV9pZCA9IFRSVUUpCiMgZXh0cmFjdCBnZW5lIElECnRtcC5nZW5lLmlkIDwtIHNhcHBseSh0bXAuZ2VuZS5sbmssIGZ1bmN0aW9uKHgpIHgkbGlua3MkcHJvdGVpbl9nZW5lKQojIGZldGNoIHN1bW1hcnkgaW5mbyBmcm9tIHRoZSBnZW5lIGRhdGFiYXNlCnRtcC5nZW5lIDwtIGVudHJlel9zdW1tYXJ5KGRiID0gImdlbmUiLCBpZCA9IHRtcC5nZW5lLmlkKQojIGV4dHJhY3QgZ2VuZSBuYW1lCnRtcC5nZW5lLm5hbWUgPC0gZXh0cmFjdF9mcm9tX2VzdW1tYXJ5KHRtcC5nZW5lLCBlbGVtZW50cyA9ICJuYW1lIikKIyBleHRyYWN0IHN5c3RlbWF0aWMgSUQgKHN0b3JlZCBpbiB0aGUgb3RoZXJhbGlhcyBmaWVsZCkKdG1wLmdlbmUuYWxpYXMgPC0gZXh0cmFjdF9mcm9tX2VzdW1tYXJ5KHRtcC5nZW5lLCBlbGVtZW50cyA9ICJvdGhlcmFsaWFzZXMiKQojIGV4dHJhY3QgY2hyb21vc29tYWwgbG9jYXRpb25zLCBvbmx5IGF2YWlsYWJsZSBmb3IgZnVsbHkgYXNzZW1ibGVkIGdlbm9tZXMKdG1wLmdlbmUubG9jIDwtIGJpbmRfcm93cyhleHRyYWN0X2Zyb21fZXN1bW1hcnkodG1wLmdlbmUsIGVsZW1lbnRzID0gImdlbm9taWNpbmZvIiksIC5pZCA9ICJnZW5lX3VpZCIpCiMgcG9vbCB0aGUgdW5pcXVlIGNocm9tb3NvbWUgc2VxdWVuY2UgSURzIHRvIGZpbmQgb3V0IHRoZWlyIGxlbmd0aAp0bXAuY2hyLmlkIDwtIHVuaXF1ZSh0bXAuZ2VuZS5sb2MkY2hyYWNjdmVyKQojIG9idGFpbiB0aGUgY2hyb21vc29tZSBsZW5ndGggaW5mb3JtYXRpb24gZnJvbSB0aGUgbnVjY29yZSBkYXRhYmFzZQp0bXAuY2hyIDwtIGVudHJlel9zdW1tYXJ5KCJudWNjb3JlIiwgaWQgPSB0bXAuY2hyLmlkKQp0bXAuY2hyLmxlbiA8LSBleHRyYWN0X2Zyb21fZXN1bW1hcnkodG1wLmNociwgZWxlbWVudHMgPSAic2xlbiIpOyAKbmFtZXModG1wLmNoci5sZW4pID0gdG1wLmNoci5pZAojIGFsc28gZ2V0IHRoZSBhc3NlbWJseSBpZAp0bXAuYXNzZW1ibHkubG5rIDwtIGVudHJlel9saW5rKGRiZnJvbSA9ICJudWNjb3JlIiwgaWQgPSB0bXAuY2hyLmlkLCBkYiA9ICJhc3NlbWJseSIsIGJ5X2lkID0gVCkKIyBzb21lIHNlcXVlbmNlcyBtYXAgdG8gbW9yZSB0aGFuIG9uZSBhc3NlbWJseS4gdXNlIHRoZSBmaXJzdCBvbmUKdG1wLmFzc2VtYmx5LmlkIDwtIHNhcHBseSh0bXAuYXNzZW1ibHkubG5rLCBmdW5jdGlvbihpKSBpJGxpbmtzJG51Y2NvcmVfYXNzZW1ibHlbMV0pCiMgMjAyMS0wNi0xOCBmb3IgdW5rbm93biByZWFzb25zLCBvbmUgb2YgdGhlIGNocm9tb3NvbWVzIGZhaWxlZCB0byBnZXQgYSBsaW5rIGV2ZW4gdGhvdWdoIAojICAgc2VhcmNoZXMgb24gdGhlIE5DQkkgd2Vic2l0ZSBnaXZlcyB0aGUgZXhwZWN0ZWQgcmVzdWx0LiBNYW51YWxseSBhZGQgaXQgaW4KIyB0bXAuYXNzZW1ibHkuaWRbWzhdXSA8LSAiMjc3NDc4Igp0bXAuYXNzZW1ibHkgPC0gbWFwX2Nocih0bXAuYXNzZW1ibHkuaWQsIGZ1bmN0aW9uKHgpCiAgZW50cmV6X3N1bW1hcnkoZGIgPSAiYXNzZW1ibHkiLCBpZCA9IHgpJGFzc2VtYmx5YWNjZXNzaW9uKQpuYW1lcyh0bXAuYXNzZW1ibHkpIDwtIHRtcC5jaHIuaWQKIyBjb25zdHJ1Y3QgYSBuZXcgdGliYmxlIHRvIGhvbGQgdGhlIGNocm9tb3NvbWFsIGxvY2F0aW9uIGluZm8KcmVmc2VxMiA8LSByZWZzZXExICU+JSAKICBzZWxlY3QoYWNjZXNzaW9uLCBzcGVjaWVzLCBvcmdhbmlzbSkgJT4lIAogIG11dGF0ZShnZW5lX3VpZCA9IG5hbWVzKHRtcC5nZW5lLm5hbWUpLCBnZW5lX2lkID0gdG1wLmdlbmUubmFtZSwgYWxpYXMgPSB0bXAuZ2VuZS5hbGlhcykgJT4lIAogIGxlZnRfam9pbih0bXAuZ2VuZS5sb2MsIGJ5ID0gYygiZ2VuZV91aWQiID0gImdlbmVfdWlkIikpICU+JSAKICBtdXRhdGUoYXNzZW1ibHkgPSB0bXAuYXNzZW1ibHlbY2hyYWNjdmVyXSwgY2hyTCA9IHRtcC5jaHIubGVuW2NocmFjY3Zlcl0sCiAgICAgICAgIHJlbExvYyA9IHJvdW5kKGNocnN0YXJ0L2NockwsMikpICU+JSAKICBzZWxlY3QoLWV4b25jb3VudCkKYGBgCgpgYGB7ciBzZXBhcmF0ZWx5X3RyZWF0X2Z1bGx5X2Zyb21fcGFydGlhbGx5X2Fzc2VtYmxlZF9nZW5vbWVzfQojIHNpbmNlIHNvbWUgc3BlY2llcyBhcmUgZnVsbHkgYXNzZW1ibGVkIHdoaWxlIG90aGVycyBhcmUgbm90LCBsZXQncyBzcGxpdCB0aGVtIGludG8gdHdvIGFuZCBkZWFsIHdpdGggdGhlIGxhdHRlciBzZXBhcmF0ZWx5CnJlZnNlcTJzIDwtIGdyb3VwX3NwbGl0KHJlZnNlcTIsIGlzLm5hKGNocmFjY3ZlciksIC5rZWVwID0gRkFMU0UpCiMgcHJpbnQgdGhlIG9uZXMgc3VjY2Vzc2Z1bGx5IGFzc2VtYmxlZApyZWZzZXEyc1tbMV1dICU+JSBzZWxlY3QoLWdlbmVfdWlkLCAtY2hyYWNjdmVyKSAlPiUgYXJyYW5nZShzcGVjaWVzLCByZWxMb2MpCmBgYApBYm92ZSBhcmUgdGhlIG9uZXMgdGhhdCB3ZSBjYW4gZmluZCB0aGUgY2hyb21vc29tYWwgaW5mb3JtYXRpb24gZm9yLgoKMy4gTmV4dCBsZXQncyBkZWFsIHdpdGggdGhlIHBhcnRpYWxseSBhc3NlbWJsZWQgZ2Vub21lcy4gVGhlIGlkZWEgaXMgdG8gZmlyc3QgZG93bmxvYWQgdGhlIGdlbm9tZSBhbm5vdGF0aW9uIGZpbGVzIGZvciB0aGUgc3BlY2llcy9zdHJhaW5zIGFuZCB0aGVuIGV4dHJhY3QgdGhlIGdlbm9taWMgaW5mb3JtYXRpb24gZnJvbSB0aGVtLiBCZWNhdXNlIHNvbWUgc3BlY2llcy9zdHJhaW4gY291bGQgaGF2ZSBtdWx0aXBsZSBhc3NlbWJsaWVzLCB3ZSBuZWVkIHRvIGNvbGxlY3QgYWxsIG9mIHRoZW0gYW5kIHRoZW4gaWRlbnRpZnkgdGhlIGxhdGVzdCAvIHJlcHJlc2VudGF0aXZlIG9uZS4gSSBmb3VuZCB0aGF0IGVhY2ggc3BlY2llcy9zdHJhaW4gaGFzIG9ubHkgb25lIGFzc2VtYmx5IHRoYXQgaGFzIHRoZSAicmVmc2VxIiBzdGF0dXMuIFRoZXJlZm9yZSBJIGNob3NlIHRoYXQgYXMgdGhlIHJlcHJlc2VudGF0aXZlIGFzc2VtYmx5LgoKQmVsb3cgYXJlIGFsbCB0aGUgc3BlY2llcyBpbiBvdXIgZGF0YXNldCwgZm9sbG93ZWQgYnkgdGhlIGNvcnJlc3BvbmRpbmcgcmVmc2VxIGFzc2VtYmx5LgpgYGB7ciBjaHJfbG9jMn0KIyAxLiBMaXN0IHRoZSB1bmlxdWUgc3BlY2llcyBpbiB0aGUgZGF0YXNldApzcHMubGlzdCA8LSBuYW1lcyhhbGwuc3RyYWluKQpzcHMubGlzdAojIDIuIE9idGFpbiBhc3NlbWJseSBpbmZvcm1hdGlvbgojIyBleHBsYW5hdGlvbiBmb3IgdGhlIGZpZWxkcyBmcm9tIHRoZSBhc3NlbWJseSByZWNvcmQ6IGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvYXNzZW1ibHkvaGVscC8KIyMgdG8gcGFyc2UgaW5mb3JtYXRpb24gZnJvbSB0aGUgIm1ldGEiIGZpZWxkLCB3aGljaCBpcyBpbiBYTUwgZm9ybWF0LCBJIHVzZWQgdGhlICJYTUwiIHBhY2thZ2UKIyMgYW5kIHRvIGFjY2VzcyBwYXJ0aWN1bGFyIG5vZGVzIGZyb20gdGhlIG9iamVjdCwgSSB1c2VkIHRoZSBzeW50YXggYXMgc3BlY2lmaWVkIGZvciBYUGF0SAojIyByZWZlcmVuY2U6IGh0dHBzOi8vd3d3Lnczc2Nob29scy5jb20veG1sL3hwYXRoX3N5bnRheC5hc3AKc3BzLnN0YXQgPC0gdGliYmxlKHNwZWNpZXMgPSBjaGFyYWN0ZXIoKSwgc3RyYWluID0gY2hhcmFjdGVyKCksICBhc3NlbWJseWFjY2Vzc2lvbiA9IGNoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgYXNzZW1ibHluYW1lID0gY2hhcmFjdGVyKCksIGFzc2VtYmx5c3RhdHVzID0gY2hhcmFjdGVyKCksCiAgICAgICAgICAgICAgICAgICBuX2NociA9IGludGVnZXIoKSwgbl9zY2FmZm9sZCA9IGludGVnZXIoKSwgbl9jb250aWcgPSBpbnRlZ2VyKCksCiAgICAgICAgICAgICAgICAgICBsYXN0dXBkYXRlID0gY2hhcmFjdGVyKCksIHN1Ym1pdHRlciA9IGNoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgcmVmc2VxID0gY2hhcmFjdGVyKCksIGZ0cHJlcG9ydCA9IGNoYXJhY3RlcigpKQoKZm9yKHMgaW4gc3BzLmxpc3QpewogIHNzIDwtIGFzc2VtYmx5LmluZm9bW3NdXQogIGlmKGNsYXNzKHNzKVsxXSA9PSAiZXN1bW1hcnkiKXsKICAgICMgc29tZSBzdHJhaW5zIGhhdmUganVzdCBvbmUgYXNzZW1ibHkKICAgICMgbW9zdCBvZiB0aGUgaW5mb3JtYXRpb24gYXJlIGVhc2lseSBhY2Nlc3NpYmxlIGFzIGVsZW1lbnRzIG9mIHRoZSBsaXN0CiAgICAjIGZvciB0aGUgIyBvZiBjb250aWdzIGFuZCAjIG9mIHNjYWZmb2xkcywgaG93ZXZlciwgSSBuZWVkIHRvIHBhcnNlIHRoZSBYTUwgZm9ybWF0dGVkICJleHRyYSIgZmllbGQKICAgIGYgPSBuZXdYTUxOb2RlKCJhc3NlbWJseSIpCiAgICBwYXJzZVhNTEFuZEFkZChzcyRtZXRhLCBmKQogICAgbi5jaHIgPSBnZXROb2RlU2V0KGYsICIvL1N0YXRbQGNhdGVnb3J5PSdjaHJvbW9zb21lX2NvdW50J10iKSAlPiUgeG1sVmFsdWUgJT4lIGFzLmludGVnZXIKICAgIG4uc2NhZmZvbGQgPSBnZXROb2RlU2V0KGYsICIvL1N0YXRbQGNhdGVnb3J5PSdzY2FmZm9sZF9jb3VudCcgYW5kIEBzZXF1ZW5jZV90YWc9J2FsbCddIikgJT4lIHhtbFZhbHVlICU+JSBhcy5pbnRlZ2VyCiAgICBuLmNvbnRpZyA9IGdldE5vZGVTZXQoZiwgIi8vU3RhdFtAY2F0ZWdvcnk9J2NvbnRpZ19jb3VudCddIikgJT4lIHhtbFZhbHVlICU+JSBhcy5pbnRlZ2VyCiAgICB0bXAuc3RyYWluID0gc3MkYmlvc291cmNlJGluZnJhc3BlY2llc2xpc3Qkc3ViX3ZhbHVlCiAgICBzcHMuc3RhdCA8LSBzcHMuc3RhdCAlPiUgCiAgICAgIGFkZF9yb3coc3BlY2llcyA9IHMsIHN0cmFpbiA9IGlmZWxzZShpcy5udWxsKHRtcC5zdHJhaW4pLCBzcyRiaW9zb3VyY2UkaXNvbGF0ZSwgdG1wLnN0cmFpbiksIAogICAgICAgICAgICAgIGFzc2VtYmx5YWNjZXNzaW9uID0gc3MkYXNzZW1ibHlhY2Nlc3Npb24sIGFzc2VtYmx5bmFtZSA9IHNzJGFzc2VtYmx5bmFtZSwgYXNzZW1ibHlzdGF0dXMgPSBzcyRhc3NlbWJseXN0YXR1cywKICAgICAgICAgICAgICBuX2NociA9IG4uY2hyLCBuX3NjYWZmb2xkID0gbi5zY2FmZm9sZCwgbl9jb250aWcgPSBuLmNvbnRpZywKICAgICAgICAgICAgICBsYXN0dXBkYXRlID0gc3MkbGFzdHVwZGF0ZWRhdGUsIHN1Ym1pdHRlciA9IHNzJHN1Ym1pdHRlcm9yZ2FuaXphdGlvbiwKICAgICAgICAgICAgICByZWZzZXEgPSBzcyRyZWZzZXFfY2F0ZWdvcnksIGZ0cHJlcG9ydCA9IHNzJGZ0cHBhdGhfYXNzZW1ibHlfcnB0KQogIH1lbHNlIGlmKGNsYXNzKHNzKVsxXSA9PSAiZXN1bW1hcnlfbGlzdCIpewogICAgIyBzb21lIHN0cmFpbnMgaGF2ZSBtb3JlIHRoYW4gb25lIGFzc2VtYmx5IGFuZCB3ZSBpdGVyYXRlIHRocm91Z2ggdGhlbQogICAgc3MwID0gc3MKICAgIGZvcihzcyBpbiBzczApewogICAgICBmID0gbmV3WE1MTm9kZSgiYXNzZW1ibHkiKQogICAgICBwYXJzZVhNTEFuZEFkZChzcyRtZXRhLCBmKQogICAgICBuLmNociA9IGdldE5vZGVTZXQoZiwgIi8vU3RhdFtAY2F0ZWdvcnk9J2Nocm9tb3NvbWVfY291bnQnXSIpICU+JSB4bWxWYWx1ZSAlPiUgYXMuaW50ZWdlcgogICAgICBuLnNjYWZmb2xkID0gZ2V0Tm9kZVNldChmLCAiLy9TdGF0W0BjYXRlZ29yeT0nc2NhZmZvbGRfY291bnQnIGFuZCBAc2VxdWVuY2VfdGFnPSdhbGwnXSIpICU+JSB4bWxWYWx1ZSAlPiUgYXMuaW50ZWdlcgogICAgICBuLmNvbnRpZyA9IGdldE5vZGVTZXQoZiwgIi8vU3RhdFtAY2F0ZWdvcnk9J2NvbnRpZ19jb3VudCddIikgJT4lIHhtbFZhbHVlICU+JSBhcy5pbnRlZ2VyCiAgICAgIHNwcy5zdGF0IDwtIHNwcy5zdGF0ICU+JSAKICAgICAgICBhZGRfcm93KHNwZWNpZXMgPSBzLCBzdHJhaW4gPSBzcyRiaW9zb3VyY2UkaW5mcmFzcGVjaWVzbGlzdCRzdWJfdmFsdWUsIAogICAgICAgICAgICAgICAgYXNzZW1ibHlhY2Nlc3Npb24gPSBzcyRhc3NlbWJseWFjY2Vzc2lvbiwgYXNzZW1ibHluYW1lID0gc3MkYXNzZW1ibHluYW1lLCBhc3NlbWJseXN0YXR1cyA9IHNzJGFzc2VtYmx5c3RhdHVzLAogICAgICAgICAgICAgICAgbl9jaHIgPSBuLmNociwgbl9zY2FmZm9sZCA9IG4uc2NhZmZvbGQsIG5fY29udGlnID0gbi5jb250aWcsCiAgICAgICAgICAgICAgICBsYXN0dXBkYXRlID0gc3MkbGFzdHVwZGF0ZWRhdGUsIHN1Ym1pdHRlciA9IHNzJHN1Ym1pdHRlcm9yZ2FuaXphdGlvbiwgCiAgICAgICAgICAgICAgICByZWZzZXEgPSBzcyRyZWZzZXFfY2F0ZWdvcnksIGZ0cHJlcG9ydCA9IHNzJGZ0cHBhdGhfYXNzZW1ibHlfcnB0KQogICAgfQogIH0KfQoKc3BzLnN0YXQgPC0gc3BzLnN0YXQgJT4lIAogIG11dGF0ZShuX2NociA9IGlmZWxzZShuX2NociA8PSAxLCBOQSwgbl9jaHIpLAogICAgICAgICBuX3NjYWZmb2xkID0gaWZlbHNlKG5fc2NhZmZvbGQgPD0gMSwgTkEsIG5fc2NhZmZvbGQpLAogICAgICAgICBuX2NvbnRpZyA9IGlmZWxzZShuX2NvbnRpZyA8PSAwLCBOQSwgbl9jb250aWcpKQoKc3BzLnN0YXQgJT4lIGZpbHRlcihyZWZzZXEgIT0gIm5hIikgJT4lIHNlbGVjdCgtYXNzZW1ibHlhY2Nlc3Npb24sIC1hc3NlbWJseW5hbWUsIC1mdHByZXBvcnQsIC1yZWZzZXEpICU+JSBhcnJhbmdlKGFzc2VtYmx5c3RhdHVzKQpgYGAKCk5vdyB0aGF0IHdlIGhhdmUgZ290IHRoZSByZXByZXNlbnRhdGl2ZSBnZW5vbWUgYXNzZW1ibGllcywgd2UgY2FuIHVzZSB0aGUgZnRwcGF0aCB0byBkb3dubG9hZCB0aGUgYXNzZW1ibHkgaW5mb3JtYXRpb24uCmBgYHtyIGRvd25sb2FkX2Fzc2VtYmx5X2luZm8sIGV2YWw9RkFMU0V9CmZ0cHJlcG9ydCA8LSBzcHMuc3RhdCAlPiUgZmlsdGVyKHJlZnNlcSAhPSAibmEiKSAlPiUgcHVsbChmdHByZXBvcnQpCmZ0cGZlYXR1cmUgPC0gZ3N1YigiX2Fzc2VtYmx5X3JlcG9ydC50eHQiLCAiX2ZlYXR1cmVfdGFibGUudHh0Lmd6IiwgZnRwcmVwb3J0KQpmb3IoZiBpbiBjKGZ0cHJlcG9ydCwgZnRwZmVhdHVyZSkpewogIGRlc3RmaWxlID0gcGFzdGUwKCJkYXRhL2Fzc2VtYmx5LWluZm8vIixsYXN0KHN0cl9zcGxpdChmLCAiLyIpW1sxXV0pKQogICMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTQ5MDQ5ODMvaG93LWRvLWktY2hlY2stdGhlLWV4aXN0ZW5jZS1vZi1hLWRvd25sb2FkZWQtZmlsZQogIGlmKCFmaWxlLmV4aXN0cyhkZXN0ZmlsZSkpewogICAgdHJ5Q2F0Y2goZG93bmxvYWQuZmlsZShmLCBkZXN0ZmlsZSksIGVycm9yID0gZnVuY3Rpb24oZSkgMSkKICB9Cn0KYGBgCk5vdGUgdGhhdCB0aHJlZSBnZW5vbWVzIGRvIG5vdCBoYXZlIGZlYXR1cmUgdGFibGVzIGF0IHRoZSB0aW1lIG9mIGRvd25sb2FkaW5nICgyMDIxLTAyLTA0KS4gVGhleSBhcmU6IE5ha2FzZW9teWNlcyBkZWxwaGVuc2lzIENCUyAyMTcwLCBOYWthc2VvbXljZXMgYnJhY2FyZW5zaXMgQ0JTIDEwMTU0LCBOYWthc2VvbXljZXMgbml2YXJpZW5zaXMgQ0JTIDk5ODMuCgpJbnN0ZWFkIG9mIGxvYWRpbmcgYWxsIHRoZSB0YWJsZXMsIEkgZm91bmQgaXQgaXMgZWFzaWVyIHRvIGp1c3Qgc2VhcmNoIHRocm91Z2ggdGhlbSB1c2luZyBgZ3JlcGAuIFRoZSBpZGVhIGlzIHRvIGl0ZXJhdGUgdGhyb3VnaCB0aGUgYHJlZnNlcTJzW1syXV1gLCB3aGljaCBjb250YWlucyBhbGwgdGhlIHByb3RlaW5zIHdpdGggdW5kZXRlcm1pbmVkIGxvY2F0aW9ucy4gRm9yIGVhY2ggZW50cnksIHNlYXJjaCB0aGUgZ2VuZSBJRCBhZ2FpbnN0IHRoZSBmZWF0dXJlIHRhYmxlcyB1c2luZyBgemdyZXBgIGFuZCBjYXB0dXJlIHRoZSBvdXRwdXQuCmBgYHtyfQpzZWFyY2hfY2hyX2xvYyA8LSBmdW5jdGlvbihnZW5lKXsKICBjbWQgPC0gc3ByaW50ZigiemdyZXAgJXMgZGF0YS9hc3NlbWJseS1pbmZvLypmZWF0dXJlX3RhYmxlLnR4dC5neiB8IGdyZXAgJzpnZW5lJyIsIGdlbmUpCiAgcmVzIDwtIHN5c3RlbShjbWQsIGludGVybiA9IFRSVUUpCiAgaWYobGVuZ3RoKHJlcykgPiAxKXsKICAgIHN0b3Aoc3ByaW50ZignTW9yZSB0aGFuIG9uZSByZWNvcmQgZm91bmQgZm9yICIlcyInLCBnZW5lKSkKICB9ZWxzZSBpZihsZW5ndGgocmVzKSA9PSAwKXsKICAgIHJldHVybihOQSkKICB9CiAgZ0luZm8gPC0gc3RyX3NwbGl0KHJlcywgIlx0IilbWzFdXQogICMgMTogZmlsZSBuYW1lOyAzOiBhc3NlbWJseSBuYW1lOyA3OiAiY2hyb21vc29tZSIgbmFtZTsgODogc3RhcnQ7IDk6IGVuZDsgMTA6IHN0cmFuZDsgMTc6IGdlbmUgSUQ7IDE4OiBnZW5lIGxlbmd0aAogIGFzc2VtYmx5LmZpbGUgPC0gZ3N1YigiZmVhdHVyZV90YWJsZS50eHQuZ3o6Z2VuZSIsICJhc3NlbWJseV9yZXBvcnQudHh0IiwgZ0luZm9bMV0pCiAgcmVzMSA8LSBzeXN0ZW0ocGFzdGUoImdyZXAiLGdJbmZvWzddLGFzc2VtYmx5LmZpbGUpLCBpbnRlcm4gPSBUUlVFKQogIGlmKGxlbmd0aChyZXMxKSA+IDEpCiAgICBzdG9wKHNwcmludGYoJ01vcmUgdGhhbiBvbmUgcmVjb3JkIGZvdW5kIGZvciAiJXMiJywgZ2VuZVs3XSkpCiAgY2hyIDwtIHN0cl9zcGxpdChyZXMxLCAiXHQiKVtbMV1dCiAgIyAxOiBzZXFfbmFtZTsgNzogcmVmc2VxX2FjYzsgOTogbGVuZ3RoCiAgZmluYWwgPC0gdGliYmxlKGdlbmVfaWQgPSBnZW5lLCBhc3NlbWJseSA9IGdJbmZvWzNdLCBjaHJsb2MgPSBjaHJbMV0sIGNocmFjY3ZlciA9IGdJbmZvWzddLCAKICAgICAgICAgICAgICAgICAgY2hyc3RhcnQgPSBnSW5mb1s4XSwgY2hyc3RvcCA9IGdJbmZvWzldLCBjaHJMID0gY2hyWzldKQogIHJldHVybihmaW5hbCkKfQpgYGAKCmBgYHtyIHNlYXJjaF9yZWZzZXF9CnRtcDEgPC0gbWFwX2RmcihyZWZzZXEyc1tbMl1dJGdlbmVfaWQsIHNlYXJjaF9jaHJfbG9jKQp0bXAxIDwtIHRtcDEgJT4lIAogIG11dGF0ZShjaHJzdGFydCA9IGFzLmludGVnZXIoY2hyc3RhcnQpLCBjaHJzdG9wID0gYXMuaW50ZWdlcihjaHJzdG9wKSwgY2hyTCA9IGFzLmludGVnZXIoY2hyTCksCiAgICAgICAgIHJlbExvYyA9IHJvdW5kKGNocnN0YXJ0L2NockwsIDIpKQpgYGAKClVwZGF0ZSB0aGUgb3JpZ2luYWwgdGFibGUKYGBge3J9IApyZWZzZXEyc1tbMl1dIDwtIHJvd3NfdXBkYXRlKHJlZnNlcTJzW1syXV0sIHRtcDEsIGJ5ID0gImdlbmVfaWQiKQpyZWZzZXEzIDwtIGJpbmRfcm93cyhyZWZzZXEycykKYGBgCgo0LiBMZXQncyBkbyB0aGUgc2FtZSBzZWFyY2ggd2l0aCB0aGUgRnVuZ2lEQiBoaXRzLiBHUllDIGhhcyBubyBtYXRjaGVzIHNpbmNlIG5vbmUgb2YgdGhlIEdSWUMgc3RyYWlucyBhcmUgaW4gdGhlIE5DQkkgYXNzZW1ibHkgZGF0YWJhc2UuCmBgYHtyIHNlYXJjaF9mZGJfZ3J5Y30KdG1wMiA8LSBtYXBfZGZyKGZkYi5saXN0Wy0yXSwgc2VhcmNoX2Nocl9sb2MpICMgdGhlIEI5SjA4IHNlcXVlbmNlIGlzIGlnbm9yZWQgc2luY2UgaXRzIGdlbm9tZSBpcyBub3QgdGhlIHJlZnNlcSBhc3NlbWJseQp0bXAyIDwtIHRtcDIgJT4lIAogIHJlbmFtZShjaHJfbmFtZSA9IGNocmxvYykgJT4lIAogIG11dGF0ZShjaHJzdGFydCA9IGFzLmludGVnZXIoY2hyc3RhcnQpLCBjaHJzdG9wID0gYXMuaW50ZWdlcihjaHJzdG9wKSwgCiAgICAgICAgIGNockwgPSBhcy5pbnRlZ2VyKGNockwpLCByZWxMb2MgPSByb3VuZChjaHJzdGFydC9jaHJMLCAyKSkKYGBgCgo+IFRoZSBCOUowOCBzZXF1ZW5jZSBpcyB0aGUgbWlzc2luZyBob21vbG9nIGZyb20gQjExMjIxLiBBcyBzaG93biBpbiB0aGUgUkVBRE1FLm1kIG9mIHRoaXMgYmxhc3QgZm9sZGVyLCBJIGZvdW5kIHRoYXQgdGhlICJtaXNzaW5nIGhvbW9sb2ciIGlzIGFjdHVhbGx5IHByZXNlbnQsIGJ1dCB3YXMgYnJva2VuIGludG8gdHdvIGZyYWdtZW50cyBkdWUgdG8gaW5jb21wbGV0ZSBhc3NlbWJseS4gVGhlIEMtdGVybWludXMgb2YgdGhlIEI5SjA4IHNlcXVlbmNlIGFsaWducyB0byBhIGZyYWdtZW50IE5XXzAyMTY0MDE2NS4xLCB3aGljaCBjb3JyZXNwb25kcyB0byBzY2FmZm9sZDAwMDAxLiBJdHMgbGVuZ3RoIGlzIDQsMzAzLDM5NyBicCwgd2hpY2ggaXMgbmVhcmx5IHRoZSBzYW1lIGFzIHRoZSByZXBvcnRlZCBjaHJvbW9zb21lIDEgbGVuZ3RoIGZyb20gTXXDsW96IF9ldCBhbF8uIDIwMjEgKEZpZy4gMSkuIEkgdGhlcmVmb3JlIGJlbGlldmUgdGhhdCB0aGlzIGhvbW9sb2cgKEhpbDQpIGlzIGxvY2F0ZWQgYXQgdGhlIDMnIHRpcCBvZiBjaHJvbW9zb21lIDEgb2YgQjExMjIxLgoKNS4gRmluYWxseSwgd2UgY2FuIGNvbWJpbmUgdGhlIGFib3ZlIHJlc3VsdHMgd2l0aCB0aGUgbWFudWFsbHkgZW50ZXJlZCBHUllDIHJlc3VsdHMKYGBge3J9Cm5vbi5yZWZzZXEgPC0gcmVhZF90c3YoImRhdGEvR1JZQy9YUF8wMjg4ODkwMzNfaG9tb2xvZ3NfY2hyX2xvY19mdW5naWRiX2dyeWMudHh0IiwgY29tbWVudCA9ICIjIikgJT4lIAogIHJvd3NfdXBkYXRlKHRtcDIsIGJ5ID0gImdlbmVfaWQiKSAjIHVwZGF0ZSB0aGUgbGlzdCB3aXRoIHRoZSBzZWFyY2ggcmVzdWx0cyBhYm92ZSBmb3IgdGhlIGZ1bmdpZGIgb25lcwoKIyBkZWNpZGUgdGhlIHRpYmJsZSBzdHJ1Y3R1cmU6CiMgYWNjZXNzaW9uIChmb3IgcHJvdGVpbiBmcm9tIHNvdXJjZSk7IHNvdXJjZTogR1JZQy9GdW5naURCL1JlZnNlcTsgc3BlY2llczsgc3BlY2llc19pZDsgc3RyYWluCiMgYXNzZW1ibHk7IGFzc2VtYmx5c3RhdHVzOyBuX3NlcXM7IGdlbmVfaWQ7IGdlbmVfbmFtZTsgY2hyYWNjdmVyOyBjaHJfbmFtZTsgY2hyc3RhcnQsIGNocnN0b3A7CiMgY2hyTDsgcmVsTG9jCnJlZnNlcTQgPC0gcmVmc2VxMyAlPiUgCiAgcmVuYW1lKGdlbmVfaWQgPSBhbGlhcywgZ2VuZV9uYW1lID0gZ2VuZV9pZCwgY2hyX25hbWUgPSBjaHJsb2MpICU+JSAKICBsZWZ0X2pvaW4oc3BzLnN0YXQgJT4lIGZpbHRlcihyZWZzZXEgIT0gICJuYSIpICU+JSAKICAgICAgICAgICAgICBzZWxlY3Qob3JnYW5pc20gPSBzcGVjaWVzLCBzdHJhaW4sIGFzc2VtYmx5ID0gYXNzZW1ibHlhY2Nlc3Npb24sIGFzc2VtYmx5c3RhdHVzLCBzdGFydHNfd2l0aCgibl8iKSksCiAgICAgICAgICAgIGJ5ID0gYygib3JnYW5pc20iLCAiYXNzZW1ibHkiKSkgJT4lIAogIG11dGF0ZShzb3VyY2UgPSAiUmVmc2VxIiwgc3BlY2llc19pZCA9IHBhc3RlMChzdWJzdHIoc3BlY2llcywxLDEpLHdvcmQoc3BlY2llcywgMiwgMikpLCAKICAgICAgICAgIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8yODA3MDg3OC9yLXVzZS1taW4td2l0aGluLWRwbHlybXV0YXRlCiAgICAgICAgIG5fc2VxcyA9IHBtaW4obl9jaHIsIG5fc2NhZmZvbGQsIG5fY29udGlnLCBuYS5ybSA9IFQpKSAlPiUgCiAgc2VsZWN0KHNvdXJjZSwgYWNjZXNzaW9uLCBzcGVjaWVzLCBzcGVjaWVzX2lkLCBzdHJhaW4sIGFzc2VtYmx5LCBhc3NlbWJseXN0YXR1cywgbl9zZXFzLCBnZW5lX2lkLCBnZW5lX25hbWUsCiAgICAgICAgIGNocmFjY3ZlciwgY2hyX25hbWUsIGNocnN0YXJ0LCBjaHJzdG9wLCBjaHJMLCByZWxMb2MpCmFsbC5nZW5lcyA8LSBiaW5kX3Jvd3MocmVmc2VxNCwgbm9uLnJlZnNlcSkKd3JpdGVfdHN2KGFsbC5nZW5lcywgIm91dHB1dC9YUF8wMjg4ODkwMzNfaG9tb2xvZ3NfY2hyX2xvYy50c3YiKQpgYGAKCiMjIEV2YWx1YXRlIGUtdmFsdWUgY3V0b2ZmCgpKYW4gYXNrZWQgd2hldGhlciB0aGUgMWUtNSBjdXRvZmYgaXMgdG9vIGxpYmVyYWwgc3VjaCB0aGF0IHNvbWUgb2YgdGhlIGhpdHMgdGhhdCBqdXN0ICJtYWRlIHRoZSBjdXQiIG1heSBiZSBmYWxzZSBwb3NpdGl2ZXMuIEkgZGVjaWRlIHRvIGxvb2sgaW50byB0aGlzIGJ5IGhpZ2hsaWdodGluZyB0aG9zZSBzZXF1ZW5jZXMgaW4gdGhlIGFsaWdubWVudCwgYW5kIHRvIGV4YW1pbmUgdGhlaXIgZmVhdHVyZXMgc3VjaCBhcyBwcm90ZWluIGxlbmd0aCBhbmQgYW5ub3RhdGVkIGZ1bmN0aW9ucy4KYGBge3J9CiMgSWRlbnRpZnkgdGhlIHN1YnNldCB3aXRoIEUtdmFsdWUgYmV0d2VlbiAxZS01IHRvIDFlLTEwCnJlZnNlcS5oaWdoRSA8LSByZWZzZXExICU+JSBmaWx0ZXIoLWxvZzEwKGUudmFsdWUpIDwgMTApOyByZWZzZXEuaGlnaEUKZmRiLmhpZ2hFICAgIDwtIGZ1bmdpZGIgJT4lIGZpbHRlcihnZW5lX2lkICVpbiUgZmRiLmxpc3QsIC1sb2cxMChlX3ZhbHVlKSA8IDEwKTsgZmRiLmhpZ2hFCmdyeWMuaGlnaEUgICA8LSBncnljICU+JSBmaWx0ZXIoaWQgJWluJSBncnljMSRpZCwgLWxvZzEwKG1pbl9lX3ZhbHVlKSA8IDEwKTsgZ3J5Yy5oaWdoRQoKIyBnZXQgdGhlIHN1YnNldCBvZiBzZXF1ZW5jZXMKaGlnaEUuc2VxIDwtIGMocmVmc2VxZGIuc2VxW3JlZnNlcS5oaWdoRSRhY2Nlc3Npb25dLCBmdW5naWRiLnNlcVtmZGIuaGlnaEUkZ2VuZV9pZF0sCiAgICAgICAgICAgICAgIGdyeWMuc2VxW2dyeWMuaGlnaEUkb3JpX2lkXSkKaGlnaEUubmFtZXMgPC0gYyhwYXN0ZShyZWZzZXEuaGlnaEUkYWNjZXNzaW9uLCByZWZzZXEuaGlnaEUkc3BlY2llc19pZCwgc2VwID0gIl8iKSwKICAgICAgICAgICAgICAgICBwYXN0ZShmZGIuaGlnaEUkZ2VuZV9pZCwgZmRiLmhpZ2hFJHNwZWNpZXNfaWQsIHNlcCA9ICJfIiksCiAgICAgICAgICAgICAgICAgcGFzdGUoZ3J5Yy5oaWdoRSRvcmlfaWQsIGdyeWMuaGlnaEUkc3BlY2llc19pZCwgc2VwID0gIl8iKSkKd3JpdGUuZmFzdGEoaGlnaEUuc2VxLCBuYW1lcyA9IGhpZ2hFLm5hbWVzLCAKICAgICAgICAgICAgZmlsZS5vdXQgPSAib3V0cHV0L1hQXzAyODg4OTAzM19ob21vbG9nc19oaWdoX0V2YWx1ZS5mYXN0YSIpCmBgYAoKV2hlbiBJIHN1Ym1pdHRlZCB0aGUgYGxlbmd0aChoaWdoRS5uYW1lcylgIHNlcXVlbmNlcyB0byB0aGUgW0NERCBiYXRjaCBzZWFyY2ggc2l0ZV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9TdHJ1Y3R1cmUvYndycHNiL2J3cnBzYi5jZ2kpIGFuZCBkb3dubG9hZGVkIHRoZSByZXN1bHRzLiBCZWxvdyBpcyBhIHNjcmVlbnNob3Qgb2YgdGhlIGhpdHM6CgohW0NERCBoaXRzXShpbWcvMjAyMDA4MTYtaGlnaC1FdmFsdWUtQ0RELWhpdHMucG5nKQpXZSBjYW4gc2VlIHRoYXQgYWxsIG9mIHRoZW0gaGF2ZSB0aGUgSHlwaGFsbHkgcmVndWxhdGVkIGNlbGwgd2FsbCBwcm90ZWluIChIeXBoYWxfcmVnX0NXUCkgZG9tYWluLiBUaGVyZWZvcmUgSSBiZWxpZXZlIHRoZXkgYXJlIHRydWUgaG9tb2xvZ3MgdG8gWFBfMDI4ODg5MDMzLiBCZWxvdyBpcyB0aGUgbXVsdGlwbGUgc2VxdWVuY2UgYWxpZ25tZW50IGluIHdoaWNoIEkgbW92ZWQgYWxsIHRoZSBzZXF1ZW5jZXMgd2l0aCBlLXZhbHVlID4gMWUtMTAgdG8gdGhlIHRvcC4gV2UgY2FuIHNlZSB0aGF0IHdoaWxlIHRoZXkgYXJlIG1vcmUgZGl2ZXJnZW50IGZyb20gdGhlIHJlc3Qgb2YgdGhlIHNlcXVlbmNlcywgdGhlIGFsaWdubWVudCBhcHBlYXJzIHRvIGJlIGNyZWRpYmxlLgoKIVtzZXF1ZW5jZSBhbGlnbm1lbnRzIHdpdGggZS12YWx1ZSA+IDFlLTEwXShpbWcvMjAyMDA4MTUtZS12YWx1ZS1hYm92ZS0xRS0xMC1hbGlnbm1lbnQucG5nKQ==