1 Setup

library(tidyverse)
source("../6.paper_figures/viz_themes.R")

2 Load

path <- "https://raw.githubusercontent.com/broadinstitute/lincs-profiling-complementarity/master/6.paper_figures/data/significant_moas_by_threshold_both_assays.tsv.gz"

moa_performance <- read_tsv(path)
Rows: 1100 Columns: 9── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (3): moa, dose, moa_color_passing
dbl (3): Cell Painting, L1000, avg_replicate_count
lgl (3): pass_thresh_cellpainting, pass_thresh_l1000, pass_both
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
path <- "https://raw.githubusercontent.com/broadinstitute/lincs-profiling-complementarity/3f39cdd2e97d98c394e1031e338cab072d106a1a/6.paper_figures/results/moa_scores.tsv"

moa_scores <- read_tsv(path)
Rows: 2200 Columns: 8── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (3): moa, dose, assay
dbl (4): no_of_replicates, matching_score, p_value, neg_log_10_p_val
lgl (1): pass_thresh
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# To add analysis facet of all dose percent matching
updated_dose_order <- c(dose_order, "All*", "All")

# Load precision scores
precision_file <- file.path("..", "1.Data-exploration", "results", "moa_target_precision.tsv.gz")

precision_cols <- readr::cols(
  drug_impact = readr::col_character(),
  dose = readr::col_character(),
  avg_precision = readr::col_double(),
  impact_category = readr::col_character(),
  assay = readr::col_character()
)

# Load and process data for plotting
precision_df <-
  readr::read_tsv(precision_file, col_types = precision_cols) %>%
  filter(impact_category == "moa") %>%
  rename(moa = "drug_impact") %>%
  select(-impact_category) %>%
  mutate(assay = forcats::fct_recode(assay, "Cell Painting" = "cell_painting"))

# Separate the dose comparison and recode dose separately
same_dose_precision_df <- precision_df %>%
    dplyr::filter(dose_comparison == "same_dose")

same_dose_precision_df$dose <- as.numeric(paste(same_dose_precision_df$dose))
same_dose_precision_df$dose <- dplyr::recode_factor(same_dose_precision_df$dose, !!!dose_rename)

precision_df <- dplyr::bind_rows(
    same_dose_precision_df,
    precision_df %>%
        dplyr::filter(dose_comparison != "same_dose")
    ) %>%
    tidyr::drop_na()

precision_df$dose <- factor(precision_df$dose, levels = updated_dose_order)

3 Inspect

3.1 moa_scores

moa_scores %>% 
  skimr::skim()
── Data Summary ────────────────────────
                           Values    
Name                       Piped data
Number of rows             2200      
Number of columns          8         
_______________________              
Column type frequency:               
  character                3         
  logical                  1         
  numeric                  4         
________________________             
Group variables            None      
moa_scores %>% 
  group_by(pass_thresh, assay) %>%
  skimr::skim()
── Data Summary ────────────────────────
                           Values            
Name                       Piped data        
Number of rows             2200              
Number of columns          8                 
_______________________                      
Column type frequency:                       
  character                2                 
  numeric                  4                 
________________________                     
Group variables            pass_thresh, assay
moa_scores %>%
  ggplot(aes(no_of_replicates)) +
  geom_histogram(bins = 50) +
  theme_bw() + 
  scale_x_log10() +
  facet_grid(assay~dose)

3.2 precision_df

precision_df %>% 
  skimr::skim()
── Data Summary ────────────────────────
                           Values    
Name                       Piped data
Number of rows             2933      
Number of columns          5         
_______________________              
Column type frequency:               
  character                2         
  factor                   2         
  numeric                  1         
________________________             
Group variables            None      
precision_df %>%
  anti_join(moa_scores, by = c("moa" = "moa")) %>%
  arrange(moa)

moa_scores %>%
  anti_join(precision_df, by = c("moa" = "moa")) %>%
  arrange(moa)
moa_scores <- 
  moa_scores %>%
  inner_join(precision_df, by = c("moa", "dose", "assay")) %>%
  arrange(moa)

4 Model

model_fit <- function(data, output_variable, input_variable = "log(no_of_replicates)") {
  lm(formula = as.formula(paste0(
    output_variable, "~", input_variable
  )),
  data = data)
}

avg_precision depends on number of replicates

