sample_sheet <- glue::glue("sample_sheets/tmrc2_samples_202206.xlsx")

1 Introduction

This is mostly just a run of this worksheet to reacquaint myself with it.

This document is intended to provide a general overview of the TMRC2 samples which have thus far been sequenced. In some cases, this includes only those samples starting in 2019; in other instances I am including our previous (2015-2016) samples.

In all cases the processing performed was:

  1. Default trimming was performed.
  2. Hisat2 was used to map the remaining reads against the Leishmania panamensis genome revision 36.
  3. The alignments from hisat2 were used to count reads/gene against the revision 36 annotations with htseq.
  4. These alignments were also passed to the pileup functionality of samtools and the vcf/bcf utilities in order to make a matrix of all observed differences between each sample with respect to the reference.

The analyses in this document use the matrices of counts/gene from #3 and variants/position from #4 in order to provide some images and metrics describing the samples we have sequenced so far.

2 Annotations

Everything which follows depends on the Existing TriTrypDB annotations revision 46, circa 2019. The following block loads a database of these annotations and turns it into a matrix where the rows are genes and columns are all the annotation types provided by TriTrypDB.

The same database was used to create a matrix of orthologous genes between L.panamensis and all of the other species in the TriTrypDB.

tt <- sm(library(EuPathDB))
orgdb <- "org.Lpanamensis.MHOMCOL81L13.v46.eg.db"
tt <- sm(library(orgdb, character.only=TRUE))
pan_db <- org.Lpanamensis.MHOMCOL81L13.v46.eg.db
all_fields <- columns(pan_db)

all_lp_annot <- sm(load_orgdb_annotations(
    pan_db,
    keytype = "gid",
    fields = c("annot_gene_entrez_id", "annot_gene_name",
               "annot_strand", "annot_chromosome", "annot_cds_length",
               "annot_gene_product")))$genes

lp_go <- sm(load_orgdb_go(pan_db))
lp_lengths <- all_lp_annot[, c("gid", "annot_cds_length")]
colnames(lp_lengths)  <- c("ID", "length")
all_lp_annot[["annot_gene_product"]] <- tolower(all_lp_annot[["annot_gene_product"]])
orthos <- sm(EuPathDB::extract_eupath_orthologs(db = pan_db))

hisat_annot <- all_lp_annot

3 Load a genome

meta <- sm(EuPathDB::download_eupath_metadata(webservice="tritrypdb"))
lp_entry <- EuPathDB::get_eupath_entry(species="Leishmania panamensis", metadata=meta)
## Found the following hits: Leishmania panamensis MHOM/COL/81/L13, Leishmania panamensis strain MHOM/PA/94/PSC-1, choosing the first.
## Using: Leishmania panamensis MHOM/COL/81/L13.
colnames(lp_entry)
##  [1] "AnnotationVersion"  "AnnotationSource"   "BiocVersion"       
##  [4] "DataProvider"       "Genome"             "GenomeSource"      
##  [7] "GenomeVersion"      "NumArrayGene"       "NumChipChipGene"   
## [10] "NumChromosome"      "NumCodingGene"      "NumCommunity"      
## [13] "NumContig"          "NumEC"              "NumEST"            
## [16] "NumGene"            "NumGO"              "NumOrtholog"       
## [19] "NumOtherGene"       "NumPopSet"          "NumProteomics"     
## [22] "NumPseudogene"      "NumRNASeq"          "NumRTPCR"          
## [25] "NumSNP"             "NumTFBS"            "Organellar"        
## [28] "ReferenceStrain"    "MegaBP"             "PrimaryKey"        
## [31] "ProjectID"          "RecordClassName"    "SourceID"          
## [34] "SourceVersion"      "TaxonomyID"         "TaxonomyName"      
## [37] "URLGenome"          "URLGFF"             "URLProtein"        
## [40] "Coordinate_1_based" "Maintainer"         "SourceUrl"         
## [43] "Tags"               "BsgenomePkg"        "GrangesPkg"        
## [46] "OrganismdbiPkg"     "OrgdbPkg"           "TxdbPkg"           
## [49] "Taxon"              "Genus"              "Species"           
## [52] "Strain"             "BsgenomeFile"       "GrangesFile"       
## [55] "OrganismdbiFile"    "OrgdbFile"          "TxdbFile"          
## [58] "GenusSpecies"       "TaxonUnmodified"    "TaxonCanonical"    
## [61] "TaxonXref"
testing_panamensis <- "BSGenome.Leishmania.panamensis.MHOMCOL81L13.v53"
## testing_panamensis <- EuPathDB::make_eupath_bsgenome(entry=lp_entry, eu_version="v46")
library(as.character(testing_panamensis), character.only=TRUE)
## Loading required package: BSgenome
## Loading required package: Biostrings
## Loading required package: XVector
## 
## Attaching package: 'Biostrings'
## The following object is masked from 'package:base':
## 
##     strsplit
## Loading required package: rtracklayer
genome <- get0(as.character(testing_panamensis))

4 TODO:

Resequence samples: TMRC20002, TMRC20006, TMRC20004 (maybe TMRC20008 and TMRC20029)

5 Generate Expressionsets and Sample Estimation

The process of sample estimation takes two primary inputs:

  1. The sample sheet, which contains all the metadata we currently have on hand, including filenames for the outputs of #3 and #4 above.
  2. The gene annotations.

An expressionset is a data structure used in R to examine RNASeq data. It is comprised of annotations, metadata, and expression data. In the case of our processing pipeline, the location of the expression data is provided by the filenames in the metadata.

The first lines of the following block create the Expressionset. All of the following lines perform various normalizations and generate plots from it.

5.1 Notes

The following samples are much lower coverage:

  • TMRC20002
  • TMRC20006
  • TMRC20007
  • TMRC20008

20210610: I made some manual changes to the sample sheet which I downloaded, filling in some zymodeme with ‘unknown’

5.2 TODO:

  1. Do the multi-gene family removal right here instead of way down at the bottom
  2. Add zymodeme snps to the annotation later.
  3. Start phylogenetic analysis of variant table.
sanitize_columns <- c("passagenumber", "clinicalresponse", "clinicalcategorical",
                      "zymodemecategorical", "zymodemecategorical")
lp_expt <- create_expt(sample_sheet,
                       gene_info = hisat_annot,
                       annotation = orgdb,
                       id_column = "hpglidentifier",
                       file_column = "lpanamensisv36hisatfile") %>%
  set_expt_conditions(fact = "zymodemecategorical") %>%
  subset_expt(nonzero = 8550) %>%
  subset_expt(coverage = 5000000) %>%
  semantic_expt_filter(semantic = c("amastin", "gp63", "leishmanolysin"),
                       semantic_column = "annot_gene_product") %>%
  sanitize_expt_metadata(columns = sanitize_columns) %>%
  set_expt_factors(columns = sanitize_columns, class = "factor")
## Reading the sample metadata.
## Error in read.xlsx.default(xlsxFile = file, sheet = 1) : 
##   File does not exist.
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'fData': error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'exprs': error in evaluating the argument 'object' in selecting a method for function 'sampleNames': error in evaluating the argument 'object' in selecting a method for function 'sampleNames': error in evaluating the argument 'object' in selecting a method for function 'pData': Unable to read the metadata file: sample_sheets/tmrc2_samples_202206.xlsx
libsizes <- plot_libsize(lp_expt)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'plot_libsize': object 'lp_expt' not found
dev <- pp(file = "images/lp_expt_libsizes.png", width = 14, height = 9)
## Writing png?
libsizes$plot
## Error in eval(expr, envir, enclos): object 'libsizes' not found
closed <- dev.off()
libsizes$plot
## Error in eval(expr, envir, enclos): object 'libsizes' not found
## I think samples 7,10 should be removed at minimum, probably also 9,11
nonzero <- plot_nonzero(lp_expt)
## Error in plot_nonzero(lp_expt): object 'lp_expt' not found
dev <- pp(file = "images/lp_nonzero.png", width=9, height=9)
## Writing png?
nonzero$plot
## Error in eval(expr, envir, enclos): object 'nonzero' not found
closed <- dev.off()

