Data
library(GSEABase)
GBMSeurat_cancer = readRDS("./Data/GSM3828673_10X_GBM_seurat_cancer.RDS")
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"
# split the dataset into a list of two seurat objects (stim and CTRL)
ifnb.list <- SplitObject(GBMSeurat_cancer, split.by = "orig.ident")
# normalize and identify variable features for each dataset independently
ifnb.list <- lapply(X = ifnb.list, FUN = function(x) {
x <- NormalizeData(x)
x <- FindVariableFeatures(x, selection.method = "vst", nfeatures = 2000)
})
# select features that are repeatedly variable across datasets for integration
features <- SelectIntegrationFeatures(object.list = ifnb.list)
immune.anchors <- FindIntegrationAnchors(object.list = ifnb.list, anchor.features = features)
# this command creates an 'integrated' data assay
GBM.combined <- IntegrateData(anchorset = immune.anchors)
GBM.combined <- ScaleData(GBM.combined, verbose = FALSE)
GBM.combined <- RunPCA(GBM.combined, npcs = 30, verbose = FALSE)
ElbowPlot(GBM.combined,ndims = 30)
GBM.combined <- RunUMAP(GBM.combined, reduction = "pca", dims = 1:15)
GBM.combined <- FindNeighbors(GBM.combined, reduction = "pca", dims = 1:15)
GBM.combined <- FindClusters(GBM.combined, resolution = 0.8)
UMAP
after integration
print_tab(DimPlot(GBM.combined, reduction = "umap", group.by = "orig.ident"),title = "by patient")
by patient

print_tab(DimPlot(GBM.combined, reduction = "umap",label = T),title = "by cluster")
by cluster

NA
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
GBM.combined[["MesLike1"]] = GBMSeurat_cancer$cancer_markers1
GBM.combined[["MesLike2"]] = GBMSeurat_cancer$cancer_markers2
GBM.combined[["ACLike"]] = GBMSeurat_cancer$cancer_markers3
GBM.combined[["OPCLike"]] = GBMSeurat_cancer$cancer_markers4
GBM.combined[["NPCLike1"]] = GBMSeurat_cancer$cancer_markers5
GBM.combined[["NPCLike2"]] = GBMSeurat_cancer$cancer_markers6
cancer_scores = FetchData(object = GBM.combined,vars = c("MesLike1","MesLike2","ACLike","OPCLike","NPCLike1","NPCLike2"))
cancer_scores$assignment<- colnames(cancer_scores)[max.col(cancer_scores, ties.method = "first")]
GBM.combined %<>% AddMetaData(metadata = cancer_scores$assignment,col.name = "cancer_type")
DimPlot(GBM.combined,group.by = "cancer_type",label = T)

DimPlot(GBM.combined,group.by = "seurat_clusters",label = T)

Feature
plots
# Testing for clusters enriched in markers of the different malignant meta-modules
print_tab(FeaturePlot(GBM.combined, features = "MesLike1"),title = "MESLike1Scores")
MESLike1Scores

print_tab(FeaturePlot(GBM.combined, features = "MesLike2"),title = "MESLike2Scores")
MESLike2Scores

print_tab(FeaturePlot(GBM.combined, features = "ACLike"),title = "ACLikeScores")
ACLikeScores

print_tab(FeaturePlot(GBM.combined, features = "OPCLike"),title = "OPCLikeScores")
OPCLikeScores

print_tab(FeaturePlot(GBM.combined, features = "NPCLike1"),title = "NPCLike1Scores")
NPCLike1Scores

print_tab(FeaturePlot(GBM.combined, features = "NPCLike2"),title = "NPCLike2Scores")
NPCLike2Scores

NA
pdf(file = "./Figures/Seurat/UMAP_orig.ident.pdf", onefile = FALSE)
DimPlot(GBM.combined, reduction = "umap", group.by = "orig.ident")
dev.off()
pdf(file = "./Figures/Seurat/UMAP_clustering.pdf", onefile = FALSE)
DimPlot(GBM.combined, reduction = "umap")
dev.off()
pdf(file = "./Figures/Seurat/macrophageScore.pdf", onefile = FALSE)
print(FeaturePlot(GBM.combined, features = "macrophageScore"))
dev.off()
pdf(file = "./Figures/Seurat/TCellScores.pdf", onefile = FALSE)
print(FeaturePlot(GBM.combined, features = "TCellScores"))
dev.off()
pdf(file = "./Figures/Seurat/oligodendrocyteScores.pdf", onefile = FALSE)
print(FeaturePlot(GBM.combined, features = "oligodendrocyteScores"))
dev.off()
pdf(file = "./Figures/Seurat/oligodendrocyteScores.pdf", onefile = FALSE)
print(FeaturePlot(GBM.combined, features = "oligodendrocyteScores"))
dev.off()
pdf(file = "./Figures/Seurat/MESLike1Scores.pdf", onefile = FALSE)
print(FeaturePlot(GBM.combined, features = "MESLike1Scores"))
dev.off()
pdf(file = "./Figures/Seurat/MESLike2Scores.pdf", onefile = FALSE)
print(FeaturePlot(GBM.combined, features = "MESLike2Scores"))
dev.off()
pdf(file = "./Figures/Seurat/ACLikeScores.pdf", onefile = FALSE)
print(FeaturePlot(GBM.combined, features = "ACLikeScores"))
dev.off()
pdf(file = "./Figures/Seurat/OPCLikeScores.pdf", onefile = FALSE)
print(FeaturePlot(GBM.combined, features = "OPCLikeScores"))
dev.off()
pdf(file = "./Figures/Seurat/NPCLike1Scores.pdf", onefile = FALSE)
print(FeaturePlot(GBM.combined, features = "NPCLike1Scores"))
dev.off()
pdf(file = "./Figures/Seurat/NPCLike2Scores.pdf", onefile = FALSE)
print(FeaturePlot(GBM.combined, features = "NPCLike2Scores"))
dev.off()
Combine cancer
subtypes
GBM.combined$cancer_type = GBM.combined$cancer_type %>% gsub(pattern = "MesLike1|MesLike2",replacement = "MesLike")%>% gsub(pattern = "NPCLike1|NPCLike2",replacement = "NPCLike")
clusters_and_scores = FetchData(object = GBM.combined,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 = "SiPSiC",subtitle = "integration score=" %s+% integration_score %s+% "%")+ylab("% from cluster")
p4

library(cluster, quietly = TRUE)
dist.matrix <- dist(x = Embeddings(object = GBM.combined[["umap"]]))
clusters <- GBM.combined$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.08230674 -0.05689546 0.20320946 0.22385726
Individual silhouette widths:
Min. 1st Qu. Median Mean 3rd Qu. Max.
-0.669558 -0.240717 0.068159 0.006789 0.231651 0.521249
GBM.combined$sil = sil[,3]
VlnPlot(object = GBM.combined,features = "sil",group.by = "cancer_type")

