1. load libraries

2. Load Seurat Object


All_samples_Merged <- readRDS("../../PHD_3rd_YEAR_Analysis/0-Seurat_RDS_OBJECT_FINAL/All_samples_Merged_with_STCAT_Annotation_final-5-09-2025.rds")

DefaultAssay(All_samples_Merged) <- "RNA"

All_samples_Merged <- NormalizeData(
    All_samples_Merged,
    normalization.method = "LogNormalize",
    scale.factor = 1e4,
    verbose = TRUE
  )
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|

3.find Top markers

Idents(All_samples_Merged) <- "seurat_clusters"

# ---------------------------------------------------------
# 2️⃣ Find marker genes per cluster
SS_markers <- FindAllMarkers(
  All_samples_Merged,
  only.pos = TRUE,
  min.pct = 0.25,
  logfc.threshold = 0.25,
  min.pct.diff = 0.2
  
)

library(dplyr)

# Precise blacklist for uninformative genes
blacklist_patterns <- c(
  "^TRAV", "^TRBV", "^TRGV", "^TRDV", "^TRBC", "^TRAC", "^TRDC", "^TRGC", # TCR
  "^IGH", "^IGK", "^IGL", "^IGJ",                                         # Ig genes
  "^RPL", "^RPS",                                                         # ribosomal
  "^MT-",                                                                 # mitochondria
  "^HBA", "^HBB", "^HB[ABZ]",                                             # hemoglobins
  "^NEAT1$", "^MALAT1$",                                                  # optional lncRNAs
  "^XIST$"                              )

blacklist_regex <- paste(blacklist_patterns, collapse = "|")

# Preview which markers will be removed
to_remove <- SS_markers %>%
  filter(grepl(blacklist_regex, gene, ignore.case = TRUE))
message("Rows to remove: ", nrow(to_remove))
head(to_remove$gene)
[1] "TRAV17"  "TRAV9-2" "RPL22L1" "RPL7"    "RPL35A"  "NEAT1"  
# Filter markers (keep important metabolic/proliferation genes)
SS_markers_filtered <- SS_markers %>%
  filter(!grepl(blacklist_regex, gene, ignore.case = TRUE))

4. Top5 Markers


library(dplyr)

# ---------------------------------------------------------
# Save filtered markers
write.csv(SS_markers_filtered, file = "SS_markers_filtered.csv", row.names = FALSE)

# ---------------------------------------------------------
# Extract top 25 markers per cluster
top25_markers <- SS_markers_filtered %>%
  filter(p_val_adj < 0.05) %>%  # ensure statistical significance
  group_by(cluster) %>%
  slice_max(order_by = avg_log2FC, n = 25) %>%
  ungroup()

write.csv(top25_markers, file = "SS_markers_top25.csv", row.names = FALSE)

# ---------------------------------------------------------
# Extract top 5 markers per cluster
top5_markers <- SS_markers_filtered %>%
  filter(p_val_adj < 0.05) %>%  # ensure statistical significance
  group_by(cluster) %>%
  slice_max(order_by = avg_log2FC, n = 5) %>%
  ungroup()

write.csv(top5_markers, file = "SS_markers_top5.csv", row.names = FALSE)

message("Filtered markers, top25, and top5 markers saved successfully.")

4. Top5 Markers


library(dplyr)

# ---------------------------------------------------------
# Save filtered markers
write.csv(SS_markers_filtered, file = "SS_markers_filtered.csv", row.names = FALSE)

# ---------------------------------------------------------
# Extract top 25 markers per cluster
top25_markers <- SS_markers_filtered %>%
  filter(p_val_adj < 0.05) %>%  # ensure statistical significance
  group_by(cluster) %>%
  slice_max(order_by = avg_log2FC, n = 25) %>%
  ungroup()

write.csv(top25_markers, file = "SS_markers_top25.csv", row.names = FALSE)

# ---------------------------------------------------------
# Extract top 5 markers per cluster
top5_markers <- SS_markers_filtered %>%
  filter(p_val_adj < 0.05) %>%  # ensure statistical significance
  group_by(cluster) %>%
  slice_max(order_by = avg_log2FC, n = 5) %>%
  ungroup()

write.csv(top5_markers, file = "SS_markers_top5.csv", row.names = FALSE)

message("Filtered markers, top25, and top5 markers saved successfully.")

5.Rename Clusters

