1 TODO:

Check what was done with the over expression vector sequence, did I definitely map against it? (I think so).

2 Introduction

I still need to do some reading to understand this series of experiments; but I believe the idea was to use a short-hairpin to lower expression of hdac5 in some cases, and an adenovirus overexpression vector in others.

3 Preprocessing

I passed these samples to my default preprocessing pipeline on the umiacs cluster with the following command:

cd preprocessing
start=$(pwd)
for i in $(/bin/ls -d *); do
    cd $i
    cyoa --task pipe --method prnaseq --species rnorvegicus_7.2_107 --gff_type gene \
         --stranded reverse --gff_tag ID \
         --input $(/bin/ls *.fastq.gz | tr '\n' ':' | sed 's/:$//g')
    cd $start
done

The above command iterates over every directory of raw data I created and runs my rnaseq (–method prnaseq) pipeline (–task pipe) using a new copy of the ensembl Rattus norvegicus genome (version 7.2) from ensembl’s release 107 (–species rnorvegicus_7.2_107). When I downloaded it, I saved a gene feature file and the fasta genome with the same prefix and therefore htseq will use the gene annotations (–gff_type gene) by identifier ‘ID’ (–gff_tag ID).

I did not provide any instructions for trimming nor mapping, so it will use my defaults. All of the scripts run on the cluster are therefore located in the preprocessing/*/scripts/ directories in files named lexically in the order they were performed.

4 Genome Annotation

I will extract annotations from ensembl, since it is my favorite. My load_biomart_annotation() function attempts to find an archive server from one year ago, this may be made explicit if one wishes.

One important caveat, when creating the expressionset data structure, the gene IDs must match and the gff usually uses IDs which are a specific combination of columns from the ensembl table. It usually takes a moment to figure out how the two data sources line up; as a result sometimes it is easier to just extract annotations directly from the gff file.

columns <- c("ensembl_gene_id", "version", "ensembl_transcript_id", "transcript_version",
             "description", "gene_biotype", "entrezgene_description", "rgd_symbol", "band")
rn_annot <- load_biomart_annotations(species = "rnorvegicus", gene_requests = columns,
                                     year = "2020", month = 8)
## The biomart annotations file already exists, loading from it.

5 Metadata collection

I often just copy/paste the relevant fields from the output files created by trimomatic/htseq/hisat/etc. However, I did make a fun function: gather_preprocessing_metadata() which in theory at least can fill out a metadata xlsx file.

written <- gather_preprocessing_metadata("sample_sheets/all_samples.xlsx")

I noticed a couple problems with my function:

  1. It does not like dates in the MM/DD/YYYY format. There are a couple reasons for which this may have happened.
  2. It did not save the condition/batch columns I created, so I copy/pasted them from the previous sheet.

6 Initial Data Structure

Let us create an expression set of the metadata and annotations. Note that the

annot <- rn_annot[["annotation"]]
rownames(annot) <- make.names(paste0("gene:", annot[["ensembl_gene_id"]]), unique = TRUE)
rownames(annot) <- gsub(pattern="\\.", replacement = ":", x = rownames(annot))

rn_expt <- create_expt(metadata="sample_sheets/all_samples_modified.xlsx",
                       gene_info=annot, file_column="hisatcounttable")
## Reading the sample metadata.
## The sample definitions comprises: 24 rows(samples) and 30 columns(metadata fields).
## Matched 18641 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 23139 features and 24 samples.
written <- write_expt(rn_expt, excel="excel/rn_expt.xlsx")
## Deleting the file excel/rn_expt.xlsx before writing the tables.
## Writing the first sheet, containing a legend and some summary data.
## `geom_smooth()` using formula = 'y ~ x'
## Error in checkModelStatus(fit, showWarnings = showWarnings, colinearityCutoff = colinearityCutoff,  : 
##   The variables specified in this model are redundant,
## so the design matrix is not full rank
## Retrying with only condition in the model.
## Loading required package: Matrix
## 
## Attaching package: 'Matrix'
## 
## The following object is masked from 'package:S4Vectors':
## 
##     expand
## 
## 
## Total:82 s
## `geom_smooth()` using formula = 'y ~ x'
## Error in checkModelStatus(fit, showWarnings = showWarnings, colinearityCutoff = colinearityCutoff,  : 
##   The variables specified in this model are redundant,
## so the design matrix is not full rank
## Retrying with only condition in the model.
## 
## Total:65 s

7 Initial plots

Let us look briefly at a couple of likely plots of the data.

plot_libsize(rn_expt)$plot

plot_nonzero(rn_expt)$plot
## 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.

plot_variance_coefficients(rn_expt)$plot
## Naively calculating coefficient of variation/dispersion with respect to condition.
## Finished calculating dispersion estimates.

This is worrisome. The trimming kept ~95% of the reads, which is awesome. Mapping appears to have been in the 90%s. I would therefore have assumed the numbers of reads mapped to genes to be higher than 1.1<x<3.7 million.

My first place to look into this is the fastqc output. My initial hypothesis is that the sequence duplication file will show a huge number of reads mapping to rRNA or some other ncRNA. It appears that guess was incorrect, there do not appear to be significantly over represented sequences in this data. Maybe lets look at the reads in IGV?

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

rn_nbsva <- normalize_expt(rn_expt, filter=TRUE, convert="cpm",
                           batch="svaseq", transform="log2")
## Removing 8998 low-count genes (14141 remaining).
## Setting 920 low elements to zero.
## transform_counts: Found 920 values equal to 0, adding 1 to the matrix.
plot_pca(rn_nbsva)$plot
## Warning: ggrepel: 11 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

7.1 Test other factors

The sample metadata is a little limited vis a vis the ways in which the samples are categorized. We have, in effect, 4 primary factors:

  • The expression method used: either (I am assuming this from the sample name) a short hairpin which was presumably transfected, or an adenovirus overexpression transfection.
  • The perturbation goal: There are three goals used in these transfections, either do nothing, over-express HDAC5, or lower its expression.
  • The target of the transfection: This is either luc (luciferase I assume), GFP, or histone deacetylase 5 (which of course has its own tremendous set of changes it causes to the transcriptional landscape).
  • The combination of the above: There are at least two which one may express this, I chose 4 categories: control_kd, hdac5_kd, control_over, and hdac5_over. One may instead choose to combine the controls into one category, though I would argue (and the data agrees with me) that doing so is wrong; but in the ideal world the perturbation of the luciferase/GFP targets would do nothing and these categories would be identical.

With the above in mind, let us consider some of the other categorizations of the data and see if any make sense. I know already from the first picture that knockdown and overexpression are the two primary sources of variance.

rn_var <- simple_varpart(
    rn_expt, chosen_factor = "insertion",
    factors = c("replicatenumber", "insertion", "perturbation", "worked"))
## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable

## Warning in summary.lm(object, ...): essentially perfect fit: summary may be
## unreliable
## 
## Total:149 s
pp(file = "images/rn_varpart.png", image = rn_var$partition_plot)
## Warning in pp(file = "images/rn_varpart.png", image = rn_var$partition_plot):
## There is no device to shut down.
kd_over <- set_expt_conditions(rn_expt, fact = "insertion")
## 
##   adeno_assoc short_hairpin 
##            14            10
## This will, by definition, just be the exact plot as before colored differently.
kd_over_norm <- normalize_expt(kd_over, transform = "log2", convert = "cpm",
                               filter = TRUE, norm = "quant")
## Removing 8998 low-count genes (14141 remaining).
## transform_counts: Found 2 values equal to 0, adding 1 to the matrix.
pp(file = "images/over_pca.png", image = plot_pca(kd_over_norm)$plot)
## Warning in pp(file = "images/over_pca.png", image =
## plot_pca(kd_over_norm)$plot): There is no device to shut down.
perturb <- set_expt_conditions(rn_expt, fact = "perturbation")
## 
##     control   knockdown overexpress 
##          12           5           7
## This will, by definition, just be the exact plot as before colored differently.
perturb_norm <- normalize_expt(perturb, transform = "log2", convert = "cpm",
                               filter = TRUE, norm = "quant")
## Removing 8998 low-count genes (14141 remaining).
## transform_counts: Found 2 values equal to 0, adding 1 to the matrix.
plot_pca(perturb_norm)$plot
## Warning: ggrepel: 8 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
perturb_nb <- normalize_expt(perturb, transform = "log2", convert = "cpm",
                             filter = TRUE, batch = "svaseq")
## Removing 8998 low-count genes (14141 remaining).
## Setting 662 low elements to zero.
## transform_counts: Found 662 values equal to 0, adding 1 to the matrix.
plot_pca(perturb_nb)$plot

target <- set_expt_conditions(rn_expt, fact = "target")
## 
##        GFP      HDAC5 luciferase 
##          7         12          5
## This will, by definition, just be the exact plot as before colored differently.
target_norm <- normalize_expt(target, transform = "log2", convert = "cpm",
                              filter = TRUE, norm = "quant")
## Removing 8998 low-count genes (14141 remaining).
## transform_counts: Found 2 values equal to 0, adding 1 to the matrix.
plot_pca(target_norm)$plot
target_nb <- normalize_expt(target, transform = "log2", convert = "cpm",
                            filter = TRUE, batch = "svaseq")
## Removing 8998 low-count genes (14141 remaining).
## Setting 560 low elements to zero.
## transform_counts: Found 560 values equal to 0, adding 1 to the matrix.
plot_pca(target_nb)$plot

8 Separate sets

NOTE!: We made a column in the sample sheet called ‘worked’, it is a categorical defined by its neighboring column ‘eYFP ratio’, which in turn is defined as the percentage of the unmapped to mouse reads which successfully mapped to the eYFP vector. That ratio ranged from 0.0 to 0.03 (e.g. absolute 0 to 3% of the remaining reads). I therefore made categories: none, hundredths, tenths, ones.

We then made the decision that a successful sample should have more than 0.09% reads mapped to YFP and excluded the others.

Simultaneously, experimental batch c1e3 is off on its own with only 2 samples and so we decided to exclude it. (There were two dates on which samples were collected and 3 dates of RNA isolation; c1e3 is the first sample date, but RNA on a later date and comprised 1 control and 1 over expression sample).

kd <- subset_expt(rn_expt, subset = "insertion=='short_hairpin'") %>%
  subset_expt(subset = "worked!='hundredths'")
## subset_expt(): There were 24, now there are 10 samples.
## subset_expt(): There were 10, now there are 5 samples.
kd_norm <- normalize_expt(kd, transform = "log2", convert = "cpm",
                          norm = "quant", filter = TRUE)
## Removing 9775 low-count genes (13364 remaining).
pp(file = "images/knockdown_pca.png", image = plot_pca(kd_norm)$plot)
## Warning in pp(file = "images/knockdown_pca.png", image =
## plot_pca(kd_norm)$plot): There is no device to shut down.
kd_nb <- normalize_expt(kd, transform = "log2",
                        convert = "cpm", batch = "svaseq", filter = TRUE)
## Removing 9775 low-count genes (13364 remaining).
## Setting 3 low elements to zero.
## transform_counts: Found 3 values equal to 0, adding 1 to the matrix.
plot_pca(kd_nb)$plot

oe <- subset_expt(rn_expt, subset = "insertion=='adeno_assoc'") %>%
  subset_expt(subset = "batch!='c1e3'")
## subset_expt(): There were 24, now there are 14 samples.
## subset_expt(): There were 14, now there are 12 samples.
oe_norm <- normalize_expt(oe, transform = "log2",
                          convert = "cpm", norm = "quant", filter = TRUE)
## Removing 10067 low-count genes (13072 remaining).
## transform_counts: Found 1 values equal to 0, adding 1 to the matrix.
plot_pca(oe_norm)$plot
oe_nb <- normalize_expt(oe, transform = "log2",
                        convert = "cpm", batch = "svaseq", filter = TRUE)
## Removing 10067 low-count genes (13072 remaining).
## Setting 40 low elements to zero.
## transform_counts: Found 40 values equal to 0, adding 1 to the matrix.
plot_pca(oe_nb)$plot

9 Attempt a perturbation differential expression.

kd_de <- all_pairwise(kd, model_batch = FALSE, filter = TRUE)
## This DE analysis will perform all pairwise comparisons among:
## 
## control_kd   hdac5_kd 
##          3          2
## This will pre-filter the input data using normalize_expt's: TRUE argument.
## Finished running DE analyses, collecting outputs.
## Comparing analyses.
kd_keeper <- list("down_control" = c("hdac5kd", "controlkd"))
kd_table <- combine_de_tables(kd_de, keepers = kd_keeper, excel = "excel/kd_table.xlsx")
## Deleting the file excel/kd_table.xlsx before writing the tables.
## Getting the genes >= 1.5 stdevs away from the mean of all.
## Getting the genes >= 1.5 stdevs away from the mean of all.
## Getting the genes >= 1.5 stdevs away from the mean of all.
kd_sig <- extract_significant_genes(kd_table, according_to = "deseq", p = 0.1,
                                    lfc = 0.38, excel = "excel/kd_sig.xlsx")
## Deleting the file excel/kd_sig.xlsx before writing the tables.
kd_up <- kd_sig[["deseq"]][["ups"]][["down_control"]]
kd_down <- kd_sig[["deseq"]][["downs"]][["down_control"]]
kd_gp <- simple_gprofiler(kd_down, species = "rnorvegicus")
## Warning in simple_gprofiler2(...): Hey, it looks like you forgot to strip off
## the htseq prefix for the gene IDs.
## No results to show
## Please make sure that the organism is correct or set significant = FALSE
## No results to show
## Please make sure that the organism is correct or set significant = FALSE
## No results to show
## Please make sure that the organism is correct or set significant = FALSE
## No results to show
## Please make sure that the organism is correct or set significant = FALSE
## Error in gprofiler2::gost(query = gene_ids, organism = species, evcodes = evcodes,  : 
##   Bad request, response code 400
## No results to show
## Please make sure that the organism is correct or set significant = FALSE
## No results to show
## Please make sure that the organism is correct or set significant = FALSE
oe_de <- all_pairwise(oe, model_batch = "svaseq", filter = TRUE)
## This DE analysis will perform all pairwise comparisons among:
## 
## control_over   hdac5_over 
##            6            6
## 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 (13072 remaining).
## Setting 40 low elements to zero.
## transform_counts: Found 40 values equal to 0, adding 1 to the matrix.
## Finished running DE analyses, collecting outputs.
## Comparing analyses.
oe_keeper <- list("up_control" = c("hdac5over", "controlover"))
oe_table <- combine_de_tables(oe_de, keepers = oe_keeper, excel = "excel/oe_table.xlsx")
## Deleting the file excel/oe_table.xlsx before writing the tables.
## Getting the genes >= 1.5 stdevs away from the mean of all.
## Getting the genes >= 1.5 stdevs away from the mean of all.
## Getting the genes >= 1.5 stdevs away from the mean of all.
oe_sig <- extract_significant_genes(oe_table, according_to = "deseq", p = 0.1, lfc = 0.38,
                                    excel = "excel/oe_sig.xlsx")
## Deleting the file excel/oe_sig.xlsx before writing the tables.
kd_up <- kd_sig[["deseq"]][["ups"]][["down_control"]]
nrow(kd_up)
## [1] 539
kd_down <- kd_sig[["deseq"]][["downs"]][["down_control"]]
nrow(kd_down)
## [1] 588
oe_up <- oe_sig[["deseq"]][["ups"]][["up_control"]]
nrow(oe_up)
## [1] 359
oe_down <- oe_sig[["deseq"]][["downs"]][["up_control"]]
nrow(oe_down)
## [1] 106
up_lst <- list(
    "kd_up" = rownames(kd_up),
    "oe_down" = rownames(oe_down))
up_inter <- Vennerable::Venn(up_lst)

down_lst <- list(
    "kd_down" = rownames(kd_down),
    "oe_up" = rownames(oe_up))
down_inter <- Vennerable::Venn(down_lst)

up_up <- list(
    "kd_up" = rownames(kd_up),
    "oe_up" = rownames(oe_up))
Vennerable::Venn(up_up)
## A Venn object on 2 sets named
## kd_up,oe_up 
##  00  10  01  11 
##   0 528 348  11
down_down <- list(
    "kd_down" = rownames(kd_down),
    "oe_down" = rownames(oe_down))
Vennerable::Venn(down_down)
## A Venn object on 2 sets named
## kd_down,oe_down 
##  00  10  01  11 
##   0 563  81  25
pander::pander(sessionInfo())

R version 4.2.0 (2022-04-22)

Platform: x86_64-pc-linux-gnu (64-bit)

locale: LC_CTYPE=en_US.UTF-8, LC_NUMERIC=C, LC_TIME=en_US.UTF-8, LC_COLLATE=en_US.UTF-8, LC_MONETARY=en_US.UTF-8, LC_MESSAGES=en_US.UTF-8, LC_PAPER=en_US.UTF-8, LC_NAME=C, LC_ADDRESS=C, LC_TELEPHONE=C, LC_MEASUREMENT=en_US.UTF-8 and LC_IDENTIFICATION=C

attached base packages: splines, stats4, stats, graphics, grDevices, utils, datasets, methods and base

other attached packages: ruv(v.0.9.7.1), lme4(v.1.1-31), Matrix(v.1.5-3), BiocParallel(v.1.32.5), variancePartition(v.1.28.4), hpgltools(v.1.0), testthat(v.3.1.6), reticulate(v.1.28), SummarizedExperiment(v.1.28.0), GenomicRanges(v.1.50.2), GenomeInfoDb(v.1.34.9), IRanges(v.2.32.0), S4Vectors(v.0.36.1), MatrixGenerics(v.1.10.0), matrixStats(v.0.63.0), Biobase(v.2.58.0) and BiocGenerics(v.0.44.0)

loaded via a namespace (and not attached): rappdirs(v.0.3.3), rtracklayer(v.1.58.0), R.methodsS3(v.1.8.2), tidyr(v.1.3.0), ggplot2(v.3.4.1), clusterGeneration(v.1.3.7), bit64(v.4.0.5), knitr(v.1.42), R.utils(v.2.12.2), DelayedArray(v.0.24.0), data.table(v.1.14.6), KEGGREST(v.1.38.0), RCurl(v.1.98-1.10), doParallel(v.1.0.17), generics(v.0.1.3), preprocessCore(v.1.60.2), GenomicFeatures(v.1.50.4), callr(v.3.7.3), RhpcBLASctl(v.0.23-42), cowplot(v.1.1.1), usethis(v.2.1.6), RSQLite(v.2.2.20), shadowtext(v.0.1.2), bit(v.4.0.5), enrichplot(v.1.18.3), xml2(v.1.3.3), httpuv(v.1.6.8), assertthat(v.0.2.1), viridis(v.0.6.2), xfun(v.0.37), hms(v.1.1.2), jquerylib(v.0.1.4), IHW(v.1.26.0), evaluate(v.0.20), promises(v.1.2.0.1), DEoptimR(v.1.0-11), fansi(v.1.0.4), restfulr(v.0.0.15), progress(v.1.2.2), caTools(v.1.18.2), dbplyr(v.2.3.0), geneplotter(v.1.76.0), igraph(v.1.4.0), DBI(v.1.1.3), htmlwidgets(v.1.6.1), purrr(v.1.0.1), ellipsis(v.0.3.2), crosstalk(v.1.2.0), dplyr(v.1.1.0), backports(v.1.4.1), gprofiler2(v.0.2.1), annotate(v.1.76.0), aod(v.1.3.2), biomaRt(v.2.54.0), vctrs(v.0.5.2), remotes(v.2.4.2), cachem(v.1.0.6), withr(v.2.5.0), ggforce(v.0.4.1), HDO.db(v.0.99.1), robustbase(v.0.95-0), GenomicAlignments(v.1.34.0), treeio(v.1.22.0), fdrtool(v.1.2.17), prettyunits(v.1.1.1), DOSE(v.3.24.2), ape(v.5.6-2), lazyeval(v.0.2.2), crayon(v.1.5.2), genefilter(v.1.80.3), slam(v.0.1-50), labeling(v.0.4.2), edgeR(v.3.40.2), pkgconfig(v.2.0.3), tweenr(v.2.0.2), nlme(v.3.1-162), pkgload(v.1.3.2), devtools(v.2.4.5), rlang(v.1.0.6), lifecycle(v.1.0.3), miniUI(v.0.1.1.1), downloader(v.0.4), filelock(v.1.0.2), BiocFileCache(v.2.6.0), directlabels(v.2021.1.13), rprojroot(v.2.0.3), polyclip(v.1.10-4), graph(v.1.76.0), aplot(v.0.1.9), lpsymphony(v.1.26.3), boot(v.1.3-28.1), processx(v.3.8.0), png(v.0.1-8), viridisLite(v.0.4.1), rjson(v.0.2.21), bitops(v.1.0-7), R.oo(v.1.25.0), gson(v.0.0.9), KernSmooth(v.2.23-20), pander(v.0.6.5), Biostrings(v.2.66.0), blob(v.1.2.3), stringr(v.1.5.0), qvalue(v.2.30.0), remaCor(v.0.0.11), gridGraphics(v.0.5-1), scales(v.1.2.1), memoise(v.2.0.1), GSEABase(v.1.60.0), magrittr(v.2.0.3), plyr(v.1.8.8), gplots(v.3.1.3), zlibbioc(v.1.44.0), compiler(v.4.2.0), scatterpie(v.0.1.8), BiocIO(v.1.8.0), RColorBrewer(v.1.1-3), DESeq2(v.1.38.3), Rsamtools(v.2.14.0), cli(v.3.6.0), XVector(v.0.38.0), urlchecker(v.1.0.1), patchwork(v.1.1.2), ps(v.1.7.2), MASS(v.7.3-58.2), mgcv(v.1.8-41), tidyselect(v.1.2.0), stringi(v.1.7.12), highr(v.0.10), yaml(v.2.3.7), GOSemSim(v.2.24.0), locfit(v.1.5-9.7), ggrepel(v.0.9.3), grid(v.4.2.0), sass(v.0.4.5), fastmatch(v.1.1-3), tools(v.4.2.0), parallel(v.4.2.0), rstudioapi(v.0.14), foreach(v.1.5.2), gridExtra(v.2.3), Rtsne(v.0.16), farver(v.2.1.1), ggraph(v.2.1.0), digest(v.0.6.31), shiny(v.1.7.4), quadprog(v.1.5-8), Rcpp(v.1.0.10), broom(v.1.0.3), later(v.1.3.0), httr(v.1.4.4), AnnotationDbi(v.1.60.0), Rdpack(v.2.4), colorspace(v.2.1-0), brio(v.1.1.3), XML(v.3.99-0.13), fs(v.1.6.1), RBGL(v.1.74.0), yulab.utils(v.0.0.6), PROPER(v.1.30.0), tidytree(v.0.4.2), graphlayouts(v.0.8.4), ggplotify(v.0.1.0), plotly(v.4.10.1), sessioninfo(v.1.2.2), xtable(v.1.8-4), jsonlite(v.1.8.4), nloptr(v.2.0.3), ggtree(v.3.6.2), tidygraph(v.1.2.3), corpcor(v.1.6.10), ggfun(v.0.0.9), Vennerable(v.3.1.0.9000), R6(v.2.5.1), RUnit(v.0.4.32), profvis(v.0.3.7), pillar(v.1.8.1), htmltools(v.0.5.4), mime(v.0.12), glue(v.1.6.2), fastmap(v.1.1.0), minqa(v.1.2.5), clusterProfiler(v.4.6.0), codetools(v.0.2-19), fgsea(v.1.24.0), pkgbuild(v.1.4.0), mvtnorm(v.1.1-3), utf8(v.1.2.3), lattice(v.0.20-45), bslib(v.0.4.2), tibble(v.3.1.8), sva(v.3.46.0), pbkrtest(v.0.5.2), curl(v.5.0.0), gtools(v.3.9.4), zip(v.2.2.2), GO.db(v.3.16.0), openxlsx(v.4.2.5.2), survival(v.3.5-3), limma(v.3.54.1), rmarkdown(v.2.20), desc(v.1.4.2), munsell(v.0.5.0), fastcluster(v.1.2.3), GenomeInfoDbData(v.1.2.9), iterators(v.1.0.14), reshape2(v.1.4.4), gtable(v.0.3.1) and rbibutils(v.2.2.13)

message(paste0("This is hpgltools commit: ", get_git_commit()))
## If you wish to reproduce this exact build of hpgltools, invoke the following:
## > git clone http://github.com/abelew/hpgltools.git
## > git reset c100d5666f5032d24c933739015d267ef651c323
## This is hpgltools commit: Wed Mar 1 09:50:14 2023 -0500: c100d5666f5032d24c933739015d267ef651c323
this_save <- paste0(gsub(pattern="\\.Rmd", replace="", x=rmd_file), "-v", ver, ".rda.xz")
message(paste0("Saving to ", this_save))
## Saving to index-v202303.rda.xz
tmp <- sm(saveme(filename=this_save))
LS0tCnRpdGxlOiAiSGlzdG9uZSBEZWFjZXR5bGFzZSA1IHVuZGVyL292ZXIgZXhwcmVzc2lvbiBpbiBSLm5vcnZlZ2ljdXMuIgphdXRob3I6ICJhdGIgYWJlbGV3QGdtYWlsLmNvbSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZmlnX2NhcHRpb246IHRydWUKICAgIGZpZ19oZWlnaHQ6IDcKICAgIGZpZ193aWR0aDogNwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAga2VlcF9tZDogZmFsc2UKICAgIG1vZGU6IHNlbGZjb250YWluZWQKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgc2VsZl9jb250YWluZWQ6IHRydWUKICAgIHRoZW1lOiByZWFkYWJsZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UKICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICB3aWR0aDogMzAwCiAgICBrZWVwX21kOiBmYWxzZQogICAgbW9kZTogc2VsZmNvbnRhaW5lZAogICAgdG9jX2Zsb2F0OiB0cnVlCiAgQmlvY1N0eWxlOjpodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBrZWVwX21kOiBmYWxzZQogICAgbW9kZTogc2VsZmNvbnRhaW5lZAogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keSwgdGQgewogIGZvbnQtc2l6ZTogMTZweDsKfQpjb2RlLnJ7CiAgZm9udC1zaXplOiAxNnB4Owp9CnByZSB7CiBmb250LXNpemU6IDE2cHgKfQo8L3N0eWxlPgoKYGBge3Igb3B0aW9ucywgaW5jbHVkZT1GQUxTRX0KbGlicmFyeSgiaHBnbHRvb2xzIikKbGlicmFyeSgicmV0aWN1bGF0ZSIpCnR0IDwtIGRldnRvb2xzOjpsb2FkX2FsbCgifi9ocGdsdG9vbHMiKQprbml0cjo6b3B0c19rbml0JHNldCh3aWR0aCA9IDEyMCwKICAgICAgICAgICAgICAgICAgICAgcHJvZ3Jlc3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgZWNobyA9IFRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBkcGkgPSA5NikKb2xkX29wdGlvbnMgPC0gb3B0aW9ucyhkaWdpdHMgPSA0LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICBrbml0ci5kdXBsaWNhdGUubGFiZWwgPSAiYWxsb3ciKQpnZ3Bsb3QyOjp0aGVtZV9zZXQoZ2dwbG90Mjo6dGhlbWVfYncoYmFzZV9zaXplID0gMTApKQpydW5kYXRlIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCBmb3JtYXQgPSAiJVklbSVkIikKcHJldmlvdXNfZmlsZSA8LSAiIgp2ZXIgPC0gZm9ybWF0KFN5cy5EYXRlKCksICIlWSVtIikKCiMjdG1wIDwtIHNtKGxvYWRtZShmaWxlbmFtZT1wYXN0ZTAoZ3N1YihwYXR0ZXJuPSJcXC5SbWQiLCByZXBsYWNlPSIiLCB4PXByZXZpb3VzX2ZpbGUpLCAiLXYiLCB2ZXIsICIucmRhLnh6IikpKQpybWRfZmlsZSA8LSAiaW5kZXguUm1kIgpgYGAKCiMgVE9ETzoKCkNoZWNrIHdoYXQgd2FzIGRvbmUgd2l0aCB0aGUgb3ZlciBleHByZXNzaW9uIHZlY3RvciBzZXF1ZW5jZSwgZGlkIEkKZGVmaW5pdGVseSBtYXAgYWdhaW5zdCBpdD8gKEkgdGhpbmsgc28pLgoKIyBJbnRyb2R1Y3Rpb24KCkkgc3RpbGwgbmVlZCB0byBkbyBzb21lIHJlYWRpbmcgdG8gdW5kZXJzdGFuZCB0aGlzIHNlcmllcyBvZgpleHBlcmltZW50czsgYnV0IEkgYmVsaWV2ZSB0aGUgaWRlYSB3YXMgdG8gdXNlIGEgc2hvcnQtaGFpcnBpbiB0bwpsb3dlciBleHByZXNzaW9uIG9mIGhkYWM1IGluIHNvbWUgY2FzZXMsIGFuZCBhbiBhZGVub3ZpcnVzCm92ZXJleHByZXNzaW9uIHZlY3RvciBpbiBvdGhlcnMuCgojIFByZXByb2Nlc3NpbmcKCkkgcGFzc2VkIHRoZXNlIHNhbXBsZXMgdG8gbXkgZGVmYXVsdCBwcmVwcm9jZXNzaW5nIHBpcGVsaW5lIG9uIHRoZQp1bWlhY3MgY2x1c3RlciB3aXRoIHRoZSBmb2xsb3dpbmcgY29tbWFuZDoKCmBgYHtiYXNoIHByZXByb2Nlc3MsIGV2YWw9RkFMU0V9CmNkIHByZXByb2Nlc3NpbmcKc3RhcnQ9JChwd2QpCmZvciBpIGluICQoL2Jpbi9scyAtZCAqKTsgZG8KICAgIGNkICRpCiAgICBjeW9hIC0tdGFzayBwaXBlIC0tbWV0aG9kIHBybmFzZXEgLS1zcGVjaWVzIHJub3J2ZWdpY3VzXzcuMl8xMDcgLS1nZmZfdHlwZSBnZW5lIFwKICAgICAgICAgLS1zdHJhbmRlZCByZXZlcnNlIC0tZ2ZmX3RhZyBJRCBcCiAgICAgICAgIC0taW5wdXQgJCgvYmluL2xzICouZmFzdHEuZ3ogfCB0ciAnXG4nICc6JyB8IHNlZCAncy86JC8vZycpCiAgICBjZCAkc3RhcnQKZG9uZQpgYGAKClRoZSBhYm92ZSBjb21tYW5kIGl0ZXJhdGVzIG92ZXIgZXZlcnkgZGlyZWN0b3J5IG9mIHJhdyBkYXRhIEkgY3JlYXRlZAphbmQgcnVucyBteSBybmFzZXEgKC0tbWV0aG9kIHBybmFzZXEpIHBpcGVsaW5lICgtLXRhc2sgcGlwZSkgdXNpbmcgYQpuZXcgY29weSBvZiB0aGUgZW5zZW1ibCBSYXR0dXMgbm9ydmVnaWN1cyBnZW5vbWUgKHZlcnNpb24gNy4yKSBmcm9tCmVuc2VtYmwncyByZWxlYXNlIDEwNyAoLS1zcGVjaWVzIHJub3J2ZWdpY3VzXzcuMl8xMDcpLiAgV2hlbiBJCmRvd25sb2FkZWQgaXQsIEkgc2F2ZWQgYSBnZW5lIGZlYXR1cmUgZmlsZSBhbmQgdGhlIGZhc3RhIGdlbm9tZSB3aXRoCnRoZSBzYW1lIHByZWZpeCBhbmQgdGhlcmVmb3JlIGh0c2VxIHdpbGwgdXNlIHRoZSBnZW5lIGFubm90YXRpb25zCigtLWdmZl90eXBlIGdlbmUpIGJ5IGlkZW50aWZpZXIgJ0lEJyAoLS1nZmZfdGFnIElEKS4KCkkgZGlkIG5vdCBwcm92aWRlIGFueSBpbnN0cnVjdGlvbnMgZm9yIHRyaW1taW5nIG5vciBtYXBwaW5nLCBzbyBpdAp3aWxsIHVzZSBteSBkZWZhdWx0cy4gIEFsbCBvZiB0aGUgc2NyaXB0cyBydW4gb24gdGhlIGNsdXN0ZXIgYXJlCnRoZXJlZm9yZSBsb2NhdGVkIGluIHRoZSBwcmVwcm9jZXNzaW5nLyovc2NyaXB0cy8gZGlyZWN0b3JpZXMgaW4gZmlsZXMKbmFtZWQgbGV4aWNhbGx5IGluIHRoZSBvcmRlciB0aGV5IHdlcmUgcGVyZm9ybWVkLgoKIyBHZW5vbWUgQW5ub3RhdGlvbgoKSSB3aWxsIGV4dHJhY3QgYW5ub3RhdGlvbnMgZnJvbSBlbnNlbWJsLCBzaW5jZSBpdCBpcyBteSBmYXZvcml0ZS4gIE15CmxvYWRfYmlvbWFydF9hbm5vdGF0aW9uKCkgZnVuY3Rpb24gYXR0ZW1wdHMgdG8gZmluZCBhbiBhcmNoaXZlIHNlcnZlcgpmcm9tIG9uZSB5ZWFyIGFnbywgdGhpcyBtYXkgYmUgbWFkZSBleHBsaWNpdCBpZiBvbmUgd2lzaGVzLgoKT25lIGltcG9ydGFudCBjYXZlYXQsIHdoZW4gY3JlYXRpbmcgdGhlIGV4cHJlc3Npb25zZXQgZGF0YSBzdHJ1Y3R1cmUsCnRoZSBnZW5lIElEcyBtdXN0IG1hdGNoIGFuZCB0aGUgZ2ZmIHVzdWFsbHkgdXNlcyBJRHMgd2hpY2ggYXJlIGEKc3BlY2lmaWMgY29tYmluYXRpb24gb2YgY29sdW1ucyBmcm9tIHRoZSBlbnNlbWJsIHRhYmxlLiAgSXQgdXN1YWxseQp0YWtlcyBhIG1vbWVudCB0byBmaWd1cmUgb3V0IGhvdyB0aGUgdHdvIGRhdGEgc291cmNlcyBsaW5lIHVwOyBhcyBhCnJlc3VsdCBzb21ldGltZXMgaXQgaXMgZWFzaWVyIHRvIGp1c3QgZXh0cmFjdCBhbm5vdGF0aW9ucyBkaXJlY3RseQpmcm9tIHRoZSBnZmYgZmlsZS4KCmBgYHtyIGFubm90YXRpb259CmNvbHVtbnMgPC0gYygiZW5zZW1ibF9nZW5lX2lkIiwgInZlcnNpb24iLCAiZW5zZW1ibF90cmFuc2NyaXB0X2lkIiwgInRyYW5zY3JpcHRfdmVyc2lvbiIsCiAgICAgICAgICAgICAiZGVzY3JpcHRpb24iLCAiZ2VuZV9iaW90eXBlIiwgImVudHJlemdlbmVfZGVzY3JpcHRpb24iLCAicmdkX3N5bWJvbCIsICJiYW5kIikKcm5fYW5ub3QgPC0gbG9hZF9iaW9tYXJ0X2Fubm90YXRpb25zKHNwZWNpZXMgPSAicm5vcnZlZ2ljdXMiLCBnZW5lX3JlcXVlc3RzID0gY29sdW1ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgPSAiMjAyMCIsIG1vbnRoID0gOCkKYGBgCgojIE1ldGFkYXRhIGNvbGxlY3Rpb24KCkkgb2Z0ZW4ganVzdCBjb3B5L3Bhc3RlIHRoZSByZWxldmFudCBmaWVsZHMgZnJvbSB0aGUgb3V0cHV0IGZpbGVzCmNyZWF0ZWQgYnkgdHJpbW9tYXRpYy9odHNlcS9oaXNhdC9ldGMuICBIb3dldmVyLCBJIGRpZCBtYWtlIGEgZnVuCmZ1bmN0aW9uOiBnYXRoZXJfcHJlcHJvY2Vzc2luZ19tZXRhZGF0YSgpIHdoaWNoIGluIHRoZW9yeSBhdCBsZWFzdCBjYW4KZmlsbCBvdXQgYSBtZXRhZGF0YSB4bHN4IGZpbGUuCgpgYGB7ciBtZXRhZGF0YSwgZXZhbD1GQUxTRX0Kd3JpdHRlbiA8LSBnYXRoZXJfcHJlcHJvY2Vzc2luZ19tZXRhZGF0YSgic2FtcGxlX3NoZWV0cy9hbGxfc2FtcGxlcy54bHN4IikKYGBgCgpJIG5vdGljZWQgYSBjb3VwbGUgcHJvYmxlbXMgd2l0aCBteSBmdW5jdGlvbjoKCjEuICBJdCBkb2VzIG5vdCBsaWtlIGRhdGVzIGluIHRoZSBNTS9ERC9ZWVlZIGZvcm1hdC4gIFRoZXJlIGFyZSBhCiAgICBjb3VwbGUgcmVhc29ucyBmb3Igd2hpY2ggdGhpcyBtYXkgaGF2ZSBoYXBwZW5lZC4KMi4gIEl0IGRpZCBub3Qgc2F2ZSB0aGUgY29uZGl0aW9uL2JhdGNoIGNvbHVtbnMgSSBjcmVhdGVkLCBzbyBJCiAgICBjb3B5L3Bhc3RlZCB0aGVtIGZyb20gdGhlIHByZXZpb3VzIHNoZWV0LgoKIyBJbml0aWFsIERhdGEgU3RydWN0dXJlCgpMZXQgdXMgY3JlYXRlIGFuIGV4cHJlc3Npb24gc2V0IG9mIHRoZSBtZXRhZGF0YSBhbmQgYW5ub3RhdGlvbnMuICBOb3RlCnRoYXQgdGhlCgpgYGB7ciBleHB0fQphbm5vdCA8LSBybl9hbm5vdFtbImFubm90YXRpb24iXV0Kcm93bmFtZXMoYW5ub3QpIDwtIG1ha2UubmFtZXMocGFzdGUwKCJnZW5lOiIsIGFubm90W1siZW5zZW1ibF9nZW5lX2lkIl1dKSwgdW5pcXVlID0gVFJVRSkKcm93bmFtZXMoYW5ub3QpIDwtIGdzdWIocGF0dGVybj0iXFwuIiwgcmVwbGFjZW1lbnQgPSAiOiIsIHggPSByb3duYW1lcyhhbm5vdCkpCgpybl9leHB0IDwtIGNyZWF0ZV9leHB0KG1ldGFkYXRhPSJzYW1wbGVfc2hlZXRzL2FsbF9zYW1wbGVzX21vZGlmaWVkLnhsc3giLAogICAgICAgICAgICAgICAgICAgICAgIGdlbmVfaW5mbz1hbm5vdCwgZmlsZV9jb2x1bW49Imhpc2F0Y291bnR0YWJsZSIpCgp3cml0dGVuIDwtIHdyaXRlX2V4cHQocm5fZXhwdCwgZXhjZWw9ImV4Y2VsL3JuX2V4cHQueGxzeCIpCmBgYAoKIyBJbml0aWFsIHBsb3RzCgpMZXQgdXMgbG9vayBicmllZmx5IGF0IGEgY291cGxlIG9mIGxpa2VseSBwbG90cyBvZiB0aGUgZGF0YS4KCmBgYHtyIGluaXRpYWx9CnBsb3RfbGlic2l6ZShybl9leHB0KSRwbG90CgpwbG90X25vbnplcm8ocm5fZXhwdCkkcGxvdAoKcGxvdF92YXJpYW5jZV9jb2VmZmljaWVudHMocm5fZXhwdCkkcGxvdApgYGAKClRoaXMgaXMgd29ycmlzb21lLiAgVGhlIHRyaW1taW5nIGtlcHQgfjk1JSBvZiB0aGUgcmVhZHMsIHdoaWNoIGlzCmF3ZXNvbWUuICBNYXBwaW5nIGFwcGVhcnMgdG8gaGF2ZSBiZWVuIGluIHRoZSA5MCVzLiAgSSB3b3VsZCB0aGVyZWZvcmUKaGF2ZSBhc3N1bWVkIHRoZSBudW1iZXJzIG9mIHJlYWRzIG1hcHBlZCB0byBnZW5lcyB0byBiZSBoaWdoZXIgdGhhbgoxLjE8eDwzLjcgbWlsbGlvbi4KCk15IGZpcnN0IHBsYWNlIHRvIGxvb2sgaW50byB0aGlzIGlzIHRoZSBmYXN0cWMgb3V0cHV0LiAgTXkgaW5pdGlhbApoeXBvdGhlc2lzIGlzIHRoYXQgdGhlIHNlcXVlbmNlIGR1cGxpY2F0aW9uIGZpbGUgd2lsbCBzaG93IGEgaHVnZQpudW1iZXIgb2YgcmVhZHMgbWFwcGluZyB0byByUk5BIG9yIHNvbWUgb3RoZXIgbmNSTkEuICBJdCBhcHBlYXJzIHRoYXQKZ3Vlc3Mgd2FzIGluY29ycmVjdCwgdGhlcmUgZG8gbm90IGFwcGVhciB0byBiZSBzaWduaWZpY2FudGx5IG92ZXIKcmVwcmVzZW50ZWQgc2VxdWVuY2VzIGluIHRoaXMgZGF0YS4gIE1heWJlIGxldHMgbG9vayBhdCB0aGUgcmVhZHMgaW4gSUdWPwoKYGBge3Igb3RoZXJfcGxvdHN9CnJuX25vcm0gPC0gbm9ybWFsaXplX2V4cHQocm5fZXhwdCwgZmlsdGVyPVRSVUUsIGNvbnZlcnQ9ImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybT0icXVhbnQiLCB0cmFuc2Zvcm09ImxvZzIiKQpwbG90X3BjYShybl9ub3JtKSRwbG90Cgpybl9uYnN2YSA8LSBub3JtYWxpemVfZXhwdChybl9leHB0LCBmaWx0ZXI9VFJVRSwgY29udmVydD0iY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYmF0Y2g9InN2YXNlcSIsIHRyYW5zZm9ybT0ibG9nMiIpCnBsb3RfcGNhKHJuX25ic3ZhKSRwbG90CmBgYAoKIyMgVGVzdCBvdGhlciBmYWN0b3JzCgpUaGUgc2FtcGxlIG1ldGFkYXRhIGlzIGEgbGl0dGxlIGxpbWl0ZWQgdmlzIGEgdmlzIHRoZSB3YXlzIGluIHdoaWNoCnRoZSBzYW1wbGVzIGFyZSBjYXRlZ29yaXplZC4gIFdlIGhhdmUsIGluIGVmZmVjdCwgNCBwcmltYXJ5IGZhY3RvcnM6CgoqIFRoZSBleHByZXNzaW9uIG1ldGhvZCB1c2VkOiBlaXRoZXIgKEkgYW0gYXNzdW1pbmcgdGhpcyBmcm9tIHRoZQogIHNhbXBsZSBuYW1lKSBhIHNob3J0IGhhaXJwaW4gd2hpY2ggd2FzIHByZXN1bWFibHkgdHJhbnNmZWN0ZWQsIG9yIGFuCiAgYWRlbm92aXJ1cyBvdmVyZXhwcmVzc2lvbiB0cmFuc2ZlY3Rpb24uCiogVGhlIHBlcnR1cmJhdGlvbiBnb2FsOiBUaGVyZSBhcmUgdGhyZWUgZ29hbHMgdXNlZCBpbiB0aGVzZQogIHRyYW5zZmVjdGlvbnMsIGVpdGhlciBkbyBub3RoaW5nLCBvdmVyLWV4cHJlc3MgSERBQzUsIG9yIGxvd2VyIGl0cwogIGV4cHJlc3Npb24uCiogVGhlIHRhcmdldCBvZiB0aGUgdHJhbnNmZWN0aW9uOiBUaGlzIGlzIGVpdGhlciBsdWMgKGx1Y2lmZXJhc2UgSQogIGFzc3VtZSksIEdGUCwgb3IgaGlzdG9uZSBkZWFjZXR5bGFzZSA1ICh3aGljaCBvZiBjb3Vyc2UgaGFzIGl0cyBvd24KICB0cmVtZW5kb3VzIHNldCBvZiBjaGFuZ2VzIGl0IGNhdXNlcyB0byB0aGUgdHJhbnNjcmlwdGlvbmFsCiAgbGFuZHNjYXBlKS4KKiBUaGUgY29tYmluYXRpb24gb2YgdGhlIGFib3ZlOiBUaGVyZSBhcmUgYXQgbGVhc3QgdHdvIHdoaWNoIG9uZSBtYXkKICBleHByZXNzIHRoaXMsIEkgY2hvc2UgNCBjYXRlZ29yaWVzOiBjb250cm9sX2tkLCBoZGFjNV9rZCwKICBjb250cm9sX292ZXIsIGFuZCBoZGFjNV9vdmVyLiAgT25lIG1heSBpbnN0ZWFkIGNob29zZSB0byBjb21iaW5lIHRoZQogIGNvbnRyb2xzIGludG8gb25lIGNhdGVnb3J5LCB0aG91Z2ggSSB3b3VsZCBhcmd1ZSAoYW5kIHRoZSBkYXRhCiAgYWdyZWVzIHdpdGggbWUpIHRoYXQgZG9pbmcgc28gaXMgd3Jvbmc7IGJ1dCBpbiB0aGUgaWRlYWwgd29ybGQgdGhlCiAgcGVydHVyYmF0aW9uIG9mIHRoZSBsdWNpZmVyYXNlL0dGUCB0YXJnZXRzIHdvdWxkIGRvIG5vdGhpbmcgYW5kCiAgdGhlc2UgY2F0ZWdvcmllcyB3b3VsZCBiZSBpZGVudGljYWwuCgpXaXRoIHRoZSBhYm92ZSBpbiBtaW5kLCBsZXQgdXMgY29uc2lkZXIgc29tZSBvZiB0aGUgb3RoZXIKY2F0ZWdvcml6YXRpb25zIG9mIHRoZSBkYXRhIGFuZCBzZWUgaWYgYW55IG1ha2Ugc2Vuc2UuICBJIGtub3cgYWxyZWFkeQpmcm9tIHRoZSBmaXJzdCBwaWN0dXJlIHRoYXQga25vY2tkb3duIGFuZCBvdmVyZXhwcmVzc2lvbiBhcmUgdGhlIHR3bwpwcmltYXJ5IHNvdXJjZXMgb2YgdmFyaWFuY2UuCgpgYGB7ciBlZmZpY2llbmN5fQpybl92YXIgPC0gc2ltcGxlX3ZhcnBhcnQoCiAgICBybl9leHB0LCBjaG9zZW5fZmFjdG9yID0gImluc2VydGlvbiIsCiAgICBmYWN0b3JzID0gYygicmVwbGljYXRlbnVtYmVyIiwgImluc2VydGlvbiIsICJwZXJ0dXJiYXRpb24iLCAid29ya2VkIikpCnBwKGZpbGUgPSAiaW1hZ2VzL3JuX3ZhcnBhcnQucG5nIiwgaW1hZ2UgPSBybl92YXIkcGFydGl0aW9uX3Bsb3QpCmBgYAoKYGBge3Igb3RoZXJfY29sb3JzfQprZF9vdmVyIDwtIHNldF9leHB0X2NvbmRpdGlvbnMocm5fZXhwdCwgZmFjdCA9ICJpbnNlcnRpb24iKQojIyBUaGlzIHdpbGwsIGJ5IGRlZmluaXRpb24sIGp1c3QgYmUgdGhlIGV4YWN0IHBsb3QgYXMgYmVmb3JlIGNvbG9yZWQgZGlmZmVyZW50bHkuCmtkX292ZXJfbm9ybSA8LSBub3JtYWxpemVfZXhwdChrZF9vdmVyLCB0cmFuc2Zvcm0gPSAibG9nMiIsIGNvbnZlcnQgPSAiY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IFRSVUUsIG5vcm0gPSAicXVhbnQiKQpwcChmaWxlID0gImltYWdlcy9vdmVyX3BjYS5wbmciLCBpbWFnZSA9IHBsb3RfcGNhKGtkX292ZXJfbm9ybSkkcGxvdCkKCnBlcnR1cmIgPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhybl9leHB0LCBmYWN0ID0gInBlcnR1cmJhdGlvbiIpCiMjIFRoaXMgd2lsbCwgYnkgZGVmaW5pdGlvbiwganVzdCBiZSB0aGUgZXhhY3QgcGxvdCBhcyBiZWZvcmUgY29sb3JlZCBkaWZmZXJlbnRseS4KcGVydHVyYl9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KHBlcnR1cmIsIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyID0gVFJVRSwgbm9ybSA9ICJxdWFudCIpCnBsb3RfcGNhKHBlcnR1cmJfbm9ybSkkcGxvdApwZXJ0dXJiX25iIDwtIG5vcm1hbGl6ZV9leHB0KHBlcnR1cmIsIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IFRSVUUsIGJhdGNoID0gInN2YXNlcSIpCnBsb3RfcGNhKHBlcnR1cmJfbmIpJHBsb3QKCnRhcmdldCA8LSBzZXRfZXhwdF9jb25kaXRpb25zKHJuX2V4cHQsIGZhY3QgPSAidGFyZ2V0IikKIyMgVGhpcyB3aWxsLCBieSBkZWZpbml0aW9uLCBqdXN0IGJlIHRoZSBleGFjdCBwbG90IGFzIGJlZm9yZSBjb2xvcmVkIGRpZmZlcmVudGx5Lgp0YXJnZXRfbm9ybSA8LSBub3JtYWxpemVfZXhwdCh0YXJnZXQsIHRyYW5zZm9ybSA9ICJsb2cyIiwgY29udmVydCA9ICJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLCBub3JtID0gInF1YW50IikKcGxvdF9wY2EodGFyZ2V0X25vcm0pJHBsb3QKdGFyZ2V0X25iIDwtIG5vcm1hbGl6ZV9leHB0KHRhcmdldCwgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBUUlVFLCBiYXRjaCA9ICJzdmFzZXEiKQpwbG90X3BjYSh0YXJnZXRfbmIpJHBsb3QKYGBgCgojIFNlcGFyYXRlIHNldHMKCk5PVEUhOiAgV2UgbWFkZSBhIGNvbHVtbiBpbiB0aGUgc2FtcGxlIHNoZWV0IGNhbGxlZCAnd29ya2VkJywgaXQgaXMgYQpjYXRlZ29yaWNhbCBkZWZpbmVkIGJ5IGl0cyBuZWlnaGJvcmluZyBjb2x1bW4gJ2VZRlAgcmF0aW8nLCB3aGljaCBpbgp0dXJuIGlzIGRlZmluZWQgYXMgdGhlIHBlcmNlbnRhZ2Ugb2YgdGhlIHVubWFwcGVkIHRvIG1vdXNlIHJlYWRzIHdoaWNoCnN1Y2Nlc3NmdWxseSBtYXBwZWQgdG8gdGhlIGVZRlAgdmVjdG9yLiAgVGhhdCByYXRpbyByYW5nZWQgZnJvbSAwLjAgdG8KMC4wMyAoZS5nLiBhYnNvbHV0ZSAwIHRvIDMlIG9mIHRoZSByZW1haW5pbmcgcmVhZHMpLiAgSSB0aGVyZWZvcmUgbWFkZQpjYXRlZ29yaWVzOiBub25lLCBodW5kcmVkdGhzLCB0ZW50aHMsIG9uZXMuCgpXZSB0aGVuIG1hZGUgdGhlIGRlY2lzaW9uIHRoYXQgYSBzdWNjZXNzZnVsIHNhbXBsZSBzaG91bGQgaGF2ZSBtb3JlCnRoYW4gMC4wOSUgcmVhZHMgbWFwcGVkIHRvIFlGUCBhbmQgZXhjbHVkZWQgdGhlIG90aGVycy4KClNpbXVsdGFuZW91c2x5LCBleHBlcmltZW50YWwgYmF0Y2ggYzFlMyBpcyBvZmYgb24gaXRzIG93biB3aXRoIG9ubHkgMgpzYW1wbGVzIGFuZCBzbyB3ZSBkZWNpZGVkIHRvIGV4Y2x1ZGUgaXQuICAoVGhlcmUgd2VyZSB0d28gZGF0ZXMgb24Kd2hpY2ggc2FtcGxlcyB3ZXJlIGNvbGxlY3RlZCBhbmQgMyBkYXRlcyBvZiBSTkEgaXNvbGF0aW9uOyBjMWUzIGlzIHRoZQpmaXJzdCBzYW1wbGUgZGF0ZSwgYnV0IFJOQSBvbiBhIGxhdGVyIGRhdGUgYW5kIGNvbXByaXNlZCAxIGNvbnRyb2wgYW5kCjEgb3ZlciBleHByZXNzaW9uIHNhbXBsZSkuCgpgYGB7ciBrZF9leHBlcmltZW50fQprZCA8LSBzdWJzZXRfZXhwdChybl9leHB0LCBzdWJzZXQgPSAiaW5zZXJ0aW9uPT0nc2hvcnRfaGFpcnBpbiciKSAlPiUKICBzdWJzZXRfZXhwdChzdWJzZXQgPSAid29ya2VkIT0naHVuZHJlZHRocyciKQoKa2Rfbm9ybSA8LSBub3JtYWxpemVfZXhwdChrZCwgdHJhbnNmb3JtID0gImxvZzIiLCBjb252ZXJ0ID0gImNwbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybSA9ICJxdWFudCIsIGZpbHRlciA9IFRSVUUpCnBwKGZpbGUgPSAiaW1hZ2VzL2tub2NrZG93bl9wY2EucG5nIiwgaW1hZ2UgPSBwbG90X3BjYShrZF9ub3JtKSRwbG90KQprZF9uYiA8LSBub3JtYWxpemVfZXhwdChrZCwgdHJhbnNmb3JtID0gImxvZzIiLAogICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJ0ID0gImNwbSIsIGJhdGNoID0gInN2YXNlcSIsIGZpbHRlciA9IFRSVUUpCnBsb3RfcGNhKGtkX25iKSRwbG90CgpvZSA8LSBzdWJzZXRfZXhwdChybl9leHB0LCBzdWJzZXQgPSAiaW5zZXJ0aW9uPT0nYWRlbm9fYXNzb2MnIikgJT4lCiAgc3Vic2V0X2V4cHQoc3Vic2V0ID0gImJhdGNoIT0nYzFlMyciKQpvZV9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KG9lLCB0cmFuc2Zvcm0gPSAibG9nMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29udmVydCA9ICJjcG0iLCBub3JtID0gInF1YW50IiwgZmlsdGVyID0gVFJVRSkKcGxvdF9wY2Eob2Vfbm9ybSkkcGxvdApvZV9uYiA8LSBub3JtYWxpemVfZXhwdChvZSwgdHJhbnNmb3JtID0gImxvZzIiLAogICAgICAgICAgICAgICAgICAgICAgICBjb252ZXJ0ID0gImNwbSIsIGJhdGNoID0gInN2YXNlcSIsIGZpbHRlciA9IFRSVUUpCnBsb3RfcGNhKG9lX25iKSRwbG90CmBgYAoKIyBBdHRlbXB0IGEgcGVydHVyYmF0aW9uIGRpZmZlcmVudGlhbCBleHByZXNzaW9uLgoKYGBge3IgcGVydHVyYl9kZX0Ka2RfZGUgPC0gYWxsX3BhaXJ3aXNlKGtkLCBtb2RlbF9iYXRjaCA9IEZBTFNFLCBmaWx0ZXIgPSBUUlVFKQprZF9rZWVwZXIgPC0gbGlzdCgiZG93bl9jb250cm9sIiA9IGMoImhkYWM1a2QiLCAiY29udHJvbGtkIikpCmtkX3RhYmxlIDwtIGNvbWJpbmVfZGVfdGFibGVzKGtkX2RlLCBrZWVwZXJzID0ga2Rfa2VlcGVyLCBleGNlbCA9ICJleGNlbC9rZF90YWJsZS54bHN4IikKa2Rfc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoa2RfdGFibGUsIGFjY29yZGluZ190byA9ICJkZXNlcSIsIHAgPSAwLjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxmYyA9IDAuMzgsIGV4Y2VsID0gImV4Y2VsL2tkX3NpZy54bHN4IikKa2RfdXAgPC0ga2Rfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJkb3duX2NvbnRyb2wiXV0Ka2RfZG93biA8LSBrZF9zaWdbWyJkZXNlcSJdXVtbImRvd25zIl1dW1siZG93bl9jb250cm9sIl1dCmtkX2dwIDwtIHNpbXBsZV9ncHJvZmlsZXIoa2RfZG93biwgc3BlY2llcyA9ICJybm9ydmVnaWN1cyIpCgpvZV9kZSA8LSBhbGxfcGFpcndpc2Uob2UsIG1vZGVsX2JhdGNoID0gInN2YXNlcSIsIGZpbHRlciA9IFRSVUUpCm9lX2tlZXBlciA8LSBsaXN0KCJ1cF9jb250cm9sIiA9IGMoImhkYWM1b3ZlciIsICJjb250cm9sb3ZlciIpKQpvZV90YWJsZSA8LSBjb21iaW5lX2RlX3RhYmxlcyhvZV9kZSwga2VlcGVycyA9IG9lX2tlZXBlciwgZXhjZWwgPSAiZXhjZWwvb2VfdGFibGUueGxzeCIpCm9lX3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKG9lX3RhYmxlLCBhY2NvcmRpbmdfdG8gPSAiZGVzZXEiLCBwID0gMC4xLCBsZmMgPSAwLjM4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleGNlbCA9ICJleGNlbC9vZV9zaWcueGxzeCIpCgprZF91cCA8LSBrZF9zaWdbWyJkZXNlcSJdXVtbInVwcyJdXVtbImRvd25fY29udHJvbCJdXQpucm93KGtkX3VwKQprZF9kb3duIDwtIGtkX3NpZ1tbImRlc2VxIl1dW1siZG93bnMiXV1bWyJkb3duX2NvbnRyb2wiXV0KbnJvdyhrZF9kb3duKQoKb2VfdXAgPC0gb2Vfc2lnW1siZGVzZXEiXV1bWyJ1cHMiXV1bWyJ1cF9jb250cm9sIl1dCm5yb3cob2VfdXApCm9lX2Rvd24gPC0gb2Vfc2lnW1siZGVzZXEiXV1bWyJkb3ducyJdXVtbInVwX2NvbnRyb2wiXV0KbnJvdyhvZV9kb3duKQoKdXBfbHN0IDwtIGxpc3QoCiAgICAia2RfdXAiID0gcm93bmFtZXMoa2RfdXApLAogICAgIm9lX2Rvd24iID0gcm93bmFtZXMob2VfZG93bikpCnVwX2ludGVyIDwtIFZlbm5lcmFibGU6OlZlbm4odXBfbHN0KQoKZG93bl9sc3QgPC0gbGlzdCgKICAgICJrZF9kb3duIiA9IHJvd25hbWVzKGtkX2Rvd24pLAogICAgIm9lX3VwIiA9IHJvd25hbWVzKG9lX3VwKSkKZG93bl9pbnRlciA8LSBWZW5uZXJhYmxlOjpWZW5uKGRvd25fbHN0KQoKdXBfdXAgPC0gbGlzdCgKICAgICJrZF91cCIgPSByb3duYW1lcyhrZF91cCksCiAgICAib2VfdXAiID0gcm93bmFtZXMob2VfdXApKQpWZW5uZXJhYmxlOjpWZW5uKHVwX3VwKQoKZG93bl9kb3duIDwtIGxpc3QoCiAgICAia2RfZG93biIgPSByb3duYW1lcyhrZF9kb3duKSwKICAgICJvZV9kb3duIiA9IHJvd25hbWVzKG9lX2Rvd24pKQpWZW5uZXJhYmxlOjpWZW5uKGRvd25fZG93bikKYGBgCgoKYGBge3Igc2F2ZW1lfQpwYW5kZXI6OnBhbmRlcihzZXNzaW9uSW5mbygpKQptZXNzYWdlKHBhc3RlMCgiVGhpcyBpcyBocGdsdG9vbHMgY29tbWl0OiAiLCBnZXRfZ2l0X2NvbW1pdCgpKSkKdGhpc19zYXZlIDwtIHBhc3RlMChnc3ViKHBhdHRlcm49IlxcLlJtZCIsIHJlcGxhY2U9IiIsIHg9cm1kX2ZpbGUpLCAiLXYiLCB2ZXIsICIucmRhLnh6IikKbWVzc2FnZShwYXN0ZTAoIlNhdmluZyB0byAiLCB0aGlzX3NhdmUpKQp0bXAgPC0gc20oc2F2ZW1lKGZpbGVuYW1lPXRoaXNfc2F2ZSkpCmBgYAo=