libsizes <- plot_libsize(lp_expt)
dev <- pp("images/lp_expt_libsizes.png", width = 14, height = 9)
libsizes$plot
closed <- dev.off()
libsizes$plot

## I think samples 7,10 should be removed at minimum, probably also 9,11
nonzero <- plot_nonzero(lp_expt)
## Scale for 'colour' is already present. Adding another scale for 'colour',
## which will replace the existing scale.
## Scale for 'fill' is already present. Adding another scale for 'fill', which
## will replace the existing scale.
dev <- pp(file = "images/lp_nonzero.png", width=9, height = 9)
nonzero$plot
## Warning: ggrepel: 81 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
closed <- dev.off()
nonzero$plot
## Warning: ggrepel: 83 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

lp_box <- plot_boxplot(lp_expt)
## 8122 entries are 0.  We are on a log scale, adding 1 to the data.
dev <- pp(file = "images/lp_expt_boxplot.png", width = 16, height = 9)
lp_box
closed <- dev.off()
lp_box

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

table(pData(lp_expt)[["zymodemecategorical"]])
## 
##   b2904 unknown     z10     z15     z20     z21     z22     z23     z24     z30 
##       1       2       1       1       1       7      43      41       2       1 
##     z32 
##       1
table(pData(lp_expt)[["clinicalresponse"]])
## 
##                                  cure                               failure 
##                                    38                                    38 
##                       laboratory line laboratory line miltefosine resistant 
##                                     1                                     1 
##                                    nd                      reference strain 
##                                    19                                     4

0.1 Distribution Visualizations

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

0.2 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
strain_norm <- normalize_expt(lp_strain, norm = "quant", transform = "log2",
                              convert = "cpm", filter = TRUE)
## Removing 134 low-count genes (8576 remaining).
## transform_counts: Found 2 values equal to 0, adding 1 to the matrix.
zymo_pca <- plot_pca(strain_norm, plot_title = "PCA of parasite expression values",
                     plot_labels = FALSE)
ggplt(zymo_pca$plot)
## [1] "ggplot.html"
dev <- pp(file = "images/promastigote_zymocol_sensshape.png")
zymo_pca$plot
closed <- dev.off()
zymo_pca$plot

zymo_tsne <- plot_tsne(strain_norm, plot_title = "TSNE of parasite expression values")
## plot labels was not set and there are more than 100 samples, disabling it.
zymo_tsne$plot

strain_nb <- normalize_expt(lp_strain, convert = "cpm", transform = "log2",
                            filter = TRUE, batch = "svaseq")
## Removing 134 low-count genes (8576 remaining).
## Setting 738 low elements to zero.
## transform_counts: Found 738 values equal to 0, adding 1 to the matrix.
strain_nb_pca <- plot_pca(strain_nb, plot_title = "PCA of parasite expression values",
                          plot_labels = FALSE)
dev <- pp(file = "images/clinical_nb_pca_sus_shape.png")
strain_nb_pca$plot
closed <- dev.off()
strain_nb_pca$plot

strain_nb_tsne <- plot_tsne(strain_nb, plot_title = "TSNE of parasite expression values")
## plot labels was not set and there are more than 100 samples, disabling it.
strain_nb_tsne$plot

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

plot_sm(strain_norm)$plot
## Performing correlation.

0.3 Limit to three strains: 2.1/2.2/2.3

only_three_types <- subset_expt(lp_strain, subset = "condition=='z2.1'|condition=='z2.3'|condition=='z2.2'")
## subset_expt(): There were 101, now there are 91 samples.
only_three_norm <- sm(normalize_expt(only_three_types, norm = "quant", transform = "log2",
                                     convert = "cpm", batch = FALSE, filter = TRUE)) %>%
  set_expt_batches(fact="phase")
onlythree_pca <- plot_pca(only_three_norm, plot_title = "PCA of z2.1, z2.2 and z2.3 parasite expression values",
                          plot_labels = FALSE)
pp(file="images/promastigote_threetypes_zymocol_noshape.png")
onlythree_pca$plot
dev.off()
## png 
##   2
onlythree_pca$plot

0.4 Limit to just two strains: 2.2/2.3

lp_two_strains_norm <- sm(normalize_expt(lp_two_strains, norm = "quant", transform = "log2",
                                         convert = "cpm", batch = FALSE, filter = TRUE))
onlytwo_pca <- plot_pca(lp_two_strains_norm, plot_title = "PCA of z2.2 and z2.3 parasite expression values",
                        plot_labels = FALSE)
dev <- pp(file = "images/zymo_z2.2_z2.3_pca_sus_shape.pdf")
onlytwo_pca$plot
closed <- dev.off()
onlytwo_pca$plot

0.5 By Cure/Fail status

cf_norm <- normalize_expt(lp_cf, convert = "cpm", transform = "log2",
                          norm = "quant", filter = TRUE)
## Removing 134 low-count genes (8576 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",
                     plot_labels = FALSE)
dev <- pp(file = "images/cf_sus_shape.png")
start_cf$plot
closed <- dev.off()
start_cf$plot

cf_nb <- normalize_expt(lp_cf_known, convert = "cpm", transform = "log2",
                        filter = TRUE, batch = "svaseq")
## Removing 162 low-count genes (8548 remaining).
## Setting 117 low elements to zero.
## transform_counts: Found 117 values equal to 0, adding 1 to the matrix.
cf_nb_pca <- plot_pca(cf_nb, plot_title = "PCA of parasite expression values",
                      plot_labels = FALSE)
dev <- pp(file = "images/cf_sus_share_nb.png")
cf_nb_pca$plot
closed <- dev.off()
cf_nb_pca$plot

cf_norm <- normalize_expt(lp_cf, transform = "log2", convert = "cpm",
                          filter = TRUE, norm = "quant")
## Removing 134 low-count genes (8576 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)
## plot labels was not set and there are more than 100 samples, disabling it.
test$anova_p
##                           PC1      PC2    PC3       PC4       PC5       PC6
## clinicalcategorical 3.139e-01 0.457872 0.9691 7.839e-03 2.264e-01 3.371e-01
## zymodemecategorical 4.787e-07 0.001621 0.5959 5.970e-02 3.966e-05 5.040e-01
## pathogenstrain      4.747e-01 0.870333 0.6433 5.629e-05 1.889e-02 2.316e-01
## passagenumber       9.502e-01 0.174448 0.4657 3.136e-02 8.602e-01 5.429e-06
test$cor_heatmap

sus_norm <- normalize_expt(lp_susceptibility, transform = "log2", convert = "cpm",
                           norm = "quant", filter = TRUE)
## Removing 134 low-count genes (8576 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",
                    plot_labels = FALSE)
dev <- pp(file = "images/sus_norm_pca.png")
sus_pca[["plot"]]
closed <- dev.off()
sus_pca[["plot"]]

sus_nb <- normalize_expt(lp_susceptibility, transform = "log2", convert = "cpm",
                         batch = "svaseq", filter = TRUE)
## Removing 134 low-count genes (8576 remaining).
## Setting 405 low elements to zero.
## transform_counts: Found 405 values equal to 0, adding 1 to the matrix.
sus_nb_pca <- plot_pca(sus_nb, plot_title = "PCA of parasite expression values",
                       plot_labels = FALSE)
dev <- pp(file = "images/sus_nb_pca.png")
sus_nb_pca[["plot"]]
closed <- dev.off()
sus_nb_pca[["plot"]]

0.6 Zymodeme enzyme gene IDs

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

They are:

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

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

0.6.1 Expression levels of zymodeme genes

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

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

zymo_expt <- exclude_genes_expt(strain_norm, ids = my_genes, method = "keep")
## remove_genes_expt(), before removal, there were 8576 genes, now there are 6.
## There are 101 samples which kept less than 90 percent counts.
## TMRC20001 TMRC20065 TMRC20005 TMRC20007 TMRC20008 TMRC20027 TMRC20028 TMRC20032 
##   0.08652   0.08512   0.08414   0.08695   0.08365   0.08470   0.08796   0.08394 
## TMRC20040 TMRC20066 TMRC20039 TMRC20037 TMRC20038 TMRC20067 TMRC20068 TMRC20041 
##   0.08260   0.08191   0.08481   0.08204   0.08359   0.08402   0.08449   0.08315 
## TMRC20015 TMRC20009 TMRC20010 TMRC20016 TMRC20011 TMRC20012 TMRC20013 TMRC20017 
##   0.08490   0.08382   0.08432   0.08365   0.08356   0.08550   0.08577   0.08344 
## TMRC20014 TMRC20018 TMRC20019 TMRC20070 TMRC20020 TMRC20021 TMRC20022 TMRC20025 
##   0.08400   0.08355   0.08372   0.08410   0.08220   0.08198   0.08548   0.08592 
## TMRC20024 TMRC20036 TMRC20069 TMRC20033 TMRC20026 TMRC20031 TMRC20076 TMRC20073 
##   0.08229   0.08273   0.08271   0.08278   0.08754   0.08204   0.08331   0.08490 
## TMRC20055 TMRC20079 TMRC20071 TMRC20078 TMRC20094 TMRC20042 TMRC20058 TMRC20072 
##   0.08446   0.08525   0.08434   0.08392   0.08409   0.08430   0.08318   0.08411 
## TMRC20059 TMRC20048 TMRC20057 TMRC20088 TMRC20056 TMRC20060 TMRC20077 TMRC20074 
##   0.08360   0.08241   0.08607   0.08494   0.08475   0.08320   0.08402   0.08375 
## TMRC20063 TMRC20053 TMRC20052 TMRC20064 TMRC20075 TMRC20051 TMRC20050 TMRC20049 
##   0.08251   0.08292   0.08267   0.08314   0.08374   0.08448   0.08262   0.08544 
## TMRC20062 TMRC20110 TMRC20080 TMRC20043 TMRC20083 TMRC20054 TMRC20085 TMRC20046 
##   0.08427   0.08519   0.08222   0.08343   0.08444   0.08488   0.08429   0.08544 
## TMRC20093 TMRC20089 TMRC20047 TMRC20090 TMRC20044 TMRC20045 TMRC20061 TMRC20105 
##   0.08460   0.08355   0.08430   0.08171   0.08531   0.08388   0.08348   0.08449 
## TMRC20108 TMRC20109 TMRC20098 TMRC20096 TMRC20097 TMRC20101 TMRC20092 TMRC20082 
##   0.08313   0.08458   0.08489   0.08363   0.08338   0.08366   0.08318   0.08277 
## TMRC20102 TMRC20099 TMRC20100 TMRC20091 TMRC20084 TMRC20087 TMRC20103 TMRC20104 
##   0.08338   0.08468   0.08324   0.08503   0.08319   0.08445   0.08440   0.08415 
## TMRC20086 TMRC20107 TMRC20081 TMRC20106 TMRC20095 
##   0.08366   0.08155   0.08221   0.08079   0.07790
zymo_heatmap <- plot_sample_heatmap(zymo_expt, row_label = my_names)
zymo_heatmap

new_conditions <- paste0(pData(hs_macrophage)[["macrophagetreatment"]], "_",
                         pData(hs_macrophage)[["macrophagezymodeme"]])

tmrc2_macrophage_norm <- normalize_expt(hs_macrophage, filter=TRUE, norm="quant",
                                        convert="cpm", transform="log2")
## Removing 10021 low-count genes (11460 remaining).
## transform_counts: Found 6 values equal to 0, adding 1 to the matrix.
macrophage_hs_pca <- plot_pca(tmrc2_macrophage_norm, plot_labels=FALSE)
pp(file="images/macrophage_hs_infection.png")
macrophage_hs_pca$plot
dev.off()
## png 
##   2
macrophage_hs_pca$plot

hs_macrophage_drugzymo <- set_expt_conditions(hs_macrophage,
                                              fact = new_conditions)
hs_macrophage_drugzymo_norm <- normalize_expt(hs_macrophage_drugzymo,
                                              filter=TRUE, norm="quant", convert="cpm",
                                              transform="log2")
## Removing 10021 low-count genes (11460 remaining).
## transform_counts: Found 6 values equal to 0, adding 1 to the matrix.
plot_pca(hs_macrophage_drugzymo_norm)$plot

tmrc2_macro_nosb_drugzymo <- subset_expt(hs_macrophage_drugzymo,
                                         subset="drug!='Antimony'") %>%
  subset_expt(subset="macrophagetreatment!='uninf'")
## subset_expt(): There were 28, now there are 14 samples.
## subset_expt(): There were 14, now there are 12 samples.
tmrc2_macro_nosb_drugzymo_norm <- normalize_expt(tmrc2_macro_nosb_drugzymo,
                                                 filter=TRUE, convert="cpm",
                                                 norm="quant", transform="log2")
## Removing 10435 low-count genes (11046 remaining).
## transform_counts: Found 1 values equal to 0, adding 1 to the matrix.
pp(file="images/tmrc2_macro_nosb_drugzymo_pca.png",
   image=plot_pca(tmrc2_macro_nosb_drugzymo_norm, plot_labels=FALSE)$plot)
## Warning in pp(file = "images/tmrc2_macro_nosb_drugzymo_pca.png", image =
## plot_pca(tmrc2_macro_nosb_drugzymo_norm, : There is no device to shut down.
new_conditions <- paste0(pData(lp_macrophage)[["macrophagetreatment"]], "_",
                         pData(lp_macrophage)[["macrophagezymodeme"]])
lp_macrophage <- set_expt_conditions(lp_macrophage, fact = new_conditions)

macrophage_libsize <- plot_libsize(lp_macrophage)
pp(file="images/tmrc2_macrophage_lp_libsize.png")
macrophage_libsize$plot
dev.off()
## png 
##   2
macrophage_libsize$plot

lp_macrophage_norm <- normalize_expt(lp_macrophage,
                                     filter=TRUE, norm="quant", transform="log2",
                                     convert="cpm")
## Removing 188 low-count genes (8522 remaining).
## transform_counts: Found 4 values equal to 0, adding 1 to the matrix.
lp_macrophage_pca <- plot_pca(lp_macrophage_norm, plot_labels=FALSE)
pp(file="images/amastigote_zymocol_includesb.png")
lp_macrophage_pca$plot
dev.off()
## png 
##   2
lp_macrophage_pca$plot

lp_macrophage_nosb <- subset_expt(lp_macrophage,
                                  subset="condition!='inf_sb_z2.3'")
## subset_expt(): There were 11, now there are 10 samples.
lp_macrophage_nosb_norm <- normalize_expt(lp_macrophage_nosb,
                                          filter=TRUE, norm="quant", transform="log2",
                                          convert="cpm")
## Removing 190 low-count genes (8520 remaining).
## transform_counts: Found 4 values equal to 0, adding 1 to the matrix.
lp_macrophage_nosb_pca <- plot_pca(lp_macrophage_nosb_norm,
                                   plot_labels=FALSE)
pp(file="images/amastigote_zymocol_excludesb.png")
lp_macrophage_nosb_pca$plot
dev.off()
## png 
##   2
lp_macrophage_nosb_pca$plot

lp_macrophage_de <- all_pairwise(lp_macrophage,
                                 model_batch="svaseq", filter=TRUE)
## This DE analysis will perform all pairwise comparisons among:
## 
## inf_sb_z2.3    inf_z2.2    inf_z2.3 
##           1           5           5
## This analysis will include surrogate estimates from: svaseq.
## This will pre-filter the input data using normalize_expt's: TRUE argument.
## Removing 0 low-count genes (8522 remaining).
## Setting 53 low elements to zero.
## transform_counts: Found 53 values equal to 0, adding 1 to the matrix.
## Finished running DE analyses, collecting outputs.
## Comparing analyses.

tmrc2_parasite_keepers <- list(
    "z23nosb_vs_z22nosb" = c("infz23", "infz22"))
lp_macrophage_table <- combine_de_tables(
  lp_macrophage_de, keepers = tmrc2_parasite_keepers,
  excel=glue::glue("excel/macrophage_parasite_infection_de-v{ver}.xlsx"))
lp_macrophage_sig <- extract_significant_genes(
    lp_macrophage_table,
    excel=glue::glue("excel/macrophage_parasite_sig-v{ver}.xlsx"))
## Using p column: limma_adjp.
## Using p column: edger_adjp.
## Using p column: deseq_adjp.
## Using p column: ebseq_adjp.
## Using p column: basic_adjp.

A recent suggestion included a query about the relationship of our amastigote TMRC2 samples which were the result of infecting a set of macrophages vs. these promastigote samples.

So far, we have kept these two experiments separate, now let us merge them.

tmrc2_macrophage_norm <- normalize_expt(lp_macrophage, transform="log2", convert="cpm",
                                        norm="quant", filter=TRUE)
## Removing 188 low-count genes (8522 remaining).
## transform_counts: Found 4 values equal to 0, adding 1 to the matrix.
all_tmrc2 <- combine_expts(lp_expt, lp_macrophage)

all_nosb <- all_tmrc2
pData(all_nosb)[["stage"]] <- "promastigote"
na_idx <- is.na(pData(all_nosb)[["macrophagetreatment"]])
pData(all_nosb)[na_idx, "macrophagetreatment"] <- "undefined"
all_nosb <- subset_expt(all_nosb, subset="macrophagetreatment!='inf_sb'")
## subset_expt(): There were 112, now there are 111 samples.
ama_idx <- pData(all_nosb)[["macrophagetreatment"]] == "inf"
pData(all_nosb)[ama_idx, "stage" ] <- "amastigote"

pData(all_nosb)[["batch"]] <- pData(all_nosb)[["stage"]]
all_norm <- normalize_expt(all_nosb, convert="cpm", norm="quant", transform="log2", filter=TRUE)
## Removing 129 low-count genes (8581 remaining).
## transform_counts: Found 2 values equal to 0, adding 1 to the matrix.
plot_pca(all_norm)$plot
## plot labels was not set and there are more than 100 samples, disabling it.

I think the above picture is sort of the opposite of what we want to compare in a DE analysis for this set of data, e.g. we want to compare promastigotes from amastigotes?

all_nosb <- set_expt_batches(all_nosb, fact="condition") %>%
  set_expt_conditions(fact="stage")

two_zymo <- subset_expt(all_nosb, subset="zymodemecategorical=='z22'|zymodemecategorical=='z23'|zymodemecategorical=='unknown'")
## subset_expt(): There were 111, now there are 86 samples.
pro_ama <- all_pairwise(all_nosb, filter=TRUE, model_batch="svaseq")
## This DE analysis will perform all pairwise comparisons among:
## 
##   amastigote promastigote 
##           10          101
## This analysis will include surrogate estimates from: svaseq.
## This will pre-filter the input data using normalize_expt's: TRUE argument.
## Removing 0 low-count genes (8581 remaining).
## Setting 539 low elements to zero.
## transform_counts: Found 539 values equal to 0, adding 1 to the matrix.
## Finished running DE analyses, collecting outputs.
## Comparing analyses.
pro_ama_table <- combine_de_tables(pro_ama, excel="excel/tmrc2_pro_vs_ama.xlsx")
## Deleting the file excel/tmrc2_pro_vs_ama.xlsx before writing the tables.

1 Human macrophage comparison

new_conditions <- paste0(pData(hs_macrophage)[["macrophagetreatment"]], "_",
                         pData(hs_macrophage)[["macrophagezymodeme"]])
hs_macrophage <- set_expt_conditions(hs_macrophage, fact = new_conditions)

hs_macrophage_de <- all_pairwise(hs_macrophage, model_batch="svaseq", filter=TRUE)
## This DE analysis will perform all pairwise comparisons among:
## 
##   inf_sb_z2.2   inf_sb_z2.3      inf_z2.2      inf_z2.3    uninf_none 
##             6             6             6             6             2 
## uninf_sb_none 
##             2
## This analysis will include surrogate estimates from: svaseq.
## This will pre-filter the input data using normalize_expt's: TRUE argument.
## Removing 0 low-count genes (11460 remaining).
## Setting 757 low elements to zero.
## transform_counts: Found 757 values equal to 0, adding 1 to the matrix.
## Finished running DE analyses, collecting outputs.
## Comparing analyses.

tmrc2_human_keepers <- list(
    "z23nosb_vs_uninf" = c("infz23", "uninfnone"),
    "z22nosb_vs_uninf" = c("infz22", "uninfnone"),
    "z23nosb_vs_z22nosb" = c("infz23", "infz22"),
    "z23sb_vs_z22sb" = c("infsbz23", "infsbz22"),
    "z23sb_vs_z23nosb" = c("infsbz23", "infz23"),
    "z22sb_vs_z22nosb" = c("infsbz22", "infz22"),
    "z23sb_vs_sb" = c("infz23", "uninfsbnone"),
    "z22sb_vs_sb" = c("infz22", "uninfsbnone"),
    "sb_vs_uninf" = c("uninfsbnone", "uninfnone"))
hs_macrophage_table <- combine_de_tables(
    hs_macrophage_de,
    keepers = tmrc2_human_keepers,
    excel=glue::glue("excel/macrophage_human_table-v{ver}.xlsx"))
hs_macrophage_sig <- extract_significant_genes(
    hs_macrophage_table,
    excel=glue::glue("excel/macrophage_human_sig-v{ver}.xlsx"))
## Using p column: limma_adjp.
## Using p column: edger_adjp.
## Using p column: deseq_adjp.
## Using p column: basic_adjp.

2 SNP profiles

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

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

old_expt <- create_expt("sample_sheets/tmrc2_samples_20191203.xlsx",
                        file_column = "tophat2file")
## Reading the sample metadata.
## Dropped 13 rows from the sample metadata because the sample ID is blank.
## The sample definitions comprises: 50 rows(samples) and 38 columns(metadata fields).
## Warning in create_expt("sample_sheets/tmrc2_samples_20191203.xlsx", file_column
## = "tophat2file"): Some samples were removed when cross referencing the samples
## against the count data.
## Matched 8841 annotations and counts.
## Bringing together the count matrix and gene information.
## Saving the expressionset to 'expt.rda'.
## The final expressionset has 8841 features and 33 samples.
##tt <- lp_expt[["expressionset"]]
##rownames(tt) <- gsub(pattern = "^exon_", replacement = "", x = rownames(tt))
##rownames(tt) <- gsub(pattern = "\\.E1$", replacement = "", x = rownames(tt))
##lp_expt$expressionset <- tt

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

