Prepare analysis

Load libraries

library(tidyverse)
library(rhdf5)
#library(DropletUtils) # install but do not load

Set filepaths

knitr::opts_knit$set(root.dir = rprojroot::find_rstudio_root_file(),
                     fig.width=15,
                     digit=5,
                     scipen=8)
options(digits=5, 
        scipen=8,
        future.globals.maxSize = +Inf)
project_dir <- rprojroot::find_rstudio_root_file()
if(is.null(project_dir)){
  project_dir <- getwd()
  warning(sprintf("No rstudio project root file  found. 
                  Setting project directory to current workflow.Rmd file location: %s. 
                  Override if needed.",
                  project_dir))
 
}
message(sprintf("Project directory: %s",
                project_dir))
Project directory: /home/rfarouni/Documents/index_hopping
code_dir <- file.path(project_dir, "code")
source(file.path(code_dir, "1_create_joined_counts_table.R"))
datasets_names <- c("hiseq4000_nonplexed", "hiseq4000_plexed")
names(datasets_names) <- datasets_names
datasets_names
  hiseq4000_nonplexed      hiseq4000_plexed 
"hiseq4000_nonplexed"    "hiseq4000_plexed" 
validation_output_dir <- file.path(project_dir, "data", "hiseq4000_validation")
dir.create(validation_output_dir)
figures_dir <- file.path(validation_output_dir, "figures")
dir.create(figures_dir)

Define functions

get_read_counts <- function(dataset_name) {
  data_dir <- file.path(project_dir, "data", dataset_name)
  output_dir <- file.path(data_dir, "output")
  input_dir <- file.path(data_dir, "input")
  read_counts_filepath <- file.path(
    output_dir,
    paste0(
      dataset_name,
      "_read_counts.rds"
    )
  )
  read_counts <- 
    create_joined_counts(
      input_dir,
      read_counts_filepath
  )
  return(read_counts)
}

Explore and prepare data

Load data

read_counts <- map(datasets_names, get_read_counts)
data_fj <-
  full_join(read_counts$hiseq4000_nonplexed %>%
    select(-outcome),
  read_counts$hiseq4000_plexed,
  by = c("cell", "gene", "umi"),
  suffix = c("_nonplexed", "_plexed")
  )

Anonymize

create keys

old_key <- unique(data_fj$gene)
new_key <-
  sample.int(
    n = length(old_key),
    size = length(old_key)
  )
names(new_key) <- old_key
new_key <-
  enframe(new_key,
    name = "gene",
    value = "gene_new"
  )

recode

data_fj <-
  left_join(data_fj, new_key, by = "gene") %>%
  mutate(
    gene = NULL,
    gene = gene_new
  ) %>%
  select(cell, gene, umi, everything()) %>%
  select(-gene_new) %>%
  mutate_if(is.double, as.integer) %>%
  set_names(c("cell", "gene", "umi", "s1_nonplexed", "s2_nonplexed", "s1_plexed", "s2_plexed", "outcome"))
data_fj

Save data

write_tsv(data_fj,
          file.path(validation_output_dir, "hiseq4000_joined_datatable_plexed_nonplexed.txt"))

Retain molecules that are observed in both datasets

inner join

data <-  
  data_fj %>%
  drop_na()
data

Create goundtruth labels

data <-
  data %>% 
  mutate(label= case_when(
      s1_nonplexed != 0 & s2_nonplexed != 0 & s1_plexed!=0 & s2_plexed!=0~ "r,r",
      s1_nonplexed == 0 & s1_plexed == 0 ~ "0,r",
      s2_nonplexed == 0 & s2_plexed == 0 ~ "r,0",
      s1_nonplexed == 0 & s1_plexed != 0 & s2_plexed == 0 ~ "f,0",
      s1_nonplexed == 0 & s1_plexed != 0 & s2_plexed != 0 ~ "f,r",
      s2_nonplexed == 0 & s2_plexed != 0 & s1_plexed == 0 ~ "0,f",
      s2_nonplexed == 0 & s2_plexed != 0 & s1_plexed != 0 ~ "r,f",
      s1_nonplexed != 0 & s2_nonplexed != 0 & s1_plexed==0 & s2_plexed!=0~ "0,r",
      s1_nonplexed != 0 & s2_nonplexed != 0 & s1_plexed!=0 & s2_plexed==0~ "r,0",
      TRUE                      ~ "NA"
    )) 
data

Tally labels

label_tally <-
  data %>%
  group_by(label) %>%
  tally() %>%
  add_row(label = "TOTAL", n=sum(.$n))
label_tally

There are only 52 (real, real) colliding CUGs out of 9252147 so assumption I is validated. The collision rate is 0.00001.

Filter out colliding CUGs

data <-
  data %>% 
  filter(label!="r,r")

Save data

write_tsv(data, file.path(validation_output_dir,"hiseq4000_inner_joined_with_labels.txt"))

Summary statistics

number of reads (in millions)

data_fj %>%  
  ungroup() %>%
  summarize_at(vars(matches("^s")), list(~ sum(./10^6,  na.rm = TRUE)))
data  %>% 
  summarize_at(vars(matches("^s")), list(~ sum(./10^6)))

number of molecules (in millions)

data_fj %>%  
  mutate_at(vars(matches("^s")), list(~ as.integer(.!=0)))  %>%
  ungroup() %>%
  summarize_at(vars(matches("^s")), list(~ sum(./10^6, na.rm = TRUE)))
 data  %>% 
  mutate_at(vars(matches("^s")), list(~ as.integer(.!=0)))  %>%
  ungroup()  %>% 
  summarize_at(vars(matches("^s")), list(~ sum(./10^6, na.rm = TRUE)))

Examine concordance between samples

Read counts with same CUG (cell-umi-gene) label

p1 <- ggplot(data, aes(x = s1_nonplexed,
                  y = s1_plexed)) 
p1 + geom_hex(bins = 500)+
  geom_abline(slope=.5,intercept=0)

p2 <- ggplot(data, 
            aes(x = s2_nonplexed,
                  y = s2_plexed)) 
p2 + 
  geom_hex(bins = 500)+
  geom_abline(slope=.5,intercept=0)

 #   geom_point() 

Gene expression read counts with same cell-gene label

data_reads_cell_gene <- 
    data %>%  
  group_by(cell, gene) %>%
  summarize_at(vars(matches("^s")),
               list(~ sum(.)))
 ggplot(data_reads_cell_gene,
            aes(x = s1_nonplexed,
                  y = s1_plexed))  + 
  geom_hex(bins = 400) +
  geom_abline(slope=1,intercept=0)+
  scale_x_log10() +
   scale_y_log10() 

ggplot(data_reads_cell_gene,
            aes(x = s2_nonplexed,
                  y = s2_plexed))  + 
  geom_hex(bins = 400) +
    geom_abline(slope=1,intercept=0)+
  scale_x_log10() +
   scale_y_log10() 

NA
ggplot(data_reads_cell_gene,
            aes(x = s1_nonplexed,
                  y = s2_nonplexed))  + 
  geom_hex(bins = 400) +
  geom_abline(slope=1,intercept=0)+
  scale_x_log10() +
   scale_y_log10() 

NA
ggplot(data_reads_cell_gene,
            aes(x = s1_plexed,
                  y = s2_plexed))  + 
  geom_hex(bins = 400) +
  geom_abline(slope=1,intercept=0)+
  scale_x_log10() +
   scale_y_log10() 

NA
ggplot(data_reads_cell_gene,
            aes(x = s1_plexed,
                  y = s2_nonplexed))  + 
  geom_hex(bins = 400) +
  geom_abline(slope=1,intercept=0)+
  scale_x_log10() +
   scale_y_log10() 

NA

## Gene expression UMI counts with same cell-gene label

data_molec_cell_gene <- 
    data %>%   
  mutate(purged= case_when(
      label=="r,0"~ "1,0",
      label=="r,f"~ "1,0",
      label=="0,r"~ "0,1",      
      label=="f,r"~ "0,1",
      label=="f,0"~ "0,0",
      label=="0,f"~ "0,0"
    ))  %>%
  separate(purged, c("s1_purged", "s2_purged"), sep=",", convert=TRUE) %>%  
  group_by(cell, gene) %>%
  summarize_at(vars(matches("^s")),
               list(~ sum(.)))
 data_molec_cell_gene
 ggplot(data_molec_cell_gene,
            aes(x = s1_nonplexed,
                  y = s1_plexed))  + 
  geom_hex(bins = 400) +
  geom_abline(slope=1,intercept=0)+
  scale_x_log10() +
   scale_y_log10() 

ggplot(data_molec_cell_gene,
            aes(x = s2_nonplexed,
                  y = s2_plexed))  + 
  geom_hex(bins = 400) +
  geom_abline(slope=1,intercept=0)+
  scale_x_log10() +
   scale_y_log10() 

NA
ggplot(data_molec_cell_gene,
            aes(x = s1_nonplexed,
                  y = s2_nonplexed))  + 
  geom_hex(bins = 400) +
  geom_abline(slope=1,intercept=0)+
  scale_x_log10() +
   scale_y_log10() 

NA

A visual demonstration of the effects of index hopping

p_hopping <-
  ggplot(data_molec_cell_gene,
            aes(x = s1_plexed,
                  y = s2_plexed))  + 
  #geom_point(alpha=0.4, size=0.4) +
  geom_hex(bins = 450) +
#  geom_abline(slope=1,intercept=0)+
  scale_x_log10() +
   scale_y_log10() +
    labs(x="UMI count by cell and gene (Sample 1 Multiplexed)",
         y="UMI count by cell and gene (Sample 2 Multiplexed)") 
    
ggsave(file.path(figures_dir, "samples_multiplexed_hopping.pdf"), p_hopping, width=9, height=6)
 p_hopping

p_purged <-
  ggplot(data_molec_cell_gene,
            aes(x = s1_purged,
                  y = s2_purged))  + 
  #geom_point(alpha=0.4, size=0.4) +
  geom_hex(bins = 450) +
