We observed a slight “platform bias” in 05-pca_test_compendium
– do the lowly expressed genes account for the difference in platforms?
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==