We observed a slight “platform bias” in 05-pca_test_compendium – do the lowly expressed genes account for the difference in platforms?

Set up

`%>%` <- dplyr::`%>%`

Function for calculating the mode from https://stackoverflow.com/questions/2547402/is-there-a-built-in-function-for-finding-the-mode

calculate_mode <- function(x) {
  # where x is a numeric vector, return the numeric value of the mode 
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}

Read in data

exprs_mat <- readRDS(file.path("data", "larger_compendium_exprs_mat.RDS"))
# inferring sample technology from column name
sample_names <- colnames(exprs_mat)
compendium_technology_labels <- rep("MICROARRAY", length(sample_names))
compendium_technology_labels[grep("SRR|ERR|DRR", sample_names)] <- "RNA-SEQ"
table(compendium_technology_labels)
compendium_technology_labels
MICROARRAY    RNA-SEQ 
      2077      11513 

Split by technology, they have different distributions. More on that below!

seq_mat <- exprs_mat[, which(compendium_technology_labels == "RNA-SEQ")]
array_mat <- exprs_mat[, which(compendium_technology_labels == "MICROARRAY")]

Explore distributions

This test compendium is quantile normalized (QN’d). RNA-seq data, even after all the other processing steps, likely has many zeroes and we can spot that in the data post-QN. Let’s look at the distribution of a single RNA-seq sample for illustrative purposes.

# selecting a single sample for illustrative purposes -- there is no reason for
# using this sample in particular
random_index <- 34
example_ecdf_plot <- data.frame(x = seq_mat[, random_index]) %>%
  ggplot2::ggplot(ggplot2::aes(x)) +
  ggplot2::stat_ecdf(geom = "point", size = 0.75, alpha = 0.75) +
  ggplot2::labs(y = "ecdf(x)", x = "expression value (x)",
                title = "RNA-seq sample") +
  ggplot2::theme_bw()
example_ecdf_plot

We can see that because of the quantile normalization there is a “run” of values at the lowest express value in the sample but it’s not zero.

I think the expression values that get filled in for the zero values can be determined by finding the mode for each sample rather than the minimum value for each sample because there’s nothing forcing the lowest value in a RNA-seq sample to be positive. We’re assuming that the mode for an RNA-seq sample before we QN is zero, which is probably a pretty safe assumption; we can dig into this more later.

Let’s take a look at our example, adding a dotted line for the mode.

example_mode <- calculate_mode(seq_mat[, random_index])
example_ecdf_plot + ggplot2::geom_vline(xintercept = example_mode,
                                        lty = "dotted")

And another example.

another_random_index <- 387
data.frame(x = seq_mat[, another_random_index]) %>%
  ggplot2::ggplot(ggplot2::aes(x)) +
  ggplot2::stat_ecdf(geom = "point", size = 0.75, alpha = 0.75) +
  ggplot2::labs(y = "ecdf(x)", x = "expression value (x)") +
  ggplot2::theme_bw() +
  ggplot2::geom_vline(xintercept = 
                        calculate_mode(seq_mat[, another_random_index]),
                      lty = "dotted")

Picking an array sample at random we can look at the two ECDFs.

data.frame(
  expression_value = c(array_mat[, another_random_index],
                       seq_mat[, another_random_index]),
  technology = c(rep("MICROARRAY", nrow(array_mat)),
                 rep("RNA-SEQ", nrow(seq_mat)))
) %>% 
  ggplot2::ggplot(ggplot2::aes(x = expression_value,
                               colour = technology)) +
  ggplot2::stat_ecdf(geom = "point", size = 0.75, alpha = 0.2) +
  ggplot2::labs(y = "ecdf(x)", x = "expression value (x)") +
  ggplot2::theme_bw() +
  ggplot2::scale_color_manual(labels = c("MICROARRAY", "RNA-SEQ"), 
                              values = c("#E69F00", "#56B4E9"))

