1 Introduction

This dataset contains data from two experiments:

  1. ‘rph’: This appears to me to be a reasonably simple comparison of a ‘normal’ PA14 lab strain and two methods to knock out the rph gene ribonuclease PH; “Phosphorolytic 3’ to 5’ exoribonuclease” which helps tRNA 3’ end maturation. It removes residues after the CCA end, and also sometimes adds nucleotides using nucleoside diphosphates as input. Potentially helps degrade 16S when starving.
  2. ‘alginate’: This is a very complex experiment which uses a single strain (PA14 with a plasmid containing algU). AlgU regulates the rsmA promoter which is important for virulence and has quite a lot of literature surrounding its function. I am guessing this is a knockout strain for algU? Either way, this strain was grown either in LB or SCFM (synthetic cystic fibrosis sputum media) with one or more supplements: DMSO (control), or IPTG (induce), and/or Eb/EbS/EbO (reducers).

2 Annotations

I am only now checking out pseudomonas.com. At first glance I am not seeing a good way to programmatically download the annotation data, but there is a nice catalog of files to download. In addition, it appears they do a good job of ensuring that all of the strain annotations are at NCBI as genbank files; so I should not have significant troubles if I want to get these annotations.

With that in mind, I am using two data sources for annotations, the gff file I extracted from the NCBI reference genome and copied to the local ‘reference/’ directory (thus I assume it is identical to what is at pseudomonas.com), and annotations from microbesonline.org.

pa14_gff <- load_gff_annotations("reference/paeruginosa_pa14.gff", id_col="gene_id")
## Trying attempt: rtracklayer::import.gff3(gff, sequenceRegionsAsSeqinfo = TRUE)
## Had a successful gff import with rtracklayer::import.gff3(gff, sequenceRegionsAsSeqinfo = TRUE)
## Returning a df with 16 columns and 11946 rows.
rownames(pa14_gff) <- pa14_gff[["gene_id"]]
## The Alias column has PA14_00010

pa14_microbes <- as.data.frame(load_microbesonline_annotations("PA14"))
## Found 1 entry.
## Pseudomonas aeruginosa UCBPP-PA14Proteobacteria2006-11-22yes105972208963
## The species being downloaded is: Pseudomonas aeruginosa UCBPP-PA14
## Downloading: http://www.microbesonline.org/cgi-bin/genomeInfo.cgi?tId=208963;export=tab
## The sysName column has PA14_0010

pa14_annot <- merge(pa14_gff, pa14_microbes, by.x="Alias", by.y="sysName")
rownames(pa14_annot) <- pa14_annot[["gene_id"]]

## The identifiers are a bit odd, so we need to do a little work
pa14_length <- pa14_annot[, c("gene_id", "width", "Alias")]
pa14_go <- load_microbesonline_go(species="PA14", id_column="sysName")
## Found 1 entry.
## Pseudomonas aeruginosa UCBPP-PA14Proteobacteria2006-11-22yes105972208963
## The species being downloaded is: Pseudomonas aeruginosa UCBPP-PA14 and is being downloaded as 208963.tab.
pa14_go_length <- merge(pa14_go, pa14_length, by.x="sysName", by.y="Alias")
pa14_go <- pa14_go_length[, c("sysName", "GO")]
colnames(pa14_go) <- c("ID", "GO")
pa14_length <- pa14_go_length[, c("sysName", "width")]
##pa14_length_ids <- unique(pa14_length)[["gene_id"]]
##pa14_length <- pa14_length[pa14_length_ids, ]
rownames(pa14_length) <- make.names(pa14_length[[1]], unique=TRUE)
colnames(pa14_length) <- c("ID", "width")

3 Make the expressionset

Previously, I loaded all samples from both groups of experiments together and separated them. I decided in this iteration to separate them in the hopes of avoiding any potential confusion.

The following line reads in the sample sheet and creates a data structure which contains all of the sample annotations, the gene annotations, and the counts/gene.

sk_expt <- create_expt("sample_sheets/all_samples_sk_202206.xlsx",
                       gene_info=pa14_annot,
                       file_column="hisatcounttablereverse")
## Reading the sample metadata.
## Did not find the condition column in the sample sheet.
## Filling it in as undefined.
## Did not find the batch column in the sample sheet.
## Filling it in as undefined.
## The sample definitions comprises: 45 rows(samples) and 32 columns(metadata fields).
## Matched 5972 annotations and counts.
## Bringing together the count matrix and gene information.
## Some annotations were lost in merging, setting them to 'undefined'.
## Saving the expressionset to 'expt.rda'.
## The final expressionset has 5979 features and 45 samples.

4 Quick view of the data

The first view of the data is to see how many counts/sample were observed and to get a sense of how many genes were observed:

The range of reads/sample goes from ~ 3.1 to 5.9 million, which seems quite reasonable to me.

The range of genes observed goes from ~ 5,925 to 5,980. This is a beautifully tight range.

pa14_libsize <- plot_libsize(sk_expt)
pa14_libsize$plot

pa14_nonzero <- plot_nonzero(sk_expt)
pa14_nonzero$plot
## Warning: ggrepel: 6 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

5 Separate the experiments and write out the data

I suspect that SooKyoung might enjoy poking at the counts in excel. Before writing it out though let us set the conditions and batches so the excel output has some pretty colors.

sk_expt <- set_expt_conditions(sk_expt, fact="media") %>%
  set_expt_batches(fact="bioreplicate")

rph_expt <- subset_expt(sk_expt, subset="media=='LB'") %>%
  set_expt_conditions(fact="strains") %>%
  sanitize_expt_metadata()
## subset_expt(): There were 45, now there are 9 samples.
rph_written <- write_expt(rph_expt,
                          excel=glue::glue("excel/rph_data-v{ver}.xlsx"))
## Deleting the file excel/rph_data-v20220630.xlsx before writing the tables.
## Writing the first sheet, containing a legend and some summary data.
## `geom_smooth()` using formula 'y ~ x'
## `geom_smooth()` using formula 'y ~ x'
## Loading required package: Matrix
## 
## Attaching package: 'Matrix'
## The following object is masked from 'package:S4Vectors':
## 
##     expand
## 
## Total:43 s
## `geom_smooth()` using formula 'y ~ x'
## `geom_smooth()` using formula 'y ~ x'
## 
## Total:40 s

6 Alginate induce vs. reduce

Before writing out the alginate data, let us take a moment to separate the factors for inducing and reducing expression

alginate_expt <- subset_expt(sk_expt, subset="media!='LB'") %>%
  set_expt_conditions(fact="media")
## subset_expt(): There were 45, now there are 36 samples.
conditions <- pData(alginate_expt)[["condition"]]
induce_bool <- grepl(pattern="IPTG", x=conditions)
induce_factor <- rep("not_induced", length(induce_bool))
induce_factor[induce_bool] <- "IPTG"

reduce_bool <- grepl(pattern="Eb", x=conditions)
reduce_factor <- rep("not_reduced", length(reduce_bool))
eb_samples <-  grepl(pattern="Eb", x=conditions)
reduce_factor[eb_samples] <- "Eb"
ebo_samples <- grepl(pattern="EbO", x=conditions)
reduce_factor[ebo_samples] <- "EbO"
ebs_samples <- grepl(pattern="EbS", x=conditions)
reduce_factor[ebs_samples] <- "EbS"

media_factor <- conditions
lb_bool <- grepl(pattern="LB", x=conditions)
media_factor[lb_bool] <- "LB"
media_factor[!lb_bool] <- "SCFM"

pData(alginate_expt)[["media_add"]] <- pData(alginate_expt)[["media"]]
pData(alginate_expt)[["media"]] <- media_factor
pData(alginate_expt)[["induced"]] <- as.factor(induce_factor)
pData(alginate_expt)[["reduced"]] <- as.factor(reduce_factor)

induce_reduce_factor <- paste0(induce_factor, "_", reduce_factor)
pData(alginate_expt)[["induce_reduce"]] <- induce_reduce_factor

media_induce_reduce_factor <- paste0(media_factor, "_", induce_reduce_factor)

alginate_expt <- set_expt_conditions(alginate_expt, fact=media_induce_reduce_factor) %>%
  set_expt_batches(fact="bioreplicate")

reduce_bool <- reduce_factor
reduce_idx <- grepl(pattern="Eb", x=reduce_bool)
reduce_bool[reduce_idx] <- "reduced"
pData(alginate_expt)[["reduced_bool"]] <- reduce_bool

7 Write the new alginate data

Now we have rewritten the alginate metadata to have separate factors for if/when samples were induced via IPTG and if/when the were reduced via Eb*, and the media factor is only Lb vs. SCFM.

alginate_written <- write_expt(alginate_expt,
                               excel=glue::glue("excel/alginate_data-v{ver}.xlsx"))
## Deleting the file excel/alginate_data-v20220630.xlsx before writing the tables.
## Writing the first sheet, containing a legend and some summary data.
## `geom_smooth()` using formula 'y ~ x'
## `geom_smooth()` using formula 'y ~ x'
## 
## Total:39 s
## Error in solve.default(t(mod) %*% mod) : 
##   Lapack routine dgesv: system is exactly singular: U[18,18] = 0
## Error in solve.default(t(mod) %*% mod) : 
##   Lapack routine dgesv: system is exactly singular: U[18,18] = 0
## `geom_smooth()` using formula 'y ~ x'
## `geom_smooth()` using formula 'y ~ x'
## 
## Total:42 s

