1 load libraries

2 Load Seurat Object


All_samples_Merged <- readRDS("../../../PHD_3rd_YEAR_Analysis/0-Seurat_RDS_OBJECT_FINAL/All_samples_Merged_with_Renamed_Clusters_Cell_state-03-12-2025.rds.rds")

ss <- All_samples_Merged

rm(All_samples_Merged)

gc()
             used   (Mb) gc trigger    (Mb)   max used   (Mb)
Ncells    9806904  523.8   14314937   764.6   14314937  764.6
Vcells 1234922613 9421.8 1533268137 11698.0 1235214144 9424.0
ss
An object of class Seurat 
62900 features across 49305 samples within 6 assays 
Active assay: RNA (36601 features, 0 variable features)
 2 layers present: data, counts
 5 other assays present: ADT, prediction.score.celltype.l1, prediction.score.celltype.l2, prediction.score.celltype.l3, SCT
 5 dimensional reductions calculated: integrated_dr, ref.umap, pca, umap, harmony

3 Create the base UMAP using SCpubr with ALL original populations

# =============================================================================
# SIMPLIFIED APPROACH: Label only cell line regions (L1/L2, L3/L4, L5/L6/L7)
# with P1, P2, P3, and normal region with "Normal CD4 T cells"
# =============================================================================

# Step 1: Base plot
p <- SCpubr::do_DimPlot(
  sample = seurat_obj,
  group.by = "orig.ident",
  label = FALSE,
  legend.position = "right",
   plot.axes = T,
  pt.size = 0.8,
  raster = FALSE
)

# Step 2: UMAP coords (lowercase columns)
umap_coords <- as.data.frame(seurat_obj@reductions$umap@cell.embeddings)
umap_coords$orig.ident <- seurat_obj$orig.ident

# Step 3: Calculate centroids
label_positions <- data.frame(label = character(), umap_1 = numeric(), umap_2 = numeric())

# P1: L1+L2
p1_cells <- umap_coords[umap_coords$orig.ident %in% c("L1", "L2"), ]
if(nrow(p1_cells) > 0) label_positions <- rbind(label_positions,
  data.frame(label = "P1", 
             umap_1 = median(p1_cells$umap_1, na.rm = TRUE),
             umap_2 = median(p1_cells$umap_2, na.rm = TRUE)))

# P2: L3+L4
p2_cells <- umap_coords[umap_coords$orig.ident %in% c("L3", "L4"), ]
if(nrow(p2_cells) > 0) label_positions <- rbind(label_positions,
  data.frame(label = "P2", 
             umap_1 = median(p2_cells$umap_1, na.rm = TRUE),
             umap_2 = median(p2_cells$umap_2, na.rm = TRUE)))

# P3: L5+L6+L7
p3_cells <- umap_coords[umap_coords$orig.ident %in% c("L5", "L6", "L7"), ]
if(nrow(p3_cells) > 0) label_positions <- rbind(label_positions,
  data.frame(label = "P3", 
             umap_1 = median(p3_cells$umap_1, na.rm = TRUE),
             umap_2 = median(p3_cells$umap_2, na.rm = TRUE)))

# Normal CD4 T cells
normal_cells <- umap_coords[umap_coords$orig.ident %in% c("CD4T_lab", "CD4T_10x"), ]
if(nrow(normal_cells) > 0) label_positions <- rbind(label_positions,
  data.frame(label = "Normal CD4 T cells", 
             umap_1 = median(normal_cells$umap_1, na.rm = TRUE),
             umap_2 = median(normal_cells$umap_2, na.rm = TRUE)))

print("Label positions:")
[1] "Label positions:"
print(label_positions)

# Step 4: Final plot with UMAP1/UMAP2 axes
p_final <- p + 
  geom_text_repel(
    data = label_positions,
    aes(x = umap_1, y = umap_2, label = label),
    inherit.aes = FALSE,
    size = 6, fontface = "bold", color = "black",
    bg.color = "white", bg.r = 0.15,
    box.padding = 0.5, point.padding = 0.5,
    min.segment.length = 0, max.overlaps = Inf
  ) +
  xlab("UMAP_1") + 
  ylab("UMAP_2") +
  theme(axis.title = element_text(size = 14, face = "bold"))

