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="all")$genes)

lp_go <- sm(load_orgdb_go(pan_db))
lp_lengths <- all_lp_annot[, c("gid", "length")]
colnames(lp_lengths)  <- c("ID", "length")

orthos <- sm(EuPathDB::extract_eupath_orthologs(db=pan_db))

hisat_annot <- all_lp_annot
## rownames(hisat_annot) <- paste0("exon_", rownames(hisat_annot), ".E1")

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

3.1 Generate expressionsets

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

lp_expt <- sm(create_expt("sample_sheets/tmrc2_samples_20200915.xlsx",
                          gene_info=hisat_annot,
                          file_column="lpanamensisv36hisatfile"))

libsizes <- plot_libsize(lp_expt)
## The scale difference between the smallest and largest
## libraries is > 10. Assuming a log10 scale is better, set scale=FALSE if not.
libsizes$plot

## I think samples 7,10 should be removed at minimum, probably also 9,11
nonzero <- plot_nonzero(lp_expt)
nonzero$plot

plot_boxplot(lp_expt)
## This data will benefit from being displayed on the log scale.
## If this is not desired, set scale='raw'
## Some entries are 0.  We are on log scale, adding 1 to the data.
## Changed 1920 zero count features.

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

pheno_expt <- set_expt_conditions(lp_expt, fact="phenotypiccharacteristics")
all_norm <- normalize_expt(pheno_expt, norm="quant", transform="log2", convert="cpm",
                           batch=FALSE, filter=TRUE)
## This function will replace the expt$expressionset slot with:
## log2(cpm(quant(cbcb(data))))
## It will save copies of each step along the way
##  in expt$normalized with the corresponding libsizes. Keep libsizes in mind
##  when invoking limma.  The appropriate libsize is non-log(cpm(normalized)).
##  This is most likely kept at:
##  'new_expt$normalized$intermediate_counts$normalization$libsizes'
##  A copy of this may also be found at:
##  new_expt$best_libsize
## Not correcting the count-data for batch effects.  If batch is
##  included in EdgerR/limma's model, then this is probably wise; but in extreme
##  batch effects this is a good parameter to play with.
## Step 1: performing count filter with option: cbcb
## Removing 155 low-count genes (8623 remaining).
## Step 2: normalizing the data with quant.
## Step 3: converting the data with cpm.
## Step 4: transforming the data with log2.
## transform_counts: Found 17 values equal to 0, adding 1 to the matrix.
## Step 5: not doing batch correction.
plot_pca(all_norm)$plot
## Warning in MASS::cov.trob(data[, vars]): Probable convergence failure

## Warning in MASS::cov.trob(data[, vars]): Probable convergence failure

## Note, samples 2, 6, 7, 8 are all very low coverage compared to the later samples.

plot_tsne(all_norm)$plot

plot_corheat(all_norm)$plot

plot_sm(all_norm)$plot
## Performing correlation.

## plot_variance_coefficients(all_norm)$plot
## plot_sample_cvheatmap(all_norm)$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’.

zy_expt <- subset_expt(pheno_expt, subset="condition=='z2.2'|condition=='z2.3'")
## Using a subset expression.
## There were 17, now there are 14 samples.
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_table$plots[[1]][["deseq_ma_plots"]][["plot"]]

zy_sig <- sm(extract_significant_genes(zy_table, excel=glue::glue("excel/zy_sig-v{ver}.xlsx")))

lp_go <- lp_go[, c("GID", "GOALL")]
## Error in `[.data.frame`(lp_go, , c("GID", "GOALL")): undefined columns selected
colnames(lp_go) <- c("ID", "GO")

## 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))

zy_go_up$pvalue_plots$bpp_plot_over

## 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))

zy_go_down$pvalue_plots$bpp_plot_over

4 Zymodeme genes

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.

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(all_norm, ids=my_genes, method="keep")
## Before removal, there were 8623 entries.
## Now there are 6 entries.
## Percent kept: 0.086, 0.081, 0.084, 0.084, 0.083, 0.086, 0.083, 0.084, 0.083, 0.084, 0.083, 0.083, 0.085, 0.085, 0.083, 0.083, 0.083
## Percent removed: 99.914, 99.919, 99.916, 99.916, 99.917, 99.914, 99.917, 99.916, 99.917, 99.916, 99.917, 99.917, 99.915, 99.915, 99.917, 99.917, 99.917
test <- plot_sample_heatmap(zymo_expt, row_label=my_names)

5 Variant profiles

In this block, I am combining our previous samples and our new samples in the hopes of finding variant positions which help elucidate aspects of either the new or old samples. 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. We may be able 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).

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

