S.pyogenes 5448 pdxR 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.137 seconds ]
## 2022-04-25 16:26:05 Starting creation of gene GRanges
## 2022-04-25 16:26:08 Starting creation of CDS GRanges
## 2022-04-25 16:26:16 Starting creation of exon GRanges
## No exons read from genbank file. Assuming sections of CDS are full exons
## 2022-04-25 16:26:17 Starting creation of variant VRanges
## 2022-04-25 16:26:17 Starting creation of transcript GRanges
## No transcript features (mRNA) found, using spans of CDSs
## 2022-04-25 16:26:18 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 16:26:19 - Done creating GenBankRecord object [ 13.585 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’.
pdxr_expt <- create_expt(metadata = "sample_sheets/all_samples.xlsx",
gene_info = gff_annot, file_column = "spyogenes5448genecounts") %>%
subset_expt(subset="experiment=='pdxR'") %>%
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
pdxr_libsize <- plot_libsize(pdxr_expt)
pdxr_libsize$plot

pdxr_filter_plot <- plot_libsize_prepost(pdxr_expt)
pdxr_filter_plot$count_plot

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

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

## awesome, there is a range of ~ 10 genes!
written <- write_expt(pdxr_expt, excel = glue::glue("excel/pdxr_expt-v{ver}.xlsx"))
## 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:14 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.
pdxr_norm <- normalize_expt(pdxr_expt, filter=TRUE, norm="quant", convert="cpm", transform="log2")
## Removing 31 low-count genes (1783 remaining).
## transform_counts: Found 12 values equal to 0, adding 1 to the matrix.
pdxr_pca <- plot_pca(pdxr_norm)
pdxr_pca$plot
## Warning in MASS::cov.trob(data[, vars]): Probable convergence failure
## Warning in MASS::cov.trob(data[, vars]): Probable convergence failure

pdxr_heatmap <- plot_disheat(pdxr_norm)
pdxr_heatmap$plot

pdxr_sm <- plot_sm(pdxr_norm)
## Performing correlation.
pdxr_sm$plot

Considering time
Repeat the previous plots, but this time using limma’s batch removal method (which is just a residuals).
pdxr_nb <- normalize_expt(pdxr_expt, filter=TRUE, norm="quant", convert="cpm",
transform="log2", batch="limma")
## Removing 31 low-count genes (1783 remaining).
## If you receive a warning: 'NANs produced', one potential reason is that the data was quantile normalized.
## Setting 27 low elements to zero.
## transform_counts: Found 27 values equal to 0, adding 1 to the matrix.
pdxr_nb_pca <- plot_pca(pdxr_nb)
pdxr_nb_pca$plot

pdxr_nb_heatmap <- plot_corheat(pdxr_nb)
pdxr_nb_heatmap$plot

Well, it is pretty clear that time is the dominant factor.
Differential Expression analyses
I am going to do the DE in 3 separate pieces:
- Only compare strains
- Only compare times
- Compare the concatenation of strains and times.
Strain only comparisons
strain_de <- all_pairwise(pdxr_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"),
"complement_wt" = c("complement", "WT"),
"delta_complement" = c("delta", "complement"))
strain_tables <- combine_de_tables(
strain_de, keepers = strain_keepers,
excel = glue::glue("excel/pdxr_strain_tables-v{ver}.xlsx"))
## Deleting the file excel/pdxr_strain_tables-v20190916.xlsx before writing the tables.
strain_sig <- extract_significant_genes(
strain_tables,
excel = glue::glue("excel/pdxr_strain_sig-v{ver}.xlsx"))
## Deleting the file excel/pdxr_strain_sig-v20190916.xlsx before writing the tables.
Time only comparisons
pdxr_time <- set_expt_conditions(pdxr_expt, fact="growthphase") %>%
set_expt_batches(fact="genotype")
time_de <- all_pairwise(pdxr_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(
"e20m_start" = c("e20m", "start"),
"e60m_e20m" = c("e60m", "e20m"),
"e60m_start" = c("e60m", "start"))
time_tables <- combine_de_tables(
time_de, keepers = time_keepers,
excel = glue::glue("excel/pdxr_time_tables-v{ver}.xlsx"))
## Deleting the file excel/pdxr_time_tables-v20190916.xlsx before writing the tables.
time_sig <- extract_significant_genes(
time_tables,
excel = glue::glue("excel/pdxr_time_sig-v{ver}.xlsx"))
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"]][["start_e20m"]][, 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")
## Error in cor.test.default(both[["deseq_logfc.x"]], both[["deseq_logfc.y"]], : 'x' must be a numeric vector
plotted <- plot_linear_scatter(both[, c("deseq_logfc.x", "deseq_logfc.y")])
## Error in `[.data.frame`(both, , c("deseq_logfc.x", "deseq_logfc.y")): undefined columns selected
plotted$scatter
## Error in eval(expr, envir, enclos): object 'plotted' not found
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(pdxr_expt)[["genotype"]], "_",
pData(pdxr_expt)[["growthphase"]])
combined_expt <- set_expt_conditions(pdxr_expt, fact=combined_factors) %>%
combined_de <- all_pairwise(combined_expt, model_batch="svaseq", filter=TRUE)
combined_keepers <- list(
"e20m_delta_vs_wt" = c("deltae20m", "WTe20m"),
"e20m_delta_vs_complement" = c("deltae20m", "complemente20m"),
"e60m_delta_vs_wt" = c("deltae60m", "WTe60m"),
"e60m_delta_vs_complement" = c("deltae60m", "complemente60m"),
"start_delta_vs_wt" = c("deltastart", "WTstart"),
"start_delta_vs_complement" = c("deltastart", "complementstart"),
"WT_e20m_vs_start" = c("WTe20m", "WTstart"),
"delta_e20m_vs_start" = c("deltae20m", "deltastart"),
"complement_e20m_vs_start" = c("complemente20m", "complementstart"),
"WT_e60m_vs_start" = c("WTe60m", "WTstart"),
"delta_e60m_vs_start" = c("deltae60m", "deltastart"),
"complement_e60m_vs_start" = c("complemente60m", "complementstart"),
"WT_e60m_vs_e20m" = c("WTe60m", "WTe20m"),
"delta_e60m_vs_e20m" = c("deltae60m", "deltae20m"),
"complement_e60m_vs_e20m" = c("complemente60m", "complemente20m"))
combined_tables <- combine_de_tables(
combined_de, keepers = combined_keepers,
excel = glue::glue("excel/pdxr_combined_tables-v{ver}.xlsx"))
combined_sig <- extract_significant_genes(
combined_tables,
excel = glue::glue("excel/pdxr_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
LS0tCnRpdGxlOiAiUy5weW9nZW5lcyA1NDQ4IHJuYXNlcSBjb21wYXJpbmcgcGR4UiBzdHJhaW5zIGFjcm9zcyB0aW1lLiIKYXV0aG9yOiAiYXRiIGFiZWxld0BnbWFpbC5jb20iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogaHRtbF9kb2N1bWVudDoKICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgY29kZV9mb2xkaW5nOiBzaG93CiAgZmlnX2NhcHRpb246IHRydWUKICBmaWdfaGVpZ2h0OiA3CiAgZmlnX3dpZHRoOiA3CiAgaGlnaGxpZ2h0OiBkZWZhdWx0CiAga2VlcF9tZDogZmFsc2UKICBtb2RlOiBzZWxmY29udGFpbmVkCiAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgc2VsZl9jb250YWluZWQ6IHRydWUKICB0aGVtZTogcmVhZGFibGUKICB0b2M6IHRydWUKICB0b2NfZmxvYXQ6CiAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQotLS0KCjxzdHlsZT4KICBib2R5IC5tYWluLWNvbnRhaW5lciB7CiAgICBtYXgtd2lkdGg6IDE2MDBweDsKICB9Cjwvc3R5bGU+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkoaHBnbHRvb2xzKQp0dCA8LSBkZXZ0b29sczo6bG9hZF9hbGwoIn4vaHBnbHRvb2xzIikKa25pdHI6Om9wdHNfa25pdCRzZXQocHJvZ3Jlc3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgd2lkdGggPSA5MCwKICAgICAgICAgICAgICAgICAgICAgZWNobyA9IFRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBmaWcud2lkdGggPSA4LAogICAgICAgICAgICAgICAgICAgICAgZmlnLmhlaWdodCA9IDgsCiAgICAgICAgICAgICAgICAgICAgICBkcGkgPSA5NikKb2xkX29wdGlvbnMgPC0gb3B0aW9ucyhkaWdpdHMgPSA0LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICBrbml0ci5kdXBsaWNhdGUubGFiZWwgPSAiYWxsb3ciKQpnZ3Bsb3QyOjp0aGVtZV9zZXQoZ2dwbG90Mjo6dGhlbWVfYncoYmFzZV9zaXplID0gMTApKQpzZXQuc2VlZCgxKQp2ZXIgPC0gIjIwMTkwOTE2IgpybWRfZmlsZSA8LSAiaW5kZXguUm1kIgpgYGAKCiMgSW1wb3J0YW50IG5vdGUKCkkgY29waWVkIG15IGluZGV4X3JvZkEgYW5kIGp1c3QgcGVyZm9ybWVkIHJlcGxhY2Utc3RyaW5nIG9uIGl0IGZvciB0aGUgdGltZXMgYW5kIHN0cmFpbnM6CgoxLiAgcy9yZXZlcnQvY29tcGxlbWVudC9nCjIuICBzL3RyYW5zaXRpb24vc3RhcnQvZwozLiAgcy9leHBvbmVudGlhbC9lMjBtL2cKNC4gIHMvc3RhdGlvbmFyeS9lNjBtL2cKCiMgUy5weW9nZW5lcyA1NDQ4IHBkeFIgUk5BU2VxIHZlcnNpb246IGByIHZlcmAKCiMjIFByZXByb2Nlc3NpbmcKCkkgdXNlZCBteSBjeW9hIHRvb2wgdG8gcHJvY2VzcyB0aGVzZSBzYW1wbGVzIGJ5IGRvaW5nIHRoZSBmb2xsb3dpbmc6CgoxLiAgQ29weWluZyB0aGUgZGF0YSBmcm9tIHRoZSBzZXF1ZW5jZXIgaW50byB0aGUgZGlyZWN0b3J5ICdwcmVwcm9jZXNzaW5nLycKMi4gIFVzZWQgYSBzbGlnaHRseSBpbnZvbHZlZCBzaGVsbCBjb21tYW5kIHRvIGNyZWF0ZSBhIGRpcmVjdG9yeSBmb3IKICAgIGVhY2ggc2FtcGxlIGFuZCBjb3B5IHRoZSByZWFkcyBmb3IgaXQgdG8gdGhlICd1bnByb2Nlc3NlZC8nCiAgICBzdWJkaXJlY3Rvcnkgd2l0aGluIGl0LgozLiAgaW52b2tlZCB0aGUgZm9sbG93aW5nOgoKYGBge2Jhc2gsIGV2YWw9RkFMU0V9CmNkIHByZXByb2Nlc3NpbmcKc3RhcnQ9JChwd2QpCmZvciBpIGluICQoL2Jpbi9scyAtZCAuLyopCmRvCiAgY2QgJGkKICBybSAtcmYgb3V0cHV0cyBzY3JpcHRzCiAgY3lvYSAtLXRhc2sgcGlwZSAtLW1ldGhvZCBwcm5hcyAtLXNwZWNpZXMgc3B5b2dlbmVzXzU0NDggXAogICAgICAgLS1nZmZfdHlwZSBnZW5lIC0tZ2ZmX3RhZyBsb2N1c190YWcgXAogICAgICAgLS1pbnB1dCAkKC9iaW4vbHMgdW5wcm9jZXNzZWQvKiB8IHRyICdcbicgJzonIHwgc2VkICdzLzokLy9nJykKICBjZCAkc3RhcnQKZG9uZQpgYGAKClRoZSBhYm92ZSBmb3IgbG9vcCBnb2VzIGludG8gZWFjaCBzYW1wbGUgYW5kIGRvZXMgdGhlIGZvbGxvd2luZzoKCjEuICBUcmltcyB0aGUgZGF0YSwgaGVhdmlseSBjb21wcmVzc2VzIHRoZSBvdXRwdXRzLgoyLiAgUnVucyBmYXN0cWMKMy4gIFJ1bnMgaGlzYXQyIHVzaW5nIG15IHNweW9nZW5lc181NDQ4IGluZGljZXMuCjQuICBDb252ZXJ0cyB0aGUgc2FtIGFsaWdubWVudCB0byBzb3J0ZWQvaW5kZXhlZCBiYW0uCjUuICBNYWtlcyBhIGNvdXBsZSBvZiBleHRyYSBjb3BpZXMgb2YgaXQgd2l0aCBzb21lIGZpbHRlcnMuCjYuICBDb21wcmVzc2VzIHRoZSBhbGlnbmVkL3VuYWxpZ25lZCByZWFkcy4KNy4gIFJ1bnMgaHRzZXEtY291bnQgb24gdGhlIGFsaWdubWVudHMgdG8gY291bnQgcmVhZHMvZ2VuZS4KCiAgTm90ZSB0aGUgZm9sbG93aW5nIHN0ZXBzIHdlcmUgbm90IGFjdHVhbGx5IHJ1biBiZWNhdXNlIEkgaGFkIGEKICBzcGVlbGluZyBlcnJvci4gIEJ1dCBzaW5jZSB0aGV5IGFyZSBub3QgbmVjZXNzYXJ5IGZvciB0aGUgZXhwbGljaXRseQogIFJOQVNlcSBhbmFseXNlcyBJIGZpcnN0IHdhbnQgdG8gZG8sIEkgaWdub3JlZCBpdC4gIEkgYW0gY3VyaW91cwogIHRob3VnaCB0byBzZWUgaWYgdGhlcmUgYXJlIG90aGVyIG11dGF0aW9ucyBpbiB0aGVzZSBzdHJhaW5zLCBzbyBJCiAgd2lsbCBsaWtlbHkgcnVuIHRob3NlIHBvcnRpb25zIG1hbnVhbGx5LgoKOC4gIFJ1bnMgZnJlZWJheWVzIG9uIHRoZSBhbGlnbm1lbnRzIHRvIGxvb2sgZm9yIHZhcmlhbnRzLgo5LiAgU29ydHMvY29tcHJlc3NlcyB0aGUgZnJlZWJheWVzIG91dHB1dC4KMTAuIERvZXMgc29tZSBwYXJzaW5nIG9mIHRoZSBmcmVlYmF5ZXMgb3V0cHV0IGFuZCBwcm92aWRlcyBzb21lIHRhYmxlcwogICAgYWJvdXQgd2hlcmUgbXV0YXRpb25zIHdlcmUgb2JzZXJ2ZWQuCgojIyBDb2xsZWN0IGFubm90YXRpb24gaW5mb3JtYXRpb24KClNhbWUgdHdvIHByaW1hcnkgYW5ub3RhdGlvbiBzb3VyY2VzLCB0aGUgZ2ZmIGZpbGUgdXNlZCBmb3IgbWFwcGluZy9jb3VudGluZywKYW5kIG1pY3JvYmVzb25saW5lLm9yZy4gIE5vdGUgdGhhdCBzaW5jZSBJIG1vdmVkIHRvIGp1c3QgZG93bmxvYWRpbmcgdGhlCm1hdGVyaWFsIGZyb20gdGhlIHdlYiBpbnRlcmZhY2UsIEkgbm8gbG9uZ2VyIGhhdmUgYSBoYW5keSBtZXRob2QgdG8gZ2V0IHRoZQp0YXhvbiBJRCwgc28gSSBnbyB0aGVyZSBhbmQgaHVudCBkb3duIHRoZSB0YXhJZCBtYW51YWxseS4KCk5vdyB0aGF0IEkgYW0gdGhpbmtpbmcgYWJvdXQgaXQsIG15IDU0NDggZ2Vub21lL2Fubm90YXRpb25zIGFyZSBraW5kCm9mIG9sZCwgSSB3aWxsIGFzayBhbmQgY2hlY2sgdG8gc2VlIGlmIHRoZXJlIGlzIGFueXRoaW5nIG5ld2VyLgoKQWxzbywgNTQ0OCBkb2VzIG5vdCBoYXZlIGFuIGVudHJ5IGF0IG1pY3JvYmVzb25saW5lLm9yZywgYSBmYWN0IHdoaWNoCkkgZm9yZ290LiAgSSBuZWVkIHRvIGdvIHBva2luZyBpbiBteSBub3RlcyB0byByZWNvbm5lY3QgNTAwNSBhbmQgNTQ0OC4KCmBgYHtyIGFubm90YXRpb259CmdmZl9hbm5vdCA8LSBsb2FkX2dmZl9hbm5vdGF0aW9ucygicmVmZXJlbmNlL3NweW9nZW5lc181NDQ4LmdmZiIsIHR5cGUgPSAiZ2VuZSIpCnJvd25hbWVzKGdmZl9hbm5vdCkgPC0gZ2ZmX2Fubm90W1sibG9jdXNfdGFnIl1dCmhlYWQoZ2ZmX2Fubm90KQoKbWdhc19kYXRhIDwtIGxvYWRfZ2VuYmFua19hbm5vdGF0aW9ucyhhY2Nlc3Npb249IkNQMDA4Nzc2IikKZ2Vub21lX3NpemUgPC0gR2Vub21pY1Jhbmdlczo6d2lkdGgobWdhc19kYXRhJHNlcSkgICMjIFRoaXMgZmFpbHMgb24gdHJhdmlzPwptZ2FzX2NkcyA8LSBhcy5kYXRhLmZyYW1lKG1nYXNfZGF0YSRjZHMpCiMjIEdldCByaWQgb2YgYW1pbm8gYWNpZCBzZXF1ZW5jZQpyb3duYW1lcyhtZ2FzX2NkcykgPC0gbWdhc19jZHNbWyJsb2N1c190YWciXV0Kd2FudGVkIDwtICEgY29sbmFtZXMobWdhc19jZHMpICVpbiUgYygidHJhbnNsYXRpb24iLCAidHlwZSIsICJzdHJhbmQiLCAic2VxbmFtZXMiLCAic3RhcnQiLCAiZW5kIiwgImxvY3VzX3RhZyIsICJub3RlIiwgImdlbmUiLCAiZ2VuZV9zeW5vbnltIiwgIndpZHRoIikKbWdhc19jZHMgPC0gbWdhc19jZHNbLCB3YW50ZWRdCiMjIEFuZCBFQ19udW1iZXIgYmVjYXVzZSB3dGYgaXMgdGhhdD8KbWdhc19hbm5vdCA8LSBtZXJnZShtZ2FzX2NkcywgZ2ZmX2Fubm90LCBieT0icm93Lm5hbWVzIikKcm93bmFtZXMobWdhc19hbm5vdCkgPC0gbWdhc19hbm5vdFtbIlJvdy5uYW1lcyJdXQptZ2FzX2Fubm90W1siUm93Lm5hbWVzIl1dIDwtIE5VTEwKYGBgCgojIyBDcmVhdGUgZXhwcmVzc2lvblNldAoKTm90ZSB0aGF0IEkgZGlkIHRoZSBmb2xsb3dpbmcgdG8gdGhlIHNhbXBsZSBzaGVldCBwcm92aWRlZCBieQpEci4gTWNJdmVyOgoKMS4gIENoYW5nZWQgdGhlIGRQX1IyMF80IHNhbXBsZSB0byBkUF9SMjBfMiAoVGhlIG9yaWdpbmFsIHNhbXBsZSBuYW1lCiAgICBpcyBzdGlsbCB0aGVyZSBhcmUgdGhlIG9yaWdpbmFsIGNvbHVtbikuCjIuICBBZGRlZCBjb2x1bW5zIGF0IHRoZSBlbmQgY29udGFpbmluZyB0aGUgY291bnQgdGFibGUgbG9jYXRpb25zLgozLiAgQWRkZWQgY29sdW1ucyAnc2hvcnRfbWVkaWEnLCAnZ3Jvd3RoX3BoYXNlJywgJ2dlbm90eXBlJyB3aGljaAogICAgaG9wZWZ1bGx5IGNvbnRhaW4gdGhlIHJlbGV2YW50IG1ldGFkYXRhIGV4dHJhY3RlZCBmcm9tIHRoZSBzYW1wbGUKICAgIGRlc2NyaXB0aW9ucy4KNC4gIEFkZGVkIGEgY29sdW1uICdFeHBlcmltZW50JyB3aGljaCBpcyBlaXRoZXIgJ3JvZkEnIG9yICdwZHhSJy4KCmBgYHtyIGV4cHR9CnBkeHJfZXhwdCA8LSBjcmVhdGVfZXhwdChtZXRhZGF0YSA9ICJzYW1wbGVfc2hlZXRzL2FsbF9zYW1wbGVzLnhsc3giLAogICAgICAgICAgICAgICAgICAgICAgICBnZW5lX2luZm8gPSBnZmZfYW5ub3QsIGZpbGVfY29sdW1uID0gInNweW9nZW5lczU0NDhnZW5lY291bnRzIikgJT4lCiAgc3Vic2V0X2V4cHQoc3Vic2V0PSJleHBlcmltZW50PT0ncGR4UiciKSAlPiUKICBzZXRfZXhwdF9jb25kaXRpb25zKGZhY3Q9Imdlbm90eXBlIikgJT4lCiAgc2V0X2V4cHRfYmF0Y2hlcyhmYWN0PSJncm93dGhwaGFzZSIpCmBgYAoKSSBoYXZlIDM2IHNhbXBsZXMgdG8gcGxheSB3aXRoLCBsZXQgdXMgc2VlIHdoYXQgdGhleSBsb29rIGxpa2UuCgojIyBQb2tlIGV4cHJlc3Npb25TZXQKCmBgYHtyIHBva2V9CnBkeHJfbGlic2l6ZSA8LSBwbG90X2xpYnNpemUocGR4cl9leHB0KQpwZHhyX2xpYnNpemUkcGxvdAoKcGR4cl9maWx0ZXJfcGxvdCA8LSBwbG90X2xpYnNpemVfcHJlcG9zdChwZHhyX2V4cHQpCnBkeHJfZmlsdGVyX3Bsb3QkY291bnRfcGxvdApwZHhyX2ZpbHRlcl9wbG90JGxvd2dlbmVfcGxvdAoKcGR4cl9ub256ZXJvIDwtIHBsb3Rfbm9uemVybyhwZHhyX2V4cHQpCnBkeHJfbm9uemVybyRwbG90CiMjIGF3ZXNvbWUsIHRoZXJlIGlzIGEgcmFuZ2Ugb2YgfiAxMCBnZW5lcyEKCndyaXR0ZW4gPC0gd3JpdGVfZXhwdChwZHhyX2V4cHQsIGV4Y2VsID0gZ2x1ZTo6Z2x1ZSgiZXhjZWwvcGR4cl9leHB0LXZ7dmVyfS54bHN4IikpCmBgYAoKIyMgUXVpY2sgdmlzdWFsaXphdGlvbnMKCiMjIyBXaXRob3V0IGNvbnNpZGVyaW5nIHRpbWUKCkxldCB1cyBzdGFydCB3aXRoIHNvbWUgdmlld3Mgb2YgdGhlIGRhdGEgd2l0aG91dCB0aGlua2luZyBhYm91dCBiYXRjaCBlZmZlY3RzLgoKYGBge3Igdmlld19ub2JhdGNofQpwZHhyX25vcm0gPC0gbm9ybWFsaXplX2V4cHQocGR4cl9leHB0LCBmaWx0ZXI9VFJVRSwgbm9ybT0icXVhbnQiLCBjb252ZXJ0PSJjcG0iLCB0cmFuc2Zvcm09ImxvZzIiKQpwZHhyX3BjYSA8LSBwbG90X3BjYShwZHhyX25vcm0pCnBkeHJfcGNhJHBsb3QKCnBkeHJfaGVhdG1hcCA8LSBwbG90X2Rpc2hlYXQocGR4cl9ub3JtKQpwZHhyX2hlYXRtYXAkcGxvdAoKcGR4cl9zbSA8LSBwbG90X3NtKHBkeHJfbm9ybSkKcGR4cl9zbSRwbG90CmBgYAoKIyMjIENvbnNpZGVyaW5nIHRpbWUKClJlcGVhdCB0aGUgcHJldmlvdXMgcGxvdHMsIGJ1dCB0aGlzIHRpbWUgdXNpbmcgbGltbWEncyBiYXRjaCByZW1vdmFsCm1ldGhvZCAod2hpY2ggaXMganVzdCBhIHJlc2lkdWFscykuCgpgYGB7ciBuYl92aWV3fQpwZHhyX25iIDwtIG5vcm1hbGl6ZV9leHB0KHBkeHJfZXhwdCwgZmlsdGVyPVRSVUUsIG5vcm09InF1YW50IiwgY29udmVydD0iY3BtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFuc2Zvcm09ImxvZzIiLCBiYXRjaD0ibGltbWEiKQpwZHhyX25iX3BjYSA8LSBwbG90X3BjYShwZHhyX25iKQpwZHhyX25iX3BjYSRwbG90CgpwZHhyX25iX2hlYXRtYXAgPC0gcGxvdF9jb3JoZWF0KHBkeHJfbmIpCnBkeHJfbmJfaGVhdG1hcCRwbG90CmBgYAoKV2VsbCwgaXQgaXMgcHJldHR5IGNsZWFyIHRoYXQgdGltZSBpcyB0aGUgZG9taW5hbnQgZmFjdG9yLgoKIyMgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gYW5hbHlzZXMKCkkgYW0gZ29pbmcgdG8gZG8gdGhlIERFIGluIDMgc2VwYXJhdGUgcGllY2VzOgoKMS4gIE9ubHkgY29tcGFyZSBzdHJhaW5zCjIuICBPbmx5IGNvbXBhcmUgdGltZXMKMy4gIENvbXBhcmUgdGhlIGNvbmNhdGVuYXRpb24gb2Ygc3RyYWlucyBhbmQgdGltZXMuCgojIyMgU3RyYWluIG9ubHkgY29tcGFyaXNvbnMKCmBgYHtyIHBhaXJ3aXNlX3N0cmFpbn0Kc3RyYWluX2RlIDwtIGFsbF9wYWlyd2lzZShwZHhyX2V4cHQsIG1vZGVsX2JhdGNoPVRSVUUsIGZpbHRlcj1UUlVFKQpzdHJhaW5fa2VlcGVycyA8LSBsaXN0KAogICAgImRlbHRhX3d0IiA9IGMoImRlbHRhIiwgIldUIiksCiAgICAiY29tcGxlbWVudF93dCIgPSBjKCJjb21wbGVtZW50IiwgIldUIiksCiAgICAiZGVsdGFfY29tcGxlbWVudCIgPSBjKCJkZWx0YSIsICJjb21wbGVtZW50IikpCnN0cmFpbl90YWJsZXMgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgICBzdHJhaW5fZGUsIGtlZXBlcnMgPSBzdHJhaW5fa2VlcGVycywKICAgIGV4Y2VsID0gZ2x1ZTo6Z2x1ZSgiZXhjZWwvcGR4cl9zdHJhaW5fdGFibGVzLXZ7dmVyfS54bHN4IikpCnN0cmFpbl9zaWcgPC0gZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICAgIHN0cmFpbl90YWJsZXMsCiAgICBleGNlbCA9IGdsdWU6OmdsdWUoImV4Y2VsL3BkeHJfc3RyYWluX3NpZy12e3Zlcn0ueGxzeCIpKQpgYGAKCiMjIyBUaW1lIG9ubHkgY29tcGFyaXNvbnMKCmBgYHtyIHBhaXJ3aXNlX3RpbWV9CnBkeHJfdGltZSA8LSBzZXRfZXhwdF9jb25kaXRpb25zKHBkeHJfZXhwdCwgZmFjdD0iZ3Jvd3RocGhhc2UiKSAlPiUKICBzZXRfZXhwdF9iYXRjaGVzKGZhY3Q9Imdlbm90eXBlIikKdGltZV9kZSA8LSBhbGxfcGFpcndpc2UocGR4cl90aW1lLCBtb2RlbF9iYXRjaD1UUlVFKQp0aW1lX2tlZXBlcnMgPC0gbGlzdCgKICAgICJlMjBtX3N0YXJ0IiA9IGMoImUyMG0iLCAic3RhcnQiKSwKICAgICJlNjBtX2UyMG0iID0gYygiZTYwbSIsICJlMjBtIiksCiAgICAiZTYwbV9zdGFydCIgPSBjKCJlNjBtIiwgInN0YXJ0IikpCnRpbWVfdGFibGVzIDwtIGNvbWJpbmVfZGVfdGFibGVzKAogICAgdGltZV9kZSwga2VlcGVycyA9IHRpbWVfa2VlcGVycywKICAgIGV4Y2VsID0gZ2x1ZTo6Z2x1ZSgiZXhjZWwvcGR4cl90aW1lX3RhYmxlcy12e3Zlcn0ueGxzeCIpKQp0aW1lX3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogICAgdGltZV90YWJsZXMsCiAgICBleGNlbCA9IGdsdWU6OmdsdWUoImV4Y2VsL3BkeHJfdGltZV9zaWctdnt2ZXJ9Lnhsc3giKSkKYGBgCgojIyMjIENvbXBhcmUgdGltZXMgdnMgc3RyYWlucwoKSSBhbHdheXMgd29ycnkgdGhhdCBjb21wYXJpbmcgYSBkYXRhIHNldCBhY3Jvc3MgbXVsdGlwbGUgY29uZGl0aW9ucwpyZXN1bHRzIGluIG5vdCB3aGF0IEkgdGhpbmsgaXQgd2lsbC4gIExldCB1cyB0aGVyZWZvcmUgcGxvdCB0aGUgbG9nRkNzCm9mIGxpa2VseSBjb250cmFzdHMgYWdhaW5zdCBlYWNoIG90aGVyLgoKYGBge3IgY29tcGFyZV90aW1lc192c19zdHJhaW5zfQp4X2F4aXMgPC0gdGltZV90YWJsZXNbWyJkYXRhIl1dW1sic3RhcnRfZTIwbSJdXVssIGMoImRlc2VxX2xvZ2ZjIiwgImRlc2VxX2FkanAiKV0KeV9heGlzIDwtIHN0cmFpbl90YWJsZXNbWyJkYXRhIl1dW1siZGVsdGFfd3QiXV1bLCBjKCJkZXNlcV9sb2dmYyIsICJkZXNlcV9hZGpwIildCmJvdGggPC0gbWVyZ2UoeF9heGlzLCB5X2F4aXMsIGJ5PSJyb3cubmFtZXMiKQpyb3duYW1lcyhib3RoKSA8LSBib3RoW1siUm93Lm5hbWVzIl1dCmJvdGhbWyJSb3cubmFtZXMiXV0gPC0gTlVMTApjb3IudGVzdChib3RoW1siZGVzZXFfbG9nZmMueCJdXSwgYm90aFtbImRlc2VxX2xvZ2ZjLnkiXV0sIG1ldGhvZD0ic3BlYXJtYW4iKQpwbG90dGVkIDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIoYm90aFssIGMoImRlc2VxX2xvZ2ZjLngiLCAiZGVzZXFfbG9nZmMueSIpXSkKcGxvdHRlZCRzY2F0dGVyCmBgYAoKIyMjIEludGVyYWN0aW9uIG1vZGVsCgpJIHdhbnQgdG8gbWFrZSBzdXJlIHRoYXQgbXkgbWV0aG9kcyBvZiBwZXJmb3JtaW5nIGludGVyYWN0aW9uIG1vZGVscwp3b3JrIGFzIEkgdGhpbmsgaXQgZG9lcywgYW5kIHRoaXMgZGF0YSBzZXQgbG9va3MgdG8gbWUgdG8gYmUgYSBwZXJmZWN0CnBsYWNlIHRvIHRlc3QgdGhhdC4KCmBgYHtyIGNvbWJpbmVkX2RlLCBldmFsPUZBTFNFfQpjb21iaW5lZF9mYWN0b3JzIDwtIHBhc3RlMChwRGF0YShwZHhyX2V4cHQpW1siZ2Vub3R5cGUiXV0sICJfIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcERhdGEocGR4cl9leHB0KVtbImdyb3d0aHBoYXNlIl1dKQpjb21iaW5lZF9leHB0IDwtIHNldF9leHB0X2NvbmRpdGlvbnMocGR4cl9leHB0LCBmYWN0PWNvbWJpbmVkX2ZhY3RvcnMpICU+JQoKY29tYmluZWRfZGUgPC0gYWxsX3BhaXJ3aXNlKGNvbWJpbmVkX2V4cHQsIG1vZGVsX2JhdGNoPSJzdmFzZXEiLCBmaWx0ZXI9VFJVRSkKCmNvbWJpbmVkX2tlZXBlcnMgPC0gbGlzdCgKICAgICJlMjBtX2RlbHRhX3ZzX3d0IiA9IGMoImRlbHRhZTIwbSIsICJXVGUyMG0iKSwKICAgICJlMjBtX2RlbHRhX3ZzX2NvbXBsZW1lbnQiID0gYygiZGVsdGFlMjBtIiwgImNvbXBsZW1lbnRlMjBtIiksCiAgICAiZTYwbV9kZWx0YV92c193dCIgPSBjKCJkZWx0YWU2MG0iLCAiV1RlNjBtIiksCiAgICAiZTYwbV9kZWx0YV92c19jb21wbGVtZW50IiA9IGMoImRlbHRhZTYwbSIsICJjb21wbGVtZW50ZTYwbSIpLAogICAgInN0YXJ0X2RlbHRhX3ZzX3d0IiA9IGMoImRlbHRhc3RhcnQiLCAiV1RzdGFydCIpLAogICAgInN0YXJ0X2RlbHRhX3ZzX2NvbXBsZW1lbnQiID0gYygiZGVsdGFzdGFydCIsICJjb21wbGVtZW50c3RhcnQiKSwKICAgICJXVF9lMjBtX3ZzX3N0YXJ0IiA9IGMoIldUZTIwbSIsICJXVHN0YXJ0IiksCiAgICAiZGVsdGFfZTIwbV92c19zdGFydCIgPSBjKCJkZWx0YWUyMG0iLCAiZGVsdGFzdGFydCIpLAogICAgImNvbXBsZW1lbnRfZTIwbV92c19zdGFydCIgPSBjKCJjb21wbGVtZW50ZTIwbSIsICJjb21wbGVtZW50c3RhcnQiKSwKICAgICJXVF9lNjBtX3ZzX3N0YXJ0IiA9IGMoIldUZTYwbSIsICJXVHN0YXJ0IiksCiAgICAiZGVsdGFfZTYwbV92c19zdGFydCIgPSBjKCJkZWx0YWU2MG0iLCAiZGVsdGFzdGFydCIpLAogICAgImNvbXBsZW1lbnRfZTYwbV92c19zdGFydCIgPSBjKCJjb21wbGVtZW50ZTYwbSIsICJjb21wbGVtZW50c3RhcnQiKSwKICAgICJXVF9lNjBtX3ZzX2UyMG0iID0gYygiV1RlNjBtIiwgIldUZTIwbSIpLAogICAgImRlbHRhX2U2MG1fdnNfZTIwbSIgPSBjKCJkZWx0YWU2MG0iLCAiZGVsdGFlMjBtIiksCiAgICAiY29tcGxlbWVudF9lNjBtX3ZzX2UyMG0iID0gYygiY29tcGxlbWVudGU2MG0iLCAiY29tcGxlbWVudGUyMG0iKSkKCgpjb21iaW5lZF90YWJsZXMgPC0gY29tYmluZV9kZV90YWJsZXMoCiAgICBjb21iaW5lZF9kZSwga2VlcGVycyA9IGNvbWJpbmVkX2tlZXBlcnMsCiAgICBleGNlbCA9IGdsdWU6OmdsdWUoImV4Y2VsL3BkeHJfY29tYmluZWRfdGFibGVzLXZ7dmVyfS54bHN4IikpCmNvbWJpbmVkX3NpZyA8LSBleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogICAgY29tYmluZWRfdGFibGVzLAogICAgZXhjZWwgPSBnbHVlOjpnbHVlKCJleGNlbC9wZHhyX2NvbWJpbmVkX3NpZy12e3Zlcn0ueGxzeCIpKQpgYGAKCgpgYGB7ciBzYXZlbWV9CmlmICghaXNUUlVFKGdldDAoInNraXBfbG9hZCIpKSkgewogIHBhbmRlcjo6cGFuZGVyKHNlc3Npb25JbmZvKCkpCiAgbWVzc2FnZShwYXN0ZTAoIlRoaXMgaXMgaHBnbHRvb2xzIGNvbW1pdDogIiwgZ2V0X2dpdF9jb21taXQoKSkpCiAgdGhpc19zYXZlIDwtIHBhc3RlMChnc3ViKHBhdHRlcm4gPSAiXFwuUm1kIiwgcmVwbGFjZSA9ICIiLCB4ID0gcm1kX2ZpbGUpLCAiLXYiLCB2ZXIsICIucmRhLnh6IikKICBtZXNzYWdlKHBhc3RlMCgiU2F2aW5nIHRvICIsIHRoaXNfc2F2ZSkpCiAgdG1wIDwtIHNtKHNhdmVtZShmaWxlbmFtZSA9IHRoaXNfc2F2ZSkpCn0KYGBgCg==