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=