1. load libraries
2. Read DE Files for Each Cluster
cluster_files <- list(
"0" = "../Updated_DE_Cluster_0_with_MeanExpr.csv",
"1" = "../Updated_DE_Cluster_1_with_MeanExpr.csv",
"2" = "../Updated_DE_Cluster_2_with_MeanExpr.csv",
"4" = "../Updated_DE_Cluster_4_with_MeanExpr.csv",
"5" = "../Updated_DE_Cluster_5_with_MeanExpr.csv",
"6" = "../Updated_DE_Cluster_6_with_MeanExpr.csv",
"7" = "../Updated_DE_Cluster_7_with_MeanExpr.csv",
"8" = "../Updated_DE_Cluster_8_with_MeanExpr.csv",
"9" = "../Updated_DE_Cluster_9_with_MeanExpr.csv",
"11" = "../Updated_DE_Cluster_11_with_MeanExpr.csv",
"12" = "../Updated_DE_Cluster_12_with_MeanExpr.csv",
"13" = "../Updated_DE_Cluster_13_with_MeanExpr.csv"
)
4. Load Gene Sets from MSigDB
library(msigdbr)
# Hallmark (no subcollection needed)
hallmark_sets <- msigdbr(species = "Homo sapiens", category = "H") %>%
split(x = .$gene_symbol, f = .$gs_name)
Avis : The `category` argument of `msigdbr()` is deprecated as of msigdbr 10.0.0.
Please use the `collection` argument instead.
# KEGG legacy pathways
kegg_sets <- msigdbr(
species = "Homo sapiens",
category = "C2",
subcollection = "CP:KEGG_LEGACY"
) %>%
split(x = .$gene_symbol, f = .$gs_name)
# Reactome pathways
reactome_sets <- msigdbr(
species = "Homo sapiens",
category = "C2",
subcollection = "CP:REACTOME"
) %>%
split(x = .$gene_symbol, f = .$gs_name)
# Immunologic signatures
c7_sets <- msigdbr(species = "Homo sapiens", category = "C7") %>%
split(x = .$gene_symbol, f = .$gs_name)
# GO Biological Process, CC, MF
c5_bp <- msigdbr(species = "Homo sapiens", category = "C5", subcollection = "BP") %>%
split(x = .$gene_symbol, f = .$gs_name)
# Combine all sets
msigdb_sets <- list(
Hallmark = hallmark_sets,
KEGG = kegg_sets,
Reactome = reactome_sets,
C7 = c7_sets,
GO_BP = c5_bp
)
4. Run GSEA per Cluster using DE Files
gsea_all <- list()
for (cluster_id in names(cluster_files)) {
# Load DE result for the cluster
de_df <- read.csv(cluster_files[[cluster_id]])
# Optional: filter genes with NA or very low avg_log2FC (if desired)
de_df <- de_df %>% filter(!is.na(avg_log2FC))
# Use only avg_log2FC as ranking metric
ranked_genes <- de_df$avg_log2FC
names(ranked_genes) <- de_df$gene
ranked_genes <- sort(ranked_genes, decreasing = TRUE)
# Run GSEA for each MSigDB collection
for (db_name in names(msigdb_sets)) {
gsea_res <- fgsea(pathways = msigdb_sets[[db_name]],
stats = ranked_genes,
minSize = 10,
maxSize = 300,
nperm = 10000)
if (nrow(gsea_res) > 0) {
gsea_res$cluster <- cluster_id
gsea_res$database <- db_name
gsea_all[[paste0("Cluster_", cluster_id, "_", db_name)]] <- gsea_res
}
}
}
Combine and Save All GSEA Results
library(purrr)
library(dplyr)
# Combine all GSEA results from list into a single dataframe
gsea_df <- bind_rows(gsea_all)
# Flatten the leadingEdge column (list to character)
gsea_df_flat <- gsea_df %>%
mutate(leadingEdge = map_chr(leadingEdge, ~ paste(.x, collapse = ";")))
Plot Top GSEA Pathways per Cluster and Collection
top_terms <- gsea_df %>%
filter(padj < 0.05) %>%
group_by(cluster, database) %>%
top_n(3, wt = NES) %>%
ungroup()
top_terms$pathway <- gsub("GO_|REACTOME_|KEGG_", "", top_terms$pathway)
top_terms$pathway <- gsub("_", " ", top_terms$pathway)
library(stringr)
top_terms$pathway <- str_to_title(top_terms$pathway)
ggplot(top_terms, aes(x = NES, y = fct_reorder(pathway, NES), fill = padj)) +
geom_col(show.legend = FALSE) +
facet_grid(cluster ~ database, scales = "free_y", space = "free_y") +
scale_fill_viridis_c(option = "D", direction = -1) +
labs(title = "Top GSEA Pathways per Cluster",
x = "Normalized Enrichment Score (NES)",
y = "Pathway") +
theme_minimal(base_size = 11) +
theme(strip.text = element_text(size = 10, face = "bold"))