# 4️⃣ Verify order
levels(All_samples_Merged$renamed_clusters)
 [1] "0: Malignant_CD4T (Transcriptionally_Dysregulated_with_APC)"
 [2] "1: Malignant_CD4T (cytotoxic/NK-like)"                      
 [3] "2: Malignant_CD4T (Th2-skewed)"                             
 [4] "3: Healthy_CD4T (Mixed_Population(Tn/TCM))"                 
 [5] "4: Malignant_CD4T (Migratory/Inflammatory)"                 
 [6] "5: Malignant_CD4T (Stem-like/Th2-associated)"               
 [7] "6: Malignant_CD4T (Sézary_like CCL17⁺ IL-13⁺)"              
 [8] "7: Malignant_CD4T (Metabolic_Stress_Active MXD3+ KLHL4+)"   
 [9] "8: Malignant_CD4T (Prolif_Metabolically_active_LHX9+)"      
[10] "9: Malignant_CD4T (Th2/Cytotoxic)"                          
[11] "10: Healthy_CD4T (TCM)"                                     
[12] "11: Malignant_CD4T (stressed/pro-inflammatory)"             
[13] "12: Malignant_CD4T (Cytotoxic/Inflammatory)"                
[14] "13: Malignant_CD4T (Type-I_Interferon_Hyperactivated)"      
table(All_samples_Merged$renamed_clusters)

0: Malignant_CD4T (Transcriptionally_Dysregulated_with_APC) 
                                                       6789 
                      1: Malignant_CD4T (cytotoxic/NK-like) 
                                                       5275 
                             2: Malignant_CD4T (Th2-skewed) 
                                                       4663 
                 3: Healthy_CD4T (Mixed_Population(Tn/TCM)) 
                                                       4661 
                 4: Malignant_CD4T (Migratory/Inflammatory) 
                                                       4086 
               5: Malignant_CD4T (Stem-like/Th2-associated) 
                                                       3634 
              6: Malignant_CD4T (Sézary_like CCL17⁺ IL-13⁺) 
                                                       3536 
   7: Malignant_CD4T (Metabolic_Stress_Active MXD3+ KLHL4+) 
                                                       3409 
      8: Malignant_CD4T (Prolif_Metabolically_active_LHX9+) 
                                                       3338 
                          9: Malignant_CD4T (Th2/Cytotoxic) 
                                                       3273 
                                     10: Healthy_CD4T (TCM) 
                                                       3212 
             11: Malignant_CD4T (stressed/pro-inflammatory) 
                                                       1675 
                12: Malignant_CD4T (Cytotoxic/Inflammatory) 
                                                       1063 
      13: Malignant_CD4T (Type-I_Interferon_Hyperactivated) 
                                                        691 
# 5️⃣ Plot
DimPlot(
  All_samples_Merged,
  group.by = "renamed_clusters",
  label = TRUE,
  repel = TRUE,
  reduction = "umap"
) +
  ggtitle("Cluster Annotations (Ordered 0–13)") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

Save the Annotated Seurat Object

# ✅ Save the Seurat object with new cluster annotations
saveRDS(
  All_samples_Merged,
  file = "../../PHD_3rd_YEAR_Analysis/0-Seurat_RDS_OBJECT_FINAL/All_samples_Merged_with_Renamed_Clusters_final-26-10-2025.rds"
)

message("✅ Seurat object saved successfully with renamed clusters and metadata column 'renamed_clusters'.")

dimplot

DimPlot(All_samples_Merged, group.by = "renamed_clusters", reduction = "umap", label = T, label.box = T, repel = T)

DimPlot(All_samples_Merged, group.by = "renamed_clusters", reduction = "umap", label = F, label.box = T, repel = T)


library(SCpubr)
SCpubr::do_DimPlot(sample = All_samples_Merged, group.by = "renamed_clusters", reduction = "umap", label = F, label.box = T, repel = T,
                    legend.position = "right")

 SCpubr::do_DimPlot(sample = All_samples_Merged, group.by = "renamed_clusters", reduction = "umap", label = T, label.box = T, repel = T,
                    legend.position = "none")

SCpubr::do_DimPlot(sample = All_samples_Merged, group.by = "renamed_clusters", reduction = "umap", label = F, label.box = T, repel = T,
                    legend.position = "right")


library(scCustomize)
DimPlot_scCustom(seurat_object = All_samples_Merged, group.by = "renamed_clusters", reduction = "umap", label = F, label.box = T, repel = T)