Consistent with expectations based on what we’ve laid out above: the microarray sample doesn’t have a run of repeated values at the low end.

Explore lowly expressed genes

The differences between platforms could be driven, in part, by these “used-to-be-zero” values in the RNA-seq data. So what are they and do they tend to be the same genes in many RNA-seq samples?

RNA-seq

Find the gene identifiers of each of the “used-to-be-zero” values

zero_gene_list <- apply(seq_mat, 2, 
                       function(x) { 
                         mode_value <- calculate_mode(x)
                         mode_index <- which(x == mode_value)
                         rownames(seq_mat)[mode_index]
                       })

Is it the same genes over and over again? Let’s count.

zero_counts_per_gene <- data.frame(table(unlist(zero_gene_list)))
colnames(zero_counts_per_gene)[1] <- "Gene"
head(zero_counts_per_gene)

What does the distribution of these counts look like?

zero_counts_per_gene %>%
  ggplot2::ggplot(ggplot2::aes(x = Freq)) +
  ggplot2::geom_bar() +
  ggplot2::theme_bw() +
  ggplot2::labs(x = "number of samples",
                title = "Frequency of zero values") +
  ggplot2::xlim(c(0, ncol(seq_mat)))

This tells use that there a few genes that are missing (zero) in many of the RNA-seq samples we’ve tested (but none that are missing in all samples) and lots of genes that are missing in only a few samples.

Okay, do the genes that are missing in a bunch of samples tend to have lower expression values in the test compendium than all the other genes?

high_zero_count_genes <- zero_counts_per_gene %>%
  dplyr::filter(Freq >= quantile(Freq, 0.75)) %>%
  dplyr::pull(Gene) %>%
  as.character()

Get the average expression for each gene for the microarray samples

array_avg_exprs_df <- data.frame(average_expression = rowMeans(array_mat),
                                 gene = rownames(array_mat)) %>%
  dplyr::mutate(gene_category = dplyr::case_when(
    gene %in% high_zero_count_genes ~ "high zero count",
    TRUE ~ "other"
  ))

Plot the distributions

array_avg_exprs_df %>%
  ggplot2::ggplot(ggplot2::aes(x = average_expression,
                               fill = gene_category)) +
  ggplot2::geom_density(alpha = 0.4) +
  ggplot2::theme_bw() +
  ggplot2::labs(x = "average expression in microarray samples") +
  ggplot2::guides(fill = ggplot2::guide_legend(title = "gene category in RNA-seq")) +
  ggplot2::scale_fill_manual(values = c("#000000", "#FFFFFF"))

plot_name <- file.path("plots", "average_expression_array_zero_count_genes.png")
ggplot2::ggsave(plot_name)
Saving 7 x 7 in image

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    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=C              LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C             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

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.18     rstudioapi_0.7   knitr_1.20       bindr_0.1.1      magrittr_1.5     tidyselect_0.2.4 munsell_0.5.0   
 [8] colorspace_1.3-2 R6_2.2.2         rlang_0.2.2      plyr_1.8.4       dplyr_0.7.6      tools_3.5.1      grid_3.5.1      