NA
NA
NA
NA
NA
Plot Top GSEA Pathways per Cluster and Collection2
library(ggplot2)
library(ggforce)
library(forcats)
library(dplyr)
library(stringr)
# Prepare data (reuse your `top_terms` and enhance labels)
top_terms <- gsea_df %>%
filter(padj < 0.05) %>%
group_by(cluster, database) %>%
top_n(3, wt = NES) %>%
ungroup()
# Clean pathway names
top_terms$pathway <- gsub("GO_|REACTOME_|KEGG_", "", top_terms$pathway)
top_terms$pathway <- gsub("_", " ", top_terms$pathway)
top_terms$pathway <- str_to_title(top_terms$pathway)
# Optional: shorten long pathway names for plot clarity
top_terms$pathway <- str_trunc(top_terms$pathway, width = 60)
# Plot
ggplot(top_terms, aes(x = fct_reorder(pathway, NES), y = NES, fill = database)) +
geom_col(width = 0.9) +
coord_flip() +
facet_grid(database ~ cluster, scales = "free_y", space = "free") +
geom_hline(yintercept = 0, linetype = "dashed", color = "gray50") +
labs(
x = "Pathway",
y = "Normalized Enrichment Score (NES)",
title = "Top GSEA Pathways per Cluster",
fill = "Database"
) +
scale_fill_brewer(palette = "Set2") +
theme_minimal(base_size = 16) +
theme(
strip.text = element_text(face = "bold", size = 16),
strip.background = element_rect(fill = "gray90", color = "black"),
strip.placement = "outside",
legend.position = "bottom",
axis.text.y = element_text(face = "bold", size = 13),
axis.text.x = element_text(size = 14),
panel.spacing = unit(1, "lines"),
panel.border = element_rect(color = "black", fill = NA, size = 1.5)
)