ggarrange(p1,p2,p3,common.legend = T)
# Measuring the percentage of cells of each malignant meta module that are in a single cluster
clusterAssignments <- Idents(GBM.combined)
ScoresAndClusters <- cbind(clusterAssignments, MacrophageScores = macrophageScores$pathwayScores, OligodecdrocytesScores = oligodendrocyteScores$pathwayScores,
MESLike1Scores = MesLike1Scores$pathwayScores, MESLike2Scores = MesLike2Scores$pathwayScores,
ACLikeScores = ACLikeScores$pathwayScores, OPCLikeScores = OPCLikeScores$pathwayScores,
NPCLike1Scores = NPCLike1Scores$pathwayScores, NPCLike2Scores = NPCLike2Scores$pathwayScores,
TCellScores = TCellScores$pathwayScores, as.data.frame(GBM.combined@meta.data$orig.ident))
colnames(ScoresAndClusters)[colnames(ScoresAndClusters) == "GBM.combined@meta.data$orig.ident"] <- "Patient"
# Checking the proportions of the different patients in each cluster
clusterByPatientIDs <- ScoresAndClusters[,c("clusterAssignments", "Patient")]
clusterByPatientIDs <- clusterByPatientIDs[order(clusterByPatientIDs$clusterAssignments),]
for(currCluster in 0:max(as.numeric(as.character(clusterByPatientIDs[,"clusterAssignments"]))))
{
currClusterCells <- clusterByPatientIDs[clusterByPatientIDs[,"clusterAssignments"] == currCluster,"Patient"]
cat("\n", "Cluster number: ", currCluster, " Total Cells: ", sum(clusterByPatientIDs[,"clusterAssignments"] == currCluster), "\n")
print(table(currClusterCells))
}
NPCThreshold <- ACLikeThreshold <- OPCLikeThreshold <- MESLikesThreshold <- 0.13
OligodendrocyteThreshold <- 1.5
MacrophageThreshold <- TCellThreshold <- 0.05
macrophageOverThresh <- ScoresAndClusters[ScoresAndClusters[,"MacrophageScores"] > MacrophageThreshold,c("clusterAssignments", "MacrophageScores")]
print("Macrophage cells: Total over threshold = ")
print(nrow(macrophageOverThresh))
print(table(macrophageOverThresh[,"clusterAssignments"]))
cat("\n\n")
TCellsOverThresh <- ScoresAndClusters[ScoresAndClusters[,"TCellScores"] > TCellThreshold,c("clusterAssignments", "TCellScores")]
print("T cells: Total over threshold = ")
print(nrow(TCellsOverThresh))
print(table(TCellsOverThresh[,"clusterAssignments"]))
cat("\n\n")
OligodendrocytesOverThresh <- ScoresAndClusters[ScoresAndClusters[,"OligodecdrocytesScores"] > OligodendrocyteThreshold,c("clusterAssignments", "OligodecdrocytesScores")]
print("Oligodendrocyte cells: Total over threshold = ")
print(nrow(OligodendrocytesOverThresh))
print(table(OligodendrocytesOverThresh[,"clusterAssignments"]))
cat("\n\n")
NPCs1OverThresh <- ScoresAndClusters[ScoresAndClusters[,"NPCLike1Scores"] > NPCThreshold,c("clusterAssignments", "NPCLike1Scores")]
print("NPC1 cells: Total over threshold = ")
print(nrow(NPCs1OverThresh))
print(table(NPCs1OverThresh[,"clusterAssignments"]))
cat("\n\n")
NPCs2OverThresh <- ScoresAndClusters[ScoresAndClusters[,"NPCLike2Scores"] > NPCThreshold,c("clusterAssignments", "NPCLike2Scores")]
print("NPC2 cells: Total over threshold = ")
print(nrow(NPCs2OverThresh))
print(table(NPCs2OverThresh[,"clusterAssignments"]))
cat("\n\n")
ACLikesOverThresh <- ScoresAndClusters[ScoresAndClusters[,"ACLikeScores"] > ACLikeThreshold,c("clusterAssignments", "ACLikeScores")]
print("ACLike cells: Total over threshold = ")
print(nrow(ACLikesOverThresh))
print(table(ACLikesOverThresh[,"clusterAssignments"]))
cat("\n\n")
OPCLikesOverThresh <- ScoresAndClusters[ScoresAndClusters[,"OPCLikeScores"] > OPCLikeThreshold,c("clusterAssignments", "OPCLikeScores")]
print("OPCLike cells: Total over threshold = ")
print(nrow(OPCLikesOverThresh))
print(table(OPCLikesOverThresh[,"clusterAssignments"]))
cat("\n\n")
MES1OverThresh <- ScoresAndClusters[ScoresAndClusters[,"MESLike1Scores"] > MESLikesThreshold,c("clusterAssignments", "MESLike1Scores")]
print("MES1 cells: Total over threshold = ")
print(nrow(MES1OverThresh))
print(table(MES1OverThresh[,"clusterAssignments"]))
cat("\n\n")
MES2OverThresh <- ScoresAndClusters[ScoresAndClusters[,"MESLike2Scores"] > MESLikesThreshold,c("clusterAssignments", "MESLike2Scores")]
print("MES2 cells: Total over threshold = ")
print(nrow(MES2OverThresh))
print(table(MES2OverThresh[,"clusterAssignments"]))
cat("\n\n")
# Checking the distribution of each patient's malignant cells across clusters
isCellMalignant <- ScoresAndClusters[,"NPCLike1Scores"] > NPCThreshold |
ScoresAndClusters[,"NPCLike2Scores"] > NPCThreshold |
ScoresAndClusters[,"ACLikeScores"] > ACLikeThreshold |
ScoresAndClusters[,"OPCLikeScores"] > OPCLikeThreshold |
ScoresAndClusters[,"MESLike1Scores"] > MESLikesThreshold |
ScoresAndClusters[,"MESLike2Scores"] > MESLikesThreshold
malignantCellsOnly <- ScoresAndClusters[isCellMalignant,]
for (currPatient in levels(malignantCellsOnly$Patient))
{
currPatientMaligCells <- malignantCellsOnly[malignantCellsOnly[,"Patient"] == currPatient,]
cat("\n", "Patient number: ", currPatient, "\nTotal number of cells: ", nrow(currPatientMaligCells), "\n")
print(table(currPatientMaligCells[,"clusterAssignments"]))
# Finding the NPC-Like cells of this patient only
currPatientNPCLike1 <- currPatientMaligCells[currPatientMaligCells[,"NPCLike1Scores"] > NPCThreshold,]
currPatientNPCLike2 <- currPatientMaligCells[currPatientMaligCells[,"NPCLike2Scores"] > NPCThreshold,]
cat("\n", "NPC-Like1 cells in this cluster:\n")
cat("Total number of NPC-Like1: ", nrow(currPatientNPCLike1))
print(table(currPatientNPCLike1[, "clusterAssignments"]))
cat("\n\n", "NPC-Like2 cells in this cluster:\n")
cat("Total number of NPC-Like2: ", nrow(currPatientNPCLike2))
print(table(currPatientNPCLike2[, "clusterAssignments"]))
cat("\n\n")
}
clusters_assignment_results = data.frame(cluster = 0:9)
clusters_assignment_results[,"Macrophages"] = table(macrophageOverThresh[,"clusterAssignments"]) %>% as.data.frame() %>% pull(Freq)
clusters_assignment_results[,"TCellS"] = table(TCellsOverThresh[,"clusterAssignments"]) %>% as.data.frame() %>% pull(Freq)
clusters_assignment_results[,"Oligodecdrocytes"] = table(OligodendrocytesOverThresh[,"clusterAssignments"]) %>% as.data.frame() %>% pull(Freq)
clusters_assignment_results[,"NPC1"] = table(NPCs1OverThresh[,"clusterAssignments"]) %>% as.data.frame() %>% pull(Freq)
clusters_assignment_results[,"NPC2"] = table(NPCs2OverThresh[,"clusterAssignments"]) %>% as.data.frame() %>% pull(Freq)
clusters_assignment_results[,"ACLike"] = table(ACLikesOverThresh[,"clusterAssignments"]) %>% as.data.frame() %>% pull(Freq)
clusters_assignment_results[,"OPCLike"] = table(OPCLikesOverThresh[,"clusterAssignments"]) %>% as.data.frame() %>% pull(Freq)
clusters_assignment_results[,"MES1"] = table(MES1OverThresh[,"clusterAssignments"]) %>% as.data.frame() %>% pull(Freq)
clusters_assignment_results[,"MES2"] = table(MES2OverThresh[,"clusterAssignments"]) %>% as.data.frame() %>% pull(Freq)
df2 = reshape2::melt(clusters_assignment_results, id.vars = c("cluster"), variable.name = "cell_type", value.name = "n_cells")
ggplot(data=df2, aes(x=cluster, y=n_cells, fill=cell_type)) +
geom_bar(stat="identity")+theme_minimal() + scale_x_continuous(breaks=0:13)+ scale_fill_brewer(palette="Paired")
LS0tCnRpdGxlOiAnYHIgcnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLHJlcGxhY2VtZW50ID0gIiIpYCcgCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19jb2xsYXBzZTogeWVzCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvY19kZXB0aDogMQogICAgc2VsZl9jb250YWluZWQ6IHRydWUKLS0tCgoKCiMgRnVuY3Rpb25zCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpgYGAKCiMgRGF0YQoKYGBge3J9CmxpYnJhcnkoR1NFQUJhc2UpCkdCTVNldXJhdF9jYW5jZXIgPSByZWFkUkRTKCIuL0RhdGEvR1NNMzgyODY3M18xMFhfR0JNX3NldXJhdF9jYW5jZXIuUkRTIikKbWV0YU1vZHVsZUdlbmVzIDwtIHJlYWQudGFibGUoZmlsZSA9ICIuL0RhdGEvTWV0YU1vZHVsZUdlbmVzX0NlbGxQYXBlci50c3YiLCBoZWFkZXIgPSBUUlVFKQoKIyBDcmVhdGluZyBnZW5lc2V0cyBmb3IgdGhlIG1ldGEgbW9kdWxlIG1hcmtlcnMKTWVzTGlrZTJHZW5lU2V0IDwtIEdlbmVTZXQobWV0YU1vZHVsZUdlbmVzWywxXSkKTWVzTGlrZTFHZW5lU2V0IDwtIEdlbmVTZXQobWV0YU1vZHVsZUdlbmVzWywyXSkKQUNMaWtlR2VuZVNldCA8LSBHZW5lU2V0KG1ldGFNb2R1bGVHZW5lc1shaXMubmEobWV0YU1vZHVsZUdlbmVzWywzXSksIDNdKQpPUENMaWtlR2VuZVNldCA8LSBHZW5lU2V0KG1ldGFNb2R1bGVHZW5lc1ssNF0pCk5QQ0xpa2UxR2VuZVNldCA8LSBHZW5lU2V0KG1ldGFNb2R1bGVHZW5lc1ssNV0pCk5QQ0xpa2UyR2VuZVNldCA8LSBHZW5lU2V0KG1ldGFNb2R1bGVHZW5lc1ssNl0pCgpNZXNMaWtlMUdlbmVTZXRAc2V0TmFtZSA8LSAiTWVzTGlrZTEiCk1lc0xpa2UyR2VuZVNldEBzZXROYW1lIDwtICJNZXNMaWtlMiIKQUNMaWtlR2VuZVNldEBzZXROYW1lIDwtICJBQ0xpa2UiCk9QQ0xpa2VHZW5lU2V0QHNldE5hbWUgPC0gIk9QQ0xpa2UiCk5QQ0xpa2UxR2VuZVNldEBzZXROYW1lIDwtICJOUENMaWtlMSIKTlBDTGlrZTJHZW5lU2V0QHNldE5hbWUgPC0gIk5QQ0xpa2UyIgpgYGAKCgoKYGBge3J9CgojIHNwbGl0IHRoZSBkYXRhc2V0IGludG8gYSBsaXN0IG9mIHR3byBzZXVyYXQgb2JqZWN0cyAoc3RpbSBhbmQgQ1RSTCkKaWZuYi5saXN0IDwtIFNwbGl0T2JqZWN0KEdCTVNldXJhdF9jYW5jZXIsIHNwbGl0LmJ5ID0gIm9yaWcuaWRlbnQiKQojIG5vcm1hbGl6ZSBhbmQgaWRlbnRpZnkgdmFyaWFibGUgZmVhdHVyZXMgZm9yIGVhY2ggZGF0YXNldCBpbmRlcGVuZGVudGx5CmlmbmIubGlzdCA8LSBsYXBwbHkoWCA9IGlmbmIubGlzdCwgRlVOID0gZnVuY3Rpb24oeCkgewp4IDwtIE5vcm1hbGl6ZURhdGEoeCkKeCA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyh4LCBzZWxlY3Rpb24ubWV0aG9kID0gInZzdCIsIG5mZWF0dXJlcyA9IDIwMDApCn0pCiMgc2VsZWN0IGZlYXR1cmVzIHRoYXQgYXJlIHJlcGVhdGVkbHkgdmFyaWFibGUgYWNyb3NzIGRhdGFzZXRzIGZvciBpbnRlZ3JhdGlvbgpmZWF0dXJlcyA8LSBTZWxlY3RJbnRlZ3JhdGlvbkZlYXR1cmVzKG9iamVjdC5saXN0ID0gaWZuYi5saXN0KQppbW11bmUuYW5jaG9ycyA8LSBGaW5kSW50ZWdyYXRpb25BbmNob3JzKG9iamVjdC5saXN0ID0gaWZuYi5saXN0LCBhbmNob3IuZmVhdHVyZXMgPSBmZWF0dXJlcykKIyB0aGlzIGNvbW1hbmQgY3JlYXRlcyBhbiAnaW50ZWdyYXRlZCcgZGF0YSBhc3NheQpHQk0uY29tYmluZWQgPC0gSW50ZWdyYXRlRGF0YShhbmNob3JzZXQgPSBpbW11bmUuYW5jaG9ycykKR0JNLmNvbWJpbmVkIDwtIFNjYWxlRGF0YShHQk0uY29tYmluZWQsIHZlcmJvc2UgPSBGQUxTRSkKR0JNLmNvbWJpbmVkIDwtIFJ1blBDQShHQk0uY29tYmluZWQsIG5wY3MgPSAzMCwgdmVyYm9zZSA9IEZBTFNFKQpFbGJvd1Bsb3QoR0JNLmNvbWJpbmVkLG5kaW1zID0gMzApCkdCTS5jb21iaW5lZCA8LSBSdW5VTUFQKEdCTS5jb21iaW5lZCwgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOjE1KQpHQk0uY29tYmluZWQgPC0gRmluZE5laWdoYm9ycyhHQk0uY29tYmluZWQsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gMToxNSkKR0JNLmNvbWJpbmVkIDwtIEZpbmRDbHVzdGVycyhHQk0uY29tYmluZWQsIHJlc29sdXRpb24gPSAwLjgpCmBgYAoKIyBVTUFQIGFmdGVyIGludGVncmF0aW9uIHsudGFic2V0fQpgYGB7ciByZXN1bHRzPSdhc2lzJ30KcHJpbnRfdGFiKERpbVBsb3QoR0JNLmNvbWJpbmVkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiKSx0aXRsZSA9ICJieSBwYXRpZW50IikKcHJpbnRfdGFiKERpbVBsb3QoR0JNLmNvbWJpbmVkLCByZWR1Y3Rpb24gPSAidW1hcCIsbGFiZWwgPSBUKSx0aXRsZSA9ICJieSBjbHVzdGVyIikKYGBgCgoKYGBge3J9CmNhbmNlcl9tYXJrZXJzID0gbGlzdCgKICBNZXNMaWtlMSA9IE1lc0xpa2UxR2VuZVNldEBnZW5lSWRzLAogIE1lc0xpa2UyID0gTWVzTGlrZTJHZW5lU2V0QGdlbmVJZHMsCiAgQUNMaWtlID0gQUNMaWtlR2VuZVNldEBnZW5lSWRzLAogIE9QQ0xpa2UgPSBPUENMaWtlR2VuZVNldEBnZW5lSWRzLAogIE5QQ0xpa2UxID0gTlBDTGlrZTFHZW5lU2V0QGdlbmVJZHMsCiAgTlBDTGlrZTIgPSBOUENMaWtlMkdlbmVTZXRAZ2VuZUlkcwopCgpHQk1TZXVyYXRfY2FuY2VyIDwtIEFkZE1vZHVsZVNjb3JlKAogIG9iamVjdCA9IEdCTVNldXJhdF9jYW5jZXIsCiAgZmVhdHVyZXMgPSBjYW5jZXJfbWFya2VycyxjdHJsID0gNTAsbmFtZSA9ICJjYW5jZXJfbWFya2VycyIpCgpHQk0uY29tYmluZWRbWyJNZXNMaWtlMSJdXSA9IEdCTVNldXJhdF9jYW5jZXIkY2FuY2VyX21hcmtlcnMxCkdCTS5jb21iaW5lZFtbIk1lc0xpa2UyIl1dID0gR0JNU2V1cmF0X2NhbmNlciRjYW5jZXJfbWFya2VyczIKR0JNLmNvbWJpbmVkW1siQUNMaWtlIl1dID0gR0JNU2V1cmF0X2NhbmNlciRjYW5jZXJfbWFya2VyczMKR0JNLmNvbWJpbmVkW1siT1BDTGlrZSJdXSA9IEdCTVNldXJhdF9jYW5jZXIkY2FuY2VyX21hcmtlcnM0CkdCTS5jb21iaW5lZFtbIk5QQ0xpa2UxIl1dID0gR0JNU2V1cmF0X2NhbmNlciRjYW5jZXJfbWFya2VyczUKR0JNLmNvbWJpbmVkW1siTlBDTGlrZTIiXV0gPSBHQk1TZXVyYXRfY2FuY2VyJGNhbmNlcl9tYXJrZXJzNgoKCmNhbmNlcl9zY29yZXMgID0gRmV0Y2hEYXRhKG9iamVjdCA9IEdCTS5jb21iaW5lZCx2YXJzID0gYygiTWVzTGlrZTEiLCJNZXNMaWtlMiIsIkFDTGlrZSIsIk9QQ0xpa2UiLCJOUENMaWtlMSIsIk5QQ0xpa2UyIikpCmNhbmNlcl9zY29yZXMkYXNzaWdubWVudDwtIGNvbG5hbWVzKGNhbmNlcl9zY29yZXMpW21heC5jb2woY2FuY2VyX3Njb3JlcywgdGllcy5tZXRob2QgPSAiZmlyc3QiKV0KCkdCTS5jb21iaW5lZCAlPD4lIEFkZE1ldGFEYXRhKG1ldGFkYXRhID0gY2FuY2VyX3Njb3JlcyRhc3NpZ25tZW50LGNvbC5uYW1lID0gImNhbmNlcl90eXBlIikKYGBgCgpgYGB7cn0KRGltUGxvdChHQk0uY29tYmluZWQsZ3JvdXAuYnkgPSAiY2FuY2VyX3R5cGUiLGxhYmVsID0gVCkKRGltUGxvdChHQk0uY29tYmluZWQsZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIixsYWJlbCA9IFQpCgpgYGAKCgoKCgoKIyBGZWF0dXJlIHBsb3RzIHsudGFic2V0fQpgYGB7ciByZXN1bHRzPSdhc2lzJ30KCgojIFRlc3RpbmcgZm9yIGNsdXN0ZXJzIGVucmljaGVkIGluIG1hcmtlcnMgb2YgdGhlIGRpZmZlcmVudCBtYWxpZ25hbnQgbWV0YS1tb2R1bGVzCnByaW50X3RhYihGZWF0dXJlUGxvdChHQk0uY29tYmluZWQsIGZlYXR1cmVzID0gIk1lc0xpa2UxIiksdGl0bGUgPSAiTUVTTGlrZTFTY29yZXMiKQpwcmludF90YWIoRmVhdHVyZVBsb3QoR0JNLmNvbWJpbmVkLCBmZWF0dXJlcyA9ICJNZXNMaWtlMiIpLHRpdGxlID0gIk1FU0xpa2UyU2NvcmVzIikKcHJpbnRfdGFiKEZlYXR1cmVQbG90KEdCTS5jb21iaW5lZCwgZmVhdHVyZXMgPSAiQUNMaWtlIiksdGl0bGUgPSAiQUNMaWtlU2NvcmVzIikKcHJpbnRfdGFiKEZlYXR1cmVQbG90KEdCTS5jb21iaW5lZCwgZmVhdHVyZXMgPSAiT1BDTGlrZSIpLHRpdGxlID0gIk9QQ0xpa2VTY29yZXMiKQpwcmludF90YWIoRmVhdHVyZVBsb3QoR0JNLmNvbWJpbmVkLCBmZWF0dXJlcyA9ICJOUENMaWtlMSIpLHRpdGxlID0gIk5QQ0xpa2UxU2NvcmVzIikKcHJpbnRfdGFiKEZlYXR1cmVQbG90KEdCTS5jb21iaW5lZCwgZmVhdHVyZXMgPSAiTlBDTGlrZTIiKSx0aXRsZSA9ICJOUENMaWtlMlNjb3JlcyIpCgoKYGBgCgoKYGBge3J9CnBkZihmaWxlID0gIi4vRmlndXJlcy9TZXVyYXQvVU1BUF9vcmlnLmlkZW50LnBkZiIsIG9uZWZpbGUgPSBGQUxTRSkKRGltUGxvdChHQk0uY29tYmluZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpCmRldi5vZmYoKQoKCnBkZihmaWxlID0gIi4vRmlndXJlcy9TZXVyYXQvVU1BUF9jbHVzdGVyaW5nLnBkZiIsIG9uZWZpbGUgPSBGQUxTRSkKRGltUGxvdChHQk0uY29tYmluZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIikKZGV2Lm9mZigpCgoKcGRmKGZpbGUgPSAiLi9GaWd1cmVzL1NldXJhdC9tYWNyb3BoYWdlU2NvcmUucGRmIiwgb25lZmlsZSA9IEZBTFNFKQpwcmludChGZWF0dXJlUGxvdChHQk0uY29tYmluZWQsIGZlYXR1cmVzID0gIm1hY3JvcGhhZ2VTY29yZSIpKQoKZGV2Lm9mZigpCgpwZGYoZmlsZSA9ICIuL0ZpZ3VyZXMvU2V1cmF0L1RDZWxsU2NvcmVzLnBkZiIsIG9uZWZpbGUgPSBGQUxTRSkKcHJpbnQoRmVhdHVyZVBsb3QoR0JNLmNvbWJpbmVkLCBmZWF0dXJlcyA9ICJUQ2VsbFNjb3JlcyIpKQoKZGV2Lm9mZigpCnBkZihmaWxlID0gIi4vRmlndXJlcy9TZXVyYXQvb2xpZ29kZW5kcm9jeXRlU2NvcmVzLnBkZiIsIG9uZWZpbGUgPSBGQUxTRSkKcHJpbnQoRmVhdHVyZVBsb3QoR0JNLmNvbWJpbmVkLCBmZWF0dXJlcyA9ICJvbGlnb2RlbmRyb2N5dGVTY29yZXMiKSkKCmRldi5vZmYoKQpwZGYoZmlsZSA9ICIuL0ZpZ3VyZXMvU2V1cmF0L29saWdvZGVuZHJvY3l0ZVNjb3Jlcy5wZGYiLCBvbmVmaWxlID0gRkFMU0UpCnByaW50KEZlYXR1cmVQbG90KEdCTS5jb21iaW5lZCwgZmVhdHVyZXMgPSAib2xpZ29kZW5kcm9jeXRlU2NvcmVzIikpCgpkZXYub2ZmKCkKcGRmKGZpbGUgPSAiLi9GaWd1cmVzL1NldXJhdC9NRVNMaWtlMVNjb3Jlcy5wZGYiLCBvbmVmaWxlID0gRkFMU0UpCnByaW50KEZlYXR1cmVQbG90KEdCTS5jb21iaW5lZCwgZmVhdHVyZXMgPSAiTUVTTGlrZTFTY29yZXMiKSkKCmRldi5vZmYoKQpwZGYoZmlsZSA9ICIuL0ZpZ3VyZXMvU2V1cmF0L01FU0xpa2UyU2NvcmVzLnBkZiIsIG9uZWZpbGUgPSBGQUxTRSkKcHJpbnQoRmVhdHVyZVBsb3QoR0JNLmNvbWJpbmVkLCBmZWF0dXJlcyA9ICJNRVNMaWtlMlNjb3JlcyIpKQoKZGV2Lm9mZigpCnBkZihmaWxlID0gIi4vRmlndXJlcy9TZXVyYXQvQUNMaWtlU2NvcmVzLnBkZiIsIG9uZWZpbGUgPSBGQUxTRSkKcHJpbnQoRmVhdHVyZVBsb3QoR0JNLmNvbWJpbmVkLCBmZWF0dXJlcyA9ICJBQ0xpa2VTY29yZXMiKSkKCmRldi5vZmYoKQpwZGYoZmlsZSA9ICIuL0ZpZ3VyZXMvU2V1cmF0L09QQ0xpa2VTY29yZXMucGRmIiwgb25lZmlsZSA9IEZBTFNFKQpwcmludChGZWF0dXJlUGxvdChHQk0uY29tYmluZWQsIGZlYXR1cmVzID0gIk9QQ0xpa2VTY29yZXMiKSkKCmRldi5vZmYoKQoKCnBkZihmaWxlID0gIi4vRmlndXJlcy9TZXVyYXQvTlBDTGlrZTFTY29yZXMucGRmIiwgb25lZmlsZSA9IEZBTFNFKQpwcmludChGZWF0dXJlUGxvdChHQk0uY29tYmluZWQsIGZlYXR1cmVzID0gIk5QQ0xpa2UxU2NvcmVzIikpCgpkZXYub2ZmKCkKCnBkZihmaWxlID0gIi4vRmlndXJlcy9TZXVyYXQvTlBDTGlrZTJTY29yZXMucGRmIiwgb25lZmlsZSA9IEZBTFNFKQpwcmludChGZWF0dXJlUGxvdChHQk0uY29tYmluZWQsIGZlYXR1cmVzID0gIk5QQ0xpa2UyU2NvcmVzIikpCgpkZXYub2ZmKCkKCgoKYGBgCgojIEFuYWx5c2lzCgoKYGBge3J9CmNsdXN0ZXJzX2FuZF9zY29yZXMgPSBGZXRjaERhdGEob2JqZWN0ID0gR0JNLmNvbWJpbmVkLHZhcnM9IGMoImNhbmNlcl90eXBlIiwic2V1cmF0X2NsdXN0ZXJzIikpICU+JSAgZ3JvdXBfYnkoc2V1cmF0X2NsdXN0ZXJzLGNhbmNlcl90eXBlKSAlPiUgIHN1bW1hcmlzZShuX2NlbGxzID0gbigpKSU+JSBtdXRhdGUocGVyID0gIDEwMCAqbl9jZWxscy9zdW0obl9jZWxscykpCgppbnRlZ3JhdGlvbl9zY29yZSA9IGNsdXN0ZXJzX2FuZF9zY29yZXMgJT4lICBncm91cF9ieShzZXVyYXRfY2x1c3RlcnMpICU+JSBmaWx0ZXIobl9jZWxscyA9PSBtYXgobl9jZWxscykpICU+JSBwdWxsKHBlcikgJT4lIG1lYW4oKSAlPiUgcm91bmQoZGlnaXRzID0gMikKCnZfZmFjdG9yX2xldmVscyA8LWMoICJNZXNMaWtlMSIsICJNZXNMaWtlMiIsICJOUENMaWtlMSIsICJOUENMaWtlMiIsICJPUENMaWtlIiwiQUNMaWtlIikKY29sb3JzID0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDYsICJQYWlyZWQiKTsgY29sb3JzWzVdID0gIm9yYW5nZSIKcDMgPSBnZ3Bsb3QoZGF0YT1jbHVzdGVyc19hbmRfc2NvcmVzLCBhZXMoeD1zZXVyYXRfY2x1c3RlcnMsIHk9cGVyLCBmaWxsPWZhY3RvcihjYW5jZXJfdHlwZSwgbGV2ZWxzID0gdl9mYWN0b3JfbGV2ZWxzKSkpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpK3RoZW1lX21pbmltYWwoKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9ycyxuYW1lICA9ICJDYW5jZXIgdHlwZSIpKyBsYWJzKHRpdGxlID0gIlNldXJhdCIsc3VidGl0bGUgPSAiaW50ZWdyYXRpb24gc2NvcmU9IiAlcyslIGludGVncmF0aW9uX3Njb3JlICVzKyUgIiUiKSt5bGFiKCIlIGZyb20gY2x1c3RlciIpCnAzCmBgYAoKCgoKIyBzaWxob3VldHRlIHNjb3JlCmBgYHtyfQpsaWJyYXJ5KGNsdXN0ZXIsIHF1aWV0bHkgPSBUUlVFKQpkaXN0Lm1hdHJpeCA8LSBkaXN0KHggPSBFbWJlZGRpbmdzKG9iamVjdCA9IEdCTS5jb21iaW5lZFtbInVtYXAiXV0pKQpjbHVzdGVycyA8LSBHQk0uY29tYmluZWQkY2FuY2VyX3R5cGUKc2lsIDwtIHNpbGhvdWV0dGUoeCA9IGFzLm51bWVyaWMoeCA9IGFzLmZhY3Rvcih4ID0gY2x1c3RlcnMpKSwgZGlzdCA9IGRpc3QubWF0cml4KQpzdW1tYXJ5KHNpbCkKR0JNLmNvbWJpbmVkJHNpbCA9IHNpbFssM10KVmxuUGxvdChvYmplY3QgPSBHQk0uY29tYmluZWQsZmVhdHVyZXMgPSAic2lsIixncm91cC5ieSA9ICJjYW5jZXJfdHlwZSIpCmBgYAoKIyBDb21iaW5lIGNhbmNlciBzdWJ0eXBlcwpgYGB7cn0KR0JNLmNvbWJpbmVkJGNhbmNlcl90eXBlID0gR0JNLmNvbWJpbmVkJGNhbmNlcl90eXBlICU+JSBnc3ViKHBhdHRlcm4gPSAiTWVzTGlrZTF8TWVzTGlrZTIiLHJlcGxhY2VtZW50ID0gIk1lc0xpa2UiKSU+JSBnc3ViKHBhdHRlcm4gPSAiTlBDTGlrZTF8TlBDTGlrZTIiLHJlcGxhY2VtZW50ID0gIk5QQ0xpa2UiKQpgYGAKCmBgYHtyfQpjbHVzdGVyc19hbmRfc2NvcmVzID0gRmV0Y2hEYXRhKG9iamVjdCA9IEdCTS5jb21iaW5lZCx2YXJzPSBjKCJjYW5jZXJfdHlwZSIsInNldXJhdF9jbHVzdGVycyIpKSAlPiUgIGdyb3VwX2J5KHNldXJhdF9jbHVzdGVycyxjYW5jZXJfdHlwZSkgJT4lICBzdW1tYXJpc2Uobl9jZWxscyA9IG4oKSklPiUgbXV0YXRlKHBlciA9ICAxMDAgKm5fY2VsbHMvc3VtKG5fY2VsbHMpKQoKaW50ZWdyYXRpb25fc2NvcmUgPSBjbHVzdGVyc19hbmRfc2NvcmVzICU+JSAgZ3JvdXBfYnkoc2V1cmF0X2NsdXN0ZXJzKSAlPiUgZmlsdGVyKG5fY2VsbHMgPT0gbWF4KG5fY2VsbHMpKSAlPiUgcHVsbChwZXIpICU+JSBtZWFuKCkgJT4lIHJvdW5kKGRpZ2l0cyA9IDIpCgp2X2ZhY3Rvcl9sZXZlbHMgPC1jKCAiTWVzTGlrZSIsICJOUENMaWtlIiwgIk9QQ0xpa2UiLCJBQ0xpa2UiKQpjb2xvcnMgPSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoNiwgIlBhaXJlZCIpW2MoMiw0LDUsNildOyBjb2xvcnNbM10gPSAib3JhbmdlIgpwNCA9IGdncGxvdChkYXRhPWNsdXN0ZXJzX2FuZF9zY29yZXMsIGFlcyh4PXNldXJhdF9jbHVzdGVycywgeT1wZXIsIGZpbGw9ZmFjdG9yKGNhbmNlcl90eXBlLCBsZXZlbHMgPSB2X2ZhY3Rvcl9sZXZlbHMpKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikrdGhlbWVfbWluaW1hbCgpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3JzLG5hbWUgID0gIkNhbmNlciB0eXBlIikrIGxhYnModGl0bGUgPSAiU2lQU2lDIixzdWJ0aXRsZSA9ICJpbnRlZ3JhdGlvbiBzY29yZT0iICVzKyUgaW50ZWdyYXRpb25fc2NvcmUgJXMrJSAiJSIpK3lsYWIoIiUgZnJvbSBjbHVzdGVyIikKcDQKYGBgCgpgYGB7cn0KbGlicmFyeShjbHVzdGVyLCBxdWlldGx5ID0gVFJVRSkKZGlzdC5tYXRyaXggPC0gZGlzdCh4ID0gRW1iZWRkaW5ncyhvYmplY3QgPSBHQk0uY29tYmluZWRbWyJ1bWFwIl1dKSkKY2x1c3RlcnMgPC0gR0JNLmNvbWJpbmVkJGNhbmNlcl90eXBlCnNpbCA8LSBzaWxob3VldHRlKHggPSBhcy5udW1lcmljKHggPSBhcy5mYWN0b3IoeCA9IGNsdXN0ZXJzKSksIGRpc3QgPSBkaXN0Lm1hdHJpeCkKc3VtbWFyeShzaWwpCkdCTS5jb21iaW5lZCRzaWwgPSBzaWxbLDNdClZsblBsb3Qob2JqZWN0ID0gR0JNLmNvbWJpbmVkLGZlYXR1cmVzID0gInNpbCIsZ3JvdXAuYnkgPSAiY2FuY2VyX3R5cGUiKQpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD0xMH0KZ2dhcnJhbmdlKHAxLHAyLHAzLGNvbW1vbi5sZWdlbmQgPSBUKQpgYGAKCgpgYGB7cn0KCiMgTWVhc3VyaW5nIHRoZSBwZXJjZW50YWdlIG9mIGNlbGxzIG9mIGVhY2ggbWFsaWduYW50IG1ldGEgbW9kdWxlIHRoYXQgYXJlIGluIGEgc2luZ2xlIGNsdXN0ZXIKY2x1c3RlckFzc2lnbm1lbnRzIDwtIElkZW50cyhHQk0uY29tYmluZWQpClNjb3Jlc0FuZENsdXN0ZXJzIDwtIGNiaW5kKGNsdXN0ZXJBc3NpZ25tZW50cywgTWFjcm9waGFnZVNjb3JlcyA9IG1hY3JvcGhhZ2VTY29yZXMkcGF0aHdheVNjb3JlcywgT2xpZ29kZWNkcm9jeXRlc1Njb3JlcyA9IG9saWdvZGVuZHJvY3l0ZVNjb3JlcyRwYXRod2F5U2NvcmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICBNRVNMaWtlMVNjb3JlcyA9IE1lc0xpa2UxU2NvcmVzJHBhdGh3YXlTY29yZXMsIE1FU0xpa2UyU2NvcmVzID0gTWVzTGlrZTJTY29yZXMkcGF0aHdheVNjb3JlcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEFDTGlrZVNjb3JlcyA9IEFDTGlrZVNjb3JlcyRwYXRod2F5U2NvcmVzLCBPUENMaWtlU2NvcmVzID0gT1BDTGlrZVNjb3JlcyRwYXRod2F5U2NvcmVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgTlBDTGlrZTFTY29yZXMgPSBOUENMaWtlMVNjb3JlcyRwYXRod2F5U2NvcmVzLCBOUENMaWtlMlNjb3JlcyA9IE5QQ0xpa2UyU2NvcmVzJHBhdGh3YXlTY29yZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBUQ2VsbFNjb3JlcyA9IFRDZWxsU2NvcmVzJHBhdGh3YXlTY29yZXMsIGFzLmRhdGEuZnJhbWUoR0JNLmNvbWJpbmVkQG1ldGEuZGF0YSRvcmlnLmlkZW50KSkKY29sbmFtZXMoU2NvcmVzQW5kQ2x1c3RlcnMpW2NvbG5hbWVzKFNjb3Jlc0FuZENsdXN0ZXJzKSA9PSAiR0JNLmNvbWJpbmVkQG1ldGEuZGF0YSRvcmlnLmlkZW50Il0gPC0gIlBhdGllbnQiCgojIENoZWNraW5nIHRoZSBwcm9wb3J0aW9ucyBvZiB0aGUgZGlmZmVyZW50IHBhdGllbnRzIGluIGVhY2ggY2x1c3RlcgpjbHVzdGVyQnlQYXRpZW50SURzIDwtIFNjb3Jlc0FuZENsdXN0ZXJzWyxjKCJjbHVzdGVyQXNzaWdubWVudHMiLCAiUGF0aWVudCIpXQpjbHVzdGVyQnlQYXRpZW50SURzIDwtIGNsdXN0ZXJCeVBhdGllbnRJRHNbb3JkZXIoY2x1c3RlckJ5UGF0aWVudElEcyRjbHVzdGVyQXNzaWdubWVudHMpLF0KZm9yKGN1cnJDbHVzdGVyIGluIDA6bWF4KGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGNsdXN0ZXJCeVBhdGllbnRJRHNbLCJjbHVzdGVyQXNzaWdubWVudHMiXSkpKSkKewogIGN1cnJDbHVzdGVyQ2VsbHMgPC0gY2x1c3RlckJ5UGF0aWVudElEc1tjbHVzdGVyQnlQYXRpZW50SURzWywiY2x1c3RlckFzc2lnbm1lbnRzIl0gPT0gY3VyckNsdXN0ZXIsIlBhdGllbnQiXQogIGNhdCgiXG4iLCAiQ2x1c3RlciBudW1iZXI6ICIsIGN1cnJDbHVzdGVyLCAiIFRvdGFsIENlbGxzOiAiLCBzdW0oY2x1c3RlckJ5UGF0aWVudElEc1ssImNsdXN0ZXJBc3NpZ25tZW50cyJdID09IGN1cnJDbHVzdGVyKSwgIlxuIikKICBwcmludCh0YWJsZShjdXJyQ2x1c3RlckNlbGxzKSkKfQoKCk5QQ1RocmVzaG9sZCA8LSBBQ0xpa2VUaHJlc2hvbGQgPC0gT1BDTGlrZVRocmVzaG9sZCA8LSBNRVNMaWtlc1RocmVzaG9sZCA8LSAwLjEzCk9saWdvZGVuZHJvY3l0ZVRocmVzaG9sZCA8LSAxLjUKTWFjcm9waGFnZVRocmVzaG9sZCA8LSBUQ2VsbFRocmVzaG9sZCA8LSAwLjA1CgptYWNyb3BoYWdlT3ZlclRocmVzaCA8LSBTY29yZXNBbmRDbHVzdGVyc1tTY29yZXNBbmRDbHVzdGVyc1ssIk1hY3JvcGhhZ2VTY29yZXMiXSA+IE1hY3JvcGhhZ2VUaHJlc2hvbGQsYygiY2x1c3RlckFzc2lnbm1lbnRzIiwgIk1hY3JvcGhhZ2VTY29yZXMiKV0KcHJpbnQoIk1hY3JvcGhhZ2UgY2VsbHM6IFRvdGFsIG92ZXIgdGhyZXNob2xkID0gIikKcHJpbnQobnJvdyhtYWNyb3BoYWdlT3ZlclRocmVzaCkpCnByaW50KHRhYmxlKG1hY3JvcGhhZ2VPdmVyVGhyZXNoWywiY2x1c3RlckFzc2lnbm1lbnRzIl0pKQpjYXQoIlxuXG4iKQoKVENlbGxzT3ZlclRocmVzaCA8LSBTY29yZXNBbmRDbHVzdGVyc1tTY29yZXNBbmRDbHVzdGVyc1ssIlRDZWxsU2NvcmVzIl0gPiBUQ2VsbFRocmVzaG9sZCxjKCJjbHVzdGVyQXNzaWdubWVudHMiLCAiVENlbGxTY29yZXMiKV0KcHJpbnQoIlQgY2VsbHM6IFRvdGFsIG92ZXIgdGhyZXNob2xkID0gIikKcHJpbnQobnJvdyhUQ2VsbHNPdmVyVGhyZXNoKSkKcHJpbnQodGFibGUoVENlbGxzT3ZlclRocmVzaFssImNsdXN0ZXJBc3NpZ25tZW50cyJdKSkKY2F0KCJcblxuIikKCk9saWdvZGVuZHJvY3l0ZXNPdmVyVGhyZXNoIDwtIFNjb3Jlc0FuZENsdXN0ZXJzW1Njb3Jlc0FuZENsdXN0ZXJzWywiT2xpZ29kZWNkcm9jeXRlc1Njb3JlcyJdID4gT2xpZ29kZW5kcm9jeXRlVGhyZXNob2xkLGMoImNsdXN0ZXJBc3NpZ25tZW50cyIsICJPbGlnb2RlY2Ryb2N5dGVzU2NvcmVzIildCnByaW50KCJPbGlnb2RlbmRyb2N5dGUgY2VsbHM6IFRvdGFsIG92ZXIgdGhyZXNob2xkID0gIikKcHJpbnQobnJvdyhPbGlnb2RlbmRyb2N5dGVzT3ZlclRocmVzaCkpCnByaW50KHRhYmxlKE9saWdvZGVuZHJvY3l0ZXNPdmVyVGhyZXNoWywiY2x1c3RlckFzc2lnbm1lbnRzIl0pKQpjYXQoIlxuXG4iKQoKTlBDczFPdmVyVGhyZXNoIDwtIFNjb3Jlc0FuZENsdXN0ZXJzW1Njb3Jlc0FuZENsdXN0ZXJzWywiTlBDTGlrZTFTY29yZXMiXSA+IE5QQ1RocmVzaG9sZCxjKCJjbHVzdGVyQXNzaWdubWVudHMiLCAiTlBDTGlrZTFTY29yZXMiKV0KcHJpbnQoIk5QQzEgY2VsbHM6IFRvdGFsIG92ZXIgdGhyZXNob2xkID0gIikKcHJpbnQobnJvdyhOUENzMU92ZXJUaHJlc2gpKQpwcmludCh0YWJsZShOUENzMU92ZXJUaHJlc2hbLCJjbHVzdGVyQXNzaWdubWVudHMiXSkpCmNhdCgiXG5cbiIpCgpOUENzMk92ZXJUaHJlc2ggPC0gU2NvcmVzQW5kQ2x1c3RlcnNbU2NvcmVzQW5kQ2x1c3RlcnNbLCJOUENMaWtlMlNjb3JlcyJdID4gTlBDVGhyZXNob2xkLGMoImNsdXN0ZXJBc3NpZ25tZW50cyIsICJOUENMaWtlMlNjb3JlcyIpXQpwcmludCgiTlBDMiBjZWxsczogVG90YWwgb3ZlciB0aHJlc2hvbGQgPSAiKQpwcmludChucm93KE5QQ3MyT3ZlclRocmVzaCkpCnByaW50KHRhYmxlKE5QQ3MyT3ZlclRocmVzaFssImNsdXN0ZXJBc3NpZ25tZW50cyJdKSkKY2F0KCJcblxuIikKCkFDTGlrZXNPdmVyVGhyZXNoIDwtIFNjb3Jlc0FuZENsdXN0ZXJzW1Njb3Jlc0FuZENsdXN0ZXJzWywiQUNMaWtlU2NvcmVzIl0gPiBBQ0xpa2VUaHJlc2hvbGQsYygiY2x1c3RlckFzc2lnbm1lbnRzIiwgIkFDTGlrZVNjb3JlcyIpXQpwcmludCgiQUNMaWtlIGNlbGxzOiBUb3RhbCBvdmVyIHRocmVzaG9sZCA9ICIpCnByaW50KG5yb3coQUNMaWtlc092ZXJUaHJlc2gpKQpwcmludCh0YWJsZShBQ0xpa2VzT3ZlclRocmVzaFssImNsdXN0ZXJBc3NpZ25tZW50cyJdKSkKY2F0KCJcblxuIikKCk9QQ0xpa2VzT3ZlclRocmVzaCA8LSBTY29yZXNBbmRDbHVzdGVyc1tTY29yZXNBbmRDbHVzdGVyc1ssIk9QQ0xpa2VTY29yZXMiXSA+IE9QQ0xpa2VUaHJlc2hvbGQsYygiY2x1c3RlckFzc2lnbm1lbnRzIiwgIk9QQ0xpa2VTY29yZXMiKV0KcHJpbnQoIk9QQ0xpa2UgY2VsbHM6IFRvdGFsIG92ZXIgdGhyZXNob2xkID0gIikKcHJpbnQobnJvdyhPUENMaWtlc092ZXJUaHJlc2gpKQpwcmludCh0YWJsZShPUENMaWtlc092ZXJUaHJlc2hbLCJjbHVzdGVyQXNzaWdubWVudHMiXSkpCmNhdCgiXG5cbiIpCgpNRVMxT3ZlclRocmVzaCA8LSBTY29yZXNBbmRDbHVzdGVyc1tTY29yZXNBbmRDbHVzdGVyc1ssIk1FU0xpa2UxU2NvcmVzIl0gPiBNRVNMaWtlc1RocmVzaG9sZCxjKCJjbHVzdGVyQXNzaWdubWVudHMiLCAiTUVTTGlrZTFTY29yZXMiKV0KcHJpbnQoIk1FUzEgY2VsbHM6IFRvdGFsIG92ZXIgdGhyZXNob2xkID0gIikKcHJpbnQobnJvdyhNRVMxT3ZlclRocmVzaCkpCnByaW50KHRhYmxlKE1FUzFPdmVyVGhyZXNoWywiY2x1c3RlckFzc2lnbm1lbnRzIl0pKQpjYXQoIlxuXG4iKQoKTUVTMk92ZXJUaHJlc2ggPC0gU2NvcmVzQW5kQ2x1c3RlcnNbU2NvcmVzQW5kQ2x1c3RlcnNbLCJNRVNMaWtlMlNjb3JlcyJdID4gTUVTTGlrZXNUaHJlc2hvbGQsYygiY2x1c3RlckFzc2lnbm1lbnRzIiwgIk1FU0xpa2UyU2NvcmVzIildCnByaW50KCJNRVMyIGNlbGxzOiBUb3RhbCBvdmVyIHRocmVzaG9sZCA9ICIpCnByaW50KG5yb3coTUVTMk92ZXJUaHJlc2gpKQpwcmludCh0YWJsZShNRVMyT3ZlclRocmVzaFssImNsdXN0ZXJBc3NpZ25tZW50cyJdKSkKY2F0KCJcblxuIikKCiMgQ2hlY2tpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBlYWNoIHBhdGllbnQncyBtYWxpZ25hbnQgY2VsbHMgYWNyb3NzIGNsdXN0ZXJzCgppc0NlbGxNYWxpZ25hbnQgPC0gU2NvcmVzQW5kQ2x1c3RlcnNbLCJOUENMaWtlMVNjb3JlcyJdID4gTlBDVGhyZXNob2xkIHwKICAgICAgICAgICAgICAgICAgIFNjb3Jlc0FuZENsdXN0ZXJzWywiTlBDTGlrZTJTY29yZXMiXSA+IE5QQ1RocmVzaG9sZCB8CiAgICAgICAgICAgICAgICAgICBTY29yZXNBbmRDbHVzdGVyc1ssIkFDTGlrZVNjb3JlcyJdID4gQUNMaWtlVGhyZXNob2xkIHwKICAgICAgICAgICAgICAgICAgIFNjb3Jlc0FuZENsdXN0ZXJzWywiT1BDTGlrZVNjb3JlcyJdID4gT1BDTGlrZVRocmVzaG9sZCB8CiAgICAgICAgICAgICAgICAgICBTY29yZXNBbmRDbHVzdGVyc1ssIk1FU0xpa2UxU2NvcmVzIl0gPiBNRVNMaWtlc1RocmVzaG9sZCB8CiAgICAgICAgICAgICAgICAgICBTY29yZXNBbmRDbHVzdGVyc1ssIk1FU0xpa2UyU2NvcmVzIl0gPiBNRVNMaWtlc1RocmVzaG9sZAptYWxpZ25hbnRDZWxsc09ubHkgPC0gU2NvcmVzQW5kQ2x1c3RlcnNbaXNDZWxsTWFsaWduYW50LF0KCmZvciAoY3VyclBhdGllbnQgaW4gbGV2ZWxzKG1hbGlnbmFudENlbGxzT25seSRQYXRpZW50KSkKewogIGN1cnJQYXRpZW50TWFsaWdDZWxscyA8LSBtYWxpZ25hbnRDZWxsc09ubHlbbWFsaWduYW50Q2VsbHNPbmx5WywiUGF0aWVudCJdID09IGN1cnJQYXRpZW50LF0KICBjYXQoIlxuIiwgIlBhdGllbnQgbnVtYmVyOiAiLCBjdXJyUGF0aWVudCwgIlxuVG90YWwgbnVtYmVyIG9mIGNlbGxzOiAiLCBucm93KGN1cnJQYXRpZW50TWFsaWdDZWxscyksICJcbiIpCiAgcHJpbnQodGFibGUoY3VyclBhdGllbnRNYWxpZ0NlbGxzWywiY2x1c3RlckFzc2lnbm1lbnRzIl0pKQogIAogICMgRmluZGluZyB0aGUgTlBDLUxpa2UgY2VsbHMgb2YgdGhpcyBwYXRpZW50IG9ubHkKICBjdXJyUGF0aWVudE5QQ0xpa2UxIDwtIGN1cnJQYXRpZW50TWFsaWdDZWxsc1tjdXJyUGF0aWVudE1hbGlnQ2VsbHNbLCJOUENMaWtlMVNjb3JlcyJdID4gTlBDVGhyZXNob2xkLF0KICBjdXJyUGF0aWVudE5QQ0xpa2UyIDwtIGN1cnJQYXRpZW50TWFsaWdDZWxsc1tjdXJyUGF0aWVudE1hbGlnQ2VsbHNbLCJOUENMaWtlMlNjb3JlcyJdID4gTlBDVGhyZXNob2xkLF0KICAKICBjYXQoIlxuIiwgIk5QQy1MaWtlMSBjZWxscyBpbiB0aGlzIGNsdXN0ZXI6XG4iKQogIGNhdCgiVG90YWwgbnVtYmVyIG9mIE5QQy1MaWtlMTogIiwgbnJvdyhjdXJyUGF0aWVudE5QQ0xpa2UxKSkKICBwcmludCh0YWJsZShjdXJyUGF0aWVudE5QQ0xpa2UxWywgImNsdXN0ZXJBc3NpZ25tZW50cyJdKSkKICAKICBjYXQoIlxuXG4iLCAiTlBDLUxpa2UyIGNlbGxzIGluIHRoaXMgY2x1c3RlcjpcbiIpCiAgY2F0KCJUb3RhbCBudW1iZXIgb2YgTlBDLUxpa2UyOiAiLCBucm93KGN1cnJQYXRpZW50TlBDTGlrZTIpKQogIHByaW50KHRhYmxlKGN1cnJQYXRpZW50TlBDTGlrZTJbLCAiY2x1c3RlckFzc2lnbm1lbnRzIl0pKQogIAogIGNhdCgiXG5cbiIpCn0KYGBgCgpgYGB7cn0KY2x1c3RlcnNfYXNzaWdubWVudF9yZXN1bHRzID0gZGF0YS5mcmFtZShjbHVzdGVyID0gMDo5KQpjbHVzdGVyc19hc3NpZ25tZW50X3Jlc3VsdHNbLCJNYWNyb3BoYWdlcyJdID0gdGFibGUobWFjcm9waGFnZU92ZXJUaHJlc2hbLCJjbHVzdGVyQXNzaWdubWVudHMiXSkgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgcHVsbChGcmVxKQpjbHVzdGVyc19hc3NpZ25tZW50X3Jlc3VsdHNbLCJUQ2VsbFMiXSA9IHRhYmxlKFRDZWxsc092ZXJUaHJlc2hbLCJjbHVzdGVyQXNzaWdubWVudHMiXSkgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgcHVsbChGcmVxKQpjbHVzdGVyc19hc3NpZ25tZW50X3Jlc3VsdHNbLCJPbGlnb2RlY2Ryb2N5dGVzIl0gPSB0YWJsZShPbGlnb2RlbmRyb2N5dGVzT3ZlclRocmVzaFssImNsdXN0ZXJBc3NpZ25tZW50cyJdKSAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBwdWxsKEZyZXEpCmNsdXN0ZXJzX2Fzc2lnbm1lbnRfcmVzdWx0c1ssIk5QQzEiXSA9IHRhYmxlKE5QQ3MxT3ZlclRocmVzaFssImNsdXN0ZXJBc3NpZ25tZW50cyJdKSAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBwdWxsKEZyZXEpCgpjbHVzdGVyc19hc3NpZ25tZW50X3Jlc3VsdHNbLCJOUEMyIl0gPSB0YWJsZShOUENzMk92ZXJUaHJlc2hbLCJjbHVzdGVyQXNzaWdubWVudHMiXSkgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgcHVsbChGcmVxKQpjbHVzdGVyc19hc3NpZ25tZW50X3Jlc3VsdHNbLCJBQ0xpa2UiXSA9IHRhYmxlKEFDTGlrZXNPdmVyVGhyZXNoWywiY2x1c3RlckFzc2lnbm1lbnRzIl0pICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIHB1bGwoRnJlcSkKY2x1c3RlcnNfYXNzaWdubWVudF9yZXN1bHRzWywiT1BDTGlrZSJdID0gdGFibGUoT1BDTGlrZXNPdmVyVGhyZXNoWywiY2x1c3RlckFzc2lnbm1lbnRzIl0pICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIHB1bGwoRnJlcSkKCmNsdXN0ZXJzX2Fzc2lnbm1lbnRfcmVzdWx0c1ssIk1FUzEiXSA9IHRhYmxlKE1FUzFPdmVyVGhyZXNoWywiY2x1c3RlckFzc2lnbm1lbnRzIl0pICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIHB1bGwoRnJlcSkKY2x1c3RlcnNfYXNzaWdubWVudF9yZXN1bHRzWywiTUVTMiJdID0gdGFibGUoTUVTMk92ZXJUaHJlc2hbLCJjbHVzdGVyQXNzaWdubWVudHMiXSkgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgcHVsbChGcmVxKQoKYGBgCgpgYGB7cn0KZGYyID0gcmVzaGFwZTI6Om1lbHQoY2x1c3RlcnNfYXNzaWdubWVudF9yZXN1bHRzLCBpZC52YXJzID0gYygiY2x1c3RlciIpLCB2YXJpYWJsZS5uYW1lID0gImNlbGxfdHlwZSIsIHZhbHVlLm5hbWUgPSAibl9jZWxscyIpCmdncGxvdChkYXRhPWRmMiwgYWVzKHg9Y2x1c3RlciwgeT1uX2NlbGxzLCBmaWxsPWNlbGxfdHlwZSkpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpK3RoZW1lX21pbmltYWwoKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3M9MDoxMykrIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IlBhaXJlZCIpCmBgYAoKCiMgU2Vzc2lvbiBpbmZvCmBgYHtyfQpzZXNzaW9uX2luZm8oKQpgYGAKCjxzY3JpcHQgc3JjPSJodHRwczovL2h5cG90aGVzLmlzL2VtYmVkLmpzIiBhc3luYz48L3NjcmlwdD4KCg==