# Display
print(p_final)


# Step 5: Save BOTH PDF and PNG
ggsave("UMAP_P1_P2_P3_Normal.pdf", p_final, width = 12, height = 8, dpi = 300)
ggsave("UMAP_P1_P2_P3_Normal.png", p_final, width = 12, height = 8, dpi = 300)

cat("\n✅ Saved both files:\n")

✅ Saved both files:
cat("   📄 UMAP_P1_P2_P3_Normal.pdf\n")
   📄 UMAP_P1_P2_P3_Normal.pdf
cat("   🖼️  UMAP_P1_P2_P3_Normal.png\n")
   🖼️  UMAP_P1_P2_P3_Normal.png

4 Create the base UMAP using SCpubr with ALL original populations

5 Create the base UMAP using SCpubr with ALL original populations



# =============================================================================
# SIMPLIFIED: Show only cluster numbers, no P1/P2/P3 labels
# =============================================================================

# Step 1: Base plot with cluster labels
p_cluster <- SCpubr::do_DimPlot(
  sample = seurat_obj,
  group.by = "seurat_clusters",
  label = TRUE,
  legend.position = "right",
  label.color = "white",
  plot.axes = TRUE,
  pt.size = 0.8,
  raster = FALSE
)

# Step 2: Add axis labels (no region annotations)
p2 <- p_cluster + 
  xlab("UMAP_1") + 
  ylab("UMAP_2") +
  theme(axis.title = element_text(size = 14, face = "bold"))

# Step 3: Display
print(p2)


# Step 4: Save both PDF and PNG
ggsave("UMAP_Clusters_only.pdf", p2, width = 12, height = 8, dpi = 300)
ggsave("UMAP_Clusters_only.png", p2, width = 12, height = 8, dpi = 300)

cat("\n✅ Saved both files:\n")

✅ Saved both files:
cat("   📄 UMAP_Clusters_only.pdf\n")
   📄 UMAP_Clusters_only.pdf
cat("   🖼️  UMAP_Clusters_only.png\n")
   🖼️  UMAP_Clusters_only.png

6 Th Polarization


genes <- list(
  Th1 = c("TBX21", "CXCR3", "CXCR6", "IFNG"),
  Th2 = c("GATA3", "CCR4", "PTGDR2", "IL4"),
  Th17_Th22 = c("RORC", "CCR6", "CCR10", "RORA", "STAT3", "IL17A", "IL21"),
  Treg = c("FOXP3", "IL2RA", "CTLA4")
)

p2 <- SCpubr::do_DotPlot(
  sample = seurat_obj, group.by = "orig.ident",
  features = genes
)

p2

7 Th Polarization-flip


genes <- list(
  Th1 = c("TBX21", "CXCR3", "CXCR6", "IFNG"),
  Th2 = c("GATA3", "CCR4", "PTGDR2", "IL4"),
  Th17_Th22 = c("RORC", "CCR6", "CCR10", "RORA", "STAT3", "IL17A", "IL21"),
  Treg = c("FOXP3", "IL2RA", "CTLA4")
)


p3 <- SCpubr::do_DotPlot(sample = seurat_obj, group.by = "orig.ident",flip = T,
                        features = genes)

p3

