Functions
Utils
Get feature matrix
feats <- function(X, pattern = "Cells_AreaShape_Zernike", ...) {
X %>% dplyr::select(matches(pattern))
}
Statistics
Difference between means
Compute difference between mean of specified group and everything else
mean_diff <-
function(X, label_i, label_col = "Metadata_pert_name", ...) {
A <- X %>% filter(.data[[label_col]] == label_i)
B <- X %>% filter(.data[[label_col]] != label_i)
sqrt(sum((colMeans(feats(
A, ...
)) - colMeans(feats(
B, ...
))) ** 2))
}
Inv of mean pairwise distance
invmpd <-
function(X, label_i, label_col = "Metadata_pert_name", ...) {
A <- X %>% filter(.data[[label_col]] == label_i)
1 / mean(dist(as.matrix(feats(A, ...))))
}
Trace of precision matrix
Compute trace of precision matrix of specified group (indicated by label_i)
trace_precision <- function(X, label_i, label_col = "Metadata_pert_name", ...) {
A <- X %>% filter(.data[[label_col]] == label_i)
sv <- svd(as.matrix(feats(A, ...)), nu = 0, nv = 0)$d
sum(1/(sv**2))
}
Inverse of major axis length
pc1 <- function(X, label_i, label_col = "Metadata_pert_name", ...) {
A <- X %>% filter(.data[[label_col]] == label_i)
sv <- svd(as.matrix(feats(A, ...)), nu = 0, nv = 0)$d
1/sv[1]
}
Precision
beta <- function(X, label_i, label_col = "Metadata_pert_name", ...) {
A <- X %>% filter(.data[[label_col]] == label_i)
1 / sd(as.matrix(feats(A, ...)))
}
Testing
Permute label
permute <- function(X, label_col = "Metadata_pert_name") {
X[[label_col]] <- sample(X[[label_col]])
X
}
Compute null distribution i.e. the sampling distribution of the test statistic under the null hypothesis
generate_null <-
function(X,
label,
test_statistic = mean_diff,
n = 100,
label_col = "Metadata_pert_name",
...) {
test_statistic(permute(X, label_col), label, ...)
times(n) %dopar% test_statistic(permute(X, label_col), label, ...)
}
Run permutation test and plot
run_test <- function(test_statistic, ...) {
null_distribution <- generate_null(test_statistic = test_statistic, ...)
test_statistic_value <- test_statistic(...)
p_value <- sum(null_distribution > test_statistic_value) / length(null_distribution)
p <-
data.frame(x = null_distribution) %>%
ggplot(aes(x)) + geom_histogram(bins = 30) +
geom_vline(xintercept = test_statistic_value, color = "red") +
labs(caption = glue("p-value = {round(p_value, 2)}"))
p
}
Load
Load cell health data
url <-
"https://github.com/broadinstitute/grit-benchmark/raw/main/1.calculate-metrics/cell-health/data/cell_health_merged_feature_select.csv.gz"
cellhealth <-
read_csv(
url,
col_types = cols(
.default = col_double(),
Metadata_Plate = col_character(),
Metadata_Well = col_character(),
Metadata_WellRow = col_character(),
Metadata_WellCol = col_character(),
Metadata_cell_line = col_character(),
Metadata_gene_name = col_character(),
Metadata_pert_name = col_character()
)
)
cellhealth <-
cellhealth %>%
filter(Metadata_cell_line == "A549") %>%
filter(!(Metadata_gene_name %in% c("EMPTY", "Chr2")))
cellhealth %>%
count(Metadata_pert_name, name = "n_replicates") %>%
count(n_replicates)
Run
X <- cellhealth
n <- 300
label_col <- "Metadata_pert_name"
pattern <- "Cells_Intensity"
Difference between means
label <- "LacZ-3"
test_statistic <- mean_diff
test_statistic_name <- "mean_diff"
run_test(
X = X,
label = label,
test_statistic = test_statistic,
n = n,
label_col = label_col
) +
xlab(test_statistic_name) +
ggtitle(
glue(
"{label_col} = {label}\n{test_statistic_name} = {round(test_statistic(X, label), 2)}"
)
)

label <- "CDK4-2"
test_statistic <- mean_diff
test_statistic_name <- "mean_diff"
run_test(
X = X,
label = label,
test_statistic = test_statistic,
n = n,
label_col = label_col
) +
xlab(test_statistic_name) +
ggtitle(
glue(
"{label_col} = {label}\n{test_statistic_name} = {round(test_statistic(X, label), 2)}"
)
)

Inv of mean pairwise distance
label <- "LacZ-3"
test_statistic <- invmpd
test_statistic_name <- "invmpd"
run_test(X = X,
label = label,
test_statistic = test_statistic,
n = n,
label_col = label_col) +
xlab(test_statistic_name) +
ggtitle(glue(
"{label_col} = {label}\n{test_statistic_name} = {round(test_statistic(X, label), 3)}"
))

