TODOs
- Check this for correctness.
- Reorganize it
- GSVA (Hänzelmann, Castelo, and Guinney
(2013))
- Consider embedding some/many/all of the Excel outputs into the html
output via xfun::embed_dir(‘excel/’)
Meeting with
Theresa
Previous papers did not do an explicit subtraction, instead just
compared to WT and kept the genes which are > in delta/het vs. wt.
There are multiple ways to deal with this and that query has not yet
been defined. Later, Theresa came to the conclusion that the subtraction
method is not appropriate.
Introduction
In this document I hope to explore the freshly processed samples and
perform some comparisons to see that we have the expected similarities
and differences from the prior analysis performed by Theresa.
There is one way in which I expect any/all of these analyses to be
explicitly different: this should include the changes produced by
April’s renaming of some samples.
My intention is to produce a sample sheet which includes one column
with non-umi-deduplicated results and one with deduplicated results.
With the exception of the previous point, I hope that the first will be
identical (or at least very close to identical) to Theresa’s result
while the second I expect will be subtly different – but I am hoping
subtly enough that it will not significantly change the interpretation
but be a little more precise.
Lets see! I need therefore to make a change to my metadata gathering
function to include the umi deduplicated result. I am thinking therefore
to create a separate specification for umi-barcoded samples because
looking through the logs for umi stuff when they are not used will be
too much of a pain…
Small random
reminder
I have a couple pictures of RPL22 to help me remember the
experimental design:
- The human ribosome with RPL22 in red:

- The mouse ribosome with RPL22 in green at the center:

That second picture came from: (Li et al.
(2022))
A note about
implementation
I would like to improve this document by comparing/contrasting the
methodologies performed by other groups and those performed by me in it.
I never fully appreciated the suite of computational methods applied by
previous groups when examining TRAP data; I instead simply followed
Theresa’s notebook without considering other possibilities.
I therefore spent a little time stepping through her thesis and
pulling out the relevant papers in the hopes of learning these various
methods. I should therefore be able soon to compare/contrast the various
methods employed by other labs in addition to copying Theresa’s
logic.
The following block
cannot work in the container
The following block assumes the full tree of preprocessed data with
the logs from the trimmer, mapping, umi deduplication, counting, etc. As
a result it cannot work in the container which has only the various
count tables.
As a result, I am including a copy of this sheet after running the
following block in my working tree. I suppose for the moment you will
have to trust that it worked. (for right now, when testing out this
container, I am just sending the R working directory to my tree for this
block, then moving it back.
I will need to manually edit one column though, the symlink column
from Theresa has a series of paths which do not work in the
container.
umi_spec <- make_rnaseq_spec(umi = TRUE)
iprgc_2022_meta <- gather_preprocessing_metadata("sample_sheets/20240606_only_umd_sequenced.xlsx",
spec = umi_spec, species = "mm39_112", verbose = FALSE,
basedir = "preprocessing/umd_sequenced")
colnames(iprgc_2022_meta[["new_meta"]])
head(iprgc_2022_meta[["new_meta"]])
sample_sheet <- "sample_sheets/20240606_only_umd_sequenced_modified.xlsx"
From this point on, I am hoping/intending to pull liberally from
Theresa’s notebook with a diversion to compare the three datasets:
- Pre-April renaming: E.g. Theresa’s current dataset
- Post renaming: Unless I am mistaken, this should be very similar to
the above.
- Post deduplication: Given what I saw from the extracted logs in the
sample sheet, I expect this to be similar but not identical to the
previous two.
Lets find out! But first, annotations!
Annotation data
I am pulling this from Theresa’s anxontrapR_pipeline.Rmd, primarily
because it looks similar to the other documents, but was modified more
recently. I will change it slightly, primarily because I grabbed a new
mmusculus assembly and therefore I will pull the mmusculus annotations
from a specific biomart (Smedley et al.
(2009)) archive that should match it.
mm_annot <- load_biomart_annotations(species = "mmusculus", year = "2023", month = "02")
## The biomart annotations file already exists, loading from it.
mm_annot <- mm_annot[["annotation"]]
mm_annot[["txid"]] <- paste0(mm_annot[["ensembl_transcript_id"]], ".", mm_annot[["version"]])
rownames(mm_annot) <- make.names(mm_annot[["ensembl_gene_id"]], unique=TRUE)
tx_gene_map <- mm_annot[, c("txid", "ensembl_gene_id")]
Hisat2
expressionsets
The primary difference between my block and Theresa’s are:
- I am pulling the metadata directly from the
gather_preprocessing_metadata() above.
- I am using the column ‘symlink’ which is just a copy of the existing
‘file’ column with a small change so I can load it from my directory
without having to copy everything.
- I am using the ensembl genome release 39, version 112 and so pulled
a somewhat newer copy of the annotation data.
- The original is named ‘v1’, followed by ‘v2’ and ‘v3’ for the other
two treatments I performed.
Color choices and
reused parameters
Given that we are excluding a bunch of the older samples, the set of
colors I expect to find is different; so I will make explicit here the
various colors used to denote location/genotype/time/etc.
April turned me onto this website ‘paletton.com’ for this kind of
stuff and I will try and pick out palettes which basically match what I
am getting with the original colors.
color_choices <- list(
"all" = list(
"p08_het_dlgn" = "#E7298A",
"p15_het_dlgn" = "#E7298A",
"p08_het_retina" = "#238B45",
"p15_het_retina" = "#238B45",
"p08_het_scn" = "#4292C6",
"p15_het_scn" = "#4292C6",
"p08_ko_dlgn" = "#C994C7",
"p15_ko_dlgn" = "#C994C7",
"p08_ko_retina" = "#74c476",
"p15_ko_retina" = "#74c476",
"p08_ko_scn" = "#9BCAE1",
"p15_ko_scn" = "#9BCAE1",
"p08_wt_dlgn" = "#980043",
"p15_wt_dlgn" = "#980043",
"p08_wt_retina" = "#004008",
"p15_wt_retina" = "#004008",
"p08_wt_scn" = "#08519C",
"p15_wt_scn" = "#08519C",
"p60_wt_dlgn" = "#333333",
"p60_wt_retina" = "#222222",
"p60_wt_scn" = "#111111"),
"geno_loc" = list(
"het_dlgn" = "#E7298A",
"het_retina" = "#238B45",
"het_scn" = "#4292C6",
"ko_dlgn" = "#C994C7",
"ko_retina" = "#74c476",
"ko_scn" = "#9BCAE1",
"wt_dlgn" = "#980043",
"wt_retina" = "#004008",
"wt_scn" = "#08519C"),
"location" = list(
"retina" = "#004008",
"dlgn" = "#980043",
"scn" = "#08519C"),
"genotype" = list(
"wt" = "#74c476",
"het" = "#238B45",
"ko" = "#006D2C"),
"time" = list(
"p08" = "#5E104B",
"p15" = "#4E9231"))
label_column <- "mgi_symbol" ## Set the column used to extract gene symbols rather than ENSG.....
colors <- color_choices[["geno_loc"]]
There is one noteworthy sample: iprgc_103, it was effectively
replaced when April renamed the samples and so exists in the v1 data,
but not v2/v3; they instead have the newly named samples which I called
iprgc_123 to iprgc_130. As a result, I copied the annotations for
iprgc_123 to my column so that there is no discrepency in terms of
genotype/location/time.
The original count
tables
At the moment I have not included the original counts in this
container because we made some changes to the mapping strategy and also
found that a couple samples were mixed up in sequencing; as a result I
documented all of the changes in the sample sheets and preprocessing
documents and excluded the original files.
This is also why some columns in the sample sheet have suffixes like
‘adh’ and ‘atb’, those denote from whom the relevant metadata columns
came from.
mm38_hisat_v1 <- create_expt(sample_sheet,
gene_info = mm_annot,
file_column = "symlink") %>%
set_expt_conditions(fact = "geno_loc_atb") %>%
set_expt_batches(fact = "time_atb") %>%
set_expt_colors(color_choices[["geno_loc"]])
mm38_hisat_v1
Recounted tables and
the deduplicated result
In the following I make two more versions of the data, one remapped
with the changes to the sample identities, and one with deduplication
applied.
mm38_hisat_v2 <- create_expt(sample_sheet, gene_info = mm_annot,
file_column = "hisat_count_table") %>%
set_expt_conditions(fact = "geno_loc_atb") %>%
set_expt_batches(fact = "time_atb") %>%
set_expt_colors(color_choices[["geno_loc"]])
## Reading the sample metadata.
## 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 76 columns(metadata fields).
## Warning in file(file, "rt"): cannot open file
## '/lab/sw/singularity/iprgc_analyses/data/preprocessing/umd_sequenced/iprgc_62/outputs/40hisat_mm39_112/mm39_112_genome-paired_sreverse_gene_ID.count.xz':
## No such file or directory
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': cannot open the connection
## Error: object 'mm38_hisat_v2' not found
mm38_hisat_v3 <- create_expt(sample_sheet, gene_info = mm_annot,
file_column = "umi_dedup_output_count") %>%
set_expt_conditions(fact = "geno_loc_atb") %>%
set_expt_batches(fact = "time_atb") %>%
set_expt_colors(color_choices[["geno_loc"]])
## Reading the sample metadata.
## 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 76 columns(metadata fields).
## Warning in file(file, "rt"): cannot open file
## '/lab/sw/singularity/iprgc_analyses/data/preprocessing/umd_sequenced/iprgc_62/outputs/04umi_dedup/umi_tools_deduplicated_sreverse_gene_ID.count.xz':
## No such file or directory
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': cannot open the connection
## Error: object 'mm38_hisat_v3' not found
all_fact <- paste0(pData(mm38_hisat_v3)[["time_atb"]], "_",
pData(mm38_hisat_v3)[["geno_loc_atb"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'mm38_hisat_v3' not found
pData(mm38_hisat_v3)[["time_geno_loc"]] <- all_fact
## Error: object 'all_fact' not found
Note the end of the previous block, I created a factor out of the
combination of time, genotype, and location. In a future invocation of
this notebook, I will change the pairwise comparisons to add each of
these three factors to the statistical model instead of this. The code
to do that is not quite ready yet.
Non-zero Counts per
Sample
Let’s look at the number of non-zero genes for all samples versus the
coverage.
As above, this does not get run because I did not copy the count
tables.
v1_nonzero <- plot_nonzero(mm38_hisat_v1)
v1_nonzero
But these do!
plot_legend(mm38_hisat_v2)
## Error: object 'mm38_hisat_v2' not found
v2_nonzero <- plot_nonzero(mm38_hisat_v2)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'plot_nonzero': object 'mm38_hisat_v2' not found
## Error: object 'v2_nonzero' not found
pp(file = "images/nonzero_v2_unfiltered.pdf")
v2_nonzero[["plot"]]
## Error: object 'v2_nonzero' not found
plotted <- dev.off()
v3_nonzero <- plot_nonzero(mm38_hisat_v3)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'plot_nonzero': object 'mm38_hisat_v3' not found
## Error: object 'v3_nonzero' not found
pp(file = "images/nonzero_v3_unfiltered.pdf")
v3_nonzero[["plot"]]
## Error: object 'v3_nonzero' not found
Oh wow, I did not expect such a profound effect on the cpm values on
the more saturated libraries. I guess in retrospect I should have?
Also note to self, we are not messing with p60.
mm38_hisat_v2 <- subset_expt(mm38_hisat_v2, subset = "time_atb!='p60'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'mm38_hisat_v2' not found
v2_nonzero_filt <- plot_nonzero(mm38_hisat_v2, plot_labels = FALSE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'plot_nonzero': object 'mm38_hisat_v2' not found
pp(file = "images/nonzero_v2_filt.pdf")
v2_nonzero_filt[["plot"]]
## Error: object 'v2_nonzero_filt' not found
plotted <- dev.off()
mm38_hisat_v3 <- subset_expt(mm38_hisat_v3, subset = "time_atb!='p60'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'mm38_hisat_v3' not found
v3_nonzero_filt <- plot_nonzero(mm38_hisat_v3, plot_labels = FALSE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'plot_nonzero': object 'mm38_hisat_v3' not found
pp(file = "images/nonzero_v3_filt.pdf")
v3_nonzero_filt[["plot"]]
## Error: object 'v3_nonzero_filt' not found
Once again, I do not want to lose the previous code, so here is the
v1 invocation
mm38_hisat_v1 <- subset_expt(mm38_hisat_v1, subset = "time_atb!='p60'")
Quick PCA, then return
to Theresa’s document
v2_norm <- normalize_expt(mm38_hisat_v2, transform = "log2", convert = "cpm",
norm = "quant", filter = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'normalize_expt': object 'mm38_hisat_v2' not found
v2_norm_pca <- plot_pca(v2_norm)
## Error: object 'v2_norm' not found
## Error: object 'v2_norm_pca' not found
pp(file = "images/v2_norm_pca.pdf")
v2_norm_pca[["plot"]]
## Error: object 'v2_norm_pca' not found
plotted <- dev.off()
v2_norm_pca[["plot"]]
## Error: object 'v2_norm_pca' not found
v3_norm <- normalize_expt(mm38_hisat_v3, transform = "log2", convert = "cpm",
norm = "quant", filter = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'normalize_expt': object 'mm38_hisat_v3' not found
v3_norm_pca <- plot_pca(v3_norm)
## Error: object 'v3_norm' not found
pp(file = "images/v3_norm_pca.pdf")
v3_norm_pca[["plot"]]
## Error: object 'v3_norm_pca' not found
plotted <- dev.off()
v3_norm_pca[["plot"]]
## Error: object 'v3_norm_pca' not found
Ibid.
v1_norm <- normalize_expt(mm38_hisat_v1, transform = "log2", convert = "cpm",
norm = "quant", filter = TRUE)
plot_pca(v1_norm)
To my eyes it looks like we just have 1 weirdo p15 sample?
Deduplication had a minor but significant effect on the PCA.
With that in mind, let us look at Theresa’s WORKING document and see
what we can recapitulate.
Theresa’s document: The TRAP protocol has some variability which is
introduced at different stpdf including homogenization, antibody
labeling, pulldown efficiency/specificity, sample handling during
cleanup stpdf, and library prep/sequencing. We know from Rashmi’s QC
that there is variability at the level of pulldown efficiency (amount of
RNA isolated). She is doing a good job of keeping track of this for all
her samples and we have validated her P8 results (attached supplementary
figure 3D). We consistently see clear differences between control and
cre samples for the retina, which makes sense because the cell bodies
are in the retina. The target tissue differences are smaller, which also
makes sense for axon-TRAP. We think that some of her P15 samples are not
good based on low amounts of isolated RNA from cre(+) retina samples. We
plan to drop these samples and not perform additional isolations at this
time point. Based on this (and the general lack of large developmental
effects), we were planning to focus on presenting the P8 data only in
the paper. Interested to hear your thoughts in this…
My notes: Theresa’s first operations in this notebook were to:
- Set location as condition, genotype as batch.
- Perform PCA before/after sva.
v3_loc_geno <- set_expt_conditions(mm38_hisat_v3, fact = "location_atb",
colors = color_choices[["location"]]) %>%
set_expt_batches(fact = "genotype_atb")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': object 'mm38_hisat_v3' not found
The associated
PCA
At different times, it appears to me that Theresa has preferred
slightly different normalization methods, primarily a mix of TMM and
quantile.
Thus I will use different suffix letters to denote various
normalizations employed, and if they turn out the same I will pick one
arbitrarily.
loc_geno_nq <- normalize_expt(v3_loc_geno, transform = "log2", convert = "cpm",
filter = TRUE, norm = "quant")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'normalize_expt': object 'v3_loc_geno' not found
location_genotype_pca <- plot_pca(loc_geno_nq)
## Error: object 'loc_geno_nq' not found
pp(file = "images/location_genotype_norm_pca.pdf")
location_genotype_pca[["plot"]]
## Error: object 'location_genotype_pca' not found
plotted <- dev.off()
location_genotype_pca[["plot"]]
## Error: object 'location_genotype_pca' not found
## ok, I have two weirdo samples which look very much like they are actually dlgn.
## These are sample IDs iprgc_66 and iprgc_130
loc_geno_nt <- normalize_expt(v3_loc_geno, transform = "log2", convert = "cpm",
filter = TRUE, norm = "tmm")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'normalize_expt': object 'v3_loc_geno' not found
location_genotype_tmm_pca <- plot_pca(loc_geno_nt)
## Error: object 'loc_geno_nt' not found
pp(file = "images/location_genotype_tmm_pca.pdf")
location_genotype_tmm_pca[["plot"]]
## Error: object 'location_genotype_tmm_pca' not found
plotted <- dev.off()
location_genotype_tmm_pca[["plot"]]
## Error: object 'location_genotype_tmm_pca' not found
A random thought about these PCA plots, it might be worth while to
add a panel below the legend with the sample numbers per
condition/batch.
Of course, the same information is provided in a more fun fashion via
my silly sankey function:
sample_sankey <- plot_meta_sankey(v3_loc_geno, color_choices = color_choices,
factors = c("genotype_atb", "location_atb", "time_atb"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'design' in selecting a method for function 'plot_meta_sankey': object 'v3_loc_geno' not found
pp(file = "images/design_sankey.pdf")
sample_sankey[["ggplot"]]
## Error: object 'sample_sankey' not found
plotted <- dev.off()
sample_sankey
## Error: object 'sample_sankey' not found
A Short conversation
with Rashmi
Rashmi came by and we discussed the samples a little. She suggested
that is likely that we will need to exclude the 202205 samples, these
may be identified by a few ways, most easily I think via the
‘project_ah’ column, they are the 021_1 samples.
My sense was that she concurred with my interpretation of the umi
deduplication, so I will continue using the deduplicated results
exclusively, at least for now.
Melanopsin Sanity
Check
One of Theresa’s first checks was wisely for melanopsin. Let us
repeat a version of this:
opn4_exprs <- data.frame(combined = pData(loc_geno_nt)[["geno_loc_atb"]],
location = pData(loc_geno_nt)[["location_atb"]],
genotype = pData(loc_geno_nt)[["genotype_atb"]],
opn = exprs(loc_geno_nt)["ENSMUSG00000021799", ])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'loc_geno_nt' not found
groupedstats::grouped_summary(opn4_exprs, location, opn)
## Error in loadNamespace(x): there is no package called 'groupedstats'
opn4_location <- ggbetweenstats(data = opn4_exprs, x = location, y = opn)
## Error: object 'opn4_exprs' not found
pp(file = "images/ggbetween_location.pdf")
opn4_location
## Error: object 'opn4_location' not found
plotted <- dev.off()
opn4_location
## Error: object 'opn4_location' not found
opn4_genotype <- ggbetweenstats(data = opn4_exprs, x = genotype, y = opn)
## Error: object 'opn4_exprs' not found
pp(file = "images/ggbetween_location.pdf")
opn4_location
## Error: object 'opn4_location' not found
plotted <- dev.off()
opn4_location
## Error: object 'opn4_location' not found
opn4_combined <- ggbetweenstats(data = opn4_exprs, x = combined, y = opn)
## Error: object 'opn4_exprs' not found
pp(file = "images/ggbetween_combined.pdf")
opn4_combined
## Error: object 'opn4_combined' not found
plotted <- dev.off()
opn4_combined
## Error: object 'opn4_combined' not found
ok, so I plotted the question a bit differently, but got the same
answer.
Here is the text of Theresa’s notebook following this analysis:
“Ugh oh, looks like there is at least one retina KO sample that has
some melanopsin expression in it. Turns out ipRGC_07 is a bad egg which
is supposed to be a KO but has melanopsin expression. It’s friends which
were pooled from the same mice are iprgc_06 and iprgc_08, so we need to
exclude all these samples.”
I am also seeing some knockout expression with some caveats: I do not
have the affected samples in my dataset (iprgc_07) and the levels I am
seeing are quite low – I will look in IGV to double check, but I
strongly suspect that these are some piddly reads near the UTRs.
Onward!
Library sizes
post-deduplication
Theresa’s next operation was to perform libsize/nonzero plots. I
already did the pre/post deduplication nonzero, here is the analagous
libsize.
v2 is pre-deduplication and v3 is post.
plot_libsize(mm38_hisat_v2)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'plot_libsize': object 'mm38_hisat_v2' not found
post_filter_nonzero <- plot_libsize(mm38_hisat_v3, text = FALSE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'plot_libsize': object 'mm38_hisat_v3' not found
pp(file = "images/post_all_filteres_nonzero.pdf")
post_filter_nonzero[["plot"]]
## Error: object 'post_filter_nonzero' not found
plotted <- dev.off()
post_filter_nonzero
## Error: object 'post_filter_nonzero' not found
I am a bit concerned about some of these library sizes
post-deduplication.
Let us look at the relationship between reads and duplication, which
I assume will be relatively linear.
test <- pData(mm38_hisat_v3)[, c("hisat_genome_single_all", "umi_dedup_pct_reads")]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'mm38_hisat_v3' not found
test_plot <- plot_linear_scatter(test, loess = TRUE)
## Error: object 'test' not found
## Error: object 'test_plot' not found
Theresa also produced a density/sample plot, that might prove quite
useful for these due to their significantly larger variance across
samples (due to deduplication).
mm38_density <- plot_density(loc_geno_nt)
## Error: object 'loc_geno_nt' not found
mm38_density[["plot"]] +
theme(legend.position = "none")
## Error: object 'mm38_density' not found
plot_boxplot(loc_geno_nt)
## Error: object 'loc_geno_nt' not found
There is some difference across sample densities, but it is not too
crazytown.
PCA plots
PCA of all genes by
location
Theresa’s first pca was of log2 cpm values. I might add quantile/tmm
to this?
v3_location <- set_expt_conditions(mm38_hisat_v3, fact = "location_atb") %>%
set_expt_batches(fact = "genotype_atb") %>%
set_expt_colors(color_choices[["location"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': object 'mm38_hisat_v3' not found
v3_location_norm <- normalize_expt(v3_location, filter = TRUE, norm = "quant",
transform = "log2", convert = "cpm")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'normalize_expt': object 'v3_location' not found
plot_pca(v3_location_norm)
## Error: object 'v3_location_norm' not found
Once again we see that samples iprgc_66 and iprgc_130 are likely
actually DLGN and not SCN. I am therefore going to add a column to the
sample sheet noting this, and remove them from the expressionset.
I will thus replot the data after removing those two. If we want to
see what it looks like with the re-attributed locations, we can do
so.
Theresa has a nice change to the PCA plotter in which she sets the
alpha channel as an additional visual queue for a metadata factor…
mm38_hisat_v3 <- subset_expt(mm38_hisat_v3, subset="sampleid!='iprgc_130'") %>%
subset_expt(subset="sampleid!='iprgc_66'")
## 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 'mm38_hisat_v3' not found
v3_location <- set_expt_conditions(mm38_hisat_v3, fact = "location_atb") %>%
set_expt_batches(fact = "genotype_atb") %>%
set_expt_colors(color_choices[["location"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': object 'mm38_hisat_v3' not found
v3_location_norm <- normalize_expt(v3_location, filter = TRUE, norm = "quant",
transform = "log2", convert = "cpm")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'normalize_expt': object 'v3_location' not found
filtered_location_pca <- plot_pca(v3_location_norm)
## Error: object 'v3_location_norm' not found
pp(file = "images/filtered_location_pca.pdf")
filtered_location_pca[["plot"]]
## Error: object 'filtered_location_pca' not found
plotted <- dev.off()
filtered_location_pca
## Error: object 'filtered_location_pca' not found
removed_sankey <- plot_meta_sankey(v3_location, color_choices = color_choices,
factors = c("genotype_atb", "location_atb", "time_atb"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'design' in selecting a method for function 'plot_meta_sankey': object 'v3_location' not found
pp(file = "images/filtered_sankey.pdf")
removed_sankey[["ggplot"]]
## Error: object 'removed_sankey' not found
plotted <- dev.off()
removed_sankey
## Error: object 'removed_sankey' not found
Here is Theresa’s text, recall once again that I do not have some of
these older samples (iprgc_62):
PC1 vs PC2 identifies retina vs axon is still the main component of
variation. We do see though that in the PC2 direction, we see with the
new samples added, we don’t see separation based on axonal targets (dLGN
vs SCN). In the PC1 vs PC3 plot, we see that it’s PC3 where we start to
see variation correlated with axonal compartment. Let’s look at PC1 vs
PC2 colored by batch (when they were processed/sequenced) to see if that
is what is contributing so much variation in PC2.
Side note: ipRGC 62 seems like an odd ball. This seems to me like it
should have been a dLGN P08 sample. Is there any possibility this got
mislabeled early on? I went back and double checked to see if all my
processing is correct and it indeed was labeled an SCN P15 from the time
I got the samples, and it is indeed.
DE
I now switched to Theresa’s document ‘WORKING_axonTRAP…’ and will
start pulling sections from it. I am reasonably certain I have
reasonably similar sample distributions, so I presume I can invoke
similar/identical calls for DESeq and friends.
p8 retinas
In the block immediately before the DE analyses, Theresa created a
subset expressionset of only p08 retinas. Thus this initial DE I assume
will be used to subtract for the SCN/DLGN analyses that follow. (I guess
I could read ahead and find out, but no! I want to be a blank slate)
Theresa’s primary workflow makes heavy use of DESeq2 (Love, Huber, and Anders (2014)) and sva (Leek et al. (2012)). In some(most?) of Theresa’s
invocations of the all_pairwise() function, she excludes the other
methods that it performs. In this workbook, I left those methods on,
thus we can evaluate the relative performance DESeq2 vs. some (all? I
may have disabled EBSeq/dream because they were taking too long) of the
following:
- limma: (Ritchie et al. (2015)) (among
other references) originally written for microarrays.
- EdgeR: (Robinson, McCarthy, and Smyth
(2010)), which shares many assumptions with DESeq2.
- EBSeq: (Leng et al. (2013)), because I
have a soft spot for any Bayesian method.
- Noiseq: (Tarazona et al. (2011)),
which seeks to directly model variance in an RNASeq dataset and use that
to improve the sensitivity of the result, much like:
- Dream: (Gabriel E. Hoffman and Roussos
(2020)), written by the same authors (and uses very similar
logic) as one of my favorite tools, variancePartition(Gabriel E. Hoffman and Schadt (2016)).
mm38_p8_retina <- subset_expt(mm38_hisat_v3, subset = "time_atb=='p08' & location_atb=='retina'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'mm38_hisat_v3' not found
mm_normal_p8_ret_de <- all_pairwise(mm38_p8_retina, model_batch = "svaseq", filter = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'mm38_p8_retina' not found
## Error: object 'mm_normal_p8_ret_de' not found
The following invocation performed by Theresa filters the wt/het
comparison for only those genes which increased by at least 0.25 logFC
with a significant adjusted p-value. I assume that this is to use the wt
samples as a translational control for the ket/ko comparisons; I am
therefore thinking that for my purposes, I will therefore separate the
contrasts from all_pairwise do this in a stepwise fashion…
The block of code immediately following Theresa’s all_pairwise()
invocation is a little confusing for me and warrants some explanation by
me to me in the hopes that I do not misunderstand what is happening and
the goals therein.
I think I can safely assume that the goal here is to pull out the IDs
which increased in het with respect to wild type; even if by a small
margin, as long as it is statistically significant vis a vis the
adjusted p-value.
I am going to perform what I think is the same thing in a slightly
different fashion so that I can share a copy of the results with
whomever is interested. I will also repeat Theresa’s invocation and
prove to myself that I understood and got the same answer.
wt_het_keeper <- list("het_vs_wt" = c("het_retina", "wt_retina"))
het_wt_table <- combine_de_tables(mm_normal_p8_ret_de, keepers = wt_het_keeper,
label_column = label_column,
excel = "excel/het_retina_control.xlsx")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'mm_normal_p8_ret_de' not found
wanted_sig <- extract_significant_genes(het_wt_table,
lfc = 0.25,
according_to = "deseq")
## Error: object 'het_wt_table' not found
wanted_het_increased <- wanted_sig[["deseq"]][["ups"]][["het_vs_wt"]]
## Error: object 'wanted_sig' not found
increased_het_genes <- rownames(wanted_het_increased)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'wanted_het_increased' not found
Here are Theresa’s next lines:
mm_de_normal_p8_ret <- mm_normal_p8_ret_de
## Error: object 'mm_normal_p8_ret_de' not found
hetkeeper_genes <- mm_de_normal_p8_ret$deseq$all_tables$wt_retina_vs_het_retina %>%
filter(logFC <= -0.25 & adj.P.Val <= 0.05)
## Error: object 'mm_de_normal_p8_ret' not found
kokeeper_genes <- mm_de_normal_p8_ret$deseq$all_tables$wt_retina_vs_ko_retina %>%
filter(logFC <= -0.25 & adj.P.Val <= 0.05)
## Error: object 'mm_de_normal_p8_ret' not found
keepergenes <- unique(c(rownames(hetkeeper_genes),
rownames(kokeeper_genes)))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'unique': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'hetkeeper_genes' not found
## We know a priori that Opn4 is ENSMUSG00000021799
## I do not expect to see it in this set, it should be higher in wt
## retina vs ko retina by a significant margin.
"ENSMUSG00000021799" %in% keepergenes
## Error in h(simpleError(msg, call)): error in evaluating the argument 'table' in selecting a method for function '%in%': object 'keepergenes' not found
## Oooohhh but it _is_ higher in het vs. wt, as we saw in
## the violin plot earlier.
I think Rashmi made a compelling point which illustrates why we
likely should expect the expression of Opn4 to significantly higher in
the heterozygotes vs wild-type:
- Recall that the assay is using the immunopurification to extract the
RNAs.
- The wt samples do not have the cre recombinase and therefore no HA
and therefore everything we observe is due to non-specific binding.
- The set of genes observed due to non-specific binding is different
than het/ko (presumably a larger number of relatively small values),
therefore the divisor performed in the cpm is likely relativly large
resulting in normalized values getting shifted down to some degree.
- On the other hand, the set of genes observed in het/ko are more
likely to be only the specific binders and therefore smaller (I can test
this) resulting in a smaller divisor and slight shifting up in the cpm
values.
This makes me wonder if any normalization methods exist which do
something like multiply the values by some value related to the
proportion of observed genes; and/or if this is a good/bad/indifferent
idea.
Also, just a note for me to remember: RPL22, not RPS22, for some
reason I keep thinking the small subunit.
Prove I
understood
hetkeeper_genes <- mm_normal_p8_ret_de$deseq$all_tables$wt_retina_vs_het_retina %>%
filter(logFC <= -0.25 & adj.P.Val <= 0.05)
## Error: object 'mm_normal_p8_ret_de' not found
testthat::expect_true(nrow(hetkeeper_genes) == length(increased_het_genes))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'nrow': object 'hetkeeper_genes' not found
taa_keepers <- sort(rownames(hetkeeper_genes))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'sort': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'hetkeeper_genes' not found
atb_keepers <- sort(increased_het_genes)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'sort': object 'increased_het_genes' not found
testthat::expect_equal(taa_keepers, atb_keepers)
## Error: object 'taa_keepers' not found
Yay! I can read! Now let us repeat for the KO vs wt
wt_ko_keeper <- list("ko_vs_wt" = c("ko_retina", "wt_retina"))
ko_wt_table <- combine_de_tables(mm_normal_p8_ret_de, keepers = wt_ko_keeper,
label_column = label_column,
excel = "excel/ko_retina_control.xlsx")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'mm_normal_p8_ret_de' not found
wanted_sig <- extract_significant_genes(ko_wt_table,
lfc = 0.25,
according_to = "deseq")
## Error: object 'ko_wt_table' not found
wanted_ko_increased <- wanted_sig[["deseq"]][["ups"]][["ko_vs_wt"]]
## Error: object 'wanted_sig' not found
increased_ko_genes <- rownames(wanted_ko_increased)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'wanted_ko_increased' not found
The next thing performed in Theresa’s document is a
unique(concatenation of these two gene groups), thus sucking up every
gene which was significantly higher in either the knockout or
heterzyous samples with respect to wild-type.
This was followed by a couple of merge operations of a little bit of
the annotation data; I am not sure I understand the goal yet…
Here is her code. I copied the annotation ‘mgi_symbol’ column to
‘external_gene_name’ so that I need not change any of her code. I am
assuming this is the appropriate column of interest, I do not know this
for certain, but it seems quite likely.
While I am at it, here is the set_sig_limma() function from Theresa’s
helpers.R
set_sig_limma <- function(limma_tbl, factors = NULL) {
if (is.null(factors)) {
#set significance for plotting colors
limma_tbl$Significance <- NA
limma_tbl[abs(limma_tbl$logFC) < 1 | limma_tbl$adj.P.Val > .05, "Significance"] <- "Not \nEnriched"
limma_tbl[limma_tbl$logFC >= 1 & limma_tbl$adj.P.Val <= .05, ][["Significance"]] <- "Disease \nUpregulated"
limma_tbl[limma_tbl$logFC <= -1 & limma_tbl$adj.P.Val <= .05, ][["Significance"]] <- "Disease \nDownregulated"
limma_tbl$Significance <- factor(limma_tbl$Significance, levels = c("Upregulated", "Downregulated", "Not \nEnriched"))
} else {
limma_tbl$Significance <- NA
limma_tbl[abs(limma_tbl$logFC) < 1 | limma_tbl$adj.P.Val > .05, "Significance"] <- "Not \nEnriched"
if(nrow(limma_tbl[limma_tbl$logFC >= 1 & limma_tbl$adj.P.Val <= .05, ]) != 0) {
limma_tbl[limma_tbl$logFC >= 1 & limma_tbl$adj.P.Val <= .05, ][["Significance"]] <- factors[1]
}
if (nrow(limma_tbl[limma_tbl$logFC <= -1 & limma_tbl$adj.P.Val <= .05, ]) != 0) {
limma_tbl[limma_tbl$logFC <= -1 & limma_tbl$adj.P.Val <= .05, ][["Significance"]] <- factors[2]
}
limma_tbl$Significance <- factor(limma_tbl$Significance, levels = c(factors, "Not \nEnriched"))
}
return(limma_tbl)
}
Combining het/wt
and ko/wt
mm_annot[["external_gene_name"]] <- mm_annot[["mgi_symbol"]]
keepergenes <- unique(c(rownames(hetkeeper_genes), rownames(kokeeper_genes)))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'unique': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'hetkeeper_genes' not found
## Error: object 'keepergenes' not found
annots_to_merge <- mm_annot %>%
select(ensembl_gene_id, external_gene_name) %>%
filter(ensembl_gene_id %in%
rownames(mm_de_normal_p8_ret$deseq$all_tables$ko_retina_vs_het_retina)) %>%
distinct()
## Error in `filter()`:
## ℹ In argument: `ensembl_gene_id %in%
## rownames(mm_de_normal_p8_ret$deseq$all_tables$ko_retina_vs_het_retina)`.
## Caused by error in `h()`:
## ! error in evaluating the argument 'table' in selecting a method for function '%in%': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'mm_de_normal_p8_ret' not found
mm_de_normal_p8_ret$deseq$all_tables$ko_retina_vs_het_retina <- merge(
mm_de_normal_p8_ret$deseq$all_tables$ko_retina_vs_het_retina, annots_to_merge,
by.x = 0, by.y = "ensembl_gene_id", all.x = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'mm_de_normal_p8_ret' not found
df <- mm_de_normal_p8_ret$deseq$all_tables$ko_retina_vs_het_retina %>%
dplyr::mutate(logFC = -logFC) %>%
set_sig_limma(factors = c("Het Enriched", "KO Enriched"))
## Error: object 'mm_de_normal_p8_ret' not found
My version of the above task makes use of the excludes option of
combine_de_tabes. Given the set of unique gene IDs increased in the
het/ko, I can ask to exlude anything not in that set. I could also have
more parsimoniously directly excluded any gene ID increased in the wt
samples. But, Theresa already provided the code to do the former, so it
will be less typing/opportunity for silly mistakes to just do that.
both_increased_genes <- unique(c(increased_het_genes, increased_ko_genes))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'unique': object 'increased_het_genes' not found
## arbitrairly grab all genes from one of my data structures.
all_genes <- rownames(exprs(mm38_hisat_v3))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'mm38_hisat_v3' not found
exclude_idx <- all_genes %in% both_increased_genes
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function '%in%': object 'all_genes' not found
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'exclude_idx' not found
exclude_increased_genes <- all_genes[exclude_idx]
## Error: object 'all_genes' not found
retina_keepers <- list(
"het_vs_wt" = c("het_retina", "wt_retina"),
"ko_vs_wt" = c("ko_retina", "wt_retina"),
"ko_vs_het" = c("ko_retina", "het_retina"))
## A reminder to myself: there is also a parameter 'wanted_genes'
## which does effectively the same thing as excludes in this context;
## excludes was originally written to allow flexible, keyword-based
## exclusion.
p8_retina_tables <- combine_de_tables(
mm_normal_p8_ret_de, keepers = retina_keepers,
wanted_genes = both_increased_genes, label_column = label_column,
excel = glue("excel/p8_retina_kept_genes_increased_in_wt_tables-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'mm_normal_p8_ret_de' not found
p8_retina_sig <- extract_significant_genes(
p8_retina_tables,
excel = glue("excel/p8_retina_kept_genes_increased_in_wt_sig-v{ver}.xlsx"),
according_to = "deseq")
## Error: object 'p8_retina_tables' not found
opposite_p8_retina_tables <- combine_de_tables(
mm_normal_p8_ret_de, keepers = retina_keepers,
excludes = both_increased_genes, label_column = label_column,
excel = glue("excel/p8_retina_removed_genes_increased_in_wt_tables-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'mm_normal_p8_ret_de' not found
opposite_p8_retina_sig <- extract_significant_genes(
p8_retina_tables,
excel = glue("excel/p8_retina_removed_genes_increased_in_wt_sig-v{ver}.xlsx"),
according_to = "deseq")
## Error: object 'p8_retina_tables' not found
Filtering out
non-specific genes and examining the results
The following is a copy/paste from Theresa containing the remaining
tasks she performed and will provide the template for implementation of
the final tasks.
This picks up with the lines from her notebook immediately following
the invocation of ‘set_sig_limma(factors = c(“Het Enriched” …’.
For all of the remaining blocks I will copy in her code, turn off its
evaluation, run the blocks manually, compare them to her notebook
output, then enable each block as I ensure I understand it.
I will likely therefore introduce some small formatting changes and
add some additional GSEA/enrichment tasks once the non-specific
filtering is complete.
df <- df %>%
filter(Row.names %in% keepergenes)
## Error in UseMethod("filter"): no applicable method for 'filter' applied to an object of class "function"
labels_ups <- df %>%
filter(adj.P.Val <= 0.05 & abs(logFC) > 1) %>%
arrange(logFC) %>%
head(n = 9)
## Error in UseMethod("filter"): no applicable method for 'filter' applied to an object of class "function"
labels_downs <- df %>%
filter(adj.P.Val <= 0.05 & abs(logFC) > 1) %>%
arrange(-logFC) %>%
head(n = 11)
## Error in UseMethod("filter"): no applicable method for 'filter' applied to an object of class "function"
labels <- rbind(labels_ups, labels_downs)
## Error: object 'labels_ups' not found
res_tbl <- df
DEplot <- ggplot(res_tbl, aes(x = logFC, y = -log10(adj.P.Val), label = external_gene_name)) +
geom_point(aes(colour = Significance), size = 4) +
geom_vline(xintercept = c(-1,1)) +
geom_hline(yintercept = -log10(.05)) +
theme_classic(base_size = 20) +
xlab("log2(FC)") +
ylab("-log10(p-value)") +
theme(legend.position = "right") +
scale_color_manual(values = c("#F8766D", "#00BFC4", "Grey")) +
geom_label_repel(
data = filter(df,
## c('s5_het_dlgn', 's5_het_ret', 's5_het_scn')),
external_gene_name %in% labels$external_gene_name),
## nudge_x = -0.5,
nudge_y = 3, max.overlaps = 15) +
xlim(c(-3, 6))
## Error in `ggplot()`:
## ! `data` cannot be a function.
## ℹ Have you misspelled the `data` argument in `ggplot()`
pp(file = "images/p08_retina_DE_1312024.pdf")
DEplot
## Error: object 'DEplot' not found
plotted <- dev.off()
DEplot
## Error: object 'DEplot' not found
writexl::write_xlsx(df, path = "excel/retinahet_vs_retinako_WTfiltered.xlsx")
## Error in loadNamespace(x): there is no package called 'writexl'
How many
ups/downs
ko_enriched <- df %>%
filter(Significance == "KO Enriched")
## Error in UseMethod("filter"): no applicable method for 'filter' applied to an object of class "function"
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'nrow': object 'ko_enriched' not found
het_enriched <- df %>%
filter(Significance == "Het Enriched")
## Error in UseMethod("filter"): no applicable method for 'filter' applied to an object of class "function"
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'nrow': object 'het_enriched' not found
category
enrichment/GSEA
regulated_genes <- res_tbl %>%
filter(adj.P.Val <= 0.05) %>%
arrange(logFC) %>%
select(Row.names, logFC, adj.P.Val, external_gene_name, Significance) %>%
filter(abs(logFC) >= 1)
## Error in UseMethod("filter"): no applicable method for 'filter' applied to an object of class "function"
## gsea_result_ko <- gost(query = ko_genes$external_gene_name,
## organism = "mmusculus",
## evcodes = TRUE,
## ordered_query = TRUE)
gsea_result_het <- gost(query = het_enriched$external_gene_name,
organism = "mmusculus",
evcodes = TRUE,
ordered_query = TRUE)
## Error: object 'het_enriched' not found
##gsea_result_alldysregulated <- gost(query = alldysregulated_genes$external_gene_name,
## organism = "mmusculus",
## evcodes = TRUE,
## ordered_query = TRUE)
I have a function in my package which seeks to make gProfiler queries
a bit more complete and easy. Let us see how similar the result is…
rownames(alldysregulated_genes) <- alldysregulated_genes[["Row.names"]]
alldysregulated_genes[["Row.names"]] <- NULL
het_gp <- simple_gprofiler(rownames(alldysregulated_genes),
species = "mmusculus",
excel = glue("excel/het_gprofiler-v{ver}.xlsx"))
het_gp
enrichplot::dotplot(het_gp[["BP_enrich"]])
gp_pair <- enrichplot::pairwise_termsim(het_gp[["BP_enrich"]])
enrichplot::emapplot(gp_pair)
enrichplot::ssplot(gp_pair)
enrichplot::treeplot(gp_pair)
upsetplot(het_gp[["BP_enrich"]])
enrichplot::dotplot(het_gp[["REAC_enrich"]])
gp_pair <- enrichplot::pairwise_termsim(het_gp[["REAC_enrich"]])
enrichplot::emapplot(gp_pair)
enrichplot::ssplot(gp_pair)
enrichplot::treeplot(gp_pair)
upsetplot(het_gp[["REAC_enrich"]])
I make a somewhat arbitrary distinction between the concepts of
over-enrichment analyses and GSEA: the former (as performed by
gprofiler) (Raudvere et al. (2019)) seeks
to find groups of genes overrepresented in GO/reactome/etc. These groups
of genes are taken exclusively from the top-n/bottom-n genes with
respect to fold-change between conditions of interest; in this case most
different than wt in the p08 retina ko or het samples.
With that in mind, I can invoke a similar function using the full
table of DE results to get what I call the GSEA result using
clusterProfiler (Yu (n.d.)). In the
following block I will use the ‘all_cprofiler’ function on the data
structures named ‘p8_retina_tables’ and ‘opposite_p8_retina_tables’ in
order to get these GSEA results for each contrast performed (het/wt,
ko/wt, het/ko). I will follow that up with ‘all_gprofiler’ which does
the same, but uses gProfiler’s enrichment analyses (it will therefore
include what we just looked at).
p08_retina_all_cp <- all_cprofiler(p8_retina_sig, p8_retina_tables, orgdb = "org.Mm.eg.db")
## Error: object 'p8_retina_sig' not found
enrichplot::dotplot(p08_retina_all_cp[["ko_vs_het_up"]][["enrich_objects"]][["MF_all"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'dotplot': object 'p08_retina_all_cp' not found
p08_topn_gsea <- plot_topn_gsea(p08_retina_all_cp)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'gse' in selecting a method for function 'plot_topn_gsea': object 'p08_retina_all_cp' not found
pp(file = "images/gsea_p08_retina_ko_vs_het_top_hit.png")
p08_topn_gsea[["GO_ko_vs_het_up"]][[1]]
## Error: object 'p08_topn_gsea' not found
plotted <- dev.off()
p08_topn_gsea[["GO_ko_vs_het_up"]][[1]]
## Error: object 'p08_topn_gsea' not found
p08_topn_gsea[["GO_ko_vs_het_up"]][[2]]
## Error: object 'p08_topn_gsea' not found
p08_topn_gsea[["GO_ko_vs_het_up"]][[3]]
## Error: object 'p08_topn_gsea' not found
p08_topn_gsea[["GO_ko_vs_het_up"]][[4]]
## Error: object 'p08_topn_gsea' not found
p08_topn_gsea[["GO_ko_vs_het_up"]][[5]]
## Error: object 'p08_topn_gsea' not found
#gsea_ko <- gsea_result_ko[["result"]] %>%
# select(term_name, p_value, term_size, intersection_size, recall, source, intersection) %>%
# arrange(desc(recall)) %>%
# head(n = 10)
# gsea_plots_ko <- ggplot(gsea_ko, aes(x = recall, y = reorder(term_name, recall), fill = p_value)) +
# geom_bar(stat = "identity")+
# scale_fill_continuous(low = "blue", high = "red") +
# theme_bw()+
# ylab("") +
# xlab("GSEA Score")
gsea_het <- gsea_result_het[["result"]] %>%
select(term_name, p_value, term_size, intersection_size, recall, source, intersection) %>%
arrange(desc(recall)) %>%
head(n = 10)
## Error: object 'gsea_result_het' not found
gsea_plots_het <- ggplot(gsea_het, aes(x = recall, y = reorder(term_name, recall), fill = p_value)) +
geom_bar(stat = "identity") +
scale_fill_continuous(low = "blue", high = "red") +
theme_bw() +
ylab("") +
xlab("Over Representation Score")
## Error: object 'gsea_het' not found
pp(file = "images/GSEA_p08_axontrap_retinahet_upregulated_vs_retinako.pdf")
gsea_plots_het
## Error: object 'gsea_plots_het' not found
gsea_all <- gsea_result_alldysregulated[["result"]] %>%
select(term_name, p_value, term_size, intersection_size, recall, source, intersection) %>%
arrange(desc(recall)) %>%
head(n = 10)
gsea_plots_all <- ggplot(gsea_all, aes(x = recall, y = reorder(term_name, recall), fill = p_value)) +
geom_bar(stat = "identity") +
scale_fill_continuous(low = "blue", high = "red") +
theme_bw() +
ylab("") +
xlab("Over Representation Score")
pp(file = "images/GSEA_p08_retina_axontrap_alldysregulatedgenes.pdf")
gsea_plots_all
dev.off()
SCN Het vs KO
It is only now that I realized we are splitting the data by location
for each set of comparisons. I think that, left to my own devices, I
would prefer to keep the input data structure intact, perform the
somewhat larger number of contrasts, and then split up the results.
Ideally this will slightly improve the fidelity of the results returned
by DESeq2 and friends. But, I will run the state of Theresa’s notebook
with as few changes as possible first, then add this.
PCA
I am going to skip this PCA plot for a couple of reasons: I already
did a superset of it, and the subset Theresa performed is not valid
given the set of samples included in my sample sheet, and figuring out
the actually corresponding subset will take me forever… In addition, I
want to use my mm38_hisat_v3 for everything…
mm38_subset <- subset_expt(
mm38_hisat,
subset = "(batch == '4' | batch == '5' | batch == '6') & time == 'p08' & location == 'scn' | sampleid == 'iprgc_03'")
mm38_norm <- normalize_expt(mm38_subset, filter = TRUE, convert = "cpm",
transform = "log2", batch = "svaseq")
mm38_norm <- set_expt_batches(mm38_norm, fact = "location")
mm38_norm <- set_expt_conditions(mm38_norm, fact = "genotype")
pca_norm <- plot_pca(mm38_norm, max_overlaps = 70)
pca_norm$plot
Instead I will simplify the subset and see what happens…
scn_samples <- subset_expt(mm38_hisat_v3,
subset = "location_atb == 'scn'") %>%
set_expt_batches(fact = "location_atb") %>%
set_expt_conditions(fact = "genotype_atb")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'mm38_hisat_v3' not found
scn_norm <- normalize_expt(scn_samples, filter = TRUE, convert = "cpm",
transform = "log2", batch = "svaseq")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'normalize_expt': object 'scn_samples' not found
scn_norm_pca <- plot_pca(scn_norm)
## Error: object 'scn_norm' not found
## Error: object 'scn_norm_pca' not found
Diverging a
little
At this point in the document I read ahead a bit and came to the
conclusion that it repeats the above logic of taking the union of wt
comparisons to remove genes from the appropriate het/ko or p15/p08 or
location comparisons. This seems quite reasonable to me, but I would
prefer to not separate all the data, so I will attempt to duplicate and
slightly streamline this logic on the full dataset. Thus I am going to
skip down to the end and attempt to implement this.
DE
mm_de_normal_p8_scn <- all_pairwise(mm38_subset, model_batch = "svaseq",
parallel = FALSE, do_ebseq = FALSE, do_basic = FALSE,
do_dream = FALSE, do_noiseq = FALSE, do_edger = FALSE,
filter = TRUE)
annots_to_merge <- mm_annot %>%
select(ensembl_gene_id, external_gene_name) %>%
filter(ensembl_gene_id %in% rownames(mm_de_normal_p8_scn$deseq$all_tables$ko_scn_vs_het_scn)) %>%
distinct()
mm_de_normal_p8_scn$deseq$all_tables$ko_scn_vs_het_scn <- merge(
mm_de_normal_p8_scn$deseq$all_tables$ko_scn_vs_het_scn,
annots_to_merge, by.x = 0, by.y = "ensembl_gene_id", all.x = TRUE)
hetkeeper_genes <- mm_de_normal_p8_scn$deseq$all_tables$wt_scn_vs_het_scn %>%
filter(logFC <= -.1 & adj.P.Val <= 0.05)
kokeeper_genes <- mm_de_normal_p8_scn$deseq$all_tables$wt_scn_vs_ko_scn %>%
filter(logFC <= -.1 & adj.P.Val <= 0.05)
keepergenes <- unique(c(rownames(hetkeeper_genes), rownames(kokeeper_genes)))
df <- mm_de_normal_p8_scn$deseq$all_tables$koscn_vs_hetscn %>%
dplyr::mutate(logFC = -logFC) %>%
set_sig_limma(factors = c("Het Enriched",
"KO Enriched"))
df <- df %>%
filter(Row.names %in% keepergenes)
labels_ups <- df %>%
filter(abs(logFC) > 1) %>%
arrange(logFC) %>%
head(n = 1)
labels_downs <- df %>%
filter(abs(logFC) > 1) %>%
arrange(-logFC) %>%
head(n = 1)
labels <- rbind(labels_ups, labels_downs)
res_tbl <- df
DEplot <- ggplot(res_tbl, aes(x = logFC, y = -log10(adj.P.Val), label = external_gene_name)) +
geom_point(aes(colour = Significance), size = 4) +
geom_vline(xintercept = c(-1, 1)) +
geom_hline(yintercept = -log10(0.05)) +
theme_classic(base_size = 20) +
xlab("log2(FC)") +
ylab("-log10(p-value)") +
## ggtitle(title, subtitle = subtitle) +
theme(legend.position="right") +
scale_color_manual(values=c("Het Enriched" = "#F8766D",
"KO Enriched" = "#00BFC4",
"Not\n Enriched" = "Grey")) +
geom_label_repel(data=filter(df,
## c('s5_het_dlgn', 's5_het_ret', 's5_het_scn')),
external_gene_name %in% labels$external_gene_name),
## nudge_x = -0.5,
nudge_y = 3, max.overlaps = 15) +
ggtitle("SCN Het vs KO Translatome")
pp(file = "images/p08_scn_DE_1312024.pdf")
DEplot
dev.off()
writexl::write_xlsx(df, path = "excel/scnhet_vs_scnko_WTfiltered.xlsx")
How many
ups/downs
ko_enriched <- df %>%
filter(Significance == "KO Enriched")
het_enriched <- df %>%
filter(Significance == "Het Enriched")
GSEA
ko_genes <- res_tbl %>%
filter(adj.P.Val <= 0.05) %>%
arrange(-abs(logFC)) %>%
select(Row.names, logFC, adj.P.Val, external_gene_name, Significance) %>%
filter(logFC <= -1)
het_genes <- res_tbl %>%
filter(adj.P.Val <= 0.05) %>%
arrange(-abs(logFC)) %>%
select(Row.names, logFC, adj.P.Val, external_gene_name, Significance) %>%
filter(logFC >= 1)
alldysregulated_genes <- res_tbl %>%
filter(adj.P.Val <= 0.05) %>%
arrange(logFC) %>%
select(Row.names, logFC, adj.P.Val, external_gene_name, Significance) %>%
filter(abs(logFC) >= 1)
gsea_result_ko <- gost(query = ko_genes$external_gene_name,
organism = "mmusculus",
evcodes = TRUE,
ordered_query = TRUE)
gsea_result_het <- gost(query = het_genes$external_gene_name,
organism = "mmusculus",
evcodes = TRUE,
ordered_query = TRUE)
gsea_result_alldysregulated <- gost(query = alldysregulated_genes$external_gene_name,
organism = "mmusculus",
evcodes = TRUE,
ordered_query = TRUE)
gsea_ko <- gsea_result_ko[["result"]] %>%
select(term_name, p_value, term_size, intersection_size, recall, source, intersection) %>%
arrange(desc(recall)) %>%
head(n = 10)
gsea_plots_ko <- ggplot(gsea_ko, aes(x = recall, y = reorder(term_name, recall), fill = p_value)) +
geom_bar(stat = "identity") +
scale_fill_continuous(low = "blue", high = "red") +
theme_bw() +
ylab("") +
xlab("Over enrichment Score")
gsea_het <- gsea_result_het[["result"]] %>%
select(term_name, p_value, term_size, intersection_size, recall, source, intersection) %>%
arrange(desc(recall)) %>%
head(n = 10)
gsea_plots_het <- ggplot(gsea_het, aes(x = recall, y = reorder(term_name, recall), fill = p_value)) +
geom_bar(stat = "identity") +
scale_fill_continuous(low = "blue", high = "red") +
theme_bw() +
ylab("") +
xlab("Over enrichment Score")
gsea_all <- gsea_result_alldysregulated[["result"]] %>%
select(term_name, p_value, term_size, intersection_size, recall, source, intersection) %>%
arrange(desc(recall)) %>%
head(n = 10)
gsea_plots_all <- ggplot(gsea_all, aes(x = recall, y = reorder(term_name, recall), fill = p_value)) +
geom_bar(stat = "identity") +
scale_fill_continuous(low = "blue", high = "red") +
theme_bw() +
ylab("") +
xlab("Over enrichment Score")
pp(file = "images/GSEA_p08_retina_axontrap_alldysregulatedgenes.pdf")
gsea_plots_all
dev.off()
Het Retina vs Het
SCN
mm38_subset2 <- subset_expt(
mm38_hisat,
subset = "(batch == '4' | batch == '5' | batch == '6') & time == 'p08' & genotype != 'ko' & location != 'dlgn' | sampleid == 'iprgc_03'")
mm38_subset2 <- subset_expt(mm38_subset2, subset = "sampleid != 'iprgc_89'")
mm38_subset2$design %>%
select(genotype, location) %>%
table()
mm38_norm2 <- normalize_expt(mm38_subset2, filter=TRUE,
convert="cpm",
transform="log2", batch = "svaseq")
PCA
mm38_norm2 <- set_expt_batches(mm38_norm2, fact = "location")
mm38_norm2 <- set_expt_conditions(mm38_norm2, fact = "genotype")
pca_norm2 <- plot_pca(mm38_norm2, max_overlaps = 70)
pca_norm2$plot
DE
mm_de_subset2 <- all_pairwise(mm38_subset2,
model_batch="svaseq",
parallel=FALSE, do_ebseq=FALSE,
do_basic = FALSE, do_dream = FALSE,
do_noiseq = FALSE, do_edger = FALSE,
filter = TRUE)
retinakeeper_genes <- mm_de_subset2$deseq$all_tables$wt_retina_vs_het_retina %>%
filter(logFC <= -0.1 & adj.P.Val <= 0.05)
scnkeeper_genes <- mm_de_subset2$deseq$all_tables$wt_scn_vs_het_scn %>%
filter(logFC <= -0.1 & adj.P.Val <= 0.05)
keepergenes <- unique(c(rownames(retinakeeper_genes), rownames(scnkeeper_genes)))
annots_to_merge <- mm_annot %>%
select(ensembl_gene_id, external_gene_name) %>%
filter(ensembl_gene_id %in% rownames(mm_de_subset2$deseq$all_tables$het_scn_vs_het_retina)) %>%
distinct()
mm_de_subset2$deseq$all_tables$het_scn_vs_het_retina <- merge(
mm_de_subset2$deseq$all_tables$het_scn_vs_het_retina,
annots_to_merge, by.x = 0,
by.y = "ensembl_gene_id", all.x = TRUE)
df <- mm_de_subset2$deseq$all_tables$het_scn_vs_het_retina %>%
mutate(Significance = case_when(logFC <= -1.0 ~ "Retina Enriched",
logFC >= 1.0 ~ "SCN Enriched",
logFC > -1.0 & logFC < 1.0 ~ "Not\n Enriched"))
df <- df %>%
filter(Row.names %in% keepergenes)
scn_enriched <- df %>%
filter(adj.P.Val <= 0.05 & logFC >= 1.0) %>%
arrange(-logFC) %>%
select(Row.names, external_gene_name, logFC, adj.P.Val) %>%
mutate(Significance = "SCN Enriched") %>%
filter(Row.names %in% rownames(scnkeeper_genes))
retina_enriched <- df %>%
filter(adj.P.Val <= 0.05 & logFC <= -1.0) %>%
arrange(logFC) %>%
select(Row.names, external_gene_name, logFC, adj.P.Val) %>%
mutate(Significance = "Retina Enriched") %>%
filter(Row.names %in% rownames(retinakeeper_genes))
notenriched <- df %>%
select(Row.names, external_gene_name, logFC, adj.P.Val, Significance) %>%
filter(Row.names %in% c(rownames(retinakeeper_genes),
rownames(scnkeeper_genes))[duplicated(c(rownames(retinakeeper_genes),
rownames(scnkeeper_genes)))]) %>%
filter(Significance == "Not\n Enriched")
df <- rbind(scn_enriched, retina_enriched, notenriched)
df <- df %>%
distinct()
## writexl::write_xlsx(df, path = "axonTRAP_DE_results_20240202/retinahet_vs_scn_het_WTfiltered.xlsx")
labels_ups <- df %>%
filter(adj.P.Val <= 0.05 & abs(logFC) > 1.0) %>%
arrange(logFC) %>%
head(n = 10)
labels_downs <- df %>%
filter(adj.P.Val <= 0.05 & abs(logFC) > 1.0) %>%
arrange(-logFC) %>%
head(n = 10)
labels <- rbind(labels_ups, labels_downs)
labels_requested <- c("Cdh10","Cdh12","Cdh13","Cdh18",
"Cdh7","Cdh8","Cdh9","Cntn3",
"Cntn4","Cntn5","Cntn6","Kirrel3",
"Nrxn1","Nrxn3","Sema3c","Sema6d",
"Tenm1","Tenm2","Tenm4")
res_tbl <- df
DEplot <- ggplot(res_tbl, aes(x = logFC, y = -log10(adj.P.Val), label = external_gene_name)) +
geom_point(aes(colour = Significance), size = 4) +
geom_vline(xintercept = c(-1, 1)) +
geom_hline(yintercept = -log10(0.05)) +
theme_classic(base_size = 20) +
xlab("log2(FC)") +
ylab("-log10(p-value)") +
## ggtitle(title, subtitle = subtitle) +
theme(legend.position="right") +
scale_color_manual(values=c("Grey", "#F8766D", "#00BFC4")) +
geom_label_repel(data=filter(df,
external_gene_name %in% labels_requested),
## c(labels$external_gene_name, "Opn4")), #c('s5_het_dlgn', 's5_het_ret', 's5_het_scn')),
## nudge_x = -0.5,
nudge_y = 15, max.overlaps = 25)
#pp(file = "axonTRAP_Volcanoplots_20240202/p08_retinavsscnhet_DE_requested_genelabels_02052024.pdf")
DEplot
#dev.off()
How many
ups/downs
scn_enriched <- df %>%
filter(adj.P.Val <= 0.05 & logFC >= 1.0) %>%
arrange(-logFC) %>%
select(Row.names, external_gene_name, logFC, adj.P.Val, Significance)
retina_enriched <- df %>%
filter(adj.P.Val <= 0.05 & logFC <= -1.0) %>%
arrange(logFC) %>%
select(Row.names, external_gene_name, logFC, adj.P.Val, Significance)
scn_enriched
retina_enriched
df %>%
filter(Significance == "Not\n Enriched")
GSEA
gsea_result_scn <- gost(query = scn_enriched$external_gene_name,
organism = "mmusculus", evcodes = TRUE,
ordered_query = TRUE, source = c("GO"))
gsea_result_ret <- gost(query = retina_enriched$external_gene_name,
organism = "mmusculus", evcodes = TRUE,
ordered_query = TRUE, source = c("GO"))
gsea_scn <- gsea_result_scn[["result"]] %>%
select(term_name, p_value, term_size, intersection_size, recall, source, intersection) %>%
arrange(desc(recall)) %>%
head(n = 20)
gsea_plots_scn <- ggplot(gsea_scn, aes(x = recall, y = reorder(term_name, recall), fill = p_value)) +
geom_bar(stat = "identity") +
scale_fill_continuous(low = "blue", high = "red") +
theme_bw() +
ylab("") +
xlab("Over enrichment Score")
pp(file = "images/GSEA_SCNhet_vs_retina_enriched_P08.pdf")
gsea_plots_scn
dev.off()
gsea_ret <- gsea_result_ret[["result"]] %>%
select(term_name, p_value, term_size, intersection_size, recall, source, intersection) %>%
arrange(desc(recall)) %>%
head(n = 20)
gsea_plots_ret <- ggplot(gsea_ret, aes(x = recall, y = reorder(term_name, recall), fill = p_value)) +
geom_bar(stat = "identity") +
scale_fill_continuous(low = "blue", high = "red") +
theme_bw() +
ylab("") +
xlab("Over enrichment Score")
pp(file = "images/GSEA_Retinahet_vs_SCN_enriched_P08.pdf")
gsea_plots_ret
dev.off()
KO Retina vs KO
SCN
mm38_subset3 <- subset_expt(
mm38_hisat,
subset = "(batch == '4' | batch == '5' | batch == '6') & time == 'p08' & genotype != 'het' & location != 'dlgn' | sampleid == 'iprgc_03'")
mm38_subset3 <- subset_expt(mm38_subset3, subset = "sampleid != 'iprgc_86'")
mm38_subset3$design %>%
select(genotype, location) %>%
table()
mm38_norm3 <- normalize_expt(mm38_subset3, filter=TRUE,
convert="cpm", transform="log2", batch = "svaseq")
PCA
mm38_norm3 <- set_expt_batches(mm38_norm3, fact = "location")
mm38_norm3 <- set_expt_conditions(mm38_norm3, fact = "genotype")
pca_norm3 <- plot_pca(mm38_norm3, max_overlaps = 70)
pca_norm3$plot
DE
mm_de_subset3 <- all_pairwise(mm38_subset3,
model_batch="svaseq",
parallel=FALSE, do_ebseq=FALSE,
do_basic = FALSE, do_dream = FALSE,
do_noiseq = FALSE, do_edger = FALSE,
filter = TRUE)
retinakeeper_genes <- mm_de_subset3$deseq$all_tables$wtretina_vs_koretina %>%
filter(logFC <= -1.0 & adj.P.Val <= 0.05)
scnkeeper_genes <- mm_de_subset3$deseq$all_tables$wtscn_vs_koscn %>%
filter(logFC <= -1.0 & adj.P.Val <= 0.05)
keepergenes <- unique(c(rownames(retinakeeper_genes), rownames(scnkeeper_genes)))
annots_to_merge <- mm_annot %>%
select(ensembl_gene_id, external_gene_name) %>%
filter(ensembl_gene_id %in% rownames(mm_de_subset3$deseq$all_tables$ko_scn_vs_ko_retina)) %>%
distinct()
mm_de_subset3$deseq$all_tables$ko_scn_vs_ko_retina <- merge(
mm_de_subset3$deseq$all_tables$ko_scn_vs_ko_retina,
annots_to_merge, by.x = 0,
by.y = "ensembl_gene_id", all.x = TRUE)
df <- mm_de_subset3$deseq$all_tables$ko_scn_vs_ko_retina %>%
mutate(Significance = case_when(logFC <= -1 ~ "Retina Enriched",
logFC >= 1 ~ "SCN Enriched",
logFC > -1 & logFC < 1 ~ "Not\n Enriched"))
df <- df %>%
filter(Row.names %in% keepergenes)
scn_enriched <- df %>%
filter(adj.P.Val <= 0.05 & logFC >= 1) %>%
arrange(-logFC) %>%
select(Row.names, external_gene_name, logFC, adj.P.Val) %>%
mutate(Significance = "SCN Enriched") %>%
filter(Row.names %in% rownames(scnkeeper_genes))
df %>%
filter(adj.P.Val <= 0.05 & logFC <= -1) %>%
arrange(logFC) %>%
select(Row.names, external_gene_name, logFC, adj.P.Val) %>%
mutate(Significance = "Retina Enriched") %>%
filter(Row.names %in% rownames(retinakeeper_genes)) -> retina_enriched
notenriched <- df %>%
select(Row.names, external_gene_name, logFC, adj.P.Val, Significance) %>%
filter(Row.names %in% c(rownames(retinakeeper_genes),
rownames(scnkeeper_genes))[duplicated(c(rownames(retinakeeper_genes),
rownames(scnkeeper_genes)))])
df <- rbind(scn_enriched, retina_enriched, notenriched)
labels_ups <- df %>%
filter(adj.P.Val <= 0.05 & abs(logFC) > 1) %>%
arrange(logFC) %>%
head(n = 10)
labels_downs <- df %>%
filter(adj.P.Val <= 0.05 & abs(logFC) > 1) %>%
arrange(-logFC) %>%
head(n = 10)
labels <- rbind(labels_ups, labels_downs)
## wanted_column <- "Significance"
res_tbl <- df
DEplot <- ggplot(res_tbl, aes(x = logFC, y = -log10(adj.P.Val), label = external_gene_name)) +
geom_point(aes(colour = Significance), size = 4) +
## geom_point(aes(colour = !!sym(wanted_column)), size = 4) +
geom_vline(xintercept = c(-1, 1)) +
geom_hline(yintercept = -log10(0.05)) +
theme_classic(base_size = 20) +
xlab("log2(FC)") +
ylab("-log10(p-value)") +
## ggtitle(title, subtitle = subtitle) +
theme(legend.position = "right") +
scale_color_manual(values = c("Grey", "#F8766D", "#00BFC4")) +
geom_label_repel(data = filter(
df, external_gene_name %in% c(labels$external_gene_name, "Opn4")),
## c('s5_het_dlgn', 's5_het_ret', 's5_het_scn')),
## nudge_x = -0.5,
nudge_y = 10, max.overlaps = 25)
pp(file = "images/p08_retinavsscnko_DE_1312024.pdf")
DEplot
dev.off()
How many
ups/downs
scn_enriched
retina_enriched
notenriched %>%
filter(Significance == "Not\n Enriched")
GSEA
gsea_result_scn <- gost(query = scn_enriched$external_gene_name,
organism = "mmusculus",
evcodes = TRUE,
ordered_query = TRUE,
source = c("GO"))
gsea_result_ret <- gost(query = retina_enriched$external_gene_name,
organism = "mmusculus",
evcodes = TRUE,
ordered_query = TRUE,
source = c("GO"))
gsea_scn <- gsea_result_scn[["result"]] %>%
select(term_name, p_value, term_size, intersection_size, recall, source, intersection) %>%
arrange(desc(recall)) %>%
head(n = 20)
gsea_plots_scn <- ggplot(gsea_scn, aes(x = recall, y = reorder(term_name, recall), fill = p_value)) +
geom_bar(stat = "identity") +
scale_fill_continuous(low = "blue", high = "red") +
theme_bw() +
ylab("") +
xlab("GSEA Score")
pp(file = "images/GSEA_SCNko_enriched_vs_retina_P08.pdf")
gsea_plots_scn
dev.off()
gsea_ret <- gsea_result_ret[["result"]] %>%
select(term_name, p_value, term_size, intersection_size, recall, source, intersection) %>%
arrange(desc(recall)) %>%
head(n = 20)
gsea_plots_ret <- ggplot(gsea_ret, aes(x = recall, y = reorder(term_name, recall), fill = p_value)) +
geom_bar(stat = "identity") +
scale_fill_continuous(low = "blue", high = "red") +
theme_bw() +
ylab("") +
xlab("GSEA Score")
pp(file = "images/GSEA_Retinako_enriched_vs_SCN_P08.pdf")
gsea_plots_ret
dev.off()
My version of the
global analysis
I want to have an invocation of all_pairwise() which uses all
samples, in the following block I will set that up using a set of
‘keepers’ which will be named by time, location, then 2 letters for the
numerator/denominator: w for WT, h for het, d for delta; thus
“p08_retina_hw” is comparing the het/wt for the p08 retina samples.
If they are of interest, I will have a separate set which follows the
same convention with names like “p08_ko_sr” to compare p08 deltas with
SCN as the numerator and retina as the denominator.
Set up the exclusion
dataset
The most peculiar aspect of this analysis resides in the choices
around choosing which genes to consider when comparing the
genotypes/locations/times. The general idea is pretty clear: find the
genes which are non-specifically being pulled down in the WT samples and
either exclude or discount them. The various potential methods for
performing this are confusing:
- Which set of comparisons of wt/ko wt/het do we use to
exclude/discount genes?
- Should it be a combination of all samples wt vs. x?
- Should it be only the ‘relevant’ comparison, e.g. if we are
comparing p08_dlgn_het vs. p08_scn_het; do we remove genes observed in
(p08_dlgn_het/wt && p08_scn_het/wt)
- Do we instead attempt to use this x/wt information to normalize the
expression values in the other conditions and keep those genes?
Theresa’s current worksheet implements a version of 1b in which she
separated the various input gene sets to define the exclusion genes. I
am going to repeat this, but leave the starting data structure
intact.
In this first iteration, I will do that by creating a simplified
model of the data which combines the time/genotype/location and using
sva. In my next iteration I will use a full statistical model containing
each of those factors (and probably also using sva).
Note: my color choices are kind of garbage.
In addition, the exclusion dataset is the same as the analysis
dataset, it is really only the contrasts which will be different.
v3_pairwise_input <- set_expt_conditions(mm38_hisat_v3, fact = "time_geno_loc",
colors = color_choices[["all"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'mm38_hisat_v3' not found
Set up the
contrasts
In the following few blocks I will set up the various comparisons of
interest. Starting with the set of genes to exclude because they were
observed to bind non-specifically in the wt samples.
Inclusion
contrasts
In each exclusion I will have the contrast first followed by the pair
of contrasts which will be used to define the gene set to exclude.
- p15_het_dlgn/p08_het_dlgn: p15_wt_dlgn/p15_het_dlgn,
p08_wt_dlgn/p08_het_dlgn; remove the genes increased in wt.
- p15_ko_scn/p08_ko_scn: p15_wt_scn/p15_ko_scn,
p08_wt_scn/p15_ko_scn
- p15_het_retina/p08_het_retina: I think you get it, wt/het for both
p15 retinas and p08 retinas…
Put slightly differently, for every term of interest I will create a
contrast with the wt as numerator and the desired term as denominator,
then pull out the genes increased in wt.
inclusions <- list(
## I like alphabetizing things, start with dlgn
"p15_het_dlgn" = c("p15_het_dlgn", "p15_wt_dlgn"),
"p08_het_dlgn" = c("p08_het_dlgn", "p08_wt_dlgn"),
"p15_ko_dlgn" = c("p15_ko_dlgn", "p15_wt_dlgn"),
"p08_ko_dlgn" = c("p08_ko_dlgn", "p08_wt_dlgn"),
## Then retinas
"p15_het_retina" = c("p15_het_retina", "p15_wt_retina"),
"p08_het_retina" = c("p08_het_retina", "p08_wt_retina"),
"p15_ko_retina" = c("p15_ko_retina", "p15_wt_retina"),
"p08_ko_retina" = c("p08_ko_retina", "p08_wt_retina"),
## Then scn
"p15_het_scn" = c("p15_het_scn", "p15_wt_scn"),
"p08_het_scn" = c("p08_het_scn", "p08_wt_scn"),
"p15_ko_scn" = c("p15_ko_scn", "p15_wt_scn"),
"p08_ko_scn" = c("p08_ko_scn", "p08_wt_scn"))
Time
contrasts
For each location/genotype of interest, let us compare p15/p08
time_keepers <- list(
## DLGN
"t_het_dlgn" = c("p15_het_dlgn", "p08_het_dlgn"),
"t_ko_dlgn" = c("p15_ko_dlgn", "p08_ko_dlgn"),
## Retina
"t_het_retina" = c("p15_het_retina", "p08_het_retina"),
"t_ko_retina" = c("p15_ko_retina", "p08_ko_retina"),
## SCN
"t_het_scn" = c("p15_het_scn", "p08_het_scn"),
"t_ko_scn" = c("p15_ko_scn", "p08_ko_scn"))
Location
contrasts
Compare locations and keep time/genotype consistent. I will use the
location initials to define numerator/denominator.
location_keepers <- list(
## dlgn/retina
"dr_p08_het" = c("p08_het_dlgn", "p08_het_retina"),
"dr_p15_het" = c("p15_het_dlgn", "p15_het_retina"),
"dr_p08_ko" = c("p08_ko_dlgn", "p08_ko_retina"),
"dr_p15_ko" = c("p15_ko_dlgn", "p15_ko_retina"),
## scn/retina
"sr_p08_het" = c("p08_het_scn", "p08_het_retina"),
"sr_p15_het" = c("p15_het_scn", "p15_het_retina"),
"sr_p08_ko" = c("p08_ko_scn", "p08_ko_retina"),
"sr_p15_ko" = c("p15_ko_scn", "p15_ko_retina"),
## dlgn/scn
"ds_p08_het" = c("p08_het_dlgn", "p08_het_scn"),
"ds_p15_het" = c("p15_het_dlgn", "p15_het_scn"),
"ds_p08_ko" = c("p08_ko_dlgn", "p08_ko_scn"),
"ds_p15_ko" = c("p15_ko_dlgn", "p15_ko_scn"))
Genotype
contrasts
Compare ko/het while keeping time/location constant. Similarly, use
the initials to denote numerator/denominator, which will always be
kh.
genotype_keepers <- list(
## DLGN
"kh_p08_dlgn" = c("p08_ko_dlgn", "p08_het_dlgn"),
"kh_p15_dlgn" = c("p15_ko_dlgn", "p15_het_dlgn"),
## Retina
"kh_p08_retina" = c("p08_ko_retina", "p08_het_retina"),
"kh_p15_retina" = c("p15_ko_retina", "p15_het_retina"),
## SCN
"kh_p08_scn" = c("p08_ko_scn", "p08_het_scn"),
"kh_p15_scn" = c("p15_ko_scn", "p15_het_scn"))
Perform the
exclusion comparison
My all_pairwise() function now has a parameter which allows me to
choose which contrasts to perform instead of literally doing every
possible comparison. That is well suited for these operations:
In a container, the following appears to fail with:
“error code 1 from Lapack routine ‘dgesdd’”
Running it manually outside the container results in it working
without error. I assume therefore that the problem lies in the
compilation flags of LAPACK in the container.
lfc_cutoff <- 0.1
adjp_cutoff <- 0.1
inclusion_de <- all_pairwise(v3_pairwise_input, filter = "simple",
keepers = inclusions, model_batch = "svaseq")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'v3_pairwise_input' not found
## Error: object 'inclusion_de' not found
inclusion_tables <- combine_de_tables(
inclusion_de, keepers = inclusions, label_column = label_column,
excel = glue("wt_comparisons/inclusion_tables-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'inclusion_de' not found
## Error: object 'inclusion_tables' not found
inclusion_sig <- extract_significant_genes(
inclusion_tables, lfc = lfc_cutoff, p = adjp_cutoff,
according_to = "deseq",
excel = glue("wt_comparisons/inclusion_sig-v{ver}.xlsx"))
## Error: object 'inclusion_tables' not found
## Error: object 'inclusion_sig' not found
inclusion_upsets <- upsetr_sig(inclusion_sig)
## Error: object 'inclusion_sig' not found
inclusion_intersects <- write_upset_groups(inclusion_upsets, excel = "excel/inclusion_gene_groups.xlsx")
## Error: object 'inclusion_upsets' not found
Check vs Theresa’s
filter
Up above Theresa performed a 0.25 log2FC and 0.05 adjp filter which
provided a set of 2,640 genes observed higher in the p08 het retinas
vs. wt retinas. I should see that in this inclusion_sig data
structure.
There is an important caveat though: in Theresa’s filter above, she
did a DE of only the retina samples but I did all samples. I
expected that this would result in basically the same result (I actually
assumed I would get a few more genes), but instead it appears to have
retrieved a significantly smaller number of genes (about 1/2, happily
they pretty much all appear in the previous filter). As a result, I am
going to try relaxing my constraints slightly to see if I can
recapitulate her filter (which would match Theresa’s later filter,
though I guess that in turn will lead to a smaller set of genes compared
to her later, relaxed 0.1 filter).
comparison <- inclusion_sig[["deseq"]][["ups"]][["p08_het_retina"]]
## Error: object 'inclusion_sig' not found
comp <- list(
"taa" = taa_keepers,
"new" = rownames(comparison))
## Error: object 'taa_keepers' not found
test_comparison <- Vennerable::Venn(comp)
## Error in loadNamespace(x): there is no package called 'Vennerable'
Vennerable::plot(test_comparison)
## Error in loadNamespace(x): there is no package called 'Vennerable'
I want to have a little function which, given a contrast of interest,
will extract the gene sets which should be included/excluded given the
above.
write_all_cp <- function(all_cp) {
all_written <- list()
for (g in seq_len(length(all_cp))) {
name <- names(all_cp)[g]
datum <- all_cp[[name]]
filename <- glue("cprofiler/{ver}/{name}_cprofiler-v{ver}.xlsx")
written <- sm(write_cp_data(datum, excel = filename))
all_written[[g]] <- written
}
return(all_written)
}
write_all_gp <- function(all_gp) {
all_written <- list()
for (g in seq_len(length(all_gp))) {
name <- names(all_gp)[g]
datum <- all_gp[[name]]
filename <- glue("gprofiler/{ver}/{name}_gprofiler-v{ver}.xlsx")
written <- sm(write_gprofiler_data(datum, excel = filename))
all_written[[g]] <- written
}
return(all_written)
}
extract_inclusions <- function(inclusion_sig, inclusion_tables, inclusions, keepers, all_genes,
according_to = "deseq", which = "ups") {
retlist <- list()
table_names <- names(inclusion_sig[[according_to]][[which]])
for (c_num in seq_along(keepers)) {
contrast <- names(keepers)[c_num]
numerator_name <- keepers[[c_num]][1]
denominator_name <- keepers[[c_num]][2]
## In my new branch I cleaned up the sanitizer function for contrasts so this is not needed.
## The following two lines are no longer needed because of the cleanups I performed.
##numerator_name <- gsub(x = numerator_name, pattern = "(het|ko|wt)", replacement = "_\\1_")
##denominator_name <- gsub(x = denominator_name, pattern = "(het|ko|wt)", replacement = "_\\1_")
numerator_table <- inclusion_sig[[according_to]][[which]][[numerator_name]]
numerator_genes <- rownames(numerator_table)
denominator_table <- inclusion_sig[[according_to]][[which]][[denominator_name]]
denominator_genes <- rownames(denominator_table)
df_columns <- paste0("deseq_", c("logfc", "adjp", "den"))
included_num <- inclusion_tables[["data"]][[numerator_name]][, df_columns]
colnames(included_num) <- c("numerator_vs_wt_logfc", "numerator_vs_wt_adjp", "num_wt_mean_exprs")
included_den <- inclusion_tables[["data"]][[denominator_name]][, df_columns]
colnames(included_den) <- c("denominator_vs_wt_logfc", "denominator_vs_wt_adjp", "den_wt_mean_exprs")
included_df <- merge(included_num, included_den, by = "row.names")
rownames(included_df) <- included_df[["Row.names"]]
included_df[["Row.names"]] <- NULL
include_genes <- unique(c(numerator_genes, denominator_genes))
message("The set of unique genes higher in ", numerator_name,
" vs. wt is ", length(numerator_genes), ".")
message("The set of unique genes higher in ", denominator_name,
" vs. wt is ", length(denominator_genes), ".")
message("The unique union of them is ", length(include_genes), " genes.")
include_name <- paste0("inc_", contrast)
include_idx <- all_genes %in% include_genes
include_genes <- all_genes[include_idx]
df_name <- paste0("df_", contrast)
retlist[[df_name]] <- included_df
written_inclusion <- write_xlsx(data = included_df,
excel = glue("included_genes/{include_name}-v{ver}.xlsx"))
retlist[[include_name]] <- include_genes
retlist[[contrast]] <- include_genes
}
return(retlist)
}
Extract the relevant
tables and include genes lower in wt
Genotype
contrasts
I will start with the tables and no inclusions so I can check my
work.
In this first block I will explain a little more thoroughly what is
going on:
- Dump the full table of the contrasts I defined above comparing the 3
genotypes across time/location.
- Iterate over each of those contrasts and do the following:
- Extract the name of the contrast, ‘kh_p08_dlgn’ for example
- Yank out that specific entry from the keeper list and its name
- Yank out the corresponding set of genes to include from the
inclusions data structure.
- Create a filename given the name in (a) above and the logFC cutoff
chosen for the inclusions (I am assuming we may change this)
- Given (b), (c), and (d), extract the corresponding table from the
differential expression analysis and include the appropriate genes.
genotype_tables_full <- combine_de_tables(
genotype_de, keepers = genotype_keepers, label_column = label_column,
fancy = TRUE,
excel = glue("full_contrasts/genotype_full_tables-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'genotype_de' not found
## Error: object 'genotype_tables_full' not found
genotype_sig_full <- extract_significant_genes(
genotype_tables_full, according_to = "deseq",
excel = glue("full_contrasts/genotype_full_sig-v{ver}.xlsx"))
## Error: object 'genotype_tables_full' not found
## Error: object 'genotype_sig_full' not found
genotype_full_gp <- all_gprofiler(genotype_sig_full, species = "mmusculus",
excel = "excel/all_gprofiler_genotype_full.xlsx")
## Error: object 'genotype_sig_full' not found
genotype_full_cp <- all_cprofiler(genotype_sig_full, genotype_tables_full,
orgdb = "org.Mm.eg.db",
excel = "excel/all_cprofiler_genotype_full.xlsx")
## Error: object 'genotype_sig_full' not found
genotype_full_upset <- upsetr_sig(genotype_sig_full)
## Error: object 'genotype_sig_full' not found
genotype_full_intersects <- write_upset_groups(genotype_full_upset,
excel = "excel/genotype_full_gene_groups.xlsx")
## Error: object 'genotype_full_upset' not found
genotype_tables <- list()
genotype_sig <- list()
genotype_gp <- list()
genotype_cp <- list()
for (k in seq_along(genotype_keepers)) {
name <- names(genotype_keepers)[k]
message("Examining ", name)
keeper <- genotype_keepers[name]
include_name <- paste0("inc_", name)
include_df_name <- paste0("df_", name)
include_df <- genotype_inclusions[[include_df_name]]
includes <- genotype_inclusions[[include_name]]
summary(rownames(genotype_sig_full[["deseq"]][["ups"]][[name]]) %in% includes)
include_filename <- glue("genotype_contrasts/genotype_{name}_including_wt_{lfc_cutoff}_decreased_table-v{ver}.xlsx")
include_sig_filename <- glue("genotype_contrasts/genotype_{name}_including_wt_{lfc_cutoff}_decreased_sig-v{ver}.xlsx")
genotype_tables[[name]] <- combine_de_tables(
genotype_de, extra_annot = include_df,
keepers = keeper, label_column = label_column,
excel = include_filename, wanted_genes = includes)
print(genotype_tables[[name]])
genotype_sig[[name]] <- extract_significant_genes(
genotype_tables[[name]], according_to = "deseq",
excel = include_sig_filename)
print(genotype_sig[[name]])
num_rows <- nrow(genotype_sig[[name]][["deseq"]][["ups"]][[name]]) +
nrow(genotype_sig[[name]][["deseq"]][["downs"]][[name]])
message("There are ", num_rows, " significant up and down genes.")
if (num_rows >= 10) {
message("Performing gprofiler/clusterProfiler.")
genotype_gp[[name]] <- all_gprofiler(genotype_sig[[name]], species = "mmusculus")
gp_written <- write_all_gp(genotype_gp[[name]])
genotype_cp[[name]] <- all_cprofiler(genotype_sig[[name]], genotype_tables[[name]],
orgdb = "org.Mm.eg.db")
cp_written <- write_all_cp(genotype_cp[[name]])
} else {
warning("There are less than 10 genes up and down in the ", name, " comparison.")
message("There are less than 10 genes up and down in the ", name, " comparison.")
}
}
## Examining kh_p08_dlgn
## Error: object 'genotype_inclusions' not found
A few specific plots of interest: Colenso asked to label a few genes
for the knockout/het p08_retinas, p08_scn, and p08_dlgn: either the
top-15 or all significant. I am pretty sure if I tell it 15 and there
are not that many, it will just do the significant? Let us find out!
ko/het for p08
retinas
For some crazy reason, this plot is double-labelling!
table_name <- "kh_p08_retina"
table_input <- genotype_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
interesting <- c("Opn4", "Gm9008", "Lrr1", "Cnbd1")
kh_p08_retina_volcano <- plot_volcano_condition_de(
table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp", fill = "black",
color_low = colors[["ko_retina"]], color_high = colors[["het_retina"]],
label_column = "mgi_symbol", label = interesting, alpha = 1.0,
size = 4, label_type = "label")
## Error in plot_volcano_condition_de(table, table_name, fc_col = "deseq_logfc", : Column: deseq_logfc is not in the table.
pp(file = "images/kh_p08_retina_volcano.pdf", width = 9, height = 9)
kh_p08_retina_volcano[["plot"]]
## Error: object 'kh_p08_retina_volcano' not found
## png
## 2
kh_p08_retina_volcano[["plot"]]
## Error: object 'kh_p08_retina_volcano' not found
## why in the crap is it double-labelling!?
## My MA plotter isn't as smart as the volcano plotter, the genes are:
kh_p08_retina_ma <- plot_ma_condition_de(
table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
color_low = colors[["ko_retina"]], color_high = colors[["het_retina"]],
p_col = "deseq_adjp", label_column = "mgi_symbol", label = interesting)
## The column: mgi_symbol is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean", : NAs
## introduced by coercion
## Error in `[[<-.data.frame`(`*tmp*`, "pval", value = NA_real_): replacement has 1 row, data has 0
pp(file = "images/kh_p08_retina_ma.pdf", width = 9, height = 9)
kh_p08_retina_ma[["plot"]]
## Error: object 'kh_p08_retina_ma' not found
## png
## 2
kh_p08_retina_ma[["plot"]]
## Error: object 'kh_p08_retina_ma' not found
ko/het p08
SCN
Holy crappers, this plot did not double label; oooh I have a
check in my plotter to see if there are too few/too many labels and I
foolishly allowed it to concatenate the labels! What in the crap was I
thinking?
I am going to make an executive decision for this plot, 15 is too
many and makes it crazy cluttered.
Repeat this with
two sets of genes
table_name <- "kh_p08_scn"
table_input <- genotype_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
interesting_genes <- c("Fign", "Nrn1", "Dpysl2", "Actb", "Fgf9", "Otx2", "Sec23",
"Ncam1", "Map4", "Sec22b", "Nlgn3", "Marcks", "Cd47",
"Dpysl3", "Lin7c", "Cadm1", "Snx12", "Rhoa", "Inpp5f",
"Atg12", "Set", "Gsk3b", "Pdcd4", "Gabra2", "Tmco1", "Anapc16")
kh_p08_scn_volcano <- plot_volcano_condition_de(
table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
label_column = "mgi_symbol", label = interesting_genes, size = 4, alpha = 1.0,
color_low = colors[["ko_scn"]], color_high = colors[["het_scn"]],
label_type = "label")
## Error in plot_volcano_condition_de(table, table_name, fc_col = "deseq_logfc", : Column: deseq_logfc is not in the table.
pp(file = "images/kh_p08_scn_volcano.pdf", width = 9, height = 9)
kh_p08_scn_volcano[["plot"]]
## Error: object 'kh_p08_scn_volcano' not found
## png
## 2
kh_p08_scn_volcano[["plot"]]
## Error: object 'kh_p08_scn_volcano' not found
## why in the crap is it double-labelling!?
## My MA plotter isn't as smart as the volcano plotter, the genes are:
kh_p08_scn_ma <- plot_ma_condition_de(
table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
color_low = colors[["ko_scn"]], color_high = colors[["het_scn"]],
p_col = "deseq_adjp", label_column = "mgi_symbol", label = interesting_genes)
## The column: mgi_symbol is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean", : NAs
## introduced by coercion
## Error in `[[<-.data.frame`(`*tmp*`, "pval", value = NA_real_): replacement has 1 row, data has 0
pp(file = "images/kh_p08_scn_ma.pdf", width = 9, height = 9)
kh_p08_scn_ma[["plot"]]
## Error: object 'kh_p08_scn_ma' not found
## png
## 2
## Error: object 'kh_p08_scn_ma' not found
table_name <- "kh_p08_scn"
table_input <- genotype_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
interesting_genes <- c(
"Anapc16", "Gabra2", "Tmco1", "Sod2", "Fgf9", "Pdcd4", "Rhoa", "Gsk3b", "Foxp1",
"Ncam1", "Marcks", "Fign", "Dpysl3", "Inpp5f", "Cadm1", "Map4", "Ugcg", "Elovl4",
"Elavl1", "Cfl2", "Tnnt1", "Gnb1", "Impact", "Nrn1", "Nlgn3", "Actb", "Cd47",
"Sec22b", "Slc17a7", "Vglut1", "Actb", "B4galt5", "Foxp1", "Otx2", "Lin7c",
"Snx12", "Atg12", "Set")
kh_p08_scn_volcano <- plot_volcano_condition_de(
table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
color_low = colors[["ko_scn"]], color_high = colors[["het_scn"]],
label_column = "mgi_symbol", label = interesting_genes, size = 4, alpha = 1.0,
label_type = "repel", nudge_x = -1, nudge_y = 0)
## Error in plot_volcano_condition_de(table, table_name, fc_col = "deseq_logfc", : Column: deseq_logfc is not in the table.
pp(file = "images/kh_p08_scn_volcano_v2.pdf", width = 9, height = 9)
kh_p08_scn_volcano[["plot"]]
## Error: object 'kh_p08_scn_volcano' not found
## png
## 2
kh_p08_scn_volcano[["plot"]]
## Error: object 'kh_p08_scn_volcano' not found
## why in the crap is it double-labelling!?
## My MA plotter isn't as smart as the volcano plotter, the genes are:
kh_p08_scn_ma <- plot_ma_condition_de(
table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
color_low = colors[["ko_scn"]], color_high = colors[["het_scn"]],
p_col = "deseq_adjp", label_column = "mgi_symbol", label = interesting_genes)
## The column: mgi_symbol is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean", : NAs
## introduced by coercion
## Error in `[[<-.data.frame`(`*tmp*`, "pval", value = NA_real_): replacement has 1 row, data has 0
pp(file = "images/kh_p08_scn_ma_v2.pdf", width = 9, height = 9)
kh_p08_scn_ma[["plot"]]
## Error: object 'kh_p08_scn_ma' not found
## png
## 2
## Error: object 'kh_p08_scn_ma' not found
ko/het p08
dLGN
table_name <- "kh_p08_dlgn"
table_input <- genotype_tables[[table_name]]
table <- table_input[["data"]][[table_name]]
kh_p08_dlgn_volcano <- plot_volcano_condition_de(
table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
color_low = colors[["ko_dlgn"]], color_high = colors[["het_dlgn"]],
label_column = "mgi_symbol", label = 10, size = 4, alpha = 1.0,
label_type = "label")
## Error in plot_volcano_condition_de(table, table_name, fc_col = "deseq_logfc", : Column: deseq_logfc is not in the table.
pp(file = "images/kh_p08_dlgn_volcano.pdf", width = 9, height = 9)
kh_p08_dlgn_volcano[["plot"]]
## Error: object 'kh_p08_dlgn_volcano' not found
## png
## 2
kh_p08_dlgn_volcano[["plot"]]
## Error: object 'kh_p08_dlgn_volcano' not found
## My MA plotter isn't as smart as the volcano plotter, the genes are:
kh_p08_dlgn_ma <- plot_ma_condition_de(
table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
color_low = colors[["ko_dlgn"]], color_high = colors[["het_dlgn"]],
p_col = "deseq_adjp", label_column = "mgi_symbol", label = 10)
## The column: mgi_symbol is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean", : NAs
## introduced by coercion
## Error in `[[<-.data.frame`(`*tmp*`, "pval", value = NA_real_): replacement has 1 row, data has 0
pp(file = "images/kh_p08_dlgn_ma.pdf", width = 9, height = 9)
kh_p08_dlgn_ma[["plot"]]
## Error: object 'kh_p08_dlgn_ma' not found
## png
## 2
## Error: object 'kh_p08_dlgn_ma' not found
Location contrasts
with genes removed/kept
Repeat the same block with a find/replace of genotype/location.
location_tables_full <- combine_de_tables(
location_de, keepers = location_keepers, label_column = label_column,
excel = glue("full_contrasts/location_full_tables-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'location_de' not found
## Error: object 'location_tables_full' not found
location_sig_full <- extract_significant_genes(
location_tables_full, according_to = "deseq",
excel = glue("full_contrasts/location_full_sig-v{ver}.xlsx"))
## Error: object 'location_tables_full' not found
## Error: object 'location_sig_full' not found
location_full_upset <- upsetr_sig(location_sig_full)
## Error: object 'location_sig_full' not found
##location_full_intersects <- write_upset_groups(
## location_full_upset,
## excel = "excel/location_full_gene_groups.xlsx")
location_tables <- list()
location_sig <- list()
location_gp <- list()
location_cp <- list()
for (k in seq_along(location_keepers)) {
name <- names(location_keepers)[k]
message("Examining ", name)
keeper <- location_keepers[name]
includes <- location_inclusions[[name]]
include_name <- paste0("inc_", name)
include_df_name <- paste0("df_", name)
include_df <- location_inclusions[[include_df_name]]
includes <- location_inclusions[[include_name]]
summary(rownames(location_sig_full[["deseq"]][["ups"]][[name]]) %in% includes)
include_filename <- glue("location_contrasts/location_{name}_including_wt_{lfc_cutoff}_decreased_table-v{ver}.xlsx")
include_sig_filename <- glue("location_contrasts/location_{name}_including_wt_{lfc_cutoff}_decreased_sig-v{ver}.xlsx")
location_tables[[name]] <- combine_de_tables(
location_de, extra_annot = include_df,
keepers = keeper, label_column = label_column,
excel = include_filename, wanted_genes = includes)
print(location_tables[[name]])
location_sig[[name]] <- extract_significant_genes(
location_tables[[name]], according_to = "deseq",
excel = include_sig_filename)
print(location_sig[[name]])
num_rows <- nrow(location_sig[[name]][["deseq"]][["ups"]][[name]]) +
nrow(location_sig[[name]][["deseq"]][["downs"]][[name]])
message("There are ", num_rows, " significant up and down genes.")
if (num_rows > 10) {
location_gp[[name]] <- all_gprofiler(location_sig[[name]], species = "mmusculus")
gp_written <- write_all_gp(genotype_gp[[name]])
location_cp[[name]] <- all_cprofiler(location_sig[[name]], location_tables[[name]],
orgdb = "org.Mm.eg.db")
cp_written <- write_all_cp(genotype_cp[[name]])
}
}
## Examining dr_p08_het
## Error: object 'location_inclusions' not found
Colenso sent a specific query of interest, comparing SCN vs. Retinas
at p08 in the heterozygotes including a set of genes of particular
interest. Perhaps I can use some of these as markers to quality control
my work in the future?
Here are the genes:
Opn4, Eomes, Trpc7, Oprm1, Nr4a3, Tbx20, Irx6, AW551984, Pcdh19,
Adcyap1, Baiap3, Chl1, Grin3a, Igf1, Gria1, Grin2d, Grin3a, Chrna6,
Chrna3, Htr5a, Htr2a, Htr7, Irx4, PlxnC1, Sema6d, Sema4f, Sema4a,
Sema6b, Lrrc4b, Lrrc58, Lrrc3b, Wnt4, Wnt9b, Ctxn3, Tenm1, Gna14, Rgs4,
Rgs6, Rgs5
table_input <- location_tables[["sr_p08_het"]]
table_name <- "sr_p08_het"
table <- table_input[["data"]][[table_name]]
interesting_genes <- c("Opn4", "Eomes", "Trpc7", "Oprm1", "Nr4a3", "Tbx20",
"Irx6", "AW551984", "Pcdh19", "Adcyap1r1", "Baiap3",
"Chl1", "Grin3a", "Igf1", "Gria1", "Grin2d", "Grin3a",
"Chrna6", "Chrna3", "Htr5a", "Htr2a", "Htr7", "Irx4",
"PlxnC1", "Sema6d", "Sema4f", "Sema4a", "Sema6b", "Lrrc4b",
"Lrrc58", "Lrrc3b", "Wnt4", "Wnt9b", "Ctxn3", "Tenm1", "Gna14",
"Rgs4", "Rgs6", "Rgs5", "Pou4f2", "Chrnb3", "Bcan")
sr_p08_het_volcano <- plot_volcano_condition_de(
table, table_name, fc_col = "deseq_logfc", p_col = "deseq_adjp",
color_low = colors[["het_scn"]], color_high = colors[["het_retina"]],
label_column = "mgi_symbol", label = interesting_genes, alpha = 1.0,
label_type = "label", size = 4)
## Error in plot_volcano_condition_de(table, table_name, fc_col = "deseq_logfc", : Column: deseq_logfc is not in the table.
pp(file = "images/sr_p08_het_volcano.pdf", width = 9, height = 9)
sr_p08_het_volcano[["plot"]]
## Error: object 'sr_p08_het_volcano' not found
## png
## 2
sr_p08_het_volcano[["plot"]]
## Error: object 'sr_p08_het_volcano' not found
sr_p08_het_ma <- plot_ma_condition_de(
table, table_name, expr_col = "deseq_basemean", fc_col = "deseq_logfc",
color_low = colors[["het_scn"]], color_high = colors[["het_retina"]],
p_col = "deseq_adjp", label_column = "mgi_symbol", label = interesting_genes)
## The column: mgi_symbol is not in the data, using rownames.
## Warning in max(newdf[["avg"]]): no non-missing arguments to max; returning -Inf
## Warning in plot_ma_condition_de(table, table_name, expr_col = "deseq_basemean", : NAs
## introduced by coercion
## Error in `[[<-.data.frame`(`*tmp*`, "pval", value = NA_real_): replacement has 1 row, data has 0
pp(file = "images/sr_p08_het_ma.pdf", width = 9, height = 9)
sr_p08_het_ma[["plot"]]
## Error: object 'sr_p08_het_ma' not found
## png
## 2
## Error: object 'sr_p08_het_ma' not found
Test a specific
location query for duplicated IDs
Let us see if any Ensembl gene IDs and/or MGI IDs are shared in the
worksheet location_sr_p08_ko_including_wt_0.1_decreased_sig up/down.
test_table_up <- location_sig[["sr_p08_ko"]][["deseq"]][["ups"]][[1]]
test_table_down <- location_sig[["sr_p08_ko"]][["deseq"]][["downs"]][[1]]
query <- list("up" = rownames(test_table_up),
"down" = rownames(test_table_down))
query_upset <- UpSetR::fromList(query)
UpSetR::upset(query_upset)
## Error in start_col:end_col: argument of length 0
query <- list("up" = test_table_up[["mgi_symbol"]],
"down" = test_table_down[["mgi_symbol"]])
query_upset <- UpSetR::fromList(query)
UpSetR::upset(query_upset)
## Error in start_col:end_col: argument of length 0
And time
time_tables_full <- combine_de_tables(
time_de, keepers = time_keepers,
label_column = label_column,
excel = glue("full_contrasts/time_full_tables-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'time_de' not found
time_sig_full <- extract_significant_genes(
time_tables_full, according_to = "deseq",
excel = glue("full_contrasts/time_full_sig-v{ver}.xlsx"))
## Error: object 'time_tables_full' not found
time_tables <- list()
time_sig <- list()
time_gp <- list()
time_cp <- list()
for (k in seq_along(time_keepers)) {
name <- names(time_keepers)[k]
message("Examining ", name)
keeper <- time_keepers[name]
includes <- time_inclusions[[name]]
include_name <- paste0("inc_", name)
include_df_name <- paste0("df_", name)
include_df <- time_inclusions[[include_df_name]]
includes <- time_inclusions[[include_name]]
summary(rownames(time_sig_full[["deseq"]][["ups"]][[name]]) %in% includes)
include_filename <- glue("time_contrasts/time_{name}_including_wt_{lfc_cutoff}_decreased_table-v{ver}.xlsx")
include_sig_filename <- glue("time_contrasts/time_{name}_including_wt_{lfc_cutoff}_decreased_sig-v{ver}.xlsx")
time_tables[[name]] <- combine_de_tables(
time_de, extra_annot = include_df,
keepers = keeper, label_column = label_column,
excel = include_filename, wanted_genes = includes)
print(time_tables[[name]])
time_sig[[name]] <- extract_significant_genes(
time_tables[[name]], according_to = "deseq",
excel = include_filename)
print(time_sig[[name]])
num_rows <- nrow(time_sig[[name]][["deseq"]][["ups"]][[name]]) +
nrow(time_sig[[name]][["deseq"]][["downs"]][[name]])
message("There are ", num_rows, " significant up and down genes.")
if (num_rows > 10) {
time_gp[[name]] <- all_gprofiler(time_sig[[name]], species = "mmusculus")
gp_written <- write_all_gp(time_gp[[name]])
time_cp[[name]] <- all_cprofiler(time_sig[[name]], time_tables[[name]],
orgdb = "org.Mm.eg.db")
cp_written <- write_all_cp(time_cp[[name]])
}
}
## Examining t_het_dlgn
## Error: object 'time_inclusions' not found
Translatome
queries
In conversation with Colenso, he spoke about a series of contrasts
which would be interesting to attempt in order to query the changes
across both locations and genotypes and/or both locations and time,
thus:
(p08_het_scn / p08_het_retina) / (p08_ko_scn / p08_ko_retina)
as an example. We can definitely do these, but they do not work for
all methods employed (I think they work best with limma and edgeR).
Lets find out!
Two scn/retina
comparisons
- (p08_het_scn / p08_het_retina) / (p08_ko_scn / p08_ko_retina)
- (p15_het_scn / p15_het_retina) / (p15_ko_scn / p15_ko_retina)
scn_extra <- glue("\\
p08het = (conditionp08_het_scn - conditionp08_het_retina), \\
p08ko = (conditionp08_ko_scn - conditionp08_ko_retina), \\
p08het_vs_p08ko = (conditionp08_het_scn - conditionp08_het_retina) - (conditionp08_ko_scn - conditionp08_ko_retina), \\
p15het = (conditionp15_het_scn - conditionp15_het_retina), \\
p15ko = (conditionp15_ko_scn - conditionp15_ko_retina), \\
p15het_vs_p15ko = (conditionp15_het_scn - conditionp15_het_retina) - (conditionp15_ko_scn - conditionp15_ko_retina)")
scn_translatome_de_keepers <- list(
"p08het" = c("p08_het_scn", "p08_het_retina"),
"p08ko" = c("p08_ko_scn", "p08_ko_retina"),
"p15het" = c("p15_het_scn", "p15_het_retina"),
"p15ko" = c("p15_ko_scn", "p15_ko_retina"))
scn_translatome_keepers <- list(
"p08het" = c("p08_het_scn", "p08_het_retina"),
"p08ko" = c("p08_ko_scn", "p08_ko_retina"),
"p08_scn_translatome" = c("p08het", "p08ko"),
"p15het" = c("p15_het_scn", "p15_het_retina"),
"p15ko" = c("p15_ko_scn", "p15_ko_retina"),
"p15_scn_translatome" = c("p15het", "p15ko"))
filt <- normalize_expt(v3_pairwise_input, filter = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'normalize_expt': object 'v3_pairwise_input' not found
limma_test <- limma_pairwise(filt,
keepers = scn_translatome_de_keepers,
keep_underscore = TRUE,
model_batch = FALSE, extra_contrastrs = scn_extra)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'filt' not found
edger_test <- edger_pairwise(filt,
keepers = scn_translatome_de_keepers,
keep_underscore = TRUE,
model_batch = FALSE, extra_contrasts = scn_extra)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'filt' not found
scn_translatome_de <- all_pairwise(v3_pairwise_input, filter = TRUE,
keepers = scn_translatome_de_keepers,
model_batch = FALSE,
do_basic = FALSE, do_dream = FALSE,
do_noiseq = FALSE, do_ebseq = FALSE,
extra_contrasts = scn_extra,
parallel = FALSE, keep_underscore = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'v3_pairwise_input' not found
scn_combined_test <- combine_de_tables(
scn_translatome_de, keepers = scn_translatome_keepers,
excel = glue("translatome/test_scn_translatome_unfiltered_nosva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'scn_translatome_de' not found
scn_translatome_de_sva <- all_pairwise(v3_pairwise_input, filter = TRUE,
keepers = scn_translatome_de_keepers,
model_batch = "svaseq",
do_basic = FALSE, do_dream = FALSE,
do_noiseq = FALSE, do_ebseq = FALSE,
extra_contrasts = scn_extra,
parallel = FALSE, keep_underscore = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'v3_pairwise_input' not found
scn_combined_test_sva <- combine_de_tables(
scn_translatome_de_sva, keepers = scn_translatome_keepers,
excel = glue("translatome/test_scn_translatome_unfiltered_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'scn_translatome_de_sva' not found
Subtracting DESeq2
results: p08 scn het vs ko
p08_scn_combined_deseq <- subtract_deseq_results(
first_table = scn_combined_test[["data"]][["p08het"]],
second_table = scn_combined_test[["data"]][["p08ko"]],
first_lfc = "deseq_logfc", second_lfc = "deseq_logfc",
first_p = "deseq_adjp", second_p = "deseq_adjp",
first_name = "het", second_name = "ko",
excel = glue("translatome/translatome_p08_scn_combined_deseq-v{ver}.xlsx"))
## Error: object 'scn_combined_test' not found
Subtracting DESeq2
results: p15 scn het vs ko
p15_scn_combined_deseq <- subtract_deseq_results(
first_table = scn_combined_test[["data"]][["p15het"]],
second_table = scn_combined_test[["data"]][["p15ko"]],
first_lfc = "deseq_logfc", second_lfc = "deseq_logfc",
first_p = "deseq_adjp", second_p = "deseq_adjp",
first_name = "het", second_name = "ko",
excel = glue("translatome/translatome_p15_scn_combined_deseq-v{ver}.xlsx"))
## Error: object 'scn_combined_test' not found
One dlgn/retina
comparison
- (p08_het_dlgn / p08_het_retina) / (p08_ko_dlgn / p08_ko_retina)
p08_dlgn_extra <- "p08het_vs_p08ko = (conditionp08_het_dlgn - conditionp08_het_retina) - (conditionp08_ko_dlgn - conditionp08_ko_retina)"
p08_dlgn_translatome_de_keepers <- list(
"p08het" = c("p08_het_dlgn", "p08_het_retina"),
"p08ko" = c("p08_ko_dlgn", "p08_ko_retina"))
p08_dlgn_translatome_keepers <- list(
"p08_het_dlgn_vs_retina" = c("p08_het_dlgn", "p08_het_retina"),
"p08_ko_dlgn_vs_retina" = c("p08_ko_dlgn", "p08_ko_retina"),
"p08_dlgn_translatome" = c("p08het", "p08ko"))
p08_dlgn_translatome_de <- all_pairwise(v3_pairwise_input, filter = TRUE,
keepers = p08_dlgn_translatome_de_keepers,
model_batch = FALSE,
do_basic = FALSE, do_dream = FALSE,
do_noiseq = FALSE, do_ebseq = FALSE,
extra_contrasts = p08_dlgn_extra,
parallel = FALSE, keep_underscore = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'v3_pairwise_input' not found
p08_dlgn_combined_test <- combine_de_tables(
p08_dlgn_translatome_de, keepers = p08_dlgn_translatome_keepers,
label_column = label_column,
excel = glue("translatome/test_p08_dlgn_translatome_unfiltered_nosva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'p08_dlgn_translatome_de' not found
p08_dlgn_translatome_de_sva <- all_pairwise(v3_pairwise_input, filter = TRUE,
keepers = p08_dlgn_translatome_de_keepers,
model_batch = "svaseq",
do_basic = FALSE, do_dream = FALSE,
do_noiseq = FALSE, do_ebseq = FALSE,
extra_contrasts = p08_dlgn_extra,
parallel = FALSE, keep_underscore = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'v3_pairwise_input' not found
p08_dlgn_combined_test_sva <- combine_de_tables(
p08_dlgn_translatome_de_sva, keepers = p08_dlgn_translatome_keepers,
label_column = label_column,
excel = glue("translatome/test_p08_dlgn_translatome_unfiltered_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'p08_dlgn_translatome_de_sva' not found
Subtracting the
DESeq2 results
Two scn/retina
comparisons (p15/p08 across het/ko)
- (p15_het_scn / p15_het_retina) / (p08_het_scn / p08_het_retina)
- (p15_ko_scn / p15_ko_retina) / (p08_ko_scn / p08_ko_retina)
time_scn_extra <- glue("\\
p15het = (conditionp15_het_scn - conditionp15_het_retina), \\
p08het = (conditionp08_het_scn - conditionp08_het_retina), \\
p15het_vs_p08het = (conditionp15_het_scn - conditionp15_het_retina) - (conditionp08_het_scn - conditionp08_het_retina),
p15ko = (conditionp15_ko_scn - conditionp15_ko_retina), \\
p08ko = (conditionp08_ko_scn - conditionp08_ko_retina), \\
p15ko_vs_p08ko = (conditionp15_ko_scn - conditionp15_ko_retina) - (conditionp08_ko_scn - conditionp08_ko_retina)")
time_scn_translatome_de_keepers <- list(
"p15het" = c("p15_het_scn", "p15_het_retina"),
"p08het" = c("p08_het_scn", "p08_het_retina"),
"p15ko" = c("p15_ko_scn", "p15_ko_retina"),
"p08ko" = c("p08_ko_scn", "p08_ko_retina"))
time_scn_translatome_keepers <- list(
"p15het" = c("p15_het_scn", "p15_het_retina"),
"p08het" = c("p08_het_scn", "p08_het_retina"),
"p15ko" = c("p15_ko_scn", "p15_ko_retina"),
"p08ko" = c("p08_ko_scn", "p08_ko_retina"),
"p15_het_sc_vs_retina" = c("p15_het_scn", "p15_het_retina"),
"p08_het_sc_vs_retina" = c("p08_het_scn", "p08_het_retina"),
"scn_het_translatome" = c("p15het", "p08het"),
"scn_ko_translatome" = c("p15ko", "p08ko"))
time_scn_translatome_de <- all_pairwise(v3_pairwise_input, filter = TRUE,
keepers = time_scn_translatome_de_keepers,
model_batch = FALSE,
do_basic = FALSE, do_dream = FALSE,
do_noiseq = FALSE, do_ebseq = FALSE,
extra_contrasts = time_scn_extra,
parallel = FALSE, keep_underscore = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'v3_pairwise_input' not found
time_scn_translatome_test <- combine_de_tables(
time_scn_translatome_de,
keepers = time_scn_translatome_keepers,
label_column = label_column,
excel = glue("translatome/test_time_scn_translatome_unfiltered_nosva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'time_scn_translatome_de' not found
time_scn_translatome_de_sva <- all_pairwise(v3_pairwise_input, filter = TRUE,
keepers = time_scn_translatome_de_keepers,
model_batch = "svaseq",
do_basic = FALSE, do_dream = FALSE,
do_noiseq = FALSE, do_ebseq = FALSE,
extra_contrasts = time_scn_extra,
parallel = FALSE, keep_underscore = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'v3_pairwise_input' not found
time_scn_translatome_test_sva <- combine_de_tables(
time_scn_translatome_de_sva,
keepers = time_scn_translatome_keepers,
label_column = label_column,
excel = glue("translatome/test_time_scn_translatome_unfiltered_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'time_scn_translatome_de_sva' not found
Next step: Perform the retina filter; need to think about the proper
union/intersection of the retina/x expression values
In the previous block, we are making 2 global comparisons, here is
one of them:
(p15hetscn/p15hetret)/(p08hetscn/p08hetret)
I therefore want to extract the most logical set of genes higher in
some/all of these conditions with respect to the corresponding wt
conditions. Previously, in section ‘Extract genes included for each set
of contrasts’, I attempted to perform this operation for 2 specific wt
conditions. When this was performed, it took the unique(union) of the
two sets. Thus it stands to reason that I want to take the unique(union)
of all 4 in this instance? e.g.:
(p15hetscn > p15wtscn) | (p15hetret > p15wtret) | (p08hetscn
> p08wtscn) | (p08hetret > p08wtret)
I kind of think it should be:
((p15hetscn > p15wtscn) | (p15hetret > p15wtret)) &
((p08hetscn > p08wtscn) | (p08hetret > p08wtret))
gross, perhaps I should just do this manually, given that there are
only a few putative translatomes to query?
Quick and dirty DESeq2
contrast of contrasts
In a fashion similar to how Hector handled the effect of phagocytosis
with Laura and Najib a long time ago, I propose to do a simple
subtraction of the results of our two contrasts which comprise the
translatome query (I was thinking about this last week, thus the
inclusion of them in the de tables above). Similarly to the phagocytosis
effect, I will simply take the worst posible adjusted p-value. I will
repeat this with limma/EdgeR and see how similar the final results are
to what those methods provide in the (a/b)/(c/d) comparisons. I am
reasonably certain that DESeq2’s results() function has the ability to
perform these odd contrasts, but I have never figured out how; perhaps I
will use this as a chance to revisit that…
Let us test this idea with the p08 dlgn query, which seeks to
compare:
(p08_het_dlgn / p08_het_retina) / (p08_ko_dlgn / p08_ko_retina)
These are maintained in the de_table with the names
‘p08_het_dlgn_vs_retina’ and ‘p08_ko_dlgn_vs_retina’
p08 dlgn het vs
ko
p08_dlgn_combined_deseq <- subtract_deseq_results(
first_table = p08_dlgn_combined_test[["data"]][["p08_het_dlgn_vs_retina"]],
second_table = p08_dlgn_combined_test[["data"]][["p08_ko_dlgn_vs_retina"]],
first_lfc = "deseq_logfc", second_lfc = "deseq_logfc",
first_p = "deseq_adjp", second_p = "deseq_adjp",
first_name = "het", second_name = "ko",
excel = glue("translatome/translatome_p08_dlgn_combined_deseq-v{ver}.xlsx"))
## Error: object 'p08_dlgn_combined_test' not found
See how similar these results are to those obtained from
limma/edger.
test_columns <- c("edger_logfc", "limma_logfc", "edger_adjp", "limma_adjp")
test_df <- p08_dlgn_combined_test[["data"]][["p08_dlgn_translatome"]][, test_columns]
## Error: object 'p08_dlgn_combined_test' not found
test_df <- merge(test_df, p08_dlgn_combined_deseq, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'test_df' not found
rownames(test_df) <- test_df[["Row.names"]]
## Error: object 'test_df' not found
test_df[["Row.names"]] <- NULL
## Error: object 'test_df' not found
cor.test(test_df[["limma_logfc"]], test_df[["het_vs_ko_logfc"]])
## Error: object 'test_df' not found
cor.test(test_df[["edger_logfc"]], test_df[["het_vs_ko_logfc"]])
## Error: object 'test_df' not found
tt <- plot_linear_scatter(test_df[, c("limma_logfc", "het_vs_ko_logfc")])
## Error: object 'test_df' not found
## NULL
tt <- plot_linear_scatter(test_df[, c("edger_adjp", "het_vs_ko_p")])
## Error: object 'test_df' not found
## NULL
## So, using the maximum p-value is a complete failure; but the extreme similarities
## between this and edgeR suggest to me that it is likely possible to use the results
## from edgeR without concern (or limma for that matter, it was also extremely similar)
## Or I can spend a little time and collect the numbers on each side of the division
## and calculate a t statistic myself.
Non-Specific filtering
of the translatome data
I have on hand
- p08het_vs_p08ko : (p08_het_scn - p08_het_retina) - (p08_ko_scn -
p08_ko_retina)
- p15het_vs_p15ko : (p15_het_scn - p15_het_retina) - (p15_ko_scn -
p15_ko_retina)
- p08het_vs_p08ko : (p08_het_dlgn - p08_het_retina) - (p08_ko_dlgn -
p08_ko_retina)
- p15het_vs_p08het : (p15_het_scn - p15_het_retina) - (p08_het_scn -
p08_het_retina)
- p15ko_vs_p08ko : (p15_ko_scn - p15_ko_retina) - (p08_ko_scn -
p08_ko_retina)
I have gene sets up above which define the genes suitable for each of
these pieces. There are only 5 comparisons, let us step through
them.
SCN translatome
het/ko at p08
The data for this contrast resides in scn_combined_test\(data\)p08_scn_translatome or the same slot
of scn_combined_test_sva
- p08_het_scn - p08_het_retina) - (p08_ko_scn - p08_ko_retina)
Thus, the inclusion_sig portions to extract are found in:
inclusion_sig[[“deseq”]][[“ups”]], and are named exactly as written
above!
p08_het_vs_ko_translatome_unfilt <- scn_combined_test[["data"]][["p08_scn_translatome"]]
## Error: object 'scn_combined_test' not found
num_union <- unique(c(rownames(inclusion_sig[["deseq"]][["ups"]][["p08_het_scn"]]),
rownames(inclusion_sig[["deseq"]][["ups"]][["p08_het_retina"]])))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'unique': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'inclusion_sig' not found
## Error: object 'num_union' not found
den_union <- unique(c(rownames(inclusion_sig[["deseq"]][["ups"]][["p08_ko_scn"]]),
rownames(inclusion_sig[["deseq"]][["ups"]][["p08_ko_retina"]])))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'unique': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'inclusion_sig' not found
## Error: object 'den_union' not found
both_union <- unique(c(num_union, den_union))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'unique': object 'num_union' not found
## Error: object 'both_union' not found
both_inter_idx <- num_union %in% den_union
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function '%in%': object 'num_union' not found
both_inter <- num_union[both_inter_idx]
## Error: object 'num_union' not found
## Error: object 'both_inter' not found
keeper <- list("p08_scn_translatome" = c("p08het", "p08ko"))
p08_scn_translatome_union_filtered <- combine_de_tables(
scn_translatome_de, keepers = keeper,
label_column = label_column,
excel = glue("translatome/p08_scn_translatome_union_filtered_nosva-v{ver}.xlsx"),
wanted_genes = both_union)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'scn_translatome_de' not found
p08_scn_translatome_inter_filtered <- combine_de_tables(
scn_translatome_de, keepers = keeper,
label_column = label_column,
excel = glue("translatome/p08_scn_translatome_intersect_filtered_nosva-v{ver}.xlsx"),
wanted_genes = both_inter)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'scn_translatome_de' not found
p08_scn_translatome_union_filtered_sva <- combine_de_tables(
scn_translatome_de_sva, keepers = keeper,
label_column = label_column,
excel = glue("translatome/p08_scn_translatome_union_filtered_sva-v{ver}.xlsx"),
wanted_genes = both_union)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'scn_translatome_de_sva' not found
p08_scn_translatome_union_filtered <- combine_de_tables(
scn_translatome_de, keepers = keeper,
label_column = label_column,
excel = glue("translatome/p08_scn_translatome_intersect_filtered_sva-v{ver}.xlsx"),
wanted_genes = both_inter)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'scn_translatome_de' not found
Venn/UpSet of Retina,
SCN, and dLGN DE Genes
Here is a snippet from Rashmi which expresses nicely the DE-result
comparisons she is most interested:
Since, I want to know the number of DEG expressed in Retina, SCN and
dLGN with respect to genotype, Location and time. I prepared the venn
diagram for these comparison:
- Genotype: P8 Ret Het vs KO, P15 Ret Het vs KO, P8 SCN Het vs KO, P15
SCN Het vs KO, P8 dLGN Het vs KO, P15 dLGN Het vs KO
- Location: P8_het Ret vs SCN, P8_KO Ret vs SCN, P15_het Ret vs SCN,
P15_KO Ret vs SCN, P8_het Ret vs dLGN, P8_KO Ret vs dLGN, P15_het Ret vs
dLGN, P15_KO Ret vs dLGN, P8_het SCN vs dLGN, P8_KO SCN vs dLGN, P15_het
SCN vs dLGN, P15_KO SCN vs dLGN.
Since I was interested in understanding the change in local
translatome according to Location for different developmental time
points for Het and KO. Hence, I tried to generate a venn diagram for
Location (Ret and SCN) at developmental time points P8 and P15 for
genotype het and KO. So the venn diagram / upset plot will be for
location where some genes will be shared/unique for P8_Ret_het,
P8_SCN_Het, P15_Ret_HET, P15_SCN_HET. We can prepare an upset plot for
P8_Ret_KO, P8_SCN_KO, P15_Ret_KO and P15_SCN_KO also. Or can generate an
upset plot by combining both P8_Ret_het, P8_SCN_Het, P15_Ret_HET and
P15_SCN_HET and P8_Ret_KO, P8_SCN_KO, P15_Ret_KO and P15_SCN_KO.
Ok, let us see if I can implement this, starting with the genotype
query
- Genotype: P8 Ret Het vs KO, P15 Ret Het vs KO, P8 SCN Het vs KO, P15
SCN Het vs KO, P8 dLGN Het vs KO, P15 dLGN Het vs KO
ko vs het; all
locations and times
## The appropriate data structure is 'genotype_tables',
## and the tables of interest are:
table_names <- c("kh_p08_retina", "kh_p15_retina", "kh_p08_scn",
"kh_p15_scn", "kh_p08_dlgn", "kh_p15_dlgn")
table_names %in% names(genotype_sig)
## [1] FALSE FALSE FALSE FALSE FALSE FALSE
newsig <- genotype_sig[[1]]
## Error in genotype_sig[[1]]: subscript out of bounds
for (sig in 2:length(table_names)) {
name <- table_names[sig]
newsig[["deseq"]][["ups"]][[name]] <- genotype_sig[[name]][["deseq"]][["ups"]][[name]]
newsig[["deseq"]][["downs"]][[name]] <- genotype_sig[[name]][["deseq"]][["downs"]][[name]]
}
## Error: object 'newsig' not found
genotype_upsetr <- upsetr_sig(newsig)
## Error: object 'newsig' not found
genotype_upset_written <- write_upset_groups(genotype_upsetr, excel = "excel/genotype_upset_groups.xlsx")
## Error: object 'genotype_upsetr' not found
genotype_upsetr[["all_plot"]]
## Error: object 'genotype_upsetr' not found
pp(file = "images/test_genotype_upset.pdf")
print(genotype_upsetr[["all_plot"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'print': object 'genotype_upsetr' not found
Now let us try the location-specific comparisons
scn vs retina,
p08
## The appropriate data structure is 'genotype_tables',
## and the tables of interest are:
table_names <- c("sr_p08_het", "sr_p08_ko")
table_names %in% names(location_sig)
## [1] FALSE FALSE
location_upset_input <- list()
first_table <- table_names[1]
newsig <- location_sig[[first_table]]
for (sig in 2:length(table_names)) {
name <- table_names[sig]
newsig[["deseq"]][["ups"]][[name]] <- location_sig[[name]][["deseq"]][["ups"]][[name]]
newsig[["deseq"]][["downs"]][[name]] <- location_sig[[name]][["deseq"]][["downs"]][[name]]
}
location_upsetr <- upsetr_sig(newsig)
## Error in xtfrm.data.frame(x): cannot xtfrm data frames
location_upset_written <- write_upset_groups(location_upsetr, excel = "excel/sr_p08_hetko_upset_groups.xlsx")
## Error: object 'location_upsetr' not found
location_upsetr[["all_plot"]]
## Error: object 'location_upsetr' not found
pp(file = "images/test_location_sr_p08_hetko_upset.pdf")
print(location_upsetr[["all_plot"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'print': object 'location_upsetr' not found
## png
## 2
I am reasonably certain that Rashmi would like a table of the genes
shared among increased scn ko and het in the above plot along with the
increased retina (e.g. the 269 and 103 gene sets).
scn vs retina,
p15
table_names <- c("sr_p15_het", "sr_p15_ko")
table_names %in% names(location_sig)
## [1] FALSE FALSE
location_upset_input <- list()
first_table <- table_names[1]
newsig <- location_sig[[first_table]]
for (sig in 2:length(table_names)) {
name <- table_names[sig]
newsig[["deseq"]][["ups"]][[name]] <- location_sig[[name]][["deseq"]][["ups"]][[name]]
newsig[["deseq"]][["downs"]][[name]] <- location_sig[[name]][["deseq"]][["downs"]][[name]]
}
location_upsetr <- upsetr_sig(newsig)
## Error in xtfrm.data.frame(x): cannot xtfrm data frames
location_upset_written <- write_upset_groups(location_upsetr, excel = "excel/sr_p15_hetko_upset_groups.xlsx")
## Error: object 'location_upsetr' not found
location_upsetr[["all_plot"]]
## Error: object 'location_upsetr' not found
print(scn_retina_p15_upset_result)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'print': object 'scn_retina_p15_upset_result' not found
pp(file = "images/test_location_sr_p15_hetko_upset.pdf")
print(location_upsetr[["all_plot"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'print': object 'location_upsetr' not found
## png
## 2
dlgn vs retina,
p08
## The appropriate data structure is 'genotype_tables',
## and the tables of interest are:
table_names <- c("dr_p08_het", "dr_p08_ko")
location_upset_input <- list()
first_table <- table_names[1]
newsig <- location_sig[[first_table]]
for (sig in 2:length(table_names)) {
name <- table_names[sig]
newsig[["deseq"]][["ups"]][[name]] <- location_sig[[name]][["deseq"]][["ups"]][[name]]
newsig[["deseq"]][["downs"]][[name]] <- location_sig[[name]][["deseq"]][["downs"]][[name]]
}
location_upsetr <- upsetr_sig(newsig)
## Error in xtfrm.data.frame(x): cannot xtfrm data frames
location_upset_written <- write_upset_groups(location_upsetr, excel = "excel/dr_p08_hetko_upset_groups.xlsx")
## Error: object 'location_upsetr' not found
location_upsetr[["all_plot"]]
## Error: object 'location_upsetr' not found
pp(file = "images/test_location_dr_p08_hetko_upset.pdf")
print(location_upsetr[["all_plot"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'print': object 'location_upsetr' not found
## png
## 2
dlgn vs retina,
p15
## The appropriate data structure is 'genotype_tables',
## and the tables of interest are:
table_names <- c("dr_p15_het", "dr_p15_ko")
location_upset_input <- list()
first_table <- table_names[1]
newsig <- location_sig[[first_table]]
for (sig in 2:length(table_names)) {
name <- table_names[sig]
newsig[["deseq"]][["ups"]][[name]] <- location_sig[[name]][["deseq"]][["ups"]][[name]]
newsig[["deseq"]][["downs"]][[name]] <- location_sig[[name]][["deseq"]][["downs"]][[name]]
}
location_upsetr <- upsetr_sig(newsig)
## Error in xtfrm.data.frame(x): cannot xtfrm data frames
location_upset_written <- write_upset_groups(location_upsetr, excel = "excel/dr_p15_hetko_upset_groups.xlsx")
## Error: object 'location_upsetr' not found
location_upsetr[["all_plot"]]
## Error: object 'location_upsetr' not found
pp(file = "images/test_location_dr_p15_hetko_upset.pdf")
print(location_upsetr[["all_plot"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'print': object 'location_upsetr' not found
## png
## 2
dlgn vs scn,
p08
table_names <- c("ds_p08_het", "ds_p08_ko")
location_upset_input <- list()
first_table <- table_names[1]
newsig <- location_sig[[first_table]]
for (sig in 2:length(table_names)) {
name <- table_names[sig]
newsig[["deseq"]][["ups"]][[name]] <- location_sig[[name]][["deseq"]][["ups"]][[name]]
newsig[["deseq"]][["downs"]][[name]] <- location_sig[[name]][["deseq"]][["downs"]][[name]]
}
location_upsetr <- upsetr_sig(newsig)
## Error in xtfrm.data.frame(x): cannot xtfrm data frames
location_upset_written <- write_upset_groups(location_upsetr, excel = "excel/ds_p08_hetko_upset_groups.xlsx")
## Error: object 'location_upsetr' not found
location_upsetr[["all_plot"]]
## Error: object 'location_upsetr' not found
pp(file = "images/test_location_ds_p08_hetko_upset.pdf")
print(location_upsetr[["all_plot"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'print': object 'location_upsetr' not found
## png
## 2
dlgn vs scn,
p15
table_names <- c("ds_p15_het", "ds_p15_ko")
location_upset_input <- list()
first_table <- table_names[1]
newsig <- location_sig[[first_table]]
for (sig in 2:length(table_names)) {
name <- table_names[sig]
newsig[["deseq"]][["ups"]][[name]] <- location_sig[[name]][["deseq"]][["ups"]][[name]]
newsig[["deseq"]][["downs"]][[name]] <- location_sig[[name]][["deseq"]][["downs"]][[name]]
}
location_upsetr <- upsetr_sig(newsig)
## Error in xtfrm.data.frame(x): cannot xtfrm data frames
location_upset_written <- write_upset_groups(location_upsetr, excel = "excel/ds_p15_hetko_upset_groups.xlsx")
## Error: object 'location_upsetr' not found
location_upsetr[["all_plot"]]
## Error: object 'location_upsetr' not found
pp(file = "images/test_location_ds_p15_hetko_upset.pdf")
print(location_upsetr[["all_plot"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'print': object 'location_upsetr' not found
## png
## 2
Shared and unique gene
sets across x/wt
In this block I want to find the unique and shared genes between:
- scn p8 het/wt and retina p8 het/wt: hwp08scninc, hwp08retinc,
hwp08scndec, hwp08retdec
- scn p15 het/wt and retina p15 het/wt: hwp15scninc, hwp15retinc,
hwp15scndec, hwp15retdec
- #1 and #2 together: 8 catgories above
- scn p8 ko/wt and retina p8 ko/wt
- scn p15 ko/wt and retina p15 ko/wt
- #4 and #5 together
The comparisons of het/wt are found in the ‘inclusion_sig’ dataset;
because they are providing our cutoffs for nonspecific binding.
Number 1 above:
p08_het vs wt for scn and retina.
table_names <- c("p08_het_scn", "p08_het_retina")
inclusion_upsetr <- upsetr_sig(inclusion_sig, contrasts = table_names)
## Error: object 'inclusion_sig' not found
inclusion_upset_written <- write_upset_groups(inclusion_upsetr, excel = "excel/rs_p08_het_inclusion_upset_groups.xlsx")
## Error: object 'inclusion_upsetr' not found
inclusion_upsetr[["all_plot"]]
## Error: object 'inclusion_upsetr' not found
pp(file = "images/inclusion_sr_p08_upset.pdf")
print(inclusion_upsetr[["all_plot"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'print': object 'inclusion_upsetr' not found
## png
## 2
Number 2 above:
p15_het vs wt for scn and retina.
table_names <- c("p15_het_scn", "p15_het_retina")
inclusion_upsetr <- upsetr_sig(inclusion_sig, contrasts = table_names)
## Error: object 'inclusion_sig' not found
inclusion_upset_written <- write_upset_groups(inclusion_upsetr, excel = "excel/rs_p15_het_inclusion_upset_groups.xlsx")
## Error: object 'inclusion_upsetr' not found
inclusion_upsetr[["all_plot"]]
## Error: object 'inclusion_upsetr' not found
pp(file = "images/inclusion_sr_p15_upset.pdf")
print(inclusion_upsetr[["all_plot"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'print': object 'inclusion_upsetr' not found
## png
## 2
Number 3 above:
combination of #1 and #2
table_names <- c("p08_het_scn", "p08_het_retina", "p15_het_scn", "p15_het_retina")
inclusion_upsetr <- upsetr_sig(inclusion_sig, contrasts = table_names)
## Error: object 'inclusion_sig' not found
inclusion_upset_written <- write_upset_groups(inclusion_upsetr, excel = "excel/rs_p08p15_het_inclusion_upset_groups.xlsx")
## Error: object 'inclusion_upsetr' not found
inclusion_upsetr[["all_plot"]]
## Error: object 'inclusion_upsetr' not found
pp(file = "images/inclusion_sr_p08p15_upset.pdf")
print(inclusion_upsetr[["all_plot"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'print': object 'inclusion_upsetr' not found
## png
## 2
Number 4 above
p08_ko vs wt for scn and retina.
table_names <- c("p08_ko_scn", "p08_ko_retina")
inclusion_upsetr <- upsetr_sig(inclusion_sig, contrasts = table_names)
## Error: object 'inclusion_sig' not found
inclusion_upset_written <- write_upset_groups(inclusion_upsetr, excel = "excel/rs_p08_ko_inclusion_upset_groups.xlsx")
## Error: object 'inclusion_upsetr' not found
inclusion_upsetr[["all_plot"]]
## Error: object 'inclusion_upsetr' not found
pp(file = "images/inclusion_sr_p08_ko_upset.pdf")
print(inclusion_upsetr[["all_plot"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'print': object 'inclusion_upsetr' not found
## png
## 2
Number 5 above
p15_ko vs wt for scn and retina.
table_names <- c("p15_ko_scn", "p15_ko_retina")
inclusion_upsetr <- upsetr_sig(inclusion_sig, contrasts = table_names)
## Error: object 'inclusion_sig' not found
inclusion_upset_written <- write_upset_groups(inclusion_upsetr, excel = "excel/rs_p15_ko_inclusion_upset_groups.xlsx")
## Error: object 'inclusion_upsetr' not found
inclusion_upsetr[["all_plot"]]
## Error: object 'inclusion_upsetr' not found
pp(file = "images/inclusion_sr_p15_ko_upset.pdf")
print(inclusion_upsetr[["all_plot"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'print': object 'inclusion_upsetr' not found
## png
## 2
Number 6 above:
Combining #4 and #5
table_names <- c("p08_ko_scn", "p08_ko_retina", "p15_ko_scn", "p15_ko_retina")
inclusion_upsetr <- upsetr_sig(inclusion_sig, contrasts = table_names)
## Error: object 'inclusion_sig' not found
inclusion_upset_written <- write_upset_groups(inclusion_upsetr, excel = "excel/rs_p08p15_ko_inclusion_upset_groups.xlsx")
## Error: object 'inclusion_upsetr' not found
inclusion_upsetr[["all_plot"]]
## Error: object 'inclusion_upsetr' not found
pp(file = "images/inclusion_sr_p08p15_ko_upset.pdf")
print(inclusion_upsetr[["all_plot"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'print': object 'inclusion_upsetr' not found
## png
## 2
GSVA
msigdb <- "reference/msigdb_v2024.1.Mm.db"
if (file.exists(msigdb)) {
v3_h_gsva <- simple_gsva(v3_pairwise_input, orgdb = "org.Mm.eg.db", signature_category = "mh",
signatures = msigdb, id_source = "fdata",
required_id = "mgi_symbol")
v3_h_gsva
v3_h_gsva_sig <- get_sig_gsva_categories(
v3_h_gsva, excel = "excel/gsva_sig_hallmark_categories.xlsx")
v3_h_gsva_sig
v3_m1_gsva <- simple_gsva(v3_pairwise_input, orgdb = "org.Mm.eg.db", signature_category = "m1",
signatures = msigdb, id_source = "fdata",
required_id = "mgi_symbol")
v3_m1_gsva
v3_m1_gsva_sig <- get_sig_gsva_categories(
v3_m1_gsva, excel = "excel/gsva_sig_positional_categories.xlsx")
v3_m1_gsva_sig
v3_m2_gsva <- simple_gsva(v3_pairwise_input, orgdb = "org.Mm.eg.db", signature_category = "m2",
signatures = msigdb, id_source = "fdata",
required_id = "mgi_symbol")
v3_m2_gsva
v3_m2_gsva_sig <- get_sig_gsva_categories(
v3_m2_gsva, excel = "excel/gsva_sig_curated_categories.xlsx")
v3_m2_gsva_sig
v3_m3_gsva <- simple_gsva(v3_pairwise_input, orgdb = "org.Mm.eg.db", signature_category = "m3",
signatures = msigdb, id_source = "fdata",
required_id = "mgi_symbol")
v3_m3_gsva
v3_m3_gsva_sig <- get_sig_gsva_categories(
v3_m3_gsva, excel = "excel/gsva_sig_regulatory_categories.xlsx")
v3_m3_gsva_sig
v3_m5_gsva <- simple_gsva(v3_pairwise_input, orgdb = "org.Mm.eg.db", signature_category = "m5",
signatures = msigdb, id_source = "fdata",
required_id = "mgi_symbol")
v3_m5_gsva
v3_m5_gsva_sig <- get_sig_gsva_categories(
v3_m5_gsva, excel = "excel/gsva_sig_ontology_categories.xlsx")
v3_m5_gsva_sig
v3_m8_gsva <- simple_gsva(v3_pairwise_input, orgdb = "org.Mm.eg.db", signature_category = "m8",
signatures = msigdb, id_source = "fdata",
required_id = "mgi_symbol")
v3_m8_gsva
v3_m8_gsva_sig <- get_sig_gsva_categories(
v3_m8_gsva, excel = "excel/gsva_sig_celltype_categories.xlsx")
v3_m8_gsva_sig
}
## Error: object 'v3_pairwise_input' not found
GSEA images
Up above I created a fairly large set of enrichment/GSEA analyses.
Let us pull some of the most interesting results here and look at
them.
Here are the specific queries from Rashmi:
- Genotype (het vs ko):
- P8 het and ko for Ret
- SCN (P8 het vs KO SCN
- P8 het vs KO Ret)
- P15 het and ko for Ret
- Location (somal vs axonal):
- SR_P08_KO
- SR_P08_Het
- SR_P15_KO
- SR_P15_Het
- Time(p8vs p15):
- t_het_Ret_ po8-p15
- t_KO_Ret_C po8-p15
- t_het_SCN_po8-p15
- t_KO_SCN_po8-p15
Genotype
Let us take a moment and see for which contrasts I acquired
results:
I need to make a little summary for clusterprofiler too so that I can
easily see how many hits there are for each contrast.
summary(genotype_full_gp)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'genotype_full_gp' not found
for (i in names(genotype_full_gp)) {
print(i)
print(genotype_full_gp[[i]][["num_hits"]])
}
## Error: object 'genotype_full_gp' not found
summary(genotype_full_cp)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'genotype_full_cp' not found
for (i in names(genotype_full_cp)) {
print(i)
print(nrow(genotype_full_cp[[i]][["gse_go"]]))
}
## Error: object 'genotype_full_cp' not found
p8 het/ko for
retina:
This contrast, even before filtering away the high-wt genes, only has
8 genes in the set of up and down genes combined. As a result, my
function which performs gProfiler/clusterProfiler skips it, and also
skips the p15 het/ko for retina samples.
p8 het/ko for
scn:
This has a bunch more genes: 51 up and 128 down. Unfortunately,
gProfiler sees no significant over-representation in the up category of
genes. The down category has
The up/down sets from clusterProfiler have enrich_go, gse_go, and
enrich_objects to look at.
genotype_full_gp$kh_p08_scn_up$num_hits
## Error: object 'genotype_full_gp' not found
genotype_full_gp$kh_p08_scn_down$num_hits
## Error: object 'genotype_full_gp' not found
plots <- plot_enrichresult(genotype_full_gp$kh_p08_scn_down[["BP_enrich"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'dotplot': object 'genotype_full_gp' not found
## Error: object 'plots' not found
## Error: object 'plots' not found
Perhaps I should just ask the question: for which categories did I
get results back?
summary(genotype_full_gp)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'genotype_full_gp' not found
kh_p08_dlgn_up: No significant gProfiler results. kh_p15_dlgn_up:
Significant BP, HP, KEGG, MF, REAC, TF kh_p08_scn_up: No significant
gProfiler results. kh_p08_scn_down: Significant BP, MiRNA, MF, TF
kh_p15_scn_down: Significant BP, MF
plots <- plot_enrichresult(genotype_full_gp[["kh_p15_dlgn_up"]][["BP_enrich"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'dotplot': object 'genotype_full_gp' not found
## Error: object 'plots' not found
Location
Scn vs retina ko,
p08
plots <- plot_enrichresult(location_gp[["sr_p08_ko"]][["sr_p08_ko_up"]][["BP_enrich"]])
## Error: unable to find an inherited method for function 'dotplot' for signature 'object = "NULL"'
## Error: object 'plots' not found
plots <- plot_enrichresult(location_gp[["sr_p08_ko"]][["sr_p08_ko_down"]][["BP_enrich"]])
## Error: unable to find an inherited method for function 'dotplot' for signature 'object = "NULL"'
## Error: object 'plots' not found
scn vs retina,
het, p08
Enriched groups: BP, KEGG, MF, TF, CC
summary(location_gp[["sr_p08_het"]][["sr_p08_het_up"]])
## Length Class Mode
## 0 NULL NULL
plots <- plot_enrichresult(location_gp[["sr_p08_het"]][["sr_p08_het_up"]][["BP_enrich"]])
## Error: unable to find an inherited method for function 'dotplot' for signature 'object = "NULL"'
## Error: object 'plots' not found
plots <- plot_enrichresult(location_gp[["sr_p08_het"]][["sr_p08_het_up"]][["CC_enrich"]])
## Error: unable to find an inherited method for function 'dotplot' for signature 'object = "NULL"'
## Error: object 'plots' not found
plots <- plot_enrichresult(location_gp[["sr_p08_het"]][["sr_p08_het_down"]][["BP_enrich"]])
## Error: unable to find an inherited method for function 'dotplot' for signature 'object = "NULL"'
## Error: object 'plots' not found
sr_p08_het_topn_gsea <- plot_topn_gsea(location_cp[[""]])
## Error in if (nrow(gse) < topn) {: argument is of length zero
Scn vs retina ko,
p15
plots <- plot_enrichresult(location_gp[["sr_p15_ko"]][["sr_p15_ko_up"]][["BP_enrich"]])
## Error: unable to find an inherited method for function 'dotplot' for signature 'object = "NULL"'
## Error: object 'plots' not found
plots <- plot_enrichresult(location_gp[["sr_p15_ko"]][["sr_p15_ko_down"]][["BP_enrich"]])
## Error: unable to find an inherited method for function 'dotplot' for signature 'object = "NULL"'
## Error: object 'plots' not found
scn vs retina,
het, p15
plots <- plot_enrichresult(location_gp[["sr_p15_het"]][["sr_p15_het_up"]][["BP_enrich"]])
## Error: unable to find an inherited method for function 'dotplot' for signature 'object = "NULL"'
## Error: object 'plots' not found
plots <- plot_enrichresult(location_gp[["sr_p15_het"]][["sr_p15_het_down"]][["BP_enrich"]])
## Error: unable to find an inherited method for function 'dotplot' for signature 'object = "NULL"'
## Error: object 'plots' not found
Time
het retina
Ups: significant results for BP, MF, TF Downs: BP, MF, REAC, TF,
WP
plots <- plot_enrichresult(time_gp[["t_het_retina"]][["t_het_retina_up"]][["BP_enrich"]])
## Error: unable to find an inherited method for function 'dotplot' for signature 'object = "NULL"'
## Error: object 'plots' not found
plots <- plot_enrichresult(time_gp[["t_het_retina"]][["t_het_retina_down"]][["BP_enrich"]])
## Error: unable to find an inherited method for function 'dotplot' for signature 'object = "NULL"'
## Error: object 'plots' not found
ko retina
Up: BP, MiRNA, MF Down: BP, MF, REAC, TF
plots <- plot_enrichresult(time_gp[["t_ko_retina"]][["t_ko_retina_up"]][["BP_enrich"]])
## Error: unable to find an inherited method for function 'dotplot' for signature 'object = "NULL"'
## Error: object 'plots' not found
plots <- plot_enrichresult(time_gp[["t_ko_retina"]][["t_ko_retina_down"]][["BP_enrich"]])
## Error: unable to find an inherited method for function 'dotplot' for signature 'object = "NULL"'
## Error: object 'plots' not found
het scn
Neither of the SCN gProfiler queries provided any results.
## no love.
Bibliography
pander::pander(sessionInfo())
message(paste0("This is hpgltools commit: ", get_git_commit()))
message(paste0("Saving to ", savefile))
tmp <- sm(saveme(filename = savefile))
tmp <- loadme(filename = savefile)
Hänzelmann, Sonja, Robert Castelo, and Justin Guinney. 2013.
“GSVA: Gene Set Variation Analysis for Microarray and
RNA-Seq Data.” BMC Bioinformatics 14 (1):
7.
https://doi.org/10.1186/1471-2105-14-7.
Hoffman, Gabriel E, and Panos Roussos. 2020.
“Dream: Powerful
Differential Expression Analysis for Repeated Measures Designs.”
Bioinformatics 37 (2): 192–201.
https://doi.org/10.1093/bioinformatics/btaa687.
Hoffman, Gabriel E., and Eric E. Schadt. 2016.
“variancePartition: Interpreting Drivers of
Variation in Complex Gene Expression Studies.” BMC
Bioinformatics 17 (1): 483.
https://doi.org/10.1186/s12859-016-1323-z.
Leek, Jeffrey T., W. Evan Johnson, Hilary S. Parker, Andrew E. Jaffe,
and John D. Storey. 2012.
“The SVA Package for
Removing Batch Effects and Other Unwanted Variation in High-Throughput
Experiments.” Bioinformatics 28 (6): 882–83.
https://doi.org/10.1093/bioinformatics/bts034.
Leng, Ning, John A. Dawson, James A. Thomson, Victor Ruotti, Anna I.
Rissman, Bart M. G. Smits, Jill D. Haag, Michael N. Gould, Ron M.
Stewart, and Christina Kendziorski. 2013.
“EBSeq: An
Empirical Bayes Hierarchical Model for Inference in RNA-seq Experiments.”
Bioinformatics 29 (8): 1035–43.
https://doi.org/10.1093/bioinformatics/btt087.
Li, Huiling, Yangao Huo, Xi He, Liping Yao, Hao Zhang, Yiqiang Cui,
Huijuan Xiao, et al. 2022.
“A Male Germ-Cell-Specific Ribosome
Controls Male Fertility.” Nature 612 (7941): 725–31.
https://doi.org/10.1038/s41586-022-05508-0.
Love, Michael I., Wolfgang Huber, and Simon Anders. 2014.
“Moderated Estimation of Fold Change and Dispersion for
RNA-Seq Data with DESeq2.”
bioRxiv.
https://doi.org/10.1101/002832.
Raudvere, Uku, Liis Kolberg, Ivan Kuzmin, Tambet Arak, Priit Adler, Hedi
Peterson, and Jaak Vilo. 2019.
“G:Profiler: A Web
Server for Functional Enrichment Analysis and Conversions of Gene Lists
(2019 Update).” Nucleic Acids Research 47 (W1): W191–98.
https://doi.org/10.1093/nar/gkz369.
Ritchie, Matthew E., Belinda Phipson, Di Wu, Yifang Hu, Charity W. Law,
Wei Shi, and Gordon K. Smyth. 2015.
“Limma Powers Differential
Expression Analyses for RNA-sequencing and
Microarray Studies.” Nucleic Acids Research 43 (7): e47.
https://doi.org/10.1093/nar/gkv007.
Robinson, Mark D., Davis J. McCarthy, and Gordon K. Smyth. 2010.
“edgeR: A Bioconductor
Package for Differential Expression Analysis of Digital Gene Expression
Data.” Bioinformatics 26 (1): 139–40.
https://doi.org/10.1093/bioinformatics/btp616.
Smedley, Damian, Syed Haider, Benoit Ballester, Richard Holland, Darin
London, Gudmundur Thorisson, and Arek Kasprzyk. 2009.
“BioMart – Biological Queries Made Easy.”
BMC Genomics 10 (1): 22.
https://doi.org/10.1186/1471-2164-10-22.
Tarazona, Sonia, Fernando García, Alberto Ferrer, Joaquín Dopazo, and
Ana Conesa. 2011.
“NOIseq: A RNA-seq Differential Expression Method Robust for
Sequencing Depth Biases.” EMBnet.journal 17 (B): 18–19.
https://doi.org/10.14806/ej.17.B.265.
Yu, Guangchuang. n.d. 📖 Introduction
Biomedical Knowledge Mining Using GOSemSim and
clusterProfiler. Accessed June 21,
2024.
LS0tCnRpdGxlOiAiQW5hbHlzZXMgb2YgdGhlIElQUkdDIHJlLXByb2Nlc3NlZCBzYW1wbGVzLiIKYXV0aG9yOiAiYXRiIGFiZWxld0BnbWFpbC5jb20iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKYmlibGlvZ3JhcGh5OiBhdGIuYmliCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIGtlZXBfbWQ6IGZhbHNlCiAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCiAgICAgIHJtZGZvcm1hdHM6OnJlYWR0aGVkb3duOgogICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgICAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgICAgICBkZl9wcmludDogcGFnZWQKICAgICAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgICAgIGZpZ19oZWlnaHQ6IDcKICAgICAgICBmaWdfd2lkdGg6IDcKICAgICAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgICAgICB3aWR0aDogMzAwCiAgICAgICAga2VlcF9tZDogZmFsc2UKICAgICAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgICAgQmlvY1N0eWxlOjpodG1sX2RvY3VtZW50OgogICAgICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgICAgICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICAgICAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgICAgICAgZmlnX2hlaWdodDogNwogICAgICAgICAgZmlnX3dpZHRoOiA3CiAgICAgICAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgICAgICAgIGtlZXBfbWQ6IGZhbHNlCiAgICAgICAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICAgICAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpib2R5LCB0ZCB7CmZvbnQtc2l6ZTogMTZweDsKfQpjb2RlLnJ7CmZvbnQtc2l6ZTogMTZweDsKfQpwcmUgewpmb250LXNpemU6IDE2cHgKfQpib2R5IC5tYWluLWNvbnRhaW5lciB7Cm1heC13aWR0aDogMTYwMHB4Owp9Cjwvc3R5bGU+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KGhwZ2x0b29scykKbGlicmFyeShkcGx5cikKbGlicmFyeShlbnJpY2hwbG90KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShnZ3N0YXRzcGxvdCkKbGlicmFyeShncHJvZmlsZXIyKQp0dCA8LSB0cnkoZGV2dG9vbHM6OmxvYWRfYWxsKCJ+L2hwZ2x0b29scyIpKQprbml0cjo6b3B0c19rbml0JHNldCgKICBwcm9ncmVzcyA9IFRSVUUsIHZlcmJvc2UgPSBUUlVFLCB3aWR0aCA9IDkwLCBlY2hvID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVycm9yID0gVFJVRSwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDgsIGZpZy5yZXRpbmEgPSAyLAogIG91dC53aWR0aCA9ICIxMDAlIiwgZGV2ID0gInBuZyIsCiAgZGV2LmFyZ3MgPSBsaXN0KHBuZyA9IGxpc3QodHlwZSA9ICJjYWlyby1wbmciKSkpCm9sZF9vcHRpb25zIDwtIG9wdGlvbnMoZGlnaXRzID0gNCwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLCBrbml0ci5kdXBsaWNhdGUubGFiZWwgPSAiYWxsb3ciKQpnZ3Bsb3QyOjp0aGVtZV9zZXQoZ2dwbG90Mjo6dGhlbWVfYncoYmFzZV9zaXplID0gMTIpKQp2ZXIgPC0gIjIwMjQwOCIKcHJldmlvdXNfZmlsZSA8LSAiIgp2ZXIgPC0gZm9ybWF0KFN5cy5EYXRlKCksICIlWSVtJWQiKQoKIyN0bXAgPC0gc20obG9hZG1lKGZpbGVuYW1lPXBhc3RlMChnc3ViKHBhdHRlcm49IlxcLlJtZCIsIHJlcGxhY2U9IiIsIHg9cHJldmlvdXNfZmlsZSksICItdiIsIHZlciwgIi5yZGEueHoiKSkpCnJtZF9maWxlIDwtICJpcHJnY19hbmFseXNlc18yMDI0MDguUm1kIgpzYXZlZmlsZSA8LSBnc3ViKHBhdHRlcm4gPSAiXFwuUm1kIiwgcmVwbGFjZSA9ICJcXC5yZGFcXC54eiIsIHggPSBybWRfZmlsZSkKYGBgCgojIFRPRE9zCgoqIENoZWNrIHRoaXMgZm9yIGNvcnJlY3RuZXNzLgoqIFJlb3JnYW5pemUgaXQKKiBHU1ZBIChAaGFuemVsbWFubkdTVkFHZW5lU2V0MjAxM2EpCiogQ29uc2lkZXIgZW1iZWRkaW5nIHNvbWUvbWFueS9hbGwgb2YgdGhlIEV4Y2VsIG91dHB1dHMgaW50byB0aGUgaHRtbAogIG91dHB1dCB2aWEgeGZ1bjo6ZW1iZWRfZGlyKCdleGNlbC8nKQoKIyBNZWV0aW5nIHdpdGggVGhlcmVzYQoKUHJldmlvdXMgcGFwZXJzIGRpZCBub3QgZG8gYW4gZXhwbGljaXQgc3VidHJhY3Rpb24sIGluc3RlYWQganVzdApjb21wYXJlZCB0byBXVCBhbmQga2VwdCB0aGUgZ2VuZXMgd2hpY2ggYXJlID4gaW4gZGVsdGEvaGV0IHZzLiB3dC4KVGhlcmUgYXJlIG11bHRpcGxlIHdheXMgdG8gZGVhbCB3aXRoIHRoaXMgYW5kIHRoYXQgcXVlcnkgaGFzIG5vdCB5ZXQKYmVlbiBkZWZpbmVkLiAgTGF0ZXIsIFRoZXJlc2EgY2FtZSB0byB0aGUgY29uY2x1c2lvbiB0aGF0IHRoZQpzdWJ0cmFjdGlvbiBtZXRob2QgaXMgbm90IGFwcHJvcHJpYXRlLgoKIyBJbnRyb2R1Y3Rpb24KCkluIHRoaXMgZG9jdW1lbnQgSSBob3BlIHRvIGV4cGxvcmUgdGhlIGZyZXNobHkgcHJvY2Vzc2VkIHNhbXBsZXMgYW5kCnBlcmZvcm0gc29tZSBjb21wYXJpc29ucyB0byBzZWUgdGhhdCB3ZSBoYXZlIHRoZSBleHBlY3RlZCBzaW1pbGFyaXRpZXMKYW5kIGRpZmZlcmVuY2VzIGZyb20gdGhlIHByaW9yIGFuYWx5c2lzIHBlcmZvcm1lZCBieSBUaGVyZXNhLgoKVGhlcmUgaXMgb25lIHdheSBpbiB3aGljaCBJIGV4cGVjdCBhbnkvYWxsIG9mIHRoZXNlIGFuYWx5c2VzIHRvIGJlCmV4cGxpY2l0bHkgZGlmZmVyZW50OiB0aGlzIHNob3VsZCBpbmNsdWRlIHRoZSBjaGFuZ2VzIHByb2R1Y2VkIGJ5CkFwcmlsJ3MgcmVuYW1pbmcgb2Ygc29tZSBzYW1wbGVzLgoKTXkgaW50ZW50aW9uIGlzIHRvIHByb2R1Y2UgYSBzYW1wbGUgc2hlZXQgd2hpY2ggaW5jbHVkZXMgb25lIGNvbHVtbgp3aXRoIG5vbi11bWktZGVkdXBsaWNhdGVkIHJlc3VsdHMgYW5kIG9uZSB3aXRoIGRlZHVwbGljYXRlZCByZXN1bHRzLgpXaXRoIHRoZSBleGNlcHRpb24gb2YgdGhlIHByZXZpb3VzIHBvaW50LCBJIGhvcGUgdGhhdCB0aGUgZmlyc3Qgd2lsbApiZSBpZGVudGljYWwgKG9yIGF0IGxlYXN0IHZlcnkgY2xvc2UgdG8gaWRlbnRpY2FsKSB0byBUaGVyZXNhJ3MgcmVzdWx0CndoaWxlIHRoZSBzZWNvbmQgSSBleHBlY3Qgd2lsbCBiZSBzdWJ0bHkgZGlmZmVyZW50IC0tIGJ1dCBJIGFtIGhvcGluZwpzdWJ0bHkgZW5vdWdoIHRoYXQgaXQgd2lsbCBub3Qgc2lnbmlmaWNhbnRseSBjaGFuZ2UgdGhlIGludGVycHJldGF0aW9uCmJ1dCBiZSBhIGxpdHRsZSBtb3JlIHByZWNpc2UuCgpMZXRzIHNlZSEgIEkgbmVlZCB0aGVyZWZvcmUgdG8gbWFrZSBhIGNoYW5nZSB0byBteSBtZXRhZGF0YSBnYXRoZXJpbmcKZnVuY3Rpb24gdG8gaW5jbHVkZSB0aGUgdW1pIGRlZHVwbGljYXRlZCByZXN1bHQuICBJIGFtIHRoaW5raW5nCnRoZXJlZm9yZSB0byBjcmVhdGUgYSBzZXBhcmF0ZSBzcGVjaWZpY2F0aW9uIGZvciB1bWktYmFyY29kZWQgc2FtcGxlcwpiZWNhdXNlIGxvb2tpbmcgdGhyb3VnaCB0aGUgbG9ncyBmb3IgdW1pIHN0dWZmIHdoZW4gdGhleSBhcmUgbm90IHVzZWQKd2lsbCBiZSB0b28gbXVjaCBvZiBhIHBhaW4uLi4KCiMjIFNtYWxsIHJhbmRvbSByZW1pbmRlcgoKSSBoYXZlIGEgY291cGxlIHBpY3R1cmVzIG9mIFJQTDIyIHRvIGhlbHAgbWUgcmVtZW1iZXIgdGhlIGV4cGVyaW1lbnRhbApkZXNpZ246CgoqIFRoZSBodW1hbiByaWJvc29tZSB3aXRoIFJQTDIyIGluIHJlZDogIVtodW1hbl9ycGwyMl9yZWRdKGh1bWFuX3JpYm9zb21lX0wyMl9yZWQucG5nKQoqIFRoZSBtb3VzZSByaWJvc29tZSB3aXRoIFJQTDIyIGluIGdyZWVuIGF0IHRoZSBjZW50ZXI6CiFbbW91c2VfcnBsMjJdKG1tdXNjdWx1c19SUEwyMl9ncmVlbl9jZW50ZXIucG5nKQoKVGhhdCBzZWNvbmQgcGljdHVyZSBjYW1lIGZyb206IChAbGlNYWxlR2VybWNlbGxzcGVjaWZpY1JpYm9zb21lMjAyMikKCiMgQSBub3RlIGFib3V0IGltcGxlbWVudGF0aW9uCgpJIHdvdWxkIGxpa2UgdG8gaW1wcm92ZSB0aGlzIGRvY3VtZW50IGJ5IGNvbXBhcmluZy9jb250cmFzdGluZyB0aGUKbWV0aG9kb2xvZ2llcyBwZXJmb3JtZWQgYnkgb3RoZXIgZ3JvdXBzIGFuZCB0aG9zZSBwZXJmb3JtZWQgYnkgbWUgaW4KaXQuICBJIG5ldmVyIGZ1bGx5IGFwcHJlY2lhdGVkIHRoZSBzdWl0ZSBvZiBjb21wdXRhdGlvbmFsIG1ldGhvZHMKYXBwbGllZCBieSBwcmV2aW91cyBncm91cHMgd2hlbiBleGFtaW5pbmcgVFJBUCBkYXRhOyBJIGluc3RlYWQgc2ltcGx5CmZvbGxvd2VkIFRoZXJlc2EncyBub3RlYm9vayB3aXRob3V0IGNvbnNpZGVyaW5nIG90aGVyIHBvc3NpYmlsaXRpZXMuCgpJIHRoZXJlZm9yZSBzcGVudCBhIGxpdHRsZSB0aW1lIHN0ZXBwaW5nIHRocm91Z2ggaGVyIHRoZXNpcyBhbmQKcHVsbGluZyBvdXQgdGhlIHJlbGV2YW50IHBhcGVycyBpbiB0aGUgaG9wZXMgb2YgbGVhcm5pbmcgdGhlc2UgdmFyaW91cwptZXRob2RzLiAgSSBzaG91bGQgdGhlcmVmb3JlIGJlIGFibGUgc29vbiB0byBjb21wYXJlL2NvbnRyYXN0IHRoZQp2YXJpb3VzIG1ldGhvZHMgZW1wbG95ZWQgYnkgb3RoZXIgbGFicyBpbiBhZGRpdGlvbiB0byBjb3B5aW5nClRoZXJlc2EncyBsb2dpYy4KCiMjIFRoZSBmb2xsb3dpbmcgYmxvY2sgY2Fubm90IHdvcmsgaW4gdGhlIGNvbnRhaW5lcgoKVGhlIGZvbGxvd2luZyBibG9jayBhc3N1bWVzIHRoZSBmdWxsIHRyZWUgb2YgcHJlcHJvY2Vzc2VkIGRhdGEgd2l0aAp0aGUgbG9ncyBmcm9tIHRoZSB0cmltbWVyLCBtYXBwaW5nLCB1bWkgZGVkdXBsaWNhdGlvbiwgY291bnRpbmcsIGV0Yy4KQXMgYSByZXN1bHQgaXQgY2Fubm90IHdvcmsgaW4gdGhlIGNvbnRhaW5lciB3aGljaCBoYXMgb25seSB0aGUgdmFyaW91cwpjb3VudCB0YWJsZXMuCgpBcyBhIHJlc3VsdCwgSSBhbSBpbmNsdWRpbmcgYSBjb3B5IG9mIHRoaXMgc2hlZXQgYWZ0ZXIgcnVubmluZyB0aGUKZm9sbG93aW5nIGJsb2NrIGluIG15IHdvcmtpbmcgdHJlZS4gIEkgc3VwcG9zZSBmb3IgdGhlIG1vbWVudCB5b3Ugd2lsbApoYXZlIHRvIHRydXN0IHRoYXQgaXQgd29ya2VkLiAgKGZvciByaWdodCBub3csIHdoZW4gdGVzdGluZyBvdXQgdGhpcwpjb250YWluZXIsIEkgYW0ganVzdCBzZW5kaW5nIHRoZSBSIHdvcmtpbmcgZGlyZWN0b3J5IHRvIG15IHRyZWUgZm9yCnRoaXMgYmxvY2ssIHRoZW4gbW92aW5nIGl0IGJhY2suCgpJIHdpbGwgbmVlZCB0byBtYW51YWxseSBlZGl0IG9uZSBjb2x1bW4gdGhvdWdoLCB0aGUgc3ltbGluayBjb2x1bW4KZnJvbSBUaGVyZXNhIGhhcyBhIHNlcmllcyBvZiBwYXRocyB3aGljaCBkbyBub3Qgd29yayBpbiB0aGUgY29udGFpbmVyLgoKCmBgYHtyLCBldmFsPUZBTFNFfQp1bWlfc3BlYyA8LSBtYWtlX3JuYXNlcV9zcGVjKHVtaSA9IFRSVUUpCmlwcmdjXzIwMjJfbWV0YSA8LSBnYXRoZXJfcHJlcHJvY2Vzc2luZ19tZXRhZGF0YSgic2FtcGxlX3NoZWV0cy8yMDI0MDYwNl9vbmx5X3VtZF9zZXF1ZW5jZWQueGxzeCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGVjID0gdW1pX3NwZWMsIHNwZWNpZXMgPSAibW0zOV8xMTIiLCB2ZXJib3NlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYXNlZGlyID0gInByZXByb2Nlc3NpbmcvdW1kX3NlcXVlbmNlZCIpCmNvbG5hbWVzKGlwcmdjXzIwMjJfbWV0YVtbIm5ld19tZXRhIl1dKQpoZWFkKGlwcmdjXzIwMjJfbWV0YVtbIm5ld19tZXRhIl1dKQpgYGAKCmBgYHtyfQpzYW1wbGVfc2hlZXQgPC0gInNhbXBsZV9zaGVldHMvMjAyNDA2MDZfb25seV91bWRfc2VxdWVuY2VkX21vZGlmaWVkLnhsc3giCmBgYAoKRnJvbSB0aGlzIHBvaW50IG9uLCBJIGFtIGhvcGluZy9pbnRlbmRpbmcgdG8gcHVsbCBsaWJlcmFsbHkgZnJvbQpUaGVyZXNhJ3Mgbm90ZWJvb2sgd2l0aCBhIGRpdmVyc2lvbiB0byBjb21wYXJlIHRoZSB0aHJlZSBkYXRhc2V0czoKCiogUHJlLUFwcmlsIHJlbmFtaW5nOiBFLmcuIFRoZXJlc2EncyBjdXJyZW50IGRhdGFzZXQKKiBQb3N0IHJlbmFtaW5nOiBVbmxlc3MgSSBhbSBtaXN0YWtlbiwgdGhpcyBzaG91bGQgYmUgdmVyeSBzaW1pbGFyIHRvCiAgdGhlIGFib3ZlLgoqIFBvc3QgZGVkdXBsaWNhdGlvbjogR2l2ZW4gd2hhdCBJIHNhdyBmcm9tIHRoZSBleHRyYWN0ZWQgbG9ncyBpbiB0aGUKICBzYW1wbGUgc2hlZXQsIEkgZXhwZWN0IHRoaXMgdG8gYmUgc2ltaWxhciBidXQgbm90IGlkZW50aWNhbCB0byB0aGUKICBwcmV2aW91cyB0d28uCgpMZXRzIGZpbmQgb3V0ISAgIEJ1dCBmaXJzdCwgYW5ub3RhdGlvbnMhCgojIEFubm90YXRpb24gZGF0YQoKSSBhbSBwdWxsaW5nIHRoaXMgZnJvbSBUaGVyZXNhJ3MgYW54b250cmFwUl9waXBlbGluZS5SbWQsIHByaW1hcmlseQpiZWNhdXNlIGl0IGxvb2tzIHNpbWlsYXIgdG8gdGhlIG90aGVyIGRvY3VtZW50cywgYnV0IHdhcyBtb2RpZmllZCBtb3JlCnJlY2VudGx5LiAgSSB3aWxsIGNoYW5nZSBpdCBzbGlnaHRseSwgcHJpbWFyaWx5IGJlY2F1c2UgSSBncmFiYmVkIGEKbmV3IG1tdXNjdWx1cyBhc3NlbWJseSBhbmQgdGhlcmVmb3JlIEkgd2lsbCBwdWxsIHRoZSBtbXVzY3VsdXMKYW5ub3RhdGlvbnMgZnJvbSBhIHNwZWNpZmljIGJpb21hcnQKKEBzbWVkbGV5QmlvTWFydEJpb2xvZ2ljYWxRdWVyaWVzMjAwOSkgYXJjaGl2ZSB0aGF0IHNob3VsZCBtYXRjaCBpdC4KCmBgYHtyfQptbV9hbm5vdCA8LSBsb2FkX2Jpb21hcnRfYW5ub3RhdGlvbnMoc3BlY2llcyA9ICJtbXVzY3VsdXMiLCB5ZWFyID0gIjIwMjMiLCBtb250aCA9ICIwMiIpCm1tX2Fubm90IDwtIG1tX2Fubm90W1siYW5ub3RhdGlvbiJdXQptbV9hbm5vdFtbInR4aWQiXV0gPC0gcGFzdGUwKG1tX2Fubm90W1siZW5zZW1ibF90cmFuc2NyaXB0X2lkIl1dLCAiLiIsIG1tX2Fubm90W1sidmVyc2lvbiJdXSkKcm93bmFtZXMobW1fYW5ub3QpIDwtIG1ha2UubmFtZXMobW1fYW5ub3RbWyJlbnNlbWJsX2dlbmVfaWQiXV0sIHVuaXF1ZT1UUlVFKQp0eF9nZW5lX21hcCA8LSBtbV9hbm5vdFssIGMoInR4aWQiLCAiZW5zZW1ibF9nZW5lX2lkIildCmBgYAoKIyBIaXNhdDIgZXhwcmVzc2lvbnNldHMKClRoZSBwcmltYXJ5IGRpZmZlcmVuY2UgYmV0d2VlbiBteSBibG9jayBhbmQgVGhlcmVzYSdzIGFyZToKCjEuICBJIGFtIHB1bGxpbmcgdGhlIG1ldGFkYXRhIGRpcmVjdGx5IGZyb20gdGhlCiAgICBnYXRoZXJfcHJlcHJvY2Vzc2luZ19tZXRhZGF0YSgpIGFib3ZlLgoyLiAgSSBhbSB1c2luZyB0aGUgY29sdW1uICdzeW1saW5rJyB3aGljaCBpcyBqdXN0IGEgY29weSBvZiB0aGUKICAgIGV4aXN0aW5nICdmaWxlJyBjb2x1bW4gd2l0aCBhIHNtYWxsIGNoYW5nZSBzbyBJIGNhbiBsb2FkIGl0IGZyb20KICAgIG15IGRpcmVjdG9yeSB3aXRob3V0IGhhdmluZyB0byBjb3B5IGV2ZXJ5dGhpbmcuCjMuICBJIGFtIHVzaW5nIHRoZSBlbnNlbWJsIGdlbm9tZSByZWxlYXNlIDM5LCB2ZXJzaW9uIDExMiBhbmQgc28KICAgIHB1bGxlZCBhIHNvbWV3aGF0IG5ld2VyIGNvcHkgb2YgdGhlIGFubm90YXRpb24gZGF0YS4KNC4gIFRoZSBvcmlnaW5hbCBpcyBuYW1lZCAndjEnLCBmb2xsb3dlZCBieSAndjInIGFuZCAndjMnIGZvciB0aGUKICAgIG90aGVyIHR3byB0cmVhdG1lbnRzIEkgcGVyZm9ybWVkLgoKIyMgQ29sb3IgY2hvaWNlcyBhbmQgcmV1c2VkIHBhcmFtZXRlcnMKCkdpdmVuIHRoYXQgd2UgYXJlIGV4Y2x1ZGluZyBhIGJ1bmNoIG9mIHRoZSBvbGRlciBzYW1wbGVzLCB0aGUgc2V0IG9mCmNvbG9ycyBJIGV4cGVjdCB0byBmaW5kIGlzIGRpZmZlcmVudDsgc28gSSB3aWxsIG1ha2UgZXhwbGljaXQgaGVyZSB0aGUKdmFyaW91cyBjb2xvcnMgdXNlZCB0byBkZW5vdGUgbG9jYXRpb24vZ2Vub3R5cGUvdGltZS9ldGMuCgpBcHJpbCB0dXJuZWQgbWUgb250byB0aGlzIHdlYnNpdGUgJ3BhbGV0dG9uLmNvbScgZm9yIHRoaXMga2luZCBvZgpzdHVmZiBhbmQgSSB3aWxsIHRyeSBhbmQgcGljayBvdXQgcGFsZXR0ZXMgd2hpY2ggYmFzaWNhbGx5IG1hdGNoIHdoYXQKSSBhbSBnZXR0aW5nIHdpdGggdGhlIG9yaWdpbmFsIGNvbG9ycy4KCmBgYHtyfQpjb2xvcl9jaG9pY2VzIDwtIGxpc3QoCiAgImFsbCIgPSBsaXN0KAogICAgInAwOF9oZXRfZGxnbiIgPSAiI0U3Mjk4QSIsCiAgICAicDE1X2hldF9kbGduIiA9ICIjRTcyOThBIiwKICAgICJwMDhfaGV0X3JldGluYSIgPSAiIzIzOEI0NSIsCiAgICAicDE1X2hldF9yZXRpbmEiID0gIiMyMzhCNDUiLAogICAgInAwOF9oZXRfc2NuIiA9ICIjNDI5MkM2IiwKICAgICJwMTVfaGV0X3NjbiIgPSAiIzQyOTJDNiIsCiAgICAicDA4X2tvX2RsZ24iID0gIiNDOTk0QzciLAogICAgInAxNV9rb19kbGduIiA9ICIjQzk5NEM3IiwKICAgICJwMDhfa29fcmV0aW5hIiA9ICIjNzRjNDc2IiwKICAgICJwMTVfa29fcmV0aW5hIiA9ICIjNzRjNDc2IiwKICAgICJwMDhfa29fc2NuIiA9ICIjOUJDQUUxIiwKICAgICJwMTVfa29fc2NuIiA9ICIjOUJDQUUxIiwKICAgICJwMDhfd3RfZGxnbiIgPSAiIzk4MDA0MyIsCiAgICAicDE1X3d0X2RsZ24iID0gIiM5ODAwNDMiLAogICAgInAwOF93dF9yZXRpbmEiID0gIiMwMDQwMDgiLAogICAgInAxNV93dF9yZXRpbmEiID0gIiMwMDQwMDgiLAogICAgInAwOF93dF9zY24iID0gIiMwODUxOUMiLAogICAgInAxNV93dF9zY24iID0gIiMwODUxOUMiLAogICAgInA2MF93dF9kbGduIiA9ICIjMzMzMzMzIiwKICAgICJwNjBfd3RfcmV0aW5hIiA9ICIjMjIyMjIyIiwKICAgICJwNjBfd3Rfc2NuIiA9ICIjMTExMTExIiksCiAgImdlbm9fbG9jIiA9IGxpc3QoCiAgICAiaGV0X2RsZ24iID0gIiNFNzI5OEEiLAogICAgImhldF9yZXRpbmEiID0gIiMyMzhCNDUiLAogICAgImhldF9zY24iID0gIiM0MjkyQzYiLAogICAgImtvX2RsZ24iID0gIiNDOTk0QzciLAogICAgImtvX3JldGluYSIgPSAiIzc0YzQ3NiIsCiAgICAia29fc2NuIiA9ICIjOUJDQUUxIiwKICAgICJ3dF9kbGduIiA9ICIjOTgwMDQzIiwKICAgICJ3dF9yZXRpbmEiID0gIiMwMDQwMDgiLAogICAgInd0X3NjbiIgPSAiIzA4NTE5QyIpLAogICJsb2NhdGlvbiIgPSBsaXN0KAogICAgInJldGluYSIgPSAiIzAwNDAwOCIsCiAgICAiZGxnbiIgPSAiIzk4MDA0MyIsCiAgICAic2NuIiA9ICIjMDg1MTlDIiksCiAgImdlbm90eXBlIiA9IGxpc3QoCiAgICAid3QiID0gIiM3NGM0NzYiLAogICAgImhldCIgPSAiIzIzOEI0NSIsCiAgICAia28iID0gIiMwMDZEMkMiKSwKICAidGltZSIgPSBsaXN0KAogICAgInAwOCIgPSAiIzVFMTA0QiIsCiAgICAicDE1IiA9ICIjNEU5MjMxIikpCmxhYmVsX2NvbHVtbiA8LSAibWdpX3N5bWJvbCIgIyMgU2V0IHRoZSBjb2x1bW4gdXNlZCB0byBleHRyYWN0IGdlbmUgc3ltYm9scyByYXRoZXIgdGhhbiBFTlNHLi4uLi4KY29sb3JzIDwtIGNvbG9yX2Nob2ljZXNbWyJnZW5vX2xvYyJdXQpgYGAKClRoZXJlIGlzIG9uZSBub3Rld29ydGh5IHNhbXBsZTogaXByZ2NfMTAzLCBpdCB3YXMgZWZmZWN0aXZlbHkgcmVwbGFjZWQKd2hlbiBBcHJpbCByZW5hbWVkIHRoZSBzYW1wbGVzIGFuZCBzbyBleGlzdHMgaW4gdGhlIHYxIGRhdGEsIGJ1dCBub3QKdjIvdjM7IHRoZXkgaW5zdGVhZCBoYXZlIHRoZSBuZXdseSBuYW1lZCBzYW1wbGVzIHdoaWNoIEkgY2FsbGVkCmlwcmdjXzEyMyB0byBpcHJnY18xMzAuICBBcyBhIHJlc3VsdCwgSSBjb3BpZWQgdGhlIGFubm90YXRpb25zIGZvcgppcHJnY18xMjMgdG8gbXkgY29sdW1uIHNvIHRoYXQgdGhlcmUgaXMgbm8gZGlzY3JlcGVuY3kgaW4gdGVybXMgb2YKZ2Vub3R5cGUvbG9jYXRpb24vdGltZS4KCiMjIFRoZSBvcmlnaW5hbCBjb3VudCB0YWJsZXMKCkF0IHRoZSBtb21lbnQgSSBoYXZlIG5vdCBpbmNsdWRlZCB0aGUgb3JpZ2luYWwgY291bnRzIGluIHRoaXMKY29udGFpbmVyIGJlY2F1c2Ugd2UgbWFkZSBzb21lIGNoYW5nZXMgdG8gdGhlIG1hcHBpbmcgc3RyYXRlZ3kgYW5kCmFsc28gZm91bmQgdGhhdCBhIGNvdXBsZSBzYW1wbGVzIHdlcmUgbWl4ZWQgdXAgaW4gc2VxdWVuY2luZzsgYXMgYQpyZXN1bHQgSSBkb2N1bWVudGVkIGFsbCBvZiB0aGUgY2hhbmdlcyBpbiB0aGUgc2FtcGxlIHNoZWV0cyBhbmQKcHJlcHJvY2Vzc2luZyBkb2N1bWVudHMgYW5kIGV4Y2x1ZGVkIHRoZSBvcmlnaW5hbCBmaWxlcy4KClRoaXMgaXMgYWxzbyB3aHkgc29tZSBjb2x1bW5zIGluIHRoZSBzYW1wbGUgc2hlZXQgaGF2ZSBzdWZmaXhlcyBsaWtlCidhZGgnIGFuZCAnYXRiJywgdGhvc2UgZGVub3RlIGZyb20gd2hvbSB0aGUgcmVsZXZhbnQgbWV0YWRhdGEgY29sdW1ucwpjYW1lIGZyb20uCgpgYGB7ciwgZXZhbD1GQUxTRX0KbW0zOF9oaXNhdF92MSA8LSBjcmVhdGVfZXhwdChzYW1wbGVfc2hlZXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9pbmZvID0gbW1fYW5ub3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZV9jb2x1bW4gPSAic3ltbGluayIpICU+JQogIHNldF9leHB0X2NvbmRpdGlvbnMoZmFjdCA9ICJnZW5vX2xvY19hdGIiKSAlPiUKICBzZXRfZXhwdF9iYXRjaGVzKGZhY3QgPSAidGltZV9hdGIiKSAlPiUKICBzZXRfZXhwdF9jb2xvcnMoY29sb3JfY2hvaWNlc1tbImdlbm9fbG9jIl1dKQptbTM4X2hpc2F0X3YxCmBgYAoKIyMgUmVjb3VudGVkIHRhYmxlcyBhbmQgdGhlIGRlZHVwbGljYXRlZCByZXN1bHQKCkluIHRoZSBmb2xsb3dpbmcgSSBtYWtlIHR3byBtb3JlIHZlcnNpb25zIG9mIHRoZSBkYXRhLCBvbmUgcmVtYXBwZWQKd2l0aCB0aGUgY2hhbmdlcyB0byB0aGUgc2FtcGxlIGlkZW50aXRpZXMsIGFuZCBvbmUgd2l0aCBkZWR1cGxpY2F0aW9uCmFwcGxpZWQuCgpgYGB7cn0KbW0zOF9oaXNhdF92MiA8LSBjcmVhdGVfZXhwdChzYW1wbGVfc2hlZXQsIGdlbmVfaW5mbyA9IG1tX2Fubm90LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVfY29sdW1uID0gImhpc2F0X2NvdW50X3RhYmxlIikgJT4lCiAgc2V0X2V4cHRfY29uZGl0aW9ucyhmYWN0ID0gImdlbm9fbG9jX2F0YiIpICU+JQogIHNldF9leHB0X2JhdGNoZXMoZmFjdCA9ICJ0aW1lX2F0YiIpICU+JQogIHNldF9leHB0X2NvbG9ycyhjb2xvcl9jaG9pY2VzW1siZ2Vub19sb2MiXV0pCm1tMzhfaGlzYXRfdjIKCm1tMzhfaGlzYXRfdjMgPC0gY3JlYXRlX2V4cHQoc2FtcGxlX3NoZWV0LCBnZW5lX2luZm8gPSBtbV9hbm5vdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlX2NvbHVtbiA9ICJ1bWlfZGVkdXBfb3V0cHV0X2NvdW50IikgJT4lCiAgc2V0X2V4cHRfY29uZGl0aW9ucyhmYWN0ID0gImdlbm9fbG9jX2F0YiIpICU+JQogIHNldF9leHB0X2JhdGNoZXMoZmFjdCA9ICJ0aW1lX2F0YiIpICU+JQogIHNldF9leHB0X2NvbG9ycyhjb2xvcl9jaG9pY2VzW1siZ2Vub19sb2MiXV0pCm1tMzhfaGlzYXRfdjMKCmFsbF9mYWN0IDwtIHBhc3RlMChwRGF0YShtbTM4X2hpc2F0X3YzKVtbInRpbWVfYXRiIl1dLCAiXyIsCiAgICAgICAgICAgICAgICAgICBwRGF0YShtbTM4X2hpc2F0X3YzKVtbImdlbm9fbG9jX2F0YiJdXSkKcERhdGEobW0zOF9oaXNhdF92MylbWyJ0aW1lX2dlbm9fbG9jIl1dIDwtIGFsbF9mYWN0CmBgYAoKTm90ZSB0aGUgZW5kIG9mIHRoZSBwcmV2aW91cyBibG9jaywgSSBjcmVhdGVkIGEgZmFjdG9yIG91dCBvZiB0aGUKY29tYmluYXRpb24gb2YgdGltZSwgZ2Vub3R5cGUsIGFuZCBsb2NhdGlvbi4gIEluIGEgZnV0dXJlIGludm9jYXRpb24Kb2YgdGhpcyBub3RlYm9vaywgSSB3aWxsIGNoYW5nZSB0aGUgcGFpcndpc2UgY29tcGFyaXNvbnMgdG8gYWRkIGVhY2gKb2YgdGhlc2UgdGhyZWUgZmFjdG9ycyB0byB0aGUgc3RhdGlzdGljYWwgbW9kZWwgaW5zdGVhZCBvZiB0aGlzLiAgVGhlCmNvZGUgdG8gZG8gdGhhdCBpcyBub3QgX3F1aXRlXyByZWFkeSB5ZXQuCgojIE5vbi16ZXJvIENvdW50cyBwZXIgU2FtcGxlCgpMZXQncyBsb29rIGF0IHRoZSBudW1iZXIgb2Ygbm9uLXplcm8gZ2VuZXMgZm9yIGFsbCBzYW1wbGVzIHZlcnN1cyB0aGUKY292ZXJhZ2UuCgpBcyBhYm92ZSwgdGhpcyBkb2VzIG5vdCBnZXQgcnVuIGJlY2F1c2UgSSBkaWQgbm90IGNvcHkgdGhlIGNvdW50IHRhYmxlcy4KCmBgYHtyIGV2YWw9RkFMU0V9CnYxX25vbnplcm8gPC0gcGxvdF9ub256ZXJvKG1tMzhfaGlzYXRfdjEpCnYxX25vbnplcm8KYGBgCgpCdXQgdGhlc2UgZG8hCgpgYGB7cn0KcGxvdF9sZWdlbmQobW0zOF9oaXNhdF92MikKdjJfbm9uemVybyAgPC0gcGxvdF9ub256ZXJvKG1tMzhfaGlzYXRfdjIpCnYyX25vbnplcm8KcHAoZmlsZSA9ICJpbWFnZXMvbm9uemVyb192Ml91bmZpbHRlcmVkLnBkZiIpCnYyX25vbnplcm9bWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCgp2M19ub256ZXJvICA8LSBwbG90X25vbnplcm8obW0zOF9oaXNhdF92MykKdjNfbm9uemVybwpwcChmaWxlID0gImltYWdlcy9ub256ZXJvX3YzX3VuZmlsdGVyZWQucGRmIikKdjNfbm9uemVyb1tbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKYGBgCgpPaCB3b3csIEkgZGlkIG5vdCBleHBlY3Qgc3VjaCBhIHByb2ZvdW5kIGVmZmVjdCBvbiB0aGUgY3BtIHZhbHVlcyBvbgp0aGUgbW9yZSBzYXR1cmF0ZWQgbGlicmFyaWVzLiAgSSBndWVzcyBpbiByZXRyb3NwZWN0IEkgc2hvdWxkIGhhdmU/CgpBbHNvIG5vdGUgdG8gc2VsZiwgd2UgYXJlIG5vdCBtZXNzaW5nIHdpdGggcDYwLgoKYGBge3J9Cm1tMzhfaGlzYXRfdjIgPC0gc3Vic2V0X2V4cHQobW0zOF9oaXNhdF92Miwgc3Vic2V0ID0gInRpbWVfYXRiIT0ncDYwJyIpCnYyX25vbnplcm9fZmlsdCA8LSBwbG90X25vbnplcm8obW0zOF9oaXNhdF92MiwgcGxvdF9sYWJlbHMgPSBGQUxTRSkKcHAoZmlsZSA9ICJpbWFnZXMvbm9uemVyb192Ml9maWx0LnBkZiIpCnYyX25vbnplcm9fZmlsdFtbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKbW0zOF9oaXNhdF92MyA8LSBzdWJzZXRfZXhwdChtbTM4X2hpc2F0X3YzLCBzdWJzZXQgPSAidGltZV9hdGIhPSdwNjAnIikKdjNfbm9uemVyb19maWx0IDwtIHBsb3Rfbm9uemVybyhtbTM4X2hpc2F0X3YzLCBwbG90X2xhYmVscyA9IEZBTFNFKQpwcChmaWxlID0gImltYWdlcy9ub256ZXJvX3YzX2ZpbHQucGRmIikKdjNfbm9uemVyb19maWx0W1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpgYGAKCk9uY2UgYWdhaW4sIEkgZG8gbm90IHdhbnQgdG8gbG9zZSB0aGUgcHJldmlvdXMgY29kZSwgc28gaGVyZSBpcyB0aGUgdjEgaW52b2NhdGlvbgoKYGBge3IsIGV2YWw9RkFMU0V9Cm1tMzhfaGlzYXRfdjEgPC0gc3Vic2V0X2V4cHQobW0zOF9oaXNhdF92MSwgc3Vic2V0ID0gInRpbWVfYXRiIT0ncDYwJyIpCmBgYAoKIyBRdWljayBQQ0EsIHRoZW4gcmV0dXJuIHRvIFRoZXJlc2EncyBkb2N1bWVudAoKYGBge3J9CnYyX25vcm0gPC0gbm9ybWFsaXplX2V4cHQobW0zOF9oaXNhdF92MiwgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybSA9ICJxdWFudCIsIGZpbHRlciA9IFRSVUUpCnYyX25vcm1fcGNhIDwtIHBsb3RfcGNhKHYyX25vcm0pCnYyX25vcm1fcGNhCnBwKGZpbGUgPSAiaW1hZ2VzL3YyX25vcm1fcGNhLnBkZiIpCnYyX25vcm1fcGNhW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQp2Ml9ub3JtX3BjYVtbInBsb3QiXV0KCnYzX25vcm0gPC0gbm9ybWFsaXplX2V4cHQobW0zOF9oaXNhdF92MywgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybSA9ICJxdWFudCIsIGZpbHRlciA9IFRSVUUpCnYzX25vcm1fcGNhIDwtIHBsb3RfcGNhKHYzX25vcm0pCnBwKGZpbGUgPSAiaW1hZ2VzL3YzX25vcm1fcGNhLnBkZiIpCnYzX25vcm1fcGNhW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQp2M19ub3JtX3BjYVtbInBsb3QiXV0KYGBgCgpJYmlkLgoKYGBge3IsIGV2YWw9RkFMU0V9CnYxX25vcm0gPC0gbm9ybWFsaXplX2V4cHQobW0zOF9oaXNhdF92MSwgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybSA9ICJxdWFudCIsIGZpbHRlciA9IFRSVUUpCnBsb3RfcGNhKHYxX25vcm0pCmBgYAoKVG8gbXkgZXllcyBpdCBsb29rcyBsaWtlIHdlIGp1c3QgaGF2ZSAxIHdlaXJkbyBwMTUgc2FtcGxlPwpEZWR1cGxpY2F0aW9uIGhhZCBhIG1pbm9yIGJ1dCBzaWduaWZpY2FudCBlZmZlY3Qgb24gdGhlIFBDQS4KCldpdGggdGhhdCBpbiBtaW5kLCBsZXQgdXMgbG9vayBhdCBUaGVyZXNhJ3MgV09SS0lORyBkb2N1bWVudCBhbmQgc2VlCndoYXQgd2UgY2FuIHJlY2FwaXR1bGF0ZS4KClRoZXJlc2EncyBkb2N1bWVudDogVGhlIFRSQVAgcHJvdG9jb2wgaGFzIHNvbWUgdmFyaWFiaWxpdHkgd2hpY2ggaXMKaW50cm9kdWNlZCBhdCBkaWZmZXJlbnQgc3RwZGYgaW5jbHVkaW5nIGhvbW9nZW5pemF0aW9uLCBhbnRpYm9keQpsYWJlbGluZywgcHVsbGRvd24gZWZmaWNpZW5jeS9zcGVjaWZpY2l0eSwgc2FtcGxlIGhhbmRsaW5nIGR1cmluZwpjbGVhbnVwIHN0cGRmLCBhbmQgbGlicmFyeSBwcmVwL3NlcXVlbmNpbmcuIFdlIGtub3cgZnJvbSBSYXNobWkncyBRQwp0aGF0IHRoZXJlIGlzIHZhcmlhYmlsaXR5IGF0IHRoZSBsZXZlbCBvZiBwdWxsZG93biBlZmZpY2llbmN5IChhbW91bnQKb2YgUk5BIGlzb2xhdGVkKS4gU2hlIGlzIGRvaW5nIGEgZ29vZCBqb2Igb2Yga2VlcGluZyB0cmFjayBvZiB0aGlzIGZvcgphbGwgaGVyIHNhbXBsZXMgYW5kIHdlIGhhdmUgdmFsaWRhdGVkIGhlciBQOCByZXN1bHRzIChhdHRhY2hlZApzdXBwbGVtZW50YXJ5IGZpZ3VyZSAzRCkuIFdlIGNvbnNpc3RlbnRseSBzZWUgY2xlYXIgZGlmZmVyZW5jZXMKYmV0d2VlbiBjb250cm9sIGFuZCBjcmUgc2FtcGxlcyBmb3IgdGhlIHJldGluYSwgd2hpY2ggbWFrZXMgc2Vuc2UKYmVjYXVzZSB0aGUgY2VsbCBib2RpZXMgYXJlIGluIHRoZSByZXRpbmEuIFRoZSB0YXJnZXQgdGlzc3VlCmRpZmZlcmVuY2VzIGFyZSBzbWFsbGVyLCB3aGljaCBhbHNvIG1ha2VzIHNlbnNlIGZvciBheG9uLVRSQVAuIFdlCnRoaW5rIHRoYXQgc29tZSBvZiBoZXIgUDE1IHNhbXBsZXMgYXJlIG5vdCBnb29kIGJhc2VkIG9uIGxvdyBhbW91bnRzCm9mIGlzb2xhdGVkIFJOQSBmcm9tIGNyZSgrKSByZXRpbmEgc2FtcGxlcy4gV2UgcGxhbiB0byBkcm9wIHRoZXNlCnNhbXBsZXMgYW5kIG5vdCBwZXJmb3JtIGFkZGl0aW9uYWwgaXNvbGF0aW9ucyBhdCB0aGlzIHRpbWUKcG9pbnQuIEJhc2VkIG9uIHRoaXMgKGFuZCB0aGUgZ2VuZXJhbCBsYWNrIG9mIGxhcmdlIGRldmVsb3BtZW50YWwKZWZmZWN0cyksIHdlIHdlcmUgcGxhbm5pbmcgdG8gZm9jdXMgb24gcHJlc2VudGluZyB0aGUgUDggZGF0YSBvbmx5IGluCnRoZSBwYXBlci4gSW50ZXJlc3RlZCB0byBoZWFyIHlvdXIgdGhvdWdodHMgaW4gdGhpcy4uLgoKTXkgbm90ZXM6IFRoZXJlc2EncyBmaXJzdCBvcGVyYXRpb25zIGluIHRoaXMgbm90ZWJvb2sgd2VyZSB0bzoKCjEuICBTZXQgbG9jYXRpb24gYXMgY29uZGl0aW9uLCBnZW5vdHlwZSBhcyBiYXRjaC4KMi4gIFBlcmZvcm0gUENBIGJlZm9yZS9hZnRlciBzdmEuCgpgYGB7cn0KdjNfbG9jX2dlbm8gPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhtbTM4X2hpc2F0X3YzLCBmYWN0ID0gImxvY2F0aW9uX2F0YiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gY29sb3JfY2hvaWNlc1tbImxvY2F0aW9uIl1dKSAlPiUKICBzZXRfZXhwdF9iYXRjaGVzKGZhY3QgPSAiZ2Vub3R5cGVfYXRiIikKYGBgCgojIyBUaGUgYXNzb2NpYXRlZCBQQ0EKCkF0IGRpZmZlcmVudCB0aW1lcywgaXQgYXBwZWFycyB0byBtZSB0aGF0IFRoZXJlc2EgaGFzIHByZWZlcnJlZApzbGlnaHRseSBkaWZmZXJlbnQgbm9ybWFsaXphdGlvbiBtZXRob2RzLCBwcmltYXJpbHkgYSBtaXggb2YgVE1NIGFuZApxdWFudGlsZS4KClRodXMgSSB3aWxsIHVzZSBkaWZmZXJlbnQgc3VmZml4IGxldHRlcnMgdG8gZGVub3RlIHZhcmlvdXMKbm9ybWFsaXphdGlvbnMgZW1wbG95ZWQsIGFuZCBpZiB0aGV5IHR1cm4gb3V0IHRoZSBzYW1lIEkgd2lsbCBwaWNrIG9uZSBhcmJpdHJhcmlseS4KCmBgYHtyfQpsb2NfZ2Vub19ucSA8LSBub3JtYWxpemVfZXhwdCh2M19sb2NfZ2VubywgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IFRSVUUsIG5vcm0gPSAicXVhbnQiKQpsb2NhdGlvbl9nZW5vdHlwZV9wY2EgPC0gcGxvdF9wY2EobG9jX2dlbm9fbnEpCnBwKGZpbGUgPSAiaW1hZ2VzL2xvY2F0aW9uX2dlbm90eXBlX25vcm1fcGNhLnBkZiIpCmxvY2F0aW9uX2dlbm90eXBlX3BjYVtbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKbG9jYXRpb25fZ2Vub3R5cGVfcGNhW1sicGxvdCJdXQojIyBvaywgSSBoYXZlIHR3byB3ZWlyZG8gc2FtcGxlcyB3aGljaCBsb29rIHZlcnkgbXVjaCBsaWtlIHRoZXkgYXJlIGFjdHVhbGx5IGRsZ24uCiMjIFRoZXNlIGFyZSBzYW1wbGUgSURzIGlwcmdjXzY2IGFuZCBpcHJnY18xMzAKCmxvY19nZW5vX250IDwtIG5vcm1hbGl6ZV9leHB0KHYzX2xvY19nZW5vLCB0cmFuc2Zvcm0gPSAibG9nMiIsIGNvbnZlcnQgPSAiY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwgbm9ybSA9ICJ0bW0iKQpsb2NhdGlvbl9nZW5vdHlwZV90bW1fcGNhIDwtIHBsb3RfcGNhKGxvY19nZW5vX250KQpwcChmaWxlID0gImltYWdlcy9sb2NhdGlvbl9nZW5vdHlwZV90bW1fcGNhLnBkZiIpCmxvY2F0aW9uX2dlbm90eXBlX3RtbV9wY2FbWyJwbG90Il1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmxvY2F0aW9uX2dlbm90eXBlX3RtbV9wY2FbWyJwbG90Il1dCmBgYAoKQSByYW5kb20gdGhvdWdodCBhYm91dCB0aGVzZSBQQ0EgcGxvdHMsIGl0IG1pZ2h0IGJlIHdvcnRoIHdoaWxlIHRvIGFkZAphIHBhbmVsIGJlbG93IHRoZSBsZWdlbmQgd2l0aCB0aGUgc2FtcGxlIG51bWJlcnMgcGVyIGNvbmRpdGlvbi9iYXRjaC4KCk9mIGNvdXJzZSwgdGhlIHNhbWUgaW5mb3JtYXRpb24gaXMgcHJvdmlkZWQgaW4gYSBtb3JlIGZ1biBmYXNoaW9uIHZpYQpteSBzaWxseSBzYW5rZXkgZnVuY3Rpb246CgpgYGB7cn0Kc2FtcGxlX3NhbmtleSA8LSBwbG90X21ldGFfc2Fua2V5KHYzX2xvY19nZW5vLCBjb2xvcl9jaG9pY2VzID0gY29sb3JfY2hvaWNlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY3RvcnMgPSBjKCJnZW5vdHlwZV9hdGIiLCAibG9jYXRpb25fYXRiIiwgInRpbWVfYXRiIikpCnBwKGZpbGUgPSAiaW1hZ2VzL2Rlc2lnbl9zYW5rZXkucGRmIikKc2FtcGxlX3NhbmtleVtbImdncGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpzYW1wbGVfc2Fua2V5CmBgYAoKIyBBIFNob3J0IGNvbnZlcnNhdGlvbiB3aXRoIFJhc2htaQoKUmFzaG1pIGNhbWUgYnkgYW5kIHdlIGRpc2N1c3NlZCB0aGUgc2FtcGxlcyBhIGxpdHRsZS4gIFNoZSBzdWdnZXN0ZWQKdGhhdCBpcyBsaWtlbHkgdGhhdCB3ZSB3aWxsIG5lZWQgdG8gZXhjbHVkZSB0aGUgMjAyMjA1IHNhbXBsZXMsIHRoZXNlCm1heSBiZSBpZGVudGlmaWVkIGJ5IGEgZmV3IHdheXMsIG1vc3QgZWFzaWx5IEkgdGhpbmsgdmlhIHRoZQoncHJvamVjdF9haCcgY29sdW1uLCB0aGV5IGFyZSB0aGUgMDIxXzEgc2FtcGxlcy4KCk15IHNlbnNlIHdhcyB0aGF0IHNoZSBjb25jdXJyZWQgd2l0aCBteSBpbnRlcnByZXRhdGlvbiBvZiB0aGUgdW1pCmRlZHVwbGljYXRpb24sIHNvIEkgd2lsbCBjb250aW51ZSB1c2luZyB0aGUgZGVkdXBsaWNhdGVkIHJlc3VsdHMKZXhjbHVzaXZlbHksIGF0IGxlYXN0IGZvciBub3cuCgojIE1lbGFub3BzaW4gU2FuaXR5IENoZWNrCgpPbmUgb2YgVGhlcmVzYSdzIGZpcnN0IGNoZWNrcyB3YXMgd2lzZWx5IGZvciBtZWxhbm9wc2luLiAgTGV0IHVzCnJlcGVhdCBhIHZlcnNpb24gb2YgdGhpczoKCmBgYHtyfQpvcG40X2V4cHJzIDwtIGRhdGEuZnJhbWUoY29tYmluZWQgPSBwRGF0YShsb2NfZ2Vub19udClbWyJnZW5vX2xvY19hdGIiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhdGlvbiA9IHBEYXRhKGxvY19nZW5vX250KVtbImxvY2F0aW9uX2F0YiJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgIGdlbm90eXBlID0gcERhdGEobG9jX2dlbm9fbnQpW1siZ2Vub3R5cGVfYXRiIl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgb3BuID0gZXhwcnMobG9jX2dlbm9fbnQpWyJFTlNNVVNHMDAwMDAwMjE3OTkiLCBdKQoKZ3JvdXBlZHN0YXRzOjpncm91cGVkX3N1bW1hcnkob3BuNF9leHBycywgbG9jYXRpb24sIG9wbikKb3BuNF9sb2NhdGlvbiA8LSBnZ2JldHdlZW5zdGF0cyhkYXRhID0gb3BuNF9leHBycywgeCA9IGxvY2F0aW9uLCB5ID0gb3BuKQpwcChmaWxlID0gImltYWdlcy9nZ2JldHdlZW5fbG9jYXRpb24ucGRmIikKb3BuNF9sb2NhdGlvbgpwbG90dGVkIDwtIGRldi5vZmYoKQpvcG40X2xvY2F0aW9uCm9wbjRfZ2Vub3R5cGUgPC0gZ2diZXR3ZWVuc3RhdHMoZGF0YSA9IG9wbjRfZXhwcnMsIHggPSBnZW5vdHlwZSwgeSA9IG9wbikKcHAoZmlsZSA9ICJpbWFnZXMvZ2diZXR3ZWVuX2xvY2F0aW9uLnBkZiIpCm9wbjRfbG9jYXRpb24KcGxvdHRlZCA8LSBkZXYub2ZmKCkKb3BuNF9sb2NhdGlvbgpvcG40X2NvbWJpbmVkIDwtIGdnYmV0d2VlbnN0YXRzKGRhdGEgPSBvcG40X2V4cHJzLCB4ID0gY29tYmluZWQsIHkgPSBvcG4pCnBwKGZpbGUgPSAiaW1hZ2VzL2dnYmV0d2Vlbl9jb21iaW5lZC5wZGYiKQpvcG40X2NvbWJpbmVkCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCm9wbjRfY29tYmluZWQKYGBgCgpvaywgc28gSSBwbG90dGVkIHRoZSBxdWVzdGlvbiBhIGJpdCBkaWZmZXJlbnRseSwgYnV0IGdvdCB0aGUgc2FtZQphbnN3ZXIuCgpIZXJlIGlzIHRoZSB0ZXh0IG9mIFRoZXJlc2EncyBub3RlYm9vayBmb2xsb3dpbmcgdGhpcyBhbmFseXNpczoKCiJVZ2ggb2gsIGxvb2tzIGxpa2UgdGhlcmUgaXMgYXQgbGVhc3Qgb25lIHJldGluYSBLTyBzYW1wbGUgdGhhdCBoYXMKc29tZSBtZWxhbm9wc2luIGV4cHJlc3Npb24gaW4gaXQuIFR1cm5zIG91dCBpcFJHQ18wNyBpcyBhIGJhZCBlZ2cKd2hpY2ggaXMgc3VwcG9zZWQgdG8gYmUgYSBLTyBidXQgaGFzIG1lbGFub3BzaW4gZXhwcmVzc2lvbi4gSXTigJlzCmZyaWVuZHMgd2hpY2ggd2VyZSBwb29sZWQgZnJvbSB0aGUgc2FtZSBtaWNlIGFyZSBpcHJnY18wNiBhbmQKaXByZ2NfMDgsIHNvIHdlIG5lZWQgdG8gZXhjbHVkZSBhbGwgdGhlc2Ugc2FtcGxlcy4iCgpJIGFtIGFsc28gc2VlaW5nIHNvbWUga25vY2tvdXQgZXhwcmVzc2lvbiB3aXRoIHNvbWUgY2F2ZWF0czogSSBkbyBub3QKaGF2ZSB0aGUgYWZmZWN0ZWQgc2FtcGxlcyBpbiBteSBkYXRhc2V0IChpcHJnY18wNykgYW5kIHRoZSBsZXZlbHMgSSBhbQpzZWVpbmcgYXJlIHF1aXRlIGxvdyAtLSBJIHdpbGwgbG9vayBpbiBJR1YgdG8gZG91YmxlIGNoZWNrLCBidXQgSQpzdHJvbmdseSBzdXNwZWN0IHRoYXQgdGhlc2UgYXJlIHNvbWUgcGlkZGx5IHJlYWRzIG5lYXIgdGhlIFVUUnMuCgpPbndhcmQhCgojIExpYnJhcnkgc2l6ZXMgcG9zdC1kZWR1cGxpY2F0aW9uCgpUaGVyZXNhJ3MgbmV4dCBvcGVyYXRpb24gd2FzIHRvIHBlcmZvcm0gbGlic2l6ZS9ub256ZXJvIHBsb3RzLiAgSQphbHJlYWR5IGRpZCB0aGUgcHJlL3Bvc3QgZGVkdXBsaWNhdGlvbiBub256ZXJvLCBoZXJlIGlzIHRoZSBhbmFsYWdvdXMKbGlic2l6ZS4KCnYyIGlzIHByZS1kZWR1cGxpY2F0aW9uIGFuZCB2MyBpcyBwb3N0LgoKYGBge3J9CnBsb3RfbGlic2l6ZShtbTM4X2hpc2F0X3YyKQpwb3N0X2ZpbHRlcl9ub256ZXJvIDwtIHBsb3RfbGlic2l6ZShtbTM4X2hpc2F0X3YzLCB0ZXh0ID0gRkFMU0UpCnBwKGZpbGUgPSAiaW1hZ2VzL3Bvc3RfYWxsX2ZpbHRlcmVzX25vbnplcm8ucGRmIikKcG9zdF9maWx0ZXJfbm9uemVyb1tbInBsb3QiXV0KcGxvdHRlZCA8LSBkZXYub2ZmKCkKcG9zdF9maWx0ZXJfbm9uemVybwpgYGAKCkkgYW0gYSBiaXQgY29uY2VybmVkIGFib3V0IHNvbWUgb2YgdGhlc2UgbGlicmFyeSBzaXplcwpwb3N0LWRlZHVwbGljYXRpb24uCgpMZXQgdXMgbG9vayBhdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gcmVhZHMgYW5kIGR1cGxpY2F0aW9uLCB3aGljaCBJCmFzc3VtZSB3aWxsIGJlIHJlbGF0aXZlbHkgbGluZWFyLgoKYGBge3J9CnRlc3QgPC0gcERhdGEobW0zOF9oaXNhdF92MylbLCBjKCJoaXNhdF9nZW5vbWVfc2luZ2xlX2FsbCIsICJ1bWlfZGVkdXBfcGN0X3JlYWRzIildCnRlc3RfcGxvdCA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKHRlc3QsIGxvZXNzID0gVFJVRSkKdGVzdF9wbG90W1sic2NhdHRlciJdXQpgYGAKClRoZXJlc2EgYWxzbyBwcm9kdWNlZCBhIGRlbnNpdHkvc2FtcGxlIHBsb3QsIHRoYXQgbWlnaHQgcHJvdmUgcXVpdGUKdXNlZnVsIGZvciB0aGVzZSBkdWUgdG8gdGhlaXIgc2lnbmlmaWNhbnRseSBsYXJnZXIgdmFyaWFuY2UgYWNyb3NzCnNhbXBsZXMgKGR1ZSB0byBkZWR1cGxpY2F0aW9uKS4KCmBgYHtyfQptbTM4X2RlbnNpdHkgPC0gcGxvdF9kZW5zaXR5KGxvY19nZW5vX250KQptbTM4X2RlbnNpdHlbWyJwbG90Il1dICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCnBsb3RfYm94cGxvdChsb2NfZ2Vub19udCkKYGBgCgpUaGVyZSBpcyBzb21lIGRpZmZlcmVuY2UgYWNyb3NzIHNhbXBsZSBkZW5zaXRpZXMsIGJ1dCBpdCBpcyBub3QgdG9vCmNyYXp5dG93bi4KCiMgUENBIHBsb3RzCgojIyBQQ0Egb2YgYWxsIGdlbmVzIGJ5IGxvY2F0aW9uCgpUaGVyZXNhJ3MgZmlyc3QgcGNhIHdhcyBvZiBsb2cyIGNwbSB2YWx1ZXMuICBJIG1pZ2h0IGFkZCBxdWFudGlsZS90bW0KdG8gdGhpcz8KCmBgYHtyfQp2M19sb2NhdGlvbiA8LSBzZXRfZXhwdF9jb25kaXRpb25zKG1tMzhfaGlzYXRfdjMsIGZhY3QgPSAibG9jYXRpb25fYXRiIikgJT4lCiAgc2V0X2V4cHRfYmF0Y2hlcyhmYWN0ID0gImdlbm90eXBlX2F0YiIpICU+JQogIHNldF9leHB0X2NvbG9ycyhjb2xvcl9jaG9pY2VzW1sibG9jYXRpb24iXV0pCgp2M19sb2NhdGlvbl9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KHYzX2xvY2F0aW9uLCBmaWx0ZXIgPSBUUlVFLCBub3JtID0gInF1YW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFuc2Zvcm0gPSAibG9nMiIsIGNvbnZlcnQgPSAiY3BtIikKcGxvdF9wY2EodjNfbG9jYXRpb25fbm9ybSkKYGBgCgpPbmNlIGFnYWluIHdlIHNlZSB0aGF0IHNhbXBsZXMgaXByZ2NfNjYgYW5kIGlwcmdjXzEzMCBhcmUgbGlrZWx5CmFjdHVhbGx5IERMR04gYW5kIG5vdCBTQ04uICBJIGFtIHRoZXJlZm9yZSBnb2luZyB0byBhZGQgYSBjb2x1bW4gdG8KdGhlIHNhbXBsZSBzaGVldCBub3RpbmcgdGhpcywgYW5kIHJlbW92ZSB0aGVtIGZyb20gdGhlIGV4cHJlc3Npb25zZXQuCgpJIHdpbGwgdGh1cyByZXBsb3QgdGhlIGRhdGEgYWZ0ZXIgcmVtb3ZpbmcgdGhvc2UgdHdvLiAgSWYgd2Ugd2FudCB0bwpzZWUgd2hhdCBpdCBsb29rcyBsaWtlIHdpdGggdGhlIHJlLWF0dHJpYnV0ZWQgbG9jYXRpb25zLCB3ZSBjYW4gZG8gc28uCgpUaGVyZXNhIGhhcyBhIG5pY2UgY2hhbmdlIHRvIHRoZSBQQ0EgcGxvdHRlciBpbiB3aGljaCBzaGUgc2V0cyB0aGUKYWxwaGEgY2hhbm5lbCBhcyBhbiBhZGRpdGlvbmFsIHZpc3VhbCBxdWV1ZSBmb3IgYSBtZXRhZGF0YSBmYWN0b3IuLi4KCmBgYHtyfQptbTM4X2hpc2F0X3YzIDwtIHN1YnNldF9leHB0KG1tMzhfaGlzYXRfdjMsIHN1YnNldD0ic2FtcGxlaWQhPSdpcHJnY18xMzAnIikgJT4lCiAgc3Vic2V0X2V4cHQoc3Vic2V0PSJzYW1wbGVpZCE9J2lwcmdjXzY2JyIpCnYzX2xvY2F0aW9uIDwtIHNldF9leHB0X2NvbmRpdGlvbnMobW0zOF9oaXNhdF92MywgZmFjdCA9ICJsb2NhdGlvbl9hdGIiKSAlPiUKICBzZXRfZXhwdF9iYXRjaGVzKGZhY3QgPSAiZ2Vub3R5cGVfYXRiIikgJT4lCiAgc2V0X2V4cHRfY29sb3JzKGNvbG9yX2Nob2ljZXNbWyJsb2NhdGlvbiJdXSkKdjNfbG9jYXRpb25fbm9ybSA8LSBub3JtYWxpemVfZXhwdCh2M19sb2NhdGlvbiwgZmlsdGVyID0gVFJVRSwgbm9ybSA9ICJxdWFudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIpCmZpbHRlcmVkX2xvY2F0aW9uX3BjYSA8LSBwbG90X3BjYSh2M19sb2NhdGlvbl9ub3JtKQpwcChmaWxlID0gImltYWdlcy9maWx0ZXJlZF9sb2NhdGlvbl9wY2EucGRmIikKZmlsdGVyZWRfbG9jYXRpb25fcGNhW1sicGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpmaWx0ZXJlZF9sb2NhdGlvbl9wY2EKCnJlbW92ZWRfc2Fua2V5IDwtIHBsb3RfbWV0YV9zYW5rZXkodjNfbG9jYXRpb24sIGNvbG9yX2Nob2ljZXMgPSBjb2xvcl9jaG9pY2VzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY3RvcnMgPSBjKCJnZW5vdHlwZV9hdGIiLCAibG9jYXRpb25fYXRiIiwgInRpbWVfYXRiIikpCnBwKGZpbGUgPSAiaW1hZ2VzL2ZpbHRlcmVkX3NhbmtleS5wZGYiKQpyZW1vdmVkX3NhbmtleVtbImdncGxvdCJdXQpwbG90dGVkIDwtIGRldi5vZmYoKQpyZW1vdmVkX3NhbmtleQpgYGAKCkhlcmUgaXMgVGhlcmVzYSdzIHRleHQsIHJlY2FsbCBvbmNlIGFnYWluIHRoYXQgSSBkbyBub3QgaGF2ZSBzb21lIG9mCnRoZXNlIG9sZGVyIHNhbXBsZXMgKGlwcmdjXzYyKToKClBDMSB2cyBQQzIgaWRlbnRpZmllcyByZXRpbmEgdnMgYXhvbiBpcyBzdGlsbCB0aGUgbWFpbiBjb21wb25lbnQgb2YKdmFyaWF0aW9uLiBXZSBkbyBzZWUgdGhvdWdoIHRoYXQgaW4gdGhlIFBDMiBkaXJlY3Rpb24sIHdlIHNlZSB3aXRoIHRoZQpuZXcgc2FtcGxlcyBhZGRlZCwgd2UgZG9u4oCZdCBzZWUgc2VwYXJhdGlvbiBiYXNlZCBvbiBheG9uYWwgdGFyZ2V0cwooZExHTiB2cyBTQ04pLiBJbiB0aGUgUEMxIHZzIFBDMyBwbG90LCB3ZSBzZWUgdGhhdCBpdOKAmXMgUEMzIHdoZXJlIHdlCnN0YXJ0IHRvIHNlZSB2YXJpYXRpb24gY29ycmVsYXRlZCB3aXRoIGF4b25hbCBjb21wYXJ0bWVudC4gTGV04oCZcyBsb29rCmF0IFBDMSB2cyBQQzIgY29sb3JlZCBieSBiYXRjaCAod2hlbiB0aGV5IHdlcmUgcHJvY2Vzc2VkL3NlcXVlbmNlZCkgdG8Kc2VlIGlmIHRoYXQgaXMgd2hhdCBpcyBjb250cmlidXRpbmcgc28gbXVjaCB2YXJpYXRpb24gaW4gUEMyLgoKU2lkZSBub3RlOiBpcFJHQyA2MiBzZWVtcyBsaWtlIGFuIG9kZCBiYWxsLiBUaGlzIHNlZW1zIHRvIG1lIGxpa2UgaXQKc2hvdWxkIGhhdmUgYmVlbiBhIGRMR04gUDA4IHNhbXBsZS4gSXMgdGhlcmUgYW55IHBvc3NpYmlsaXR5IHRoaXMgZ290Cm1pc2xhYmVsZWQgZWFybHkgb24/IEkgd2VudCBiYWNrIGFuZCBkb3VibGUgY2hlY2tlZCB0byBzZWUgaWYgYWxsIG15CnByb2Nlc3NpbmcgaXMgY29ycmVjdCBhbmQgaXQgaW5kZWVkIHdhcyBsYWJlbGVkIGFuIFNDTiBQMTUgZnJvbSB0aGUKdGltZSBJIGdvdCB0aGUgc2FtcGxlcywgYW5kIGl0IGlzIGluZGVlZC4KCiMgREUKCkkgbm93IHN3aXRjaGVkIHRvIFRoZXJlc2EncyBkb2N1bWVudCAnV09SS0lOR19heG9uVFJBUC4uLicgYW5kIHdpbGwKc3RhcnQgcHVsbGluZyBzZWN0aW9ucyBmcm9tIGl0LiAgSSBhbSByZWFzb25hYmx5IGNlcnRhaW4gSSBoYXZlCnJlYXNvbmFibHkgc2ltaWxhciBzYW1wbGUgZGlzdHJpYnV0aW9ucywgc28gSSBwcmVzdW1lIEkgY2FuIGludm9rZQpzaW1pbGFyL2lkZW50aWNhbCBjYWxscyBmb3IgREVTZXEgYW5kIGZyaWVuZHMuCgojIyBwOCByZXRpbmFzCgpJbiB0aGUgYmxvY2sgaW1tZWRpYXRlbHkgYmVmb3JlIHRoZSBERSBhbmFseXNlcywgVGhlcmVzYSBjcmVhdGVkIGEKc3Vic2V0IGV4cHJlc3Npb25zZXQgb2Ygb25seSBwMDggcmV0aW5hcy4gIFRodXMgdGhpcyBpbml0aWFsIERFIEkKYXNzdW1lIHdpbGwgYmUgdXNlZCB0byBzdWJ0cmFjdCBmb3IgdGhlIFNDTi9ETEdOIGFuYWx5c2VzIHRoYXQgZm9sbG93LgooSSBndWVzcyBJIGNvdWxkIHJlYWQgYWhlYWQgYW5kIGZpbmQgb3V0LCBidXQgbm8hIEkgd2FudCB0byBiZSBhCmJsYW5rIHNsYXRlKQoKVGhlcmVzYSdzIHByaW1hcnkgd29ya2Zsb3cgbWFrZXMgaGVhdnkgdXNlIG9mIERFU2VxMgooQGxvdmVNb2RlcmF0ZWRFc3RpbWF0aW9uRm9sZDIwMTQpIGFuZCBzdmEKKEBsZWVrU1ZBUGFja2FnZVJlbW92aW5nMjAxMikuICBJbiBzb21lKG1vc3Q/KSBvZiBUaGVyZXNhJ3MKaW52b2NhdGlvbnMgb2YgdGhlIGFsbF9wYWlyd2lzZSgpIGZ1bmN0aW9uLCBzaGUgZXhjbHVkZXMgdGhlIG90aGVyCm1ldGhvZHMgdGhhdCBpdCBwZXJmb3Jtcy4gIEluIHRoaXMgd29ya2Jvb2ssIEkgbGVmdCB0aG9zZSBtZXRob2RzIG9uLAp0aHVzIHdlIGNhbiBldmFsdWF0ZSB0aGUgcmVsYXRpdmUgcGVyZm9ybWFuY2UgREVTZXEyIHZzLiBzb21lIChhbGw/IEkKbWF5IGhhdmUgZGlzYWJsZWQgRUJTZXEvZHJlYW0gYmVjYXVzZSB0aGV5IHdlcmUgdGFraW5nIHRvbyBsb25nKQpvZiB0aGUgZm9sbG93aW5nOgoKKiBsaW1tYTogKEByaXRjaGllTGltbWFQb3dlcnNEaWZmZXJlbnRpYWwyMDE1KSAoYW1vbmcgb3RoZXIKICByZWZlcmVuY2VzKSBvcmlnaW5hbGx5IHdyaXR0ZW4gZm9yIG1pY3JvYXJyYXlzLgoqIEVkZ2VSOiAoQHJvYmluc29uRWRnZVJCaW9jb25kdWN0b3JQYWNrYWdlMjAxMCksIHdoaWNoIHNoYXJlcyBtYW55CiAgYXNzdW1wdGlvbnMgd2l0aCBERVNlcTIuCiogRUJTZXE6IChAbGVuZ0VCU2VxRW1waXJpY2FsQmF5ZXMyMDEzKSwgYmVjYXVzZSBJIGhhdmUgYSBzb2Z0IHNwb3QKICBmb3IgYW55IEJheWVzaWFuIG1ldGhvZC4KKiBOb2lzZXE6IChAdGFyYXpvbmFOT0lzZXFSTkFzZXFEaWZmZXJlbnRpYWwyMDExKSwgd2hpY2ggc2Vla3MgdG8KICBkaXJlY3RseSBtb2RlbCB2YXJpYW5jZSBpbiBhbiBSTkFTZXEgZGF0YXNldCBhbmQgdXNlIHRoYXQgdG8gaW1wcm92ZQogIHRoZSBzZW5zaXRpdml0eSBvZiB0aGUgcmVzdWx0LCBtdWNoIGxpa2U6CiogRHJlYW06IChAaG9mZm1hbkRyZWFtUG93ZXJmdWxEaWZmZXJlbnRpYWwyMDIwKSwgd3JpdHRlbiBieSB0aGUgc2FtZQogIGF1dGhvcnMgKGFuZCB1c2VzIHZlcnkgc2ltaWxhciBsb2dpYykgYXMgb25lIG9mIG15IGZhdm9yaXRlIHRvb2xzLAogIHZhcmlhbmNlUGFydGl0aW9uKEBob2ZmbWFuVmFyaWFuY2VQYXJ0aXRpb25JbnRlcnByZXRpbmdEcml2ZXJzMjAxNikuCgpgYGB7cn0KbW0zOF9wOF9yZXRpbmEgPC0gc3Vic2V0X2V4cHQobW0zOF9oaXNhdF92Mywgc3Vic2V0ID0gInRpbWVfYXRiPT0ncDA4JyAmIGxvY2F0aW9uX2F0Yj09J3JldGluYSciKQptbV9ub3JtYWxfcDhfcmV0X2RlIDwtIGFsbF9wYWlyd2lzZShtbTM4X3A4X3JldGluYSwgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwgZmlsdGVyID0gVFJVRSkKbW1fbm9ybWFsX3A4X3JldF9kZQpgYGAKClRoZSBmb2xsb3dpbmcgaW52b2NhdGlvbiBwZXJmb3JtZWQgYnkgVGhlcmVzYSBmaWx0ZXJzIHRoZSB3dC9oZXQKY29tcGFyaXNvbiBmb3Igb25seSB0aG9zZSBnZW5lcyB3aGljaCBpbmNyZWFzZWQgYnkgYXQgbGVhc3QgMC4yNSBsb2dGQwp3aXRoIGEgc2lnbmlmaWNhbnQgYWRqdXN0ZWQgcC12YWx1ZS4gIEkgYXNzdW1lIHRoYXQgdGhpcyBpcyB0byB1c2UgdGhlCnd0IHNhbXBsZXMgYXMgYSB0cmFuc2xhdGlvbmFsIGNvbnRyb2wgZm9yIHRoZSBrZXQva28gY29tcGFyaXNvbnM7IEkgYW0KdGhlcmVmb3JlIHRoaW5raW5nIHRoYXQgZm9yIG15IHB1cnBvc2VzLCBJIHdpbGwgdGhlcmVmb3JlIHNlcGFyYXRlIHRoZQpjb250cmFzdHMgZnJvbSBhbGxfcGFpcndpc2UgZG8gdGhpcyBpbiBhIHN0ZXB3aXNlIGZhc2hpb24uLi4KClRoZSBibG9jayBvZiBjb2RlIGltbWVkaWF0ZWx5IGZvbGxvd2luZyBUaGVyZXNhJ3MgYWxsX3BhaXJ3aXNlKCkKaW52b2NhdGlvbiBpcyBhIGxpdHRsZSBjb25mdXNpbmcgZm9yIG1lIGFuZCB3YXJyYW50cyBzb21lIGV4cGxhbmF0aW9uCmJ5IG1lIHRvIG1lIGluIHRoZSBob3BlcyB0aGF0IEkgZG8gbm90IG1pc3VuZGVyc3RhbmQgd2hhdCBpcyBoYXBwZW5pbmcKYW5kIHRoZSBnb2FscyB0aGVyZWluLgoKSSB0aGluayBJIGNhbiBzYWZlbHkgYXNzdW1lIHRoYXQgdGhlIGdvYWwgaGVyZSBpcyB0byBwdWxsIG91dCB0aGUgSURzCndoaWNoIGluY3JlYXNlZCBpbiBoZXQgd2l0aCByZXNwZWN0IHRvIHdpbGQgdHlwZTsgZXZlbiBpZiBieSBhIHNtYWxsCm1hcmdpbiwgYXMgbG9uZyBhcyBpdCBpcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IHZpcyBhIHZpcyB0aGUKYWRqdXN0ZWQgcC12YWx1ZS4KCkkgYW0gZ29pbmcgdG8gcGVyZm9ybSB3aGF0IEkgdGhpbmsgaXMgdGhlIHNhbWUgdGhpbmcgaW4gYSBzbGlnaHRseQpkaWZmZXJlbnQgZmFzaGlvbiBzbyB0aGF0IEkgY2FuIHNoYXJlIGEgY29weSBvZiB0aGUgcmVzdWx0cyB3aXRoCndob21ldmVyIGlzIGludGVyZXN0ZWQuICBJIHdpbGwgYWxzbyByZXBlYXQgVGhlcmVzYSdzIGludm9jYXRpb24gYW5kCnByb3ZlIHRvIG15c2VsZiB0aGF0IEkgdW5kZXJzdG9vZCBhbmQgZ290IHRoZSBzYW1lIGFuc3dlci4KCmBgYHtyfQp3dF9oZXRfa2VlcGVyIDwtIGxpc3QoImhldF92c193dCIgPSBjKCJoZXRfcmV0aW5hIiwgInd0X3JldGluYSIpKQpoZXRfd3RfdGFibGUgPC0gY29tYmluZV9kZV90YWJsZXMobW1fbm9ybWFsX3A4X3JldF9kZSwga2VlcGVycyA9IHd0X2hldF9rZWVwZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbF9jb2x1bW4gPSBsYWJlbF9jb2x1bW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleGNlbCA9ICJleGNlbC9oZXRfcmV0aW5hX2NvbnRyb2wueGxzeCIpCndhbnRlZF9zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcyhoZXRfd3RfdGFibGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZmMgPSAwLjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNjb3JkaW5nX3RvID0gImRlc2VxIikKCndhbnRlZF9oZXRfaW5jcmVhc2VkIDwtIHdhbnRlZF9zaWdbWyJkZXNlcSJdXVtbInVwcyJdXVtbImhldF92c193dCJdXQppbmNyZWFzZWRfaGV0X2dlbmVzIDwtIHJvd25hbWVzKHdhbnRlZF9oZXRfaW5jcmVhc2VkKQpgYGAKCkhlcmUgYXJlIFRoZXJlc2EncyBuZXh0IGxpbmVzOgoKYGBge3J9Cm1tX2RlX25vcm1hbF9wOF9yZXQgPC0gbW1fbm9ybWFsX3A4X3JldF9kZQpoZXRrZWVwZXJfZ2VuZXMgPC0gbW1fZGVfbm9ybWFsX3A4X3JldCRkZXNlcSRhbGxfdGFibGVzJHd0X3JldGluYV92c19oZXRfcmV0aW5hICU+JQogIGZpbHRlcihsb2dGQyA8PSAtMC4yNSAmIGFkai5QLlZhbCA8PSAwLjA1KQoKa29rZWVwZXJfZ2VuZXMgPC0gbW1fZGVfbm9ybWFsX3A4X3JldCRkZXNlcSRhbGxfdGFibGVzJHd0X3JldGluYV92c19rb19yZXRpbmEgJT4lCiAgZmlsdGVyKGxvZ0ZDIDw9IC0wLjI1ICYgYWRqLlAuVmFsIDw9IDAuMDUpCgprZWVwZXJnZW5lcyA8LSB1bmlxdWUoYyhyb3duYW1lcyhoZXRrZWVwZXJfZ2VuZXMpLAogICAgICAgICAgICAgICAgICAgICAgICByb3duYW1lcyhrb2tlZXBlcl9nZW5lcykpKQoKIyMgV2Uga25vdyBhIHByaW9yaSB0aGF0IE9wbjQgaXMgRU5TTVVTRzAwMDAwMDIxNzk5CiMjIEkgZG8gbm90IGV4cGVjdCB0byBzZWUgaXQgaW4gdGhpcyBzZXQsIGl0IHNob3VsZCBiZSBoaWdoZXIgaW4gd3QKIyMgcmV0aW5hIHZzIGtvIHJldGluYSBieSBhIHNpZ25pZmljYW50IG1hcmdpbi4KIkVOU01VU0cwMDAwMDAyMTc5OSIgJWluJSBrZWVwZXJnZW5lcwojIyBPb29vaGhoIGJ1dCBpdCBfaXNfIGhpZ2hlciBpbiBoZXQgdnMuIHd0LCBhcyB3ZSBzYXcgaW4KIyMgdGhlIHZpb2xpbiBwbG90IGVhcmxpZXIuCmBgYAoKSSB0aGluayBSYXNobWkgbWFkZSBhIGNvbXBlbGxpbmcgcG9pbnQgd2hpY2ggaWxsdXN0cmF0ZXMgd2h5IHdlIGxpa2VseQpzaG91bGQgZXhwZWN0IHRoZSBleHByZXNzaW9uIG9mIE9wbjQgdG8gc2lnbmlmaWNhbnRseSBoaWdoZXIgaW4gdGhlCmhldGVyb3p5Z290ZXMgdnMgd2lsZC10eXBlOgoKMS4gIFJlY2FsbCB0aGF0IHRoZSBhc3NheSBpcyB1c2luZyB0aGUgaW1tdW5vcHVyaWZpY2F0aW9uIHRvIGV4dHJhY3QKICAgIHRoZSBSTkFzLgoyLiAgVGhlIHd0IHNhbXBsZXMgZG8gbm90IGhhdmUgdGhlIGNyZSByZWNvbWJpbmFzZSBhbmQgdGhlcmVmb3JlIG5vIEhBCiAgICBhbmQgdGhlcmVmb3JlIGV2ZXJ5dGhpbmcgd2Ugb2JzZXJ2ZSBpcyBkdWUgdG8gbm9uLXNwZWNpZmljCiAgICBiaW5kaW5nLgozLiAgVGhlIHNldCBvZiBnZW5lcyBvYnNlcnZlZCBkdWUgdG8gbm9uLXNwZWNpZmljIGJpbmRpbmcgaXMgZGlmZmVyZW50CiAgICB0aGFuIGhldC9rbyAocHJlc3VtYWJseSBhIGxhcmdlciBudW1iZXIgb2YgcmVsYXRpdmVseSBzbWFsbAogICAgdmFsdWVzKSwgdGhlcmVmb3JlIHRoZSBkaXZpc29yIHBlcmZvcm1lZCBpbiB0aGUgY3BtIGlzIGxpa2VseQogICAgcmVsYXRpdmx5IGxhcmdlIHJlc3VsdGluZyBpbiBub3JtYWxpemVkIHZhbHVlcyBnZXR0aW5nIHNoaWZ0ZWQKICAgIGRvd24gdG8gc29tZSBkZWdyZWUuCjQuICBPbiB0aGUgb3RoZXIgaGFuZCwgdGhlIHNldCBvZiBnZW5lcyBvYnNlcnZlZCBpbiBoZXQva28gYXJlIG1vcmUKICAgIGxpa2VseSB0byBiZSBvbmx5IHRoZSBzcGVjaWZpYyBiaW5kZXJzIGFuZCB0aGVyZWZvcmUgc21hbGxlciAoSQogICAgY2FuIHRlc3QgdGhpcykgcmVzdWx0aW5nIGluIGEgc21hbGxlciBkaXZpc29yIGFuZCBzbGlnaHQgc2hpZnRpbmcKICAgIHVwIGluIHRoZSBjcG0gdmFsdWVzLgoKVGhpcyBtYWtlcyBtZSB3b25kZXIgaWYgYW55IG5vcm1hbGl6YXRpb24gbWV0aG9kcyBleGlzdCB3aGljaCBkbwpzb21ldGhpbmcgbGlrZSBtdWx0aXBseSB0aGUgdmFsdWVzIGJ5IHNvbWUgdmFsdWUgcmVsYXRlZCB0byB0aGUKcHJvcG9ydGlvbiBvZiBvYnNlcnZlZCBnZW5lczsgYW5kL29yIGlmIHRoaXMgaXMgYSBnb29kL2JhZC9pbmRpZmZlcmVudAppZGVhLgoKQWxzbywganVzdCBhIG5vdGUgZm9yIG1lIHRvIHJlbWVtYmVyOiBSUEwyMiwgbm90IFJQUzIyLCBmb3Igc29tZQpyZWFzb24gSSBrZWVwIHRoaW5raW5nIHRoZSBzbWFsbCBzdWJ1bml0LgoKIyMgUHJvdmUgSSB1bmRlcnN0b29kCgpgYGB7cn0KaGV0a2VlcGVyX2dlbmVzIDwtIG1tX25vcm1hbF9wOF9yZXRfZGUkZGVzZXEkYWxsX3RhYmxlcyR3dF9yZXRpbmFfdnNfaGV0X3JldGluYSAlPiUKICBmaWx0ZXIobG9nRkMgPD0gLTAuMjUgJiBhZGouUC5WYWwgPD0gMC4wNSkKdGVzdHRoYXQ6OmV4cGVjdF90cnVlKG5yb3coaGV0a2VlcGVyX2dlbmVzKSA9PSBsZW5ndGgoaW5jcmVhc2VkX2hldF9nZW5lcykpCgp0YWFfa2VlcGVycyA8LSBzb3J0KHJvd25hbWVzKGhldGtlZXBlcl9nZW5lcykpCmF0Yl9rZWVwZXJzIDwtIHNvcnQoaW5jcmVhc2VkX2hldF9nZW5lcykKdGVzdHRoYXQ6OmV4cGVjdF9lcXVhbCh0YWFfa2VlcGVycywgYXRiX2tlZXBlcnMpCmBgYAoKWWF5ISBJIGNhbiByZWFkISAgTm93IGxldCB1cyByZXBlYXQgZm9yIHRoZSBLTyB2cyB3dAoKYGBge3J9Cnd0X2tvX2tlZXBlciA8LSBsaXN0KCJrb192c193dCIgPSBjKCJrb19yZXRpbmEiLCAid3RfcmV0aW5hIikpCmtvX3d0X3RhYmxlIDwtIGNvbWJpbmVfZGVfdGFibGVzKG1tX25vcm1hbF9wOF9yZXRfZGUsIGtlZXBlcnMgPSB3dF9rb19rZWVwZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsX2NvbHVtbiA9IGxhYmVsX2NvbHVtbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjZWwgPSAiZXhjZWwva29fcmV0aW5hX2NvbnRyb2wueGxzeCIpCndhbnRlZF9zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcyhrb193dF90YWJsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxmYyA9IDAuMjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY2NvcmRpbmdfdG8gPSAiZGVzZXEiKQp3YW50ZWRfa29faW5jcmVhc2VkIDwtIHdhbnRlZF9zaWdbWyJkZXNlcSJdXVtbInVwcyJdXVtbImtvX3ZzX3d0Il1dCmluY3JlYXNlZF9rb19nZW5lcyA8LSByb3duYW1lcyh3YW50ZWRfa29faW5jcmVhc2VkKQpgYGAKClRoZSBuZXh0IHRoaW5nIHBlcmZvcm1lZCBpbiBUaGVyZXNhJ3MgZG9jdW1lbnQgaXMgYSB1bmlxdWUoY29uY2F0ZW5hdGlvbiBvZgp0aGVzZSB0d28gZ2VuZSBncm91cHMpLCB0aHVzIHN1Y2tpbmcgdXAgZXZlcnkgZ2VuZSB3aGljaCB3YXMKc2lnbmlmaWNhbnRseSBoaWdoZXIgaW4gZWl0aGVyIHRoZSBrbm9ja291dCBfb3JfIGhldGVyenlvdXMgc2FtcGxlcwp3aXRoIHJlc3BlY3QgdG8gd2lsZC10eXBlLgoKVGhpcyB3YXMgZm9sbG93ZWQgYnkgYSBjb3VwbGUgb2YgbWVyZ2Ugb3BlcmF0aW9ucyBvZiBhIGxpdHRsZSBiaXQgb2YKdGhlIGFubm90YXRpb24gZGF0YTsgSSBhbSBub3Qgc3VyZSBJIHVuZGVyc3RhbmQgdGhlIGdvYWwgeWV0Li4uCgpIZXJlIGlzIGhlciBjb2RlLiBJIGNvcGllZCB0aGUgYW5ub3RhdGlvbiAnbWdpX3N5bWJvbCcgY29sdW1uIHRvCidleHRlcm5hbF9nZW5lX25hbWUnIHNvIHRoYXQgSSBuZWVkIG5vdCBjaGFuZ2UgYW55IG9mIGhlciBjb2RlLiAgSSBhbQphc3N1bWluZyB0aGlzIGlzIHRoZSBhcHByb3ByaWF0ZSBjb2x1bW4gb2YgaW50ZXJlc3QsIEkgZG8gbm90IGtub3cKdGhpcyBmb3IgY2VydGFpbiwgYnV0IGl0IHNlZW1zIHF1aXRlIGxpa2VseS4KCldoaWxlIEkgYW0gYXQgaXQsIGhlcmUgaXMgdGhlIHNldF9zaWdfbGltbWEoKSBmdW5jdGlvbiBmcm9tIFRoZXJlc2EncyBoZWxwZXJzLlIKCmBgYHtyfQpzZXRfc2lnX2xpbW1hIDwtIGZ1bmN0aW9uKGxpbW1hX3RibCwgZmFjdG9ycyA9IE5VTEwpIHsKICBpZiAoaXMubnVsbChmYWN0b3JzKSkgewogICAgI3NldCBzaWduaWZpY2FuY2UgZm9yIHBsb3R0aW5nIGNvbG9ycwogICAgbGltbWFfdGJsJFNpZ25pZmljYW5jZSA8LSBOQQogICAgbGltbWFfdGJsW2FicyhsaW1tYV90YmwkbG9nRkMpIDwgMSB8IGxpbW1hX3RibCRhZGouUC5WYWwgPiAuMDUsICJTaWduaWZpY2FuY2UiXSA8LSAiTm90IFxuRW5yaWNoZWQiCiAgICBsaW1tYV90YmxbbGltbWFfdGJsJGxvZ0ZDID49IDEgICYgbGltbWFfdGJsJGFkai5QLlZhbCA8PSAuMDUsIF1bWyJTaWduaWZpY2FuY2UiXV0gPC0gIkRpc2Vhc2UgXG5VcHJlZ3VsYXRlZCIKICAgIGxpbW1hX3RibFtsaW1tYV90YmwkbG9nRkMgPD0gLTEgICYgbGltbWFfdGJsJGFkai5QLlZhbCA8PSAuMDUsIF1bWyJTaWduaWZpY2FuY2UiXV0gPC0gIkRpc2Vhc2UgXG5Eb3ducmVndWxhdGVkIgogICAgbGltbWFfdGJsJFNpZ25pZmljYW5jZSA8LSBmYWN0b3IobGltbWFfdGJsJFNpZ25pZmljYW5jZSwgbGV2ZWxzID0gYygiVXByZWd1bGF0ZWQiLCAiRG93bnJlZ3VsYXRlZCIsICAiTm90IFxuRW5yaWNoZWQiKSkKICB9IGVsc2UgewogICAgbGltbWFfdGJsJFNpZ25pZmljYW5jZSA8LSBOQQogICAgbGltbWFfdGJsW2FicyhsaW1tYV90YmwkbG9nRkMpIDwgMSB8IGxpbW1hX3RibCRhZGouUC5WYWwgPiAuMDUsICJTaWduaWZpY2FuY2UiXSA8LSAiTm90IFxuRW5yaWNoZWQiCiAgICBpZihucm93KGxpbW1hX3RibFtsaW1tYV90YmwkbG9nRkMgPj0gMSAgJiBsaW1tYV90YmwkYWRqLlAuVmFsIDw9IC4wNSwgXSkgIT0gMCkgewogICAgICBsaW1tYV90YmxbbGltbWFfdGJsJGxvZ0ZDID49IDEgICYgbGltbWFfdGJsJGFkai5QLlZhbCA8PSAuMDUsIF1bWyJTaWduaWZpY2FuY2UiXV0gPC0gZmFjdG9yc1sxXQogICAgfQogICAgaWYgKG5yb3cobGltbWFfdGJsW2xpbW1hX3RibCRsb2dGQyA8PSAtMSAgJiBsaW1tYV90YmwkYWRqLlAuVmFsIDw9IC4wNSwgXSkgIT0gMCkgewogICAgICBsaW1tYV90YmxbbGltbWFfdGJsJGxvZ0ZDIDw9IC0xICAmIGxpbW1hX3RibCRhZGouUC5WYWwgPD0gLjA1LCBdW1siU2lnbmlmaWNhbmNlIl1dIDwtIGZhY3RvcnNbMl0KICAgIH0KICAgIGxpbW1hX3RibCRTaWduaWZpY2FuY2UgPC0gZmFjdG9yKGxpbW1hX3RibCRTaWduaWZpY2FuY2UsIGxldmVscyA9IGMoZmFjdG9ycywgICJOb3QgXG5FbnJpY2hlZCIpKQogIH0KICByZXR1cm4obGltbWFfdGJsKQp9CmBgYAoKIyMjIENvbWJpbmluZyBoZXQvd3QgYW5kIGtvL3d0CgpgYGB7cn0KbW1fYW5ub3RbWyJleHRlcm5hbF9nZW5lX25hbWUiXV0gPC0gbW1fYW5ub3RbWyJtZ2lfc3ltYm9sIl1dCmtlZXBlcmdlbmVzIDwtIHVuaXF1ZShjKHJvd25hbWVzKGhldGtlZXBlcl9nZW5lcyksIHJvd25hbWVzKGtva2VlcGVyX2dlbmVzKSkpCmxlbmd0aChrZWVwZXJnZW5lcykKCmFubm90c190b19tZXJnZSA8LSBtbV9hbm5vdCAlPiUKICBzZWxlY3QoZW5zZW1ibF9nZW5lX2lkLCBleHRlcm5hbF9nZW5lX25hbWUpICU+JQogIGZpbHRlcihlbnNlbWJsX2dlbmVfaWQgJWluJQogICAgICAgICAgIHJvd25hbWVzKG1tX2RlX25vcm1hbF9wOF9yZXQkZGVzZXEkYWxsX3RhYmxlcyRrb19yZXRpbmFfdnNfaGV0X3JldGluYSkpICU+JQogIGRpc3RpbmN0KCkKCm1tX2RlX25vcm1hbF9wOF9yZXQkZGVzZXEkYWxsX3RhYmxlcyRrb19yZXRpbmFfdnNfaGV0X3JldGluYSA8LSBtZXJnZSgKICBtbV9kZV9ub3JtYWxfcDhfcmV0JGRlc2VxJGFsbF90YWJsZXMka29fcmV0aW5hX3ZzX2hldF9yZXRpbmEsIGFubm90c190b19tZXJnZSwKICBieS54ID0gMCwgYnkueSA9ICJlbnNlbWJsX2dlbmVfaWQiLCBhbGwueCA9IFRSVUUpCgpkZiA8LSBtbV9kZV9ub3JtYWxfcDhfcmV0JGRlc2VxJGFsbF90YWJsZXMka29fcmV0aW5hX3ZzX2hldF9yZXRpbmEgJT4lCiAgZHBseXI6Om11dGF0ZShsb2dGQyA9IC1sb2dGQykgJT4lCiAgc2V0X3NpZ19saW1tYShmYWN0b3JzID0gYygiSGV0IEVucmljaGVkIiwgIktPIEVucmljaGVkIikpCmBgYAoKTXkgdmVyc2lvbiBvZiB0aGUgYWJvdmUgdGFzayBtYWtlcyB1c2Ugb2YgdGhlIGV4Y2x1ZGVzIG9wdGlvbiBvZgpjb21iaW5lX2RlX3RhYmVzLiAgR2l2ZW4gdGhlIHNldCBvZiB1bmlxdWUgZ2VuZSBJRHMgaW5jcmVhc2VkIGluIHRoZQpoZXQva28sIEkgY2FuIGFzayB0byBleGx1ZGUgYW55dGhpbmcgbm90IGluIHRoYXQgc2V0LiAgSSBjb3VsZCBhbHNvCmhhdmUgbW9yZSBwYXJzaW1vbmlvdXNseSBkaXJlY3RseSBleGNsdWRlZCBhbnkgZ2VuZSBJRCBpbmNyZWFzZWQgaW4KdGhlIHd0IHNhbXBsZXMuICBCdXQsIFRoZXJlc2EgYWxyZWFkeSBwcm92aWRlZCB0aGUgY29kZSB0byBkbyB0aGUKZm9ybWVyLCBzbyBpdCB3aWxsIGJlIGxlc3MgdHlwaW5nL29wcG9ydHVuaXR5IGZvciBzaWxseSBtaXN0YWtlcyB0bwpqdXN0IGRvIHRoYXQuCgpgYGB7cn0KYm90aF9pbmNyZWFzZWRfZ2VuZXMgPC0gdW5pcXVlKGMoaW5jcmVhc2VkX2hldF9nZW5lcywgaW5jcmVhc2VkX2tvX2dlbmVzKSkKIyMgYXJiaXRyYWlybHkgZ3JhYiBhbGwgZ2VuZXMgZnJvbSBvbmUgb2YgbXkgZGF0YSBzdHJ1Y3R1cmVzLgphbGxfZ2VuZXMgPC0gcm93bmFtZXMoZXhwcnMobW0zOF9oaXNhdF92MykpCmV4Y2x1ZGVfaWR4IDwtIGFsbF9nZW5lcyAlaW4lIGJvdGhfaW5jcmVhc2VkX2dlbmVzCnN1bW1hcnkoZXhjbHVkZV9pZHgpCmV4Y2x1ZGVfaW5jcmVhc2VkX2dlbmVzIDwtIGFsbF9nZW5lc1tleGNsdWRlX2lkeF0KcmV0aW5hX2tlZXBlcnMgPC0gbGlzdCgKICAiaGV0X3ZzX3d0IiA9IGMoImhldF9yZXRpbmEiLCAid3RfcmV0aW5hIiksCiAgImtvX3ZzX3d0IiA9IGMoImtvX3JldGluYSIsICJ3dF9yZXRpbmEiKSwKICAia29fdnNfaGV0IiA9IGMoImtvX3JldGluYSIsICJoZXRfcmV0aW5hIikpCgojIyBBIHJlbWluZGVyIHRvIG15c2VsZjogdGhlcmUgaXMgYWxzbyBhIHBhcmFtZXRlciAnd2FudGVkX2dlbmVzJwojIyB3aGljaCBkb2VzIGVmZmVjdGl2ZWx5IHRoZSBzYW1lIHRoaW5nIGFzIGV4Y2x1ZGVzIGluIHRoaXMgY29udGV4dDsKIyMgZXhjbHVkZXMgd2FzIG9yaWdpbmFsbHkgd3JpdHRlbiB0byBhbGxvdyBmbGV4aWJsZSwga2V5d29yZC1iYXNlZAojIyBleGNsdXNpb24uCnA4X3JldGluYV90YWJsZXMgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgbW1fbm9ybWFsX3A4X3JldF9kZSwga2VlcGVycyA9IHJldGluYV9rZWVwZXJzLAogIHdhbnRlZF9nZW5lcyA9IGJvdGhfaW5jcmVhc2VkX2dlbmVzLCBsYWJlbF9jb2x1bW4gPSBsYWJlbF9jb2x1bW4sCiAgZXhjZWwgPSBnbHVlKCJleGNlbC9wOF9yZXRpbmFfa2VwdF9nZW5lc19pbmNyZWFzZWRfaW5fd3RfdGFibGVzLXZ7dmVyfS54bHN4IikpCnA4X3JldGluYV9zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICBwOF9yZXRpbmFfdGFibGVzLAogIGV4Y2VsID0gZ2x1ZSgiZXhjZWwvcDhfcmV0aW5hX2tlcHRfZ2VuZXNfaW5jcmVhc2VkX2luX3d0X3NpZy12e3Zlcn0ueGxzeCIpLAogIGFjY29yZGluZ190byA9ICJkZXNlcSIpCgpvcHBvc2l0ZV9wOF9yZXRpbmFfdGFibGVzIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIG1tX25vcm1hbF9wOF9yZXRfZGUsIGtlZXBlcnMgPSByZXRpbmFfa2VlcGVycywKICBleGNsdWRlcyA9IGJvdGhfaW5jcmVhc2VkX2dlbmVzLCBsYWJlbF9jb2x1bW4gPSBsYWJlbF9jb2x1bW4sCiAgZXhjZWwgPSBnbHVlKCJleGNlbC9wOF9yZXRpbmFfcmVtb3ZlZF9nZW5lc19pbmNyZWFzZWRfaW5fd3RfdGFibGVzLXZ7dmVyfS54bHN4IikpCm9wcG9zaXRlX3A4X3JldGluYV9zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICBwOF9yZXRpbmFfdGFibGVzLAogIGV4Y2VsID0gZ2x1ZSgiZXhjZWwvcDhfcmV0aW5hX3JlbW92ZWRfZ2VuZXNfaW5jcmVhc2VkX2luX3d0X3NpZy12e3Zlcn0ueGxzeCIpLAogIGFjY29yZGluZ190byA9ICJkZXNlcSIpCmBgYAoKIyBGaWx0ZXJpbmcgb3V0IG5vbi1zcGVjaWZpYyBnZW5lcyBhbmQgZXhhbWluaW5nIHRoZSByZXN1bHRzCgpUaGUgZm9sbG93aW5nIGlzIGEgY29weS9wYXN0ZSBmcm9tIFRoZXJlc2EgY29udGFpbmluZyB0aGUgcmVtYWluaW5nCnRhc2tzIHNoZSBwZXJmb3JtZWQgYW5kIHdpbGwgcHJvdmlkZSB0aGUgdGVtcGxhdGUgZm9yIGltcGxlbWVudGF0aW9uCm9mIHRoZSBmaW5hbCB0YXNrcy4KClRoaXMgcGlja3MgdXAgd2l0aCB0aGUgbGluZXMgZnJvbSBoZXIgbm90ZWJvb2sgaW1tZWRpYXRlbHkgZm9sbG93aW5nCnRoZSBpbnZvY2F0aW9uIG9mICdzZXRfc2lnX2xpbW1hKGZhY3RvcnMgPSBjKCJIZXQgRW5yaWNoZWQiIC4uLicuCgpGb3IgYWxsIG9mIHRoZSByZW1haW5pbmcgYmxvY2tzIEkgd2lsbCBjb3B5IGluIGhlciBjb2RlLCB0dXJuIG9mZiBpdHMKZXZhbHVhdGlvbiwgcnVuIHRoZSBibG9ja3MgbWFudWFsbHksIGNvbXBhcmUgdGhlbSB0byBoZXIgbm90ZWJvb2sKb3V0cHV0LCB0aGVuIGVuYWJsZSBlYWNoIGJsb2NrIGFzIEkgZW5zdXJlIEkgdW5kZXJzdGFuZCBpdC4KCkkgd2lsbCBsaWtlbHkgdGhlcmVmb3JlIGludHJvZHVjZSBzb21lIHNtYWxsIGZvcm1hdHRpbmcgY2hhbmdlcyBhbmQKYWRkIHNvbWUgYWRkaXRpb25hbCBHU0VBL2VucmljaG1lbnQgdGFza3Mgb25jZSB0aGUgbm9uLXNwZWNpZmljCmZpbHRlcmluZyBpcyBjb21wbGV0ZS4KCmBgYHtyfQpkZiA8LSBkZiAlPiUKICBmaWx0ZXIoUm93Lm5hbWVzICVpbiUga2VlcGVyZ2VuZXMpCgpsYWJlbHNfdXBzIDwtIGRmICU+JQogIGZpbHRlcihhZGouUC5WYWwgPD0gMC4wNSAmIGFicyhsb2dGQykgPiAxKSAlPiUKICBhcnJhbmdlKGxvZ0ZDKSAlPiUKICBoZWFkKG4gPSA5KQoKbGFiZWxzX2Rvd25zIDwtIGRmICU+JQogIGZpbHRlcihhZGouUC5WYWwgPD0gMC4wNSAmIGFicyhsb2dGQykgPiAxKSAlPiUKICBhcnJhbmdlKC1sb2dGQykgJT4lCiAgaGVhZChuID0gMTEpCgpsYWJlbHMgPC0gcmJpbmQobGFiZWxzX3VwcywgbGFiZWxzX2Rvd25zKQoKcmVzX3RibCA8LSBkZgpERXBsb3QgPC0gZ2dwbG90KHJlc190YmwsIGFlcyh4ID0gbG9nRkMsIHkgPSAtbG9nMTAoYWRqLlAuVmFsKSwgbGFiZWwgPSBleHRlcm5hbF9nZW5lX25hbWUpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gU2lnbmlmaWNhbmNlKSwgc2l6ZSA9IDQpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKC0xLDEpKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKC4wNSkpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDIwKSArCiAgeGxhYigibG9nMihGQykiKSArCiAgeWxhYigiLWxvZzEwKHAtdmFsdWUpIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiI0Y4NzY2RCIsICIjMDBCRkM0IiwgIkdyZXkiKSkgKwogIGdlb21fbGFiZWxfcmVwZWwoCiAgICBkYXRhID0gZmlsdGVyKGRmLAogICAgICAgICAgICAgICAgICAjIyBjKCdzNV9oZXRfZGxnbicsICdzNV9oZXRfcmV0JywgJ3M1X2hldF9zY24nKSksCiAgICAgICAgICAgICAgICAgIGV4dGVybmFsX2dlbmVfbmFtZSAlaW4lIGxhYmVscyRleHRlcm5hbF9nZW5lX25hbWUpLAogICAgIyMgbnVkZ2VfeCA9IC0wLjUsCiAgICBudWRnZV95ID0gMywgbWF4Lm92ZXJsYXBzID0gMTUpICsKICB4bGltKGMoLTMsIDYpKQoKcHAoZmlsZSA9ICJpbWFnZXMvcDA4X3JldGluYV9ERV8xMzEyMDI0LnBkZiIpCkRFcGxvdApwbG90dGVkIDwtIGRldi5vZmYoKQpERXBsb3QKCndyaXRleGw6OndyaXRlX3hsc3goZGYsIHBhdGggPSAiZXhjZWwvcmV0aW5haGV0X3ZzX3JldGluYWtvX1dUZmlsdGVyZWQueGxzeCIpCmBgYAoKIyMgSG93IG1hbnkgdXBzL2Rvd25zCgpgYGB7cn0Ka29fZW5yaWNoZWQgPC0gZGYgJT4lCiAgZmlsdGVyKFNpZ25pZmljYW5jZSA9PSAiS08gRW5yaWNoZWQiKQpucm93KGtvX2VucmljaGVkKQoKaGV0X2VucmljaGVkIDwtICBkZiAlPiUKICBmaWx0ZXIoU2lnbmlmaWNhbmNlID09ICJIZXQgRW5yaWNoZWQiKQpucm93KGhldF9lbnJpY2hlZCkKYGBgCgojIyBjYXRlZ29yeSBlbnJpY2htZW50L0dTRUEKCmBgYHtyfQpyZWd1bGF0ZWRfZ2VuZXMgPC0gcmVzX3RibCAlPiUKICBmaWx0ZXIoYWRqLlAuVmFsIDw9IDAuMDUpICU+JQogIGFycmFuZ2UobG9nRkMpICU+JQogIHNlbGVjdChSb3cubmFtZXMsIGxvZ0ZDLCBhZGouUC5WYWwsIGV4dGVybmFsX2dlbmVfbmFtZSwgU2lnbmlmaWNhbmNlKSAlPiUKICBmaWx0ZXIoYWJzKGxvZ0ZDKSA+PSAxKQoKIyMgZ3NlYV9yZXN1bHRfa28gPC0gZ29zdChxdWVyeSA9IGtvX2dlbmVzJGV4dGVybmFsX2dlbmVfbmFtZSwKIyMgICAgICAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICJtbXVzY3VsdXMiLAojIyAgICAgICAgICAgICAgICAgICAgICAgIGV2Y29kZXMgPSBUUlVFLAojIyAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWRfcXVlcnkgPSBUUlVFKQoKZ3NlYV9yZXN1bHRfaGV0IDwtIGdvc3QocXVlcnkgPSBoZXRfZW5yaWNoZWQkZXh0ZXJuYWxfZ2VuZV9uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICJtbXVzY3VsdXMiLAogICAgICAgICAgICAgICAgICAgICAgICBldmNvZGVzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZF9xdWVyeSA9IFRSVUUpCgojI2dzZWFfcmVzdWx0X2FsbGR5c3JlZ3VsYXRlZCA8LSBnb3N0KHF1ZXJ5ID0gYWxsZHlzcmVndWxhdGVkX2dlbmVzJGV4dGVybmFsX2dlbmVfbmFtZSwKIyMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICJtbXVzY3VsdXMiLAojIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV2Y29kZXMgPSBUUlVFLAojIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWRfcXVlcnkgPSBUUlVFKQpgYGAKCkkgaGF2ZSBhIGZ1bmN0aW9uIGluIG15IHBhY2thZ2Ugd2hpY2ggc2Vla3MgdG8gbWFrZSBnUHJvZmlsZXIgcXVlcmllcwphIGJpdCBtb3JlIGNvbXBsZXRlIGFuZCBlYXN5LiAgTGV0IHVzIHNlZSBob3cgc2ltaWxhciB0aGUgcmVzdWx0IGlzLi4uCgpgYGB7ciwgZXZhbD1GQUxTRX0Kcm93bmFtZXMoYWxsZHlzcmVndWxhdGVkX2dlbmVzKSA8LSBhbGxkeXNyZWd1bGF0ZWRfZ2VuZXNbWyJSb3cubmFtZXMiXV0KYWxsZHlzcmVndWxhdGVkX2dlbmVzW1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKCmhldF9ncCA8LSBzaW1wbGVfZ3Byb2ZpbGVyKHJvd25hbWVzKGFsbGR5c3JlZ3VsYXRlZF9nZW5lcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwZWNpZXMgPSAibW11c2N1bHVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjZWwgPSBnbHVlKCJleGNlbC9oZXRfZ3Byb2ZpbGVyLXZ7dmVyfS54bHN4IikpCmhldF9ncAplbnJpY2hwbG90Ojpkb3RwbG90KGhldF9ncFtbIkJQX2VucmljaCJdXSkKZ3BfcGFpciA8LSBlbnJpY2hwbG90OjpwYWlyd2lzZV90ZXJtc2ltKGhldF9ncFtbIkJQX2VucmljaCJdXSkKZW5yaWNocGxvdDo6ZW1hcHBsb3QoZ3BfcGFpcikKZW5yaWNocGxvdDo6c3NwbG90KGdwX3BhaXIpCmVucmljaHBsb3Q6OnRyZWVwbG90KGdwX3BhaXIpCnVwc2V0cGxvdChoZXRfZ3BbWyJCUF9lbnJpY2giXV0pCgplbnJpY2hwbG90Ojpkb3RwbG90KGhldF9ncFtbIlJFQUNfZW5yaWNoIl1dKQpncF9wYWlyIDwtIGVucmljaHBsb3Q6OnBhaXJ3aXNlX3Rlcm1zaW0oaGV0X2dwW1siUkVBQ19lbnJpY2giXV0pCmVucmljaHBsb3Q6OmVtYXBwbG90KGdwX3BhaXIpCmVucmljaHBsb3Q6OnNzcGxvdChncF9wYWlyKQplbnJpY2hwbG90Ojp0cmVlcGxvdChncF9wYWlyKQp1cHNldHBsb3QoaGV0X2dwW1siUkVBQ19lbnJpY2giXV0pCmBgYAoKSSBtYWtlIGEgc29tZXdoYXQgYXJiaXRyYXJ5IGRpc3RpbmN0aW9uIGJldHdlZW4gdGhlIGNvbmNlcHRzIG9mCm92ZXItZW5yaWNobWVudCBhbmFseXNlcyBhbmQgR1NFQTogdGhlIGZvcm1lciAoYXMgcGVyZm9ybWVkIGJ5Cmdwcm9maWxlcikgKEByYXVkdmVyZVByb2ZpbGVyV2ViU2VydmVyMjAxOSkgc2Vla3MgdG8gZmluZCBncm91cHMgb2YKZ2VuZXMgb3ZlcnJlcHJlc2VudGVkIGluIEdPL3JlYWN0b21lL2V0Yy4gIFRoZXNlIGdyb3VwcyBvZiBnZW5lcyBhcmUKdGFrZW4gZXhjbHVzaXZlbHkgZnJvbSB0aGUgdG9wLW4vYm90dG9tLW4gZ2VuZXMgd2l0aCByZXNwZWN0IHRvCmZvbGQtY2hhbmdlIGJldHdlZW4gY29uZGl0aW9ucyBvZiBpbnRlcmVzdDsgaW4gdGhpcyBjYXNlIG1vc3QKZGlmZmVyZW50IHRoYW4gd3QgaW4gdGhlIHAwOCByZXRpbmEga28gb3IgaGV0IHNhbXBsZXMuCgpXaXRoIHRoYXQgaW4gbWluZCwgSSBjYW4gaW52b2tlIGEgc2ltaWxhciBmdW5jdGlvbiB1c2luZyB0aGUgZnVsbAp0YWJsZSBvZiBERSByZXN1bHRzIHRvIGdldCB3aGF0IEkgY2FsbCB0aGUgR1NFQSByZXN1bHQgdXNpbmcKY2x1c3RlclByb2ZpbGVyIChAeXVJbnRyb2R1Y3Rpb25CaW9tZWRpY2FsS25vd2xlZGdlKS4gIEluIHRoZQpmb2xsb3dpbmcgYmxvY2sgSSB3aWxsIHVzZSB0aGUgJ2FsbF9jcHJvZmlsZXInIGZ1bmN0aW9uIG9uIHRoZSBkYXRhCnN0cnVjdHVyZXMgbmFtZWQgJ3A4X3JldGluYV90YWJsZXMnIGFuZCAnb3Bwb3NpdGVfcDhfcmV0aW5hX3RhYmxlcycgaW4Kb3JkZXIgdG8gZ2V0IHRoZXNlIEdTRUEgcmVzdWx0cyBmb3IgZWFjaCBjb250cmFzdCBwZXJmb3JtZWQgKGhldC93dCwKa28vd3QsIGhldC9rbykuICBJIHdpbGwgZm9sbG93IHRoYXQgdXAgd2l0aCAnYWxsX2dwcm9maWxlcicgd2hpY2ggZG9lcwp0aGUgc2FtZSwgYnV0IHVzZXMgZ1Byb2ZpbGVyJ3MgZW5yaWNobWVudCBhbmFseXNlcyAoaXQgd2lsbCB0aGVyZWZvcmUKaW5jbHVkZSB3aGF0IHdlIGp1c3QgbG9va2VkIGF0KS4KCmBgYHtyfQpwMDhfcmV0aW5hX2FsbF9jcCA8LSBhbGxfY3Byb2ZpbGVyKHA4X3JldGluYV9zaWcsIHA4X3JldGluYV90YWJsZXMsIG9yZ2RiID0gIm9yZy5NbS5lZy5kYiIpCgplbnJpY2hwbG90Ojpkb3RwbG90KHAwOF9yZXRpbmFfYWxsX2NwW1sia29fdnNfaGV0X3VwIl1dW1siZW5yaWNoX29iamVjdHMiXV1bWyJNRl9hbGwiXV0pCgpwMDhfdG9wbl9nc2VhIDwtIHBsb3RfdG9wbl9nc2VhKHAwOF9yZXRpbmFfYWxsX2NwKQpwcChmaWxlID0gImltYWdlcy9nc2VhX3AwOF9yZXRpbmFfa29fdnNfaGV0X3RvcF9oaXQucG5nIikKcDA4X3RvcG5fZ3NlYVtbIkdPX2tvX3ZzX2hldF91cCJdXVtbMV1dCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCnAwOF90b3BuX2dzZWFbWyJHT19rb192c19oZXRfdXAiXV1bWzFdXQpwMDhfdG9wbl9nc2VhW1siR09fa29fdnNfaGV0X3VwIl1dW1syXV0KcDA4X3RvcG5fZ3NlYVtbIkdPX2tvX3ZzX2hldF91cCJdXVtbM11dCnAwOF90b3BuX2dzZWFbWyJHT19rb192c19oZXRfdXAiXV1bWzRdXQpwMDhfdG9wbl9nc2VhW1siR09fa29fdnNfaGV0X3VwIl1dW1s1XV0KYGBgCgpgYGB7cn0KI2dzZWFfa28gPC0gIGdzZWFfcmVzdWx0X2tvW1sicmVzdWx0Il1dICU+JQojICAgIHNlbGVjdCh0ZXJtX25hbWUsIHBfdmFsdWUsIHRlcm1fc2l6ZSwgaW50ZXJzZWN0aW9uX3NpemUsIHJlY2FsbCwgc291cmNlLCBpbnRlcnNlY3Rpb24pICU+JQojICAgIGFycmFuZ2UoZGVzYyhyZWNhbGwpKSAlPiUKIyAgICBoZWFkKG4gPSAxMCkKIyAgZ3NlYV9wbG90c19rbyA8LSBnZ3Bsb3QoZ3NlYV9rbywgYWVzKHggPSByZWNhbGwsIHkgPSByZW9yZGVyKHRlcm1fbmFtZSwgcmVjYWxsKSwgZmlsbCA9IHBfdmFsdWUpKSArCiMgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSsKIyAgc2NhbGVfZmlsbF9jb250aW51b3VzKGxvdyA9ICJibHVlIiwgaGlnaCA9ICJyZWQiKSArCiMgIHRoZW1lX2J3KCkrCiMgIHlsYWIoIiIpICsKIyAgeGxhYigiR1NFQSBTY29yZSIpCgpnc2VhX2hldCA8LSAgZ3NlYV9yZXN1bHRfaGV0W1sicmVzdWx0Il1dICU+JQogIHNlbGVjdCh0ZXJtX25hbWUsIHBfdmFsdWUsIHRlcm1fc2l6ZSwgaW50ZXJzZWN0aW9uX3NpemUsIHJlY2FsbCwgc291cmNlLCBpbnRlcnNlY3Rpb24pICU+JQogIGFycmFuZ2UoZGVzYyhyZWNhbGwpKSAlPiUKICBoZWFkKG4gPSAxMCkKZ3NlYV9wbG90c19oZXQgPC0gZ2dwbG90KGdzZWFfaGV0LCBhZXMoeCA9IHJlY2FsbCwgeSA9IHJlb3JkZXIodGVybV9uYW1lLCByZWNhbGwpLCBmaWxsID0gcF92YWx1ZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIHNjYWxlX2ZpbGxfY29udGludW91cyhsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIikgKwogIHRoZW1lX2J3KCkgKwogIHlsYWIoIiIpICsKICB4bGFiKCJPdmVyIFJlcHJlc2VudGF0aW9uIFNjb3JlIikKCnBwKGZpbGUgPSAiaW1hZ2VzL0dTRUFfcDA4X2F4b250cmFwX3JldGluYWhldF91cHJlZ3VsYXRlZF92c19yZXRpbmFrby5wZGYiKQpnc2VhX3Bsb3RzX2hldApwbG90dGVkIDwtIGRldi5vZmYoKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpnc2VhX2FsbCA8LSAgZ3NlYV9yZXN1bHRfYWxsZHlzcmVndWxhdGVkW1sicmVzdWx0Il1dICU+JQogIHNlbGVjdCh0ZXJtX25hbWUsIHBfdmFsdWUsIHRlcm1fc2l6ZSwgaW50ZXJzZWN0aW9uX3NpemUsIHJlY2FsbCwgc291cmNlLCBpbnRlcnNlY3Rpb24pICU+JQogIGFycmFuZ2UoZGVzYyhyZWNhbGwpKSAlPiUKICBoZWFkKG4gPSAxMCkKZ3NlYV9wbG90c19hbGwgPC0gZ2dwbG90KGdzZWFfYWxsLCBhZXMoeCA9IHJlY2FsbCwgeSA9IHJlb3JkZXIodGVybV9uYW1lLCByZWNhbGwpLCBmaWxsID0gcF92YWx1ZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIHNjYWxlX2ZpbGxfY29udGludW91cyhsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIikgKwogIHRoZW1lX2J3KCkgKwogIHlsYWIoIiIpICsKICB4bGFiKCJPdmVyIFJlcHJlc2VudGF0aW9uIFNjb3JlIikKCnBwKGZpbGUgPSAiaW1hZ2VzL0dTRUFfcDA4X3JldGluYV9heG9udHJhcF9hbGxkeXNyZWd1bGF0ZWRnZW5lcy5wZGYiKQpnc2VhX3Bsb3RzX2FsbApkZXYub2ZmKCkKYGBgCgojIFNDTiBIZXQgdnMgS08KCkl0IGlzIG9ubHkgbm93IHRoYXQgSSByZWFsaXplZCB3ZSBhcmUgc3BsaXR0aW5nIHRoZSBkYXRhIGJ5IGxvY2F0aW9uCmZvciBlYWNoIHNldCBvZiBjb21wYXJpc29ucy4gIEkgdGhpbmsgdGhhdCwgbGVmdCB0byBteSBvd24gZGV2aWNlcywgSQp3b3VsZCBwcmVmZXIgdG8ga2VlcCB0aGUgaW5wdXQgZGF0YSBzdHJ1Y3R1cmUgaW50YWN0LCBwZXJmb3JtIHRoZQpzb21ld2hhdCBsYXJnZXIgbnVtYmVyIG9mIGNvbnRyYXN0cywgYW5kIHRoZW4gc3BsaXQgdXAgdGhlIHJlc3VsdHMuCklkZWFsbHkgdGhpcyB3aWxsIHNsaWdodGx5IGltcHJvdmUgdGhlIGZpZGVsaXR5IG9mIHRoZSByZXN1bHRzCnJldHVybmVkIGJ5IERFU2VxMiBhbmQgZnJpZW5kcy4gIEJ1dCwgSSB3aWxsIHJ1biB0aGUgc3RhdGUgb2YKVGhlcmVzYSdzIG5vdGVib29rIHdpdGggYXMgZmV3IGNoYW5nZXMgYXMgcG9zc2libGUgZmlyc3QsIHRoZW4gYWRkCnRoaXMuCgojIyBQQ0EKCkkgYW0gZ29pbmcgdG8gc2tpcCB0aGlzIFBDQSBwbG90IGZvciBhIGNvdXBsZSBvZiByZWFzb25zOiBJIGFscmVhZHkKZGlkIGEgc3VwZXJzZXQgb2YgaXQsIGFuZCB0aGUgc3Vic2V0IFRoZXJlc2EgcGVyZm9ybWVkIGlzIG5vdCB2YWxpZApnaXZlbiB0aGUgc2V0IG9mIHNhbXBsZXMgaW5jbHVkZWQgaW4gbXkgc2FtcGxlIHNoZWV0LCBhbmQgZmlndXJpbmcgb3V0CnRoZSBhY3R1YWxseSBjb3JyZXNwb25kaW5nIHN1YnNldCB3aWxsIHRha2UgbWUgZm9yZXZlci4uLiAgSW4KYWRkaXRpb24sIEkgd2FudCB0byB1c2UgbXkgbW0zOF9oaXNhdF92MyBmb3IgZXZlcnl0aGluZy4uLgoKYGBge3IsIGV2YWw9RkFMU0V9Cm1tMzhfc3Vic2V0IDwtIHN1YnNldF9leHB0KAogIG1tMzhfaGlzYXQsCiAgc3Vic2V0ID0gIihiYXRjaCA9PSAnNCcgfCBiYXRjaCA9PSAnNScgfCBiYXRjaCA9PSAnNicpICYgdGltZSA9PSAncDA4JyAmIGxvY2F0aW9uID09ICdzY24nIHwgc2FtcGxlaWQgPT0gJ2lwcmdjXzAzJyIpCgptbTM4X25vcm0gPC0gbm9ybWFsaXplX2V4cHQobW0zOF9zdWJzZXQsIGZpbHRlciA9IFRSVUUsIGNvbnZlcnQgPSAiY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYW5zZm9ybSA9ICJsb2cyIiwgYmF0Y2ggPSAic3Zhc2VxIikKCm1tMzhfbm9ybSA8LSBzZXRfZXhwdF9iYXRjaGVzKG1tMzhfbm9ybSwgZmFjdCA9ICJsb2NhdGlvbiIpCm1tMzhfbm9ybSA8LSBzZXRfZXhwdF9jb25kaXRpb25zKG1tMzhfbm9ybSwgZmFjdCA9ICJnZW5vdHlwZSIpCnBjYV9ub3JtIDwtIHBsb3RfcGNhKG1tMzhfbm9ybSwgbWF4X292ZXJsYXBzID0gNzApCnBjYV9ub3JtJHBsb3QKYGBgCgpJbnN0ZWFkIEkgd2lsbCBzaW1wbGlmeSB0aGUgc3Vic2V0IGFuZCBzZWUgd2hhdCBoYXBwZW5zLi4uCgpgYGB7cn0Kc2NuX3NhbXBsZXMgPC0gc3Vic2V0X2V4cHQobW0zOF9oaXNhdF92MywKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gImxvY2F0aW9uX2F0YiA9PSAnc2NuJyIpICU+JQogIHNldF9leHB0X2JhdGNoZXMoZmFjdCA9ICJsb2NhdGlvbl9hdGIiKSAlPiUKICBzZXRfZXhwdF9jb25kaXRpb25zKGZhY3QgPSAiZ2Vub3R5cGVfYXRiIikKCnNjbl9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KHNjbl9zYW1wbGVzLCBmaWx0ZXIgPSBUUlVFLCBjb252ZXJ0ID0gImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYW5zZm9ybSA9ICJsb2cyIiwgYmF0Y2ggPSAic3Zhc2VxIikKc2NuX25vcm1fcGNhIDwtIHBsb3RfcGNhKHNjbl9ub3JtKQpzY25fbm9ybV9wY2EKYGBgCgojIERpdmVyZ2luZyBhIGxpdHRsZQoKQXQgdGhpcyBwb2ludCBpbiB0aGUgZG9jdW1lbnQgSSByZWFkIGFoZWFkIGEgYml0IGFuZCBjYW1lIHRvIHRoZQpjb25jbHVzaW9uIHRoYXQgaXQgcmVwZWF0cyB0aGUgYWJvdmUgbG9naWMgb2YgdGFraW5nIHRoZSB1bmlvbiBvZiB3dApjb21wYXJpc29ucyB0byByZW1vdmUgZ2VuZXMgZnJvbSB0aGUgYXBwcm9wcmlhdGUgaGV0L2tvIG9yIHAxNS9wMDggb3IKbG9jYXRpb24gY29tcGFyaXNvbnMuICBUaGlzIHNlZW1zIHF1aXRlIHJlYXNvbmFibGUgdG8gbWUsIGJ1dCBJIHdvdWxkCnByZWZlciB0byBub3Qgc2VwYXJhdGUgYWxsIHRoZSBkYXRhLCBzbyBJIHdpbGwgYXR0ZW1wdCB0byBkdXBsaWNhdGUKYW5kIHNsaWdodGx5IHN0cmVhbWxpbmUgdGhpcyBsb2dpYyBvbiB0aGUgZnVsbCBkYXRhc2V0LiAgVGh1cyBJIGFtCmdvaW5nIHRvIHNraXAgZG93biB0byB0aGUgZW5kIGFuZCBhdHRlbXB0IHRvIGltcGxlbWVudCB0aGlzLgoKIyMgREUKCmBgYHtyLCBldmFsPUZBTFNFfQptbV9kZV9ub3JtYWxfcDhfc2NuIDwtIGFsbF9wYWlyd2lzZShtbTM4X3N1YnNldCwgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYWxsZWwgPSBGQUxTRSwgZG9fZWJzZXEgPSBGQUxTRSwgZG9fYmFzaWMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG9fZHJlYW0gPSBGQUxTRSwgZG9fbm9pc2VxID0gRkFMU0UsIGRvX2VkZ2VyID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IFRSVUUpCgphbm5vdHNfdG9fbWVyZ2UgPC0gbW1fYW5ub3QgJT4lCiAgc2VsZWN0KGVuc2VtYmxfZ2VuZV9pZCwgZXh0ZXJuYWxfZ2VuZV9uYW1lKSAlPiUKICBmaWx0ZXIoZW5zZW1ibF9nZW5lX2lkICVpbiUgcm93bmFtZXMobW1fZGVfbm9ybWFsX3A4X3NjbiRkZXNlcSRhbGxfdGFibGVzJGtvX3Njbl92c19oZXRfc2NuKSkgJT4lCiAgZGlzdGluY3QoKQoKbW1fZGVfbm9ybWFsX3A4X3NjbiRkZXNlcSRhbGxfdGFibGVzJGtvX3Njbl92c19oZXRfc2NuIDwtIG1lcmdlKAogIG1tX2RlX25vcm1hbF9wOF9zY24kZGVzZXEkYWxsX3RhYmxlcyRrb19zY25fdnNfaGV0X3NjbiwKICBhbm5vdHNfdG9fbWVyZ2UsIGJ5LnggPSAwLCBieS55ID0gImVuc2VtYmxfZ2VuZV9pZCIsIGFsbC54ID0gVFJVRSkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KaGV0a2VlcGVyX2dlbmVzIDwtIG1tX2RlX25vcm1hbF9wOF9zY24kZGVzZXEkYWxsX3RhYmxlcyR3dF9zY25fdnNfaGV0X3NjbiAlPiUKICBmaWx0ZXIobG9nRkMgPD0gLS4xICYgYWRqLlAuVmFsIDw9IDAuMDUpCgprb2tlZXBlcl9nZW5lcyA8LSBtbV9kZV9ub3JtYWxfcDhfc2NuJGRlc2VxJGFsbF90YWJsZXMkd3Rfc2NuX3ZzX2tvX3NjbiAlPiUKICBmaWx0ZXIobG9nRkMgPD0gLS4xICYgYWRqLlAuVmFsIDw9IDAuMDUpCgprZWVwZXJnZW5lcyA8LSB1bmlxdWUoYyhyb3duYW1lcyhoZXRrZWVwZXJfZ2VuZXMpLCByb3duYW1lcyhrb2tlZXBlcl9nZW5lcykpKQoKZGYgPC0gbW1fZGVfbm9ybWFsX3A4X3NjbiRkZXNlcSRhbGxfdGFibGVzJGtvc2NuX3ZzX2hldHNjbiAlPiUKICBkcGx5cjo6bXV0YXRlKGxvZ0ZDID0gLWxvZ0ZDKSAlPiUKICBzZXRfc2lnX2xpbW1hKGZhY3RvcnMgPSBjKCJIZXQgRW5yaWNoZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIktPIEVucmljaGVkIikpCgpkZiA8LSBkZiAlPiUKICBmaWx0ZXIoUm93Lm5hbWVzICVpbiUga2VlcGVyZ2VuZXMpCgpsYWJlbHNfdXBzIDwtIGRmICU+JQogIGZpbHRlcihhYnMobG9nRkMpID4gMSkgJT4lCiAgYXJyYW5nZShsb2dGQykgJT4lCiAgaGVhZChuID0gMSkKCmxhYmVsc19kb3ducyA8LSBkZiAlPiUKICBmaWx0ZXIoYWJzKGxvZ0ZDKSA+IDEpICU+JQogIGFycmFuZ2UoLWxvZ0ZDKSAlPiUKICBoZWFkKG4gPSAxKQoKbGFiZWxzIDwtIHJiaW5kKGxhYmVsc191cHMsIGxhYmVsc19kb3ducykKCnJlc190YmwgPC0gZGYKREVwbG90IDwtIGdncGxvdChyZXNfdGJsLCBhZXMoeCA9IGxvZ0ZDLCB5ID0gLWxvZzEwKGFkai5QLlZhbCksIGxhYmVsID0gZXh0ZXJuYWxfZ2VuZV9uYW1lKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IFNpZ25pZmljYW5jZSksIHNpemUgPSA0KSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygtMSwgMSkpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAoMC4wNSkpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDIwKSArCiAgeGxhYigibG9nMihGQykiKSArCiAgeWxhYigiLWxvZzEwKHAtdmFsdWUpIikgKwogICMjIGdndGl0bGUodGl0bGUsIHN1YnRpdGxlID0gc3VidGl0bGUpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiSGV0IEVucmljaGVkIiA9ICIjRjg3NjZEIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktPIEVucmljaGVkIiA9ICIjMDBCRkM0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5vdFxuIEVucmljaGVkIiA9ICJHcmV5IikpICsKICBnZW9tX2xhYmVsX3JlcGVsKGRhdGE9ZmlsdGVyKGRmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyMgYygnczVfaGV0X2RsZ24nLCAnczVfaGV0X3JldCcsICdzNV9oZXRfc2NuJykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXh0ZXJuYWxfZ2VuZV9uYW1lICVpbiUgbGFiZWxzJGV4dGVybmFsX2dlbmVfbmFtZSksCiAgICAgICAgICAgICAgICAgICAjIyBudWRnZV94ID0gLTAuNSwKICAgICAgICAgICAgICAgICAgIG51ZGdlX3kgPSAzLCBtYXgub3ZlcmxhcHMgPSAxNSkgKwogIGdndGl0bGUoIlNDTiBIZXQgdnMgS08gVHJhbnNsYXRvbWUiKQoKcHAoZmlsZSA9ICJpbWFnZXMvcDA4X3Njbl9ERV8xMzEyMDI0LnBkZiIpCkRFcGxvdApkZXYub2ZmKCkKCndyaXRleGw6OndyaXRlX3hsc3goZGYsIHBhdGggPSAiZXhjZWwvc2NuaGV0X3ZzX3NjbmtvX1dUZmlsdGVyZWQueGxzeCIpCmBgYAoKIyMgSG93IG1hbnkgdXBzL2Rvd25zCgpgYGB7ciwgZXZhbD1GQUxTRX0Ka29fZW5yaWNoZWQgPC0gZGYgJT4lCiAgZmlsdGVyKFNpZ25pZmljYW5jZSA9PSAiS08gRW5yaWNoZWQiKQoKaGV0X2VucmljaGVkIDwtIGRmICU+JQogIGZpbHRlcihTaWduaWZpY2FuY2UgPT0gIkhldCBFbnJpY2hlZCIpCmBgYAoKIyMgR1NFQQoKYGBge3IsIGV2YWw9RkFMU0V9CmtvX2dlbmVzIDwtIHJlc190YmwgJT4lCiAgZmlsdGVyKGFkai5QLlZhbCA8PSAwLjA1KSAlPiUKICBhcnJhbmdlKC1hYnMobG9nRkMpKSAlPiUKICBzZWxlY3QoUm93Lm5hbWVzLCBsb2dGQywgYWRqLlAuVmFsLCBleHRlcm5hbF9nZW5lX25hbWUsIFNpZ25pZmljYW5jZSkgJT4lCiAgZmlsdGVyKGxvZ0ZDIDw9IC0xKQoKaGV0X2dlbmVzIDwtIHJlc190YmwgJT4lCiAgZmlsdGVyKGFkai5QLlZhbCA8PSAwLjA1KSAlPiUKICBhcnJhbmdlKC1hYnMobG9nRkMpKSAlPiUKICBzZWxlY3QoUm93Lm5hbWVzLCBsb2dGQywgYWRqLlAuVmFsLCBleHRlcm5hbF9nZW5lX25hbWUsIFNpZ25pZmljYW5jZSkgJT4lCiAgZmlsdGVyKGxvZ0ZDID49IDEpCgphbGxkeXNyZWd1bGF0ZWRfZ2VuZXMgPC0gcmVzX3RibCAlPiUKICBmaWx0ZXIoYWRqLlAuVmFsIDw9IDAuMDUpICU+JQogIGFycmFuZ2UobG9nRkMpICU+JQogIHNlbGVjdChSb3cubmFtZXMsIGxvZ0ZDLCBhZGouUC5WYWwsIGV4dGVybmFsX2dlbmVfbmFtZSwgU2lnbmlmaWNhbmNlKSAlPiUKICBmaWx0ZXIoYWJzKGxvZ0ZDKSA+PSAxKQoKZ3NlYV9yZXN1bHRfa28gPC0gZ29zdChxdWVyeSA9IGtvX2dlbmVzJGV4dGVybmFsX2dlbmVfbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICJtbXVzY3VsdXMiLAogICAgICAgICAgICAgICAgICAgICAgIGV2Y29kZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWRfcXVlcnkgPSBUUlVFKQoKZ3NlYV9yZXN1bHRfaGV0IDwtIGdvc3QocXVlcnkgPSBoZXRfZ2VuZXMkZXh0ZXJuYWxfZ2VuZV9uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICJtbXVzY3VsdXMiLAogICAgICAgICAgICAgICAgICAgICAgICBldmNvZGVzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZF9xdWVyeSA9IFRSVUUpCgpnc2VhX3Jlc3VsdF9hbGxkeXNyZWd1bGF0ZWQgPC0gZ29zdChxdWVyeSA9IGFsbGR5c3JlZ3VsYXRlZF9nZW5lcyRleHRlcm5hbF9nZW5lX25hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZ2FuaXNtID0gIm1tdXNjdWx1cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV2Y29kZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkX3F1ZXJ5ID0gVFJVRSkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KZ3NlYV9rbyA8LSAgZ3NlYV9yZXN1bHRfa29bWyJyZXN1bHQiXV0gJT4lCiAgc2VsZWN0KHRlcm1fbmFtZSwgcF92YWx1ZSwgdGVybV9zaXplLCBpbnRlcnNlY3Rpb25fc2l6ZSwgcmVjYWxsLCBzb3VyY2UsIGludGVyc2VjdGlvbikgJT4lCiAgYXJyYW5nZShkZXNjKHJlY2FsbCkpICU+JQogIGhlYWQobiA9IDEwKQpnc2VhX3Bsb3RzX2tvIDwtIGdncGxvdChnc2VhX2tvLCBhZXMoeCA9IHJlY2FsbCwgeSA9IHJlb3JkZXIodGVybV9uYW1lLCByZWNhbGwpLCBmaWxsID0gcF92YWx1ZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIHNjYWxlX2ZpbGxfY29udGludW91cyhsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIikgKwogIHRoZW1lX2J3KCkgKwogIHlsYWIoIiIpICsKICB4bGFiKCJPdmVyIGVucmljaG1lbnQgU2NvcmUiKQoKZ3NlYV9oZXQgPC0gIGdzZWFfcmVzdWx0X2hldFtbInJlc3VsdCJdXSAlPiUKICBzZWxlY3QodGVybV9uYW1lLCBwX3ZhbHVlLCB0ZXJtX3NpemUsIGludGVyc2VjdGlvbl9zaXplLCByZWNhbGwsIHNvdXJjZSwgaW50ZXJzZWN0aW9uKSAlPiUKICBhcnJhbmdlKGRlc2MocmVjYWxsKSkgJT4lCiAgaGVhZChuID0gMTApCmdzZWFfcGxvdHNfaGV0IDwtIGdncGxvdChnc2VhX2hldCwgYWVzKHggPSByZWNhbGwsIHkgPSByZW9yZGVyKHRlcm1fbmFtZSwgcmVjYWxsKSwgZmlsbCA9IHBfdmFsdWUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBzY2FsZV9maWxsX2NvbnRpbnVvdXMobG93ID0gImJsdWUiLCBoaWdoID0gInJlZCIpICsKICB0aGVtZV9idygpICsKICB5bGFiKCIiKSArCiAgeGxhYigiT3ZlciBlbnJpY2htZW50IFNjb3JlIikKCmdzZWFfYWxsIDwtICBnc2VhX3Jlc3VsdF9hbGxkeXNyZWd1bGF0ZWRbWyJyZXN1bHQiXV0gJT4lCiAgc2VsZWN0KHRlcm1fbmFtZSwgcF92YWx1ZSwgdGVybV9zaXplLCBpbnRlcnNlY3Rpb25fc2l6ZSwgcmVjYWxsLCBzb3VyY2UsIGludGVyc2VjdGlvbikgJT4lCiAgYXJyYW5nZShkZXNjKHJlY2FsbCkpICU+JQogIGhlYWQobiA9IDEwKQpnc2VhX3Bsb3RzX2FsbCA8LSBnZ3Bsb3QoZ3NlYV9hbGwsIGFlcyh4ID0gcmVjYWxsLCB5ID0gcmVvcmRlcih0ZXJtX25hbWUsIHJlY2FsbCksIGZpbGwgPSBwX3ZhbHVlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgc2NhbGVfZmlsbF9jb250aW51b3VzKGxvdyA9ICJibHVlIiwgaGlnaCA9ICJyZWQiKSArCiAgdGhlbWVfYncoKSArCiAgeWxhYigiIikgKwogIHhsYWIoIk92ZXIgZW5yaWNobWVudCBTY29yZSIpCgpwcChmaWxlID0gImltYWdlcy9HU0VBX3AwOF9yZXRpbmFfYXhvbnRyYXBfYWxsZHlzcmVndWxhdGVkZ2VuZXMucGRmIikKZ3NlYV9wbG90c19hbGwKZGV2Lm9mZigpCmBgYAoKIyBIZXQgUmV0aW5hIHZzIEhldCBTQ04KCmBgYHtyLCBldmFsPUZBTFNFfQptbTM4X3N1YnNldDIgPC0gc3Vic2V0X2V4cHQoCiAgbW0zOF9oaXNhdCwKICBzdWJzZXQgPSAiKGJhdGNoID09ICc0JyB8IGJhdGNoID09ICc1JyB8IGJhdGNoID09ICc2JykgJiB0aW1lID09ICdwMDgnICYgZ2Vub3R5cGUgIT0gJ2tvJyAmIGxvY2F0aW9uICE9ICdkbGduJyB8IHNhbXBsZWlkID09ICdpcHJnY18wMyciKQoKbW0zOF9zdWJzZXQyIDwtIHN1YnNldF9leHB0KG1tMzhfc3Vic2V0Miwgc3Vic2V0ID0gInNhbXBsZWlkICE9ICdpcHJnY184OSciKQptbTM4X3N1YnNldDIkZGVzaWduICU+JQogIHNlbGVjdChnZW5vdHlwZSwgbG9jYXRpb24pICU+JQogIHRhYmxlKCkKbW0zOF9ub3JtMiA8LSBub3JtYWxpemVfZXhwdChtbTM4X3N1YnNldDIsIGZpbHRlcj1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnZlcnQ9ImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhbnNmb3JtPSJsb2cyIiwgYmF0Y2ggPSAic3Zhc2VxIikKYGBgCgojIyBQQ0EKCmBgYHtyLCBldmFsPUZBTFNFfQptbTM4X25vcm0yIDwtIHNldF9leHB0X2JhdGNoZXMobW0zOF9ub3JtMiwgZmFjdCA9ICJsb2NhdGlvbiIpCm1tMzhfbm9ybTIgPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhtbTM4X25vcm0yLCBmYWN0ID0gImdlbm90eXBlIikKcGNhX25vcm0yIDwtIHBsb3RfcGNhKG1tMzhfbm9ybTIsIG1heF9vdmVybGFwcyA9IDcwKQpwY2Ffbm9ybTIkcGxvdApgYGAKCiMjIERFCgpgYGB7ciwgZXZhbD1GQUxTRX0KbW1fZGVfc3Vic2V0MiA8LSBhbGxfcGFpcndpc2UobW0zOF9zdWJzZXQyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF9iYXRjaD0ic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYWxsZWw9RkFMU0UsIGRvX2Vic2VxPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb19iYXNpYyA9IEZBTFNFLCBkb19kcmVhbSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb19ub2lzZXEgPSBGQUxTRSwgZG9fZWRnZXIgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KcmV0aW5ha2VlcGVyX2dlbmVzIDwtIG1tX2RlX3N1YnNldDIkZGVzZXEkYWxsX3RhYmxlcyR3dF9yZXRpbmFfdnNfaGV0X3JldGluYSAlPiUKICBmaWx0ZXIobG9nRkMgPD0gLTAuMSAmIGFkai5QLlZhbCA8PSAwLjA1KQoKc2Nua2VlcGVyX2dlbmVzIDwtIG1tX2RlX3N1YnNldDIkZGVzZXEkYWxsX3RhYmxlcyR3dF9zY25fdnNfaGV0X3NjbiAlPiUKICBmaWx0ZXIobG9nRkMgPD0gLTAuMSAmIGFkai5QLlZhbCA8PSAwLjA1KQoKa2VlcGVyZ2VuZXMgPC0gdW5pcXVlKGMocm93bmFtZXMocmV0aW5ha2VlcGVyX2dlbmVzKSwgcm93bmFtZXMoc2Nua2VlcGVyX2dlbmVzKSkpCgphbm5vdHNfdG9fbWVyZ2UgPC0gbW1fYW5ub3QgJT4lCiAgc2VsZWN0KGVuc2VtYmxfZ2VuZV9pZCwgZXh0ZXJuYWxfZ2VuZV9uYW1lKSAlPiUKICBmaWx0ZXIoZW5zZW1ibF9nZW5lX2lkICVpbiUgcm93bmFtZXMobW1fZGVfc3Vic2V0MiRkZXNlcSRhbGxfdGFibGVzJGhldF9zY25fdnNfaGV0X3JldGluYSkpICU+JQogIGRpc3RpbmN0KCkKCm1tX2RlX3N1YnNldDIkZGVzZXEkYWxsX3RhYmxlcyRoZXRfc2NuX3ZzX2hldF9yZXRpbmEgPC0gbWVyZ2UoCiAgbW1fZGVfc3Vic2V0MiRkZXNlcSRhbGxfdGFibGVzJGhldF9zY25fdnNfaGV0X3JldGluYSwKICBhbm5vdHNfdG9fbWVyZ2UsIGJ5LnggPSAwLAogIGJ5LnkgPSAiZW5zZW1ibF9nZW5lX2lkIiwgYWxsLnggPSBUUlVFKQoKZGYgPC0gbW1fZGVfc3Vic2V0MiRkZXNlcSRhbGxfdGFibGVzJGhldF9zY25fdnNfaGV0X3JldGluYSAlPiUKICBtdXRhdGUoU2lnbmlmaWNhbmNlID0gY2FzZV93aGVuKGxvZ0ZDIDw9IC0xLjAgfiAiUmV0aW5hIEVucmljaGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ0ZDID49IDEuMCB+ICJTQ04gRW5yaWNoZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nRkMgPiAtMS4wICYgbG9nRkMgPCAxLjAgfiAiTm90XG4gRW5yaWNoZWQiKSkKCmRmIDwtIGRmICU+JQogIGZpbHRlcihSb3cubmFtZXMgJWluJSBrZWVwZXJnZW5lcykKCnNjbl9lbnJpY2hlZCA8LSBkZiAlPiUKICBmaWx0ZXIoYWRqLlAuVmFsIDw9IDAuMDUgJiBsb2dGQyA+PSAxLjApICU+JQogIGFycmFuZ2UoLWxvZ0ZDKSAlPiUKICBzZWxlY3QoUm93Lm5hbWVzLCBleHRlcm5hbF9nZW5lX25hbWUsIGxvZ0ZDLCBhZGouUC5WYWwpICU+JQogIG11dGF0ZShTaWduaWZpY2FuY2UgPSAiU0NOIEVucmljaGVkIikgJT4lCiAgZmlsdGVyKFJvdy5uYW1lcyAlaW4lIHJvd25hbWVzKHNjbmtlZXBlcl9nZW5lcykpCgpyZXRpbmFfZW5yaWNoZWQgPC0gZGYgJT4lCiAgZmlsdGVyKGFkai5QLlZhbCA8PSAwLjA1ICYgbG9nRkMgPD0gLTEuMCkgJT4lCiAgYXJyYW5nZShsb2dGQykgICU+JQogIHNlbGVjdChSb3cubmFtZXMsIGV4dGVybmFsX2dlbmVfbmFtZSwgbG9nRkMsIGFkai5QLlZhbCkgJT4lCiAgbXV0YXRlKFNpZ25pZmljYW5jZSA9ICJSZXRpbmEgRW5yaWNoZWQiKSAlPiUKICBmaWx0ZXIoUm93Lm5hbWVzICVpbiUgcm93bmFtZXMocmV0aW5ha2VlcGVyX2dlbmVzKSkKCm5vdGVucmljaGVkIDwtIGRmICU+JQogIHNlbGVjdChSb3cubmFtZXMsIGV4dGVybmFsX2dlbmVfbmFtZSwgbG9nRkMsIGFkai5QLlZhbCwgU2lnbmlmaWNhbmNlKSAlPiUKICBmaWx0ZXIoUm93Lm5hbWVzICVpbiUgYyhyb3duYW1lcyhyZXRpbmFrZWVwZXJfZ2VuZXMpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzKHNjbmtlZXBlcl9nZW5lcykpW2R1cGxpY2F0ZWQoYyhyb3duYW1lcyhyZXRpbmFrZWVwZXJfZ2VuZXMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3duYW1lcyhzY25rZWVwZXJfZ2VuZXMpKSldKSAlPiUKICBmaWx0ZXIoU2lnbmlmaWNhbmNlID09ICJOb3RcbiBFbnJpY2hlZCIpCgpkZiA8LSByYmluZChzY25fZW5yaWNoZWQsIHJldGluYV9lbnJpY2hlZCwgbm90ZW5yaWNoZWQpCmRmIDwtIGRmICU+JQogIGRpc3RpbmN0KCkKCiMjIHdyaXRleGw6OndyaXRlX3hsc3goZGYsIHBhdGggPSAiYXhvblRSQVBfREVfcmVzdWx0c18yMDI0MDIwMi9yZXRpbmFoZXRfdnNfc2NuX2hldF9XVGZpbHRlcmVkLnhsc3giKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpsYWJlbHNfdXBzIDwtIGRmICU+JQogIGZpbHRlcihhZGouUC5WYWwgPD0gMC4wNSAmIGFicyhsb2dGQykgPiAxLjApICU+JQogIGFycmFuZ2UobG9nRkMpICU+JQogIGhlYWQobiA9IDEwKQoKbGFiZWxzX2Rvd25zIDwtIGRmICU+JQogIGZpbHRlcihhZGouUC5WYWwgPD0gMC4wNSAmIGFicyhsb2dGQykgPiAxLjApICU+JQogIGFycmFuZ2UoLWxvZ0ZDKSAlPiUKICBoZWFkKG4gPSAxMCkKCmxhYmVscyA8LSByYmluZChsYWJlbHNfdXBzLCBsYWJlbHNfZG93bnMpCgpsYWJlbHNfcmVxdWVzdGVkIDwtIGMoIkNkaDEwIiwiQ2RoMTIiLCJDZGgxMyIsIkNkaDE4IiwKICAgICAgICAgICAgICAgICAgICAgICJDZGg3IiwiQ2RoOCIsIkNkaDkiLCJDbnRuMyIsCiAgICAgICAgICAgICAgICAgICAgICAiQ250bjQiLCJDbnRuNSIsIkNudG42IiwiS2lycmVsMyIsCiAgICAgICAgICAgICAgICAgICAgICAiTnJ4bjEiLCJOcnhuMyIsIlNlbWEzYyIsIlNlbWE2ZCIsCiAgICAgICAgICAgICAgICAgICAgICAiVGVubTEiLCJUZW5tMiIsIlRlbm00IikKcmVzX3RibCA8LSBkZgpERXBsb3QgPC0gZ2dwbG90KHJlc190YmwsIGFlcyh4ID0gbG9nRkMsIHkgPSAtbG9nMTAoYWRqLlAuVmFsKSwgbGFiZWwgPSBleHRlcm5hbF9nZW5lX25hbWUpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gU2lnbmlmaWNhbmNlKSwgc2l6ZSA9IDQpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKC0xLCAxKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSkgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMjApICsKICB4bGFiKCJsb2cyKEZDKSIpICsKICB5bGFiKCItbG9nMTAocC12YWx1ZSkiKSArCiAgIyMgZ2d0aXRsZSh0aXRsZSwgc3VidGl0bGUgPSBzdWJ0aXRsZSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJHcmV5IiwgIiNGODc2NkQiLCAiIzAwQkZDNCIpKSArCiAgZ2VvbV9sYWJlbF9yZXBlbChkYXRhPWZpbHRlcihkZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4dGVybmFsX2dlbmVfbmFtZSAlaW4lIGxhYmVsc19yZXF1ZXN0ZWQpLAogICAgICAgICAgICAgICAgICAgIyMgYyhsYWJlbHMkZXh0ZXJuYWxfZ2VuZV9uYW1lLCAiT3BuNCIpKSwgI2MoJ3M1X2hldF9kbGduJywgJ3M1X2hldF9yZXQnLCAnczVfaGV0X3NjbicpKSwKICAgICAgICAgICAgICAgICAgICMjIG51ZGdlX3ggPSAtMC41LAogICAgICAgICAgICAgICAgICAgbnVkZ2VfeSA9IDE1LCBtYXgub3ZlcmxhcHMgPSAyNSkKCiNwcChmaWxlID0gImF4b25UUkFQX1ZvbGNhbm9wbG90c18yMDI0MDIwMi9wMDhfcmV0aW5hdnNzY25oZXRfREVfcmVxdWVzdGVkX2dlbmVsYWJlbHNfMDIwNTIwMjQucGRmIikKREVwbG90CiNkZXYub2ZmKCkKYGBgCgojIyBIb3cgbWFueSB1cHMvZG93bnMKCmBgYHtyLCBldmFsPUZBTFNFfQpzY25fZW5yaWNoZWQgPC0gZGYgJT4lCiAgZmlsdGVyKGFkai5QLlZhbCA8PSAwLjA1ICYgbG9nRkMgPj0gMS4wKSAlPiUKICBhcnJhbmdlKC1sb2dGQykgJT4lCiAgc2VsZWN0KFJvdy5uYW1lcywgZXh0ZXJuYWxfZ2VuZV9uYW1lLCBsb2dGQywgYWRqLlAuVmFsLCBTaWduaWZpY2FuY2UpCgpyZXRpbmFfZW5yaWNoZWQgPC0gZGYgJT4lCiAgZmlsdGVyKGFkai5QLlZhbCA8PSAwLjA1ICYgbG9nRkMgPD0gLTEuMCkgJT4lCiAgYXJyYW5nZShsb2dGQykgICU+JQogIHNlbGVjdChSb3cubmFtZXMsIGV4dGVybmFsX2dlbmVfbmFtZSwgbG9nRkMsIGFkai5QLlZhbCwgU2lnbmlmaWNhbmNlKQoKc2NuX2VucmljaGVkCnJldGluYV9lbnJpY2hlZAoKZGYgJT4lCiAgZmlsdGVyKFNpZ25pZmljYW5jZSA9PSAiTm90XG4gRW5yaWNoZWQiKQpgYGAKCiMjIEdTRUEKCmBgYHtyLCBldmFsPUZBTFNFfQpnc2VhX3Jlc3VsdF9zY24gPC0gZ29zdChxdWVyeSA9IHNjbl9lbnJpY2hlZCRleHRlcm5hbF9nZW5lX25hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgIG9yZ2FuaXNtID0gIm1tdXNjdWx1cyIsIGV2Y29kZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkX3F1ZXJ5ID0gVFJVRSwgc291cmNlID0gYygiR08iKSkKCmdzZWFfcmVzdWx0X3JldCA8LSBnb3N0KHF1ZXJ5ID0gcmV0aW5hX2VucmljaGVkJGV4dGVybmFsX2dlbmVfbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5pc20gPSAibW11c2N1bHVzIiwgZXZjb2RlcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWRfcXVlcnkgPSBUUlVFLCBzb3VyY2UgPSBjKCJHTyIpKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpnc2VhX3NjbiA8LSAgZ3NlYV9yZXN1bHRfc2NuW1sicmVzdWx0Il1dICU+JQogIHNlbGVjdCh0ZXJtX25hbWUsIHBfdmFsdWUsIHRlcm1fc2l6ZSwgaW50ZXJzZWN0aW9uX3NpemUsIHJlY2FsbCwgc291cmNlLCBpbnRlcnNlY3Rpb24pICU+JQogIGFycmFuZ2UoZGVzYyhyZWNhbGwpKSAlPiUKICBoZWFkKG4gPSAyMCkKZ3NlYV9wbG90c19zY24gPC0gZ2dwbG90KGdzZWFfc2NuLCBhZXMoeCA9IHJlY2FsbCwgeSA9IHJlb3JkZXIodGVybV9uYW1lLCByZWNhbGwpLCBmaWxsID0gcF92YWx1ZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIHNjYWxlX2ZpbGxfY29udGludW91cyhsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIikgKwogIHRoZW1lX2J3KCkgKwogIHlsYWIoIiIpICsKICB4bGFiKCJPdmVyIGVucmljaG1lbnQgU2NvcmUiKQoKcHAoZmlsZSA9ICJpbWFnZXMvR1NFQV9TQ05oZXRfdnNfcmV0aW5hX2VucmljaGVkX1AwOC5wZGYiKQpnc2VhX3Bsb3RzX3NjbgpkZXYub2ZmKCkKCmdzZWFfcmV0IDwtICBnc2VhX3Jlc3VsdF9yZXRbWyJyZXN1bHQiXV0gJT4lCiAgc2VsZWN0KHRlcm1fbmFtZSwgcF92YWx1ZSwgdGVybV9zaXplLCBpbnRlcnNlY3Rpb25fc2l6ZSwgcmVjYWxsLCBzb3VyY2UsIGludGVyc2VjdGlvbikgJT4lCiAgYXJyYW5nZShkZXNjKHJlY2FsbCkpICU+JQogIGhlYWQobiA9IDIwKQpnc2VhX3Bsb3RzX3JldCA8LSBnZ3Bsb3QoZ3NlYV9yZXQsIGFlcyh4ID0gcmVjYWxsLCB5ID0gcmVvcmRlcih0ZXJtX25hbWUsIHJlY2FsbCksIGZpbGwgPSBwX3ZhbHVlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgc2NhbGVfZmlsbF9jb250aW51b3VzKGxvdyA9ICJibHVlIiwgaGlnaCA9ICJyZWQiKSArCiAgdGhlbWVfYncoKSArCiAgeWxhYigiIikgKwogIHhsYWIoIk92ZXIgZW5yaWNobWVudCBTY29yZSIpCgpwcChmaWxlID0gImltYWdlcy9HU0VBX1JldGluYWhldF92c19TQ05fZW5yaWNoZWRfUDA4LnBkZiIpCmdzZWFfcGxvdHNfcmV0CmRldi5vZmYoKQpgYGAKCiMgS08gUmV0aW5hIHZzIEtPIFNDTgoKYGBge3IsIGV2YWw9RkFMU0V9Cm1tMzhfc3Vic2V0MyA8LSBzdWJzZXRfZXhwdCgKICBtbTM4X2hpc2F0LAogIHN1YnNldCA9ICIoYmF0Y2ggPT0gJzQnIHwgYmF0Y2ggPT0gJzUnIHwgYmF0Y2ggPT0gJzYnKSAmIHRpbWUgPT0gJ3AwOCcgICYgZ2Vub3R5cGUgIT0gJ2hldCcgJiBsb2NhdGlvbiAhPSAnZGxnbicgfCBzYW1wbGVpZCA9PSAnaXByZ2NfMDMnIikKCm1tMzhfc3Vic2V0MyA8LSBzdWJzZXRfZXhwdChtbTM4X3N1YnNldDMsIHN1YnNldCA9ICJzYW1wbGVpZCAhPSAnaXByZ2NfODYnIikKbW0zOF9zdWJzZXQzJGRlc2lnbiAlPiUKICBzZWxlY3QoZ2Vub3R5cGUsIGxvY2F0aW9uKSAlPiUKICB0YWJsZSgpCm1tMzhfbm9ybTMgPC0gbm9ybWFsaXplX2V4cHQobW0zOF9zdWJzZXQzLCBmaWx0ZXI9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJ0PSJjcG0iLCB0cmFuc2Zvcm09ImxvZzIiLCBiYXRjaCA9ICJzdmFzZXEiKQpgYGAKCiMjIFBDQQoKYGBge3IsIGV2YWw9RkFMU0V9Cm1tMzhfbm9ybTMgPC0gc2V0X2V4cHRfYmF0Y2hlcyhtbTM4X25vcm0zLCBmYWN0ID0gImxvY2F0aW9uIikKbW0zOF9ub3JtMyA8LSBzZXRfZXhwdF9jb25kaXRpb25zKG1tMzhfbm9ybTMsIGZhY3QgPSAiZ2Vub3R5cGUiKQpwY2Ffbm9ybTMgPC0gcGxvdF9wY2EobW0zOF9ub3JtMywgbWF4X292ZXJsYXBzID0gNzApCnBjYV9ub3JtMyRwbG90CmBgYAoKIyMgREUKCmBgYHtyLCBldmFsPUZBTFNFfQptbV9kZV9zdWJzZXQzIDwtIGFsbF9wYWlyd2lzZShtbTM4X3N1YnNldDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX2JhdGNoPSJzdmFzZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbGxlbD1GQUxTRSwgZG9fZWJzZXE9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvX2Jhc2ljID0gRkFMU0UsIGRvX2RyZWFtID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvX25vaXNlcSA9IEZBTFNFLCBkb19lZGdlciA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFKQoKcmV0aW5ha2VlcGVyX2dlbmVzIDwtIG1tX2RlX3N1YnNldDMkZGVzZXEkYWxsX3RhYmxlcyR3dHJldGluYV92c19rb3JldGluYSAlPiUKICBmaWx0ZXIobG9nRkMgPD0gLTEuMCAmIGFkai5QLlZhbCA8PSAwLjA1KQoKc2Nua2VlcGVyX2dlbmVzIDwtIG1tX2RlX3N1YnNldDMkZGVzZXEkYWxsX3RhYmxlcyR3dHNjbl92c19rb3NjbiAlPiUKICBmaWx0ZXIobG9nRkMgPD0gLTEuMCAmIGFkai5QLlZhbCA8PSAwLjA1KQoKa2VlcGVyZ2VuZXMgPC0gdW5pcXVlKGMocm93bmFtZXMocmV0aW5ha2VlcGVyX2dlbmVzKSwgcm93bmFtZXMoc2Nua2VlcGVyX2dlbmVzKSkpCgphbm5vdHNfdG9fbWVyZ2UgPC0gbW1fYW5ub3QgJT4lCiAgc2VsZWN0KGVuc2VtYmxfZ2VuZV9pZCwgZXh0ZXJuYWxfZ2VuZV9uYW1lKSAlPiUKICBmaWx0ZXIoZW5zZW1ibF9nZW5lX2lkICVpbiUgcm93bmFtZXMobW1fZGVfc3Vic2V0MyRkZXNlcSRhbGxfdGFibGVzJGtvX3Njbl92c19rb19yZXRpbmEpKSAlPiUKICBkaXN0aW5jdCgpCgptbV9kZV9zdWJzZXQzJGRlc2VxJGFsbF90YWJsZXMka29fc2NuX3ZzX2tvX3JldGluYSA8LSBtZXJnZSgKICBtbV9kZV9zdWJzZXQzJGRlc2VxJGFsbF90YWJsZXMka29fc2NuX3ZzX2tvX3JldGluYSwKICBhbm5vdHNfdG9fbWVyZ2UsIGJ5LnggPSAwLAogIGJ5LnkgPSAiZW5zZW1ibF9nZW5lX2lkIiwgYWxsLnggPSBUUlVFKQoKZGYgPC0gbW1fZGVfc3Vic2V0MyRkZXNlcSRhbGxfdGFibGVzJGtvX3Njbl92c19rb19yZXRpbmEgJT4lCiAgbXV0YXRlKFNpZ25pZmljYW5jZSA9IGNhc2Vfd2hlbihsb2dGQyA8PSAtMSB+ICJSZXRpbmEgRW5yaWNoZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nRkMgPj0gMSB+ICJTQ04gRW5yaWNoZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nRkMgPiAtMSAmIGxvZ0ZDIDwgMSB+ICJOb3RcbiBFbnJpY2hlZCIpKQoKZGYgPC0gZGYgJT4lCiAgZmlsdGVyKFJvdy5uYW1lcyAlaW4lIGtlZXBlcmdlbmVzKQoKc2NuX2VucmljaGVkIDwtIGRmICU+JQogIGZpbHRlcihhZGouUC5WYWwgPD0gMC4wNSAmIGxvZ0ZDID49IDEpICU+JQogIGFycmFuZ2UoLWxvZ0ZDKSAlPiUKICBzZWxlY3QoUm93Lm5hbWVzLCBleHRlcm5hbF9nZW5lX25hbWUsIGxvZ0ZDLCBhZGouUC5WYWwpICU+JQogIG11dGF0ZShTaWduaWZpY2FuY2UgPSAiU0NOIEVucmljaGVkIikgJT4lCiAgZmlsdGVyKFJvdy5uYW1lcyAlaW4lIHJvd25hbWVzKHNjbmtlZXBlcl9nZW5lcykpCgpkZiAlPiUKICBmaWx0ZXIoYWRqLlAuVmFsIDw9IDAuMDUgJiBsb2dGQyA8PSAtMSkgJT4lCiAgYXJyYW5nZShsb2dGQykgICU+JQogIHNlbGVjdChSb3cubmFtZXMsIGV4dGVybmFsX2dlbmVfbmFtZSwgbG9nRkMsIGFkai5QLlZhbCkgJT4lCiAgbXV0YXRlKFNpZ25pZmljYW5jZSA9ICJSZXRpbmEgRW5yaWNoZWQiKSAlPiUKICBmaWx0ZXIoUm93Lm5hbWVzICVpbiUgcm93bmFtZXMocmV0aW5ha2VlcGVyX2dlbmVzKSkgLT4gcmV0aW5hX2VucmljaGVkCgpub3RlbnJpY2hlZCA8LSBkZiAlPiUKICBzZWxlY3QoUm93Lm5hbWVzLCBleHRlcm5hbF9nZW5lX25hbWUsIGxvZ0ZDLCBhZGouUC5WYWwsIFNpZ25pZmljYW5jZSkgJT4lCiAgZmlsdGVyKFJvdy5uYW1lcyAlaW4lIGMocm93bmFtZXMocmV0aW5ha2VlcGVyX2dlbmVzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICByb3duYW1lcyhzY25rZWVwZXJfZ2VuZXMpKVtkdXBsaWNhdGVkKGMocm93bmFtZXMocmV0aW5ha2VlcGVyX2dlbmVzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93bmFtZXMoc2Nua2VlcGVyX2dlbmVzKSkpXSkKCmRmIDwtIHJiaW5kKHNjbl9lbnJpY2hlZCwgcmV0aW5hX2VucmljaGVkLCBub3RlbnJpY2hlZCkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KbGFiZWxzX3VwcyA8LSBkZiAlPiUKICBmaWx0ZXIoYWRqLlAuVmFsIDw9IDAuMDUgJiBhYnMobG9nRkMpID4gMSkgJT4lCiAgYXJyYW5nZShsb2dGQykgJT4lCiAgaGVhZChuID0gMTApCgpsYWJlbHNfZG93bnMgPC0gZGYgJT4lCiAgZmlsdGVyKGFkai5QLlZhbCA8PSAwLjA1ICYgYWJzKGxvZ0ZDKSA+IDEpICU+JQogIGFycmFuZ2UoLWxvZ0ZDKSAlPiUKICBoZWFkKG4gPSAxMCkKCmxhYmVscyA8LSByYmluZChsYWJlbHNfdXBzLCBsYWJlbHNfZG93bnMpCiMjIHdhbnRlZF9jb2x1bW4gPC0gIlNpZ25pZmljYW5jZSIKCnJlc190YmwgPC0gZGYKREVwbG90IDwtIGdncGxvdChyZXNfdGJsLCBhZXMoeCA9IGxvZ0ZDLCB5ID0gLWxvZzEwKGFkai5QLlZhbCksIGxhYmVsID0gZXh0ZXJuYWxfZ2VuZV9uYW1lKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IFNpZ25pZmljYW5jZSksIHNpemUgPSA0KSArCiAgIyMgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gISFzeW0od2FudGVkX2NvbHVtbikpLCBzaXplID0gNCkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoLTEsIDEpKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAyMCkgKwogIHhsYWIoImxvZzIoRkMpIikgKwogIHlsYWIoIi1sb2cxMChwLXZhbHVlKSIpICsKICAjIyBnZ3RpdGxlKHRpdGxlLCBzdWJ0aXRsZSA9IHN1YnRpdGxlKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJHcmV5IiwgIiNGODc2NkQiLCAiIzAwQkZDNCIpKSArCiAgZ2VvbV9sYWJlbF9yZXBlbChkYXRhID0gZmlsdGVyKAogICAgZGYsIGV4dGVybmFsX2dlbmVfbmFtZSAlaW4lIGMobGFiZWxzJGV4dGVybmFsX2dlbmVfbmFtZSwgIk9wbjQiKSksCiAgICAjIyBjKCdzNV9oZXRfZGxnbicsICdzNV9oZXRfcmV0JywgJ3M1X2hldF9zY24nKSksCiAgICAjIyBudWRnZV94ID0gLTAuNSwKICAgIG51ZGdlX3kgPSAxMCwgbWF4Lm92ZXJsYXBzID0gMjUpCgpwcChmaWxlID0gImltYWdlcy9wMDhfcmV0aW5hdnNzY25rb19ERV8xMzEyMDI0LnBkZiIpCkRFcGxvdApkZXYub2ZmKCkKYGBgCgojIyBIb3cgbWFueSB1cHMvZG93bnMKCmBgYHtyLCBldmFsPUZBTFNFfQpzY25fZW5yaWNoZWQKcmV0aW5hX2VucmljaGVkCm5vdGVucmljaGVkICU+JQogIGZpbHRlcihTaWduaWZpY2FuY2UgPT0gIk5vdFxuIEVucmljaGVkIikKYGBgCgojIyBHU0VBCgpgYGB7ciwgZXZhbD1GQUxTRX0KZ3NlYV9yZXN1bHRfc2NuIDwtIGdvc3QocXVlcnkgPSBzY25fZW5yaWNoZWQkZXh0ZXJuYWxfZ2VuZV9uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICJtbXVzY3VsdXMiLAogICAgICAgICAgICAgICAgICAgICAgICBldmNvZGVzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZF9xdWVyeSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZSA9IGMoIkdPIikpCgpnc2VhX3Jlc3VsdF9yZXQgPC0gZ29zdChxdWVyeSA9IHJldGluYV9lbnJpY2hlZCRleHRlcm5hbF9nZW5lX25hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgIG9yZ2FuaXNtID0gIm1tdXNjdWx1cyIsCiAgICAgICAgICAgICAgICAgICAgICAgIGV2Y29kZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkX3F1ZXJ5ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgc291cmNlID0gYygiR08iKSkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KZ3NlYV9zY24gPC0gIGdzZWFfcmVzdWx0X3NjbltbInJlc3VsdCJdXSAlPiUKICBzZWxlY3QodGVybV9uYW1lLCBwX3ZhbHVlLCB0ZXJtX3NpemUsIGludGVyc2VjdGlvbl9zaXplLCByZWNhbGwsIHNvdXJjZSwgaW50ZXJzZWN0aW9uKSAlPiUKICBhcnJhbmdlKGRlc2MocmVjYWxsKSkgJT4lCiAgaGVhZChuID0gMjApCmdzZWFfcGxvdHNfc2NuIDwtIGdncGxvdChnc2VhX3NjbiwgYWVzKHggPSByZWNhbGwsIHkgPSByZW9yZGVyKHRlcm1fbmFtZSwgcmVjYWxsKSwgZmlsbCA9IHBfdmFsdWUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBzY2FsZV9maWxsX2NvbnRpbnVvdXMobG93ID0gImJsdWUiLCBoaWdoID0gInJlZCIpICsKICB0aGVtZV9idygpICsKICB5bGFiKCIiKSArCiAgeGxhYigiR1NFQSBTY29yZSIpCgpwcChmaWxlID0gImltYWdlcy9HU0VBX1NDTmtvX2VucmljaGVkX3ZzX3JldGluYV9QMDgucGRmIikKZ3NlYV9wbG90c19zY24KZGV2Lm9mZigpCgpnc2VhX3JldCA8LSAgZ3NlYV9yZXN1bHRfcmV0W1sicmVzdWx0Il1dICU+JQogIHNlbGVjdCh0ZXJtX25hbWUsIHBfdmFsdWUsIHRlcm1fc2l6ZSwgaW50ZXJzZWN0aW9uX3NpemUsIHJlY2FsbCwgc291cmNlLCBpbnRlcnNlY3Rpb24pICU+JQogIGFycmFuZ2UoZGVzYyhyZWNhbGwpKSAlPiUKICBoZWFkKG4gPSAyMCkKZ3NlYV9wbG90c19yZXQgPC0gZ2dwbG90KGdzZWFfcmV0LCBhZXMoeCA9IHJlY2FsbCwgeSA9IHJlb3JkZXIodGVybV9uYW1lLCByZWNhbGwpLCBmaWxsID0gcF92YWx1ZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIHNjYWxlX2ZpbGxfY29udGludW91cyhsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIikgKwogIHRoZW1lX2J3KCkgKwogIHlsYWIoIiIpICsKICB4bGFiKCJHU0VBIFNjb3JlIikKCnBwKGZpbGUgPSAiaW1hZ2VzL0dTRUFfUmV0aW5ha29fZW5yaWNoZWRfdnNfU0NOX1AwOC5wZGYiKQpnc2VhX3Bsb3RzX3JldApkZXYub2ZmKCkKYGBgCgojIE15IHZlcnNpb24gb2YgdGhlIGdsb2JhbCBhbmFseXNpcwoKSSB3YW50IHRvIGhhdmUgYW4gaW52b2NhdGlvbiBvZiBhbGxfcGFpcndpc2UoKSB3aGljaCB1c2VzIGFsbCBzYW1wbGVzLAppbiB0aGUgZm9sbG93aW5nIGJsb2NrIEkgd2lsbCBzZXQgdGhhdCB1cCB1c2luZyBhIHNldCBvZiAna2VlcGVycycKd2hpY2ggd2lsbCBiZSBuYW1lZCBieSB0aW1lLCBsb2NhdGlvbiwgdGhlbiAyIGxldHRlcnMgZm9yIHRoZQpudW1lcmF0b3IvZGVub21pbmF0b3I6IHcgZm9yIFdULCBoIGZvciBoZXQsIGQgZm9yIGRlbHRhOyB0aHVzCiJwMDhfcmV0aW5hX2h3IiBpcyBjb21wYXJpbmcgdGhlIGhldC93dCBmb3IgdGhlIHAwOCByZXRpbmEgc2FtcGxlcy4KCklmIHRoZXkgYXJlIG9mIGludGVyZXN0LCBJIHdpbGwgaGF2ZSBhIHNlcGFyYXRlIHNldCB3aGljaCBmb2xsb3dzIHRoZQpzYW1lIGNvbnZlbnRpb24gd2l0aCBuYW1lcyBsaWtlICJwMDhfa29fc3IiIHRvIGNvbXBhcmUgcDA4IGRlbHRhcyB3aXRoClNDTiBhcyB0aGUgbnVtZXJhdG9yIGFuZCByZXRpbmEgYXMgdGhlIGRlbm9taW5hdG9yLgoKIyMgU2V0IHVwIHRoZSBleGNsdXNpb24gZGF0YXNldAoKVGhlIG1vc3QgcGVjdWxpYXIgYXNwZWN0IG9mIHRoaXMgYW5hbHlzaXMgcmVzaWRlcyBpbiB0aGUgY2hvaWNlcwphcm91bmQgY2hvb3Npbmcgd2hpY2ggZ2VuZXMgdG8gY29uc2lkZXIgd2hlbiBjb21wYXJpbmcgdGhlCmdlbm90eXBlcy9sb2NhdGlvbnMvdGltZXMuICBUaGUgZ2VuZXJhbCBpZGVhIGlzIHByZXR0eSBjbGVhcjogZmluZCB0aGUKZ2VuZXMgd2hpY2ggYXJlIG5vbi1zcGVjaWZpY2FsbHkgYmVpbmcgcHVsbGVkIGRvd24gaW4gdGhlIFdUIHNhbXBsZXMKYW5kIGVpdGhlciBleGNsdWRlIG9yIGRpc2NvdW50IHRoZW0uICBUaGUgdmFyaW91cyBwb3RlbnRpYWwgbWV0aG9kcwpmb3IgcGVyZm9ybWluZyB0aGlzIGFyZSBjb25mdXNpbmc6CgoxLiAgV2hpY2ggc2V0IG9mIGNvbXBhcmlzb25zIG9mIHd0L2tvIHd0L2hldCBkbyB3ZSB1c2UgdG8KICAgIGV4Y2x1ZGUvZGlzY291bnQgZ2VuZXM/CiAgICBhLiAgU2hvdWxkIGl0IGJlIGEgY29tYmluYXRpb24gb2YgYWxsIHNhbXBsZXMgd3QgdnMuIHg/CiAgICBiLiAgU2hvdWxkIGl0IGJlIG9ubHkgdGhlICdyZWxldmFudCcgY29tcGFyaXNvbiwgZS5nLiBpZiB3ZSBhcmUKICAgIGNvbXBhcmluZyBwMDhfZGxnbl9oZXQgdnMuIHAwOF9zY25faGV0OyBkbyB3ZSByZW1vdmUgZ2VuZXMKICAgIG9ic2VydmVkIGluIChwMDhfZGxnbl9oZXQvd3QgJiYgcDA4X3Njbl9oZXQvd3QpCjIuICBEbyB3ZSBpbnN0ZWFkIGF0dGVtcHQgdG8gdXNlIHRoaXMgeC93dCBpbmZvcm1hdGlvbiB0byBub3JtYWxpemUKICAgIHRoZSBleHByZXNzaW9uIHZhbHVlcyBpbiB0aGUgb3RoZXIgY29uZGl0aW9ucyBhbmQga2VlcCB0aG9zZQogICAgZ2VuZXM/CgpUaGVyZXNhJ3MgY3VycmVudCB3b3Jrc2hlZXQgaW1wbGVtZW50cyBhIHZlcnNpb24gb2YgMWIgaW4gd2hpY2ggc2hlCnNlcGFyYXRlZCB0aGUgdmFyaW91cyBpbnB1dCBnZW5lIHNldHMgdG8gZGVmaW5lIHRoZSBleGNsdXNpb24gZ2VuZXMuCkkgYW0gZ29pbmcgdG8gcmVwZWF0IHRoaXMsIGJ1dCBsZWF2ZSB0aGUgc3RhcnRpbmcgZGF0YSBzdHJ1Y3R1cmUKaW50YWN0LgoKSW4gdGhpcyBmaXJzdCBpdGVyYXRpb24sIEkgd2lsbCBkbyB0aGF0IGJ5IGNyZWF0aW5nIGEgc2ltcGxpZmllZCBtb2RlbApvZiB0aGUgZGF0YSB3aGljaCBjb21iaW5lcyB0aGUgdGltZS9nZW5vdHlwZS9sb2NhdGlvbiBhbmQgdXNpbmcgc3ZhLgpJbiBteSBuZXh0IGl0ZXJhdGlvbiBJIHdpbGwgdXNlIGEgZnVsbCBzdGF0aXN0aWNhbCBtb2RlbCBjb250YWluaW5nCmVhY2ggb2YgdGhvc2UgZmFjdG9ycyAoYW5kIHByb2JhYmx5IGFsc28gdXNpbmcgc3ZhKS4KCk5vdGU6IG15IGNvbG9yIGNob2ljZXMgYXJlIGtpbmQgb2YgZ2FyYmFnZS4KCkluIGFkZGl0aW9uLCB0aGUgZXhjbHVzaW9uIGRhdGFzZXQgaXMgdGhlIHNhbWUgYXMgdGhlIGFuYWx5c2lzCmRhdGFzZXQsIGl0IGlzIHJlYWxseSBvbmx5IHRoZSBjb250cmFzdHMgd2hpY2ggd2lsbCBiZSBkaWZmZXJlbnQuCgpgYGB7cn0KdjNfcGFpcndpc2VfaW5wdXQgPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhtbTM4X2hpc2F0X3YzLCBmYWN0ID0gInRpbWVfZ2Vub19sb2MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycyA9IGNvbG9yX2Nob2ljZXNbWyJhbGwiXV0pCmBgYAoKIyMgU2V0IHVwIHRoZSBjb250cmFzdHMKCkluIHRoZSBmb2xsb3dpbmcgZmV3IGJsb2NrcyBJIHdpbGwgc2V0IHVwIHRoZSB2YXJpb3VzIGNvbXBhcmlzb25zIG9mCmludGVyZXN0LiAgU3RhcnRpbmcgd2l0aCB0aGUgc2V0IG9mIGdlbmVzIHRvIGV4Y2x1ZGUgYmVjYXVzZSB0aGV5IHdlcmUKb2JzZXJ2ZWQgdG8gYmluZCBub24tc3BlY2lmaWNhbGx5IGluIHRoZSB3dCBzYW1wbGVzLgoKIyMjIEluY2x1c2lvbiBjb250cmFzdHMKCkluIGVhY2ggZXhjbHVzaW9uIEkgd2lsbCBoYXZlIHRoZSBjb250cmFzdCBmaXJzdCBmb2xsb3dlZCBieSB0aGUgcGFpcgpvZiBjb250cmFzdHMgd2hpY2ggd2lsbCBiZSB1c2VkIHRvIGRlZmluZSB0aGUgZ2VuZSBzZXQgdG8gZXhjbHVkZS4KCiogcDE1X2hldF9kbGduL3AwOF9oZXRfZGxnbjogcDE1X3d0X2RsZ24vcDE1X2hldF9kbGduLAogIHAwOF93dF9kbGduL3AwOF9oZXRfZGxnbjsgcmVtb3ZlIHRoZSBnZW5lcyBpbmNyZWFzZWQgaW4gd3QuCiogcDE1X2tvX3Njbi9wMDhfa29fc2NuOiBwMTVfd3Rfc2NuL3AxNV9rb19zY24sIHAwOF93dF9zY24vcDE1X2tvX3NjbgoqIHAxNV9oZXRfcmV0aW5hL3AwOF9oZXRfcmV0aW5hOiBJIHRoaW5rIHlvdSBnZXQgaXQsIHd0L2hldCBmb3IgYm90aAogIHAxNSByZXRpbmFzIGFuZCBwMDggcmV0aW5hcy4uLgoKUHV0IHNsaWdodGx5IGRpZmZlcmVudGx5LCBmb3IgZXZlcnkgdGVybSBvZiBpbnRlcmVzdCBJIHdpbGwgY3JlYXRlIGEKY29udHJhc3Qgd2l0aCB0aGUgd3QgYXMgbnVtZXJhdG9yIGFuZCB0aGUgZGVzaXJlZCB0ZXJtIGFzIGRlbm9taW5hdG9yLAp0aGVuIHB1bGwgb3V0IHRoZSBnZW5lcyBpbmNyZWFzZWQgaW4gd3QuCgpgYGB7cn0KaW5jbHVzaW9ucyA8LSBsaXN0KAogICMjIEkgbGlrZSBhbHBoYWJldGl6aW5nIHRoaW5ncywgc3RhcnQgd2l0aCBkbGduCiAgInAxNV9oZXRfZGxnbiIgPSBjKCJwMTVfaGV0X2RsZ24iLCAicDE1X3d0X2RsZ24iKSwKICAicDA4X2hldF9kbGduIiA9IGMoInAwOF9oZXRfZGxnbiIsICJwMDhfd3RfZGxnbiIpLAogICJwMTVfa29fZGxnbiIgPSBjKCJwMTVfa29fZGxnbiIsICJwMTVfd3RfZGxnbiIpLAogICJwMDhfa29fZGxnbiIgPSBjKCJwMDhfa29fZGxnbiIsICJwMDhfd3RfZGxnbiIpLAogICMjIFRoZW4gcmV0aW5hcwogICJwMTVfaGV0X3JldGluYSIgPSBjKCJwMTVfaGV0X3JldGluYSIsICJwMTVfd3RfcmV0aW5hIiksCiAgInAwOF9oZXRfcmV0aW5hIiA9IGMoInAwOF9oZXRfcmV0aW5hIiwgInAwOF93dF9yZXRpbmEiKSwKICAicDE1X2tvX3JldGluYSIgPSBjKCJwMTVfa29fcmV0aW5hIiwgInAxNV93dF9yZXRpbmEiKSwKICAicDA4X2tvX3JldGluYSIgPSBjKCJwMDhfa29fcmV0aW5hIiwgInAwOF93dF9yZXRpbmEiKSwKICAjIyBUaGVuIHNjbgogICJwMTVfaGV0X3NjbiIgPSBjKCJwMTVfaGV0X3NjbiIsICJwMTVfd3Rfc2NuIiksCiAgInAwOF9oZXRfc2NuIiA9IGMoInAwOF9oZXRfc2NuIiwgInAwOF93dF9zY24iKSwKICAicDE1X2tvX3NjbiIgPSBjKCJwMTVfa29fc2NuIiwgInAxNV93dF9zY24iKSwKICAicDA4X2tvX3NjbiIgPSBjKCJwMDhfa29fc2NuIiwgInAwOF93dF9zY24iKSkKYGBgCgojIyMgVGltZSBjb250cmFzdHMKCkZvciBlYWNoIGxvY2F0aW9uL2dlbm90eXBlIG9mIGludGVyZXN0LCBsZXQgdXMgY29tcGFyZSBwMTUvcDA4CgpgYGB7cn0KdGltZV9rZWVwZXJzIDwtIGxpc3QoCiAgIyMgRExHTgogICJ0X2hldF9kbGduIiA9IGMoInAxNV9oZXRfZGxnbiIsICJwMDhfaGV0X2RsZ24iKSwKICAidF9rb19kbGduIiA9IGMoInAxNV9rb19kbGduIiwgInAwOF9rb19kbGduIiksCiAgIyMgUmV0aW5hCiAgInRfaGV0X3JldGluYSIgPSBjKCJwMTVfaGV0X3JldGluYSIsICJwMDhfaGV0X3JldGluYSIpLAogICJ0X2tvX3JldGluYSIgPSBjKCJwMTVfa29fcmV0aW5hIiwgInAwOF9rb19yZXRpbmEiKSwKICAjIyBTQ04KICAidF9oZXRfc2NuIiA9IGMoInAxNV9oZXRfc2NuIiwgInAwOF9oZXRfc2NuIiksCiAgInRfa29fc2NuIiA9IGMoInAxNV9rb19zY24iLCAicDA4X2tvX3NjbiIpKQpgYGAKCiMjIyBMb2NhdGlvbiBjb250cmFzdHMKCkNvbXBhcmUgbG9jYXRpb25zIGFuZCBrZWVwIHRpbWUvZ2Vub3R5cGUgY29uc2lzdGVudC4gIEkgd2lsbCB1c2UgdGhlCmxvY2F0aW9uIGluaXRpYWxzIHRvIGRlZmluZSBudW1lcmF0b3IvZGVub21pbmF0b3IuCgpgYGB7cn0KbG9jYXRpb25fa2VlcGVycyA8LSBsaXN0KAogICMjIGRsZ24vcmV0aW5hCiAgImRyX3AwOF9oZXQiID0gYygicDA4X2hldF9kbGduIiwgInAwOF9oZXRfcmV0aW5hIiksCiAgImRyX3AxNV9oZXQiID0gYygicDE1X2hldF9kbGduIiwgInAxNV9oZXRfcmV0aW5hIiksCiAgImRyX3AwOF9rbyIgPSBjKCJwMDhfa29fZGxnbiIsICJwMDhfa29fcmV0aW5hIiksCiAgImRyX3AxNV9rbyIgPSBjKCJwMTVfa29fZGxnbiIsICJwMTVfa29fcmV0aW5hIiksCiAgIyMgc2NuL3JldGluYQogICJzcl9wMDhfaGV0IiA9IGMoInAwOF9oZXRfc2NuIiwgInAwOF9oZXRfcmV0aW5hIiksCiAgInNyX3AxNV9oZXQiID0gYygicDE1X2hldF9zY24iLCAicDE1X2hldF9yZXRpbmEiKSwKICAic3JfcDA4X2tvIiA9IGMoInAwOF9rb19zY24iLCAicDA4X2tvX3JldGluYSIpLAogICJzcl9wMTVfa28iID0gYygicDE1X2tvX3NjbiIsICJwMTVfa29fcmV0aW5hIiksCiAgIyMgZGxnbi9zY24KICAiZHNfcDA4X2hldCIgPSBjKCJwMDhfaGV0X2RsZ24iLCAicDA4X2hldF9zY24iKSwKICAiZHNfcDE1X2hldCIgPSBjKCJwMTVfaGV0X2RsZ24iLCAicDE1X2hldF9zY24iKSwKICAiZHNfcDA4X2tvIiA9IGMoInAwOF9rb19kbGduIiwgInAwOF9rb19zY24iKSwKICAiZHNfcDE1X2tvIiA9IGMoInAxNV9rb19kbGduIiwgInAxNV9rb19zY24iKSkKYGBgCgojIyMgR2Vub3R5cGUgY29udHJhc3RzCgpDb21wYXJlIGtvL2hldCB3aGlsZSBrZWVwaW5nIHRpbWUvbG9jYXRpb24gY29uc3RhbnQuICBTaW1pbGFybHksIHVzZQp0aGUgaW5pdGlhbHMgdG8gZGVub3RlIG51bWVyYXRvci9kZW5vbWluYXRvciwgd2hpY2ggd2lsbCBhbHdheXMgYmUga2guCgpgYGB7cn0KZ2Vub3R5cGVfa2VlcGVycyA8LSBsaXN0KAogICMjIERMR04KICAia2hfcDA4X2RsZ24iID0gYygicDA4X2tvX2RsZ24iLCAicDA4X2hldF9kbGduIiksCiAgImtoX3AxNV9kbGduIiA9IGMoInAxNV9rb19kbGduIiwgInAxNV9oZXRfZGxnbiIpLAogICMjIFJldGluYQogICJraF9wMDhfcmV0aW5hIiA9IGMoInAwOF9rb19yZXRpbmEiLCAicDA4X2hldF9yZXRpbmEiKSwKICAia2hfcDE1X3JldGluYSIgPSBjKCJwMTVfa29fcmV0aW5hIiwgInAxNV9oZXRfcmV0aW5hIiksCiAgIyMgU0NOCiAgImtoX3AwOF9zY24iID0gYygicDA4X2tvX3NjbiIsICJwMDhfaGV0X3NjbiIpLAogICJraF9wMTVfc2NuIiA9IGMoInAxNV9rb19zY24iLCAicDE1X2hldF9zY24iKSkKYGBgCgojIyBQZXJmb3JtIHRoZSBleGNsdXNpb24gY29tcGFyaXNvbgoKTXkgYWxsX3BhaXJ3aXNlKCkgZnVuY3Rpb24gbm93IGhhcyBhIHBhcmFtZXRlciB3aGljaCBhbGxvd3MgbWUgdG8KY2hvb3NlIHdoaWNoIGNvbnRyYXN0cyB0byBwZXJmb3JtIGluc3RlYWQgb2YgbGl0ZXJhbGx5IGRvaW5nIGV2ZXJ5CnBvc3NpYmxlIGNvbXBhcmlzb24uICBUaGF0IGlzIHdlbGwgc3VpdGVkIGZvciB0aGVzZSBvcGVyYXRpb25zOgoKSW4gYSBjb250YWluZXIsIHRoZSBmb2xsb3dpbmcgYXBwZWFycyB0byBmYWlsIHdpdGg6CgoiZXJyb3IgY29kZSAxIGZyb20gTGFwYWNrIHJvdXRpbmUgJ2RnZXNkZCciCgpSdW5uaW5nIGl0IG1hbnVhbGx5IG91dHNpZGUgdGhlIGNvbnRhaW5lciByZXN1bHRzIGluIGl0IHdvcmtpbmcKd2l0aG91dCBlcnJvci4gIEkgYXNzdW1lIHRoZXJlZm9yZSB0aGF0IHRoZSBwcm9ibGVtIGxpZXMgaW4gdGhlCmNvbXBpbGF0aW9uIGZsYWdzIG9mIExBUEFDSyBpbiB0aGUgY29udGFpbmVyLgoKYGBge3J9CmxmY19jdXRvZmYgPC0gMC4xCmFkanBfY3V0b2ZmIDwtIDAuMQppbmNsdXNpb25fZGUgPC0gYWxsX3BhaXJ3aXNlKHYzX3BhaXJ3aXNlX2lucHV0LCBmaWx0ZXIgPSAic2ltcGxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZWVwZXJzID0gaW5jbHVzaW9ucywgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIikKaW5jbHVzaW9uX2RlCmluY2x1c2lvbl90YWJsZXMgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgaW5jbHVzaW9uX2RlLCBrZWVwZXJzID0gaW5jbHVzaW9ucywgbGFiZWxfY29sdW1uID0gbGFiZWxfY29sdW1uLAogIGV4Y2VsID0gZ2x1ZSgid3RfY29tcGFyaXNvbnMvaW5jbHVzaW9uX3RhYmxlcy12e3Zlcn0ueGxzeCIpKQppbmNsdXNpb25fdGFibGVzCmluY2x1c2lvbl9zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICBpbmNsdXNpb25fdGFibGVzLCBsZmMgPSBsZmNfY3V0b2ZmLCBwID0gYWRqcF9jdXRvZmYsCiAgYWNjb3JkaW5nX3RvID0gImRlc2VxIiwKICBleGNlbCA9IGdsdWUoInd0X2NvbXBhcmlzb25zL2luY2x1c2lvbl9zaWctdnt2ZXJ9Lnhsc3giKSkKaW5jbHVzaW9uX3NpZwoKaW5jbHVzaW9uX3Vwc2V0cyA8LSB1cHNldHJfc2lnKGluY2x1c2lvbl9zaWcpCmluY2x1c2lvbl9pbnRlcnNlY3RzIDwtIHdyaXRlX3Vwc2V0X2dyb3VwcyhpbmNsdXNpb25fdXBzZXRzLCBleGNlbCA9ICJleGNlbC9pbmNsdXNpb25fZ2VuZV9ncm91cHMueGxzeCIpCmBgYAoKIyMjIENoZWNrIHZzIFRoZXJlc2EncyBmaWx0ZXIKClVwIGFib3ZlIFRoZXJlc2EgcGVyZm9ybWVkIGEgMC4yNSBsb2cyRkMgYW5kIDAuMDUgYWRqcCBmaWx0ZXIgd2hpY2gKcHJvdmlkZWQgYSBzZXQgb2YgMiw2NDAgZ2VuZXMgb2JzZXJ2ZWQgaGlnaGVyIGluIHRoZSBwMDggaGV0IHJldGluYXMKdnMuIHd0IHJldGluYXMuICBJIHNob3VsZCBzZWUgdGhhdCBpbiB0aGlzIGluY2x1c2lvbl9zaWcgZGF0YSBzdHJ1Y3R1cmUuCgpUaGVyZSBpcyBhbiBpbXBvcnRhbnQgY2F2ZWF0IHRob3VnaDogaW4gVGhlcmVzYSdzIGZpbHRlciBhYm92ZSwgc2hlCmRpZCBhIERFIG9mIF9vbmx5XyB0aGUgcmV0aW5hIHNhbXBsZXMgYnV0IEkgZGlkIGFsbCBzYW1wbGVzLiAgSQpleHBlY3RlZCB0aGF0IHRoaXMgd291bGQgcmVzdWx0IGluIGJhc2ljYWxseSB0aGUgc2FtZSByZXN1bHQgKEkKYWN0dWFsbHkgYXNzdW1lZCBJIHdvdWxkIGdldCBhIGZldyBtb3JlIGdlbmVzKSwgYnV0IGluc3RlYWQgaXQgYXBwZWFycwp0byBoYXZlIHJldHJpZXZlZCBhIHNpZ25pZmljYW50bHkgc21hbGxlciBudW1iZXIgb2YgZ2VuZXMgKGFib3V0IDEvMiwKaGFwcGlseSB0aGV5IHByZXR0eSBtdWNoIGFsbCBhcHBlYXIgaW4gdGhlIHByZXZpb3VzIGZpbHRlcikuICBBcyBhCnJlc3VsdCwgSSBhbSBnb2luZyB0byB0cnkgcmVsYXhpbmcgbXkgY29uc3RyYWludHMgc2xpZ2h0bHkgdG8gc2VlIGlmIEkKY2FuIHJlY2FwaXR1bGF0ZSBoZXIgZmlsdGVyICh3aGljaCB3b3VsZCBtYXRjaCBUaGVyZXNhJ3MgbGF0ZXIgZmlsdGVyLAp0aG91Z2ggSSBndWVzcyB0aGF0IGluIHR1cm4gd2lsbCBsZWFkIHRvIGEgc21hbGxlciBzZXQgb2YgZ2VuZXMKY29tcGFyZWQgdG8gaGVyIGxhdGVyLCByZWxheGVkIDAuMSBmaWx0ZXIpLgoKYGBge3J9CmNvbXBhcmlzb24gPC0gaW5jbHVzaW9uX3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1sicDA4X2hldF9yZXRpbmEiXV0KY29tcCA8LSBsaXN0KAogICJ0YWEiID0gdGFhX2tlZXBlcnMsCiAgIm5ldyIgPSByb3duYW1lcyhjb21wYXJpc29uKSkKdGVzdF9jb21wYXJpc29uIDwtIFZlbm5lcmFibGU6OlZlbm4oY29tcCkKVmVubmVyYWJsZTo6cGxvdCh0ZXN0X2NvbXBhcmlzb24pCmBgYAoKSSB3YW50IHRvIGhhdmUgYSBsaXR0bGUgZnVuY3Rpb24gd2hpY2gsIGdpdmVuIGEgY29udHJhc3Qgb2YgaW50ZXJlc3QsCndpbGwgZXh0cmFjdCB0aGUgZ2VuZSBzZXRzIHdoaWNoIHNob3VsZCBiZSBpbmNsdWRlZC9leGNsdWRlZCBnaXZlbiB0aGUKYWJvdmUuCgpgYGB7cn0Kd3JpdGVfYWxsX2NwIDwtIGZ1bmN0aW9uKGFsbF9jcCkgewogIGFsbF93cml0dGVuIDwtIGxpc3QoKQogIGZvciAoZyBpbiBzZXFfbGVuKGxlbmd0aChhbGxfY3ApKSkgewogICAgbmFtZSA8LSBuYW1lcyhhbGxfY3ApW2ddCiAgICBkYXR1bSA8LSBhbGxfY3BbW25hbWVdXQogICAgZmlsZW5hbWUgPC0gZ2x1ZSgiY3Byb2ZpbGVyL3t2ZXJ9L3tuYW1lfV9jcHJvZmlsZXItdnt2ZXJ9Lnhsc3giKQogICAgd3JpdHRlbiA8LSBzbSh3cml0ZV9jcF9kYXRhKGRhdHVtLCBleGNlbCA9IGZpbGVuYW1lKSkKICAgIGFsbF93cml0dGVuW1tnXV0gPC0gd3JpdHRlbgogIH0KICByZXR1cm4oYWxsX3dyaXR0ZW4pCn0KCndyaXRlX2FsbF9ncCA8LSBmdW5jdGlvbihhbGxfZ3ApIHsKICBhbGxfd3JpdHRlbiA8LSBsaXN0KCkKICBmb3IgKGcgaW4gc2VxX2xlbihsZW5ndGgoYWxsX2dwKSkpIHsKICAgIG5hbWUgPC0gbmFtZXMoYWxsX2dwKVtnXQogICAgZGF0dW0gPC0gYWxsX2dwW1tuYW1lXV0KICAgIGZpbGVuYW1lIDwtIGdsdWUoImdwcm9maWxlci97dmVyfS97bmFtZX1fZ3Byb2ZpbGVyLXZ7dmVyfS54bHN4IikKICAgIHdyaXR0ZW4gPC0gc20od3JpdGVfZ3Byb2ZpbGVyX2RhdGEoZGF0dW0sIGV4Y2VsID0gZmlsZW5hbWUpKQogICAgYWxsX3dyaXR0ZW5bW2ddXSA8LSB3cml0dGVuCiAgfQogIHJldHVybihhbGxfd3JpdHRlbikKfQoKZXh0cmFjdF9pbmNsdXNpb25zIDwtIGZ1bmN0aW9uKGluY2x1c2lvbl9zaWcsIGluY2x1c2lvbl90YWJsZXMsIGluY2x1c2lvbnMsIGtlZXBlcnMsIGFsbF9nZW5lcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjY29yZGluZ190byA9ICJkZXNlcSIsIHdoaWNoID0gInVwcyIpIHsKICByZXRsaXN0IDwtIGxpc3QoKQogIHRhYmxlX25hbWVzIDwtIG5hbWVzKGluY2x1c2lvbl9zaWdbW2FjY29yZGluZ190b11dW1t3aGljaF1dKQogIGZvciAoY19udW0gaW4gc2VxX2Fsb25nKGtlZXBlcnMpKSB7CiAgICBjb250cmFzdCA8LSBuYW1lcyhrZWVwZXJzKVtjX251bV0KICAgIG51bWVyYXRvcl9uYW1lIDwtIGtlZXBlcnNbW2NfbnVtXV1bMV0KICAgIGRlbm9taW5hdG9yX25hbWUgPC0ga2VlcGVyc1tbY19udW1dXVsyXQogICAgIyMgSW4gbXkgbmV3IGJyYW5jaCBJIGNsZWFuZWQgdXAgdGhlIHNhbml0aXplciBmdW5jdGlvbiBmb3IgY29udHJhc3RzIHNvIHRoaXMgaXMgbm90IG5lZWRlZC4KICAgICMjIFRoZSBmb2xsb3dpbmcgdHdvIGxpbmVzIGFyZSBubyBsb25nZXIgbmVlZGVkIGJlY2F1c2Ugb2YgdGhlIGNsZWFudXBzIEkgcGVyZm9ybWVkLgogICAgIyNudW1lcmF0b3JfbmFtZSA8LSBnc3ViKHggPSBudW1lcmF0b3JfbmFtZSwgcGF0dGVybiA9ICIoaGV0fGtvfHd0KSIsIHJlcGxhY2VtZW50ID0gIl9cXDFfIikKICAgICMjZGVub21pbmF0b3JfbmFtZSA8LSBnc3ViKHggPSBkZW5vbWluYXRvcl9uYW1lLCBwYXR0ZXJuID0gIihoZXR8a298d3QpIiwgcmVwbGFjZW1lbnQgPSAiX1xcMV8iKQogICAgbnVtZXJhdG9yX3RhYmxlIDwtIGluY2x1c2lvbl9zaWdbW2FjY29yZGluZ190b11dW1t3aGljaF1dW1tudW1lcmF0b3JfbmFtZV1dCiAgICBudW1lcmF0b3JfZ2VuZXMgPC0gcm93bmFtZXMobnVtZXJhdG9yX3RhYmxlKQogICAgZGVub21pbmF0b3JfdGFibGUgPC0gaW5jbHVzaW9uX3NpZ1tbYWNjb3JkaW5nX3RvXV1bW3doaWNoXV1bW2Rlbm9taW5hdG9yX25hbWVdXQogICAgZGVub21pbmF0b3JfZ2VuZXMgPC0gcm93bmFtZXMoZGVub21pbmF0b3JfdGFibGUpCiAgICBkZl9jb2x1bW5zIDwtIHBhc3RlMCgiZGVzZXFfIiwgYygibG9nZmMiLCAiYWRqcCIsICJkZW4iKSkKICAgIGluY2x1ZGVkX251bSA8LSBpbmNsdXNpb25fdGFibGVzW1siZGF0YSJdXVtbbnVtZXJhdG9yX25hbWVdXVssIGRmX2NvbHVtbnNdCiAgICBjb2xuYW1lcyhpbmNsdWRlZF9udW0pIDwtIGMoIm51bWVyYXRvcl92c193dF9sb2dmYyIsICJudW1lcmF0b3JfdnNfd3RfYWRqcCIsICJudW1fd3RfbWVhbl9leHBycyIpCiAgICBpbmNsdWRlZF9kZW4gPC0gaW5jbHVzaW9uX3RhYmxlc1tbImRhdGEiXV1bW2Rlbm9taW5hdG9yX25hbWVdXVssIGRmX2NvbHVtbnNdCiAgICBjb2xuYW1lcyhpbmNsdWRlZF9kZW4pIDwtIGMoImRlbm9taW5hdG9yX3ZzX3d0X2xvZ2ZjIiwgImRlbm9taW5hdG9yX3ZzX3d0X2FkanAiLCAiZGVuX3d0X21lYW5fZXhwcnMiKQogICAgaW5jbHVkZWRfZGYgPC0gbWVyZ2UoaW5jbHVkZWRfbnVtLCBpbmNsdWRlZF9kZW4sIGJ5ID0gInJvdy5uYW1lcyIpCiAgICByb3duYW1lcyhpbmNsdWRlZF9kZikgPC0gaW5jbHVkZWRfZGZbWyJSb3cubmFtZXMiXV0KICAgIGluY2x1ZGVkX2RmW1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKICAgIGluY2x1ZGVfZ2VuZXMgPC0gdW5pcXVlKGMobnVtZXJhdG9yX2dlbmVzLCBkZW5vbWluYXRvcl9nZW5lcykpCiAgICBtZXNzYWdlKCJUaGUgc2V0IG9mIHVuaXF1ZSBnZW5lcyBoaWdoZXIgaW4gIiwgbnVtZXJhdG9yX25hbWUsCiAgICAgICAgICAgICIgdnMuIHd0IGlzICIsIGxlbmd0aChudW1lcmF0b3JfZ2VuZXMpLCAiLiIpCiAgICBtZXNzYWdlKCJUaGUgc2V0IG9mIHVuaXF1ZSBnZW5lcyBoaWdoZXIgaW4gIiwgZGVub21pbmF0b3JfbmFtZSwKICAgICAgICAgICAgIiB2cy4gd3QgaXMgIiwgbGVuZ3RoKGRlbm9taW5hdG9yX2dlbmVzKSwgIi4iKQogICAgbWVzc2FnZSgiVGhlIHVuaXF1ZSB1bmlvbiBvZiB0aGVtIGlzICIsIGxlbmd0aChpbmNsdWRlX2dlbmVzKSwgIiBnZW5lcy4iKQogICAgaW5jbHVkZV9uYW1lIDwtIHBhc3RlMCgiaW5jXyIsIGNvbnRyYXN0KQogICAgaW5jbHVkZV9pZHggPC0gYWxsX2dlbmVzICVpbiUgaW5jbHVkZV9nZW5lcwogICAgaW5jbHVkZV9nZW5lcyA8LSBhbGxfZ2VuZXNbaW5jbHVkZV9pZHhdCiAgICBkZl9uYW1lIDwtIHBhc3RlMCgiZGZfIiwgY29udHJhc3QpCiAgICByZXRsaXN0W1tkZl9uYW1lXV0gPC0gaW5jbHVkZWRfZGYKICAgIHdyaXR0ZW5faW5jbHVzaW9uIDwtIHdyaXRlX3hsc3goZGF0YSA9IGluY2x1ZGVkX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleGNlbCA9IGdsdWUoImluY2x1ZGVkX2dlbmVzL3tpbmNsdWRlX25hbWV9LXZ7dmVyfS54bHN4IikpCiAgICByZXRsaXN0W1tpbmNsdWRlX25hbWVdXSA8LSBpbmNsdWRlX2dlbmVzCiAgICByZXRsaXN0W1tjb250cmFzdF1dIDwtIGluY2x1ZGVfZ2VuZXMKICB9CiAgcmV0dXJuKHJldGxpc3QpCn0KYGBgCgojIyMgRXh0cmFjdCBnZW5lcyBpbmNsdWRlZCBmb3IgZWFjaCBzZXQgb2YgY29udHJhc3RzCgpOb3csIHVzaW5nIHRoYXQgZnVuY3Rpb24sIHB1bGwgb3V0IHRoZSBnZW5lIElEcyBvZiBnZW5lcyB3ZSBkbyBub3QKdHJ1c3QgYmVjYXVzZSB0aGV5IHdlcmUgdG9vIGhpZ2ggaW4gd3QgZm9yIGV2ZXJ5IGNvbnRyYXN0IHdlIGFyZQpsaWtlbHkgdG8gcGVyZm9ybS4KCmBgYHtyfQphbGxfZ2VuZXMgPC0gcm93bmFtZXMoZXhwcnModjNfcGFpcndpc2VfaW5wdXQpKQp0aW1lX2luY2x1c2lvbnMgPC0gZXh0cmFjdF9pbmNsdXNpb25zKGluY2x1c2lvbl9zaWcsIGluY2x1c2lvbl90YWJsZXMsIGluY2x1c2lvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZV9rZWVwZXJzLCBhbGxfZ2VuZXMpCgpsb2NhdGlvbl9pbmNsdXNpb25zIDwtIGV4dHJhY3RfaW5jbHVzaW9ucyhpbmNsdXNpb25fc2lnLCBpbmNsdXNpb25fdGFibGVzLCBpbmNsdXNpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhdGlvbl9rZWVwZXJzLCBhbGxfZ2VuZXMpCgpnZW5vdHlwZV9pbmNsdXNpb25zIDwtIGV4dHJhY3RfaW5jbHVzaW9ucyhpbmNsdXNpb25fc2lnLCBpbmNsdXNpb25fdGFibGVzLCBpbmNsdXNpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5vdHlwZV9rZWVwZXJzLCBhbGxfZ2VuZXMpCmBgYAoKIyMgUGVyZm9ybSB0aGUgREUgYW5hbHlzZXMgYW5kIGV4Y2x1ZGUgdGhlIHRhcmdldCBnZW5lcwoKYGBge3J9Cmdlbm90eXBlX2RlIDwtIGFsbF9wYWlyd2lzZSh2M19wYWlyd2lzZV9pbnB1dCwgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBlcnMgPSBnZW5vdHlwZV9rZWVwZXJzLCBtb2RlbF9iYXRjaCA9ICJzdmFzZXEiKQpnZW5vdHlwZV9kZQoKbG9jYXRpb25fZGUgPC0gYWxsX3BhaXJ3aXNlKHYzX3BhaXJ3aXNlX2lucHV0LCBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAga2VlcGVycyA9IGxvY2F0aW9uX2tlZXBlcnMsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIpCmxvY2F0aW9uX2RlCgp0aW1lX2RlIDwtIGFsbF9wYWlyd2lzZSh2M19wYWlyd2lzZV9pbnB1dCwgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAga2VlcGVycyA9IHRpbWVfa2VlcGVycywgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIikKdGltZV9kZQpgYGAKCiMjIEV4dHJhY3QgdGhlIHJlbGV2YW50IHRhYmxlcyBhbmQgaW5jbHVkZSBnZW5lcyBsb3dlciBpbiB3dAoKIyMjIEdlbm90eXBlIGNvbnRyYXN0cwoKSSB3aWxsIHN0YXJ0IHdpdGggdGhlIHRhYmxlcyBhbmQgbm8gaW5jbHVzaW9ucyBzbyBJIGNhbiBjaGVjayBteSB3b3JrLgoKSW4gdGhpcyBmaXJzdCBibG9jayBJIHdpbGwgZXhwbGFpbiBhIGxpdHRsZSBtb3JlIHRob3JvdWdobHkgd2hhdCBpcwpnb2luZyBvbjoKCjEuICBEdW1wIHRoZSBmdWxsIHRhYmxlIG9mIHRoZSBjb250cmFzdHMgSSBkZWZpbmVkIGFib3ZlIGNvbXBhcmluZyB0aGUKICAgIDMgZ2Vub3R5cGVzIGFjcm9zcyB0aW1lL2xvY2F0aW9uLgoyLiAgSXRlcmF0ZSBvdmVyIGVhY2ggb2YgdGhvc2UgY29udHJhc3RzIGFuZCBkbyB0aGUgZm9sbG93aW5nOgogICAgYS4gIEV4dHJhY3QgdGhlIG5hbWUgb2YgdGhlIGNvbnRyYXN0LCAna2hfcDA4X2RsZ24nIGZvciBleGFtcGxlCiAgICBiLiAgWWFuayBvdXQgdGhhdCBzcGVjaWZpYyBlbnRyeSBmcm9tIHRoZSBrZWVwZXIgbGlzdCBhbmQgaXRzIG5hbWUKICAgIGMuICBZYW5rIG91dCB0aGUgY29ycmVzcG9uZGluZyBzZXQgb2YgZ2VuZXMgdG8gaW5jbHVkZSBmcm9tIHRoZQogICAgICAgIGluY2x1c2lvbnMgZGF0YSBzdHJ1Y3R1cmUuCiAgICBkLiAgQ3JlYXRlIGEgZmlsZW5hbWUgZ2l2ZW4gdGhlIG5hbWUgaW4gKGEpIGFib3ZlIGFuZCB0aGUgbG9nRkMKICAgICAgICBjdXRvZmYgY2hvc2VuIGZvciB0aGUgaW5jbHVzaW9ucyAoSSBhbSBhc3N1bWluZyB3ZSBtYXkgY2hhbmdlCiAgICAgICAgdGhpcykKICAgIGUuICBHaXZlbiAoYiksIChjKSwgYW5kIChkKSwgZXh0cmFjdCB0aGUgY29ycmVzcG9uZGluZyB0YWJsZSBmcm9tCiAgICAgICAgdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIGFuZCBpbmNsdWRlIHRoZSBhcHByb3ByaWF0ZQogICAgICAgIGdlbmVzLgoKYGBge3J9Cmdlbm90eXBlX3RhYmxlc19mdWxsIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIGdlbm90eXBlX2RlLCBrZWVwZXJzID0gZ2Vub3R5cGVfa2VlcGVycywgbGFiZWxfY29sdW1uID0gbGFiZWxfY29sdW1uLAogIGZhbmN5ID0gVFJVRSwKICBleGNlbCA9IGdsdWUoImZ1bGxfY29udHJhc3RzL2dlbm90eXBlX2Z1bGxfdGFibGVzLXZ7dmVyfS54bHN4IikpCmdlbm90eXBlX3RhYmxlc19mdWxsCmdlbm90eXBlX3NpZ19mdWxsIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgZ2Vub3R5cGVfdGFibGVzX2Z1bGwsIGFjY29yZGluZ190byA9ICJkZXNlcSIsCiAgZXhjZWwgPSBnbHVlKCJmdWxsX2NvbnRyYXN0cy9nZW5vdHlwZV9mdWxsX3NpZy12e3Zlcn0ueGxzeCIpKQpnZW5vdHlwZV9zaWdfZnVsbAoKZ2Vub3R5cGVfZnVsbF9ncCA8LSBhbGxfZ3Byb2ZpbGVyKGdlbm90eXBlX3NpZ19mdWxsLCBzcGVjaWVzID0gIm1tdXNjdWx1cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleGNlbCA9ICJleGNlbC9hbGxfZ3Byb2ZpbGVyX2dlbm90eXBlX2Z1bGwueGxzeCIpCmdlbm90eXBlX2Z1bGxfY3AgPC0gYWxsX2Nwcm9maWxlcihnZW5vdHlwZV9zaWdfZnVsbCwgZ2Vub3R5cGVfdGFibGVzX2Z1bGwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmdkYiA9ICJvcmcuTW0uZWcuZGIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjZWwgPSAiZXhjZWwvYWxsX2Nwcm9maWxlcl9nZW5vdHlwZV9mdWxsLnhsc3giKQpnZW5vdHlwZV9mdWxsX3Vwc2V0IDwtIHVwc2V0cl9zaWcoZ2Vub3R5cGVfc2lnX2Z1bGwpCmdlbm90eXBlX2Z1bGxfaW50ZXJzZWN0cyA8LSB3cml0ZV91cHNldF9ncm91cHMoZ2Vub3R5cGVfZnVsbF91cHNldCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleGNlbCA9ICJleGNlbC9nZW5vdHlwZV9mdWxsX2dlbmVfZ3JvdXBzLnhsc3giKQpnZW5vdHlwZV90YWJsZXMgPC0gbGlzdCgpCmdlbm90eXBlX3NpZyA8LSBsaXN0KCkKZ2Vub3R5cGVfZ3AgPC0gbGlzdCgpCmdlbm90eXBlX2NwIDwtIGxpc3QoKQpmb3IgKGsgaW4gc2VxX2Fsb25nKGdlbm90eXBlX2tlZXBlcnMpKSB7CiAgbmFtZSA8LSBuYW1lcyhnZW5vdHlwZV9rZWVwZXJzKVtrXQogIG1lc3NhZ2UoIkV4YW1pbmluZyAiLCBuYW1lKQogIGtlZXBlciA8LSBnZW5vdHlwZV9rZWVwZXJzW25hbWVdCiAgaW5jbHVkZV9uYW1lIDwtIHBhc3RlMCgiaW5jXyIsIG5hbWUpCiAgaW5jbHVkZV9kZl9uYW1lIDwtIHBhc3RlMCgiZGZfIiwgbmFtZSkKICBpbmNsdWRlX2RmIDwtIGdlbm90eXBlX2luY2x1c2lvbnNbW2luY2x1ZGVfZGZfbmFtZV1dCiAgaW5jbHVkZXMgPC0gZ2Vub3R5cGVfaW5jbHVzaW9uc1tbaW5jbHVkZV9uYW1lXV0KICBzdW1tYXJ5KHJvd25hbWVzKGdlbm90eXBlX3NpZ19mdWxsW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXSkgJWluJSBpbmNsdWRlcykKICBpbmNsdWRlX2ZpbGVuYW1lIDwtIGdsdWUoImdlbm90eXBlX2NvbnRyYXN0cy9nZW5vdHlwZV97bmFtZX1faW5jbHVkaW5nX3d0X3tsZmNfY3V0b2ZmfV9kZWNyZWFzZWRfdGFibGUtdnt2ZXJ9Lnhsc3giKQogIGluY2x1ZGVfc2lnX2ZpbGVuYW1lIDwtIGdsdWUoImdlbm90eXBlX2NvbnRyYXN0cy9nZW5vdHlwZV97bmFtZX1faW5jbHVkaW5nX3d0X3tsZmNfY3V0b2ZmfV9kZWNyZWFzZWRfc2lnLXZ7dmVyfS54bHN4IikKICBnZW5vdHlwZV90YWJsZXNbW25hbWVdXSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICAgIGdlbm90eXBlX2RlLCBleHRyYV9hbm5vdCA9IGluY2x1ZGVfZGYsCiAgICBrZWVwZXJzID0ga2VlcGVyLCBsYWJlbF9jb2x1bW4gPSBsYWJlbF9jb2x1bW4sCiAgICBleGNlbCA9IGluY2x1ZGVfZmlsZW5hbWUsIHdhbnRlZF9nZW5lcyA9IGluY2x1ZGVzKQogIHByaW50KGdlbm90eXBlX3RhYmxlc1tbbmFtZV1dKQogIGdlbm90eXBlX3NpZ1tbbmFtZV1dIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgICBnZW5vdHlwZV90YWJsZXNbW25hbWVdXSwgYWNjb3JkaW5nX3RvID0gImRlc2VxIiwKICAgIGV4Y2VsID0gaW5jbHVkZV9zaWdfZmlsZW5hbWUpCiAgcHJpbnQoZ2Vub3R5cGVfc2lnW1tuYW1lXV0pCiAgbnVtX3Jvd3MgPC0gbnJvdyhnZW5vdHlwZV9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0pICsKICAgIG5yb3coZ2Vub3R5cGVfc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbImRvd25zIl1dW1tuYW1lXV0pCiAgbWVzc2FnZSgiVGhlcmUgYXJlICIsIG51bV9yb3dzLCAiIHNpZ25pZmljYW50IHVwIGFuZCBkb3duIGdlbmVzLiIpCiAgaWYgKG51bV9yb3dzID49IDEwKSB7CiAgICBtZXNzYWdlKCJQZXJmb3JtaW5nIGdwcm9maWxlci9jbHVzdGVyUHJvZmlsZXIuIikKICAgIGdlbm90eXBlX2dwW1tuYW1lXV0gPC0gYWxsX2dwcm9maWxlcihnZW5vdHlwZV9zaWdbW25hbWVdXSwgc3BlY2llcyA9ICJtbXVzY3VsdXMiKQogICAgZ3Bfd3JpdHRlbiA8LSB3cml0ZV9hbGxfZ3AoZ2Vub3R5cGVfZ3BbW25hbWVdXSkKICAgIGdlbm90eXBlX2NwW1tuYW1lXV0gPC0gYWxsX2Nwcm9maWxlcihnZW5vdHlwZV9zaWdbW25hbWVdXSwgZ2Vub3R5cGVfdGFibGVzW1tuYW1lXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnZGIgPSAib3JnLk1tLmVnLmRiIikKICAgIGNwX3dyaXR0ZW4gPC0gd3JpdGVfYWxsX2NwKGdlbm90eXBlX2NwW1tuYW1lXV0pCiAgfSBlbHNlIHsKICAgIHdhcm5pbmcoIlRoZXJlIGFyZSBsZXNzIHRoYW4gMTAgZ2VuZXMgdXAgYW5kIGRvd24gaW4gdGhlICIsIG5hbWUsICIgY29tcGFyaXNvbi4iKQogICAgbWVzc2FnZSgiVGhlcmUgYXJlIGxlc3MgdGhhbiAxMCBnZW5lcyB1cCBhbmQgZG93biBpbiB0aGUgIiwgbmFtZSwgIiBjb21wYXJpc29uLiIpCiAgfQp9CmBgYAoKQSBmZXcgc3BlY2lmaWMgcGxvdHMgb2YgaW50ZXJlc3Q6IENvbGVuc28gYXNrZWQgdG8gbGFiZWwgYSBmZXcgZ2VuZXMKZm9yIHRoZSBrbm9ja291dC9oZXQgcDA4X3JldGluYXMsIHAwOF9zY24sIGFuZCBwMDhfZGxnbjogZWl0aGVyIHRoZQp0b3AtMTUgb3IgYWxsIHNpZ25pZmljYW50LiAgSSBhbSBwcmV0dHkgc3VyZSBpZiBJIHRlbGwgaXQgMTUgYW5kIHRoZXJlCmFyZSBub3QgdGhhdCBtYW55LCBpdCB3aWxsIGp1c3QgZG8gdGhlIHNpZ25pZmljYW50PyAgTGV0IHVzIGZpbmQgb3V0IQoKIyMjIyBrby9oZXQgZm9yIHAwOCByZXRpbmFzCgpGb3Igc29tZSBjcmF6eSByZWFzb24sIHRoaXMgcGxvdCBpcyBkb3VibGUtbGFiZWxsaW5nIQoKYGBge3J9CnRhYmxlX25hbWUgPC0gImtoX3AwOF9yZXRpbmEiCnRhYmxlX2lucHV0IDwtIGdlbm90eXBlX3RhYmxlc1tbdGFibGVfbmFtZV1dCnRhYmxlIDwtIHRhYmxlX2lucHV0W1siZGF0YSJdXVtbdGFibGVfbmFtZV1dCmludGVyZXN0aW5nIDwtIGMoIk9wbjQiLCAiR205MDA4IiwgIkxycjEiLCAiQ25iZDEiKQpraF9wMDhfcmV0aW5hX3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGZpbGwgPSAiYmxhY2siLAogIGNvbG9yX2xvdyA9IGNvbG9yc1tbImtvX3JldGluYSJdXSwgY29sb3JfaGlnaCA9IGNvbG9yc1tbImhldF9yZXRpbmEiXV0sCiAgbGFiZWxfY29sdW1uID0gIm1naV9zeW1ib2wiLCBsYWJlbCA9IGludGVyZXN0aW5nLCBhbHBoYSA9IDEuMCwKICBzaXplID0gNCwgbGFiZWxfdHlwZSA9ICJsYWJlbCIpCnBwKGZpbGUgPSAiaW1hZ2VzL2toX3AwOF9yZXRpbmFfdm9sY2Fuby5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmtoX3AwOF9yZXRpbmFfdm9sY2Fub1tbInBsb3QiXV0KZGV2Lm9mZigpCmtoX3AwOF9yZXRpbmFfdm9sY2Fub1tbInBsb3QiXV0KIyMgd2h5IGluIHRoZSBjcmFwIGlzIGl0IGRvdWJsZS1sYWJlbGxpbmchPwoKIyMgTXkgTUEgcGxvdHRlciBpc24ndCBhcyBzbWFydCBhcyB0aGUgdm9sY2FubyBwbG90dGVyLCB0aGUgZ2VuZXMgYXJlOgpraF9wMDhfcmV0aW5hX21hIDwtIHBsb3RfbWFfY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBleHByX2NvbCA9ICJkZXNlcV9iYXNlbWVhbiIsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsCiAgY29sb3JfbG93ID0gY29sb3JzW1sia29fcmV0aW5hIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1siaGV0X3JldGluYSJdXSwKICBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgbGFiZWxfY29sdW1uID0gIm1naV9zeW1ib2wiLCBsYWJlbCA9IGludGVyZXN0aW5nKQpwcChmaWxlID0gImltYWdlcy9raF9wMDhfcmV0aW5hX21hLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa2hfcDA4X3JldGluYV9tYVtbInBsb3QiXV0KZGV2Lm9mZigpCmtoX3AwOF9yZXRpbmFfbWFbWyJwbG90Il1dCmBgYAoKIyMjIyBrby9oZXQgcDA4IFNDTgoKSG9seSBjcmFwcGVycywgdGhpcyBwbG90IGRpZCBfbm90XyBkb3VibGUgbGFiZWw7IG9vb2ggSSBoYXZlIGEgY2hlY2sKaW4gbXkgcGxvdHRlciB0byBzZWUgaWYgdGhlcmUgYXJlIHRvbyBmZXcvdG9vIG1hbnkgbGFiZWxzIGFuZCBJCmZvb2xpc2hseSBhbGxvd2VkIGl0IHRvIGNvbmNhdGVuYXRlIHRoZSBsYWJlbHMhICBXaGF0IGluIHRoZSBjcmFwIHdhcwpJIHRoaW5raW5nPwoKSSBhbSBnb2luZyB0byBtYWtlIGFuIGV4ZWN1dGl2ZSBkZWNpc2lvbiBmb3IgdGhpcyBwbG90LCAxNSBpcyB0b28gbWFueQphbmQgbWFrZXMgaXQgY3JhenkgY2x1dHRlcmVkLgoKIyMjIFJlcGVhdCB0aGlzIHdpdGggdHdvIHNldHMgb2YgZ2VuZXMKCmBgYHtyfQp0YWJsZV9uYW1lIDwtICJraF9wMDhfc2NuIgp0YWJsZV9pbnB1dCA8LSBnZW5vdHlwZV90YWJsZXNbW3RhYmxlX25hbWVdXQp0YWJsZSA8LSB0YWJsZV9pbnB1dFtbImRhdGEiXV1bW3RhYmxlX25hbWVdXQppbnRlcmVzdGluZ19nZW5lcyA8LSBjKCJGaWduIiwgIk5ybjEiLCAiRHB5c2wyIiwgIkFjdGIiLCAiRmdmOSIsICJPdHgyIiwgIlNlYzIzIiwKICAgICAgICAgICAgICAgICAgICAgICAiTmNhbTEiLCAiTWFwNCIsICJTZWMyMmIiLCAiTmxnbjMiLCAiTWFyY2tzIiwgIkNkNDciLAogICAgICAgICAgICAgICAgICAgICAgICJEcHlzbDMiLCAiTGluN2MiLCAiQ2FkbTEiLCAiU254MTIiLCAiUmhvYSIsICJJbnBwNWYiLAogICAgICAgICAgICAgICAgICAgICAgICJBdGcxMiIsICJTZXQiLCAiR3NrM2IiLCAiUGRjZDQiLCAiR2FicmEyIiwgIlRtY28xIiwgIkFuYXBjMTYiKQpraF9wMDhfc2NuX3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsCiAgbGFiZWxfY29sdW1uID0gIm1naV9zeW1ib2wiLCBsYWJlbCA9IGludGVyZXN0aW5nX2dlbmVzLCBzaXplID0gNCwgYWxwaGEgPSAxLjAsCiAgY29sb3JfbG93ID0gY29sb3JzW1sia29fc2NuIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1siaGV0X3NjbiJdXSwKICBsYWJlbF90eXBlID0gImxhYmVsIikKcHAoZmlsZSA9ICJpbWFnZXMva2hfcDA4X3Njbl92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa2hfcDA4X3Njbl92b2xjYW5vW1sicGxvdCJdXQpkZXYub2ZmKCkKa2hfcDA4X3Njbl92b2xjYW5vW1sicGxvdCJdXQojIyB3aHkgaW4gdGhlIGNyYXAgaXMgaXQgZG91YmxlLWxhYmVsbGluZyE/CiMjIE15IE1BIHBsb3R0ZXIgaXNuJ3QgYXMgc21hcnQgYXMgdGhlIHZvbGNhbm8gcGxvdHRlciwgdGhlIGdlbmVzIGFyZToKa2hfcDA4X3Njbl9tYSA8LSBwbG90X21hX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZXhwcl9jb2wgPSAiZGVzZXFfYmFzZW1lYW4iLCBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLAogIGNvbG9yX2xvdyA9IGNvbG9yc1tbImtvX3NjbiJdXSwgY29sb3JfaGlnaCA9IGNvbG9yc1tbImhldF9zY24iXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJtZ2lfc3ltYm9sIiwgbGFiZWwgPSBpbnRlcmVzdGluZ19nZW5lcykKcHAoZmlsZSA9ICJpbWFnZXMva2hfcDA4X3Njbl9tYS5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmtoX3AwOF9zY25fbWFbWyJwbG90Il1dCmRldi5vZmYoKQpraF9wMDhfc2NuX21hW1sicGxvdCJdXQpgYGAKCgpgYGB7cn0KdGFibGVfbmFtZSA8LSAia2hfcDA4X3NjbiIKdGFibGVfaW5wdXQgPC0gZ2Vub3R5cGVfdGFibGVzW1t0YWJsZV9uYW1lXV0KdGFibGUgPC0gdGFibGVfaW5wdXRbWyJkYXRhIl1dW1t0YWJsZV9uYW1lXV0KaW50ZXJlc3RpbmdfZ2VuZXMgPC0gYygKICAiQW5hcGMxNiIsICJHYWJyYTIiLCAiVG1jbzEiLCAiU29kMiIsICJGZ2Y5IiwgIlBkY2Q0IiwgIlJob2EiLCAiR3NrM2IiLCAiRm94cDEiLAogICJOY2FtMSIsICJNYXJja3MiLCAiRmlnbiIsICJEcHlzbDMiLCAiSW5wcDVmIiwgIkNhZG0xIiwgIk1hcDQiLCAiVWdjZyIsICJFbG92bDQiLAogICJFbGF2bDEiLCAiQ2ZsMiIsICJUbm50MSIsICJHbmIxIiwgIkltcGFjdCIsICJOcm4xIiwgIk5sZ24zIiwgIkFjdGIiLCAiQ2Q0NyIsCiAgIlNlYzIyYiIsICJTbGMxN2E3IiwgIlZnbHV0MSIsICJBY3RiIiwgIkI0Z2FsdDUiLCAiRm94cDEiLCAiT3R4MiIsICJMaW43YyIsCiAgIlNueDEyIiwgIkF0ZzEyIiwgIlNldCIpCmtoX3AwOF9zY25fdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwKICBjb2xvcl9sb3cgPSBjb2xvcnNbWyJrb19zY24iXV0sIGNvbG9yX2hpZ2ggPSBjb2xvcnNbWyJoZXRfc2NuIl1dLAogIGxhYmVsX2NvbHVtbiA9ICJtZ2lfc3ltYm9sIiwgbGFiZWwgPSBpbnRlcmVzdGluZ19nZW5lcywgc2l6ZSA9IDQsIGFscGhhID0gMS4wLAogIGxhYmVsX3R5cGUgPSAicmVwZWwiLCBudWRnZV94ID0gLTEsIG51ZGdlX3kgPSAwKQpwcChmaWxlID0gImltYWdlcy9raF9wMDhfc2NuX3ZvbGNhbm9fdjIucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQpraF9wMDhfc2NuX3ZvbGNhbm9bWyJwbG90Il1dCmRldi5vZmYoKQpraF9wMDhfc2NuX3ZvbGNhbm9bWyJwbG90Il1dCiMjIHdoeSBpbiB0aGUgY3JhcCBpcyBpdCBkb3VibGUtbGFiZWxsaW5nIT8KIyMgTXkgTUEgcGxvdHRlciBpc24ndCBhcyBzbWFydCBhcyB0aGUgdm9sY2FubyBwbG90dGVyLCB0aGUgZ2VuZXMgYXJlOgpraF9wMDhfc2NuX21hIDwtIHBsb3RfbWFfY29uZGl0aW9uX2RlKAogIHRhYmxlLCB0YWJsZV9uYW1lLCBleHByX2NvbCA9ICJkZXNlcV9iYXNlbWVhbiIsIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsCiAgY29sb3JfbG93ID0gY29sb3JzW1sia29fc2NuIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1siaGV0X3NjbiJdXSwKICBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgbGFiZWxfY29sdW1uID0gIm1naV9zeW1ib2wiLCBsYWJlbCA9IGludGVyZXN0aW5nX2dlbmVzKQpwcChmaWxlID0gImltYWdlcy9raF9wMDhfc2NuX21hX3YyLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa2hfcDA4X3Njbl9tYVtbInBsb3QiXV0KZGV2Lm9mZigpCmtoX3AwOF9zY25fbWFbWyJwbG90Il1dCmBgYAoKCiMjIyMga28vaGV0IHAwOCBkTEdOCgpgYGB7cn0KdGFibGVfbmFtZSA8LSAia2hfcDA4X2RsZ24iCnRhYmxlX2lucHV0IDwtIGdlbm90eXBlX3RhYmxlc1tbdGFibGVfbmFtZV1dCnRhYmxlIDwtIHRhYmxlX2lucHV0W1siZGF0YSJdXVtbdGFibGVfbmFtZV1dCmtoX3AwOF9kbGduX3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsCiAgY29sb3JfbG93ID0gY29sb3JzW1sia29fZGxnbiJdXSwgY29sb3JfaGlnaCA9IGNvbG9yc1tbImhldF9kbGduIl1dLAogIGxhYmVsX2NvbHVtbiA9ICJtZ2lfc3ltYm9sIiwgbGFiZWwgPSAxMCwgc2l6ZSA9IDQsIGFscGhhID0gMS4wLAogIGxhYmVsX3R5cGUgPSAibGFiZWwiKQpwcChmaWxlID0gImltYWdlcy9raF9wMDhfZGxnbl92b2xjYW5vLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKa2hfcDA4X2RsZ25fdm9sY2Fub1tbInBsb3QiXV0KZGV2Lm9mZigpCmtoX3AwOF9kbGduX3ZvbGNhbm9bWyJwbG90Il1dCiMjIE15IE1BIHBsb3R0ZXIgaXNuJ3QgYXMgc21hcnQgYXMgdGhlIHZvbGNhbm8gcGxvdHRlciwgdGhlIGdlbmVzIGFyZToKa2hfcDA4X2RsZ25fbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBjb2xvcnNbWyJrb19kbGduIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1siaGV0X2RsZ24iXV0sCiAgcF9jb2wgPSAiZGVzZXFfYWRqcCIsIGxhYmVsX2NvbHVtbiA9ICJtZ2lfc3ltYm9sIiwgbGFiZWwgPSAxMCkKcHAoZmlsZSA9ICJpbWFnZXMva2hfcDA4X2RsZ25fbWEucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQpraF9wMDhfZGxnbl9tYVtbInBsb3QiXV0KZGV2Lm9mZigpCmtoX3AwOF9kbGduX21hW1sicGxvdCJdXQpgYGAKCiMjIyBMb2NhdGlvbiBjb250cmFzdHMgd2l0aCBnZW5lcyByZW1vdmVkL2tlcHQKClJlcGVhdCB0aGUgc2FtZSBibG9jayB3aXRoIGEgZmluZC9yZXBsYWNlIG9mIGdlbm90eXBlL2xvY2F0aW9uLgoKYGBge3J9CmxvY2F0aW9uX3RhYmxlc19mdWxsIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIGxvY2F0aW9uX2RlLCBrZWVwZXJzID0gbG9jYXRpb25fa2VlcGVycywgbGFiZWxfY29sdW1uID0gbGFiZWxfY29sdW1uLAogIGV4Y2VsID0gZ2x1ZSgiZnVsbF9jb250cmFzdHMvbG9jYXRpb25fZnVsbF90YWJsZXMtdnt2ZXJ9Lnhsc3giKSkKbG9jYXRpb25fdGFibGVzX2Z1bGwKbG9jYXRpb25fc2lnX2Z1bGwgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICBsb2NhdGlvbl90YWJsZXNfZnVsbCwgYWNjb3JkaW5nX3RvID0gImRlc2VxIiwKICBleGNlbCA9IGdsdWUoImZ1bGxfY29udHJhc3RzL2xvY2F0aW9uX2Z1bGxfc2lnLXZ7dmVyfS54bHN4IikpCmxvY2F0aW9uX3NpZ19mdWxsCmxvY2F0aW9uX2Z1bGxfdXBzZXQgPC0gdXBzZXRyX3NpZyhsb2NhdGlvbl9zaWdfZnVsbCkKIyNsb2NhdGlvbl9mdWxsX2ludGVyc2VjdHMgPC0gd3JpdGVfdXBzZXRfZ3JvdXBzKAojIyAgbG9jYXRpb25fZnVsbF91cHNldCwKIyMgIGV4Y2VsID0gImV4Y2VsL2xvY2F0aW9uX2Z1bGxfZ2VuZV9ncm91cHMueGxzeCIpCmxvY2F0aW9uX3RhYmxlcyA8LSBsaXN0KCkKbG9jYXRpb25fc2lnIDwtIGxpc3QoKQpsb2NhdGlvbl9ncCA8LSBsaXN0KCkKbG9jYXRpb25fY3AgPC0gbGlzdCgpCmZvciAoayBpbiBzZXFfYWxvbmcobG9jYXRpb25fa2VlcGVycykpIHsKICBuYW1lIDwtIG5hbWVzKGxvY2F0aW9uX2tlZXBlcnMpW2tdCiAgbWVzc2FnZSgiRXhhbWluaW5nICIsIG5hbWUpCiAga2VlcGVyIDwtIGxvY2F0aW9uX2tlZXBlcnNbbmFtZV0KICBpbmNsdWRlcyA8LSBsb2NhdGlvbl9pbmNsdXNpb25zW1tuYW1lXV0KICBpbmNsdWRlX25hbWUgPC0gcGFzdGUwKCJpbmNfIiwgbmFtZSkKICBpbmNsdWRlX2RmX25hbWUgPC0gcGFzdGUwKCJkZl8iLCBuYW1lKQogIGluY2x1ZGVfZGYgPC0gbG9jYXRpb25faW5jbHVzaW9uc1tbaW5jbHVkZV9kZl9uYW1lXV0KICBpbmNsdWRlcyA8LSBsb2NhdGlvbl9pbmNsdXNpb25zW1tpbmNsdWRlX25hbWVdXQogIHN1bW1hcnkocm93bmFtZXMobG9jYXRpb25fc2lnX2Z1bGxbWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dKSAlaW4lIGluY2x1ZGVzKQogIGluY2x1ZGVfZmlsZW5hbWUgPC0gZ2x1ZSgibG9jYXRpb25fY29udHJhc3RzL2xvY2F0aW9uX3tuYW1lfV9pbmNsdWRpbmdfd3Rfe2xmY19jdXRvZmZ9X2RlY3JlYXNlZF90YWJsZS12e3Zlcn0ueGxzeCIpCiAgaW5jbHVkZV9zaWdfZmlsZW5hbWUgPC0gZ2x1ZSgibG9jYXRpb25fY29udHJhc3RzL2xvY2F0aW9uX3tuYW1lfV9pbmNsdWRpbmdfd3Rfe2xmY19jdXRvZmZ9X2RlY3JlYXNlZF9zaWctdnt2ZXJ9Lnhsc3giKQogIGxvY2F0aW9uX3RhYmxlc1tbbmFtZV1dIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogICAgbG9jYXRpb25fZGUsIGV4dHJhX2Fubm90ID0gaW5jbHVkZV9kZiwKICAgIGtlZXBlcnMgPSBrZWVwZXIsIGxhYmVsX2NvbHVtbiA9IGxhYmVsX2NvbHVtbiwKICAgIGV4Y2VsID0gaW5jbHVkZV9maWxlbmFtZSwgd2FudGVkX2dlbmVzID0gaW5jbHVkZXMpCiAgcHJpbnQobG9jYXRpb25fdGFibGVzW1tuYW1lXV0pCiAgbG9jYXRpb25fc2lnW1tuYW1lXV0gPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICAgIGxvY2F0aW9uX3RhYmxlc1tbbmFtZV1dLCBhY2NvcmRpbmdfdG8gPSAiZGVzZXEiLAogICAgZXhjZWwgPSBpbmNsdWRlX3NpZ19maWxlbmFtZSkKICBwcmludChsb2NhdGlvbl9zaWdbW25hbWVdXSkKICBudW1fcm93cyA8LSBucm93KGxvY2F0aW9uX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXSkgKwogICAgbnJvdyhsb2NhdGlvbl9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1siZG93bnMiXV1bW25hbWVdXSkKICBtZXNzYWdlKCJUaGVyZSBhcmUgIiwgbnVtX3Jvd3MsICIgc2lnbmlmaWNhbnQgdXAgYW5kIGRvd24gZ2VuZXMuIikKICBpZiAobnVtX3Jvd3MgPiAxMCkgewogICAgbG9jYXRpb25fZ3BbW25hbWVdXSA8LSBhbGxfZ3Byb2ZpbGVyKGxvY2F0aW9uX3NpZ1tbbmFtZV1dLCBzcGVjaWVzID0gIm1tdXNjdWx1cyIpCiAgICBncF93cml0dGVuIDwtIHdyaXRlX2FsbF9ncChnZW5vdHlwZV9ncFtbbmFtZV1dKQogICAgbG9jYXRpb25fY3BbW25hbWVdXSA8LSBhbGxfY3Byb2ZpbGVyKGxvY2F0aW9uX3NpZ1tbbmFtZV1dLCBsb2NhdGlvbl90YWJsZXNbW25hbWVdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmdkYiA9ICJvcmcuTW0uZWcuZGIiKQogICAgY3Bfd3JpdHRlbiA8LSB3cml0ZV9hbGxfY3AoZ2Vub3R5cGVfY3BbW25hbWVdXSkKICB9Cn0KYGBgCgpDb2xlbnNvIHNlbnQgYSBzcGVjaWZpYyBxdWVyeSBvZiBpbnRlcmVzdCwgY29tcGFyaW5nIFNDTiB2cy4gUmV0aW5hcwphdCBwMDggaW4gdGhlIGhldGVyb3p5Z290ZXMgaW5jbHVkaW5nIGEgc2V0IG9mIGdlbmVzIG9mIHBhcnRpY3VsYXIKaW50ZXJlc3QuICBQZXJoYXBzIEkgY2FuIHVzZSBzb21lIG9mIHRoZXNlIGFzIG1hcmtlcnMgdG8gcXVhbGl0eQpjb250cm9sIG15IHdvcmsgaW4gdGhlIGZ1dHVyZT8KCkhlcmUgYXJlIHRoZSBnZW5lczoKCk9wbjQsIEVvbWVzLCBUcnBjNywgT3BybTEsIE5yNGEzLCBUYngyMCwgSXJ4NiwgQVc1NTE5ODQsIFBjZGgxOSwKQWRjeWFwMSwgQmFpYXAzLCBDaGwxLCBHcmluM2EsIElnZjEsIEdyaWExLCBHcmluMmQsIEdyaW4zYSwgQ2hybmE2LApDaHJuYTMsIEh0cjVhLCBIdHIyYSwgSHRyNywgSXJ4NCwgUGx4bkMxLCBTZW1hNmQsIFNlbWE0ZiwgU2VtYTRhLApTZW1hNmIsIExycmM0YiwgTHJyYzU4LCBMcnJjM2IsIFdudDQsIFdudDliLCBDdHhuMywgVGVubTEsIEduYTE0LApSZ3M0LCBSZ3M2LCBSZ3M1CgpgYGB7cn0KdGFibGVfaW5wdXQgPC0gbG9jYXRpb25fdGFibGVzW1sic3JfcDA4X2hldCJdXQp0YWJsZV9uYW1lIDwtICJzcl9wMDhfaGV0Igp0YWJsZSA8LSB0YWJsZV9pbnB1dFtbImRhdGEiXV1bW3RhYmxlX25hbWVdXQppbnRlcmVzdGluZ19nZW5lcyA8LSBjKCJPcG40IiwgIkVvbWVzIiwgIlRycGM3IiwgIk9wcm0xIiwgIk5yNGEzIiwgIlRieDIwIiwKICAgICAgICAgICAgICAgICAgICAgICAiSXJ4NiIsICJBVzU1MTk4NCIsICJQY2RoMTkiLCAiQWRjeWFwMXIxIiwgIkJhaWFwMyIsCiAgICAgICAgICAgICAgICAgICAgICAgIkNobDEiLCAiR3JpbjNhIiwgIklnZjEiLCAiR3JpYTEiLCAiR3JpbjJkIiwgIkdyaW4zYSIsCiAgICAgICAgICAgICAgICAgICAgICAgIkNocm5hNiIsICJDaHJuYTMiLCAiSHRyNWEiLCAiSHRyMmEiLCAiSHRyNyIsICJJcng0IiwKICAgICAgICAgICAgICAgICAgICAgICAiUGx4bkMxIiwgIlNlbWE2ZCIsICJTZW1hNGYiLCAiU2VtYTRhIiwgIlNlbWE2YiIsICJMcnJjNGIiLAogICAgICAgICAgICAgICAgICAgICAgICJMcnJjNTgiLCAiTHJyYzNiIiwgIldudDQiLCAiV250OWIiLCAiQ3R4bjMiLCAiVGVubTEiLCAiR25hMTQiLAogICAgICAgICAgICAgICAgICAgICAgICJSZ3M0IiwgIlJnczYiLCAiUmdzNSIsICJQb3U0ZjIiLCAiQ2hybmIzIiwgIkJjYW4iKQpzcl9wMDhfaGV0X3ZvbGNhbm8gPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICB0YWJsZSwgdGFibGVfbmFtZSwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwgcF9jb2wgPSAiZGVzZXFfYWRqcCIsCiAgY29sb3JfbG93ID0gY29sb3JzW1siaGV0X3NjbiJdXSwgY29sb3JfaGlnaCA9IGNvbG9yc1tbImhldF9yZXRpbmEiXV0sCiAgbGFiZWxfY29sdW1uID0gIm1naV9zeW1ib2wiLCBsYWJlbCA9IGludGVyZXN0aW5nX2dlbmVzLCBhbHBoYSA9IDEuMCwKICBsYWJlbF90eXBlID0gImxhYmVsIiwgc2l6ZSA9IDQpCnBwKGZpbGUgPSAiaW1hZ2VzL3NyX3AwOF9oZXRfdm9sY2Fuby5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCnNyX3AwOF9oZXRfdm9sY2Fub1tbInBsb3QiXV0KZGV2Lm9mZigpCnNyX3AwOF9oZXRfdm9sY2Fub1tbInBsb3QiXV0KCnNyX3AwOF9oZXRfbWEgPC0gcGxvdF9tYV9jb25kaXRpb25fZGUoCiAgdGFibGUsIHRhYmxlX25hbWUsIGV4cHJfY29sID0gImRlc2VxX2Jhc2VtZWFuIiwgZmNfY29sID0gImRlc2VxX2xvZ2ZjIiwKICBjb2xvcl9sb3cgPSBjb2xvcnNbWyJoZXRfc2NuIl1dLCBjb2xvcl9oaWdoID0gY29sb3JzW1siaGV0X3JldGluYSJdXSwKICBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgbGFiZWxfY29sdW1uID0gIm1naV9zeW1ib2wiLCBsYWJlbCA9IGludGVyZXN0aW5nX2dlbmVzKQpwcChmaWxlID0gImltYWdlcy9zcl9wMDhfaGV0X21hLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKc3JfcDA4X2hldF9tYVtbInBsb3QiXV0KZGV2Lm9mZigpCnNyX3AwOF9oZXRfbWFbWyJwbG90Il1dCmBgYAoKIyMjIyBUZXN0IGEgc3BlY2lmaWMgbG9jYXRpb24gcXVlcnkgZm9yIGR1cGxpY2F0ZWQgSURzCgpMZXQgdXMgc2VlIGlmIGFueSBFbnNlbWJsIGdlbmUgSURzIGFuZC9vciBNR0kgSURzIGFyZSBzaGFyZWQgaW4gdGhlCndvcmtzaGVldCBsb2NhdGlvbl9zcl9wMDhfa29faW5jbHVkaW5nX3d0XzAuMV9kZWNyZWFzZWRfc2lnIHVwL2Rvd24uCgpgYGB7cn0KdGVzdF90YWJsZV91cCA8LSBsb2NhdGlvbl9zaWdbWyJzcl9wMDhfa28iXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbMV1dCnRlc3RfdGFibGVfZG93biA8LSBsb2NhdGlvbl9zaWdbWyJzcl9wMDhfa28iXV1bWyJkZXNlcSJdXVtbImRvd25zIl1dW1sxXV0KCnF1ZXJ5IDwtIGxpc3QoInVwIiA9IHJvd25hbWVzKHRlc3RfdGFibGVfdXApLAogICAgICAgICAgICAgICJkb3duIiA9IHJvd25hbWVzKHRlc3RfdGFibGVfZG93bikpCnF1ZXJ5X3Vwc2V0IDwtIFVwU2V0Ujo6ZnJvbUxpc3QocXVlcnkpClVwU2V0Ujo6dXBzZXQocXVlcnlfdXBzZXQpCgpxdWVyeSA8LSBsaXN0KCJ1cCIgPSB0ZXN0X3RhYmxlX3VwW1sibWdpX3N5bWJvbCJdXSwKICAgICAgICAgICAgICAiZG93biIgPSB0ZXN0X3RhYmxlX2Rvd25bWyJtZ2lfc3ltYm9sIl1dKQpxdWVyeV91cHNldCA8LSBVcFNldFI6OmZyb21MaXN0KHF1ZXJ5KQpVcFNldFI6OnVwc2V0KHF1ZXJ5X3Vwc2V0KQojIyBvaywgZ29vZC4KYGBgCgojIyMgQW5kIHRpbWUKCmBgYHtyfQp0aW1lX3RhYmxlc19mdWxsIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHRpbWVfZGUsIGtlZXBlcnMgPSB0aW1lX2tlZXBlcnMsCiAgbGFiZWxfY29sdW1uID0gbGFiZWxfY29sdW1uLAogIGV4Y2VsID0gZ2x1ZSgiZnVsbF9jb250cmFzdHMvdGltZV9mdWxsX3RhYmxlcy12e3Zlcn0ueGxzeCIpKQp0aW1lX3NpZ19mdWxsIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgdGltZV90YWJsZXNfZnVsbCwgYWNjb3JkaW5nX3RvID0gImRlc2VxIiwKICBleGNlbCA9IGdsdWUoImZ1bGxfY29udHJhc3RzL3RpbWVfZnVsbF9zaWctdnt2ZXJ9Lnhsc3giKSkKdGltZV90YWJsZXMgPC0gbGlzdCgpCnRpbWVfc2lnIDwtIGxpc3QoKQp0aW1lX2dwIDwtIGxpc3QoKQp0aW1lX2NwIDwtIGxpc3QoKQpmb3IgKGsgaW4gc2VxX2Fsb25nKHRpbWVfa2VlcGVycykpIHsKICBuYW1lIDwtIG5hbWVzKHRpbWVfa2VlcGVycylba10KICBtZXNzYWdlKCJFeGFtaW5pbmcgIiwgbmFtZSkKICBrZWVwZXIgPC0gdGltZV9rZWVwZXJzW25hbWVdCiAgaW5jbHVkZXMgPC0gdGltZV9pbmNsdXNpb25zW1tuYW1lXV0KICBpbmNsdWRlX25hbWUgPC0gcGFzdGUwKCJpbmNfIiwgbmFtZSkKICBpbmNsdWRlX2RmX25hbWUgPC0gcGFzdGUwKCJkZl8iLCBuYW1lKQogIGluY2x1ZGVfZGYgPC0gdGltZV9pbmNsdXNpb25zW1tpbmNsdWRlX2RmX25hbWVdXQogIGluY2x1ZGVzIDwtIHRpbWVfaW5jbHVzaW9uc1tbaW5jbHVkZV9uYW1lXV0KICBzdW1tYXJ5KHJvd25hbWVzKHRpbWVfc2lnX2Z1bGxbWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dKSAlaW4lIGluY2x1ZGVzKQogIGluY2x1ZGVfZmlsZW5hbWUgPC0gZ2x1ZSgidGltZV9jb250cmFzdHMvdGltZV97bmFtZX1faW5jbHVkaW5nX3d0X3tsZmNfY3V0b2ZmfV9kZWNyZWFzZWRfdGFibGUtdnt2ZXJ9Lnhsc3giKQogIGluY2x1ZGVfc2lnX2ZpbGVuYW1lIDwtIGdsdWUoInRpbWVfY29udHJhc3RzL3RpbWVfe25hbWV9X2luY2x1ZGluZ193dF97bGZjX2N1dG9mZn1fZGVjcmVhc2VkX3NpZy12e3Zlcn0ueGxzeCIpCiAgdGltZV90YWJsZXNbW25hbWVdXSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICAgIHRpbWVfZGUsIGV4dHJhX2Fubm90ID0gaW5jbHVkZV9kZiwKICAgIGtlZXBlcnMgPSBrZWVwZXIsIGxhYmVsX2NvbHVtbiA9IGxhYmVsX2NvbHVtbiwKICAgIGV4Y2VsID0gaW5jbHVkZV9maWxlbmFtZSwgd2FudGVkX2dlbmVzID0gaW5jbHVkZXMpCiAgcHJpbnQodGltZV90YWJsZXNbW25hbWVdXSkKICB0aW1lX3NpZ1tbbmFtZV1dIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgICB0aW1lX3RhYmxlc1tbbmFtZV1dLCBhY2NvcmRpbmdfdG8gPSAiZGVzZXEiLAogICAgZXhjZWwgPSBpbmNsdWRlX2ZpbGVuYW1lKQogIHByaW50KHRpbWVfc2lnW1tuYW1lXV0pCiAgbnVtX3Jvd3MgPC0gbnJvdyh0aW1lX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXSkgKwogICAgbnJvdyh0aW1lX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dKQogIG1lc3NhZ2UoIlRoZXJlIGFyZSAiLCBudW1fcm93cywgIiBzaWduaWZpY2FudCB1cCBhbmQgZG93biBnZW5lcy4iKQogIGlmIChudW1fcm93cyA+IDEwKSB7CiAgICB0aW1lX2dwW1tuYW1lXV0gPC0gYWxsX2dwcm9maWxlcih0aW1lX3NpZ1tbbmFtZV1dLCBzcGVjaWVzID0gIm1tdXNjdWx1cyIpCiAgICBncF93cml0dGVuIDwtIHdyaXRlX2FsbF9ncCh0aW1lX2dwW1tuYW1lXV0pCiAgICB0aW1lX2NwW1tuYW1lXV0gPC0gYWxsX2Nwcm9maWxlcih0aW1lX3NpZ1tbbmFtZV1dLCB0aW1lX3RhYmxlc1tbbmFtZV1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnZGIgPSAib3JnLk1tLmVnLmRiIikKICAgIGNwX3dyaXR0ZW4gPC0gd3JpdGVfYWxsX2NwKHRpbWVfY3BbW25hbWVdXSkKICB9Cn0KYGBgCgojIFRyYW5zbGF0b21lIHF1ZXJpZXMKCkluIGNvbnZlcnNhdGlvbiB3aXRoIENvbGVuc28sIGhlIHNwb2tlIGFib3V0IGEgc2VyaWVzIG9mIGNvbnRyYXN0cwp3aGljaCB3b3VsZCBiZSBpbnRlcmVzdGluZyB0byBhdHRlbXB0IGluIG9yZGVyIHRvIHF1ZXJ5IHRoZSBjaGFuZ2VzCmFjcm9zcyBib3RoIGxvY2F0aW9ucyBhbmQgZ2Vub3R5cGVzIGFuZC9vciBib3RoIGxvY2F0aW9ucyBhbmQgdGltZSwKdGh1czoKCihwMDhfaGV0X3NjbiAvIHAwOF9oZXRfcmV0aW5hKSAvIChwMDhfa29fc2NuIC8gcDA4X2tvX3JldGluYSkKCmFzIGFuIGV4YW1wbGUuICBXZSBjYW4gZGVmaW5pdGVseSBkbyB0aGVzZSwgYnV0IHRoZXkgZG8gbm90IHdvcmsgZm9yCmFsbCBtZXRob2RzIGVtcGxveWVkIChJIHRoaW5rIHRoZXkgd29yayBiZXN0IHdpdGggbGltbWEgYW5kIGVkZ2VSKS4KCkxldHMgZmluZCBvdXQhCgojIyBUd28gc2NuL3JldGluYSBjb21wYXJpc29ucwoKKiAocDA4X2hldF9zY24gLyBwMDhfaGV0X3JldGluYSkgLyAocDA4X2tvX3NjbiAvIHAwOF9rb19yZXRpbmEpCiogKHAxNV9oZXRfc2NuIC8gcDE1X2hldF9yZXRpbmEpIC8gKHAxNV9rb19zY24gLyBwMTVfa29fcmV0aW5hKQoKYGBge3J9CnNjbl9leHRyYSA8LSBnbHVlKCJcXAogIHAwOGhldCA9IChjb25kaXRpb25wMDhfaGV0X3NjbiAtIGNvbmRpdGlvbnAwOF9oZXRfcmV0aW5hKSwgXFwKICBwMDhrbyA9IChjb25kaXRpb25wMDhfa29fc2NuIC0gY29uZGl0aW9ucDA4X2tvX3JldGluYSksIFxcCiAgcDA4aGV0X3ZzX3AwOGtvID0gKGNvbmRpdGlvbnAwOF9oZXRfc2NuIC0gY29uZGl0aW9ucDA4X2hldF9yZXRpbmEpIC0gKGNvbmRpdGlvbnAwOF9rb19zY24gLSBjb25kaXRpb25wMDhfa29fcmV0aW5hKSwgXFwKICBwMTVoZXQgPSAoY29uZGl0aW9ucDE1X2hldF9zY24gLSBjb25kaXRpb25wMTVfaGV0X3JldGluYSksIFxcCiAgcDE1a28gPSAoY29uZGl0aW9ucDE1X2tvX3NjbiAtIGNvbmRpdGlvbnAxNV9rb19yZXRpbmEpLCBcXAogIHAxNWhldF92c19wMTVrbyA9IChjb25kaXRpb25wMTVfaGV0X3NjbiAtIGNvbmRpdGlvbnAxNV9oZXRfcmV0aW5hKSAtIChjb25kaXRpb25wMTVfa29fc2NuIC0gY29uZGl0aW9ucDE1X2tvX3JldGluYSkiKQpzY25fdHJhbnNsYXRvbWVfZGVfa2VlcGVycyA8LSBsaXN0KAogICJwMDhoZXQiID0gYygicDA4X2hldF9zY24iLCAicDA4X2hldF9yZXRpbmEiKSwKICAicDA4a28iID0gYygicDA4X2tvX3NjbiIsICJwMDhfa29fcmV0aW5hIiksCiAgInAxNWhldCIgPSBjKCJwMTVfaGV0X3NjbiIsICJwMTVfaGV0X3JldGluYSIpLAogICJwMTVrbyIgPSBjKCJwMTVfa29fc2NuIiwgInAxNV9rb19yZXRpbmEiKSkKc2NuX3RyYW5zbGF0b21lX2tlZXBlcnMgPC0gbGlzdCgKICAicDA4aGV0IiA9IGMoInAwOF9oZXRfc2NuIiwgInAwOF9oZXRfcmV0aW5hIiksCiAgInAwOGtvIiA9IGMoInAwOF9rb19zY24iLCAicDA4X2tvX3JldGluYSIpLAogICJwMDhfc2NuX3RyYW5zbGF0b21lIiA9IGMoInAwOGhldCIsICJwMDhrbyIpLAogICJwMTVoZXQiID0gYygicDE1X2hldF9zY24iLCAicDE1X2hldF9yZXRpbmEiKSwKICAicDE1a28iID0gYygicDE1X2tvX3NjbiIsICJwMTVfa29fcmV0aW5hIiksCiAgInAxNV9zY25fdHJhbnNsYXRvbWUiID0gYygicDE1aGV0IiwgInAxNWtvIikpCgpmaWx0IDwtIG5vcm1hbGl6ZV9leHB0KHYzX3BhaXJ3aXNlX2lucHV0LCBmaWx0ZXIgPSBUUlVFKQpsaW1tYV90ZXN0IDwtIGxpbW1hX3BhaXJ3aXNlKGZpbHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2VlcGVycyA9IHNjbl90cmFuc2xhdG9tZV9kZV9rZWVwZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBfdW5kZXJzY29yZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfYmF0Y2ggPSBGQUxTRSwgZXh0cmFfY29udHJhc3RycyA9IHNjbl9leHRyYSkKZWRnZXJfdGVzdCA8LSBlZGdlcl9wYWlyd2lzZShmaWx0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBlcnMgPSBzY25fdHJhbnNsYXRvbWVfZGVfa2VlcGVycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZWVwX3VuZGVyc2NvcmUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX2JhdGNoID0gRkFMU0UsIGV4dHJhX2NvbnRyYXN0cyA9IHNjbl9leHRyYSkKc2NuX3RyYW5zbGF0b21lX2RlIDwtIGFsbF9wYWlyd2lzZSh2M19wYWlyd2lzZV9pbnB1dCwgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZWVwZXJzID0gc2NuX3RyYW5zbGF0b21lX2RlX2tlZXBlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfYmF0Y2ggPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb19iYXNpYyA9IEZBTFNFLCBkb19kcmVhbSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvX25vaXNlcSA9IEZBTFNFLCBkb19lYnNlcSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4dHJhX2NvbnRyYXN0cyA9IHNjbl9leHRyYSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbGxlbCA9IEZBTFNFLCBrZWVwX3VuZGVyc2NvcmUgPSBUUlVFKQpzY25fY29tYmluZWRfdGVzdCA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBzY25fdHJhbnNsYXRvbWVfZGUsIGtlZXBlcnMgPSBzY25fdHJhbnNsYXRvbWVfa2VlcGVycywKICBleGNlbCA9IGdsdWUoInRyYW5zbGF0b21lL3Rlc3Rfc2NuX3RyYW5zbGF0b21lX3VuZmlsdGVyZWRfbm9zdmEtdnt2ZXJ9Lnhsc3giKSkKCnNjbl90cmFuc2xhdG9tZV9kZV9zdmEgPC0gYWxsX3BhaXJ3aXNlKHYzX3BhaXJ3aXNlX2lucHV0LCBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZWVwZXJzID0gc2NuX3RyYW5zbGF0b21lX2RlX2tlZXBlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvX2Jhc2ljID0gRkFMU0UsIGRvX2RyZWFtID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvX25vaXNlcSA9IEZBTFNFLCBkb19lYnNlcSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHRyYV9jb250cmFzdHMgPSBzY25fZXh0cmEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFsbGVsID0gRkFMU0UsIGtlZXBfdW5kZXJzY29yZSA9IFRSVUUpCnNjbl9jb21iaW5lZF90ZXN0X3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBzY25fdHJhbnNsYXRvbWVfZGVfc3ZhLCBrZWVwZXJzID0gc2NuX3RyYW5zbGF0b21lX2tlZXBlcnMsCiAgZXhjZWwgPSBnbHVlKCJ0cmFuc2xhdG9tZS90ZXN0X3Njbl90cmFuc2xhdG9tZV91bmZpbHRlcmVkX3N2YS12e3Zlcn0ueGxzeCIpKQpgYGAKCiMjIyBTdWJ0cmFjdGluZyBERVNlcTIgcmVzdWx0czogcDA4IHNjbiBoZXQgdnMga28KCmBgYHtyfQpwMDhfc2NuX2NvbWJpbmVkX2Rlc2VxIDwtIHN1YnRyYWN0X2Rlc2VxX3Jlc3VsdHMoCiAgZmlyc3RfdGFibGUgPSBzY25fY29tYmluZWRfdGVzdFtbImRhdGEiXV1bWyJwMDhoZXQiXV0sCiAgc2Vjb25kX3RhYmxlID0gc2NuX2NvbWJpbmVkX3Rlc3RbWyJkYXRhIl1dW1sicDA4a28iXV0sCiAgZmlyc3RfbGZjID0gImRlc2VxX2xvZ2ZjIiwgc2Vjb25kX2xmYyA9ICJkZXNlcV9sb2dmYyIsCiAgZmlyc3RfcCA9ICJkZXNlcV9hZGpwIiwgc2Vjb25kX3AgPSAiZGVzZXFfYWRqcCIsCiAgZmlyc3RfbmFtZSA9ICJoZXQiLCBzZWNvbmRfbmFtZSA9ICJrbyIsCiAgZXhjZWwgPSBnbHVlKCJ0cmFuc2xhdG9tZS90cmFuc2xhdG9tZV9wMDhfc2NuX2NvbWJpbmVkX2Rlc2VxLXZ7dmVyfS54bHN4IikpCmBgYAoKIyMjIFN1YnRyYWN0aW5nIERFU2VxMiByZXN1bHRzOiBwMTUgc2NuIGhldCB2cyBrbwoKYGBge3J9CnAxNV9zY25fY29tYmluZWRfZGVzZXEgPC0gc3VidHJhY3RfZGVzZXFfcmVzdWx0cygKICBmaXJzdF90YWJsZSA9IHNjbl9jb21iaW5lZF90ZXN0W1siZGF0YSJdXVtbInAxNWhldCJdXSwKICBzZWNvbmRfdGFibGUgPSBzY25fY29tYmluZWRfdGVzdFtbImRhdGEiXV1bWyJwMTVrbyJdXSwKICBmaXJzdF9sZmMgPSAiZGVzZXFfbG9nZmMiLCBzZWNvbmRfbGZjID0gImRlc2VxX2xvZ2ZjIiwKICBmaXJzdF9wID0gImRlc2VxX2FkanAiLCBzZWNvbmRfcCA9ICJkZXNlcV9hZGpwIiwKICBmaXJzdF9uYW1lID0gImhldCIsIHNlY29uZF9uYW1lID0gImtvIiwKICBleGNlbCA9IGdsdWUoInRyYW5zbGF0b21lL3RyYW5zbGF0b21lX3AxNV9zY25fY29tYmluZWRfZGVzZXEtdnt2ZXJ9Lnhsc3giKSkKYGBgCgojIyBPbmUgZGxnbi9yZXRpbmEgY29tcGFyaXNvbgoKKiAocDA4X2hldF9kbGduIC8gcDA4X2hldF9yZXRpbmEpIC8gKHAwOF9rb19kbGduIC8gcDA4X2tvX3JldGluYSkKCmBgYHtyfQpwMDhfZGxnbl9leHRyYSA8LSAicDA4aGV0X3ZzX3AwOGtvID0gKGNvbmRpdGlvbnAwOF9oZXRfZGxnbiAtIGNvbmRpdGlvbnAwOF9oZXRfcmV0aW5hKSAtIChjb25kaXRpb25wMDhfa29fZGxnbiAtIGNvbmRpdGlvbnAwOF9rb19yZXRpbmEpIgpwMDhfZGxnbl90cmFuc2xhdG9tZV9kZV9rZWVwZXJzIDwtIGxpc3QoCiAgInAwOGhldCIgPSBjKCJwMDhfaGV0X2RsZ24iLCAicDA4X2hldF9yZXRpbmEiKSwKICAicDA4a28iID0gYygicDA4X2tvX2RsZ24iLCAicDA4X2tvX3JldGluYSIpKQoKcDA4X2RsZ25fdHJhbnNsYXRvbWVfa2VlcGVycyA8LSBsaXN0KAogICJwMDhfaGV0X2RsZ25fdnNfcmV0aW5hIiA9IGMoInAwOF9oZXRfZGxnbiIsICJwMDhfaGV0X3JldGluYSIpLAogICJwMDhfa29fZGxnbl92c19yZXRpbmEiID0gYygicDA4X2tvX2RsZ24iLCAicDA4X2tvX3JldGluYSIpLAogICJwMDhfZGxnbl90cmFuc2xhdG9tZSIgPSBjKCJwMDhoZXQiLCAicDA4a28iKSkKCnAwOF9kbGduX3RyYW5zbGF0b21lX2RlIDwtIGFsbF9wYWlyd2lzZSh2M19wYWlyd2lzZV9pbnB1dCwgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBlcnMgPSBwMDhfZGxnbl90cmFuc2xhdG9tZV9kZV9rZWVwZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfYmF0Y2ggPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvX2Jhc2ljID0gRkFMU0UsIGRvX2RyZWFtID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb19ub2lzZXEgPSBGQUxTRSwgZG9fZWJzZXEgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4dHJhX2NvbnRyYXN0cyA9IHAwOF9kbGduX2V4dHJhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYWxsZWwgPSBGQUxTRSwga2VlcF91bmRlcnNjb3JlID0gVFJVRSkKcDA4X2RsZ25fY29tYmluZWRfdGVzdCA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBwMDhfZGxnbl90cmFuc2xhdG9tZV9kZSwga2VlcGVycyA9IHAwOF9kbGduX3RyYW5zbGF0b21lX2tlZXBlcnMsCiAgbGFiZWxfY29sdW1uID0gbGFiZWxfY29sdW1uLAogIGV4Y2VsID0gZ2x1ZSgidHJhbnNsYXRvbWUvdGVzdF9wMDhfZGxnbl90cmFuc2xhdG9tZV91bmZpbHRlcmVkX25vc3ZhLXZ7dmVyfS54bHN4IikpCgpwMDhfZGxnbl90cmFuc2xhdG9tZV9kZV9zdmEgPC0gYWxsX3BhaXJ3aXNlKHYzX3BhaXJ3aXNlX2lucHV0LCBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBlcnMgPSBwMDhfZGxnbl90cmFuc2xhdG9tZV9kZV9rZWVwZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG9fYmFzaWMgPSBGQUxTRSwgZG9fZHJlYW0gPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb19ub2lzZXEgPSBGQUxTRSwgZG9fZWJzZXEgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHRyYV9jb250cmFzdHMgPSBwMDhfZGxnbl9leHRyYSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbGxlbCA9IEZBTFNFLCBrZWVwX3VuZGVyc2NvcmUgPSBUUlVFKQpwMDhfZGxnbl9jb21iaW5lZF90ZXN0X3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBwMDhfZGxnbl90cmFuc2xhdG9tZV9kZV9zdmEsIGtlZXBlcnMgPSBwMDhfZGxnbl90cmFuc2xhdG9tZV9rZWVwZXJzLAogIGxhYmVsX2NvbHVtbiA9IGxhYmVsX2NvbHVtbiwKICBleGNlbCA9IGdsdWUoInRyYW5zbGF0b21lL3Rlc3RfcDA4X2RsZ25fdHJhbnNsYXRvbWVfdW5maWx0ZXJlZF9zdmEtdnt2ZXJ9Lnhsc3giKSkKYGBgCgojIyMgU3VidHJhY3RpbmcgdGhlIERFU2VxMiByZXN1bHRzCgojIyBUd28gc2NuL3JldGluYSBjb21wYXJpc29ucyAocDE1L3AwOCBhY3Jvc3MgaGV0L2tvKQoKKiAocDE1X2hldF9zY24gLyBwMTVfaGV0X3JldGluYSkgLyAocDA4X2hldF9zY24gLyBwMDhfaGV0X3JldGluYSkKKiAocDE1X2tvX3NjbiAvIHAxNV9rb19yZXRpbmEpIC8gKHAwOF9rb19zY24gLyBwMDhfa29fcmV0aW5hKQoKYGBge3J9CnRpbWVfc2NuX2V4dHJhIDwtIGdsdWUoIlxcCiAgcDE1aGV0ID0gKGNvbmRpdGlvbnAxNV9oZXRfc2NuIC0gY29uZGl0aW9ucDE1X2hldF9yZXRpbmEpLCBcXAogIHAwOGhldCA9IChjb25kaXRpb25wMDhfaGV0X3NjbiAtIGNvbmRpdGlvbnAwOF9oZXRfcmV0aW5hKSwgXFwKICBwMTVoZXRfdnNfcDA4aGV0ID0gKGNvbmRpdGlvbnAxNV9oZXRfc2NuIC0gY29uZGl0aW9ucDE1X2hldF9yZXRpbmEpIC0gKGNvbmRpdGlvbnAwOF9oZXRfc2NuIC0gY29uZGl0aW9ucDA4X2hldF9yZXRpbmEpLAogIHAxNWtvID0gKGNvbmRpdGlvbnAxNV9rb19zY24gLSBjb25kaXRpb25wMTVfa29fcmV0aW5hKSwgXFwKICBwMDhrbyA9IChjb25kaXRpb25wMDhfa29fc2NuIC0gY29uZGl0aW9ucDA4X2tvX3JldGluYSksIFxcCiAgcDE1a29fdnNfcDA4a28gPSAoY29uZGl0aW9ucDE1X2tvX3NjbiAtIGNvbmRpdGlvbnAxNV9rb19yZXRpbmEpIC0gKGNvbmRpdGlvbnAwOF9rb19zY24gLSBjb25kaXRpb25wMDhfa29fcmV0aW5hKSIpCnRpbWVfc2NuX3RyYW5zbGF0b21lX2RlX2tlZXBlcnMgPC0gbGlzdCgKICAicDE1aGV0IiA9IGMoInAxNV9oZXRfc2NuIiwgInAxNV9oZXRfcmV0aW5hIiksCiAgInAwOGhldCIgPSBjKCJwMDhfaGV0X3NjbiIsICJwMDhfaGV0X3JldGluYSIpLAogICJwMTVrbyIgPSBjKCJwMTVfa29fc2NuIiwgInAxNV9rb19yZXRpbmEiKSwKICAicDA4a28iID0gYygicDA4X2tvX3NjbiIsICJwMDhfa29fcmV0aW5hIikpCnRpbWVfc2NuX3RyYW5zbGF0b21lX2tlZXBlcnMgPC0gbGlzdCgKICAicDE1aGV0IiA9IGMoInAxNV9oZXRfc2NuIiwgInAxNV9oZXRfcmV0aW5hIiksCiAgInAwOGhldCIgPSBjKCJwMDhfaGV0X3NjbiIsICJwMDhfaGV0X3JldGluYSIpLAogICJwMTVrbyIgPSBjKCJwMTVfa29fc2NuIiwgInAxNV9rb19yZXRpbmEiKSwKICAicDA4a28iID0gYygicDA4X2tvX3NjbiIsICJwMDhfa29fcmV0aW5hIiksCiAgInAxNV9oZXRfc2NfdnNfcmV0aW5hIiA9IGMoInAxNV9oZXRfc2NuIiwgInAxNV9oZXRfcmV0aW5hIiksCiAgInAwOF9oZXRfc2NfdnNfcmV0aW5hIiA9IGMoInAwOF9oZXRfc2NuIiwgInAwOF9oZXRfcmV0aW5hIiksCiAgInNjbl9oZXRfdHJhbnNsYXRvbWUiID0gYygicDE1aGV0IiwgInAwOGhldCIpLAogICJzY25fa29fdHJhbnNsYXRvbWUiID0gYygicDE1a28iLCAicDA4a28iKSkKCnRpbWVfc2NuX3RyYW5zbGF0b21lX2RlIDwtIGFsbF9wYWlyd2lzZSh2M19wYWlyd2lzZV9pbnB1dCwgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBlcnMgPSB0aW1lX3Njbl90cmFuc2xhdG9tZV9kZV9rZWVwZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfYmF0Y2ggPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvX2Jhc2ljID0gRkFMU0UsIGRvX2RyZWFtID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb19ub2lzZXEgPSBGQUxTRSwgZG9fZWJzZXEgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4dHJhX2NvbnRyYXN0cyA9IHRpbWVfc2NuX2V4dHJhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYWxsZWwgPSBGQUxTRSwga2VlcF91bmRlcnNjb3JlID0gVFJVRSkKdGltZV9zY25fdHJhbnNsYXRvbWVfdGVzdCA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0aW1lX3Njbl90cmFuc2xhdG9tZV9kZSwKICBrZWVwZXJzID0gdGltZV9zY25fdHJhbnNsYXRvbWVfa2VlcGVycywKICBsYWJlbF9jb2x1bW4gPSBsYWJlbF9jb2x1bW4sCiAgZXhjZWwgPSBnbHVlKCJ0cmFuc2xhdG9tZS90ZXN0X3RpbWVfc2NuX3RyYW5zbGF0b21lX3VuZmlsdGVyZWRfbm9zdmEtdnt2ZXJ9Lnhsc3giKSkKCnRpbWVfc2NuX3RyYW5zbGF0b21lX2RlX3N2YSA8LSBhbGxfcGFpcndpc2UodjNfcGFpcndpc2VfaW5wdXQsIGZpbHRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2VlcGVycyA9IHRpbWVfc2NuX3RyYW5zbGF0b21lX2RlX2tlZXBlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb19iYXNpYyA9IEZBTFNFLCBkb19kcmVhbSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvX25vaXNlcSA9IEZBTFNFLCBkb19lYnNlcSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4dHJhX2NvbnRyYXN0cyA9IHRpbWVfc2NuX2V4dHJhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFsbGVsID0gRkFMU0UsIGtlZXBfdW5kZXJzY29yZSA9IFRSVUUpCnRpbWVfc2NuX3RyYW5zbGF0b21lX3Rlc3Rfc3ZhIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHRpbWVfc2NuX3RyYW5zbGF0b21lX2RlX3N2YSwKICBrZWVwZXJzID0gdGltZV9zY25fdHJhbnNsYXRvbWVfa2VlcGVycywKICBsYWJlbF9jb2x1bW4gPSBsYWJlbF9jb2x1bW4sCiAgZXhjZWwgPSBnbHVlKCJ0cmFuc2xhdG9tZS90ZXN0X3RpbWVfc2NuX3RyYW5zbGF0b21lX3VuZmlsdGVyZWRfc3ZhLXZ7dmVyfS54bHN4IikpCmBgYAoKTmV4dCBzdGVwOiBQZXJmb3JtIHRoZSByZXRpbmEgZmlsdGVyOyBuZWVkIHRvIHRoaW5rIGFib3V0IHRoZSBwcm9wZXIKdW5pb24vaW50ZXJzZWN0aW9uIG9mIHRoZSByZXRpbmEveCBleHByZXNzaW9uIHZhbHVlcwoKSW4gdGhlIHByZXZpb3VzIGJsb2NrLCB3ZSBhcmUgbWFraW5nIDIgZ2xvYmFsIGNvbXBhcmlzb25zLCBoZXJlIGlzIG9uZQpvZiB0aGVtOgoKKHAxNWhldHNjbi9wMTVoZXRyZXQpLyhwMDhoZXRzY24vcDA4aGV0cmV0KQoKSSB0aGVyZWZvcmUgd2FudCB0byBleHRyYWN0IHRoZSBtb3N0IGxvZ2ljYWwgc2V0IG9mIGdlbmVzIGhpZ2hlciBpbgpzb21lL2FsbCBvZiB0aGVzZSBjb25kaXRpb25zIHdpdGggcmVzcGVjdCB0byB0aGUgY29ycmVzcG9uZGluZyB3dApjb25kaXRpb25zLiAgUHJldmlvdXNseSwgaW4gc2VjdGlvbiAnRXh0cmFjdCBnZW5lcyBpbmNsdWRlZCBmb3IgZWFjaApzZXQgb2YgY29udHJhc3RzJywgSSBhdHRlbXB0ZWQgdG8gcGVyZm9ybSB0aGlzIG9wZXJhdGlvbiBmb3IgMgpzcGVjaWZpYyB3dCBjb25kaXRpb25zLiAgV2hlbiB0aGlzIHdhcyBwZXJmb3JtZWQsIGl0IHRvb2sgdGhlCnVuaXF1ZSh1bmlvbikgb2YgdGhlIHR3byBzZXRzLiAgVGh1cyBpdCBzdGFuZHMgdG8gcmVhc29uIHRoYXQgSSB3YW50CnRvIHRha2UgdGhlIHVuaXF1ZSh1bmlvbikgb2YgYWxsIDQgaW4gdGhpcyBpbnN0YW5jZT8gIGUuZy46CgoocDE1aGV0c2NuID4gcDE1d3RzY24pIHwgKHAxNWhldHJldCA+IHAxNXd0cmV0KSB8CiAgKHAwOGhldHNjbiA+IHAwOHd0c2NuKSB8IChwMDhoZXRyZXQgPiBwMDh3dHJldCkKCkkga2luZCBvZiB0aGluayBpdCBzaG91bGQgYmU6CgooKHAxNWhldHNjbiA+IHAxNXd0c2NuKSB8IChwMTVoZXRyZXQgPiBwMTV3dHJldCkpICYKICAoKHAwOGhldHNjbiA+IHAwOHd0c2NuKSB8IChwMDhoZXRyZXQgPiBwMDh3dHJldCkpCgpncm9zcywgcGVyaGFwcyBJIHNob3VsZCBqdXN0IGRvIHRoaXMgbWFudWFsbHksIGdpdmVuIHRoYXQgdGhlcmUgYXJlCm9ubHkgYSBmZXcgcHV0YXRpdmUgdHJhbnNsYXRvbWVzIHRvIHF1ZXJ5PwoKIyBRdWljayBhbmQgZGlydHkgREVTZXEyIGNvbnRyYXN0IG9mIGNvbnRyYXN0cwoKSW4gYSBmYXNoaW9uIHNpbWlsYXIgdG8gaG93IEhlY3RvciBoYW5kbGVkIHRoZSBlZmZlY3Qgb2YgcGhhZ29jeXRvc2lzCndpdGggTGF1cmEgYW5kIE5hamliIGEgbG9uZyB0aW1lIGFnbywgSSBwcm9wb3NlIHRvIGRvIGEgc2ltcGxlCnN1YnRyYWN0aW9uIG9mIHRoZSByZXN1bHRzIG9mIG91ciB0d28gY29udHJhc3RzIHdoaWNoIGNvbXByaXNlIHRoZQp0cmFuc2xhdG9tZSBxdWVyeSAoSSB3YXMgdGhpbmtpbmcgYWJvdXQgdGhpcyBsYXN0IHdlZWssIHRodXMgdGhlCmluY2x1c2lvbiBvZiB0aGVtIGluIHRoZSBkZSB0YWJsZXMgYWJvdmUpLiAgU2ltaWxhcmx5IHRvIHRoZQpwaGFnb2N5dG9zaXMgZWZmZWN0LCBJIHdpbGwgc2ltcGx5IHRha2UgdGhlIHdvcnN0IHBvc2libGUgYWRqdXN0ZWQKcC12YWx1ZS4gIEkgd2lsbCByZXBlYXQgdGhpcyB3aXRoIGxpbW1hL0VkZ2VSIGFuZCBzZWUgaG93IHNpbWlsYXIgdGhlCmZpbmFsIHJlc3VsdHMgYXJlIHRvIHdoYXQgdGhvc2UgbWV0aG9kcyBwcm92aWRlIGluIHRoZSAoYS9iKS8oYy9kKQpjb21wYXJpc29ucy4gIEkgYW0gcmVhc29uYWJseSBjZXJ0YWluIHRoYXQgREVTZXEyJ3MgcmVzdWx0cygpIGZ1bmN0aW9uCmhhcyB0aGUgYWJpbGl0eSB0byBwZXJmb3JtIHRoZXNlIG9kZCBjb250cmFzdHMsIGJ1dCBJIGhhdmUgbmV2ZXIKZmlndXJlZCBvdXQgaG93OyBwZXJoYXBzIEkgd2lsbCB1c2UgdGhpcyBhcyBhIGNoYW5jZSB0byByZXZpc2l0IHRoYXQuLi4KCkxldCB1cyB0ZXN0IHRoaXMgaWRlYSB3aXRoIHRoZSBwMDggZGxnbiBxdWVyeSwgd2hpY2ggc2Vla3MgdG8gY29tcGFyZToKCihwMDhfaGV0X2RsZ24gLyBwMDhfaGV0X3JldGluYSkgLyAocDA4X2tvX2RsZ24gLyBwMDhfa29fcmV0aW5hKQoKVGhlc2UgYXJlIG1haW50YWluZWQgaW4gdGhlIGRlX3RhYmxlIHdpdGggdGhlIG5hbWVzCidwMDhfaGV0X2RsZ25fdnNfcmV0aW5hJyBhbmQgJ3AwOF9rb19kbGduX3ZzX3JldGluYScKCiMjIHAwOCBkbGduIGhldCB2cyBrbwoKYGBge3J9CnAwOF9kbGduX2NvbWJpbmVkX2Rlc2VxIDwtIHN1YnRyYWN0X2Rlc2VxX3Jlc3VsdHMoCiAgZmlyc3RfdGFibGUgPSBwMDhfZGxnbl9jb21iaW5lZF90ZXN0W1siZGF0YSJdXVtbInAwOF9oZXRfZGxnbl92c19yZXRpbmEiXV0sCiAgc2Vjb25kX3RhYmxlID0gcDA4X2RsZ25fY29tYmluZWRfdGVzdFtbImRhdGEiXV1bWyJwMDhfa29fZGxnbl92c19yZXRpbmEiXV0sCiAgZmlyc3RfbGZjID0gImRlc2VxX2xvZ2ZjIiwgc2Vjb25kX2xmYyA9ICJkZXNlcV9sb2dmYyIsCiAgZmlyc3RfcCA9ICJkZXNlcV9hZGpwIiwgc2Vjb25kX3AgPSAiZGVzZXFfYWRqcCIsCiAgZmlyc3RfbmFtZSA9ICJoZXQiLCBzZWNvbmRfbmFtZSA9ICJrbyIsCiAgZXhjZWwgPSBnbHVlKCJ0cmFuc2xhdG9tZS90cmFuc2xhdG9tZV9wMDhfZGxnbl9jb21iaW5lZF9kZXNlcS12e3Zlcn0ueGxzeCIpKQpgYGAKClNlZSBob3cgc2ltaWxhciB0aGVzZSByZXN1bHRzIGFyZSB0byB0aG9zZSBvYnRhaW5lZCBmcm9tIGxpbW1hL2VkZ2VyLgoKYGBge3J9CnRlc3RfY29sdW1ucyA8LSBjKCJlZGdlcl9sb2dmYyIsICJsaW1tYV9sb2dmYyIsICJlZGdlcl9hZGpwIiwgImxpbW1hX2FkanAiKQp0ZXN0X2RmIDwtIHAwOF9kbGduX2NvbWJpbmVkX3Rlc3RbWyJkYXRhIl1dW1sicDA4X2RsZ25fdHJhbnNsYXRvbWUiXV1bLCB0ZXN0X2NvbHVtbnNdCnRlc3RfZGYgPC0gbWVyZ2UodGVzdF9kZiwgcDA4X2RsZ25fY29tYmluZWRfZGVzZXEsIGJ5ID0gInJvdy5uYW1lcyIpCnJvd25hbWVzKHRlc3RfZGYpIDwtIHRlc3RfZGZbWyJSb3cubmFtZXMiXV0KdGVzdF9kZltbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCmNvci50ZXN0KHRlc3RfZGZbWyJsaW1tYV9sb2dmYyJdXSwgdGVzdF9kZltbImhldF92c19rb19sb2dmYyJdXSkKY29yLnRlc3QodGVzdF9kZltbImVkZ2VyX2xvZ2ZjIl1dLCB0ZXN0X2RmW1siaGV0X3ZzX2tvX2xvZ2ZjIl1dKQp0dCA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKHRlc3RfZGZbLCBjKCJsaW1tYV9sb2dmYyIsICJoZXRfdnNfa29fbG9nZmMiKV0pCnR0W1sic2NhdHRlciJdXQp0dCA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKHRlc3RfZGZbLCBjKCJlZGdlcl9hZGpwIiwgImhldF92c19rb19wIildKQp0dFtbInNjYXR0ZXIiXV0KIyMgU28sIHVzaW5nIHRoZSBtYXhpbXVtIHAtdmFsdWUgaXMgYSBjb21wbGV0ZSBmYWlsdXJlOyBidXQgdGhlIGV4dHJlbWUgc2ltaWxhcml0aWVzCiMjIGJldHdlZW4gdGhpcyBhbmQgZWRnZVIgc3VnZ2VzdCB0byBtZSB0aGF0IGl0IGlzIGxpa2VseSBwb3NzaWJsZSB0byB1c2UgdGhlIHJlc3VsdHMKIyMgZnJvbSBlZGdlUiB3aXRob3V0IGNvbmNlcm4gKG9yIGxpbW1hIGZvciB0aGF0IG1hdHRlciwgaXQgd2FzIGFsc28gZXh0cmVtZWx5IHNpbWlsYXIpCiMjIE9yIEkgY2FuIHNwZW5kIGEgbGl0dGxlIHRpbWUgYW5kIGNvbGxlY3QgdGhlIG51bWJlcnMgb24gZWFjaCBzaWRlIG9mIHRoZSBkaXZpc2lvbgojIyBhbmQgY2FsY3VsYXRlIGEgdCBzdGF0aXN0aWMgbXlzZWxmLgpgYGAKCiMgTm9uLVNwZWNpZmljIGZpbHRlcmluZyBvZiB0aGUgdHJhbnNsYXRvbWUgZGF0YQoKSSBoYXZlIG9uIGhhbmQKCiogcDA4aGV0X3ZzX3AwOGtvIDogKHAwOF9oZXRfc2NuIC0gcDA4X2hldF9yZXRpbmEpIC0gKHAwOF9rb19zY24gLSBwMDhfa29fcmV0aW5hKQoqIHAxNWhldF92c19wMTVrbyA6IChwMTVfaGV0X3NjbiAtIHAxNV9oZXRfcmV0aW5hKSAtIChwMTVfa29fc2NuIC0gcDE1X2tvX3JldGluYSkKKiBwMDhoZXRfdnNfcDA4a28gOiAocDA4X2hldF9kbGduIC0gcDA4X2hldF9yZXRpbmEpIC0gKHAwOF9rb19kbGduIC0gcDA4X2tvX3JldGluYSkKKiBwMTVoZXRfdnNfcDA4aGV0IDogKHAxNV9oZXRfc2NuIC0gcDE1X2hldF9yZXRpbmEpIC0gKHAwOF9oZXRfc2NuIC0gcDA4X2hldF9yZXRpbmEpCiogcDE1a29fdnNfcDA4a28gOiAocDE1X2tvX3NjbiAtIHAxNV9rb19yZXRpbmEpIC0gKHAwOF9rb19zY24gLSBwMDhfa29fcmV0aW5hKQoKSSBoYXZlIGdlbmUgc2V0cyB1cCBhYm92ZSB3aGljaCBkZWZpbmUgdGhlIGdlbmVzIHN1aXRhYmxlIGZvciBlYWNoIG9mCnRoZXNlIHBpZWNlcy4gIFRoZXJlIGFyZSBvbmx5IDUgY29tcGFyaXNvbnMsIGxldCB1cyBzdGVwIHRocm91Z2ggdGhlbS4KCiMjIFNDTiB0cmFuc2xhdG9tZSBoZXQva28gYXQgcDA4CgpUaGUgZGF0YSBmb3IgdGhpcyBjb250cmFzdCByZXNpZGVzIGluCnNjbl9jb21iaW5lZF90ZXN0JGRhdGEkcDA4X3Njbl90cmFuc2xhdG9tZSBvciB0aGUgc2FtZSBzbG90IG9mCnNjbl9jb21iaW5lZF90ZXN0X3N2YQoKKiBwMDhfaGV0X3NjbiAtIHAwOF9oZXRfcmV0aW5hKSAtIChwMDhfa29fc2NuIC0gcDA4X2tvX3JldGluYSkKClRodXMsIHRoZSBpbmNsdXNpb25fc2lnIHBvcnRpb25zIHRvIGV4dHJhY3QgYXJlIGZvdW5kIGluOgppbmNsdXNpb25fc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV0sIGFuZCBhcmUgbmFtZWQgZXhhY3RseSBhcyB3cml0dGVuIGFib3ZlIQoKYGBge3J9CnAwOF9oZXRfdnNfa29fdHJhbnNsYXRvbWVfdW5maWx0IDwtIHNjbl9jb21iaW5lZF90ZXN0W1siZGF0YSJdXVtbInAwOF9zY25fdHJhbnNsYXRvbWUiXV0KCm51bV91bmlvbiA8LSB1bmlxdWUoYyhyb3duYW1lcyhpbmNsdXNpb25fc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJwMDhfaGV0X3NjbiJdXSksCiAgICAgICAgICAgICAgICAgICAgICByb3duYW1lcyhpbmNsdXNpb25fc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJwMDhfaGV0X3JldGluYSJdXSkpKQpsZW5ndGgobnVtX3VuaW9uKQpkZW5fdW5pb24gPC0gdW5pcXVlKGMocm93bmFtZXMoaW5jbHVzaW9uX3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1sicDA4X2tvX3NjbiJdXSksCiAgICAgICAgICAgICAgICAgICAgICByb3duYW1lcyhpbmNsdXNpb25fc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJwMDhfa29fcmV0aW5hIl1dKSkpCmxlbmd0aChkZW5fdW5pb24pCmJvdGhfdW5pb24gPC0gdW5pcXVlKGMobnVtX3VuaW9uLCBkZW5fdW5pb24pKQpsZW5ndGgoYm90aF91bmlvbikKYm90aF9pbnRlcl9pZHggPC0gbnVtX3VuaW9uICVpbiUgZGVuX3VuaW9uCmJvdGhfaW50ZXIgPC0gbnVtX3VuaW9uW2JvdGhfaW50ZXJfaWR4XQpsZW5ndGgoYm90aF9pbnRlcikKCmtlZXBlciA8LSBsaXN0KCJwMDhfc2NuX3RyYW5zbGF0b21lIiA9IGMoInAwOGhldCIsICJwMDhrbyIpKQpwMDhfc2NuX3RyYW5zbGF0b21lX3VuaW9uX2ZpbHRlcmVkIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHNjbl90cmFuc2xhdG9tZV9kZSwga2VlcGVycyA9IGtlZXBlciwKICBsYWJlbF9jb2x1bW4gPSBsYWJlbF9jb2x1bW4sCiAgZXhjZWwgPSBnbHVlKCJ0cmFuc2xhdG9tZS9wMDhfc2NuX3RyYW5zbGF0b21lX3VuaW9uX2ZpbHRlcmVkX25vc3ZhLXZ7dmVyfS54bHN4IiksCiAgd2FudGVkX2dlbmVzID0gYm90aF91bmlvbikKcDA4X3Njbl90cmFuc2xhdG9tZV9pbnRlcl9maWx0ZXJlZCA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBzY25fdHJhbnNsYXRvbWVfZGUsIGtlZXBlcnMgPSBrZWVwZXIsCiAgbGFiZWxfY29sdW1uID0gbGFiZWxfY29sdW1uLAogIGV4Y2VsID0gZ2x1ZSgidHJhbnNsYXRvbWUvcDA4X3Njbl90cmFuc2xhdG9tZV9pbnRlcnNlY3RfZmlsdGVyZWRfbm9zdmEtdnt2ZXJ9Lnhsc3giKSwKICB3YW50ZWRfZ2VuZXMgPSBib3RoX2ludGVyKQpwMDhfc2NuX3RyYW5zbGF0b21lX3VuaW9uX2ZpbHRlcmVkX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBzY25fdHJhbnNsYXRvbWVfZGVfc3ZhLCBrZWVwZXJzID0ga2VlcGVyLAogIGxhYmVsX2NvbHVtbiA9IGxhYmVsX2NvbHVtbiwKICBleGNlbCA9IGdsdWUoInRyYW5zbGF0b21lL3AwOF9zY25fdHJhbnNsYXRvbWVfdW5pb25fZmlsdGVyZWRfc3ZhLXZ7dmVyfS54bHN4IiksCiAgd2FudGVkX2dlbmVzID0gYm90aF91bmlvbikKcDA4X3Njbl90cmFuc2xhdG9tZV91bmlvbl9maWx0ZXJlZCA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBzY25fdHJhbnNsYXRvbWVfZGUsIGtlZXBlcnMgPSBrZWVwZXIsCiAgbGFiZWxfY29sdW1uID0gbGFiZWxfY29sdW1uLAogIGV4Y2VsID0gZ2x1ZSgidHJhbnNsYXRvbWUvcDA4X3Njbl90cmFuc2xhdG9tZV9pbnRlcnNlY3RfZmlsdGVyZWRfc3ZhLXZ7dmVyfS54bHN4IiksCiAgd2FudGVkX2dlbmVzID0gYm90aF9pbnRlcikKYGBgCgojIFZlbm4vVXBTZXQgb2YgUmV0aW5hLCBTQ04sIGFuZCBkTEdOIERFIEdlbmVzCgpIZXJlIGlzIGEgc25pcHBldCBmcm9tIFJhc2htaSB3aGljaCBleHByZXNzZXMgbmljZWx5IHRoZSBERS1yZXN1bHQKY29tcGFyaXNvbnMgc2hlIGlzIG1vc3QgaW50ZXJlc3RlZDoKClNpbmNlLCBJIHdhbnQgdG8ga25vdyB0aGUgbnVtYmVyIG9mIERFRyBleHByZXNzZWQgaW4gUmV0aW5hLCBTQ04gYW5kCmRMR04gd2l0aCByZXNwZWN0IHRvIGdlbm90eXBlLCBMb2NhdGlvbiBhbmQgdGltZS4gSSBwcmVwYXJlZCB0aGUgdmVubgpkaWFncmFtIGZvciB0aGVzZSBjb21wYXJpc29uOgoKKiBHZW5vdHlwZTogUDggUmV0IEhldCB2cyBLTywgUDE1IFJldCBIZXQgdnMgS08sIFA4IFNDTiBIZXQgdnMgS08sCiAgICAgICAgICAgIFAxNSBTQ04gSGV0IHZzIEtPLCBQOCBkTEdOIEhldCB2cyBLTywgUDE1IGRMR04gSGV0IHZzIEtPCiogTG9jYXRpb246IFA4X2hldCBSZXQgdnMgU0NOLCBQOF9LTyBSZXQgdnMgU0NOLCBQMTVfaGV0IFJldCB2cyBTQ04sCiAgICAgICAgICAgIFAxNV9LTyBSZXQgdnMgU0NOLCBQOF9oZXQgUmV0IHZzIGRMR04sIFA4X0tPIFJldCB2cyBkTEdOLAogICAgICAgICAgICBQMTVfaGV0IFJldCB2cyBkTEdOLCBQMTVfS08gUmV0IHZzIGRMR04sIFA4X2hldCBTQ04gdnMgZExHTiwKICAgICAgICAgICAgUDhfS08gU0NOIHZzIGRMR04sIFAxNV9oZXQgU0NOIHZzIGRMR04sIFAxNV9LTyBTQ04gdnMgZExHTi4KClNpbmNlIEkgd2FzIGludGVyZXN0ZWQgaW4gdW5kZXJzdGFuZGluZyB0aGUgY2hhbmdlIGluIGxvY2FsIHRyYW5zbGF0b21lCmFjY29yZGluZyB0byBMb2NhdGlvbiBmb3IgZGlmZmVyZW50IGRldmVsb3BtZW50YWwgdGltZSBwb2ludHMgZm9yIEhldAphbmQgS08uIEhlbmNlLCBJIHRyaWVkIHRvIGdlbmVyYXRlIGEgdmVubiBkaWFncmFtIGZvciBMb2NhdGlvbiAoUmV0CmFuZCBTQ04pIGF0IGRldmVsb3BtZW50YWwgdGltZSBwb2ludHMgUDggYW5kIFAxNSBmb3IgZ2Vub3R5cGUgaGV0IGFuZApLTy4gIFNvIHRoZSB2ZW5uIGRpYWdyYW0gLyB1cHNldCBwbG90IHdpbGwgYmUgZm9yIGxvY2F0aW9uIHdoZXJlIHNvbWUKZ2VuZXMgd2lsbCBiZSBzaGFyZWQvdW5pcXVlIGZvciBQOF9SZXRfaGV0LCBQOF9TQ05fSGV0LCBQMTVfUmV0X0hFVCwKUDE1X1NDTl9IRVQuICBXZSBjYW4gcHJlcGFyZSBhbiB1cHNldCBwbG90IGZvciBQOF9SZXRfS08sClA4X1NDTl9LTywgUDE1X1JldF9LTyBhbmQgUDE1X1NDTl9LTyBhbHNvLiBPciBjYW4gZ2VuZXJhdGUgYW4gdXBzZXQKcGxvdCBieSBjb21iaW5pbmcgYm90aCBQOF9SZXRfaGV0LCBQOF9TQ05fSGV0LCBQMTVfUmV0X0hFVCBhbmQKUDE1X1NDTl9IRVQgYW5kIFA4X1JldF9LTywgUDhfU0NOX0tPLCBQMTVfUmV0X0tPIGFuZCBQMTVfU0NOX0tPLgoKT2ssIGxldCB1cyBzZWUgaWYgSSBjYW4gaW1wbGVtZW50IHRoaXMsIHN0YXJ0aW5nIHdpdGggdGhlIGdlbm90eXBlIHF1ZXJ5CgoqIEdlbm90eXBlOiBQOCBSZXQgSGV0IHZzIEtPLCBQMTUgUmV0IEhldCB2cyBLTywgUDggU0NOIEhldCB2cyBLTywKICAgICAgICAgICAgUDE1IFNDTiBIZXQgdnMgS08sIFA4IGRMR04gSGV0IHZzIEtPLCBQMTUgZExHTiBIZXQgdnMgS08KCiMjIGtvIHZzIGhldDsgYWxsIGxvY2F0aW9ucyBhbmQgdGltZXMKCmBgYHtyfQojIyBUaGUgYXBwcm9wcmlhdGUgZGF0YSBzdHJ1Y3R1cmUgaXMgJ2dlbm90eXBlX3RhYmxlcycsCiMjIGFuZCB0aGUgdGFibGVzIG9mIGludGVyZXN0IGFyZToKdGFibGVfbmFtZXMgPC0gYygia2hfcDA4X3JldGluYSIsICJraF9wMTVfcmV0aW5hIiwgImtoX3AwOF9zY24iLAogICAgICAgICAgICAgICAgICJraF9wMTVfc2NuIiwgImtoX3AwOF9kbGduIiwgImtoX3AxNV9kbGduIikKdGFibGVfbmFtZXMgJWluJSBuYW1lcyhnZW5vdHlwZV9zaWcpCm5ld3NpZyA8LSBnZW5vdHlwZV9zaWdbWzFdXQpmb3IgKHNpZyBpbiAyOmxlbmd0aCh0YWJsZV9uYW1lcykpIHsKICBuYW1lIDwtIHRhYmxlX25hbWVzW3NpZ10KICBuZXdzaWdbWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dIDwtIGdlbm90eXBlX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXQogIG5ld3NpZ1tbImRlc2VxIl1dW1siZG93bnMiXV1bW25hbWVdXSA8LSBnZW5vdHlwZV9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1siZG93bnMiXV1bW25hbWVdXQp9Cmdlbm90eXBlX3Vwc2V0ciA8LSB1cHNldHJfc2lnKG5ld3NpZykKZ2Vub3R5cGVfdXBzZXRfd3JpdHRlbiA8LSB3cml0ZV91cHNldF9ncm91cHMoZ2Vub3R5cGVfdXBzZXRyLCBleGNlbCA9ICJleGNlbC9nZW5vdHlwZV91cHNldF9ncm91cHMueGxzeCIpCmdlbm90eXBlX3Vwc2V0cltbImFsbF9wbG90Il1dCnBwKGZpbGUgPSAiaW1hZ2VzL3Rlc3RfZ2Vub3R5cGVfdXBzZXQucGRmIikKcHJpbnQoZ2Vub3R5cGVfdXBzZXRyW1siYWxsX3Bsb3QiXV0pCnBsb3R0ZWQgPC0gZGV2Lm9mZigpCmBgYAoKTm93IGxldCB1cyB0cnkgdGhlIGxvY2F0aW9uLXNwZWNpZmljIGNvbXBhcmlzb25zCgojIyBzY24gdnMgcmV0aW5hLCBwMDgKCmBgYHtyfQojIyBUaGUgYXBwcm9wcmlhdGUgZGF0YSBzdHJ1Y3R1cmUgaXMgJ2dlbm90eXBlX3RhYmxlcycsCiMjIGFuZCB0aGUgdGFibGVzIG9mIGludGVyZXN0IGFyZToKdGFibGVfbmFtZXMgPC0gYygic3JfcDA4X2hldCIsICJzcl9wMDhfa28iKQp0YWJsZV9uYW1lcyAlaW4lIG5hbWVzKGxvY2F0aW9uX3NpZykKbG9jYXRpb25fdXBzZXRfaW5wdXQgPC0gbGlzdCgpCmZpcnN0X3RhYmxlIDwtIHRhYmxlX25hbWVzWzFdCm5ld3NpZyA8LSBsb2NhdGlvbl9zaWdbW2ZpcnN0X3RhYmxlXV0KZm9yIChzaWcgaW4gMjpsZW5ndGgodGFibGVfbmFtZXMpKSB7CiAgbmFtZSA8LSB0YWJsZV9uYW1lc1tzaWddCiAgbmV3c2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXSA8LSBsb2NhdGlvbl9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0KICBuZXdzaWdbWyJkZXNlcSJdXVtbImRvd25zIl1dW1tuYW1lXV0gPC0gbG9jYXRpb25fc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbImRvd25zIl1dW1tuYW1lXV0KfQpsb2NhdGlvbl91cHNldHIgPC0gdXBzZXRyX3NpZyhuZXdzaWcpCmxvY2F0aW9uX3Vwc2V0X3dyaXR0ZW4gPC0gd3JpdGVfdXBzZXRfZ3JvdXBzKGxvY2F0aW9uX3Vwc2V0ciwgZXhjZWwgPSAiZXhjZWwvc3JfcDA4X2hldGtvX3Vwc2V0X2dyb3Vwcy54bHN4IikKbG9jYXRpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0KcHAoZmlsZSA9ICJpbWFnZXMvdGVzdF9sb2NhdGlvbl9zcl9wMDhfaGV0a29fdXBzZXQucGRmIikKcHJpbnQobG9jYXRpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0pCmRldi5vZmYoKQpgYGAKCkkgYW0gcmVhc29uYWJseSBjZXJ0YWluIHRoYXQgUmFzaG1pIHdvdWxkIGxpa2UgYSB0YWJsZSBvZiB0aGUgZ2VuZXMKc2hhcmVkIGFtb25nIGluY3JlYXNlZCBzY24ga28gYW5kIGhldCBpbiB0aGUgYWJvdmUgcGxvdCBhbG9uZyB3aXRoIHRoZQppbmNyZWFzZWQgcmV0aW5hIChlLmcuIHRoZSAyNjkgYW5kIDEwMyBnZW5lIHNldHMpLgoKIyMgc2NuIHZzIHJldGluYSwgcDE1CgpgYGB7cn0KdGFibGVfbmFtZXMgPC0gYygic3JfcDE1X2hldCIsICJzcl9wMTVfa28iKQp0YWJsZV9uYW1lcyAlaW4lIG5hbWVzKGxvY2F0aW9uX3NpZykKbG9jYXRpb25fdXBzZXRfaW5wdXQgPC0gbGlzdCgpCmZpcnN0X3RhYmxlIDwtIHRhYmxlX25hbWVzWzFdCm5ld3NpZyA8LSBsb2NhdGlvbl9zaWdbW2ZpcnN0X3RhYmxlXV0KZm9yIChzaWcgaW4gMjpsZW5ndGgodGFibGVfbmFtZXMpKSB7CiAgbmFtZSA8LSB0YWJsZV9uYW1lc1tzaWddCiAgbmV3c2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXSA8LSBsb2NhdGlvbl9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0KICBuZXdzaWdbWyJkZXNlcSJdXVtbImRvd25zIl1dW1tuYW1lXV0gPC0gbG9jYXRpb25fc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbImRvd25zIl1dW1tuYW1lXV0KfQpsb2NhdGlvbl91cHNldHIgPC0gdXBzZXRyX3NpZyhuZXdzaWcpCmxvY2F0aW9uX3Vwc2V0X3dyaXR0ZW4gPC0gd3JpdGVfdXBzZXRfZ3JvdXBzKGxvY2F0aW9uX3Vwc2V0ciwgZXhjZWwgPSAiZXhjZWwvc3JfcDE1X2hldGtvX3Vwc2V0X2dyb3Vwcy54bHN4IikKbG9jYXRpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0KcHJpbnQoc2NuX3JldGluYV9wMTVfdXBzZXRfcmVzdWx0KQpwcChmaWxlID0gImltYWdlcy90ZXN0X2xvY2F0aW9uX3NyX3AxNV9oZXRrb191cHNldC5wZGYiKQpwcmludChsb2NhdGlvbl91cHNldHJbWyJhbGxfcGxvdCJdXSkKZGV2Lm9mZigpCmBgYAoKIyMgZGxnbiB2cyByZXRpbmEsIHAwOAoKYGBge3J9CiMjIFRoZSBhcHByb3ByaWF0ZSBkYXRhIHN0cnVjdHVyZSBpcyAnZ2Vub3R5cGVfdGFibGVzJywKIyMgYW5kIHRoZSB0YWJsZXMgb2YgaW50ZXJlc3QgYXJlOgp0YWJsZV9uYW1lcyA8LSBjKCJkcl9wMDhfaGV0IiwgImRyX3AwOF9rbyIpCmxvY2F0aW9uX3Vwc2V0X2lucHV0IDwtIGxpc3QoKQpmaXJzdF90YWJsZSA8LSB0YWJsZV9uYW1lc1sxXQpuZXdzaWcgPC0gbG9jYXRpb25fc2lnW1tmaXJzdF90YWJsZV1dCmZvciAoc2lnIGluIDI6bGVuZ3RoKHRhYmxlX25hbWVzKSkgewogIG5hbWUgPC0gdGFibGVfbmFtZXNbc2lnXQogIG5ld3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0gPC0gbG9jYXRpb25fc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dCiAgbmV3c2lnW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dIDwtIGxvY2F0aW9uX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dCn0KbG9jYXRpb25fdXBzZXRyIDwtIHVwc2V0cl9zaWcobmV3c2lnKQpsb2NhdGlvbl91cHNldF93cml0dGVuIDwtIHdyaXRlX3Vwc2V0X2dyb3Vwcyhsb2NhdGlvbl91cHNldHIsIGV4Y2VsID0gImV4Y2VsL2RyX3AwOF9oZXRrb191cHNldF9ncm91cHMueGxzeCIpCmxvY2F0aW9uX3Vwc2V0cltbImFsbF9wbG90Il1dCnBwKGZpbGUgPSAiaW1hZ2VzL3Rlc3RfbG9jYXRpb25fZHJfcDA4X2hldGtvX3Vwc2V0LnBkZiIpCnByaW50KGxvY2F0aW9uX3Vwc2V0cltbImFsbF9wbG90Il1dKQpkZXYub2ZmKCkKYGBgCgojIyBkbGduIHZzIHJldGluYSwgcDE1CgpgYGB7cn0KIyMgVGhlIGFwcHJvcHJpYXRlIGRhdGEgc3RydWN0dXJlIGlzICdnZW5vdHlwZV90YWJsZXMnLAojIyBhbmQgdGhlIHRhYmxlcyBvZiBpbnRlcmVzdCBhcmU6CnRhYmxlX25hbWVzIDwtIGMoImRyX3AxNV9oZXQiLCAiZHJfcDE1X2tvIikKbG9jYXRpb25fdXBzZXRfaW5wdXQgPC0gbGlzdCgpCmZpcnN0X3RhYmxlIDwtIHRhYmxlX25hbWVzWzFdCm5ld3NpZyA8LSBsb2NhdGlvbl9zaWdbW2ZpcnN0X3RhYmxlXV0KZm9yIChzaWcgaW4gMjpsZW5ndGgodGFibGVfbmFtZXMpKSB7CiAgbmFtZSA8LSB0YWJsZV9uYW1lc1tzaWddCiAgbmV3c2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXSA8LSBsb2NhdGlvbl9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0KICBuZXdzaWdbWyJkZXNlcSJdXVtbImRvd25zIl1dW1tuYW1lXV0gPC0gbG9jYXRpb25fc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbImRvd25zIl1dW1tuYW1lXV0KfQpsb2NhdGlvbl91cHNldHIgPC0gdXBzZXRyX3NpZyhuZXdzaWcpCmxvY2F0aW9uX3Vwc2V0X3dyaXR0ZW4gPC0gd3JpdGVfdXBzZXRfZ3JvdXBzKGxvY2F0aW9uX3Vwc2V0ciwgZXhjZWwgPSAiZXhjZWwvZHJfcDE1X2hldGtvX3Vwc2V0X2dyb3Vwcy54bHN4IikKbG9jYXRpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0KcHAoZmlsZSA9ICJpbWFnZXMvdGVzdF9sb2NhdGlvbl9kcl9wMTVfaGV0a29fdXBzZXQucGRmIikKcHJpbnQobG9jYXRpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0pCmRldi5vZmYoKQpgYGAKCiMjIGRsZ24gdnMgc2NuLCBwMDgKCmBgYHtyfQp0YWJsZV9uYW1lcyA8LSBjKCJkc19wMDhfaGV0IiwgImRzX3AwOF9rbyIpCmxvY2F0aW9uX3Vwc2V0X2lucHV0IDwtIGxpc3QoKQpmaXJzdF90YWJsZSA8LSB0YWJsZV9uYW1lc1sxXQpuZXdzaWcgPC0gbG9jYXRpb25fc2lnW1tmaXJzdF90YWJsZV1dCmZvciAoc2lnIGluIDI6bGVuZ3RoKHRhYmxlX25hbWVzKSkgewogIG5hbWUgPC0gdGFibGVfbmFtZXNbc2lnXQogIG5ld3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1tuYW1lXV0gPC0gbG9jYXRpb25fc2lnW1tuYW1lXV1bWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dCiAgbmV3c2lnW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dIDwtIGxvY2F0aW9uX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbbmFtZV1dCn0KbG9jYXRpb25fdXBzZXRyIDwtIHVwc2V0cl9zaWcobmV3c2lnKQpsb2NhdGlvbl91cHNldF93cml0dGVuIDwtIHdyaXRlX3Vwc2V0X2dyb3Vwcyhsb2NhdGlvbl91cHNldHIsIGV4Y2VsID0gImV4Y2VsL2RzX3AwOF9oZXRrb191cHNldF9ncm91cHMueGxzeCIpCmxvY2F0aW9uX3Vwc2V0cltbImFsbF9wbG90Il1dCnBwKGZpbGUgPSAiaW1hZ2VzL3Rlc3RfbG9jYXRpb25fZHNfcDA4X2hldGtvX3Vwc2V0LnBkZiIpCnByaW50KGxvY2F0aW9uX3Vwc2V0cltbImFsbF9wbG90Il1dKQpkZXYub2ZmKCkKYGBgCgojIyBkbGduIHZzIHNjbiwgcDE1CgpgYGB7cn0KdGFibGVfbmFtZXMgPC0gYygiZHNfcDE1X2hldCIsICJkc19wMTVfa28iKQpsb2NhdGlvbl91cHNldF9pbnB1dCA8LSBsaXN0KCkKZmlyc3RfdGFibGUgPC0gdGFibGVfbmFtZXNbMV0KbmV3c2lnIDwtIGxvY2F0aW9uX3NpZ1tbZmlyc3RfdGFibGVdXQpmb3IgKHNpZyBpbiAyOmxlbmd0aCh0YWJsZV9uYW1lcykpIHsKICBuYW1lIDwtIHRhYmxlX25hbWVzW3NpZ10KICBuZXdzaWdbWyJkZXNlcSJdXVtbInVwcyJdXVtbbmFtZV1dIDwtIGxvY2F0aW9uX3NpZ1tbbmFtZV1dW1siZGVzZXEiXV1bWyJ1cHMiXV1bW25hbWVdXQogIG5ld3NpZ1tbImRlc2VxIl1dW1siZG93bnMiXV1bW25hbWVdXSA8LSBsb2NhdGlvbl9zaWdbW25hbWVdXVtbImRlc2VxIl1dW1siZG93bnMiXV1bW25hbWVdXQp9CmxvY2F0aW9uX3Vwc2V0ciA8LSB1cHNldHJfc2lnKG5ld3NpZykKbG9jYXRpb25fdXBzZXRfd3JpdHRlbiA8LSB3cml0ZV91cHNldF9ncm91cHMobG9jYXRpb25fdXBzZXRyLCBleGNlbCA9ICJleGNlbC9kc19wMTVfaGV0a29fdXBzZXRfZ3JvdXBzLnhsc3giKQpsb2NhdGlvbl91cHNldHJbWyJhbGxfcGxvdCJdXQpwcChmaWxlID0gImltYWdlcy90ZXN0X2xvY2F0aW9uX2RzX3AxNV9oZXRrb191cHNldC5wZGYiKQpwcmludChsb2NhdGlvbl91cHNldHJbWyJhbGxfcGxvdCJdXSkKZGV2Lm9mZigpCmBgYAoKIyBTaGFyZWQgYW5kIHVuaXF1ZSBnZW5lIHNldHMgYWNyb3NzIHgvd3QKCkluIHRoaXMgYmxvY2sgSSB3YW50IHRvIGZpbmQgdGhlIHVuaXF1ZSBhbmQgc2hhcmVkIGdlbmVzIGJldHdlZW46CgoxLiAgc2NuIHA4IGhldC93dCBhbmQgcmV0aW5hIHA4IGhldC93dDogaHdwMDhzY25pbmMsIGh3cDA4cmV0aW5jLCBod3AwOHNjbmRlYywgaHdwMDhyZXRkZWMKMi4gIHNjbiBwMTUgaGV0L3d0IGFuZCByZXRpbmEgcDE1IGhldC93dDogaHdwMTVzY25pbmMsIGh3cDE1cmV0aW5jLCBod3AxNXNjbmRlYywgaHdwMTVyZXRkZWMKMy4gICMxIGFuZCAjMiB0b2dldGhlcjogIDggY2F0Z29yaWVzIGFib3ZlCjQuICBzY24gcDgga28vd3QgYW5kIHJldGluYSBwOCBrby93dAo1LiAgc2NuIHAxNSBrby93dCBhbmQgcmV0aW5hIHAxNSBrby93dAo2LiAgIzQgYW5kICM1IHRvZ2V0aGVyCgpUaGUgY29tcGFyaXNvbnMgb2YgaGV0L3d0IGFyZSBmb3VuZCBpbiB0aGUgJ2luY2x1c2lvbl9zaWcnIGRhdGFzZXQ7CmJlY2F1c2UgdGhleSBhcmUgcHJvdmlkaW5nIG91ciBjdXRvZmZzIGZvciBub25zcGVjaWZpYyBiaW5kaW5nLgoKIyMgTnVtYmVyIDEgYWJvdmU6IHAwOF9oZXQgdnMgd3QgZm9yIHNjbiBhbmQgcmV0aW5hLgoKYGBge3J9CnRhYmxlX25hbWVzIDwtIGMoInAwOF9oZXRfc2NuIiwgInAwOF9oZXRfcmV0aW5hIikKaW5jbHVzaW9uX3Vwc2V0ciA8LSB1cHNldHJfc2lnKGluY2x1c2lvbl9zaWcsIGNvbnRyYXN0cyA9IHRhYmxlX25hbWVzKQppbmNsdXNpb25fdXBzZXRfd3JpdHRlbiA8LSB3cml0ZV91cHNldF9ncm91cHMoaW5jbHVzaW9uX3Vwc2V0ciwgZXhjZWwgPSAiZXhjZWwvcnNfcDA4X2hldF9pbmNsdXNpb25fdXBzZXRfZ3JvdXBzLnhsc3giKQppbmNsdXNpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0KcHAoZmlsZSA9ICJpbWFnZXMvaW5jbHVzaW9uX3NyX3AwOF91cHNldC5wZGYiKQpwcmludChpbmNsdXNpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0pCmRldi5vZmYoKQpgYGAKCiMjIE51bWJlciAyIGFib3ZlOiBwMTVfaGV0IHZzIHd0IGZvciBzY24gYW5kIHJldGluYS4KCmBgYHtyfQp0YWJsZV9uYW1lcyA8LSBjKCJwMTVfaGV0X3NjbiIsICJwMTVfaGV0X3JldGluYSIpCmluY2x1c2lvbl91cHNldHIgPC0gdXBzZXRyX3NpZyhpbmNsdXNpb25fc2lnLCBjb250cmFzdHMgPSB0YWJsZV9uYW1lcykKaW5jbHVzaW9uX3Vwc2V0X3dyaXR0ZW4gPC0gd3JpdGVfdXBzZXRfZ3JvdXBzKGluY2x1c2lvbl91cHNldHIsIGV4Y2VsID0gImV4Y2VsL3JzX3AxNV9oZXRfaW5jbHVzaW9uX3Vwc2V0X2dyb3Vwcy54bHN4IikKaW5jbHVzaW9uX3Vwc2V0cltbImFsbF9wbG90Il1dCnBwKGZpbGUgPSAiaW1hZ2VzL2luY2x1c2lvbl9zcl9wMTVfdXBzZXQucGRmIikKcHJpbnQoaW5jbHVzaW9uX3Vwc2V0cltbImFsbF9wbG90Il1dKQpkZXYub2ZmKCkKYGBgCgojIyBOdW1iZXIgMyBhYm92ZTogY29tYmluYXRpb24gb2YgIzEgYW5kICMyCgpgYGB7cn0KdGFibGVfbmFtZXMgPC0gYygicDA4X2hldF9zY24iLCAicDA4X2hldF9yZXRpbmEiLCAicDE1X2hldF9zY24iLCAicDE1X2hldF9yZXRpbmEiKQppbmNsdXNpb25fdXBzZXRyIDwtIHVwc2V0cl9zaWcoaW5jbHVzaW9uX3NpZywgY29udHJhc3RzID0gdGFibGVfbmFtZXMpCmluY2x1c2lvbl91cHNldF93cml0dGVuIDwtIHdyaXRlX3Vwc2V0X2dyb3VwcyhpbmNsdXNpb25fdXBzZXRyLCBleGNlbCA9ICJleGNlbC9yc19wMDhwMTVfaGV0X2luY2x1c2lvbl91cHNldF9ncm91cHMueGxzeCIpCmluY2x1c2lvbl91cHNldHJbWyJhbGxfcGxvdCJdXQpwcChmaWxlID0gImltYWdlcy9pbmNsdXNpb25fc3JfcDA4cDE1X3Vwc2V0LnBkZiIpCnByaW50KGluY2x1c2lvbl91cHNldHJbWyJhbGxfcGxvdCJdXSkKZGV2Lm9mZigpCmBgYAoKIyMgTnVtYmVyIDQgYWJvdmUgcDA4X2tvIHZzIHd0IGZvciBzY24gYW5kIHJldGluYS4KCmBgYHtyfQp0YWJsZV9uYW1lcyA8LSBjKCJwMDhfa29fc2NuIiwgInAwOF9rb19yZXRpbmEiKQppbmNsdXNpb25fdXBzZXRyIDwtIHVwc2V0cl9zaWcoaW5jbHVzaW9uX3NpZywgY29udHJhc3RzID0gdGFibGVfbmFtZXMpCmluY2x1c2lvbl91cHNldF93cml0dGVuIDwtIHdyaXRlX3Vwc2V0X2dyb3VwcyhpbmNsdXNpb25fdXBzZXRyLCBleGNlbCA9ICJleGNlbC9yc19wMDhfa29faW5jbHVzaW9uX3Vwc2V0X2dyb3Vwcy54bHN4IikKaW5jbHVzaW9uX3Vwc2V0cltbImFsbF9wbG90Il1dCnBwKGZpbGUgPSAiaW1hZ2VzL2luY2x1c2lvbl9zcl9wMDhfa29fdXBzZXQucGRmIikKcHJpbnQoaW5jbHVzaW9uX3Vwc2V0cltbImFsbF9wbG90Il1dKQpkZXYub2ZmKCkKYGBgCgojIyBOdW1iZXIgNSBhYm92ZSBwMTVfa28gdnMgd3QgZm9yIHNjbiBhbmQgcmV0aW5hLgoKYGBge3J9CnRhYmxlX25hbWVzIDwtIGMoInAxNV9rb19zY24iLCAicDE1X2tvX3JldGluYSIpCmluY2x1c2lvbl91cHNldHIgPC0gdXBzZXRyX3NpZyhpbmNsdXNpb25fc2lnLCBjb250cmFzdHMgPSB0YWJsZV9uYW1lcykKaW5jbHVzaW9uX3Vwc2V0X3dyaXR0ZW4gPC0gd3JpdGVfdXBzZXRfZ3JvdXBzKGluY2x1c2lvbl91cHNldHIsIGV4Y2VsID0gImV4Y2VsL3JzX3AxNV9rb19pbmNsdXNpb25fdXBzZXRfZ3JvdXBzLnhsc3giKQppbmNsdXNpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0KcHAoZmlsZSA9ICJpbWFnZXMvaW5jbHVzaW9uX3NyX3AxNV9rb191cHNldC5wZGYiKQpwcmludChpbmNsdXNpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0pCmRldi5vZmYoKQpgYGAKCiMjIE51bWJlciA2IGFib3ZlOiBDb21iaW5pbmcgIzQgYW5kICM1CgpgYGB7cn0KdGFibGVfbmFtZXMgPC0gYygicDA4X2tvX3NjbiIsICJwMDhfa29fcmV0aW5hIiwgInAxNV9rb19zY24iLCAicDE1X2tvX3JldGluYSIpCmluY2x1c2lvbl91cHNldHIgPC0gdXBzZXRyX3NpZyhpbmNsdXNpb25fc2lnLCBjb250cmFzdHMgPSB0YWJsZV9uYW1lcykKaW5jbHVzaW9uX3Vwc2V0X3dyaXR0ZW4gPC0gd3JpdGVfdXBzZXRfZ3JvdXBzKGluY2x1c2lvbl91cHNldHIsIGV4Y2VsID0gImV4Y2VsL3JzX3AwOHAxNV9rb19pbmNsdXNpb25fdXBzZXRfZ3JvdXBzLnhsc3giKQppbmNsdXNpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0KcHAoZmlsZSA9ICJpbWFnZXMvaW5jbHVzaW9uX3NyX3AwOHAxNV9rb191cHNldC5wZGYiKQpwcmludChpbmNsdXNpb25fdXBzZXRyW1siYWxsX3Bsb3QiXV0pCmRldi5vZmYoKQpgYGAKCiMgR1NWQQoKYGBge3J9Cm1zaWdkYiA8LSAicmVmZXJlbmNlL21zaWdkYl92MjAyNC4xLk1tLmRiIgppZiAoZmlsZS5leGlzdHMobXNpZ2RiKSkgewogIHYzX2hfZ3N2YSA8LSBzaW1wbGVfZ3N2YSh2M19wYWlyd2lzZV9pbnB1dCwgb3JnZGIgPSAib3JnLk1tLmVnLmRiIiwgc2lnbmF0dXJlX2NhdGVnb3J5ID0gIm1oIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmF0dXJlcyA9IG1zaWdkYiwgaWRfc291cmNlID0gImZkYXRhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWlyZWRfaWQgPSAibWdpX3N5bWJvbCIpCiAgdjNfaF9nc3ZhCgogIHYzX2hfZ3N2YV9zaWcgPC0gZ2V0X3NpZ19nc3ZhX2NhdGVnb3JpZXMoCiAgICB2M19oX2dzdmEsIGV4Y2VsID0gImV4Y2VsL2dzdmFfc2lnX2hhbGxtYXJrX2NhdGVnb3JpZXMueGxzeCIpCiAgdjNfaF9nc3ZhX3NpZwoKICB2M19tMV9nc3ZhIDwtIHNpbXBsZV9nc3ZhKHYzX3BhaXJ3aXNlX2lucHV0LCBvcmdkYiA9ICJvcmcuTW0uZWcuZGIiLCBzaWduYXR1cmVfY2F0ZWdvcnkgPSAibTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmF0dXJlcyA9IG1zaWdkYiwgaWRfc291cmNlID0gImZkYXRhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkX2lkID0gIm1naV9zeW1ib2wiKQogIHYzX20xX2dzdmEKICB2M19tMV9nc3ZhX3NpZyA8LSBnZXRfc2lnX2dzdmFfY2F0ZWdvcmllcygKICAgIHYzX20xX2dzdmEsIGV4Y2VsID0gImV4Y2VsL2dzdmFfc2lnX3Bvc2l0aW9uYWxfY2F0ZWdvcmllcy54bHN4IikKICB2M19tMV9nc3ZhX3NpZwoKICB2M19tMl9nc3ZhIDwtIHNpbXBsZV9nc3ZhKHYzX3BhaXJ3aXNlX2lucHV0LCBvcmdkYiA9ICJvcmcuTW0uZWcuZGIiLCBzaWduYXR1cmVfY2F0ZWdvcnkgPSAibTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmF0dXJlcyA9IG1zaWdkYiwgaWRfc291cmNlID0gImZkYXRhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkX2lkID0gIm1naV9zeW1ib2wiKQogIHYzX20yX2dzdmEKICB2M19tMl9nc3ZhX3NpZyA8LSBnZXRfc2lnX2dzdmFfY2F0ZWdvcmllcygKICAgIHYzX20yX2dzdmEsIGV4Y2VsID0gImV4Y2VsL2dzdmFfc2lnX2N1cmF0ZWRfY2F0ZWdvcmllcy54bHN4IikKICB2M19tMl9nc3ZhX3NpZwoKICB2M19tM19nc3ZhIDwtIHNpbXBsZV9nc3ZhKHYzX3BhaXJ3aXNlX2lucHV0LCBvcmdkYiA9ICJvcmcuTW0uZWcuZGIiLCBzaWduYXR1cmVfY2F0ZWdvcnkgPSAibTMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmF0dXJlcyA9IG1zaWdkYiwgaWRfc291cmNlID0gImZkYXRhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkX2lkID0gIm1naV9zeW1ib2wiKQogIHYzX20zX2dzdmEKICB2M19tM19nc3ZhX3NpZyA8LSBnZXRfc2lnX2dzdmFfY2F0ZWdvcmllcygKICAgIHYzX20zX2dzdmEsIGV4Y2VsID0gImV4Y2VsL2dzdmFfc2lnX3JlZ3VsYXRvcnlfY2F0ZWdvcmllcy54bHN4IikKICB2M19tM19nc3ZhX3NpZwoKICB2M19tNV9nc3ZhIDwtIHNpbXBsZV9nc3ZhKHYzX3BhaXJ3aXNlX2lucHV0LCBvcmdkYiA9ICJvcmcuTW0uZWcuZGIiLCBzaWduYXR1cmVfY2F0ZWdvcnkgPSAibTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmF0dXJlcyA9IG1zaWdkYiwgaWRfc291cmNlID0gImZkYXRhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcXVpcmVkX2lkID0gIm1naV9zeW1ib2wiKQogIHYzX201X2dzdmEKICB2M19tNV9nc3ZhX3NpZyA8LSBnZXRfc2lnX2dzdmFfY2F0ZWdvcmllcygKICAgIHYzX201X2dzdmEsIGV4Y2VsID0gImV4Y2VsL2dzdmFfc2lnX29udG9sb2d5X2NhdGVnb3JpZXMueGxzeCIpCiAgdjNfbTVfZ3N2YV9zaWcKCiAgdjNfbThfZ3N2YSA8LSBzaW1wbGVfZ3N2YSh2M19wYWlyd2lzZV9pbnB1dCwgb3JnZGIgPSAib3JnLk1tLmVnLmRiIiwgc2lnbmF0dXJlX2NhdGVnb3J5ID0gIm04IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ25hdHVyZXMgPSBtc2lnZGIsIGlkX3NvdXJjZSA9ICJmZGF0YSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXF1aXJlZF9pZCA9ICJtZ2lfc3ltYm9sIikKICB2M19tOF9nc3ZhCiAgdjNfbThfZ3N2YV9zaWcgPC0gZ2V0X3NpZ19nc3ZhX2NhdGVnb3JpZXMoCiAgICB2M19tOF9nc3ZhLCBleGNlbCA9ICJleGNlbC9nc3ZhX3NpZ19jZWxsdHlwZV9jYXRlZ29yaWVzLnhsc3giKQogIHYzX204X2dzdmFfc2lnCn0KYGBgCgojIEdTRUEgaW1hZ2VzCgpVcCBhYm92ZSBJIGNyZWF0ZWQgYSBmYWlybHkgbGFyZ2Ugc2V0IG9mIGVucmljaG1lbnQvR1NFQSBhbmFseXNlcy4KTGV0IHVzIHB1bGwgc29tZSBvZiB0aGUgbW9zdCBpbnRlcmVzdGluZyByZXN1bHRzIGhlcmUgYW5kIGxvb2sgYXQKdGhlbS4KCkhlcmUgYXJlIHRoZSBzcGVjaWZpYyBxdWVyaWVzIGZyb20gUmFzaG1pOgoKKiBHZW5vdHlwZSAoaGV0IHZzIGtvKToKICAqIFA4IGhldCBhbmQga28gZm9yIFJldAogICogU0NOIChQOCBoZXQgdnMgS08gU0NOCiAgKiBQOCBoZXQgdnMgS08gUmV0KQogICogUDE1IGhldCBhbmQga28gZm9yIFJldAoqIExvY2F0aW9uIChzb21hbCB2cyBheG9uYWwpOgogICogU1JfUDA4X0tPCiAgKiBTUl9QMDhfSGV0CiAgKiBTUl9QMTVfS08KICAqIFNSX1AxNV9IZXQKKiBUaW1lKHA4dnMgcDE1KToKICAqIHRfaGV0X1JldF8gcG84LXAxNQogICogdF9LT19SZXRfQyBwbzgtcDE1CiAgKiB0X2hldF9TQ05fcG84LXAxNQogICogdF9LT19TQ05fcG84LXAxNQoKIyMgR2Vub3R5cGUKCkxldCB1cyB0YWtlIGEgbW9tZW50IGFuZCBzZWUgZm9yIHdoaWNoIGNvbnRyYXN0cyBJIGFjcXVpcmVkIHJlc3VsdHM6CgpJIG5lZWQgdG8gbWFrZSBhIGxpdHRsZSBzdW1tYXJ5IGZvciBjbHVzdGVycHJvZmlsZXIgdG9vIHNvIHRoYXQgSSBjYW4gZWFzaWx5IHNlZSBob3cgbWFueSBoaXRzIHRoZXJlCmFyZSBmb3IgZWFjaCBjb250cmFzdC4KCmBgYHtyfQpzdW1tYXJ5KGdlbm90eXBlX2Z1bGxfZ3ApCmZvciAoaSBpbiBuYW1lcyhnZW5vdHlwZV9mdWxsX2dwKSkgewogIHByaW50KGkpCiAgcHJpbnQoZ2Vub3R5cGVfZnVsbF9ncFtbaV1dW1sibnVtX2hpdHMiXV0pCn0Kc3VtbWFyeShnZW5vdHlwZV9mdWxsX2NwKQpmb3IgKGkgaW4gbmFtZXMoZ2Vub3R5cGVfZnVsbF9jcCkpIHsKICBwcmludChpKQogIHByaW50KG5yb3coZ2Vub3R5cGVfZnVsbF9jcFtbaV1dW1siZ3NlX2dvIl1dKSkKfQpgYGAKCiMjIyBwOCBoZXQva28gZm9yIHJldGluYToKClRoaXMgY29udHJhc3QsIGV2ZW4gYmVmb3JlIGZpbHRlcmluZyBhd2F5IHRoZSBoaWdoLXd0IGdlbmVzLCBvbmx5IGhhcyA4IGdlbmVzIGluIHRoZSBzZXQgb2YgdXAgYW5kCmRvd24gZ2VuZXMgY29tYmluZWQuICBBcyBhIHJlc3VsdCwgbXkgZnVuY3Rpb24gd2hpY2ggcGVyZm9ybXMgZ1Byb2ZpbGVyL2NsdXN0ZXJQcm9maWxlciBza2lwcyBpdCwKYW5kIGFsc28gc2tpcHMgdGhlIHAxNSBoZXQva28gZm9yIHJldGluYSBzYW1wbGVzLgoKIyMjIHA4IGhldC9rbyBmb3Igc2NuOgoKVGhpcyBoYXMgYSBidW5jaCBtb3JlIGdlbmVzOiA1MSB1cCBhbmQgMTI4IGRvd24uClVuZm9ydHVuYXRlbHksIGdQcm9maWxlciBzZWVzIG5vIHNpZ25pZmljYW50IG92ZXItcmVwcmVzZW50YXRpb24gaW4gdGhlIHVwIGNhdGVnb3J5IG9mIGdlbmVzLgpUaGUgZG93biBjYXRlZ29yeSBoYXMKClRoZSB1cC9kb3duIHNldHMgZnJvbSBjbHVzdGVyUHJvZmlsZXIgaGF2ZSBlbnJpY2hfZ28sIGdzZV9nbywgYW5kIGVucmljaF9vYmplY3RzIHRvIGxvb2sgYXQuCgpgYGB7cn0KZ2Vub3R5cGVfZnVsbF9ncCRraF9wMDhfc2NuX3VwJG51bV9oaXRzCmdlbm90eXBlX2Z1bGxfZ3Aka2hfcDA4X3Njbl9kb3duJG51bV9oaXRzCgpwbG90cyA8LSBwbG90X2VucmljaHJlc3VsdChnZW5vdHlwZV9mdWxsX2dwJGtoX3AwOF9zY25fZG93bltbIkJQX2VucmljaCJdXSkKcGxvdHNbWyJkb3QiXV0KcGxvdHNbWyJ0cmVlIl1dCmBgYAoKUGVyaGFwcyBJIHNob3VsZCBqdXN0IGFzayB0aGUgcXVlc3Rpb246IGZvciB3aGljaCBjYXRlZ29yaWVzIGRpZCBJIGdldCByZXN1bHRzIGJhY2s/CgpgYGB7cn0Kc3VtbWFyeShnZW5vdHlwZV9mdWxsX2dwKQpgYGAKCmtoX3AwOF9kbGduX3VwOiBObyBzaWduaWZpY2FudCBnUHJvZmlsZXIgcmVzdWx0cy4Ka2hfcDE1X2RsZ25fdXA6IFNpZ25pZmljYW50IEJQLCBIUCwgS0VHRywgTUYsIFJFQUMsIFRGCmtoX3AwOF9zY25fdXA6IE5vIHNpZ25pZmljYW50IGdQcm9maWxlciByZXN1bHRzLgpraF9wMDhfc2NuX2Rvd246IFNpZ25pZmljYW50IEJQLCBNaVJOQSwgTUYsIFRGCmtoX3AxNV9zY25fZG93bjogU2lnbmlmaWNhbnQgQlAsIE1GCgoKYGBge3J9CnBsb3RzIDwtIHBsb3RfZW5yaWNocmVzdWx0KGdlbm90eXBlX2Z1bGxfZ3BbWyJraF9wMTVfZGxnbl91cCJdXVtbIkJQX2VucmljaCJdXSkKcGxvdHNbWyJkb3QiXV0KYGBgCgojIyBMb2NhdGlvbgoKIyMjIFNjbiB2cyByZXRpbmEga28sIHAwOAoKYGBge3J9CgpwbG90cyA8LSBwbG90X2VucmljaHJlc3VsdChsb2NhdGlvbl9ncFtbInNyX3AwOF9rbyJdXVtbInNyX3AwOF9rb191cCJdXVtbIkJQX2VucmljaCJdXSkKcGxvdHNbWyJkb3QiXV0KCnBsb3RzIDwtIHBsb3RfZW5yaWNocmVzdWx0KGxvY2F0aW9uX2dwW1sic3JfcDA4X2tvIl1dW1sic3JfcDA4X2tvX2Rvd24iXV1bWyJCUF9lbnJpY2giXV0pCnBsb3RzW1siZG90Il1dCmBgYAoKIyMjIHNjbiB2cyByZXRpbmEsIGhldCwgcDA4CgpFbnJpY2hlZCBncm91cHM6IEJQLCBLRUdHLCBNRiwgVEYsIENDCgpgYGB7cn0Kc3VtbWFyeShsb2NhdGlvbl9ncFtbInNyX3AwOF9oZXQiXV1bWyJzcl9wMDhfaGV0X3VwIl1dKQpwbG90cyA8LSBwbG90X2VucmljaHJlc3VsdChsb2NhdGlvbl9ncFtbInNyX3AwOF9oZXQiXV1bWyJzcl9wMDhfaGV0X3VwIl1dW1siQlBfZW5yaWNoIl1dKQpwbG90c1tbImRvdCJdXQoKcGxvdHMgPC0gcGxvdF9lbnJpY2hyZXN1bHQobG9jYXRpb25fZ3BbWyJzcl9wMDhfaGV0Il1dW1sic3JfcDA4X2hldF91cCJdXVtbIkNDX2VucmljaCJdXSkKcGxvdHNbWyJkb3QiXV0KCnBsb3RzIDwtIHBsb3RfZW5yaWNocmVzdWx0KGxvY2F0aW9uX2dwW1sic3JfcDA4X2hldCJdXVtbInNyX3AwOF9oZXRfZG93biJdXVtbIkJQX2VucmljaCJdXSkKcGxvdHNbWyJkb3QiXV0KCnNyX3AwOF9oZXRfdG9wbl9nc2VhIDwtIHBsb3RfdG9wbl9nc2VhKGxvY2F0aW9uX2NwW1siIl1dKQoKYGBgCgojIyMgU2NuIHZzIHJldGluYSBrbywgcDE1CgpgYGB7cn0KcGxvdHMgPC0gcGxvdF9lbnJpY2hyZXN1bHQobG9jYXRpb25fZ3BbWyJzcl9wMTVfa28iXV1bWyJzcl9wMTVfa29fdXAiXV1bWyJCUF9lbnJpY2giXV0pCnBsb3RzW1siZG90Il1dCgpwbG90cyA8LSBwbG90X2VucmljaHJlc3VsdChsb2NhdGlvbl9ncFtbInNyX3AxNV9rbyJdXVtbInNyX3AxNV9rb19kb3duIl1dW1siQlBfZW5yaWNoIl1dKQpwbG90c1tbImRvdCJdXQpgYGAKCiMjIyBzY24gdnMgcmV0aW5hLCBoZXQsIHAxNQoKYGBge3J9CnBsb3RzIDwtIHBsb3RfZW5yaWNocmVzdWx0KGxvY2F0aW9uX2dwW1sic3JfcDE1X2hldCJdXVtbInNyX3AxNV9oZXRfdXAiXV1bWyJCUF9lbnJpY2giXV0pCnBsb3RzW1siZG90Il1dCgpwbG90cyA8LSBwbG90X2VucmljaHJlc3VsdChsb2NhdGlvbl9ncFtbInNyX3AxNV9oZXQiXV1bWyJzcl9wMTVfaGV0X2Rvd24iXV1bWyJCUF9lbnJpY2giXV0pCnBsb3RzW1siZG90Il1dCmBgYAoKIyMgVGltZQoKIyMjIGhldCByZXRpbmEKClVwczogc2lnbmlmaWNhbnQgcmVzdWx0cyBmb3IgQlAsIE1GLCBURgpEb3duczogQlAsIE1GLCBSRUFDLCBURiwgV1AKCmBgYHtyfQpwbG90cyA8LSBwbG90X2VucmljaHJlc3VsdCh0aW1lX2dwW1sidF9oZXRfcmV0aW5hIl1dW1sidF9oZXRfcmV0aW5hX3VwIl1dW1siQlBfZW5yaWNoIl1dKQpwbG90c1tbImRvdCJdXQoKcGxvdHMgPC0gcGxvdF9lbnJpY2hyZXN1bHQodGltZV9ncFtbInRfaGV0X3JldGluYSJdXVtbInRfaGV0X3JldGluYV9kb3duIl1dW1siQlBfZW5yaWNoIl1dKQpwbG90c1tbImRvdCJdXQpgYGAKCiMjIyBrbyByZXRpbmEKClVwOiBCUCwgTWlSTkEsIE1GCkRvd246IEJQLCBNRiwgUkVBQywgVEYKCmBgYHtyfQpwbG90cyA8LSBwbG90X2VucmljaHJlc3VsdCh0aW1lX2dwW1sidF9rb19yZXRpbmEiXV1bWyJ0X2tvX3JldGluYV91cCJdXVtbIkJQX2VucmljaCJdXSkKcGxvdHNbWyJkb3QiXV0KCnBsb3RzIDwtIHBsb3RfZW5yaWNocmVzdWx0KHRpbWVfZ3BbWyJ0X2tvX3JldGluYSJdXVtbInRfa29fcmV0aW5hX2Rvd24iXV1bWyJCUF9lbnJpY2giXV0pCnBsb3RzW1siZG90Il1dCmBgYAoKIyMjIGhldCBzY24KCk5laXRoZXIgb2YgdGhlIFNDTiBnUHJvZmlsZXIgcXVlcmllcyBwcm92aWRlZCBhbnkgcmVzdWx0cy4KCmBgYHtyfQptZXNzYWdlKCJubyBsb3ZlLiIpCmBgYAoKIyMjIGtvIHNjbgoKYGBge3J9Cm1lc3NhZ2UoIm5vIGxvdmUuIikKYGBgCgoKCiMgQmlibGlvZ3JhcGh5CgpgYGB7ciBzYXZlbWUsIGV2YWw9RkFMU0V9CnBhbmRlcjo6cGFuZGVyKHNlc3Npb25JbmZvKCkpCm1lc3NhZ2UocGFzdGUwKCJUaGlzIGlzIGhwZ2x0b29scyBjb21taXQ6ICIsIGdldF9naXRfY29tbWl0KCkpKQptZXNzYWdlKHBhc3RlMCgiU2F2aW5nIHRvICIsIHNhdmVmaWxlKSkKdG1wIDwtIHNtKHNhdmVtZShmaWxlbmFtZSA9IHNhdmVmaWxlKSkKYGBgCgpgYGB7ciBsb2FkbWVfYWZ0ZXIsIGV2YWw9RkFMU0V9CnRtcCA8LSBsb2FkbWUoZmlsZW5hbWUgPSBzYXZlZmlsZSkKYGBgCg==