output: 
  html_notebook:
    toc: true
    toc_float: true
    toc_depth: 3
    number_sections: true
    theme: lumen
output: github_document

1 Boilerplate

If running interactively in RStudio,

  • change output in the header of this markdown to html_notebook and
  • change to eval=TRUE below

When knitting for pushing to GitHub,

  • change output in the header of this markdown to github_document and
  • change to eval=FALSE below
show_table <- knitr::kable
knitr::opts_chunk$set(fig.width = 6, fig.asp = 2/3)

2 Libraries

library(magrittr)
library(tidyverse)
library(arrow)
source("../../R/cytominer-eval.R")

3 Load data

# read these features
# https://github.com/broadinstitute/grit-benchmark/blob/31d9812e0c267d3535c85a72ef6bd0104d1a2130/1.calculate-metrics/cell-health/0.calculate-grit.ipynb
# (See chunk 4)

profiles <- read_csv("data/cell_health_merged_feature_select.csv.gz")

metadata <- get_annotation(profiles)
if (file.exists("data/sim.parquet")) {
  message("Loading existing similarity file...")
  sim_df <- arrow::read_parquet("data/sim.parquet")

} else {
  sim_df <- sim_calculate(profiles)
  sim_df %>% arrow::write_parquet("data/sim.parquet")
}
Loading existing similarity file...

4 Compute similarity sets


# ---- 0. Filter out some rows ----

drop_group <-
  data.frame(Metadata_gene_name = "EMPTY")

# ---- 1. Similarity to reference ----

# Fetch similarities between
# a. all rows (except, optionally those containing `reference`)
# and
# b. all rows containing `reference`
# Do so only for those (a, b) pairs that
# - have *same* values in *all* columns of `all_same_cols_ref`
    
reference <-
  data.frame(Metadata_gene_name = c("Chr2", "Luc", "LacZ"))

# all_same_cols_ref <-
#   c("Metadata_cell_line")

all_same_cols_ref <-
  c("Metadata_cell_line",
    "Metadata_Plate")

# ---- 2. Similarity to replicates (no references) ----

# Fetch similarities between
# a. all rows except `reference` rows
# and
# b. all rows except `reference` rows (i.e. to each other)
#
# Do so for only those (a, b) pairs that
# - have *same* values in *all* columns of `all_same_cols_rep
#
# Keep, both, (a, b) and (b, a)

all_same_cols_rep <-
  c("Metadata_cell_line",
    "Metadata_gene_name",
    "Metadata_pert_name")

# ---- 3. Similarity to replicates (only references) ----

# Fetch similarities between
# a. all rows containing `reference`
# to
# b. all rows containing `reference` (i.e. to each other)
#
# Do so for only those (a, b) pairs that
# - have *same* values in *all* columns of `all_same_cols_rep_ref`.
#
# Keep, both, (a, b) and (b, a)

all_same_cols_rep_ref <-
  c(
    "Metadata_cell_line",
    "Metadata_gene_name",
    "Metadata_pert_name",
    "Metadata_Plate"
  )

# all_same_cols_rep_ref <-
#   c(
#     "Metadata_cell_line",
#     "Metadata_gene_name",
#     "Metadata_pert_name"
#   )
# ---- 4. Similarity to non-replicates ----

# Fetch similarities between
# a. all rows (except, optionally, `reference` rows)
# to
# b. all rows except `reference` rows
#
# Do so for only those (a, b) pairs that
# - have *same* values in *all* columns of `all_same_cols_non_rep`
# - have *different* values in *all* columns `all_different_cols_non_rep`
# - have *different* values in *at least one* column of `any_different_cols_non_rep`
#
# Keep, both, (a, b) and (b, a)

any_different_cols_non_rep <-
  c("Metadata_cell_line",
    "Metadata_gene_name",
    "Metadata_pert_name")

all_same_cols_non_rep <-
  c("Metadata_cell_line", 
    "Metadata_Plate")

all_different_cols_non_rep <-
  c("Metadata_gene_name")

# ---- 5. Similarity to group ----

# Fetch similarities between
# a. all rows (except, optionally, `reference` rows)
# and
# b. all rows (except, optionally, `reference` rows)
#
# Do so for only those (a, b) pairs that
# - have *same* values in *all* columns of `all_same_cols_group`
# - have *different* values in *at least one* column of `any_different_cols_group`
#
# Keep, both, (a, b) and (b, a)

all_same_cols_group <-
  c("Metadata_cell_line",
    "Metadata_gene_name")

any_different_cols_group <-
  c("Metadata_cell_line",
    "Metadata_gene_name",
    "Metadata_pert_name")

# ---- 6. Combine 1-5 and annotate the similarity matrix ----

annotation_cols <-
  c("Metadata_cell_line",
    "Metadata_gene_name",
    "Metadata_pert_name")

munged_sim <-
  sim_munge(
    sim_df,
    metadata,
    reference,
    all_same_cols_rep = all_same_cols_rep,
    all_same_cols_rep_ref = all_same_cols_rep_ref,
    all_same_cols_ref = all_same_cols_ref,
    any_different_cols_non_rep = any_different_cols_non_rep,
    all_same_cols_non_rep = all_same_cols_non_rep,
    all_different_cols_non_rep = all_different_cols_non_rep,
    any_different_cols_group = any_different_cols_group,
    all_same_cols_group = all_same_cols_group,
    annotation_cols = annotation_cols,
    drop_group = drop_group
  )

