In 7-technology_diff_exp, we observed that genes that are shorter tend to have lower expression values in RNA-seq data in the test compendium.

Here, we’re interested in if short genes tend to be unobserved (e.g., have counts/count-scale data that are zero) in the RNA-seq data used in the test compendium. We’ll go back to the lengthScaledTPM.tsv that has not yet been through the aggregation process to explore this. Note that we will only be using RNA-seq files that were used in an earlier analysis of imputation performance. These will be ~400 RNA-seq samples that were included in a smaller test compendium and thus, for later analyses in this notebook we’ll use the smaller test compendium.

Set up

`%>%` <- dplyr::`%>%`
# we'll need ggfortify to do the PCA plots
library(ggfortify)
Loading required package: ggplot2

Custom functions

These are a series of wrapper functions for plotting, as we typically make plots for all genes and then a subset of genes (such as those that are in the compendium). They generally expect certain objects to be in the global environment; see the documentation for the individual functions.

zero_proportion_point_wrapper <- function(df, plot.subtitle) {
  # Make the point plots (with geom_smooth) for the proportion of samples that
  # have a value of 0 for a gene vs. that gene's length
  # 
  # Args:
  #   df: a data.frame that includes 'gene_length' and 'Zero_proportion'
  #   plot.subtitle: subtitle for the plot; character
  # 
  # Returns:
  #   a ggplot as described above
  
  if(!all(c("gene_length" %in% colnames(df), 
            "Zero_proportion" %in% colnames(df)))) {
    stop("df must have colnames 'gene_length' and 'Zero_proportion'")  
  }
    
  df %>%
    ggplot(aes(x = gene_length, y = Zero_proportion)) +
    geom_point(alpha = 0.2) +
    geom_smooth() +
    theme_bw() +
    labs(x = "gene length", y = "proportion of samples with zero count",
         subtitle = plot.subtitle, title = "Gene length and observations")
}
pca_plot_wrapper <- function(prcomp_results, plot.title) {
  # given the output of prcomp, make a scatterplot of PC1 and PC2 with 
  # ggfortify::autoplot where the samples are colored by technology (e.g.,
  # microarray, RNA-seq)
  # requires technology_df to be in the global environment
  # Args:
  #  prcomp_results: output of prcomp, run on a transposed expression matrix
  #  plot.title: title of the plot; character
  #
  # Returns
  #   ggplot as described above
  autoplot(prcomp_results, data = technology_df, alpha = 0.5, 
           colour = 'compendium_technology_labels', scale = 0) +
    scale_color_manual(labels = c("MICROARRAY", "RNA-SEQ"), 
                       values = c("#E69F00", "#56B4E9")) +
    theme_bw() +
    ggtitle(plot.title) +
    theme(plot.title = element_text(hjust = 0.5, face = "bold")) +
    guides(colour = guide_legend(title = "technology"))
}
pca_pairs_plot_wrapper <- function(prcomp_results, plot.title) {
  # given the output of prcomp, make a pairs plot of PC1-5 with where the 
  # samples are colored by technology (e.g., microarray, RNA-seq)
  # requires compendium_technology_labels to be in the global environment
  #
  # Args:
  #  prcomp_results: output of prcomp, run on a transposed expression matrix
  #  plot.title: title of the plot; character
  #
  # Returns
  #   plot as described above
  pairs(prcomp_results$x[, 1:5], 
        col = dplyr::recode(compendium_technology_labels,
                            `MICROARRAY` = "#E69F00",
                            `RNASEQ` = "#56B4E9"),
        main = plot.title)
}

Directories

# directory to hold the plots
plot_dir <- "plots"

Read in data

lengthScaledTPM.tsv files

list_of_rnaseq_files <- list.files(file.path("..", "select_imputation_method", 
                                             "data", "rnaseq"),
                                   full.names = TRUE)

Read in the files such that they are individual data.frame in a list

rnaseq_df_list <- lapply(list_of_rnaseq_files, readr::read_tsv)
# bind all the individual sample data.frame together
rnaseq_df <- dplyr::bind_cols(rnaseq_df_list)
# we end up with a bunch of "extra" gene columns this way, but they should be
# in the same order
all.equal(rnaseq_df$Gene1, rnaseq_df$Gene112)
[1] TRUE
# drop the extra gene columns and set the rownames to gene identifiers -- this
# will help with calculating the proportion of zeros, etc.
rnaseq_df <- rnaseq_df %>%
  as.data.frame() %>%
  tibble::column_to_rownames("Gene") %>%
  dplyr::select(-dplyr::contains("Gene"))
# remove the list of individual sample data.frame
rm(rnaseq_df_list)
# read in gene lengths
gene_lengths_df <- 
  readr::read_tsv(file.path("data", 
                            "Danio_rerio.GRCz11_gene_lengths_exons.tsv"))
Parsed with column specification:
cols(
  Gene = col_character(),
  gene_length = col_integer()
)

Compendium genes, expression matrix, technology labels

We will explore this gene lengths-observations/expression levels relationship in all genes and also subset to only the genes in the test compendium, so we need to pull those gene identifiers out of the test compendium expression matrix.