8 rph data

Lets take a moment to look at and perform some simple analyses of the rph data set first. One thing to note, my sanitizer function removed the delta from the condition, perhaps I should have it replace delta with ‘d’ or something? In any event, the deletion strain is more similar to wild-type than the transposon mutant – by a lot.

rph_norm <- normalize_expt(rph_expt, transform="log2",
                           convert="cpm", norm="quant", filter=TRUE)
## Removing 211 low-count genes (5768 remaining).
## transform_counts: Found 220 values equal to 0, adding 1 to the matrix.
plot_pca(rph_norm)$plot

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

8.1 Differential expression

The data is clear enough that I do not think it warrants any intrusive things like sva, I will just include bioreplicate in the statistical model.

rph_de <- all_pairwise(rph_expt, model_batch=TRUE, filter=TRUE)
## Using limma's removeBatchEffect to visualize with(out) batch inclusion.
## Finished running DE analyses, collecting outputs.
## Comparing analyses.

## I suspect that only the first two contrasts will be of significant interest.
interesting <- list(
    "deletion_vs_wt" = c("pa14rph", "pa14wt"),
    "trans_vs_wt" = c("pa14tnrph", "pa14wt"),
    "deletion_vs_trans" = c("pa14rph", "pa14tnrph"))
rph_tables <- combine_de_tables(
    rph_de, keepers=interesting,
    excel=glue::glue("excel/rph_tables-v{ver}.xlsx"))
## Deleting the file excel/rph_tables-v20220630.xlsx before writing the tables.
rph_sig <- extract_significant_genes(
    rph_tables,
    excel=glue::glue("excel/rph_sig-v{ver}.xlsx"))
## Deleting the file excel/rph_sig-v20220630.xlsx before writing the tables.

8.2 Ontology

Let us see if there are some groups of genes of interest as identified by GO.

deletion_ups <- rph_sig[["deseq"]][["ups"]][[1]]
rownames(deletion_ups) <- make.names(deletion_ups[["namex"]], unique=TRUE)
deletion_downs <- rph_sig[["deseq"]][["downs"]][[1]]
rownames(deletion_downs) <- make.names(deletion_downs[["namex"]], unique=TRUE)

trans_ups <- rph_sig[["deseq"]][["ups"]][[2]]
rownames(trans_ups) <- make.names(trans_ups[["namex"]], unique=TRUE)
trans_downs <- rph_sig[["deseq"]][["downs"]][[2]]
rownames(trans_downs) <- make.names(trans_downs[["namex"]], unique=TRUE)

## The go data from microbesonline is keyed by the gene name
## (e.g. dnaA), not gene ID or PA id or whatever.

deletion_up_goseq <- simple_goseq(deletion_ups, go_db=pa14_go, length_db=pa14_length,
                                  excel=glue::glue("excel/deletion_up_goseq-v{ver}.xlsx"))
## Found 62 go_db genes and 62 length_db genes out of 210.
## Testing that go categories are defined.
## Removing undefined categories.
## Gathering synonyms.
## Gathering category definitions.
## The score column is null, defaulting to score.
## Possible columns are:
## [1] "category"                 "over_represented_pvalue" 
## [3] "under_represented_pvalue" "numDEInCat"              
## [5] "numInCat"                 "term"                    
## [7] "ontology"                 "qvalue"
## The score column is null, defaulting to score.
## Possible columns are:
## [1] "category"                 "over_represented_pvalue" 
## [3] "under_represented_pvalue" "numDEInCat"              
## [5] "numInCat"                 "term"                    
## [7] "ontology"                 "qvalue"
## The score column is null, defaulting to score.
## Possible columns are:
## [1] "category"                 "over_represented_pvalue" 
## [3] "under_represented_pvalue" "numDEInCat"              
## [5] "numInCat"                 "term"                    
## [7] "ontology"                 "qvalue"
## Deleting the file excel/deletion_up_goseq-v20220630.xlsx before writing the tables.
## Writing a sheet containing the legend.
## Writing the BP data.
## Writing the MF data.
## Writing the CC data.
## Finished writing excel file.
deletion_up_goseq[["pvalue_plots"]][["bpp_plot_over"]]

deletion_up_goseq[["pvalue_plots"]][["mfp_plot_over"]]

deletion_down_goseq <- simple_goseq(deletion_downs, go_db=pa14_go, length_db=pa14_length,
                                    excel=glue::glue("excel/deletion_down_goseq-v{ver}.xlsx"))
## Found 34 go_db genes and 34 length_db genes out of 79.
## Testing that go categories are defined.
## Removing undefined categories.
## Gathering synonyms.
## Gathering category definitions.
## The score column is null, defaulting to score.
## Possible columns are:
## [1] "category"                 "over_represented_pvalue" 
## [3] "under_represented_pvalue" "numDEInCat"              
## [5] "numInCat"                 "term"                    
## [7] "ontology"                 "qvalue"
## The score column is null, defaulting to score.
## Possible columns are:
## [1] "category"                 "over_represented_pvalue" 
## [3] "under_represented_pvalue" "numDEInCat"              
## [5] "numInCat"                 "term"                    
## [7] "ontology"                 "qvalue"
## The score column is null, defaulting to score.
## Possible columns are:
## [1] "category"                 "over_represented_pvalue" 
## [3] "under_represented_pvalue" "numDEInCat"              
## [5] "numInCat"                 "term"                    
## [7] "ontology"                 "qvalue"
## Deleting the file excel/deletion_down_goseq-v20220630.xlsx before writing the tables.
## Writing a sheet containing the legend.
## Writing the BP data.
## Writing the MF data.
## Writing the CC data.
## Finished writing excel file.
deletion_down_goseq[["pvalue_plots"]][["bpp_plot_over"]]

deletion_down_goseq[["pvalue_plots"]][["mfp_plot_over"]]

trans_up_goseq <- simple_goseq(trans_ups, go_db=pa14_go, length_db=pa14_length,
                               excel=glue::glue("excel/trans_up_goseq-v{ver}.xlsx"))
## Found 332 go_db genes and 332 length_db genes out of 913.
## Testing that go categories are defined.
## Removing undefined categories.
## Gathering synonyms.
## Gathering category definitions.
## The score column is null, defaulting to score.
## Possible columns are:
## [1] "category"                 "over_represented_pvalue" 
## [3] "under_represented_pvalue" "numDEInCat"              
## [5] "numInCat"                 "term"                    
## [7] "ontology"                 "qvalue"
## The score column is null, defaulting to score.
## Possible columns are:
## [1] "category"                 "over_represented_pvalue" 
## [3] "under_represented_pvalue" "numDEInCat"              
## [5] "numInCat"                 "term"                    
## [7] "ontology"                 "qvalue"
## The score column is null, defaulting to score.
## Possible columns are:
## [1] "category"                 "over_represented_pvalue" 
## [3] "under_represented_pvalue" "numDEInCat"              
## [5] "numInCat"                 "term"                    
## [7] "ontology"                 "qvalue"
## Deleting the file excel/trans_up_goseq-v20220630.xlsx before writing the tables.
## Writing a sheet containing the legend.
## Writing the BP data.
## Writing the MF data.
## Writing the CC data.
## Finished writing excel file.
trans_down_goseq <- simple_goseq(trans_downs, go_db=pa14_go, length_db=pa14_length,
                                 excel=glue::glue("excel/trans_down_goseq-v{ver}.xlsx"))
## Found 285 go_db genes and 285 length_db genes out of 777.
## Testing that go categories are defined.
## Removing undefined categories.
## Gathering synonyms.
## Gathering category definitions.
## The score column is null, defaulting to score.
## Possible columns are:
## [1] "category"                 "over_represented_pvalue" 
## [3] "under_represented_pvalue" "numDEInCat"              
## [5] "numInCat"                 "term"                    
## [7] "ontology"                 "qvalue"
## The score column is null, defaulting to score.
## Possible columns are:
## [1] "category"                 "over_represented_pvalue" 
## [3] "under_represented_pvalue" "numDEInCat"              
## [5] "numInCat"                 "term"                    
## [7] "ontology"                 "qvalue"
## The score column is null, defaulting to score.
## Possible columns are:
## [1] "category"                 "over_represented_pvalue" 
## [3] "under_represented_pvalue" "numDEInCat"              
## [5] "numInCat"                 "term"                    
## [7] "ontology"                 "qvalue"
## Deleting the file excel/trans_down_goseq-v20220630.xlsx before writing the tables.
## Writing a sheet containing the legend.
## Writing the BP data.
## Writing the MF data.
## Writing the CC data.
## Finished writing excel file.

8.3 Circos

As a final quick thing, lets plot the genes via circos.

pa14_annot[["chromosome"]] <- "Pseudomonas_aeruginosa_UCBPP_PA14"
rph_delta_df <- rph_tables[["data"]][["deletion_vs_wt"]][, c("deseq_logfc", "deseq_adjp")]
rph_trans_df <- rph_tables[["data"]][["trans_vs_wt"]][, c("deseq_logfc", "deseq_adjp")]

rph_cfg <- circos_prefix(pa14_annot, name="rph", cog_column = "COGFun",
                            start_column="start.x", end_column="end", strand_column="strand.x",
                            chr_column="chromosome", id_column="gene_id")
