output:
html_notebook:
toc: true
toc_float: true
toc_depth: 3
number_sections: true
theme: lumen
output: github_document
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)
Libraries
library(magrittr)
library(tidyverse)
library(arrow)
source("../../R/cytominer-eval.R")
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...
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
)
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)
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=