NA
NA

ScCutomize Dotplot

library(scCustomize)

Clustered_DotPlot(seurat_object = All_samples_Merged, features = top5_markers$gene, k=7, , x_lab_rotate = 90)
[[1]]

[[2]]

ScCutomize heatmap

# Scale the data for the selected features
alldata <- ScaleData(
  All_samples_Merged,
  features = as.character(unique(top5_markers$gene)),
  assay = "RNA"
)

  |                                                                                                                                 
  |                                                                                                                           |   0%
  |                                                                                                                                 
  |===========================================================================================================================| 100%
# Create the heatmap object
p <- DoHeatmap(
  alldata,
  features = as.character(unique(top5_markers$gene)),
  group.by = "renamed_clusters",  # use your annotated clusters
  assay = "RNA",
  size = 4
) +
  ggtitle("Top 5 Marker Genes per Cluster (Sézary Syndrome)") +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold", size = 14)
  )

# ✅ Save as PNG
ggsave(
  filename = "Top5_Markers_Heatmap.png",
  plot = p,
  width = 20,
  height = 16,
  dpi = 300
)

# ✅ Save as PDF
ggsave(
  filename = "Top5_Markers_Heatmap.pdf",
  plot = p,
  width = 20,
  height = 16,
  dpi = 300
)

message("✅ Heatmap saved successfully as both PNG and PDF.")

ScCutomize Dotplot

ScCutomize Dotplot

library(scCustomize)

Clustered_DotPlot(seurat_object = All_samples_Merged, features = top5_markers$gene, k=7, , x_lab_rotate = 90)
[[1]]

[[2]]