lp_box <- plot_boxplot(lp_expt)
## Error in plot_boxplot(lp_expt): object 'lp_expt' not found
dev <- pp(file = "images/lp_expt_boxplot.png", width = 12, height = 9)
## Writing png?
lp_box
## Error in eval(expr, envir, enclos): object 'lp_box' not found
closed <- dev.off()
lp_box
## Error in eval(expr, envir, enclos): object 'lp_box' not found
filter_plot <- plot_libsize_prepost(lp_expt)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'data' in selecting a method for function 'plot_libsize': object 'lp_expt' not found
filter_plot$lowgene_plot
## Error in eval(expr, envir, enclos): object 'filter_plot' not found
filter_plot$count_plot
## Error in eval(expr, envir, enclos): object 'filter_plot' not found
table(pData(lp_expt)[["zymodemecategorical"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
table(pData(lp_expt)[["clinicalresponse"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found

5.3 Distribution Visualization

Najib’s favorite plots are of course the PCA/TNSE. These are nice to look at in order to get a sense of the relationships between samples. They also provide a good opportunity to see what happens when one applies different normalizations, surrogate analyses, filters, etc. In addition, one may set different experimental factors as the primary ‘condition’ (usually the color of plots) and surrogate ‘batches’.

5.4 By Susceptilibity

Column ‘Q’ in the sample sheet, make a categorical version of it with these parameters:

  • 0 <= x <= 35 is resistant
  • 36 <= x <= 48 is ambiguous
  • 49 <= x is sensitive
starting <- as.numeric(pData(lp_expt)[["susceptibilityinfectionreduction32ugmlsbvhistoricaldata"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
sus_categorical <- starting
## Error in eval(expr, envir, enclos): object 'starting' not found
na_idx <- is.na(starting)
## Error in eval(expr, envir, enclos): object 'starting' not found
sus_categorical[na_idx] <- "unknown"
## Error in sus_categorical[na_idx] <- "unknown": object 'sus_categorical' not found
resist_idx <- starting <= 0.35
## Error in eval(expr, envir, enclos): object 'starting' not found
sus_categorical[resist_idx] <- "resistant"
## Error in sus_categorical[resist_idx] <- "resistant": object 'sus_categorical' not found
indeterminant_idx <- starting >= 0.36 & starting <= 0.48
## Error in eval(expr, envir, enclos): object 'starting' not found
sus_categorical[indeterminant_idx] <- "ambiguous"
## Error in sus_categorical[indeterminant_idx] <- "ambiguous": object 'sus_categorical' not found
susceptible_idx <- starting >= 0.49
## Error in eval(expr, envir, enclos): object 'starting' not found
sus_categorical[susceptible_idx] <- "sensitive"
## Error in sus_categorical[susceptible_idx] <- "sensitive": object 'sus_categorical' not found
pData(lp_expt)[["sus_category"]] <- sus_categorical
## Error in eval(expr, envir, enclos): object 'sus_categorical' not found
table(sus_categorical)
## Error in eval(quote(list(...)), env): object 'sus_categorical' not found
clinical_colors <- list(
    "z1.0" = "#333333",
    "z2.0" = "#555555",
    "z3.0" = "#777777",
    "z2.1" = "#874400",
    "z2.2" = "#0000cc",
    "z2.3" = "#cc0000",
    "z2.4" = "#df7000",
    "unknown" = "#cbcbcb",
    "null" = "#000000")
clinical_samples <- lp_expt %>%
  set_expt_batches(fact = sus_categorical) %>%
  set_expt_colors(clinical_colors)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
table(pData(clinical_samples)[["condition"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'clinical_samples' not found
clinical_norm <- normalize_expt(clinical_samples, norm = "quant", transform = "log2",
                                   convert = "cpm", filter = TRUE)
## Error in normalize_expt(clinical_samples, norm = "quant", transform = "log2", : object 'clinical_samples' not found
zymo_pca <- plot_pca(clinical_norm, plot_title = "PCA of parasite expression values",
                     plot_labels = FALSE)
## Error in plot_pca(clinical_norm, plot_title = "PCA of parasite expression values", : object 'clinical_norm' not found
ggplt(zymo_pca$plot)
## Error in plotly::ggplotly(gg, ...): object 'zymo_pca' not found
dev <- pp(file = "images/zymo_pca_sus_shape.png")
## Writing png?
zymo_pca$plot
## Error in eval(expr, envir, enclos): object 'zymo_pca' not found
closed <- dev.off()
zymo_pca$plot
## Error in eval(expr, envir, enclos): object 'zymo_pca' not found
only_two_types <- subset_expt(clinical_samples, subset = "condition=='z2.3'|condition=='z2.2'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'sampleNames': object 'clinical_samples' not found
only_two_norm <- sm(normalize_expt(only_two_types, norm = "quant", transform = "log2",
                                   convert = "cpm", batch = FALSE, filter = TRUE))
## Error in normalize_expt(only_two_types, norm = "quant", transform = "log2", : object 'only_two_types' not found
onlytwo_pca <- plot_pca(only_two_norm, plot_title = "PCA of z2.2 and z2.3 parasite expression values",
                     plot_labels = FALSE)
## Error in plot_pca(only_two_norm, plot_title = "PCA of z2.2 and z2.3 parasite expression values", : object 'only_two_norm' not found
dev <- pp(file = "images/zymo_z2.2_z2.3_pca_sus_shape.pdf")
onlytwo_pca$plot
## Error in eval(expr, envir, enclos): object 'onlytwo_pca' not found
closed <- dev.off()
onlytwo_pca$plot
## Error in eval(expr, envir, enclos): object 'onlytwo_pca' not found
zymo_3dpca <- plot_3d_pca(zymo_pca)
## Error in plot_3d_pca(zymo_pca): object 'zymo_pca' not found
zymo_3dpca$plot
## Error in eval(expr, envir, enclos): object 'zymo_3dpca' not found
clinical_n <- sm(normalize_expt(clinical_samples, transform = "log2",
                                convert = "cpm", batch = FALSE, filter = TRUE))
## Error in normalize_expt(clinical_samples, transform = "log2", convert = "cpm", : object 'clinical_samples' not found
zymo_tsne <- plot_tsne(clinical_n, plot_title = "TSNE of parasite expression values")
## Error in plot_pca(..., pc_method = "tsne"): object 'clinical_n' not found
zymo_tsne$plot
## Error in eval(expr, envir, enclos): object 'zymo_tsne' not found
clinical_nb <- normalize_expt(clinical_samples, convert = "cpm", transform = "log2",
                         filter = TRUE, batch = "svaseq")
## Error in normalize_expt(clinical_samples, convert = "cpm", transform = "log2", : object 'clinical_samples' not found
clinical_nb_pca <- plot_pca(clinical_nb, plot_title = "PCA of parasite expression values",
                            plot_labels = FALSE)
## Error in plot_pca(clinical_nb, plot_title = "PCA of parasite expression values", : object 'clinical_nb' not found
dev <- pp(file = "images/clinical_nb_pca_sus_shape.png")
## Writing png?
clinical_nb_pca$plot
## Error in eval(expr, envir, enclos): object 'clinical_nb_pca' not found
closed <- dev.off()
clinical_nb_pca$plot
## Error in eval(expr, envir, enclos): object 'clinical_nb_pca' not found
clinical_nb_tsne <- plot_tsne(clinical_nb, plot_title = "TSNE of parasite expression values")
## Error in plot_pca(..., pc_method = "tsne"): object 'clinical_nb' not found
clinical_nb_tsne$plot
## Error in eval(expr, envir, enclos): object 'clinical_nb_tsne' not found
corheat <- plot_corheat(clinical_norm, plot_title = "Correlation heatmap of parasite
                 expression values
")
## Error in "expt" %in% class(expt): object 'clinical_norm' not found
corheat$plot
## Error in eval(expr, envir, enclos): object 'corheat' not found
plot_sm(clinical_norm)$plot
## Error in plot_sm(clinical_norm): object 'clinical_norm' not found

5.5 By Cure/Fail status

cf_colors <- list(
    "cure" = "#006f00",
    "fail" = "#9dffa0",
    "unknown" = "#cbcbcb",
    "notapplicable" = "#000000")
cf_expt <- set_expt_conditions(lp_expt, fact = "clinicalcategorical") %>%
  set_expt_batches(fact = sus_categorical) %>%
  set_expt_colors(cf_colors)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
table(pData(cf_expt)[["condition"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'cf_expt' not found
cf_norm <- normalize_expt(cf_expt, convert = "cpm", transform = "log2",
                          norm = "quant", filter = TRUE)
## Error in normalize_expt(cf_expt, convert = "cpm", transform = "log2", : object 'cf_expt' not found
start_cf <- plot_pca(cf_norm, plot_title = "PCA of parasite expression values",
                     plot_labels = FALSE)
## Error in plot_pca(cf_norm, plot_title = "PCA of parasite expression values", : object 'cf_norm' not found
dev <- pp(file = "images/cf_sus_shape.png")
## Writing png?
start_cf$plot
## Error in eval(expr, envir, enclos): object 'start_cf' not found
closed <- dev.off()
start_cf$plot
## Error in eval(expr, envir, enclos): object 'start_cf' not found
cf_nb <- normalize_expt(cf_expt, convert = "cpm", transform = "log2",
                        filter = TRUE, batch = "svaseq")
## Error in normalize_expt(cf_expt, convert = "cpm", transform = "log2", : object 'cf_expt' not found
cf_nb_pca <- plot_pca(cf_nb, plot_title = "PCA of parasite expression values",
                      plot_labels = FALSE)
## Error in plot_pca(cf_nb, plot_title = "PCA of parasite expression values", : object 'cf_nb' not found
dev <- pp(file = "images/cf_sus_share_nb.png")
## Writing png?
cf_nb_pca$plot
## Error in eval(expr, envir, enclos): object 'cf_nb_pca' not found
closed <- dev.off()
cf_nb_pca$plot
## Error in eval(expr, envir, enclos): object 'cf_nb_pca' not found
cf_norm <- normalize_expt(cf_expt, transform = "log2", convert = "cpm",
                          filter = TRUE, norm = "quant")
## Error in normalize_expt(cf_expt, transform = "log2", convert = "cpm", : object 'cf_expt' not found
test <- pca_information(cf_norm,
                        expt_factors = c("clinicalcategorical", "zymodemecategorical",
                                         "pathogenstrain", "passagenumber"),
                        num_components = 6, plot_pcas = TRUE)
## Error in pca_information(cf_norm, expt_factors = c("clinicalcategorical", : object 'cf_norm' not found
test$anova_p
## Error in eval(expr, envir, enclos): object 'test' not found
test$cor_heatmap
## Error in eval(expr, envir, enclos): object 'test' not found
sus_colors <- list(
    "resistant" = "#8563a7",
    "sensitive" = "#8d0000",
    "ambiguous" = "#cbcbcb",
    "unknown" = "#000000")
sus_expt <- set_expt_conditions(lp_expt, fact = "sus_category") %>%
  set_expt_batches(fact = "zymodemecategorical") %>%
  set_expt_colors(colors = sus_colors) %>%
  subset_expt(subset = "batch!='z24'") %>%
  subset_expt(subset = "batch!='z21'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'sampleNames': error in evaluating the argument 'object' in selecting a method for function 'sampleNames': error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_expt' not found
sus_norm <- normalize_expt(sus_expt, transform = "log2", convert = "cpm",
                           norm = "quant", filter = TRUE)
## Error in normalize_expt(sus_expt, transform = "log2", convert = "cpm", : object 'sus_expt' not found
sus_pca <- plot_pca(sus_norm, plot_title = "PCA of parasite expression values",
                    plot_labels = FALSE)
## Error in plot_pca(sus_norm, plot_title = "PCA of parasite expression values", : object 'sus_norm' not found
dev <- pp(file = "images/sus_norm_pca.png")
## Writing png?
sus_pca[["plot"]]
## Error in eval(expr, envir, enclos): object 'sus_pca' not found
closed <- dev.off()
sus_pca[["plot"]]
## Error in eval(expr, envir, enclos): object 'sus_pca' not found
sus_nb <- normalize_expt(sus_expt, transform = "log2", convert = "cpm",
                         batch = "svaseq", filter = TRUE)
## Error in normalize_expt(sus_expt, transform = "log2", convert = "cpm", : object 'sus_expt' not found
sus_nb_pca <- plot_pca(sus_nb, plot_title = "PCA of parasite expression values",
                       plot_labels = FALSE)
## Error in plot_pca(sus_nb, plot_title = "PCA of parasite expression values", : object 'sus_nb' not found
dev <- pp(file = "images/sus_nb_pca.png")
## Writing png?
sus_nb_pca[["plot"]]
## Error in eval(expr, envir, enclos): object 'sus_nb_pca' not found
closed <- dev.off()
sus_nb_pca[["plot"]]
## Error in eval(expr, envir, enclos): object 'sus_nb_pca' not found

6 Zymodeme analyses

The following sections perform a series of analyses which seek to elucidate differences between the zymodemes 2.2 and 2.3 either through differential expression or variant profiles.

6.1 Differential expression

6.1.1 With respect to zymodeme attribution

TODO: Do this with and without sva and compare the results.

zy_expt <- subset_expt(lp_expt, subset = "condition=='z2.2'|condition=='z2.3'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'sampleNames': object 'lp_expt' not found
zy_norm <- normalize_expt(zy_expt, filter = TRUE, convert = "cpm", norm = "quant")
## Error in normalize_expt(zy_expt, filter = TRUE, convert = "cpm", norm = "quant"): object 'zy_expt' not found
zy_de_nobatch <- all_pairwise(zy_expt, filter = TRUE, model_batch = FALSE)
## Error in normalize_expt(input, filter = filter): object 'zy_expt' not found
zy_table_nobatch <- combine_de_tables(
    zy_de_nobatch, excel = glue::glue("excel/zy_tables_nobatch-v{ver}.xlsx"),
    gmt = glue::glue("gmt/zymodeme_nobatch-v{ver}.gmt"))
## Error in combine_de_tables(zy_de_nobatch, excel = glue::glue("excel/zy_tables_nobatch-v{ver}.xlsx"), : object 'zy_de_nobatch' not found
zy_sig_nobatch <- extract_significant_genes(
    zy_table_nobatch,
    excel = glue::glue("excel/zy_sig_nobatch-v{ver}.xlsx"))
## Error in extract_significant_genes(zy_table_nobatch, excel = glue::glue("excel/zy_sig_nobatch-v{ver}.xlsx")): object 'zy_table_nobatch' not found
zy_de_sva <- all_pairwise(zy_expt, filter = TRUE, model_batch = "svaseq")
## Error in normalize_expt(input, filter = filter): object 'zy_expt' not found
zy_table_sva <- combine_de_tables(
    zy_de_sva, excel = glue::glue("excel/zy_tables_sva-v{ver}.xlsx"),
    gmt = glue::glue("gmt/zymodeme_sva-v{ver}.gmt"))
## Error in combine_de_tables(zy_de_sva, excel = glue::glue("excel/zy_tables_sva-v{ver}.xlsx"), : object 'zy_de_sva' not found
zy_sig_sva <- extract_significant_genes(
    zy_table_sva,
    excel = glue::glue("excel/zy_sig_sva-v{ver}.xlsx"))
## Error in extract_significant_genes(zy_table_sva, excel = glue::glue("excel/zy_sig_sva-v{ver}.xlsx")): object 'zy_table_sva' not found

6.1.2 Images of zymodeme DE

dev <- pp(file = "images/zymo_ma.png")
## Writing png?
zy_table_sva[["plots"]][["z23_vs_z22"]][["deseq_ma_plots"]][["plot"]]
## Error in eval(expr, envir, enclos): object 'zy_table_sva' not found
closed <- dev.off()
zy_table_sva[["plots"]][["z23_vs_z22"]][["deseq_ma_plots"]][["plot"]]
## Error in eval(expr, envir, enclos): object 'zy_table_sva' not found

6.2 With respect to cure/failure

In contrast, we can search for genes which are differentially expressed with respect to cure/failure status.

cf_de <- all_pairwise(cf_expt, filter = TRUE, model_batch = "svaseq")
## Error in normalize_expt(input, filter = filter): object 'cf_expt' not found
cf_table <- combine_de_tables(cf_de, excel = glue::glue("excel/cf_tables-v{ver}.xlsx"))
## Error in combine_de_tables(cf_de, excel = glue::glue("excel/cf_tables-v{ver}.xlsx")): object 'cf_de' not found
cf_sig <- extract_significant_genes(cf_table, excel = glue::glue("excel/cf_sig-v{ver}.xlsx"))
## Error in extract_significant_genes(cf_table, excel = glue::glue("excel/cf_sig-v{ver}.xlsx")): object 'cf_table' not found
dev <- pp(file = "images/cf_ma.png")
## Writing png?
cf_table[["plots"]][["fail_vs_cure"]][["deseq_ma_plots"]][["plot"]]
## Error in eval(expr, envir, enclos): object 'cf_table' not found
closed <- dev.off()
cf_table[["plots"]][["fail_vs_cure"]][["deseq_ma_plots"]][["plot"]]
## Error in eval(expr, envir, enclos): object 'cf_table' not found

6.3 With respect to susceptibility

Finally, we can use our category of susceptibility and look for genes which change from sensitive to resistant. Keep in mind, though, that for the moment we have a lot of ambiguous and unknown strains.

sus_de_sva <- all_pairwise(sus_expt, filter = TRUE, model_batch = "svaseq")
## Error in normalize_expt(input, filter = filter): object 'sus_expt' not found
sus_table_sva <- combine_de_tables(sus_de_sva, excel = glue::glue("excel/sus_tables_sva-v{ver}.xlsx"))
## Error in combine_de_tables(sus_de_sva, excel = glue::glue("excel/sus_tables_sva-v{ver}.xlsx")): object 'sus_de_sva' not found
sus_sig_sva <- extract_significant_genes(sus_table_sva, excel = glue::glue("excel/sus_sig_sva-v{ver}.xlsx"))
## Error in extract_significant_genes(sus_table_sva, excel = glue::glue("excel/sus_sig_sva-v{ver}.xlsx")): object 'sus_table_sva' not found
sus_de_nobatch <- all_pairwise(sus_expt, filter = TRUE, model_batch = FALSE)
## Error in normalize_expt(input, filter = filter): object 'sus_expt' not found
sus_table_nobatch <- combine_de_tables(sus_de_nobatch, excel = glue::glue("excel/sus_tables_nobatch-v{ver}.xlsx"))
## Error in combine_de_tables(sus_de_nobatch, excel = glue::glue("excel/sus_tables_nobatch-v{ver}.xlsx")): object 'sus_de_nobatch' not found
sus_sig_nobatch <- extract_significant_genes(sus_table_nobatch, excel = glue::glue("excel/sus_sig_nobatch-v{ver}.xlsx"))
## Error in extract_significant_genes(sus_table_nobatch, excel = glue::glue("excel/sus_sig_nobatch-v{ver}.xlsx")): object 'sus_table_nobatch' not found
knitr::kable(head(sus_sig_sva$deseq$ups$sensitive_vs_resistant, n = 20))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'head': object 'sus_sig_sva' not found
knitr::kable(head(sus_sig_sva$deseq$downs$sensitive_vs_resistant, n = 20))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'head': object 'sus_sig_sva' not found
sus_ma <- sus_table_sva[["plots"]][["sensitive_vs_resistant"]][["deseq_ma_plots"]][["plot"]]
## Error in eval(expr, envir, enclos): object 'sus_table_sva' not found
dev <- pp(file = "images/sus_ma_sva.png")
## Writing png?
sus_ma
## Error in eval(expr, envir, enclos): object 'sus_ma' not found
closed <- dev.off()
sus_ma
## Error in eval(expr, envir, enclos): object 'sus_ma' not found
## test <- ggplt(sus_ma)

6.4 Ontology searches

Now let us look for ontology categories which are increased in the 2.3 samples followed by the 2.2 samples.

## Gene categories more represented in the 2.3 group.
zy_go_up <- simple_goseq(sig_genes = zy_sig_sva[["deseq"]][["ups"]][[1]],
                         go_db = lp_go, length_db = lp_lengths)
## Error in simple_goseq(sig_genes = zy_sig_sva[["deseq"]][["ups"]][[1]], : object 'zy_sig_sva' not found
## Gene categories more represented in the 2.2 group.
zy_go_down <- simple_goseq(sig_genes = zy_sig_sva[["deseq"]][["downs"]][[1]],
                           go_db = lp_go, length_db = lp_lengths)
## Error in simple_goseq(sig_genes = zy_sig_sva[["deseq"]][["downs"]][[1]], : object 'zy_sig_sva' not found

6.4.1 A couple plots from the differential expression

6.4.1.1 Number of genes in agreement among DE methods, 2.3 more than 2.2

In the function ‘combined_de_tables()’ above, one of the tasks performed is to look at the agreement among DESeq2, limma, and edgeR. The following show a couple of these for the set of genes observed with a fold-change >= |2| and adjusted p-value <= 0.05.

zy_table_sva[["venns"]][[1]][["p_lfc1"]][["up_noweight"]]
## Error in eval(expr, envir, enclos): object 'zy_table_sva' not found

6.4.1.2 Number of genes in agreement among DE methods, 2.2 more than 2.3

zy_table_sva[["venns"]][[1]][["p_lfc1"]][["down_noweight"]]
## Error in eval(expr, envir, enclos): object 'zy_table_sva' not found

6.4.1.3 goseq ontology plots of groups of genes, 2.3 more than 2.2

zy_go_up[["pvalue_plots"]][["bpp_plot_over"]]
## Error in eval(expr, envir, enclos): object 'zy_go_up' not found

6.4.1.4 goseq ontology plots of groups of genes, 2.2 more than 2.3

zy_go_down[["pvalue_plots"]][["bpp_plot_over"]]
## Error in eval(expr, envir, enclos): object 'zy_go_down' not found

6.5 Look for agreement between sensitivity and zymodemes

Remind myself, the data structures are (zy|sus)_(de|table|sig).

zy_df <- zy_table_sva[["data"]][["z23_vs_z22"]]
## Error in eval(expr, envir, enclos): object 'zy_table_sva' not found
sus_df <- sus_table_sva[["data"]][["sensitive_vs_resistant"]]
## Error in eval(expr, envir, enclos): object 'sus_table_sva' not found
both_df <- merge(zy_df, sus_df, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'zy_df' not found
plot_df <- both_df[, c("deseq_logfc.x", "deseq_logfc.y")]
## Error in eval(expr, envir, enclos): object 'both_df' not found
rownames(plot_df) <- both_df[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'both_df' not found
colnames(plot_df) <- c("z23_vs_z22", "sensitive_vs_resistant")
## Error in colnames(plot_df) <- c("z23_vs_z22", "sensitive_vs_resistant"): object 'plot_df' not found
compare <- plot_linear_scatter(plot_df)
## Error in data.frame(df[, c(1, 2)]): object 'plot_df' not found
dev <- pp(file = "images/compare_sus_zy.png")
## Writing png?
compare$scatter
## Error in compare$scatter: object of type 'closure' is not subsettable
closed <- dev.off()
compare$scatter
## Error in compare$scatter: object of type 'closure' is not subsettable
compare$cor
## Error in compare$cor: object of type 'closure' is not subsettable

6.6 Zymodeme enzyme gene IDs

Najib read me an email listing off the gene names associated with the zymodeme classification. I took those names and cross referenced them against the Leishmania panamensis gene annotations and found the following:

They are:

  1. ALAT: LPAL13_120010900 – alanine aminotransferase
  2. ASAT: LPAL13_340013000 – aspartate aminotransferase
  3. G6PD: LPAL13_000054100 – glucase-6-phosphate 1-dehydrogenase
  4. NH: LPAL13_14006100, LPAL13_180018500 – inosine-guanine nucleoside hydrolase
  5. MPI: LPAL13_320022300 (maybe) – mannose phosphate isomerase (I chose phosphomannose isomerase)

Given these 6 gene IDs (NH has two gene IDs associated with it), I can do some looking for specific differences among the various samples.

6.6.1 Expression levels of zymodeme genes

The following creates a colorspace (red to green) heatmap showing the observed expression of these genes in every sample.

my_genes <- c("LPAL13_120010900", "LPAL13_340013000", "LPAL13_000054100",
              "LPAL13_140006100", "LPAL13_180018500", "LPAL13_320022300",
              "other")
my_names <- c("ALAT", "ASAT", "G6PD", "NHv1", "NHv2", "MPI", "other")

zymo_expt <- exclude_genes_expt(zy_norm, ids = my_genes, method = "keep")
## Error in exclude_genes_expt(zy_norm, ids = my_genes, method = "keep"): object 'zy_norm' not found
zymo_heatmap <- plot_sample_heatmap(zymo_expt, row_label = my_names)
## Error in plot_sample_heatmap(zymo_expt, row_label = my_names): object 'zymo_expt' not found
zymo_heatmap
## Error in eval(expr, envir, enclos): object 'zymo_heatmap' not found

6.7 Empirically observed Zymodeme genes from differential expression analysis

In contrast, the following plots take the set of genes which are shared among all differential expression methods (|lfc| >= 1.0 and adjp <= 0.05) and use them to make categories of genes which are increased in 2.3 or 2.2.

shared_zymo <- intersect_significant(zy_table_sva)
## Deleting the file excel/intersect_significant.xlsx before writing the tables.
## Error in is.data.frame(x): object 'zy_table_sva' not found
up_shared <- shared_zymo[["ups"]][[1]][["data"]][["all"]]
## Error in eval(expr, envir, enclos): object 'shared_zymo' not found
rownames(up_shared)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'up_shared' not found
upshared_expt <- exclude_genes_expt(zy_norm, ids = rownames(up_shared), method = "keep")
## Error in exclude_genes_expt(zy_norm, ids = rownames(up_shared), method = "keep"): object 'zy_norm' not found

We can plot a quick heatmap to get a sense of the differences observed between the genes which are different between the two zymodemes.

6.7.1 Heatmap of zymodeme gene expression increased in 2.3 vs. 2.2

high_23_heatmap <- plot_sample_heatmap(upshared_expt, row_label = rownames(up_shared))
## Error in plot_sample_heatmap(upshared_expt, row_label = rownames(up_shared)): object 'upshared_expt' not found
high_23_heatmap
## Error in eval(expr, envir, enclos): object 'high_23_heatmap' not found

6.7.2 Heatmap of zymodeme gene expression increased in 2.2 vs. 2.3

down_shared <- shared_zymo[["downs"]][[1]][["data"]][["all"]]
## Error in eval(expr, envir, enclos): object 'shared_zymo' not found
downshared_expt <- exclude_genes_expt(zy_norm, ids = rownames(down_shared), method = "keep")
## Error in exclude_genes_expt(zy_norm, ids = rownames(down_shared), method = "keep"): object 'zy_norm' not found
high_22_heatmap <- plot_sample_heatmap(downshared_expt, row_label = rownames(down_shared))
## Error in plot_sample_heatmap(downshared_expt, row_label = rownames(down_shared)): object 'downshared_expt' not found
high_22_heatmap
## Error in eval(expr, envir, enclos): object 'high_22_heatmap' not found

7 SNP profiles

Over the last couple of weeks, I redid all the variant searches with a newer, (I think) more sensitive and more specific variant tool. In addition I changed my script which interprets the results so that it is able to extract any tags from it, instead of just the one or two that my previous script handled. In addition, at least in theory it is now able to provide the set of amino acid substitutions for every gene in species without or with introns (not really relevant for Leishmania panamensis).

However, as of this writing, I have not re-performed the same tasks with the 2016 data, primarily because it will require remapping all of the samples. As a result, for the moment I cannot combine the older and newer samples. Thus, any of the following blocks which use the 2016 data are currently disabled.

old_expt <- create_expt("sample_sheets/tmrc2_samples_20191203.xlsx",
                        file_column = "tophat2file")

tt <- lp_expt[["expressionset"]]
rownames(tt) <- gsub(pattern = "^exon_", replacement = "", x = rownames(tt))
rownames(tt) <- gsub(pattern = "\\.E1$", replacement = "", x = rownames(tt))
lp_expt$expressionset <- tt

tt <- old_expt$expressionset
rownames(tt) <- gsub(pattern = "^exon_", replacement = "", x = rownames(tt))
rownames(tt) <- gsub(pattern = "\\.1$", replacement = "", x = rownames(tt))
old_expt$expressionset <- tt
rm(tt)

7.1 Create the SNP expressionset

One other important caveat, we have a group of new samples which have not yet run through the variant search pipeline, so I need to remove them from consideration. Though it looks like they finished overnight…

## The next line drops the samples which are missing the SNP pipeline.
lp_snp <- subset_expt(lp_expt, subset="!is.na(pData(lp_expt)[['freebayessummary']])")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'sampleNames': object 'lp_expt' not found
new_snps <- count_expt_snps(lp_snp, annot_column = "freebayessummary", snp_column="PAIRED")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'lp_snp' not found
nonzero_snps <- exprs(new_snps) != 0
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'new_snps' not found
colSums(nonzero_snps)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'colSums': object 'nonzero_snps' not found
## My old_snps is using an older annotation incorrectly, so fix it here:
Biobase::annotation(old_snps$expressionset) = Biobase::annotation(new_snps$expressionset)
both_snps <- combine_expts(new_snps, old_snps)
both_norm <- normalize_expt(both_snps, transform = "log2", convert = "cpm", filter = TRUE)

## strains <- both_norm[["design"]][["strain"]]
both_strain <- set_expt_conditions(both_norm, fact = "strain")

The data structure ‘both_norm’ now contains our 2016 data along with the newer data collected since 2019.

7.2 Plot of SNP profiles for zymodemes

The following plot shows the SNP profiles of all samples (old and new) where the colors at the top show either the 2.2 strains (orange), 2.3 strains (green), the previous samples (purple), or the various lab strains (pink etc).

new_variant_heatmap <- plot_disheat(new_snps)
## Error in plot_heatmap(expt_data, expt_colors = expt_colors, expt_design = expt_design, : object 'new_snps' not found
dev <- pp(file = "images/raw_snp_disheat.png", height=12, width=12)
## Writing png?
new_variant_heatmap$plot
## Error in eval(expr, envir, enclos): object 'new_variant_heatmap' not found
closed <- dev.off()
new_variant_heatmap$plot
## Error in eval(expr, envir, enclos): object 'new_variant_heatmap' not found

The function get_snp_sets() takes the provided metadata factor (in this case ‘condition’) and looks for variants which are exclusive to each element in it. In this case, this is looking for differences between 2.2 and 2.3, as well as the set shared among them.

snp_sets <- get_snp_sets(both_snps, factor = "condition")
Biobase::annotation(old_expt$expressionset) = Biobase::annotation(lp_expt$expressionset)
both_expt <- combine_expts(lp_expt, old_expt)

snp_genes <- sm(snps_vs_genes(both_expt, snp_sets, expt_name_col = "chromosome"))
## I think we have some metrics here we can plot...
snp_subset <- sm(snp_subset_genes(
  both_expt, both_snps,
  genes = c("LPAL13_120010900", "LPAL13_340013000", "LPAL13_000054100",
            "LPAL13_140006100", "LPAL13_180018500", "LPAL13_320022300")))
zymo_heat <- plot_sample_heatmap(snp_subset, row_label = rownames(exprs(snp_subset)))
zymo_heat

Didn’t I create a set of densities by chromosome? Oh I think they come in from get_snp_sets()

7.3 SNPS associated with clinical response in the TMRC samples

clinical_sets <- get_snp_sets(new_snps, factor = "clinicalresponse")
## Error in get_snp_sets(new_snps, factor = "clinicalresponse"): object 'new_snps' not found
density_vec <- clinical_sets[["density"]]
## Error in eval(expr, envir, enclos): object 'clinical_sets' not found
chromosome_idx <- grep(pattern = "LpaL", x = names(density_vec))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'grep': object 'density_vec' not found
density_df <- as.data.frame(density_vec[chromosome_idx])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'density_vec' not found
density_df[["chr"]] <- rownames(density_df)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'density_df' not found
colnames(density_df) <- c("density_vec", "chr")
## Error in colnames(density_df) <- c("density_vec", "chr"): object 'density_df' not found
ggplot(density_df, aes_string(x = "chr", y = "density_vec")) +
  ggplot2::geom_col() +
  ggplot2::theme(axis.text = ggplot2::element_text(size = 10, colour = "black"),
                 axis.text.x = ggplot2::element_text(angle = 90, vjust = 0.5))
## Error in ggplot(density_df, aes_string(x = "chr", y = "density_vec")): object 'density_df' not found
## clinical_written <- write_variants(new_snps)

7.3.1 Cross reference these variants by gene

clinical_genes <- sm(snps_vs_genes(lp_expt, clinical_sets, expt_name_col = "chromosome"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'fData': object 'lp_expt' not found
snp_density <- merge(as.data.frame(clinical_genes[["summary_by_gene"]]),
                     as.data.frame(fData(lp_expt)),
                     by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'clinical_genes' not found
snp_density <- snp_density[, c(1, 2, 4, 15)]
## Error in eval(expr, envir, enclos): object 'snp_density' not found
colnames(snp_density) <- c("name", "snps", "product", "length")
## Error in colnames(snp_density) <- c("name", "snps", "product", "length"): object 'snp_density' not found
snp_density[["product"]] <- tolower(snp_density[["product"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'tolower': object 'snp_density' not found
snp_density[["length"]] <- as.numeric(snp_density[["length"]])
## Error in eval(expr, envir, enclos): object 'snp_density' not found
snp_density[["density"]] <- snp_density[["snps"]] / snp_density[["length"]]
## Error in eval(expr, envir, enclos): object 'snp_density' not found
snp_idx <- order(snp_density[["density"]], decreasing = TRUE)
## Error in eval(quote(list(...)), env): object 'snp_density' not found
snp_density <- snp_density[snp_idx, ]
## Error in eval(expr, envir, enclos): object 'snp_density' not found
removers <- c("amastin", "gp63", "leishmanolysin")
for (r in removers) {
  drop_idx <- grepl(pattern = r, x = snp_density[["product"]])
  snp_density <- snp_density[!drop_idx, ]
}
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'grepl': object 'snp_density' not found
## Filter these for [A|a]mastin gp63 Leishmanolysin
clinical_snps <- snps_intersections(lp_expt, clinical_sets, chr_column = "chromosome")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'fData': object 'lp_expt' not found
fail_ref_snps <- as.data.frame(clinical_snps[["inters"]][["failure, reference strain"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'clinical_snps' not found
fail_ref_snps <- rbind(fail_ref_snps,
                       as.data.frame(clinical_snps[["inters"]][["failure"]]))
## Error in eval(quote(list(...)), env): object 'fail_ref_snps' not found
cure_snps <- as.data.frame(clinical_snps[["inters"]][["cure"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'clinical_snps' not found
head(fail_ref_snps)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'head': object 'fail_ref_snps' not found
head(cure_snps)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'head': object 'cure_snps' not found
write.csv(file="csv/cure_variants.txt", x=rownames(cure_snps))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'cure_snps' not found
write.csv(file="csv/fail_variants.txt", x=rownames(fail_ref_snps))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'fail_ref_snps' not found
annot <- fData(lp_expt)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'fData': object 'lp_expt' not found
clinical_interest <- as.data.frame(clinical_snps[["gene_summaries"]][["cure"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'clinical_snps' not found
clinical_interest <- merge(clinical_interest,
                           as.data.frame(clinical_snps[["gene_summaries"]][["failure, reference strain"]]),
                           by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'clinical_interest' not found
rownames(clinical_interest) <- clinical_interest[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'clinical_interest' not found
clinical_interest[["Row.names"]] <- NULL
## Error in clinical_interest[["Row.names"]] <- NULL: object 'clinical_interest' not found
colnames(clinical_interest) <- c("cure_snps","fail_snps")
## Error in colnames(clinical_interest) <- c("cure_snps", "fail_snps"): object 'clinical_interest' not found
annot <- merge(annot, clinical_interest, by = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'annot' not found
rownames(annot) <- annot[["Row.names"]]
## Error in eval(expr, envir, enclos): object 'annot' not found
annot[["Row.names"]] <- NULL
## Error in annot[["Row.names"]] <- NULL: object 'annot' not found
fData(lp_expt$expressionset) <- annot
## Error in eval(expr, envir, enclos): object 'annot' not found

8 Zymodeme for new samples

The heatmap produced here should show the variants only for the zymodeme genes.

8.1 Hunt for snp clusters

I am thinking that if we find clusters of locations which are variant, that might provide some PCR testing possibilities.

## Drop the 2.1, 2.4, unknown, and null
pruned_snps <- subset_expt(new_snps, subset="condition=='z2.2'|condition=='z2.3'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'sampleNames': object 'new_snps' not found
new_sets <- get_snp_sets(pruned_snps, factor = "zymodemecategorical")
## Error in get_snp_sets(pruned_snps, factor = "zymodemecategorical"): object 'pruned_snps' not found
summary(new_sets)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'new_sets' not found
## 1000000: 2.2
## 0100000: 2.3

summary(new_sets[["intersections"]][["10"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'new_sets' not found
write.csv(file="csv/variants_22.csv", x=new_sets[["intersections"]][["10"]])
## Error in is.data.frame(x): object 'new_sets' not found
summary(new_sets[["intersections"]][["01"]])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'summary': object 'new_sets' not found
write.csv(file="csv/variants_23.csv", x=new_sets[["intersections"]][["01"]])
## Error in is.data.frame(x): object 'new_sets' not found

Thus we see that there are 3,553 variants associated with 2.2 and 81,589 associated with 2.3.

8.1.1 A small function for searching for potential PCR primers

The following function uses the positional data to look for sequential mismatches associated with zymodeme in the hopes that there will be some regions which would provide good potential targets for a PCR-based assay.

sequential_variants <- function(snp_sets, conditions = NULL, minimum = 3, maximum_separation = 3) {
  if (is.null(conditions)) {
    conditions <- 1
  }
  intersection_sets <- snp_sets[["intersections"]]
  intersection_names <- snp_sets[["set_names"]]
  chosen_intersection <- 1
  if (is.numeric(conditions)) {
    chosen_intersection <- conditions
  } else {
    intersection_idx <- intersection_names == conditions
    chosen_intersection <- names(intersection_names)[intersection_idx]
  }

  possible_positions <- intersection_sets[[chosen_intersection]]
  position_table <- data.frame(row.names = possible_positions)
  pat <- "^chr_(.+)_pos_(.+)_ref_.*$"
  position_table[["chr"]] <- gsub(pattern = pat, replacement = "\\1", x = rownames(position_table))
  position_table[["pos"]] <- as.numeric(gsub(pattern = pat, replacement = "\\2", x = rownames(position_table)))
  position_idx <- order(position_table[, "chr"], position_table[, "pos"])
  position_table <- position_table[position_idx, ]
  position_table[["dist"]] <- 0

  last_chr <- ""
  for (r in 1:nrow(position_table)) {
    this_chr <- position_table[r, "chr"]
    if (r == 1) {
      position_table[r, "dist"] <- position_table[r, "pos"]
      last_chr <- this_chr
      next
    }
    if (this_chr == last_chr) {
      position_table[r, "dist"] <- position_table[r, "pos"] - position_table[r - 1, "pos"]
    } else {
      position_table[r, "dist"] <- position_table[r, "pos"]
    }
    last_chr <- this_chr
  }

  ## Working interactively here.

  doubles <- position_table[["dist"]] == 1
  doubles <- position_table[doubles, ]
  write.csv(doubles, "doubles.csv")

  one_away <- position_table[["dist"]] == 2
  one_away <- position_table[one_away, ]
  write.csv(one_away, "one_away.csv")

  two_away <- position_table[["dist"]] == 3
  two_away <- position_table[two_away, ]
  write.csv(two_away, "two_away.csv")

  combined <- rbind(doubles, one_away)
  combined <- rbind(combined, two_away)
  position_idx <- order(combined[, "chr"], combined[, "pos"])
  combined <- combined[position_idx, ]

  this_chr <- ""
  for (r in 1:nrow(combined)) {
    this_chr <- combined[r, "chr"]
    if (r == 1) {
      combined[r, "dist_pair"] <- combined[r, "pos"]
      last_chr <- this_chr
      next
    }
    if (this_chr == last_chr) {
      combined[r, "dist_pair"] <- combined[r, "pos"] - combined[r - 1, "pos"]
    } else {
      combined[r, "dist_pair"] <- combined[r, "pos"]
    }
    last_chr <- this_chr
  }

  dist_pair_maximum <- 1000
  dist_pair_minimum <- 200
  dist_pair_idx <- combined[["dist_pair"]] <= dist_pair_maximum &
    combined[["dist_pair"]] >= dist_pair_minimum
  remaining <- combined[dist_pair_idx, ]
  no_weak_idx <- grepl(pattern="ref_(G|C)", x=rownames(remaining))
  remaining <- remaining[no_weak_idx, ]

  print(head(table(position_table[["dist"]])))
  sequentials <- position_table[["dist"]] <= maximum_separation
  message("There are ", sum(sequentials), " candidate regions.")

  ## The following can tell me how many runs of each length occurred, that is not quite what I want.
  ## Now use run length encoding to find the set of sequential sequentials!
  rle_result <- rle(sequentials)
  rle_values <- rle_result[["values"]]
  ## The following line is equivalent to just leaving values alone:
  ## true_values <- rle_result[["values"]] == TRUE
  rle_lengths <- rle_result[["lengths"]]
  true_sequentials <- rle_lengths[rle_values]
  rle_idx <- cumsum(rle_lengths)[which(rle_values)]

  position_table[["last_sequential"]] <- 0
  count <- 0
  for (r in rle_idx) {
    count <- count + 1
    position_table[r, "last_sequential"] <- true_sequentials[count]
  }
  message("The maximum sequential set is: ", max(position_table[["last_sequential"]]), ".")

  wanted_idx <- position_table[["last_sequential"]] >= minimum
  wanted <- position_table[wanted_idx, c("chr", "pos")]
  return(wanted)
}

zymo22_sequentials <- sequential_variants(new_sets, conditions = "z22", minimum=1, maximum_separation=2)
dim(zymo22_sequentials)
## 7 candidate regions for zymodeme 2.2 -- thus I am betting that the reference strain is a 2.2
zymo23_sequentials <- sequential_variants(new_sets, conditions = "z23",
                                          minimum = 2, maximum_separation = 2)
dim(zymo23_sequentials)
## In contrast, there are lots (587) of interesting regions for 2.3!

8.1.2 Extract a promising region from the genome

The first 4 candidate regions from my set of remaining: * Chr Pos. Distance * LpaL13-15 238433 448 * LpaL13-18 142844 613 * LpaL13-29 830342 252 * LpaL13-33 1331507 843

Lets define a couple of terms: * Third: Each of the 4 above positions. * Second: Third - Distance * End: Third + PrimerLen * Start: Second - Primerlen

In each instance, these are the last positions, so we want to grab three things:

  • The entire region from End -> Start, this way we can have a quick sanity check.
  • Start -> Second.
  • (Third -> End) <- Reverse complemented
## * LpaL13-15 238433 448
first_candidate_chr <- genome[["LpaL13_15"]]
primer_length <- 22
amplicon_length <- 448
first_candidate_third <- 238433
first_candidate_second <- first_candidate_third - amplicon_length
first_candidate_start <- first_candidate_second - primer_length
first_candidate_end <- first_candidate_third + primer_length
first_candidate_region <- subseq(first_candidate_chr, first_candidate_start, first_candidate_end)
first_candidate_region
first_candidate_5p <- subseq(first_candidate_chr, first_candidate_start, first_candidate_second)
as.character(first_candidate_5p)
first_candidate_3p <- spgs::reverseComplement(subseq(first_candidate_chr, first_candidate_third, first_candidate_end))
first_candidate_3p


## * LpaL13-18 142844 613
second_candidate_chr <- genome[["LpaL13_18"]]
primer_length <- 22
amplicon_length <- 613
second_candidate_third <- 142844
second_candidate_second <- second_candidate_third - amplicon_length
second_candidate_start <- second_candidate_second - primer_length
second_candidate_end <- second_candidate_third + primer_length
second_candidate_region <- subseq(second_candidate_chr, second_candidate_start, second_candidate_end)
second_candidate_region
second_candidate_5p <- subseq(second_candidate_chr, second_candidate_start, second_candidate_second)
as.character(second_candidate_5p)
second_candidate_3p <- spgs::reverseComplement(subseq(second_candidate_chr, second_candidate_third, second_candidate_end))
second_candidate_3p


## * LpaL13-29 830342 252
third_candidate_chr <- genome[["LpaL13_29"]]
primer_length <- 22
amplicon_length <- 252
third_candidate_third <- 830342
third_candidate_second <- third_candidate_third - amplicon_length
third_candidate_start <- third_candidate_second - primer_length
third_candidate_end <- third_candidate_third + primer_length
third_candidate_region <- subseq(third_candidate_chr, third_candidate_start, third_candidate_end)
third_candidate_region
third_candidate_5p <- subseq(third_candidate_chr, third_candidate_start, third_candidate_second)
as.character(third_candidate_5p)
third_candidate_3p <- spgs::reverseComplement(subseq(third_candidate_chr, third_candidate_third, third_candidate_end))
third_candidate_3p
## You are a garbage polypyrimidine tract.
## Which is actually interesting if the mutations mess it up.


## * LpaL13-33 1331507 843
fourth_candidate_chr <- genome[["LpaL13_33"]]
primer_length <- 22
amplicon_length <- 843
fourth_candidate_third <- 1331507
fourth_candidate_second <- fourth_candidate_third - amplicon_length
fourth_candidate_start <- fourth_candidate_second - primer_length
fourth_candidate_end <- fourth_candidate_third + primer_length
fourth_candidate_region <- subseq(fourth_candidate_chr, fourth_candidate_start, fourth_candidate_end)
fourth_candidate_region
fourth_candidate_5p <- subseq(fourth_candidate_chr, fourth_candidate_start, fourth_candidate_second)
as.character(fourth_candidate_5p)
fourth_candidate_3p <- spgs::reverseComplement(subseq(fourth_candidate_chr, fourth_candidate_third, fourth_candidate_end))
fourth_candidate_3p

8.2 Go hunting for Sanger sequencing regions

I made a fun little function which should find regions which have lots of variants associated with a given experimental factor.

pheno <- subset_expt(lp_expt, subset = "condition=='z2.2'|condition=='z2.3'")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'sampleNames': object 'lp_expt' not found
pheno <- subset_expt(pheno, subset = "!is.na(pData(pheno)[['bcftable']])")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'sampleNames': object 'pheno' not found
pheno_snps <- sm(count_expt_snps(pheno, annot_column = "bcftable"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'pheno' not found
fun_stuff <- snp_density_primers(
    pheno_snps,
    bsgenome = "BSGenome.Leishmania.panamensis.MHOMCOL81L13.v53",
    gff = "reference/TriTrypDB-53_LpanamensisMHOMCOL81L13.gff")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'pheno_snps' not found
drop_scaffolds <- grepl(x = rownames(fun_stuff$favorites), pattern = "SCAF")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'grepl': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'fun_stuff' not found
favorite_primer_regions <- fun_stuff[["favorites"]][!drop_scaffolds, ]
## Error in eval(expr, envir, enclos): object 'fun_stuff' not found
favorite_primer_regions[["bin"]] <- rownames(favorite_primer_regions)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'favorite_primer_regions' not found
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:Biostrings':
## 
##     collapse, intersect, setdiff, setequal, union
## The following object is masked from 'package:XVector':
## 
##     slice
## The following object is masked from 'package:AnnotationDbi':
## 
##     select
## The following object is masked from 'package:hpgltools':
## 
##     combine
## The following object is masked from 'package:testthat':
## 
##     matches
## The following objects are masked from 'package:GenomicRanges':
## 
##     intersect, setdiff, union
## The following object is masked from 'package:GenomeInfoDb':
## 
##     intersect
## The following objects are masked from 'package:IRanges':
## 
##     collapse, desc, intersect, setdiff, slice, union
## The following objects are masked from 'package:S4Vectors':
## 
##     first, intersect, rename, setdiff, setequal, union
## The following object is masked from 'package:matrixStats':
## 
##     count
## The following object is masked from 'package:Biobase':
## 
##     combine
## The following objects are masked from 'package:BiocGenerics':
## 
##     combine, intersect, setdiff, union
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
favorite_primer_regions <- favorite_primer_regions %>%
  relocate(bin)
## Error in relocate(., bin): object 'favorite_primer_regions' not found

8.3 Combine this table with 2.2/2.3 genes

Here is my note from our meeting:

Cross reference primers to DE genes of 2.2/2.3 and/or resistance/suscpetible, add a column to the primer spreadsheet with the DE genes (in retrospect I am guessing this actually means to put the logFC as a column.

One nice thing, I did a semantic removal on the lp_expt, so the set of logFC/pvalues should not have any of the offending types; thus I should be able to automagically get rid of them in the merge.

logfc <- zy_table_sva[["data"]][["z23_vs_z22"]]
## Error in eval(expr, envir, enclos): object 'zy_table_sva' not found
logfc_columns <- logfc[, c("deseq_logfc", "deseq_adjp")]
## Error in eval(expr, envir, enclos): object 'logfc' not found
colnames(logfc_columns) <- c("z23_logfc", "z23_adjp")
## Error in colnames(logfc_columns) <- c("z23_logfc", "z23_adjp"): object 'logfc_columns' not found
new_table <- merge(favorite_primer_regions, logfc_columns,
                   by.x = "closest_gene_before_id", by.y = "row.names")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'favorite_primer_regions' not found
sus <- sus_table_sva[["data"]][["sensitive_vs_resistant"]]
## Error in eval(expr, envir, enclos): object 'sus_table_sva' not found
sus_columns <- sus[, c("deseq_logfc", "deseq_adjp")]
## Error in eval(expr, envir, enclos): object 'sus' not found
colnames(sus_columns) <- c("sus_logfc", "sus_adjp")
## Error in colnames(sus_columns) <- c("sus_logfc", "sus_adjp"): object 'sus_columns' not found
new_table <- merge(new_table, sus_columns,
                   by.x = "closest_gene_before_id", by.y = "row.names") %>%
  relocate(bin)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'new_table' not found
written <- write_xlsx(data=new_table,
                      excel="excel/favorite_primers_xref_zy_sus.xlsx")
## Error in write_xlsx(data = new_table, excel = "excel/favorite_primers_xref_zy_sus.xlsx"): object 'new_table' not found

8.4 Make a heatmap describing the clustering of variants

We can cross reference the variants against the zymodeme status and plot a heatmap of the results and hopefully see how they separate.

## pruned_snps <- subset_expt(new_snps, subset="condition=='z2.2'|condition=='z2.3'")
snp_genes <- sm(snps_vs_genes(lp_expt, new_sets, expt_name_col = "chromosome"))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'fData': object 'lp_expt' not found
##new_zymo_norm <- normalize_expt(pruned_snps, filter = TRUE, convert = "cpm", norm = "quant", transform = TRUE)
##new_zymo_norm <- set_expt_conditions(new_zymo_norm, fact = "zymodemecategorical")
clinical_colors_v2 <- list(
    "z22" = "#0000cc",
    "z23" = "#cc0000")
new_zymo_norm <- normalize_expt(pruned_snps, filter = TRUE, convert = "cpm", norm = "quant", transform = TRUE) %>%
  set_expt_conditions(fact = "zymodemecategorical") %>%
  set_expt_colors(clinical_colors_v2)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': error in evaluating the argument 'object' in selecting a method for function 'pData': object 'pruned_snps' not found
zymo_heat <- plot_disheat(new_zymo_norm)
## Error in plot_heatmap(expt_data, expt_colors = expt_colors, expt_design = expt_design, : object 'new_zymo_norm' not found
dev <- pp(file = "images/onlyz22_z23_snp_heatmap.pdf", width=12, height=12)
zymo_heat[["plot"]]
## Error in eval(expr, envir, enclos): object 'zymo_heat' not found
closed <- dev.off()
zymo_heat[["plot"]]
## Error in eval(expr, envir, enclos): object 'zymo_heat' not found

8.4.1 Annotated heatmap of variants

Now let us try to make a heatmap which includes some of the annotation data.

des <- both_norm[["design"]]
## Error in eval(expr, envir, enclos): object 'both_norm' not found
undef_idx <- is.na(des[["strain"]])
## Error in eval(expr, envir, enclos): object 'des' not found
des[undef_idx, "strain"] <- "unknown"
## Error in des[undef_idx, "strain"] <- "unknown": object 'des' not found
##hmcols <- colorRampPalette(c("yellow","black","darkblue"))(256)
correlations <- hpgl_cor(exprs(both_norm))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'both_norm' not found
zymo_missing_idx <- is.na(des[["zymodemecategorical"]])
## Error in eval(expr, envir, enclos): object 'des' not found
des[["zymodemecategorical"]] <- as.character(des[["zymodemecategorical"]])
## Error in eval(expr, envir, enclos): object 'des' not found
des[["clinicalcategorical"]] <- as.character(des[["clinicalcategorical"]])
## Error in eval(expr, envir, enclos): object 'des' not found
des[zymo_missing_idx, "zymodemecategorical"] <- "unknown"
## Error in des[zymo_missing_idx, "zymodemecategorical"] <- "unknown": object 'des' not found
mydendro <- list(
  "clustfun" = hclust,
  "lwd" = 2.0)
col_data <- as.data.frame(des[, c("zymodemecategorical", "clinicalcategorical")])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'des' not found
unknown_clinical <- is.na(col_data[["clinicalcategorical"]])
## Error in eval(expr, envir, enclos): object 'col_data' not found
row_data <- as.data.frame(des[, c("strain")])
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'as.data.frame': object 'des' not found
colnames(col_data) <- c("zymodeme", "outcome")
## Error in colnames(col_data) <- c("zymodeme", "outcome"): object 'col_data' not found
col_data[unknown_clinical, "outcome"] <- "undefined"
## Error in col_data[unknown_clinical, "outcome"] <- "undefined": object 'col_data' not found
colnames(row_data) <- c("strain")
## Error in colnames(row_data) <- c("strain"): object 'row_data' not found
myannot <- list(
  "Col" = list("data" = col_data),
  "Row" = list("data" = row_data))
## Error in eval(expr, envir, enclos): object 'col_data' not found
myclust <- list("cuth" = 1.0,
                "col" = BrewerClusterCol)
mylabs <- list(
  "Row" = list("nrow" = 4),
  "Col" = list("nrow" = 4))
hmcols <- colorRampPalette(c("darkblue", "beige"))(240)
zymo_annot_heat <- annHeatmap2(
    correlations,
    dendrogram = mydendro,
    annotation = myannot,
    cluster = myclust,
    labels = mylabs,
    ## The following controls if the picture is symmetric
    scale = "none",
    col = hmcols)
## Error in annHeatmap2(correlations, dendrogram = mydendro, annotation = myannot, : object 'correlations' not found
dev <- pp(file = "images/dendro_heatmap.png", height = 20, width = 20)
## Writing png?
plot(zymo_annot_heat)
## Error in plot(zymo_annot_heat): object 'zymo_annot_heat' not found
closed <- dev.off()
plot(zymo_annot_heat)
## Error in plot(zymo_annot_heat): object 'zymo_annot_heat' not found

Print the larger heatmap so that all the labels appear. Keep in mind that as we get more samples, this image needs to continue getting bigger.

big heatmap

xref_prop <- table(pheno_snps[["conditions"]])
## Error in eval(quote(list(...)), env): object 'pheno_snps' not found
pheno_snps$conditions
## Error in eval(expr, envir, enclos): object 'pheno_snps' not found
idx_tbl <- exprs(pheno_snps) > 5
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'pheno_snps' not found
new_tbl <- data.frame(row.names = rownames(exprs(pheno_snps)))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'pheno_snps' not found
for (n in names(xref_prop)) {
  new_tbl[[n]] <- 0
  idx_cols <- which(pheno_snps[["conditions"]] == n)
  prop_col <- rowSums(idx_tbl[, idx_cols]) / xref_prop[n]
  new_tbl[n] <- prop_col
}
## Error in eval(expr, envir, enclos): object 'xref_prop' not found
keepers <- grepl(x = rownames(new_tbl), pattern = "LpaL13")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'grepl': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'new_tbl' not found
new_tbl <- new_tbl[keepers, ]
## Error in eval(expr, envir, enclos): object 'new_tbl' not found
new_tbl[["strong22"]] <- 1.001 - new_tbl[["z2.2"]]
## Error in eval(expr, envir, enclos): object 'new_tbl' not found
new_tbl[["strong23"]] <- 1.001 - new_tbl[["z2.3"]]
## Error in eval(expr, envir, enclos): object 'new_tbl' not found
s22_na <- new_tbl[["strong22"]] > 1
## Error in eval(expr, envir, enclos): object 'new_tbl' not found
new_tbl[s22_na, "strong22"] <- 1
## Error in new_tbl[s22_na, "strong22"] <- 1: object 'new_tbl' not found
s23_na <- new_tbl[["strong23"]] > 1
## Error in eval(expr, envir, enclos): object 'new_tbl' not found
new_tbl[s23_na, "strong23"] <- 1
## Error in new_tbl[s23_na, "strong23"] <- 1: object 'new_tbl' not found
new_tbl[["SNP"]] <- rownames(new_tbl)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'new_tbl' not found
new_tbl[["Chromosome"]] <- gsub(x = new_tbl[["SNP"]], pattern = "chr_(.*)_pos_.*", replacement = "\\1")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'gsub': object 'new_tbl' not found
new_tbl[["Position"]] <- gsub(x = new_tbl[["SNP"]], pattern = ".*_pos_(\\d+)_.*", replacement = "\\1")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'gsub': object 'new_tbl' not found
new_tbl <- new_tbl[, c("SNP", "Chromosome", "Position", "strong22", "strong23")]
## Error in eval(expr, envir, enclos): object 'new_tbl' not found
library(CMplot)
## Much appreciate for using CMplot.
## Full description, Bug report, Suggestion and the latest codes:
## https://github.com/YinLiLin/CMplot
simplify <- new_tbl
## Error in eval(expr, envir, enclos): object 'new_tbl' not found
simplify[["strong22"]] <- NULL
## Error in simplify[["strong22"]] <- NULL: object 'simplify' not found
CMplot(simplify, bin.size = 100000)
## Error in is.data.frame(x): object 'simplify' not found
CMplot(new_tbl, plot.type="m", multracks=TRUE, threshold = c(0.01, 0.05),
       threshold.lwd=c(1,1), threshold.col=c("black","grey"),
       amplify=TRUE, bin.size=10000,
       chr.den.col=c("darkgreen", "yellow", "red"),
       signal.col=c("red", "green", "blue"),
       signal.cex=1, file="jpg", memo="", dpi=300, file.output=TRUE, verbose=TRUE)
## Error in is.data.frame(x): object 'new_tbl' not found

SNP Density Circular Manhattan Rectangular Manhattan QQ

8.5 Try out MatrixEQTL

This tool looks a little opaque, but provides sample data with things that make sense to me and should be pretty easy to recapitulate in our data.

  1. covariates.txt: Columns are samples, rows are things from pData – the most likely ones of interest for our data would be zymodeme, sensitivity
  2. geneloc.txt: columns are ‘geneid’, ‘chr’, ‘left’, ‘right’. I guess I can assume left and right are start/stop; in which case this is trivially acquirable from fData.
  3. ge.txt: This appears to be a log(rpkm/cpm) table with rows as genes and columns as samples
  4. snpsloc.txt: columns are ‘snpid’, ‘chr’, ‘pos’
  5. snps.txt: columns are samples, rows are the ids from snsploc, values a 0,1,2. I assume 0 is identical and 1..12 are the various A->TGC T->AGC C->AGT G->ACT
## For this, let us use the 'new_snps' data structure.
## Caveat here: these need to be coerced to numbers.
my_covariates <- pData(new_snps)[, c("zymodemecategorical", "clinicalcategorical")]
for (col in colnames(my_covariates)) {
  my_covariates[[col]] <- as.numeric(as.factor(my_covariates[[col]]))
}
my_covariates <- t(my_covariates)

my_geneloc <- fData(lp_expt)[, c("gid", "chromosome", "start", "end")]
colnames(my_geneloc) <- c("geneid", "chr", "left", "right")

my_ge <- exprs(normalize_expt(lp_expt, transform = "log2", filter = TRUE, convert = "cpm"))
used_samples <- tolower(colnames(my_ge)) %in% colnames(exprs(new_snps))
my_ge <- my_ge[, used_samples]

my_snpsloc <- data.frame(rownames = rownames(exprs(new_snps)))
## Oh, caveat here: Because of the way I stored the data,
## I could have duplicate rows which presumably will make matrixEQTL sad
my_snpsloc[["chr"]] <- gsub(pattern = "^chr_(.+)_pos(.+)_ref_.*$", replacement = "\\1",
                            x = rownames(my_snpsloc))
my_snpsloc[["pos"]] <- gsub(pattern = "^chr_(.+)_pos(.+)_ref_.*$", replacement = "\\2",
                            x = rownames(my_snpsloc))
test <- duplicated(my_snpsloc)
## Each duplicated row would be another variant at that position;
## so in theory we would do a rle to number them I am guessing
## However, I do not have different variants so I think I can ignore this for the moment
## but will need to make my matrix either 0 or 1.
if (sum(test) > 0) {
  message("There are: ", sum(duplicated), " duplicated entries.")
  keep_idx <- ! test
  my_snpsloc <- my_snpsloc[keep_idx, ]
}

my_snps <- exprs(new_snps)
one_idx <- my_snps > 0
my_snps[one_idx] <- 1

## Ok, at this point I think I have all the pieces which this method wants...
## Oh, no I guess not; it actually wants the data as a set of filenames...
library(MatrixEQTL)
write.table(my_snps, "eqtl/snps.tsv", na = "NA", col.names = TRUE, row.names = TRUE, sep = "\t", quote = TRUE)
## readr::write_tsv(my_snps, "eqtl/snps.tsv", )
write.table(my_snpsloc, "eqtl/snpsloc.tsv", na = "NA", col.names = TRUE, row.names = TRUE, sep = "\t", quote = TRUE)
## readr::write_tsv(my_snpsloc, "eqtl/snpsloc.tsv")
write.table(as.data.frame(my_ge), "eqtl/ge.tsv", na = "NA", col.names = TRUE, row.names = TRUE, sep = "\t", quote = TRUE)
## readr::write_tsv(as.data.frame(my_ge), "eqtl/ge.tsv")
write.table(as.data.frame(my_geneloc), "eqtl/geneloc.tsv", na = "NA", col.names = TRUE, row.names = TRUE, sep = "\t", quote = TRUE)
## readr::write_tsv(as.data.frame(my_geneloc), "eqtl/geneloc.tsv")
write.table(as.data.frame(my_covariates), "eqtl/covariates.tsv", na = "NA", col.names = TRUE, row.names = TRUE, sep = "\t", quote = TRUE)
## readr::write_tsv(as.data.frame(my_covariates), "eqtl/covariates.tsv")

useModel = modelLINEAR # modelANOVA, modelLINEAR, or modelLINEAR_CROSS

# Genotype file name
SNP_file_name = "eqtl/snps.tsv"
snps_location_file_name = "eqtl/snpsloc.tsv"
expression_file_name = "eqtl/ge.tsv"
gene_location_file_name = "eqtl/geneloc.tsv"
covariates_file_name = "eqtl/covariates.tsv"
# Output file name
output_file_name_cis = tempfile()
output_file_name_tra = tempfile()
# Only associations significant at this level will be saved
pvOutputThreshold_cis = 0.1
pvOutputThreshold_tra = 0.1
# Error covariance matrix
# Set to numeric() for identity.
errorCovariance = numeric()
# errorCovariance = read.table("Sample_Data/errorCovariance.txt");
# Distance for local gene-SNP pairs
cisDist = 1e6
## Load genotype data
snps = SlicedData$new()
snps$fileDelimiter = "\t"      # the TAB character
snps$fileOmitCharacters = "NA" # denote missing values;
snps$fileSkipRows = 1          # one row of column labels
snps$fileSkipColumns = 1       # one column of row labels
snps$fileSliceSize = 2000      # read file in slices of 2,000 rows
snps$LoadFile(SNP_file_name)
## Load gene expression data
gene = SlicedData$new()
gene$fileDelimiter = "\t"      # the TAB character
gene$fileOmitCharacters = "NA" # denote missing values;
gene$fileSkipRows = 1          # one row of column labels
gene$fileSkipColumns = 1       # one column of row labels
gene$fileSliceSize = 2000      # read file in slices of 2,000 rows
gene$LoadFile(expression_file_name)
## Load covariates
cvrt = SlicedData$new()
cvrt$fileDelimiter = "\t"      # the TAB character
cvrt$fileOmitCharacters = "NA" # denote missing values;
cvrt$fileSkipRows = 1          # one row of column labels
cvrt$fileSkipColumns = 1       # one column of row labels
if(length(covariates_file_name) > 0) {
  cvrt$LoadFile(covariates_file_name)
}
## Run the analysis
snpspos = read.table(snps_location_file_name, header = TRUE, stringsAsFactors = FALSE)
genepos = read.table(gene_location_file_name, header = TRUE, stringsAsFactors = FALSE)

me = Matrix_eQTL_main(
    snps = snps,
    gene = gene,
    cvrt = cvrt,
    output_file_name = output_file_name_tra,
    pvOutputThreshold = pvOutputThreshold_tra,
    useModel = useModel,
    errorCovariance = errorCovariance,
    verbose = TRUE,
    output_file_name.cis = output_file_name_cis,
    pvOutputThreshold.cis = pvOutputThreshold_cis,
    snpspos = snpspos,
    genepos = genepos,
    cisDist = cisDist,
    pvalue.hist = "qqplot",
    min.pv.by.genesnp = FALSE,
    noFDRsaveMemory = FALSE);
if (!isTRUE(get0("skip_load"))) {
  pander::pander(sessionInfo())
  message(paste0("This is hpgltools commit: ", get_git_commit()))
  message(paste0("Saving to ", savefile))
  tmp <- sm(saveme(filename = savefile))
}
## If you wish to reproduce this exact build of hpgltools, invoke the following:
## > git clone http://github.com/abelew/hpgltools.git
## > git reset ee4d224084d5338793b0f4f7be352c1473426849
## This is hpgltools commit: Mon Jun 6 13:12:47 2022 -0400: ee4d224084d5338793b0f4f7be352c1473426849
## Saving to tmrc2_02sample_estimation_v202206.rda.xz
tmp <- loadme(filename = savefile)
LS0tCnRpdGxlOiAiVE1SQzIgQ29tcHJlaGVuc2l2ZSBEYXRhIEFuYWx5c2lzOiAyMDIyMDQiCmF1dGhvcjogImF0YiBhYmVsZXdAZ21haWwuY29tIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKIGh0bWxfZG9jdW1lbnQ6CiAgY29kZV9kb3dubG9hZDogdHJ1ZQogIGNvZGVfZm9sZGluZzogc2hvdwogIGZpZ19jYXB0aW9uOiB0cnVlCiAgZmlnX2hlaWdodDogNwogIGZpZ193aWR0aDogNwogIGhpZ2hsaWdodDogZGVmYXVsdAogIGtlZXBfbWQ6IGZhbHNlCiAgbW9kZTogc2VsZmNvbnRhaW5lZAogIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgdGhlbWU6IHJlYWRhYmxlCiAgdG9jOiB0cnVlCiAgdG9jX2Zsb2F0OgogICBjb2xsYXBzZWQ6IGZhbHNlCiAgIHNtb290aF9zY3JvbGw6IGZhbHNlCi0tLQoKPHN0eWxlPgogIGJvZHkgLm1haW4tY29udGFpbmVyIHsKICAgIG1heC13aWR0aDogMTYwMHB4OwogIH0KPC9zdHlsZT4KCmBgYHtyIG9wdGlvbnMsIGluY2x1ZGUgPSBGQUxTRX0KbGlicmFyeShocGdsdG9vbHMpCnR0IDwtIGRldnRvb2xzOjpsb2FkX2FsbCgifi9ocGdsdG9vbHMiKQprbml0cjo6b3B0c19rbml0JHNldChwcm9ncmVzcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICB3aWR0aCA9IDkwLAogICAgICAgICAgICAgICAgICAgICBlY2hvID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVycm9yID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy53aWR0aCA9IDgsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0ID0gOCwKICAgICAgICAgICAgICAgICAgICAgIGRwaSA9IDk2KQpvbGRfb3B0aW9ucyA8LSBvcHRpb25zKGRpZ2l0cyA9IDQsCiAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgIGtuaXRyLmR1cGxpY2F0ZS5sYWJlbCA9ICJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemUgPSAxMikpCnZlciA8LSAiMjAyMjA2IgpydW5kYXRlIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCBmb3JtYXQgPSAiJVklbSVkIikKCiMjIHRtcCA8LSB0cnkoc20obG9hZG1lKGZpbGVuYW1lID0gZ3N1YihwYXR0ZXJuID0gIlxcLlJtZCIsIHJlcGxhY2UgPSAiXFwucmRhXFwueHoiLCB4ID0gcHJldmlvdXNfZmlsZSkpKSkKcm1kX2ZpbGUgPC0gZ2x1ZTo6Z2x1ZSgidG1yYzJfMDJzYW1wbGVfZXN0aW1hdGlvbl92e3Zlcn0uUm1kIikKc2F2ZWZpbGUgPC0gZ3N1YihwYXR0ZXJuID0gIlxcLlJtZCIsIHJlcGxhY2UgPSAiXFwucmRhXFwueHoiLCB4ID0gcm1kX2ZpbGUpCgpsaWJyYXJ5KEhlYXRwbHVzKQpgYGAKCmBgYHtyIGN1cnJlbnRfc2FtcGxlc2hlZXR9CnNhbXBsZV9zaGVldCA8LSBnbHVlOjpnbHVlKCJzYW1wbGVfc2hlZXRzL3RtcmMyX3NhbXBsZXNfMjAyMjA2Lnhsc3giKQpgYGAKCiMgSW50cm9kdWN0aW9uCgpUaGlzIGlzIG1vc3RseSBqdXN0IGEgcnVuIG9mIHRoaXMgd29ya3NoZWV0IHRvIHJlYWNxdWFpbnQgbXlzZWxmIHdpdGggaXQuCgpUaGlzIGRvY3VtZW50IGlzIGludGVuZGVkIHRvIHByb3ZpZGUgYSBnZW5lcmFsIG92ZXJ2aWV3IG9mIHRoZSBUTVJDMiBzYW1wbGVzCndoaWNoIGhhdmUgdGh1cyBmYXIgYmVlbiBzZXF1ZW5jZWQuICBJbiBzb21lIGNhc2VzLCB0aGlzIGluY2x1ZGVzIG9ubHkgdGhvc2UKc2FtcGxlcyBzdGFydGluZyBpbiAyMDE5OyBpbiBvdGhlciBpbnN0YW5jZXMgSSBhbSBpbmNsdWRpbmcgb3VyIHByZXZpb3VzCigyMDE1LTIwMTYpIHNhbXBsZXMuCgpJbiBhbGwgY2FzZXMgdGhlIHByb2Nlc3NpbmcgcGVyZm9ybWVkIHdhczoKCjEuICBEZWZhdWx0IHRyaW1taW5nIHdhcyBwZXJmb3JtZWQuCjIuICBIaXNhdDIgd2FzIHVzZWQgdG8gbWFwIHRoZSByZW1haW5pbmcgcmVhZHMgYWdhaW5zdCB0aGUgTGVpc2htYW5pYQogICAgcGFuYW1lbnNpcyBnZW5vbWUgcmV2aXNpb24gMzYuCjMuICBUaGUgYWxpZ25tZW50cyBmcm9tIGhpc2F0MiB3ZXJlIHVzZWQgdG8gY291bnQgcmVhZHMvZ2VuZSBhZ2FpbnN0IHRoZQogICAgcmV2aXNpb24gMzYgYW5ub3RhdGlvbnMgd2l0aCBodHNlcS4KNC4gIFRoZXNlIGFsaWdubWVudHMgd2VyZSBhbHNvIHBhc3NlZCB0byB0aGUgcGlsZXVwIGZ1bmN0aW9uYWxpdHkgb2Ygc2FtdG9vbHMKICAgIGFuZCB0aGUgdmNmL2JjZiB1dGlsaXRpZXMgaW4gb3JkZXIgdG8gbWFrZSBhIG1hdHJpeCBvZiBhbGwgb2JzZXJ2ZWQKICAgIGRpZmZlcmVuY2VzIGJldHdlZW4gZWFjaCBzYW1wbGUgd2l0aCByZXNwZWN0IHRvIHRoZSByZWZlcmVuY2UuCgpUaGUgYW5hbHlzZXMgaW4gdGhpcyBkb2N1bWVudCB1c2UgdGhlIG1hdHJpY2VzIG9mIGNvdW50cy9nZW5lIGZyb20gIzMgYW5kCnZhcmlhbnRzL3Bvc2l0aW9uIGZyb20gIzQgaW4gb3JkZXIgdG8gcHJvdmlkZSBzb21lIGltYWdlcyBhbmQgbWV0cmljcyBkZXNjcmliaW5nCnRoZSBzYW1wbGVzIHdlIGhhdmUgc2VxdWVuY2VkIHNvIGZhci4KCiMgQW5ub3RhdGlvbnMKCkV2ZXJ5dGhpbmcgd2hpY2ggZm9sbG93cyBkZXBlbmRzIG9uIHRoZSBFeGlzdGluZyBUcmlUcnlwREIgYW5ub3RhdGlvbnMgcmV2aXNpb24KNDYsIGNpcmNhIDIwMTkuICBUaGUgZm9sbG93aW5nIGJsb2NrIGxvYWRzIGEgZGF0YWJhc2Ugb2YgdGhlc2UgYW5ub3RhdGlvbnMgYW5kCnR1cm5zIGl0IGludG8gYSBtYXRyaXggd2hlcmUgdGhlIHJvd3MgYXJlIGdlbmVzIGFuZCBjb2x1bW5zIGFyZSBhbGwgdGhlCmFubm90YXRpb24gdHlwZXMgcHJvdmlkZWQgYnkgVHJpVHJ5cERCLgoKVGhlIHNhbWUgZGF0YWJhc2Ugd2FzIHVzZWQgdG8gY3JlYXRlIGEgbWF0cml4IG9mIG9ydGhvbG9nb3VzIGdlbmVzIGJldHdlZW4KTC5wYW5hbWVuc2lzIGFuZCBhbGwgb2YgdGhlIG90aGVyIHNwZWNpZXMgaW4gdGhlIFRyaVRyeXBEQi4KCmBgYHtyIGFubm90fQp0dCA8LSBzbShsaWJyYXJ5KEV1UGF0aERCKSkKb3JnZGIgPC0gIm9yZy5McGFuYW1lbnNpcy5NSE9NQ09MODFMMTMudjQ2LmVnLmRiIgp0dCA8LSBzbShsaWJyYXJ5KG9yZ2RiLCBjaGFyYWN0ZXIub25seT1UUlVFKSkKcGFuX2RiIDwtIG9yZy5McGFuYW1lbnNpcy5NSE9NQ09MODFMMTMudjQ2LmVnLmRiCmFsbF9maWVsZHMgPC0gY29sdW1ucyhwYW5fZGIpCgphbGxfbHBfYW5ub3QgPC0gc20obG9hZF9vcmdkYl9hbm5vdGF0aW9ucygKICAgIHBhbl9kYiwKICAgIGtleXR5cGUgPSAiZ2lkIiwKICAgIGZpZWxkcyA9IGMoImFubm90X2dlbmVfZW50cmV6X2lkIiwgImFubm90X2dlbmVfbmFtZSIsCiAgICAgICAgICAgICAgICJhbm5vdF9zdHJhbmQiLCAiYW5ub3RfY2hyb21vc29tZSIsICJhbm5vdF9jZHNfbGVuZ3RoIiwKICAgICAgICAgICAgICAgImFubm90X2dlbmVfcHJvZHVjdCIpKSkkZ2VuZXMKCmxwX2dvIDwtIHNtKGxvYWRfb3JnZGJfZ28ocGFuX2RiKSkKbHBfbGVuZ3RocyA8LSBhbGxfbHBfYW5ub3RbLCBjKCJnaWQiLCAiYW5ub3RfY2RzX2xlbmd0aCIpXQpjb2xuYW1lcyhscF9sZW5ndGhzKSAgPC0gYygiSUQiLCAibGVuZ3RoIikKYWxsX2xwX2Fubm90W1siYW5ub3RfZ2VuZV9wcm9kdWN0Il1dIDwtIHRvbG93ZXIoYWxsX2xwX2Fubm90W1siYW5ub3RfZ2VuZV9wcm9kdWN0Il1dKQpvcnRob3MgPC0gc20oRXVQYXRoREI6OmV4dHJhY3RfZXVwYXRoX29ydGhvbG9ncyhkYiA9IHBhbl9kYikpCgpoaXNhdF9hbm5vdCA8LSBhbGxfbHBfYW5ub3QKYGBgCgojIExvYWQgYSBnZW5vbWUKCmBgYHtyIGdlbm9tZX0KbWV0YSA8LSBzbShFdVBhdGhEQjo6ZG93bmxvYWRfZXVwYXRoX21ldGFkYXRhKHdlYnNlcnZpY2U9InRyaXRyeXBkYiIpKQpscF9lbnRyeSA8LSBFdVBhdGhEQjo6Z2V0X2V1cGF0aF9lbnRyeShzcGVjaWVzPSJMZWlzaG1hbmlhIHBhbmFtZW5zaXMiLCBtZXRhZGF0YT1tZXRhKQpjb2xuYW1lcyhscF9lbnRyeSkKdGVzdGluZ19wYW5hbWVuc2lzIDwtICJCU0dlbm9tZS5MZWlzaG1hbmlhLnBhbmFtZW5zaXMuTUhPTUNPTDgxTDEzLnY1MyIKIyMgdGVzdGluZ19wYW5hbWVuc2lzIDwtIEV1UGF0aERCOjptYWtlX2V1cGF0aF9ic2dlbm9tZShlbnRyeT1scF9lbnRyeSwgZXVfdmVyc2lvbj0idjQ2IikKbGlicmFyeShhcy5jaGFyYWN0ZXIodGVzdGluZ19wYW5hbWVuc2lzKSwgY2hhcmFjdGVyLm9ubHk9VFJVRSkKZ2Vub21lIDwtIGdldDAoYXMuY2hhcmFjdGVyKHRlc3RpbmdfcGFuYW1lbnNpcykpCmBgYAoKIyBUT0RPOgoKUmVzZXF1ZW5jZSBzYW1wbGVzOiBUTVJDMjAwMDIsIFRNUkMyMDAwNiwgVE1SQzIwMDA0IChtYXliZSBUTVJDMjAwMDggYW5kIFRNUkMyMDAyOSkKCiMgR2VuZXJhdGUgRXhwcmVzc2lvbnNldHMgYW5kIFNhbXBsZSBFc3RpbWF0aW9uCgpUaGUgcHJvY2VzcyBvZiBzYW1wbGUgZXN0aW1hdGlvbiB0YWtlcyB0d28gcHJpbWFyeSBpbnB1dHM6CgoxLiAgVGhlIHNhbXBsZSBzaGVldCwgd2hpY2ggY29udGFpbnMgYWxsIHRoZSBtZXRhZGF0YSB3ZSBjdXJyZW50bHkgaGF2ZSBvbiBoYW5kLAogICAgaW5jbHVkaW5nIGZpbGVuYW1lcyBmb3IgdGhlIG91dHB1dHMgb2YgIzMgYW5kICM0IGFib3ZlLgoyLiAgVGhlIGdlbmUgYW5ub3RhdGlvbnMuCgpBbiBleHByZXNzaW9uc2V0IGlzIGEgZGF0YSBzdHJ1Y3R1cmUgdXNlZCBpbiBSIHRvIGV4YW1pbmUgUk5BU2VxIGRhdGEuICBJdAppcyBjb21wcmlzZWQgb2YgYW5ub3RhdGlvbnMsIG1ldGFkYXRhLCBhbmQgZXhwcmVzc2lvbiBkYXRhLiAgSW4gdGhlIGNhc2Ugb2Ygb3VyCnByb2Nlc3NpbmcgcGlwZWxpbmUsIHRoZSBsb2NhdGlvbiBvZiB0aGUgZXhwcmVzc2lvbiBkYXRhIGlzIHByb3ZpZGVkIGJ5IHRoZQpmaWxlbmFtZXMgaW4gdGhlIG1ldGFkYXRhLgoKVGhlIGZpcnN0IGxpbmVzIG9mIHRoZSBmb2xsb3dpbmcgYmxvY2sgY3JlYXRlIHRoZSBFeHByZXNzaW9uc2V0LiAgQWxsIG9mIHRoZQpmb2xsb3dpbmcgbGluZXMgcGVyZm9ybSB2YXJpb3VzIG5vcm1hbGl6YXRpb25zIGFuZCBnZW5lcmF0ZSBwbG90cyBmcm9tIGl0LgoKIyMgTm90ZXMKClRoZSBmb2xsb3dpbmcgc2FtcGxlcyBhcmUgbXVjaCBsb3dlciBjb3ZlcmFnZToKCiogVE1SQzIwMDAyCiogVE1SQzIwMDA2CiogVE1SQzIwMDA3CiogVE1SQzIwMDA4CgoyMDIxMDYxMDogSSBtYWRlIHNvbWUgbWFudWFsIGNoYW5nZXMgdG8gdGhlIHNhbXBsZSBzaGVldCB3aGljaCBJCmRvd25sb2FkZWQsIGZpbGxpbmcgaW4gc29tZSB6eW1vZGVtZSB3aXRoICd1bmtub3duJwoKIyMgVE9ETzoKCjEuICBEbyB0aGUgbXVsdGktZ2VuZSBmYW1pbHkgcmVtb3ZhbCByaWdodCBoZXJlIGluc3RlYWQgb2Ygd2F5IGRvd24gYXQgdGhlIGJvdHRvbQoyLiAgQWRkIHp5bW9kZW1lIHNucHMgdG8gdGhlIGFubm90YXRpb24gbGF0ZXIuCjMuICBTdGFydCBwaHlsb2dlbmV0aWMgYW5hbHlzaXMgb2YgdmFyaWFudCB0YWJsZS4KCmBgYHtyIG5ld19zYW1wbGVzX2hpc2F0fQpzYW5pdGl6ZV9jb2x1bW5zIDwtIGMoInBhc3NhZ2VudW1iZXIiLCAiY2xpbmljYWxyZXNwb25zZSIsICJjbGluaWNhbGNhdGVnb3JpY2FsIiwKICAgICAgICAgICAgICAgICAgICAgICJ6eW1vZGVtZWNhdGVnb3JpY2FsIiwgInp5bW9kZW1lY2F0ZWdvcmljYWwiKQpscF9leHB0IDwtIGNyZWF0ZV9leHB0KHNhbXBsZV9zaGVldCwKICAgICAgICAgICAgICAgICAgICAgICBnZW5lX2luZm8gPSBoaXNhdF9hbm5vdCwKICAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uID0gb3JnZGIsCiAgICAgICAgICAgICAgICAgICAgICAgaWRfY29sdW1uID0gImhwZ2xpZGVudGlmaWVyIiwKICAgICAgICAgICAgICAgICAgICAgICBmaWxlX2NvbHVtbiA9ICJscGFuYW1lbnNpc3YzNmhpc2F0ZmlsZSIpICU+JQogIHNldF9leHB0X2NvbmRpdGlvbnMoZmFjdCA9ICJ6eW1vZGVtZWNhdGVnb3JpY2FsIikgJT4lCiAgc3Vic2V0X2V4cHQobm9uemVybyA9IDg1NTApICU+JQogIHN1YnNldF9leHB0KGNvdmVyYWdlID0gNTAwMDAwMCkgJT4lCiAgc2VtYW50aWNfZXhwdF9maWx0ZXIoc2VtYW50aWMgPSBjKCJhbWFzdGluIiwgImdwNjMiLCAibGVpc2htYW5vbHlzaW4iKSwKICAgICAgICAgICAgICAgICAgICAgICBzZW1hbnRpY19jb2x1bW4gPSAiYW5ub3RfZ2VuZV9wcm9kdWN0IikgJT4lCiAgc2FuaXRpemVfZXhwdF9tZXRhZGF0YShjb2x1bW5zID0gc2FuaXRpemVfY29sdW1ucykgJT4lCiAgc2V0X2V4cHRfZmFjdG9ycyhjb2x1bW5zID0gc2FuaXRpemVfY29sdW1ucywgY2xhc3MgPSAiZmFjdG9yIikKCmxpYnNpemVzIDwtIHBsb3RfbGlic2l6ZShscF9leHB0KQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvbHBfZXhwdF9saWJzaXplcy5wbmciLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSA5KQpsaWJzaXplcyRwbG90CmNsb3NlZCA8LSBkZXYub2ZmKCkKbGlic2l6ZXMkcGxvdAoKIyMgSSB0aGluayBzYW1wbGVzIDcsMTAgc2hvdWxkIGJlIHJlbW92ZWQgYXQgbWluaW11bSwgcHJvYmFibHkgYWxzbyA5LDExCm5vbnplcm8gPC0gcGxvdF9ub256ZXJvKGxwX2V4cHQpCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9scF9ub256ZXJvLnBuZyIsIHdpZHRoPTksIGhlaWdodD05KQpub256ZXJvJHBsb3QKY2xvc2VkIDwtIGRldi5vZmYoKQoKbHBfYm94IDwtIHBsb3RfYm94cGxvdChscF9leHB0KQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvbHBfZXhwdF9ib3hwbG90LnBuZyIsIHdpZHRoID0gMTIsIGhlaWdodCA9IDkpCmxwX2JveApjbG9zZWQgPC0gZGV2Lm9mZigpCmxwX2JveAoKZmlsdGVyX3Bsb3QgPC0gcGxvdF9saWJzaXplX3ByZXBvc3QobHBfZXhwdCkKZmlsdGVyX3Bsb3QkbG93Z2VuZV9wbG90CmZpbHRlcl9wbG90JGNvdW50X3Bsb3QKCnRhYmxlKHBEYXRhKGxwX2V4cHQpW1sienltb2RlbWVjYXRlZ29yaWNhbCJdXSkKdGFibGUocERhdGEobHBfZXhwdClbWyJjbGluaWNhbHJlc3BvbnNlIl1dKQpgYGAKCiMjIERpc3RyaWJ1dGlvbiBWaXN1YWxpemF0aW9uCgpOYWppYidzIGZhdm9yaXRlIHBsb3RzIGFyZSBvZiBjb3Vyc2UgdGhlIFBDQS9UTlNFLiAgVGhlc2UgYXJlIG5pY2UgdG8gbG9vayBhdCBpbgpvcmRlciB0byBnZXQgYSBzZW5zZSBvZiB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHNhbXBsZXMuICBUaGV5IGFsc28gcHJvdmlkZSBhCmdvb2Qgb3Bwb3J0dW5pdHkgdG8gc2VlIHdoYXQgaGFwcGVucyB3aGVuIG9uZSBhcHBsaWVzIGRpZmZlcmVudCBub3JtYWxpemF0aW9ucywKc3Vycm9nYXRlIGFuYWx5c2VzLCBmaWx0ZXJzLCBldGMuICBJbiBhZGRpdGlvbiwgb25lIG1heSBzZXQgZGlmZmVyZW50CmV4cGVyaW1lbnRhbCBmYWN0b3JzIGFzIHRoZSBwcmltYXJ5ICdjb25kaXRpb24nICh1c3VhbGx5IHRoZSBjb2xvciBvZiBwbG90cykgYW5kCnN1cnJvZ2F0ZSAnYmF0Y2hlcycuCgojIyBCeSBTdXNjZXB0aWxpYml0eQoKQ29sdW1uICdRJyBpbiB0aGUgc2FtcGxlIHNoZWV0LCBtYWtlIGEgY2F0ZWdvcmljYWwgdmVyc2lvbiBvZiBpdCB3aXRoIHRoZXNlIHBhcmFtZXRlcnM6CgoqIDAgPD0geCA8PSAzNSBpcyByZXNpc3RhbnQKKiAzNiA8PSB4IDw9IDQ4IGlzIGFtYmlndW91cwoqIDQ5IDw9IHggaXMgc2Vuc2l0aXZlCgpgYGB7ciBzdXNjZXB0aWJpbGl0eX0Kc3RhcnRpbmcgPC0gYXMubnVtZXJpYyhwRGF0YShscF9leHB0KVtbInN1c2NlcHRpYmlsaXR5aW5mZWN0aW9ucmVkdWN0aW9uMzJ1Z21sc2J2aGlzdG9yaWNhbGRhdGEiXV0pCnN1c19jYXRlZ29yaWNhbCA8LSBzdGFydGluZwpuYV9pZHggPC0gaXMubmEoc3RhcnRpbmcpCnN1c19jYXRlZ29yaWNhbFtuYV9pZHhdIDwtICJ1bmtub3duIgoKcmVzaXN0X2lkeCA8LSBzdGFydGluZyA8PSAwLjM1CnN1c19jYXRlZ29yaWNhbFtyZXNpc3RfaWR4XSA8LSAicmVzaXN0YW50IgppbmRldGVybWluYW50X2lkeCA8LSBzdGFydGluZyA+PSAwLjM2ICYgc3RhcnRpbmcgPD0gMC40OApzdXNfY2F0ZWdvcmljYWxbaW5kZXRlcm1pbmFudF9pZHhdIDwtICJhbWJpZ3VvdXMiCnN1c2NlcHRpYmxlX2lkeCA8LSBzdGFydGluZyA+PSAwLjQ5CnN1c19jYXRlZ29yaWNhbFtzdXNjZXB0aWJsZV9pZHhdIDwtICJzZW5zaXRpdmUiCgpwRGF0YShscF9leHB0KVtbInN1c19jYXRlZ29yeSJdXSA8LSBzdXNfY2F0ZWdvcmljYWwKdGFibGUoc3VzX2NhdGVnb3JpY2FsKQpgYGAKCmBgYHtyIHByZV9xdWVzdGlvbnN9CmNsaW5pY2FsX2NvbG9ycyA8LSBsaXN0KAogICAgInoxLjAiID0gIiMzMzMzMzMiLAogICAgInoyLjAiID0gIiM1NTU1NTUiLAogICAgInozLjAiID0gIiM3Nzc3NzciLAogICAgInoyLjEiID0gIiM4NzQ0MDAiLAogICAgInoyLjIiID0gIiMwMDAwY2MiLAogICAgInoyLjMiID0gIiNjYzAwMDAiLAogICAgInoyLjQiID0gIiNkZjcwMDAiLAogICAgInVua25vd24iID0gIiNjYmNiY2IiLAogICAgIm51bGwiID0gIiMwMDAwMDAiKQpjbGluaWNhbF9zYW1wbGVzIDwtIGxwX2V4cHQgJT4lCiAgc2V0X2V4cHRfYmF0Y2hlcyhmYWN0ID0gc3VzX2NhdGVnb3JpY2FsKSAlPiUKICBzZXRfZXhwdF9jb2xvcnMoY2xpbmljYWxfY29sb3JzKQp0YWJsZShwRGF0YShjbGluaWNhbF9zYW1wbGVzKVtbImNvbmRpdGlvbiJdXSkKCmNsaW5pY2FsX25vcm0gPC0gbm9ybWFsaXplX2V4cHQoY2xpbmljYWxfc2FtcGxlcywgbm9ybSA9ICJxdWFudCIsIHRyYW5zZm9ybSA9ICJsb2cyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJ0ID0gImNwbSIsIGZpbHRlciA9IFRSVUUpCnp5bW9fcGNhIDwtIHBsb3RfcGNhKGNsaW5pY2FsX25vcm0sIHBsb3RfdGl0bGUgPSAiUENBIG9mIHBhcmFzaXRlIGV4cHJlc3Npb24gdmFsdWVzIiwKICAgICAgICAgICAgICAgICAgICAgcGxvdF9sYWJlbHMgPSBGQUxTRSkKZ2dwbHQoenltb19wY2EkcGxvdCkKZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL3p5bW9fcGNhX3N1c19zaGFwZS5wbmciKQp6eW1vX3BjYSRwbG90CmNsb3NlZCA8LSBkZXYub2ZmKCkKenltb19wY2EkcGxvdAoKb25seV90d29fdHlwZXMgPC0gc3Vic2V0X2V4cHQoY2xpbmljYWxfc2FtcGxlcywgc3Vic2V0ID0gImNvbmRpdGlvbj09J3oyLjMnfGNvbmRpdGlvbj09J3oyLjInIikKb25seV90d29fbm9ybSA8LSBzbShub3JtYWxpemVfZXhwdChvbmx5X3R3b190eXBlcywgbm9ybSA9ICJxdWFudCIsIHRyYW5zZm9ybSA9ICJsb2cyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJ0ID0gImNwbSIsIGJhdGNoID0gRkFMU0UsIGZpbHRlciA9IFRSVUUpKQpvbmx5dHdvX3BjYSA8LSBwbG90X3BjYShvbmx5X3R3b19ub3JtLCBwbG90X3RpdGxlID0gIlBDQSBvZiB6Mi4yIGFuZCB6Mi4zIHBhcmFzaXRlIGV4cHJlc3Npb24gdmFsdWVzIiwKICAgICAgICAgICAgICAgICAgICAgcGxvdF9sYWJlbHMgPSBGQUxTRSkKZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL3p5bW9fejIuMl96Mi4zX3BjYV9zdXNfc2hhcGUucGRmIikKb25seXR3b19wY2EkcGxvdApjbG9zZWQgPC0gZGV2Lm9mZigpCm9ubHl0d29fcGNhJHBsb3QKCnp5bW9fM2RwY2EgPC0gcGxvdF8zZF9wY2Eoenltb19wY2EpCnp5bW9fM2RwY2EkcGxvdAoKY2xpbmljYWxfbiA8LSBzbShub3JtYWxpemVfZXhwdChjbGluaWNhbF9zYW1wbGVzLCB0cmFuc2Zvcm0gPSAibG9nMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udmVydCA9ICJjcG0iLCBiYXRjaCA9IEZBTFNFLCBmaWx0ZXIgPSBUUlVFKSkKenltb190c25lIDwtIHBsb3RfdHNuZShjbGluaWNhbF9uLCBwbG90X3RpdGxlID0gIlRTTkUgb2YgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiKQp6eW1vX3RzbmUkcGxvdAoKY2xpbmljYWxfbmIgPC0gbm9ybWFsaXplX2V4cHQoY2xpbmljYWxfc2FtcGxlcywgY29udmVydCA9ICJjcG0iLCB0cmFuc2Zvcm0gPSAibG9nMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLCBiYXRjaCA9ICJzdmFzZXEiKQpjbGluaWNhbF9uYl9wY2EgPC0gcGxvdF9wY2EoY2xpbmljYWxfbmIsIHBsb3RfdGl0bGUgPSAiUENBIG9mIHBhcmFzaXRlIGV4cHJlc3Npb24gdmFsdWVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfbGFiZWxzID0gRkFMU0UpCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9jbGluaWNhbF9uYl9wY2Ffc3VzX3NoYXBlLnBuZyIpCmNsaW5pY2FsX25iX3BjYSRwbG90CmNsb3NlZCA8LSBkZXYub2ZmKCkKY2xpbmljYWxfbmJfcGNhJHBsb3QKCmNsaW5pY2FsX25iX3RzbmUgPC0gcGxvdF90c25lKGNsaW5pY2FsX25iLCBwbG90X3RpdGxlID0gIlRTTkUgb2YgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiKQpjbGluaWNhbF9uYl90c25lJHBsb3QKCmNvcmhlYXQgPC0gcGxvdF9jb3JoZWF0KGNsaW5pY2FsX25vcm0sIHBsb3RfdGl0bGUgPSAiQ29ycmVsYXRpb24gaGVhdG1hcCBvZiBwYXJhc2l0ZQogICAgICAgICAgICAgICAgIGV4cHJlc3Npb24gdmFsdWVzCiIpCmNvcmhlYXQkcGxvdAoKcGxvdF9zbShjbGluaWNhbF9ub3JtKSRwbG90CmBgYAoKIyMgQnkgQ3VyZS9GYWlsIHN0YXR1cwoKYGBge3IgY2Zfc3RhdHVzfQpjZl9jb2xvcnMgPC0gbGlzdCgKICAgICJjdXJlIiA9ICIjMDA2ZjAwIiwKICAgICJmYWlsIiA9ICIjOWRmZmEwIiwKICAgICJ1bmtub3duIiA9ICIjY2JjYmNiIiwKICAgICJub3RhcHBsaWNhYmxlIiA9ICIjMDAwMDAwIikKY2ZfZXhwdCA8LSBzZXRfZXhwdF9jb25kaXRpb25zKGxwX2V4cHQsIGZhY3QgPSAiY2xpbmljYWxjYXRlZ29yaWNhbCIpICU+JQogIHNldF9leHB0X2JhdGNoZXMoZmFjdCA9IHN1c19jYXRlZ29yaWNhbCkgJT4lCiAgc2V0X2V4cHRfY29sb3JzKGNmX2NvbG9ycykKdGFibGUocERhdGEoY2ZfZXhwdClbWyJjb25kaXRpb24iXV0pCgpjZl9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KGNmX2V4cHQsIGNvbnZlcnQgPSAiY3BtIiwgdHJhbnNmb3JtID0gImxvZzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm0gPSAicXVhbnQiLCBmaWx0ZXIgPSBUUlVFKQpzdGFydF9jZiA8LSBwbG90X3BjYShjZl9ub3JtLCBwbG90X3RpdGxlID0gIlBDQSBvZiBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIsCiAgICAgICAgICAgICAgICAgICAgIHBsb3RfbGFiZWxzID0gRkFMU0UpCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9jZl9zdXNfc2hhcGUucG5nIikKc3RhcnRfY2YkcGxvdApjbG9zZWQgPC0gZGV2Lm9mZigpCnN0YXJ0X2NmJHBsb3QKCmNmX25iIDwtIG5vcm1hbGl6ZV9leHB0KGNmX2V4cHQsIGNvbnZlcnQgPSAiY3BtIiwgdHJhbnNmb3JtID0gImxvZzIiLAogICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLCBiYXRjaCA9ICJzdmFzZXEiKQpjZl9uYl9wY2EgPC0gcGxvdF9wY2EoY2ZfbmIsIHBsb3RfdGl0bGUgPSAiUENBIG9mIHBhcmFzaXRlIGV4cHJlc3Npb24gdmFsdWVzIiwKICAgICAgICAgICAgICAgICAgICAgIHBsb3RfbGFiZWxzID0gRkFMU0UpCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9jZl9zdXNfc2hhcmVfbmIucG5nIikKY2ZfbmJfcGNhJHBsb3QKY2xvc2VkIDwtIGRldi5vZmYoKQpjZl9uYl9wY2EkcGxvdAoKY2Zfbm9ybSA8LSBub3JtYWxpemVfZXhwdChjZl9leHB0LCB0cmFuc2Zvcm0gPSAibG9nMiIsIGNvbnZlcnQgPSAiY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLCBub3JtID0gInF1YW50IikKdGVzdCA8LSBwY2FfaW5mb3JtYXRpb24oY2Zfbm9ybSwKICAgICAgICAgICAgICAgICAgICAgICAgZXhwdF9mYWN0b3JzID0gYygiY2xpbmljYWxjYXRlZ29yaWNhbCIsICJ6eW1vZGVtZWNhdGVnb3JpY2FsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aG9nZW5zdHJhaW4iLCAicGFzc2FnZW51bWJlciIpLAogICAgICAgICAgICAgICAgICAgICAgICBudW1fY29tcG9uZW50cyA9IDYsIHBsb3RfcGNhcyA9IFRSVUUpCnRlc3QkYW5vdmFfcAp0ZXN0JGNvcl9oZWF0bWFwCmBgYAoKYGBge3Igc3VzY2VwdGliaWxpdHlfcGNhfQpzdXNfY29sb3JzIDwtIGxpc3QoCiAgICAicmVzaXN0YW50IiA9ICIjODU2M2E3IiwKICAgICJzZW5zaXRpdmUiID0gIiM4ZDAwMDAiLAogICAgImFtYmlndW91cyIgPSAiI2NiY2JjYiIsCiAgICAidW5rbm93biIgPSAiIzAwMDAwMCIpCnN1c19leHB0IDwtIHNldF9leHB0X2NvbmRpdGlvbnMobHBfZXhwdCwgZmFjdCA9ICJzdXNfY2F0ZWdvcnkiKSAlPiUKICBzZXRfZXhwdF9iYXRjaGVzKGZhY3QgPSAienltb2RlbWVjYXRlZ29yaWNhbCIpICU+JQogIHNldF9leHB0X2NvbG9ycyhjb2xvcnMgPSBzdXNfY29sb3JzKSAlPiUKICBzdWJzZXRfZXhwdChzdWJzZXQgPSAiYmF0Y2ghPSd6MjQnIikgJT4lCiAgc3Vic2V0X2V4cHQoc3Vic2V0ID0gImJhdGNoIT0nejIxJyIpCgpzdXNfbm9ybSA8LSBub3JtYWxpemVfZXhwdChzdXNfZXhwdCwgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm0gPSAicXVhbnQiLCBmaWx0ZXIgPSBUUlVFKQpzdXNfcGNhIDwtIHBsb3RfcGNhKHN1c19ub3JtLCBwbG90X3RpdGxlID0gIlBDQSBvZiBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIsCiAgICAgICAgICAgICAgICAgICAgcGxvdF9sYWJlbHMgPSBGQUxTRSkKZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL3N1c19ub3JtX3BjYS5wbmciKQpzdXNfcGNhW1sicGxvdCJdXQpjbG9zZWQgPC0gZGV2Lm9mZigpCnN1c19wY2FbWyJwbG90Il1dCgpzdXNfbmIgPC0gbm9ybWFsaXplX2V4cHQoc3VzX2V4cHQsIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgYmF0Y2ggPSAic3Zhc2VxIiwgZmlsdGVyID0gVFJVRSkKc3VzX25iX3BjYSA8LSBwbG90X3BjYShzdXNfbmIsIHBsb3RfdGl0bGUgPSAiUENBIG9mIHBhcmFzaXRlIGV4cHJlc3Npb24gdmFsdWVzIiwKICAgICAgICAgICAgICAgICAgICAgICBwbG90X2xhYmVscyA9IEZBTFNFKQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvc3VzX25iX3BjYS5wbmciKQpzdXNfbmJfcGNhW1sicGxvdCJdXQpjbG9zZWQgPC0gZGV2Lm9mZigpCnN1c19uYl9wY2FbWyJwbG90Il1dCmBgYAoKIyBaeW1vZGVtZSBhbmFseXNlcwoKVGhlIGZvbGxvd2luZyBzZWN0aW9ucyBwZXJmb3JtIGEgc2VyaWVzIG9mIGFuYWx5c2VzIHdoaWNoIHNlZWsgdG8gZWx1Y2lkYXRlCmRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIHp5bW9kZW1lcyAyLjIgYW5kIDIuMyBlaXRoZXIgdGhyb3VnaCBkaWZmZXJlbnRpYWwKZXhwcmVzc2lvbiBvciB2YXJpYW50IHByb2ZpbGVzLgoKIyMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24KCiMjIyBXaXRoIHJlc3BlY3QgdG8genltb2RlbWUgYXR0cmlidXRpb24KClRPRE86IERvIHRoaXMgd2l0aCBhbmQgd2l0aG91dCBzdmEgYW5kIGNvbXBhcmUgdGhlIHJlc3VsdHMuCgpgYGB7ciB6eW1vX2RlLCBmaWcuc2hvdyA9ICJoaWRlIn0KenlfZXhwdCA8LSBzdWJzZXRfZXhwdChscF9leHB0LCBzdWJzZXQgPSAiY29uZGl0aW9uPT0nejIuMid8Y29uZGl0aW9uPT0nejIuMyciKQp6eV9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KHp5X2V4cHQsIGZpbHRlciA9IFRSVUUsIGNvbnZlcnQgPSAiY3BtIiwgbm9ybSA9ICJxdWFudCIpCgp6eV9kZV9ub2JhdGNoIDwtIGFsbF9wYWlyd2lzZSh6eV9leHB0LCBmaWx0ZXIgPSBUUlVFLCBtb2RlbF9iYXRjaCA9IEZBTFNFKQp6eV90YWJsZV9ub2JhdGNoIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogICAgenlfZGVfbm9iYXRjaCwgZXhjZWwgPSBnbHVlOjpnbHVlKCJleGNlbC96eV90YWJsZXNfbm9iYXRjaC12e3Zlcn0ueGxzeCIpLAogICAgZ210ID0gZ2x1ZTo6Z2x1ZSgiZ210L3p5bW9kZW1lX25vYmF0Y2gtdnt2ZXJ9LmdtdCIpKQp6eV9zaWdfbm9iYXRjaCA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogICAgenlfdGFibGVfbm9iYXRjaCwKICAgIGV4Y2VsID0gZ2x1ZTo6Z2x1ZSgiZXhjZWwvenlfc2lnX25vYmF0Y2gtdnt2ZXJ9Lnhsc3giKSkKCnp5X2RlX3N2YSA8LSBhbGxfcGFpcndpc2UoenlfZXhwdCwgZmlsdGVyID0gVFJVRSwgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIikKenlfdGFibGVfc3ZhIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogICAgenlfZGVfc3ZhLCBleGNlbCA9IGdsdWU6OmdsdWUoImV4Y2VsL3p5X3RhYmxlc19zdmEtdnt2ZXJ9Lnhsc3giKSwKICAgIGdtdCA9IGdsdWU6OmdsdWUoImdtdC96eW1vZGVtZV9zdmEtdnt2ZXJ9LmdtdCIpKQp6eV9zaWdfc3ZhIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgICB6eV90YWJsZV9zdmEsCiAgICBleGNlbCA9IGdsdWU6OmdsdWUoImV4Y2VsL3p5X3NpZ19zdmEtdnt2ZXJ9Lnhsc3giKSkKYGBgCgojIyMgSW1hZ2VzIG9mIHp5bW9kZW1lIERFCgpgYGB7ciB6eW1vZF9kZV9waWN0dXJlc30KZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL3p5bW9fbWEucG5nIikKenlfdGFibGVfc3ZhW1sicGxvdHMiXV1bWyJ6MjNfdnNfejIyIl1dW1siZGVzZXFfbWFfcGxvdHMiXV1bWyJwbG90Il1dCmNsb3NlZCA8LSBkZXYub2ZmKCkKenlfdGFibGVfc3ZhW1sicGxvdHMiXV1bWyJ6MjNfdnNfejIyIl1dW1siZGVzZXFfbWFfcGxvdHMiXV1bWyJwbG90Il1dCmBgYAoKIyMgV2l0aCByZXNwZWN0IHRvIGN1cmUvZmFpbHVyZQoKSW4gY29udHJhc3QsIHdlIGNhbiBzZWFyY2ggZm9yIGdlbmVzIHdoaWNoIGFyZSBkaWZmZXJlbnRpYWxseQpleHByZXNzZWQgd2l0aCByZXNwZWN0IHRvIGN1cmUvZmFpbHVyZSBzdGF0dXMuCgpgYGB7ciBjdXJlZmFpbF9kZSwgZmlnLnNob3cgPSAiaGlkZSJ9CmNmX2RlIDwtIGFsbF9wYWlyd2lzZShjZl9leHB0LCBmaWx0ZXIgPSBUUlVFLCBtb2RlbF9iYXRjaCA9ICJzdmFzZXEiKQpjZl90YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcyhjZl9kZSwgZXhjZWwgPSBnbHVlOjpnbHVlKCJleGNlbC9jZl90YWJsZXMtdnt2ZXJ9Lnhsc3giKSkKY2Zfc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoY2ZfdGFibGUsIGV4Y2VsID0gZ2x1ZTo6Z2x1ZSgiZXhjZWwvY2Zfc2lnLXZ7dmVyfS54bHN4IikpCgpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvY2ZfbWEucG5nIikKY2ZfdGFibGVbWyJwbG90cyJdXVtbImZhaWxfdnNfY3VyZSJdXVtbImRlc2VxX21hX3Bsb3RzIl1dW1sicGxvdCJdXQpjbG9zZWQgPC0gZGV2Lm9mZigpCmNmX3RhYmxlW1sicGxvdHMiXV1bWyJmYWlsX3ZzX2N1cmUiXV1bWyJkZXNlcV9tYV9wbG90cyJdXVtbInBsb3QiXV0KYGBgCgojIyBXaXRoIHJlc3BlY3QgdG8gc3VzY2VwdGliaWxpdHkKCkZpbmFsbHksIHdlIGNhbiB1c2Ugb3VyIGNhdGVnb3J5IG9mIHN1c2NlcHRpYmlsaXR5IGFuZCBsb29rIGZvciBnZW5lcwp3aGljaCBjaGFuZ2UgZnJvbSBzZW5zaXRpdmUgdG8gcmVzaXN0YW50LiAgS2VlcCBpbiBtaW5kLCB0aG91Z2gsIHRoYXQKZm9yIHRoZSBtb21lbnQgd2UgaGF2ZSBhIGxvdCBvZiBhbWJpZ3VvdXMgYW5kIHVua25vd24gc3RyYWlucy4KCmBgYHtyIGN1cmVmYWlsX2RlMDIsIGZpZy5zaG93ID0gImhpZGUifQpzdXNfZGVfc3ZhIDwtIGFsbF9wYWlyd2lzZShzdXNfZXhwdCwgZmlsdGVyID0gVFJVRSwgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIikKc3VzX3RhYmxlX3N2YSA8LSBjb21iaW5lX2RlX3RhYmxlcyhzdXNfZGVfc3ZhLCBleGNlbCA9IGdsdWU6OmdsdWUoImV4Y2VsL3N1c190YWJsZXNfc3ZhLXZ7dmVyfS54bHN4IikpCnN1c19zaWdfc3ZhIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoc3VzX3RhYmxlX3N2YSwgZXhjZWwgPSBnbHVlOjpnbHVlKCJleGNlbC9zdXNfc2lnX3N2YS12e3Zlcn0ueGxzeCIpKQoKc3VzX2RlX25vYmF0Y2ggPC0gYWxsX3BhaXJ3aXNlKHN1c19leHB0LCBmaWx0ZXIgPSBUUlVFLCBtb2RlbF9iYXRjaCA9IEZBTFNFKQpzdXNfdGFibGVfbm9iYXRjaCA8LSBjb21iaW5lX2RlX3RhYmxlcyhzdXNfZGVfbm9iYXRjaCwgZXhjZWwgPSBnbHVlOjpnbHVlKCJleGNlbC9zdXNfdGFibGVzX25vYmF0Y2gtdnt2ZXJ9Lnhsc3giKSkKc3VzX3NpZ19ub2JhdGNoIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoc3VzX3RhYmxlX25vYmF0Y2gsIGV4Y2VsID0gZ2x1ZTo6Z2x1ZSgiZXhjZWwvc3VzX3NpZ19ub2JhdGNoLXZ7dmVyfS54bHN4IikpCmBgYAoKYGBge3Igenltb2RfZGVfcGljdHVyZXMwMX0Ka25pdHI6OmthYmxlKGhlYWQoc3VzX3NpZ19zdmEkZGVzZXEkdXBzJHNlbnNpdGl2ZV92c19yZXNpc3RhbnQsIG4gPSAyMCkpCgprbml0cjo6a2FibGUoaGVhZChzdXNfc2lnX3N2YSRkZXNlcSRkb3ducyRzZW5zaXRpdmVfdnNfcmVzaXN0YW50LCBuID0gMjApKQoKc3VzX21hIDwtIHN1c190YWJsZV9zdmFbWyJwbG90cyJdXVtbInNlbnNpdGl2ZV92c19yZXNpc3RhbnQiXV1bWyJkZXNlcV9tYV9wbG90cyJdXVtbInBsb3QiXV0KZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL3N1c19tYV9zdmEucG5nIikKc3VzX21hCmNsb3NlZCA8LSBkZXYub2ZmKCkKc3VzX21hCgojIyB0ZXN0IDwtIGdncGx0KHN1c19tYSkKYGBgCgojIyBPbnRvbG9neSBzZWFyY2hlcwoKTm93IGxldCB1cyBsb29rIGZvciBvbnRvbG9neSBjYXRlZ29yaWVzIHdoaWNoIGFyZSBpbmNyZWFzZWQgaW4gdGhlIDIuMwpzYW1wbGVzIGZvbGxvd2VkIGJ5IHRoZSAyLjIgc2FtcGxlcy4KCmBgYHtyIGdvLCBzaWcuc2hvdyA9ICJoaWRlIn0KIyMgR2VuZSBjYXRlZ29yaWVzIG1vcmUgcmVwcmVzZW50ZWQgaW4gdGhlIDIuMyBncm91cC4KenlfZ29fdXAgPC0gc2ltcGxlX2dvc2VxKHNpZ19nZW5lcyA9IHp5X3NpZ19zdmFbWyJkZXNlcSJdXVtbInVwcyJdXVtbMV1dLAogICAgICAgICAgICAgICAgICAgICAgICAgZ29fZGIgPSBscF9nbywgbGVuZ3RoX2RiID0gbHBfbGVuZ3RocykKCiMjIEdlbmUgY2F0ZWdvcmllcyBtb3JlIHJlcHJlc2VudGVkIGluIHRoZSAyLjIgZ3JvdXAuCnp5X2dvX2Rvd24gPC0gc2ltcGxlX2dvc2VxKHNpZ19nZW5lcyA9IHp5X3NpZ19zdmFbWyJkZXNlcSJdXVtbImRvd25zIl1dW1sxXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdvX2RiID0gbHBfZ28sIGxlbmd0aF9kYiA9IGxwX2xlbmd0aHMpCmBgYAoKIyMjIEEgY291cGxlIHBsb3RzIGZyb20gdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uCgojIyMjIE51bWJlciBvZiBnZW5lcyBpbiBhZ3JlZW1lbnQgYW1vbmcgREUgbWV0aG9kcywgMi4zIG1vcmUgdGhhbiAyLjIKCkluIHRoZSBmdW5jdGlvbiAnY29tYmluZWRfZGVfdGFibGVzKCknIGFib3ZlLCBvbmUgb2YgdGhlIHRhc2tzCnBlcmZvcm1lZCBpcyB0byBsb29rIGF0IHRoZSBhZ3JlZW1lbnQgYW1vbmcgREVTZXEyLCBsaW1tYSwgYW5kIGVkZ2VSLgpUaGUgZm9sbG93aW5nIHNob3cgYSBjb3VwbGUgb2YgdGhlc2UgZm9yIHRoZSBzZXQgb2YgZ2VuZXMgb2JzZXJ2ZWQKd2l0aCBhIGZvbGQtY2hhbmdlID49IHwyfCBhbmQgYWRqdXN0ZWQgcC12YWx1ZSA8PSAwLjA1LgoKYGBge3IgZGVfcGxvdHN9Cnp5X3RhYmxlX3N2YVtbInZlbm5zIl1dW1sxXV1bWyJwX2xmYzEiXV1bWyJ1cF9ub3dlaWdodCJdXQpgYGAKCiMjIyMgTnVtYmVyIG9mIGdlbmVzIGluIGFncmVlbWVudCBhbW9uZyBERSBtZXRob2RzLCAyLjIgbW9yZSB0aGFuIDIuMwoKYGBge3IgZGVfcGxvdHMwMX0KenlfdGFibGVfc3ZhW1sidmVubnMiXV1bWzFdXVtbInBfbGZjMSJdXVtbImRvd25fbm93ZWlnaHQiXV0KYGBgCgojIyMjIGdvc2VxIG9udG9sb2d5IHBsb3RzIG9mIGdyb3VwcyBvZiBnZW5lcywgMi4zIG1vcmUgdGhhbiAyLjIKCgpgYGB7ciBnb3NlcV91cH0KenlfZ29fdXBbWyJwdmFsdWVfcGxvdHMiXV1bWyJicHBfcGxvdF9vdmVyIl1dCmBgYAoKIyMjIyBnb3NlcSBvbnRvbG9neSBwbG90cyBvZiBncm91cHMgb2YgZ2VuZXMsIDIuMiBtb3JlIHRoYW4gMi4zCgpgYGB7ciBnb3NlcV9kb3dufQp6eV9nb19kb3duW1sicHZhbHVlX3Bsb3RzIl1dW1siYnBwX3Bsb3Rfb3ZlciJdXQpgYGAKCiMjIExvb2sgZm9yIGFncmVlbWVudCBiZXR3ZWVuIHNlbnNpdGl2aXR5IGFuZCB6eW1vZGVtZXMKClJlbWluZCBteXNlbGYsIHRoZSBkYXRhIHN0cnVjdHVyZXMgYXJlICh6eXxzdXMpXyhkZXx0YWJsZXxzaWcpLgoKYGBge3Igc2Vuc2l0aXZlX3ZzX3p5bW99Cnp5X2RmIDwtIHp5X3RhYmxlX3N2YVtbImRhdGEiXV1bWyJ6MjNfdnNfejIyIl1dCnN1c19kZiA8LSBzdXNfdGFibGVfc3ZhW1siZGF0YSJdXVtbInNlbnNpdGl2ZV92c19yZXNpc3RhbnQiXV0KCmJvdGhfZGYgPC0gbWVyZ2UoenlfZGYsIHN1c19kZiwgYnkgPSAicm93Lm5hbWVzIikKcGxvdF9kZiA8LSBib3RoX2RmWywgYygiZGVzZXFfbG9nZmMueCIsICJkZXNlcV9sb2dmYy55IildCnJvd25hbWVzKHBsb3RfZGYpIDwtIGJvdGhfZGZbWyJSb3cubmFtZXMiXV0KY29sbmFtZXMocGxvdF9kZikgPC0gYygiejIzX3ZzX3oyMiIsICJzZW5zaXRpdmVfdnNfcmVzaXN0YW50IikKCmNvbXBhcmUgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihwbG90X2RmKQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvY29tcGFyZV9zdXNfenkucG5nIikKY29tcGFyZSRzY2F0dGVyCmNsb3NlZCA8LSBkZXYub2ZmKCkKY29tcGFyZSRzY2F0dGVyCmNvbXBhcmUkY29yCmBgYAoKIyMgWnltb2RlbWUgZW56eW1lIGdlbmUgSURzCgpOYWppYiByZWFkIG1lIGFuIGVtYWlsIGxpc3Rpbmcgb2ZmIHRoZSBnZW5lIG5hbWVzIGFzc29jaWF0ZWQgd2l0aCB0aGUgenltb2RlbWUKY2xhc3NpZmljYXRpb24uICBJIHRvb2sgdGhvc2UgbmFtZXMgYW5kIGNyb3NzIHJlZmVyZW5jZWQgdGhlbSBhZ2FpbnN0IHRoZQpMZWlzaG1hbmlhIHBhbmFtZW5zaXMgZ2VuZSBhbm5vdGF0aW9ucyBhbmQgZm91bmQgdGhlIGZvbGxvd2luZzoKClRoZXkgYXJlOgoKMS4gQUxBVDogTFBBTDEzXzEyMDAxMDkwMCAtLSBhbGFuaW5lIGFtaW5vdHJhbnNmZXJhc2UKMi4gQVNBVDogTFBBTDEzXzM0MDAxMzAwMCAtLSBhc3BhcnRhdGUgYW1pbm90cmFuc2ZlcmFzZQozLiBHNlBEOiBMUEFMMTNfMDAwMDU0MTAwIC0tIGdsdWNhc2UtNi1waG9zcGhhdGUgMS1kZWh5ZHJvZ2VuYXNlCjQuIE5IOiBMUEFMMTNfMTQwMDYxMDAsIExQQUwxM18xODAwMTg1MDAgLS0gaW5vc2luZS1ndWFuaW5lIG51Y2xlb3NpZGUgaHlkcm9sYXNlCjUuIE1QSTogTFBBTDEzXzMyMDAyMjMwMCAobWF5YmUpIC0tIG1hbm5vc2UgcGhvc3BoYXRlIGlzb21lcmFzZSAoSSBjaG9zZSBwaG9zcGhvbWFubm9zZSBpc29tZXJhc2UpCgpHaXZlbiB0aGVzZSA2IGdlbmUgSURzIChOSCBoYXMgdHdvIGdlbmUgSURzIGFzc29jaWF0ZWQgd2l0aCBpdCksIEkgY2FuIGRvIHNvbWUKbG9va2luZyBmb3Igc3BlY2lmaWMgZGlmZmVyZW5jZXMgYW1vbmcgdGhlIHZhcmlvdXMgc2FtcGxlcy4KCiMjIyBFeHByZXNzaW9uIGxldmVscyBvZiB6eW1vZGVtZSBnZW5lcwoKVGhlIGZvbGxvd2luZyBjcmVhdGVzIGEgY29sb3JzcGFjZSAocmVkIHRvIGdyZWVuKSBoZWF0bWFwIHNob3dpbmcgdGhlIG9ic2VydmVkCmV4cHJlc3Npb24gb2YgdGhlc2UgZ2VuZXMgaW4gZXZlcnkgc2FtcGxlLgoKYGBge3Igenltb2RlbWVzfQpteV9nZW5lcyA8LSBjKCJMUEFMMTNfMTIwMDEwOTAwIiwgIkxQQUwxM18zNDAwMTMwMDAiLCAiTFBBTDEzXzAwMDA1NDEwMCIsCiAgICAgICAgICAgICAgIkxQQUwxM18xNDAwMDYxMDAiLCAiTFBBTDEzXzE4MDAxODUwMCIsICJMUEFMMTNfMzIwMDIyMzAwIiwKICAgICAgICAgICAgICAib3RoZXIiKQpteV9uYW1lcyA8LSBjKCJBTEFUIiwgIkFTQVQiLCAiRzZQRCIsICJOSHYxIiwgIk5IdjIiLCAiTVBJIiwgIm90aGVyIikKCnp5bW9fZXhwdCA8LSBleGNsdWRlX2dlbmVzX2V4cHQoenlfbm9ybSwgaWRzID0gbXlfZ2VuZXMsIG1ldGhvZCA9ICJrZWVwIikKenltb19oZWF0bWFwIDwtIHBsb3Rfc2FtcGxlX2hlYXRtYXAoenltb19leHB0LCByb3dfbGFiZWwgPSBteV9uYW1lcykKenltb19oZWF0bWFwCmBgYAoKIyMgRW1waXJpY2FsbHkgb2JzZXJ2ZWQgWnltb2RlbWUgZ2VuZXMgZnJvbSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcwoKSW4gY29udHJhc3QsIHRoZSBmb2xsb3dpbmcgcGxvdHMgdGFrZSB0aGUgc2V0IG9mIGdlbmVzIHdoaWNoIGFyZSBzaGFyZWQgYW1vbmcKYWxsIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG1ldGhvZHMgKHxsZmN8ID49IDEuMCBhbmQgYWRqcCA8PSAwLjA1KSBhbmQgdXNlIHRoZW0KdG8gbWFrZSBjYXRlZ29yaWVzIG9mIGdlbmVzIHdoaWNoIGFyZSBpbmNyZWFzZWQgaW4gMi4zIG9yIDIuMi4KCmBgYHtyIHp5bW9kZW1lX2dlbmVzX2VtcGlyaWNhbH0Kc2hhcmVkX3p5bW8gPC0gaW50ZXJzZWN0X3NpZ25pZmljYW50KHp5X3RhYmxlX3N2YSkKdXBfc2hhcmVkIDwtIHNoYXJlZF96eW1vW1sidXBzIl1dW1sxXV1bWyJkYXRhIl1dW1siYWxsIl1dCnJvd25hbWVzKHVwX3NoYXJlZCkKdXBzaGFyZWRfZXhwdCA8LSBleGNsdWRlX2dlbmVzX2V4cHQoenlfbm9ybSwgaWRzID0gcm93bmFtZXModXBfc2hhcmVkKSwgbWV0aG9kID0gImtlZXAiKQpgYGAKCldlIGNhbiBwbG90IGEgcXVpY2sgaGVhdG1hcCB0byBnZXQgYSBzZW5zZSBvZiB0aGUgZGlmZmVyZW5jZXMgb2JzZXJ2ZWQKYmV0d2VlbiB0aGUgZ2VuZXMgd2hpY2ggYXJlIGRpZmZlcmVudCBiZXR3ZWVuIHRoZSB0d28genltb2RlbWVzLgoKIyMjIEhlYXRtYXAgb2Ygenltb2RlbWUgZ2VuZSBleHByZXNzaW9uIGluY3JlYXNlZCBpbiAyLjMgdnMuIDIuMgoKYGBge3Igenltb2VtcHVwfQpoaWdoXzIzX2hlYXRtYXAgPC0gcGxvdF9zYW1wbGVfaGVhdG1hcCh1cHNoYXJlZF9leHB0LCByb3dfbGFiZWwgPSByb3duYW1lcyh1cF9zaGFyZWQpKQpoaWdoXzIzX2hlYXRtYXAKYGBgCgojIyMgSGVhdG1hcCBvZiB6eW1vZGVtZSBnZW5lIGV4cHJlc3Npb24gaW5jcmVhc2VkIGluIDIuMiB2cy4gMi4zCgpgYGB7ciB6eW1vZW1kb3dufQpkb3duX3NoYXJlZCA8LSBzaGFyZWRfenltb1tbImRvd25zIl1dW1sxXV1bWyJkYXRhIl1dW1siYWxsIl1dCmRvd25zaGFyZWRfZXhwdCA8LSBleGNsdWRlX2dlbmVzX2V4cHQoenlfbm9ybSwgaWRzID0gcm93bmFtZXMoZG93bl9zaGFyZWQpLCBtZXRob2QgPSAia2VlcCIpCmhpZ2hfMjJfaGVhdG1hcCA8LSBwbG90X3NhbXBsZV9oZWF0bWFwKGRvd25zaGFyZWRfZXhwdCwgcm93X2xhYmVsID0gcm93bmFtZXMoZG93bl9zaGFyZWQpKQpoaWdoXzIyX2hlYXRtYXAKYGBgCgojIFNOUCBwcm9maWxlcwoKT3ZlciB0aGUgbGFzdCBjb3VwbGUgb2Ygd2Vla3MsIEkgcmVkaWQgYWxsIHRoZSB2YXJpYW50IHNlYXJjaGVzIHdpdGggYQpuZXdlciwgKEkgdGhpbmspIG1vcmUgc2Vuc2l0aXZlIGFuZCBtb3JlIHNwZWNpZmljIHZhcmlhbnQgdG9vbC4gIEluCmFkZGl0aW9uIEkgY2hhbmdlZCBteSBzY3JpcHQgd2hpY2ggaW50ZXJwcmV0cyB0aGUgcmVzdWx0cyBzbyB0aGF0IGl0CmlzIGFibGUgdG8gZXh0cmFjdCBhbnkgdGFncyBmcm9tIGl0LCBpbnN0ZWFkIG9mIGp1c3QgdGhlIG9uZSBvciB0d28KdGhhdCBteSBwcmV2aW91cyBzY3JpcHQgaGFuZGxlZC4gIEluIGFkZGl0aW9uLCBhdCBsZWFzdCBpbiB0aGVvcnkgaXQKaXMgbm93IGFibGUgdG8gcHJvdmlkZSB0aGUgc2V0IG9mIGFtaW5vIGFjaWQgc3Vic3RpdHV0aW9ucyBmb3IgZXZlcnkKZ2VuZSBpbiBzcGVjaWVzIHdpdGhvdXQgb3Igd2l0aCBpbnRyb25zIChub3QgcmVhbGx5IHJlbGV2YW50IGZvcgpMZWlzaG1hbmlhIHBhbmFtZW5zaXMpLgoKSG93ZXZlciwgYXMgb2YgdGhpcyB3cml0aW5nLCBJIGhhdmUgbm90IHJlLXBlcmZvcm1lZCB0aGUgc2FtZSB0YXNrcwp3aXRoIHRoZSAyMDE2IGRhdGEsIHByaW1hcmlseSBiZWNhdXNlIGl0IHdpbGwgcmVxdWlyZSByZW1hcHBpbmcgYWxsIG9mCnRoZSBzYW1wbGVzLiAgQXMgYSByZXN1bHQsIGZvciB0aGUgbW9tZW50IEkgY2Fubm90IGNvbWJpbmUgdGhlIG9sZGVyCmFuZCBuZXdlciBzYW1wbGVzLiAgVGh1cywgYW55IG9mIHRoZSBmb2xsb3dpbmcgYmxvY2tzIHdoaWNoIHVzZSB0aGUKMjAxNiBkYXRhIGFyZSBjdXJyZW50bHkgZGlzYWJsZWQuCgpgYGB7ciBvbGRuZXdfdmFyaWFudHMsIGV2YWw9RkFMU0V9Cm9sZF9leHB0IDwtIGNyZWF0ZV9leHB0KCJzYW1wbGVfc2hlZXRzL3RtcmMyX3NhbXBsZXNfMjAxOTEyMDMueGxzeCIsCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVfY29sdW1uID0gInRvcGhhdDJmaWxlIikKCnR0IDwtIGxwX2V4cHRbWyJleHByZXNzaW9uc2V0Il1dCnJvd25hbWVzKHR0KSA8LSBnc3ViKHBhdHRlcm4gPSAiXmV4b25fIiwgcmVwbGFjZW1lbnQgPSAiIiwgeCA9IHJvd25hbWVzKHR0KSkKcm93bmFtZXModHQpIDwtIGdzdWIocGF0dGVybiA9ICJcXC5FMSQiLCByZXBsYWNlbWVudCA9ICIiLCB4ID0gcm93bmFtZXModHQpKQpscF9leHB0JGV4cHJlc3Npb25zZXQgPC0gdHQKCnR0IDwtIG9sZF9leHB0JGV4cHJlc3Npb25zZXQKcm93bmFtZXModHQpIDwtIGdzdWIocGF0dGVybiA9ICJeZXhvbl8iLCByZXBsYWNlbWVudCA9ICIiLCB4ID0gcm93bmFtZXModHQpKQpyb3duYW1lcyh0dCkgPC0gZ3N1YihwYXR0ZXJuID0gIlxcLjEkIiwgcmVwbGFjZW1lbnQgPSAiIiwgeCA9IHJvd25hbWVzKHR0KSkKb2xkX2V4cHQkZXhwcmVzc2lvbnNldCA8LSB0dApybSh0dCkKYGBgCgojIyBDcmVhdGUgdGhlIFNOUCBleHByZXNzaW9uc2V0CgpPbmUgb3RoZXIgaW1wb3J0YW50IGNhdmVhdCwgd2UgaGF2ZSBhIGdyb3VwIG9mIG5ldyBzYW1wbGVzIHdoaWNoIGhhdmUKbm90IHlldCBydW4gdGhyb3VnaCB0aGUgdmFyaWFudCBzZWFyY2ggcGlwZWxpbmUsIHNvIEkgbmVlZCB0byByZW1vdmUKdGhlbSBmcm9tIGNvbnNpZGVyYXRpb24uICBUaG91Z2ggaXQgbG9va3MgbGlrZSB0aGV5IGZpbmlzaGVkIG92ZXJuaWdodC4uLgoKYGBge3IgY291bnRfZXhwdF9vbGRfbmV3fQojIyBUaGUgbmV4dCBsaW5lIGRyb3BzIHRoZSBzYW1wbGVzIHdoaWNoIGFyZSBtaXNzaW5nIHRoZSBTTlAgcGlwZWxpbmUuCmxwX3NucCA8LSBzdWJzZXRfZXhwdChscF9leHB0LCBzdWJzZXQ9IiFpcy5uYShwRGF0YShscF9leHB0KVtbJ2ZyZWViYXllc3N1bW1hcnknXV0pIikKbmV3X3NucHMgPC0gY291bnRfZXhwdF9zbnBzKGxwX3NucCwgYW5ub3RfY29sdW1uID0gImZyZWViYXllc3N1bW1hcnkiLCBzbnBfY29sdW1uPSJQQUlSRUQiKQoKbm9uemVyb19zbnBzIDwtIGV4cHJzKG5ld19zbnBzKSAhPSAwCmNvbFN1bXMobm9uemVyb19zbnBzKQpgYGAKCmBgYHtyIGNvbWJpbmVfb2xkX3NucHMsIGV2YWw9RkFMU0V9CiMjIE15IG9sZF9zbnBzIGlzIHVzaW5nIGFuIG9sZGVyIGFubm90YXRpb24gaW5jb3JyZWN0bHksIHNvIGZpeCBpdCBoZXJlOgpCaW9iYXNlOjphbm5vdGF0aW9uKG9sZF9zbnBzJGV4cHJlc3Npb25zZXQpID0gQmlvYmFzZTo6YW5ub3RhdGlvbihuZXdfc25wcyRleHByZXNzaW9uc2V0KQpib3RoX3NucHMgPC0gY29tYmluZV9leHB0cyhuZXdfc25wcywgb2xkX3NucHMpCmJvdGhfbm9ybSA8LSBub3JtYWxpemVfZXhwdChib3RoX3NucHMsIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iLCBmaWx0ZXIgPSBUUlVFKQoKIyMgc3RyYWlucyA8LSBib3RoX25vcm1bWyJkZXNpZ24iXV1bWyJzdHJhaW4iXV0KYm90aF9zdHJhaW4gPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhib3RoX25vcm0sIGZhY3QgPSAic3RyYWluIikKYGBgCgpUaGUgZGF0YSBzdHJ1Y3R1cmUgJ2JvdGhfbm9ybScgbm93IGNvbnRhaW5zIG91ciAyMDE2IGRhdGEgYWxvbmcgd2l0aAp0aGUgbmV3ZXIgZGF0YSBjb2xsZWN0ZWQgc2luY2UgMjAxOS4KCiMjIFBsb3Qgb2YgU05QIHByb2ZpbGVzIGZvciB6eW1vZGVtZXMKClRoZSBmb2xsb3dpbmcgcGxvdCBzaG93cyB0aGUgU05QIHByb2ZpbGVzIG9mIGFsbCBzYW1wbGVzIChvbGQgYW5kIG5ldykgd2hlcmUgdGhlCmNvbG9ycyBhdCB0aGUgdG9wIHNob3cgZWl0aGVyIHRoZSAyLjIgc3RyYWlucyAob3JhbmdlKSwgMi4zIHN0cmFpbnMgKGdyZWVuKSwgdGhlCnByZXZpb3VzIHNhbXBsZXMgKHB1cnBsZSksIG9yIHRoZSB2YXJpb3VzIGxhYiBzdHJhaW5zIChwaW5rIGV0YykuCgpgYGB7ciBwbG90dGluZ192YXJpYW50c30KbmV3X3ZhcmlhbnRfaGVhdG1hcCA8LSBwbG90X2Rpc2hlYXQobmV3X3NucHMpCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9yYXdfc25wX2Rpc2hlYXQucG5nIiwgaGVpZ2h0PTEyLCB3aWR0aD0xMikKbmV3X3ZhcmlhbnRfaGVhdG1hcCRwbG90CmNsb3NlZCA8LSBkZXYub2ZmKCkKbmV3X3ZhcmlhbnRfaGVhdG1hcCRwbG90CmBgYAoKVGhlIGZ1bmN0aW9uIGdldF9zbnBfc2V0cygpIHRha2VzIHRoZSBwcm92aWRlZCBtZXRhZGF0YSBmYWN0b3IgKGluCnRoaXMgY2FzZSAnY29uZGl0aW9uJykgYW5kIGxvb2tzIGZvciB2YXJpYW50cyB3aGljaCBhcmUgZXhjbHVzaXZlIHRvCmVhY2ggZWxlbWVudCBpbiBpdC4gIEluIHRoaXMgY2FzZSwgdGhpcyBpcyBsb29raW5nIGZvciBkaWZmZXJlbmNlcwpiZXR3ZWVuIDIuMiBhbmQgMi4zLCBhcyB3ZWxsIGFzIHRoZSBzZXQgc2hhcmVkIGFtb25nIHRoZW0uCgpgYGB7ciBnZXRfc25wX3NldHMxLCBldmFsPUZBTFNFfQpzbnBfc2V0cyA8LSBnZXRfc25wX3NldHMoYm90aF9zbnBzLCBmYWN0b3IgPSAiY29uZGl0aW9uIikKQmlvYmFzZTo6YW5ub3RhdGlvbihvbGRfZXhwdCRleHByZXNzaW9uc2V0KSA9IEJpb2Jhc2U6OmFubm90YXRpb24obHBfZXhwdCRleHByZXNzaW9uc2V0KQpib3RoX2V4cHQgPC0gY29tYmluZV9leHB0cyhscF9leHB0LCBvbGRfZXhwdCkKCnNucF9nZW5lcyA8LSBzbShzbnBzX3ZzX2dlbmVzKGJvdGhfZXhwdCwgc25wX3NldHMsIGV4cHRfbmFtZV9jb2wgPSAiY2hyb21vc29tZSIpKQojIyBJIHRoaW5rIHdlIGhhdmUgc29tZSBtZXRyaWNzIGhlcmUgd2UgY2FuIHBsb3QuLi4Kc25wX3N1YnNldCA8LSBzbShzbnBfc3Vic2V0X2dlbmVzKAogIGJvdGhfZXhwdCwgYm90aF9zbnBzLAogIGdlbmVzID0gYygiTFBBTDEzXzEyMDAxMDkwMCIsICJMUEFMMTNfMzQwMDEzMDAwIiwgIkxQQUwxM18wMDAwNTQxMDAiLAogICAgICAgICAgICAiTFBBTDEzXzE0MDAwNjEwMCIsICJMUEFMMTNfMTgwMDE4NTAwIiwgIkxQQUwxM18zMjAwMjIzMDAiKSkpCnp5bW9faGVhdCA8LSBwbG90X3NhbXBsZV9oZWF0bWFwKHNucF9zdWJzZXQsIHJvd19sYWJlbCA9IHJvd25hbWVzKGV4cHJzKHNucF9zdWJzZXQpKSkKenltb19oZWF0CmBgYAoKRGlkbid0IEkgY3JlYXRlIGEgc2V0IG9mIGRlbnNpdGllcyBieSBjaHJvbW9zb21lPwpPaCBJIHRoaW5rIHRoZXkgY29tZSBpbiBmcm9tIGdldF9zbnBfc2V0cygpCgojIyBTTlBTIGFzc29jaWF0ZWQgd2l0aCBjbGluaWNhbCByZXNwb25zZSBpbiB0aGUgVE1SQyBzYW1wbGVzCgpgYGB7ciBzbnBfY2xpbmljYWx9CmNsaW5pY2FsX3NldHMgPC0gZ2V0X3NucF9zZXRzKG5ld19zbnBzLCBmYWN0b3IgPSAiY2xpbmljYWxyZXNwb25zZSIpCgpkZW5zaXR5X3ZlYyA8LSBjbGluaWNhbF9zZXRzW1siZGVuc2l0eSJdXQpjaHJvbW9zb21lX2lkeCA8LSBncmVwKHBhdHRlcm4gPSAiTHBhTCIsIHggPSBuYW1lcyhkZW5zaXR5X3ZlYykpCmRlbnNpdHlfZGYgPC0gYXMuZGF0YS5mcmFtZShkZW5zaXR5X3ZlY1tjaHJvbW9zb21lX2lkeF0pCmRlbnNpdHlfZGZbWyJjaHIiXV0gPC0gcm93bmFtZXMoZGVuc2l0eV9kZikKY29sbmFtZXMoZGVuc2l0eV9kZikgPC0gYygiZGVuc2l0eV92ZWMiLCAiY2hyIikKZ2dwbG90KGRlbnNpdHlfZGYsIGFlc19zdHJpbmcoeCA9ICJjaHIiLCB5ID0gImRlbnNpdHlfdmVjIikpICsKICBnZ3Bsb3QyOjpnZW9tX2NvbCgpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRleHQgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvdXIgPSAiYmxhY2siKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSkpCgojIyBjbGluaWNhbF93cml0dGVuIDwtIHdyaXRlX3ZhcmlhbnRzKG5ld19zbnBzKQpgYGAKCiMjIyBDcm9zcyByZWZlcmVuY2UgdGhlc2UgdmFyaWFudHMgYnkgZ2VuZQoKYGBge3Igc25wX2NsYXNzaWZpY2F0aW9uc30KY2xpbmljYWxfZ2VuZXMgPC0gc20oc25wc192c19nZW5lcyhscF9leHB0LCBjbGluaWNhbF9zZXRzLCBleHB0X25hbWVfY29sID0gImNocm9tb3NvbWUiKSkKCnNucF9kZW5zaXR5IDwtIG1lcmdlKGFzLmRhdGEuZnJhbWUoY2xpbmljYWxfZ2VuZXNbWyJzdW1tYXJ5X2J5X2dlbmUiXV0pLAogICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKGZEYXRhKGxwX2V4cHQpKSwKICAgICAgICAgICAgICAgICAgICAgYnkgPSAicm93Lm5hbWVzIikKc25wX2RlbnNpdHkgPC0gc25wX2RlbnNpdHlbLCBjKDEsIDIsIDQsIDE1KV0KY29sbmFtZXMoc25wX2RlbnNpdHkpIDwtIGMoIm5hbWUiLCAic25wcyIsICJwcm9kdWN0IiwgImxlbmd0aCIpCnNucF9kZW5zaXR5W1sicHJvZHVjdCJdXSA8LSB0b2xvd2VyKHNucF9kZW5zaXR5W1sicHJvZHVjdCJdXSkKc25wX2RlbnNpdHlbWyJsZW5ndGgiXV0gPC0gYXMubnVtZXJpYyhzbnBfZGVuc2l0eVtbImxlbmd0aCJdXSkKc25wX2RlbnNpdHlbWyJkZW5zaXR5Il1dIDwtIHNucF9kZW5zaXR5W1sic25wcyJdXSAvIHNucF9kZW5zaXR5W1sibGVuZ3RoIl1dCnNucF9pZHggPC0gb3JkZXIoc25wX2RlbnNpdHlbWyJkZW5zaXR5Il1dLCBkZWNyZWFzaW5nID0gVFJVRSkKc25wX2RlbnNpdHkgPC0gc25wX2RlbnNpdHlbc25wX2lkeCwgXQoKcmVtb3ZlcnMgPC0gYygiYW1hc3RpbiIsICJncDYzIiwgImxlaXNobWFub2x5c2luIikKZm9yIChyIGluIHJlbW92ZXJzKSB7CiAgZHJvcF9pZHggPC0gZ3JlcGwocGF0dGVybiA9IHIsIHggPSBzbnBfZGVuc2l0eVtbInByb2R1Y3QiXV0pCiAgc25wX2RlbnNpdHkgPC0gc25wX2RlbnNpdHlbIWRyb3BfaWR4LCBdCn0KIyMgRmlsdGVyIHRoZXNlIGZvciBbQXxhXW1hc3RpbiBncDYzIExlaXNobWFub2x5c2luCmBgYAoKCmBgYHtyIHNucF9pbnRlcnNlY3Rpb25zfQpjbGluaWNhbF9zbnBzIDwtIHNucHNfaW50ZXJzZWN0aW9ucyhscF9leHB0LCBjbGluaWNhbF9zZXRzLCBjaHJfY29sdW1uID0gImNocm9tb3NvbWUiKQoKZmFpbF9yZWZfc25wcyA8LSBhcy5kYXRhLmZyYW1lKGNsaW5pY2FsX3NucHNbWyJpbnRlcnMiXV1bWyJmYWlsdXJlLCByZWZlcmVuY2Ugc3RyYWluIl1dKQpmYWlsX3JlZl9zbnBzIDwtIHJiaW5kKGZhaWxfcmVmX3NucHMsCiAgICAgICAgICAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZShjbGluaWNhbF9zbnBzW1siaW50ZXJzIl1dW1siZmFpbHVyZSJdXSkpCmN1cmVfc25wcyA8LSBhcy5kYXRhLmZyYW1lKGNsaW5pY2FsX3NucHNbWyJpbnRlcnMiXV1bWyJjdXJlIl1dKQoKaGVhZChmYWlsX3JlZl9zbnBzKQpoZWFkKGN1cmVfc25wcykKd3JpdGUuY3N2KGZpbGU9ImNzdi9jdXJlX3ZhcmlhbnRzLnR4dCIsIHg9cm93bmFtZXMoY3VyZV9zbnBzKSkKd3JpdGUuY3N2KGZpbGU9ImNzdi9mYWlsX3ZhcmlhbnRzLnR4dCIsIHg9cm93bmFtZXMoZmFpbF9yZWZfc25wcykpCgphbm5vdCA8LSBmRGF0YShscF9leHB0KQpjbGluaWNhbF9pbnRlcmVzdCA8LSBhcy5kYXRhLmZyYW1lKGNsaW5pY2FsX3NucHNbWyJnZW5lX3N1bW1hcmllcyJdXVtbImN1cmUiXV0pCmNsaW5pY2FsX2ludGVyZXN0IDwtIG1lcmdlKGNsaW5pY2FsX2ludGVyZXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKGNsaW5pY2FsX3NucHNbWyJnZW5lX3N1bW1hcmllcyJdXVtbImZhaWx1cmUsIHJlZmVyZW5jZSBzdHJhaW4iXV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJyb3cubmFtZXMiKQpyb3duYW1lcyhjbGluaWNhbF9pbnRlcmVzdCkgPC0gY2xpbmljYWxfaW50ZXJlc3RbWyJSb3cubmFtZXMiXV0KY2xpbmljYWxfaW50ZXJlc3RbWyJSb3cubmFtZXMiXV0gPC0gTlVMTApjb2xuYW1lcyhjbGluaWNhbF9pbnRlcmVzdCkgPC0gYygiY3VyZV9zbnBzIiwiZmFpbF9zbnBzIikKYW5ub3QgPC0gbWVyZ2UoYW5ub3QsIGNsaW5pY2FsX2ludGVyZXN0LCBieSA9ICJyb3cubmFtZXMiKQpyb3duYW1lcyhhbm5vdCkgPC0gYW5ub3RbWyJSb3cubmFtZXMiXV0KYW5ub3RbWyJSb3cubmFtZXMiXV0gPC0gTlVMTApmRGF0YShscF9leHB0JGV4cHJlc3Npb25zZXQpIDwtIGFubm90CmBgYAoKIyBaeW1vZGVtZSBmb3IgbmV3IHNhbXBsZXMKClRoZSBoZWF0bWFwIHByb2R1Y2VkIGhlcmUgc2hvdWxkIHNob3cgdGhlIHZhcmlhbnRzIG9ubHkgZm9yIHRoZSB6eW1vZGVtZSBnZW5lcy4KCiMjIEh1bnQgZm9yIHNucCBjbHVzdGVycwoKSSBhbSB0aGlua2luZyB0aGF0IGlmIHdlIGZpbmQgY2x1c3RlcnMgb2YgbG9jYXRpb25zIHdoaWNoIGFyZSB2YXJpYW50LCB0aGF0Cm1pZ2h0IHByb3ZpZGUgc29tZSBQQ1IgdGVzdGluZyBwb3NzaWJpbGl0aWVzLgoKYGBge3IgbmV3X3p5bW99CiMjIERyb3AgdGhlIDIuMSwgMi40LCB1bmtub3duLCBhbmQgbnVsbApwcnVuZWRfc25wcyA8LSBzdWJzZXRfZXhwdChuZXdfc25wcywgc3Vic2V0PSJjb25kaXRpb249PSd6Mi4yJ3xjb25kaXRpb249PSd6Mi4zJyIpCm5ld19zZXRzIDwtIGdldF9zbnBfc2V0cyhwcnVuZWRfc25wcywgZmFjdG9yID0gInp5bW9kZW1lY2F0ZWdvcmljYWwiKQpzdW1tYXJ5KG5ld19zZXRzKQojIyAxMDAwMDAwOiAyLjIKIyMgMDEwMDAwMDogMi4zCgpzdW1tYXJ5KG5ld19zZXRzW1siaW50ZXJzZWN0aW9ucyJdXVtbIjEwIl1dKQp3cml0ZS5jc3YoZmlsZT0iY3N2L3ZhcmlhbnRzXzIyLmNzdiIsIHg9bmV3X3NldHNbWyJpbnRlcnNlY3Rpb25zIl1dW1siMTAiXV0pCnN1bW1hcnkobmV3X3NldHNbWyJpbnRlcnNlY3Rpb25zIl1dW1siMDEiXV0pCndyaXRlLmNzdihmaWxlPSJjc3YvdmFyaWFudHNfMjMuY3N2IiwgeD1uZXdfc2V0c1tbImludGVyc2VjdGlvbnMiXV1bWyIwMSJdXSkKYGBgCgpUaHVzIHdlIHNlZSB0aGF0IHRoZXJlIGFyZSAzLDU1MyB2YXJpYW50cyBhc3NvY2lhdGVkIHdpdGggMi4yIGFuZCA4MSw1ODkgYXNzb2NpYXRlZCB3aXRoIDIuMy4KCiMjIyBBIHNtYWxsIGZ1bmN0aW9uIGZvciBzZWFyY2hpbmcgZm9yIHBvdGVudGlhbCBQQ1IgcHJpbWVycwoKVGhlIGZvbGxvd2luZyBmdW5jdGlvbiB1c2VzIHRoZSBwb3NpdGlvbmFsIGRhdGEgdG8gbG9vayBmb3Igc2VxdWVudGlhbAptaXNtYXRjaGVzIGFzc29jaWF0ZWQgd2l0aCB6eW1vZGVtZSBpbiB0aGUgaG9wZXMgdGhhdCB0aGVyZSB3aWxsIGJlCnNvbWUgcmVnaW9ucyB3aGljaCB3b3VsZCBwcm92aWRlIGdvb2QgcG90ZW50aWFsIHRhcmdldHMgZm9yIGEKUENSLWJhc2VkIGFzc2F5LgoKYGBge3Igc2VxdWVudGlhbF9zZWFyY2gsIGV2YWw9RkFMU0V9CnNlcXVlbnRpYWxfdmFyaWFudHMgPC0gZnVuY3Rpb24oc25wX3NldHMsIGNvbmRpdGlvbnMgPSBOVUxMLCBtaW5pbXVtID0gMywgbWF4aW11bV9zZXBhcmF0aW9uID0gMykgewogIGlmIChpcy5udWxsKGNvbmRpdGlvbnMpKSB7CiAgICBjb25kaXRpb25zIDwtIDEKICB9CiAgaW50ZXJzZWN0aW9uX3NldHMgPC0gc25wX3NldHNbWyJpbnRlcnNlY3Rpb25zIl1dCiAgaW50ZXJzZWN0aW9uX25hbWVzIDwtIHNucF9zZXRzW1sic2V0X25hbWVzIl1dCiAgY2hvc2VuX2ludGVyc2VjdGlvbiA8LSAxCiAgaWYgKGlzLm51bWVyaWMoY29uZGl0aW9ucykpIHsKICAgIGNob3Nlbl9pbnRlcnNlY3Rpb24gPC0gY29uZGl0aW9ucwogIH0gZWxzZSB7CiAgICBpbnRlcnNlY3Rpb25faWR4IDwtIGludGVyc2VjdGlvbl9uYW1lcyA9PSBjb25kaXRpb25zCiAgICBjaG9zZW5faW50ZXJzZWN0aW9uIDwtIG5hbWVzKGludGVyc2VjdGlvbl9uYW1lcylbaW50ZXJzZWN0aW9uX2lkeF0KICB9CgogIHBvc3NpYmxlX3Bvc2l0aW9ucyA8LSBpbnRlcnNlY3Rpb25fc2V0c1tbY2hvc2VuX2ludGVyc2VjdGlvbl1dCiAgcG9zaXRpb25fdGFibGUgPC0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSBwb3NzaWJsZV9wb3NpdGlvbnMpCiAgcGF0IDwtICJeY2hyXyguKylfcG9zXyguKylfcmVmXy4qJCIKICBwb3NpdGlvbl90YWJsZVtbImNociJdXSA8LSBnc3ViKHBhdHRlcm4gPSBwYXQsIHJlcGxhY2VtZW50ID0gIlxcMSIsIHggPSByb3duYW1lcyhwb3NpdGlvbl90YWJsZSkpCiAgcG9zaXRpb25fdGFibGVbWyJwb3MiXV0gPC0gYXMubnVtZXJpYyhnc3ViKHBhdHRlcm4gPSBwYXQsIHJlcGxhY2VtZW50ID0gIlxcMiIsIHggPSByb3duYW1lcyhwb3NpdGlvbl90YWJsZSkpKQogIHBvc2l0aW9uX2lkeCA8LSBvcmRlcihwb3NpdGlvbl90YWJsZVssICJjaHIiXSwgcG9zaXRpb25fdGFibGVbLCAicG9zIl0pCiAgcG9zaXRpb25fdGFibGUgPC0gcG9zaXRpb25fdGFibGVbcG9zaXRpb25faWR4LCBdCiAgcG9zaXRpb25fdGFibGVbWyJkaXN0Il1dIDwtIDAKCiAgbGFzdF9jaHIgPC0gIiIKICBmb3IgKHIgaW4gMTpucm93KHBvc2l0aW9uX3RhYmxlKSkgewogICAgdGhpc19jaHIgPC0gcG9zaXRpb25fdGFibGVbciwgImNociJdCiAgICBpZiAociA9PSAxKSB7CiAgICAgIHBvc2l0aW9uX3RhYmxlW3IsICJkaXN0Il0gPC0gcG9zaXRpb25fdGFibGVbciwgInBvcyJdCiAgICAgIGxhc3RfY2hyIDwtIHRoaXNfY2hyCiAgICAgIG5leHQKICAgIH0KICAgIGlmICh0aGlzX2NociA9PSBsYXN0X2NocikgewogICAgICBwb3NpdGlvbl90YWJsZVtyLCAiZGlzdCJdIDwtIHBvc2l0aW9uX3RhYmxlW3IsICJwb3MiXSAtIHBvc2l0aW9uX3RhYmxlW3IgLSAxLCAicG9zIl0KICAgIH0gZWxzZSB7CiAgICAgIHBvc2l0aW9uX3RhYmxlW3IsICJkaXN0Il0gPC0gcG9zaXRpb25fdGFibGVbciwgInBvcyJdCiAgICB9CiAgICBsYXN0X2NociA8LSB0aGlzX2NocgogIH0KCiAgIyMgV29ya2luZyBpbnRlcmFjdGl2ZWx5IGhlcmUuCgogIGRvdWJsZXMgPC0gcG9zaXRpb25fdGFibGVbWyJkaXN0Il1dID09IDEKICBkb3VibGVzIDwtIHBvc2l0aW9uX3RhYmxlW2RvdWJsZXMsIF0KICB3cml0ZS5jc3YoZG91YmxlcywgImRvdWJsZXMuY3N2IikKCiAgb25lX2F3YXkgPC0gcG9zaXRpb25fdGFibGVbWyJkaXN0Il1dID09IDIKICBvbmVfYXdheSA8LSBwb3NpdGlvbl90YWJsZVtvbmVfYXdheSwgXQogIHdyaXRlLmNzdihvbmVfYXdheSwgIm9uZV9hd2F5LmNzdiIpCgogIHR3b19hd2F5IDwtIHBvc2l0aW9uX3RhYmxlW1siZGlzdCJdXSA9PSAzCiAgdHdvX2F3YXkgPC0gcG9zaXRpb25fdGFibGVbdHdvX2F3YXksIF0KICB3cml0ZS5jc3YodHdvX2F3YXksICJ0d29fYXdheS5jc3YiKQoKICBjb21iaW5lZCA8LSByYmluZChkb3VibGVzLCBvbmVfYXdheSkKICBjb21iaW5lZCA8LSByYmluZChjb21iaW5lZCwgdHdvX2F3YXkpCiAgcG9zaXRpb25faWR4IDwtIG9yZGVyKGNvbWJpbmVkWywgImNociJdLCBjb21iaW5lZFssICJwb3MiXSkKICBjb21iaW5lZCA8LSBjb21iaW5lZFtwb3NpdGlvbl9pZHgsIF0KCiAgdGhpc19jaHIgPC0gIiIKICBmb3IgKHIgaW4gMTpucm93KGNvbWJpbmVkKSkgewogICAgdGhpc19jaHIgPC0gY29tYmluZWRbciwgImNociJdCiAgICBpZiAociA9PSAxKSB7CiAgICAgIGNvbWJpbmVkW3IsICJkaXN0X3BhaXIiXSA8LSBjb21iaW5lZFtyLCAicG9zIl0KICAgICAgbGFzdF9jaHIgPC0gdGhpc19jaHIKICAgICAgbmV4dAogICAgfQogICAgaWYgKHRoaXNfY2hyID09IGxhc3RfY2hyKSB7CiAgICAgIGNvbWJpbmVkW3IsICJkaXN0X3BhaXIiXSA8LSBjb21iaW5lZFtyLCAicG9zIl0gLSBjb21iaW5lZFtyIC0gMSwgInBvcyJdCiAgICB9IGVsc2UgewogICAgICBjb21iaW5lZFtyLCAiZGlzdF9wYWlyIl0gPC0gY29tYmluZWRbciwgInBvcyJdCiAgICB9CiAgICBsYXN0X2NociA8LSB0aGlzX2NocgogIH0KCiAgZGlzdF9wYWlyX21heGltdW0gPC0gMTAwMAogIGRpc3RfcGFpcl9taW5pbXVtIDwtIDIwMAogIGRpc3RfcGFpcl9pZHggPC0gY29tYmluZWRbWyJkaXN0X3BhaXIiXV0gPD0gZGlzdF9wYWlyX21heGltdW0gJgogICAgY29tYmluZWRbWyJkaXN0X3BhaXIiXV0gPj0gZGlzdF9wYWlyX21pbmltdW0KICByZW1haW5pbmcgPC0gY29tYmluZWRbZGlzdF9wYWlyX2lkeCwgXQogIG5vX3dlYWtfaWR4IDwtIGdyZXBsKHBhdHRlcm49InJlZl8oR3xDKSIsIHg9cm93bmFtZXMocmVtYWluaW5nKSkKICByZW1haW5pbmcgPC0gcmVtYWluaW5nW25vX3dlYWtfaWR4LCBdCgogIHByaW50KGhlYWQodGFibGUocG9zaXRpb25fdGFibGVbWyJkaXN0Il1dKSkpCiAgc2VxdWVudGlhbHMgPC0gcG9zaXRpb25fdGFibGVbWyJkaXN0Il1dIDw9IG1heGltdW1fc2VwYXJhdGlvbgogIG1lc3NhZ2UoIlRoZXJlIGFyZSAiLCBzdW0oc2VxdWVudGlhbHMpLCAiIGNhbmRpZGF0ZSByZWdpb25zLiIpCgogICMjIFRoZSBmb2xsb3dpbmcgY2FuIHRlbGwgbWUgaG93IG1hbnkgcnVucyBvZiBlYWNoIGxlbmd0aCBvY2N1cnJlZCwgdGhhdCBpcyBub3QgcXVpdGUgd2hhdCBJIHdhbnQuCiAgIyMgTm93IHVzZSBydW4gbGVuZ3RoIGVuY29kaW5nIHRvIGZpbmQgdGhlIHNldCBvZiBzZXF1ZW50aWFsIHNlcXVlbnRpYWxzIQogIHJsZV9yZXN1bHQgPC0gcmxlKHNlcXVlbnRpYWxzKQogIHJsZV92YWx1ZXMgPC0gcmxlX3Jlc3VsdFtbInZhbHVlcyJdXQogICMjIFRoZSBmb2xsb3dpbmcgbGluZSBpcyBlcXVpdmFsZW50IHRvIGp1c3QgbGVhdmluZyB2YWx1ZXMgYWxvbmU6CiAgIyMgdHJ1ZV92YWx1ZXMgPC0gcmxlX3Jlc3VsdFtbInZhbHVlcyJdXSA9PSBUUlVFCiAgcmxlX2xlbmd0aHMgPC0gcmxlX3Jlc3VsdFtbImxlbmd0aHMiXV0KICB0cnVlX3NlcXVlbnRpYWxzIDwtIHJsZV9sZW5ndGhzW3JsZV92YWx1ZXNdCiAgcmxlX2lkeCA8LSBjdW1zdW0ocmxlX2xlbmd0aHMpW3doaWNoKHJsZV92YWx1ZXMpXQoKICBwb3NpdGlvbl90YWJsZVtbImxhc3Rfc2VxdWVudGlhbCJdXSA8LSAwCiAgY291bnQgPC0gMAogIGZvciAociBpbiBybGVfaWR4KSB7CiAgICBjb3VudCA8LSBjb3VudCArIDEKICAgIHBvc2l0aW9uX3RhYmxlW3IsICJsYXN0X3NlcXVlbnRpYWwiXSA8LSB0cnVlX3NlcXVlbnRpYWxzW2NvdW50XQogIH0KICBtZXNzYWdlKCJUaGUgbWF4aW11bSBzZXF1ZW50aWFsIHNldCBpczogIiwgbWF4KHBvc2l0aW9uX3RhYmxlW1sibGFzdF9zZXF1ZW50aWFsIl1dKSwgIi4iKQoKICB3YW50ZWRfaWR4IDwtIHBvc2l0aW9uX3RhYmxlW1sibGFzdF9zZXF1ZW50aWFsIl1dID49IG1pbmltdW0KICB3YW50ZWQgPC0gcG9zaXRpb25fdGFibGVbd2FudGVkX2lkeCwgYygiY2hyIiwgInBvcyIpXQogIHJldHVybih3YW50ZWQpCn0KCnp5bW8yMl9zZXF1ZW50aWFscyA8LSBzZXF1ZW50aWFsX3ZhcmlhbnRzKG5ld19zZXRzLCBjb25kaXRpb25zID0gInoyMiIsIG1pbmltdW09MSwgbWF4aW11bV9zZXBhcmF0aW9uPTIpCmRpbSh6eW1vMjJfc2VxdWVudGlhbHMpCiMjIDcgY2FuZGlkYXRlIHJlZ2lvbnMgZm9yIHp5bW9kZW1lIDIuMiAtLSB0aHVzIEkgYW0gYmV0dGluZyB0aGF0IHRoZSByZWZlcmVuY2Ugc3RyYWluIGlzIGEgMi4yCnp5bW8yM19zZXF1ZW50aWFscyA8LSBzZXF1ZW50aWFsX3ZhcmlhbnRzKG5ld19zZXRzLCBjb25kaXRpb25zID0gInoyMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbmltdW0gPSAyLCBtYXhpbXVtX3NlcGFyYXRpb24gPSAyKQpkaW0oenltbzIzX3NlcXVlbnRpYWxzKQojIyBJbiBjb250cmFzdCwgdGhlcmUgYXJlIGxvdHMgKDU4Nykgb2YgaW50ZXJlc3RpbmcgcmVnaW9ucyBmb3IgMi4zIQpgYGAKCiMjIyBFeHRyYWN0IGEgcHJvbWlzaW5nIHJlZ2lvbiBmcm9tIHRoZSBnZW5vbWUKClRoZSBmaXJzdCA0IGNhbmRpZGF0ZSByZWdpb25zIGZyb20gbXkgc2V0IG9mIHJlbWFpbmluZzoKKiBDaHIgICAgICAgUG9zLiAgIERpc3RhbmNlCiogTHBhTDEzLTE1IDIzODQzMyA0NDgKKiBMcGFMMTMtMTggMTQyODQ0IDYxMwoqIExwYUwxMy0yOSA4MzAzNDIgMjUyCiogTHBhTDEzLTMzIDEzMzE1MDcgODQzCgpMZXRzIGRlZmluZSBhIGNvdXBsZSBvZiB0ZXJtczoKKiBUaGlyZDogRWFjaCBvZiB0aGUgNCBhYm92ZSBwb3NpdGlvbnMuCiogU2Vjb25kOiBUaGlyZCAtIERpc3RhbmNlCiogRW5kOiBUaGlyZCArIFByaW1lckxlbgoqIFN0YXJ0OiBTZWNvbmQgLSBQcmltZXJsZW4KCkluIGVhY2ggaW5zdGFuY2UsIHRoZXNlIGFyZSB0aGUgbGFzdCBwb3NpdGlvbnMsIHNvIHdlIHdhbnQgdG8gZ3JhYiB0aHJlZSB0aGluZ3M6CgoqIFRoZSBlbnRpcmUgcmVnaW9uIGZyb20gRW5kIC0+IFN0YXJ0LCB0aGlzIHdheSB3ZSBjYW4gaGF2ZSBhIHF1aWNrIHNhbml0eSBjaGVjay4KKiBTdGFydCAtPiBTZWNvbmQuCiogKFRoaXJkIC0+IEVuZCkgPC0gUmV2ZXJzZSBjb21wbGVtZW50ZWQKCmBgYHtyIGV4dHJhY3RfYnNnZW5vbWUsIGV2YWw9RkFMU0V9CiMjICogTHBhTDEzLTE1IDIzODQzMyA0NDgKZmlyc3RfY2FuZGlkYXRlX2NociA8LSBnZW5vbWVbWyJMcGFMMTNfMTUiXV0KcHJpbWVyX2xlbmd0aCA8LSAyMgphbXBsaWNvbl9sZW5ndGggPC0gNDQ4CmZpcnN0X2NhbmRpZGF0ZV90aGlyZCA8LSAyMzg0MzMKZmlyc3RfY2FuZGlkYXRlX3NlY29uZCA8LSBmaXJzdF9jYW5kaWRhdGVfdGhpcmQgLSBhbXBsaWNvbl9sZW5ndGgKZmlyc3RfY2FuZGlkYXRlX3N0YXJ0IDwtIGZpcnN0X2NhbmRpZGF0ZV9zZWNvbmQgLSBwcmltZXJfbGVuZ3RoCmZpcnN0X2NhbmRpZGF0ZV9lbmQgPC0gZmlyc3RfY2FuZGlkYXRlX3RoaXJkICsgcHJpbWVyX2xlbmd0aApmaXJzdF9jYW5kaWRhdGVfcmVnaW9uIDwtIHN1YnNlcShmaXJzdF9jYW5kaWRhdGVfY2hyLCBmaXJzdF9jYW5kaWRhdGVfc3RhcnQsIGZpcnN0X2NhbmRpZGF0ZV9lbmQpCmZpcnN0X2NhbmRpZGF0ZV9yZWdpb24KZmlyc3RfY2FuZGlkYXRlXzVwIDwtIHN1YnNlcShmaXJzdF9jYW5kaWRhdGVfY2hyLCBmaXJzdF9jYW5kaWRhdGVfc3RhcnQsIGZpcnN0X2NhbmRpZGF0ZV9zZWNvbmQpCmFzLmNoYXJhY3RlcihmaXJzdF9jYW5kaWRhdGVfNXApCmZpcnN0X2NhbmRpZGF0ZV8zcCA8LSBzcGdzOjpyZXZlcnNlQ29tcGxlbWVudChzdWJzZXEoZmlyc3RfY2FuZGlkYXRlX2NociwgZmlyc3RfY2FuZGlkYXRlX3RoaXJkLCBmaXJzdF9jYW5kaWRhdGVfZW5kKSkKZmlyc3RfY2FuZGlkYXRlXzNwCgoKIyMgKiBMcGFMMTMtMTggMTQyODQ0IDYxMwpzZWNvbmRfY2FuZGlkYXRlX2NociA8LSBnZW5vbWVbWyJMcGFMMTNfMTgiXV0KcHJpbWVyX2xlbmd0aCA8LSAyMgphbXBsaWNvbl9sZW5ndGggPC0gNjEzCnNlY29uZF9jYW5kaWRhdGVfdGhpcmQgPC0gMTQyODQ0CnNlY29uZF9jYW5kaWRhdGVfc2Vjb25kIDwtIHNlY29uZF9jYW5kaWRhdGVfdGhpcmQgLSBhbXBsaWNvbl9sZW5ndGgKc2Vjb25kX2NhbmRpZGF0ZV9zdGFydCA8LSBzZWNvbmRfY2FuZGlkYXRlX3NlY29uZCAtIHByaW1lcl9sZW5ndGgKc2Vjb25kX2NhbmRpZGF0ZV9lbmQgPC0gc2Vjb25kX2NhbmRpZGF0ZV90aGlyZCArIHByaW1lcl9sZW5ndGgKc2Vjb25kX2NhbmRpZGF0ZV9yZWdpb24gPC0gc3Vic2VxKHNlY29uZF9jYW5kaWRhdGVfY2hyLCBzZWNvbmRfY2FuZGlkYXRlX3N0YXJ0LCBzZWNvbmRfY2FuZGlkYXRlX2VuZCkKc2Vjb25kX2NhbmRpZGF0ZV9yZWdpb24Kc2Vjb25kX2NhbmRpZGF0ZV81cCA8LSBzdWJzZXEoc2Vjb25kX2NhbmRpZGF0ZV9jaHIsIHNlY29uZF9jYW5kaWRhdGVfc3RhcnQsIHNlY29uZF9jYW5kaWRhdGVfc2Vjb25kKQphcy5jaGFyYWN0ZXIoc2Vjb25kX2NhbmRpZGF0ZV81cCkKc2Vjb25kX2NhbmRpZGF0ZV8zcCA8LSBzcGdzOjpyZXZlcnNlQ29tcGxlbWVudChzdWJzZXEoc2Vjb25kX2NhbmRpZGF0ZV9jaHIsIHNlY29uZF9jYW5kaWRhdGVfdGhpcmQsIHNlY29uZF9jYW5kaWRhdGVfZW5kKSkKc2Vjb25kX2NhbmRpZGF0ZV8zcAoKCiMjICogTHBhTDEzLTI5IDgzMDM0MiAyNTIKdGhpcmRfY2FuZGlkYXRlX2NociA8LSBnZW5vbWVbWyJMcGFMMTNfMjkiXV0KcHJpbWVyX2xlbmd0aCA8LSAyMgphbXBsaWNvbl9sZW5ndGggPC0gMjUyCnRoaXJkX2NhbmRpZGF0ZV90aGlyZCA8LSA4MzAzNDIKdGhpcmRfY2FuZGlkYXRlX3NlY29uZCA8LSB0aGlyZF9jYW5kaWRhdGVfdGhpcmQgLSBhbXBsaWNvbl9sZW5ndGgKdGhpcmRfY2FuZGlkYXRlX3N0YXJ0IDwtIHRoaXJkX2NhbmRpZGF0ZV9zZWNvbmQgLSBwcmltZXJfbGVuZ3RoCnRoaXJkX2NhbmRpZGF0ZV9lbmQgPC0gdGhpcmRfY2FuZGlkYXRlX3RoaXJkICsgcHJpbWVyX2xlbmd0aAp0aGlyZF9jYW5kaWRhdGVfcmVnaW9uIDwtIHN1YnNlcSh0aGlyZF9jYW5kaWRhdGVfY2hyLCB0aGlyZF9jYW5kaWRhdGVfc3RhcnQsIHRoaXJkX2NhbmRpZGF0ZV9lbmQpCnRoaXJkX2NhbmRpZGF0ZV9yZWdpb24KdGhpcmRfY2FuZGlkYXRlXzVwIDwtIHN1YnNlcSh0aGlyZF9jYW5kaWRhdGVfY2hyLCB0aGlyZF9jYW5kaWRhdGVfc3RhcnQsIHRoaXJkX2NhbmRpZGF0ZV9zZWNvbmQpCmFzLmNoYXJhY3Rlcih0aGlyZF9jYW5kaWRhdGVfNXApCnRoaXJkX2NhbmRpZGF0ZV8zcCA8LSBzcGdzOjpyZXZlcnNlQ29tcGxlbWVudChzdWJzZXEodGhpcmRfY2FuZGlkYXRlX2NociwgdGhpcmRfY2FuZGlkYXRlX3RoaXJkLCB0aGlyZF9jYW5kaWRhdGVfZW5kKSkKdGhpcmRfY2FuZGlkYXRlXzNwCiMjIFlvdSBhcmUgYSBnYXJiYWdlIHBvbHlweXJpbWlkaW5lIHRyYWN0LgojIyBXaGljaCBpcyBhY3R1YWxseSBpbnRlcmVzdGluZyBpZiB0aGUgbXV0YXRpb25zIG1lc3MgaXQgdXAuCgoKIyMgKiBMcGFMMTMtMzMgMTMzMTUwNyA4NDMKZm91cnRoX2NhbmRpZGF0ZV9jaHIgPC0gZ2Vub21lW1siTHBhTDEzXzMzIl1dCnByaW1lcl9sZW5ndGggPC0gMjIKYW1wbGljb25fbGVuZ3RoIDwtIDg0Mwpmb3VydGhfY2FuZGlkYXRlX3RoaXJkIDwtIDEzMzE1MDcKZm91cnRoX2NhbmRpZGF0ZV9zZWNvbmQgPC0gZm91cnRoX2NhbmRpZGF0ZV90aGlyZCAtIGFtcGxpY29uX2xlbmd0aApmb3VydGhfY2FuZGlkYXRlX3N0YXJ0IDwtIGZvdXJ0aF9jYW5kaWRhdGVfc2Vjb25kIC0gcHJpbWVyX2xlbmd0aApmb3VydGhfY2FuZGlkYXRlX2VuZCA8LSBmb3VydGhfY2FuZGlkYXRlX3RoaXJkICsgcHJpbWVyX2xlbmd0aApmb3VydGhfY2FuZGlkYXRlX3JlZ2lvbiA8LSBzdWJzZXEoZm91cnRoX2NhbmRpZGF0ZV9jaHIsIGZvdXJ0aF9jYW5kaWRhdGVfc3RhcnQsIGZvdXJ0aF9jYW5kaWRhdGVfZW5kKQpmb3VydGhfY2FuZGlkYXRlX3JlZ2lvbgpmb3VydGhfY2FuZGlkYXRlXzVwIDwtIHN1YnNlcShmb3VydGhfY2FuZGlkYXRlX2NociwgZm91cnRoX2NhbmRpZGF0ZV9zdGFydCwgZm91cnRoX2NhbmRpZGF0ZV9zZWNvbmQpCmFzLmNoYXJhY3Rlcihmb3VydGhfY2FuZGlkYXRlXzVwKQpmb3VydGhfY2FuZGlkYXRlXzNwIDwtIHNwZ3M6OnJldmVyc2VDb21wbGVtZW50KHN1YnNlcShmb3VydGhfY2FuZGlkYXRlX2NociwgZm91cnRoX2NhbmRpZGF0ZV90aGlyZCwgZm91cnRoX2NhbmRpZGF0ZV9lbmQpKQpmb3VydGhfY2FuZGlkYXRlXzNwCmBgYAoKIyMgR28gaHVudGluZyBmb3IgU2FuZ2VyIHNlcXVlbmNpbmcgcmVnaW9ucwoKSSBtYWRlIGEgZnVuIGxpdHRsZSBmdW5jdGlvbiB3aGljaCBzaG91bGQgZmluZCByZWdpb25zIHdoaWNoIGhhdmUgbG90cyBvZiB2YXJpYW50cwphc3NvY2lhdGVkIHdpdGggYSBnaXZlbiBleHBlcmltZW50YWwgZmFjdG9yLgoKYGBge3Igc2FuZ2VyX2Z1bn0KcGhlbm8gPC0gc3Vic2V0X2V4cHQobHBfZXhwdCwgc3Vic2V0ID0gImNvbmRpdGlvbj09J3oyLjInfGNvbmRpdGlvbj09J3oyLjMnIikKcGhlbm8gPC0gc3Vic2V0X2V4cHQocGhlbm8sIHN1YnNldCA9ICIhaXMubmEocERhdGEocGhlbm8pW1snYmNmdGFibGUnXV0pIikKcGhlbm9fc25wcyA8LSBzbShjb3VudF9leHB0X3NucHMocGhlbm8sIGFubm90X2NvbHVtbiA9ICJiY2Z0YWJsZSIpKQoKZnVuX3N0dWZmIDwtIHNucF9kZW5zaXR5X3ByaW1lcnMoCiAgICBwaGVub19zbnBzLAogICAgYnNnZW5vbWUgPSAiQlNHZW5vbWUuTGVpc2htYW5pYS5wYW5hbWVuc2lzLk1IT01DT0w4MUwxMy52NTMiLAogICAgZ2ZmID0gInJlZmVyZW5jZS9UcmlUcnlwREItNTNfTHBhbmFtZW5zaXNNSE9NQ09MODFMMTMuZ2ZmIikKZHJvcF9zY2FmZm9sZHMgPC0gZ3JlcGwoeCA9IHJvd25hbWVzKGZ1bl9zdHVmZiRmYXZvcml0ZXMpLCBwYXR0ZXJuID0gIlNDQUYiKQpmYXZvcml0ZV9wcmltZXJfcmVnaW9ucyA8LSBmdW5fc3R1ZmZbWyJmYXZvcml0ZXMiXV1bIWRyb3Bfc2NhZmZvbGRzLCBdCmZhdm9yaXRlX3ByaW1lcl9yZWdpb25zW1siYmluIl1dIDwtIHJvd25hbWVzKGZhdm9yaXRlX3ByaW1lcl9yZWdpb25zKQpsaWJyYXJ5KGRwbHlyKQpmYXZvcml0ZV9wcmltZXJfcmVnaW9ucyA8LSBmYXZvcml0ZV9wcmltZXJfcmVnaW9ucyAlPiUKICByZWxvY2F0ZShiaW4pCmBgYAoKIyMgQ29tYmluZSB0aGlzIHRhYmxlIHdpdGggMi4yLzIuMyBnZW5lcwoKSGVyZSBpcyBteSBub3RlIGZyb20gb3VyIG1lZXRpbmc6CgpDcm9zcyByZWZlcmVuY2UgcHJpbWVycyB0byBERSBnZW5lcyBvZiAyLjIvMi4zIGFuZC9vciByZXNpc3RhbmNlL3N1c2NwZXRpYmxlLAphZGQgYSBjb2x1bW4gdG8gdGhlIHByaW1lciBzcHJlYWRzaGVldCB3aXRoIHRoZSBERSBnZW5lcyAoaW4gcmV0cm9zcGVjdCBJIGFtIGd1ZXNzaW5nCnRoaXMgYWN0dWFsbHkgbWVhbnMgdG8gcHV0IHRoZSBsb2dGQyBhcyBhIGNvbHVtbi4KCk9uZSBuaWNlIHRoaW5nLCBJIGRpZCBhIHNlbWFudGljIHJlbW92YWwgb24gdGhlIGxwX2V4cHQsIHNvIHRoZSBzZXQgb2YgbG9nRkMvcHZhbHVlcwpzaG91bGQgbm90IGhhdmUgYW55IG9mIHRoZSBvZmZlbmRpbmcgdHlwZXM7IHRodXMgSSBzaG91bGQgYmUgYWJsZSB0byBhdXRvbWFnaWNhbGx5CmdldCByaWQgb2YgdGhlbSBpbiB0aGUgbWVyZ2UuCgpgYGB7ciB4cmVmX3ByaW1lcnNfZGVnfQpsb2dmYyA8LSB6eV90YWJsZV9zdmFbWyJkYXRhIl1dW1siejIzX3ZzX3oyMiJdXQpsb2dmY19jb2x1bW5zIDwtIGxvZ2ZjWywgYygiZGVzZXFfbG9nZmMiLCAiZGVzZXFfYWRqcCIpXQpjb2xuYW1lcyhsb2dmY19jb2x1bW5zKSA8LSBjKCJ6MjNfbG9nZmMiLCAiejIzX2FkanAiKQpuZXdfdGFibGUgPC0gbWVyZ2UoZmF2b3JpdGVfcHJpbWVyX3JlZ2lvbnMsIGxvZ2ZjX2NvbHVtbnMsCiAgICAgICAgICAgICAgICAgICBieS54ID0gImNsb3Nlc3RfZ2VuZV9iZWZvcmVfaWQiLCBieS55ID0gInJvdy5uYW1lcyIpCnN1cyA8LSBzdXNfdGFibGVfc3ZhW1siZGF0YSJdXVtbInNlbnNpdGl2ZV92c19yZXNpc3RhbnQiXV0Kc3VzX2NvbHVtbnMgPC0gc3VzWywgYygiZGVzZXFfbG9nZmMiLCAiZGVzZXFfYWRqcCIpXQpjb2xuYW1lcyhzdXNfY29sdW1ucykgPC0gYygic3VzX2xvZ2ZjIiwgInN1c19hZGpwIikKbmV3X3RhYmxlIDwtIG1lcmdlKG5ld190YWJsZSwgc3VzX2NvbHVtbnMsCiAgICAgICAgICAgICAgICAgICBieS54ID0gImNsb3Nlc3RfZ2VuZV9iZWZvcmVfaWQiLCBieS55ID0gInJvdy5uYW1lcyIpICU+JQogIHJlbG9jYXRlKGJpbikKd3JpdHRlbiA8LSB3cml0ZV94bHN4KGRhdGE9bmV3X3RhYmxlLAogICAgICAgICAgICAgICAgICAgICAgZXhjZWw9ImV4Y2VsL2Zhdm9yaXRlX3ByaW1lcnNfeHJlZl96eV9zdXMueGxzeCIpCmBgYAoKCiMjIE1ha2UgYSBoZWF0bWFwIGRlc2NyaWJpbmcgdGhlIGNsdXN0ZXJpbmcgb2YgdmFyaWFudHMKCldlIGNhbiBjcm9zcyByZWZlcmVuY2UgdGhlIHZhcmlhbnRzIGFnYWluc3QgdGhlIHp5bW9kZW1lIHN0YXR1cyBhbmQKcGxvdCBhIGhlYXRtYXAgb2YgdGhlIHJlc3VsdHMgYW5kIGhvcGVmdWxseSBzZWUgaG93IHRoZXkgc2VwYXJhdGUuCgpgYGB7ciB6eW1vX2hlYXRtYXBzfQojIyBwcnVuZWRfc25wcyA8LSBzdWJzZXRfZXhwdChuZXdfc25wcywgc3Vic2V0PSJjb25kaXRpb249PSd6Mi4yJ3xjb25kaXRpb249PSd6Mi4zJyIpCnNucF9nZW5lcyA8LSBzbShzbnBzX3ZzX2dlbmVzKGxwX2V4cHQsIG5ld19zZXRzLCBleHB0X25hbWVfY29sID0gImNocm9tb3NvbWUiKSkKCiMjbmV3X3p5bW9fbm9ybSA8LSBub3JtYWxpemVfZXhwdChwcnVuZWRfc25wcywgZmlsdGVyID0gVFJVRSwgY29udmVydCA9ICJjcG0iLCBub3JtID0gInF1YW50IiwgdHJhbnNmb3JtID0gVFJVRSkKIyNuZXdfenltb19ub3JtIDwtIHNldF9leHB0X2NvbmRpdGlvbnMobmV3X3p5bW9fbm9ybSwgZmFjdCA9ICJ6eW1vZGVtZWNhdGVnb3JpY2FsIikKY2xpbmljYWxfY29sb3JzX3YyIDwtIGxpc3QoCiAgICAiejIyIiA9ICIjMDAwMGNjIiwKICAgICJ6MjMiID0gIiNjYzAwMDAiKQpuZXdfenltb19ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KHBydW5lZF9zbnBzLCBmaWx0ZXIgPSBUUlVFLCBjb252ZXJ0ID0gImNwbSIsIG5vcm0gPSAicXVhbnQiLCB0cmFuc2Zvcm0gPSBUUlVFKSAlPiUKICBzZXRfZXhwdF9jb25kaXRpb25zKGZhY3QgPSAienltb2RlbWVjYXRlZ29yaWNhbCIpICU+JQogIHNldF9leHB0X2NvbG9ycyhjbGluaWNhbF9jb2xvcnNfdjIpCgp6eW1vX2hlYXQgPC0gcGxvdF9kaXNoZWF0KG5ld196eW1vX25vcm0pCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9vbmx5ejIyX3oyM19zbnBfaGVhdG1hcC5wZGYiLCB3aWR0aD0xMiwgaGVpZ2h0PTEyKQp6eW1vX2hlYXRbWyJwbG90Il1dCmNsb3NlZCA8LSBkZXYub2ZmKCkKenltb19oZWF0W1sicGxvdCJdXQpgYGAKCiMjIyBBbm5vdGF0ZWQgaGVhdG1hcCBvZiB2YXJpYW50cwoKTm93IGxldCB1cyB0cnkgdG8gbWFrZSBhIGhlYXRtYXAgd2hpY2ggaW5jbHVkZXMgc29tZSBvZiB0aGUgYW5ub3RhdGlvbiBkYXRhLgoKYGBge3Igenltb19oZWF0X3BhbmVsX2dlbmVzfQpkZXMgPC0gYm90aF9ub3JtW1siZGVzaWduIl1dCnVuZGVmX2lkeCA8LSBpcy5uYShkZXNbWyJzdHJhaW4iXV0pCmRlc1t1bmRlZl9pZHgsICJzdHJhaW4iXSA8LSAidW5rbm93biIKCiMjaG1jb2xzIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygieWVsbG93IiwiYmxhY2siLCJkYXJrYmx1ZSIpKSgyNTYpCmNvcnJlbGF0aW9ucyA8LSBocGdsX2NvcihleHBycyhib3RoX25vcm0pKQoKenltb19taXNzaW5nX2lkeCA8LSBpcy5uYShkZXNbWyJ6eW1vZGVtZWNhdGVnb3JpY2FsIl1dKQpkZXNbWyJ6eW1vZGVtZWNhdGVnb3JpY2FsIl1dIDwtIGFzLmNoYXJhY3RlcihkZXNbWyJ6eW1vZGVtZWNhdGVnb3JpY2FsIl1dKQpkZXNbWyJjbGluaWNhbGNhdGVnb3JpY2FsIl1dIDwtIGFzLmNoYXJhY3RlcihkZXNbWyJjbGluaWNhbGNhdGVnb3JpY2FsIl1dKQpkZXNbenltb19taXNzaW5nX2lkeCwgInp5bW9kZW1lY2F0ZWdvcmljYWwiXSA8LSAidW5rbm93biIKbXlkZW5kcm8gPC0gbGlzdCgKICAiY2x1c3RmdW4iID0gaGNsdXN0LAogICJsd2QiID0gMi4wKQpjb2xfZGF0YSA8LSBhcy5kYXRhLmZyYW1lKGRlc1ssIGMoInp5bW9kZW1lY2F0ZWdvcmljYWwiLCAiY2xpbmljYWxjYXRlZ29yaWNhbCIpXSkKCnVua25vd25fY2xpbmljYWwgPC0gaXMubmEoY29sX2RhdGFbWyJjbGluaWNhbGNhdGVnb3JpY2FsIl1dKQpyb3dfZGF0YSA8LSBhcy5kYXRhLmZyYW1lKGRlc1ssIGMoInN0cmFpbiIpXSkKY29sbmFtZXMoY29sX2RhdGEpIDwtIGMoInp5bW9kZW1lIiwgIm91dGNvbWUiKQpjb2xfZGF0YVt1bmtub3duX2NsaW5pY2FsLCAib3V0Y29tZSJdIDwtICJ1bmRlZmluZWQiCgpjb2xuYW1lcyhyb3dfZGF0YSkgPC0gYygic3RyYWluIikKbXlhbm5vdCA8LSBsaXN0KAogICJDb2wiID0gbGlzdCgiZGF0YSIgPSBjb2xfZGF0YSksCiAgIlJvdyIgPSBsaXN0KCJkYXRhIiA9IHJvd19kYXRhKSkKbXljbHVzdCA8LSBsaXN0KCJjdXRoIiA9IDEuMCwKICAgICAgICAgICAgICAgICJjb2wiID0gQnJld2VyQ2x1c3RlckNvbCkKbXlsYWJzIDwtIGxpc3QoCiAgIlJvdyIgPSBsaXN0KCJucm93IiA9IDQpLAogICJDb2wiID0gbGlzdCgibnJvdyIgPSA0KSkKaG1jb2xzIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiZGFya2JsdWUiLCAiYmVpZ2UiKSkoMjQwKQp6eW1vX2Fubm90X2hlYXQgPC0gYW5uSGVhdG1hcDIoCiAgICBjb3JyZWxhdGlvbnMsCiAgICBkZW5kcm9ncmFtID0gbXlkZW5kcm8sCiAgICBhbm5vdGF0aW9uID0gbXlhbm5vdCwKICAgIGNsdXN0ZXIgPSBteWNsdXN0LAogICAgbGFiZWxzID0gbXlsYWJzLAogICAgIyMgVGhlIGZvbGxvd2luZyBjb250cm9scyBpZiB0aGUgcGljdHVyZSBpcyBzeW1tZXRyaWMKICAgIHNjYWxlID0gIm5vbmUiLAogICAgY29sID0gaG1jb2xzKQoKZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL2RlbmRyb19oZWF0bWFwLnBuZyIsIGhlaWdodCA9IDIwLCB3aWR0aCA9IDIwKQpwbG90KHp5bW9fYW5ub3RfaGVhdCkKY2xvc2VkIDwtIGRldi5vZmYoKQpwbG90KHp5bW9fYW5ub3RfaGVhdCkKYGBgCgpQcmludCB0aGUgbGFyZ2VyIGhlYXRtYXAgc28gdGhhdCBhbGwgdGhlIGxhYmVscyBhcHBlYXIuICBLZWVwIGluIG1pbmQKdGhhdCBhcyB3ZSBnZXQgbW9yZSBzYW1wbGVzLCB0aGlzIGltYWdlIG5lZWRzIHRvIGNvbnRpbnVlIGdldHRpbmcKYmlnZ2VyLgoKIVtiaWcgaGVhdG1hcF0oaW1hZ2VzL2RlbmRyb19oZWF0bWFwLnBuZykKCgpgYGB7ciB0aGVyZXNhX2lkZWF9CnhyZWZfcHJvcCA8LSB0YWJsZShwaGVub19zbnBzW1siY29uZGl0aW9ucyJdXSkKcGhlbm9fc25wcyRjb25kaXRpb25zCmlkeF90YmwgPC0gZXhwcnMocGhlbm9fc25wcykgPiA1Cm5ld190YmwgPC0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSByb3duYW1lcyhleHBycyhwaGVub19zbnBzKSkpCmZvciAobiBpbiBuYW1lcyh4cmVmX3Byb3ApKSB7CiAgbmV3X3RibFtbbl1dIDwtIDAKICBpZHhfY29scyA8LSB3aGljaChwaGVub19zbnBzW1siY29uZGl0aW9ucyJdXSA9PSBuKQogIHByb3BfY29sIDwtIHJvd1N1bXMoaWR4X3RibFssIGlkeF9jb2xzXSkgLyB4cmVmX3Byb3Bbbl0KICBuZXdfdGJsW25dIDwtIHByb3BfY29sCn0Ka2VlcGVycyA8LSBncmVwbCh4ID0gcm93bmFtZXMobmV3X3RibCksIHBhdHRlcm4gPSAiTHBhTDEzIikKbmV3X3RibCA8LSBuZXdfdGJsW2tlZXBlcnMsIF0KbmV3X3RibFtbInN0cm9uZzIyIl1dIDwtIDEuMDAxIC0gbmV3X3RibFtbInoyLjIiXV0KbmV3X3RibFtbInN0cm9uZzIzIl1dIDwtIDEuMDAxIC0gbmV3X3RibFtbInoyLjMiXV0KczIyX25hIDwtIG5ld190YmxbWyJzdHJvbmcyMiJdXSA+IDEKbmV3X3RibFtzMjJfbmEsICJzdHJvbmcyMiJdIDwtIDEKczIzX25hIDwtIG5ld190YmxbWyJzdHJvbmcyMyJdXSA+IDEKbmV3X3RibFtzMjNfbmEsICJzdHJvbmcyMyJdIDwtIDEKCm5ld190YmxbWyJTTlAiXV0gPC0gcm93bmFtZXMobmV3X3RibCkKbmV3X3RibFtbIkNocm9tb3NvbWUiXV0gPC0gZ3N1Yih4ID0gbmV3X3RibFtbIlNOUCJdXSwgcGF0dGVybiA9ICJjaHJfKC4qKV9wb3NfLioiLCByZXBsYWNlbWVudCA9ICJcXDEiKQpuZXdfdGJsW1siUG9zaXRpb24iXV0gPC0gZ3N1Yih4ID0gbmV3X3RibFtbIlNOUCJdXSwgcGF0dGVybiA9ICIuKl9wb3NfKFxcZCspXy4qIiwgcmVwbGFjZW1lbnQgPSAiXFwxIikKbmV3X3RibCA8LSBuZXdfdGJsWywgYygiU05QIiwgIkNocm9tb3NvbWUiLCAiUG9zaXRpb24iLCAic3Ryb25nMjIiLCAic3Ryb25nMjMiKV0KCgpsaWJyYXJ5KENNcGxvdCkKc2ltcGxpZnkgPC0gbmV3X3RibApzaW1wbGlmeVtbInN0cm9uZzIyIl1dIDwtIE5VTEwKCgpDTXBsb3Qoc2ltcGxpZnksIGJpbi5zaXplID0gMTAwMDAwKQoKQ01wbG90KG5ld190YmwsIHBsb3QudHlwZT0ibSIsIG11bHRyYWNrcz1UUlVFLCB0aHJlc2hvbGQgPSBjKDAuMDEsIDAuMDUpLAogICAgICAgdGhyZXNob2xkLmx3ZD1jKDEsMSksIHRocmVzaG9sZC5jb2w9YygiYmxhY2siLCJncmV5IiksCiAgICAgICBhbXBsaWZ5PVRSVUUsIGJpbi5zaXplPTEwMDAwLAogICAgICAgY2hyLmRlbi5jb2w9YygiZGFya2dyZWVuIiwgInllbGxvdyIsICJyZWQiKSwKICAgICAgIHNpZ25hbC5jb2w9YygicmVkIiwgImdyZWVuIiwgImJsdWUiKSwKICAgICAgIHNpZ25hbC5jZXg9MSwgZmlsZT0ianBnIiwgbWVtbz0iIiwgZHBpPTMwMCwgZmlsZS5vdXRwdXQ9VFJVRSwgdmVyYm9zZT1UUlVFKQpgYGAKCiFbU05QIERlbnNpdHldKFNOUC1EZW5zaXR5LnJhdGlvLmpwZykKIVtDaXJjdWxhciBNYW5oYXR0YW5dKENpcmN1bGFyLU1hbmhhdHRhbi5yYXRpby5qcGcpCiFbUmVjdGFuZ3VsYXIgTWFuaGF0dGFuXShSZWN0YW5ndWxhci1NYW5oYXR0YW4ucmF0aW8uanBnKQohW1FRXShRUXBsb3QucmF0aW8uanBnKQoKIyMgVHJ5IG91dCBNYXRyaXhFUVRMCgpUaGlzIHRvb2wgbG9va3MgYSBsaXR0bGUgb3BhcXVlLCBidXQgcHJvdmlkZXMgc2FtcGxlIGRhdGEgd2l0aCB0aGluZ3MKdGhhdCBtYWtlIHNlbnNlIHRvIG1lIGFuZCBzaG91bGQgYmUgcHJldHR5IGVhc3kgdG8gcmVjYXBpdHVsYXRlIGluIG91cgpkYXRhLgoKMS4gIGNvdmFyaWF0ZXMudHh0OiBDb2x1bW5zIGFyZSBzYW1wbGVzLCByb3dzIGFyZSB0aGluZ3MgZnJvbSBwRGF0YSAtLSB0aGUKICAgIG1vc3QgbGlrZWx5IG9uZXMgb2YgaW50ZXJlc3QgZm9yIG91ciBkYXRhIHdvdWxkIGJlIHp5bW9kZW1lLAogICAgc2Vuc2l0aXZpdHkKMi4gIGdlbmVsb2MudHh0OiBjb2x1bW5zIGFyZSAnZ2VuZWlkJywgJ2NocicsICdsZWZ0JywgJ3JpZ2h0Jy4gIEkKICAgIGd1ZXNzIEkgY2FuIGFzc3VtZSBsZWZ0IGFuZCByaWdodCBhcmUgc3RhcnQvc3RvcDsgaW4gd2hpY2ggY2FzZQogICAgdGhpcyBpcyB0cml2aWFsbHkgYWNxdWlyYWJsZSBmcm9tIGZEYXRhLgozLiAgZ2UudHh0OiBUaGlzIGFwcGVhcnMgdG8gYmUgYSBsb2cocnBrbS9jcG0pIHRhYmxlIHdpdGggcm93cyBhcyBnZW5lcyBhbmQKICAgIGNvbHVtbnMgYXMgc2FtcGxlcwo0LiAgc25wc2xvYy50eHQ6IGNvbHVtbnMgYXJlICdzbnBpZCcsICdjaHInLCAncG9zJwo1LiAgc25wcy50eHQ6IGNvbHVtbnMgYXJlIHNhbXBsZXMsIHJvd3MgYXJlIHRoZSBpZHMgZnJvbSBzbnNwbG9jLAogICAgdmFsdWVzIGEgMCwxLDIuICBJIGFzc3VtZSAwIGlzIGlkZW50aWNhbCBhbmQgMS4uMTIgYXJlIHRoZSB2YXJpb3VzCiAgICBBLT5UR0MgVC0+QUdDIEMtPkFHVCBHLT5BQ1QKCmBgYHtyIG1hdHJpeGVxdGwsIGV2YWw9RkFMU0V9CiMjIEZvciB0aGlzLCBsZXQgdXMgdXNlIHRoZSAnbmV3X3NucHMnIGRhdGEgc3RydWN0dXJlLgojIyBDYXZlYXQgaGVyZTogdGhlc2UgbmVlZCB0byBiZSBjb2VyY2VkIHRvIG51bWJlcnMuCm15X2NvdmFyaWF0ZXMgPC0gcERhdGEobmV3X3NucHMpWywgYygienltb2RlbWVjYXRlZ29yaWNhbCIsICJjbGluaWNhbGNhdGVnb3JpY2FsIildCmZvciAoY29sIGluIGNvbG5hbWVzKG15X2NvdmFyaWF0ZXMpKSB7CiAgbXlfY292YXJpYXRlc1tbY29sXV0gPC0gYXMubnVtZXJpYyhhcy5mYWN0b3IobXlfY292YXJpYXRlc1tbY29sXV0pKQp9Cm15X2NvdmFyaWF0ZXMgPC0gdChteV9jb3ZhcmlhdGVzKQoKbXlfZ2VuZWxvYyA8LSBmRGF0YShscF9leHB0KVssIGMoImdpZCIsICJjaHJvbW9zb21lIiwgInN0YXJ0IiwgImVuZCIpXQpjb2xuYW1lcyhteV9nZW5lbG9jKSA8LSBjKCJnZW5laWQiLCAiY2hyIiwgImxlZnQiLCAicmlnaHQiKQoKbXlfZ2UgPC0gZXhwcnMobm9ybWFsaXplX2V4cHQobHBfZXhwdCwgdHJhbnNmb3JtID0gImxvZzIiLCBmaWx0ZXIgPSBUUlVFLCBjb252ZXJ0ID0gImNwbSIpKQp1c2VkX3NhbXBsZXMgPC0gdG9sb3dlcihjb2xuYW1lcyhteV9nZSkpICVpbiUgY29sbmFtZXMoZXhwcnMobmV3X3NucHMpKQpteV9nZSA8LSBteV9nZVssIHVzZWRfc2FtcGxlc10KCm15X3NucHNsb2MgPC0gZGF0YS5mcmFtZShyb3duYW1lcyA9IHJvd25hbWVzKGV4cHJzKG5ld19zbnBzKSkpCiMjIE9oLCBjYXZlYXQgaGVyZTogQmVjYXVzZSBvZiB0aGUgd2F5IEkgc3RvcmVkIHRoZSBkYXRhLAojIyBJIGNvdWxkIGhhdmUgZHVwbGljYXRlIHJvd3Mgd2hpY2ggcHJlc3VtYWJseSB3aWxsIG1ha2UgbWF0cml4RVFUTCBzYWQKbXlfc25wc2xvY1tbImNociJdXSA8LSBnc3ViKHBhdHRlcm4gPSAiXmNocl8oLispX3BvcyguKylfcmVmXy4qJCIsIHJlcGxhY2VtZW50ID0gIlxcMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gcm93bmFtZXMobXlfc25wc2xvYykpCm15X3NucHNsb2NbWyJwb3MiXV0gPC0gZ3N1YihwYXR0ZXJuID0gIl5jaHJfKC4rKV9wb3MoLispX3JlZl8uKiQiLCByZXBsYWNlbWVudCA9ICJcXDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IHJvd25hbWVzKG15X3NucHNsb2MpKQp0ZXN0IDwtIGR1cGxpY2F0ZWQobXlfc25wc2xvYykKIyMgRWFjaCBkdXBsaWNhdGVkIHJvdyB3b3VsZCBiZSBhbm90aGVyIHZhcmlhbnQgYXQgdGhhdCBwb3NpdGlvbjsKIyMgc28gaW4gdGhlb3J5IHdlIHdvdWxkIGRvIGEgcmxlIHRvIG51bWJlciB0aGVtIEkgYW0gZ3Vlc3NpbmcKIyMgSG93ZXZlciwgSSBkbyBub3QgaGF2ZSBkaWZmZXJlbnQgdmFyaWFudHMgc28gSSB0aGluayBJIGNhbiBpZ25vcmUgdGhpcyBmb3IgdGhlIG1vbWVudAojIyBidXQgd2lsbCBuZWVkIHRvIG1ha2UgbXkgbWF0cml4IGVpdGhlciAwIG9yIDEuCmlmIChzdW0odGVzdCkgPiAwKSB7CiAgbWVzc2FnZSgiVGhlcmUgYXJlOiAiLCBzdW0oZHVwbGljYXRlZCksICIgZHVwbGljYXRlZCBlbnRyaWVzLiIpCiAga2VlcF9pZHggPC0gISB0ZXN0CiAgbXlfc25wc2xvYyA8LSBteV9zbnBzbG9jW2tlZXBfaWR4LCBdCn0KCm15X3NucHMgPC0gZXhwcnMobmV3X3NucHMpCm9uZV9pZHggPC0gbXlfc25wcyA+IDAKbXlfc25wc1tvbmVfaWR4XSA8LSAxCgojIyBPaywgYXQgdGhpcyBwb2ludCBJIHRoaW5rIEkgaGF2ZSBhbGwgdGhlIHBpZWNlcyB3aGljaCB0aGlzIG1ldGhvZCB3YW50cy4uLgojIyBPaCwgbm8gSSBndWVzcyBub3Q7IGl0IGFjdHVhbGx5IHdhbnRzIHRoZSBkYXRhIGFzIGEgc2V0IG9mIGZpbGVuYW1lcy4uLgpsaWJyYXJ5KE1hdHJpeEVRVEwpCndyaXRlLnRhYmxlKG15X3NucHMsICJlcXRsL3NucHMudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YobXlfc25wcywgImVxdGwvc25wcy50c3YiLCApCndyaXRlLnRhYmxlKG15X3NucHNsb2MsICJlcXRsL3NucHNsb2MudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YobXlfc25wc2xvYywgImVxdGwvc25wc2xvYy50c3YiKQp3cml0ZS50YWJsZShhcy5kYXRhLmZyYW1lKG15X2dlKSwgImVxdGwvZ2UudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YoYXMuZGF0YS5mcmFtZShteV9nZSksICJlcXRsL2dlLnRzdiIpCndyaXRlLnRhYmxlKGFzLmRhdGEuZnJhbWUobXlfZ2VuZWxvYyksICJlcXRsL2dlbmVsb2MudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YoYXMuZGF0YS5mcmFtZShteV9nZW5lbG9jKSwgImVxdGwvZ2VuZWxvYy50c3YiKQp3cml0ZS50YWJsZShhcy5kYXRhLmZyYW1lKG15X2NvdmFyaWF0ZXMpLCAiZXF0bC9jb3ZhcmlhdGVzLnRzdiIsIG5hID0gIk5BIiwgY29sLm5hbWVzID0gVFJVRSwgcm93Lm5hbWVzID0gVFJVRSwgc2VwID0gIlx0IiwgcXVvdGUgPSBUUlVFKQojIyByZWFkcjo6d3JpdGVfdHN2KGFzLmRhdGEuZnJhbWUobXlfY292YXJpYXRlcyksICJlcXRsL2NvdmFyaWF0ZXMudHN2IikKCnVzZU1vZGVsID0gbW9kZWxMSU5FQVIgIyBtb2RlbEFOT1ZBLCBtb2RlbExJTkVBUiwgb3IgbW9kZWxMSU5FQVJfQ1JPU1MKCiMgR2Vub3R5cGUgZmlsZSBuYW1lClNOUF9maWxlX25hbWUgPSAiZXF0bC9zbnBzLnRzdiIKc25wc19sb2NhdGlvbl9maWxlX25hbWUgPSAiZXF0bC9zbnBzbG9jLnRzdiIKZXhwcmVzc2lvbl9maWxlX25hbWUgPSAiZXF0bC9nZS50c3YiCmdlbmVfbG9jYXRpb25fZmlsZV9uYW1lID0gImVxdGwvZ2VuZWxvYy50c3YiCmNvdmFyaWF0ZXNfZmlsZV9uYW1lID0gImVxdGwvY292YXJpYXRlcy50c3YiCiMgT3V0cHV0IGZpbGUgbmFtZQpvdXRwdXRfZmlsZV9uYW1lX2NpcyA9IHRlbXBmaWxlKCkKb3V0cHV0X2ZpbGVfbmFtZV90cmEgPSB0ZW1wZmlsZSgpCiMgT25seSBhc3NvY2lhdGlvbnMgc2lnbmlmaWNhbnQgYXQgdGhpcyBsZXZlbCB3aWxsIGJlIHNhdmVkCnB2T3V0cHV0VGhyZXNob2xkX2NpcyA9IDAuMQpwdk91dHB1dFRocmVzaG9sZF90cmEgPSAwLjEKIyBFcnJvciBjb3ZhcmlhbmNlIG1hdHJpeAojIFNldCB0byBudW1lcmljKCkgZm9yIGlkZW50aXR5LgplcnJvckNvdmFyaWFuY2UgPSBudW1lcmljKCkKIyBlcnJvckNvdmFyaWFuY2UgPSByZWFkLnRhYmxlKCJTYW1wbGVfRGF0YS9lcnJvckNvdmFyaWFuY2UudHh0Iik7CiMgRGlzdGFuY2UgZm9yIGxvY2FsIGdlbmUtU05QIHBhaXJzCmNpc0Rpc3QgPSAxZTYKIyMgTG9hZCBnZW5vdHlwZSBkYXRhCnNucHMgPSBTbGljZWREYXRhJG5ldygpCnNucHMkZmlsZURlbGltaXRlciA9ICJcdCIgICAgICAjIHRoZSBUQUIgY2hhcmFjdGVyCnNucHMkZmlsZU9taXRDaGFyYWN0ZXJzID0gIk5BIiAjIGRlbm90ZSBtaXNzaW5nIHZhbHVlczsKc25wcyRmaWxlU2tpcFJvd3MgPSAxICAgICAgICAgICMgb25lIHJvdyBvZiBjb2x1bW4gbGFiZWxzCnNucHMkZmlsZVNraXBDb2x1bW5zID0gMSAgICAgICAjIG9uZSBjb2x1bW4gb2Ygcm93IGxhYmVscwpzbnBzJGZpbGVTbGljZVNpemUgPSAyMDAwICAgICAgIyByZWFkIGZpbGUgaW4gc2xpY2VzIG9mIDIsMDAwIHJvd3MKc25wcyRMb2FkRmlsZShTTlBfZmlsZV9uYW1lKQojIyBMb2FkIGdlbmUgZXhwcmVzc2lvbiBkYXRhCmdlbmUgPSBTbGljZWREYXRhJG5ldygpCmdlbmUkZmlsZURlbGltaXRlciA9ICJcdCIgICAgICAjIHRoZSBUQUIgY2hhcmFjdGVyCmdlbmUkZmlsZU9taXRDaGFyYWN0ZXJzID0gIk5BIiAjIGRlbm90ZSBtaXNzaW5nIHZhbHVlczsKZ2VuZSRmaWxlU2tpcFJvd3MgPSAxICAgICAgICAgICMgb25lIHJvdyBvZiBjb2x1bW4gbGFiZWxzCmdlbmUkZmlsZVNraXBDb2x1bW5zID0gMSAgICAgICAjIG9uZSBjb2x1bW4gb2Ygcm93IGxhYmVscwpnZW5lJGZpbGVTbGljZVNpemUgPSAyMDAwICAgICAgIyByZWFkIGZpbGUgaW4gc2xpY2VzIG9mIDIsMDAwIHJvd3MKZ2VuZSRMb2FkRmlsZShleHByZXNzaW9uX2ZpbGVfbmFtZSkKIyMgTG9hZCBjb3ZhcmlhdGVzCmN2cnQgPSBTbGljZWREYXRhJG5ldygpCmN2cnQkZmlsZURlbGltaXRlciA9ICJcdCIgICAgICAjIHRoZSBUQUIgY2hhcmFjdGVyCmN2cnQkZmlsZU9taXRDaGFyYWN0ZXJzID0gIk5BIiAjIGRlbm90ZSBtaXNzaW5nIHZhbHVlczsKY3ZydCRmaWxlU2tpcFJvd3MgPSAxICAgICAgICAgICMgb25lIHJvdyBvZiBjb2x1bW4gbGFiZWxzCmN2cnQkZmlsZVNraXBDb2x1bW5zID0gMSAgICAgICAjIG9uZSBjb2x1bW4gb2Ygcm93IGxhYmVscwppZihsZW5ndGgoY292YXJpYXRlc19maWxlX25hbWUpID4gMCkgewogIGN2cnQkTG9hZEZpbGUoY292YXJpYXRlc19maWxlX25hbWUpCn0KIyMgUnVuIHRoZSBhbmFseXNpcwpzbnBzcG9zID0gcmVhZC50YWJsZShzbnBzX2xvY2F0aW9uX2ZpbGVfbmFtZSwgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpnZW5lcG9zID0gcmVhZC50YWJsZShnZW5lX2xvY2F0aW9uX2ZpbGVfbmFtZSwgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKbWUgPSBNYXRyaXhfZVFUTF9tYWluKAogICAgc25wcyA9IHNucHMsCiAgICBnZW5lID0gZ2VuZSwKICAgIGN2cnQgPSBjdnJ0LAogICAgb3V0cHV0X2ZpbGVfbmFtZSA9IG91dHB1dF9maWxlX25hbWVfdHJhLAogICAgcHZPdXRwdXRUaHJlc2hvbGQgPSBwdk91dHB1dFRocmVzaG9sZF90cmEsCiAgICB1c2VNb2RlbCA9IHVzZU1vZGVsLAogICAgZXJyb3JDb3ZhcmlhbmNlID0gZXJyb3JDb3ZhcmlhbmNlLAogICAgdmVyYm9zZSA9IFRSVUUsCiAgICBvdXRwdXRfZmlsZV9uYW1lLmNpcyA9IG91dHB1dF9maWxlX25hbWVfY2lzLAogICAgcHZPdXRwdXRUaHJlc2hvbGQuY2lzID0gcHZPdXRwdXRUaHJlc2hvbGRfY2lzLAogICAgc25wc3BvcyA9IHNucHNwb3MsCiAgICBnZW5lcG9zID0gZ2VuZXBvcywKICAgIGNpc0Rpc3QgPSBjaXNEaXN0LAogICAgcHZhbHVlLmhpc3QgPSAicXFwbG90IiwKICAgIG1pbi5wdi5ieS5nZW5lc25wID0gRkFMU0UsCiAgICBub0ZEUnNhdmVNZW1vcnkgPSBGQUxTRSk7CmBgYAoKCgpgYGB7ciBzYXZlbWV9CmlmICghaXNUUlVFKGdldDAoInNraXBfbG9hZCIpKSkgewogIHBhbmRlcjo6cGFuZGVyKHNlc3Npb25JbmZvKCkpCiAgbWVzc2FnZShwYXN0ZTAoIlRoaXMgaXMgaHBnbHRvb2xzIGNvbW1pdDogIiwgZ2V0X2dpdF9jb21taXQoKSkpCiAgbWVzc2FnZShwYXN0ZTAoIlNhdmluZyB0byAiLCBzYXZlZmlsZSkpCiAgdG1wIDwtIHNtKHNhdmVtZShmaWxlbmFtZSA9IHNhdmVmaWxlKSkKfQpgYGAKCmBgYHtyIGxvYWRtZV9hZnRlciwgZXZhbCA9IEZBTFNFfQp0bXAgPC0gbG9hZG1lKGZpbGVuYW1lID0gc2F2ZWZpbGUpCmBgYAo=