Functions
Data
calculatePathwayScores <- function(pathwayToScore, countsMatrix)
{
pathwayName <- pathwayToScore@setName
print(pathwayName)
pathwayGenes <- pathwayToScore@geneIds
pathwayScoresObject <- getPathwayScores(countsMatrix, pathwayGenes)
suppressWarnings(
if(is.na(pathwayScoresObject)){return (NA)}
)
return(pathwayScoresObject$pathwayScores)
}
GBMSeurat_cancer = readRDS("./Data/GSM3828673_10X_GBM_seurat_cancer.RDS")
genesets_cp <- getGmt("./Data/c2.cp.v2023.2.Hs.symbols.gmt")
metaModuleGenes <- read.table(file = "./Data/MetaModuleGenes_CellPaper.tsv", header = TRUE)
# Creating genesets for the meta module markers
MesLike2GeneSet <- GeneSet(metaModuleGenes[,1])
MesLike1GeneSet <- GeneSet(metaModuleGenes[,2])
ACLikeGeneSet <- GeneSet(metaModuleGenes[!is.na(metaModuleGenes[,3]), 3])
OPCLikeGeneSet <- GeneSet(metaModuleGenes[,4])
NPCLike1GeneSet <- GeneSet(metaModuleGenes[,5])
NPCLike2GeneSet <- GeneSet(metaModuleGenes[,6])
MesLike1GeneSet@setName <- "MesLike1"
MesLike2GeneSet@setName <- "MesLike2"
ACLikeGeneSet@setName <- "ACLike"
OPCLikeGeneSet@setName <- "OPCLike"
NPCLike1GeneSet@setName <- "NPCLike1"
NPCLike2GeneSet@setName <- "NPCLike2"
pathwayScoreLists2 <- pathwayScoreLists[!na.rm(pathwayScoreLists)]
Error in na.rm(pathwayScoreLists) : could not find function "na.rm"
## Adding the pathway name to each list of pathway scores, as the names will be used during the clustering pre-processing steps
for(currPathwayIndex in 1:length(pathwayScoreLists)){
rownames(pathwayScoresMatrix)[currPathwayIndex] <- genesets_cp@.Data[[currPathwayIndex]]@setName
}
GBMSeurat_cancer_sipsic_cp <- CreateSeuratObject(counts = pathwayScoresMatrix)
Warning: Feature names cannot have underscores ('_'), replacing with dashes ('-')
saveRDS(object = GBMSeurat_cancer_sipsic_cp,file = "./Data/GBMSeurat_cancer_sipsic_cp.RDS")
GBMSeurat_cancer_sipsic_cp = readRDS(file = "./Data/GBMSeurat_cancer_sipsic_cp.RDS")
# calculate cancer_markers on original data
cancer_markers = list(
MesLike1 = MesLike1GeneSet@geneIds,
MesLike2 = MesLike2GeneSet@geneIds,
ACLike = ACLikeGeneSet@geneIds,
OPCLike = OPCLikeGeneSet@geneIds,
NPCLike1 = NPCLike1GeneSet@geneIds,
NPCLike2 = NPCLike2GeneSet@geneIds
)
GBMSeurat_cancer <- AddModuleScore(
object = GBMSeurat_cancer,
features = cancer_markers,ctrl = 50,name = "cancer_markers")
Warning: The following features are not present in the object: ERO1L, not searching for symbol synonyms
Warning: The following features are not present in the object: PPAP2B, not searching for symbol synonyms
Warning: The following features are not present in the object: SOX2-OT, LPPR1, not searching for symbol synonyms
Warning: The following features are not present in the object: CD24, GPR56, not searching for symbol synonyms
Warning: The following features are not present in the object: CD24, HMP19, LOC150568, not searching for symbol synonyms
GBMSeurat_cancer_sipsic_cp[["MesLike1"]] = GBMSeurat_cancer$cancer_markers1
GBMSeurat_cancer_sipsic_cp[["MesLike2"]] = GBMSeurat_cancer$cancer_markers2
GBMSeurat_cancer_sipsic_cp[["ACLike"]] = GBMSeurat_cancer$cancer_markers3
GBMSeurat_cancer_sipsic_cp[["OPCLike"]] = GBMSeurat_cancer$cancer_markers4
GBMSeurat_cancer_sipsic_cp[["NPCLike1"]] = GBMSeurat_cancer$cancer_markers5
GBMSeurat_cancer_sipsic_cp[["NPCLike2"]] = GBMSeurat_cancer$cancer_markers6
# further pre-processing steps before moving on to clustering
# GBMSeurat_cancer_sipsic_cp <- FindVariableFeatures(GBMSeurat_cancer_sipsic_cp, selection.method = "vst", nfeatures = 100)
GBMSeurat_cancer_sipsic_cp <- ScaleData(GBMSeurat_cancer_sipsic_cp, rownames(GBMSeurat_cancer_sipsic_cp))
GBMSeurat_cancer_sipsic_cp <- RunPCA(GBMSeurat_cancer_sipsic_cp,features = rownames(GBMSeurat_cancer_sipsic_cp))
# print(GBMSeurat_cancer_sipsic_cp[["pca"]], dims=1:5, nfeatures = 5)
print(ElbowPlot(GBMSeurat_cancer_sipsic_cp))
# Clustering based on the top principal components
GBMSeurat_cancer_sipsic_cp <- FindNeighbors(GBMSeurat_cancer_sipsic_cp, dims = 1:15)
GBMSeurat_cancer_sipsic_cp <- FindClusters(GBMSeurat_cancer_sipsic_cp, resolution = 0.65)
GBMSeurat_cancer_sipsic_cp <- RunUMAP(GBMSeurat_cancer_sipsic_cp, dims = 1:15)
Feature
plots
# Testing for clusters enriched in markers of the different malignant meta-modules
print_tab(FeaturePlot(GBMSeurat_cancer_sipsic_cp, features = "MesLike1"),title = "MESLike1Scores")
MESLike1Scores

print_tab(FeaturePlot(GBMSeurat_cancer_sipsic_cp, features = "MesLike2"),title = "MESLike2Scores")
MESLike2Scores

print_tab(FeaturePlot(GBMSeurat_cancer_sipsic_cp, features = "ACLike"),title = "ACLikeScores")
ACLikeScores

print_tab(FeaturePlot(GBMSeurat_cancer_sipsic_cp, features = "OPCLike"),title = "OPCLikeScores")
OPCLikeScores

print_tab(FeaturePlot(GBMSeurat_cancer_sipsic_cp, features = "NPCLike1"),title = "NPCLike1Scores")
NPCLike1Scores

print_tab(FeaturePlot(GBMSeurat_cancer_sipsic_cp, features = "NPCLike2"),title = "NPCLike2Scores")
NPCLike2Scores

NA
UMAPS
print_tab(DimPlot(GBMSeurat_cancer_sipsic_cp, reduction = "umap"),title = "clusters")
clusters

print_tab(DimPlot(GBMSeurat_cancer_sipsic_cp, reduction = "umap",group.by = "orig.ident"),title = "by patient")
by patient

print_tab(DimPlot(GBMSeurat_cancer_sipsic_cp, reduction = "umap",group.by = "cancer_type"),title = "by cell types")
by cell types

NA
stacked batplot
clusters_and_scores = FetchData(object = GBMSeurat_cancer_sipsic_cp,vars= c("cancer_type","seurat_clusters")) %>% group_by(seurat_clusters,cancer_type) %>% summarise(n_cells = n())%>% mutate(per = 100 *n_cells/sum(n_cells))
`summarise()` has grouped output by 'seurat_clusters'. You can override using the `.groups` argument.
integration_score = clusters_and_scores %>% group_by(seurat_clusters) %>% filter(n_cells == max(n_cells)) %>% pull(per) %>% mean() %>% round(digits = 2)
v_factor_levels <-c( "MesLike1", "MesLike2", "NPCLike1", "NPCLike2", "OPCLike","ACLike")
colors = RColorBrewer::brewer.pal(6, "Paired"); colors[5] = "orange"
p4 = ggplot(data=clusters_and_scores, aes(x=seurat_clusters, y=per, fill=factor(cancer_type, levels = v_factor_levels))) +
geom_bar(stat="identity")+theme_minimal() + scale_fill_manual(values = colors,name = "Cancer type")+ labs(title = "SiPSic CP",subtitle = "integration score=" %s+% integration_score %s+% "%")+ylab("% from cluster")
p4

silhouette score
library(cluster, quietly = TRUE)
dist.matrix <- dist(x = Embeddings(object = GBMSeurat_cancer_sipsic_cp[["umap"]]))
clusters <- GBMSeurat_cancer_sipsic_cp$cancer_type
sil <- silhouette(x = as.numeric(x = as.factor(x = clusters)), dist = dist.matrix)
summary(sil)
Silhouette of 10000 units in 6 clusters from silhouette.default(x = as.numeric(x = as.factor(x = clusters)), from dist = dist.matrix) :
Cluster sizes and average silhouette widths:
2842 2968 1479 1984 537 190
-0.05610227 0.04080160 -0.15085952 -0.23096790 0.42317404 -0.16351472
Individual silhouette widths:
Min. 1st Qu. Median Mean 3rd Qu. Max.
-0.81349 -0.31760 -0.01108 -0.05235 0.18586 0.55330
GBMSeurat_cancer_sipsic_cp$sil = sil[,3]
VlnPlot(object = GBMSeurat_cancer_sipsic_cp,features = "sil",group.by = "cancer_type")

Combine cancer
subtypes
GBMSeurat_cancer_sipsic_cp$cancer_type = GBMSeurat_cancer_sipsic_cp$cancer_type %>% gsub(pattern = "MesLike1|MesLike2",replacement = "MesLike")%>% gsub(pattern = "NPCLike1|NPCLike2",replacement = "NPCLike")
clusters_and_scores = FetchData(object = GBMSeurat_cancer_sipsic_cp,vars= c("cancer_type","seurat_clusters")) %>% group_by(seurat_clusters,cancer_type) %>% summarise(n_cells = n())%>% mutate(per = 100 *n_cells/sum(n_cells))
`summarise()` has grouped output by 'seurat_clusters'. You can override using the `.groups` argument.
integration_score = clusters_and_scores %>% group_by(seurat_clusters) %>% filter(n_cells == max(n_cells)) %>% pull(per) %>% mean() %>% round(digits = 2)
v_factor_levels <-c( "MesLike", "NPCLike", "OPCLike","ACLike")
colors = RColorBrewer::brewer.pal(6, "Paired")[c(2,4,5,6)]; colors[3] = "orange"
p4 = ggplot(data=clusters_and_scores, aes(x=seurat_clusters, y=per, fill=factor(cancer_type, levels = v_factor_levels))) +
geom_bar(stat="identity")+theme_minimal() + scale_fill_manual(values = colors,name = "Cancer type")+ labs(title = "original",subtitle = "integration score=" %s+% integration_score %s+% "%")+ylab("% from cluster")
p4

library(cluster, quietly = TRUE)
dist.matrix <- dist(x = Embeddings(object = GBMSeurat_cancer_sipsic_cp[["umap"]]))
clusters <- GBMSeurat_cancer_sipsic_cp$cancer_type
sil <- silhouette(x = as.numeric(x = as.factor(x = clusters)), dist = dist.matrix)
summary(sil)
Silhouette of 10000 units in 4 clusters from silhouette.default(x = as.numeric(x = as.factor(x = clusters)), from dist = dist.matrix) :
Cluster sizes and average silhouette widths:
2842 4447 2521 190
0.03131561 -0.03240623 0.31179352 -0.04210191
Individual silhouette widths:
Min. 1st Qu. Median Mean 3rd Qu. Max.
-0.68597 -0.15797 0.15271 0.07229 0.27370 0.50481
GBMSeurat_cancer_sipsic_cp$sil = sil[,3]
VlnPlot(object = GBMSeurat_cancer_sipsic_cp,features = "sil",group.by = "cancer_type")

print(DimPlot(GBMSeurat_cancer_sipsic_cp, reduction = "umap",group.by = "cancer_type"))