5 Compute metrics

norm_non_rep_metrics <- 
  sim_metrics(munged_sim, "non_rep", calculate_grouped = TRUE)

norm_ref_metrics <- 
  sim_metrics(munged_sim, "ref", calculate_grouped = TRUE)

per_row_metrics <-
  inner_join(
    norm_non_rep_metrics[["per_row"]],
    norm_ref_metrics[["per_row"]],
    by = c("id1", all_same_cols_rep, "sim_mean_agg")
  )

per_set_metrics <-
  inner_join(
    norm_non_rep_metrics[["per_set"]],
    norm_ref_metrics[["per_set"]],
    by = c(all_same_cols_rep, "sim_mean_agg_mean_agg", "sim_mean_agg_median_agg")
  )

per_set_group_metrics <-
  inner_join(
    norm_non_rep_metrics[["per_set_group"]],
    norm_ref_metrics[["per_set_group"]],
    by = c(all_same_cols_rep, "sim_mean_agg", "sim_median_agg")
  )
per_set_all_metrics <-
  per_set_group_metrics %>%
  rename_with( ~ paste0(., "_group"), starts_with("sim")) %>%
  inner_join(
    per_set_metrics %>%
      rename_with( ~ paste0(., "_indiv"), starts_with("sim")),
    by = c(all_same_cols_rep)
  ) 

grit <-
  per_set_all_metrics %>%
  mutate(
    grit_group = sim_scaled_mean_agg_ref_group,
    grit_indiv = sim_scaled_mean_agg_ref_mean_agg_indiv
  ) %>%
  select(all_of(all_same_cols_rep), grit_group, grit_indiv)

6 Plot distributions

target_guide <- data.frame(
  Metadata_cell_line = "A549",
  Metadata_gene_name = "ITGAV",
  Metadata_pert_name = "ITGAV-1")

sister_guide <- data.frame(
  Metadata_cell_line = "A549",
  Metadata_gene_name = "ITGAV",
  Metadata_pert_name = "ITGAV-2")

cutting_controls <- data.frame(
  Metadata_cell_line = c("A549", "A549", "A549"),
  Metadata_gene_name = c("Chr2", "Luc", "LacZ")
)

metadata <- 
  profiles %>%
  select(matches("Metadata")) %>% 
  select(-Metadata_WellCol, -Metadata_WellRow)

features <- function(df) t(as.matrix(df %>% select(-matches("^Metadata_"))))

plate <- function(df) df$Metadata_Plate

p_target_guide <- profiles %>% inner_join(target_guide)
Joining, by = c("Metadata_cell_line", "Metadata_gene_name", "Metadata_pert_name")
p_sister_guide <- profiles %>% inner_join(sister_guide)
Joining, by = c("Metadata_cell_line", "Metadata_gene_name", "Metadata_pert_name")
p_cutting_controls <- profiles %>% inner_join(cutting_controls)
Joining, by = c("Metadata_cell_line", "Metadata_gene_name")
p_target_guide %>% group_by(across(all_of(unique(c(all_same_cols_rep, all_same_cols_ref))))) %>% tally()
p_sister_guide %>% group_by(across(all_of(unique(c(all_same_cols_rep, all_same_cols_ref))))) %>% tally()
p_cutting_controls %>% group_by(across(all_of(all_same_cols_ref))) %>% tally()
all_sim_counts <-
  munged_sim %>%
  group_by(across(all_of(c("id1", all_same_cols_rep, "type")))) %>%
  tally() %>%
  arrange(across(all_of(all_same_cols_rep))) %>%
  pivot_wider(names_from = "type",
              names_prefix = "n_",
              values_from = n,
              values_fill = 0)
all_sim_counts %>%
  inner_join(target_guide)
Joining, by = c("Metadata_cell_line", "Metadata_gene_name", "Metadata_pert_name")
target_distributions <-
  munged_sim %>%
  inner_join(target_guide)
Joining, by = c("Metadata_cell_line", "Metadata_gene_name", "Metadata_pert_name")
target_distributions %>%
  group_by(type) %>%
  tally()
target_distributions %>%
  group_by(type) %>%
  tally() %>%
  ggplot(aes(type, n)) + geom_col()

grit %>%
  inner_join(target_guide, by = colnames(target_guide))

target_distributions %>%
  filter(type %in% c("ref", "rep_group", "non_rep")) %>%
  ggplot(aes(sim)) + geom_histogram(binwidth = .02) + facet_wrap(~type, ncol = 1)

target_distributions %>%
  filter(type %in% c("ref", "rep", "non_rep")) %>%
  ggplot(aes(sim)) + geom_histogram(binwidth = .02) + facet_wrap(~type, ncol = 1)

target_guide <- data.frame(
  Metadata_cell_line = "A549",
  Metadata_gene_name = "CCND1",
  Metadata_pert_name = "CCND1-1")

munged_sim %>%
  inner_join(target_guide, by = colnames(target_guide)) %>%
  ggplot(aes(sim)) + geom_histogram(binwidth = .02) + facet_wrap(~type, ncol = 1)  


grit %>%
  inner_join(target_guide, by = colnames(target_guide)) 