#  geom_abline(slope=1,intercept=0)+
  scale_x_log10() +
   scale_y_log10() +
    labs(x="UMI count by cell and gene (Sample 1 Multiplexed)",
         y="UMI count by cell and gene (Sample 2 Multiplexed)") 
    
ggsave(file.path(figures_dir, "samples_multiplexed_purged.pdf"), p_purged, width=9, height=6)
p_purged

sessionInfo()
R version 3.6.0 (2019-04-26)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.2 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1

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=en_US.UTF-8    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] hexbin_1.27.3   rhdf5_2.28.0    forcats_0.4.0   stringr_1.4.0   dplyr_0.8.1     purrr_0.3.2     readr_1.3.1     tidyr_0.8.3     tibble_2.1.2   
[10] ggplot2_3.1.1   tidyverse_1.2.1

loaded via a namespace (and not attached):
 [1] tidyselect_0.2.5 xfun_0.7         haven_2.1.0      lattice_0.20-38  colorspace_1.4-1 generics_0.0.2   htmltools_0.3.6  yaml_2.2.0       base64enc_0.1-3 
[10] rlang_0.3.4      pillar_1.4.1     glue_1.3.1       withr_2.1.2      modelr_0.1.4     readxl_1.3.1     plyr_1.8.4       munsell_0.5.0    gtable_0.3.0    
[19] cellranger_1.1.0 rvest_0.3.4      evaluate_0.14    labeling_0.3     knitr_1.23       broom_0.5.2      Rcpp_1.0.1       scales_1.0.0     backports_1.1.4 
[28] jsonlite_1.6     hms_0.4.2        digest_0.6.19    stringi_1.4.3    grid_3.6.0       rprojroot_1.3-2  cli_1.1.0        tools_3.6.0      magrittr_1.5    
[37] lazyeval_0.2.2   crayon_1.3.4     pkgconfig_2.0.2  xml2_1.2.0       lubridate_1.7.4  assertthat_0.2.1 rmarkdown_1.13   httr_1.4.0       rstudioapi_0.10 
[46] Rhdf5lib_1.6.0   R6_2.4.0         nlme_3.1-140     compiler_3.6.0  
LS0tCnRpdGxlOiAiUGhhbnRvbSBQdXJnZSIKc3VidGl0bGU6ICJWYWxpZGF0aW9uIEFuYWx5c2lzOiBQYXJ0IEkiCmF1dGhvcjogCi0gbmFtZTogUmljayBGYXJvdW5pCiAgYWZmaWxpYXRpb246CiAgLSAmY3J1ayBHw6lub21lIFF1w6liZWMgSW5ub3ZhdGlvbiBDZW50cmUsIE1jR2lsbCBVbml2ZXJzaXR5LCBNb250cmVhbCwgQ2FuYWRhCmRhdGU6ICdgciBmb3JtYXQoU3lzLkRhdGUoKSwgIiVZLSVCLSVkIilgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0b2M6IG5vCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCi0tLQoKIyBQcmVwYXJlIGFuYWx5c2lzCgojIyMgTG9hZCBsaWJyYXJpZXMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJoZGY1KQojbGlicmFyeShEcm9wbGV0VXRpbHMpICMgaW5zdGFsbCBidXQgZG8gbm90IGxvYWQKYGBgCgoKIyMjIFNldCBmaWxlcGF0aHMgCgoKYGBge3Igc2V0dXB9CmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gcnByb2pyb290OjpmaW5kX3JzdHVkaW9fcm9vdF9maWxlKCksCiAgICAgICAgICAgICAgICAgICAgIGZpZy53aWR0aD0xNSwKICAgICAgICAgICAgICAgICAgICAgZGlnaXQ9NSwKICAgICAgICAgICAgICAgICAgICAgc2NpcGVuPTgpCm9wdGlvbnMoZGlnaXRzPTUsIAogICAgICAgIHNjaXBlbj04LAogICAgICAgIGZ1dHVyZS5nbG9iYWxzLm1heFNpemUgPSArSW5mKQpgYGAKCgoKYGBge3J9CnByb2plY3RfZGlyIDwtIHJwcm9qcm9vdDo6ZmluZF9yc3R1ZGlvX3Jvb3RfZmlsZSgpCgppZihpcy5udWxsKHByb2plY3RfZGlyKSl7CiAgcHJvamVjdF9kaXIgPC0gZ2V0d2QoKQogIHdhcm5pbmcoc3ByaW50ZigiTm8gcnN0dWRpbyBwcm9qZWN0IHJvb3QgZmlsZSAgZm91bmQuIAogICAgICAgICAgICAgICAgICBTZXR0aW5nIHByb2plY3QgZGlyZWN0b3J5IHRvIGN1cnJlbnQgd29ya2Zsb3cuUm1kIGZpbGUgbG9jYXRpb246ICVzLiAKICAgICAgICAgICAgICAgICAgT3ZlcnJpZGUgaWYgbmVlZGVkLiIsCiAgICAgICAgICAgICAgICAgIHByb2plY3RfZGlyKSkKIAp9Cm1lc3NhZ2Uoc3ByaW50ZigiUHJvamVjdCBkaXJlY3Rvcnk6ICVzIiwKICAgICAgICAgICAgICAgIHByb2plY3RfZGlyKSkKYGBgCgoKCmBgYHtyfQpjb2RlX2RpciA8LSBmaWxlLnBhdGgocHJvamVjdF9kaXIsICJjb2RlIikKc291cmNlKGZpbGUucGF0aChjb2RlX2RpciwgIjFfY3JlYXRlX2pvaW5lZF9jb3VudHNfdGFibGUuUiIpKQoKZGF0YXNldHNfbmFtZXMgPC0gYygiaGlzZXE0MDAwX25vbnBsZXhlZCIsICJoaXNlcTQwMDBfcGxleGVkIikKbmFtZXMoZGF0YXNldHNfbmFtZXMpIDwtIGRhdGFzZXRzX25hbWVzCmRhdGFzZXRzX25hbWVzCmBgYAoKCmBgYHtyfQp2YWxpZGF0aW9uX291dHB1dF9kaXIgPC0gZmlsZS5wYXRoKHByb2plY3RfZGlyLCAiZGF0YSIsICJoaXNlcTQwMDBfdmFsaWRhdGlvbiIpCmRpci5jcmVhdGUodmFsaWRhdGlvbl9vdXRwdXRfZGlyKQpmaWd1cmVzX2RpciA8LSBmaWxlLnBhdGgodmFsaWRhdGlvbl9vdXRwdXRfZGlyLCAiZmlndXJlcyIpCmRpci5jcmVhdGUoZmlndXJlc19kaXIpCmBgYAoKIyMjIERlZmluZSBmdW5jdGlvbnMKCmBgYHtyfQpnZXRfcmVhZF9jb3VudHMgPC0gZnVuY3Rpb24oZGF0YXNldF9uYW1lKSB7CiAgZGF0YV9kaXIgPC0gZmlsZS5wYXRoKHByb2plY3RfZGlyLCAiZGF0YSIsIGRhdGFzZXRfbmFtZSkKICBvdXRwdXRfZGlyIDwtIGZpbGUucGF0aChkYXRhX2RpciwgIm91dHB1dCIpCiAgaW5wdXRfZGlyIDwtIGZpbGUucGF0aChkYXRhX2RpciwgImlucHV0IikKCiAgcmVhZF9jb3VudHNfZmlsZXBhdGggPC0gZmlsZS5wYXRoKAogICAgb3V0cHV0X2RpciwKICAgIHBhc3RlMCgKICAgICAgZGF0YXNldF9uYW1lLAogICAgICAiX3JlYWRfY291bnRzLnJkcyIKICAgICkKICApCgoKICByZWFkX2NvdW50cyA8LSAKICAgIGNyZWF0ZV9qb2luZWRfY291bnRzKAogICAgICBpbnB1dF9kaXIsCiAgICAgIHJlYWRfY291bnRzX2ZpbGVwYXRoCiAgKQoKICByZXR1cm4ocmVhZF9jb3VudHMpCn0KYGBgCgoKIyBFeHBsb3JlIGFuZCBwcmVwYXJlIGRhdGEKCiMjIExvYWQgZGF0YQoKYGBge3J9CnJlYWRfY291bnRzIDwtIG1hcChkYXRhc2V0c19uYW1lcywgZ2V0X3JlYWRfY291bnRzKQpgYGAKCgpgYGB7cn0KZGF0YV9maiA8LQogIGZ1bGxfam9pbihyZWFkX2NvdW50cyRoaXNlcTQwMDBfbm9ucGxleGVkICU+JQogICAgc2VsZWN0KC1vdXRjb21lKSwKICByZWFkX2NvdW50cyRoaXNlcTQwMDBfcGxleGVkLAogIGJ5ID0gYygiY2VsbCIsICJnZW5lIiwgInVtaSIpLAogIHN1ZmZpeCA9IGMoIl9ub25wbGV4ZWQiLCAiX3BsZXhlZCIpCiAgKQpgYGAKCgoKIyMgQW5vbnltaXplCgpjcmVhdGUga2V5cwoKYGBge3J9Cm9sZF9rZXkgPC0gdW5pcXVlKGRhdGFfZmokZ2VuZSkKCm5ld19rZXkgPC0KICBzYW1wbGUuaW50KAogICAgbiA9IGxlbmd0aChvbGRfa2V5KSwKICAgIHNpemUgPSBsZW5ndGgob2xkX2tleSkKICApCm5hbWVzKG5ld19rZXkpIDwtIG9sZF9rZXkKbmV3X2tleSA8LQogIGVuZnJhbWUobmV3X2tleSwKICAgIG5hbWUgPSAiZ2VuZSIsCiAgICB2YWx1ZSA9ICJnZW5lX25ldyIKICApCmBgYAoKcmVjb2RlCgpgYGB7cn0KZGF0YV9maiA8LQogIGxlZnRfam9pbihkYXRhX2ZqLCBuZXdfa2V5LCBieSA9ICJnZW5lIikgJT4lCiAgbXV0YXRlKAogICAgZ2VuZSA9IE5VTEwsCiAgICBnZW5lID0gZ2VuZV9uZXcKICApICU+JQogIHNlbGVjdChjZWxsLCBnZW5lLCB1bWksIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgc2VsZWN0KC1nZW5lX25ldykgJT4lCiAgbXV0YXRlX2lmKGlzLmRvdWJsZSwgYXMuaW50ZWdlcikgJT4lCiAgc2V0X25hbWVzKGMoImNlbGwiLCAiZ2VuZSIsICJ1bWkiLCAiczFfbm9ucGxleGVkIiwgInMyX25vbnBsZXhlZCIsICJzMV9wbGV4ZWQiLCAiczJfcGxleGVkIiwgIm91dGNvbWUiKSkKCmRhdGFfZmoKYGBgCgoKCiMjIyBTYXZlIGRhdGEKCmBgYHtyfQp3cml0ZV90c3YoZGF0YV9maiwKICAgICAgICAgIGZpbGUucGF0aCh2YWxpZGF0aW9uX291dHB1dF9kaXIsICJoaXNlcTQwMDBfam9pbmVkX2RhdGF0YWJsZV9wbGV4ZWRfbm9ucGxleGVkLnR4dCIpKQpgYGAKCgojIyMgUmV0YWluIG1vbGVjdWxlcyB0aGF0IGFyZSBvYnNlcnZlZCBpbiBib3RoIGRhdGFzZXRzCgppbm5lciBqb2luCgpgYGB7cn0KZGF0YSA8LSAgCiAgZGF0YV9maiAlPiUKICBkcm9wX25hKCkKZGF0YQpgYGAKCiMjIyBDcmVhdGUgZ291bmR0cnV0aCBsYWJlbHMKCmBgYHtyfQpkYXRhIDwtCiAgZGF0YSAlPiUgCiAgbXV0YXRlKGxhYmVsPSBjYXNlX3doZW4oCiAgICAgIHMxX25vbnBsZXhlZCAhPSAwICYgczJfbm9ucGxleGVkICE9IDAgJiBzMV9wbGV4ZWQhPTAgJiBzMl9wbGV4ZWQhPTB+ICJyLHIiLAogICAgICBzMV9ub25wbGV4ZWQgPT0gMCAmIHMxX3BsZXhlZCA9PSAwIH4gIjAsciIsCiAgICAgIHMyX25vbnBsZXhlZCA9PSAwICYgczJfcGxleGVkID09IDAgfiAiciwwIiwKICAgICAgczFfbm9ucGxleGVkID09IDAgJiBzMV9wbGV4ZWQgIT0gMCAmIHMyX3BsZXhlZCA9PSAwIH4gImYsMCIsCiAgICAgIHMxX25vbnBsZXhlZCA9PSAwICYgczFfcGxleGVkICE9IDAgJiBzMl9wbGV4ZWQgIT0gMCB+ICJmLHIiLAogICAgICBzMl9ub25wbGV4ZWQgPT0gMCAmIHMyX3BsZXhlZCAhPSAwICYgczFfcGxleGVkID09IDAgfiAiMCxmIiwKICAgICAgczJfbm9ucGxleGVkID09IDAgJiBzMl9wbGV4ZWQgIT0gMCAmIHMxX3BsZXhlZCAhPSAwIH4gInIsZiIsCiAgICAgIHMxX25vbnBsZXhlZCAhPSAwICYgczJfbm9ucGxleGVkICE9IDAgJiBzMV9wbGV4ZWQ9PTAgJiBzMl9wbGV4ZWQhPTB+ICIwLHIiLAogICAgICBzMV9ub25wbGV4ZWQgIT0gMCAmIHMyX25vbnBsZXhlZCAhPSAwICYgczFfcGxleGVkIT0wICYgczJfcGxleGVkPT0wfiAiciwwIiwKICAgICAgVFJVRSAgICAgICAgICAgICAgICAgICAgICB+ICJOQSIKICAgICkpIApkYXRhCmBgYAoKIyMjIFRhbGx5IGxhYmVscwoKCmBgYHtyfQpsYWJlbF90YWxseSA8LQogIGRhdGEgJT4lCiAgZ3JvdXBfYnkobGFiZWwpICU+JQogIHRhbGx5KCkgJT4lCiAgYWRkX3JvdyhsYWJlbCA9ICJUT1RBTCIsIG49c3VtKC4kbikpCmxhYmVsX3RhbGx5CmBgYAoKVGhlcmUgYXJlIG9ubHkgYHIgbGFiZWxfdGFsbHkgJT4lIGZpbHRlcihsYWJlbD09InIsciIpICU+JSBwdWxsKG4pYCAocmVhbCwgcmVhbCkgY29sbGlkaW5nIENVR3Mgb3V0IG9mIGByIGxhYmVsX3RhbGx5ICU+JSBmaWx0ZXIobGFiZWw9PSJUT1RBTCIpICU+JSBwdWxsKG4pYCBzbyBhc3N1bXB0aW9uIEkgaXMgdmFsaWRhdGVkLiBUaGUgY29sbGlzaW9uIHJhdGUgaXMgYHIgKGxhYmVsX3RhbGx5ICU+JSBmaWx0ZXIobGFiZWw9PSJyLHIiKSAlPiUgcHVsbChuKSkvKGxhYmVsX3RhbGx5ICU+JSBmaWx0ZXIobGFiZWw9PSJUT1RBTCIpICU+JSBwdWxsKG4pKWAuCgoKIyMjIEZpbHRlciBvdXQgY29sbGlkaW5nIENVR3MKCmBgYHtyfQpkYXRhIDwtCiAgZGF0YSAlPiUgCiAgZmlsdGVyKGxhYmVsIT0icixyIikKYGBgCgojIyMgU2F2ZSBkYXRhCgpgYGB7cn0Kd3JpdGVfdHN2KGRhdGEsIGZpbGUucGF0aCh2YWxpZGF0aW9uX291dHB1dF9kaXIsImhpc2VxNDAwMF9pbm5lcl9qb2luZWRfd2l0aF9sYWJlbHMudHh0IikpCmBgYAoKCiMjIFN1bW1hcnkgc3RhdGlzdGljcwoKbnVtYmVyIG9mIHJlYWRzIChpbiBtaWxsaW9ucykKCmBgYHtyfQpkYXRhX2ZqICU+JSAgCiAgdW5ncm91cCgpICU+JQogIHN1bW1hcml6ZV9hdCh2YXJzKG1hdGNoZXMoIl5zIikpLCBsaXN0KH4gc3VtKC4vMTBeNiwgIG5hLnJtID0gVFJVRSkpKQpgYGAKCgpgYGB7cn0KZGF0YSAgJT4lIAogIHN1bW1hcml6ZV9hdCh2YXJzKG1hdGNoZXMoIl5zIikpLCBsaXN0KH4gc3VtKC4vMTBeNikpKQpgYGAKCgpudW1iZXIgb2YgbW9sZWN1bGVzIChpbiBtaWxsaW9ucykKCmBgYHtyfQpkYXRhX2ZqICU+JSAgCiAgbXV0YXRlX2F0KHZhcnMobWF0Y2hlcygiXnMiKSksIGxpc3QofiBhcy5pbnRlZ2VyKC4hPTApKSkgICU+JQogIHVuZ3JvdXAoKSAlPiUKICBzdW1tYXJpemVfYXQodmFycyhtYXRjaGVzKCJecyIpKSwgbGlzdCh+IHN1bSguLzEwXjYsIG5hLnJtID0gVFJVRSkpKQpgYGAKCgpgYGB7cn0KIGRhdGEgICU+JSAKICBtdXRhdGVfYXQodmFycyhtYXRjaGVzKCJecyIpKSwgbGlzdCh+IGFzLmludGVnZXIoLiE9MCkpKSAgJT4lCiAgdW5ncm91cCgpICAlPiUgCiAgc3VtbWFyaXplX2F0KHZhcnMobWF0Y2hlcygiXnMiKSksIGxpc3QofiBzdW0oLi8xMF42LCBuYS5ybSA9IFRSVUUpKSkKYGBgCgoKIyBFeGFtaW5lIGNvbmNvcmRhbmNlIGJldHdlZW4gc2FtcGxlcwoKCiMjIFJlYWQgY291bnRzIHdpdGggc2FtZSBDVUcgKGNlbGwtdW1pLWdlbmUpIGxhYmVsCgpgYGB7ciwgZmlnLmhlaWdodD0xMH0KcDEgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0gczFfbm9ucGxleGVkLAogICAgICAgICAgICAgICAgICB5ID0gczFfcGxleGVkKSkgCnAxICsgZ2VvbV9oZXgoYmlucyA9IDUwMCkrCiAgZ2VvbV9hYmxpbmUoc2xvcGU9LjUsaW50ZXJjZXB0PTApCmBgYAoKCgoKYGBge3IsIGZpZy5oZWlnaHQ9MTB9CnAyIDwtIGdncGxvdChkYXRhLCAKICAgICAgICAgICAgYWVzKHggPSBzMl9ub25wbGV4ZWQsCiAgICAgICAgICAgICAgICAgIHkgPSBzMl9wbGV4ZWQpKSAKcDIgKyAKICBnZW9tX2hleChiaW5zID0gNTAwKSsKICBnZW9tX2FibGluZShzbG9wZT0uNSxpbnRlcmNlcHQ9MCkKICMgICBnZW9tX3BvaW50KCkgCmBgYAoKCgoKIyMgR2VuZSBleHByZXNzaW9uIHJlYWQgY291bnRzIHdpdGggc2FtZSBjZWxsLWdlbmUgbGFiZWwKCgpgYGB7cn0KZGF0YV9yZWFkc19jZWxsX2dlbmUgPC0gCiAgICBkYXRhICU+JSAgCiAgZ3JvdXBfYnkoY2VsbCwgZ2VuZSkgJT4lCiAgc3VtbWFyaXplX2F0KHZhcnMobWF0Y2hlcygiXnMiKSksCiAgICAgICAgICAgICAgIGxpc3QofiBzdW0oLikpKQpgYGAKCgoKCgpgYGB7ciwgZmlnLmhlaWdodD0xMH0KIGdncGxvdChkYXRhX3JlYWRzX2NlbGxfZ2VuZSwKICAgICAgICAgICAgYWVzKHggPSBzMV9ub25wbGV4ZWQsCiAgICAgICAgICAgICAgICAgIHkgPSBzMV9wbGV4ZWQpKSAgKyAKICBnZW9tX2hleChiaW5zID0gNDAwKSArCiAgZ2VvbV9hYmxpbmUoc2xvcGU9MSxpbnRlcmNlcHQ9MCkrCiAgc2NhbGVfeF9sb2cxMCgpICsKICAgc2NhbGVfeV9sb2cxMCgpIApgYGAKCgoKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpnZ3Bsb3QoZGF0YV9yZWFkc19jZWxsX2dlbmUsCiAgICAgICAgICAgIGFlcyh4ID0gczJfbm9ucGxleGVkLAogICAgICAgICAgICAgICAgICB5ID0gczJfcGxleGVkKSkgICsgCiAgZ2VvbV9oZXgoYmlucyA9IDQwMCkgKwogICAgZ2VvbV9hYmxpbmUoc2xvcGU9MSxpbnRlcmNlcHQ9MCkrCiAgc2NhbGVfeF9sb2cxMCgpICsKICAgc2NhbGVfeV9sb2cxMCgpIAogCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9MTB9CmdncGxvdChkYXRhX3JlYWRzX2NlbGxfZ2VuZSwKICAgICAgICAgICAgYWVzKHggPSBzMV9ub25wbGV4ZWQsCiAgICAgICAgICAgICAgICAgIHkgPSBzMl9ub25wbGV4ZWQpKSAgKyAKICBnZW9tX2hleChiaW5zID0gNDAwKSArCiAgZ2VvbV9hYmxpbmUoc2xvcGU9MSxpbnRlcmNlcHQ9MCkrCiAgc2NhbGVfeF9sb2cxMCgpICsKICAgc2NhbGVfeV9sb2cxMCgpIAogCmBgYAoKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpnZ3Bsb3QoZGF0YV9yZWFkc19jZWxsX2dlbmUsCiAgICAgICAgICAgIGFlcyh4ID0gczFfcGxleGVkLAogICAgICAgICAgICAgICAgICB5ID0gczJfcGxleGVkKSkgICsgCiAgZ2VvbV9oZXgoYmlucyA9IDQwMCkgKwogIGdlb21fYWJsaW5lKHNsb3BlPTEsaW50ZXJjZXB0PTApKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgIHNjYWxlX3lfbG9nMTAoKSAKIApgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpnZ3Bsb3QoZGF0YV9yZWFkc19jZWxsX2dlbmUsCiAgICAgICAgICAgIGFlcyh4ID0gczFfcGxleGVkLAogICAgICAgICAgICAgICAgICB5ID0gczJfbm9ucGxleGVkKSkgICsgCiAgZ2VvbV9oZXgoYmlucyA9IDQwMCkgKwogIGdlb21fYWJsaW5lKHNsb3BlPTEsaW50ZXJjZXB0PTApKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgIHNjYWxlX3lfbG9nMTAoKSAKIApgYGAKCiAjIyBHZW5lIGV4cHJlc3Npb24gVU1JIGNvdW50cyB3aXRoIHNhbWUgY2VsbC1nZW5lIGxhYmVsCiAKCmBgYHtyfQpkYXRhX21vbGVjX2NlbGxfZ2VuZSA8LSAKICAgIGRhdGEgJT4lICAgCiAgbXV0YXRlKHB1cmdlZD0gY2FzZV93aGVuKAogICAgICBsYWJlbD09InIsMCJ+ICIxLDAiLAogICAgICBsYWJlbD09InIsZiJ+ICIxLDAiLAogICAgICBsYWJlbD09IjAsciJ+ICIwLDEiLCAgICAgIAogICAgICBsYWJlbD09ImYsciJ+ICIwLDEiLAogICAgICBsYWJlbD09ImYsMCJ+ICIwLDAiLAogICAgICBsYWJlbD09IjAsZiJ+ICIwLDAiCiAgICApKSAgJT4lCiAgc2VwYXJhdGUocHVyZ2VkLCBjKCJzMV9wdXJnZWQiLCAiczJfcHVyZ2VkIiksIHNlcD0iLCIsIGNvbnZlcnQ9VFJVRSkgJT4lICAKICBncm91cF9ieShjZWxsLCBnZW5lKSAlPiUKICBzdW1tYXJpemVfYXQodmFycyhtYXRjaGVzKCJecyIpKSwKICAgICAgICAgICAgICAgbGlzdCh+IHN1bSguKSkpCgogZGF0YV9tb2xlY19jZWxsX2dlbmUKYGBgCgoKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQogZ2dwbG90KGRhdGFfbW9sZWNfY2VsbF9nZW5lLAogICAgICAgICAgICBhZXMoeCA9IHMxX25vbnBsZXhlZCwKICAgICAgICAgICAgICAgICAgeSA9IHMxX3BsZXhlZCkpICArIAogIGdlb21faGV4KGJpbnMgPSA0MDApICsKICBnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD0wKSsKICBzY2FsZV94X2xvZzEwKCkgKwogICBzY2FsZV95X2xvZzEwKCkgCmBgYAoKCgoKYGBge3IsIGZpZy5oZWlnaHQ9MTB9CmdncGxvdChkYXRhX21vbGVjX2NlbGxfZ2VuZSwKICAgICAgICAgICAgYWVzKHggPSBzMl9ub25wbGV4ZWQsCiAgICAgICAgICAgICAgICAgIHkgPSBzMl9wbGV4ZWQpKSAgKyAKICBnZW9tX2hleChiaW5zID0gNDAwKSArCiAgZ2VvbV9hYmxpbmUoc2xvcGU9MSxpbnRlcmNlcHQ9MCkrCiAgc2NhbGVfeF9sb2cxMCgpICsKICAgc2NhbGVfeV9sb2cxMCgpIAogCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9MTB9CmdncGxvdChkYXRhX21vbGVjX2NlbGxfZ2VuZSwKICAgICAgICAgICAgYWVzKHggPSBzMV9ub25wbGV4ZWQsCiAgICAgICAgICAgICAgICAgIHkgPSBzMl9ub25wbGV4ZWQpKSAgKyAKICBnZW9tX2hleChiaW5zID0gNDAwKSArCiAgZ2VvbV9hYmxpbmUoc2xvcGU9MSxpbnRlcmNlcHQ9MCkrCiAgc2NhbGVfeF9sb2cxMCgpICsKICAgc2NhbGVfeV9sb2cxMCgpIAogCmBgYAoKIyMjIEEgdmlzdWFsIGRlbW9uc3RyYXRpb24gb2YgdGhlIGVmZmVjdHMgb2YgaW5kZXggaG9wcGluZwoKYGBge3IsIGZpZy5oZWlnaHQ9MTB9CnBfaG9wcGluZyA8LQogIGdncGxvdChkYXRhX21vbGVjX2NlbGxfZ2VuZSwKICAgICAgICAgICAgYWVzKHggPSBzMV9wbGV4ZWQsCiAgICAgICAgICAgICAgICAgIHkgPSBzMl9wbGV4ZWQpKSAgKyAKICAjZ2VvbV9wb2ludChhbHBoYT0wLjQsIHNpemU9MC40KSArCiAgZ2VvbV9oZXgoYmlucyA9IDQ1MCkgKwojICBnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD0wKSsKICBzY2FsZV94X2xvZzEwKCkgKwogICBzY2FsZV95X2xvZzEwKCkgKwogICAgbGFicyh4PSJVTUkgY291bnQgYnkgY2VsbCBhbmQgZ2VuZSAoU2FtcGxlIDEgTXVsdGlwbGV4ZWQpIiwKICAgICAgICAgeT0iVU1JIGNvdW50IGJ5IGNlbGwgYW5kIGdlbmUgKFNhbXBsZSAyIE11bHRpcGxleGVkKSIpIAogICAgCgpnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLCAic2FtcGxlc19tdWx0aXBsZXhlZF9ob3BwaW5nLnBkZiIpLCBwX2hvcHBpbmcsIHdpZHRoPTksIGhlaWdodD02KQogcF9ob3BwaW5nCmBgYAoKCgpgYGB7ciwgZmlnLmhlaWdodD04fQpwX3B1cmdlZCA8LQogIGdncGxvdChkYXRhX21vbGVjX2NlbGxfZ2VuZSwKICAgICAgICAgICAgYWVzKHggPSBzMV9wdXJnZWQsCiAgICAgICAgICAgICAgICAgIHkgPSBzMl9wdXJnZWQpKSAgKyAKICAjZ2VvbV9wb2ludChhbHBoYT0wLjQsIHNpemU9MC40KSArCiAgZ2VvbV9oZXgoYmlucyA9IDQ1MCkgKwojICBnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD0wKSsKICBzY2FsZV94X2xvZzEwKCkgKwogICBzY2FsZV95X2xvZzEwKCkgKwogICAgbGFicyh4PSJVTUkgY291bnQgYnkgY2VsbCBhbmQgZ2VuZSAoU2FtcGxlIDEgTXVsdGlwbGV4ZWQpIiwKICAgICAgICAgeT0iVU1JIGNvdW50IGJ5IGNlbGwgYW5kIGdlbmUgKFNhbXBsZSAyIE11bHRpcGxleGVkKSIpIAogICAgCgpnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLCAic2FtcGxlc19tdWx0aXBsZXhlZF9wdXJnZWQucGRmIiksIHBfcHVyZ2VkLCB3aWR0aD05LCBoZWlnaHQ9NikKcF9wdXJnZWQKYGBgCgoKCiAKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgoK