1. load libraries

2. Load Seurat Object


#Load Seurat Object merged from cell lines and a control(PBMC) after filtration
load("../../../0-IMP-OBJECTS/All_Samples_Merged_with_10x_Azitmuth_Annotated_SCT_HPC_without_harmony_integration.robj")

All_samples_Merged
An object of class Seurat 
64169 features across 59355 samples within 6 assays 
Active assay: SCT (27417 features, 3000 variable features)
 3 layers present: counts, data, scale.data
 5 other assays present: RNA, ADT, prediction.score.celltype.l1, prediction.score.celltype.l2, prediction.score.celltype.l3
 4 dimensional reductions calculated: integrated_dr, ref.umap, pca, umap

3. Data PREPARATION and Harmony Integration-1


# Create a new metadata column for grouping
All_samples_Merged$sample_group <- case_when(
  grepl("^L", All_samples_Merged$cell_line) ~ "Cell_Line",
  All_samples_Merged$cell_line == "PBMC" ~ "PBMC",
  All_samples_Merged$cell_line == "PBMC_10x" ~ "PBMC_10x",
  TRUE ~ "Other"
)

# Create the cell line grouping correctly
All_samples_Merged$cell_line_group <- case_when(
  grepl("^L[1-2]", All_samples_Merged$cell_line) ~ "P1",
  grepl("^L[3-4]", All_samples_Merged$cell_line) ~ "P2",
  grepl("^L[5-7]", All_samples_Merged$cell_line) ~ "P3",
  All_samples_Merged$cell_line == "PBMC" ~ "PBMC",
  All_samples_Merged$cell_line == "PBMC_10x" ~ "PBMC_10x",
  TRUE ~ "Other"  # This catches any unexpected values
)

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

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

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

NA
NA
NA

Harmony Visualization-1

library(harmony)

All_samples_Merged <- RunHarmony(
  object = All_samples_Merged,
  group.by.vars = c("cell_line"),
  theta = c(0),
  dims.use = 1:22,  # Increased to capture more variation
  plot_convergence = TRUE
)
Transposing data matrix
Initializing state using k-means centroids initialization
Harmony 1/10
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Harmony 2/10
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Harmony converged after 2 iterations

# Run UMAP on the new Harmony reduction
All_samples_Merged <- RunUMAP(All_samples_Merged, reduction = "harmony", dims = 1:22, reduction.name = "umap.harmony")
Avis : The default method for RunUMAP has changed from calling Python UMAP via reticulate to the R-native UWOT using the cosine metric
To use Python UMAP via reticulate, set umap.method to 'umap-learn' and metric to 'correlation'
This message will be shown once per session11:32:46 UMAP embedding parameters a = 0.9922 b = 1.112
11:32:46 Read 59355 rows and found 22 numeric columns
11:32:46 Using Annoy for neighbor search, n_neighbors = 30
11:32:46 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
11:32:54 Writing NN index file to temp file /tmp/RtmpyqnRv8/file46c3e342a84ee
11:32:54 Searching Annoy index using 1 thread, search_k = 3000
11:33:19 Annoy recall = 100%
11:33:21 Commencing smooth kNN distance calibration using 1 thread with target n_neighbors = 30
11:33:25 Initializing from normalized Laplacian + noise (using RSpectra)
11:33:30 Commencing optimization for 200 epochs, with 2528458 positive edges
Using method 'umap'
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
11:34:08 Optimization finished
# Find neighbors and clusters using the Harmony reduction
All_samples_Merged <- FindNeighbors(All_samples_Merged, reduction = "harmony", dims = 1:22)
Computing nearest neighbor graph
Computing SNN
All_samples_Merged <- FindClusters(All_samples_Merged, resolution = 0.5)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 59355
Number of edges: 1902411

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9303
Number of communities: 19
Elapsed time: 18 seconds
p1 <- DimPlot(All_samples_Merged, reduction = "umap.harmony", group.by = "sample_group", label = T, label.box = T)
p2 <- DimPlot(All_samples_Merged, reduction = "umap.harmony", group.by = "cell_line_group",label = T, label.box = T)
p3 <- DimPlot(All_samples_Merged, reduction = "umap.harmony", group.by = "cell_line",label = T, label.box = T)

p1 + p2 + p3


DimPlot(All_samples_Merged, reduction = "umap.harmony", group.by = "sample_group",label = T, label.box = T)

DimPlot(All_samples_Merged, reduction = "umap.harmony", group.by = "cell_line_group",label = T, label.box = T)

DimPlot(All_samples_Merged, reduction = "umap.harmony", group.by = "cell_line",label = T, label.box = T)




# Compare with original UMAP
p4 <- DimPlot(All_samples_Merged, reduction = "umap", group.by = "cell_line",label = T, label.box = T) + 
  ggtitle("Original Integration - By Cell Line")
p5 <- DimPlot(All_samples_Merged, reduction = "umap", group.by = "seurat_clusters",label = T, label.box = T) + 
  ggtitle("Original Integration - By Clusters")

# Print the plots
p4 + p5


DimPlot(All_samples_Merged, reduction = "umap", group.by = "cell_line",label = T, label.box = T) + 
  ggtitle("Original Integration - By Cell Line")

DimPlot(All_samples_Merged, reduction = "umap", group.by = "seurat_clusters",label = T, label.box = T) + 
  ggtitle("Original Integration - By Clusters")


# Visualize results
p6 <- DimPlot(All_samples_Merged, reduction = "umap.harmony", group.by = "cell_line", label = T, label.box = T) + 
  ggtitle("Harmony Integration - By Cell Line")
p7 <- DimPlot(All_samples_Merged, reduction = "umap.harmony", group.by = "seurat_clusters",label = T, label.box = T) + 
  ggtitle("Harmony Integration - By Clusters")
# Print the plots
p6 + p7


DimPlot(All_samples_Merged, reduction = "umap.harmony", group.by = "cell_line", label = T, label.box = T) + 
  ggtitle("Harmony Integration - By Cell Line")

DimPlot(All_samples_Merged, reduction = "umap.harmony", group.by = "seurat_clusters",label = T, label.box = T) + 
  ggtitle("Harmony Integration - By Clusters")

DimPlot(All_samples_Merged, reduction = "umap.harmony", group.by = "predicted.celltype.l2",label = T, label.box = T) + 
  ggtitle("Harmony Integration - Annotations")

Marker Gene Visualization



# Set marker genes specific to requested immune cell types
myfeatures <- c("CD19", "CD79A", "MS4A1", # B cells
                "CD14", "LYZ", "FCGR3A", # Monocytes
                "CSF1R", "CD68", # Macrophages
                "NKG7", "GNLY", "KIR3DL1", # NK cells
                "MKI67", # Proliferating NK cells
                "CD34", "KIT", # HSPCs
                "CD3E", "CCR7", # T cells
                "SELL", "CD45RO", # Tnaive, Tcm
                "CD44", "CD45RA") # Tem, Temra
# Visualize marker genes for Harmony
FeaturePlot(All_samples_Merged, features = myfeatures, reduction = "umap.harmony", ncol = 4) + 
  ggtitle("Marker Gene Expression - Harmony Integration") +
  NoLegend()
Avis : Could not find CD45RO in the default search locations, found in 'ADT' assay insteadAvis : Could not find CD45RA in the default search locations, found in 'ADT' assay instead

4. Save the Seurat object as an Robj file


#save(All_samples_Merged, file = "../../../0-IMP-OBJECTS/Harmony_integrated_All_samples_Merged_with_PBMC10x.Robj")
LS0tCnRpdGxlOiAiSGFybW9ueSBpbnRlZ3JhdGlvbnMgb2YgUEJNQzEweCBieSBjZWxsX2xpbmUtdGhldGEtMCIKYXV0aG9yOiBOYXNpciBNYWhtb29kIEFiYmFzaQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICAjcm1kZm9ybWF0czo6cmVhZHRoZWRvd24KICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19jb2xsYXBzZWQ6IHRydWUKLS0tCgoKIyAxLiBsb2FkIGxpYnJhcmllcwpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoU2V1cmF0V3JhcHBlcnMpCmxpYnJhcnkoU2V1cmF0T2JqZWN0KQpsaWJyYXJ5KFNldXJhdERhdGEpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGhhcm1vbnkpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShyZXRpY3VsYXRlKQpsaWJyYXJ5KEF6aW11dGgpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoUnRzbmUpCmxpYnJhcnkoaGFybW9ueSkKCm9wdGlvbnMoZnV0dXJlLmdsb2JhbHMubWF4U2l6ZSA9IDFlOSkKCmBgYAoKCgoKIyAyLiBMb2FkIFNldXJhdCBPYmplY3QgCmBgYHtyIGxvYWRfc2V1cmF0fQoKI0xvYWQgU2V1cmF0IE9iamVjdCBtZXJnZWQgZnJvbSBjZWxsIGxpbmVzIGFuZCBhIGNvbnRyb2woUEJNQykgYWZ0ZXIgZmlsdHJhdGlvbgpsb2FkKCIuLi8uLi8uLi8wLUlNUC1PQkpFQ1RTL0FsbF9TYW1wbGVzX01lcmdlZF93aXRoXzEweF9Beml0bXV0aF9Bbm5vdGF0ZWRfU0NUX0hQQ193aXRob3V0X2hhcm1vbnlfaW50ZWdyYXRpb24ucm9iaiIpCgpBbGxfc2FtcGxlc19NZXJnZWQKCgoKYGBgCgoKCiMgMy4gRGF0YSBQUkVQQVJBVElPTiBhbmQgSGFybW9ueSBJbnRlZ3JhdGlvbi0xCmBgYHtyIGRhdGExLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KCiMgQ3JlYXRlIGEgbmV3IG1ldGFkYXRhIGNvbHVtbiBmb3IgZ3JvdXBpbmcKQWxsX3NhbXBsZXNfTWVyZ2VkJHNhbXBsZV9ncm91cCA8LSBjYXNlX3doZW4oCiAgZ3JlcGwoIl5MIiwgQWxsX3NhbXBsZXNfTWVyZ2VkJGNlbGxfbGluZSkgfiAiQ2VsbF9MaW5lIiwKICBBbGxfc2FtcGxlc19NZXJnZWQkY2VsbF9saW5lID09ICJQQk1DIiB+ICJQQk1DIiwKICBBbGxfc2FtcGxlc19NZXJnZWQkY2VsbF9saW5lID09ICJQQk1DXzEweCIgfiAiUEJNQ18xMHgiLAogIFRSVUUgfiAiT3RoZXIiCikKCiMgQ3JlYXRlIHRoZSBjZWxsIGxpbmUgZ3JvdXBpbmcgY29ycmVjdGx5CkFsbF9zYW1wbGVzX01lcmdlZCRjZWxsX2xpbmVfZ3JvdXAgPC0gY2FzZV93aGVuKAogIGdyZXBsKCJeTFsxLTJdIiwgQWxsX3NhbXBsZXNfTWVyZ2VkJGNlbGxfbGluZSkgfiAiUDEiLAogIGdyZXBsKCJeTFszLTRdIiwgQWxsX3NhbXBsZXNfTWVyZ2VkJGNlbGxfbGluZSkgfiAiUDIiLAogIGdyZXBsKCJeTFs1LTddIiwgQWxsX3NhbXBsZXNfTWVyZ2VkJGNlbGxfbGluZSkgfiAiUDMiLAogIEFsbF9zYW1wbGVzX01lcmdlZCRjZWxsX2xpbmUgPT0gIlBCTUMiIH4gIlBCTUMiLAogIEFsbF9zYW1wbGVzX01lcmdlZCRjZWxsX2xpbmUgPT0gIlBCTUNfMTB4IiB+ICJQQk1DXzEweCIsCiAgVFJVRSB+ICJPdGhlciIgICMgVGhpcyBjYXRjaGVzIGFueSB1bmV4cGVjdGVkIHZhbHVlcwopCgpEaW1QbG90KEFsbF9zYW1wbGVzX01lcmdlZCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJzYW1wbGVfZ3JvdXAiLCBsYWJlbCA9IFQsIGxhYmVsLmJveCA9IFQpCkRpbVBsb3QoQWxsX3NhbXBsZXNfTWVyZ2VkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImNlbGxfbGluZV9ncm91cCIsIGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCkKRGltUGxvdChBbGxfc2FtcGxlc19NZXJnZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiY2VsbF9saW5lIiwgbGFiZWwgPSBULCBsYWJlbC5ib3ggPSBUKQoKCgpgYGAKCiMjICBIYXJtb255IFZpc3VhbGl6YXRpb24tMQpgYGB7ciBoYXJtb255LXZpc3VhbGl6YXRpb24xLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KbGlicmFyeShoYXJtb255KQoKQWxsX3NhbXBsZXNfTWVyZ2VkIDwtIFJ1bkhhcm1vbnkoCiAgb2JqZWN0ID0gQWxsX3NhbXBsZXNfTWVyZ2VkLAogIGdyb3VwLmJ5LnZhcnMgPSBjKCJjZWxsX2xpbmUiKSwKICB0aGV0YSA9IGMoMCksCiAgZGltcy51c2UgPSAxOjIyLCAgIyBJbmNyZWFzZWQgdG8gY2FwdHVyZSBtb3JlIHZhcmlhdGlvbgogIHBsb3RfY29udmVyZ2VuY2UgPSBUUlVFCikKCiMgUnVuIFVNQVAgb24gdGhlIG5ldyBIYXJtb255IHJlZHVjdGlvbgpBbGxfc2FtcGxlc19NZXJnZWQgPC0gUnVuVU1BUChBbGxfc2FtcGxlc19NZXJnZWQsIHJlZHVjdGlvbiA9ICJoYXJtb255IiwgZGltcyA9IDE6MjIsIHJlZHVjdGlvbi5uYW1lID0gInVtYXAuaGFybW9ueSIpCgojIEZpbmQgbmVpZ2hib3JzIGFuZCBjbHVzdGVycyB1c2luZyB0aGUgSGFybW9ueSByZWR1Y3Rpb24KQWxsX3NhbXBsZXNfTWVyZ2VkIDwtIEZpbmROZWlnaGJvcnMoQWxsX3NhbXBsZXNfTWVyZ2VkLCByZWR1Y3Rpb24gPSAiaGFybW9ueSIsIGRpbXMgPSAxOjIyKQpBbGxfc2FtcGxlc19NZXJnZWQgPC0gRmluZENsdXN0ZXJzKEFsbF9zYW1wbGVzX01lcmdlZCwgcmVzb2x1dGlvbiA9IDAuNSkKCnAxIDwtIERpbVBsb3QoQWxsX3NhbXBsZXNfTWVyZ2VkLCByZWR1Y3Rpb24gPSAidW1hcC5oYXJtb255IiwgZ3JvdXAuYnkgPSAic2FtcGxlX2dyb3VwIiwgbGFiZWwgPSBULCBsYWJlbC5ib3ggPSBUKQpwMiA8LSBEaW1QbG90KEFsbF9zYW1wbGVzX01lcmdlZCwgcmVkdWN0aW9uID0gInVtYXAuaGFybW9ueSIsIGdyb3VwLmJ5ID0gImNlbGxfbGluZV9ncm91cCIsbGFiZWwgPSBULCBsYWJlbC5ib3ggPSBUKQpwMyA8LSBEaW1QbG90KEFsbF9zYW1wbGVzX01lcmdlZCwgcmVkdWN0aW9uID0gInVtYXAuaGFybW9ueSIsIGdyb3VwLmJ5ID0gImNlbGxfbGluZSIsbGFiZWwgPSBULCBsYWJlbC5ib3ggPSBUKQoKcDEgKyBwMiArIHAzCgpEaW1QbG90KEFsbF9zYW1wbGVzX01lcmdlZCwgcmVkdWN0aW9uID0gInVtYXAuaGFybW9ueSIsIGdyb3VwLmJ5ID0gInNhbXBsZV9ncm91cCIsbGFiZWwgPSBULCBsYWJlbC5ib3ggPSBUKQpEaW1QbG90KEFsbF9zYW1wbGVzX01lcmdlZCwgcmVkdWN0aW9uID0gInVtYXAuaGFybW9ueSIsIGdyb3VwLmJ5ID0gImNlbGxfbGluZV9ncm91cCIsbGFiZWwgPSBULCBsYWJlbC5ib3ggPSBUKQpEaW1QbG90KEFsbF9zYW1wbGVzX01lcmdlZCwgcmVkdWN0aW9uID0gInVtYXAuaGFybW9ueSIsIGdyb3VwLmJ5ID0gImNlbGxfbGluZSIsbGFiZWwgPSBULCBsYWJlbC5ib3ggPSBUKQoKCgojIENvbXBhcmUgd2l0aCBvcmlnaW5hbCBVTUFQCnA0IDwtIERpbVBsb3QoQWxsX3NhbXBsZXNfTWVyZ2VkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImNlbGxfbGluZSIsbGFiZWwgPSBULCBsYWJlbC5ib3ggPSBUKSArIAogIGdndGl0bGUoIk9yaWdpbmFsIEludGVncmF0aW9uIC0gQnkgQ2VsbCBMaW5lIikKcDUgPC0gRGltUGxvdChBbGxfc2FtcGxlc19NZXJnZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIixsYWJlbCA9IFQsIGxhYmVsLmJveCA9IFQpICsgCiAgZ2d0aXRsZSgiT3JpZ2luYWwgSW50ZWdyYXRpb24gLSBCeSBDbHVzdGVycyIpCgojIFByaW50IHRoZSBwbG90cwpwNCArIHA1CgpEaW1QbG90KEFsbF9zYW1wbGVzX01lcmdlZCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJjZWxsX2xpbmUiLGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCkgKyAKICBnZ3RpdGxlKCJPcmlnaW5hbCBJbnRlZ3JhdGlvbiAtIEJ5IENlbGwgTGluZSIpCkRpbVBsb3QoQWxsX3NhbXBsZXNfTWVyZ2VkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsbGFiZWwgPSBULCBsYWJlbC5ib3ggPSBUKSArIAogIGdndGl0bGUoIk9yaWdpbmFsIEludGVncmF0aW9uIC0gQnkgQ2x1c3RlcnMiKQoKIyBWaXN1YWxpemUgcmVzdWx0cwpwNiA8LSBEaW1QbG90KEFsbF9zYW1wbGVzX01lcmdlZCwgcmVkdWN0aW9uID0gInVtYXAuaGFybW9ueSIsIGdyb3VwLmJ5ID0gImNlbGxfbGluZSIsIGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCkgKyAKICBnZ3RpdGxlKCJIYXJtb255IEludGVncmF0aW9uIC0gQnkgQ2VsbCBMaW5lIikKcDcgPC0gRGltUGxvdChBbGxfc2FtcGxlc19NZXJnZWQsIHJlZHVjdGlvbiA9ICJ1bWFwLmhhcm1vbnkiLCBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCkgKyAKICBnZ3RpdGxlKCJIYXJtb255IEludGVncmF0aW9uIC0gQnkgQ2x1c3RlcnMiKQojIFByaW50IHRoZSBwbG90cwpwNiArIHA3CgpEaW1QbG90KEFsbF9zYW1wbGVzX01lcmdlZCwgcmVkdWN0aW9uID0gInVtYXAuaGFybW9ueSIsIGdyb3VwLmJ5ID0gImNlbGxfbGluZSIsIGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCkgKyAKICBnZ3RpdGxlKCJIYXJtb255IEludGVncmF0aW9uIC0gQnkgQ2VsbCBMaW5lIikKRGltUGxvdChBbGxfc2FtcGxlc19NZXJnZWQsIHJlZHVjdGlvbiA9ICJ1bWFwLmhhcm1vbnkiLCBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCkgKyAKICBnZ3RpdGxlKCJIYXJtb255IEludGVncmF0aW9uIC0gQnkgQ2x1c3RlcnMiKQpEaW1QbG90KEFsbF9zYW1wbGVzX01lcmdlZCwgcmVkdWN0aW9uID0gInVtYXAuaGFybW9ueSIsIGdyb3VwLmJ5ID0gInByZWRpY3RlZC5jZWxsdHlwZS5sMiIsbGFiZWwgPSBULCBsYWJlbC5ib3ggPSBUKSArIAogIGdndGl0bGUoIkhhcm1vbnkgSW50ZWdyYXRpb24gLSBBbm5vdGF0aW9ucyIpCgpgYGAKCiMjICBNYXJrZXIgR2VuZSBWaXN1YWxpemF0aW9uCmBgYHtyIGZlYXR1cmVwbG90LWhhcm1vbnksIGZpZy5oZWlnaHQ9MTQsIGZpZy53aWR0aD0xOH0KCgojIFNldCBtYXJrZXIgZ2VuZXMgc3BlY2lmaWMgdG8gcmVxdWVzdGVkIGltbXVuZSBjZWxsIHR5cGVzCm15ZmVhdHVyZXMgPC0gYygiQ0QxOSIsICJDRDc5QSIsICJNUzRBMSIsICMgQiBjZWxscwogICAgICAgICAgICAgICAgIkNEMTQiLCAiTFlaIiwgIkZDR1IzQSIsICMgTW9ub2N5dGVzCiAgICAgICAgICAgICAgICAiQ1NGMVIiLCAiQ0Q2OCIsICMgTWFjcm9waGFnZXMKICAgICAgICAgICAgICAgICJOS0c3IiwgIkdOTFkiLCAiS0lSM0RMMSIsICMgTksgY2VsbHMKICAgICAgICAgICAgICAgICJNS0k2NyIsICMgUHJvbGlmZXJhdGluZyBOSyBjZWxscwogICAgICAgICAgICAgICAgIkNEMzQiLCAiS0lUIiwgIyBIU1BDcwogICAgICAgICAgICAgICAgIkNEM0UiLCAiQ0NSNyIsICMgVCBjZWxscwogICAgICAgICAgICAgICAgIlNFTEwiLCAiQ0Q0NVJPIiwgIyBUbmFpdmUsIFRjbQogICAgICAgICAgICAgICAgIkNENDQiLCAiQ0Q0NVJBIikgIyBUZW0sIFRlbXJhCiMgVmlzdWFsaXplIG1hcmtlciBnZW5lcyBmb3IgSGFybW9ueQpGZWF0dXJlUGxvdChBbGxfc2FtcGxlc19NZXJnZWQsIGZlYXR1cmVzID0gbXlmZWF0dXJlcywgcmVkdWN0aW9uID0gInVtYXAuaGFybW9ueSIsIG5jb2wgPSA0KSArIAogIGdndGl0bGUoIk1hcmtlciBHZW5lIEV4cHJlc3Npb24gLSBIYXJtb255IEludGVncmF0aW9uIikgKwogIE5vTGVnZW5kKCkKYGBgCgoKCiMgNC4gU2F2ZSB0aGUgU2V1cmF0IG9iamVjdCBhcyBhbiBSb2JqIGZpbGUKYGBge3Igc2F2ZVJPQkp9Cgojc2F2ZShBbGxfc2FtcGxlc19NZXJnZWQsIGZpbGUgPSAiLi4vLi4vLi4vMC1JTVAtT0JKRUNUUy9IYXJtb255X2ludGVncmF0ZWRfQWxsX3NhbXBsZXNfTWVyZ2VkX3dpdGhfUEJNQzEweC5Sb2JqIikKCmBgYAoKCgoK