target_guide <- data.frame(
  Metadata_cell_line = "A549",
  Metadata_gene_name = "CCND1",
  Metadata_pert_name = "CCND1-2")

munged_sim %>%
  inner_join(target_guide, by = colnames(target_guide)) %>%
  ggplot(aes(sim)) + geom_histogram(binwidth = .02) + facet_wrap(~type, ncol = 1)  


grit %>%
  inner_join(target_guide, by = colnames(target_guide))
target_guide <- data.frame(
  Metadata_cell_line = "A549",
  Metadata_gene_name = "STAT3",
  Metadata_pert_name = "STAT3-1")

munged_sim %>%
  inner_join(target_guide, by = colnames(target_guide)) %>%
  ggplot(aes(sim)) + geom_histogram(binwidth = .02) + facet_wrap(~type, ncol = 1)  


grit %>%
  inner_join(target_guide, by = colnames(target_guide))
target_guide <- data.frame(
  Metadata_cell_line = "A549",
  Metadata_gene_name = "MYC",
  Metadata_pert_name = "MYC-1")

munged_sim %>%
  inner_join(target_guide, by = colnames(target_guide)) %>%
  ggplot(aes(sim)) + geom_histogram(binwidth = .02) + facet_wrap(~type, ncol = 1)  


grit %>%
  inner_join(target_guide, by = colnames(target_guide))
