suppressPackageStartupMessages(require(tidyverse))
suppressPackageStartupMessages(require(cowplot))
suppressPackageStartupMessages(require(ggvenn))
suppressPackageStartupMessages(require(seqinr))
# set NCBI Entrez API key
suppressPackageStartupMessages(require(rentrez))
set_entrez_key("f4f01c220f3a0a162bada5b0bd5d4b131908")

Background

This analysis is in response to reviewer 1’s questions:

  1. The submitted version used C. auris Hil1’s PF11765 domain as the query (bait). What if other homologs were used? Would the number and identity of the blast results change?
  2. Why remove species from the blast output?

To address the first problem, I did blastp with two additional queries: the PF11765 domains in C. albicans Hyr1 and C. glabrata Hyr1.

As for the second question, my response and solutions are to include any species that were among the Y1000 tree in Shen et al. 2018. The more complete set can be used for the family expansion analysis. I plan to keep the homologs analysis restricted to the 104 homologs, as adding more species are not expected to change the main conclusions.

Data

I performed blastp analysis using the three queries against either the refseq_prot, the new clustered nr databases or in FungiDB. For the former two (performed through web interface on NCBI website), I downloaded the results using the Request ID (RID) with the blast_formatter program and then used the same tool to reformat the output to contain the desired columns. For the FungiDB result, I tried downloading the results in ASN.1 format, but wasn’t able to use blast_formatter to reformat it. Therefore I downloaded the table as shown on the default result page.

# header for ncbi output
header <- c("query", "qcovs", "sid", "slen", "pident", "q.start", "q.end", "s.start", "s.end", "evalue", "staxid", "sscinames")
# refseq results
refseq <- read_tsv("data/20220406-expanded-PF11765-blastp-refseq.txt", comment = "#", col_types = cols(), col_names = header) %>% 
  mutate(species = word(sscinames, 1, 2) %>% gsub("\\[|\\]", "", x = .), strain = word(sscinames, 3, -1),
         pident = num(pident, digits = 1)) %>% 
  select(-sscinames) %>% 
  relocate(species:strain, .after = pident)
# nr results
nr <- read_tsv("data/20220406-expanded-PF11765-blastp-clusternr.txt", comment = "#", col_types = cols(), col_names = header) %>% 
  mutate(species = word(sscinames, 1, 2) %>% gsub("\\[|\\]", "", x = .), strain = word(sscinames, 3, -1),
         pident = num(pident, digits = 1)) %>% 
  select(-sscinames) %>% 
  relocate(species:strain, .after = pident)
# fungidb results
fdb <- read_csv("data/20220427-expanded-blast-fungidb.csv") %>% 
  rename(sid = Accession, evalue = `E-Value`, score = Score, alen = `Align Length`, qcovs = `Query Coverage`) %>% 
  mutate(
    species = word(Organism, 1, 2) %>% gsub("\\[|\\]", "", x = .), strain = word(Organism, 3, -1),
    query = word(Query, 1, 1),
    pident = num(Identity*100, digits = 1),
    qcovs = num(qcovs*100, digits = 0),
    evalue = signif(evalue, digits = 1),
    score = signif(score, digits = 3),
    .keep = "unused"
  ) %>% 
  # make spelling consistent with ncbi
  mutate(species = gsub("Candida haemulonis", "Candida haemuloni", species)) %>%
  select(query, qcovs, sid, pident, species, strain, score, evalue, `Rank Per Query`, `Rank Per Subject`)

Analysis

1. Overlap between the three queries?

What’s the extent of overlap between the blastp hits using the three different queries?

Hits with <50% query coverage are removed.

refseq %>% filter(qcovs < 50)

Venn diagram showing the extent of overlap and set specific hits:

set.ovlp <- refseq %>% 
  filter(qcovs >= 50) %>% 
  group_by(sid) %>% 
  summarize(
    Caur = any(query == "Caur_Hil1"),
    Calb = any(query == "Calb_Hyr1"),
    Cgla = any(query == "Cgla_Hyr1")
  )
ggvenn(set.ovlp)

Which genes are found by the C. albicans and/or C. glabrata Hyr1 query blast but not by the C. auris Hil1 query blast?

Calb.not.Caur.hits <- set.ovlp %>% filter(!Caur) %>% pull(sid)
refseq %>% filter(sid %in% Calb.not.Caur.hits) %>% 
  group_by(sid) %>% filter(evalue == min(evalue)) %>% ungroup() %>% 
  select(query, qcovs, sid, slen, pident, evalue, species) %>% 
  mutate(species = paste(str_sub(species, 1, 1), word(species, 2, 2), sep = ". ")) %>% 
  arrange(species)
  • The majority of the combined set is already in the subset identified using C. auris Hil1 as the query.
  • Of the ones that are specific to the C. albicans Hyr1 query, I found the three C. parasilosis hits are already included in the 104 homologs via the FungiDB blast. Whatever the reason that exluded them from the ncbi blastp search using C. auris Hil1 domain as the query, they are already in our dataset.
  • Same is true for XP_452176.1 from K. lactis, this time through the GRYC search.

Among the species included previously, how many of the “new” sequences were not part of the 104 homologs list?

sid gid species in_early_verion source
XP_002999585 CAGL0L00227g C. glabrata Yes GRYC
XP_002615218 CLUG_05233 C. lusitaniae Yes FungiDB
XP_002620045 CLUG_01204 C. lusitaniae No^ NA
XP_002614121 CLUG_05607 C. lusitaniae No NA
XP_036665262 CPAR2_806390 C. parapsilosis Yes FungiDB
XP_036662815 CPAR2_600430 C. parapsilosis Yes FungiDB
XP_036665263 CPAR2_806400 C. parapsilosis No NA
XP_452176 KLLA0_B14498g K. lactis Yes GRYC
XP_001527307 LELG_02136 L. elongisporus No NA
XP_001385683 PICST_3402 S. stipitis No^ NA
XP_001383953 PICST_31095 S. stipitis Yes* NCBI

*this hit was originally included when the N560 aa was used as query. In later analysis where N360 aa (of C. auris Hil1) was used as query, this hit dropped off the list due to E-value being above the 1e-5 cutoff. So it is a legit, but borderline hit.

^shorter than 500 a.a.

Five hits are new, among which two are shorter than 500 a.a. and would have been excluded from the list (although they were not identified at all with the C. auris query).

2. Species to include

The new criteria is whether the species is among the 332 included in the large-scale phylogeny of Shen et al. 2018. I downloaded the tree from https://github.com/JLSteenwyk/treehouse/blob/master/Data/Saccharomycotina_fig2_Shen_etal_2018.tre and manually converted it into a list of species names.

shen2018 <- scan("../../data/yeast-phylogeny/Saccharomycotina_fig2_Shen_etal_2018.txt", what = "character", sep = "\n")

Any species in the refseq hit list not in the Shen 2018 phylogeny?

setdiff(unique(refseq$species), shen2018)
 [1] "Candida haemuloni"                "Candida duobushaemulonis"         "Candida pseudohaemulonii"        
 [4] "Suhomyces tanzawaensis"           "Pseudomonas syringae;Pseudomonas" "Diutina rugosa"                  
 [7] "Yamadazyma tenuis"                "Schizosaccharomyces cryophilus"   "Kazachstania barnettii"          
[10] "Artibeus jamaicensis"            
refseq_name taxid in_Shen_2018 shen2018_name
Candida haemuloni 45357 No NA
Candida duobushaemulonis 1231522 No NA
Candida pseudohaemulonii 418784 No NA
Suhomyces tanzawaensis 984487 Yes Candida tanzawaensis
Pseudomonas syringae:Pseudomonas 317;59510 No NA
Diutina rugosa 5481 No NA
Yamadazyma tenuis 590646 Yes Candida tenuis
Schizosaccharomyces cryophilus 653667 No NA
Kazachstania barnettii 61262 No NA
Artibeus jamaicensis 9417 No NA

Manually figure out species synonyms and record both names for the species to use. update: downloaded the species name convertion table from Shen et al 2018 supplementary material.

spsNameConvert <- read_tsv("../../data/yeast-phylogeny/343taxa_speicies-name_clade-name_color-code.txt", col_types = cols()) %>% 
  mutate(species = word(`Species name`, 1, 2))
spsNameSyn <- tibble(
  ncbi.name = c(intersect(refseq$species, shen2018), "Suhomyces tanzawaensis", "Yamadazyma tenuis", "Lachancea kluyveri"),
  shen.name = c(intersect(refseq$species, shen2018), "Candida tanzawaensis", "Candida tenuis", "Lachancea kluyveri")
)

3. Check protein completeness

Our goal is to create a table documenting the number of putative homologs per species. We used to apply a length cutoff of 500 aa. However, the specific value of the cutoff is not based on any comprehensive studies. This combined with the uncertainty in some putative homolog’s length due to assembly issues led us to reconsider this filter. The solution we came up with is to include all blast hits based on the PF11765 domain alone in the family expansion analysis (CAFE and phylogenetic reconstruction). Later we will selectively include some of them for the protein feature analysis. Since the latter analysis is mainly to demonstrate the fast evolution of the central domain features, it doesn’t rely on the completeness of the Hil family or the inclusion of all species for which we have records for.

In curating the blast hits, we found that some (protein) blast hits are annotated as missing the carboxy terminus. This would prevent us from properly assessing the length of the protein. Note that “incomplete” means different things for the protein vs mRNA record. For the latter, “incomplete” could mean missing 5’ and/or 3’UTR while the CDS may be complete. A large number of the mRNA records corresponding to the protein hits in the list are “partial”. That’s not reason for exclusion.

For C. tropicalis, I found a new, 2019 assembly that uses both PacBio and Illumina. The assembly did use the reference genome assembly as a guide.

To identify all partial mRNA, we will use the rentrez package to query the ncbi database.

# define a function for retrieving the mRNA information from the nucleotide databased, based on protein ID
retrieve_mrna_info <- function(ids){
  nuc.lnk <- entrez_link(dbfrom = "protein", db = "nucleotide", id = ids, by_id = TRUE)
  nucID <- sapply(nuc.lnk, function(x){
    lnk = x$links$protein_nuccore_mrna
    return(ifelse(is.null(lnk), NA, lnk))
  })
  nucSum <- entrez_summary(db = "nuccore", id = na.omit(nucID))
  names(nucSum) <- ids[!is.na(nucID)]
  return(nucSum)
}

retrieve_protein_info <- function(ids){
  pSum <- entrez_summary(db = "protein", id = ids)
  names(pSum) <- ids
  return(pSum)
}
# all refseq protein IDs to get the mRNA information for
pid <- unique(refseq$sid)
# posting too many queries can hit the rate-limit. split into multiples of 50s
iter.pid <- split(x = pid, f = ceiling(seq_along(pid)/50))
pSum <- sapply(iter.pid, retrieve_protein_info)
nucSum <- sapply(iter.pid, retrieve_mrna_info)
# extract the mRNA info
mrnaInfo <- lapply(nucSum, function(x) {
  extract_from_esummary(x, elements = c("caption", "completeness"), simplify = FALSE) %>% 
    bind_rows(.id = "sid") %>% 
    rename(tID = caption, tComplete = completeness)
}) %>% bind_rows()
# extract the protein completeness info
pInfo <- lapply(pSum, function(x) {
  extract_from_esummary(x, elements = c("completeness"), simplify = FALSE) %>% 
    bind_rows(.id = "sid") %>% 
    rename(pComplete = completeness)
}) %>% bind_rows()

Now remove duplicates (resulting from the different queries) and filter by species (those that overlap the Shen 2018 list plus the MDR clade)

sps.include <- c(spsNameSyn$ncbi.name, "Candida haemuloni", "Candida duobushaemulonis", "Candida pseudohaemulonii")
dedup.ref <- refseq %>% 
  filter(species %in% sps.include, qcovs >= 50) %>% 
  group_by(sid) %>% 
  filter(evalue == min(evalue)) %>% 
  left_join(mrnaInfo, by = c("sid" = "sid")) %>% 
  left_join(pInfo, by = c("sid" = "sid")) %>% 
  select(query, sid, slen, pComplete, tID, tComplete, species, strain, everything())

Summarize the deduplicated list - for each species, record the total number of hits, those that are labeled as incomplete in the protein record, those that are above 500 aa and the number of hits below 500 aa that are labeled as incomplete

sum.ref <- dedup.ref %>% 
  group_by(species, strain) %>% 
  summarize(
    total = n(),
    incompl = sum(pComplete != ""),
    gt500 = sum(slen >= 500),
    shortIncompl = sum(pComplete != "" & slen < 500)
  )
`summarise()` has grouped output by 'species'. You can override using the `.groups` argument.
sum.ref %>% arrange(desc(shortIncompl))
  • S. stipitis and M. bicuspidata had a large number of incomplete proteins, likely a reflection of the genome assembly status. For M. bicuspidata, a newer Illumina + Oxford Nanopore assembly is available for this species but is only assembled to the contig level, with no gene annotation. I tried blasting the refseq hits from the species against the new assembly but the match was poor for most of the hits - instead of getting a long, contiguous HSP, I got mostly broken pieces with lots of mismatches. I suspect this is due to a combination of tandem gene arrays causing difficulty for assembly around the Hil homologs loci and the sequencing depth and assembly itself. Based on these observations, I plan to include the species in the CAFE analysis and potentially in the phylogenetic reconstruction as well, but exclude it from the homolog protein trait analyses.
  • For S. stipitis, I identified a newer assembly for a different strain (NRRL Y-7124) and performed local blast against the annotated protein sequences. I identified a total of 13 hits, of which seven are above the 50% query coverage threshold. This is quite different from the 18 hits identified in the refseq assembly. When I blasted the refseq hits against the new assembly, some of them had poor matches. Without further investigation, it is not clear what is the cause of the discrepancy. I plan to use the new assembly hits for downstream analyses given that it is more recently sequenced.
  • For the same reasons as above, I plan to include Y. tenuis, S. tanzawaensis and C. jadinii in the CAFE analysis but exclude them from the protein feature analyses. Given that they have relatively few hits, I could consider manually searching for the full length ORF, but that’s optional.
  • For D. fabryi, the Y-1000 plus project has sequenced another strain from this species. I blasted against that assembly and identified two matches. But the two hits appear too similar to each other, leaving me wonder if they are truly distinct loci, or are they partially duplicate contigs.

Hits based on the new S. stipitis assembly

s.stipitis <- read_tsv("../../data/long-read-assemblies/s_stipitis_NRRL_Y7124/S-stipitis-NRRL-Y7124-Hil-homologs-blast.txt",
                       comment = "#", col_names = header[1:10], col_types = cols()) %>% 
  mutate(species = "Scheffersomyces stipitis", strain = "NRRL Y-7124", staxid = "913138",
         pident = num(pident, digits = 1)) %>% 
  relocate(species:strain, .after = pident) %>% 
  group_by(sid) %>% 
  filter(evalue == min(evalue))

4. Candida glabrata hits

The Cormack lab released new assemblies for a couple of C. glabrata strains in Xu 2020 and Xu 2021 (PMIDs: 32068314, 33713372). These assemblies were based on long-red sequencing technologies supplemented with short reads for polishing the sequences. They also paid special attention to the subtelomeres, using various means to verify that their newly assembled subtelomeric regions are correct. Performing blastp searches against these assemblies (I tested two of them: ATTC2001=CBS138 and BG2) recovered 13 instead of 3 hits (in the new refseq hit list). I verified that all of them are credible. Actually, Xu et al. 2020 supplementary table specifically listed these 13 as predicted adhesins belonging to cluster V, which also included Awp2 studied by Reithofer et al 2021. Including them in the homologs list would properly reflect the family size in the species, but will also highlight the issue that incompletely assembled subtelomeres may have led to many false negatives in the gene family inventory. I think it is worth including it and commenting on the caveats.

c.glabrata <- read_tsv("../../data/long-read-assemblies/c_glabrata_CBS138/C-glabrata-CBS138-Hil-homologs-blast.txt",
                       comment = "#", col_names = header[1:10], col_types = cols()) %>% 
  mutate(species = "Candida glabrata", strain = "CBS 138", staxid = "5478",
         pident = num(pident, digits = 1)) %>% 
  relocate(species:strain, .after = pident) %>% 
  group_by(sid) %>% 
  filter(evalue == min(evalue))# %>% 
  #rename(pid = sid)

c.glabrata

Note that I also checked three other species for which I can find a recent assembly that used long-read technlogoies. None of them led to so many more hits as the C. glabrata assembly did. In fact, only the C. nivariensis assembly yielded one more hit than the older, short-read based assembly. There may be two possibilities: 1) not all new assemblies are created equal. there are some special properties of the C. glabrata assembly that made its subtelomeric regions especially well assembled; 2) there is something special about C. glabrata in that the Hil family expanded more significantly than even the closely related Nakaseomyces species. As of now we cannot distinguish between these two.

5. FungiDB hits

FungiDB doesn’t include any species not already in the refseq database. The hits that were added to the refseq hits from the earlier analysis are

prev.fdb.add <- tribble(
  ~gid, ~pid, ~species,
  "CLUG_05233", "XP_002615218.1", "Clavispora lusitaniae",
  "CPAR2_600430", "XP_036662815.1", "Candida parapsilosis",
  "CPAR2_806390", "XP_036665262.1", "Candida parapsilosis",
  "CPAR2_806420", "XP_036665265.1", "Candida parapsilosis",
  "B9J08_004098", "PIS52481.1", "Candida auris"
)
prev.fdb.add %>% filter(!pid %in% dedup.ref$sid)

The first four are not missing, just a difference in the ID format. The missing C. auris sequence is known to us and will be added regardless. Based on the above, we will no longer consult the FungiDB to supplement the refseq hits.

c.auris <- read_tsv("../../output/XP_028889033-Cauris-five-strains-blast.txt",
                    comment = "#", col_types = cols()) %>% 
  filter(qseqid == "XP_028889033.1_PF11765", sseqid == "PIS52481.1") %>% 
  rename(sid = sseqid) %>% 
  mutate(
    pid = sid,
    query = "Caur_Hil1", 
    # the blast result from the above file was done using the first 325 amino acids as query
    # I manually did two sequence blast with the PF11765 from Hil1 as the query and entered 
    # the query and subject start and end positions below
    q.start = 12, q.end = 326, s.start = 11, s.end = 325,
    qcovs = (q.end - q.start + 1)/325*100,
    slen = 1247,
    species = "Candida auris",
    staxid = "498019"
  ) %>% 
  select(query, sid, slen, species, qcovs, pident, q.start, q.end, s.start, s.end, evalue, staxid, pid)

I now consider the refseq list complete, we will gather the final list of IDs and retrieve their sequences.

Retrieve protein info (no mRNA information is available)

id.add <- c(c.glabrata$sid, s.stipitis$sid, c.auris$sid)
pSum1 <- retrieve_protein_info(id.add)
# extract the protein completeness info
pInfo1 <- extract_from_esummary(pSum1, elements = c("completeness"), simplify = FALSE) %>% 
    bind_rows(.id = "sid") %>% 
    rename(pComplete = completeness)

Replace C. glabrata and S. stipitis in refseq hit list. Add the C. auris Hil4 from B8441.

dedup.ref.cg <- dedup.ref %>% 
  filter(!species %in% c("Candida glabrata", "Scheffersomyces stipitis")) %>% 
  bind_rows(filter(s.stipitis, qcovs >= 50)) %>% 
  bind_rows(filter(c.glabrata, qcovs >= 50)) %>% 
  bind_rows(filter(c.auris, qcovs >= 50)) %>% 
  rows_update(pInfo1, by = "sid", unmatched = "ignore") %>% 
  mutate(pid = gsub(".*\\|(.*)\\|.*", "\\1", sid))

Retrieve sequences

iter.pid1 <- split(x = dedup.ref.cg$pid, f = ceiling(seq_along(dedup.ref.cg$pid)/50))
ref.seq <- lapply(iter.pid1, function(x){
  entrez_fetch(db = "protein", id = x, rettype = "fasta")
})
# merge all the sequences and write to file
ref.seq %>% paste0(collapse = "") %>% 
  write(file = "../../output/20220503-expanded-blast-refseq-homologs.faa")

6. GRYC hits