## This assumes you have a colors.conf in circos/colors/ and fonts.conf in circos/fonts/
## It also assumes you have conf/ideogram.conf, conf/ticks.conf, and conf/housekeeping.conf
## It will write circos/conf/rph.conf with a reasonable first approximation config file.
## Wrote karyotype to circos/conf/ideograms/rph.conf
## This should match the ideogram= line in rph.conf
## Wrote ticks to circos/conf/ticks_rph.conf
rph_kary <- circos_karyotype(rph_cfg, fasta="reference/paeruginosa_pa14.fasta")
## Wrote karyotype to circos/conf/karyotypes/rph.conf
## This should match the karyotype= line in rph.conf
rph_plus_minus <- circos_plus_minus(rph_cfg, width=0.06, thickness=40)
## Writing data file: circos/data/rph_plus_go.txt with the + strand GO data.
## Writing data file: circos/data/rph_minus_go.txt with the - strand GO data.
## Wrote the +/- config files.  Appending their inclusion to the master file.
## Returning the inner width: 0.88.  Use it as the outer for the next ring.
rph_delta_hist <- circos_hist(rph_cfg, rph_delta_df, colname="deseq_logfc",
                              basename="delta", outer=rph_plus_minus)
## Assuming the input is a dataframe.
## Writing data file: circos/data/rph_deltadeseq_logfc_hist.txt with the deltadeseq_logfc column.
## Returning the inner width: 0.8.  Use it as the outer for the next ring.
rph_tn_hist <- circos_hist(rph_cfg, rph_trans_df, colname="deseq_logfc",
                           basename="trans", outer=rph_delta_hist)
## Assuming the input is a dataframe.
## Writing data file: circos/data/rph_transdeseq_logfc_hist.txt with the transdeseq_logfc column.
## Returning the inner width: 0.72.  Use it as the outer for the next ring.
rph_finish <- circos_suffix(rph_cfg)
rph_made <- circos_make(rph_cfg, target="rph")

9 Examine the algU data

There are two media, an inducer, and multiple methods of reducing expression…

9.1 Initial visualization

alg_norm <- normalize_expt(alginate_expt, transform="log2",
                           convert="cpm", norm="quant", filter=TRUE)
## Removing 50 low-count genes (5929 remaining).
## transform_counts: Found 199 values equal to 0, adding 1 to the matrix.
plot_pca(alg_norm)$plot
## Warning: ggrepel: 1 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

I perceive a few different groups, which if broadly read from left to right include:

  1. synthetic sputum, neither IPTG nor Eb*. This is the most ‘divergent’ group of all.
  2. LB, neither IPTG nor Eb*. These are closest to the top-left SCFM samples.
  3. Adjacent to #2 are the LB+Eb0 and LB+Eb: E.g. the reduced LB samples.
  4. Below #3 are the induced SCFM samples on the left.
  5. Immediately to the right are the various SCFM samples with both induction and reduction (IPTG + Eb*).
  6. The final large group is all LB, the left most group is induced.
  7. The last clustered LB group on the farthest right is comprised of all the LB samples with both induction and reduction (IPTG + Eb*).