LS0tCnRpdGxlOiAiRXhwbGFpbiBHcml0IgphdXRob3I6ICJTaGFudGFudSBTaW5naCIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0aGVtZTogbHVtZW4KLS0tCgpgYGAKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0aGVtZTogbHVtZW4KYGBgCgpgYGAKb3V0cHV0OiBnaXRodWJfZG9jdW1lbnQKYGBgCgojIEJvaWxlcnBsYXRlIAoKYGBge3IgZWNobz1GQUxTRX0Kc2hvd190YWJsZSA8LSBwcmludApgYGAKCklmIHJ1bm5pbmcgaW50ZXJhY3RpdmVseSBpbiBSU3R1ZGlvLCAKCi0gY2hhbmdlIGBvdXRwdXRgIGluIHRoZSBoZWFkZXIgb2YgdGhpcyBtYXJrZG93biB0byBgaHRtbF9ub3RlYm9va2AgYW5kCi0gY2hhbmdlIHRvIGBldmFsPVRSVUVgIGJlbG93CgpXaGVuIGtuaXR0aW5nIGZvciBwdXNoaW5nIHRvIEdpdEh1YiwKCi0gY2hhbmdlIGBvdXRwdXRgIGluIHRoZSBoZWFkZXIgb2YgdGhpcyBtYXJrZG93biB0byBgZ2l0aHViX2RvY3VtZW50YCBhbmQKLSBjaGFuZ2UgdG8gYGV2YWw9RkFMU0VgIGJlbG93CgpgYGB7ciBldmFsPUZBTFNFfQpzaG93X3RhYmxlIDwtIGtuaXRyOjprYWJsZQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gNiwgZmlnLmFzcCA9IDIvMykKYGBgCgojIExpYnJhcmllcwoKYGBge3IgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShtYWdyaXR0cikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoYXJyb3cpCmBgYAoKCmBgYHtyfQpzb3VyY2UoIi4uLy4uL1IvY3l0b21pbmVyLWV2YWwuUiIpCmBgYAoKIyBMb2FkIGRhdGEKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CiMgcmVhZCB0aGVzZSBmZWF0dXJlcwojIGh0dHBzOi8vZ2l0aHViLmNvbS9icm9hZGluc3RpdHV0ZS9ncml0LWJlbmNobWFyay9ibG9iLzMxZDk4MTJlMGMyNjdkMzUzNWM4NWE3MmVmNmJkMDEwNGQxYTIxMzAvMS5jYWxjdWxhdGUtbWV0cmljcy9jZWxsLWhlYWx0aC8wLmNhbGN1bGF0ZS1ncml0LmlweW5iCiMgKFNlZSBjaHVuayA0KQoKcHJvZmlsZXMgPC0gcmVhZF9jc3YoImRhdGEvY2VsbF9oZWFsdGhfbWVyZ2VkX2ZlYXR1cmVfc2VsZWN0LmNzdi5neiIpCgptZXRhZGF0YSA8LSBnZXRfYW5ub3RhdGlvbihwcm9maWxlcykKYGBgCgoKYGBge3J9CmlmIChmaWxlLmV4aXN0cygiZGF0YS9zaW0ucGFycXVldCIpKSB7CiAgbWVzc2FnZSgiTG9hZGluZyBleGlzdGluZyBzaW1pbGFyaXR5IGZpbGUuLi4iKQogIHNpbV9kZiA8LSBhcnJvdzo6cmVhZF9wYXJxdWV0KCJkYXRhL3NpbS5wYXJxdWV0IikKCn0gZWxzZSB7CiAgc2ltX2RmIDwtIHNpbV9jYWxjdWxhdGUocHJvZmlsZXMpCiAgc2ltX2RmICU+JSBhcnJvdzo6d3JpdGVfcGFycXVldCgiZGF0YS9zaW0ucGFycXVldCIpCn0KYGBgCgojIENvbXB1dGUgc2ltaWxhcml0eSBzZXRzCgpgYGB7cn0KCiMgLS0tLSAwLiBGaWx0ZXIgb3V0IHNvbWUgcm93cyAtLS0tCgpkcm9wX2dyb3VwIDwtCiAgZGF0YS5mcmFtZShNZXRhZGF0YV9nZW5lX25hbWUgPSAiRU1QVFkiKQoKIyAtLS0tIDEuIFNpbWlsYXJpdHkgdG8gcmVmZXJlbmNlIC0tLS0KCiMgRmV0Y2ggc2ltaWxhcml0aWVzIGJldHdlZW4KIyBhLiBhbGwgcm93cyAoZXhjZXB0LCBvcHRpb25hbGx5IHRob3NlIGNvbnRhaW5pbmcgYHJlZmVyZW5jZWApCiMgYW5kCiMgYi4gYWxsIHJvd3MgY29udGFpbmluZyBgcmVmZXJlbmNlYAojIERvIHNvIG9ubHkgZm9yIHRob3NlIChhLCBiKSBwYWlycyB0aGF0CiMgLSBoYXZlICpzYW1lKiB2YWx1ZXMgaW4gKmFsbCogY29sdW1ucyBvZiBgYWxsX3NhbWVfY29sc19yZWZgCiAgICAKcmVmZXJlbmNlIDwtCiAgZGF0YS5mcmFtZShNZXRhZGF0YV9nZW5lX25hbWUgPSBjKCJDaHIyIiwgIkx1YyIsICJMYWNaIikpCgojIGFsbF9zYW1lX2NvbHNfcmVmIDwtCiMgICBjKCJNZXRhZGF0YV9jZWxsX2xpbmUiKQoKYWxsX3NhbWVfY29sc19yZWYgPC0KICBjKCJNZXRhZGF0YV9jZWxsX2xpbmUiLAogICAgIk1ldGFkYXRhX1BsYXRlIikKCiMgLS0tLSAyLiBTaW1pbGFyaXR5IHRvIHJlcGxpY2F0ZXMgKG5vIHJlZmVyZW5jZXMpIC0tLS0KCiMgRmV0Y2ggc2ltaWxhcml0aWVzIGJldHdlZW4KIyBhLiBhbGwgcm93cyBleGNlcHQgYHJlZmVyZW5jZWAgcm93cwojIGFuZAojIGIuIGFsbCByb3dzIGV4Y2VwdCBgcmVmZXJlbmNlYCByb3dzIChpLmUuIHRvIGVhY2ggb3RoZXIpCiMKIyBEbyBzbyBmb3Igb25seSB0aG9zZSAoYSwgYikgcGFpcnMgdGhhdAojIC0gaGF2ZSAqc2FtZSogdmFsdWVzIGluICphbGwqIGNvbHVtbnMgb2YgYGFsbF9zYW1lX2NvbHNfcmVwCiMKIyBLZWVwLCBib3RoLCAoYSwgYikgYW5kIChiLCBhKQoKYWxsX3NhbWVfY29sc19yZXAgPC0KICBjKCJNZXRhZGF0YV9jZWxsX2xpbmUiLAogICAgIk1ldGFkYXRhX2dlbmVfbmFtZSIsCiAgICAiTWV0YWRhdGFfcGVydF9uYW1lIikKCiMgLS0tLSAzLiBTaW1pbGFyaXR5IHRvIHJlcGxpY2F0ZXMgKG9ubHkgcmVmZXJlbmNlcykgLS0tLQoKIyBGZXRjaCBzaW1pbGFyaXRpZXMgYmV0d2VlbgojIGEuIGFsbCByb3dzIGNvbnRhaW5pbmcgYHJlZmVyZW5jZWAKIyB0bwojIGIuIGFsbCByb3dzIGNvbnRhaW5pbmcgYHJlZmVyZW5jZWAgKGkuZS4gdG8gZWFjaCBvdGhlcikKIwojIERvIHNvIGZvciBvbmx5IHRob3NlIChhLCBiKSBwYWlycyB0aGF0CiMgLSBoYXZlICpzYW1lKiB2YWx1ZXMgaW4gKmFsbCogY29sdW1ucyBvZiBgYWxsX3NhbWVfY29sc19yZXBfcmVmYC4KIwojIEtlZXAsIGJvdGgsIChhLCBiKSBhbmQgKGIsIGEpCgphbGxfc2FtZV9jb2xzX3JlcF9yZWYgPC0KICBjKAogICAgIk1ldGFkYXRhX2NlbGxfbGluZSIsCiAgICAiTWV0YWRhdGFfZ2VuZV9uYW1lIiwKICAgICJNZXRhZGF0YV9wZXJ0X25hbWUiLAogICAgIk1ldGFkYXRhX1BsYXRlIgogICkKCiMgYWxsX3NhbWVfY29sc19yZXBfcmVmIDwtCiMgICBjKAojICAgICAiTWV0YWRhdGFfY2VsbF9saW5lIiwKIyAgICAgIk1ldGFkYXRhX2dlbmVfbmFtZSIsCiMgICAgICJNZXRhZGF0YV9wZXJ0X25hbWUiCiMgICApCiMgLS0tLSA0LiBTaW1pbGFyaXR5IHRvIG5vbi1yZXBsaWNhdGVzIC0tLS0KCiMgRmV0Y2ggc2ltaWxhcml0aWVzIGJldHdlZW4KIyBhLiBhbGwgcm93cyAoZXhjZXB0LCBvcHRpb25hbGx5LCBgcmVmZXJlbmNlYCByb3dzKQojIHRvCiMgYi4gYWxsIHJvd3MgZXhjZXB0IGByZWZlcmVuY2VgIHJvd3MKIwojIERvIHNvIGZvciBvbmx5IHRob3NlIChhLCBiKSBwYWlycyB0aGF0CiMgLSBoYXZlICpzYW1lKiB2YWx1ZXMgaW4gKmFsbCogY29sdW1ucyBvZiBgYWxsX3NhbWVfY29sc19ub25fcmVwYAojIC0gaGF2ZSAqZGlmZmVyZW50KiB2YWx1ZXMgaW4gKmFsbCogY29sdW1ucyBgYWxsX2RpZmZlcmVudF9jb2xzX25vbl9yZXBgCiMgLSBoYXZlICpkaWZmZXJlbnQqIHZhbHVlcyBpbiAqYXQgbGVhc3Qgb25lKiBjb2x1bW4gb2YgYGFueV9kaWZmZXJlbnRfY29sc19ub25fcmVwYAojCiMgS2VlcCwgYm90aCwgKGEsIGIpIGFuZCAoYiwgYSkKCmFueV9kaWZmZXJlbnRfY29sc19ub25fcmVwIDwtCiAgYygiTWV0YWRhdGFfY2VsbF9saW5lIiwKICAgICJNZXRhZGF0YV9nZW5lX25hbWUiLAogICAgIk1ldGFkYXRhX3BlcnRfbmFtZSIpCgphbGxfc2FtZV9jb2xzX25vbl9yZXAgPC0KICBjKCJNZXRhZGF0YV9jZWxsX2xpbmUiLCAKICAgICJNZXRhZGF0YV9QbGF0ZSIpCgphbGxfZGlmZmVyZW50X2NvbHNfbm9uX3JlcCA8LQogIGMoIk1ldGFkYXRhX2dlbmVfbmFtZSIpCgojIC0tLS0gNS4gU2ltaWxhcml0eSB0byBncm91cCAtLS0tCgojIEZldGNoIHNpbWlsYXJpdGllcyBiZXR3ZWVuCiMgYS4gYWxsIHJvd3MgKGV4Y2VwdCwgb3B0aW9uYWxseSwgYHJlZmVyZW5jZWAgcm93cykKIyBhbmQKIyBiLiBhbGwgcm93cyAoZXhjZXB0LCBvcHRpb25hbGx5LCBgcmVmZXJlbmNlYCByb3dzKQojCiMgRG8gc28gZm9yIG9ubHkgdGhvc2UgKGEsIGIpIHBhaXJzIHRoYXQKIyAtIGhhdmUgKnNhbWUqIHZhbHVlcyBpbiAqYWxsKiBjb2x1bW5zIG9mIGBhbGxfc2FtZV9jb2xzX2dyb3VwYAojIC0gaGF2ZSAqZGlmZmVyZW50KiB2YWx1ZXMgaW4gKmF0IGxlYXN0IG9uZSogY29sdW1uIG9mIGBhbnlfZGlmZmVyZW50X2NvbHNfZ3JvdXBgCiMKIyBLZWVwLCBib3RoLCAoYSwgYikgYW5kIChiLCBhKQoKYWxsX3NhbWVfY29sc19ncm91cCA8LQogIGMoIk1ldGFkYXRhX2NlbGxfbGluZSIsCiAgICAiTWV0YWRhdGFfZ2VuZV9uYW1lIikKCmFueV9kaWZmZXJlbnRfY29sc19ncm91cCA8LQogIGMoIk1ldGFkYXRhX2NlbGxfbGluZSIsCiAgICAiTWV0YWRhdGFfZ2VuZV9uYW1lIiwKICAgICJNZXRhZGF0YV9wZXJ0X25hbWUiKQoKIyAtLS0tIDYuIENvbWJpbmUgMS01IGFuZCBhbm5vdGF0ZSB0aGUgc2ltaWxhcml0eSBtYXRyaXggLS0tLQoKYW5ub3RhdGlvbl9jb2xzIDwtCiAgYygiTWV0YWRhdGFfY2VsbF9saW5lIiwKICAgICJNZXRhZGF0YV9nZW5lX25hbWUiLAogICAgIk1ldGFkYXRhX3BlcnRfbmFtZSIpCgptdW5nZWRfc2ltIDwtCiAgc2ltX211bmdlKAogICAgc2ltX2RmLAogICAgbWV0YWRhdGEsCiAgICByZWZlcmVuY2UsCiAgICBhbGxfc2FtZV9jb2xzX3JlcCA9IGFsbF9zYW1lX2NvbHNfcmVwLAogICAgYWxsX3NhbWVfY29sc19yZXBfcmVmID0gYWxsX3NhbWVfY29sc19yZXBfcmVmLAogICAgYWxsX3NhbWVfY29sc19yZWYgPSBhbGxfc2FtZV9jb2xzX3JlZiwKICAgIGFueV9kaWZmZXJlbnRfY29sc19ub25fcmVwID0gYW55X2RpZmZlcmVudF9jb2xzX25vbl9yZXAsCiAgICBhbGxfc2FtZV9jb2xzX25vbl9yZXAgPSBhbGxfc2FtZV9jb2xzX25vbl9yZXAsCiAgICBhbGxfZGlmZmVyZW50X2NvbHNfbm9uX3JlcCA9IGFsbF9kaWZmZXJlbnRfY29sc19ub25fcmVwLAogICAgYW55X2RpZmZlcmVudF9jb2xzX2dyb3VwID0gYW55X2RpZmZlcmVudF9jb2xzX2dyb3VwLAogICAgYWxsX3NhbWVfY29sc19ncm91cCA9IGFsbF9zYW1lX2NvbHNfZ3JvdXAsCiAgICBhbm5vdGF0aW9uX2NvbHMgPSBhbm5vdGF0aW9uX2NvbHMsCiAgICBkcm9wX2dyb3VwID0gZHJvcF9ncm91cAogICkKCmBgYAoKIyBDb21wdXRlIG1ldHJpY3MKCmBgYHtyfQpub3JtX25vbl9yZXBfbWV0cmljcyA8LSAKICBzaW1fbWV0cmljcyhtdW5nZWRfc2ltLCAibm9uX3JlcCIsIGNhbGN1bGF0ZV9ncm91cGVkID0gVFJVRSkKCm5vcm1fcmVmX21ldHJpY3MgPC0gCiAgc2ltX21ldHJpY3MobXVuZ2VkX3NpbSwgInJlZiIsIGNhbGN1bGF0ZV9ncm91cGVkID0gVFJVRSkKCnBlcl9yb3dfbWV0cmljcyA8LQogIGlubmVyX2pvaW4oCiAgICBub3JtX25vbl9yZXBfbWV0cmljc1tbInBlcl9yb3ciXV0sCiAgICBub3JtX3JlZl9tZXRyaWNzW1sicGVyX3JvdyJdXSwKICAgIGJ5ID0gYygiaWQxIiwgYWxsX3NhbWVfY29sc19yZXAsICJzaW1fbWVhbl9hZ2ciKQogICkKCnBlcl9zZXRfbWV0cmljcyA8LQogIGlubmVyX2pvaW4oCiAgICBub3JtX25vbl9yZXBfbWV0cmljc1tbInBlcl9zZXQiXV0sCiAgICBub3JtX3JlZl9tZXRyaWNzW1sicGVyX3NldCJdXSwKICAgIGJ5ID0gYyhhbGxfc2FtZV9jb2xzX3JlcCwgInNpbV9tZWFuX2FnZ19tZWFuX2FnZyIsICJzaW1fbWVhbl9hZ2dfbWVkaWFuX2FnZyIpCiAgKQoKcGVyX3NldF9ncm91cF9tZXRyaWNzIDwtCiAgaW5uZXJfam9pbigKICAgIG5vcm1fbm9uX3JlcF9tZXRyaWNzW1sicGVyX3NldF9ncm91cCJdXSwKICAgIG5vcm1fcmVmX21ldHJpY3NbWyJwZXJfc2V0X2dyb3VwIl1dLAogICAgYnkgPSBjKGFsbF9zYW1lX2NvbHNfcmVwLCAic2ltX21lYW5fYWdnIiwgInNpbV9tZWRpYW5fYWdnIikKICApCmBgYAoKCmBgYHtyfQpwZXJfc2V0X2FsbF9tZXRyaWNzIDwtCiAgcGVyX3NldF9ncm91cF9tZXRyaWNzICU+JQogIHJlbmFtZV93aXRoKCB+IHBhc3RlMCguLCAiX2dyb3VwIiksIHN0YXJ0c193aXRoKCJzaW0iKSkgJT4lCiAgaW5uZXJfam9pbigKICAgIHBlcl9zZXRfbWV0cmljcyAlPiUKICAgICAgcmVuYW1lX3dpdGgoIH4gcGFzdGUwKC4sICJfaW5kaXYiKSwgc3RhcnRzX3dpdGgoInNpbSIpKSwKICAgIGJ5ID0gYyhhbGxfc2FtZV9jb2xzX3JlcCkKICApIAoKZ3JpdCA8LQogIHBlcl9zZXRfYWxsX21ldHJpY3MgJT4lCiAgbXV0YXRlKAogICAgZ3JpdF9ncm91cCA9IHNpbV9zY2FsZWRfbWVhbl9hZ2dfcmVmX2dyb3VwLAogICAgZ3JpdF9pbmRpdiA9IHNpbV9zY2FsZWRfbWVhbl9hZ2dfcmVmX21lYW5fYWdnX2luZGl2CiAgKSAlPiUKICBzZWxlY3QoYWxsX29mKGFsbF9zYW1lX2NvbHNfcmVwKSwgZ3JpdF9ncm91cCwgZ3JpdF9pbmRpdikKYGBgCgojIFBsb3QgZGlzdHJpYnV0aW9ucwoKYGBge3J9CnRhcmdldF9ndWlkZSA8LSBkYXRhLmZyYW1lKAogIE1ldGFkYXRhX2NlbGxfbGluZSA9ICJBNTQ5IiwKICBNZXRhZGF0YV9nZW5lX25hbWUgPSAiSVRHQVYiLAogIE1ldGFkYXRhX3BlcnRfbmFtZSA9ICJJVEdBVi0xIikKCnNpc3Rlcl9ndWlkZSA8LSBkYXRhLmZyYW1lKAogIE1ldGFkYXRhX2NlbGxfbGluZSA9ICJBNTQ5IiwKICBNZXRhZGF0YV9nZW5lX25hbWUgPSAiSVRHQVYiLAogIE1ldGFkYXRhX3BlcnRfbmFtZSA9ICJJVEdBVi0yIikKCmN1dHRpbmdfY29udHJvbHMgPC0gZGF0YS5mcmFtZSgKICBNZXRhZGF0YV9jZWxsX2xpbmUgPSBjKCJBNTQ5IiwgIkE1NDkiLCAiQTU0OSIpLAogIE1ldGFkYXRhX2dlbmVfbmFtZSA9IGMoIkNocjIiLCAiTHVjIiwgIkxhY1oiKQopCgptZXRhZGF0YSA8LSAKICBwcm9maWxlcyAlPiUKICBzZWxlY3QobWF0Y2hlcygiTWV0YWRhdGEiKSkgJT4lIAogIHNlbGVjdCgtTWV0YWRhdGFfV2VsbENvbCwgLU1ldGFkYXRhX1dlbGxSb3cpCgpmZWF0dXJlcyA8LSBmdW5jdGlvbihkZikgdChhcy5tYXRyaXgoZGYgJT4lIHNlbGVjdCgtbWF0Y2hlcygiXk1ldGFkYXRhXyIpKSkpCgpwbGF0ZSA8LSBmdW5jdGlvbihkZikgZGYkTWV0YWRhdGFfUGxhdGUKCnBfdGFyZ2V0X2d1aWRlIDwtIHByb2ZpbGVzICU+JSBpbm5lcl9qb2luKHRhcmdldF9ndWlkZSkKCnBfc2lzdGVyX2d1aWRlIDwtIHByb2ZpbGVzICU+JSBpbm5lcl9qb2luKHNpc3Rlcl9ndWlkZSkKCnBfY3V0dGluZ19jb250cm9scyA8LSBwcm9maWxlcyAlPiUgaW5uZXJfam9pbihjdXR0aW5nX2NvbnRyb2xzKQpgYGAKCmBgYHtyfQpwX3RhcmdldF9ndWlkZSAlPiUgZ3JvdXBfYnkoYWNyb3NzKGFsbF9vZih1bmlxdWUoYyhhbGxfc2FtZV9jb2xzX3JlcCwgYWxsX3NhbWVfY29sc19yZWYpKSkpKSAlPiUgdGFsbHkoKQpgYGAKCgpgYGB7cn0KcF9zaXN0ZXJfZ3VpZGUgJT4lIGdyb3VwX2J5KGFjcm9zcyhhbGxfb2YodW5pcXVlKGMoYWxsX3NhbWVfY29sc19yZXAsIGFsbF9zYW1lX2NvbHNfcmVmKSkpKSkgJT4lIHRhbGx5KCkKYGBgCgoKYGBge3J9CnBfY3V0dGluZ19jb250cm9scyAlPiUgZ3JvdXBfYnkoYWNyb3NzKGFsbF9vZihhbGxfc2FtZV9jb2xzX3JlZikpKSAlPiUgdGFsbHkoKQpgYGAKCgpgYGB7cn0KYWxsX3NpbV9jb3VudHMgPC0KICBtdW5nZWRfc2ltICU+JQogIGdyb3VwX2J5KGFjcm9zcyhhbGxfb2YoYygiaWQxIiwgYWxsX3NhbWVfY29sc19yZXAsICJ0eXBlIikpKSkgJT4lCiAgdGFsbHkoKSAlPiUKICBhcnJhbmdlKGFjcm9zcyhhbGxfb2YoYWxsX3NhbWVfY29sc19yZXApKSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJ0eXBlIiwKICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAibl8iLAogICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gbiwKICAgICAgICAgICAgICB2YWx1ZXNfZmlsbCA9IDApCmBgYAoKYGBge3J9CmFsbF9zaW1fY291bnRzICU+JQogIGlubmVyX2pvaW4odGFyZ2V0X2d1aWRlKQpgYGAKYGBge3J9CnRhcmdldF9kaXN0cmlidXRpb25zIDwtCiAgbXVuZ2VkX3NpbSAlPiUKICBpbm5lcl9qb2luKHRhcmdldF9ndWlkZSkKCnRhcmdldF9kaXN0cmlidXRpb25zICU+JQogIGdyb3VwX2J5KHR5cGUpICU+JQogIHRhbGx5KCkKYGBgCgpgYGB7cn0KdGFyZ2V0X2Rpc3RyaWJ1dGlvbnMgJT4lCiAgZ3JvdXBfYnkodHlwZSkgJT4lCiAgdGFsbHkoKSAlPiUKICBnZ3Bsb3QoYWVzKHR5cGUsIG4pKSArIGdlb21fY29sKCkKYGBgCgpgYGB7cn0KZ3JpdCAlPiUKICBpbm5lcl9qb2luKHRhcmdldF9ndWlkZSwgYnkgPSBjb2xuYW1lcyh0YXJnZXRfZ3VpZGUpKQoKdGFyZ2V0X2Rpc3RyaWJ1dGlvbnMgJT4lCiAgZmlsdGVyKHR5cGUgJWluJSBjKCJyZWYiLCAicmVwX2dyb3VwIiwgIm5vbl9yZXAiKSkgJT4lCiAgZ2dwbG90KGFlcyhzaW0pKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gLjAyKSArIGZhY2V0X3dyYXAofnR5cGUsIG5jb2wgPSAxKQpgYGAKCgpgYGB7cn0KdGFyZ2V0X2Rpc3RyaWJ1dGlvbnMgJT4lCiAgZmlsdGVyKHR5cGUgJWluJSBjKCJyZWYiLCAicmVwIiwgIm5vbl9yZXAiKSkgJT4lCiAgZ2dwbG90KGFlcyhzaW0pKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gLjAyKSArIGZhY2V0X3dyYXAofnR5cGUsIG5jb2wgPSAxKQpgYGAKCmBgYHtyfQp0YXJnZXRfZ3VpZGUgPC0gZGF0YS5mcmFtZSgKICBNZXRhZGF0YV9jZWxsX2xpbmUgPSAiQTU0OSIsCiAgTWV0YWRhdGFfZ2VuZV9uYW1lID0gIkNDTkQxIiwKICBNZXRhZGF0YV9wZXJ0X25hbWUgPSAiQ0NORDEtMSIpCgptdW5nZWRfc2ltICU+JQogIGlubmVyX2pvaW4odGFyZ2V0X2d1aWRlLCBieSA9IGNvbG5hbWVzKHRhcmdldF9ndWlkZSkpICU+JQogIGdncGxvdChhZXMoc2ltKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IC4wMikgKyBmYWNldF93cmFwKH50eXBlLCBuY29sID0gMSkgIAoKZ3JpdCAlPiUKICBpbm5lcl9qb2luKHRhcmdldF9ndWlkZSwgYnkgPSBjb2xuYW1lcyh0YXJnZXRfZ3VpZGUpKSAKYGBgCmBgYHtyfQp0YXJnZXRfZ3VpZGUgPC0gZGF0YS5mcmFtZSgKICBNZXRhZGF0YV9jZWxsX2xpbmUgPSAiQTU0OSIsCiAgTWV0YWRhdGFfZ2VuZV9uYW1lID0gIkNDTkQxIiwKICBNZXRhZGF0YV9wZXJ0X25hbWUgPSAiQ0NORDEtMiIpCgptdW5nZWRfc2ltICU+JQogIGlubmVyX2pvaW4odGFyZ2V0X2d1aWRlLCBieSA9IGNvbG5hbWVzKHRhcmdldF9ndWlkZSkpICU+JQogIGdncGxvdChhZXMoc2ltKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IC4wMikgKyBmYWNldF93cmFwKH50eXBlLCBuY29sID0gMSkgIAoKZ3JpdCAlPiUKICBpbm5lcl9qb2luKHRhcmdldF9ndWlkZSwgYnkgPSBjb2xuYW1lcyh0YXJnZXRfZ3VpZGUpKQpgYGAKCmBgYHtyfQp0YXJnZXRfZ3VpZGUgPC0gZGF0YS5mcmFtZSgKICBNZXRhZGF0YV9jZWxsX2xpbmUgPSAiQTU0OSIsCiAgTWV0YWRhdGFfZ2VuZV9uYW1lID0gIlNUQVQzIiwKICBNZXRhZGF0YV9wZXJ0X25hbWUgPSAiU1RBVDMtMSIpCgptdW5nZWRfc2ltICU+JQogIGlubmVyX2pvaW4odGFyZ2V0X2d1aWRlLCBieSA9IGNvbG5hbWVzKHRhcmdldF9ndWlkZSkpICU+JQogIGdncGxvdChhZXMoc2ltKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IC4wMikgKyBmYWNldF93cmFwKH50eXBlLCBuY29sID0gMSkgIAoKZ3JpdCAlPiUKICBpbm5lcl9qb2luKHRhcmdldF9ndWlkZSwgYnkgPSBjb2xuYW1lcyh0YXJnZXRfZ3VpZGUpKQpgYGAKYGBge3J9CnRhcmdldF9ndWlkZSA8LSBkYXRhLmZyYW1lKAogIE1ldGFkYXRhX2NlbGxfbGluZSA9ICJBNTQ5IiwKICBNZXRhZGF0YV9nZW5lX25hbWUgPSAiTVlDIiwKICBNZXRhZGF0YV9wZXJ0X25hbWUgPSAiTVlDLTEiKQoKbXVuZ2VkX3NpbSAlPiUKICBpbm5lcl9qb2luKHRhcmdldF9ndWlkZSwgYnkgPSBjb2xuYW1lcyh0YXJnZXRfZ3VpZGUpKSAlPiUKICBnZ3Bsb3QoYWVzKHNpbSkpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAuMDIpICsgZmFjZXRfd3JhcCh+dHlwZSwgbmNvbCA9IDEpICAKCmdyaXQgJT4lCiAgaW5uZXJfam9pbih0YXJnZXRfZ3VpZGUsIGJ5ID0gY29sbmFtZXModGFyZ2V0X2d1aWRlKSkKYGBgCgo=