GRYC did include a few more species, especially in the Nakaseomyces and Lachancea genera. While the same assembly data appear to exist in the ncbi database, they are certainly not included in the refseq database. Moreover, even when the assemblies are present in the ncbi database, there are only the genomic sequences, not translated CDS, which likely make them unavailable to blastp searches. Therefore I downloaded the five non-glabrata Nakaseomyces genomes plus one Lachancea species, L. kluyveri and performed local tblastn searches. Below I will process the results and decide which ones to include in the final list.

gryc.sps <- tribble(
  ~sps.abr, ~species, ~strain, ~staxid,
  "CANI", "Candida nivariensis", "CBS 9983", "1279115",
  "NADE", "Nakaseomyces delphensis", "CBS 2170", "1279113",
  "NABA", "Nakaseomyces bacillisporus", "CBS 7720", "1279116",
  "CACA", "Candida castellii", "CBS 4332", "1279086",
  "CABR", "Candida bracarensis", "CBS 10154", "1279114",
  "SAKL", "Lachancea kluyveri", "CBS 3082", "4934"
)

gryc <- read_tsv("../../data/Nakaseomyces_GRYC/Nakaseomyces-Hil-homologs-blast.txt",
                       comment = "#", col_names = header[1:10], col_types = cols()) %>% 
  mutate(sps.abr = gsub("BN[0-9]*_", "", sid) %>% str_sub(1,4), pident = num(pident, digits = 1)) %>% 
  left_join(gryc.sps, by = "sps.abr") %>% 
  relocate(species:strain, .after = pident) %>% 
  select(-sps.abr)

dedup.gryc <- gryc %>% 
  group_by(sid) %>% 
  filter(evalue == min(evalue)) %>% 
  mutate(pid = gsub("BN[0-9]*_", "", sid) %>% gsub("1_1$", "", .)) %>% 
  filter(qcovs >= 50) %>% 
  ungroup()

Extract the protein sequences from the combined fasta

cat(dedup.gryc$sid, file = "20220506-gryc-deduplicated-id-for-extracting-fasta.txt", sep = "\n")

Run the following at the command line assuming the current working directory is 02-blast

python3 script/extract_fasta_gz.py \
  data/Nakaseomyces_GRYC/20220503-gryc-nakaseomyces-protein.faa.gz \
  analysis/expanded-blast/20220506-gryc-deduplicated-id-for-extracting-fasta.txt \
  output/20220506-expanded-blast-gryc-homologs.faa

Merge the GRYC sequences with the refseq sequences - run the following at the command line

cat 20220503-expanded-blast-refseq-homologs.faa \
  20220506-expanded-blast-gryc-homologs.faa \
  > 20220506-expanded-blast-combined-homologs.faa

Check to make sure that no duplicates exist.

bioawk -c fastx '{print $name}' 20220506-expanded-blast-combined-homologs.faa | sort | uniq -d

If there is no output, then there is no duplicates. The command will output one line per sequence that occcurs more than once in the file.

7. Merge all the sequences

Export the combined homologs list

dedup.merge <- bind_rows(
  RefSeq = dedup.ref.cg,
  GRYC = dedup.gryc,
  .id = "source"
  ) %>% 
  mutate(name = paste(pid, gsub(" ", "_", species), sep = "_")) %>% 
  select(-q.start, -q.end, -s.start, -s.end) %>% 
  ungroup()
write_tsv(dedup.merge, file = "../../output/20220506-expanded-blast-combined-homologs.tsv")

8. Compare with the former 104 list

Hits in the previous refseq blast list but not in the current one:

p104 <- read_tsv("../../output/XP_028889033_homologs_combine_blast_stat.tsv", col_types = cols())
ggvenn(list(expanded_blast = dedup.merge$pid, previous_104 = p104$id))

p104 %>% filter(!id %in% gsub("BN[0-9]*_", "", dedup.merge$pid)) %>% arrange(species)
  • The four S. stipitis sequences have been replaced by the new assembly
  • The C. auris, C. glabrata, C. parasilosis and Clavispora lusitaniae sequences are in fact all present in the new list, just under different IDs

9. Identify the PF11765 domain

Use HMMER to identify the location of the PF11765 domain. The hmm model was downloaded from the pfam database on 2022-05-04. Run the following command in 02-blast directory.

hmmscan --noali --domE 1e-3 \
  -o 20220504-hmmscan.log \
  --domtblout output/20220506-expanded-blast-combined-PF11765-hmmscan.txt \
  data/HMM-profile/Hyphal_reg_CWP.hmm \
  output/20220506-expanded-blast-combined-homologs.faa

Read the result

hmm.header <- c("target", "tacc", "tlen", "pid", "pacc", "slen", "full.evalue", "full.score", "full.bias",
                "num", "of", "c.evalue", "i.evalue", "domain.score", "domain.bias",
                "hmm.from", "hmm.to", "ali.from", "ali.to", "env.from", "env.to", "acc")
hmm <- read_table("../../output/20220506-expanded-blast-combined-PF11765-hmmscan.txt", 
                  comment = "#", col_names = hmm.header, col_types = cols()) %>% 
  mutate(sid = pid, pid = gsub("BN[0-9]*_", "", pid) %>% gsub("1_1$", "", .)) %>% 
  select(sid, pid, num, of, i.evalue:env.to)
dedup.merge %>% filter(!pid %in% hmm$pid)

Curious why hmmscan didn’t identify the PF11765 domain in the two sequences, I submitted the same sequence file to Batch CD-search, which uses the RPS-BLAST to search a collection of protein sequences for matches to a set of profile matrices. It is the reverse of the PSI-BLAST.

cdsearch <- read_tsv("../../output/20220509-expanded-blast-combined-pfam-cd-search-output.txt", skip = 7,
                     col_types = cols()) %>% 
  filter(grepl("Hyphal", `Short name`)) %>%
  mutate(sid = str_match(Query, ">([a-zA-Z0-9_.]*) ")[,2],
         pid = gsub("BN[0-9]*_", "", sid) %>% gsub("1_1$", "", .)) %>% 
  select(sid, pid, everything(), -Query)
cdsearch %>% filter(pid %in% setdiff(dedup.merge$pid, hmm$pid)) %>% select(-sid)

Both sequences did have a match, although one of them has a “superfamily” level match, suggesting that no hits exceed the domain level E-value, but multiple hits met the “non-specific” criteria. Not sure why hmmscan didn’t pick up the domain in the C. glabrata sequence though

Merge the CD-Search and HMMSCAM results into one. I’m using the envelope coordinates from hmmscan rather than the alignment coordinates. According to PFam documentation, the envelope coordinates reflect the region where the match has been probabilistically determined to lie. It will be slightly wider than the alignment.

hmm.cd <- cdsearch %>% filter(grepl("Hyphal_reg_CWP", `Short name`)) %>% 
  select(sid, pid, cd.type = `Hit type`, cd.incom = Incomplete, cd.from = From, cd.to = To) %>% 
  full_join(select(hmm, sid, pid, ali.from, ali.to, env.from, env.to)) %>% 
  mutate(cd.len = cd.to - cd.from + 1,
         ali.len = ali.to - ali.from + 1,
         env.len = env.to - env.from + 1)
Joining, by = c("sid", "pid")
hmm.cd %>% group_by(pid) %>% filter(n() > 1) %>% select(-sid, -ends_with("to"))
  • Above are the sequences with more than one entries from either search. Note that with the parameters used for CD-Search, it appears that the algorithm only reports the strongest match (specific). This can probably be changed by switching to the full report mode. But it’s not important as I plan to use the HMM envelope coordinates whenever available.
  • The second match in XP_003671892.1 is very short. Remove it.
  • The second match in NADE0s03e00110g largely overlaps with the first one. Remove.
domain.final <- hmm.cd %>% 
  # remove the short hit and duplicates due to the join operation, but save the rows where hmmscan didn't find a match
  filter(is.na(ali.from) | (abs(ali.from - cd.from) < 1000 & ali.len > 100)) %>% 
  filter(!(pid == "NADE0s03e00110g" & ali.from == 39)) # remove the overlapping entry
domain.final %>% 
  pivot_longer(c(cd.len, env.len), values_to = "length", names_to = "method") %>%
  mutate(method = factor(method, levels = c("cd.len", "env.len"), labels = c("CD-Search hits", "HMMSCAN hits"))) %>% 
  ggplot(aes(x = length)) + 
  geom_histogram(aes(fill = cd.incom != "-"), bins = 30) + 
  geom_vline(xintercept = 328, linetype = 2) +
  scale_fill_brewer("Incomplete", type = "qual") +
  facet_wrap(~method, nrow = 2, ) + theme_cowplot()
Warning: Removed 2 rows containing non-finite values (stat_bin).

Some are incomplete matches.

Write the coordinates into a BED formatted file for extracting the sequences.

domain.final %>% 
  mutate(
    # if hmm hits are not available, use cd-search hit coordinates, with 3 bp expansion on both sides
    # note that BED format is 0-based for the start position
    from = ifelse(is.na(env.from), cd.from - 3 - 1, env.from - 1), 
    to = ifelse(is.na(env.to), cd.to + 3, env.to)) %>% 
  select(seq = sid, from, to) %>% 
  # the following statement is to remove the second PF11765 domain from one protein for the purpose of gene tree
  group_by(seq) %>% 
  filter(from == min(from)) %>% 
  write_tsv(file = "../../output/20220512-expanded-blast-combined-homologs-PF11765.BED", col_names = FALSE)

Also write a version with the long name and not removing the second domain from the one sequence, for the purpose of plotting and calculating sequence length - PF11765

domain.final %>% 
  mutate(
    from = ifelse(is.na(env.from), cd.from - 3 - 1, env.from - 1), 
    to = ifelse(is.na(env.to), cd.to + 3, env.to)) %>% 
  select(seq = pid, from, to) %>% # select pid for merging with dedup.merge
  left_join(select(dedup.merge, seq = pid, name), by = "seq") %>% 
  ungroup %>% select(name, from, to) %>% 
  write_tsv(file = "../../output/20220623-expanded-blast-combined-homologs-PF11765-longname.BED", col_names = FALSE)

To extract the sequence matches to the PF11765 profile, execute the following shell command

seqtk subseq 20220506-expanded-blast-combined-homologs.faa \
             20220512-expanded-blast-combined-homologs-PF11765.BED  > \
             20220512-expanded-blast-combined-homologs-PF11765.faa

10. Rename the sequences for gene tree reconstruction

To facilitate gene tree reconstruction, rename the sequence names in the form of ID_species_name, and get rid of the description

renamer <- dedup.merge %>% 
  # a bit complicated: in the sequence file, sequence names from Refseq sequences were modified 
  # while sequences from GRYC were not
  mutate(id = ifelse(source == "RefSeq", pid, sid)) %>% 
  select(id, name) %>% 
  deframe()
myFastaRename <- function(file, out){
  input <- read.fasta(file, seqtype = "AA", as.string = TRUE)
  if(any(grepl(":", names(input)))){
    # deal with the seqtk output
    names(input) <- gsub(":.*$", "", names(input))
  }
  if(!all(names(input) %in% names(renamer))){
    warning("The following sequences were not found in the rename list:")
    print(setdiff(names(input), names(renamer)))
  }
  write.fasta(input, names = renamer[names(input)], file.out = out, nbchar = 5000)
}
myFastaRename(file = "../../output/20220506-expanded-blast-combined-homologs.faa", 
              out = "../../output/20220506-expanded-blast-combined-homologs-longname.faa")
myFastaRename(file = "../../output/20220512-expanded-blast-combined-homologs-PF11765.faa", 
              out = "../../output/20220512-expanded-blast-combined-homologs-PF11765-longname.faa")

11. Obtain subtrees for the species included

This is for the latter gene tree reconcilation and plotting.

Output the species used as a single column text file:

# after adding the GRYC species, we need to expand the sps.include list
# add S. cerevisiae for species tree illustrations
sps.final <- c(unique(dedup.merge$species), "Saccharomyces cerevisiae")
# convert the species names to the Shen 2018 name
sps.final.shen <- spsNameConvert %>% 
  mutate(ncbi.name = species,
         shen2018 = word(gsub("_", " ", old_speceis_names), -2, -1)) %>% 
  select(ncbi.name, shen2018, `Major clade`, Family, Genus) %>% 
  filter(ncbi.name %in% sps.final)
cat("The following species are not present in the Shen 2018 list and need to be added later.")
The following species are not present in the Shen 2018 list and need to be added later.
setdiff(sps.final, spsNameConvert$species)
[1] "Candida haemuloni"        "Candida duobushaemulonis" "Candida pseudohaemulonii"
sps.final.shen$shen2018 %>% na.omit() %>% gsub(" ", "_", .) %>%
  write(file = paste0("data/", gsub("-", "", Sys.Date()), "-expanded-blast-species-list.txt"))

Get the species tree using TreeHouse

# first install shiny and phytools package
shiny::runGitHub("treehouse", "JLSteenwyk")

I then manually added the missing species by editing the Newick file (without branch length version). The final result is shown below: species tree

Also export the gene-to-species mapping for generax

gene2sps.map <- dedup.merge %>% 
  select(name, species) %>% 
  # modify the two species names that differ between the gene names and species tree
  mutate(treeName = gsub(" ", "_", species))#treeName = case_when(
    #species == "Yamadazyma tenuis" ~ "Candida tenuis",
    #species == "Suhomyces tanzawaensis" ~ "Candida tanzawaensis",
    #TRUE ~ species),
    #treeName = gsub(" ", "_", treeName))
    
gene2sps.map %>% 
  select(name, treeName) %>% 
  write_tsv("data/20220516-expanded-blast-genes-to-species-map.tsv", col_names = FALSE)

Append the species list with additional information

sps.patho <- read_tsv("data/20220518-expanded-blast-species-pathogen-info.txt", col_types = cols())
Warning: One or more parsing issues, see `problems()` for details
sps.summary <- dedup.merge %>% 
  group_by(species, staxid) %>% 
  summarize(
    total = n(),
    short = sum(slen < 500),
    shortIncompl = ifelse(
      any(is.na(pComplete)), NA,
      sum(pComplete %in% c("no-ends", "no-left", "no-right") & slen < 500)
    ),
    .groups = "drop"
  ) %>% 
  add_row(species = "Saccharomyces cerevisiae",
          staxid = "559292",
          total = 0, short = 0, shortIncompl = NA) %>% 
  left_join(sps.patho, by = "species") %>% 
  arrange(desc(total))
sps.summary %>% select(-staxid)

Query the taxonomy database for lineage information for each species

taxon <- entrez_fetch(db = "taxonomy", id = sps.summary$staxid, rettype = "xml") %>% 
  XML::xmlToList()
names(taxon) <- sps.summary$species

Parse the taxonomy information

sps.lin <- lapply(taxon, function(x) {
  bind_rows(x$LineageEx) %>% tail(-10) %>% filter(Rank != "species")
  }) %>% 
  bind_rows(.id = "species")
write_tsv(sps.lin, "data/20220518-expanded-blast-species-taxonomy.tsv")

Write species info to file

spsInfo <- sps.lin %>% 
  filter(Rank == "family") %>% 
  select(species, family = ScientificName) %>% 
  mutate(treeName = gsub(" ", "_", species)) %>% 
    #treeName = case_when(
    #species == "Yamadazyma tenuis" ~ "Candida tenuis",
    #species == "Suhomyces tanzawaensis" ~ "Candida tanzawaensis",
    #TRUE ~ species),
    #treeName = gsub(" ", "_", treeName)) %>% 
  right_join(sps.summary, by = "species")

write_tsv(spsInfo, file = "data/20220518-expanded-blast-species-info.tsv")

12. Genome assembly info

Use species taxid to fetch the links to the taxonomy database. Note that some of the taxids are for specific strains while others are at the species level. As a result, there may be different number of assemblies being fetched.

staxid <- sps.summary %>% select(species, staxid) %>% deframe()
staxid["Scheffersomyces stipitis"] <- "4924" # use the species general ID
assembly.lnk <- entrez_link(dbfrom = "taxonomy", db = "assembly", id = staxid, by_id = TRUE)
assembly.lnk <- lapply(assembly.lnk, function(x) x$links$taxonomy_assembly)
names(assembly.lnk) <- sps.summary$species
assembly.lnk["Lachancea kluyveri"] <- "9527871"

Get the assembly summary info from ncbi

assembly.info <- lapply(assembly.lnk, function(i){
  if(!is_empty(i))
    entrez_summary(db = "assembly", id = i)
})

Extract the information into a tibble

fields <- c("assemblyaccession", "lastmajorreleaseaccession", "taxid", "speciestaxid",
            "speciesname", "assemblytype", "assemblystatus", "coverage", "submissiondate",
            "lastupdatedate", "refseq_category", "contign50", "scaffoldn50")
assembly.tb <- lapply(assembly.info, function(x) {
  tb <- extract_from_esummary(x, elements = fields, simplify = FALSE)
  bind_rows(tb)
}) %>% bind_rows() %>%
  mutate(species = gsub("\\[|\\]", "", speciesname)) %>% 
  arrange(desc(refseq_category), species) %>% 
  select(species, assemblystatus:scaffoldn50, everything())
  #select(species = speciesname, everything())

Select the refseq or one alternative assembly

assembly.tbl <- assembly.tb %>% 
  mutate(assemblystatus = factor(assemblystatus, levels = c("Complete Genome", "Chromosome", "Scaffold", "Contig"))) %>% 
  group_by(species) %>% 
  filter(refseq_category != "na" | scaffoldn50 == max(scaffoldn50) | assemblystatus %in% c("Complete Genome", "Chromosome")) %>% 
  group_by(species, assemblystatus) %>% 
  filter(refseq_category != "na" | scaffoldn50 == max(scaffoldn50)) %>% 
  ungroup()
write_tsv(assembly.tbl, file = "../../output/20220525-expanded-blast-species-assembly-info.tsv")

13. Chromosomal locations

The goal is to fetch the chromosomal locations for the BLAST hits. We are only going to use the assemblies that are at a chromosomal or complete genome level. Note that C. auris refseq genome is complete even though the record says it is scaffold.

# Get the list of species whose genome assembly are at a chromosomal or complete genome level
list.genome.compl <- assembly.tbl %>% 
  filter(assemblystatus %in% c("Complete Genome", "Chromosome"), refseq_category != "na") %>% 
  pull(species) %>% unique()
chr.sps <- c(list.genome.compl, "Scheffersomyces stipitis", "Candida auris")
chr.sid <- dedup.merge %>% filter(species %in% chr.sps, grepl("^ref", sid)) %>% pull(sid) 
# Get the protein's gene ID (not refseq ID)
tmp.gene.lnk <- entrez_link(dbfrom = "protein", id = chr.sid, db = "gene", by_id = TRUE)
# extract gene ID
tmp.gene.id <- sapply(tmp.gene.lnk, function(x) x$links$protein_gene)
# fetch summary info from the gene database
tmp.gene <- entrez_summary(db = "gene", id = tmp.gene.id)
# extract gene name
tmp.gene.name <- extract_from_esummary(tmp.gene, elements = "name")
# extract systematic ID (stored in the otheralias field)
tmp.gene.alias <- extract_from_esummary(tmp.gene, elements = "otheraliases")
# extract chromosomal locations, only available for fully assembled genomes
tmp.gene.loc <- bind_rows(extract_from_esummary(tmp.gene, elements = "genomicinfo"), .id = "gene_uid")
# pool the unique chromosome sequence IDs to find out their length
tmp.chr.id <- unique(tmp.gene.loc$chraccver)
# obtain the chromosome length information from the nuccore database
tmp.chr <- entrez_summary("nuccore", id = tmp.chr.id)
tmp.chr.len <- extract_from_esummary(tmp.chr, elements = "slen"); 
names(tmp.chr.len) = tmp.chr.id
# assemble the chromosomal location dataset
chr.loc <- tibble(sid = chr.sid, gene_uid = names(tmp.gene.name), 
                  gene_name = tmp.gene.name, gene_alias = tmp.gene.alias) %>% 
  left_join(tmp.gene.loc, by = "gene_uid") %>% 
  mutate(chrL = tmp.chr.len[chraccver], relLoc = num(chrstart/chrL, digits = 2)) %>% 
  left_join(select(dedup.merge, sid, pid, species, strain), by = "sid") %>% 
  relocate(pid:strain, .after = sid) %>% relocate(sid, .after = last_col()) %>% 
  select(-exoncount) %>% 
  arrange(species, chrloc)
write_tsv(chr.loc, file = "../../output/20220609-expanded-blast-chromosomal-locations.tsv")
LS0tCnRpdGxlOiAiQzAzNyBFeHBhbmRpbmcgTkNCSSBibGFzdHAgYW5hbHlzaXMiCmF1dGhvcjogQmluIEhlCmRhdGU6ICIyMDIyLTA0LTA4IHVwZGF0ZWQgYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKLS0tCgpgYGB7cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHJlcXVpcmUodGlkeXZlcnNlKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHJlcXVpcmUoY293cGxvdCkpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhyZXF1aXJlKGdndmVubikpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhyZXF1aXJlKHNlcWlucikpCiMgc2V0IE5DQkkgRW50cmV6IEFQSSBrZXkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHJlcXVpcmUocmVudHJleikpCnNldF9lbnRyZXpfa2V5KCJmNGYwMWMyMjBmM2EwYTE2MmJhZGE1YjBiZDVkNGIxMzE5MDgiKQpgYGAKCiMjIEJhY2tncm91bmQKVGhpcyBhbmFseXNpcyBpcyBpbiByZXNwb25zZSB0byByZXZpZXdlciAxJ3MgcXVlc3Rpb25zOgoKMS4gVGhlIHN1Ym1pdHRlZCB2ZXJzaW9uIHVzZWQgX0MuIGF1cmlzXyBIaWwxJ3MgUEYxMTc2NSBkb21haW4gYXMgdGhlIHF1ZXJ5IChiYWl0KS4gV2hhdCBpZiBvdGhlciBob21vbG9ncyB3ZXJlIHVzZWQ/IFdvdWxkIHRoZSBudW1iZXIgYW5kIGlkZW50aXR5IG9mIHRoZSBibGFzdCByZXN1bHRzIGNoYW5nZT8KMS4gV2h5IHJlbW92ZSBzcGVjaWVzIGZyb20gdGhlIGJsYXN0IG91dHB1dD8KClRvIGFkZHJlc3MgdGhlIGZpcnN0IHByb2JsZW0sIEkgZGlkIGJsYXN0cCB3aXRoIHR3byBhZGRpdGlvbmFsIHF1ZXJpZXM6IHRoZSBQRjExNzY1IGRvbWFpbnMgaW4gX0MuIGFsYmljYW5zXyBIeXIxIGFuZCBfQy4gZ2xhYnJhdGFfIEh5cjEuCgpBcyBmb3IgdGhlIHNlY29uZCBxdWVzdGlvbiwgbXkgcmVzcG9uc2UgYW5kIHNvbHV0aW9ucyBhcmUgdG8gaW5jbHVkZSBhbnkgc3BlY2llcyB0aGF0IHdlcmUgYW1vbmcgdGhlIFkxMDAwIHRyZWUgaW4gU2hlbiBldCBhbC4gMjAxOC4gVGhlIG1vcmUgY29tcGxldGUgc2V0IGNhbiBiZSB1c2VkIGZvciB0aGUgZmFtaWx5IGV4cGFuc2lvbiBhbmFseXNpcy4gSSBwbGFuIHRvIGtlZXAgdGhlIGhvbW9sb2dzIGFuYWx5c2lzIHJlc3RyaWN0ZWQgdG8gdGhlIDEwNCBob21vbG9ncywgYXMgYWRkaW5nIG1vcmUgc3BlY2llcyBhcmUgbm90IGV4cGVjdGVkIHRvIGNoYW5nZSB0aGUgbWFpbiBjb25jbHVzaW9ucy4KCiMjIERhdGEKSSBwZXJmb3JtZWQgYmxhc3RwIGFuYWx5c2lzIHVzaW5nIHRoZSB0aHJlZSBxdWVyaWVzIGFnYWluc3QgZWl0aGVyIHRoZSByZWZzZXFfcHJvdCwgdGhlIG5ldyBjbHVzdGVyZWQgbnIgZGF0YWJhc2VzIG9yIGluIEZ1bmdpREIuIEZvciB0aGUgZm9ybWVyIHR3byAocGVyZm9ybWVkIHRocm91Z2ggd2ViIGludGVyZmFjZSBvbiBOQ0JJIHdlYnNpdGUpLCBJIGRvd25sb2FkZWQgdGhlIHJlc3VsdHMgdXNpbmcgdGhlIFJlcXVlc3QgSUQgKFJJRCkgd2l0aCB0aGUgYGJsYXN0X2Zvcm1hdHRlcmAgcHJvZ3JhbSBhbmQgdGhlbiB1c2VkIHRoZSBzYW1lIHRvb2wgdG8gcmVmb3JtYXQgdGhlIG91dHB1dCB0byBjb250YWluIHRoZSBkZXNpcmVkIGNvbHVtbnMuIEZvciB0aGUgRnVuZ2lEQiByZXN1bHQsIEkgdHJpZWQgZG93bmxvYWRpbmcgdGhlIHJlc3VsdHMgaW4gQVNOLjEgZm9ybWF0LCBidXQgd2Fzbid0IGFibGUgdG8gdXNlIGBibGFzdF9mb3JtYXR0ZXJgIHRvIHJlZm9ybWF0IGl0LiBUaGVyZWZvcmUgSSBkb3dubG9hZGVkIHRoZSB0YWJsZSBhcyBzaG93biBvbiB0aGUgZGVmYXVsdCByZXN1bHQgcGFnZS4KYGBge3J9CiMgaGVhZGVyIGZvciBuY2JpIG91dHB1dApoZWFkZXIgPC0gYygicXVlcnkiLCAicWNvdnMiLCAic2lkIiwgInNsZW4iLCAicGlkZW50IiwgInEuc3RhcnQiLCAicS5lbmQiLCAicy5zdGFydCIsICJzLmVuZCIsICJldmFsdWUiLCAic3RheGlkIiwgInNzY2luYW1lcyIpCiMgcmVmc2VxIHJlc3VsdHMKcmVmc2VxIDwtIHJlYWRfdHN2KCJkYXRhLzIwMjIwNDA2LWV4cGFuZGVkLVBGMTE3NjUtYmxhc3RwLXJlZnNlcS50eHQiLCBjb21tZW50ID0gIiMiLCBjb2xfdHlwZXMgPSBjb2xzKCksIGNvbF9uYW1lcyA9IGhlYWRlcikgJT4lIAogIG11dGF0ZShzcGVjaWVzID0gd29yZChzc2NpbmFtZXMsIDEsIDIpICU+JSBnc3ViKCJcXFt8XFxdIiwgIiIsIHggPSAuKSwgc3RyYWluID0gd29yZChzc2NpbmFtZXMsIDMsIC0xKSwKICAgICAgICAgcGlkZW50ID0gbnVtKHBpZGVudCwgZGlnaXRzID0gMSkpICU+JSAKICBzZWxlY3QoLXNzY2luYW1lcykgJT4lIAogIHJlbG9jYXRlKHNwZWNpZXM6c3RyYWluLCAuYWZ0ZXIgPSBwaWRlbnQpCiMgbnIgcmVzdWx0cwpuciA8LSByZWFkX3RzdigiZGF0YS8yMDIyMDQwNi1leHBhbmRlZC1QRjExNzY1LWJsYXN0cC1jbHVzdGVybnIudHh0IiwgY29tbWVudCA9ICIjIiwgY29sX3R5cGVzID0gY29scygpLCBjb2xfbmFtZXMgPSBoZWFkZXIpICU+JSAKICBtdXRhdGUoc3BlY2llcyA9IHdvcmQoc3NjaW5hbWVzLCAxLCAyKSAlPiUgZ3N1YigiXFxbfFxcXSIsICIiLCB4ID0gLiksIHN0cmFpbiA9IHdvcmQoc3NjaW5hbWVzLCAzLCAtMSksCiAgICAgICAgIHBpZGVudCA9IG51bShwaWRlbnQsIGRpZ2l0cyA9IDEpKSAlPiUgCiAgc2VsZWN0KC1zc2NpbmFtZXMpICU+JSAKICByZWxvY2F0ZShzcGVjaWVzOnN0cmFpbiwgLmFmdGVyID0gcGlkZW50KQojIGZ1bmdpZGIgcmVzdWx0cwpmZGIgPC0gcmVhZF9jc3YoImRhdGEvMjAyMjA0MjctZXhwYW5kZWQtYmxhc3QtZnVuZ2lkYi5jc3YiKSAlPiUgCiAgcmVuYW1lKHNpZCA9IEFjY2Vzc2lvbiwgZXZhbHVlID0gYEUtVmFsdWVgLCBzY29yZSA9IFNjb3JlLCBhbGVuID0gYEFsaWduIExlbmd0aGAsIHFjb3ZzID0gYFF1ZXJ5IENvdmVyYWdlYCkgJT4lIAogIG11dGF0ZSgKICAgIHNwZWNpZXMgPSB3b3JkKE9yZ2FuaXNtLCAxLCAyKSAlPiUgZ3N1YigiXFxbfFxcXSIsICIiLCB4ID0gLiksIHN0cmFpbiA9IHdvcmQoT3JnYW5pc20sIDMsIC0xKSwKICAgIHF1ZXJ5ID0gd29yZChRdWVyeSwgMSwgMSksCiAgICBwaWRlbnQgPSBudW0oSWRlbnRpdHkqMTAwLCBkaWdpdHMgPSAxKSwKICAgIHFjb3ZzID0gbnVtKHFjb3ZzKjEwMCwgZGlnaXRzID0gMCksCiAgICBldmFsdWUgPSBzaWduaWYoZXZhbHVlLCBkaWdpdHMgPSAxKSwKICAgIHNjb3JlID0gc2lnbmlmKHNjb3JlLCBkaWdpdHMgPSAzKSwKICAgIC5rZWVwID0gInVudXNlZCIKICApICU+JSAKICAjIG1ha2Ugc3BlbGxpbmcgY29uc2lzdGVudCB3aXRoIG5jYmkKICBtdXRhdGUoc3BlY2llcyA9IGdzdWIoIkNhbmRpZGEgaGFlbXVsb25pcyIsICJDYW5kaWRhIGhhZW11bG9uaSIsIHNwZWNpZXMpKSAlPiUKICBzZWxlY3QocXVlcnksIHFjb3ZzLCBzaWQsIHBpZGVudCwgc3BlY2llcywgc3RyYWluLCBzY29yZSwgZXZhbHVlLCBgUmFuayBQZXIgUXVlcnlgLCBgUmFuayBQZXIgU3ViamVjdGApCmBgYAoKIyMgQW5hbHlzaXMKIyMjIDEuIE92ZXJsYXAgYmV0d2VlbiB0aGUgdGhyZWUgcXVlcmllcz8KV2hhdCdzIHRoZSBleHRlbnQgb2Ygb3ZlcmxhcCBiZXR3ZWVuIHRoZSBibGFzdHAgaGl0cyB1c2luZyB0aGUgdGhyZWUgZGlmZmVyZW50IHF1ZXJpZXM/CgpIaXRzIHdpdGggPDUwJSBxdWVyeSBjb3ZlcmFnZSBhcmUgcmVtb3ZlZC4KYGBge3J9CnJlZnNlcSAlPiUgZmlsdGVyKHFjb3ZzIDwgNTApCmBgYApWZW5uIGRpYWdyYW0gc2hvd2luZyB0aGUgZXh0ZW50IG9mIG92ZXJsYXAgYW5kIHNldCBzcGVjaWZpYyBoaXRzOgpgYGB7cn0Kc2V0Lm92bHAgPC0gcmVmc2VxICU+JSAKICBmaWx0ZXIocWNvdnMgPj0gNTApICU+JSAKICBncm91cF9ieShzaWQpICU+JSAKICBzdW1tYXJpemUoCiAgICBDYXVyID0gYW55KHF1ZXJ5ID09ICJDYXVyX0hpbDEiKSwKICAgIENhbGIgPSBhbnkocXVlcnkgPT0gIkNhbGJfSHlyMSIpLAogICAgQ2dsYSA9IGFueShxdWVyeSA9PSAiQ2dsYV9IeXIxIikKICApCmdndmVubihzZXQub3ZscCkKYGBgCgpXaGljaCBnZW5lcyBhcmUgZm91bmQgYnkgdGhlIF9DLiBhbGJpY2Fuc18gYW5kL29yIF9DLiBnbGFicmF0YV8gSHlyMSBxdWVyeSBibGFzdCBidXQgbm90IGJ5IHRoZSBfQy4gYXVyaXNfIEhpbDEgcXVlcnkgYmxhc3Q/CmBgYHtyfQpDYWxiLm5vdC5DYXVyLmhpdHMgPC0gc2V0Lm92bHAgJT4lIGZpbHRlcighQ2F1cikgJT4lIHB1bGwoc2lkKQpyZWZzZXEgJT4lIGZpbHRlcihzaWQgJWluJSBDYWxiLm5vdC5DYXVyLmhpdHMpICU+JSAKICBncm91cF9ieShzaWQpICU+JSBmaWx0ZXIoZXZhbHVlID09IG1pbihldmFsdWUpKSAlPiUgdW5ncm91cCgpICU+JSAKICBzZWxlY3QocXVlcnksIHFjb3ZzLCBzaWQsIHNsZW4sIHBpZGVudCwgZXZhbHVlLCBzcGVjaWVzKSAlPiUgCiAgbXV0YXRlKHNwZWNpZXMgPSBwYXN0ZShzdHJfc3ViKHNwZWNpZXMsIDEsIDEpLCB3b3JkKHNwZWNpZXMsIDIsIDIpLCBzZXAgPSAiLiAiKSkgJT4lIAogIGFycmFuZ2Uoc3BlY2llcykKYGBgCj4gLSBUaGUgbWFqb3JpdHkgb2YgdGhlIGNvbWJpbmVkIHNldCBpcyBhbHJlYWR5IGluIHRoZSBzdWJzZXQgaWRlbnRpZmllZCB1c2luZyBfQy4gYXVyaXNfIEhpbDEgYXMgdGhlIHF1ZXJ5Lgo+IC0gT2YgdGhlIG9uZXMgdGhhdCBhcmUgc3BlY2lmaWMgdG8gdGhlIF9DLiBhbGJpY2Fuc18gSHlyMSBxdWVyeSwgSSBmb3VuZCB0aGUgdGhyZWUgX0MuIHBhcmFzaWxvc2lzXyBoaXRzIGFyZSBhbHJlYWR5IGluY2x1ZGVkIGluIHRoZSAxMDQgaG9tb2xvZ3MgdmlhIHRoZSBGdW5naURCIGJsYXN0LiBXaGF0ZXZlciB0aGUgcmVhc29uIHRoYXQgZXhsdWRlZCB0aGVtIGZyb20gdGhlIG5jYmkgYmxhc3RwIHNlYXJjaCB1c2luZyBfQy4gYXVyaXNfIEhpbDEgZG9tYWluIGFzIHRoZSBxdWVyeSwgdGhleSBhcmUgYWxyZWFkeSBpbiBvdXIgZGF0YXNldC4KPiAtIFNhbWUgaXMgdHJ1ZSBmb3IgWFBfNDUyMTc2LjEgZnJvbSBfSy4gbGFjdGlzXywgdGhpcyB0aW1lIHRocm91Z2ggdGhlIEdSWUMgc2VhcmNoLgoKQW1vbmcgdGhlIHNwZWNpZXMgaW5jbHVkZWQgcHJldmlvdXNseSwgaG93IG1hbnkgb2YgdGhlICJuZXciIHNlcXVlbmNlcyB3ZXJlIG5vdCBwYXJ0IG9mIHRoZSAxMDQgaG9tb2xvZ3MgbGlzdD8KCnwgc2lkIHwgZ2lkIHwgc3BlY2llcyB8IGluX2Vhcmx5X3ZlcmlvbiB8IHNvdXJjZSB8Cnw6LS18Oi0tfDotLXw6LS18Oi0tfAp8IFhQXzAwMjk5OTU4NSB8IENBR0wwTDAwMjI3ZyB8IEMuIGdsYWJyYXRhIHwgWWVzIHwgR1JZQyB8CnwgWFBfMDAyNjE1MjE4IHwgQ0xVR18wNTIzMyB8IEMuIGx1c2l0YW5pYWUgfCBZZXMgfCBGdW5naURCIHwKfCBYUF8wMDI2MjAwNDUgfCBDTFVHXzAxMjA0IHwgQy4gbHVzaXRhbmlhZSB8IE5vXiB8IE5BIHwKfCBYUF8wMDI2MTQxMjEgfCBDTFVHXzA1NjA3IHwgQy4gbHVzaXRhbmlhZSB8IE5vIHwgTkEgfAp8IFhQXzAzNjY2NTI2MiB8IENQQVIyXzgwNjM5MCB8IEMuIHBhcmFwc2lsb3NpcyB8IFllcyB8IEZ1bmdpREIgfAp8IFhQXzAzNjY2MjgxNSB8IENQQVIyXzYwMDQzMCB8IEMuIHBhcmFwc2lsb3NpcyB8IFllcyB8IEZ1bmdpREIgfAp8IFhQXzAzNjY2NTI2MyB8IENQQVIyXzgwNjQwMCB8IEMuIHBhcmFwc2lsb3NpcyB8IE5vIHwgTkEgfAp8IFhQXzQ1MjE3NiB8IEtMTEEwX0IxNDQ5OGcgfCBLLiBsYWN0aXMgfCBZZXMgfCBHUllDIHwKfCBYUF8wMDE1MjczMDcgfCBMRUxHXzAyMTM2IHwgTC4gZWxvbmdpc3BvcnVzIHwgTm8gfCBOQSB8CnwgWFBfMDAxMzg1NjgzIHwgUElDU1RfMzQwMiB8IFMuIHN0aXBpdGlzIHwgTm9eIHwgTkEgfAp8IFhQXzAwMTM4Mzk1MyB8IFBJQ1NUXzMxMDk1IHwgUy4gc3RpcGl0aXMgfCBZZXMqIHwgTkNCSSB8CgoqdGhpcyBoaXQgd2FzIG9yaWdpbmFsbHkgaW5jbHVkZWQgd2hlbiB0aGUgTjU2MCBhYSB3YXMgdXNlZCBhcyBxdWVyeS4gSW4gbGF0ZXIgYW5hbHlzaXMgd2hlcmUgTjM2MCBhYSAob2YgX0MuIGF1cmlzXyBIaWwxKSB3YXMgdXNlZCBhcyBxdWVyeSwgdGhpcyBoaXQgZHJvcHBlZCBvZmYgdGhlIGxpc3QgZHVlIHRvIEUtdmFsdWUgYmVpbmcgYWJvdmUgdGhlIDFlLTUgY3V0b2ZmLiBTbyBpdCBpcyBhIGxlZ2l0LCBidXQgYm9yZGVybGluZSBoaXQuCgpec2hvcnRlciB0aGFuIDUwMCBhLmEuCgo+IEZpdmUgaGl0cyBhcmUgbmV3LCBhbW9uZyB3aGljaCB0d28gYXJlIHNob3J0ZXIgdGhhbiA1MDAgYS5hLiBhbmQgd291bGQgaGF2ZSBiZWVuIGV4Y2x1ZGVkIGZyb20gdGhlIGxpc3QgKGFsdGhvdWdoIHRoZXkgd2VyZSBub3QgaWRlbnRpZmllZCBhdCBhbGwgd2l0aCB0aGUgX0MuIGF1cmlzXyBxdWVyeSkuCgoKIyMjIDIuIFNwZWNpZXMgdG8gaW5jbHVkZQpUaGUgbmV3IGNyaXRlcmlhIGlzIHdoZXRoZXIgdGhlIHNwZWNpZXMgaXMgYW1vbmcgdGhlIDMzMiBpbmNsdWRlZCBpbiB0aGUgbGFyZ2Utc2NhbGUgcGh5bG9nZW55IG9mIFNoZW4gX2V0IGFsLl8gMjAxOC4gSSBkb3dubG9hZGVkIHRoZSB0cmVlIGZyb20gaHR0cHM6Ly9naXRodWIuY29tL0pMU3RlZW53eWsvdHJlZWhvdXNlL2Jsb2IvbWFzdGVyL0RhdGEvU2FjY2hhcm9teWNvdGluYV9maWcyX1NoZW5fZXRhbF8yMDE4LnRyZSBhbmQgbWFudWFsbHkgY29udmVydGVkIGl0IGludG8gYSBsaXN0IG9mIHNwZWNpZXMgbmFtZXMuCmBgYHtyfQpzaGVuMjAxOCA8LSBzY2FuKCIuLi8uLi9kYXRhL3llYXN0LXBoeWxvZ2VueS9TYWNjaGFyb215Y290aW5hX2ZpZzJfU2hlbl9ldGFsXzIwMTgudHh0Iiwgd2hhdCA9ICJjaGFyYWN0ZXIiLCBzZXAgPSAiXG4iKQpgYGAKCkFueSBzcGVjaWVzIGluIHRoZSByZWZzZXEgaGl0IGxpc3Qgbm90IGluIHRoZSBTaGVuIDIwMTggcGh5bG9nZW55PwpgYGB7cn0Kc2V0ZGlmZih1bmlxdWUocmVmc2VxJHNwZWNpZXMpLCBzaGVuMjAxOCkKYGBgCgp8IHJlZnNlcV9uYW1lIHwgdGF4aWQgfCBpbl9TaGVuXzIwMTggfCBzaGVuMjAxOF9uYW1lIHwKfDotLXw6LS18Oi0tfDotLXwKfCBDYW5kaWRhIGhhZW11bG9uaSB8IDQ1MzU3IHwgTm8gfCBOQSB8CnwgQ2FuZGlkYSBkdW9idXNoYWVtdWxvbmlzIHwgMTIzMTUyMiB8IE5vIHwgTkEgfAp8IENhbmRpZGEgcHNldWRvaGFlbXVsb25paSB8IDQxODc4NCB8IE5vIHwgTkEgfAp8IFN1aG9teWNlcyB0YW56YXdhZW5zaXMgfCA5ODQ0ODcgfCBZZXMgfCBDYW5kaWRhIHRhbnphd2FlbnNpcyB8CnwgUHNldWRvbW9uYXMgc3lyaW5nYWU6UHNldWRvbW9uYXMgfCAzMTc7NTk1MTAgfCBObyB8IE5BIHwKfCBEaXV0aW5hIHJ1Z29zYSB8IDU0ODEgfCBObyB8IE5BIHwKfCBZYW1hZGF6eW1hIHRlbnVpcyB8IDU5MDY0NiB8IFllcyB8IENhbmRpZGEgdGVudWlzIHwKfCBTY2hpem9zYWNjaGFyb215Y2VzIGNyeW9waGlsdXMgfCA2NTM2NjcgfCBObyB8IE5BIHwKfCBLYXphY2hzdGFuaWEgYmFybmV0dGlpIHwgNjEyNjIgfCBObyB8IE5BIHwKfCBBcnRpYmV1cyBqYW1haWNlbnNpcyB8IDk0MTcgfCBObyB8IE5BIHwKCk1hbnVhbGx5IGZpZ3VyZSBvdXQgc3BlY2llcyBzeW5vbnltcyBhbmQgcmVjb3JkIGJvdGggbmFtZXMgZm9yIHRoZSBzcGVjaWVzIHRvIHVzZS4gKip1cGRhdGUqKjogZG93bmxvYWRlZCB0aGUgc3BlY2llcyBuYW1lIGNvbnZlcnRpb24gdGFibGUgZnJvbSBTaGVuIGV0IGFsIDIwMTggc3VwcGxlbWVudGFyeSBtYXRlcmlhbC4KYGBge3J9CnNwc05hbWVDb252ZXJ0IDwtIHJlYWRfdHN2KCIuLi8uLi9kYXRhL3llYXN0LXBoeWxvZ2VueS8zNDN0YXhhX3NwZWljaWVzLW5hbWVfY2xhZGUtbmFtZV9jb2xvci1jb2RlLnR4dCIsIGNvbF90eXBlcyA9IGNvbHMoKSkgJT4lIAogIG11dGF0ZShzcGVjaWVzID0gd29yZChgU3BlY2llcyBuYW1lYCwgMSwgMikpCnNwc05hbWVTeW4gPC0gdGliYmxlKAogIG5jYmkubmFtZSA9IGMoaW50ZXJzZWN0KHJlZnNlcSRzcGVjaWVzLCBzaGVuMjAxOCksICJTdWhvbXljZXMgdGFuemF3YWVuc2lzIiwgIllhbWFkYXp5bWEgdGVudWlzIiwgIkxhY2hhbmNlYSBrbHV5dmVyaSIpLAogIHNoZW4ubmFtZSA9IGMoaW50ZXJzZWN0KHJlZnNlcSRzcGVjaWVzLCBzaGVuMjAxOCksICJDYW5kaWRhIHRhbnphd2FlbnNpcyIsICJDYW5kaWRhIHRlbnVpcyIsICJMYWNoYW5jZWEga2x1eXZlcmkiKQopCmBgYAoKIyMjIDMuIENoZWNrIHByb3RlaW4gY29tcGxldGVuZXNzCgpPdXIgZ29hbCBpcyB0byBjcmVhdGUgYSB0YWJsZSBkb2N1bWVudGluZyB0aGUgbnVtYmVyIG9mIHB1dGF0aXZlIGhvbW9sb2dzIHBlciBzcGVjaWVzLiBXZSB1c2VkIHRvIGFwcGx5IGEgbGVuZ3RoIGN1dG9mZiBvZiA1MDAgYWEuIEhvd2V2ZXIsIHRoZSBzcGVjaWZpYyB2YWx1ZSBvZiB0aGUgY3V0b2ZmIGlzIG5vdCBiYXNlZCBvbiBhbnkgY29tcHJlaGVuc2l2ZSBzdHVkaWVzLiBUaGlzIGNvbWJpbmVkIHdpdGggdGhlIHVuY2VydGFpbnR5IGluIHNvbWUgcHV0YXRpdmUgaG9tb2xvZydzIGxlbmd0aCBkdWUgdG8gYXNzZW1ibHkgaXNzdWVzIGxlZCB1cyB0byByZWNvbnNpZGVyIHRoaXMgZmlsdGVyLiBUaGUgc29sdXRpb24gd2UgY2FtZSB1cCB3aXRoIGlzIHRvIGluY2x1ZGUgYWxsIGJsYXN0IGhpdHMgYmFzZWQgb24gdGhlIFBGMTE3NjUgZG9tYWluIGFsb25lIGluIHRoZSBmYW1pbHkgZXhwYW5zaW9uIGFuYWx5c2lzIChDQUZFIGFuZCBwaHlsb2dlbmV0aWMgcmVjb25zdHJ1Y3Rpb24pLiBMYXRlciB3ZSB3aWxsIHNlbGVjdGl2ZWx5IGluY2x1ZGUgc29tZSBvZiB0aGVtIGZvciB0aGUgcHJvdGVpbiBmZWF0dXJlIGFuYWx5c2lzLiBTaW5jZSB0aGUgbGF0dGVyIGFuYWx5c2lzIGlzIG1haW5seSB0byBkZW1vbnN0cmF0ZSB0aGUgZmFzdCBldm9sdXRpb24gb2YgdGhlIGNlbnRyYWwgZG9tYWluIGZlYXR1cmVzLCBpdCBkb2Vzbid0IHJlbHkgb24gdGhlIGNvbXBsZXRlbmVzcyBvZiB0aGUgSGlsIGZhbWlseSBvciB0aGUgaW5jbHVzaW9uIG9mIGFsbCBzcGVjaWVzIGZvciB3aGljaCB3ZSBoYXZlIHJlY29yZHMgZm9yLgoKSW4gY3VyYXRpbmcgdGhlIGJsYXN0IGhpdHMsIHdlIGZvdW5kIHRoYXQgc29tZSAocHJvdGVpbikgYmxhc3QgaGl0cyBhcmUgYW5ub3RhdGVkIGFzIG1pc3NpbmcgdGhlIGNhcmJveHkgdGVybWludXMuIFRoaXMgd291bGQgcHJldmVudCB1cyBmcm9tIHByb3Blcmx5IGFzc2Vzc2luZyB0aGUgbGVuZ3RoIG9mIHRoZSBwcm90ZWluLiBOb3RlIHRoYXQgImluY29tcGxldGUiIG1lYW5zIGRpZmZlcmVudCB0aGluZ3MgZm9yIHRoZSBwcm90ZWluIHZzIG1STkEgcmVjb3JkLiBGb3IgdGhlIGxhdHRlciwgImluY29tcGxldGUiIGNvdWxkIG1lYW4gbWlzc2luZyA1JyBhbmQvb3IgMydVVFIgd2hpbGUgdGhlIENEUyBtYXkgYmUgY29tcGxldGUuIEEgbGFyZ2UgbnVtYmVyIG9mIHRoZSBtUk5BIHJlY29yZHMgY29ycmVzcG9uZGluZyB0byB0aGUgcHJvdGVpbiBoaXRzIGluIHRoZSBsaXN0IGFyZSAicGFydGlhbCIuIFRoYXQncyBub3QgcmVhc29uIGZvciBleGNsdXNpb24uCgpGb3IgX0MuIHRyb3BpY2FsaXNfLCBJIGZvdW5kIGEgbmV3LCBbMjAxOSBhc3NlbWJseV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9hc3NlbWJseS9HQ0FfMDEzMTc3NTU1LjEpIHRoYXQgdXNlcyBib3RoIFBhY0JpbyBhbmQgSWxsdW1pbmEuIFRoZSBhc3NlbWJseSBkaWQgdXNlIHRoZSByZWZlcmVuY2UgZ2Vub21lIGFzc2VtYmx5IGFzIGEgZ3VpZGUuCgpUbyBpZGVudGlmeSBhbGwgcGFydGlhbCBtUk5BLCB3ZSB3aWxsIHVzZSB0aGUgYHJlbnRyZXpgIHBhY2thZ2UgdG8gcXVlcnkgdGhlIG5jYmkgZGF0YWJhc2UuCmBgYHtyfQojIGRlZmluZSBhIGZ1bmN0aW9uIGZvciByZXRyaWV2aW5nIHRoZSBtUk5BIGluZm9ybWF0aW9uIGZyb20gdGhlIG51Y2xlb3RpZGUgZGF0YWJhc2VkLCBiYXNlZCBvbiBwcm90ZWluIElECnJldHJpZXZlX21ybmFfaW5mbyA8LSBmdW5jdGlvbihpZHMpewogIG51Yy5sbmsgPC0gZW50cmV6X2xpbmsoZGJmcm9tID0gInByb3RlaW4iLCBkYiA9ICJudWNsZW90aWRlIiwgaWQgPSBpZHMsIGJ5X2lkID0gVFJVRSkKICBudWNJRCA8LSBzYXBwbHkobnVjLmxuaywgZnVuY3Rpb24oeCl7CiAgICBsbmsgPSB4JGxpbmtzJHByb3RlaW5fbnVjY29yZV9tcm5hCiAgICByZXR1cm4oaWZlbHNlKGlzLm51bGwobG5rKSwgTkEsIGxuaykpCiAgfSkKICBudWNTdW0gPC0gZW50cmV6X3N1bW1hcnkoZGIgPSAibnVjY29yZSIsIGlkID0gbmEub21pdChudWNJRCkpCiAgbmFtZXMobnVjU3VtKSA8LSBpZHNbIWlzLm5hKG51Y0lEKV0KICByZXR1cm4obnVjU3VtKQp9CgpyZXRyaWV2ZV9wcm90ZWluX2luZm8gPC0gZnVuY3Rpb24oaWRzKXsKICBwU3VtIDwtIGVudHJlel9zdW1tYXJ5KGRiID0gInByb3RlaW4iLCBpZCA9IGlkcykKICBuYW1lcyhwU3VtKSA8LSBpZHMKICByZXR1cm4ocFN1bSkKfQpgYGAKCmBgYHtyfQojIGFsbCByZWZzZXEgcHJvdGVpbiBJRHMgdG8gZ2V0IHRoZSBtUk5BIGluZm9ybWF0aW9uIGZvcgpwaWQgPC0gdW5pcXVlKHJlZnNlcSRzaWQpCiMgcG9zdGluZyB0b28gbWFueSBxdWVyaWVzIGNhbiBoaXQgdGhlIHJhdGUtbGltaXQuIHNwbGl0IGludG8gbXVsdGlwbGVzIG9mIDUwcwppdGVyLnBpZCA8LSBzcGxpdCh4ID0gcGlkLCBmID0gY2VpbGluZyhzZXFfYWxvbmcocGlkKS81MCkpCnBTdW0gPC0gc2FwcGx5KGl0ZXIucGlkLCByZXRyaWV2ZV9wcm90ZWluX2luZm8pCm51Y1N1bSA8LSBzYXBwbHkoaXRlci5waWQsIHJldHJpZXZlX21ybmFfaW5mbykKYGBgCgpgYGB7cn0KIyBleHRyYWN0IHRoZSBtUk5BIGluZm8KbXJuYUluZm8gPC0gbGFwcGx5KG51Y1N1bSwgZnVuY3Rpb24oeCkgewogIGV4dHJhY3RfZnJvbV9lc3VtbWFyeSh4LCBlbGVtZW50cyA9IGMoImNhcHRpb24iLCAiY29tcGxldGVuZXNzIiksIHNpbXBsaWZ5ID0gRkFMU0UpICU+JSAKICAgIGJpbmRfcm93cyguaWQgPSAic2lkIikgJT4lIAogICAgcmVuYW1lKHRJRCA9IGNhcHRpb24sIHRDb21wbGV0ZSA9IGNvbXBsZXRlbmVzcykKfSkgJT4lIGJpbmRfcm93cygpCiMgZXh0cmFjdCB0aGUgcHJvdGVpbiBjb21wbGV0ZW5lc3MgaW5mbwpwSW5mbyA8LSBsYXBwbHkocFN1bSwgZnVuY3Rpb24oeCkgewogIGV4dHJhY3RfZnJvbV9lc3VtbWFyeSh4LCBlbGVtZW50cyA9IGMoImNvbXBsZXRlbmVzcyIpLCBzaW1wbGlmeSA9IEZBTFNFKSAlPiUgCiAgICBiaW5kX3Jvd3MoLmlkID0gInNpZCIpICU+JSAKICAgIHJlbmFtZShwQ29tcGxldGUgPSBjb21wbGV0ZW5lc3MpCn0pICU+JSBiaW5kX3Jvd3MoKQpgYGAKCk5vdyByZW1vdmUgZHVwbGljYXRlcyAocmVzdWx0aW5nIGZyb20gdGhlIGRpZmZlcmVudCBxdWVyaWVzKSBhbmQgZmlsdGVyIGJ5IHNwZWNpZXMgKHRob3NlIHRoYXQgb3ZlcmxhcCB0aGUgU2hlbiAyMDE4IGxpc3QgcGx1cyB0aGUgTURSIGNsYWRlKQpgYGB7cn0Kc3BzLmluY2x1ZGUgPC0gYyhzcHNOYW1lU3luJG5jYmkubmFtZSwgIkNhbmRpZGEgaGFlbXVsb25pIiwgIkNhbmRpZGEgZHVvYnVzaGFlbXVsb25pcyIsICJDYW5kaWRhIHBzZXVkb2hhZW11bG9uaWkiKQpkZWR1cC5yZWYgPC0gcmVmc2VxICU+JSAKICBmaWx0ZXIoc3BlY2llcyAlaW4lIHNwcy5pbmNsdWRlLCBxY292cyA+PSA1MCkgJT4lIAogIGdyb3VwX2J5KHNpZCkgJT4lIAogIGZpbHRlcihldmFsdWUgPT0gbWluKGV2YWx1ZSkpICU+JSAKICBsZWZ0X2pvaW4obXJuYUluZm8sIGJ5ID0gYygic2lkIiA9ICJzaWQiKSkgJT4lIAogIGxlZnRfam9pbihwSW5mbywgYnkgPSBjKCJzaWQiID0gInNpZCIpKSAlPiUgCiAgc2VsZWN0KHF1ZXJ5LCBzaWQsIHNsZW4sIHBDb21wbGV0ZSwgdElELCB0Q29tcGxldGUsIHNwZWNpZXMsIHN0cmFpbiwgZXZlcnl0aGluZygpKQpgYGAKCgpTdW1tYXJpemUgdGhlIGRlZHVwbGljYXRlZCBsaXN0IC0gZm9yIGVhY2ggc3BlY2llcywgcmVjb3JkIHRoZSB0b3RhbCBudW1iZXIgb2YgaGl0cywgdGhvc2UgdGhhdCBhcmUgbGFiZWxlZCBhcyBpbmNvbXBsZXRlIGluIHRoZSBwcm90ZWluIHJlY29yZCwgdGhvc2UgdGhhdCBhcmUgYWJvdmUgNTAwIGFhIGFuZCB0aGUgbnVtYmVyIG9mIGhpdHMgYmVsb3cgNTAwIGFhIHRoYXQgYXJlIGxhYmVsZWQgYXMgaW5jb21wbGV0ZQpgYGB7cn0Kc3VtLnJlZiA8LSBkZWR1cC5yZWYgJT4lIAogIGdyb3VwX2J5KHNwZWNpZXMsIHN0cmFpbikgJT4lIAogIHN1bW1hcml6ZSgKICAgIHRvdGFsID0gbigpLAogICAgaW5jb21wbCA9IHN1bShwQ29tcGxldGUgIT0gIiIpLAogICAgZ3Q1MDAgPSBzdW0oc2xlbiA+PSA1MDApLAogICAgc2hvcnRJbmNvbXBsID0gc3VtKHBDb21wbGV0ZSAhPSAiIiAmIHNsZW4gPCA1MDApCiAgKQpzdW0ucmVmICU+JSBhcnJhbmdlKGRlc2Moc2hvcnRJbmNvbXBsKSkKYGBgCj4gLSBfUy4gc3RpcGl0aXNfIGFuZCBfTS4gYmljdXNwaWRhdGFfIGhhZCBhIGxhcmdlIG51bWJlciBvZiBpbmNvbXBsZXRlIHByb3RlaW5zLCBsaWtlbHkgYSByZWZsZWN0aW9uIG9mIHRoZSBnZW5vbWUgYXNzZW1ibHkgc3RhdHVzLiBGb3IgX00uIGJpY3VzcGlkYXRhXywgYSBuZXdlciBJbGx1bWluYSArIE94Zm9yZCBOYW5vcG9yZSBbYXNzZW1ibHldKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvYXNzZW1ibHkvR0NBXzAyMjU3NTk1NS4xLykgaXMgYXZhaWxhYmxlIGZvciB0aGlzIHNwZWNpZXMgYnV0IGlzIG9ubHkgYXNzZW1ibGVkIHRvIHRoZSBjb250aWcgbGV2ZWwsIHdpdGggbm8gZ2VuZSBhbm5vdGF0aW9uLiBJIHRyaWVkIGJsYXN0aW5nIHRoZSByZWZzZXEgaGl0cyBmcm9tIHRoZSBzcGVjaWVzIGFnYWluc3QgdGhlIG5ldyBhc3NlbWJseSBidXQgdGhlIG1hdGNoIHdhcyBwb29yIGZvciBtb3N0IG9mIHRoZSBoaXRzIC0gaW5zdGVhZCBvZiBnZXR0aW5nIGEgbG9uZywgY29udGlndW91cyBIU1AsIEkgZ290IG1vc3RseSBicm9rZW4gcGllY2VzIHdpdGggbG90cyBvZiBtaXNtYXRjaGVzLiBJIHN1c3BlY3QgdGhpcyBpcyBkdWUgdG8gYSBjb21iaW5hdGlvbiBvZiB0YW5kZW0gZ2VuZSBhcnJheXMgY2F1c2luZyBkaWZmaWN1bHR5IGZvciBhc3NlbWJseSBhcm91bmQgdGhlIEhpbCBob21vbG9ncyBsb2NpIGFuZCB0aGUgc2VxdWVuY2luZyBkZXB0aCBhbmQgYXNzZW1ibHkgaXRzZWxmLiBCYXNlZCBvbiB0aGVzZSBvYnNlcnZhdGlvbnMsIEkgcGxhbiB0byBpbmNsdWRlIHRoZSBzcGVjaWVzIGluIHRoZSBDQUZFIGFuYWx5c2lzIGFuZCBwb3RlbnRpYWxseSBpbiB0aGUgcGh5bG9nZW5ldGljIHJlY29uc3RydWN0aW9uIGFzIHdlbGwsIGJ1dCBleGNsdWRlIGl0IGZyb20gdGhlIGhvbW9sb2cgcHJvdGVpbiB0cmFpdCBhbmFseXNlcy4KPiAtIEZvciBfUy4gc3RpcGl0aXNfLCBJIGlkZW50aWZpZWQgYSBbbmV3ZXIgYXNzZW1ibHldKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvYXNzZW1ibHkvR0NBXzAxNjg1OTI5NS4xLykgZm9yIGEgZGlmZmVyZW50IHN0cmFpbiAoTlJSTCBZLTcxMjQpIGFuZCBwZXJmb3JtZWQgbG9jYWwgYmxhc3QgYWdhaW5zdCB0aGUgYW5ub3RhdGVkIHByb3RlaW4gc2VxdWVuY2VzLiBJIGlkZW50aWZpZWQgYSB0b3RhbCBvZiAxMyBoaXRzLCBvZiB3aGljaCBzZXZlbiBhcmUgYWJvdmUgdGhlIDUwJSBxdWVyeSBjb3ZlcmFnZSB0aHJlc2hvbGQuIFRoaXMgaXMgcXVpdGUgZGlmZmVyZW50IGZyb20gdGhlIDE4IGhpdHMgaWRlbnRpZmllZCBpbiB0aGUgcmVmc2VxIGFzc2VtYmx5LiBXaGVuIEkgYmxhc3RlZCB0aGUgcmVmc2VxIGhpdHMgYWdhaW5zdCB0aGUgbmV3IGFzc2VtYmx5LCBzb21lIG9mIHRoZW0gaGFkIHBvb3IgbWF0Y2hlcy4gV2l0aG91dCBmdXJ0aGVyIGludmVzdGlnYXRpb24sIGl0IGlzIG5vdCBjbGVhciB3aGF0IGlzIHRoZSBjYXVzZSBvZiB0aGUgZGlzY3JlcGFuY3kuIEkgcGxhbiB0byB1c2UgdGhlIG5ldyBhc3NlbWJseSBoaXRzIGZvciBkb3duc3RyZWFtIGFuYWx5c2VzIGdpdmVuIHRoYXQgaXQgaXMgbW9yZSByZWNlbnRseSBzZXF1ZW5jZWQuCj4gLSBGb3IgdGhlIHNhbWUgcmVhc29ucyBhcyBhYm92ZSwgSSBwbGFuIHRvIGluY2x1ZGUgX1kuIHRlbnVpc18sIF9TLiB0YW56YXdhZW5zaXNfIGFuZCBfQy4gamFkaW5paV8gaW4gdGhlIENBRkUgYW5hbHlzaXMgYnV0IGV4Y2x1ZGUgdGhlbSBmcm9tIHRoZSBwcm90ZWluIGZlYXR1cmUgYW5hbHlzZXMuIEdpdmVuIHRoYXQgdGhleSBoYXZlIHJlbGF0aXZlbHkgZmV3IGhpdHMsIEkgY291bGQgY29uc2lkZXIgbWFudWFsbHkgc2VhcmNoaW5nIGZvciB0aGUgZnVsbCBsZW5ndGggT1JGLCBidXQgdGhhdCdzIG9wdGlvbmFsLgo+IC0gRm9yIF9ELiBmYWJyeWlfLCB0aGUgWS0xMDAwIHBsdXMgcHJvamVjdCBoYXMgc2VxdWVuY2VkIGFub3RoZXIgc3RyYWluIGZyb20gdGhpcyBzcGVjaWVzLiBJIGJsYXN0ZWQgYWdhaW5zdCB0aGF0IGFzc2VtYmx5IGFuZCBpZGVudGlmaWVkIHR3byBtYXRjaGVzLiBCdXQgdGhlIHR3byBoaXRzIGFwcGVhciB0b28gc2ltaWxhciB0byBlYWNoIG90aGVyLCBsZWF2aW5nIG1lIHdvbmRlciBpZiB0aGV5IGFyZSB0cnVseSBkaXN0aW5jdCBsb2NpLCBvciBhcmUgdGhleSBwYXJ0aWFsbHkgZHVwbGljYXRlIGNvbnRpZ3MuCgpIaXRzIGJhc2VkIG9uIHRoZSBuZXcgX1MuIHN0aXBpdGlzXyBhc3NlbWJseQpgYGB7cn0Kcy5zdGlwaXRpcyA8LSByZWFkX3RzdigiLi4vLi4vZGF0YS9sb25nLXJlYWQtYXNzZW1ibGllcy9zX3N0aXBpdGlzX05SUkxfWTcxMjQvUy1zdGlwaXRpcy1OUlJMLVk3MTI0LUhpbC1ob21vbG9ncy1ibGFzdC50eHQiLAogICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSAiIyIsIGNvbF9uYW1lcyA9IGhlYWRlclsxOjEwXSwgY29sX3R5cGVzID0gY29scygpKSAlPiUgCiAgbXV0YXRlKHNwZWNpZXMgPSAiU2NoZWZmZXJzb215Y2VzIHN0aXBpdGlzIiwgc3RyYWluID0gIk5SUkwgWS03MTI0Iiwgc3RheGlkID0gIjkxMzEzOCIsCiAgICAgICAgIHBpZGVudCA9IG51bShwaWRlbnQsIGRpZ2l0cyA9IDEpKSAlPiUgCiAgcmVsb2NhdGUoc3BlY2llczpzdHJhaW4sIC5hZnRlciA9IHBpZGVudCkgJT4lIAogIGdyb3VwX2J5KHNpZCkgJT4lIAogIGZpbHRlcihldmFsdWUgPT0gbWluKGV2YWx1ZSkpCmBgYAoKIyMjIDQuIF9DYW5kaWRhIGdsYWJyYXRhXyBoaXRzClRoZSBDb3JtYWNrIGxhYiByZWxlYXNlZCBuZXcgYXNzZW1ibGllcyBmb3IgYSBjb3VwbGUgb2YgX0MuIGdsYWJyYXRhXyBzdHJhaW5zIGluIFh1IDIwMjAgYW5kIFh1IDIwMjEgKFBNSURzOiAzMjA2ODMxNCwgMzM3MTMzNzIpLiBUaGVzZSBhc3NlbWJsaWVzIHdlcmUgYmFzZWQgb24gbG9uZy1yZWQgc2VxdWVuY2luZyB0ZWNobm9sb2dpZXMgc3VwcGxlbWVudGVkIHdpdGggc2hvcnQgcmVhZHMgZm9yIHBvbGlzaGluZyB0aGUgc2VxdWVuY2VzLiBUaGV5IGFsc28gcGFpZCBzcGVjaWFsIGF0dGVudGlvbiB0byB0aGUgc3VidGVsb21lcmVzLCB1c2luZyB2YXJpb3VzIG1lYW5zIHRvIHZlcmlmeSB0aGF0IHRoZWlyIG5ld2x5IGFzc2VtYmxlZCBzdWJ0ZWxvbWVyaWMgcmVnaW9ucyBhcmUgY29ycmVjdC4gUGVyZm9ybWluZyBibGFzdHAgc2VhcmNoZXMgYWdhaW5zdCB0aGVzZSBhc3NlbWJsaWVzIChJIHRlc3RlZCB0d28gb2YgdGhlbTogQVRUQzIwMDE9Q0JTMTM4IGFuZCBCRzIpIHJlY292ZXJlZCAxMyBpbnN0ZWFkIG9mIDMgaGl0cyAoaW4gdGhlIG5ldyByZWZzZXEgaGl0IGxpc3QpLiBJIHZlcmlmaWVkIHRoYXQgYWxsIG9mIHRoZW0gYXJlIGNyZWRpYmxlLiBBY3R1YWxseSwgWHUgX2V0IGFsLl8gMjAyMCBzdXBwbGVtZW50YXJ5IHRhYmxlIHNwZWNpZmljYWxseSBsaXN0ZWQgdGhlc2UgMTMgYXMgcHJlZGljdGVkIGFkaGVzaW5zIGJlbG9uZ2luZyB0byBjbHVzdGVyIFYsIHdoaWNoIGFsc28gaW5jbHVkZWQgQXdwMiBzdHVkaWVkIGJ5IFJlaXRob2ZlciBldCBhbCAyMDIxLiBJbmNsdWRpbmcgdGhlbSBpbiB0aGUgaG9tb2xvZ3MgbGlzdCB3b3VsZCBwcm9wZXJseSByZWZsZWN0IHRoZSBmYW1pbHkgc2l6ZSBpbiB0aGUgc3BlY2llcywgYnV0IHdpbGwgYWxzbyBoaWdobGlnaHQgdGhlIGlzc3VlIHRoYXQgaW5jb21wbGV0ZWx5IGFzc2VtYmxlZCBzdWJ0ZWxvbWVyZXMgbWF5IGhhdmUgbGVkIHRvIG1hbnkgZmFsc2UgbmVnYXRpdmVzIGluIHRoZSBnZW5lIGZhbWlseSBpbnZlbnRvcnkuIEkgdGhpbmsgaXQgaXMgd29ydGggaW5jbHVkaW5nIGl0IGFuZCBjb21tZW50aW5nIG9uIHRoZSBjYXZlYXRzLgoKYGBge3J9CmMuZ2xhYnJhdGEgPC0gcmVhZF90c3YoIi4uLy4uL2RhdGEvbG9uZy1yZWFkLWFzc2VtYmxpZXMvY19nbGFicmF0YV9DQlMxMzgvQy1nbGFicmF0YS1DQlMxMzgtSGlsLWhvbW9sb2dzLWJsYXN0LnR4dCIsCiAgICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9ICIjIiwgY29sX25hbWVzID0gaGVhZGVyWzE6MTBdLCBjb2xfdHlwZXMgPSBjb2xzKCkpICU+JSAKICBtdXRhdGUoc3BlY2llcyA9ICJDYW5kaWRhIGdsYWJyYXRhIiwgc3RyYWluID0gIkNCUyAxMzgiLCBzdGF4aWQgPSAiNTQ3OCIsCiAgICAgICAgIHBpZGVudCA9IG51bShwaWRlbnQsIGRpZ2l0cyA9IDEpKSAlPiUgCiAgcmVsb2NhdGUoc3BlY2llczpzdHJhaW4sIC5hZnRlciA9IHBpZGVudCkgJT4lIAogIGdyb3VwX2J5KHNpZCkgJT4lIAogIGZpbHRlcihldmFsdWUgPT0gbWluKGV2YWx1ZSkpIyAlPiUgCiAgI3JlbmFtZShwaWQgPSBzaWQpCgpjLmdsYWJyYXRhCmBgYAoKTm90ZSB0aGF0IEkgYWxzbyBjaGVja2VkIHRocmVlIG90aGVyIHNwZWNpZXMgZm9yIHdoaWNoIEkgY2FuIGZpbmQgYSByZWNlbnQgYXNzZW1ibHkgdGhhdCB1c2VkIGxvbmctcmVhZCB0ZWNobmxvZ29pZXMuIE5vbmUgb2YgdGhlbSBsZWQgdG8gc28gbWFueSBtb3JlIGhpdHMgYXMgdGhlIF9DLiBnbGFicmF0YV8gYXNzZW1ibHkgZGlkLiBJbiBmYWN0LCBvbmx5IHRoZSBfQy4gbml2YXJpZW5zaXNfIGFzc2VtYmx5IHlpZWxkZWQgb25lIG1vcmUgaGl0IHRoYW4gdGhlIG9sZGVyLCBzaG9ydC1yZWFkIGJhc2VkIGFzc2VtYmx5LiBUaGVyZSBtYXkgYmUgdHdvIHBvc3NpYmlsaXRpZXM6IDEpIG5vdCBhbGwgbmV3IGFzc2VtYmxpZXMgYXJlIGNyZWF0ZWQgZXF1YWwuIHRoZXJlIGFyZSBzb21lIHNwZWNpYWwgcHJvcGVydGllcyBvZiB0aGUgX0MuIGdsYWJyYXRhXyBhc3NlbWJseSB0aGF0IG1hZGUgaXRzIHN1YnRlbG9tZXJpYyByZWdpb25zIGVzcGVjaWFsbHkgd2VsbCBhc3NlbWJsZWQ7IDIpIHRoZXJlIGlzIHNvbWV0aGluZyBzcGVjaWFsIGFib3V0IF9DLiBnbGFicmF0YV8gaW4gdGhhdCB0aGUgSGlsIGZhbWlseSBleHBhbmRlZCBtb3JlIHNpZ25pZmljYW50bHkgdGhhbiBldmVuIHRoZSBjbG9zZWx5IHJlbGF0ZWQgTmFrYXNlb215Y2VzIHNwZWNpZXMuIEFzIG9mIG5vdyB3ZSBjYW5ub3QgZGlzdGluZ3Vpc2ggYmV0d2VlbiB0aGVzZSB0d28uCgojIyMgNS4gRnVuZ2lEQiBoaXRzCkZ1bmdpREIgZG9lc24ndCBpbmNsdWRlIGFueSBzcGVjaWVzIG5vdCBhbHJlYWR5IGluIHRoZSByZWZzZXEgZGF0YWJhc2UuIFRoZSBoaXRzIHRoYXQgd2VyZSBhZGRlZCB0byB0aGUgcmVmc2VxIGhpdHMgZnJvbSB0aGUgZWFybGllciBhbmFseXNpcyBhcmUKCmBgYHtyfQpwcmV2LmZkYi5hZGQgPC0gdHJpYmJsZSgKICB+Z2lkLCB+cGlkLCB+c3BlY2llcywKICAiQ0xVR18wNTIzMyIsICJYUF8wMDI2MTUyMTguMSIsICJDbGF2aXNwb3JhIGx1c2l0YW5pYWUiLAogICJDUEFSMl82MDA0MzAiLCAiWFBfMDM2NjYyODE1LjEiLCAiQ2FuZGlkYSBwYXJhcHNpbG9zaXMiLAogICJDUEFSMl84MDYzOTAiLCAiWFBfMDM2NjY1MjYyLjEiLCAiQ2FuZGlkYSBwYXJhcHNpbG9zaXMiLAogICJDUEFSMl84MDY0MjAiLCAiWFBfMDM2NjY1MjY1LjEiLCAiQ2FuZGlkYSBwYXJhcHNpbG9zaXMiLAogICJCOUowOF8wMDQwOTgiLCAiUElTNTI0ODEuMSIsICJDYW5kaWRhIGF1cmlzIgopCnByZXYuZmRiLmFkZCAlPiUgZmlsdGVyKCFwaWQgJWluJSBkZWR1cC5yZWYkc2lkKQpgYGAKPiBUaGUgZmlyc3QgZm91ciBhcmUgbm90IG1pc3NpbmcsIGp1c3QgYSBkaWZmZXJlbmNlIGluIHRoZSBJRCBmb3JtYXQuCj4gVGhlIG1pc3NpbmcgX0MuIGF1cmlzXyBzZXF1ZW5jZSBpcyBrbm93biB0byB1cyBhbmQgd2lsbCBiZSBhZGRlZCByZWdhcmRsZXNzLgo+IEJhc2VkIG9uIHRoZSBhYm92ZSwgd2Ugd2lsbCBubyBsb25nZXIgY29uc3VsdCB0aGUgRnVuZ2lEQiB0byBzdXBwbGVtZW50IHRoZSByZWZzZXEgaGl0cy4KCmBgYHtyfQpjLmF1cmlzIDwtIHJlYWRfdHN2KCIuLi8uLi9vdXRwdXQvWFBfMDI4ODg5MDMzLUNhdXJpcy1maXZlLXN0cmFpbnMtYmxhc3QudHh0IiwKICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gIiMiLCBjb2xfdHlwZXMgPSBjb2xzKCkpICU+JSAKICBmaWx0ZXIocXNlcWlkID09ICJYUF8wMjg4ODkwMzMuMV9QRjExNzY1Iiwgc3NlcWlkID09ICJQSVM1MjQ4MS4xIikgJT4lIAogIHJlbmFtZShzaWQgPSBzc2VxaWQpICU+JSAKICBtdXRhdGUoCiAgICBwaWQgPSBzaWQsCiAgICBxdWVyeSA9ICJDYXVyX0hpbDEiLCAKICAgICMgdGhlIGJsYXN0IHJlc3VsdCBmcm9tIHRoZSBhYm92ZSBmaWxlIHdhcyBkb25lIHVzaW5nIHRoZSBmaXJzdCAzMjUgYW1pbm8gYWNpZHMgYXMgcXVlcnkKICAgICMgSSBtYW51YWxseSBkaWQgdHdvIHNlcXVlbmNlIGJsYXN0IHdpdGggdGhlIFBGMTE3NjUgZnJvbSBIaWwxIGFzIHRoZSBxdWVyeSBhbmQgZW50ZXJlZCAKICAgICMgdGhlIHF1ZXJ5IGFuZCBzdWJqZWN0IHN0YXJ0IGFuZCBlbmQgcG9zaXRpb25zIGJlbG93CiAgICBxLnN0YXJ0ID0gMTIsIHEuZW5kID0gMzI2LCBzLnN0YXJ0ID0gMTEsIHMuZW5kID0gMzI1LAogICAgcWNvdnMgPSAocS5lbmQgLSBxLnN0YXJ0ICsgMSkvMzI1KjEwMCwKICAgIHNsZW4gPSAxMjQ3LAogICAgc3BlY2llcyA9ICJDYW5kaWRhIGF1cmlzIiwKICAgIHN0YXhpZCA9ICI0OTgwMTkiCiAgKSAlPiUgCiAgc2VsZWN0KHF1ZXJ5LCBzaWQsIHNsZW4sIHNwZWNpZXMsIHFjb3ZzLCBwaWRlbnQsIHEuc3RhcnQsIHEuZW5kLCBzLnN0YXJ0LCBzLmVuZCwgZXZhbHVlLCBzdGF4aWQsIHBpZCkKYGBgCgpJIG5vdyBjb25zaWRlciB0aGUgcmVmc2VxIGxpc3QgY29tcGxldGUsIHdlIHdpbGwgZ2F0aGVyIHRoZSBmaW5hbCBsaXN0IG9mIElEcyBhbmQgcmV0cmlldmUgdGhlaXIgc2VxdWVuY2VzLgoKUmV0cmlldmUgcHJvdGVpbiBpbmZvIChubyBtUk5BIGluZm9ybWF0aW9uIGlzIGF2YWlsYWJsZSkKYGBge3J9CmlkLmFkZCA8LSBjKGMuZ2xhYnJhdGEkc2lkLCBzLnN0aXBpdGlzJHNpZCwgYy5hdXJpcyRzaWQpCnBTdW0xIDwtIHJldHJpZXZlX3Byb3RlaW5faW5mbyhpZC5hZGQpCiMgZXh0cmFjdCB0aGUgcHJvdGVpbiBjb21wbGV0ZW5lc3MgaW5mbwpwSW5mbzEgPC0gZXh0cmFjdF9mcm9tX2VzdW1tYXJ5KHBTdW0xLCBlbGVtZW50cyA9IGMoImNvbXBsZXRlbmVzcyIpLCBzaW1wbGlmeSA9IEZBTFNFKSAlPiUgCiAgICBiaW5kX3Jvd3MoLmlkID0gInNpZCIpICU+JSAKICAgIHJlbmFtZShwQ29tcGxldGUgPSBjb21wbGV0ZW5lc3MpCmBgYAoKUmVwbGFjZSBfQy4gZ2xhYnJhdGFfIGFuZCBfUy4gc3RpcGl0aXNfIGluIHJlZnNlcSBoaXQgbGlzdC4gQWRkIHRoZSBfQy4gYXVyaXNfIEhpbDQgZnJvbSBCODQ0MS4KYGBge3J9CmRlZHVwLnJlZi5jZyA8LSBkZWR1cC5yZWYgJT4lIAogIGZpbHRlcighc3BlY2llcyAlaW4lIGMoIkNhbmRpZGEgZ2xhYnJhdGEiLCAiU2NoZWZmZXJzb215Y2VzIHN0aXBpdGlzIikpICU+JSAKICBiaW5kX3Jvd3MoZmlsdGVyKHMuc3RpcGl0aXMsIHFjb3ZzID49IDUwKSkgJT4lIAogIGJpbmRfcm93cyhmaWx0ZXIoYy5nbGFicmF0YSwgcWNvdnMgPj0gNTApKSAlPiUgCiAgYmluZF9yb3dzKGZpbHRlcihjLmF1cmlzLCBxY292cyA+PSA1MCkpICU+JSAKICByb3dzX3VwZGF0ZShwSW5mbzEsIGJ5ID0gInNpZCIsIHVubWF0Y2hlZCA9ICJpZ25vcmUiKSAlPiUgCiAgbXV0YXRlKHBpZCA9IGdzdWIoIi4qXFx8KC4qKVxcfC4qIiwgIlxcMSIsIHNpZCkpCmBgYAoKClJldHJpZXZlIHNlcXVlbmNlcwpgYGB7cn0KaXRlci5waWQxIDwtIHNwbGl0KHggPSBkZWR1cC5yZWYuY2ckcGlkLCBmID0gY2VpbGluZyhzZXFfYWxvbmcoZGVkdXAucmVmLmNnJHBpZCkvNTApKQpyZWYuc2VxIDwtIGxhcHBseShpdGVyLnBpZDEsIGZ1bmN0aW9uKHgpewogIGVudHJlel9mZXRjaChkYiA9ICJwcm90ZWluIiwgaWQgPSB4LCByZXR0eXBlID0gImZhc3RhIikKfSkKYGBgCgpgYGB7cn0KIyBtZXJnZSBhbGwgdGhlIHNlcXVlbmNlcyBhbmQgd3JpdGUgdG8gZmlsZQpyZWYuc2VxICU+JSBwYXN0ZTAoY29sbGFwc2UgPSAiIikgJT4lIAogIHdyaXRlKGZpbGUgPSAiLi4vLi4vb3V0cHV0LzIwMjIwNTAzLWV4cGFuZGVkLWJsYXN0LXJlZnNlcS1ob21vbG9ncy5mYWEiKQpgYGAKCiMjIyA2LiBHUllDIGhpdHMKR1JZQyBkaWQgaW5jbHVkZSBhIGZldyBtb3JlIHNwZWNpZXMsIGVzcGVjaWFsbHkgaW4gdGhlIE5ha2FzZW9teWNlcyBhbmQgTGFjaGFuY2VhIGdlbmVyYS4gV2hpbGUgdGhlIHNhbWUgYXNzZW1ibHkgZGF0YSBhcHBlYXIgdG8gZXhpc3QgaW4gdGhlIG5jYmkgZGF0YWJhc2UsIHRoZXkgYXJlIGNlcnRhaW5seSBub3QgaW5jbHVkZWQgaW4gdGhlIHJlZnNlcSBkYXRhYmFzZS4gTW9yZW92ZXIsIGV2ZW4gd2hlbiB0aGUgYXNzZW1ibGllcyBhcmUgcHJlc2VudCBpbiB0aGUgbmNiaSBkYXRhYmFzZSwgdGhlcmUgYXJlIG9ubHkgdGhlIGdlbm9taWMgc2VxdWVuY2VzLCBub3QgdHJhbnNsYXRlZCBDRFMsIHdoaWNoIGxpa2VseSBtYWtlIHRoZW0gdW5hdmFpbGFibGUgdG8gYmxhc3RwIHNlYXJjaGVzLiBUaGVyZWZvcmUgSSBkb3dubG9hZGVkIHRoZSBmaXZlIG5vbi1nbGFicmF0YSBOYWthc2VvbXljZXMgZ2Vub21lcyBwbHVzIG9uZSBMYWNoYW5jZWEgc3BlY2llcywgX0wuIGtsdXl2ZXJpXyBhbmQgcGVyZm9ybWVkIGxvY2FsIGB0Ymxhc3RuYCBzZWFyY2hlcy4gQmVsb3cgSSB3aWxsIHByb2Nlc3MgdGhlIHJlc3VsdHMgYW5kIGRlY2lkZSB3aGljaCBvbmVzIHRvIGluY2x1ZGUgaW4gdGhlIGZpbmFsIGxpc3QuCmBgYHtyfQpncnljLnNwcyA8LSB0cmliYmxlKAogIH5zcHMuYWJyLCB+c3BlY2llcywgfnN0cmFpbiwgfnN0YXhpZCwKICAiQ0FOSSIsICJDYW5kaWRhIG5pdmFyaWVuc2lzIiwgIkNCUyA5OTgzIiwgIjEyNzkxMTUiLAogICJOQURFIiwgIk5ha2FzZW9teWNlcyBkZWxwaGVuc2lzIiwgIkNCUyAyMTcwIiwgIjEyNzkxMTMiLAogICJOQUJBIiwgIk5ha2FzZW9teWNlcyBiYWNpbGxpc3BvcnVzIiwgIkNCUyA3NzIwIiwgIjEyNzkxMTYiLAogICJDQUNBIiwgIkNhbmRpZGEgY2FzdGVsbGlpIiwgIkNCUyA0MzMyIiwgIjEyNzkwODYiLAogICJDQUJSIiwgIkNhbmRpZGEgYnJhY2FyZW5zaXMiLCAiQ0JTIDEwMTU0IiwgIjEyNzkxMTQiLAogICJTQUtMIiwgIkxhY2hhbmNlYSBrbHV5dmVyaSIsICJDQlMgMzA4MiIsICI0OTM0IgopCgpncnljIDwtIHJlYWRfdHN2KCIuLi8uLi9kYXRhL05ha2FzZW9teWNlc19HUllDL05ha2FzZW9teWNlcy1IaWwtaG9tb2xvZ3MtYmxhc3QudHh0IiwKICAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gIiMiLCBjb2xfbmFtZXMgPSBoZWFkZXJbMToxMF0sIGNvbF90eXBlcyA9IGNvbHMoKSkgJT4lIAogIG11dGF0ZShzcHMuYWJyID0gZ3N1YigiQk5bMC05XSpfIiwgIiIsIHNpZCkgJT4lIHN0cl9zdWIoMSw0KSwgcGlkZW50ID0gbnVtKHBpZGVudCwgZGlnaXRzID0gMSkpICU+JSAKICBsZWZ0X2pvaW4oZ3J5Yy5zcHMsIGJ5ID0gInNwcy5hYnIiKSAlPiUgCiAgcmVsb2NhdGUoc3BlY2llczpzdHJhaW4sIC5hZnRlciA9IHBpZGVudCkgJT4lIAogIHNlbGVjdCgtc3BzLmFicikKCmRlZHVwLmdyeWMgPC0gZ3J5YyAlPiUgCiAgZ3JvdXBfYnkoc2lkKSAlPiUgCiAgZmlsdGVyKGV2YWx1ZSA9PSBtaW4oZXZhbHVlKSkgJT4lIAogIG11dGF0ZShwaWQgPSBnc3ViKCJCTlswLTldKl8iLCAiIiwgc2lkKSAlPiUgZ3N1YigiMV8xJCIsICIiLCAuKSkgJT4lIAogIGZpbHRlcihxY292cyA+PSA1MCkgJT4lIAogIHVuZ3JvdXAoKQpgYGAKCkV4dHJhY3QgdGhlIHByb3RlaW4gc2VxdWVuY2VzIGZyb20gdGhlIGNvbWJpbmVkIGZhc3RhCmBgYHtyfQpjYXQoZGVkdXAuZ3J5YyRzaWQsIGZpbGUgPSAiMjAyMjA1MDYtZ3J5Yy1kZWR1cGxpY2F0ZWQtaWQtZm9yLWV4dHJhY3RpbmctZmFzdGEudHh0Iiwgc2VwID0gIlxuIikKYGBgCgpSdW4gdGhlIGZvbGxvd2luZyBhdCB0aGUgY29tbWFuZCBsaW5lIGFzc3VtaW5nIHRoZSBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5IGlzIGAwMi1ibGFzdGAKYGBgYmFzaApweXRob24zIHNjcmlwdC9leHRyYWN0X2Zhc3RhX2d6LnB5IFwKICBkYXRhL05ha2FzZW9teWNlc19HUllDLzIwMjIwNTAzLWdyeWMtbmFrYXNlb215Y2VzLXByb3RlaW4uZmFhLmd6IFwKICBhbmFseXNpcy9leHBhbmRlZC1ibGFzdC8yMDIyMDUwNi1ncnljLWRlZHVwbGljYXRlZC1pZC1mb3ItZXh0cmFjdGluZy1mYXN0YS50eHQgXAogIG91dHB1dC8yMDIyMDUwNi1leHBhbmRlZC1ibGFzdC1ncnljLWhvbW9sb2dzLmZhYQpgYGAKCk1lcmdlIHRoZSBHUllDIHNlcXVlbmNlcyB3aXRoIHRoZSByZWZzZXEgc2VxdWVuY2VzIC0gcnVuIHRoZSBmb2xsb3dpbmcgYXQgdGhlIGNvbW1hbmQgbGluZQpgYGBiYXNoCmNhdCAyMDIyMDUwMy1leHBhbmRlZC1ibGFzdC1yZWZzZXEtaG9tb2xvZ3MuZmFhIFwKICAyMDIyMDUwNi1leHBhbmRlZC1ibGFzdC1ncnljLWhvbW9sb2dzLmZhYSBcCiAgPiAyMDIyMDUwNi1leHBhbmRlZC1ibGFzdC1jb21iaW5lZC1ob21vbG9ncy5mYWEKYGBgCgpDaGVjayB0byBtYWtlIHN1cmUgdGhhdCBubyBkdXBsaWNhdGVzIGV4aXN0LgoKYGBgCmJpb2F3ayAtYyBmYXN0eCAne3ByaW50ICRuYW1lfScgMjAyMjA1MDYtZXhwYW5kZWQtYmxhc3QtY29tYmluZWQtaG9tb2xvZ3MuZmFhIHwgc29ydCB8IHVuaXEgLWQKYGBgCgpJZiB0aGVyZSBpcyBubyBvdXRwdXQsIHRoZW4gdGhlcmUgaXMgbm8gZHVwbGljYXRlcy4gVGhlIGNvbW1hbmQgd2lsbCBvdXRwdXQgb25lIGxpbmUgcGVyIHNlcXVlbmNlIHRoYXQgb2NjY3VycyBtb3JlIHRoYW4gb25jZSBpbiB0aGUgZmlsZS4KCiMjIyA3LiBNZXJnZSBhbGwgdGhlIHNlcXVlbmNlcwpFeHBvcnQgdGhlIGNvbWJpbmVkIGhvbW9sb2dzIGxpc3QKYGBge3J9CmRlZHVwLm1lcmdlIDwtIGJpbmRfcm93cygKICBSZWZTZXEgPSBkZWR1cC5yZWYuY2csCiAgR1JZQyA9IGRlZHVwLmdyeWMsCiAgLmlkID0gInNvdXJjZSIKICApICU+JSAKICBtdXRhdGUobmFtZSA9IHBhc3RlKHBpZCwgZ3N1YigiICIsICJfIiwgc3BlY2llcyksIHNlcCA9ICJfIikpICU+JSAKICBzZWxlY3QoLXEuc3RhcnQsIC1xLmVuZCwgLXMuc3RhcnQsIC1zLmVuZCkgJT4lIAogIHVuZ3JvdXAoKQp3cml0ZV90c3YoZGVkdXAubWVyZ2UsIGZpbGUgPSAiLi4vLi4vb3V0cHV0LzIwMjIwNTA2LWV4cGFuZGVkLWJsYXN0LWNvbWJpbmVkLWhvbW9sb2dzLnRzdiIpCmBgYAoKIyMjIDguIENvbXBhcmUgd2l0aCB0aGUgZm9ybWVyIDEwNCBsaXN0CkhpdHMgaW4gdGhlIHByZXZpb3VzIHJlZnNlcSBibGFzdCBsaXN0IGJ1dCBub3QgaW4gdGhlIGN1cnJlbnQgb25lOgpgYGB7cn0KcDEwNCA8LSByZWFkX3RzdigiLi4vLi4vb3V0cHV0L1hQXzAyODg4OTAzM19ob21vbG9nc19jb21iaW5lX2JsYXN0X3N0YXQudHN2IiwgY29sX3R5cGVzID0gY29scygpKQpnZ3Zlbm4obGlzdChleHBhbmRlZF9ibGFzdCA9IGRlZHVwLm1lcmdlJHBpZCwgcHJldmlvdXNfMTA0ID0gcDEwNCRpZCkpCnAxMDQgJT4lIGZpbHRlcighaWQgJWluJSBnc3ViKCJCTlswLTldKl8iLCAiIiwgZGVkdXAubWVyZ2UkcGlkKSkgJT4lIGFycmFuZ2Uoc3BlY2llcykKYGBgCj4gLSBUaGUgZm91ciBfUy4gc3RpcGl0aXNfIHNlcXVlbmNlcyBoYXZlIGJlZW4gcmVwbGFjZWQgYnkgdGhlIG5ldyBhc3NlbWJseQo+IC0gVGhlIF9DLiBhdXJpc18sIF9DLiBnbGFicmF0YV8sIF9DLiBwYXJhc2lsb3Npc18gYW5kIF9DbGF2aXNwb3JhIGx1c2l0YW5pYWVfIHNlcXVlbmNlcyBhcmUgaW4gZmFjdCBhbGwgcHJlc2VudCBpbiB0aGUgbmV3IGxpc3QsIGp1c3QgdW5kZXIgZGlmZmVyZW50IElEcwoKIyMjIDkuIElkZW50aWZ5IHRoZSBQRjExNzY1IGRvbWFpbgpVc2UgSE1NRVIgdG8gaWRlbnRpZnkgdGhlIGxvY2F0aW9uIG9mIHRoZSBQRjExNzY1IGRvbWFpbi4gVGhlIGhtbSBtb2RlbCB3YXMgZG93bmxvYWRlZCBmcm9tIHRoZSBwZmFtIGRhdGFiYXNlIG9uIDIwMjItMDUtMDQuIFJ1biB0aGUgZm9sbG93aW5nIGNvbW1hbmQgaW4gYDAyLWJsYXN0YCBkaXJlY3RvcnkuCmBgYApobW1zY2FuIC0tbm9hbGkgLS1kb21FIDFlLTMgXAogIC1vIDIwMjIwNTA0LWhtbXNjYW4ubG9nIFwKICAtLWRvbXRibG91dCBvdXRwdXQvMjAyMjA1MDYtZXhwYW5kZWQtYmxhc3QtY29tYmluZWQtUEYxMTc2NS1obW1zY2FuLnR4dCBcCiAgZGF0YS9ITU0tcHJvZmlsZS9IeXBoYWxfcmVnX0NXUC5obW0gXAogIG91dHB1dC8yMDIyMDUwNi1leHBhbmRlZC1ibGFzdC1jb21iaW5lZC1ob21vbG9ncy5mYWEKYGBgCgpSZWFkIHRoZSByZXN1bHQKYGBge3Igd2FybmluZz1GQUxTRX0KaG1tLmhlYWRlciA8LSBjKCJ0YXJnZXQiLCAidGFjYyIsICJ0bGVuIiwgInBpZCIsICJwYWNjIiwgInNsZW4iLCAiZnVsbC5ldmFsdWUiLCAiZnVsbC5zY29yZSIsICJmdWxsLmJpYXMiLAogICAgICAgICAgICAgICAgIm51bSIsICJvZiIsICJjLmV2YWx1ZSIsICJpLmV2YWx1ZSIsICJkb21haW4uc2NvcmUiLCAiZG9tYWluLmJpYXMiLAogICAgICAgICAgICAgICAgImhtbS5mcm9tIiwgImhtbS50byIsICJhbGkuZnJvbSIsICJhbGkudG8iLCAiZW52LmZyb20iLCAiZW52LnRvIiwgImFjYyIpCmhtbSA8LSByZWFkX3RhYmxlKCIuLi8uLi9vdXRwdXQvMjAyMjA1MDYtZXhwYW5kZWQtYmxhc3QtY29tYmluZWQtUEYxMTc2NS1obW1zY2FuLnR4dCIsIAogICAgICAgICAgICAgICAgICBjb21tZW50ID0gIiMiLCBjb2xfbmFtZXMgPSBobW0uaGVhZGVyLCBjb2xfdHlwZXMgPSBjb2xzKCkpICU+JSAKICBtdXRhdGUoc2lkID0gcGlkLCBwaWQgPSBnc3ViKCJCTlswLTldKl8iLCAiIiwgcGlkKSAlPiUgZ3N1YigiMV8xJCIsICIiLCAuKSkgJT4lIAogIHNlbGVjdChzaWQsIHBpZCwgbnVtLCBvZiwgaS5ldmFsdWU6ZW52LnRvKQpkZWR1cC5tZXJnZSAlPiUgZmlsdGVyKCFwaWQgJWluJSBobW0kcGlkKQpgYGAKCkN1cmlvdXMgd2h5IGBobW1zY2FuYCBkaWRuJ3QgaWRlbnRpZnkgdGhlIFBGMTE3NjUgZG9tYWluIGluIHRoZSB0d28gc2VxdWVuY2VzLCBJIHN1Ym1pdHRlZCB0aGUgc2FtZSBzZXF1ZW5jZSBmaWxlIHRvIFtgQmF0Y2ggQ0Qtc2VhcmNoYF0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9TdHJ1Y3R1cmUvYndycHNiL2J3cnBzYi5jZ2kpLCB3aGljaCB1c2VzIHRoZSBSUFMtQkxBU1QgdG8gc2VhcmNoIGEgY29sbGVjdGlvbiBvZiBwcm90ZWluIHNlcXVlbmNlcyBmb3IgbWF0Y2hlcyB0byBhIHNldCBvZiBwcm9maWxlIG1hdHJpY2VzLiBJdCBpcyB0aGUgcmV2ZXJzZSBvZiB0aGUgUFNJLUJMQVNULgpgYGB7cn0KY2RzZWFyY2ggPC0gcmVhZF90c3YoIi4uLy4uL291dHB1dC8yMDIyMDUwOS1leHBhbmRlZC1ibGFzdC1jb21iaW5lZC1wZmFtLWNkLXNlYXJjaC1vdXRwdXQudHh0Iiwgc2tpcCA9IDcsCiAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IGNvbHMoKSkgJT4lIAogIGZpbHRlcihncmVwbCgiSHlwaGFsIiwgYFNob3J0IG5hbWVgKSkgJT4lCiAgbXV0YXRlKHNpZCA9IHN0cl9tYXRjaChRdWVyeSwgIj4oW2EtekEtWjAtOV8uXSopICIpWywyXSwKICAgICAgICAgcGlkID0gZ3N1YigiQk5bMC05XSpfIiwgIiIsIHNpZCkgJT4lIGdzdWIoIjFfMSQiLCAiIiwgLikpICU+JSAKICBzZWxlY3Qoc2lkLCBwaWQsIGV2ZXJ5dGhpbmcoKSwgLVF1ZXJ5KQpjZHNlYXJjaCAlPiUgZmlsdGVyKHBpZCAlaW4lIHNldGRpZmYoZGVkdXAubWVyZ2UkcGlkLCBobW0kcGlkKSkgJT4lIHNlbGVjdCgtc2lkKQpgYGAKPiBCb3RoIHNlcXVlbmNlcyBkaWQgaGF2ZSBhIG1hdGNoLCBhbHRob3VnaCBvbmUgb2YgdGhlbSBoYXMgYSAic3VwZXJmYW1pbHkiIGxldmVsIG1hdGNoLCBzdWdnZXN0aW5nIHRoYXQgbm8gaGl0cyBleGNlZWQgdGhlIGRvbWFpbiBsZXZlbCBFLXZhbHVlLCBidXQgbXVsdGlwbGUgaGl0cyBtZXQgdGhlICJub24tc3BlY2lmaWMiIGNyaXRlcmlhLiBOb3Qgc3VyZSB3aHkgaG1tc2NhbiBkaWRuJ3QgcGljayB1cCB0aGUgZG9tYWluIGluIHRoZSBfQy4gZ2xhYnJhdGFfIHNlcXVlbmNlIHRob3VnaAoKTWVyZ2UgdGhlIENELVNlYXJjaCBhbmQgSE1NU0NBTSByZXN1bHRzIGludG8gb25lLiBJJ20gdXNpbmcgdGhlIGVudmVsb3BlIGNvb3JkaW5hdGVzIGZyb20gYGhtbXNjYW5gIHJhdGhlciB0aGFuIHRoZSBhbGlnbm1lbnQgY29vcmRpbmF0ZXMuIEFjY29yZGluZyB0byBbUEZhbSBkb2N1bWVudGF0aW9uXShodHRwczovL3BmYW0tZG9jcy5yZWFkdGhlZG9jcy5pby9lbi9sYXRlc3QvZ2xvc3NhcnkuaHRtbCNhbGlnbm1lbnQtY29vcmRpbmF0ZXMpLCB0aGUgZW52ZWxvcGUgY29vcmRpbmF0ZXMgcmVmbGVjdCB0aGUgcmVnaW9uIHdoZXJlIHRoZSBtYXRjaCBoYXMgYmVlbiAqcHJvYmFiaWxpc3RpY2FsbHkqIGRldGVybWluZWQgdG8gbGllLiBJdCB3aWxsIGJlIHNsaWdodGx5IHdpZGVyIHRoYW4gdGhlIGFsaWdubWVudC4KYGBge3J9CmhtbS5jZCA8LSBjZHNlYXJjaCAlPiUgZmlsdGVyKGdyZXBsKCJIeXBoYWxfcmVnX0NXUCIsIGBTaG9ydCBuYW1lYCkpICU+JSAKICBzZWxlY3Qoc2lkLCBwaWQsIGNkLnR5cGUgPSBgSGl0IHR5cGVgLCBjZC5pbmNvbSA9IEluY29tcGxldGUsIGNkLmZyb20gPSBGcm9tLCBjZC50byA9IFRvKSAlPiUgCiAgZnVsbF9qb2luKHNlbGVjdChobW0sIHNpZCwgcGlkLCBhbGkuZnJvbSwgYWxpLnRvLCBlbnYuZnJvbSwgZW52LnRvKSkgJT4lIAogIG11dGF0ZShjZC5sZW4gPSBjZC50byAtIGNkLmZyb20gKyAxLAogICAgICAgICBhbGkubGVuID0gYWxpLnRvIC0gYWxpLmZyb20gKyAxLAogICAgICAgICBlbnYubGVuID0gZW52LnRvIC0gZW52LmZyb20gKyAxKQpobW0uY2QgJT4lIGdyb3VwX2J5KHBpZCkgJT4lIGZpbHRlcihuKCkgPiAxKSAlPiUgc2VsZWN0KC1zaWQsIC1lbmRzX3dpdGgoInRvIikpCmBgYAoKPiAtIEFib3ZlIGFyZSB0aGUgc2VxdWVuY2VzIHdpdGggbW9yZSB0aGFuIG9uZSBlbnRyaWVzIGZyb20gZWl0aGVyIHNlYXJjaC4gTm90ZSB0aGF0IHdpdGggdGhlIHBhcmFtZXRlcnMgdXNlZCBmb3IgQ0QtU2VhcmNoLCBpdCBhcHBlYXJzIHRoYXQgdGhlIGFsZ29yaXRobSBvbmx5IHJlcG9ydHMgdGhlIHN0cm9uZ2VzdCBtYXRjaCAoc3BlY2lmaWMpLiBUaGlzIGNhbiBwcm9iYWJseSBiZSBjaGFuZ2VkIGJ5IHN3aXRjaGluZyB0byB0aGUgZnVsbCByZXBvcnQgbW9kZS4gQnV0IGl0J3Mgbm90IGltcG9ydGFudCBhcyBJIHBsYW4gdG8gdXNlIHRoZSBITU0gZW52ZWxvcGUgY29vcmRpbmF0ZXMgd2hlbmV2ZXIgYXZhaWxhYmxlLgo+IC0gVGhlIHNlY29uZCBtYXRjaCBpbiBYUF8wMDM2NzE4OTIuMSBpcyB2ZXJ5IHNob3J0LiBSZW1vdmUgaXQuCj4gLSBUaGUgc2Vjb25kIG1hdGNoIGluIE5BREUwczAzZTAwMTEwZyBsYXJnZWx5IG92ZXJsYXBzIHdpdGggdGhlIGZpcnN0IG9uZS4gUmVtb3ZlLgoKYGBge3J9CmRvbWFpbi5maW5hbCA8LSBobW0uY2QgJT4lIAogICMgcmVtb3ZlIHRoZSBzaG9ydCBoaXQgYW5kIGR1cGxpY2F0ZXMgZHVlIHRvIHRoZSBqb2luIG9wZXJhdGlvbiwgYnV0IHNhdmUgdGhlIHJvd3Mgd2hlcmUgaG1tc2NhbiBkaWRuJ3QgZmluZCBhIG1hdGNoCiAgZmlsdGVyKGlzLm5hKGFsaS5mcm9tKSB8IChhYnMoYWxpLmZyb20gLSBjZC5mcm9tKSA8IDEwMDAgJiBhbGkubGVuID4gMTAwKSkgJT4lIAogIGZpbHRlcighKHBpZCA9PSAiTkFERTBzMDNlMDAxMTBnIiAmIGFsaS5mcm9tID09IDM5KSkgIyByZW1vdmUgdGhlIG92ZXJsYXBwaW5nIGVudHJ5CmRvbWFpbi5maW5hbCAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoY2QubGVuLCBlbnYubGVuKSwgdmFsdWVzX3RvID0gImxlbmd0aCIsIG5hbWVzX3RvID0gIm1ldGhvZCIpICU+JQogIG11dGF0ZShtZXRob2QgPSBmYWN0b3IobWV0aG9kLCBsZXZlbHMgPSBjKCJjZC5sZW4iLCAiZW52LmxlbiIpLCBsYWJlbHMgPSBjKCJDRC1TZWFyY2ggaGl0cyIsICJITU1TQ0FOIGhpdHMiKSkpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBsZW5ndGgpKSArIAogIGdlb21faGlzdG9ncmFtKGFlcyhmaWxsID0gY2QuaW5jb20gIT0gIi0iKSwgYmlucyA9IDMwKSArIAogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDMyOCwgbGluZXR5cGUgPSAyKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIoIkluY29tcGxldGUiLCB0eXBlID0gInF1YWwiKSArCiAgZmFjZXRfd3JhcCh+bWV0aG9kLCBucm93ID0gMiwgKSArIHRoZW1lX2Nvd3Bsb3QoKQpgYGAKPiBTb21lIGFyZSBpbmNvbXBsZXRlIG1hdGNoZXMuCgpgciBkb21haW4uZmluYWwgJT4lIGZpbHRlcihjZC5pbmNvbSAhPSAiLSIpYAoKV3JpdGUgdGhlIGNvb3JkaW5hdGVzIGludG8gYSBCRUQgZm9ybWF0dGVkIGZpbGUgZm9yIGV4dHJhY3RpbmcgdGhlIHNlcXVlbmNlcy4KYGBge3J9CmRvbWFpbi5maW5hbCAlPiUgCiAgbXV0YXRlKAogICAgIyBpZiBobW0gaGl0cyBhcmUgbm90IGF2YWlsYWJsZSwgdXNlIGNkLXNlYXJjaCBoaXQgY29vcmRpbmF0ZXMsIHdpdGggMyBicCBleHBhbnNpb24gb24gYm90aCBzaWRlcwogICAgIyBub3RlIHRoYXQgQkVEIGZvcm1hdCBpcyAwLWJhc2VkIGZvciB0aGUgc3RhcnQgcG9zaXRpb24KICAgIGZyb20gPSBpZmVsc2UoaXMubmEoZW52LmZyb20pLCBjZC5mcm9tIC0gMyAtIDEsIGVudi5mcm9tIC0gMSksIAogICAgdG8gPSBpZmVsc2UoaXMubmEoZW52LnRvKSwgY2QudG8gKyAzLCBlbnYudG8pKSAlPiUgCiAgc2VsZWN0KHNlcSA9IHNpZCwgZnJvbSwgdG8pICU+JSAKICAjIHRoZSBmb2xsb3dpbmcgc3RhdGVtZW50IGlzIHRvIHJlbW92ZSB0aGUgc2Vjb25kIFBGMTE3NjUgZG9tYWluIGZyb20gb25lIHByb3RlaW4gZm9yIHRoZSBwdXJwb3NlIG9mIGdlbmUgdHJlZQogIGdyb3VwX2J5KHNlcSkgJT4lIAogIGZpbHRlcihmcm9tID09IG1pbihmcm9tKSkgJT4lIAogIHdyaXRlX3RzdihmaWxlID0gIi4uLy4uL291dHB1dC8yMDIyMDUxMi1leHBhbmRlZC1ibGFzdC1jb21iaW5lZC1ob21vbG9ncy1QRjExNzY1LkJFRCIsIGNvbF9uYW1lcyA9IEZBTFNFKQpgYGAKCkFsc28gd3JpdGUgYSB2ZXJzaW9uIHdpdGggdGhlIGxvbmcgbmFtZSBhbmQgbm90IHJlbW92aW5nIHRoZSBzZWNvbmQgZG9tYWluIGZyb20gdGhlIG9uZSBzZXF1ZW5jZSwgZm9yIHRoZSBwdXJwb3NlIG9mIHBsb3R0aW5nIGFuZCBjYWxjdWxhdGluZyBzZXF1ZW5jZSBsZW5ndGggLSBQRjExNzY1CmBgYHtyfQpkb21haW4uZmluYWwgJT4lIAogIG11dGF0ZSgKICAgIGZyb20gPSBpZmVsc2UoaXMubmEoZW52LmZyb20pLCBjZC5mcm9tIC0gMyAtIDEsIGVudi5mcm9tIC0gMSksIAogICAgdG8gPSBpZmVsc2UoaXMubmEoZW52LnRvKSwgY2QudG8gKyAzLCBlbnYudG8pKSAlPiUgCiAgc2VsZWN0KHNlcSA9IHBpZCwgZnJvbSwgdG8pICU+JSAjIHNlbGVjdCBwaWQgZm9yIG1lcmdpbmcgd2l0aCBkZWR1cC5tZXJnZQogIGxlZnRfam9pbihzZWxlY3QoZGVkdXAubWVyZ2UsIHNlcSA9IHBpZCwgbmFtZSksIGJ5ID0gInNlcSIpICU+JSAKICB1bmdyb3VwICU+JSBzZWxlY3QobmFtZSwgZnJvbSwgdG8pICU+JSAKICB3cml0ZV90c3YoZmlsZSA9ICIuLi8uLi9vdXRwdXQvMjAyMjA2MjMtZXhwYW5kZWQtYmxhc3QtY29tYmluZWQtaG9tb2xvZ3MtUEYxMTc2NS1sb25nbmFtZS5CRUQiLCBjb2xfbmFtZXMgPSBGQUxTRSkKYGBgCgpUbyBleHRyYWN0IHRoZSBzZXF1ZW5jZSBtYXRjaGVzIHRvIHRoZSBQRjExNzY1IHByb2ZpbGUsIGV4ZWN1dGUgdGhlIGZvbGxvd2luZyBzaGVsbCBjb21tYW5kCmBgYApzZXF0ayBzdWJzZXEgMjAyMjA1MDYtZXhwYW5kZWQtYmxhc3QtY29tYmluZWQtaG9tb2xvZ3MuZmFhIFwKICAgICAgICAgICAgIDIwMjIwNTEyLWV4cGFuZGVkLWJsYXN0LWNvbWJpbmVkLWhvbW9sb2dzLVBGMTE3NjUuQkVEICA+IFwKICAgICAgICAgICAgIDIwMjIwNTEyLWV4cGFuZGVkLWJsYXN0LWNvbWJpbmVkLWhvbW9sb2dzLVBGMTE3NjUuZmFhCmBgYAoKIyMjIDEwLiBSZW5hbWUgdGhlIHNlcXVlbmNlcyBmb3IgZ2VuZSB0cmVlIHJlY29uc3RydWN0aW9uClRvIGZhY2lsaXRhdGUgZ2VuZSB0cmVlIHJlY29uc3RydWN0aW9uLCByZW5hbWUgdGhlIHNlcXVlbmNlIG5hbWVzIGluIHRoZSBmb3JtIG9mIElEX3NwZWNpZXNfbmFtZSwgYW5kIGdldCByaWQgb2YgdGhlIGRlc2NyaXB0aW9uCgpgYGB7cn0KcmVuYW1lciA8LSBkZWR1cC5tZXJnZSAlPiUgCiAgIyBhIGJpdCBjb21wbGljYXRlZDogaW4gdGhlIHNlcXVlbmNlIGZpbGUsIHNlcXVlbmNlIG5hbWVzIGZyb20gUmVmc2VxIHNlcXVlbmNlcyB3ZXJlIG1vZGlmaWVkIAogICMgd2hpbGUgc2VxdWVuY2VzIGZyb20gR1JZQyB3ZXJlIG5vdAogIG11dGF0ZShpZCA9IGlmZWxzZShzb3VyY2UgPT0gIlJlZlNlcSIsIHBpZCwgc2lkKSkgJT4lIAogIHNlbGVjdChpZCwgbmFtZSkgJT4lIAogIGRlZnJhbWUoKQpteUZhc3RhUmVuYW1lIDwtIGZ1bmN0aW9uKGZpbGUsIG91dCl7CiAgaW5wdXQgPC0gcmVhZC5mYXN0YShmaWxlLCBzZXF0eXBlID0gIkFBIiwgYXMuc3RyaW5nID0gVFJVRSkKICBpZihhbnkoZ3JlcGwoIjoiLCBuYW1lcyhpbnB1dCkpKSl7CiAgICAjIGRlYWwgd2l0aCB0aGUgc2VxdGsgb3V0cHV0CiAgICBuYW1lcyhpbnB1dCkgPC0gZ3N1YigiOi4qJCIsICIiLCBuYW1lcyhpbnB1dCkpCiAgfQogIGlmKCFhbGwobmFtZXMoaW5wdXQpICVpbiUgbmFtZXMocmVuYW1lcikpKXsKICAgIHdhcm5pbmcoIlRoZSBmb2xsb3dpbmcgc2VxdWVuY2VzIHdlcmUgbm90IGZvdW5kIGluIHRoZSByZW5hbWUgbGlzdDoiKQogICAgcHJpbnQoc2V0ZGlmZihuYW1lcyhpbnB1dCksIG5hbWVzKHJlbmFtZXIpKSkKICB9CiAgd3JpdGUuZmFzdGEoaW5wdXQsIG5hbWVzID0gcmVuYW1lcltuYW1lcyhpbnB1dCldLCBmaWxlLm91dCA9IG91dCwgbmJjaGFyID0gNTAwMCkKfQpgYGAKYGBge3J9Cm15RmFzdGFSZW5hbWUoZmlsZSA9ICIuLi8uLi9vdXRwdXQvMjAyMjA1MDYtZXhwYW5kZWQtYmxhc3QtY29tYmluZWQtaG9tb2xvZ3MuZmFhIiwgCiAgICAgICAgICAgICAgb3V0ID0gIi4uLy4uL291dHB1dC8yMDIyMDUwNi1leHBhbmRlZC1ibGFzdC1jb21iaW5lZC1ob21vbG9ncy1sb25nbmFtZS5mYWEiKQpteUZhc3RhUmVuYW1lKGZpbGUgPSAiLi4vLi4vb3V0cHV0LzIwMjIwNTEyLWV4cGFuZGVkLWJsYXN0LWNvbWJpbmVkLWhvbW9sb2dzLVBGMTE3NjUuZmFhIiwgCiAgICAgICAgICAgICAgb3V0ID0gIi4uLy4uL291dHB1dC8yMDIyMDUxMi1leHBhbmRlZC1ibGFzdC1jb21iaW5lZC1ob21vbG9ncy1QRjExNzY1LWxvbmduYW1lLmZhYSIpCmBgYAoKIyMjIDExLiBPYnRhaW4gc3VidHJlZXMgZm9yIHRoZSBzcGVjaWVzIGluY2x1ZGVkClRoaXMgaXMgZm9yIHRoZSBsYXR0ZXIgZ2VuZSB0cmVlIHJlY29uY2lsYXRpb24gYW5kIHBsb3R0aW5nLiAKCk91dHB1dCB0aGUgc3BlY2llcyB1c2VkIGFzIGEgc2luZ2xlIGNvbHVtbiB0ZXh0IGZpbGU6CmBgYHtyfQojIGFmdGVyIGFkZGluZyB0aGUgR1JZQyBzcGVjaWVzLCB3ZSBuZWVkIHRvIGV4cGFuZCB0aGUgc3BzLmluY2x1ZGUgbGlzdAojIGFkZCBTLiBjZXJldmlzaWFlIGZvciBzcGVjaWVzIHRyZWUgaWxsdXN0cmF0aW9ucwpzcHMuZmluYWwgPC0gYyh1bmlxdWUoZGVkdXAubWVyZ2Ukc3BlY2llcyksICJTYWNjaGFyb215Y2VzIGNlcmV2aXNpYWUiKQojIGNvbnZlcnQgdGhlIHNwZWNpZXMgbmFtZXMgdG8gdGhlIFNoZW4gMjAxOCBuYW1lCnNwcy5maW5hbC5zaGVuIDwtIHNwc05hbWVDb252ZXJ0ICU+JSAKICBtdXRhdGUobmNiaS5uYW1lID0gc3BlY2llcywKICAgICAgICAgc2hlbjIwMTggPSB3b3JkKGdzdWIoIl8iLCAiICIsIG9sZF9zcGVjZWlzX25hbWVzKSwgLTIsIC0xKSkgJT4lIAogIHNlbGVjdChuY2JpLm5hbWUsIHNoZW4yMDE4LCBgTWFqb3IgY2xhZGVgLCBGYW1pbHksIEdlbnVzKSAlPiUgCiAgZmlsdGVyKG5jYmkubmFtZSAlaW4lIHNwcy5maW5hbCkKY2F0KCJUaGUgZm9sbG93aW5nIHNwZWNpZXMgYXJlIG5vdCBwcmVzZW50IGluIHRoZSBTaGVuIDIwMTggbGlzdCBhbmQgbmVlZCB0byBiZSBhZGRlZCBsYXRlci4iKQpzZXRkaWZmKHNwcy5maW5hbCwgc3BzTmFtZUNvbnZlcnQkc3BlY2llcykKYGBgCgpgYGB7cn0Kc3BzLmZpbmFsLnNoZW4kc2hlbjIwMTggJT4lIG5hLm9taXQoKSAlPiUgZ3N1YigiICIsICJfIiwgLikgJT4lCiAgd3JpdGUoZmlsZSA9IHBhc3RlMCgiZGF0YS8iLCBnc3ViKCItIiwgIiIsIFN5cy5EYXRlKCkpLCAiLWV4cGFuZGVkLWJsYXN0LXNwZWNpZXMtbGlzdC50eHQiKSkKYGBgCgpHZXQgdGhlIHNwZWNpZXMgdHJlZSB1c2luZyBbVHJlZUhvdXNlXShodHRwczovL2dpdGh1Yi5jb20vSkxTdGVlbnd5ay90cmVlaG91c2UpCmBgYHtyfQojIGZpcnN0IGluc3RhbGwgc2hpbnkgYW5kIHBoeXRvb2xzIHBhY2thZ2UKc2hpbnk6OnJ1bkdpdEh1YigidHJlZWhvdXNlIiwgIkpMU3RlZW53eWsiKQpgYGAKCkkgdGhlbiBtYW51YWxseSBhZGRlZCB0aGUgbWlzc2luZyBzcGVjaWVzIGJ5IGVkaXRpbmcgdGhlIE5ld2ljayBmaWxlICh3aXRob3V0IGJyYW5jaCBsZW5ndGggdmVyc2lvbikuIFRoZSBmaW5hbCByZXN1bHQgaXMgc2hvd24gYmVsb3c6CiFbc3BlY2llcyB0cmVlXShkYXRhLzIwMjIwNTE1LWV4cGFuZGVkLWJsYXN0LXNwZWNpZXMtdHJlZS5wbmcpCgpBbHNvIGV4cG9ydCB0aGUgZ2VuZS10by1zcGVjaWVzIG1hcHBpbmcgZm9yIGBnZW5lcmF4YApgYGB7cn0KZ2VuZTJzcHMubWFwIDwtIGRlZHVwLm1lcmdlICU+JSAKICBzZWxlY3QobmFtZSwgc3BlY2llcykgJT4lIAogICMgbW9kaWZ5IHRoZSB0d28gc3BlY2llcyBuYW1lcyB0aGF0IGRpZmZlciBiZXR3ZWVuIHRoZSBnZW5lIG5hbWVzIGFuZCBzcGVjaWVzIHRyZWUKICBtdXRhdGUodHJlZU5hbWUgPSBnc3ViKCIgIiwgIl8iLCBzcGVjaWVzKSkjdHJlZU5hbWUgPSBjYXNlX3doZW4oCiAgICAjc3BlY2llcyA9PSAiWWFtYWRhenltYSB0ZW51aXMiIH4gIkNhbmRpZGEgdGVudWlzIiwKICAgICNzcGVjaWVzID09ICJTdWhvbXljZXMgdGFuemF3YWVuc2lzIiB+ICJDYW5kaWRhIHRhbnphd2FlbnNpcyIsCiAgICAjVFJVRSB+IHNwZWNpZXMpLAogICAgI3RyZWVOYW1lID0gZ3N1YigiICIsICJfIiwgdHJlZU5hbWUpKQogICAgCmdlbmUyc3BzLm1hcCAlPiUgCiAgc2VsZWN0KG5hbWUsIHRyZWVOYW1lKSAlPiUgCiAgd3JpdGVfdHN2KCJkYXRhLzIwMjIwNTE2LWV4cGFuZGVkLWJsYXN0LWdlbmVzLXRvLXNwZWNpZXMtbWFwLnRzdiIsIGNvbF9uYW1lcyA9IEZBTFNFKQpgYGAKCkFwcGVuZCB0aGUgc3BlY2llcyBsaXN0IHdpdGggYWRkaXRpb25hbCBpbmZvcm1hdGlvbgpgYGB7cn0Kc3BzLnBhdGhvIDwtIHJlYWRfdHN2KCJkYXRhLzIwMjIwNTE4LWV4cGFuZGVkLWJsYXN0LXNwZWNpZXMtcGF0aG9nZW4taW5mby50eHQiLCBjb2xfdHlwZXMgPSBjb2xzKCkpCnNwcy5zdW1tYXJ5IDwtIGRlZHVwLm1lcmdlICU+JSAKICBncm91cF9ieShzcGVjaWVzLCBzdGF4aWQpICU+JSAKICBzdW1tYXJpemUoCiAgICB0b3RhbCA9IG4oKSwKICAgIHNob3J0ID0gc3VtKHNsZW4gPCA1MDApLAogICAgc2hvcnRJbmNvbXBsID0gaWZlbHNlKAogICAgICBhbnkoaXMubmEocENvbXBsZXRlKSksIE5BLAogICAgICBzdW0ocENvbXBsZXRlICVpbiUgYygibm8tZW5kcyIsICJuby1sZWZ0IiwgIm5vLXJpZ2h0IikgJiBzbGVuIDwgNTAwKQogICAgKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApICU+JSAKICBhZGRfcm93KHNwZWNpZXMgPSAiU2FjY2hhcm9teWNlcyBjZXJldmlzaWFlIiwKICAgICAgICAgIHN0YXhpZCA9ICI1NTkyOTIiLAogICAgICAgICAgdG90YWwgPSAwLCBzaG9ydCA9IDAsIHNob3J0SW5jb21wbCA9IE5BKSAlPiUgCiAgbGVmdF9qb2luKHNwcy5wYXRobywgYnkgPSAic3BlY2llcyIpICU+JSAKICBhcnJhbmdlKGRlc2ModG90YWwpKQpzcHMuc3VtbWFyeSAlPiUgc2VsZWN0KC1zdGF4aWQpCmBgYAoKUXVlcnkgdGhlIHRheG9ub215IGRhdGFiYXNlIGZvciBsaW5lYWdlIGluZm9ybWF0aW9uIGZvciBlYWNoIHNwZWNpZXMKYGBge3J9CnRheG9uIDwtIGVudHJlel9mZXRjaChkYiA9ICJ0YXhvbm9teSIsIGlkID0gc3BzLnN1bW1hcnkkc3RheGlkLCByZXR0eXBlID0gInhtbCIpICU+JSAKICBYTUw6OnhtbFRvTGlzdCgpCm5hbWVzKHRheG9uKSA8LSBzcHMuc3VtbWFyeSRzcGVjaWVzCmBgYAoKUGFyc2UgdGhlIHRheG9ub215IGluZm9ybWF0aW9uCmBgYHtyfQpzcHMubGluIDwtIGxhcHBseSh0YXhvbiwgZnVuY3Rpb24oeCkgewogIGJpbmRfcm93cyh4JExpbmVhZ2VFeCkgJT4lIHRhaWwoLTEwKSAlPiUgZmlsdGVyKFJhbmsgIT0gInNwZWNpZXMiKQogIH0pICU+JSAKICBiaW5kX3Jvd3MoLmlkID0gInNwZWNpZXMiKQp3cml0ZV90c3Yoc3BzLmxpbiwgImRhdGEvMjAyMjA1MTgtZXhwYW5kZWQtYmxhc3Qtc3BlY2llcy10YXhvbm9teS50c3YiKQpgYGAKCgpXcml0ZSBzcGVjaWVzIGluZm8gdG8gZmlsZQpgYGB7cn0Kc3BzSW5mbyA8LSBzcHMubGluICU+JSAKICBmaWx0ZXIoUmFuayA9PSAiZmFtaWx5IikgJT4lIAogIHNlbGVjdChzcGVjaWVzLCBmYW1pbHkgPSBTY2llbnRpZmljTmFtZSkgJT4lIAogIG11dGF0ZSh0cmVlTmFtZSA9IGdzdWIoIiAiLCAiXyIsIHNwZWNpZXMpKSAlPiUgCiAgICAjdHJlZU5hbWUgPSBjYXNlX3doZW4oCiAgICAjc3BlY2llcyA9PSAiWWFtYWRhenltYSB0ZW51aXMiIH4gIkNhbmRpZGEgdGVudWlzIiwKICAgICNzcGVjaWVzID09ICJTdWhvbXljZXMgdGFuemF3YWVuc2lzIiB+ICJDYW5kaWRhIHRhbnphd2FlbnNpcyIsCiAgICAjVFJVRSB+IHNwZWNpZXMpLAogICAgI3RyZWVOYW1lID0gZ3N1YigiICIsICJfIiwgdHJlZU5hbWUpKSAlPiUgCiAgcmlnaHRfam9pbihzcHMuc3VtbWFyeSwgYnkgPSAic3BlY2llcyIpCgp3cml0ZV90c3Yoc3BzSW5mbywgZmlsZSA9ICJkYXRhLzIwMjIwNTE4LWV4cGFuZGVkLWJsYXN0LXNwZWNpZXMtaW5mby50c3YiKQpgYGAKCiMjIyAxMi4gR2Vub21lIGFzc2VtYmx5IGluZm8KVXNlIHNwZWNpZXMgdGF4aWQgdG8gZmV0Y2ggdGhlIGxpbmtzIHRvIHRoZSB0YXhvbm9teSBkYXRhYmFzZS4gTm90ZSB0aGF0IHNvbWUgb2YgdGhlIHRheGlkcyBhcmUgZm9yIHNwZWNpZmljIHN0cmFpbnMgd2hpbGUgb3RoZXJzIGFyZSBhdCB0aGUgc3BlY2llcyBsZXZlbC4gQXMgYSByZXN1bHQsIHRoZXJlIG1heSBiZSBkaWZmZXJlbnQgbnVtYmVyIG9mIGFzc2VtYmxpZXMgYmVpbmcgZmV0Y2hlZC4KYGBge3IgZ2Vub21lX2Fzc2VtYmx5fQpzdGF4aWQgPC0gc3BzLnN1bW1hcnkgJT4lIHNlbGVjdChzcGVjaWVzLCBzdGF4aWQpICU+JSBkZWZyYW1lKCkKc3RheGlkWyJTY2hlZmZlcnNvbXljZXMgc3RpcGl0aXMiXSA8LSAiNDkyNCIgIyB1c2UgdGhlIHNwZWNpZXMgZ2VuZXJhbCBJRAphc3NlbWJseS5sbmsgPC0gZW50cmV6X2xpbmsoZGJmcm9tID0gInRheG9ub215IiwgZGIgPSAiYXNzZW1ibHkiLCBpZCA9IHN0YXhpZCwgYnlfaWQgPSBUUlVFKQphc3NlbWJseS5sbmsgPC0gbGFwcGx5KGFzc2VtYmx5LmxuaywgZnVuY3Rpb24oeCkgeCRsaW5rcyR0YXhvbm9teV9hc3NlbWJseSkKbmFtZXMoYXNzZW1ibHkubG5rKSA8LSBzcHMuc3VtbWFyeSRzcGVjaWVzCmFzc2VtYmx5Lmxua1siTGFjaGFuY2VhIGtsdXl2ZXJpIl0gPC0gIjk1Mjc4NzEiCmBgYApHZXQgdGhlIGFzc2VtYmx5IHN1bW1hcnkgaW5mbyBmcm9tIG5jYmkKYGBge3J9CmFzc2VtYmx5LmluZm8gPC0gbGFwcGx5KGFzc2VtYmx5LmxuaywgZnVuY3Rpb24oaSl7CiAgaWYoIWlzX2VtcHR5KGkpKQogICAgZW50cmV6X3N1bW1hcnkoZGIgPSAiYXNzZW1ibHkiLCBpZCA9IGkpCn0pCmBgYApFeHRyYWN0IHRoZSBpbmZvcm1hdGlvbiBpbnRvIGEgdGliYmxlCmBgYHtyfQpmaWVsZHMgPC0gYygiYXNzZW1ibHlhY2Nlc3Npb24iLCAibGFzdG1ham9ycmVsZWFzZWFjY2Vzc2lvbiIsICJ0YXhpZCIsICJzcGVjaWVzdGF4aWQiLAogICAgICAgICAgICAic3BlY2llc25hbWUiLCAiYXNzZW1ibHl0eXBlIiwgImFzc2VtYmx5c3RhdHVzIiwgImNvdmVyYWdlIiwgInN1Ym1pc3Npb25kYXRlIiwKICAgICAgICAgICAgImxhc3R1cGRhdGVkYXRlIiwgInJlZnNlcV9jYXRlZ29yeSIsICJjb250aWduNTAiLCAic2NhZmZvbGRuNTAiKQphc3NlbWJseS50YiA8LSBsYXBwbHkoYXNzZW1ibHkuaW5mbywgZnVuY3Rpb24oeCkgewogIHRiIDwtIGV4dHJhY3RfZnJvbV9lc3VtbWFyeSh4LCBlbGVtZW50cyA9IGZpZWxkcywgc2ltcGxpZnkgPSBGQUxTRSkKICBiaW5kX3Jvd3ModGIpCn0pICU+JSBiaW5kX3Jvd3MoKSAlPiUKICBtdXRhdGUoc3BlY2llcyA9IGdzdWIoIlxcW3xcXF0iLCAiIiwgc3BlY2llc25hbWUpKSAlPiUgCiAgYXJyYW5nZShkZXNjKHJlZnNlcV9jYXRlZ29yeSksIHNwZWNpZXMpICU+JSAKICBzZWxlY3Qoc3BlY2llcywgYXNzZW1ibHlzdGF0dXM6c2NhZmZvbGRuNTAsIGV2ZXJ5dGhpbmcoKSkKICAjc2VsZWN0KHNwZWNpZXMgPSBzcGVjaWVzbmFtZSwgZXZlcnl0aGluZygpKQpgYGAKU2VsZWN0IHRoZSByZWZzZXEgb3Igb25lIGFsdGVybmF0aXZlIGFzc2VtYmx5CmBgYHtyfQphc3NlbWJseS50YmwgPC0gYXNzZW1ibHkudGIgJT4lIAogIG11dGF0ZShhc3NlbWJseXN0YXR1cyA9IGZhY3Rvcihhc3NlbWJseXN0YXR1cywgbGV2ZWxzID0gYygiQ29tcGxldGUgR2Vub21lIiwgIkNocm9tb3NvbWUiLCAiU2NhZmZvbGQiLCAiQ29udGlnIikpKSAlPiUgCiAgZ3JvdXBfYnkoc3BlY2llcykgJT4lIAogIGZpbHRlcihyZWZzZXFfY2F0ZWdvcnkgIT0gIm5hIiB8IHNjYWZmb2xkbjUwID09IG1heChzY2FmZm9sZG41MCkgfCBhc3NlbWJseXN0YXR1cyAlaW4lIGMoIkNvbXBsZXRlIEdlbm9tZSIsICJDaHJvbW9zb21lIikpICU+JSAKICBncm91cF9ieShzcGVjaWVzLCBhc3NlbWJseXN0YXR1cykgJT4lIAogIGZpbHRlcihyZWZzZXFfY2F0ZWdvcnkgIT0gIm5hIiB8IHNjYWZmb2xkbjUwID09IG1heChzY2FmZm9sZG41MCkpICU+JSAKICB1bmdyb3VwKCkKd3JpdGVfdHN2KGFzc2VtYmx5LnRibCwgZmlsZSA9ICIuLi8uLi9vdXRwdXQvMjAyMjA1MjUtZXhwYW5kZWQtYmxhc3Qtc3BlY2llcy1hc3NlbWJseS1pbmZvLnRzdiIpCmBgYAoKIyMjIDEzLiBDaHJvbW9zb21hbCBsb2NhdGlvbnMKVGhlIGdvYWwgaXMgdG8gZmV0Y2ggdGhlIGNocm9tb3NvbWFsIGxvY2F0aW9ucyBmb3IgdGhlIEJMQVNUIGhpdHMuIFdlIGFyZSBvbmx5IGdvaW5nIHRvIHVzZSB0aGUgYXNzZW1ibGllcyB0aGF0IGFyZSBhdCBhIGNocm9tb3NvbWFsIG9yIGNvbXBsZXRlIGdlbm9tZSBsZXZlbC4gTm90ZSB0aGF0IF9DLiBhdXJpc18gcmVmc2VxIGdlbm9tZSBpcyBjb21wbGV0ZSBldmVuIHRob3VnaCB0aGUgcmVjb3JkIHNheXMgaXQgaXMgc2NhZmZvbGQuCgpgYGB7ciBjaHJfbG9jMX0KIyBHZXQgdGhlIGxpc3Qgb2Ygc3BlY2llcyB3aG9zZSBnZW5vbWUgYXNzZW1ibHkgYXJlIGF0IGEgY2hyb21vc29tYWwgb3IgY29tcGxldGUgZ2Vub21lIGxldmVsCmxpc3QuZ2Vub21lLmNvbXBsIDwtIGFzc2VtYmx5LnRibCAlPiUgCiAgZmlsdGVyKGFzc2VtYmx5c3RhdHVzICVpbiUgYygiQ29tcGxldGUgR2Vub21lIiwgIkNocm9tb3NvbWUiKSwgcmVmc2VxX2NhdGVnb3J5ICE9ICJuYSIpICU+JSAKICBwdWxsKHNwZWNpZXMpICU+JSB1bmlxdWUoKQpjaHIuc3BzIDwtIGMobGlzdC5nZW5vbWUuY29tcGwsICJTY2hlZmZlcnNvbXljZXMgc3RpcGl0aXMiLCAiQ2FuZGlkYSBhdXJpcyIpCmNoci5zaWQgPC0gZGVkdXAubWVyZ2UgJT4lIGZpbHRlcihzcGVjaWVzICVpbiUgY2hyLnNwcywgZ3JlcGwoIl5yZWYiLCBzaWQpKSAlPiUgcHVsbChzaWQpIAojIEdldCB0aGUgcHJvdGVpbidzIGdlbmUgSUQgKG5vdCByZWZzZXEgSUQpCnRtcC5nZW5lLmxuayA8LSBlbnRyZXpfbGluayhkYmZyb20gPSAicHJvdGVpbiIsIGlkID0gY2hyLnNpZCwgZGIgPSAiZ2VuZSIsIGJ5X2lkID0gVFJVRSkKIyBleHRyYWN0IGdlbmUgSUQKdG1wLmdlbmUuaWQgPC0gc2FwcGx5KHRtcC5nZW5lLmxuaywgZnVuY3Rpb24oeCkgeCRsaW5rcyRwcm90ZWluX2dlbmUpCiMgZmV0Y2ggc3VtbWFyeSBpbmZvIGZyb20gdGhlIGdlbmUgZGF0YWJhc2UKdG1wLmdlbmUgPC0gZW50cmV6X3N1bW1hcnkoZGIgPSAiZ2VuZSIsIGlkID0gdG1wLmdlbmUuaWQpCiMgZXh0cmFjdCBnZW5lIG5hbWUKdG1wLmdlbmUubmFtZSA8LSBleHRyYWN0X2Zyb21fZXN1bW1hcnkodG1wLmdlbmUsIGVsZW1lbnRzID0gIm5hbWUiKQojIGV4dHJhY3Qgc3lzdGVtYXRpYyBJRCAoc3RvcmVkIGluIHRoZSBvdGhlcmFsaWFzIGZpZWxkKQp0bXAuZ2VuZS5hbGlhcyA8LSBleHRyYWN0X2Zyb21fZXN1bW1hcnkodG1wLmdlbmUsIGVsZW1lbnRzID0gIm90aGVyYWxpYXNlcyIpCiMgZXh0cmFjdCBjaHJvbW9zb21hbCBsb2NhdGlvbnMsIG9ubHkgYXZhaWxhYmxlIGZvciBmdWxseSBhc3NlbWJsZWQgZ2Vub21lcwp0bXAuZ2VuZS5sb2MgPC0gYmluZF9yb3dzKGV4dHJhY3RfZnJvbV9lc3VtbWFyeSh0bXAuZ2VuZSwgZWxlbWVudHMgPSAiZ2Vub21pY2luZm8iKSwgLmlkID0gImdlbmVfdWlkIikKIyBwb29sIHRoZSB1bmlxdWUgY2hyb21vc29tZSBzZXF1ZW5jZSBJRHMgdG8gZmluZCBvdXQgdGhlaXIgbGVuZ3RoCnRtcC5jaHIuaWQgPC0gdW5pcXVlKHRtcC5nZW5lLmxvYyRjaHJhY2N2ZXIpCiMgb2J0YWluIHRoZSBjaHJvbW9zb21lIGxlbmd0aCBpbmZvcm1hdGlvbiBmcm9tIHRoZSBudWNjb3JlIGRhdGFiYXNlCnRtcC5jaHIgPC0gZW50cmV6X3N1bW1hcnkoIm51Y2NvcmUiLCBpZCA9IHRtcC5jaHIuaWQpCnRtcC5jaHIubGVuIDwtIGV4dHJhY3RfZnJvbV9lc3VtbWFyeSh0bXAuY2hyLCBlbGVtZW50cyA9ICJzbGVuIik7IApuYW1lcyh0bXAuY2hyLmxlbikgPSB0bXAuY2hyLmlkCiMgYXNzZW1ibGUgdGhlIGNocm9tb3NvbWFsIGxvY2F0aW9uIGRhdGFzZXQKY2hyLmxvYyA8LSB0aWJibGUoc2lkID0gY2hyLnNpZCwgZ2VuZV91aWQgPSBuYW1lcyh0bXAuZ2VuZS5uYW1lKSwgCiAgICAgICAgICAgICAgICAgIGdlbmVfbmFtZSA9IHRtcC5nZW5lLm5hbWUsIGdlbmVfYWxpYXMgPSB0bXAuZ2VuZS5hbGlhcykgJT4lIAogIGxlZnRfam9pbih0bXAuZ2VuZS5sb2MsIGJ5ID0gImdlbmVfdWlkIikgJT4lIAogIG11dGF0ZShjaHJMID0gdG1wLmNoci5sZW5bY2hyYWNjdmVyXSwgcmVsTG9jID0gbnVtKGNocnN0YXJ0L2NockwsIGRpZ2l0cyA9IDIpKSAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChkZWR1cC5tZXJnZSwgc2lkLCBwaWQsIHNwZWNpZXMsIHN0cmFpbiksIGJ5ID0gInNpZCIpICU+JSAKICByZWxvY2F0ZShwaWQ6c3RyYWluLCAuYWZ0ZXIgPSBzaWQpICU+JSByZWxvY2F0ZShzaWQsIC5hZnRlciA9IGxhc3RfY29sKCkpICU+JSAKICBzZWxlY3QoLWV4b25jb3VudCkgJT4lIAogIGFycmFuZ2Uoc3BlY2llcywgY2hybG9jKQpgYGAKCmBgYHtyfQp3cml0ZV90c3YoY2hyLmxvYywgZmlsZSA9ICIuLi8uLi9vdXRwdXQvMjAyMjA2MDktZXhwYW5kZWQtYmxhc3QtY2hyb21vc29tYWwtbG9jYXRpb25zLnRzdiIpCmBgYAo=