moa_scores %>%
  mutate(all_dose = dose %in% c("All", "All*")) %>%
  ggplot(aes(no_of_replicates, avg_precision)) +
  geom_bin2d() +
  geom_smooth(method = "lm", formula = "y~x") +
  theme_bw() + 
  facet_grid(assay~all_dose, labeller = labeller(.cols = label_both), scales = "free_x")

moa_scores %>%
  mutate(all_dose = dose %in% c("All", "All*")) %>%
  ggplot(aes(log(no_of_replicates), log(avg_precision))) +
  geom_bin2d() +
  geom_smooth(method = "lm", formula = "y~x") +
  theme_bw() + 
  facet_grid(assay~all_dose, labeller = labeller(.cols = label_both), scales = "free_x")

moa_summary <-
  moa_scores %>%
  mutate(all_dose = dose %in% c("All", "All*")) %>%
  group_nest(assay, all_dose) %>%
  mutate(model = map(data, model_fit, output_variable = "log(avg_precision)", input_variable = "log(no_of_replicates)"))  %>%
  mutate(model_summary = map(model, broom::tidy))

moa_summary %>%
  unnest(model_summary) %>%
  filter(term != "(Intercept)") %>%
  dplyr::select(assay, all_dose, term, estimate:p.value)
moa_summary <-
  moa_scores %>%
  mutate(all_dose = dose %in% c("All", "All*")) %>%
  group_nest(assay, all_dose) %>%
  mutate(model = map(data, model_fit, output_variable = "log(avg_precision)", input_variable = "no_of_replicates"))  %>%
  mutate(model_summary = map(model, broom::tidy))

moa_summary %>%
  unnest(model_summary) %>%
  filter(term != "(Intercept)") %>%
  dplyr::select(assay, all_dose, term, estimate:p.value)

median pairwise correlation (matching_score) does not depend on number of replicates

moa_scores %>%
  mutate(all_dose = dose %in% c("All", "All*")) %>%
  ggplot(aes(log(no_of_replicates), matching_score)) +
  geom_bin2d() +
  geom_smooth(method = "lm", formula = "y~x") +
  theme_bw() + 
  facet_grid(assay~all_dose, labeller = labeller(.cols = label_both))

moa_summary <-
  moa_scores %>%
  mutate(all_dose = dose %in% c("All", "All*")) %>%
  group_nest(assay, all_dose) %>%
  mutate(model = map(data, model_fit, output_variable = "matching_score"))  %>%
  mutate(model_summary = map(model, broom::tidy))

moa_summary %>%
  unnest(model_summary) %>%
  filter(term != "(Intercept)") %>%
  dplyr::select(assay, all_dose, term, estimate:p.value)

p-value of matching_score does depend on number of replicates

moa_scores %>%
  mutate(all_dose = dose %in% c("All", "All*")) %>%
  ggplot(aes(log(no_of_replicates), neg_log_10_p_val)) +
  geom_bin2d() +
  geom_smooth(method = "lm", formula = "y~x") +
  theme_bw() + 
  facet_grid(assay~all_dose, labeller = labeller(.cols = label_both))

moa_summary <-
  moa_scores %>%
  mutate(all_dose = dose %in% c("All", "All*")) %>%
  group_nest(assay, all_dose) %>%
  mutate(model = map(data, model_fit, output_variable = "neg_log_10_p_val"))  %>%
  mutate(model_summary = map(model, broom::tidy))

moa_summary %>%
  unnest(model_summary) %>%
  filter(term != "(Intercept)") %>%
  dplyr::select(assay, all_dose, term, estimate:p.value)
