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)
'/home/rfarouni/Documents/index_hopping/data/hiseq4000_validation' already exists
figures_dir <- file.path(validation_output_dir, "figures")
dir.create(figures_dir)
'/home/rfarouni/Documents/index_hopping/data/hiseq4000_validation/figures' already exists

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")
  )
data_fj

rename

data_fj <-
  data_fj %>%
  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_hg38_ensembl95.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_hg38_ensembl95.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

 data_molec_cell_gene <- 
  data_molec_cell_gene %>% 
  mutate(source= case_when(s1_purged==0 ~ "s2",
                       s2_purged==0 ~ "s1",
                       TRUE ~"s1s2"))
 p_hopping <- 
  ggplot(data_molec_cell_gene )  + 
  geom_point(
            aes(x = s1_plexed,
                  y = s2_plexed,
                color=source),
            alpha=0.4, size=0.4) +
#  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)") +
  theme_bw() +
  theme(axis.line = element_line(colour = "black"),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    panel.background = element_blank()) +
   expand_limits(x = c(0, 650000), y = c(0, 110000))
  
ggsave(file.path(figures_dir, "samples_multiplexed_hopping.jpeg"), p_hopping, width=9, height=6, dpi=320)
 p_hopping
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio

p_purged <-
ggplot(data_molec_cell_gene)  + 
  geom_point( aes(x = s1_purged,
                  y = s2_purged,
                  color=source),
              size=1.2) +
#  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)") +
  theme_bw()   +
  theme(axis.line = element_line(colour = "black"),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    panel.background = element_blank()) +
   expand_limits(x = c(0, 650000), y = c(0, 110000))

ggsave(file.path(figures_dir, "samples_multiplexed_purged.jpeg"), p_purged, width=9, height=6)
p_purged

data_molec_cell <-
  data_molec_cell_gene %>%
  select(-gene, -source) %>%
  group_by(cell) %>%
  summarize_all(sum)
  
data_molec_cell <-
  data_molec_cell %>%
  mutate(source= case_when(s1_purged==0 ~ "s2",
                       s2_purged==0 ~ "s1",
                       TRUE ~"s1s2"))
p_phantomcells <- 
  ggplot(data_molec_cell)  + 
  geom_point(
            aes(x = s1_plexed,
                  y = s2_plexed,
                color=source),
            alpha=0.4, size=0.3) +
#  geom_abline(slope=1,intercept=0)+
  scale_x_log10() +
   scale_y_log10() +
    labs(x="UMI count by cell (Sample 1 Multiplexed)",
         y="UMI count by cell (Sample 2 Multiplexed)") +
  theme_bw()   +
  theme(axis.line = element_line(colour = "black"),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    panel.background = element_blank()) +
   expand_limits(x = c(0, 1000000), y = c(0, 300000))
ggsave(file.path(figures_dir, "samples_multiplexed_phantomcells.jpeg"), p_phantomcells, width=9, height=6, dpi=320)
p_phantomcells

p_cells <- 
  ggplot(data_molec_cell)  + 
  geom_point(
            aes(x = s1_purged,
                  y = s2_purged,
                color=source),
            alpha=0.5, size=0.6) +
#  geom_abline(slope=1,intercept=0)+
  scale_x_log10() +
   scale_y_log10() +
    labs(x="UMI count by cell (Sample 1 Multiplexed)",
         y="UMI count by cell (Sample 2 Multiplexed)") +
  theme_bw()   +
  theme(axis.line = element_line(colour = "black"),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    panel.background = element_blank())  +
   expand_limits(x = c(0, 1000000), y = c(0, 300000))
ggsave(file.path(figures_dir, "samples_multiplexed_cells.jpeg"), p_cells, width=9, height=6, dpi=320)
p_cells

sessionInfo()
R version 3.6.3 (2020-02-29)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.4 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       
 [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] 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] rhdf5_2.30.1    forcats_0.5.0   stringr_1.4.0   dplyr_0.8.5     purrr_0.3.3     readr_1.3.1    
 [7] tidyr_1.0.2     tibble_3.0.0    ggplot2_3.3.0   tidyverse_1.3.0

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.4         lubridate_1.7.8    lattice_0.20-40    prettyunits_1.1.1  ps_1.3.2          
 [6] utf8_1.1.4         digest_0.6.25      assertthat_0.2.1   rprojroot_1.3-2    packrat_0.5.0     