Given this, there are a few ways to compare the data. The following three groups describe the easily measurable comparisons in order of difference (thus #1 is a huge difference, #3 is small):

  1. There is a profound difference between LB and SCFM samples.
  2. In both of the media types, there is a palpable difference between the no-additives and everything else (IPTG and/or Eb*).
  3. Among the samples where the various additives were applied, there is a palpable difference between when IPTG was added vs. IPTG and Eb*.

9.2 Differential expression

I spoke with Theresa and we agreed that a likely good statistical model is: ~ media + induce:reduce which is to say we will attempt an interaction comparison of induce vs. reduce controlling for media.

For simplicity sake, I will also do the simple comparisons among the conditions as they are delineated in the above PCA plot.

9.2.1 All Pairwise

I will start with the simpler task:

interesting <- list(
    "scfm_lb" = c("SCFMnotinducednotreduced", "LBnotinducednotreduced"),
    "lb_lbinduce" = c("LBnotinducednotreduced", "LBIPTGnotreduced"),
    "scfm_scfminduce" = c("SCFMnotinducednotreduced", "SCFMIPTGnotreduced"),
    "lb_lbcompound" = c("LBnotinducednotreduced", "LBnotinducedEb"),
    "iptg_iptgeb" = c("LBIPTGnotreduced", "LBIPTGEb"),
    "iptg_iptgebo" = c("LBIPTGnotreduced", "LBIPTGEbO"),
    "iptg_iptgebs" = c("LBIPTGnotreduced", "LBIPTGEbS"),
    "iptgeb_iptgebo" = c("LBIPTGEb", "LBIPTGEbO"),
    "iptgeb_iptgebs" = c("LBIPTGEb", "LBIPTGEbS"),
    "scfmiptg_lbiptg" = c("SCFMIPTGnotreduced", "LBIPTGnotreduced"),
    "scfmdmso_scfmiptg" = c("SCFMnotinducednotreduced", "SCFMIPTGnotreduced"),
    "scfmiptg_scfmiptgeb" = c("SCFMIPTGnotreduced", "SCFMIPTGEb"),
    "scfmiptg_scfmiptgebo" = c("SCFMIPTGnotreduced", "SCFMIPTGEbO"),
    "scfmiptg_scfmiptgebs" = c("SCFMIPTGnotreduced", "SCFMIPTGEbS"))

alg_de_nobatch <- all_pairwise(alginate_expt, model_batch=FALSE)
## Finished running DE analyses, collecting outputs.
## Comparing analyses.

alg_nobatch_tables <- combine_de_tables(
    alg_de_nobatch, keepers=interesting,
    excel=glue::glue("excel/alg_tables_nobatch-v{ver}.xlsx"))
## Deleting the file excel/alg_tables_nobatch-v20220630.xlsx before writing the tables.
alg_nobatch_sig <- extract_significant_genes(
    alg_nobatch_tables,
    excel = glue::glue("excel/alg_sig_nobatch-v{ver}.xlsx"))
## Deleting the file excel/alg_sig_nobatch-v20220630.xlsx before writing the tables.
alg_de <- all_pairwise(alginate_expt, model_batch=TRUE)
## Using limma's removeBatchEffect to visualize with(out) batch inclusion.
## Finished running DE analyses, collecting outputs.
## Comparing analyses.

alg_tables <- combine_de_tables(
    alg_de, keepers=interesting,
    excel=glue::glue("excel/alg_tables_batch-v{ver}.xlsx"))
## Deleting the file excel/alg_tables_batch-v20220630.xlsx before writing the tables.
alg_sig <- extract_significant_genes(
    alg_tables,
    excel=glue::glue("excel/alg_sig_batch-v{ver}.xlsx"))
## Deleting the file excel/alg_sig_batch-v20220630.xlsx before writing the tables.

9.2.2 Interaction Model

There is an important caveat for comparing this data and thinking about an interaction model: In the LB, we have a full rank matrix of LB+nothing, LB+inducer, LB+reducers, and LB+inducer+reducers. In the SCFM, we do not, we have only SCFM+nothing, SCFM+inducer, SCFM+inducer+reducers – but no SCFM+reducers. As a result, we do not have the power to do some of the most interesting stuff, e.g. media+inducer:reducers.

If we take away the SCFM samples, we can do inducer:reducers, but that is much less exciting.

pander::pander(sessionInfo())
message(paste0("This is hpgltools commit: ", get_git_commit()))
this_save <- paste0(gsub(pattern="\\.Rmd", replace="", x=rmd_file), "-v", ver, ".rda.xz")
message(paste0("Saving to ", this_save))
tmp <- sm(saveme(filename=this_save))
LS0tCnRpdGxlOiAiU29tZSBQc2V1ZG9tb25hcyBSTkFzZXEgZGF0YTogU0suIgphdXRob3I6ICJhdGIgYWJlbGV3QGdtYWlsLmNvbSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZmlnX2NhcHRpb246IHRydWUKICAgIGZpZ19oZWlnaHQ6IDcKICAgIGZpZ193aWR0aDogNwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAga2VlcF9tZDogZmFsc2UKICAgIG1vZGU6IHNlbGZjb250YWluZWQKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgc2VsZl9jb250YWluZWQ6IHRydWUKICAgIHRoZW1lOiByZWFkYWJsZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UKICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICB3aWR0aDogMzAwCiAgICBrZWVwX21kOiBmYWxzZQogICAgbW9kZTogc2VsZmNvbnRhaW5lZAogICAgdG9jX2Zsb2F0OiB0cnVlCiAgQmlvY1N0eWxlOjpodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBrZWVwX21kOiBmYWxzZQogICAgbW9kZTogc2VsZmNvbnRhaW5lZAogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keSwgdGQgewogIGZvbnQtc2l6ZTogMTZweDsKfQpjb2RlLnJ7CiAgZm9udC1zaXplOiAxNnB4Owp9CnByZSB7CiBmb250LXNpemU6IDE2cHgKfQo8L3N0eWxlPgoKYGBge3Igb3B0aW9ucywgaW5jbHVkZT1GQUxTRX0KbGlicmFyeSgiaHBnbHRvb2xzIikKdHQgPC0gZGV2dG9vbHM6OmxvYWRfYWxsKCJ+L2hwZ2x0b29scyIpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHdpZHRoPTEyMCwKICAgICAgICAgICAgICAgICAgICAgcHJvZ3Jlc3M9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1UUlVFLAogICAgICAgICAgICAgICAgICAgICBlY2hvPVRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvcj1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgZHBpPTk2KQpvbGRfb3B0aW9ucyA8LSBvcHRpb25zKGRpZ2l0cz00LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAga25pdHIuZHVwbGljYXRlLmxhYmVsPSJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemU9MTApKQpydW5kYXRlIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCBmb3JtYXQ9IiVZJW0lZCIpCnByZXZpb3VzX2ZpbGUgPC0gIiIKdmVyIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCAiJVklbSVkIikKCiMjdG1wIDwtIHNtKGxvYWRtZShmaWxlbmFtZT1wYXN0ZTAoZ3N1YihwYXR0ZXJuPSJcXC5SbWQiLCByZXBsYWNlPSIiLCB4PXByZXZpb3VzX2ZpbGUpLCAiLXYiLCB2ZXIsICIucmRhLnh6IikpKQpybWRfZmlsZSA8LSAiaW5kZXhfc2tfMjAyMjA2LlJtZCIKYGBgCgojIEludHJvZHVjdGlvbgoKVGhpcyBkYXRhc2V0IGNvbnRhaW5zIGRhdGEgZnJvbSB0d28gZXhwZXJpbWVudHM6CgoxLiAgJ3JwaCc6IFRoaXMgYXBwZWFycyB0byBtZSB0byBiZSBhIHJlYXNvbmFibHkgc2ltcGxlIGNvbXBhcmlzb24gb2YKICAgIGEgJ25vcm1hbCcgUEExNCBsYWIgc3RyYWluIGFuZCB0d28gbWV0aG9kcyB0byBrbm9jayBvdXQgdGhlIHJwaAogICAgZ2VuZSByaWJvbnVjbGVhc2UgUEg7ICJQaG9zcGhvcm9seXRpYyAzJyB0byA1JyBleG9yaWJvbnVjbGVhc2UiCiAgICB3aGljaCBoZWxwcyB0Uk5BIDMnIGVuZCBtYXR1cmF0aW9uLiAgSXQgcmVtb3ZlcyByZXNpZHVlcyBhZnRlciB0aGUKICAgIENDQSBlbmQsIGFuZCBhbHNvIHNvbWV0aW1lcyBhZGRzIG51Y2xlb3RpZGVzIHVzaW5nIG51Y2xlb3NpZGUKICAgIGRpcGhvc3BoYXRlcyBhcyBpbnB1dC4gIFBvdGVudGlhbGx5IGhlbHBzIGRlZ3JhZGUgMTZTIHdoZW4KICAgIHN0YXJ2aW5nLgoyLiAgJ2FsZ2luYXRlJzogVGhpcyBpcyBhIHZlcnkgY29tcGxleCBleHBlcmltZW50IHdoaWNoIHVzZXMgYSBzaW5nbGUKICAgIHN0cmFpbiAoUEExNCB3aXRoIGEgcGxhc21pZCBjb250YWluaW5nIGFsZ1UpLiAgQWxnVSByZWd1bGF0ZXMgdGhlCiAgICByc21BIHByb21vdGVyIHdoaWNoIGlzIGltcG9ydGFudCBmb3IgdmlydWxlbmNlIGFuZCBoYXMgcXVpdGUgYSBsb3QKICAgIG9mIGxpdGVyYXR1cmUgc3Vycm91bmRpbmcgaXRzIGZ1bmN0aW9uLiAgSSBhbSBndWVzc2luZyB0aGlzIGlzIGEKICAgIGtub2Nrb3V0IHN0cmFpbiBmb3IgYWxnVT8gIEVpdGhlciB3YXksIHRoaXMgc3RyYWluIHdhcyBncm93bgogICAgZWl0aGVyIGluIExCIG9yIFNDRk0gKHN5bnRoZXRpYyBjeXN0aWMgZmlicm9zaXMgc3B1dHVtIG1lZGlhKSB3aXRoCiAgICBvbmUgb3IgbW9yZSBzdXBwbGVtZW50czogRE1TTyAoY29udHJvbCksIG9yIElQVEcgKGluZHVjZSksIGFuZC9vcgogICAgRWIvRWJTL0ViTyAocmVkdWNlcnMpLgoKIyBBbm5vdGF0aW9ucwoKSSBhbSBvbmx5IG5vdyBjaGVja2luZyBvdXQgcHNldWRvbW9uYXMuY29tLiAgQXQgZmlyc3QgZ2xhbmNlIEkgYW0gbm90CnNlZWluZyBhIGdvb2Qgd2F5IHRvIHByb2dyYW1tYXRpY2FsbHkgZG93bmxvYWQgdGhlIGFubm90YXRpb24gZGF0YSwKYnV0IHRoZXJlIGlzIGEgbmljZSBjYXRhbG9nIG9mIGZpbGVzIHRvIGRvd25sb2FkLiAgSW4gYWRkaXRpb24sIGl0CmFwcGVhcnMgdGhleSBkbyBhIGdvb2Qgam9iIG9mIGVuc3VyaW5nIHRoYXQgYWxsIG9mIHRoZSBzdHJhaW4KYW5ub3RhdGlvbnMgYXJlIGF0IE5DQkkgYXMgZ2VuYmFuayBmaWxlczsgc28gSSBzaG91bGQgbm90IGhhdmUKc2lnbmlmaWNhbnQgdHJvdWJsZXMgaWYgSSB3YW50IHRvIGdldCB0aGVzZSBhbm5vdGF0aW9ucy4KCldpdGggdGhhdCBpbiBtaW5kLCBJIGFtIHVzaW5nIHR3byBkYXRhIHNvdXJjZXMgZm9yIGFubm90YXRpb25zLCB0aGUKZ2ZmIGZpbGUgSSBleHRyYWN0ZWQgZnJvbSB0aGUgTkNCSSByZWZlcmVuY2UgZ2Vub21lIGFuZCBjb3BpZWQgdG8gdGhlCmxvY2FsICdyZWZlcmVuY2UvJyBkaXJlY3RvcnkgKHRodXMgSSBhc3N1bWUgaXQgaXMgaWRlbnRpY2FsIHRvIHdoYXQgaXMKYXQgcHNldWRvbW9uYXMuY29tKSwgYW5kIGFubm90YXRpb25zIGZyb20gbWljcm9iZXNvbmxpbmUub3JnLgoKYGBge3IgYW5ub3RhdGlvbn0KcGExNF9nZmYgPC0gbG9hZF9nZmZfYW5ub3RhdGlvbnMoInJlZmVyZW5jZS9wYWVydWdpbm9zYV9wYTE0LmdmZiIsIGlkX2NvbD0iZ2VuZV9pZCIpCnJvd25hbWVzKHBhMTRfZ2ZmKSA8LSBwYTE0X2dmZltbImdlbmVfaWQiXV0KIyMgVGhlIEFsaWFzIGNvbHVtbiBoYXMgUEExNF8wMDAxMAoKcGExNF9taWNyb2JlcyA8LSBhcy5kYXRhLmZyYW1lKGxvYWRfbWljcm9iZXNvbmxpbmVfYW5ub3RhdGlvbnMoIlBBMTQiKSkKIyMgVGhlIHN5c05hbWUgY29sdW1uIGhhcyBQQTE0XzAwMTAKCnBhMTRfYW5ub3QgPC0gbWVyZ2UocGExNF9nZmYsIHBhMTRfbWljcm9iZXMsIGJ5Lng9IkFsaWFzIiwgYnkueT0ic3lzTmFtZSIpCnJvd25hbWVzKHBhMTRfYW5ub3QpIDwtIHBhMTRfYW5ub3RbWyJnZW5lX2lkIl1dCgojIyBUaGUgaWRlbnRpZmllcnMgYXJlIGEgYml0IG9kZCwgc28gd2UgbmVlZCB0byBkbyBhIGxpdHRsZSB3b3JrCnBhMTRfbGVuZ3RoIDwtIHBhMTRfYW5ub3RbLCBjKCJnZW5lX2lkIiwgIndpZHRoIiwgIkFsaWFzIildCnBhMTRfZ28gPC0gbG9hZF9taWNyb2Jlc29ubGluZV9nbyhzcGVjaWVzPSJQQTE0IiwgaWRfY29sdW1uPSJzeXNOYW1lIikKcGExNF9nb19sZW5ndGggPC0gbWVyZ2UocGExNF9nbywgcGExNF9sZW5ndGgsIGJ5Lng9InN5c05hbWUiLCBieS55PSJBbGlhcyIpCnBhMTRfZ28gPC0gcGExNF9nb19sZW5ndGhbLCBjKCJzeXNOYW1lIiwgIkdPIildCmNvbG5hbWVzKHBhMTRfZ28pIDwtIGMoIklEIiwgIkdPIikKcGExNF9sZW5ndGggPC0gcGExNF9nb19sZW5ndGhbLCBjKCJzeXNOYW1lIiwgIndpZHRoIildCiMjcGExNF9sZW5ndGhfaWRzIDwtIHVuaXF1ZShwYTE0X2xlbmd0aClbWyJnZW5lX2lkIl1dCiMjcGExNF9sZW5ndGggPC0gcGExNF9sZW5ndGhbcGExNF9sZW5ndGhfaWRzLCBdCnJvd25hbWVzKHBhMTRfbGVuZ3RoKSA8LSBtYWtlLm5hbWVzKHBhMTRfbGVuZ3RoW1sxXV0sIHVuaXF1ZT1UUlVFKQpjb2xuYW1lcyhwYTE0X2xlbmd0aCkgPC0gYygiSUQiLCAid2lkdGgiKQpgYGAKCiMgTWFrZSB0aGUgZXhwcmVzc2lvbnNldAoKUHJldmlvdXNseSwgSSBsb2FkZWQgYWxsIHNhbXBsZXMgZnJvbSBib3RoIGdyb3VwcyBvZiBleHBlcmltZW50cwp0b2dldGhlciBhbmQgc2VwYXJhdGVkIHRoZW0uICBJIGRlY2lkZWQgaW4gdGhpcyBpdGVyYXRpb24gdG8gc2VwYXJhdGUKdGhlbSBpbiB0aGUgaG9wZXMgb2YgYXZvaWRpbmcgYW55IHBvdGVudGlhbCBjb25mdXNpb24uCgpUaGUgZm9sbG93aW5nIGxpbmUgcmVhZHMgaW4gdGhlIHNhbXBsZSBzaGVldCBhbmQgY3JlYXRlcyBhIGRhdGEKc3RydWN0dXJlIHdoaWNoIGNvbnRhaW5zIGFsbCBvZiB0aGUgc2FtcGxlIGFubm90YXRpb25zLCB0aGUgZ2VuZQphbm5vdGF0aW9ucywgYW5kIHRoZSBjb3VudHMvZ2VuZS4KCmBgYHtyIGV4cHJlc3Npb25zZXR9CnNrX2V4cHQgPC0gY3JlYXRlX2V4cHQoInNhbXBsZV9zaGVldHMvYWxsX3NhbXBsZXNfc2tfMjAyMjA2Lnhsc3giLAogICAgICAgICAgICAgICAgICAgICAgIGdlbmVfaW5mbz1wYTE0X2Fubm90LAogICAgICAgICAgICAgICAgICAgICAgIGZpbGVfY29sdW1uPSJoaXNhdGNvdW50dGFibGVyZXZlcnNlIikKYGBgCgojIFF1aWNrIHZpZXcgb2YgdGhlIGRhdGEKClRoZSBmaXJzdCB2aWV3IG9mIHRoZSBkYXRhIGlzIHRvIHNlZSBob3cgbWFueSBjb3VudHMvc2FtcGxlIHdlcmUKb2JzZXJ2ZWQgYW5kIHRvIGdldCBhIHNlbnNlIG9mIGhvdyBtYW55IGdlbmVzIHdlcmUgb2JzZXJ2ZWQ6CgpUaGUgcmFuZ2Ugb2YgcmVhZHMvc2FtcGxlIGdvZXMgZnJvbSB+IDMuMSB0byA1LjkgbWlsbGlvbiwgd2hpY2ggc2VlbXMKcXVpdGUgcmVhc29uYWJsZSB0byBtZS4KClRoZSByYW5nZSBvZiBnZW5lcyBvYnNlcnZlZCBnb2VzIGZyb20gfiA1LDkyNSB0byA1LDk4MC4gIFRoaXMgaXMgYQpiZWF1dGlmdWxseSB0aWdodCByYW5nZS4KCmBgYHtyIG1ldHJpY3N9CnBhMTRfbGlic2l6ZSA8LSBwbG90X2xpYnNpemUoc2tfZXhwdCkKcGExNF9saWJzaXplJHBsb3QKCnBhMTRfbm9uemVybyA8LSBwbG90X25vbnplcm8oc2tfZXhwdCkKcGExNF9ub256ZXJvJHBsb3QKYGBgCgojIFNlcGFyYXRlIHRoZSBleHBlcmltZW50cyBhbmQgd3JpdGUgb3V0IHRoZSBkYXRhCgpJIHN1c3BlY3QgdGhhdCBTb29LeW91bmcgbWlnaHQgZW5qb3kgcG9raW5nIGF0IHRoZSBjb3VudHMgaW4gZXhjZWwuCkJlZm9yZSB3cml0aW5nIGl0IG91dCB0aG91Z2ggbGV0IHVzIHNldCB0aGUgY29uZGl0aW9ucyBhbmQgYmF0Y2hlcyBzbwp0aGUgZXhjZWwgb3V0cHV0IGhhcyBzb21lIHByZXR0eSBjb2xvcnMuCgpgYGB7ciB3cml0ZV9leHB0fQpza19leHB0IDwtIHNldF9leHB0X2NvbmRpdGlvbnMoc2tfZXhwdCwgZmFjdD0ibWVkaWEiKSAlPiUKICBzZXRfZXhwdF9iYXRjaGVzKGZhY3Q9ImJpb3JlcGxpY2F0ZSIpCgpycGhfZXhwdCA8LSBzdWJzZXRfZXhwdChza19leHB0LCBzdWJzZXQ9Im1lZGlhPT0nTEInIikgJT4lCiAgc2V0X2V4cHRfY29uZGl0aW9ucyhmYWN0PSJzdHJhaW5zIikgJT4lCiAgc2FuaXRpemVfZXhwdF9tZXRhZGF0YSgpCgpycGhfd3JpdHRlbiA8LSB3cml0ZV9leHB0KHJwaF9leHB0LAogICAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VsPWdsdWU6OmdsdWUoImV4Y2VsL3JwaF9kYXRhLXZ7dmVyfS54bHN4IikpCmBgYAoKIyBBbGdpbmF0ZSBpbmR1Y2UgdnMuIHJlZHVjZQoKQmVmb3JlIHdyaXRpbmcgb3V0IHRoZSBhbGdpbmF0ZSBkYXRhLCBsZXQgdXMgdGFrZSBhIG1vbWVudCB0byBzZXBhcmF0ZQp0aGUgZmFjdG9ycyBmb3IgaW5kdWNpbmcgYW5kIHJlZHVjaW5nIGV4cHJlc3Npb24KCmBgYHtyIGFsZ2luYXRlX3JlZHVjZV9pbmR1Y2V9CmFsZ2luYXRlX2V4cHQgPC0gc3Vic2V0X2V4cHQoc2tfZXhwdCwgc3Vic2V0PSJtZWRpYSE9J0xCJyIpICU+JQogIHNldF9leHB0X2NvbmRpdGlvbnMoZmFjdD0ibWVkaWEiKQoKY29uZGl0aW9ucyA8LSBwRGF0YShhbGdpbmF0ZV9leHB0KVtbImNvbmRpdGlvbiJdXQppbmR1Y2VfYm9vbCA8LSBncmVwbChwYXR0ZXJuPSJJUFRHIiwgeD1jb25kaXRpb25zKQppbmR1Y2VfZmFjdG9yIDwtIHJlcCgibm90X2luZHVjZWQiLCBsZW5ndGgoaW5kdWNlX2Jvb2wpKQppbmR1Y2VfZmFjdG9yW2luZHVjZV9ib29sXSA8LSAiSVBURyIKCnJlZHVjZV9ib29sIDwtIGdyZXBsKHBhdHRlcm49IkViIiwgeD1jb25kaXRpb25zKQpyZWR1Y2VfZmFjdG9yIDwtIHJlcCgibm90X3JlZHVjZWQiLCBsZW5ndGgocmVkdWNlX2Jvb2wpKQplYl9zYW1wbGVzIDwtICBncmVwbChwYXR0ZXJuPSJFYiIsIHg9Y29uZGl0aW9ucykKcmVkdWNlX2ZhY3RvcltlYl9zYW1wbGVzXSA8LSAiRWIiCmVib19zYW1wbGVzIDwtIGdyZXBsKHBhdHRlcm49IkViTyIsIHg9Y29uZGl0aW9ucykKcmVkdWNlX2ZhY3RvcltlYm9fc2FtcGxlc10gPC0gIkViTyIKZWJzX3NhbXBsZXMgPC0gZ3JlcGwocGF0dGVybj0iRWJTIiwgeD1jb25kaXRpb25zKQpyZWR1Y2VfZmFjdG9yW2Vic19zYW1wbGVzXSA8LSAiRWJTIgoKbWVkaWFfZmFjdG9yIDwtIGNvbmRpdGlvbnMKbGJfYm9vbCA8LSBncmVwbChwYXR0ZXJuPSJMQiIsIHg9Y29uZGl0aW9ucykKbWVkaWFfZmFjdG9yW2xiX2Jvb2xdIDwtICJMQiIKbWVkaWFfZmFjdG9yWyFsYl9ib29sXSA8LSAiU0NGTSIKCnBEYXRhKGFsZ2luYXRlX2V4cHQpW1sibWVkaWFfYWRkIl1dIDwtIHBEYXRhKGFsZ2luYXRlX2V4cHQpW1sibWVkaWEiXV0KcERhdGEoYWxnaW5hdGVfZXhwdClbWyJtZWRpYSJdXSA8LSBtZWRpYV9mYWN0b3IKcERhdGEoYWxnaW5hdGVfZXhwdClbWyJpbmR1Y2VkIl1dIDwtIGFzLmZhY3RvcihpbmR1Y2VfZmFjdG9yKQpwRGF0YShhbGdpbmF0ZV9leHB0KVtbInJlZHVjZWQiXV0gPC0gYXMuZmFjdG9yKHJlZHVjZV9mYWN0b3IpCgppbmR1Y2VfcmVkdWNlX2ZhY3RvciA8LSBwYXN0ZTAoaW5kdWNlX2ZhY3RvciwgIl8iLCByZWR1Y2VfZmFjdG9yKQpwRGF0YShhbGdpbmF0ZV9leHB0KVtbImluZHVjZV9yZWR1Y2UiXV0gPC0gaW5kdWNlX3JlZHVjZV9mYWN0b3IKCm1lZGlhX2luZHVjZV9yZWR1Y2VfZmFjdG9yIDwtIHBhc3RlMChtZWRpYV9mYWN0b3IsICJfIiwgaW5kdWNlX3JlZHVjZV9mYWN0b3IpCgphbGdpbmF0ZV9leHB0IDwtIHNldF9leHB0X2NvbmRpdGlvbnMoYWxnaW5hdGVfZXhwdCwgZmFjdD1tZWRpYV9pbmR1Y2VfcmVkdWNlX2ZhY3RvcikgJT4lCiAgc2V0X2V4cHRfYmF0Y2hlcyhmYWN0PSJiaW9yZXBsaWNhdGUiKQoKcmVkdWNlX2Jvb2wgPC0gcmVkdWNlX2ZhY3RvcgpyZWR1Y2VfaWR4IDwtIGdyZXBsKHBhdHRlcm49IkViIiwgeD1yZWR1Y2VfYm9vbCkKcmVkdWNlX2Jvb2xbcmVkdWNlX2lkeF0gPC0gInJlZHVjZWQiCnBEYXRhKGFsZ2luYXRlX2V4cHQpW1sicmVkdWNlZF9ib29sIl1dIDwtIHJlZHVjZV9ib29sCmBgYAoKIyBXcml0ZSB0aGUgbmV3IGFsZ2luYXRlIGRhdGEKCk5vdyB3ZSBoYXZlIHJld3JpdHRlbiB0aGUgYWxnaW5hdGUgbWV0YWRhdGEgdG8gaGF2ZSBzZXBhcmF0ZSBmYWN0b3JzCmZvciBpZi93aGVuIHNhbXBsZXMgd2VyZSBpbmR1Y2VkIHZpYSBJUFRHIGFuZCBpZi93aGVuIHRoZSB3ZXJlIHJlZHVjZWQKdmlhIEViKiwgYW5kIHRoZSBtZWRpYSBmYWN0b3IgaXMgb25seSBMYiB2cy4gU0NGTS4KCmBgYHtyIHdyaXRlX2FsZ2luYXRlfQphbGdpbmF0ZV93cml0dGVuIDwtIHdyaXRlX2V4cHQoYWxnaW5hdGVfZXhwdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VsPWdsdWU6OmdsdWUoImV4Y2VsL2FsZ2luYXRlX2RhdGEtdnt2ZXJ9Lnhsc3giKSkKYGBgCgojIHJwaCBkYXRhCgpMZXRzIHRha2UgYSBtb21lbnQgdG8gbG9vayBhdCBhbmQgcGVyZm9ybSBzb21lIHNpbXBsZSBhbmFseXNlcyBvZiB0aGUKcnBoIGRhdGEgc2V0IGZpcnN0LiAgT25lIHRoaW5nIHRvIG5vdGUsIG15IHNhbml0aXplciBmdW5jdGlvbiByZW1vdmVkCnRoZSBkZWx0YSBmcm9tIHRoZSBjb25kaXRpb24sIHBlcmhhcHMgSSBzaG91bGQgaGF2ZSBpdCByZXBsYWNlIGRlbHRhCndpdGggJ2QnIG9yIHNvbWV0aGluZz8gIEluIGFueSBldmVudCwgdGhlIGRlbGV0aW9uIHN0cmFpbiBpcyBtb3JlCnNpbWlsYXIgdG8gd2lsZC10eXBlIHRoYW4gdGhlIHRyYW5zcG9zb24gbXV0YW50IC0tIGJ5IGEgbG90LgoKYGBge3IgcnBofQpycGhfbm9ybSA8LSBub3JtYWxpemVfZXhwdChycGhfZXhwdCwgdHJhbnNmb3JtPSJsb2cyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udmVydD0iY3BtIiwgbm9ybT0icXVhbnQiLCBmaWx0ZXI9VFJVRSkKcGxvdF9wY2EocnBoX25vcm0pJHBsb3QKCnBsb3RfYm94cGxvdChycGhfZXhwdCkKYGBgCgojIyBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbgoKVGhlIGRhdGEgaXMgY2xlYXIgZW5vdWdoIHRoYXQgSSBkbyBub3QgdGhpbmsgaXQgd2FycmFudHMgYW55IGludHJ1c2l2ZQp0aGluZ3MgbGlrZSBzdmEsIEkgd2lsbCBqdXN0IGluY2x1ZGUgYmlvcmVwbGljYXRlIGluIHRoZSBzdGF0aXN0aWNhbAptb2RlbC4KCmBgYHtyIHJwaF9kZX0KcnBoX2RlIDwtIGFsbF9wYWlyd2lzZShycGhfZXhwdCwgbW9kZWxfYmF0Y2g9VFJVRSwgZmlsdGVyPVRSVUUpCiMjIEkgc3VzcGVjdCB0aGF0IG9ubHkgdGhlIGZpcnN0IHR3byBjb250cmFzdHMgd2lsbCBiZSBvZiBzaWduaWZpY2FudCBpbnRlcmVzdC4KaW50ZXJlc3RpbmcgPC0gbGlzdCgKICAgICJkZWxldGlvbl92c193dCIgPSBjKCJwYTE0cnBoIiwgInBhMTR3dCIpLAogICAgInRyYW5zX3ZzX3d0IiA9IGMoInBhMTR0bnJwaCIsICJwYTE0d3QiKSwKICAgICJkZWxldGlvbl92c190cmFucyIgPSBjKCJwYTE0cnBoIiwgInBhMTR0bnJwaCIpKQpycGhfdGFibGVzIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogICAgcnBoX2RlLCBrZWVwZXJzPWludGVyZXN0aW5nLAogICAgZXhjZWw9Z2x1ZTo6Z2x1ZSgiZXhjZWwvcnBoX3RhYmxlcy12e3Zlcn0ueGxzeCIpKQpycGhfc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgICBycGhfdGFibGVzLAogICAgZXhjZWw9Z2x1ZTo6Z2x1ZSgiZXhjZWwvcnBoX3NpZy12e3Zlcn0ueGxzeCIpKQpgYGAKCiMjIE9udG9sb2d5CgpMZXQgdXMgc2VlIGlmIHRoZXJlIGFyZSBzb21lIGdyb3VwcyBvZiBnZW5lcyBvZiBpbnRlcmVzdCBhcyBpZGVudGlmaWVkCmJ5IEdPLgoKYGBge3IgZ29fcnBofQpkZWxldGlvbl91cHMgPC0gcnBoX3NpZ1tbImRlc2VxIl1dW1sidXBzIl1dW1sxXV0Kcm93bmFtZXMoZGVsZXRpb25fdXBzKSA8LSBtYWtlLm5hbWVzKGRlbGV0aW9uX3Vwc1tbIm5hbWV4Il1dLCB1bmlxdWU9VFJVRSkKZGVsZXRpb25fZG93bnMgPC0gcnBoX3NpZ1tbImRlc2VxIl1dW1siZG93bnMiXV1bWzFdXQpyb3duYW1lcyhkZWxldGlvbl9kb3ducykgPC0gbWFrZS5uYW1lcyhkZWxldGlvbl9kb3duc1tbIm5hbWV4Il1dLCB1bmlxdWU9VFJVRSkKCnRyYW5zX3VwcyA8LSBycGhfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWzJdXQpyb3duYW1lcyh0cmFuc191cHMpIDwtIG1ha2UubmFtZXModHJhbnNfdXBzW1sibmFtZXgiXV0sIHVuaXF1ZT1UUlVFKQp0cmFuc19kb3ducyA8LSBycGhfc2lnW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbMl1dCnJvd25hbWVzKHRyYW5zX2Rvd25zKSA8LSBtYWtlLm5hbWVzKHRyYW5zX2Rvd25zW1sibmFtZXgiXV0sIHVuaXF1ZT1UUlVFKQoKIyMgVGhlIGdvIGRhdGEgZnJvbSBtaWNyb2Jlc29ubGluZSBpcyBrZXllZCBieSB0aGUgZ2VuZSBuYW1lCiMjIChlLmcuIGRuYUEpLCBub3QgZ2VuZSBJRCBvciBQQSBpZCBvciB3aGF0ZXZlci4KCmRlbGV0aW9uX3VwX2dvc2VxIDwtIHNpbXBsZV9nb3NlcShkZWxldGlvbl91cHMsIGdvX2RiPXBhMTRfZ28sIGxlbmd0aF9kYj1wYTE0X2xlbmd0aCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VsPWdsdWU6OmdsdWUoImV4Y2VsL2RlbGV0aW9uX3VwX2dvc2VxLXZ7dmVyfS54bHN4IikpCmRlbGV0aW9uX3VwX2dvc2VxW1sicHZhbHVlX3Bsb3RzIl1dW1siYnBwX3Bsb3Rfb3ZlciJdXQpkZWxldGlvbl91cF9nb3NlcVtbInB2YWx1ZV9wbG90cyJdXVtbIm1mcF9wbG90X292ZXIiXV0KCmRlbGV0aW9uX2Rvd25fZ29zZXEgPC0gc2ltcGxlX2dvc2VxKGRlbGV0aW9uX2Rvd25zLCBnb19kYj1wYTE0X2dvLCBsZW5ndGhfZGI9cGExNF9sZW5ndGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VsPWdsdWU6OmdsdWUoImV4Y2VsL2RlbGV0aW9uX2Rvd25fZ29zZXEtdnt2ZXJ9Lnhsc3giKSkKZGVsZXRpb25fZG93bl9nb3NlcVtbInB2YWx1ZV9wbG90cyJdXVtbImJwcF9wbG90X292ZXIiXV0KZGVsZXRpb25fZG93bl9nb3NlcVtbInB2YWx1ZV9wbG90cyJdXVtbIm1mcF9wbG90X292ZXIiXV0KCnRyYW5zX3VwX2dvc2VxIDwtIHNpbXBsZV9nb3NlcSh0cmFuc191cHMsIGdvX2RiPXBhMTRfZ28sIGxlbmd0aF9kYj1wYTE0X2xlbmd0aCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VsPWdsdWU6OmdsdWUoImV4Y2VsL3RyYW5zX3VwX2dvc2VxLXZ7dmVyfS54bHN4IikpCnRyYW5zX2Rvd25fZ29zZXEgPC0gc2ltcGxlX2dvc2VxKHRyYW5zX2Rvd25zLCBnb19kYj1wYTE0X2dvLCBsZW5ndGhfZGI9cGExNF9sZW5ndGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VsPWdsdWU6OmdsdWUoImV4Y2VsL3RyYW5zX2Rvd25fZ29zZXEtdnt2ZXJ9Lnhsc3giKSkKYGBgCgojIyBDaXJjb3MKCkFzIGEgZmluYWwgcXVpY2sgdGhpbmcsIGxldHMgcGxvdCB0aGUgZ2VuZXMgdmlhIGNpcmNvcy4KCmBgYHtyIGNpcmNvc19mdW59CnBhMTRfYW5ub3RbWyJjaHJvbW9zb21lIl1dIDwtICJQc2V1ZG9tb25hc19hZXJ1Z2lub3NhX1VDQlBQX1BBMTQiCnJwaF9kZWx0YV9kZiA8LSBycGhfdGFibGVzW1siZGF0YSJdXVtbImRlbGV0aW9uX3ZzX3d0Il1dWywgYygiZGVzZXFfbG9nZmMiLCAiZGVzZXFfYWRqcCIpXQpycGhfdHJhbnNfZGYgPC0gcnBoX3RhYmxlc1tbImRhdGEiXV1bWyJ0cmFuc192c193dCJdXVssIGMoImRlc2VxX2xvZ2ZjIiwgImRlc2VxX2FkanAiKV0KCnJwaF9jZmcgPC0gY2lyY29zX3ByZWZpeChwYTE0X2Fubm90LCBuYW1lPSJycGgiLCBjb2dfY29sdW1uID0gIkNPR0Z1biIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydF9jb2x1bW49InN0YXJ0LngiLCBlbmRfY29sdW1uPSJlbmQiLCBzdHJhbmRfY29sdW1uPSJzdHJhbmQueCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaHJfY29sdW1uPSJjaHJvbW9zb21lIiwgaWRfY29sdW1uPSJnZW5lX2lkIikKcnBoX2thcnkgPC0gY2lyY29zX2thcnlvdHlwZShycGhfY2ZnLCBmYXN0YT0icmVmZXJlbmNlL3BhZXJ1Z2lub3NhX3BhMTQuZmFzdGEiKQpycGhfcGx1c19taW51cyA8LSBjaXJjb3NfcGx1c19taW51cyhycGhfY2ZnLCB3aWR0aD0wLjA2LCB0aGlja25lc3M9NDApCnJwaF9kZWx0YV9oaXN0IDwtIGNpcmNvc19oaXN0KHJwaF9jZmcsIHJwaF9kZWx0YV9kZiwgY29sbmFtZT0iZGVzZXFfbG9nZmMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYXNlbmFtZT0iZGVsdGEiLCBvdXRlcj1ycGhfcGx1c19taW51cykKcnBoX3RuX2hpc3QgPC0gY2lyY29zX2hpc3QocnBoX2NmZywgcnBoX3RyYW5zX2RmLCBjb2xuYW1lPSJkZXNlcV9sb2dmYyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhc2VuYW1lPSJ0cmFucyIsIG91dGVyPXJwaF9kZWx0YV9oaXN0KQpycGhfZmluaXNoIDwtIGNpcmNvc19zdWZmaXgocnBoX2NmZykKcnBoX21hZGUgPC0gY2lyY29zX21ha2UocnBoX2NmZywgdGFyZ2V0PSJycGgiKQpgYGAKCiMgRXhhbWluZSB0aGUgYWxnVSBkYXRhCgpUaGVyZSBhcmUgdHdvIG1lZGlhLCBhbiBpbmR1Y2VyLCBhbmQgbXVsdGlwbGUgbWV0aG9kcyBvZiByZWR1Y2luZyBleHByZXNzaW9uLi4uCgojIyBJbml0aWFsIHZpc3VhbGl6YXRpb24KCmBgYHtyIGFsZ2luYXRlMDJ9CmFsZ19ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KGFsZ2luYXRlX2V4cHQsIHRyYW5zZm9ybT0ibG9nMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnZlcnQ9ImNwbSIsIG5vcm09InF1YW50IiwgZmlsdGVyPVRSVUUpCnBsb3RfcGNhKGFsZ19ub3JtKSRwbG90CmBgYAoKSSBwZXJjZWl2ZSBhIGZldyBkaWZmZXJlbnQgZ3JvdXBzLCB3aGljaCBpZiBicm9hZGx5IHJlYWQgZnJvbSBsZWZ0IHRvCnJpZ2h0IGluY2x1ZGU6CgoxLiAgc3ludGhldGljIHNwdXR1bSwgbmVpdGhlciBJUFRHIG5vciBFYiouICBUaGlzIGlzIHRoZSBtb3N0CiAgICAnZGl2ZXJnZW50JyBncm91cCBvZiBhbGwuCjIuICBMQiwgbmVpdGhlciBJUFRHIG5vciBFYiouICBUaGVzZSBhcmUgY2xvc2VzdCB0byB0aGUgdG9wLWxlZnQgU0NGTQogICAgc2FtcGxlcy4KMy4gIEFkamFjZW50IHRvICMyIGFyZSB0aGUgTEIrRWIwIGFuZCBMQitFYjogIEUuZy4gdGhlIHJlZHVjZWQgTEIKICAgIHNhbXBsZXMuCjQuICBCZWxvdyAjMyBhcmUgdGhlIGluZHVjZWQgU0NGTSBzYW1wbGVzIG9uIHRoZSBsZWZ0Lgo1LiAgSW1tZWRpYXRlbHkgdG8gdGhlIHJpZ2h0IGFyZSB0aGUgdmFyaW91cyBTQ0ZNIHNhbXBsZXMgd2l0aCBib3RoCiAgICBpbmR1Y3Rpb24gYW5kIHJlZHVjdGlvbiAoSVBURyArIEViKikuCjYuICBUaGUgZmluYWwgbGFyZ2UgZ3JvdXAgaXMgYWxsIExCLCB0aGUgbGVmdCBtb3N0IGdyb3VwIGlzIGluZHVjZWQuCjcuICBUaGUgbGFzdCBjbHVzdGVyZWQgTEIgZ3JvdXAgb24gdGhlIGZhcnRoZXN0IHJpZ2h0IGlzIGNvbXByaXNlZCBvZgogICAgYWxsIHRoZSBMQiBzYW1wbGVzIHdpdGggYm90aCBpbmR1Y3Rpb24gYW5kIHJlZHVjdGlvbiAoSVBURyArIEViKikuCgpHaXZlbiB0aGlzLCB0aGVyZSBhcmUgYSBmZXcgd2F5cyB0byBjb21wYXJlIHRoZSBkYXRhLiAgVGhlIGZvbGxvd2luZwp0aHJlZSBncm91cHMgZGVzY3JpYmUgdGhlIGVhc2lseSBtZWFzdXJhYmxlIGNvbXBhcmlzb25zIGluIG9yZGVyIG9mCmRpZmZlcmVuY2UgKHRodXMgIzEgaXMgYSBodWdlIGRpZmZlcmVuY2UsICMzIGlzIHNtYWxsKToKCjEuICBUaGVyZSBpcyBhIHByb2ZvdW5kIGRpZmZlcmVuY2UgYmV0d2VlbiBMQiBhbmQgU0NGTSBzYW1wbGVzLgoyLiAgSW4gYm90aCBvZiB0aGUgbWVkaWEgdHlwZXMsIHRoZXJlIGlzIGEgcGFscGFibGUgZGlmZmVyZW5jZSBiZXR3ZWVuCiAgICB0aGUgbm8tYWRkaXRpdmVzIGFuZCBldmVyeXRoaW5nIGVsc2UgKElQVEcgYW5kL29yIEViKikuCjMuICBBbW9uZyB0aGUgc2FtcGxlcyB3aGVyZSB0aGUgdmFyaW91cyBhZGRpdGl2ZXMgd2VyZSBhcHBsaWVkLCB0aGVyZQogICAgaXMgYSBwYWxwYWJsZSBkaWZmZXJlbmNlIGJldHdlZW4gd2hlbiBJUFRHIHdhcyBhZGRlZCB2cy4gSVBURwogICAgX2FuZF8gRWIqLgoKIyMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24KCkkgc3Bva2Ugd2l0aCBUaGVyZXNhIGFuZCB3ZSBhZ3JlZWQgdGhhdCBhIGxpa2VseSBnb29kIHN0YXRpc3RpY2FsCm1vZGVsIGlzOiB+IG1lZGlhICsgaW5kdWNlOnJlZHVjZSB3aGljaCBpcyB0byBzYXkgd2Ugd2lsbCBhdHRlbXB0IGFuCmludGVyYWN0aW9uIGNvbXBhcmlzb24gb2YgaW5kdWNlIHZzLiByZWR1Y2UgY29udHJvbGxpbmcgZm9yIG1lZGlhLgoKRm9yIHNpbXBsaWNpdHkgc2FrZSwgSSB3aWxsIGFsc28gZG8gdGhlIHNpbXBsZSBjb21wYXJpc29ucyBhbW9uZyB0aGUKY29uZGl0aW9ucyBhcyB0aGV5IGFyZSBkZWxpbmVhdGVkIGluIHRoZSBhYm92ZSBQQ0EgcGxvdC4KCiMjIyBBbGwgUGFpcndpc2UKCkkgd2lsbCBzdGFydCB3aXRoIHRoZSBzaW1wbGVyIHRhc2s6CgpgYGB7ciBzaW1wbGVfZGV9CmludGVyZXN0aW5nIDwtIGxpc3QoCiAgICAic2NmbV9sYiIgPSBjKCJTQ0ZNbm90aW5kdWNlZG5vdHJlZHVjZWQiLCAiTEJub3RpbmR1Y2Vkbm90cmVkdWNlZCIpLAogICAgImxiX2xiaW5kdWNlIiA9IGMoIkxCbm90aW5kdWNlZG5vdHJlZHVjZWQiLCAiTEJJUFRHbm90cmVkdWNlZCIpLAogICAgInNjZm1fc2NmbWluZHVjZSIgPSBjKCJTQ0ZNbm90aW5kdWNlZG5vdHJlZHVjZWQiLCAiU0NGTUlQVEdub3RyZWR1Y2VkIiksCiAgICAibGJfbGJjb21wb3VuZCIgPSBjKCJMQm5vdGluZHVjZWRub3RyZWR1Y2VkIiwgIkxCbm90aW5kdWNlZEViIiksCiAgICAiaXB0Z19pcHRnZWIiID0gYygiTEJJUFRHbm90cmVkdWNlZCIsICJMQklQVEdFYiIpLAogICAgImlwdGdfaXB0Z2VibyIgPSBjKCJMQklQVEdub3RyZWR1Y2VkIiwgIkxCSVBUR0ViTyIpLAogICAgImlwdGdfaXB0Z2VicyIgPSBjKCJMQklQVEdub3RyZWR1Y2VkIiwgIkxCSVBUR0ViUyIpLAogICAgImlwdGdlYl9pcHRnZWJvIiA9IGMoIkxCSVBUR0ViIiwgIkxCSVBUR0ViTyIpLAogICAgImlwdGdlYl9pcHRnZWJzIiA9IGMoIkxCSVBUR0ViIiwgIkxCSVBUR0ViUyIpLAogICAgInNjZm1pcHRnX2xiaXB0ZyIgPSBjKCJTQ0ZNSVBUR25vdHJlZHVjZWQiLCAiTEJJUFRHbm90cmVkdWNlZCIpLAogICAgInNjZm1kbXNvX3NjZm1pcHRnIiA9IGMoIlNDRk1ub3RpbmR1Y2Vkbm90cmVkdWNlZCIsICJTQ0ZNSVBUR25vdHJlZHVjZWQiKSwKICAgICJzY2ZtaXB0Z19zY2ZtaXB0Z2ViIiA9IGMoIlNDRk1JUFRHbm90cmVkdWNlZCIsICJTQ0ZNSVBUR0ViIiksCiAgICAic2NmbWlwdGdfc2NmbWlwdGdlYm8iID0gYygiU0NGTUlQVEdub3RyZWR1Y2VkIiwgIlNDRk1JUFRHRWJPIiksCiAgICAic2NmbWlwdGdfc2NmbWlwdGdlYnMiID0gYygiU0NGTUlQVEdub3RyZWR1Y2VkIiwgIlNDRk1JUFRHRWJTIikpCgphbGdfZGVfbm9iYXRjaCA8LSBhbGxfcGFpcndpc2UoYWxnaW5hdGVfZXhwdCwgbW9kZWxfYmF0Y2g9RkFMU0UpCgphbGdfbm9iYXRjaF90YWJsZXMgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgICBhbGdfZGVfbm9iYXRjaCwga2VlcGVycz1pbnRlcmVzdGluZywKICAgIGV4Y2VsPWdsdWU6OmdsdWUoImV4Y2VsL2FsZ190YWJsZXNfbm9iYXRjaC12e3Zlcn0ueGxzeCIpKQphbGdfbm9iYXRjaF9zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICAgIGFsZ19ub2JhdGNoX3RhYmxlcywKICAgIGV4Y2VsID0gZ2x1ZTo6Z2x1ZSgiZXhjZWwvYWxnX3NpZ19ub2JhdGNoLXZ7dmVyfS54bHN4IikpCgphbGdfZGUgPC0gYWxsX3BhaXJ3aXNlKGFsZ2luYXRlX2V4cHQsIG1vZGVsX2JhdGNoPVRSVUUpCmFsZ190YWJsZXMgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgICBhbGdfZGUsIGtlZXBlcnM9aW50ZXJlc3RpbmcsCiAgICBleGNlbD1nbHVlOjpnbHVlKCJleGNlbC9hbGdfdGFibGVzX2JhdGNoLXZ7dmVyfS54bHN4IikpCmFsZ19zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICAgIGFsZ190YWJsZXMsCiAgICBleGNlbD1nbHVlOjpnbHVlKCJleGNlbC9hbGdfc2lnX2JhdGNoLXZ7dmVyfS54bHN4IikpCmBgYAoKIyMjIEludGVyYWN0aW9uIE1vZGVsCgpUaGVyZSBpcyBhbiBpbXBvcnRhbnQgY2F2ZWF0IGZvciBjb21wYXJpbmcgdGhpcyBkYXRhIGFuZCB0aGlua2luZwphYm91dCBhbiBpbnRlcmFjdGlvbiBtb2RlbDogIEluIHRoZSBMQiwgd2UgaGF2ZSBhIGZ1bGwgcmFuayBtYXRyaXggb2YKTEIrbm90aGluZywgTEIraW5kdWNlciwgTEIrcmVkdWNlcnMsIGFuZCBMQitpbmR1Y2VyK3JlZHVjZXJzLiAgSW4gdGhlClNDRk0sIHdlIGRvIG5vdCwgd2UgaGF2ZSBvbmx5IFNDRk0rbm90aGluZywgU0NGTStpbmR1Y2VyLApTQ0ZNK2luZHVjZXIrcmVkdWNlcnMgLS0gYnV0IG5vIFNDRk0rcmVkdWNlcnMuICBBcyBhIHJlc3VsdCwgd2UgZG8gbm90CmhhdmUgdGhlIHBvd2VyIHRvIGRvIHNvbWUgb2YgdGhlIG1vc3QgaW50ZXJlc3Rpbmcgc3R1ZmYsCmUuZy4gbWVkaWEraW5kdWNlcjpyZWR1Y2Vycy4KCklmIHdlIHRha2UgYXdheSB0aGUgU0NGTSBzYW1wbGVzLCB3ZSBjYW4gZG8gaW5kdWNlcjpyZWR1Y2VycywgYnV0IHRoYXQKaXMgbXVjaCBsZXNzIGV4Y2l0aW5nLgoKYGBge3Igc2F2ZW1lLCBldmFsPUZBTFNFfQpwYW5kZXI6OnBhbmRlcihzZXNzaW9uSW5mbygpKQptZXNzYWdlKHBhc3RlMCgiVGhpcyBpcyBocGdsdG9vbHMgY29tbWl0OiAiLCBnZXRfZ2l0X2NvbW1pdCgpKSkKdGhpc19zYXZlIDwtIHBhc3RlMChnc3ViKHBhdHRlcm49IlxcLlJtZCIsIHJlcGxhY2U9IiIsIHg9cm1kX2ZpbGUpLCAiLXYiLCB2ZXIsICIucmRhLnh6IikKbWVzc2FnZShwYXN0ZTAoIlNhdmluZyB0byAiLCB0aGlzX3NhdmUpKQp0bXAgPC0gc20oc2F2ZW1lKGZpbGVuYW1lPXRoaXNfc2F2ZSkpCmBgYAo=