LS0tCnRpdGxlOiAiSW5zcGVjdCBwZXJjZW50IG1hdGNoaW5nIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIFNldHVwCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCnNvdXJjZSgiLi4vNi5wYXBlcl9maWd1cmVzL3Zpel90aGVtZXMuUiIpCmBgYAoKIyBMb2FkCgpgYGB7cn0KcGF0aCA8LSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2Jyb2FkaW5zdGl0dXRlL2xpbmNzLXByb2ZpbGluZy1jb21wbGVtZW50YXJpdHkvbWFzdGVyLzYucGFwZXJfZmlndXJlcy9kYXRhL3NpZ25pZmljYW50X21vYXNfYnlfdGhyZXNob2xkX2JvdGhfYXNzYXlzLnRzdi5neiIKCm1vYV9wZXJmb3JtYW5jZSA8LSByZWFkX3RzdihwYXRoKQpgYGAKCmBgYHtyfQpwYXRoIDwtICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYnJvYWRpbnN0aXR1dGUvbGluY3MtcHJvZmlsaW5nLWNvbXBsZW1lbnRhcml0eS8zZjM5Y2RkMmU5N2Q5OGMzOTRlMTAzMWUzMzhjYWIwNzJkMTA2YTFhLzYucGFwZXJfZmlndXJlcy9yZXN1bHRzL21vYV9zY29yZXMudHN2IgoKbW9hX3Njb3JlcyA8LSByZWFkX3RzdihwYXRoKQpgYGAKCmBgYHtyfQojIFRvIGFkZCBhbmFseXNpcyBmYWNldCBvZiBhbGwgZG9zZSBwZXJjZW50IG1hdGNoaW5nCnVwZGF0ZWRfZG9zZV9vcmRlciA8LSBjKGRvc2Vfb3JkZXIsICJBbGwqIiwgIkFsbCIpCgojIExvYWQgcHJlY2lzaW9uIHNjb3JlcwpwcmVjaXNpb25fZmlsZSA8LSBmaWxlLnBhdGgoIi4uIiwgIjEuRGF0YS1leHBsb3JhdGlvbiIsICJyZXN1bHRzIiwgIm1vYV90YXJnZXRfcHJlY2lzaW9uLnRzdi5neiIpCgpwcmVjaXNpb25fY29scyA8LSByZWFkcjo6Y29scygKICBkcnVnX2ltcGFjdCA9IHJlYWRyOjpjb2xfY2hhcmFjdGVyKCksCiAgZG9zZSA9IHJlYWRyOjpjb2xfY2hhcmFjdGVyKCksCiAgYXZnX3ByZWNpc2lvbiA9IHJlYWRyOjpjb2xfZG91YmxlKCksCiAgaW1wYWN0X2NhdGVnb3J5ID0gcmVhZHI6OmNvbF9jaGFyYWN0ZXIoKSwKICBhc3NheSA9IHJlYWRyOjpjb2xfY2hhcmFjdGVyKCkKKQoKIyBMb2FkIGFuZCBwcm9jZXNzIGRhdGEgZm9yIHBsb3R0aW5nCnByZWNpc2lvbl9kZiA8LQogIHJlYWRyOjpyZWFkX3RzdihwcmVjaXNpb25fZmlsZSwgY29sX3R5cGVzID0gcHJlY2lzaW9uX2NvbHMpICU+JQogIGZpbHRlcihpbXBhY3RfY2F0ZWdvcnkgPT0gIm1vYSIpICU+JQogIHJlbmFtZShtb2EgPSAiZHJ1Z19pbXBhY3QiKSAlPiUKICBzZWxlY3QoLWltcGFjdF9jYXRlZ29yeSkgJT4lCiAgbXV0YXRlKGFzc2F5ID0gZm9yY2F0czo6ZmN0X3JlY29kZShhc3NheSwgIkNlbGwgUGFpbnRpbmciID0gImNlbGxfcGFpbnRpbmciKSkKCiMgU2VwYXJhdGUgdGhlIGRvc2UgY29tcGFyaXNvbiBhbmQgcmVjb2RlIGRvc2Ugc2VwYXJhdGVseQpzYW1lX2Rvc2VfcHJlY2lzaW9uX2RmIDwtIHByZWNpc2lvbl9kZiAlPiUKICAgIGRwbHlyOjpmaWx0ZXIoZG9zZV9jb21wYXJpc29uID09ICJzYW1lX2Rvc2UiKQoKc2FtZV9kb3NlX3ByZWNpc2lvbl9kZiRkb3NlIDwtIGFzLm51bWVyaWMocGFzdGUoc2FtZV9kb3NlX3ByZWNpc2lvbl9kZiRkb3NlKSkKc2FtZV9kb3NlX3ByZWNpc2lvbl9kZiRkb3NlIDwtIGRwbHlyOjpyZWNvZGVfZmFjdG9yKHNhbWVfZG9zZV9wcmVjaXNpb25fZGYkZG9zZSwgISEhZG9zZV9yZW5hbWUpCgpwcmVjaXNpb25fZGYgPC0gZHBseXI6OmJpbmRfcm93cygKICAgIHNhbWVfZG9zZV9wcmVjaXNpb25fZGYsCiAgICBwcmVjaXNpb25fZGYgJT4lCiAgICAgICAgZHBseXI6OmZpbHRlcihkb3NlX2NvbXBhcmlzb24gIT0gInNhbWVfZG9zZSIpCiAgICApICU+JQogICAgdGlkeXI6OmRyb3BfbmEoKQoKcHJlY2lzaW9uX2RmJGRvc2UgPC0gZmFjdG9yKHByZWNpc2lvbl9kZiRkb3NlLCBsZXZlbHMgPSB1cGRhdGVkX2Rvc2Vfb3JkZXIpCmBgYAoKIyBJbnNwZWN0CgojIyBtb2Ffc2NvcmVzCgpgYGB7cn0KbW9hX3Njb3JlcyAlPiUgCiAgc2tpbXI6OnNraW0oKQpgYGAKCgpgYGB7cn0KbW9hX3Njb3JlcyAlPiUgCiAgZ3JvdXBfYnkocGFzc190aHJlc2gsIGFzc2F5KSAlPiUKICBza2ltcjo6c2tpbSgpCmBgYAoKCmBgYHtyfQptb2Ffc2NvcmVzICU+JQogIGdncGxvdChhZXMobm9fb2ZfcmVwbGljYXRlcykpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTApICsKICB0aGVtZV9idygpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsKICBmYWNldF9ncmlkKGFzc2F5fmRvc2UpCmBgYAojIyBwcmVjaXNpb25fZGYKCmBgYHtyfQpwcmVjaXNpb25fZGYgJT4lIAogIHNraW1yOjpza2ltKCkKYGBgCgpgYGB7ciByb3dzLnByaW50PTIwfQpwcmVjaXNpb25fZGYgJT4lCiAgYW50aV9qb2luKG1vYV9zY29yZXMsIGJ5ID0gYygibW9hIiA9ICJtb2EiKSkgJT4lCiAgYXJyYW5nZShtb2EpCgptb2Ffc2NvcmVzICU+JQogIGFudGlfam9pbihwcmVjaXNpb25fZGYsIGJ5ID0gYygibW9hIiA9ICJtb2EiKSkgJT4lCiAgYXJyYW5nZShtb2EpCmBgYApgYGB7cn0KbW9hX3Njb3JlcyA8LSAKICBtb2Ffc2NvcmVzICU+JQogIGlubmVyX2pvaW4ocHJlY2lzaW9uX2RmLCBieSA9IGMoIm1vYSIsICJkb3NlIiwgImFzc2F5IikpICU+JQogIGFycmFuZ2UobW9hKQpgYGAKCiMgTW9kZWwKCmBgYHtyIHJvd3MucHJpbnQ9MjB9Cm1vZGVsX2ZpdCA8LSBmdW5jdGlvbihkYXRhLCBvdXRwdXRfdmFyaWFibGUsIGlucHV0X3ZhcmlhYmxlID0gImxvZyhub19vZl9yZXBsaWNhdGVzKSIpIHsKICBsbShmb3JtdWxhID0gYXMuZm9ybXVsYShwYXN0ZTAoCiAgICBvdXRwdXRfdmFyaWFibGUsICJ+IiwgaW5wdXRfdmFyaWFibGUKICApKSwKICBkYXRhID0gZGF0YSkKfQpgYGAKCmBhdmdfcHJlY2lzaW9uYCBkZXBlbmRzIG9uIG51bWJlciBvZiByZXBsaWNhdGVzCgpgYGB7cn0KbW9hX3Njb3JlcyAlPiUKICBtdXRhdGUoYWxsX2Rvc2UgPSBkb3NlICVpbiUgYygiQWxsIiwgIkFsbCoiKSkgJT4lCiAgZ2dwbG90KGFlcyhub19vZl9yZXBsaWNhdGVzLCBhdmdfcHJlY2lzaW9uKSkgKwogIGdlb21fYmluMmQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9ICJ5fngiKSArCiAgdGhlbWVfYncoKSArIAogIGZhY2V0X2dyaWQoYXNzYXl+YWxsX2Rvc2UsIGxhYmVsbGVyID0gbGFiZWxsZXIoLmNvbHMgPSBsYWJlbF9ib3RoKSwgc2NhbGVzID0gImZyZWVfeCIpCmBgYAoKCmBgYHtyfQptb2Ffc2NvcmVzICU+JQogIG11dGF0ZShhbGxfZG9zZSA9IGRvc2UgJWluJSBjKCJBbGwiLCAiQWxsKiIpKSAlPiUKICBnZ3Bsb3QoYWVzKGxvZyhub19vZl9yZXBsaWNhdGVzKSwgbG9nKGF2Z19wcmVjaXNpb24pKSkgKwogIGdlb21fYmluMmQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9ICJ5fngiKSArCiAgdGhlbWVfYncoKSArIAogIGZhY2V0X2dyaWQoYXNzYXl+YWxsX2Rvc2UsIGxhYmVsbGVyID0gbGFiZWxsZXIoLmNvbHMgPSBsYWJlbF9ib3RoKSwgc2NhbGVzID0gImZyZWVfeCIpCmBgYAoKCmBgYHtyfQptb2Ffc3VtbWFyeSA8LQogIG1vYV9zY29yZXMgJT4lCiAgbXV0YXRlKGFsbF9kb3NlID0gZG9zZSAlaW4lIGMoIkFsbCIsICJBbGwqIikpICU+JQogIGdyb3VwX25lc3QoYXNzYXksIGFsbF9kb3NlKSAlPiUKICBtdXRhdGUobW9kZWwgPSBtYXAoZGF0YSwgbW9kZWxfZml0LCBvdXRwdXRfdmFyaWFibGUgPSAibG9nKGF2Z19wcmVjaXNpb24pIiwgaW5wdXRfdmFyaWFibGUgPSAibG9nKG5vX29mX3JlcGxpY2F0ZXMpIikpICAlPiUKICBtdXRhdGUobW9kZWxfc3VtbWFyeSA9IG1hcChtb2RlbCwgYnJvb206OnRpZHkpKQoKbW9hX3N1bW1hcnkgJT4lCiAgdW5uZXN0KG1vZGVsX3N1bW1hcnkpICU+JQogIGZpbHRlcih0ZXJtICE9ICIoSW50ZXJjZXB0KSIpICU+JQogIGRwbHlyOjpzZWxlY3QoYXNzYXksIGFsbF9kb3NlLCB0ZXJtLCBlc3RpbWF0ZTpwLnZhbHVlKQpgYGAKYGBge3J9Cm1vYV9zdW1tYXJ5IDwtCiAgbW9hX3Njb3JlcyAlPiUKICBtdXRhdGUoYWxsX2Rvc2UgPSBkb3NlICVpbiUgYygiQWxsIiwgIkFsbCoiKSkgJT4lCiAgZ3JvdXBfbmVzdChhc3NheSwgYWxsX2Rvc2UpICU+JQogIG11dGF0ZShtb2RlbCA9IG1hcChkYXRhLCBtb2RlbF9maXQsIG91dHB1dF92YXJpYWJsZSA9ICJsb2coYXZnX3ByZWNpc2lvbikiLCBpbnB1dF92YXJpYWJsZSA9ICJub19vZl9yZXBsaWNhdGVzIikpICAlPiUKICBtdXRhdGUobW9kZWxfc3VtbWFyeSA9IG1hcChtb2RlbCwgYnJvb206OnRpZHkpKQoKbW9hX3N1bW1hcnkgJT4lCiAgdW5uZXN0KG1vZGVsX3N1bW1hcnkpICU+JQogIGZpbHRlcih0ZXJtICE9ICIoSW50ZXJjZXB0KSIpICU+JQogIGRwbHlyOjpzZWxlY3QoYXNzYXksIGFsbF9kb3NlLCB0ZXJtLCBlc3RpbWF0ZTpwLnZhbHVlKQpgYGAKCm1lZGlhbiBwYWlyd2lzZSBjb3JyZWxhdGlvbiAoYG1hdGNoaW5nX3Njb3JlYCkgZG9lcyBub3QgZGVwZW5kIG9uIG51bWJlciBvZiByZXBsaWNhdGVzCgpgYGB7cn0KbW9hX3Njb3JlcyAlPiUKICBtdXRhdGUoYWxsX2Rvc2UgPSBkb3NlICVpbiUgYygiQWxsIiwgIkFsbCoiKSkgJT4lCiAgZ2dwbG90KGFlcyhsb2cobm9fb2ZfcmVwbGljYXRlcyksIG1hdGNoaW5nX3Njb3JlKSkgKwogIGdlb21fYmluMmQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9ICJ5fngiKSArCiAgdGhlbWVfYncoKSArIAogIGZhY2V0X2dyaWQoYXNzYXl+YWxsX2Rvc2UsIGxhYmVsbGVyID0gbGFiZWxsZXIoLmNvbHMgPSBsYWJlbF9ib3RoKSkKYGBgCgoKYGBge3J9Cm1vYV9zdW1tYXJ5IDwtCiAgbW9hX3Njb3JlcyAlPiUKICBtdXRhdGUoYWxsX2Rvc2UgPSBkb3NlICVpbiUgYygiQWxsIiwgIkFsbCoiKSkgJT4lCiAgZ3JvdXBfbmVzdChhc3NheSwgYWxsX2Rvc2UpICU+JQogIG11dGF0ZShtb2RlbCA9IG1hcChkYXRhLCBtb2RlbF9maXQsIG91dHB1dF92YXJpYWJsZSA9ICJtYXRjaGluZ19zY29yZSIpKSAgJT4lCiAgbXV0YXRlKG1vZGVsX3N1bW1hcnkgPSBtYXAobW9kZWwsIGJyb29tOjp0aWR5KSkKCm1vYV9zdW1tYXJ5ICU+JQogIHVubmVzdChtb2RlbF9zdW1tYXJ5KSAlPiUKICBmaWx0ZXIodGVybSAhPSAiKEludGVyY2VwdCkiKSAlPiUKICBkcGx5cjo6c2VsZWN0KGFzc2F5LCBhbGxfZG9zZSwgdGVybSwgZXN0aW1hdGU6cC52YWx1ZSkKYGBgCnAtdmFsdWUgb2YgYG1hdGNoaW5nX3Njb3JlYCBkb2VzIGRlcGVuZCBvbiBudW1iZXIgb2YgcmVwbGljYXRlcwoKYGBge3J9Cm1vYV9zY29yZXMgJT4lCiAgbXV0YXRlKGFsbF9kb3NlID0gZG9zZSAlaW4lIGMoIkFsbCIsICJBbGwqIikpICU+JQogIGdncGxvdChhZXMobG9nKG5vX29mX3JlcGxpY2F0ZXMpLCBuZWdfbG9nXzEwX3BfdmFsKSkgKwogIGdlb21fYmluMmQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9ICJ5fngiKSArCiAgdGhlbWVfYncoKSArIAogIGZhY2V0X2dyaWQoYXNzYXl+YWxsX2Rvc2UsIGxhYmVsbGVyID0gbGFiZWxsZXIoLmNvbHMgPSBsYWJlbF9ib3RoKSkKYGBgCgoKYGBge3J9Cm1vYV9zdW1tYXJ5IDwtCiAgbW9hX3Njb3JlcyAlPiUKICBtdXRhdGUoYWxsX2Rvc2UgPSBkb3NlICVpbiUgYygiQWxsIiwgIkFsbCoiKSkgJT4lCiAgZ3JvdXBfbmVzdChhc3NheSwgYWxsX2Rvc2UpICU+JQogIG11dGF0ZShtb2RlbCA9IG1hcChkYXRhLCBtb2RlbF9maXQsIG91dHB1dF92YXJpYWJsZSA9ICJuZWdfbG9nXzEwX3BfdmFsIikpICAlPiUKICBtdXRhdGUobW9kZWxfc3VtbWFyeSA9IG1hcChtb2RlbCwgYnJvb206OnRpZHkpKQoKbW9hX3N1bW1hcnkgJT4lCiAgdW5uZXN0KG1vZGVsX3N1bW1hcnkpICU+JQogIGZpbHRlcih0ZXJtICE9ICIoSW50ZXJjZXB0KSIpICU+JQogIGRwbHlyOjpzZWxlY3QoYXNzYXksIGFsbF9kb3NlLCB0ZXJtLCBlc3RpbWF0ZTpwLnZhbHVlKQpgYGA=