NA
NA
NA
Combine and Save All GSEA Results
library(dplyr)
library(purrr)
gsea_df <- bind_rows(gsea_all)
# Flatten leadingEdge column (list of gene names per pathway)
gsea_df <- gsea_df %>%
mutate(leadingEdge = map_chr(leadingEdge, ~ paste(.x, collapse = ";")))
# Now write full combined result
write.csv(gsea_df, "GSEA_MSigDB_Results_Clusterwise.csv", row.names = FALSE)
Updated Code: Create folder per cluster and save CSV inside
# Combine into single df and flatten leadingEdge
library(dplyr)
library(purrr)
gsea_df <- bind_rows(gsea_all) %>%
mutate(leadingEdge = map_chr(leadingEdge, ~ paste(.x, collapse = ";")))
# Split by cluster
gsea_by_cluster <- split(gsea_df, gsea_df$cluster)
# Write one file per cluster inside its own folder
for (cluster_id in names(gsea_by_cluster)) {
folder_name <- paste0("Cluster_", cluster_id)
# Create folder if it doesn't exist
if (!dir.exists(folder_name)) {
dir.create(folder_name)
}
# Write CSV inside the folder
file_path <- file.path(folder_name, paste0("GSEA_Cluster_", cluster_id, ".csv"))
write.csv(gsea_by_cluster[[cluster_id]], file = file_path, row.names = FALSE)
}
LS0tCnRpdGxlOiAiZmdzZWEgQ2x1c3RlciB2cyBOb3JtYWwgQ29udHJvbC1ERS1OZXdVTUFQIgphdXRob3I6ICJOYXNpciBNYWhtb29kIEFiYmFzaSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2NvbGxhcHNlZDogeWVzCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKLS0tCgoKIyAxLiBsb2FkIGxpYnJhcmllcwpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZmdzZWEpCmxpYnJhcnkobXNpZ2RicikKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KHB1cnJyKQoKICAKYGBgCgojIDIuIFJlYWQgREUgRmlsZXMgZm9yIEVhY2ggQ2x1c3RlcgpgYGB7ciBkYXRhMSwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9CgpjbHVzdGVyX2ZpbGVzIDwtIGxpc3QoCiAgIjAiID0gIi4uL1VwZGF0ZWRfREVfQ2x1c3Rlcl8wX3dpdGhfTWVhbkV4cHIuY3N2IiwKICAiMSIgPSAiLi4vVXBkYXRlZF9ERV9DbHVzdGVyXzFfd2l0aF9NZWFuRXhwci5jc3YiLAogICIyIiA9ICIuLi9VcGRhdGVkX0RFX0NsdXN0ZXJfMl93aXRoX01lYW5FeHByLmNzdiIsCiAgIjQiID0gIi4uL1VwZGF0ZWRfREVfQ2x1c3Rlcl80X3dpdGhfTWVhbkV4cHIuY3N2IiwKICAiNSIgPSAiLi4vVXBkYXRlZF9ERV9DbHVzdGVyXzVfd2l0aF9NZWFuRXhwci5jc3YiLAogICI2IiA9ICIuLi9VcGRhdGVkX0RFX0NsdXN0ZXJfNl93aXRoX01lYW5FeHByLmNzdiIsCiAgIjciID0gIi4uL1VwZGF0ZWRfREVfQ2x1c3Rlcl83X3dpdGhfTWVhbkV4cHIuY3N2IiwKICAiOCIgPSAiLi4vVXBkYXRlZF9ERV9DbHVzdGVyXzhfd2l0aF9NZWFuRXhwci5jc3YiLAogICI5IiA9ICIuLi9VcGRhdGVkX0RFX0NsdXN0ZXJfOV93aXRoX01lYW5FeHByLmNzdiIsCiAgIjExIiA9ICIuLi9VcGRhdGVkX0RFX0NsdXN0ZXJfMTFfd2l0aF9NZWFuRXhwci5jc3YiLAogICIxMiIgPSAiLi4vVXBkYXRlZF9ERV9DbHVzdGVyXzEyX3dpdGhfTWVhbkV4cHIuY3N2IiwKICAiMTMiID0gIi4uL1VwZGF0ZWRfREVfQ2x1c3Rlcl8xM193aXRoX01lYW5FeHByLmNzdiIKKQoKCmBgYAoKCgoKIyA0LiAgIExvYWQgR2VuZSBTZXRzIGZyb20gTVNpZ0RCCmBgYHtyICwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9CmxpYnJhcnkobXNpZ2RicikKCiMgSGFsbG1hcmsgKG5vIHN1YmNvbGxlY3Rpb24gbmVlZGVkKQpoYWxsbWFya19zZXRzIDwtIG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiLCBjYXRlZ29yeSA9ICJIIikgJT4lCiAgc3BsaXQoeCA9IC4kZ2VuZV9zeW1ib2wsIGYgPSAuJGdzX25hbWUpCgojIEtFR0cgbGVnYWN5IHBhdGh3YXlzCmtlZ2dfc2V0cyA8LSBtc2lnZGJyKAogICAgc3BlY2llcyAgICAgID0gIkhvbW8gc2FwaWVucyIsCiAgICBjYXRlZ29yeSAgICAgPSAiQzIiLAogICAgc3ViY29sbGVjdGlvbiA9ICJDUDpLRUdHX0xFR0FDWSIKICApICU+JQogIHNwbGl0KHggPSAuJGdlbmVfc3ltYm9sLCBmID0gLiRnc19uYW1lKQoKIyBSZWFjdG9tZSBwYXRod2F5cwpyZWFjdG9tZV9zZXRzIDwtIG1zaWdkYnIoCiAgICBzcGVjaWVzICAgICAgPSAiSG9tbyBzYXBpZW5zIiwKICAgIGNhdGVnb3J5ICAgICA9ICJDMiIsCiAgICBzdWJjb2xsZWN0aW9uID0gIkNQOlJFQUNUT01FIgogICkgJT4lCiAgc3BsaXQoeCA9IC4kZ2VuZV9zeW1ib2wsIGYgPSAuJGdzX25hbWUpCgojIEltbXVub2xvZ2ljIHNpZ25hdHVyZXMKYzdfc2V0cyA8LSBtc2lnZGJyKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwgY2F0ZWdvcnkgPSAiQzciKSAlPiUKICBzcGxpdCh4ID0gLiRnZW5lX3N5bWJvbCwgZiA9IC4kZ3NfbmFtZSkKCiMgR08gQmlvbG9naWNhbCBQcm9jZXNzLCBDQywgTUYKYzVfYnAgPC0gbXNpZ2RicihzcGVjaWVzID0gIkhvbW8gc2FwaWVucyIsIGNhdGVnb3J5ID0gIkM1Iiwgc3ViY29sbGVjdGlvbiA9ICJCUCIpICU+JQogIHNwbGl0KHggPSAuJGdlbmVfc3ltYm9sLCBmID0gLiRnc19uYW1lKQoKCiMgQ29tYmluZSBhbGwgc2V0cwptc2lnZGJfc2V0cyA8LSBsaXN0KAogIEhhbGxtYXJrID0gaGFsbG1hcmtfc2V0cywKICBLRUdHICAgICA9IGtlZ2dfc2V0cywKICBSZWFjdG9tZSA9IHJlYWN0b21lX3NldHMsCiAgQzcgICAgICAgPSBjN19zZXRzLAogIEdPX0JQICAgID0gYzVfYnAKKQoKYGBgCgoKCiMgNC4gIFJ1biBHU0VBIHBlciBDbHVzdGVyIHVzaW5nIERFIEZpbGVzCmBgYHtyICwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9CgoKZ3NlYV9hbGwgPC0gbGlzdCgpCgpmb3IgKGNsdXN0ZXJfaWQgaW4gbmFtZXMoY2x1c3Rlcl9maWxlcykpIHsKICAKICAjIExvYWQgREUgcmVzdWx0IGZvciB0aGUgY2x1c3RlcgogIGRlX2RmIDwtIHJlYWQuY3N2KGNsdXN0ZXJfZmlsZXNbW2NsdXN0ZXJfaWRdXSkKICAKICAjIE9wdGlvbmFsOiBmaWx0ZXIgZ2VuZXMgd2l0aCBOQSBvciB2ZXJ5IGxvdyBhdmdfbG9nMkZDIChpZiBkZXNpcmVkKQogIGRlX2RmIDwtIGRlX2RmICU+JSBmaWx0ZXIoIWlzLm5hKGF2Z19sb2cyRkMpKQogIAogICMgVXNlIG9ubHkgYXZnX2xvZzJGQyBhcyByYW5raW5nIG1ldHJpYwogIHJhbmtlZF9nZW5lcyA8LSBkZV9kZiRhdmdfbG9nMkZDCiAgbmFtZXMocmFua2VkX2dlbmVzKSA8LSBkZV9kZiRnZW5lCiAgcmFua2VkX2dlbmVzIDwtIHNvcnQocmFua2VkX2dlbmVzLCBkZWNyZWFzaW5nID0gVFJVRSkKICAKICAjIFJ1biBHU0VBIGZvciBlYWNoIE1TaWdEQiBjb2xsZWN0aW9uCiAgZm9yIChkYl9uYW1lIGluIG5hbWVzKG1zaWdkYl9zZXRzKSkgewogICAgZ3NlYV9yZXMgPC0gZmdzZWEocGF0aHdheXMgPSBtc2lnZGJfc2V0c1tbZGJfbmFtZV1dLAogICAgICAgICAgICAgICAgICAgICAgc3RhdHMgPSByYW5rZWRfZ2VuZXMsCiAgICAgICAgICAgICAgICAgICAgICBtaW5TaXplID0gMTAsCiAgICAgICAgICAgICAgICAgICAgICBtYXhTaXplID0gMzAwLAogICAgICAgICAgICAgICAgICAgICAgbnBlcm0gPSAxMDAwMCkKICAgIAogICAgaWYgKG5yb3coZ3NlYV9yZXMpID4gMCkgewogICAgICBnc2VhX3JlcyRjbHVzdGVyIDwtIGNsdXN0ZXJfaWQKICAgICAgZ3NlYV9yZXMkZGF0YWJhc2UgPC0gZGJfbmFtZQogICAgICBnc2VhX2FsbFtbcGFzdGUwKCJDbHVzdGVyXyIsIGNsdXN0ZXJfaWQsICJfIiwgZGJfbmFtZSldXSA8LSBnc2VhX3JlcwogICAgfQogIH0KfQoKCgpgYGAKCgoKIyMgQ29tYmluZSBhbmQgU2F2ZSBBbGwgR1NFQSBSZXN1bHRzCmBgYHtyICwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9CmxpYnJhcnkocHVycnIpCmxpYnJhcnkoZHBseXIpCgojIENvbWJpbmUgYWxsIEdTRUEgcmVzdWx0cyBmcm9tIGxpc3QgaW50byBhIHNpbmdsZSBkYXRhZnJhbWUKZ3NlYV9kZiA8LSBiaW5kX3Jvd3MoZ3NlYV9hbGwpCgojIEZsYXR0ZW4gdGhlIGxlYWRpbmdFZGdlIGNvbHVtbiAobGlzdCB0byBjaGFyYWN0ZXIpCmdzZWFfZGZfZmxhdCA8LSBnc2VhX2RmICU+JSAKICBtdXRhdGUobGVhZGluZ0VkZ2UgPSBtYXBfY2hyKGxlYWRpbmdFZGdlLCB+IHBhc3RlKC54LCBjb2xsYXBzZSA9ICI7IikpKQoKCgoKYGBgCgoKCiMjICBQbG90IFRvcCBHU0VBIFBhdGh3YXlzIHBlciBDbHVzdGVyIGFuZCBDb2xsZWN0aW9uCmBgYHtyICwgZmlnLmhlaWdodD0yOCwgZmlnLndpZHRoPTIwfQp0b3BfdGVybXMgPC0gZ3NlYV9kZiAlPiUKICBmaWx0ZXIocGFkaiA8IDAuMDUpICU+JQogIGdyb3VwX2J5KGNsdXN0ZXIsIGRhdGFiYXNlKSAlPiUKICB0b3BfbigzLCB3dCA9IE5FUykgJT4lCiAgdW5ncm91cCgpCgp0b3BfdGVybXMkcGF0aHdheSA8LSBnc3ViKCJHT198UkVBQ1RPTUVffEtFR0dfIiwgIiIsIHRvcF90ZXJtcyRwYXRod2F5KQp0b3BfdGVybXMkcGF0aHdheSA8LSBnc3ViKCJfIiwgIiAiLCB0b3BfdGVybXMkcGF0aHdheSkKbGlicmFyeShzdHJpbmdyKQoKdG9wX3Rlcm1zJHBhdGh3YXkgPC0gc3RyX3RvX3RpdGxlKHRvcF90ZXJtcyRwYXRod2F5KQoKCmdncGxvdCh0b3BfdGVybXMsIGFlcyh4ID0gTkVTLCB5ID0gZmN0X3Jlb3JkZXIocGF0aHdheSwgTkVTKSwgZmlsbCA9IHBhZGopKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGZhY2V0X2dyaWQoY2x1c3RlciB+IGRhdGFiYXNlLCBzY2FsZXMgPSAiZnJlZV95Iiwgc3BhY2UgPSAiZnJlZV95IikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJEIiwgZGlyZWN0aW9uID0gLTEpICsKICBsYWJzKHRpdGxlID0gIlRvcCBHU0VBIFBhdGh3YXlzIHBlciBDbHVzdGVyIiwKICAgICAgIHggPSAiTm9ybWFsaXplZCBFbnJpY2htZW50IFNjb3JlIChORVMpIiwKICAgICAgIHkgPSAiUGF0aHdheSIpICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDExKSArCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpKQoKCgoKCmBgYAoKIyMgIFBsb3QgVG9wIEdTRUEgUGF0aHdheXMgcGVyIENsdXN0ZXIgYW5kIENvbGxlY3Rpb24yCmBgYHtyICwgZmlnLmhlaWdodD0yOCwgZmlnLndpZHRoPTIwfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dmb3JjZSkKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHN0cmluZ3IpCgojIFByZXBhcmUgZGF0YSAocmV1c2UgeW91ciBgdG9wX3Rlcm1zYCBhbmQgZW5oYW5jZSBsYWJlbHMpCnRvcF90ZXJtcyA8LSBnc2VhX2RmICU+JQogIGZpbHRlcihwYWRqIDwgMC4wNSkgJT4lCiAgZ3JvdXBfYnkoY2x1c3RlciwgZGF0YWJhc2UpICU+JQogIHRvcF9uKDMsIHd0ID0gTkVTKSAlPiUKICB1bmdyb3VwKCkKCiMgQ2xlYW4gcGF0aHdheSBuYW1lcwp0b3BfdGVybXMkcGF0aHdheSA8LSBnc3ViKCJHT198UkVBQ1RPTUVffEtFR0dfIiwgIiIsIHRvcF90ZXJtcyRwYXRod2F5KQp0b3BfdGVybXMkcGF0aHdheSA8LSBnc3ViKCJfIiwgIiAiLCB0b3BfdGVybXMkcGF0aHdheSkKdG9wX3Rlcm1zJHBhdGh3YXkgPC0gc3RyX3RvX3RpdGxlKHRvcF90ZXJtcyRwYXRod2F5KQoKIyBPcHRpb25hbDogc2hvcnRlbiBsb25nIHBhdGh3YXkgbmFtZXMgZm9yIHBsb3QgY2xhcml0eQp0b3BfdGVybXMkcGF0aHdheSA8LSBzdHJfdHJ1bmModG9wX3Rlcm1zJHBhdGh3YXksIHdpZHRoID0gNjApCgojIFBsb3QKZ2dwbG90KHRvcF90ZXJtcywgYWVzKHggPSBmY3RfcmVvcmRlcihwYXRod2F5LCBORVMpLCB5ID0gTkVTLCBmaWxsID0gZGF0YWJhc2UpKSArCiAgZ2VvbV9jb2wod2lkdGggPSAwLjkpICsKICBjb29yZF9mbGlwKCkgKwogIGZhY2V0X2dyaWQoZGF0YWJhc2UgfiBjbHVzdGVyLCBzY2FsZXMgPSAiZnJlZV95Iiwgc3BhY2UgPSAiZnJlZSIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmF5NTAiKSArCiAgbGFicygKICAgIHggPSAiUGF0aHdheSIsCiAgICB5ID0gIk5vcm1hbGl6ZWQgRW5yaWNobWVudCBTY29yZSAoTkVTKSIsCiAgICB0aXRsZSA9ICJUb3AgR1NFQSBQYXRod2F5cyBwZXIgQ2x1c3RlciIsCiAgICBmaWxsID0gIkRhdGFiYXNlIgogICkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDE2KSArCiAgdGhlbWUoCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNiksCiAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JheTkwIiwgY29sb3IgPSAiYmxhY2siKSwKICAgIHN0cmlwLnBsYWNlbWVudCA9ICJvdXRzaWRlIiwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEzKSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICBwYW5lbC5zcGFjaW5nID0gdW5pdCgxLCAibGluZXMiKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJibGFjayIsIGZpbGwgPSBOQSwgc2l6ZSA9IDEuNSkKICApCgoKCmBgYAojIyBDb21iaW5lIGFuZCBTYXZlIEFsbCBHU0VBIFJlc3VsdHMKYGBge3IgLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KbGlicmFyeShkcGx5cikKbGlicmFyeShwdXJycikKCmdzZWFfZGYgPC0gYmluZF9yb3dzKGdzZWFfYWxsKQoKIyBGbGF0dGVuIGxlYWRpbmdFZGdlIGNvbHVtbiAobGlzdCBvZiBnZW5lIG5hbWVzIHBlciBwYXRod2F5KQpnc2VhX2RmIDwtIGdzZWFfZGYgJT4lCiAgbXV0YXRlKGxlYWRpbmdFZGdlID0gbWFwX2NocihsZWFkaW5nRWRnZSwgfiBwYXN0ZSgueCwgY29sbGFwc2UgPSAiOyIpKSkKCiMgTm93IHdyaXRlIGZ1bGwgY29tYmluZWQgcmVzdWx0CndyaXRlLmNzdihnc2VhX2RmLCAiR1NFQV9NU2lnREJfUmVzdWx0c19DbHVzdGVyd2lzZS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCgoKYGBgCgojIyBVcGRhdGVkIENvZGU6IENyZWF0ZSBmb2xkZXIgcGVyIGNsdXN0ZXIgYW5kIHNhdmUgQ1NWIGluc2lkZQpgYGB7ciAsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEyfQojIENvbWJpbmUgaW50byBzaW5nbGUgZGYgYW5kIGZsYXR0ZW4gbGVhZGluZ0VkZ2UKbGlicmFyeShkcGx5cikKbGlicmFyeShwdXJycikKCmdzZWFfZGYgPC0gYmluZF9yb3dzKGdzZWFfYWxsKSAlPiUKICBtdXRhdGUobGVhZGluZ0VkZ2UgPSBtYXBfY2hyKGxlYWRpbmdFZGdlLCB+IHBhc3RlKC54LCBjb2xsYXBzZSA9ICI7IikpKQoKIyBTcGxpdCBieSBjbHVzdGVyCmdzZWFfYnlfY2x1c3RlciA8LSBzcGxpdChnc2VhX2RmLCBnc2VhX2RmJGNsdXN0ZXIpCgojIFdyaXRlIG9uZSBmaWxlIHBlciBjbHVzdGVyIGluc2lkZSBpdHMgb3duIGZvbGRlcgpmb3IgKGNsdXN0ZXJfaWQgaW4gbmFtZXMoZ3NlYV9ieV9jbHVzdGVyKSkgewogIGZvbGRlcl9uYW1lIDwtIHBhc3RlMCgiQ2x1c3Rlcl8iLCBjbHVzdGVyX2lkKQogIAogICMgQ3JlYXRlIGZvbGRlciBpZiBpdCBkb2Vzbid0IGV4aXN0CiAgaWYgKCFkaXIuZXhpc3RzKGZvbGRlcl9uYW1lKSkgewogICAgZGlyLmNyZWF0ZShmb2xkZXJfbmFtZSkKICB9CiAgCiAgIyBXcml0ZSBDU1YgaW5zaWRlIHRoZSBmb2xkZXIKICBmaWxlX3BhdGggPC0gZmlsZS5wYXRoKGZvbGRlcl9uYW1lLCBwYXN0ZTAoIkdTRUFfQ2x1c3Rlcl8iLCBjbHVzdGVyX2lkLCAiLmNzdiIpKQogIHdyaXRlLmNzdihnc2VhX2J5X2NsdXN0ZXJbW2NsdXN0ZXJfaWRdXSwgZmlsZSA9IGZpbGVfcGF0aCwgcm93Lm5hbWVzID0gRkFMU0UpCn0KCgoKCgoKCmBgYAo=