Introduction
This document demonstrates a strategy to adjust the p-values for a differential translation efficiency analysis using riborex. The dataset involves a time-course experiment studying the effect of radiation on glioblatoma cells. Briefly, two glioblastoma cell lines U251 (p53 mt) and U343 (p53 wt) were profiled using RNA-Seq and Ribo-Seq at 3 time points: 0h, 1h and 2h post radiation.
Utility functions
suppressMessages(library(riborex))
suppressMessages(library(fdrtool))
suppressMessages(library(cowplot))
annotations <- read.table(file.path('..', 'annotations',
'hg38_gene_names_stripped.tsv'),
header = F,
col.names = c('gene_id', 'gene_name',
'gene_type'),
stringsAsFactors = F)
rownames(annotations) <- annotations$gene_id
histone.genes.df <- read.table(file.path('..', 'annotations',
'histone_genes.tsv'),
header = T,
stringsAsFactors = F,
sep = '\t')
histone.genes.1 <- annotations[grep('HIST', annotations$gene_name), ]$gene_id
histone.genes.2 <-annotations[annotations$gene_name %in%
histone.genes.df$Approved.Symbol, ]$gene_id
# Add RP1-34B20.21 separately since symbol doesn't have HIST in it.
histone.genes <- unique(c(histone.genes.1, histone.genes.2, 'ENSG00000282988'))
readcounts.dir <- file.path('..', 'read_counts', 'byCDS')
results.dir <- file.path('..', 'results', 'translation_efficiency',
'without_histones_edgeRD')
rna.design.file <- file.path('..', 'design_files', 'rna_seq_design.tsv')
ribo.design.file <- file.path('..', 'design_files', 'ribo_seq_design.tsv')
## Suffix of htseq-count output
counts.suffix <- '.CDS.counts.tsv'
rna.design.info <- read.table(rna.design.file, header=T,
stringsAsFactors=FALSE)
ribo.design.info <- read.table(ribo.design.file, header=T,
stringsAsFactors=FALSE)
count.reads <- function(design.info, dirname){
files <- paste(design.info$SampleFile, counts.suffix, sep='')
sampleName <- design.info$SampleName
sampleTable <- data.frame(sampleName=sampleName,
fileName=files,
time=factor(design.info$Time, levels=c('T0', 'T1', 'T24')),
Cell_line=factor(design.info$Cell_line, levels=c('U251', 'U343')))
ddsHTSeq <- DESeqDataSetFromHTSeqCount(sampleTable = sampleTable,
directory = file.path(readcounts.dir, dirname),
design = ~ time+Cell_line)
rownames(ddsHTSeq) <- gsub('\\.[0-9]+', '', rownames(ddsHTSeq))
# Remove histones
ddsHTSeq <- ddsHTSeq[!(rownames(ddsHTSeq) %in% histone.genes),]
ddsHTSeq <- ddsHTSeq[ rowSums(counts(ddsHTSeq)) > 1, ]
dds <- DESeq(ddsHTSeq)
return(dds)
}
filter_results <- function(df){
df<- as.data.frame(df)
df <- df[order(df$padj),]
df$gene_name <- annotations[rownames(df),]$gene_name
df.sig <- subset(df, padj<0.05)
return (df.sig)
}
filter_results.edgeR <- function(df){
df<- as.data.frame(df$table)
df <- df[order(df$FDR),]
df$gene_name <- annotations[rownames(df),]$gene_name
df.sig <- subset(df, FDR<0.05)
return (df.sig)
}
doPvalueAdjustment <- function(results){
hist(results$pvalue, main = 'DESeq2 unadjusted p-values',
xlab = 'Unadjusted p-values')
results <- results[ !is.na(results$padj), ]
results <- results[ !is.na(results$pvalue), ]
results <- results[, -which(names(results) == 'padj')]
resultsFDR <- fdrtool(results$stat,
statistic= 'normal',
plot = T)
results[,'padj'] <- p.adjust(resultsFDR$pval,
method = 'BH')
hist(resultsFDR$pval,
main = 'DESeq2 corrected p-values | Empirical null',
xlab = 'Corrected p-values')
return (results)
}
riborex.for.cellline <- function(rna.read.counts, ribo.read.counts,
cell.line, contrast, engine='DESeq2', minMeanCount = 20,
merge.T1=FALSE){
rna.read.counts <- rna.read.counts[, grepl(cell.line,
colnames(rna.read.counts))]
ribo.read.counts <- ribo.read.counts[, grepl(cell.line,
colnames(ribo.read.counts))]
rna.conditions.time <- as.factor(as.vector(
sapply(colnames(rna.read.counts),
function(x) unlist(strsplit(x, '_'))[3])))
ribo.conditions.time <- as.factor(as.vector(
sapply(colnames(ribo.read.counts),
function(x) unlist(strsplit(x, '_'))[4])))
rna.conditions.cell <- as.factor(as.vector(
sapply(colnames(rna.read.counts),
function(x) unlist(strsplit(x, '_'))[1])))
ribo.conditions.cell <- as.factor(
as.vector(sapply(colnames(ribo.read.counts),
function(x) unlist(strsplit(x, '_'))[1])))
if (merge.T1){
levels(rna.conditions.time)[levels(rna.conditions.time)=='T0'] <- 'T0T1'
levels(rna.conditions.time)[levels(rna.conditions.time)=='T1'] <- 'T0T1'
levels(ribo.conditions.time)[levels(ribo.conditions.time)=='T0'] <- 'T0T1'
levels(ribo.conditions.time)[levels(ribo.conditions.time)=='T1'] <- 'T0T1'
}
rna.conditions <- data.frame('time' = rna.conditions.time)
ribo.conditions <- data.frame('time' = ribo.conditions.time)
common.genes <- intersect(rownames(rna.read.counts),
rownames(ribo.read.counts))
rna.read.counts <- rna.read.counts[common.genes,]
ribo.read.counts <- ribo.read.counts[common.genes,]
colnames(rna.read.counts) <- paste(colnames(rna.read.counts),
'RNA', sep='_')
colnames(ribo.read.counts) <- paste(colnames(ribo.read.counts),
'Ribo', sep='_')
res <- riborex(rna.read.counts,
ribo.read.counts,
rna.conditions,
ribo.conditions,
contrast = contrast,
engine = engine,
minMeanCount = minMeanCount)
return (res)
}
Read data
rna.read.counts.all <- count.reads(rna.design.info, 'rna_seq')
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
ribo.read.counts.all <- count.reads(ribo.design.info, 'ribo_seq')
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
rna.conditions.time.all <- as.factor(
as.vector(sapply(colnames(rna.read.counts.all),
function(x) unlist(strsplit(x, '_'))[3])))
ribo.conditions.time.all <- as.factor(
as.vector(sapply(colnames(ribo.read.counts.all),
function(x) unlist(strsplit(x, '_'))[4])))
rna.conditions.cell.all <- as.factor(
as.vector(sapply(colnames(rna.read.counts.all),
function(x) unlist(strsplit(x, '_'))[1])))
ribo.conditions.cell.all <- as.factor(as.vector(
sapply(colnames(ribo.read.counts.all),
function(x) unlist(strsplit(x, '_'))[1])))
rna.conditions.all <- data.frame('cell.type' = rna.conditions.cell.all,
'time' = rna.conditions.time.all)
ribo.conditions.all <- data.frame('cell.type' = ribo.conditions.cell.all,
'time' = ribo.conditions.time.all)
common.genes <- intersect(rownames(rna.read.counts.all),
rownames(ribo.read.counts.all))
rna.read.counts.all <- rna.read.counts.all[common.genes,]
ribo.read.counts.all <- ribo.read.counts.all[common.genes,]
colnames(rna.read.counts.all) <- paste(colnames(rna.read.counts.all),
'RNA', sep='_')
colnames(ribo.read.counts.all) <- paste(colnames(ribo.read.counts.all),
'Ribo', sep='_')
contrast.T1vsT0 <- c('time', 'T1', 'T0')
contrast.T24vsT0 <- c('time', 'T24', 'T0')
contrast.T24vsT1 <- c('time', 'T24', 'T1')
var.rna <- ( 1/ log2(exp(1)) )^2 * (1 / rowData(rna.read.counts.all)$baseMean + rowData(rna.read.counts.all)$dispersion)
var.ribo <- ( 1/ log2(exp(1)) )^2 * (1 / rowData(ribo.read.counts.all)$baseMean + rowData(ribo.read.counts.all)$dispersion)
sqrtsd.rna <- sqrt(sqrt(var.rna))
sqrtsd.ribo <- sqrt(sqrt(var.ribo))
library(tidyverse)
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
collapse(): dplyr, IRanges
combine(): dplyr, Biobase, BiocGenerics
count(): dplyr, matrixStats
desc(): dplyr, IRanges
expand(): tidyr, Matrix, S4Vectors
filter(): dplyr, stats
first(): dplyr, S4Vectors
ggsave(): ggplot2, cowplot
lag(): dplyr, stats
Position(): ggplot2, BiocGenerics, base
reduce(): purrr, GenomicRanges, IRanges
rename(): dplyr, S4Vectors
select(): dplyr, AnnotationDbi
simplify(): purrr, clusterProfiler
slice(): dplyr, IRanges
# Difference in scores cetner around zero
qplot(sqrtsd.rna - sqrtsd.ribo, geom = "dotplot", binwidth = 0.008)

