LOAD DATA &
SETUP
# Make sure the file name matches exactly what you have
fg_all <- read.csv("../../../../fgsea_all_results.csv", stringsAsFactors = FALSE)
cat("Loaded", nrow(fg_all), "pathways from fgsea_all_results.csv\n")
Loaded 9200 pathways from fgsea_all_results.csv
# STRICT EXCLUSION LIST (Updated)
prolif_terms <- c(
"CELL_CYCLE", "MITOTIC", "G2M", "E2F", "SPINDLE",
"CHROMOSOME", "DNA_REPLICATION", "NUCLEAR_DIVISION",
"ORGANELLE_FISSION", "KINETOCHORE", "CENTROSOME",
"REPLICATION", "SEGREGATION", "DIVISION", "M_PHASE",
"KINESINS", "MEIOSIS", "OOCYTE",
"MICROTUBULE", "CYTOSKELETON", "TRAFFIC", "GOLGI", "CYCLIN",
"RECOMBINATION", "REPAIR", "REPLICATIVE", "POLO_LIKE", "CHECKPOINTS",
"TRANSCRIPTION", "S_PHASE", "ANAPHASE", "TELOPHASE", "PROPHASE",
"CYTOKINESIS", "SPINDLE_ASSEMBLY", "SPINDLE_CHECKPOINT",
"MITOTIC_SPINDLE", "MITOTIC_CHECKPOINT", "MITOTIC_G1",
"MITOTIC_S_PHASE", "MITOTIC_G2M"
)
PREPARE DATA FOR RADAR
PLOT (WITH DATABASE PREFIX)–V1
prepare_radar_data_v1 <- function(fg_tbl, topN_per_db = 3, exclude_prolif = TRUE) {
# A. Filter Proliferation (if requested)
if (exclude_prolif) {
fg_tbl <- fg_tbl %>% filter(!grepl(paste(prolif_terms, collapse = "|"), pathway, ignore.case = TRUE))
}
# B. Filter for significant pathways only
fg_tbl <- fg_tbl %>% filter(pval < 0.05)
# C. Select Top N pathways PER DATABASE (balanced)
top_paths <- bind_rows(
fg_tbl %>% filter(dataset == "hallmark") %>% arrange(pval) %>% slice_head(n = topN_per_db),
fg_tbl %>% filter(dataset == "kegg") %>% arrange(pval) %>% slice_head(n = topN_per_db),
fg_tbl %>% filter(dataset == "reactome") %>% arrange(pval) %>% slice_head(n = topN_per_db),
fg_tbl %>% filter(dataset == "go_bp") %>% arrange(pval) %>% slice_head(n = topN_per_db)
) %>%
mutate(
log_pval = -log10(pval + 1e-15), # Transform P-value
# Add Database Prefix
db_prefix = case_when(
dataset == "hallmark" ~ "HALLMARK",
dataset == "kegg" ~ "KEGG",
dataset == "reactome" ~ "REACTOME",
dataset == "go_bp" ~ "GOBP"
),
# Clean pathway name
clean_pathway = gsub("^HALLMARK_|^KEGG_|^REACTOME_|^GOBP_", "", pathway),
clean_pathway = str_trunc(clean_pathway, 25),
# Combine: DB_PATHWAY format
plot_label = paste0(db_prefix, "_", clean_pathway),
direction = ifelse(NES > 0, "Up", "Down")
) %>%
# D. Make unique to avoid duplicates
mutate(plot_label = make.unique(as.character(plot_label), sep = " ")) %>%
# E. Sort by log_pval for smooth spiral
arrange(log_pval) %>%
mutate(plot_label = factor(plot_label, levels = plot_label))
return(top_paths)
}
PREPARE DATA FOR RADAR
PLOT (BALANCED UP/DOWN FROM EACH DATABASE)-V2
prepare_radar_data_v2 <- function(fg_tbl, topN_per_db = 3, exclude_prolif = TRUE) {
# A. Filter Proliferation (if requested)
if (exclude_prolif) {
fg_tbl <- fg_tbl %>% filter(!grepl(paste(prolif_terms, collapse = "|"), pathway, ignore.case = TRUE))
}
# B. Filter for significant pathways only
fg_tbl <- fg_tbl %>% filter(pval < 0.05)
# C. Select Top N UPREGULATED and DOWNREGULATED pathways PER DATABASE
top_paths <- bind_rows(
# Hallmark
fg_tbl %>% filter(dataset == "hallmark", NES > 0) %>% arrange(pval) %>% slice_head(n = ceiling(topN_per_db/2)),
fg_tbl %>% filter(dataset == "hallmark", NES < 0) %>% arrange(pval) %>% slice_head(n = floor(topN_per_db/2)),
# KEGG
fg_tbl %>% filter(dataset == "kegg", NES > 0) %>% arrange(pval) %>% slice_head(n = ceiling(topN_per_db/2)),
fg_tbl %>% filter(dataset == "kegg", NES < 0) %>% arrange(pval) %>% slice_head(n = floor(topN_per_db/2)),
# Reactome
fg_tbl %>% filter(dataset == "reactome", NES > 0) %>% arrange(pval) %>% slice_head(n = ceiling(topN_per_db/2)),
fg_tbl %>% filter(dataset == "reactome", NES < 0) %>% arrange(pval) %>% slice_head(n = floor(topN_per_db/2)),
# GO:BP
fg_tbl %>% filter(dataset == "go_bp", NES > 0) %>% arrange(pval) %>% slice_head(n = ceiling(topN_per_db/2)),
fg_tbl %>% filter(dataset == "go_bp", NES < 0) %>% arrange(pval) %>% slice_head(n = floor(topN_per_db/2))
) %>%
mutate(
log_pval = -log10(pval + 1e-15),
db_prefix = case_when(
dataset == "hallmark" ~ "HALLMARK",
dataset == "kegg" ~ "KEGG",
dataset == "reactome" ~ "REACTOME",
dataset == "go_bp" ~ "GOBP"
),
clean_pathway = gsub("^HALLMARK_|^KEGG_|^REACTOME_|^GOBP_", "", pathway),
clean_pathway = str_trunc(clean_pathway, 25),
plot_label = paste0(db_prefix, "_", clean_pathway),
direction = ifelse(NES > 0, "Up", "Down")
) %>%
mutate(plot_label = make.unique(as.character(plot_label), sep = " ")) %>%
arrange(log_pval) %>%
mutate(plot_label = factor(plot_label, levels = plot_label))
return(top_paths)
}
GGPLOT RADAR FUNCTION
(SPIRAL STYLE)
create_ggplot_radar <- function(data, title_text) {
# Threshold line for p < 0.05 (-log10(0.05) ~= 1.3)
threshold_val <- -log10(0.05)
# Max limit for the plot
max_val <- max(data$log_pval) * 1.2
ggplot(data, aes(x = plot_label, y = log_pval)) +
# A) The Shaded Area (Spiral Effect)
geom_area(aes(group = 1), fill = "#DDA0DD", alpha = 0.4) +
# B) The Line Border
geom_line(aes(group = 1), color = "#800080", linewidth = 1) +
# C) The Points (Colored by Up/Down)
geom_point(aes(color = direction), size = 3) +
scale_color_manual(values = c("Up" = "purple", "Down" = "blue")) +
# D) The "P < 0.05" Central Threshold Circle
geom_hline(yintercept = threshold_val, linetype = "dashed", color = "black", linewidth = 0.8) +
annotate("text", x = 1, y = threshold_val, label = "p < 0.05",
color = "black", fontface = "bold", size = 3, vjust = -1) +
# E) Radial Transformation
coord_polar(start = 0, clip = "off") +
# F) Theme Adjustments
theme_minimal() +
labs(
title = title_text,
x = NULL,
y = "-log10(p-value)",
color = "Regulation"
) +
scale_y_continuous(limits = c(0, max_val)) +
theme(
axis.text.x = element_text(size = 9, face = "bold", color = "black"),
axis.text.y = element_text(size = 8, color = "gray50"),
panel.grid.major = element_line(color = "gray85", linetype = "dotted"),
plot.title = element_text(hjust = 0.5, face = "bold", size = 14),
legend.position = "bottom",
plot.margin = margin(t = 20, r = 20, b = 20, l = 20)
)
}
LS0tCnRpdGxlOiAiR0dQTE9UMiBSQURBUiBQTE9UUyAtIEFMTCBQQVRIV0FZUyAoU2lnbmlmaWNhbnQgT25seSkiCmF1dGhvcjogTmFzaXIgTWFobW9vZCBBYmJhc2kKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgdGhlbWU6IGpvdXJuYWwKLS0tCgoKIyBsb2FkIGxpYnJhcmllcwpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDdXN0b20gR1NFQSBQbG90dGluZyBTY3JpcHQgLSBTUElSQUwgUkFEQVIgQ0hBUlQKIyBGZWF0dXJlczoKIyAtIFJlcGxpY2F0ZXMgdGhlICJTcGlyYWwiIGxvb2sgZnJvbSByZWZlcmVuY2UKIyAtIFNvcnRzIHBhdGh3YXlzIGJ5IHNpZ25pZmljYW5jZSBmb3Igc21vb3RoIHJpc2UKIyAtIFBsb3RzIC1sb2cxMChwLXZhbHVlKSBvbiByYWRpYWwgYXhpcwojIC0gR2VuZXJhdGVzIDQgcGxvdHM6IFdpdGggYW5kIFdpdGhvdXQgUHJvbGlmZXJhdGlvbiAoMiB2ZXJzaW9ucyBlYWNoKQojIC0gSW5jbHVkZXMgRGF0YWJhc2UgUHJlZml4IChIQUxMTUFSS18sIEtFR0dfLCBldGMuKQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZmdzZWEpCmxpYnJhcnkobXNpZ2RicikKbGlicmFyeShlbnJpY2hwbG90KQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHNjYWxlcykKCmBgYAoKCiMgTE9BRCBEQVRBICYgU0VUVVAgCmBgYHtyIGxvYWRTZXVyYXR9CiMgTWFrZSBzdXJlIHRoZSBmaWxlIG5hbWUgbWF0Y2hlcyBleGFjdGx5IHdoYXQgeW91IGhhdmUKZmdfYWxsIDwtIHJlYWQuY3N2KCIuLi8uLi8uLi8uLi9mZ3NlYV9hbGxfcmVzdWx0cy5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgpjYXQoIkxvYWRlZCIsIG5yb3coZmdfYWxsKSwgInBhdGh3YXlzIGZyb20gZmdzZWFfYWxsX3Jlc3VsdHMuY3N2XG4iKQoKIyBTVFJJQ1QgRVhDTFVTSU9OIExJU1QgKFVwZGF0ZWQpCnByb2xpZl90ZXJtcyA8LSBjKAogICJDRUxMX0NZQ0xFIiwgIk1JVE9USUMiLCAiRzJNIiwgIkUyRiIsICJTUElORExFIiwgCiAgIkNIUk9NT1NPTUUiLCAiRE5BX1JFUExJQ0FUSU9OIiwgIk5VQ0xFQVJfRElWSVNJT04iLAogICJPUkdBTkVMTEVfRklTU0lPTiIsICJLSU5FVE9DSE9SRSIsICJDRU5UUk9TT01FIiwKICAiUkVQTElDQVRJT04iLCAiU0VHUkVHQVRJT04iLCAiRElWSVNJT04iLCAiTV9QSEFTRSIsIAogICJLSU5FU0lOUyIsICJNRUlPU0lTIiwgIk9PQ1lURSIsIAogICJNSUNST1RVQlVMRSIsICJDWVRPU0tFTEVUT04iLCAiVFJBRkZJQyIsICJHT0xHSSIsICJDWUNMSU4iLAogICJSRUNPTUJJTkFUSU9OIiwgIlJFUEFJUiIsICJSRVBMSUNBVElWRSIsICJQT0xPX0xJS0UiLCAiQ0hFQ0tQT0lOVFMiLAogICJUUkFOU0NSSVBUSU9OIiwgIlNfUEhBU0UiLCAiQU5BUEhBU0UiLCAiVEVMT1BIQVNFIiwgIlBST1BIQVNFIiwgCiAgIkNZVE9LSU5FU0lTIiwgIlNQSU5ETEVfQVNTRU1CTFkiLCAiU1BJTkRMRV9DSEVDS1BPSU5UIiwgCiAgIk1JVE9USUNfU1BJTkRMRSIsICJNSVRPVElDX0NIRUNLUE9JTlQiLCAiTUlUT1RJQ19HMSIsIAogICJNSVRPVElDX1NfUEhBU0UiLCAiTUlUT1RJQ19HMk0iCikKYGBgCgoKIyBQUkVQQVJFIERBVEEgRk9SIFJBREFSIFBMT1QgKFdJVEggREFUQUJBU0UgUFJFRklYKS0tVjEKYGBge3IsIGZpZy5oZWlnaHQ9IDYsIGZpZy53aWR0aD0gMTB9CnByZXBhcmVfcmFkYXJfZGF0YV92MSA8LSBmdW5jdGlvbihmZ190YmwsIHRvcE5fcGVyX2RiID0gMywgZXhjbHVkZV9wcm9saWYgPSBUUlVFKSB7CgogICMgQS4gRmlsdGVyIFByb2xpZmVyYXRpb24gKGlmIHJlcXVlc3RlZCkKICBpZiAoZXhjbHVkZV9wcm9saWYpIHsKICAgIGZnX3RibCA8LSBmZ190YmwgJT4lIGZpbHRlcighZ3JlcGwocGFzdGUocHJvbGlmX3Rlcm1zLCBjb2xsYXBzZSA9ICJ8IiksIHBhdGh3YXksIGlnbm9yZS5jYXNlID0gVFJVRSkpCiAgfQoKICAjIEIuIEZpbHRlciBmb3Igc2lnbmlmaWNhbnQgcGF0aHdheXMgb25seQogIGZnX3RibCA8LSBmZ190YmwgJT4lIGZpbHRlcihwdmFsIDwgMC4wNSkKCiAgIyBDLiBTZWxlY3QgVG9wIE4gcGF0aHdheXMgUEVSIERBVEFCQVNFIChiYWxhbmNlZCkKICB0b3BfcGF0aHMgPC0gYmluZF9yb3dzKAogICAgZmdfdGJsICU+JSBmaWx0ZXIoZGF0YXNldCA9PSAiaGFsbG1hcmsiKSAlPiUgYXJyYW5nZShwdmFsKSAlPiUgc2xpY2VfaGVhZChuID0gdG9wTl9wZXJfZGIpLAogICAgZmdfdGJsICU+JSBmaWx0ZXIoZGF0YXNldCA9PSAia2VnZyIpICU+JSBhcnJhbmdlKHB2YWwpICU+JSBzbGljZV9oZWFkKG4gPSB0b3BOX3Blcl9kYiksCiAgICBmZ190YmwgJT4lIGZpbHRlcihkYXRhc2V0ID09ICJyZWFjdG9tZSIpICU+JSBhcnJhbmdlKHB2YWwpICU+JSBzbGljZV9oZWFkKG4gPSB0b3BOX3Blcl9kYiksCiAgICBmZ190YmwgJT4lIGZpbHRlcihkYXRhc2V0ID09ICJnb19icCIpICU+JSBhcnJhbmdlKHB2YWwpICU+JSBzbGljZV9oZWFkKG4gPSB0b3BOX3Blcl9kYikKICApICU+JQogICAgbXV0YXRlKAogICAgICBsb2dfcHZhbCA9IC1sb2cxMChwdmFsICsgMWUtMTUpLCAjIFRyYW5zZm9ybSBQLXZhbHVlCiAgICAgICMgQWRkIERhdGFiYXNlIFByZWZpeAogICAgICBkYl9wcmVmaXggPSBjYXNlX3doZW4oCiAgICAgICAgZGF0YXNldCA9PSAiaGFsbG1hcmsiIH4gIkhBTExNQVJLIiwKICAgICAgICBkYXRhc2V0ID09ICJrZWdnIiB+ICJLRUdHIiwKICAgICAgICBkYXRhc2V0ID09ICJyZWFjdG9tZSIgfiAiUkVBQ1RPTUUiLAogICAgICAgIGRhdGFzZXQgPT0gImdvX2JwIiB+ICJHT0JQIgogICAgICApLAogICAgICAjIENsZWFuIHBhdGh3YXkgbmFtZQogICAgICBjbGVhbl9wYXRod2F5ID0gZ3N1YigiXkhBTExNQVJLX3xeS0VHR198XlJFQUNUT01FX3xeR09CUF8iLCAiIiwgcGF0aHdheSksCiAgICAgIGNsZWFuX3BhdGh3YXkgPSBzdHJfdHJ1bmMoY2xlYW5fcGF0aHdheSwgMjUpLAogICAgICAjIENvbWJpbmU6IERCX1BBVEhXQVkgZm9ybWF0CiAgICAgIHBsb3RfbGFiZWwgPSBwYXN0ZTAoZGJfcHJlZml4LCAiXyIsIGNsZWFuX3BhdGh3YXkpLAogICAgICBkaXJlY3Rpb24gPSBpZmVsc2UoTkVTID4gMCwgIlVwIiwgIkRvd24iKQogICAgKSAlPiUKICAgICMgRC4gTWFrZSB1bmlxdWUgdG8gYXZvaWQgZHVwbGljYXRlcwogICAgbXV0YXRlKHBsb3RfbGFiZWwgPSBtYWtlLnVuaXF1ZShhcy5jaGFyYWN0ZXIocGxvdF9sYWJlbCksIHNlcCA9ICIgIikpICU+JQogICAgIyBFLiBTb3J0IGJ5IGxvZ19wdmFsIGZvciBzbW9vdGggc3BpcmFsCiAgICBhcnJhbmdlKGxvZ19wdmFsKSAlPiUKICAgIG11dGF0ZShwbG90X2xhYmVsID0gZmFjdG9yKHBsb3RfbGFiZWwsIGxldmVscyA9IHBsb3RfbGFiZWwpKQoKICByZXR1cm4odG9wX3BhdGhzKQp9CmBgYAoKCiMgUFJFUEFSRSBEQVRBIEZPUiBSQURBUiBQTE9UIChCQUxBTkNFRCBVUC9ET1dOIEZST00gRUFDSCBEQVRBQkFTRSktVjIKYGBge3IsIGZpZy5oZWlnaHQ9IDYsIGZpZy53aWR0aD0gOH0KcHJlcGFyZV9yYWRhcl9kYXRhX3YyIDwtIGZ1bmN0aW9uKGZnX3RibCwgdG9wTl9wZXJfZGIgPSAzLCBleGNsdWRlX3Byb2xpZiA9IFRSVUUpIHsKCiAgIyBBLiBGaWx0ZXIgUHJvbGlmZXJhdGlvbiAoaWYgcmVxdWVzdGVkKQogIGlmIChleGNsdWRlX3Byb2xpZikgewogICAgZmdfdGJsIDwtIGZnX3RibCAlPiUgZmlsdGVyKCFncmVwbChwYXN0ZShwcm9saWZfdGVybXMsIGNvbGxhcHNlID0gInwiKSwgcGF0aHdheSwgaWdub3JlLmNhc2UgPSBUUlVFKSkKICB9CgogICMgQi4gRmlsdGVyIGZvciBzaWduaWZpY2FudCBwYXRod2F5cyBvbmx5CiAgZmdfdGJsIDwtIGZnX3RibCAlPiUgZmlsdGVyKHB2YWwgPCAwLjA1KQoKICAjIEMuIFNlbGVjdCBUb3AgTiBVUFJFR1VMQVRFRCBhbmQgRE9XTlJFR1VMQVRFRCBwYXRod2F5cyBQRVIgREFUQUJBU0UKICB0b3BfcGF0aHMgPC0gYmluZF9yb3dzKAogICAgIyBIYWxsbWFyawogICAgZmdfdGJsICU+JSBmaWx0ZXIoZGF0YXNldCA9PSAiaGFsbG1hcmsiLCBORVMgPiAwKSAlPiUgYXJyYW5nZShwdmFsKSAlPiUgc2xpY2VfaGVhZChuID0gY2VpbGluZyh0b3BOX3Blcl9kYi8yKSksCiAgICBmZ190YmwgJT4lIGZpbHRlcihkYXRhc2V0ID09ICJoYWxsbWFyayIsIE5FUyA8IDApICU+JSBhcnJhbmdlKHB2YWwpICU+JSBzbGljZV9oZWFkKG4gPSBmbG9vcih0b3BOX3Blcl9kYi8yKSksCiAgICAKICAgICMgS0VHRwogICAgZmdfdGJsICU+JSBmaWx0ZXIoZGF0YXNldCA9PSAia2VnZyIsIE5FUyA+IDApICU+JSBhcnJhbmdlKHB2YWwpICU+JSBzbGljZV9oZWFkKG4gPSBjZWlsaW5nKHRvcE5fcGVyX2RiLzIpKSwKICAgIGZnX3RibCAlPiUgZmlsdGVyKGRhdGFzZXQgPT0gImtlZ2ciLCBORVMgPCAwKSAlPiUgYXJyYW5nZShwdmFsKSAlPiUgc2xpY2VfaGVhZChuID0gZmxvb3IodG9wTl9wZXJfZGIvMikpLAogICAgCiAgICAjIFJlYWN0b21lCiAgICBmZ190YmwgJT4lIGZpbHRlcihkYXRhc2V0ID09ICJyZWFjdG9tZSIsIE5FUyA+IDApICU+JSBhcnJhbmdlKHB2YWwpICU+JSBzbGljZV9oZWFkKG4gPSBjZWlsaW5nKHRvcE5fcGVyX2RiLzIpKSwKICAgIGZnX3RibCAlPiUgZmlsdGVyKGRhdGFzZXQgPT0gInJlYWN0b21lIiwgTkVTIDwgMCkgJT4lIGFycmFuZ2UocHZhbCkgJT4lIHNsaWNlX2hlYWQobiA9IGZsb29yKHRvcE5fcGVyX2RiLzIpKSwKICAgIAogICAgIyBHTzpCUAogICAgZmdfdGJsICU+JSBmaWx0ZXIoZGF0YXNldCA9PSAiZ29fYnAiLCBORVMgPiAwKSAlPiUgYXJyYW5nZShwdmFsKSAlPiUgc2xpY2VfaGVhZChuID0gY2VpbGluZyh0b3BOX3Blcl9kYi8yKSksCiAgICBmZ190YmwgJT4lIGZpbHRlcihkYXRhc2V0ID09ICJnb19icCIsIE5FUyA8IDApICU+JSBhcnJhbmdlKHB2YWwpICU+JSBzbGljZV9oZWFkKG4gPSBmbG9vcih0b3BOX3Blcl9kYi8yKSkKICApICU+JQogICAgbXV0YXRlKAogICAgICBsb2dfcHZhbCA9IC1sb2cxMChwdmFsICsgMWUtMTUpLAogICAgICBkYl9wcmVmaXggPSBjYXNlX3doZW4oCiAgICAgICAgZGF0YXNldCA9PSAiaGFsbG1hcmsiIH4gIkhBTExNQVJLIiwKICAgICAgICBkYXRhc2V0ID09ICJrZWdnIiB+ICJLRUdHIiwKICAgICAgICBkYXRhc2V0ID09ICJyZWFjdG9tZSIgfiAiUkVBQ1RPTUUiLAogICAgICAgIGRhdGFzZXQgPT0gImdvX2JwIiB+ICJHT0JQIgogICAgICApLAogICAgICBjbGVhbl9wYXRod2F5ID0gZ3N1YigiXkhBTExNQVJLX3xeS0VHR198XlJFQUNUT01FX3xeR09CUF8iLCAiIiwgcGF0aHdheSksCiAgICAgIGNsZWFuX3BhdGh3YXkgPSBzdHJfdHJ1bmMoY2xlYW5fcGF0aHdheSwgMjUpLAogICAgICBwbG90X2xhYmVsID0gcGFzdGUwKGRiX3ByZWZpeCwgIl8iLCBjbGVhbl9wYXRod2F5KSwKICAgICAgZGlyZWN0aW9uID0gaWZlbHNlKE5FUyA+IDAsICJVcCIsICJEb3duIikKICAgICkgJT4lCiAgICBtdXRhdGUocGxvdF9sYWJlbCA9IG1ha2UudW5pcXVlKGFzLmNoYXJhY3RlcihwbG90X2xhYmVsKSwgc2VwID0gIiAiKSkgJT4lCiAgICBhcnJhbmdlKGxvZ19wdmFsKSAlPiUKICAgIG11dGF0ZShwbG90X2xhYmVsID0gZmFjdG9yKHBsb3RfbGFiZWwsIGxldmVscyA9IHBsb3RfbGFiZWwpKQoKICByZXR1cm4odG9wX3BhdGhzKQp9CmBgYAoKCgojIyBHR1BMT1QgUkFEQVIgRlVOQ1RJT04gKFNQSVJBTCBTVFlMRSkKYGBge3IsIGZpZy5oZWlnaHQ9IDYsIGZpZy53aWR0aD0gMTB9CmNyZWF0ZV9nZ3Bsb3RfcmFkYXIgPC0gZnVuY3Rpb24oZGF0YSwgdGl0bGVfdGV4dCkgewoKICAjIFRocmVzaG9sZCBsaW5lIGZvciBwIDwgMC4wNSAoLWxvZzEwKDAuMDUpIH49IDEuMykKICB0aHJlc2hvbGRfdmFsIDwtIC1sb2cxMCgwLjA1KQoKICAjIE1heCBsaW1pdCBmb3IgdGhlIHBsb3QKICBtYXhfdmFsIDwtIG1heChkYXRhJGxvZ19wdmFsKSAqIDEuMgoKICBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBwbG90X2xhYmVsLCB5ID0gbG9nX3B2YWwpKSArCgogICAgIyBBKSBUaGUgU2hhZGVkIEFyZWEgKFNwaXJhbCBFZmZlY3QpCiAgICBnZW9tX2FyZWEoYWVzKGdyb3VwID0gMSksIGZpbGwgPSAiI0REQTBERCIsIGFscGhhID0gMC40KSArCiAgICAKICAgICMgQikgVGhlIExpbmUgQm9yZGVyCiAgICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gMSksIGNvbG9yID0gIiM4MDAwODAiLCBsaW5ld2lkdGggPSAxKSArCgogICAgIyBDKSBUaGUgUG9pbnRzIChDb2xvcmVkIGJ5IFVwL0Rvd24pCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGRpcmVjdGlvbiksIHNpemUgPSAzKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiVXAiID0gInB1cnBsZSIsICJEb3duIiA9ICJibHVlIikpICsKCiAgICAjIEQpIFRoZSAiUCA8IDAuMDUiIENlbnRyYWwgVGhyZXNob2xkIENpcmNsZQogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gdGhyZXNob2xkX3ZhbCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAwLjgpICsKICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEsIHkgPSB0aHJlc2hvbGRfdmFsLCBsYWJlbCA9ICJwIDwgMC4wNSIsIAogICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBmb250ZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDMsIHZqdXN0ID0gLTEpICsKCiAgICAjIEUpIFJhZGlhbCBUcmFuc2Zvcm1hdGlvbgogICAgY29vcmRfcG9sYXIoc3RhcnQgPSAwLCBjbGlwID0gIm9mZiIpICsKCiAgICAjIEYpIFRoZW1lIEFkanVzdG1lbnRzCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgbGFicygKICAgICAgdGl0bGUgPSB0aXRsZV90ZXh0LAogICAgICB4ID0gTlVMTCwKICAgICAgeSA9ICItbG9nMTAocC12YWx1ZSkiLAogICAgICBjb2xvciA9ICJSZWd1bGF0aW9uIgogICAgKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCBtYXhfdmFsKSkgKwogICAgdGhlbWUoCiAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LCBmYWNlID0gImJvbGQiLCBjb2xvciA9ICJibGFjayIpLAogICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3IgPSAiZ3JheTUwIiksCiAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheTg1IiwgbGluZXR5cGUgPSAiZG90dGVkIiksCiAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCksCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbih0ID0gMjAsIHIgPSAyMCwgYiA9IDIwLCBsID0gMjApCiAgICApCn0KCmBgYAoKCiMjIEdFTkVSQVRFIEZJR1VSRSAxIChWZXJzaW9uIDEgLSBObyBQcm9saWZlcmF0aW9uKQpgYGB7ciwgZmlnLmhlaWdodD0gMTgsIGZpZy53aWR0aD0gMTh9CnJhZGFyX2RmX2ZpZzEgPC0gcHJlcGFyZV9yYWRhcl9kYXRhX3YxKGZnX2FsbCwgdG9wTl9wZXJfZGIgPSA1LCBleGNsdWRlX3Byb2xpZiA9IFRSVUUpCnAxIDwtIGNyZWF0ZV9nZ3Bsb3RfcmFkYXIocmFkYXJfZGZfZmlnMSwgIlRvcCBFbnJpY2hlZCBQYXRod2F5cyAoTm9uLVByb2xpZmVyYXRpdmUpIikKCmdnc2F2ZSgiRmlnMV9SYWRhcl9Ob1Byb2xpZl9BbGxTaWcucG5nIiwgcDEsIHdpZHRoID0gMTgsIGhlaWdodCA9IDE4LCBkcGkgPSAzMDApCmdnc2F2ZSgiRmlnMV9SYWRhcl9Ob1Byb2xpZl9BbGxTaWcucGRmIiwgcDEsIHdpZHRoID0gMTgsIGhlaWdodCA9IDE4KQoKcHJpbnQoIuKchSBDcmVhdGVkIEZpZ3VyZSAxOiBGaWcxX1JhZGFyX05vUHJvbGlmX0FsbFNpZy5wbmciKQpwcmludChwMSkKCnJhZGFyX2RmX2ZpZzIgPC0gcHJlcGFyZV9yYWRhcl9kYXRhX3YxKGZnX2FsbCwgdG9wTl9wZXJfZGIgPSA1LCBleGNsdWRlX3Byb2xpZiA9IEZBTFNFKQpwMiA8LSBjcmVhdGVfZ2dwbG90X3JhZGFyKHJhZGFyX2RmX2ZpZzIsICJUb3AgRW5yaWNoZWQgUGF0aHdheXMgKEluY2x1ZGluZyBQcm9saWZlcmF0aW9uKSIpCgpnZ3NhdmUoIkZpZzJfUmFkYXJfV2l0aFByb2xpZl9BbGxTaWcucG5nIiwgcDIsIHdpZHRoID0gMTgsIGhlaWdodCA9IDE4LCBkcGkgPSAzMDApCmdnc2F2ZSgiRmlnMl9SYWRhcl9XaXRoUHJvbGlmX0FsbFNpZy5wZGYiLCBwMiwgd2lkdGggPSAxOCwgaGVpZ2h0ID0gMTgpCgpwcmludCgi4pyFIENyZWF0ZWQgRmlndXJlIDI6IEZpZzJfUmFkYXJfV2l0aFByb2xpZl9BbGxTaWcucG5nIikKcHJpbnQocDIpCgpgYGAKCiMjIEdFTkVSQVRFIEZJR1VSRSAzIChWZXJzaW9uIDIgLSBObyBQcm9saWZlcmF0aW9uLCBCYWxhbmNlZCBVcC9Eb3duKQpgYGB7ciwgZmlnLmhlaWdodD0gMTgsIGZpZy53aWR0aD0gMTh9CnJhZGFyX2RmX2ZpZzMgPC0gcHJlcGFyZV9yYWRhcl9kYXRhX3YyKGZnX2FsbCwgdG9wTl9wZXJfZGIgPSA1LCBleGNsdWRlX3Byb2xpZiA9IFRSVUUpCnAzIDwtIGNyZWF0ZV9nZ3Bsb3RfcmFkYXIocmFkYXJfZGZfZmlnMywgIlRvcCBFbnJpY2hlZCBQYXRod2F5cyAoTm9uLVByb2xpZmVyYXRpdmUsIEJhbGFuY2VkKSIpCgpnZ3NhdmUoIkZpZzNfUmFkYXJfTm9Qcm9saWZfQmFsYW5jZWQucG5nIiwgcDMsIHdpZHRoID0gMTgsIGhlaWdodCA9IDE4LCBkcGkgPSAzMDApCmdnc2F2ZSgiRmlnM19SYWRhcl9Ob1Byb2xpZl9CYWxhbmNlZC5wZGYiLCBwMywgd2lkdGggPSAxOCwgaGVpZ2h0ID0gMTgpCgpwcmludCgi4pyFIENyZWF0ZWQgRmlndXJlIDM6IEZpZzNfUmFkYXJfTm9Qcm9saWZfQmFsYW5jZWQucG5nIikKcHJpbnQocDMpCgpyYWRhcl9kZl9maWc0IDwtIHByZXBhcmVfcmFkYXJfZGF0YV92MihmZ19hbGwsIHRvcE5fcGVyX2RiID0gNSwgZXhjbHVkZV9wcm9saWYgPSBGQUxTRSkKcDQgPC0gY3JlYXRlX2dncGxvdF9yYWRhcihyYWRhcl9kZl9maWc0LCAiVG9wIEVucmljaGVkIFBhdGh3YXlzIChJbmNsdWRpbmcgUHJvbGlmZXJhdGlvbiwgQmFsYW5jZWQpIikKCmdnc2F2ZSgiRmlnNF9SYWRhcl9XaXRoUHJvbGlmX0JhbGFuY2VkLnBuZyIsIHA0LCB3aWR0aCA9IDE4LCBoZWlnaHQgPSAxOCwgZHBpID0gMzAwKQpnZ3NhdmUoIkZpZzRfUmFkYXJfV2l0aFByb2xpZl9CYWxhbmNlZC5wZGYiLCBwNCwgd2lkdGggPSAxOCwgaGVpZ2h0ID0gMTgpCgpwcmludCgi4pyFIENyZWF0ZWQgRmlndXJlIDQ6IEZpZzRfUmFkYXJfV2l0aFByb2xpZl9CYWxhbmNlZC5wbmciKQpwcmludChwNCkKCmBgYAoKCgoK