1 Purpose

This script performs functional enrichment analysis (GO, KEGG, Reactome, Hallmark) on the top 100 marker genes per cluster (Supplementary Table S6) to independently validate the cluster names you assigned manually. If the enriched pathways align with your proposed name (e.g., “NK-like” cluster enriched for “natural killer cell mediated cytotoxicity”), the annotation is supported.

2 load libraries

3 Load Top100 Marker Gene Table


markers <- read_excel("Supplementary_Table_S6.xlsx")

# Expected columns: pval, avglog2FC, pct.1, pct.2, pvaladj, cluster, gene
markers <- markers %>%
  rename_with(tolower) %>%
  mutate(cluster = as.character(cluster))

clusters_list <- sort(unique(markers$cluster))
cat("Clusters found:", paste(clusters_list, collapse = ", "), "\n")

clusters_list <- as.character(sort(as.numeric(unique(markers$cluster))))
cat("Clusters found:", paste(clusters_list, collapse = ", "), "\n")

4 Map Gene Symbols to Entrez IDs

gene_map <- bitr(unique(markers$gene),
                  fromType = "SYMBOL",
                  toType = "ENTREZID",
                  OrgDb = org.Hs.eg.db)

markers_mapped <- markers %>%
  left_join(gene_map, by = c("gene" = "SYMBOL")) %>%
  filter(!is.na(ENTREZID))

unmapped_genes <- setdiff(unique(markers$gene), gene_map$SYMBOL)
print(unmapped_genes)

5 Prepare Background Gene Universe

universe_entrez <- unique(markers_mapped$ENTREZID)

6 Define Enrichment Function

run_cluster_enrichment <- function(cluster_id, marker_df) {

  sub <- marker_df %>% filter(cluster == cluster_id)
  entrez_ids <- unique(sub$ENTREZID)

  message("Cluster ", cluster_id, ": ", length(entrez_ids), " mapped genes")

  results <- list()

  results$GO_BP <- tryCatch(
    enrichGO(gene = entrez_ids, universe = universe_entrez,
             OrgDb = org.Hs.eg.db, keyType = "ENTREZID", ont = "BP",
             pAdjustMethod = "BH", pvalueCutoff = 0.05, qvalueCutoff = 0.2,
             readable = TRUE),
    error = function(e) NULL)

  results$KEGG <- tryCatch(
    enrichKEGG(gene = entrez_ids, universe = universe_entrez,
               organism = "hsa", pAdjustMethod = "BH",
               pvalueCutoff = 0.05, qvalueCutoff = 0.2),
    error = function(e) NULL)

  results$Reactome <- tryCatch(
    enrichPathway(gene = entrez_ids, universe = universe_entrez,
                   organism = "human", pAdjustMethod = "BH",
                   pvalueCutoff = 0.05, qvalueCutoff = 0.2, readable = TRUE),
    error = function(e) NULL)

  hallmark_sets <- msigdbr(species = "Homo sapiens", category = "H") %>%
    dplyr::select(gs_name, entrez_gene)

  results$Hallmark <- tryCatch(
    enricher(gene = entrez_ids, universe = universe_entrez,
             TERM2GENE = hallmark_sets, pAdjustMethod = "BH",
             pvalueCutoff = 0.05, qvalueCutoff = 0.2),
    error = function(e) NULL)

  return(results)
}

7 Run Enrichment for Every Cluster

all_results <- list()

for (cl in clusters_list) {
  all_results[[cl]] <- tryCatch(
    run_cluster_enrichment(cl, markers_mapped),
    error = function(e) {
      message("Cluster ", cl, " failed: ", e$message)
      NULL
    }
  )
}

8 Export Enrichment Tables to Excel

wb <- createWorkbook()

for (cl in names(all_results)) {
  res <- all_results[[cl]]
  if (is.null(res)) next

  for (ont in names(res)) {
    obj <- res[[ont]]
    if (is.null(obj) || nrow(as.data.frame(obj)) == 0) next

    sheet_name <- substr(paste0("C", cl, "_", ont), 1, 31)
    addWorksheet(wb, sheet_name)
    writeData(wb, sheet_name, as.data.frame(obj))
  }
}