qplot(sqrtsd.rna - sqrtsd.ribo, geom = "density")

t.test(sqrtsd.rna, sqrtsd.ribo, paired = TRUE)
Paired t-test
data: sqrtsd.rna and sqrtsd.ribo
t = -26.34, df = 15140, p-value < 2.2e-16
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-0.04533102 -0.03905170
sample estimates:
mean of the differences
-0.04219136
LS0tCnRpdGxlOiAiQWNjb3VudGluZyBmb3Igb3ZlcmRpc3BlcnNpb24gaW4gUmlib3JleCIKc3VidGl0bGU6IFdoZW4gdGhlIHRoZW9yZXRpY2FsIGRpc3RyaWJ1dGlvbiBpcyB0b28gdGhlb3JldGljYWwuCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAotLS0KCiMgSW50cm9kdWN0aW9uCgpUaGlzICBkb2N1bWVudCBkZW1vbnN0cmF0ZXMgYSBzdHJhdGVneSB0byBhZGp1c3QgdGhlIHAtdmFsdWVzIGZvciBhIGRpZmZlcmVudGlhbCB0cmFuc2xhdGlvbiBlZmZpY2llbmN5IGFuYWx5c2lzIHVzaW5nIHJpYm9yZXguIFRoZSBkYXRhc2V0IGludm9sdmVzIGEgdGltZS1jb3Vyc2UgZXhwZXJpbWVudCBzdHVkeWluZyB0aGUgZWZmZWN0IG9mIHJhZGlhdGlvbiBvbiBnbGlvYmxhdG9tYSBjZWxscy4gQnJpZWZseSwgdHdvIGdsaW9ibGFzdG9tYSBjZWxsIGxpbmVzIFUyNTEgKHA1MyBtdCkgYW5kIFUzNDMgKHA1MyB3dCkgd2VyZSBwcm9maWxlZCB1c2luZyBSTkEtU2VxIGFuZCBSaWJvLVNlcSBhdCAzIHRpbWUgcG9pbnRzOiAwaCwgMWggYW5kIDJoIHBvc3QgcmFkaWF0aW9uLgoKIyBVdGlsaXR5IGZ1bmN0aW9ucwpgYGB7cn0Kc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHJpYm9yZXgpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZmRydG9vbCkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShjb3dwbG90KSkKCmFubm90YXRpb25zIDwtIHJlYWQudGFibGUoZmlsZS5wYXRoKCcuLicsICdhbm5vdGF0aW9ucycsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnaGczOF9nZW5lX25hbWVzX3N0cmlwcGVkLnRzdicpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wubmFtZXMgPSBjKCdnZW5lX2lkJywgJ2dlbmVfbmFtZScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2dlbmVfdHlwZScpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpyb3duYW1lcyhhbm5vdGF0aW9ucykgPC0gYW5ub3RhdGlvbnMkZ2VuZV9pZAoKCmhpc3RvbmUuZ2VuZXMuZGYgPC0gcmVhZC50YWJsZShmaWxlLnBhdGgoJy4uJywgJ2Fubm90YXRpb25zJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2hpc3RvbmVfZ2VuZXMudHN2JyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICdcdCcpIApoaXN0b25lLmdlbmVzLjEgPC0gYW5ub3RhdGlvbnNbZ3JlcCgnSElTVCcsIGFubm90YXRpb25zJGdlbmVfbmFtZSksIF0kZ2VuZV9pZApoaXN0b25lLmdlbmVzLjIgPC1hbm5vdGF0aW9uc1thbm5vdGF0aW9ucyRnZW5lX25hbWUgJWluJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoaXN0b25lLmdlbmVzLmRmJEFwcHJvdmVkLlN5bWJvbCwgXSRnZW5lX2lkCiMgQWRkIFJQMS0zNEIyMC4yMSBzZXBhcmF0ZWx5IHNpbmNlIHN5bWJvbCBkb2Vzbid0IGhhdmUgSElTVCBpbiBpdC4KaGlzdG9uZS5nZW5lcyA8LSB1bmlxdWUoYyhoaXN0b25lLmdlbmVzLjEsIGhpc3RvbmUuZ2VuZXMuMiwgJ0VOU0cwMDAwMDI4Mjk4OCcpKQoKCnJlYWRjb3VudHMuZGlyIDwtIGZpbGUucGF0aCgnLi4nLCAncmVhZF9jb3VudHMnLCAnYnlDRFMnKQpyZXN1bHRzLmRpciA8LSBmaWxlLnBhdGgoJy4uJywgJ3Jlc3VsdHMnLCAndHJhbnNsYXRpb25fZWZmaWNpZW5jeScsIAogICAgICAgICAgICAgICAgICAgICAgICAgJ3dpdGhvdXRfaGlzdG9uZXNfZWRnZVJEJykKcm5hLmRlc2lnbi5maWxlIDwtIGZpbGUucGF0aCgnLi4nLCAnZGVzaWduX2ZpbGVzJywgJ3JuYV9zZXFfZGVzaWduLnRzdicpCnJpYm8uZGVzaWduLmZpbGUgPC0gZmlsZS5wYXRoKCcuLicsICdkZXNpZ25fZmlsZXMnLCAncmlib19zZXFfZGVzaWduLnRzdicpCgojIyBTdWZmaXggb2YgaHRzZXEtY291bnQgb3V0cHV0CmNvdW50cy5zdWZmaXggPC0gJy5DRFMuY291bnRzLnRzdicKCnJuYS5kZXNpZ24uaW5mbyA8LSByZWFkLnRhYmxlKHJuYS5kZXNpZ24uZmlsZSwgaGVhZGVyPVQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKQpyaWJvLmRlc2lnbi5pbmZvIDwtIHJlYWQudGFibGUocmliby5kZXNpZ24uZmlsZSwgaGVhZGVyPVQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKCgpjb3VudC5yZWFkcyA8LSBmdW5jdGlvbihkZXNpZ24uaW5mbywgZGlybmFtZSl7CiBmaWxlcyA8LSBwYXN0ZShkZXNpZ24uaW5mbyRTYW1wbGVGaWxlLCBjb3VudHMuc3VmZml4LCBzZXA9JycpCiBzYW1wbGVOYW1lIDwtIGRlc2lnbi5pbmZvJFNhbXBsZU5hbWUKIHNhbXBsZVRhYmxlIDwtIGRhdGEuZnJhbWUoc2FtcGxlTmFtZT1zYW1wbGVOYW1lLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZU5hbWU9ZmlsZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lPWZhY3RvcihkZXNpZ24uaW5mbyRUaW1lLCBsZXZlbHM9YygnVDAnLCAnVDEnLCAnVDI0JykpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2VsbF9saW5lPWZhY3RvcihkZXNpZ24uaW5mbyRDZWxsX2xpbmUsIGxldmVscz1jKCdVMjUxJywgJ1UzNDMnKSkpCiBkZHNIVFNlcSA8LSBERVNlcURhdGFTZXRGcm9tSFRTZXFDb3VudChzYW1wbGVUYWJsZSA9IHNhbXBsZVRhYmxlLCAKICAgICAgICAgIGRpcmVjdG9yeSA9IGZpbGUucGF0aChyZWFkY291bnRzLmRpciwgZGlybmFtZSksCiAgICAgICAgICBkZXNpZ24gPSB+IHRpbWUrQ2VsbF9saW5lKQogcm93bmFtZXMoZGRzSFRTZXEpIDwtIGdzdWIoJ1xcLlswLTldKycsICcnLCByb3duYW1lcyhkZHNIVFNlcSkpCiAjIFJlbW92ZSBoaXN0b25lcwogZGRzSFRTZXEgPC0gZGRzSFRTZXFbIShyb3duYW1lcyhkZHNIVFNlcSkgJWluJSBoaXN0b25lLmdlbmVzKSxdCiBkZHNIVFNlcSA8LSBkZHNIVFNlcVsgcm93U3Vtcyhjb3VudHMoZGRzSFRTZXEpKSA+IDEsIF0KIGRkcyA8LSBERVNlcShkZHNIVFNlcSkKCiByZXR1cm4oZGRzKQp9CgpmaWx0ZXJfcmVzdWx0cyA8LSBmdW5jdGlvbihkZil7CiBkZjwtIGFzLmRhdGEuZnJhbWUoZGYpCiBkZiA8LSBkZltvcmRlcihkZiRwYWRqKSxdCiBkZiRnZW5lX25hbWUgPC0gYW5ub3RhdGlvbnNbcm93bmFtZXMoZGYpLF0kZ2VuZV9uYW1lCiBkZi5zaWcgPC0gc3Vic2V0KGRmLCBwYWRqPDAuMDUpCiByZXR1cm4gKGRmLnNpZykKfQoKZmlsdGVyX3Jlc3VsdHMuZWRnZVIgPC0gZnVuY3Rpb24oZGYpewogZGY8LSBhcy5kYXRhLmZyYW1lKGRmJHRhYmxlKQogZGYgPC0gZGZbb3JkZXIoZGYkRkRSKSxdCiBkZiRnZW5lX25hbWUgPC0gYW5ub3RhdGlvbnNbcm93bmFtZXMoZGYpLF0kZ2VuZV9uYW1lCiBkZi5zaWcgPC0gc3Vic2V0KGRmLCBGRFI8MC4wNSkKIHJldHVybiAoZGYuc2lnKQp9Cgpkb1B2YWx1ZUFkanVzdG1lbnQgPC0gZnVuY3Rpb24ocmVzdWx0cyl7CiAgaGlzdChyZXN1bHRzJHB2YWx1ZSwgIG1haW4gPSAnREVTZXEyIHVuYWRqdXN0ZWQgcC12YWx1ZXMnLCAKICAgICAgIHhsYWIgPSAnVW5hZGp1c3RlZCBwLXZhbHVlcycpCiAgcmVzdWx0cyA8LSByZXN1bHRzWyAhaXMubmEocmVzdWx0cyRwYWRqKSwgXQogIHJlc3VsdHMgPC0gcmVzdWx0c1sgIWlzLm5hKHJlc3VsdHMkcHZhbHVlKSwgXQogIHJlc3VsdHMgPC0gcmVzdWx0c1ssIC13aGljaChuYW1lcyhyZXN1bHRzKSA9PSAncGFkaicpXQogIHJlc3VsdHNGRFIgPC0gZmRydG9vbChyZXN1bHRzJHN0YXQsIAogICAgICAgICAgICAgICAgICAgICAgICBzdGF0aXN0aWM9ICdub3JtYWwnLCAKICAgICAgICAgICAgICAgICAgICAgICAgcGxvdCA9IFQpCiAgcmVzdWx0c1ssJ3BhZGonXSAgPC0gcC5hZGp1c3QocmVzdWx0c0ZEUiRwdmFsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICdCSCcpCiAgaGlzdChyZXN1bHRzRkRSJHB2YWwsIAogICAgICAgbWFpbiA9ICdERVNlcTIgY29ycmVjdGVkIHAtdmFsdWVzIHwgRW1waXJpY2FsIG51bGwnLCAKICAgICAgIHhsYWIgPSAnQ29ycmVjdGVkIHAtdmFsdWVzJykKICByZXR1cm4gKHJlc3VsdHMpCn0KCnJpYm9yZXguZm9yLmNlbGxsaW5lIDwtIGZ1bmN0aW9uKHJuYS5yZWFkLmNvdW50cywgcmliby5yZWFkLmNvdW50cywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlbGwubGluZSwgY29udHJhc3QsIGVuZ2luZT0nREVTZXEyJywgbWluTWVhbkNvdW50ID0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lcmdlLlQxPUZBTFNFKXsKIHJuYS5yZWFkLmNvdW50cyA8LSBybmEucmVhZC5jb3VudHNbLCBncmVwbChjZWxsLmxpbmUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG5hbWVzKHJuYS5yZWFkLmNvdW50cykpXQogcmliby5yZWFkLmNvdW50cyA8LSByaWJvLnJlYWQuY291bnRzWywgZ3JlcGwoY2VsbC5saW5lLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG5hbWVzKHJpYm8ucmVhZC5jb3VudHMpKV0KIHJuYS5jb25kaXRpb25zLnRpbWUgPC0gYXMuZmFjdG9yKGFzLnZlY3RvcigKICAgc2FwcGx5KGNvbG5hbWVzKHJuYS5yZWFkLmNvdW50cyksIAogICAgICAgICAgZnVuY3Rpb24oeCkgdW5saXN0KHN0cnNwbGl0KHgsICdfJykpWzNdKSkpCiByaWJvLmNvbmRpdGlvbnMudGltZSA8LSBhcy5mYWN0b3IoYXMudmVjdG9yKAogICBzYXBwbHkoY29sbmFtZXMocmliby5yZWFkLmNvdW50cyksIAogICAgICAgICAgZnVuY3Rpb24oeCkgdW5saXN0KHN0cnNwbGl0KHgsICdfJykpWzRdKSkpCiBybmEuY29uZGl0aW9ucy5jZWxsIDwtIGFzLmZhY3Rvcihhcy52ZWN0b3IoCiAgIHNhcHBseShjb2xuYW1lcyhybmEucmVhZC5jb3VudHMpLCAKICAgICAgICAgIGZ1bmN0aW9uKHgpIHVubGlzdChzdHJzcGxpdCh4LCAnXycpKVsxXSkpKQogcmliby5jb25kaXRpb25zLmNlbGwgPC0gYXMuZmFjdG9yKAogICBhcy52ZWN0b3Ioc2FwcGx5KGNvbG5hbWVzKHJpYm8ucmVhZC5jb3VudHMpLCAKICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB1bmxpc3Qoc3Ryc3BsaXQoeCwgJ18nKSlbMV0pKSkKIAogaWYgKG1lcmdlLlQxKXsKICAgbGV2ZWxzKHJuYS5jb25kaXRpb25zLnRpbWUpW2xldmVscyhybmEuY29uZGl0aW9ucy50aW1lKT09J1QwJ10gPC0gJ1QwVDEnCiAgIGxldmVscyhybmEuY29uZGl0aW9ucy50aW1lKVtsZXZlbHMocm5hLmNvbmRpdGlvbnMudGltZSk9PSdUMSddIDwtICdUMFQxJwogICBsZXZlbHMocmliby5jb25kaXRpb25zLnRpbWUpW2xldmVscyhyaWJvLmNvbmRpdGlvbnMudGltZSk9PSdUMCddIDwtICdUMFQxJwogICBsZXZlbHMocmliby5jb25kaXRpb25zLnRpbWUpW2xldmVscyhyaWJvLmNvbmRpdGlvbnMudGltZSk9PSdUMSddIDwtICdUMFQxJwogfQogcm5hLmNvbmRpdGlvbnMgPC0gZGF0YS5mcmFtZSgndGltZScgPSBybmEuY29uZGl0aW9ucy50aW1lKQogcmliby5jb25kaXRpb25zIDwtIGRhdGEuZnJhbWUoJ3RpbWUnID0gcmliby5jb25kaXRpb25zLnRpbWUpCiBjb21tb24uZ2VuZXMgPC0gaW50ZXJzZWN0KHJvd25hbWVzKHJuYS5yZWFkLmNvdW50cyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICByb3duYW1lcyhyaWJvLnJlYWQuY291bnRzKSkgCiBybmEucmVhZC5jb3VudHMgPC0gcm5hLnJlYWQuY291bnRzW2NvbW1vbi5nZW5lcyxdCiByaWJvLnJlYWQuY291bnRzIDwtIHJpYm8ucmVhZC5jb3VudHNbY29tbW9uLmdlbmVzLF0KIGNvbG5hbWVzKHJuYS5yZWFkLmNvdW50cykgPC0gcGFzdGUoY29sbmFtZXMocm5hLnJlYWQuY291bnRzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1JOQScsIHNlcD0nXycpCiBjb2xuYW1lcyhyaWJvLnJlYWQuY291bnRzKSA8LSBwYXN0ZShjb2xuYW1lcyhyaWJvLnJlYWQuY291bnRzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdSaWJvJywgc2VwPSdfJykKIHJlcyA8LSByaWJvcmV4KHJuYS5yZWFkLmNvdW50cywgCiAgICAgICAgICAgICAgICByaWJvLnJlYWQuY291bnRzLCAKICAgICAgICAgICAgICAgIHJuYS5jb25kaXRpb25zLCAKICAgICAgICAgICAgICAgIHJpYm8uY29uZGl0aW9ucywgCiAgICAgICAgICAgICAgICBjb250cmFzdCA9IGNvbnRyYXN0LCAKICAgICAgICAgICAgICAgIGVuZ2luZSA9IGVuZ2luZSwKICAgICAgICAgICAgICAgIG1pbk1lYW5Db3VudCA9IG1pbk1lYW5Db3VudCkKIHJldHVybiAocmVzKQp9CgpgYGAKCiMgUmVhZCBkYXRhCgpgYGB7cn0Kcm5hLnJlYWQuY291bnRzLmFsbCA8LSBjb3VudC5yZWFkcyhybmEuZGVzaWduLmluZm8sICdybmFfc2VxJykKcmliby5yZWFkLmNvdW50cy5hbGwgPC0gY291bnQucmVhZHMocmliby5kZXNpZ24uaW5mbywgJ3JpYm9fc2VxJykKCnJuYS5jb25kaXRpb25zLnRpbWUuYWxsIDwtIGFzLmZhY3RvcigKICBhcy52ZWN0b3Ioc2FwcGx5KGNvbG5hbWVzKHJuYS5yZWFkLmNvdW50cy5hbGwpLCAKICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHVubGlzdChzdHJzcGxpdCh4LCAnXycpKVszXSkpKQpyaWJvLmNvbmRpdGlvbnMudGltZS5hbGwgPC0gYXMuZmFjdG9yKAogIGFzLnZlY3RvcihzYXBwbHkoY29sbmFtZXMocmliby5yZWFkLmNvdW50cy5hbGwpLCAKICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHVubGlzdChzdHJzcGxpdCh4LCAnXycpKVs0XSkpKQpybmEuY29uZGl0aW9ucy5jZWxsLmFsbCA8LSBhcy5mYWN0b3IoCiAgYXMudmVjdG9yKHNhcHBseShjb2xuYW1lcyhybmEucmVhZC5jb3VudHMuYWxsKSwKICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHVubGlzdChzdHJzcGxpdCh4LCAnXycpKVsxXSkpKQpyaWJvLmNvbmRpdGlvbnMuY2VsbC5hbGwgPC0gYXMuZmFjdG9yKGFzLnZlY3RvcigKICBzYXBwbHkoY29sbmFtZXMocmliby5yZWFkLmNvdW50cy5hbGwpLAogICAgICAgICBmdW5jdGlvbih4KSB1bmxpc3Qoc3Ryc3BsaXQoeCwgJ18nKSlbMV0pKSkKCnJuYS5jb25kaXRpb25zLmFsbCA8LSBkYXRhLmZyYW1lKCdjZWxsLnR5cGUnID0gcm5hLmNvbmRpdGlvbnMuY2VsbC5hbGwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAndGltZScgPSBybmEuY29uZGl0aW9ucy50aW1lLmFsbCkKcmliby5jb25kaXRpb25zLmFsbCA8LSBkYXRhLmZyYW1lKCdjZWxsLnR5cGUnID0gcmliby5jb25kaXRpb25zLmNlbGwuYWxsLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICd0aW1lJyA9IHJpYm8uY29uZGl0aW9ucy50aW1lLmFsbCkKCmNvbW1vbi5nZW5lcyA8LSBpbnRlcnNlY3Qocm93bmFtZXMocm5hLnJlYWQuY291bnRzLmFsbCksIAogICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzKHJpYm8ucmVhZC5jb3VudHMuYWxsKSkgCgpybmEucmVhZC5jb3VudHMuYWxsIDwtIHJuYS5yZWFkLmNvdW50cy5hbGxbY29tbW9uLmdlbmVzLF0Kcmliby5yZWFkLmNvdW50cy5hbGwgPC0gcmliby5yZWFkLmNvdW50cy5hbGxbY29tbW9uLmdlbmVzLF0KCmNvbG5hbWVzKHJuYS5yZWFkLmNvdW50cy5hbGwpIDwtIHBhc3RlKGNvbG5hbWVzKHJuYS5yZWFkLmNvdW50cy5hbGwpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUk5BJywgc2VwPSdfJykKY29sbmFtZXMocmliby5yZWFkLmNvdW50cy5hbGwpIDwtIHBhc3RlKGNvbG5hbWVzKHJpYm8ucmVhZC5jb3VudHMuYWxsKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdSaWJvJywgc2VwPSdfJykKCmNvbnRyYXN0LlQxdnNUMCA8LSBjKCd0aW1lJywgJ1QxJywgJ1QwJykKY29udHJhc3QuVDI0dnNUMCA8LSBjKCd0aW1lJywgJ1QyNCcsICdUMCcpCmNvbnRyYXN0LlQyNHZzVDEgPC0gYygndGltZScsICdUMjQnLCAnVDEnKQpgYGAKCgpgYGB7cn0KdmFyLnJuYSA8LSAoIDEvIGxvZzIoZXhwKDEpKSApXjIgKiAoMSAvIHJvd0RhdGEocm5hLnJlYWQuY291bnRzLmFsbCkkYmFzZU1lYW4gKyByb3dEYXRhKHJuYS5yZWFkLmNvdW50cy5hbGwpJGRpc3BlcnNpb24pCnZhci5yaWJvIDwtICggMS8gbG9nMihleHAoMSkpICleMiAqICgxIC8gcm93RGF0YShyaWJvLnJlYWQuY291bnRzLmFsbCkkYmFzZU1lYW4gKyByb3dEYXRhKHJpYm8ucmVhZC5jb3VudHMuYWxsKSRkaXNwZXJzaW9uKQoKc3FydHNkLnJuYSA8LSAgc3FydChzcXJ0KHZhci5ybmEpKQpzcXJ0c2QucmlibyA8LSAgc3FydChzcXJ0KHZhci5yaWJvKSkKCgpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMgRGlmZmVyZW5jZSBpbiBzY29yZXMgY2V0bmVyIGFyb3VuZCB6ZXJvCnFwbG90KHNxcnRzZC5ybmEgLSBzcXJ0c2QucmlibywgZ2VvbSA9ICJkb3RwbG90IiwgYmlud2lkdGggPSAwLjAwOCkKcXBsb3Qoc3FydHNkLnJuYSAtIHNxcnRzZC5yaWJvLCBnZW9tID0gImRlbnNpdHkiKQpgYGAKYGBge3J9CnQudGVzdChzcXJ0c2Qucm5hLCBzcXJ0c2QucmlibywgcGFpcmVkID0gIFRSVUUpCmBgYAo=