label <- "CDK4-2"
test_statistic <- invmpd
test_statistic_name <- "invmpd"
run_test(X = X,
label = label,
test_statistic = test_statistic,
n = n,
label_col = label_col) +
xlab(test_statistic_name) +
ggtitle(glue(
"{label_col} = {label}\n{test_statistic_name} = {round(test_statistic(X, label), 3)}"
))

Trace of precision matrix
label <- "LacZ-3"
test_statistic <- trace_precision
test_statistic_name <- "trace_precision"
run_test(X = X,
label = label,
test_statistic = test_statistic,
n = n,
label_col = label_col) +
xlab(test_statistic_name) +
ggtitle(glue(
"{label_col} = {label}\n{test_statistic_name} = {round(test_statistic(X, label), 3)}"
))

label <- "CDK4-2"
test_statistic <- trace_precision
test_statistic_name <- "trace_precision"
run_test(X = X,
label = label,
test_statistic = test_statistic,
n = n,
label_col = label_col) +
xlab(test_statistic_name) +
ggtitle(glue(
"{label_col} = {label}\n{test_statistic_name} = {round(test_statistic(X, label), 3)}"
))

Inverse of major axis length
label <- "LacZ-3"
test_statistic <- pc1
test_statistic_name <- "pc1"
run_test(X = X,
label = label,
test_statistic = test_statistic,
n = n,
label_col = label_col) +
xlab(test_statistic_name) +
ggtitle(glue(
"{label_col} = {label}\n{test_statistic_name} = {round(test_statistic(X, label), 3)}"
))

label <- "CDK4-2"
test_statistic <- pc1
test_statistic_name <- "pc1"
run_test(X = X,
label = label,
test_statistic = test_statistic,
n = n,
label_col = label_col) +
xlab(test_statistic_name) +
ggtitle(glue(
"{label_col} = {label}\n{test_statistic_name} = {round(test_statistic(X, label), 3)}"
))

Precision - single variable
label <- "LacZ-3"
test_statistic <- beta
test_statistic_name <- "beta"
pattern <- "Cells_AreaShape_Zernike_0_0"
run_test(X = X,
label = label,
test_statistic = test_statistic,
n = n,
label_col = label_col,
pattern = pattern) +
xlab(test_statistic_name) +
ggtitle(glue(
"{label_col} = {label}\n{test_statistic_name} = {round(test_statistic(X, label), 3)}"
))

label <- "CDK4-2"
test_statistic <- beta
test_statistic_name <- "beta"
pattern <- "Cells_AreaShape_Zernike_0_0"
run_test(X = X,
label = label,
test_statistic = test_statistic,
n = n,
label_col = label_col,
pattern = pattern) +
xlab(test_statistic_name) +
ggtitle(glue(
"{label_col} = {label}\n{test_statistic_name} = {round(test_statistic(X, label), 3)}"
))

Difference between means - single variable
label <- "LacZ-3"
test_statistic <- mean_diff
test_statistic_name <- "mean_diff"
pattern <- "Cells_AreaShape_Zernike_0_0"
run_test(X = X,
label = label,
test_statistic = test_statistic,
n = n,
label_col = label_col,
pattern = pattern) +
xlab(test_statistic_name) +
ggtitle(glue(
"{label_col} = {label}\n{test_statistic_name} = {round(test_statistic(X, label), 3)}"
))

label <- "CDK4-2"
test_statistic <- mean_diff
test_statistic_name <- "mean_diff"
pattern <- "Cells_AreaShape_Zernike_0_0"
run_test(X = X,
label = label,
test_statistic = test_statistic,
n = n,
label_col = label_col,
pattern = pattern) +
xlab(test_statistic_name) +
ggtitle(glue(
"{label_col} = {label}\n{test_statistic_name} = {round(test_statistic(X, label), 3)}"
))