# read in smaller test compendium and convert to expression matrix
compendium_exprs_file <- file.path("refinebio-data", "DANIO_RERIO", 
                                   "DANIO_RERIO.tsv")
exprs_mat <- readr::read_tsv(compendium_exprs_file, progress = FALSE) %>%
  as.data.frame() %>%
  tibble::column_to_rownames("X1") %>%
  as.matrix()
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  .default = col_double(),
  X1 = col_character()
)
See spec(...) for full column specifications.
# derive the technology labels from the sample names
sample_names <- colnames(exprs_mat)
compendium_technology_labels <- rep("MICROARRAY", length(sample_names))
compendium_technology_labels[grep("lengthScaledTPM", sample_names)] <- "RNASEQ"
# get the technology labels as a data.frame for plotting later
technology_df <- as.data.frame(compendium_technology_labels) 
# get the list of compendium genes
compendium_genes <- rownames(exprs_mat)

Gene zero counts and expression levels

For each gene, how many samples have a count-scale expression value of zero?

# calculate the proportion of samples a gene is zero for
gene_zeroes_proportion <- data.frame(
  "Gene" = rownames(rnaseq_df),
  "Zero_proportion" = apply(rnaseq_df, 1, 
                            function(x) sum(x == 0) / length(x))
)
# bind to lengths, this will be used for plotting the relationship between
# gene length and number of zeroes
zeroes_df <- dplyr::inner_join(gene_lengths_df, gene_zeroes_proportion,
                               by = "Gene")
Column `Gene` joining character vector and factor, coercing into character vector
# there are some really long genes that will probably make it difficult to
# visualize what is going on
summary(zeroes_df$gene_length)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
     10     896    1794    2384    3155   98586 

All genes

# filtering genes based on length to get a clearer picture
zeroes_df %>%
  dplyr::filter(gene_length <= 20000) %>%
  zero_proportion_point_wrapper(plot.subtitle = "All genes")

ggsave(filename = file.path(plot_dir, 
                            "gene_length_vs_zero_count_all_genes.png"),
       plot = last_plot())
Saving 7 x 7 in image

Compendium genes only

# subset just to genes that are in the compendium
zeroes_df %>%
  dplyr::filter(Gene %in% compendium_genes, 
                gene_length <= 20000) %>%
  zero_proportion_point_wrapper(plot.subtitle = "Test compendium genes only")

ggsave(filename = file.path(plot_dir, 
                            "gene_length_vs_zero_count_compendium_genes.png"),
       plot = last_plot())
Saving 7 x 7 in image

Takeaway

Shorter genes do tend to have a larger proportion of samples where they have zero values. It stands to reason that if we do not observe shorter genes, we can not correct for length-bias effectively. (Recall that lengthScaledTPM are calculated using the average tx length observed across samples.)

Removing “short” genes from the test species compendium

Does removing shorter genes from the species compendium remove evidence of a technology bias?

PCA - all genes

PC1 and PC2 for all genes

pca_results <- prcomp(t(exprs_mat))
pca_plot_wrapper(prcomp_results = pca_results, 
                 plot.title = "Test Compendium by Technology: All genes")

ggsave(filename = file.path(plot_dir, 
                            "small_test_compendium_PCA.png"),
       plot = last_plot())
Saving 7 x 7 in image

Pairs plot (PC1-5) for all genes

png(file.path(plot_dir, "small_test_compendium_pairs_plot.png"))
pca_pairs_plot_wrapper(prcomp_results = pca_results,
                       plot.title = "Test Compendium by Technology: All genes")
dev.off()
null device 
          1 

Drop the “short” genes from the expression matrix

# what genes are short? using 25th percentile
short_genes <- zeroes_df %>%
  dplyr::filter(gene_length <= quantile(zeroes_df$gene_length, 0.25)) %>%
  dplyr::pull(Gene)
length_filtered_exprs_mat <- exprs_mat[!rownames(exprs_mat) %in% short_genes, ]
dim(length_filtered_exprs_mat)
[1] 8650  830

PCA with the short genes removed

# PCA on the length filtered exprs mat
filtered_pca_results <- prcomp(t(length_filtered_exprs_mat))

PC1 and PC2 scatterplot

pca_plot_wrapper(prcomp_results = filtered_pca_results,
                 plot.title = paste("Test Compendium by Technology:",  
                                    "Short genes removed"))

ggsave(filename = file.path(plot_dir, 
                            "gene_lengths_test_compendium_PCA.png"),
       plot = last_plot())
Saving 7 x 7 in image

Pairs plot (PC1-5)

png(file.path(plot_dir, "gene_lengths_test_compendium_pairs_plot.png"))
pca_pairs_plot_wrapper(prcomp_results = filtered_pca_results,
                       plot.title = paste("Test Compendium by Technology:",  
                                          "Short genes removed"))
dev.off()
null device 
          1 

Takeaway

Removing short genes from the test species compendium does not remove technology bias, but there are some considerations:

  • These data have already been quantile normalized – if the gene length contributes to the expression values prior to quantile normalization, this bias could possibly “leak into” the quantile normalized values for all the genes.

Session Info

sessionInfo()
R version 3.5.1 (2018-07-02)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 9 (stretch)