[11] R6_2.4.1           cellranger_1.1.0   backports_1.1.6    reprex_0.3.0       stats4_3.6.3      
[16] evaluate_0.14      httr_1.4.1         pillar_1.4.3       rlang_0.4.5        readxl_1.3.1      
[21] rstudioapi_0.11    hexbin_1.28.1      callr_3.4.3        rmarkdown_2.1      labeling_0.3      
[26] loo_2.2.0          munsell_0.5.0      broom_0.5.5        compiler_3.6.3     modelr_0.1.6      
[31] xfun_0.12          rstan_2.19.3       base64enc_0.1-3    pkgconfig_2.0.3    pkgbuild_1.0.6    
[36] htmltools_0.4.0    tidyselect_1.0.0   gridExtra_2.3      matrixStats_0.56.0 fansi_0.4.1       
[41] crayon_1.3.4       dbplyr_1.4.2       withr_2.1.2        grid_3.6.3         nlme_3.1-144      
[46] jsonlite_1.6.1     gtable_0.3.0       lifecycle_0.2.0    DBI_1.1.0          magrittr_1.5      
[51] StanHeaders_2.19.2 scales_1.1.0       cli_2.0.2          stringi_1.4.6      farver_2.0.3      
[56] fs_1.4.1           xml2_1.3.0         ellipsis_0.3.0     generics_0.0.2     vctrs_0.2.4       
[61] Rhdf5lib_1.8.0     tools_3.6.3        glue_1.4.0         hms_0.5.3          yaml_2.2.1        
[66] processx_3.4.2     parallel_3.6.3     inline_0.3.15      colorspace_1.4-1   rvest_0.3.5       
[71] knitr_1.28         haven_2.2.0       
LS0tCnRpdGxlOiAiUGhhbnRvbSBQdXJnZSIKc3VidGl0bGU6ICJWYWxpZGF0aW9uIEFuYWx5c2lzOiBQYXJ0IEkiCmF1dGhvcjogCi0gbmFtZTogUmljayBGYXJvdW5pCiAgYWZmaWxpYXRpb246CiAgLSAmY3J1ayBHw6lub21lIFF1w6liZWMgSW5ub3ZhdGlvbiBDZW50cmUsIE1jR2lsbCBVbml2ZXJzaXR5LCBNb250cmVhbCwgQ2FuYWRhCmRhdGU6ICdgciBmb3JtYXQoU3lzLkRhdGUoKSwgIiVZLSVCLSVkIilgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0b2M6IG5vCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCi0tLQoKIyBQcmVwYXJlIGFuYWx5c2lzCgojIyMgTG9hZCBsaWJyYXJpZXMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJoZGY1KQojbGlicmFyeShEcm9wbGV0VXRpbHMpICMgaW5zdGFsbCBidXQgZG8gbm90IGxvYWQKYGBgCgoKIyMjIFNldCBmaWxlcGF0aHMgCgoKYGBge3Igc2V0dXB9CmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gcnByb2pyb290OjpmaW5kX3JzdHVkaW9fcm9vdF9maWxlKCksCiAgICAgICAgICAgICAgICAgICAgIGZpZy53aWR0aD0xNSwKICAgICAgICAgICAgICAgICAgICAgZGlnaXQ9NSwKICAgICAgICAgICAgICAgICAgICAgc2NpcGVuPTgpCm9wdGlvbnMoZGlnaXRzPTUsIAogICAgICAgIHNjaXBlbj04LAogICAgICAgIGZ1dHVyZS5nbG9iYWxzLm1heFNpemUgPSArSW5mKQpgYGAKCgoKYGBge3J9CnByb2plY3RfZGlyIDwtIHJwcm9qcm9vdDo6ZmluZF9yc3R1ZGlvX3Jvb3RfZmlsZSgpCgppZihpcy5udWxsKHByb2plY3RfZGlyKSl7CiAgcHJvamVjdF9kaXIgPC0gZ2V0d2QoKQogIHdhcm5pbmcoc3ByaW50ZigiTm8gcnN0dWRpbyBwcm9qZWN0IHJvb3QgZmlsZSAgZm91bmQuIAogICAgICAgICAgICAgICAgICBTZXR0aW5nIHByb2plY3QgZGlyZWN0b3J5IHRvIGN1cnJlbnQgd29ya2Zsb3cuUm1kIGZpbGUgbG9jYXRpb246ICVzLiAKICAgICAgICAgICAgICAgICAgT3ZlcnJpZGUgaWYgbmVlZGVkLiIsCiAgICAgICAgICAgICAgICAgIHByb2plY3RfZGlyKSkKIAp9Cm1lc3NhZ2Uoc3ByaW50ZigiUHJvamVjdCBkaXJlY3Rvcnk6ICVzIiwKICAgICAgICAgICAgICAgIHByb2plY3RfZGlyKSkKYGBgCgoKCmBgYHtyfQpjb2RlX2RpciA8LSBmaWxlLnBhdGgocHJvamVjdF9kaXIsICJjb2RlIikKc291cmNlKGZpbGUucGF0aChjb2RlX2RpciwgIjFfY3JlYXRlX2pvaW5lZF9jb3VudHNfdGFibGUuUiIpKQoKZGF0YXNldHNfbmFtZXMgPC0gYygiaGlzZXE0MDAwX25vbnBsZXhlZCIsICJoaXNlcTQwMDBfcGxleGVkIikKbmFtZXMoZGF0YXNldHNfbmFtZXMpIDwtIGRhdGFzZXRzX25hbWVzCmRhdGFzZXRzX25hbWVzCmBgYAoKCmBgYHtyfQp2YWxpZGF0aW9uX291dHB1dF9kaXIgPC0gZmlsZS5wYXRoKHByb2plY3RfZGlyLCAiZGF0YSIsICJoaXNlcTQwMDBfdmFsaWRhdGlvbiIpCmRpci5jcmVhdGUodmFsaWRhdGlvbl9vdXRwdXRfZGlyKQpmaWd1cmVzX2RpciA8LSBmaWxlLnBhdGgodmFsaWRhdGlvbl9vdXRwdXRfZGlyLCAiZmlndXJlcyIpCmRpci5jcmVhdGUoZmlndXJlc19kaXIpCmBgYAoKIyMjIERlZmluZSBmdW5jdGlvbnMKCmBgYHtyfQpnZXRfcmVhZF9jb3VudHMgPC0gZnVuY3Rpb24oZGF0YXNldF9uYW1lKSB7CiAgZGF0YV9kaXIgPC0gZmlsZS5wYXRoKHByb2plY3RfZGlyLCAiZGF0YSIsIGRhdGFzZXRfbmFtZSkKICBvdXRwdXRfZGlyIDwtIGZpbGUucGF0aChkYXRhX2RpciwgIm91dHB1dCIpCiAgaW5wdXRfZGlyIDwtIGZpbGUucGF0aChkYXRhX2RpciwgImlucHV0IikKCiAgcmVhZF9jb3VudHNfZmlsZXBhdGggPC0gZmlsZS5wYXRoKAogICAgb3V0cHV0X2RpciwKICAgIHBhc3RlMCgKICAgICAgZGF0YXNldF9uYW1lLAogICAgICAiX3JlYWRfY291bnRzLnJkcyIKICAgICkKICApCgoKICByZWFkX2NvdW50cyA8LSAKICAgIGNyZWF0ZV9qb2luZWRfY291bnRzKAogICAgICBpbnB1dF9kaXIsCiAgICAgIHJlYWRfY291bnRzX2ZpbGVwYXRoCiAgKQoKICByZXR1cm4ocmVhZF9jb3VudHMpCn0KYGBgCgoKIyBFeHBsb3JlIGFuZCBwcmVwYXJlIGRhdGEKCiMjIExvYWQgZGF0YQoKYGBge3J9CnJlYWRfY291bnRzIDwtIG1hcChkYXRhc2V0c19uYW1lcywgZ2V0X3JlYWRfY291bnRzKQpgYGAKCgpgYGB7cn0KZGF0YV9maiA8LQogIGZ1bGxfam9pbihyZWFkX2NvdW50cyRoaXNlcTQwMDBfbm9ucGxleGVkICU+JQogICAgc2VsZWN0KC1vdXRjb21lKSwKICByZWFkX2NvdW50cyRoaXNlcTQwMDBfcGxleGVkLAogIGJ5ID0gYygiY2VsbCIsICJnZW5lIiwgInVtaSIpLAogIHN1ZmZpeCA9IGMoIl9ub25wbGV4ZWQiLCAiX3BsZXhlZCIpCiAgKQpgYGAKYGBge3J9CmRhdGFfZmoKYGBgCgoKCgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQojIGFub255bWl6ZSBhbmQgY3JlYXRlIGtleXMKb2xkX2tleSA8LSB1bmlxdWUoZGF0YV9maiRnZW5lKQoKbmV3X2tleSA8LQogIHNhbXBsZS5pbnQoCiAgICBuID0gbGVuZ3RoKG9sZF9rZXkpLAogICAgc2l6ZSA9IGxlbmd0aChvbGRfa2V5KQogICkKbmFtZXMobmV3X2tleSkgPC0gb2xkX2tleQpuZXdfa2V5IDwtCiAgZW5mcmFtZShuZXdfa2V5LAogICAgbmFtZSA9ICJnZW5lIiwKICAgIHZhbHVlID0gImdlbmVfbmV3IgogICkKCmRhdGFfZmogPC0KICBsZWZ0X2pvaW4oZGF0YV9maiwgbmV3X2tleSwgYnkgPSAiZ2VuZSIpICU+JQogIG11dGF0ZSgKICAgIGdlbmUgPSBOVUxMLAogICAgZ2VuZSA9IGdlbmVfbmV3CiAgKQpgYGAKCiMjIHJlbmFtZQoKYGBge3J9CmRhdGFfZmogPC0KICBkYXRhX2ZqICU+JQogIHNlbGVjdChjZWxsLCBnZW5lLCB1bWksIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgI3NlbGVjdCgtZ2VuZV9uZXcpICU+JQogIG11dGF0ZV9pZihpcy5kb3VibGUsIGFzLmludGVnZXIpICU+JQogIHNldF9uYW1lcyhjKCJjZWxsIiwgImdlbmUiLCAidW1pIiwgInMxX25vbnBsZXhlZCIsICJzMl9ub25wbGV4ZWQiLCAiczFfcGxleGVkIiwgInMyX3BsZXhlZCIsICJvdXRjb21lIikpCgpkYXRhX2ZqCmBgYAoKCgojIyMgU2F2ZSBkYXRhCgpgYGB7cn0Kd3JpdGVfdHN2KGRhdGFfZmosCiAgICAgICAgICBmaWxlLnBhdGgodmFsaWRhdGlvbl9vdXRwdXRfZGlyLCAiaGlzZXE0MDAwX2pvaW5lZF9kYXRhdGFibGVfcGxleGVkX25vbnBsZXhlZF9oZzM4X2Vuc2VtYmw5NS50eHQiKSkKYGBgCgoKIyMjIFJldGFpbiBtb2xlY3VsZXMgdGhhdCBhcmUgb2JzZXJ2ZWQgaW4gYm90aCBkYXRhc2V0cwoKaW5uZXIgam9pbgoKYGBge3J9CmRhdGEgPC0gIAogIGRhdGFfZmogJT4lCiAgZHJvcF9uYSgpCmRhdGEKYGBgCgojIyMgQ3JlYXRlIGdvdW5kdHJ1dGggbGFiZWxzCgpgYGB7cn0KZGF0YSA8LQogIGRhdGEgJT4lIAogIG11dGF0ZShsYWJlbD0gY2FzZV93aGVuKAogICAgICBzMV9ub25wbGV4ZWQgIT0gMCAmIHMyX25vbnBsZXhlZCAhPSAwICYgczFfcGxleGVkIT0wICYgczJfcGxleGVkIT0wfiAicixyIiwKICAgICAgczFfbm9ucGxleGVkID09IDAgJiBzMV9wbGV4ZWQgPT0gMCB+ICIwLHIiLAogICAgICBzMl9ub25wbGV4ZWQgPT0gMCAmIHMyX3BsZXhlZCA9PSAwIH4gInIsMCIsCiAgICAgIHMxX25vbnBsZXhlZCA9PSAwICYgczFfcGxleGVkICE9IDAgJiBzMl9wbGV4ZWQgPT0gMCB+ICJmLDAiLAogICAgICBzMV9ub25wbGV4ZWQgPT0gMCAmIHMxX3BsZXhlZCAhPSAwICYgczJfcGxleGVkICE9IDAgfiAiZixyIiwKICAgICAgczJfbm9ucGxleGVkID09IDAgJiBzMl9wbGV4ZWQgIT0gMCAmIHMxX3BsZXhlZCA9PSAwIH4gIjAsZiIsCiAgICAgIHMyX25vbnBsZXhlZCA9PSAwICYgczJfcGxleGVkICE9IDAgJiBzMV9wbGV4ZWQgIT0gMCB+ICJyLGYiLAogICAgICBzMV9ub25wbGV4ZWQgIT0gMCAmIHMyX25vbnBsZXhlZCAhPSAwICYgczFfcGxleGVkPT0wICYgczJfcGxleGVkIT0wfiAiMCxyIiwKICAgICAgczFfbm9ucGxleGVkICE9IDAgJiBzMl9ub25wbGV4ZWQgIT0gMCAmIHMxX3BsZXhlZCE9MCAmIHMyX3BsZXhlZD09MH4gInIsMCIsCiAgICAgIFRSVUUgICAgICAgICAgICAgICAgICAgICAgfiAiTkEiCiAgICApKSAKZGF0YQpgYGAKCiMjIyBUYWxseSBsYWJlbHMKCgpgYGB7cn0KbGFiZWxfdGFsbHkgPC0KICBkYXRhICU+JQogIGdyb3VwX2J5KGxhYmVsKSAlPiUKICB0YWxseSgpICU+JQogIGFkZF9yb3cobGFiZWwgPSAiVE9UQUwiLCBuPXN1bSguJG4pKQpsYWJlbF90YWxseQpgYGAKClRoZXJlIGFyZSBvbmx5IGByIGxhYmVsX3RhbGx5ICU+JSBmaWx0ZXIobGFiZWw9PSJyLHIiKSAlPiUgcHVsbChuKWAgKHJlYWwsIHJlYWwpIGNvbGxpZGluZyBDVUdzIG91dCBvZiBgciBsYWJlbF90YWxseSAlPiUgZmlsdGVyKGxhYmVsPT0iVE9UQUwiKSAlPiUgcHVsbChuKWAgc28gYXNzdW1wdGlvbiBJIGlzIHZhbGlkYXRlZC4gVGhlIGNvbGxpc2lvbiByYXRlIGlzIGByIChsYWJlbF90YWxseSAlPiUgZmlsdGVyKGxhYmVsPT0icixyIikgJT4lIHB1bGwobikpLyhsYWJlbF90YWxseSAlPiUgZmlsdGVyKGxhYmVsPT0iVE9UQUwiKSAlPiUgcHVsbChuKSlgLgoKCiMjIyBGaWx0ZXIgb3V0IGNvbGxpZGluZyBDVUdzCgpgYGB7cn0KZGF0YSA8LQogIGRhdGEgJT4lIAogIGZpbHRlcihsYWJlbCE9InIsciIpCmBgYAoKIyMjIFNhdmUgZGF0YQoKYGBge3J9CndyaXRlX3RzdihkYXRhLCBmaWxlLnBhdGgodmFsaWRhdGlvbl9vdXRwdXRfZGlyLCJoaXNlcTQwMDBfaW5uZXJfam9pbmVkX3dpdGhfbGFiZWxzX2hnMzhfZW5zZW1ibDk1LnR4dCIpKQpgYGAKCgojIyBTdW1tYXJ5IHN0YXRpc3RpY3MKCm51bWJlciBvZiByZWFkcyAoaW4gbWlsbGlvbnMpCgpgYGB7cn0KZGF0YV9maiAlPiUgIAogIHVuZ3JvdXAoKSAlPiUKICBzdW1tYXJpemVfYXQodmFycyhtYXRjaGVzKCJecyIpKSwgbGlzdCh+IHN1bSguLzEwXjYsICBuYS5ybSA9IFRSVUUpKSkKYGBgCgoKYGBge3J9CmRhdGEgICU+JSAKICBzdW1tYXJpemVfYXQodmFycyhtYXRjaGVzKCJecyIpKSwgbGlzdCh+IHN1bSguLzEwXjYpKSkKYGBgCgoKbnVtYmVyIG9mIG1vbGVjdWxlcyAoaW4gbWlsbGlvbnMpCgpgYGB7cn0KZGF0YV9maiAlPiUgIAogIG11dGF0ZV9hdCh2YXJzKG1hdGNoZXMoIl5zIikpLCBsaXN0KH4gYXMuaW50ZWdlciguIT0wKSkpICAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc3VtbWFyaXplX2F0KHZhcnMobWF0Y2hlcygiXnMiKSksIGxpc3QofiBzdW0oLi8xMF42LCBuYS5ybSA9IFRSVUUpKSkKYGBgCgoKYGBge3J9CiBkYXRhICAlPiUgCiAgbXV0YXRlX2F0KHZhcnMobWF0Y2hlcygiXnMiKSksIGxpc3QofiBhcy5pbnRlZ2VyKC4hPTApKSkgICU+JQogIHVuZ3JvdXAoKSAgJT4lIAogIHN1bW1hcml6ZV9hdCh2YXJzKG1hdGNoZXMoIl5zIikpLCBsaXN0KH4gc3VtKC4vMTBeNiwgbmEucm0gPSBUUlVFKSkpCmBgYAoKCiMgRXhhbWluZSBjb25jb3JkYW5jZSBiZXR3ZWVuIHNhbXBsZXMKCgojIyBSZWFkIGNvdW50cyB3aXRoIHNhbWUgQ1VHIChjZWxsLXVtaS1nZW5lKSBsYWJlbAoKYGBge3IsIGZpZy5oZWlnaHQ9MTB9CnAxIDwtIGdncGxvdChkYXRhLCBhZXMoeCA9IHMxX25vbnBsZXhlZCwKICAgICAgICAgICAgICAgICAgeSA9IHMxX3BsZXhlZCkpIApwMSArIGdlb21faGV4KGJpbnMgPSA1MDApKwogIGdlb21fYWJsaW5lKHNsb3BlPS41LGludGVyY2VwdD0wKQpgYGAKCgoKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpwMiA8LSBnZ3Bsb3QoZGF0YSwgCiAgICAgICAgICAgIGFlcyh4ID0gczJfbm9ucGxleGVkLAogICAgICAgICAgICAgICAgICB5ID0gczJfcGxleGVkKSkgCnAyICsgCiAgZ2VvbV9oZXgoYmlucyA9IDUwMCkrCiAgZ2VvbV9hYmxpbmUoc2xvcGU9LjUsaW50ZXJjZXB0PTApCiAjICAgZ2VvbV9wb2ludCgpIApgYGAKCgoKCiMjIEdlbmUgZXhwcmVzc2lvbiByZWFkIGNvdW50cyB3aXRoIHNhbWUgY2VsbC1nZW5lIGxhYmVsCgoKYGBge3J9CmRhdGFfcmVhZHNfY2VsbF9nZW5lIDwtIAogICAgZGF0YSAlPiUgIAogIGdyb3VwX2J5KGNlbGwsIGdlbmUpICU+JQogIHN1bW1hcml6ZV9hdCh2YXJzKG1hdGNoZXMoIl5zIikpLAogICAgICAgICAgICAgICBsaXN0KH4gc3VtKC4pKSkKYGBgCgoKCgoKYGBge3IsIGZpZy5oZWlnaHQ9MTB9CiBnZ3Bsb3QoZGF0YV9yZWFkc19jZWxsX2dlbmUsCiAgICAgICAgICAgIGFlcyh4ID0gczFfbm9ucGxleGVkLAogICAgICAgICAgICAgICAgICB5ID0gczFfcGxleGVkKSkgICsgCiAgZ2VvbV9oZXgoYmlucyA9IDQwMCkgKwogIGdlb21fYWJsaW5lKHNsb3BlPTEsaW50ZXJjZXB0PTApKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgIHNjYWxlX3lfbG9nMTAoKSAKYGBgCgoKCgpgYGB7ciwgZmlnLmhlaWdodD0xMH0KZ2dwbG90KGRhdGFfcmVhZHNfY2VsbF9nZW5lLAogICAgICAgICAgICBhZXMoeCA9IHMyX25vbnBsZXhlZCwKICAgICAgICAgICAgICAgICAgeSA9IHMyX3BsZXhlZCkpICArIAogIGdlb21faGV4KGJpbnMgPSA0MDApICsKICAgIGdlb21fYWJsaW5lKHNsb3BlPTEsaW50ZXJjZXB0PTApKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgIHNjYWxlX3lfbG9nMTAoKSAKIApgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpnZ3Bsb3QoZGF0YV9yZWFkc19jZWxsX2dlbmUsCiAgICAgICAgICAgIGFlcyh4ID0gczFfbm9ucGxleGVkLAogICAgICAgICAgICAgICAgICB5ID0gczJfbm9ucGxleGVkKSkgICsgCiAgZ2VvbV9oZXgoYmlucyA9IDQwMCkgKwogIGdlb21fYWJsaW5lKHNsb3BlPTEsaW50ZXJjZXB0PTApKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgIHNjYWxlX3lfbG9nMTAoKSAKIApgYGAKCgpgYGB7ciwgZmlnLmhlaWdodD0xMH0KZ2dwbG90KGRhdGFfcmVhZHNfY2VsbF9nZW5lLAogICAgICAgICAgICBhZXMoeCA9IHMxX3BsZXhlZCwKICAgICAgICAgICAgICAgICAgeSA9IHMyX3BsZXhlZCkpICArIAogIGdlb21faGV4KGJpbnMgPSA0MDApICsKICBnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD0wKSsKICBzY2FsZV94X2xvZzEwKCkgKwogICBzY2FsZV95X2xvZzEwKCkgCiAKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0xMH0KZ2dwbG90KGRhdGFfcmVhZHNfY2VsbF9nZW5lLAogICAgICAgICAgICBhZXMoeCA9IHMxX3BsZXhlZCwKICAgICAgICAgICAgICAgICAgeSA9IHMyX25vbnBsZXhlZCkpICArIAogIGdlb21faGV4KGJpbnMgPSA0MDApICsKICBnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD0wKSsKICBzY2FsZV94X2xvZzEwKCkgKwogICBzY2FsZV95X2xvZzEwKCkgCiAKYGBgCgogIyMgR2VuZSBleHByZXNzaW9uIFVNSSBjb3VudHMgd2l0aCBzYW1lIGNlbGwtZ2VuZSBsYWJlbAogCgpgYGB7cn0KZGF0YV9tb2xlY19jZWxsX2dlbmUgPC0gCiAgICBkYXRhICU+JSAgIAogIG11dGF0ZShwdXJnZWQ9IGNhc2Vfd2hlbigKICAgICAgbGFiZWw9PSJyLDAifiAiMSwwIiwKICAgICAgbGFiZWw9PSJyLGYifiAiMSwwIiwKICAgICAgbGFiZWw9PSIwLHIifiAiMCwxIiwgICAgICAKICAgICAgbGFiZWw9PSJmLHIifiAiMCwxIiwKICAgICAgbGFiZWw9PSJmLDAifiAiMCwwIiwKICAgICAgbGFiZWw9PSIwLGYifiAiMCwwIgogICAgKSkgICU+JQogIHNlcGFyYXRlKHB1cmdlZCwgYygiczFfcHVyZ2VkIiwgInMyX3B1cmdlZCIpLCBzZXA9IiwiLCBjb252ZXJ0PVRSVUUpICU+JSAgCiAgZ3JvdXBfYnkoY2VsbCwgZ2VuZSkgJT4lCiAgc3VtbWFyaXplX2F0KHZhcnMobWF0Y2hlcygiXnMiKSksCiAgICAgICAgICAgICAgIGxpc3QofiBzdW0oLikpKQoKIGRhdGFfbW9sZWNfY2VsbF9nZW5lCmBgYAoKCgpgYGB7ciwgZmlnLmhlaWdodD0xMH0KIGdncGxvdChkYXRhX21vbGVjX2NlbGxfZ2VuZSwKICAgICAgICAgICAgYWVzKHggPSBzMV9ub25wbGV4ZWQsCiAgICAgICAgICAgICAgICAgIHkgPSBzMV9wbGV4ZWQpKSAgKyAKICBnZW9tX2hleChiaW5zID0gNDAwKSArCiAgZ2VvbV9hYmxpbmUoc2xvcGU9MSxpbnRlcmNlcHQ9MCkrCiAgc2NhbGVfeF9sb2cxMCgpICsKICAgc2NhbGVfeV9sb2cxMCgpIApgYGAKCgoKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpnZ3Bsb3QoZGF0YV9tb2xlY19jZWxsX2dlbmUsCiAgICAgICAgICAgIGFlcyh4ID0gczJfbm9ucGxleGVkLAogICAgICAgICAgICAgICAgICB5ID0gczJfcGxleGVkKSkgICsgCiAgZ2VvbV9oZXgoYmlucyA9IDQwMCkgKwogIGdlb21fYWJsaW5lKHNsb3BlPTEsaW50ZXJjZXB0PTApKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgIHNjYWxlX3lfbG9nMTAoKSAKIApgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpnZ3Bsb3QoZGF0YV9tb2xlY19jZWxsX2dlbmUsCiAgICAgICAgICAgIGFlcyh4ID0gczFfbm9ucGxleGVkLAogICAgICAgICAgICAgICAgICB5ID0gczJfbm9ucGxleGVkKSkgICsgCiAgZ2VvbV9oZXgoYmlucyA9IDQwMCkgKwogIGdlb21fYWJsaW5lKHNsb3BlPTEsaW50ZXJjZXB0PTApKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgIHNjYWxlX3lfbG9nMTAoKSAKIApgYGAKCiMjIyBBIHZpc3VhbCBkZW1vbnN0cmF0aW9uIG9mIHRoZSBlZmZlY3RzIG9mIGluZGV4IGhvcHBpbmcKCgpgYGB7cn0KIGRhdGFfbW9sZWNfY2VsbF9nZW5lIDwtIAogIGRhdGFfbW9sZWNfY2VsbF9nZW5lICU+JSAKICBtdXRhdGUoc291cmNlPSBjYXNlX3doZW4oczFfcHVyZ2VkPT0wIH4gInMyIiwKICAgICAgICAgICAgICAgICAgICAgICBzMl9wdXJnZWQ9PTAgfiAiczEiLAogICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiJzMXMyIikpCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9OH0KIHBfaG9wcGluZyA8LSAKICBnZ3Bsb3QoZGF0YV9tb2xlY19jZWxsX2dlbmUgKSAgKyAKICBnZW9tX3BvaW50KAogICAgICAgICAgICBhZXMoeCA9IHMxX3BsZXhlZCwKICAgICAgICAgICAgICAgICAgeSA9IHMyX3BsZXhlZCwKICAgICAgICAgICAgICAgIGNvbG9yPXNvdXJjZSksCiAgICAgICAgICAgIGFscGhhPTAuNCwgc2l6ZT0wLjQpICsKIyAgZ2VvbV9hYmxpbmUoc2xvcGU9MSxpbnRlcmNlcHQ9MCkrCiAgc2NhbGVfeF9sb2cxMCgpICsKICAgc2NhbGVfeV9sb2cxMCgpICsKICAgIGxhYnMoeD0iVU1JIGNvdW50IGJ5IGNlbGwgYW5kIGdlbmUgKFNhbXBsZSAxIE11bHRpcGxleGVkKSIsCiAgICAgICAgIHk9IlVNSSBjb3VudCBieSBjZWxsIGFuZCBnZW5lIChTYW1wbGUgMiBNdWx0aXBsZXhlZCkiKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArCiAgIGV4cGFuZF9saW1pdHMoeCA9IGMoMCwgNjUwMDAwKSwgeSA9IGMoMCwgMTEwMDAwKSkKICAKZ2dzYXZlKGZpbGUucGF0aChmaWd1cmVzX2RpciwgInNhbXBsZXNfbXVsdGlwbGV4ZWRfaG9wcGluZy5qcGVnIiksIHBfaG9wcGluZywgd2lkdGg9OSwgaGVpZ2h0PTYsIGRwaT0zMjApCiBwX2hvcHBpbmcKYGBgCgoKCgpgYGB7ciwgZmlnLmhlaWdodD04fQpwX3B1cmdlZCA8LQpnZ3Bsb3QoZGF0YV9tb2xlY19jZWxsX2dlbmUpICArIAogIGdlb21fcG9pbnQoIGFlcyh4ID0gczFfcHVyZ2VkLAogICAgICAgICAgICAgICAgICB5ID0gczJfcHVyZ2VkLAogICAgICAgICAgICAgICAgICBjb2xvcj1zb3VyY2UpLAogICAgICAgICAgICAgIHNpemU9MS4yKSArCiMgIGdlb21fYWJsaW5lKHNsb3BlPTEsaW50ZXJjZXB0PTApKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgIHNjYWxlX3lfbG9nMTAoKSArCiAgICBsYWJzKHg9IlVNSSBjb3VudCBieSBjZWxsIGFuZCBnZW5lIChTYW1wbGUgMSBNdWx0aXBsZXhlZCkiLAogICAgICAgICB5PSJVTUkgY291bnQgYnkgY2VsbCBhbmQgZ2VuZSAoU2FtcGxlIDIgTXVsdGlwbGV4ZWQpIikgKwogIHRoZW1lX2J3KCkgICArCiAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArCiAgIGV4cGFuZF9saW1pdHMoeCA9IGMoMCwgNjUwMDAwKSwgeSA9IGMoMCwgMTEwMDAwKSkKCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJzYW1wbGVzX211bHRpcGxleGVkX3B1cmdlZC5qcGVnIiksIHBfcHVyZ2VkLCB3aWR0aD05LCBoZWlnaHQ9NikKcF9wdXJnZWQKYGBgCgoKCgoKCmBgYHtyfQpkYXRhX21vbGVjX2NlbGwgPC0KICBkYXRhX21vbGVjX2NlbGxfZ2VuZSAlPiUKICBzZWxlY3QoLWdlbmUsIC1zb3VyY2UpICU+JQogIGdyb3VwX2J5KGNlbGwpICU+JQogIHN1bW1hcml6ZV9hbGwoc3VtKQogIApgYGAKCgpgYGB7cn0KZGF0YV9tb2xlY19jZWxsIDwtCiAgZGF0YV9tb2xlY19jZWxsICU+JQogIG11dGF0ZShzb3VyY2U9IGNhc2Vfd2hlbihzMV9wdXJnZWQ9PTAgfiAiczIiLAogICAgICAgICAgICAgICAgICAgICAgIHMyX3B1cmdlZD09MCB+ICJzMSIsCiAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+InMxczIiKSkKYGBgCgoKCmBgYHtyLCBmaWcuaGVpZ2h0PTh9CnBfcGhhbnRvbWNlbGxzIDwtIAogIGdncGxvdChkYXRhX21vbGVjX2NlbGwpICArIAogIGdlb21fcG9pbnQoCiAgICAgICAgICAgIGFlcyh4ID0gczFfcGxleGVkLAogICAgICAgICAgICAgICAgICB5ID0gczJfcGxleGVkLAogICAgICAgICAgICAgICAgY29sb3I9c291cmNlKSwKICAgICAgICAgICAgYWxwaGE9MC40LCBzaXplPTAuMykgKwojICBnZW9tX2FibGluZShzbG9wZT0xLGludGVyY2VwdD0wKSsKICBzY2FsZV94X2xvZzEwKCkgKwogICBzY2FsZV95X2xvZzEwKCkgKwogICAgbGFicyh4PSJVTUkgY291bnQgYnkgY2VsbCAoU2FtcGxlIDEgTXVsdGlwbGV4ZWQpIiwKICAgICAgICAgeT0iVU1JIGNvdW50IGJ5IGNlbGwgKFNhbXBsZSAyIE11bHRpcGxleGVkKSIpICsKICB0aGVtZV9idygpICAgKwogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siKSwKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICBleHBhbmRfbGltaXRzKHggPSBjKDAsIDEwMDAwMDApLCB5ID0gYygwLCAzMDAwMDApKQpnZ3NhdmUoZmlsZS5wYXRoKGZpZ3VyZXNfZGlyLCAic2FtcGxlc19tdWx0aXBsZXhlZF9waGFudG9tY2VsbHMuanBlZyIpLCBwX3BoYW50b21jZWxscywgd2lkdGg9OSwgaGVpZ2h0PTYsIGRwaT0zMjApCnBfcGhhbnRvbWNlbGxzCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9OH0KcF9jZWxscyA8LSAKICBnZ3Bsb3QoZGF0YV9tb2xlY19jZWxsKSAgKyAKICBnZW9tX3BvaW50KAogICAgICAgICAgICBhZXMoeCA9IHMxX3B1cmdlZCwKICAgICAgICAgICAgICAgICAgeSA9IHMyX3B1cmdlZCwKICAgICAgICAgICAgICAgIGNvbG9yPXNvdXJjZSksCiAgICAgICAgICAgIGFscGhhPTAuNSwgc2l6ZT0wLjYpICsKIyAgZ2VvbV9hYmxpbmUoc2xvcGU9MSxpbnRlcmNlcHQ9MCkrCiAgc2NhbGVfeF9sb2cxMCgpICsKICAgc2NhbGVfeV9sb2cxMCgpICsKICAgIGxhYnMoeD0iVU1JIGNvdW50IGJ5IGNlbGwgKFNhbXBsZSAxIE11bHRpcGxleGVkKSIsCiAgICAgICAgIHk9IlVNSSBjb3VudCBieSBjZWxsIChTYW1wbGUgMiBNdWx0aXBsZXhlZCkiKSArCiAgdGhlbWVfYncoKSAgICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICArCiAgIGV4cGFuZF9saW1pdHMoeCA9IGMoMCwgMTAwMDAwMCksIHkgPSBjKDAsIDMwMDAwMCkpCmdnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJzYW1wbGVzX211bHRpcGxleGVkX2NlbGxzLmpwZWciKSwgcF9jZWxscywgd2lkdGg9OSwgaGVpZ2h0PTYsIGRwaT0zMjApCnBfY2VsbHMKYGBgCgogCgogCiAKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgoK