TODO
- Have a set where we merge 2.1/2.2, 2.3/2.4.
- Represent the transition from a global view of the samples without
any classification, then state the subpopulations/zymodemes, then add
sensitivity/resistance, then cure/fail.
- Perhaps make an explicit plot where all samples are one color
excepting a relatively small number of previously assayed set? The
samples which would be colored in this view would be from Olga’s 2014
paper I think.
- Make a flow diagram going from s/r -> subpopulation -> c/f/u.
(sankey)
- Make a table similar to the TMRC3 containing the statuses of the
samples.
- Explicitly consider metadata column ‘P’ for reference strains – make
an all grey plot with a few samples colored taken from this column.
Changelog
20230524/20230628
- Frozen metadata, now using sample sheet
‘ClinicalStrains_TMRC2_Frozen 21062023.xlsx’
20230410
- Updating the version number due to some moderately intrusive changes
I made in order to more carefully create plots of the differential
expresison data. I don’t think anything I did should actually change any
of the data, but some of the analyses are definitely affected (note that
the only change in results is due to a mistake I made in defining one of
the contrasts, all other changes are just plot aesthetic
improvements)
20230205
Did the stuff on this morning’s TODO which came out of this
morning’s meeting: do a PCA without the oddball strains (already done in
the worksheet), highlight reference strains, and add L.major IDs and
Descriptions (done by appending a collapsed version of the ortholog data
to the all_lp_annot data).
Fixed human IDs for the macrophage data.
Changed input metadata sheets: primarily because I only
remembered yesterday to finish the SL search for samples >TMRC20095.
They are running now and will be added momentarily (I will have to
redownload the sheet).
Setting up to make a hclust/phylogenetic tree of strains, use
these are reference: 2168(2.3), 2272(2.2), for other 2.x choose
arbitrarily (lower numbers are better).
Added another sanitize columns call for Antimony vs. antimony and
None vs. none in the TMRC2 macrophage samples.
Introduction
This document is intended to create the data structures used to
evaluate our TMRC2 samples. In some cases, this includes only those
samples starting in 2019; in other instances I am including our previous
(2015-2016) samples.
In all cases the processing performed was:
- Default trimming was performed.
- Hisat2 was used to map the remaining reads against the Leishmania
panamensis genome revision 36.
- The alignments from hisat2 were used to count reads/gene against the
revision 36 annotations with htseq.
- These alignments were also passed to the pileup functionality of
samtools and the vcf/bcf utilities in order to make a matrix of all
observed differences between each sample with respect to the
reference.
- The freebayes variant estimation tool was used in addition to #4 to
search for variant positions in a more robust fashion.
- The trimmed reads were passed to kraken2 using a viral database in
order to look for samples with potential LRV sequence.
- An explicit, grep-based search for spliced leader reads was used
against all human-derived samples. The results from this were
copy/pasted into the sample sheet.
Notes 20221206
meeting
I am thinking that this meeting will bring Maria Adelaida fully back
into the analyses of the parasite data, and therefore may focus
primarily on the goals rather than the analyses?
- Maria Adelaida meeting with Olgla/Mariana: integrating
transcriptomics/genomics question.
- Paper on relationship btwn primary metadata factors via
transcriptome/genome.
- Second on drug susceptibility without those factors (I think this
means the macrophages)
- Definition of species? MAG: Define consensus sequences for various
strains/species. We effectively have this on hand, though the quality
may be a little less good for 2.3.
- Resulting goal: Create a tree of the strains (I am just going to
call zymodemes strains from now on). ** What organisms would we include
in a tree to describe these relationships: guyanensis, braziliensis
2904, 2.2, 2.3, 2.1, 2.4, panamensis reference, peruviania(sp? I have
not seen this genome), panama, 2903; actually this may be tricky because
we have always done this with a specific reference strain (panamensis
col) which is one of the strains in the comparison. hmm… ** Check the
most variant strains for identity (Luc) ** Methods for creating tree,
traditional phylogeny vs. variant hclust?
- PCR queries, works well if one performs sanger sequencing.
Multiple
datasets
In a couple of important ways the TMRC2 data is much more complex
than the TMRC3:
- It comprises multiple, completely separate queries:
- Sequencing the parasite samples
- Sequencing a set of human macrophage samples which were infected
with specific parasite samples.
- The parasite transcriptomic samples comprise multiple different
types of queries:
- Differential expression to look at strain, susceptibility, and
clinical outcomes.
- Individual variant searches to look for potentially useful SNPs for
classification of parasite samples.
- The human macrophage samples may be used to query both the host and
parasite transcriptomes because (at least when not drug treated) there
is a tremendous population of parasite reads in them.
Sample sheet(s)
Our shared online sample sheet is nearly static at the time of this
writing (202209), I expect at this point the only likely updates will be
to annotate some strains as more or less susceptible to drug
treatment.
sample_sheet <- "sample_sheets/ClinicalStrains_TMRC2.xlsx"
macrophage_sheet <- "sample_sheets/tmrc2_macrophage_samples.xlsx"
Modify the sample
sheet
The following block provides an example invocation of how I
automatically extract things like percent reads mapped/trimmed/etc from
the logs produced by trimomatic/cutadapt/hisat/salmon/etc. The caveat is
that this container only has a small portion of the material available
in the main working tree, as a result the new columns added to the
sample sheet are relatively sparse compared to what I get on my
computer.
In addition, because these samples have gone through ~ 3 different
versions of my pipeline, and the code which extracts the numbers
explicitly assumes only the most recent version (because it is the
best!), it does not get out the data for all the samples.
modified <- gather_preprocessing_metadata(sample_sheet, species = "lpanamensis_v36")
## Checking the state of the condition column.
## Checking the state of the batch column.
## Checking the condition factor.
## preprocessing/TMRC20001/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20002/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20065/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20004/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20005/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20066/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20039/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20037/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20038/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20067/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20068/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20041/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20015/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20009/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20010/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20016/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20011/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20012/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20013/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20017/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20014/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20018/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20019/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20070/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20020/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20021/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20022/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20024/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20036/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20069/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20033/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20026/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20031/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20076/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20073/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20055/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20079/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20071/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20078/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20094/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20042/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20058/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20072/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20059/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20048/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20057/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20088/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20056/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20060/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20077/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20074/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20063/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20053/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20052/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20064/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20075/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20051/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20050/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20049/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20062/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20110/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20080/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20043/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20083/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20054/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20085/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20046/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20093/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20089/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20047/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20090/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20044/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20045/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20105/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20108/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20109/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20098/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20096/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20101/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20092/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20082/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20102/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20099/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20100/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20091/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20084/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20087/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20103/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20104/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20086/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20107/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20081/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## preprocessing/TMRC20095/outputs/*hisat*_lpanamensis_v36/lpanamensis_v36_*genome*_gene_ID_fcounts.csv.xz
## Writing new metadata to: sample_sheets/ClinicalStrains_TMRC2_modified.xlsx
Annotations
Everything which follows depends on the Existing TriTrypDB
annotations revision 46, circa 2019. The following block loads a
database of these annotations and turns it into a matrix where the rows
are genes and columns are all the annotation types provided by
TriTrypDB.
The same database was used to create a matrix of orthologous genes
between L.panamensis and all of the other species in the TriTrypDB.
The same database of annotations also provides mappings to the set of
annotated GO categories for the L.panamensis genome along with gene
lengths.
The following block assumes one has access to tritrypdb.org, which is
no currently guaranteed. Thus I bundled a pre-generated copy of the
genome, Txdb, and annotations.
## meta <- download_eupath_metadata(webservice = "tritrypdb", eu_version = "v46")
meta <- download_eupath_metadata(webservice = "tritrypdb")
panamensis_entry <- get_eupath_entry("MHOM", metadata = meta[["valid"]])
panamensis_db <- make_eupath_orgdb(panamensis_entry)
panamensis_pkg <- panamensis_db[["pkgname"]]
package_name <- panamensis_db[["pkgname"]]
if (is.null(panamensis_pkg)) {
panamensis_pkg <- panamensis_db[["orgdb_name"]]
package_name <- panamensis_pkg
}
tt <- library(panamensis_pkg, character.only = TRUE)
panamensis_pkg <- get0(panamensis_pkg)
all_fields <- columns(panamensis_pkg)
all_lp_annot <- sm(load_orgdb_annotations(
panamensis_pkg,
keytype = "gid",
fields = c("annot_gene_entrez_id", "annot_gene_name",
"annot_strand", "annot_chromosome", "annot_cds_length",
"annot_gene_product")))$genes
lp_go <- load_orgdb_go(package_name)
lp_go <- lp_go[, c("GID", "GO")]
lp_lengths <- all_lp_annot[, c("gid", "annot_cds_length")]
colnames(lp_lengths) <- c("ID", "length")
all_lp_annot[["annot_gene_product"]] <- tolower(all_lp_annot[["annot_gene_product"]])
orthos <- sm(extract_eupath_orthologs(db = panamensis_pkg))
data_structures <- c(data_structures, "lp_lengths", "lp_go", "all_lp_annot", "meta")
all_installed <- rownames(installed.packages())
candidates <- grepl(pattern = "^org.Lpanamensis.MHOM.*v68.eg.db", x = all_installed)
orgdb_pkg_name <- all_installed[candidates]
tt <- library(orgdb_pkg_name, character.only = TRUE)
## Error in library(orgdb_pkg_name, character.only = TRUE): 'package' must be of length 1
panamensis_pkg <- get0(orgdb_pkg_name)
## Error in get0(orgdb_pkg_name): invalid first argument
all_fields <- columns(panamensis_pkg)
## Error in columns(panamensis_pkg): could not find function "columns"
all_lp_annot <- sm(load_orgdb_annotations(
panamensis_pkg,
keytype = "gid",
fields = c("annot_gene_entrez_id", "annot_gene_name", "annot_gene_location_text",
"annot_strand", "annot_chromosome", "annot_cds_length",
"annot_gene_product")))$genes
## Error: object 'panamensis_pkg' not found
lp_go <- load_orgdb_go(panamensis_pkg)
## Error: object 'panamensis_pkg' not found
lp_go <- lp_go[, c("GID", "GO")]
## Error: object 'lp_go' not found
lp_lengths <- all_lp_annot[, c("gid", "annot_cds_length")]
## Error: object 'all_lp_annot' not found
colnames(lp_lengths) <- c("ID", "length")
## Error: object 'lp_lengths' not found
all_lp_annot[["annot_gene_product"]] <- tolower(all_lp_annot[["annot_gene_product"]])
## Error: object 'all_lp_annot' not found
data_structures <- c(data_structures, "lp_lengths", "lp_go", "all_lp_annot", "meta")
Repeat for the
L.major annotations
Recently there was a request to include the Leishmania major gene IDs
and descriptions. Thus I will extract them along with the orthologs and
append that to the annotations used.
Having spent the time to run the following code, I realized that the
orthologs data structure above actually already has the gene IDs and
descriptions.
Thus I will leave my query in place to extract the major annotations,
but follow it up with a collapse of the major orthologs and appending of
that to the panamensis annotations.
orgdb <- "org.Lmajor.Friedlin.v49.eg.db"
tt <- sm(library(orgdb, character.only = TRUE))
major_db <- org.Lmajor.Friedlin.v49.eg.db
all_fields <- columns(pan_db)
all_lm_annot <- sm(load_orgdb_annotations(
major_db,
keytype = "gid",
fields = c("annot_gene_entrez_id", "annot_gene_name",
"annot_strand", "annot_chromosome", "annot_cds_length",
"annot_gene_product")))$genes
wanted_orthos_idx <- orthos[["ORTHOLOGS_SPECIES"]] == "Leishmania major strain Friedlin"
sum(wanted_orthos_idx)
wanted_orthos <- orthos[wanted_orthos_idx, ]
wanted_orthos <- wanted_orthos[, c("GID", "ORTHOLOGS_ID", "ORTHOLOGS_NAME")]
collapsed_orthos <- wanted_orthos %>%
group_by(GID) %>%
summarise(collapsed_id = stringr::str_c(ORTHOLOGS_ID, collapse = " ; "),
collapsed_name = stringr::str_c(ORTHOLOGS_NAME, collapse = " ; "))
all_lp_annot <- merge(all_lp_annot, collapsed_orthos, by.x = "row.names",
by.y = "GID", all.x = TRUE)
rownames(all_lp_annot) <- all_lp_annot[["Row.names"]]
all_lp_annot[["Row.names"]] <- NULL
data_structures <- c(data_structures, "lp_lengths", "lp_go", "all_lp_annot")
Load a genome
The following block loads the full genome sequence for panamensis. We
may use this later to attempt to estimate PCR primers to discern
strains.
I am not sure how to increase the number of open files in a
container, as a result this does not work.
## testing_panamensis <- make_eupath_bsgenome(entry = panamensis_entry, eu_version = "v46")
pkg_candidates <- grepl(x = all_installed, pattern = "BSGenome\\.Leishmania\\.panamensis.*")
pkg_name <- all_installed[pkg_candidates][1]
library(as.character(pkg_name), character.only = TRUE)
## Error in library(as.character(pkg_name), character.only = TRUE): invalid package name
lp_genome <- get0(as.character(pkg_name))
data_structures <- c(data_structures, "lp_genome")
Generate Expressionsets
and Sample Estimation
The process of sample estimation takes two primary inputs:
- The sample sheet, which contains all the metadata we currently have
on hand, including filenames for the outputs of #3 and #4 above.
- The gene annotations.
An expressionSet(or summarizedExperiment) is a data structure used in
R to examine RNASeq data. It is comprised of annotations, metadata, and
expression data. In the case of our processing pipeline, the location of
the expression data is provided by the filenames in the metadata.
Notes
The following samples are much lower coverage:
- TMRC20002
- TMRC20006
- TMRC20007
- TMRC20008
There is a set of strains which acquired resistance in vitro. These
are included in the dataset, but there are not likely enough of them to
query that question explicitly.
Define colors
The following list contains the colors we have chosen to use when
plotting the various ways of discerning the data.
color_choices <- list(
"strain" = list(
## "z1.0" = "#333333", ## Changed this to 'braz' to make it easier to find them.
"z2.0" = "#555555",
"z3.0" = "#777777",
"z2.1" = "#874400",
"z2.2" = "#0000cc",
"z2.3" = "#cc0000",
"z2.4" = "#df7000",
"z3.2" = "#888888",
"z1.0" = "#cc00cc",
"z1.5" = "#cc00cc",
"b2904" = "#cc00cc",
"unknown" = "#cbcbcb"),
## "null" = "#000000"),
"zymo" = list(
"z22" = "#0000cc",
"z23" = "#cc0000"),
"cf" = list(
"cure" = "#006f00",
"fail" = "#9dffa0",
"unknown" = "#cbcbcb",
"notapplicable" = "#000000"),
"susceptibility" = list(
"resistant" = "#8563a7",
"sensitive" = "#8d0000",
"ambiguous" = "#cbcbcb",
"unknown" = "#555555"))
data_structures <- c(data_structures, "color_choices")
Parasite-only data
structure
The data structure ‘lp_expt’ contains the data for all samples which
have hisat2 count tables, and which pass a few initial quality tests
(e.g. they must have more than 8550 genes with >0 counts and >5e6
reads which mapped to a gene); genes which are annotated with a few key
redundant categories (leishmanolysin for example) are also culled.
All (almost)
samples
There are a few metadata columns which we really want to make certain
are standardized.
Note: I changed this to print both the number of reads and genes for
removed samples.
sanitize_columns <- c("passagenumber", "clinicalresponse", "clinicalcategorical",
"zymodemecategorical", "included")
lp_expt <- create_expt(sample_sheet,
gene_info = all_lp_annot,
annotation_name = orgdb_pkg_name,
savefile = glue("rda/tmrc2_lp_expt_all_raw-v{ver}.rda"),
id_column = "hpglidentifier",
file_column = "lpanamensisv36hisatfile") %>%
set_expt_conditions(fact = "zymodemecategorical", colors = color_choices[["strain"]]) %>%
semantic_expt_filter(semantic = c("amastin", "gp63", "leishmanolysin"),
semantic_column = "annot_gene_product") %>%
sanitize_expt_pData(columns = sanitize_columns) %>%
subset_expt(subset = "included=='yes'") %>%
set_expt_factors(columns = sanitize_columns, class = "factor")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'fData': error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': could not find function "sanitize_expt_pData"
data_structures <- c(data_structures, "lp_expt")
save(list = "lp_expt", file = glue("rda/tmrc2_lp_expt_all_sanitized-v{ver}.rda"))
## Error in save(list = "lp_expt", file = glue("rda/tmrc2_lp_expt_all_sanitized-v{ver}.rda")): object 'lp_expt' not found
table(pData(lp_expt)[["zymodemecategorical"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
table(pData(lp_expt)[["clinicalresponse"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
table(pData(lp_expt)[["clinicalcategorical"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'lp_expt' not found
Print sample IDs by
status
Cure
cure_ids <- pData(lp_expt)[["clinicalcategorical"]] == "cure"
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
rownames(pData(lp_expt))[cure_ids]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
Fail
fail_ids <- pData(lp_expt)[["clinicalcategorical"]] == "fail"
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
rownames(pData(lp_expt))[fail_ids]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
Unknown
unknown_ids <- pData(lp_expt)[["clinicalcategorical"]] == "unknown"
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
rownames(pData(lp_expt))[unknown_ids]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
Susceptible and
strain
all_sensitive_ids <- pData(lp_expt)[["qualitativeclassificationofdrugsusceptibility"]] == "Sensitive"
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
sensitive_ids <- rownames(pData(lp_expt))[all_sensitive_ids]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
all_resistant_ids <- pData(lp_expt)[["qualitativeclassificationofdrugsusceptibility"]] == "Resistant"
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
resistant_ids <- rownames(pData(lp_expt))[all_resistant_ids]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
all_z23 <- pData(lp_expt)[["zymodemecategorical"]] == "z23"
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
z23_ids <- rownames(pData(lp_expt))[all_z23]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
all_z22 <- pData(lp_expt)[["zymodemecategorical"]] == "z22"
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
z22_ids <- rownames(pData(lp_expt))[all_z22]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
both_ids <- z22_ids %in% resistant_ids
## Error: object 'z22_ids' not found
## Error: object 'both_ids' not found
rownames(pData(lp_expt))[[both_ids]]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
both_ids <- z23_ids %in% sensitive_ids
## Error: object 'z23_ids' not found
## Error: object 'both_ids' not found
All the following data will derive from this starting point.
Add library sizes
before filtering
table(pData(lp_expt)[["clinicalcategorical"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
unknown_ids <- pData(lp_expt)[["clinicalcategorical"]] == "unknown"
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
rownames(pData(lp_expt))[unknown_ids]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
failed_ids <- pData(lp_expt)[["clinicalcategorical"]] == "fail"
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
rownames(pData(lp_expt))[failed_ids]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
pre_libsize <- plot_libsize(lp_expt)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'plot_libsize': object 'lp_expt' not found
## Error: object 'pre_libsize' not found
pdf(file = "figures/library_size_pre_filter.pdf", width = 24, height = 12)
pre_libsize$plot
## Error: object 'pre_libsize' not found
## png
## 2
pre_nonzero <- plot_nonzero(lp_expt)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'plot_nonzero': object 'lp_expt' not found
## Error: object 'pre_nonzero' not found
pdf(file = "figures/nonzero_pre_filter.pdf")
pre_nonzero$plot
## Error: object 'pre_nonzero' not found
## png
## 2
## Error: object 'lp_expt' not found
lp_expt <- subset_expt(lp_expt, nonzero = 8550)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'lp_expt' not found
post_nonzero <- plot_nonzero(lp_expt)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'plot_nonzero': object 'lp_expt' not found
## Error: object 'post_nonzero' not found
Clinical outcome
Clinical outcome is by far the most problematic comparison in this
data, but here is the recategorization of the data using it:
lp_cf <- set_expt_conditions(lp_expt, fact = "clinicalcategorical",
colors = color_choices[["cf"]]) %>%
set_expt_batches(fact = sus_categorical_current)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'set_expt_batches': error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
table(pData(lp_cf)[["condition"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_cf' not found
data_structures <- c(data_structures, "lp_cf")
save(list = "lp_cf",
file = glue("rda/tmrc2_lp_cf-v{ver}.rda"))
## Error in save(list = "lp_cf", file = glue("rda/tmrc2_lp_cf-v{ver}.rda")): object 'lp_cf' not found
lp_cf_known <- subset_expt(lp_cf, subset = "condition!='unknown'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'lp_cf' not found
data_structures <- c(data_structures, "lp_cf_known")
save(list = "lp_cf_known",
file = glue("rda/tmrc2_lp_cf_known-v{ver}.rda"))
## Error in save(list = "lp_cf_known", file = glue("rda/tmrc2_lp_cf_known-v{ver}.rda")): object 'lp_cf_known' not found
data_structures <- c(data_structures, "lp_cf_known")
save(list = "lp_cf_known",
file = glue("rda/tmrc2_lp_cf_known-v{ver}.rda"))
## Error in save(list = "lp_cf_known", file = glue("rda/tmrc2_lp_cf_known-v{ver}.rda")): object 'lp_cf_known' not found
Create a historical
susceptibility dataset
Use the factorized version of susceptibility to categorize the
samples by the historical data.
lp_susceptibility_historical <- set_expt_conditions(
lp_expt, fact = "sus_category_historical", colors = color_choices[["susceptibility"]]) %>%
set_expt_batches(fact = "clinicalcategorical")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'set_expt_batches': error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
save(list = "lp_susceptibility_historical",
file = glue("rda/tmrc2_lp_susceptibility_historical-v{ver}.rda"))
## Error in save(list = "lp_susceptibility_historical", file = glue("rda/tmrc2_lp_susceptibility_historical-v{ver}.rda")): object 'lp_susceptibility_historical' not found
data_structures <- c(data_structures, "lp_susceptibility_historical")
Create a current
susceptibility dataset
Use the factorized version of susceptibility to categorize the
samples by the historical data.
This will likely be our canonical susceptibility dataset, so I will
remove the suffix and just call it ‘lp_susceptibility’.
lp_susceptibility <- set_expt_conditions(
lp_expt, fact = "sus_category_current", colors = color_choices[["susceptibility"]]) %>%
set_expt_batches(fact = "clinicalcategorical")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'set_expt_batches': error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
save(list = "lp_susceptibility",
file = glue("rda/tmrc2_lp_susceptibility-v{ver}.rda"))
## Error in save(list = "lp_susceptibility", file = glue("rda/tmrc2_lp_susceptibility-v{ver}.rda")): object 'lp_susceptibility' not found
data_structures <- c(data_structures, "lp_susceptibility")
Pull out only the
samples with two zymodemes
I think this is redundant with a previous block, but I am leaving it
until I am certain that it is not required in a following document.
Note: IMPORTANT This is the set Maria Adeliada prefers to
use.
lp_zymo <- subset_expt(lp_expt, subset = "condition=='z2.2'|condition=='z2.3'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'lp_expt' not found
data_structures <- c(data_structures, "lp_zymo")
save(list = "lp_zymo",
file = glue("rda/tmrc2_lp_zymo-v{ver}.rda"))
## Error in save(list = "lp_zymo", file = glue("rda/tmrc2_lp_zymo-v{ver}.rda")): object 'lp_zymo' not found
Variant data using
parasite RNASeq reads
The following section will create some initial data structures of the
observed variants in the parasite samples. This will include some of our
2016 samples for some classification queries.
The 2016 variant
data
I changed and improved the mapping and variant detection methods from
what we used for the 2016 data. So some small changes will be required
to merge them.
lp_previous <- create_expt("sample_sheets/tmrc2_samples_20191203.xlsx",
file_column = "tophat2file",
savefile = glue("rda/lp_previous-v{ver}.rda"))
tt <- lp_previous$expressionset
rownames(tt) <- gsub(pattern = "^exon_", replacement = "", x = rownames(tt))
rownames(tt) <- gsub(pattern = "\\.1$", replacement = "", x = rownames(tt))
rownames(tt) <- gsub(pattern = "\\-1$", replacement = "", x = rownames(tt))
lp_previous$expressionset <- tt
rm(tt)
data_structures <- c(data_structures, "lp_previous")
Create the SNP
expressionset
The count_expt_snps() function uses our expressionset data and a
metadata column in order to extract the mpileup or freebayes-based
variant calls and create matrices of the likelihood that each
position-per-sample is in fact a variant.
There is an important caveat here which changed on 202301: I was
interpreting using the PAIRED tag, which is only used for,
unsurprisingly, paired-end samples. A couple samples are not paired and
so were failing silently. The QA tag looks like it is more appropriate
and should work across both types. One way to find out, I am setting it
here and will look to see if the results make more sense for my test
samples (TMRC2001, TMRC2005, TMRC2007).
## The next line drops the samples which are missing the SNP pipeline.
lp_snp <- subset_expt(lp_expt, subset = "!is.na(pData(lp_expt)[['freebayessummary']])")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'lp_expt' not found
lp_snp_sufficient <- subset_expt(lp_snp, subset = "rownames!='TMRC20082'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'lp_snp' not found
lp_snp_only22_23_ref <- subset_expt(lp_snp, subset = "zymodemereference=='z2.2'|zymodemereference=='z2.3'") %>%
subset_expt(subset = "rownames!='TMRC20082'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'lp_snp' not found
lp_snp_22_23_ml <- subset_expt(lp_snp, subset = "knnv2classification=='z22'|knnv2classification=='z23'") %>%
subset_expt(subset = "rownames!='TMRC20082'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'lp_snp' not found
new_snps_sufficient <- count_expt_snps(lp_snp_sufficient, annot_column = "freebayessummary", snp_column = "QA",
reader = "readr")
## Error in count_expt_snps(lp_snp_sufficient, annot_column = "freebayessummary", : could not find function "count_expt_snps"
new_snps_only22_23_ref_suf <- count_expt_snps(lp_snp_only22_23_ref, annot_column = "freebayessummary", snp_column = "QA",
reader = "readr")
## Error in count_expt_snps(lp_snp_only22_23_ref, annot_column = "freebayessummary", : could not find function "count_expt_snps"
new_snps_22_23_ml_suf <- count_expt_snps(lp_snp_22_23_ml, annot_column = "freebayessummary", snp_column = "QA",
reader = "readr")
## Error in count_expt_snps(lp_snp_22_23_ml, annot_column = "freebayessummary", : could not find function "count_expt_snps"
## Lets see if we get numbers which make sense.
summary(exprs(new_snps_sufficient)[["tmrc20001"]]) ## My weirdo sample
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'new_snps_sufficient' not found
summary(exprs(new_snps_sufficient)[["tmrc20072"]]) ## Another sample chosen at random
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'new_snps_sufficient' not found
summary(exprs(new_snps_sufficient)[["tmrc20021"]]) ## Another sample chosen at random
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'new_snps_sufficient' not found
## Now that we are reasonably confident that things make more sense, lets save and move on...
data_structures <- c(data_structures, "new_snps_sufficient", "lp_snp")
sufficient_norm <- normalize_expt(new_snps_sufficient, transform = "log2")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'input' in selecting a method for function 'state': object 'new_snps_sufficient' not found
plot_boxplot(sufficient_normw)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'plot_boxplot': object 'sufficient_normw' not found
Now let us pull in the 2016 data.
old_snps <- count_expt_snps(lp_previous, annot_column = "bcftable", snp_column = 2)
data_structures <- c(data_structures, "old_snps")
save(list = "lp_snp",
file = glue("rda/lp_snp-v{ver}.rda"))
data_structures <- c(data_structures, "lp_snp")
save(list = "new_snps",
file = glue("rda/new_snps-v{ver}.rda"))
data_structures <- c(data_structures, "new_snps")
save(list = "old_snps",
file = glue("rda/old_snps-v{ver}.rda"))
data_structures <- c(data_structures, "old_snps")
nonzero_snps <- exprs(new_snps) != 0
colSums(nonzero_snps)
Combine the previous
and current data
As far as I can tell, freebayes and mpileup are reasonably similar in
their sensitivity/specificity; so combining the two datasets like this
is expected to work with minimal problems. The most likely problem is
that my mpileup-based pipeline is unable to handle indels.
## My old_snps is using an older annotation incorrectly, so fix it here:
#annotation(old_snps) <- annotation(new_snps)
both_snps <- combine_expts(new_snps, old_snps)
save(list = "both_snps",
file = glue("rda/both_snps-v{ver}.rda"))
data_structures <- c(data_structures, "both_snps")
Subclade manual
interpretation
I am taking a heatmap from our variant data and manually identifying
sample groups.
- A: TMRC20025, TMRC20027, TMRC20028
- B: hpgl0641, hpgl0247, hpgl0631, hpgl0658, close to A
- C: TMRC20008, TMRC20007, TMRC20001, TMRC20005, hpgl0318,
TMRC20012
- D: hpgl0643, hpgl0316, hpgl0320, hpgl0641, close to C
- E: TMRC20032, TMRC20061
- F: TMRC20040, TMRC20036, hpgl0245, TMRC20103, TMRC20093, TMRC20045,
TMRC20041, TMRC20072, TMRC20046, TMRC20057, TMRC20097, TMRC20084, close
to E
- G: hpgl0632, hpgl0652, hpgl0248, hpgl0659
- H: hpgl0654, hpgl0634, hpgl0243, hpgl0243, closest to G
- I: hpgl0242, hpgl0322, hpgl0636, hpgl0663, hpgl0638, close to H
- J: TMRC20017, TMRC20033, TMRC20053, TMRC20063, TMRC20056, TMRC20074,
TMRC20055, TMRC20022, TMRC20026, TMRC20083, TMRC20077, TMRC20060
- K: TMRC20050, TMRC20042, TMRC20078, TMRC20049, TMRC20069, TMRC20044,
close to J
- L: TMRC20076, TMRC20024, TMRC2009
- M: TMRC20019, TMRC20020, TMRC20031, TMRC20014, TMRC20011, close to
L
- N: TMRC20096, TMRC20081, TMRC20110, TMRC20092, TMRC20088, TMRC20101,
TMRC20106, TMRC20091, TMRC20109, TMRC20087, TMRC20086, closeish to
M
- O: TMRC20095, TMRC20016, TMRC20018, quite far from everyone
- P: TMRC20082, TMRC20075, pretty separate too
- Q: hpgl0246, hpgl0653, hpgl0633, hpgl0244, hpgl0635, hpgl0655,
hpgl0639, hpgl0662
- R: TMRC20059, TMRC20089, TMRC20021, TMRC20048, TMRC20067
- S: TMRC20013, TMRC20010, TMRC20037, TMRC20066, TMRC20062, TMRC20038,
close to R
- T: TMRC20015, TMRC20108, TMRC20099, TMRC20102, TMRC20085, TMRC20090,
TMRC20104, TMRC20098, TMRC20100, TMRC20107
- U: TMRC20047, TMRC20068, TMRC20080, TMRC20105, TMRC20094, TMRC20065,
TMRC20071, TMRC20064, TMRC20043, TMRC20070, TMRC20062, TMRC20051,
TMRC20079, TMRC20073, TMRC20058, TMRC20054
Macrophage data
All of the above focused entire on the parasite samples, now let us
pull up the macrophage infected samples. This will comprise two
datasets, one of the human and one of the parasite.
Macrophage host
data
The metadata for the macrophage samples contains a couple of columns
for mapped human and parasite reads. We will therefore use them
separately to create two expressionsets, one for each species.
hs_annot <- load_biomart_annotations(year = "2020", month = 4)
## Using mart: ENSEMBL_MART_ENSEMBL from host: apr2020.archive.ensembl.org.
## Successfully connected to the hsapiens_gene_ensembl database.
## Finished downloading ensembl gene annotations.
## Finished downloading ensembl structure annotations.
## symbol columns is null, pattern matching 'symbol' and taking the first.
## Including symbols, there are 67149 vs the 249740 gene annotations.
## Not dropping haplotype chromosome annotations, set drop_haplotypes = TRUE if this is bad.
## Saving annotations to hsapiens_biomart_annotations.rda.
## Finished save().
hs_annot <- hs_annot[["annotation"]]
hs_annot[["transcript"]] <- paste0(rownames(hs_annot), ".", hs_annot[["transcript_version"]])
rownames(hs_annot) <- make.names(hs_annot[["ensembl_gene_id"]], unique = TRUE)
rownames(hs_annot) <- paste0("gene:", rownames(hs_annot))
tx_gene_map <- hs_annot[, c("transcript", "ensembl_gene_id")]
sanitize_columns <- c("drug", "macrophagetreatment", "macrophagezymodeme")
macr_annot <- hs_annot
rownames(macr_annot) <- gsub(x = rownames(macr_annot),
pattern = "^gene:",
replacement = "")
hs_macrophage <- create_expt(
macrophage_sheet,
gene_info = macr_annot,
file_column = "hg38100hisatfile") %>%
set_expt_conditions(fact = "macrophagetreatment") %>%
set_expt_batches(fact = "macrophagezymodeme") %>%
sanitize_expt_pData(columns = sanitize_columns) %>%
subset_expt(nonzero = 12000)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': could not find function "sanitize_expt_pData"
fixed_genenames <- gsub(x = rownames(exprs(hs_macrophage)), pattern = "^gene:",
replacement = "")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'hs_macrophage' not found
hs_macrophage <- set_expt_genenames(hs_macrophage, ids = fixed_genenames)
## Error: object 'hs_macrophage' not found
table(pData(hs_macrophage)$condition)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'hs_macrophage' not found
## The following 3 lines were copy/pasted to datastructures and should be removed soon.
nostrain <- is.na(pData(hs_macrophage)[["strainid"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'hs_macrophage' not found
pData(hs_macrophage)[nostrain, "strainid"] <- "none"
## Error: object 'hs_macrophage' not found
pData(hs_macrophage)[["strain_zymo"]] <- paste0("s", pData(hs_macrophage)[["strainid"]],
"_", pData(hs_macrophage)[["macrophagezymodeme"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'hs_macrophage' not found
uninfected <- pData(hs_macrophage)[["strain_zymo"]] == "snone_none"
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'hs_macrophage' not found
pData(hs_macrophage)[uninfected, "strain_zymo"] <- "uninfected"
## Error: object 'hs_macrophage' not found
data_structures <- c(data_structures, "hs_macrophage")
Finally, split off the U937 samples.
hs_u937 <- subset_expt(hs_macrophage, subset = "typeofcells!='Macrophages'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'hs_macrophage' not found
data_structures <- c(data_structures, "hs_u937")
Macrophage parasite
data
In the previous block, we used a new invocation of ensembl-derived
annotation data, this time we can just use our existing parasite gene
annotations.
lp_macrophage <- create_expt(macrophage_sheet,
file_column = "lpanamensisv36hisatfile",
gene_info = all_lp_annot,
savefile = glue("rda/lp_macrophage-v{ver}.rda"),
annotation = "org.Lpanamensis.MHOMCOL81L13.v46.eg.db") %>%
set_expt_conditions(fact = "macrophagezymodeme") %>%
set_expt_batches(fact = "macrophagetreatment")
## Reading the sample metadata.
## Did not find the column: sampleid.
## Setting the ID column to the first column.
## Did not find the condition column in the sample sheet.
## Filling it in as undefined.
## Did not find the batch column in the sample sheet.
## Filling it in as undefined.
## Checking the state of the condition column.
## Checking the state of the batch column.
## Checking the condition factor.
## The sample definitions comprises: 69 rows(samples) and 80 columns(metadata fields).
## Warning in create_expt(macrophage_sheet, file_column =
## "lpanamensisv36hisatfile", : Some samples were removed when cross referencing
## the samples against the count data.
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'set_expt_batches': error in evaluating the argument 'object' in selecting a method for function 'pData': object 'all_lp_annot' not found
unfilt_written <- write_expt(
lp_macrophage,
excel = glue("analyses/macrophage_de/{ver}/read_counts/lp_macrophage_reads_unfiltered-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'ncol': error in evaluating the argument 'x' in selecting a method for function 'assay': object 'lp_macrophage' not found
lp_macrophage_filt <- subset_expt(lp_macrophage, nonzero = 2500) %>%
semantic_expt_filter(semantic = c("amastin", "gp63", "leishmanolysin"),
semantic_column = "annot_gene_product")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'lp_macrophage' not found
data_structures <- c(data_structures, "lp_macrophage", "lp_macrophage_filt")
filt_written <- write_expt(lp_macrophage_filt,
excel = glue("analyses/macrophage_de/{ver}/read_counts/lp_macrophage_reads_filtered-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'ncol': error in evaluating the argument 'x' in selecting a method for function 'assay': object 'lp_macrophage_filt' not found
lp_macrophage <- lp_macrophage_filt
## Error: object 'lp_macrophage_filt' not found
lp_macrophage_nosb <- subset_expt(lp_macrophage, subset = "batch!='inf_sb'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'lp_macrophage' not found
lp_nosb_write <- write_expt(
lp_macrophage_nosb,
excel = glue("analyses/macrophage_de/{ver}/read_counts/lp_macrophage_nosb_reads-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'ncol': error in evaluating the argument 'x' in selecting a method for function 'assay': object 'lp_macrophage_nosb' not found
data_structures <- c(data_structures, "lp_macrophage_nosb")
spec <- make_rnaseq_spec()
test <- sm(gather_preprocessing_metadata(macrophage_sheet, specification = spec))
Plot SL Reads on a per
condition basis
lp_meta <- pData(lp_macrophage)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_macrophage' not found
lp_meta[["slvsreads_log"]] <- log10(lp_meta[["slvsreads"]])
## Error: object 'lp_meta' not found
inf_values <- is.infinite(lp_meta[["slvsreads_log"]])
## Error: object 'lp_meta' not found
lp_meta[inf_values, "slvsreads_log"] <- -10
## Error: object 'lp_meta' not found
color_vector <- as.character(color_choices[["strain"]])
names(color_vector) <- names(color_choices[["strain"]])
color_vector <- color_vector[c("z2.2", "z2.3", "unknown")]
names(color_vector) <- c("z2.2", "z2.3", "none")
sl_violin <- ggplot(lp_meta,
aes(x = .data[["condition"]], y = .data[["slvsreads_log"]],
fill = .data[["condition"]])) +
geom_violin() +
geom_point() +
scale_fill_manual(values = color_vector)
## Error: object 'lp_meta' not found
## Error: object 'sl_violin' not found
ggstatsplot::ggbetweenstats(lp_meta, x = "condition", y = "slvsreads_log")
## Error: object 'lp_meta' not found
Make a silly ploidy
plot
I want to make an estimate of ploidy using transcriptomic data. This
is by definition a foold’s errand, but I think it might work.
lp_rpkm <- normalize_expt(lp_expt, convert = "rpkm", filter = TRUE, column = "annot_cds_length", na_to_zero = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'input' in selecting a method for function 'state': object 'lp_expt' not found
## Exclude scaffolds
unwanted <- grepl(pattern = "SCAF", x = fData(lp_rpkm)[["annot_gene_location_text"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'fData': object 'lp_rpkm' not found
## I think my subset logic is bacwards...
lp_wanted <- lp_rpkm[!unwanted, ]
## Error: object 'lp_rpkm' not found
summary_df <- as.data.frame(exprs(lp_wanted))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'lp_wanted' not found
summary_df[["gene_mean"]] <- rowMeans(summary_df, na.rm = TRUE)
## Error: object 'summary_df' not found
summary_df[["chromosome"]] <- fData(lp_wanted)[["annot_chromosome"]]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'fData': object 'lp_wanted' not found
summary_df[["chromosome"]] <- as.factor(summary_df[["chromosome"]])
## Error: object 'summary_df' not found
levels(summary_df[["chromosome"]]) <- c(seq_len(19), "20.1", "20.2", 21:35)
## Error: object 'summary_df' not found
summary_df <- summary_df[, c("gene_mean", "chromosome")] %>%
group_by(chromosome) %>%
summarize(chr_mean = mean(gene_mean, na.rm = TRUE))
## Error: object 'summary_df' not found
min_rpkm <- min(summary_df[["chr_mean"]])
## Error: object 'summary_df' not found
summary_df[["chr_mean"]] <- summary_df[["chr_mean"]] / min_rpkm
## Error: object 'summary_df' not found
ggplot(summary_df, aes(y = chromosome, x = chr_mean)) +
geom_col()
## Error: object 'summary_df' not found
wanted <- pData(lp_wanted)[["knnv2classification"]] == "z22" | pData(lp_wanted)[["knnv2classification"]] == "z23"
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_wanted' not found
lp_z <- lp_wanted[, wanted]
## Error: object 'lp_wanted' not found
z22_samples <- pData(lp_z)[["knnv2classification"]] == "z22"
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_z' not found
z23_samples <- pData(lp_z)[["knnv2classification"]] == "z23"
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_z' not found
lp_z_exprs <- as.data.frame(exprs(lp_z))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'lp_z' not found
lp_z_exprs[["z22_gene_mean"]] <- rowMeans(lp_z_exprs[, z22_samples], na.rm = TRUE)
## Error: object 'lp_z_exprs' not found
lp_z_exprs[["z23_gene_mean"]] <- rowMeans(lp_z_exprs[, z23_samples], na.rm = TRUE)
## Error: object 'lp_z_exprs' not found
lp_z_exprs[["chromosome"]] <- fData(lp_z)[["annot_chromosome"]]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'fData': object 'lp_z' not found
lp_z_means <- lp_z_exprs[, c("z22_gene_mean", "z23_gene_mean", "chromosome")] %>%
group_by(chromosome) %>%
summarize(z22_mean = mean(z22_gene_mean, na.rm = TRUE),
z23_mean = mean(z23_gene_mean, na.rm = TRUE))
## Error: object 'lp_z_exprs' not found
chr_reshaped <- reshape2::melt(lp_z_means, id.vars = "chromosome")
## Error: object 'lp_z_means' not found
chr_reshaped[["chromosome"]] <- factor(chr_reshaped[["chromosome"]],
levels = c(as.character(1:19), "20.1", "20.2",
as.character(21:35)))
## Error: object 'chr_reshaped' not found
putative_aneuploid <- ggplot(chr_reshaped, aes(x = value, y = chromosome)) +
geom_bar(aes(fill = variable), position = "dodge", stat = "identity")
## Error: object 'chr_reshaped' not found
pp(file = "images/putative_aneuploid.svg")
putative_aneuploid
## Error: object 'putative_aneuploid' not found
## png
## 2
## Error: object 'putative_aneuploid' not found
Save all data
structures into one rda
found_idx <- data_structures %in% ls()
if (sum(!found_idx) > 0) {
not_found <- data_structures[!found_idx]
warning("Some datastructures were not generated: ", toString(not_found), ".")
data_structures <- data_structures[found_idx]
}
## Warning: Some datastructures were not generated: lp_lengths, lp_go,
## all_lp_annot, meta, lp_expt, lp_strain, lp_two_strains, lp_cf, lp_cf_known,
## lp_cf_known, lp_susceptibility_historical, lp_susceptibility, lp_zymo,
## new_snps_sufficient, lp_snp, hs_macrophage, hs_u937, lp_macrophage,
## lp_macrophage_filt, lp_macrophage_nosb.
save(list = data_structures, file = glue("rda/tmrc2_data_structures-v{ver}.rda"))
pander::pander(sessionInfo())
## Warning: Your system is mis-configured: '/etc/localtime' is not a symlink
## Warning: It is strongly recommended to set envionment variable TZ to
## 'America/New_York' (or equivalent)
R version 4.5.0 (2025-04-11)
Platform: x86_64-pc-linux-gnu
locale: C
attached base packages: stats,
graphics, grDevices, utils,
datasets, methods and base
other attached packages: hpgltools(v.1.2),
Heatplus(v.3.16.0), ggplot2(v.4.0.0),
glue(v.1.8.0) and dplyr(v.1.1.4)
loaded via a namespace (and not attached):
RColorBrewer(v.1.1-3), jsonlite(v.2.0.0),
correlation(v.0.8.8), datawizard(v.1.2.0),
magrittr(v.2.0.4), farver(v.2.1.2),
rmarkdown(v.2.29), fs(v.1.6.6),
vctrs(v.0.6.5), memoise(v.2.0.1),
paletteer(v.1.6.0), effectsize(v.1.0.1),
htmltools(v.0.5.8.1), S4Arrays(v.1.8.1),
progress(v.1.2.3), curl(v.7.0.0),
broom(v.1.0.10), SparseArray(v.1.8.1),
sass(v.0.4.10), bslib(v.0.9.0),
htmlwidgets(v.1.6.4), plyr(v.1.8.9),
httr2(v.1.2.1), plotly(v.4.11.0),
cachem(v.1.1.0), mime(v.0.13),
lifecycle(v.1.0.4), iterators(v.1.0.14),
pkgconfig(v.2.0.3), Matrix(v.1.7-3),
R6(v.2.6.1), fastmap(v.1.2.0),
GenomeInfoDbData(v.1.2.14), MatrixGenerics(v.1.20.0),
shiny(v.1.11.1), digest(v.0.6.37),
rematch2(v.2.1.2), patchwork(v.1.3.2),
AnnotationDbi(v.1.70.0), S4Vectors(v.0.46.0),
GenomicRanges(v.1.60.0), RSQLite(v.2.4.3),
filelock(v.1.0.3), httr(v.1.4.7),
abind(v.1.4-8), compiler(v.4.5.0),
pander(v.0.6.6), bit64(v.4.6.0-1),
withr(v.3.0.2), S7(v.0.2.0),
backports(v.1.5.0), BiocParallel(v.1.42.1),
DBI(v.1.2.3), R.utils(v.2.13.0),
biomaRt(v.2.64.0), rappdirs(v.0.3.3),
DelayedArray(v.0.34.1), tools(v.4.5.0),
zip(v.2.3.3), httpuv(v.1.6.16),
statsExpressions(v.1.7.1), varhandle(v.2.0.6),
R.oo(v.1.27.1), GOSemSim(v.2.34.0),
promises(v.1.3.3), grid(v.4.5.0),
reshape2(v.1.4.4), fgsea(v.1.34.2),
generics(v.0.1.4), gtable(v.0.3.6),
tzdb(v.0.5.0), R.methodsS3(v.1.8.2),
tidyr(v.1.3.1), data.table(v.1.17.8),
hms(v.1.1.3), xml2(v.1.4.0),
XVector(v.0.48.0), BiocGenerics(v.0.54.0),
foreach(v.1.5.2), pillar(v.1.11.0),
stringr(v.1.5.1), yulab.utils(v.0.2.1),
vroom(v.1.6.5), later(v.1.4.3),
splines(v.4.5.0), BiocFileCache(v.2.16.1),
lattice(v.0.22-7), bit(v.4.6.0),
annotate(v.1.86.1), ggstatsplot(v.0.13.3),
tidyselect(v.1.2.1), GO.db(v.3.21.0),
Biostrings(v.2.76.0), knitr(v.1.50),
IRanges(v.2.42.0), SummarizedExperiment(v.1.38.1),
stats4(v.4.5.0), xfun(v.0.53),
Biobase(v.2.68.0), matrixStats(v.1.5.0),
stringi(v.1.8.7), UCSC.utils(v.1.4.0),
lazyeval(v.0.2.2), yaml(v.2.3.10),
evaluate(v.1.0.4), codetools(v.0.2-20),
tibble(v.3.3.0), qvalue(v.2.40.0),
graph(v.1.86.0), cli(v.3.6.5),
RcppParallel(v.5.1.11-1), parameters(v.0.28.2),
xtable(v.1.8-4), jquerylib(v.0.1.4),
dichromat(v.2.0-0.1), Rcpp(v.1.1.0),
GenomeInfoDb(v.1.44.2), zeallot(v.0.2.0),
dbplyr(v.2.5.0), png(v.0.1-8),
XML(v.3.99-0.19), parallel(v.4.5.0),
rstantools(v.2.5.0), readr(v.2.1.5),
blob(v.1.2.4), prettyunits(v.1.2.0),
bayestestR(v.0.17.0), DOSE(v.4.2.0),
viridisLite(v.0.4.2), GSEABase(v.1.70.0),
scales(v.1.4.0), insight(v.1.4.2),
openxlsx(v.4.2.8), purrr(v.1.1.0),
crayon(v.1.5.3), rlang(v.1.1.6),
cowplot(v.1.2.0), fastmatch(v.1.1-6) and
KEGGREST(v.1.48.1)
message("This is hpgltools commit: ", get_git_commit())
## If you wish to reproduce this exact build of hpgltools, invoke the following:
## > git clone http://github.com/abelew/hpgltools.git
## > git reset 5043179aa73cd71040a7ba82276b0cf26cc661bd
## This is hpgltools commit: Mon Oct 6 12:01:25 2025 -0400: 5043179aa73cd71040a7ba82276b0cf26cc661bd
message("Saving to ", savefile)
## Saving to 01datasets.rda.xz
# tmp <- sm(saveme(filename = savefile))
tmp <- loadme(filename = savefile)
LS0tCnRpdGxlOiAiVE1SQzIgYHIgU3lzLmdldGVudignVkVSU0lPTicpYDogRGF0YSBTZXQgQ3JlYXRpb24iCmF1dGhvcjogImF0YiBhYmVsZXdAZ21haWwuY29tIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCmJpYmxpb2dyYXBoeTogYXRiLmJpYgpvdXRwdXQ6CiBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIGtlZXBfbWQ6IGZhbHNlCiAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keSAubWFpbi1jb250YWluZXIgewogIG1heC13aWR0aDogMTYwMHB4Owp9CmJvZHksIHRkIHsKICBmb250LXNpemU6IDE2cHg7Cn0KY29kZS5yewogIGZvbnQtc2l6ZTogMTZweDsKfQpwcmUgewogIGZvbnQtc2l6ZTogMTZweAp9Cjwvc3R5bGU+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2x1ZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KEhlYXRwbHVzKQpsaWJyYXJ5KGhwZ2x0b29scykKCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHByb2dyZXNzID0gVFJVRSwgdmVyYm9zZSA9IFRSVUUsIHdpZHRoID0gOTAsIGVjaG8gPSBUUlVFKQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZXJyb3IgPSBUUlVFLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gOSwgZmlnLnJldGluYSA9IDIsCiAgb3V0LndpZHRoID0gIjEwMCUiLCBkZXYgPSAicG5nIiwKICBkZXYuYXJncyA9IGxpc3QocG5nID0gbGlzdCh0eXBlID0gImNhaXJvLXBuZyIpKSkKb2xkX29wdGlvbnMgPC0gb3B0aW9ucyhkaWdpdHMgPSA0LCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIGtuaXRyLmR1cGxpY2F0ZS5sYWJlbCA9ICJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemUgPSAxMikpCnZlciA8LSBTeXMuZ2V0ZW52KCJWRVJTSU9OIikKcHJldmlvdXNfZmlsZSA8LSAiIgpydW5kYXRlIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCBmb3JtYXQgPSAiJVklbSVkIikKCnJtZF9maWxlIDwtICIwMWRhdGFzZXRzLlJtZCIKc2F2ZWZpbGUgPC0gZ3N1YihwYXR0ZXJuID0gIlxcLlJtZCIsIHJlcGxhY2UgPSAiXFwucmRhXFwueHoiLCB4ID0gcm1kX2ZpbGUpCmRhdGFfc3RydWN0dXJlcyA8LSBjKCkKYGBgCgojIFRPRE8KCjEuICBIYXZlIGEgc2V0IHdoZXJlIHdlIG1lcmdlIDIuMS8yLjIsIDIuMy8yLjQuCjIuICBSZXByZXNlbnQgdGhlIHRyYW5zaXRpb24gZnJvbSBhIGdsb2JhbCB2aWV3IG9mIHRoZSBzYW1wbGVzIHdpdGhvdXQKICAgIGFueSBjbGFzc2lmaWNhdGlvbiwgdGhlbiBzdGF0ZSB0aGUgc3VicG9wdWxhdGlvbnMvenltb2RlbWVzLCB0aGVuCiAgICBhZGQgc2Vuc2l0aXZpdHkvcmVzaXN0YW5jZSwgdGhlbiBjdXJlL2ZhaWwuCjMuICBQZXJoYXBzIG1ha2UgYW4gZXhwbGljaXQgcGxvdCB3aGVyZSBhbGwgc2FtcGxlcyBhcmUgb25lIGNvbG9yCiAgICBleGNlcHRpbmcgYSByZWxhdGl2ZWx5IHNtYWxsIG51bWJlciBvZiBwcmV2aW91c2x5IGFzc2F5ZWQgc2V0PwogICAgVGhlIHNhbXBsZXMgd2hpY2ggd291bGQgYmUgY29sb3JlZCBpbiB0aGlzIHZpZXcgd291bGQgYmUgZnJvbQogICAgT2xnYSdzIDIwMTQgcGFwZXIgSSB0aGluay4KNC4gIE1ha2UgYSBmbG93IGRpYWdyYW0gZ29pbmcgZnJvbSBzL3IgLT4gc3VicG9wdWxhdGlvbiAtPgogICAgYy9mL3UuIChzYW5rZXkpCjUuICBNYWtlIGEgdGFibGUgc2ltaWxhciB0byB0aGUgVE1SQzMgY29udGFpbmluZyB0aGUgc3RhdHVzZXMgb2YgdGhlCiAgICBzYW1wbGVzLgo2LiAgRXhwbGljaXRseSBjb25zaWRlciBtZXRhZGF0YSBjb2x1bW4gJ1AnIGZvciByZWZlcmVuY2Ugc3RyYWlucyAtLQogICAgbWFrZSBhbiBhbGwgZ3JleSBwbG90IHdpdGggYSBmZXcgc2FtcGxlcyBjb2xvcmVkIHRha2VuIGZyb20gdGhpcwogICAgY29sdW1uLgoKIyBDaGFuZ2Vsb2cKCiMjIDIwMjMwNTI0LzIwMjMwNjI4CgoqIEZyb3plbiBtZXRhZGF0YSwgbm93IHVzaW5nIHNhbXBsZSBzaGVldCAnQ2xpbmljYWxTdHJhaW5zX1RNUkMyX0Zyb3plblwgMjEwNjIwMjMueGxzeCcKCiMjIDIwMjMwNDEwCgoqIFVwZGF0aW5nIHRoZSB2ZXJzaW9uIG51bWJlciBkdWUgdG8gc29tZSBtb2RlcmF0ZWx5IGludHJ1c2l2ZSBjaGFuZ2VzCiAgSSBtYWRlIGluIG9yZGVyIHRvIG1vcmUgY2FyZWZ1bGx5IGNyZWF0ZSBwbG90cyBvZiB0aGUgZGlmZmVyZW50aWFsCiAgZXhwcmVzaXNvbiBkYXRhLiAgSSBkb24ndCB0aGluayBhbnl0aGluZyBJIGRpZCBzaG91bGQgYWN0dWFsbHkKICBjaGFuZ2UgYW55IG9mIHRoZSBkYXRhLCBidXQgc29tZSBvZiB0aGUgYW5hbHlzZXMgYXJlIGRlZmluaXRlbHkKICBhZmZlY3RlZCAobm90ZSB0aGF0IHRoZSBvbmx5IGNoYW5nZSBpbiByZXN1bHRzIGlzIGR1ZSB0byBhIG1pc3Rha2UgSQogIG1hZGUgaW4gZGVmaW5pbmcgb25lIG9mIHRoZSBjb250cmFzdHMsIGFsbCBvdGhlciBjaGFuZ2VzIGFyZSBqdXN0CiAgcGxvdCBhZXN0aGV0aWMgaW1wcm92ZW1lbnRzKQoKIyMgMjAyMzAyMDUKCiogRGlkIHRoZSBzdHVmZiBvbiB0aGlzIG1vcm5pbmcncyBUT0RPIHdoaWNoIGNhbWUgb3V0IG9mIHRoaXMKICBtb3JuaW5nJ3MgbWVldGluZzogZG8gYSBQQ0Egd2l0aG91dCB0aGUgb2RkYmFsbCBzdHJhaW5zIChhbHJlYWR5CiAgZG9uZSBpbiB0aGUgd29ya3NoZWV0KSwgaGlnaGxpZ2h0IHJlZmVyZW5jZSBzdHJhaW5zLCBhbmQgYWRkIEwubWFqb3IKICBJRHMgYW5kIERlc2NyaXB0aW9ucyAoZG9uZSBieSBhcHBlbmRpbmcgYSBjb2xsYXBzZWQgdmVyc2lvbiBvZiB0aGUKICBvcnRob2xvZyBkYXRhIHRvIHRoZSBhbGxfbHBfYW5ub3QgZGF0YSkuCgoqIEZpeGVkIGh1bWFuIElEcyBmb3IgdGhlIG1hY3JvcGhhZ2UgZGF0YS4KKiBDaGFuZ2VkIGlucHV0IG1ldGFkYXRhIHNoZWV0czogcHJpbWFyaWx5IGJlY2F1c2UgSSBvbmx5IHJlbWVtYmVyZWQKICB5ZXN0ZXJkYXkgdG8gZmluaXNoIHRoZSBTTCBzZWFyY2ggZm9yIHNhbXBsZXMgPlRNUkMyMDA5NS4gIFRoZXkgYXJlCiAgcnVubmluZyBub3cgYW5kIHdpbGwgYmUgYWRkZWQgbW9tZW50YXJpbHkgKEkgd2lsbCBoYXZlIHRvIHJlZG93bmxvYWQKICB0aGUgc2hlZXQpLgoqIFNldHRpbmcgdXAgdG8gbWFrZSBhIGhjbHVzdC9waHlsb2dlbmV0aWMgdHJlZSBvZiBzdHJhaW5zLCB1c2UgdGhlc2UKICBhcmUgcmVmZXJlbmNlOiAyMTY4KDIuMyksIDIyNzIoMi4yKSwgZm9yIG90aGVyIDIueCBjaG9vc2UKICBhcmJpdHJhcmlseSAobG93ZXIgbnVtYmVycyBhcmUgYmV0dGVyKS4KKiBBZGRlZCBhbm90aGVyIHNhbml0aXplIGNvbHVtbnMgY2FsbCBmb3IgQW50aW1vbnkgdnMuIGFudGltb255IGFuZCBOb25lIHZzLgogIG5vbmUgaW4gdGhlIFRNUkMyIG1hY3JvcGhhZ2Ugc2FtcGxlcy4KCiMgSW50cm9kdWN0aW9uCgpUaGlzIGRvY3VtZW50IGlzIGludGVuZGVkIHRvIGNyZWF0ZSB0aGUgZGF0YSBzdHJ1Y3R1cmVzIHVzZWQgdG8KZXZhbHVhdGUgb3VyIFRNUkMyIHNhbXBsZXMuICBJbiBzb21lIGNhc2VzLCB0aGlzIGluY2x1ZGVzIG9ubHkgdGhvc2UKc2FtcGxlcyBzdGFydGluZyBpbiAyMDE5OyBpbiBvdGhlciBpbnN0YW5jZXMgSSBhbSBpbmNsdWRpbmcgb3VyCnByZXZpb3VzICgyMDE1LTIwMTYpIHNhbXBsZXMuCgpJbiBhbGwgY2FzZXMgdGhlIHByb2Nlc3NpbmcgcGVyZm9ybWVkIHdhczoKCjEuICBEZWZhdWx0IHRyaW1taW5nIHdhcyBwZXJmb3JtZWQuCjIuICBIaXNhdDIgd2FzIHVzZWQgdG8gbWFwIHRoZSByZW1haW5pbmcgcmVhZHMgYWdhaW5zdCB0aGUgTGVpc2htYW5pYQogICAgcGFuYW1lbnNpcyBnZW5vbWUgcmV2aXNpb24gMzYuCjMuICBUaGUgYWxpZ25tZW50cyBmcm9tIGhpc2F0MiB3ZXJlIHVzZWQgdG8gY291bnQgcmVhZHMvZ2VuZSBhZ2FpbnN0IHRoZQogICAgcmV2aXNpb24gMzYgYW5ub3RhdGlvbnMgd2l0aCBodHNlcS4KNC4gIFRoZXNlIGFsaWdubWVudHMgd2VyZSBhbHNvIHBhc3NlZCB0byB0aGUgcGlsZXVwIGZ1bmN0aW9uYWxpdHkgb2Ygc2FtdG9vbHMKICAgIGFuZCB0aGUgdmNmL2JjZiB1dGlsaXRpZXMgaW4gb3JkZXIgdG8gbWFrZSBhIG1hdHJpeCBvZiBhbGwgb2JzZXJ2ZWQKICAgIGRpZmZlcmVuY2VzIGJldHdlZW4gZWFjaCBzYW1wbGUgd2l0aCByZXNwZWN0IHRvIHRoZSByZWZlcmVuY2UuCjUuICBUaGUgZnJlZWJheWVzIHZhcmlhbnQgZXN0aW1hdGlvbiB0b29sIHdhcyB1c2VkIGluIGFkZGl0aW9uIHRvICM0CiAgICB0byBzZWFyY2ggZm9yIHZhcmlhbnQgcG9zaXRpb25zIGluIGEgbW9yZSByb2J1c3QgZmFzaGlvbi4KNi4gIFRoZSB0cmltbWVkIHJlYWRzIHdlcmUgcGFzc2VkIHRvIGtyYWtlbjIgdXNpbmcgYSB2aXJhbCBkYXRhYmFzZSBpbgogICAgb3JkZXIgdG8gbG9vayBmb3Igc2FtcGxlcyB3aXRoIHBvdGVudGlhbCBMUlYgc2VxdWVuY2UuCjcuICBBbiBleHBsaWNpdCwgZ3JlcC1iYXNlZCBzZWFyY2ggZm9yIHNwbGljZWQgbGVhZGVyIHJlYWRzIHdhcyB1c2VkCiAgICBhZ2FpbnN0IGFsbCBodW1hbi1kZXJpdmVkIHNhbXBsZXMuICBUaGUgcmVzdWx0cyBmcm9tIHRoaXMgd2VyZQogICAgY29weS9wYXN0ZWQgaW50byB0aGUgc2FtcGxlIHNoZWV0LgoKIyBOb3RlcyAyMDIyMTIwNiBtZWV0aW5nCgpJIGFtIHRoaW5raW5nIHRoYXQgdGhpcyBtZWV0aW5nIHdpbGwgYnJpbmcgTWFyaWEgQWRlbGFpZGEgZnVsbHkgYmFjawppbnRvIHRoZSBhbmFseXNlcyBvZiB0aGUgcGFyYXNpdGUgZGF0YSwgYW5kIHRoZXJlZm9yZSBtYXkgZm9jdXMKcHJpbWFyaWx5IG9uIHRoZSBnb2FscyByYXRoZXIgdGhhbiB0aGUgYW5hbHlzZXM/CgoqIE1hcmlhIEFkZWxhaWRhIG1lZXRpbmcgd2l0aCBPbGdsYS9NYXJpYW5hOiBpbnRlZ3JhdGluZwogIHRyYW5zY3JpcHRvbWljcy9nZW5vbWljcyBxdWVzdGlvbi4KKiBQYXBlciBvbiByZWxhdGlvbnNoaXAgYnR3biBwcmltYXJ5IG1ldGFkYXRhIGZhY3RvcnMgdmlhIHRyYW5zY3JpcHRvbWUvZ2Vub21lLgoqIFNlY29uZCBvbiBkcnVnIHN1c2NlcHRpYmlsaXR5IHdpdGhvdXQgdGhvc2UgZmFjdG9ycyAoSSB0aGluayB0aGlzCiAgbWVhbnMgdGhlIG1hY3JvcGhhZ2VzKQoqIERlZmluaXRpb24gb2Ygc3BlY2llcz8gIE1BRzogRGVmaW5lIGNvbnNlbnN1cyBzZXF1ZW5jZXMgZm9yIHZhcmlvdXMKICBzdHJhaW5zL3NwZWNpZXMuICBXZSBlZmZlY3RpdmVseSBoYXZlIHRoaXMgb24gaGFuZCwgdGhvdWdoIHRoZQogIHF1YWxpdHkgbWF5IGJlIGEgbGl0dGxlIGxlc3MgZ29vZCBmb3IgMi4zLgoqIFJlc3VsdGluZyBnb2FsOiBDcmVhdGUgYSB0cmVlIG9mIHRoZSBzdHJhaW5zIChJIGFtIGp1c3QgZ29pbmcgdG8KICBjYWxsIHp5bW9kZW1lcyBzdHJhaW5zIGZyb20gbm93IG9uKS4KKiogIFdoYXQgb3JnYW5pc21zIHdvdWxkIHdlIGluY2x1ZGUgaW4gYSB0cmVlIHRvIGRlc2NyaWJlIHRoZXNlCiAgICByZWxhdGlvbnNoaXBzOiBndXlhbmVuc2lzLCBicmF6aWxpZW5zaXMgMjkwNCwgMi4yLCAyLjMsIDIuMSwgMi40LAogICAgcGFuYW1lbnNpcyByZWZlcmVuY2UsIHBlcnV2aWFuaWEoc3A/IEkgaGF2ZSBub3Qgc2VlbiB0aGlzIGdlbm9tZSksCiAgICBwYW5hbWEsIDI5MDM7IGFjdHVhbGx5IHRoaXMgbWF5IGJlIHRyaWNreSBiZWNhdXNlIHdlIGhhdmUgYWx3YXlzCiAgICBkb25lIHRoaXMgd2l0aCBhIHNwZWNpZmljIHJlZmVyZW5jZSBzdHJhaW4gKHBhbmFtZW5zaXMgY29sKSB3aGljaCBpcwogICAgb25lIG9mIHRoZSBzdHJhaW5zIGluIHRoZSBjb21wYXJpc29uLiAgaG1tLi4uCioqICBDaGVjayB0aGUgbW9zdCB2YXJpYW50IHN0cmFpbnMgZm9yIGlkZW50aXR5IChMdWMpCioqICBNZXRob2RzIGZvciBjcmVhdGluZyB0cmVlLCB0cmFkaXRpb25hbCBwaHlsb2dlbnkgdnMuIHZhcmlhbnQKICAgIGhjbHVzdD8KKiBQQ1IgcXVlcmllcywgd29ya3Mgd2VsbCBpZiBvbmUgcGVyZm9ybXMgc2FuZ2VyIHNlcXVlbmNpbmcuCgojIyBNdWx0aXBsZSBkYXRhc2V0cwoKSW4gYSBjb3VwbGUgb2YgaW1wb3J0YW50IHdheXMgdGhlIFRNUkMyIGRhdGEgaXMgbXVjaCBtb3JlIGNvbXBsZXggdGhhbiB0aGUKVE1SQzM6CgoxLiAgSXQgY29tcHJpc2VzIG11bHRpcGxlLCBjb21wbGV0ZWx5IHNlcGFyYXRlIHF1ZXJpZXM6CiAgICBhLiAgU2VxdWVuY2luZyB0aGUgcGFyYXNpdGUgc2FtcGxlcwogICAgYi4gIFNlcXVlbmNpbmcgYSBzZXQgb2YgaHVtYW4gbWFjcm9waGFnZSBzYW1wbGVzIHdoaWNoIHdlcmUgaW5mZWN0ZWQKICAgICAgICB3aXRoIHNwZWNpZmljIHBhcmFzaXRlIHNhbXBsZXMuCjIuICBUaGUgcGFyYXNpdGUgdHJhbnNjcmlwdG9taWMgc2FtcGxlcyBjb21wcmlzZSBtdWx0aXBsZSBkaWZmZXJlbnQKICAgIHR5cGVzIG9mIHF1ZXJpZXM6CiAgICBhLiAgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gdG8gbG9vayBhdCBzdHJhaW4sIHN1c2NlcHRpYmlsaXR5LCBhbmQKICAgIGNsaW5pY2FsIG91dGNvbWVzLgogICAgYi4gIEluZGl2aWR1YWwgdmFyaWFudCBzZWFyY2hlcyB0byBsb29rIGZvciBwb3RlbnRpYWxseSB1c2VmdWwKICAgIFNOUHMgZm9yIGNsYXNzaWZpY2F0aW9uIG9mIHBhcmFzaXRlIHNhbXBsZXMuCjMuICBUaGUgaHVtYW4gbWFjcm9waGFnZSBzYW1wbGVzIG1heSBiZSB1c2VkIHRvIHF1ZXJ5IGJvdGggdGhlIGhvc3QKICAgIGFuZCBwYXJhc2l0ZSB0cmFuc2NyaXB0b21lcyBiZWNhdXNlIChhdCBsZWFzdCB3aGVuIG5vdCBkcnVnCiAgICB0cmVhdGVkKSB0aGVyZSBpcyBhIHRyZW1lbmRvdXMgcG9wdWxhdGlvbiBvZiBwYXJhc2l0ZSByZWFkcyBpbgogICAgdGhlbS4KCiMjIFNhbXBsZSBzaGVldChzKQoKT3VyIHNoYXJlZCBvbmxpbmUgc2FtcGxlIHNoZWV0IGlzIG5lYXJseSBzdGF0aWMgYXQgdGhlIHRpbWUgb2YgdGhpcwp3cml0aW5nICgyMDIyMDkpLCBJIGV4cGVjdCBhdCB0aGlzIHBvaW50IHRoZSBvbmx5IGxpa2VseSB1cGRhdGVzIHdpbGwKYmUgdG8gYW5ub3RhdGUgc29tZSBzdHJhaW5zIGFzIG1vcmUgb3IgbGVzcyBzdXNjZXB0aWJsZSB0byBkcnVnCnRyZWF0bWVudC4KCmBgYHtyfQpzYW1wbGVfc2hlZXQgPC0gInNhbXBsZV9zaGVldHMvQ2xpbmljYWxTdHJhaW5zX1RNUkMyLnhsc3giCm1hY3JvcGhhZ2Vfc2hlZXQgPC0gInNhbXBsZV9zaGVldHMvdG1yYzJfbWFjcm9waGFnZV9zYW1wbGVzLnhsc3giCmBgYAoKIyMjIE1vZGlmeSB0aGUgc2FtcGxlIHNoZWV0CgpUaGUgZm9sbG93aW5nIGJsb2NrIHByb3ZpZGVzIGFuIGV4YW1wbGUgaW52b2NhdGlvbiBvZiBob3cgSQphdXRvbWF0aWNhbGx5IGV4dHJhY3QgdGhpbmdzIGxpa2UgcGVyY2VudCByZWFkcyBtYXBwZWQvdHJpbW1lZC9ldGMKZnJvbSB0aGUgbG9ncyBwcm9kdWNlZCBieSB0cmltb21hdGljL2N1dGFkYXB0L2hpc2F0L3NhbG1vbi9ldGMuICBUaGUKY2F2ZWF0IGlzIHRoYXQgdGhpcyBjb250YWluZXIgb25seSBoYXMgYSBzbWFsbCBwb3J0aW9uIG9mIHRoZSBtYXRlcmlhbAphdmFpbGFibGUgaW4gdGhlIG1haW4gd29ya2luZyB0cmVlLCBhcyBhIHJlc3VsdCB0aGUgbmV3IGNvbHVtbnMgYWRkZWQKdG8gdGhlIHNhbXBsZSBzaGVldCBhcmUgcmVsYXRpdmVseSBzcGFyc2UgY29tcGFyZWQgdG8gd2hhdCBJIGdldCBvbiBteQpjb21wdXRlci4KCkluIGFkZGl0aW9uLCBiZWNhdXNlIHRoZXNlIHNhbXBsZXMgaGF2ZSBnb25lIHRocm91Z2ggfiAzIGRpZmZlcmVudAp2ZXJzaW9ucyBvZiBteSBwaXBlbGluZSwgYW5kIHRoZSBjb2RlIHdoaWNoIGV4dHJhY3RzIHRoZSBudW1iZXJzCmV4cGxpY2l0bHkgYXNzdW1lcyBvbmx5IHRoZSBtb3N0IHJlY2VudCB2ZXJzaW9uIChiZWNhdXNlIGl0IGlzIHRoZQpiZXN0ISksIGl0IGRvZXMgbm90IGdldCBvdXQgdGhlIGRhdGEgZm9yIGFsbCB0aGUgc2FtcGxlcy4KCmBgYHtyfQptb2RpZmllZCA8LSBnYXRoZXJfcHJlcHJvY2Vzc2luZ19tZXRhZGF0YShzYW1wbGVfc2hlZXQsIHNwZWNpZXMgPSAibHBhbmFtZW5zaXNfdjM2IikKYGBgCgojIEFubm90YXRpb25zCgpFdmVyeXRoaW5nIHdoaWNoIGZvbGxvd3MgZGVwZW5kcyBvbiB0aGUgRXhpc3RpbmcgVHJpVHJ5cERCIGFubm90YXRpb25zIHJldmlzaW9uCjQ2LCBjaXJjYSAyMDE5LiAgVGhlIGZvbGxvd2luZyBibG9jayBsb2FkcyBhIGRhdGFiYXNlIG9mIHRoZXNlIGFubm90YXRpb25zIGFuZAp0dXJucyBpdCBpbnRvIGEgbWF0cml4IHdoZXJlIHRoZSByb3dzIGFyZSBnZW5lcyBhbmQgY29sdW1ucyBhcmUgYWxsIHRoZQphbm5vdGF0aW9uIHR5cGVzIHByb3ZpZGVkIGJ5IFRyaVRyeXBEQi4KClRoZSBzYW1lIGRhdGFiYXNlIHdhcyB1c2VkIHRvIGNyZWF0ZSBhIG1hdHJpeCBvZiBvcnRob2xvZ291cyBnZW5lcyBiZXR3ZWVuCkwucGFuYW1lbnNpcyBhbmQgYWxsIG9mIHRoZSBvdGhlciBzcGVjaWVzIGluIHRoZSBUcmlUcnlwREIuCgpUaGUgc2FtZSBkYXRhYmFzZSBvZiBhbm5vdGF0aW9ucyBhbHNvIHByb3ZpZGVzIG1hcHBpbmdzIHRvIHRoZSBzZXQgb2YKYW5ub3RhdGVkIEdPIGNhdGVnb3JpZXMgZm9yIHRoZSBMLnBhbmFtZW5zaXMgZ2Vub21lIGFsb25nIHdpdGggZ2VuZQpsZW5ndGhzLgoKVGhlIGZvbGxvd2luZyBibG9jayBhc3N1bWVzIG9uZSBoYXMgYWNjZXNzIHRvIHRyaXRyeXBkYi5vcmcsIHdoaWNoIGlzCm5vIGN1cnJlbnRseSBndWFyYW50ZWVkLiAgVGh1cyBJIGJ1bmRsZWQgYSBwcmUtZ2VuZXJhdGVkIGNvcHkgb2YgdGhlCmdlbm9tZSwgVHhkYiwgYW5kIGFubm90YXRpb25zLgoKYGBge3IsIGV2YWw9RkFMU0V9CiMjIG1ldGEgPC0gZG93bmxvYWRfZXVwYXRoX21ldGFkYXRhKHdlYnNlcnZpY2UgPSAidHJpdHJ5cGRiIiwgZXVfdmVyc2lvbiA9ICJ2NDYiKQptZXRhIDwtIGRvd25sb2FkX2V1cGF0aF9tZXRhZGF0YSh3ZWJzZXJ2aWNlID0gInRyaXRyeXBkYiIpCnBhbmFtZW5zaXNfZW50cnkgPC0gZ2V0X2V1cGF0aF9lbnRyeSgiTUhPTSIsIG1ldGFkYXRhID0gbWV0YVtbInZhbGlkIl1dKQpwYW5hbWVuc2lzX2RiIDwtIG1ha2VfZXVwYXRoX29yZ2RiKHBhbmFtZW5zaXNfZW50cnkpCnBhbmFtZW5zaXNfcGtnIDwtIHBhbmFtZW5zaXNfZGJbWyJwa2duYW1lIl1dCnBhY2thZ2VfbmFtZSA8LSBwYW5hbWVuc2lzX2RiW1sicGtnbmFtZSJdXQppZiAoaXMubnVsbChwYW5hbWVuc2lzX3BrZykpIHsKICBwYW5hbWVuc2lzX3BrZyA8LSBwYW5hbWVuc2lzX2RiW1sib3JnZGJfbmFtZSJdXQogIHBhY2thZ2VfbmFtZSA8LSBwYW5hbWVuc2lzX3BrZwp9CnR0IDwtIGxpYnJhcnkocGFuYW1lbnNpc19wa2csIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkKcGFuYW1lbnNpc19wa2cgPC0gZ2V0MChwYW5hbWVuc2lzX3BrZykKYWxsX2ZpZWxkcyA8LSBjb2x1bW5zKHBhbmFtZW5zaXNfcGtnKQphbGxfbHBfYW5ub3QgPC0gc20obG9hZF9vcmdkYl9hbm5vdGF0aW9ucygKICAgIHBhbmFtZW5zaXNfcGtnLAogICAga2V5dHlwZSA9ICJnaWQiLAogICAgZmllbGRzID0gYygiYW5ub3RfZ2VuZV9lbnRyZXpfaWQiLCAiYW5ub3RfZ2VuZV9uYW1lIiwKICAgICAgICAgICAgICAgImFubm90X3N0cmFuZCIsICJhbm5vdF9jaHJvbW9zb21lIiwgImFubm90X2Nkc19sZW5ndGgiLAogICAgICAgICAgICAgICAiYW5ub3RfZ2VuZV9wcm9kdWN0IikpKSRnZW5lcwoKbHBfZ28gPC0gbG9hZF9vcmdkYl9nbyhwYWNrYWdlX25hbWUpCmxwX2dvIDwtIGxwX2dvWywgYygiR0lEIiwgIkdPIildCmxwX2xlbmd0aHMgPC0gYWxsX2xwX2Fubm90WywgYygiZ2lkIiwgImFubm90X2Nkc19sZW5ndGgiKV0KY29sbmFtZXMobHBfbGVuZ3RocykgIDwtIGMoIklEIiwgImxlbmd0aCIpCmFsbF9scF9hbm5vdFtbImFubm90X2dlbmVfcHJvZHVjdCJdXSA8LSB0b2xvd2VyKGFsbF9scF9hbm5vdFtbImFubm90X2dlbmVfcHJvZHVjdCJdXSkKb3J0aG9zIDwtIHNtKGV4dHJhY3RfZXVwYXRoX29ydGhvbG9ncyhkYiA9IHBhbmFtZW5zaXNfcGtnKSkKZGF0YV9zdHJ1Y3R1cmVzIDwtIGMoZGF0YV9zdHJ1Y3R1cmVzLCAibHBfbGVuZ3RocyIsICJscF9nbyIsICJhbGxfbHBfYW5ub3QiLCAibWV0YSIpCmBgYAoKYGBge3J9CmFsbF9pbnN0YWxsZWQgPC0gcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpCmNhbmRpZGF0ZXMgPC0gZ3JlcGwocGF0dGVybiA9ICJeb3JnLkxwYW5hbWVuc2lzLk1IT00uKnY2OC5lZy5kYiIsIHggPSBhbGxfaW5zdGFsbGVkKQpvcmdkYl9wa2dfbmFtZSA8LSBhbGxfaW5zdGFsbGVkW2NhbmRpZGF0ZXNdCgp0dCA8LSBsaWJyYXJ5KG9yZ2RiX3BrZ19uYW1lLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCnBhbmFtZW5zaXNfcGtnIDwtIGdldDAob3JnZGJfcGtnX25hbWUpCmFsbF9maWVsZHMgPC0gY29sdW1ucyhwYW5hbWVuc2lzX3BrZykKYWxsX2xwX2Fubm90IDwtIHNtKGxvYWRfb3JnZGJfYW5ub3RhdGlvbnMoCiAgICBwYW5hbWVuc2lzX3BrZywKICAgIGtleXR5cGUgPSAiZ2lkIiwKICAgIGZpZWxkcyA9IGMoImFubm90X2dlbmVfZW50cmV6X2lkIiwgImFubm90X2dlbmVfbmFtZSIsICJhbm5vdF9nZW5lX2xvY2F0aW9uX3RleHQiLAogICAgICAgICAgICAgICAiYW5ub3Rfc3RyYW5kIiwgImFubm90X2Nocm9tb3NvbWUiLCAiYW5ub3RfY2RzX2xlbmd0aCIsCiAgICAgICAgICAgICAgICJhbm5vdF9nZW5lX3Byb2R1Y3QiKSkpJGdlbmVzCgpscF9nbyA8LSBsb2FkX29yZ2RiX2dvKHBhbmFtZW5zaXNfcGtnKQpscF9nbyA8LSBscF9nb1ssIGMoIkdJRCIsICJHTyIpXQpscF9sZW5ndGhzIDwtIGFsbF9scF9hbm5vdFssIGMoImdpZCIsICJhbm5vdF9jZHNfbGVuZ3RoIildCmNvbG5hbWVzKGxwX2xlbmd0aHMpICA8LSBjKCJJRCIsICJsZW5ndGgiKQphbGxfbHBfYW5ub3RbWyJhbm5vdF9nZW5lX3Byb2R1Y3QiXV0gPC0gdG9sb3dlcihhbGxfbHBfYW5ub3RbWyJhbm5vdF9nZW5lX3Byb2R1Y3QiXV0pCmRhdGFfc3RydWN0dXJlcyA8LSBjKGRhdGFfc3RydWN0dXJlcywgImxwX2xlbmd0aHMiLCAibHBfZ28iLCAiYWxsX2xwX2Fubm90IiwgIm1ldGEiKQpgYGAKCiMjIFJlcGVhdCBmb3IgdGhlIEwubWFqb3IgYW5ub3RhdGlvbnMKClJlY2VudGx5IHRoZXJlIHdhcyBhIHJlcXVlc3QgdG8gaW5jbHVkZSB0aGUgTGVpc2htYW5pYSBtYWpvciBnZW5lIElEcwphbmQgZGVzY3JpcHRpb25zLiAgVGh1cyBJIHdpbGwgZXh0cmFjdCB0aGVtIGFsb25nIHdpdGggdGhlIG9ydGhvbG9ncwphbmQgYXBwZW5kIHRoYXQgdG8gdGhlIGFubm90YXRpb25zIHVzZWQuCgpIYXZpbmcgc3BlbnQgdGhlIHRpbWUgdG8gcnVuIHRoZSBmb2xsb3dpbmcgY29kZSwgSSByZWFsaXplZCB0aGF0IHRoZQpvcnRob2xvZ3MgZGF0YSBzdHJ1Y3R1cmUgYWJvdmUgYWN0dWFsbHkgYWxyZWFkeSBoYXMgdGhlIGdlbmUgSURzIGFuZApkZXNjcmlwdGlvbnMuCgpUaHVzIEkgd2lsbCBsZWF2ZSBteSBxdWVyeSBpbiBwbGFjZSB0byBleHRyYWN0IHRoZSBtYWpvciBhbm5vdGF0aW9ucywKYnV0IGZvbGxvdyBpdCB1cCB3aXRoIGEgY29sbGFwc2Ugb2YgdGhlIG1ham9yIG9ydGhvbG9ncyBhbmQgYXBwZW5kaW5nCm9mIHRoYXQgdG8gdGhlIHBhbmFtZW5zaXMgYW5ub3RhdGlvbnMuCgpgYGB7ciBldXBhdGhkYl9sbWFqb3IsIGV2YWw9RkFMU0V9Cm9yZ2RiIDwtICJvcmcuTG1ham9yLkZyaWVkbGluLnY0OS5lZy5kYiIKdHQgPC0gc20obGlicmFyeShvcmdkYiwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkKbWFqb3JfZGIgPC0gb3JnLkxtYWpvci5GcmllZGxpbi52NDkuZWcuZGIKYWxsX2ZpZWxkcyA8LSBjb2x1bW5zKHBhbl9kYikKYWxsX2xtX2Fubm90IDwtIHNtKGxvYWRfb3JnZGJfYW5ub3RhdGlvbnMoCiAgICBtYWpvcl9kYiwKICAgIGtleXR5cGUgPSAiZ2lkIiwKICAgIGZpZWxkcyA9IGMoImFubm90X2dlbmVfZW50cmV6X2lkIiwgImFubm90X2dlbmVfbmFtZSIsCiAgICAgICAgICAgICAgICJhbm5vdF9zdHJhbmQiLCAiYW5ub3RfY2hyb21vc29tZSIsICJhbm5vdF9jZHNfbGVuZ3RoIiwKICAgICAgICAgICAgICAgImFubm90X2dlbmVfcHJvZHVjdCIpKSkkZ2VuZXMKCndhbnRlZF9vcnRob3NfaWR4IDwtIG9ydGhvc1tbIk9SVEhPTE9HU19TUEVDSUVTIl1dID09ICJMZWlzaG1hbmlhIG1ham9yIHN0cmFpbiBGcmllZGxpbiIKc3VtKHdhbnRlZF9vcnRob3NfaWR4KQp3YW50ZWRfb3J0aG9zIDwtIG9ydGhvc1t3YW50ZWRfb3J0aG9zX2lkeCwgXQp3YW50ZWRfb3J0aG9zIDwtIHdhbnRlZF9vcnRob3NbLCBjKCJHSUQiLCAiT1JUSE9MT0dTX0lEIiwgIk9SVEhPTE9HU19OQU1FIildCgpjb2xsYXBzZWRfb3J0aG9zIDwtIHdhbnRlZF9vcnRob3MgJT4lCiAgZ3JvdXBfYnkoR0lEKSAlPiUKICBzdW1tYXJpc2UoY29sbGFwc2VkX2lkID0gc3RyaW5ncjo6c3RyX2MoT1JUSE9MT0dTX0lELCBjb2xsYXBzZSA9ICIgOyAiKSwKICAgICAgICAgICAgY29sbGFwc2VkX25hbWUgPSBzdHJpbmdyOjpzdHJfYyhPUlRIT0xPR1NfTkFNRSwgY29sbGFwc2UgPSAiIDsgIikpCmFsbF9scF9hbm5vdCA8LSBtZXJnZShhbGxfbHBfYW5ub3QsIGNvbGxhcHNlZF9vcnRob3MsIGJ5LnggPSAicm93Lm5hbWVzIiwKICAgICAgICAgICAgICAgICAgICAgIGJ5LnkgPSAiR0lEIiwgYWxsLnggPSBUUlVFKQpyb3duYW1lcyhhbGxfbHBfYW5ub3QpIDwtIGFsbF9scF9hbm5vdFtbIlJvdy5uYW1lcyJdXQphbGxfbHBfYW5ub3RbWyJSb3cubmFtZXMiXV0gPC0gTlVMTApkYXRhX3N0cnVjdHVyZXMgPC0gYyhkYXRhX3N0cnVjdHVyZXMsICJscF9sZW5ndGhzIiwgImxwX2dvIiwgImFsbF9scF9hbm5vdCIpCmBgYAoKIyBMb2FkIGEgZ2Vub21lCgpUaGUgZm9sbG93aW5nIGJsb2NrIGxvYWRzIHRoZSBmdWxsIGdlbm9tZSBzZXF1ZW5jZSBmb3IgcGFuYW1lbnNpcy4gIFdlCm1heSB1c2UgdGhpcyBsYXRlciB0byBhdHRlbXB0IHRvIGVzdGltYXRlIFBDUiBwcmltZXJzIHRvIGRpc2Nlcm4gc3RyYWlucy4KCkkgYW0gbm90IHN1cmUgaG93IHRvIGluY3JlYXNlIHRoZSBudW1iZXIgb2Ygb3BlbiBmaWxlcyBpbiBhIGNvbnRhaW5lciwKYXMgYSByZXN1bHQgdGhpcyBkb2VzIG5vdCB3b3JrLgoKYGBge3IgZ2Vub21lfQojIyB0ZXN0aW5nX3BhbmFtZW5zaXMgPC0gbWFrZV9ldXBhdGhfYnNnZW5vbWUoZW50cnkgPSBwYW5hbWVuc2lzX2VudHJ5LCBldV92ZXJzaW9uID0gInY0NiIpCnBrZ19jYW5kaWRhdGVzIDwtIGdyZXBsKHggPSBhbGxfaW5zdGFsbGVkLCBwYXR0ZXJuID0gIkJTR2Vub21lXFwuTGVpc2htYW5pYVxcLnBhbmFtZW5zaXMuKiIpCnBrZ19uYW1lIDwtIGFsbF9pbnN0YWxsZWRbcGtnX2NhbmRpZGF0ZXNdWzFdCmxpYnJhcnkoYXMuY2hhcmFjdGVyKHBrZ19uYW1lKSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQpscF9nZW5vbWUgPC0gZ2V0MChhcy5jaGFyYWN0ZXIocGtnX25hbWUpKQpkYXRhX3N0cnVjdHVyZXMgPC0gYyhkYXRhX3N0cnVjdHVyZXMsICJscF9nZW5vbWUiKQpgYGAKCiMgR2VuZXJhdGUgRXhwcmVzc2lvbnNldHMgYW5kIFNhbXBsZSBFc3RpbWF0aW9uCgpUaGUgcHJvY2VzcyBvZiBzYW1wbGUgZXN0aW1hdGlvbiB0YWtlcyB0d28gcHJpbWFyeSBpbnB1dHM6CgoxLiAgVGhlIHNhbXBsZSBzaGVldCwgd2hpY2ggY29udGFpbnMgYWxsIHRoZSBtZXRhZGF0YSB3ZSBjdXJyZW50bHkgaGF2ZSBvbiBoYW5kLAogICAgaW5jbHVkaW5nIGZpbGVuYW1lcyBmb3IgdGhlIG91dHB1dHMgb2YgIzMgYW5kICM0IGFib3ZlLgoyLiAgVGhlIGdlbmUgYW5ub3RhdGlvbnMuCgpBbiBleHByZXNzaW9uU2V0KG9yIHN1bW1hcml6ZWRFeHBlcmltZW50KSBpcyBhIGRhdGEgc3RydWN0dXJlIHVzZWQgaW4KUiB0byBleGFtaW5lIFJOQVNlcSBkYXRhLiAgSXQgaXMgY29tcHJpc2VkIG9mIGFubm90YXRpb25zLCBtZXRhZGF0YSwKYW5kIGV4cHJlc3Npb24gZGF0YS4gIEluIHRoZSBjYXNlIG9mIG91ciBwcm9jZXNzaW5nIHBpcGVsaW5lLCB0aGUKbG9jYXRpb24gb2YgdGhlIGV4cHJlc3Npb24gZGF0YSBpcyBwcm92aWRlZCBieSB0aGUgZmlsZW5hbWVzIGluIHRoZSBtZXRhZGF0YS4KCiMjIE5vdGVzCgpUaGUgZm9sbG93aW5nIHNhbXBsZXMgYXJlIG11Y2ggbG93ZXIgY292ZXJhZ2U6CgoqIFRNUkMyMDAwMgoqIFRNUkMyMDAwNgoqIFRNUkMyMDAwNwoqIFRNUkMyMDAwOAoKVGhlcmUgaXMgYSBzZXQgb2Ygc3RyYWlucyB3aGljaCBhY3F1aXJlZCByZXNpc3RhbmNlIGluIHZpdHJvLiAgVGhlc2UKYXJlIGluY2x1ZGVkIGluIHRoZSBkYXRhc2V0LCBidXQgdGhlcmUgYXJlIG5vdCBsaWtlbHkgZW5vdWdoIG9mIHRoZW0KdG8gcXVlcnkgdGhhdCBxdWVzdGlvbiBleHBsaWNpdGx5LgoKIyMgRGVmaW5lIGNvbG9ycwoKVGhlIGZvbGxvd2luZyBsaXN0IGNvbnRhaW5zIHRoZSBjb2xvcnMgd2UgaGF2ZSBjaG9zZW4gdG8gdXNlIHdoZW4KcGxvdHRpbmcgdGhlIHZhcmlvdXMgd2F5cyBvZiBkaXNjZXJuaW5nIHRoZSBkYXRhLgoKYGBge3J9CmNvbG9yX2Nob2ljZXMgPC0gbGlzdCgKICAgICJzdHJhaW4iID0gbGlzdCgKICAgICAgICAjIyAiejEuMCIgPSAiIzMzMzMzMyIsICMjIENoYW5nZWQgdGhpcyB0byAnYnJheicgdG8gbWFrZSBpdCBlYXNpZXIgdG8gZmluZCB0aGVtLgogICAgICAgICJ6Mi4wIiA9ICIjNTU1NTU1IiwKICAgICAgICAiejMuMCIgPSAiIzc3Nzc3NyIsCiAgICAgICAgInoyLjEiID0gIiM4NzQ0MDAiLAogICAgICAgICJ6Mi4yIiA9ICIjMDAwMGNjIiwKICAgICAgICAiejIuMyIgPSAiI2NjMDAwMCIsCiAgICAgICAgInoyLjQiID0gIiNkZjcwMDAiLAogICAgICAgICJ6My4yIiA9ICIjODg4ODg4IiwKICAgICAgICAiejEuMCIgPSAiI2NjMDBjYyIsCiAgICAgICAgInoxLjUiID0gIiNjYzAwY2MiLAogICAgICAgICJiMjkwNCIgPSAiI2NjMDBjYyIsCiAgICAgICAgInVua25vd24iID0gIiNjYmNiY2IiKSwKICAgICMjICJudWxsIiA9ICIjMDAwMDAwIiksCiAgICAienltbyIgPSBsaXN0KAogICAgICAiejIyIiA9ICIjMDAwMGNjIiwKICAgICAgInoyMyIgPSAiI2NjMDAwMCIpLAogICAgImNmIiA9IGxpc3QoCiAgICAgICAgImN1cmUiID0gIiMwMDZmMDAiLAogICAgICAgICJmYWlsIiA9ICIjOWRmZmEwIiwKICAgICAgICAidW5rbm93biIgPSAiI2NiY2JjYiIsCiAgICAgICAgIm5vdGFwcGxpY2FibGUiID0gIiMwMDAwMDAiKSwKICAgICJzdXNjZXB0aWJpbGl0eSIgPSBsaXN0KAogICAgICAgICJyZXNpc3RhbnQiID0gIiM4NTYzYTciLAogICAgICAgICJzZW5zaXRpdmUiID0gIiM4ZDAwMDAiLAogICAgICAgICJhbWJpZ3VvdXMiID0gIiNjYmNiY2IiLAogICAgICAgICJ1bmtub3duIiA9ICIjNTU1NTU1IikpCmRhdGFfc3RydWN0dXJlcyA8LSBjKGRhdGFfc3RydWN0dXJlcywgImNvbG9yX2Nob2ljZXMiKQpgYGAKCiMgUGFyYXNpdGUtb25seSBkYXRhIHN0cnVjdHVyZQoKVGhlIGRhdGEgc3RydWN0dXJlICdscF9leHB0JyBjb250YWlucyB0aGUgZGF0YSBmb3IgYWxsIHNhbXBsZXMgd2hpY2gKaGF2ZSBoaXNhdDIgY291bnQgdGFibGVzLCBhbmQgd2hpY2ggcGFzcyBhIGZldyBpbml0aWFsIHF1YWxpdHkgdGVzdHMKKGUuZy4gdGhleSBtdXN0IGhhdmUgbW9yZSB0aGFuIDg1NTAgZ2VuZXMgd2l0aCA+MCBjb3VudHMgYW5kID41ZTYKcmVhZHMgd2hpY2ggbWFwcGVkIHRvIGEgZ2VuZSk7IGdlbmVzIHdoaWNoIGFyZSBhbm5vdGF0ZWQgd2l0aCBhIGZldwprZXkgcmVkdW5kYW50IGNhdGVnb3JpZXMgKGxlaXNobWFub2x5c2luIGZvciBleGFtcGxlKSBhcmUgYWxzbyBjdWxsZWQuCgojIyBBbGwgKGFsbW9zdCkgc2FtcGxlcwoKVGhlcmUgYXJlIGEgZmV3IG1ldGFkYXRhIGNvbHVtbnMgd2hpY2ggd2UgcmVhbGx5IHdhbnQgdG8gbWFrZSBjZXJ0YWluCmFyZSBzdGFuZGFyZGl6ZWQuCgpOb3RlOiBJIGNoYW5nZWQgdGhpcyB0byBwcmludCBib3RoIHRoZSBudW1iZXIgb2YgcmVhZHMgYW5kIGdlbmVzIGZvciByZW1vdmVkIHNhbXBsZXMuCgpgYGB7cn0Kc2FuaXRpemVfY29sdW1ucyA8LSBjKCJwYXNzYWdlbnVtYmVyIiwgImNsaW5pY2FscmVzcG9uc2UiLCAiY2xpbmljYWxjYXRlZ29yaWNhbCIsCiAgICAgICAgICAgICAgICAgICAgICAienltb2RlbWVjYXRlZ29yaWNhbCIsICJpbmNsdWRlZCIpCmxwX2V4cHQgPC0gY3JlYXRlX2V4cHQoc2FtcGxlX3NoZWV0LAogICAgICAgICAgICAgICAgICAgICAgIGdlbmVfaW5mbyA9IGFsbF9scF9hbm5vdCwKICAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX25hbWUgPSBvcmdkYl9wa2dfbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICBzYXZlZmlsZSA9IGdsdWUoInJkYS90bXJjMl9scF9leHB0X2FsbF9yYXctdnt2ZXJ9LnJkYSIpLAogICAgICAgICAgICAgICAgICAgICAgIGlkX2NvbHVtbiA9ICJocGdsaWRlbnRpZmllciIsCiAgICAgICAgICAgICAgICAgICAgICAgZmlsZV9jb2x1bW4gPSAibHBhbmFtZW5zaXN2MzZoaXNhdGZpbGUiKSAlPiUKICBzZXRfZXhwdF9jb25kaXRpb25zKGZhY3QgPSAienltb2RlbWVjYXRlZ29yaWNhbCIsIGNvbG9ycyA9IGNvbG9yX2Nob2ljZXNbWyJzdHJhaW4iXV0pICU+JQogIHNlbWFudGljX2V4cHRfZmlsdGVyKHNlbWFudGljID0gYygiYW1hc3RpbiIsICJncDYzIiwgImxlaXNobWFub2x5c2luIiksCiAgICAgICAgICAgICAgICAgICAgICAgc2VtYW50aWNfY29sdW1uID0gImFubm90X2dlbmVfcHJvZHVjdCIpICU+JQogIHNhbml0aXplX2V4cHRfcERhdGEoY29sdW1ucyA9IHNhbml0aXplX2NvbHVtbnMpICU+JQogIHN1YnNldF9leHB0KHN1YnNldCA9ICJpbmNsdWRlZD09J3llcyciKSAlPiUKICBzZXRfZXhwdF9mYWN0b3JzKGNvbHVtbnMgPSBzYW5pdGl6ZV9jb2x1bW5zLCBjbGFzcyA9ICJmYWN0b3IiKQpkYXRhX3N0cnVjdHVyZXMgPC0gYyhkYXRhX3N0cnVjdHVyZXMsICJscF9leHB0IikKc2F2ZShsaXN0ID0gImxwX2V4cHQiLCBmaWxlID0gZ2x1ZSgicmRhL3RtcmMyX2xwX2V4cHRfYWxsX3Nhbml0aXplZC12e3Zlcn0ucmRhIikpCgp0YWJsZShwRGF0YShscF9leHB0KVtbInp5bW9kZW1lY2F0ZWdvcmljYWwiXV0pCnRhYmxlKHBEYXRhKGxwX2V4cHQpW1siY2xpbmljYWxyZXNwb25zZSJdXSkKdGFibGUocERhdGEobHBfZXhwdClbWyJjbGluaWNhbGNhdGVnb3JpY2FsIl1dKQpuY29sKGV4cHJzKGxwX2V4cHQpKQpgYGAKCiMjIFByaW50IHNhbXBsZSBJRHMgYnkgc3RhdHVzCgojIyMgQ3VyZQoKYGBge3J9CmN1cmVfaWRzIDwtIHBEYXRhKGxwX2V4cHQpW1siY2xpbmljYWxjYXRlZ29yaWNhbCJdXSA9PSAiY3VyZSIKcm93bmFtZXMocERhdGEobHBfZXhwdCkpW2N1cmVfaWRzXQpgYGAKCiMjIyBGYWlsCgpgYGB7cn0KZmFpbF9pZHMgPC0gcERhdGEobHBfZXhwdClbWyJjbGluaWNhbGNhdGVnb3JpY2FsIl1dID09ICJmYWlsIgpyb3duYW1lcyhwRGF0YShscF9leHB0KSlbZmFpbF9pZHNdCmBgYAoKIyMjIFVua25vd24KCmBgYHtyfQp1bmtub3duX2lkcyA8LSBwRGF0YShscF9leHB0KVtbImNsaW5pY2FsY2F0ZWdvcmljYWwiXV0gPT0gInVua25vd24iCnJvd25hbWVzKHBEYXRhKGxwX2V4cHQpKVt1bmtub3duX2lkc10KYGBgCgojIyMgU3VzY2VwdGlibGUgYW5kIHN0cmFpbgoKYGBge3J9CmFsbF9zZW5zaXRpdmVfaWRzIDwtIHBEYXRhKGxwX2V4cHQpW1sicXVhbGl0YXRpdmVjbGFzc2lmaWNhdGlvbm9mZHJ1Z3N1c2NlcHRpYmlsaXR5Il1dID09ICJTZW5zaXRpdmUiCnNlbnNpdGl2ZV9pZHMgPC0gcm93bmFtZXMocERhdGEobHBfZXhwdCkpW2FsbF9zZW5zaXRpdmVfaWRzXQoKYWxsX3Jlc2lzdGFudF9pZHMgPC0gcERhdGEobHBfZXhwdClbWyJxdWFsaXRhdGl2ZWNsYXNzaWZpY2F0aW9ub2ZkcnVnc3VzY2VwdGliaWxpdHkiXV0gPT0gIlJlc2lzdGFudCIKcmVzaXN0YW50X2lkcyA8LSByb3duYW1lcyhwRGF0YShscF9leHB0KSlbYWxsX3Jlc2lzdGFudF9pZHNdCgphbGxfejIzIDwtIHBEYXRhKGxwX2V4cHQpW1sienltb2RlbWVjYXRlZ29yaWNhbCJdXSA9PSAiejIzIgp6MjNfaWRzIDwtIHJvd25hbWVzKHBEYXRhKGxwX2V4cHQpKVthbGxfejIzXQoKYWxsX3oyMiA8LSBwRGF0YShscF9leHB0KVtbInp5bW9kZW1lY2F0ZWdvcmljYWwiXV0gPT0gInoyMiIKejIyX2lkcyA8LSByb3duYW1lcyhwRGF0YShscF9leHB0KSlbYWxsX3oyMl0KCmJvdGhfaWRzIDwtIHoyMl9pZHMgJWluJSByZXNpc3RhbnRfaWRzCnN1bW1hcnkoYm90aF9pZHMpCnJvd25hbWVzKHBEYXRhKGxwX2V4cHQpKVtbYm90aF9pZHNdXQoKYm90aF9pZHMgPC0gejIzX2lkcyAlaW4lIHNlbnNpdGl2ZV9pZHMKc3VtbWFyeShib3RoX2lkcykKYGBgCgpBbGwgdGhlIGZvbGxvd2luZyBkYXRhIHdpbGwgZGVyaXZlIGZyb20gdGhpcyBzdGFydGluZyBwb2ludC4KCiMjIEV4dHJhY3Qgc2FtcGxlcyBmcm9tIG9ubHkgdGhlIHR3byAnY2Fub25pY2FsJyBzdHJhaW5zCgojIyMgUXVpY2sgZGl2ZXJnZW5jZQoKSGVyZSBpcyBhIHRhYmxlIG9mIG15IGN1cnJlbnQgY2xhc3NpZmllcidzIGludGVycHJldGF0aW9uIG9mIHRoZSBzdHJhaW5zLgoKYGBge3J9CnRhYmxlKHBEYXRhKGxwX2V4cHQpW1sia25udjJjbGFzc2lmaWNhdGlvbiJdXSkKYGBgCgojIyMgTWVyZ2UgMi4xLzIuMiBhbmQgMi40LzIuMwoKYGBge3J9Cm1lcmdlZF96eW1vIDwtIGxwX2V4cHQKcERhdGEobWVyZ2VkX3p5bW8pW1sienltb2RlbWUiXV0gPC0gYXMuY2hhcmFjdGVyKHBEYXRhKG1lcmdlZF96eW1vKVtbInp5bW9kZW1lY2F0ZWdvcmljYWwiXV0pCnoyMV9pZHggPC0gcERhdGEobWVyZ2VkX3p5bW8pW1sienltb2RlbWUiXV0gPT0gInoyMSIKcERhdGEobWVyZ2VkX3p5bW8pW3oyMV9pZHgsICJ6eW1vZGVtZSJdIDwtICJ6MjIiCgp6MjRfaWR4IDwtIHBEYXRhKG1lcmdlZF96eW1vKVtbInp5bW9kZW1lIl1dID09ICJ6MjQiCnBEYXRhKG1lcmdlZF96eW1vKVt6MjRfaWR4LCAienltb2RlbWUiXSA8LSAiejIzIgoKa2VlcGVycyA8LSBwRGF0YShtZXJnZWRfenltbylbWyJ6eW1vZGVtZSJdXSA9PSAiejIyIiB8CiAgcERhdGEobWVyZ2VkX3p5bW8pW1sienltb2RlbWUiXV0gPT0gInoyMyIKbWVyZ2VkX3p5bW8gPC0gbWVyZ2VkX3p5bW9bLCBrZWVwZXJzXSAlPiUKICBzZXRfZXhwdF9jb25kaXRpb25zKGZhY3QgPSAienltb2RlbWUiLCBjb2xvcnMgPSBjb2xvcl9jaG9pY2VzW1sienltbyJdXSkKYGBgCgojIEFkZCBsaWJyYXJ5IHNpemVzIGJlZm9yZSBmaWx0ZXJpbmcKCmBgYHtyfQp0YWJsZShwRGF0YShscF9leHB0KVtbImNsaW5pY2FsY2F0ZWdvcmljYWwiXV0pCnVua25vd25faWRzIDwtIHBEYXRhKGxwX2V4cHQpW1siY2xpbmljYWxjYXRlZ29yaWNhbCJdXSA9PSAidW5rbm93biIKcm93bmFtZXMocERhdGEobHBfZXhwdCkpW3Vua25vd25faWRzXQpmYWlsZWRfaWRzIDwtIHBEYXRhKGxwX2V4cHQpW1siY2xpbmljYWxjYXRlZ29yaWNhbCJdXSA9PSAiZmFpbCIKcm93bmFtZXMocERhdGEobHBfZXhwdCkpW2ZhaWxlZF9pZHNdCgpwcmVfbGlic2l6ZSA8LSBwbG90X2xpYnNpemUobHBfZXhwdCkKcHJlX2xpYnNpemUKCnBkZihmaWxlID0gImZpZ3VyZXMvbGlicmFyeV9zaXplX3ByZV9maWx0ZXIucGRmIiwgd2lkdGggPSAyNCwgaGVpZ2h0ID0gMTIpCnByZV9saWJzaXplJHBsb3QKZGV2Lm9mZigpCgpwcmVfbm9uemVybyA8LSBwbG90X25vbnplcm8obHBfZXhwdCkKcHJlX25vbnplcm8KcGRmKGZpbGUgPSAiZmlndXJlcy9ub256ZXJvX3ByZV9maWx0ZXIucGRmIikKcHJlX25vbnplcm8kcGxvdApkZXYub2ZmKCkKCmxwX2V4cHRfcHJlIDwtIGxwX2V4cHQKbHBfZXhwdCA8LSBzdWJzZXRfZXhwdChscF9leHB0LCBub256ZXJvID0gODU1MCkKCnBvc3Rfbm9uemVybyA8LSBwbG90X25vbnplcm8obHBfZXhwdCkKcG9zdF9ub256ZXJvCmBgYAoKIyMgRXh0cmFjdCBoaXN0b3JpY2FsIHN1c2NlcHRpYmlsaXR5IGRhdGEKCkNvbHVtbiAnUScgaW4gdGhlIHNhbXBsZSBzaGVldCwgbWFrZSBhIGNhdGVnb3JpY2FsIHZlcnNpb24gb2YgaXQgd2l0aCB0aGVzZSBwYXJhbWV0ZXJzOgoKKiAwIDw9IHggPD0gMzUgaXMgcmVzaXN0YW50CiogMzYgPD0geCA8PSA0OCBpcyBhbWJpZ3VvdXMKKiA0OSA8PSB4IGlzIHNlbnNpdGl2ZQoKTm90ZSB0aGF0IHRoZXNlIGN1dG9mZnMgYXJlIG9ubHkgdmFsaWQgZm9yIHRoZSBoaXN0b3JpY2FsIGRhdGEuICBUaGUKbmV3ZXIgc3VzY2VwdGliaWxpdHkgZGF0YSB1c2VzIGEgY3V0b2ZmIG9mIDAuNzggZm9yIHNlbnNpdGl2ZS4gIEkgd2lsbApzZXQgYW1iaWd1b3VzIHRvIDAuNSB0byAwLjc4PwoKYGBge3J9Cm1heF9yZXNpc3RfaGlzdG9yaWNhbCA8LSAwLjM1Cm1pbl9zZW5zaXRpdmVfaGlzdG9yaWNhbCA8LSAwLjQ5CgojIyAyMDIzMDU6IFJlbW92ZWQgYW1iaWd1b3VzIGNhdGVnb3J5IGZvciB0aGUgY3VycmVudCBzZXQuCm1heF9yZXNpc3RfY3VycmVudCA8LSAwLjc3Cm1pbl9zZW5zaXRpdmVfY3VycmVudCA8LSAwLjc3CmBgYAoKVGhlIHNhbml0aXplX3BlcmNlbnQoKSBmdW5jdGlvbiBzZWVrcyB0byBtYWtlIHRoZSBwZXJjZW50YWdlIHZhbHVlcwpyZWNvcmRlZCBieSBleGNlbCBtb3JlIHJlbGlhYmxlLiAgVW5mb3J0dW5hdGVseSwgc29tZXRpbWVzIGV4Y2VsCmRpc3BsYXlzIHRoZSB2YWx1ZSAnNDklJyB3aGVuIHRoZSBpbmZvcm1hdGlvbiByZWNvcmRlZCBpbiB0aGUKd29ya3NoZWV0IGlzIGFueSBvbmUgb2YgdGhlIGZvbGxvd2luZzoKCiogJzQ5JQoqIDAuNDkKKiAiMC40OSIKClRodXMsIHRoZSBmb2xsb3dpbmcgYmxvY2sgd2lsbCBzYW5pdGl6ZSB0aGVzZSBwZXJjZW50YWdlIHZhbHVlcyBpbnRvIGEKc2luZ2xlIGRlY2ltYWwgbnVtYmVyIGFuZCBtYWtlIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgZnJvbSBpdCB1c2luZwpwcmUtZGVmaW5lZCB2YWx1ZXMgZm9yIHJlc2lzdGFudC9hbWJpZ3VvdXMvc2Vuc2l0aXZlLiAgVGhpcwpjYXRlZ29yaWNhbCB2YXJpYWJsZSB3aWxsIGJlIHN0b3JlZCBpbiBhIG5ldyBjb2x1bW46ICdzdXNfY2F0ZWdvcnlfaGlzdG9yaWNhbCcuCgpgYGB7cn0Kc3QgPC0gcERhdGEobHBfZXhwdClbWyJzdXNjZXB0aWJpbGl0eWluZmVjdGlvbnJlZHVjdGlvbjMydWdtbHNidmhpc3RvcmljYWxkYXRhIl1dCnN0YXJ0aW5nIDwtIHNhbml0aXplX3BlcmNlbnQoc3QpCnN0CnN0YXJ0aW5nCnN1c19jYXRlZ29yaWNhbCA8LSBzdGFydGluZwpuYV9pZHggPC0gaXMubmEoc3RhcnRpbmcpCnN1bShuYV9pZHgpCnN1c19jYXRlZ29yaWNhbFtuYV9pZHhdIDwtICJ1bmtub3duIgoKcmVzaXN0X2lkeCA8LSBzdGFydGluZyA8PSBtYXhfcmVzaXN0X2hpc3RvcmljYWwKc3VzX2NhdGVnb3JpY2FsW3Jlc2lzdF9pZHhdIDwtICJyZXNpc3RhbnQiCmluZGV0ZXJtaW5hbnRfaWR4IDwtIHN0YXJ0aW5nID4gbWF4X3Jlc2lzdF9oaXN0b3JpY2FsICYKICBzdGFydGluZyA8IG1pbl9zZW5zaXRpdmVfaGlzdG9yaWNhbApzdXNfY2F0ZWdvcmljYWxbaW5kZXRlcm1pbmFudF9pZHhdIDwtICJhbWJpZ3VvdXMiCnN1c2NlcHRpYmxlX2lkeCA8LSBzdGFydGluZyA+PSBtaW5fc2Vuc2l0aXZlX2hpc3RvcmljYWwKc3VzX2NhdGVnb3JpY2FsW3N1c2NlcHRpYmxlX2lkeF0gPC0gInNlbnNpdGl2ZSIKCnN1c19jYXRlZ29yaWNhbCA8LSBhcy5mYWN0b3Ioc3VzX2NhdGVnb3JpY2FsKQpwRGF0YShscF9leHB0KVtbInN1c19jYXRlZ29yeV9oaXN0b3JpY2FsIl1dIDwtIHN1c19jYXRlZ29yaWNhbAp0YWJsZShzdXNfY2F0ZWdvcmljYWwpCgp0d29fc2Fua2V5IDwtIHBsb3RfbWV0YV9zYW5rZXkoCiAgbWVyZ2VkX3p5bW8sIGZhY3RvcnMgPSBjKCJ6eW1vZGVtZSIsICJjbGluaWNhbGNhdGVnb3JpY2FsIiwgInN1c2NlcHRpYmlsaXR5IiksCiAgZHJpbGxfZG93biA9IFRSVUUsIGNvbG9yX2Nob2ljZXMgPSBjb2xvcl9jaG9pY2VzKQp0d29fc2Fua2V5CmBgYAoKIyMgRXh0cmFjdCBjdXJyZW50IHN1c2NlcHRpYmlsaXR5IGRhdGEKClRoZSBzYW1lIHByb2Nlc3Mgd2lsbCBiZSByZXBlYXRlZCBmb3IgdGhlIGN1cnJlbnQgaXRlcmF0aW9uIG9mIHRoZQpzZW5zaXRpdml0eSBhc3NheSBhbmQgc3RvcmVkIGluIHRoZSAnc3VzX2NhdGVnb3J5X2N1cnJlbnQnIGNvbHVtbi4KCmBgYHtyfQpzdGFydGluZ19jdXJyZW50IDwtIHNhbml0aXplX3BlcmNlbnQocERhdGEobHBfZXhwdClbWyJzdXNjZXB0aWJpbGl0eWluZmVjdGlvbnJlZHVjdGlvbjMydWdtbHNidmN1cnJlbnRkYXRhIl1dKQpzdXNfY2F0ZWdvcmljYWxfY3VycmVudCA8LSBzdGFydGluZ19jdXJyZW50Cm5hX2lkeCA8LSBpcy5uYShzdGFydGluZ19jdXJyZW50KQpzdW0obmFfaWR4KQpzdXNfY2F0ZWdvcmljYWxfY3VycmVudFtuYV9pZHhdIDwtICJ1bmtub3duIgoKIyMgVGhlIGZvbGxvd2luZyBpcyBvbmx5IHZhbGlkIHdoZW4gd2UgaGFkIHRocmVlIGNhdGVnb3JpZXMsIHJlc2lzdGFudC9hbWJpZ3VvdXMvc2Vuc2l0aXZlCiMjIFRoZSBuZXcgY3V0b2ZmcyBkcm9wIGFtYmlndW91cy4KI3Jlc2lzdF9pZHggPC0gc3RhcnRpbmdfY3VycmVudCA8PSBtYXhfcmVzaXN0X2N1cnJlbnQKI3N1c19jYXRlZ29yaWNhbF9jdXJyZW50W3Jlc2lzdF9pZHhdIDwtICJyZXNpc3RhbnQiCiNpbmRldGVybWluYW50X2lkeCA8LSBzdGFydGluZ19jdXJyZW50ID4gbWF4X3Jlc2lzdF9jdXJyZW50ICYKIyAgc3RhcnRpbmdfY3VycmVudCA8IG1pbl9zZW5zaXRpdmVfY3VycmVudAojc3VzX2NhdGVnb3JpY2FsX2N1cnJlbnRbaW5kZXRlcm1pbmFudF9pZHhdIDwtICJhbWJpZ3VvdXMiCiNzdXNjZXB0aWJsZV9pZHggPC0gc3RhcnRpbmdfY3VycmVudCA+PSBtaW5fc2Vuc2l0aXZlX2N1cnJlbnQKI3N1c19jYXRlZ29yaWNhbF9jdXJyZW50W3N1c2NlcHRpYmxlX2lkeF0gPC0gInNlbnNpdGl2ZSIKI3N1c19jYXRlZ29yaWNhbF9jdXJyZW50IDwtIGFzLmZhY3RvcihzdXNfY2F0ZWdvcmljYWxfY3VycmVudCkKcmVzaXN0X2lkeCA8LSBzdGFydGluZ19jdXJyZW50IDw9IG1heF9yZXNpc3RfY3VycmVudApzZW5zaXRpdmVfaWR4IDwtICFyZXNpc3RfaWR4CnN1c19jYXRlZ29yaWNhbF9jdXJyZW50W3Jlc2lzdF9pZHhdIDwtICJyZXNpc3RhbnQiCnN1c19jYXRlZ29yaWNhbF9jdXJyZW50W3NlbnNpdGl2ZV9pZHhdIDwtICJzZW5zaXRpdmUiCnN1c19jYXRlZ29yaWNhbF9jdXJyZW50IDwtIGFzLmZhY3RvcihzdXNfY2F0ZWdvcmljYWxfY3VycmVudCkKCnBEYXRhKGxwX2V4cHQpW1sic3VzX2NhdGVnb3J5X2N1cnJlbnQiXV0gPC0gc3VzX2NhdGVnb3JpY2FsX2N1cnJlbnQKcERhdGEobHBfZXhwdClbWyJzdXNjZXB0aWJpbGl0eSJdXSA8LSBzdXNfY2F0ZWdvcmljYWxfY3VycmVudAp0YWJsZShzdXNfY2F0ZWdvcmljYWxfY3VycmVudCkKCmxwX3NhbmtleSA8LSBwbG90X21ldGFfc2Fua2V5KAogIGxwX2V4cHQsIGZhY3RvcnMgPSBjKCJ6eW1vZGVtZWNhdGVnb3JpY2FsIiwgImNsaW5pY2FsY2F0ZWdvcmljYWwiLCAic3VzY2VwdGliaWxpdHkiKSwKICBkcmlsbF9kb3duID0gVFJVRSwgY29sb3JfY2hvaWNlcyA9IGNvbG9yX2Nob2ljZXMpCmxwX3NhbmtleQpgYGAKCkluIG1hbnkgcXVlcmllcywgd2Ugd2lsbCBzZWVrIHRvIGNvbXBhcmUgb25seSB0aGUgdHdvIHByaW1hcnkgc3RyYWlucywKenltb2RlbWUgMi4yIGFuZCAyLjMuICBUaGUgZm9sbG93aW5nIGJsb2NrIHdpbGwgZXh0cmFjdCBvbmx5IHRob3NlCnNhbXBsZXMuCgpOb3RlOiAqSU1QT1JUQU5UKiBNYXJpYSBBZGVsYWlkYSBwcmVmZXJzIG5vdCB0byB1c2UgbHBfdHdvX3N0cmFpbnMuICBXZSBzaG91bGQgbm90IGF0IHRoaXMgdGltZQp1c2UgdGhlIG1lcmdlZCAyLjEvMi4yIGFuZCAyLjQvMi4zIGNhdGVnb3JpZXMuCgpgYGB7cn0KbHBfc3RyYWluIDwtIGxwX2V4cHQgJT4lCiAgc2V0X2V4cHRfYmF0Y2hlcyhmYWN0ID0gc3VzX2NhdGVnb3JpY2FsX2N1cnJlbnQpICU+JQogIHNldF9leHB0X2NvbG9ycyhjb2xvcl9jaG9pY2VzW1sic3RyYWluIl1dKQp0YWJsZShwRGF0YShscF9zdHJhaW4pW1siY29uZGl0aW9uIl1dKQpzYXZlKGxpc3QgPSAibHBfc3RyYWluIiwgZmlsZSA9IGdsdWUoInJkYS90bXJjMl9scF9zdHJhaW4tdnt2ZXJ9LnJkYSIpKQpkYXRhX3N0cnVjdHVyZXMgPC0gYyhkYXRhX3N0cnVjdHVyZXMsICJscF9zdHJhaW4iKQoKbHBfdHdvX3N0cmFpbnMgPC0gbWVyZ2VkX3p5bW8Kc2F2ZShsaXN0ID0gImxwX3R3b19zdHJhaW5zIiwKICAgICBmaWxlID0gZ2x1ZSgicmRhL3RtcmMyX2xwX3R3b19zdHJhaW5zLXZ7dmVyfS5yZGEiKSkKZGF0YV9zdHJ1Y3R1cmVzIDwtIGMoZGF0YV9zdHJ1Y3R1cmVzLCAibHBfdHdvX3N0cmFpbnMiKQpgYGAKCiMjIENsaW5pY2FsIG91dGNvbWUKCkNsaW5pY2FsIG91dGNvbWUgaXMgYnkgZmFyIHRoZSBtb3N0IHByb2JsZW1hdGljIGNvbXBhcmlzb24gaW4gdGhpcwpkYXRhLCBidXQgaGVyZSBpcyB0aGUgcmVjYXRlZ29yaXphdGlvbiBvZiB0aGUgZGF0YSB1c2luZyBpdDoKCmBgYHtyfQpscF9jZiA8LSBzZXRfZXhwdF9jb25kaXRpb25zKGxwX2V4cHQsIGZhY3QgPSAiY2xpbmljYWxjYXRlZ29yaWNhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gY29sb3JfY2hvaWNlc1tbImNmIl1dKSAlPiUKICBzZXRfZXhwdF9iYXRjaGVzKGZhY3QgPSBzdXNfY2F0ZWdvcmljYWxfY3VycmVudCkKdGFibGUocERhdGEobHBfY2YpW1siY29uZGl0aW9uIl1dKQpkYXRhX3N0cnVjdHVyZXMgPC0gYyhkYXRhX3N0cnVjdHVyZXMsICJscF9jZiIpCnNhdmUobGlzdCA9ICJscF9jZiIsCiAgICAgZmlsZSA9IGdsdWUoInJkYS90bXJjMl9scF9jZi12e3Zlcn0ucmRhIikpCgpscF9jZl9rbm93biA8LSBzdWJzZXRfZXhwdChscF9jZiwgc3Vic2V0ID0gImNvbmRpdGlvbiE9J3Vua25vd24nIikKZGF0YV9zdHJ1Y3R1cmVzIDwtIGMoZGF0YV9zdHJ1Y3R1cmVzLCAibHBfY2Zfa25vd24iKQpzYXZlKGxpc3QgPSAibHBfY2Zfa25vd24iLAogICAgIGZpbGUgPSBnbHVlKCJyZGEvdG1yYzJfbHBfY2Zfa25vd24tdnt2ZXJ9LnJkYSIpKQpkYXRhX3N0cnVjdHVyZXMgPC0gYyhkYXRhX3N0cnVjdHVyZXMsICJscF9jZl9rbm93biIpCnNhdmUobGlzdCA9ICJscF9jZl9rbm93biIsCiAgICAgZmlsZSA9IGdsdWUoInJkYS90bXJjMl9scF9jZl9rbm93bi12e3Zlcn0ucmRhIikpCmBgYAoKIyMgQ3JlYXRlIGEgaGlzdG9yaWNhbCBzdXNjZXB0aWJpbGl0eSBkYXRhc2V0CgpVc2UgdGhlIGZhY3Rvcml6ZWQgdmVyc2lvbiBvZiBzdXNjZXB0aWJpbGl0eSB0byBjYXRlZ29yaXplIHRoZSBzYW1wbGVzCmJ5IHRoZSBoaXN0b3JpY2FsIGRhdGEuCgpgYGB7cn0KbHBfc3VzY2VwdGliaWxpdHlfaGlzdG9yaWNhbCA8LSBzZXRfZXhwdF9jb25kaXRpb25zKAogIGxwX2V4cHQsIGZhY3QgPSAic3VzX2NhdGVnb3J5X2hpc3RvcmljYWwiLCBjb2xvcnMgPSBjb2xvcl9jaG9pY2VzW1sic3VzY2VwdGliaWxpdHkiXV0pICU+JQogIHNldF9leHB0X2JhdGNoZXMoZmFjdCA9ICJjbGluaWNhbGNhdGVnb3JpY2FsIikKc2F2ZShsaXN0ID0gImxwX3N1c2NlcHRpYmlsaXR5X2hpc3RvcmljYWwiLAogICAgIGZpbGUgPSBnbHVlKCJyZGEvdG1yYzJfbHBfc3VzY2VwdGliaWxpdHlfaGlzdG9yaWNhbC12e3Zlcn0ucmRhIikpCmRhdGFfc3RydWN0dXJlcyA8LSBjKGRhdGFfc3RydWN0dXJlcywgImxwX3N1c2NlcHRpYmlsaXR5X2hpc3RvcmljYWwiKQpgYGAKCiMjIENyZWF0ZSBhIGN1cnJlbnQgc3VzY2VwdGliaWxpdHkgZGF0YXNldAoKVXNlIHRoZSBmYWN0b3JpemVkIHZlcnNpb24gb2Ygc3VzY2VwdGliaWxpdHkgdG8gY2F0ZWdvcml6ZSB0aGUgc2FtcGxlcwpieSB0aGUgaGlzdG9yaWNhbCBkYXRhLgoKVGhpcyB3aWxsIGxpa2VseSBiZSBvdXIgY2Fub25pY2FsIHN1c2NlcHRpYmlsaXR5IGRhdGFzZXQsIHNvIEkgd2lsbApyZW1vdmUgdGhlIHN1ZmZpeCBhbmQganVzdCBjYWxsIGl0ICdscF9zdXNjZXB0aWJpbGl0eScuCgpgYGB7cn0KbHBfc3VzY2VwdGliaWxpdHkgPC0gc2V0X2V4cHRfY29uZGl0aW9ucygKICBscF9leHB0LCBmYWN0ID0gInN1c19jYXRlZ29yeV9jdXJyZW50IiwgY29sb3JzID0gY29sb3JfY2hvaWNlc1tbInN1c2NlcHRpYmlsaXR5Il1dKSAlPiUKICBzZXRfZXhwdF9iYXRjaGVzKGZhY3QgPSAiY2xpbmljYWxjYXRlZ29yaWNhbCIpCnNhdmUobGlzdCA9ICJscF9zdXNjZXB0aWJpbGl0eSIsCiAgICAgZmlsZSA9IGdsdWUoInJkYS90bXJjMl9scF9zdXNjZXB0aWJpbGl0eS12e3Zlcn0ucmRhIikpCmRhdGFfc3RydWN0dXJlcyA8LSBjKGRhdGFfc3RydWN0dXJlcywgImxwX3N1c2NlcHRpYmlsaXR5IikKYGBgCgojIyBQdWxsIG91dCBvbmx5IHRoZSBzYW1wbGVzIHdpdGggdHdvIHp5bW9kZW1lcwoKSSB0aGluayB0aGlzIGlzIHJlZHVuZGFudCB3aXRoIGEgcHJldmlvdXMgYmxvY2ssIGJ1dCBJIGFtIGxlYXZpbmcgaXQKdW50aWwgSSBhbSBjZXJ0YWluIHRoYXQgaXQgaXMgbm90IHJlcXVpcmVkIGluIGEgZm9sbG93aW5nIGRvY3VtZW50LgoKTm90ZTogKklNUE9SVEFOVCogVGhpcyBpcyB0aGUgc2V0IE1hcmlhIEFkZWxpYWRhIHByZWZlcnMgdG8gdXNlLgoKYGBge3Igenltb19kZSwgZmlnLnNob3c9ImhpZGUifQpscF96eW1vIDwtIHN1YnNldF9leHB0KGxwX2V4cHQsIHN1YnNldCA9ICJjb25kaXRpb249PSd6Mi4yJ3xjb25kaXRpb249PSd6Mi4zJyIpCmRhdGFfc3RydWN0dXJlcyA8LSBjKGRhdGFfc3RydWN0dXJlcywgImxwX3p5bW8iKQpzYXZlKGxpc3QgPSAibHBfenltbyIsCiAgICAgZmlsZSA9IGdsdWUoInJkYS90bXJjMl9scF96eW1vLXZ7dmVyfS5yZGEiKSkKYGBgCgojIFZhcmlhbnQgZGF0YSB1c2luZyBwYXJhc2l0ZSBSTkFTZXEgcmVhZHMKClRoZSBmb2xsb3dpbmcgc2VjdGlvbiB3aWxsIGNyZWF0ZSBzb21lIGluaXRpYWwgZGF0YSBzdHJ1Y3R1cmVzIG9mIHRoZQpvYnNlcnZlZCB2YXJpYW50cyBpbiB0aGUgcGFyYXNpdGUgc2FtcGxlcy4gIFRoaXMgd2lsbCBpbmNsdWRlIHNvbWUgb2YKb3VyIDIwMTYgc2FtcGxlcyBmb3Igc29tZSBjbGFzc2lmaWNhdGlvbiBxdWVyaWVzLgoKIyMgVGhlIDIwMTYgdmFyaWFudCBkYXRhCgpJIGNoYW5nZWQgYW5kIGltcHJvdmVkIHRoZSBtYXBwaW5nIGFuZCB2YXJpYW50IGRldGVjdGlvbiBtZXRob2RzIGZyb20Kd2hhdCB3ZSB1c2VkIGZvciB0aGUgMjAxNiBkYXRhLiAgU28gc29tZSBzbWFsbCBjaGFuZ2VzIHdpbGwgYmUKcmVxdWlyZWQgdG8gbWVyZ2UgdGhlbS4KCmBgYHtyIG9sZG5ld192YXJpYW50cywgZXZhbD1GQUxTRX0KbHBfcHJldmlvdXMgPC0gY3JlYXRlX2V4cHQoInNhbXBsZV9zaGVldHMvdG1yYzJfc2FtcGxlc18yMDE5MTIwMy54bHN4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZV9jb2x1bW4gPSAidG9waGF0MmZpbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzYXZlZmlsZSA9IGdsdWUoInJkYS9scF9wcmV2aW91cy12e3Zlcn0ucmRhIikpCnR0IDwtIGxwX3ByZXZpb3VzJGV4cHJlc3Npb25zZXQKcm93bmFtZXModHQpIDwtIGdzdWIocGF0dGVybiA9ICJeZXhvbl8iLCByZXBsYWNlbWVudCA9ICIiLCB4ID0gcm93bmFtZXModHQpKQpyb3duYW1lcyh0dCkgPC0gZ3N1YihwYXR0ZXJuID0gIlxcLjEkIiwgcmVwbGFjZW1lbnQgPSAiIiwgeCA9IHJvd25hbWVzKHR0KSkKcm93bmFtZXModHQpIDwtIGdzdWIocGF0dGVybiA9ICJcXC0xJCIsIHJlcGxhY2VtZW50ID0gIiIsIHggPSByb3duYW1lcyh0dCkpCmxwX3ByZXZpb3VzJGV4cHJlc3Npb25zZXQgPC0gdHQKcm0odHQpCmRhdGFfc3RydWN0dXJlcyA8LSBjKGRhdGFfc3RydWN0dXJlcywgImxwX3ByZXZpb3VzIikKYGBgCgojIyBDcmVhdGUgdGhlIFNOUCBleHByZXNzaW9uc2V0CgpUaGUgY291bnRfZXhwdF9zbnBzKCkgZnVuY3Rpb24gdXNlcyBvdXIgZXhwcmVzc2lvbnNldCBkYXRhIGFuZCBhCm1ldGFkYXRhIGNvbHVtbiBpbiBvcmRlciB0byBleHRyYWN0IHRoZSBtcGlsZXVwIG9yIGZyZWViYXllcy1iYXNlZAp2YXJpYW50IGNhbGxzIGFuZCBjcmVhdGUgbWF0cmljZXMgb2YgdGhlIGxpa2VsaWhvb2QgdGhhdCBlYWNoCnBvc2l0aW9uLXBlci1zYW1wbGUgaXMgaW4gZmFjdCBhIHZhcmlhbnQuCgpUaGVyZSBpcyBhbiBpbXBvcnRhbnQgY2F2ZWF0IGhlcmUgd2hpY2ggY2hhbmdlZCBvbiAyMDIzMDE6ICBJIHdhcwppbnRlcnByZXRpbmcgdXNpbmcgdGhlIFBBSVJFRCB0YWcsIHdoaWNoIGlzIG9ubHkgdXNlZCBmb3IsCnVuc3VycHJpc2luZ2x5LCBwYWlyZWQtZW5kIHNhbXBsZXMuICBBIGNvdXBsZSBzYW1wbGVzIGFyZSBub3QgcGFpcmVkCmFuZCBzbyB3ZXJlIGZhaWxpbmcgc2lsZW50bHkuICBUaGUgUUEgdGFnIGxvb2tzIGxpa2UgaXQgaXMgbW9yZQphcHByb3ByaWF0ZSBhbmQgc2hvdWxkIHdvcmsgYWNyb3NzIGJvdGggdHlwZXMuICBPbmUgd2F5IHRvIGZpbmQgb3V0LCBJCmFtIHNldHRpbmcgaXQgaGVyZSBhbmQgd2lsbCBsb29rIHRvIHNlZSBpZiB0aGUgcmVzdWx0cyBtYWtlIG1vcmUgc2Vuc2UKZm9yIG15IHRlc3Qgc2FtcGxlcyAoVE1SQzIwMDEsIFRNUkMyMDA1LCBUTVJDMjAwNykuCgpgYGB7cn0KIyMgVGhlIG5leHQgbGluZSBkcm9wcyB0aGUgc2FtcGxlcyB3aGljaCBhcmUgbWlzc2luZyB0aGUgU05QIHBpcGVsaW5lLgpscF9zbnAgPC0gc3Vic2V0X2V4cHQobHBfZXhwdCwgc3Vic2V0ID0gIiFpcy5uYShwRGF0YShscF9leHB0KVtbJ2ZyZWViYXllc3N1bW1hcnknXV0pIikKCmxwX3NucF9zdWZmaWNpZW50IDwtIHN1YnNldF9leHB0KGxwX3NucCwgc3Vic2V0ID0gInJvd25hbWVzIT0nVE1SQzIwMDgyJyIpCmxwX3NucF9vbmx5MjJfMjNfcmVmIDwtIHN1YnNldF9leHB0KGxwX3NucCwgc3Vic2V0ID0gInp5bW9kZW1lcmVmZXJlbmNlPT0nejIuMid8enltb2RlbWVyZWZlcmVuY2U9PSd6Mi4zJyIpICU+JQogIHN1YnNldF9leHB0KHN1YnNldCA9ICJyb3duYW1lcyE9J1RNUkMyMDA4MiciKQpscF9zbnBfMjJfMjNfbWwgPC0gc3Vic2V0X2V4cHQobHBfc25wLCBzdWJzZXQgPSAia25udjJjbGFzc2lmaWNhdGlvbj09J3oyMid8a25udjJjbGFzc2lmaWNhdGlvbj09J3oyMyciKSAlPiUKICBzdWJzZXRfZXhwdChzdWJzZXQgPSAicm93bmFtZXMhPSdUTVJDMjAwODInIikKCm5ld19zbnBzX3N1ZmZpY2llbnQgPC0gY291bnRfZXhwdF9zbnBzKGxwX3NucF9zdWZmaWNpZW50LCBhbm5vdF9jb2x1bW4gPSAiZnJlZWJheWVzc3VtbWFyeSIsIHNucF9jb2x1bW4gPSAiUUEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkZXIgPSAicmVhZHIiKQpuZXdfc25wc19vbmx5MjJfMjNfcmVmX3N1ZiA8LSBjb3VudF9leHB0X3NucHMobHBfc25wX29ubHkyMl8yM19yZWYsIGFubm90X2NvbHVtbiA9ICJmcmVlYmF5ZXNzdW1tYXJ5Iiwgc25wX2NvbHVtbiA9ICJRQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkZXIgPSAicmVhZHIiKQpuZXdfc25wc18yMl8yM19tbF9zdWYgPC0gY291bnRfZXhwdF9zbnBzKGxwX3NucF8yMl8yM19tbCwgYW5ub3RfY29sdW1uID0gImZyZWViYXllc3N1bW1hcnkiLCBzbnBfY29sdW1uID0gIlFBIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkZXIgPSAicmVhZHIiKQoKIyMgTGV0cyBzZWUgaWYgd2UgZ2V0IG51bWJlcnMgd2hpY2ggbWFrZSBzZW5zZS4Kc3VtbWFyeShleHBycyhuZXdfc25wc19zdWZmaWNpZW50KVtbInRtcmMyMDAwMSJdXSkgICMjIE15IHdlaXJkbyBzYW1wbGUKc3VtbWFyeShleHBycyhuZXdfc25wc19zdWZmaWNpZW50KVtbInRtcmMyMDA3MiJdXSkgICMjIEFub3RoZXIgc2FtcGxlIGNob3NlbiBhdCByYW5kb20Kc3VtbWFyeShleHBycyhuZXdfc25wc19zdWZmaWNpZW50KVtbInRtcmMyMDAyMSJdXSkgICMjIEFub3RoZXIgc2FtcGxlIGNob3NlbiBhdCByYW5kb20KIyMgTm93IHRoYXQgd2UgYXJlIHJlYXNvbmFibHkgY29uZmlkZW50IHRoYXQgdGhpbmdzIG1ha2UgbW9yZSBzZW5zZSwgbGV0cyBzYXZlIGFuZCBtb3ZlIG9uLi4uCmRhdGFfc3RydWN0dXJlcyA8LSBjKGRhdGFfc3RydWN0dXJlcywgIm5ld19zbnBzX3N1ZmZpY2llbnQiLCAibHBfc25wIikKCnN1ZmZpY2llbnRfbm9ybSA8LSBub3JtYWxpemVfZXhwdChuZXdfc25wc19zdWZmaWNpZW50LCB0cmFuc2Zvcm0gPSAibG9nMiIpCnBsb3RfYm94cGxvdChzdWZmaWNpZW50X25vcm13KQpgYGAKCk5vdyBsZXQgdXMgcHVsbCBpbiB0aGUgMjAxNiBkYXRhLgoKYGBge3IgbWVyZ2VfbmV3X29sZCwgZXZhbD1GQUxTRX0Kb2xkX3NucHMgPC0gY291bnRfZXhwdF9zbnBzKGxwX3ByZXZpb3VzLCBhbm5vdF9jb2x1bW4gPSAiYmNmdGFibGUiLCBzbnBfY29sdW1uID0gMikKZGF0YV9zdHJ1Y3R1cmVzIDwtIGMoZGF0YV9zdHJ1Y3R1cmVzLCAib2xkX3NucHMiKQoKc2F2ZShsaXN0ID0gImxwX3NucCIsCiAgICAgZmlsZSA9IGdsdWUoInJkYS9scF9zbnAtdnt2ZXJ9LnJkYSIpKQpkYXRhX3N0cnVjdHVyZXMgPC0gYyhkYXRhX3N0cnVjdHVyZXMsICJscF9zbnAiKQpzYXZlKGxpc3QgPSAibmV3X3NucHMiLAogICAgIGZpbGUgPSBnbHVlKCJyZGEvbmV3X3NucHMtdnt2ZXJ9LnJkYSIpKQpkYXRhX3N0cnVjdHVyZXMgPC0gYyhkYXRhX3N0cnVjdHVyZXMsICJuZXdfc25wcyIpCnNhdmUobGlzdCA9ICJvbGRfc25wcyIsCiAgICAgZmlsZSA9IGdsdWUoInJkYS9vbGRfc25wcy12e3Zlcn0ucmRhIikpCmRhdGFfc3RydWN0dXJlcyA8LSBjKGRhdGFfc3RydWN0dXJlcywgIm9sZF9zbnBzIikKCm5vbnplcm9fc25wcyA8LSBleHBycyhuZXdfc25wcykgIT0gMApjb2xTdW1zKG5vbnplcm9fc25wcykKYGBgCgojIyBDb21iaW5lIHRoZSBwcmV2aW91cyBhbmQgY3VycmVudCBkYXRhCgpBcyBmYXIgYXMgSSBjYW4gdGVsbCwgZnJlZWJheWVzIGFuZCBtcGlsZXVwIGFyZSByZWFzb25hYmx5IHNpbWlsYXIgaW4KdGhlaXIgc2Vuc2l0aXZpdHkvc3BlY2lmaWNpdHk7IHNvIGNvbWJpbmluZyB0aGUgdHdvIGRhdGFzZXRzIGxpa2UgdGhpcwppcyBleHBlY3RlZCB0byB3b3JrIHdpdGggbWluaW1hbCBwcm9ibGVtcy4gIFRoZSBtb3N0IGxpa2VseSBwcm9ibGVtIGlzCnRoYXQgbXkgbXBpbGV1cC1iYXNlZCBwaXBlbGluZSBpcyB1bmFibGUgdG8gaGFuZGxlIGluZGVscy4KCmBgYHtyIGNvbWJpbmVfb2xkX3NucHMsIGV2YWw9RkFMU0V9CiMjIE15IG9sZF9zbnBzIGlzIHVzaW5nIGFuIG9sZGVyIGFubm90YXRpb24gaW5jb3JyZWN0bHksIHNvIGZpeCBpdCBoZXJlOgojYW5ub3RhdGlvbihvbGRfc25wcykgPC0gYW5ub3RhdGlvbihuZXdfc25wcykKYm90aF9zbnBzIDwtIGNvbWJpbmVfZXhwdHMobmV3X3NucHMsIG9sZF9zbnBzKQpzYXZlKGxpc3QgPSAiYm90aF9zbnBzIiwKICAgICBmaWxlID0gZ2x1ZSgicmRhL2JvdGhfc25wcy12e3Zlcn0ucmRhIikpCmRhdGFfc3RydWN0dXJlcyA8LSBjKGRhdGFfc3RydWN0dXJlcywgImJvdGhfc25wcyIpCmBgYAoKIyBTdWJjbGFkZSBtYW51YWwgaW50ZXJwcmV0YXRpb24KCkkgYW0gdGFraW5nIGEgaGVhdG1hcCBmcm9tIG91ciB2YXJpYW50IGRhdGEgYW5kIG1hbnVhbGx5IGlkZW50aWZ5aW5nIHNhbXBsZSBncm91cHMuCgoqIEE6IFRNUkMyMDAyNSwgVE1SQzIwMDI3LCBUTVJDMjAwMjgKKiBCOiBocGdsMDY0MSwgaHBnbDAyNDcsIGhwZ2wwNjMxLCBocGdsMDY1OCwgY2xvc2UgdG8gQQoqIEM6IFRNUkMyMDAwOCwgVE1SQzIwMDA3LCBUTVJDMjAwMDEsIFRNUkMyMDAwNSwgaHBnbDAzMTgsIFRNUkMyMDAxMgoqIEQ6IGhwZ2wwNjQzLCBocGdsMDMxNiwgaHBnbDAzMjAsIGhwZ2wwNjQxLCBjbG9zZSB0byBDCiogRTogVE1SQzIwMDMyLCBUTVJDMjAwNjEKKiBGOiBUTVJDMjAwNDAsIFRNUkMyMDAzNiwgaHBnbDAyNDUsIFRNUkMyMDEwMywgVE1SQzIwMDkzLCBUTVJDMjAwNDUsCiAgICAgVE1SQzIwMDQxLCBUTVJDMjAwNzIsIFRNUkMyMDA0NiwgVE1SQzIwMDU3LCBUTVJDMjAwOTcsIFRNUkMyMDA4NCwKICAgICBjbG9zZSB0byBFCiogRzogaHBnbDA2MzIsIGhwZ2wwNjUyLCBocGdsMDI0OCwgaHBnbDA2NTkKKiBIOiBocGdsMDY1NCwgaHBnbDA2MzQsIGhwZ2wwMjQzLCBocGdsMDI0MywgY2xvc2VzdCB0byBHCiogSTogaHBnbDAyNDIsIGhwZ2wwMzIyLCBocGdsMDYzNiwgaHBnbDA2NjMsIGhwZ2wwNjM4LCBjbG9zZSB0byBICiogSjogVE1SQzIwMDE3LCBUTVJDMjAwMzMsIFRNUkMyMDA1MywgVE1SQzIwMDYzLCBUTVJDMjAwNTYsIFRNUkMyMDA3NCwKICAgICBUTVJDMjAwNTUsIFRNUkMyMDAyMiwgVE1SQzIwMDI2LCBUTVJDMjAwODMsIFRNUkMyMDA3NywgVE1SQzIwMDYwCiogSzogVE1SQzIwMDUwLCBUTVJDMjAwNDIsIFRNUkMyMDA3OCwgVE1SQzIwMDQ5LCBUTVJDMjAwNjksIFRNUkMyMDA0NCwKICAgICBjbG9zZSB0byBKCiogTDogVE1SQzIwMDc2LCBUTVJDMjAwMjQsIFRNUkMyMDA5CiogTTogVE1SQzIwMDE5LCBUTVJDMjAwMjAsIFRNUkMyMDAzMSwgVE1SQzIwMDE0LCBUTVJDMjAwMTEsIGNsb3NlIHRvIEwKKiBOOiBUTVJDMjAwOTYsIFRNUkMyMDA4MSwgVE1SQzIwMTEwLCBUTVJDMjAwOTIsIFRNUkMyMDA4OCwgVE1SQzIwMTAxLAogICAgIFRNUkMyMDEwNiwgVE1SQzIwMDkxLCBUTVJDMjAxMDksIFRNUkMyMDA4NywgVE1SQzIwMDg2LCBjbG9zZWlzaAogICAgIHRvIE0KKiBPOiBUTVJDMjAwOTUsIFRNUkMyMDAxNiwgVE1SQzIwMDE4LCBxdWl0ZSBmYXIgZnJvbSBldmVyeW9uZQoqIFA6IFRNUkMyMDA4MiwgVE1SQzIwMDc1LCBwcmV0dHkgc2VwYXJhdGUgdG9vCiogUTogaHBnbDAyNDYsIGhwZ2wwNjUzLCBocGdsMDYzMywgaHBnbDAyNDQsIGhwZ2wwNjM1LCBocGdsMDY1NSwKICAgICBocGdsMDYzOSwgaHBnbDA2NjIKKiBSOiBUTVJDMjAwNTksIFRNUkMyMDA4OSwgVE1SQzIwMDIxLCBUTVJDMjAwNDgsIFRNUkMyMDA2NwoqIFM6IFRNUkMyMDAxMywgVE1SQzIwMDEwLCBUTVJDMjAwMzcsIFRNUkMyMDA2NiwgVE1SQzIwMDYyLCBUTVJDMjAwMzgsCiAgICAgY2xvc2UgdG8gUgoqIFQ6IFRNUkMyMDAxNSwgVE1SQzIwMTA4LCBUTVJDMjAwOTksIFRNUkMyMDEwMiwgVE1SQzIwMDg1LCBUTVJDMjAwOTAsCiAgICAgVE1SQzIwMTA0LCBUTVJDMjAwOTgsIFRNUkMyMDEwMCwgVE1SQzIwMTA3CiogVTogVE1SQzIwMDQ3LCBUTVJDMjAwNjgsIFRNUkMyMDA4MCwgVE1SQzIwMTA1LCBUTVJDMjAwOTQsIFRNUkMyMDA2NSwKICAgICBUTVJDMjAwNzEsIFRNUkMyMDA2NCwgVE1SQzIwMDQzLCBUTVJDMjAwNzAsIFRNUkMyMDA2MiwgVE1SQzIwMDUxLAogICAgIFRNUkMyMDA3OSwgVE1SQzIwMDczLCBUTVJDMjAwNTgsIFRNUkMyMDA1NAoKIyBNYWNyb3BoYWdlIGRhdGEKCkFsbCBvZiB0aGUgYWJvdmUgZm9jdXNlZCBlbnRpcmUgb24gdGhlIHBhcmFzaXRlIHNhbXBsZXMsIG5vdyBsZXQgdXMKcHVsbCB1cCB0aGUgbWFjcm9waGFnZSBpbmZlY3RlZCBzYW1wbGVzLiAgVGhpcyB3aWxsIGNvbXByaXNlIHR3bwpkYXRhc2V0cywgb25lIG9mIHRoZSBodW1hbiBhbmQgb25lIG9mIHRoZSBwYXJhc2l0ZS4KCiMjIE1hY3JvcGhhZ2UgaG9zdCBkYXRhCgpUaGUgbWV0YWRhdGEgZm9yIHRoZSBtYWNyb3BoYWdlIHNhbXBsZXMgY29udGFpbnMgYSBjb3VwbGUgb2YgY29sdW1ucwpmb3IgbWFwcGVkIGh1bWFuIGFuZCBwYXJhc2l0ZSByZWFkcy4gIFdlIHdpbGwgdGhlcmVmb3JlIHVzZSB0aGVtCnNlcGFyYXRlbHkgdG8gY3JlYXRlIHR3byBleHByZXNzaW9uc2V0cywgb25lIGZvciBlYWNoIHNwZWNpZXMuCgpgYGB7cn0KaHNfYW5ub3QgPC0gbG9hZF9iaW9tYXJ0X2Fubm90YXRpb25zKHllYXIgPSAiMjAyMCIsIG1vbnRoID0gNCkKaHNfYW5ub3QgPC0gaHNfYW5ub3RbWyJhbm5vdGF0aW9uIl1dCmhzX2Fubm90W1sidHJhbnNjcmlwdCJdXSA8LSBwYXN0ZTAocm93bmFtZXMoaHNfYW5ub3QpLCAiLiIsIGhzX2Fubm90W1sidHJhbnNjcmlwdF92ZXJzaW9uIl1dKQpyb3duYW1lcyhoc19hbm5vdCkgPC0gbWFrZS5uYW1lcyhoc19hbm5vdFtbImVuc2VtYmxfZ2VuZV9pZCJdXSwgdW5pcXVlID0gVFJVRSkKcm93bmFtZXMoaHNfYW5ub3QpIDwtIHBhc3RlMCgiZ2VuZToiLCByb3duYW1lcyhoc19hbm5vdCkpCnR4X2dlbmVfbWFwIDwtIGhzX2Fubm90WywgYygidHJhbnNjcmlwdCIsICJlbnNlbWJsX2dlbmVfaWQiKV0KCnNhbml0aXplX2NvbHVtbnMgPC0gYygiZHJ1ZyIsICJtYWNyb3BoYWdldHJlYXRtZW50IiwgIm1hY3JvcGhhZ2V6eW1vZGVtZSIpCm1hY3JfYW5ub3QgPC0gaHNfYW5ub3QKcm93bmFtZXMobWFjcl9hbm5vdCkgPC0gZ3N1Yih4ID0gcm93bmFtZXMobWFjcl9hbm5vdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJeZ2VuZToiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gIiIpCmhzX21hY3JvcGhhZ2UgPC0gY3JlYXRlX2V4cHQoCiAgICBtYWNyb3BoYWdlX3NoZWV0LAogICAgZ2VuZV9pbmZvID0gbWFjcl9hbm5vdCwKICAgIGZpbGVfY29sdW1uID0gImhnMzgxMDBoaXNhdGZpbGUiKSAlPiUKICBzZXRfZXhwdF9jb25kaXRpb25zKGZhY3QgPSAibWFjcm9waGFnZXRyZWF0bWVudCIpICU+JQogIHNldF9leHB0X2JhdGNoZXMoZmFjdCA9ICJtYWNyb3BoYWdlenltb2RlbWUiKSAlPiUKICBzYW5pdGl6ZV9leHB0X3BEYXRhKGNvbHVtbnMgPSBzYW5pdGl6ZV9jb2x1bW5zKSAlPiUKICBzdWJzZXRfZXhwdChub256ZXJvID0gMTIwMDApCmZpeGVkX2dlbmVuYW1lcyA8LSBnc3ViKHggPSByb3duYW1lcyhleHBycyhoc19tYWNyb3BoYWdlKSksIHBhdHRlcm4gPSAiXmdlbmU6IiwKICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiIikKaHNfbWFjcm9waGFnZSA8LSBzZXRfZXhwdF9nZW5lbmFtZXMoaHNfbWFjcm9waGFnZSwgaWRzID0gZml4ZWRfZ2VuZW5hbWVzKQp0YWJsZShwRGF0YShoc19tYWNyb3BoYWdlKSRjb25kaXRpb24pCgojIyBUaGUgZm9sbG93aW5nIDMgbGluZXMgd2VyZSBjb3B5L3Bhc3RlZCB0byBkYXRhc3RydWN0dXJlcyBhbmQgc2hvdWxkIGJlIHJlbW92ZWQgc29vbi4Kbm9zdHJhaW4gPC0gaXMubmEocERhdGEoaHNfbWFjcm9waGFnZSlbWyJzdHJhaW5pZCJdXSkKcERhdGEoaHNfbWFjcm9waGFnZSlbbm9zdHJhaW4sICJzdHJhaW5pZCJdIDwtICJub25lIgoKcERhdGEoaHNfbWFjcm9waGFnZSlbWyJzdHJhaW5fenltbyJdXSA8LSBwYXN0ZTAoInMiLCBwRGF0YShoc19tYWNyb3BoYWdlKVtbInN0cmFpbmlkIl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiXyIsIHBEYXRhKGhzX21hY3JvcGhhZ2UpW1sibWFjcm9waGFnZXp5bW9kZW1lIl1dKQp1bmluZmVjdGVkIDwtIHBEYXRhKGhzX21hY3JvcGhhZ2UpW1sic3RyYWluX3p5bW8iXV0gPT0gInNub25lX25vbmUiCnBEYXRhKGhzX21hY3JvcGhhZ2UpW3VuaW5mZWN0ZWQsICJzdHJhaW5fenltbyJdIDwtICJ1bmluZmVjdGVkIgoKZGF0YV9zdHJ1Y3R1cmVzIDwtIGMoZGF0YV9zdHJ1Y3R1cmVzLCAiaHNfbWFjcm9waGFnZSIpCmBgYAoKRmluYWxseSwgc3BsaXQgb2ZmIHRoZSBVOTM3IHNhbXBsZXMuCgpgYGB7cn0KaHNfdTkzNyA8LSBzdWJzZXRfZXhwdChoc19tYWNyb3BoYWdlLCBzdWJzZXQgPSAidHlwZW9mY2VsbHMhPSdNYWNyb3BoYWdlcyciKQpkYXRhX3N0cnVjdHVyZXMgPC0gYyhkYXRhX3N0cnVjdHVyZXMsICJoc191OTM3IikKYGBgCgojIyBNYWNyb3BoYWdlIHBhcmFzaXRlIGRhdGEKCkluIHRoZSBwcmV2aW91cyBibG9jaywgd2UgdXNlZCBhIG5ldyBpbnZvY2F0aW9uIG9mIGVuc2VtYmwtZGVyaXZlZAphbm5vdGF0aW9uIGRhdGEsIHRoaXMgdGltZSB3ZSBjYW4ganVzdCB1c2Ugb3VyIGV4aXN0aW5nIHBhcmFzaXRlIGdlbmUKYW5ub3RhdGlvbnMuCgpgYGB7cn0KbHBfbWFjcm9waGFnZSA8LSBjcmVhdGVfZXhwdChtYWNyb3BoYWdlX3NoZWV0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVfY29sdW1uID0gImxwYW5hbWVuc2lzdjM2aGlzYXRmaWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX2luZm8gPSBhbGxfbHBfYW5ub3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2F2ZWZpbGUgPSBnbHVlKCJyZGEvbHBfbWFjcm9waGFnZS12e3Zlcn0ucmRhIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbiA9ICJvcmcuTHBhbmFtZW5zaXMuTUhPTUNPTDgxTDEzLnY0Ni5lZy5kYiIpICU+JQpzZXRfZXhwdF9jb25kaXRpb25zKGZhY3QgPSAibWFjcm9waGFnZXp5bW9kZW1lIikgJT4lCiAgc2V0X2V4cHRfYmF0Y2hlcyhmYWN0ID0gIm1hY3JvcGhhZ2V0cmVhdG1lbnQiKQoKdW5maWx0X3dyaXR0ZW4gPC0gd3JpdGVfZXhwdCgKICBscF9tYWNyb3BoYWdlLAogIGV4Y2VsID0gZ2x1ZSgiYW5hbHlzZXMvbWFjcm9waGFnZV9kZS97dmVyfS9yZWFkX2NvdW50cy9scF9tYWNyb3BoYWdlX3JlYWRzX3VuZmlsdGVyZWQtdnt2ZXJ9Lnhsc3giKSkKCmxwX21hY3JvcGhhZ2VfZmlsdCA8LSBzdWJzZXRfZXhwdChscF9tYWNyb3BoYWdlLCBub256ZXJvID0gMjUwMCkgJT4lCiAgc2VtYW50aWNfZXhwdF9maWx0ZXIoc2VtYW50aWMgPSBjKCJhbWFzdGluIiwgImdwNjMiLCAibGVpc2htYW5vbHlzaW4iKSwKICAgICAgICAgICAgICAgICAgICAgICBzZW1hbnRpY19jb2x1bW4gPSAiYW5ub3RfZ2VuZV9wcm9kdWN0IikKZGF0YV9zdHJ1Y3R1cmVzIDwtIGMoZGF0YV9zdHJ1Y3R1cmVzLCAibHBfbWFjcm9waGFnZSIsICJscF9tYWNyb3BoYWdlX2ZpbHQiKQpmaWx0X3dyaXR0ZW4gPC0gd3JpdGVfZXhwdChscF9tYWNyb3BoYWdlX2ZpbHQsCiAgZXhjZWwgPSBnbHVlKCJhbmFseXNlcy9tYWNyb3BoYWdlX2RlL3t2ZXJ9L3JlYWRfY291bnRzL2xwX21hY3JvcGhhZ2VfcmVhZHNfZmlsdGVyZWQtdnt2ZXJ9Lnhsc3giKSkKbHBfbWFjcm9waGFnZSA8LSBscF9tYWNyb3BoYWdlX2ZpbHQKCmxwX21hY3JvcGhhZ2Vfbm9zYiA8LSBzdWJzZXRfZXhwdChscF9tYWNyb3BoYWdlLCBzdWJzZXQgPSAiYmF0Y2ghPSdpbmZfc2InIikKbHBfbm9zYl93cml0ZSA8LSB3cml0ZV9leHB0KAogIGxwX21hY3JvcGhhZ2Vfbm9zYiwKICBleGNlbCA9IGdsdWUoImFuYWx5c2VzL21hY3JvcGhhZ2VfZGUve3Zlcn0vcmVhZF9jb3VudHMvbHBfbWFjcm9waGFnZV9ub3NiX3JlYWRzLXZ7dmVyfS54bHN4IikpCmRhdGFfc3RydWN0dXJlcyA8LSBjKGRhdGFfc3RydWN0dXJlcywgImxwX21hY3JvcGhhZ2Vfbm9zYiIpCgpzcGVjIDwtIG1ha2Vfcm5hc2VxX3NwZWMoKQp0ZXN0IDwtIHNtKGdhdGhlcl9wcmVwcm9jZXNzaW5nX21ldGFkYXRhKG1hY3JvcGhhZ2Vfc2hlZXQsIHNwZWNpZmljYXRpb24gPSBzcGVjKSkKYGBgCgojIFBsb3QgU0wgUmVhZHMgb24gYSBwZXIgY29uZGl0aW9uIGJhc2lzCgpgYGB7cn0KbHBfbWV0YSA8LSBwRGF0YShscF9tYWNyb3BoYWdlKQpscF9tZXRhW1sic2x2c3JlYWRzX2xvZyJdXSA8LSBsb2cxMChscF9tZXRhW1sic2x2c3JlYWRzIl1dKQppbmZfdmFsdWVzIDwtIGlzLmluZmluaXRlKGxwX21ldGFbWyJzbHZzcmVhZHNfbG9nIl1dKQpscF9tZXRhW2luZl92YWx1ZXMsICJzbHZzcmVhZHNfbG9nIl0gPC0gLTEwCgpjb2xvcl92ZWN0b3IgPC0gYXMuY2hhcmFjdGVyKGNvbG9yX2Nob2ljZXNbWyJzdHJhaW4iXV0pCm5hbWVzKGNvbG9yX3ZlY3RvcikgPC0gbmFtZXMoY29sb3JfY2hvaWNlc1tbInN0cmFpbiJdXSkKY29sb3JfdmVjdG9yIDwtIGNvbG9yX3ZlY3RvcltjKCJ6Mi4yIiwgInoyLjMiLCAidW5rbm93biIpXQpuYW1lcyhjb2xvcl92ZWN0b3IpIDwtIGMoInoyLjIiLCAiejIuMyIsICJub25lIikKc2xfdmlvbGluIDwtIGdncGxvdChscF9tZXRhLAogICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gLmRhdGFbWyJjb25kaXRpb24iXV0sIHkgPSAuZGF0YVtbInNsdnNyZWFkc19sb2ciXV0sCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSAuZGF0YVtbImNvbmRpdGlvbiJdXSkpICsKICBnZW9tX3Zpb2xpbigpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9yX3ZlY3RvcikKc2xfdmlvbGluCgpnZ3N0YXRzcGxvdDo6Z2diZXR3ZWVuc3RhdHMobHBfbWV0YSwgeCA9ICJjb25kaXRpb24iLCB5ID0gInNsdnNyZWFkc19sb2ciKQpgYGAKCiMgTWFrZSBhIHNpbGx5IHBsb2lkeSBwbG90CgpJIHdhbnQgdG8gbWFrZSBhbiBlc3RpbWF0ZSBvZiBwbG9pZHkgdXNpbmcgdHJhbnNjcmlwdG9taWMgZGF0YS4gIFRoaXMKaXMgYnkgZGVmaW5pdGlvbiBhIGZvb2xkJ3MgZXJyYW5kLCBidXQgSSB0aGluayBpdCBtaWdodCB3b3JrLgoKYGBge3J9CmxwX3Jwa20gPC0gbm9ybWFsaXplX2V4cHQobHBfZXhwdCwgY29udmVydCA9ICJycGttIiwgZmlsdGVyID0gVFJVRSwgY29sdW1uID0gImFubm90X2Nkc19sZW5ndGgiLCBuYV90b196ZXJvID0gVFJVRSkKCiMjIEV4Y2x1ZGUgc2NhZmZvbGRzCnVud2FudGVkIDwtIGdyZXBsKHBhdHRlcm4gPSAiU0NBRiIsIHggPSBmRGF0YShscF9ycGttKVtbImFubm90X2dlbmVfbG9jYXRpb25fdGV4dCJdXSkKIyMgSSB0aGluayBteSBzdWJzZXQgbG9naWMgaXMgYmFjd2FyZHMuLi4KbHBfd2FudGVkIDwtIGxwX3Jwa21bIXVud2FudGVkLCBdCgpzdW1tYXJ5X2RmIDwtIGFzLmRhdGEuZnJhbWUoZXhwcnMobHBfd2FudGVkKSkKc3VtbWFyeV9kZltbImdlbmVfbWVhbiJdXSA8LSByb3dNZWFucyhzdW1tYXJ5X2RmLCBuYS5ybSA9IFRSVUUpCnN1bW1hcnlfZGZbWyJjaHJvbW9zb21lIl1dIDwtIGZEYXRhKGxwX3dhbnRlZClbWyJhbm5vdF9jaHJvbW9zb21lIl1dCnN1bW1hcnlfZGZbWyJjaHJvbW9zb21lIl1dIDwtIGFzLmZhY3RvcihzdW1tYXJ5X2RmW1siY2hyb21vc29tZSJdXSkKbGV2ZWxzKHN1bW1hcnlfZGZbWyJjaHJvbW9zb21lIl1dKSA8LSBjKHNlcV9sZW4oMTkpLCAiMjAuMSIsICIyMC4yIiwgMjE6MzUpCgpzdW1tYXJ5X2RmIDwtIHN1bW1hcnlfZGZbLCBjKCJnZW5lX21lYW4iLCAiY2hyb21vc29tZSIpXSAlPiUKICBncm91cF9ieShjaHJvbW9zb21lKSAlPiUKICBzdW1tYXJpemUoY2hyX21lYW4gPSBtZWFuKGdlbmVfbWVhbiwgbmEucm0gPSBUUlVFKSkKCm1pbl9ycGttIDwtIG1pbihzdW1tYXJ5X2RmW1siY2hyX21lYW4iXV0pCnN1bW1hcnlfZGZbWyJjaHJfbWVhbiJdXSA8LSBzdW1tYXJ5X2RmW1siY2hyX21lYW4iXV0gLyBtaW5fcnBrbQoKZ2dwbG90KHN1bW1hcnlfZGYsIGFlcyh5ID0gY2hyb21vc29tZSwgeCA9IGNocl9tZWFuKSkgKwogIGdlb21fY29sKCkKCndhbnRlZCA8LSBwRGF0YShscF93YW50ZWQpW1sia25udjJjbGFzc2lmaWNhdGlvbiJdXSA9PSAiejIyIiB8IHBEYXRhKGxwX3dhbnRlZClbWyJrbm52MmNsYXNzaWZpY2F0aW9uIl1dID09ICJ6MjMiCmxwX3ogPC0gbHBfd2FudGVkWywgd2FudGVkXQp6MjJfc2FtcGxlcyA8LSBwRGF0YShscF96KVtbImtubnYyY2xhc3NpZmljYXRpb24iXV0gPT0gInoyMiIKejIzX3NhbXBsZXMgPC0gcERhdGEobHBfeilbWyJrbm52MmNsYXNzaWZpY2F0aW9uIl1dID09ICJ6MjMiCmxwX3pfZXhwcnMgPC0gYXMuZGF0YS5mcmFtZShleHBycyhscF96KSkKbHBfel9leHByc1tbInoyMl9nZW5lX21lYW4iXV0gPC0gcm93TWVhbnMobHBfel9leHByc1ssIHoyMl9zYW1wbGVzXSwgbmEucm0gPSBUUlVFKQpscF96X2V4cHJzW1siejIzX2dlbmVfbWVhbiJdXSA8LSByb3dNZWFucyhscF96X2V4cHJzWywgejIzX3NhbXBsZXNdLCBuYS5ybSA9IFRSVUUpCmxwX3pfZXhwcnNbWyJjaHJvbW9zb21lIl1dIDwtIGZEYXRhKGxwX3opW1siYW5ub3RfY2hyb21vc29tZSJdXQpscF96X21lYW5zIDwtIGxwX3pfZXhwcnNbLCBjKCJ6MjJfZ2VuZV9tZWFuIiwgInoyM19nZW5lX21lYW4iLCAiY2hyb21vc29tZSIpXSAlPiUKICBncm91cF9ieShjaHJvbW9zb21lKSAlPiUKICBzdW1tYXJpemUoejIyX21lYW4gPSBtZWFuKHoyMl9nZW5lX21lYW4sIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIHoyM19tZWFuID0gbWVhbih6MjNfZ2VuZV9tZWFuLCBuYS5ybSA9IFRSVUUpKQoKY2hyX3Jlc2hhcGVkIDwtIHJlc2hhcGUyOjptZWx0KGxwX3pfbWVhbnMsIGlkLnZhcnMgPSAiY2hyb21vc29tZSIpCmNocl9yZXNoYXBlZFtbImNocm9tb3NvbWUiXV0gPC0gZmFjdG9yKGNocl9yZXNoYXBlZFtbImNocm9tb3NvbWUiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoYXMuY2hhcmFjdGVyKDE6MTkpLCAiMjAuMSIsICIyMC4yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoMjE6MzUpKSkKCnB1dGF0aXZlX2FuZXVwbG9pZCA8LSBnZ3Bsb3QoY2hyX3Jlc2hhcGVkLCBhZXMoeCA9IHZhbHVlLCB5ID0gY2hyb21vc29tZSkpICsKICBnZW9tX2JhcihhZXMoZmlsbCA9IHZhcmlhYmxlKSwgcG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gImlkZW50aXR5IikKcHAoZmlsZSA9ICJpbWFnZXMvcHV0YXRpdmVfYW5ldXBsb2lkLnN2ZyIpCnB1dGF0aXZlX2FuZXVwbG9pZApkZXYub2ZmKCkKcHV0YXRpdmVfYW5ldXBsb2lkCmBgYAoKIyBTYXZlIGFsbCBkYXRhIHN0cnVjdHVyZXMgaW50byBvbmUgcmRhCgpgYGB7cn0KZm91bmRfaWR4IDwtIGRhdGFfc3RydWN0dXJlcyAlaW4lIGxzKCkKaWYgKHN1bSghZm91bmRfaWR4KSA+IDApIHsKICBub3RfZm91bmQgPC0gZGF0YV9zdHJ1Y3R1cmVzWyFmb3VuZF9pZHhdCiAgd2FybmluZygiU29tZSBkYXRhc3RydWN0dXJlcyB3ZXJlIG5vdCBnZW5lcmF0ZWQ6ICIsIHRvU3RyaW5nKG5vdF9mb3VuZCksICIuIikKICBkYXRhX3N0cnVjdHVyZXMgPC0gZGF0YV9zdHJ1Y3R1cmVzW2ZvdW5kX2lkeF0KfQpzYXZlKGxpc3QgPSBkYXRhX3N0cnVjdHVyZXMsIGZpbGUgPSBnbHVlKCJyZGEvdG1yYzJfZGF0YV9zdHJ1Y3R1cmVzLXZ7dmVyfS5yZGEiKSkKYGBgCgpgYGB7cn0KcGFuZGVyOjpwYW5kZXIoc2Vzc2lvbkluZm8oKSkKbWVzc2FnZSgiVGhpcyBpcyBocGdsdG9vbHMgY29tbWl0OiAiLCBnZXRfZ2l0X2NvbW1pdCgpKQptZXNzYWdlKCJTYXZpbmcgdG8gIiwgc2F2ZWZpbGUpCiMgdG1wIDwtIHNtKHNhdmVtZShmaWxlbmFtZSA9IHNhdmVmaWxlKSkKYGBgCgpgYGB7ciBsb2FkbWVfYWZ0ZXIsIGV2YWw9RkFMU0V9CnRtcCA8LSBsb2FkbWUoZmlsZW5hbWUgPSBzYXZlZmlsZSkKYGBgCg==