LS0tCnRpdGxlOiAiSWRlbnRpZnkgVG9wNSBNYXJrZXJzIGFuZCByZW5hbWUgY2x1c3RlcnMgIgphdXRob3I6ICJOYXNpciBNYWhtb29kIEFiYmFzaSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2NvbGxhcHNlZDogeWVzCiAgd29yZF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwotLS0KCgojIDEuIGxvYWQgbGlicmFyaWVzCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIExvYWQgYmVsb3cgbGlicmFyaWVzCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShjb3dwbG90KQoKCmxpYnJhcnkoU0NwdWJyKQpsaWJyYXJ5KGRwbHlyKQpgYGAKCgojIDIuIExvYWQgU2V1cmF0IE9iamVjdCAKYGBge3J9CgpBbGxfc2FtcGxlc19NZXJnZWQgPC0gcmVhZFJEUygiLi4vLi4vUEhEXzNyZF9ZRUFSX0FuYWx5c2lzLzAtU2V1cmF0X1JEU19PQkpFQ1RfRklOQUwvQWxsX3NhbXBsZXNfTWVyZ2VkX3dpdGhfU1RDQVRfQW5ub3RhdGlvbl9maW5hbC01LTA5LTIwMjUucmRzIikKCkRlZmF1bHRBc3NheShBbGxfc2FtcGxlc19NZXJnZWQpIDwtICJSTkEiCgpBbGxfc2FtcGxlc19NZXJnZWQgPC0gTm9ybWFsaXplRGF0YSgKICAgIEFsbF9zYW1wbGVzX01lcmdlZCwKICAgIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIsCiAgICBzY2FsZS5mYWN0b3IgPSAxZTQsCiAgICB2ZXJib3NlID0gVFJVRQogICkKCgpgYGAKCiMgMy5maW5kIFRvcCBtYXJrZXJzCmBgYHtyfQpJZGVudHMoQWxsX3NhbXBsZXNfTWVyZ2VkKSA8LSAic2V1cmF0X2NsdXN0ZXJzIgoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAy77iP4oOjIEZpbmQgbWFya2VyIGdlbmVzIHBlciBjbHVzdGVyClNTX21hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMoCiAgQWxsX3NhbXBsZXNfTWVyZ2VkLAogIG9ubHkucG9zID0gVFJVRSwKICBtaW4ucGN0ID0gMC4yNSwKICBsb2dmYy50aHJlc2hvbGQgPSAwLjI1LAogIG1pbi5wY3QuZGlmZiA9IDAuMgogIAopCgpsaWJyYXJ5KGRwbHlyKQoKIyBQcmVjaXNlIGJsYWNrbGlzdCBmb3IgdW5pbmZvcm1hdGl2ZSBnZW5lcwpibGFja2xpc3RfcGF0dGVybnMgPC0gYygKICAiXlRSQVYiLCAiXlRSQlYiLCAiXlRSR1YiLCAiXlRSRFYiLCAiXlRSQkMiLCAiXlRSQUMiLCAiXlRSREMiLCAiXlRSR0MiLCAjIFRDUgogICJeSUdIIiwgIl5JR0siLCAiXklHTCIsICJeSUdKIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgSWcgZ2VuZXMKICAiXlJQTCIsICJeUlBTIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHJpYm9zb21hbAogICJeTVQtIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbWl0b2Nob25kcmlhCiAgIl5IQkEiLCAiXkhCQiIsICJeSEJbQUJaXSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBoZW1vZ2xvYmlucwogICJeTkVBVDEkIiwgIl5NQUxBVDEkIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgb3B0aW9uYWwgbG5jUk5BcwogICJeWElTVCQiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKYmxhY2tsaXN0X3JlZ2V4IDwtIHBhc3RlKGJsYWNrbGlzdF9wYXR0ZXJucywgY29sbGFwc2UgPSAifCIpCgojIFByZXZpZXcgd2hpY2ggbWFya2VycyB3aWxsIGJlIHJlbW92ZWQKdG9fcmVtb3ZlIDwtIFNTX21hcmtlcnMgJT4lCiAgZmlsdGVyKGdyZXBsKGJsYWNrbGlzdF9yZWdleCwgZ2VuZSwgaWdub3JlLmNhc2UgPSBUUlVFKSkKbWVzc2FnZSgiUm93cyB0byByZW1vdmU6ICIsIG5yb3codG9fcmVtb3ZlKSkKaGVhZCh0b19yZW1vdmUkZ2VuZSkKCiMgRmlsdGVyIG1hcmtlcnMgKGtlZXAgaW1wb3J0YW50IG1ldGFib2xpYy9wcm9saWZlcmF0aW9uIGdlbmVzKQpTU19tYXJrZXJzX2ZpbHRlcmVkIDwtIFNTX21hcmtlcnMgJT4lCiAgZmlsdGVyKCFncmVwbChibGFja2xpc3RfcmVnZXgsIGdlbmUsIGlnbm9yZS5jYXNlID0gVFJVRSkpCgoKYGBgCgoKIyA0LiBUb3A1IE1hcmtlcnMKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQoKbGlicmFyeShkcGx5cikKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgU2F2ZSBmaWx0ZXJlZCBtYXJrZXJzCndyaXRlLmNzdihTU19tYXJrZXJzX2ZpbHRlcmVkLCBmaWxlID0gIlNTX21hcmtlcnNfZmlsdGVyZWQuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEV4dHJhY3QgdG9wIDI1IG1hcmtlcnMgcGVyIGNsdXN0ZXIKdG9wMjVfbWFya2VycyA8LSBTU19tYXJrZXJzX2ZpbHRlcmVkICU+JQogIGZpbHRlcihwX3ZhbF9hZGogPCAwLjA1KSAlPiUgICMgZW5zdXJlIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQogIHNsaWNlX21heChvcmRlcl9ieSA9IGF2Z19sb2cyRkMsIG4gPSAyNSkgJT4lCiAgdW5ncm91cCgpCgp3cml0ZS5jc3YodG9wMjVfbWFya2VycywgZmlsZSA9ICJTU19tYXJrZXJzX3RvcDI1LmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBFeHRyYWN0IHRvcCA1IG1hcmtlcnMgcGVyIGNsdXN0ZXIKdG9wNV9tYXJrZXJzIDwtIFNTX21hcmtlcnNfZmlsdGVyZWQgJT4lCiAgZmlsdGVyKHBfdmFsX2FkaiA8IDAuMDUpICU+JSAgIyBlbnN1cmUgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lCiAgc2xpY2VfbWF4KG9yZGVyX2J5ID0gYXZnX2xvZzJGQywgbiA9IDUpICU+JQogIHVuZ3JvdXAoKQoKd3JpdGUuY3N2KHRvcDVfbWFya2VycywgZmlsZSA9ICJTU19tYXJrZXJzX3RvcDUuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgptZXNzYWdlKCJGaWx0ZXJlZCBtYXJrZXJzLCB0b3AyNSwgYW5kIHRvcDUgbWFya2VycyBzYXZlZCBzdWNjZXNzZnVsbHkuIikKCmBgYAoKIyA0LiBUb3A1IE1hcmtlcnMKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQoKbGlicmFyeShkcGx5cikKCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgU2F2ZSBmaWx0ZXJlZCBtYXJrZXJzCndyaXRlLmNzdihTU19tYXJrZXJzX2ZpbHRlcmVkLCBmaWxlID0gIlNTX21hcmtlcnNfZmlsdGVyZWQuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEV4dHJhY3QgdG9wIDI1IG1hcmtlcnMgcGVyIGNsdXN0ZXIKdG9wMjVfbWFya2VycyA8LSBTU19tYXJrZXJzX2ZpbHRlcmVkICU+JQogIGZpbHRlcihwX3ZhbF9hZGogPCAwLjA1KSAlPiUgICMgZW5zdXJlIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQogIHNsaWNlX21heChvcmRlcl9ieSA9IGF2Z19sb2cyRkMsIG4gPSAyNSkgJT4lCiAgdW5ncm91cCgpCgp3cml0ZS5jc3YodG9wMjVfbWFya2VycywgZmlsZSA9ICJTU19tYXJrZXJzX3RvcDI1LmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBFeHRyYWN0IHRvcCA1IG1hcmtlcnMgcGVyIGNsdXN0ZXIKdG9wNV9tYXJrZXJzIDwtIFNTX21hcmtlcnNfZmlsdGVyZWQgJT4lCiAgZmlsdGVyKHBfdmFsX2FkaiA8IDAuMDUpICU+JSAgIyBlbnN1cmUgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lCiAgc2xpY2VfbWF4KG9yZGVyX2J5ID0gYXZnX2xvZzJGQywgbiA9IDUpICU+JQogIHVuZ3JvdXAoKQoKd3JpdGUuY3N2KHRvcDVfbWFya2VycywgZmlsZSA9ICJTU19tYXJrZXJzX3RvcDUuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgptZXNzYWdlKCJGaWx0ZXJlZCBtYXJrZXJzLCB0b3AyNSwgYW5kIHRvcDUgbWFya2VycyBzYXZlZCBzdWNjZXNzZnVsbHkuIikKCmBgYAoKCiMgNS5SZW5hbWUgQ2x1c3RlcnMKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQoKbGlicmFyeShkcGx5cikKCiMgMe+4j+KDoyBEZWZpbmUgbmV3IGNsdXN0ZXIgbmFtZXMgd2l0aCBudW1lcmljIHByZWZpeApjbHVzdGVyX25hbWVzIDwtIGMoCiAgIjAiICA9ICIwOiBNYWxpZ25hbnRfQ0Q0VCAoVHJhbnNjcmlwdGlvbmFsbHlfRHlzcmVndWxhdGVkX3dpdGhfQVBDKSIsICAgICAgICAgICAgICAgICAgICAgICAgCiAgIjEiICA9ICIxOiBNYWxpZ25hbnRfQ0Q0VCAoY3l0b3RveGljL05LLWxpa2UpIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAiMiIgID0gIjI6IE1hbGlnbmFudF9DRDRUIChUaDItc2tld2VkKSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAiMyIgID0gIjM6IEhlYWx0aHlfQ0Q0VCAoTWl4ZWRfUG9wdWxhdGlvbihUbi9UQ00pKSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICI0IiAgPSAiNDogTWFsaWduYW50X0NENFQgKE1pZ3JhdG9yeS9JbmZsYW1tYXRvcnkpIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICI1IiAgPSAiNTogTWFsaWduYW50X0NENFQgKFN0ZW0tbGlrZS9UaDItYXNzb2NpYXRlZCkiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgIjYiICA9ICI2OiBNYWxpZ25hbnRfQ0Q0VCAoU8OpemFyeV9saWtlIENDTDE34oG6IElMLTEz4oG6KSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgIjciICA9ICI3OiBNYWxpZ25hbnRfQ0Q0VCAoTWV0YWJvbGljX1N0cmVzc19BY3RpdmUgTVhEMysgS0xITDQrKSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAiOCIgID0gIjg6IE1hbGlnbmFudF9DRDRUIChQcm9saWZfTWV0YWJvbGljYWxseV9hY3RpdmVfTEhYOSspIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgIjkiICA9ICI5OiBNYWxpZ25hbnRfQ0Q0VCAoVGgyL0N5dG90b3hpYykiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAiMTAiID0gIjEwOiBIZWFsdGh5X0NENFQgKFRDTSkiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAiMTEiID0gIjExOiBNYWxpZ25hbnRfQ0Q0VCAoc3RyZXNzZWQvcHJvLWluZmxhbW1hdG9yeSkiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgIjEyIiA9ICIxMjogTWFsaWduYW50X0NENFQgKEN5dG90b3hpYy9JbmZsYW1tYXRvcnkpIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAiMTMiID0gIjEzOiBNYWxpZ25hbnRfQ0Q0VCAoVHlwZS1JX0ludGVyZmVyb25fSHlwZXJhY3RpdmF0ZWQpIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKKQoKIyAy77iP4oOjIEFwcGx5IHJlbmFtaW5nCkFsbF9zYW1wbGVzX01lcmdlZCA8LSBSZW5hbWVJZGVudHMoQWxsX3NhbXBsZXNfTWVyZ2VkLCBjbHVzdGVyX25hbWVzKQoKIyAz77iP4oOjIEFkZCBtZXRhZGF0YSBjb2x1bW4KQWxsX3NhbXBsZXNfTWVyZ2VkJHJlbmFtZWRfY2x1c3RlcnMgPC0gZmFjdG9yKAogIElkZW50cyhBbGxfc2FtcGxlc19NZXJnZWQpLAogIGxldmVscyA9IGNsdXN0ZXJfbmFtZXMgICMgZW5zdXJlcyBvcmRlciAw4oaSMTMgaW4gcGxvdHMgYW5kIGxlZ2VuZHMKKQoKIyA077iP4oOjIFZlcmlmeSBvcmRlcgpsZXZlbHMoQWxsX3NhbXBsZXNfTWVyZ2VkJHJlbmFtZWRfY2x1c3RlcnMpCgp0YWJsZShBbGxfc2FtcGxlc19NZXJnZWQkcmVuYW1lZF9jbHVzdGVycykKCiMgNe+4j+KDoyBQbG90CkRpbVBsb3QoCiAgQWxsX3NhbXBsZXNfTWVyZ2VkLAogIGdyb3VwLmJ5ID0gInJlbmFtZWRfY2x1c3RlcnMiLAogIGxhYmVsID0gVFJVRSwKICByZXBlbCA9IFRSVUUsCiAgcmVkdWN0aW9uID0gInVtYXAiCikgKwogIGdndGl0bGUoIkNsdXN0ZXIgQW5ub3RhdGlvbnMgKE9yZGVyZWQgMOKAkzEzKSIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSkKCgpgYGAKCiMjIFNhdmUgdGhlIEFubm90YXRlZCBTZXVyYXQgT2JqZWN0CmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0KIyAjIOKchSBTYXZlIHRoZSBTZXVyYXQgb2JqZWN0IHdpdGggbmV3IGNsdXN0ZXIgYW5ub3RhdGlvbnMKIyBzYXZlUkRTKAojICAgQWxsX3NhbXBsZXNfTWVyZ2VkLAojICAgZmlsZSA9ICIuLi8uLi9QSERfM3JkX1lFQVJfQW5hbHlzaXMvMC1TZXVyYXRfUkRTX09CSkVDVF9GSU5BTC9BbGxfc2FtcGxlc19NZXJnZWRfd2l0aF9SZW5hbWVkX0NsdXN0ZXJzX2ZpbmFsLTI2LTEwLTIwMjUucmRzIgojICkKIyAKIyBtZXNzYWdlKCLinIUgU2V1cmF0IG9iamVjdCBzYXZlZCBzdWNjZXNzZnVsbHkgd2l0aCByZW5hbWVkIGNsdXN0ZXJzIGFuZCBtZXRhZGF0YSBjb2x1bW4gJ3JlbmFtZWRfY2x1c3RlcnMnLiIpCgoKCmBgYAojIyBkaW1wbG90CmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0KRGltUGxvdChBbGxfc2FtcGxlc19NZXJnZWQsIGdyb3VwLmJ5ID0gInJlbmFtZWRfY2x1c3RlcnMiLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCwgcmVwZWwgPSBUKQpEaW1QbG90KEFsbF9zYW1wbGVzX01lcmdlZCwgZ3JvdXAuYnkgPSAicmVuYW1lZF9jbHVzdGVycyIsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBGLCBsYWJlbC5ib3ggPSBULCByZXBlbCA9IFQpCgpsaWJyYXJ5KFNDcHVicikKU0NwdWJyOjpkb19EaW1QbG90KHNhbXBsZSA9IEFsbF9zYW1wbGVzX01lcmdlZCwgZ3JvdXAuYnkgPSAicmVuYW1lZF9jbHVzdGVycyIsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBGLCBsYWJlbC5ib3ggPSBULCByZXBlbCA9IFQsCiAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKIFNDcHVicjo6ZG9fRGltUGxvdChzYW1wbGUgPSBBbGxfc2FtcGxlc19NZXJnZWQsIGdyb3VwLmJ5ID0gInJlbmFtZWRfY2x1c3RlcnMiLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCwgcmVwZWwgPSBULAogICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKU0NwdWJyOjpkb19EaW1QbG90KHNhbXBsZSA9IEFsbF9zYW1wbGVzX01lcmdlZCwgZ3JvdXAuYnkgPSAicmVuYW1lZF9jbHVzdGVycyIsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBGLCBsYWJlbC5ib3ggPSBULCByZXBlbCA9IFQsCiAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKCmxpYnJhcnkoc2NDdXN0b21pemUpCkRpbVBsb3Rfc2NDdXN0b20oc2V1cmF0X29iamVjdCA9IEFsbF9zYW1wbGVzX01lcmdlZCwgZ3JvdXAuYnkgPSAicmVuYW1lZF9jbHVzdGVycyIsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBGLCBsYWJlbC5ib3ggPSBULCByZXBlbCA9IFQpCgoKYGBgCgoKCiMjIFNjQ3V0b21pemUgRG90cGxvdApgYGB7ciwgZmlnLndpZHRoPTE2LCBmaWcuaGVpZ2h0PTE2fQpsaWJyYXJ5KHNjQ3VzdG9taXplKQoKQ2x1c3RlcmVkX0RvdFBsb3Qoc2V1cmF0X29iamVjdCA9IEFsbF9zYW1wbGVzX01lcmdlZCwgZmVhdHVyZXMgPSB0b3A1X21hcmtlcnMkZ2VuZSwgaz03LCAsIHhfbGFiX3JvdGF0ZSA9IDkwKQoKYGBgCgoKCgoKCgojIyBTY0N1dG9taXplIGhlYXRtYXAKYGBge3IsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD0xNn0KIyBTY2FsZSB0aGUgZGF0YSBmb3IgdGhlIHNlbGVjdGVkIGZlYXR1cmVzCmFsbGRhdGEgPC0gU2NhbGVEYXRhKAogIEFsbF9zYW1wbGVzX01lcmdlZCwKICBmZWF0dXJlcyA9IGFzLmNoYXJhY3Rlcih1bmlxdWUodG9wNV9tYXJrZXJzJGdlbmUpKSwKICBhc3NheSA9ICJSTkEiCikKCiMgQ3JlYXRlIHRoZSBoZWF0bWFwIG9iamVjdApwIDwtIERvSGVhdG1hcCgKICBhbGxkYXRhLAogIGZlYXR1cmVzID0gYXMuY2hhcmFjdGVyKHVuaXF1ZSh0b3A1X21hcmtlcnMkZ2VuZSkpLAogIGdyb3VwLmJ5ID0gInJlbmFtZWRfY2x1c3RlcnMiLCAgIyB1c2UgeW91ciBhbm5vdGF0ZWQgY2x1c3RlcnMKICBhc3NheSA9ICJSTkEiLAogIHNpemUgPSA0CikgKwogIGdndGl0bGUoIlRvcCA1IE1hcmtlciBHZW5lcyBwZXIgQ2x1c3RlciAoU8OpemFyeSBTeW5kcm9tZSkiKSArCiAgdGhlbWUoCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLCBzaXplID0gMTQpCiAgKQoKIyDinIUgU2F2ZSBhcyBQTkcKZ2dzYXZlKAogIGZpbGVuYW1lID0gIlRvcDVfTWFya2Vyc19IZWF0bWFwLnBuZyIsCiAgcGxvdCA9IHAsCiAgd2lkdGggPSAyMCwKICBoZWlnaHQgPSAxNiwKICBkcGkgPSAzMDAKKQoKIyDinIUgU2F2ZSBhcyBQREYKZ2dzYXZlKAogIGZpbGVuYW1lID0gIlRvcDVfTWFya2Vyc19IZWF0bWFwLnBkZiIsCiAgcGxvdCA9IHAsCiAgd2lkdGggPSAyMCwKICBoZWlnaHQgPSAxNiwKICBkcGkgPSAzMDAKKQoKbWVzc2FnZSgi4pyFIEhlYXRtYXAgc2F2ZWQgc3VjY2Vzc2Z1bGx5IGFzIGJvdGggUE5HIGFuZCBQREYuIikKCmBgYAoKCgoKCgoKCiMjIFNjQ3V0b21pemUgRG90cGxvdApgYGB7ciwgZmlnLndpZHRoPTE2LCBmaWcuaGVpZ2h0PTE2fQpsaWJyYXJ5KFNDcHVicikKCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoU0NwdWJyKQoKIyBFbnN1cmUgeW91ciB0b3A1X21hcmtlcnMgdGFibGUgaGFzIGEgY29sdW1uIHdpdGggcmVuYW1lZCBjbHVzdGVycwojIEhlcmUsIHdlIGFzc3VtZSB5b3UgYWxyZWFkeSBhZGRlZCBpdCBhcyBBbGxfc2FtcGxlc19NZXJnZWQkcmVuYW1lZF9jbHVzdGVycwp0b3A1X21hcmtlcnMgPC0gdG9wNV9tYXJrZXJzICU+JQogIG11dGF0ZShyZW5hbWVkX2NsdXN0ZXIgPSBwYXN0ZTAoY2x1c3RlciwgIjogIiwgSWRlbnRzKEFsbF9zYW1wbGVzX01lcmdlZClbbWF0Y2goY2x1c3RlciwgQWxsX3NhbXBsZXNfTWVyZ2VkJHNldXJhdF9jbHVzdGVycyldKSkKCiMgQ3JlYXRlIGEgbmFtZWQgbGlzdDogbmFtZXMgPSBjbHVzdGVyIGFubm90YXRpb25zLCB2YWx1ZXMgPSB0b3AgNSBnZW5lcwp0b3A1X2xpc3QgPC0gdG9wNV9tYXJrZXJzICU+JQogIGdyb3VwX2J5KHJlbmFtZWRfY2x1c3RlcikgJT4lCiAgc3VtbWFyaXNlKGdlbmVzID0gbGlzdCh1bmlxdWUoZ2VuZSkpKSAlPiUKICBkZWZyYW1lKCkgICMgY29udmVydHMgdGliYmxlIHRvIG5hbWVkIGxpc3QKCiMgU2V0IGlkZW50aXRpZXMgdG8gcmVuYW1lZCBjbHVzdGVycwpJZGVudHMoQWxsX3NhbXBsZXNfTWVyZ2VkKSA8LSBBbGxfc2FtcGxlc19NZXJnZWQkcmVuYW1lZF9jbHVzdGVycwoKIyBEb3RQbG90IHVzaW5nIHRoZSBuYW1lZCBsaXN0CnAgPC0gU0NwdWJyOjpkb19Eb3RQbG90KAogIHNhbXBsZSA9IEFsbF9zYW1wbGVzX01lcmdlZCwKICBmZWF0dXJlcyA9IHRvcDVfbGlzdCwKICBncm91cC5ieSA9ICJyZW5hbWVkX2NsdXN0ZXJzIiwKICBkb3Quc2NhbGUgPSA0LAogIGZvbnQuc2l6ZSA9IDEwCikKCnAKCmdnc2F2ZSgiVG9wNV9NYXJrZXJzX0RvdFBsb3RfZ3JvdXBlZC5wbmciLCBwbG90ID0gcCwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gOCwgZHBpID0gMzAwKQpnZ3NhdmUoIlRvcDVfTWFya2Vyc19Eb3RQbG90X2dyb3VwZWQucGRmIiwgcGxvdCA9IHAsIHdpZHRoID0gMTQsIGhlaWdodCA9IDgpCgpgYGAKCgoKCgoKCgoKIyMgU2NDdXRvbWl6ZSBEb3RwbG90CmBgYHtyLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9MTZ9CmxpYnJhcnkoc2NDdXN0b21pemUpCgpDbHVzdGVyZWRfRG90UGxvdChzZXVyYXRfb2JqZWN0ID0gQWxsX3NhbXBsZXNfTWVyZ2VkLCBmZWF0dXJlcyA9IHRvcDVfbWFya2VycyRnZW5lLCBrPTcsICwgeF9sYWJfcm90YXRlID0gOTApCgpgYGA=