saveWorkbook(wb, "Cluster_Enrichment_Results_Top100.xlsx", overwrite = TRUE)

9 Visualize Top Pathways per Cluster

for (cl in names(all_results)) {
  res <- all_results[[cl]]
  if (is.null(res)) next

  cat("\n## Cluster", cl, "\n")

  for (ont in c("GO_BP", "KEGG", "Reactome", "Hallmark")) {
    obj <- res[[ont]]
    if (is.null(obj) || nrow(as.data.frame(obj)) == 0) next

    cat("\n###", ont, "- Cluster", cl, "\n")
    p <- dotplot(obj, showCategory = 10) +
      ggtitle(paste("Cluster", cl, "-", ont)) +
      theme(axis.text.y = element_text(size = 9))
    print(p)
    ggsave(filename = paste0("cluster", cl, "_", ont, "_dotplot.png"),
           plot = p, width = 8, height = 6, dpi = 300)
  }
}

## Cluster 0 

### KEGG - Cluster 0 

## Cluster 1 

## Cluster 2 

## Cluster 3 

## Cluster 4 

## Cluster 5 

### Reactome - Cluster 5 

## Cluster 6 

## Cluster 7 

### GO_BP - Cluster 7 

### KEGG - Cluster 7 

### Reactome - Cluster 7 

### Hallmark - Cluster 7 

## Cluster 8 

## Cluster 9 

## Cluster 10 

## Cluster 11 

### GO_BP - Cluster 11 

### Reactome - Cluster 11 

### Hallmark - Cluster 11 

## Cluster 12 

### GO_BP - Cluster 12 

### Hallmark - Cluster 12 

## Cluster 13 

### GO_BP - Cluster 13 

### KEGG - Cluster 13 

### Reactome - Cluster 13 

### Hallmark - Cluster 13 

10 Cross-Check: Annotation vs Enrichment

Manual cross-check: proposed name vs. expected enriched terms
Cluster Proposed_Name Expected_Enriched_Terms
0 MHC-II high aberrant state antigen processing and presentation, MHC class II, interferon response (STAT1)
1 NK-like cytotoxic natural killer cell mediated cytotoxicity, NK receptor signalling, KIR family activity
2 Th2-like Th2 cytokine production, IL-13 signalling, chemokine (CCL17) response
3 Naive/CD4 reference T cell differentiation, naive T cell, TCR signalling
4 Migratory/Adhesion state cell adhesion, extracellular matrix interaction, cell migration (ENPP2/autotaxin signalling)
5 Stem-like stem cell population maintenance, chromatin remodeling (HMGA2), T-cell lineage commitment (RUNX1)
6 Th2-like (Activated) T cell activation, costimulation (4-1BB/TNFRSF9), Th2 cytokine production
7 Cycling (G2/M) mitotic nuclear division, kinetochore organization, G2/M cell cycle checkpoint
8 Metabolically reprogrammed lipid biosynthesis, metabolic reprogramming, lymphocyte activation (CD38)
9 GZMA-cytotoxic T cell mediated cytotoxicity, granzyme-mediated apoptotic signalling
10 Central memory/CD4 reference T cell quiescence, central memory differentiation
11 Pro-inflammatory inflammatory response, S100 alarmin signalling, TNF-family receptor signalling
12 GZMB-high inflammatory granzyme-mediated cytotoxicity, inflammatory chemokine signalling, GM-CSF response
13 IFN stimulated type I interferon signalling, response to virus, ISGylation

