1 Introduction

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))
tt <- sm(library(org.Lpanamensis.MHOMCOL81L13.v46.eg.db))
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
## rownames(hisat_annot) <- paste0("exon_", rownames(hisat_annot), ".E1")

3 TODO:

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

4 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.

4.1 Notes

The following samples are much lower coverage:

  • TMRC20002
  • TMRC20006
  • TMRC20007
  • TMRC20008

4.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.
sample_sheet <- glue::glue("sample_sheets/tmrc2_samples_20210528.xlsx")

lp_expt <- sm(create_expt(sample_sheet,
                          gene_info = hisat_annot,
                          id_column = "hpglidentifier",
                          file_column = "lpanamensisv36hisatfile")) %>%
  set_expt_conditions(fact = "zymodemecategorical") %>%
  subset_expt(nonzero = 8600) %>%
  semantic_expt_filter(semantic = c("amastin", "gp63", "leishmanolysin"),
                       semantic_column = "annot_gene_product")
## The samples (and read coverage) removed when filtering 8600 non-zero genes are:
## TMRC20002 TMRC20004 TMRC20006 TMRC20029 TMRC20008 
##  11681227    564812   6670348   1658096   6249790
## subset_expt(): There were 48, now there are 43 samples.
## semantic_expt_filter(): Removed 68 genes.
libsizes <- plot_libsize(lp_expt)
libsizes$plot

## I think samples 7,10 should be removed at minimum, probably also 9,11
nonzero <- plot_nonzero(lp_expt)
nonzero$plot
## Warning: ggrepel: 2 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

plot_boxplot(lp_expt)
## 2826 entries are 0.  We are on a log scale, adding 1 to the data.

filter_plot <- plot_libsize_prepost(lp_expt)
filter_plot$lowgene_plot
## Warning: Using alpha for a discrete variable is not advised.

filter_plot$count_plot

4.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’.

4.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"]])
sus_categorical <- starting
na_idx <- is.na(starting)
sus_categorical[na_idx] <- "unknown"

resist_idx <- starting <= 0.35
sus_categorical[resist_idx] <- "resistant"
indeterminant_idx <- starting >= 0.36 & starting <= 0.48
sus_categorical[indeterminant_idx] <- "ambiguous"
susceptible_idx <- starting >= 0.49
sus_categorical[susceptible_idx] <- "sensitive"

pData(lp_expt$expressionset)[["sus_category"]] <- sus_categorical
clinical_samples <- lp_expt %>%
  set_expt_batches(fact = sus_categorical)

clinical_norm <- sm(normalize_expt(clinical_samples, norm = "quant", transform = "log2",
                                   convert = "cpm", batch = FALSE, filter = TRUE))
zymo_pca <- plot_pca(clinical_norm, plot_title = "PCA of parasite expression values")
pp(file = "images/zymo_pca_sus_shape.png", image = zymo_pca$plot)

zymo_3dpca <- plot_3d_pca(zymo_pca)
zymo_3dpca$plot
zymo_tsne <- plot_tsne(clinical_norm, plot_title = "TSNE of parasite expression values")
zymo_tsne$plot

clinical_nb <- normalize_expt(clinical_samples, convert = "cpm", transform = "log2",
                         filter = TRUE, batch = "svaseq")
## Removing 146 low-count genes (8564 remaining).
## batch_counts: Before batch/surrogate estimation, 507 entries are x==0: 0%.
## batch_counts: Before batch/surrogate estimation, 1893 entries are 0<x<1: 1%.
## Setting 129 low elements to zero.
## transform_counts: Found 129 values equal to 0, adding 1 to the matrix.
clinical_nb_pca <- plot_pca(clinical_nb, plot_title = "PCA of parasite expression values")
pp(file = "images/clinical_nb_pca_sus_shape.png", image = clinical_nb_pca$plot)

clinical_nb_tsne <- plot_tsne(clinical_nb, plot_title = "TSNE of parasite expression values")
clinical_nb_tsne$plot
## Warning in MASS::cov.trob(data[, vars]): Probable convergence failure

## Warning in MASS::cov.trob(data[, vars]): Probable convergence failure
## Warning: ggrepel: 38 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

corheat <- plot_corheat(clinical_norm, plot_title = "Correlation heatmap of parasite
                 expression values
")
corheat$plot

plot_sm(clinical_norm)$plot
## Performing correlation.

4.5 By Cure/Fail status

cf_expt <- set_expt_conditions(lp_expt, fact = "clinicalcategorical") %>%
  set_expt_batches(fact = sus_categorical)

cf_norm <- normalize_expt(cf_expt, convert = "cpm", transform = "log2",
                          norm = "quant", filter = TRUE)
## Removing 146 low-count genes (8564 remaining).
## transform_counts: Found 2 values equal to 0, adding 1 to the matrix.
start_cf <- plot_pca(cf_norm, plot_title = "PCA of parasite expression values")
pp(file = "images/cf_sus_shape.png", image = start_cf$plot)

cf_nb <- normalize_expt(cf_expt, convert = "cpm", transform = "log2",
                        norm = "quant", filter = TRUE, batch = "svaseq")
## Warning in normalize_expt(cf_expt, convert = "cpm", transform = "log2", :
## Quantile normalization and sva do not always play well together.
## Removing 146 low-count genes (8564 remaining).
## batch_counts: Before batch/surrogate estimation, 2 entries are x==0: 0%.
## batch_counts: Before batch/surrogate estimation, 2294 entries are 0<x<1: 1%.
## Setting 94 low elements to zero.
## transform_counts: Found 94 values equal to 0, adding 1 to the matrix.
cf_nb_pca <- plot_pca(cf_nb, plot_title = "PCA of parasite expression values")
pp(file = "images/cf_sus_share_nb.png", image = cf_nb_pca$plot)

cf_norm <- normalize_expt(cf_expt, transform = "log2", convert = "cpm",
                          filter = TRUE, norm = "quant")
## Removing 146 low-count genes (8564 remaining).
## transform_counts: Found 2 values equal to 0, adding 1 to the matrix.
test <- pca_information(cf_norm,
                        expt_factors = c("clinicalcategorical", "zymodemecategorical",
                                         "pathogenstrain", "passagenumber"),
                        num_components = 6, plot_pcas = TRUE)
test$anova_p
##                           PC1     PC2       PC3    PC4    PC5    PC6
## clinicalcategorical 0.000e+00 0.00000 0.000e+00 0.0000 0.0000 0.0000
## zymodemecategorical 2.065e-06 0.11542 3.763e-01 0.2677 0.3496 0.7188
## pathogenstrain      9.790e-01 0.08496 5.768e-06 0.4458 0.5482 0.9486
## passagenumber       0.000e+00 0.00000 0.000e+00 0.0000 0.0000 0.0000
test$cor_heatmap

sus_expt <- set_expt_conditions(lp_expt, fact = "sus_category") %>%
  set_expt_batches(fact = "zymodemecategorical")
sus_norm <- normalize_expt(sus_expt, transform = "log2", convert = "cpm",
                           norm = "quant", filter = TRUE)
## Removing 146 low-count genes (8564 remaining).
## transform_counts: Found 2 values equal to 0, adding 1 to the matrix.
sus_pca <- plot_pca(sus_norm, plot_title = "PCA of parasite expression values")
sus_pca$plot

sus_nb <- normalize_expt(sus_expt, transform = "log2", convert = "cpm",
                         batch = "svaseq", filter = TRUE)
## Removing 146 low-count genes (8564 remaining).
## batch_counts: Before batch/surrogate estimation, 507 entries are x==0: 0%.
## batch_counts: Before batch/surrogate estimation, 1893 entries are 0<x<1: 1%.
## Setting 109 low elements to zero.
## transform_counts: Found 109 values equal to 0, adding 1 to the matrix.
sus_nb_pca <- plot_pca(sus_nb, plot_title = "PCA of parasite expression values")
pp(file = "images/sus_nb_pca.png", image = sus_nb_pca$plot)

At this time, we do not have very many samples, so the set of metrics/plots is fairly limited. There is really only one factor in the metadata which we can use for performing differential expression analyses, the ‘zymodeme’.

5 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.

5.1 Differential expression

5.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'")
## subset_expt(): There were 43, now there are 23 samples.
zy_norm <- normalize_expt(zy_expt, filter = TRUE, convert = "cpm", norm = "quant")
## Removing 167 low-count genes (8543 remaining).
zy_de_nobatch <- sm(all_pairwise(zy_expt, filter = TRUE, model_batch = "svaseq"))
zy_de <- sm(all_pairwise(zy_expt, filter = TRUE, model_batch = "svaseq"))
zy_table <- sm(combine_de_tables(zy_de, excel = glue::glue("excel/zy_tables-v{ver}.xlsx")))
zy_sig <- sm(extract_significant_genes(zy_table, excel = glue::glue("excel/zy_sig-v{ver}.xlsx")))

5.1.2 Images of zymodeme DE

zy_table[["plots"]][["z23_vs_z22"]][["deseq_ma_plots"]][["plot"]]

5.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 <- sm(all_pairwise(cf_expt, filter = TRUE, model_batch = "svaseq"))
cf_table <- sm(combine_de_tables(cf_de, excel = glue::glue("excel/cf_tables-v{ver}.xlsx")))
cf_sig <- sm(extract_significant_genes(cf_table, excel = glue::glue("excel/cf_sig-v{ver}.xlsx")))

5.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 <- sm(all_pairwise(sus_expt, filter = TRUE, model_batch = "svaseq"))
sus_table <- sm(combine_de_tables(sus_de, excel = glue::glue("excel/sus_tables-v{ver}.xlsx")))
sus_sig <- sm(extract_significant_genes(sus_table, excel = glue::glue("excel/sus_sig-v{ver}.xlsx")))

5.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 <- sm(simple_goseq(sig_genes = zy_sig[["deseq"]][["ups"]][[1]],
                            go_db = lp_go, length_db = lp_lengths))

## Gene categories more represented in the 2.2 group.
zy_go_down <- sm(simple_goseq(sig_genes = zy_sig[["deseq"]][["downs"]][[1]],
                              go_db = lp_go, length_db = lp_lengths))

5.4.1 A couple plots from the differential expression

5.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[["venns"]][[1]][["p_lfc1"]][["up_noweight"]]

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

zy_table[["venns"]][[1]][["p_lfc1"]][["down_noweight"]]

5.4.1.3 MA plot of the differential expression between the zymodemes.

zy_table$plots[[1]][["deseq_ma_plots"]][["plot"]]

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

zy_go_up$pvalue_plots$bpp_plot_over

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

zy_go_down$pvalue_plots$bpp_plot_over

5.5 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.

5.5.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")
## Before removal, there were 8543 genes, now there are 6.
## There are 23 samples which kept less than 90 percent counts.
## TMRC20001 TMRC20005 TMRC20039 TMRC20037 TMRC20038 TMRC20041 TMRC20015 TMRC20009 
##    0.1307    0.1315    0.1296    0.1098    0.1126    0.1176    0.1144    0.1133 
## TMRC20010 TMRC20016 TMRC20011 TMRC20012 TMRC20013 TMRC20017 TMRC20014 TMRC20018 
##    0.1096    0.1058    0.1099    0.1203    0.1202    0.1062    0.1087    0.1142 
## TMRC20021 TMRC20022 TMRC20053 TMRC20052 TMRC20051 TMRC20050 TMRC20054 
##    0.1059    0.1302    0.1179    0.1102    0.1277    0.1149    0.1273
zymo_heatmap <- plot_sample_heatmap(zymo_expt, row_label = my_names)
zymo_heatmap

5.6 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)
## Deleting the file excel/intersect_significant.xlsx before writing the tables.
up_shared <- shared_zymo[["ups"]][[1]][["data"]][["all"]]
rownames(up_shared)
##  [1] "LPAL13_000033300" "LPAL13_000012000" "LPAL13_310031300" "LPAL13_000038400"
##  [5] "LPAL13_000038500" "LPAL13_000012100" "LPAL13_340039600" "LPAL13_050005000"
##  [9] "LPAL13_310031000" "LPAL13_310039200" "LPAL13_210015500" "LPAL13_350063000"
## [13] "LPAL13_270034100" "LPAL13_140019300" "LPAL13_340039700" "LPAL13_350013200"
## [17] "LPAL13_180013900" "LPAL13_170015400" "LPAL13_330021800" "LPAL13_240009700"
## [21] "LPAL13_140019100" "LPAL13_330021900" "LPAL13_140019200" "LPAL13_250025700"
## [25] "LPAL13_320038700" "LPAL13_350073200" "LPAL13_310028500" "LPAL13_210005000"
## [29] "LPAL13_230011200" "LPAL13_300031600" "LPAL13_230011400" "LPAL13_110015700"
## [33] "LPAL13_040007800" "LPAL13_290016200" "LPAL13_230011500" "LPAL13_310032500"
## [37] "LPAL13_000045100" "LPAL13_160014500" "LPAL13_000010600"
upshared_expt <- exclude_genes_expt(zy_norm, ids = rownames(up_shared), method = "keep")
## Before removal, there were 8543 genes, now there are 39.
## There are 23 samples which kept less than 90 percent counts.
## TMRC20001 TMRC20005 TMRC20039 TMRC20037 TMRC20038 TMRC20041 TMRC20015 TMRC20009 
##    0.4148    0.1539    0.2236    0.6113    0.6912    0.1913    0.5184    0.1865 
## TMRC20010 TMRC20016 TMRC20011 TMRC20012 TMRC20013 TMRC20017 TMRC20014 TMRC20018 
##    0.4754    0.3863    0.1949    0.1527    0.4594    0.2529    0.2052    0.4372 
## TMRC20021 TMRC20022 TMRC20053 TMRC20052 TMRC20051 TMRC20050 TMRC20054 
##    0.4913    0.1731    0.2528    0.5766    0.7879    0.2603    0.6963

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

5.6.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))
high_23_heatmap

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

down_shared <- shared_zymo[["downs"]][[1]][["data"]][["all"]]
downshared_expt <- exclude_genes_expt(zy_norm, ids = rownames(down_shared), method = "keep")
## Before removal, there were 8543 genes, now there are 61.
## There are 23 samples which kept less than 90 percent counts.
## TMRC20001 TMRC20005 TMRC20039 TMRC20037 TMRC20038 TMRC20041 TMRC20015 TMRC20009 
##    0.2175    0.6777    0.6555    0.1982    0.1892    0.6830    0.1799    0.6311 
## TMRC20010 TMRC20016 TMRC20011 TMRC20012 TMRC20013 TMRC20017 TMRC20014 TMRC20018 
##    0.1650    0.2055    0.5672    0.5549    0.1627    0.6520    0.6407    0.1590 
## TMRC20021 TMRC20022 TMRC20053 TMRC20052 TMRC20051 TMRC20050 TMRC20054 
##    0.1592    0.6787    0.5680    0.1789    0.1838    0.6124    0.1955
high_22_heatmap <- plot_sample_heatmap(downshared_expt, row_label = rownames(down_shared))
high_22_heatmap

6 SNP profiles

Now I will combine our previous samples and our new samples in the hopes of finding variant positions which help elucidate currently unknown aspects of either group via their clustering to known samples from the other group. In other words, we do not know the zymodeme annotations for the old samples nor the strain identities (or the shortcut ‘chronic vs. self-healing’) for the new samples. I hope to make educated guesses given the variant profiles. There are some differences in how the previous and current data sets were analyzed (though I have since redone the old samples so it should be trivial to remove those differences now).

I added our 2016 data to a specific TMRC2 sample sheet, dated 20191203. Thus I will load the data here. That previous data was mapped using tophat, so I will also need to make some changes to the gene names to accomodate the two mappings.

old_expt <- sm(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

6.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)[['bcftable']])")
## subset_expt(): There were 43, now there are 43 samples.
new_snps <- sm(count_expt_snps(lp_snp, annot_column = "bcftable"))
old_snps <- sm(count_expt_snps(old_expt, annot_column = "bcftable", snp_column = 2))

both_snps <- combine_expts(new_snps, old_snps)
both_norm <- sm(normalize_expt(both_snps, transform = "log2", convert = "cpm", filter = TRUE))

## strains <- both_norm[["design"]][["strain"]]
both_norm <- 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.

6.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).

old_new_variant_heatmap <- plot_disheat(both_norm)
pp(file = "images/raw_snp_disheat.png", image = old_new_variant_heatmap,
   height = 12, width = 12)

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")
## The factor z2.3 has 12 rows.
## The factor z2.2 has 11 rows.
## The factor unknown has 20 rows.
## The factor sh has 13 rows.
## The factor chr has 14 rows.
## The factor inf has 6 rows.
## Iterating over 727 elements.
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)))

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

6.3 SNPS associated with clinical response in the TMRC samples

clinical_sets <- get_snp_sets(new_snps, factor = "clinicalresponse")
## The factor Cure has 17 rows.
## The factor Failure has 13 rows.
## The factor Laboratory line has only 1 row.
## The factor ND has 3 rows.
## The factor Reference strain has 4 rows.
## The factor unknown has 5 rows.
## Iterating over 693 elements.
density_vec <- clinical_sets[["density"]]
chromosome_idx <- grep(pattern = "LpaL", x = names(density_vec))
density_df <- as.data.frame(density_vec[chromosome_idx])
density_df[["chr"]] <- rownames(density_df)
colnames(density_df) <- c("density_vec", "chr")
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))

