TMRC3 202502: Differential Expression analyses, Tumaco only.

atb

2025-02-02

1 Changelog

  • 202412: Reorganizing the lme work
  • 202411: Working on the addition of linear mixed models.
  • 202406: Added an explicit comparison of different model constructions using our most variable cell type, the neutrophils.
  • 202406: Working entirely out of the container now, separated GSE/GSEA analyses, added a full treatment with clusterProfiler; I am not currently writing the cp results out as xlsx files until/unless someone expresses interest in them.
  • 202309: Disabled GSVA analyses until/unless we get permission to include the mSigDB 7.5.1 release (what I used). I will simplify the filenames so that one may easily drop in a downloaded copy of the data and run hose blocks. Until then, I guess you (fictitious reader) will have to trust me when I say those blocks all work? (Also, GSVA was moved to a separate document)
  • 202309: Moved all gene set enrichment analyses to 04lrt_gsea_gsva.Rmd
  • 202309 next day: Moving gene set enrichment back because it adds too much complexity to save/reload the DE results for gProfiler and friends.
  • Still hunting for messed up colors, changed input data to match new version.

2 Notes/TODOs for 202412+

  • Meeting with NMES/MAG: Query, which results to use? MAG prefers DESeq2 with a full discussion and presentation of lmm results.

  • How do we present to the reviewer that the lmm is much more conservative? MAG: That argument is already in the shared document and modified by Neal; the reason lies both in dream and limma, we probably should include an argument in support of DESeq2. Will need to show the similarities between limma/dream and deseq with the caveat of different p-values. (Perhaps this is where the AUCC comes in?)

  • What do we think about dream’s adjusted p-value results?

  • Create tables of the lmm results as xlsx files, do not bother pulling them into the tables with deseq etc. ** 5 tables: monocyte, neutrophil, eosinophil, all, all+sva

  • Create scatter plots showing similarities between p-values perhaps and z-scores, and logFC.

  • Perform GO etc with lmm results.

3 Introduction

The various differential expression analyses of the data generated in tmrc3_datasets will occur in this document. Most of the actual work is via the function ‘all_pairwise()’; the word ‘all’ in the name does a lot of work; it is responsible for performing all possible pairwise contrasts using all possible methods for which I have sufficient understanding to be able to write a reasonably robust pairwise function. Currently this is limited to:

  • DESeq2 (Love, Huber, and Anders (2014)): Our ‘default’
  • edgeR (McCarthy, Chen, and Smyth (2012)): shares a close conceptual lineage with DESeq2 I think.
  • limma (Ritchie et al. (2015)): along with voom this provides a nicely robust set of tools.
  • EBseq (Leng et al. (2013)): I think it is not as robust as the previous entries, but I like using it because it is an almost purely bayesian method and as such provides a different perspective on any dataset.
  • Noiseq (Tarazona et al. (2015)): I noticed this method relatively recently and was sufficiently intrigued that I threw a method together using it. The authors appear to me to be looking to understand a lot of the questions on which I spend a lot of time.
  • Dream (Hoffman and Roussos (2020)): I mostly like this because it uses variancePartition, which I think is a really nice toy when trying to understand what is going on in a dataset.
  • basic is my own, explicitly uninformed analysis. It is my ‘negative control’ method because, if something agrees entirely with it, then I know that all the fancy math and statistics performed by that method worked out just the same as some doofus (me) just log2 subtracting the expression values. It is not quite that basic, but pretty close.

The first 3 methods allow one to add surrogate variable estimates to the model when performing the differential expression analyses. Noiseq handles surrogates using its own heuristics, EBSeq is inimicable to that kind of model, and I explicitly chose to not make that possible for basic. I am uncertain at this time how the random effect factors used with dream interact with surrogates from sva. With that in mind, in most instances I usually deal with surrogates/batches in one of a few ways:

  1. If the data is absurdly pretty, do nothing (pretty much only for well-controlled bacterial data).
  2. Add a known batch factor to the model (the default).
  3. Try to ensure the data is suitable and invoke sva
    1. to acquire estimates and add them to the model.
  4. If the data has a known batch factor and it is particularly pathological, use the combat implementation in sva. As a general rule I do not like this option because it is data destructive.

The last two options are handled via a function named ‘all_adjusters’ in hpgltools which is responsible for ensuring that the data is sane for the assumptions made by each method and invokes each method (hopefully) properly. It returns both modified counts and model estimates when possible and has implementations for a fair number of methods in this realm. sva is my favorite by a pretty big margin, though I do sometimes use RUV (Risso et al. (2014)) and of course, in writing this document I stumbled into another interesting contender: (Molania et al. (2023)) all_adjusters() also has implementations of every example/method I got out of the papers for sva (e.g. ssva/fsva), isva, smartsva, and some others.

I have been changing hpgltools so that it is now possible to trivially pass arbitrarily complex models to the various methods; with the caveat that there is no good way currently to mix fixed effects and random effects across methods; so I am running dream separately and adding it to the result of all_pairwise post-facto.

3.1 Define contrasts for DE analyses

Each of the following lists describes the set of contrasts that I think are interesting for the various ways one might consider the TMRC3 dataset. The variables are named according to the assumed data with which they will be used, thus tc_cf_contrasts is expected to be used for the Tumaco+Cali data and provide a series of cure/fail comparisons which (to the extent possible) across both locations. In every case, the name of the list element will be used as the contrast name, and will thus be seen as the sheet name in the output xlsx file(s); the two pieces of the character vector value are the numerator and denominator of the associated contrast.

  • Our primary question: fail/cure: Any excel file written using this contrast will get a single worksheet comparing fail/cure.
  • Compare fail/cure for each visit: This takes a more granular view of the previous contrast. If one is so-inclined, one could compare results from the following contrast against the previous and following contrast to learn about the dynamics of the healing (or not) process.
  • All samples by visit: This is effectively the opposite of the previous and compares all samples of visit x against visit y.
  • Visit 1 vs everything else: When I first did the previous set of contrasts I quickly realized that visits 2 and 3 are relatively similar and that it may be possible to gain a little power and learn a little more by combining them.
  • Directly compare celltypes: We have three clinical cell types in the data and the differences among them are quite interesting.
  • Ethnicities: We also have three ethnic groups in the data, though there are some wacky confounded variables when considering them through the lense of cure/fail; so any results comparing them should be treated with caution.
  • Powerless visits+celltype+cf: This is a last-minute addition requested by Maria Adelaida. I assume it was suggested by a reviewer, though I do not recall seeing anything in the reviews which made this request. The number of samples we have in the data just barely supports these contrasts, and given the strength of all the various surrogates, I would be somewhat reluctant to trust any genes deemed DE in them without some other evidence. It should be noted that this is the intellectual counterpoint to the critique from a different reviewer, that artifically merging factors like this is problematic (I personally tend to agree with the later argument more than the former with the caveat that the added complexity (with respect to what is actually typed by the person (me)) can be a problem. Thus I tend to do the thing which is explicitly less statistically correct (but I can also show pretty definitively that the results are very nearly identical) in order to make it easier to show that no mistakes were made. E.g. tension between ‘correctness’ and ‘robustness’.
t_cf_contrast <- list(
  "outcome" = c("tumaco_failure", "tumaco_cure"))
cf_contrast <- list(
  "outcome" = c("failure", "cure"))
visitcf_contrasts <- list(
  "v1cf" = c("v1_failure", "v1_cure"),
  "v2cf" = c("v2_failure", "v2_cure"),
  "v3cf" = c("v3_failure", "v3_cure"))
visit_contrasts <- list(
  "v2v1" = c("v2", "v1"),
  "v3v1" = c("v3", "v1"),
  "v3v2" = c("v3", "v2"))
visit_v1later <- list(
  "later_vs_first" = c("later", "first"))
celltypes <- list(
  "eo_mono" = c("eosinophils", "monocytes"),
  "ne_mono" = c("neutrophils", "monocytes"),
  "eo_ne" = c("eosinophils", "neutrophils"))
ethnicity_contrasts <- list(
  "mestizo_indigenous" = c("mestiza", "indigena"),
  "mestizo_afrocol" = c("mestiza", "afrocol"),
  "indigenous_afrocol" = c("indigena", "afrocol"))
outcometype_contrasts <- list(
  "monocyte_cf" = c("failure_monocytes", "cure_monocytes"),
  "neutrophil_cf" = c("failure_neutrophils", "cure_neutrophils"),
  "eosinophil_cf" = c("failure_eosinophils", "cure_eosinophils"))
visittype_contrasts_mono <- list(
  "v2v1_mono_cure" = c("monocytes_2_cure", "monocytes_1_cure"),
  "v2v1_mono_failure" = c("monocytes_2_failure", "monocytes_1_failure"),
  "v3v1_mono_cure" = c("monocytes_3_cure", "monocytes_1_cure"),
  "v3v1_mono_failure" = c("monocytes_3_failure", "monocytes_1_failure"))
visittype_contrasts_eo <- list(
  "v2v1_eo_cure" = c("eosinophils_2_cure", "eosinophils_1_cure"),
  "v2v1_eo_failure" = c("eosinophils_2_failure", "eosinophils_1_failure"),
  "v3v1_eo_cure" = c("eosinophils_3_cure", "eosinophils_1_cure"),
  "v3v1_eo_failure" = c("eosinophils_3_failure", "eosinophils_1_failure"))
visittype_contrasts_ne <- list(
  "v2v1_ne_cure" = c("neutrophils_2_cure", "neutrophils_1_cure"),
  "v2v1_ne_failure" = c("neutrophils_2_failure", "neutrophils_1_failure"),
  "v3v1_ne_cure" = c("neutrophils_3_cure", "neutrophils_1_cure"),
  "v3v1_ne_failure" = c("neutrophils_3_failure", "neutrophils_1_failure"))
visittype_contrasts <- c(visittype_contrasts_mono,
                         visittype_contrasts_eo,
                         visittype_contrasts_ne)

3.2 Gene Set Enrichment / over representation

Previously, the over representation analyses (e.g. GO and friends) followed each DE analysis during this document. I recently mentally severed my conception of GO analyses into two camps: over representation analyses in which one provides a group of genes deemed significant in some way and asks if there are known categories which contain these genes more than one would expect at random. In contrast, I am defining gene set enrichment analyses explcitly as the process of passing all genes with their metric of choice (logFC, exprs, whatever) and asking if the distribution of all genes is significant with respect to the categories. With that in mind, I added a series of explicitly GSEA analyses in my later iterations of these documents so that both ways of thinking are provided.

However, I moved those analyses to a separate document (05enrichment.Rmd) in the hopes of improving their organization.

4 Only Tumaco samples

Start over, this time with only the samples from Tumaco. We currently are assuming these will prove to be the only analyses used for final interpretation. This is primarily because we have insufficient samples which failed treatment from Cali. There is one disadvantage when using these samples: they had to travel further than the samples taken in Cali and there is significant variance observed between the two locations and we cannot discern its source. In the worst case scenario (one which I think unlikely), the variance is caused by degraded RNA during transit. We do know that the samples were well-stored in RNALater and frozen/etc, so I am inclined to discount that possibility. (Also, looking at the reads in IGV they don’t ‘look’ degraded to me.) I think a more compelling difference lies in the different population demographics observed in the two locations. Actually, now that I have typed these sentences out, I think I can semi-test this hypothesis by looking at the set of DE genes between the two locations and compare that result to the Tumaco (and/or Cali) ethnicity comparison which is most representative of the ethnicity differences between them. If I get it into my head to try this, I will need to load the DE tables from the 03differential_expression_both.Rmd document; so I am most likely to try it out in the 07var_coef document, which was mostly written by Theresa and is already examining some similar questions.

4.1 All samples

Start by considering all Tumaco cell types. Note that in this case we only use SVA, primarily because I am not certain what would be an appropriate batch factor, perhaps visit?

t_cf_clinical_de_sva <- all_pairwise(t_clinical, model_batch = "svaseq",
                                     filter = TRUE,
                                     methods = methods)
## 
##    cure failure 
##      67      56
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_clinical <- t_cf_clinical_de_sva[["input"]]
## Error in eval(expr, envir, enclos): object 't_cf_clinical_de_sva' not found
t_cf_clinical_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_clinical_de_sva' not found
t_cf_clinical_table_sva <- combine_de_tables(
  t_cf_clinical_de_sva, keepers = cf_contrast,
  excel = glue("{cf_prefix}/All_Samples/t_clinical_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_clinical_de_sva' not found
t_cf_clinical_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_clinical_table_sva' not found
t_cf_clinical_table_sva[["plots"]][["outcome"]][["deseq_ma_plots"]]
## Error in eval(expr, envir, enclos): object 't_cf_clinical_table_sva' not found
t_cf_clinical_sig_sva <- extract_significant_genes(
  t_cf_clinical_table_sva,
  excel = glue("{cf_prefix}/All_Samples/t_clinical_cf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_clinical_table_sva' not found
t_cf_clinical_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_clinical_sig_sva' not found
dim(t_cf_clinical_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_clinical_sig_sva' not found
dim(t_cf_clinical_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_clinical_sig_sva' not found

Repeat without the biopsies.

t_cf_clinicalnb_de_sva <- all_pairwise(t_clinical_nobiop, model_batch = "svaseq",
                                       filter = TRUE,
                                       methods = methods)
## 
##    cure failure 
##      58      51
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_clinical_nobiop <- t_cf_clinicalnb_de_sva[["input"]]
## Error in eval(expr, envir, enclos): object 't_cf_clinicalnb_de_sva' not found
t_cf_clinicalnb_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_clinicalnb_de_sva' not found
t_cf_clinicalnb_table_sva <- combine_de_tables(
  t_cf_clinicalnb_de_sva, keepers = cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/All_Samples/t_clinical_nobiop_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_clinicalnb_de_sva' not found
t_cf_clinicalnb_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_clinicalnb_table_sva' not found
t_cf_clinicalnb_table_sva[["plots"]][["outcome"]][["deseq_ma_plots"]]
## Error in eval(expr, envir, enclos): object 't_cf_clinicalnb_table_sva' not found
t_cf_clinicalnb_sig_sva <- extract_significant_genes(
  t_cf_clinicalnb_table_sva,
  excel = glue("{cf_prefix}/All_Samples/t_clinical_nobiop_cf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_clinicalnb_table_sva' not found
t_cf_clinicalnb_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_clinicalnb_sig_sva' not found
dim(t_cf_clinicalnb_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_clinicalnb_sig_sva' not found
dim(t_cf_clinicalnb_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_clinicalnb_sig_sva' not found

As the data structure’s name suggests, the above comparison seeks to learn if there are fail/cure differences discernable across all clinical celltypes in samples taken in Tumaco.

The set of steps taken in this previous block will be essentially repeated for every set of contrasts and way of mixing/matching the data and follows the path:

  1. Run all_pairwise to run deseq and friends using surogate estimates provided by sva when appropriate/possible. This creates an unwieldy datastructure containing the results from all methods and all contrasts as a series of nested lists.
  2. Mash them together with combine_de_tables, use the ‘keepers’ argument to define the desired numerators/denominators, and write the tables to the file provided in the ‘excel’ argument.
  3. Yank out the ‘significant’ genes and send them to a separate excel document. In all cases, ‘significant’ is the set with a |log2FC| >= 1.0 and adjusted p-value <= 0.05. This reminds me, one of the reviewers mentioned a set of international guidelines for significant genes, I thought I basically know what I am doing, but this caught me completely unaware. If anyone ever reads this (no one will, let us be honest) I would love to know. The closest thing I found is: (Chung et al. (2021)), but I do not think it really addresses this idea (I have not yet read it carefully).

These datastructures are all exposed to various functions in hpgltools which allow one to poke/compare them; I am not a fan of Excel, but I think the xlsx documents it creates are pretty decent, too.

5 Visit comparisons

Later in this document I do a bunch of visit/cf comparisons. In this block I want to explicitly only compare v1 to other visits. This is something I did quite a lot in the 2019 datasets, but never actually moved to this document.

tv1_vs_later <- all_pairwise(t_v1vs, model_batch = "svaseq",
                             filter = TRUE,
                             methods = methods)
## 
## first later 
##    40    69
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_v1vs <- tv1_vs_later[["input"]]
## Error in eval(expr, envir, enclos): object 'tv1_vs_later' not found
tv1_vs_later
## Error in eval(expr, envir, enclos): object 'tv1_vs_later' not found
tv1_vs_later_table <- combine_de_tables(
  tv1_vs_later, keepers = visit_v1later, scale_p = TRUE,
  excel = glue("{xlsx_prefix}/DE_Visits/tv1_vs_later_tables-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'tv1_vs_later' not found
tv1_vs_later_table
## Error in eval(expr, envir, enclos): object 'tv1_vs_later_table' not found
tv1_vs_later_sig <- extract_significant_genes(
  tv1_vs_later_table,
  excel = glue("{xlsx_prefix}/DE_Visits/tv1_vs_later_sig-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 'tv1_vs_later_table' not found
tv1_vs_later_sig
## Error in eval(expr, envir, enclos): object 'tv1_vs_later_sig' not found

6 Sex comparison

There is an important caveat when considering the sex of people in the study: there are very few females who failed. As a result I primarily concerned with the cure samples male/female.

t_sex <- subset_expt(tc_sex, subset = "clinic == 'tumaco'")
## subset_expt(): There were 184, now there are 123 samples.
t_sex
## A modified expressionSet containing 19952  and 123 sample. There are 164 metadata columns and 15 annotation columns.
## The primary condition is comprised of:
## female, male.
## Its current state is: raw(data).
t_sex_de <- all_pairwise(t_sex, model_batch = "svaseq", methods = methods,
                         filter = TRUE)
## 
## female   male 
##     22    101
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_sex <- t_sex_de[["input"]]
## Error in eval(expr, envir, enclos): object 't_sex_de' not found
t_sex_de
## Error in eval(expr, envir, enclos): object 't_sex_de' not found
t_sex_table <- combine_de_tables(
  t_sex_de, scale_p = TRUE,
  excel = glue("{xlsx_prefix}/Gene_Set_Enrichment/t_sex_table-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_sex_de' not found
t_sex_table
## Error in eval(expr, envir, enclos): object 't_sex_table' not found
t_sex_sig <- extract_significant_genes(
  t_sex_table, excel = glue("{xlsx_prefix}/Gene_Set_Enrichment/t_sex_sig-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_sex_table' not found
t_sex_sig
## Error in eval(expr, envir, enclos): object 't_sex_sig' not found

In the following block I removed the failed people so that the comparison makes actual sense.

tc_sex_cure <- subset_expt(tc_sex, subset = "finaloutcome=='cure'")
## subset_expt(): There were 184, now there are 122 samples.
t_sex_cure <- subset_expt(tc_sex_cure, subset = "clinic == 'tumaco'")
## subset_expt(): There were 122, now there are 67 samples.
t_sex_cure_de <- all_pairwise(t_sex_cure, model_batch = "svaseq",
                              filter = TRUE,
                              methods = methods)
## 
## female   male 
##     13     54
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_sex_cure <- t_sex_cure_de[["input"]]
## Error in eval(expr, envir, enclos): object 't_sex_cure_de' not found
t_sex_cure_de
## Error in eval(expr, envir, enclos): object 't_sex_cure_de' not found
t_sex_cure_table <- combine_de_tables(
  t_sex_cure_de, scale_p = TRUE,
  excel = glue("{xlsx_prefix}/DE_Sex/t_sex_cure_table-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_sex_cure_de' not found
t_sex_cure_table
## Error in eval(expr, envir, enclos): object 't_sex_cure_table' not found
t_sex_cure_sig <- extract_significant_genes(
  t_sex_cure_table, excel = glue("{xlsx_prefix}/DE_Sex/t_sex_cure_sig-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_sex_cure_table' not found
t_sex_cure_sig
## Error in eval(expr, envir, enclos): object 't_sex_cure_sig' not found

7 Ethnicity comparisons

In a fashion similar to the putative sex comparisons; there are few/no fails for one ethnicity. In addition, the observed ethnicities are very different for the two clinics. This makes comparisons of the ethnicities tricky.

t_ethnicity_de <- all_pairwise(t_etnia_expt, model_batch = "svaseq",
                               filter = TRUE,
                               methods = methods)
## 
##  afrocol indigena  mestiza 
##       76       19       28
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_etnia_expt <- t_ethnicity_de[["input"]]
## Error in eval(expr, envir, enclos): object 't_ethnicity_de' not found
t_ethnicity_de
## Error in eval(expr, envir, enclos): object 't_ethnicity_de' not found
t_ethnicity_table <- combine_de_tables(
  t_ethnicity_de, keepers = ethnicity_contrasts, scale_p = TRUE,
  excel = glue("{xlsx_prefix}/DE_Ethnicity/t_ethnicity_table-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_ethnicity_de' not found
t_ethnicity_table
## Error in eval(expr, envir, enclos): object 't_ethnicity_table' not found
t_ethnicity_sig <- extract_significant_genes(
  t_ethnicity_table, according_to = "deseq",
  excel = glue("{xlsx_prefix}/DE_Ethnicity/t_ethnicity_sig-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_ethnicity_table' not found
t_ethnicity_sig
## Error in eval(expr, envir, enclos): object 't_ethnicity_sig' not found

8 Separate the Tumaco data by visit

One of the most compelling ideas in the data is the opportunity to find genes in the first visit which may help predict the likelihood that a person will respond well to treatment. The following block will therefore look at cure/fail from Tumaco at visit 1.

8.1 Cure/Fail, Tumaco Visit 1

t_cf_clinical_v1_de_sva <- all_pairwise(tv1_samples, model_batch = "svaseq",
                                        filter = TRUE,
                                        methods = methods)
## 
##    cure failure 
##      30      24
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
tv1_samples <- t_cf_clinical_v1_de_sva[["input"]]
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v1_de_sva' not found
t_cf_clinical_v1_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v1_de_sva' not found
t_cf_clinical_v1_table_sva <- combine_de_tables(
  t_cf_clinical_v1_de_sva, keepers = cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Visits/t_clinical_v1_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_clinical_v1_de_sva' not found
t_cf_clinical_v1_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v1_table_sva' not found
t_cf_clinical_v1_sig_sva <- extract_significant_genes(
  t_cf_clinical_v1_table_sva,
  excel = glue("{cf_prefix}/Visits/t_clinical_v1_cf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v1_table_sva' not found
t_cf_clinical_v1_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v1_sig_sva' not found
dim(t_cf_clinical_v1_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v1_sig_sva' not found
dim(t_cf_clinical_v1_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v1_sig_sva' not found

8.2 Cure/Fail, Tumaco Visit 2

The visit 2 and visit 3 samples are interesting because they provide an opportunity to see if we can observe changes in response in the middle and end of treatment…

t_cf_clinical_v2_de_sva <- all_pairwise(tv2_samples, model_batch = "svaseq",
                                        filter = TRUE,
                                        methods = methods)
## 
##    cure failure 
##      20      15
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
tv2_samples <- t_cf_clinical_v2_de_sva[["input"]]
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v2_de_sva' not found
t_cf_clinical_v2_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v2_de_sva' not found
t_cf_clinical_v2_table_sva <- combine_de_tables(
  t_cf_clinical_v2_de_sva, keepers = cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Visits/t_clinical_v2_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_clinical_v2_de_sva' not found
t_cf_clinical_v2_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v2_table_sva' not found
t_cf_clinical_v2_sig_sva <- extract_significant_genes(
  t_cf_clinical_v2_table_sva,
  excel = glue("{cf_prefix}/Visits/t_clinical_v2_cf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v2_table_sva' not found
t_cf_clinical_v2_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v2_sig_sva' not found
dim(t_cf_clinical_v2_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v2_sig_sva' not found
dim(t_cf_clinical_v2_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v2_sig_sva' not found

8.3 Cure/Fail, Tumaco Visit 3

Repeat for visit 3

t_cf_clinical_v3_de_sva <- all_pairwise(tv3_samples, model_batch = "svaseq",
                                        filter = TRUE,
                                        methods = methods)
## 
##    cure failure 
##      17      17
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
tv3_samples <- t_cf_clinical_v3_de_sva[["input"]]
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v3_de_sva' not found
t_cf_clinical_v3_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v3_de_sva' not found
t_cf_clinical_v3_table_sva <- combine_de_tables(
  t_cf_clinical_v3_de_sva, keepers = cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Visits/t_clinical_v3_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_clinical_v3_de_sva' not found
t_cf_clinical_v3_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v3_table_sva' not found
t_cf_clinical_v3_sig_sva <- extract_significant_genes(
  t_cf_clinical_v3_table_sva,
  excel = glue("{cf_prefix}/Visits/t_clinical_v3_cf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v3_table_sva' not found
t_cf_clinical_v3_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v3_sig_sva' not found
dim(t_cf_clinical_v3_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v3_sig_sva' not found
dim(t_cf_clinical_v3_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_clinical_v3_sig_sva' not found

9 By cell type

Now let us switch our view to each individual cell type collected. The hope here is that we will be able to learn some cell-specific differences in the response for people who did(not) respond well.

9.1 Cure/Fail, Biopsies

A primary hypothesis/assumption that we have held for quite a while with this data: the biopsy samples, given that they are comprised of hetergeneous tissue types as well as a mix of healthy and infected tissue; are unlikely to be very information rich vis a vis cure/fail. The following block seems to support that; we observe very few genes in the biopsies.

I therefore did not spend the time invoking other models.

t_cf_biopsy_de_sva <- all_pairwise(t_biopsies, model_batch = "svaseq",
                                   filter = TRUE,
                                   methods = methods)
## 
##    tumaco_cure tumaco_failure 
##              9              5
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_biopsies <- t_cf_biopsy_de_sva[["input"]]
## Error in eval(expr, envir, enclos): object 't_cf_biopsy_de_sva' not found
t_cf_biopsy_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_biopsy_de_sva' not found
t_cf_biopsy_table_sva <- combine_de_tables(
  t_cf_biopsy_de_sva, keepers = t_cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Biopsies/t_biopsy_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_biopsy_de_sva' not found
t_cf_biopsy_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_biopsy_table_sva' not found
t_cf_biopsy_sig_sva <- extract_significant_genes(
  t_cf_biopsy_table_sva,
  excel = glue("{cf_prefix}/Biopsies/t_cf_biopsy_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_biopsy_table_sva' not found
t_cf_biopsy_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_biopsy_sig_sva' not found
dim(t_cf_biopsy_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_biopsy_sig_sva' not found
dim(t_cf_biopsy_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_biopsy_sig_sva' not found

9.2 Cure/Fail, Monocytes

Same question, but this time looking at monocytes. In addition, this comparison was done twice, once using SVA and once using visit as a batch factor.

I have been using this block to ensure that changed I have been making to the hpgltools do not change the analysis results. Thus the comment with a few logFC values; those are the first 6 observed DESeq2 logFC values in my last result before I made some changes to hpgltools in order to be able to work with random effect models.

t_cf_monocyte_de_sva <- all_pairwise(t_monocytes, model_batch = "svaseq",
                                     filter = TRUE,
                                     methods = methods)
## 
##    tumaco_cure tumaco_failure 
##             21             21
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
## The svs are added to the expressionset during all_pairwise.
t_monocytes <- t_cf_monocyte_de_sva[["input"]]
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_de_sva' not found
t_cf_monocyte_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_de_sva' not found
t_cf_monocyte_table_sva <- combine_de_tables(
  t_cf_monocyte_de_sva, keepers = t_cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Monocytes/t_monocyte_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_monocyte_de_sva' not found
t_cf_monocyte_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_table_sva' not found
head(t_cf_monocyte_table_sva[["data"]][["outcome"]][["deseq_logfc"]])
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_table_sva' not found
## The first few values in my pre-change result set are:
## 0.338, -0.072, 0.097, -0.091, -0.135, 0.233
t_cf_monocyte_sig_sva <- extract_significant_genes(
  t_cf_monocyte_table_sva,
  excel = glue("{cf_prefix}/Monocytes/t_monocyte_cf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_table_sva' not found
t_cf_monocyte_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_sig_sva' not found
dim(t_cf_monocyte_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_sig_sva' not found
dim(t_cf_monocyte_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_sig_sva' not found
t_cf_monocyte_de_batchvisit <- all_pairwise(t_monocytes, model_batch = TRUE,
                                            filter = TRUE,
                                            methods = methods)
## 
##    tumaco_cure tumaco_failure 
##             21             21 
## 
## v3 v2 v1 
## 13 13 16
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_cf_monocyte_de_batchvisit
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_de_batchvisit' not found
t_cf_monocyte_table_batchvisit <- combine_de_tables(
  t_cf_monocyte_de_batchvisit, keepers = t_cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Monocytes/t_monocyte_cf_table_batchvisit-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_monocyte_de_batchvisit' not found
t_cf_monocyte_table_batchvisit
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_table_batchvisit' not found
t_cf_monocyte_sig_batchvisit <- extract_significant_genes(
  t_cf_monocyte_table_batchvisit,
  excel = glue("{cf_prefix}/Monocytes/t_monocyte_cf_sig_batchvisit-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_table_batchvisit' not found
t_cf_monocyte_sig_batchvisit
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_sig_batchvisit' not found
dim(t_cf_monocyte_sig_batchvisit[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_sig_batchvisit' not found
dim(t_cf_monocyte_sig_batchvisit[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_sig_batchvisit' not found

9.3 Individual visits, Monocytes

Now focus in on the monocyte samples on a per-visit basis.

9.3.1 Visit 1

t_cf_monocyte_v1_de_sva <- all_pairwise(tv1_monocytes, model_batch = "svaseq",
                                        filter = TRUE,
                                        methods = methods)
## 
##    tumaco_cure tumaco_failure 
##              8              8
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
tv1_monocytes <- t_cf_monocyte_v1_de_sva[["input"]]
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_v1_de_sva' not found
t_cf_monocyte_v1_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_v1_de_sva' not found
t_cf_monocyte_v1_table_sva <- combine_de_tables(
  t_cf_monocyte_v1_de_sva, keepers = t_cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Monocytes/t_monocyte_v1_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_monocyte_v1_de_sva' not found
t_cf_monocyte_v1_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_v1_table_sva' not found
t_cf_monocyte_v1_sig_sva <- extract_significant_genes(
  t_cf_monocyte_v1_table_sva,
  excel = glue("{cf_prefix}/Monocytes/t_monocyte_v1_cf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_v1_table_sva' not found
t_cf_monocyte_v1_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_v1_sig_sva' not found
dim(t_cf_monocyte_v1_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_v1_sig_sva' not found
dim(t_cf_monocyte_v1_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_v1_sig_sva' not found

9.3.2 Monocytes: Compare sva to batch-in-model

sva_aucc <- calculate_aucc(t_cf_monocyte_table_sva[["data"]][[1]],
                           tbl2 = t_cf_monocyte_table_batchvisit[["data"]][[1]],
                           py = "deseq_adjp", ly = "deseq_logfc")
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_table_sva' not found
sva_aucc
## Error in eval(expr, envir, enclos): object 'sva_aucc' not found
shared_ids <- rownames(t_cf_monocyte_table_sva[["data"]][[1]]) %in%
  rownames(t_cf_monocyte_table_batchvisit[["data"]][[1]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function '%in%': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 't_cf_monocyte_table_sva' not found
first <- t_cf_monocyte_table_sva[["data"]][[1]][shared_ids, ]
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_table_sva' not found
second <- t_cf_monocyte_table_batchvisit[["data"]][[1]][rownames(first), ]
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_table_batchvisit' not found
cor.test(first[["deseq_logfc"]], second[["deseq_logfc"]])
## Error in first[["deseq_logfc"]]: object of type 'closure' is not subsettable

9.4 Neutrophil samples

Switch context to the Neutrophils, once again repeat the analysis using SVA and visit as a batch factor.

t_cf_neutrophil_de_sva <- all_pairwise(t_neutrophils, model_batch = "svaseq",
                                       filter = TRUE,
                                       methods = methods)
## 
##    tumaco_cure tumaco_failure 
##             20             21
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_cf_neutrophil_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_de_sva' not found
t_cf_neutrophil_table_sva <- combine_de_tables(
  t_cf_neutrophil_de_sva, keepers = t_cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Neutrophils/t_neutrophil_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_neutrophil_de_sva' not found
t_cf_neutrophil_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_table_sva' not found
t_cf_neutrophil_sig_sva <- extract_significant_genes(
  t_cf_neutrophil_table_sva,
  excel = glue("{cf_prefix}/Neutrophils/t_neutrophil_cf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_table_sva' not found
t_cf_neutrophil_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_sig_sva' not found
dim(t_cf_neutrophil_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_sig_sva' not found
dim(t_cf_neutrophil_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_sig_sva' not found
t_cf_neutrophil_de_batchvisit <- all_pairwise(t_neutrophils, model_batch = TRUE,
                                              filter = TRUE,
                                              methods = methods)
## 
##    tumaco_cure tumaco_failure 
##             20             21 
## 
## v3 v2 v1 
## 12 13 16
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_cf_neutrophil_de_batchvisit
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_de_batchvisit' not found
t_cf_neutrophil_table_batchvisit <- combine_de_tables(
  t_cf_neutrophil_de_batchvisit, keepers = t_cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Neutrophils/t_neutrophil_cf_table_batchvisit-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_neutrophil_de_batchvisit' not found
t_cf_neutrophil_table_batchvisit
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_table_batchvisit' not found
t_cf_neutrophil_sig_batchvisit <- extract_significant_genes(
  t_cf_neutrophil_table_batchvisit,
  excel = glue("{cf_prefix}/Neutrophils/t_neutrophil_cf_sig_batchvisit-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_table_batchvisit' not found
t_cf_neutrophil_sig_batchvisit
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_sig_batchvisit' not found
dim(t_cf_neutrophil_sig_batchvisit[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_sig_batchvisit' not found
dim(t_cf_neutrophil_sig_batchvisit[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_sig_batchvisit' not found

9.4.1 Neutrophils by visit

When I did this with the monocytes, I split it up into multiple blocks for each visit. This time I am just going to run them all together.

visitcf_factor <- paste0(pData(t_neutrophils)[["visitnumber"]], "_",
                         pData(t_neutrophils)[["finaloutcome"]])
t_neutrophil_visitcf <- set_expt_conditions(t_neutrophils, fact = visitcf_factor)
## The numbers of samples by condition are:
## 
##    v1_cure v1_failure    v2_cure v2_failure    v3_cure v3_failure 
##          8          8          7          6          5          7
t_cf_neutrophil_visits_de_sva <- all_pairwise(t_neutrophil_visitcf, model_batch = "svaseq",
                                              filter = TRUE,
                                              methods = methods)
## 
##    v1_cure v1_failure    v2_cure v2_failure    v3_cure v3_failure 
##          8          8          7          6          5          7
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_cf_neutrophil_visits_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_visits_de_sva' not found
t_cf_neutrophil_visits_table_sva <- combine_de_tables(
  t_cf_neutrophil_visits_de_sva, keepers = visitcf_contrasts, scale_p = TRUE,
  excel = glue("{cf_prefix}/Neutrophils/t_neutrophil_visitcf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_neutrophil_visits_de_sva' not found
t_cf_neutrophil_visits_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_visits_table_sva' not found
t_cf_neutrophil_visits_sig_sva <- extract_significant_genes(
  t_cf_neutrophil_visits_table_sva,
  excel = glue("{cf_prefix}/Neutrophils/t_neutrophil_visitcf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_visits_table_sva' not found
t_cf_neutrophil_visits_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_visits_sig_sva' not found
dim(t_cf_neutrophil_visits_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_visits_sig_sva' not found
dim(t_cf_neutrophil_visits_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_visits_sig_sva' not found

Now V1

t_cf_neutrophil_v1_de_sva <- all_pairwise(tv1_neutrophils, model_batch = "svaseq",
                                          filter = TRUE,
                                          methods = methods)
## 
##    tumaco_cure tumaco_failure 
##              8              8
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_cf_neutrophil_v1_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v1_de_sva' not found
t_cf_neutrophil_v1_table_sva <- combine_de_tables(
  t_cf_neutrophil_v1_de_sva, keepers = t_cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Neutrophils/t_neutrophil_v1_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_neutrophil_v1_de_sva' not found
t_cf_neutrophil_v1_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v1_table_sva' not found
t_cf_neutrophil_v1_sig_sva <- extract_significant_genes(
  t_cf_neutrophil_v1_table_sva,
  excel = glue("{cf_prefix}/Neutrophils/t_neutrophil_v1_cf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v1_table_sva' not found
t_cf_neutrophil_v1_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v1_sig_sva' not found
dim(t_cf_neutrophil_v1_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v1_sig_sva' not found
dim(t_cf_neutrophil_v1_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v1_sig_sva' not found

Followed by visit 2.

t_cf_neutrophil_v2_de_sva <- all_pairwise(tv2_neutrophils, model_batch = "svaseq",
                                          filter = TRUE,
                                          methods = methods)
## 
##    tumaco_cure tumaco_failure 
##              7              6
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_cf_neutrophil_v2_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v2_de_sva' not found
t_cf_neutrophil_v2_table_sva <- combine_de_tables(
  t_cf_neutrophil_v2_de_sva, scale_p = TRUE, keepers = t_cf_contrast,
  excel = glue("{cf_prefix}/Neutrophils/t_neutrophil_v2_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_neutrophil_v2_de_sva' not found
t_cf_neutrophil_v2_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v2_table_sva' not found
t_cf_neutrophil_v2_sig_sva <- extract_significant_genes(
  t_cf_neutrophil_v2_table_sva,
  excel = glue("{cf_prefix}/Neutrophils/t_neutrophil_v2_cf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v2_table_sva' not found
t_cf_neutrophil_v2_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v2_sig_sva' not found
dim(t_cf_neutrophil_v2_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v2_sig_sva' not found
dim(t_cf_neutrophil_v2_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v2_sig_sva' not found

and visit 3.

t_cf_neutrophil_v3_de_sva <- all_pairwise(tv3_neutrophils, model_batch = "svaseq",
                                          filter = TRUE,
                                          methods = methods)
## 
##    tumaco_cure tumaco_failure 
##              5              7
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_cf_neutrophil_v3_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v3_de_sva' not found
t_cf_neutrophil_v3_table_sva <- combine_de_tables(
  t_cf_neutrophil_v3_de_sva, keepers = t_cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Neutrophils/t_neutrophil_v3_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_neutrophil_v3_de_sva' not found
t_cf_neutrophil_v3_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v3_table_sva' not found
t_cf_neutrophil_v3_sig_sva <- extract_significant_genes(
  t_cf_neutrophil_v3_table_sva,
  excel = glue("{cf_prefix}/Neutrophils/t_neutrophil_v3_cf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v3_table_sva' not found
t_cf_neutrophil_v3_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v3_sig_sva' not found
dim(t_cf_neutrophil_v3_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v3_sig_sva' not found
dim(t_cf_neutrophil_v3_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_v3_sig_sva' not found

9.4.2 Neutrophils: Compare sva to batch-in-model

sva_aucc <- calculate_aucc(t_cf_neutrophil_table_sva[["data"]][[1]],
                           tbl2 = t_cf_neutrophil_table_batchvisit[["data"]][[1]],
                           py = "deseq_adjp", ly = "deseq_logfc")
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_table_sva' not found
sva_aucc
## Error in eval(expr, envir, enclos): object 'sva_aucc' not found
shared_ids <- rownames(t_cf_neutrophil_table_sva[["data"]][[1]]) %in%
  rownames(t_cf_neutrophil_table_batchvisit[["data"]][[1]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function '%in%': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 't_cf_neutrophil_table_sva' not found
first <- t_cf_neutrophil_table_sva[["data"]][[1]][shared_ids, ]
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_table_sva' not found
second <- t_cf_neutrophil_table_batchvisit[["data"]][[1]][rownames(first), ]
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_table_batchvisit' not found
cor.test(first[["deseq_logfc"]], second[["deseq_logfc"]])
## Error in first[["deseq_logfc"]]: object of type 'closure' is not subsettable

9.5 Eosinophils

This time, with feeling! Repeating the same set of tasks with the eosinophil samples.

t_cf_eosinophil_de_sva <- all_pairwise(t_eosinophils, model_batch = "svaseq",
                                       filter = TRUE,
                                       methods = methods)
## 
##    tumaco_cure tumaco_failure 
##             17              9
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_cf_eosinophil_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_de_sva' not found
t_cf_eosinophil_table_sva <- combine_de_tables(
  t_cf_eosinophil_de_sva, keepers = t_cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Eosinophils/t_eosinophil_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_eosinophil_de_sva' not found
t_cf_eosinophil_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_table_sva' not found
t_cf_eosinophil_sig_sva <- extract_significant_genes(
  t_cf_eosinophil_table_sva,
  excel = glue("{cf_prefix}/Eosinophils/t_eosinophil_cf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_table_sva' not found
t_cf_eosinophil_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_sig_sva' not found
dim(t_cf_eosinophil_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_sig_sva' not found
dim(t_cf_eosinophil_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_sig_sva' not found
head(t_cf_eosinophil_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_sig_sva' not found
head(t_cf_eosinophil_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_sig_sva' not found

Repeat with batch in the model.

t_cf_eosinophil_de_batchvisit <- all_pairwise(t_eosinophils, model_batch = TRUE,
                                              filter = TRUE,
                                              methods = methods)
## 
##    tumaco_cure tumaco_failure 
##             17              9 
## 
## v3 v2 v1 
##  9  9  8
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_cf_eosinophil_de_batchvisit
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_de_batchvisit' not found
t_cf_eosinophil_table_batchvisit <- combine_de_tables(
  t_cf_eosinophil_de_batchvisit, keepers = t_cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Eosinophils/t_eosinophil_cf_table_batchvisit-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_eosinophil_de_batchvisit' not found
t_cf_eosinophil_table_batchvisit
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_table_batchvisit' not found
t_cf_eosinophil_sig_batchvisit <- extract_significant_genes(
  t_cf_eosinophil_table_batchvisit,
  excel = glue("{cf_prefix}/Eosinophils/t_eosinophil_cf_sig_batchvisit-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_table_batchvisit' not found
t_cf_eosinophil_sig_batchvisit
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_sig_batchvisit' not found
dim(t_cf_eosinophil_sig_batchvisit[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_sig_batchvisit' not found
dim(t_cf_eosinophil_sig_batchvisit[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_sig_batchvisit' not found
head(t_cf_eosinophil_sig_batchvisit[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_sig_batchvisit' not found
head(t_cf_eosinophil_sig_batchvisit[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_sig_batchvisit' not found

Repeat with visit in the condition contrast.

visitcf_factor <- paste0(pData(t_eosinophils)[["visitnumber"]], "_",
                         pData(t_eosinophils)[["finaloutcome"]])
t_eosinophil_visitcf <- set_expt_conditions(t_eosinophils, fact = visitcf_factor)
## The numbers of samples by condition are:
## 
##    v1_cure v1_failure    v2_cure v2_failure    v3_cure v3_failure 
##          5          3          6          3          6          3
t_cf_eosinophil_visits_de_sva <- all_pairwise(t_eosinophil_visitcf, model_batch = "svaseq",
                                              filter = TRUE,
                                              methods = methods)
## 
##    v1_cure v1_failure    v2_cure v2_failure    v3_cure v3_failure 
##          5          3          6          3          6          3
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_cf_eosinophil_visits_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_visits_de_sva' not found
t_cf_eosinophil_visits_table_sva <- combine_de_tables(
   t_cf_eosinophil_visits_de_sva, keepers = visitcf_contrasts, scale_p = TRUE,
  excel = glue("{cf_prefix}/Eosinophils/t_eosinophil_visitcf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_eosinophil_visits_de_sva' not found
t_cf_eosinophil_visits_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_visits_table_sva' not found
t_cf_eosinophil_visits_sig_sva <- extract_significant_genes(
  t_cf_eosinophil_visits_table_sva,
  excel = glue("{cf_prefix}/Eosinophils/t_eosinophil_visitcf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_visits_table_sva' not found
t_cf_eosinophil_visits_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_visits_sig_sva' not found
dim(t_cf_eosinophil_visits_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_visits_sig_sva' not found
dim(t_cf_eosinophil_visits_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_visits_sig_sva' not found
head(t_cf_eosinophil_visits_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_visits_sig_sva' not found
head(t_cf_eosinophil_visits_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_visits_sig_sva' not found

10 Compare to Visit explicitly in the model

As a reminder, there are a few genes of particular interest:

expected_genes <- c("IFI44L", "IFI27", "PRR5", "PRR5-ARHGAP8", "RHCE",
                    "FBXO39", "RSAD2", "SMTNL1", "USP18", "AFAP1")
annot <- fData(t_monocytes)
wanted_idx <- annot[["hgnc_symbol"]] %in% expected_genes
expected_ensg <- rownames(annot)[wanted_idx]

10.1 Monocytes

Either above or below this section I have a nearly identical block which seeks to demonstrate the similarities/difference observed between my preferred/simplified model vs. a more explicitly correct and complex model. If the trend holds from what we observed with the eosinophils and neutrophils, I would expect to see that the results are marginally ‘better’ (as defined by the strength of the perceived interleukin response and raw number of ‘significant’ genes); but I remain worried that this will prove a more brittle and error-prone analysis.

10.1.1 Filter the data and perform svaseq

Start out by extracting the perceived svs via svaseq on the filtered input.

Note to self: the model work I did in hpgltools makes this irrelevant; by replacing the foolish model_connd/model_batch arguments with fstring and model_sv I can do this trivially. In addition, it allows me to mix and match sv methods and compare them using the same data structure because it returns the expressionset with new columns named stuff like ‘svaseq_sv1’…

## The original pairwise invocation with sva:
##t_cf_monocyte_de_sva <- all_pairwise(t_monocyte, model_batch = "svaseq",
##                                     filter = TRUE, parallel = FALSE,
##                                     methods = methods)
test_monocytes <- normalize_expt(t_monocytes, filter = "simple")
## Removing 2619 low-count genes (17333 remaining).
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
test_mono_design <- pData(test_monocytes)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'test_monocytes' not found
test_formula <- as.formula("~ finaloutcome + visitnumber")
test_model <- model.matrix(test_formula, data = test_mono_design)
## Error in eval(expr, envir, enclos): object 'test_mono_design' not found
null_formula <- as.formula("~ visitnumber")
null_model <- model.matrix(null_formula, data = test_mono_design)
## Error in eval(expr, envir, enclos): object 'test_mono_design' not found
linear_mtrx <- exprs(test_monocytes)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'test_monocytes' not found
l2_mtrx <- log2(linear_mtrx + 1)
## Error in eval(expr, envir, enclos): object 'linear_mtrx' not found
chosen_surrogates <- sva::num.sv(dat = l2_mtrx, mod = test_model)
## Error in eval(expr, envir, enclos): object 'l2_mtrx' not found
chosen_surrogates
## Error in eval(expr, envir, enclos): object 'chosen_surrogates' not found
surrogate_result <- sva::svaseq(
  dat = linear_mtrx, n.sv = chosen_surrogates, mod = test_model, mod0 = null_model)
## Error in eval(expr, envir, enclos): object 'linear_mtrx' not found
model_adjust <- as.matrix(surrogate_result[["sv"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.matrix': object 'surrogate_result' not found

10.1.2 Add the svs to the data model and create a new DESeq2 dataset

We can now create a new DESeq2 dataset which takes these putative surrogates into account.

colnames(model_adjust) <- paste0("SV", seq_len(chosen_surrogates))
## Error in eval(expr, envir, enclos): object 'chosen_surrogates' not found
rownames(model_adjust) <- rownames(pData(test_monocytes))
## 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 'pData': object 'test_monocytes' not found
addition_string <- ""
for (sv in colnames(model_adjust)) {
  addition_string <- paste0(addition_string, " + ", sv)
}
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'colnames': object 'model_adjust' not found
longer_model <- as.formula(glue("~ finaloutcome + visitnumber{addition_string}"))
mono_design_svs <- cbind(test_mono_design, model_adjust)
## Error in eval(expr, envir, enclos): object 'test_mono_design' not found
summarized <- DESeq2::DESeqDataSetFromMatrix(countData = linear_mtrx,
                                             colData = mono_design_svs,
                                             design = longer_model)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'ncol': object 'linear_mtrx' not found

10.1.3 Run DESeq and compare the results to our previous invocation

In order to compare these and the previous results, I tend to rely on simple correlations and aucc plots. I have been reading the modelr code recently and it looks like there is a suite of other metrics which might be more appropriate.

deseq_run <- DESeq2::DESeq(summarized)
## Error in eval(expr, envir, enclos): object 'summarized' not found
deseq_table <- as.data.frame(DESeq2::results(object = deseq_run,
                                             contrast = c("finaloutcome", "failure", "cure"),
                                             format = "DataFrame"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'deseq_run' not found
big_table <- t_cf_monocyte_table_sva[["data"]][["outcome"]]
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_table_sva' not found
only_deseq <- big_table[, c("deseq_logfc", "deseq_adjp")]
## Error in eval(expr, envir, enclos): object 'big_table' not found
merged <- merge(deseq_table, only_deseq, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'deseq_table' not found
rownames(merged) <- merged[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'merged' not found
merged[["Row.names"]] <- NULL
## Error: object 'merged' not found
cor_value <- cor.test(merged[["log2FoldChange"]], merged[["deseq_logfc"]])
## Error in eval(expr, envir, enclos): object 'merged' not found
cor_value
## Error in eval(expr, envir, enclos): object 'cor_value' not found
logfc_plotter <- plot_linear_scatter(merged[, c("log2FoldChange", "deseq_logfc")],
                                     add_cor = TRUE, add_rsq = TRUE, identity = TRUE,
                                     add_equation = TRUE)
## Error in eval(expr, envir, enclos): object 'merged' not found
logfc_plot <- logfc_plotter[["scatter"]] +
  xlab("DESeq2 log2FC: Visit explicitly in model") +
  ylab("DESeq2 log2FC: Default pairwise comparison")
## Error in eval(expr, envir, enclos): object 'logfc_plotter' not found
pp(file = "figures/compare_cf_and_visit_in_model_monocyte_logfc.svg")
logfc_plot
## Error in eval(expr, envir, enclos): object 'logfc_plot' not found
dev.off()
## png 
##   2
logfc_plot
## Error in eval(expr, envir, enclos): object 'logfc_plot' not found
cor_value <- cor.test(merged[["padj"]], merged[["deseq_adjp"]], method = "spearman")
## Error in eval(expr, envir, enclos): object 'merged' not found
cor_value
## Error in eval(expr, envir, enclos): object 'cor_value' not found
adjp_plotter <- plot_linear_scatter(merged[, c("padj", "deseq_adjp")])
## Error in eval(expr, envir, enclos): object 'merged' not found
adjp_plot <- adjp_plotter[["scatter"]] +
  xlab("DESeq2 adjp: Visit explicitly in model") +
  ylab("DESeq2 adjp: Default pairwise comparison")
## Error in eval(expr, envir, enclos): object 'adjp_plotter' not found
pp(file = "images/compare_cf_and_visit_in_model_monocyte_adjp.svg")
adjp_plot
## Error in eval(expr, envir, enclos): object 'adjp_plot' not found
dev.off()
## png 
##   2
adjp_plot
## Error in eval(expr, envir, enclos): object 'adjp_plot' not found
previous_sig_idx <- big_table[["deseq_adjp"]] <= 0.05 &
  abs(big_table[["deseq_logfc"]] >= 1.0)
## Error in eval(expr, envir, enclos): object 'big_table' not found
summary(previous_sig_idx)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'previous_sig_idx' not found
previous_genes <- rownames(big_table)[previous_sig_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'big_table' not found
new_sig_idx <- abs(deseq_table[["log2FoldChange"]]) >= 1.0 &
  deseq_table[["padj"]] < 0.05
## Error in eval(expr, envir, enclos): object 'deseq_table' not found
new_genes <- rownames(deseq_table)[new_sig_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'deseq_table' not found
na_idx <- is.na(new_genes)
## Error in eval(expr, envir, enclos): object 'new_genes' not found
new_genes <- new_genes[!na_idx]
## Error in eval(expr, envir, enclos): object 'new_genes' not found
Vennerable::Venn(list("previous" = previous_genes, "new" = new_genes))
## Error in eval(expr, envir, enclos): object 'previous_genes' not found
test_new <- simple_gprofiler(new_genes)
## Error in eval(expr, envir, enclos): object 'new_genes' not found
test_new
## Error in eval(expr, envir, enclos): object 'test_new' not found
test_old <- simple_gprofiler(previous_genes)
## Error in eval(expr, envir, enclos): object 'previous_genes' not found
test_old
## Error in eval(expr, envir, enclos): object 'test_old' not found
new_annotated <- merge(fData(t_monocytes), deseq_table, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'y' in selecting a method for function 'merge': object 'deseq_table' not found
rownames(new_annotated) <- new_annotated[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'new_annotated' not found
new_annotated[["Row.names"]] <- NULL
## Error: object 'new_annotated' not found
write_xlsx(data = new_annotated, excel = "excel/monocyte_visit_in_model_sva_cf_new.xlsx")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'write_xlsx': object 'new_annotated' not found
old_annotated <- merge(fData(t_eosinophils), big_table, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'y' in selecting a method for function 'merge': object 'big_table' not found
rownames(old_annotated) <- old_annotated[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'old_annotated' not found
old_annotated[["Row.names"]] <- NULL
## Error: object 'old_annotated' not found
write_xlsx(data = old_annotated, excel = "excel/monocyte_visit_in_model_sva_cf_old.xlsx")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'write_xlsx': object 'old_annotated' not found

Are the expected Ensembl gene IDs found in this new set?

sum(new_genes %in% expected_ensg)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function '%in%': object 'new_genes' not found

10.2 Eosinophils

We wish to ensure that my model simplification did not do anything incorrect to the data for all three cell types, I already did this for the neutrophils, let us repeat for the eosinophils. I am therefore (mostly) copy/pasting the neutrophil section here.

## The original pairwise invocation with sva:
#t_cf_eosinophil_de_sva <- all_pairwise(t_eosinophils, model_batch = "svaseq",
#                                       filter = TRUE, parallel=FALSE, methods = methods)
test_eosinophils <- normalize_expt(t_eosinophils, filter = "simple")
## Removing 2652 low-count genes (17300 remaining).
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
test_eo_design <- pData(test_eosinophils)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'test_eosinophils' not found
test_formula <- as.formula("~ 0 + finaloutcome + visitnumber")
test_model <- model.matrix(test_formula, data = test_eo_design)
## Error in eval(expr, envir, enclos): object 'test_eo_design' not found
null_formula <- as.formula("~ 0 + visitnumber")
null_model <- model.matrix(null_formula, data = test_eo_design)
## Error in eval(expr, envir, enclos): object 'test_eo_design' not found
linear_mtrx <- exprs(test_eosinophils)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'test_eosinophils' not found
l2_mtrx <- log2(linear_mtrx + 1)
## Error in eval(expr, envir, enclos): object 'linear_mtrx' not found
chosen_surrogates <- sva::num.sv(dat = l2_mtrx, mod = test_model)
## Error in eval(expr, envir, enclos): object 'l2_mtrx' not found
chosen_surrogates
## Error in eval(expr, envir, enclos): object 'chosen_surrogates' not found
surrogate_result <- sva::svaseq(
  dat = linear_mtrx, n.sv = chosen_surrogates, mod = test_model, mod0 = null_model)
## Error in eval(expr, envir, enclos): object 'linear_mtrx' not found
model_adjust <- as.matrix(surrogate_result[["sv"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.matrix': object 'surrogate_result' not found
colnames(model_adjust) <- c("SV1", "SV2", "SV3")
## Error: object 'model_adjust' not found
rownames(model_adjust) <- rownames(pData(test_eosinophils))
## 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 'pData': object 'test_eosinophils' not found
longer_model <- as.formula("~ finaloutcome + visitnumber + SV1 + SV2 + SV3")
eo_design_svs <- cbind(test_eo_design, model_adjust)
## Error in eval(expr, envir, enclos): object 'test_eo_design' not found
summarized <- DESeq2::DESeqDataSetFromMatrix(countData = linear_mtrx,
                                             colData = eo_design_svs,
                                             design = longer_model)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'ncol': object 'linear_mtrx' not found
deseq_run <- DESeq2::DESeq(summarized)
## Error in eval(expr, envir, enclos): object 'summarized' not found
deseq_table <- as.data.frame(DESeq2::results(object = deseq_run,
                                             contrast = c("finaloutcome", "failure", "cure"),
                                             format = "DataFrame"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'deseq_run' not found
big_table <- t_cf_eosinophil_table_sva[["data"]][["outcome"]]
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_table_sva' not found
only_deseq <- big_table[, c("deseq_logfc", "deseq_adjp")]
## Error in eval(expr, envir, enclos): object 'big_table' not found
merged <- merge(deseq_table, only_deseq, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'deseq_table' not found
rownames(merged) <- merged[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'merged' not found
merged[["Row.names"]] <- NULL
## Error: object 'merged' not found
cor_value <- cor.test(merged[["log2FoldChange"]], merged[["deseq_logfc"]])
## Error in eval(expr, envir, enclos): object 'merged' not found
cor_value
## Error in eval(expr, envir, enclos): object 'cor_value' not found
logfc_plotter <- plot_linear_scatter(merged[, c("log2FoldChange", "deseq_logfc")])
## Error in eval(expr, envir, enclos): object 'merged' not found
logfc_plot <- logfc_plotter[["scatter"]] +
  xlab("DESeq2 log2FC: Visit explicitly in model") +
  ylab("DESeq2 log2FC: Default pairwise comparison")
## Error in eval(expr, envir, enclos): object 'logfc_plotter' not found
pp(file = "figures/compare_cf_and_visit_in_model_eosinophil_logfc.svg")
logfc_plot
## Error in eval(expr, envir, enclos): object 'logfc_plot' not found
dev.off()
## png 
##   2
logfc_plot
## Error in eval(expr, envir, enclos): object 'logfc_plot' not found
cor_value <- cor.test(merged[["padj"]], merged[["deseq_adjp"]], method = "spearman")
## Error in eval(expr, envir, enclos): object 'merged' not found
cor_value
## Error in eval(expr, envir, enclos): object 'cor_value' not found
adjp_plotter <- plot_linear_scatter(merged[, c("padj", "deseq_adjp")])
## Error in eval(expr, envir, enclos): object 'merged' not found
adjp_plot <- adjp_plotter[["scatter"]] +
  xlab("DESeq2 adjp: Visit explicitly in model") +
  ylab("DESeq2 adjp: Default pairwise comparison")
## Error in eval(expr, envir, enclos): object 'adjp_plotter' not found
pp(file = "images/compare_cf_and_visit_in_model_eosinophil_adjp.svg")
adjp_plot
## Error in eval(expr, envir, enclos): object 'adjp_plot' not found
dev.off()
## png 
##   2
adjp_plot
## Error in eval(expr, envir, enclos): object 'adjp_plot' not found
previous_sig_idx <- big_table[["deseq_adjp"]] <= 0.05 &
  abs(big_table[["deseq_logfc"]] >= 1.0)
## Error in eval(expr, envir, enclos): object 'big_table' not found
summary(previous_sig_idx)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'previous_sig_idx' not found
previous_genes <- rownames(big_table)[previous_sig_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'big_table' not found
new_sig_idx <- abs(deseq_table[["log2FoldChange"]]) >= 1.0 &
  deseq_table[["padj"]] < 0.05
## Error in eval(expr, envir, enclos): object 'deseq_table' not found
new_genes <- rownames(deseq_table)[new_sig_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'deseq_table' not found
na_idx <- is.na(new_genes)
## Error in eval(expr, envir, enclos): object 'new_genes' not found
new_genes <- new_genes[!na_idx]
## Error in eval(expr, envir, enclos): object 'new_genes' not found
Vennerable::Venn(list("previous" = previous_genes, "new" = new_genes))
## Error in eval(expr, envir, enclos): object 'previous_genes' not found
test_new <- simple_gprofiler(new_genes)
## Error in eval(expr, envir, enclos): object 'new_genes' not found
test_new
## Error in eval(expr, envir, enclos): object 'test_new' not found
test_old <- simple_gprofiler(previous_genes)
## Error in eval(expr, envir, enclos): object 'previous_genes' not found
test_old
## Error in eval(expr, envir, enclos): object 'test_old' not found
new_annotated <- merge(fData(t_eosinophils), deseq_table, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'y' in selecting a method for function 'merge': object 'deseq_table' not found
rownames(new_annotated) <- new_annotated[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'new_annotated' not found
new_annotated[["Row.names"]] <- NULL
## Error: object 'new_annotated' not found
write_xlsx(data = new_annotated, excel = "excel/eosinophil_visit_in_model_sva_cf_new.xlsx")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'write_xlsx': object 'new_annotated' not found
old_annotated <- merge(fData(t_eosinophils), big_table, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'y' in selecting a method for function 'merge': object 'big_table' not found
rownames(old_annotated) <- old_annotated[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'old_annotated' not found
old_annotated[["Row.names"]] <- NULL
## Error: object 'old_annotated' not found
write_xlsx(data = old_annotated, excel = "excel/eosinophil_visit_in_model_sva_cf_old.xlsx")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'write_xlsx': object 'old_annotated' not found

Check our genes of particular interest

sum(new_genes %in% expected_ensg)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function '%in%': object 'new_genes' not found

Not quite as similar as the monocyte data.

10.3 Neutrophils

## The original pairwise invocation with sva:
## t_cf_neutrophil_de_sva <- all_pairwise(t_neutrophils, model_batch = "svaseq",
##                                        parallel = parallel, filter = TRUE,
##                                        methods = methods)
test_neutrophils <- normalize_expt(t_neutrophils, filter = "simple")
## Removing 2652 low-count genes (17300 remaining).
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
test_neut_design <- pData(test_neutrophils)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'test_neutrophils' not found
test_formula <- as.formula("~ 0 + finaloutcome + visitnumber")
test_model <- model.matrix(test_formula, data = test_neut_design)
## Error in eval(expr, envir, enclos): object 'test_neut_design' not found
## Note to self: double-check that the following line is correct.
null_formula <- as.formula("~ 0 + visitnumber")
## null_model <- test_model[, c(1, 2)]
null_model <- model.matrix(null_formula, data = test_neut_design)
## Error in eval(expr, envir, enclos): object 'test_neut_design' not found
linear_mtrx <- exprs(test_neutrophils)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'test_neutrophils' not found
l2_mtrx <- log2(linear_mtrx + 1)
## Error in eval(expr, envir, enclos): object 'linear_mtrx' not found
chosen_surrogates <- sva::num.sv(dat = l2_mtrx, mod = test_model)
## Error in eval(expr, envir, enclos): object 'l2_mtrx' not found
chosen_surrogates
## Error in eval(expr, envir, enclos): object 'chosen_surrogates' not found
surrogate_result <- sva::svaseq(
  dat = linear_mtrx, n.sv = chosen_surrogates, mod = test_model, mod0 = null_model)
## Error in eval(expr, envir, enclos): object 'linear_mtrx' not found
model_adjust <- as.matrix(surrogate_result[["sv"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.matrix': object 'surrogate_result' not found
## I don't think the following is actually required, but it is weird to just have this
## unnamed matrix hangingout.
## Set the columns to the SV#s
colnames(model_adjust) <- c("SV1", "SV2", "SV3", "SV4")
## Error: object 'model_adjust' not found
## Set the rows the sample IDs
rownames(model_adjust) <- rownames(pData(test_neutrophils))
## 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 'pData': object 'test_neutrophils' not found
longer_model <- as.formula("~ finaloutcome + visitnumber + SV1 + SV2 + SV3 + SV4")
neut_design_svs <- cbind(test_neut_design, model_adjust)
## Error in eval(expr, envir, enclos): object 'test_neut_design' not found
summarized <- DESeq2::DESeqDataSetFromMatrix(countData = linear_mtrx,
                                             colData = neut_design_svs,
                                             design = longer_model)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'ncol': object 'linear_mtrx' not found
deseq_run <- DESeq2::DESeq(summarized)
## Error in eval(expr, envir, enclos): object 'summarized' not found
deseq_table <- as.data.frame(DESeq2::results(object = deseq_run,
                                             contrast = c("finaloutcome", "failure", "cure"),
                                             format = "DataFrame"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'deseq_run' not found
## We should be able to directly compare this to the the deseq columns from the above
## data structure named: t_cf_neutrophil_table_sva

big_table <- t_cf_neutrophil_table_sva[["data"]][["outcome"]]
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_table_sva' not found
only_deseq <- big_table[, c("deseq_logfc", "deseq_adjp")]
## Error in eval(expr, envir, enclos): object 'big_table' not found
merged <- merge(deseq_table, only_deseq, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'deseq_table' not found
rownames(merged) <- merged[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'merged' not found
merged[["Row.names"]] <- NULL
## Error: object 'merged' not found
cor_value <- cor.test(merged[["log2FoldChange"]], merged[["deseq_logfc"]])
## Error in eval(expr, envir, enclos): object 'merged' not found
cor_value
## Error in eval(expr, envir, enclos): object 'cor_value' not found
logfc_plotter <- plot_linear_scatter(merged[, c("log2FoldChange", "deseq_logfc")])
## Error in eval(expr, envir, enclos): object 'merged' not found
logfc_plot <- logfc_plotter[["scatter"]] +
  xlab("DESeq2 log2FC: Visit explicitly in model") +
  ylab("DESeq2 log2FC: Default pairwise comparison")
## Error in eval(expr, envir, enclos): object 'logfc_plotter' not found
pp(file = "figures/compare_cf_and_visit_in_model_neutrophil_logfc.svg")
logfc_plot
## Error in eval(expr, envir, enclos): object 'logfc_plot' not found
dev.off()
## png 
##   2
logfc_plot
## Error in eval(expr, envir, enclos): object 'logfc_plot' not found
cor_value <- cor.test(merged[["padj"]], merged[["deseq_adjp"]], method = "spearman")
## Error in eval(expr, envir, enclos): object 'merged' not found
cor_value
## Error in eval(expr, envir, enclos): object 'cor_value' not found
adjp_plotter <- plot_linear_scatter(merged[, c("padj", "deseq_adjp")])
## Error in eval(expr, envir, enclos): object 'merged' not found
adjp_plot <- adjp_plotter[["scatter"]] +
  xlab("DESeq2 adjp: Visit explicitly in model") +
  ylab("DESeq2 adjp: Default pairwise comparison")
## Error in eval(expr, envir, enclos): object 'adjp_plotter' not found
pp(file = "images/compare_cf_and_visit_in_model_neutrophil_adjp.svg")
adjp_plot
## Error in eval(expr, envir, enclos): object 'adjp_plot' not found
dev.off()
## png 
##   2
adjp_plot
## Error in eval(expr, envir, enclos): object 'adjp_plot' not found
previous_sig_idx <- big_table[["deseq_adjp"]] <= 0.05 &
  abs(big_table[["deseq_logfc"]] >= 1.0)
## Error in eval(expr, envir, enclos): object 'big_table' not found
summary(previous_sig_idx)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'previous_sig_idx' not found
previous_genes <- rownames(big_table)[previous_sig_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'big_table' not found
new_sig_idx <- abs(deseq_table[["log2FoldChange"]]) >= 1.0 &
  deseq_table[["padj"]] < 0.05
## Error in eval(expr, envir, enclos): object 'deseq_table' not found
new_genes <- rownames(deseq_table)[new_sig_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'deseq_table' not found
na_idx <- is.na(new_genes)
## Error in eval(expr, envir, enclos): object 'new_genes' not found
new_genes <- new_genes[!na_idx]
## Error in eval(expr, envir, enclos): object 'new_genes' not found
Vennerable::Venn(list("previous" = previous_genes, "new" = new_genes))
## Error in eval(expr, envir, enclos): object 'previous_genes' not found
test_new <- simple_gprofiler(new_genes)
## Error in eval(expr, envir, enclos): object 'new_genes' not found
test_new
## Error in eval(expr, envir, enclos): object 'test_new' not found
test_old <- simple_gprofiler(previous_genes)
## Error in eval(expr, envir, enclos): object 'previous_genes' not found
test_old
## Error in eval(expr, envir, enclos): object 'test_old' not found
new_annotated <- merge(fData(t_neutrophils), deseq_table, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'y' in selecting a method for function 'merge': object 'deseq_table' not found
rownames(new_annotated) <- new_annotated[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'new_annotated' not found
new_annotated[["Row.names"]] <- NULL
## Error: object 'new_annotated' not found
write_xlsx(data = new_annotated, excel = "excel/neutrophil_visit_in_model_sva_cf_new.xlsx")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'write_xlsx': object 'new_annotated' not found
old_annotated <- merge(fData(t_neutrophils), big_table, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'y' in selecting a method for function 'merge': object 'big_table' not found
rownames(old_annotated) <- old_annotated[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'old_annotated' not found
old_annotated[["Row.names"]] <- NULL
## Error: object 'old_annotated' not found
write_xlsx(data = old_annotated, excel = "excel/neutrophil_visit_in_model_sva_cf_old.xlsx")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'write_xlsx': object 'old_annotated' not found

Once again, see how many of our favorite genes are here

sum(new_genes %in% expected_ensg)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function '%in%': object 'new_genes' not found

11 Mixed linear models

When the above work was reviewed for publication, one concern raised arose because we are not considering the variance of each person in the contrasts above and are potentially over-representing the significance/power of the results because the models we are using do not include the donor. My previous understanding was that it is sufficient to include visit in the model because that would result in a model matrix which separates samples from each person; but I am now reasonably certain this is incorrect.

Therefore, the previous couple of blocks I now think are not approaching this problem correctly. We spent some time talking with Neal and discussing the various models and methods we employed. He made a series of suggestions about ways which might prove more correct. It seems that a mixed linear model is the most appropriate method for this type of query. I think I can perform that with limma, via voom. Let us try and see what happens. After doing some reading, I think the most appropriate way to perform this is to use dream() from varianceParition, which is cool because I really like it.

As I write this, we are reasonably certain that a mixed linear model provides a statistically correct framework for representing our expression data as a function of finaloutcome, visit, and person, e.g:

exprs ~ finaloutcome + visit + (1|donor)

In our discussions surrounding the various ways to compare/contrast the various results with/out the mixed linear model; there were a few primary goals laid out by Maria Adelaida and Neal. The goal is to observe if/how well our previous analyses agree with results obtained using a mixed linear model. There are a couple of caveats:

  1. The lmm is not available for data in a negative binomial distribution. Ergo, DESeq2/EdgeR are out a priori. This is a little sad because we have generally relied upon DESeq2 results. However, I do routinely compare DESeq2 to voom->limma and am usually impressed at the degree of similarity.
  2. lmm analyses are significantly more computationally expensive. When I have played with them via variancePartition in the past I have run my very nice machine OoM on more than a few occasions. This is important, because I want to have everything in my container, but I cannot expect any else’s computer to have > 200G RAM. I can definitely lower the parallel processing requirements to save memory, but then these will take forever (well, probably a couple days to a week).

So, with that in mind, Maria Adelaida, Najib, and Neal focused on repeating a useful subset of the analyses using the lmm and comparing them to our extant results rather than re-implementing everything. The following are the things they suggested are the most important comparison points:

  1. Repeat this process, clean it up for: monocytes/neutrophils
  2. Compare the results when using models which are (note that this way of writing fixes the slope of each donor’s model but allows the intercept to change):
    1. ~ finaloutcome + visitnumber + (1|donor)
    2. ~ finaloutcome + visitnumber
    3. ~ finaloutcome + (1|donor)
  3. Compare the results from limma for a,b,c (really, they asked me to only focus on a,b; I wanted to compare c as well)
  4. Extract the set of ‘significant’ genes via logFC/pvalue for all of the above and see the shared/unique genes.

I have already written a skeleton function ‘dream_pairwise()’ as a sibling to my other *_pairwise() functions. I think that with some minor modifications (or maybe none at all, when I wrote it I was thinking about fun models that variancePartition supports) it can accept the mixed linear model of interest.

11.1 Using a mixed linear model with dream

In the following block, the mixed formula will get passed to dream. I set the code to use the first element (after the intercept) as the ‘condition’ factor. Thus if I had made the model ‘~ 0 + visitnumber + finaloutcome + (1|donor)’, it would compare visits.

The dream_pairwise() function is responsible for making sure the variancePartition replacement functions are used for things like voom, lmfit, ebayes, and toptable. Strangely, some of them will automatically fall back to limma’s functions if there is no random-effect in the model, but others will not. As a result, I have a check and invoke the appropriate functions explicitly in dream_pairwise().

mixed_fstring <- "~ 0 + finaloutcome + visitnumber + (1|donor)"
mixed_form <- as.formula(mixed_fstring)
get_formula_factors(mixed_form)
## $type
## [1] "cellmeans"
## 
## $interaction
## [1] FALSE
## 
## $mixed
## [1] TRUE
## 
## $mixers
## [1] "1"     "donor"
## 
## $cellmeans_intercept
## [1] "0"
## 
## $factors
## [1] "finaloutcome" "visitnumber"  "donor"       
## 
## $contrast
## [1] "finaloutcome"
mixed_eosinophil_de <- dream_pairwise(t_eosinophils, alt_model = mixed_form)
## libsize was not specified, this parameter has profound effects on limma's result.
## Using the libsize from expt$libsize.
## Warning in createContrastL(objFlt$formula, objFlt$data, L): Contrasts with only
## a single non-zero term are already evaluated by default.
mixed_eosinophil_de_xlsx <- write_de_table(
  mixed_eosinophil_de, type = "limma",
  excel = glue("excel/mixed_eosinophil_table-v{ver}.xlsx"))

mixed_monocyte_de <- dream_pairwise(t_monocytes, alt_model = mixed_form)
## libsize was not specified, this parameter has profound effects on limma's result.
## Using the libsize from expt$libsize.
## Warning in createContrastL(objFlt$formula, objFlt$data, L): Contrasts with only
## a single non-zero term are already evaluated by default.
mixed_monocyte_de_xlsx <- write_de_table(
  mixed_monocyte_de, type = "limma",
  excel = glue("excel/mixed_monocyte_table-v{ver}.xlsx"))

mixed_neutrophil_de <- dream_pairwise(t_neutrophils, alt_model = mixed_form)
## libsize was not specified, this parameter has profound effects on limma's result.
## Using the libsize from expt$libsize.
## Warning in createContrastL(objFlt$formula, objFlt$data, L): Contrasts with only
## a single non-zero term are already evaluated by default.
mixed_neutrophil_de_xlsx <- write_de_table(
  mixed_neutrophil_de, type = "limma",
  excel = glue("excel/mixed_neutrophil_table-v{ver}.xlsx"))

11.2 Using the same method without the mixed model

In other words, the following invocations will go much faster and likely be nearly (or completely) identical to the results from limma using the same model since the ‘mixed_fstring_fv’ does not have a random effect.

mixed_fstring_fv <- "~ 0 + finaloutcome + visitnumber"
mixed_form_fv <- as.formula(mixed_fstring_fv)
get_formula_factors(mixed_form_fv)
## $type
## [1] "cellmeans"
## 
## $interaction
## [1] FALSE
## 
## $mixed
## [1] FALSE
## 
## $cellmeans_intercept
## [1] "0"
## 
## $factors
## [1] "finaloutcome" "visitnumber" 
## 
## $contrast
## [1] "finaloutcome"
mixed_eosinophil_fv_de <- dream_pairwise(t_eosinophils, alt_model = mixed_form_fv)
## libsize was not specified, this parameter has profound effects on limma's result.
## Using the libsize from expt$libsize.
## Warning in createContrastL(objFlt$formula, objFlt$data, L): Contrasts with only
## a single non-zero term are already evaluated by default.
mixed_eosinophil_de_nodonor_xlsx <- write_de_table(
  mixed_eosinophil_fv_de, type = "limma",
  excel = glue("excel/mixed_eosinophil_nodonor_table-v{ver}.xlsx"))

mixed_monocyte_fv_de <- dream_pairwise(t_monocytes, alt_model = mixed_form_fv)
## libsize was not specified, this parameter has profound effects on limma's result.
## Using the libsize from expt$libsize.
## Warning in createContrastL(objFlt$formula, objFlt$data, L): Contrasts with only
## a single non-zero term are already evaluated by default.
mixed_monocyte_de_nodonor_xlsx <- write_de_table(mixed_monocyte_fv_de, type = "limma",
                                                 excel = glue("excel/mixed_monocyte_nodonor_table-v{ver}.xlsx"))

mixed_neutrophil_fv_de <- dream_pairwise(t_neutrophils, alt_model = mixed_form_fv)
## libsize was not specified, this parameter has profound effects on limma's result.
## Using the libsize from expt$libsize.
## Warning in createContrastL(objFlt$formula, objFlt$data, L): Contrasts with only
## a single non-zero term are already evaluated by default.
mixed_neutrophil_de_nodonor_xlsx <- write_de_table(mixed_neutrophil_fv_de, type = "limma",
                                                   excel = glue("excel/mixed_neutrophil_nodonor_table-v{ver}.xlsx"))

11.3 Comparing the results

There are a couple observations here which are important and/or troubling:

  1. Using the lmm results in no genes with a significant FDR adjusted p-value. This supports the hypothesis that we over-represented the significance of the data in our original analysis I think in a pretty compelling fashion.
  2. However, there is this interesting note from the dream documentation: “Since dream uses an estimated degrees of freedom value for each hypothesis test, the degrees of freedom is different for each gene here. Therefore, the t-statistics are not directly comparable since they have different degrees of freedom. In order to be able to compare test statistics, we report z.std which is the p-value transformed into a signed z-score. This can be used for downstream analysis.”
  3. I spent some time reading the R markdown documents at https://github.com/GabrielHoffman/dream_analysis.git which accompany the paper (Hoffman and Roussos (2021)) and found that there is only one instance in which they make use of adjusted p-values; at the very end of the iPSC data. In addition they only use the zstd metric when pulling gene sets for comparing against GO categories. In all other instances, the metric used for significance is the ‘raw’ p-value.

11.3.1 A little function to print overlaps

Najib asked if I would compare the set of overlapping genes observed with the various significance metrics provided. I think I should write a little function to do this because there are ample opportunities for typeos.

deseq_df <- t_cf_monocyte_table_sva[["data"]][["outcome"]]
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_table_sva' not found
deseq_gene_idx <- abs(deseq_df[["deseq_logfc"]]) >= 1.0 &
  deseq_df[["deseq_adjp"]] <= 0.05
## Error in eval(expr, envir, enclos): object 'deseq_df' not found
deseq_symb <- annot[deseq_gene_idx, "hgnc_symbol"]
## Error in eval(expr, envir, enclos): object 'deseq_gene_idx' not found
deseq_symb
## Error in eval(expr, envir, enclos): object 'deseq_symb' not found
deseq_genes <- rownames(annot)[deseq_gene_idx]
## Error in eval(expr, envir, enclos): object 'deseq_gene_idx' not found
overlap_sig <- function(mixed, deseq = deseq_genes, mixed_pcol = "P.Value",
                        annot = fData(t_monocytes), mixed_cutoff = 0.05, direction = "lt",
                        expected = expected_genes) {
  if (direction == "lt") {
    mixed_sig_idx <- abs(mixed[["logFC"]]) >= 1.0 &
      mixed[[mixed_pcol]] <= mixed_cutoff
  } else {
    mixed_sig_idx <- abs(mixed[["logFC"]]) >= 1.0 &
      mixed[[mixed_pcol]] >= mixed_cutoff
  }
  mixed_genes <- rownames(mixed)[mixed_sig_idx]
  venn_lst <- list(
    "mixed_model" = mixed_genes,
    "DESeq_sva" = deseq)
  mixed_deseq_comp <- Vennerable::Venn(venn_lst)
  Vennerable::plot(mixed_deseq_comp)
  mixed_ensg <- mixed_deseq_comp@IntersectionSets[["11"]]
  overlap_genes <- annot[mixed_ensg, "hgnc_symbol"]
  message("The set of all overlapping genes:")
  print(overlap_genes)
  found_idx <- expected %in% overlap_genes
  message("Overlapping genes in the 10 favorites:")
  print(expected[found_idx])
}

11.3.2 Monocytes

In this block I am looking at the similarities between the mixed model with donor and without donor (which is no longer a mixed model; it is just using the dream functions (which I am pretty sure just fall back to limma when there is not a random effect)).

monocyte_visit_with_donor <- mixed_monocyte_de[["all_tables"]][["failure_vs_cure"]]
monocyte_visit_without_donor <- mixed_monocyte_fv_de[["all_tables"]][["failure_vs_cure"]]
donor_aucc <- calculate_aucc(monocyte_visit_with_donor, monocyte_visit_without_donor,
                             px = "adj.P.Val", py = "adj.P.Val",
                             lx = "logFC", ly = "logFC")
donor_aucc
## These two tables have an aucc value of: 0.549656147547975 and correlation:
## 
##  Pearson's product-moment correlation
## 
## data:  tbl[[lx]] and tbl[[ly]]
## t = 1001, df = 19950, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.9899 0.9905
## sample estimates:
##    cor 
## 0.9902

with_donor_genes <- abs(monocyte_visit_with_donor[["logFC"]]) >= 1.0 &
  monocyte_visit_with_donor[["P.Value"]] <= 0.05
without_donor_genes <- abs(monocyte_visit_without_donor[["logFC"]]) >= 1.0 &
  monocyte_visit_with_donor[["P.Value"]] <= 0.05
donor_genes <- rownames(monocyte_visit_with_donor)[with_donor_genes]
donor_z_idx <- abs(monocyte_visit_with_donor[["logFC"]]) >= 1.0 &
  monocyte_visit_with_donor[["z.std"]] >= 1.0
donor_z_genes <- rownames(monocyte_visit_with_donor)[donor_z_idx]

overlap_sig(monocyte_visit_with_donor)
## Error in overlap_sig(monocyte_visit_with_donor): object 'deseq_genes' not found
overlap_sig(monocyte_visit_with_donor,
            mixed_pcol = "z.std", direction = "gt", mixed_cutoff = 1.5)
## Error in overlap_sig(monocyte_visit_with_donor, mixed_pcol = "z.std", : object 'deseq_genes' not found

I would have sworn that the 2.0 z-score set was much larger than the p-value set and included all of the 10 genes. Apparently I was very wrong.

11.3.3 Neutrophils

Now examine the various models for the neutrophil samples.

neutrophil_visit_with_donor <- mixed_neutrophil_de[["all_tables"]][["failure_vs_cure"]]
neutrophil_visit_without_donor <- mixed_neutrophil_fv_de[["all_tables"]][["failure_vs_cure"]]
donor_aucc <- calculate_aucc(neutrophil_visit_with_donor, neutrophil_visit_without_donor,
                             px = "adj.P.Val", py = "adj.P.Val",
                             lx = "logFC", ly = "logFC")
donor_aucc
## These two tables have an aucc value of: 0.544934636423573 and correlation:
## 
##  Pearson's product-moment correlation
## 
## data:  tbl[[lx]] and tbl[[ly]]
## t = 742, df = 19950, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.9819 0.9828
## sample estimates:
##    cor 
## 0.9824

with_donor_genes <- abs(neutrophil_visit_with_donor[["logFC"]]) >= 1.0 &
  neutrophil_visit_with_donor[["P.Value"]] <= 0.05
without_donor_genes <- abs(neutrophil_visit_without_donor[["logFC"]]) >= 1.0 &
  neutrophil_visit_with_donor[["P.Value"]] <= 0.05
donor_genes <- rownames(neutrophil_visit_with_donor)[with_donor_genes]
visit_genes <- rownames(neutrophil_visit_with_donor)[without_donor_genes]
venn_lst <- list(
  "with_donor" = donor_genes,
  "with_visit" = visit_genes)
Vennerable::Venn(venn_lst)
## A Venn object on 2 sets named
## with_donor,with_visit 
##  00  10  01  11 
##   0   0  15 214
overlap_sig(neutrophil_visit_with_donor)
## Error in overlap_sig(neutrophil_visit_with_donor): object 'deseq_genes' not found
overlap_sig(neutrophil_visit_with_donor,
            mixed_pcol = "z.std", direction = "gt", mixed_cutoff = 1.5)
## Error in overlap_sig(neutrophil_visit_with_donor, mixed_pcol = "z.std", : object 'deseq_genes' not found

11.3.4 Eosinophils

Finally, compare for the eosinophil samples.

eosinophil_visit_with_donor <- mixed_eosinophil_de[["all_tables"]][["failure_vs_cure"]]
eosinophil_visit_without_donor <- mixed_eosinophil_fv_de[["all_tables"]][["failure_vs_cure"]]
donor_aucc <- calculate_aucc(eosinophil_visit_with_donor, eosinophil_visit_without_donor,
                             px = "adj.P.Val", py = "adj.P.Val",
                             lx = "logFC", ly = "logFC")
donor_aucc
## These two tables have an aucc value of: 0.90324615179282 and correlation:
## 
##  Pearson's product-moment correlation
## 
## data:  tbl[[lx]] and tbl[[ly]]
## t = 746, df = 19950, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.982 0.983
## sample estimates:
##    cor 
## 0.9825

with_donor_genes <- abs(eosinophil_visit_with_donor[["logFC"]]) >= 1.0 &
  eosinophil_visit_with_donor[["P.Value"]] <= 0.05
without_donor_genes <- abs(eosinophil_visit_without_donor[["logFC"]]) >= 1.0 &
  eosinophil_visit_with_donor[["P.Value"]] <= 0.05
donor_genes <- rownames(eosinophil_visit_with_donor)[with_donor_genes]
visit_genes <- rownames(eosinophil_visit_with_donor)[without_donor_genes]
venn_lst <- list(
  "with_donor" = donor_genes,
  "with_visit" = visit_genes)
Vennerable::Venn(venn_lst)
## A Venn object on 2 sets named
## with_donor,with_visit 
##   00   10   01   11 
##    0    0   26 3709
overlap_sig(eosinophil_visit_with_donor)
## Error in overlap_sig(eosinophil_visit_with_donor): object 'deseq_genes' not found
overlap_sig(eosinophil_visit_with_donor,
            mixed_pcol = "z.std", direction = "gt", mixed_cutoff = 1.5)
## Error in overlap_sig(eosinophil_visit_with_donor, mixed_pcol = "z.std", : object 'deseq_genes' not found

Compare back to deseq with SVA and with SVA+visit and see how they look with respect to the dream invocation without the random donor effect.

deseq_aucc <- calculate_aucc(merged, monocyte_visit_without_donor,
                             px = "deseq_adjp", py = "P.Value",
                             lx = "deseq_logfc", ly = "logFC")
## Error in eval(expr, envir, enclos): object 'merged' not found
deseq_aucc
## Error in eval(expr, envir, enclos): object 'deseq_aucc' not found
deseq_genes_idx <- abs(merged[["deseq_logfc"]]) >= 1.0 &
  merged[["deseq_adjp"]] <= 0.05
## Error in eval(expr, envir, enclos): object 'merged' not found
without_donor_genes_idx <- abs(monocyte_visit_without_donor[["logFC"]]) >= 1.0 &
  monocyte_visit_with_donor[["P.Value"]] <= 0.05
deseq_genes <- rownames(merged)[deseq_genes_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'merged' not found
visit_genes <- rownames(monocyte_visit_with_donor)[without_donor_genes_idx]
venn_lst <- list(
  "with_donor" = deseq_genes,
  "with_visit" = visit_genes)
## Error in eval(expr, envir, enclos): object 'deseq_genes' not found
Vennerable::Venn(venn_lst)
## A Venn object on 2 sets named
## with_donor,with_visit 
##   00   10   01   11 
##    0    0   26 3709

This time we are comparing back to the monocyte results which did not include the random donor effect.

deseq_aucc <- calculate_aucc(merged, monocyte_visit_without_donor,
                             px = "log2FoldChange", py = "padj",
                             lx = "adj.P.Val", ly = "logFC")
## Error in eval(expr, envir, enclos): object 'merged' not found
deseq_aucc
## Error in eval(expr, envir, enclos): object 'deseq_aucc' not found
deseq_genes_idx <- abs(merged[["log2FoldChange"]]) >= 1.0 &
  merged[["padj"]] <= 0.05
## Error in eval(expr, envir, enclos): object 'merged' not found
without_donor_genes_idx <- abs(monocyte_visit_without_donor[["logFC"]]) >= 1.0 &
  monocyte_visit_with_donor[["P.Value"]] <= 0.05
deseq_genes <- rownames(merged)[deseq_genes_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'merged' not found
visit_genes <- rownames(monocyte_visit_with_donor)[without_donor_genes_idx]
venn_lst <- list(
  "with_donor" = deseq_genes,
  "with_visit" = visit_genes)
## Error in eval(expr, envir, enclos): object 'deseq_genes' not found
Vennerable::Venn(venn_lst)
## A Venn object on 2 sets named
## with_donor,with_visit 
##   00   10   01   11 
##    0    0   26 3709

This is the orthologous approach: include a random effect for donor and ignore the visit effect.

mixed_fstring_fd <- "~ 0 + finaloutcome + (1|donor)"
mixed_form_fd <- as.formula(mixed_fstring_fd)
get_formula_factors(mixed_form_fd)
## $type
## [1] "cellmeans"
## 
## $interaction
## [1] FALSE
## 
## $mixed
## [1] TRUE
## 
## $mixers
## [1] "1"     "donor"
## 
## $cellmeans_intercept
## [1] "0"
## 
## $factors
## [1] "finaloutcome" "donor"       
## 
## $contrast
## [1] "finaloutcome"
mixed_eosinophil_fd_de <- dream_pairwise(t_eosinophils, alt_model = mixed_form_fd)
## libsize was not specified, this parameter has profound effects on limma's result.
## Using the libsize from expt$libsize.
## Warning in createContrastL(objFlt$formula, objFlt$data, L): Contrasts with only
## a single non-zero term are already evaluated by default.
mixed_monocyte_fd_de <- dream_pairwise(t_monocytes, alt_model = mixed_form_fd)
## libsize was not specified, this parameter has profound effects on limma's result.
## Using the libsize from expt$libsize.
## Warning in createContrastL(objFlt$formula, objFlt$data, L): Contrasts with only
## a single non-zero term are already evaluated by default.
mixed_neutrophil_fd_de <- dream_pairwise(t_neutrophils, alt_model = mixed_form_fd)
## libsize was not specified, this parameter has profound effects on limma's result.
## Using the libsize from expt$libsize.
## Warning in createContrastL(objFlt$formula, objFlt$data, L): Contrasts with only
## a single non-zero term are already evaluated by default.

11.3.5 Compare monocytes

Now see how these results compare against our previous results…

monocyte_dream_result <- mixed_monocyte_de[["all_tables"]][["failure_vs_cure"]]

big_table <- t_cf_monocyte_table_sva[["data"]][["outcome"]]
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_table_sva' not found
merged <- merge(big_table, monocyte_dream_result, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'big_table' not found
rownames(merged) <- merged[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'merged' not found
merged[["Row.names"]] <- NULL
## Error: object 'merged' not found
cor_value <- cor.test(merged[["logFC"]], merged[["deseq_logfc"]])
## Error in eval(expr, envir, enclos): object 'merged' not found
cor_value
## Error in eval(expr, envir, enclos): object 'cor_value' not found
t_cf_monocyte_de_sva[["dream"]] <- mixed_monocyte_de
## Error: object 't_cf_monocyte_de_sva' not found
test <- combine_de_tables(
  t_cf_monocyte_de_sva, scale_p = TRUE,
  excel = "excel/test_monocyte_combined.xlsx")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_monocyte_de_sva' not found
test_aucc <- calculate_aucc(merged,
                            px = "deseq_adjp", py = "adj.P.Val",
                            lx = "deseq_logfc", ly = "logFC")
## Error in eval(expr, envir, enclos): object 'merged' not found
test_aucc[["plot"]]
## Error in eval(expr, envir, enclos): object 'test_aucc' not found
test_aucc[["scatter"]][["scatter"]]
## Error in eval(expr, envir, enclos): object 'test_aucc' not found
logfc_plotter <- plot_linear_scatter(merged[, c("logFC", "deseq_logfc")])
## Error in eval(expr, envir, enclos): object 'merged' not found
logfc_plot <- logfc_plotter[["scatter"]] +
  xlab("Dream log2FC with (1|donor) and visit in model") +
  ylab("DESeq2 log2FC: Default pairwise comparison")
## Error in eval(expr, envir, enclos): object 'logfc_plotter' not found
pp(file = "figures/compare_cf_and_full_dream_monocyte_logfc.svg")
logfc_plot
## Error in eval(expr, envir, enclos): object 'logfc_plot' not found
dev.off()
## png 
##   2
logfc_plot
## Error in eval(expr, envir, enclos): object 'logfc_plot' not found
previous_sig_idx <- merged[["deseq_adjp"]] <= 0.05 & abs(merged[["deseq_logfc"]] >= 1.0)
## Error in eval(expr, envir, enclos): object 'merged' not found
summary(previous_sig_idx)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'previous_sig_idx' not found
previous_genes <- rownames(merged)[previous_sig_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'merged' not found
new_sig_idx <- abs(merged[["logFC"]]) >= 1.0 & merged[["P.Value"]] < 0.05
## Error in eval(expr, envir, enclos): object 'merged' not found
summary(new_sig_idx)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'new_sig_idx' not found
new_genes <- rownames(merged)[new_sig_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'merged' not found
na_idx <- is.na(new_genes)
## Error in eval(expr, envir, enclos): object 'new_genes' not found
new_genes <- new_genes[!na_idx]
## Error in eval(expr, envir, enclos): object 'new_genes' not found
annot <- fData(t_monocytes)
compare <- Vennerable::Venn(list("previous" = previous_genes, "new" = new_genes))
## Error in eval(expr, envir, enclos): object 'previous_genes' not found
shared_genes <- compare@IntersectionSets[["11"]]
## Error in compare@IntersectionSets: no applicable method for `@` applied to an object of class "function"
name_idx <- rownames(annot) %in% shared_genes
## Error in h(simpleError(msg, call)): error in evaluating the argument 'table' in selecting a method for function '%in%': object 'shared_genes' not found
Vennerable::plot(compare)
## Error in compare.numeric(x): argument "y" is missing, with no default
annot[name_idx, ]
## Error in eval(expr, envir, enclos): object 'name_idx' not found

11.3.6 Neutrophils

neutrophil_dream_result <- mixed_neutrophil_de[["all_tables"]][["failure_vs_cure"]]

big_table <- t_cf_neutrophil_table_sva[["data"]][["outcome"]]
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_table_sva' not found
merged <- merge(big_table, neutrophil_dream_result, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'big_table' not found
rownames(merged) <- merged[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'merged' not found
merged[["Row.names"]] <- NULL
## Error: object 'merged' not found
cor_value <- cor.test(merged[["logFC"]], merged[["deseq_logfc"]])
## Error in eval(expr, envir, enclos): object 'merged' not found
cor_value
## Error in eval(expr, envir, enclos): object 'cor_value' not found
t_cf_neutrophil_de_sva[["dream"]] <- mixed_neutrophil_de
## Error: object 't_cf_neutrophil_de_sva' not found
test <- combine_de_tables(
  t_cf_neutrophil_de_sva, scale_p = TRUE,
  excel = "excel/test_neutrophil_combined.xlsx")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_neutrophil_de_sva' not found
test_aucc <- calculate_aucc(merged,
                            px = "deseq_adjp", py = "adj.P.Val",
                            lx = "deseq_logfc", ly = "logFC")
## Error in eval(expr, envir, enclos): object 'merged' not found
test_aucc[["plot"]]
## Error in eval(expr, envir, enclos): object 'test_aucc' not found
test_aucc[["scatter"]][["scatter"]]
## Error in eval(expr, envir, enclos): object 'test_aucc' not found
logfc_plotter <- plot_linear_scatter(merged[, c("logFC", "deseq_logfc")])
## Error in eval(expr, envir, enclos): object 'merged' not found
logfc_plot <- logfc_plotter[["scatter"]] +
  xlab("Dream log2FC with (1|donor) and visit in model") +
  ylab("DESeq2 log2FC: Default pairwise comparison")
## Error in eval(expr, envir, enclos): object 'logfc_plotter' not found
pp(file = "figures/compare_cf_and_full_dream_neutrophil_logfc.svg")
logfc_plot
## Error in eval(expr, envir, enclos): object 'logfc_plot' not found
dev.off()
## png 
##   2
logfc_plot
## Error in eval(expr, envir, enclos): object 'logfc_plot' not found
previous_sig_idx <- merged[["deseq_adjp"]] <= 0.05 & abs(merged[["deseq_logfc"]] >= 1.0)
## Error in eval(expr, envir, enclos): object 'merged' not found
summary(previous_sig_idx)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'previous_sig_idx' not found
previous_genes <- rownames(merged)[previous_sig_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'merged' not found
new_sig_idx <- abs(merged[["logFC"]]) >= 1.0 & merged[["P.Value"]] < 0.05
## Error in eval(expr, envir, enclos): object 'merged' not found
summary(new_sig_idx)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'new_sig_idx' not found
new_genes <- rownames(merged)[new_sig_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'merged' not found
na_idx <- is.na(new_genes)
## Error in eval(expr, envir, enclos): object 'new_genes' not found
new_genes <- new_genes[!na_idx]
## Error in eval(expr, envir, enclos): object 'new_genes' not found
annot <- fData(t_neutrophils)
compare <- Vennerable::Venn(list("previous" = previous_genes, "new" = new_genes))
## Error in eval(expr, envir, enclos): object 'previous_genes' not found
shared_genes <- compare@IntersectionSets[["11"]]
## Error in compare@IntersectionSets: no applicable method for `@` applied to an object of class "function"
name_idx <- rownames(annot) %in% shared_genes
## Error in h(simpleError(msg, call)): error in evaluating the argument 'table' in selecting a method for function '%in%': object 'shared_genes' not found
annot[name_idx, ]
## Error in eval(expr, envir, enclos): object 'name_idx' not found
Vennerable::plot(compare)
## Error in compare.numeric(x): argument "y" is missing, with no default

11.3.7 Eosinophils

eosinophil_dream_result <- mixed_eosinophil_de[["all_tables"]][["failure_vs_cure"]]

big_table <- t_cf_eosinophil_table_sva[["data"]][["outcome"]]
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_table_sva' not found
merged <- merge(big_table, eosinophil_dream_result, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'big_table' not found
rownames(merged) <- merged[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'merged' not found
merged[["Row.names"]] <- NULL
## Error: object 'merged' not found
cor_value <- cor.test(merged[["logFC"]], merged[["deseq_logfc"]])
## Error in eval(expr, envir, enclos): object 'merged' not found
cor_value
## Error in eval(expr, envir, enclos): object 'cor_value' not found
t_cf_eosinophil_de_sva[["dream"]] <- mixed_eosinophil_de
## Error: object 't_cf_eosinophil_de_sva' not found
test <- combine_de_tables(
  t_cf_eosinophil_de_sva, scale_p = TRUE,
  excel = "excel/test_eosinophil_combined.xlsx")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_eosinophil_de_sva' not found
test_aucc <- calculate_aucc(merged,
                            px = "deseq_adjp", py = "adj.P.Val",
                            lx = "deseq_logfc", ly = "logFC")
## Error in eval(expr, envir, enclos): object 'merged' not found
test_aucc[["plot"]]
## Error in eval(expr, envir, enclos): object 'test_aucc' not found
test_aucc[["scatter"]][["scatter"]]
## Error in eval(expr, envir, enclos): object 'test_aucc' not found
logfc_plotter <- plot_linear_scatter(merged[, c("logFC", "deseq_logfc")])
## Error in eval(expr, envir, enclos): object 'merged' not found
logfc_plot <- logfc_plotter[["scatter"]] +
  xlab("Dream log2FC with (1|donor) and visit in model") +
  ylab("DESeq2 log2FC: Default pairwise comparison")
## Error in eval(expr, envir, enclos): object 'logfc_plotter' not found
pp(file = "figures/compare_cf_full_dream_eosinophil_logfc.svg")
logfc_plot
## Error in eval(expr, envir, enclos): object 'logfc_plot' not found
dev.off()
## png 
##   2
logfc_plot
## Error in eval(expr, envir, enclos): object 'logfc_plot' not found
previous_sig_idx <- merged[["deseq_adjp"]] <= 0.05 & abs(merged[["deseq_logfc"]] >= 1.0)
## Error in eval(expr, envir, enclos): object 'merged' not found
summary(previous_sig_idx)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'previous_sig_idx' not found
previous_genes <- rownames(merged)[previous_sig_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'merged' not found
new_sig_idx <- abs(merged[["logFC"]]) >= 1.0 & merged[["P.Value"]] < 0.05
## Error in eval(expr, envir, enclos): object 'merged' not found
summary(new_sig_idx)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'new_sig_idx' not found
new_genes <- rownames(merged)[new_sig_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'merged' not found
na_idx <- is.na(new_genes)
## Error in eval(expr, envir, enclos): object 'new_genes' not found
new_genes <- new_genes[!na_idx]
## Error in eval(expr, envir, enclos): object 'new_genes' not found
annot <- fData(t_eosinophils)
compare <- Vennerable::Venn(list("previous" = previous_genes, "new" = new_genes))
## Error in eval(expr, envir, enclos): object 'previous_genes' not found
shared_genes <- compare@IntersectionSets[["11"]]
## Error in compare@IntersectionSets: no applicable method for `@` applied to an object of class "function"
name_idx <- rownames(annot) %in% shared_genes
## Error in h(simpleError(msg, call)): error in evaluating the argument 'table' in selecting a method for function '%in%': object 'shared_genes' not found
annot[name_idx, ]
## Error in eval(expr, envir, enclos): object 'name_idx' not found
Vennerable::plot(compare)
## Error in compare.numeric(x): argument "y" is missing, with no default

12 Perform dream with all samples together and a model with all factors

Now that I have performed all of the above, I think it should be possible to have a working analysis using dream that includes celltype, visitnumber, finaloutcome, donor, and perhaps SVs.

mixed_fstring <- "~ 0 + finaloutcome + typeofcells + visitnumber + (1|donor)"
mixed_formula <- as.formula(mixed_fstring)
mixed_fstring_svs <- "~ 0 + finaloutcome + typeofcells + visitnumber + (1|donor) + svaseq_SV1 + svaseq_SV2 + svaseq_SV3 + svaseq_SV4"
mixed_formula_svs <- as.formula(mixed_fstring_svs)
all_dream_de <- dream_pairwise(t_clinical_nobiop, alt_model = mixed_formula)
## libsize was not specified, this parameter has profound effects on limma's result.
## Using the libsize from expt$libsize.
## Warning in createContrastL(objFlt$formula, objFlt$data, L): Contrasts with only
## a single non-zero term are already evaluated by default.
mixed_all_celltypes_de_xlsx <- write_de_table(
  all_dream_de, type = "limma",
  excel = glue("excel/mixed_all_celltypes_nobiop_table-v{ver}.xlsx"))
all_dream_result <- all_dream_de[["all_tables"]][["failure_vs_cure"]] %>%
  arrange(desc(logFC))
fc_sig_idx <- all_dream_result[["logFC"]] >= 1.0 & all_dream_result[["z.std"]] >= 2.0
dream_sig <- rownames(all_dream_result[fc_sig_idx, ])

svs_all_dream_de <- dream_pairwise(t_clinical_nobiop, alt_model = mixed_formula_svs)
## libsize was not specified, this parameter has profound effects on limma's result.
## Using the libsize from expt$libsize.
## Error in filterInputData(counts, formula, data, weights, useWeights = FALSE, : Variable in formula not found in data: svaseq_SV1, svaseq_SV2, svaseq_SV3, svaseq_SV4
test <- hpgl_padjust(svs_all_dream_de[["all_tables"]][["failure_vs_cure"]],
                     mean_column = "AveExpr", pvalue_column = "P.Value",
                     method = "ihw", type = "limma")
## Error in eval(expr, envir, enclos): object 'svs_all_dream_de' not found
t_clinical_outcomecell_fact <- paste0(pData(t_clinical_nobiop)[["finaloutcome"]], "_",
                                      pData(t_clinical_nobiop)[["typeofcells"]])
t_clinical_outcomecell <- t_clinical_nobiop
pData(t_clinical_outcomecell)[["outcomecell"]] <- t_clinical_outcomecell_fact
t_clinical_outcomecell <- set_expt_conditions(t_clinical_outcomecell, fact = "outcomecell")
## The numbers of samples by condition are:
## 
##    cure_eosinophils      cure_monocytes    cure_neutrophils failure_eosinophils 
##                  17                  21                  20                   9 
##   failure_monocytes failure_neutrophils 
##                  21                  21
t_clinical_outcomecell_de <- all_pairwise(t_clinical_outcomecell, keepers = outcometype_contrasts,
                                          model_batch = "svaseq")
## 
##    cure_eosinophils      cure_monocytes    cure_neutrophils failure_eosinophils 
##                  17                  21                  20                   9 
##   failure_monocytes failure_neutrophils 
##                  21                  21 
## Error in density.default(x, adjust = adj) : 'x' contains missing values
## Warning in all_adjusters(input, estimate_type = model_type, surrogates =
## surrogates): It is highly likely that the underlying reason for this error is
## too many 0's in the dataset, please try doing a filtering of the data and
## retry.
## Error in density.default(x, adjust = adj): 'x' contains missing values
mixed_fstring <- "~ 0 + condition + visitnumber + (1|donor)"
t_clinical_outcomecell_dream <- dream_pairwise(t_clinical_outcomecell,
                                               alt_model = as.formula(mixed_fstring),
                                               keepers = outcometype_contrasts)
## libsize was not specified, this parameter has profound effects on limma's result.
## Using the libsize from expt$libsize.
## Warning in createContrastL(objFlt$formula, objFlt$data, L): Contrasts with only
## a single non-zero term are already evaluated by default.
t_clinical_outcomecell_table <- write_de_table(t_clinical_outcomecell_dream,
                                               type = "limma",
                                               excel = glue("excel/mixed_clinical_outcomecell-v{ver}.xlsx"))
big_table <- t_cf_clinicalnb_table_sva[["data"]][["outcome"]]
## Error in eval(expr, envir, enclos): object 't_cf_clinicalnb_table_sva' not found
merged <- merge(big_table, all_dream_result, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'big_table' not found
rownames(merged) <- merged[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'merged' not found
merged[["Row.names"]] <- NULL
## Error: object 'merged' not found
cor_value <- cor.test(merged[["logFC"]], merged[["deseq_logfc"]])
## Error in eval(expr, envir, enclos): object 'merged' not found
cor_value
## Error in eval(expr, envir, enclos): object 'cor_value' not found
test_aucc <- calculate_aucc(merged,
                            px = "deseq_adjp", py = "adj.P.Val",
                            lx = "deseq_logfc", ly = "logFC")
## Error in eval(expr, envir, enclos): object 'merged' not found
test_aucc[["plot"]]
## Error in eval(expr, envir, enclos): object 'test_aucc' not found
test_aucc[["scatter"]][["scatter"]]
## Error in eval(expr, envir, enclos): object 'test_aucc' not found
logfc_plotter <- plot_linear_scatter(merged[, c("logFC", "deseq_logfc")])
## Error in eval(expr, envir, enclos): object 'merged' not found
logfc_plot <- logfc_plotter[["scatter"]] +
  xlab("Dream log2FC with (1|donor) and visit in model") +
  ylab("DESeq2 log2FC: Default pairwise comparison")
## Error in eval(expr, envir, enclos): object 'logfc_plotter' not found
pp(file = "images/compare_cf_and_dream_clinical_samples.png")
logfc_plot
## Error in eval(expr, envir, enclos): object 'logfc_plot' not found
dev.off()
## png 
##   2
logfc_plot
## Error in eval(expr, envir, enclos): object 'logfc_plot' not found
cor_value <- cor.test(merged[["P.Value"]], merged[["deseq_adjp"]], method = "spearman")
## Error in eval(expr, envir, enclos): object 'merged' not found
cor_value
## Error in eval(expr, envir, enclos): object 'cor_value' not found
adjp_plotter <- plot_linear_scatter(merged[, c("P.Value", "deseq_adjp")])
## Error in eval(expr, envir, enclos): object 'merged' not found
adjp_plot <- adjp_plotter[["scatter"]] +
  xlab("DESeq2 adjp: Dream not-adjusted p-value") +
  ylab("DESeq2 adjp: Default pairwise comparison")
## Error in eval(expr, envir, enclos): object 'adjp_plotter' not found
pp(file = "images/compare_cf_and_visit_in_model_monocyte_adjp.svg")
adjp_plot
## Error in eval(expr, envir, enclos): object 'adjp_plot' not found
dev.off()
## png 
##   2
adjp_plot
## Error in eval(expr, envir, enclos): object 'adjp_plot' not found
previous_sig_idx <- merged[["deseq_adjp"]] <= 0.05 & abs(merged[["deseq_logfc"]] >= 1.0)
## Error in eval(expr, envir, enclos): object 'merged' not found
summary(previous_sig_idx)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'previous_sig_idx' not found
previous_genes <- rownames(merged)[previous_sig_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'merged' not found
new_sig_idx <- abs(merged[["logFC"]]) >= 1.0 & merged[["P.Value"]] < 0.05
## Error in eval(expr, envir, enclos): object 'merged' not found
summary(new_sig_idx)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'new_sig_idx' not found
new_genes <- rownames(merged)[new_sig_idx]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'merged' not found
na_idx <- is.na(new_genes)
## Error in eval(expr, envir, enclos): object 'new_genes' not found
new_genes <- new_genes[!na_idx]
## Error in eval(expr, envir, enclos): object 'new_genes' not found
annot <- fData(t_monocytes)
compare <- Vennerable::Venn(list("previous" = previous_genes, "new" = new_genes))
## Error in eval(expr, envir, enclos): object 'previous_genes' not found
shared_genes <- compare@IntersectionSets[["11"]]
## Error in compare@IntersectionSets: no applicable method for `@` applied to an object of class "function"
name_idx <- rownames(annot) %in% shared_genes
## Error in h(simpleError(msg, call)): error in evaluating the argument 'table' in selecting a method for function '%in%': object 'shared_genes' not found
annot[name_idx, ]
## Error in eval(expr, envir, enclos): object 'name_idx' not found

Let us use the overlap_sig() from above to see how similar this result is to our DESeq2+SVA.

all_dream_table <- all_dream_de[["all_tables"]][["failure_vs_cure"]]
overlap_sig(all_dream_table)
## Error in overlap_sig(all_dream_table): object 'deseq_genes' not found
overlap_sig(all_dream_table, direction = "gt", mixed_pcol = "z.std", mixed_cutoff = 1.5)
## Error in overlap_sig(all_dream_table, direction = "gt", mixed_pcol = "z.std", : object 'deseq_genes' not found
all_dream_table_svs <- svs_all_dream_de[["all_tables"]][["failure_vs_cure"]]
## Error in eval(expr, envir, enclos): object 'svs_all_dream_de' not found
overlap_sig(all_dream_table_svs)
## Error in eval(expr, envir, enclos): object 'all_dream_table_svs' not found
overlap_sig(all_dream_table_svs, direction = "gt", mixed_pcol = "z.std", mixed_cutoff = 1.5)
## Error in eval(expr, envir, enclos): object 'all_dream_table_svs' not found

12.1 Recapitulating the 10 genes of interest

One figure I did not create is a venn diagram showing the overlap of the eosionphil, neutrophil, and monocyte results and the 10 genes shared among them all. At least in theory I should be easily able to create a similar/identical plot.

observed_eosinophils <- c(
  rownames(t_cf_eosinophil_sig_sva[["deseq"]][["ups"]][["outcome"]]),
  rownames(t_cf_eosinophil_sig_sva[["deseq"]][["downs"]][["outcome"]]))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 't_cf_eosinophil_sig_sva' not found
observed_monocytes <- c(
  rownames(t_cf_monocyte_sig_sva[["deseq"]][["ups"]][["outcome"]]),
  rownames(t_cf_monocyte_sig_sva[["deseq"]][["downs"]][["outcome"]]))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 't_cf_monocyte_sig_sva' not found
observed_neutrophils <- c(
  rownames(t_cf_neutrophil_sig_sva[["deseq"]][["ups"]][["outcome"]]),
  rownames(t_cf_neutrophil_sig_sva[["deseq"]][["downs"]][["outcome"]]))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 't_cf_neutrophil_sig_sva' not found
venn_input <- list(
  "eosinophil" = observed_eosinophils,
  "monocyte" = observed_monocytes,
  "neutrophils" = observed_neutrophils)
## Error in eval(expr, envir, enclos): object 'observed_eosinophils' not found
shared <- Vennerable::Venn(venn_input)
## Error in eval(expr, envir, enclos): object 'venn_input' not found
shared
## Error in eval(expr, envir, enclos): object 'shared' not found
Vennerable::plot(shared)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'plot': object 'shared' not found
intersect <- "eosinophil:monocyte:neutrophils"
celltype_upset <- UpSetR::upset(UpSetR::fromList(venn_input), text.scale = 2)
## Error in eval(expr, envir, enclos): object 'venn_input' not found
celltype_upset
## Error in eval(expr, envir, enclos): object 'celltype_upset' not found
celltype_shared_genes <- overlap_groups(venn_input)
## Error in eval(expr, envir, enclos): object 'venn_input' not found
celltype_geneids <- overlap_geneids(celltype_shared_genes, intersect)
## Error in eval(expr, envir, enclos): object 'celltype_shared_genes' not found
ids <- attr(celltype_shared_genes, "elements")[celltype_shared_genes[[intersect]]]
## Error in eval(expr, envir, enclos): object 'celltype_shared_genes' not found
ids
## Error in eval(expr, envir, enclos): object 'ids' not found
rows <- fData(t_monocytes)[ids, ]
## Error in eval(expr, envir, enclos): object 'ids' not found
rows[["hgnc_symbol"]]
## Error in eval(expr, envir, enclos): object 'rows' not found

Note to self, when I rendered the html, stupid R ran out of temp files and so did not actually print the darn html document, as a result I modified the render function to try to make sure there is a clean directory in which to work; testing now. If it continues to not work, I will need to remove some of the images created in this document.

13 A question of p-values

Maria Adelaida has asked about the distribution of (non)adjusted p-values produced by the various methods we employed. I use BH by default; so lets take a moment to examine the distribution of p-values and how they get adjusted by BH and a few of the other methods.

dream_pvalues <- all_dream_table[["P.Value"]]
names(dream_pvalues) <- rownames(all_dream_table)

deseq_pvalues <- t_cf_clinicalnb_table_sva[["data"]][["outcome"]][["deseq_p"]]
## Error in eval(expr, envir, enclos): object 't_cf_clinicalnb_table_sva' not found
names(deseq_pvalues) <- rownames(t_cf_clinicalnb_table_sva[["data"]][["outcome"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 't_cf_clinicalnb_table_sva' not found
## Note, my xlsx files provide these images.
plot_histogram(dream_pvalues)

plot_histogram(deseq_pvalues)
## Error in eval(expr, envir, enclos): object 'deseq_pvalues' not found

Immediately we see that the values produced have very different distributions and that, though there are many low p-values produced by dream, they are far fewer than observed by deseq.

Now consider the BH correction; using it, we rank order the p-values from lowest to highest. Then we choose a denominator for every p-value which ranges from 1 to the number of elements in the set of p-values. Finally we take the minimum between 1 and the cumulative minimum of (#pvalues/denominator) * that-pvalue. Written out the process looks like this:

test_pvalues <- deseq_pvalues
## Error in eval(expr, envir, enclos): object 'deseq_pvalues' not found
idx <- order(test_pvalues)
## Error in eval(expr, envir, enclos): object 'test_pvalues' not found
test_pvalues <- test_pvalues[idx]
## Error in eval(expr, envir, enclos): object 'test_pvalues' not found
num_pvalues <- length(test_pvalues)
## Error in eval(expr, envir, enclos): object 'test_pvalues' not found
new_pvalues <- test_pvalues
## Error in eval(expr, envir, enclos): object 'test_pvalues' not found
for (i in seq_along(test_pvalues)) {
  element <- test_pvalues[i]
  new_pvalues[i] <- min(1, cummin((num_pvalues / i) * element))
}
## Error in eval(expr, envir, enclos): object 'test_pvalues' not found
test_against <- p.adjust(test_pvalues, method = "BH")
## Error in eval(expr, envir, enclos): object 'test_pvalues' not found

So, consider for a moment the first p-values produced by deseq: 1.195e-24, 3.489e-22, 9.612e-22, 4.853e-18, 9.864e-15, 3.275e-14

The new p-values will be the (number of genes / the current position) * the current element

  • (11910 / 1) * 1.195e-24 which is 1.423e-10
  • (11910 / 2) * 3.489e-22 which is 2.078e-18
  • (11910 / 3) * 9.612e-22 which is 3.816e-18
  • (11910 / 4) * 4.853e-18 which is 1.445e-14
  • (11910 / 5) * 9.864e-15 which is 2.350e-11
  • (11910 / 6) * 3.275e-14 which is 6.501e-11

In contrast, consider the first few values from dream ordered in the same fashion: 2.162e-07, 3.757e-05, 8.119e-05, 1.664e-04, 3.123e-04, 5.600e-04

These start at values which are 1e17 higher than those from DESeq and so we can expect the resulting values to end up starting at ~ 5e11 higher than similar values. Thus when we do the math (and be amused at the fact that the number of p-values in the table is a factor of 2,3,4,5,6):

11910 * 2.16e-07: 0.002573 5955 * 3.757e-5: 0.223711 3970 * 8.119e-5: 0.322297 2978 * 1.664e-4: 0.4955 2382 * 3.123e-4: 0.743836 1985 * 5.600e-4: 1.112 which is caught by pmin() and reset to 1.

14 Print some volcano plots

Having performed all of the above, let us plot some of the results with a few labels of the top-10 genes on each side of the contrasts.

num_color <- color_choices[["clinic_cf"]][["tumaco_failure"]]
den_color <- color_choices[["clinic_cf"]][["tumaco_cure"]]

cf_monocyte_table <- t_cf_monocyte_table_sva[["data"]][["outcome"]]
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_table_sva' not found
cf_monocyte_volcano <- plot_volcano_condition_de(
  cf_monocyte_table, "outcome", label = expected_genes,
  fc_col = "deseq_logfc", p_col = "deseq_adjp", line_position = NULL,
  color_high = num_color, color_low = den_color, label_size = 6)
## Error in eval(expr, envir, enclos): object 'cf_monocyte_table' not found
pp(file = "figures/cf_monocyte_volcano_labeled.svg")
cf_monocyte_volcano[["plot"]]
## Error in eval(expr, envir, enclos): object 'cf_monocyte_volcano' not found
dev.off()
## png 
##   2
cf_monocyte_volcano[["plot"]]
## Error in eval(expr, envir, enclos): object 'cf_monocyte_volcano' not found
cf_monocyte_volcano_top10 <- plot_volcano_condition_de(
  cf_monocyte_table, "outcome", label = 10,
  fc_col = "deseq_logfc", p_col = "deseq_adjp", line_position = NULL,
  color_high = num_color, color_low = den_color, label_size = 6)
## Error in eval(expr, envir, enclos): object 'cf_monocyte_table' not found
pp(file = glue("images/cf_monocyte_volcano_labeled_top10-v{ver}.svg"))
cf_monocyte_volcano_top10[["plot"]]
## Error in eval(expr, envir, enclos): object 'cf_monocyte_volcano_top10' not found
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadev.off()
## Error in aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadev.off(): could not find function "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadev.off"
cf_monocyte_volcano_top10[["plot"]]
## Error in eval(expr, envir, enclos): object 'cf_monocyte_volcano_top10' not found
cf_eosinophil_table <- t_cf_eosinophil_table_sva[["data"]][["outcome"]]
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_table_sva' not found
cf_eosinophil_volcano <- plot_volcano_condition_de(
  cf_eosinophil_table, "outcome", label = expected_genes,
  fc_col = "deseq_logfc", p_col = "deseq_adjp", line_position = NULL,
  color_high = num_color, color_low = den_color, label_size = 6)
## Error in eval(expr, envir, enclos): object 'cf_eosinophil_table' not found
pp(file = "figures/cf_eosinophil_volcano_labeled.svg")
cf_eosinophil_volcano[["plot"]]
## Error in eval(expr, envir, enclos): object 'cf_eosinophil_volcano' not found
dev.off()
## png 
##   2
cf_eosinophil_volcano[["plot"]]
## Error in eval(expr, envir, enclos): object 'cf_eosinophil_volcano' not found
cf_eosinophil_volcano_top10 <- plot_volcano_condition_de(
  cf_eosinophil_table, "outcome", label = 10,
  fc_col = "deseq_logfc", p_col = "deseq_adjp", line_position = NULL,
  color_high = num_color, color_low = den_color, label_size = 6)
## Error in eval(expr, envir, enclos): object 'cf_eosinophil_table' not found
pp(file = glue("images/cf_eosinophil_volcano_labeled_top10-v{ver}.svg"))
cf_eosinophil_volcano_top10[["plot"]]
## Error in eval(expr, envir, enclos): object 'cf_eosinophil_volcano_top10' not found
dev.off()
## png 
##   2
cf_eosinophil_volcano_top10[["plot"]]
## Error in eval(expr, envir, enclos): object 'cf_eosinophil_volcano_top10' not found
cf_neutrophil_table <- t_cf_neutrophil_table_sva[["data"]][["outcome"]]
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_table_sva' not found
cf_neutrophil_volcano <- plot_volcano_condition_de(
  cf_neutrophil_table, "outcome", label = expected_genes,
  fc_col = "deseq_logfc", p_col = "deseq_adjp", line_position = NULL,
  color_high = num_color, color_low = den_color, label_size = 6)
## Error in eval(expr, envir, enclos): object 'cf_neutrophil_table' not found
pp(file = "figures/cf_neutrophil_volcano_labeled.svg")
cf_neutrophil_volcano[["plot"]]
## Error in eval(expr, envir, enclos): object 'cf_neutrophil_volcano' not found
dev.off()
## png 
##   2
cf_neutrophil_volcano[["plot"]]
## Error in eval(expr, envir, enclos): object 'cf_neutrophil_volcano' not found
cf_neutrophil_volcano_top10 <- plot_volcano_condition_de(
  cf_neutrophil_table, "outcome", label = 10,
  fc_col = "deseq_logfc", p_col = "deseq_adjp", line_position = NULL,
  color_high = num_color, color_low = den_color, label_size = 6)
## Error in eval(expr, envir, enclos): object 'cf_neutrophil_table' not found
pp(file = glue("images/cf_neutrophil_volcano_labeled_top10-v{ver}.svg"))
cf_neutrophil_volcano_top10[["plot"]]
## Error in eval(expr, envir, enclos): object 'cf_neutrophil_volcano_top10' not found
dev.off()
## png 
##   2
cf_neutrophil_volcano_top10[["plot"]]
## Error in eval(expr, envir, enclos): object 'cf_neutrophil_volcano_top10' not found

15 Eosinophil time comparisons

15.1 Visit 1

t_cf_eosinophil_v1_de_sva <- all_pairwise(tv1_eosinophils, model_batch = "svaseq",
                                          filter = TRUE,
                                          methods = methods)
## 
##    tumaco_cure tumaco_failure 
##              5              3
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_cf_eosinophil_v1_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v1_de_sva' not found
t_cf_eosinophil_v1_table_sva <- combine_de_tables(
  t_cf_eosinophil_v1_de_sva, keepers = t_cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Eosinophils/t_eosinophil_v1_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_eosinophil_v1_de_sva' not found
t_cf_eosinophil_v1_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v1_table_sva' not found
t_cf_eosinophil_v1_sig_sva <- extract_significant_genes(
  t_cf_eosinophil_v1_table_sva,
  excel = glue("{cf_prefix}/Eosinophils/t_eosinophil_v1_cf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v1_table_sva' not found
t_cf_eosinophil_v1_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v1_table_sva' not found
dim(t_cf_eosinophil_v1_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v1_sig_sva' not found
dim(t_cf_eosinophil_v1_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v1_sig_sva' not found

15.2 Visit 2

t_cf_eosinophil_v2_de_sva <- all_pairwise(tv2_eosinophils, model_batch = "svaseq",
                                          filter = TRUE,
                                          methods = methods)
## 
##    tumaco_cure tumaco_failure 
##              6              3
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_cf_eosinophil_v2_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v2_de_sva' not found
t_cf_eosinophil_v2_table_sva <- combine_de_tables(
  t_cf_eosinophil_v2_de_sva, keepers = t_cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Eosinophils/t_eosinophil_v2_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_eosinophil_v2_de_sva' not found
t_cf_eosinophil_v2_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v2_table_sva' not found
t_cf_eosinophil_v2_sig_sva <- extract_significant_genes(
  t_cf_eosinophil_v2_table_sva,
  excel = glue("{cf_prefix}/Eosinophils/t_eosinophil_v2_cf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v2_table_sva' not found
t_cf_eosinophil_v2_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v2_sig_sva' not found
dim(t_cf_eosinophil_v2_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v2_sig_sva' not found
dim(t_cf_eosinophil_v2_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v2_sig_sva' not found

15.3 Visit 3

t_cf_eosinophil_v3_de_sva <- all_pairwise(tv3_eosinophils, model_batch = "svaseq",
                                          filter = TRUE,
                                          methods = methods)
## 
##    tumaco_cure tumaco_failure 
##              6              3
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_cf_eosinophil_v3_de_sva
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v3_de_sva' not found
t_cf_eosinophil_v3_table_sva <- combine_de_tables(
  t_cf_eosinophil_v3_de_sva, keepers = t_cf_contrast, scale_p = TRUE,
  excel = glue("{cf_prefix}/Eosinophils/t_eosinophil_v3_cf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cf_eosinophil_v3_de_sva' not found
t_cf_eosinophil_v3_table_sva
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v3_table_sva' not found
t_cf_eosinophil_v3_sig_sva <- extract_significant_genes(
  t_cf_eosinophil_v3_table_sva,
  excel = glue("{cf_prefix}/Eosinophils/t_eosinophil_v3_cf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v3_table_sva' not found
t_cf_eosinophil_v3_sig_sva
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v3_sig_sva' not found
dim(t_cf_eosinophil_v3_sig_sva[["deseq"]][["ups"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v3_sig_sva' not found
dim(t_cf_eosinophil_v3_sig_sva[["deseq"]][["downs"]][[1]])
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_v3_sig_sva' not found

15.4 Eosinophils: Compare sva to batch-in-visit

sva_aucc <- calculate_aucc(t_cf_eosinophil_table_sva[["data"]][[1]],
                           tbl2 = t_cf_eosinophil_table_batchvisit[["data"]][[1]],
                           py = "deseq_adjp", ly = "deseq_logfc")
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_table_sva' not found
sva_aucc
## Error in eval(expr, envir, enclos): object 'sva_aucc' not found
shared_ids <- rownames(t_cf_eosinophil_table_sva[["data"]][[1]]) %in%
  rownames(t_cf_eosinophil_table_batchvisit[["data"]][[1]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function '%in%': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 't_cf_eosinophil_table_sva' not found
first <- t_cf_eosinophil_table_sva[["data"]][[1]][shared_ids, ]
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_table_sva' not found
second <- t_cf_eosinophil_table_batchvisit[["data"]][[1]][rownames(first), ]
## Error in eval(expr, envir, enclos): object 't_cf_eosinophil_table_batchvisit' not found
cor.test(first[["deseq_logfc"]], second[["deseq_logfc"]])
## Error in first[["deseq_logfc"]]: object of type 'closure' is not subsettable

15.5 Compare monocyte CF, neutrophil CF, eosinophil CF

t_mono_neut_sva_aucc <- calculate_aucc(t_cf_monocyte_table_sva[["data"]][["outcome"]],
                                       tbl2 = t_cf_neutrophil_table_sva[["data"]][["outcome"]],
                                       py = "deseq_adjp", ly = "deseq_logfc")
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_table_sva' not found
t_mono_neut_sva_aucc
## Error in eval(expr, envir, enclos): object 't_mono_neut_sva_aucc' not found
t_mono_eo_sva_aucc <- calculate_aucc(t_cf_monocyte_table_sva[["data"]][["outcome"]],
                                     tbl2 = t_cf_eosinophil_table_sva[["data"]][["outcome"]],
                                     py = "deseq_adjp", ly = "deseq_logfc")
## Error in eval(expr, envir, enclos): object 't_cf_monocyte_table_sva' not found
t_mono_eo_sva_aucc
## Error in eval(expr, envir, enclos): object 't_mono_eo_sva_aucc' not found
t_neut_eo_sva_aucc <- calculate_aucc(t_cf_neutrophil_table_sva[["data"]][["outcome"]],
                                     tbl2 = t_cf_eosinophil_table_sva[["data"]][["outcome"]],
                                     py = "deseq_adjp", ly = "deseq_logfc")
## Error in eval(expr, envir, enclos): object 't_cf_neutrophil_table_sva' not found
t_neut_eo_sva_aucc
## Error in eval(expr, envir, enclos): object 't_neut_eo_sva_aucc' not found

16 By visit

For these contrasts, we want to see fail_v1 vs. cure_v1, fail_v2 vs. cure_v2 etc. As a result, we will need to juggle the data slightly and add another set of contrasts.

16.1 Cure/Fail by visits, all cell types

t_visit_cf_all_de_sva <- all_pairwise(t_visitcf, model_batch = "svaseq",
                                      filter = TRUE,
                                      methods = methods)
## 
##    v1_cure v1_failure    v2_cure v2_failure    v3_cure v3_failure 
##         30         24         20         15         17         17
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_visit_cf_all_de_sva
## Error in eval(expr, envir, enclos): object 't_visit_cf_all_de_sva' not found
t_visit_cf_all_table_sva <- combine_de_tables(
  t_visit_cf_all_de_sva, keepers = visitcf_contrasts, scale_p = TRUE,
  excel = glue("{cf_prefix}/t_all_visitcf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_visit_cf_all_de_sva' not found
t_visit_cf_all_table_sva
## Error in eval(expr, envir, enclos): object 't_visit_cf_all_table_sva' not found
t_visit_cf_all_sig_sva <- extract_significant_genes(
  t_visit_cf_all_table_sva,
  excel = glue("{cf_prefix}/t_all_visitcf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_visit_cf_all_table_sva' not found
t_visit_cf_all_sig_sva
## Error in eval(expr, envir, enclos): object 't_visit_cf_all_sig_sva' not found

16.2 Cure/Fail by visit, Monocytes

In the following block, I am including all samples for the monocytes and splitting them up by visit and then comparing v1 cure/fail, v2 cure/fail, v3 cure/fail.

I expect that this should be more robust than the datasets of only visit 1.

visitcf_factor <- paste0(pData(t_monocytes)[["visitnumber"]], "_",
                         pData(t_monocytes)[["finaloutcome"]])
t_monocytes_visitcf <- set_expt_conditions(t_monocytes, fact = visitcf_factor)
## The numbers of samples by condition are:
## 
##    v1_cure v1_failure    v2_cure v2_failure    v3_cure v3_failure 
##          8          8          7          6          6          7
t_visit_cf_monocyte_de_sva <- all_pairwise(t_monocytes_visitcf, model_batch = "svaseq",
                                           filter = TRUE,
                                           methods = methods)
## 
##    v1_cure v1_failure    v2_cure v2_failure    v3_cure v3_failure 
##          8          8          7          6          6          7
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_visit_cf_monocyte_de_sva
## Error in eval(expr, envir, enclos): object 't_visit_cf_monocyte_de_sva' not found
t_visit_cf_monocyte_table_sva <- combine_de_tables(
  t_visit_cf_monocyte_de_sva, keepers = visitcf_contrasts, scale_p = TRUE,
  excel = glue("{cf_prefix}/Monocytes/t_monocyte_visitcf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_visit_cf_monocyte_de_sva' not found
t_visit_cf_monocyte_table_sva
## Error in eval(expr, envir, enclos): object 't_visit_cf_monocyte_table_sva' not found
t_visit_cf_monocyte_sig_sva <- extract_significant_genes(
  t_visit_cf_monocyte_table_sva,
  excel = glue("{cf_prefix}/Monocytes/t_monocyte_visitcf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_visit_cf_monocyte_table_sva' not found
t_visit_cf_monocyte_sig_sva
## Error in eval(expr, envir, enclos): object 't_visit_cf_monocyte_sig_sva' not found
t_v1fc_deseq_ma <- t_visit_cf_monocyte_table_sva[["plots"]][["v1cf"]][["deseq_ma_plots"]]
## Error in eval(expr, envir, enclos): object 't_visit_cf_monocyte_table_sva' not found
dev <- pp(file = "images/monocyte_cf_de_v1_maplot.png")
t_v1fc_deseq_ma
## Error in eval(expr, envir, enclos): object 't_v1fc_deseq_ma' not found
closed <- dev.off()
t_v1fc_deseq_ma
## Error in eval(expr, envir, enclos): object 't_v1fc_deseq_ma' not found
t_v2fc_deseq_ma <- t_visit_cf_monocyte_table_sva[["plots"]][["v2cf"]][["deseq_ma_plots"]]
## Error in eval(expr, envir, enclos): object 't_visit_cf_monocyte_table_sva' not found
dev <- pp(file = "images/monocyte_cf_de_v2_maplot.png")
t_v2fc_deseq_ma
## Error in eval(expr, envir, enclos): object 't_v2fc_deseq_ma' not found
closed <- dev.off()
t_v2fc_deseq_ma
## Error in eval(expr, envir, enclos): object 't_v2fc_deseq_ma' not found
t_v3fc_deseq_ma <- t_visit_cf_monocyte_table_sva[["plots"]][["v3cf"]][["deseq_ma_plots"]]
## Error in eval(expr, envir, enclos): object 't_visit_cf_monocyte_table_sva' not found
dev <- pp(file = "images/monocyte_cf_de_v3_maplot.png")
t_v3fc_deseq_ma
## Error in eval(expr, envir, enclos): object 't_v3fc_deseq_ma' not found
closed <- dev.off()
t_v3fc_deseq_ma
## Error in eval(expr, envir, enclos): object 't_v3fc_deseq_ma' not found

One query from Alejandro is to look at the genes shared up/down across visits. I am not entirely certain we have enough samples for this to work, but let us find out.

I am thinking this is a good place to use the AUCC curves I learned about thanks to Julie Cridland.

Note that the following is all monocyte samples, this should therefore potentially be moved up and a version of this with only the Tumaco samples put here?

v1cf <- t_visit_cf_monocyte_table_sva[["data"]][["v1cf"]]
## Error in eval(expr, envir, enclos): object 't_visit_cf_monocyte_table_sva' not found
v2cf <- t_visit_cf_monocyte_table_sva[["data"]][["v2cf"]]
## Error in eval(expr, envir, enclos): object 't_visit_cf_monocyte_table_sva' not found
v3cf <- t_visit_cf_monocyte_table_sva[["data"]][["v3cf"]]
## Error in eval(expr, envir, enclos): object 't_visit_cf_monocyte_table_sva' not found
v1_sig <- c(
  rownames(t_visit_cf_monocyte_sig_sva[["deseq"]][["ups"]][["v1cf"]]),
  rownames(t_visit_cf_monocyte_sig_sva[["deseq"]][["downs"]][["v1cf"]]))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 't_visit_cf_monocyte_sig_sva' not found
length(v1_sig)
## Error in eval(expr, envir, enclos): object 'v1_sig' not found
v2_sig <- c(
  rownames(t_visit_cf_monocyte_sig_sva[["deseq"]][["ups"]][["v2cf"]]),
  rownames(t_visit_cf_monocyte_sig_sva[["deseq"]][["downs"]][["v2cf"]]))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 't_visit_cf_monocyte_sig_sva' not found
length(v2_sig)
## Error in eval(expr, envir, enclos): object 'v2_sig' not found
v3_sig <- c(
  rownames(t_visit_cf_monocyte_sig_sva[["deseq"]][["ups"]][["v2cf"]]),
  rownames(t_visit_cf_monocyte_sig_sva[["deseq"]][["downs"]][["v2cf"]]))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 't_visit_cf_monocyte_sig_sva' not found
length(v3_sig)
## Error in eval(expr, envir, enclos): object 'v3_sig' not found
t_monocyte_visit_aucc_v2v1 <- calculate_aucc(v1cf, tbl2 = v2cf,
                                             py = "deseq_adjp", ly = "deseq_logfc")
## Error in eval(expr, envir, enclos): object 'v1cf' not found
dev <- pp(file = "images/monocyte_visit_v2v1_aucc.png")
t_monocyte_visit_aucc_v2v1[["plot"]]
## Error in eval(expr, envir, enclos): object 't_monocyte_visit_aucc_v2v1' not found
closed <- dev.off()
t_monocyte_visit_aucc_v2v1[["plot"]]
## Error in eval(expr, envir, enclos): object 't_monocyte_visit_aucc_v2v1' not found
t_monocyte_visit_aucc_v3v1 <- calculate_aucc(v1cf, tbl2 = v3cf,
                                             py = "deseq_adjp", ly = "deseq_logfc")
## Error in eval(expr, envir, enclos): object 'v1cf' not found
dev <- pp(file = "images/monocyte_visit_v3v1_aucc.png")
t_monocyte_visit_aucc_v3v1[["plot"]]
## Error in eval(expr, envir, enclos): object 't_monocyte_visit_aucc_v3v1' not found
closed <- dev.off()
t_monocyte_visit_aucc_v3v1[["plot"]]
## Error in eval(expr, envir, enclos): object 't_monocyte_visit_aucc_v3v1' not found

16.3 Cure/Fail by visit, Neutrophils

visitcf_factor <- paste0(pData(t_neutrophils)[["visitnumber"]], "_",
                         pData(t_neutrophils)[["finaloutcome"]])
t_neutrophil_visitcf <- set_expt_conditions(t_neutrophils, fact = visitcf_factor)
## The numbers of samples by condition are:
## 
##    v1_cure v1_failure    v2_cure v2_failure    v3_cure v3_failure 
##          8          8          7          6          5          7
t_visit_cf_neutrophil_de_sva <- all_pairwise(t_neutrophil_visitcf, model_batch = "svaseq",
                                             filter = TRUE,
                                             methods = methods)
## 
##    v1_cure v1_failure    v2_cure v2_failure    v3_cure v3_failure 
##          8          8          7          6          5          7
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_visit_cf_neutrophil_de_sva
## Error in eval(expr, envir, enclos): object 't_visit_cf_neutrophil_de_sva' not found
t_visit_cf_neutrophil_table_sva <- combine_de_tables(
  t_visit_cf_neutrophil_de_sva, keepers = visitcf_contrasts, scale_p = TRUE,
  excel = glue("{cf_prefix}/Neutrophils/t_neutrophil_visitcf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_visit_cf_neutrophil_de_sva' not found
t_visit_cf_neutrophil_table_sva
## Error in eval(expr, envir, enclos): object 't_visit_cf_neutrophil_table_sva' not found
t_visit_cf_neutrophil_sig_sva <- extract_significant_genes(
  t_visit_cf_neutrophil_table_sva,
  excel = glue("{cf_prefix}/Neutrophils/t_neutrophil_visitcf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_visit_cf_neutrophil_table_sva' not found
t_visit_cf_neutrophil_sig_sva
## Error in eval(expr, envir, enclos): object 't_visit_cf_neutrophil_sig_sva' not found

16.4 Cure/Fail by visit, Eosinophils

visitcf_factor <- paste0(pData(t_eosinophils)[["visitnumber"]], "_",
                         pData(t_eosinophils)[["finaloutcome"]])
t_eosinophil_visitcf <- set_expt_conditions(t_eosinophils, fact = visitcf_factor)
## The numbers of samples by condition are:
## 
##    v1_cure v1_failure    v2_cure v2_failure    v3_cure v3_failure 
##          5          3          6          3          6          3
t_visit_cf_eosinophil_de_sva <- all_pairwise(t_eosinophil_visitcf, model_batch = "svaseq",
                                             filter = TRUE,
                                             methods = methods, keepers = visitcf_contrasts)
## 
##    v1_cure v1_failure    v2_cure v2_failure    v3_cure v3_failure 
##          5          3          6          3          6          3
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_visit_cf_eosinophil_de_sva
## Error in eval(expr, envir, enclos): object 't_visit_cf_eosinophil_de_sva' not found
t_visit_cf_eosinophil_table_sva <- combine_de_tables(
  t_visit_cf_eosinophil_de_sva, keepers = visitcf_contrasts, scale_p = TRUE,
  excel = glue("{cf_prefix}/Eosinophils/t_eosinophil_visitcf_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_visit_cf_eosinophil_de_sva' not found
t_visit_cf_eosinophil_table_sva
## Error in eval(expr, envir, enclos): object 't_visit_cf_eosinophil_table_sva' not found
t_visit_cf_eosinophil_sig_sva <- extract_significant_genes(
  t_visit_cf_eosinophil_table_sva,
  excel = glue("{cf_prefix}/Eosinophils/t_eosinophil_visitcf_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_visit_cf_eosinophil_table_sva' not found
t_visit_cf_eosinophil_sig_sva
## Error in eval(expr, envir, enclos): object 't_visit_cf_eosinophil_sig_sva' not found

17 Shared genes in visit 1

Let us see how many genes are shared across these three visits using only the visit 1 data.

observed_v1_eosinophils <- c(
  rownames(t_cf_eosinophil_v1_sig_sva[["deseq"]][["ups"]][["outcome"]]),
  rownames(t_cf_eosinophil_v1_sig_sva[["deseq"]][["downs"]][["outcome"]]))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 't_cf_eosinophil_v1_sig_sva' not found
observed_v1_monocytes <- c(
  rownames(t_cf_monocyte_v1_sig_sva[["deseq"]][["ups"]][["outcome"]]),
  rownames(t_cf_monocyte_v1_sig_sva[["deseq"]][["downs"]][["outcome"]]))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 't_cf_monocyte_v1_sig_sva' not found
observed_v1_neutrophils <- c(
  rownames(t_cf_neutrophil_v1_sig_sva[["deseq"]][["ups"]][["outcome"]]),
  rownames(t_cf_neutrophil_v1_sig_sva[["deseq"]][["downs"]][["outcome"]]))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 't_cf_neutrophil_v1_sig_sva' not found
venn_input <- list(
  "eosinophil" = observed_v1_eosinophils,
  "monocyte" = observed_v1_monocytes,
  "neutrophils" = observed_v1_neutrophils)
## Error in eval(expr, envir, enclos): object 'observed_v1_eosinophils' not found
shared <- Vennerable::Venn(venn_input)
## Error in eval(expr, envir, enclos): object 'venn_input' not found
shared
## Error in eval(expr, envir, enclos): object 'shared' not found
Vennerable::plot(shared)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'plot': object 'shared' not found

Najib suggests that we should look at all cell types together at visit 1. Let us try and see what happens… Oh, I already did this in the block ‘Separate the Tumaco data by visit’ above.

Let us add a new block in which we test a concern: if we explicitly add visit to the model (with sva, potentially without too), will that change the results we observe? My assumption is that it should change the results very minimally; but we should make absolutely certain that this is true. The neutrophils are the place to test this first because they have some of the most variance observed in the data.

Therefore I want to have an instance of the pairwise contrast that has a model of ~ finaloutcome + visitnumber + SVs where the SVs come from an invocation of sva which also has finaloutcome + visitnumber before the null model.

In theory, all_pairwise() is able to do this via the argument alt_model, but it may be safer to do it manually in order to absolutely ensure that nothing unintended happens.

18 Persistence in visit 3

Having put some SL read mapping information in the sample sheet, Maria Adelaida added a new column using it with the putative persistence state on a per-sample basis. One question which arised from that: what differences are observable between the persistent yes vs. no samples on a per-cell-type basis among the visit 3 samples.

18.1 Setting up

First things first, create the datasets.

persistence_expt <- subset_expt(t_clinical, subset = "persistence=='Y'|persistence=='N'") %>%
  subset_expt(subset = 'visitnumber==3') %>%
  set_expt_conditions(fact = 'persistence')
## subset_expt(): There were 123, now there are 83 samples.
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': When the subset was taken, the resulting design has 0 members.
## persistence_biopsy <- subset_expt(persistence_expt, subset = "typeofcells=='biopsy'")
persistence_monocyte <- subset_expt(persistence_expt, subset = "typeofcells=='monocytes'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'persistence_expt' not found
persistence_neutrophil <- subset_expt(persistence_expt, subset = "typeofcells=='neutrophils'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'persistence_expt' not found
persistence_eosinophil <- subset_expt(persistence_expt, subset = "typeofcells=='eosinophils'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'subset_expt': object 'persistence_expt' not found

18.2 Take a look

See if there are any patterns which look usable.

## All
persistence_norm <- normalize_expt(persistence_expt, 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 'persistence_expt' not found
plot_pca(persistence_norm)[["plot"]]
## Error in eval(expr, envir, enclos): object 'persistence_norm' not found
persistence_nb <- normalize_expt(persistence_expt, transform = "log2", convert = "cpm",
                                 batch = "svaseq", filter = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'normalize_expt': object 'persistence_expt' not found
plot_pca(persistence_nb)[["plot"]]
## Error in eval(expr, envir, enclos): object 'persistence_nb' not found
## Biopsies
##persistence_biopsy_norm <- normalize_expt(persistence_biopsy, transform = "log2", convert = "cpm",
##                                   norm = "quant", filter = TRUE)
##plot_pca(persistence_biopsy_norm)[["plot"]]
## Insufficient data

## Monocytes
persistence_monocyte_norm <- normalize_expt(persistence_monocyte, 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 'persistence_monocyte' not found
plot_pca(persistence_monocyte_norm)[["plot"]]
## Error in eval(expr, envir, enclos): object 'persistence_monocyte_norm' not found
persistence_monocyte_nb <- normalize_expt(persistence_monocyte, transform = "log2", convert = "cpm",
                                          batch = "svaseq", filter = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'normalize_expt': object 'persistence_monocyte' not found
plot_pca(persistence_monocyte_nb)[["plot"]]
## Error in eval(expr, envir, enclos): object 'persistence_monocyte_nb' not found
## Neutrophils
persistence_neutrophil_norm <- normalize_expt(persistence_neutrophil, 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 'persistence_neutrophil' not found
plot_pca(persistence_neutrophil_norm)[["plot"]]
## Error in eval(expr, envir, enclos): object 'persistence_neutrophil_norm' not found
persistence_neutrophil_nb <- normalize_expt(persistence_neutrophil, transform = "log2", convert = "cpm",
                                            batch = "svaseq", filter = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'normalize_expt': object 'persistence_neutrophil' not found
plot_pca(persistence_neutrophil_nb)[["plot"]]
## Error in eval(expr, envir, enclos): object 'persistence_neutrophil_nb' not found
## Eosinophils
persistence_eosinophil_norm <- normalize_expt(persistence_eosinophil, 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 'persistence_eosinophil' not found
plot_pca(persistence_eosinophil_norm)[["plot"]]
## Error in eval(expr, envir, enclos): object 'persistence_eosinophil_norm' not found
persistence_eosinophil_nb <- normalize_expt(persistence_eosinophil, transform = "log2", convert = "cpm",
                                            batch = "svaseq", filter = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'normalize_expt': object 'persistence_eosinophil' not found
plot_pca(persistence_eosinophil_nb)[["plot"]]
## Error in eval(expr, envir, enclos): object 'persistence_eosinophil_nb' not found

18.3 persistence DE

This is pretty sparse and unlikely to yield any interesting results I am thinking.

persistence_de_sva <- all_pairwise(persistence_expt, filter = TRUE, methods = methods,
                                   model_batch = "svaseq")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'persistence_expt' not found
persistence_de_sva
## Error in eval(expr, envir, enclos): object 'persistence_de_sva' not found
persistence_table_sva <- combine_de_tables(
  persistence_de_sva, scale_p = TRUE,
  excel = glue("{xlsx_prefix}/DE_Persistence/persistence_all_de_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'persistence_de_sva' not found
persistence_table_sva
## Error in eval(expr, envir, enclos): object 'persistence_table_sva' not found
persistence_monocyte_de_sva <- all_pairwise(persistence_monocyte, filter = TRUE,
                                            model_batch = "svaseq",
                                            methods = methods)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'persistence_monocyte' not found
persistence_monocyte_de_sva
## Error in eval(expr, envir, enclos): object 'persistence_monocyte_de_sva' not found
persistence_monocyte_table_sva <- combine_de_tables(
  persistence_monocyte_de_sva, scale_p = TRUE,
  excel = glue("{xlsx_prefix}/DE_Persistence/persistence_monocyte_de_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'persistence_monocyte_de_sva' not found
persistence_monocyte_table_sva
## Error in eval(expr, envir, enclos): object 'persistence_monocyte_table_sva' not found
persistence_neutrophil_de_sva <- all_pairwise(persistence_neutrophil, filter = TRUE,
                                              model_batch = "svaseq",
                                              methods = methods)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'persistence_neutrophil' not found
persistence_neutrophil_de_sva
## Error in eval(expr, envir, enclos): object 'persistence_neutrophil_de_sva' not found
persistence_neutrophil_table_sva <- combine_de_tables(
  persistence_neutrophil_de_sva, scale_p = TRUE,
  excel = glue("{xlsx_prefix}/DE_Persistence/persistence_neutrophil_de_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'persistence_neutrophil_de_sva' not found
persistence_neutrophil_table_sva
## Error in eval(expr, envir, enclos): object 'persistence_neutrophil_table_sva' not found
## There are insufficient samples (1) in the 'N' category.
##persistence_eosinophil_de_sva <- all_pairwise(persistence_eosinophil, filter = TRUE,
#model_batch = "svaseq",
##                                              methods = methods)
##persistence_eosinophil_de_sva
##persistence_eosinophil_table_sva <- combine_de_tables(
##  persistence_eosinophil_de_sva,
##  excel = glue("{xlsx_prefix}/DE_Persistence/persistence_eosinophil_de_sva-v{ver}.xlsx"))

19 Comparing visits without regard to cure/fail

In the following, I am hoping to lower variance associated with factors other than visit via sva and therefore be able to see what genes are changing for everyone with respect to time.

This is the one instance where I think it would be really nice to have biopsy samples for all three visits; I presume that we would have a really nice signal of stuff like keratin and other wound-healing associated genes.

19.1 All cell types

t_visit_all_de_sva <- all_pairwise(t_visit, filter = TRUE, methods = methods,
                                   model_batch = "svaseq")
## 
## v3 v2 v1 
## 34 35 40
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_visit_all_de_sva
## Error in eval(expr, envir, enclos): object 't_visit_all_de_sva' not found
t_visit_all_table_sva <- combine_de_tables(
  t_visit_all_de_sva, keepers = visit_contrasts, scale_p = TRUE,
  excel = glue("{xlsx_prefix}/DE_Visits/t_all_visit_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_visit_all_de_sva' not found
t_visit_all_table_sva
## Error in eval(expr, envir, enclos): object 't_visit_all_table_sva' not found
t_visit_all_sig_sva <- extract_significant_genes(
  t_visit_all_table_sva,
  excel = glue("{xlsx_prefix}/DE_Visits/t_all_visit_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_visit_all_table_sva' not found
t_visit_all_sig_sva
## Error in eval(expr, envir, enclos): object 't_visit_all_sig_sva' not found

19.2 Monocyte samples

t_visit_monocytes <- set_expt_conditions(t_monocytes, fact = "visitnumber")
## The numbers of samples by condition are:
## 
## v3 v2 v1 
## 13 13 16
t_visit_monocyte_de_sva <- all_pairwise(t_visit_monocytes, filter = TRUE,
                                        model_batch = "svaseq",
                                        methods = methods)
## 
## v3 v2 v1 
## 13 13 16
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_visit_monocyte_de_sva
## Error in eval(expr, envir, enclos): object 't_visit_monocyte_de_sva' not found
t_visit_monocyte_table_sva <- combine_de_tables(
  t_visit_monocyte_de_sva, keepers = visit_contrasts, scale_p = TRUE,
  excel = glue("{xlsx_prefix}/DE_Visits/Monocytes/t_monocyte_visit_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_visit_monocyte_de_sva' not found
t_visit_monocyte_table_sva
## Error in eval(expr, envir, enclos): object 't_visit_monocyte_table_sva' not found
t_visit_monocyte_sig_sva <- extract_significant_genes(
  t_visit_monocyte_table_sva,
  excel = glue("{xlsx_prefix}/DE_Visits/Monocytes/t_monocyte_visit_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_visit_monocyte_table_sva' not found
t_visit_monocyte_sig_sva
## Error in eval(expr, envir, enclos): object 't_visit_monocyte_sig_sva' not found

19.3 Neutrophil samples

t_visit_neutrophils <- set_expt_conditions(t_neutrophils, fact = "visitnumber")
## The numbers of samples by condition are:
## 
## v3 v2 v1 
## 12 13 16
t_visit_neutrophil_de_sva <- all_pairwise(t_visit_neutrophils, filter = TRUE,
                                          model_batch = "svaseq",
                                          methods = methods)
## 
## v3 v2 v1 
## 12 13 16
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_visit_neutrophil_de_sva
## Error in eval(expr, envir, enclos): object 't_visit_neutrophil_de_sva' not found
t_visit_neutrophil_table_sva <- combine_de_tables(
  t_visit_neutrophil_de_sva, keepers = visit_contrasts, scale_p = TRUE,
  excel = glue("{xlsx_prefix}/DE_Visits/Neutrophils/t_neutrophil_visit_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_visit_neutrophil_de_sva' not found
t_visit_neutrophil_table_sva
## Error in eval(expr, envir, enclos): object 't_visit_neutrophil_table_sva' not found
t_visit_neutrophil_sig_sva <- extract_significant_genes(
  t_visit_neutrophil_table_sva,
  excel = glue("{xlsx_prefix}/DE_Visits/Neutrophils/t_neutrophil_visit_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_visit_neutrophil_table_sva' not found
t_visit_neutrophil_sig_sva
## Error in eval(expr, envir, enclos): object 't_visit_neutrophil_sig_sva' not found

19.4 Eosinophil samples

t_visit_eosinophils <- set_expt_conditions(t_eosinophils, fact="visitnumber")

t_visit_eosinophil_de <- all_pairwise(t_visit_eosinophils, filter = TRUE,
                                      model_batch = "svaseq",
                                      methods = methods)
t_visit_eosinophil_de
t_visit_eosinophil_table <- combine_de_tables(
  t_visit_eosinophil_de, keepers = visit_contrasts, scale_p = TRUE
  excel = glue("{xlsx_prefix}/DE_Visits/Eosinophils/t_eosinophil_visit_table_sva-v{ver}.xlsx"))
t_visit_eosinophil_table
t_visit_eosinophil_sig <- extract_significant_genes(
  t_visit_eosinophil_table,
  excel = glue("{xlsx_prefix}/DE_Visits/Eosinophils/t_eosinophil_visit_sig_sva-v{ver}.xlsx"))
## No significant genes observed.
## Error: <text>:9:3: unexpected symbol
## 8:   t_visit_eosinophil_de, keepers = visit_contrasts, scale_p = TRUE
## 9:   excel
##      ^

20 Explore ROC

Alejandro showed some ROC curves for eosinophil data showing sensitivity vs. specificity of a couple genes which were observed in v1 eosinophils vs. all-times eosinophils across cure/fail. I am curious to better understand how this was done and what utility it might have in other contexts.

To that end, I want to try something similar myself. In order to properly perform the analysis with these various tools, I need to reconfigure the data in a pretty specific format:

  1. Single df with 1 row per set of observations (sample in this case I think)
  2. The outcome column(s) need to be 1 (or more?) metadata factor(s) (cure/fail or a paste0 of relevant queries (eo_v1_cure, eo_v123_cure, etc)
  3. The predictor column(s) are the measurements (rpkm of 1 or more genes), 1 column each gene.

If I intend to use this for our tx data, I will likely need a utility function to create the properly formatted input df.

For the purposes of my playing, I will choose three genes from the eosinophil C/F table, one which is significant, one which is not, and an arbitrary.

The input genes will therefore be chosen from the data structure: t_cf_eosinophil_table_sva:

ENSG00000198178, ENSG00000179344, ENSG00000182628

eo_rpkm <- normalize_expt(tv1_eosinophils, convert = "rpkm", column = "cds_length")
## There appear to be 5355 genes without a length.
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found

21 An external dataset

This paper is DOI:10.1126/scitranslmed.aax4204

Variable gene expression and parasite load predict treatment outcome in cutaneous leishmaniasis.

One query from Maria Adelaida is to see how this data fits with ours. I have read this paper a couple of times now and I get confused on a couple of points every time, which I will explain in a moment. The expermental design is key to my confusion and key to what I think is being missed in our interpretation of the results:

  1. The PCA is not cure vs. fail but healthy skin vs. CL lesion. It should be said that the text makes this perfectly clear, but I can never seem to remember that when I go to look at the data; presumably because I am thinking primarily about cure/fail.

21.1 Only the Scott data

external_norm <- normalize_expt(external_cf, filter = TRUE, norm = "quant",
                                convert = "cpm", transform = "log2")
## Removing 7327 low-count genes (14154 remaining).
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
plot_pca(external_norm)
## Error in eval(expr, envir, enclos): object 'external_norm' not found
external_nb <- normalize_expt(external_cf, filter = TRUE, batch = "svaseq",
                                convert = "cpm", transform = "log2")
## Removing 7327 low-count genes (14154 remaining).
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform,  : 
##   object 'surrogates' not found
## Warning in do_batch(count_table, method = batch, design = design, batch1 =
## batch1, : The batch_counts call failed.  Returning non-batch reduced data.
## transform_counts: Found 165 values equal to 0, adding 1 to the matrix.
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
plot_pca(external_nb)
## Error in eval(expr, envir, enclos): object 'external_nb' not found
external_de <- all_pairwise(external_cf, filter = TRUE, methods = methods,
                            model_batch = "svaseq")
## 
##    cure failure 
##      14       7
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
external_de
## Error in eval(expr, envir, enclos): object 'external_de' not found
external_table <- combine_de_tables(
  external_de, scale_p = TRUE,
  excel = "excel/scott_table.xlsx")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'external_de' not found
external_table
## Error in eval(expr, envir, enclos): object 'external_table' not found
external_sig <- extract_significant_genes(external_table, excel = "excel/scott_sig.xlsx")
## Error in eval(expr, envir, enclos): object 'external_table' not found
external_sig
## Error in eval(expr, envir, enclos): object 'external_sig' not found
external_top100 <- extract_significant_genes(external_table, n = 100)
## Error in eval(expr, envir, enclos): object 'external_table' not found
external_up <- external_top100[["deseq"]][["ups"]][["failure_vs_cure"]]
## Error in eval(expr, envir, enclos): object 'external_top100' not found
external_down <- external_top100[["deseq"]][["downs"]][["failure_vs_cure"]]
## Error in eval(expr, envir, enclos): object 'external_top100' not found

21.2 An explicit comparison of methods.

I think I am getting a significantly different result from Scott, so I am going to do an explicit side-by-side comparison of our results at each step. In order to do this, I am using the capsule they kindly provided with their publication.

I am copy/pasting material from their publication with some modification which I will note as I go.

Here is their block ‘r packages’

Note/Spoiler alert: It actually turns out our results are basically relatively similar, I just didn’t understand what comparisons are actually in paper vs those I have primary interest. In addition, we handled gene IDs differently (gene card vs. EnsemblID) which has a surprisingly big effect.

Oh, I just realized that when I did these analyses, I did them in a completely separate tree and compared the results post-facto. This assumption remains in this document and therefore is unlikely to work properly in the containerized environment I am attempting to create. Given that the primary goal of this section is to show to myself that I compared the two datasets as thoroughly as I could, perhaps I should just disable them for the container and allow the reader to perform the exercise de-novo.

library(tidyverse)
library(ggthemes)
library(reshape2)
library(edgeR)
library(patchwork)
library(vegan)
library(DT)
library(tximport)
library(gplots)
library(FinCal)
library(ggrepel)
library(gt)
library(ggExtra)
library(EnsDb.Hsapiens.v86)
library(stringr)
library(cowplot)
library(ggpubr)

I have a separate tree in which I copied the capsule and data. I performed exactly their steps kallisto quant steps within it and put the output data into the same place within it. I did change the commands slightly because I downloaded the files from SRA and so don’t have them with names like ‘host_CL01’, but instead ‘PRJNA…’. But the samples are in the same order, so I sent the output files to the same final filenames. Here is an example from the first sample:

cd preprocessing
module add kallisto
kallisto index -i Homo_sapiens.GRCh38.cdna.all.Index Homo_sapiens.GRCh38.cdna.all.fa
# Map reads to the indexed reference transcriptome for HOST
# first the healthy subjects (HS)
export LESS = '--buffers 0 -B'
kallisto quant -i Homo_sapiens.GRCh38.cdna.all.Index -o host_HS01 -t 24 -b 60 \
         --single -l 250 -s 30 <(less SRR8668755/*-trimmed.fastq.xz) 2>host_HS01.log 1>&2 &

21.3 Block ‘sample_info’

I am going to change the path very slightly in the following block simply because I put the capsule in a separate directory and do not want to copy it here. Otherwise it is unmodified. Also, the function gt::tab_header() annoys the crap out of me.

import <- read_tsv("../scott_2019/capsule-6534016/data/studydesign.txt")
import %>% dplyr::filter(disease == "cutaneous") %>%
  dplyr::select(-2) %>%  gt() %>%
  tab_header(title = md("Clinical metadata from patients with cutaneous leishmaniasis (CL)"),
             subtitle = md("`(n=21)`")) %>%  cols_align(align = "center", columns = TRUE)
targets.lesion <- import
targets.onlypatients <- targets.lesion[8:28,] # only CL lesions (n=21)

# Making factors that will be used for pairwise comparisons:
# HS vs. CL lesions as a factor:
disease.lesion <- factor(targets.lesion$disease)
# Cure vs. Failure lesions as a factor:
treatment.lesion <- factor(targets.onlypatients$treatment_outcome)

21.4 Importing the data and annotations

They did use a slightly different annotation set, Ensembl revision 86. Once again I am modifying the paths slightly to reflect where I put the capsule.

# capturing Ensembl transcript IDs (tx) and gene symbols ("gene_name") from
# EnsDb.Hsapiens.v86 annotation package
Tx <- as.data.frame(transcripts(EnsDb.Hsapiens.v86,
                                columns=c(listColumns(EnsDb.Hsapiens.v86, "tx"),
                                          "gene_name")))

Tx <- dplyr::rename(Tx, target_id = tx_id)
row.names(Tx) <- NULL
Tx <- Tx[,c(6,12)]

# getting file paths for Kallisto outputs
paths.all <- file.path("../scott_2019/capsule-6534016/data/readMapping/human", targets.lesion$sample, "abundance.h5")
paths.patients <- file.path("../scott_2019/capsule-6534016/data/readMapping/human", targets.onlypatients$sample, "abundance.h5")

# importing .h5 Kallisto data and collapsing transcript-level data to genes
Txi.lesion.coding <- tximport(paths.all,
                              type = "kallisto",
                              tx2gene = Tx,
                              txOut = FALSE,
                              ignoreTxVersion = TRUE,
                              countsFromAbundance = "lengthScaledTPM")

# importing againg, but this time just the CL patients
Txi.lesion.coding.onlypatients <- tximport(paths.patients,
                                           type = "kallisto",
                                           tx2gene = Tx,
                                           txOut = FALSE,
                                           ignoreTxVersion = TRUE,
                                           countsFromAbundance = "lengthScaledTPM")

21.5 Filtering and normalization

The block ‘visualizationDatasets’ follows unchanged. In the next block I will add another plot or perhaps 2

# First make a DGEList from the counts:
Txi.lesion.coding.DGEList <- DGEList(Txi.lesion.coding$counts)
colnames(Txi.lesion.coding.DGEList$counts) <- targets.lesion$sample
colnames(Txi.lesion.coding$counts) <- targets.lesion$sample

Txi.lesion.coding.DGEList.OP <- DGEList(Txi.lesion.coding.onlypatients$counts)
colnames(Txi.lesion.coding.DGEList.OP) <- targets.onlypatients$sample

# Convert to counts per million:
Txi.lesion.coding.DGEList.cpm <- edgeR::cpm(Txi.lesion.coding.DGEList, log = TRUE)
Txi.lesion.coding.DGEList.OP.cpm <- edgeR::cpm(Txi.lesion.coding.DGEList.OP, log = TRUE)

keepers.coding <- rowSums(Txi.lesion.coding.DGEList.cpm>1)>=7
keepers.coding.OP <- rowSums(Txi.lesion.coding.DGEList.OP.cpm>1)>=7

Txi.lesion.coding.DGEList.filtered <- Txi.lesion.coding.DGEList[keepers.coding,]
Txi.lesion.coding.DGEList.OP.filtered <- Txi.lesion.coding.DGEList.OP[keepers.coding.OP,]

# convert back to cpm:
Txi.lesion.coding.DGEList.LogCPM.filtered <- edgeR::cpm(Txi.lesion.coding.DGEList.filtered,
                                                        log=TRUE)
Txi.lesion.coding.DGEList.LogCPM.OP.filtered <- edgeR::cpm(Txi.lesion.coding.DGEList.OP.filtered,
                                                           log=TRUE)

# Normalizing data:
calcNorm1 <- calcNormFactors(Txi.lesion.coding.DGEList.filtered, method = "TMM")
calcNorm2 <- calcNormFactors(Txi.lesion.coding.DGEList.OP.filtered, method = "TMM")

Txi.lesion.coding.DGEList.LogCPM.filtered.norm <- edgeR::cpm(calcNorm1, log=TRUE)
colnames(Txi.lesion.coding.DGEList.LogCPM.filtered.norm) <- targets.lesion$sample
Txi.lesion.coding.DGEList.OP.LogCPM.filtered.norm <- edgeR::cpm(calcNorm2, log=TRUE)
colnames(Txi.lesion.coding.DGEList.OP.LogCPM.filtered.norm) <- targets.onlypatients$sample
# Raw dataset:
V1 <- as.data.frame(Txi.lesion.coding.DGEList.cpm)
colnames(V1) <- targets.lesion$sample
V1 <- melt(V1)
colnames(V1) <- c("sample","expression")

# Filtered dataset:
V1.1 <- as.data.frame(Txi.lesion.coding.DGEList.LogCPM.filtered)
colnames(V1.1) <- targets.lesion$sample
V1.1 <- melt(V1.1)
colnames(V1.1) <- c("sample","expression")

# Filtered-normalized dataset:
V1.1.1 <- as.data.frame(Txi.lesion.coding.DGEList.LogCPM.filtered.norm)
colnames(V1.1.1) <- targets.lesion$sample
V1.1.1 <- melt(V1.1.1)
colnames(V1.1.1) <- c("sample","expression")

# plotting:
ggplot(V1, aes(x=sample, y=expression, fill=sample)) +
  geom_violin(trim = TRUE, show.legend = TRUE) +
  stat_summary(fun.y = "median", geom = "point", shape = 95, size = 10, color = "black") +
  theme_bw() +
  theme(legend.position = "none", axis.title=element_text(size=7),
        axis.title.x=element_blank(), axis.text=element_text(size=5),
        axis.text.x = element_text(angle = 90, hjust = 1),
        plot.title = element_text(size = 7)) +
  ggtitle("Raw dataset") +
  ggplot(V1.1, aes(x=sample, y=expression, fill=sample)) +
  geom_violin(trim = TRUE, show.legend = TRUE) +
  stat_summary(fun.y = "median", geom = "point", shape = 95, size = 10, color = "black") +
  theme_bw() +
  theme(legend.position = "none", axis.title=element_text(size=7),
        axis.title.x=element_blank(), axis.text=element_text(size=5),
        axis.text.x = element_text(angle = 90, hjust = 1),
        plot.title = element_text(size = 7)) +
  ggtitle("Filtered dataset") +
  ggplot(V1.1.1, aes(x=sample, y=expression, fill=sample)) +
  geom_violin(trim = TRUE, show.legend = TRUE) +
  stat_summary(fun.y = "median", geom = "point", shape = 95, size = 10, color = "black") +
  theme_bw() +
  theme(legend.position = "none", axis.title=element_text(size=7),
        axis.title.x=element_blank(), axis.text=element_text(size=5),
        axis.text.x = element_text(angle = 90, hjust = 1),
        plot.title = element_text(size = 7)) +
  ggtitle("Filtered and normalized dataset")

21.6 The unfiltered data

The following block in their dataset recreated the matrix without filtering and will use that for differential expression. It is a little hard to follow for me because they subset based on the sample numbers (8 to 28, which if I am not mistaken just drops the healthy samples).

DataNotFiltered_Norm_OP <- calcNormFactors(Txi.lesion.coding.DGEList[,8:28],
                                           method = "TMM")
DataNotFiltered_Norm_log2CPM_OP <- edgeR::cpm(DataNotFiltered_Norm_OP, log=TRUE)
colnames(DataNotFiltered_Norm_log2CPM_OP) <- targets.onlypatients$sample
CPM_normData_notfiltered_OP <- 2^(DataNotFiltered_Norm_log2CPM_OP)
#uncomment the next line to produce raw data that was uploaded to the Gene Expression Omnibus (GEO) for publication.
#write.table(Txi.lesion.coding$counts, file = "Amorim_GEO_raw.txt", sep = "\t", quote = FALSE)

# Including all the individuals (HS and CL patients) for public domain submission:
DataNotFiltered_Norm <- calcNormFactors(Txi.lesion.coding.DGEList, method = "TMM")
DataNotFiltered_Norm_log2CPM <- edgeR::cpm(DataNotFiltered_Norm, log=TRUE)
colnames(DataNotFiltered_Norm_log2CPM) <- targets.lesion$sample
CPM_normData_notfiltered <- 2^(DataNotFiltered_Norm_log2CPM)
#uncomment the next line to produce the normalized data file that was uploaded to the Gene Expression Omnibus (GEO) for publication.
#write.table(DataNotFiltered_Norm_log2CPM, "Amorim_GEO_normalized.txt", sep = "\t", quote = FALSE)

21.7 The scott exploratory analysis

The following block generated a couple of the figures in the paper and comprise a pretty straightforward PCA. I am going to make a following block containing the same image with the cure/fail visualization using the same method/data.

pca.res <- prcomp(t(Txi.lesion.coding.DGEList.LogCPM.filtered.norm), scale.=F, retx=T)
pc.var <- pca.res$sdev^2
pc.per <- round(pc.var/sum(pc.var)*100, 1)
data.frame <- as.data.frame(pca.res$x)

# Calculate distance between samples by permanova:
allsamples.dist <- vegdist(t(2^Txi.lesion.coding.DGEList.LogCPM.filtered.norm),
                           method = "bray")

vegan <- adonis2(allsamples.dist~targets.lesion$disease,
                 data=targets.lesion,
                 permutations = 999, method="bray")

targets.lesion$disease
ggplot(data.frame, aes(x=PC1, y=PC2, color=factor(targets.lesion$disease))) +
  geom_point(size=5, shape=20) +
  theme_calc() +
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
        axis.text.x = element_text(size = 15, vjust = 0.5),
        axis.text.y = element_text(size = 15), axis.title = element_text(size = 15),
        legend.position="none") +
  scale_color_manual(values = c("#073F80","#EB512C")) +
  annotate("text", x=-50, y=80, label=paste("Permanova Pr(>F) =",
                                            vegan[1,5]), size=3, fontface="bold") +
  xlab(paste("PC1 -",pc.per[1],"%")) +
  ylab(paste("PC2 -",pc.per[2],"%")) +
  xlim(-200,110)

21.7.1 My most similar pca

I just realized that somewhere along the way in creating this container, I messed up this analysis pretty badly:

  1. I dropped the 7 control samples.
  2. I am comparing cure/fail but these analyses are all control/cutaneous.

When I originally did this on my workstation I had an actual 1:1 comparison and saw that our results were quite similar. I need to bring that back into this in order to show that neither we nor they are crazy people.

Either way, I think the main takeaway is that their dataset does not spend much time looking at cure/fail but instead control/infected for a reason.

Note, the fun aspects of the experiment (time to cure, size of lesion, etc) are not annotated in the metadata provided by SRA, but instead may be found in the capsule kindly provided by the lab. As a result, I copied that file into the sample_sheets/ directory and have added it to the expressionset. There is an important caveat, though: I did not include the non-diseased samples for this comparison; as a result the disease metadata factor is boring (e.g. it is only cutaneous).

external_cf[["accession"]] <- pData(external_cf)[["sample"]]
disease_factor <- pData(external_cf)[["disease"]]
table(disease_factor)
## disease_factor
## cutaneous 
##        21
external_disease <- set_expt_conditions(external_cf, fact = disease_factor)
## The numbers of samples by condition are:
## 
## cutaneous 
##        21
external_l2cpm <- normalize_expt(external_cf, filter = TRUE,
                                convert = "cpm", transform = "log2")
## Removing 7327 low-count genes (14154 remaining).
## transform_counts: Found 165 values equal to 0, adding 1 to the matrix.
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
plot_pca(external_l2cpm, plot_labels = "repel")
## Error in eval(expr, envir, enclos): object 'external_l2cpm' not found

Use the following block if you wish to bring together SRA-downloaded data with the experimental design from the Scott paper. It requires running the blocks above in which I loaded the capsule-derived metadata.

test <- pData(external_cf)
test_import <- as.data.frame(import)
test_import[["accession"]] <- pData(external_cf[["accession"]])
test_merged <- merge(test, import, by = "accession")

This is real comparison point to their cure/fail analysis.

21.8 Cure/Fail PCA using the same prcomp result

I am just copy/pasting their code again, but changing the color factor so that cure is purple, failure is red, and na(uninfected) is black.

The following plot should be the first direct comparison point between the two analysis pipelines. Thus, if you look back a few block at my invocation of plot_pca(external_norm), you will see a green/orange plot which is functionally identical if you note:

  1. The x and y axes are flipped, which ok whatever it is PCA.
  2. I excluded the healthy samples.
  3. I dropped to gene level and used hisat.

With those caveats in mind, it is trivial to find the same relationshipes in the samples. E.g. the bottom red/purple individual samples are in the same relative position as my top orange/green pair. the same 4 samples are relative x-axis outliers (my right green, their left purple). The last 6 samples (my orange, their red) are all in the relative orientation.

I think I can further prove the similarity of our inputs via a direct comparison of the datastructures: Txi.lesion.coding.DGEList.LogCPM.filtered.norm (ugh what a name) vs. external_cf. In order to make that comparison, I need to rename my rows to the genecard IDs and the columns.

their_norm_exprs <- Txi.lesion.coding.DGEList.LogCPM.filtered.norm

my_hgnc_ids <- make.names(fData(external_cf)[["hgnc_symbol"]], unique = TRUE)
my_renamed <- set_expt_genenames(external_cf, ids = my_hgnc_ids)
my_norm <- normalize_expt(my_renamed, filter = TRUE, transform = "log2", convert = "cpm")
my_norm_exprs <- as.data.frame(exprs(my_norm))

our_exprs <- merge(their_norm_exprs, my_norm_exprs, by = "row.names")
rownames(our_exprs) <- our_exprs[["Row.names"]]
our_exprs[["Row.names"]] <- NULL
dim(our_exprs)

## I fully expected a correlation heatmap of the combined
## data to show a set of paired samples across the board.
## That is absolutely not true.
correlations <- plot_corheat(our_exprs)
correlations[["scatter"]]
correlations[["plot"]]
color_fact <- factor(targets.lesion$treatment_outcome)
levels(color_fact)
## Added by atb to see cure/fail on the same dataset
ggplot(data.frame, aes(x=PC1, y=PC2, color=color_fact)) +
  geom_point(size=5, shape=20) +
  theme_calc() +
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
        axis.text.x = element_text(size = 15, vjust = 0.5),
        axis.text.y = element_text(size = 15), axis.title = element_text(size = 15),
        legend.position="none") +
  scale_color_manual(values = c("purple", "red","black")) +
  annotate("text", x=-50, y=80, label=paste("Permanova Pr(>F) =",
                                            vegan[1,5]), size=3, fontface="bold") +
  xlab(paste("PC1 -",pc.per[1],"%")) +
  ylab(paste("PC2 -",pc.per[2],"%")) +
  xlim(-200,110)

21.9 DE comparisons

The following is their comparison of healthy tissue vs. CL lesion and Failure vs. Cure. I am going to follow it with my analagous examination using limma. Note, each of the pairs of variables created in the following block is xxx followed by xxx.treat; the former is healthy vs lesion and the latter is the fail vs cure set.

# Model matrices:
# CL lesions vs. HS:
design.lesion <- model.matrix(~0 + disease.lesion)
colnames(design.lesion) <- levels(disease.lesion)

# Failure vs. Cure:
design.lesion.treatment <- model.matrix(~0 + treatment.lesion)
colnames(design.lesion.treatment) <- levels(treatment.lesion)

myDGEList.lesion.coding <- DGEList(calcNorm1$counts)
myDGEList.OP.NotFil <- DGEList(CPM_normData_notfiltered_OP)

# Model mean-variance trend and fit linear model to data.
# Use VOOM function from Limma package to model the mean-variance relationship
normData.lesion.coding <- voom(myDGEList.lesion.coding, design.lesion)
normData.OP.NotFil <- voom(myDGEList.OP.NotFil, design.lesion.treatment)

colnames(normData.lesion.coding) <- targets.lesion$sample
colnames(normData.OP.NotFil) <- targets.onlypatients$sample

# fit a linear model to your data
fit.lesion.coding <- lmFit(normData.lesion.coding, design.lesion)
fit.lesion.coding.treatment <- lmFit(normData.OP.NotFil, design.lesion.treatment)

# contrast matrix
contrast.matrix.lesion <- makeContrasts(CL.vs.CON = cutaneous - control,
                                        levels=design.lesion)
contrast.matrix.lesion.treat <- makeContrasts(failure.vs.cure = failure - cure,
                                              levels=design.lesion.treatment)

# extract the linear model fit
fits.lesion.coding <- contrasts.fit(fit.lesion.coding,
                                    contrast.matrix.lesion)
fits.lesion.coding.treat <- contrasts.fit(fit.lesion.coding.treatment,
                                          contrast.matrix.lesion.treat)

# get bayesian stats for your linear model fit
ebFit.lesion.coding <- eBayes(fits.lesion.coding)
ebFit.lesion.coding.treat <- eBayes(fits.lesion.coding.treat)

# TopTable ----
allHits.lesion.coding <- topTable(ebFit.lesion.coding,
                                  adjust ="BH", coef=1,
                                  number=34935, sort.by="logFC")
allHits.lesion.coding.treat <- topTable(ebFit.lesion.coding.treat,
                                        adjust ="BH", coef=1,
                                        number=34776, sort.by="logFC")
myTopHits <- rownames_to_column(allHits.lesion.coding, "geneID")
myTopHits.treat <- rownames_to_column(allHits.lesion.coding.treat, "geneID")

# mutate the format of numeric values:
myTopHits <- mutate(myTopHits, log10Pval = round(-log10(adj.P.Val),2),
                    adj.P.Val = round(adj.P.Val, 2),
                    B = round(B, 2),
                    AveExpr = round(AveExpr, 2),
                    t = round(t, 2),
                    logFC = round(logFC, 2),
                    geneID = geneID)

myTopHits.treat <- mutate(myTopHits.treat, log10Pval = round(-log10(adj.P.Val),2),
                          adj.P.Val = round(adj.P.Val, 2),
                          B = round(B, 2),
                          AveExpr = round(AveExpr, 2),
                          t = round(t, 2),
                          logFC = round(logFC, 2),
                          geneID = geneID)
#save(myTopHits, file = "myTopHits")
#save(myTopHits.treat, file = "myTopHits.treat")

21.10 Perform my analagous limma analysis

my_filt <- normalize_expt(my_renamed, filter = "simple")
limma_cf <- limma_pairwise(my_filt, model_batch = FALSE)

my_table <- limma_cf[["all_tables"]][["failure_vs_cure"]]
their_table <- myTopHits.treat

dim(my_table)
dim(myTopHits.treat)
our_table <- merge(my_table, myTopHits.treat, by.x = "row.names", by.y = "geneID")
dim(our_table)
comparison <- plot_linear_scatter(our_table[, c("logFC.x", "logFC.y")])
comparison$scatter
comparison$correlation
comparison$lm_model

Ok, so there is a constituitive difference in our results, and it is significant. What does that mean for the set of genes observed?

With that said, in my most recent manual run of this, the results are quite good, I got a 0.75 correlation; I bet the primary outliers (on the axes) are just genes for which we got different gene<->tx mappings due to me using hisat and their usage of kallisto.

I guess I can test this hypothesis by just swapping in their counts into my data structure.

test_counts <- as.data.frame(myDGEList.lesion.coding[["counts"]])
test_counts[["host_HS01"]] <- NULL
test_counts[["host_HS02"]] <- NULL
test_counts[["host_HS03"]] <- NULL
test_counts[["host_HS04"]] <- NULL
test_counts[["host_HS05"]] <- NULL
test_counts[["host_HS06"]] <- NULL
test_counts[["host_HS07"]] <- NULL

dim(test_counts)
dim(exprs(my_test))
## Oh, that surprises me, the kallisto data has ~ 6k fewer genes?

21.11 See if there are shared DE genes

!!NOTE!! I am using a non-adjusted p-value filter here because I want to use the same filter they used for the volcano plot.

my_filter <- abs(my_table[["logFC"]]) > 1.0 & my_table[["P.Value"]] <= 0.05
sum(my_filter)
their_filter <- abs(their_table[["logFC"]]) > 1.0 & their_table[["P.Value"]] <= 0.05
sum(their_filter)

my_shared <- rownames(my_table)[my_filter] %in% their_table[their_filter, "geneID"]
sum(my_shared)

shared <- rownames(my_table)[my_filter]
shared[my_shared]

both <- list(
  "us" = rownames(my_table)[my_filter],
  "them" = their_table[their_filter, "geneID"])
tt <- UpSetR::fromList(both)
UpSetR::upset(tt)

21.12 Compare the two datasets directly

only_tmrc3 <- subset_expt(tmrc3_external, subset = "condition=='Colombia'") %>%
  set_expt_conditions(fact = "finaloutcome")
## subset_expt(): There were 39, now there are 18 samples.
## The numbers of samples by condition are:
## 
## failure    cure 
##       5      13
only_tmrc3_de <- all_pairwise(only_tmrc3, model_batch = "svaseq",
                              filter = TRUE,
                              methods = methods)
## 
## failure    cure 
##       5      13
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
only_tmrc3_de
## Error in eval(expr, envir, enclos): object 'only_tmrc3_de' not found
only_tmrc3_table <- combine_de_tables(only_tmrc3_de, scale_p = TRUE)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'only_tmrc3_de' not found
only_tmrc3_table
## Error in eval(expr, envir, enclos): object 'only_tmrc3_table' not found
only_tmrc3_top100 <- extract_significant_genes(only_tmrc3_table, n = 100)
## Error in eval(expr, envir, enclos): object 'only_tmrc3_table' not found
only_tmrc3_up <- only_tmrc3_top100[["deseq"]][["ups"]][["failure_vs_cure"]]
## Error in eval(expr, envir, enclos): object 'only_tmrc3_top100' not found
only_tmrc3_down <- only_tmrc3_top100[["deseq"]][["downs"]][["failure_vs_cure"]]
## Error in eval(expr, envir, enclos): object 'only_tmrc3_top100' not found
tmrc3_external_de <- all_pairwise(tmrc3_external, model_batch = "svaseq",
                                  filter = "simple",
                                  methods = methods)
## 
##   Brazil Colombia 
##       21       18
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
tmrc3_external_table <- combine_de_tables(
  tmrc3_external_de, scale_p = TRUE,
  excel = "excel/tmrc3_scott_biopsies.xlsx")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'tmrc3_external_de' not found
tmrc3_external_sig <- extract_significant_genes(
  tmrc3_external_table, excel = "excel/tmrc3_scott_biopsies_sig.xlsx")
## Error in eval(expr, envir, enclos): object 'tmrc3_external_table' not found
tmrc3_external_cf <- set_expt_conditions(tmrc3_external, fact = "finaloutcome")
## The numbers of samples by condition are:
## 
## failure    cure 
##      12      27
tmrc3_external_cf <-  set_expt_batches(tmrc3_external_cf, fact = "lab")
## The number of samples by batch are:
## 
##   Brazil Colombia 
##       21       18
tmrc3_external_cf_norm <- normalize_expt(tmrc3_external_cf, filter = TRUE,
                                         norm = "quant", convert = "cpm", transform = "log2")
## Removing 6904 low-count genes (14577 remaining).
## transform_counts: Found 18 values equal to 0, adding 1 to the matrix.
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
plot_pca(tmrc3_external_cf_norm)
## Error in eval(expr, envir, enclos): object 'tmrc3_external_cf_norm' not found
tmrc3_external_cf_nb <- normalize_expt(tmrc3_external_cf, filter = TRUE,
                                       batch = "svaseq", convert = "cpm", transform = "log2")
## Removing 6904 low-count genes (14577 remaining).
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform,  : 
##   object 'surrogates' not found
## Warning in do_batch(count_table, method = batch, design = design, batch1 =
## batch1, : The batch_counts call failed.  Returning non-batch reduced data.
## transform_counts: Found 7483 values equal to 0, adding 1 to the matrix.
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
plot_pca(tmrc3_external_cf_nb)
## Error in eval(expr, envir, enclos): object 'tmrc3_external_cf_nb' not found
tmrc3_external_cf_de <- all_pairwise(tmrc3_external_cf, model_batch = "svaseq",
                                     filter = TRUE,
                                     methods = methods)
## 
## failure    cure 
##      12      27
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
tmrc3_external_cf_de
## Error in eval(expr, envir, enclos): object 'tmrc3_external_cf_de' not found
tmrc3_external_cf_table <- combine_de_tables(
  tmrc3_external_cf_de, scale_p = TRUE,
  excel = "excel/tmrc3_scott_cf_table.xlsx")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 'tmrc3_external_cf_de' not found
tmrc3_external_cf_table
## Error in eval(expr, envir, enclos): object 'tmrc3_external_cf_table' not found
tmrc3_external_cf_sig <- extract_significant_genes(
  tmrc3_external_cf_table, excel = "excel/tmrc3_scott_cf_sig.xlsx")
## Error in eval(expr, envir, enclos): object 'tmrc3_external_cf_table' not found
tmrc3_external_cf_sig
## Error in eval(expr, envir, enclos): object 'tmrc3_external_cf_sig' not found
tmrc3_external_species <- set_expt_conditions(tmrc3_external, fact = "ParasiteSpecies") %>%
  set_expt_colors(color_choices[["parasite"]])
## The numbers of samples by condition are:
## 
## lvbraziliensis   lvpanamensis  notapplicable 
##             22             14              3
## Warning in set_expt_colors(., color_choices[["parasite"]]): Colors for the
## following categories are not being used: lvguyanensis.

21.13 Compare the l2FC values

Let us look at the top/bottom 100 genes of these two datasets and see if they have any similarities.

Note to self, set up s4 dispatch on compare_de_tables!

compared <- compare_de_tables(only_tmrc3_table, external_table, first_table = 1, second_table = 1)
## Error in eval(expr, envir, enclos): object 'only_tmrc3_table' not found
compared$scatter
## Error in eval(expr, envir, enclos): object 'compared' not found
compared$correlation
## Error in eval(expr, envir, enclos): object 'compared' not found

22 Compare visits by celltype and C/F

I assume this request came out of the review process, but I am not quite sure where to put it. If I understand it correctly, the goal is to look across visits for combinations of cure and fail (not fail/cure, but v2/v1) and across cell types.

Thus, in order to do this, I will need to combine those three parameters or set up a more complex model to handle this.

t_cellvisitcf <- set_expt_conditions(t_clinical_nobiop, fact = "cell_visit_cf")
## The numbers of samples by condition are:
## 
##    eosinophils_v1_cure eosinophils_v1_failure    eosinophils_v2_cure 
##                      5                      3                      6 
## eosinophils_v2_failure    eosinophils_v3_cure eosinophils_v3_failure 
##                      3                      6                      3 
##      monocytes_v1_cure   monocytes_v1_failure      monocytes_v2_cure 
##                      8                      8                      7 
##   monocytes_v2_failure      monocytes_v3_cure   monocytes_v3_failure 
##                      6                      6                      7 
##    neutrophils_v1_cure neutrophils_v1_failure    neutrophils_v2_cure 
##                      8                      8                      7 
## neutrophils_v2_failure    neutrophils_v3_cure neutrophils_v3_failure 
##                      6                      5                      7
t_cellvisitcf_de <- all_pairwise(t_cellvisitcf, keepers = visittype_contrasts,
                                 model_batch = "svaseq", filter = TRUE,
                                 methods = methods)
## 
##    eosinophils_v1_cure eosinophils_v1_failure    eosinophils_v2_cure 
##                      5                      3                      6 
## eosinophils_v2_failure    eosinophils_v3_cure eosinophils_v3_failure 
##                      3                      6                      3 
##      monocytes_v1_cure   monocytes_v1_failure      monocytes_v2_cure 
##                      8                      8                      7 
##   monocytes_v2_failure      monocytes_v3_cure   monocytes_v3_failure 
##                      6                      6                      7 
##    neutrophils_v1_cure neutrophils_v1_failure    neutrophils_v2_cure 
##                      8                      8                      7 
## neutrophils_v2_failure    neutrophils_v3_cure neutrophils_v3_failure 
##                      6                      5                      7
## Error in hpgl_norm(data, expt_state = expt_state, design = design, transform = transform, : object 'surrogates' not found
t_cellvisitcf_de
## Error in eval(expr, envir, enclos): object 't_cellvisitcf_de' not found
t_cellvisitcf_mono_table <- combine_de_tables(
  t_cellvisitcf_de, keepers = visittype_contrasts_mono, scale_p = TRUE,
  excel = glue("{xlsx_prefix}/DE_Visits/Cure_Fail/monocyte_visit_cf_combined_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cellvisitcf_de' not found
t_cellvisitcf_mono_table
## Error in eval(expr, envir, enclos): object 't_cellvisitcf_mono_table' not found
t_cellvisitcf_mono_sig <- extract_significant_genes(
  t_cellvisitcf_mono_table,
  excel = glue("{xlsx_prefix}/DE_Visits/Cure_Fail/monocyte_visit_cf_combined_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cellvisitcf_mono_table' not found
t_cellvisitcf_mono_sig
## Error in eval(expr, envir, enclos): object 't_cellvisitcf_mono_sig' not found
t_cellvisitcf_neut_table <- combine_de_tables(
  t_cellvisitcf_de, keepers = visittype_contrasts_ne, scale_p = TRUE,
  excel = glue("{xlsx_prefix}/DE_Visits/Cure_Fail/neutrophil_visit_cf_combined_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cellvisitcf_de' not found
t_cellvisitcf_neut_table
## Error in eval(expr, envir, enclos): object 't_cellvisitcf_neut_table' not found
t_cellvisitcf_neut_sig <- extract_significant_genes(
  t_cellvisitcf_neut_table,
  excel = glue("{xlsx_prefix}/DE_Visits/Cure_Fail/neutrophil_visit_cf_combined_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cellvisitcf_neut_table' not found
t_cellvisitcf_neut_sig
## Error in eval(expr, envir, enclos): object 't_cellvisitcf_neut_sig' not found
t_cellvisitcf_eo_table <- combine_de_tables(
  t_cellvisitcf_de, keepers = visittype_contrasts_eo,
  excel = glue("{xlsx_prefix}/DE_Visits/Cure_Fail/eosinophil_visit_cf_combined_table_sva-v{ver}.xlsx"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'expt' in selecting a method for function 'colors': object 't_cellvisitcf_de' not found
t_cellvisitcf_eo_table
## Error in eval(expr, envir, enclos): object 't_cellvisitcf_eo_table' not found
t_cellvisitcf_eo_sig <- extract_significant_genes(
  t_cellvisitcf_eo_table,
  excel = glue("{xlsx_prefix}/DE_Visits/Cure_Fail/eosinophil_visit_cf_combined_sig_sva-v{ver}.xlsx"))
## Error in eval(expr, envir, enclos): object 't_cellvisitcf_eo_table' not found
t_cellvisitcf_eo_sig
## Error in eval(expr, envir, enclos): object 't_cellvisitcf_eo_sig' not found
tmp <- loadme(filename = savefile)

Bibliography

Chung, Matthew, Vincent M. Bruno, David A. Rasko, Christina A. Cuomo, José F. Muñoz, Jonathan Livny, Amol C. Shetty, Anup Mahurkar, and Julie C. Dunning Hotopp. 2021. “Best Practices on the Differential Expression Analysis of Multi-Species RNA-seq.” Genome Biology 22 (April): 121. https://doi.org/10.1186/s13059-021-02337-8.
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.
———. 2021. “Dream: Powerful Differential Expression Analysis for Repeated Measures Designs.” Bioinformatics 37 (2): 192–201. https://doi.org/10.1093/bioinformatics/btaa687.
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.
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.
McCarthy, Davis J., Yunshun Chen, and Gordon K. Smyth. 2012. “Differential Expression Analysis of Multifactor RNA-Seq Experiments with Respect to Biological Variation.” Nucleic Acids Research 40 (10): 4288–97. https://doi.org/10.1093/nar/gks042.
Molania, Ramyar, Momeneh Foroutan, Johann A. Gagnon-Bartsch, Luke C. Gandolfo, Aryan Jain, Abhishek Sinha, Gavriel Olshansky, Alexander Dobrovic, Anthony T. Papenfuss, and Terence P. Speed. 2023. “Removing Unwanted Variation from Large-Scale RNA Sequencing Data with PRPS.” Nature Biotechnology 41 (1): 82–95. https://doi.org/10.1038/s41587-022-01440-w.
Risso, Davide, John Ngai, Terence P. Speed, and Sandrine Dudoit. 2014. “Normalization of RNA-seq Data Using Factor Analysis of Control Genes or Samples.” Nature Biotechnology 32 (9): 896–902. https://doi.org/10.1038/nbt.2931.
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–47. https://doi.org/10.1093/nar/gkv007.
Tarazona, Sonia, Pedro Furió-Tarí, David Turrà, Antonio Di Pietro, María José Nueda, Alberto Ferrer, and Ana Conesa. 2015. “Data Quality Aware Analysis of Differential Expression in RNA-seq with NOISeq R/Bioc Package.” Nucleic Acids Research 43 (21): e140. https://doi.org/10.1093/nar/gkv711.
LS0tCnRpdGxlOiAiVE1SQzMgYHIgU3lzLmdldGVudignVkVSU0lPTicpYDogRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gYW5hbHlzZXMsIFR1bWFjbyBvbmx5LiIKYXV0aG9yOiAiYXRiIGFiZWxld0BnbWFpbC5jb20iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKYmlibGlvZ3JhcGh5OiBhdGIuYmliCnJ1bnRpbWU6IHNoaW55Cm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBkZl9wcmludDogcGFnZWQKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDcKICAgIGhpZ2hsaWdodDogemVuYnVybgogICAga2VlcF9tZDogZmFsc2UKICAgIG1vZGU6IHNlbGZjb250YWluZWQKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgc2VsZl9jb250YWluZWQ6IHRydWUKICAgIHRoZW1lOiByZWFkYWJsZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UKLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpib2R5IC5tYWluLWNvbnRhaW5lciB7CiAgbWF4LXdpZHRoOiAxNjAwcHg7Cn0KYm9keSwgdGQgewogIGZvbnQtc2l6ZTogMTZweDsKfQpjb2RlLnIgewogIGZvbnQtc2l6ZTogMTZweDsKfQpwcmUgewogIGZvbnQtc2l6ZTogMTZweAp9Cjwvc3R5bGU+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KGhwZ2x0b29scykKCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZW5yaWNocGxvdCkKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KGdsdWUpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShsbWU0KQoKZGV2dG9vbHM6OmxvYWRfYWxsKCJ+L2hwZ2x0b29scyIpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHByb2dyZXNzID0gVFJVRSwgdmVyYm9zZSA9IFRSVUUsIHdpZHRoID0gOTAsIGVjaG8gPSBUUlVFKQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZXJyb3IgPSBUUlVFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gOCwgZmlnLnJldGluYSA9IDIsCiAgb3V0LndpZHRoID0gIjEwMCUiLCBkZXYgPSAicG5nIiwKICBkZXYuYXJncyA9IGxpc3QocG5nID0gbGlzdCh0eXBlID0gImNhaXJvLXBuZyIpKSkKb2xkX29wdGlvbnMgPC0gb3B0aW9ucyhkaWdpdHMgPSA0LCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIGtuaXRyLmR1cGxpY2F0ZS5sYWJlbCA9ICJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemUgPSAxMikpCnZlciA8LSBTeXMuZ2V0ZW52KCJWRVJTSU9OIikKcnVuZGF0ZSA8LSBmb3JtYXQoU3lzLkRhdGUoKSwgZm9ybWF0ID0gIiVZJW0lZCIpCgpybWRfZmlsZSA8LSBnbHVlKCIwNGRpZmZlcmVudGlhbF9leHByZXNzaW9uX3R1bWFjby5SbWQiKQpzYXZlZmlsZSA8LSBnc3ViKHBhdHRlcm4gPSAiXFwuUm1kIiwgcmVwbGFjZSA9ICJcXC5yZGFcXC54eiIsIHggPSBybWRfZmlsZSkKbG9hZGVkIDwtIGxvYWQoZmlsZSA9IGdsdWUoInJkYS90bXJjM19kYXRhX3N0cnVjdHVyZXMtdnt2ZXJ9LnJkYSIpKQp4bHN4X3ByZWZpeCA8LSAiYW5hbHlzZXMvNF90dW1hY28iCmNmX3ByZWZpeCA8LSBnbHVlKCJ7eGxzeF9wcmVmaXh9L0RFX0N1cmVfRmFpbCIpCmBgYAoKIyBDaGFuZ2Vsb2cKCiogMjAyNDEyOiBSZW9yZ2FuaXppbmcgdGhlIGxtZSB3b3JrCiogMjAyNDExOiBXb3JraW5nIG9uIHRoZSBhZGRpdGlvbiBvZiBsaW5lYXIgbWl4ZWQgbW9kZWxzLgoqIDIwMjQwNjogQWRkZWQgYW4gZXhwbGljaXQgY29tcGFyaXNvbiBvZiBkaWZmZXJlbnQgbW9kZWwKICBjb25zdHJ1Y3Rpb25zIHVzaW5nIG91ciBtb3N0IHZhcmlhYmxlIGNlbGwgdHlwZSwgdGhlIG5ldXRyb3BoaWxzLgoqIDIwMjQwNjogV29ya2luZyBlbnRpcmVseSBvdXQgb2YgdGhlIGNvbnRhaW5lciBub3csIHNlcGFyYXRlZAogIEdTRS9HU0VBIGFuYWx5c2VzLCBhZGRlZCBhIGZ1bGwgdHJlYXRtZW50IHdpdGggY2x1c3RlclByb2ZpbGVyOyBJIGFtCiAgbm90IGN1cnJlbnRseSB3cml0aW5nIHRoZSBjcCByZXN1bHRzIG91dCBhcyB4bHN4IGZpbGVzIHVudGlsL3VubGVzcwogIHNvbWVvbmUgZXhwcmVzc2VzIGludGVyZXN0IGluIHRoZW0uCiogMjAyMzA5OiBEaXNhYmxlZCBHU1ZBIGFuYWx5c2VzIHVudGlsL3VubGVzcyB3ZSBnZXQgcGVybWlzc2lvbiB0bwogIGluY2x1ZGUgdGhlIG1TaWdEQiA3LjUuMSByZWxlYXNlICh3aGF0IEkgdXNlZCkuICBJIHdpbGwgc2ltcGxpZnkgdGhlCiAgZmlsZW5hbWVzIHNvIHRoYXQgb25lIG1heSBlYXNpbHkgZHJvcCBpbiBhIGRvd25sb2FkZWQgY29weSBvZiB0aGUKICBkYXRhIGFuZCBydW4gaG9zZSBibG9ja3MuICBVbnRpbCB0aGVuLCBJIGd1ZXNzIHlvdSAoZmljdGl0aW91cwogIHJlYWRlcikgd2lsbCBoYXZlIHRvIHRydXN0IG1lIHdoZW4gSSBzYXkgdGhvc2UgYmxvY2tzIGFsbCB3b3JrPwogIChBbHNvLCBHU1ZBIHdhcyBtb3ZlZCB0byBhIHNlcGFyYXRlIGRvY3VtZW50KQoqIDIwMjMwOTogTW92ZWQgYWxsIGdlbmUgc2V0IGVucmljaG1lbnQgYW5hbHlzZXMgdG8gMDRscnRfZ3NlYV9nc3ZhLlJtZAoqIDIwMjMwOSBuZXh0IGRheTogTW92aW5nIGdlbmUgc2V0IGVucmljaG1lbnQgYmFjayBiZWNhdXNlIGl0IGFkZHMgdG9vIG11Y2gKICBjb21wbGV4aXR5IHRvIHNhdmUvcmVsb2FkIHRoZSBERSByZXN1bHRzIGZvciBnUHJvZmlsZXIgYW5kIGZyaWVuZHMuCiogU3RpbGwgaHVudGluZyBmb3IgbWVzc2VkIHVwIGNvbG9ycywgY2hhbmdlZCBpbnB1dCBkYXRhIHRvIG1hdGNoIG5ldyB2ZXJzaW9uLgoKIyBOb3Rlcy9UT0RPcyBmb3IgMjAyNDEyKwoKKiBNZWV0aW5nIHdpdGggTk1FUy9NQUc6IFF1ZXJ5LCB3aGljaCByZXN1bHRzIHRvIHVzZT8gIE1BRyBwcmVmZXJzCiAgREVTZXEyIHdpdGggYSBmdWxsIGRpc2N1c3Npb24gYW5kIHByZXNlbnRhdGlvbiBvZiBsbW0gcmVzdWx0cy4KKiBIb3cgZG8gd2UgcHJlc2VudCB0byB0aGUgcmV2aWV3ZXIgdGhhdCB0aGUgbG1tIGlzIG11Y2ggbW9yZQogIGNvbnNlcnZhdGl2ZT8gIE1BRzogVGhhdCBhcmd1bWVudCBpcyBhbHJlYWR5IGluIHRoZSBzaGFyZWQgZG9jdW1lbnQKICBhbmQgbW9kaWZpZWQgYnkgTmVhbDsgdGhlIHJlYXNvbiBsaWVzIGJvdGggaW4gZHJlYW0gYW5kIGxpbW1hLCB3ZQogIHByb2JhYmx5IHNob3VsZCBpbmNsdWRlIGFuIGFyZ3VtZW50IGluIHN1cHBvcnQgb2YgREVTZXEyLiAgV2lsbCBuZWVkCiAgdG8gc2hvdyB0aGUgc2ltaWxhcml0aWVzIGJldHdlZW4gbGltbWEvZHJlYW0gYW5kIGRlc2VxIHdpdGggdGhlCiAgY2F2ZWF0IG9mIGRpZmZlcmVudCBwLXZhbHVlcy4gIChQZXJoYXBzIHRoaXMgaXMgd2hlcmUgdGhlIEFVQ0MgY29tZXMKICBpbj8pCgoqIFdoYXQgZG8gd2UgdGhpbmsgYWJvdXQgZHJlYW0ncyBhZGp1c3RlZCBwLXZhbHVlIHJlc3VsdHM/CiogQ3JlYXRlIHRhYmxlcyBvZiB0aGUgbG1tIHJlc3VsdHMgYXMgeGxzeCBmaWxlcywgZG8gbm90IGJvdGhlcgogIHB1bGxpbmcgdGhlbSBpbnRvIHRoZSB0YWJsZXMgd2l0aCBkZXNlcSBldGMuCiAgKiogNSB0YWJsZXM6IG1vbm9jeXRlLCBuZXV0cm9waGlsLCBlb3Npbm9waGlsLCBhbGwsIGFsbCtzdmEKKiBDcmVhdGUgc2NhdHRlciBwbG90cyBzaG93aW5nIHNpbWlsYXJpdGllcyBiZXR3ZWVuIHAtdmFsdWVzIHBlcmhhcHMKICBhbmQgei1zY29yZXMsIGFuZCBsb2dGQy4KKiBQZXJmb3JtIEdPIGV0YyB3aXRoIGxtbSByZXN1bHRzLgoKIyBJbnRyb2R1Y3Rpb24KClRoZSB2YXJpb3VzIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2VzIG9mIHRoZSBkYXRhIGdlbmVyYXRlZCBpbgp0bXJjM19kYXRhc2V0cyB3aWxsIG9jY3VyIGluIHRoaXMgZG9jdW1lbnQuICBNb3N0IG9mIHRoZSBhY3R1YWwgd29yawppcyB2aWEgdGhlIGZ1bmN0aW9uICdhbGxfcGFpcndpc2UoKSc7IHRoZSB3b3JkICdhbGwnIGluIHRoZSBuYW1lIGRvZXMKYSBsb3Qgb2Ygd29yazsgaXQgaXMgcmVzcG9uc2libGUgZm9yIHBlcmZvcm1pbmcgYWxsIHBvc3NpYmxlIHBhaXJ3aXNlCmNvbnRyYXN0cyB1c2luZyBhbGwgcG9zc2libGUgbWV0aG9kcyBmb3Igd2hpY2ggSSBoYXZlIHN1ZmZpY2llbnQKdW5kZXJzdGFuZGluZyB0byBiZSBhYmxlIHRvIHdyaXRlIGEgcmVhc29uYWJseSByb2J1c3QgcGFpcndpc2UKZnVuY3Rpb24uICBDdXJyZW50bHkgdGhpcyBpcyBsaW1pdGVkIHRvOgoKKiBERVNlcTIgKEBsb3ZlTW9kZXJhdGVkRXN0aW1hdGlvbkZvbGQyMDE0KTogIE91ciAnZGVmYXVsdCcKKiBlZGdlUiAoQG1jY2FydGh5RGlmZmVyZW50aWFsRXhwcmVzc2lvbkFuYWx5c2lzMjAxMik6ICBzaGFyZXMgYQogIGNsb3NlIGNvbmNlcHR1YWwgbGluZWFnZSB3aXRoIERFU2VxMiBJIHRoaW5rLgoqIGxpbW1hIChAcml0Y2hpZUxpbW1hUG93ZXJzRGlmZmVyZW50aWFsMjAxNWEpOiAgYWxvbmcgd2l0aCB2b29tIHRoaXMKICBwcm92aWRlcyBhIG5pY2VseSByb2J1c3Qgc2V0IG9mIHRvb2xzLgoqIEVCc2VxIChAbGVuZ0VCU2VxRW1waXJpY2FsQmF5ZXMyMDEzKTogIEkgdGhpbmsgaXQgaXMgbm90IGFzIHJvYnVzdCBhcwogIHRoZSBwcmV2aW91cyBlbnRyaWVzLCBidXQgSSBsaWtlIHVzaW5nIGl0IGJlY2F1c2UgaXQgaXMgYW4gYWxtb3N0CiAgcHVyZWx5IGJheWVzaWFuIG1ldGhvZCBhbmQgYXMgc3VjaCBwcm92aWRlcyBhIGRpZmZlcmVudCBwZXJzcGVjdGl2ZQogIG9uIGFueSBkYXRhc2V0LgoqIE5vaXNlcSAoQHRhcmF6b25hRGF0YVF1YWxpdHlBd2FyZTIwMTUpOiAgSSBub3RpY2VkIHRoaXMgbWV0aG9kCiAgcmVsYXRpdmVseSByZWNlbnRseSBhbmQgd2FzIHN1ZmZpY2llbnRseSBpbnRyaWd1ZWQgdGhhdCBJIHRocmV3IGEKICBtZXRob2QgdG9nZXRoZXIgdXNpbmcgaXQuICBUaGUgYXV0aG9ycyBhcHBlYXIgdG8gbWUgdG8gYmUgbG9va2luZyB0bwogIHVuZGVyc3RhbmQgYSBsb3Qgb2YgdGhlIHF1ZXN0aW9ucyBvbiB3aGljaCBJIHNwZW5kIGEgbG90IG9mIHRpbWUuCiogRHJlYW0gKEBob2ZmbWFuRHJlYW1Qb3dlcmZ1bERpZmZlcmVudGlhbDIwMjApOiBJIG1vc3RseSBsaWtlIHRoaXMKICBiZWNhdXNlIGl0IHVzZXMgdmFyaWFuY2VQYXJ0aXRpb24sIHdoaWNoIEkgdGhpbmsgaXMgYSByZWFsbHkgbmljZQogIHRveSB3aGVuIHRyeWluZyB0byB1bmRlcnN0YW5kIHdoYXQgaXMgZ29pbmcgb24gaW4gYSBkYXRhc2V0LgoqIGJhc2ljIGlzIG15IG93biwgZXhwbGljaXRseSB1bmluZm9ybWVkIGFuYWx5c2lzLiAgSXQgaXMgbXkKICAnbmVnYXRpdmUgY29udHJvbCcgbWV0aG9kIGJlY2F1c2UsIGlmIHNvbWV0aGluZyBhZ3JlZXMgZW50aXJlbHkgd2l0aAogIGl0LCB0aGVuIEkga25vdyB0aGF0IGFsbCB0aGUgZmFuY3kgbWF0aCBhbmQgc3RhdGlzdGljcyBwZXJmb3JtZWQgYnkKICB0aGF0IG1ldGhvZCB3b3JrZWQgb3V0IGp1c3QgdGhlIHNhbWUgYXMgc29tZSBkb29mdXMgKG1lKSBqdXN0IGxvZzIKICBzdWJ0cmFjdGluZyB0aGUgZXhwcmVzc2lvbiB2YWx1ZXMuICBJdCBpcyBub3QgcXVpdGUgdGhhdCBiYXNpYywgYnV0CiAgcHJldHR5IGNsb3NlLgoKVGhlIGZpcnN0IDMgbWV0aG9kcyBhbGxvdyBvbmUgdG8gYWRkIHN1cnJvZ2F0ZSB2YXJpYWJsZSBlc3RpbWF0ZXMgdG8KdGhlIG1vZGVsIHdoZW4gcGVyZm9ybWluZyB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzZXMuCk5vaXNlcSBoYW5kbGVzIHN1cnJvZ2F0ZXMgdXNpbmcgaXRzIG93biBoZXVyaXN0aWNzLCBFQlNlcQppcyBpbmltaWNhYmxlIHRvIHRoYXQga2luZCBvZiBtb2RlbCwgYW5kIEkgZXhwbGljaXRseSBjaG9zZSB0byBub3QKbWFrZSB0aGF0IHBvc3NpYmxlIGZvciBiYXNpYy4gIEkgYW0gdW5jZXJ0YWluIGF0IHRoaXMgdGltZSBob3cgdGhlCnJhbmRvbSBlZmZlY3QgZmFjdG9ycyB1c2VkIHdpdGggZHJlYW0gaW50ZXJhY3Qgd2l0aCBzdXJyb2dhdGVzIGZyb20Kc3ZhLiAgV2l0aCB0aGF0IGluIG1pbmQsIGluIG1vc3QgaW5zdGFuY2VzIEkgdXN1YWxseSBkZWFsIHdpdGgKc3Vycm9nYXRlcy9iYXRjaGVzIGluIG9uZSBvZiBhIGZldyB3YXlzOgoKMS4gIElmIHRoZSBkYXRhIGlzIGFic3VyZGx5IHByZXR0eSwgZG8gbm90aGluZyAocHJldHR5IG11Y2gKICAgIG9ubHkgZm9yIHdlbGwtY29udHJvbGxlZCBiYWN0ZXJpYWwgZGF0YSkuCjIuICBBZGQgYSBrbm93biBiYXRjaCBmYWN0b3IgdG8gdGhlIG1vZGVsICh0aGUgZGVmYXVsdCkuCjMuICBUcnkgdG8gZW5zdXJlIHRoZSBkYXRhIGlzIHN1aXRhYmxlIGFuZCBpbnZva2Ugc3ZhCiAgICAoQGxlZWtTVkFQYWNrYWdlUmVtb3ZpbmcyMDEyKSB0byBhY3F1aXJlIGVzdGltYXRlcyBhbmQgYWRkIHRoZW0gdG8KICAgIHRoZSBtb2RlbC4KNC4gIElmIHRoZSBkYXRhIGhhcyBhIGtub3duIGJhdGNoIGZhY3RvciBhbmQgaXQgaXMgcGFydGljdWxhcmx5CiAgICBwYXRob2xvZ2ljYWwsIHVzZSB0aGUgY29tYmF0IGltcGxlbWVudGF0aW9uIGluIHN2YS4gIEFzIGEgZ2VuZXJhbAogICAgcnVsZSBJIGRvIG5vdCBsaWtlIHRoaXMgb3B0aW9uIGJlY2F1c2UgaXQgaXMgZGF0YSBkZXN0cnVjdGl2ZS4KClRoZSBsYXN0IHR3byBvcHRpb25zIGFyZSBoYW5kbGVkIHZpYSBhIGZ1bmN0aW9uIG5hbWVkICdhbGxfYWRqdXN0ZXJzJwppbiBocGdsdG9vbHMgd2hpY2ggaXMgcmVzcG9uc2libGUgZm9yIGVuc3VyaW5nIHRoYXQgdGhlIGRhdGEgaXMgc2FuZQpmb3IgdGhlIGFzc3VtcHRpb25zIG1hZGUgYnkgZWFjaCBtZXRob2QgYW5kIGludm9rZXMgZWFjaCBtZXRob2QKKGhvcGVmdWxseSkgcHJvcGVybHkuICBJdCByZXR1cm5zIGJvdGggbW9kaWZpZWQgY291bnRzIGFuZCBtb2RlbAplc3RpbWF0ZXMgd2hlbiBwb3NzaWJsZSBhbmQgaGFzIGltcGxlbWVudGF0aW9ucyBmb3IgYSBmYWlyIG51bWJlciBvZgptZXRob2RzIGluIHRoaXMgcmVhbG0uICBzdmEgaXMgbXkgZmF2b3JpdGUgYnkgYSBwcmV0dHkgYmlnIG1hcmdpbiwKdGhvdWdoIEkgZG8gc29tZXRpbWVzIHVzZSBSVVYgKEByaXNzb05vcm1hbGl6YXRpb25STkFzZXFEYXRhMjAxNCkgYW5kIG9mCmNvdXJzZSwgaW4gd3JpdGluZyB0aGlzIGRvY3VtZW50IEkgc3R1bWJsZWQgaW50byBhbm90aGVyIGludGVyZXN0aW5nCmNvbnRlbmRlcjogKEBtb2xhbmlhUmVtb3ZpbmdVbndhbnRlZFZhcmlhdGlvbjIwMjMpICBhbGxfYWRqdXN0ZXJzKCkKYWxzbyBoYXMgaW1wbGVtZW50YXRpb25zIG9mIGV2ZXJ5IGV4YW1wbGUvbWV0aG9kIEkgZ290IG91dCBvZiB0aGUKcGFwZXJzIGZvciBzdmEgKGUuZy4gc3N2YS9mc3ZhKSwgaXN2YSwgc21hcnRzdmEsIGFuZCBzb21lIG90aGVycy4KCkkgaGF2ZSBiZWVuIGNoYW5naW5nIGhwZ2x0b29scyBzbyB0aGF0IGl0IGlzIG5vdyBwb3NzaWJsZSB0byB0cml2aWFsbHkKcGFzcyBhcmJpdHJhcmlseSBjb21wbGV4IG1vZGVscyB0byB0aGUgdmFyaW91cyBtZXRob2RzOyB3aXRoIHRoZQpjYXZlYXQgdGhhdCB0aGVyZSBpcyBubyBnb29kIHdheSBjdXJyZW50bHkgdG8gbWl4IGZpeGVkIGVmZmVjdHMgYW5kCnJhbmRvbSBlZmZlY3RzIGFjcm9zcyBtZXRob2RzOyBzbyBJIGFtIHJ1bm5pbmcgZHJlYW0gc2VwYXJhdGVseSBhbmQKYWRkaW5nIGl0IHRvIHRoZSByZXN1bHQgb2YgYWxsX3BhaXJ3aXNlIHBvc3QtZmFjdG8uCgojIyBEZWZpbmUgY29udHJhc3RzIGZvciBERSBhbmFseXNlcwoKRWFjaCBvZiB0aGUgZm9sbG93aW5nIGxpc3RzIGRlc2NyaWJlcyB0aGUgc2V0IG9mIGNvbnRyYXN0cyB0aGF0IEkKdGhpbmsgYXJlIGludGVyZXN0aW5nIGZvciB0aGUgdmFyaW91cyB3YXlzIG9uZSBtaWdodCBjb25zaWRlciB0aGUKVE1SQzMgZGF0YXNldC4gIFRoZSB2YXJpYWJsZXMgYXJlIG5hbWVkIGFjY29yZGluZyB0byB0aGUgYXNzdW1lZCBkYXRhCndpdGggd2hpY2ggdGhleSB3aWxsIGJlIHVzZWQsIHRodXMgdGNfY2ZfY29udHJhc3RzIGlzIGV4cGVjdGVkIHRvIGJlCnVzZWQgZm9yIHRoZSBUdW1hY28rQ2FsaSBkYXRhIGFuZCBwcm92aWRlIGEgc2VyaWVzIG9mIGN1cmUvZmFpbApjb21wYXJpc29ucyB3aGljaCAodG8gdGhlIGV4dGVudCBwb3NzaWJsZSkgYWNyb3NzIGJvdGggbG9jYXRpb25zLiAgSW4KZXZlcnkgY2FzZSwgdGhlIG5hbWUgb2YgdGhlIGxpc3QgZWxlbWVudCB3aWxsIGJlIHVzZWQgYXMgdGhlIGNvbnRyYXN0Cm5hbWUsIGFuZCB3aWxsIHRodXMgYmUgc2VlbiBhcyB0aGUgc2hlZXQgbmFtZSBpbiB0aGUgb3V0cHV0IHhsc3gKZmlsZShzKTsgdGhlIHR3byBwaWVjZXMgb2YgdGhlIGNoYXJhY3RlciB2ZWN0b3IgdmFsdWUgYXJlIHRoZQpudW1lcmF0b3IgYW5kIGRlbm9taW5hdG9yIG9mIHRoZSBhc3NvY2lhdGVkIGNvbnRyYXN0LgoKKiBPdXIgcHJpbWFyeSBxdWVzdGlvbjogZmFpbC9jdXJlOiAgQW55IGV4Y2VsIGZpbGUgd3JpdHRlbiB1c2luZyB0aGlzCmNvbnRyYXN0IHdpbGwgZ2V0IGEgc2luZ2xlIHdvcmtzaGVldCBjb21wYXJpbmcgZmFpbC9jdXJlLgoqIENvbXBhcmUgZmFpbC9jdXJlIGZvciBlYWNoIHZpc2l0OiBUaGlzIHRha2VzIGEgbW9yZSBncmFudWxhciB2aWV3IG9mCnRoZSBwcmV2aW91cyBjb250cmFzdC4gIElmIG9uZSBpcyBzby1pbmNsaW5lZCwgb25lIGNvdWxkIGNvbXBhcmUKcmVzdWx0cyBmcm9tIHRoZSBmb2xsb3dpbmcgY29udHJhc3QgYWdhaW5zdCB0aGUgcHJldmlvdXMgYW5kIGZvbGxvd2luZwpjb250cmFzdCB0byBsZWFybiBhYm91dCB0aGUgZHluYW1pY3Mgb2YgdGhlIGhlYWxpbmcgKG9yIG5vdCkgcHJvY2Vzcy4KKiBBbGwgc2FtcGxlcyBieSB2aXNpdDogVGhpcyBpcyBlZmZlY3RpdmVseSB0aGUgb3Bwb3NpdGUgb2YgdGhlCnByZXZpb3VzIGFuZCBjb21wYXJlcyBhbGwgc2FtcGxlcyBvZiB2aXNpdCB4IGFnYWluc3QgdmlzaXQgeS4KKiBWaXNpdCAxIHZzIGV2ZXJ5dGhpbmcgZWxzZTogV2hlbiBJIGZpcnN0IGRpZCB0aGUgcHJldmlvdXMgc2V0IG9mCmNvbnRyYXN0cyBJIHF1aWNrbHkgcmVhbGl6ZWQgdGhhdCB2aXNpdHMgMiBhbmQgMyBhcmUgcmVsYXRpdmVseQpzaW1pbGFyIGFuZCB0aGF0IGl0IG1heSBiZSBwb3NzaWJsZSB0byBnYWluIGEgbGl0dGxlIHBvd2VyIGFuZCBsZWFybiBhCmxpdHRsZSBtb3JlIGJ5IGNvbWJpbmluZyB0aGVtLgoqIERpcmVjdGx5IGNvbXBhcmUgY2VsbHR5cGVzOiBXZSBoYXZlIHRocmVlIGNsaW5pY2FsIGNlbGwgdHlwZXMgaW4gdGhlCmRhdGEgYW5kIHRoZSBkaWZmZXJlbmNlcyBhbW9uZyB0aGVtIGFyZSBxdWl0ZSBpbnRlcmVzdGluZy4KKiBFdGhuaWNpdGllczogV2UgYWxzbyBoYXZlIHRocmVlIGV0aG5pYyBncm91cHMgaW4gdGhlIGRhdGEsIHRob3VnaAp0aGVyZSBhcmUgc29tZSB3YWNreSBjb25mb3VuZGVkIHZhcmlhYmxlcyB3aGVuIGNvbnNpZGVyaW5nIHRoZW0KdGhyb3VnaCB0aGUgbGVuc2Ugb2YgY3VyZS9mYWlsOyBzbyBhbnkgcmVzdWx0cyBjb21wYXJpbmcgdGhlbSBzaG91bGQKYmUgdHJlYXRlZCB3aXRoIGNhdXRpb24uCiogUG93ZXJsZXNzIHZpc2l0cytjZWxsdHlwZStjZjogVGhpcyBpcyBhIGxhc3QtbWludXRlIGFkZGl0aW9uCnJlcXVlc3RlZCBieSBNYXJpYSBBZGVsYWlkYS4gIEkgYXNzdW1lIGl0IHdhcyBzdWdnZXN0ZWQgYnkgYSByZXZpZXdlciwKdGhvdWdoIEkgZG8gbm90IHJlY2FsbCBzZWVpbmcgYW55dGhpbmcgaW4gdGhlIHJldmlld3Mgd2hpY2ggbWFkZSB0aGlzCnJlcXVlc3QuICBUaGUgbnVtYmVyIG9mIHNhbXBsZXMgd2UgaGF2ZSBpbiB0aGUgZGF0YSBqdXN0IF9iYXJlbHlfCnN1cHBvcnRzIHRoZXNlIGNvbnRyYXN0cywgYW5kIGdpdmVuIHRoZSBzdHJlbmd0aCBvZiBhbGwgdGhlIHZhcmlvdXMKc3Vycm9nYXRlcywgSSB3b3VsZCBiZSBzb21ld2hhdCByZWx1Y3RhbnQgdG8gdHJ1c3QgYW55IGdlbmVzIGRlZW1lZCBERQppbiB0aGVtIHdpdGhvdXQgc29tZSBvdGhlciBldmlkZW5jZS4gIEl0IHNob3VsZCBiZSBub3RlZCB0aGF0IHRoaXMgaXMKdGhlIGludGVsbGVjdHVhbCBjb3VudGVycG9pbnQgdG8gdGhlIGNyaXRpcXVlIGZyb20gYSBkaWZmZXJlbnQKcmV2aWV3ZXIsIHRoYXQgYXJ0aWZpY2FsbHkgbWVyZ2luZyBmYWN0b3JzIGxpa2UgdGhpcyBpcyBwcm9ibGVtYXRpYyAoSQpwZXJzb25hbGx5IHRlbmQgdG8gYWdyZWUgd2l0aCB0aGUgbGF0ZXIgYXJndW1lbnQgbW9yZSB0aGFuIHRoZSBmb3JtZXIKd2l0aCB0aGUgY2F2ZWF0IHRoYXQgdGhlIGFkZGVkIGNvbXBsZXhpdHkgKHdpdGggcmVzcGVjdCB0byB3aGF0IGlzCmFjdHVhbGx5IHR5cGVkIGJ5IHRoZSBwZXJzb24gKG1lKSkgY2FuIGJlIGEgcHJvYmxlbS4gIFRodXMgSSB0ZW5kIHRvCmRvIHRoZSB0aGluZyB3aGljaCBpcyBleHBsaWNpdGx5IGxlc3Mgc3RhdGlzdGljYWxseSBjb3JyZWN0IChidXQgSSBjYW4KYWxzbyBzaG93IHByZXR0eSBkZWZpbml0aXZlbHkgdGhhdCB0aGUgcmVzdWx0cyBhcmUgdmVyeSBuZWFybHkKaWRlbnRpY2FsKSBpbiBvcmRlciB0byBtYWtlIGl0IGVhc2llciB0byBzaG93IHRoYXQgbm8gbWlzdGFrZXMgd2VyZQptYWRlLiAgRS5nLiB0ZW5zaW9uIGJldHdlZW4gJ2NvcnJlY3RuZXNzJyBhbmQgJ3JvYnVzdG5lc3MnLgoKYGBge3J9CnRfY2ZfY29udHJhc3QgPC0gbGlzdCgKICAib3V0Y29tZSIgPSBjKCJ0dW1hY29fZmFpbHVyZSIsICJ0dW1hY29fY3VyZSIpKQpjZl9jb250cmFzdCA8LSBsaXN0KAogICJvdXRjb21lIiA9IGMoImZhaWx1cmUiLCAiY3VyZSIpKQp2aXNpdGNmX2NvbnRyYXN0cyA8LSBsaXN0KAogICJ2MWNmIiA9IGMoInYxX2ZhaWx1cmUiLCAidjFfY3VyZSIpLAogICJ2MmNmIiA9IGMoInYyX2ZhaWx1cmUiLCAidjJfY3VyZSIpLAogICJ2M2NmIiA9IGMoInYzX2ZhaWx1cmUiLCAidjNfY3VyZSIpKQp2aXNpdF9jb250cmFzdHMgPC0gbGlzdCgKICAidjJ2MSIgPSBjKCJ2MiIsICJ2MSIpLAogICJ2M3YxIiA9IGMoInYzIiwgInYxIiksCiAgInYzdjIiID0gYygidjMiLCAidjIiKSkKdmlzaXRfdjFsYXRlciA8LSBsaXN0KAogICJsYXRlcl92c19maXJzdCIgPSBjKCJsYXRlciIsICJmaXJzdCIpKQpjZWxsdHlwZXMgPC0gbGlzdCgKICAiZW9fbW9ubyIgPSBjKCJlb3Npbm9waGlscyIsICJtb25vY3l0ZXMiKSwKICAibmVfbW9ubyIgPSBjKCJuZXV0cm9waGlscyIsICJtb25vY3l0ZXMiKSwKICAiZW9fbmUiID0gYygiZW9zaW5vcGhpbHMiLCAibmV1dHJvcGhpbHMiKSkKZXRobmljaXR5X2NvbnRyYXN0cyA8LSBsaXN0KAogICJtZXN0aXpvX2luZGlnZW5vdXMiID0gYygibWVzdGl6YSIsICJpbmRpZ2VuYSIpLAogICJtZXN0aXpvX2Fmcm9jb2wiID0gYygibWVzdGl6YSIsICJhZnJvY29sIiksCiAgImluZGlnZW5vdXNfYWZyb2NvbCIgPSBjKCJpbmRpZ2VuYSIsICJhZnJvY29sIikpCm91dGNvbWV0eXBlX2NvbnRyYXN0cyA8LSBsaXN0KAogICJtb25vY3l0ZV9jZiIgPSBjKCJmYWlsdXJlX21vbm9jeXRlcyIsICJjdXJlX21vbm9jeXRlcyIpLAogICJuZXV0cm9waGlsX2NmIiA9IGMoImZhaWx1cmVfbmV1dHJvcGhpbHMiLCAiY3VyZV9uZXV0cm9waGlscyIpLAogICJlb3Npbm9waGlsX2NmIiA9IGMoImZhaWx1cmVfZW9zaW5vcGhpbHMiLCAiY3VyZV9lb3Npbm9waGlscyIpKQp2aXNpdHR5cGVfY29udHJhc3RzX21vbm8gPC0gbGlzdCgKICAidjJ2MV9tb25vX2N1cmUiID0gYygibW9ub2N5dGVzXzJfY3VyZSIsICJtb25vY3l0ZXNfMV9jdXJlIiksCiAgInYydjFfbW9ub19mYWlsdXJlIiA9IGMoIm1vbm9jeXRlc18yX2ZhaWx1cmUiLCAibW9ub2N5dGVzXzFfZmFpbHVyZSIpLAogICJ2M3YxX21vbm9fY3VyZSIgPSBjKCJtb25vY3l0ZXNfM19jdXJlIiwgIm1vbm9jeXRlc18xX2N1cmUiKSwKICAidjN2MV9tb25vX2ZhaWx1cmUiID0gYygibW9ub2N5dGVzXzNfZmFpbHVyZSIsICJtb25vY3l0ZXNfMV9mYWlsdXJlIikpCnZpc2l0dHlwZV9jb250cmFzdHNfZW8gPC0gbGlzdCgKICAidjJ2MV9lb19jdXJlIiA9IGMoImVvc2lub3BoaWxzXzJfY3VyZSIsICJlb3Npbm9waGlsc18xX2N1cmUiKSwKICAidjJ2MV9lb19mYWlsdXJlIiA9IGMoImVvc2lub3BoaWxzXzJfZmFpbHVyZSIsICJlb3Npbm9waGlsc18xX2ZhaWx1cmUiKSwKICAidjN2MV9lb19jdXJlIiA9IGMoImVvc2lub3BoaWxzXzNfY3VyZSIsICJlb3Npbm9waGlsc18xX2N1cmUiKSwKICAidjN2MV9lb19mYWlsdXJlIiA9IGMoImVvc2lub3BoaWxzXzNfZmFpbHVyZSIsICJlb3Npbm9waGlsc18xX2ZhaWx1cmUiKSkKdmlzaXR0eXBlX2NvbnRyYXN0c19uZSA8LSBsaXN0KAogICJ2MnYxX25lX2N1cmUiID0gYygibmV1dHJvcGhpbHNfMl9jdXJlIiwgIm5ldXRyb3BoaWxzXzFfY3VyZSIpLAogICJ2MnYxX25lX2ZhaWx1cmUiID0gYygibmV1dHJvcGhpbHNfMl9mYWlsdXJlIiwgIm5ldXRyb3BoaWxzXzFfZmFpbHVyZSIpLAogICJ2M3YxX25lX2N1cmUiID0gYygibmV1dHJvcGhpbHNfM19jdXJlIiwgIm5ldXRyb3BoaWxzXzFfY3VyZSIpLAogICJ2M3YxX25lX2ZhaWx1cmUiID0gYygibmV1dHJvcGhpbHNfM19mYWlsdXJlIiwgIm5ldXRyb3BoaWxzXzFfZmFpbHVyZSIpKQp2aXNpdHR5cGVfY29udHJhc3RzIDwtIGModmlzaXR0eXBlX2NvbnRyYXN0c19tb25vLAogICAgICAgICAgICAgICAgICAgICAgICAgdmlzaXR0eXBlX2NvbnRyYXN0c19lbywKICAgICAgICAgICAgICAgICAgICAgICAgIHZpc2l0dHlwZV9jb250cmFzdHNfbmUpCmBgYAoKIyMgR2VuZSBTZXQgRW5yaWNobWVudCAvIG92ZXIgcmVwcmVzZW50YXRpb24KClByZXZpb3VzbHksIHRoZSBvdmVyIHJlcHJlc2VudGF0aW9uIGFuYWx5c2VzIChlLmcuIEdPIGFuZCBmcmllbmRzKQpmb2xsb3dlZCBlYWNoIERFIGFuYWx5c2lzIGR1cmluZyB0aGlzIGRvY3VtZW50LiAgSSByZWNlbnRseSBtZW50YWxseQpzZXZlcmVkIG15IGNvbmNlcHRpb24gb2YgR08gYW5hbHlzZXMgaW50byB0d28gY2FtcHM6IG92ZXIKcmVwcmVzZW50YXRpb24gYW5hbHlzZXMgaW4gd2hpY2ggb25lIHByb3ZpZGVzIGEgZ3JvdXAgb2YgZ2VuZXMgZGVlbWVkCnNpZ25pZmljYW50IGluIHNvbWUgd2F5IGFuZCBhc2tzIGlmIHRoZXJlIGFyZSBrbm93biBjYXRlZ29yaWVzIHdoaWNoCmNvbnRhaW4gdGhlc2UgZ2VuZXMgbW9yZSB0aGFuIG9uZSB3b3VsZCBleHBlY3QgYXQgcmFuZG9tLiAgSW4KY29udHJhc3QsIEkgYW0gZGVmaW5pbmcgZ2VuZSBzZXQgZW5yaWNobWVudCBhbmFseXNlcyBleHBsY2l0bHkgYXMgdGhlCnByb2Nlc3Mgb2YgcGFzc2luZyBhbGwgZ2VuZXMgd2l0aCB0aGVpciBtZXRyaWMgb2YgY2hvaWNlIChsb2dGQywKZXhwcnMsIHdoYXRldmVyKSBhbmQgYXNraW5nIGlmIHRoZSBkaXN0cmlidXRpb24gb2YgYWxsIGdlbmVzIGlzCnNpZ25pZmljYW50IHdpdGggcmVzcGVjdCB0byB0aGUgY2F0ZWdvcmllcy4KV2l0aCB0aGF0IGluIG1pbmQsIEkgYWRkZWQgYSBzZXJpZXMgb2YgZXhwbGljaXRseSBHU0VBIGFuYWx5c2VzIGluIG15CmxhdGVyIGl0ZXJhdGlvbnMgb2YgdGhlc2UgZG9jdW1lbnRzIHNvIHRoYXQgYm90aCB3YXlzIG9mIHRoaW5raW5nIGFyZQpwcm92aWRlZC4KCkhvd2V2ZXIsIEkgbW92ZWQgdGhvc2UgYW5hbHlzZXMgdG8gYSBzZXBhcmF0ZSBkb2N1bWVudAooMDVlbnJpY2htZW50LlJtZCkgaW4gdGhlIGhvcGVzIG9mIGltcHJvdmluZyB0aGVpciBvcmdhbml6YXRpb24uCgojIE9ubHkgVHVtYWNvIHNhbXBsZXMKClN0YXJ0IG92ZXIsIHRoaXMgdGltZSB3aXRoIG9ubHkgdGhlIHNhbXBsZXMgZnJvbSBUdW1hY28uICBXZSBjdXJyZW50bHkKYXJlIGFzc3VtaW5nIHRoZXNlIHdpbGwgcHJvdmUgdG8gYmUgdGhlIG9ubHkgYW5hbHlzZXMgdXNlZCBmb3IgZmluYWwKaW50ZXJwcmV0YXRpb24uICBUaGlzIGlzIHByaW1hcmlseSBiZWNhdXNlIHdlIGhhdmUgaW5zdWZmaWNpZW50CnNhbXBsZXMgd2hpY2ggZmFpbGVkIHRyZWF0bWVudCBmcm9tIENhbGkuICBUaGVyZSBpcyBvbmUgZGlzYWR2YW50YWdlCndoZW4gdXNpbmcgdGhlc2Ugc2FtcGxlczogdGhleSBoYWQgdG8gdHJhdmVsIGZ1cnRoZXIgdGhhbiB0aGUgc2FtcGxlcwp0YWtlbiBpbiBDYWxpIGFuZCB0aGVyZSBpcyBzaWduaWZpY2FudCB2YXJpYW5jZSBvYnNlcnZlZCBiZXR3ZWVuIHRoZQp0d28gbG9jYXRpb25zIGFuZCB3ZSBjYW5ub3QgZGlzY2VybiBpdHMgc291cmNlLiAgSW4gdGhlIHdvcnN0IGNhc2UKc2NlbmFyaW8gKG9uZSB3aGljaCBJIHRoaW5rIHVubGlrZWx5KSwgdGhlIHZhcmlhbmNlIGlzIGNhdXNlZApieSBkZWdyYWRlZCBSTkEgZHVyaW5nIHRyYW5zaXQuICBXZSBkbyBrbm93IHRoYXQgdGhlIHNhbXBsZXMgd2VyZQp3ZWxsLXN0b3JlZCBpbiBSTkFMYXRlciBhbmQgZnJvemVuL2V0Yywgc28gSSBhbSBpbmNsaW5lZCB0byBkaXNjb3VudAp0aGF0IHBvc3NpYmlsaXR5LiAgKEFsc28sIGxvb2tpbmcgYXQgdGhlIHJlYWRzIGluIElHViB0aGV5IGRvbid0Cidsb29rJyBkZWdyYWRlZCB0byBtZS4pICBJIHRoaW5rIGEgbW9yZSBjb21wZWxsaW5nIGRpZmZlcmVuY2UgbGllcyBpbgp0aGUgZGlmZmVyZW50IHBvcHVsYXRpb24gZGVtb2dyYXBoaWNzIG9ic2VydmVkIGluIHRoZSB0d28gbG9jYXRpb25zLgpBY3R1YWxseSwgbm93IHRoYXQgSSBoYXZlIHR5cGVkIHRoZXNlIHNlbnRlbmNlcyBvdXQsIEkgdGhpbmsgSSBjYW4Kc2VtaS10ZXN0IHRoaXMgaHlwb3RoZXNpcyBieSBsb29raW5nIGF0IHRoZSBzZXQgb2YgREUgZ2VuZXMgYmV0d2Vlbgp0aGUgdHdvIGxvY2F0aW9ucyBhbmQgY29tcGFyZSB0aGF0IHJlc3VsdCB0byB0aGUgVHVtYWNvIChhbmQvb3IgQ2FsaSkKZXRobmljaXR5IGNvbXBhcmlzb24gd2hpY2ggaXMgbW9zdCByZXByZXNlbnRhdGl2ZSBvZiB0aGUgZXRobmljaXR5CmRpZmZlcmVuY2VzIGJldHdlZW4gdGhlbS4gIElmIEkgZ2V0IGl0IGludG8gbXkgaGVhZCB0byB0cnkgdGhpcywgSQp3aWxsIG5lZWQgdG8gbG9hZCB0aGUgREUgdGFibGVzIGZyb20gdGhlCjAzZGlmZmVyZW50aWFsX2V4cHJlc3Npb25fYm90aC5SbWQgZG9jdW1lbnQ7IHNvIEkgYW0gbW9zdCBsaWtlbHkgdG8KdHJ5IGl0IG91dCBpbiB0aGUgMDd2YXJfY29lZiBkb2N1bWVudCwgd2hpY2ggd2FzIG1vc3RseSB3cml0dGVuIGJ5ClRoZXJlc2EgYW5kIGlzIGFscmVhZHkgZXhhbWluaW5nIHNvbWUgc2ltaWxhciBxdWVzdGlvbnMuCgojIyBBbGwgc2FtcGxlcwoKU3RhcnQgYnkgY29uc2lkZXJpbmcgYWxsIFR1bWFjbyBjZWxsIHR5cGVzLiAgTm90ZSB0aGF0IGluIHRoaXMgY2FzZSB3ZQpvbmx5IHVzZSBTVkEsIHByaW1hcmlseSBiZWNhdXNlIEkgYW0gbm90IGNlcnRhaW4gd2hhdCB3b3VsZCBiZSBhbgphcHByb3ByaWF0ZSBiYXRjaCBmYWN0b3IsIHBlcmhhcHMgdmlzaXQ/CgpgYGB7cn0KdF9jZl9jbGluaWNhbF9kZV9zdmEgPC0gYWxsX3BhaXJ3aXNlKHRfY2xpbmljYWwsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfY2xpbmljYWwgPC0gdF9jZl9jbGluaWNhbF9kZV9zdmFbWyJpbnB1dCJdXQp0X2NmX2NsaW5pY2FsX2RlX3N2YQp0X2NmX2NsaW5pY2FsX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X2NmX2NsaW5pY2FsX2RlX3N2YSwga2VlcGVycyA9IGNmX2NvbnRyYXN0LAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vQWxsX1NhbXBsZXMvdF9jbGluaWNhbF9jZl90YWJsZV9zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZl9jbGluaWNhbF90YWJsZV9zdmEKdF9jZl9jbGluaWNhbF90YWJsZV9zdmFbWyJwbG90cyJdXVtbIm91dGNvbWUiXV1bWyJkZXNlcV9tYV9wbG90cyJdXQp0X2NmX2NsaW5pY2FsX3NpZ19zdmEgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X2NmX2NsaW5pY2FsX3RhYmxlX3N2YSwKICBleGNlbCA9IGdsdWUoIntjZl9wcmVmaXh9L0FsbF9TYW1wbGVzL3RfY2xpbmljYWxfY2Zfc2lnX3N2YS12e3Zlcn0ueGxzeCIpKQp0X2NmX2NsaW5pY2FsX3NpZ19zdmEKCmRpbSh0X2NmX2NsaW5pY2FsX3NpZ19zdmFbWyJkZXNlcSJdXVtbInVwcyJdXVtbMV1dKQpkaW0odF9jZl9jbGluaWNhbF9zaWdfc3ZhW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbMV1dKQpgYGAKClJlcGVhdCB3aXRob3V0IHRoZSBiaW9wc2llcy4KCmBgYHtyfQp0X2NmX2NsaW5pY2FsbmJfZGVfc3ZhIDwtIGFsbF9wYWlyd2lzZSh0X2NsaW5pY2FsX25vYmlvcCwgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfY2xpbmljYWxfbm9iaW9wIDwtIHRfY2ZfY2xpbmljYWxuYl9kZV9zdmFbWyJpbnB1dCJdXQp0X2NmX2NsaW5pY2FsbmJfZGVfc3ZhCnRfY2ZfY2xpbmljYWxuYl90YWJsZV9zdmEgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgdF9jZl9jbGluaWNhbG5iX2RlX3N2YSwga2VlcGVycyA9IGNmX2NvbnRyYXN0LCBzY2FsZV9wID0gVFJVRSwKICBleGNlbCA9IGdsdWUoIntjZl9wcmVmaXh9L0FsbF9TYW1wbGVzL3RfY2xpbmljYWxfbm9iaW9wX2NmX3RhYmxlX3N2YS12e3Zlcn0ueGxzeCIpKQp0X2NmX2NsaW5pY2FsbmJfdGFibGVfc3ZhCnRfY2ZfY2xpbmljYWxuYl90YWJsZV9zdmFbWyJwbG90cyJdXVtbIm91dGNvbWUiXV1bWyJkZXNlcV9tYV9wbG90cyJdXQp0X2NmX2NsaW5pY2FsbmJfc2lnX3N2YSA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHRfY2ZfY2xpbmljYWxuYl90YWJsZV9zdmEsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9BbGxfU2FtcGxlcy90X2NsaW5pY2FsX25vYmlvcF9jZl9zaWdfc3ZhLXZ7dmVyfS54bHN4IikpCnRfY2ZfY2xpbmljYWxuYl9zaWdfc3ZhCgpkaW0odF9jZl9jbGluaWNhbG5iX3NpZ19zdmFbWyJkZXNlcSJdXVtbInVwcyJdXVtbMV1dKQpkaW0odF9jZl9jbGluaWNhbG5iX3NpZ19zdmFbWyJkZXNlcSJdXVtbImRvd25zIl1dW1sxXV0pCmBgYAoKQXMgdGhlIGRhdGEgc3RydWN0dXJlJ3MgbmFtZSBzdWdnZXN0cywgdGhlIGFib3ZlIGNvbXBhcmlzb24gc2Vla3MgdG8KbGVhcm4gaWYgdGhlcmUgYXJlIGZhaWwvY3VyZSBkaWZmZXJlbmNlcyBkaXNjZXJuYWJsZSBhY3Jvc3MgYWxsCmNsaW5pY2FsIGNlbGx0eXBlcyBpbiBzYW1wbGVzIHRha2VuIGluIFR1bWFjby4KClRoZSBzZXQgb2Ygc3RlcHMgdGFrZW4gaW4gdGhpcyBwcmV2aW91cyBibG9jayB3aWxsIGJlIGVzc2VudGlhbGx5CnJlcGVhdGVkIGZvciBldmVyeSBzZXQgb2YgY29udHJhc3RzIGFuZCB3YXkgb2YgbWl4aW5nL21hdGNoaW5nIHRoZQpkYXRhIGFuZCBmb2xsb3dzIHRoZSBwYXRoOgoKMS4gIFJ1biBhbGxfcGFpcndpc2UgdG8gcnVuIGRlc2VxIGFuZCBmcmllbmRzIHVzaW5nIHN1cm9nYXRlIGVzdGltYXRlcwogICAgcHJvdmlkZWQgYnkgc3ZhIHdoZW4gYXBwcm9wcmlhdGUvcG9zc2libGUuICBUaGlzIGNyZWF0ZXMgYW4gdW53aWVsZHkKICAgIGRhdGFzdHJ1Y3R1cmUgY29udGFpbmluZyB0aGUgcmVzdWx0cyBmcm9tIGFsbCBtZXRob2RzIGFuZCBhbGwKICAgIGNvbnRyYXN0cyBhcyBhIHNlcmllcyBvZiBuZXN0ZWQgbGlzdHMuCjIuICBNYXNoIHRoZW0gdG9nZXRoZXIgd2l0aCBjb21iaW5lX2RlX3RhYmxlcywgdXNlIHRoZSAna2VlcGVycycKICAgIGFyZ3VtZW50IHRvIGRlZmluZSB0aGUgZGVzaXJlZCBudW1lcmF0b3JzL2Rlbm9taW5hdG9ycywgYW5kIHdyaXRlCiAgICB0aGUgdGFibGVzIHRvIHRoZSBmaWxlIHByb3ZpZGVkIGluIHRoZSAnZXhjZWwnIGFyZ3VtZW50LgozLiAgWWFuayBvdXQgdGhlICdzaWduaWZpY2FudCcgZ2VuZXMgYW5kIHNlbmQgdGhlbSB0byBhIHNlcGFyYXRlIGV4Y2VsCiAgICBkb2N1bWVudC4gIEluIGFsbCBjYXNlcywgJ3NpZ25pZmljYW50JyBpcyB0aGUgc2V0IHdpdGggYSB8bG9nMkZDfAogICAgPj0gMS4wIGFuZCBhZGp1c3RlZCBwLXZhbHVlIDw9IDAuMDUuICBUaGlzIHJlbWluZHMgbWUsIG9uZSBvZiB0aGUKICAgIHJldmlld2VycyBtZW50aW9uZWQgYSBzZXQgb2YgaW50ZXJuYXRpb25hbCBndWlkZWxpbmVzIGZvcgogICAgc2lnbmlmaWNhbnQgZ2VuZXMsIEkgdGhvdWdodCBJIGJhc2ljYWxseSBrbm93IHdoYXQgSSBhbSBkb2luZywgYnV0CiAgICB0aGlzIGNhdWdodCBtZSBjb21wbGV0ZWx5IHVuYXdhcmUuICBJZiBhbnlvbmUgZXZlciByZWFkcyB0aGlzIChubwogICAgb25lIHdpbGwsIGxldCB1cyBiZSBob25lc3QpIEkgd291bGQgbG92ZSB0byBrbm93LiAgVGhlIGNsb3Nlc3QKICAgIHRoaW5nIEkgZm91bmQgaXM6IChAY2h1bmdCZXN0UHJhY3RpY2VzRGlmZmVyZW50aWFsMjAyMSksIGJ1dCBJIGRvCiAgICBub3QgdGhpbmsgaXQgcmVhbGx5IGFkZHJlc3NlcyB0aGlzIGlkZWEgKEkgaGF2ZSBub3QgeWV0IHJlYWQgaXQKICAgIGNhcmVmdWxseSkuCgpUaGVzZSBkYXRhc3RydWN0dXJlcyBhcmUgYWxsIGV4cG9zZWQgdG8gdmFyaW91cyBmdW5jdGlvbnMgaW4gaHBnbHRvb2xzCndoaWNoIGFsbG93IG9uZSB0byBwb2tlL2NvbXBhcmUgdGhlbTsgSSBhbSBub3QgYSBmYW4gb2YgRXhjZWwsIGJ1dCBJCnRoaW5rIHRoZSB4bHN4IGRvY3VtZW50cyBpdCBjcmVhdGVzIGFyZSBwcmV0dHkgZGVjZW50LCB0b28uCgojIFZpc2l0IGNvbXBhcmlzb25zCgpMYXRlciBpbiB0aGlzIGRvY3VtZW50IEkgZG8gYSBidW5jaCBvZiB2aXNpdC9jZiBjb21wYXJpc29ucy4gIEluIHRoaXMKYmxvY2sgSSB3YW50IHRvIGV4cGxpY2l0bHkgb25seSBjb21wYXJlIHYxIHRvIG90aGVyIHZpc2l0cy4gIFRoaXMgaXMKc29tZXRoaW5nIEkgZGlkIHF1aXRlIGEgbG90IGluIHRoZSAyMDE5IGRhdGFzZXRzLCBidXQgbmV2ZXIgYWN0dWFsbHkKbW92ZWQgdG8gdGhpcyBkb2N1bWVudC4KCmBgYHtyfQp0djFfdnNfbGF0ZXIgPC0gYWxsX3BhaXJ3aXNlKHRfdjF2cywgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBtZXRob2RzKQp0X3YxdnMgPC0gdHYxX3ZzX2xhdGVyW1siaW5wdXQiXV0KdHYxX3ZzX2xhdGVyCnR2MV92c19sYXRlcl90YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0djFfdnNfbGF0ZXIsIGtlZXBlcnMgPSB2aXNpdF92MWxhdGVyLCBzY2FsZV9wID0gVFJVRSwKICBleGNlbCA9IGdsdWUoInt4bHN4X3ByZWZpeH0vREVfVmlzaXRzL3R2MV92c19sYXRlcl90YWJsZXMtdnt2ZXJ9Lnhsc3giKSkKdHYxX3ZzX2xhdGVyX3RhYmxlCnR2MV92c19sYXRlcl9zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0djFfdnNfbGF0ZXJfdGFibGUsCiAgZXhjZWwgPSBnbHVlKCJ7eGxzeF9wcmVmaXh9L0RFX1Zpc2l0cy90djFfdnNfbGF0ZXJfc2lnLXZ7dmVyfS54bHN4IikpCnR2MV92c19sYXRlcl9zaWcKYGBgCgojIFNleCBjb21wYXJpc29uCgpUaGVyZSBpcyBhbiBpbXBvcnRhbnQgY2F2ZWF0IHdoZW4gY29uc2lkZXJpbmcgdGhlIHNleCBvZiBwZW9wbGUgaW4gdGhlCnN0dWR5OiB0aGVyZSBhcmUgdmVyeSBmZXcgZmVtYWxlcyB3aG8gZmFpbGVkLiAgQXMgYSByZXN1bHQgSSBwcmltYXJpbHkKY29uY2VybmVkIHdpdGggdGhlIGN1cmUgc2FtcGxlcyBtYWxlL2ZlbWFsZS4KCmBgYHtyfQp0X3NleCA8LSBzdWJzZXRfZXhwdCh0Y19zZXgsIHN1YnNldCA9ICJjbGluaWMgPT0gJ3R1bWFjbyciKQp0X3NleAp0X3NleF9kZSA8LSBhbGxfcGFpcndpc2UodF9zZXgsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsIG1ldGhvZHMgPSBtZXRob2RzLAogICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSkKdF9zZXggPC0gdF9zZXhfZGVbWyJpbnB1dCJdXQp0X3NleF9kZQp0X3NleF90YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X3NleF9kZSwgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSBnbHVlKCJ7eGxzeF9wcmVmaXh9L0dlbmVfU2V0X0VucmljaG1lbnQvdF9zZXhfdGFibGUtdnt2ZXJ9Lnhsc3giKSkKdF9zZXhfdGFibGUKdF9zZXhfc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgdF9zZXhfdGFibGUsIGV4Y2VsID0gZ2x1ZSgie3hsc3hfcHJlZml4fS9HZW5lX1NldF9FbnJpY2htZW50L3Rfc2V4X3NpZy12e3Zlcn0ueGxzeCIpKQp0X3NleF9zaWcKYGBgCgpJbiB0aGUgZm9sbG93aW5nIGJsb2NrIEkgcmVtb3ZlZCB0aGUgZmFpbGVkIHBlb3BsZSBzbyB0aGF0IHRoZQpjb21wYXJpc29uIG1ha2VzIGFjdHVhbCBzZW5zZS4KCmBgYHtyfQp0Y19zZXhfY3VyZSA8LSBzdWJzZXRfZXhwdCh0Y19zZXgsIHN1YnNldCA9ICJmaW5hbG91dGNvbWU9PSdjdXJlJyIpCnRfc2V4X2N1cmUgPC0gc3Vic2V0X2V4cHQodGNfc2V4X2N1cmUsIHN1YnNldCA9ICJjbGluaWMgPT0gJ3R1bWFjbyciKQoKdF9zZXhfY3VyZV9kZSA8LSBhbGxfcGFpcndpc2UodF9zZXhfY3VyZSwgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfc2V4X2N1cmUgPC0gdF9zZXhfY3VyZV9kZVtbImlucHV0Il1dCnRfc2V4X2N1cmVfZGUKdF9zZXhfY3VyZV90YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X3NleF9jdXJlX2RlLCBzY2FsZV9wID0gVFJVRSwKICBleGNlbCA9IGdsdWUoInt4bHN4X3ByZWZpeH0vREVfU2V4L3Rfc2V4X2N1cmVfdGFibGUtdnt2ZXJ9Lnhsc3giKSkKdF9zZXhfY3VyZV90YWJsZQp0X3NleF9jdXJlX3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHRfc2V4X2N1cmVfdGFibGUsIGV4Y2VsID0gZ2x1ZSgie3hsc3hfcHJlZml4fS9ERV9TZXgvdF9zZXhfY3VyZV9zaWctdnt2ZXJ9Lnhsc3giKSkKdF9zZXhfY3VyZV9zaWcKYGBgCgojIEV0aG5pY2l0eSBjb21wYXJpc29ucwoKSW4gYSBmYXNoaW9uIHNpbWlsYXIgdG8gdGhlIHB1dGF0aXZlIHNleCBjb21wYXJpc29uczsgdGhlcmUgYXJlIGZldy9ubwpmYWlscyBmb3Igb25lIGV0aG5pY2l0eS4gIEluIGFkZGl0aW9uLCB0aGUgb2JzZXJ2ZWQgZXRobmljaXRpZXMgYXJlCnZlcnkgZGlmZmVyZW50IGZvciB0aGUgdHdvIGNsaW5pY3MuICBUaGlzIG1ha2VzIGNvbXBhcmlzb25zIG9mIHRoZQpldGhuaWNpdGllcyB0cmlja3kuCgpgYGB7cn0KdF9ldGhuaWNpdHlfZGUgPC0gYWxsX3BhaXJ3aXNlKHRfZXRuaWFfZXhwdCwgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2RzID0gbWV0aG9kcykKdF9ldG5pYV9leHB0IDwtIHRfZXRobmljaXR5X2RlW1siaW5wdXQiXV0KdF9ldGhuaWNpdHlfZGUKdF9ldGhuaWNpdHlfdGFibGUgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgdF9ldGhuaWNpdHlfZGUsIGtlZXBlcnMgPSBldGhuaWNpdHlfY29udHJhc3RzLCBzY2FsZV9wID0gVFJVRSwKICBleGNlbCA9IGdsdWUoInt4bHN4X3ByZWZpeH0vREVfRXRobmljaXR5L3RfZXRobmljaXR5X3RhYmxlLXZ7dmVyfS54bHN4IikpCnRfZXRobmljaXR5X3RhYmxlCnRfZXRobmljaXR5X3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHRfZXRobmljaXR5X3RhYmxlLCBhY2NvcmRpbmdfdG8gPSAiZGVzZXEiLAogIGV4Y2VsID0gZ2x1ZSgie3hsc3hfcHJlZml4fS9ERV9FdGhuaWNpdHkvdF9ldGhuaWNpdHlfc2lnLXZ7dmVyfS54bHN4IikpCnRfZXRobmljaXR5X3NpZwpgYGAKCiMgU2VwYXJhdGUgdGhlIFR1bWFjbyBkYXRhIGJ5IHZpc2l0CgpPbmUgb2YgdGhlIG1vc3QgY29tcGVsbGluZyBpZGVhcyBpbiB0aGUgZGF0YSBpcyB0aGUgb3Bwb3J0dW5pdHkgdG8KZmluZCBnZW5lcyBpbiB0aGUgZmlyc3QgdmlzaXQgd2hpY2ggbWF5IGhlbHAgcHJlZGljdCB0aGUgbGlrZWxpaG9vZAp0aGF0IGEgcGVyc29uIHdpbGwgcmVzcG9uZCB3ZWxsIHRvIHRyZWF0bWVudC4gIFRoZSBmb2xsb3dpbmcgYmxvY2sKd2lsbCB0aGVyZWZvcmUgbG9vayBhdCBjdXJlL2ZhaWwgZnJvbSBUdW1hY28gYXQgdmlzaXQgMS4KCiMjIEN1cmUvRmFpbCwgVHVtYWNvIFZpc2l0IDEKCmBgYHtyfQp0X2NmX2NsaW5pY2FsX3YxX2RlX3N2YSA8LSBhbGxfcGFpcndpc2UodHYxX3NhbXBsZXMsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnR2MV9zYW1wbGVzIDwtIHRfY2ZfY2xpbmljYWxfdjFfZGVfc3ZhW1siaW5wdXQiXV0KdF9jZl9jbGluaWNhbF92MV9kZV9zdmEKdF9jZl9jbGluaWNhbF92MV90YWJsZV9zdmEgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgdF9jZl9jbGluaWNhbF92MV9kZV9zdmEsIGtlZXBlcnMgPSBjZl9jb250cmFzdCwgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9WaXNpdHMvdF9jbGluaWNhbF92MV9jZl90YWJsZV9zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZl9jbGluaWNhbF92MV90YWJsZV9zdmEKdF9jZl9jbGluaWNhbF92MV9zaWdfc3ZhIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgdF9jZl9jbGluaWNhbF92MV90YWJsZV9zdmEsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9WaXNpdHMvdF9jbGluaWNhbF92MV9jZl9zaWdfc3ZhLXZ7dmVyfS54bHN4IikpCnRfY2ZfY2xpbmljYWxfdjFfc2lnX3N2YQoKZGltKHRfY2ZfY2xpbmljYWxfdjFfc2lnX3N2YVtbImRlc2VxIl1dW1sidXBzIl1dW1sxXV0pCmRpbSh0X2NmX2NsaW5pY2FsX3YxX3NpZ19zdmFbWyJkZXNlcSJdXVtbImRvd25zIl1dW1sxXV0pCmBgYAoKIyMgQ3VyZS9GYWlsLCBUdW1hY28gVmlzaXQgMgoKVGhlIHZpc2l0IDIgYW5kIHZpc2l0IDMgc2FtcGxlcyBhcmUgaW50ZXJlc3RpbmcgYmVjYXVzZSB0aGV5IHByb3ZpZGUKYW4gb3Bwb3J0dW5pdHkgdG8gc2VlIGlmIHdlIGNhbiBvYnNlcnZlIGNoYW5nZXMgaW4gcmVzcG9uc2UgaW4gdGhlCm1pZGRsZSBhbmQgZW5kIG9mIHRyZWF0bWVudC4uLgoKYGBge3J9CnRfY2ZfY2xpbmljYWxfdjJfZGVfc3ZhIDwtIGFsbF9wYWlyd2lzZSh0djJfc2FtcGxlcywgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2RzID0gbWV0aG9kcykKdHYyX3NhbXBsZXMgPC0gdF9jZl9jbGluaWNhbF92Ml9kZV9zdmFbWyJpbnB1dCJdXQp0X2NmX2NsaW5pY2FsX3YyX2RlX3N2YQp0X2NmX2NsaW5pY2FsX3YyX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X2NmX2NsaW5pY2FsX3YyX2RlX3N2YSwga2VlcGVycyA9IGNmX2NvbnRyYXN0LCBzY2FsZV9wID0gVFJVRSwKICBleGNlbCA9IGdsdWUoIntjZl9wcmVmaXh9L1Zpc2l0cy90X2NsaW5pY2FsX3YyX2NmX3RhYmxlX3N2YS12e3Zlcn0ueGxzeCIpKQp0X2NmX2NsaW5pY2FsX3YyX3RhYmxlX3N2YQp0X2NmX2NsaW5pY2FsX3YyX3NpZ19zdmEgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X2NmX2NsaW5pY2FsX3YyX3RhYmxlX3N2YSwKICBleGNlbCA9IGdsdWUoIntjZl9wcmVmaXh9L1Zpc2l0cy90X2NsaW5pY2FsX3YyX2NmX3NpZ19zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZl9jbGluaWNhbF92Ml9zaWdfc3ZhCgpkaW0odF9jZl9jbGluaWNhbF92Ml9zaWdfc3ZhW1siZGVzZXEiXV1bWyJ1cHMiXV1bWzFdXSkKZGltKHRfY2ZfY2xpbmljYWxfdjJfc2lnX3N2YVtbImRlc2VxIl1dW1siZG93bnMiXV1bWzFdXSkKYGBgCgojIyBDdXJlL0ZhaWwsIFR1bWFjbyBWaXNpdCAzCgpSZXBlYXQgZm9yIHZpc2l0IDMKCmBgYHtyfQp0X2NmX2NsaW5pY2FsX3YzX2RlX3N2YSA8LSBhbGxfcGFpcndpc2UodHYzX3NhbXBsZXMsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnR2M19zYW1wbGVzIDwtIHRfY2ZfY2xpbmljYWxfdjNfZGVfc3ZhW1siaW5wdXQiXV0KdF9jZl9jbGluaWNhbF92M19kZV9zdmEKdF9jZl9jbGluaWNhbF92M190YWJsZV9zdmEgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgdF9jZl9jbGluaWNhbF92M19kZV9zdmEsIGtlZXBlcnMgPSBjZl9jb250cmFzdCwgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9WaXNpdHMvdF9jbGluaWNhbF92M19jZl90YWJsZV9zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZl9jbGluaWNhbF92M190YWJsZV9zdmEKdF9jZl9jbGluaWNhbF92M19zaWdfc3ZhIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgdF9jZl9jbGluaWNhbF92M190YWJsZV9zdmEsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9WaXNpdHMvdF9jbGluaWNhbF92M19jZl9zaWdfc3ZhLXZ7dmVyfS54bHN4IikpCnRfY2ZfY2xpbmljYWxfdjNfc2lnX3N2YQoKZGltKHRfY2ZfY2xpbmljYWxfdjNfc2lnX3N2YVtbImRlc2VxIl1dW1sidXBzIl1dW1sxXV0pCmRpbSh0X2NmX2NsaW5pY2FsX3YzX3NpZ19zdmFbWyJkZXNlcSJdXVtbImRvd25zIl1dW1sxXV0pCmBgYAoKIyBCeSBjZWxsIHR5cGUKCk5vdyBsZXQgdXMgc3dpdGNoIG91ciB2aWV3IHRvIGVhY2ggaW5kaXZpZHVhbCBjZWxsIHR5cGUgY29sbGVjdGVkLgpUaGUgaG9wZSBoZXJlIGlzIHRoYXQgd2Ugd2lsbCBiZSBhYmxlIHRvIGxlYXJuIHNvbWUgY2VsbC1zcGVjaWZpYwpkaWZmZXJlbmNlcyBpbiB0aGUgcmVzcG9uc2UgZm9yIHBlb3BsZSB3aG8gZGlkKG5vdCkgcmVzcG9uZCB3ZWxsLgoKIyMgQ3VyZS9GYWlsLCBCaW9wc2llcwoKQSBwcmltYXJ5IGh5cG90aGVzaXMvYXNzdW1wdGlvbiB0aGF0IHdlIGhhdmUgaGVsZCBmb3IgcXVpdGUgYSB3aGlsZQp3aXRoIHRoaXMgZGF0YTogdGhlIGJpb3BzeSBzYW1wbGVzLCBnaXZlbiB0aGF0IHRoZXkgYXJlIGNvbXByaXNlZCBvZgpoZXRlcmdlbmVvdXMgdGlzc3VlIHR5cGVzIGFzIHdlbGwgYXMgYSBtaXggb2YgaGVhbHRoeSBhbmQgaW5mZWN0ZWQKdGlzc3VlOyBhcmUgdW5saWtlbHkgdG8gYmUgdmVyeSBpbmZvcm1hdGlvbiByaWNoIHZpcyBhIHZpcyBjdXJlL2ZhaWwuClRoZSBmb2xsb3dpbmcgYmxvY2sgc2VlbXMgdG8gc3VwcG9ydCB0aGF0OyB3ZSBvYnNlcnZlIHZlcnkgZmV3IGdlbmVzCmluIHRoZSBiaW9wc2llcy4KCkkgdGhlcmVmb3JlIGRpZCBub3Qgc3BlbmQgdGhlIHRpbWUgaW52b2tpbmcgb3RoZXIgbW9kZWxzLgoKYGBge3J9CnRfY2ZfYmlvcHN5X2RlX3N2YSA8LSBhbGxfcGFpcndpc2UodF9iaW9wc2llcywgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBtZXRob2RzKQp0X2Jpb3BzaWVzIDwtIHRfY2ZfYmlvcHN5X2RlX3N2YVtbImlucHV0Il1dCnRfY2ZfYmlvcHN5X2RlX3N2YQp0X2NmX2Jpb3BzeV90YWJsZV9zdmEgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgdF9jZl9iaW9wc3lfZGVfc3ZhLCBrZWVwZXJzID0gdF9jZl9jb250cmFzdCwgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9CaW9wc2llcy90X2Jpb3BzeV9jZl90YWJsZV9zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZl9iaW9wc3lfdGFibGVfc3ZhCnRfY2ZfYmlvcHN5X3NpZ19zdmEgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X2NmX2Jpb3BzeV90YWJsZV9zdmEsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9CaW9wc2llcy90X2NmX2Jpb3BzeV9zaWdfc3ZhLXZ7dmVyfS54bHN4IikpCnRfY2ZfYmlvcHN5X3NpZ19zdmEKCmRpbSh0X2NmX2Jpb3BzeV9zaWdfc3ZhW1siZGVzZXEiXV1bWyJ1cHMiXV1bWzFdXSkKZGltKHRfY2ZfYmlvcHN5X3NpZ19zdmFbWyJkZXNlcSJdXVtbImRvd25zIl1dW1sxXV0pCmBgYAoKIyMgQ3VyZS9GYWlsLCBNb25vY3l0ZXMKClNhbWUgcXVlc3Rpb24sIGJ1dCB0aGlzIHRpbWUgbG9va2luZyBhdCBtb25vY3l0ZXMuICBJbiBhZGRpdGlvbiwgdGhpcwpjb21wYXJpc29uIHdhcyBkb25lIHR3aWNlLCBvbmNlIHVzaW5nIFNWQSBhbmQgb25jZSB1c2luZyB2aXNpdCBhcyBhCmJhdGNoIGZhY3Rvci4KCkkgaGF2ZSBiZWVuIHVzaW5nIHRoaXMgYmxvY2sgdG8gZW5zdXJlIHRoYXQgY2hhbmdlZCBJIGhhdmUgYmVlbiBtYWtpbmcKdG8gdGhlIGhwZ2x0b29scyBkbyBub3QgY2hhbmdlIHRoZSBhbmFseXNpcyByZXN1bHRzLiAgVGh1cyB0aGUgY29tbWVudAp3aXRoIGEgZmV3IGxvZ0ZDIHZhbHVlczsgdGhvc2UgYXJlIHRoZSBmaXJzdCA2IG9ic2VydmVkIERFU2VxMiBsb2dGQwp2YWx1ZXMgaW4gbXkgbGFzdCByZXN1bHQgYmVmb3JlIEkgbWFkZSBzb21lIGNoYW5nZXMgdG8gaHBnbHRvb2xzIGluCm9yZGVyIHRvIGJlIGFibGUgdG8gd29yayB3aXRoIHJhbmRvbSBlZmZlY3QgbW9kZWxzLgoKYGBge3J9CnRfY2ZfbW9ub2N5dGVfZGVfc3ZhIDwtIGFsbF9wYWlyd2lzZSh0X21vbm9jeXRlcywgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2RzID0gbWV0aG9kcykKIyMgVGhlIHN2cyBhcmUgYWRkZWQgdG8gdGhlIGV4cHJlc3Npb25zZXQgZHVyaW5nIGFsbF9wYWlyd2lzZS4KdF9tb25vY3l0ZXMgPC0gdF9jZl9tb25vY3l0ZV9kZV9zdmFbWyJpbnB1dCJdXQp0X2NmX21vbm9jeXRlX2RlX3N2YQp0X2NmX21vbm9jeXRlX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X2NmX21vbm9jeXRlX2RlX3N2YSwga2VlcGVycyA9IHRfY2ZfY29udHJhc3QsIHNjYWxlX3AgPSBUUlVFLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vTW9ub2N5dGVzL3RfbW9ub2N5dGVfY2ZfdGFibGVfc3ZhLXZ7dmVyfS54bHN4IikpCnRfY2ZfbW9ub2N5dGVfdGFibGVfc3ZhCmhlYWQodF9jZl9tb25vY3l0ZV90YWJsZV9zdmFbWyJkYXRhIl1dW1sib3V0Y29tZSJdXVtbImRlc2VxX2xvZ2ZjIl1dKQojIyBUaGUgZmlyc3QgZmV3IHZhbHVlcyBpbiBteSBwcmUtY2hhbmdlIHJlc3VsdCBzZXQgYXJlOgojIyAwLjMzOCwgLTAuMDcyLCAwLjA5NywgLTAuMDkxLCAtMC4xMzUsIDAuMjMzCnRfY2ZfbW9ub2N5dGVfc2lnX3N2YSA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHRfY2ZfbW9ub2N5dGVfdGFibGVfc3ZhLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vTW9ub2N5dGVzL3RfbW9ub2N5dGVfY2Zfc2lnX3N2YS12e3Zlcn0ueGxzeCIpKQp0X2NmX21vbm9jeXRlX3NpZ19zdmEKCmRpbSh0X2NmX21vbm9jeXRlX3NpZ19zdmFbWyJkZXNlcSJdXVtbInVwcyJdXVtbMV1dKQpkaW0odF9jZl9tb25vY3l0ZV9zaWdfc3ZhW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbMV1dKQoKdF9jZl9tb25vY3l0ZV9kZV9iYXRjaHZpc2l0IDwtIGFsbF9wYWlyd2lzZSh0X21vbm9jeXRlcywgbW9kZWxfYmF0Y2ggPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfY2ZfbW9ub2N5dGVfZGVfYmF0Y2h2aXNpdAp0X2NmX21vbm9jeXRlX3RhYmxlX2JhdGNodmlzaXQgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgdF9jZl9tb25vY3l0ZV9kZV9iYXRjaHZpc2l0LCBrZWVwZXJzID0gdF9jZl9jb250cmFzdCwgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9Nb25vY3l0ZXMvdF9tb25vY3l0ZV9jZl90YWJsZV9iYXRjaHZpc2l0LXZ7dmVyfS54bHN4IikpCnRfY2ZfbW9ub2N5dGVfdGFibGVfYmF0Y2h2aXNpdAp0X2NmX21vbm9jeXRlX3NpZ19iYXRjaHZpc2l0IDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgdF9jZl9tb25vY3l0ZV90YWJsZV9iYXRjaHZpc2l0LAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vTW9ub2N5dGVzL3RfbW9ub2N5dGVfY2Zfc2lnX2JhdGNodmlzaXQtdnt2ZXJ9Lnhsc3giKSkKdF9jZl9tb25vY3l0ZV9zaWdfYmF0Y2h2aXNpdAoKZGltKHRfY2ZfbW9ub2N5dGVfc2lnX2JhdGNodmlzaXRbWyJkZXNlcSJdXVtbInVwcyJdXVtbMV1dKQpkaW0odF9jZl9tb25vY3l0ZV9zaWdfYmF0Y2h2aXNpdFtbImRlc2VxIl1dW1siZG93bnMiXV1bWzFdXSkKYGBgCgojIyBJbmRpdmlkdWFsIHZpc2l0cywgTW9ub2N5dGVzCgpOb3cgZm9jdXMgaW4gb24gdGhlIG1vbm9jeXRlIHNhbXBsZXMgb24gYSBwZXItdmlzaXQgYmFzaXMuCgojIyMgVmlzaXQgMQoKYGBge3J9CnRfY2ZfbW9ub2N5dGVfdjFfZGVfc3ZhIDwtIGFsbF9wYWlyd2lzZSh0djFfbW9ub2N5dGVzLCBtb2RlbF9iYXRjaCA9ICJzdmFzZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBtZXRob2RzKQp0djFfbW9ub2N5dGVzIDwtIHRfY2ZfbW9ub2N5dGVfdjFfZGVfc3ZhW1siaW5wdXQiXV0KdF9jZl9tb25vY3l0ZV92MV9kZV9zdmEKdF9jZl9tb25vY3l0ZV92MV90YWJsZV9zdmEgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgdF9jZl9tb25vY3l0ZV92MV9kZV9zdmEsIGtlZXBlcnMgPSB0X2NmX2NvbnRyYXN0LCBzY2FsZV9wID0gVFJVRSwKICBleGNlbCA9IGdsdWUoIntjZl9wcmVmaXh9L01vbm9jeXRlcy90X21vbm9jeXRlX3YxX2NmX3RhYmxlX3N2YS12e3Zlcn0ueGxzeCIpKQp0X2NmX21vbm9jeXRlX3YxX3RhYmxlX3N2YQp0X2NmX21vbm9jeXRlX3YxX3NpZ19zdmEgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X2NmX21vbm9jeXRlX3YxX3RhYmxlX3N2YSwKICBleGNlbCA9IGdsdWUoIntjZl9wcmVmaXh9L01vbm9jeXRlcy90X21vbm9jeXRlX3YxX2NmX3NpZ19zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZl9tb25vY3l0ZV92MV9zaWdfc3ZhCgpkaW0odF9jZl9tb25vY3l0ZV92MV9zaWdfc3ZhW1siZGVzZXEiXV1bWyJ1cHMiXV1bWzFdXSkKZGltKHRfY2ZfbW9ub2N5dGVfdjFfc2lnX3N2YVtbImRlc2VxIl1dW1siZG93bnMiXV1bWzFdXSkKYGBgCgojIyMgTW9ub2N5dGVzOiBDb21wYXJlIHN2YSB0byBiYXRjaC1pbi1tb2RlbAoKYGBge3J9CnN2YV9hdWNjIDwtIGNhbGN1bGF0ZV9hdWNjKHRfY2ZfbW9ub2N5dGVfdGFibGVfc3ZhW1siZGF0YSJdXVtbMV1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICB0YmwyID0gdF9jZl9tb25vY3l0ZV90YWJsZV9iYXRjaHZpc2l0W1siZGF0YSJdXVtbMV1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICBweSA9ICJkZXNlcV9hZGpwIiwgbHkgPSAiZGVzZXFfbG9nZmMiKQpzdmFfYXVjYwoKc2hhcmVkX2lkcyA8LSByb3duYW1lcyh0X2NmX21vbm9jeXRlX3RhYmxlX3N2YVtbImRhdGEiXV1bWzFdXSkgJWluJQogIHJvd25hbWVzKHRfY2ZfbW9ub2N5dGVfdGFibGVfYmF0Y2h2aXNpdFtbImRhdGEiXV1bWzFdXSkKZmlyc3QgPC0gdF9jZl9tb25vY3l0ZV90YWJsZV9zdmFbWyJkYXRhIl1dW1sxXV1bc2hhcmVkX2lkcywgXQpzZWNvbmQgPC0gdF9jZl9tb25vY3l0ZV90YWJsZV9iYXRjaHZpc2l0W1siZGF0YSJdXVtbMV1dW3Jvd25hbWVzKGZpcnN0KSwgXQpjb3IudGVzdChmaXJzdFtbImRlc2VxX2xvZ2ZjIl1dLCBzZWNvbmRbWyJkZXNlcV9sb2dmYyJdXSkKYGBgCgojIyBOZXV0cm9waGlsIHNhbXBsZXMKClN3aXRjaCBjb250ZXh0IHRvIHRoZSBOZXV0cm9waGlscywgb25jZSBhZ2FpbiByZXBlYXQgdGhlIGFuYWx5c2lzCnVzaW5nIFNWQSBhbmQgdmlzaXQgYXMgYSBiYXRjaCBmYWN0b3IuCgpgYGB7cn0KdF9jZl9uZXV0cm9waGlsX2RlX3N2YSA8LSBhbGxfcGFpcndpc2UodF9uZXV0cm9waGlscywgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfY2ZfbmV1dHJvcGhpbF9kZV9zdmEKdF9jZl9uZXV0cm9waGlsX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X2NmX25ldXRyb3BoaWxfZGVfc3ZhLCBrZWVwZXJzID0gdF9jZl9jb250cmFzdCwgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9OZXV0cm9waGlscy90X25ldXRyb3BoaWxfY2ZfdGFibGVfc3ZhLXZ7dmVyfS54bHN4IikpCnRfY2ZfbmV1dHJvcGhpbF90YWJsZV9zdmEKdF9jZl9uZXV0cm9waGlsX3NpZ19zdmEgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X2NmX25ldXRyb3BoaWxfdGFibGVfc3ZhLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vTmV1dHJvcGhpbHMvdF9uZXV0cm9waGlsX2NmX3NpZ19zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZl9uZXV0cm9waGlsX3NpZ19zdmEKCmRpbSh0X2NmX25ldXRyb3BoaWxfc2lnX3N2YVtbImRlc2VxIl1dW1sidXBzIl1dW1sxXV0pCmRpbSh0X2NmX25ldXRyb3BoaWxfc2lnX3N2YVtbImRlc2VxIl1dW1siZG93bnMiXV1bWzFdXSkKCnRfY2ZfbmV1dHJvcGhpbF9kZV9iYXRjaHZpc2l0IDwtIGFsbF9wYWlyd2lzZSh0X25ldXRyb3BoaWxzLCBtb2RlbF9iYXRjaCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfY2ZfbmV1dHJvcGhpbF9kZV9iYXRjaHZpc2l0CnRfY2ZfbmV1dHJvcGhpbF90YWJsZV9iYXRjaHZpc2l0IDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHRfY2ZfbmV1dHJvcGhpbF9kZV9iYXRjaHZpc2l0LCBrZWVwZXJzID0gdF9jZl9jb250cmFzdCwgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9OZXV0cm9waGlscy90X25ldXRyb3BoaWxfY2ZfdGFibGVfYmF0Y2h2aXNpdC12e3Zlcn0ueGxzeCIpKQp0X2NmX25ldXRyb3BoaWxfdGFibGVfYmF0Y2h2aXNpdAp0X2NmX25ldXRyb3BoaWxfc2lnX2JhdGNodmlzaXQgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X2NmX25ldXRyb3BoaWxfdGFibGVfYmF0Y2h2aXNpdCwKICBleGNlbCA9IGdsdWUoIntjZl9wcmVmaXh9L05ldXRyb3BoaWxzL3RfbmV1dHJvcGhpbF9jZl9zaWdfYmF0Y2h2aXNpdC12e3Zlcn0ueGxzeCIpKQp0X2NmX25ldXRyb3BoaWxfc2lnX2JhdGNodmlzaXQKCmRpbSh0X2NmX25ldXRyb3BoaWxfc2lnX2JhdGNodmlzaXRbWyJkZXNlcSJdXVtbInVwcyJdXVtbMV1dKQpkaW0odF9jZl9uZXV0cm9waGlsX3NpZ19iYXRjaHZpc2l0W1siZGVzZXEiXV1bWyJkb3ducyJdXVtbMV1dKQpgYGAKCiMjIyBOZXV0cm9waGlscyBieSB2aXNpdAoKV2hlbiBJIGRpZCB0aGlzIHdpdGggdGhlIG1vbm9jeXRlcywgSSBzcGxpdCBpdCB1cCBpbnRvIG11bHRpcGxlIGJsb2Nrcwpmb3IgZWFjaCB2aXNpdC4gIFRoaXMgdGltZSBJIGFtIGp1c3QgZ29pbmcgdG8gcnVuIHRoZW0gYWxsIHRvZ2V0aGVyLgoKYGBge3J9CnZpc2l0Y2ZfZmFjdG9yIDwtIHBhc3RlMChwRGF0YSh0X25ldXRyb3BoaWxzKVtbInZpc2l0bnVtYmVyIl1dLCAiXyIsCiAgICAgICAgICAgICAgICAgICAgICAgICBwRGF0YSh0X25ldXRyb3BoaWxzKVtbImZpbmFsb3V0Y29tZSJdXSkKdF9uZXV0cm9waGlsX3Zpc2l0Y2YgPC0gc2V0X2V4cHRfY29uZGl0aW9ucyh0X25ldXRyb3BoaWxzLCBmYWN0ID0gdmlzaXRjZl9mYWN0b3IpCgp0X2NmX25ldXRyb3BoaWxfdmlzaXRzX2RlX3N2YSA8LSBhbGxfcGFpcndpc2UodF9uZXV0cm9waGlsX3Zpc2l0Y2YsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfY2ZfbmV1dHJvcGhpbF92aXNpdHNfZGVfc3ZhCnRfY2ZfbmV1dHJvcGhpbF92aXNpdHNfdGFibGVfc3ZhIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHRfY2ZfbmV1dHJvcGhpbF92aXNpdHNfZGVfc3ZhLCBrZWVwZXJzID0gdmlzaXRjZl9jb250cmFzdHMsIHNjYWxlX3AgPSBUUlVFLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vTmV1dHJvcGhpbHMvdF9uZXV0cm9waGlsX3Zpc2l0Y2ZfdGFibGVfc3ZhLXZ7dmVyfS54bHN4IikpCnRfY2ZfbmV1dHJvcGhpbF92aXNpdHNfdGFibGVfc3ZhCnRfY2ZfbmV1dHJvcGhpbF92aXNpdHNfc2lnX3N2YSA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHRfY2ZfbmV1dHJvcGhpbF92aXNpdHNfdGFibGVfc3ZhLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vTmV1dHJvcGhpbHMvdF9uZXV0cm9waGlsX3Zpc2l0Y2Zfc2lnX3N2YS12e3Zlcn0ueGxzeCIpKQp0X2NmX25ldXRyb3BoaWxfdmlzaXRzX3NpZ19zdmEKCmRpbSh0X2NmX25ldXRyb3BoaWxfdmlzaXRzX3NpZ19zdmFbWyJkZXNlcSJdXVtbInVwcyJdXVtbMV1dKQpkaW0odF9jZl9uZXV0cm9waGlsX3Zpc2l0c19zaWdfc3ZhW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbMV1dKQpgYGAKCk5vdyBWMQoKYGBge3J9CnRfY2ZfbmV1dHJvcGhpbF92MV9kZV9zdmEgPC0gYWxsX3BhaXJ3aXNlKHR2MV9uZXV0cm9waGlscywgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfY2ZfbmV1dHJvcGhpbF92MV9kZV9zdmEKdF9jZl9uZXV0cm9waGlsX3YxX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X2NmX25ldXRyb3BoaWxfdjFfZGVfc3ZhLCBrZWVwZXJzID0gdF9jZl9jb250cmFzdCwgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9OZXV0cm9waGlscy90X25ldXRyb3BoaWxfdjFfY2ZfdGFibGVfc3ZhLXZ7dmVyfS54bHN4IikpCnRfY2ZfbmV1dHJvcGhpbF92MV90YWJsZV9zdmEKdF9jZl9uZXV0cm9waGlsX3YxX3NpZ19zdmEgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X2NmX25ldXRyb3BoaWxfdjFfdGFibGVfc3ZhLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vTmV1dHJvcGhpbHMvdF9uZXV0cm9waGlsX3YxX2NmX3NpZ19zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZl9uZXV0cm9waGlsX3YxX3NpZ19zdmEKCmRpbSh0X2NmX25ldXRyb3BoaWxfdjFfc2lnX3N2YVtbImRlc2VxIl1dW1sidXBzIl1dW1sxXV0pCmRpbSh0X2NmX25ldXRyb3BoaWxfdjFfc2lnX3N2YVtbImRlc2VxIl1dW1siZG93bnMiXV1bWzFdXSkKYGBgCgpGb2xsb3dlZCBieSB2aXNpdCAyLgoKYGBge3J9CnRfY2ZfbmV1dHJvcGhpbF92Ml9kZV9zdmEgPC0gYWxsX3BhaXJ3aXNlKHR2Ml9uZXV0cm9waGlscywgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfY2ZfbmV1dHJvcGhpbF92Ml9kZV9zdmEKdF9jZl9uZXV0cm9waGlsX3YyX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X2NmX25ldXRyb3BoaWxfdjJfZGVfc3ZhLCBzY2FsZV9wID0gVFJVRSwga2VlcGVycyA9IHRfY2ZfY29udHJhc3QsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9OZXV0cm9waGlscy90X25ldXRyb3BoaWxfdjJfY2ZfdGFibGVfc3ZhLXZ7dmVyfS54bHN4IikpCnRfY2ZfbmV1dHJvcGhpbF92Ml90YWJsZV9zdmEKdF9jZl9uZXV0cm9waGlsX3YyX3NpZ19zdmEgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X2NmX25ldXRyb3BoaWxfdjJfdGFibGVfc3ZhLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vTmV1dHJvcGhpbHMvdF9uZXV0cm9waGlsX3YyX2NmX3NpZ19zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZl9uZXV0cm9waGlsX3YyX3NpZ19zdmEKCmRpbSh0X2NmX25ldXRyb3BoaWxfdjJfc2lnX3N2YVtbImRlc2VxIl1dW1sidXBzIl1dW1sxXV0pCmRpbSh0X2NmX25ldXRyb3BoaWxfdjJfc2lnX3N2YVtbImRlc2VxIl1dW1siZG93bnMiXV1bWzFdXSkKYGBgCgphbmQgdmlzaXQgMy4KCmBgYHtyfQp0X2NmX25ldXRyb3BoaWxfdjNfZGVfc3ZhIDwtIGFsbF9wYWlyd2lzZSh0djNfbmV1dHJvcGhpbHMsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBtZXRob2RzKQp0X2NmX25ldXRyb3BoaWxfdjNfZGVfc3ZhCnRfY2ZfbmV1dHJvcGhpbF92M190YWJsZV9zdmEgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgdF9jZl9uZXV0cm9waGlsX3YzX2RlX3N2YSwga2VlcGVycyA9IHRfY2ZfY29udHJhc3QsIHNjYWxlX3AgPSBUUlVFLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vTmV1dHJvcGhpbHMvdF9uZXV0cm9waGlsX3YzX2NmX3RhYmxlX3N2YS12e3Zlcn0ueGxzeCIpKQp0X2NmX25ldXRyb3BoaWxfdjNfdGFibGVfc3ZhCnRfY2ZfbmV1dHJvcGhpbF92M19zaWdfc3ZhIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgdF9jZl9uZXV0cm9waGlsX3YzX3RhYmxlX3N2YSwKICBleGNlbCA9IGdsdWUoIntjZl9wcmVmaXh9L05ldXRyb3BoaWxzL3RfbmV1dHJvcGhpbF92M19jZl9zaWdfc3ZhLXZ7dmVyfS54bHN4IikpCnRfY2ZfbmV1dHJvcGhpbF92M19zaWdfc3ZhCgpkaW0odF9jZl9uZXV0cm9waGlsX3YzX3NpZ19zdmFbWyJkZXNlcSJdXVtbInVwcyJdXVtbMV1dKQpkaW0odF9jZl9uZXV0cm9waGlsX3YzX3NpZ19zdmFbWyJkZXNlcSJdXVtbImRvd25zIl1dW1sxXV0pCmBgYAoKIyMjIE5ldXRyb3BoaWxzOiBDb21wYXJlIHN2YSB0byBiYXRjaC1pbi1tb2RlbAoKYGBge3J9CnN2YV9hdWNjIDwtIGNhbGN1bGF0ZV9hdWNjKHRfY2ZfbmV1dHJvcGhpbF90YWJsZV9zdmFbWyJkYXRhIl1dW1sxXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRibDIgPSB0X2NmX25ldXRyb3BoaWxfdGFibGVfYmF0Y2h2aXNpdFtbImRhdGEiXV1bWzFdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcHkgPSAiZGVzZXFfYWRqcCIsIGx5ID0gImRlc2VxX2xvZ2ZjIikKc3ZhX2F1Y2MKCnNoYXJlZF9pZHMgPC0gcm93bmFtZXModF9jZl9uZXV0cm9waGlsX3RhYmxlX3N2YVtbImRhdGEiXV1bWzFdXSkgJWluJQogIHJvd25hbWVzKHRfY2ZfbmV1dHJvcGhpbF90YWJsZV9iYXRjaHZpc2l0W1siZGF0YSJdXVtbMV1dKQpmaXJzdCA8LSB0X2NmX25ldXRyb3BoaWxfdGFibGVfc3ZhW1siZGF0YSJdXVtbMV1dW3NoYXJlZF9pZHMsIF0Kc2Vjb25kIDwtIHRfY2ZfbmV1dHJvcGhpbF90YWJsZV9iYXRjaHZpc2l0W1siZGF0YSJdXVtbMV1dW3Jvd25hbWVzKGZpcnN0KSwgXQpjb3IudGVzdChmaXJzdFtbImRlc2VxX2xvZ2ZjIl1dLCBzZWNvbmRbWyJkZXNlcV9sb2dmYyJdXSkKYGBgCgojIyBFb3Npbm9waGlscwoKVGhpcyB0aW1lLCB3aXRoIGZlZWxpbmchICBSZXBlYXRpbmcgdGhlIHNhbWUgc2V0IG9mIHRhc2tzIHdpdGggdGhlCmVvc2lub3BoaWwgc2FtcGxlcy4KCmBgYHtyfQp0X2NmX2Vvc2lub3BoaWxfZGVfc3ZhIDwtIGFsbF9wYWlyd2lzZSh0X2Vvc2lub3BoaWxzLCBtb2RlbF9iYXRjaCA9ICJzdmFzZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2RzID0gbWV0aG9kcykKdF9jZl9lb3Npbm9waGlsX2RlX3N2YQp0X2NmX2Vvc2lub3BoaWxfdGFibGVfc3ZhIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHRfY2ZfZW9zaW5vcGhpbF9kZV9zdmEsIGtlZXBlcnMgPSB0X2NmX2NvbnRyYXN0LCBzY2FsZV9wID0gVFJVRSwKICBleGNlbCA9IGdsdWUoIntjZl9wcmVmaXh9L0Vvc2lub3BoaWxzL3RfZW9zaW5vcGhpbF9jZl90YWJsZV9zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZl9lb3Npbm9waGlsX3RhYmxlX3N2YQp0X2NmX2Vvc2lub3BoaWxfc2lnX3N2YSA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHRfY2ZfZW9zaW5vcGhpbF90YWJsZV9zdmEsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9Fb3Npbm9waGlscy90X2Vvc2lub3BoaWxfY2Zfc2lnX3N2YS12e3Zlcn0ueGxzeCIpKQp0X2NmX2Vvc2lub3BoaWxfc2lnX3N2YQoKZGltKHRfY2ZfZW9zaW5vcGhpbF9zaWdfc3ZhW1siZGVzZXEiXV1bWyJ1cHMiXV1bWzFdXSkKZGltKHRfY2ZfZW9zaW5vcGhpbF9zaWdfc3ZhW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbMV1dKQoKaGVhZCh0X2NmX2Vvc2lub3BoaWxfc2lnX3N2YVtbImRlc2VxIl1dW1sidXBzIl1dW1sxXV0pCmhlYWQodF9jZl9lb3Npbm9waGlsX3NpZ19zdmFbWyJkZXNlcSJdXVtbImRvd25zIl1dW1sxXV0pCmBgYAoKUmVwZWF0IHdpdGggYmF0Y2ggaW4gdGhlIG1vZGVsLgoKYGBge3J9CnRfY2ZfZW9zaW5vcGhpbF9kZV9iYXRjaHZpc2l0IDwtIGFsbF9wYWlyd2lzZSh0X2Vvc2lub3BoaWxzLCBtb2RlbF9iYXRjaCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfY2ZfZW9zaW5vcGhpbF9kZV9iYXRjaHZpc2l0CnRfY2ZfZW9zaW5vcGhpbF90YWJsZV9iYXRjaHZpc2l0IDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHRfY2ZfZW9zaW5vcGhpbF9kZV9iYXRjaHZpc2l0LCBrZWVwZXJzID0gdF9jZl9jb250cmFzdCwgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9Fb3Npbm9waGlscy90X2Vvc2lub3BoaWxfY2ZfdGFibGVfYmF0Y2h2aXNpdC12e3Zlcn0ueGxzeCIpKQp0X2NmX2Vvc2lub3BoaWxfdGFibGVfYmF0Y2h2aXNpdAp0X2NmX2Vvc2lub3BoaWxfc2lnX2JhdGNodmlzaXQgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X2NmX2Vvc2lub3BoaWxfdGFibGVfYmF0Y2h2aXNpdCwKICBleGNlbCA9IGdsdWUoIntjZl9wcmVmaXh9L0Vvc2lub3BoaWxzL3RfZW9zaW5vcGhpbF9jZl9zaWdfYmF0Y2h2aXNpdC12e3Zlcn0ueGxzeCIpKQp0X2NmX2Vvc2lub3BoaWxfc2lnX2JhdGNodmlzaXQKCmRpbSh0X2NmX2Vvc2lub3BoaWxfc2lnX2JhdGNodmlzaXRbWyJkZXNlcSJdXVtbInVwcyJdXVtbMV1dKQpkaW0odF9jZl9lb3Npbm9waGlsX3NpZ19iYXRjaHZpc2l0W1siZGVzZXEiXV1bWyJkb3ducyJdXVtbMV1dKQoKaGVhZCh0X2NmX2Vvc2lub3BoaWxfc2lnX2JhdGNodmlzaXRbWyJkZXNlcSJdXVtbInVwcyJdXVtbMV1dKQpoZWFkKHRfY2ZfZW9zaW5vcGhpbF9zaWdfYmF0Y2h2aXNpdFtbImRlc2VxIl1dW1siZG93bnMiXV1bWzFdXSkKYGBgCgpSZXBlYXQgd2l0aCB2aXNpdCBpbiB0aGUgY29uZGl0aW9uIGNvbnRyYXN0LgoKYGBge3J9CnZpc2l0Y2ZfZmFjdG9yIDwtIHBhc3RlMChwRGF0YSh0X2Vvc2lub3BoaWxzKVtbInZpc2l0bnVtYmVyIl1dLCAiXyIsCiAgICAgICAgICAgICAgICAgICAgICAgICBwRGF0YSh0X2Vvc2lub3BoaWxzKVtbImZpbmFsb3V0Y29tZSJdXSkKdF9lb3Npbm9waGlsX3Zpc2l0Y2YgPC0gc2V0X2V4cHRfY29uZGl0aW9ucyh0X2Vvc2lub3BoaWxzLCBmYWN0ID0gdmlzaXRjZl9mYWN0b3IpCgp0X2NmX2Vvc2lub3BoaWxfdmlzaXRzX2RlX3N2YSA8LSBhbGxfcGFpcndpc2UodF9lb3Npbm9waGlsX3Zpc2l0Y2YsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfY2ZfZW9zaW5vcGhpbF92aXNpdHNfZGVfc3ZhCnRfY2ZfZW9zaW5vcGhpbF92aXNpdHNfdGFibGVfc3ZhIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogICB0X2NmX2Vvc2lub3BoaWxfdmlzaXRzX2RlX3N2YSwga2VlcGVycyA9IHZpc2l0Y2ZfY29udHJhc3RzLCBzY2FsZV9wID0gVFJVRSwKICBleGNlbCA9IGdsdWUoIntjZl9wcmVmaXh9L0Vvc2lub3BoaWxzL3RfZW9zaW5vcGhpbF92aXNpdGNmX3RhYmxlX3N2YS12e3Zlcn0ueGxzeCIpKQp0X2NmX2Vvc2lub3BoaWxfdmlzaXRzX3RhYmxlX3N2YQp0X2NmX2Vvc2lub3BoaWxfdmlzaXRzX3NpZ19zdmEgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X2NmX2Vvc2lub3BoaWxfdmlzaXRzX3RhYmxlX3N2YSwKICBleGNlbCA9IGdsdWUoIntjZl9wcmVmaXh9L0Vvc2lub3BoaWxzL3RfZW9zaW5vcGhpbF92aXNpdGNmX3NpZ19zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZl9lb3Npbm9waGlsX3Zpc2l0c19zaWdfc3ZhCgpkaW0odF9jZl9lb3Npbm9waGlsX3Zpc2l0c19zaWdfc3ZhW1siZGVzZXEiXV1bWyJ1cHMiXV1bWzFdXSkKZGltKHRfY2ZfZW9zaW5vcGhpbF92aXNpdHNfc2lnX3N2YVtbImRlc2VxIl1dW1siZG93bnMiXV1bWzFdXSkKCmhlYWQodF9jZl9lb3Npbm9waGlsX3Zpc2l0c19zaWdfc3ZhW1siZGVzZXEiXV1bWyJ1cHMiXV1bWzFdXSkKaGVhZCh0X2NmX2Vvc2lub3BoaWxfdmlzaXRzX3NpZ19zdmFbWyJkZXNlcSJdXVtbImRvd25zIl1dW1sxXV0pCmBgYAoKIyBDb21wYXJlIHRvIFZpc2l0IGV4cGxpY2l0bHkgaW4gdGhlIG1vZGVsCgpBcyBhIHJlbWluZGVyLCB0aGVyZSBhcmUgYSBmZXcgZ2VuZXMgb2YgcGFydGljdWxhciBpbnRlcmVzdDoKCmBgYHtyfQpleHBlY3RlZF9nZW5lcyA8LSBjKCJJRkk0NEwiLCAiSUZJMjciLCAiUFJSNSIsICJQUlI1LUFSSEdBUDgiLCAiUkhDRSIsCiAgICAgICAgICAgICAgICAgICAgIkZCWE8zOSIsICJSU0FEMiIsICJTTVROTDEiLCAiVVNQMTgiLCAiQUZBUDEiKQphbm5vdCA8LSBmRGF0YSh0X21vbm9jeXRlcykKd2FudGVkX2lkeCA8LSBhbm5vdFtbImhnbmNfc3ltYm9sIl1dICVpbiUgZXhwZWN0ZWRfZ2VuZXMKZXhwZWN0ZWRfZW5zZyA8LSByb3duYW1lcyhhbm5vdClbd2FudGVkX2lkeF0KYGBgCgojIyBNb25vY3l0ZXMKCkVpdGhlciBhYm92ZSBvciBiZWxvdyB0aGlzIHNlY3Rpb24gSSBoYXZlIGEgbmVhcmx5IGlkZW50aWNhbCBibG9jawp3aGljaCBzZWVrcyB0byBkZW1vbnN0cmF0ZSB0aGUgc2ltaWxhcml0aWVzL2RpZmZlcmVuY2Ugb2JzZXJ2ZWQKYmV0d2VlbiBteSBwcmVmZXJyZWQvc2ltcGxpZmllZCBtb2RlbCB2cy4gYSBtb3JlIGV4cGxpY2l0bHkgY29ycmVjdAphbmQgY29tcGxleCBtb2RlbC4gIElmIHRoZSB0cmVuZCBob2xkcyBmcm9tIHdoYXQgd2Ugb2JzZXJ2ZWQgd2l0aCB0aGUKZW9zaW5vcGhpbHMgYW5kIG5ldXRyb3BoaWxzLCBJIHdvdWxkIGV4cGVjdCB0byBzZWUgdGhhdCB0aGUgcmVzdWx0cwphcmUgbWFyZ2luYWxseSAnYmV0dGVyJyAoYXMgZGVmaW5lZCBieSB0aGUgc3RyZW5ndGggb2YgdGhlIHBlcmNlaXZlZAppbnRlcmxldWtpbiByZXNwb25zZSBhbmQgcmF3IG51bWJlciBvZiAnc2lnbmlmaWNhbnQnIGdlbmVzKTsgYnV0IEkKcmVtYWluIHdvcnJpZWQgdGhhdCB0aGlzIHdpbGwgcHJvdmUgYSBtb3JlIGJyaXR0bGUgYW5kIGVycm9yLXByb25lCmFuYWx5c2lzLgoKIyMjIEZpbHRlciB0aGUgZGF0YSBhbmQgcGVyZm9ybSBzdmFzZXEKClN0YXJ0IG91dCBieSBleHRyYWN0aW5nIHRoZSBwZXJjZWl2ZWQgc3ZzIHZpYSBzdmFzZXEgb24gdGhlIGZpbHRlcmVkCmlucHV0LgoKTm90ZSB0byBzZWxmOiB0aGUgbW9kZWwgd29yayBJIGRpZCBpbiBocGdsdG9vbHMgbWFrZXMgdGhpcyBpcnJlbGV2YW50OwpieSByZXBsYWNpbmcgdGhlIGZvb2xpc2ggbW9kZWxfY29ubmQvbW9kZWxfYmF0Y2ggYXJndW1lbnRzIHdpdGgKZnN0cmluZyBhbmQgbW9kZWxfc3YgSSBjYW4gZG8gdGhpcyB0cml2aWFsbHkuICBJbiBhZGRpdGlvbiwgaXQgYWxsb3dzCm1lIHRvIG1peCBhbmQgbWF0Y2ggc3YgbWV0aG9kcyBhbmQgY29tcGFyZSB0aGVtIHVzaW5nIHRoZSBzYW1lIGRhdGEKc3RydWN0dXJlIGJlY2F1c2UgaXQgcmV0dXJucyB0aGUgZXhwcmVzc2lvbnNldCB3aXRoIG5ldyBjb2x1bW5zIG5hbWVkCnN0dWZmIGxpa2UgJ3N2YXNlcV9zdjEnLi4uCgpgYGB7cn0KIyMgVGhlIG9yaWdpbmFsIHBhaXJ3aXNlIGludm9jYXRpb24gd2l0aCBzdmE6CiMjdF9jZl9tb25vY3l0ZV9kZV9zdmEgPC0gYWxsX3BhaXJ3aXNlKHRfbW9ub2N5dGUsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiMjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IFRSVUUsIHBhcmFsbGVsID0gRkFMU0UsCiMjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBtZXRob2RzKQp0ZXN0X21vbm9jeXRlcyA8LSBub3JtYWxpemVfZXhwdCh0X21vbm9jeXRlcywgZmlsdGVyID0gInNpbXBsZSIpCnRlc3RfbW9ub19kZXNpZ24gPC0gcERhdGEodGVzdF9tb25vY3l0ZXMpCnRlc3RfZm9ybXVsYSA8LSBhcy5mb3JtdWxhKCJ+IGZpbmFsb3V0Y29tZSArIHZpc2l0bnVtYmVyIikKdGVzdF9tb2RlbCA8LSBtb2RlbC5tYXRyaXgodGVzdF9mb3JtdWxhLCBkYXRhID0gdGVzdF9tb25vX2Rlc2lnbikKbnVsbF9mb3JtdWxhIDwtIGFzLmZvcm11bGEoIn4gdmlzaXRudW1iZXIiKQpudWxsX21vZGVsIDwtIG1vZGVsLm1hdHJpeChudWxsX2Zvcm11bGEsIGRhdGEgPSB0ZXN0X21vbm9fZGVzaWduKQoKbGluZWFyX210cnggPC0gZXhwcnModGVzdF9tb25vY3l0ZXMpCmwyX210cnggPC0gbG9nMihsaW5lYXJfbXRyeCArIDEpCmNob3Nlbl9zdXJyb2dhdGVzIDwtIHN2YTo6bnVtLnN2KGRhdCA9IGwyX210cngsIG1vZCA9IHRlc3RfbW9kZWwpCmNob3Nlbl9zdXJyb2dhdGVzCgpzdXJyb2dhdGVfcmVzdWx0IDwtIHN2YTo6c3Zhc2VxKAogIGRhdCA9IGxpbmVhcl9tdHJ4LCBuLnN2ID0gY2hvc2VuX3N1cnJvZ2F0ZXMsIG1vZCA9IHRlc3RfbW9kZWwsIG1vZDAgPSBudWxsX21vZGVsKQptb2RlbF9hZGp1c3QgPC0gYXMubWF0cml4KHN1cnJvZ2F0ZV9yZXN1bHRbWyJzdiJdXSkKYGBgCgojIyMgQWRkIHRoZSBzdnMgdG8gdGhlIGRhdGEgbW9kZWwgYW5kIGNyZWF0ZSBhIG5ldyBERVNlcTIgZGF0YXNldAoKV2UgY2FuIG5vdyBjcmVhdGUgYSBuZXcgREVTZXEyIGRhdGFzZXQgd2hpY2ggdGFrZXMgdGhlc2UgcHV0YXRpdmUKc3Vycm9nYXRlcyBpbnRvIGFjY291bnQuCgpgYGB7cn0KY29sbmFtZXMobW9kZWxfYWRqdXN0KSA8LSBwYXN0ZTAoIlNWIiwgc2VxX2xlbihjaG9zZW5fc3Vycm9nYXRlcykpCnJvd25hbWVzKG1vZGVsX2FkanVzdCkgPC0gcm93bmFtZXMocERhdGEodGVzdF9tb25vY3l0ZXMpKQphZGRpdGlvbl9zdHJpbmcgPC0gIiIKZm9yIChzdiBpbiBjb2xuYW1lcyhtb2RlbF9hZGp1c3QpKSB7CiAgYWRkaXRpb25fc3RyaW5nIDwtIHBhc3RlMChhZGRpdGlvbl9zdHJpbmcsICIgKyAiLCBzdikKfQpsb25nZXJfbW9kZWwgPC0gYXMuZm9ybXVsYShnbHVlKCJ+IGZpbmFsb3V0Y29tZSArIHZpc2l0bnVtYmVye2FkZGl0aW9uX3N0cmluZ30iKSkKbW9ub19kZXNpZ25fc3ZzIDwtIGNiaW5kKHRlc3RfbW9ub19kZXNpZ24sIG1vZGVsX2FkanVzdCkKCnN1bW1hcml6ZWQgPC0gREVTZXEyOjpERVNlcURhdGFTZXRGcm9tTWF0cml4KGNvdW50RGF0YSA9IGxpbmVhcl9tdHJ4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gbW9ub19kZXNpZ25fc3ZzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSBsb25nZXJfbW9kZWwpCmBgYAoKIyMjIFJ1biBERVNlcSBhbmQgY29tcGFyZSB0aGUgcmVzdWx0cyB0byBvdXIgcHJldmlvdXMgaW52b2NhdGlvbgoKSW4gb3JkZXIgdG8gY29tcGFyZSB0aGVzZSBhbmQgdGhlIHByZXZpb3VzIHJlc3VsdHMsIEkgdGVuZCB0byByZWx5IG9uCnNpbXBsZSBjb3JyZWxhdGlvbnMgYW5kIGF1Y2MgcGxvdHMuICBJIGhhdmUgYmVlbiByZWFkaW5nIHRoZSBtb2RlbHIKY29kZSByZWNlbnRseSBhbmQgaXQgbG9va3MgbGlrZSB0aGVyZSBpcyBhIHN1aXRlIG9mIG90aGVyIG1ldHJpY3MKd2hpY2ggbWlnaHQgYmUgbW9yZSBhcHByb3ByaWF0ZS4KCmBgYHtyfQpkZXNlcV9ydW4gPC0gREVTZXEyOjpERVNlcShzdW1tYXJpemVkKQpkZXNlcV90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKERFU2VxMjo6cmVzdWx0cyhvYmplY3QgPSBkZXNlcV9ydW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyYXN0ID0gYygiZmluYWxvdXRjb21lIiwgImZhaWx1cmUiLCAiY3VyZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQgPSAiRGF0YUZyYW1lIikpCgpiaWdfdGFibGUgPC0gdF9jZl9tb25vY3l0ZV90YWJsZV9zdmFbWyJkYXRhIl1dW1sib3V0Y29tZSJdXQpvbmx5X2Rlc2VxIDwtIGJpZ190YWJsZVssIGMoImRlc2VxX2xvZ2ZjIiwgImRlc2VxX2FkanAiKV0KbWVyZ2VkIDwtIG1lcmdlKGRlc2VxX3RhYmxlLCBvbmx5X2Rlc2VxLCBieSA9ICJyb3cubmFtZXMiKQpyb3duYW1lcyhtZXJnZWQpIDwtIG1lcmdlZFtbIlJvdy5uYW1lcyJdXQptZXJnZWRbWyJSb3cubmFtZXMiXV0gPC0gTlVMTAoKY29yX3ZhbHVlIDwtIGNvci50ZXN0KG1lcmdlZFtbImxvZzJGb2xkQ2hhbmdlIl1dLCBtZXJnZWRbWyJkZXNlcV9sb2dmYyJdXSkKY29yX3ZhbHVlCmxvZ2ZjX3Bsb3R0ZXIgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihtZXJnZWRbLCBjKCJsb2cyRm9sZENoYW5nZSIsICJkZXNlcV9sb2dmYyIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFkZF9jb3IgPSBUUlVFLCBhZGRfcnNxID0gVFJVRSwgaWRlbnRpdHkgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWRkX2VxdWF0aW9uID0gVFJVRSkKbG9nZmNfcGxvdCA8LSBsb2dmY19wbG90dGVyW1sic2NhdHRlciJdXSArCiAgeGxhYigiREVTZXEyIGxvZzJGQzogVmlzaXQgZXhwbGljaXRseSBpbiBtb2RlbCIpICsKICB5bGFiKCJERVNlcTIgbG9nMkZDOiBEZWZhdWx0IHBhaXJ3aXNlIGNvbXBhcmlzb24iKQpwcChmaWxlID0gImZpZ3VyZXMvY29tcGFyZV9jZl9hbmRfdmlzaXRfaW5fbW9kZWxfbW9ub2N5dGVfbG9nZmMuc3ZnIikKbG9nZmNfcGxvdApkZXYub2ZmKCkKbG9nZmNfcGxvdAoKY29yX3ZhbHVlIDwtIGNvci50ZXN0KG1lcmdlZFtbInBhZGoiXV0sIG1lcmdlZFtbImRlc2VxX2FkanAiXV0sIG1ldGhvZCA9ICJzcGVhcm1hbiIpCmNvcl92YWx1ZQphZGpwX3Bsb3R0ZXIgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihtZXJnZWRbLCBjKCJwYWRqIiwgImRlc2VxX2FkanAiKV0pCmFkanBfcGxvdCA8LSBhZGpwX3Bsb3R0ZXJbWyJzY2F0dGVyIl1dICsKICB4bGFiKCJERVNlcTIgYWRqcDogVmlzaXQgZXhwbGljaXRseSBpbiBtb2RlbCIpICsKICB5bGFiKCJERVNlcTIgYWRqcDogRGVmYXVsdCBwYWlyd2lzZSBjb21wYXJpc29uIikKcHAoZmlsZSA9ICJpbWFnZXMvY29tcGFyZV9jZl9hbmRfdmlzaXRfaW5fbW9kZWxfbW9ub2N5dGVfYWRqcC5zdmciKQphZGpwX3Bsb3QKZGV2Lm9mZigpCmFkanBfcGxvdAoKcHJldmlvdXNfc2lnX2lkeCA8LSBiaWdfdGFibGVbWyJkZXNlcV9hZGpwIl1dIDw9IDAuMDUgJgogIGFicyhiaWdfdGFibGVbWyJkZXNlcV9sb2dmYyJdXSA+PSAxLjApCnN1bW1hcnkocHJldmlvdXNfc2lnX2lkeCkKcHJldmlvdXNfZ2VuZXMgPC0gcm93bmFtZXMoYmlnX3RhYmxlKVtwcmV2aW91c19zaWdfaWR4XQoKbmV3X3NpZ19pZHggPC0gYWJzKGRlc2VxX3RhYmxlW1sibG9nMkZvbGRDaGFuZ2UiXV0pID49IDEuMCAmCiAgZGVzZXFfdGFibGVbWyJwYWRqIl1dIDwgMC4wNQpuZXdfZ2VuZXMgPC0gcm93bmFtZXMoZGVzZXFfdGFibGUpW25ld19zaWdfaWR4XQpuYV9pZHggPC0gaXMubmEobmV3X2dlbmVzKQpuZXdfZ2VuZXMgPC0gbmV3X2dlbmVzWyFuYV9pZHhdCgpWZW5uZXJhYmxlOjpWZW5uKGxpc3QoInByZXZpb3VzIiA9IHByZXZpb3VzX2dlbmVzLCAibmV3IiA9IG5ld19nZW5lcykpCgp0ZXN0X25ldyA8LSBzaW1wbGVfZ3Byb2ZpbGVyKG5ld19nZW5lcykKdGVzdF9uZXcKdGVzdF9vbGQgPC0gc2ltcGxlX2dwcm9maWxlcihwcmV2aW91c19nZW5lcykKdGVzdF9vbGQKCm5ld19hbm5vdGF0ZWQgPC0gbWVyZ2UoZkRhdGEodF9tb25vY3l0ZXMpLCBkZXNlcV90YWJsZSwgYnkgPSAicm93Lm5hbWVzIikKcm93bmFtZXMobmV3X2Fubm90YXRlZCkgPC0gbmV3X2Fubm90YXRlZFtbIlJvdy5uYW1lcyJdXQpuZXdfYW5ub3RhdGVkW1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKd3JpdGVfeGxzeChkYXRhID0gbmV3X2Fubm90YXRlZCwgZXhjZWwgPSAiZXhjZWwvbW9ub2N5dGVfdmlzaXRfaW5fbW9kZWxfc3ZhX2NmX25ldy54bHN4IikKCm9sZF9hbm5vdGF0ZWQgPC0gbWVyZ2UoZkRhdGEodF9lb3Npbm9waGlscyksIGJpZ190YWJsZSwgYnkgPSAicm93Lm5hbWVzIikKcm93bmFtZXMob2xkX2Fubm90YXRlZCkgPC0gb2xkX2Fubm90YXRlZFtbIlJvdy5uYW1lcyJdXQpvbGRfYW5ub3RhdGVkW1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKd3JpdGVfeGxzeChkYXRhID0gb2xkX2Fubm90YXRlZCwgZXhjZWwgPSAiZXhjZWwvbW9ub2N5dGVfdmlzaXRfaW5fbW9kZWxfc3ZhX2NmX29sZC54bHN4IikKYGBgCgpBcmUgdGhlIGV4cGVjdGVkIEVuc2VtYmwgZ2VuZSBJRHMgZm91bmQgaW4gdGhpcyBuZXcgc2V0PwoKYGBge3J9CnN1bShuZXdfZ2VuZXMgJWluJSBleHBlY3RlZF9lbnNnKQpgYGAKCiMjIEVvc2lub3BoaWxzCgpXZSB3aXNoIHRvIGVuc3VyZSB0aGF0IG15IG1vZGVsIHNpbXBsaWZpY2F0aW9uIGRpZCBub3QgZG8gYW55dGhpbmcKaW5jb3JyZWN0IHRvIHRoZSBkYXRhIGZvciBhbGwgdGhyZWUgY2VsbCB0eXBlcywgSSBhbHJlYWR5IGRpZCB0aGlzIGZvcgp0aGUgbmV1dHJvcGhpbHMsIGxldCB1cyByZXBlYXQgZm9yIHRoZSBlb3Npbm9waGlscy4gIEkgYW0gdGhlcmVmb3JlCihtb3N0bHkpIGNvcHkvcGFzdGluZyB0aGUgbmV1dHJvcGhpbCBzZWN0aW9uIGhlcmUuCgpgYGB7cn0KIyMgVGhlIG9yaWdpbmFsIHBhaXJ3aXNlIGludm9jYXRpb24gd2l0aCBzdmE6CiN0X2NmX2Vvc2lub3BoaWxfZGVfc3ZhIDwtIGFsbF9wYWlyd2lzZSh0X2Vvc2lub3BoaWxzLCBtb2RlbF9iYXRjaCA9ICJzdmFzZXEiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwgcGFyYWxsZWw9RkFMU0UsIG1ldGhvZHMgPSBtZXRob2RzKQp0ZXN0X2Vvc2lub3BoaWxzIDwtIG5vcm1hbGl6ZV9leHB0KHRfZW9zaW5vcGhpbHMsIGZpbHRlciA9ICJzaW1wbGUiKQp0ZXN0X2VvX2Rlc2lnbiA8LSBwRGF0YSh0ZXN0X2Vvc2lub3BoaWxzKQp0ZXN0X2Zvcm11bGEgPC0gYXMuZm9ybXVsYSgifiAwICsgZmluYWxvdXRjb21lICsgdmlzaXRudW1iZXIiKQp0ZXN0X21vZGVsIDwtIG1vZGVsLm1hdHJpeCh0ZXN0X2Zvcm11bGEsIGRhdGEgPSB0ZXN0X2VvX2Rlc2lnbikKbnVsbF9mb3JtdWxhIDwtIGFzLmZvcm11bGEoIn4gMCArIHZpc2l0bnVtYmVyIikKbnVsbF9tb2RlbCA8LSBtb2RlbC5tYXRyaXgobnVsbF9mb3JtdWxhLCBkYXRhID0gdGVzdF9lb19kZXNpZ24pCgpsaW5lYXJfbXRyeCA8LSBleHBycyh0ZXN0X2Vvc2lub3BoaWxzKQpsMl9tdHJ4IDwtIGxvZzIobGluZWFyX210cnggKyAxKQpjaG9zZW5fc3Vycm9nYXRlcyA8LSBzdmE6Om51bS5zdihkYXQgPSBsMl9tdHJ4LCBtb2QgPSB0ZXN0X21vZGVsKQpjaG9zZW5fc3Vycm9nYXRlcwoKc3Vycm9nYXRlX3Jlc3VsdCA8LSBzdmE6OnN2YXNlcSgKICBkYXQgPSBsaW5lYXJfbXRyeCwgbi5zdiA9IGNob3Nlbl9zdXJyb2dhdGVzLCBtb2QgPSB0ZXN0X21vZGVsLCBtb2QwID0gbnVsbF9tb2RlbCkKbW9kZWxfYWRqdXN0IDwtIGFzLm1hdHJpeChzdXJyb2dhdGVfcmVzdWx0W1sic3YiXV0pCgpjb2xuYW1lcyhtb2RlbF9hZGp1c3QpIDwtIGMoIlNWMSIsICJTVjIiLCAiU1YzIikKcm93bmFtZXMobW9kZWxfYWRqdXN0KSA8LSByb3duYW1lcyhwRGF0YSh0ZXN0X2Vvc2lub3BoaWxzKSkKbG9uZ2VyX21vZGVsIDwtIGFzLmZvcm11bGEoIn4gZmluYWxvdXRjb21lICsgdmlzaXRudW1iZXIgKyBTVjEgKyBTVjIgKyBTVjMiKQplb19kZXNpZ25fc3ZzIDwtIGNiaW5kKHRlc3RfZW9fZGVzaWduLCBtb2RlbF9hZGp1c3QpCnN1bW1hcml6ZWQgPC0gREVTZXEyOjpERVNlcURhdGFTZXRGcm9tTWF0cml4KGNvdW50RGF0YSA9IGxpbmVhcl9tdHJ4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gZW9fZGVzaWduX3N2cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduID0gbG9uZ2VyX21vZGVsKQpkZXNlcV9ydW4gPC0gREVTZXEyOjpERVNlcShzdW1tYXJpemVkKQpkZXNlcV90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKERFU2VxMjo6cmVzdWx0cyhvYmplY3QgPSBkZXNlcV9ydW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyYXN0ID0gYygiZmluYWxvdXRjb21lIiwgImZhaWx1cmUiLCAiY3VyZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQgPSAiRGF0YUZyYW1lIikpCgpiaWdfdGFibGUgPC0gdF9jZl9lb3Npbm9waGlsX3RhYmxlX3N2YVtbImRhdGEiXV1bWyJvdXRjb21lIl1dCm9ubHlfZGVzZXEgPC0gYmlnX3RhYmxlWywgYygiZGVzZXFfbG9nZmMiLCAiZGVzZXFfYWRqcCIpXQptZXJnZWQgPC0gbWVyZ2UoZGVzZXFfdGFibGUsIG9ubHlfZGVzZXEsIGJ5ID0gInJvdy5uYW1lcyIpCnJvd25hbWVzKG1lcmdlZCkgPC0gbWVyZ2VkW1siUm93Lm5hbWVzIl1dCm1lcmdlZFtbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCgpjb3JfdmFsdWUgPC0gY29yLnRlc3QobWVyZ2VkW1sibG9nMkZvbGRDaGFuZ2UiXV0sIG1lcmdlZFtbImRlc2VxX2xvZ2ZjIl1dKQpjb3JfdmFsdWUKbG9nZmNfcGxvdHRlciA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKG1lcmdlZFssIGMoImxvZzJGb2xkQ2hhbmdlIiwgImRlc2VxX2xvZ2ZjIildKQpsb2dmY19wbG90IDwtIGxvZ2ZjX3Bsb3R0ZXJbWyJzY2F0dGVyIl1dICsKICB4bGFiKCJERVNlcTIgbG9nMkZDOiBWaXNpdCBleHBsaWNpdGx5IGluIG1vZGVsIikgKwogIHlsYWIoIkRFU2VxMiBsb2cyRkM6IERlZmF1bHQgcGFpcndpc2UgY29tcGFyaXNvbiIpCnBwKGZpbGUgPSAiZmlndXJlcy9jb21wYXJlX2NmX2FuZF92aXNpdF9pbl9tb2RlbF9lb3Npbm9waGlsX2xvZ2ZjLnN2ZyIpCmxvZ2ZjX3Bsb3QKZGV2Lm9mZigpCmxvZ2ZjX3Bsb3QKCmNvcl92YWx1ZSA8LSBjb3IudGVzdChtZXJnZWRbWyJwYWRqIl1dLCBtZXJnZWRbWyJkZXNlcV9hZGpwIl1dLCBtZXRob2QgPSAic3BlYXJtYW4iKQpjb3JfdmFsdWUKYWRqcF9wbG90dGVyIDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIobWVyZ2VkWywgYygicGFkaiIsICJkZXNlcV9hZGpwIildKQphZGpwX3Bsb3QgPC0gYWRqcF9wbG90dGVyW1sic2NhdHRlciJdXSArCiAgeGxhYigiREVTZXEyIGFkanA6IFZpc2l0IGV4cGxpY2l0bHkgaW4gbW9kZWwiKSArCiAgeWxhYigiREVTZXEyIGFkanA6IERlZmF1bHQgcGFpcndpc2UgY29tcGFyaXNvbiIpCnBwKGZpbGUgPSAiaW1hZ2VzL2NvbXBhcmVfY2ZfYW5kX3Zpc2l0X2luX21vZGVsX2Vvc2lub3BoaWxfYWRqcC5zdmciKQphZGpwX3Bsb3QKZGV2Lm9mZigpCmFkanBfcGxvdAoKcHJldmlvdXNfc2lnX2lkeCA8LSBiaWdfdGFibGVbWyJkZXNlcV9hZGpwIl1dIDw9IDAuMDUgJgogIGFicyhiaWdfdGFibGVbWyJkZXNlcV9sb2dmYyJdXSA+PSAxLjApCnN1bW1hcnkocHJldmlvdXNfc2lnX2lkeCkKcHJldmlvdXNfZ2VuZXMgPC0gcm93bmFtZXMoYmlnX3RhYmxlKVtwcmV2aW91c19zaWdfaWR4XQoKbmV3X3NpZ19pZHggPC0gYWJzKGRlc2VxX3RhYmxlW1sibG9nMkZvbGRDaGFuZ2UiXV0pID49IDEuMCAmCiAgZGVzZXFfdGFibGVbWyJwYWRqIl1dIDwgMC4wNQpuZXdfZ2VuZXMgPC0gcm93bmFtZXMoZGVzZXFfdGFibGUpW25ld19zaWdfaWR4XQpuYV9pZHggPC0gaXMubmEobmV3X2dlbmVzKQpuZXdfZ2VuZXMgPC0gbmV3X2dlbmVzWyFuYV9pZHhdCgpWZW5uZXJhYmxlOjpWZW5uKGxpc3QoInByZXZpb3VzIiA9IHByZXZpb3VzX2dlbmVzLCAibmV3IiA9IG5ld19nZW5lcykpCgp0ZXN0X25ldyA8LSBzaW1wbGVfZ3Byb2ZpbGVyKG5ld19nZW5lcykKdGVzdF9uZXcKdGVzdF9vbGQgPC0gc2ltcGxlX2dwcm9maWxlcihwcmV2aW91c19nZW5lcykKdGVzdF9vbGQKCm5ld19hbm5vdGF0ZWQgPC0gbWVyZ2UoZkRhdGEodF9lb3Npbm9waGlscyksIGRlc2VxX3RhYmxlLCBieSA9ICJyb3cubmFtZXMiKQpyb3duYW1lcyhuZXdfYW5ub3RhdGVkKSA8LSBuZXdfYW5ub3RhdGVkW1siUm93Lm5hbWVzIl1dCm5ld19hbm5vdGF0ZWRbWyJSb3cubmFtZXMiXV0gPC0gTlVMTAp3cml0ZV94bHN4KGRhdGEgPSBuZXdfYW5ub3RhdGVkLCBleGNlbCA9ICJleGNlbC9lb3Npbm9waGlsX3Zpc2l0X2luX21vZGVsX3N2YV9jZl9uZXcueGxzeCIpCgpvbGRfYW5ub3RhdGVkIDwtIG1lcmdlKGZEYXRhKHRfZW9zaW5vcGhpbHMpLCBiaWdfdGFibGUsIGJ5ID0gInJvdy5uYW1lcyIpCnJvd25hbWVzKG9sZF9hbm5vdGF0ZWQpIDwtIG9sZF9hbm5vdGF0ZWRbWyJSb3cubmFtZXMiXV0Kb2xkX2Fubm90YXRlZFtbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCndyaXRlX3hsc3goZGF0YSA9IG9sZF9hbm5vdGF0ZWQsIGV4Y2VsID0gImV4Y2VsL2Vvc2lub3BoaWxfdmlzaXRfaW5fbW9kZWxfc3ZhX2NmX29sZC54bHN4IikKYGBgCgpDaGVjayBvdXIgZ2VuZXMgb2YgcGFydGljdWxhciBpbnRlcmVzdAoKYGBge3J9CnN1bShuZXdfZ2VuZXMgJWluJSBleHBlY3RlZF9lbnNnKQpgYGAKCk5vdCBxdWl0ZSBhcyBzaW1pbGFyIGFzIHRoZSBtb25vY3l0ZSBkYXRhLgoKIyMgTmV1dHJvcGhpbHMKCmBgYHtyfQojIyBUaGUgb3JpZ2luYWwgcGFpcndpc2UgaW52b2NhdGlvbiB3aXRoIHN2YToKIyMgdF9jZl9uZXV0cm9waGlsX2RlX3N2YSA8LSBhbGxfcGFpcndpc2UodF9uZXV0cm9waGlscywgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKIyMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYWxsZWwgPSBwYXJhbGxlbCwgZmlsdGVyID0gVFJVRSwKIyMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRlc3RfbmV1dHJvcGhpbHMgPC0gbm9ybWFsaXplX2V4cHQodF9uZXV0cm9waGlscywgZmlsdGVyID0gInNpbXBsZSIpCnRlc3RfbmV1dF9kZXNpZ24gPC0gcERhdGEodGVzdF9uZXV0cm9waGlscykKdGVzdF9mb3JtdWxhIDwtIGFzLmZvcm11bGEoIn4gMCArIGZpbmFsb3V0Y29tZSArIHZpc2l0bnVtYmVyIikKdGVzdF9tb2RlbCA8LSBtb2RlbC5tYXRyaXgodGVzdF9mb3JtdWxhLCBkYXRhID0gdGVzdF9uZXV0X2Rlc2lnbikKIyMgTm90ZSB0byBzZWxmOiBkb3VibGUtY2hlY2sgdGhhdCB0aGUgZm9sbG93aW5nIGxpbmUgaXMgY29ycmVjdC4KbnVsbF9mb3JtdWxhIDwtIGFzLmZvcm11bGEoIn4gMCArIHZpc2l0bnVtYmVyIikKIyMgbnVsbF9tb2RlbCA8LSB0ZXN0X21vZGVsWywgYygxLCAyKV0KbnVsbF9tb2RlbCA8LSBtb2RlbC5tYXRyaXgobnVsbF9mb3JtdWxhLCBkYXRhID0gdGVzdF9uZXV0X2Rlc2lnbikKCmxpbmVhcl9tdHJ4IDwtIGV4cHJzKHRlc3RfbmV1dHJvcGhpbHMpCmwyX210cnggPC0gbG9nMihsaW5lYXJfbXRyeCArIDEpCmNob3Nlbl9zdXJyb2dhdGVzIDwtIHN2YTo6bnVtLnN2KGRhdCA9IGwyX210cngsIG1vZCA9IHRlc3RfbW9kZWwpCmNob3Nlbl9zdXJyb2dhdGVzCgpzdXJyb2dhdGVfcmVzdWx0IDwtIHN2YTo6c3Zhc2VxKAogIGRhdCA9IGxpbmVhcl9tdHJ4LCBuLnN2ID0gY2hvc2VuX3N1cnJvZ2F0ZXMsIG1vZCA9IHRlc3RfbW9kZWwsIG1vZDAgPSBudWxsX21vZGVsKQptb2RlbF9hZGp1c3QgPC0gYXMubWF0cml4KHN1cnJvZ2F0ZV9yZXN1bHRbWyJzdiJdXSkKCiMjIEkgZG9uJ3QgdGhpbmsgdGhlIGZvbGxvd2luZyBpcyBhY3R1YWxseSByZXF1aXJlZCwgYnV0IGl0IGlzIHdlaXJkIHRvIGp1c3QgaGF2ZSB0aGlzCiMjIHVubmFtZWQgbWF0cml4IGhhbmdpbmdvdXQuCiMjIFNldCB0aGUgY29sdW1ucyB0byB0aGUgU1Yjcwpjb2xuYW1lcyhtb2RlbF9hZGp1c3QpIDwtIGMoIlNWMSIsICJTVjIiLCAiU1YzIiwgIlNWNCIpCiMjIFNldCB0aGUgcm93cyB0aGUgc2FtcGxlIElEcwpyb3duYW1lcyhtb2RlbF9hZGp1c3QpIDwtIHJvd25hbWVzKHBEYXRhKHRlc3RfbmV1dHJvcGhpbHMpKQoKbG9uZ2VyX21vZGVsIDwtIGFzLmZvcm11bGEoIn4gZmluYWxvdXRjb21lICsgdmlzaXRudW1iZXIgKyBTVjEgKyBTVjIgKyBTVjMgKyBTVjQiKQpuZXV0X2Rlc2lnbl9zdnMgPC0gY2JpbmQodGVzdF9uZXV0X2Rlc2lnbiwgbW9kZWxfYWRqdXN0KQpzdW1tYXJpemVkIDwtIERFU2VxMjo6REVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBsaW5lYXJfbXRyeCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IG5ldXRfZGVzaWduX3N2cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduID0gbG9uZ2VyX21vZGVsKQpkZXNlcV9ydW4gPC0gREVTZXEyOjpERVNlcShzdW1tYXJpemVkKQpkZXNlcV90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKERFU2VxMjo6cmVzdWx0cyhvYmplY3QgPSBkZXNlcV9ydW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyYXN0ID0gYygiZmluYWxvdXRjb21lIiwgImZhaWx1cmUiLCAiY3VyZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQgPSAiRGF0YUZyYW1lIikpCgojIyBXZSBzaG91bGQgYmUgYWJsZSB0byBkaXJlY3RseSBjb21wYXJlIHRoaXMgdG8gdGhlIHRoZSBkZXNlcSBjb2x1bW5zIGZyb20gdGhlIGFib3ZlCiMjIGRhdGEgc3RydWN0dXJlIG5hbWVkOiB0X2NmX25ldXRyb3BoaWxfdGFibGVfc3ZhCgpiaWdfdGFibGUgPC0gdF9jZl9uZXV0cm9waGlsX3RhYmxlX3N2YVtbImRhdGEiXV1bWyJvdXRjb21lIl1dCm9ubHlfZGVzZXEgPC0gYmlnX3RhYmxlWywgYygiZGVzZXFfbG9nZmMiLCAiZGVzZXFfYWRqcCIpXQptZXJnZWQgPC0gbWVyZ2UoZGVzZXFfdGFibGUsIG9ubHlfZGVzZXEsIGJ5ID0gInJvdy5uYW1lcyIpCnJvd25hbWVzKG1lcmdlZCkgPC0gbWVyZ2VkW1siUm93Lm5hbWVzIl1dCm1lcmdlZFtbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCgpjb3JfdmFsdWUgPC0gY29yLnRlc3QobWVyZ2VkW1sibG9nMkZvbGRDaGFuZ2UiXV0sIG1lcmdlZFtbImRlc2VxX2xvZ2ZjIl1dKQpjb3JfdmFsdWUKbG9nZmNfcGxvdHRlciA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKG1lcmdlZFssIGMoImxvZzJGb2xkQ2hhbmdlIiwgImRlc2VxX2xvZ2ZjIildKQpsb2dmY19wbG90IDwtIGxvZ2ZjX3Bsb3R0ZXJbWyJzY2F0dGVyIl1dICsKICB4bGFiKCJERVNlcTIgbG9nMkZDOiBWaXNpdCBleHBsaWNpdGx5IGluIG1vZGVsIikgKwogIHlsYWIoIkRFU2VxMiBsb2cyRkM6IERlZmF1bHQgcGFpcndpc2UgY29tcGFyaXNvbiIpCnBwKGZpbGUgPSAiZmlndXJlcy9jb21wYXJlX2NmX2FuZF92aXNpdF9pbl9tb2RlbF9uZXV0cm9waGlsX2xvZ2ZjLnN2ZyIpCmxvZ2ZjX3Bsb3QKZGV2Lm9mZigpCmxvZ2ZjX3Bsb3QKCmNvcl92YWx1ZSA8LSBjb3IudGVzdChtZXJnZWRbWyJwYWRqIl1dLCBtZXJnZWRbWyJkZXNlcV9hZGpwIl1dLCBtZXRob2QgPSAic3BlYXJtYW4iKQpjb3JfdmFsdWUKYWRqcF9wbG90dGVyIDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIobWVyZ2VkWywgYygicGFkaiIsICJkZXNlcV9hZGpwIildKQphZGpwX3Bsb3QgPC0gYWRqcF9wbG90dGVyW1sic2NhdHRlciJdXSArCiAgeGxhYigiREVTZXEyIGFkanA6IFZpc2l0IGV4cGxpY2l0bHkgaW4gbW9kZWwiKSArCiAgeWxhYigiREVTZXEyIGFkanA6IERlZmF1bHQgcGFpcndpc2UgY29tcGFyaXNvbiIpCnBwKGZpbGUgPSAiaW1hZ2VzL2NvbXBhcmVfY2ZfYW5kX3Zpc2l0X2luX21vZGVsX25ldXRyb3BoaWxfYWRqcC5zdmciKQphZGpwX3Bsb3QKZGV2Lm9mZigpCmFkanBfcGxvdAoKcHJldmlvdXNfc2lnX2lkeCA8LSBiaWdfdGFibGVbWyJkZXNlcV9hZGpwIl1dIDw9IDAuMDUgJgogIGFicyhiaWdfdGFibGVbWyJkZXNlcV9sb2dmYyJdXSA+PSAxLjApCnN1bW1hcnkocHJldmlvdXNfc2lnX2lkeCkKcHJldmlvdXNfZ2VuZXMgPC0gcm93bmFtZXMoYmlnX3RhYmxlKVtwcmV2aW91c19zaWdfaWR4XQoKbmV3X3NpZ19pZHggPC0gYWJzKGRlc2VxX3RhYmxlW1sibG9nMkZvbGRDaGFuZ2UiXV0pID49IDEuMCAmCiAgZGVzZXFfdGFibGVbWyJwYWRqIl1dIDwgMC4wNQpuZXdfZ2VuZXMgPC0gcm93bmFtZXMoZGVzZXFfdGFibGUpW25ld19zaWdfaWR4XQpuYV9pZHggPC0gaXMubmEobmV3X2dlbmVzKQpuZXdfZ2VuZXMgPC0gbmV3X2dlbmVzWyFuYV9pZHhdCgpWZW5uZXJhYmxlOjpWZW5uKGxpc3QoInByZXZpb3VzIiA9IHByZXZpb3VzX2dlbmVzLCAibmV3IiA9IG5ld19nZW5lcykpCgp0ZXN0X25ldyA8LSBzaW1wbGVfZ3Byb2ZpbGVyKG5ld19nZW5lcykKdGVzdF9uZXcKdGVzdF9vbGQgPC0gc2ltcGxlX2dwcm9maWxlcihwcmV2aW91c19nZW5lcykKdGVzdF9vbGQKCm5ld19hbm5vdGF0ZWQgPC0gbWVyZ2UoZkRhdGEodF9uZXV0cm9waGlscyksIGRlc2VxX3RhYmxlLCBieSA9ICJyb3cubmFtZXMiKQpyb3duYW1lcyhuZXdfYW5ub3RhdGVkKSA8LSBuZXdfYW5ub3RhdGVkW1siUm93Lm5hbWVzIl1dCm5ld19hbm5vdGF0ZWRbWyJSb3cubmFtZXMiXV0gPC0gTlVMTAp3cml0ZV94bHN4KGRhdGEgPSBuZXdfYW5ub3RhdGVkLCBleGNlbCA9ICJleGNlbC9uZXV0cm9waGlsX3Zpc2l0X2luX21vZGVsX3N2YV9jZl9uZXcueGxzeCIpCgpvbGRfYW5ub3RhdGVkIDwtIG1lcmdlKGZEYXRhKHRfbmV1dHJvcGhpbHMpLCBiaWdfdGFibGUsIGJ5ID0gInJvdy5uYW1lcyIpCnJvd25hbWVzKG9sZF9hbm5vdGF0ZWQpIDwtIG9sZF9hbm5vdGF0ZWRbWyJSb3cubmFtZXMiXV0Kb2xkX2Fubm90YXRlZFtbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCndyaXRlX3hsc3goZGF0YSA9IG9sZF9hbm5vdGF0ZWQsIGV4Y2VsID0gImV4Y2VsL25ldXRyb3BoaWxfdmlzaXRfaW5fbW9kZWxfc3ZhX2NmX29sZC54bHN4IikKYGBgCgpPbmNlIGFnYWluLCBzZWUgaG93IG1hbnkgb2Ygb3VyIGZhdm9yaXRlIGdlbmVzIGFyZSBoZXJlCgpgYGB7cn0Kc3VtKG5ld19nZW5lcyAlaW4lIGV4cGVjdGVkX2Vuc2cpCmBgYAoKIyBNaXhlZCBsaW5lYXIgbW9kZWxzCgpXaGVuIHRoZSBhYm92ZSB3b3JrIHdhcyByZXZpZXdlZCBmb3IgcHVibGljYXRpb24sIG9uZSBjb25jZXJuIHJhaXNlZAphcm9zZSBiZWNhdXNlIHdlIGFyZSBub3QgY29uc2lkZXJpbmcgdGhlIHZhcmlhbmNlIG9mIGVhY2ggcGVyc29uIGluCnRoZSBjb250cmFzdHMgYWJvdmUgYW5kIGFyZSBwb3RlbnRpYWxseSBvdmVyLXJlcHJlc2VudGluZyB0aGUKc2lnbmlmaWNhbmNlL3Bvd2VyIG9mIHRoZSByZXN1bHRzIGJlY2F1c2UgdGhlIG1vZGVscyB3ZSBhcmUgdXNpbmcgZG8Kbm90IGluY2x1ZGUgdGhlIGRvbm9yLiAgTXkgcHJldmlvdXMgdW5kZXJzdGFuZGluZyB3YXMgdGhhdCBpdCBpcwpzdWZmaWNpZW50IHRvIGluY2x1ZGUgdmlzaXQgaW4gdGhlIG1vZGVsIGJlY2F1c2UgdGhhdCB3b3VsZCByZXN1bHQgaW4KYSBtb2RlbCBtYXRyaXggd2hpY2ggc2VwYXJhdGVzIHNhbXBsZXMgZnJvbSBlYWNoIHBlcnNvbjsgYnV0IEkgYW0gbm93CnJlYXNvbmFibHkgY2VydGFpbiB0aGlzIGlzIGluY29ycmVjdC4KClRoZXJlZm9yZSwgdGhlIHByZXZpb3VzIGNvdXBsZSBvZiBibG9ja3MgSSBub3cgdGhpbmsgYXJlIG5vdAphcHByb2FjaGluZyB0aGlzIHByb2JsZW0gY29ycmVjdGx5LiBXZSBzcGVudCBzb21lIHRpbWUgdGFsa2luZyB3aXRoCk5lYWwgYW5kIGRpc2N1c3NpbmcgdGhlIHZhcmlvdXMgbW9kZWxzIGFuZCBtZXRob2RzIHdlIGVtcGxveWVkLiAgSGUKbWFkZSBhIHNlcmllcyBvZiBzdWdnZXN0aW9ucyBhYm91dCB3YXlzIHdoaWNoIG1pZ2h0IHByb3ZlIG1vcmUKY29ycmVjdC4gIEl0IHNlZW1zIHRoYXQgYSBtaXhlZCBsaW5lYXIgbW9kZWwgaXMgdGhlIG1vc3QgYXBwcm9wcmlhdGUKbWV0aG9kIGZvciB0aGlzIHR5cGUgb2YgcXVlcnkuICBJIHRoaW5rIEkgY2FuIHBlcmZvcm0gdGhhdCB3aXRoIGxpbW1hLAp2aWEgdm9vbS4gTGV0IHVzIHRyeSBhbmQgc2VlIHdoYXQgaGFwcGVucy4gIEFmdGVyIGRvaW5nIHNvbWUgcmVhZGluZywKSSB0aGluayB0aGUgbW9zdCBhcHByb3ByaWF0ZSB3YXkgdG8gcGVyZm9ybSB0aGlzIGlzIHRvIHVzZSBkcmVhbSgpIGZyb20KdmFyaWFuY2VQYXJpdGlvbiwgd2hpY2ggaXMgY29vbCBiZWNhdXNlIEkgcmVhbGx5IGxpa2UgaXQuCgpBcyBJIHdyaXRlIHRoaXMsIHdlIGFyZSByZWFzb25hYmx5IGNlcnRhaW4gdGhhdCBhIG1peGVkCmxpbmVhciBtb2RlbCBwcm92aWRlcyBhIHN0YXRpc3RpY2FsbHkgY29ycmVjdCBmcmFtZXdvcmsgZm9yCnJlcHJlc2VudGluZyBvdXIgZXhwcmVzc2lvbiBkYXRhIGFzIGEgZnVuY3Rpb24gb2YgZmluYWxvdXRjb21lLCB2aXNpdCwKYW5kIHBlcnNvbiwgZS5nOgoKZXhwcnMgfiBmaW5hbG91dGNvbWUgKyB2aXNpdCArICgxfGRvbm9yKQoKSW4gb3VyIGRpc2N1c3Npb25zIHN1cnJvdW5kaW5nIHRoZSB2YXJpb3VzIHdheXMgdG8gY29tcGFyZS9jb250cmFzdAp0aGUgdmFyaW91cyByZXN1bHRzIHdpdGgvb3V0IHRoZSBtaXhlZCBsaW5lYXIgbW9kZWw7IHRoZXJlIHdlcmUgYSBmZXcKcHJpbWFyeSBnb2FscyBsYWlkIG91dCBieSBNYXJpYSBBZGVsYWlkYSBhbmQgTmVhbC4gIFRoZSBnb2FsIGlzIHRvCm9ic2VydmUgaWYvaG93IHdlbGwgb3VyIHByZXZpb3VzIGFuYWx5c2VzIGFncmVlIHdpdGggcmVzdWx0cyBvYnRhaW5lZAp1c2luZyBhIG1peGVkIGxpbmVhciBtb2RlbC4gIFRoZXJlIGFyZSBhIGNvdXBsZSBvZiBjYXZlYXRzOgoKMS4gIFRoZSBsbW0gaXMgbm90IGF2YWlsYWJsZSBmb3IgZGF0YSBpbiBhIG5lZ2F0aXZlIGJpbm9taWFsCiAgICBkaXN0cmlidXRpb24uICBFcmdvLCBERVNlcTIvRWRnZVIgYXJlIG91dCBhIHByaW9yaS4gIFRoaXMgaXMgYQogICAgbGl0dGxlIHNhZCBiZWNhdXNlIHdlIGhhdmUgZ2VuZXJhbGx5IHJlbGllZCB1cG9uIERFU2VxMiByZXN1bHRzLgogICAgSG93ZXZlciwgSSBkbyByb3V0aW5lbHkgY29tcGFyZSBERVNlcTIgdG8gdm9vbS0+bGltbWEgYW5kIGFtCiAgICB1c3VhbGx5IGltcHJlc3NlZCBhdCB0aGUgZGVncmVlIG9mIHNpbWlsYXJpdHkuCjIuICBsbW0gYW5hbHlzZXMgYXJlIHNpZ25pZmljYW50bHkgbW9yZSBjb21wdXRhdGlvbmFsbHkgZXhwZW5zaXZlLgogICAgV2hlbiBJIGhhdmUgcGxheWVkIHdpdGggdGhlbSB2aWEgdmFyaWFuY2VQYXJ0aXRpb24gaW4gdGhlIHBhc3QgSQogICAgaGF2ZSBydW4gbXkgdmVyeSBuaWNlIG1hY2hpbmUgT29NIG9uIG1vcmUgdGhhbiBhIGZldyBvY2Nhc2lvbnMuCiAgICBUaGlzIGlzIGltcG9ydGFudCwgYmVjYXVzZSBJIHdhbnQgdG8gaGF2ZSBldmVyeXRoaW5nIGluIG15CiAgICBjb250YWluZXIsIGJ1dCBJIGNhbm5vdCBleHBlY3QgYW55IGVsc2UncyBjb21wdXRlciB0byBoYXZlID4gMjAwRwogICAgUkFNLiAgSSBjYW4gZGVmaW5pdGVseSBsb3dlciB0aGUgcGFyYWxsZWwgcHJvY2Vzc2luZyByZXF1aXJlbWVudHMKICAgIHRvIHNhdmUgbWVtb3J5LCBidXQgdGhlbiB0aGVzZSB3aWxsIHRha2UgZm9yZXZlciAod2VsbCwgcHJvYmFibHkgYQogICAgY291cGxlIGRheXMgdG8gYSB3ZWVrKS4KClNvLCB3aXRoIHRoYXQgaW4gbWluZCwgTWFyaWEgQWRlbGFpZGEsIE5hamliLCBhbmQgTmVhbCBmb2N1c2VkIG9uCnJlcGVhdGluZyBhIHVzZWZ1bCBzdWJzZXQgb2YgdGhlIGFuYWx5c2VzIHVzaW5nIHRoZSBsbW0gYW5kIGNvbXBhcmluZwp0aGVtIHRvIG91ciBleHRhbnQgcmVzdWx0cyByYXRoZXIgdGhhbiByZS1pbXBsZW1lbnRpbmcgZXZlcnl0aGluZy4KVGhlIGZvbGxvd2luZyBhcmUgdGhlIHRoaW5ncyB0aGV5IHN1Z2dlc3RlZCBhcmUgdGhlIG1vc3QgaW1wb3J0YW50CmNvbXBhcmlzb24gcG9pbnRzOgoKMS4gIFJlcGVhdCB0aGlzIHByb2Nlc3MsIGNsZWFuIGl0IHVwIGZvcjogbW9ub2N5dGVzL25ldXRyb3BoaWxzCjIuICBDb21wYXJlIHRoZSByZXN1bHRzIHdoZW4gdXNpbmcgbW9kZWxzIHdoaWNoIGFyZSAobm90ZSB0aGF0IHRoaXMKICAgIHdheSBvZiB3cml0aW5nIGZpeGVzIHRoZSBzbG9wZSBvZiBlYWNoIGRvbm9yJ3MgbW9kZWwgYnV0IGFsbG93cwogICAgdGhlIGludGVyY2VwdCB0byBjaGFuZ2UpOgogICAgYS4gIH4gZmluYWxvdXRjb21lICsgdmlzaXRudW1iZXIgKyAoMXxkb25vcikKICAgIGIuICB+IGZpbmFsb3V0Y29tZSArIHZpc2l0bnVtYmVyCiAgICBjLiAgfiBmaW5hbG91dGNvbWUgKyAoMXxkb25vcikKMy4gIENvbXBhcmUgdGhlIHJlc3VsdHMgZnJvbSBsaW1tYSBmb3IgYSxiLGMgKHJlYWxseSwgdGhleSBhc2tlZCBtZSB0bwogICAgb25seSBmb2N1cyBvbiBhLGI7IEkgd2FudGVkIHRvIGNvbXBhcmUgYyBhcyB3ZWxsKQo0LiAgRXh0cmFjdCB0aGUgc2V0IG9mICdzaWduaWZpY2FudCcgZ2VuZXMgdmlhIGxvZ0ZDL3B2YWx1ZSBmb3IgYWxsIG9mCiAgICB0aGUgYWJvdmUgYW5kIHNlZSB0aGUgc2hhcmVkL3VuaXF1ZSBnZW5lcy4KCkkgaGF2ZSBhbHJlYWR5IHdyaXR0ZW4gYSBza2VsZXRvbiBmdW5jdGlvbiAnZHJlYW1fcGFpcndpc2UoKScgYXMgYQpzaWJsaW5nIHRvIG15IG90aGVyICpfcGFpcndpc2UoKSBmdW5jdGlvbnMuICBJIHRoaW5rIHRoYXQgd2l0aCBzb21lCm1pbm9yIG1vZGlmaWNhdGlvbnMgKG9yIG1heWJlIG5vbmUgYXQgYWxsLCB3aGVuIEkgd3JvdGUgaXQgSSB3YXMKdGhpbmtpbmcgYWJvdXQgZnVuIG1vZGVscyB0aGF0IHZhcmlhbmNlUGFydGl0aW9uIHN1cHBvcnRzKSBpdCBjYW4KYWNjZXB0IHRoZSBtaXhlZCBsaW5lYXIgbW9kZWwgb2YgaW50ZXJlc3QuCgojIyBVc2luZyBhIG1peGVkIGxpbmVhciBtb2RlbCB3aXRoIGRyZWFtCgpJbiB0aGUgZm9sbG93aW5nIGJsb2NrLCB0aGUgbWl4ZWQgZm9ybXVsYSB3aWxsIGdldCBwYXNzZWQgdG8gZHJlYW0uICBJCnNldCB0aGUgY29kZSB0byB1c2UgdGhlIGZpcnN0IGVsZW1lbnQgKGFmdGVyIHRoZSBpbnRlcmNlcHQpIGFzIHRoZQonY29uZGl0aW9uJyBmYWN0b3IuICBUaHVzIGlmIEkgaGFkIG1hZGUgdGhlIG1vZGVsICd+IDAgKyB2aXNpdG51bWJlciArCmZpbmFsb3V0Y29tZSArICgxfGRvbm9yKScsIGl0IHdvdWxkIGNvbXBhcmUgdmlzaXRzLgoKVGhlIGRyZWFtX3BhaXJ3aXNlKCkgZnVuY3Rpb24gaXMgcmVzcG9uc2libGUgZm9yIG1ha2luZyBzdXJlIHRoZQp2YXJpYW5jZVBhcnRpdGlvbiByZXBsYWNlbWVudCBmdW5jdGlvbnMgYXJlIHVzZWQgZm9yIHRoaW5ncyBsaWtlIHZvb20sCmxtZml0LCBlYmF5ZXMsIGFuZCB0b3B0YWJsZS4gIFN0cmFuZ2VseSwgc29tZSBvZiB0aGVtIHdpbGwKYXV0b21hdGljYWxseSBmYWxsIGJhY2sgdG8gbGltbWEncyBmdW5jdGlvbnMgaWYgdGhlcmUgaXMgbm8KcmFuZG9tLWVmZmVjdCBpbiB0aGUgbW9kZWwsIGJ1dCBvdGhlcnMgd2lsbCBub3QuICBBcyBhIHJlc3VsdCwgSSBoYXZlCmEgY2hlY2sgYW5kIGludm9rZSB0aGUgYXBwcm9wcmlhdGUgZnVuY3Rpb25zIGV4cGxpY2l0bHkgaW4KZHJlYW1fcGFpcndpc2UoKS4KCmBgYHtyfQptaXhlZF9mc3RyaW5nIDwtICJ+IDAgKyBmaW5hbG91dGNvbWUgKyB2aXNpdG51bWJlciArICgxfGRvbm9yKSIKbWl4ZWRfZm9ybSA8LSBhcy5mb3JtdWxhKG1peGVkX2ZzdHJpbmcpCmdldF9mb3JtdWxhX2ZhY3RvcnMobWl4ZWRfZm9ybSkKbWl4ZWRfZW9zaW5vcGhpbF9kZSA8LSBkcmVhbV9wYWlyd2lzZSh0X2Vvc2lub3BoaWxzLCBhbHRfbW9kZWwgPSBtaXhlZF9mb3JtKQptaXhlZF9lb3Npbm9waGlsX2RlX3hsc3ggPC0gd3JpdGVfZGVfdGFibGUoCiAgbWl4ZWRfZW9zaW5vcGhpbF9kZSwgdHlwZSA9ICJsaW1tYSIsCiAgZXhjZWwgPSBnbHVlKCJleGNlbC9taXhlZF9lb3Npbm9waGlsX3RhYmxlLXZ7dmVyfS54bHN4IikpCgptaXhlZF9tb25vY3l0ZV9kZSA8LSBkcmVhbV9wYWlyd2lzZSh0X21vbm9jeXRlcywgYWx0X21vZGVsID0gbWl4ZWRfZm9ybSkKbWl4ZWRfbW9ub2N5dGVfZGVfeGxzeCA8LSB3cml0ZV9kZV90YWJsZSgKICBtaXhlZF9tb25vY3l0ZV9kZSwgdHlwZSA9ICJsaW1tYSIsCiAgZXhjZWwgPSBnbHVlKCJleGNlbC9taXhlZF9tb25vY3l0ZV90YWJsZS12e3Zlcn0ueGxzeCIpKQoKbWl4ZWRfbmV1dHJvcGhpbF9kZSA8LSBkcmVhbV9wYWlyd2lzZSh0X25ldXRyb3BoaWxzLCBhbHRfbW9kZWwgPSBtaXhlZF9mb3JtKQptaXhlZF9uZXV0cm9waGlsX2RlX3hsc3ggPC0gd3JpdGVfZGVfdGFibGUoCiAgbWl4ZWRfbmV1dHJvcGhpbF9kZSwgdHlwZSA9ICJsaW1tYSIsCiAgZXhjZWwgPSBnbHVlKCJleGNlbC9taXhlZF9uZXV0cm9waGlsX3RhYmxlLXZ7dmVyfS54bHN4IikpCmBgYAoKIyMgVXNpbmcgdGhlIHNhbWUgbWV0aG9kIHdpdGhvdXQgdGhlIG1peGVkIG1vZGVsCgpJbiBvdGhlciB3b3JkcywgdGhlIGZvbGxvd2luZyBpbnZvY2F0aW9ucyB3aWxsIGdvIF9tdWNoXyBmYXN0ZXIgYW5kCmxpa2VseSBiZSBuZWFybHkgKG9yIGNvbXBsZXRlbHkpIGlkZW50aWNhbCB0byB0aGUgcmVzdWx0cyBmcm9tIGxpbW1hCnVzaW5nIHRoZSBzYW1lIG1vZGVsIHNpbmNlIHRoZSAnbWl4ZWRfZnN0cmluZ19mdicgZG9lcyBub3QgaGF2ZSBhCnJhbmRvbSBlZmZlY3QuCgpgYGB7cn0KbWl4ZWRfZnN0cmluZ19mdiA8LSAifiAwICsgZmluYWxvdXRjb21lICsgdmlzaXRudW1iZXIiCm1peGVkX2Zvcm1fZnYgPC0gYXMuZm9ybXVsYShtaXhlZF9mc3RyaW5nX2Z2KQpnZXRfZm9ybXVsYV9mYWN0b3JzKG1peGVkX2Zvcm1fZnYpCm1peGVkX2Vvc2lub3BoaWxfZnZfZGUgPC0gZHJlYW1fcGFpcndpc2UodF9lb3Npbm9waGlscywgYWx0X21vZGVsID0gbWl4ZWRfZm9ybV9mdikKbWl4ZWRfZW9zaW5vcGhpbF9kZV9ub2Rvbm9yX3hsc3ggPC0gd3JpdGVfZGVfdGFibGUoCiAgbWl4ZWRfZW9zaW5vcGhpbF9mdl9kZSwgdHlwZSA9ICJsaW1tYSIsCiAgZXhjZWwgPSBnbHVlKCJleGNlbC9taXhlZF9lb3Npbm9waGlsX25vZG9ub3JfdGFibGUtdnt2ZXJ9Lnhsc3giKSkKCm1peGVkX21vbm9jeXRlX2Z2X2RlIDwtIGRyZWFtX3BhaXJ3aXNlKHRfbW9ub2N5dGVzLCBhbHRfbW9kZWwgPSBtaXhlZF9mb3JtX2Z2KQptaXhlZF9tb25vY3l0ZV9kZV9ub2Rvbm9yX3hsc3ggPC0gd3JpdGVfZGVfdGFibGUobWl4ZWRfbW9ub2N5dGVfZnZfZGUsIHR5cGUgPSAibGltbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjZWwgPSBnbHVlKCJleGNlbC9taXhlZF9tb25vY3l0ZV9ub2Rvbm9yX3RhYmxlLXZ7dmVyfS54bHN4IikpCgptaXhlZF9uZXV0cm9waGlsX2Z2X2RlIDwtIGRyZWFtX3BhaXJ3aXNlKHRfbmV1dHJvcGhpbHMsIGFsdF9tb2RlbCA9IG1peGVkX2Zvcm1fZnYpCm1peGVkX25ldXRyb3BoaWxfZGVfbm9kb25vcl94bHN4IDwtIHdyaXRlX2RlX3RhYmxlKG1peGVkX25ldXRyb3BoaWxfZnZfZGUsIHR5cGUgPSAibGltbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleGNlbCA9IGdsdWUoImV4Y2VsL21peGVkX25ldXRyb3BoaWxfbm9kb25vcl90YWJsZS12e3Zlcn0ueGxzeCIpKQpgYGAKCiMjIENvbXBhcmluZyB0aGUgcmVzdWx0cwoKVGhlcmUgYXJlIGEgY291cGxlIG9ic2VydmF0aW9ucyBoZXJlIHdoaWNoIGFyZSBpbXBvcnRhbnQgYW5kL29yCnRyb3VibGluZzoKCjEuICBVc2luZyB0aGUgbG1tIHJlc3VsdHMgaW4gbm8gZ2VuZXMgd2l0aCBhIHNpZ25pZmljYW50IEZEUiBhZGp1c3RlZAogICAgcC12YWx1ZS4gIFRoaXMgc3VwcG9ydHMgdGhlIGh5cG90aGVzaXMgdGhhdCB3ZSBvdmVyLXJlcHJlc2VudGVkCiAgICB0aGUgc2lnbmlmaWNhbmNlIG9mIHRoZSBkYXRhIGluIG91ciBvcmlnaW5hbCBhbmFseXNpcyBJIHRoaW5rIGluIGEKICAgIHByZXR0eSBjb21wZWxsaW5nIGZhc2hpb24uCjIuICBIb3dldmVyLCB0aGVyZSBpcyB0aGlzIGludGVyZXN0aW5nIG5vdGUgZnJvbSB0aGUgZHJlYW0KICAgIGRvY3VtZW50YXRpb246ICAiU2luY2UgZHJlYW0gdXNlcyBhbiBlc3RpbWF0ZWQgZGVncmVlcyBvZiBmcmVlZG9tCiAgICB2YWx1ZSBmb3IgZWFjaCBoeXBvdGhlc2lzIHRlc3QsIHRoZSBkZWdyZWVzIG9mIGZyZWVkb20gaXMKICAgIGRpZmZlcmVudCBmb3IgZWFjaCBnZW5lIGhlcmUuIFRoZXJlZm9yZSwgdGhlIHQtc3RhdGlzdGljcyBhcmUgbm90CiAgICBkaXJlY3RseSBjb21wYXJhYmxlIHNpbmNlIHRoZXkgaGF2ZSBkaWZmZXJlbnQgZGVncmVlcyBvZgogICAgZnJlZWRvbS4gSW4gb3JkZXIgdG8gYmUgYWJsZSB0byBjb21wYXJlIHRlc3Qgc3RhdGlzdGljcywgd2UgcmVwb3J0CiAgICB6LnN0ZCB3aGljaCBpcyB0aGUgcC12YWx1ZSB0cmFuc2Zvcm1lZCBpbnRvIGEgc2lnbmVkIHotc2NvcmUuCiAgICBUaGlzIGNhbiBiZSB1c2VkIGZvciBkb3duc3RyZWFtIGFuYWx5c2lzLiIKMy4gIEkgc3BlbnQgc29tZSB0aW1lIHJlYWRpbmcgdGhlIFIgbWFya2Rvd24gZG9jdW1lbnRzIGF0CiAgICBodHRwczovL2dpdGh1Yi5jb20vR2FicmllbEhvZmZtYW4vZHJlYW1fYW5hbHlzaXMuZ2l0IHdoaWNoCiAgICBhY2NvbXBhbnkgdGhlIHBhcGVyIChAaG9mZm1hbkRyZWFtUG93ZXJmdWxEaWZmZXJlbnRpYWwyMDIxKSBhbmQKICAgIGZvdW5kIHRoYXQgdGhlcmUgaXMgb25seSBvbmUgaW5zdGFuY2UgaW4gd2hpY2ggdGhleSBtYWtlIHVzZSBvZgogICAgYWRqdXN0ZWQgcC12YWx1ZXM7IGF0IHRoZSB2ZXJ5IGVuZCBvZiB0aGUgaVBTQyBkYXRhLiAgSW4gYWRkaXRpb24KICAgIHRoZXkgb25seSB1c2UgdGhlIHpzdGQgbWV0cmljIHdoZW4gcHVsbGluZyBnZW5lIHNldHMgZm9yIGNvbXBhcmluZwogICAgYWdhaW5zdCBHTyBjYXRlZ29yaWVzLiAgSW4gYWxsIG90aGVyIGluc3RhbmNlcywgdGhlIG1ldHJpYyB1c2VkCiAgICBmb3Igc2lnbmlmaWNhbmNlIGlzIHRoZSAncmF3JyBwLXZhbHVlLgoKIyMjIEEgbGl0dGxlIGZ1bmN0aW9uIHRvIHByaW50IG92ZXJsYXBzCgpOYWppYiBhc2tlZCBpZiBJIHdvdWxkIGNvbXBhcmUgdGhlIHNldCBvZiBvdmVybGFwcGluZyBnZW5lcyBvYnNlcnZlZAp3aXRoIHRoZSB2YXJpb3VzIHNpZ25pZmljYW5jZSBtZXRyaWNzIHByb3ZpZGVkLiAgSSB0aGluayBJIHNob3VsZAp3cml0ZSBhIGxpdHRsZSBmdW5jdGlvbiB0byBkbyB0aGlzIGJlY2F1c2UgdGhlcmUgYXJlIGFtcGxlCm9wcG9ydHVuaXRpZXMgZm9yIHR5cGVvcy4KCmBgYHtyfQpkZXNlcV9kZiA8LSB0X2NmX21vbm9jeXRlX3RhYmxlX3N2YVtbImRhdGEiXV1bWyJvdXRjb21lIl1dCmRlc2VxX2dlbmVfaWR4IDwtIGFicyhkZXNlcV9kZltbImRlc2VxX2xvZ2ZjIl1dKSA+PSAxLjAgJgogIGRlc2VxX2RmW1siZGVzZXFfYWRqcCJdXSA8PSAwLjA1CmRlc2VxX3N5bWIgPC0gYW5ub3RbZGVzZXFfZ2VuZV9pZHgsICJoZ25jX3N5bWJvbCJdCmRlc2VxX3N5bWIKZGVzZXFfZ2VuZXMgPC0gcm93bmFtZXMoYW5ub3QpW2Rlc2VxX2dlbmVfaWR4XQoKb3ZlcmxhcF9zaWcgPC0gZnVuY3Rpb24obWl4ZWQsIGRlc2VxID0gZGVzZXFfZ2VuZXMsIG1peGVkX3Bjb2wgPSAiUC5WYWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgIGFubm90ID0gZkRhdGEodF9tb25vY3l0ZXMpLCBtaXhlZF9jdXRvZmYgPSAwLjA1LCBkaXJlY3Rpb24gPSAibHQiLAogICAgICAgICAgICAgICAgICAgICAgICBleHBlY3RlZCA9IGV4cGVjdGVkX2dlbmVzKSB7CiAgaWYgKGRpcmVjdGlvbiA9PSAibHQiKSB7CiAgICBtaXhlZF9zaWdfaWR4IDwtIGFicyhtaXhlZFtbImxvZ0ZDIl1dKSA+PSAxLjAgJgogICAgICBtaXhlZFtbbWl4ZWRfcGNvbF1dIDw9IG1peGVkX2N1dG9mZgogIH0gZWxzZSB7CiAgICBtaXhlZF9zaWdfaWR4IDwtIGFicyhtaXhlZFtbImxvZ0ZDIl1dKSA+PSAxLjAgJgogICAgICBtaXhlZFtbbWl4ZWRfcGNvbF1dID49IG1peGVkX2N1dG9mZgogIH0KICBtaXhlZF9nZW5lcyA8LSByb3duYW1lcyhtaXhlZClbbWl4ZWRfc2lnX2lkeF0KICB2ZW5uX2xzdCA8LSBsaXN0KAogICAgIm1peGVkX21vZGVsIiA9IG1peGVkX2dlbmVzLAogICAgIkRFU2VxX3N2YSIgPSBkZXNlcSkKICBtaXhlZF9kZXNlcV9jb21wIDwtIFZlbm5lcmFibGU6OlZlbm4odmVubl9sc3QpCiAgVmVubmVyYWJsZTo6cGxvdChtaXhlZF9kZXNlcV9jb21wKQogIG1peGVkX2Vuc2cgPC0gbWl4ZWRfZGVzZXFfY29tcEBJbnRlcnNlY3Rpb25TZXRzW1siMTEiXV0KICBvdmVybGFwX2dlbmVzIDwtIGFubm90W21peGVkX2Vuc2csICJoZ25jX3N5bWJvbCJdCiAgbWVzc2FnZSgiVGhlIHNldCBvZiBhbGwgb3ZlcmxhcHBpbmcgZ2VuZXM6IikKICBwcmludChvdmVybGFwX2dlbmVzKQogIGZvdW5kX2lkeCA8LSBleHBlY3RlZCAlaW4lIG92ZXJsYXBfZ2VuZXMKICBtZXNzYWdlKCJPdmVybGFwcGluZyBnZW5lcyBpbiB0aGUgMTAgZmF2b3JpdGVzOiIpCiAgcHJpbnQoZXhwZWN0ZWRbZm91bmRfaWR4XSkKfQpgYGAKCiMjIyBNb25vY3l0ZXMKCkluIHRoaXMgYmxvY2sgSSBhbSBsb29raW5nIGF0IHRoZSBzaW1pbGFyaXRpZXMgYmV0d2VlbiB0aGUgbWl4ZWQgbW9kZWwKd2l0aCBkb25vciBhbmQgd2l0aG91dCBkb25vciAod2hpY2ggaXMgbm8gbG9uZ2VyIGEgbWl4ZWQgbW9kZWw7IGl0IGlzCmp1c3QgdXNpbmcgdGhlIGRyZWFtIGZ1bmN0aW9ucyAod2hpY2ggSSBhbSBwcmV0dHkgc3VyZSBqdXN0IGZhbGwgYmFjawp0byBsaW1tYSB3aGVuIHRoZXJlIGlzIG5vdCBhIHJhbmRvbSBlZmZlY3QpKS4KCmBgYHtyfQptb25vY3l0ZV92aXNpdF93aXRoX2Rvbm9yIDwtIG1peGVkX21vbm9jeXRlX2RlW1siYWxsX3RhYmxlcyJdXVtbImZhaWx1cmVfdnNfY3VyZSJdXQptb25vY3l0ZV92aXNpdF93aXRob3V0X2Rvbm9yIDwtIG1peGVkX21vbm9jeXRlX2Z2X2RlW1siYWxsX3RhYmxlcyJdXVtbImZhaWx1cmVfdnNfY3VyZSJdXQpkb25vcl9hdWNjIDwtIGNhbGN1bGF0ZV9hdWNjKG1vbm9jeXRlX3Zpc2l0X3dpdGhfZG9ub3IsIG1vbm9jeXRlX3Zpc2l0X3dpdGhvdXRfZG9ub3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHggPSAiYWRqLlAuVmFsIiwgcHkgPSAiYWRqLlAuVmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBseCA9ICJsb2dGQyIsIGx5ID0gImxvZ0ZDIikKZG9ub3JfYXVjYwoKd2l0aF9kb25vcl9nZW5lcyA8LSBhYnMobW9ub2N5dGVfdmlzaXRfd2l0aF9kb25vcltbImxvZ0ZDIl1dKSA+PSAxLjAgJgogIG1vbm9jeXRlX3Zpc2l0X3dpdGhfZG9ub3JbWyJQLlZhbHVlIl1dIDw9IDAuMDUKd2l0aG91dF9kb25vcl9nZW5lcyA8LSBhYnMobW9ub2N5dGVfdmlzaXRfd2l0aG91dF9kb25vcltbImxvZ0ZDIl1dKSA+PSAxLjAgJgogIG1vbm9jeXRlX3Zpc2l0X3dpdGhfZG9ub3JbWyJQLlZhbHVlIl1dIDw9IDAuMDUKZG9ub3JfZ2VuZXMgPC0gcm93bmFtZXMobW9ub2N5dGVfdmlzaXRfd2l0aF9kb25vcilbd2l0aF9kb25vcl9nZW5lc10KZG9ub3Jfel9pZHggPC0gYWJzKG1vbm9jeXRlX3Zpc2l0X3dpdGhfZG9ub3JbWyJsb2dGQyJdXSkgPj0gMS4wICYKICBtb25vY3l0ZV92aXNpdF93aXRoX2Rvbm9yW1siei5zdGQiXV0gPj0gMS4wCmRvbm9yX3pfZ2VuZXMgPC0gcm93bmFtZXMobW9ub2N5dGVfdmlzaXRfd2l0aF9kb25vcilbZG9ub3Jfel9pZHhdCgpvdmVybGFwX3NpZyhtb25vY3l0ZV92aXNpdF93aXRoX2Rvbm9yKQpvdmVybGFwX3NpZyhtb25vY3l0ZV92aXNpdF93aXRoX2Rvbm9yLAogICAgICAgICAgICBtaXhlZF9wY29sID0gInouc3RkIiwgZGlyZWN0aW9uID0gImd0IiwgbWl4ZWRfY3V0b2ZmID0gMS41KQpgYGAKCkkgd291bGQgaGF2ZSBzd29ybiB0aGF0IHRoZSAyLjAgei1zY29yZSBzZXQgd2FzIG11Y2ggbGFyZ2VyIHRoYW4gdGhlCnAtdmFsdWUgc2V0IGFuZCBpbmNsdWRlZCBhbGwgb2YgdGhlIDEwIGdlbmVzLiAgQXBwYXJlbnRseSBJIHdhcyB2ZXJ5Cndyb25nLgoKIyMjIE5ldXRyb3BoaWxzCgpOb3cgZXhhbWluZSB0aGUgdmFyaW91cyBtb2RlbHMgZm9yIHRoZSBuZXV0cm9waGlsIHNhbXBsZXMuCgpgYGB7cn0KbmV1dHJvcGhpbF92aXNpdF93aXRoX2Rvbm9yIDwtIG1peGVkX25ldXRyb3BoaWxfZGVbWyJhbGxfdGFibGVzIl1dW1siZmFpbHVyZV92c19jdXJlIl1dCm5ldXRyb3BoaWxfdmlzaXRfd2l0aG91dF9kb25vciA8LSBtaXhlZF9uZXV0cm9waGlsX2Z2X2RlW1siYWxsX3RhYmxlcyJdXVtbImZhaWx1cmVfdnNfY3VyZSJdXQpkb25vcl9hdWNjIDwtIGNhbGN1bGF0ZV9hdWNjKG5ldXRyb3BoaWxfdmlzaXRfd2l0aF9kb25vciwgbmV1dHJvcGhpbF92aXNpdF93aXRob3V0X2Rvbm9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB4ID0gImFkai5QLlZhbCIsIHB5ID0gImFkai5QLlZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbHggPSAibG9nRkMiLCBseSA9ICJsb2dGQyIpCmRvbm9yX2F1Y2MKd2l0aF9kb25vcl9nZW5lcyA8LSBhYnMobmV1dHJvcGhpbF92aXNpdF93aXRoX2Rvbm9yW1sibG9nRkMiXV0pID49IDEuMCAmCiAgbmV1dHJvcGhpbF92aXNpdF93aXRoX2Rvbm9yW1siUC5WYWx1ZSJdXSA8PSAwLjA1CndpdGhvdXRfZG9ub3JfZ2VuZXMgPC0gYWJzKG5ldXRyb3BoaWxfdmlzaXRfd2l0aG91dF9kb25vcltbImxvZ0ZDIl1dKSA+PSAxLjAgJgogIG5ldXRyb3BoaWxfdmlzaXRfd2l0aF9kb25vcltbIlAuVmFsdWUiXV0gPD0gMC4wNQpkb25vcl9nZW5lcyA8LSByb3duYW1lcyhuZXV0cm9waGlsX3Zpc2l0X3dpdGhfZG9ub3IpW3dpdGhfZG9ub3JfZ2VuZXNdCnZpc2l0X2dlbmVzIDwtIHJvd25hbWVzKG5ldXRyb3BoaWxfdmlzaXRfd2l0aF9kb25vcilbd2l0aG91dF9kb25vcl9nZW5lc10KdmVubl9sc3QgPC0gbGlzdCgKICAid2l0aF9kb25vciIgPSBkb25vcl9nZW5lcywKICAid2l0aF92aXNpdCIgPSB2aXNpdF9nZW5lcykKVmVubmVyYWJsZTo6VmVubih2ZW5uX2xzdCkKCm92ZXJsYXBfc2lnKG5ldXRyb3BoaWxfdmlzaXRfd2l0aF9kb25vcikKb3ZlcmxhcF9zaWcobmV1dHJvcGhpbF92aXNpdF93aXRoX2Rvbm9yLAogICAgICAgICAgICBtaXhlZF9wY29sID0gInouc3RkIiwgZGlyZWN0aW9uID0gImd0IiwgbWl4ZWRfY3V0b2ZmID0gMS41KQpgYGAKCiMjIyBFb3Npbm9waGlscwoKRmluYWxseSwgY29tcGFyZSBmb3IgdGhlIGVvc2lub3BoaWwgc2FtcGxlcy4KCmBgYHtyfQplb3Npbm9waGlsX3Zpc2l0X3dpdGhfZG9ub3IgPC0gbWl4ZWRfZW9zaW5vcGhpbF9kZVtbImFsbF90YWJsZXMiXV1bWyJmYWlsdXJlX3ZzX2N1cmUiXV0KZW9zaW5vcGhpbF92aXNpdF93aXRob3V0X2Rvbm9yIDwtIG1peGVkX2Vvc2lub3BoaWxfZnZfZGVbWyJhbGxfdGFibGVzIl1dW1siZmFpbHVyZV92c19jdXJlIl1dCmRvbm9yX2F1Y2MgPC0gY2FsY3VsYXRlX2F1Y2MoZW9zaW5vcGhpbF92aXNpdF93aXRoX2Rvbm9yLCBlb3Npbm9waGlsX3Zpc2l0X3dpdGhvdXRfZG9ub3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHggPSAiYWRqLlAuVmFsIiwgcHkgPSAiYWRqLlAuVmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBseCA9ICJsb2dGQyIsIGx5ID0gImxvZ0ZDIikKZG9ub3JfYXVjYwp3aXRoX2Rvbm9yX2dlbmVzIDwtIGFicyhlb3Npbm9waGlsX3Zpc2l0X3dpdGhfZG9ub3JbWyJsb2dGQyJdXSkgPj0gMS4wICYKICBlb3Npbm9waGlsX3Zpc2l0X3dpdGhfZG9ub3JbWyJQLlZhbHVlIl1dIDw9IDAuMDUKd2l0aG91dF9kb25vcl9nZW5lcyA8LSBhYnMoZW9zaW5vcGhpbF92aXNpdF93aXRob3V0X2Rvbm9yW1sibG9nRkMiXV0pID49IDEuMCAmCiAgZW9zaW5vcGhpbF92aXNpdF93aXRoX2Rvbm9yW1siUC5WYWx1ZSJdXSA8PSAwLjA1CmRvbm9yX2dlbmVzIDwtIHJvd25hbWVzKGVvc2lub3BoaWxfdmlzaXRfd2l0aF9kb25vcilbd2l0aF9kb25vcl9nZW5lc10KdmlzaXRfZ2VuZXMgPC0gcm93bmFtZXMoZW9zaW5vcGhpbF92aXNpdF93aXRoX2Rvbm9yKVt3aXRob3V0X2Rvbm9yX2dlbmVzXQp2ZW5uX2xzdCA8LSBsaXN0KAogICJ3aXRoX2Rvbm9yIiA9IGRvbm9yX2dlbmVzLAogICJ3aXRoX3Zpc2l0IiA9IHZpc2l0X2dlbmVzKQpWZW5uZXJhYmxlOjpWZW5uKHZlbm5fbHN0KQoKb3ZlcmxhcF9zaWcoZW9zaW5vcGhpbF92aXNpdF93aXRoX2Rvbm9yKQpvdmVybGFwX3NpZyhlb3Npbm9waGlsX3Zpc2l0X3dpdGhfZG9ub3IsCiAgICAgICAgICAgIG1peGVkX3Bjb2wgPSAiei5zdGQiLCBkaXJlY3Rpb24gPSAiZ3QiLCBtaXhlZF9jdXRvZmYgPSAxLjUpCmBgYAoKQ29tcGFyZSBiYWNrIHRvIGRlc2VxIHdpdGggU1ZBIGFuZCB3aXRoIFNWQSt2aXNpdCBhbmQgc2VlIGhvdyB0aGV5Cmxvb2sgd2l0aCByZXNwZWN0IHRvIHRoZSBkcmVhbSBpbnZvY2F0aW9uIHdpdGhvdXQgdGhlIHJhbmRvbSBkb25vcgplZmZlY3QuCgpgYGB7cn0KZGVzZXFfYXVjYyA8LSBjYWxjdWxhdGVfYXVjYyhtZXJnZWQsIG1vbm9jeXRlX3Zpc2l0X3dpdGhvdXRfZG9ub3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHggPSAiZGVzZXFfYWRqcCIsIHB5ID0gIlAuVmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGx4ID0gImRlc2VxX2xvZ2ZjIiwgbHkgPSAibG9nRkMiKQpkZXNlcV9hdWNjCgpkZXNlcV9nZW5lc19pZHggPC0gYWJzKG1lcmdlZFtbImRlc2VxX2xvZ2ZjIl1dKSA+PSAxLjAgJgogIG1lcmdlZFtbImRlc2VxX2FkanAiXV0gPD0gMC4wNQp3aXRob3V0X2Rvbm9yX2dlbmVzX2lkeCA8LSBhYnMobW9ub2N5dGVfdmlzaXRfd2l0aG91dF9kb25vcltbImxvZ0ZDIl1dKSA+PSAxLjAgJgogIG1vbm9jeXRlX3Zpc2l0X3dpdGhfZG9ub3JbWyJQLlZhbHVlIl1dIDw9IDAuMDUKZGVzZXFfZ2VuZXMgPC0gcm93bmFtZXMobWVyZ2VkKVtkZXNlcV9nZW5lc19pZHhdCnZpc2l0X2dlbmVzIDwtIHJvd25hbWVzKG1vbm9jeXRlX3Zpc2l0X3dpdGhfZG9ub3IpW3dpdGhvdXRfZG9ub3JfZ2VuZXNfaWR4XQp2ZW5uX2xzdCA8LSBsaXN0KAogICJ3aXRoX2Rvbm9yIiA9IGRlc2VxX2dlbmVzLAogICJ3aXRoX3Zpc2l0IiA9IHZpc2l0X2dlbmVzKQpWZW5uZXJhYmxlOjpWZW5uKHZlbm5fbHN0KQpgYGAKClRoaXMgdGltZSB3ZSBhcmUgY29tcGFyaW5nIGJhY2sgdG8gdGhlIG1vbm9jeXRlIHJlc3VsdHMgd2hpY2ggZGlkIG5vdAppbmNsdWRlIHRoZSByYW5kb20gZG9ub3IgZWZmZWN0LgoKYGBge3J9CmRlc2VxX2F1Y2MgPC0gY2FsY3VsYXRlX2F1Y2MobWVyZ2VkLCBtb25vY3l0ZV92aXNpdF93aXRob3V0X2Rvbm9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB4ID0gImxvZzJGb2xkQ2hhbmdlIiwgcHkgPSAicGFkaiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbHggPSAiYWRqLlAuVmFsIiwgbHkgPSAibG9nRkMiKQpkZXNlcV9hdWNjCgpkZXNlcV9nZW5lc19pZHggPC0gYWJzKG1lcmdlZFtbImxvZzJGb2xkQ2hhbmdlIl1dKSA+PSAxLjAgJgogIG1lcmdlZFtbInBhZGoiXV0gPD0gMC4wNQp3aXRob3V0X2Rvbm9yX2dlbmVzX2lkeCA8LSBhYnMobW9ub2N5dGVfdmlzaXRfd2l0aG91dF9kb25vcltbImxvZ0ZDIl1dKSA+PSAxLjAgJgogIG1vbm9jeXRlX3Zpc2l0X3dpdGhfZG9ub3JbWyJQLlZhbHVlIl1dIDw9IDAuMDUKZGVzZXFfZ2VuZXMgPC0gcm93bmFtZXMobWVyZ2VkKVtkZXNlcV9nZW5lc19pZHhdCnZpc2l0X2dlbmVzIDwtIHJvd25hbWVzKG1vbm9jeXRlX3Zpc2l0X3dpdGhfZG9ub3IpW3dpdGhvdXRfZG9ub3JfZ2VuZXNfaWR4XQp2ZW5uX2xzdCA8LSBsaXN0KAogICJ3aXRoX2Rvbm9yIiA9IGRlc2VxX2dlbmVzLAogICJ3aXRoX3Zpc2l0IiA9IHZpc2l0X2dlbmVzKQpWZW5uZXJhYmxlOjpWZW5uKHZlbm5fbHN0KQpgYGAKClRoaXMgaXMgdGhlIG9ydGhvbG9nb3VzIGFwcHJvYWNoOiBpbmNsdWRlIGEgcmFuZG9tIGVmZmVjdCBmb3IgZG9ub3IKYW5kIGlnbm9yZSB0aGUgdmlzaXQgZWZmZWN0LgoKYGBge3J9Cm1peGVkX2ZzdHJpbmdfZmQgPC0gIn4gMCArIGZpbmFsb3V0Y29tZSArICgxfGRvbm9yKSIKbWl4ZWRfZm9ybV9mZCA8LSBhcy5mb3JtdWxhKG1peGVkX2ZzdHJpbmdfZmQpCmdldF9mb3JtdWxhX2ZhY3RvcnMobWl4ZWRfZm9ybV9mZCkKbWl4ZWRfZW9zaW5vcGhpbF9mZF9kZSA8LSBkcmVhbV9wYWlyd2lzZSh0X2Vvc2lub3BoaWxzLCBhbHRfbW9kZWwgPSBtaXhlZF9mb3JtX2ZkKQptaXhlZF9tb25vY3l0ZV9mZF9kZSA8LSBkcmVhbV9wYWlyd2lzZSh0X21vbm9jeXRlcywgYWx0X21vZGVsID0gbWl4ZWRfZm9ybV9mZCkKbWl4ZWRfbmV1dHJvcGhpbF9mZF9kZSA8LSBkcmVhbV9wYWlyd2lzZSh0X25ldXRyb3BoaWxzLCBhbHRfbW9kZWwgPSBtaXhlZF9mb3JtX2ZkKQpgYGAKCiMjIyBDb21wYXJlIG1vbm9jeXRlcwoKTm93IHNlZSBob3cgdGhlc2UgcmVzdWx0cyBjb21wYXJlIGFnYWluc3Qgb3VyIHByZXZpb3VzIHJlc3VsdHMuLi4KCmBgYHtyfQptb25vY3l0ZV9kcmVhbV9yZXN1bHQgPC0gbWl4ZWRfbW9ub2N5dGVfZGVbWyJhbGxfdGFibGVzIl1dW1siZmFpbHVyZV92c19jdXJlIl1dCgpiaWdfdGFibGUgPC0gdF9jZl9tb25vY3l0ZV90YWJsZV9zdmFbWyJkYXRhIl1dW1sib3V0Y29tZSJdXQptZXJnZWQgPC0gbWVyZ2UoYmlnX3RhYmxlLCBtb25vY3l0ZV9kcmVhbV9yZXN1bHQsIGJ5ID0gInJvdy5uYW1lcyIpCnJvd25hbWVzKG1lcmdlZCkgPC0gbWVyZ2VkW1siUm93Lm5hbWVzIl1dCm1lcmdlZFtbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCmNvcl92YWx1ZSA8LSBjb3IudGVzdChtZXJnZWRbWyJsb2dGQyJdXSwgbWVyZ2VkW1siZGVzZXFfbG9nZmMiXV0pCmNvcl92YWx1ZQoKdF9jZl9tb25vY3l0ZV9kZV9zdmFbWyJkcmVhbSJdXSA8LSBtaXhlZF9tb25vY3l0ZV9kZQp0ZXN0IDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHRfY2ZfbW9ub2N5dGVfZGVfc3ZhLCBzY2FsZV9wID0gVFJVRSwKICBleGNlbCA9ICJleGNlbC90ZXN0X21vbm9jeXRlX2NvbWJpbmVkLnhsc3giKQp0ZXN0X2F1Y2MgPC0gY2FsY3VsYXRlX2F1Y2MobWVyZ2VkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHggPSAiZGVzZXFfYWRqcCIsIHB5ID0gImFkai5QLlZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBseCA9ICJkZXNlcV9sb2dmYyIsIGx5ID0gImxvZ0ZDIikKdGVzdF9hdWNjW1sicGxvdCJdXQp0ZXN0X2F1Y2NbWyJzY2F0dGVyIl1dW1sic2NhdHRlciJdXQoKbG9nZmNfcGxvdHRlciA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKG1lcmdlZFssIGMoImxvZ0ZDIiwgImRlc2VxX2xvZ2ZjIildKQpsb2dmY19wbG90IDwtIGxvZ2ZjX3Bsb3R0ZXJbWyJzY2F0dGVyIl1dICsKICB4bGFiKCJEcmVhbSBsb2cyRkMgd2l0aCAoMXxkb25vcikgYW5kIHZpc2l0IGluIG1vZGVsIikgKwogIHlsYWIoIkRFU2VxMiBsb2cyRkM6IERlZmF1bHQgcGFpcndpc2UgY29tcGFyaXNvbiIpCnBwKGZpbGUgPSAiZmlndXJlcy9jb21wYXJlX2NmX2FuZF9mdWxsX2RyZWFtX21vbm9jeXRlX2xvZ2ZjLnN2ZyIpCmxvZ2ZjX3Bsb3QKZGV2Lm9mZigpCmxvZ2ZjX3Bsb3QKCnByZXZpb3VzX3NpZ19pZHggPC0gbWVyZ2VkW1siZGVzZXFfYWRqcCJdXSA8PSAwLjA1ICYgYWJzKG1lcmdlZFtbImRlc2VxX2xvZ2ZjIl1dID49IDEuMCkKc3VtbWFyeShwcmV2aW91c19zaWdfaWR4KQpwcmV2aW91c19nZW5lcyA8LSByb3duYW1lcyhtZXJnZWQpW3ByZXZpb3VzX3NpZ19pZHhdCgpuZXdfc2lnX2lkeCA8LSBhYnMobWVyZ2VkW1sibG9nRkMiXV0pID49IDEuMCAmIG1lcmdlZFtbIlAuVmFsdWUiXV0gPCAwLjA1CnN1bW1hcnkobmV3X3NpZ19pZHgpCm5ld19nZW5lcyA8LSByb3duYW1lcyhtZXJnZWQpW25ld19zaWdfaWR4XQpuYV9pZHggPC0gaXMubmEobmV3X2dlbmVzKQpuZXdfZ2VuZXMgPC0gbmV3X2dlbmVzWyFuYV9pZHhdCgphbm5vdCA8LSBmRGF0YSh0X21vbm9jeXRlcykKY29tcGFyZSA8LSBWZW5uZXJhYmxlOjpWZW5uKGxpc3QoInByZXZpb3VzIiA9IHByZXZpb3VzX2dlbmVzLCAibmV3IiA9IG5ld19nZW5lcykpCnNoYXJlZF9nZW5lcyA8LSBjb21wYXJlQEludGVyc2VjdGlvblNldHNbWyIxMSJdXQpuYW1lX2lkeCA8LSByb3duYW1lcyhhbm5vdCkgJWluJSBzaGFyZWRfZ2VuZXMKVmVubmVyYWJsZTo6cGxvdChjb21wYXJlKQoKYW5ub3RbbmFtZV9pZHgsIF0KYGBgCgojIyMgTmV1dHJvcGhpbHMKCmBgYHtyfQpuZXV0cm9waGlsX2RyZWFtX3Jlc3VsdCA8LSBtaXhlZF9uZXV0cm9waGlsX2RlW1siYWxsX3RhYmxlcyJdXVtbImZhaWx1cmVfdnNfY3VyZSJdXQoKYmlnX3RhYmxlIDwtIHRfY2ZfbmV1dHJvcGhpbF90YWJsZV9zdmFbWyJkYXRhIl1dW1sib3V0Y29tZSJdXQptZXJnZWQgPC0gbWVyZ2UoYmlnX3RhYmxlLCBuZXV0cm9waGlsX2RyZWFtX3Jlc3VsdCwgYnkgPSAicm93Lm5hbWVzIikKcm93bmFtZXMobWVyZ2VkKSA8LSBtZXJnZWRbWyJSb3cubmFtZXMiXV0KbWVyZ2VkW1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKY29yX3ZhbHVlIDwtIGNvci50ZXN0KG1lcmdlZFtbImxvZ0ZDIl1dLCBtZXJnZWRbWyJkZXNlcV9sb2dmYyJdXSkKY29yX3ZhbHVlCgp0X2NmX25ldXRyb3BoaWxfZGVfc3ZhW1siZHJlYW0iXV0gPC0gbWl4ZWRfbmV1dHJvcGhpbF9kZQp0ZXN0IDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHRfY2ZfbmV1dHJvcGhpbF9kZV9zdmEsIHNjYWxlX3AgPSBUUlVFLAogIGV4Y2VsID0gImV4Y2VsL3Rlc3RfbmV1dHJvcGhpbF9jb21iaW5lZC54bHN4IikKdGVzdF9hdWNjIDwtIGNhbGN1bGF0ZV9hdWNjKG1lcmdlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB4ID0gImRlc2VxX2FkanAiLCBweSA9ICJhZGouUC5WYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbHggPSAiZGVzZXFfbG9nZmMiLCBseSA9ICJsb2dGQyIpCnRlc3RfYXVjY1tbInBsb3QiXV0KdGVzdF9hdWNjW1sic2NhdHRlciJdXVtbInNjYXR0ZXIiXV0KCmxvZ2ZjX3Bsb3R0ZXIgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihtZXJnZWRbLCBjKCJsb2dGQyIsICJkZXNlcV9sb2dmYyIpXSkKbG9nZmNfcGxvdCA8LSBsb2dmY19wbG90dGVyW1sic2NhdHRlciJdXSArCiAgeGxhYigiRHJlYW0gbG9nMkZDIHdpdGggKDF8ZG9ub3IpIGFuZCB2aXNpdCBpbiBtb2RlbCIpICsKICB5bGFiKCJERVNlcTIgbG9nMkZDOiBEZWZhdWx0IHBhaXJ3aXNlIGNvbXBhcmlzb24iKQpwcChmaWxlID0gImZpZ3VyZXMvY29tcGFyZV9jZl9hbmRfZnVsbF9kcmVhbV9uZXV0cm9waGlsX2xvZ2ZjLnN2ZyIpCmxvZ2ZjX3Bsb3QKZGV2Lm9mZigpCmxvZ2ZjX3Bsb3QKCnByZXZpb3VzX3NpZ19pZHggPC0gbWVyZ2VkW1siZGVzZXFfYWRqcCJdXSA8PSAwLjA1ICYgYWJzKG1lcmdlZFtbImRlc2VxX2xvZ2ZjIl1dID49IDEuMCkKc3VtbWFyeShwcmV2aW91c19zaWdfaWR4KQpwcmV2aW91c19nZW5lcyA8LSByb3duYW1lcyhtZXJnZWQpW3ByZXZpb3VzX3NpZ19pZHhdCgpuZXdfc2lnX2lkeCA8LSBhYnMobWVyZ2VkW1sibG9nRkMiXV0pID49IDEuMCAmIG1lcmdlZFtbIlAuVmFsdWUiXV0gPCAwLjA1CnN1bW1hcnkobmV3X3NpZ19pZHgpCm5ld19nZW5lcyA8LSByb3duYW1lcyhtZXJnZWQpW25ld19zaWdfaWR4XQpuYV9pZHggPC0gaXMubmEobmV3X2dlbmVzKQpuZXdfZ2VuZXMgPC0gbmV3X2dlbmVzWyFuYV9pZHhdCgphbm5vdCA8LSBmRGF0YSh0X25ldXRyb3BoaWxzKQpjb21wYXJlIDwtIFZlbm5lcmFibGU6OlZlbm4obGlzdCgicHJldmlvdXMiID0gcHJldmlvdXNfZ2VuZXMsICJuZXciID0gbmV3X2dlbmVzKSkKc2hhcmVkX2dlbmVzIDwtIGNvbXBhcmVASW50ZXJzZWN0aW9uU2V0c1tbIjExIl1dCm5hbWVfaWR4IDwtIHJvd25hbWVzKGFubm90KSAlaW4lIHNoYXJlZF9nZW5lcwoKYW5ub3RbbmFtZV9pZHgsIF0KVmVubmVyYWJsZTo6cGxvdChjb21wYXJlKQpgYGAKCiMjIyBFb3Npbm9waGlscwoKYGBge3J9CmVvc2lub3BoaWxfZHJlYW1fcmVzdWx0IDwtIG1peGVkX2Vvc2lub3BoaWxfZGVbWyJhbGxfdGFibGVzIl1dW1siZmFpbHVyZV92c19jdXJlIl1dCgpiaWdfdGFibGUgPC0gdF9jZl9lb3Npbm9waGlsX3RhYmxlX3N2YVtbImRhdGEiXV1bWyJvdXRjb21lIl1dCm1lcmdlZCA8LSBtZXJnZShiaWdfdGFibGUsIGVvc2lub3BoaWxfZHJlYW1fcmVzdWx0LCBieSA9ICJyb3cubmFtZXMiKQpyb3duYW1lcyhtZXJnZWQpIDwtIG1lcmdlZFtbIlJvdy5uYW1lcyJdXQptZXJnZWRbWyJSb3cubmFtZXMiXV0gPC0gTlVMTApjb3JfdmFsdWUgPC0gY29yLnRlc3QobWVyZ2VkW1sibG9nRkMiXV0sIG1lcmdlZFtbImRlc2VxX2xvZ2ZjIl1dKQpjb3JfdmFsdWUKCnRfY2ZfZW9zaW5vcGhpbF9kZV9zdmFbWyJkcmVhbSJdXSA8LSBtaXhlZF9lb3Npbm9waGlsX2RlCnRlc3QgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgdF9jZl9lb3Npbm9waGlsX2RlX3N2YSwgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSAiZXhjZWwvdGVzdF9lb3Npbm9waGlsX2NvbWJpbmVkLnhsc3giKQp0ZXN0X2F1Y2MgPC0gY2FsY3VsYXRlX2F1Y2MobWVyZ2VkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHggPSAiZGVzZXFfYWRqcCIsIHB5ID0gImFkai5QLlZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBseCA9ICJkZXNlcV9sb2dmYyIsIGx5ID0gImxvZ0ZDIikKdGVzdF9hdWNjW1sicGxvdCJdXQp0ZXN0X2F1Y2NbWyJzY2F0dGVyIl1dW1sic2NhdHRlciJdXQoKbG9nZmNfcGxvdHRlciA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKG1lcmdlZFssIGMoImxvZ0ZDIiwgImRlc2VxX2xvZ2ZjIildKQpsb2dmY19wbG90IDwtIGxvZ2ZjX3Bsb3R0ZXJbWyJzY2F0dGVyIl1dICsKICB4bGFiKCJEcmVhbSBsb2cyRkMgd2l0aCAoMXxkb25vcikgYW5kIHZpc2l0IGluIG1vZGVsIikgKwogIHlsYWIoIkRFU2VxMiBsb2cyRkM6IERlZmF1bHQgcGFpcndpc2UgY29tcGFyaXNvbiIpCnBwKGZpbGUgPSAiZmlndXJlcy9jb21wYXJlX2NmX2Z1bGxfZHJlYW1fZW9zaW5vcGhpbF9sb2dmYy5zdmciKQpsb2dmY19wbG90CmRldi5vZmYoKQpsb2dmY19wbG90CgpwcmV2aW91c19zaWdfaWR4IDwtIG1lcmdlZFtbImRlc2VxX2FkanAiXV0gPD0gMC4wNSAmIGFicyhtZXJnZWRbWyJkZXNlcV9sb2dmYyJdXSA+PSAxLjApCnN1bW1hcnkocHJldmlvdXNfc2lnX2lkeCkKcHJldmlvdXNfZ2VuZXMgPC0gcm93bmFtZXMobWVyZ2VkKVtwcmV2aW91c19zaWdfaWR4XQoKbmV3X3NpZ19pZHggPC0gYWJzKG1lcmdlZFtbImxvZ0ZDIl1dKSA+PSAxLjAgJiBtZXJnZWRbWyJQLlZhbHVlIl1dIDwgMC4wNQpzdW1tYXJ5KG5ld19zaWdfaWR4KQpuZXdfZ2VuZXMgPC0gcm93bmFtZXMobWVyZ2VkKVtuZXdfc2lnX2lkeF0KbmFfaWR4IDwtIGlzLm5hKG5ld19nZW5lcykKbmV3X2dlbmVzIDwtIG5ld19nZW5lc1shbmFfaWR4XQoKYW5ub3QgPC0gZkRhdGEodF9lb3Npbm9waGlscykKY29tcGFyZSA8LSBWZW5uZXJhYmxlOjpWZW5uKGxpc3QoInByZXZpb3VzIiA9IHByZXZpb3VzX2dlbmVzLCAibmV3IiA9IG5ld19nZW5lcykpCnNoYXJlZF9nZW5lcyA8LSBjb21wYXJlQEludGVyc2VjdGlvblNldHNbWyIxMSJdXQpuYW1lX2lkeCA8LSByb3duYW1lcyhhbm5vdCkgJWluJSBzaGFyZWRfZ2VuZXMKCmFubm90W25hbWVfaWR4LCBdClZlbm5lcmFibGU6OnBsb3QoY29tcGFyZSkKYGBgCgojIFBlcmZvcm0gZHJlYW0gd2l0aCBhbGwgc2FtcGxlcyB0b2dldGhlciBhbmQgYSBtb2RlbCB3aXRoIGFsbCBmYWN0b3JzCgpOb3cgdGhhdCBJIGhhdmUgcGVyZm9ybWVkIGFsbCBvZiB0aGUgYWJvdmUsIEkgdGhpbmsgaXQgc2hvdWxkIGJlCnBvc3NpYmxlIHRvIGhhdmUgYSB3b3JraW5nIGFuYWx5c2lzIHVzaW5nIGRyZWFtIHRoYXQgaW5jbHVkZXMKY2VsbHR5cGUsIHZpc2l0bnVtYmVyLCBmaW5hbG91dGNvbWUsIGRvbm9yLCBhbmQgcGVyaGFwcyBTVnMuCgpgYGB7cn0KbWl4ZWRfZnN0cmluZyA8LSAifiAwICsgZmluYWxvdXRjb21lICsgdHlwZW9mY2VsbHMgKyB2aXNpdG51bWJlciArICgxfGRvbm9yKSIKbWl4ZWRfZm9ybXVsYSA8LSBhcy5mb3JtdWxhKG1peGVkX2ZzdHJpbmcpCm1peGVkX2ZzdHJpbmdfc3ZzIDwtICJ+IDAgKyBmaW5hbG91dGNvbWUgKyB0eXBlb2ZjZWxscyArIHZpc2l0bnVtYmVyICsgKDF8ZG9ub3IpICsgc3Zhc2VxX1NWMSArIHN2YXNlcV9TVjIgKyBzdmFzZXFfU1YzICsgc3Zhc2VxX1NWNCIKbWl4ZWRfZm9ybXVsYV9zdnMgPC0gYXMuZm9ybXVsYShtaXhlZF9mc3RyaW5nX3N2cykKYWxsX2RyZWFtX2RlIDwtIGRyZWFtX3BhaXJ3aXNlKHRfY2xpbmljYWxfbm9iaW9wLCBhbHRfbW9kZWwgPSBtaXhlZF9mb3JtdWxhKQptaXhlZF9hbGxfY2VsbHR5cGVzX2RlX3hsc3ggPC0gd3JpdGVfZGVfdGFibGUoCiAgYWxsX2RyZWFtX2RlLCB0eXBlID0gImxpbW1hIiwKICBleGNlbCA9IGdsdWUoImV4Y2VsL21peGVkX2FsbF9jZWxsdHlwZXNfbm9iaW9wX3RhYmxlLXZ7dmVyfS54bHN4IikpCmFsbF9kcmVhbV9yZXN1bHQgPC0gYWxsX2RyZWFtX2RlW1siYWxsX3RhYmxlcyJdXVtbImZhaWx1cmVfdnNfY3VyZSJdXSAlPiUKICBhcnJhbmdlKGRlc2MobG9nRkMpKQpmY19zaWdfaWR4IDwtIGFsbF9kcmVhbV9yZXN1bHRbWyJsb2dGQyJdXSA+PSAxLjAgJiBhbGxfZHJlYW1fcmVzdWx0W1siei5zdGQiXV0gPj0gMi4wCmRyZWFtX3NpZyA8LSByb3duYW1lcyhhbGxfZHJlYW1fcmVzdWx0W2ZjX3NpZ19pZHgsIF0pCgpzdnNfYWxsX2RyZWFtX2RlIDwtIGRyZWFtX3BhaXJ3aXNlKHRfY2xpbmljYWxfbm9iaW9wLCBhbHRfbW9kZWwgPSBtaXhlZF9mb3JtdWxhX3N2cykKdGVzdCA8LSBocGdsX3BhZGp1c3Qoc3ZzX2FsbF9kcmVhbV9kZVtbImFsbF90YWJsZXMiXV1bWyJmYWlsdXJlX3ZzX2N1cmUiXV0sCiAgICAgICAgICAgICAgICAgICAgIG1lYW5fY29sdW1uID0gIkF2ZUV4cHIiLCBwdmFsdWVfY29sdW1uID0gIlAuVmFsdWUiLAogICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiaWh3IiwgdHlwZSA9ICJsaW1tYSIpCmBgYAoKYGBge3J9CnRfY2xpbmljYWxfb3V0Y29tZWNlbGxfZmFjdCA8LSBwYXN0ZTAocERhdGEodF9jbGluaWNhbF9ub2Jpb3ApW1siZmluYWxvdXRjb21lIl1dLCAiXyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcERhdGEodF9jbGluaWNhbF9ub2Jpb3ApW1sidHlwZW9mY2VsbHMiXV0pCnRfY2xpbmljYWxfb3V0Y29tZWNlbGwgPC0gdF9jbGluaWNhbF9ub2Jpb3AKcERhdGEodF9jbGluaWNhbF9vdXRjb21lY2VsbClbWyJvdXRjb21lY2VsbCJdXSA8LSB0X2NsaW5pY2FsX291dGNvbWVjZWxsX2ZhY3QKdF9jbGluaWNhbF9vdXRjb21lY2VsbCA8LSBzZXRfZXhwdF9jb25kaXRpb25zKHRfY2xpbmljYWxfb3V0Y29tZWNlbGwsIGZhY3QgPSAib3V0Y29tZWNlbGwiKQoKdF9jbGluaWNhbF9vdXRjb21lY2VsbF9kZSA8LSBhbGxfcGFpcndpc2UodF9jbGluaWNhbF9vdXRjb21lY2VsbCwga2VlcGVycyA9IG91dGNvbWV0eXBlX2NvbnRyYXN0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIikKbWl4ZWRfZnN0cmluZyA8LSAifiAwICsgY29uZGl0aW9uICsgdmlzaXRudW1iZXIgKyAoMXxkb25vcikiCnRfY2xpbmljYWxfb3V0Y29tZWNlbGxfZHJlYW0gPC0gZHJlYW1fcGFpcndpc2UodF9jbGluaWNhbF9vdXRjb21lY2VsbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHRfbW9kZWwgPSBhcy5mb3JtdWxhKG1peGVkX2ZzdHJpbmcpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBlcnMgPSBvdXRjb21ldHlwZV9jb250cmFzdHMpCnRfY2xpbmljYWxfb3V0Y29tZWNlbGxfdGFibGUgPC0gd3JpdGVfZGVfdGFibGUodF9jbGluaWNhbF9vdXRjb21lY2VsbF9kcmVhbSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gImxpbW1hIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleGNlbCA9IGdsdWUoImV4Y2VsL21peGVkX2NsaW5pY2FsX291dGNvbWVjZWxsLXZ7dmVyfS54bHN4IikpCmBgYAoKYGBge3J9CmJpZ190YWJsZSA8LSB0X2NmX2NsaW5pY2FsbmJfdGFibGVfc3ZhW1siZGF0YSJdXVtbIm91dGNvbWUiXV0KbWVyZ2VkIDwtIG1lcmdlKGJpZ190YWJsZSwgYWxsX2RyZWFtX3Jlc3VsdCwgYnkgPSAicm93Lm5hbWVzIikKcm93bmFtZXMobWVyZ2VkKSA8LSBtZXJnZWRbWyJSb3cubmFtZXMiXV0KbWVyZ2VkW1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKY29yX3ZhbHVlIDwtIGNvci50ZXN0KG1lcmdlZFtbImxvZ0ZDIl1dLCBtZXJnZWRbWyJkZXNlcV9sb2dmYyJdXSkKY29yX3ZhbHVlCgp0ZXN0X2F1Y2MgPC0gY2FsY3VsYXRlX2F1Y2MobWVyZ2VkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHggPSAiZGVzZXFfYWRqcCIsIHB5ID0gImFkai5QLlZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBseCA9ICJkZXNlcV9sb2dmYyIsIGx5ID0gImxvZ0ZDIikKdGVzdF9hdWNjW1sicGxvdCJdXQp0ZXN0X2F1Y2NbWyJzY2F0dGVyIl1dW1sic2NhdHRlciJdXQoKbG9nZmNfcGxvdHRlciA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKG1lcmdlZFssIGMoImxvZ0ZDIiwgImRlc2VxX2xvZ2ZjIildKQpsb2dmY19wbG90IDwtIGxvZ2ZjX3Bsb3R0ZXJbWyJzY2F0dGVyIl1dICsKICB4bGFiKCJEcmVhbSBsb2cyRkMgd2l0aCAoMXxkb25vcikgYW5kIHZpc2l0IGluIG1vZGVsIikgKwogIHlsYWIoIkRFU2VxMiBsb2cyRkM6IERlZmF1bHQgcGFpcndpc2UgY29tcGFyaXNvbiIpCnBwKGZpbGUgPSAiaW1hZ2VzL2NvbXBhcmVfY2ZfYW5kX2RyZWFtX2NsaW5pY2FsX3NhbXBsZXMucG5nIikKbG9nZmNfcGxvdApkZXYub2ZmKCkKbG9nZmNfcGxvdAoKY29yX3ZhbHVlIDwtIGNvci50ZXN0KG1lcmdlZFtbIlAuVmFsdWUiXV0sIG1lcmdlZFtbImRlc2VxX2FkanAiXV0sIG1ldGhvZCA9ICJzcGVhcm1hbiIpCmNvcl92YWx1ZQphZGpwX3Bsb3R0ZXIgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihtZXJnZWRbLCBjKCJQLlZhbHVlIiwgImRlc2VxX2FkanAiKV0pCmFkanBfcGxvdCA8LSBhZGpwX3Bsb3R0ZXJbWyJzY2F0dGVyIl1dICsKICB4bGFiKCJERVNlcTIgYWRqcDogRHJlYW0gbm90LWFkanVzdGVkIHAtdmFsdWUiKSArCiAgeWxhYigiREVTZXEyIGFkanA6IERlZmF1bHQgcGFpcndpc2UgY29tcGFyaXNvbiIpCnBwKGZpbGUgPSAiaW1hZ2VzL2NvbXBhcmVfY2ZfYW5kX3Zpc2l0X2luX21vZGVsX21vbm9jeXRlX2FkanAuc3ZnIikKYWRqcF9wbG90CmRldi5vZmYoKQphZGpwX3Bsb3QKCnByZXZpb3VzX3NpZ19pZHggPC0gbWVyZ2VkW1siZGVzZXFfYWRqcCJdXSA8PSAwLjA1ICYgYWJzKG1lcmdlZFtbImRlc2VxX2xvZ2ZjIl1dID49IDEuMCkKc3VtbWFyeShwcmV2aW91c19zaWdfaWR4KQpwcmV2aW91c19nZW5lcyA8LSByb3duYW1lcyhtZXJnZWQpW3ByZXZpb3VzX3NpZ19pZHhdCgpuZXdfc2lnX2lkeCA8LSBhYnMobWVyZ2VkW1sibG9nRkMiXV0pID49IDEuMCAmIG1lcmdlZFtbIlAuVmFsdWUiXV0gPCAwLjA1CnN1bW1hcnkobmV3X3NpZ19pZHgpCm5ld19nZW5lcyA8LSByb3duYW1lcyhtZXJnZWQpW25ld19zaWdfaWR4XQpuYV9pZHggPC0gaXMubmEobmV3X2dlbmVzKQpuZXdfZ2VuZXMgPC0gbmV3X2dlbmVzWyFuYV9pZHhdCgphbm5vdCA8LSBmRGF0YSh0X21vbm9jeXRlcykKY29tcGFyZSA8LSBWZW5uZXJhYmxlOjpWZW5uKGxpc3QoInByZXZpb3VzIiA9IHByZXZpb3VzX2dlbmVzLCAibmV3IiA9IG5ld19nZW5lcykpCnNoYXJlZF9nZW5lcyA8LSBjb21wYXJlQEludGVyc2VjdGlvblNldHNbWyIxMSJdXQpuYW1lX2lkeCA8LSByb3duYW1lcyhhbm5vdCkgJWluJSBzaGFyZWRfZ2VuZXMKCmFubm90W25hbWVfaWR4LCBdCmBgYAoKTGV0IHVzIHVzZSB0aGUgb3ZlcmxhcF9zaWcoKSBmcm9tIGFib3ZlIHRvIHNlZSBob3cgc2ltaWxhciB0aGlzIHJlc3VsdAppcyB0byBvdXIgREVTZXEyK1NWQS4KCmBgYHtyfQphbGxfZHJlYW1fdGFibGUgPC0gYWxsX2RyZWFtX2RlW1siYWxsX3RhYmxlcyJdXVtbImZhaWx1cmVfdnNfY3VyZSJdXQpvdmVybGFwX3NpZyhhbGxfZHJlYW1fdGFibGUpCm92ZXJsYXBfc2lnKGFsbF9kcmVhbV90YWJsZSwgZGlyZWN0aW9uID0gImd0IiwgbWl4ZWRfcGNvbCA9ICJ6LnN0ZCIsIG1peGVkX2N1dG9mZiA9IDEuNSkKCmFsbF9kcmVhbV90YWJsZV9zdnMgPC0gc3ZzX2FsbF9kcmVhbV9kZVtbImFsbF90YWJsZXMiXV1bWyJmYWlsdXJlX3ZzX2N1cmUiXV0Kb3ZlcmxhcF9zaWcoYWxsX2RyZWFtX3RhYmxlX3N2cykKb3ZlcmxhcF9zaWcoYWxsX2RyZWFtX3RhYmxlX3N2cywgZGlyZWN0aW9uID0gImd0IiwgbWl4ZWRfcGNvbCA9ICJ6LnN0ZCIsIG1peGVkX2N1dG9mZiA9IDEuNSkKYGBgCgojIyBSZWNhcGl0dWxhdGluZyB0aGUgMTAgZ2VuZXMgb2YgaW50ZXJlc3QKCk9uZSBmaWd1cmUgSSBkaWQgbm90IGNyZWF0ZSBpcyBhIHZlbm4gZGlhZ3JhbSBzaG93aW5nIHRoZSBvdmVybGFwIG9mCnRoZSBlb3Npb25waGlsLCBuZXV0cm9waGlsLCBhbmQgbW9ub2N5dGUgcmVzdWx0cyBhbmQgdGhlIDEwIGdlbmVzCnNoYXJlZCBhbW9uZyB0aGVtIGFsbC4gIEF0IGxlYXN0IGluIHRoZW9yeSBJIHNob3VsZCBiZSBlYXNpbHkgYWJsZSB0bwpjcmVhdGUgYSBzaW1pbGFyL2lkZW50aWNhbCBwbG90LgoKYGBge3J9Cm9ic2VydmVkX2Vvc2lub3BoaWxzIDwtIGMoCiAgcm93bmFtZXModF9jZl9lb3Npbm9waGlsX3NpZ19zdmFbWyJkZXNlcSJdXVtbInVwcyJdXVtbIm91dGNvbWUiXV0pLAogIHJvd25hbWVzKHRfY2ZfZW9zaW5vcGhpbF9zaWdfc3ZhW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbIm91dGNvbWUiXV0pKQpvYnNlcnZlZF9tb25vY3l0ZXMgPC0gYygKICByb3duYW1lcyh0X2NmX21vbm9jeXRlX3NpZ19zdmFbWyJkZXNlcSJdXVtbInVwcyJdXVtbIm91dGNvbWUiXV0pLAogIHJvd25hbWVzKHRfY2ZfbW9ub2N5dGVfc2lnX3N2YVtbImRlc2VxIl1dW1siZG93bnMiXV1bWyJvdXRjb21lIl1dKSkKb2JzZXJ2ZWRfbmV1dHJvcGhpbHMgPC0gYygKICByb3duYW1lcyh0X2NmX25ldXRyb3BoaWxfc2lnX3N2YVtbImRlc2VxIl1dW1sidXBzIl1dW1sib3V0Y29tZSJdXSksCiAgcm93bmFtZXModF9jZl9uZXV0cm9waGlsX3NpZ19zdmFbWyJkZXNlcSJdXVtbImRvd25zIl1dW1sib3V0Y29tZSJdXSkpCnZlbm5faW5wdXQgPC0gbGlzdCgKICAiZW9zaW5vcGhpbCIgPSBvYnNlcnZlZF9lb3Npbm9waGlscywKICAibW9ub2N5dGUiID0gb2JzZXJ2ZWRfbW9ub2N5dGVzLAogICJuZXV0cm9waGlscyIgPSBvYnNlcnZlZF9uZXV0cm9waGlscykKc2hhcmVkIDwtIFZlbm5lcmFibGU6OlZlbm4odmVubl9pbnB1dCkKc2hhcmVkClZlbm5lcmFibGU6OnBsb3Qoc2hhcmVkKQoKaW50ZXJzZWN0IDwtICJlb3Npbm9waGlsOm1vbm9jeXRlOm5ldXRyb3BoaWxzIgpjZWxsdHlwZV91cHNldCA8LSBVcFNldFI6OnVwc2V0KFVwU2V0Ujo6ZnJvbUxpc3QodmVubl9pbnB1dCksIHRleHQuc2NhbGUgPSAyKQpjZWxsdHlwZV91cHNldApjZWxsdHlwZV9zaGFyZWRfZ2VuZXMgPC0gb3ZlcmxhcF9ncm91cHModmVubl9pbnB1dCkKY2VsbHR5cGVfZ2VuZWlkcyA8LSBvdmVybGFwX2dlbmVpZHMoY2VsbHR5cGVfc2hhcmVkX2dlbmVzLCBpbnRlcnNlY3QpCmlkcyA8LSBhdHRyKGNlbGx0eXBlX3NoYXJlZF9nZW5lcywgImVsZW1lbnRzIilbY2VsbHR5cGVfc2hhcmVkX2dlbmVzW1tpbnRlcnNlY3RdXV0KaWRzCnJvd3MgPC0gZkRhdGEodF9tb25vY3l0ZXMpW2lkcywgXQpyb3dzW1siaGduY19zeW1ib2wiXV0KYGBgCgpOb3RlIHRvIHNlbGYsIHdoZW4gSSByZW5kZXJlZCB0aGUgaHRtbCwgc3R1cGlkIFIgcmFuIG91dCBvZiB0ZW1wIGZpbGVzCmFuZCBzbyBkaWQgbm90IGFjdHVhbGx5IHByaW50IHRoZSBkYXJuIGh0bWwgZG9jdW1lbnQsIGFzIGEgcmVzdWx0IEkKbW9kaWZpZWQgdGhlIHJlbmRlciBmdW5jdGlvbiB0byB0cnkgdG8gbWFrZSBzdXJlIHRoZXJlIGlzIGEgY2xlYW4KZGlyZWN0b3J5IGluIHdoaWNoIHRvIHdvcms7IHRlc3Rpbmcgbm93LiAgSWYgaXQgY29udGludWVzIHRvIG5vdCB3b3JrLApJIHdpbGwgbmVlZCB0byByZW1vdmUgc29tZSBvZiB0aGUgaW1hZ2VzIGNyZWF0ZWQgaW4gdGhpcyBkb2N1bWVudC4KCiMgQSBxdWVzdGlvbiBvZiBwLXZhbHVlcwoKTWFyaWEgQWRlbGFpZGEgaGFzIGFza2VkIGFib3V0IHRoZSBkaXN0cmlidXRpb24gb2YgKG5vbilhZGp1c3RlZApwLXZhbHVlcyBwcm9kdWNlZCBieSB0aGUgdmFyaW91cyBtZXRob2RzIHdlIGVtcGxveWVkLiAgSSB1c2UgQkggYnkKZGVmYXVsdDsgc28gbGV0cyB0YWtlIGEgbW9tZW50IHRvIGV4YW1pbmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwLXZhbHVlcwphbmQgaG93IHRoZXkgZ2V0IGFkanVzdGVkIGJ5IEJIIGFuZCBhIGZldyBvZiB0aGUgb3RoZXIgbWV0aG9kcy4KCmBgYHtyfQpkcmVhbV9wdmFsdWVzIDwtIGFsbF9kcmVhbV90YWJsZVtbIlAuVmFsdWUiXV0KbmFtZXMoZHJlYW1fcHZhbHVlcykgPC0gcm93bmFtZXMoYWxsX2RyZWFtX3RhYmxlKQoKZGVzZXFfcHZhbHVlcyA8LSB0X2NmX2NsaW5pY2FsbmJfdGFibGVfc3ZhW1siZGF0YSJdXVtbIm91dGNvbWUiXV1bWyJkZXNlcV9wIl1dCm5hbWVzKGRlc2VxX3B2YWx1ZXMpIDwtIHJvd25hbWVzKHRfY2ZfY2xpbmljYWxuYl90YWJsZV9zdmFbWyJkYXRhIl1dW1sib3V0Y29tZSJdXSkKCiMjIE5vdGUsIG15IHhsc3ggZmlsZXMgcHJvdmlkZSB0aGVzZSBpbWFnZXMuCnBsb3RfaGlzdG9ncmFtKGRyZWFtX3B2YWx1ZXMpCnBsb3RfaGlzdG9ncmFtKGRlc2VxX3B2YWx1ZXMpCmBgYAoKSW1tZWRpYXRlbHkgd2Ugc2VlIHRoYXQgdGhlIHZhbHVlcyBwcm9kdWNlZCBoYXZlIHZlcnkgZGlmZmVyZW50CmRpc3RyaWJ1dGlvbnMgYW5kIHRoYXQsIHRob3VnaCB0aGVyZSBhcmUgbWFueSBsb3cgcC12YWx1ZXMgcHJvZHVjZWQgYnkKZHJlYW0sIHRoZXkgYXJlIGZhciBmZXdlciB0aGFuIG9ic2VydmVkIGJ5IGRlc2VxLgoKTm93IGNvbnNpZGVyIHRoZSBCSCBjb3JyZWN0aW9uOyB1c2luZyBpdCwgd2UgcmFuayBvcmRlciB0aGUgcC12YWx1ZXMKZnJvbSBsb3dlc3QgdG8gaGlnaGVzdC4gIFRoZW4gd2UgY2hvb3NlIGEgZGVub21pbmF0b3IgZm9yIGV2ZXJ5CnAtdmFsdWUgd2hpY2ggcmFuZ2VzIGZyb20gMSB0byB0aGUgbnVtYmVyIG9mIGVsZW1lbnRzIGluIHRoZSBzZXQgb2YKcC12YWx1ZXMuICBGaW5hbGx5IHdlIHRha2UgdGhlIG1pbmltdW0gYmV0d2VlbiAxIGFuZCB0aGUgY3VtdWxhdGl2ZQptaW5pbXVtIG9mICgjcHZhbHVlcy9kZW5vbWluYXRvcikgKiB0aGF0LXB2YWx1ZS4gIFdyaXR0ZW4gb3V0IHRoZQpwcm9jZXNzIGxvb2tzIGxpa2UgdGhpczoKCmBgYHtyfQp0ZXN0X3B2YWx1ZXMgPC0gZGVzZXFfcHZhbHVlcwppZHggPC0gb3JkZXIodGVzdF9wdmFsdWVzKQp0ZXN0X3B2YWx1ZXMgPC0gdGVzdF9wdmFsdWVzW2lkeF0KbnVtX3B2YWx1ZXMgPC0gbGVuZ3RoKHRlc3RfcHZhbHVlcykKbmV3X3B2YWx1ZXMgPC0gdGVzdF9wdmFsdWVzCmZvciAoaSBpbiBzZXFfYWxvbmcodGVzdF9wdmFsdWVzKSkgewogIGVsZW1lbnQgPC0gdGVzdF9wdmFsdWVzW2ldCiAgbmV3X3B2YWx1ZXNbaV0gPC0gbWluKDEsIGN1bW1pbigobnVtX3B2YWx1ZXMgLyBpKSAqIGVsZW1lbnQpKQp9CnRlc3RfYWdhaW5zdCA8LSBwLmFkanVzdCh0ZXN0X3B2YWx1ZXMsIG1ldGhvZCA9ICJCSCIpCmBgYAoKU28sIGNvbnNpZGVyIGZvciBhIG1vbWVudCB0aGUgZmlyc3QgcC12YWx1ZXMgcHJvZHVjZWQgYnkgZGVzZXE6CjEuMTk1ZS0yNCwgMy40ODllLTIyLCA5LjYxMmUtMjIsIDQuODUzZS0xOCwgOS44NjRlLTE1LCAzLjI3NWUtMTQKClRoZSBuZXcgcC12YWx1ZXMgd2lsbCBiZSB0aGUgKG51bWJlciBvZiBnZW5lcyAvIHRoZSBjdXJyZW50CnBvc2l0aW9uKSAqIHRoZSBjdXJyZW50IGVsZW1lbnQKCiogKDExOTEwIC8gMSkgKiAxLjE5NWUtMjQgd2hpY2ggaXMgMS40MjNlLTEwCiogKDExOTEwIC8gMikgKiAzLjQ4OWUtMjIgd2hpY2ggaXMgMi4wNzhlLTE4CiogKDExOTEwIC8gMykgKiA5LjYxMmUtMjIgd2hpY2ggaXMgMy44MTZlLTE4CiogKDExOTEwIC8gNCkgKiA0Ljg1M2UtMTggd2hpY2ggaXMgMS40NDVlLTE0CiogKDExOTEwIC8gNSkgKiA5Ljg2NGUtMTUgd2hpY2ggaXMgMi4zNTBlLTExCiogKDExOTEwIC8gNikgKiAzLjI3NWUtMTQgd2hpY2ggaXMgNi41MDFlLTExCgpJbiBjb250cmFzdCwgY29uc2lkZXIgdGhlIGZpcnN0IGZldyB2YWx1ZXMgZnJvbSBkcmVhbSBvcmRlcmVkIGluIHRoZQpzYW1lIGZhc2hpb246CjIuMTYyZS0wNywgMy43NTdlLTA1LCA4LjExOWUtMDUsIDEuNjY0ZS0wNCwgMy4xMjNlLTA0LCA1LjYwMGUtMDQKClRoZXNlIHN0YXJ0IGF0IHZhbHVlcyB3aGljaCBhcmUgMWUxNyBoaWdoZXIgdGhhbiB0aG9zZSBmcm9tIERFU2VxIGFuZApzbyB3ZSBjYW4gZXhwZWN0IHRoZSByZXN1bHRpbmcgdmFsdWVzIHRvIGVuZCB1cCBzdGFydGluZyBhdCB+IDVlMTEKaGlnaGVyIHRoYW4gc2ltaWxhciB2YWx1ZXMuICBUaHVzIHdoZW4gd2UgZG8gdGhlIG1hdGggKGFuZCBiZSBhbXVzZWQKYXQgdGhlIGZhY3QgdGhhdCB0aGUgbnVtYmVyIG9mIHAtdmFsdWVzIGluIHRoZSB0YWJsZSBpcyBhIGZhY3RvciBvZgoyLDMsNCw1LDYpOgoKMTE5MTAgKiAyLjE2ZS0wNzogMC4wMDI1NzMKNTk1NSAqIDMuNzU3ZS01OiAwLjIyMzcxMQozOTcwICogOC4xMTllLTU6IDAuMzIyMjk3CjI5NzggKiAxLjY2NGUtNDogMC40OTU1CjIzODIgKiAzLjEyM2UtNDogMC43NDM4MzYKMTk4NSAqIDUuNjAwZS00OiAxLjExMiB3aGljaCBpcyBjYXVnaHQgYnkgcG1pbigpIGFuZCByZXNldCB0byAxLgoKIyBQcmludCBzb21lIHZvbGNhbm8gcGxvdHMKCkhhdmluZyBwZXJmb3JtZWQgYWxsIG9mIHRoZSBhYm92ZSwgbGV0IHVzIHBsb3Qgc29tZSBvZiB0aGUgcmVzdWx0cwp3aXRoIGEgZmV3IGxhYmVscyBvZiB0aGUgdG9wLTEwIGdlbmVzIG9uIGVhY2ggc2lkZSBvZiB0aGUgY29udHJhc3RzLgoKYGBge3J9Cm51bV9jb2xvciA8LSBjb2xvcl9jaG9pY2VzW1siY2xpbmljX2NmIl1dW1sidHVtYWNvX2ZhaWx1cmUiXV0KZGVuX2NvbG9yIDwtIGNvbG9yX2Nob2ljZXNbWyJjbGluaWNfY2YiXV1bWyJ0dW1hY29fY3VyZSJdXQoKY2ZfbW9ub2N5dGVfdGFibGUgPC0gdF9jZl9tb25vY3l0ZV90YWJsZV9zdmFbWyJkYXRhIl1dW1sib3V0Y29tZSJdXQpjZl9tb25vY3l0ZV92b2xjYW5vIDwtIHBsb3Rfdm9sY2Fub19jb25kaXRpb25fZGUoCiAgY2ZfbW9ub2N5dGVfdGFibGUsICJvdXRjb21lIiwgbGFiZWwgPSBleHBlY3RlZF9nZW5lcywKICBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgbGluZV9wb3NpdGlvbiA9IE5VTEwsCiAgY29sb3JfaGlnaCA9IG51bV9jb2xvciwgY29sb3JfbG93ID0gZGVuX2NvbG9yLCBsYWJlbF9zaXplID0gNikKcHAoZmlsZSA9ICJmaWd1cmVzL2NmX21vbm9jeXRlX3ZvbGNhbm9fbGFiZWxlZC5zdmciKQpjZl9tb25vY3l0ZV92b2xjYW5vW1sicGxvdCJdXQpkZXYub2ZmKCkKY2ZfbW9ub2N5dGVfdm9sY2Fub1tbInBsb3QiXV0KCmNmX21vbm9jeXRlX3ZvbGNhbm9fdG9wMTAgPC0gcGxvdF92b2xjYW5vX2NvbmRpdGlvbl9kZSgKICBjZl9tb25vY3l0ZV90YWJsZSwgIm91dGNvbWUiLCBsYWJlbCA9IDEwLAogIGZjX2NvbCA9ICJkZXNlcV9sb2dmYyIsIHBfY29sID0gImRlc2VxX2FkanAiLCBsaW5lX3Bvc2l0aW9uID0gTlVMTCwKICBjb2xvcl9oaWdoID0gbnVtX2NvbG9yLCBjb2xvcl9sb3cgPSBkZW5fY29sb3IsIGxhYmVsX3NpemUgPSA2KQpwcChmaWxlID0gZ2x1ZSgiaW1hZ2VzL2NmX21vbm9jeXRlX3ZvbGNhbm9fbGFiZWxlZF90b3AxMC12e3Zlcn0uc3ZnIikpCmNmX21vbm9jeXRlX3ZvbGNhbm9fdG9wMTBbWyJwbG90Il1dCmFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFkZXYub2ZmKCkKY2ZfbW9ub2N5dGVfdm9sY2Fub190b3AxMFtbInBsb3QiXV0KCmNmX2Vvc2lub3BoaWxfdGFibGUgPC0gdF9jZl9lb3Npbm9waGlsX3RhYmxlX3N2YVtbImRhdGEiXV1bWyJvdXRjb21lIl1dCmNmX2Vvc2lub3BoaWxfdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIGNmX2Vvc2lub3BoaWxfdGFibGUsICJvdXRjb21lIiwgbGFiZWwgPSBleHBlY3RlZF9nZW5lcywKICBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgbGluZV9wb3NpdGlvbiA9IE5VTEwsCiAgY29sb3JfaGlnaCA9IG51bV9jb2xvciwgY29sb3JfbG93ID0gZGVuX2NvbG9yLCBsYWJlbF9zaXplID0gNikKcHAoZmlsZSA9ICJmaWd1cmVzL2NmX2Vvc2lub3BoaWxfdm9sY2Fub19sYWJlbGVkLnN2ZyIpCmNmX2Vvc2lub3BoaWxfdm9sY2Fub1tbInBsb3QiXV0KZGV2Lm9mZigpCmNmX2Vvc2lub3BoaWxfdm9sY2Fub1tbInBsb3QiXV0KCmNmX2Vvc2lub3BoaWxfdm9sY2Fub190b3AxMCA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIGNmX2Vvc2lub3BoaWxfdGFibGUsICJvdXRjb21lIiwgbGFiZWwgPSAxMCwKICBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgbGluZV9wb3NpdGlvbiA9IE5VTEwsCiAgY29sb3JfaGlnaCA9IG51bV9jb2xvciwgY29sb3JfbG93ID0gZGVuX2NvbG9yLCBsYWJlbF9zaXplID0gNikKcHAoZmlsZSA9IGdsdWUoImltYWdlcy9jZl9lb3Npbm9waGlsX3ZvbGNhbm9fbGFiZWxlZF90b3AxMC12e3Zlcn0uc3ZnIikpCmNmX2Vvc2lub3BoaWxfdm9sY2Fub190b3AxMFtbInBsb3QiXV0KZGV2Lm9mZigpCmNmX2Vvc2lub3BoaWxfdm9sY2Fub190b3AxMFtbInBsb3QiXV0KCmNmX25ldXRyb3BoaWxfdGFibGUgPC0gdF9jZl9uZXV0cm9waGlsX3RhYmxlX3N2YVtbImRhdGEiXV1bWyJvdXRjb21lIl1dCmNmX25ldXRyb3BoaWxfdm9sY2FubyA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIGNmX25ldXRyb3BoaWxfdGFibGUsICJvdXRjb21lIiwgbGFiZWwgPSBleHBlY3RlZF9nZW5lcywKICBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgbGluZV9wb3NpdGlvbiA9IE5VTEwsCiAgY29sb3JfaGlnaCA9IG51bV9jb2xvciwgY29sb3JfbG93ID0gZGVuX2NvbG9yLCBsYWJlbF9zaXplID0gNikKcHAoZmlsZSA9ICJmaWd1cmVzL2NmX25ldXRyb3BoaWxfdm9sY2Fub19sYWJlbGVkLnN2ZyIpCmNmX25ldXRyb3BoaWxfdm9sY2Fub1tbInBsb3QiXV0KZGV2Lm9mZigpCmNmX25ldXRyb3BoaWxfdm9sY2Fub1tbInBsb3QiXV0KCmNmX25ldXRyb3BoaWxfdm9sY2Fub190b3AxMCA8LSBwbG90X3ZvbGNhbm9fY29uZGl0aW9uX2RlKAogIGNmX25ldXRyb3BoaWxfdGFibGUsICJvdXRjb21lIiwgbGFiZWwgPSAxMCwKICBmY19jb2wgPSAiZGVzZXFfbG9nZmMiLCBwX2NvbCA9ICJkZXNlcV9hZGpwIiwgbGluZV9wb3NpdGlvbiA9IE5VTEwsCiAgY29sb3JfaGlnaCA9IG51bV9jb2xvciwgY29sb3JfbG93ID0gZGVuX2NvbG9yLCBsYWJlbF9zaXplID0gNikKcHAoZmlsZSA9IGdsdWUoImltYWdlcy9jZl9uZXV0cm9waGlsX3ZvbGNhbm9fbGFiZWxlZF90b3AxMC12e3Zlcn0uc3ZnIikpCmNmX25ldXRyb3BoaWxfdm9sY2Fub190b3AxMFtbInBsb3QiXV0KZGV2Lm9mZigpCmNmX25ldXRyb3BoaWxfdm9sY2Fub190b3AxMFtbInBsb3QiXV0KYGBgCgojIEVvc2lub3BoaWwgdGltZSBjb21wYXJpc29ucwoKIyMgVmlzaXQgMQoKYGBge3J9CnRfY2ZfZW9zaW5vcGhpbF92MV9kZV9zdmEgPC0gYWxsX3BhaXJ3aXNlKHR2MV9lb3Npbm9waGlscywgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfY2ZfZW9zaW5vcGhpbF92MV9kZV9zdmEKdF9jZl9lb3Npbm9waGlsX3YxX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X2NmX2Vvc2lub3BoaWxfdjFfZGVfc3ZhLCBrZWVwZXJzID0gdF9jZl9jb250cmFzdCwgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9Fb3Npbm9waGlscy90X2Vvc2lub3BoaWxfdjFfY2ZfdGFibGVfc3ZhLXZ7dmVyfS54bHN4IikpCnRfY2ZfZW9zaW5vcGhpbF92MV90YWJsZV9zdmEKdF9jZl9lb3Npbm9waGlsX3YxX3NpZ19zdmEgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X2NmX2Vvc2lub3BoaWxfdjFfdGFibGVfc3ZhLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vRW9zaW5vcGhpbHMvdF9lb3Npbm9waGlsX3YxX2NmX3NpZ19zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZl9lb3Npbm9waGlsX3YxX3RhYmxlX3N2YQoKZGltKHRfY2ZfZW9zaW5vcGhpbF92MV9zaWdfc3ZhW1siZGVzZXEiXV1bWyJ1cHMiXV1bWzFdXSkKZGltKHRfY2ZfZW9zaW5vcGhpbF92MV9zaWdfc3ZhW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbMV1dKQpgYGAKCiMjIFZpc2l0IDIKCmBgYHtyfQp0X2NmX2Vvc2lub3BoaWxfdjJfZGVfc3ZhIDwtIGFsbF9wYWlyd2lzZSh0djJfZW9zaW5vcGhpbHMsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBtZXRob2RzKQp0X2NmX2Vvc2lub3BoaWxfdjJfZGVfc3ZhCnRfY2ZfZW9zaW5vcGhpbF92Ml90YWJsZV9zdmEgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgdF9jZl9lb3Npbm9waGlsX3YyX2RlX3N2YSwga2VlcGVycyA9IHRfY2ZfY29udHJhc3QsIHNjYWxlX3AgPSBUUlVFLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vRW9zaW5vcGhpbHMvdF9lb3Npbm9waGlsX3YyX2NmX3RhYmxlX3N2YS12e3Zlcn0ueGxzeCIpKQp0X2NmX2Vvc2lub3BoaWxfdjJfdGFibGVfc3ZhCnRfY2ZfZW9zaW5vcGhpbF92Ml9zaWdfc3ZhIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgdF9jZl9lb3Npbm9waGlsX3YyX3RhYmxlX3N2YSwKICBleGNlbCA9IGdsdWUoIntjZl9wcmVmaXh9L0Vvc2lub3BoaWxzL3RfZW9zaW5vcGhpbF92Ml9jZl9zaWdfc3ZhLXZ7dmVyfS54bHN4IikpCnRfY2ZfZW9zaW5vcGhpbF92Ml9zaWdfc3ZhCgpkaW0odF9jZl9lb3Npbm9waGlsX3YyX3NpZ19zdmFbWyJkZXNlcSJdXVtbInVwcyJdXVtbMV1dKQpkaW0odF9jZl9lb3Npbm9waGlsX3YyX3NpZ19zdmFbWyJkZXNlcSJdXVtbImRvd25zIl1dW1sxXV0pCmBgYAoKIyMgVmlzaXQgMwoKYGBge3J9CnRfY2ZfZW9zaW5vcGhpbF92M19kZV9zdmEgPC0gYWxsX3BhaXJ3aXNlKHR2M19lb3Npbm9waGlscywgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfY2ZfZW9zaW5vcGhpbF92M19kZV9zdmEKdF9jZl9lb3Npbm9waGlsX3YzX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X2NmX2Vvc2lub3BoaWxfdjNfZGVfc3ZhLCBrZWVwZXJzID0gdF9jZl9jb250cmFzdCwgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS9Fb3Npbm9waGlscy90X2Vvc2lub3BoaWxfdjNfY2ZfdGFibGVfc3ZhLXZ7dmVyfS54bHN4IikpCnRfY2ZfZW9zaW5vcGhpbF92M190YWJsZV9zdmEKdF9jZl9lb3Npbm9waGlsX3YzX3NpZ19zdmEgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X2NmX2Vvc2lub3BoaWxfdjNfdGFibGVfc3ZhLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vRW9zaW5vcGhpbHMvdF9lb3Npbm9waGlsX3YzX2NmX3NpZ19zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZl9lb3Npbm9waGlsX3YzX3NpZ19zdmEKCmRpbSh0X2NmX2Vvc2lub3BoaWxfdjNfc2lnX3N2YVtbImRlc2VxIl1dW1sidXBzIl1dW1sxXV0pCmRpbSh0X2NmX2Vvc2lub3BoaWxfdjNfc2lnX3N2YVtbImRlc2VxIl1dW1siZG93bnMiXV1bWzFdXSkKYGBgCgojIyBFb3Npbm9waGlsczogQ29tcGFyZSBzdmEgdG8gYmF0Y2gtaW4tdmlzaXQKCmBgYHtyfQpzdmFfYXVjYyA8LSBjYWxjdWxhdGVfYXVjYyh0X2NmX2Vvc2lub3BoaWxfdGFibGVfc3ZhW1siZGF0YSJdXVtbMV1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICB0YmwyID0gdF9jZl9lb3Npbm9waGlsX3RhYmxlX2JhdGNodmlzaXRbWyJkYXRhIl1dW1sxXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHB5ID0gImRlc2VxX2FkanAiLCBseSA9ICJkZXNlcV9sb2dmYyIpCnN2YV9hdWNjCgpzaGFyZWRfaWRzIDwtIHJvd25hbWVzKHRfY2ZfZW9zaW5vcGhpbF90YWJsZV9zdmFbWyJkYXRhIl1dW1sxXV0pICVpbiUKICByb3duYW1lcyh0X2NmX2Vvc2lub3BoaWxfdGFibGVfYmF0Y2h2aXNpdFtbImRhdGEiXV1bWzFdXSkKZmlyc3QgPC0gdF9jZl9lb3Npbm9waGlsX3RhYmxlX3N2YVtbImRhdGEiXV1bWzFdXVtzaGFyZWRfaWRzLCBdCnNlY29uZCA8LSB0X2NmX2Vvc2lub3BoaWxfdGFibGVfYmF0Y2h2aXNpdFtbImRhdGEiXV1bWzFdXVtyb3duYW1lcyhmaXJzdCksIF0KY29yLnRlc3QoZmlyc3RbWyJkZXNlcV9sb2dmYyJdXSwgc2Vjb25kW1siZGVzZXFfbG9nZmMiXV0pCmBgYAoKIyMgQ29tcGFyZSBtb25vY3l0ZSBDRiwgbmV1dHJvcGhpbCBDRiwgZW9zaW5vcGhpbCBDRgoKYGBge3J9CnRfbW9ub19uZXV0X3N2YV9hdWNjIDwtIGNhbGN1bGF0ZV9hdWNjKHRfY2ZfbW9ub2N5dGVfdGFibGVfc3ZhW1siZGF0YSJdXVtbIm91dGNvbWUiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRibDIgPSB0X2NmX25ldXRyb3BoaWxfdGFibGVfc3ZhW1siZGF0YSJdXVtbIm91dGNvbWUiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB5ID0gImRlc2VxX2FkanAiLCBseSA9ICJkZXNlcV9sb2dmYyIpCnRfbW9ub19uZXV0X3N2YV9hdWNjCgp0X21vbm9fZW9fc3ZhX2F1Y2MgPC0gY2FsY3VsYXRlX2F1Y2ModF9jZl9tb25vY3l0ZV90YWJsZV9zdmFbWyJkYXRhIl1dW1sib3V0Y29tZSJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRibDIgPSB0X2NmX2Vvc2lub3BoaWxfdGFibGVfc3ZhW1siZGF0YSJdXVtbIm91dGNvbWUiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBweSA9ICJkZXNlcV9hZGpwIiwgbHkgPSAiZGVzZXFfbG9nZmMiKQp0X21vbm9fZW9fc3ZhX2F1Y2MKCnRfbmV1dF9lb19zdmFfYXVjYyA8LSBjYWxjdWxhdGVfYXVjYyh0X2NmX25ldXRyb3BoaWxfdGFibGVfc3ZhW1siZGF0YSJdXVtbIm91dGNvbWUiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YmwyID0gdF9jZl9lb3Npbm9waGlsX3RhYmxlX3N2YVtbImRhdGEiXV1bWyJvdXRjb21lIl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHkgPSAiZGVzZXFfYWRqcCIsIGx5ID0gImRlc2VxX2xvZ2ZjIikKdF9uZXV0X2VvX3N2YV9hdWNjCmBgYAoKIyBCeSB2aXNpdAoKRm9yIHRoZXNlIGNvbnRyYXN0cywgd2Ugd2FudCB0byBzZWUgZmFpbF92MSB2cy4gY3VyZV92MSwgZmFpbF92Mgp2cy4gY3VyZV92MiBldGMuICBBcyBhIHJlc3VsdCwgd2Ugd2lsbCBuZWVkIHRvIGp1Z2dsZSB0aGUgZGF0YQpzbGlnaHRseSBhbmQgYWRkIGFub3RoZXIgc2V0IG9mIGNvbnRyYXN0cy4KCiMjIEN1cmUvRmFpbCBieSB2aXNpdHMsIGFsbCBjZWxsIHR5cGVzCgpgYGB7cn0KdF92aXNpdF9jZl9hbGxfZGVfc3ZhIDwtIGFsbF9wYWlyd2lzZSh0X3Zpc2l0Y2YsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2RzID0gbWV0aG9kcykKdF92aXNpdF9jZl9hbGxfZGVfc3ZhCnRfdmlzaXRfY2ZfYWxsX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X3Zpc2l0X2NmX2FsbF9kZV9zdmEsIGtlZXBlcnMgPSB2aXNpdGNmX2NvbnRyYXN0cywgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSBnbHVlKCJ7Y2ZfcHJlZml4fS90X2FsbF92aXNpdGNmX3RhYmxlX3N2YS12e3Zlcn0ueGxzeCIpKQp0X3Zpc2l0X2NmX2FsbF90YWJsZV9zdmEKdF92aXNpdF9jZl9hbGxfc2lnX3N2YSA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHRfdmlzaXRfY2ZfYWxsX3RhYmxlX3N2YSwKICBleGNlbCA9IGdsdWUoIntjZl9wcmVmaXh9L3RfYWxsX3Zpc2l0Y2Zfc2lnX3N2YS12e3Zlcn0ueGxzeCIpKQp0X3Zpc2l0X2NmX2FsbF9zaWdfc3ZhCmBgYAoKIyMgQ3VyZS9GYWlsIGJ5IHZpc2l0LCBNb25vY3l0ZXMKCkluIHRoZSBmb2xsb3dpbmcgYmxvY2ssIEkgYW0gaW5jbHVkaW5nIGFsbCBzYW1wbGVzIGZvciB0aGUgbW9ub2N5dGVzCmFuZCBzcGxpdHRpbmcgdGhlbSB1cCBieSB2aXNpdCBhbmQgdGhlbiBjb21wYXJpbmcgdjEgY3VyZS9mYWlsLCB2MgpjdXJlL2ZhaWwsIHYzIGN1cmUvZmFpbC4KCkkgZXhwZWN0IHRoYXQgdGhpcyBzaG91bGQgYmUgbW9yZSByb2J1c3QgdGhhbiB0aGUgZGF0YXNldHMgb2Ygb25seQp2aXNpdCAxLgoKYGBge3J9CnZpc2l0Y2ZfZmFjdG9yIDwtIHBhc3RlMChwRGF0YSh0X21vbm9jeXRlcylbWyJ2aXNpdG51bWJlciJdXSwgIl8iLAogICAgICAgICAgICAgICAgICAgICAgICAgcERhdGEodF9tb25vY3l0ZXMpW1siZmluYWxvdXRjb21lIl1dKQp0X21vbm9jeXRlc192aXNpdGNmIDwtIHNldF9leHB0X2NvbmRpdGlvbnModF9tb25vY3l0ZXMsIGZhY3QgPSB2aXNpdGNmX2ZhY3RvcikKCnRfdmlzaXRfY2ZfbW9ub2N5dGVfZGVfc3ZhIDwtIGFsbF9wYWlyd2lzZSh0X21vbm9jeXRlc192aXNpdGNmLCBtb2RlbF9iYXRjaCA9ICJzdmFzZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBtZXRob2RzKQp0X3Zpc2l0X2NmX21vbm9jeXRlX2RlX3N2YQp0X3Zpc2l0X2NmX21vbm9jeXRlX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X3Zpc2l0X2NmX21vbm9jeXRlX2RlX3N2YSwga2VlcGVycyA9IHZpc2l0Y2ZfY29udHJhc3RzLCBzY2FsZV9wID0gVFJVRSwKICBleGNlbCA9IGdsdWUoIntjZl9wcmVmaXh9L01vbm9jeXRlcy90X21vbm9jeXRlX3Zpc2l0Y2ZfdGFibGVfc3ZhLXZ7dmVyfS54bHN4IikpCnRfdmlzaXRfY2ZfbW9ub2N5dGVfdGFibGVfc3ZhCnRfdmlzaXRfY2ZfbW9ub2N5dGVfc2lnX3N2YSA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHRfdmlzaXRfY2ZfbW9ub2N5dGVfdGFibGVfc3ZhLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vTW9ub2N5dGVzL3RfbW9ub2N5dGVfdmlzaXRjZl9zaWdfc3ZhLXZ7dmVyfS54bHN4IikpCnRfdmlzaXRfY2ZfbW9ub2N5dGVfc2lnX3N2YQoKdF92MWZjX2Rlc2VxX21hIDwtIHRfdmlzaXRfY2ZfbW9ub2N5dGVfdGFibGVfc3ZhW1sicGxvdHMiXV1bWyJ2MWNmIl1dW1siZGVzZXFfbWFfcGxvdHMiXV0KZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL21vbm9jeXRlX2NmX2RlX3YxX21hcGxvdC5wbmciKQp0X3YxZmNfZGVzZXFfbWEKY2xvc2VkIDwtIGRldi5vZmYoKQp0X3YxZmNfZGVzZXFfbWEKCnRfdjJmY19kZXNlcV9tYSA8LSB0X3Zpc2l0X2NmX21vbm9jeXRlX3RhYmxlX3N2YVtbInBsb3RzIl1dW1sidjJjZiJdXVtbImRlc2VxX21hX3Bsb3RzIl1dCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9tb25vY3l0ZV9jZl9kZV92Ml9tYXBsb3QucG5nIikKdF92MmZjX2Rlc2VxX21hCmNsb3NlZCA8LSBkZXYub2ZmKCkKdF92MmZjX2Rlc2VxX21hCgp0X3YzZmNfZGVzZXFfbWEgPC0gdF92aXNpdF9jZl9tb25vY3l0ZV90YWJsZV9zdmFbWyJwbG90cyJdXVtbInYzY2YiXV1bWyJkZXNlcV9tYV9wbG90cyJdXQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvbW9ub2N5dGVfY2ZfZGVfdjNfbWFwbG90LnBuZyIpCnRfdjNmY19kZXNlcV9tYQpjbG9zZWQgPC0gZGV2Lm9mZigpCnRfdjNmY19kZXNlcV9tYQpgYGAKCk9uZSBxdWVyeSBmcm9tIEFsZWphbmRybyBpcyB0byBsb29rIGF0IHRoZSBnZW5lcyBzaGFyZWQgdXAvZG93biBhY3Jvc3MKdmlzaXRzLiAgSSBhbSBub3QgZW50aXJlbHkgY2VydGFpbiB3ZSBoYXZlIGVub3VnaCBzYW1wbGVzIGZvciB0aGlzIHRvCndvcmssIGJ1dCBsZXQgdXMgZmluZCBvdXQuCgpJIGFtIHRoaW5raW5nIHRoaXMgaXMgYSBnb29kIHBsYWNlIHRvIHVzZSB0aGUgQVVDQyBjdXJ2ZXMgSSBsZWFybmVkCmFib3V0IHRoYW5rcyB0byBKdWxpZSBDcmlkbGFuZC4KCk5vdGUgdGhhdCB0aGUgZm9sbG93aW5nIGlzIGFsbCBtb25vY3l0ZSBzYW1wbGVzLCB0aGlzIHNob3VsZCB0aGVyZWZvcmUKcG90ZW50aWFsbHkgYmUgbW92ZWQgdXAgYW5kIGEgdmVyc2lvbiBvZiB0aGlzIHdpdGggb25seSB0aGUgVHVtYWNvCnNhbXBsZXMgcHV0IGhlcmU/CgpgYGB7cn0KdjFjZiA8LSB0X3Zpc2l0X2NmX21vbm9jeXRlX3RhYmxlX3N2YVtbImRhdGEiXV1bWyJ2MWNmIl1dCnYyY2YgPC0gdF92aXNpdF9jZl9tb25vY3l0ZV90YWJsZV9zdmFbWyJkYXRhIl1dW1sidjJjZiJdXQp2M2NmIDwtIHRfdmlzaXRfY2ZfbW9ub2N5dGVfdGFibGVfc3ZhW1siZGF0YSJdXVtbInYzY2YiXV0KCnYxX3NpZyA8LSBjKAogIHJvd25hbWVzKHRfdmlzaXRfY2ZfbW9ub2N5dGVfc2lnX3N2YVtbImRlc2VxIl1dW1sidXBzIl1dW1sidjFjZiJdXSksCiAgcm93bmFtZXModF92aXNpdF9jZl9tb25vY3l0ZV9zaWdfc3ZhW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbInYxY2YiXV0pKQpsZW5ndGgodjFfc2lnKQoKdjJfc2lnIDwtIGMoCiAgcm93bmFtZXModF92aXNpdF9jZl9tb25vY3l0ZV9zaWdfc3ZhW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJ2MmNmIl1dKSwKICByb3duYW1lcyh0X3Zpc2l0X2NmX21vbm9jeXRlX3NpZ19zdmFbWyJkZXNlcSJdXVtbImRvd25zIl1dW1sidjJjZiJdXSkpCmxlbmd0aCh2Ml9zaWcpCgp2M19zaWcgPC0gYygKICByb3duYW1lcyh0X3Zpc2l0X2NmX21vbm9jeXRlX3NpZ19zdmFbWyJkZXNlcSJdXVtbInVwcyJdXVtbInYyY2YiXV0pLAogIHJvd25hbWVzKHRfdmlzaXRfY2ZfbW9ub2N5dGVfc2lnX3N2YVtbImRlc2VxIl1dW1siZG93bnMiXV1bWyJ2MmNmIl1dKSkKbGVuZ3RoKHYzX3NpZykKCnRfbW9ub2N5dGVfdmlzaXRfYXVjY192MnYxIDwtIGNhbGN1bGF0ZV9hdWNjKHYxY2YsIHRibDIgPSB2MmNmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBweSA9ICJkZXNlcV9hZGpwIiwgbHkgPSAiZGVzZXFfbG9nZmMiKQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvbW9ub2N5dGVfdmlzaXRfdjJ2MV9hdWNjLnBuZyIpCnRfbW9ub2N5dGVfdmlzaXRfYXVjY192MnYxW1sicGxvdCJdXQpjbG9zZWQgPC0gZGV2Lm9mZigpCnRfbW9ub2N5dGVfdmlzaXRfYXVjY192MnYxW1sicGxvdCJdXQoKdF9tb25vY3l0ZV92aXNpdF9hdWNjX3YzdjEgPC0gY2FsY3VsYXRlX2F1Y2ModjFjZiwgdGJsMiA9IHYzY2YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB5ID0gImRlc2VxX2FkanAiLCBseSA9ICJkZXNlcV9sb2dmYyIpCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9tb25vY3l0ZV92aXNpdF92M3YxX2F1Y2MucG5nIikKdF9tb25vY3l0ZV92aXNpdF9hdWNjX3YzdjFbWyJwbG90Il1dCmNsb3NlZCA8LSBkZXYub2ZmKCkKdF9tb25vY3l0ZV92aXNpdF9hdWNjX3YzdjFbWyJwbG90Il1dCmBgYAoKIyMgQ3VyZS9GYWlsIGJ5IHZpc2l0LCBOZXV0cm9waGlscwoKYGBge3J9CnZpc2l0Y2ZfZmFjdG9yIDwtIHBhc3RlMChwRGF0YSh0X25ldXRyb3BoaWxzKVtbInZpc2l0bnVtYmVyIl1dLCAiXyIsCiAgICAgICAgICAgICAgICAgICAgICAgICBwRGF0YSh0X25ldXRyb3BoaWxzKVtbImZpbmFsb3V0Y29tZSJdXSkKdF9uZXV0cm9waGlsX3Zpc2l0Y2YgPC0gc2V0X2V4cHRfY29uZGl0aW9ucyh0X25ldXRyb3BoaWxzLCBmYWN0ID0gdmlzaXRjZl9mYWN0b3IpCgp0X3Zpc2l0X2NmX25ldXRyb3BoaWxfZGVfc3ZhIDwtIGFsbF9wYWlyd2lzZSh0X25ldXRyb3BoaWxfdmlzaXRjZiwgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfdmlzaXRfY2ZfbmV1dHJvcGhpbF9kZV9zdmEKdF92aXNpdF9jZl9uZXV0cm9waGlsX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X3Zpc2l0X2NmX25ldXRyb3BoaWxfZGVfc3ZhLCBrZWVwZXJzID0gdmlzaXRjZl9jb250cmFzdHMsIHNjYWxlX3AgPSBUUlVFLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vTmV1dHJvcGhpbHMvdF9uZXV0cm9waGlsX3Zpc2l0Y2ZfdGFibGVfc3ZhLXZ7dmVyfS54bHN4IikpCnRfdmlzaXRfY2ZfbmV1dHJvcGhpbF90YWJsZV9zdmEKdF92aXNpdF9jZl9uZXV0cm9waGlsX3NpZ19zdmEgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X3Zpc2l0X2NmX25ldXRyb3BoaWxfdGFibGVfc3ZhLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vTmV1dHJvcGhpbHMvdF9uZXV0cm9waGlsX3Zpc2l0Y2Zfc2lnX3N2YS12e3Zlcn0ueGxzeCIpKQp0X3Zpc2l0X2NmX25ldXRyb3BoaWxfc2lnX3N2YQpgYGAKCiMjIEN1cmUvRmFpbCBieSB2aXNpdCwgRW9zaW5vcGhpbHMKCmBgYHtyfQp2aXNpdGNmX2ZhY3RvciA8LSBwYXN0ZTAocERhdGEodF9lb3Npbm9waGlscylbWyJ2aXNpdG51bWJlciJdXSwgIl8iLAogICAgICAgICAgICAgICAgICAgICAgICAgcERhdGEodF9lb3Npbm9waGlscylbWyJmaW5hbG91dGNvbWUiXV0pCnRfZW9zaW5vcGhpbF92aXNpdGNmIDwtIHNldF9leHB0X2NvbmRpdGlvbnModF9lb3Npbm9waGlscywgZmFjdCA9IHZpc2l0Y2ZfZmFjdG9yKQoKdF92aXNpdF9jZl9lb3Npbm9waGlsX2RlX3N2YSA8LSBhbGxfcGFpcndpc2UodF9lb3Npbm9waGlsX3Zpc2l0Y2YsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBtZXRob2RzLCBrZWVwZXJzID0gdmlzaXRjZl9jb250cmFzdHMpCnRfdmlzaXRfY2ZfZW9zaW5vcGhpbF9kZV9zdmEKdF92aXNpdF9jZl9lb3Npbm9waGlsX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X3Zpc2l0X2NmX2Vvc2lub3BoaWxfZGVfc3ZhLCBrZWVwZXJzID0gdmlzaXRjZl9jb250cmFzdHMsIHNjYWxlX3AgPSBUUlVFLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vRW9zaW5vcGhpbHMvdF9lb3Npbm9waGlsX3Zpc2l0Y2ZfdGFibGVfc3ZhLXZ7dmVyfS54bHN4IikpCnRfdmlzaXRfY2ZfZW9zaW5vcGhpbF90YWJsZV9zdmEKdF92aXNpdF9jZl9lb3Npbm9waGlsX3NpZ19zdmEgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X3Zpc2l0X2NmX2Vvc2lub3BoaWxfdGFibGVfc3ZhLAogIGV4Y2VsID0gZ2x1ZSgie2NmX3ByZWZpeH0vRW9zaW5vcGhpbHMvdF9lb3Npbm9waGlsX3Zpc2l0Y2Zfc2lnX3N2YS12e3Zlcn0ueGxzeCIpKQp0X3Zpc2l0X2NmX2Vvc2lub3BoaWxfc2lnX3N2YQpgYGAKCiMgU2hhcmVkIGdlbmVzIGluIHZpc2l0IDEKCkxldCB1cyBzZWUgaG93IG1hbnkgZ2VuZXMgYXJlIHNoYXJlZCBhY3Jvc3MgdGhlc2UgdGhyZWUgdmlzaXRzIHVzaW5nCm9ubHkgdGhlIHZpc2l0IDEgZGF0YS4KCgpgYGB7cn0Kb2JzZXJ2ZWRfdjFfZW9zaW5vcGhpbHMgPC0gYygKICByb3duYW1lcyh0X2NmX2Vvc2lub3BoaWxfdjFfc2lnX3N2YVtbImRlc2VxIl1dW1sidXBzIl1dW1sib3V0Y29tZSJdXSksCiAgcm93bmFtZXModF9jZl9lb3Npbm9waGlsX3YxX3NpZ19zdmFbWyJkZXNlcSJdXVtbImRvd25zIl1dW1sib3V0Y29tZSJdXSkpCm9ic2VydmVkX3YxX21vbm9jeXRlcyA8LSBjKAogIHJvd25hbWVzKHRfY2ZfbW9ub2N5dGVfdjFfc2lnX3N2YVtbImRlc2VxIl1dW1sidXBzIl1dW1sib3V0Y29tZSJdXSksCiAgcm93bmFtZXModF9jZl9tb25vY3l0ZV92MV9zaWdfc3ZhW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbIm91dGNvbWUiXV0pKQpvYnNlcnZlZF92MV9uZXV0cm9waGlscyA8LSBjKAogIHJvd25hbWVzKHRfY2ZfbmV1dHJvcGhpbF92MV9zaWdfc3ZhW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJvdXRjb21lIl1dKSwKICByb3duYW1lcyh0X2NmX25ldXRyb3BoaWxfdjFfc2lnX3N2YVtbImRlc2VxIl1dW1siZG93bnMiXV1bWyJvdXRjb21lIl1dKSkKdmVubl9pbnB1dCA8LSBsaXN0KAogICJlb3Npbm9waGlsIiA9IG9ic2VydmVkX3YxX2Vvc2lub3BoaWxzLAogICJtb25vY3l0ZSIgPSBvYnNlcnZlZF92MV9tb25vY3l0ZXMsCiAgIm5ldXRyb3BoaWxzIiA9IG9ic2VydmVkX3YxX25ldXRyb3BoaWxzKQpzaGFyZWQgPC0gVmVubmVyYWJsZTo6VmVubih2ZW5uX2lucHV0KQpzaGFyZWQKVmVubmVyYWJsZTo6cGxvdChzaGFyZWQpCmBgYAoKTmFqaWIgc3VnZ2VzdHMgdGhhdCB3ZSBzaG91bGQgbG9vayBhdCBhbGwgY2VsbCB0eXBlcyB0b2dldGhlciBhdAp2aXNpdCAxLiAgTGV0IHVzIHRyeSBhbmQgc2VlIHdoYXQgaGFwcGVucy4uLiAgT2gsIEkgYWxyZWFkeSBkaWQgdGhpcwppbiB0aGUgYmxvY2sgJ1NlcGFyYXRlIHRoZSBUdW1hY28gZGF0YSBieSB2aXNpdCcgYWJvdmUuCgpMZXQgdXMgYWRkIGEgbmV3IGJsb2NrIGluIHdoaWNoIHdlIHRlc3QgYSBjb25jZXJuOiBpZiB3ZSBleHBsaWNpdGx5CmFkZCB2aXNpdCB0byB0aGUgbW9kZWwgKHdpdGggc3ZhLCBwb3RlbnRpYWxseSB3aXRob3V0IHRvbyksIHdpbGwgdGhhdApjaGFuZ2UgdGhlIHJlc3VsdHMgd2Ugb2JzZXJ2ZT8gIE15IGFzc3VtcHRpb24gaXMgdGhhdCBpdCBzaG91bGQgY2hhbmdlCnRoZSByZXN1bHRzIHZlcnkgbWluaW1hbGx5OyBidXQgd2Ugc2hvdWxkIG1ha2UgYWJzb2x1dGVseSBjZXJ0YWluIHRoYXQKdGhpcyBpcyB0cnVlLiAgVGhlIG5ldXRyb3BoaWxzIGFyZSB0aGUgcGxhY2UgdG8gdGVzdCB0aGlzIGZpcnN0CmJlY2F1c2UgdGhleSBoYXZlIHNvbWUgb2YgdGhlIG1vc3QgdmFyaWFuY2Ugb2JzZXJ2ZWQgaW4gdGhlIGRhdGEuCgpUaGVyZWZvcmUgSSB3YW50IHRvIGhhdmUgYW4gaW5zdGFuY2Ugb2YgdGhlIHBhaXJ3aXNlIGNvbnRyYXN0IHRoYXQgaGFzCmEgbW9kZWwgb2YgfiBmaW5hbG91dGNvbWUgKyB2aXNpdG51bWJlciArIFNWcyB3aGVyZSB0aGUgU1ZzIGNvbWUgZnJvbQphbiBpbnZvY2F0aW9uIG9mIHN2YSB3aGljaCBhbHNvIGhhcyBmaW5hbG91dGNvbWUgKyB2aXNpdG51bWJlciBiZWZvcmUKdGhlIG51bGwgbW9kZWwuCgpJbiB0aGVvcnksIGFsbF9wYWlyd2lzZSgpIGlzIGFibGUgdG8gZG8gdGhpcyB2aWEgdGhlIGFyZ3VtZW50CmFsdF9tb2RlbCwgYnV0IGl0IG1heSBiZSBzYWZlciB0byBkbyBpdCBtYW51YWxseSBpbiBvcmRlciB0bwphYnNvbHV0ZWx5IGVuc3VyZSB0aGF0IG5vdGhpbmcgdW5pbnRlbmRlZCBoYXBwZW5zLgoKIyBQZXJzaXN0ZW5jZSBpbiB2aXNpdCAzCgpIYXZpbmcgcHV0IHNvbWUgU0wgcmVhZCBtYXBwaW5nIGluZm9ybWF0aW9uIGluIHRoZSBzYW1wbGUgc2hlZXQsIE1hcmlhCkFkZWxhaWRhIGFkZGVkIGEgbmV3IGNvbHVtbiB1c2luZyBpdCB3aXRoIHRoZSBwdXRhdGl2ZSBwZXJzaXN0ZW5jZQpzdGF0ZSBvbiBhIHBlci1zYW1wbGUgYmFzaXMuICBPbmUgcXVlc3Rpb24gd2hpY2ggYXJpc2VkIGZyb20gdGhhdDoKd2hhdCBkaWZmZXJlbmNlcyBhcmUgb2JzZXJ2YWJsZSBiZXR3ZWVuIHRoZSBwZXJzaXN0ZW50IHllcyB2cy4gbm8Kc2FtcGxlcyBvbiBhIHBlci1jZWxsLXR5cGUgYmFzaXMgYW1vbmcgdGhlIHZpc2l0IDMgc2FtcGxlcy4KCiMjIFNldHRpbmcgdXAKCkZpcnN0IHRoaW5ncyBmaXJzdCwgY3JlYXRlIHRoZSBkYXRhc2V0cy4KCmBgYHtyfQpwZXJzaXN0ZW5jZV9leHB0IDwtIHN1YnNldF9leHB0KHRfY2xpbmljYWwsIHN1YnNldCA9ICJwZXJzaXN0ZW5jZT09J1knfHBlcnNpc3RlbmNlPT0nTiciKSAlPiUKICBzdWJzZXRfZXhwdChzdWJzZXQgPSAndmlzaXRudW1iZXI9PTMnKSAlPiUKICBzZXRfZXhwdF9jb25kaXRpb25zKGZhY3QgPSAncGVyc2lzdGVuY2UnKQoKIyMgcGVyc2lzdGVuY2VfYmlvcHN5IDwtIHN1YnNldF9leHB0KHBlcnNpc3RlbmNlX2V4cHQsIHN1YnNldCA9ICJ0eXBlb2ZjZWxscz09J2Jpb3BzeSciKQpwZXJzaXN0ZW5jZV9tb25vY3l0ZSA8LSBzdWJzZXRfZXhwdChwZXJzaXN0ZW5jZV9leHB0LCBzdWJzZXQgPSAidHlwZW9mY2VsbHM9PSdtb25vY3l0ZXMnIikKcGVyc2lzdGVuY2VfbmV1dHJvcGhpbCA8LSBzdWJzZXRfZXhwdChwZXJzaXN0ZW5jZV9leHB0LCBzdWJzZXQgPSAidHlwZW9mY2VsbHM9PSduZXV0cm9waGlscyciKQpwZXJzaXN0ZW5jZV9lb3Npbm9waGlsIDwtIHN1YnNldF9leHB0KHBlcnNpc3RlbmNlX2V4cHQsIHN1YnNldCA9ICJ0eXBlb2ZjZWxscz09J2Vvc2lub3BoaWxzJyIpCmBgYAoKIyMgVGFrZSBhIGxvb2sKClNlZSBpZiB0aGVyZSBhcmUgYW55IHBhdHRlcm5zIHdoaWNoIGxvb2sgdXNhYmxlLgoKYGBge3J9CiMjIEFsbApwZXJzaXN0ZW5jZV9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KHBlcnNpc3RlbmNlX2V4cHQsIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm0gPSAicXVhbnQiLCBmaWx0ZXIgPSBUUlVFKQpwbG90X3BjYShwZXJzaXN0ZW5jZV9ub3JtKVtbInBsb3QiXV0KcGVyc2lzdGVuY2VfbmIgPC0gbm9ybWFsaXplX2V4cHQocGVyc2lzdGVuY2VfZXhwdCwgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhdGNoID0gInN2YXNlcSIsIGZpbHRlciA9IFRSVUUpCnBsb3RfcGNhKHBlcnNpc3RlbmNlX25iKVtbInBsb3QiXV0KCiMjIEJpb3BzaWVzCiMjcGVyc2lzdGVuY2VfYmlvcHN5X25vcm0gPC0gbm9ybWFsaXplX2V4cHQocGVyc2lzdGVuY2VfYmlvcHN5LCB0cmFuc2Zvcm0gPSAibG9nMiIsIGNvbnZlcnQgPSAiY3BtIiwKIyMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm0gPSAicXVhbnQiLCBmaWx0ZXIgPSBUUlVFKQojI3Bsb3RfcGNhKHBlcnNpc3RlbmNlX2Jpb3BzeV9ub3JtKVtbInBsb3QiXV0KIyMgSW5zdWZmaWNpZW50IGRhdGEKCiMjIE1vbm9jeXRlcwpwZXJzaXN0ZW5jZV9tb25vY3l0ZV9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KHBlcnNpc3RlbmNlX21vbm9jeXRlLCB0cmFuc2Zvcm0gPSAibG9nMiIsIGNvbnZlcnQgPSAiY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtID0gInF1YW50IiwgZmlsdGVyID0gVFJVRSkKcGxvdF9wY2EocGVyc2lzdGVuY2VfbW9ub2N5dGVfbm9ybSlbWyJwbG90Il1dCnBlcnNpc3RlbmNlX21vbm9jeXRlX25iIDwtIG5vcm1hbGl6ZV9leHB0KHBlcnNpc3RlbmNlX21vbm9jeXRlLCB0cmFuc2Zvcm0gPSAibG9nMiIsIGNvbnZlcnQgPSAiY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmF0Y2ggPSAic3Zhc2VxIiwgZmlsdGVyID0gVFJVRSkKcGxvdF9wY2EocGVyc2lzdGVuY2VfbW9ub2N5dGVfbmIpW1sicGxvdCJdXQoKIyMgTmV1dHJvcGhpbHMKcGVyc2lzdGVuY2VfbmV1dHJvcGhpbF9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KHBlcnNpc3RlbmNlX25ldXRyb3BoaWwsIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybSA9ICJxdWFudCIsIGZpbHRlciA9IFRSVUUpCnBsb3RfcGNhKHBlcnNpc3RlbmNlX25ldXRyb3BoaWxfbm9ybSlbWyJwbG90Il1dCnBlcnNpc3RlbmNlX25ldXRyb3BoaWxfbmIgPC0gbm9ybWFsaXplX2V4cHQocGVyc2lzdGVuY2VfbmV1dHJvcGhpbCwgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmF0Y2ggPSAic3Zhc2VxIiwgZmlsdGVyID0gVFJVRSkKcGxvdF9wY2EocGVyc2lzdGVuY2VfbmV1dHJvcGhpbF9uYilbWyJwbG90Il1dCgojIyBFb3Npbm9waGlscwpwZXJzaXN0ZW5jZV9lb3Npbm9waGlsX25vcm0gPC0gbm9ybWFsaXplX2V4cHQocGVyc2lzdGVuY2VfZW9zaW5vcGhpbCwgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtID0gInF1YW50IiwgZmlsdGVyID0gVFJVRSkKcGxvdF9wY2EocGVyc2lzdGVuY2VfZW9zaW5vcGhpbF9ub3JtKVtbInBsb3QiXV0KcGVyc2lzdGVuY2VfZW9zaW5vcGhpbF9uYiA8LSBub3JtYWxpemVfZXhwdChwZXJzaXN0ZW5jZV9lb3Npbm9waGlsLCB0cmFuc2Zvcm0gPSAibG9nMiIsIGNvbnZlcnQgPSAiY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYXRjaCA9ICJzdmFzZXEiLCBmaWx0ZXIgPSBUUlVFKQpwbG90X3BjYShwZXJzaXN0ZW5jZV9lb3Npbm9waGlsX25iKVtbInBsb3QiXV0KYGBgCgojIyBwZXJzaXN0ZW5jZSBERQoKVGhpcyBpcyBwcmV0dHkgc3BhcnNlIGFuZCB1bmxpa2VseSB0byB5aWVsZCBhbnkgaW50ZXJlc3RpbmcgcmVzdWx0cyBJCmFtIHRoaW5raW5nLgoKYGBge3J9CnBlcnNpc3RlbmNlX2RlX3N2YSA8LSBhbGxfcGFpcndpc2UocGVyc2lzdGVuY2VfZXhwdCwgZmlsdGVyID0gVFJVRSwgbWV0aG9kcyA9IG1ldGhvZHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIikKcGVyc2lzdGVuY2VfZGVfc3ZhCnBlcnNpc3RlbmNlX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBwZXJzaXN0ZW5jZV9kZV9zdmEsIHNjYWxlX3AgPSBUUlVFLAogIGV4Y2VsID0gZ2x1ZSgie3hsc3hfcHJlZml4fS9ERV9QZXJzaXN0ZW5jZS9wZXJzaXN0ZW5jZV9hbGxfZGVfc3ZhLXZ7dmVyfS54bHN4IikpCnBlcnNpc3RlbmNlX3RhYmxlX3N2YQpwZXJzaXN0ZW5jZV9tb25vY3l0ZV9kZV9zdmEgPC0gYWxsX3BhaXJ3aXNlKHBlcnNpc3RlbmNlX21vbm9jeXRlLCBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnBlcnNpc3RlbmNlX21vbm9jeXRlX2RlX3N2YQpwZXJzaXN0ZW5jZV9tb25vY3l0ZV90YWJsZV9zdmEgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgcGVyc2lzdGVuY2VfbW9ub2N5dGVfZGVfc3ZhLCBzY2FsZV9wID0gVFJVRSwKICBleGNlbCA9IGdsdWUoInt4bHN4X3ByZWZpeH0vREVfUGVyc2lzdGVuY2UvcGVyc2lzdGVuY2VfbW9ub2N5dGVfZGVfc3ZhLXZ7dmVyfS54bHN4IikpCnBlcnNpc3RlbmNlX21vbm9jeXRlX3RhYmxlX3N2YQoKcGVyc2lzdGVuY2VfbmV1dHJvcGhpbF9kZV9zdmEgPC0gYWxsX3BhaXJ3aXNlKHBlcnNpc3RlbmNlX25ldXRyb3BoaWwsIGZpbHRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF9iYXRjaCA9ICJzdmFzZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnBlcnNpc3RlbmNlX25ldXRyb3BoaWxfZGVfc3ZhCnBlcnNpc3RlbmNlX25ldXRyb3BoaWxfdGFibGVfc3ZhIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIHBlcnNpc3RlbmNlX25ldXRyb3BoaWxfZGVfc3ZhLCBzY2FsZV9wID0gVFJVRSwKICBleGNlbCA9IGdsdWUoInt4bHN4X3ByZWZpeH0vREVfUGVyc2lzdGVuY2UvcGVyc2lzdGVuY2VfbmV1dHJvcGhpbF9kZV9zdmEtdnt2ZXJ9Lnhsc3giKSkKcGVyc2lzdGVuY2VfbmV1dHJvcGhpbF90YWJsZV9zdmEKCiMjIFRoZXJlIGFyZSBpbnN1ZmZpY2llbnQgc2FtcGxlcyAoMSkgaW4gdGhlICdOJyBjYXRlZ29yeS4KIyNwZXJzaXN0ZW5jZV9lb3Npbm9waGlsX2RlX3N2YSA8LSBhbGxfcGFpcndpc2UocGVyc2lzdGVuY2VfZW9zaW5vcGhpbCwgZmlsdGVyID0gVFJVRSwKI21vZGVsX2JhdGNoID0gInN2YXNlcSIsCiMjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBtZXRob2RzKQojI3BlcnNpc3RlbmNlX2Vvc2lub3BoaWxfZGVfc3ZhCiMjcGVyc2lzdGVuY2VfZW9zaW5vcGhpbF90YWJsZV9zdmEgPC0gY29tYmluZV9kZV90YWJsZXMoCiMjICBwZXJzaXN0ZW5jZV9lb3Npbm9waGlsX2RlX3N2YSwKIyMgIGV4Y2VsID0gZ2x1ZSgie3hsc3hfcHJlZml4fS9ERV9QZXJzaXN0ZW5jZS9wZXJzaXN0ZW5jZV9lb3Npbm9waGlsX2RlX3N2YS12e3Zlcn0ueGxzeCIpKQpgYGAKCiMgQ29tcGFyaW5nIHZpc2l0cyB3aXRob3V0IHJlZ2FyZCB0byBjdXJlL2ZhaWwKCkluIHRoZSBmb2xsb3dpbmcsIEkgYW0gaG9waW5nIHRvIGxvd2VyIHZhcmlhbmNlIGFzc29jaWF0ZWQgd2l0aApmYWN0b3JzIG90aGVyIHRoYW4gdmlzaXQgdmlhIHN2YSBhbmQgdGhlcmVmb3JlIGJlIGFibGUgdG8gc2VlIHdoYXQKZ2VuZXMgYXJlIGNoYW5naW5nIGZvciBldmVyeW9uZSB3aXRoIHJlc3BlY3QgdG8gdGltZS4KClRoaXMgaXMgdGhlIG9uZSBpbnN0YW5jZSB3aGVyZSBJIHRoaW5rIGl0IHdvdWxkIGJlIHJlYWxseSBuaWNlIHRvIGhhdmUKYmlvcHN5IHNhbXBsZXMgZm9yIGFsbCB0aHJlZSB2aXNpdHM7IEkgcHJlc3VtZSB0aGF0IHdlIHdvdWxkIGhhdmUgYQpyZWFsbHkgbmljZSBzaWduYWwgb2Ygc3R1ZmYgbGlrZSBrZXJhdGluIGFuZCBvdGhlciB3b3VuZC1oZWFsaW5nCmFzc29jaWF0ZWQgZ2VuZXMuCgojIyBBbGwgY2VsbCB0eXBlcwoKYGBge3J9CnRfdmlzaXRfYWxsX2RlX3N2YSA8LSBhbGxfcGFpcndpc2UodF92aXNpdCwgZmlsdGVyID0gVFJVRSwgbWV0aG9kcyA9IG1ldGhvZHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIikKdF92aXNpdF9hbGxfZGVfc3ZhCnRfdmlzaXRfYWxsX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X3Zpc2l0X2FsbF9kZV9zdmEsIGtlZXBlcnMgPSB2aXNpdF9jb250cmFzdHMsIHNjYWxlX3AgPSBUUlVFLAogIGV4Y2VsID0gZ2x1ZSgie3hsc3hfcHJlZml4fS9ERV9WaXNpdHMvdF9hbGxfdmlzaXRfdGFibGVfc3ZhLXZ7dmVyfS54bHN4IikpCnRfdmlzaXRfYWxsX3RhYmxlX3N2YQp0X3Zpc2l0X2FsbF9zaWdfc3ZhIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgdF92aXNpdF9hbGxfdGFibGVfc3ZhLAogIGV4Y2VsID0gZ2x1ZSgie3hsc3hfcHJlZml4fS9ERV9WaXNpdHMvdF9hbGxfdmlzaXRfc2lnX3N2YS12e3Zlcn0ueGxzeCIpKQp0X3Zpc2l0X2FsbF9zaWdfc3ZhCmBgYAoKIyMgTW9ub2N5dGUgc2FtcGxlcwoKYGBge3J9CnRfdmlzaXRfbW9ub2N5dGVzIDwtIHNldF9leHB0X2NvbmRpdGlvbnModF9tb25vY3l0ZXMsIGZhY3QgPSAidmlzaXRudW1iZXIiKQoKdF92aXNpdF9tb25vY3l0ZV9kZV9zdmEgPC0gYWxsX3BhaXJ3aXNlKHRfdmlzaXRfbW9ub2N5dGVzLCBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBtZXRob2RzKQp0X3Zpc2l0X21vbm9jeXRlX2RlX3N2YQp0X3Zpc2l0X21vbm9jeXRlX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X3Zpc2l0X21vbm9jeXRlX2RlX3N2YSwga2VlcGVycyA9IHZpc2l0X2NvbnRyYXN0cywgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSBnbHVlKCJ7eGxzeF9wcmVmaXh9L0RFX1Zpc2l0cy9Nb25vY3l0ZXMvdF9tb25vY3l0ZV92aXNpdF90YWJsZV9zdmEtdnt2ZXJ9Lnhsc3giKSkKdF92aXNpdF9tb25vY3l0ZV90YWJsZV9zdmEKdF92aXNpdF9tb25vY3l0ZV9zaWdfc3ZhIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgdF92aXNpdF9tb25vY3l0ZV90YWJsZV9zdmEsCiAgZXhjZWwgPSBnbHVlKCJ7eGxzeF9wcmVmaXh9L0RFX1Zpc2l0cy9Nb25vY3l0ZXMvdF9tb25vY3l0ZV92aXNpdF9zaWdfc3ZhLXZ7dmVyfS54bHN4IikpCnRfdmlzaXRfbW9ub2N5dGVfc2lnX3N2YQpgYGAKCiMjIE5ldXRyb3BoaWwgc2FtcGxlcwoKYGBge3J9CnRfdmlzaXRfbmV1dHJvcGhpbHMgPC0gc2V0X2V4cHRfY29uZGl0aW9ucyh0X25ldXRyb3BoaWxzLCBmYWN0ID0gInZpc2l0bnVtYmVyIikKCnRfdmlzaXRfbmV1dHJvcGhpbF9kZV9zdmEgPC0gYWxsX3BhaXJ3aXNlKHRfdmlzaXRfbmV1dHJvcGhpbHMsIGZpbHRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBtZXRob2RzKQp0X3Zpc2l0X25ldXRyb3BoaWxfZGVfc3ZhCnRfdmlzaXRfbmV1dHJvcGhpbF90YWJsZV9zdmEgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgdF92aXNpdF9uZXV0cm9waGlsX2RlX3N2YSwga2VlcGVycyA9IHZpc2l0X2NvbnRyYXN0cywgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSBnbHVlKCJ7eGxzeF9wcmVmaXh9L0RFX1Zpc2l0cy9OZXV0cm9waGlscy90X25ldXRyb3BoaWxfdmlzaXRfdGFibGVfc3ZhLXZ7dmVyfS54bHN4IikpCnRfdmlzaXRfbmV1dHJvcGhpbF90YWJsZV9zdmEKdF92aXNpdF9uZXV0cm9waGlsX3NpZ19zdmEgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X3Zpc2l0X25ldXRyb3BoaWxfdGFibGVfc3ZhLAogIGV4Y2VsID0gZ2x1ZSgie3hsc3hfcHJlZml4fS9ERV9WaXNpdHMvTmV1dHJvcGhpbHMvdF9uZXV0cm9waGlsX3Zpc2l0X3NpZ19zdmEtdnt2ZXJ9Lnhsc3giKSkKdF92aXNpdF9uZXV0cm9waGlsX3NpZ19zdmEKYGBgCgojIyBFb3Npbm9waGlsIHNhbXBsZXMKCmBgYHtyfQp0X3Zpc2l0X2Vvc2lub3BoaWxzIDwtIHNldF9leHB0X2NvbmRpdGlvbnModF9lb3Npbm9waGlscywgZmFjdD0idmlzaXRudW1iZXIiKQoKdF92aXNpdF9lb3Npbm9waGlsX2RlIDwtIGFsbF9wYWlyd2lzZSh0X3Zpc2l0X2Vvc2lub3BoaWxzLCBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfdmlzaXRfZW9zaW5vcGhpbF9kZQp0X3Zpc2l0X2Vvc2lub3BoaWxfdGFibGUgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgdF92aXNpdF9lb3Npbm9waGlsX2RlLCBrZWVwZXJzID0gdmlzaXRfY29udHJhc3RzLCBzY2FsZV9wID0gVFJVRQogIGV4Y2VsID0gZ2x1ZSgie3hsc3hfcHJlZml4fS9ERV9WaXNpdHMvRW9zaW5vcGhpbHMvdF9lb3Npbm9waGlsX3Zpc2l0X3RhYmxlX3N2YS12e3Zlcn0ueGxzeCIpKQp0X3Zpc2l0X2Vvc2lub3BoaWxfdGFibGUKdF92aXNpdF9lb3Npbm9waGlsX3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHRfdmlzaXRfZW9zaW5vcGhpbF90YWJsZSwKICBleGNlbCA9IGdsdWUoInt4bHN4X3ByZWZpeH0vREVfVmlzaXRzL0Vvc2lub3BoaWxzL3RfZW9zaW5vcGhpbF92aXNpdF9zaWdfc3ZhLXZ7dmVyfS54bHN4IikpCiMjIE5vIHNpZ25pZmljYW50IGdlbmVzIG9ic2VydmVkLgpgYGAKCiMgRXhwbG9yZSBST0MKCkFsZWphbmRybyBzaG93ZWQgc29tZSBST0MgY3VydmVzIGZvciBlb3Npbm9waGlsIGRhdGEgc2hvd2luZwpzZW5zaXRpdml0eSB2cy4gc3BlY2lmaWNpdHkgb2YgYSBjb3VwbGUgZ2VuZXMgd2hpY2ggd2VyZSBvYnNlcnZlZCBpbgp2MSBlb3Npbm9waGlscyB2cy4gYWxsLXRpbWVzIGVvc2lub3BoaWxzIGFjcm9zcyBjdXJlL2ZhaWwuICBJIGFtCmN1cmlvdXMgdG8gYmV0dGVyIHVuZGVyc3RhbmQgaG93IHRoaXMgd2FzIGRvbmUgYW5kIHdoYXQgdXRpbGl0eSBpdAptaWdodCBoYXZlIGluIG90aGVyIGNvbnRleHRzLgoKVG8gdGhhdCBlbmQsIEkgd2FudCB0byB0cnkgc29tZXRoaW5nIHNpbWlsYXIgbXlzZWxmLiBJbiBvcmRlciB0bwpwcm9wZXJseSBwZXJmb3JtIHRoZSBhbmFseXNpcyB3aXRoIHRoZXNlIHZhcmlvdXMgdG9vbHMsIEkgbmVlZCB0bwpyZWNvbmZpZ3VyZSB0aGUgZGF0YSBpbiBhIHByZXR0eSBzcGVjaWZpYyBmb3JtYXQ6CgoxLiAgU2luZ2xlIGRmIHdpdGggMSByb3cgcGVyIHNldCBvZiBvYnNlcnZhdGlvbnMgKHNhbXBsZSBpbiB0aGlzIGNhc2UKSSB0aGluaykKMi4gIFRoZSBvdXRjb21lIGNvbHVtbihzKSBuZWVkIHRvIGJlIDEgKG9yIG1vcmU/KSBtZXRhZGF0YSBmYWN0b3IocykKKGN1cmUvZmFpbCBvciBhIHBhc3RlMCBvZiByZWxldmFudCBxdWVyaWVzIChlb192MV9jdXJlLAplb192MTIzX2N1cmUsIGV0YykKMy4gIFRoZSBwcmVkaWN0b3IgY29sdW1uKHMpIGFyZSB0aGUgbWVhc3VyZW1lbnRzIChycGttIG9mIDEgb3IgbW9yZQpnZW5lcyksIDEgY29sdW1uIGVhY2ggZ2VuZS4KCklmIEkgaW50ZW5kIHRvIHVzZSB0aGlzIGZvciBvdXIgdHggZGF0YSwgSSB3aWxsIGxpa2VseSBuZWVkIGEgdXRpbGl0eQpmdW5jdGlvbiB0byBjcmVhdGUgdGhlIHByb3Blcmx5IGZvcm1hdHRlZCBpbnB1dCBkZi4KCkZvciB0aGUgcHVycG9zZXMgb2YgbXkgcGxheWluZywgSSB3aWxsIGNob29zZSB0aHJlZSBnZW5lcyBmcm9tIHRoZQplb3Npbm9waGlsIEMvRiB0YWJsZSwgb25lIHdoaWNoIGlzIHNpZ25pZmljYW50LCBvbmUgd2hpY2ggaXMgbm90LCBhbmQKYW4gYXJiaXRyYXJ5LgoKVGhlIGlucHV0IGdlbmVzIHdpbGwgdGhlcmVmb3JlIGJlIGNob3NlbiBmcm9tIHRoZSBkYXRhIHN0cnVjdHVyZToKdF9jZl9lb3Npbm9waGlsX3RhYmxlX3N2YToKCkVOU0cwMDAwMDE5ODE3OCwgRU5TRzAwMDAwMTc5MzQ0LCBFTlNHMDAwMDAxODI2MjgKCmBgYHtyfQplb19ycGttIDwtIG5vcm1hbGl6ZV9leHB0KHR2MV9lb3Npbm9waGlscywgY29udmVydCA9ICJycGttIiwgY29sdW1uID0gImNkc19sZW5ndGgiKQpgYGAKCiMgQW4gZXh0ZXJuYWwgZGF0YXNldAoKVGhpcyBwYXBlciBpcyBET0k6MTAuMTEyNi9zY2l0cmFuc2xtZWQuYWF4NDIwNAoKVmFyaWFibGUgZ2VuZSBleHByZXNzaW9uIGFuZCBwYXJhc2l0ZSBsb2FkIHByZWRpY3QgdHJlYXRtZW50IG91dGNvbWUgaW4gY3V0YW5lb3VzIGxlaXNobWFuaWFzaXMuCgpPbmUgcXVlcnkgZnJvbSBNYXJpYSBBZGVsYWlkYSBpcyB0byBzZWUgaG93IHRoaXMgZGF0YSBmaXRzIHdpdGggb3Vycy4KSSBoYXZlIHJlYWQgdGhpcyBwYXBlciBhIGNvdXBsZSBvZiB0aW1lcyBub3cgYW5kIEkgZ2V0IGNvbmZ1c2VkIG9uIGEKY291cGxlIG9mIHBvaW50cyBldmVyeSB0aW1lLCB3aGljaCBJIHdpbGwgZXhwbGFpbiBpbiBhIG1vbWVudC4gIFRoZQpleHBlcm1lbnRhbCBkZXNpZ24gaXMga2V5IHRvIG15IGNvbmZ1c2lvbiBhbmQga2V5IHRvIHdoYXQgSSB0aGluayBpcwpiZWluZyBtaXNzZWQgaW4gb3VyIGludGVycHJldGF0aW9uIG9mIHRoZSByZXN1bHRzOgoKMS4gIFRoZSBQQ0EgaXMgbm90IGN1cmUgdnMuIGZhaWwgYnV0IGhlYWx0aHkgc2tpbiB2cy4gQ0wgbGVzaW9uLiAgSXQKICAgIHNob3VsZCBiZSBzYWlkIHRoYXQgdGhlIHRleHQgbWFrZXMgdGhpcyBwZXJmZWN0bHkgY2xlYXIsIGJ1dCBJIGNhbgogICAgbmV2ZXIgc2VlbSB0byByZW1lbWJlciB0aGF0IHdoZW4gSSBnbyB0byBsb29rIGF0IHRoZSBkYXRhOwogICAgcHJlc3VtYWJseSBiZWNhdXNlIEkgYW0gdGhpbmtpbmcgcHJpbWFyaWx5IGFib3V0IGN1cmUvZmFpbC4KCiMjIE9ubHkgdGhlIFNjb3R0IGRhdGEKCmBgYHtyfQpleHRlcm5hbF9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KGV4dGVybmFsX2NmLCBmaWx0ZXIgPSBUUlVFLCBub3JtID0gInF1YW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJ0ID0gImNwbSIsIHRyYW5zZm9ybSA9ICJsb2cyIikKcGxvdF9wY2EoZXh0ZXJuYWxfbm9ybSkKZXh0ZXJuYWxfbmIgPC0gbm9ybWFsaXplX2V4cHQoZXh0ZXJuYWxfY2YsIGZpbHRlciA9IFRSVUUsIGJhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udmVydCA9ICJjcG0iLCB0cmFuc2Zvcm0gPSAibG9nMiIpCnBsb3RfcGNhKGV4dGVybmFsX25iKQoKZXh0ZXJuYWxfZGUgPC0gYWxsX3BhaXJ3aXNlKGV4dGVybmFsX2NmLCBmaWx0ZXIgPSBUUlVFLCBtZXRob2RzID0gbWV0aG9kcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX2JhdGNoID0gInN2YXNlcSIpCmV4dGVybmFsX2RlCmV4dGVybmFsX3RhYmxlIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogIGV4dGVybmFsX2RlLCBzY2FsZV9wID0gVFJVRSwKICBleGNlbCA9ICJleGNlbC9zY290dF90YWJsZS54bHN4IikKZXh0ZXJuYWxfdGFibGUKZXh0ZXJuYWxfc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoZXh0ZXJuYWxfdGFibGUsIGV4Y2VsID0gImV4Y2VsL3Njb3R0X3NpZy54bHN4IikKZXh0ZXJuYWxfc2lnCgpleHRlcm5hbF90b3AxMDAgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcyhleHRlcm5hbF90YWJsZSwgbiA9IDEwMCkKZXh0ZXJuYWxfdXAgPC0gZXh0ZXJuYWxfdG9wMTAwW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJmYWlsdXJlX3ZzX2N1cmUiXV0KZXh0ZXJuYWxfZG93biA8LSBleHRlcm5hbF90b3AxMDBbWyJkZXNlcSJdXVtbImRvd25zIl1dW1siZmFpbHVyZV92c19jdXJlIl1dCmBgYAoKIyMgQW4gZXhwbGljaXQgY29tcGFyaXNvbiBvZiBtZXRob2RzLgoKSSB0aGluayBJIGFtIGdldHRpbmcgYSBzaWduaWZpY2FudGx5IGRpZmZlcmVudCByZXN1bHQgZnJvbSBTY290dCwgc28gSQphbSBnb2luZyB0byBkbyBhbiBleHBsaWNpdCBzaWRlLWJ5LXNpZGUgY29tcGFyaXNvbiBvZiBvdXIgcmVzdWx0cyBhdAplYWNoIHN0ZXAuICBJbiBvcmRlciB0byBkbyB0aGlzLCBJIGFtIHVzaW5nIHRoZSBjYXBzdWxlIHRoZXkga2luZGx5CnByb3ZpZGVkIHdpdGggdGhlaXIgcHVibGljYXRpb24uCgpJIGFtIGNvcHkvcGFzdGluZyBtYXRlcmlhbCBmcm9tIHRoZWlyIHB1YmxpY2F0aW9uIHdpdGggc29tZQptb2RpZmljYXRpb24gd2hpY2ggSSB3aWxsIG5vdGUgYXMgSSBnby4KCkhlcmUgaXMgdGhlaXIgYmxvY2sgJ3IgcGFja2FnZXMnCgoqTm90ZS9TcG9pbGVyIGFsZXJ0KjogSXQgYWN0dWFsbHkgdHVybnMgb3V0IG91ciByZXN1bHRzIGFyZSBiYXNpY2FsbHkKcmVsYXRpdmVseSBzaW1pbGFyLCBJIGp1c3QgZGlkbid0IHVuZGVyc3RhbmQgd2hhdCBjb21wYXJpc29ucyBhcmUKYWN0dWFsbHkgaW4gcGFwZXIgdnMgdGhvc2UgSSBoYXZlIHByaW1hcnkgaW50ZXJlc3QuICBJbiBhZGRpdGlvbiwgd2UKaGFuZGxlZCBnZW5lIElEcyBkaWZmZXJlbnRseSAoZ2VuZSBjYXJkIHZzLiBFbnNlbWJsSUQpIHdoaWNoIGhhcyBhCnN1cnByaXNpbmdseSBiaWcgZWZmZWN0LgoKT2gsIEkganVzdCByZWFsaXplZCB0aGF0IHdoZW4gSSBkaWQgdGhlc2UgYW5hbHlzZXMsIEkgZGlkIHRoZW0gaW4gYQpjb21wbGV0ZWx5IHNlcGFyYXRlIHRyZWUgYW5kIGNvbXBhcmVkIHRoZSByZXN1bHRzIHBvc3QtZmFjdG8uICBUaGlzCmFzc3VtcHRpb24gcmVtYWlucyBpbiB0aGlzIGRvY3VtZW50IGFuZCB0aGVyZWZvcmUgaXMgdW5saWtlbHkgdG8gd29yawpwcm9wZXJseSBpbiB0aGUgY29udGFpbmVyaXplZCBlbnZpcm9ubWVudCBJIGFtIGF0dGVtcHRpbmcgdG8gY3JlYXRlLgpHaXZlbiB0aGF0IHRoZSBwcmltYXJ5IGdvYWwgb2YgdGhpcyBzZWN0aW9uIGlzIHRvIHNob3cgdG8gbXlzZWxmIHRoYXQKSSBjb21wYXJlZCB0aGUgdHdvIGRhdGFzZXRzIGFzIHRob3JvdWdobHkgYXMgSSBjb3VsZCwgcGVyaGFwcyBJIHNob3VsZApqdXN0IGRpc2FibGUgdGhlbSBmb3IgdGhlIGNvbnRhaW5lciBhbmQgYWxsb3cgdGhlIHJlYWRlciB0byBwZXJmb3JtCnRoZSBleGVyY2lzZSBkZS1ub3ZvLgoKYGBge3IsIGV2YWw9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdndGhlbWVzKQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KGVkZ2VSKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeSh2ZWdhbikKbGlicmFyeShEVCkKbGlicmFyeSh0eGltcG9ydCkKbGlicmFyeShncGxvdHMpCmxpYnJhcnkoRmluQ2FsKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoZ3QpCmxpYnJhcnkoZ2dFeHRyYSkKbGlicmFyeShFbnNEYi5Ic2FwaWVucy52ODYpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KGdncHVicikKYGBgCgpJIGhhdmUgYSBzZXBhcmF0ZSB0cmVlIGluIHdoaWNoIEkgY29waWVkIHRoZSBjYXBzdWxlIGFuZCBkYXRhLiAgSQpwZXJmb3JtZWQgZXhhY3RseSB0aGVpciBzdGVwcyBrYWxsaXN0byBxdWFudCBzdGVwcyB3aXRoaW4gaXQgYW5kIHB1dAp0aGUgb3V0cHV0IGRhdGEgaW50byB0aGUgc2FtZSBwbGFjZSB3aXRoaW4gaXQuICBJIGRpZCBjaGFuZ2UgdGhlCmNvbW1hbmRzIHNsaWdodGx5IGJlY2F1c2UgSSBkb3dubG9hZGVkIHRoZSBmaWxlcyBmcm9tIFNSQSBhbmQgc28gZG9uJ3QKaGF2ZSB0aGVtIHdpdGggbmFtZXMgbGlrZSAnaG9zdF9DTDAxJywgYnV0IGluc3RlYWQgJ1BSSk5BLi4uJy4gIEJ1dAp0aGUgc2FtcGxlcyBhcmUgaW4gdGhlIHNhbWUgb3JkZXIsIHNvIEkgc2VudCB0aGUgb3V0cHV0IGZpbGVzIHRvIHRoZQpzYW1lIGZpbmFsIGZpbGVuYW1lcy4gIEhlcmUgaXMgYW4gZXhhbXBsZSBmcm9tIHRoZSBmaXJzdCBzYW1wbGU6CgpgYGB7YmFzaCBmaXJzdF9zYW1wbGUsIGV2YWw9RkFMU0V9CmNkIHByZXByb2Nlc3NpbmcKbW9kdWxlIGFkZCBrYWxsaXN0bwprYWxsaXN0byBpbmRleCAtaSBIb21vX3NhcGllbnMuR1JDaDM4LmNkbmEuYWxsLkluZGV4IEhvbW9fc2FwaWVucy5HUkNoMzguY2RuYS5hbGwuZmEKIyBNYXAgcmVhZHMgdG8gdGhlIGluZGV4ZWQgcmVmZXJlbmNlIHRyYW5zY3JpcHRvbWUgZm9yIEhPU1QKIyBmaXJzdCB0aGUgaGVhbHRoeSBzdWJqZWN0cyAoSFMpCmV4cG9ydCBMRVNTID0gJy0tYnVmZmVycyAwIC1CJwprYWxsaXN0byBxdWFudCAtaSBIb21vX3NhcGllbnMuR1JDaDM4LmNkbmEuYWxsLkluZGV4IC1vIGhvc3RfSFMwMSAtdCAyNCAtYiA2MCBcCiAgICAgICAgIC0tc2luZ2xlIC1sIDI1MCAtcyAzMCA8KGxlc3MgU1JSODY2ODc1NS8qLXRyaW1tZWQuZmFzdHEueHopIDI+aG9zdF9IUzAxLmxvZyAxPiYyICYKYGBgCgojIyBCbG9jayAnc2FtcGxlX2luZm8nCgpJIGFtIGdvaW5nIHRvIGNoYW5nZSB0aGUgcGF0aCB2ZXJ5IHNsaWdodGx5IGluIHRoZSBmb2xsb3dpbmcgYmxvY2sKc2ltcGx5IGJlY2F1c2UgSSBwdXQgdGhlIGNhcHN1bGUgaW4gYSBzZXBhcmF0ZSBkaXJlY3RvcnkgYW5kIGRvIG5vdAp3YW50IHRvIGNvcHkgaXQgaGVyZS4gIE90aGVyd2lzZSBpdCBpcyB1bm1vZGlmaWVkLiAgQWxzbywgdGhlIGZ1bmN0aW9uCmd0Ojp0YWJfaGVhZGVyKCkgYW5ub3lzIHRoZSBjcmFwIG91dCBvZiBtZS4KCmBgYHtyLCBldmFsPUZBTFNFfQppbXBvcnQgPC0gcmVhZF90c3YoIi4uL3Njb3R0XzIwMTkvY2Fwc3VsZS02NTM0MDE2L2RhdGEvc3R1ZHlkZXNpZ24udHh0IikKaW1wb3J0ICU+JSBkcGx5cjo6ZmlsdGVyKGRpc2Vhc2UgPT0gImN1dGFuZW91cyIpICU+JQogIGRwbHlyOjpzZWxlY3QoLTIpICU+JSAgZ3QoKSAlPiUKICB0YWJfaGVhZGVyKHRpdGxlID0gbWQoIkNsaW5pY2FsIG1ldGFkYXRhIGZyb20gcGF0aWVudHMgd2l0aCBjdXRhbmVvdXMgbGVpc2htYW5pYXNpcyAoQ0wpIiksCiAgICAgICAgICAgICBzdWJ0aXRsZSA9IG1kKCJgKG49MjEpYCIpKSAlPiUgIGNvbHNfYWxpZ24oYWxpZ24gPSAiY2VudGVyIiwgY29sdW1ucyA9IFRSVUUpCnRhcmdldHMubGVzaW9uIDwtIGltcG9ydAp0YXJnZXRzLm9ubHlwYXRpZW50cyA8LSB0YXJnZXRzLmxlc2lvbls4OjI4LF0gIyBvbmx5IENMIGxlc2lvbnMgKG49MjEpCgojIE1ha2luZyBmYWN0b3JzIHRoYXQgd2lsbCBiZSB1c2VkIGZvciBwYWlyd2lzZSBjb21wYXJpc29uczoKIyBIUyB2cy4gQ0wgbGVzaW9ucyBhcyBhIGZhY3RvcjoKZGlzZWFzZS5sZXNpb24gPC0gZmFjdG9yKHRhcmdldHMubGVzaW9uJGRpc2Vhc2UpCiMgQ3VyZSB2cy4gRmFpbHVyZSBsZXNpb25zIGFzIGEgZmFjdG9yOgp0cmVhdG1lbnQubGVzaW9uIDwtIGZhY3Rvcih0YXJnZXRzLm9ubHlwYXRpZW50cyR0cmVhdG1lbnRfb3V0Y29tZSkKYGBgCgojIyBJbXBvcnRpbmcgdGhlIGRhdGEgYW5kIGFubm90YXRpb25zCgpUaGV5IGRpZCB1c2UgYSBzbGlnaHRseSBkaWZmZXJlbnQgYW5ub3RhdGlvbiBzZXQsIEVuc2VtYmwgcmV2aXNpb24gODYuCk9uY2UgYWdhaW4gSSBhbSBtb2RpZnlpbmcgdGhlIHBhdGhzIHNsaWdodGx5IHRvIHJlZmxlY3Qgd2hlcmUgSSBwdXQKdGhlIGNhcHN1bGUuCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBjYXB0dXJpbmcgRW5zZW1ibCB0cmFuc2NyaXB0IElEcyAodHgpIGFuZCBnZW5lIHN5bWJvbHMgKCJnZW5lX25hbWUiKSBmcm9tCiMgRW5zRGIuSHNhcGllbnMudjg2IGFubm90YXRpb24gcGFja2FnZQpUeCA8LSBhcy5kYXRhLmZyYW1lKHRyYW5zY3JpcHRzKEVuc0RiLkhzYXBpZW5zLnY4NiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5zPWMobGlzdENvbHVtbnMoRW5zRGIuSHNhcGllbnMudjg2LCAidHgiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdlbmVfbmFtZSIpKSkKClR4IDwtIGRwbHlyOjpyZW5hbWUoVHgsIHRhcmdldF9pZCA9IHR4X2lkKQpyb3cubmFtZXMoVHgpIDwtIE5VTEwKVHggPC0gVHhbLGMoNiwxMildCgojIGdldHRpbmcgZmlsZSBwYXRocyBmb3IgS2FsbGlzdG8gb3V0cHV0cwpwYXRocy5hbGwgPC0gZmlsZS5wYXRoKCIuLi9zY290dF8yMDE5L2NhcHN1bGUtNjUzNDAxNi9kYXRhL3JlYWRNYXBwaW5nL2h1bWFuIiwgdGFyZ2V0cy5sZXNpb24kc2FtcGxlLCAiYWJ1bmRhbmNlLmg1IikKcGF0aHMucGF0aWVudHMgPC0gZmlsZS5wYXRoKCIuLi9zY290dF8yMDE5L2NhcHN1bGUtNjUzNDAxNi9kYXRhL3JlYWRNYXBwaW5nL2h1bWFuIiwgdGFyZ2V0cy5vbmx5cGF0aWVudHMkc2FtcGxlLCAiYWJ1bmRhbmNlLmg1IikKCiMgaW1wb3J0aW5nIC5oNSBLYWxsaXN0byBkYXRhIGFuZCBjb2xsYXBzaW5nIHRyYW5zY3JpcHQtbGV2ZWwgZGF0YSB0byBnZW5lcwpUeGkubGVzaW9uLmNvZGluZyA8LSB0eGltcG9ydChwYXRocy5hbGwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAia2FsbGlzdG8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eDJnZW5lID0gVHgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR4T3V0ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlnbm9yZVR4VmVyc2lvbiA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvdW50c0Zyb21BYnVuZGFuY2UgPSAibGVuZ3RoU2NhbGVkVFBNIikKCiMgaW1wb3J0aW5nIGFnYWluZywgYnV0IHRoaXMgdGltZSBqdXN0IHRoZSBDTCBwYXRpZW50cwpUeGkubGVzaW9uLmNvZGluZy5vbmx5cGF0aWVudHMgPC0gdHhpbXBvcnQocGF0aHMucGF0aWVudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gImthbGxpc3RvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR4MmdlbmUgPSBUeCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR4T3V0ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmVUeFZlcnNpb24gPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY291bnRzRnJvbUFidW5kYW5jZSA9ICJsZW5ndGhTY2FsZWRUUE0iKQpgYGAKCiMjIEZpbHRlcmluZyBhbmQgbm9ybWFsaXphdGlvbgoKVGhlIGJsb2NrICd2aXN1YWxpemF0aW9uRGF0YXNldHMnIGZvbGxvd3MgdW5jaGFuZ2VkLiAgSW4gdGhlIG5leHQKYmxvY2sgSSB3aWxsIGFkZCBhbm90aGVyIHBsb3Qgb3IgcGVyaGFwcyAyCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBGaXJzdCBtYWtlIGEgREdFTGlzdCBmcm9tIHRoZSBjb3VudHM6ClR4aS5sZXNpb24uY29kaW5nLkRHRUxpc3QgPC0gREdFTGlzdChUeGkubGVzaW9uLmNvZGluZyRjb3VudHMpCmNvbG5hbWVzKFR4aS5sZXNpb24uY29kaW5nLkRHRUxpc3QkY291bnRzKSA8LSB0YXJnZXRzLmxlc2lvbiRzYW1wbGUKY29sbmFtZXMoVHhpLmxlc2lvbi5jb2RpbmckY291bnRzKSA8LSB0YXJnZXRzLmxlc2lvbiRzYW1wbGUKClR4aS5sZXNpb24uY29kaW5nLkRHRUxpc3QuT1AgPC0gREdFTGlzdChUeGkubGVzaW9uLmNvZGluZy5vbmx5cGF0aWVudHMkY291bnRzKQpjb2xuYW1lcyhUeGkubGVzaW9uLmNvZGluZy5ER0VMaXN0Lk9QKSA8LSB0YXJnZXRzLm9ubHlwYXRpZW50cyRzYW1wbGUKCiMgQ29udmVydCB0byBjb3VudHMgcGVyIG1pbGxpb246ClR4aS5sZXNpb24uY29kaW5nLkRHRUxpc3QuY3BtIDwtIGVkZ2VSOjpjcG0oVHhpLmxlc2lvbi5jb2RpbmcuREdFTGlzdCwgbG9nID0gVFJVRSkKVHhpLmxlc2lvbi5jb2RpbmcuREdFTGlzdC5PUC5jcG0gPC0gZWRnZVI6OmNwbShUeGkubGVzaW9uLmNvZGluZy5ER0VMaXN0Lk9QLCBsb2cgPSBUUlVFKQoKa2VlcGVycy5jb2RpbmcgPC0gcm93U3VtcyhUeGkubGVzaW9uLmNvZGluZy5ER0VMaXN0LmNwbT4xKT49NwprZWVwZXJzLmNvZGluZy5PUCA8LSByb3dTdW1zKFR4aS5sZXNpb24uY29kaW5nLkRHRUxpc3QuT1AuY3BtPjEpPj03CgpUeGkubGVzaW9uLmNvZGluZy5ER0VMaXN0LmZpbHRlcmVkIDwtIFR4aS5sZXNpb24uY29kaW5nLkRHRUxpc3Rba2VlcGVycy5jb2RpbmcsXQpUeGkubGVzaW9uLmNvZGluZy5ER0VMaXN0Lk9QLmZpbHRlcmVkIDwtIFR4aS5sZXNpb24uY29kaW5nLkRHRUxpc3QuT1Bba2VlcGVycy5jb2RpbmcuT1AsXQoKIyBjb252ZXJ0IGJhY2sgdG8gY3BtOgpUeGkubGVzaW9uLmNvZGluZy5ER0VMaXN0LkxvZ0NQTS5maWx0ZXJlZCA8LSBlZGdlUjo6Y3BtKFR4aS5sZXNpb24uY29kaW5nLkRHRUxpc3QuZmlsdGVyZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nPVRSVUUpClR4aS5sZXNpb24uY29kaW5nLkRHRUxpc3QuTG9nQ1BNLk9QLmZpbHRlcmVkIDwtIGVkZ2VSOjpjcG0oVHhpLmxlc2lvbi5jb2RpbmcuREdFTGlzdC5PUC5maWx0ZXJlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2c9VFJVRSkKCiMgTm9ybWFsaXppbmcgZGF0YToKY2FsY05vcm0xIDwtIGNhbGNOb3JtRmFjdG9ycyhUeGkubGVzaW9uLmNvZGluZy5ER0VMaXN0LmZpbHRlcmVkLCBtZXRob2QgPSAiVE1NIikKY2FsY05vcm0yIDwtIGNhbGNOb3JtRmFjdG9ycyhUeGkubGVzaW9uLmNvZGluZy5ER0VMaXN0Lk9QLmZpbHRlcmVkLCBtZXRob2QgPSAiVE1NIikKClR4aS5sZXNpb24uY29kaW5nLkRHRUxpc3QuTG9nQ1BNLmZpbHRlcmVkLm5vcm0gPC0gZWRnZVI6OmNwbShjYWxjTm9ybTEsIGxvZz1UUlVFKQpjb2xuYW1lcyhUeGkubGVzaW9uLmNvZGluZy5ER0VMaXN0LkxvZ0NQTS5maWx0ZXJlZC5ub3JtKSA8LSB0YXJnZXRzLmxlc2lvbiRzYW1wbGUKVHhpLmxlc2lvbi5jb2RpbmcuREdFTGlzdC5PUC5Mb2dDUE0uZmlsdGVyZWQubm9ybSA8LSBlZGdlUjo6Y3BtKGNhbGNOb3JtMiwgbG9nPVRSVUUpCmNvbG5hbWVzKFR4aS5sZXNpb24uY29kaW5nLkRHRUxpc3QuT1AuTG9nQ1BNLmZpbHRlcmVkLm5vcm0pIDwtIHRhcmdldHMub25seXBhdGllbnRzJHNhbXBsZQojIFJhdyBkYXRhc2V0OgpWMSA8LSBhcy5kYXRhLmZyYW1lKFR4aS5sZXNpb24uY29kaW5nLkRHRUxpc3QuY3BtKQpjb2xuYW1lcyhWMSkgPC0gdGFyZ2V0cy5sZXNpb24kc2FtcGxlClYxIDwtIG1lbHQoVjEpCmNvbG5hbWVzKFYxKSA8LSBjKCJzYW1wbGUiLCJleHByZXNzaW9uIikKCiMgRmlsdGVyZWQgZGF0YXNldDoKVjEuMSA8LSBhcy5kYXRhLmZyYW1lKFR4aS5sZXNpb24uY29kaW5nLkRHRUxpc3QuTG9nQ1BNLmZpbHRlcmVkKQpjb2xuYW1lcyhWMS4xKSA8LSB0YXJnZXRzLmxlc2lvbiRzYW1wbGUKVjEuMSA8LSBtZWx0KFYxLjEpCmNvbG5hbWVzKFYxLjEpIDwtIGMoInNhbXBsZSIsImV4cHJlc3Npb24iKQoKIyBGaWx0ZXJlZC1ub3JtYWxpemVkIGRhdGFzZXQ6ClYxLjEuMSA8LSBhcy5kYXRhLmZyYW1lKFR4aS5sZXNpb24uY29kaW5nLkRHRUxpc3QuTG9nQ1BNLmZpbHRlcmVkLm5vcm0pCmNvbG5hbWVzKFYxLjEuMSkgPC0gdGFyZ2V0cy5sZXNpb24kc2FtcGxlClYxLjEuMSA8LSBtZWx0KFYxLjEuMSkKY29sbmFtZXMoVjEuMS4xKSA8LSBjKCJzYW1wbGUiLCJleHByZXNzaW9uIikKCiMgcGxvdHRpbmc6CmdncGxvdChWMSwgYWVzKHg9c2FtcGxlLCB5PWV4cHJlc3Npb24sIGZpbGw9c2FtcGxlKSkgKwogIGdlb21fdmlvbGluKHRyaW0gPSBUUlVFLCBzaG93LmxlZ2VuZCA9IFRSVUUpICsKICBzdGF0X3N1bW1hcnkoZnVuLnkgPSAibWVkaWFuIiwgZ2VvbSA9ICJwb2ludCIsIHNoYXBlID0gOTUsIHNpemUgPSAxMCwgY29sb3IgPSAiYmxhY2siKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTcpLAogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT01KSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpKSArCiAgZ2d0aXRsZSgiUmF3IGRhdGFzZXQiKSArCiAgZ2dwbG90KFYxLjEsIGFlcyh4PXNhbXBsZSwgeT1leHByZXNzaW9uLCBmaWxsPXNhbXBsZSkpICsKICBnZW9tX3Zpb2xpbih0cmltID0gVFJVRSwgc2hvdy5sZWdlbmQgPSBUUlVFKSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi55ID0gIm1lZGlhbiIsIGdlb20gPSAicG9pbnQiLCBzaGFwZSA9IDk1LCBzaXplID0gMTAsIGNvbG9yID0gImJsYWNrIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT03KSwKICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NSksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSkgKwogIGdndGl0bGUoIkZpbHRlcmVkIGRhdGFzZXQiKSArCiAgZ2dwbG90KFYxLjEuMSwgYWVzKHg9c2FtcGxlLCB5PWV4cHJlc3Npb24sIGZpbGw9c2FtcGxlKSkgKwogIGdlb21fdmlvbGluKHRyaW0gPSBUUlVFLCBzaG93LmxlZ2VuZCA9IFRSVUUpICsKICBzdGF0X3N1bW1hcnkoZnVuLnkgPSAibWVkaWFuIiwgZ2VvbSA9ICJwb2ludCIsIHNoYXBlID0gOTUsIHNpemUgPSAxMCwgY29sb3IgPSAiYmxhY2siKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTcpLAogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT01KSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpKSArCiAgZ2d0aXRsZSgiRmlsdGVyZWQgYW5kIG5vcm1hbGl6ZWQgZGF0YXNldCIpCmBgYAoKIyMgVGhlIHVuZmlsdGVyZWQgZGF0YQoKVGhlIGZvbGxvd2luZyBibG9jayBpbiB0aGVpciBkYXRhc2V0IHJlY3JlYXRlZCB0aGUgbWF0cml4IHdpdGhvdXQKZmlsdGVyaW5nIGFuZCB3aWxsIHVzZSB0aGF0IGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4gIEl0IGlzIGEKbGl0dGxlIGhhcmQgdG8gZm9sbG93IGZvciBtZSBiZWNhdXNlIHRoZXkgc3Vic2V0IGJhc2VkIG9uIHRoZSBzYW1wbGUKbnVtYmVycyAoOCB0byAyOCwgd2hpY2ggaWYgSSBhbSBub3QgbWlzdGFrZW4ganVzdCBkcm9wcyB0aGUgaGVhbHRoeQpzYW1wbGVzKS4KCmBgYHtyLCBldmFsPUZBTFNFfQpEYXRhTm90RmlsdGVyZWRfTm9ybV9PUCA8LSBjYWxjTm9ybUZhY3RvcnMoVHhpLmxlc2lvbi5jb2RpbmcuREdFTGlzdFssODoyOF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiVE1NIikKRGF0YU5vdEZpbHRlcmVkX05vcm1fbG9nMkNQTV9PUCA8LSBlZGdlUjo6Y3BtKERhdGFOb3RGaWx0ZXJlZF9Ob3JtX09QLCBsb2c9VFJVRSkKY29sbmFtZXMoRGF0YU5vdEZpbHRlcmVkX05vcm1fbG9nMkNQTV9PUCkgPC0gdGFyZ2V0cy5vbmx5cGF0aWVudHMkc2FtcGxlCkNQTV9ub3JtRGF0YV9ub3RmaWx0ZXJlZF9PUCA8LSAyXihEYXRhTm90RmlsdGVyZWRfTm9ybV9sb2cyQ1BNX09QKQojdW5jb21tZW50IHRoZSBuZXh0IGxpbmUgdG8gcHJvZHVjZSByYXcgZGF0YSB0aGF0IHdhcyB1cGxvYWRlZCB0byB0aGUgR2VuZSBFeHByZXNzaW9uIE9tbmlidXMgKEdFTykgZm9yIHB1YmxpY2F0aW9uLgojd3JpdGUudGFibGUoVHhpLmxlc2lvbi5jb2RpbmckY291bnRzLCBmaWxlID0gIkFtb3JpbV9HRU9fcmF3LnR4dCIsIHNlcCA9ICJcdCIsIHF1b3RlID0gRkFMU0UpCgojIEluY2x1ZGluZyBhbGwgdGhlIGluZGl2aWR1YWxzIChIUyBhbmQgQ0wgcGF0aWVudHMpIGZvciBwdWJsaWMgZG9tYWluIHN1Ym1pc3Npb246CkRhdGFOb3RGaWx0ZXJlZF9Ob3JtIDwtIGNhbGNOb3JtRmFjdG9ycyhUeGkubGVzaW9uLmNvZGluZy5ER0VMaXN0LCBtZXRob2QgPSAiVE1NIikKRGF0YU5vdEZpbHRlcmVkX05vcm1fbG9nMkNQTSA8LSBlZGdlUjo6Y3BtKERhdGFOb3RGaWx0ZXJlZF9Ob3JtLCBsb2c9VFJVRSkKY29sbmFtZXMoRGF0YU5vdEZpbHRlcmVkX05vcm1fbG9nMkNQTSkgPC0gdGFyZ2V0cy5sZXNpb24kc2FtcGxlCkNQTV9ub3JtRGF0YV9ub3RmaWx0ZXJlZCA8LSAyXihEYXRhTm90RmlsdGVyZWRfTm9ybV9sb2cyQ1BNKQojdW5jb21tZW50IHRoZSBuZXh0IGxpbmUgdG8gcHJvZHVjZSB0aGUgbm9ybWFsaXplZCBkYXRhIGZpbGUgdGhhdCB3YXMgdXBsb2FkZWQgdG8gdGhlIEdlbmUgRXhwcmVzc2lvbiBPbW5pYnVzIChHRU8pIGZvciBwdWJsaWNhdGlvbi4KI3dyaXRlLnRhYmxlKERhdGFOb3RGaWx0ZXJlZF9Ob3JtX2xvZzJDUE0sICJBbW9yaW1fR0VPX25vcm1hbGl6ZWQudHh0Iiwgc2VwID0gIlx0IiwgcXVvdGUgPSBGQUxTRSkKYGBgCgojIyBUaGUgc2NvdHQgZXhwbG9yYXRvcnkgYW5hbHlzaXMKClRoZSBmb2xsb3dpbmcgYmxvY2sgZ2VuZXJhdGVkIGEgY291cGxlIG9mIHRoZSBmaWd1cmVzIGluIHRoZSBwYXBlciBhbmQKY29tcHJpc2UgYSBwcmV0dHkgc3RyYWlnaHRmb3J3YXJkIFBDQS4gIEkgYW0gZ29pbmcgdG8gbWFrZSBhIGZvbGxvd2luZwpibG9jayBjb250YWluaW5nIHRoZSBzYW1lIGltYWdlIHdpdGggdGhlIGN1cmUvZmFpbCB2aXN1YWxpemF0aW9uIHVzaW5nCnRoZSBzYW1lIG1ldGhvZC9kYXRhLgoKYGBge3IsIGV2YWw9RkFMU0V9CnBjYS5yZXMgPC0gcHJjb21wKHQoVHhpLmxlc2lvbi5jb2RpbmcuREdFTGlzdC5Mb2dDUE0uZmlsdGVyZWQubm9ybSksIHNjYWxlLj1GLCByZXR4PVQpCnBjLnZhciA8LSBwY2EucmVzJHNkZXZeMgpwYy5wZXIgPC0gcm91bmQocGMudmFyL3N1bShwYy52YXIpKjEwMCwgMSkKZGF0YS5mcmFtZSA8LSBhcy5kYXRhLmZyYW1lKHBjYS5yZXMkeCkKCiMgQ2FsY3VsYXRlIGRpc3RhbmNlIGJldHdlZW4gc2FtcGxlcyBieSBwZXJtYW5vdmE6CmFsbHNhbXBsZXMuZGlzdCA8LSB2ZWdkaXN0KHQoMl5UeGkubGVzaW9uLmNvZGluZy5ER0VMaXN0LkxvZ0NQTS5maWx0ZXJlZC5ub3JtKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImJyYXkiKQoKdmVnYW4gPC0gYWRvbmlzMihhbGxzYW1wbGVzLmRpc3R+dGFyZ2V0cy5sZXNpb24kZGlzZWFzZSwKICAgICAgICAgICAgICAgICBkYXRhPXRhcmdldHMubGVzaW9uLAogICAgICAgICAgICAgICAgIHBlcm11dGF0aW9ucyA9IDk5OSwgbWV0aG9kPSJicmF5IikKCnRhcmdldHMubGVzaW9uJGRpc2Vhc2UKZ2dwbG90KGRhdGEuZnJhbWUsIGFlcyh4PVBDMSwgeT1QQzIsIGNvbG9yPWZhY3Rvcih0YXJnZXRzLmxlc2lvbiRkaXNlYXNlKSkpICsKICBnZW9tX3BvaW50KHNpemU9NSwgc2hhcGU9MjApICsKICB0aGVtZV9jYWxjKCkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1LCB2anVzdCA9IDAuNSksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiIzA3M0Y4MCIsIiNFQjUxMkMiKSkgKwogIGFubm90YXRlKCJ0ZXh0IiwgeD0tNTAsIHk9ODAsIGxhYmVsPXBhc3RlKCJQZXJtYW5vdmEgUHIoPkYpID0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlZ2FuWzEsNV0pLCBzaXplPTMsIGZvbnRmYWNlPSJib2xkIikgKwogIHhsYWIocGFzdGUoIlBDMSAtIixwYy5wZXJbMV0sIiUiKSkgKwogIHlsYWIocGFzdGUoIlBDMiAtIixwYy5wZXJbMl0sIiUiKSkgKwogIHhsaW0oLTIwMCwxMTApCmBgYAoKIyMjIE15IG1vc3Qgc2ltaWxhciBwY2EKCkkganVzdCByZWFsaXplZCB0aGF0IHNvbWV3aGVyZSBhbG9uZyB0aGUgd2F5IGluIGNyZWF0aW5nIHRoaXMKY29udGFpbmVyLCBJIG1lc3NlZCB1cCB0aGlzIGFuYWx5c2lzIHByZXR0eSBiYWRseToKCjEuICBJIGRyb3BwZWQgdGhlIDcgY29udHJvbCBzYW1wbGVzLgoyLiAgSSBhbSBjb21wYXJpbmcgY3VyZS9mYWlsIGJ1dCB0aGVzZSBhbmFseXNlcyBhcmUgYWxsCiAgICBjb250cm9sL2N1dGFuZW91cy4KCldoZW4gSSBvcmlnaW5hbGx5IGRpZCB0aGlzIG9uIG15IHdvcmtzdGF0aW9uIEkgaGFkIGFuIGFjdHVhbCAxOjEKY29tcGFyaXNvbiBhbmQgc2F3IHRoYXQgb3VyIHJlc3VsdHMgd2VyZSBxdWl0ZSBzaW1pbGFyLiAgSSBuZWVkIHRvCmJyaW5nIHRoYXQgYmFjayBpbnRvIHRoaXMgaW4gb3JkZXIgdG8gc2hvdyB0aGF0IG5laXRoZXIgd2Ugbm9yIHRoZXkKYXJlIGNyYXp5IHBlb3BsZS4KCkVpdGhlciB3YXksIEkgdGhpbmsgdGhlIG1haW4gdGFrZWF3YXkgaXMgdGhhdCB0aGVpciBkYXRhc2V0IGRvZXMgbm90CnNwZW5kIG11Y2ggdGltZSBsb29raW5nIGF0IGN1cmUvZmFpbCBidXQgaW5zdGVhZCBjb250cm9sL2luZmVjdGVkIGZvcgphIHJlYXNvbi4KCk5vdGUsIHRoZSBmdW4gYXNwZWN0cyBvZiB0aGUgZXhwZXJpbWVudCAodGltZSB0byBjdXJlLCBzaXplIG9mIGxlc2lvbiwKZXRjKSBhcmUgbm90IGFubm90YXRlZCBpbiB0aGUgbWV0YWRhdGEgcHJvdmlkZWQgYnkgU1JBLCBidXQgaW5zdGVhZAptYXkgYmUgZm91bmQgaW4gdGhlIGNhcHN1bGUga2luZGx5IHByb3ZpZGVkIGJ5IHRoZSBsYWIuICBBcyBhIHJlc3VsdCwKSSBjb3BpZWQgdGhhdCBmaWxlIGludG8gdGhlIHNhbXBsZV9zaGVldHMvIGRpcmVjdG9yeSBhbmQgaGF2ZSBhZGRlZCBpdAp0byB0aGUgZXhwcmVzc2lvbnNldC4gIFRoZXJlIGlzIGFuIGltcG9ydGFudCBjYXZlYXQsIHRob3VnaDogSSBkaWQgbm90CmluY2x1ZGUgdGhlIG5vbi1kaXNlYXNlZCBzYW1wbGVzIGZvciB0aGlzIGNvbXBhcmlzb247IGFzIGEgcmVzdWx0IHRoZQpkaXNlYXNlIG1ldGFkYXRhIGZhY3RvciBpcyBib3JpbmcgKGUuZy4gaXQgaXMgb25seSBjdXRhbmVvdXMpLgoKYGBge3J9CmV4dGVybmFsX2NmW1siYWNjZXNzaW9uIl1dIDwtIHBEYXRhKGV4dGVybmFsX2NmKVtbInNhbXBsZSJdXQpkaXNlYXNlX2ZhY3RvciA8LSBwRGF0YShleHRlcm5hbF9jZilbWyJkaXNlYXNlIl1dCnRhYmxlKGRpc2Vhc2VfZmFjdG9yKQpleHRlcm5hbF9kaXNlYXNlIDwtIHNldF9leHB0X2NvbmRpdGlvbnMoZXh0ZXJuYWxfY2YsIGZhY3QgPSBkaXNlYXNlX2ZhY3RvcikKCmV4dGVybmFsX2wyY3BtIDwtIG5vcm1hbGl6ZV9leHB0KGV4dGVybmFsX2NmLCBmaWx0ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnZlcnQgPSAiY3BtIiwgdHJhbnNmb3JtID0gImxvZzIiKQpwbG90X3BjYShleHRlcm5hbF9sMmNwbSwgcGxvdF9sYWJlbHMgPSAicmVwZWwiKQpgYGAKClVzZSB0aGUgZm9sbG93aW5nIGJsb2NrIGlmIHlvdSB3aXNoIHRvIGJyaW5nIHRvZ2V0aGVyIFNSQS1kb3dubG9hZGVkCmRhdGEgd2l0aCB0aGUgZXhwZXJpbWVudGFsIGRlc2lnbiBmcm9tIHRoZSBTY290dCBwYXBlci4gIEl0IHJlcXVpcmVzCnJ1bm5pbmcgdGhlIGJsb2NrcyBhYm92ZSBpbiB3aGljaCBJIGxvYWRlZCB0aGUgY2Fwc3VsZS1kZXJpdmVkCm1ldGFkYXRhLgoKYGBge3IsIGV2YWw9RkFMU0V9CnRlc3QgPC0gcERhdGEoZXh0ZXJuYWxfY2YpCnRlc3RfaW1wb3J0IDwtIGFzLmRhdGEuZnJhbWUoaW1wb3J0KQp0ZXN0X2ltcG9ydFtbImFjY2Vzc2lvbiJdXSA8LSBwRGF0YShleHRlcm5hbF9jZltbImFjY2Vzc2lvbiJdXSkKdGVzdF9tZXJnZWQgPC0gbWVyZ2UodGVzdCwgaW1wb3J0LCBieSA9ICJhY2Nlc3Npb24iKQpgYGAKClRoaXMgaXMgcmVhbCBjb21wYXJpc29uIHBvaW50IHRvIHRoZWlyIGN1cmUvZmFpbCBhbmFseXNpcy4KCiMjIEN1cmUvRmFpbCBQQ0EgdXNpbmcgdGhlIHNhbWUgcHJjb21wIHJlc3VsdAoKSSBhbSBqdXN0IGNvcHkvcGFzdGluZyB0aGVpciBjb2RlIGFnYWluLCBidXQgY2hhbmdpbmcgdGhlIGNvbG9yIGZhY3RvcgpzbyB0aGF0IGN1cmUgaXMgcHVycGxlLCBmYWlsdXJlIGlzIHJlZCwgYW5kIG5hKHVuaW5mZWN0ZWQpIGlzIGJsYWNrLgoKVGhlIGZvbGxvd2luZyBwbG90IHNob3VsZCBiZSB0aGUgZmlyc3QgZGlyZWN0IGNvbXBhcmlzb24gcG9pbnQgYmV0d2Vlbgp0aGUgdHdvIGFuYWx5c2lzIHBpcGVsaW5lcy4gIFRodXMsIGlmIHlvdSBsb29rIGJhY2sgYSBmZXcgYmxvY2sgYXQgbXkKaW52b2NhdGlvbiBvZiBwbG90X3BjYShleHRlcm5hbF9ub3JtKSwgeW91IHdpbGwgc2VlIGEgZ3JlZW4vb3JhbmdlCnBsb3Qgd2hpY2ggaXMgZnVuY3Rpb25hbGx5IGlkZW50aWNhbCBpZiB5b3Ugbm90ZToKCjEuICBUaGUgeCBhbmQgeSBheGVzIGFyZSBmbGlwcGVkLCB3aGljaCBvayB3aGF0ZXZlciBpdCBpcyBQQ0EuCjIuICBJIGV4Y2x1ZGVkIHRoZSBoZWFsdGh5IHNhbXBsZXMuCjMuICBJIGRyb3BwZWQgdG8gZ2VuZSBsZXZlbCBhbmQgdXNlZCBoaXNhdC4KCldpdGggdGhvc2UgY2F2ZWF0cyBpbiBtaW5kLCBpdCBpcyB0cml2aWFsIHRvIGZpbmQgdGhlIHNhbWUKcmVsYXRpb25zaGlwZXMgaW4gdGhlIHNhbXBsZXMuICBFLmcuIHRoZSBib3R0b20gcmVkL3B1cnBsZSBpbmRpdmlkdWFsCnNhbXBsZXMgYXJlIGluIHRoZSBzYW1lIHJlbGF0aXZlIHBvc2l0aW9uIGFzIG15IHRvcCBvcmFuZ2UvZ3JlZW4gcGFpci4KdGhlIHNhbWUgNCBzYW1wbGVzIGFyZSByZWxhdGl2ZSB4LWF4aXMgb3V0bGllcnMgKG15IHJpZ2h0IGdyZWVuLCB0aGVpcgpsZWZ0IHB1cnBsZSkuICBUaGUgbGFzdCA2IHNhbXBsZXMgKG15IG9yYW5nZSwgdGhlaXIgcmVkKSBhcmUgYWxsIGluCnRoZSByZWxhdGl2ZSBvcmllbnRhdGlvbi4KCkkgdGhpbmsgSSBjYW4gZnVydGhlciBwcm92ZSB0aGUgc2ltaWxhcml0eSBvZiBvdXIgaW5wdXRzIHZpYSBhIGRpcmVjdApjb21wYXJpc29uIG9mIHRoZSBkYXRhc3RydWN0dXJlczoKVHhpLmxlc2lvbi5jb2RpbmcuREdFTGlzdC5Mb2dDUE0uZmlsdGVyZWQubm9ybSAodWdoIHdoYXQgYSBuYW1lKQp2cy4gZXh0ZXJuYWxfY2YuICBJbiBvcmRlciB0byBtYWtlIHRoYXQgY29tcGFyaXNvbiwgSSBuZWVkIHRvIHJlbmFtZQpteSByb3dzIHRvIHRoZSBnZW5lY2FyZCBJRHMgYW5kIHRoZSBjb2x1bW5zLgoKYGBge3IsIGV2YWw9RkFMU0V9CnRoZWlyX25vcm1fZXhwcnMgPC0gVHhpLmxlc2lvbi5jb2RpbmcuREdFTGlzdC5Mb2dDUE0uZmlsdGVyZWQubm9ybQoKbXlfaGduY19pZHMgPC0gbWFrZS5uYW1lcyhmRGF0YShleHRlcm5hbF9jZilbWyJoZ25jX3N5bWJvbCJdXSwgdW5pcXVlID0gVFJVRSkKbXlfcmVuYW1lZCA8LSBzZXRfZXhwdF9nZW5lbmFtZXMoZXh0ZXJuYWxfY2YsIGlkcyA9IG15X2hnbmNfaWRzKQpteV9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KG15X3JlbmFtZWQsIGZpbHRlciA9IFRSVUUsIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iKQpteV9ub3JtX2V4cHJzIDwtIGFzLmRhdGEuZnJhbWUoZXhwcnMobXlfbm9ybSkpCgpvdXJfZXhwcnMgPC0gbWVyZ2UodGhlaXJfbm9ybV9leHBycywgbXlfbm9ybV9leHBycywgYnkgPSAicm93Lm5hbWVzIikKcm93bmFtZXMob3VyX2V4cHJzKSA8LSBvdXJfZXhwcnNbWyJSb3cubmFtZXMiXV0Kb3VyX2V4cHJzW1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKZGltKG91cl9leHBycykKCiMjIEkgZnVsbHkgZXhwZWN0ZWQgYSBjb3JyZWxhdGlvbiBoZWF0bWFwIG9mIHRoZSBjb21iaW5lZAojIyBkYXRhIHRvIHNob3cgYSBzZXQgb2YgcGFpcmVkIHNhbXBsZXMgYWNyb3NzIHRoZSBib2FyZC4KIyMgVGhhdCBpcyBhYnNvbHV0ZWx5IG5vdCB0cnVlLgpjb3JyZWxhdGlvbnMgPC0gcGxvdF9jb3JoZWF0KG91cl9leHBycykKY29ycmVsYXRpb25zW1sic2NhdHRlciJdXQpjb3JyZWxhdGlvbnNbWyJwbG90Il1dCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CmNvbG9yX2ZhY3QgPC0gZmFjdG9yKHRhcmdldHMubGVzaW9uJHRyZWF0bWVudF9vdXRjb21lKQpsZXZlbHMoY29sb3JfZmFjdCkKIyMgQWRkZWQgYnkgYXRiIHRvIHNlZSBjdXJlL2ZhaWwgb24gdGhlIHNhbWUgZGF0YXNldApnZ3Bsb3QoZGF0YS5mcmFtZSwgYWVzKHg9UEMxLCB5PVBDMiwgY29sb3I9Y29sb3JfZmFjdCkpICsKICBnZW9tX3BvaW50KHNpemU9NSwgc2hhcGU9MjApICsKICB0aGVtZV9jYWxjKCkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1LCB2anVzdCA9IDAuNSksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygicHVycGxlIiwgInJlZCIsImJsYWNrIikpICsKICBhbm5vdGF0ZSgidGV4dCIsIHg9LTUwLCB5PTgwLCBsYWJlbD1wYXN0ZSgiUGVybWFub3ZhIFByKD5GKSA9IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZWdhblsxLDVdKSwgc2l6ZT0zLCBmb250ZmFjZT0iYm9sZCIpICsKICB4bGFiKHBhc3RlKCJQQzEgLSIscGMucGVyWzFdLCIlIikpICsKICB5bGFiKHBhc3RlKCJQQzIgLSIscGMucGVyWzJdLCIlIikpICsKICB4bGltKC0yMDAsMTEwKQpgYGAKCiMjIERFIGNvbXBhcmlzb25zCgpUaGUgZm9sbG93aW5nIGlzIHRoZWlyIGNvbXBhcmlzb24gb2YgaGVhbHRoeSB0aXNzdWUgdnMuICBDTCBsZXNpb24gYW5kCkZhaWx1cmUgdnMuIEN1cmUuICBJIGFtIGdvaW5nIHRvIGZvbGxvdyBpdCB3aXRoIG15IGFuYWxhZ291cwpleGFtaW5hdGlvbiB1c2luZyBsaW1tYS4gIE5vdGUsIGVhY2ggb2YgdGhlIHBhaXJzIG9mIHZhcmlhYmxlcyBjcmVhdGVkCmluIHRoZSBmb2xsb3dpbmcgYmxvY2sgaXMgeHh4IGZvbGxvd2VkIGJ5IHh4eC50cmVhdDsgdGhlIGZvcm1lciBpcwpoZWFsdGh5IHZzIGxlc2lvbiBhbmQgdGhlIGxhdHRlciBpcyB0aGUgZmFpbCB2cyBjdXJlIHNldC4KCmBgYHtyLCBldmFsPUZBTFNFfQojIE1vZGVsIG1hdHJpY2VzOgojIENMIGxlc2lvbnMgdnMuIEhTOgpkZXNpZ24ubGVzaW9uIDwtIG1vZGVsLm1hdHJpeCh+MCArIGRpc2Vhc2UubGVzaW9uKQpjb2xuYW1lcyhkZXNpZ24ubGVzaW9uKSA8LSBsZXZlbHMoZGlzZWFzZS5sZXNpb24pCgojIEZhaWx1cmUgdnMuIEN1cmU6CmRlc2lnbi5sZXNpb24udHJlYXRtZW50IDwtIG1vZGVsLm1hdHJpeCh+MCArIHRyZWF0bWVudC5sZXNpb24pCmNvbG5hbWVzKGRlc2lnbi5sZXNpb24udHJlYXRtZW50KSA8LSBsZXZlbHModHJlYXRtZW50Lmxlc2lvbikKCm15REdFTGlzdC5sZXNpb24uY29kaW5nIDwtIERHRUxpc3QoY2FsY05vcm0xJGNvdW50cykKbXlER0VMaXN0Lk9QLk5vdEZpbCA8LSBER0VMaXN0KENQTV9ub3JtRGF0YV9ub3RmaWx0ZXJlZF9PUCkKCiMgTW9kZWwgbWVhbi12YXJpYW5jZSB0cmVuZCBhbmQgZml0IGxpbmVhciBtb2RlbCB0byBkYXRhLgojIFVzZSBWT09NIGZ1bmN0aW9uIGZyb20gTGltbWEgcGFja2FnZSB0byBtb2RlbCB0aGUgbWVhbi12YXJpYW5jZSByZWxhdGlvbnNoaXAKbm9ybURhdGEubGVzaW9uLmNvZGluZyA8LSB2b29tKG15REdFTGlzdC5sZXNpb24uY29kaW5nLCBkZXNpZ24ubGVzaW9uKQpub3JtRGF0YS5PUC5Ob3RGaWwgPC0gdm9vbShteURHRUxpc3QuT1AuTm90RmlsLCBkZXNpZ24ubGVzaW9uLnRyZWF0bWVudCkKCmNvbG5hbWVzKG5vcm1EYXRhLmxlc2lvbi5jb2RpbmcpIDwtIHRhcmdldHMubGVzaW9uJHNhbXBsZQpjb2xuYW1lcyhub3JtRGF0YS5PUC5Ob3RGaWwpIDwtIHRhcmdldHMub25seXBhdGllbnRzJHNhbXBsZQoKIyBmaXQgYSBsaW5lYXIgbW9kZWwgdG8geW91ciBkYXRhCmZpdC5sZXNpb24uY29kaW5nIDwtIGxtRml0KG5vcm1EYXRhLmxlc2lvbi5jb2RpbmcsIGRlc2lnbi5sZXNpb24pCmZpdC5sZXNpb24uY29kaW5nLnRyZWF0bWVudCA8LSBsbUZpdChub3JtRGF0YS5PUC5Ob3RGaWwsIGRlc2lnbi5sZXNpb24udHJlYXRtZW50KQoKIyBjb250cmFzdCBtYXRyaXgKY29udHJhc3QubWF0cml4Lmxlc2lvbiA8LSBtYWtlQ29udHJhc3RzKENMLnZzLkNPTiA9IGN1dGFuZW91cyAtIGNvbnRyb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9ZGVzaWduLmxlc2lvbikKY29udHJhc3QubWF0cml4Lmxlc2lvbi50cmVhdCA8LSBtYWtlQ29udHJhc3RzKGZhaWx1cmUudnMuY3VyZSA9IGZhaWx1cmUgLSBjdXJlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWRlc2lnbi5sZXNpb24udHJlYXRtZW50KQoKIyBleHRyYWN0IHRoZSBsaW5lYXIgbW9kZWwgZml0CmZpdHMubGVzaW9uLmNvZGluZyA8LSBjb250cmFzdHMuZml0KGZpdC5sZXNpb24uY29kaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cmFzdC5tYXRyaXgubGVzaW9uKQpmaXRzLmxlc2lvbi5jb2RpbmcudHJlYXQgPC0gY29udHJhc3RzLmZpdChmaXQubGVzaW9uLmNvZGluZy50cmVhdG1lbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyYXN0Lm1hdHJpeC5sZXNpb24udHJlYXQpCgojIGdldCBiYXllc2lhbiBzdGF0cyBmb3IgeW91ciBsaW5lYXIgbW9kZWwgZml0CmViRml0Lmxlc2lvbi5jb2RpbmcgPC0gZUJheWVzKGZpdHMubGVzaW9uLmNvZGluZykKZWJGaXQubGVzaW9uLmNvZGluZy50cmVhdCA8LSBlQmF5ZXMoZml0cy5sZXNpb24uY29kaW5nLnRyZWF0KQoKIyBUb3BUYWJsZSAtLS0tCmFsbEhpdHMubGVzaW9uLmNvZGluZyA8LSB0b3BUYWJsZShlYkZpdC5sZXNpb24uY29kaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWRqdXN0ID0iQkgiLCBjb2VmPTEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXI9MzQ5MzUsIHNvcnQuYnk9ImxvZ0ZDIikKYWxsSGl0cy5sZXNpb24uY29kaW5nLnRyZWF0IDwtIHRvcFRhYmxlKGViRml0Lmxlc2lvbi5jb2RpbmcudHJlYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZGp1c3QgPSJCSCIsIGNvZWY9MSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlcj0zNDc3Niwgc29ydC5ieT0ibG9nRkMiKQpteVRvcEhpdHMgPC0gcm93bmFtZXNfdG9fY29sdW1uKGFsbEhpdHMubGVzaW9uLmNvZGluZywgImdlbmVJRCIpCm15VG9wSGl0cy50cmVhdCA8LSByb3duYW1lc190b19jb2x1bW4oYWxsSGl0cy5sZXNpb24uY29kaW5nLnRyZWF0LCAiZ2VuZUlEIikKCiMgbXV0YXRlIHRoZSBmb3JtYXQgb2YgbnVtZXJpYyB2YWx1ZXM6Cm15VG9wSGl0cyA8LSBtdXRhdGUobXlUb3BIaXRzLCBsb2cxMFB2YWwgPSByb3VuZCgtbG9nMTAoYWRqLlAuVmFsKSwyKSwKICAgICAgICAgICAgICAgICAgICBhZGouUC5WYWwgPSByb3VuZChhZGouUC5WYWwsIDIpLAogICAgICAgICAgICAgICAgICAgIEIgPSByb3VuZChCLCAyKSwKICAgICAgICAgICAgICAgICAgICBBdmVFeHByID0gcm91bmQoQXZlRXhwciwgMiksCiAgICAgICAgICAgICAgICAgICAgdCA9IHJvdW5kKHQsIDIpLAogICAgICAgICAgICAgICAgICAgIGxvZ0ZDID0gcm91bmQobG9nRkMsIDIpLAogICAgICAgICAgICAgICAgICAgIGdlbmVJRCA9IGdlbmVJRCkKCm15VG9wSGl0cy50cmVhdCA8LSBtdXRhdGUobXlUb3BIaXRzLnRyZWF0LCBsb2cxMFB2YWwgPSByb3VuZCgtbG9nMTAoYWRqLlAuVmFsKSwyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBhZGouUC5WYWwgPSByb3VuZChhZGouUC5WYWwsIDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEIgPSByb3VuZChCLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBBdmVFeHByID0gcm91bmQoQXZlRXhwciwgMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgdCA9IHJvdW5kKHQsIDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ0ZDID0gcm91bmQobG9nRkMsIDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVJRCA9IGdlbmVJRCkKI3NhdmUobXlUb3BIaXRzLCBmaWxlID0gIm15VG9wSGl0cyIpCiNzYXZlKG15VG9wSGl0cy50cmVhdCwgZmlsZSA9ICJteVRvcEhpdHMudHJlYXQiKQpgYGAKCiMjIFBlcmZvcm0gbXkgYW5hbGFnb3VzIGxpbW1hIGFuYWx5c2lzCgpgYGB7ciBjb21wYXJlX3Jlc3VsdCwgZXZhbD1GQUxTRX0KbXlfZmlsdCA8LSBub3JtYWxpemVfZXhwdChteV9yZW5hbWVkLCBmaWx0ZXIgPSAic2ltcGxlIikKbGltbWFfY2YgPC0gbGltbWFfcGFpcndpc2UobXlfZmlsdCwgbW9kZWxfYmF0Y2ggPSBGQUxTRSkKCm15X3RhYmxlIDwtIGxpbW1hX2NmW1siYWxsX3RhYmxlcyJdXVtbImZhaWx1cmVfdnNfY3VyZSJdXQp0aGVpcl90YWJsZSA8LSBteVRvcEhpdHMudHJlYXQKCmRpbShteV90YWJsZSkKZGltKG15VG9wSGl0cy50cmVhdCkKb3VyX3RhYmxlIDwtIG1lcmdlKG15X3RhYmxlLCBteVRvcEhpdHMudHJlYXQsIGJ5LnggPSAicm93Lm5hbWVzIiwgYnkueSA9ICJnZW5lSUQiKQpkaW0ob3VyX3RhYmxlKQpjb21wYXJpc29uIDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIob3VyX3RhYmxlWywgYygibG9nRkMueCIsICJsb2dGQy55IildKQpjb21wYXJpc29uJHNjYXR0ZXIKY29tcGFyaXNvbiRjb3JyZWxhdGlvbgpjb21wYXJpc29uJGxtX21vZGVsCmBgYAoKT2ssIHNvIHRoZXJlIGlzIGEgY29uc3RpdHVpdGl2ZSBkaWZmZXJlbmNlIGluIG91ciByZXN1bHRzLCBhbmQgaXQgaXMKc2lnbmlmaWNhbnQuICBXaGF0IGRvZXMgdGhhdCBtZWFuIGZvciB0aGUgc2V0IG9mIGdlbmVzIG9ic2VydmVkPwoKV2l0aCB0aGF0IHNhaWQsIGluIG15IG1vc3QgcmVjZW50IG1hbnVhbCBydW4gb2YgdGhpcywgdGhlIHJlc3VsdHMgYXJlCnF1aXRlIGdvb2QsIEkgZ290IGEgMC43NSBjb3JyZWxhdGlvbjsgSSBiZXQgdGhlIHByaW1hcnkgb3V0bGllcnMgKG9uCnRoZSBheGVzKSBhcmUganVzdCBnZW5lcyBmb3Igd2hpY2ggd2UgZ290IGRpZmZlcmVudCBnZW5lPC0+dHggbWFwcGluZ3MKZHVlIHRvIG1lIHVzaW5nIGhpc2F0IGFuZCB0aGVpciB1c2FnZSBvZiBrYWxsaXN0by4KCkkgZ3Vlc3MgSSBjYW4gdGVzdCB0aGlzIGh5cG90aGVzaXMgYnkganVzdCBzd2FwcGluZyBpbiB0aGVpciBjb3VudHMKaW50byBteSBkYXRhIHN0cnVjdHVyZS4KCmBgYHtyLCBldmFsPUZBTFNFfQp0ZXN0X2NvdW50cyA8LSBhcy5kYXRhLmZyYW1lKG15REdFTGlzdC5sZXNpb24uY29kaW5nW1siY291bnRzIl1dKQp0ZXN0X2NvdW50c1tbImhvc3RfSFMwMSJdXSA8LSBOVUxMCnRlc3RfY291bnRzW1siaG9zdF9IUzAyIl1dIDwtIE5VTEwKdGVzdF9jb3VudHNbWyJob3N0X0hTMDMiXV0gPC0gTlVMTAp0ZXN0X2NvdW50c1tbImhvc3RfSFMwNCJdXSA8LSBOVUxMCnRlc3RfY291bnRzW1siaG9zdF9IUzA1Il1dIDwtIE5VTEwKdGVzdF9jb3VudHNbWyJob3N0X0hTMDYiXV0gPC0gTlVMTAp0ZXN0X2NvdW50c1tbImhvc3RfSFMwNyJdXSA8LSBOVUxMCgpkaW0odGVzdF9jb3VudHMpCmRpbShleHBycyhteV90ZXN0KSkKIyMgT2gsIHRoYXQgc3VycHJpc2VzIG1lLCB0aGUga2FsbGlzdG8gZGF0YSBoYXMgfiA2ayBmZXdlciBnZW5lcz8KYGBgCgojIyBTZWUgaWYgdGhlcmUgYXJlIHNoYXJlZCBERSBnZW5lcwoKISFOT1RFISEgIEkgYW0gdXNpbmcgYSBub24tYWRqdXN0ZWQgcC12YWx1ZSBmaWx0ZXIgaGVyZSBiZWNhdXNlIEkgd2FudAp0byB1c2UgdGhlIHNhbWUgZmlsdGVyIHRoZXkgdXNlZCBmb3IgdGhlIHZvbGNhbm8gcGxvdC4KCmBgYHtyIHNoYXJlZF9nZW5lcywgZXZhbD1GQUxTRX0KbXlfZmlsdGVyIDwtIGFicyhteV90YWJsZVtbImxvZ0ZDIl1dKSA+IDEuMCAmIG15X3RhYmxlW1siUC5WYWx1ZSJdXSA8PSAwLjA1CnN1bShteV9maWx0ZXIpCnRoZWlyX2ZpbHRlciA8LSBhYnModGhlaXJfdGFibGVbWyJsb2dGQyJdXSkgPiAxLjAgJiB0aGVpcl90YWJsZVtbIlAuVmFsdWUiXV0gPD0gMC4wNQpzdW0odGhlaXJfZmlsdGVyKQoKbXlfc2hhcmVkIDwtIHJvd25hbWVzKG15X3RhYmxlKVtteV9maWx0ZXJdICVpbiUgdGhlaXJfdGFibGVbdGhlaXJfZmlsdGVyLCAiZ2VuZUlEIl0Kc3VtKG15X3NoYXJlZCkKCnNoYXJlZCA8LSByb3duYW1lcyhteV90YWJsZSlbbXlfZmlsdGVyXQpzaGFyZWRbbXlfc2hhcmVkXQoKYm90aCA8LSBsaXN0KAogICJ1cyIgPSByb3duYW1lcyhteV90YWJsZSlbbXlfZmlsdGVyXSwKICAidGhlbSIgPSB0aGVpcl90YWJsZVt0aGVpcl9maWx0ZXIsICJnZW5lSUQiXSkKdHQgPC0gVXBTZXRSOjpmcm9tTGlzdChib3RoKQpVcFNldFI6OnVwc2V0KHR0KQpgYGAKCiMjIENvbXBhcmUgdGhlIHR3byBkYXRhc2V0cyBkaXJlY3RseQoKYGBge3Igc2NvdHRfZXh0ZXJuYWx9Cm9ubHlfdG1yYzMgPC0gc3Vic2V0X2V4cHQodG1yYzNfZXh0ZXJuYWwsIHN1YnNldCA9ICJjb25kaXRpb249PSdDb2xvbWJpYSciKSAlPiUKICBzZXRfZXhwdF9jb25kaXRpb25zKGZhY3QgPSAiZmluYWxvdXRjb21lIikKb25seV90bXJjM19kZSA8LSBhbGxfcGFpcndpc2Uob25seV90bXJjMywgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCm9ubHlfdG1yYzNfZGUKb25seV90bXJjM190YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcyhvbmx5X3RtcmMzX2RlLCBzY2FsZV9wID0gVFJVRSkKb25seV90bXJjM190YWJsZQpvbmx5X3RtcmMzX3RvcDEwMCA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKG9ubHlfdG1yYzNfdGFibGUsIG4gPSAxMDApCm9ubHlfdG1yYzNfdXAgPC0gb25seV90bXJjM190b3AxMDBbWyJkZXNlcSJdXVtbInVwcyJdXVtbImZhaWx1cmVfdnNfY3VyZSJdXQpvbmx5X3RtcmMzX2Rvd24gPC0gb25seV90bXJjM190b3AxMDBbWyJkZXNlcSJdXVtbImRvd25zIl1dW1siZmFpbHVyZV92c19jdXJlIl1dCgp0bXJjM19leHRlcm5hbF9kZSA8LSBhbGxfcGFpcndpc2UodG1yYzNfZXh0ZXJuYWwsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSAic2ltcGxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBtZXRob2RzKQp0bXJjM19leHRlcm5hbF90YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0bXJjM19leHRlcm5hbF9kZSwgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSAiZXhjZWwvdG1yYzNfc2NvdHRfYmlvcHNpZXMueGxzeCIpCnRtcmMzX2V4dGVybmFsX3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHRtcmMzX2V4dGVybmFsX3RhYmxlLCBleGNlbCA9ICJleGNlbC90bXJjM19zY290dF9iaW9wc2llc19zaWcueGxzeCIpCgp0bXJjM19leHRlcm5hbF9jZiA8LSBzZXRfZXhwdF9jb25kaXRpb25zKHRtcmMzX2V4dGVybmFsLCBmYWN0ID0gImZpbmFsb3V0Y29tZSIpCnRtcmMzX2V4dGVybmFsX2NmIDwtICBzZXRfZXhwdF9iYXRjaGVzKHRtcmMzX2V4dGVybmFsX2NmLCBmYWN0ID0gImxhYiIpCnRtcmMzX2V4dGVybmFsX2NmX25vcm0gPC0gbm9ybWFsaXplX2V4cHQodG1yYzNfZXh0ZXJuYWxfY2YsIGZpbHRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybSA9ICJxdWFudCIsIGNvbnZlcnQgPSAiY3BtIiwgdHJhbnNmb3JtID0gImxvZzIiKQpwbG90X3BjYSh0bXJjM19leHRlcm5hbF9jZl9ub3JtKQp0bXJjM19leHRlcm5hbF9jZl9uYiA8LSBub3JtYWxpemVfZXhwdCh0bXJjM19leHRlcm5hbF9jZiwgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmF0Y2ggPSAic3Zhc2VxIiwgY29udmVydCA9ICJjcG0iLCB0cmFuc2Zvcm0gPSAibG9nMiIpCnBsb3RfcGNhKHRtcmMzX2V4dGVybmFsX2NmX25iKQoKdG1yYzNfZXh0ZXJuYWxfY2ZfZGUgPC0gYWxsX3BhaXJ3aXNlKHRtcmMzX2V4dGVybmFsX2NmLCBtb2RlbF9iYXRjaCA9ICJzdmFzZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZHMgPSBtZXRob2RzKQp0bXJjM19leHRlcm5hbF9jZl9kZQp0bXJjM19leHRlcm5hbF9jZl90YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0bXJjM19leHRlcm5hbF9jZl9kZSwgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSAiZXhjZWwvdG1yYzNfc2NvdHRfY2ZfdGFibGUueGxzeCIpCnRtcmMzX2V4dGVybmFsX2NmX3RhYmxlCnRtcmMzX2V4dGVybmFsX2NmX3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHRtcmMzX2V4dGVybmFsX2NmX3RhYmxlLCBleGNlbCA9ICJleGNlbC90bXJjM19zY290dF9jZl9zaWcueGxzeCIpCnRtcmMzX2V4dGVybmFsX2NmX3NpZwoKdG1yYzNfZXh0ZXJuYWxfc3BlY2llcyA8LSBzZXRfZXhwdF9jb25kaXRpb25zKHRtcmMzX2V4dGVybmFsLCBmYWN0ID0gIlBhcmFzaXRlU3BlY2llcyIpICU+JQogIHNldF9leHB0X2NvbG9ycyhjb2xvcl9jaG9pY2VzW1sicGFyYXNpdGUiXV0pCmBgYAoKIyMgQ29tcGFyZSB0aGUgbDJGQyB2YWx1ZXMKCkxldCB1cyBsb29rIGF0IHRoZSB0b3AvYm90dG9tIDEwMCBnZW5lcyBvZiB0aGVzZSB0d28gZGF0YXNldHMgYW5kIHNlZSBpZiB0aGV5CmhhdmUgYW55IHNpbWlsYXJpdGllcy4KCk5vdGUgdG8gc2VsZiwgc2V0IHVwIHM0IGRpc3BhdGNoIG9uIGNvbXBhcmVfZGVfdGFibGVzIQoKYGBge3J9CmNvbXBhcmVkIDwtIGNvbXBhcmVfZGVfdGFibGVzKG9ubHlfdG1yYzNfdGFibGUsIGV4dGVybmFsX3RhYmxlLCBmaXJzdF90YWJsZSA9IDEsIHNlY29uZF90YWJsZSA9IDEpCmNvbXBhcmVkJHNjYXR0ZXIKY29tcGFyZWQkY29ycmVsYXRpb24KYGBgCgojIENvbXBhcmUgdmlzaXRzIGJ5IGNlbGx0eXBlIGFuZCBDL0YKCkkgYXNzdW1lIHRoaXMgcmVxdWVzdCBjYW1lIG91dCBvZiB0aGUgcmV2aWV3IHByb2Nlc3MsIGJ1dCBJIGFtIG5vdApxdWl0ZSBzdXJlIHdoZXJlIHRvIHB1dCBpdC4gIElmIEkgdW5kZXJzdGFuZCBpdCBjb3JyZWN0bHksIHRoZSBnb2FsIGlzCnRvIGxvb2sgYWNyb3NzIHZpc2l0cyBmb3IgY29tYmluYXRpb25zIG9mIGN1cmUgYW5kIGZhaWwgKG5vdApmYWlsL2N1cmUsIGJ1dCB2Mi92MSkgYW5kIGFjcm9zcyBjZWxsIHR5cGVzLgoKVGh1cywgaW4gb3JkZXIgdG8gZG8gdGhpcywgSSB3aWxsIG5lZWQgdG8gY29tYmluZSB0aG9zZSB0aHJlZQpwYXJhbWV0ZXJzIG9yIHNldCB1cCBhIG1vcmUgY29tcGxleCBtb2RlbCB0byBoYW5kbGUgdGhpcy4KCmBgYHtyfQp0X2NlbGx2aXNpdGNmIDwtIHNldF9leHB0X2NvbmRpdGlvbnModF9jbGluaWNhbF9ub2Jpb3AsIGZhY3QgPSAiY2VsbF92aXNpdF9jZiIpCgp0X2NlbGx2aXNpdGNmX2RlIDwtIGFsbF9wYWlyd2lzZSh0X2NlbGx2aXNpdGNmLCBrZWVwZXJzID0gdmlzaXR0eXBlX2NvbnRyYXN0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIiwgZmlsdGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kcyA9IG1ldGhvZHMpCnRfY2VsbHZpc2l0Y2ZfZGUKCnRfY2VsbHZpc2l0Y2ZfbW9ub190YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X2NlbGx2aXNpdGNmX2RlLCBrZWVwZXJzID0gdmlzaXR0eXBlX2NvbnRyYXN0c19tb25vLCBzY2FsZV9wID0gVFJVRSwKICBleGNlbCA9IGdsdWUoInt4bHN4X3ByZWZpeH0vREVfVmlzaXRzL0N1cmVfRmFpbC9tb25vY3l0ZV92aXNpdF9jZl9jb21iaW5lZF90YWJsZV9zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZWxsdmlzaXRjZl9tb25vX3RhYmxlCnRfY2VsbHZpc2l0Y2ZfbW9ub19zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X2NlbGx2aXNpdGNmX21vbm9fdGFibGUsCiAgZXhjZWwgPSBnbHVlKCJ7eGxzeF9wcmVmaXh9L0RFX1Zpc2l0cy9DdXJlX0ZhaWwvbW9ub2N5dGVfdmlzaXRfY2ZfY29tYmluZWRfc2lnX3N2YS12e3Zlcn0ueGxzeCIpKQp0X2NlbGx2aXNpdGNmX21vbm9fc2lnCnRfY2VsbHZpc2l0Y2ZfbmV1dF90YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X2NlbGx2aXNpdGNmX2RlLCBrZWVwZXJzID0gdmlzaXR0eXBlX2NvbnRyYXN0c19uZSwgc2NhbGVfcCA9IFRSVUUsCiAgZXhjZWwgPSBnbHVlKCJ7eGxzeF9wcmVmaXh9L0RFX1Zpc2l0cy9DdXJlX0ZhaWwvbmV1dHJvcGhpbF92aXNpdF9jZl9jb21iaW5lZF90YWJsZV9zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZWxsdmlzaXRjZl9uZXV0X3RhYmxlCnRfY2VsbHZpc2l0Y2ZfbmV1dF9zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICB0X2NlbGx2aXNpdGNmX25ldXRfdGFibGUsCiAgZXhjZWwgPSBnbHVlKCJ7eGxzeF9wcmVmaXh9L0RFX1Zpc2l0cy9DdXJlX0ZhaWwvbmV1dHJvcGhpbF92aXNpdF9jZl9jb21iaW5lZF9zaWdfc3ZhLXZ7dmVyfS54bHN4IikpCnRfY2VsbHZpc2l0Y2ZfbmV1dF9zaWcKdF9jZWxsdmlzaXRjZl9lb190YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICB0X2NlbGx2aXNpdGNmX2RlLCBrZWVwZXJzID0gdmlzaXR0eXBlX2NvbnRyYXN0c19lbywKICBleGNlbCA9IGdsdWUoInt4bHN4X3ByZWZpeH0vREVfVmlzaXRzL0N1cmVfRmFpbC9lb3Npbm9waGlsX3Zpc2l0X2NmX2NvbWJpbmVkX3RhYmxlX3N2YS12e3Zlcn0ueGxzeCIpKQp0X2NlbGx2aXNpdGNmX2VvX3RhYmxlCnRfY2VsbHZpc2l0Y2ZfZW9fc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgdF9jZWxsdmlzaXRjZl9lb190YWJsZSwKICBleGNlbCA9IGdsdWUoInt4bHN4X3ByZWZpeH0vREVfVmlzaXRzL0N1cmVfRmFpbC9lb3Npbm9waGlsX3Zpc2l0X2NmX2NvbWJpbmVkX3NpZ19zdmEtdnt2ZXJ9Lnhsc3giKSkKdF9jZWxsdmlzaXRjZl9lb19zaWcKYGBgCgpgYGB7ciBsb2FkbWVfYWZ0ZXIsIGV2YWw9RkFMU0V9CnRtcCA8LSBsb2FkbWUoZmlsZW5hbWUgPSBzYXZlZmlsZSkKYGBgCgojIEJpYmxpb2dyYXBoeQo=