LS0tCnRpdGxlOiAiVU1BUF92aXN1YWxpemF0aW9uIgphdXRob3I6ICJOYXNpciBNYWhtb29kIEFiYmFzaSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgdGhlbWU6IGpvdXJuYWwKLS0tCgoKIyBsb2FkIGxpYnJhcmllcwpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoU0NwdWJyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dyZXBlbCkKCgpgYGAKCiMgTG9hZCBTZXVyYXQgT2JqZWN0IApgYGB7cn0KCkFsbF9zYW1wbGVzX01lcmdlZCA8LSByZWFkUkRTKCIuLi8uLi8uLi9QSERfM3JkX1lFQVJfQW5hbHlzaXMvMC1TZXVyYXRfUkRTX09CSkVDVF9GSU5BTC9BbGxfc2FtcGxlc19NZXJnZWRfd2l0aF9SZW5hbWVkX0NsdXN0ZXJzX0NlbGxfc3RhdGUtMDMtMTItMjAyNS5yZHMucmRzIikKCnNzIDwtIEFsbF9zYW1wbGVzX01lcmdlZAoKcm0oQWxsX3NhbXBsZXNfTWVyZ2VkKQoKZ2MoKQoKc2V1cmF0X29iaiA8LSBzcwpybShzcykKYGBgCgojIENyZWF0ZSB0aGUgYmFzZSBVTUFQIHVzaW5nIFNDcHViciB3aXRoIEFMTCBvcmlnaW5hbCBwb3B1bGF0aW9ucwpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OH0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIFNJTVBMSUZJRUQgQVBQUk9BQ0g6IExhYmVsIG9ubHkgY2VsbCBsaW5lIHJlZ2lvbnMgKEwxL0wyLCBMMy9MNCwgTDUvTDYvTDcpCiMgd2l0aCBQMSwgUDIsIFAzLCBhbmQgbm9ybWFsIHJlZ2lvbiB3aXRoICJOb3JtYWwgQ0Q0IFQgY2VsbHMiCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCiMgU3RlcCAxOiBCYXNlIHBsb3QKcCA8LSBTQ3B1YnI6OmRvX0RpbVBsb3QoCiAgc2FtcGxlID0gc2V1cmF0X29iaiwKICBncm91cC5ieSA9ICJvcmlnLmlkZW50IiwKICBsYWJlbCA9IEZBTFNFLAogIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgIHBsb3QuYXhlcyA9IFQsCiAgcHQuc2l6ZSA9IDAuOCwKICByYXN0ZXIgPSBGQUxTRQopCgojIFN0ZXAgMjogVU1BUCBjb29yZHMgKGxvd2VyY2FzZSBjb2x1bW5zKQp1bWFwX2Nvb3JkcyA8LSBhcy5kYXRhLmZyYW1lKHNldXJhdF9vYmpAcmVkdWN0aW9ucyR1bWFwQGNlbGwuZW1iZWRkaW5ncykKdW1hcF9jb29yZHMkb3JpZy5pZGVudCA8LSBzZXVyYXRfb2JqJG9yaWcuaWRlbnQKCiMgU3RlcCAzOiBDYWxjdWxhdGUgY2VudHJvaWRzCmxhYmVsX3Bvc2l0aW9ucyA8LSBkYXRhLmZyYW1lKGxhYmVsID0gY2hhcmFjdGVyKCksIHVtYXBfMSA9IG51bWVyaWMoKSwgdW1hcF8yID0gbnVtZXJpYygpKQoKIyBQMTogTDErTDIKcDFfY2VsbHMgPC0gdW1hcF9jb29yZHNbdW1hcF9jb29yZHMkb3JpZy5pZGVudCAlaW4lIGMoIkwxIiwgIkwyIiksIF0KaWYobnJvdyhwMV9jZWxscykgPiAwKSBsYWJlbF9wb3NpdGlvbnMgPC0gcmJpbmQobGFiZWxfcG9zaXRpb25zLAogIGRhdGEuZnJhbWUobGFiZWwgPSAiUDEiLCAKICAgICAgICAgICAgIHVtYXBfMSA9IG1lZGlhbihwMV9jZWxscyR1bWFwXzEsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICB1bWFwXzIgPSBtZWRpYW4ocDFfY2VsbHMkdW1hcF8yLCBuYS5ybSA9IFRSVUUpKSkKCiMgUDI6IEwzK0w0CnAyX2NlbGxzIDwtIHVtYXBfY29vcmRzW3VtYXBfY29vcmRzJG9yaWcuaWRlbnQgJWluJSBjKCJMMyIsICJMNCIpLCBdCmlmKG5yb3cocDJfY2VsbHMpID4gMCkgbGFiZWxfcG9zaXRpb25zIDwtIHJiaW5kKGxhYmVsX3Bvc2l0aW9ucywKICBkYXRhLmZyYW1lKGxhYmVsID0gIlAyIiwgCiAgICAgICAgICAgICB1bWFwXzEgPSBtZWRpYW4ocDJfY2VsbHMkdW1hcF8xLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgdW1hcF8yID0gbWVkaWFuKHAyX2NlbGxzJHVtYXBfMiwgbmEucm0gPSBUUlVFKSkpCgojIFAzOiBMNStMNitMNwpwM19jZWxscyA8LSB1bWFwX2Nvb3Jkc1t1bWFwX2Nvb3JkcyRvcmlnLmlkZW50ICVpbiUgYygiTDUiLCAiTDYiLCAiTDciKSwgXQppZihucm93KHAzX2NlbGxzKSA+IDApIGxhYmVsX3Bvc2l0aW9ucyA8LSByYmluZChsYWJlbF9wb3NpdGlvbnMsCiAgZGF0YS5mcmFtZShsYWJlbCA9ICJQMyIsIAogICAgICAgICAgICAgdW1hcF8xID0gbWVkaWFuKHAzX2NlbGxzJHVtYXBfMSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgIHVtYXBfMiA9IG1lZGlhbihwM19jZWxscyR1bWFwXzIsIG5hLnJtID0gVFJVRSkpKQoKIyBOb3JtYWwgQ0Q0IFQgY2VsbHMKbm9ybWFsX2NlbGxzIDwtIHVtYXBfY29vcmRzW3VtYXBfY29vcmRzJG9yaWcuaWRlbnQgJWluJSBjKCJDRDRUX2xhYiIsICJDRDRUXzEweCIpLCBdCmlmKG5yb3cobm9ybWFsX2NlbGxzKSA+IDApIGxhYmVsX3Bvc2l0aW9ucyA8LSByYmluZChsYWJlbF9wb3NpdGlvbnMsCiAgZGF0YS5mcmFtZShsYWJlbCA9ICJOb3JtYWwgQ0Q0IFQgY2VsbHMiLCAKICAgICAgICAgICAgIHVtYXBfMSA9IG1lZGlhbihub3JtYWxfY2VsbHMkdW1hcF8xLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgdW1hcF8yID0gbWVkaWFuKG5vcm1hbF9jZWxscyR1bWFwXzIsIG5hLnJtID0gVFJVRSkpKQoKcHJpbnQoIkxhYmVsIHBvc2l0aW9uczoiKQpwcmludChsYWJlbF9wb3NpdGlvbnMpCgojIFN0ZXAgNDogRmluYWwgcGxvdCB3aXRoIFVNQVAxL1VNQVAyIGF4ZXMKcF9maW5hbCA8LSBwICsgCiAgZ2VvbV90ZXh0X3JlcGVsKAogICAgZGF0YSA9IGxhYmVsX3Bvc2l0aW9ucywKICAgIGFlcyh4ID0gdW1hcF8xLCB5ID0gdW1hcF8yLCBsYWJlbCA9IGxhYmVsKSwKICAgIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICBzaXplID0gNiwgZm9udGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gImJsYWNrIiwKICAgIGJnLmNvbG9yID0gIndoaXRlIiwgYmcuciA9IDAuMTUsCiAgICBib3gucGFkZGluZyA9IDAuNSwgcG9pbnQucGFkZGluZyA9IDAuNSwKICAgIG1pbi5zZWdtZW50Lmxlbmd0aCA9IDAsIG1heC5vdmVybGFwcyA9IEluZgogICkgKwogIHhsYWIoIlVNQVBfMSIpICsgCiAgeWxhYigiVU1BUF8yIikgKwogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSkKCiMgRGlzcGxheQpwcmludChwX2ZpbmFsKQoKIyBTdGVwIDU6IFNhdmUgQk9USCBQREYgYW5kIFBORwpnZ3NhdmUoIlVNQVBfUDFfUDJfUDNfTm9ybWFsLnBkZiIsIHBfZmluYWwsIHdpZHRoID0gMTIsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKZ2dzYXZlKCJVTUFQX1AxX1AyX1AzX05vcm1hbC5wbmciLCBwX2ZpbmFsLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4LCBkcGkgPSAzMDApCgpjYXQoIlxu4pyFIFNhdmVkIGJvdGggZmlsZXM6XG4iKQpjYXQoIiAgIPCfk4QgVU1BUF9QMV9QMl9QM19Ob3JtYWwucGRmXG4iKQpjYXQoIiAgIPCflrzvuI8gIFVNQVBfUDFfUDJfUDNfTm9ybWFsLnBuZ1xuIikKYGBgCgojIENyZWF0ZSB0aGUgYmFzZSBVTUFQIHVzaW5nIFNDcHViciB3aXRoIEFMTCBvcmlnaW5hbCBwb3B1bGF0aW9ucwpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OH0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIFNJTVBMSUZJRUQgQVBQUk9BQ0g6IExhYmVsIG9ubHkgY2VsbCBsaW5lIHJlZ2lvbnMgKEwxL0wyLCBMMy9MNCwgTDUvTDYvTDcpCiMgd2l0aCBQMSwgUDIsIFAzLCBhbmQgbm9ybWFsIHJlZ2lvbiB3aXRoICJOb3JtYWwgQ0Q0IFQgY2VsbHMiCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCiMgU3RlcCAxOiBCYXNlIHBsb3QKcF9jbHVzdGVyIDwtIFNDcHVicjo6ZG9fRGltUGxvdCgKICBzYW1wbGUgPSBzZXVyYXRfb2JqLAogIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsCiAgbGFiZWwgPSBULAogIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgIHBsb3QuYXhlcyA9IFQsCiAgcHQuc2l6ZSA9IDAuOCwKICByYXN0ZXIgPSBGQUxTRQopCgoKIyBTdGVwIDQ6IEZpbmFsIHBsb3Qgd2l0aCBVTUFQMS9VTUFQMiBheGVzCnBfZmluYWwyIDwtIHBfY2x1c3RlciArIAogIGdlb21fdGV4dF9yZXBlbCgKICAgIGRhdGEgPSBsYWJlbF9wb3NpdGlvbnMsCiAgICBhZXMoeCA9IHVtYXBfMSwgeSA9IHVtYXBfMiwgbGFiZWwgPSBsYWJlbCksCiAgICBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgc2l6ZSA9IDYsIGZvbnRmYWNlID0gImJvbGQiLCBjb2xvciA9ICJibGFjayIsCiAgICBiZy5jb2xvciA9ICJ3aGl0ZSIsIGJnLnIgPSAwLjE1LAogICAgYm94LnBhZGRpbmcgPSAwLjUsIHBvaW50LnBhZGRpbmcgPSAwLjUsCiAgICBtaW4uc2VnbWVudC5sZW5ndGggPSAwLCBtYXgub3ZlcmxhcHMgPSBJbmYKICApICsKICB4bGFiKCJVTUFQXzEiKSArIAogIHlsYWIoIlVNQVBfMiIpICsKICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFjZSA9ICJib2xkIikpCgojIERpc3BsYXkKcHJpbnQocF9maW5hbDIpCgojIFN0ZXAgNTogU2F2ZSBCT1RIIFBERiBhbmQgUE5HCmdnc2F2ZSgiVU1BUF9QMV9QMl9QM19Ob3JtYWwucGRmIiwgcF9maW5hbDIsIHdpZHRoID0gMTIsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKZ2dzYXZlKCJVTUFQX1AxX1AyX1AzX05vcm1hbC5wbmciLCBwX2ZpbmFsMiwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gOCwgZHBpID0gMzAwKQoKY2F0KCJcbuKchSBTYXZlZCBib3RoIGZpbGVzOlxuIikKY2F0KCIgICDwn5OEIFVNQVBfQ2x1c3RlcnMucGRmXG4iKQpjYXQoIiAgIPCflrzvuI8gIFVNQVBDbHVzdGVybC5wbmdcbiIpCmBgYAojIENyZWF0ZSB0aGUgYmFzZSBVTUFQIHVzaW5nIFNDcHViciB3aXRoIEFMTCBvcmlnaW5hbCBwb3B1bGF0aW9ucwpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OH0KCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgU0lNUExJRklFRDogU2hvdyBvbmx5IGNsdXN0ZXIgbnVtYmVycywgbm8gUDEvUDIvUDMgbGFiZWxzCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCiMgU3RlcCAxOiBCYXNlIHBsb3Qgd2l0aCBjbHVzdGVyIGxhYmVscwpwX2NsdXN0ZXIgPC0gU0NwdWJyOjpkb19EaW1QbG90KAogIHNhbXBsZSA9IHNldXJhdF9vYmosCiAgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwKICBsYWJlbCA9IFRSVUUsCiAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwKICBsYWJlbC5jb2xvciA9ICJ3aGl0ZSIsCiAgcGxvdC5heGVzID0gVFJVRSwKICBwdC5zaXplID0gMC44LAogIHJhc3RlciA9IEZBTFNFCikKCiMgU3RlcCAyOiBBZGQgYXhpcyBsYWJlbHMgKG5vIHJlZ2lvbiBhbm5vdGF0aW9ucykKcDIgPC0gcF9jbHVzdGVyICsgCiAgeGxhYigiVU1BUF8xIikgKyAKICB5bGFiKCJVTUFQXzIiKSArCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpKQoKIyBTdGVwIDM6IERpc3BsYXkKcHJpbnQocDIpCgojIFN0ZXAgNDogU2F2ZSBib3RoIFBERiBhbmQgUE5HCmdnc2F2ZSgiVU1BUF9DbHVzdGVyc19vbmx5LnBkZiIsIHAyLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4LCBkcGkgPSAzMDApCmdnc2F2ZSgiVU1BUF9DbHVzdGVyc19vbmx5LnBuZyIsIHAyLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4LCBkcGkgPSAzMDApCgpjYXQoIlxu4pyFIFNhdmVkIGJvdGggZmlsZXM6XG4iKQpjYXQoIiAgIPCfk4QgVU1BUF9DbHVzdGVyc19vbmx5LnBkZlxuIikKY2F0KCIgICDwn5a877iPICBVTUFQX0NsdXN0ZXJzX29ubHkucG5nXG4iKQoKCmBgYAoKIyBUaCBQb2xhcml6YXRpb24KYGBge3IsICwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Nn0KCmdlbmVzIDwtIGxpc3QoCiAgVGgxID0gYygiVEJYMjEiLCAiQ1hDUjMiLCAiQ1hDUjYiLCAiSUZORyIpLAogIFRoMiA9IGMoIkdBVEEzIiwgIkNDUjQiLCAiUFRHRFIyIiwgIklMNCIpLAogIFRoMTdfVGgyMiA9IGMoIlJPUkMiLCAiQ0NSNiIsICJDQ1IxMCIsICJST1JBIiwgIlNUQVQzIiwgIklMMTdBIiwgIklMMjEiKSwKICBUcmVnID0gYygiRk9YUDMiLCAiSUwyUkEiLCAiQ1RMQTQiKQopCgpwMiA8LSBTQ3B1YnI6OmRvX0RvdFBsb3QoCiAgc2FtcGxlID0gc2V1cmF0X29iaiwgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIsCiAgZmVhdHVyZXMgPSBnZW5lcwopCgpwMgpgYGAKCgojIFRoIFBvbGFyaXphdGlvbi1mbGlwCmBgYHtyLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD04fQoKZ2VuZXMgPC0gbGlzdCgKICBUaDEgPSBjKCJUQlgyMSIsICJDWENSMyIsICJDWENSNiIsICJJRk5HIiksCiAgVGgyID0gYygiR0FUQTMiLCAiQ0NSNCIsICJQVEdEUjIiLCAiSUw0IiksCiAgVGgxN19UaDIyID0gYygiUk9SQyIsICJDQ1I2IiwgIkNDUjEwIiwgIlJPUkEiLCAiU1RBVDMiLCAiSUwxN0EiLCAiSUwyMSIpLAogIFRyZWcgPSBjKCJGT1hQMyIsICJJTDJSQSIsICJDVExBNCIpCikKCgpwMyA8LSBTQ3B1YnI6OmRvX0RvdFBsb3Qoc2FtcGxlID0gc2V1cmF0X29iaiwgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIsZmxpcCA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gZ2VuZXMpCgpwMwpgYGAK