new_snps <- sm(count_expt_snps(lp_expt, 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=strains)
## Error in names(object) <- nm: 'names' attribute [11] must be the same length as the vector [10]
tt <- plot_disheat(both_norm)

snp_sets <- get_snp_sets(both_snps, factor="condition")
## The factor undefined has 17 rows.
## The factor sh has 13 rows.
## The factor chr has 14 rows.
## The factor inf has 6 rows.
## Iterating over 722 elements.
both_expt <- combine_expts(lp_expt, old_expt)
snp_genes <- sm(snps_vs_genes(both_expt, snp_sets, expt_name_col="chromosome"))

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

6 Zymodeme for new samples

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

new_sets <- get_snp_sets(new_snps, factor="phenotypiccharacteristics")
## The factor Laboratory line has 3 rows.
## The factor Laboratory line antimony resistant  has only 1 row.
## The factor Laboratory line miltefosine resistant  has only 1 row.
## The factor z2.2 has 8 rows.
## The factor z2.3 has 6 rows.
## Iterating over 620 elements.
snp_genes <- sm(snps_vs_genes(pheno_expt, new_sets, expt_name_col="chromosome"))
new_zymo_norm  <-  normalize_expt(new_snps, filter=TRUE, convert="cpm", norm="quant", transform=TRUE)
## This function will replace the expt$expressionset slot with:
## log2(cpm(quant(cbcb(data))))
## It will save copies of each step along the way
##  in expt$normalized with the corresponding libsizes. Keep libsizes in mind
##  when invoking limma.  The appropriate libsize is non-log(cpm(normalized)).
##  This is most likely kept at:
##  'new_expt$normalized$intermediate_counts$normalization$libsizes'
##  A copy of this may also be found at:
##  new_expt$best_libsize
## Not correcting the count-data for batch effects.  If batch is
##  included in EdgerR/limma's model, then this is probably wise; but in extreme
##  batch effects this is a good parameter to play with.
## Step 1: performing count filter with option: cbcb
## Removing 0 low-count genes (133649 remaining).
## Step 2: normalizing the data with quant.
## Step 3: converting the data with cpm.
## Step 4: transforming the data with log2.
## transform_counts: Found 209094 values equal to 0, adding 1 to the matrix.
## Step 5: not doing batch correction.
new_zymo_norm <- set_expt_conditions(new_zymo_norm, fact="phenotypiccharacteristics")
zymo_heat <- plot_disheat(new_zymo_norm)

zymo_subset <- sm(snp_subset_genes(
  pheno_expt, new_snps,
  genes=c("LPAL13_120010900", "LPAL13_340013000", "LPAL13_000054100",
          "LPAL13_140006100", "LPAL13_180018500", "LPAL13_320022300")))

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"
library(Heatplus)
##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")])
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)
## Warning in breakColors(breaks, col): more colors than classes: ignoring 3 last
## colors
plot(map1)