## clinical_written <- write_variants(new_snps)

6.3.1 Cross reference these variants by gene

clinical_genes <- sm(snps_vs_genes(lp_expt, clinical_sets, expt_name_col = "chromosome"))

snp_density <- merge(as.data.frame(clinical_genes[["summary_by_gene"]]),
                     as.data.frame(fData(lp_expt)),
                     by = "row.names")
snp_density <- snp_density[, c(1, 2, 4, 15)]
colnames(snp_density) <- c("name", "snps", "product", "length")
snp_density[["product"]] <- tolower(snp_density[["product"]])
snp_density[["length"]] <- as.numeric(snp_density[["length"]])
snp_density[["density"]] <- snp_density[["snps"]] / snp_density[["length"]]
snp_idx <- order(snp_density[["density"]], decreasing = TRUE)
snp_density <- snp_density[snp_idx, ]

removers <- c("amastin", "gp63", "leishmanolysin")
for (r in removers) {
  drop_idx <- grepl(pattern = r, x = snp_density[["product"]])
  snp_density <- snp_density[!drop_idx, ]
}
## Filter these for [A|a]mastin gp63 Leishmanolysin
clinical_snps <- snps_intersections(lp_expt, clinical_sets, chr_column = "chromosome")

head(as.data.frame(clinical_snps$inters[["Failure"]]))
##                                           seqnames  start    end width strand
## chr_LpaL13-02_pos_91233_ref_G_alt_T      LpaL13-02  91233  91234     2      +
## chr_LpaL13-20.1_pos_344505_ref_T_alt_C LpaL13-20.1 344505 344506     2      +
## chr_LpaL13-29_pos_484124_ref_G_alt_A     LpaL13-29 484124 484125     2      +
head(as.data.frame(clinical_snps$inters[["Cure"]]))
##                                           seqnames  start    end width strand
## chr_LpaL13-08_pos_184791_ref_T_alt_A     LpaL13-08 184791 184792     2      +
## chr_LpaL13-10_pos_347757_ref_A_alt_C     LpaL13-10 347757 347758     2      +
## chr_LpaL13-11_pos_433123_ref_C_alt_T     LpaL13-11 433123 433124     2      +
## chr_LpaL13-15_pos_47170_ref_G_alt_C      LpaL13-15  47170  47171     2      +
## chr_LpaL13-20.1_pos_106634_ref_G_alt_A LpaL13-20.1 106634 106635     2      +
## chr_LpaL13-20.1_pos_369935_ref_C_alt_T LpaL13-20.1 369935 369936     2      +
head(clinical_snps$gene_summaries$Failure)
## LPAL13_020007000 LPAL13_200014300 LPAL13_290018100 LPAL13_000005000 
##                1                1                1                0 
## LPAL13_000005400 LPAL13_000005500 
##                0                0
head(clinical_snps$gene_summaries$Cure, n = 100)
## LPAL13_200017900 LPAL13_200014600 LPAL13_230015000 LPAL13_200015100 
##                4                3                3                2 
## LPAL13_200017600 LPAL13_200017800 LPAL13_200019500 LPAL13_200019600 
##                2                2                2                2 
## LPAL13_080009800 LPAL13_100014700 LPAL13_110015500 LPAL13_150006300 
##                1                1                1                1 
## LPAL13_200008300 LPAL13_200014900 LPAL13_200015000 LPAL13_200015200 
##                1                1                1                1 
## LPAL13_200015300 LPAL13_200016400 LPAL13_200016500 LPAL13_200016900 
##                1                1                1                1 
## LPAL13_200017200 LPAL13_310008900 LPAL13_310034900 LPAL13_330014300 
##                1                1                1                1 
## LPAL13_000005000 LPAL13_000005400 LPAL13_000005500 LPAL13_000005600 
##                0                0                0                0 
## LPAL13_000005700 LPAL13_000005800 LPAL13_000005900 LPAL13_000006000 
##                0                0                0                0 
## LPAL13_000006100 LPAL13_000006200 LPAL13_000006300 LPAL13_000006400 
##                0                0                0                0 
## LPAL13_000006500 LPAL13_000006600 LPAL13_000006700 LPAL13_000006800 
##                0                0                0                0 
## LPAL13_000006900 LPAL13_000007400 LPAL13_000007500 LPAL13_000007600 
##                0                0                0                0 
## LPAL13_000007700 LPAL13_000007800 LPAL13_000007900 LPAL13_000008000 
##                0                0                0                0 
## LPAL13_000008300 LPAL13_000008400 LPAL13_000008500 LPAL13_000008600 
##                0                0                0                0 
## LPAL13_000008700 LPAL13_000008800 LPAL13_000008900 LPAL13_000009000 
##                0                0                0                0 
## LPAL13_000009100 LPAL13_000009200 LPAL13_000009300 LPAL13_000009400 
##                0                0                0                0 
## LPAL13_000009500 LPAL13_000009600 LPAL13_000009700 LPAL13_000009800 
##                0                0                0                0 
## LPAL13_000009900 LPAL13_000010000 LPAL13_000010100 LPAL13_000010200 
##                0                0                0                0 
## LPAL13_000010300 LPAL13_000010400 LPAL13_000010500 LPAL13_000010600 
##                0                0                0                0 
## LPAL13_000010700 LPAL13_000010800 LPAL13_000010900 LPAL13_000011000 
##                0                0                0                0 
## LPAL13_000011100 LPAL13_000011200 LPAL13_000011300 LPAL13_000011400 
##                0                0                0                0 
## LPAL13_000011500 LPAL13_000011600 LPAL13_000011700 LPAL13_000011800 
##                0                0                0                0 
## LPAL13_000011900 LPAL13_000012000 LPAL13_000012100 LPAL13_000012200 
##                0                0                0                0 
## LPAL13_000012300 LPAL13_000012400 LPAL13_000012500 LPAL13_000012600 
##                0                0                0                0 
## LPAL13_000012700 LPAL13_000012800 LPAL13_000012900 LPAL13_000013000 
##                0                0                0                0 
## LPAL13_000013100 LPAL13_000013200 LPAL13_000013300 LPAL13_000013400 
##                0                0                0                0
annot <- fData(lp_expt)
clinical_interest <- as.data.frame(clinical_snps[["gene_summaries"]][["Cure"]])
clinical_interest <- merge(clinical_interest, as.data.frame(clinical_snps[["gene_summaries"]][["Failure"]]), by = "row.names")
rownames(clinical_interest) <- clinical_interest[["Row.names"]]
clinical_interest[["Row.names"]] <- NULL
colnames(clinical_interest) <- c("cure_snps","fail_snps")
annot <- merge(annot, clinical_interest, by = "row.names")
rownames(annot) <- annot[["Row.names"]]
annot[["Row.names"]] <- NULL
fData(lp_expt$expressionset) <- annot

7 Zymodeme for new samples

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

7.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.

new_sets <- get_snp_sets(new_snps, factor = "phenotypiccharacteristics")
## The factor 2.2 has 11 rows.
## The factor 2.3 has 12 rows.
## The factor Laboratory line has only 1 row.
## The factor Reference strain has 4 rows.
## The factor unknown has 15 rows.
## Iterating over 693 elements.
summary(new_sets)
##               Length Class      Mode     
## medians         6    data.frame list     
## possibilities   5    -none-     character
## intersections  31    -none-     list     
## chr_data      693    -none-     list     
## set_names      32    -none-     list     
## invert_names   32    -none-     list     
## density       693    -none-     numeric
## 1000000: 2.2
## 0100000: 2.3

summary(new_sets[["intersections"]][["100000"]])
## Length  Class   Mode 
##      0   NULL   NULL
dim(new_sets$intersections[["100000"]])
## NULL
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
  }

  sequentials <- position_table[["dist"]] <= maximum_separation

  ## 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]
  }

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

zymo22_sequentials <- sequential_variants(new_sets, conditions = "2.2")
zymo22_sequentials
##                                            chr    pos
## chr_LpaL13-05_pos_260512_ref_G_alt_C LpaL13-05 260512
## chr_LpaL13-24_pos_163302_ref_A_alt_C LpaL13-24 163302
zymo23_sequentials <- sequential_variants(new_sets, conditions = "2.3")
zymo23_sequentials
##                                            chr    pos
## chr_LpaL13-05_pos_183858_ref_G_alt_A LpaL13-05 183858
## chr_LpaL13-08_pos_174502_ref_T_alt_G LpaL13-08 174502
## chr_LpaL13-09_pos_210577_ref_G_alt_C LpaL13-09 210577
## chr_LpaL13-09_pos_338720_ref_C_alt_G LpaL13-09 338720
## chr_LpaL13-09_pos_375148_ref_C_alt_T LpaL13-09 375148
## chr_LpaL13-11_pos_478993_ref_T_alt_G LpaL13-11 478993
## chr_LpaL13-11_pos_489159_ref_G_alt_A LpaL13-11 489159
## chr_LpaL13-14_pos_221315_ref_A_alt_G LpaL13-14 221315
## chr_LpaL13-28_pos_592641_ref_A_alt_C LpaL13-28 592641
## chr_LpaL13-31_pos_98759_ref_G_alt_T  LpaL13-31  98759
## chr_LpaL13-32_pos_314579_ref_C_alt_A LpaL13-32 314579
## chr_LpaL13-35_pos_26430_ref_G_alt_A  LpaL13-35  26430
snp_genes <- sm(snps_vs_genes(lp_expt, new_sets, expt_name_col = "chromosome"))
new_zymo_norm  <- normalize_expt(new_snps, filter = TRUE, convert = "cpm", norm = "quant", transform = TRUE)
## Removing 0 low-count genes (544782 remaining).
## transform_counts: Found 7670178 values equal to 0, adding 1 to the matrix.
new_zymo_norm <- set_expt_conditions(new_zymo_norm, fact = "phenotypiccharacteristics")
zymo_heat <- plot_disheat(new_zymo_norm)

zymo_subset <- snp_subset_genes(lp_expt, new_snps,
                                genes = c("LPAL13_120010900", "LPAL13_340013000", "LPAL13_000054100",
                                        "LPAL13_140006100", "LPAL13_180018500", "LPAL13_320022300"))