11 Notes

  • Enrichment used each cluster’s top 100 marker genes (Supplementary Table S6).
  • Background universe restricted to the union of all top100 genes across clusters.
  • Clusters 3 and 10 are healthy CD4 T-cell reference populations, not malignant states.
  • tryCatch() wraps every enrichment call so one failed database (e.g., no significant KEGG terms for a cluster) won’t stop the whole loop. ```
LS0tCnRpdGxlOiAiQ2x1c3RlciBFbnJpY2htZW50IEFuYWx5c2lzIC0gVmFsaWRhdGlvbiBvZiBDbHVzdGVyIEFubm90YXRpb25zIgphdXRob3I6ICJOYXNpciBNYWhtb29kIEFiYmFzaSIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgdGhlbWU6IGpvdXJuYWwKLS0tCgoKIyBQdXJwb3NlCgpUaGlzIHNjcmlwdCBwZXJmb3JtcyAqKmZ1bmN0aW9uYWwgZW5yaWNobWVudCBhbmFseXNpcyAoR08sIEtFR0csIFJlYWN0b21lLCBIYWxsbWFyaykqKgpvbiB0aGUgKip0b3AgMTAwIG1hcmtlciBnZW5lcyBwZXIgY2x1c3RlcioqIChTdXBwbGVtZW50YXJ5IFRhYmxlIFM2KSB0byBpbmRlcGVuZGVudGx5CnZhbGlkYXRlIHRoZSBjbHVzdGVyIG5hbWVzIHlvdSBhc3NpZ25lZCBtYW51YWxseS4gSWYgdGhlIGVucmljaGVkIHBhdGh3YXlzIGFsaWduIHdpdGgKeW91ciBwcm9wb3NlZCBuYW1lIChlLmcuLCAiTkstbGlrZSIgY2x1c3RlciBlbnJpY2hlZCBmb3IgIm5hdHVyYWwga2lsbGVyIGNlbGwgbWVkaWF0ZWQKY3l0b3RveGljaXR5IiksIHRoZSBhbm5vdGF0aW9uIGlzIHN1cHBvcnRlZC4KCgojIGxvYWQgbGlicmFyaWVzCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIExvYWQgYmVsb3cgbGlicmFyaWVzCmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShvcmcuSHMuZWcuZGIpCmxpYnJhcnkobXNpZ2RicikKbGlicmFyeShSZWFjdG9tZVBBKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZW5yaWNocGxvdCkKbGlicmFyeShvcGVueGxzeCkKbGlicmFyeSh0aWJibGUpCmBgYAoKCiMgTG9hZCBUb3AxMDAgTWFya2VyIEdlbmUgVGFibGUKYGBge3J9CgptYXJrZXJzIDwtIHJlYWRfZXhjZWwoIlN1cHBsZW1lbnRhcnlfVGFibGVfUzYueGxzeCIpCgojIEV4cGVjdGVkIGNvbHVtbnM6IHB2YWwsIGF2Z2xvZzJGQywgcGN0LjEsIHBjdC4yLCBwdmFsYWRqLCBjbHVzdGVyLCBnZW5lCm1hcmtlcnMgPC0gbWFya2VycyAlPiUKICByZW5hbWVfd2l0aCh0b2xvd2VyKSAlPiUKICBtdXRhdGUoY2x1c3RlciA9IGFzLmNoYXJhY3RlcihjbHVzdGVyKSkKCmNsdXN0ZXJzX2xpc3QgPC0gc29ydCh1bmlxdWUobWFya2VycyRjbHVzdGVyKSkKY2F0KCJDbHVzdGVycyBmb3VuZDoiLCBwYXN0ZShjbHVzdGVyc19saXN0LCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iKQoKY2x1c3RlcnNfbGlzdCA8LSBhcy5jaGFyYWN0ZXIoc29ydChhcy5udW1lcmljKHVuaXF1ZShtYXJrZXJzJGNsdXN0ZXIpKSkpCmNhdCgiQ2x1c3RlcnMgZm91bmQ6IiwgcGFzdGUoY2x1c3RlcnNfbGlzdCwgY29sbGFwc2UgPSAiLCAiKSwgIlxuIikKCmBgYAoKIyBNYXAgR2VuZSBTeW1ib2xzIHRvIEVudHJleiBJRHMKYGBge3IgZ2VuZS1pZC1tYXBwaW5nfQpnZW5lX21hcCA8LSBiaXRyKHVuaXF1ZShtYXJrZXJzJGdlbmUpLAogICAgICAgICAgICAgICAgICBmcm9tVHlwZSA9ICJTWU1CT0wiLAogICAgICAgICAgICAgICAgICB0b1R5cGUgPSAiRU5UUkVaSUQiLAogICAgICAgICAgICAgICAgICBPcmdEYiA9IG9yZy5Icy5lZy5kYikKCm1hcmtlcnNfbWFwcGVkIDwtIG1hcmtlcnMgJT4lCiAgbGVmdF9qb2luKGdlbmVfbWFwLCBieSA9IGMoImdlbmUiID0gIlNZTUJPTCIpKSAlPiUKICBmaWx0ZXIoIWlzLm5hKEVOVFJFWklEKSkKCnVubWFwcGVkX2dlbmVzIDwtIHNldGRpZmYodW5pcXVlKG1hcmtlcnMkZ2VuZSksIGdlbmVfbWFwJFNZTUJPTCkKcHJpbnQodW5tYXBwZWRfZ2VuZXMpCgpgYGAKCiMgUHJlcGFyZSBCYWNrZ3JvdW5kIEdlbmUgVW5pdmVyc2UKYGBge3J9CnVuaXZlcnNlX2VudHJleiA8LSB1bmlxdWUobWFya2Vyc19tYXBwZWQkRU5UUkVaSUQpCmBgYAoKIyBEZWZpbmUgRW5yaWNobWVudCBGdW5jdGlvbgpgYGB7cn0KcnVuX2NsdXN0ZXJfZW5yaWNobWVudCA8LSBmdW5jdGlvbihjbHVzdGVyX2lkLCBtYXJrZXJfZGYpIHsKCiAgc3ViIDwtIG1hcmtlcl9kZiAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gY2x1c3Rlcl9pZCkKICBlbnRyZXpfaWRzIDwtIHVuaXF1ZShzdWIkRU5UUkVaSUQpCgogIG1lc3NhZ2UoIkNsdXN0ZXIgIiwgY2x1c3Rlcl9pZCwgIjogIiwgbGVuZ3RoKGVudHJlel9pZHMpLCAiIG1hcHBlZCBnZW5lcyIpCgogIHJlc3VsdHMgPC0gbGlzdCgpCgogIHJlc3VsdHMkR09fQlAgPC0gdHJ5Q2F0Y2goCiAgICBlbnJpY2hHTyhnZW5lID0gZW50cmV6X2lkcywgdW5pdmVyc2UgPSB1bml2ZXJzZV9lbnRyZXosCiAgICAgICAgICAgICBPcmdEYiA9IG9yZy5Icy5lZy5kYiwga2V5VHlwZSA9ICJFTlRSRVpJRCIsIG9udCA9ICJCUCIsCiAgICAgICAgICAgICBwQWRqdXN0TWV0aG9kID0gIkJIIiwgcHZhbHVlQ3V0b2ZmID0gMC4wNSwgcXZhbHVlQ3V0b2ZmID0gMC4yLAogICAgICAgICAgICAgcmVhZGFibGUgPSBUUlVFKSwKICAgIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTCkKCiAgcmVzdWx0cyRLRUdHIDwtIHRyeUNhdGNoKAogICAgZW5yaWNoS0VHRyhnZW5lID0gZW50cmV6X2lkcywgdW5pdmVyc2UgPSB1bml2ZXJzZV9lbnRyZXosCiAgICAgICAgICAgICAgIG9yZ2FuaXNtID0gImhzYSIsIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjA1LCBxdmFsdWVDdXRvZmYgPSAwLjIpLAogICAgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKQoKICByZXN1bHRzJFJlYWN0b21lIDwtIHRyeUNhdGNoKAogICAgZW5yaWNoUGF0aHdheShnZW5lID0gZW50cmV6X2lkcywgdW5pdmVyc2UgPSB1bml2ZXJzZV9lbnRyZXosCiAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICJodW1hbiIsIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wNSwgcXZhbHVlQ3V0b2ZmID0gMC4yLCByZWFkYWJsZSA9IFRSVUUpLAogICAgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKQoKICBoYWxsbWFya19zZXRzIDwtIG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiLCBjYXRlZ29yeSA9ICJIIikgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGdzX25hbWUsIGVudHJlel9nZW5lKQoKICByZXN1bHRzJEhhbGxtYXJrIDwtIHRyeUNhdGNoKAogICAgZW5yaWNoZXIoZ2VuZSA9IGVudHJlel9pZHMsIHVuaXZlcnNlID0gdW5pdmVyc2VfZW50cmV6LAogICAgICAgICAgICAgVEVSTTJHRU5FID0gaGFsbG1hcmtfc2V0cywgcEFkanVzdE1ldGhvZCA9ICJCSCIsCiAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjA1LCBxdmFsdWVDdXRvZmYgPSAwLjIpLAogICAgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKQoKICByZXR1cm4ocmVzdWx0cykKfQpgYGAKCgojIFJ1biBFbnJpY2htZW50IGZvciBFdmVyeSBDbHVzdGVyCmBgYHtyfQphbGxfcmVzdWx0cyA8LSBsaXN0KCkKCmZvciAoY2wgaW4gY2x1c3RlcnNfbGlzdCkgewogIGFsbF9yZXN1bHRzW1tjbF1dIDwtIHRyeUNhdGNoKAogICAgcnVuX2NsdXN0ZXJfZW5yaWNobWVudChjbCwgbWFya2Vyc19tYXBwZWQpLAogICAgZXJyb3IgPSBmdW5jdGlvbihlKSB7CiAgICAgIG1lc3NhZ2UoIkNsdXN0ZXIgIiwgY2wsICIgZmFpbGVkOiAiLCBlJG1lc3NhZ2UpCiAgICAgIE5VTEwKICAgIH0KICApCn0KYGBgCgoKIyBFeHBvcnQgRW5yaWNobWVudCBUYWJsZXMgdG8gRXhjZWwKYGBge3J9CndiIDwtIGNyZWF0ZVdvcmtib29rKCkKCmZvciAoY2wgaW4gbmFtZXMoYWxsX3Jlc3VsdHMpKSB7CiAgcmVzIDwtIGFsbF9yZXN1bHRzW1tjbF1dCiAgaWYgKGlzLm51bGwocmVzKSkgbmV4dAoKICBmb3IgKG9udCBpbiBuYW1lcyhyZXMpKSB7CiAgICBvYmogPC0gcmVzW1tvbnRdXQogICAgaWYgKGlzLm51bGwob2JqKSB8fCBucm93KGFzLmRhdGEuZnJhbWUob2JqKSkgPT0gMCkgbmV4dAoKICAgIHNoZWV0X25hbWUgPC0gc3Vic3RyKHBhc3RlMCgiQyIsIGNsLCAiXyIsIG9udCksIDEsIDMxKQogICAgYWRkV29ya3NoZWV0KHdiLCBzaGVldF9uYW1lKQogICAgd3JpdGVEYXRhKHdiLCBzaGVldF9uYW1lLCBhcy5kYXRhLmZyYW1lKG9iaikpCiAgfQp9CgpzYXZlV29ya2Jvb2sod2IsICJDbHVzdGVyX0VucmljaG1lbnRfUmVzdWx0c19Ub3AxMDAueGxzeCIsIG92ZXJ3cml0ZSA9IFRSVUUpCmBgYAoKCgojIFZpc3VhbGl6ZSBUb3AgUGF0aHdheXMgcGVyIENsdXN0ZXIKYGBge3J9CmZvciAoY2wgaW4gbmFtZXMoYWxsX3Jlc3VsdHMpKSB7CiAgcmVzIDwtIGFsbF9yZXN1bHRzW1tjbF1dCiAgaWYgKGlzLm51bGwocmVzKSkgbmV4dAoKICBjYXQoIlxuIyMgQ2x1c3RlciIsIGNsLCAiXG4iKQoKICBmb3IgKG9udCBpbiBjKCJHT19CUCIsICJLRUdHIiwgIlJlYWN0b21lIiwgIkhhbGxtYXJrIikpIHsKICAgIG9iaiA8LSByZXNbW29udF1dCiAgICBpZiAoaXMubnVsbChvYmopIHx8IG5yb3coYXMuZGF0YS5mcmFtZShvYmopKSA9PSAwKSBuZXh0CgogICAgY2F0KCJcbiMjIyIsIG9udCwgIi0gQ2x1c3RlciIsIGNsLCAiXG4iKQogICAgcCA8LSBkb3RwbG90KG9iaiwgc2hvd0NhdGVnb3J5ID0gMTApICsKICAgICAgZ2d0aXRsZShwYXN0ZSgiQ2x1c3RlciIsIGNsLCAiLSIsIG9udCkpICsKICAgICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpKQogICAgcHJpbnQocCkKICAgIGdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMCgiY2x1c3RlciIsIGNsLCAiXyIsIG9udCwgIl9kb3RwbG90LnBuZyIpLAogICAgICAgICAgIHBsb3QgPSBwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIGRwaSA9IDMwMCkKICB9Cn0KYGBgCgojIENyb3NzLUNoZWNrOiBBbm5vdGF0aW9uIHZzIEVucmljaG1lbnQKYGBge3J9CmFubm90YXRpb25fY2hlY2sgPC0gdGliYmxlOjp0aWJibGUoCiAgQ2x1c3RlciA9IGFzLmNoYXJhY3RlcigwOjEzKSwKICBQcm9wb3NlZF9OYW1lID0gYygKICAgICJNSEMtSUkgaGlnaCBhYmVycmFudCBzdGF0ZSIsICJOSy1saWtlIGN5dG90b3hpYyIsICJUaDItbGlrZSIsCiAgICAiTmFpdmUvQ0Q0IHJlZmVyZW5jZSIsICJNaWdyYXRvcnkvQWRoZXNpb24gc3RhdGUiLCAiU3RlbS1saWtlIiwKICAgICJUaDItbGlrZSAoQWN0aXZhdGVkKSIsICJDeWNsaW5nIChHMi9NKSIsICJNZXRhYm9saWNhbGx5IHJlcHJvZ3JhbW1lZCIsCiAgICAiR1pNQS1jeXRvdG94aWMiLCAiQ2VudHJhbCBtZW1vcnkvQ0Q0IHJlZmVyZW5jZSIsICJQcm8taW5mbGFtbWF0b3J5IiwKICAgICJHWk1CLWhpZ2ggaW5mbGFtbWF0b3J5IiwgIklGTiBzdGltdWxhdGVkIgogICksCiAgRXhwZWN0ZWRfRW5yaWNoZWRfVGVybXMgPSBjKAogICAgImFudGlnZW4gcHJvY2Vzc2luZyBhbmQgcHJlc2VudGF0aW9uLCBNSEMgY2xhc3MgSUksIGludGVyZmVyb24gcmVzcG9uc2UgKFNUQVQxKSIsCiAgICAibmF0dXJhbCBraWxsZXIgY2VsbCBtZWRpYXRlZCBjeXRvdG94aWNpdHksIE5LIHJlY2VwdG9yIHNpZ25hbGxpbmcsIEtJUiBmYW1pbHkgYWN0aXZpdHkiLAogICAgIlRoMiBjeXRva2luZSBwcm9kdWN0aW9uLCBJTC0xMyBzaWduYWxsaW5nLCBjaGVtb2tpbmUgKENDTDE3KSByZXNwb25zZSIsCiAgICAiVCBjZWxsIGRpZmZlcmVudGlhdGlvbiwgbmFpdmUgVCBjZWxsLCBUQ1Igc2lnbmFsbGluZyIsCiAgICAiY2VsbCBhZGhlc2lvbiwgZXh0cmFjZWxsdWxhciBtYXRyaXggaW50ZXJhY3Rpb24sIGNlbGwgbWlncmF0aW9uIChFTlBQMi9hdXRvdGF4aW4gc2lnbmFsbGluZykiLAogICAgInN0ZW0gY2VsbCBwb3B1bGF0aW9uIG1haW50ZW5hbmNlLCBjaHJvbWF0aW4gcmVtb2RlbGluZyAoSE1HQTIpLCBULWNlbGwgbGluZWFnZSBjb21taXRtZW50IChSVU5YMSkiLAogICAgIlQgY2VsbCBhY3RpdmF0aW9uLCBjb3N0aW11bGF0aW9uICg0LTFCQi9UTkZSU0Y5KSwgVGgyIGN5dG9raW5lIHByb2R1Y3Rpb24iLAogICAgIm1pdG90aWMgbnVjbGVhciBkaXZpc2lvbiwga2luZXRvY2hvcmUgb3JnYW5pemF0aW9uLCBHMi9NIGNlbGwgY3ljbGUgY2hlY2twb2ludCIsCiAgICAibGlwaWQgYmlvc3ludGhlc2lzLCBtZXRhYm9saWMgcmVwcm9ncmFtbWluZywgbHltcGhvY3l0ZSBhY3RpdmF0aW9uIChDRDM4KSIsCiAgICAiVCBjZWxsIG1lZGlhdGVkIGN5dG90b3hpY2l0eSwgZ3Jhbnp5bWUtbWVkaWF0ZWQgYXBvcHRvdGljIHNpZ25hbGxpbmciLAogICAgIlQgY2VsbCBxdWllc2NlbmNlLCBjZW50cmFsIG1lbW9yeSBkaWZmZXJlbnRpYXRpb24iLAogICAgImluZmxhbW1hdG9yeSByZXNwb25zZSwgUzEwMCBhbGFybWluIHNpZ25hbGxpbmcsIFRORi1mYW1pbHkgcmVjZXB0b3Igc2lnbmFsbGluZyIsCiAgICAiZ3Jhbnp5bWUtbWVkaWF0ZWQgY3l0b3RveGljaXR5LCBpbmZsYW1tYXRvcnkgY2hlbW9raW5lIHNpZ25hbGxpbmcsIEdNLUNTRiByZXNwb25zZSIsCiAgICAidHlwZSBJIGludGVyZmVyb24gc2lnbmFsbGluZywgcmVzcG9uc2UgdG8gdmlydXMsIElTR3lsYXRpb24iCiAgKQopCgprbml0cjo6a2FibGUoYW5ub3RhdGlvbl9jaGVjaywgY2FwdGlvbiA9ICJNYW51YWwgY3Jvc3MtY2hlY2s6IHByb3Bvc2VkIG5hbWUgdnMuIGV4cGVjdGVkIGVucmljaGVkIHRlcm1zIikKCmBgYAoKIyBOb3RlcwoKLSBFbnJpY2htZW50IHVzZWQgZWFjaCBjbHVzdGVyJ3MgdG9wIDEwMCBtYXJrZXIgZ2VuZXMgKFN1cHBsZW1lbnRhcnkgVGFibGUgUzYpLgotIEJhY2tncm91bmQgdW5pdmVyc2UgcmVzdHJpY3RlZCB0byB0aGUgdW5pb24gb2YgYWxsIHRvcDEwMCBnZW5lcyBhY3Jvc3MgY2x1c3RlcnMuCi0gQ2x1c3RlcnMgMyBhbmQgMTAgYXJlIGhlYWx0aHkgQ0Q0IFQtY2VsbCByZWZlcmVuY2UgcG9wdWxhdGlvbnMsIG5vdCBtYWxpZ25hbnQgc3RhdGVzLgotIGB0cnlDYXRjaCgpYCB3cmFwcyBldmVyeSBlbnJpY2htZW50IGNhbGwgc28gb25lIGZhaWxlZCBkYXRhYmFzZSAoZS5nLiwgbm8gc2lnbmlmaWNhbnQKICBLRUdHIHRlcm1zIGZvciBhIGNsdXN0ZXIpIHdvbid0IHN0b3AgdGhlIHdob2xlIGxvb3AuCmBgYAoKCg==