[15] gtable_0.2.0     digest_0.6.17    yaml_2.2.0       lazyeval_0.2.1   assertthat_0.2.0 tibble_1.4.2     crayon_1.3.4    
[22] purrr_0.2.5      ggplot2_3.0.0    glue_1.3.0       labeling_0.3     compiler_3.5.1   pillar_1.3.0     scales_1.0.0    
[29] pkgconfig_2.0.2 
LS0tCnRpdGxlOiAiTG93bHkgZXhwcmVzc2VkIGdlbmVzIgpvdXRwdXQ6ICAgCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQphdXRob3I6IEouIFRhcm9uaSBmb3IgQ0NETApkYXRlOiAyMDE5Ci0tLQoKV2Ugb2JzZXJ2ZWQgYSBzbGlnaHQgInBsYXRmb3JtIGJpYXMiIGluIGAwNS1wY2FfdGVzdF9jb21wZW5kaXVtYCAtLQpkbyB0aGUgbG93bHkgZXhwcmVzc2VkIGdlbmVzIGFjY291bnQgZm9yIHRoZSBkaWZmZXJlbmNlIGluIHBsYXRmb3Jtcz8KCiMjIFNldCB1cAoKYGBge3J9CmAlPiVgIDwtIGRwbHlyOjpgJT4lYApgYGAKCkZ1bmN0aW9uIGZvciBjYWxjdWxhdGluZyB0aGUgbW9kZSBmcm9tIApodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8yNTQ3NDAyL2lzLXRoZXJlLWEtYnVpbHQtaW4tZnVuY3Rpb24tZm9yLWZpbmRpbmctdGhlLW1vZGUKCmBgYHtyfQpjYWxjdWxhdGVfbW9kZSA8LSBmdW5jdGlvbih4KSB7CiAgIyB3aGVyZSB4IGlzIGEgbnVtZXJpYyB2ZWN0b3IsIHJldHVybiB0aGUgbnVtZXJpYyB2YWx1ZSBvZiB0aGUgbW9kZSAKICB1eCA8LSB1bmlxdWUoeCkKICB1eFt3aGljaC5tYXgodGFidWxhdGUobWF0Y2goeCwgdXgpKSldCn0KYGBgCgojIyBSZWFkIGluIGRhdGEKCmBgYHtyfQpleHByc19tYXQgPC0gcmVhZFJEUyhmaWxlLnBhdGgoImRhdGEiLCAibGFyZ2VyX2NvbXBlbmRpdW1fZXhwcnNfbWF0LlJEUyIpKQpgYGAKCmBgYHtyfQojIGluZmVycmluZyBzYW1wbGUgdGVjaG5vbG9neSBmcm9tIGNvbHVtbiBuYW1lCnNhbXBsZV9uYW1lcyA8LSBjb2xuYW1lcyhleHByc19tYXQpCmNvbXBlbmRpdW1fdGVjaG5vbG9neV9sYWJlbHMgPC0gcmVwKCJNSUNST0FSUkFZIiwgbGVuZ3RoKHNhbXBsZV9uYW1lcykpCmNvbXBlbmRpdW1fdGVjaG5vbG9neV9sYWJlbHNbZ3JlcCgiU1JSfEVSUnxEUlIiLCBzYW1wbGVfbmFtZXMpXSA8LSAiUk5BLVNFUSIKYGBgCgpgYGB7cn0KdGFibGUoY29tcGVuZGl1bV90ZWNobm9sb2d5X2xhYmVscykKYGBgCgpTcGxpdCBieSB0ZWNobm9sb2d5LCB0aGV5IGhhdmUgZGlmZmVyZW50IGRpc3RyaWJ1dGlvbnMuCk1vcmUgb24gdGhhdCBiZWxvdyEKCmBgYHtyfQpzZXFfbWF0IDwtIGV4cHJzX21hdFssIHdoaWNoKGNvbXBlbmRpdW1fdGVjaG5vbG9neV9sYWJlbHMgPT0gIlJOQS1TRVEiKV0KYXJyYXlfbWF0IDwtIGV4cHJzX21hdFssIHdoaWNoKGNvbXBlbmRpdW1fdGVjaG5vbG9neV9sYWJlbHMgPT0gIk1JQ1JPQVJSQVkiKV0KYGBgCgojIyBFeHBsb3JlIGRpc3RyaWJ1dGlvbnMKClRoaXMgdGVzdCBjb21wZW5kaXVtIGlzIHF1YW50aWxlIG5vcm1hbGl6ZWQgKFFOJ2QpLiAKUk5BLXNlcSBkYXRhLCBldmVuIGFmdGVyIGFsbCB0aGUgb3RoZXIgcHJvY2Vzc2luZyBzdGVwcywgbGlrZWx5IGhhcyBtYW55IAp6ZXJvZXMgYW5kIHdlIGNhbiBzcG90IHRoYXQgaW4gdGhlIGRhdGEgcG9zdC1RTi4KTGV0J3MgbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGEgc2luZ2xlIFJOQS1zZXEgc2FtcGxlIGZvciBpbGx1c3RyYXRpdmUKcHVycG9zZXMuCgpgYGB7cn0KIyBzZWxlY3RpbmcgYSBzaW5nbGUgc2FtcGxlIGZvciBpbGx1c3RyYXRpdmUgcHVycG9zZXMgLS0gdGhlcmUgaXMgbm8gcmVhc29uIGZvcgojIHVzaW5nIHRoaXMgc2FtcGxlIGluIHBhcnRpY3VsYXIKcmFuZG9tX2luZGV4IDwtIDM0CmBgYAoKYGBge3J9CmV4YW1wbGVfZWNkZl9wbG90IDwtIGRhdGEuZnJhbWUoeCA9IHNlcV9tYXRbLCByYW5kb21faW5kZXhdKSAlPiUKICBnZ3Bsb3QyOjpnZ3Bsb3QoZ2dwbG90Mjo6YWVzKHgpKSArCiAgZ2dwbG90Mjo6c3RhdF9lY2RmKGdlb20gPSAicG9pbnQiLCBzaXplID0gMC43NSwgYWxwaGEgPSAwLjc1KSArCiAgZ2dwbG90Mjo6bGFicyh5ID0gImVjZGYoeCkiLCB4ID0gImV4cHJlc3Npb24gdmFsdWUgKHgpIiwKICAgICAgICAgICAgICAgIHRpdGxlID0gIlJOQS1zZXEgc2FtcGxlIikgKwogIGdncGxvdDI6OnRoZW1lX2J3KCkKZXhhbXBsZV9lY2RmX3Bsb3QKYGBgCgpXZSBjYW4gc2VlIHRoYXQgYmVjYXVzZSBvZiB0aGUgcXVhbnRpbGUgbm9ybWFsaXphdGlvbiB0aGVyZSBpcyBhICJydW4iIG9mCnZhbHVlcyBhdCB0aGUgbG93ZXN0IGV4cHJlc3MgdmFsdWUgaW4gdGhlIHNhbXBsZSBfYnV0XyBpdCdzIG5vdCB6ZXJvLgoKSSBfdGhpbmtfIHRoZSBleHByZXNzaW9uIHZhbHVlcyB0aGF0IGdldCBmaWxsZWQgaW4gZm9yIHRoZSB6ZXJvIHZhbHVlcyBjYW4gYmUgCmRldGVybWluZWQgYnkgZmluZGluZyB0aGUgbW9kZSBmb3IgZWFjaCBzYW1wbGUgcmF0aGVyIHRoYW4gdGhlIG1pbmltdW0gdmFsdWUKZm9yIGVhY2ggc2FtcGxlIGJlY2F1c2UgdGhlcmUncyBub3RoaW5nIGZvcmNpbmcgdGhlIGxvd2VzdCB2YWx1ZSBpbiBhIFJOQS1zZXEKc2FtcGxlIHRvIGJlIF9wb3NpdGl2ZV8uCldlJ3JlIGFzc3VtaW5nIHRoYXQgdGhlIF9tb2RlXyBmb3IgYW4gUk5BLXNlcSBzYW1wbGUgYmVmb3JlIHdlIFFOIGlzIHplcm8sIHdoaWNoCmlzIHByb2JhYmx5IGEgcHJldHR5IHNhZmUgYXNzdW1wdGlvbjsKd2UgY2FuIGRpZyBpbnRvIHRoaXMgbW9yZSBsYXRlci4KCkxldCdzIHRha2UgYSBsb29rIGF0IG91ciBleGFtcGxlLCBhZGRpbmcgYSBkb3R0ZWQgbGluZSBmb3IgdGhlIG1vZGUuCgpgYGB7cn0KZXhhbXBsZV9tb2RlIDwtIGNhbGN1bGF0ZV9tb2RlKHNlcV9tYXRbLCByYW5kb21faW5kZXhdKQpleGFtcGxlX2VjZGZfcGxvdCArIGdncGxvdDI6Omdlb21fdmxpbmUoeGludGVyY2VwdCA9IGV4YW1wbGVfbW9kZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGx0eSA9ICJkb3R0ZWQiKQpgYGAKCkFuZCBhbm90aGVyIGV4YW1wbGUuCgpgYGB7cn0KYW5vdGhlcl9yYW5kb21faW5kZXggPC0gMzg3CmRhdGEuZnJhbWUoeCA9IHNlcV9tYXRbLCBhbm90aGVyX3JhbmRvbV9pbmRleF0pICU+JQogIGdncGxvdDI6OmdncGxvdChnZ3Bsb3QyOjphZXMoeCkpICsKICBnZ3Bsb3QyOjpzdGF0X2VjZGYoZ2VvbSA9ICJwb2ludCIsIHNpemUgPSAwLjc1LCBhbHBoYSA9IDAuNzUpICsKICBnZ3Bsb3QyOjpsYWJzKHkgPSAiZWNkZih4KSIsIHggPSAiZXhwcmVzc2lvbiB2YWx1ZSAoeCkiKSArCiAgZ2dwbG90Mjo6dGhlbWVfYncoKSArCiAgZ2dwbG90Mjo6Z2VvbV92bGluZSh4aW50ZXJjZXB0ID0gCiAgICAgICAgICAgICAgICAgICAgICAgIGNhbGN1bGF0ZV9tb2RlKHNlcV9tYXRbLCBhbm90aGVyX3JhbmRvbV9pbmRleF0pLAogICAgICAgICAgICAgICAgICAgICAgbHR5ID0gImRvdHRlZCIpCmBgYAoKUGlja2luZyBhbiBhcnJheSBzYW1wbGUgYXQgcmFuZG9tIHdlIGNhbiBsb29rIGF0IHRoZSB0d28gRUNERnMuCgpgYGB7cn0KZGF0YS5mcmFtZSgKICBleHByZXNzaW9uX3ZhbHVlID0gYyhhcnJheV9tYXRbLCBhbm90aGVyX3JhbmRvbV9pbmRleF0sCiAgICAgICAgICAgICAgICAgICAgICAgc2VxX21hdFssIGFub3RoZXJfcmFuZG9tX2luZGV4XSksCiAgdGVjaG5vbG9neSA9IGMocmVwKCJNSUNST0FSUkFZIiwgbnJvdyhhcnJheV9tYXQpKSwKICAgICAgICAgICAgICAgICByZXAoIlJOQS1TRVEiLCBucm93KHNlcV9tYXQpKSkKKSAlPiUgCiAgZ2dwbG90Mjo6Z2dwbG90KGdncGxvdDI6OmFlcyh4ID0gZXhwcmVzc2lvbl92YWx1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IHRlY2hub2xvZ3kpKSArCiAgZ2dwbG90Mjo6c3RhdF9lY2RmKGdlb20gPSAicG9pbnQiLCBzaXplID0gMC43NSwgYWxwaGEgPSAwLjIpICsKICBnZ3Bsb3QyOjpsYWJzKHkgPSAiZWNkZih4KSIsIHggPSAiZXhwcmVzc2lvbiB2YWx1ZSAoeCkiKSArCiAgZ2dwbG90Mjo6dGhlbWVfYncoKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKGxhYmVscyA9IGMoIk1JQ1JPQVJSQVkiLCAiUk5BLVNFUSIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygiI0U2OUYwMCIsICIjNTZCNEU5IikpCmBgYAoKQ29uc2lzdGVudCB3aXRoIGV4cGVjdGF0aW9ucyBiYXNlZCBvbiB3aGF0IHdlJ3ZlIGxhaWQgb3V0IGFib3ZlOiB0aGUgbWljcm9hcnJheQpzYW1wbGUgZG9lc24ndCBoYXZlIGEgcnVuIG9mIHJlcGVhdGVkIHZhbHVlcyBhdCB0aGUgbG93IGVuZC4KCiMjIEV4cGxvcmUgbG93bHkgZXhwcmVzc2VkIGdlbmVzCgpUaGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBwbGF0Zm9ybXMgY291bGQgYmUgZHJpdmVuLCBpbiBwYXJ0LCBieSB0aGVzZSAKInVzZWQtdG8tYmUtemVybyIgdmFsdWVzIGluIHRoZSBSTkEtc2VxIGRhdGEuClNvIHdoYXQgYXJlIHRoZXkgYW5kIGRvIHRoZXkgdGVuZCB0byBiZSB0aGUgc2FtZSBnZW5lcyBpbiBtYW55IFJOQS1zZXEgc2FtcGxlcz8KCiMjIyBSTkEtc2VxCgpGaW5kIHRoZSBnZW5lIGlkZW50aWZpZXJzIG9mIGVhY2ggb2YgdGhlICJ1c2VkLXRvLWJlLXplcm8iIHZhbHVlcwoKYGBge3J9Cnplcm9fZ2VuZV9saXN0IDwtIGFwcGx5KHNlcV9tYXQsIDIsIAogICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7IAogICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVfdmFsdWUgPC0gY2FsY3VsYXRlX21vZGUoeCkKICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlX2luZGV4IDwtIHdoaWNoKHggPT0gbW9kZV92YWx1ZSkKICAgICAgICAgICAgICAgICAgICAgICAgICByb3duYW1lcyhzZXFfbWF0KVttb2RlX2luZGV4XQogICAgICAgICAgICAgICAgICAgICAgICB9KQpgYGAKCklzIGl0IHRoZSBzYW1lIGdlbmVzIG92ZXIgYW5kIG92ZXIgYWdhaW4/IApMZXQncyBjb3VudC4KCmBgYHtyfQp6ZXJvX2NvdW50c19wZXJfZ2VuZSA8LSBkYXRhLmZyYW1lKHRhYmxlKHVubGlzdCh6ZXJvX2dlbmVfbGlzdCkpKQpjb2xuYW1lcyh6ZXJvX2NvdW50c19wZXJfZ2VuZSlbMV0gPC0gIkdlbmUiCmhlYWQoemVyb19jb3VudHNfcGVyX2dlbmUpCmBgYAoKV2hhdCBkb2VzIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlc2UgY291bnRzIGxvb2sgbGlrZT8KCmBgYHtyfQp6ZXJvX2NvdW50c19wZXJfZ2VuZSAlPiUKICBnZ3Bsb3QyOjpnZ3Bsb3QoZ2dwbG90Mjo6YWVzKHggPSBGcmVxKSkgKwogIGdncGxvdDI6Omdlb21fYmFyKCkgKwogIGdncGxvdDI6OnRoZW1lX2J3KCkgKwogIGdncGxvdDI6OmxhYnMoeCA9ICJudW1iZXIgb2Ygc2FtcGxlcyIsCiAgICAgICAgICAgICAgICB0aXRsZSA9ICJGcmVxdWVuY3kgb2YgemVybyB2YWx1ZXMiKSArCiAgZ2dwbG90Mjo6eGxpbShjKDAsIG5jb2woc2VxX21hdCkpKQpgYGAKClRoaXMgdGVsbHMgdXNlIHRoYXQgdGhlcmUgYSBmZXcgZ2VuZXMgdGhhdCBhcmUgbWlzc2luZyAoemVybykgaW4gbWFueSBvZiB0aGUgClJOQS1zZXEgc2FtcGxlcyB3ZSd2ZSB0ZXN0ZWQgKGJ1dCBub25lIHRoYXQgYXJlIG1pc3NpbmcgaW4gYWxsIHNhbXBsZXMpIGFuZCBsb3RzCm9mIGdlbmVzIHRoYXQgYXJlIG1pc3NpbmcgaW4gb25seSBhIGZldyBzYW1wbGVzLgoKT2theSwgZG8gdGhlIGdlbmVzIHRoYXQgYXJlIG1pc3NpbmcgaW4gYSBidW5jaCBvZiBzYW1wbGVzIHRlbmQgdG8gaGF2ZSBsb3dlcgpleHByZXNzaW9uIHZhbHVlcyBpbiB0aGUgdGVzdCBjb21wZW5kaXVtIHRoYW4gYWxsIHRoZSBvdGhlciBnZW5lcz8KCmBgYHtyfQpoaWdoX3plcm9fY291bnRfZ2VuZXMgPC0gemVyb19jb3VudHNfcGVyX2dlbmUgJT4lCiAgZHBseXI6OmZpbHRlcihGcmVxID49IHF1YW50aWxlKEZyZXEsIDAuNzUpKSAlPiUKICBkcGx5cjo6cHVsbChHZW5lKSAlPiUKICBhcy5jaGFyYWN0ZXIoKQpgYGAKCkdldCB0aGUgYXZlcmFnZSBleHByZXNzaW9uIGZvciBlYWNoIGdlbmUgZm9yIHRoZSBtaWNyb2FycmF5IHNhbXBsZXMKCmBgYHtyfQphcnJheV9hdmdfZXhwcnNfZGYgPC0gZGF0YS5mcmFtZShhdmVyYWdlX2V4cHJlc3Npb24gPSByb3dNZWFucyhhcnJheV9tYXQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lID0gcm93bmFtZXMoYXJyYXlfbWF0KSkgJT4lCiAgZHBseXI6Om11dGF0ZShnZW5lX2NhdGVnb3J5ID0gZHBseXI6OmNhc2Vfd2hlbigKICAgIGdlbmUgJWluJSBoaWdoX3plcm9fY291bnRfZ2VuZXMgfiAiaGlnaCB6ZXJvIGNvdW50IiwKICAgIFRSVUUgfiAib3RoZXIiCiAgKSkKYGBgCgpQbG90IHRoZSBkaXN0cmlidXRpb25zCgpgYGB7cn0KYXJyYXlfYXZnX2V4cHJzX2RmICU+JQogIGdncGxvdDI6OmdncGxvdChnZ3Bsb3QyOjphZXMoeCA9IGF2ZXJhZ2VfZXhwcmVzc2lvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBnZW5lX2NhdGVnb3J5KSkgKwogIGdncGxvdDI6Omdlb21fZGVuc2l0eShhbHBoYSA9IDAuNCkgKwogIGdncGxvdDI6OnRoZW1lX2J3KCkgKwogIGdncGxvdDI6OmxhYnMoeCA9ICJhdmVyYWdlIGV4cHJlc3Npb24gaW4gbWljcm9hcnJheSBzYW1wbGVzIikgKwogIGdncGxvdDI6Omd1aWRlcyhmaWxsID0gZ2dwbG90Mjo6Z3VpZGVfbGVnZW5kKHRpdGxlID0gImdlbmUgY2F0ZWdvcnkgaW4gUk5BLXNlcSIpKSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzAwMDAwMCIsICIjRkZGRkZGIikpCmBgYAoKYGBge3J9CnBsb3RfbmFtZSA8LSBmaWxlLnBhdGgoInBsb3RzIiwgImF2ZXJhZ2VfZXhwcmVzc2lvbl9hcnJheV96ZXJvX2NvdW50X2dlbmVzLnBuZyIpCmdncGxvdDI6Omdnc2F2ZShwbG90X25hbWUpCmBgYAoKIyMgU2Vzc2lvbiBpbmZvCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==