## Warning in .Seqinfo.mergexy(x, y): Each of the 2 combined objects has sequence levels not in the other:
##   - in 'x': LPAL13-SCAF000002, LPAL13-SCAF000003, LPAL13-SCAF000004, LPAL13-SCAF000005, LPAL13-SCAF000009, LPAL13-SCAF000010, LPAL13-SCAF000013, LPAL13-SCAF000014, LPAL13-SCAF000015, LPAL13-SCAF000018, LPAL13-SCAF000019, LPAL13-SCAF000020, LPAL13-SCAF000022, LPAL13-SCAF000023, LPAL13-SCAF000026, LPAL13-SCAF000029, LPAL13-SCAF000030, LPAL13-SCAF000031, LPAL13-SCAF000032, LPAL13-SCAF000035, LPAL13-SCAF000036, LPAL13-SCAF000037, LPAL13-SCAF000038, LPAL13-SCAF000042, LPAL13-SCAF000043, LPAL13-SCAF000045, LPAL13-SCAF000047, LPAL13-SCAF000049, LPAL13-SCAF000050, LPAL13-SCAF000052, LPAL13-SCAF000054, LPAL13-SCAF000056, LPAL13-SCAF000057, LPAL13-SCAF000058, LPAL13-SCAF000060, LPAL13-SCAF000066, LPAL13-SCAF000067, LPAL13-SCAF000069, LPAL13-SCAF000070, LPAL13-SCAF000072, LPAL13-SCAF000073, LPAL13-SCAF000081, LPAL13-SCAF000082, LPAL13-SCAF000083, LPAL13-SCAF000085, LPAL13-SCAF000086, LPAL13-SCAF000088, LPAL13-SCAF000090, LPAL13-SCAF000091, LPAL13-SCAF000092, LPAL13-SCAF000095, LPAL13-SCAF000098, LPAL13-SCAF000101, LPAL13-SCAF000103, LPAL13-SCAF000106, LPAL13-SCAF000109, LPAL13-SCAF000111, LPAL13-SCAF000112, LPAL13-SCAF000113, LPAL13-SCAF000118, LPAL13-SCAF000125, LPAL13-SCAF000126, LPAL13-SCAF000128, LPAL13-SCAF000138, LPAL13-SCAF000139, LPAL13-SCAF000140, LPAL13-SCAF000141, LPAL13-SCAF000144, LPAL13-SCAF000145, LPAL13-SCAF000147, LPAL13-SCAF000148, LPAL13-SCAF000150, LPAL13-SCAF000151, LPAL13-SCAF000152, LPAL13-SCAF000154, LPAL13-SCAF000155, LPAL13-SCAF000156, LPAL13-SCAF000157, LPAL13-SCAF000158, LPAL13-SCAF000159, LPAL13-SCAF000160, LPAL13-SCAF000161, LPAL13-SCAF000163, LPAL13-SCAF000164, LPAL13-SCAF000167, LPAL13-SCAF000168, LPAL13-SCAF000169, LPAL13-SCAF000170, LPAL13-SCAF000175, LPAL13-SCAF000177, LPAL13-SCAF000178, LPAL13-SCAF000179, LPAL13-SCAF000180, LPAL13-SCAF000183, LPAL13-SCAF000184, LPAL13-SCAF000185, LPAL13-SCAF000189, LPAL13-SCAF000190, LPAL13-SCAF000192, LPAL13-SCAF000195, LPAL13-SCAF000196, LPAL13-SCAF000198, LPAL13-SCAF000199, LPAL13-SCAF000204, LPAL13-SCAF000207, LPAL13-SCAF000208, LPAL13-SCAF000210, LPAL13-SCAF000212, LPAL13-SCAF000213, LPAL13-SCAF000214, LPAL13-SCAF000215, LPAL13-SCAF000216, LPAL13-SCAF000218, LPAL13-SCAF000219, LPAL13-SCAF000221, LPAL13-SCAF000222, LPAL13-SCAF000223, LPAL13-SCAF000224, LPAL13-SCAF000225, LPAL13-SCAF000226, LPAL13-SCAF000228, LPAL13-SCAF000232, LPAL13-SCAF000234, LPAL13-SCAF000236, LPAL13-SCAF000238, LPAL13-SCAF000240, LPAL13-SCAF000241, LPAL13-SCAF000242, LPAL13-SCAF000243, LPAL13-SCAF000244, LPAL13-SCAF000246, LPAL13-SCAF000247, LPAL13-SCAF000249, LPAL13-SCAF000251, LPAL13-SCAF000252, LPAL13-SCAF000254, LPAL13-SCAF000255, LPAL13-SCAF000257, LPAL13-SCAF000258, LPAL13-SCAF000260, LPAL13-SCAF000262, LPAL13-SCAF000263, LPAL13-SCAF000264, LPAL13-SCAF000268, LPAL13-SCAF000269, LPAL13-SCAF000270, LPAL13-SCAF000272, LPAL13-SCAF000273, LPAL13-SCAF000274, LPAL13-SCAF000275, LPAL13-SCAF000276, LPAL13-SCAF000277, LPAL13-SCAF000278, LPAL13-SCAF000279, LPAL13-SCAF000280, LPAL13-SCAF000282, LPAL13-SCAF000283, LPAL13-SCAF000284, LPAL13-SCAF000289, LPAL13-SCAF000290, LPAL13-SCAF000293, LPAL13-SCAF000294, LPAL13-SCAF000297, LPAL13-SCAF000298, LPAL13-SCAF000299, LPAL13-SCAF000304, LPAL13-SCAF000305, LPAL13-SCAF000306, LPAL13-SCAF000307, LPAL13-SCAF000308, LPAL13-SCAF000310, LPAL13-SCAF000311, LPAL13-SCAF000312, LPAL13-SCAF000313, LPAL13-SCAF000315, LPAL13-SCAF000318, LPAL13-SCAF000323, LPAL13-SCAF000324, LPAL13-SCAF000325, LPAL13-SCAF000327, LPAL13-SCAF000329, LPAL13-SCAF000331, LPAL13-SCAF000332, LPAL13-SCAF000333, LPAL13-SCAF000334, LPAL13-SCAF000336, LPAL13-SCAF000341, LPAL13-SCAF000342, LPAL13-SCAF000343, LPAL13-SCAF000344, LPAL13-SCAF000345, LPAL13-SCAF000346, LPAL13-SCAF000348, LPAL13-SCAF000349, LPAL13-SCAF000350, LPAL13-SCAF000351, LPAL13-SCAF000352, LPAL13-SCAF000353, LPAL13-SCAF000354, LPAL13-SCAF000355, LPAL13-SCAF000356, LPAL13-SCAF000357, LPAL13-SCAF000359, LPAL13-SCAF000360, LPAL13-SCAF000361, LPAL13-SCAF000362, LPAL13-SCAF000365, LPAL13-SCAF000366, LPAL13-SCAF000369, LPAL13-SCAF000371, LPAL13-SCAF000372, LPAL13-SCAF000373, LPAL13-SCAF000375, LPAL13-SCAF000376, LPAL13-SCAF000377, LPAL13-SCAF000378, LPAL13-SCAF000379, LPAL13-SCAF000380, LPAL13-SCAF000381, LPAL13-SCAF000382, LPAL13-SCAF000383, LPAL13-SCAF000384, LPAL13-SCAF000385, LPAL13-SCAF000386, LPAL13-SCAF000387, LPAL13-SCAF000388, LPAL13-SCAF000389, LPAL13-SCAF000390, LPAL13-SCAF000392, LPAL13-SCAF000393, LPAL13-SCAF000394, LPAL13-SCAF000395, LPAL13-SCAF000396, LPAL13-SCAF000397, LPAL13-SCAF000398, LPAL13-SCAF000399, LPAL13-SCAF000402, LPAL13-SCAF000404, LPAL13-SCAF000406, LPAL13-SCAF000407, LPAL13-SCAF000408, LPAL13-SCAF000409, LPAL13-SCAF000410, LPAL13-SCAF000411, LPAL13-SCAF000412, LPAL13-SCAF000413, LPAL13-SCAF000414, LPAL13-SCAF000415, LPAL13-SCAF000416, LPAL13-SCAF000418, LPAL13-SCAF000422, LPAL13-SCAF000423, LPAL13-SCAF000425, LPAL13-SCAF000427, LPAL13-SCAF000428, LPAL13-SCAF000429, LPAL13-SCAF000431, LPAL13-SCAF000433, LPAL13-SCAF000435, LPAL13-SCAF000437, LPAL13-SCAF000438, LPAL13-SCAF000439, LPAL13-SCAF000441, LPAL13-SCAF000442, LPAL13-SCAF000443, LPAL13-SCAF000444, LPAL13-SCAF000445, LPAL13-SCAF000449, LPAL13-SCAF000450, LPAL13-SCAF000451, LPAL13-SCAF000452, LPAL13-SCAF000454, LPAL13-SCAF000455, LPAL13-SCAF000457, LPAL13-SCAF000458, LPAL13-SCAF000462, LPAL13-SCAF000464, LPAL13-SCAF000466, LPAL13-SCAF000467, LPAL13-SCAF000472, LPAL13-SCAF000473, LPAL13-SCAF000474, LPAL13-SCAF000475, LPAL13-SCAF000476, LPAL13-SCAF000478, LPAL13-SCAF000479, LPAL13-SCAF000480, LPAL13-SCAF000481, LPAL13-SCAF000482, LPAL13-SCAF000485, LPAL13-SCAF000487, LPAL13-SCAF000489, LPAL13-SCAF000493, LPAL13-SCAF000494, LPAL13-SCAF000495, LPAL13-SCAF000497, LPAL13-SCAF000498, LPAL13-SCAF000499, LPAL13-SCAF000501, LPAL13-SCAF000502, LPAL13-SCAF000504, LPAL13-SCAF000506, LPAL13-SCAF000509, LPAL13-SCAF000510, LPAL13-SCAF000513, LPAL13-SCAF000514, LPAL13-SCAF000516, LPAL13-SCAF000517, LPAL13-SCAF000518, LPAL13-SCAF000519, LPAL13-SCAF000520, LPAL13-SCAF000521, LPAL13-SCAF000523, LPAL13-SCAF000524, LPAL13-SCAF000525, LPAL13-SCAF000526, LPAL13-SCAF000530, LPAL13-SCAF000531, LPAL13-SCAF000534, LPAL13-SCAF000543, LPAL13-SCAF000545, LPAL13-SCAF000546, LPAL13-SCAF000550, LPAL13-SCAF000551, LPAL13-SCAF000557, LPAL13-SCAF000559, LPAL13-SCAF000561, LPAL13-SCAF000565, LPAL13-SCAF000571, LPAL13-SCAF000579, LPAL13-SCAF000581, LPAL13-SCAF000583, LPAL13-SCAF000584, LPAL13-SCAF000589, LPAL13-SCAF000592, LPAL13-SCAF000594, LPAL13-SCAF000595, LPAL13-SCAF000596, LPAL13-SCAF000597, LPAL13-SCAF000600, LPAL13-SCAF000602, LPAL13-SCAF000604, LPAL13-SCAF000606, LPAL13-SCAF000608, LPAL13-SCAF000609, LPAL13-SCAF000612, LPAL13-SCAF000613, LPAL13-SCAF000615, LPAL13-SCAF000620, LPAL13-SCAF000621, LPAL13-SCAF000623, LPAL13-SCAF000624, LPAL13-SCAF000629, LPAL13-SCAF000630, LPAL13-SCAF000631, LPAL13-SCAF000632, LPAL13-SCAF000633, LPAL13-SCAF000634, LPAL13-SCAF000635, LPAL13-SCAF000638, LPAL13-SCAF000640, LPAL13-SCAF000642, LPAL13-SCAF000647, LPAL13-SCAF000648, LPAL13-SCAF000657, LPAL13-SCAF000658, LPAL13-SCAF000660, LPAL13-SCAF000662, LPAL13-SCAF000663, LPAL13-SCAF000664, LPAL13-SCAF000665, LPAL13-SCAF000667, LPAL13-SCAF000669, LPAL13-SCAF000670, LPAL13-SCAF000671, LPAL13-SCAF000673, LPAL13-SCAF000674, LPAL13-SCAF000675, LPAL13-SCAF000676, LPAL13-SCAF000677, LPAL13-SCAF000678, LPAL13-SCAF000680, LPAL13-SCAF000683, LPAL13-SCAF000684, LPAL13-SCAF000685, LPAL13-SCAF000686, LPAL13-SCAF000687, LPAL13-SCAF000689, LPAL13-SCAF000690, LPAL13-SCAF000691, LPAL13-SCAF000692, LPAL13-SCAF000693, LPAL13-SCAF000694, LPAL13-SCAF000696, LPAL13-SCAF000699, LPAL13-SCAF000701, LPAL13-SCAF000702, LPAL13-SCAF000703, LPAL13-SCAF000705, LPAL13-SCAF000706, LPAL13-SCAF000708, LPAL13-SCAF000709, LPAL13-SCAF000710, LPAL13-SCAF000712, LPAL13-SCAF000715, LPAL13-SCAF000718, LPAL13-SCAF000721, LPAL13-SCAF000724, LPAL13-SCAF000725, LPAL13-SCAF000728, LPAL13-SCAF000729, LPAL13-SCAF000730, LPAL13-SCAF000731, LPAL13-SCAF000733, LPAL13-SCAF000736, LPAL13-SCAF000739, LPAL13-SCAF000740, LPAL13-SCAF000741, LPAL13-SCAF000742, LPAL13-SCAF000743, LPAL13-SCAF000745, LPAL13-SCAF000746, LPAL13-SCAF000747, LPAL13-SCAF000749, LPAL13-SCAF000750, LPAL13-SCAF000751, LPAL13-SCAF0007
## Before removal, there were 544782 genes, now there are 83.
## There are 43 samples which kept less than 90 percent counts.
## tmrc20001 tmrc20005 tmrc20007 tmrc20027 tmrc20028 tmrc20032 tmrc20040 tmrc20039 
##  0.037035  0.041720  0.053085  0.059906  0.077365  0.037129  0.015234  0.041766 
## tmrc20037 tmrc20038 tmrc20041 tmrc20015 tmrc20009 tmrc20010 tmrc20016 tmrc20011 
##  0.028449  0.029649  0.008748  0.026217  0.000000  0.027716  0.026359  0.024992 
## tmrc20012 tmrc20013 tmrc20017 tmrc20014 tmrc20018 tmrc20019 tmrc20020 tmrc20021 
##  0.000000  0.029377  0.020294  0.018363  0.032806  0.079907  0.072428  0.032435 
## tmrc20022 tmrc20025 tmrc20024 tmrc20036 tmrc20033 tmrc20026 tmrc20031 tmrc20042 
##  0.000000  0.063343  0.040538  0.008628  0.000000  0.081882  0.045886  0.106630 
## tmrc20048 tmrc20053 tmrc20052 tmrc20051 tmrc20050 tmrc20043 tmrc20054 tmrc20046 
##  0.029476  0.000000  0.033177  0.035482  0.057916  0.032996  0.036392  0.005881 
## tmrc20047 tmrc20044 tmrc20045 
##  0.034909  0.065649  0.006047
zymo_subset <- set_expt_conditions(zymo_subset, fact = "phenotypiccharacteristics")
## zymo_heat <- plot_sample_heatmap(zymo_subset, row_label = rownames(exprs(snp_subset)))

des <- both_norm$design
undef_idx <- is.na(des[["strain"]])
des[undef_idx, "strain"] <- "unknown"

##hmcols <- colorRampPalette(c("yellow","black","darkblue"))(256)
correlations <- hpgl_cor(exprs(both_norm))

zymo_missing_idx <- is.na(des[["phenotypiccharacteristics"]])
des[zymo_missing_idx, "phenotypiccharacteristics"] <- "unknown"
mydendro <- list(
  "clustfun" = hclust,
  "lwd" = 2.0)
col_data <- as.data.frame(des[, c("phenotypiccharacteristics", "clinicalcategorical")])
unknown_clinical <- is.na(col_data[["clinicalcategorical"]])
row_data <- as.data.frame(des[, c("strain")])
colnames(col_data) <- c("zymodeme", "outcome")
col_data[unknown_clinical, "outcome"] <- "undefined"

colnames(row_data) <- c("strain")
myannot <- list(
  "Col" = list("data" = col_data),
  "Row" = list("data" = row_data))
myclust <- list("cuth" = 1.0,
                "col" = BrewerClusterCol)
mylabs <- list(
  "Row" = list("nrow" = 4),
  "Col" = list("nrow" = 4))
hmcols <- colorRampPalette(c("darkblue", "beige"))(240)
map1 <- annHeatmap2(
  correlations,
  dendrogram = mydendro,
  annotation = myannot,
  cluster = myclust,
  labels = mylabs,
  ## The following controls if the picture is symmetric
  scale = "none",
  col = hmcols)
## Warning in breakColors(breaks, col): more colors than classes: ignoring 28 last
## colors
pp(file = "images/dendro_heatmap.png", image = map1, height=12, width = 12)
## annotated Heatmap
## 
## Rows: 'dendrogram' with 2 branches and 76 members total, at height 5.092 
##   11  annotation variable(s)
## Cols: 'dendrogram' with 2 branches and 76 members total, at height 5.092 
##   9  annotation variable(s)
## plot(map1)

8 Using Variant profiles to make guesses about strains and chronic/self-healing

The following uses the same information to make some guesses about the strains used in the new samples.

des <- both_norm$design
undef_idx <- is.na(des[["strain"]])
des[undef_idx, "strain"] <- "unknown"
##hmcols <- colorRampPalette(c("yellow","black","darkblue"))(256)
correlations <- hpgl_cor(exprs(both_norm))

mydendro <- list(
  "clustfun" = hclust,
  "lwd" = 2.0)
col_data <- as.data.frame(des[, c("condition")])
row_data <- as.data.frame(des[, c("strain")])
colnames(col_data) <- c("condition")
colnames(row_data) <- c("strain")
myannot <- list(
  "Col" = list("data" = col_data),
  "Row" = list("data" = row_data))
myclust <- list("cuth" = 1.0,
                "col" = BrewerClusterCol)
mylabs <- list(
  "Row" = list("nrow" = 4),
  "Col" = list("nrow" = 4))
hmcols <- colorRampPalette(c("darkblue", "beige"))(170)
map1 <- annHeatmap2(
  correlations,
  dendrogram = mydendro,
  annotation = myannot,
  cluster = myclust,
  labels = mylabs)
##  col = hmcols)
plot(map1)

pheno <- subset_expt(lp_expt, subset = "condition=='z2.2'|condition=='z2.3'")
## subset_expt(): There were 43, now there are 23 samples.
pheno <- subset_expt(pheno, subset="!is.na(pData(pheno)[['bcftable']])")
## subset_expt(): There were 23, now there are 23 samples.
pheno_snps <- sm(count_expt_snps(pheno, annot_column = "bcftable"))

