Note, I am using my previous worksheet from when last I worked with Rezia as a template for this (I copied it from there and am modifying it now).
S.pyogenes 5448 rofA RNASeq version: 20190916
Preprocessing
I used my cyoa tool to process these samples by doing the following:
- Copying the data from the sequencer into the directory ‘preprocessing/’
- Used a slightly involved shell command to create a directory for each sample and copy the reads for it to the ‘unprocessed/’ subdirectory within it.
- invoked the following:
cd preprocessing
start=$(pwd)
for i in $(/bin/ls -d ./*)
do
cd $i
rm -rf outputs scripts
cyoa --task pipe --method prnas --species spyogenes_5448 \
--gff_type gene --gff_tag locus_tag \
--input $(/bin/ls unprocessed/* | tr '\n' ':' | sed 's/:$//g')
cd $start
done
The above for loop goes into each sample and does the following:
- Trims the data, heavily compresses the outputs.
- Runs fastqc
- Runs hisat2 using my spyogenes_5448 indices.
- Converts the sam alignment to sorted/indexed bam.
- Makes a couple of extra copies of it with some filters.
- Compresses the aligned/unaligned reads.
- Runs htseq-count on the alignments to count reads/gene.
Note the following steps were not actually run because I had a speeling error. But since they are not necessary for the explicitly RNASeq analyses I first want to do, I ignored it. I am curious though to see if there are other mutations in these strains, so I will likely run those portions manually.
- Runs freebayes on the alignments to look for variants.
- Sorts/compresses the freebayes output.
- Does some parsing of the freebayes output and provides some tables about where mutations were observed.
Collect annotation information
Same two primary annotation sources, the gff file used for mapping/counting, and microbesonline.org. Note that since I moved to just downloading the material from the web interface, I no longer have a handy method to get the taxon ID, so I go there and hunt down the taxId manually.
Now that I am thinking about it, my 5448 genome/annotations are kind of old, I will ask and check to see if there is anything newer.
Also, 5448 does not have an entry at microbesonline.org, a fact which I forgot. I need to go poking in my notes to reconnect 5005 and 5448.
gff_annot <- load_gff_annotations("reference/spyogenes_5448.gff", type = "gene")
## Trying attempt: rtracklayer::import.gff3(gff, sequenceRegionsAsSeqinfo = TRUE)
## Trying attempt: rtracklayer::import.gff3(gff, sequenceRegionsAsSeqinfo = FALSE)
## Had a successful gff import with rtracklayer::import.gff3(gff, sequenceRegionsAsSeqinfo = FALSE)
## Returning a df with 14 columns and 1814 rows.
rownames(gff_annot) <- gff_annot[["locus_tag"]]
head(gff_annot)
## seqnames start end width strand source type score
## SP5448_00005 CP008776 232 1587 1356 + EMBL/GenBank/SwissProt gene NA
## SP5448_00010 CP008776 1742 2878 1137 + EMBL/GenBank/SwissProt gene NA
## SP5448_00015 CP008776 2953 3150 198 + EMBL/GenBank/SwissProt gene NA
## SP5448_00020 CP008776 3480 4595 1116 + EMBL/GenBank/SwissProt gene NA
## SP5448_00025 CP008776 4665 5234 570 + EMBL/GenBank/SwissProt gene NA
## SP5448_00030 CP008776 5237 8740 3504 + EMBL/GenBank/SwissProt gene NA
## phase locus_tag gene gene_synonym note pseudo
## SP5448_00005 1 SP5448_00005 <NA> <NA> <NA>
## SP5448_00010 1 SP5448_00010 <NA> <NA> <NA>
## SP5448_00015 1 SP5448_00015 <NA> <NA> <NA>
## SP5448_00020 1 SP5448_00020 <NA> <NA> <NA>
## SP5448_00025 1 SP5448_00025 <NA> <NA> <NA>
## SP5448_00030 1 SP5448_00030 <NA> <NA> <NA>
mgas_data <- load_genbank_annotations(accession="CP008776")
## Loading required namespace: rentrez
## Done Parsing raw GenBank file text. [ 14.023 seconds ]
## 2022-04-25 15:51:33 Starting creation of gene GRanges
## 2022-04-25 15:51:36 Starting creation of CDS GRanges
## 2022-04-25 15:51:43 Starting creation of exon GRanges
## No exons read from genbank file. Assuming sections of CDS are full exons
## 2022-04-25 15:51:45 Starting creation of variant VRanges
## 2022-04-25 15:51:45 Starting creation of transcript GRanges
## No transcript features (mRNA) found, using spans of CDSs
## 2022-04-25 15:51:46 Starting creation of misc feature GRanges
## Warning in fill_stack_df(feats[!typs %in% c("gene", "exon", "CDS",
## "variation", : Got unexpected multi-value field(s) [ inference ]. The resulting
## column(s) will be of class CharacterList, rather than vector(s). Please contact
## the maintainer if multi-valuedness is expected/meaningful for the listed
## field(s).
## 2022-04-25 15:51:46 - Done creating GenBankRecord object [ 13.159 seconds ]
genome_size <- GenomicRanges::width(mgas_data$seq) ## This fails on travis?
mgas_cds <- as.data.frame(mgas_data$cds)
## Get rid of amino acid sequence
rownames(mgas_cds) <- mgas_cds[["locus_tag"]]
wanted <- ! colnames(mgas_cds) %in% c("translation", "type", "strand", "seqnames", "start", "end", "locus_tag", "note", "gene", "gene_synonym", "width")
mgas_cds <- mgas_cds[, wanted]
## And EC_number because wtf is that?
mgas_annot <- merge(mgas_cds, gff_annot, by="row.names")
rownames(mgas_annot) <- mgas_annot[["Row.names"]]
mgas_annot[["Row.names"]] <- NULL
Create expressionSet
Note that I did the following to the sample sheet provided by Dr. McIver:
- Changed the dP_R20_4 sample to dP_R20_2 (The original sample name is still there are the original column).
- Added columns at the end containing the count table locations.
- Added columns ‘short_media’, ‘growth_phase’, ‘genotype’ which hopefully contain the relevant metadata extracted from the sample descriptions.
- Added a column ‘Experiment’ which is either ‘rofA’ or ‘pdxR’.
rofa_expt <- create_expt(metadata = "sample_sheets/all_samples.xlsx",
gene_info = gff_annot, file_column = "spyogenes5448genecounts") %>%
subset_expt(subset="experiment=='rofA'") %>%
set_expt_conditions(fact="genotype") %>%
set_expt_batches(fact="growthphase")
## 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: 72 rows(samples) and 27 columns(metadata fields).
## Matched 1814 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 1814 rows and 72 columns.
## subset_expt(): There were 72, now there are 36 samples.
I have 36 samples to play with, let us see what they look like.
Poke expressionSet
rofa_libsize <- plot_libsize(rofa_expt)
rofa_libsize$plot

rofa_filter_plot <- plot_libsize_prepost(rofa_expt)
rofa_filter_plot$count_plot

rofa_filter_plot$lowgene_plot
## Warning: Using alpha for a discrete variable is not advised.

rofa_nonzero <- plot_nonzero(rofa_expt)
rofa_nonzero$plot
## Warning: ggrepel: 17 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

## awesome, there is a range of ~ 25 genes.
written <- write_expt(rofa_expt, excel = glue::glue("excel/rofA_expt-v{ver}.xlsx"))
## Deleting the file excel/rofA_expt-v20190916.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:13 s
## `geom_smooth()` using formula 'y ~ x'
## `geom_smooth()` using formula 'y ~ x'
##
## Total:13 s
Quick visualizations
Without considering time
Let us start with some views of the data without thinking about batch effects.
rofa_norm <- normalize_expt(rofa_expt, filter=TRUE, norm="quant", convert="cpm", transform="log2")
## Removing 40 low-count genes (1774 remaining).
rofa_pca <- plot_pca(rofa_norm)
rofa_pca$plot

rofa_heatmap <- plot_disheat(rofa_norm)
rofa_heatmap$plot

Considering time
Repeat the previous plots, but this time using limma’s batch removal method (which is just a residuals).
rofa_time <- set_expt_conditions(rofa_expt, fact="growthphase") %>%
set_expt_batches(fact="genotype")
rofa_time_norm <- normalize_expt(rofa_time, filter=TRUE, norm="quant", convert="cpm",
transform="log2")
## Removing 40 low-count genes (1774 remaining).
rofa_time_pca <- plot_pca(rofa_time_norm)
rofa_time_pca$plot

rofa_time_nb <- normalize_expt(rofa_time, filter=TRUE, norm="quant", convert="cpm",
transform="log2", batch="limma")
## Removing 40 low-count genes (1774 remaining).
## If you receive a warning: 'NANs produced', one potential reason is that the data was quantile normalized.
## Setting 28 low elements to zero.
## transform_counts: Found 28 values equal to 0, adding 1 to the matrix.
rofa_time_nb_pca <- plot_pca(rofa_time_nb)
rofa_time_nb_pca$plot

Well, it is pretty clear that time is the dominant factor.
Differential Expression analyses
I am going to do the DE in 4 separate pieces:
- Only compare strains
- Only compare times
- Compare the concatenation of strains and times.
Strain only comparisons
strain_de <- all_pairwise(rofa_expt, model_batch=TRUE, filter=TRUE)
## Using limma's removeBatchEffect to visualize with(out) batch inclusion.
## Finished running DE analyses, collecting outputs.
## Comparing analyses.

strain_keepers <- list(
"delta_wt" = c("delta", "WT"),
"revert_wt" = c("revert", "WT"),
"delta_revert" = c("delta", "revert"))
strain_tables <- combine_de_tables(
strain_de, keepers = strain_keepers,
excel = glue::glue("excel/rofa_strain_tables-v{ver}.xlsx"))
## Deleting the file excel/rofa_strain_tables-v20190916.xlsx before writing the tables.
strain_sig <- extract_significant_genes(
strain_tables,
excel = glue::glue("excel/rofa_strain_sig-v{ver}.xlsx"))
## Deleting the file excel/rofa_strain_sig-v20190916.xlsx before writing the tables.
Time only comparisons
time_de <- all_pairwise(rofa_time, model_batch=TRUE)
## Using limma's removeBatchEffect to visualize with(out) batch inclusion.
## Finished running DE analyses, collecting outputs.
## Comparing analyses.

time_keepers <- list(
"transition_exponential" = c("transition", "exponential"),
"stationary_exponential" = c("stationary", "exponential"),
"stationary_transition" = c("stationary", "transition"))
time_tables <- combine_de_tables(
time_de, keepers = time_keepers,
excel = glue::glue("excel/rofa_time_tables-v{ver}.xlsx"))
## Deleting the file excel/rofa_time_tables-v20190916.xlsx before writing the tables.
time_sig <- extract_significant_genes(
time_tables,
excel = glue::glue("excel/rofa_time_sig-v{ver}.xlsx"))
## Deleting the file excel/rofa_time_sig-v20190916.xlsx before writing the tables.
Compare times vs strains
I always worry that comparing a data set across multiple conditions results in not what I think it will. Let us therefore plot the logFCs of likely contrasts against each other.
x_axis <- time_tables[["data"]][["transition_exponential"]][, c("deseq_logfc", "deseq_adjp")]
y_axis <- strain_tables[["data"]][["delta_wt"]][, c("deseq_logfc", "deseq_adjp")]
both <- merge(x_axis, y_axis, by="row.names")
rownames(both) <- both[["Row.names"]]
both[["Row.names"]] <- NULL
cor.test(both[["deseq_logfc.x"]], both[["deseq_logfc.y"]], method="spearman")
## Warning in cor.test.default(both[["deseq_logfc.x"]], both[["deseq_logfc.y"]], :
## Cannot compute exact p-value with ties
##
## Spearman's rank correlation rho
##
## data: both[["deseq_logfc.x"]] and both[["deseq_logfc.y"]]
## S = 5.3e+08, p-value <2e-16
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
## rho
## 0.4274
plotted <- plot_linear_scatter(both[, c("deseq_logfc.x", "deseq_logfc.y")])
## Warning in plot_multihistogram(df): NAs introduced by coercion
plotted$scatter

Interaction model
I want to make sure that my methods of performing interaction models work as I think it does, and this data set looks to me to be a perfect place to test that.
combined_factors <- paste0(pData(rofa_expt)[["genotype"]], "_",
pData(rofa_expt)[["growthphase"]])
combined_expt <- set_expt_conditions(rofa_expt, fact=combined_factors) %>%
combined_de <- all_pairwise(combined_expt, model_batch="svaseq", filter=TRUE)
combined_keepers <- list(
"exponential_delta_vs_wt" = c("deltaexponential", "WTexponential"),
"exponential_delta_vs_revert" = c("deltaexponential", "revertexponential"),
"stationary_delta_vs_wt" = c("deltastationary", "WTstationary"),
"stationary_delta_vs_revert" = c("deltastationary", "revertstationary"),
"transition_delta_vs_wt" = c("deltatransition", "WTtransition"),
"transition_delta_vs_revert" = c("deltatransition", "reverttransition"),
"WT_exponential_vs_transition" = c("WTexponential", "WTtransition"),
"delta_exponential_vs_transition" = c("deltaexponential", "deltatransition"),
"revert_exponential_vs_transition" = c("revertexponential", "reverttransition"),
"WT_stationary_vs_transition" = c("WTstationary", "WTtransition"),
"delta_stationary_vs_transition" = c("deltastationary", "deltatransition"),
"revert_stationary_vs_transition" = c("revertstationary", "reverttransition"),
"WT_stationary_vs_exponential" = c("WTstationary", "WTexponential"),
"delta_stationary_vs_exponential" = c("deltastationary", "deltaexponential"),
"revert_stationary_vs_exponential" = c("revertstationary", "revertexponential"))
combined_tables <- combine_de_tables(
combined_de, keepers = combined_keepers,
excel = glue::glue("excel/rofa_combined_tables-v{ver}.xlsx"))
combined_sig <- extract_significant_genes(
combined_tables,
excel = glue::glue("excel/rofa_combined_sig-v{ver}.xlsx"))
if (!isTRUE(get0("skip_load"))) {
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))
}
## If you wish to reproduce this exact build of hpgltools, invoke the following:
## > git clone http://github.com/abelew/hpgltools.git
## > git reset 09b2f72b4cb5e7f7f74b7a7970f5b2f7f3609e41
## This is hpgltools commit: Tue Apr 19 18:55:02 2022 -0400: 09b2f72b4cb5e7f7f74b7a7970f5b2f7f3609e41
## Saving to index-v20190916.rda.xz
LS0tCnRpdGxlOiAiUy5weW9nZW5lcyA1NDQ4IHJuYXNlcSBjb21wYXJpbmcgcm9mQSBzdHJhaW5zIGFjcm9zcyB0aW1lLiIKYXV0aG9yOiAiYXRiIGFiZWxld0BnbWFpbC5jb20iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogaHRtbF9kb2N1bWVudDoKICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgY29kZV9mb2xkaW5nOiBzaG93CiAgZmlnX2NhcHRpb246IHRydWUKICBmaWdfaGVpZ2h0OiA3CiAgZmlnX3dpZHRoOiA3CiAgaGlnaGxpZ2h0OiBkZWZhdWx0CiAga2VlcF9tZDogZmFsc2UKICBtb2RlOiBzZWxmY29udGFpbmVkCiAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgc2VsZl9jb250YWluZWQ6IHRydWUKICB0aGVtZTogcmVhZGFibGUKICB0b2M6IHRydWUKICB0b2NfZmxvYXQ6CiAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQotLS0KCjxzdHlsZT4KICBib2R5IC5tYWluLWNvbnRhaW5lciB7CiAgICBtYXgtd2lkdGg6IDE2MDBweDsKICB9Cjwvc3R5bGU+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkoaHBnbHRvb2xzKQp0dCA8LSBkZXZ0b29sczo6bG9hZF9hbGwoIn4vaHBnbHRvb2xzIikKa25pdHI6Om9wdHNfa25pdCRzZXQocHJvZ3Jlc3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgd2lkdGggPSA5MCwKICAgICAgICAgICAgICAgICAgICAgZWNobyA9IFRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBmaWcud2lkdGggPSA4LAogICAgICAgICAgICAgICAgICAgICAgZmlnLmhlaWdodCA9IDgsCiAgICAgICAgICAgICAgICAgICAgICBkcGkgPSA5NikKb2xkX29wdGlvbnMgPC0gb3B0aW9ucyhkaWdpdHMgPSA0LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICBrbml0ci5kdXBsaWNhdGUubGFiZWwgPSAiYWxsb3ciKQpnZ3Bsb3QyOjp0aGVtZV9zZXQoZ2dwbG90Mjo6dGhlbWVfYncoYmFzZV9zaXplID0gMTApKQpzZXQuc2VlZCgxKQp2ZXIgPC0gIjIwMTkwOTE2IgpybWRfZmlsZSA8LSAiaW5kZXguUm1kIgpgYGAKCk5vdGUsIEkgYW0gdXNpbmcgbXkgcHJldmlvdXMgd29ya3NoZWV0IGZyb20gd2hlbiBsYXN0IEkgd29ya2VkIHdpdGgKUmV6aWEgYXMgYSB0ZW1wbGF0ZSBmb3IgdGhpcyAoSSBjb3BpZWQgaXQgZnJvbSB0aGVyZSBhbmQgYW0gbW9kaWZ5aW5nCml0IG5vdykuCgojIFMucHlvZ2VuZXMgNTQ0OCByb2ZBIFJOQVNlcSB2ZXJzaW9uOiBgciB2ZXJgCgojIyBQcmVwcm9jZXNzaW5nCgpJIHVzZWQgbXkgY3lvYSB0b29sIHRvIHByb2Nlc3MgdGhlc2Ugc2FtcGxlcyBieSBkb2luZyB0aGUgZm9sbG93aW5nOgoKMS4gIENvcHlpbmcgdGhlIGRhdGEgZnJvbSB0aGUgc2VxdWVuY2VyIGludG8gdGhlIGRpcmVjdG9yeSAncHJlcHJvY2Vzc2luZy8nCjIuICBVc2VkIGEgc2xpZ2h0bHkgaW52b2x2ZWQgc2hlbGwgY29tbWFuZCB0byBjcmVhdGUgYSBkaXJlY3RvcnkgZm9yCiAgICBlYWNoIHNhbXBsZSBhbmQgY29weSB0aGUgcmVhZHMgZm9yIGl0IHRvIHRoZSAndW5wcm9jZXNzZWQvJwogICAgc3ViZGlyZWN0b3J5IHdpdGhpbiBpdC4KMy4gIGludm9rZWQgdGhlIGZvbGxvd2luZzoKCmBgYHtiYXNoLCBldmFsPUZBTFNFfQpjZCBwcmVwcm9jZXNzaW5nCnN0YXJ0PSQocHdkKQpmb3IgaSBpbiAkKC9iaW4vbHMgLWQgLi8qKQpkbwogIGNkICRpCiAgcm0gLXJmIG91dHB1dHMgc2NyaXB0cwogIGN5b2EgLS10YXNrIHBpcGUgLS1tZXRob2QgcHJuYXMgLS1zcGVjaWVzIHNweW9nZW5lc181NDQ4IFwKICAgICAgIC0tZ2ZmX3R5cGUgZ2VuZSAtLWdmZl90YWcgbG9jdXNfdGFnIFwKICAgICAgIC0taW5wdXQgJCgvYmluL2xzIHVucHJvY2Vzc2VkLyogfCB0ciAnXG4nICc6JyB8IHNlZCAncy86JC8vZycpCiAgY2QgJHN0YXJ0CmRvbmUKYGBgCgpUaGUgYWJvdmUgZm9yIGxvb3AgZ29lcyBpbnRvIGVhY2ggc2FtcGxlIGFuZCBkb2VzIHRoZSBmb2xsb3dpbmc6CgoxLiAgVHJpbXMgdGhlIGRhdGEsIGhlYXZpbHkgY29tcHJlc3NlcyB0aGUgb3V0cHV0cy4KMi4gIFJ1bnMgZmFzdHFjCjMuICBSdW5zIGhpc2F0MiB1c2luZyBteSBzcHlvZ2VuZXNfNTQ0OCBpbmRpY2VzLgo0LiAgQ29udmVydHMgdGhlIHNhbSBhbGlnbm1lbnQgdG8gc29ydGVkL2luZGV4ZWQgYmFtLgo1LiAgTWFrZXMgYSBjb3VwbGUgb2YgZXh0cmEgY29waWVzIG9mIGl0IHdpdGggc29tZSBmaWx0ZXJzLgo2LiAgQ29tcHJlc3NlcyB0aGUgYWxpZ25lZC91bmFsaWduZWQgcmVhZHMuCjcuICBSdW5zIGh0c2VxLWNvdW50IG9uIHRoZSBhbGlnbm1lbnRzIHRvIGNvdW50IHJlYWRzL2dlbmUuCgogIE5vdGUgdGhlIGZvbGxvd2luZyBzdGVwcyB3ZXJlIG5vdCBhY3R1YWxseSBydW4gYmVjYXVzZSBJIGhhZCBhCiAgc3BlZWxpbmcgZXJyb3IuICBCdXQgc2luY2UgdGhleSBhcmUgbm90IG5lY2Vzc2FyeSBmb3IgdGhlIGV4cGxpY2l0bHkKICBSTkFTZXEgYW5hbHlzZXMgSSBmaXJzdCB3YW50IHRvIGRvLCBJIGlnbm9yZWQgaXQuICBJIGFtIGN1cmlvdXMKICB0aG91Z2ggdG8gc2VlIGlmIHRoZXJlIGFyZSBvdGhlciBtdXRhdGlvbnMgaW4gdGhlc2Ugc3RyYWlucywgc28gSQogIHdpbGwgbGlrZWx5IHJ1biB0aG9zZSBwb3J0aW9ucyBtYW51YWxseS4KCjguICBSdW5zIGZyZWViYXllcyBvbiB0aGUgYWxpZ25tZW50cyB0byBsb29rIGZvciB2YXJpYW50cy4KOS4gIFNvcnRzL2NvbXByZXNzZXMgdGhlIGZyZWViYXllcyBvdXRwdXQuCjEwLiBEb2VzIHNvbWUgcGFyc2luZyBvZiB0aGUgZnJlZWJheWVzIG91dHB1dCBhbmQgcHJvdmlkZXMgc29tZSB0YWJsZXMKICAgIGFib3V0IHdoZXJlIG11dGF0aW9ucyB3ZXJlIG9ic2VydmVkLgoKIyMgQ29sbGVjdCBhbm5vdGF0aW9uIGluZm9ybWF0aW9uCgpTYW1lIHR3byBwcmltYXJ5IGFubm90YXRpb24gc291cmNlcywgdGhlIGdmZiBmaWxlIHVzZWQgZm9yIG1hcHBpbmcvY291bnRpbmcsCmFuZCBtaWNyb2Jlc29ubGluZS5vcmcuICBOb3RlIHRoYXQgc2luY2UgSSBtb3ZlZCB0byBqdXN0IGRvd25sb2FkaW5nIHRoZQptYXRlcmlhbCBmcm9tIHRoZSB3ZWIgaW50ZXJmYWNlLCBJIG5vIGxvbmdlciBoYXZlIGEgaGFuZHkgbWV0aG9kIHRvIGdldCB0aGUKdGF4b24gSUQsIHNvIEkgZ28gdGhlcmUgYW5kIGh1bnQgZG93biB0aGUgdGF4SWQgbWFudWFsbHkuCgpOb3cgdGhhdCBJIGFtIHRoaW5raW5nIGFib3V0IGl0LCBteSA1NDQ4IGdlbm9tZS9hbm5vdGF0aW9ucyBhcmUga2luZApvZiBvbGQsIEkgd2lsbCBhc2sgYW5kIGNoZWNrIHRvIHNlZSBpZiB0aGVyZSBpcyBhbnl0aGluZyBuZXdlci4KCkFsc28sIDU0NDggZG9lcyBub3QgaGF2ZSBhbiBlbnRyeSBhdCBtaWNyb2Jlc29ubGluZS5vcmcsIGEgZmFjdCB3aGljaApJIGZvcmdvdC4gIEkgbmVlZCB0byBnbyBwb2tpbmcgaW4gbXkgbm90ZXMgdG8gcmVjb25uZWN0IDUwMDUgYW5kIDU0NDguCgpgYGB7ciBhbm5vdGF0aW9ufQpnZmZfYW5ub3QgPC0gbG9hZF9nZmZfYW5ub3RhdGlvbnMoInJlZmVyZW5jZS9zcHlvZ2VuZXNfNTQ0OC5nZmYiLCB0eXBlID0gImdlbmUiKQpyb3duYW1lcyhnZmZfYW5ub3QpIDwtIGdmZl9hbm5vdFtbImxvY3VzX3RhZyJdXQpoZWFkKGdmZl9hbm5vdCkKCm1nYXNfZGF0YSA8LSBsb2FkX2dlbmJhbmtfYW5ub3RhdGlvbnMoYWNjZXNzaW9uPSJDUDAwODc3NiIpCmdlbm9tZV9zaXplIDwtIEdlbm9taWNSYW5nZXM6OndpZHRoKG1nYXNfZGF0YSRzZXEpICAjIyBUaGlzIGZhaWxzIG9uIHRyYXZpcz8KbWdhc19jZHMgPC0gYXMuZGF0YS5mcmFtZShtZ2FzX2RhdGEkY2RzKQojIyBHZXQgcmlkIG9mIGFtaW5vIGFjaWQgc2VxdWVuY2UKcm93bmFtZXMobWdhc19jZHMpIDwtIG1nYXNfY2RzW1sibG9jdXNfdGFnIl1dCndhbnRlZCA8LSAhIGNvbG5hbWVzKG1nYXNfY2RzKSAlaW4lIGMoInRyYW5zbGF0aW9uIiwgInR5cGUiLCAic3RyYW5kIiwgInNlcW5hbWVzIiwgInN0YXJ0IiwgImVuZCIsICJsb2N1c190YWciLCAibm90ZSIsICJnZW5lIiwgImdlbmVfc3lub255bSIsICJ3aWR0aCIpCm1nYXNfY2RzIDwtIG1nYXNfY2RzWywgd2FudGVkXQojIyBBbmQgRUNfbnVtYmVyIGJlY2F1c2Ugd3RmIGlzIHRoYXQ/Cm1nYXNfYW5ub3QgPC0gbWVyZ2UobWdhc19jZHMsIGdmZl9hbm5vdCwgYnk9InJvdy5uYW1lcyIpCnJvd25hbWVzKG1nYXNfYW5ub3QpIDwtIG1nYXNfYW5ub3RbWyJSb3cubmFtZXMiXV0KbWdhc19hbm5vdFtbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCmBgYAoKIyMgQ3JlYXRlIGV4cHJlc3Npb25TZXQKCk5vdGUgdGhhdCBJIGRpZCB0aGUgZm9sbG93aW5nIHRvIHRoZSBzYW1wbGUgc2hlZXQgcHJvdmlkZWQgYnkKRHIuIE1jSXZlcjoKCjEuICBDaGFuZ2VkIHRoZSBkUF9SMjBfNCBzYW1wbGUgdG8gZFBfUjIwXzIgKFRoZSBvcmlnaW5hbCBzYW1wbGUgbmFtZQogICAgaXMgc3RpbGwgdGhlcmUgYXJlIHRoZSBvcmlnaW5hbCBjb2x1bW4pLgoyLiAgQWRkZWQgY29sdW1ucyBhdCB0aGUgZW5kIGNvbnRhaW5pbmcgdGhlIGNvdW50IHRhYmxlIGxvY2F0aW9ucy4KMy4gIEFkZGVkIGNvbHVtbnMgJ3Nob3J0X21lZGlhJywgJ2dyb3d0aF9waGFzZScsICdnZW5vdHlwZScgd2hpY2gKICAgIGhvcGVmdWxseSBjb250YWluIHRoZSByZWxldmFudCBtZXRhZGF0YSBleHRyYWN0ZWQgZnJvbSB0aGUgc2FtcGxlCiAgICBkZXNjcmlwdGlvbnMuCjQuICBBZGRlZCBhIGNvbHVtbiAnRXhwZXJpbWVudCcgd2hpY2ggaXMgZWl0aGVyICdyb2ZBJyBvciAncGR4UicuCgpgYGB7ciBleHB0fQpyb2ZhX2V4cHQgPC0gY3JlYXRlX2V4cHQobWV0YWRhdGEgPSAic2FtcGxlX3NoZWV0cy9hbGxfc2FtcGxlcy54bHN4IiwKICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9pbmZvID0gZ2ZmX2Fubm90LCBmaWxlX2NvbHVtbiA9ICJzcHlvZ2VuZXM1NDQ4Z2VuZWNvdW50cyIpICU+JQogIHN1YnNldF9leHB0KHN1YnNldD0iZXhwZXJpbWVudD09J3JvZkEnIikgJT4lCiAgc2V0X2V4cHRfY29uZGl0aW9ucyhmYWN0PSJnZW5vdHlwZSIpICU+JQogIHNldF9leHB0X2JhdGNoZXMoZmFjdD0iZ3Jvd3RocGhhc2UiKQpgYGAKCkkgaGF2ZSAzNiBzYW1wbGVzIHRvIHBsYXkgd2l0aCwgbGV0IHVzIHNlZSB3aGF0IHRoZXkgbG9vayBsaWtlLgoKIyMgUG9rZSBleHByZXNzaW9uU2V0CgpgYGB7ciBwb2tlfQpyb2ZhX2xpYnNpemUgPC0gcGxvdF9saWJzaXplKHJvZmFfZXhwdCkKcm9mYV9saWJzaXplJHBsb3QKCnJvZmFfZmlsdGVyX3Bsb3QgPC0gcGxvdF9saWJzaXplX3ByZXBvc3Qocm9mYV9leHB0KQpyb2ZhX2ZpbHRlcl9wbG90JGNvdW50X3Bsb3QKcm9mYV9maWx0ZXJfcGxvdCRsb3dnZW5lX3Bsb3QKCnJvZmFfbm9uemVybyA8LSBwbG90X25vbnplcm8ocm9mYV9leHB0KQpyb2ZhX25vbnplcm8kcGxvdAojIyBhd2Vzb21lLCB0aGVyZSBpcyBhIHJhbmdlIG9mIH4gMjUgZ2VuZXMuCgp3cml0dGVuIDwtIHdyaXRlX2V4cHQocm9mYV9leHB0LCBleGNlbCA9IGdsdWU6OmdsdWUoImV4Y2VsL3JvZkFfZXhwdC12e3Zlcn0ueGxzeCIpKQpgYGAKCiMjIFF1aWNrIHZpc3VhbGl6YXRpb25zCgojIyMgV2l0aG91dCBjb25zaWRlcmluZyB0aW1lCgpMZXQgdXMgc3RhcnQgd2l0aCBzb21lIHZpZXdzIG9mIHRoZSBkYXRhIHdpdGhvdXQgdGhpbmtpbmcgYWJvdXQgYmF0Y2ggZWZmZWN0cy4KCmBgYHtyIHZpZXdfbm9iYXRjaH0Kcm9mYV9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KHJvZmFfZXhwdCwgZmlsdGVyPVRSVUUsIG5vcm09InF1YW50IiwgY29udmVydD0iY3BtIiwgdHJhbnNmb3JtPSJsb2cyIikKcm9mYV9wY2EgPC0gcGxvdF9wY2Eocm9mYV9ub3JtKQpyb2ZhX3BjYSRwbG90Cgpyb2ZhX2hlYXRtYXAgPC0gcGxvdF9kaXNoZWF0KHJvZmFfbm9ybSkKcm9mYV9oZWF0bWFwJHBsb3QKYGBgCgojIyMgQ29uc2lkZXJpbmcgdGltZQoKUmVwZWF0IHRoZSBwcmV2aW91cyBwbG90cywgYnV0IHRoaXMgdGltZSB1c2luZyBsaW1tYSdzIGJhdGNoIHJlbW92YWwKbWV0aG9kICh3aGljaCBpcyBqdXN0IGEgcmVzaWR1YWxzKS4KCmBgYHtyIG5iX3ZpZXd9CnJvZmFfdGltZSA8LSBzZXRfZXhwdF9jb25kaXRpb25zKHJvZmFfZXhwdCwgZmFjdD0iZ3Jvd3RocGhhc2UiKSAlPiUKICBzZXRfZXhwdF9iYXRjaGVzKGZhY3Q9Imdlbm90eXBlIikKCnJvZmFfdGltZV9ub3JtIDwtIG5vcm1hbGl6ZV9leHB0KHJvZmFfdGltZSwgZmlsdGVyPVRSVUUsIG5vcm09InF1YW50IiwgY29udmVydD0iY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhbnNmb3JtPSJsb2cyIikKcm9mYV90aW1lX3BjYSA8LSBwbG90X3BjYShyb2ZhX3RpbWVfbm9ybSkKcm9mYV90aW1lX3BjYSRwbG90Cgpyb2ZhX3RpbWVfbmIgPC0gbm9ybWFsaXplX2V4cHQocm9mYV90aW1lLCBmaWx0ZXI9VFJVRSwgbm9ybT0icXVhbnQiLCBjb252ZXJ0PSJjcG0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYW5zZm9ybT0ibG9nMiIsIGJhdGNoPSJsaW1tYSIpCnJvZmFfdGltZV9uYl9wY2EgPC0gcGxvdF9wY2Eocm9mYV90aW1lX25iKQpyb2ZhX3RpbWVfbmJfcGNhJHBsb3QKYGBgCgpXZWxsLCBpdCBpcyBwcmV0dHkgY2xlYXIgdGhhdCB0aW1lIGlzIHRoZSBkb21pbmFudCBmYWN0b3IuCgojIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBhbmFseXNlcwoKSSBhbSBnb2luZyB0byBkbyB0aGUgREUgaW4gNCBzZXBhcmF0ZSBwaWVjZXM6CgoxLiAgT25seSBjb21wYXJlIHN0cmFpbnMKMi4gIE9ubHkgY29tcGFyZSB0aW1lcwozLiAgQ29tcGFyZSB0aGUgY29uY2F0ZW5hdGlvbiBvZiBzdHJhaW5zIGFuZCB0aW1lcy4KCiMjIyBTdHJhaW4gb25seSBjb21wYXJpc29ucwoKYGBge3IgcGFpcndpc2Vfc3RyYWlufQpzdHJhaW5fZGUgPC0gYWxsX3BhaXJ3aXNlKHJvZmFfZXhwdCwgbW9kZWxfYmF0Y2g9VFJVRSwgZmlsdGVyPVRSVUUpCnN0cmFpbl9rZWVwZXJzIDwtIGxpc3QoCiAgICAiZGVsdGFfd3QiID0gYygiZGVsdGEiLCAiV1QiKSwKICAgICJyZXZlcnRfd3QiID0gYygicmV2ZXJ0IiwgIldUIiksCiAgICAiZGVsdGFfcmV2ZXJ0IiA9IGMoImRlbHRhIiwgInJldmVydCIpKQpzdHJhaW5fdGFibGVzIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogICAgc3RyYWluX2RlLCBrZWVwZXJzID0gc3RyYWluX2tlZXBlcnMsCiAgICBleGNlbCA9IGdsdWU6OmdsdWUoImV4Y2VsL3JvZmFfc3RyYWluX3RhYmxlcy12e3Zlcn0ueGxzeCIpKQpzdHJhaW5fc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgICBzdHJhaW5fdGFibGVzLAogICAgZXhjZWwgPSBnbHVlOjpnbHVlKCJleGNlbC9yb2ZhX3N0cmFpbl9zaWctdnt2ZXJ9Lnhsc3giKSkKYGBgCgojIyMgVGltZSBvbmx5IGNvbXBhcmlzb25zCgpgYGB7ciBwYWlyd2lzZV90aW1lfQp0aW1lX2RlIDwtIGFsbF9wYWlyd2lzZShyb2ZhX3RpbWUsIG1vZGVsX2JhdGNoPVRSVUUpCnRpbWVfa2VlcGVycyA8LSBsaXN0KAogICAgInRyYW5zaXRpb25fZXhwb25lbnRpYWwiID0gYygidHJhbnNpdGlvbiIsICJleHBvbmVudGlhbCIpLAogICAgInN0YXRpb25hcnlfZXhwb25lbnRpYWwiID0gYygic3RhdGlvbmFyeSIsICJleHBvbmVudGlhbCIpLAogICAgInN0YXRpb25hcnlfdHJhbnNpdGlvbiIgPSBjKCJzdGF0aW9uYXJ5IiwgInRyYW5zaXRpb24iKSkKdGltZV90YWJsZXMgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgICB0aW1lX2RlLCBrZWVwZXJzID0gdGltZV9rZWVwZXJzLAogICAgZXhjZWwgPSBnbHVlOjpnbHVlKCJleGNlbC9yb2ZhX3RpbWVfdGFibGVzLXZ7dmVyfS54bHN4IikpCnRpbWVfc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgICB0aW1lX3RhYmxlcywKICAgIGV4Y2VsID0gZ2x1ZTo6Z2x1ZSgiZXhjZWwvcm9mYV90aW1lX3NpZy12e3Zlcn0ueGxzeCIpKQpgYGAKCiMjIyMgQ29tcGFyZSB0aW1lcyB2cyBzdHJhaW5zCgpJIGFsd2F5cyB3b3JyeSB0aGF0IGNvbXBhcmluZyBhIGRhdGEgc2V0IGFjcm9zcyBtdWx0aXBsZSBjb25kaXRpb25zCnJlc3VsdHMgaW4gbm90IHdoYXQgSSB0aGluayBpdCB3aWxsLiAgTGV0IHVzIHRoZXJlZm9yZSBwbG90IHRoZSBsb2dGQ3MKb2YgbGlrZWx5IGNvbnRyYXN0cyBhZ2FpbnN0IGVhY2ggb3RoZXIuCgpgYGB7ciBjb21wYXJlX3RpbWVzX3ZzX3N0cmFpbnN9CnhfYXhpcyA8LSB0aW1lX3RhYmxlc1tbImRhdGEiXV1bWyJ0cmFuc2l0aW9uX2V4cG9uZW50aWFsIl1dWywgYygiZGVzZXFfbG9nZmMiLCAiZGVzZXFfYWRqcCIpXQp5X2F4aXMgPC0gc3RyYWluX3RhYmxlc1tbImRhdGEiXV1bWyJkZWx0YV93dCJdXVssIGMoImRlc2VxX2xvZ2ZjIiwgImRlc2VxX2FkanAiKV0KYm90aCA8LSBtZXJnZSh4X2F4aXMsIHlfYXhpcywgYnk9InJvdy5uYW1lcyIpCnJvd25hbWVzKGJvdGgpIDwtIGJvdGhbWyJSb3cubmFtZXMiXV0KYm90aFtbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCmNvci50ZXN0KGJvdGhbWyJkZXNlcV9sb2dmYy54Il1dLCBib3RoW1siZGVzZXFfbG9nZmMueSJdXSwgbWV0aG9kPSJzcGVhcm1hbiIpCnBsb3R0ZWQgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihib3RoWywgYygiZGVzZXFfbG9nZmMueCIsICJkZXNlcV9sb2dmYy55IildKQpwbG90dGVkJHNjYXR0ZXIKYGBgCgojIyMgSW50ZXJhY3Rpb24gbW9kZWwKCkkgd2FudCB0byBtYWtlIHN1cmUgdGhhdCBteSBtZXRob2RzIG9mIHBlcmZvcm1pbmcgaW50ZXJhY3Rpb24gbW9kZWxzCndvcmsgYXMgSSB0aGluayBpdCBkb2VzLCBhbmQgdGhpcyBkYXRhIHNldCBsb29rcyB0byBtZSB0byBiZSBhIHBlcmZlY3QKcGxhY2UgdG8gdGVzdCB0aGF0LgoKYGBge3IgY29tYmluZWRfZGUsIGV2YWw9RkFMU0V9CmNvbWJpbmVkX2ZhY3RvcnMgPC0gcGFzdGUwKHBEYXRhKHJvZmFfZXhwdClbWyJnZW5vdHlwZSJdXSwgIl8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwRGF0YShyb2ZhX2V4cHQpW1siZ3Jvd3RocGhhc2UiXV0pCmNvbWJpbmVkX2V4cHQgPC0gc2V0X2V4cHRfY29uZGl0aW9ucyhyb2ZhX2V4cHQsIGZhY3Q9Y29tYmluZWRfZmFjdG9ycykgJT4lCgpjb21iaW5lZF9kZSA8LSBhbGxfcGFpcndpc2UoY29tYmluZWRfZXhwdCwgbW9kZWxfYmF0Y2g9InN2YXNlcSIsIGZpbHRlcj1UUlVFKQoKY29tYmluZWRfa2VlcGVycyA8LSBsaXN0KAogICAgImV4cG9uZW50aWFsX2RlbHRhX3ZzX3d0IiA9IGMoImRlbHRhZXhwb25lbnRpYWwiLCAiV1RleHBvbmVudGlhbCIpLAogICAgImV4cG9uZW50aWFsX2RlbHRhX3ZzX3JldmVydCIgPSBjKCJkZWx0YWV4cG9uZW50aWFsIiwgInJldmVydGV4cG9uZW50aWFsIiksCiAgICAic3RhdGlvbmFyeV9kZWx0YV92c193dCIgPSBjKCJkZWx0YXN0YXRpb25hcnkiLCAiV1RzdGF0aW9uYXJ5IiksCiAgICAic3RhdGlvbmFyeV9kZWx0YV92c19yZXZlcnQiID0gYygiZGVsdGFzdGF0aW9uYXJ5IiwgInJldmVydHN0YXRpb25hcnkiKSwKICAgICJ0cmFuc2l0aW9uX2RlbHRhX3ZzX3d0IiA9IGMoImRlbHRhdHJhbnNpdGlvbiIsICJXVHRyYW5zaXRpb24iKSwKICAgICJ0cmFuc2l0aW9uX2RlbHRhX3ZzX3JldmVydCIgPSBjKCJkZWx0YXRyYW5zaXRpb24iLCAicmV2ZXJ0dHJhbnNpdGlvbiIpLAogICAgIldUX2V4cG9uZW50aWFsX3ZzX3RyYW5zaXRpb24iID0gYygiV1RleHBvbmVudGlhbCIsICJXVHRyYW5zaXRpb24iKSwKICAgICJkZWx0YV9leHBvbmVudGlhbF92c190cmFuc2l0aW9uIiA9IGMoImRlbHRhZXhwb25lbnRpYWwiLCAiZGVsdGF0cmFuc2l0aW9uIiksCiAgICAicmV2ZXJ0X2V4cG9uZW50aWFsX3ZzX3RyYW5zaXRpb24iID0gYygicmV2ZXJ0ZXhwb25lbnRpYWwiLCAicmV2ZXJ0dHJhbnNpdGlvbiIpLAogICAgIldUX3N0YXRpb25hcnlfdnNfdHJhbnNpdGlvbiIgPSBjKCJXVHN0YXRpb25hcnkiLCAiV1R0cmFuc2l0aW9uIiksCiAgICAiZGVsdGFfc3RhdGlvbmFyeV92c190cmFuc2l0aW9uIiA9IGMoImRlbHRhc3RhdGlvbmFyeSIsICJkZWx0YXRyYW5zaXRpb24iKSwKICAgICJyZXZlcnRfc3RhdGlvbmFyeV92c190cmFuc2l0aW9uIiA9IGMoInJldmVydHN0YXRpb25hcnkiLCAicmV2ZXJ0dHJhbnNpdGlvbiIpLAogICAgIldUX3N0YXRpb25hcnlfdnNfZXhwb25lbnRpYWwiID0gYygiV1RzdGF0aW9uYXJ5IiwgIldUZXhwb25lbnRpYWwiKSwKICAgICJkZWx0YV9zdGF0aW9uYXJ5X3ZzX2V4cG9uZW50aWFsIiA9IGMoImRlbHRhc3RhdGlvbmFyeSIsICJkZWx0YWV4cG9uZW50aWFsIiksCiAgICAicmV2ZXJ0X3N0YXRpb25hcnlfdnNfZXhwb25lbnRpYWwiID0gYygicmV2ZXJ0c3RhdGlvbmFyeSIsICJyZXZlcnRleHBvbmVudGlhbCIpKQoKCmNvbWJpbmVkX3RhYmxlcyA8LSBjb21iaW5lX2RlX3RhYmxlcygKICAgIGNvbWJpbmVkX2RlLCBrZWVwZXJzID0gY29tYmluZWRfa2VlcGVycywKICAgIGV4Y2VsID0gZ2x1ZTo6Z2x1ZSgiZXhjZWwvcm9mYV9jb21iaW5lZF90YWJsZXMtdnt2ZXJ9Lnhsc3giKSkKY29tYmluZWRfc2lnIDwtIGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgICBjb21iaW5lZF90YWJsZXMsCiAgICBleGNlbCA9IGdsdWU6OmdsdWUoImV4Y2VsL3JvZmFfY29tYmluZWRfc2lnLXZ7dmVyfS54bHN4IikpCmBgYAoKCmBgYHtyIHNhdmVtZX0KaWYgKCFpc1RSVUUoZ2V0MCgic2tpcF9sb2FkIikpKSB7CiAgcGFuZGVyOjpwYW5kZXIoc2Vzc2lvbkluZm8oKSkKICBtZXNzYWdlKHBhc3RlMCgiVGhpcyBpcyBocGdsdG9vbHMgY29tbWl0OiAiLCBnZXRfZ2l0X2NvbW1pdCgpKSkKICB0aGlzX3NhdmUgPC0gcGFzdGUwKGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLCByZXBsYWNlID0gIiIsIHggPSBybWRfZmlsZSksICItdiIsIHZlciwgIi5yZGEueHoiKQogIG1lc3NhZ2UocGFzdGUwKCJTYXZpbmcgdG8gIiwgdGhpc19zYXZlKSkKICB0bXAgPC0gc20oc2F2ZW1lKGZpbGVuYW1lID0gdGhpc19zYXZlKSkKfQpgYGAK