2.1 Create the SNP expressionset

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

## The next line drops the samples which are missing the SNP pipeline.
lp_snp <- subset_expt(lp_expt, subset="!is.na(pData(lp_expt)[['freebayessummary']])")
## subset_expt(): There were 101, now there are 101 samples.
new_snps <- count_expt_snps(lp_snp, annot_column = "freebayessummary", snp_column="PAIRED")
## New names:
## • `DP` -> `DP...3`
## • `RO` -> `RO...8`
## • `AO` -> `AO...9`
## • `QR` -> `QR...12`
## • `QA` -> `QA...13`
## • `DP` -> `DP...42`
## • `RO` -> `RO...43`
## • `QR` -> `QR...44`
## • `AO` -> `AO...45`
## • `QA` -> `QA...46`
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## • `DP` -> `DP...3`
## • `RO` -> `RO...8`
## • `AO` -> `AO...9`
## • `QR` -> `QR...12`
## • `QA` -> `QA...13`
## • `DP` -> `DP...42`
## • `RO` -> `RO...43`
## • `QR` -> `QR...44`
## • `AO` -> `AO...45`
## • `QA` -> `QA...46`
## Warning: NAs introduced by coercion
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## New names:
## • `DP` -> `DP...3`
## • `RO` -> `RO...8`
## • `AO` -> `AO...9`
## • `QR` -> `QR...12`
## • `QA` -> `QA...13`
## • `DP` -> `DP...42`
## • `RO` -> `RO...43`
## • `QR` -> `QR...44`
## • `AO` -> `AO...45`
## • `QA` -> `QA...46`
old_snps <- count_expt_snps(old_expt, annot_column = "bcftable", snp_column = 2)
## The rownames are missing the chromosome identifier,
## they probably came from an older version of this method.
nonzero_snps <- exprs(new_snps) != 0
colSums(nonzero_snps)
## tmrc20001 tmrc20065 tmrc20005 tmrc20007 tmrc20008 tmrc20027 tmrc20028 tmrc20032 
##         0     93649         0         0         0    351343    338580    146302 
## tmrc20040 tmrc20066 tmrc20039 tmrc20037 tmrc20038 tmrc20067 tmrc20068 tmrc20041 
##     58753     93615     25115     98958     97676     93954     96583     53184 
## tmrc20015 tmrc20009 tmrc20010 tmrc20016 tmrc20011 tmrc20012 tmrc20013 tmrc20017 
##     96398     15890     93816    146124     13914       456     94766     48288 
## tmrc20014 tmrc20018 tmrc20019 tmrc20070 tmrc20020 tmrc20021 tmrc20022 tmrc20025 
##     17245    140438     14829     97336     15484    101127     18143    364240 
## tmrc20024 tmrc20036 tmrc20069 tmrc20033 tmrc20026 tmrc20031 tmrc20076 tmrc20073 
##     18471     60087     18792     33663     15074     19139     18385     96169 
## tmrc20055 tmrc20079 tmrc20071 tmrc20078 tmrc20094 tmrc20042 tmrc20058 tmrc20072 
##     22246     96224     94353     18836     87878     19734     94524     50292 
## tmrc20059 tmrc20048 tmrc20057 tmrc20088 tmrc20056 tmrc20060 tmrc20077 tmrc20074 
##     94091     97164     48944     15594     22683     21506     18773     22132 
## tmrc20063 tmrc20053 tmrc20052 tmrc20064 tmrc20075 tmrc20051 tmrc20050 tmrc20049 
##     28254     20181    100709     93173     97982     94125     17200     16168 
## tmrc20062 tmrc20110 tmrc20080 tmrc20043 tmrc20083 tmrc20054 tmrc20085 tmrc20046 
##     93677     16997     96528     95623     21167     93603     89765     48608 
## tmrc20093 tmrc20089 tmrc20047 tmrc20090 tmrc20044 tmrc20045 tmrc20061 tmrc20105 
##     48254     90421     92637     91564     14861     50403    116906     86758 
## tmrc20108 tmrc20109 tmrc20098 tmrc20096 tmrc20097 tmrc20101 tmrc20092 tmrc20082 
##     97005     17932     92927     17534     46863     17753     16578    108121 
## tmrc20102 tmrc20099 tmrc20100 tmrc20091 tmrc20084 tmrc20087 tmrc20103 tmrc20104 
##     92380     91383     94381     15059     46548     14947     49368     94237 
## tmrc20086 tmrc20107 tmrc20081 tmrc20106 tmrc20095 
##     15813     95370     19533     18830     81200
## My old_snps is using an older annotation incorrectly, so fix it here:
Biobase::annotation(old_snps$expressionset) <- Biobase::annotation(new_snps$expressionset)
both_snps <- combine_expts(new_snps, old_snps)
both_norm <- normalize_expt(both_snps, transform = "log2", norm = "quant")
## transform_counts: Found 207502544 values equal to 0, adding 1 to the matrix.
## strains <- both_norm[["design"]][["strain"]]
both_strain <- set_expt_conditions(both_norm, fact = "strain")

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

2.2 Plot of SNP profiles for zymodemes

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

new_variant_heatmap <- plot_disheat(new_snps)
dev <- pp(file = "images/raw_snp_disheat.png", height=12, width=12)
new_variant_heatmap$plot
closed <- dev.off()
new_variant_heatmap$plot

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 41 rows.
## The factor z2.2 has 43 rows.
## The factor unknown has 2 rows.
## The factor z1.0 has only 1 row.
## The factor b2904 has only 1 row.
## The factor z3.0 has only 1 row.
## The factor z2.0 has only 1 row.
## The factor z1.5 has only 1 row.
## The factor z2.1 has 7 rows.
## The factor z2.4 has 2 rows.
## The factor z3.2 has only 1 row.
## The factor sh has 13 rows.
## The factor chr has 14 rows.
## The factor inf has 6 rows.
Biobase::annotation(old_expt$expressionset) = Biobase::annotation(lp_expt$expressionset)
both_expt <- combine_expts(lp_expt, old_expt)

snp_genes <- sm(snps_vs_genes(both_expt, snp_sets, expt_name_col = "chromosome"))
## I think we have some metrics here we can plot...
snp_subset <- snp_subset_genes(
  both_expt, both_snps,
  genes = c("LPAL13_120010900", "LPAL13_340013000", "LPAL13_000054100",
            "LPAL13_140006100", "LPAL13_180018500", "LPAL13_320022300"))
## remove_genes_expt(), before removal, there were 1514127 genes, now there are 179.
## There are 134 samples which kept less than 90 percent counts.
## tmrc20001 tmrc20065 tmrc20005 tmrc20007 tmrc20008 tmrc20027 tmrc20028 tmrc20032 
##  0.000000  0.010678  0.000000  0.000000  0.000000  0.018785  0.020084  0.010253 
## tmrc20040 tmrc20066 tmrc20039 tmrc20037 tmrc20038 tmrc20067 tmrc20068 tmrc20041 
##  0.008510  0.012818  0.015927  0.012126  0.013309  0.012772  0.011389  0.005641 
## tmrc20015 tmrc20009 tmrc20010 tmrc20016 tmrc20011 tmrc20012 tmrc20013 tmrc20017 
##  0.010374  0.000000  0.012046  0.007528  0.007586  0.219298  0.010836  0.004142 
## tmrc20014 tmrc20018 tmrc20019 tmrc20070 tmrc20020 tmrc20021 tmrc20022 tmrc20025 
##  0.005799  0.007833  0.006744  0.011301  0.012917  0.009889  0.000000  0.018120 
## tmrc20024 tmrc20036 tmrc20069 tmrc20033 tmrc20026 tmrc20031 tmrc20076 tmrc20073 
##  0.005414  0.003329  0.021286  0.011882  0.006634  0.005225  0.005439  0.011438 
## tmrc20055 tmrc20079 tmrc20071 tmrc20078 tmrc20094 tmrc20042 tmrc20058 tmrc20072 
##  0.017981  0.012471  0.011658  0.010618  0.013655  0.010135  0.010579  0.003977 
## tmrc20059 tmrc20048 tmrc20057 tmrc20088 tmrc20056 tmrc20060 tmrc20077 tmrc20074 
##  0.010628  0.013379  0.006129  0.019238  0.004409  0.013950  0.005327  0.013555 
## tmrc20063 tmrc20053 tmrc20052 tmrc20064 tmrc20075 tmrc20051 tmrc20050 tmrc20049 
##  0.010618  0.019821  0.010923  0.011806  0.015309  0.011687  0.005814  0.018555 
## tmrc20062 tmrc20110 tmrc20080 tmrc20043 tmrc20083 tmrc20054 tmrc20085 tmrc20046 
##  0.010675  0.011767  0.012432  0.013595  0.009449  0.011752  0.012254  0.002057 
## tmrc20093 tmrc20089 tmrc20047 tmrc20090 tmrc20044 tmrc20045 tmrc20061 tmrc20105 
##  0.004145  0.012165  0.014033  0.013106  0.013458  0.001984  0.013686  0.013832 
## tmrc20108 tmrc20109 tmrc20098 tmrc20096 tmrc20097 tmrc20101 tmrc20092 tmrc20082 
##  0.011340  0.022306  0.011837  0.022813  0.004268  0.005633  0.018096  0.010469 
## tmrc20102 tmrc20099 tmrc20100 tmrc20091 tmrc20084 tmrc20087 tmrc20103 tmrc20104 
##  0.011907  0.010943  0.011655  0.013281  0.008593  0.006690  0.004051  0.011673 
## tmrc20086 tmrc20107 tmrc20081 tmrc20106 tmrc20095  hpgl0242  hpgl0243  hpgl0244 
##  0.006324  0.013631  0.010239  0.010621  0.020936  0.000000  0.029118  0.027772 
##  hpgl0245  hpgl0246  hpgl0247  hpgl0248  hpgl0316  hpgl0318  hpgl0320  hpgl0322 
##  0.009257  0.028169  0.069020  0.000000  0.013550  0.106838  0.058167  0.052041 
##  hpgl0631  hpgl0632  hpgl0633  hpgl0634  hpgl0635  hpgl0636  hpgl0638  hpgl0639 
##  0.083820  0.000000  0.032016  0.048212  0.030793  0.000000  0.000000  0.029641 
##  hpgl0641  hpgl0643  hpgl0651  hpgl0652  hpgl0653  hpgl0654  hpgl0655  hpgl0656 
##  0.024917  0.109469  0.086478  0.000000  0.036742  0.040519  0.035387  0.000000 
##  hpgl0658  hpgl0659  hpgl0660  hpgl0661  hpgl0662  hpgl0663 
##  0.084983  0.000000  0.038113  0.033367  0.028960  0.000000
zymo_heat <- plot_sample_heatmap(snp_subset, row_label = rownames(exprs(snp_subset)))
zymo_heat

2.3 Compare variants to DE genes

Najib has asked a few times about the relationship between variants and DE genes. In subsequent conversations I figured out what he really wants to learn is variants in the UTR (most likely 5’) which might affect expression of genes. The following explicitly does not help this question, but is a paralog: is there a relationship between variants in the CDS and differential expression?

vars_df <- data.frame(ID = names(snp_genes$summary_by_gene), variants = as.numeric(snp_genes$summary_by_gene))
vars_df[["variants"]] <- log2(vars_df[["variants"]] + 1)
vars_by_de_gene <- merge(zy_df, vars_df, by.x="row.names", by.y="ID")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'merge': object 'zy_df' not found
cor.test(vars_by_de_gene$deseq_logfc, vars_by_de_gene$variants)
## Error in cor.test(vars_by_de_gene$deseq_logfc, vars_by_de_gene$variants): object 'vars_by_de_gene' not found
variants_wrt_logfc <- plot_linear_scatter(vars_by_de_gene[, c("deseq_logfc", "variants")])
## Error in data.frame(df[, c(1, 2)]): object 'vars_by_de_gene' not found
variants_wrt_logfc$scatter
## Error in eval(expr, envir, enclos): object 'variants_wrt_logfc' not found
## It looks like there might be some genes of interest, even though this is not actually
## the question of interest.

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

2.4 SNPS associated with clinical response in the TMRC samples

clinical_sets <- get_snp_sets(new_snps, factor = "clinicalresponse")
## The factor cure has 38 rows.
## The factor failure has 38 rows.
## The factor laboratory line has only 1 row.
## The factor laboratory line miltefosine resistant has only 1 row.
## The factor nd has 19 rows.
## The factor reference strain has 4 rows.
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)

2.4.1 Cross reference these variants by gene

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

fail_ref_snps <- as.data.frame(clinical_snps[["inters"]][["failure, reference strain"]])
fail_ref_snps <- rbind(fail_ref_snps,
                       as.data.frame(clinical_snps[["inters"]][["failure"]]))
cure_snps <- as.data.frame(clinical_snps[["inters"]][["cure"]])

head(fail_ref_snps)
##                                       seqnames  start    end width strand
## chr_LpaL13-01_pos_110212_ref_T_alt_C LpaL13-01 110212 110213     2      +
## chr_LpaL13-01_pos_156486_ref_T_alt_C LpaL13-01 156486 156487     2      +
## chr_LpaL13-02_pos_143639_ref_T_alt_C LpaL13-02 143639 143640     2      +
## chr_LpaL13-02_pos_196792_ref_A_alt_C LpaL13-02 196792 196793     2      +
## chr_LpaL13-02_pos_197657_ref_T_alt_C LpaL13-02 197657 197658     2      +
## chr_LpaL13-02_pos_198494_ref_T_alt_C LpaL13-02 198494 198495     2      +
head(cure_snps)
##                                       seqnames  start    end width strand
## chr_LpaL13-01_pos_137363_ref_C_alt_A LpaL13-01 137363 137364     2      +
## chr_LpaL13-01_pos_140306_ref_C_alt_A LpaL13-01 140306 140307     2      +
## chr_LpaL13-01_pos_169299_ref_A_alt_G LpaL13-01 169299 169300     2      +
## chr_LpaL13-02_pos_71147_ref_G_alt_A  LpaL13-02  71147  71148     2      +
## chr_LpaL13-02_pos_76744_ref_A_alt_G  LpaL13-02  76744  76745     2      +
## chr_LpaL13-02_pos_76932_ref_G_alt_A  LpaL13-02  76932  76933     2      +
write.csv(file="csv/cure_variants.txt", x=rownames(cure_snps))
## Warning in file(file, ifelse(append, "a", "w")): cannot open file 'csv/
## cure_variants.txt': No such file or directory
## Error in file(file, ifelse(append, "a", "w")): cannot open the connection
write.csv(file="csv/fail_variants.txt", x=rownames(fail_ref_snps))
## Warning in file(file, ifelse(append, "a", "w")): cannot open file 'csv/
## fail_variants.txt': No such file or directory
## Error in file(file, ifelse(append, "a", "w")): cannot open the connection
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, reference strain"]]),
                           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

3 Zymodeme for new samples

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

3.1 Hunt for snp clusters

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

## Drop the 2.1, 2.4, unknown, and null
pruned_snps <- subset_expt(new_snps, subset="condition=='z2.2'|condition=='z2.3'")
## subset_expt(): There were 101, now there are 84 samples.
new_sets <- get_snp_sets(pruned_snps, factor = "zymodemecategorical")
## The factor z22 has 43 rows.
## The factor z23 has 41 rows.
summary(new_sets)
##               Length Class      Mode     
## medians         3    data.frame list     
## possibilities   2    -none-     character
## intersections   3    -none-     list     
## chr_data      726    -none-     list     
## set_names       4    -none-     list     
## invert_names    4    -none-     list     
## density       726    -none-     numeric
## 1000000: 2.2
## 0100000: 2.3

summary(new_sets[["intersections"]][["10"]])
##    Length     Class      Mode 
##      3562 character character
write.csv(file="csv/variants_22.csv", x=new_sets[["intersections"]][["10"]])
## Warning in file(file, ifelse(append, "a", "w")): cannot open file 'csv/
## variants_22.csv': No such file or directory
## Error in file(file, ifelse(append, "a", "w")): cannot open the connection
summary(new_sets[["intersections"]][["01"]])
##    Length     Class      Mode 
##     81485 character character
write.csv(file="csv/variants_23.csv", x=new_sets[["intersections"]][["01"]])
## Warning in file(file, ifelse(append, "a", "w")): cannot open file 'csv/
## variants_23.csv': No such file or directory
## Error in file(file, ifelse(append, "a", "w")): cannot open the connection

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

3.1.1 A small function for searching for potential PCR primers

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

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

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

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

  ## Working interactively here.

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

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

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

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

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

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

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

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

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

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

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

3.1.2 Extract a promising region from the genome

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

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

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

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

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


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


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

3.2 Go hunting for Sanger sequencing regions

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

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

fun_stuff <- snp_density_primers(
    pheno_snps,
    bsgenome = "BSGenome.Leishmania.panamensis.MHOMCOL81L13.v53",
    gff = "reference/TriTrypDB-53_LpanamensisMHOMCOL81L13.gff")
## Loading required package: BSgenome
## Loading required package: Biostrings
## Loading required package: XVector
## 
## Attaching package: 'Biostrings'
## The following object is masked from 'package:base':
## 
##     strsplit
## Loading required package: rtracklayer
## Warning in seq_len(chromosomes): first element used of 'length.out' argument
## Warning in snp_density_primers(pheno_snps, bsgenome =
## "BSGenome.Leishmania.panamensis.MHOMCOL81L13.v53", : NAs introduced by coercion
## Error in seq_len(chromosomes): argument must be coercible to non-negative integer
drop_scaffolds <- grepl(x = rownames(fun_stuff$favorites), pattern = "SCAF")
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'grepl': error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'fun_stuff' not found
favorite_primer_regions <- fun_stuff[["favorites"]][!drop_scaffolds, ]
## Error in eval(expr, envir, enclos): object 'fun_stuff' not found
favorite_primer_regions[["bin"]] <- rownames(favorite_primer_regions)
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': object 'favorite_primer_regions' not found
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:Biostrings':
## 
##     collapse, intersect, setdiff, setequal, union
## The following object is masked from 'package:XVector':
## 
##     slice
## The following object is masked from 'package:hpgltools':
## 
##     combine
## The following object is masked from 'package:testthat':
## 
##     matches
## The following objects are masked from 'package:GenomicRanges':
## 
##     intersect, setdiff, union
## The following object is masked from 'package:GenomeInfoDb':
## 
##     intersect
## The following objects are masked from 'package:IRanges':
## 
##     collapse, desc, intersect, setdiff, slice, union
## The following objects are masked from 'package:S4Vectors':
## 
##     first, intersect, rename, setdiff, setequal, union
## The following object is masked from 'package:matrixStats':
## 
##     count
## The following object is masked from 'package:Biobase':
## 
##     combine
## The following objects are masked from 'package:BiocGenerics':
## 
##     combine, intersect, setdiff, union
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
favorite_primer_regions <- favorite_primer_regions %>%
  relocate(bin)
## Error in relocate(., bin): object 'favorite_primer_regions' not found

3.3 Combine this table with 2.2/2.3 genes

Here is my note from our meeting:

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

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

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

3.4 Make a heatmap describing the clustering of variants

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

snp_genes <- sm(snps_vs_genes(lp_expt, new_sets, expt_name_col = "chromosome"))

clinical_colors_v2 <- list(
    "z22" = "#0000cc",
    "z23" = "#cc0000")
new_zymo_norm <- normalize_expt(pruned_snps, normq = "quant") %>%
  set_expt_conditions(fact = "zymodemecategorical") %>%
  set_expt_colors(clinical_colors_v2)

zymo_heat <- plot_disheat(new_zymo_norm)
dev <- pp(file = "images/onlyz22_z23_snp_heatmap.pdf", width=12, height=12)
zymo_heat[["plot"]]
closed <- dev.off()
zymo_heat[["plot"]]

3.4.1 Annotated heatmap of variants

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

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

##hmcols <- colorRampPalette(c("yellow","black","darkblue"))(256)
correlations <- hpgl_cor(exprs(both_norm))
## Warning in stats::cor(df, method = method, ...): the standard deviation is zero
na_idx <- is.na(correlations)
correlations[na_idx] <- 0

zymo_missing_idx <- is.na(des[["zymodemecategorical"]])
des[["zymodemecategorical"]] <- as.character(des[["zymodemecategorical"]])
des[["clinicalcategorical"]] <- as.character(des[["clinicalcategorical"]])
des[zymo_missing_idx, "zymodemecategorical"] <- "unknown"
mydendro <- list(
  "clustfun" = hclust,
  "lwd" = 2.0)