xref_prop <- table(pheno_snps$conditions)
pheno_snps$conditions
##  [1] "z2.3" "z2.2" "z2.2" "z2.3" "z2.3" "z2.2" "z2.3" "z2.2" "z2.3" "z2.3"
## [11] "z2.2" "z2.2" "z2.3" "z2.2" "z2.2" "z2.3" "z2.3" "z2.2" "z2.2" "z2.3"
## [21] "z2.3" "z2.2" "z2.3"
idx_tbl <- exprs(pheno_snps) > 5
new_tbl <- data.frame(row.names = rownames(exprs(pheno_snps)))
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
}
new_tbl[["ratio"]] <- (new_tbl[["z2.2"]] - new_tbl[["z2.3"]])
keepers <- grepl(x = rownames(new_tbl), pattern = "LpaL13")
new_tbl <- new_tbl[keepers, ]
new_tbl[["SNP"]] <- rownames(new_tbl)
new_tbl[["Chromosome"]] <- gsub(x = new_tbl[["SNP"]], pattern = "chr_(.*)_pos_.*", replacement = "\\1")
new_tbl[["Position"]] <- gsub(x = new_tbl[["SNP"]], pattern = ".*_pos_(\\d+)_.*", replacement = "\\1")
new_tbl <- new_tbl[, c("SNP", "Chromosome", "Position", "ratio")]
library(CMplot)
## Much appreciate for using CMplot.
## Full description, Bug report, Suggestion and the latest codes:
## https://github.com/YinLiLin/CMplot
CMplot(new_tbl)
##  SNP-Density Plotting.
##  Circular-Manhattan Plotting ratio.
##  Rectangular-Manhattan Plotting ratio.
##  QQ Plotting ratio.
##  Plots are stored in: /mnt/cbcb/fs01_abelew/cbcb-lab/nelsayed/scratch/atb/rnaseq/lpanamensis_tmrc_2019
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 68b1ce610bf0c750d9a3ed2f6bd2a529b1744c29
## This is hpgltools commit: Thu May 27 17:01:01 2021 -0400: 68b1ce610bf0c750d9a3ed2f6bd2a529b1744c29
## Saving to tmrc2_02sample_estimation_v202105.rda.xz
tmp <- loadme(filename = savefile)
LS0tCnRpdGxlOiAiVE1SQzIgQ29tcHJlaGVuc2l2ZSBEYXRhIEFuYWx5c2lzOiAyMDIxMDUiCmF1dGhvcjogImF0YiBhYmVsZXdAZ21haWwuY29tIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKIGh0bWxfZG9jdW1lbnQ6CiAgY29kZV9kb3dubG9hZDogdHJ1ZQogIGNvZGVfZm9sZGluZzogc2hvdwogIGZpZ19jYXB0aW9uOiB0cnVlCiAgZmlnX2hlaWdodDogNwogIGZpZ193aWR0aDogNwogIGhpZ2hsaWdodDogZGVmYXVsdAogIGtlZXBfbWQ6IGZhbHNlCiAgbW9kZTogc2VsZmNvbnRhaW5lZAogIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgdGhlbWU6IHJlYWRhYmxlCiAgdG9jOiB0cnVlCiAgdG9jX2Zsb2F0OgogICBjb2xsYXBzZWQ6IGZhbHNlCiAgIHNtb290aF9zY3JvbGw6IGZhbHNlCi0tLQoKPHN0eWxlPgogIGJvZHkgLm1haW4tY29udGFpbmVyIHsKICAgIG1heC13aWR0aDogMTYwMHB4OwogIH0KPC9zdHlsZT4KCmBgYHtyIG9wdGlvbnMsIGluY2x1ZGUgPSBGQUxTRX0KbGlicmFyeShocGdsdG9vbHMpCnR0IDwtIHNtKGRldnRvb2xzOjpsb2FkX2FsbCgifi9ocGdsdG9vbHMiKSkKa25pdHI6Om9wdHNfa25pdCRzZXQocHJvZ3Jlc3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgd2lkdGggPSA5MCwKICAgICAgICAgICAgICAgICAgICAgZWNobyA9IFRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBmaWcud2lkdGggPSA4LAogICAgICAgICAgICAgICAgICAgICAgZmlnLmhlaWdodCA9IDgsCiAgICAgICAgICAgICAgICAgICAgICBkcGkgPSA5NikKb2xkX29wdGlvbnMgPC0gb3B0aW9ucyhkaWdpdHMgPSA0LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICBrbml0ci5kdXBsaWNhdGUubGFiZWwgPSAiYWxsb3ciKQpnZ3Bsb3QyOjp0aGVtZV9zZXQoZ2dwbG90Mjo6dGhlbWVfYncoYmFzZV9zaXplID0gMTIpKQp2ZXIgPC0gIjIwMjEwNSIKcnVuZGF0ZSA8LSBmb3JtYXQoU3lzLkRhdGUoKSwgZm9ybWF0ID0gIiVZJW0lZCIpCgojIyB0bXAgPC0gdHJ5KHNtKGxvYWRtZShmaWxlbmFtZSA9IGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLCByZXBsYWNlID0gIlxcLnJkYVxcLnh6IiwgeCA9IHByZXZpb3VzX2ZpbGUpKSkpCnJtZF9maWxlIDwtICJ0bXJjMl8wMnNhbXBsZV9lc3RpbWF0aW9uX3YyMDIxMDUuUm1kIgpzYXZlZmlsZSA8LSBnc3ViKHBhdHRlcm4gPSAiXFwuUm1kIiwgcmVwbGFjZSA9ICJcXC5yZGFcXC54eiIsIHggPSBybWRfZmlsZSkKCmxpYnJhcnkoSGVhdHBsdXMpCmBgYAoKIyBJbnRyb2R1Y3Rpb24KClRoaXMgZG9jdW1lbnQgaXMgaW50ZW5kZWQgdG8gcHJvdmlkZSBhIGdlbmVyYWwgb3ZlcnZpZXcgb2YgdGhlIFRNUkMyIHNhbXBsZXMKd2hpY2ggaGF2ZSB0aHVzIGZhciBiZWVuIHNlcXVlbmNlZC4gIEluIHNvbWUgY2FzZXMsIHRoaXMgaW5jbHVkZXMgb25seSB0aG9zZQpzYW1wbGVzIHN0YXJ0aW5nIGluIDIwMTk7IGluIG90aGVyIGluc3RhbmNlcyBJIGFtIGluY2x1ZGluZyBvdXIgcHJldmlvdXMKKDIwMTUtMjAxNikgc2FtcGxlcy4KCkluIGFsbCBjYXNlcyB0aGUgcHJvY2Vzc2luZyBwZXJmb3JtZWQgd2FzOgoKMS4gIERlZmF1bHQgdHJpbW1pbmcgd2FzIHBlcmZvcm1lZC4KMi4gIEhpc2F0MiB3YXMgdXNlZCB0byBtYXAgdGhlIHJlbWFpbmluZyByZWFkcyBhZ2FpbnN0IHRoZSBMZWlzaG1hbmlhCiAgICBwYW5hbWVuc2lzIGdlbm9tZSByZXZpc2lvbiAzNi4KMy4gIFRoZSBhbGlnbm1lbnRzIGZyb20gaGlzYXQyIHdlcmUgdXNlZCB0byBjb3VudCByZWFkcy9nZW5lIGFnYWluc3QgdGhlCiAgICByZXZpc2lvbiAzNiBhbm5vdGF0aW9ucyB3aXRoIGh0c2VxLgo0LiAgVGhlc2UgYWxpZ25tZW50cyB3ZXJlIGFsc28gcGFzc2VkIHRvIHRoZSBwaWxldXAgZnVuY3Rpb25hbGl0eSBvZiBzYW10b29scwogICAgYW5kIHRoZSB2Y2YvYmNmIHV0aWxpdGllcyBpbiBvcmRlciB0byBtYWtlIGEgbWF0cml4IG9mIGFsbCBvYnNlcnZlZAogICAgZGlmZmVyZW5jZXMgYmV0d2VlbiBlYWNoIHNhbXBsZSB3aXRoIHJlc3BlY3QgdG8gdGhlIHJlZmVyZW5jZS4KClRoZSBhbmFseXNlcyBpbiB0aGlzIGRvY3VtZW50IHVzZSB0aGUgbWF0cmljZXMgb2YgY291bnRzL2dlbmUgZnJvbSAjMyBhbmQKdmFyaWFudHMvcG9zaXRpb24gZnJvbSAjNCBpbiBvcmRlciB0byBwcm92aWRlIHNvbWUgaW1hZ2VzIGFuZCBtZXRyaWNzIGRlc2NyaWJpbmcKdGhlIHNhbXBsZXMgd2UgaGF2ZSBzZXF1ZW5jZWQgc28gZmFyLgoKIyBBbm5vdGF0aW9ucwoKRXZlcnl0aGluZyB3aGljaCBmb2xsb3dzIGRlcGVuZHMgb24gdGhlIEV4aXN0aW5nIFRyaVRyeXBEQiBhbm5vdGF0aW9ucyByZXZpc2lvbgo0NiwgY2lyY2EgMjAxOS4gIFRoZSBmb2xsb3dpbmcgYmxvY2sgbG9hZHMgYSBkYXRhYmFzZSBvZiB0aGVzZSBhbm5vdGF0aW9ucyBhbmQKdHVybnMgaXQgaW50byBhIG1hdHJpeCB3aGVyZSB0aGUgcm93cyBhcmUgZ2VuZXMgYW5kIGNvbHVtbnMgYXJlIGFsbCB0aGUKYW5ub3RhdGlvbiB0eXBlcyBwcm92aWRlZCBieSBUcmlUcnlwREIuCgpUaGUgc2FtZSBkYXRhYmFzZSB3YXMgdXNlZCB0byBjcmVhdGUgYSBtYXRyaXggb2Ygb3J0aG9sb2dvdXMgZ2VuZXMgYmV0d2VlbgpMLnBhbmFtZW5zaXMgYW5kIGFsbCBvZiB0aGUgb3RoZXIgc3BlY2llcyBpbiB0aGUgVHJpVHJ5cERCLgoKYGBge3IgYW5ub3R9CnR0IDwtIHNtKGxpYnJhcnkoRXVQYXRoREIpKQp0dCA8LSBzbShsaWJyYXJ5KG9yZy5McGFuYW1lbnNpcy5NSE9NQ09MODFMMTMudjQ2LmVnLmRiKSkKcGFuX2RiIDwtIG9yZy5McGFuYW1lbnNpcy5NSE9NQ09MODFMMTMudjQ2LmVnLmRiCmFsbF9maWVsZHMgPC0gY29sdW1ucyhwYW5fZGIpCgphbGxfbHBfYW5ub3QgPC0gc20obG9hZF9vcmdkYl9hbm5vdGF0aW9ucygKICAgIHBhbl9kYiwKICAgIGtleXR5cGUgPSAiZ2lkIiwKICAgIGZpZWxkcyA9IGMoImFubm90X2dlbmVfZW50cmV6X2lkIiwgImFubm90X2dlbmVfbmFtZSIsCiAgICAgICAgICAgICAgICJhbm5vdF9zdHJhbmQiLCAiYW5ub3RfY2hyb21vc29tZSIsICJhbm5vdF9jZHNfbGVuZ3RoIiwKICAgICAgICAgICAgICAgImFubm90X2dlbmVfcHJvZHVjdCIpKSkkZ2VuZXMKCmxwX2dvIDwtIHNtKGxvYWRfb3JnZGJfZ28ocGFuX2RiKSkKbHBfbGVuZ3RocyA8LSBhbGxfbHBfYW5ub3RbLCBjKCJnaWQiLCAiYW5ub3RfY2RzX2xlbmd0aCIpXQpjb2xuYW1lcyhscF9sZW5ndGhzKSAgPC0gYygiSUQiLCAibGVuZ3RoIikKYWxsX2xwX2Fubm90W1siYW5ub3RfZ2VuZV9wcm9kdWN0Il1dIDwtIHRvbG93ZXIoYWxsX2xwX2Fubm90W1siYW5ub3RfZ2VuZV9wcm9kdWN0Il1dKQpvcnRob3MgPC0gc20oRXVQYXRoREI6OmV4dHJhY3RfZXVwYXRoX29ydGhvbG9ncyhkYiA9IHBhbl9kYikpCgpoaXNhdF9hbm5vdCA8LSBhbGxfbHBfYW5ub3QKIyMgcm93bmFtZXMoaGlzYXRfYW5ub3QpIDwtIHBhc3RlMCgiZXhvbl8iLCByb3duYW1lcyhoaXNhdF9hbm5vdCksICIuRTEiKQpgYGAKCiMgVE9ETzoKClJlc2VxdWVuY2Ugc2FtcGxlczogVE1SQzIwMDAyLCBUTVJDMjAwMDYsIFRNUkMyMDAwNCAobWF5YmUgVE1SQzIwMDA4IGFuZCBUTVJDMjAwMjkpCgojIEdlbmVyYXRlIEV4cHJlc3Npb25zZXRzIGFuZCBTYW1wbGUgRXN0aW1hdGlvbgoKVGhlIHByb2Nlc3Mgb2Ygc2FtcGxlIGVzdGltYXRpb24gdGFrZXMgdHdvIHByaW1hcnkgaW5wdXRzOgoKMS4gIFRoZSBzYW1wbGUgc2hlZXQsIHdoaWNoIGNvbnRhaW5zIGFsbCB0aGUgbWV0YWRhdGEgd2UgY3VycmVudGx5IGhhdmUgb24gaGFuZCwKICAgIGluY2x1ZGluZyBmaWxlbmFtZXMgZm9yIHRoZSBvdXRwdXRzIG9mICMzIGFuZCAjNCBhYm92ZS4KMi4gIFRoZSBnZW5lIGFubm90YXRpb25zLgoKQW4gZXhwcmVzc2lvbnNldCBpcyBhIGRhdGEgc3RydWN0dXJlIHVzZWQgaW4gUiB0byBleGFtaW5lIFJOQVNlcSBkYXRhLiAgSXQKaXMgY29tcHJpc2VkIG9mIGFubm90YXRpb25zLCBtZXRhZGF0YSwgYW5kIGV4cHJlc3Npb24gZGF0YS4gIEluIHRoZSBjYXNlIG9mIG91cgpwcm9jZXNzaW5nIHBpcGVsaW5lLCB0aGUgbG9jYXRpb24gb2YgdGhlIGV4cHJlc3Npb24gZGF0YSBpcyBwcm92aWRlZCBieSB0aGUKZmlsZW5hbWVzIGluIHRoZSBtZXRhZGF0YS4KClRoZSBmaXJzdCBsaW5lcyBvZiB0aGUgZm9sbG93aW5nIGJsb2NrIGNyZWF0ZSB0aGUgRXhwcmVzc2lvbnNldC4gIEFsbCBvZiB0aGUKZm9sbG93aW5nIGxpbmVzIHBlcmZvcm0gdmFyaW91cyBub3JtYWxpemF0aW9ucyBhbmQgZ2VuZXJhdGUgcGxvdHMgZnJvbSBpdC4KCiMjIE5vdGVzCgpUaGUgZm9sbG93aW5nIHNhbXBsZXMgYXJlIG11Y2ggbG93ZXIgY292ZXJhZ2U6CgoqIFRNUkMyMDAwMgoqIFRNUkMyMDAwNgoqIFRNUkMyMDAwNwoqIFRNUkMyMDAwOAoKIyMgVE9ETzoKCjEuICBEbyB0aGUgbXVsdGktZ2VuZSBmYW1pbHkgcmVtb3ZhbCByaWdodCBoZXJlIGluc3RlYWQgb2Ygd2F5IGRvd24gYXQgdGhlIGJvdHRvbQoyLiAgQWRkIHp5bW9kZW1lIHNucHMgdG8gdGhlIGFubm90YXRpb24gbGF0ZXIuCjMuICBTdGFydCBwaHlsb2dlbmV0aWMgYW5hbHlzaXMgb2YgdmFyaWFudCB0YWJsZS4KCgpgYGB7ciBuZXdfc2FtcGxlc19oaXNhdH0Kc2FtcGxlX3NoZWV0IDwtIGdsdWU6OmdsdWUoInNhbXBsZV9zaGVldHMvdG1yYzJfc2FtcGxlc18yMDIxMDUyOC54bHN4IikKCmxwX2V4cHQgPC0gc20oY3JlYXRlX2V4cHQoc2FtcGxlX3NoZWV0LAogICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfaW5mbyA9IGhpc2F0X2Fubm90LAogICAgICAgICAgICAgICAgICAgICAgICAgIGlkX2NvbHVtbiA9ICJocGdsaWRlbnRpZmllciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZV9jb2x1bW4gPSAibHBhbmFtZW5zaXN2MzZoaXNhdGZpbGUiKSkgJT4lCiAgc2V0X2V4cHRfY29uZGl0aW9ucyhmYWN0ID0gInp5bW9kZW1lY2F0ZWdvcmljYWwiKSAlPiUKICBzdWJzZXRfZXhwdChub256ZXJvID0gODYwMCkgJT4lCiAgc2VtYW50aWNfZXhwdF9maWx0ZXIoc2VtYW50aWMgPSBjKCJhbWFzdGluIiwgImdwNjMiLCAibGVpc2htYW5vbHlzaW4iKSwKICAgICAgICAgICAgICAgICAgICAgICBzZW1hbnRpY19jb2x1bW4gPSAiYW5ub3RfZ2VuZV9wcm9kdWN0IikKCmxpYnNpemVzIDwtIHBsb3RfbGlic2l6ZShscF9leHB0KQpsaWJzaXplcyRwbG90CiMjIEkgdGhpbmsgc2FtcGxlcyA3LDEwIHNob3VsZCBiZSByZW1vdmVkIGF0IG1pbmltdW0sIHByb2JhYmx5IGFsc28gOSwxMQpub256ZXJvIDwtIHBsb3Rfbm9uemVybyhscF9leHB0KQpub256ZXJvJHBsb3QKcGxvdF9ib3hwbG90KGxwX2V4cHQpCgpmaWx0ZXJfcGxvdCA8LSBwbG90X2xpYnNpemVfcHJlcG9zdChscF9leHB0KQpmaWx0ZXJfcGxvdCRsb3dnZW5lX3Bsb3QKZmlsdGVyX3Bsb3QkY291bnRfcGxvdApgYGAKCiMjIERpc3RyaWJ1dGlvbiBWaXN1YWxpemF0aW9uCgpOYWppYidzIGZhdm9yaXRlIHBsb3RzIGFyZSBvZiBjb3Vyc2UgdGhlIFBDQS9UTlNFLiAgVGhlc2UgYXJlIG5pY2UgdG8gbG9vayBhdCBpbgpvcmRlciB0byBnZXQgYSBzZW5zZSBvZiB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHNhbXBsZXMuICBUaGV5IGFsc28gcHJvdmlkZSBhCmdvb2Qgb3Bwb3J0dW5pdHkgdG8gc2VlIHdoYXQgaGFwcGVucyB3aGVuIG9uZSBhcHBsaWVzIGRpZmZlcmVudCBub3JtYWxpemF0aW9ucywKc3Vycm9nYXRlIGFuYWx5c2VzLCBmaWx0ZXJzLCBldGMuICBJbiBhZGRpdGlvbiwgb25lIG1heSBzZXQgZGlmZmVyZW50CmV4cGVyaW1lbnRhbCBmYWN0b3JzIGFzIHRoZSBwcmltYXJ5ICdjb25kaXRpb24nICh1c3VhbGx5IHRoZSBjb2xvciBvZiBwbG90cykgYW5kCnN1cnJvZ2F0ZSAnYmF0Y2hlcycuCgojIyBCeSBTdXNjZXB0aWxpYml0eQoKQ29sdW1uICdRJyBpbiB0aGUgc2FtcGxlIHNoZWV0LCBtYWtlIGEgY2F0ZWdvcmljYWwgdmVyc2lvbiBvZiBpdCB3aXRoIHRoZXNlIHBhcmFtZXRlcnM6CgoqIDAgPD0geCA8PSAzNSBpcyByZXNpc3RhbnQKKiAzNiA8PSB4IDw9IDQ4IGlzIGFtYmlndW91cwoqIDQ5IDw9IHggaXMgc2Vuc2l0aXZlCgpgYGB7ciBzdXNjZXB0aWJpbGl0eX0Kc3RhcnRpbmcgPC0gYXMubnVtZXJpYyhwRGF0YShscF9leHB0KVtbInN1c2NlcHRpYmlsaXR5aW5mZWN0aW9ucmVkdWN0aW9uMzJ1Z21sc2J2aGlzdG9yaWNhbGRhdGEiXV0pCnN1c19jYXRlZ29yaWNhbCA8LSBzdGFydGluZwpuYV9pZHggPC0gaXMubmEoc3RhcnRpbmcpCnN1c19jYXRlZ29yaWNhbFtuYV9pZHhdIDwtICJ1bmtub3duIgoKcmVzaXN0X2lkeCA8LSBzdGFydGluZyA8PSAwLjM1CnN1c19jYXRlZ29yaWNhbFtyZXNpc3RfaWR4XSA8LSAicmVzaXN0YW50IgppbmRldGVybWluYW50X2lkeCA8LSBzdGFydGluZyA+PSAwLjM2ICYgc3RhcnRpbmcgPD0gMC40OApzdXNfY2F0ZWdvcmljYWxbaW5kZXRlcm1pbmFudF9pZHhdIDwtICJhbWJpZ3VvdXMiCnN1c2NlcHRpYmxlX2lkeCA8LSBzdGFydGluZyA+PSAwLjQ5CnN1c19jYXRlZ29yaWNhbFtzdXNjZXB0aWJsZV9pZHhdIDwtICJzZW5zaXRpdmUiCgpwRGF0YShscF9leHB0JGV4cHJlc3Npb25zZXQpW1sic3VzX2NhdGVnb3J5Il1dIDwtIHN1c19jYXRlZ29yaWNhbApgYGAKCmBgYHtyIHByZV9xdWVzdGlvbnN9CmNsaW5pY2FsX3NhbXBsZXMgPC0gbHBfZXhwdCAlPiUKICBzZXRfZXhwdF9iYXRjaGVzKGZhY3QgPSBzdXNfY2F0ZWdvcmljYWwpCgpjbGluaWNhbF9ub3JtIDwtIHNtKG5vcm1hbGl6ZV9leHB0KGNsaW5pY2FsX3NhbXBsZXMsIG5vcm0gPSAicXVhbnQiLCB0cmFuc2Zvcm0gPSAibG9nMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udmVydCA9ICJjcG0iLCBiYXRjaCA9IEZBTFNFLCBmaWx0ZXIgPSBUUlVFKSkKenltb19wY2EgPC0gcGxvdF9wY2EoY2xpbmljYWxfbm9ybSwgcGxvdF90aXRsZSA9ICJQQ0Egb2YgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiKQpwcChmaWxlID0gImltYWdlcy96eW1vX3BjYV9zdXNfc2hhcGUucG5nIiwgaW1hZ2UgPSB6eW1vX3BjYSRwbG90KQoKenltb18zZHBjYSA8LSBwbG90XzNkX3BjYSh6eW1vX3BjYSkKenltb18zZHBjYSRwbG90Cgp6eW1vX3RzbmUgPC0gcGxvdF90c25lKGNsaW5pY2FsX25vcm0sIHBsb3RfdGl0bGUgPSAiVFNORSBvZiBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIpCnp5bW9fdHNuZSRwbG90CgpjbGluaWNhbF9uYiA8LSBub3JtYWxpemVfZXhwdChjbGluaWNhbF9zYW1wbGVzLCBjb252ZXJ0ID0gImNwbSIsIHRyYW5zZm9ybSA9ICJsb2cyIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IFRSVUUsIGJhdGNoID0gInN2YXNlcSIpCmNsaW5pY2FsX25iX3BjYSA8LSBwbG90X3BjYShjbGluaWNhbF9uYiwgcGxvdF90aXRsZSA9ICJQQ0Egb2YgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiKQpwcChmaWxlID0gImltYWdlcy9jbGluaWNhbF9uYl9wY2Ffc3VzX3NoYXBlLnBuZyIsIGltYWdlID0gY2xpbmljYWxfbmJfcGNhJHBsb3QpCgoKY2xpbmljYWxfbmJfdHNuZSA8LSBwbG90X3RzbmUoY2xpbmljYWxfbmIsIHBsb3RfdGl0bGUgPSAiVFNORSBvZiBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIpCmNsaW5pY2FsX25iX3RzbmUkcGxvdAoKY29yaGVhdCA8LSBwbG90X2NvcmhlYXQoY2xpbmljYWxfbm9ybSwgcGxvdF90aXRsZSA9ICJDb3JyZWxhdGlvbiBoZWF0bWFwIG9mIHBhcmFzaXRlCiAgICAgICAgICAgICAgICAgZXhwcmVzc2lvbiB2YWx1ZXMKIikKY29yaGVhdCRwbG90CgpwbG90X3NtKGNsaW5pY2FsX25vcm0pJHBsb3QKYGBgCgojIyBCeSBDdXJlL0ZhaWwgc3RhdHVzCgpgYGB7ciBjZl9zdGF0dXN9CmNmX2V4cHQgPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhscF9leHB0LCBmYWN0ID0gImNsaW5pY2FsY2F0ZWdvcmljYWwiKSAlPiUKICBzZXRfZXhwdF9iYXRjaGVzKGZhY3QgPSBzdXNfY2F0ZWdvcmljYWwpCgpjZl9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KGNmX2V4cHQsIGNvbnZlcnQgPSAiY3BtIiwgdHJhbnNmb3JtID0gImxvZzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm0gPSAicXVhbnQiLCBmaWx0ZXIgPSBUUlVFKQpzdGFydF9jZiA8LSBwbG90X3BjYShjZl9ub3JtLCBwbG90X3RpdGxlID0gIlBDQSBvZiBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIpCnBwKGZpbGUgPSAiaW1hZ2VzL2NmX3N1c19zaGFwZS5wbmciLCBpbWFnZSA9IHN0YXJ0X2NmJHBsb3QpCgpjZl9uYiA8LSBub3JtYWxpemVfZXhwdChjZl9leHB0LCBjb252ZXJ0ID0gImNwbSIsIHRyYW5zZm9ybSA9ICJsb2cyIiwKICAgICAgICAgICAgICAgICAgICAgICAgbm9ybSA9ICJxdWFudCIsIGZpbHRlciA9IFRSVUUsIGJhdGNoID0gInN2YXNlcSIpCmNmX25iX3BjYSA8LSBwbG90X3BjYShjZl9uYiwgcGxvdF90aXRsZSA9ICJQQ0Egb2YgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiKQpwcChmaWxlID0gImltYWdlcy9jZl9zdXNfc2hhcmVfbmIucG5nIiwgaW1hZ2UgPSBjZl9uYl9wY2EkcGxvdCkKCmNmX25vcm0gPC0gbm9ybWFsaXplX2V4cHQoY2ZfZXhwdCwgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwgbm9ybSA9ICJxdWFudCIpCgp0ZXN0IDwtIHBjYV9pbmZvcm1hdGlvbihjZl9ub3JtLAogICAgICAgICAgICAgICAgICAgICAgICBleHB0X2ZhY3RvcnMgPSBjKCJjbGluaWNhbGNhdGVnb3JpY2FsIiwgInp5bW9kZW1lY2F0ZWdvcmljYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRob2dlbnN0cmFpbiIsICJwYXNzYWdlbnVtYmVyIiksCiAgICAgICAgICAgICAgICAgICAgICAgIG51bV9jb21wb25lbnRzID0gNiwgcGxvdF9wY2FzID0gVFJVRSkKdGVzdCRhbm92YV9wCnRlc3QkY29yX2hlYXRtYXAKYGBgCgpgYGB7ciBzdXNjZXB0aWJpbGl0eV9wY2F9CnN1c19leHB0IDwtIHNldF9leHB0X2NvbmRpdGlvbnMobHBfZXhwdCwgZmFjdCA9ICJzdXNfY2F0ZWdvcnkiKSAlPiUKICBzZXRfZXhwdF9iYXRjaGVzKGZhY3QgPSAienltb2RlbWVjYXRlZ29yaWNhbCIpCnN1c19ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KHN1c19leHB0LCB0cmFuc2Zvcm0gPSAibG9nMiIsIGNvbnZlcnQgPSAiY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybSA9ICJxdWFudCIsIGZpbHRlciA9IFRSVUUpCnN1c19wY2EgPC0gcGxvdF9wY2Eoc3VzX25vcm0sIHBsb3RfdGl0bGUgPSAiUENBIG9mIHBhcmFzaXRlIGV4cHJlc3Npb24gdmFsdWVzIikKc3VzX3BjYSRwbG90CgpzdXNfbmIgPC0gbm9ybWFsaXplX2V4cHQoc3VzX2V4cHQsIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgYmF0Y2ggPSAic3Zhc2VxIiwgZmlsdGVyID0gVFJVRSkKc3VzX25iX3BjYSA8LSBwbG90X3BjYShzdXNfbmIsIHBsb3RfdGl0bGUgPSAiUENBIG9mIHBhcmFzaXRlIGV4cHJlc3Npb24gdmFsdWVzIikKcHAoZmlsZSA9ICJpbWFnZXMvc3VzX25iX3BjYS5wbmciLCBpbWFnZSA9IHN1c19uYl9wY2EkcGxvdCkKYGBgCgpBdCB0aGlzIHRpbWUsIHdlIGRvIG5vdCBoYXZlIHZlcnkgbWFueSBzYW1wbGVzLCBzbyB0aGUgc2V0IG9mIG1ldHJpY3MvcGxvdHMgaXMKZmFpcmx5IGxpbWl0ZWQuICBUaGVyZSBpcyByZWFsbHkgb25seSBvbmUgZmFjdG9yIGluIHRoZSBtZXRhZGF0YSB3aGljaCB3ZSBjYW4KdXNlIGZvciBwZXJmb3JtaW5nIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2VzLCB0aGUgJ3p5bW9kZW1lJy4KCiMgWnltb2RlbWUgYW5hbHlzZXMKClRoZSBmb2xsb3dpbmcgc2VjdGlvbnMgcGVyZm9ybSBhIHNlcmllcyBvZiBhbmFseXNlcyB3aGljaCBzZWVrIHRvIGVsdWNpZGF0ZQpkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSB6eW1vZGVtZXMgMi4yIGFuZCAyLjMgZWl0aGVyIHRocm91Z2ggZGlmZmVyZW50aWFsCmV4cHJlc3Npb24gb3IgdmFyaWFudCBwcm9maWxlcy4KCiMjIERpZmZlcmVudGlhbCBleHByZXNzaW9uCgojIyMgV2l0aCByZXNwZWN0IHRvIHp5bW9kZW1lIGF0dHJpYnV0aW9uCgpUT0RPOiBEbyB0aGlzIHdpdGggYW5kIHdpdGhvdXQgc3ZhIGFuZCBjb21wYXJlIHRoZSByZXN1bHRzLgoKYGBge3Igenltb19kZSwgZmlnLnNob3cgPSAiaGlkZSJ9Cnp5X2V4cHQgPC0gc3Vic2V0X2V4cHQobHBfZXhwdCwgc3Vic2V0ID0gImNvbmRpdGlvbj09J3oyLjInfGNvbmRpdGlvbj09J3oyLjMnIikKenlfbm9ybSA8LSBub3JtYWxpemVfZXhwdCh6eV9leHB0LCBmaWx0ZXIgPSBUUlVFLCBjb252ZXJ0ID0gImNwbSIsIG5vcm0gPSAicXVhbnQiKQp6eV9kZV9ub2JhdGNoIDwtIHNtKGFsbF9wYWlyd2lzZSh6eV9leHB0LCBmaWx0ZXIgPSBUUlVFLCBtb2RlbF9iYXRjaCA9ICJzdmFzZXEiKSkKenlfZGUgPC0gc20oYWxsX3BhaXJ3aXNlKHp5X2V4cHQsIGZpbHRlciA9IFRSVUUsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIpKQp6eV90YWJsZSA8LSBzbShjb21iaW5lX2RlX3RhYmxlcyh6eV9kZSwgZXhjZWwgPSBnbHVlOjpnbHVlKCJleGNlbC96eV90YWJsZXMtdnt2ZXJ9Lnhsc3giKSkpCnp5X3NpZyA8LSBzbShleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKHp5X3RhYmxlLCBleGNlbCA9IGdsdWU6OmdsdWUoImV4Y2VsL3p5X3NpZy12e3Zlcn0ueGxzeCIpKSkKYGBgCgojIyMgSW1hZ2VzIG9mIHp5bW9kZW1lIERFCgpgYGB7ciB6eW1vZF9kZV9waWN0dXJlc30KenlfdGFibGVbWyJwbG90cyJdXVtbInoyM192c196MjIiXV1bWyJkZXNlcV9tYV9wbG90cyJdXVtbInBsb3QiXV0KYGBgCgojIyBXaXRoIHJlc3BlY3QgdG8gY3VyZS9mYWlsdXJlCgpJbiBjb250cmFzdCwgd2UgY2FuIHNlYXJjaCBmb3IgZ2VuZXMgd2hpY2ggYXJlIGRpZmZlcmVudGlhbGx5CmV4cHJlc3NlZCB3aXRoIHJlc3BlY3QgdG8gY3VyZS9mYWlsdXJlIHN0YXR1cy4KCmBgYHtyIGN1cmVmYWlsX2RlLCBmaWcuc2hvdyA9ICJoaWRlIn0KY2ZfZGUgPC0gc20oYWxsX3BhaXJ3aXNlKGNmX2V4cHQsIGZpbHRlciA9IFRSVUUsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIpKQpjZl90YWJsZSA8LSBzbShjb21iaW5lX2RlX3RhYmxlcyhjZl9kZSwgZXhjZWwgPSBnbHVlOjpnbHVlKCJleGNlbC9jZl90YWJsZXMtdnt2ZXJ9Lnhsc3giKSkpCmNmX3NpZyA8LSBzbShleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKGNmX3RhYmxlLCBleGNlbCA9IGdsdWU6OmdsdWUoImV4Y2VsL2NmX3NpZy12e3Zlcn0ueGxzeCIpKSkKYGBgCgojIyBXaXRoIHJlc3BlY3QgdG8gc3VzY2VwdGliaWxpdHkKCkZpbmFsbHksIHdlIGNhbiB1c2Ugb3VyIGNhdGVnb3J5IG9mIHN1c2NlcHRpYmlsaXR5IGFuZCBsb29rIGZvciBnZW5lcwp3aGljaCBjaGFuZ2UgZnJvbSBzZW5zaXRpdmUgdG8gcmVzaXN0YW50LiAgS2VlcCBpbiBtaW5kLCB0aG91Z2gsIHRoYXQKZm9yIHRoZSBtb21lbnQgd2UgaGF2ZSBhIGxvdCBvZiBhbWJpZ3VvdXMgYW5kIHVua25vd24gc3RyYWlucy4KCmBgYHtyIGN1cmVmYWlsX2RlLCBmaWcuc2hvdyA9ICJoaWRlIn0Kc3VzX2RlIDwtIHNtKGFsbF9wYWlyd2lzZShzdXNfZXhwdCwgZmlsdGVyID0gVFJVRSwgbW9kZWxfYmF0Y2ggPSAic3Zhc2VxIikpCnN1c190YWJsZSA8LSBzbShjb21iaW5lX2RlX3RhYmxlcyhzdXNfZGUsIGV4Y2VsID0gZ2x1ZTo6Z2x1ZSgiZXhjZWwvc3VzX3RhYmxlcy12e3Zlcn0ueGxzeCIpKSkKc3VzX3NpZyA8LSBzbShleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKHN1c190YWJsZSwgZXhjZWwgPSBnbHVlOjpnbHVlKCJleGNlbC9zdXNfc2lnLXZ7dmVyfS54bHN4IikpKQpgYGAKCiMjIE9udG9sb2d5IHNlYXJjaGVzCgpOb3cgbGV0IHVzIGxvb2sgZm9yIG9udG9sb2d5IGNhdGVnb3JpZXMgd2hpY2ggYXJlIGluY3JlYXNlZCBpbiB0aGUgMi4zCnNhbXBsZXMgZm9sbG93ZWQgYnkgdGhlIDIuMiBzYW1wbGVzLgoKYGBge3IgZ28sIHNpZy5zaG93ID0gImhpZGUifQojIyBHZW5lIGNhdGVnb3JpZXMgbW9yZSByZXByZXNlbnRlZCBpbiB0aGUgMi4zIGdyb3VwLgp6eV9nb191cCA8LSBzbShzaW1wbGVfZ29zZXEoc2lnX2dlbmVzID0genlfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWzFdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdvX2RiID0gbHBfZ28sIGxlbmd0aF9kYiA9IGxwX2xlbmd0aHMpKQoKIyMgR2VuZSBjYXRlZ29yaWVzIG1vcmUgcmVwcmVzZW50ZWQgaW4gdGhlIDIuMiBncm91cC4KenlfZ29fZG93biA8LSBzbShzaW1wbGVfZ29zZXEoc2lnX2dlbmVzID0genlfc2lnW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbMV1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnb19kYiA9IGxwX2dvLCBsZW5ndGhfZGIgPSBscF9sZW5ndGhzKSkKYGBgCgojIyMgQSBjb3VwbGUgcGxvdHMgZnJvbSB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24KCiMjIyMgTnVtYmVyIG9mIGdlbmVzIGluIGFncmVlbWVudCBhbW9uZyBERSBtZXRob2RzLCAyLjMgbW9yZSB0aGFuIDIuMgoKSW4gdGhlIGZ1bmN0aW9uICdjb21iaW5lZF9kZV90YWJsZXMoKScgYWJvdmUsIG9uZSBvZiB0aGUgdGFza3MKcGVyZm9ybWVkIGlzIHRvIGxvb2sgYXQgdGhlIGFncmVlbWVudCBhbW9uZyBERVNlcTIsIGxpbW1hLCBhbmQgZWRnZVIuClRoZSBmb2xsb3dpbmcgc2hvdyBhIGNvdXBsZSBvZiB0aGVzZSBmb3IgdGhlIHNldCBvZiBnZW5lcyBvYnNlcnZlZAp3aXRoIGEgZm9sZC1jaGFuZ2UgPj0gfDJ8IGFuZCBhZGp1c3RlZCBwLXZhbHVlIDw9IDAuMDUuCgpgYGB7ciBkZV9wbG90c30KenlfdGFibGVbWyJ2ZW5ucyJdXVtbMV1dW1sicF9sZmMxIl1dW1sidXBfbm93ZWlnaHQiXV0KYGBgCgojIyMjIE51bWJlciBvZiBnZW5lcyBpbiBhZ3JlZW1lbnQgYW1vbmcgREUgbWV0aG9kcywgMi4yIG1vcmUgdGhhbiAyLjMKCmBgYHtyIGRlX3Bsb3RzfQp6eV90YWJsZVtbInZlbm5zIl1dW1sxXV1bWyJwX2xmYzEiXV1bWyJkb3duX25vd2VpZ2h0Il1dCmBgYAoKIyMjIyBNQSBwbG90IG9mIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBiZXR3ZWVuIHRoZSB6eW1vZGVtZXMuCgpgYGB7ciBvdGhlcl9wbG90c30KenlfdGFibGUkcGxvdHNbWzFdXVtbImRlc2VxX21hX3Bsb3RzIl1dW1sicGxvdCJdXQpgYGAKCiMjIyMgZ29zZXEgb250b2xvZ3kgcGxvdHMgb2YgZ3JvdXBzIG9mIGdlbmVzLCAyLjMgbW9yZSB0aGFuIDIuMgoKYGBge3IgZ29zZXFfdXB9Cnp5X2dvX3VwJHB2YWx1ZV9wbG90cyRicHBfcGxvdF9vdmVyCmBgYAoKIyMjIyBnb3NlcSBvbnRvbG9neSBwbG90cyBvZiBncm91cHMgb2YgZ2VuZXMsIDIuMiBtb3JlIHRoYW4gMi4zCgpgYGB7ciBnb3NlcV9kb3dufQp6eV9nb19kb3duJHB2YWx1ZV9wbG90cyRicHBfcGxvdF9vdmVyCmBgYAoKIyMgWnltb2RlbWUgZW56eW1lIGdlbmUgSURzCgpOYWppYiByZWFkIG1lIGFuIGVtYWlsIGxpc3Rpbmcgb2ZmIHRoZSBnZW5lIG5hbWVzIGFzc29jaWF0ZWQgd2l0aCB0aGUgenltb2RlbWUKY2xhc3NpZmljYXRpb24uICBJIHRvb2sgdGhvc2UgbmFtZXMgYW5kIGNyb3NzIHJlZmVyZW5jZWQgdGhlbSBhZ2FpbnN0IHRoZQpMZWlzaG1hbmlhIHBhbmFtZW5zaXMgZ2VuZSBhbm5vdGF0aW9ucyBhbmQgZm91bmQgdGhlIGZvbGxvd2luZzoKClRoZXkgYXJlOgoKMS4gQUxBVDogTFBBTDEzXzEyMDAxMDkwMCAtLSBhbGFuaW5lIGFtaW5vdHJhbnNmZXJhc2UKMi4gQVNBVDogTFBBTDEzXzM0MDAxMzAwMCAtLSBhc3BhcnRhdGUgYW1pbm90cmFuc2ZlcmFzZQozLiBHNlBEOiBMUEFMMTNfMDAwMDU0MTAwIC0tIGdsdWNhc2UtNi1waG9zcGhhdGUgMS1kZWh5ZHJvZ2VuYXNlCjQuIE5IOiBMUEFMMTNfMTQwMDYxMDAsIExQQUwxM18xODAwMTg1MDAgLS0gaW5vc2luZS1ndWFuaW5lIG51Y2xlb3NpZGUgaHlkcm9sYXNlCjUuIE1QSTogTFBBTDEzXzMyMDAyMjMwMCAobWF5YmUpIC0tIG1hbm5vc2UgcGhvc3BoYXRlIGlzb21lcmFzZSAoSSBjaG9zZSBwaG9zcGhvbWFubm9zZSBpc29tZXJhc2UpCgpHaXZlbiB0aGVzZSA2IGdlbmUgSURzIChOSCBoYXMgdHdvIGdlbmUgSURzIGFzc29jaWF0ZWQgd2l0aCBpdCksIEkgY2FuIGRvIHNvbWUKbG9va2luZyBmb3Igc3BlY2lmaWMgZGlmZmVyZW5jZXMgYW1vbmcgdGhlIHZhcmlvdXMgc2FtcGxlcy4KCiMjIyBFeHByZXNzaW9uIGxldmVscyBvZiB6eW1vZGVtZSBnZW5lcwoKVGhlIGZvbGxvd2luZyBjcmVhdGVzIGEgY29sb3JzcGFjZSAocmVkIHRvIGdyZWVuKSBoZWF0bWFwIHNob3dpbmcgdGhlIG9ic2VydmVkCmV4cHJlc3Npb24gb2YgdGhlc2UgZ2VuZXMgaW4gZXZlcnkgc2FtcGxlLgoKYGBge3Igenltb2RlbWVzfQpteV9nZW5lcyA8LSBjKCJMUEFMMTNfMTIwMDEwOTAwIiwgIkxQQUwxM18zNDAwMTMwMDAiLCAiTFBBTDEzXzAwMDA1NDEwMCIsCiAgICAgICAgICAgICAgIkxQQUwxM18xNDAwMDYxMDAiLCAiTFBBTDEzXzE4MDAxODUwMCIsICJMUEFMMTNfMzIwMDIyMzAwIiwKICAgICAgICAgICAgICAib3RoZXIiKQpteV9uYW1lcyA8LSBjKCJBTEFUIiwgIkFTQVQiLCAiRzZQRCIsICJOSHYxIiwgIk5IdjIiLCAiTVBJIiwgIm90aGVyIikKCnp5bW9fZXhwdCA8LSBleGNsdWRlX2dlbmVzX2V4cHQoenlfbm9ybSwgaWRzID0gbXlfZ2VuZXMsIG1ldGhvZCA9ICJrZWVwIikKenltb19oZWF0bWFwIDwtIHBsb3Rfc2FtcGxlX2hlYXRtYXAoenltb19leHB0LCByb3dfbGFiZWwgPSBteV9uYW1lcykKenltb19oZWF0bWFwCmBgYAoKIyMgRW1waXJpY2FsbHkgb2JzZXJ2ZWQgWnltb2RlbWUgZ2VuZXMgZnJvbSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcwoKSW4gY29udHJhc3QsIHRoZSBmb2xsb3dpbmcgcGxvdHMgdGFrZSB0aGUgc2V0IG9mIGdlbmVzIHdoaWNoIGFyZSBzaGFyZWQgYW1vbmcKYWxsIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG1ldGhvZHMgKHxsZmN8ID49IDEuMCBhbmQgYWRqcCA8PSAwLjA1KSBhbmQgdXNlIHRoZW0KdG8gbWFrZSBjYXRlZ29yaWVzIG9mIGdlbmVzIHdoaWNoIGFyZSBpbmNyZWFzZWQgaW4gMi4zIG9yIDIuMi4KCmBgYHtyIHp5bW9kZW1lX2dlbmVzX2VtcGlyaWNhbH0Kc2hhcmVkX3p5bW8gPC0gaW50ZXJzZWN0X3NpZ25pZmljYW50KHp5X3RhYmxlKQp1cF9zaGFyZWQgPC0gc2hhcmVkX3p5bW9bWyJ1cHMiXV1bWzFdXVtbImRhdGEiXV1bWyJhbGwiXV0Kcm93bmFtZXModXBfc2hhcmVkKQp1cHNoYXJlZF9leHB0IDwtIGV4Y2x1ZGVfZ2VuZXNfZXhwdCh6eV9ub3JtLCBpZHMgPSByb3duYW1lcyh1cF9zaGFyZWQpLCBtZXRob2QgPSAia2VlcCIpCmBgYAoKV2UgY2FuIHBsb3QgYSBxdWljayBoZWF0bWFwIHRvIGdldCBhIHNlbnNlIG9mIHRoZSBkaWZmZXJlbmNlcyBvYnNlcnZlZApiZXR3ZWVuIHRoZSBnZW5lcyB3aGljaCBhcmUgZGlmZmVyZW50IGJldHdlZW4gdGhlIHR3byB6eW1vZGVtZXMuCgojIyMgSGVhdG1hcCBvZiB6eW1vZGVtZSBnZW5lIGV4cHJlc3Npb24gaW5jcmVhc2VkIGluIDIuMyB2cy4gMi4yCgpgYGB7ciB6eW1vZW1wdXB9CmhpZ2hfMjNfaGVhdG1hcCA8LSBwbG90X3NhbXBsZV9oZWF0bWFwKHVwc2hhcmVkX2V4cHQsIHJvd19sYWJlbCA9IHJvd25hbWVzKHVwX3NoYXJlZCkpCmhpZ2hfMjNfaGVhdG1hcApgYGAKCiMjIyBIZWF0bWFwIG9mIHp5bW9kZW1lIGdlbmUgZXhwcmVzc2lvbiBpbmNyZWFzZWQgaW4gMi4yIHZzLiAyLjMKCmBgYHtyIHp5bW9lbWRvd259CmRvd25fc2hhcmVkIDwtIHNoYXJlZF96eW1vW1siZG93bnMiXV1bWzFdXVtbImRhdGEiXV1bWyJhbGwiXV0KZG93bnNoYXJlZF9leHB0IDwtIGV4Y2x1ZGVfZ2VuZXNfZXhwdCh6eV9ub3JtLCBpZHMgPSByb3duYW1lcyhkb3duX3NoYXJlZCksIG1ldGhvZCA9ICJrZWVwIikKaGlnaF8yMl9oZWF0bWFwIDwtIHBsb3Rfc2FtcGxlX2hlYXRtYXAoZG93bnNoYXJlZF9leHB0LCByb3dfbGFiZWwgPSByb3duYW1lcyhkb3duX3NoYXJlZCkpCmhpZ2hfMjJfaGVhdG1hcApgYGAKCiMgU05QIHByb2ZpbGVzCgpOb3cgSSB3aWxsIGNvbWJpbmUgb3VyIHByZXZpb3VzIHNhbXBsZXMgYW5kIG91ciBuZXcgc2FtcGxlcyBpbiB0aGUKaG9wZXMgb2YgZmluZGluZyB2YXJpYW50IHBvc2l0aW9ucyB3aGljaCBoZWxwIGVsdWNpZGF0ZSBjdXJyZW50bHkKdW5rbm93biBhc3BlY3RzIG9mIGVpdGhlciBncm91cCB2aWEgdGhlaXIgY2x1c3RlcmluZyB0byBrbm93biBzYW1wbGVzCmZyb20gdGhlIG90aGVyIGdyb3VwLiBJbiBvdGhlciB3b3Jkcywgd2UgZG8gbm90IGtub3cgdGhlIHp5bW9kZW1lCmFubm90YXRpb25zIGZvciB0aGUgb2xkIHNhbXBsZXMgbm9yIHRoZSBzdHJhaW4gaWRlbnRpdGllcyAob3IgdGhlCnNob3J0Y3V0ICdjaHJvbmljIHZzLiBzZWxmLWhlYWxpbmcnKSBmb3IgdGhlIG5ldyBzYW1wbGVzLiBJIGhvcGUgdG8KbWFrZSBlZHVjYXRlZCBndWVzc2VzIGdpdmVuIHRoZSB2YXJpYW50IHByb2ZpbGVzLiBUaGVyZSBhcmUgc29tZQpkaWZmZXJlbmNlcyBpbiBob3cgdGhlIHByZXZpb3VzIGFuZCBjdXJyZW50IGRhdGEgc2V0cyB3ZXJlIGFuYWx5emVkCih0aG91Z2ggSSBoYXZlIHNpbmNlIHJlZG9uZSB0aGUgb2xkIHNhbXBsZXMgc28gaXQgc2hvdWxkIGJlIHRyaXZpYWwgdG8KcmVtb3ZlIHRob3NlIGRpZmZlcmVuY2VzIG5vdykuCgpJIGFkZGVkIG91ciAyMDE2IGRhdGEgdG8gYSBzcGVjaWZpYyBUTVJDMiBzYW1wbGUgc2hlZXQsCmRhdGVkIDIwMTkxMjAzLiAgVGh1cyBJIHdpbGwgbG9hZCB0aGUgZGF0YSBoZXJlLiAgVGhhdCBwcmV2aW91cyBkYXRhCndhcyBtYXBwZWQgdXNpbmcgdG9waGF0LCBzbyBJIHdpbGwgYWxzbyBuZWVkIHRvIG1ha2Ugc29tZSBjaGFuZ2VzIHRvCnRoZSBnZW5lIG5hbWVzIHRvIGFjY29tb2RhdGUgdGhlIHR3byBtYXBwaW5ncy4KCmBgYHtyIG9sZG5ld192YXJpYW50c30Kb2xkX2V4cHQgPC0gc20oY3JlYXRlX2V4cHQoInNhbXBsZV9zaGVldHMvdG1yYzJfc2FtcGxlc18yMDE5MTIwMy54bHN4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZV9jb2x1bW4gPSAidG9waGF0MmZpbGUiKSkKCnR0IDwtIGxwX2V4cHQkZXhwcmVzc2lvbnNldApyb3duYW1lcyh0dCkgPC0gZ3N1YihwYXR0ZXJuID0gIl5leG9uXyIsIHJlcGxhY2VtZW50ID0gIiIsIHggPSByb3duYW1lcyh0dCkpCnJvd25hbWVzKHR0KSA8LSBnc3ViKHBhdHRlcm4gPSAiXFwuRTEkIiwgcmVwbGFjZW1lbnQgPSAiIiwgeCA9IHJvd25hbWVzKHR0KSkKbHBfZXhwdCRleHByZXNzaW9uc2V0IDwtIHR0Cgp0dCA8LSBvbGRfZXhwdCRleHByZXNzaW9uc2V0CnJvd25hbWVzKHR0KSA8LSBnc3ViKHBhdHRlcm4gPSAiXmV4b25fIiwgcmVwbGFjZW1lbnQgPSAiIiwgeCA9IHJvd25hbWVzKHR0KSkKcm93bmFtZXModHQpIDwtIGdzdWIocGF0dGVybiA9ICJcXC4xJCIsIHJlcGxhY2VtZW50ID0gIiIsIHggPSByb3duYW1lcyh0dCkpCm9sZF9leHB0JGV4cHJlc3Npb25zZXQgPC0gdHQKYGBgCgojIyBDcmVhdGUgdGhlIFNOUCBleHByZXNzaW9uc2V0CgpPbmUgb3RoZXIgaW1wb3J0YW50IGNhdmVhdCwgd2UgaGF2ZSBhIGdyb3VwIG9mIG5ldyBzYW1wbGVzIHdoaWNoIGhhdmUKbm90IHlldCBydW4gdGhyb3VnaCB0aGUgdmFyaWFudCBzZWFyY2ggcGlwZWxpbmUsIHNvIEkgbmVlZCB0byByZW1vdmUKdGhlbSBmcm9tIGNvbnNpZGVyYXRpb24uICBUaG91Z2ggaXQgbG9va3MgbGlrZSB0aGV5IGZpbmlzaGVkIG92ZXJuaWdodC4uLgoKYGBge3IgY291bnRfZXhwdF9vbGRfbmV3fQojIyBUaGUgbmV4dCBsaW5lIGRyb3BzIHRoZSBzYW1wbGVzIHdoaWNoIGFyZSBtaXNzaW5nIHRoZSBTTlAgcGlwZWxpbmUuCmxwX3NucCA8LSBzdWJzZXRfZXhwdChscF9leHB0LCBzdWJzZXQ9IiFpcy5uYShwRGF0YShscF9leHB0KVtbJ2JjZnRhYmxlJ11dKSIpCm5ld19zbnBzIDwtIHNtKGNvdW50X2V4cHRfc25wcyhscF9zbnAsIGFubm90X2NvbHVtbiA9ICJiY2Z0YWJsZSIpKQpvbGRfc25wcyA8LSBzbShjb3VudF9leHB0X3NucHMob2xkX2V4cHQsIGFubm90X2NvbHVtbiA9ICJiY2Z0YWJsZSIsIHNucF9jb2x1bW4gPSAyKSkKCmJvdGhfc25wcyA8LSBjb21iaW5lX2V4cHRzKG5ld19zbnBzLCBvbGRfc25wcykKYm90aF9ub3JtIDwtIHNtKG5vcm1hbGl6ZV9leHB0KGJvdGhfc25wcywgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIsIGZpbHRlciA9IFRSVUUpKQoKIyMgc3RyYWlucyA8LSBib3RoX25vcm1bWyJkZXNpZ24iXV1bWyJzdHJhaW4iXV0KYm90aF9ub3JtIDwtIHNldF9leHB0X2NvbmRpdGlvbnMoYm90aF9ub3JtLCBmYWN0ID0gInN0cmFpbiIpCmBgYAoKVGhlIGRhdGEgc3RydWN0dXJlICdib3RoX25vcm0nIG5vdyBjb250YWlucyBvdXIgMjAxNiBkYXRhIGFsb25nIHdpdGgKdGhlIG5ld2VyIGRhdGEgY29sbGVjdGVkIHNpbmNlIDIwMTkuCgojIyBQbG90IG9mIFNOUCBwcm9maWxlcyBmb3Igenltb2RlbWVzCgpUaGUgZm9sbG93aW5nIHBsb3Qgc2hvd3MgdGhlIFNOUCBwcm9maWxlcyBvZiBhbGwgc2FtcGxlcyAob2xkIGFuZCBuZXcpIHdoZXJlIHRoZQpjb2xvcnMgYXQgdGhlIHRvcCBzaG93IGVpdGhlciB0aGUgMi4yIHN0cmFpbnMgKG9yYW5nZSksIDIuMyBzdHJhaW5zIChncmVlbiksIHRoZQpwcmV2aW91cyBzYW1wbGVzIChwdXJwbGUpLCBvciB0aGUgdmFyaW91cyBsYWIgc3RyYWlucyAocGluayBldGMpLgoKYGBge3IgcGxvdHRpbmdfdmFyaWFudHN9Cm9sZF9uZXdfdmFyaWFudF9oZWF0bWFwIDwtIHBsb3RfZGlzaGVhdChib3RoX25vcm0pCnBwKGZpbGUgPSAiaW1hZ2VzL3Jhd19zbnBfZGlzaGVhdC5wbmciLCBpbWFnZSA9IG9sZF9uZXdfdmFyaWFudF9oZWF0bWFwLAogICBoZWlnaHQgPSAxMiwgd2lkdGggPSAxMikKYGBgCgpUaGUgZnVuY3Rpb24gZ2V0X3NucF9zZXRzKCkgdGFrZXMgdGhlIHByb3ZpZGVkIG1ldGFkYXRhIGZhY3RvciAoaW4KdGhpcyBjYXNlICdjb25kaXRpb24nKSBhbmQgbG9va3MgZm9yIHZhcmlhbnRzIHdoaWNoIGFyZSBleGNsdXNpdmUgdG8KZWFjaCBlbGVtZW50IGluIGl0LiAgSW4gdGhpcyBjYXNlLCB0aGlzIGlzIGxvb2tpbmcgZm9yIGRpZmZlcmVuY2VzCmJldHdlZW4gMi4yIGFuZCAyLjMsIGFzIHdlbGwgYXMgdGhlIHNldCBzaGFyZWQgYW1vbmcgdGhlbS4KCmBgYHtyIGdldF9zbnBfc2V0czF9CnNucF9zZXRzIDwtIGdldF9zbnBfc2V0cyhib3RoX3NucHMsIGZhY3RvciA9ICJjb25kaXRpb24iKQpib3RoX2V4cHQgPC0gY29tYmluZV9leHB0cyhscF9leHB0LCBvbGRfZXhwdCkKCnNucF9nZW5lcyA8LSBzbShzbnBzX3ZzX2dlbmVzKGJvdGhfZXhwdCwgc25wX3NldHMsIGV4cHRfbmFtZV9jb2wgPSAiY2hyb21vc29tZSIpKQojIyBJIHRoaW5rIHdlIGhhdmUgc29tZSBtZXRyaWNzIGhlcmUgd2UgY2FuIHBsb3QuLi4Kc25wX3N1YnNldCA8LSBzbShzbnBfc3Vic2V0X2dlbmVzKAogIGJvdGhfZXhwdCwgYm90aF9zbnBzLAogIGdlbmVzID0gYygiTFBBTDEzXzEyMDAxMDkwMCIsICJMUEFMMTNfMzQwMDEzMDAwIiwgIkxQQUwxM18wMDAwNTQxMDAiLAogICAgICAgICAgICAiTFBBTDEzXzE0MDAwNjEwMCIsICJMUEFMMTNfMTgwMDE4NTAwIiwgIkxQQUwxM18zMjAwMjIzMDAiKSkpCiMjIHp5bW9faGVhdCA8LSBwbG90X3NhbXBsZV9oZWF0bWFwKHNucF9zdWJzZXQsIHJvd19sYWJlbCA9IHJvd25hbWVzKGV4cHJzKHNucF9zdWJzZXQpKSkKYGBgCgpEaWRuJ3QgSSBjcmVhdGUgYSBzZXQgb2YgZGVuc2l0aWVzIGJ5IGNocm9tb3NvbWU/Ck9oIEkgdGhpbmsgdGhleSBjb21lIGluIGZyb20gZ2V0X3NucF9zZXRzKCkKCiMjIFNOUFMgYXNzb2NpYXRlZCB3aXRoIGNsaW5pY2FsIHJlc3BvbnNlIGluIHRoZSBUTVJDIHNhbXBsZXMKCmBgYHtyIHNucF9jbGluaWNhbH0KY2xpbmljYWxfc2V0cyA8LSBnZXRfc25wX3NldHMobmV3X3NucHMsIGZhY3RvciA9ICJjbGluaWNhbHJlc3BvbnNlIikKCmRlbnNpdHlfdmVjIDwtIGNsaW5pY2FsX3NldHNbWyJkZW5zaXR5Il1dCmNocm9tb3NvbWVfaWR4IDwtIGdyZXAocGF0dGVybiA9ICJMcGFMIiwgeCA9IG5hbWVzKGRlbnNpdHlfdmVjKSkKZGVuc2l0eV9kZiA8LSBhcy5kYXRhLmZyYW1lKGRlbnNpdHlfdmVjW2Nocm9tb3NvbWVfaWR4XSkKZGVuc2l0eV9kZltbImNociJdXSA8LSByb3duYW1lcyhkZW5zaXR5X2RmKQpjb2xuYW1lcyhkZW5zaXR5X2RmKSA8LSBjKCJkZW5zaXR5X3ZlYyIsICJjaHIiKQpnZ3Bsb3QoZGVuc2l0eV9kZiwgYWVzX3N0cmluZyh4ID0gImNociIsIHkgPSAiZGVuc2l0eV92ZWMiKSkgKwogIGdncGxvdDI6Omdlb21fY29sKCkgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChzaXplID0gMTAsIGNvbG91ciA9ICJibGFjayIpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41KSkKCiMjIGNsaW5pY2FsX3dyaXR0ZW4gPC0gd3JpdGVfdmFyaWFudHMobmV3X3NucHMpCmBgYAoKIyMjIENyb3NzIHJlZmVyZW5jZSB0aGVzZSB2YXJpYW50cyBieSBnZW5lCgpgYGB7ciBzbnBfY2xhc3NpZmljYXRpb25zfQpjbGluaWNhbF9nZW5lcyA8LSBzbShzbnBzX3ZzX2dlbmVzKGxwX2V4cHQsIGNsaW5pY2FsX3NldHMsIGV4cHRfbmFtZV9jb2wgPSAiY2hyb21vc29tZSIpKQoKc25wX2RlbnNpdHkgPC0gbWVyZ2UoYXMuZGF0YS5mcmFtZShjbGluaWNhbF9nZW5lc1tbInN1bW1hcnlfYnlfZ2VuZSJdXSksCiAgICAgICAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUoZkRhdGEobHBfZXhwdCkpLAogICAgICAgICAgICAgICAgICAgICBieSA9ICJyb3cubmFtZXMiKQpzbnBfZGVuc2l0eSA8LSBzbnBfZGVuc2l0eVssIGMoMSwgMiwgNCwgMTUpXQpjb2xuYW1lcyhzbnBfZGVuc2l0eSkgPC0gYygibmFtZSIsICJzbnBzIiwgInByb2R1Y3QiLCAibGVuZ3RoIikKc25wX2RlbnNpdHlbWyJwcm9kdWN0Il1dIDwtIHRvbG93ZXIoc25wX2RlbnNpdHlbWyJwcm9kdWN0Il1dKQpzbnBfZGVuc2l0eVtbImxlbmd0aCJdXSA8LSBhcy5udW1lcmljKHNucF9kZW5zaXR5W1sibGVuZ3RoIl1dKQpzbnBfZGVuc2l0eVtbImRlbnNpdHkiXV0gPC0gc25wX2RlbnNpdHlbWyJzbnBzIl1dIC8gc25wX2RlbnNpdHlbWyJsZW5ndGgiXV0Kc25wX2lkeCA8LSBvcmRlcihzbnBfZGVuc2l0eVtbImRlbnNpdHkiXV0sIGRlY3JlYXNpbmcgPSBUUlVFKQpzbnBfZGVuc2l0eSA8LSBzbnBfZGVuc2l0eVtzbnBfaWR4LCBdCgpyZW1vdmVycyA8LSBjKCJhbWFzdGluIiwgImdwNjMiLCAibGVpc2htYW5vbHlzaW4iKQpmb3IgKHIgaW4gcmVtb3ZlcnMpIHsKICBkcm9wX2lkeCA8LSBncmVwbChwYXR0ZXJuID0gciwgeCA9IHNucF9kZW5zaXR5W1sicHJvZHVjdCJdXSkKICBzbnBfZGVuc2l0eSA8LSBzbnBfZGVuc2l0eVshZHJvcF9pZHgsIF0KfQojIyBGaWx0ZXIgdGhlc2UgZm9yIFtBfGFdbWFzdGluIGdwNjMgTGVpc2htYW5vbHlzaW4KYGBgCgoKYGBge3Igc25wX2ludGVyc2VjdGlvbnN9CmNsaW5pY2FsX3NucHMgPC0gc25wc19pbnRlcnNlY3Rpb25zKGxwX2V4cHQsIGNsaW5pY2FsX3NldHMsIGNocl9jb2x1bW4gPSAiY2hyb21vc29tZSIpCgpoZWFkKGFzLmRhdGEuZnJhbWUoY2xpbmljYWxfc25wcyRpbnRlcnNbWyJGYWlsdXJlIl1dKSkKaGVhZChhcy5kYXRhLmZyYW1lKGNsaW5pY2FsX3NucHMkaW50ZXJzW1siQ3VyZSJdXSkpCgpoZWFkKGNsaW5pY2FsX3NucHMkZ2VuZV9zdW1tYXJpZXMkRmFpbHVyZSkKaGVhZChjbGluaWNhbF9zbnBzJGdlbmVfc3VtbWFyaWVzJEN1cmUsIG4gPSAxMDApCgphbm5vdCA8LSBmRGF0YShscF9leHB0KQpjbGluaWNhbF9pbnRlcmVzdCA8LSBhcy5kYXRhLmZyYW1lKGNsaW5pY2FsX3NucHNbWyJnZW5lX3N1bW1hcmllcyJdXVtbIkN1cmUiXV0pCmNsaW5pY2FsX2ludGVyZXN0IDwtIG1lcmdlKGNsaW5pY2FsX2ludGVyZXN0LCBhcy5kYXRhLmZyYW1lKGNsaW5pY2FsX3NucHNbWyJnZW5lX3N1bW1hcmllcyJdXVtbIkZhaWx1cmUiXV0pLCBieSA9ICJyb3cubmFtZXMiKQpyb3duYW1lcyhjbGluaWNhbF9pbnRlcmVzdCkgPC0gY2xpbmljYWxfaW50ZXJlc3RbWyJSb3cubmFtZXMiXV0KY2xpbmljYWxfaW50ZXJlc3RbWyJSb3cubmFtZXMiXV0gPC0gTlVMTApjb2xuYW1lcyhjbGluaWNhbF9pbnRlcmVzdCkgPC0gYygiY3VyZV9zbnBzIiwiZmFpbF9zbnBzIikKYW5ub3QgPC0gbWVyZ2UoYW5ub3QsIGNsaW5pY2FsX2ludGVyZXN0LCBieSA9ICJyb3cubmFtZXMiKQpyb3duYW1lcyhhbm5vdCkgPC0gYW5ub3RbWyJSb3cubmFtZXMiXV0KYW5ub3RbWyJSb3cubmFtZXMiXV0gPC0gTlVMTApmRGF0YShscF9leHB0JGV4cHJlc3Npb25zZXQpIDwtIGFubm90CgoKYGBgCgojIFp5bW9kZW1lIGZvciBuZXcgc2FtcGxlcwoKVGhlIGhlYXRtYXAgcHJvZHVjZWQgaGVyZSBzaG91bGQgc2hvdyB0aGUgdmFyaWFudHMgb25seSBmb3IgdGhlIHp5bW9kZW1lIGdlbmVzLgoKIyMgSHVudCBmb3Igc25wIGNsdXN0ZXJzCgpJIGFtIHRoaW5raW5nIHRoYXQgaWYgd2UgZmluZCBjbHVzdGVycyBvZiBsb2NhdGlvbnMgd2hpY2ggYXJlIHZhcmlhbnQsIHRoYXQKbWlnaHQgcHJvdmlkZSBzb21lIFBDUiB0ZXN0aW5nIHBvc3NpYmlsaXRpZXMuCgpgYGB7ciBuZXdfenltb30KbmV3X3NldHMgPC0gZ2V0X3NucF9zZXRzKG5ld19zbnBzLCBmYWN0b3IgPSAicGhlbm90eXBpY2NoYXJhY3RlcmlzdGljcyIpCnN1bW1hcnkobmV3X3NldHMpCiMjIDEwMDAwMDA6IDIuMgojIyAwMTAwMDAwOiAyLjMKCnN1bW1hcnkobmV3X3NldHNbWyJpbnRlcnNlY3Rpb25zIl1dW1siMTAwMDAwIl1dKQpkaW0obmV3X3NldHMkaW50ZXJzZWN0aW9uc1tbIjEwMDAwMCJdXSkKCnNlcXVlbnRpYWxfdmFyaWFudHMgPC0gZnVuY3Rpb24oc25wX3NldHMsIGNvbmRpdGlvbnMgPSBOVUxMLCBtaW5pbXVtID0gMywgbWF4aW11bV9zZXBhcmF0aW9uID0gMykgewogIGlmIChpcy5udWxsKGNvbmRpdGlvbnMpKSB7CiAgICBjb25kaXRpb25zIDwtIDEKICB9CiAgaW50ZXJzZWN0aW9uX3NldHMgPC0gc25wX3NldHNbWyJpbnRlcnNlY3Rpb25zIl1dCiAgaW50ZXJzZWN0aW9uX25hbWVzIDwtIHNucF9zZXRzW1sic2V0X25hbWVzIl1dCiAgY2hvc2VuX2ludGVyc2VjdGlvbiA8LSAxCiAgaWYgKGlzLm51bWVyaWMoY29uZGl0aW9ucykpIHsKICAgIGNob3Nlbl9pbnRlcnNlY3Rpb24gPC0gY29uZGl0aW9ucwogIH0gZWxzZSB7CiAgICBpbnRlcnNlY3Rpb25faWR4IDwtIGludGVyc2VjdGlvbl9uYW1lcyA9PSBjb25kaXRpb25zCiAgICBjaG9zZW5faW50ZXJzZWN0aW9uIDwtIG5hbWVzKGludGVyc2VjdGlvbl9uYW1lcylbaW50ZXJzZWN0aW9uX2lkeF0KICB9CgogIHBvc3NpYmxlX3Bvc2l0aW9ucyA8LSBpbnRlcnNlY3Rpb25fc2V0c1tbY2hvc2VuX2ludGVyc2VjdGlvbl1dCiAgcG9zaXRpb25fdGFibGUgPC0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSBwb3NzaWJsZV9wb3NpdGlvbnMpCiAgcGF0IDwtICJeY2hyXyguKylfcG9zXyguKylfcmVmXy4qJCIKICBwb3NpdGlvbl90YWJsZVtbImNociJdXSA8LSBnc3ViKHBhdHRlcm4gPSBwYXQsIHJlcGxhY2VtZW50ID0gIlxcMSIsIHggPSByb3duYW1lcyhwb3NpdGlvbl90YWJsZSkpCiAgcG9zaXRpb25fdGFibGVbWyJwb3MiXV0gPC0gYXMubnVtZXJpYyhnc3ViKHBhdHRlcm4gPSBwYXQsIHJlcGxhY2VtZW50ID0gIlxcMiIsIHggPSByb3duYW1lcyhwb3NpdGlvbl90YWJsZSkpKQogIHBvc2l0aW9uX2lkeCA8LSBvcmRlcihwb3NpdGlvbl90YWJsZVssICJjaHIiXSwgcG9zaXRpb25fdGFibGVbLCAicG9zIl0pCiAgcG9zaXRpb25fdGFibGUgPC0gcG9zaXRpb25fdGFibGVbcG9zaXRpb25faWR4LCBdCiAgcG9zaXRpb25fdGFibGVbWyJkaXN0Il1dIDwtIDAKCiAgbGFzdF9jaHIgPC0gIiIKICBmb3IgKHIgaW4gMTpucm93KHBvc2l0aW9uX3RhYmxlKSkgewogICAgdGhpc19jaHIgPC0gcG9zaXRpb25fdGFibGVbciwgImNociJdCiAgICBpZiAociA9PSAxKSB7CiAgICAgIHBvc2l0aW9uX3RhYmxlW3IsICJkaXN0Il0gPC0gcG9zaXRpb25fdGFibGVbciwgInBvcyJdCiAgICAgIGxhc3RfY2hyIDwtIHRoaXNfY2hyCiAgICAgIG5leHQKICAgIH0KICAgIGlmICh0aGlzX2NociA9PSBsYXN0X2NocikgewogICAgICBwb3NpdGlvbl90YWJsZVtyLCAiZGlzdCJdIDwtIHBvc2l0aW9uX3RhYmxlW3IsICJwb3MiXSAtIHBvc2l0aW9uX3RhYmxlW3IgLSAxLCAicG9zIl0KICAgIH0gZWxzZSB7CiAgICAgIHBvc2l0aW9uX3RhYmxlW3IsICJkaXN0Il0gPC0gcG9zaXRpb25fdGFibGVbciwgInBvcyJdCiAgICB9CiAgICBsYXN0X2NociA8LSB0aGlzX2NocgogIH0KCiAgc2VxdWVudGlhbHMgPC0gcG9zaXRpb25fdGFibGVbWyJkaXN0Il1dIDw9IG1heGltdW1fc2VwYXJhdGlvbgoKICAjIyBUaGUgZm9sbG93aW5nIGNhbiB0ZWxsIG1lIGhvdyBtYW55IHJ1bnMgb2YgZWFjaCBsZW5ndGggb2NjdXJyZWQsIHRoYXQgaXMgbm90IHF1aXRlIHdoYXQgSSB3YW50LgogICMjIE5vdyB1c2UgcnVuIGxlbmd0aCBlbmNvZGluZyB0byBmaW5kIHRoZSBzZXQgb2Ygc2VxdWVudGlhbCBzZXF1ZW50aWFscyEKICBybGVfcmVzdWx0IDwtIHJsZShzZXF1ZW50aWFscykKICBybGVfdmFsdWVzIDwtIHJsZV9yZXN1bHRbWyJ2YWx1ZXMiXV0KICAjIyBUaGUgZm9sbG93aW5nIGxpbmUgaXMgZXF1aXZhbGVudCB0byBqdXN0IGxlYXZpbmcgdmFsdWVzIGFsb25lOgogICMjIHRydWVfdmFsdWVzIDwtIHJsZV9yZXN1bHRbWyJ2YWx1ZXMiXV0gPT0gVFJVRQogIHJsZV9sZW5ndGhzIDwtIHJsZV9yZXN1bHRbWyJsZW5ndGhzIl1dCiAgdHJ1ZV9zZXF1ZW50aWFscyA8LSBybGVfbGVuZ3Roc1tybGVfdmFsdWVzXQogIHJsZV9pZHggPC0gY3Vtc3VtKHJsZV9sZW5ndGhzKVt3aGljaChybGVfdmFsdWVzKV0KCiAgcG9zaXRpb25fdGFibGVbWyJsYXN0X3NlcXVlbnRpYWwiXV0gPC0gMAogIGNvdW50IDwtIDAKICBmb3IgKHIgaW4gcmxlX2lkeCkgewogICAgY291bnQgPC0gY291bnQgKyAxCiAgICBwb3NpdGlvbl90YWJsZVtyLCAibGFzdF9zZXF1ZW50aWFsIl0gPC0gdHJ1ZV9zZXF1ZW50aWFsc1tjb3VudF0KICB9CgogIHdhbnRlZF9pZHggPC0gcG9zaXRpb25fdGFibGVbWyJsYXN0X3NlcXVlbnRpYWwiXV0gPj0gbWluaW11bQogIHdhbnRlZCA8LSBwb3NpdGlvbl90YWJsZVt3YW50ZWRfaWR4LCBjKCJjaHIiLCAicG9zIildCiAgcmV0dXJuKHdhbnRlZCkKfQoKenltbzIyX3NlcXVlbnRpYWxzIDwtIHNlcXVlbnRpYWxfdmFyaWFudHMobmV3X3NldHMsIGNvbmRpdGlvbnMgPSAiMi4yIikKenltbzIyX3NlcXVlbnRpYWxzCnp5bW8yM19zZXF1ZW50aWFscyA8LSBzZXF1ZW50aWFsX3ZhcmlhbnRzKG5ld19zZXRzLCBjb25kaXRpb25zID0gIjIuMyIpCnp5bW8yM19zZXF1ZW50aWFscwpgYGAKCmBgYHtyIHp5bW9faGVhdG1hcHN9CnNucF9nZW5lcyA8LSBzbShzbnBzX3ZzX2dlbmVzKGxwX2V4cHQsIG5ld19zZXRzLCBleHB0X25hbWVfY29sID0gImNocm9tb3NvbWUiKSkKbmV3X3p5bW9fbm9ybSAgPC0gbm9ybWFsaXplX2V4cHQobmV3X3NucHMsIGZpbHRlciA9IFRSVUUsIGNvbnZlcnQgPSAiY3BtIiwgbm9ybSA9ICJxdWFudCIsIHRyYW5zZm9ybSA9IFRSVUUpCm5ld196eW1vX25vcm0gPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhuZXdfenltb19ub3JtLCBmYWN0ID0gInBoZW5vdHlwaWNjaGFyYWN0ZXJpc3RpY3MiKQp6eW1vX2hlYXQgPC0gcGxvdF9kaXNoZWF0KG5ld196eW1vX25vcm0pCgp6eW1vX3N1YnNldCA8LSBzbnBfc3Vic2V0X2dlbmVzKGxwX2V4cHQsIG5ld19zbnBzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVzID0gYygiTFBBTDEzXzEyMDAxMDkwMCIsICJMUEFMMTNfMzQwMDEzMDAwIiwgIkxQQUwxM18wMDAwNTQxMDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxQQUwxM18xNDAwMDYxMDAiLCAiTFBBTDEzXzE4MDAxODUwMCIsICJMUEFMMTNfMzIwMDIyMzAwIikpCgp6eW1vX3N1YnNldCA8LSBzZXRfZXhwdF9jb25kaXRpb25zKHp5bW9fc3Vic2V0LCBmYWN0ID0gInBoZW5vdHlwaWNjaGFyYWN0ZXJpc3RpY3MiKQojIyB6eW1vX2hlYXQgPC0gcGxvdF9zYW1wbGVfaGVhdG1hcCh6eW1vX3N1YnNldCwgcm93X2xhYmVsID0gcm93bmFtZXMoZXhwcnMoc25wX3N1YnNldCkpKQoKZGVzIDwtIGJvdGhfbm9ybSRkZXNpZ24KdW5kZWZfaWR4IDwtIGlzLm5hKGRlc1tbInN0cmFpbiJdXSkKZGVzW3VuZGVmX2lkeCwgInN0cmFpbiJdIDwtICJ1bmtub3duIgoKIyNobWNvbHMgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJ5ZWxsb3ciLCJibGFjayIsImRhcmtibHVlIikpKDI1NikKY29ycmVsYXRpb25zIDwtIGhwZ2xfY29yKGV4cHJzKGJvdGhfbm9ybSkpCgp6eW1vX21pc3NpbmdfaWR4IDwtIGlzLm5hKGRlc1tbInBoZW5vdHlwaWNjaGFyYWN0ZXJpc3RpY3MiXV0pCmRlc1t6eW1vX21pc3NpbmdfaWR4LCAicGhlbm90eXBpY2NoYXJhY3RlcmlzdGljcyJdIDwtICJ1bmtub3duIgpteWRlbmRybyA8LSBsaXN0KAogICJjbHVzdGZ1biIgPSBoY2x1c3QsCiAgImx3ZCIgPSAyLjApCmNvbF9kYXRhIDwtIGFzLmRhdGEuZnJhbWUoZGVzWywgYygicGhlbm90eXBpY2NoYXJhY3RlcmlzdGljcyIsICJjbGluaWNhbGNhdGVnb3JpY2FsIildKQp1bmtub3duX2NsaW5pY2FsIDwtIGlzLm5hKGNvbF9kYXRhW1siY2xpbmljYWxjYXRlZ29yaWNhbCJdXSkKcm93X2RhdGEgPC0gYXMuZGF0YS5mcmFtZShkZXNbLCBjKCJzdHJhaW4iKV0pCmNvbG5hbWVzKGNvbF9kYXRhKSA8LSBjKCJ6eW1vZGVtZSIsICJvdXRjb21lIikKY29sX2RhdGFbdW5rbm93bl9jbGluaWNhbCwgIm91dGNvbWUiXSA8LSAidW5kZWZpbmVkIgoKY29sbmFtZXMocm93X2RhdGEpIDwtIGMoInN0cmFpbiIpCm15YW5ub3QgPC0gbGlzdCgKICAiQ29sIiA9IGxpc3QoImRhdGEiID0gY29sX2RhdGEpLAogICJSb3ciID0gbGlzdCgiZGF0YSIgPSByb3dfZGF0YSkpCm15Y2x1c3QgPC0gbGlzdCgiY3V0aCIgPSAxLjAsCiAgICAgICAgICAgICAgICAiY29sIiA9IEJyZXdlckNsdXN0ZXJDb2wpCm15bGFicyA8LSBsaXN0KAogICJSb3ciID0gbGlzdCgibnJvdyIgPSA0KSwKICAiQ29sIiA9IGxpc3QoIm5yb3ciID0gNCkpCmhtY29scyA8LSBjb2xvclJhbXBQYWxldHRlKGMoImRhcmtibHVlIiwgImJlaWdlIikpKDI0MCkKbWFwMSA8LSBhbm5IZWF0bWFwMigKICBjb3JyZWxhdGlvbnMsCiAgZGVuZHJvZ3JhbSA9IG15ZGVuZHJvLAogIGFubm90YXRpb24gPSBteWFubm90LAogIGNsdXN0ZXIgPSBteWNsdXN0LAogIGxhYmVscyA9IG15bGFicywKICAjIyBUaGUgZm9sbG93aW5nIGNvbnRyb2xzIGlmIHRoZSBwaWN0dXJlIGlzIHN5bW1ldHJpYwogIHNjYWxlID0gIm5vbmUiLAogIGNvbCA9IGhtY29scykKcHAoZmlsZSA9ICJpbWFnZXMvZGVuZHJvX2hlYXRtYXAucG5nIiwgaW1hZ2UgPSBtYXAxLCBoZWlnaHQ9MTIsIHdpZHRoID0gMTIpCiMjIHBsb3QobWFwMSkKYGBgCgojIFVzaW5nIFZhcmlhbnQgcHJvZmlsZXMgdG8gbWFrZSBndWVzc2VzIGFib3V0IHN0cmFpbnMgYW5kIGNocm9uaWMvc2VsZi1oZWFsaW5nCgpUaGUgZm9sbG93aW5nIHVzZXMgdGhlIHNhbWUgaW5mb3JtYXRpb24gdG8gbWFrZSBzb21lIGd1ZXNzZXMgYWJvdXQgdGhlIHN0cmFpbnMKdXNlZCBpbiB0aGUgbmV3IHNhbXBsZXMuCgpgYGB7ciBvbGRfYW5kX25ld19jaHJvbmljfQpkZXMgPC0gYm90aF9ub3JtJGRlc2lnbgp1bmRlZl9pZHggPC0gaXMubmEoZGVzW1sic3RyYWluIl1dKQpkZXNbdW5kZWZfaWR4LCAic3RyYWluIl0gPC0gInVua25vd24iCiMjaG1jb2xzIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygieWVsbG93IiwiYmxhY2siLCJkYXJrYmx1ZSIpKSgyNTYpCmNvcnJlbGF0aW9ucyA8LSBocGdsX2NvcihleHBycyhib3RoX25vcm0pKQoKbXlkZW5kcm8gPC0gbGlzdCgKICAiY2x1c3RmdW4iID0gaGNsdXN0LAogICJsd2QiID0gMi4wKQpjb2xfZGF0YSA8LSBhcy5kYXRhLmZyYW1lKGRlc1ssIGMoImNvbmRpdGlvbiIpXSkKcm93X2RhdGEgPC0gYXMuZGF0YS5mcmFtZShkZXNbLCBjKCJzdHJhaW4iKV0pCmNvbG5hbWVzKGNvbF9kYXRhKSA8LSBjKCJjb25kaXRpb24iKQpjb2xuYW1lcyhyb3dfZGF0YSkgPC0gYygic3RyYWluIikKbXlhbm5vdCA8LSBsaXN0KAogICJDb2wiID0gbGlzdCgiZGF0YSIgPSBjb2xfZGF0YSksCiAgIlJvdyIgPSBsaXN0KCJkYXRhIiA9IHJvd19kYXRhKSkKbXljbHVzdCA8LSBsaXN0KCJjdXRoIiA9IDEuMCwKICAgICAgICAgICAgICAgICJjb2wiID0gQnJld2VyQ2x1c3RlckNvbCkKbXlsYWJzIDwtIGxpc3QoCiAgIlJvdyIgPSBsaXN0KCJucm93IiA9IDQpLAogICJDb2wiID0gbGlzdCgibnJvdyIgPSA0KSkKaG1jb2xzIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiZGFya2JsdWUiLCAiYmVpZ2UiKSkoMTcwKQptYXAxIDwtIGFubkhlYXRtYXAyKAogIGNvcnJlbGF0aW9ucywKICBkZW5kcm9ncmFtID0gbXlkZW5kcm8sCiAgYW5ub3RhdGlvbiA9IG15YW5ub3QsCiAgY2x1c3RlciA9IG15Y2x1c3QsCiAgbGFiZWxzID0gbXlsYWJzKQojIyAgY29sID0gaG1jb2xzKQpwbG90KG1hcDEpCmBgYAoKYGBge3IgdGhlcmVzYV9pZGVhfQpwaGVubyA8LSBzdWJzZXRfZXhwdChscF9leHB0LCBzdWJzZXQgPSAiY29uZGl0aW9uPT0nejIuMid8Y29uZGl0aW9uPT0nejIuMyciKQpwaGVubyA8LSBzdWJzZXRfZXhwdChwaGVubywgc3Vic2V0PSIhaXMubmEocERhdGEocGhlbm8pW1snYmNmdGFibGUnXV0pIikKcGhlbm9fc25wcyA8LSBzbShjb3VudF9leHB0X3NucHMocGhlbm8sIGFubm90X2NvbHVtbiA9ICJiY2Z0YWJsZSIpKQoKeHJlZl9wcm9wIDwtIHRhYmxlKHBoZW5vX3NucHMkY29uZGl0aW9ucykKcGhlbm9fc25wcyRjb25kaXRpb25zCmlkeF90YmwgPC0gZXhwcnMocGhlbm9fc25wcykgPiA1Cm5ld190YmwgPC0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSByb3duYW1lcyhleHBycyhwaGVub19zbnBzKSkpCmZvciAobiBpbiBuYW1lcyh4cmVmX3Byb3ApKSB7CiAgbmV3X3RibFtbbl1dIDwtIDAKICBpZHhfY29scyA8LSB3aGljaChwaGVub19zbnBzW1siY29uZGl0aW9ucyJdXSA9PSBuKQogIHByb3BfY29sIDwtIHJvd1N1bXMoaWR4X3RibFssIGlkeF9jb2xzXSkgLyB4cmVmX3Byb3Bbbl0KICBuZXdfdGJsW25dIDwtIHByb3BfY29sCn0KbmV3X3RibFtbInJhdGlvIl1dIDwtIChuZXdfdGJsW1siejIuMiJdXSAtIG5ld190YmxbWyJ6Mi4zIl1dKQprZWVwZXJzIDwtIGdyZXBsKHggPSByb3duYW1lcyhuZXdfdGJsKSwgcGF0dGVybiA9ICJMcGFMMTMiKQpuZXdfdGJsIDwtIG5ld190Ymxba2VlcGVycywgXQpuZXdfdGJsW1siU05QIl1dIDwtIHJvd25hbWVzKG5ld190YmwpCm5ld190YmxbWyJDaHJvbW9zb21lIl1dIDwtIGdzdWIoeCA9IG5ld190YmxbWyJTTlAiXV0sIHBhdHRlcm4gPSAiY2hyXyguKilfcG9zXy4qIiwgcmVwbGFjZW1lbnQgPSAiXFwxIikKbmV3X3RibFtbIlBvc2l0aW9uIl1dIDwtIGdzdWIoeCA9IG5ld190YmxbWyJTTlAiXV0sIHBhdHRlcm4gPSAiLipfcG9zXyhcXGQrKV8uKiIsIHJlcGxhY2VtZW50ID0gIlxcMSIpCm5ld190YmwgPC0gbmV3X3RibFssIGMoIlNOUCIsICJDaHJvbW9zb21lIiwgIlBvc2l0aW9uIiwgInJhdGlvIildCmxpYnJhcnkoQ01wbG90KQpDTXBsb3QobmV3X3RibCkKYGBgCgpgYGB7ciBzYXZlbWV9CmlmICghaXNUUlVFKGdldDAoInNraXBfbG9hZCIpKSkgewogIHBhbmRlcjo6cGFuZGVyKHNlc3Npb25JbmZvKCkpCiAgbWVzc2FnZShwYXN0ZTAoIlRoaXMgaXMgaHBnbHRvb2xzIGNvbW1pdDogIiwgZ2V0X2dpdF9jb21taXQoKSkpCiAgbWVzc2FnZShwYXN0ZTAoIlNhdmluZyB0byAiLCBzYXZlZmlsZSkpCiAgdG1wIDwtIHNtKHNhdmVtZShmaWxlbmFtZSA9IHNhdmVmaWxlKSkKfQpgYGAKCmBgYHtyIGxvYWRtZV9hZnRlciwgZXZhbCA9IEZBTFNFfQp0bXAgPC0gbG9hZG1lKGZpbGVuYW1lID0gc2F2ZWZpbGUpCmBgYAo=