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=