LS0tCnRpdGxlOiAnYHIgcnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLHJlcGxhY2VtZW50ID0gIiIpYCcgCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19jb2xsYXBzZTogeWVzCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvY19kZXB0aDogMQotLS0KCgoKIyBGdW5jdGlvbnMKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmBgYAoKIyBEYXRhCgpgYGB7cn0KY2FsY3VsYXRlUGF0aHdheVNjb3JlcyA8LSBmdW5jdGlvbihwYXRod2F5VG9TY29yZSwgY291bnRzTWF0cml4KQp7CiAgcGF0aHdheU5hbWUgPC0gcGF0aHdheVRvU2NvcmVAc2V0TmFtZQogIHByaW50KHBhdGh3YXlOYW1lKQogIHBhdGh3YXlHZW5lcyA8LSBwYXRod2F5VG9TY29yZUBnZW5lSWRzCiAgcGF0aHdheVNjb3Jlc09iamVjdCA8LSBnZXRQYXRod2F5U2NvcmVzKGNvdW50c01hdHJpeCwgcGF0aHdheUdlbmVzKSAKICBzdXBwcmVzc1dhcm5pbmdzKAogICAgaWYoaXMubmEocGF0aHdheVNjb3Jlc09iamVjdCkpe3JldHVybiAoTkEpfQogICkKICByZXR1cm4ocGF0aHdheVNjb3Jlc09iamVjdCRwYXRod2F5U2NvcmVzKQp9CkdCTVNldXJhdF9jYW5jZXIgPSByZWFkUkRTKCIuL0RhdGEvR1NNMzgyODY3M18xMFhfR0JNX3NldXJhdF9jYW5jZXIuUkRTIikKZ2VuZXNldHNfY3AgPC0gZ2V0R210KCIuL0RhdGEvYzIuY3AudjIwMjMuMi5Icy5zeW1ib2xzLmdtdCIpCm1ldGFNb2R1bGVHZW5lcyA8LSByZWFkLnRhYmxlKGZpbGUgPSAiLi9EYXRhL01ldGFNb2R1bGVHZW5lc19DZWxsUGFwZXIudHN2IiwgaGVhZGVyID0gVFJVRSkKCiMgQ3JlYXRpbmcgZ2VuZXNldHMgZm9yIHRoZSBtZXRhIG1vZHVsZSBtYXJrZXJzCk1lc0xpa2UyR2VuZVNldCA8LSBHZW5lU2V0KG1ldGFNb2R1bGVHZW5lc1ssMV0pCk1lc0xpa2UxR2VuZVNldCA8LSBHZW5lU2V0KG1ldGFNb2R1bGVHZW5lc1ssMl0pCkFDTGlrZUdlbmVTZXQgPC0gR2VuZVNldChtZXRhTW9kdWxlR2VuZXNbIWlzLm5hKG1ldGFNb2R1bGVHZW5lc1ssM10pLCAzXSkKT1BDTGlrZUdlbmVTZXQgPC0gR2VuZVNldChtZXRhTW9kdWxlR2VuZXNbLDRdKQpOUENMaWtlMUdlbmVTZXQgPC0gR2VuZVNldChtZXRhTW9kdWxlR2VuZXNbLDVdKQpOUENMaWtlMkdlbmVTZXQgPC0gR2VuZVNldChtZXRhTW9kdWxlR2VuZXNbLDZdKQoKTWVzTGlrZTFHZW5lU2V0QHNldE5hbWUgPC0gIk1lc0xpa2UxIgpNZXNMaWtlMkdlbmVTZXRAc2V0TmFtZSA8LSAiTWVzTGlrZTIiCkFDTGlrZUdlbmVTZXRAc2V0TmFtZSA8LSAiQUNMaWtlIgpPUENMaWtlR2VuZVNldEBzZXROYW1lIDwtICJPUENMaWtlIgpOUENMaWtlMUdlbmVTZXRAc2V0TmFtZSA8LSAiTlBDTGlrZTEiCk5QQ0xpa2UyR2VuZVNldEBzZXROYW1lIDwtICJOUENMaWtlMiIKCmBgYAoKYGBge3J9CmdlbmVzZXRzX2NwXzIgPSBnZW5lc2V0c19jcFtuYW1lcyAoZ2VuZXNldHNfY3ApICE9ICJLRUdHX01FRElDVVNfVkFSSUFOVF9JR0hfTU1TRVRfRlVTSU9OX1RPX1RSQU5TQ1JJUFRJT05BTF9BQ1RJVkFUSU9OIl0KZ2VuZXNldHNfY3BfMiA9IGdlbmVzZXRzX2NwWyJLRUdHX01FRElDVVNfVkFSSUFOVF9JR0hfTU1TRVRfRlVTSU9OX1RPX1RSQU5TQ1JJUFRJT05BTF9BQ1RJVkFUSU9OIl0KcGF0aHdheVNjb3JlTGlzdHMgPC0gbGFwcGx5KFggPSBnZW5lc2V0c19jcEAuRGF0YSwgY2FsY3VsYXRlUGF0aHdheVNjb3JlcywgR0JNU2V1cmF0X2NhbmNlckBhc3NheXMkUk5BQGNvdW50cyAlPiUgYXMubWF0cml4KCkgJT4lIE1hdHJpeChzcGFyc2UgPSBUKSkKcGF0aHdheVNjb3JlTGlzdHMgPC0gcGF0aHdheVNjb3JlTGlzdHNbIWlzLm5hKHBhdGh3YXlTY29yZUxpc3RzKV0gI3JlbW92ZSBOQSBwYXRod2F5cwpwYXRod2F5U2NvcmVzTWF0cml4IDwtIGFzLmRhdGEuZnJhbWUoZG8uY2FsbCgicmJpbmQiLCBwYXRod2F5U2NvcmVMaXN0cykpCgpgYGAKCmBgYHtyfQojIyBBZGRpbmcgdGhlIHBhdGh3YXkgbmFtZSB0byBlYWNoIGxpc3Qgb2YgcGF0aHdheSBzY29yZXMsIGFzIHRoZSBuYW1lcyB3aWxsIGJlIHVzZWQgZHVyaW5nIHRoZSBjbHVzdGVyaW5nIHByZS1wcm9jZXNzaW5nIHN0ZXBzCmZvcihjdXJyUGF0aHdheUluZGV4IGluIDE6bGVuZ3RoKHBhdGh3YXlTY29yZUxpc3RzKSl7CiAgcm93bmFtZXMocGF0aHdheVNjb3Jlc01hdHJpeClbY3VyclBhdGh3YXlJbmRleF0gPC0gZ2VuZXNldHNfY3BALkRhdGFbW2N1cnJQYXRod2F5SW5kZXhdXUBzZXROYW1lCn0KCkdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBwYXRod2F5U2NvcmVzTWF0cml4KQpgYGAKYGBge3J9CiMgc2F2ZVJEUyhvYmplY3QgPSBHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCxmaWxlID0gIi4vRGF0YS9HQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcC5SRFMiKQpgYGAKCgpgYGB7cn0KR0JNU2V1cmF0X2NhbmNlcl9zaXBzaWNfY3AgPSByZWFkUkRTKGZpbGUgPSAiLi9EYXRhL0dCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwLlJEUyIpCmBgYAoKCmBgYHtyfQojIGNhbGN1bGF0ZSBjYW5jZXJfbWFya2VycyBvbiBvcmlnaW5hbCBkYXRhCmNhbmNlcl9tYXJrZXJzID0gbGlzdCgKICBNZXNMaWtlMSA9IE1lc0xpa2UxR2VuZVNldEBnZW5lSWRzLAogIE1lc0xpa2UyID0gTWVzTGlrZTJHZW5lU2V0QGdlbmVJZHMsCiAgQUNMaWtlID0gQUNMaWtlR2VuZVNldEBnZW5lSWRzLAogIE9QQ0xpa2UgPSBPUENMaWtlR2VuZVNldEBnZW5lSWRzLAogIE5QQ0xpa2UxID0gTlBDTGlrZTFHZW5lU2V0QGdlbmVJZHMsCiAgTlBDTGlrZTIgPSBOUENMaWtlMkdlbmVTZXRAZ2VuZUlkcwopCgpHQk1TZXVyYXRfY2FuY2VyIDwtIEFkZE1vZHVsZVNjb3JlKAogIG9iamVjdCA9IEdCTVNldXJhdF9jYW5jZXIsCiAgZmVhdHVyZXMgPSBjYW5jZXJfbWFya2VycyxjdHJsID0gNTAsbmFtZSA9ICJjYW5jZXJfbWFya2VycyIpCgpHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcFtbIk1lc0xpa2UxIl1dID0gR0JNU2V1cmF0X2NhbmNlciRjYW5jZXJfbWFya2VyczEKR0JNU2V1cmF0X2NhbmNlcl9zaXBzaWNfY3BbWyJNZXNMaWtlMiJdXSA9IEdCTVNldXJhdF9jYW5jZXIkY2FuY2VyX21hcmtlcnMyCkdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwW1siQUNMaWtlIl1dID0gR0JNU2V1cmF0X2NhbmNlciRjYW5jZXJfbWFya2VyczMKR0JNU2V1cmF0X2NhbmNlcl9zaXBzaWNfY3BbWyJPUENMaWtlIl1dID0gR0JNU2V1cmF0X2NhbmNlciRjYW5jZXJfbWFya2VyczQKR0JNU2V1cmF0X2NhbmNlcl9zaXBzaWNfY3BbWyJOUENMaWtlMSJdXSA9IEdCTVNldXJhdF9jYW5jZXIkY2FuY2VyX21hcmtlcnM1CkdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwW1siTlBDTGlrZTIiXV0gPSBHQk1TZXVyYXRfY2FuY2VyJGNhbmNlcl9tYXJrZXJzNgoKCmNhbmNlcl9zY29yZXMgID0gRmV0Y2hEYXRhKG9iamVjdCA9IEdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwLHZhcnMgPSBjKCJNZXNMaWtlMSIsIk1lc0xpa2UyIiwiQUNMaWtlIiwiT1BDTGlrZSIsIk5QQ0xpa2UxIiwiTlBDTGlrZTIiKSkKY2FuY2VyX3Njb3JlcyRhc3NpZ25tZW50PC0gY29sbmFtZXMoY2FuY2VyX3Njb3JlcylbbWF4LmNvbChjYW5jZXJfc2NvcmVzLCB0aWVzLm1ldGhvZCA9ICJmaXJzdCIpXQpHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCAlPD4lIEFkZE1ldGFEYXRhKG1ldGFkYXRhID0gY2FuY2VyX3Njb3JlcyRhc3NpZ25tZW50LGNvbC5uYW1lID0gImNhbmNlcl90eXBlIikKYGBgCgoKYGBge3J9CiMgZnVydGhlciBwcmUtcHJvY2Vzc2luZyBzdGVwcyBiZWZvcmUgbW92aW5nIG9uIHRvIGNsdXN0ZXJpbmcKIyBHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLCBuZmVhdHVyZXMgPSAxMDApCkdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwIDwtIFNjYWxlRGF0YShHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCwgcm93bmFtZXMoR0JNU2V1cmF0X2NhbmNlcl9zaXBzaWNfY3ApKQpHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCA8LSBSdW5QQ0EoR0JNU2V1cmF0X2NhbmNlcl9zaXBzaWNfY3AsZmVhdHVyZXMgPSAgcm93bmFtZXMoR0JNU2V1cmF0X2NhbmNlcl9zaXBzaWNfY3ApKQojIHByaW50KEdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwW1sicGNhIl1dLCBkaW1zPTE6NSwgbmZlYXR1cmVzID0gNSkKcHJpbnQoRWxib3dQbG90KEdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwKSkKYGBgCmBgYHtyfQojIENsdXN0ZXJpbmcgYmFzZWQgb24gdGhlIHRvcCBwcmluY2lwYWwgY29tcG9uZW50cwpHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCA8LSBGaW5kTmVpZ2hib3JzKEdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwLCBkaW1zID0gMToxNSkKR0JNU2V1cmF0X2NhbmNlcl9zaXBzaWNfY3AgPC0gRmluZENsdXN0ZXJzKEdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwLCByZXNvbHV0aW9uID0gMC42NSkKR0JNU2V1cmF0X2NhbmNlcl9zaXBzaWNfY3AgPC0gUnVuVU1BUChHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCwgZGltcyA9IDE6MTUpCmBgYAoKCiMgRmVhdHVyZSBwbG90cyB7LnRhYnNldH0KYGBge3IgcmVzdWx0cz0nYXNpcyd9CiMgVGVzdGluZyBmb3IgY2x1c3RlcnMgZW5yaWNoZWQgaW4gbWFya2VycyBvZiB0aGUgZGlmZmVyZW50IG1hbGlnbmFudCBtZXRhLW1vZHVsZXMKcHJpbnRfdGFiKEZlYXR1cmVQbG90KEdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwLCBmZWF0dXJlcyA9ICJNZXNMaWtlMSIpLHRpdGxlID0gIk1FU0xpa2UxU2NvcmVzIikKcHJpbnRfdGFiKEZlYXR1cmVQbG90KEdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwLCBmZWF0dXJlcyA9ICJNZXNMaWtlMiIpLHRpdGxlID0gIk1FU0xpa2UyU2NvcmVzIikKcHJpbnRfdGFiKEZlYXR1cmVQbG90KEdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwLCBmZWF0dXJlcyA9ICJBQ0xpa2UiKSx0aXRsZSA9ICJBQ0xpa2VTY29yZXMiKQpwcmludF90YWIoRmVhdHVyZVBsb3QoR0JNU2V1cmF0X2NhbmNlcl9zaXBzaWNfY3AsIGZlYXR1cmVzID0gIk9QQ0xpa2UiKSx0aXRsZSA9ICJPUENMaWtlU2NvcmVzIikKcHJpbnRfdGFiKEZlYXR1cmVQbG90KEdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwLCBmZWF0dXJlcyA9ICJOUENMaWtlMSIpLHRpdGxlID0gIk5QQ0xpa2UxU2NvcmVzIikKcHJpbnRfdGFiKEZlYXR1cmVQbG90KEdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwLCBmZWF0dXJlcyA9ICJOUENMaWtlMiIpLHRpdGxlID0gIk5QQ0xpa2UyU2NvcmVzIikKYGBgCiMgVU1BUFMgey50YWJzZXR9CmBgYHtyIHJlc3VsdHM9J2FzaXMnfQpwcmludF90YWIoRGltUGxvdChHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCwgcmVkdWN0aW9uID0gInVtYXAiKSx0aXRsZSA9ICJjbHVzdGVycyIpCnByaW50X3RhYihEaW1QbG90KEdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwLCByZWR1Y3Rpb24gPSAidW1hcCIsZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpLHRpdGxlID0gImJ5IHBhdGllbnQiKQpwcmludF90YWIoRGltUGxvdChHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCwgcmVkdWN0aW9uID0gInVtYXAiLGdyb3VwLmJ5ID0gImNhbmNlcl90eXBlIiksdGl0bGUgPSAiYnkgY2VsbCB0eXBlcyIpCgpgYGAKIyBzdGFja2VkIGJhdHBsb3QKYGBge3J9CmNsdXN0ZXJzX2FuZF9zY29yZXMgPSBGZXRjaERhdGEob2JqZWN0ID0gR0JNU2V1cmF0X2NhbmNlcl9zaXBzaWNfY3AsdmFycz0gYygiY2FuY2VyX3R5cGUiLCJzZXVyYXRfY2x1c3RlcnMiKSkgJT4lICBncm91cF9ieShzZXVyYXRfY2x1c3RlcnMsY2FuY2VyX3R5cGUpICU+JSAgc3VtbWFyaXNlKG5fY2VsbHMgPSBuKCkpJT4lIG11dGF0ZShwZXIgPSAgMTAwICpuX2NlbGxzL3N1bShuX2NlbGxzKSkKCmludGVncmF0aW9uX3Njb3JlID0gY2x1c3RlcnNfYW5kX3Njb3JlcyAlPiUgIGdyb3VwX2J5KHNldXJhdF9jbHVzdGVycykgJT4lIGZpbHRlcihuX2NlbGxzID09IG1heChuX2NlbGxzKSkgJT4lIHB1bGwocGVyKSAlPiUgbWVhbigpICU+JSByb3VuZChkaWdpdHMgPSAyKQoKdl9mYWN0b3JfbGV2ZWxzIDwtYyggIk1lc0xpa2UxIiwgIk1lc0xpa2UyIiwgIk5QQ0xpa2UxIiwgIk5QQ0xpa2UyIiwgIk9QQ0xpa2UiLCJBQ0xpa2UiKQpjb2xvcnMgPSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoNiwgIlBhaXJlZCIpOyBjb2xvcnNbNV0gPSAib3JhbmdlIgpwNCA9IGdncGxvdChkYXRhPWNsdXN0ZXJzX2FuZF9zY29yZXMsIGFlcyh4PXNldXJhdF9jbHVzdGVycywgeT1wZXIsIGZpbGw9ZmFjdG9yKGNhbmNlcl90eXBlLCBsZXZlbHMgPSB2X2ZhY3Rvcl9sZXZlbHMpKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikrdGhlbWVfbWluaW1hbCgpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3JzLG5hbWUgID0gIkNhbmNlciB0eXBlIikrIGxhYnModGl0bGUgPSAiU2lQU2ljIENQIixzdWJ0aXRsZSA9ICJpbnRlZ3JhdGlvbiBzY29yZT0iICVzKyUgaW50ZWdyYXRpb25fc2NvcmUgJXMrJSAiJSIpK3lsYWIoIiUgZnJvbSBjbHVzdGVyIikKcDQKYGBgCiMgc2lsaG91ZXR0ZSBzY29yZQpgYGB7cn0KbGlicmFyeShjbHVzdGVyLCBxdWlldGx5ID0gVFJVRSkKZGlzdC5tYXRyaXggPC0gZGlzdCh4ID0gRW1iZWRkaW5ncyhvYmplY3QgPSBHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcFtbInVtYXAiXV0pKQpjbHVzdGVycyA8LSBHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCRjYW5jZXJfdHlwZQpzaWwgPC0gc2lsaG91ZXR0ZSh4ID0gYXMubnVtZXJpYyh4ID0gYXMuZmFjdG9yKHggPSBjbHVzdGVycykpLCBkaXN0ID0gZGlzdC5tYXRyaXgpCnN1bW1hcnkoc2lsKQpHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCRzaWwgPSBzaWxbLDNdClZsblBsb3Qob2JqZWN0ID0gR0JNU2V1cmF0X2NhbmNlcl9zaXBzaWNfY3AsZmVhdHVyZXMgPSAic2lsIixncm91cC5ieSA9ICJjYW5jZXJfdHlwZSIpCmBgYAojIENvbWJpbmUgY2FuY2VyIHN1YnR5cGVzCmBgYHtyfQpHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCRjYW5jZXJfdHlwZSA9IEdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwJGNhbmNlcl90eXBlICU+JSBnc3ViKHBhdHRlcm4gPSAiTWVzTGlrZTF8TWVzTGlrZTIiLHJlcGxhY2VtZW50ID0gIk1lc0xpa2UiKSU+JSBnc3ViKHBhdHRlcm4gPSAiTlBDTGlrZTF8TlBDTGlrZTIiLHJlcGxhY2VtZW50ID0gIk5QQ0xpa2UiKQpgYGAKCmBgYHtyfQpjbHVzdGVyc19hbmRfc2NvcmVzID0gRmV0Y2hEYXRhKG9iamVjdCA9IEdCTVNldXJhdF9jYW5jZXJfc2lwc2ljX2NwLHZhcnM9IGMoImNhbmNlcl90eXBlIiwic2V1cmF0X2NsdXN0ZXJzIikpICU+JSAgZ3JvdXBfYnkoc2V1cmF0X2NsdXN0ZXJzLGNhbmNlcl90eXBlKSAlPiUgIHN1bW1hcmlzZShuX2NlbGxzID0gbigpKSU+JSBtdXRhdGUocGVyID0gIDEwMCAqbl9jZWxscy9zdW0obl9jZWxscykpCgppbnRlZ3JhdGlvbl9zY29yZSA9IGNsdXN0ZXJzX2FuZF9zY29yZXMgJT4lICBncm91cF9ieShzZXVyYXRfY2x1c3RlcnMpICU+JSBmaWx0ZXIobl9jZWxscyA9PSBtYXgobl9jZWxscykpICU+JSBwdWxsKHBlcikgJT4lIG1lYW4oKSAlPiUgcm91bmQoZGlnaXRzID0gMikKCnZfZmFjdG9yX2xldmVscyA8LWMoICJNZXNMaWtlIiwgIk5QQ0xpa2UiLCAiT1BDTGlrZSIsIkFDTGlrZSIpCmNvbG9ycyA9IFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCg2LCAiUGFpcmVkIilbYygyLDQsNSw2KV07IGNvbG9yc1szXSA9ICJvcmFuZ2UiCnA0ID0gZ2dwbG90KGRhdGE9Y2x1c3RlcnNfYW5kX3Njb3JlcywgYWVzKHg9c2V1cmF0X2NsdXN0ZXJzLCB5PXBlciwgZmlsbD1mYWN0b3IoY2FuY2VyX3R5cGUsIGxldmVscyA9IHZfZmFjdG9yX2xldmVscykpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSt0aGVtZV9taW5pbWFsKCkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMsbmFtZSAgPSAiQ2FuY2VyIHR5cGUiKSsgbGFicyh0aXRsZSA9ICJvcmlnaW5hbCIsc3VidGl0bGUgPSAiaW50ZWdyYXRpb24gc2NvcmU9IiAlcyslIGludGVncmF0aW9uX3Njb3JlICVzKyUgIiUiKSt5bGFiKCIlIGZyb20gY2x1c3RlciIpCnA0CmBgYApgYGB7cn0KbGlicmFyeShjbHVzdGVyLCBxdWlldGx5ID0gVFJVRSkKZGlzdC5tYXRyaXggPC0gZGlzdCh4ID0gRW1iZWRkaW5ncyhvYmplY3QgPSBHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcFtbInVtYXAiXV0pKQpjbHVzdGVycyA8LSBHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCRjYW5jZXJfdHlwZQpzaWwgPC0gc2lsaG91ZXR0ZSh4ID0gYXMubnVtZXJpYyh4ID0gYXMuZmFjdG9yKHggPSBjbHVzdGVycykpLCBkaXN0ID0gZGlzdC5tYXRyaXgpCnN1bW1hcnkoc2lsKQpHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCRzaWwgPSBzaWxbLDNdClZsblBsb3Qob2JqZWN0ID0gR0JNU2V1cmF0X2NhbmNlcl9zaXBzaWNfY3AsZmVhdHVyZXMgPSAic2lsIixncm91cC5ieSA9ICJjYW5jZXJfdHlwZSIpCmBgYApgYGB7cn0KcHJpbnQoRGltUGxvdChHQk1TZXVyYXRfY2FuY2VyX3NpcHNpY19jcCwgcmVkdWN0aW9uID0gInVtYXAiLGdyb3VwLmJ5ID0gImNhbmNlcl90eXBlIikpCgpgYGAKCjxzY3JpcHQgc3JjPSJodHRwczovL2h5cG90aGVzLmlzL2VtYmVkLmpzIiBhc3luYz48L3NjcmlwdD4KCg==