col_data <- as.data.frame(des[, c("zymodemecategorical", "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)
zymo_annot_heat <- annHeatmap2(
    correlations,
    dendrogram = mydendro,
    annotation = myannot,
    cluster = myclust,
    labels = mylabs,
    ## The following controls if the picture is symmetric
    scale = "none",
    col = hmcols)
## Warning in breakColors(breaks, col): more colors than classes: ignoring 26 last
## colors
dev <- pp(file = "images/dendro_heatmap.png", height = 20, width = 20)
plot(zymo_annot_heat)
closed <- dev.off()
plot(zymo_annot_heat)

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

big heatmap

xref_prop <- table(pheno_snps[["conditions"]])
pheno_snps$conditions
##  [1] "z2.3" "z2.3" "z2.2" "z2.3" "z2.2" "z2.3" "z2.3" "z2.3" "z2.3" "z2.2"
## [11] "z2.3" "z2.2" "z2.3" "z2.3" "z2.2" "z2.2" "z2.3" "z2.2" "z2.2" "z2.3"
## [21] "z2.2" "z2.3" "z2.2" "z2.3" "z2.2" "z2.2" "z2.2" "z2.2" "z2.2" "z2.2"
## [31] "z2.2" "z2.3" "z2.2" "z2.3" "z2.3" "z2.2" "z2.2" "z2.3" "z2.2" "z2.3"
## [41] "z2.3" "z2.2" "z2.2" "z2.2" "z2.2" "z2.3" "z2.3" "z2.3" "z2.2" "z2.3"
## [51] "z2.3" "z2.3" "z2.3" "z2.2" "z2.2"
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
}
keepers <- grepl(x = rownames(new_tbl), pattern = "LpaL13")
new_tbl <- new_tbl[keepers, ]
new_tbl[["strong22"]] <- 1.001 - new_tbl[["z2.2"]]
new_tbl[["strong23"]] <- 1.001 - new_tbl[["z2.3"]]
s22_na <- new_tbl[["strong22"]] > 1
new_tbl[s22_na, "strong22"] <- 1
s23_na <- new_tbl[["strong23"]] > 1
new_tbl[s23_na, "strong23"] <- 1

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", "strong22", "strong23")]

library(CMplot)
## Much appreciate for using CMplot.
## Full description, Bug report, Suggestion and the latest codes:
## https://github.com/YinLiLin/CMplot
simplify <- new_tbl
simplify[["strong22"]] <- NULL

CMplot(simplify, bin.size = 100000)
##  SNP-Density Plotting.
##  Circular-Manhattan Plotting strong23.
##  Rectangular-Manhattan Plotting strong23.
##  QQ Plotting strong23.
##  Plots are stored in: /mnt/cbcb/fs01_abelew/cbcb-lab/nelsayed/scratch/atb/rnaseq/lpanamensis_tmrc_git
CMplot(new_tbl, plot.type="m", multracks=TRUE, threshold = c(0.01, 0.05),
       threshold.lwd=c(1,1), threshold.col=c("black","grey"),
       amplify=TRUE, bin.size=10000,
       chr.den.col=c("darkgreen", "yellow", "red"),
       signal.col=c("red", "green", "blue"),
       signal.cex=1, file="jpg", memo="", dpi=300, file.output=TRUE, verbose=TRUE)
##  Multracks-Manhattan Plotting strong22.
##  Multracks-Manhattan Plotting strong23.
##  Multraits-Rectangular Plotting...(finished 73%)
 Multraits-Rectangular Plotting...(finished 74%)
 Multraits-Rectangular Plotting...(finished 75%)
 Multraits-Rectangular Plotting...(finished 76%)
 Multraits-Rectangular Plotting...(finished 77%)
 Multraits-Rectangular Plotting...(finished 78%)
 Multraits-Rectangular Plotting...(finished 79%)
 Multraits-Rectangular Plotting...(finished 80%)
 Multraits-Rectangular Plotting...(finished 81%)
 Multraits-Rectangular Plotting...(finished 82%)
 Multraits-Rectangular Plotting...(finished 83%)
 Multraits-Rectangular Plotting...(finished 84%)
 Multraits-Rectangular Plotting...(finished 85%)
 Multraits-Rectangular Plotting...(finished 86%)
 Multraits-Rectangular Plotting...(finished 87%)
 Multraits-Rectangular Plotting...(finished 88%)
 Multraits-Rectangular Plotting...(finished 89%)
 Multraits-Rectangular Plotting...(finished 90%)
 Multraits-Rectangular Plotting...(finished 91%)
 Multraits-Rectangular Plotting...(finished 92%)
 Multraits-Rectangular Plotting...(finished 93%)
 Multraits-Rectangular Plotting...(finished 94%)
 Multraits-Rectangular Plotting...(finished 95%)
 Multraits-Rectangular Plotting...(finished 96%)
 Multraits-Rectangular Plotting...(finished 97%)
 Multraits-Rectangular Plotting...(finished 98%)
 Multraits-Rectangular Plotting...(finished 99%)
 Multraits-Rectangular Plotting...(finished 100%)
##  Plots are stored in: /mnt/cbcb/fs01_abelew/cbcb-lab/nelsayed/scratch/atb/rnaseq/lpanamensis_tmrc_git

3.5 Try out MatrixEQTL

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

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

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

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

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

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

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

useModel = modelLINEAR # modelANOVA, modelLINEAR, or modelLINEAR_CROSS

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

me = Matrix_eQTL_main(
    snps = snps,
    gene = gene,
    cvrt = cvrt,
    output_file_name = output_file_name_tra,
    pvOutputThreshold = pvOutputThreshold_tra,
    useModel = useModel,
    errorCovariance = errorCovariance,
    verbose = TRUE,
    output_file_name.cis = output_file_name_cis,
    pvOutputThreshold.cis = pvOutputThreshold_cis,
    snpspos = snpspos,
    genepos = genepos,
    cisDist = cisDist,
    pvalue.hist = "qqplot",
    min.pv.by.genesnp = FALSE,
    noFDRsaveMemory = FALSE);
if (!isTRUE(get0("skip_load"))) {
  pander::pander(sessionInfo())
  message(paste0("This is hpgltools commit: ", get_git_commit()))
  message(paste0("Saving to ", savefile))
  tmp <- sm(saveme(filename = savefile))
}
## If you wish to reproduce this exact build of hpgltools, invoke the following:
## > git clone http://github.com/abelew/hpgltools.git
## > git reset 605cc89b5f1cadea6923b53ac71e234ba0181fe7
## This is hpgltools commit: Wed Aug 10 22:39:40 2022 -0400: 605cc89b5f1cadea6923b53ac71e234ba0181fe7
## Saving to tmrc2_visualization_202207.rda.xz
tmp <- loadme(filename = savefile)
LS0tCnRpdGxlOiAiTC4gcGFuYW1lbnNpcyAyMDIyMDc6IFZpc3VhbGl6aW5nIFRNUkMyIEFuYWx5c2VzIgphdXRob3I6ICJhdGIgYWJlbGV3QGdtYWlsLmNvbSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiBodG1sX2RvY3VtZW50OgogIGNvZGVfZG93bmxvYWQ6IHRydWUKICBjb2RlX2ZvbGRpbmc6IHNob3cKICBmaWdfY2FwdGlvbjogdHJ1ZQogIGZpZ19oZWlnaHQ6IDcKICBmaWdfd2lkdGg6IDcKICBoaWdobGlnaHQ6IGRlZmF1bHQKICBrZWVwX21kOiBmYWxzZQogIG1vZGU6IHNlbGZjb250YWluZWQKICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQogIHRoZW1lOiByZWFkYWJsZQogIHRvYzogdHJ1ZQogIHRvY19mbG9hdDoKICAgY29sbGFwc2VkOiBmYWxzZQogICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQotLS0KCjxzdHlsZT4KICBib2R5IC5tYWluLWNvbnRhaW5lciB7CiAgICBtYXgtd2lkdGg6IDE2MDBweDsKICB9Cjwvc3R5bGU+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkoSGVhdHBsdXMpCmxpYnJhcnkoaHBnbHRvb2xzKQp0dCA8LSBkZXZ0b29sczo6bG9hZF9hbGwoIn4vaHBnbHRvb2xzIikKa25pdHI6Om9wdHNfa25pdCRzZXQocHJvZ3Jlc3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgd2lkdGggPSA5MCwKICAgICAgICAgICAgICAgICAgICAgZWNobyA9IFRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBmaWcud2lkdGggPSA4LAogICAgICAgICAgICAgICAgICAgICAgZmlnLmhlaWdodCA9IDgsCiAgICAgICAgICAgICAgICAgICAgICBkcGkgPSA5NikKb2xkX29wdGlvbnMgPC0gb3B0aW9ucyhkaWdpdHMgPSA0LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICBrbml0ci5kdXBsaWNhdGUubGFiZWwgPSAiYWxsb3ciKQpnZ3Bsb3QyOjp0aGVtZV9zZXQoZ2dwbG90Mjo6dGhlbWVfYncoYmFzZV9zaXplID0gMTIpKQp2ZXIgPC0gIjIwMjIwNyIKcHJldmlvdXNfZmlsZSA8LSAiIgpydW5kYXRlIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCBmb3JtYXQgPSAiJVklbSVkIikKCiMjIHRtcCA8LSB0cnkoc20obG9hZG1lKGZpbGVuYW1lID0gZ3N1YihwYXR0ZXJuID0gIlxcLlJtZCIsIHJlcGxhY2UgPSAiXFwucmRhXFwueHoiLCB4ID0gcHJldmlvdXNfZmlsZSkpKSkKcm1kX2ZpbGUgPC0gZ2x1ZTo6Z2x1ZSgidG1yYzJfdmlzdWFsaXphdGlvbl97dmVyfS5SbWQiKQpzYXZlZmlsZSA8LSBnc3ViKHBhdHRlcm4gPSAiXFwuUm1kIiwgcmVwbGFjZSA9ICJcXC5yZGFcXC54eiIsIHggPSBybWRfZmlsZSkKbG9hZGVkIDwtIGxvYWQoZmlsZT1nbHVlOjpnbHVlKCJyZGEvdG1yYzJfZGF0YV9zdHJ1Y3R1cmVzLXZ7dmVyfS5yZGEiKSkKYGBgCgpgYGB7ciBpbml0aWFsX3Zpc3VhbH0KbGlic2l6ZXMgPC0gcGxvdF9saWJzaXplKGxwX2V4cHQpCmRldiA8LSBwcCgiaW1hZ2VzL2xwX2V4cHRfbGlic2l6ZXMucG5nIiwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gOSkKbGlic2l6ZXMkcGxvdApjbG9zZWQgPC0gZGV2Lm9mZigpCmxpYnNpemVzJHBsb3QKCiMjIEkgdGhpbmsgc2FtcGxlcyA3LDEwIHNob3VsZCBiZSByZW1vdmVkIGF0IG1pbmltdW0sIHByb2JhYmx5IGFsc28gOSwxMQpub256ZXJvIDwtIHBsb3Rfbm9uemVybyhscF9leHB0KQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvbHBfbm9uemVyby5wbmciLCB3aWR0aD05LCBoZWlnaHQgPSA5KQpub256ZXJvJHBsb3QKY2xvc2VkIDwtIGRldi5vZmYoKQpub256ZXJvJHBsb3QKCmxwX2JveCA8LSBwbG90X2JveHBsb3QobHBfZXhwdCkKZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL2xwX2V4cHRfYm94cGxvdC5wbmciLCB3aWR0aCA9IDE2LCBoZWlnaHQgPSA5KQpscF9ib3gKY2xvc2VkIDwtIGRldi5vZmYoKQpscF9ib3gKCmZpbHRlcl9wbG90IDwtIHBsb3RfbGlic2l6ZV9wcmVwb3N0KGxwX2V4cHQpCmZpbHRlcl9wbG90JGxvd2dlbmVfcGxvdApmaWx0ZXJfcGxvdCRjb3VudF9wbG90Cgp0YWJsZShwRGF0YShscF9leHB0KVtbInp5bW9kZW1lY2F0ZWdvcmljYWwiXV0pCnRhYmxlKHBEYXRhKGxwX2V4cHQpW1siY2xpbmljYWxyZXNwb25zZSJdXSkKYGBgCgojIyBEaXN0cmlidXRpb24gVmlzdWFsaXphdGlvbnMKCk5hamliJ3MgZmF2b3JpdGUgcGxvdHMgYXJlIG9mIGNvdXJzZSB0aGUgUENBL1ROU0UuICBUaGVzZSBhcmUgbmljZSB0byBsb29rIGF0IGluCm9yZGVyIHRvIGdldCBhIHNlbnNlIG9mIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gc2FtcGxlcy4gIFRoZXkgYWxzbyBwcm92aWRlIGEKZ29vZCBvcHBvcnR1bml0eSB0byBzZWUgd2hhdCBoYXBwZW5zIHdoZW4gb25lIGFwcGxpZXMgZGlmZmVyZW50IG5vcm1hbGl6YXRpb25zLApzdXJyb2dhdGUgYW5hbHlzZXMsIGZpbHRlcnMsIGV0Yy4gIEluIGFkZGl0aW9uLCBvbmUgbWF5IHNldCBkaWZmZXJlbnQKZXhwZXJpbWVudGFsIGZhY3RvcnMgYXMgdGhlIHByaW1hcnkgJ2NvbmRpdGlvbicgKHVzdWFsbHkgdGhlIGNvbG9yIG9mIHBsb3RzKSBhbmQKc3Vycm9nYXRlICdiYXRjaGVzJy4KCiMjIEJ5IFN1c2NlcHRpbGliaXR5CgpDb2x1bW4gJ1EnIGluIHRoZSBzYW1wbGUgc2hlZXQsIG1ha2UgYSBjYXRlZ29yaWNhbCB2ZXJzaW9uIG9mIGl0IHdpdGggdGhlc2UgcGFyYW1ldGVyczoKCiogMCA8PSB4IDw9IDM1IGlzIHJlc2lzdGFudAoqIDM2IDw9IHggPD0gNDggaXMgYW1iaWd1b3VzCiogNDkgPD0geCBpcyBzZW5zaXRpdmUKCmBgYHtyIHByZV9xdWVzdGlvbnN9CnN0cmFpbl9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KGxwX3N0cmFpbiwgbm9ybSA9ICJxdWFudCIsIHRyYW5zZm9ybSA9ICJsb2cyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udmVydCA9ICJjcG0iLCBmaWx0ZXIgPSBUUlVFKQp6eW1vX3BjYSA8LSBwbG90X3BjYShzdHJhaW5fbm9ybSwgcGxvdF90aXRsZSA9ICJQQ0Egb2YgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiLAogICAgICAgICAgICAgICAgICAgICBwbG90X2xhYmVscyA9IEZBTFNFKQpnZ3BsdCh6eW1vX3BjYSRwbG90KQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvcHJvbWFzdGlnb3RlX3p5bW9jb2xfc2Vuc3NoYXBlLnBuZyIpCnp5bW9fcGNhJHBsb3QKY2xvc2VkIDwtIGRldi5vZmYoKQp6eW1vX3BjYSRwbG90Cgp6eW1vX3RzbmUgPC0gcGxvdF90c25lKHN0cmFpbl9ub3JtLCBwbG90X3RpdGxlID0gIlRTTkUgb2YgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiKQp6eW1vX3RzbmUkcGxvdAoKc3RyYWluX25iIDwtIG5vcm1hbGl6ZV9leHB0KGxwX3N0cmFpbiwgY29udmVydCA9ICJjcG0iLCB0cmFuc2Zvcm0gPSAibG9nMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLCBiYXRjaCA9ICJzdmFzZXEiKQpzdHJhaW5fbmJfcGNhIDwtIHBsb3RfcGNhKHN0cmFpbl9uYiwgcGxvdF90aXRsZSA9ICJQQ0Egb2YgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfbGFiZWxzID0gRkFMU0UpCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9jbGluaWNhbF9uYl9wY2Ffc3VzX3NoYXBlLnBuZyIpCnN0cmFpbl9uYl9wY2EkcGxvdApjbG9zZWQgPC0gZGV2Lm9mZigpCnN0cmFpbl9uYl9wY2EkcGxvdAoKc3RyYWluX25iX3RzbmUgPC0gcGxvdF90c25lKHN0cmFpbl9uYiwgcGxvdF90aXRsZSA9ICJUU05FIG9mIHBhcmFzaXRlIGV4cHJlc3Npb24gdmFsdWVzIikKc3RyYWluX25iX3RzbmUkcGxvdAoKY29yaGVhdCA8LSBwbG90X2NvcmhlYXQoc3RyYWluX25vcm0sIHBsb3RfdGl0bGUgPSAiQ29ycmVsYXRpb24gaGVhdG1hcCBvZiBwYXJhc2l0ZQogICAgICAgICAgICAgICAgIGV4cHJlc3Npb24gdmFsdWVzCiIpCmNvcmhlYXQkcGxvdAoKcGxvdF9zbShzdHJhaW5fbm9ybSkkcGxvdApgYGAKCiMjIExpbWl0IHRvIHRocmVlIHN0cmFpbnM6IDIuMS8yLjIvMi4zCgpgYGB7ciBvbmx5X3RocmVlX3N0cmFpbnN9Cm9ubHlfdGhyZWVfdHlwZXMgPC0gc3Vic2V0X2V4cHQobHBfc3RyYWluLCBzdWJzZXQgPSAiY29uZGl0aW9uPT0nejIuMSd8Y29uZGl0aW9uPT0nejIuMyd8Y29uZGl0aW9uPT0nejIuMiciKQpvbmx5X3RocmVlX25vcm0gPC0gc20obm9ybWFsaXplX2V4cHQob25seV90aHJlZV90eXBlcywgbm9ybSA9ICJxdWFudCIsIHRyYW5zZm9ybSA9ICJsb2cyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnZlcnQgPSAiY3BtIiwgYmF0Y2ggPSBGQUxTRSwgZmlsdGVyID0gVFJVRSkpICU+JQogIHNldF9leHB0X2JhdGNoZXMoZmFjdD0icGhhc2UiKQpvbmx5dGhyZWVfcGNhIDwtIHBsb3RfcGNhKG9ubHlfdGhyZWVfbm9ybSwgcGxvdF90aXRsZSA9ICJQQ0Egb2YgejIuMSwgejIuMiBhbmQgejIuMyBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF9sYWJlbHMgPSBGQUxTRSkKcHAoZmlsZT0iaW1hZ2VzL3Byb21hc3RpZ290ZV90aHJlZXR5cGVzX3p5bW9jb2xfbm9zaGFwZS5wbmciKQpvbmx5dGhyZWVfcGNhJHBsb3QKZGV2Lm9mZigpCm9ubHl0aHJlZV9wY2EkcGxvdApgYGAKCiMjIExpbWl0IHRvIGp1c3QgdHdvIHN0cmFpbnM6IDIuMi8yLjMKCmBgYHtyIHR3b19zdHJhaW5zfQpscF90d29fc3RyYWluc19ub3JtIDwtIHNtKG5vcm1hbGl6ZV9leHB0KGxwX3R3b19zdHJhaW5zLCBub3JtID0gInF1YW50IiwgdHJhbnNmb3JtID0gImxvZzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnZlcnQgPSAiY3BtIiwgYmF0Y2ggPSBGQUxTRSwgZmlsdGVyID0gVFJVRSkpCm9ubHl0d29fcGNhIDwtIHBsb3RfcGNhKGxwX3R3b19zdHJhaW5zX25vcm0sIHBsb3RfdGl0bGUgPSAiUENBIG9mIHoyLjIgYW5kIHoyLjMgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiLAogICAgICAgICAgICAgICAgICAgICAgICBwbG90X2xhYmVscyA9IEZBTFNFKQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvenltb196Mi4yX3oyLjNfcGNhX3N1c19zaGFwZS5wZGYiKQpvbmx5dHdvX3BjYSRwbG90CmNsb3NlZCA8LSBkZXYub2ZmKCkKb25seXR3b19wY2EkcGxvdApgYGAKCiMjIEJ5IEN1cmUvRmFpbCBzdGF0dXMKCmBgYHtyIGNmX3N0YXR1c30KY2Zfbm9ybSA8LSBub3JtYWxpemVfZXhwdChscF9jZiwgY29udmVydCA9ICJjcG0iLCB0cmFuc2Zvcm0gPSAibG9nMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybSA9ICJxdWFudCIsIGZpbHRlciA9IFRSVUUpCnN0YXJ0X2NmIDwtIHBsb3RfcGNhKGNmX25vcm0sIHBsb3RfdGl0bGUgPSAiUENBIG9mIHBhcmFzaXRlIGV4cHJlc3Npb24gdmFsdWVzIiwKICAgICAgICAgICAgICAgICAgICAgcGxvdF9sYWJlbHMgPSBGQUxTRSkKZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL2NmX3N1c19zaGFwZS5wbmciKQpzdGFydF9jZiRwbG90CmNsb3NlZCA8LSBkZXYub2ZmKCkKc3RhcnRfY2YkcGxvdAoKY2ZfbmIgPC0gbm9ybWFsaXplX2V4cHQobHBfY2Zfa25vd24sIGNvbnZlcnQgPSAiY3BtIiwgdHJhbnNmb3JtID0gImxvZzIiLAogICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLCBiYXRjaCA9ICJzdmFzZXEiKQpjZl9uYl9wY2EgPC0gcGxvdF9wY2EoY2ZfbmIsIHBsb3RfdGl0bGUgPSAiUENBIG9mIHBhcmFzaXRlIGV4cHJlc3Npb24gdmFsdWVzIiwKICAgICAgICAgICAgICAgICAgICAgIHBsb3RfbGFiZWxzID0gRkFMU0UpCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9jZl9zdXNfc2hhcmVfbmIucG5nIikKY2ZfbmJfcGNhJHBsb3QKY2xvc2VkIDwtIGRldi5vZmYoKQpjZl9uYl9wY2EkcGxvdAoKY2Zfbm9ybSA8LSBub3JtYWxpemVfZXhwdChscF9jZiwgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwgbm9ybSA9ICJxdWFudCIpCnRlc3QgPC0gcGNhX2luZm9ybWF0aW9uKGNmX25vcm0sCiAgICAgICAgICAgICAgICAgICAgICAgIGV4cHRfZmFjdG9ycyA9IGMoImNsaW5pY2FsY2F0ZWdvcmljYWwiLCAienltb2RlbWVjYXRlZ29yaWNhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGhvZ2Vuc3RyYWluIiwgInBhc3NhZ2VudW1iZXIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgbnVtX2NvbXBvbmVudHMgPSA2LCBwbG90X3BjYXMgPSBUUlVFKQp0ZXN0JGFub3ZhX3AKdGVzdCRjb3JfaGVhdG1hcApgYGAKCmBgYHtyIHN1c2NlcHRpYmlsaXR5X3BjYX0Kc3VzX25vcm0gPC0gbm9ybWFsaXplX2V4cHQobHBfc3VzY2VwdGliaWxpdHksIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtID0gInF1YW50IiwgZmlsdGVyID0gVFJVRSkKc3VzX3BjYSA8LSBwbG90X3BjYShzdXNfbm9ybSwgcGxvdF90aXRsZSA9ICJQQ0Egb2YgcGFyYXNpdGUgZXhwcmVzc2lvbiB2YWx1ZXMiLAogICAgICAgICAgICAgICAgICAgIHBsb3RfbGFiZWxzID0gRkFMU0UpCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9zdXNfbm9ybV9wY2EucG5nIikKc3VzX3BjYVtbInBsb3QiXV0KY2xvc2VkIDwtIGRldi5vZmYoKQpzdXNfcGNhW1sicGxvdCJdXQoKc3VzX25iIDwtIG5vcm1hbGl6ZV9leHB0KGxwX3N1c2NlcHRpYmlsaXR5LCB0cmFuc2Zvcm0gPSAibG9nMiIsIGNvbnZlcnQgPSAiY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGJhdGNoID0gInN2YXNlcSIsIGZpbHRlciA9IFRSVUUpCnN1c19uYl9wY2EgPC0gcGxvdF9wY2Eoc3VzX25iLCBwbG90X3RpdGxlID0gIlBDQSBvZiBwYXJhc2l0ZSBleHByZXNzaW9uIHZhbHVlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgcGxvdF9sYWJlbHMgPSBGQUxTRSkKZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL3N1c19uYl9wY2EucG5nIikKc3VzX25iX3BjYVtbInBsb3QiXV0KY2xvc2VkIDwtIGRldi5vZmYoKQpzdXNfbmJfcGNhW1sicGxvdCJdXQpgYGAKCiMjIFp5bW9kZW1lIGVuenltZSBnZW5lIElEcwoKTmFqaWIgcmVhZCBtZSBhbiBlbWFpbCBsaXN0aW5nIG9mZiB0aGUgZ2VuZSBuYW1lcyBhc3NvY2lhdGVkIHdpdGggdGhlIHp5bW9kZW1lCmNsYXNzaWZpY2F0aW9uLiAgSSB0b29rIHRob3NlIG5hbWVzIGFuZCBjcm9zcyByZWZlcmVuY2VkIHRoZW0gYWdhaW5zdCB0aGUKTGVpc2htYW5pYSBwYW5hbWVuc2lzIGdlbmUgYW5ub3RhdGlvbnMgYW5kIGZvdW5kIHRoZSBmb2xsb3dpbmc6CgpUaGV5IGFyZToKCjEuIEFMQVQ6IExQQUwxM18xMjAwMTA5MDAgLS0gYWxhbmluZSBhbWlub3RyYW5zZmVyYXNlCjIuIEFTQVQ6IExQQUwxM18zNDAwMTMwMDAgLS0gYXNwYXJ0YXRlIGFtaW5vdHJhbnNmZXJhc2UKMy4gRzZQRDogTFBBTDEzXzAwMDA1NDEwMCAtLSBnbHVjYXNlLTYtcGhvc3BoYXRlIDEtZGVoeWRyb2dlbmFzZQo0LiBOSDogTFBBTDEzXzE0MDA2MTAwLCBMUEFMMTNfMTgwMDE4NTAwIC0tIGlub3NpbmUtZ3VhbmluZSBudWNsZW9zaWRlIGh5ZHJvbGFzZQo1LiBNUEk6IExQQUwxM18zMjAwMjIzMDAgKG1heWJlKSAtLSBtYW5ub3NlIHBob3NwaGF0ZSBpc29tZXJhc2UgKEkgY2hvc2UgcGhvc3Bob21hbm5vc2UgaXNvbWVyYXNlKQoKR2l2ZW4gdGhlc2UgNiBnZW5lIElEcyAoTkggaGFzIHR3byBnZW5lIElEcyBhc3NvY2lhdGVkIHdpdGggaXQpLCBJIGNhbiBkbyBzb21lCmxvb2tpbmcgZm9yIHNwZWNpZmljIGRpZmZlcmVuY2VzIGFtb25nIHRoZSB2YXJpb3VzIHNhbXBsZXMuCgojIyMgRXhwcmVzc2lvbiBsZXZlbHMgb2Ygenltb2RlbWUgZ2VuZXMKClRoZSBmb2xsb3dpbmcgY3JlYXRlcyBhIGNvbG9yc3BhY2UgKHJlZCB0byBncmVlbikgaGVhdG1hcCBzaG93aW5nIHRoZSBvYnNlcnZlZApleHByZXNzaW9uIG9mIHRoZXNlIGdlbmVzIGluIGV2ZXJ5IHNhbXBsZS4KCmBgYHtyIHp5bW9kZW1lc30KbXlfZ2VuZXMgPC0gYygiTFBBTDEzXzEyMDAxMDkwMCIsICJMUEFMMTNfMzQwMDEzMDAwIiwgIkxQQUwxM18wMDAwNTQxMDAiLAogICAgICAgICAgICAgICJMUEFMMTNfMTQwMDA2MTAwIiwgIkxQQUwxM18xODAwMTg1MDAiLCAiTFBBTDEzXzMyMDAyMjMwMCIsCiAgICAgICAgICAgICAgIm90aGVyIikKbXlfbmFtZXMgPC0gYygiQUxBVCIsICJBU0FUIiwgIkc2UEQiLCAiTkh2MSIsICJOSHYyIiwgIk1QSSIsICJvdGhlciIpCgp6eW1vX2V4cHQgPC0gZXhjbHVkZV9nZW5lc19leHB0KHN0cmFpbl9ub3JtLCBpZHMgPSBteV9nZW5lcywgbWV0aG9kID0gImtlZXAiKQp6eW1vX2hlYXRtYXAgPC0gcGxvdF9zYW1wbGVfaGVhdG1hcCh6eW1vX2V4cHQsIHJvd19sYWJlbCA9IG15X25hbWVzKQp6eW1vX2hlYXRtYXAKYGBgCgpgYGB7ciBtYWNyb3BoYWdlX2h1bWFuX2NvbXBhcmlzb25zfQpuZXdfY29uZGl0aW9ucyA8LSBwYXN0ZTAocERhdGEoaHNfbWFjcm9waGFnZSlbWyJtYWNyb3BoYWdldHJlYXRtZW50Il1dLCAiXyIsCiAgICAgICAgICAgICAgICAgICAgICAgICBwRGF0YShoc19tYWNyb3BoYWdlKVtbIm1hY3JvcGhhZ2V6eW1vZGVtZSJdXSkKCnRtcmMyX21hY3JvcGhhZ2Vfbm9ybSA8LSBub3JtYWxpemVfZXhwdChoc19tYWNyb3BoYWdlLCBmaWx0ZXI9VFJVRSwgbm9ybT0icXVhbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udmVydD0iY3BtIiwgdHJhbnNmb3JtPSJsb2cyIikKbWFjcm9waGFnZV9oc19wY2EgPC0gcGxvdF9wY2EodG1yYzJfbWFjcm9waGFnZV9ub3JtLCBwbG90X2xhYmVscz1GQUxTRSkKcHAoZmlsZT0iaW1hZ2VzL21hY3JvcGhhZ2VfaHNfaW5mZWN0aW9uLnBuZyIpCm1hY3JvcGhhZ2VfaHNfcGNhJHBsb3QKZGV2Lm9mZigpCm1hY3JvcGhhZ2VfaHNfcGNhJHBsb3QKCmhzX21hY3JvcGhhZ2VfZHJ1Z3p5bW8gPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhoc19tYWNyb3BoYWdlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjdCA9IG5ld19jb25kaXRpb25zKQpoc19tYWNyb3BoYWdlX2RydWd6eW1vX25vcm0gPC0gbm9ybWFsaXplX2V4cHQoaHNfbWFjcm9waGFnZV9kcnVnenltbywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcj1UUlVFLCBub3JtPSJxdWFudCIsIGNvbnZlcnQ9ImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFuc2Zvcm09ImxvZzIiKQpwbG90X3BjYShoc19tYWNyb3BoYWdlX2RydWd6eW1vX25vcm0pJHBsb3QKCnRtcmMyX21hY3JvX25vc2JfZHJ1Z3p5bW8gPC0gc3Vic2V0X2V4cHQoaHNfbWFjcm9waGFnZV9kcnVnenltbywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQ9ImRydWchPSdBbnRpbW9ueSciKSAlPiUKICBzdWJzZXRfZXhwdChzdWJzZXQ9Im1hY3JvcGhhZ2V0cmVhdG1lbnQhPSd1bmluZiciKQp0bXJjMl9tYWNyb19ub3NiX2RydWd6eW1vX25vcm0gPC0gbm9ybWFsaXplX2V4cHQodG1yYzJfbWFjcm9fbm9zYl9kcnVnenltbywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcj1UUlVFLCBjb252ZXJ0PSJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybT0icXVhbnQiLCB0cmFuc2Zvcm09ImxvZzIiKQpwcChmaWxlPSJpbWFnZXMvdG1yYzJfbWFjcm9fbm9zYl9kcnVnenltb19wY2EucG5nIiwKICAgaW1hZ2U9cGxvdF9wY2EodG1yYzJfbWFjcm9fbm9zYl9kcnVnenltb19ub3JtLCBwbG90X2xhYmVscz1GQUxTRSkkcGxvdCkKYGBgCgoKYGBge3IgbWFjcm9waGFnZV9wYXJhc2l0ZV9jb21wYXJpc29uc30KbmV3X2NvbmRpdGlvbnMgPC0gcGFzdGUwKHBEYXRhKGxwX21hY3JvcGhhZ2UpW1sibWFjcm9waGFnZXRyZWF0bWVudCJdXSwgIl8iLAogICAgICAgICAgICAgICAgICAgICAgICAgcERhdGEobHBfbWFjcm9waGFnZSlbWyJtYWNyb3BoYWdlenltb2RlbWUiXV0pCmxwX21hY3JvcGhhZ2UgPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhscF9tYWNyb3BoYWdlLCBmYWN0ID0gbmV3X2NvbmRpdGlvbnMpCgptYWNyb3BoYWdlX2xpYnNpemUgPC0gcGxvdF9saWJzaXplKGxwX21hY3JvcGhhZ2UpCnBwKGZpbGU9ImltYWdlcy90bXJjMl9tYWNyb3BoYWdlX2xwX2xpYnNpemUucG5nIikKbWFjcm9waGFnZV9saWJzaXplJHBsb3QKZGV2Lm9mZigpCm1hY3JvcGhhZ2VfbGlic2l6ZSRwbG90CgpscF9tYWNyb3BoYWdlX25vcm0gPC0gbm9ybWFsaXplX2V4cHQobHBfbWFjcm9waGFnZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcj1UUlVFLCBub3JtPSJxdWFudCIsIHRyYW5zZm9ybT0ibG9nMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJ0PSJjcG0iKQpscF9tYWNyb3BoYWdlX3BjYSA8LSBwbG90X3BjYShscF9tYWNyb3BoYWdlX25vcm0sIHBsb3RfbGFiZWxzPUZBTFNFKQpwcChmaWxlPSJpbWFnZXMvYW1hc3RpZ290ZV96eW1vY29sX2luY2x1ZGVzYi5wbmciKQpscF9tYWNyb3BoYWdlX3BjYSRwbG90CmRldi5vZmYoKQpscF9tYWNyb3BoYWdlX3BjYSRwbG90CgpscF9tYWNyb3BoYWdlX25vc2IgPC0gc3Vic2V0X2V4cHQobHBfbWFjcm9waGFnZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnNldD0iY29uZGl0aW9uIT0naW5mX3NiX3oyLjMnIikKbHBfbWFjcm9waGFnZV9ub3NiX25vcm0gPC0gbm9ybWFsaXplX2V4cHQobHBfbWFjcm9waGFnZV9ub3NiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXI9VFJVRSwgbm9ybT0icXVhbnQiLCB0cmFuc2Zvcm09ImxvZzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJ0PSJjcG0iKQpscF9tYWNyb3BoYWdlX25vc2JfcGNhIDwtIHBsb3RfcGNhKGxwX21hY3JvcGhhZ2Vfbm9zYl9ub3JtLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfbGFiZWxzPUZBTFNFKQpwcChmaWxlPSJpbWFnZXMvYW1hc3RpZ290ZV96eW1vY29sX2V4Y2x1ZGVzYi5wbmciKQpscF9tYWNyb3BoYWdlX25vc2JfcGNhJHBsb3QKZGV2Lm9mZigpCmxwX21hY3JvcGhhZ2Vfbm9zYl9wY2EkcGxvdAoKbHBfbWFjcm9waGFnZV9kZSA8LSBhbGxfcGFpcndpc2UobHBfbWFjcm9waGFnZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfYmF0Y2g9InN2YXNlcSIsIGZpbHRlcj1UUlVFKQp0bXJjMl9wYXJhc2l0ZV9rZWVwZXJzIDwtIGxpc3QoCiAgICAiejIzbm9zYl92c196MjJub3NiIiA9IGMoImluZnoyMyIsICJpbmZ6MjIiKSkKbHBfbWFjcm9waGFnZV90YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcygKICBscF9tYWNyb3BoYWdlX2RlLCBrZWVwZXJzID0gdG1yYzJfcGFyYXNpdGVfa2VlcGVycywKICBleGNlbD1nbHVlOjpnbHVlKCJleGNlbC9tYWNyb3BoYWdlX3BhcmFzaXRlX2luZmVjdGlvbl9kZS12e3Zlcn0ueGxzeCIpKQpscF9tYWNyb3BoYWdlX3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogICAgbHBfbWFjcm9waGFnZV90YWJsZSwKICAgIGV4Y2VsPWdsdWU6OmdsdWUoImV4Y2VsL21hY3JvcGhhZ2VfcGFyYXNpdGVfc2lnLXZ7dmVyfS54bHN4IikpCmBgYAoKCkEgcmVjZW50IHN1Z2dlc3Rpb24gaW5jbHVkZWQgYSBxdWVyeSBhYm91dCB0aGUgcmVsYXRpb25zaGlwIG9mIG91cgphbWFzdGlnb3RlIFRNUkMyIHNhbXBsZXMgd2hpY2ggd2VyZSB0aGUgcmVzdWx0IG9mIGluZmVjdGluZyBhIHNldCBvZgptYWNyb3BoYWdlcyB2cy4gdGhlc2UgcHJvbWFzdGlnb3RlIHNhbXBsZXMuCgpTbyBmYXIsIHdlIGhhdmUga2VwdCB0aGVzZSB0d28gZXhwZXJpbWVudHMgc2VwYXJhdGUsIG5vdyBsZXQgdXMgbWVyZ2UgdGhlbS4KCmBgYHtyIGNvbWJpbmVfbWFjcm9waGFnZX0KdG1yYzJfbWFjcm9waGFnZV9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KGxwX21hY3JvcGhhZ2UsIHRyYW5zZm9ybT0ibG9nMiIsIGNvbnZlcnQ9ImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtPSJxdWFudCIsIGZpbHRlcj1UUlVFKQoKYWxsX3RtcmMyIDwtIGNvbWJpbmVfZXhwdHMobHBfZXhwdCwgbHBfbWFjcm9waGFnZSkKCmFsbF9ub3NiIDwtIGFsbF90bXJjMgpwRGF0YShhbGxfbm9zYilbWyJzdGFnZSJdXSA8LSAicHJvbWFzdGlnb3RlIgpuYV9pZHggPC0gaXMubmEocERhdGEoYWxsX25vc2IpW1sibWFjcm9waGFnZXRyZWF0bWVudCJdXSkKcERhdGEoYWxsX25vc2IpW25hX2lkeCwgIm1hY3JvcGhhZ2V0cmVhdG1lbnQiXSA8LSAidW5kZWZpbmVkIgphbGxfbm9zYiA8LSBzdWJzZXRfZXhwdChhbGxfbm9zYiwgc3Vic2V0PSJtYWNyb3BoYWdldHJlYXRtZW50IT0naW5mX3NiJyIpCmFtYV9pZHggPC0gcERhdGEoYWxsX25vc2IpW1sibWFjcm9waGFnZXRyZWF0bWVudCJdXSA9PSAiaW5mIgpwRGF0YShhbGxfbm9zYilbYW1hX2lkeCwgInN0YWdlIiBdIDwtICJhbWFzdGlnb3RlIgoKcERhdGEoYWxsX25vc2IpW1siYmF0Y2giXV0gPC0gcERhdGEoYWxsX25vc2IpW1sic3RhZ2UiXV0KYWxsX25vcm0gPC0gbm9ybWFsaXplX2V4cHQoYWxsX25vc2IsIGNvbnZlcnQ9ImNwbSIsIG5vcm09InF1YW50IiwgdHJhbnNmb3JtPSJsb2cyIiwgZmlsdGVyPVRSVUUpCnBsb3RfcGNhKGFsbF9ub3JtKSRwbG90CmBgYAoKSSB0aGluayB0aGUgYWJvdmUgcGljdHVyZSBpcyBzb3J0IG9mIHRoZSBvcHBvc2l0ZSBvZiB3aGF0IHdlIHdhbnQgdG8KY29tcGFyZSBpbiBhIERFIGFuYWx5c2lzIGZvciB0aGlzIHNldCBvZiBkYXRhLCBlLmcuIHdlIHdhbnQgdG8gY29tcGFyZQpwcm9tYXN0aWdvdGVzIGZyb20gYW1hc3RpZ290ZXM/CgpgYGB7ciBjb21wYXJlX3Byb19hbWF9CmFsbF9ub3NiIDwtIHNldF9leHB0X2JhdGNoZXMoYWxsX25vc2IsIGZhY3Q9ImNvbmRpdGlvbiIpICU+JQogIHNldF9leHB0X2NvbmRpdGlvbnMoZmFjdD0ic3RhZ2UiKQoKdHdvX3p5bW8gPC0gc3Vic2V0X2V4cHQoYWxsX25vc2IsIHN1YnNldD0ienltb2RlbWVjYXRlZ29yaWNhbD09J3oyMid8enltb2RlbWVjYXRlZ29yaWNhbD09J3oyMyd8enltb2RlbWVjYXRlZ29yaWNhbD09J3Vua25vd24nIikKCnByb19hbWEgPC0gYWxsX3BhaXJ3aXNlKGFsbF9ub3NiLCBmaWx0ZXI9VFJVRSwgbW9kZWxfYmF0Y2g9InN2YXNlcSIpCnByb19hbWFfdGFibGUgPC0gY29tYmluZV9kZV90YWJsZXMocHJvX2FtYSwgZXhjZWw9ImV4Y2VsL3RtcmMyX3Byb192c19hbWEueGxzeCIpCmBgYAoKIyBIdW1hbiBtYWNyb3BoYWdlIGNvbXBhcmlzb24KCmBgYHtyIG1hY3JvcGhhZ2VfaHVtYW5fZGV9Cm5ld19jb25kaXRpb25zIDwtIHBhc3RlMChwRGF0YShoc19tYWNyb3BoYWdlKVtbIm1hY3JvcGhhZ2V0cmVhdG1lbnQiXV0sICJfIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHBEYXRhKGhzX21hY3JvcGhhZ2UpW1sibWFjcm9waGFnZXp5bW9kZW1lIl1dKQpoc19tYWNyb3BoYWdlIDwtIHNldF9leHB0X2NvbmRpdGlvbnMoaHNfbWFjcm9waGFnZSwgZmFjdCA9IG5ld19jb25kaXRpb25zKQoKaHNfbWFjcm9waGFnZV9kZSA8LSBhbGxfcGFpcndpc2UoaHNfbWFjcm9waGFnZSwgbW9kZWxfYmF0Y2g9InN2YXNlcSIsIGZpbHRlcj1UUlVFKQoKdG1yYzJfaHVtYW5fa2VlcGVycyA8LSBsaXN0KAogICAgInoyM25vc2JfdnNfdW5pbmYiID0gYygiaW5mejIzIiwgInVuaW5mbm9uZSIpLAogICAgInoyMm5vc2JfdnNfdW5pbmYiID0gYygiaW5mejIyIiwgInVuaW5mbm9uZSIpLAogICAgInoyM25vc2JfdnNfejIybm9zYiIgPSBjKCJpbmZ6MjMiLCAiaW5mejIyIiksCiAgICAiejIzc2JfdnNfejIyc2IiID0gYygiaW5mc2J6MjMiLCAiaW5mc2J6MjIiKSwKICAgICJ6MjNzYl92c196MjNub3NiIiA9IGMoImluZnNiejIzIiwgImluZnoyMyIpLAogICAgInoyMnNiX3ZzX3oyMm5vc2IiID0gYygiaW5mc2J6MjIiLCAiaW5mejIyIiksCiAgICAiejIzc2JfdnNfc2IiID0gYygiaW5mejIzIiwgInVuaW5mc2Jub25lIiksCiAgICAiejIyc2JfdnNfc2IiID0gYygiaW5mejIyIiwgInVuaW5mc2Jub25lIiksCiAgICAic2JfdnNfdW5pbmYiID0gYygidW5pbmZzYm5vbmUiLCAidW5pbmZub25lIikpCmhzX21hY3JvcGhhZ2VfdGFibGUgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgICBoc19tYWNyb3BoYWdlX2RlLAogICAga2VlcGVycyA9IHRtcmMyX2h1bWFuX2tlZXBlcnMsCiAgICBleGNlbD1nbHVlOjpnbHVlKCJleGNlbC9tYWNyb3BoYWdlX2h1bWFuX3RhYmxlLXZ7dmVyfS54bHN4IikpCmhzX21hY3JvcGhhZ2Vfc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgICBoc19tYWNyb3BoYWdlX3RhYmxlLAogICAgZXhjZWw9Z2x1ZTo6Z2x1ZSgiZXhjZWwvbWFjcm9waGFnZV9odW1hbl9zaWctdnt2ZXJ9Lnhsc3giKSkKYGBgCgojIFNOUCBwcm9maWxlcwoKT3ZlciB0aGUgbGFzdCBjb3VwbGUgb2Ygd2Vla3MsIEkgcmVkaWQgYWxsIHRoZSB2YXJpYW50IHNlYXJjaGVzIHdpdGggYQpuZXdlciwgKEkgdGhpbmspIG1vcmUgc2Vuc2l0aXZlIGFuZCBtb3JlIHNwZWNpZmljIHZhcmlhbnQgdG9vbC4gIEluCmFkZGl0aW9uIEkgY2hhbmdlZCBteSBzY3JpcHQgd2hpY2ggaW50ZXJwcmV0cyB0aGUgcmVzdWx0cyBzbyB0aGF0IGl0CmlzIGFibGUgdG8gZXh0cmFjdCBhbnkgdGFncyBmcm9tIGl0LCBpbnN0ZWFkIG9mIGp1c3QgdGhlIG9uZSBvciB0d28KdGhhdCBteSBwcmV2aW91cyBzY3JpcHQgaGFuZGxlZC4gIEluIGFkZGl0aW9uLCBhdCBsZWFzdCBpbiB0aGVvcnkgaXQKaXMgbm93IGFibGUgdG8gcHJvdmlkZSB0aGUgc2V0IG9mIGFtaW5vIGFjaWQgc3Vic3RpdHV0aW9ucyBmb3IgZXZlcnkKZ2VuZSBpbiBzcGVjaWVzIHdpdGhvdXQgb3Igd2l0aCBpbnRyb25zIChub3QgcmVhbGx5IHJlbGV2YW50IGZvcgpMZWlzaG1hbmlhIHBhbmFtZW5zaXMpLgoKSG93ZXZlciwgYXMgb2YgdGhpcyB3cml0aW5nLCBJIGhhdmUgbm90IHJlLXBlcmZvcm1lZCB0aGUgc2FtZSB0YXNrcwp3aXRoIHRoZSAyMDE2IGRhdGEsIHByaW1hcmlseSBiZWNhdXNlIGl0IHdpbGwgcmVxdWlyZSByZW1hcHBpbmcgYWxsIG9mCnRoZSBzYW1wbGVzLiAgQXMgYSByZXN1bHQsIGZvciB0aGUgbW9tZW50IEkgY2Fubm90IGNvbWJpbmUgdGhlIG9sZGVyCmFuZCBuZXdlciBzYW1wbGVzLiAgVGh1cywgYW55IG9mIHRoZSBmb2xsb3dpbmcgYmxvY2tzIHdoaWNoIHVzZSB0aGUKMjAxNiBkYXRhIGFyZSBjdXJyZW50bHkgZGlzYWJsZWQuCgpgYGB7ciBvbGRuZXdfdmFyaWFudHMsIGV2YWw9VFJVRX0Kb2xkX2V4cHQgPC0gY3JlYXRlX2V4cHQoInNhbXBsZV9zaGVldHMvdG1yYzJfc2FtcGxlc18yMDE5MTIwMy54bHN4IiwKICAgICAgICAgICAgICAgICAgICAgICAgZmlsZV9jb2x1bW4gPSAidG9waGF0MmZpbGUiKQoKIyN0dCA8LSBscF9leHB0W1siZXhwcmVzc2lvbnNldCJdXQojI3Jvd25hbWVzKHR0KSA8LSBnc3ViKHBhdHRlcm4gPSAiXmV4b25fIiwgcmVwbGFjZW1lbnQgPSAiIiwgeCA9IHJvd25hbWVzKHR0KSkKIyNyb3duYW1lcyh0dCkgPC0gZ3N1YihwYXR0ZXJuID0gIlxcLkUxJCIsIHJlcGxhY2VtZW50ID0gIiIsIHggPSByb3duYW1lcyh0dCkpCiMjbHBfZXhwdCRleHByZXNzaW9uc2V0IDwtIHR0Cgp0dCA8LSBvbGRfZXhwdCRleHByZXNzaW9uc2V0CnJvd25hbWVzKHR0KSA8LSBnc3ViKHBhdHRlcm4gPSAiXmV4b25fIiwgcmVwbGFjZW1lbnQgPSAiIiwgeCA9IHJvd25hbWVzKHR0KSkKcm93bmFtZXModHQpIDwtIGdzdWIocGF0dGVybiA9ICJcXC4xJCIsIHJlcGxhY2VtZW50ID0gIiIsIHggPSByb3duYW1lcyh0dCkpCm9sZF9leHB0JGV4cHJlc3Npb25zZXQgPC0gdHQKcm0odHQpCmBgYAoKIyMgQ3JlYXRlIHRoZSBTTlAgZXhwcmVzc2lvbnNldAoKT25lIG90aGVyIGltcG9ydGFudCBjYXZlYXQsIHdlIGhhdmUgYSBncm91cCBvZiBuZXcgc2FtcGxlcyB3aGljaCBoYXZlCm5vdCB5ZXQgcnVuIHRocm91Z2ggdGhlIHZhcmlhbnQgc2VhcmNoIHBpcGVsaW5lLCBzbyBJIG5lZWQgdG8gcmVtb3ZlCnRoZW0gZnJvbSBjb25zaWRlcmF0aW9uLiAgVGhvdWdoIGl0IGxvb2tzIGxpa2UgdGhleSBmaW5pc2hlZCBvdmVybmlnaHQuLi4KCmBgYHtyIGNvdW50X2V4cHRfb2xkX25ld30KIyMgVGhlIG5leHQgbGluZSBkcm9wcyB0aGUgc2FtcGxlcyB3aGljaCBhcmUgbWlzc2luZyB0aGUgU05QIHBpcGVsaW5lLgpscF9zbnAgPC0gc3Vic2V0X2V4cHQobHBfZXhwdCwgc3Vic2V0PSIhaXMubmEocERhdGEobHBfZXhwdClbWydmcmVlYmF5ZXNzdW1tYXJ5J11dKSIpCm5ld19zbnBzIDwtIGNvdW50X2V4cHRfc25wcyhscF9zbnAsIGFubm90X2NvbHVtbiA9ICJmcmVlYmF5ZXNzdW1tYXJ5Iiwgc25wX2NvbHVtbj0iUEFJUkVEIikKb2xkX3NucHMgPC0gY291bnRfZXhwdF9zbnBzKG9sZF9leHB0LCBhbm5vdF9jb2x1bW4gPSAiYmNmdGFibGUiLCBzbnBfY29sdW1uID0gMikKCm5vbnplcm9fc25wcyA8LSBleHBycyhuZXdfc25wcykgIT0gMApjb2xTdW1zKG5vbnplcm9fc25wcykKYGBgCgpgYGB7ciBjb21iaW5lX29sZF9zbnBzLCBldmFsPVRSVUV9CiMjIE15IG9sZF9zbnBzIGlzIHVzaW5nIGFuIG9sZGVyIGFubm90YXRpb24gaW5jb3JyZWN0bHksIHNvIGZpeCBpdCBoZXJlOgpCaW9iYXNlOjphbm5vdGF0aW9uKG9sZF9zbnBzJGV4cHJlc3Npb25zZXQpIDwtIEJpb2Jhc2U6OmFubm90YXRpb24obmV3X3NucHMkZXhwcmVzc2lvbnNldCkKYm90aF9zbnBzIDwtIGNvbWJpbmVfZXhwdHMobmV3X3NucHMsIG9sZF9zbnBzKQpib3RoX25vcm0gPC0gbm9ybWFsaXplX2V4cHQoYm90aF9zbnBzLCB0cmFuc2Zvcm0gPSAibG9nMiIsIG5vcm0gPSAicXVhbnQiKQoKIyMgc3RyYWlucyA8LSBib3RoX25vcm1bWyJkZXNpZ24iXV1bWyJzdHJhaW4iXV0KYm90aF9zdHJhaW4gPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhib3RoX25vcm0sIGZhY3QgPSAic3RyYWluIikKYGBgCgpUaGUgZGF0YSBzdHJ1Y3R1cmUgJ2JvdGhfbm9ybScgbm93IGNvbnRhaW5zIG91ciAyMDE2IGRhdGEgYWxvbmcgd2l0aAp0aGUgbmV3ZXIgZGF0YSBjb2xsZWN0ZWQgc2luY2UgMjAxOS4KCiMjIFBsb3Qgb2YgU05QIHByb2ZpbGVzIGZvciB6eW1vZGVtZXMKClRoZSBmb2xsb3dpbmcgcGxvdCBzaG93cyB0aGUgU05QIHByb2ZpbGVzIG9mIGFsbCBzYW1wbGVzIChvbGQgYW5kIG5ldykgd2hlcmUgdGhlCmNvbG9ycyBhdCB0aGUgdG9wIHNob3cgZWl0aGVyIHRoZSAyLjIgc3RyYWlucyAob3JhbmdlKSwgMi4zIHN0cmFpbnMgKGdyZWVuKSwgdGhlCnByZXZpb3VzIHNhbXBsZXMgKHB1cnBsZSksIG9yIHRoZSB2YXJpb3VzIGxhYiBzdHJhaW5zIChwaW5rIGV0YykuCgpgYGB7ciBwbG90dGluZ192YXJpYW50c30KbmV3X3ZhcmlhbnRfaGVhdG1hcCA8LSBwbG90X2Rpc2hlYXQobmV3X3NucHMpCmRldiA8LSBwcChmaWxlID0gImltYWdlcy9yYXdfc25wX2Rpc2hlYXQucG5nIiwgaGVpZ2h0PTEyLCB3aWR0aD0xMikKbmV3X3ZhcmlhbnRfaGVhdG1hcCRwbG90CmNsb3NlZCA8LSBkZXYub2ZmKCkKbmV3X3ZhcmlhbnRfaGVhdG1hcCRwbG90CmBgYAoKVGhlIGZ1bmN0aW9uIGdldF9zbnBfc2V0cygpIHRha2VzIHRoZSBwcm92aWRlZCBtZXRhZGF0YSBmYWN0b3IgKGluCnRoaXMgY2FzZSAnY29uZGl0aW9uJykgYW5kIGxvb2tzIGZvciB2YXJpYW50cyB3aGljaCBhcmUgZXhjbHVzaXZlIHRvCmVhY2ggZWxlbWVudCBpbiBpdC4gIEluIHRoaXMgY2FzZSwgdGhpcyBpcyBsb29raW5nIGZvciBkaWZmZXJlbmNlcwpiZXR3ZWVuIDIuMiBhbmQgMi4zLCBhcyB3ZWxsIGFzIHRoZSBzZXQgc2hhcmVkIGFtb25nIHRoZW0uCgpgYGB7ciBnZXRfc25wX3NldHMxfQpzbnBfc2V0cyA8LSBnZXRfc25wX3NldHMoYm90aF9zbnBzLCBmYWN0b3IgPSAiY29uZGl0aW9uIikKQmlvYmFzZTo6YW5ub3RhdGlvbihvbGRfZXhwdCRleHByZXNzaW9uc2V0KSA9IEJpb2Jhc2U6OmFubm90YXRpb24obHBfZXhwdCRleHByZXNzaW9uc2V0KQpib3RoX2V4cHQgPC0gY29tYmluZV9leHB0cyhscF9leHB0LCBvbGRfZXhwdCkKCnNucF9nZW5lcyA8LSBzbShzbnBzX3ZzX2dlbmVzKGJvdGhfZXhwdCwgc25wX3NldHMsIGV4cHRfbmFtZV9jb2wgPSAiY2hyb21vc29tZSIpKQojIyBJIHRoaW5rIHdlIGhhdmUgc29tZSBtZXRyaWNzIGhlcmUgd2UgY2FuIHBsb3QuLi4Kc25wX3N1YnNldCA8LSBzbnBfc3Vic2V0X2dlbmVzKAogIGJvdGhfZXhwdCwgYm90aF9zbnBzLAogIGdlbmVzID0gYygiTFBBTDEzXzEyMDAxMDkwMCIsICJMUEFMMTNfMzQwMDEzMDAwIiwgIkxQQUwxM18wMDAwNTQxMDAiLAogICAgICAgICAgICAiTFBBTDEzXzE0MDAwNjEwMCIsICJMUEFMMTNfMTgwMDE4NTAwIiwgIkxQQUwxM18zMjAwMjIzMDAiKSkKenltb19oZWF0IDwtIHBsb3Rfc2FtcGxlX2hlYXRtYXAoc25wX3N1YnNldCwgcm93X2xhYmVsID0gcm93bmFtZXMoZXhwcnMoc25wX3N1YnNldCkpKQp6eW1vX2hlYXQKYGBgCgojIyBDb21wYXJlIHZhcmlhbnRzIHRvIERFIGdlbmVzCgpOYWppYiBoYXMgYXNrZWQgYSBmZXcgdGltZXMgYWJvdXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHZhcmlhbnRzCmFuZCBERSBnZW5lcy4gIEluIHN1YnNlcXVlbnQgY29udmVyc2F0aW9ucyBJIGZpZ3VyZWQgb3V0IHdoYXQgaGUKcmVhbGx5IHdhbnRzIHRvIGxlYXJuIGlzIHZhcmlhbnRzIGluIHRoZSBVVFIgKG1vc3QgbGlrZWx5IDUnKSB3aGljaAptaWdodCBhZmZlY3QgZXhwcmVzc2lvbiBvZiBnZW5lcy4gIFRoZSBmb2xsb3dpbmcgZXhwbGljaXRseSBkb2VzIG5vdApoZWxwIHRoaXMgcXVlc3Rpb24sIGJ1dCBpcyBhIHBhcmFsb2c6IGlzIHRoZXJlIGEgcmVsYXRpb25zaGlwIGJldHdlZW4KdmFyaWFudHMgaW4gdGhlIENEUyBhbmQgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24/CgpgYGB7ciB2YXJpYW50c192c19kZX0KdmFyc19kZiA8LSBkYXRhLmZyYW1lKElEID0gbmFtZXMoc25wX2dlbmVzJHN1bW1hcnlfYnlfZ2VuZSksIHZhcmlhbnRzID0gYXMubnVtZXJpYyhzbnBfZ2VuZXMkc3VtbWFyeV9ieV9nZW5lKSkKdmFyc19kZltbInZhcmlhbnRzIl1dIDwtIGxvZzIodmFyc19kZltbInZhcmlhbnRzIl1dICsgMSkKdmFyc19ieV9kZV9nZW5lIDwtIG1lcmdlKHp5X2RmLCB2YXJzX2RmLCBieS54PSJyb3cubmFtZXMiLCBieS55PSJJRCIpCmNvci50ZXN0KHZhcnNfYnlfZGVfZ2VuZSRkZXNlcV9sb2dmYywgdmFyc19ieV9kZV9nZW5lJHZhcmlhbnRzKQp2YXJpYW50c193cnRfbG9nZmMgPC0gcGxvdF9saW5lYXJfc2NhdHRlcih2YXJzX2J5X2RlX2dlbmVbLCBjKCJkZXNlcV9sb2dmYyIsICJ2YXJpYW50cyIpXSkKdmFyaWFudHNfd3J0X2xvZ2ZjJHNjYXR0ZXIKIyMgSXQgbG9va3MgbGlrZSB0aGVyZSBtaWdodCBiZSBzb21lIGdlbmVzIG9mIGludGVyZXN0LCBldmVuIHRob3VnaCB0aGlzIGlzIG5vdCBhY3R1YWxseQojIyB0aGUgcXVlc3Rpb24gb2YgaW50ZXJlc3QuCmBgYAoKRGlkbid0IEkgY3JlYXRlIGEgc2V0IG9mIGRlbnNpdGllcyBieSBjaHJvbW9zb21lPwpPaCBJIHRoaW5rIHRoZXkgY29tZSBpbiBmcm9tIGdldF9zbnBfc2V0cygpCgojIyBTTlBTIGFzc29jaWF0ZWQgd2l0aCBjbGluaWNhbCByZXNwb25zZSBpbiB0aGUgVE1SQyBzYW1wbGVzCgpgYGB7ciBzbnBfY2xpbmljYWx9CmNsaW5pY2FsX3NldHMgPC0gZ2V0X3NucF9zZXRzKG5ld19zbnBzLCBmYWN0b3IgPSAiY2xpbmljYWxyZXNwb25zZSIpCgpkZW5zaXR5X3ZlYyA8LSBjbGluaWNhbF9zZXRzW1siZGVuc2l0eSJdXQpjaHJvbW9zb21lX2lkeCA8LSBncmVwKHBhdHRlcm4gPSAiTHBhTCIsIHggPSBuYW1lcyhkZW5zaXR5X3ZlYykpCmRlbnNpdHlfZGYgPC0gYXMuZGF0YS5mcmFtZShkZW5zaXR5X3ZlY1tjaHJvbW9zb21lX2lkeF0pCmRlbnNpdHlfZGZbWyJjaHIiXV0gPC0gcm93bmFtZXMoZGVuc2l0eV9kZikKY29sbmFtZXMoZGVuc2l0eV9kZikgPC0gYygiZGVuc2l0eV92ZWMiLCAiY2hyIikKZ2dwbG90KGRlbnNpdHlfZGYsIGFlc19zdHJpbmcoeCA9ICJjaHIiLCB5ID0gImRlbnNpdHlfdmVjIikpICsKICBnZ3Bsb3QyOjpnZW9tX2NvbCgpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRleHQgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvdXIgPSAiYmxhY2siKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSkpCiMjIGNsaW5pY2FsX3dyaXR0ZW4gPC0gd3JpdGVfdmFyaWFudHMobmV3X3NucHMpCmBgYAoKIyMjIENyb3NzIHJlZmVyZW5jZSB0aGVzZSB2YXJpYW50cyBieSBnZW5lCgpgYGB7ciBzbnBfY2xhc3NpZmljYXRpb25zfQpjbGluaWNhbF9nZW5lcyA8LSBzbnBzX3ZzX2dlbmVzKGxwX2V4cHQsIGNsaW5pY2FsX3NldHMsIGV4cHRfbmFtZV9jb2wgPSAiY2hyb21vc29tZSIpCgpzbnBfZGVuc2l0eSA8LSBtZXJnZShhcy5kYXRhLmZyYW1lKGNsaW5pY2FsX2dlbmVzW1sic3VtbWFyeV9ieV9nZW5lIl1dKSwKICAgICAgICAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZShmRGF0YShscF9leHB0KSksCiAgICAgICAgICAgICAgICAgICAgIGJ5ID0gInJvdy5uYW1lcyIpCnNucF9kZW5zaXR5IDwtIHNucF9kZW5zaXR5WywgYygxLCAyLCA0LCAxNSldCmNvbG5hbWVzKHNucF9kZW5zaXR5KSA8LSBjKCJuYW1lIiwgInNucHMiLCAicHJvZHVjdCIsICJsZW5ndGgiKQpzbnBfZGVuc2l0eVtbInByb2R1Y3QiXV0gPC0gdG9sb3dlcihzbnBfZGVuc2l0eVtbInByb2R1Y3QiXV0pCnNucF9kZW5zaXR5W1sibGVuZ3RoIl1dIDwtIGFzLm51bWVyaWMoc25wX2RlbnNpdHlbWyJsZW5ndGgiXV0pCnNucF9kZW5zaXR5W1siZGVuc2l0eSJdXSA8LSBzbnBfZGVuc2l0eVtbInNucHMiXV0gLyBzbnBfZGVuc2l0eVtbImxlbmd0aCJdXQpzbnBfaWR4IDwtIG9yZGVyKHNucF9kZW5zaXR5W1siZGVuc2l0eSJdXSwgZGVjcmVhc2luZyA9IFRSVUUpCnNucF9kZW5zaXR5IDwtIHNucF9kZW5zaXR5W3NucF9pZHgsIF0KCnJlbW92ZXJzIDwtIGMoImFtYXN0aW4iLCAiZ3A2MyIsICJsZWlzaG1hbm9seXNpbiIpCmZvciAociBpbiByZW1vdmVycykgewogIGRyb3BfaWR4IDwtIGdyZXBsKHBhdHRlcm4gPSByLCB4ID0gc25wX2RlbnNpdHlbWyJwcm9kdWN0Il1dKQogIHNucF9kZW5zaXR5IDwtIHNucF9kZW5zaXR5WyFkcm9wX2lkeCwgXQp9CiMjIEZpbHRlciB0aGVzZSBmb3IgW0F8YV1tYXN0aW4gZ3A2MyBMZWlzaG1hbm9seXNpbgpgYGAKCgpgYGB7ciBzbnBfaW50ZXJzZWN0aW9uc30KY2xpbmljYWxfc25wcyA8LSBzbnBzX2ludGVyc2VjdGlvbnMobHBfZXhwdCwgY2xpbmljYWxfc2V0cywgY2hyX2NvbHVtbiA9ICJjaHJvbW9zb21lIikKCmZhaWxfcmVmX3NucHMgPC0gYXMuZGF0YS5mcmFtZShjbGluaWNhbF9zbnBzW1siaW50ZXJzIl1dW1siZmFpbHVyZSwgcmVmZXJlbmNlIHN0cmFpbiJdXSkKZmFpbF9yZWZfc25wcyA8LSByYmluZChmYWlsX3JlZl9zbnBzLAogICAgICAgICAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUoY2xpbmljYWxfc25wc1tbImludGVycyJdXVtbImZhaWx1cmUiXV0pKQpjdXJlX3NucHMgPC0gYXMuZGF0YS5mcmFtZShjbGluaWNhbF9zbnBzW1siaW50ZXJzIl1dW1siY3VyZSJdXSkKCmhlYWQoZmFpbF9yZWZfc25wcykKaGVhZChjdXJlX3NucHMpCndyaXRlLmNzdihmaWxlPSJjc3YvY3VyZV92YXJpYW50cy50eHQiLCB4PXJvd25hbWVzKGN1cmVfc25wcykpCndyaXRlLmNzdihmaWxlPSJjc3YvZmFpbF92YXJpYW50cy50eHQiLCB4PXJvd25hbWVzKGZhaWxfcmVmX3NucHMpKQoKYW5ub3QgPC0gZkRhdGEobHBfZXhwdCkKY2xpbmljYWxfaW50ZXJlc3QgPC0gYXMuZGF0YS5mcmFtZShjbGluaWNhbF9zbnBzW1siZ2VuZV9zdW1tYXJpZXMiXV1bWyJjdXJlIl1dKQpjbGluaWNhbF9pbnRlcmVzdCA8LSBtZXJnZShjbGluaWNhbF9pbnRlcmVzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZShjbGluaWNhbF9zbnBzW1siZ2VuZV9zdW1tYXJpZXMiXV1bWyJmYWlsdXJlLCByZWZlcmVuY2Ugc3RyYWluIl1dKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAicm93Lm5hbWVzIikKcm93bmFtZXMoY2xpbmljYWxfaW50ZXJlc3QpIDwtIGNsaW5pY2FsX2ludGVyZXN0W1siUm93Lm5hbWVzIl1dCmNsaW5pY2FsX2ludGVyZXN0W1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKY29sbmFtZXMoY2xpbmljYWxfaW50ZXJlc3QpIDwtIGMoImN1cmVfc25wcyIsImZhaWxfc25wcyIpCmFubm90IDwtIG1lcmdlKGFubm90LCBjbGluaWNhbF9pbnRlcmVzdCwgYnkgPSAicm93Lm5hbWVzIikKcm93bmFtZXMoYW5ub3QpIDwtIGFubm90W1siUm93Lm5hbWVzIl1dCmFubm90W1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKZkRhdGEobHBfZXhwdCRleHByZXNzaW9uc2V0KSA8LSBhbm5vdApgYGAKCiMgWnltb2RlbWUgZm9yIG5ldyBzYW1wbGVzCgpUaGUgaGVhdG1hcCBwcm9kdWNlZCBoZXJlIHNob3VsZCBzaG93IHRoZSB2YXJpYW50cyBvbmx5IGZvciB0aGUgenltb2RlbWUgZ2VuZXMuCgojIyBIdW50IGZvciBzbnAgY2x1c3RlcnMKCkkgYW0gdGhpbmtpbmcgdGhhdCBpZiB3ZSBmaW5kIGNsdXN0ZXJzIG9mIGxvY2F0aW9ucyB3aGljaCBhcmUgdmFyaWFudCwgdGhhdAptaWdodCBwcm92aWRlIHNvbWUgUENSIHRlc3RpbmcgcG9zc2liaWxpdGllcy4KCmBgYHtyIG5ld196eW1vfQojIyBEcm9wIHRoZSAyLjEsIDIuNCwgdW5rbm93biwgYW5kIG51bGwKcHJ1bmVkX3NucHMgPC0gc3Vic2V0X2V4cHQobmV3X3NucHMsIHN1YnNldD0iY29uZGl0aW9uPT0nejIuMid8Y29uZGl0aW9uPT0nejIuMyciKQpuZXdfc2V0cyA8LSBnZXRfc25wX3NldHMocHJ1bmVkX3NucHMsIGZhY3RvciA9ICJ6eW1vZGVtZWNhdGVnb3JpY2FsIikKc3VtbWFyeShuZXdfc2V0cykKIyMgMTAwMDAwMDogMi4yCiMjIDAxMDAwMDA6IDIuMwoKc3VtbWFyeShuZXdfc2V0c1tbImludGVyc2VjdGlvbnMiXV1bWyIxMCJdXSkKd3JpdGUuY3N2KGZpbGU9ImNzdi92YXJpYW50c18yMi5jc3YiLCB4PW5ld19zZXRzW1siaW50ZXJzZWN0aW9ucyJdXVtbIjEwIl1dKQpzdW1tYXJ5KG5ld19zZXRzW1siaW50ZXJzZWN0aW9ucyJdXVtbIjAxIl1dKQp3cml0ZS5jc3YoZmlsZT0iY3N2L3ZhcmlhbnRzXzIzLmNzdiIsIHg9bmV3X3NldHNbWyJpbnRlcnNlY3Rpb25zIl1dW1siMDEiXV0pCmBgYAoKVGh1cyB3ZSBzZWUgdGhhdCB0aGVyZSBhcmUgMyw1NTMgdmFyaWFudHMgYXNzb2NpYXRlZCB3aXRoIDIuMiBhbmQgODEsNTg5IGFzc29jaWF0ZWQgd2l0aCAyLjMuCgojIyMgQSBzbWFsbCBmdW5jdGlvbiBmb3Igc2VhcmNoaW5nIGZvciBwb3RlbnRpYWwgUENSIHByaW1lcnMKClRoZSBmb2xsb3dpbmcgZnVuY3Rpb24gdXNlcyB0aGUgcG9zaXRpb25hbCBkYXRhIHRvIGxvb2sgZm9yIHNlcXVlbnRpYWwKbWlzbWF0Y2hlcyBhc3NvY2lhdGVkIHdpdGggenltb2RlbWUgaW4gdGhlIGhvcGVzIHRoYXQgdGhlcmUgd2lsbCBiZQpzb21lIHJlZ2lvbnMgd2hpY2ggd291bGQgcHJvdmlkZSBnb29kIHBvdGVudGlhbCB0YXJnZXRzIGZvciBhClBDUi1iYXNlZCBhc3NheS4KCmBgYHtyIHNlcXVlbnRpYWxfc2VhcmNoLCBldmFsPUZBTFNFfQpzZXF1ZW50aWFsX3ZhcmlhbnRzIDwtIGZ1bmN0aW9uKHNucF9zZXRzLCBjb25kaXRpb25zID0gTlVMTCwgbWluaW11bSA9IDMsIG1heGltdW1fc2VwYXJhdGlvbiA9IDMpIHsKICBpZiAoaXMubnVsbChjb25kaXRpb25zKSkgewogICAgY29uZGl0aW9ucyA8LSAxCiAgfQogIGludGVyc2VjdGlvbl9zZXRzIDwtIHNucF9zZXRzW1siaW50ZXJzZWN0aW9ucyJdXQogIGludGVyc2VjdGlvbl9uYW1lcyA8LSBzbnBfc2V0c1tbInNldF9uYW1lcyJdXQogIGNob3Nlbl9pbnRlcnNlY3Rpb24gPC0gMQogIGlmIChpcy5udW1lcmljKGNvbmRpdGlvbnMpKSB7CiAgICBjaG9zZW5faW50ZXJzZWN0aW9uIDwtIGNvbmRpdGlvbnMKICB9IGVsc2UgewogICAgaW50ZXJzZWN0aW9uX2lkeCA8LSBpbnRlcnNlY3Rpb25fbmFtZXMgPT0gY29uZGl0aW9ucwogICAgY2hvc2VuX2ludGVyc2VjdGlvbiA8LSBuYW1lcyhpbnRlcnNlY3Rpb25fbmFtZXMpW2ludGVyc2VjdGlvbl9pZHhdCiAgfQoKICBwb3NzaWJsZV9wb3NpdGlvbnMgPC0gaW50ZXJzZWN0aW9uX3NldHNbW2Nob3Nlbl9pbnRlcnNlY3Rpb25dXQogIHBvc2l0aW9uX3RhYmxlIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzID0gcG9zc2libGVfcG9zaXRpb25zKQogIHBhdCA8LSAiXmNocl8oLispX3Bvc18oLispX3JlZl8uKiQiCiAgcG9zaXRpb25fdGFibGVbWyJjaHIiXV0gPC0gZ3N1YihwYXR0ZXJuID0gcGF0LCByZXBsYWNlbWVudCA9ICJcXDEiLCB4ID0gcm93bmFtZXMocG9zaXRpb25fdGFibGUpKQogIHBvc2l0aW9uX3RhYmxlW1sicG9zIl1dIDwtIGFzLm51bWVyaWMoZ3N1YihwYXR0ZXJuID0gcGF0LCByZXBsYWNlbWVudCA9ICJcXDIiLCB4ID0gcm93bmFtZXMocG9zaXRpb25fdGFibGUpKSkKICBwb3NpdGlvbl9pZHggPC0gb3JkZXIocG9zaXRpb25fdGFibGVbLCAiY2hyIl0sIHBvc2l0aW9uX3RhYmxlWywgInBvcyJdKQogIHBvc2l0aW9uX3RhYmxlIDwtIHBvc2l0aW9uX3RhYmxlW3Bvc2l0aW9uX2lkeCwgXQogIHBvc2l0aW9uX3RhYmxlW1siZGlzdCJdXSA8LSAwCgogIGxhc3RfY2hyIDwtICIiCiAgZm9yIChyIGluIDE6bnJvdyhwb3NpdGlvbl90YWJsZSkpIHsKICAgIHRoaXNfY2hyIDwtIHBvc2l0aW9uX3RhYmxlW3IsICJjaHIiXQogICAgaWYgKHIgPT0gMSkgewogICAgICBwb3NpdGlvbl90YWJsZVtyLCAiZGlzdCJdIDwtIHBvc2l0aW9uX3RhYmxlW3IsICJwb3MiXQogICAgICBsYXN0X2NociA8LSB0aGlzX2NocgogICAgICBuZXh0CiAgICB9CiAgICBpZiAodGhpc19jaHIgPT0gbGFzdF9jaHIpIHsKICAgICAgcG9zaXRpb25fdGFibGVbciwgImRpc3QiXSA8LSBwb3NpdGlvbl90YWJsZVtyLCAicG9zIl0gLSBwb3NpdGlvbl90YWJsZVtyIC0gMSwgInBvcyJdCiAgICB9IGVsc2UgewogICAgICBwb3NpdGlvbl90YWJsZVtyLCAiZGlzdCJdIDwtIHBvc2l0aW9uX3RhYmxlW3IsICJwb3MiXQogICAgfQogICAgbGFzdF9jaHIgPC0gdGhpc19jaHIKICB9CgogICMjIFdvcmtpbmcgaW50ZXJhY3RpdmVseSBoZXJlLgoKICBkb3VibGVzIDwtIHBvc2l0aW9uX3RhYmxlW1siZGlzdCJdXSA9PSAxCiAgZG91YmxlcyA8LSBwb3NpdGlvbl90YWJsZVtkb3VibGVzLCBdCiAgd3JpdGUuY3N2KGRvdWJsZXMsICJkb3VibGVzLmNzdiIpCgogIG9uZV9hd2F5IDwtIHBvc2l0aW9uX3RhYmxlW1siZGlzdCJdXSA9PSAyCiAgb25lX2F3YXkgPC0gcG9zaXRpb25fdGFibGVbb25lX2F3YXksIF0KICB3cml0ZS5jc3Yob25lX2F3YXksICJvbmVfYXdheS5jc3YiKQoKICB0d29fYXdheSA8LSBwb3NpdGlvbl90YWJsZVtbImRpc3QiXV0gPT0gMwogIHR3b19hd2F5IDwtIHBvc2l0aW9uX3RhYmxlW3R3b19hd2F5LCBdCiAgd3JpdGUuY3N2KHR3b19hd2F5LCAidHdvX2F3YXkuY3N2IikKCiAgY29tYmluZWQgPC0gcmJpbmQoZG91Ymxlcywgb25lX2F3YXkpCiAgY29tYmluZWQgPC0gcmJpbmQoY29tYmluZWQsIHR3b19hd2F5KQogIHBvc2l0aW9uX2lkeCA8LSBvcmRlcihjb21iaW5lZFssICJjaHIiXSwgY29tYmluZWRbLCAicG9zIl0pCiAgY29tYmluZWQgPC0gY29tYmluZWRbcG9zaXRpb25faWR4LCBdCgogIHRoaXNfY2hyIDwtICIiCiAgZm9yIChyIGluIDE6bnJvdyhjb21iaW5lZCkpIHsKICAgIHRoaXNfY2hyIDwtIGNvbWJpbmVkW3IsICJjaHIiXQogICAgaWYgKHIgPT0gMSkgewogICAgICBjb21iaW5lZFtyLCAiZGlzdF9wYWlyIl0gPC0gY29tYmluZWRbciwgInBvcyJdCiAgICAgIGxhc3RfY2hyIDwtIHRoaXNfY2hyCiAgICAgIG5leHQKICAgIH0KICAgIGlmICh0aGlzX2NociA9PSBsYXN0X2NocikgewogICAgICBjb21iaW5lZFtyLCAiZGlzdF9wYWlyIl0gPC0gY29tYmluZWRbciwgInBvcyJdIC0gY29tYmluZWRbciAtIDEsICJwb3MiXQogICAgfSBlbHNlIHsKICAgICAgY29tYmluZWRbciwgImRpc3RfcGFpciJdIDwtIGNvbWJpbmVkW3IsICJwb3MiXQogICAgfQogICAgbGFzdF9jaHIgPC0gdGhpc19jaHIKICB9CgogIGRpc3RfcGFpcl9tYXhpbXVtIDwtIDEwMDAKICBkaXN0X3BhaXJfbWluaW11bSA8LSAyMDAKICBkaXN0X3BhaXJfaWR4IDwtIGNvbWJpbmVkW1siZGlzdF9wYWlyIl1dIDw9IGRpc3RfcGFpcl9tYXhpbXVtICYKICAgIGNvbWJpbmVkW1siZGlzdF9wYWlyIl1dID49IGRpc3RfcGFpcl9taW5pbXVtCiAgcmVtYWluaW5nIDwtIGNvbWJpbmVkW2Rpc3RfcGFpcl9pZHgsIF0KICBub193ZWFrX2lkeCA8LSBncmVwbChwYXR0ZXJuPSJyZWZfKEd8QykiLCB4PXJvd25hbWVzKHJlbWFpbmluZykpCiAgcmVtYWluaW5nIDwtIHJlbWFpbmluZ1tub193ZWFrX2lkeCwgXQoKICBwcmludChoZWFkKHRhYmxlKHBvc2l0aW9uX3RhYmxlW1siZGlzdCJdXSkpKQogIHNlcXVlbnRpYWxzIDwtIHBvc2l0aW9uX3RhYmxlW1siZGlzdCJdXSA8PSBtYXhpbXVtX3NlcGFyYXRpb24KICBtZXNzYWdlKCJUaGVyZSBhcmUgIiwgc3VtKHNlcXVlbnRpYWxzKSwgIiBjYW5kaWRhdGUgcmVnaW9ucy4iKQoKICAjIyBUaGUgZm9sbG93aW5nIGNhbiB0ZWxsIG1lIGhvdyBtYW55IHJ1bnMgb2YgZWFjaCBsZW5ndGggb2NjdXJyZWQsIHRoYXQgaXMgbm90IHF1aXRlIHdoYXQgSSB3YW50LgogICMjIE5vdyB1c2UgcnVuIGxlbmd0aCBlbmNvZGluZyB0byBmaW5kIHRoZSBzZXQgb2Ygc2VxdWVudGlhbCBzZXF1ZW50aWFscyEKICBybGVfcmVzdWx0IDwtIHJsZShzZXF1ZW50aWFscykKICBybGVfdmFsdWVzIDwtIHJsZV9yZXN1bHRbWyJ2YWx1ZXMiXV0KICAjIyBUaGUgZm9sbG93aW5nIGxpbmUgaXMgZXF1aXZhbGVudCB0byBqdXN0IGxlYXZpbmcgdmFsdWVzIGFsb25lOgogICMjIHRydWVfdmFsdWVzIDwtIHJsZV9yZXN1bHRbWyJ2YWx1ZXMiXV0gPT0gVFJVRQogIHJsZV9sZW5ndGhzIDwtIHJsZV9yZXN1bHRbWyJsZW5ndGhzIl1dCiAgdHJ1ZV9zZXF1ZW50aWFscyA8LSBybGVfbGVuZ3Roc1tybGVfdmFsdWVzXQogIHJsZV9pZHggPC0gY3Vtc3VtKHJsZV9sZW5ndGhzKVt3aGljaChybGVfdmFsdWVzKV0KCiAgcG9zaXRpb25fdGFibGVbWyJsYXN0X3NlcXVlbnRpYWwiXV0gPC0gMAogIGNvdW50IDwtIDAKICBmb3IgKHIgaW4gcmxlX2lkeCkgewogICAgY291bnQgPC0gY291bnQgKyAxCiAgICBwb3NpdGlvbl90YWJsZVtyLCAibGFzdF9zZXF1ZW50aWFsIl0gPC0gdHJ1ZV9zZXF1ZW50aWFsc1tjb3VudF0KICB9CiAgbWVzc2FnZSgiVGhlIG1heGltdW0gc2VxdWVudGlhbCBzZXQgaXM6ICIsIG1heChwb3NpdGlvbl90YWJsZVtbImxhc3Rfc2VxdWVudGlhbCJdXSksICIuIikKCiAgd2FudGVkX2lkeCA8LSBwb3NpdGlvbl90YWJsZVtbImxhc3Rfc2VxdWVudGlhbCJdXSA+PSBtaW5pbXVtCiAgd2FudGVkIDwtIHBvc2l0aW9uX3RhYmxlW3dhbnRlZF9pZHgsIGMoImNociIsICJwb3MiKV0KICByZXR1cm4od2FudGVkKQp9Cgp6eW1vMjJfc2VxdWVudGlhbHMgPC0gc2VxdWVudGlhbF92YXJpYW50cyhuZXdfc2V0cywgY29uZGl0aW9ucyA9ICJ6MjIiLCBtaW5pbXVtPTEsIG1heGltdW1fc2VwYXJhdGlvbj0yKQpkaW0oenltbzIyX3NlcXVlbnRpYWxzKQojIyA3IGNhbmRpZGF0ZSByZWdpb25zIGZvciB6eW1vZGVtZSAyLjIgLS0gdGh1cyBJIGFtIGJldHRpbmcgdGhhdCB0aGUgcmVmZXJlbmNlIHN0cmFpbiBpcyBhIDIuMgp6eW1vMjNfc2VxdWVudGlhbHMgPC0gc2VxdWVudGlhbF92YXJpYW50cyhuZXdfc2V0cywgY29uZGl0aW9ucyA9ICJ6MjMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5pbXVtID0gMiwgbWF4aW11bV9zZXBhcmF0aW9uID0gMikKZGltKHp5bW8yM19zZXF1ZW50aWFscykKIyMgSW4gY29udHJhc3QsIHRoZXJlIGFyZSBsb3RzICg1ODcpIG9mIGludGVyZXN0aW5nIHJlZ2lvbnMgZm9yIDIuMyEKYGBgCgojIyMgRXh0cmFjdCBhIHByb21pc2luZyByZWdpb24gZnJvbSB0aGUgZ2Vub21lCgpUaGUgZmlyc3QgNCBjYW5kaWRhdGUgcmVnaW9ucyBmcm9tIG15IHNldCBvZiByZW1haW5pbmc6CiogQ2hyICAgICAgIFBvcy4gICBEaXN0YW5jZQoqIExwYUwxMy0xNSAyMzg0MzMgNDQ4CiogTHBhTDEzLTE4IDE0Mjg0NCA2MTMKKiBMcGFMMTMtMjkgODMwMzQyIDI1MgoqIExwYUwxMy0zMyAxMzMxNTA3IDg0MwoKTGV0cyBkZWZpbmUgYSBjb3VwbGUgb2YgdGVybXM6CiogVGhpcmQ6IEVhY2ggb2YgdGhlIDQgYWJvdmUgcG9zaXRpb25zLgoqIFNlY29uZDogVGhpcmQgLSBEaXN0YW5jZQoqIEVuZDogVGhpcmQgKyBQcmltZXJMZW4KKiBTdGFydDogU2Vjb25kIC0gUHJpbWVybGVuCgpJbiBlYWNoIGluc3RhbmNlLCB0aGVzZSBhcmUgdGhlIGxhc3QgcG9zaXRpb25zLCBzbyB3ZSB3YW50IHRvIGdyYWIgdGhyZWUgdGhpbmdzOgoKKiBUaGUgZW50aXJlIHJlZ2lvbiBmcm9tIEVuZCAtPiBTdGFydCwgdGhpcyB3YXkgd2UgY2FuIGhhdmUgYSBxdWljayBzYW5pdHkgY2hlY2suCiogU3RhcnQgLT4gU2Vjb25kLgoqIChUaGlyZCAtPiBFbmQpIDwtIFJldmVyc2UgY29tcGxlbWVudGVkCgpgYGB7ciBleHRyYWN0X2JzZ2Vub21lLCBldmFsPUZBTFNFfQojIyAqIExwYUwxMy0xNSAyMzg0MzMgNDQ4CmZpcnN0X2NhbmRpZGF0ZV9jaHIgPC0gZ2Vub21lW1siTHBhTDEzXzE1Il1dCnByaW1lcl9sZW5ndGggPC0gMjIKYW1wbGljb25fbGVuZ3RoIDwtIDQ0OApmaXJzdF9jYW5kaWRhdGVfdGhpcmQgPC0gMjM4NDMzCmZpcnN0X2NhbmRpZGF0ZV9zZWNvbmQgPC0gZmlyc3RfY2FuZGlkYXRlX3RoaXJkIC0gYW1wbGljb25fbGVuZ3RoCmZpcnN0X2NhbmRpZGF0ZV9zdGFydCA8LSBmaXJzdF9jYW5kaWRhdGVfc2Vjb25kIC0gcHJpbWVyX2xlbmd0aApmaXJzdF9jYW5kaWRhdGVfZW5kIDwtIGZpcnN0X2NhbmRpZGF0ZV90aGlyZCArIHByaW1lcl9sZW5ndGgKZmlyc3RfY2FuZGlkYXRlX3JlZ2lvbiA8LSBzdWJzZXEoZmlyc3RfY2FuZGlkYXRlX2NociwgZmlyc3RfY2FuZGlkYXRlX3N0YXJ0LCBmaXJzdF9jYW5kaWRhdGVfZW5kKQpmaXJzdF9jYW5kaWRhdGVfcmVnaW9uCmZpcnN0X2NhbmRpZGF0ZV81cCA8LSBzdWJzZXEoZmlyc3RfY2FuZGlkYXRlX2NociwgZmlyc3RfY2FuZGlkYXRlX3N0YXJ0LCBmaXJzdF9jYW5kaWRhdGVfc2Vjb25kKQphcy5jaGFyYWN0ZXIoZmlyc3RfY2FuZGlkYXRlXzVwKQpmaXJzdF9jYW5kaWRhdGVfM3AgPC0gc3Bnczo6cmV2ZXJzZUNvbXBsZW1lbnQoc3Vic2VxKGZpcnN0X2NhbmRpZGF0ZV9jaHIsIGZpcnN0X2NhbmRpZGF0ZV90aGlyZCwgZmlyc3RfY2FuZGlkYXRlX2VuZCkpCmZpcnN0X2NhbmRpZGF0ZV8zcAoKIyMgKiBMcGFMMTMtMTggMTQyODQ0IDYxMwpzZWNvbmRfY2FuZGlkYXRlX2NociA8LSBnZW5vbWVbWyJMcGFMMTNfMTgiXV0KcHJpbWVyX2xlbmd0aCA8LSAyMgphbXBsaWNvbl9sZW5ndGggPC0gNjEzCnNlY29uZF9jYW5kaWRhdGVfdGhpcmQgPC0gMTQyODQ0CnNlY29uZF9jYW5kaWRhdGVfc2Vjb25kIDwtIHNlY29uZF9jYW5kaWRhdGVfdGhpcmQgLSBhbXBsaWNvbl9sZW5ndGgKc2Vjb25kX2NhbmRpZGF0ZV9zdGFydCA8LSBzZWNvbmRfY2FuZGlkYXRlX3NlY29uZCAtIHByaW1lcl9sZW5ndGgKc2Vjb25kX2NhbmRpZGF0ZV9lbmQgPC0gc2Vjb25kX2NhbmRpZGF0ZV90aGlyZCArIHByaW1lcl9sZW5ndGgKc2Vjb25kX2NhbmRpZGF0ZV9yZWdpb24gPC0gc3Vic2VxKHNlY29uZF9jYW5kaWRhdGVfY2hyLCBzZWNvbmRfY2FuZGlkYXRlX3N0YXJ0LCBzZWNvbmRfY2FuZGlkYXRlX2VuZCkKc2Vjb25kX2NhbmRpZGF0ZV9yZWdpb24Kc2Vjb25kX2NhbmRpZGF0ZV81cCA8LSBzdWJzZXEoc2Vjb25kX2NhbmRpZGF0ZV9jaHIsIHNlY29uZF9jYW5kaWRhdGVfc3RhcnQsIHNlY29uZF9jYW5kaWRhdGVfc2Vjb25kKQphcy5jaGFyYWN0ZXIoc2Vjb25kX2NhbmRpZGF0ZV81cCkKc2Vjb25kX2NhbmRpZGF0ZV8zcCA8LSBzcGdzOjpyZXZlcnNlQ29tcGxlbWVudChzdWJzZXEoc2Vjb25kX2NhbmRpZGF0ZV9jaHIsIHNlY29uZF9jYW5kaWRhdGVfdGhpcmQsIHNlY29uZF9jYW5kaWRhdGVfZW5kKSkKc2Vjb25kX2NhbmRpZGF0ZV8zcAoKCiMjICogTHBhTDEzLTI5IDgzMDM0MiAyNTIKdGhpcmRfY2FuZGlkYXRlX2NociA8LSBnZW5vbWVbWyJMcGFMMTNfMjkiXV0KcHJpbWVyX2xlbmd0aCA8LSAyMgphbXBsaWNvbl9sZW5ndGggPC0gMjUyCnRoaXJkX2NhbmRpZGF0ZV90aGlyZCA8LSA4MzAzNDIKdGhpcmRfY2FuZGlkYXRlX3NlY29uZCA8LSB0aGlyZF9jYW5kaWRhdGVfdGhpcmQgLSBhbXBsaWNvbl9sZW5ndGgKdGhpcmRfY2FuZGlkYXRlX3N0YXJ0IDwtIHRoaXJkX2NhbmRpZGF0ZV9zZWNvbmQgLSBwcmltZXJfbGVuZ3RoCnRoaXJkX2NhbmRpZGF0ZV9lbmQgPC0gdGhpcmRfY2FuZGlkYXRlX3RoaXJkICsgcHJpbWVyX2xlbmd0aAp0aGlyZF9jYW5kaWRhdGVfcmVnaW9uIDwtIHN1YnNlcSh0aGlyZF9jYW5kaWRhdGVfY2hyLCB0aGlyZF9jYW5kaWRhdGVfc3RhcnQsIHRoaXJkX2NhbmRpZGF0ZV9lbmQpCnRoaXJkX2NhbmRpZGF0ZV9yZWdpb24KdGhpcmRfY2FuZGlkYXRlXzVwIDwtIHN1YnNlcSh0aGlyZF9jYW5kaWRhdGVfY2hyLCB0aGlyZF9jYW5kaWRhdGVfc3RhcnQsIHRoaXJkX2NhbmRpZGF0ZV9zZWNvbmQpCmFzLmNoYXJhY3Rlcih0aGlyZF9jYW5kaWRhdGVfNXApCnRoaXJkX2NhbmRpZGF0ZV8zcCA8LSBzcGdzOjpyZXZlcnNlQ29tcGxlbWVudChzdWJzZXEodGhpcmRfY2FuZGlkYXRlX2NociwgdGhpcmRfY2FuZGlkYXRlX3RoaXJkLCB0aGlyZF9jYW5kaWRhdGVfZW5kKSkKdGhpcmRfY2FuZGlkYXRlXzNwCiMjIFlvdSBhcmUgYSBnYXJiYWdlIHBvbHlweXJpbWlkaW5lIHRyYWN0LgojIyBXaGljaCBpcyBhY3R1YWxseSBpbnRlcmVzdGluZyBpZiB0aGUgbXV0YXRpb25zIG1lc3MgaXQgdXAuCgoKIyMgKiBMcGFMMTMtMzMgMTMzMTUwNyA4NDMKZm91cnRoX2NhbmRpZGF0ZV9jaHIgPC0gZ2Vub21lW1siTHBhTDEzXzMzIl1dCnByaW1lcl9sZW5ndGggPC0gMjIKYW1wbGljb25fbGVuZ3RoIDwtIDg0Mwpmb3VydGhfY2FuZGlkYXRlX3RoaXJkIDwtIDEzMzE1MDcKZm91cnRoX2NhbmRpZGF0ZV9zZWNvbmQgPC0gZm91cnRoX2NhbmRpZGF0ZV90aGlyZCAtIGFtcGxpY29uX2xlbmd0aApmb3VydGhfY2FuZGlkYXRlX3N0YXJ0IDwtIGZvdXJ0aF9jYW5kaWRhdGVfc2Vjb25kIC0gcHJpbWVyX2xlbmd0aApmb3VydGhfY2FuZGlkYXRlX2VuZCA8LSBmb3VydGhfY2FuZGlkYXRlX3RoaXJkICsgcHJpbWVyX2xlbmd0aApmb3VydGhfY2FuZGlkYXRlX3JlZ2lvbiA8LSBzdWJzZXEoZm91cnRoX2NhbmRpZGF0ZV9jaHIsIGZvdXJ0aF9jYW5kaWRhdGVfc3RhcnQsIGZvdXJ0aF9jYW5kaWRhdGVfZW5kKQpmb3VydGhfY2FuZGlkYXRlX3JlZ2lvbgpmb3VydGhfY2FuZGlkYXRlXzVwIDwtIHN1YnNlcShmb3VydGhfY2FuZGlkYXRlX2NociwgZm91cnRoX2NhbmRpZGF0ZV9zdGFydCwgZm91cnRoX2NhbmRpZGF0ZV9zZWNvbmQpCmFzLmNoYXJhY3Rlcihmb3VydGhfY2FuZGlkYXRlXzVwKQpmb3VydGhfY2FuZGlkYXRlXzNwIDwtIHNwZ3M6OnJldmVyc2VDb21wbGVtZW50KHN1YnNlcShmb3VydGhfY2FuZGlkYXRlX2NociwgZm91cnRoX2NhbmRpZGF0ZV90aGlyZCwgZm91cnRoX2NhbmRpZGF0ZV9lbmQpKQpmb3VydGhfY2FuZGlkYXRlXzNwCmBgYAoKIyMgR28gaHVudGluZyBmb3IgU2FuZ2VyIHNlcXVlbmNpbmcgcmVnaW9ucwoKSSBtYWRlIGEgZnVuIGxpdHRsZSBmdW5jdGlvbiB3aGljaCBzaG91bGQgZmluZCByZWdpb25zIHdoaWNoIGhhdmUgbG90cyBvZiB2YXJpYW50cwphc3NvY2lhdGVkIHdpdGggYSBnaXZlbiBleHBlcmltZW50YWwgZmFjdG9yLgoKYGBge3Igc2FuZ2VyX2Z1bn0KcGhlbm8gPC0gc3Vic2V0X2V4cHQobHBfZXhwdCwgc3Vic2V0ID0gImNvbmRpdGlvbj09J3oyLjInfGNvbmRpdGlvbj09J3oyLjMnIikKcGhlbm8gPC0gc3Vic2V0X2V4cHQocGhlbm8sIHN1YnNldCA9ICIhaXMubmEocERhdGEocGhlbm8pW1snYmNmdGFibGUnXV0pIikKcGhlbm9fc25wcyA8LSBzbShjb3VudF9leHB0X3NucHMocGhlbm8sIGFubm90X2NvbHVtbiA9ICJiY2Z0YWJsZSIpKQoKZnVuX3N0dWZmIDwtIHNucF9kZW5zaXR5X3ByaW1lcnMoCiAgICBwaGVub19zbnBzLAogICAgYnNnZW5vbWUgPSAiQlNHZW5vbWUuTGVpc2htYW5pYS5wYW5hbWVuc2lzLk1IT01DT0w4MUwxMy52NTMiLAogICAgZ2ZmID0gInJlZmVyZW5jZS9UcmlUcnlwREItNTNfTHBhbmFtZW5zaXNNSE9NQ09MODFMMTMuZ2ZmIikKZHJvcF9zY2FmZm9sZHMgPC0gZ3JlcGwoeCA9IHJvd25hbWVzKGZ1bl9zdHVmZiRmYXZvcml0ZXMpLCBwYXR0ZXJuID0gIlNDQUYiKQpmYXZvcml0ZV9wcmltZXJfcmVnaW9ucyA8LSBmdW5fc3R1ZmZbWyJmYXZvcml0ZXMiXV1bIWRyb3Bfc2NhZmZvbGRzLCBdCmZhdm9yaXRlX3ByaW1lcl9yZWdpb25zW1siYmluIl1dIDwtIHJvd25hbWVzKGZhdm9yaXRlX3ByaW1lcl9yZWdpb25zKQpsaWJyYXJ5KGRwbHlyKQpmYXZvcml0ZV9wcmltZXJfcmVnaW9ucyA8LSBmYXZvcml0ZV9wcmltZXJfcmVnaW9ucyAlPiUKICByZWxvY2F0ZShiaW4pCmBgYAoKIyMgQ29tYmluZSB0aGlzIHRhYmxlIHdpdGggMi4yLzIuMyBnZW5lcwoKSGVyZSBpcyBteSBub3RlIGZyb20gb3VyIG1lZXRpbmc6CgpDcm9zcyByZWZlcmVuY2UgcHJpbWVycyB0byBERSBnZW5lcyBvZiAyLjIvMi4zIGFuZC9vciByZXNpc3RhbmNlL3N1c2NwZXRpYmxlLAphZGQgYSBjb2x1bW4gdG8gdGhlIHByaW1lciBzcHJlYWRzaGVldCB3aXRoIHRoZSBERSBnZW5lcyAoaW4gcmV0cm9zcGVjdCBJIGFtIGd1ZXNzaW5nCnRoaXMgYWN0dWFsbHkgbWVhbnMgdG8gcHV0IHRoZSBsb2dGQyBhcyBhIGNvbHVtbi4KCk9uZSBuaWNlIHRoaW5nLCBJIGRpZCBhIHNlbWFudGljIHJlbW92YWwgb24gdGhlIGxwX2V4cHQsIHNvIHRoZSBzZXQgb2YgbG9nRkMvcHZhbHVlcwpzaG91bGQgbm90IGhhdmUgYW55IG9mIHRoZSBvZmZlbmRpbmcgdHlwZXM7IHRodXMgSSBzaG91bGQgYmUgYWJsZSB0byBhdXRvbWFnaWNhbGx5CmdldCByaWQgb2YgdGhlbSBpbiB0aGUgbWVyZ2UuCgpgYGB7ciB4cmVmX3ByaW1lcnNfZGVnfQpsb2dmYyA8LSB6eV90YWJsZV9zdmFbWyJkYXRhIl1dW1siejIzX3ZzX3oyMiJdXQpsb2dmY19jb2x1bW5zIDwtIGxvZ2ZjWywgYygiZGVzZXFfbG9nZmMiLCAiZGVzZXFfYWRqcCIpXQpjb2xuYW1lcyhsb2dmY19jb2x1bW5zKSA8LSBjKCJ6MjNfbG9nZmMiLCAiejIzX2FkanAiKQpuZXdfdGFibGUgPC0gbWVyZ2UoZmF2b3JpdGVfcHJpbWVyX3JlZ2lvbnMsIGxvZ2ZjX2NvbHVtbnMsCiAgICAgICAgICAgICAgICAgICBieS54ID0gImNsb3Nlc3RfZ2VuZV9iZWZvcmVfaWQiLCBieS55ID0gInJvdy5uYW1lcyIpCnN1cyA8LSBzdXNfdGFibGVfc3ZhW1siZGF0YSJdXVtbInNlbnNpdGl2ZV92c19yZXNpc3RhbnQiXV0Kc3VzX2NvbHVtbnMgPC0gc3VzWywgYygiZGVzZXFfbG9nZmMiLCAiZGVzZXFfYWRqcCIpXQpjb2xuYW1lcyhzdXNfY29sdW1ucykgPC0gYygic3VzX2xvZ2ZjIiwgInN1c19hZGpwIikKbmV3X3RhYmxlIDwtIG1lcmdlKG5ld190YWJsZSwgc3VzX2NvbHVtbnMsCiAgICAgICAgICAgICAgICAgICBieS54ID0gImNsb3Nlc3RfZ2VuZV9iZWZvcmVfaWQiLCBieS55ID0gInJvdy5uYW1lcyIpICU+JQogIHJlbG9jYXRlKGJpbikKd3JpdHRlbiA8LSB3cml0ZV94bHN4KGRhdGE9bmV3X3RhYmxlLAogICAgICAgICAgICAgICAgICAgICAgZXhjZWw9ImV4Y2VsL2Zhdm9yaXRlX3ByaW1lcnNfeHJlZl96eV9zdXMueGxzeCIpCmBgYAoKCiMjIE1ha2UgYSBoZWF0bWFwIGRlc2NyaWJpbmcgdGhlIGNsdXN0ZXJpbmcgb2YgdmFyaWFudHMKCldlIGNhbiBjcm9zcyByZWZlcmVuY2UgdGhlIHZhcmlhbnRzIGFnYWluc3QgdGhlIHp5bW9kZW1lIHN0YXR1cyBhbmQKcGxvdCBhIGhlYXRtYXAgb2YgdGhlIHJlc3VsdHMgYW5kIGhvcGVmdWxseSBzZWUgaG93IHRoZXkgc2VwYXJhdGUuCgpgYGB7ciB6eW1vX2hlYXRtYXBzfQpzbnBfZ2VuZXMgPC0gc20oc25wc192c19nZW5lcyhscF9leHB0LCBuZXdfc2V0cywgZXhwdF9uYW1lX2NvbCA9ICJjaHJvbW9zb21lIikpCgpjbGluaWNhbF9jb2xvcnNfdjIgPC0gbGlzdCgKICAgICJ6MjIiID0gIiMwMDAwY2MiLAogICAgInoyMyIgPSAiI2NjMDAwMCIpCm5ld196eW1vX25vcm0gPC0gbm9ybWFsaXplX2V4cHQocHJ1bmVkX3NucHMsIG5vcm1xID0gInF1YW50IikgJT4lCiAgc2V0X2V4cHRfY29uZGl0aW9ucyhmYWN0ID0gInp5bW9kZW1lY2F0ZWdvcmljYWwiKSAlPiUKICBzZXRfZXhwdF9jb2xvcnMoY2xpbmljYWxfY29sb3JzX3YyKQoKenltb19oZWF0IDwtIHBsb3RfZGlzaGVhdChuZXdfenltb19ub3JtKQpkZXYgPC0gcHAoZmlsZSA9ICJpbWFnZXMvb25seXoyMl96MjNfc25wX2hlYXRtYXAucGRmIiwgd2lkdGg9MTIsIGhlaWdodD0xMikKenltb19oZWF0W1sicGxvdCJdXQpjbG9zZWQgPC0gZGV2Lm9mZigpCnp5bW9faGVhdFtbInBsb3QiXV0KYGBgCgojIyMgQW5ub3RhdGVkIGhlYXRtYXAgb2YgdmFyaWFudHMKCk5vdyBsZXQgdXMgdHJ5IHRvIG1ha2UgYSBoZWF0bWFwIHdoaWNoIGluY2x1ZGVzIHNvbWUgb2YgdGhlIGFubm90YXRpb24gZGF0YS4KCmBgYHtyIHp5bW9faGVhdF9wYW5lbF9nZW5lc30KZGVzIDwtIGJvdGhfbm9ybVtbImRlc2lnbiJdXQp1bmRlZl9pZHggPC0gaXMubmEoZGVzW1sic3RyYWluIl1dKQpkZXNbdW5kZWZfaWR4LCAic3RyYWluIl0gPC0gInVua25vd24iCgojI2htY29scyA8LSBjb2xvclJhbXBQYWxldHRlKGMoInllbGxvdyIsImJsYWNrIiwiZGFya2JsdWUiKSkoMjU2KQpjb3JyZWxhdGlvbnMgPC0gaHBnbF9jb3IoZXhwcnMoYm90aF9ub3JtKSkKbmFfaWR4IDwtIGlzLm5hKGNvcnJlbGF0aW9ucykKY29ycmVsYXRpb25zW25hX2lkeF0gPC0gMAoKenltb19taXNzaW5nX2lkeCA8LSBpcy5uYShkZXNbWyJ6eW1vZGVtZWNhdGVnb3JpY2FsIl1dKQpkZXNbWyJ6eW1vZGVtZWNhdGVnb3JpY2FsIl1dIDwtIGFzLmNoYXJhY3RlcihkZXNbWyJ6eW1vZGVtZWNhdGVnb3JpY2FsIl1dKQpkZXNbWyJjbGluaWNhbGNhdGVnb3JpY2FsIl1dIDwtIGFzLmNoYXJhY3RlcihkZXNbWyJjbGluaWNhbGNhdGVnb3JpY2FsIl1dKQpkZXNbenltb19taXNzaW5nX2lkeCwgInp5bW9kZW1lY2F0ZWdvcmljYWwiXSA8LSAidW5rbm93biIKbXlkZW5kcm8gPC0gbGlzdCgKICAiY2x1c3RmdW4iID0gaGNsdXN0LAogICJsd2QiID0gMi4wKQpjb2xfZGF0YSA8LSBhcy5kYXRhLmZyYW1lKGRlc1ssIGMoInp5bW9kZW1lY2F0ZWdvcmljYWwiLCAiY2xpbmljYWxjYXRlZ29yaWNhbCIpXSkKCnVua25vd25fY2xpbmljYWwgPC0gaXMubmEoY29sX2RhdGFbWyJjbGluaWNhbGNhdGVnb3JpY2FsIl1dKQpyb3dfZGF0YSA8LSBhcy5kYXRhLmZyYW1lKGRlc1ssIGMoInN0cmFpbiIpXSkKY29sbmFtZXMoY29sX2RhdGEpIDwtIGMoInp5bW9kZW1lIiwgIm91dGNvbWUiKQpjb2xfZGF0YVt1bmtub3duX2NsaW5pY2FsLCAib3V0Y29tZSJdIDwtICJ1bmRlZmluZWQiCgpjb2xuYW1lcyhyb3dfZGF0YSkgPC0gYygic3RyYWluIikKbXlhbm5vdCA8LSBsaXN0KAogICJDb2wiID0gbGlzdCgiZGF0YSIgPSBjb2xfZGF0YSksCiAgIlJvdyIgPSBsaXN0KCJkYXRhIiA9IHJvd19kYXRhKSkKbXljbHVzdCA8LSBsaXN0KCJjdXRoIiA9IDEuMCwKICAgICAgICAgICAgICAgICJjb2wiID0gQnJld2VyQ2x1c3RlckNvbCkKbXlsYWJzIDwtIGxpc3QoCiAgIlJvdyIgPSBsaXN0KCJucm93IiA9IDQpLAogICJDb2wiID0gbGlzdCgibnJvdyIgPSA0KSkKaG1jb2xzIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiZGFya2JsdWUiLCAiYmVpZ2UiKSkoMjQwKQp6eW1vX2Fubm90X2hlYXQgPC0gYW5uSGVhdG1hcDIoCiAgICBjb3JyZWxhdGlvbnMsCiAgICBkZW5kcm9ncmFtID0gbXlkZW5kcm8sCiAgICBhbm5vdGF0aW9uID0gbXlhbm5vdCwKICAgIGNsdXN0ZXIgPSBteWNsdXN0LAogICAgbGFiZWxzID0gbXlsYWJzLAogICAgIyMgVGhlIGZvbGxvd2luZyBjb250cm9scyBpZiB0aGUgcGljdHVyZSBpcyBzeW1tZXRyaWMKICAgIHNjYWxlID0gIm5vbmUiLAogICAgY29sID0gaG1jb2xzKQoKZGV2IDwtIHBwKGZpbGUgPSAiaW1hZ2VzL2RlbmRyb19oZWF0bWFwLnBuZyIsIGhlaWdodCA9IDIwLCB3aWR0aCA9IDIwKQpwbG90KHp5bW9fYW5ub3RfaGVhdCkKY2xvc2VkIDwtIGRldi5vZmYoKQpwbG90KHp5bW9fYW5ub3RfaGVhdCkKYGBgCgpQcmludCB0aGUgbGFyZ2VyIGhlYXRtYXAgc28gdGhhdCBhbGwgdGhlIGxhYmVscyBhcHBlYXIuICBLZWVwIGluIG1pbmQKdGhhdCBhcyB3ZSBnZXQgbW9yZSBzYW1wbGVzLCB0aGlzIGltYWdlIG5lZWRzIHRvIGNvbnRpbnVlIGdldHRpbmcKYmlnZ2VyLgoKIVtiaWcgaGVhdG1hcF0oaW1hZ2VzL2RlbmRyb19oZWF0bWFwLnBuZykKCgpgYGB7ciB0aGVyZXNhX2lkZWF9CnhyZWZfcHJvcCA8LSB0YWJsZShwaGVub19zbnBzW1siY29uZGl0aW9ucyJdXSkKcGhlbm9fc25wcyRjb25kaXRpb25zCmlkeF90YmwgPC0gZXhwcnMocGhlbm9fc25wcykgPiA1Cm5ld190YmwgPC0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSByb3duYW1lcyhleHBycyhwaGVub19zbnBzKSkpCmZvciAobiBpbiBuYW1lcyh4cmVmX3Byb3ApKSB7CiAgbmV3X3RibFtbbl1dIDwtIDAKICBpZHhfY29scyA8LSB3aGljaChwaGVub19zbnBzW1siY29uZGl0aW9ucyJdXSA9PSBuKQogIHByb3BfY29sIDwtIHJvd1N1bXMoaWR4X3RibFssIGlkeF9jb2xzXSkgLyB4cmVmX3Byb3Bbbl0KICBuZXdfdGJsW25dIDwtIHByb3BfY29sCn0Ka2VlcGVycyA8LSBncmVwbCh4ID0gcm93bmFtZXMobmV3X3RibCksIHBhdHRlcm4gPSAiTHBhTDEzIikKbmV3X3RibCA8LSBuZXdfdGJsW2tlZXBlcnMsIF0KbmV3X3RibFtbInN0cm9uZzIyIl1dIDwtIDEuMDAxIC0gbmV3X3RibFtbInoyLjIiXV0KbmV3X3RibFtbInN0cm9uZzIzIl1dIDwtIDEuMDAxIC0gbmV3X3RibFtbInoyLjMiXV0KczIyX25hIDwtIG5ld190YmxbWyJzdHJvbmcyMiJdXSA+IDEKbmV3X3RibFtzMjJfbmEsICJzdHJvbmcyMiJdIDwtIDEKczIzX25hIDwtIG5ld190YmxbWyJzdHJvbmcyMyJdXSA+IDEKbmV3X3RibFtzMjNfbmEsICJzdHJvbmcyMyJdIDwtIDEKCm5ld190YmxbWyJTTlAiXV0gPC0gcm93bmFtZXMobmV3X3RibCkKbmV3X3RibFtbIkNocm9tb3NvbWUiXV0gPC0gZ3N1Yih4ID0gbmV3X3RibFtbIlNOUCJdXSwgcGF0dGVybiA9ICJjaHJfKC4qKV9wb3NfLioiLCByZXBsYWNlbWVudCA9ICJcXDEiKQpuZXdfdGJsW1siUG9zaXRpb24iXV0gPC0gZ3N1Yih4ID0gbmV3X3RibFtbIlNOUCJdXSwgcGF0dGVybiA9ICIuKl9wb3NfKFxcZCspXy4qIiwgcmVwbGFjZW1lbnQgPSAiXFwxIikKbmV3X3RibCA8LSBuZXdfdGJsWywgYygiU05QIiwgIkNocm9tb3NvbWUiLCAiUG9zaXRpb24iLCAic3Ryb25nMjIiLCAic3Ryb25nMjMiKV0KCmxpYnJhcnkoQ01wbG90KQpzaW1wbGlmeSA8LSBuZXdfdGJsCnNpbXBsaWZ5W1sic3Ryb25nMjIiXV0gPC0gTlVMTAoKQ01wbG90KHNpbXBsaWZ5LCBiaW4uc2l6ZSA9IDEwMDAwMCkKCkNNcGxvdChuZXdfdGJsLCBwbG90LnR5cGU9Im0iLCBtdWx0cmFja3M9VFJVRSwgdGhyZXNob2xkID0gYygwLjAxLCAwLjA1KSwKICAgICAgIHRocmVzaG9sZC5sd2Q9YygxLDEpLCB0aHJlc2hvbGQuY29sPWMoImJsYWNrIiwiZ3JleSIpLAogICAgICAgYW1wbGlmeT1UUlVFLCBiaW4uc2l6ZT0xMDAwMCwKICAgICAgIGNoci5kZW4uY29sPWMoImRhcmtncmVlbiIsICJ5ZWxsb3ciLCAicmVkIiksCiAgICAgICBzaWduYWwuY29sPWMoInJlZCIsICJncmVlbiIsICJibHVlIiksCiAgICAgICBzaWduYWwuY2V4PTEsIGZpbGU9ImpwZyIsIG1lbW89IiIsIGRwaT0zMDAsIGZpbGUub3V0cHV0PVRSVUUsIHZlcmJvc2U9VFJVRSkKYGBgCgo8IS0tLQohW1NOUCBEZW5zaXR5XShTTlAtRGVuc2l0eS5yYXRpby5qcGcpCiFbQ2lyY3VsYXIgTWFuaGF0dGFuXShDaXJjdWxhci1NYW5oYXR0YW4ucmF0aW8uanBnKQohW1JlY3Rhbmd1bGFyIE1hbmhhdHRhbl0oUmVjdGFuZ3VsYXItTWFuaGF0dGFuLnJhdGlvLmpwZykKIVtRUV0oUVFwbG90LnJhdGlvLmpwZykKLS0tPgoKIyMgVHJ5IG91dCBNYXRyaXhFUVRMCgpUaGlzIHRvb2wgbG9va3MgYSBsaXR0bGUgb3BhcXVlLCBidXQgcHJvdmlkZXMgc2FtcGxlIGRhdGEgd2l0aCB0aGluZ3MKdGhhdCBtYWtlIHNlbnNlIHRvIG1lIGFuZCBzaG91bGQgYmUgcHJldHR5IGVhc3kgdG8gcmVjYXBpdHVsYXRlIGluIG91cgpkYXRhLgoKMS4gIGNvdmFyaWF0ZXMudHh0OiBDb2x1bW5zIGFyZSBzYW1wbGVzLCByb3dzIGFyZSB0aGluZ3MgZnJvbSBwRGF0YSAtLSB0aGUKICAgIG1vc3QgbGlrZWx5IG9uZXMgb2YgaW50ZXJlc3QgZm9yIG91ciBkYXRhIHdvdWxkIGJlIHp5bW9kZW1lLAogICAgc2Vuc2l0aXZpdHkKMi4gIGdlbmVsb2MudHh0OiBjb2x1bW5zIGFyZSAnZ2VuZWlkJywgJ2NocicsICdsZWZ0JywgJ3JpZ2h0Jy4gIEkKICAgIGd1ZXNzIEkgY2FuIGFzc3VtZSBsZWZ0IGFuZCByaWdodCBhcmUgc3RhcnQvc3RvcDsgaW4gd2hpY2ggY2FzZQogICAgdGhpcyBpcyB0cml2aWFsbHkgYWNxdWlyYWJsZSBmcm9tIGZEYXRhLgozLiAgZ2UudHh0OiBUaGlzIGFwcGVhcnMgdG8gYmUgYSBsb2cocnBrbS9jcG0pIHRhYmxlIHdpdGggcm93cyBhcyBnZW5lcyBhbmQKICAgIGNvbHVtbnMgYXMgc2FtcGxlcwo0LiAgc25wc2xvYy50eHQ6IGNvbHVtbnMgYXJlICdzbnBpZCcsICdjaHInLCAncG9zJwo1LiAgc25wcy50eHQ6IGNvbHVtbnMgYXJlIHNhbXBsZXMsIHJvd3MgYXJlIHRoZSBpZHMgZnJvbSBzbnNwbG9jLAogICAgdmFsdWVzIGEgMCwxLDIuICBJIGFzc3VtZSAwIGlzIGlkZW50aWNhbCBhbmQgMS4uMTIgYXJlIHRoZSB2YXJpb3VzCiAgICBBLT5UR0MgVC0+QUdDIEMtPkFHVCBHLT5BQ1QKCmBgYHtyIG1hdHJpeGVxdGwsIGV2YWw9RkFMU0V9CiMjIEZvciB0aGlzLCBsZXQgdXMgdXNlIHRoZSAnbmV3X3NucHMnIGRhdGEgc3RydWN0dXJlLgojIyBDYXZlYXQgaGVyZTogdGhlc2UgbmVlZCB0byBiZSBjb2VyY2VkIHRvIG51bWJlcnMuCm15X2NvdmFyaWF0ZXMgPC0gcERhdGEobmV3X3NucHMpWywgYygienltb2RlbWVjYXRlZ29yaWNhbCIsICJjbGluaWNhbGNhdGVnb3JpY2FsIildCmZvciAoY29sIGluIGNvbG5hbWVzKG15X2NvdmFyaWF0ZXMpKSB7CiAgbXlfY292YXJpYXRlc1tbY29sXV0gPC0gYXMubnVtZXJpYyhhcy5mYWN0b3IobXlfY292YXJpYXRlc1tbY29sXV0pKQp9Cm15X2NvdmFyaWF0ZXMgPC0gdChteV9jb3ZhcmlhdGVzKQoKbXlfZ2VuZWxvYyA8LSBmRGF0YShscF9leHB0KVssIGMoImdpZCIsICJjaHJvbW9zb21lIiwgInN0YXJ0IiwgImVuZCIpXQpjb2xuYW1lcyhteV9nZW5lbG9jKSA8LSBjKCJnZW5laWQiLCAiY2hyIiwgImxlZnQiLCAicmlnaHQiKQoKbXlfZ2UgPC0gZXhwcnMobm9ybWFsaXplX2V4cHQobHBfZXhwdCwgdHJhbnNmb3JtID0gImxvZzIiLCBmaWx0ZXIgPSBUUlVFLCBjb252ZXJ0ID0gImNwbSIpKQp1c2VkX3NhbXBsZXMgPC0gdG9sb3dlcihjb2xuYW1lcyhteV9nZSkpICVpbiUgY29sbmFtZXMoZXhwcnMobmV3X3NucHMpKQpteV9nZSA8LSBteV9nZVssIHVzZWRfc2FtcGxlc10KCm15X3NucHNsb2MgPC0gZGF0YS5mcmFtZShyb3duYW1lcyA9IHJvd25hbWVzKGV4cHJzKG5ld19zbnBzKSkpCiMjIE9oLCBjYXZlYXQgaGVyZTogQmVjYXVzZSBvZiB0aGUgd2F5IEkgc3RvcmVkIHRoZSBkYXRhLAojIyBJIGNvdWxkIGhhdmUgZHVwbGljYXRlIHJvd3Mgd2hpY2ggcHJlc3VtYWJseSB3aWxsIG1ha2UgbWF0cml4RVFUTCBzYWQKbXlfc25wc2xvY1tbImNociJdXSA8LSBnc3ViKHBhdHRlcm4gPSAiXmNocl8oLispX3BvcyguKylfcmVmXy4qJCIsIHJlcGxhY2VtZW50ID0gIlxcMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gcm93bmFtZXMobXlfc25wc2xvYykpCm15X3NucHNsb2NbWyJwb3MiXV0gPC0gZ3N1YihwYXR0ZXJuID0gIl5jaHJfKC4rKV9wb3MoLispX3JlZl8uKiQiLCByZXBsYWNlbWVudCA9ICJcXDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IHJvd25hbWVzKG15X3NucHNsb2MpKQp0ZXN0IDwtIGR1cGxpY2F0ZWQobXlfc25wc2xvYykKIyMgRWFjaCBkdXBsaWNhdGVkIHJvdyB3b3VsZCBiZSBhbm90aGVyIHZhcmlhbnQgYXQgdGhhdCBwb3NpdGlvbjsKIyMgc28gaW4gdGhlb3J5IHdlIHdvdWxkIGRvIGEgcmxlIHRvIG51bWJlciB0aGVtIEkgYW0gZ3Vlc3NpbmcKIyMgSG93ZXZlciwgSSBkbyBub3QgaGF2ZSBkaWZmZXJlbnQgdmFyaWFudHMgc28gSSB0aGluayBJIGNhbiBpZ25vcmUgdGhpcyBmb3IgdGhlIG1vbWVudAojIyBidXQgd2lsbCBuZWVkIHRvIG1ha2UgbXkgbWF0cml4IGVpdGhlciAwIG9yIDEuCmlmIChzdW0odGVzdCkgPiAwKSB7CiAgbWVzc2FnZSgiVGhlcmUgYXJlOiAiLCBzdW0oZHVwbGljYXRlZCksICIgZHVwbGljYXRlZCBlbnRyaWVzLiIpCiAga2VlcF9pZHggPC0gISB0ZXN0CiAgbXlfc25wc2xvYyA8LSBteV9zbnBzbG9jW2tlZXBfaWR4LCBdCn0KCm15X3NucHMgPC0gZXhwcnMobmV3X3NucHMpCm9uZV9pZHggPC0gbXlfc25wcyA+IDAKbXlfc25wc1tvbmVfaWR4XSA8LSAxCgojIyBPaywgYXQgdGhpcyBwb2ludCBJIHRoaW5rIEkgaGF2ZSBhbGwgdGhlIHBpZWNlcyB3aGljaCB0aGlzIG1ldGhvZCB3YW50cy4uLgojIyBPaCwgbm8gSSBndWVzcyBub3Q7IGl0IGFjdHVhbGx5IHdhbnRzIHRoZSBkYXRhIGFzIGEgc2V0IG9mIGZpbGVuYW1lcy4uLgpsaWJyYXJ5KE1hdHJpeEVRVEwpCndyaXRlLnRhYmxlKG15X3NucHMsICJlcXRsL3NucHMudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YobXlfc25wcywgImVxdGwvc25wcy50c3YiLCApCndyaXRlLnRhYmxlKG15X3NucHNsb2MsICJlcXRsL3NucHNsb2MudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YobXlfc25wc2xvYywgImVxdGwvc25wc2xvYy50c3YiKQp3cml0ZS50YWJsZShhcy5kYXRhLmZyYW1lKG15X2dlKSwgImVxdGwvZ2UudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YoYXMuZGF0YS5mcmFtZShteV9nZSksICJlcXRsL2dlLnRzdiIpCndyaXRlLnRhYmxlKGFzLmRhdGEuZnJhbWUobXlfZ2VuZWxvYyksICJlcXRsL2dlbmVsb2MudHN2IiwgbmEgPSAiTkEiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IFRSVUUpCiMjIHJlYWRyOjp3cml0ZV90c3YoYXMuZGF0YS5mcmFtZShteV9nZW5lbG9jKSwgImVxdGwvZ2VuZWxvYy50c3YiKQp3cml0ZS50YWJsZShhcy5kYXRhLmZyYW1lKG15X2NvdmFyaWF0ZXMpLCAiZXF0bC9jb3ZhcmlhdGVzLnRzdiIsIG5hID0gIk5BIiwgY29sLm5hbWVzID0gVFJVRSwgcm93Lm5hbWVzID0gVFJVRSwgc2VwID0gIlx0IiwgcXVvdGUgPSBUUlVFKQojIyByZWFkcjo6d3JpdGVfdHN2KGFzLmRhdGEuZnJhbWUobXlfY292YXJpYXRlcyksICJlcXRsL2NvdmFyaWF0ZXMudHN2IikKCnVzZU1vZGVsID0gbW9kZWxMSU5FQVIgIyBtb2RlbEFOT1ZBLCBtb2RlbExJTkVBUiwgb3IgbW9kZWxMSU5FQVJfQ1JPU1MKCiMgR2Vub3R5cGUgZmlsZSBuYW1lClNOUF9maWxlX25hbWUgPSAiZXF0bC9zbnBzLnRzdiIKc25wc19sb2NhdGlvbl9maWxlX25hbWUgPSAiZXF0bC9zbnBzbG9jLnRzdiIKZXhwcmVzc2lvbl9maWxlX25hbWUgPSAiZXF0bC9nZS50c3YiCmdlbmVfbG9jYXRpb25fZmlsZV9uYW1lID0gImVxdGwvZ2VuZWxvYy50c3YiCmNvdmFyaWF0ZXNfZmlsZV9uYW1lID0gImVxdGwvY292YXJpYXRlcy50c3YiCiMgT3V0cHV0IGZpbGUgbmFtZQpvdXRwdXRfZmlsZV9uYW1lX2NpcyA9IHRlbXBmaWxlKCkKb3V0cHV0X2ZpbGVfbmFtZV90cmEgPSB0ZW1wZmlsZSgpCiMgT25seSBhc3NvY2lhdGlvbnMgc2lnbmlmaWNhbnQgYXQgdGhpcyBsZXZlbCB3aWxsIGJlIHNhdmVkCnB2T3V0cHV0VGhyZXNob2xkX2NpcyA9IDAuMQpwdk91dHB1dFRocmVzaG9sZF90cmEgPSAwLjEKIyBFcnJvciBjb3ZhcmlhbmNlIG1hdHJpeAojIFNldCB0byBudW1lcmljKCkgZm9yIGlkZW50aXR5LgplcnJvckNvdmFyaWFuY2UgPSBudW1lcmljKCkKIyBlcnJvckNvdmFyaWFuY2UgPSByZWFkLnRhYmxlKCJTYW1wbGVfRGF0YS9lcnJvckNvdmFyaWFuY2UudHh0Iik7CiMgRGlzdGFuY2UgZm9yIGxvY2FsIGdlbmUtU05QIHBhaXJzCmNpc0Rpc3QgPSAxZTYKIyMgTG9hZCBnZW5vdHlwZSBkYXRhCnNucHMgPSBTbGljZWREYXRhJG5ldygpCnNucHMkZmlsZURlbGltaXRlciA9ICJcdCIgICAgICAjIHRoZSBUQUIgY2hhcmFjdGVyCnNucHMkZmlsZU9taXRDaGFyYWN0ZXJzID0gIk5BIiAjIGRlbm90ZSBtaXNzaW5nIHZhbHVlczsKc25wcyRmaWxlU2tpcFJvd3MgPSAxICAgICAgICAgICMgb25lIHJvdyBvZiBjb2x1bW4gbGFiZWxzCnNucHMkZmlsZVNraXBDb2x1bW5zID0gMSAgICAgICAjIG9uZSBjb2x1bW4gb2Ygcm93IGxhYmVscwpzbnBzJGZpbGVTbGljZVNpemUgPSAyMDAwICAgICAgIyByZWFkIGZpbGUgaW4gc2xpY2VzIG9mIDIsMDAwIHJvd3MKc25wcyRMb2FkRmlsZShTTlBfZmlsZV9uYW1lKQojIyBMb2FkIGdlbmUgZXhwcmVzc2lvbiBkYXRhCmdlbmUgPSBTbGljZWREYXRhJG5ldygpCmdlbmUkZmlsZURlbGltaXRlciA9ICJcdCIgICAgICAjIHRoZSBUQUIgY2hhcmFjdGVyCmdlbmUkZmlsZU9taXRDaGFyYWN0ZXJzID0gIk5BIiAjIGRlbm90ZSBtaXNzaW5nIHZhbHVlczsKZ2VuZSRmaWxlU2tpcFJvd3MgPSAxICAgICAgICAgICMgb25lIHJvdyBvZiBjb2x1bW4gbGFiZWxzCmdlbmUkZmlsZVNraXBDb2x1bW5zID0gMSAgICAgICAjIG9uZSBjb2x1bW4gb2Ygcm93IGxhYmVscwpnZW5lJGZpbGVTbGljZVNpemUgPSAyMDAwICAgICAgIyByZWFkIGZpbGUgaW4gc2xpY2VzIG9mIDIsMDAwIHJvd3MKZ2VuZSRMb2FkRmlsZShleHByZXNzaW9uX2ZpbGVfbmFtZSkKIyMgTG9hZCBjb3ZhcmlhdGVzCmN2cnQgPSBTbGljZWREYXRhJG5ldygpCmN2cnQkZmlsZURlbGltaXRlciA9ICJcdCIgICAgICAjIHRoZSBUQUIgY2hhcmFjdGVyCmN2cnQkZmlsZU9taXRDaGFyYWN0ZXJzID0gIk5BIiAjIGRlbm90ZSBtaXNzaW5nIHZhbHVlczsKY3ZydCRmaWxlU2tpcFJvd3MgPSAxICAgICAgICAgICMgb25lIHJvdyBvZiBjb2x1bW4gbGFiZWxzCmN2cnQkZmlsZVNraXBDb2x1bW5zID0gMSAgICAgICAjIG9uZSBjb2x1bW4gb2Ygcm93IGxhYmVscwppZihsZW5ndGgoY292YXJpYXRlc19maWxlX25hbWUpID4gMCkgewogIGN2cnQkTG9hZEZpbGUoY292YXJpYXRlc19maWxlX25hbWUpCn0KIyMgUnVuIHRoZSBhbmFseXNpcwpzbnBzcG9zID0gcmVhZC50YWJsZShzbnBzX2xvY2F0aW9uX2ZpbGVfbmFtZSwgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpnZW5lcG9zID0gcmVhZC50YWJsZShnZW5lX2xvY2F0aW9uX2ZpbGVfbmFtZSwgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKbWUgPSBNYXRyaXhfZVFUTF9tYWluKAogICAgc25wcyA9IHNucHMsCiAgICBnZW5lID0gZ2VuZSwKICAgIGN2cnQgPSBjdnJ0LAogICAgb3V0cHV0X2ZpbGVfbmFtZSA9IG91dHB1dF9maWxlX25hbWVfdHJhLAogICAgcHZPdXRwdXRUaHJlc2hvbGQgPSBwdk91dHB1dFRocmVzaG9sZF90cmEsCiAgICB1c2VNb2RlbCA9IHVzZU1vZGVsLAogICAgZXJyb3JDb3ZhcmlhbmNlID0gZXJyb3JDb3ZhcmlhbmNlLAogICAgdmVyYm9zZSA9IFRSVUUsCiAgICBvdXRwdXRfZmlsZV9uYW1lLmNpcyA9IG91dHB1dF9maWxlX25hbWVfY2lzLAogICAgcHZPdXRwdXRUaHJlc2hvbGQuY2lzID0gcHZPdXRwdXRUaHJlc2hvbGRfY2lzLAogICAgc25wc3BvcyA9IHNucHNwb3MsCiAgICBnZW5lcG9zID0gZ2VuZXBvcywKICAgIGNpc0Rpc3QgPSBjaXNEaXN0LAogICAgcHZhbHVlLmhpc3QgPSAicXFwbG90IiwKICAgIG1pbi5wdi5ieS5nZW5lc25wID0gRkFMU0UsCiAgICBub0ZEUnNhdmVNZW1vcnkgPSBGQUxTRSk7CmBgYAoKCgpgYGB7ciBzYXZlbWV9CmlmICghaXNUUlVFKGdldDAoInNraXBfbG9hZCIpKSkgewogIHBhbmRlcjo6cGFuZGVyKHNlc3Npb25JbmZvKCkpCiAgbWVzc2FnZShwYXN0ZTAoIlRoaXMgaXMgaHBnbHRvb2xzIGNvbW1pdDogIiwgZ2V0X2dpdF9jb21taXQoKSkpCiAgbWVzc2FnZShwYXN0ZTAoIlNhdmluZyB0byAiLCBzYXZlZmlsZSkpCiAgdG1wIDwtIHNtKHNhdmVtZShmaWxlbmFtZSA9IHNhdmVmaWxlKSkKfQpgYGAKCmBgYHtyIGxvYWRtZV9hZnRlciwgZXZhbCA9IEZBTFNFfQp0bXAgPC0gbG9hZG1lKGZpbGVuYW1lID0gc2F2ZWZpbGUpCmBgYAo=