Matrix products: default
BLAS: /usr/lib/openblas-base/libblas.so.3
LAPACK: /usr/lib/libopenblasp-r0.2.19.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8   
 [6] LC_MESSAGES=C              LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] bindrcpp_0.2.2  ggfortify_0.4.5 ggplot2_3.0.0  

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.18     pillar_1.3.0     compiler_3.5.1   plyr_1.8.4       bindr_0.1.1      tools_3.5.1      digest_0.6.17    tibble_1.4.2    
 [9] gtable_0.2.0     nlme_3.1-137     lattice_0.20-35  mgcv_1.8-24      pkgconfig_2.0.2  rlang_0.2.2      Matrix_1.2-14    rstudioapi_0.7  
[17] yaml_2.2.0       gridExtra_2.3    withr_2.1.2      dplyr_0.7.6      stringr_1.3.1    knitr_1.20       hms_0.4.2        grid_3.5.1      
[25] tidyselect_0.2.4 glue_1.3.0       R6_2.2.2         purrr_0.2.5      tidyr_0.8.1      readr_1.1.1      magrittr_1.5     scales_1.0.0    
[33] assertthat_0.2.0 colorspace_1.3-2 labeling_0.3     stringi_1.2.4    lazyeval_0.2.1   munsell_0.5.0    crayon_1.3.4    
LS0tCnRpdGxlOiAiWmVicmFmaXNoIGdlbmUgbGVuZ3RocyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKYXV0aG9yOiBKLiBUYXJvbmkgZm9yIENDREwKZGF0ZTogMjAxOQotLS0KCkluIGA3LXRlY2hub2xvZ3lfZGlmZl9leHBgLCB3ZSBvYnNlcnZlZCB0aGF0IGdlbmVzIHRoYXQgYXJlIHNob3J0ZXIgdGVuZCB0byBoYXZlCmxvd2VyIGV4cHJlc3Npb24gdmFsdWVzIGluIFJOQS1zZXEgZGF0YSBpbiB0aGUgdGVzdCBjb21wZW5kaXVtLgoKSGVyZSwgd2UncmUgaW50ZXJlc3RlZCBpbiBpZiBzaG9ydCBnZW5lcyB0ZW5kIHRvIGJlIHVub2JzZXJ2ZWQgKGUuZy4sIGhhdmUKY291bnRzL2NvdW50LXNjYWxlIGRhdGEgdGhhdCBhcmUgemVybykgaW4gdGhlIFJOQS1zZXEgZGF0YSB1c2VkIGluIHRoZSB0ZXN0IApjb21wZW5kaXVtLgpXZSdsbCBnbyBiYWNrIHRvIHRoZSBgbGVuZ3RoU2NhbGVkVFBNLnRzdmAgdGhhdCBoYXMgbm90IHlldCBiZWVuIHRocm91Z2gKdGhlIGFnZ3JlZ2F0aW9uIHByb2Nlc3MgdG8gZXhwbG9yZSB0aGlzLgpOb3RlIHRoYXQgd2Ugd2lsbCBvbmx5IGJlIHVzaW5nIFJOQS1zZXEgZmlsZXMgdGhhdCB3ZXJlIHVzZWQgaW4gYW4gZWFybGllciAKYW5hbHlzaXMgb2YgaW1wdXRhdGlvbiBwZXJmb3JtYW5jZS4KVGhlc2Ugd2lsbCBiZSB+NDAwIFJOQS1zZXEgc2FtcGxlcyB0aGF0IHdlcmUgaW5jbHVkZWQgaW4gYSBzbWFsbGVyCnRlc3QgY29tcGVuZGl1bSBhbmQgdGh1cywgZm9yIGxhdGVyIGFuYWx5c2VzIGluIHRoaXMgbm90ZWJvb2sgd2UnbGwgdXNlIHRoZQpzbWFsbGVyIHRlc3QgY29tcGVuZGl1bS4KCiMjIFNldCB1cAoKYGBge3J9CmAlPiVgIDwtIGRwbHlyOjpgJT4lYAojIHdlJ2xsIG5lZWQgZ2dmb3J0aWZ5IHRvIGRvIHRoZSBQQ0EgcGxvdHMKbGlicmFyeShnZ2ZvcnRpZnkpCmBgYAoKIyMjIEN1c3RvbSBmdW5jdGlvbnMKClRoZXNlIGFyZSBhIHNlcmllcyBvZiB3cmFwcGVyIGZ1bmN0aW9ucyBmb3IgcGxvdHRpbmcsIGFzIHdlIHR5cGljYWxseSBtYWtlCnBsb3RzIGZvciBfYWxsIGdlbmVzXyBhbmQgdGhlbiBhIHN1YnNldCBvZiBnZW5lcyAoc3VjaCBhcyB0aG9zZSB0aGF0IGFyZQppbiB0aGUgY29tcGVuZGl1bSkuClRoZXkgZ2VuZXJhbGx5IGV4cGVjdCBjZXJ0YWluIG9iamVjdHMgdG8gYmUgaW4gdGhlIGdsb2JhbCBlbnZpcm9ubWVudDsKc2VlIHRoZSBkb2N1bWVudGF0aW9uIGZvciB0aGUgaW5kaXZpZHVhbCBmdW5jdGlvbnMuCgpgYGB7cn0KemVyb19wcm9wb3J0aW9uX3BvaW50X3dyYXBwZXIgPC0gZnVuY3Rpb24oZGYsIHBsb3Quc3VidGl0bGUpIHsKICAjIE1ha2UgdGhlIHBvaW50IHBsb3RzICh3aXRoIGdlb21fc21vb3RoKSBmb3IgdGhlIHByb3BvcnRpb24gb2Ygc2FtcGxlcyB0aGF0CiAgIyBoYXZlIGEgdmFsdWUgb2YgMCBmb3IgYSBnZW5lIHZzLiB0aGF0IGdlbmUncyBsZW5ndGgKICAjIAogICMgQXJnczoKICAjICAgZGY6IGEgZGF0YS5mcmFtZSB0aGF0IGluY2x1ZGVzICdnZW5lX2xlbmd0aCcgYW5kICdaZXJvX3Byb3BvcnRpb24nCiAgIyAgIHBsb3Quc3VidGl0bGU6IHN1YnRpdGxlIGZvciB0aGUgcGxvdDsgY2hhcmFjdGVyCiAgIyAKICAjIFJldHVybnM6CiAgIyAgIGEgZ2dwbG90IGFzIGRlc2NyaWJlZCBhYm92ZQogIAogIGlmKCFhbGwoYygiZ2VuZV9sZW5ndGgiICVpbiUgY29sbmFtZXMoZGYpLCAKICAgICAgICAgICAgIlplcm9fcHJvcG9ydGlvbiIgJWluJSBjb2xuYW1lcyhkZikpKSkgewogICAgc3RvcCgiZGYgbXVzdCBoYXZlIGNvbG5hbWVzICdnZW5lX2xlbmd0aCcgYW5kICdaZXJvX3Byb3BvcnRpb24nIikgIAogIH0KICAgIAogIGRmICU+JQogICAgZ2dwbG90KGFlcyh4ID0gZ2VuZV9sZW5ndGgsIHkgPSBaZXJvX3Byb3BvcnRpb24pKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICBnZW9tX3Ntb290aCgpICsKICAgIHRoZW1lX2J3KCkgKwogICAgbGFicyh4ID0gImdlbmUgbGVuZ3RoIiwgeSA9ICJwcm9wb3J0aW9uIG9mIHNhbXBsZXMgd2l0aCB6ZXJvIGNvdW50IiwKICAgICAgICAgc3VidGl0bGUgPSBwbG90LnN1YnRpdGxlLCB0aXRsZSA9ICJHZW5lIGxlbmd0aCBhbmQgb2JzZXJ2YXRpb25zIikKfQpgYGAKCmBgYHtyfQpwY2FfcGxvdF93cmFwcGVyIDwtIGZ1bmN0aW9uKHByY29tcF9yZXN1bHRzLCBwbG90LnRpdGxlKSB7CiAgIyBnaXZlbiB0aGUgb3V0cHV0IG9mIHByY29tcCwgbWFrZSBhIHNjYXR0ZXJwbG90IG9mIFBDMSBhbmQgUEMyIHdpdGggCiAgIyBnZ2ZvcnRpZnk6OmF1dG9wbG90IHdoZXJlIHRoZSBzYW1wbGVzIGFyZSBjb2xvcmVkIGJ5IHRlY2hub2xvZ3kgKGUuZy4sCiAgIyBtaWNyb2FycmF5LCBSTkEtc2VxKQogICMgcmVxdWlyZXMgdGVjaG5vbG9neV9kZiB0byBiZSBpbiB0aGUgZ2xvYmFsIGVudmlyb25tZW50CiAgIyBBcmdzOgogICMgIHByY29tcF9yZXN1bHRzOiBvdXRwdXQgb2YgcHJjb21wLCBydW4gb24gYSB0cmFuc3Bvc2VkIGV4cHJlc3Npb24gbWF0cml4CiAgIyAgcGxvdC50aXRsZTogdGl0bGUgb2YgdGhlIHBsb3Q7IGNoYXJhY3RlcgogICMKICAjIFJldHVybnMKICAjICAgZ2dwbG90IGFzIGRlc2NyaWJlZCBhYm92ZQogIGF1dG9wbG90KHByY29tcF9yZXN1bHRzLCBkYXRhID0gdGVjaG5vbG9neV9kZiwgYWxwaGEgPSAwLjUsIAogICAgICAgICAgIGNvbG91ciA9ICdjb21wZW5kaXVtX3RlY2hub2xvZ3lfbGFiZWxzJywgc2NhbGUgPSAwKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwobGFiZWxzID0gYygiTUlDUk9BUlJBWSIsICJSTkEtU0VRIiksIAogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIiNFNjlGMDAiLCAiIzU2QjRFOSIpKSArCiAgICB0aGVtZV9idygpICsKICAgIGdndGl0bGUocGxvdC50aXRsZSkgKwogICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikpICsKICAgIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAidGVjaG5vbG9neSIpKQp9CmBgYAoKYGBge3J9CnBjYV9wYWlyc19wbG90X3dyYXBwZXIgPC0gZnVuY3Rpb24ocHJjb21wX3Jlc3VsdHMsIHBsb3QudGl0bGUpIHsKICAjIGdpdmVuIHRoZSBvdXRwdXQgb2YgcHJjb21wLCBtYWtlIGEgcGFpcnMgcGxvdCBvZiBQQzEtNSB3aXRoIHdoZXJlIHRoZSAKICAjIHNhbXBsZXMgYXJlIGNvbG9yZWQgYnkgdGVjaG5vbG9neSAoZS5nLiwgbWljcm9hcnJheSwgUk5BLXNlcSkKICAjIHJlcXVpcmVzIGNvbXBlbmRpdW1fdGVjaG5vbG9neV9sYWJlbHMgdG8gYmUgaW4gdGhlIGdsb2JhbCBlbnZpcm9ubWVudAogICMKICAjIEFyZ3M6CiAgIyAgcHJjb21wX3Jlc3VsdHM6IG91dHB1dCBvZiBwcmNvbXAsIHJ1biBvbiBhIHRyYW5zcG9zZWQgZXhwcmVzc2lvbiBtYXRyaXgKICAjICBwbG90LnRpdGxlOiB0aXRsZSBvZiB0aGUgcGxvdDsgY2hhcmFjdGVyCiAgIwogICMgUmV0dXJucwogICMgICBwbG90IGFzIGRlc2NyaWJlZCBhYm92ZQogIHBhaXJzKHByY29tcF9yZXN1bHRzJHhbLCAxOjVdLCAKICAgICAgICBjb2wgPSBkcGx5cjo6cmVjb2RlKGNvbXBlbmRpdW1fdGVjaG5vbG9neV9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBgTUlDUk9BUlJBWWAgPSAiI0U2OUYwMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBgUk5BU0VRYCA9ICIjNTZCNEU5IiksCiAgICAgICAgbWFpbiA9IHBsb3QudGl0bGUpCn0KYGBgCgojIyMgRGlyZWN0b3JpZXMKCmBgYHtyfQojIGRpcmVjdG9yeSB0byBob2xkIHRoZSBwbG90cwpwbG90X2RpciA8LSAicGxvdHMiCmBgYAoKIyMgUmVhZCBpbiBkYXRhIAoKIyMjIGBsZW5ndGhTY2FsZWRUUE0udHN2YCBmaWxlcwoKYGBge3J9Cmxpc3Rfb2Zfcm5hc2VxX2ZpbGVzIDwtIGxpc3QuZmlsZXMoZmlsZS5wYXRoKCIuLiIsICJzZWxlY3RfaW1wdXRhdGlvbl9tZXRob2QiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRhdGEiLCAicm5hc2VxIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFRSVUUpCmBgYAoKUmVhZCBpbiB0aGUgZmlsZXMgc3VjaCB0aGF0IHRoZXkgYXJlIGluZGl2aWR1YWwgYGRhdGEuZnJhbWVgIGluIGEgbGlzdAoKYGBge3J9CnJuYXNlcV9kZl9saXN0IDwtIGxhcHBseShsaXN0X29mX3JuYXNlcV9maWxlcywgcmVhZHI6OnJlYWRfdHN2KQpgYGAKCmBgYHtyfQojIGJpbmQgYWxsIHRoZSBpbmRpdmlkdWFsIHNhbXBsZSBkYXRhLmZyYW1lIHRvZ2V0aGVyCnJuYXNlcV9kZiA8LSBkcGx5cjo6YmluZF9jb2xzKHJuYXNlcV9kZl9saXN0KQojIHdlIGVuZCB1cCB3aXRoIGEgYnVuY2ggb2YgImV4dHJhIiBnZW5lIGNvbHVtbnMgdGhpcyB3YXksIGJ1dCB0aGV5IHNob3VsZCBiZQojIGluIHRoZSBzYW1lIG9yZGVyCmFsbC5lcXVhbChybmFzZXFfZGYkR2VuZTEsIHJuYXNlcV9kZiRHZW5lMTEyKQpgYGAKCmBgYHtyfQojIGRyb3AgdGhlIGV4dHJhIGdlbmUgY29sdW1ucyBhbmQgc2V0IHRoZSByb3duYW1lcyB0byBnZW5lIGlkZW50aWZpZXJzIC0tIHRoaXMKIyB3aWxsIGhlbHAgd2l0aCBjYWxjdWxhdGluZyB0aGUgcHJvcG9ydGlvbiBvZiB6ZXJvcywgZXRjLgpybmFzZXFfZGYgPC0gcm5hc2VxX2RmICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICB0aWJibGU6OmNvbHVtbl90b19yb3duYW1lcygiR2VuZSIpICU+JQogIGRwbHlyOjpzZWxlY3QoLWRwbHlyOjpjb250YWlucygiR2VuZSIpKQojIHJlbW92ZSB0aGUgbGlzdCBvZiBpbmRpdmlkdWFsIHNhbXBsZSBkYXRhLmZyYW1lCnJtKHJuYXNlcV9kZl9saXN0KQpgYGAKCmBgYHtyfQojIHJlYWQgaW4gZ2VuZSBsZW5ndGhzCmdlbmVfbGVuZ3Roc19kZiA8LSAKICByZWFkcjo6cmVhZF90c3YoZmlsZS5wYXRoKCJkYXRhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGFuaW9fcmVyaW8uR1JDejExX2dlbmVfbGVuZ3Roc19leG9ucy50c3YiKSkKYGBgCgojIyMgQ29tcGVuZGl1bSBnZW5lcywgZXhwcmVzc2lvbiBtYXRyaXgsIHRlY2hub2xvZ3kgbGFiZWxzCgpXZSB3aWxsIGV4cGxvcmUgdGhpcyBnZW5lIGxlbmd0aHMtb2JzZXJ2YXRpb25zL2V4cHJlc3Npb24gbGV2ZWxzIHJlbGF0aW9uc2hpcCBpbgpfYWxsXyBnZW5lcyBhbmQgYWxzbyBzdWJzZXQgdG8gb25seSB0aGUgZ2VuZXMgaW4gdGhlIHRlc3QgY29tcGVuZGl1bSwgc28gd2UgCm5lZWQgdG8gcHVsbCB0aG9zZSBnZW5lIGlkZW50aWZpZXJzIG91dCBvZiB0aGUgdGVzdCBjb21wZW5kaXVtIGV4cHJlc3Npb24KbWF0cml4LgoKYGBge3J9CiMgcmVhZCBpbiBzbWFsbGVyIHRlc3QgY29tcGVuZGl1bSBhbmQgY29udmVydCB0byBleHByZXNzaW9uIG1hdHJpeApjb21wZW5kaXVtX2V4cHJzX2ZpbGUgPC0gZmlsZS5wYXRoKCJyZWZpbmViaW8tZGF0YSIsICJEQU5JT19SRVJJTyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEQU5JT19SRVJJTy50c3YiKQpleHByc19tYXQgPC0gcmVhZHI6OnJlYWRfdHN2KGNvbXBlbmRpdW1fZXhwcnNfZmlsZSwgcHJvZ3Jlc3MgPSBGQUxTRSkgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKCJYMSIpICU+JQogIGFzLm1hdHJpeCgpCgojIGRlcml2ZSB0aGUgdGVjaG5vbG9neSBsYWJlbHMgZnJvbSB0aGUgc2FtcGxlIG5hbWVzCnNhbXBsZV9uYW1lcyA8LSBjb2xuYW1lcyhleHByc19tYXQpCmNvbXBlbmRpdW1fdGVjaG5vbG9neV9sYWJlbHMgPC0gcmVwKCJNSUNST0FSUkFZIiwgbGVuZ3RoKHNhbXBsZV9uYW1lcykpCmNvbXBlbmRpdW1fdGVjaG5vbG9neV9sYWJlbHNbZ3JlcCgibGVuZ3RoU2NhbGVkVFBNIiwgc2FtcGxlX25hbWVzKV0gPC0gIlJOQVNFUSIKIyBnZXQgdGhlIHRlY2hub2xvZ3kgbGFiZWxzIGFzIGEgZGF0YS5mcmFtZSBmb3IgcGxvdHRpbmcgbGF0ZXIKdGVjaG5vbG9neV9kZiA8LSBhcy5kYXRhLmZyYW1lKGNvbXBlbmRpdW1fdGVjaG5vbG9neV9sYWJlbHMpIAoKIyBnZXQgdGhlIGxpc3Qgb2YgY29tcGVuZGl1bSBnZW5lcwpjb21wZW5kaXVtX2dlbmVzIDwtIHJvd25hbWVzKGV4cHJzX21hdCkKYGBgCgojIyBHZW5lIHplcm8gY291bnRzIGFuZCBleHByZXNzaW9uIGxldmVscwoKIyMjIEZvciBlYWNoIGdlbmUsIGhvdyBtYW55IHNhbXBsZXMgaGF2ZSBhIGNvdW50LXNjYWxlIGV4cHJlc3Npb24gdmFsdWUgb2YgemVybz8KCmBgYHtyfQojIGNhbGN1bGF0ZSB0aGUgcHJvcG9ydGlvbiBvZiBzYW1wbGVzIGEgZ2VuZSBpcyB6ZXJvIGZvcgpnZW5lX3plcm9lc19wcm9wb3J0aW9uIDwtIGRhdGEuZnJhbWUoCiAgIkdlbmUiID0gcm93bmFtZXMocm5hc2VxX2RmKSwKICAiWmVyb19wcm9wb3J0aW9uIiA9IGFwcGx5KHJuYXNlcV9kZiwgMSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSBzdW0oeCA9PSAwKSAvIGxlbmd0aCh4KSkKKQpgYGAKCmBgYHtyfQojIGJpbmQgdG8gbGVuZ3RocywgdGhpcyB3aWxsIGJlIHVzZWQgZm9yIHBsb3R0aW5nIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbgojIGdlbmUgbGVuZ3RoIGFuZCBudW1iZXIgb2YgemVyb2VzCnplcm9lc19kZiA8LSBkcGx5cjo6aW5uZXJfam9pbihnZW5lX2xlbmd0aHNfZGYsIGdlbmVfemVyb2VzX3Byb3BvcnRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJHZW5lIikKYGBgCgpgYGB7cn0KIyB0aGVyZSBhcmUgc29tZSByZWFsbHkgbG9uZyBnZW5lcyB0aGF0IHdpbGwgcHJvYmFibHkgbWFrZSBpdCBkaWZmaWN1bHQgdG8KIyB2aXN1YWxpemUgd2hhdCBpcyBnb2luZyBvbgpzdW1tYXJ5KHplcm9lc19kZiRnZW5lX2xlbmd0aCkKYGBgCgojIyMjIEFsbCBnZW5lcwoKYGBge3J9CiMgZmlsdGVyaW5nIGdlbmVzIGJhc2VkIG9uIGxlbmd0aCB0byBnZXQgYSBjbGVhcmVyIHBpY3R1cmUKemVyb2VzX2RmICU+JQogIGRwbHlyOjpmaWx0ZXIoZ2VuZV9sZW5ndGggPD0gMjAwMDApICU+JQogIHplcm9fcHJvcG9ydGlvbl9wb2ludF93cmFwcGVyKHBsb3Quc3VidGl0bGUgPSAiQWxsIGdlbmVzIikKYGBgCgpgYGB7cn0KZ2dzYXZlKGZpbGVuYW1lID0gZmlsZS5wYXRoKHBsb3RfZGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnZW5lX2xlbmd0aF92c196ZXJvX2NvdW50X2FsbF9nZW5lcy5wbmciKSwKICAgICAgIHBsb3QgPSBsYXN0X3Bsb3QoKSkKYGBgCgojIyMjIENvbXBlbmRpdW0gZ2VuZXMgb25seQoKYGBge3J9CiMgc3Vic2V0IGp1c3QgdG8gZ2VuZXMgdGhhdCBhcmUgaW4gdGhlIGNvbXBlbmRpdW0KemVyb2VzX2RmICU+JQogIGRwbHlyOjpmaWx0ZXIoR2VuZSAlaW4lIGNvbXBlbmRpdW1fZ2VuZXMsIAogICAgICAgICAgICAgICAgZ2VuZV9sZW5ndGggPD0gMjAwMDApICU+JQogIHplcm9fcHJvcG9ydGlvbl9wb2ludF93cmFwcGVyKHBsb3Quc3VidGl0bGUgPSAiVGVzdCBjb21wZW5kaXVtIGdlbmVzIG9ubHkiKQpgYGAKCmBgYHtyfQpnZ3NhdmUoZmlsZW5hbWUgPSBmaWxlLnBhdGgocGxvdF9kaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgImdlbmVfbGVuZ3RoX3ZzX3plcm9fY291bnRfY29tcGVuZGl1bV9nZW5lcy5wbmciKSwKICAgICAgIHBsb3QgPSBsYXN0X3Bsb3QoKSkKYGBgCgojIyMjIFRha2Vhd2F5CgpTaG9ydGVyIGdlbmVzIGRvIHRlbmQgdG8gaGF2ZSBhIGxhcmdlciBwcm9wb3J0aW9uIG9mIHNhbXBsZXMgd2hlcmUgdGhleSBoYXZlCnplcm8gdmFsdWVzLiAKSXQgc3RhbmRzIHRvIHJlYXNvbiB0aGF0IGlmIHdlIGRvIG5vdCBfb2JzZXJ2ZV8gc2hvcnRlciBnZW5lcywgd2UgY2FuIG5vdApjb3JyZWN0IGZvciBsZW5ndGgtYmlhcyBlZmZlY3RpdmVseS4gCihSZWNhbGwgdGhhdCBgbGVuZ3RoU2NhbGVkVFBNYCBhcmUgY2FsY3VsYXRlZCB1c2luZyB0aGUgYXZlcmFnZSB0eCBsZW5ndGgKb2JzZXJ2ZWQgYWNyb3NzIHNhbXBsZXMuKQoKIyMgUmVtb3ZpbmcgInNob3J0IiBnZW5lcyBmcm9tIHRoZSB0ZXN0IHNwZWNpZXMgY29tcGVuZGl1bQoKRG9lcyByZW1vdmluZyBzaG9ydGVyIGdlbmVzIGZyb20gdGhlIHNwZWNpZXMgY29tcGVuZGl1bSByZW1vdmUgZXZpZGVuY2Ugb2YgYQp0ZWNobm9sb2d5IGJpYXM/CgojIyMgUENBIC0gYWxsIGdlbmVzCgojIyMjIFBDMSBhbmQgUEMyIGZvciBhbGwgZ2VuZXMKCmBgYHtyfQpwY2FfcmVzdWx0cyA8LSBwcmNvbXAodChleHByc19tYXQpKQpwY2FfcGxvdF93cmFwcGVyKHByY29tcF9yZXN1bHRzID0gcGNhX3Jlc3VsdHMsIAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSAiVGVzdCBDb21wZW5kaXVtIGJ5IFRlY2hub2xvZ3k6IEFsbCBnZW5lcyIpCmBgYAoKYGBge3J9Cmdnc2F2ZShmaWxlbmFtZSA9IGZpbGUucGF0aChwbG90X2RpciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAic21hbGxfdGVzdF9jb21wZW5kaXVtX1BDQS5wbmciKSwKICAgICAgIHBsb3QgPSBsYXN0X3Bsb3QoKSkKYGBgCgojIyMjIFBhaXJzIHBsb3QgKFBDMS01KSBmb3IgYWxsIGdlbmVzCgpgYGB7cn0KcG5nKGZpbGUucGF0aChwbG90X2RpciwgInNtYWxsX3Rlc3RfY29tcGVuZGl1bV9wYWlyc19wbG90LnBuZyIpKQpwY2FfcGFpcnNfcGxvdF93cmFwcGVyKHByY29tcF9yZXN1bHRzID0gcGNhX3Jlc3VsdHMsCiAgICAgICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9ICJUZXN0IENvbXBlbmRpdW0gYnkgVGVjaG5vbG9neTogQWxsIGdlbmVzIikKZGV2Lm9mZigpCmBgYAoKIyMjIERyb3AgdGhlICJzaG9ydCIgZ2VuZXMgZnJvbSB0aGUgZXhwcmVzc2lvbiBtYXRyaXgKCmBgYHtyfQojIHdoYXQgZ2VuZXMgYXJlIHNob3J0PyB1c2luZyAyNXRoIHBlcmNlbnRpbGUKc2hvcnRfZ2VuZXMgPC0gemVyb2VzX2RmICU+JQogIGRwbHlyOjpmaWx0ZXIoZ2VuZV9sZW5ndGggPD0gcXVhbnRpbGUoemVyb2VzX2RmJGdlbmVfbGVuZ3RoLCAwLjI1KSkgJT4lCiAgZHBseXI6OnB1bGwoR2VuZSkKYGBgCgpgYGB7cn0KbGVuZ3RoX2ZpbHRlcmVkX2V4cHJzX21hdCA8LSBleHByc19tYXRbIXJvd25hbWVzKGV4cHJzX21hdCkgJWluJSBzaG9ydF9nZW5lcywgXQpkaW0obGVuZ3RoX2ZpbHRlcmVkX2V4cHJzX21hdCkKYGBgCgojIyMgUENBIHdpdGggdGhlIHNob3J0IGdlbmVzIHJlbW92ZWQKCmBgYHtyfQojIFBDQSBvbiB0aGUgbGVuZ3RoIGZpbHRlcmVkIGV4cHJzIG1hdApmaWx0ZXJlZF9wY2FfcmVzdWx0cyA8LSBwcmNvbXAodChsZW5ndGhfZmlsdGVyZWRfZXhwcnNfbWF0KSkKYGBgCiMjIyMgUEMxIGFuZCBQQzIgc2NhdHRlcnBsb3QKCmBgYHtyfQpwY2FfcGxvdF93cmFwcGVyKHByY29tcF9yZXN1bHRzID0gZmlsdGVyZWRfcGNhX3Jlc3VsdHMsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IHBhc3RlKCJUZXN0IENvbXBlbmRpdW0gYnkgVGVjaG5vbG9neToiLCAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTaG9ydCBnZW5lcyByZW1vdmVkIikpCmBgYAoKYGBge3J9Cmdnc2F2ZShmaWxlbmFtZSA9IGZpbGUucGF0aChwbG90X2RpciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZ2VuZV9sZW5ndGhzX3Rlc3RfY29tcGVuZGl1bV9QQ0EucG5nIiksCiAgICAgICBwbG90ID0gbGFzdF9wbG90KCkpCmBgYAoKIyMjIyBQYWlycyBwbG90IChQQzEtNSkKCmBgYHtyfQpwbmcoZmlsZS5wYXRoKHBsb3RfZGlyLCAiZ2VuZV9sZW5ndGhzX3Rlc3RfY29tcGVuZGl1bV9wYWlyc19wbG90LnBuZyIpKQpwY2FfcGFpcnNfcGxvdF93cmFwcGVyKHByY29tcF9yZXN1bHRzID0gZmlsdGVyZWRfcGNhX3Jlc3VsdHMsCiAgICAgICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IHBhc3RlKCJUZXN0IENvbXBlbmRpdW0gYnkgVGVjaG5vbG9neToiLCAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTaG9ydCBnZW5lcyByZW1vdmVkIikpCmRldi5vZmYoKQpgYGAKCiMjIyMgVGFrZWF3YXkKClJlbW92aW5nIHNob3J0IGdlbmVzIGZyb20gdGhlIHRlc3Qgc3BlY2llcyBjb21wZW5kaXVtIGRvZXMgbm90IHJlbW92ZSAKdGVjaG5vbG9neSBiaWFzLCBidXQgdGhlcmUgYXJlIHNvbWUgY29uc2lkZXJhdGlvbnM6CgoqIFRoZXNlIGRhdGEgaGF2ZSBhbHJlYWR5IGJlZW4gcXVhbnRpbGUgbm9ybWFsaXplZCAtLSBpZiB0aGUgZ2VuZSBsZW5ndGgKY29udHJpYnV0ZXMgdG8gdGhlIGV4cHJlc3Npb24gdmFsdWVzIHByaW9yIHRvIHF1YW50aWxlIG5vcm1hbGl6YXRpb24sIHRoaXMgCmJpYXMgY291bGQgcG9zc2libHkgImxlYWsgaW50byIgdGhlIHF1YW50aWxlIG5vcm1hbGl6ZWQgdmFsdWVzIGZvciBhbGwgdGhlCmdlbmVzLgoKIyMgU2Vzc2lvbiBJbmZvCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==