LS0tCnRpdGxlOiAiUGVyY2VudCByZXBsaWNhdGluZyB0aHJvdWdoIHRoZSBsZW5zIG9mIHBlcm11dGF0aW9uIHRlc3RpbmciCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgU2V0dXAKCmBgYHtyfQpzZXQuc2VlZCg0MikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZm9yZWFjaCkKbGlicmFyeShnbHVlKQpgYGAKCgpgYGB7cn0KZG9QYXJhbGxlbDo6cmVnaXN0ZXJEb1BhcmFsbGVsKGNvcmVzID0gOCkKYGBgCgojIEZ1bmN0aW9ucwoKIyMgVXRpbHMKCkdldCBmZWF0dXJlIG1hdHJpeAoKYGBge3J9CmZlYXRzIDwtIGZ1bmN0aW9uKFgsIHBhdHRlcm4gPSAiQ2VsbHNfQXJlYVNoYXBlX1plcm5pa2UiLCAuLi4pIHsKICBYICU+JSBkcGx5cjo6c2VsZWN0KG1hdGNoZXMocGF0dGVybikpCn0KYGBgCgojIyBTdGF0aXN0aWNzCgojIyMgRGlmZmVyZW5jZSBiZXR3ZWVuIG1lYW5zCgpDb21wdXRlIGRpZmZlcmVuY2UgYmV0d2VlbiBtZWFuIG9mIHNwZWNpZmllZCBncm91cCBhbmQgZXZlcnl0aGluZyBlbHNlCgpgYGB7cn0KbWVhbl9kaWZmIDwtCiAgZnVuY3Rpb24oWCwgbGFiZWxfaSwgbGFiZWxfY29sID0gIk1ldGFkYXRhX3BlcnRfbmFtZSIsIC4uLikgewogICAgQSA8LSBYICU+JSBmaWx0ZXIoLmRhdGFbW2xhYmVsX2NvbF1dID09IGxhYmVsX2kpCiAgICBCIDwtIFggJT4lIGZpbHRlciguZGF0YVtbbGFiZWxfY29sXV0gIT0gbGFiZWxfaSkKICAgIAogICAgc3FydChzdW0oKGNvbE1lYW5zKGZlYXRzKAogICAgICBBLCAuLi4KICAgICkpIC0gY29sTWVhbnMoZmVhdHMoCiAgICAgIEIsIC4uLgogICAgKSkpICoqIDIpKQogIH0KYGBgCgojIyMgTWVkaWFuIHJlcGxpY2F0ZSBjb3JyZWxhdGlvbgoKQ29tcHV0ZSBtZWRpYW4gcmVwbGljYXRlIGNvcnJlbGF0aW9uIG9mIHNwZWNpZmllZCBncm91cCAoaW5kaWNhdGVkIGJ5IGBsYWJlbF9pYCkKCmBgYHtyfQptcmMgPC0gZnVuY3Rpb24oWCwgbGFiZWxfaSwgbGFiZWxfY29sID0gIk1ldGFkYXRhX3BlcnRfbmFtZSIsIC4uLikgewogIEEgPC0gWCAlPiUgZmlsdGVyKC5kYXRhW1tsYWJlbF9jb2xdXSA9PSBsYWJlbF9pKQogIAogIGNvcnZlYyA8LSBmdW5jdGlvbihtKSB7CiAgICBjbSA8LSBjb3IobSkKICAgIGNtW3VwcGVyLnRyaShjbSldCiAgfQogIAogIG1lZGlhbihjb3J2ZWModChhcy5tYXRyaXgoZmVhdHMoCiAgICBBLCAuLi4KICApKSkpKQp9CmBgYAoKIyMjIEludiBvZiBtZWFuIHBhaXJ3aXNlIGRpc3RhbmNlCgpgYGB7cn0KaW52bXBkIDwtCiAgZnVuY3Rpb24oWCwgbGFiZWxfaSwgbGFiZWxfY29sID0gIk1ldGFkYXRhX3BlcnRfbmFtZSIsIC4uLikgewogICAgCiAgICBBIDwtIFggJT4lIGZpbHRlciguZGF0YVtbbGFiZWxfY29sXV0gPT0gbGFiZWxfaSkKICAgIAogICAgMSAvIG1lYW4oZGlzdChhcy5tYXRyaXgoZmVhdHMoQSwgLi4uKSkpKQogICAgCiAgfQpgYGAKCiMjIyBUcmFjZSBvZiBwcmVjaXNpb24gbWF0cml4CgpDb21wdXRlIHRyYWNlIG9mIHByZWNpc2lvbiBtYXRyaXggb2Ygc3BlY2lmaWVkIGdyb3VwIChpbmRpY2F0ZWQgYnkgYGxhYmVsX2lgKQoKYGBge3J9CnRyYWNlX3ByZWNpc2lvbiA8LSBmdW5jdGlvbihYLCBsYWJlbF9pLCBsYWJlbF9jb2wgPSAiTWV0YWRhdGFfcGVydF9uYW1lIiwgLi4uKSB7CiAgCiAgQSA8LSBYICU+JSBmaWx0ZXIoLmRhdGFbW2xhYmVsX2NvbF1dID09IGxhYmVsX2kpCiAgCiAgc3YgPC0gc3ZkKGFzLm1hdHJpeChmZWF0cyhBLCAuLi4pKSwgbnUgPSAwLCBudiA9IDApJGQKCiAgc3VtKDEvKHN2KioyKSkKICAKfQpgYGAKCiMjIyBJbnZlcnNlIG9mIG1ham9yIGF4aXMgbGVuZ3RoCgpgYGB7cn0KcGMxIDwtIGZ1bmN0aW9uKFgsIGxhYmVsX2ksIGxhYmVsX2NvbCA9ICJNZXRhZGF0YV9wZXJ0X25hbWUiLCAuLi4pIHsKICAKICBBIDwtIFggJT4lIGZpbHRlciguZGF0YVtbbGFiZWxfY29sXV0gPT0gbGFiZWxfaSkKICAKICBzdiA8LSBzdmQoYXMubWF0cml4KGZlYXRzKEEsIC4uLikpLCBudSA9IDAsIG52ID0gMCkkZAoKICAxL3N2WzFdCiAgCn0KYGBgCgojIyMgUHJlY2lzaW9uCgpgYGB7cn0KYmV0YSA8LSBmdW5jdGlvbihYLCBsYWJlbF9pLCBsYWJlbF9jb2wgPSAiTWV0YWRhdGFfcGVydF9uYW1lIiwgLi4uKSB7CiAgCiAgQSA8LSBYICU+JSBmaWx0ZXIoLmRhdGFbW2xhYmVsX2NvbF1dID09IGxhYmVsX2kpCiAgCiAgMSAvIHNkKGFzLm1hdHJpeChmZWF0cyhBLCAuLi4pKSkKCn0KYGBgCgojIyBUZXN0aW5nCgpQZXJtdXRlIGxhYmVsCgpgYGB7cn0KcGVybXV0ZSA8LSBmdW5jdGlvbihYLCBsYWJlbF9jb2wgPSAiTWV0YWRhdGFfcGVydF9uYW1lIikgewogIFhbW2xhYmVsX2NvbF1dIDwtIHNhbXBsZShYW1tsYWJlbF9jb2xdXSkKICBYCn0KYGBgCgpDb21wdXRlIG51bGwgZGlzdHJpYnV0aW9uIGkuZS4gdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdGVzdCBzdGF0aXN0aWMgdW5kZXIgdGhlIG51bGwgaHlwb3RoZXNpcwoKYGBge3J9CmdlbmVyYXRlX251bGwgPC0KICBmdW5jdGlvbihYLAogICAgICAgICAgIGxhYmVsLAogICAgICAgICAgIHRlc3Rfc3RhdGlzdGljID0gbWVhbl9kaWZmLAogICAgICAgICAgIG4gPSAxMDAsCiAgICAgICAgICAgbGFiZWxfY29sID0gIk1ldGFkYXRhX3BlcnRfbmFtZSIsCiAgICAgICAgICAgLi4uKSB7CiAgICAKICAgIHRlc3Rfc3RhdGlzdGljKHBlcm11dGUoWCwgbGFiZWxfY29sKSwgbGFiZWwsIC4uLikKICAgIAogICAgdGltZXMobikgJWRvcGFyJSB0ZXN0X3N0YXRpc3RpYyhwZXJtdXRlKFgsIGxhYmVsX2NvbCksIGxhYmVsLCAuLi4pCiAgICAKICB9CgpgYGAKClJ1biBwZXJtdXRhdGlvbiB0ZXN0IGFuZCBwbG90CgpgYGB7cn0KcnVuX3Rlc3QgPC0gZnVuY3Rpb24odGVzdF9zdGF0aXN0aWMsIC4uLikgewoKICBudWxsX2Rpc3RyaWJ1dGlvbiA8LSBnZW5lcmF0ZV9udWxsKHRlc3Rfc3RhdGlzdGljID0gdGVzdF9zdGF0aXN0aWMsIC4uLikKICAKICB0ZXN0X3N0YXRpc3RpY192YWx1ZSA8LSB0ZXN0X3N0YXRpc3RpYyguLi4pCiAgCiAgcF92YWx1ZSA8LSBzdW0obnVsbF9kaXN0cmlidXRpb24gPiB0ZXN0X3N0YXRpc3RpY192YWx1ZSkgLyBsZW5ndGgobnVsbF9kaXN0cmlidXRpb24pCiAgCiAgcCA8LSAKICAgIGRhdGEuZnJhbWUoeCA9IG51bGxfZGlzdHJpYnV0aW9uKSAlPiUKICAgIGdncGxvdChhZXMoeCkpICsgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwKSArIAogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gdGVzdF9zdGF0aXN0aWNfdmFsdWUsIGNvbG9yID0gInJlZCIpICsgCiAgICBsYWJzKGNhcHRpb24gPSBnbHVlKCJwLXZhbHVlID0ge3JvdW5kKHBfdmFsdWUsIDIpfSIpKQogIAogIHAKfQpgYGAKCgojIExvYWQgCgpMb2FkIGNlbGwgaGVhbHRoIGRhdGEKCmBgYHtyfQp1cmwgPC0KICAiaHR0cHM6Ly9naXRodWIuY29tL2Jyb2FkaW5zdGl0dXRlL2dyaXQtYmVuY2htYXJrL3Jhdy9tYWluLzEuY2FsY3VsYXRlLW1ldHJpY3MvY2VsbC1oZWFsdGgvZGF0YS9jZWxsX2hlYWx0aF9tZXJnZWRfZmVhdHVyZV9zZWxlY3QuY3N2Lmd6IgoKY2VsbGhlYWx0aCA8LQogIHJlYWRfY3N2KAogICAgdXJsLAogICAgY29sX3R5cGVzID0gY29scygKICAgICAgLmRlZmF1bHQgPSBjb2xfZG91YmxlKCksCiAgICAgIE1ldGFkYXRhX1BsYXRlID0gY29sX2NoYXJhY3RlcigpLAogICAgICBNZXRhZGF0YV9XZWxsID0gY29sX2NoYXJhY3RlcigpLAogICAgICBNZXRhZGF0YV9XZWxsUm93ID0gY29sX2NoYXJhY3RlcigpLAogICAgICBNZXRhZGF0YV9XZWxsQ29sID0gY29sX2NoYXJhY3RlcigpLAogICAgICBNZXRhZGF0YV9jZWxsX2xpbmUgPSBjb2xfY2hhcmFjdGVyKCksCiAgICAgIE1ldGFkYXRhX2dlbmVfbmFtZSA9IGNvbF9jaGFyYWN0ZXIoKSwKICAgICAgTWV0YWRhdGFfcGVydF9uYW1lID0gY29sX2NoYXJhY3RlcigpCiAgICApCiAgKQoKY2VsbGhlYWx0aCA8LQogIGNlbGxoZWFsdGggJT4lCiAgZmlsdGVyKE1ldGFkYXRhX2NlbGxfbGluZSA9PSAiQTU0OSIpICU+JQogIGZpbHRlcighKE1ldGFkYXRhX2dlbmVfbmFtZSAlaW4lIGMoIkVNUFRZIiwgIkNocjIiKSkpCmBgYAoKCmBgYHtyfQpjZWxsaGVhbHRoICU+JQogIGNvdW50KE1ldGFkYXRhX3BlcnRfbmFtZSwgbmFtZSA9ICJuX3JlcGxpY2F0ZXMiKSAlPiUgCiAgY291bnQobl9yZXBsaWNhdGVzKQpgYGAKCiMgUnVuCgpgYGB7cn0KWCA8LSBjZWxsaGVhbHRoCm4gPC0gMzAwCmxhYmVsX2NvbCA8LSAiTWV0YWRhdGFfcGVydF9uYW1lIgpwYXR0ZXJuIDwtICJDZWxsc19JbnRlbnNpdHkiCmBgYAoKIyMgRGlmZmVyZW5jZSBiZXR3ZWVuIG1lYW5zCgpgYGB7cn0KbGFiZWwgPC0gIkxhY1otMyIKdGVzdF9zdGF0aXN0aWMgPC0gbWVhbl9kaWZmCnRlc3Rfc3RhdGlzdGljX25hbWUgPC0gIm1lYW5fZGlmZiIKCnJ1bl90ZXN0KAogIFggPSBYLAogIGxhYmVsID0gbGFiZWwsCiAgdGVzdF9zdGF0aXN0aWMgPSB0ZXN0X3N0YXRpc3RpYywKICBuID0gbiwKICBsYWJlbF9jb2wgPSBsYWJlbF9jb2wKKSArCiAgeGxhYih0ZXN0X3N0YXRpc3RpY19uYW1lKSArCiAgZ2d0aXRsZSgKICAgIGdsdWUoCiAgICAgICJ7bGFiZWxfY29sfSA9IHtsYWJlbH1cbnt0ZXN0X3N0YXRpc3RpY19uYW1lfSA9IHtyb3VuZCh0ZXN0X3N0YXRpc3RpYyhYLCBsYWJlbCksIDIpfSIKICAgICkKICApCmBgYAoKCmBgYHtyfQpsYWJlbCA8LSAiQ0RLNC0yIgp0ZXN0X3N0YXRpc3RpYyA8LSBtZWFuX2RpZmYKdGVzdF9zdGF0aXN0aWNfbmFtZSA8LSAibWVhbl9kaWZmIgoKcnVuX3Rlc3QoCiAgWCA9IFgsCiAgbGFiZWwgPSBsYWJlbCwKICB0ZXN0X3N0YXRpc3RpYyA9IHRlc3Rfc3RhdGlzdGljLAogIG4gPSBuLAogIGxhYmVsX2NvbCA9IGxhYmVsX2NvbAopICsKICB4bGFiKHRlc3Rfc3RhdGlzdGljX25hbWUpICsKICBnZ3RpdGxlKAogICAgZ2x1ZSgKICAgICAgIntsYWJlbF9jb2x9ID0ge2xhYmVsfVxue3Rlc3Rfc3RhdGlzdGljX25hbWV9ID0ge3JvdW5kKHRlc3Rfc3RhdGlzdGljKFgsIGxhYmVsKSwgMil9IgogICAgKQogICkKYGBgCgojIyBNZWRpYW4gcmVwbGljYXRlIGNvcnJlbGF0aW9uCgpgYGB7cn0KbGFiZWwgPC0gIkxhY1otMyIKdGVzdF9zdGF0aXN0aWMgPC0gbXJjCnRlc3Rfc3RhdGlzdGljX25hbWUgPC0gIm1yYyIKCnJ1bl90ZXN0KFggPSBYLAogICAgICAgICBsYWJlbCA9IGxhYmVsLAogICAgICAgICB0ZXN0X3N0YXRpc3RpYyA9IHRlc3Rfc3RhdGlzdGljLAogICAgICAgICBuID0gbiwKICAgICAgICAgbGFiZWxfY29sID0gbGFiZWxfY29sKSArCiAgeGxhYih0ZXN0X3N0YXRpc3RpY19uYW1lKSArCiAgZ2d0aXRsZShnbHVlKAogICAgIntsYWJlbF9jb2x9ID0ge2xhYmVsfVxue3Rlc3Rfc3RhdGlzdGljX25hbWV9ID0ge3JvdW5kKHRlc3Rfc3RhdGlzdGljKFgsIGxhYmVsKSwgMil9IgogICAgKSkKYGBgCgoKYGBge3J9CmxhYmVsIDwtICJDREs0LTIiCnRlc3Rfc3RhdGlzdGljIDwtIG1yYwp0ZXN0X3N0YXRpc3RpY19uYW1lIDwtICJtcmMiCgpydW5fdGVzdChYID0gWCwKICAgICAgICAgbGFiZWwgPSBsYWJlbCwKICAgICAgICAgdGVzdF9zdGF0aXN0aWMgPSB0ZXN0X3N0YXRpc3RpYywKICAgICAgICAgbiA9IG4sCiAgICAgICAgIGxhYmVsX2NvbCA9IGxhYmVsX2NvbCkgKwogIHhsYWIodGVzdF9zdGF0aXN0aWNfbmFtZSkgKwogIGdndGl0bGUoZ2x1ZSgKICAgICJ7bGFiZWxfY29sfSA9IHtsYWJlbH1cbnt0ZXN0X3N0YXRpc3RpY19uYW1lfSA9IHtyb3VuZCh0ZXN0X3N0YXRpc3RpYyhYLCBsYWJlbCksIDIpfSIKICAgICkpCmBgYAojIyBJbnYgb2YgbWVhbiBwYWlyd2lzZSBkaXN0YW5jZQoKCmBgYHtyfQpsYWJlbCA8LSAiTGFjWi0zIgp0ZXN0X3N0YXRpc3RpYyA8LSBpbnZtcGQKdGVzdF9zdGF0aXN0aWNfbmFtZSA8LSAiaW52bXBkIgoKcnVuX3Rlc3QoWCA9IFgsCiAgICAgICAgIGxhYmVsID0gbGFiZWwsCiAgICAgICAgIHRlc3Rfc3RhdGlzdGljID0gdGVzdF9zdGF0aXN0aWMsCiAgICAgICAgIG4gPSBuLAogICAgICAgICBsYWJlbF9jb2wgPSBsYWJlbF9jb2wpICsKICB4bGFiKHRlc3Rfc3RhdGlzdGljX25hbWUpICsKICBnZ3RpdGxlKGdsdWUoCiAgICAie2xhYmVsX2NvbH0gPSB7bGFiZWx9XG57dGVzdF9zdGF0aXN0aWNfbmFtZX0gPSB7cm91bmQodGVzdF9zdGF0aXN0aWMoWCwgbGFiZWwpLCAzKX0iCiAgICApKQpgYGAKCgpgYGB7cn0KbGFiZWwgPC0gIkNESzQtMiIKdGVzdF9zdGF0aXN0aWMgPC0gaW52bXBkCnRlc3Rfc3RhdGlzdGljX25hbWUgPC0gImludm1wZCIKCnJ1bl90ZXN0KFggPSBYLAogICAgICAgICBsYWJlbCA9IGxhYmVsLAogICAgICAgICB0ZXN0X3N0YXRpc3RpYyA9IHRlc3Rfc3RhdGlzdGljLAogICAgICAgICBuID0gbiwKICAgICAgICAgbGFiZWxfY29sID0gbGFiZWxfY29sKSArCiAgeGxhYih0ZXN0X3N0YXRpc3RpY19uYW1lKSArCiAgZ2d0aXRsZShnbHVlKAogICAgIntsYWJlbF9jb2x9ID0ge2xhYmVsfVxue3Rlc3Rfc3RhdGlzdGljX25hbWV9ID0ge3JvdW5kKHRlc3Rfc3RhdGlzdGljKFgsIGxhYmVsKSwgMyl9IgogICAgKSkKYGBgCgojIyBUcmFjZSBvZiBwcmVjaXNpb24gbWF0cml4CgpgYGB7cn0KbGFiZWwgPC0gIkxhY1otMyIKdGVzdF9zdGF0aXN0aWMgPC0gdHJhY2VfcHJlY2lzaW9uCnRlc3Rfc3RhdGlzdGljX25hbWUgPC0gInRyYWNlX3ByZWNpc2lvbiIKCnJ1bl90ZXN0KFggPSBYLAogICAgICAgICBsYWJlbCA9IGxhYmVsLAogICAgICAgICB0ZXN0X3N0YXRpc3RpYyA9IHRlc3Rfc3RhdGlzdGljLAogICAgICAgICBuID0gbiwKICAgICAgICAgbGFiZWxfY29sID0gbGFiZWxfY29sKSArCiAgeGxhYih0ZXN0X3N0YXRpc3RpY19uYW1lKSArCiAgZ2d0aXRsZShnbHVlKAogICAgIntsYWJlbF9jb2x9ID0ge2xhYmVsfVxue3Rlc3Rfc3RhdGlzdGljX25hbWV9ID0ge3JvdW5kKHRlc3Rfc3RhdGlzdGljKFgsIGxhYmVsKSwgMyl9IgogICAgKSkKYGBgCgoKYGBge3J9CmxhYmVsIDwtICJDREs0LTIiCnRlc3Rfc3RhdGlzdGljIDwtIHRyYWNlX3ByZWNpc2lvbgp0ZXN0X3N0YXRpc3RpY19uYW1lIDwtICJ0cmFjZV9wcmVjaXNpb24iCgpydW5fdGVzdChYID0gWCwKICAgICAgICAgbGFiZWwgPSBsYWJlbCwKICAgICAgICAgdGVzdF9zdGF0aXN0aWMgPSB0ZXN0X3N0YXRpc3RpYywKICAgICAgICAgbiA9IG4sCiAgICAgICAgIGxhYmVsX2NvbCA9IGxhYmVsX2NvbCkgKwogIHhsYWIodGVzdF9zdGF0aXN0aWNfbmFtZSkgKwogIGdndGl0bGUoZ2x1ZSgKICAgICJ7bGFiZWxfY29sfSA9IHtsYWJlbH1cbnt0ZXN0X3N0YXRpc3RpY19uYW1lfSA9IHtyb3VuZCh0ZXN0X3N0YXRpc3RpYyhYLCBsYWJlbCksIDMpfSIKICAgICkpCmBgYAoKIyMgSW52ZXJzZSBvZiBtYWpvciBheGlzIGxlbmd0aAoKYGBge3J9CmxhYmVsIDwtICJMYWNaLTMiCnRlc3Rfc3RhdGlzdGljIDwtIHBjMQp0ZXN0X3N0YXRpc3RpY19uYW1lIDwtICJwYzEiCgpydW5fdGVzdChYID0gWCwKICAgICAgICAgbGFiZWwgPSBsYWJlbCwKICAgICAgICAgdGVzdF9zdGF0aXN0aWMgPSB0ZXN0X3N0YXRpc3RpYywKICAgICAgICAgbiA9IG4sCiAgICAgICAgIGxhYmVsX2NvbCA9IGxhYmVsX2NvbCkgKwogIHhsYWIodGVzdF9zdGF0aXN0aWNfbmFtZSkgKwogIGdndGl0bGUoZ2x1ZSgKICAgICJ7bGFiZWxfY29sfSA9IHtsYWJlbH1cbnt0ZXN0X3N0YXRpc3RpY19uYW1lfSA9IHtyb3VuZCh0ZXN0X3N0YXRpc3RpYyhYLCBsYWJlbCksIDMpfSIKICAgICkpCmBgYAoKCmBgYHtyfQpsYWJlbCA8LSAiQ0RLNC0yIgp0ZXN0X3N0YXRpc3RpYyA8LSBwYzEKdGVzdF9zdGF0aXN0aWNfbmFtZSA8LSAicGMxIgoKcnVuX3Rlc3QoWCA9IFgsCiAgICAgICAgIGxhYmVsID0gbGFiZWwsCiAgICAgICAgIHRlc3Rfc3RhdGlzdGljID0gdGVzdF9zdGF0aXN0aWMsCiAgICAgICAgIG4gPSBuLAogICAgICAgICBsYWJlbF9jb2wgPSBsYWJlbF9jb2wpICsKICB4bGFiKHRlc3Rfc3RhdGlzdGljX25hbWUpICsKICBnZ3RpdGxlKGdsdWUoCiAgICAie2xhYmVsX2NvbH0gPSB7bGFiZWx9XG57dGVzdF9zdGF0aXN0aWNfbmFtZX0gPSB7cm91bmQodGVzdF9zdGF0aXN0aWMoWCwgbGFiZWwpLCAzKX0iCiAgICApKQpgYGAKIyMgUHJlY2lzaW9uIC0gc2luZ2xlIHZhcmlhYmxlCgpgYGB7cn0KbGFiZWwgPC0gIkxhY1otMyIKdGVzdF9zdGF0aXN0aWMgPC0gYmV0YQp0ZXN0X3N0YXRpc3RpY19uYW1lIDwtICJiZXRhIgpwYXR0ZXJuIDwtICJDZWxsc19BcmVhU2hhcGVfWmVybmlrZV8wXzAiCgpydW5fdGVzdChYID0gWCwKICAgICAgICAgbGFiZWwgPSBsYWJlbCwKICAgICAgICAgdGVzdF9zdGF0aXN0aWMgPSB0ZXN0X3N0YXRpc3RpYywKICAgICAgICAgbiA9IG4sCiAgICAgICAgIGxhYmVsX2NvbCA9IGxhYmVsX2NvbCwKICAgICAgICAgcGF0dGVybiA9IHBhdHRlcm4pICsKICB4bGFiKHRlc3Rfc3RhdGlzdGljX25hbWUpICsKICBnZ3RpdGxlKGdsdWUoCiAgICAie2xhYmVsX2NvbH0gPSB7bGFiZWx9XG57dGVzdF9zdGF0aXN0aWNfbmFtZX0gPSB7cm91bmQodGVzdF9zdGF0aXN0aWMoWCwgbGFiZWwpLCAzKX0iCiAgICApKQpgYGAKCgpgYGB7cn0KbGFiZWwgPC0gIkNESzQtMiIKdGVzdF9zdGF0aXN0aWMgPC0gYmV0YQp0ZXN0X3N0YXRpc3RpY19uYW1lIDwtICJiZXRhIgpwYXR0ZXJuIDwtICJDZWxsc19BcmVhU2hhcGVfWmVybmlrZV8wXzAiCgpydW5fdGVzdChYID0gWCwKICAgICAgICAgbGFiZWwgPSBsYWJlbCwKICAgICAgICAgdGVzdF9zdGF0aXN0aWMgPSB0ZXN0X3N0YXRpc3RpYywKICAgICAgICAgbiA9IG4sCiAgICAgICAgIGxhYmVsX2NvbCA9IGxhYmVsX2NvbCwKICAgICAgICAgcGF0dGVybiA9IHBhdHRlcm4pICsKICB4bGFiKHRlc3Rfc3RhdGlzdGljX25hbWUpICsKICBnZ3RpdGxlKGdsdWUoCiAgICAie2xhYmVsX2NvbH0gPSB7bGFiZWx9XG57dGVzdF9zdGF0aXN0aWNfbmFtZX0gPSB7cm91bmQodGVzdF9zdGF0aXN0aWMoWCwgbGFiZWwpLCAzKX0iCiAgICApKQpgYGAKIyMgRGlmZmVyZW5jZSBiZXR3ZWVuIG1lYW5zIC0gc2luZ2xlIHZhcmlhYmxlCgpgYGB7cn0KbGFiZWwgPC0gIkxhY1otMyIKdGVzdF9zdGF0aXN0aWMgPC0gbWVhbl9kaWZmCnRlc3Rfc3RhdGlzdGljX25hbWUgPC0gIm1lYW5fZGlmZiIKcGF0dGVybiA8LSAiQ2VsbHNfQXJlYVNoYXBlX1plcm5pa2VfMF8wIgoKcnVuX3Rlc3QoWCA9IFgsCiAgICAgICAgIGxhYmVsID0gbGFiZWwsCiAgICAgICAgIHRlc3Rfc3RhdGlzdGljID0gdGVzdF9zdGF0aXN0aWMsCiAgICAgICAgIG4gPSBuLAogICAgICAgICBsYWJlbF9jb2wgPSBsYWJlbF9jb2wsCiAgICAgICAgIHBhdHRlcm4gPSBwYXR0ZXJuKSArCiAgeGxhYih0ZXN0X3N0YXRpc3RpY19uYW1lKSArCiAgZ2d0aXRsZShnbHVlKAogICAgIntsYWJlbF9jb2x9ID0ge2xhYmVsfVxue3Rlc3Rfc3RhdGlzdGljX25hbWV9ID0ge3JvdW5kKHRlc3Rfc3RhdGlzdGljKFgsIGxhYmVsKSwgMyl9IgogICAgKSkKYGBgCgoKYGBge3J9CmxhYmVsIDwtICJDREs0LTIiCnRlc3Rfc3RhdGlzdGljIDwtIG1lYW5fZGlmZgp0ZXN0X3N0YXRpc3RpY19uYW1lIDwtICJtZWFuX2RpZmYiCnBhdHRlcm4gPC0gIkNlbGxzX0FyZWFTaGFwZV9aZXJuaWtlXzBfMCIKCnJ1bl90ZXN0KFggPSBYLAogICAgICAgICBsYWJlbCA9IGxhYmVsLAogICAgICAgICB0ZXN0X3N0YXRpc3RpYyA9IHRlc3Rfc3RhdGlzdGljLAogICAgICAgICBuID0gbiwKICAgICAgICAgbGFiZWxfY29sID0gbGFiZWxfY29sLAogICAgICAgICBwYXR0ZXJuID0gcGF0dGVybikgKwogIHhsYWIodGVzdF9zdGF0aXN0aWNfbmFtZSkgKwogIGdndGl0bGUoZ2x1ZSgKICAgICJ7bGFiZWxfY29sfSA9IHtsYWJlbH1cbnt0ZXN0X3N0YXRpc3RpY19uYW1lfSA9IHtyb3VuZCh0ZXN0X3N0YXRpc3RpYyhYLCBsYWJlbCksIDMpfSIKICAgICkpCmBgYAoK