7 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)
## Warning in breakColors(breaks, col): more colors than classes: ignoring 3 last
## colors
plot(map1)

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 4f025ebdf7b19ddfef0cf9ddaa9ebe2857477394
## This is hpgltools commit: Wed Aug 19 10:11:52 2020 -0400: 4f025ebdf7b19ddfef0cf9ddaa9ebe2857477394
## Saving to 01_annotation_v202009.rda.xz
tmp <- loadme(filename=savefile)
LS0tCnRpdGxlOiAiTC4gcGFuYW1lbnNpcyAyMDIwMDk6IFNhbXBsZSBFc3RpbWF0aW9uIgphdXRob3I6ICJhdGIgYWJlbGV3QGdtYWlsLmNvbSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiBodG1sX2RvY3VtZW50OgogIGNvZGVfZG93bmxvYWQ6IHRydWUKICBjb2RlX2ZvbGRpbmc6IHNob3cKICBmaWdfY2FwdGlvbjogdHJ1ZQogIGZpZ19oZWlnaHQ6IDcKICBmaWdfd2lkdGg6IDcKICBoaWdobGlnaHQ6IGRlZmF1bHQKICBrZWVwX21kOiBmYWxzZQogIG1vZGU6IHNlbGZjb250YWluZWQKICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQogIHRoZW1lOiByZWFkYWJsZQogIHRvYzogdHJ1ZQogIHRvY19mbG9hdDoKICAgY29sbGFwc2VkOiBmYWxzZQogICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQotLS0KCjxzdHlsZT4KICBib2R5IC5tYWluLWNvbnRhaW5lciB7CiAgICBtYXgtd2lkdGg6IDE2MDBweDsKICB9Cjwvc3R5bGU+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlPUZBTFNFfQppZiAoIWlzVFJVRShnZXQwKCJza2lwX2xvYWQiKSkpIHsKICBsaWJyYXJ5KGhwZ2x0b29scykKICB0dCA8LSBzbShkZXZ0b29sczo6bG9hZF9hbGwoIi9kYXRhL2hwZ2x0b29scyIpKQogIGtuaXRyOjpvcHRzX2tuaXQkc2V0KHByb2dyZXNzPVRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgIHdpZHRoPTkwLAogICAgICAgICAgICAgICAgICAgICAgIGVjaG89VFJVRSkKICBrbml0cjo6b3B0c19jaHVuayRzZXQoZXJyb3I9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoPTgsCiAgICAgICAgICAgICAgICAgICAgICAgIGZpZy5oZWlnaHQ9OCwKICAgICAgICAgICAgICAgICAgICAgICAgZHBpPTk2KQogIG9sZF9vcHRpb25zIDwtIG9wdGlvbnMoZGlnaXRzPTQsCiAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAga25pdHIuZHVwbGljYXRlLmxhYmVsPSJhbGxvdyIpCiAgZ2dwbG90Mjo6dGhlbWVfc2V0KGdncGxvdDI6OnRoZW1lX2J3KGJhc2Vfc2l6ZT0xMikpCiAgdmVyIDwtICIyMDIwMDkiCiAgcHJldmlvdXNfZmlsZSA8LSBwYXN0ZTAoIjAxX2Fubm90YXRpb25fdiIsIHZlciwgIi5SbWQiKQogIHJ1bmRhdGUgPC0gZm9ybWF0KFN5cy5EYXRlKCksIGZvcm1hdD0iJVklbSVkIikKCiAgdG1wIDwtIHRyeShzbShsb2FkbWUoZmlsZW5hbWU9Z3N1YihwYXR0ZXJuPSJcXC5SbWQiLCByZXBsYWNlPSJcXC5yZGFcXC54eiIsIHg9cHJldmlvdXNfZmlsZSkpKSkKICBybWRfZmlsZSA8LSBwYXN0ZTAoIjAxX2Fubm90YXRpb25fdiIsIHZlciwgIi5SbWQiKQogIHNhdmVmaWxlIDwtIGdzdWIocGF0dGVybj0iXFwuUm1kIiwgcmVwbGFjZT0iXFwucmRhXFwueHoiLCB4PXJtZF9maWxlKQp9CmBgYAoKIyBJbnRyb2R1Y3Rpb24KClRoaXMgZG9jdW1lbnQgaXMgaW50ZW5kZWQgdG8gcHJvdmlkZSBhIGdlbmVyYWwgb3ZlcnZpZXcgb2YgdGhlIFRNUkMyIHNhbXBsZXMKd2hpY2ggaGF2ZSB0aHVzIGZhciBiZWVuIHNlcXVlbmNlZC4gIEluIHNvbWUgY2FzZXMsIHRoaXMgaW5jbHVkZXMgb25seSB0aG9zZQpzYW1wbGVzIHN0YXJ0aW5nIGluIDIwMTk7IGluIG90aGVyIGluc3RhbmNlcyBJIGFtIGluY2x1ZGluZyBvdXIgcHJldmlvdXMKKDIwMTUtMjAxNikgc2FtcGxlcy4KCkluIGFsbCBjYXNlcyB0aGUgcHJvY2Vzc2luZyBwZXJmb3JtZWQgd2FzOgoKMS4gIERlZmF1bHQgdHJpbW1pbmcgd2FzIHBlcmZvcm1lZC4KMi4gIEhpc2F0MiB3YXMgdXNlZCB0byBtYXAgdGhlIHJlbWFpbmluZyByZWFkcyBhZ2FpbnN0IHRoZSBMZWlzaG1hbmlhCiAgICBwYW5hbWVuc2lzIGdlbm9tZSByZXZpc2lvbiAzNi4KMy4gIFRoZSBhbGlnbm1lbnRzIGZyb20gaGlzYXQyIHdlcmUgdXNlZCB0byBjb3VudCByZWFkcy9nZW5lIGFnYWluc3QgdGhlCiAgICByZXZpc2lvbiAzNiBhbm5vdGF0aW9ucyB3aXRoIGh0c2VxLgo0LiAgVGhlc2UgYWxpZ25tZW50cyB3ZXJlIGFsc28gcGFzc2VkIHRvIHRoZSBwaWxldXAgZnVuY3Rpb25hbGl0eSBvZiBzYW10b29scwogICAgYW5kIHRoZSB2Y2YvYmNmIHV0aWxpdGllcyBpbiBvcmRlciB0byBtYWtlIGEgbWF0cml4IG9mIGFsbCBvYnNlcnZlZAogICAgZGlmZmVyZW5jZXMgYmV0d2VlbiBlYWNoIHNhbXBsZSB3aXRoIHJlc3BlY3QgdG8gdGhlIHJlZmVyZW5jZS4KClRoZSBhbmFseXNlcyBpbiB0aGlzIGRvY3VtZW50IHVzZSB0aGUgbWF0cmljZXMgb2YgY291bnRzL2dlbmUgZnJvbSAjMyBhbmQKdmFyaWFudHMvcG9zaXRpb24gZnJvbSAjNCBpbiBvcmRlciB0byBwcm92aWRlIHNvbWUgaW1hZ2VzIGFuZCBtZXRyaWNzIGRlc2NyaWJpbmcKdGhlIHNhbXBsZXMgd2UgaGF2ZSBzZXF1ZW5jZWQgc28gZmFyLgoKIyBBbm5vdGF0aW9ucwoKRXZlcnl0aGluZyB3aGljaCBmb2xsb3dzIGRlcGVuZHMgb24gdGhlIEV4aXN0aW5nIFRyaVRyeXBEQiBhbm5vdGF0aW9ucyByZXZpc2lvbgo0NiwgY2lyY2EgMjAxOS4gIFRoZSBmb2xsb3dpbmcgYmxvY2sgbG9hZHMgYSBkYXRhYmFzZSBvZiB0aGVzZSBhbm5vdGF0aW9ucyBhbmQKdHVybnMgaXQgaW50byBhIG1hdHJpeCB3aGVyZSB0aGUgcm93cyBhcmUgZ2VuZXMgYW5kIGNvbHVtbnMgYXJlIGFsbCB0aGUKYW5ub3RhdGlvbiB0eXBlcyBwcm92aWRlZCBieSBUcmlUcnlwREIuCgpUaGUgc2FtZSBkYXRhYmFzZSB3YXMgdXNlZCB0byBjcmVhdGUgYSBtYXRyaXggb2Ygb3J0aG9sb2dvdXMgZ2VuZXMgYmV0d2VlbgpMLnBhbmFtZW5zaXMgYW5kIGFsbCBvZiB0aGUgb3RoZXIgc3BlY2llcyBpbiB0aGUgVHJpVHJ5cERCLgoKYGBge3IgYW5ub3R9CnR0IDwtIHNtKGxpYnJhcnkoRXVQYXRoREIpKQp0dCA8LSBzbShsaWJyYXJ5KG9yZy5McGFuYW1lbnNpcy5NSE9NQ09MODFMMTMudjQ2LmVnLmRiKSkKcGFuX2RiIDwtIG9yZy5McGFuYW1lbnNpcy5NSE9NQ09MODFMMTMudjQ2LmVnLmRiCmFsbF9maWVsZHMgPC0gY29sdW1ucyhwYW5fZGIpCgphbGxfbHBfYW5ub3QgPC0gc20obG9hZF9vcmdkYl9hbm5vdGF0aW9ucygKICBwYW5fZGIsCiAga2V5dHlwZT0iZ2lkIiwKICBmaWVsZHM9ImFsbCIpJGdlbmVzKQoKbHBfZ28gPC0gc20obG9hZF9vcmdkYl9nbyhwYW5fZGIpKQpscF9sZW5ndGhzIDwtIGFsbF9scF9hbm5vdFssIGMoImdpZCIsICJsZW5ndGgiKV0KY29sbmFtZXMobHBfbGVuZ3RocykgIDwtIGMoIklEIiwgImxlbmd0aCIpCgpvcnRob3MgPC0gc20oRXVQYXRoREI6OmV4dHJhY3RfZXVwYXRoX29ydGhvbG9ncyhkYj1wYW5fZGIpKQoKaGlzYXRfYW5ub3QgPC0gYWxsX2xwX2Fubm90CiMjIHJvd25hbWVzKGhpc2F0X2Fubm90KSA8LSBwYXN0ZTAoImV4b25fIiwgcm93bmFtZXMoaGlzYXRfYW5ub3QpLCAiLkUxIikKYGBgCgojIFNhbXBsZSBFc3RpbWF0aW9uCgpUaGUgcHJvY2VzcyBvZiBzYW1wbGUgZXN0aW1hdGlvbiB0YWtlcyB0d28gcHJpbWFyeSBpbnB1dHM6CgoxLiAgVGhlIHNhbXBsZSBzaGVldCwgd2hpY2ggY29udGFpbnMgYWxsIHRoZSBtZXRhZGF0YSB3ZSBjdXJyZW50bHkgaGF2ZSBvbiBoYW5kLAogICAgaW5jbHVkaW5nIGZpbGVuYW1lcyBmb3IgdGhlIG91dHB1dHMgb2YgIzMgYW5kICM0IGFib3ZlLgoyLiAgVGhlIGdlbmUgYW5ub3RhdGlvbnMuCgpBbiBleHByZXNzaW9uc2V0IGlzIHByaW1hcnkgZGF0YSBzdHJ1Y3R1cmUgdXNlZCBpbiBSIHRvIGV4YW1pbmUgUk5BU2VxIGRhdGEuICBJdAppcyBjb21wcmlzZWQgb2YgYW5ub3RhdGlvbnMsIG1ldGFkYXRhLCBhbmQgZXhwcmVzc2lvbiBkYXRhLiAgSW4gdGhlIGNhc2Ugb2Ygb3VyCnByb2Nlc3NpbmcgcGlwZWxpbmUsIHRoZSBsb2NhdGlvbiBvZiB0aGUgZXhwcmVzc2lvbiBkYXRhIGlzIHByb3ZpZGVkIGJ5IHRoZQpmaWxlbmFtZXMgaW4gdGhlIG1ldGFkYXRhLgoKIyMgR2VuZXJhdGUgZXhwcmVzc2lvbnNldHMKClRoZSBmaXJzdCBsaW5lcyBvZiB0aGUgZm9sbG93aW5nIGJsb2NrIGNyZWF0ZSB0aGUgRXhwcmVzc2lvbnNldC4gIEFsbCBvZiB0aGUKZm9sbG93aW5nIGxpbmVzIHBlcmZvcm0gdmFyaW91cyBub3JtYWxpemF0aW9ucyBhbmQgZ2VuZXJhdGUgcGxvdHMgZnJvbSBpdC4KCmBgYHtyIG5ld19zYW1wbGVzX2hpc2F0fQpscF9leHB0IDwtIHNtKGNyZWF0ZV9leHB0KCJzYW1wbGVfc2hlZXRzL3RtcmMyX3NhbXBsZXNfMjAyMDA5MTUueGxzeCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9pbmZvPWhpc2F0X2Fubm90LAogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVfY29sdW1uPSJscGFuYW1lbnNpc3YzNmhpc2F0ZmlsZSIpKQoKbGlic2l6ZXMgPC0gcGxvdF9saWJzaXplKGxwX2V4cHQpCmxpYnNpemVzJHBsb3QKIyMgSSB0aGluayBzYW1wbGVzIDcsMTAgc2hvdWxkIGJlIHJlbW92ZWQgYXQgbWluaW11bSwgcHJvYmFibHkgYWxzbyA5LDExCm5vbnplcm8gPC0gcGxvdF9ub256ZXJvKGxwX2V4cHQpCm5vbnplcm8kcGxvdApwbG90X2JveHBsb3QobHBfZXhwdCkKYGBgCgojIyBEaXN0cmlidXRpb24gVmlzdWFsaXphdGlvbgoKTmFqaWIncyBmYXZvcml0ZSBwbG90cyBhcmUgb2YgY291cnNlIHRoZSBQQ0EvVE5TRS4gIFRoZXNlIGFyZSBuaWNlIHRvIGxvb2sgYXQgaW4Kb3JkZXIgdG8gZ2V0IGEgc2Vuc2Ugb2YgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBzYW1wbGVzLiAgVGhleSBhbHNvIHByb3ZpZGUgYQpnb29kIG9wcG9ydHVuaXR5IHRvIHNlZSB3aGF0IGhhcHBlbnMgd2hlbiBvbmUgYXBwbGllcyBkaWZmZXJlbnQgbm9ybWFsaXphdGlvbnMsCnN1cnJvZ2F0ZSBhbmFseXNlcywgZmlsdGVycywgZXRjLiAgSW4gYWRkaXRpb24sIG9uZSBtYXkgc2V0IGRpZmZlcmVudApleHBlcmltZW50YWwgZmFjdG9ycyBhcyB0aGUgcHJpbWFyeSAnY29uZGl0aW9uJyAodXN1YWxseSB0aGUgY29sb3Igb2YgcGxvdHMpIGFuZApzdXJyb2dhdGUgJ2JhdGNoZXMnLgoKYGBge3IgcHJlX3F1ZXN0aW9uc30KcGhlbm9fZXhwdCA8LSBzZXRfZXhwdF9jb25kaXRpb25zKGxwX2V4cHQsIGZhY3Q9InBoZW5vdHlwaWNjaGFyYWN0ZXJpc3RpY3MiKQphbGxfbm9ybSA8LSBub3JtYWxpemVfZXhwdChwaGVub19leHB0LCBub3JtPSJxdWFudCIsIHRyYW5zZm9ybT0ibG9nMiIsIGNvbnZlcnQ9ImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhdGNoPUZBTFNFLCBmaWx0ZXI9VFJVRSkKcGxvdF9wY2EoYWxsX25vcm0pJHBsb3QKIyMgTm90ZSwgc2FtcGxlcyAyLCA2LCA3LCA4IGFyZSBhbGwgdmVyeSBsb3cgY292ZXJhZ2UgY29tcGFyZWQgdG8gdGhlIGxhdGVyIHNhbXBsZXMuCgpwbG90X3RzbmUoYWxsX25vcm0pJHBsb3QKcGxvdF9jb3JoZWF0KGFsbF9ub3JtKSRwbG90CnBsb3Rfc20oYWxsX25vcm0pJHBsb3QKIyMgcGxvdF92YXJpYW5jZV9jb2VmZmljaWVudHMoYWxsX25vcm0pJHBsb3QKIyMgcGxvdF9zYW1wbGVfY3ZoZWF0bWFwKGFsbF9ub3JtKSRwbG90CmBgYAoKQXQgdGhpcyB0aW1lLCB3ZSBkbyBub3QgaGF2ZSB2ZXJ5IG1hbnkgc2FtcGxlcywgc28gdGhlIHNldCBvZiBtZXRyaWNzL3Bsb3RzIGlzCmZhaXJseSBsaW1pdGVkLiAgVGhlcmUgaXMgcmVhbGx5IG9ubHkgb25lIGZhY3RvciBpbiB0aGUgbWV0YWRhdGEgd2hpY2ggd2UgY2FuCnVzZSBmb3IgcGVyZm9ybWluZyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNlcywgdGhlICd6eW1vZGVtZScuCgpgYGB7ciB6eW1vX2RlfQp6eV9leHB0IDwtIHN1YnNldF9leHB0KHBoZW5vX2V4cHQsIHN1YnNldD0iY29uZGl0aW9uPT0nejIuMid8Y29uZGl0aW9uPT0nejIuMyciKQp6eV9kZSA8LSBzbShhbGxfcGFpcndpc2UoenlfZXhwdCwgZmlsdGVyPVRSVUUsIG1vZGVsX2JhdGNoPSJzdmFzZXEiKSkKenlfdGFibGUgPC0gc20oY29tYmluZV9kZV90YWJsZXMoenlfZGUsIGV4Y2VsPWdsdWU6OmdsdWUoImV4Y2VsL3p5X3RhYmxlcy12e3Zlcn0ueGxzeCIpKSkKenlfdGFibGUkcGxvdHNbWzFdXVtbImRlc2VxX21hX3Bsb3RzIl1dW1sicGxvdCJdXQp6eV9zaWcgPC0gc20oZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcyh6eV90YWJsZSwgZXhjZWw9Z2x1ZTo6Z2x1ZSgiZXhjZWwvenlfc2lnLXZ7dmVyfS54bHN4IikpKQoKbHBfZ28gPC0gbHBfZ29bLCBjKCJHSUQiLCAiR09BTEwiKV0KY29sbmFtZXMobHBfZ28pIDwtIGMoIklEIiwgIkdPIikKCiMjIEdlbmUgY2F0ZWdvcmllcyBtb3JlIHJlcHJlc2VudGVkIGluIHRoZSAyLjMgZ3JvdXAuCnp5X2dvX3VwIDwtIHNtKHNpbXBsZV9nb3NlcShzaWdfZ2VuZXM9enlfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWzFdXSwgZ29fZGI9bHBfZ28sIGxlbmd0aF9kYj1scF9sZW5ndGhzKSkKenlfZ29fdXAkcHZhbHVlX3Bsb3RzJGJwcF9wbG90X292ZXIKCiMjIEdlbmUgY2F0ZWdvcmllcyBtb3JlIHJlcHJlc2VudGVkIGluIHRoZSAyLjIgZ3JvdXAuCnp5X2dvX2Rvd24gPC0gc20oc2ltcGxlX2dvc2VxKHNpZ19nZW5lcz16eV9zaWdbWyJkZXNlcSJdXVtbImRvd25zIl1dW1sxXV0sIGdvX2RiPWxwX2dvLCBsZW5ndGhfZGI9bHBfbGVuZ3RocykpCnp5X2dvX2Rvd24kcHZhbHVlX3Bsb3RzJGJwcF9wbG90X292ZXIKYGBgCgojIFp5bW9kZW1lIGdlbmVzCgpOYWppYiByZWFkIG1lIGFuIGVtYWlsIGxpc3Rpbmcgb2ZmIHRoZSBnZW5lIG5hbWVzIGFzc29jaWF0ZWQgd2l0aCB0aGUgenltb2RlbWUKY2xhc3NpZmljYXRpb24uICBJIHRvb2sgdGhvc2UgbmFtZXMgYW5kIGNyb3NzIHJlZmVyZW5jZWQgdGhlbSBhZ2FpbnN0IHRoZQpMZWlzaG1hbmlhIHBhbmFtZW5zaXMgZ2VuZSBhbm5vdGF0aW9ucyBhbmQgZm91bmQgdGhlIGZvbGxvd2luZzoKClRoZXkgYXJlOgoKMS4gQUxBVDogTFBBTDEzXzEyMDAxMDkwMCAtLSBhbGFuaW5lIGFtaW5vdHJhbnNmZXJhc2UKMi4gQVNBVDogTFBBTDEzXzM0MDAxMzAwMCAtLSBhc3BhcnRhdGUgYW1pbm90cmFuc2ZlcmFzZQozLiBHNlBEOiBMUEFMMTNfMDAwMDU0MTAwIC0tIGdsdWNhc2UtNi1waG9zcGhhdGUgMS1kZWh5ZHJvZ2VuYXNlCjQuIE5IOiBMUEFMMTNfMTQwMDYxMDAsIExQQUwxM18xODAwMTg1MDAgLS0gaW5vc2luZS1ndWFuaW5lIG51Y2xlb3NpZGUgaHlkcm9sYXNlCjUuIE1QSTogTFBBTDEzXzMyMDAyMjMwMCAobWF5YmUpIC0tIG1hbm5vc2UgcGhvc3BoYXRlIGlzb21lcmFzZSAoSSBjaG9zZSBwaG9zcGhvbWFubm9zZSBpc29tZXJhc2UpCgpHaXZlbiB0aGVzZSA2IGdlbmUgSURzIChOSCBoYXMgdHdvIGdlbmUgSURzIGFzc29jaWF0ZWQgd2l0aCBpdCksIEkgY2FuIGRvIHNvbWUKbG9va2luZyBmb3Igc3BlY2lmaWMgZGlmZmVyZW5jZXMgYW1vbmcgdGhlIHZhcmlvdXMgc2FtcGxlcy4KCmBgYHtyIHp5bW9kZW1lc30KbXlfZ2VuZXMgPC0gYygiTFBBTDEzXzEyMDAxMDkwMCIsICJMUEFMMTNfMzQwMDEzMDAwIiwgIkxQQUwxM18wMDAwNTQxMDAiLAogICAgICAgICAgICAgICJMUEFMMTNfMTQwMDA2MTAwIiwgIkxQQUwxM18xODAwMTg1MDAiLCAiTFBBTDEzXzMyMDAyMjMwMCIsCiAgICAgICAgICAgICAgIm90aGVyIikKbXlfbmFtZXMgPC0gYygiQUxBVCIsICJBU0FUIiwgIkc2UEQiLCAiTkh2MSIsICJOSHYyIiwgIk1QSSIsICJvdGhlciIpCgp6eW1vX2V4cHQgPC0gZXhjbHVkZV9nZW5lc19leHB0KGFsbF9ub3JtLCBpZHM9bXlfZ2VuZXMsIG1ldGhvZD0ia2VlcCIpCnRlc3QgPC0gcGxvdF9zYW1wbGVfaGVhdG1hcCh6eW1vX2V4cHQsIHJvd19sYWJlbD1teV9uYW1lcykKYGBgCgojIFZhcmlhbnQgcHJvZmlsZXMKCkluIHRoaXMgYmxvY2ssIEkgYW0gY29tYmluaW5nIG91ciBwcmV2aW91cyBzYW1wbGVzIGFuZCBvdXIgbmV3IHNhbXBsZXMgaW4gdGhlCmhvcGVzIG9mIGZpbmRpbmcgdmFyaWFudCBwb3NpdGlvbnMgd2hpY2ggaGVscCBlbHVjaWRhdGUgYXNwZWN0cyBvZiBlaXRoZXIgdGhlCm5ldyBvciBvbGQgc2FtcGxlcy4gIEluIG90aGVyIHdvcmRzLCB3ZSBkbyBub3Qga25vdyB0aGUgenltb2RlbWUgYW5ub3RhdGlvbnMgZm9yCnRoZSBvbGQgc2FtcGxlcyBub3IgdGhlIHN0cmFpbiBpZGVudGl0aWVzIChvciB0aGUgc2hvcnRjdXQgJ2Nocm9uaWMKdnMuIHNlbGYtaGVhbGluZycpIGZvciB0aGUgbmV3IHNhbXBsZXMuICBXZSBtYXkgYmUgYWJsZSB0byBtYWtlIGVkdWNhdGVkIGd1ZXNzZXMKZ2l2ZW4gdGhlIHZhcmlhbnQgcHJvZmlsZXMuICBUaGVyZSBhcmUgc29tZSBkaWZmZXJlbmNlcyBpbiBob3cgdGhlIHByZXZpb3VzIGFuZApjdXJyZW50IGRhdGEgc2V0cyB3ZXJlIGFuYWx5emVkICh0aG91Z2ggSSBoYXZlIHNpbmNlIHJlZG9uZSB0aGUgb2xkIHNhbXBsZXMgc28KaXQgc2hvdWxkIGJlIHRyaXZpYWwgdG8gcmVtb3ZlIHRob3NlIGRpZmZlcmVuY2VzIG5vdykuCgpgYGB7ciBvbGRuZXdfdmFyaWFudHN9Cm9sZF9leHB0IDwtIHNtKGNyZWF0ZV9leHB0KCJzYW1wbGVfc2hlZXRzL3RtcmMyX3NhbXBsZXNfMjAxOTEyMDMueGxzeCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVfY29sdW1uPSJ0b3BoYXQyZmlsZSIpKQoKdHQgPC0gbHBfZXhwdCRleHByZXNzaW9uc2V0CnJvd25hbWVzKHR0KSA8LSBnc3ViKHBhdHRlcm49Il5leG9uXyIsIHJlcGxhY2VtZW50PSIiLCB4PXJvd25hbWVzKHR0KSkKcm93bmFtZXModHQpIDwtIGdzdWIocGF0dGVybj0iXFwuRTEkIiwgcmVwbGFjZW1lbnQ9IiIsIHg9cm93bmFtZXModHQpKQpscF9leHB0JGV4cHJlc3Npb25zZXQgPC0gdHQKCnR0IDwtIG9sZF9leHB0JGV4cHJlc3Npb25zZXQKcm93bmFtZXModHQpIDwtIGdzdWIocGF0dGVybj0iXmV4b25fIiwgcmVwbGFjZW1lbnQ9IiIsIHg9cm93bmFtZXModHQpKQpyb3duYW1lcyh0dCkgPC0gZ3N1YihwYXR0ZXJuPSJcXC4xJCIsIHJlcGxhY2VtZW50PSIiLCB4PXJvd25hbWVzKHR0KSkKb2xkX2V4cHQkZXhwcmVzc2lvbnNldCA8LSB0dAoKbmV3X3NucHMgPC0gc20oY291bnRfZXhwdF9zbnBzKGxwX2V4cHQsIGFubm90X2NvbHVtbj0iYmNmdGFibGUiKSkKb2xkX3NucHMgPC0gc20oY291bnRfZXhwdF9zbnBzKG9sZF9leHB0LCBhbm5vdF9jb2x1bW49ImJjZnRhYmxlIiwgc25wX2NvbHVtbj0yKSkKCmJvdGhfc25wcyA8LSBjb21iaW5lX2V4cHRzKG5ld19zbnBzLCBvbGRfc25wcykKYm90aF9ub3JtIDwtIHNtKG5vcm1hbGl6ZV9leHB0KGJvdGhfc25wcywgdHJhbnNmb3JtPSJsb2cyIiwgY29udmVydD0iY3BtIiwgZmlsdGVyPVRSVUUpKQoKc3RyYWlucyA8LSBib3RoX25vcm1bWyJkZXNpZ24iXV1bWyJzdHJhaW4iXV0KYm90aF9ub3JtIDwtIHNldF9leHB0X2NvbmRpdGlvbnMoYm90aF9ub3JtLCBmYWN0PXN0cmFpbnMpCnR0IDwtIHBsb3RfZGlzaGVhdChib3RoX25vcm0pCgpzbnBfc2V0cyA8LSBnZXRfc25wX3NldHMoYm90aF9zbnBzLCBmYWN0b3I9ImNvbmRpdGlvbiIpCmJvdGhfZXhwdCA8LSBjb21iaW5lX2V4cHRzKGxwX2V4cHQsIG9sZF9leHB0KQpzbnBfZ2VuZXMgPC0gc20oc25wc192c19nZW5lcyhib3RoX2V4cHQsIHNucF9zZXRzLCBleHB0X25hbWVfY29sPSJjaHJvbW9zb21lIikpCgpzbnBfc3Vic2V0IDwtIHNtKHNucF9zdWJzZXRfZ2VuZXMoCiAgYm90aF9leHB0LCBib3RoX3NucHMsCiAgZ2VuZXM9YygiTFBBTDEzXzEyMDAxMDkwMCIsICJMUEFMMTNfMzQwMDEzMDAwIiwgIkxQQUwxM18wMDAwNTQxMDAiLAogICAgICAgICAgIkxQQUwxM18xNDAwMDYxMDAiLCAiTFBBTDEzXzE4MDAxODUwMCIsICJMUEFMMTNfMzIwMDIyMzAwIikpKQoKenltb19oZWF0IDwtIHBsb3Rfc2FtcGxlX2hlYXRtYXAoc25wX3N1YnNldCwgcm93X2xhYmVsPXJvd25hbWVzKGV4cHJzKHNucF9zdWJzZXQpKSkKYGBgCgojIFp5bW9kZW1lIGZvciBuZXcgc2FtcGxlcwoKVGhlIGhlYXRtYXAgcHJvZHVjZWQgaGVyZSBzaG91bGQgc2hvdyB0aGUgdmFyaWFudHMgb25seSBmb3IgdGhlIHp5bW9kZW1lIGdlbmVzLgoKYGBge3IgbmV3X3p5bW99Cm5ld19zZXRzIDwtIGdldF9zbnBfc2V0cyhuZXdfc25wcywgZmFjdG9yPSJwaGVub3R5cGljY2hhcmFjdGVyaXN0aWNzIikKc25wX2dlbmVzIDwtIHNtKHNucHNfdnNfZ2VuZXMocGhlbm9fZXhwdCwgbmV3X3NldHMsIGV4cHRfbmFtZV9jb2w9ImNocm9tb3NvbWUiKSkKbmV3X3p5bW9fbm9ybSAgPC0gIG5vcm1hbGl6ZV9leHB0KG5ld19zbnBzLCBmaWx0ZXI9VFJVRSwgY29udmVydD0iY3BtIiwgbm9ybT0icXVhbnQiLCB0cmFuc2Zvcm09VFJVRSkKbmV3X3p5bW9fbm9ybSA8LSBzZXRfZXhwdF9jb25kaXRpb25zKG5ld196eW1vX25vcm0sIGZhY3Q9InBoZW5vdHlwaWNjaGFyYWN0ZXJpc3RpY3MiKQp6eW1vX2hlYXQgPC0gcGxvdF9kaXNoZWF0KG5ld196eW1vX25vcm0pCgp6eW1vX3N1YnNldCA8LSBzbShzbnBfc3Vic2V0X2dlbmVzKAogIHBoZW5vX2V4cHQsIG5ld19zbnBzLAogIGdlbmVzPWMoIkxQQUwxM18xMjAwMTA5MDAiLCAiTFBBTDEzXzM0MDAxMzAwMCIsICJMUEFMMTNfMDAwMDU0MTAwIiwKICAgICAgICAgICJMUEFMMTNfMTQwMDA2MTAwIiwgIkxQQUwxM18xODAwMTg1MDAiLCAiTFBBTDEzXzMyMDAyMjMwMCIpKSkKCnp5bW9fc3Vic2V0IDwtIHNldF9leHB0X2NvbmRpdGlvbnMoenltb19zdWJzZXQsIGZhY3Q9InBoZW5vdHlwaWNjaGFyYWN0ZXJpc3RpY3MiKQp6eW1vX2hlYXQgPC0gcGxvdF9zYW1wbGVfaGVhdG1hcCh6eW1vX3N1YnNldCwgcm93X2xhYmVsPXJvd25hbWVzKGV4cHJzKHNucF9zdWJzZXQpKSkKCmRlcyA8LSBib3RoX25vcm0kZGVzaWduCnVuZGVmX2lkeCA8LSBpcy5uYShkZXNbWyJzdHJhaW4iXV0pCmRlc1t1bmRlZl9pZHgsICJzdHJhaW4iXSA8LSAidW5rbm93biIKbGlicmFyeShIZWF0cGx1cykKIyNobWNvbHMgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJ5ZWxsb3ciLCJibGFjayIsImRhcmtibHVlIikpKDI1NikKY29ycmVsYXRpb25zIDwtIGhwZ2xfY29yKGV4cHJzKGJvdGhfbm9ybSkpCgp6eW1vX21pc3NpbmdfaWR4IDwtIGlzLm5hKGRlc1tbInBoZW5vdHlwaWNjaGFyYWN0ZXJpc3RpY3MiXV0pCmRlc1t6eW1vX21pc3NpbmdfaWR4LCAicGhlbm90eXBpY2NoYXJhY3RlcmlzdGljcyJdIDwtICJ1bmtub3duIgpteWRlbmRybyA8LSBsaXN0KAogICJjbHVzdGZ1biIgPSBoY2x1c3QsCiAgImx3ZCIgPSAyLjApCmNvbF9kYXRhIDwtIGFzLmRhdGEuZnJhbWUoZGVzWywgYygicGhlbm90eXBpY2NoYXJhY3RlcmlzdGljcyIpXSkKcm93X2RhdGEgPC0gYXMuZGF0YS5mcmFtZShkZXNbLCBjKCJzdHJhaW4iKV0pCmNvbG5hbWVzKGNvbF9kYXRhKSA8LSBjKCJjb25kaXRpb24iKQpjb2xuYW1lcyhyb3dfZGF0YSkgPC0gYygic3RyYWluIikKbXlhbm5vdCA8LSBsaXN0KAogICJDb2wiID0gbGlzdCgiZGF0YSIgPSBjb2xfZGF0YSksCiAgIlJvdyIgPSBsaXN0KCJkYXRhIiA9IHJvd19kYXRhKSkKbXljbHVzdCA8LSBsaXN0KCJjdXRoIiA9IDEuMCwKICAgICAgICAgICAgICAgICJjb2wiID0gQnJld2VyQ2x1c3RlckNvbCkKbXlsYWJzIDwtIGxpc3QoCiAgIlJvdyIgPSBsaXN0KCJucm93IiA9IDQpLAogICJDb2wiID0gbGlzdCgibnJvdyIgPSA0KSkKaG1jb2xzIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiZGFya2JsdWUiLCAiYmVpZ2UiKSkoMTcwKQptYXAxIDwtIGFubkhlYXRtYXAyKAogIGNvcnJlbGF0aW9ucywKICBkZW5kcm9ncmFtPW15ZGVuZHJvLAogIGFubm90YXRpb249bXlhbm5vdCwKICBjbHVzdGVyPW15Y2x1c3QsCiAgbGFiZWxzPW15bGFicywKICBjb2w9aG1jb2xzKQpwbG90KG1hcDEpCmBgYAoKIyBVc2luZyBWYXJpYW50IHByb2ZpbGVzIHRvIG1ha2UgZ3Vlc3NlcyBhYm91dCBzdHJhaW5zIGFuZCBjaHJvbmljL3NlbGYtaGVhbGluZwoKVGhlIGZvbGxvd2luZyB1c2VzIHRoZSBzYW1lIGluZm9ybWF0aW9uIHRvIG1ha2Ugc29tZSBndWVzc2VzIGFib3V0IHRoZSBzdHJhaW5zCnVzZWQgaW4gdGhlIG5ldyBzYW1wbGVzLgoKYGBge3Igb2xkX2FuZF9uZXdfY2hyb25pY30KZGVzIDwtIGJvdGhfbm9ybSRkZXNpZ24KdW5kZWZfaWR4IDwtIGlzLm5hKGRlc1tbInN0cmFpbiJdXSkKZGVzW3VuZGVmX2lkeCwgInN0cmFpbiJdIDwtICJ1bmtub3duIgojI2htY29scyA8LSBjb2xvclJhbXBQYWxldHRlKGMoInllbGxvdyIsImJsYWNrIiwiZGFya2JsdWUiKSkoMjU2KQpjb3JyZWxhdGlvbnMgPC0gaHBnbF9jb3IoZXhwcnMoYm90aF9ub3JtKSkKCm15ZGVuZHJvIDwtIGxpc3QoCiAgImNsdXN0ZnVuIiA9IGhjbHVzdCwKICAibHdkIiA9IDIuMCkKY29sX2RhdGEgPC0gYXMuZGF0YS5mcmFtZShkZXNbLCBjKCJjb25kaXRpb24iKV0pCnJvd19kYXRhIDwtIGFzLmRhdGEuZnJhbWUoZGVzWywgYygic3RyYWluIildKQpjb2xuYW1lcyhjb2xfZGF0YSkgPC0gYygiY29uZGl0aW9uIikKY29sbmFtZXMocm93X2RhdGEpIDwtIGMoInN0cmFpbiIpCm15YW5ub3QgPC0gbGlzdCgKICAiQ29sIiA9IGxpc3QoImRhdGEiID0gY29sX2RhdGEpLAogICJSb3ciID0gbGlzdCgiZGF0YSIgPSByb3dfZGF0YSkpCm15Y2x1c3QgPC0gbGlzdCgiY3V0aCIgPSAxLjAsCiAgICAgICAgICAgICAgICAiY29sIiA9IEJyZXdlckNsdXN0ZXJDb2wpCm15bGFicyA8LSBsaXN0KAogICJSb3ciID0gbGlzdCgibnJvdyIgPSA0KSwKICAiQ29sIiA9IGxpc3QoIm5yb3ciID0gNCkpCmhtY29scyA8LSBjb2xvclJhbXBQYWxldHRlKGMoImRhcmtibHVlIiwgImJlaWdlIikpKDE3MCkKbWFwMSA8LSBhbm5IZWF0bWFwMigKICBjb3JyZWxhdGlvbnMsCiAgZGVuZHJvZ3JhbT1teWRlbmRybywKICBhbm5vdGF0aW9uPW15YW5ub3QsCiAgY2x1c3Rlcj1teWNsdXN0LAogIGxhYmVscz1teWxhYnMsCiAgY29sPWhtY29scykKcGxvdChtYXAxKQpgYGAKCmBgYHtyIHNhdmVtZX0KaWYgKCFpc1RSVUUoZ2V0MCgic2tpcF9sb2FkIikpKSB7CiAgcGFuZGVyOjpwYW5kZXIoc2Vzc2lvbkluZm8oKSkKICBtZXNzYWdlKHBhc3RlMCgiVGhpcyBpcyBocGdsdG9vbHMgY29tbWl0OiAiLCBnZXRfZ2l0X2NvbW1pdCgpKSkKICBtZXNzYWdlKHBhc3RlMCgiU2F2aW5nIHRvICIsIHNhdmVmaWxlKSkKICB0bXAgPC0gc20oc2F2ZW1lKGZpbGVuYW1lPXNhdmVmaWxlKSkKfQpgYGAKCmBgYHtyIGxvYWRtZV9hZnRlciwgZXZhbD1GQUxTRX0KdG1wIDwtIGxvYWRtZShmaWxlbmFtZT1zYXZlZmlsZSkKYGBgCg==