Data

acc1_cancer_cells = readRDS("./Data/acc1_cancer_cells_15KnCount_V3.RDS")
all_acc_cancer_cells = readRDS("./Data/acc_cancer_cells_V4.RDS")
acc_all_cells = readRDS("./Data/acc_tpm_nCount_mito_no146_15k_with_ACC1_.RDS")
acc_cancerCells_noACC1 = subset(all_acc_cancer_cells,subset = patient.ident!= "HMSC")

luminal_pathways = c("CHARAFE_BREAST_CANCER_LUMINAL_VS_BASAL_DN","CHARAFE_BREAST_CANCER_LUMINAL_VS_BASAL_UP","CHARAFE_BREAST_CANCER_LUMINAL_VS_MESENCHYMAL_DN","CHARAFE_BREAST_CANCER_LUMINAL_VS_MESENCHYMAL_UP","HUPER_BREAST_BASAL_VS_LUMINAL_DN","LIM_MAMMARY_LUMINAL_PROGENITOR_UP","SMID_BREAST_CANCER_LUMINAL_B_UP" )

# add luminal pathways
luminal_gs = msigdbr(species = "Homo sapiens") %>%as.data.frame() %>% dplyr::filter(gs_name %in% luminal_pathways)%>% dplyr::distinct(gs_name, gene_symbol) %>% as.data.frame()

Parameters

functions

source_from_github(repositoy = "DEG_functions",version = "0.2.24")
source_from_github(repositoy = "HMSC_functions",version = "0.1.13",script_name = "functions.R")
source_from_github(repositoy = "cNMF_functions",version = "0.3.72",script_name = "cnmf_function_Harmony.R")

UMAP

DimPlot(object = acc1_cancer_cells,pt.size = 2)

for (cluster in unique(deg$cluster)) {
  deg_of_cluster = deg[deg$cluster == cluster,]
  enrichment_analysis(deg_of_cluster,background = VariableFeatures(acc1_cancer_cells),fdr_Cutoff = 0.01,ident.1 = paste("Cluster",cluster),ident.2 =  paste("Cluster",cluster),show_by = 1)
}

NA
NA

features UMAP

FeaturePlot(object = acc1_cancer_cells,features = c("TP63","ACTA2","IL12B","CNN1"))
Warning in FeaturePlot(object = acc1_cancer_cells, features = c("TP63",  :
  All cells have the same value (0) of IL12B.

Enrichment analysis HMSC vs ACC

all_acc_cancer_cells = SetIdent(all_acc_cancer_cells, value ="patient.ident")
acc_deg <- FindMarkers(all_acc_cancer_cells, ident.1 = "HMSC",logfc.threshold = 1.5,features = VariableFeatures(all_acc_cancer_cells))
enrichment_analysis(acc_deg,background = VariableFeatures(all_acc_cancer_cells),fdr_Cutoff = 0.01,ident.1 = "HMSC",ident.2 = "ACC",show_by = 1)

cell cycle filtering

hallmark_name = "GO_MITOTIC_CELL_CYCLE"
genesets  =getGmt("./Data/h.all.v7.0.symbols.pluscc.gmt")
var_features=acc_cancerCells_noACC1@assays$RNA@var.features
geneIds= genesets[[hallmark_name]]@geneIds
score <- apply(acc_cancerCells_noACC1@assays$RNA@scale.data[intersect(geneIds,var_features),],2,mean)
acc_cancerCells_noACC1=AddMetaData(acc_cancerCells_noACC1,score,hallmark_name)

hallmark_name = "GO_MITOTIC_CELL_CYCLE"
genesets  =getGmt("./Data/h.all.v7.0.symbols.pluscc.gmt")
var_features=acc1_cancer_cells@assays$RNA@var.features
geneIds= genesets[[hallmark_name]]@geneIds
score <- apply(acc1_cancer_cells@assays$RNA@scale.data[intersect(geneIds,var_features),],2,mean)
acc1_cancer_cells=AddMetaData(acc1_cancer_cells,score,hallmark_name)


# hallmark_name = "GO_MITOTIC_CELL_CYCLE"
# genesets  =getGmt("./Data/h.all.v7.0.symbols.pluscc.gmt")
# var_features=acc1_cancer_cells@assays$RNA@var.features
# geneIds= genesets[[hallmark_name]]@geneIds
# score <- apply(acc1_cancer_cells@assays$RNA@scale.data[intersect(geneIds,var_features),],2,mean)
# acc1_cancer_cells=AddMetaData(acc1_cancer_cells,score,hallmark_name)
library(highcharter) 
options(highcharter.theme = hc_theme_smpl(tooltip = list(valueDecimals = 2)))

cc_scores = FetchData(object = acc1_cancer_cells,vars = "GO_MITOTIC_CELL_CYCLE")
 hchart(
  density(cc_scores$GO_MITOTIC_CELL_CYCLE), 
  type = "area", name = "GO_MITOTIC_CELL_CYCLE"
  )
NA
cc_scores = FetchData(object = acc_cancerCells_noACC1,vars = "GO_MITOTIC_CELL_CYCLE")
 hchart(
  density(cc_scores$GO_MITOTIC_CELL_CYCLE), 
  type = "area", name = "GO_MITOTIC_CELL_CYCLE"
  )
NA

Before cc filtering


#filter:
all_acc_cancer_cells_ccFiltered=all_acc_cancer_cells[,all_acc_cancer_cells@meta.data[[hallmark_name]]< 0.16]


min_threshold = min(all_acc_cancer_cells$GO_MITOTIC_CELL_CYCLE)
max_threshold = max(all_acc_cancer_cells$GO_MITOTIC_CELL_CYCLE)
library(viridis)
FeaturePlot(object = all_acc_cancer_cells,features = hallmark_name) + ggtitle("Before cc filtering")  & scale_color_gradientn(colours = plasma(n = 10, direction = -1), limits = c(min_threshold, max_threshold))
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

After cc filtering

FeaturePlot(object = all_acc_cancer_cells_ccFiltered,features = hallmark_name) + ggtitle("After cc filtering") & scale_color_gradientn(colours = plasma(n = 10, direction = -1), limits = c(min_threshold, max_threshold))
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

DimPlot(object = all_acc_cancer_cells_ccFiltered,group.by = "patient.ident")

all_acc_cancer_cells_ccFiltered = SetIdent(object = all_acc_cancer_cells_ccFiltered,value = "orig.ident")
VlnPlot(object = all_acc_cancer_cells_ccFiltered,features = hallmark_name)

DimPlot(object = all_acc_cancer_cells,group.by = "orig.ident")

Enrichment analysis filtered HMSC vs ACC

patient.ident = all_acc_cancer_cells_ccFiltered$patient.ident %>% as.data.frame()
patient.ident[,1] = as.character(patient.ident[,1])
patient.ident[patient.ident[,1] == "ACC1",] = "HMSC"
patient.ident[,1] = as.factor(patient.ident[,1])
all_acc_cancer_cells_ccFiltered = AddMetaData(object = all_acc_cancer_cells_ccFiltered,metadata = patient.ident,col.name = "patient.ident")
all_acc_cancer_cells_ccFiltered = SetIdent(all_acc_cancer_cells_ccFiltered, value ="patient.ident")
acc_deg <- FindMarkers(all_acc_cancer_cells_ccFiltered, ident.1 = "HMSC",logfc.threshold = 1.5,features = VariableFeatures(all_acc_cancer_cells_ccFiltered))
enrichment_analysis(acc_deg,background = VariableFeatures(all_acc_cancer_cells_ccFiltered),fdr_Cutoff = 0.01,ident.1 = "HMSC",ident.2 = "ACC",show_by = 1)

MYB expression

all_acc_cancer_cells = SetIdent(object = all_acc_cancer_cells,value = "patient.ident") #active snn graph
FeaturePlot(object = all_acc_cancer_cells,features = "MYB",label = T)

all_acc_cancer_cells = SetIdent(object = all_acc_cancer_cells,value = "patient.ident") #active snn graph
FeaturePlot(object = all_acc_cancer_cells,features = "MYB",label = T)

CNV

new.cluster.ids <- c("cancer", #0
                     "cancer", #1
                     "CAF", #2
                     "cancer", #3
                     "Endothelial", #4
                     "cancer", #5
                     "cancer", #6
                     "CAF", #7
                     "CAF", #8
                     "CAF", #9
                     "cancer", #10
                     "CAF", #11
                     "cancer", #12
                     "cancer", #13
                     "cancer", #14
                     "cancer", #15
                     "cancer", #16
                     "WBC", #17
                     "CAF" #18
                     )
#rename idents:
acc_all_cells = SetIdent(object = acc_all_cells,value = "RNA_snn_res.1") #active snn graph
names(new.cluster.ids) <- levels(acc_all_cells) #add snn graph levels to new.cluster.ids
acc_all_cells@meta.data[["seurat_clusters"]] = acc_all_cells@meta.data[["RNA_snn_res.1"]]
acc_all_cells = SetIdent(object = acc_all_cells,value = "seurat_clusters")
acc_all_cells <- RenameIdents(acc_all_cells, new.cluster.ids) 

# divide "cancer" into patients:
cell_types = acc_all_cells@active.ident %>% as.data.frame()
cell_types[,1]<- as.character(cell_types[,1])
cell_types = cbind(cell_types,acc_all_cells$patient.ident) %>% setnames(old = names(.), 
         new = c('cell_type','patient'))
cell_types[cell_types$cell_type == "cancer",] = cell_types[cell_types$cell_type == "cancer",2]


# hmsc_rows = (startsWith(x = rownames(cell_types),prefix = "ACC.plate2") | startsWith(x = rownames(cell_types),prefix = "ACC1.")) & cell_types[,1] == "cancer" 
# acc_rows = !(startsWith(x = rownames(cell_types),prefix = "ACC.plate2") | startsWith(x = rownames(cell_types),prefix = "ACC1.")) & cell_types[,1] == "cancer" 
# cell_types[,1][hmsc_rows]  = "HMSC"
# cell_types[,1][acc_rows]  = "ACC"

#add to metadata:
cell_types[,2] = NULL 
cell_types[cell_types$cell_type == "ACC1",] = "HMSC"
acc_all_cells = AddMetaData(object =acc_all_cells ,metadata = cell_types,col.name = "cell.type")

CNV UMAP

library(infercnv)
library(tidyverse)
acc_annotation  = as.data.frame(acc_all_cells@meta.data[,"cell.type",drop = F])
acc_annotation = acc_annotation %>% rownames_to_column("orig.ident") 
acc_annotation = acc_annotation %>% mutate(orig.ident = gsub(x = acc_annotation$orig.ident,pattern = "\\.", replacement = "-") %>% 
  gsub(pattern = "_", replacement = "-"))
  

write.table(acc_annotation, "./Data/inferCNV/acc_annotation.txt", append = FALSE, 
            sep = "\t", dec = ".",row.names = FALSE, col.names = F)

infercnv_obj = CreateInfercnvObject(raw_counts_matrix="./Data/inferCNV/all.4icnv.txt", 
                                    annotations_file="./Data/inferCNV/acc_annotation.txt",
                                    delim="\t",gene_order_file="./Data/inferCNV/gencode_v19_gene_pos.txt"
                                    ,ref_group_names=c("CAF", "Endothelial", "WBC")) #groups of normal cells

infercnv_obj_default = infercnv::run(infercnv_obj, cutoff=1, out_dir='./Data/inferCNV/infercnv_output',
                                     cluster_by_groups=T, plot_steps=FALSE,
                                     denoise=TRUE, HMM=FALSE, no_prelim_plot=TRUE,
                                     png_res=300)
plot_cnv(infercnv_obj_default, output_format = "png",  write_expr_matrix = FALSE,out_dir = "./Data/inferCNV/",png_res   =800,obs_title = "Malignant cells",ref_title = "Normal cells")


cluster.info=FetchData(acc_all_cells,c("ident","orig.ident","UMAP_1","UMAP_2","nCount_RNA","nFeature_RNA","percent.mt","patient.ident","seurat_clusters"))
cluster.info$cell=rownames(cluster.info)

library(limma)
smoothed=apply(infercnv_obj_default@expr.data,2,tricubeMovingAverage, span=0.01)
cnsig=sqrt(apply((smoothed-1)^2,2,mean))
umap=cluster.info
names(cnsig)=umap$cell

acc_all_cells <- AddMetaData(object = acc_all_cells, metadata = cnsig, col.name = "copynumber")
acc_all_cells = SetIdent(object = acc_all_cells,value = "cell.type")
FeaturePlot(acc_all_cells, "copynumber",pt.size = 1, cols = c("lightblue","orange","red","darkred"),label = T,repel = T)
acc_cancer_cells <- AddMetaData(object = acc_cancer_cells, metadata = cnsig, col.name = "copynumber")
acc_cancer_cells = SetIdent(object = acc_cancer_cells,value = "cell.type")
FeaturePlot(acc_cancer_cells, "copynumber",pt.size = 1, cols = c("lightblue","orange","red","darkred"),label = T,repel = T)

CNV plot

CNV plot ## {-}

CNV subtypes

cnv_subtypes = as.data.frame(cutree(infercnv_obj_default@tumor_subclusters[["hc"]][["HMSC"]], k = 2))
names(cnv_subtypes)[1] = "cnv.cluster"
rownames(cnv_subtypes) = rownames(cnv_subtypes) %>% gsub(pattern = "-",replacement = "\\.")
infercnv.observations = data.frame(fread(file = "./Data/inferCNV/infercnv.observations.txt"), row.names=1)
Warning in fread(file = "./Data/inferCNV/infercnv.observations.txt") :
  Detected 1332 column names but the data has 1333 columns (i.e. invalid file). Added 1 extra default column name for the first column which is guessed to be row names or an index. Use setnames() afterwards if this guess is not correct, or fix the file write command that created the file to create a valid file.
names_to_keep = colnames(infercnv.observations) %in% (colnames(acc1_cancer_cells) %>% gsub(pattern = "_",replacement = "\\."))
infercnv.observations = infercnv.observations[,names_to_keep]

rownames(cnv_subtypes) = rownames(cnv_subtypes) %>% gsub(pattern = "2\\.",replacement = "2_")
rownames(cnv_subtypes) = rownames(cnv_subtypes) %>% gsub(pattern = "3\\.",replacement = "3_")
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = cnv_subtypes)
DimPlot(acc1_cancer_cells,group.by = "cnv.cluster",pt.size = 2,cols =colors)

Original score

original_myo_genes = c( "TP63", "TP73", "CAV1", "CDH3", "KRT5", "KRT14", "ACTA2", "TAGLN", "MYLK", "DKK3")
original_lum_genes = c("KIT", "EHF", "ELF5", "KRT7", "CLDN3", "CLDN4", "CD24", "LGALS3", "LCN2", "SLPI" )
calculate_score(dataset = all_acc_cancer_cells,myo_genes = original_myo_genes,lum_genes = original_lum_genes)
correlation of lum score and myo score: -0.45
correlation of lum score and original lum score: 1
correlation of myo score and original myo score: 1

Original score of ACC1

calculate_score(dataset = acc1_cancer_cells,myo_genes = original_myo_genes,lum_genes = original_lum_genes,lum_threshold = 0,myo_threshold = 0)
correlation of lum score and myo score: 0.06
correlation of lum score and original lum score: 1
correlation of myo score and original myo score: 1

0.35 Most correlated score

Myo genes

myo_protein_markers = c("CNN1", "TP63","ACTA2")
top_myo  = top_correlated(dataset = acc1_cancer_cells, genes = myo_protein_markers,threshold = 0.35)
print("Number of genes = " %>% paste(length(top_myo)))
[1] "Number of genes =  20"
message("Names of genes:")
Names of genes:
top_myo %>% head(30)
 [1] "COL16A1"     "RP1-39G22.4" "ACTG2"       "CD200"       "MYLK"        "TP63"        "KCNMB1"     
 [8] "ADAMTS2"     "LOXL2"       "TPM2"        "CLIC3"       "SNCG"        "ACTA2"       "TAGLN"      
[15] "A2M"         "NGFR"        "CNN1"        "PPP1R14A"    "MYL9"        "POM121L9P"  
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_myo,original_myo_genes) 
[1] "MYLK"  "TP63"  "ACTA2" "TAGLN"
myo_enrich_res = genes_vec_enrichment(genes = top_myo,background = rownames(acc1_cancer_cells),homer = T,title = "myo top enrichment",custom_pathways = luminal_gs)

myo_enrich_res

Lum genes

lum_protein_markers = c("KIT")
top_lum  = top_correlated(dataset = acc1_cancer_cells, genes = lum_protein_markers,threshold = 0.35,n_vargenes = 5000)
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
print("Number of genes = " %>% paste(length(top_lum)))
[1] "Number of genes =  5"
message("Names of genes:")
Names of genes:
top_lum %>% head(30)
[1] "B3GNT2"  "GLRB"    "EFNA5"   "ALDH3B2" "CCND1"  
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_lum,original_lum_genes) 
character(0)
lum_enrich_res = genes_vec_enrichment(genes = top_lum,background = rownames(acc1_cancer_cells),homer = T,title = "lum top enrichment",custom_pathways = luminal_gs)

lum_enrich_res

top correlated score

calculate_score(dataset = acc1_cancer_cells,myo_genes = top_myo,lum_genes = top_lum,lum_threshold = 0,myo_threshold = 0)
correlation of lum score and myo score: -0.05
correlation of lum score and original lum score: 0.58
correlation of myo score and original myo score: 0.77

enriched genes score

rownames(lum_enrich_res) = lum_enrich_res$pathway_name
lum_enriched_genes = lum_enrich_res[1,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,lum_protein_markers) #add original markers
rownames(myo_enrich_res) = myo_enrich_res$pathway_name
myo_enriched_genes = myo_enrich_res[1,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,myo_protein_markers) #add original markers
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_enriched_genes,lum_genes = lum_enriched_genes,lum_threshold = -2.5,myo_threshold = -2.5)
correlation of lum score and myo score: -0.16
correlation of lum score and original lum score: 0.43
correlation of myo score and original myo score: 0.79

0.2 Most correlated score

myo Genes

n_vargenes = 2000
myo_protein_markers = c("CNN1", "TP63","ACTA2")
top_myo  = top_correlated(dataset = acc1_cancer_cells, genes = myo_protein_markers,threshold = 0.2,n_vargenes = n_vargenes)
print("Number of genes = " %>% paste(length(top_myo)))
[1] "Number of genes =  48"
message("Names of genes:")
Names of genes:
top_myo %>% head(30)
 [1] "PLOD1"         "RP1-39G22.4"   "PDZK1"         "CSRP1"         "CHI3L1"        "HIST3H3"      
 [7] "AC104699.1"    "ACTG2"         "LYG1"          "DALRD3"        "CD200"         "RP11-627C21.1"
[13] "FGFBP2"        "IGFBP7"        "HSPB3"         "RP11-168A11.4" "SPARC"         "CD83"         
[19] "HLA-DOB"       "TBCC"          "NFKBIE"        "MIR3662"       "SMOC2"         "FBXL6"        
[25] "TPM2"          "SNCG"          "ACTA2"         "DKK3"          "MIR7113"       "CTA-797E19.3" 
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_myo,original_myo_genes) 
[1] "ACTA2" "DKK3"  "TAGLN"
myo_enrich_res = genes_vec_enrichment(genes = top_myo,background = VariableFeatures(acc1_cancer_cells) %>% head(n_vargenes),homer = T,title = "myo top enrichment",custom_pathways = luminal_gs)

myo_enrich_res

Lum Genes

acc1_cancer_cells = FindVariableFeatures(object = acc1_cancer_cells,nfeatures = 15000)
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
lum_protein_markers = c("KIT")
n_vargenes = 2000
top_lum  = top_correlated(dataset = acc1_cancer_cells, genes = lum_protein_markers,threshold = 0.3,n_vargenes = n_vargenes)
print("Number of genes = " %>% paste(length(top_lum)))
[1] "Number of genes =  13"
message("Names of genes:")
Names of genes:
top_lum %>% head(30)
 [1] "CSDE1"   "RRP9"    "TKT"     "TFG"     "ATP1B3"  "EFNA5"   "GABRP"   "CALML5"  "MMP7"    "SNORD68"
[11] "APP"     "SDF2L1"  "SAT1"   
message("Genes that also apeared in the original score:")
Genes that also apeared in the original score:
base::intersect(top_lum,original_lum_genes) 
character(0)
lum_enrich_res = genes_vec_enrichment(genes = top_lum,background = VariableFeatures(acc1_cancer_cells) %>% head(n_vargenes),homer = T,title = "lum top enrichment",custom_pathways = luminal_gs)

lum_enrich_res
calculate_score(dataset = acc1_cancer_cells,myo_genes = top_myo,lum_genes = top_lum,lum_threshold = 2,myo_threshold = 1)
correlation of lum score and myo score: -0.02
correlation of lum score and original lum score: 0.65
correlation of myo score and original myo score: 0.77

myo_intersected = intersect(top_myo,original_myo_genes) 
lum_intersected = intersect(top_lum,original_lum_genes) 
message("genes in myo score:")
genes in myo score:
myo_intersected
[1] "ACTA2" "DKK3"  "TAGLN"
message("genes in lum score:")
genes in lum score:
lum_intersected
[1] "LGALS3"
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_intersected,lum_genes = lum_intersected,lum_threshold = 2,myo_threshold = 1)
correlation of lum score and myo score: 0.03
correlation of lum score and original lum score: 0.69
correlation of myo score and original myo score: 0.81

enriched genes

rownames(lum_enrich_res) = lum_enrich_res$pathway_name
lum_enriched_genes = lum_enrich_res[3,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,lum_protein_markers) #add original markers
rownames(myo_enrich_res) = myo_enrich_res$pathway_name
myo_enriched_genes = myo_enrich_res[3,"geneID"] %>% strsplit(split = "/") %>% .[[1]] %>% c(.,myo_protein_markers) #add original markers
message("genes in myo score:")
genes in myo score:
myo_enriched_genes
 [1] "FBLIM1"  "NEXN"    "NMNAT2"  "MYLK"    "CCDC50"  "IGFBP7"  "RAI14"   "ARAP3"   "SPARC"   "CALD1"   "LOXL2"  
[12] "COL5A1"  "ACTA2"   "DKK3"    "MSRB3"   "COL4A1"  "ACTN1"   "TPM1"    "TGFB1I1" "ADORA2B" "MXRA7"   "CNN1"   
[23] "TP63"    "ACTA2"  
message("genes in lum score:")
genes in lum score:
lum_enriched_genes
 [1] "PADI2"      "PATJ"       "PEX11B"     "APH1A"      "C1orf43"    "EFNA4"      "NECTIN4"    "SCYL3"      "ELF3"      
[10] "SOX13"      "IRF6"       "MED28"      "EPB41L4A"   "RGL2"       "C6orf132"   "TPD52L1"    "ICA1"       "MACC1"     
[19] "TRPS1"      "FAM83H"     "RASEF"      "ARRDC1"     "COMMD3"     "ANK3"       "GSTO2"      "PDCD4"      "EHF"       
[28] "ALDH3B2"    "SHANK2"     "SORL1"      "FKBP4"      "PTPN6"      "DUSP16"     "RERG"       "ADCY6"      "ERBB3"     
[37] "ERP29"      "SUSD6"      "RPS6KA5"    "SPINT1"     "FEM1B"      "TLE3"       "SCAMP2"     "CLN3"       "ADGRG1"    
[46] "ATP2C2"     "GGT6"       "MYO1D"      "ST6GALNAC2" "CYB5A"      "BLVRB"      "VRK3"       "SYCP2"      "TMPRSS2"   
[55] "LIMK2"      "KIT"       
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_enriched_genes,lum_genes = lum_enriched_genes,lum_threshold = 0,myo_threshold = -1)
correlation of lum score and myo score: 0.1
correlation of lum score and original lum score: 0.71
correlation of myo score and original myo score: 0.76

enriched genes and in original score

myo_enriched_genes = myo_enriched_genes[myo_enriched_genes %in% original_myo_genes]
lum_enriched_genes = lum_enriched_genes[lum_enriched_genes %in% original_lum_genes]

message("genes in myo score:")
genes in myo score:
myo_enriched_genes
[1] "MYLK"  "ACTA2" "DKK3"  "TP63"  "ACTA2"
message("genes in lum score:")
genes in lum score:
lum_enriched_genes
[1] "EHF" "KIT"
calculate_score(dataset = acc1_cancer_cells,myo_genes = myo_enriched_genes,lum_genes = lum_enriched_genes,lum_threshold = 2,myo_threshold = 2)
correlation of lum score and myo score: -0.07
correlation of lum score and original lum score: 0.62
correlation of myo score and original myo score: 0.77

HPV

Only HMSC cancer cells:

HPV33_P3 = fread("./Data/HPV33_P3.txt",col.names = c("plate","reads")) %>% as.data.frame()
HPV33_P3.df = HPV33_P3 %>% mutate(
  plate = gsub(x =HPV33_P3$plate, replacement = "",pattern = "_.*$") 
  %>% gsub(pattern = "-P",replacement = ".P") 
  %>% gsub(pattern = "-",replacement = "_",)
  )
HPV33_P3.df = HPV33_P3.df %>% dplyr::filter(HPV33_P3.df$plate %in% colnames(acc1_cancer_cells))
rownames(HPV33_P3.df)  <- HPV33_P3.df$plate
HPV33_P3.df$plate = NULL


HPV33_P2 = fread("./Data/HPV33_P2.txt",col.names = c("plate","reads")) %>% as.data.frame()
HPV33_P2.df = HPV33_P2 %>% mutate(
  plate = gsub(x =HPV33_P2$plate, replacement = "",pattern = "_.*$") 
  %>% gsub(pattern = "plate2-",replacement = "plate2_",)
  %>% gsub(pattern = "-",replacement = "\\.",)
  )
HPV33_P2.df = HPV33_P2.df %>% dplyr::filter(HPV33_P2.df$plate %in% colnames(acc1_cancer_cells))
rownames(HPV33_P2.df)  <- HPV33_P2.df$plate
HPV33_P2.df$plate = NULL

HPV33 = rbind(HPV33_P3.df,HPV33_P2.df)
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = HPV33,col.name = "HPV33.reads")
FeaturePlot(acc1_cancer_cells,features = "HPV33.reads",max.cutoff = 10)

library(plotly)
data = FetchData(object = acc1_cancer_cells,vars = "HPV33.reads")
data = data$HPV33
data = data[data<100]
plot_ly(x = data, type = "histogram", nbinsx = 10)

data = FetchData(object = acc1_cancer_cells,vars = "HPV33.reads")
print(
  data %>% 
  ggplot(aes( x=HPV33.reads)) + 
  geom_histogram() + scale_x_continuous(breaks=c(1,3,10,100))
)
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

hpv33_positive = HPV33 %>% dplyr::mutate(hpv33_positive = case_when(reads >= 10 ~ "positive",
                                                                    reads < 10 ~ "negative")
)



hpv33_positive$reads = NULL
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = hpv33_positive)
DimPlot(object = acc1_cancer_cells,group.by  = c("hpv33_positive"),pt.size = 2)

cNMF

library(reticulate)
acc1_cancer_cells = FindVariableFeatures(object = acc1_cancer_cells,nfeatures = nfeatures)
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
from cnmf import cNMF
import numpy as np
nfeatures_name = r.nfeatures_name
name = 'HMSC_cNMF_'+nfeatures_name+'Kvargenes'
outdir = './Data/cNMF'
K_range = np.arange(3,10)
cnmf_obj = cNMF(output_dir=outdir, name=name)
counts_fn='./Data/cNMF/hmsc_expressionData_'+nfeatures_name+'Kvargenes.txt'
tpm_fn = counts_fn ## This is a weird case where because this dataset is not 3' end umi sequencing, we opted to use the TPM matrix as the input matrix rather than the count matrix

cnmf_obj.prepare(counts_fn=counts_fn, components=K_range, seed=14,tpm_fn=tpm_fn)
cnmf_obj.factorize(worker_i=0, total_workers=1)
cnmf_obj.combine()
cnmf_obj.k_selection_plot()

Save object

# import pickle
# f = open('./Data/cNMF/HMSC_cNMF_'+nfeatures_name+'Kvargenes/cnmf_obj.pckl', 'wb')
# pickle.dump(cnmf_obj, f)
# f.close()

Load object

from cnmf import cNMF
import pickle
nfeatures = "2K"
f = open('./Data/cNMF/HMSC_cNMF_' + nfeatures+ 'vargenes/cnmf_obj.pckl', 'rb')
cnmf_obj = pickle.load(f)
f.close()
selected_k = 4
density_threshold = 0.1
cnmf_obj.consensus(k=selected_k, density_threshold=density_threshold,show_clustering=True)
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/lib/python3.7/site-packages/scanpy/preprocessing/_simple.py:843: UserWarning: Received a view of an AnnData. Making a copy.
  view_to_actual(adata)
usage_norm, gep_scores, gep_tpm, topgenes = cnmf_obj.load_results(K=selected_k, density_threshold=density_threshold)
gep_scores = py$gep_scores
gep_tpm = py$gep_tpm
all_metagenes= py$usage_norm

Enrichment analysis by top 200 genes of each program

plt_list = list()
for (i in 1:ncol(gep_scores)) {
  top_genes = gep_scores  %>%  arrange(desc(gep_scores[i])) #sort by score a
  top = head(rownames(top_genes),200) #take top top_genes_num
  res = genes_vec_enrichment(genes = top,background = rownames(gep_scores),homer = T,title = 
                    i,silent = T,return_all = T)
   
  plt_list[[i]] = res$plt
}
gridExtra::grid.arrange(grobs = plt_list)


canonical_pathways = msigdbr(species = "Homo sapiens", category = "C2") %>% dplyr::filter(gs_subcat != "CGP") %>%  dplyr::distinct(gs_name, gene_symbol) 

plt_list = list()
for (i in 1:ncol(gep_scores)) {
  top_genes = gep_scores  %>%  arrange(desc(gep_scores[i])) #sort by score a
  top = head(rownames(top_genes),200) #take top top_genes_num
  res = genes_vec_enrichment(genes = top,background = rownames(gep_scores),homer = T,title = 
                    i,silent = T,return_all = T,custom_pathways = canonical_pathways)
   
  plt_list[[i]] = res$plt
}
gridExtra::grid.arrange(grobs = plt_list)

# lum genes in metagenes
message("lum genes in metagenes")
lum genes in metagenes
for (i in 1:ncol(gep_scores)) {
  top_genes = gep_scores  %>%  arrange(desc(gep_scores[i])) #sort by score a
  top = head(rownames(top_genes),200) #take top top_genes_num
  cat(paste0("metagene ",i,": "))
  print(original_lum_genes[original_lum_genes %in% top])

}
metagene 1: [1] "KRT7" "SLPI"
metagene 2: [1] "CLDN4"
metagene 3: character(0)
metagene 4: [1] "KRT7"   "LGALS3" "LCN2"   "SLPI"  
cat("\n")
# myo genes in metagenes
message("myo genes in metagenes")
myo genes in metagenes
for (i in 1:ncol(gep_scores)) {
  top_genes = gep_scores  %>%  arrange(desc(gep_scores[i])) #sort by score a
  top = head(rownames(top_genes),200) #take top top_genes_num
  cat(paste0("metagene ",i,": "))
  print(original_myo_genes[original_myo_genes %in% top])

}
metagene 1: [1] "KRT14" "ACTA2" "DKK3" 
metagene 2: character(0)
metagene 3: character(0)
metagene 4: character(0)
cat("\n")
notch_genes = c("JAG1","JAG2","NOTCH3","NOTCH2","NOTCH1","DLL1","MYB","HES4","HEY1","HEY2","NRARP")
# notch genes in metagenes
message("myo genes in metagenes")
myo genes in metagenes
for (i in 1:ncol(gep_scores)) {
  top_genes = gep_scores  %>%  arrange(desc(gep_scores[i])) #sort by score a
  top = head(rownames(top_genes),200) #take top top_genes_num
  cat(paste0("metagene ",i,": "))
  print(notch_genes[notch_genes %in% top])

}
metagene 1: character(0)
metagene 2: character(0)
metagene 3: character(0)
metagene 4: character(0)
cat("\n")
plt_list = list()
for (i in 1:ncol(gep_scores)) {
  top_genes = gep_scores  %>%  arrange(desc(gep_scores[i])) #sort by score a
  top = head(rownames(top_genes),10) #take top top_genes_num
  message(paste("program ",i,"top genes:"))
  print(top)

}
program  1 top genes:
 [1] "IGKV5-2" "NDUFB7"  "LGALS1"  "UBL5"    "S100A6"  "S100A11" "CUTA"    "IFITM3"  "JTB"     "IL17RB" 
program  2 top genes:
 [1] "EGR1"    "C6orf62" "JUNB"    "DNAJA1"  "ATF3"    "IER2"    "ERRFI1"  "KLF6"    "CDKN1A"  "MTHFD2" 
program  3 top genes:
 [1] "RP1-128M12.3"  "RP11-374F3.3"  "RP11-403A21.3" "RP11-454L9.2"  "AC097500.1"    "RP11-463H24.4" "RP11-515O17.2"
 [8] "RP11-536L3.4"  "PALD1"         "AL049758.2"   
program  4 top genes:
 [1] "LTF"           "PIGR"          "FMO2"          "HSD17B2"       "MLPH"          "PRR15L"        "RF00019.219"  
 [8] "RP11-817J15.2" "CD14"          "CLU"          

meta3 = FetchData(object = acc1_cancer_cells,vars = c("metagene.3"))
ggplot(meta3, aes(x=metagene.3)) + 
  geom_density()

meta3[,1] = as.numeric(meta3[,1])
sum(meta3[,1]>0,na.rm = T )
[1] 82
sum(meta3[,1]==0,na.rm = T )
[1] 53

assignment UMAP

larger_by = 2
message(paste("larger_by = ", larger_by))
larger_by =  2
acc1_cancer_cells = program_assignment(dataset = acc1_cancer_cells,larger_by = larger_by,program_names = colnames(all_metagenes))
selected_k =4
colors =  rainbow(selected_k)
colors = c(colors,"grey")
DimPlot(acc1_cancer_cells,group.by = "program.assignment",pt.size = 2,cols =colors)

show cell cycle program:

hallmark_name = "GO_MITOTIC_CELL_CYCLE"
genesets  =GSEABase::getGmt("./Data/h.all.v7.0.symbols.pluscc.gmt")
var_features=acc1_cancer_cells@assays$RNA@var.features
geneIds= genesets[[hallmark_name]]@geneIds
score <- apply(acc1_cancer_cells@assays$RNA@data[intersect(geneIds,var_features),],2,mean)
acc1_cancer_cells=AddMetaData(acc1_cancer_cells,score,hallmark_name)
FeaturePlot(object = acc1_cancer_cells,features = hallmark_name)

cc_vs_program2 = FetchData(object = acc1_cancer_cells,vars = c("metagene.2",hallmark_name))
cor(cc_vs_program2[1],cc_vs_program2[2])
           GO_MITOTIC_CELL_CYCLE
metagene.2             0.6127281

Comparisions

cnv_vs_hpv = FetchData(object = acc1_cancer_cells,vars = c("cnv.cluster","hpv33_positive"))
test <- fisher.test(table(cnv_vs_hpv))
ggbarstats(
  cnv_vs_hpv, cnv.cluster, hpv33_positive,
  results.subtitle = FALSE,
  subtitle = paste0(
    "Fisher's exact test", ", p-value = ",
    ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
  )
)
cnv_vs_cnmf = FetchData(object = acc1_cancer_cells,vars = c("program.assignment","cnv.cluster"))
cnv_vs_cnmf = cnv_vs_cnmf %>% dplyr::filter(program.assignment == "1" |program.assignment == "2" )
test <- fisher.test(table(cnv_vs_cnmf))
ggbarstats(
  cnv_vs_cnmf, program.assignment, cnv.cluster,
  results.subtitle = FALSE,
  subtitle = paste0(
    "Fisher's exact test", ", p-value = ",
    ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
  )
)
cnmf_vs_hpv = FetchData(object = acc1_cancer_cells,vars = c("program.assignment","hpv33_positive"))
cnmf_vs_hpv = cnmf_vs_hpv %>% dplyr::filter(program.assignment == "1" |program.assignment == "2" )
test <- fisher.test(table(cnmf_vs_hpv))
ggbarstats(
  cnmf_vs_hpv, program.assignment, hpv33_positive,
  results.subtitle = FALSE,
  subtitle = paste0(
    "Fisher's exact test", ", p-value = ",
    ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
  )
)
myb_vs_cnv = FetchData(object = acc1_cancer_cells,vars = c("cnv.cluster","MYB"))
myb_vs_cnv $cnv.cluster = as.character(myb_vs_cnv $cnv.cluster )

ggboxplot(myb_vs_cnv, x = "cnv.cluster", y = "MYB",
          palette = "jco",
          add = "jitter")+ stat_compare_means(method = "wilcox.test",comparisons = list(c("1","2")))
myb_vs_hpv = FetchData(object = acc1_cancer_cells,vars = c("hpv33_positive","MYB"))
myb_vs_hpv $hpv33_positive = as.character(myb_vs_hpv $hpv33_positive )

ggboxplot(myb_vs_hpv, x = "hpv33_positive", y = "MYB",
          palette = "jco",
          add = "jitter")+ stat_compare_means(method = "wilcox.test",comparisons = list(c("positive","negative")))+ stat_summary(fun.data = function(x) data.frame(y=15, label = paste("Mean=",round(mean(x),digits = 2))), geom="text") +ylab("log2(MYB)")
hpvReads_vs_myb = FetchData(object = acc1_cancer_cells,vars = c("HPV33.reads","MYB"))
corr = cor(hpvReads_vs_myb$HPV33.reads,hpvReads_vs_myb$MYB)
print("correlation of MYB abd hpv33_reads:" %>% paste(corr %>% round(digits = 2)))
acc1_cancer_cells_data = acc1_cancer_cells@assays[["RNA"]]@data %>% as.data.frame()
acc1_cancer_cells_data = cor(acc1_cancer_cells_data)
annotation = FetchData(object = acc1_cancer_cells,vars = c("orig.ident"))

colors <- c(seq(-1,1,by=0.01))
  my_palette <- c("blue",colorRampPalette(colors = c("blue", "white", "red"))
                                                   (n = length(colors)-3), "red")
  
pht1 = pheatmap(acc1_cor,annotation_col  = annotation,fontsize = 6,breaks = colors, color = my_palette,show_colnames = F,show_rownames = F)

acc1_cancer_cells_data = acc1_cancer_cells@assays[["RNA"]]@data %>% as.data.frame()
acc1_cancer_cells_data = cor(acc1_cancer_cells_data)
annotation = FetchData(object = acc1_cancer_cells,vars = c("cnv.cluster"))

colors <- c(seq(-1,1,by=0.01))
  my_palette <- c("blue",colorRampPalette(colors = c("blue", "white", "red"))
                                                   (n = length(colors)-3), "red")
  
pht1 = pheatmap(acc1_cor,annotation_col  = annotation,fontsize = 6,breaks = colors, color = my_palette,show_colnames = F,show_rownames = F)
cnv_vs_plate = FetchData(object = acc1_cancer_cells,vars = c("cnv.cluster","orig.ident"))
test <- fisher.test(table(cnv_vs_plate))
ggbarstats(
  cnv_vs_plate, cnv.cluster, orig.ident,
  results.subtitle = FALSE,
  subtitle = paste0(
    "Fisher's exact test", ", p-value = ",
    ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
  )
)
# creat colours for each group
cnv_vs_plate = FetchData(object = acc1_cancer_cells,vars = c("cnv.cluster","orig.ident"))

rownames(cnv_vs_plate) = rownames(cnv_vs_plate) %>% gsub(pattern = "_",replacement = "\\.")
cnv_vs_plate$cnv.cluster = as.character(cnv_vs_plate$cnv.cluster)
cnv_vs_plate = cnv_vs_plate %>% dplyr::rename(plate = "orig.ident")
annoCol <- list(plate = c(ACC1.P3 = "red",ACC.plate2 = "green"),cnv.cluster =c("1"="green","2" = "red"))

pheatmap(infercnv.observations2,cluster_cols = F,cluster_rows = F, show_rownames = F,show_colnames = F, breaks = breaks,color = colorRampPalette(rev(c("darkred", "white", "darkblue")))(15),annotation_row = cnv_vs_plate,annotation_colors = annoCol)

cnmf_vs_plate = FetchData(object = acc1_cancer_cells,vars = c("program.assignment","orig.ident"))
cnmf_vs_plate= cnmf_vs_plate %>% dplyr::filter(program.assignment == "1" | program.assignment == "2")
test <- fisher.test(table(cnmf_vs_plate))
ggbarstats(
  cnmf_vs_plate, program.assignment, orig.ident,
  results.subtitle = FALSE,
  subtitle = paste0(
    "Fisher's exact test", ", p-value = ",
    ifelse(test$p.value < 0.001, "< 0.001", round(test$p.value, 3))
  )
)
plate_3 = cnmf_vs_plate %>% dplyr::filter((program.assignment == 1 & orig.ident == "ACC1.P3") ) %>% rownames() 
plate_2 = cnmf_vs_plate %>% dplyr::filter((program.assignment == 2 & orig.ident == "ACC.plate2")) %>% rownames()
cells = list(ACC1.P3 = plate_3,ACC.plate2  = plate_2)

Results

exceptions

exceptions_plt = DimPlot(object = acc1_cancer_cells, cells.highlight = cells, cols.highlight = c("green","red"), cols = "gray", order = TRUE,pt.size = 2,sizes.highlight = 2,combine = F) 

Warning messages: 1: No Python documentation found for ‘cnmf_obj.prepare’. 2: No Python documentation found for ‘cnmf_obj.prepare’.

colors = c("green","red","cyan","purple","grey")
program.assignment_plt = DimPlot(acc1_cancer_cells,group.by = "program.assignment",pt.size = 2,cols = colors,combine = F)
metagene.1_plt <- FeaturePlot(object = acc1_cancer_cells,features = c("metagene.1"),combine = F)
metagene.2_plt = FeaturePlot(object = acc1_cancer_cells,features = c("metagene.2"),combine = F)

lst = list(exceptions = exceptions_plt, program.assignment = program.assignment_plt,metagene.1 = metagene.1_plt,metagene.2 = metagene.2_plt)

for (i in 1:length(lst)) {
  cat("### ",names(lst)[i]," \n")
  print(
    lst[[i]]
    )
  plot.new()
  dev.off()
  cat(' \n\n')
}

exceptions

[[1]] Warning in grSoftVersion() : unable to load shared object ‘/usr/local/lib/R/modules//R_X11.so’: libXt.so.6: cannot open shared object file: No such file or directory

program.assignment

[[1]]

metagene.1

[[1]]

metagene.2

[[1]]

NA

no_neg <- function(x) {
  x = x + abs(min(x))
  x
}
# gep_scores_norm = apply(gep_scores, MARGIN = 2, FUN = min_max_normalize)%>% as.data.frame()
# gep_scores_norm = gep_scores
gep_scores_norm = apply(gep_scores, MARGIN = 2, FUN = no_neg)%>% as.data.frame()
gep_scores_norm = sum2one(gep_scores_norm)
all_metagenes = expression_mult(gep_scores = gep_scores_norm,dataset = all_acc_cancer_cells,top_genes = T,z_score = F)
# Make metagene names
for (i in 1:ncol(all_metagenes)) {
  colnames(all_metagenes)[i] = "metagene." %>% paste0(i)
}


#add each metagene to metadata
for (i in 1:ncol(all_metagenes)) {
  metage_metadata = all_metagenes %>% select(i)
  all_acc_cancer_cells = AddMetaData(object = all_acc_cancer_cells,metadata = metage_metadata)
}

FeaturePlot(object = all_acc_cancer_cells,features = colnames(all_metagenes),max.cutoff = 100)

LS0tCnRpdGxlOiAiVGl0bGUiCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQotLS0KYGBge3IsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBpbmNsdWRlID0gVAopCmBgYAoKIyMgRGF0YQoKYGBge3J9CmFjYzFfY2FuY2VyX2NlbGxzID0gcmVhZFJEUygiLi9EYXRhL2FjYzFfY2FuY2VyX2NlbGxzXzE1S25Db3VudF9WMy5SRFMiKQphbGxfYWNjX2NhbmNlcl9jZWxscyA9IHJlYWRSRFMoIi4vRGF0YS9hY2NfY2FuY2VyX2NlbGxzX1Y0LlJEUyIpCmFjY19hbGxfY2VsbHMgPSByZWFkUkRTKCIuL0RhdGEvYWNjX3RwbV9uQ291bnRfbWl0b19ubzE0Nl8xNWtfd2l0aF9BQ0MxXy5SRFMiKQphY2NfY2FuY2VyQ2VsbHNfbm9BQ0MxID0gc3Vic2V0KGFsbF9hY2NfY2FuY2VyX2NlbGxzLHN1YnNldCA9IHBhdGllbnQuaWRlbnQhPSAiSE1TQyIpCgpsdW1pbmFsX3BhdGh3YXlzID0gYygiQ0hBUkFGRV9CUkVBU1RfQ0FOQ0VSX0xVTUlOQUxfVlNfQkFTQUxfRE4iLCJDSEFSQUZFX0JSRUFTVF9DQU5DRVJfTFVNSU5BTF9WU19CQVNBTF9VUCIsIkNIQVJBRkVfQlJFQVNUX0NBTkNFUl9MVU1JTkFMX1ZTX01FU0VOQ0hZTUFMX0ROIiwiQ0hBUkFGRV9CUkVBU1RfQ0FOQ0VSX0xVTUlOQUxfVlNfTUVTRU5DSFlNQUxfVVAiLCJIVVBFUl9CUkVBU1RfQkFTQUxfVlNfTFVNSU5BTF9ETiIsIkxJTV9NQU1NQVJZX0xVTUlOQUxfUFJPR0VOSVRPUl9VUCIsIlNNSURfQlJFQVNUX0NBTkNFUl9MVU1JTkFMX0JfVVAiICkKCiMgYWRkIGx1bWluYWwgcGF0aHdheXMKbHVtaW5hbF9ncyA9IG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiKSAlPiVhcy5kYXRhLmZyYW1lKCkgJT4lIGRwbHlyOjpmaWx0ZXIoZ3NfbmFtZSAlaW4lIGx1bWluYWxfcGF0aHdheXMpJT4lIGRwbHlyOjpkaXN0aW5jdChnc19uYW1lLCBnZW5lX3N5bWJvbCkgJT4lIGFzLmRhdGEuZnJhbWUoKQoKYGBgCgojIyBQYXJhbWV0ZXJzCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpgYGAKCgojIyBmdW5jdGlvbnMKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CnNvdXJjZV9mcm9tX2dpdGh1YihyZXBvc2l0b3kgPSAiREVHX2Z1bmN0aW9ucyIsdmVyc2lvbiA9ICIwLjIuMjQiKQpzb3VyY2VfZnJvbV9naXRodWIocmVwb3NpdG95ID0gIkhNU0NfZnVuY3Rpb25zIix2ZXJzaW9uID0gIjAuMS4xMyIsc2NyaXB0X25hbWUgPSAiZnVuY3Rpb25zLlIiKQpzb3VyY2VfZnJvbV9naXRodWIocmVwb3NpdG95ID0gImNOTUZfZnVuY3Rpb25zIix2ZXJzaW9uID0gIjAuMy43MiIsc2NyaXB0X25hbWUgPSAiY25tZl9mdW5jdGlvbl9IYXJtb255LlIiKQpgYGAKIyMgVU1BUApgYGB7ciB9CkRpbVBsb3Qob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMscHQuc2l6ZSA9IDIpCmBgYApgYGB7cn0KZGVnID0gRmluZEFsbE1hcmtlcnMob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbG9nZmMudGhyZXNob2xkID0gMSxmZWF0dXJlcyA9IFZhcmlhYmxlRmVhdHVyZXMoYWNjMV9jYW5jZXJfY2VsbHMpKQpmb3IgKGNsdXN0ZXIgaW4gdW5pcXVlKGRlZyRjbHVzdGVyKSkgewogIHByaW50KGRlZ1tkZWckY2x1c3RlciA9PSBjbHVzdGVyLF0pCn0KYGBgCmBgYHtyfQpmb3IgKGNsdXN0ZXIgaW4gdW5pcXVlKGRlZyRjbHVzdGVyKSkgewogIGRlZ19vZl9jbHVzdGVyID0gZGVnW2RlZyRjbHVzdGVyID09IGNsdXN0ZXIsXQogIGVucmljaG1lbnRfYW5hbHlzaXMoZGVnX29mX2NsdXN0ZXIsYmFja2dyb3VuZCA9IFZhcmlhYmxlRmVhdHVyZXMoYWNjMV9jYW5jZXJfY2VsbHMpLGZkcl9DdXRvZmYgPSAwLjAxLGlkZW50LjEgPSBwYXN0ZSgiQ2x1c3RlciIsY2x1c3RlciksaWRlbnQuMiA9ICBwYXN0ZSgiQ2x1c3RlciIsY2x1c3Rlciksc2hvd19ieSA9IDEpCn0KCgpgYGAKCiMjIGZlYXR1cmVzIFVNQVAKYGBge3IgZmlnLndpZHRoPTEwfQpGZWF0dXJlUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGMoIlRQNjMiLCJBQ1RBMiIsIklMMTJCIiwiQ05OMSIpKQpgYGAKCgojIyBFbnJpY2htZW50IGFuYWx5c2lzIEhNU0MgdnMgQUNDCmBgYHtyIGZpZy53aWR0aD04LCBlY2hvPVRSVUUscmVzdWx0cz0naGlkZScsZmlnLmtlZXA9J2FsbCd9CmFsbF9hY2NfY2FuY2VyX2NlbGxzID0gU2V0SWRlbnQoYWxsX2FjY19jYW5jZXJfY2VsbHMsIHZhbHVlID0icGF0aWVudC5pZGVudCIpCmFjY19kZWcgPC0gRmluZE1hcmtlcnMoYWxsX2FjY19jYW5jZXJfY2VsbHMsIGlkZW50LjEgPSAiSE1TQyIsbG9nZmMudGhyZXNob2xkID0gMS41LGZlYXR1cmVzID0gVmFyaWFibGVGZWF0dXJlcyhhbGxfYWNjX2NhbmNlcl9jZWxscykpCmVucmljaG1lbnRfYW5hbHlzaXMoYWNjX2RlZyxiYWNrZ3JvdW5kID0gVmFyaWFibGVGZWF0dXJlcyhhbGxfYWNjX2NhbmNlcl9jZWxscyksZmRyX0N1dG9mZiA9IDAuMDEsaWRlbnQuMSA9ICJITVNDIixpZGVudC4yID0gIkFDQyIsc2hvd19ieSA9IDEpCmBgYAoKIyMgY2VsbCBjeWNsZSBmaWx0ZXJpbmcgey50YWJzZXR9CgpgYGB7cn0KaGFsbG1hcmtfbmFtZSA9ICJHT19NSVRPVElDX0NFTExfQ1lDTEUiCmdlbmVzZXRzICA9Z2V0R210KCIuL0RhdGEvaC5hbGwudjcuMC5zeW1ib2xzLnBsdXNjYy5nbXQiKQp2YXJfZmVhdHVyZXM9YWNjX2NhbmNlckNlbGxzX25vQUNDMUBhc3NheXMkUk5BQHZhci5mZWF0dXJlcwpnZW5lSWRzPSBnZW5lc2V0c1tbaGFsbG1hcmtfbmFtZV1dQGdlbmVJZHMKc2NvcmUgPC0gYXBwbHkoYWNjX2NhbmNlckNlbGxzX25vQUNDMUBhc3NheXMkUk5BQHNjYWxlLmRhdGFbaW50ZXJzZWN0KGdlbmVJZHMsdmFyX2ZlYXR1cmVzKSxdLDIsbWVhbikKYWNjX2NhbmNlckNlbGxzX25vQUNDMT1BZGRNZXRhRGF0YShhY2NfY2FuY2VyQ2VsbHNfbm9BQ0MxLHNjb3JlLGhhbGxtYXJrX25hbWUpCgpoYWxsbWFya19uYW1lID0gIkdPX01JVE9USUNfQ0VMTF9DWUNMRSIKZ2VuZXNldHMgID1nZXRHbXQoIi4vRGF0YS9oLmFsbC52Ny4wLnN5bWJvbHMucGx1c2NjLmdtdCIpCnZhcl9mZWF0dXJlcz1hY2MxX2NhbmNlcl9jZWxsc0Bhc3NheXMkUk5BQHZhci5mZWF0dXJlcwpnZW5lSWRzPSBnZW5lc2V0c1tbaGFsbG1hcmtfbmFtZV1dQGdlbmVJZHMKc2NvcmUgPC0gYXBwbHkoYWNjMV9jYW5jZXJfY2VsbHNAYXNzYXlzJFJOQUBzY2FsZS5kYXRhW2ludGVyc2VjdChnZW5lSWRzLHZhcl9mZWF0dXJlcyksXSwyLG1lYW4pCmFjYzFfY2FuY2VyX2NlbGxzPUFkZE1ldGFEYXRhKGFjYzFfY2FuY2VyX2NlbGxzLHNjb3JlLGhhbGxtYXJrX25hbWUpCgoKIyBoYWxsbWFya19uYW1lID0gIkdPX01JVE9USUNfQ0VMTF9DWUNMRSIKIyBnZW5lc2V0cyAgPWdldEdtdCgiLi9EYXRhL2guYWxsLnY3LjAuc3ltYm9scy5wbHVzY2MuZ210IikKIyB2YXJfZmVhdHVyZXM9YWNjMV9jYW5jZXJfY2VsbHNAYXNzYXlzJFJOQUB2YXIuZmVhdHVyZXMKIyBnZW5lSWRzPSBnZW5lc2V0c1tbaGFsbG1hcmtfbmFtZV1dQGdlbmVJZHMKIyBzY29yZSA8LSBhcHBseShhY2MxX2NhbmNlcl9jZWxsc0Bhc3NheXMkUk5BQHNjYWxlLmRhdGFbaW50ZXJzZWN0KGdlbmVJZHMsdmFyX2ZlYXR1cmVzKSxdLDIsbWVhbikKIyBhY2MxX2NhbmNlcl9jZWxscz1BZGRNZXRhRGF0YShhY2MxX2NhbmNlcl9jZWxscyxzY29yZSxoYWxsbWFya19uYW1lKQoKCmBgYAoKYGBge3J9CmxpYnJhcnkoaGlnaGNoYXJ0ZXIpIApvcHRpb25zKGhpZ2hjaGFydGVyLnRoZW1lID0gaGNfdGhlbWVfc21wbCh0b29sdGlwID0gbGlzdCh2YWx1ZURlY2ltYWxzID0gMikpKQoKY2Nfc2NvcmVzID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSAiR09fTUlUT1RJQ19DRUxMX0NZQ0xFIikKIGhjaGFydCgKICBkZW5zaXR5KGNjX3Njb3JlcyRHT19NSVRPVElDX0NFTExfQ1lDTEUpLCAKICB0eXBlID0gImFyZWEiLCBuYW1lID0gIkdPX01JVE9USUNfQ0VMTF9DWUNMRSIKICApCgpgYGAKYGBge3J9CmNjX3Njb3JlcyA9IEZldGNoRGF0YShvYmplY3QgPSBhY2NfY2FuY2VyQ2VsbHNfbm9BQ0MxLHZhcnMgPSAiR09fTUlUT1RJQ19DRUxMX0NZQ0xFIikKIGhjaGFydCgKICBkZW5zaXR5KGNjX3Njb3JlcyRHT19NSVRPVElDX0NFTExfQ1lDTEUpLCAKICB0eXBlID0gImFyZWEiLCBuYW1lID0gIkdPX01JVE9USUNfQ0VMTF9DWUNMRSIKICApCgpgYGAKCgoKCgoKIyMjIEJlZm9yZSBjYyBmaWx0ZXJpbmcKYGBge3Igd2FybmluZz1GQUxTRX0KCiNmaWx0ZXI6CmFsbF9hY2NfY2FuY2VyX2NlbGxzX2NjRmlsdGVyZWQ9YWxsX2FjY19jYW5jZXJfY2VsbHNbLGFsbF9hY2NfY2FuY2VyX2NlbGxzQG1ldGEuZGF0YVtbaGFsbG1hcmtfbmFtZV1dPCAwLjE2XQoKCm1pbl90aHJlc2hvbGQgPSBtaW4oYWxsX2FjY19jYW5jZXJfY2VsbHMkR09fTUlUT1RJQ19DRUxMX0NZQ0xFKQptYXhfdGhyZXNob2xkID0gbWF4KGFsbF9hY2NfY2FuY2VyX2NlbGxzJEdPX01JVE9USUNfQ0VMTF9DWUNMRSkKYGBgCgpgYGB7cn0KbGlicmFyeSh2aXJpZGlzKQpGZWF0dXJlUGxvdChvYmplY3QgPSBhbGxfYWNjX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGhhbGxtYXJrX25hbWUpICsgZ2d0aXRsZSgiQmVmb3JlIGNjIGZpbHRlcmluZyIpICAmIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvdXJzID0gcGxhc21hKG4gPSAxMCwgZGlyZWN0aW9uID0gLTEpLCBsaW1pdHMgPSBjKG1pbl90aHJlc2hvbGQsIG1heF90aHJlc2hvbGQpKQpgYGAKCgojIyMgQWZ0ZXIgY2MgZmlsdGVyaW5nCgpgYGB7cn0KRmVhdHVyZVBsb3Qob2JqZWN0ID0gYWxsX2FjY19jYW5jZXJfY2VsbHNfY2NGaWx0ZXJlZCxmZWF0dXJlcyA9IGhhbGxtYXJrX25hbWUpICsgZ2d0aXRsZSgiQWZ0ZXIgY2MgZmlsdGVyaW5nIikgJiBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3VycyA9IHBsYXNtYShuID0gMTAsIGRpcmVjdGlvbiA9IC0xKSwgbGltaXRzID0gYyhtaW5fdGhyZXNob2xkLCBtYXhfdGhyZXNob2xkKSkKYGBgCmBgYHtyfQpEaW1QbG90KG9iamVjdCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzX2NjRmlsdGVyZWQsZ3JvdXAuYnkgPSAicGF0aWVudC5pZGVudCIpCmBgYAoKYGBge3IgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CmFsbF9hY2NfY2FuY2VyX2NlbGxzX2NjRmlsdGVyZWQgPSBTZXRJZGVudChvYmplY3QgPSBhbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkLHZhbHVlID0gIm9yaWcuaWRlbnQiKQpWbG5QbG90KG9iamVjdCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzX2NjRmlsdGVyZWQsZmVhdHVyZXMgPSBoYWxsbWFya19uYW1lKQpgYGAKCiMjIHstfQpgYGB7cn0KRGltUGxvdChvYmplY3QgPSBhbGxfYWNjX2NhbmNlcl9jZWxscyxncm91cC5ieSA9ICJvcmlnLmlkZW50IikKYGBgCgojIyBFbnJpY2htZW50IGFuYWx5c2lzIGZpbHRlcmVkIEhNU0MgdnMgQUNDCmBgYHtyIGZpZy53aWR0aD04LCBlY2hvPVRSVUUscmVzdWx0cz0naGlkZScsZmlnLmtlZXA9J2FsbCd9CnBhdGllbnQuaWRlbnQgPSBhbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkJHBhdGllbnQuaWRlbnQgJT4lIGFzLmRhdGEuZnJhbWUoKQpwYXRpZW50LmlkZW50WywxXSA9IGFzLmNoYXJhY3RlcihwYXRpZW50LmlkZW50WywxXSkKcGF0aWVudC5pZGVudFtwYXRpZW50LmlkZW50WywxXSA9PSAiQUNDMSIsXSA9ICJITVNDIgpwYXRpZW50LmlkZW50WywxXSA9IGFzLmZhY3RvcihwYXRpZW50LmlkZW50WywxXSkKYWxsX2FjY19jYW5jZXJfY2VsbHNfY2NGaWx0ZXJlZCA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzX2NjRmlsdGVyZWQsbWV0YWRhdGEgPSBwYXRpZW50LmlkZW50LGNvbC5uYW1lID0gInBhdGllbnQuaWRlbnQiKQphbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkID0gU2V0SWRlbnQoYWxsX2FjY19jYW5jZXJfY2VsbHNfY2NGaWx0ZXJlZCwgdmFsdWUgPSJwYXRpZW50LmlkZW50IikKYWNjX2RlZyA8LSBGaW5kTWFya2VycyhhbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkLCBpZGVudC4xID0gIkhNU0MiLGxvZ2ZjLnRocmVzaG9sZCA9IDEuNSxmZWF0dXJlcyA9IFZhcmlhYmxlRmVhdHVyZXMoYWxsX2FjY19jYW5jZXJfY2VsbHNfY2NGaWx0ZXJlZCkpCmVucmljaG1lbnRfYW5hbHlzaXMoYWNjX2RlZyxiYWNrZ3JvdW5kID0gVmFyaWFibGVGZWF0dXJlcyhhbGxfYWNjX2NhbmNlcl9jZWxsc19jY0ZpbHRlcmVkKSxmZHJfQ3V0b2ZmID0gMC4wMSxpZGVudC4xID0gIkhNU0MiLGlkZW50LjIgPSAiQUNDIixzaG93X2J5ID0gMSkKYGBgCgojIyBNWUIgZXhwcmVzc2lvbgoKYGBge3IgZmlnLndpZHRoPTEwfQphbGxfYWNjX2NhbmNlcl9jZWxscyA9IFNldElkZW50KG9iamVjdCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzLHZhbHVlID0gInBhdGllbnQuaWRlbnQiKSAjYWN0aXZlIHNubiBncmFwaApGZWF0dXJlUGxvdChvYmplY3QgPSBhbGxfYWNjX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9ICJNWUIiLGxhYmVsID0gVCkKYGBgCgpgYGB7ciBmaWcud2lkdGg9MTB9CmFsbF9hY2NfY2FuY2VyX2NlbGxzID0gU2V0SWRlbnQob2JqZWN0ID0gYWxsX2FjY19jYW5jZXJfY2VsbHMsdmFsdWUgPSAicGF0aWVudC5pZGVudCIpICNhY3RpdmUgc25uIGdyYXBoCkZlYXR1cmVQbG90KG9iamVjdCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzLGZlYXR1cmVzID0gIk1ZQiIsbGFiZWwgPSBUKQpgYGAKCiMjIENOViB7LnRhYnNldH0KCgoKYGBge3J9CiNzZXQgY2VsbCB0eXBlcwpuZXcuY2x1c3Rlci5pZHMgPC0gYygiY2FuY2VyIiwgIzAKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICMxCiAgICAgICAgICAgICAgICAgICAgICJDQUYiLCAjMgogICAgICAgICAgICAgICAgICAgICAiY2FuY2VyIiwgIzMKICAgICAgICAgICAgICAgICAgICAgIkVuZG90aGVsaWFsIiwgIzQKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICM1CiAgICAgICAgICAgICAgICAgICAgICJjYW5jZXIiLCAjNgogICAgICAgICAgICAgICAgICAgICAiQ0FGIiwgIzcKICAgICAgICAgICAgICAgICAgICAgIkNBRiIsICM4CiAgICAgICAgICAgICAgICAgICAgICJDQUYiLCAjOQogICAgICAgICAgICAgICAgICAgICAiY2FuY2VyIiwgIzEwCiAgICAgICAgICAgICAgICAgICAgICJDQUYiLCAjMTEKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICMxMgogICAgICAgICAgICAgICAgICAgICAiY2FuY2VyIiwgIzEzCiAgICAgICAgICAgICAgICAgICAgICJjYW5jZXIiLCAjMTQKICAgICAgICAgICAgICAgICAgICAgImNhbmNlciIsICMxNQogICAgICAgICAgICAgICAgICAgICAiY2FuY2VyIiwgIzE2CiAgICAgICAgICAgICAgICAgICAgICJXQkMiLCAjMTcKICAgICAgICAgICAgICAgICAgICAgIkNBRiIgIzE4CiAgICAgICAgICAgICAgICAgICAgICkKYGBgCgoKYGBge3IgZmlnLnNob3c9J2hpZGUnfQojcmVuYW1lIGlkZW50czoKYWNjX2FsbF9jZWxscyA9IFNldElkZW50KG9iamVjdCA9IGFjY19hbGxfY2VsbHMsdmFsdWUgPSAiUk5BX3Nubl9yZXMuMSIpICNhY3RpdmUgc25uIGdyYXBoCm5hbWVzKG5ldy5jbHVzdGVyLmlkcykgPC0gbGV2ZWxzKGFjY19hbGxfY2VsbHMpICNhZGQgc25uIGdyYXBoIGxldmVscyB0byBuZXcuY2x1c3Rlci5pZHMKYWNjX2FsbF9jZWxsc0BtZXRhLmRhdGFbWyJzZXVyYXRfY2x1c3RlcnMiXV0gPSBhY2NfYWxsX2NlbGxzQG1ldGEuZGF0YVtbIlJOQV9zbm5fcmVzLjEiXV0KYWNjX2FsbF9jZWxscyA9IFNldElkZW50KG9iamVjdCA9IGFjY19hbGxfY2VsbHMsdmFsdWUgPSAic2V1cmF0X2NsdXN0ZXJzIikKYWNjX2FsbF9jZWxscyA8LSBSZW5hbWVJZGVudHMoYWNjX2FsbF9jZWxscywgbmV3LmNsdXN0ZXIuaWRzKSAKCiMgZGl2aWRlICJjYW5jZXIiIGludG8gcGF0aWVudHM6CmNlbGxfdHlwZXMgPSBhY2NfYWxsX2NlbGxzQGFjdGl2ZS5pZGVudCAlPiUgYXMuZGF0YS5mcmFtZSgpCmNlbGxfdHlwZXNbLDFdPC0gYXMuY2hhcmFjdGVyKGNlbGxfdHlwZXNbLDFdKQpjZWxsX3R5cGVzID0gY2JpbmQoY2VsbF90eXBlcyxhY2NfYWxsX2NlbGxzJHBhdGllbnQuaWRlbnQpICU+JSBzZXRuYW1lcyhvbGQgPSBuYW1lcyguKSwgCiAgICAgICAgIG5ldyA9IGMoJ2NlbGxfdHlwZScsJ3BhdGllbnQnKSkKY2VsbF90eXBlc1tjZWxsX3R5cGVzJGNlbGxfdHlwZSA9PSAiY2FuY2VyIixdID0gY2VsbF90eXBlc1tjZWxsX3R5cGVzJGNlbGxfdHlwZSA9PSAiY2FuY2VyIiwyXQoKCiMgaG1zY19yb3dzID0gKHN0YXJ0c1dpdGgoeCA9IHJvd25hbWVzKGNlbGxfdHlwZXMpLHByZWZpeCA9ICJBQ0MucGxhdGUyIikgfCBzdGFydHNXaXRoKHggPSByb3duYW1lcyhjZWxsX3R5cGVzKSxwcmVmaXggPSAiQUNDMS4iKSkgJiBjZWxsX3R5cGVzWywxXSA9PSAiY2FuY2VyIiAKIyBhY2Nfcm93cyA9ICEoc3RhcnRzV2l0aCh4ID0gcm93bmFtZXMoY2VsbF90eXBlcykscHJlZml4ID0gIkFDQy5wbGF0ZTIiKSB8IHN0YXJ0c1dpdGgoeCA9IHJvd25hbWVzKGNlbGxfdHlwZXMpLHByZWZpeCA9ICJBQ0MxLiIpKSAmIGNlbGxfdHlwZXNbLDFdID09ICJjYW5jZXIiIAojIGNlbGxfdHlwZXNbLDFdW2htc2Nfcm93c10gID0gIkhNU0MiCiMgY2VsbF90eXBlc1ssMV1bYWNjX3Jvd3NdICA9ICJBQ0MiCgojYWRkIHRvIG1ldGFkYXRhOgpjZWxsX3R5cGVzWywyXSA9IE5VTEwgCmNlbGxfdHlwZXNbY2VsbF90eXBlcyRjZWxsX3R5cGUgPT0gIkFDQzEiLF0gPSAiSE1TQyIKYWNjX2FsbF9jZWxscyA9IEFkZE1ldGFEYXRhKG9iamVjdCA9YWNjX2FsbF9jZWxscyAsbWV0YWRhdGEgPSBjZWxsX3R5cGVzLGNvbC5uYW1lID0gImNlbGwudHlwZSIpCmBgYAojIyMgQ05WIFVNQVAgCgpgYGB7ciBmaWcud2lkdGg9MTB9CmxpYnJhcnkoaW5mZXJjbnYpCmxpYnJhcnkodGlkeXZlcnNlKQphY2NfYW5ub3RhdGlvbiAgPSBhcy5kYXRhLmZyYW1lKGFjY19hbGxfY2VsbHNAbWV0YS5kYXRhWywiY2VsbC50eXBlIixkcm9wID0gRl0pCmFjY19hbm5vdGF0aW9uID0gYWNjX2Fubm90YXRpb24gJT4lIHJvd25hbWVzX3RvX2NvbHVtbigib3JpZy5pZGVudCIpIAphY2NfYW5ub3RhdGlvbiA9IGFjY19hbm5vdGF0aW9uICU+JSBtdXRhdGUob3JpZy5pZGVudCA9IGdzdWIoeCA9IGFjY19hbm5vdGF0aW9uJG9yaWcuaWRlbnQscGF0dGVybiA9ICJcXC4iLCByZXBsYWNlbWVudCA9ICItIikgJT4lIAogIGdzdWIocGF0dGVybiA9ICJfIiwgcmVwbGFjZW1lbnQgPSAiLSIpKQogIAoKd3JpdGUudGFibGUoYWNjX2Fubm90YXRpb24sICIuL0RhdGEvaW5mZXJDTlYvYWNjX2Fubm90YXRpb24udHh0IiwgYXBwZW5kID0gRkFMU0UsIAogICAgICAgICAgICBzZXAgPSAiXHQiLCBkZWMgPSAiLiIscm93Lm5hbWVzID0gRkFMU0UsIGNvbC5uYW1lcyA9IEYpCgppbmZlcmNudl9vYmogPSBDcmVhdGVJbmZlcmNudk9iamVjdChyYXdfY291bnRzX21hdHJpeD0iLi9EYXRhL2luZmVyQ05WL2FsbC40aWNudi50eHQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbnNfZmlsZT0iLi9EYXRhL2luZmVyQ05WL2FjY19hbm5vdGF0aW9uLnR4dCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbGltPSJcdCIsZ2VuZV9vcmRlcl9maWxlPSIuL0RhdGEvaW5mZXJDTlYvZ2VuY29kZV92MTlfZ2VuZV9wb3MudHh0IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAscmVmX2dyb3VwX25hbWVzPWMoIkNBRiIsICJFbmRvdGhlbGlhbCIsICJXQkMiKSkgI2dyb3VwcyBvZiBub3JtYWwgY2VsbHMKCmluZmVyY252X29ial9kZWZhdWx0ID0gaW5mZXJjbnY6OnJ1bihpbmZlcmNudl9vYmosIGN1dG9mZj0xLCBvdXRfZGlyPScuL0RhdGEvaW5mZXJDTlYvaW5mZXJjbnZfb3V0cHV0JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfYnlfZ3JvdXBzPVQsIHBsb3Rfc3RlcHM9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZW5vaXNlPVRSVUUsIEhNTT1GQUxTRSwgbm9fcHJlbGltX3Bsb3Q9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBuZ19yZXM9MzAwKQpwbG90X2NudihpbmZlcmNudl9vYmpfZGVmYXVsdCwgb3V0cHV0X2Zvcm1hdCA9ICJwbmciLCAgd3JpdGVfZXhwcl9tYXRyaXggPSBGQUxTRSxvdXRfZGlyID0gIi4vRGF0YS9pbmZlckNOVi8iLHBuZ19yZXMJPTgwMCxvYnNfdGl0bGUgPSAiTWFsaWduYW50IGNlbGxzIixyZWZfdGl0bGUgPSAiTm9ybWFsIGNlbGxzIikKCgpjbHVzdGVyLmluZm89RmV0Y2hEYXRhKGFjY19hbGxfY2VsbHMsYygiaWRlbnQiLCJvcmlnLmlkZW50IiwiVU1BUF8xIiwiVU1BUF8yIiwibkNvdW50X1JOQSIsIm5GZWF0dXJlX1JOQSIsInBlcmNlbnQubXQiLCJwYXRpZW50LmlkZW50Iiwic2V1cmF0X2NsdXN0ZXJzIikpCmNsdXN0ZXIuaW5mbyRjZWxsPXJvd25hbWVzKGNsdXN0ZXIuaW5mbykKCmxpYnJhcnkobGltbWEpCnNtb290aGVkPWFwcGx5KGluZmVyY252X29ial9kZWZhdWx0QGV4cHIuZGF0YSwyLHRyaWN1YmVNb3ZpbmdBdmVyYWdlLCBzcGFuPTAuMDEpCmNuc2lnPXNxcnQoYXBwbHkoKHNtb290aGVkLTEpXjIsMixtZWFuKSkKdW1hcD1jbHVzdGVyLmluZm8KbmFtZXMoY25zaWcpPXVtYXAkY2VsbAoKYWNjX2FsbF9jZWxscyA8LSBBZGRNZXRhRGF0YShvYmplY3QgPSBhY2NfYWxsX2NlbGxzLCBtZXRhZGF0YSA9IGNuc2lnLCBjb2wubmFtZSA9ICJjb3B5bnVtYmVyIikKYWNjX2FsbF9jZWxscyA9IFNldElkZW50KG9iamVjdCA9IGFjY19hbGxfY2VsbHMsdmFsdWUgPSAiY2VsbC50eXBlIikKRmVhdHVyZVBsb3QoYWNjX2FsbF9jZWxscywgImNvcHludW1iZXIiLHB0LnNpemUgPSAxLCBjb2xzID0gYygibGlnaHRibHVlIiwib3JhbmdlIiwicmVkIiwiZGFya3JlZCIpLGxhYmVsID0gVCxyZXBlbCA9IFQpCmBgYAoKYGBge3J9CmFjY19jYW5jZXJfY2VsbHMgPC0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWNjX2NhbmNlcl9jZWxscywgbWV0YWRhdGEgPSBjbnNpZywgY29sLm5hbWUgPSAiY29weW51bWJlciIpCmFjY19jYW5jZXJfY2VsbHMgPSBTZXRJZGVudChvYmplY3QgPSBhY2NfY2FuY2VyX2NlbGxzLHZhbHVlID0gImNlbGwudHlwZSIpCkZlYXR1cmVQbG90KGFjY19jYW5jZXJfY2VsbHMsICJjb3B5bnVtYmVyIixwdC5zaXplID0gMSwgY29scyA9IGMoImxpZ2h0Ymx1ZSIsIm9yYW5nZSIsInJlZCIsImRhcmtyZWQiKSxsYWJlbCA9IFQscmVwZWwgPSBUKQpgYGAKCgojIyMgQ05WIHBsb3QgCgohW0NOViBwbG90XSgvc2NpL2xhYnMveW90YW1kL2xhYl9zaGFyZS9hdmlzaGFpLndpemVsL1JfcHJvamVjdHMvSE1TQy9EYXRhL2luZmVyQ05WL2luZmVyY252LnBuZykKIyMgey19CgojIyBDTlYgc3VidHlwZXMKCmBgYHtyfQpjbnZfc3VidHlwZXMgPSBhcy5kYXRhLmZyYW1lKGN1dHJlZShpbmZlcmNudl9vYmpfZGVmYXVsdEB0dW1vcl9zdWJjbHVzdGVyc1tbImhjIl1dW1siSE1TQyJdXSwgayA9IDIpKQpuYW1lcyhjbnZfc3VidHlwZXMpWzFdID0gImNudi5jbHVzdGVyIgpyb3duYW1lcyhjbnZfc3VidHlwZXMpID0gcm93bmFtZXMoY252X3N1YnR5cGVzKSAlPiUgZ3N1YihwYXR0ZXJuID0gIi0iLHJlcGxhY2VtZW50ID0gIlxcLiIpCmNudl9zdWJ0eXBlcyBbLDFdID0gYXMuY2hhcmFjdGVyKGNudl9zdWJ0eXBlc1ssMV0pCmluZmVyY252Lm9ic2VydmF0aW9ucyA9IGRhdGEuZnJhbWUoZnJlYWQoZmlsZSA9ICIuL0RhdGEvaW5mZXJDTlYvaW5mZXJjbnYub2JzZXJ2YXRpb25zLnR4dCIpLCByb3cubmFtZXM9MSkKbmFtZXNfdG9fa2VlcCA9IGNvbG5hbWVzKGluZmVyY252Lm9ic2VydmF0aW9ucykgJWluJSAoY29sbmFtZXMoYWNjMV9jYW5jZXJfY2VsbHMpICU+JSBnc3ViKHBhdHRlcm4gPSAiXyIscmVwbGFjZW1lbnQgPSAiXFwuIikpCmluZmVyY252Lm9ic2VydmF0aW9ucyA9IGluZmVyY252Lm9ic2VydmF0aW9uc1ssbmFtZXNfdG9fa2VlcF0KYGBgCgpgYGB7cn0Kcm90YXRlIDwtIGZ1bmN0aW9uKHgpIHQoYXBwbHkoeCwgMiwgcmV2KSkKaW5mZXJjbnYub2JzZXJ2YXRpb25zMiA9IGluZmVyY252Lm9ic2VydmF0aW9ucyAlPiUgcm90YXRlKCkgJT4lICByb3RhdGUoKSAlPiUgcm90YXRlKCklPiUgYXMuZGF0YS5mcmFtZSgpIApicmVha3MgPSBjKDAuNzAwODkxODYxNzA0ODU3LAowLjc0MjM2Njk0NTUyODM2OSwKMC43ODM4NDIwMjkzNTE4ODEsCjAuODI1MzE3MTEzMTc1MzkzLAowLjg2Njc5MjE5Njk5ODkwNSwKMC45MDgyNjcyODA4MjI0MTcsCjAuOTQ5NzQyMzY0NjQ1OTI4LAowLjk5MTIxNzQ0ODQ2OTQ0LAoxLjAzMjY5MjUzMjI5Mjk1LAoxLjA3NDE2NzYxNjExNjQ2LAoxLjExNTY0MjY5OTkzOTk4LAoxLjE1NzExNzc4Mzc2MzQ5LAoxLjE5ODU5Mjg2NzU4NywKMS4yNDAwNjc5NTE0MTA1MSwKMS4yODE1NDMwMzUyMzQwMiwKMS4zMjMwMTgxMTkwNTc1MykKcGhlYXRtYXAoaW5mZXJjbnYub2JzZXJ2YXRpb25zMixjbHVzdGVyX2NvbHMgPSBGLGNsdXN0ZXJfcm93cyA9IEYsIHNob3dfcm93bmFtZXMgPSBGLHNob3dfY29sbmFtZXMgPSBGLCBicmVha3MgPSBicmVha3MsY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKHJldihjKCJkYXJrcmVkIiwgIndoaXRlIiwgImRhcmtibHVlIikpKSgxNSksYW5ub3RhdGlvbl9yb3cgPSBjbnZfc3VidHlwZXMpCgpgYGAKCmBgYHtyfQpyb3duYW1lcyhjbnZfc3VidHlwZXMpID0gcm93bmFtZXMoY252X3N1YnR5cGVzKSAlPiUgZ3N1YihwYXR0ZXJuID0gIjJcXC4iLHJlcGxhY2VtZW50ID0gIjJfIikKcm93bmFtZXMoY252X3N1YnR5cGVzKSA9IHJvd25hbWVzKGNudl9zdWJ0eXBlcykgJT4lIGdzdWIocGF0dGVybiA9ICIzXFwuIixyZXBsYWNlbWVudCA9ICIzXyIpCmFjYzFfY2FuY2VyX2NlbGxzID0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbWV0YWRhdGEgPSBjbnZfc3VidHlwZXMpCmBgYAoKYGBge3J9CkRpbVBsb3QoYWNjMV9jYW5jZXJfY2VsbHMsZ3JvdXAuYnkgPSAiY252LmNsdXN0ZXIiLHB0LnNpemUgPSAyKQpgYGAKCgojIyBPcmlnaW5hbCBzY29yZQpgYGB7cn0Kb3JpZ2luYWxfbXlvX2dlbmVzID0gYyggIlRQNjMiLCAiVFA3MyIsICJDQVYxIiwgIkNESDMiLCAiS1JUNSIsICJLUlQxNCIsICJBQ1RBMiIsICJUQUdMTiIsICJNWUxLIiwgIkRLSzMiKQpvcmlnaW5hbF9sdW1fZ2VuZXMgPSBjKCJLSVQiLCAiRUhGIiwgIkVMRjUiLCAiS1JUNyIsICJDTEROMyIsICJDTERONCIsICJDRDI0IiwgIkxHQUxTMyIsICJMQ04yIiwgIlNMUEkiICkKYGBgCgoKCmBgYHtyfQpjYWxjdWxhdGVfc2NvcmUoZGF0YXNldCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzLG15b19nZW5lcyA9IG9yaWdpbmFsX215b19nZW5lcyxsdW1fZ2VuZXMgPSBvcmlnaW5hbF9sdW1fZ2VuZXMpCmBgYAojIyBPcmlnaW5hbCBzY29yZSBvZiBBQ0MxCmBgYHtyfQpjYWxjdWxhdGVfc2NvcmUoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLG15b19nZW5lcyA9IG9yaWdpbmFsX215b19nZW5lcyxsdW1fZ2VuZXMgPSBvcmlnaW5hbF9sdW1fZ2VuZXMsbHVtX3RocmVzaG9sZCA9IDAsbXlvX3RocmVzaG9sZCA9IDApCmBgYAoKCiMjIDAuMzUgTW9zdCBjb3JyZWxhdGVkIHNjb3JlIHsudGFic2V0fQoKIyMjIE15byBnZW5lcwoKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIGNvbGxhcHNlPVR9Cm15b19wcm90ZWluX21hcmtlcnMgPSBjKCJDTk4xIiwgIlRQNjMiLCJBQ1RBMiIpCnRvcF9teW8gID0gdG9wX2NvcnJlbGF0ZWQoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLCBnZW5lcyA9IG15b19wcm90ZWluX21hcmtlcnMsdGhyZXNob2xkID0gMC4zNSkKcHJpbnQoIk51bWJlciBvZiBnZW5lcyA9ICIgJT4lIHBhc3RlKGxlbmd0aCh0b3BfbXlvKSkpCm1lc3NhZ2UoIk5hbWVzIG9mIGdlbmVzOiIpCnRvcF9teW8gJT4lIGhlYWQoMzApCm1lc3NhZ2UoIkdlbmVzIHRoYXQgYWxzbyBhcGVhcmVkIGluIHRoZSBvcmlnaW5hbCBzY29yZToiKQpiYXNlOjppbnRlcnNlY3QodG9wX215byxvcmlnaW5hbF9teW9fZ2VuZXMpIApgYGAKYGBge3J9Cm15b19lbnJpY2hfcmVzID0gZ2VuZXNfdmVjX2VucmljaG1lbnQoZ2VuZXMgPSB0b3BfbXlvLGJhY2tncm91bmQgPSByb3duYW1lcyhhY2MxX2NhbmNlcl9jZWxscyksaG9tZXIgPSBULHRpdGxlID0gIm15byB0b3AgZW5yaWNobWVudCIsY3VzdG9tX3BhdGh3YXlzID0gbHVtaW5hbF9ncykKbXlvX2VucmljaF9yZXMKYGBgCiMjIyBMdW0gZ2VuZXMKYGBge3J9Cmx1bV9wcm90ZWluX21hcmtlcnMgPSBjKCJLSVQiKQp0b3BfbHVtICA9IHRvcF9jb3JyZWxhdGVkKGRhdGFzZXQgPSBhY2MxX2NhbmNlcl9jZWxscywgZ2VuZXMgPSBsdW1fcHJvdGVpbl9tYXJrZXJzLHRocmVzaG9sZCA9IDAuMzUsbl92YXJnZW5lcyA9IDUwMDApCnByaW50KCJOdW1iZXIgb2YgZ2VuZXMgPSAiICU+JSBwYXN0ZShsZW5ndGgodG9wX2x1bSkpKQptZXNzYWdlKCJOYW1lcyBvZiBnZW5lczoiKQp0b3BfbHVtICU+JSBoZWFkKDMwKQptZXNzYWdlKCJHZW5lcyB0aGF0IGFsc28gYXBlYXJlZCBpbiB0aGUgb3JpZ2luYWwgc2NvcmU6IikKYmFzZTo6aW50ZXJzZWN0KHRvcF9sdW0sb3JpZ2luYWxfbHVtX2dlbmVzKSAKYGBgCgpgYGB7cn0KbHVtX2VucmljaF9yZXMgPSBnZW5lc192ZWNfZW5yaWNobWVudChnZW5lcyA9IHRvcF9sdW0sYmFja2dyb3VuZCA9IHJvd25hbWVzKGFjYzFfY2FuY2VyX2NlbGxzKSxob21lciA9IFQsdGl0bGUgPSAibHVtIHRvcCBlbnJpY2htZW50IixjdXN0b21fcGF0aHdheXMgPSBsdW1pbmFsX2dzKQpsdW1fZW5yaWNoX3JlcwpgYGAKIyMjIHRvcCBjb3JyZWxhdGVkIHNjb3JlCmBgYHtyfQpjYWxjdWxhdGVfc2NvcmUoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLG15b19nZW5lcyA9IHRvcF9teW8sbHVtX2dlbmVzID0gdG9wX2x1bSxsdW1fdGhyZXNob2xkID0gMCxteW9fdGhyZXNob2xkID0gMCkKYGBgCgoKIyMjICBlbnJpY2hlZCBnZW5lcyBzY29yZQpgYGB7cn0Kcm93bmFtZXMobHVtX2VucmljaF9yZXMpID0gbHVtX2VucmljaF9yZXMkcGF0aHdheV9uYW1lCmx1bV9lbnJpY2hlZF9nZW5lcyA9IGx1bV9lbnJpY2hfcmVzWzEsImdlbmVJRCJdICU+JSBzdHJzcGxpdChzcGxpdCA9ICIvIikgJT4lIC5bWzFdXSAlPiUgYyguLGx1bV9wcm90ZWluX21hcmtlcnMpICNhZGQgb3JpZ2luYWwgbWFya2VycwpgYGAKCmBgYHtyfQpyb3duYW1lcyhteW9fZW5yaWNoX3JlcykgPSBteW9fZW5yaWNoX3JlcyRwYXRod2F5X25hbWUKbXlvX2VucmljaGVkX2dlbmVzID0gbXlvX2VucmljaF9yZXNbMSwiZ2VuZUlEIl0gJT4lIHN0cnNwbGl0KHNwbGl0ID0gIi8iKSAlPiUgLltbMV1dICU+JSBjKC4sbXlvX3Byb3RlaW5fbWFya2VycykgI2FkZCBvcmlnaW5hbCBtYXJrZXJzCmBgYAoKYGBge3J9CmNhbGN1bGF0ZV9zY29yZShkYXRhc2V0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbXlvX2dlbmVzID0gbXlvX2VucmljaGVkX2dlbmVzLGx1bV9nZW5lcyA9IGx1bV9lbnJpY2hlZF9nZW5lcyxsdW1fdGhyZXNob2xkID0gLTIuNSxteW9fdGhyZXNob2xkID0gLTIuNSkKYGBgCgoKIyMgey19CgoKIyMgIDAuMiBNb3N0IGNvcnJlbGF0ZWQgc2NvcmUgey50YWJzZXR9CgojIyMgIG15byBHZW5lcwoKYGBge3J9CgpgYGAKCmBgYHtyIHdhcm5pbmc9RkFMU0V9Cm5fdmFyZ2VuZXMgPSAyMDAwCm15b19wcm90ZWluX21hcmtlcnMgPSBjKCJDTk4xIiwgIlRQNjMiLCJBQ1RBMiIpCnRvcF9teW8gID0gdG9wX2NvcnJlbGF0ZWQoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLCBnZW5lcyA9IG15b19wcm90ZWluX21hcmtlcnMsdGhyZXNob2xkID0gMC4yLG5fdmFyZ2VuZXMgPSBuX3ZhcmdlbmVzKQpwcmludCgiTnVtYmVyIG9mIGdlbmVzID0gIiAlPiUgcGFzdGUobGVuZ3RoKHRvcF9teW8pKSkKbWVzc2FnZSgiTmFtZXMgb2YgZ2VuZXM6IikKdG9wX215byAlPiUgaGVhZCgzMCkKbWVzc2FnZSgiR2VuZXMgdGhhdCBhbHNvIGFwZWFyZWQgaW4gdGhlIG9yaWdpbmFsIHNjb3JlOiIpCmJhc2U6OmludGVyc2VjdCh0b3BfbXlvLG9yaWdpbmFsX215b19nZW5lcykgCm15b19lbnJpY2hfcmVzID0gZ2VuZXNfdmVjX2VucmljaG1lbnQoZ2VuZXMgPSB0b3BfbXlvLGJhY2tncm91bmQgPSBWYXJpYWJsZUZlYXR1cmVzKGFjYzFfY2FuY2VyX2NlbGxzKSAlPiUgaGVhZChuX3ZhcmdlbmVzKSxob21lciA9IFQsdGl0bGUgPSAibXlvIHRvcCBlbnJpY2htZW50IixjdXN0b21fcGF0aHdheXMgPSBsdW1pbmFsX2dzKQpteW9fZW5yaWNoX3JlcwpgYGAKCiMjIyAgTHVtIEdlbmVzCgpgYGB7cn0KYWNjMV9jYW5jZXJfY2VsbHMgPSBGaW5kVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxuZmVhdHVyZXMgPSAxNTAwMCkKYGBgCgpgYGB7cn0KbHVtX3Byb3RlaW5fbWFya2VycyA9IGMoIktJVCIpCm5fdmFyZ2VuZXMgPSAyMDAwCnRvcF9sdW0gID0gdG9wX2NvcnJlbGF0ZWQoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLCBnZW5lcyA9IGx1bV9wcm90ZWluX21hcmtlcnMsdGhyZXNob2xkID0gMC4zLG5fdmFyZ2VuZXMgPSBuX3ZhcmdlbmVzKQpwcmludCgiTnVtYmVyIG9mIGdlbmVzID0gIiAlPiUgcGFzdGUobGVuZ3RoKHRvcF9sdW0pKSkKbWVzc2FnZSgiTmFtZXMgb2YgZ2VuZXM6IikKdG9wX2x1bSAlPiUgaGVhZCgzMCkKbWVzc2FnZSgiR2VuZXMgdGhhdCBhbHNvIGFwZWFyZWQgaW4gdGhlIG9yaWdpbmFsIHNjb3JlOiIpCmJhc2U6OmludGVyc2VjdCh0b3BfbHVtLG9yaWdpbmFsX2x1bV9nZW5lcykgCgpsdW1fZW5yaWNoX3JlcyA9IGdlbmVzX3ZlY19lbnJpY2htZW50KGdlbmVzID0gdG9wX2x1bSxiYWNrZ3JvdW5kID0gVmFyaWFibGVGZWF0dXJlcyhhY2MxX2NhbmNlcl9jZWxscykgJT4lIGhlYWQobl92YXJnZW5lcyksaG9tZXIgPSBULHRpdGxlID0gImx1bSB0b3AgZW5yaWNobWVudCIsY3VzdG9tX3BhdGh3YXlzID0gbHVtaW5hbF9ncykKbHVtX2VucmljaF9yZXMKY2FsY3VsYXRlX3Njb3JlKGRhdGFzZXQgPSBhY2MxX2NhbmNlcl9jZWxscyxteW9fZ2VuZXMgPSB0b3BfbXlvLGx1bV9nZW5lcyA9IHRvcF9sdW0sbHVtX3RocmVzaG9sZCA9IDIsbXlvX3RocmVzaG9sZCA9IDEpCmBgYAoKCmBgYHtyfQpteW9faW50ZXJzZWN0ZWQgPSBpbnRlcnNlY3QodG9wX215byxvcmlnaW5hbF9teW9fZ2VuZXMpIApsdW1faW50ZXJzZWN0ZWQgPSBpbnRlcnNlY3QodG9wX2x1bSxvcmlnaW5hbF9sdW1fZ2VuZXMpIAptZXNzYWdlKCJnZW5lcyBpbiBteW8gc2NvcmU6IikKbXlvX2ludGVyc2VjdGVkCgptZXNzYWdlKCJnZW5lcyBpbiBsdW0gc2NvcmU6IikKbHVtX2ludGVyc2VjdGVkCmNhbGN1bGF0ZV9zY29yZShkYXRhc2V0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbXlvX2dlbmVzID0gbXlvX2ludGVyc2VjdGVkLGx1bV9nZW5lcyA9IGx1bV9pbnRlcnNlY3RlZCxsdW1fdGhyZXNob2xkID0gMixteW9fdGhyZXNob2xkID0gMSkKYGBgCgoKCiMjIyBlbnJpY2hlZCBnZW5lcwpgYGB7cn0Kcm93bmFtZXMobHVtX2VucmljaF9yZXMpID0gbHVtX2VucmljaF9yZXMkcGF0aHdheV9uYW1lCmx1bV9lbnJpY2hlZF9nZW5lcyA9IGx1bV9lbnJpY2hfcmVzWzMsImdlbmVJRCJdICU+JSBzdHJzcGxpdChzcGxpdCA9ICIvIikgJT4lIC5bWzFdXSAlPiUgYyguLGx1bV9wcm90ZWluX21hcmtlcnMpICNhZGQgb3JpZ2luYWwgbWFya2VycwpgYGAKCmBgYHtyfQpyb3duYW1lcyhteW9fZW5yaWNoX3JlcykgPSBteW9fZW5yaWNoX3JlcyRwYXRod2F5X25hbWUKbXlvX2VucmljaGVkX2dlbmVzID0gbXlvX2VucmljaF9yZXNbMywiZ2VuZUlEIl0gJT4lIHN0cnNwbGl0KHNwbGl0ID0gIi8iKSAlPiUgLltbMV1dICU+JSBjKC4sbXlvX3Byb3RlaW5fbWFya2VycykgI2FkZCBvcmlnaW5hbCBtYXJrZXJzCmBgYAoKYGBge3J9Cm1lc3NhZ2UoImdlbmVzIGluIG15byBzY29yZToiKQpteW9fZW5yaWNoZWRfZ2VuZXMKCm1lc3NhZ2UoImdlbmVzIGluIGx1bSBzY29yZToiKQpsdW1fZW5yaWNoZWRfZ2VuZXMKCmNhbGN1bGF0ZV9zY29yZShkYXRhc2V0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbXlvX2dlbmVzID0gbXlvX2VucmljaGVkX2dlbmVzLGx1bV9nZW5lcyA9IGx1bV9lbnJpY2hlZF9nZW5lcyxsdW1fdGhyZXNob2xkID0gMCxteW9fdGhyZXNob2xkID0gLTEpCmBgYAoKCiMjIyBlbnJpY2hlZCBnZW5lcyBhbmQgaW4gb3JpZ2luYWwgc2NvcmUKYGBge3J9Cm15b19lbnJpY2hlZF9nZW5lcyA9IG15b19lbnJpY2hlZF9nZW5lc1tteW9fZW5yaWNoZWRfZ2VuZXMgJWluJSBvcmlnaW5hbF9teW9fZ2VuZXNdCmx1bV9lbnJpY2hlZF9nZW5lcyA9IGx1bV9lbnJpY2hlZF9nZW5lc1tsdW1fZW5yaWNoZWRfZ2VuZXMgJWluJSBvcmlnaW5hbF9sdW1fZ2VuZXNdCgptZXNzYWdlKCJnZW5lcyBpbiBteW8gc2NvcmU6IikKbXlvX2VucmljaGVkX2dlbmVzCgptZXNzYWdlKCJnZW5lcyBpbiBsdW0gc2NvcmU6IikKbHVtX2VucmljaGVkX2dlbmVzCgpjYWxjdWxhdGVfc2NvcmUoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLG15b19nZW5lcyA9IG15b19lbnJpY2hlZF9nZW5lcyxsdW1fZ2VuZXMgPSBsdW1fZW5yaWNoZWRfZ2VuZXMsbHVtX3RocmVzaG9sZCA9IDIsbXlvX3RocmVzaG9sZCA9IDIpCmBgYAoKIyMgey19CgoKIyBIUFYKCk9ubHkgSE1TQyBjYW5jZXIgY2VsbHM6CgpgYGB7cn0KSFBWMzNfUDMgPSBmcmVhZCgiLi9EYXRhL0hQVjMzX1AzLnR4dCIsY29sLm5hbWVzID0gYygicGxhdGUiLCJyZWFkcyIpKSAlPiUgYXMuZGF0YS5mcmFtZSgpCkhQVjMzX1AzLmRmID0gSFBWMzNfUDMgJT4lIG11dGF0ZSgKICBwbGF0ZSA9IGdzdWIoeCA9SFBWMzNfUDMkcGxhdGUsIHJlcGxhY2VtZW50ID0gIiIscGF0dGVybiA9ICJfLiokIikgCiAgJT4lIGdzdWIocGF0dGVybiA9ICItUCIscmVwbGFjZW1lbnQgPSAiLlAiKSAKICAlPiUgZ3N1YihwYXR0ZXJuID0gIi0iLHJlcGxhY2VtZW50ID0gIl8iLCkKICApCkhQVjMzX1AzLmRmID0gSFBWMzNfUDMuZGYgJT4lIGRwbHlyOjpmaWx0ZXIoSFBWMzNfUDMuZGYkcGxhdGUgJWluJSBjb2xuYW1lcyhhY2MxX2NhbmNlcl9jZWxscykpCnJvd25hbWVzKEhQVjMzX1AzLmRmKSAgPC0gSFBWMzNfUDMuZGYkcGxhdGUKSFBWMzNfUDMuZGYkcGxhdGUgPSBOVUxMCgoKSFBWMzNfUDIgPSBmcmVhZCgiLi9EYXRhL0hQVjMzX1AyLnR4dCIsY29sLm5hbWVzID0gYygicGxhdGUiLCJyZWFkcyIpKSAlPiUgYXMuZGF0YS5mcmFtZSgpCkhQVjMzX1AyLmRmID0gSFBWMzNfUDIgJT4lIG11dGF0ZSgKICBwbGF0ZSA9IGdzdWIoeCA9SFBWMzNfUDIkcGxhdGUsIHJlcGxhY2VtZW50ID0gIiIscGF0dGVybiA9ICJfLiokIikgCiAgJT4lIGdzdWIocGF0dGVybiA9ICJwbGF0ZTItIixyZXBsYWNlbWVudCA9ICJwbGF0ZTJfIiwpCiAgJT4lIGdzdWIocGF0dGVybiA9ICItIixyZXBsYWNlbWVudCA9ICJcXC4iLCkKICApCkhQVjMzX1AyLmRmID0gSFBWMzNfUDIuZGYgJT4lIGRwbHlyOjpmaWx0ZXIoSFBWMzNfUDIuZGYkcGxhdGUgJWluJSBjb2xuYW1lcyhhY2MxX2NhbmNlcl9jZWxscykpCnJvd25hbWVzKEhQVjMzX1AyLmRmKSAgPC0gSFBWMzNfUDIuZGYkcGxhdGUKSFBWMzNfUDIuZGYkcGxhdGUgPSBOVUxMCgpIUFYzMyA9IHJiaW5kKEhQVjMzX1AzLmRmLEhQVjMzX1AyLmRmKQphY2MxX2NhbmNlcl9jZWxscyA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLG1ldGFkYXRhID0gSFBWMzMsY29sLm5hbWUgPSAiSFBWMzMucmVhZHMiKQpGZWF0dXJlUGxvdChhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9ICJIUFYzMy5yZWFkcyIsbWF4LmN1dG9mZiA9IDEwKQpgYGAKYGBge3J9CmxpYnJhcnkocGxvdGx5KQpkYXRhID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSAiSFBWMzMucmVhZHMiKQpkYXRhID0gZGF0YSRIUFYzMwpkYXRhID0gZGF0YVtkYXRhPDEwMF0KcGxvdF9seSh4ID0gZGF0YSwgdHlwZSA9ICJoaXN0b2dyYW0iLCBuYmluc3ggPSAxMCkKYGBgCgpgYGB7cn0KCmRhdGEgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9ICJIUFYzMy5yZWFkcyIpCnByaW50KAogIGRhdGEgJT4lIAogIGdncGxvdChhZXMoIHg9SFBWMzMucmVhZHMpKSArIAogIGdlb21faGlzdG9ncmFtKCkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPWMoMSwzLDEwLDEwMCkpCikKYGBgCmBgYHtyfQpocHYzM19wb3NpdGl2ZSA9IEhQVjMzICU+JSBkcGx5cjo6bXV0YXRlKGhwdjMzX3Bvc2l0aXZlID0gY2FzZV93aGVuKHJlYWRzID49IDEwIH4gInBvc2l0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkcyA8IDEwIH4gIm5lZ2F0aXZlIikKKQoKCgpocHYzM19wb3NpdGl2ZSRyZWFkcyA9IE5VTEwKYWNjMV9jYW5jZXJfY2VsbHMgPSBBZGRNZXRhRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxtZXRhZGF0YSA9IGhwdjMzX3Bvc2l0aXZlKQpgYGAKCmBgYHtyfQpEaW1QbG90KG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLGdyb3VwLmJ5ICA9IGMoImhwdjMzX3Bvc2l0aXZlIikscHQuc2l6ZSA9IDIpCmBgYAoKCiMgY05NRgpgYGB7cn0KbGlicmFyeShyZXRpY3VsYXRlKQpgYGAKCmBgYHtyfQojd3JpdGUgZXhwcmVzc2lvbgpuZmVhdHVyZXMgPSAxMDAwMApuZmVhdHVyZXNfbmFtZSA9IChuZmVhdHVyZXMvMTAwMCkgJT4lIGFzLmNoYXJhY3RlcigpCmFjYzFfY2FuY2VyX2NlbGxzID0gRmluZFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbmZlYXR1cmVzID0gbmZlYXR1cmVzKQp2YXJnZW5lcyA9IFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMpCmhtc2NfZXhwcmVzc2lvbiA9IHQoYXMubWF0cml4KEdldEFzc2F5RGF0YShhY2MxX2NhbmNlcl9jZWxscyxzbG90PSdkYXRhJykpKQpobXNjX2V4cHJlc3Npb24gPSAyKipobXNjX2V4cHJlc3Npb24gI2NvbnZlcnQgZnJvbSBsb2cyKHRwbSsxKSB0byB0cG0KaG1zY19leHByZXNzaW9uID0gaG1zY19leHByZXNzaW9uLTEKaG1zY19leHByZXNzaW9uID0gaG1zY19leHByZXNzaW9uWywhY29sU3VtcyhobXNjX2V4cHJlc3Npb249PTAsIG5hLnJtPVRSVUUpPT1ucm93KGhtc2NfZXhwcmVzc2lvbildICNkZWxldGUgcm93cyB0aGF0IGhhdmUgYWxsIDAKaG1zY19leHByZXNzaW9uID0gaG1zY19leHByZXNzaW9uWyx2YXJnZW5lc10Kd3JpdGUudGFibGUoeCA9IGhtc2NfZXhwcmVzc2lvbiAsZmlsZSA9IHBhc3RlMCgnLi9EYXRhL2NOTUYvaG1zY19leHByZXNzaW9uRGF0YV8nLG5mZWF0dXJlc19uYW1lLCdLdmFyZ2VuZXMudHh0Jyksc2VwID0gIlx0IikKYGBgCgoKCmBgYHtweXRob24gZXZhbD1GfQpmcm9tIGNubWYgaW1wb3J0IGNOTUYKaW1wb3J0IG51bXB5IGFzIG5wCm5mZWF0dXJlc19uYW1lID0gci5uZmVhdHVyZXNfbmFtZQpuYW1lID0gJ0hNU0NfY05NRl8nK25mZWF0dXJlc19uYW1lKydLdmFyZ2VuZXMnCm91dGRpciA9ICcuL0RhdGEvY05NRicKS19yYW5nZSA9IG5wLmFyYW5nZSgzLDEwKQpjbm1mX29iaiA9IGNOTUYob3V0cHV0X2Rpcj1vdXRkaXIsIG5hbWU9bmFtZSkKY291bnRzX2ZuPScuL0RhdGEvY05NRi9obXNjX2V4cHJlc3Npb25EYXRhXycrbmZlYXR1cmVzX25hbWUrJ0t2YXJnZW5lcy50eHQnCnRwbV9mbiA9IGNvdW50c19mbiAjIyBUaGlzIGlzIGEgd2VpcmQgY2FzZSB3aGVyZSBiZWNhdXNlIHRoaXMgZGF0YXNldCBpcyBub3QgMycgZW5kIHVtaSBzZXF1ZW5jaW5nLCB3ZSBvcHRlZCB0byB1c2UgdGhlIFRQTSBtYXRyaXggYXMgdGhlIGlucHV0IG1hdHJpeCByYXRoZXIgdGhhbiB0aGUgY291bnQgbWF0cml4Cgpjbm1mX29iai5wcmVwYXJlKGNvdW50c19mbj1jb3VudHNfZm4sIGNvbXBvbmVudHM9S19yYW5nZSwgc2VlZD0xNCx0cG1fZm49dHBtX2ZuKQpgYGAKCmBgYHtweXRob24gZXZhbD1GfQpjbm1mX29iai5mYWN0b3JpemUod29ya2VyX2k9MCwgdG90YWxfd29ya2Vycz0xKQpgYGAKCmBgYHtweXRob24gZXZhbD1GfQpjbm1mX29iai5jb21iaW5lKCkKY25tZl9vYmoua19zZWxlY3Rpb25fcGxvdCgpCmBgYAojIyBTYXZlIG9iamVjdApgYGB7cHl0aG9uIGV2YWw9Rn0KIyBpbXBvcnQgcGlja2xlCiMgZiA9IG9wZW4oJy4vRGF0YS9jTk1GL0hNU0NfY05NRl8nK25mZWF0dXJlc19uYW1lKydLdmFyZ2VuZXMvY25tZl9vYmoucGNrbCcsICd3YicpCiMgcGlja2xlLmR1bXAoY25tZl9vYmosIGYpCiMgZi5jbG9zZSgpCmBgYAoKCiMjIExvYWQgb2JqZWN0CmBgYHtweXRob259CmZyb20gY25tZiBpbXBvcnQgY05NRgppbXBvcnQgcGlja2xlCm5mZWF0dXJlcyA9ICIySyIKZiA9IG9wZW4oJy4vRGF0YS9jTk1GL0hNU0NfY05NRl8nICsgbmZlYXR1cmVzKyAndmFyZ2VuZXMvY25tZl9vYmoucGNrbCcsICdyYicpCmNubWZfb2JqID0gcGlja2xlLmxvYWQoZikKZi5jbG9zZSgpCmBgYAoKCmBgYHtweXRob259CnNlbGVjdGVkX2sgPSA0CmRlbnNpdHlfdGhyZXNob2xkID0gMC4xCmNubWZfb2JqLmNvbnNlbnN1cyhrPXNlbGVjdGVkX2ssIGRlbnNpdHlfdGhyZXNob2xkPWRlbnNpdHlfdGhyZXNob2xkLHNob3dfY2x1c3RlcmluZz1UcnVlKQp1c2FnZV9ub3JtLCBnZXBfc2NvcmVzLCBnZXBfdHBtLCB0b3BnZW5lcyA9IGNubWZfb2JqLmxvYWRfcmVzdWx0cyhLPXNlbGVjdGVkX2ssIGRlbnNpdHlfdGhyZXNob2xkPWRlbnNpdHlfdGhyZXNob2xkKQpgYGAKCmBgYHtyfQpnZXBfc2NvcmVzID0gcHkkZ2VwX3Njb3JlcwpnZXBfdHBtID0gcHkkZ2VwX3RwbQphbGxfbWV0YWdlbmVzPSBweSR1c2FnZV9ub3JtCmBgYAoKIyMgRW5yaWNobWVudCBhbmFseXNpcyBieSB0b3AgMjAwIGdlbmVzIG9mIGVhY2ggcHJvZ3JhbQpgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04LCByZXN1bHRzPSdoaWRlJ30KcGx0X2xpc3QgPSBsaXN0KCkKZm9yIChpIGluIDE6bmNvbChnZXBfc2NvcmVzKSkgewogIHRvcF9nZW5lcyA9IGdlcF9zY29yZXMgICU+JSAgYXJyYW5nZShkZXNjKGdlcF9zY29yZXNbaV0pKSAjc29ydCBieSBzY29yZSBhCiAgdG9wID0gaGVhZChyb3duYW1lcyh0b3BfZ2VuZXMpLDIwMCkgI3Rha2UgdG9wIHRvcF9nZW5lc19udW0KICByZXMgPSBnZW5lc192ZWNfZW5yaWNobWVudChnZW5lcyA9IHRvcCxiYWNrZ3JvdW5kID0gcm93bmFtZXMoZ2VwX3Njb3JlcyksaG9tZXIgPSBULHRpdGxlID0gCiAgICAgICAgICAgICAgICAgICAgaSxzaWxlbnQgPSBULHJldHVybl9hbGwgPSBUKQogICAKICBwbHRfbGlzdFtbaV1dID0gcmVzJHBsdAp9CmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKGdyb2JzID0gcGx0X2xpc3QpCmBgYApgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04LCByZXN1bHRzPSdoaWRlJ30KCmNhbm9uaWNhbF9wYXRod2F5cyA9IG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiLCBjYXRlZ29yeSA9ICJDMiIpICU+JSBkcGx5cjo6ZmlsdGVyKGdzX3N1YmNhdCAhPSAiQ0dQIikgJT4lICBkcGx5cjo6ZGlzdGluY3QoZ3NfbmFtZSwgZ2VuZV9zeW1ib2wpIAoKcGx0X2xpc3QgPSBsaXN0KCkKZm9yIChpIGluIDE6bmNvbChnZXBfc2NvcmVzKSkgewogIHRvcF9nZW5lcyA9IGdlcF9zY29yZXMgICU+JSAgYXJyYW5nZShkZXNjKGdlcF9zY29yZXNbaV0pKSAjc29ydCBieSBzY29yZSBhCiAgdG9wID0gaGVhZChyb3duYW1lcyh0b3BfZ2VuZXMpLDIwMCkgI3Rha2UgdG9wIHRvcF9nZW5lc19udW0KICByZXMgPSBnZW5lc192ZWNfZW5yaWNobWVudChnZW5lcyA9IHRvcCxiYWNrZ3JvdW5kID0gcm93bmFtZXMoZ2VwX3Njb3JlcyksaG9tZXIgPSBULHRpdGxlID0gCiAgICAgICAgICAgICAgICAgICAgaSxzaWxlbnQgPSBULHJldHVybl9hbGwgPSBULGN1c3RvbV9wYXRod2F5cyA9IGNhbm9uaWNhbF9wYXRod2F5cykKICAgCiAgcGx0X2xpc3RbW2ldXSA9IHJlcyRwbHQKfQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IHBsdF9saXN0KQpgYGAKCgpgYGB7cn0KIyBsdW0gZ2VuZXMgaW4gbWV0YWdlbmVzCm1lc3NhZ2UoImx1bSBnZW5lcyBpbiBtZXRhZ2VuZXMiKQpmb3IgKGkgaW4gMTpuY29sKGdlcF9zY29yZXMpKSB7CiAgdG9wX2dlbmVzID0gZ2VwX3Njb3JlcyAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3Jlc1tpXSkpICNzb3J0IGJ5IHNjb3JlIGEKICB0b3AgPSBoZWFkKHJvd25hbWVzKHRvcF9nZW5lcyksMjAwKSAjdGFrZSB0b3AgdG9wX2dlbmVzX251bQogIGNhdChwYXN0ZTAoIm1ldGFnZW5lICIsaSwiOiAiKSkKICBwcmludChvcmlnaW5hbF9sdW1fZ2VuZXNbb3JpZ2luYWxfbHVtX2dlbmVzICVpbiUgdG9wXSkKCn0KY2F0KCJcbiIpCgoKIyBteW8gZ2VuZXMgaW4gbWV0YWdlbmVzCm1lc3NhZ2UoIm15byBnZW5lcyBpbiBtZXRhZ2VuZXMiKQpmb3IgKGkgaW4gMTpuY29sKGdlcF9zY29yZXMpKSB7CiAgdG9wX2dlbmVzID0gZ2VwX3Njb3JlcyAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3Jlc1tpXSkpICNzb3J0IGJ5IHNjb3JlIGEKICB0b3AgPSBoZWFkKHJvd25hbWVzKHRvcF9nZW5lcyksMjAwKSAjdGFrZSB0b3AgdG9wX2dlbmVzX251bQogIGNhdChwYXN0ZTAoIm1ldGFnZW5lICIsaSwiOiAiKSkKICBwcmludChvcmlnaW5hbF9teW9fZ2VuZXNbb3JpZ2luYWxfbXlvX2dlbmVzICVpbiUgdG9wXSkKCn0KY2F0KCJcbiIpCgpub3RjaF9nZW5lcyA9IGMoIkpBRzEiLCJKQUcyIiwiTk9UQ0gzIiwiTk9UQ0gyIiwiTk9UQ0gxIiwiRExMMSIsIk1ZQiIsIkhFUzQiLCJIRVkxIiwiSEVZMiIsIk5SQVJQIikKIyBub3RjaCBnZW5lcyBpbiBtZXRhZ2VuZXMKbWVzc2FnZSgibXlvIGdlbmVzIGluIG1ldGFnZW5lcyIpCmZvciAoaSBpbiAxOm5jb2woZ2VwX3Njb3JlcykpIHsKICB0b3BfZ2VuZXMgPSBnZXBfc2NvcmVzICAlPiUgIGFycmFuZ2UoZGVzYyhnZXBfc2NvcmVzW2ldKSkgI3NvcnQgYnkgc2NvcmUgYQogIHRvcCA9IGhlYWQocm93bmFtZXModG9wX2dlbmVzKSwyMDApICN0YWtlIHRvcCB0b3BfZ2VuZXNfbnVtCiAgY2F0KHBhc3RlMCgibWV0YWdlbmUgIixpLCI6ICIpKQogIHByaW50KG5vdGNoX2dlbmVzW25vdGNoX2dlbmVzICVpbiUgdG9wXSkKCn0KY2F0KCJcbiIpCmBgYAoKCgoKYGBge3J9CnBsdF9saXN0ID0gbGlzdCgpCmZvciAoaSBpbiAxOm5jb2woZ2VwX3Njb3JlcykpIHsKICB0b3BfZ2VuZXMgPSBnZXBfc2NvcmVzICAlPiUgIGFycmFuZ2UoZGVzYyhnZXBfc2NvcmVzW2ldKSkgI3NvcnQgYnkgc2NvcmUgYQogIHRvcCA9IGhlYWQocm93bmFtZXModG9wX2dlbmVzKSwxMCkgI3Rha2UgdG9wIHRvcF9nZW5lc19udW0KICBtZXNzYWdlKHBhc3RlKCJwcm9ncmFtICIsaSwidG9wIGdlbmVzOiIpKQogIHByaW50KHRvcCkKCn0KYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CiMgTWFrZSBtZXRhZ2VuZSBuYW1lcwpmb3IgKGkgaW4gMTpuY29sKGFsbF9tZXRhZ2VuZXMpKSB7CiAgY29sbmFtZXMoYWxsX21ldGFnZW5lcylbaV0gPSAibWV0YWdlbmUuIiAlPiUgcGFzdGUwKGkpCn0KCiNhZGQgZWFjaCBtZXRhZ2VuZSB0byBtZXRhZGF0YQpmb3IgKGkgaW4gMTpuY29sKGFsbF9tZXRhZ2VuZXMpKSB7CiAgbWV0YWdlX21ldGFkYXRhID0gYWxsX21ldGFnZW5lcyAlPiUgc2VsZWN0KGkpCiAgYWNjMV9jYW5jZXJfY2VsbHMgPSBBZGRNZXRhRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxtZXRhZGF0YSA9IG1ldGFnZV9tZXRhZGF0YSkKfQoKRmVhdHVyZVBsb3Qob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsZmVhdHVyZXMgPSBjb2xuYW1lcyhhbGxfbWV0YWdlbmVzKSxtYXguY3V0b2ZmID0gMSkKCmBgYApgYGB7ciBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KIApgYGAKCmBgYHtyfQptZXRhMyA9IEZldGNoRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyx2YXJzID0gYygibWV0YWdlbmUuMyIpKQpnZ3Bsb3QobWV0YTMsIGFlcyh4PW1ldGFnZW5lLjMpKSArIAogIGdlb21fZGVuc2l0eSgpCm1ldGEzWywxXSA9IGFzLm51bWVyaWMobWV0YTNbLDFdKQpzdW0obWV0YTNbLDFdPjAsbmEucm0gPSBUICkKc3VtKG1ldGEzWywxXT09MCxuYS5ybSA9IFQgKQoKYGBgCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KCm5vX25lZyA8LSBmdW5jdGlvbih4KSB7CiAgeCA9IHggKyBhYnMobWluKHgpKQogIHgKfQoKc3VtXzJfb25lIDwtIGZ1bmN0aW9uKHgpIHsKICB4ID14L3N1bSh4KQogIHgKfQphbGxfbWV0YWdlbmVzX25vcm09IHNjYWxlKGFsbF9tZXRhZ2VuZXMpICU+JSBhcy5kYXRhLmZyYW1lKCkKIyBNYWtlIG1ldGFnZW5lIG5hbWVzCmZvciAoaSBpbiAxOm5jb2woYWxsX21ldGFnZW5lc19ub3JtKSkgewogIGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXNfbm9ybSlbaV0gPSAibWV0YWdlbmUuIiAlPiUgcGFzdGUwKGkpCn0KCiNhZGQgZWFjaCBtZXRhZ2VuZSB0byBtZXRhZGF0YQpmb3IgKGkgaW4gMTpuY29sKGFsbF9tZXRhZ2VuZXNfbm9ybSkpIHsKICBtZXRhZ2VfbWV0YWRhdGEgPSBhbGxfbWV0YWdlbmVzX25vcm0gJT4lIHNlbGVjdChpKQogIGFjYzFfY2FuY2VyX2NlbGxzID0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbWV0YWRhdGEgPSBtZXRhZ2VfbWV0YWRhdGEpCn0KCkZlYXR1cmVQbG90KG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLGZlYXR1cmVzID0gY29sbmFtZXMoYWxsX21ldGFnZW5lc19ub3JtKSxtYXguY3V0b2ZmID0gMC41KQoKYGBgCgojIyBhc3NpZ25tZW50IFVNQVAKYGBge3J9Cmxhcmdlcl9ieSA9IDIKbWVzc2FnZShwYXN0ZSgibGFyZ2VyX2J5ID0gIiwgbGFyZ2VyX2J5KSkKYWNjMV9jYW5jZXJfY2VsbHMgPSBwcm9ncmFtX2Fzc2lnbm1lbnQoZGF0YXNldCA9IGFjYzFfY2FuY2VyX2NlbGxzLGxhcmdlcl9ieSA9IGxhcmdlcl9ieSxwcm9ncmFtX25hbWVzID0gY29sbmFtZXMoYWxsX21ldGFnZW5lcykpCnNlbGVjdGVkX2sgPTQKY29sb3JzID0gIHJhaW5ib3coc2VsZWN0ZWRfaykKY29sb3JzID0gYyhjb2xvcnMsImdyZXkiKQpEaW1QbG90KGFjYzFfY2FuY2VyX2NlbGxzLGdyb3VwLmJ5ID0gInByb2dyYW0uYXNzaWdubWVudCIscHQuc2l6ZSA9IDIsY29scyA9Y29sb3JzKQpgYGAgCgpzaG93IGNlbGwgY3ljbGUgcHJvZ3JhbToKYGBge3Igd2FybmluZz1GQUxTRX0KaGFsbG1hcmtfbmFtZSA9ICJHT19NSVRPVElDX0NFTExfQ1lDTEUiCmdlbmVzZXRzICA9R1NFQUJhc2U6OmdldEdtdCgiLi9EYXRhL2guYWxsLnY3LjAuc3ltYm9scy5wbHVzY2MuZ210IikKdmFyX2ZlYXR1cmVzPWFjYzFfY2FuY2VyX2NlbGxzQGFzc2F5cyRSTkFAdmFyLmZlYXR1cmVzCmdlbmVJZHM9IGdlbmVzZXRzW1toYWxsbWFya19uYW1lXV1AZ2VuZUlkcwpzY29yZSA8LSBhcHBseShhY2MxX2NhbmNlcl9jZWxsc0Bhc3NheXMkUk5BQGRhdGFbaW50ZXJzZWN0KGdlbmVJZHMsdmFyX2ZlYXR1cmVzKSxdLDIsbWVhbikKYWNjMV9jYW5jZXJfY2VsbHM9QWRkTWV0YURhdGEoYWNjMV9jYW5jZXJfY2VsbHMsc2NvcmUsaGFsbG1hcmtfbmFtZSkKYGBgCgpgYGB7cn0KRmVhdHVyZVBsb3Qob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsZmVhdHVyZXMgPSBoYWxsbWFya19uYW1lKQoKYGBgCmBgYHtyfQpjY192c19wcm9ncmFtMiA9IEZldGNoRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyx2YXJzID0gYygibWV0YWdlbmUuMiIsaGFsbG1hcmtfbmFtZSkpCmNvcihjY192c19wcm9ncmFtMlsxXSxjY192c19wcm9ncmFtMlsyXSkKYGBgCgojIyBDb21wYXJpc2lvbnMKCgpgYGB7cn0KY252X3ZzX2hwdiA9IEZldGNoRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyx2YXJzID0gYygiY252LmNsdXN0ZXIiLCJocHYzM19wb3NpdGl2ZSIpKQp0ZXN0IDwtIGZpc2hlci50ZXN0KHRhYmxlKGNudl92c19ocHYpKQpnZ2JhcnN0YXRzKAogIGNudl92c19ocHYsIGNudi5jbHVzdGVyLCBocHYzM19wb3NpdGl2ZSwKICByZXN1bHRzLnN1YnRpdGxlID0gRkFMU0UsCiAgc3VidGl0bGUgPSBwYXN0ZTAoCiAgICAiRmlzaGVyJ3MgZXhhY3QgdGVzdCIsICIsIHAtdmFsdWUgPSAiLAogICAgaWZlbHNlKHRlc3QkcC52YWx1ZSA8IDAuMDAxLCAiPCAwLjAwMSIsIHJvdW5kKHRlc3QkcC52YWx1ZSwgMykpCiAgKQopCgpgYGAKCmBgYHtyfQpjbnZfdnNfY25tZiA9IEZldGNoRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyx2YXJzID0gYygicHJvZ3JhbS5hc3NpZ25tZW50IiwiY252LmNsdXN0ZXIiKSkKY252X3ZzX2NubWYgPSBjbnZfdnNfY25tZiAlPiUgZHBseXI6OmZpbHRlcihwcm9ncmFtLmFzc2lnbm1lbnQgPT0gIjEiIHxwcm9ncmFtLmFzc2lnbm1lbnQgPT0gIjIiICkKdGVzdCA8LSBmaXNoZXIudGVzdCh0YWJsZShjbnZfdnNfY25tZikpCmdnYmFyc3RhdHMoCiAgY252X3ZzX2NubWYsIHByb2dyYW0uYXNzaWdubWVudCwgY252LmNsdXN0ZXIsCiAgcmVzdWx0cy5zdWJ0aXRsZSA9IEZBTFNFLAogIHN1YnRpdGxlID0gcGFzdGUwKAogICAgIkZpc2hlcidzIGV4YWN0IHRlc3QiLCAiLCBwLXZhbHVlID0gIiwKICAgIGlmZWxzZSh0ZXN0JHAudmFsdWUgPCAwLjAwMSwgIjwgMC4wMDEiLCByb3VuZCh0ZXN0JHAudmFsdWUsIDMpKQogICkKKQoKYGBgCgpgYGB7cn0KY25tZl92c19ocHYgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9IGMoInByb2dyYW0uYXNzaWdubWVudCIsImhwdjMzX3Bvc2l0aXZlIikpCmNubWZfdnNfaHB2ID0gY25tZl92c19ocHYgJT4lIGRwbHlyOjpmaWx0ZXIocHJvZ3JhbS5hc3NpZ25tZW50ID09ICIxIiB8cHJvZ3JhbS5hc3NpZ25tZW50ID09ICIyIiApCnRlc3QgPC0gZmlzaGVyLnRlc3QodGFibGUoY25tZl92c19ocHYpKQpnZ2JhcnN0YXRzKAogIGNubWZfdnNfaHB2LCBwcm9ncmFtLmFzc2lnbm1lbnQsIGhwdjMzX3Bvc2l0aXZlLAogIHJlc3VsdHMuc3VidGl0bGUgPSBGQUxTRSwKICBzdWJ0aXRsZSA9IHBhc3RlMCgKICAgICJGaXNoZXIncyBleGFjdCB0ZXN0IiwgIiwgcC12YWx1ZSA9ICIsCiAgICBpZmVsc2UodGVzdCRwLnZhbHVlIDwgMC4wMDEsICI8IDAuMDAxIiwgcm91bmQodGVzdCRwLnZhbHVlLCAzKSkKICApCikKCmBgYAoKYGBge3J9Cm15Yl92c19jbnYgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9IGMoImNudi5jbHVzdGVyIiwiTVlCIikpCm15Yl92c19jbnYgJGNudi5jbHVzdGVyID0gYXMuY2hhcmFjdGVyKG15Yl92c19jbnYgJGNudi5jbHVzdGVyICkKCmdnYm94cGxvdChteWJfdnNfY252LCB4ID0gImNudi5jbHVzdGVyIiwgeSA9ICJNWUIiLAogICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLAogICAgICAgICAgYWRkID0gImppdHRlciIpKyBzdGF0X2NvbXBhcmVfbWVhbnMobWV0aG9kID0gIndpbGNveC50ZXN0Iixjb21wYXJpc29ucyA9IGxpc3QoYygiMSIsIjIiKSkpCmBgYAoKCgpgYGB7cn0KbXliX3ZzX2hwdiA9IEZldGNoRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyx2YXJzID0gYygiaHB2MzNfcG9zaXRpdmUiLCJNWUIiKSkKbXliX3ZzX2hwdiAkaHB2MzNfcG9zaXRpdmUgPSBhcy5jaGFyYWN0ZXIobXliX3ZzX2hwdiAkaHB2MzNfcG9zaXRpdmUgKQoKZ2dib3hwbG90KG15Yl92c19ocHYsIHggPSAiaHB2MzNfcG9zaXRpdmUiLCB5ID0gIk1ZQiIsCiAgICAgICAgICBwYWxldHRlID0gImpjbyIsCiAgICAgICAgICBhZGQgPSAiaml0dGVyIikrIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAid2lsY294LnRlc3QiLGNvbXBhcmlzb25zID0gbGlzdChjKCJwb3NpdGl2ZSIsIm5lZ2F0aXZlIikpKSsgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gZnVuY3Rpb24oeCkgZGF0YS5mcmFtZSh5PTE1LCBsYWJlbCA9IHBhc3RlKCJNZWFuPSIscm91bmQobWVhbih4KSxkaWdpdHMgPSAyKSkpLCBnZW9tPSJ0ZXh0IikgK3lsYWIoImxvZzIoTVlCKSIpCmBgYAoKYGBge3J9CmhwdlJlYWRzX3ZzX215YiA9IEZldGNoRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyx2YXJzID0gYygiSFBWMzMucmVhZHMiLCJNWUIiKSkKY29yciA9IGNvcihocHZSZWFkc192c19teWIkSFBWMzMucmVhZHMsaHB2UmVhZHNfdnNfbXliJE1ZQikKcHJpbnQoImNvcnJlbGF0aW9uIG9mIE1ZQiBhYmQgaHB2MzNfcmVhZHM6IiAlPiUgcGFzdGUoY29yciAlPiUgcm91bmQoZGlnaXRzID0gMikpKQpgYGAKCgpgYGB7cn0KYWNjMV9jYW5jZXJfY2VsbHNfZGF0YSA9IGFjYzFfY2FuY2VyX2NlbGxzQGFzc2F5c1tbIlJOQSJdXUBkYXRhICU+JSBhcy5kYXRhLmZyYW1lKCkKYWNjMV9jYW5jZXJfY2VsbHNfZGF0YSA9IGNvcihhY2MxX2NhbmNlcl9jZWxsc19kYXRhKQphbm5vdGF0aW9uID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSBjKCJvcmlnLmlkZW50IikpCgpjb2xvcnMgPC0gYyhzZXEoLTEsMSxieT0wLjAxKSkKICBteV9wYWxldHRlIDwtIGMoImJsdWUiLGNvbG9yUmFtcFBhbGV0dGUoY29sb3JzID0gYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKG4gPSBsZW5ndGgoY29sb3JzKS0zKSwgInJlZCIpCiAgCnBodDEgPSBwaGVhdG1hcChhY2MxX2Nvcixhbm5vdGF0aW9uX2NvbCAgPSBhbm5vdGF0aW9uLGZvbnRzaXplID0gNixicmVha3MgPSBjb2xvcnMsIGNvbG9yID0gbXlfcGFsZXR0ZSxzaG93X2NvbG5hbWVzID0gRixzaG93X3Jvd25hbWVzID0gRikKYGBgCgpgYGB7cn0KYWNjMV9jYW5jZXJfY2VsbHNfZGF0YSA9IGFjYzFfY2FuY2VyX2NlbGxzQGFzc2F5c1tbIlJOQSJdXUBkYXRhICU+JSBhcy5kYXRhLmZyYW1lKCkKYWNjMV9jYW5jZXJfY2VsbHNfZGF0YSA9IGNvcihhY2MxX2NhbmNlcl9jZWxsc19kYXRhKQphbm5vdGF0aW9uID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSBjKCJjbnYuY2x1c3RlciIpKQoKY29sb3JzIDwtIGMoc2VxKC0xLDEsYnk9MC4wMSkpCiAgbXlfcGFsZXR0ZSA8LSBjKCJibHVlIixjb2xvclJhbXBQYWxldHRlKGNvbG9ycyA9IGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChuID0gbGVuZ3RoKGNvbG9ycyktMyksICJyZWQiKQogIApwaHQxID0gcGhlYXRtYXAoYWNjMV9jb3IsYW5ub3RhdGlvbl9jb2wgID0gYW5ub3RhdGlvbixmb250c2l6ZSA9IDYsYnJlYWtzID0gY29sb3JzLCBjb2xvciA9IG15X3BhbGV0dGUsc2hvd19jb2xuYW1lcyA9IEYsc2hvd19yb3duYW1lcyA9IEYpCmBgYAoKCmBgYHtyfQpjbnZfdnNfcGxhdGUgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9IGMoImNudi5jbHVzdGVyIiwib3JpZy5pZGVudCIpKQp0ZXN0IDwtIGZpc2hlci50ZXN0KHRhYmxlKGNudl92c19wbGF0ZSkpCmdnYmFyc3RhdHMoCiAgY252X3ZzX3BsYXRlLCBjbnYuY2x1c3Rlciwgb3JpZy5pZGVudCwKICByZXN1bHRzLnN1YnRpdGxlID0gRkFMU0UsCiAgc3VidGl0bGUgPSBwYXN0ZTAoCiAgICAiRmlzaGVyJ3MgZXhhY3QgdGVzdCIsICIsIHAtdmFsdWUgPSAiLAogICAgaWZlbHNlKHRlc3QkcC52YWx1ZSA8IDAuMDAxLCAiPCAwLjAwMSIsIHJvdW5kKHRlc3QkcC52YWx1ZSwgMykpCiAgKQopCgpgYGAKCgoKYGBge3J9CiMgY3JlYXQgY29sb3VycyBmb3IgZWFjaCBncm91cApjbnZfdnNfcGxhdGUgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9IGMoImNudi5jbHVzdGVyIiwib3JpZy5pZGVudCIpKQoKcm93bmFtZXMoY252X3ZzX3BsYXRlKSA9IHJvd25hbWVzKGNudl92c19wbGF0ZSkgJT4lIGdzdWIocGF0dGVybiA9ICJfIixyZXBsYWNlbWVudCA9ICJcXC4iKQpjbnZfdnNfcGxhdGUkY252LmNsdXN0ZXIgPSBhcy5jaGFyYWN0ZXIoY252X3ZzX3BsYXRlJGNudi5jbHVzdGVyKQpjbnZfdnNfcGxhdGUgPSBjbnZfdnNfcGxhdGUgJT4lIGRwbHlyOjpyZW5hbWUocGxhdGUgPSAib3JpZy5pZGVudCIpCmFubm9Db2wgPC0gbGlzdChwbGF0ZSA9IGMoQUNDMS5QMyA9ICJyZWQiLEFDQy5wbGF0ZTIgPSAiZ3JlZW4iKSxjbnYuY2x1c3RlciA9YygiMSI9ImdyZWVuIiwiMiIgPSAicmVkIikpCgpwaGVhdG1hcChpbmZlcmNudi5vYnNlcnZhdGlvbnMyLGNsdXN0ZXJfY29scyA9IEYsY2x1c3Rlcl9yb3dzID0gRiwgc2hvd19yb3duYW1lcyA9IEYsc2hvd19jb2xuYW1lcyA9IEYsIGJyZWFrcyA9IGJyZWFrcyxjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUocmV2KGMoImRhcmtyZWQiLCAid2hpdGUiLCAiZGFya2JsdWUiKSkpKDE1KSxhbm5vdGF0aW9uX3JvdyA9IGNudl92c19wbGF0ZSxhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm9Db2wpCgpgYGAKCmBgYHtyfQpjbm1mX3ZzX3BsYXRlID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSBjKCJwcm9ncmFtLmFzc2lnbm1lbnQiLCJvcmlnLmlkZW50IikpCmNubWZfdnNfcGxhdGU9IGNubWZfdnNfcGxhdGUgJT4lIGRwbHlyOjpmaWx0ZXIocHJvZ3JhbS5hc3NpZ25tZW50ID09ICIxIiB8IHByb2dyYW0uYXNzaWdubWVudCA9PSAiMiIpCnRlc3QgPC0gZmlzaGVyLnRlc3QodGFibGUoY25tZl92c19wbGF0ZSkpCmdnYmFyc3RhdHMoCiAgY25tZl92c19wbGF0ZSwgcHJvZ3JhbS5hc3NpZ25tZW50LCBvcmlnLmlkZW50LAogIHJlc3VsdHMuc3VidGl0bGUgPSBGQUxTRSwKICBzdWJ0aXRsZSA9IHBhc3RlMCgKICAgICJGaXNoZXIncyBleGFjdCB0ZXN0IiwgIiwgcC12YWx1ZSA9ICIsCiAgICBpZmVsc2UodGVzdCRwLnZhbHVlIDwgMC4wMDEsICI8IDAuMDAxIiwgcm91bmQodGVzdCRwLnZhbHVlLCAzKSkKICApCikKCmBgYAoKCmBgYHtyfQpwbGF0ZV8zID0gY25tZl92c19wbGF0ZSAlPiUgZHBseXI6OmZpbHRlcigocHJvZ3JhbS5hc3NpZ25tZW50ID09IDEgJiBvcmlnLmlkZW50ID09ICJBQ0MxLlAzIikgKSAlPiUgcm93bmFtZXMoKSAKcGxhdGVfMiA9IGNubWZfdnNfcGxhdGUgJT4lIGRwbHlyOjpmaWx0ZXIoKHByb2dyYW0uYXNzaWdubWVudCA9PSAyICYgb3JpZy5pZGVudCA9PSAiQUNDLnBsYXRlMiIpKSAlPiUgcm93bmFtZXMoKQpjZWxscyA9IGxpc3QoQUNDMS5QMyA9IHBsYXRlXzMsQUNDLnBsYXRlMiAgPSBwbGF0ZV8yKQpgYGAKIyMgUmVzdWx0cyB7LnRhYnNldH0KCgojIyMgZXhjZXB0aW9ucwoKYGBge3IgcmVzdWx0cz0nYXNpcyd9CmV4Y2VwdGlvbnNfcGx0ID0gRGltUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscywgY2VsbHMuaGlnaGxpZ2h0ID0gY2VsbHMsIGNvbHMuaGlnaGxpZ2h0ID0gYygiZ3JlZW4iLCJyZWQiKSwgY29scyA9ICJncmF5Iiwgb3JkZXIgPSBUUlVFLHB0LnNpemUgPSAyLHNpemVzLmhpZ2hsaWdodCA9IDIsY29tYmluZSA9IEYpIApjb2xvcnMgPSBjKCJncmVlbiIsInJlZCIsImN5YW4iLCJwdXJwbGUiLCJncmV5IikKcHJvZ3JhbS5hc3NpZ25tZW50X3BsdCA9IERpbVBsb3QoYWNjMV9jYW5jZXJfY2VsbHMsZ3JvdXAuYnkgPSAicHJvZ3JhbS5hc3NpZ25tZW50IixwdC5zaXplID0gMixjb2xzID0gY29sb3JzLGNvbWJpbmUgPSBGKQptZXRhZ2VuZS4xX3BsdCA8LSBGZWF0dXJlUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGMoIm1ldGFnZW5lLjEiKSxjb21iaW5lID0gRikKbWV0YWdlbmUuMl9wbHQgPSBGZWF0dXJlUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGMoIm1ldGFnZW5lLjIiKSxjb21iaW5lID0gRikKCmxzdCA9IGxpc3QoZXhjZXB0aW9ucyA9IGV4Y2VwdGlvbnNfcGx0LCBwcm9ncmFtLmFzc2lnbm1lbnQgPSBwcm9ncmFtLmFzc2lnbm1lbnRfcGx0LG1ldGFnZW5lLjEgPSBtZXRhZ2VuZS4xX3BsdCxtZXRhZ2VuZS4yID0gbWV0YWdlbmUuMl9wbHQpCgpmb3IgKGkgaW4gMTpsZW5ndGgobHN0KSkgewogIGNhdCgiIyMjICIsbmFtZXMobHN0KVtpXSwiIFxuIikKICBwcmludCgKICAgIGxzdFtbaV1dCiAgICApCiAgcGxvdC5uZXcoKQogIGRldi5vZmYoKQogIGNhdCgnIFxuXG4nKQp9CmBgYAoKCgpgYGB7cn0Kbm9fbmVnIDwtIGZ1bmN0aW9uKHgpIHsKICB4ID0geCArIGFicyhtaW4oeCkpCiAgeAp9CiMgZ2VwX3Njb3Jlc19ub3JtID0gYXBwbHkoZ2VwX3Njb3JlcywgTUFSR0lOID0gMiwgRlVOID0gbWluX21heF9ub3JtYWxpemUpJT4lIGFzLmRhdGEuZnJhbWUoKQojIGdlcF9zY29yZXNfbm9ybSA9IGdlcF9zY29yZXMKZ2VwX3Njb3Jlc19ub3JtID0gYXBwbHkoZ2VwX3Njb3JlcywgTUFSR0lOID0gMiwgRlVOID0gbm9fbmVnKSU+JSBhcy5kYXRhLmZyYW1lKCkKZ2VwX3Njb3Jlc19ub3JtID0gc3VtMm9uZShnZXBfc2NvcmVzX25vcm0pCmFsbF9tZXRhZ2VuZXMgPSBleHByZXNzaW9uX211bHQoZ2VwX3Njb3JlcyA9IGdlcF9zY29yZXNfbm9ybSxkYXRhc2V0ID0gYWxsX2FjY19jYW5jZXJfY2VsbHMsdG9wX2dlbmVzID0gVCx6X3Njb3JlID0gRikKYGBgCgoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQojIE1ha2UgbWV0YWdlbmUgbmFtZXMKZm9yIChpIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpW2ldID0gIm1ldGFnZW5lLiIgJT4lIHBhc3RlMChpKQp9CgoKI2FkZCBlYWNoIG1ldGFnZW5lIHRvIG1ldGFkYXRhCmZvciAoaSBpbiAxOm5jb2woYWxsX21ldGFnZW5lcykpIHsKICBtZXRhZ2VfbWV0YWRhdGEgPSBhbGxfbWV0YWdlbmVzICU+JSBzZWxlY3QoaSkKICBhbGxfYWNjX2NhbmNlcl9jZWxscyA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IGFsbF9hY2NfY2FuY2VyX2NlbGxzLG1ldGFkYXRhID0gbWV0YWdlX21ldGFkYXRhKQp9CgpGZWF0dXJlUGxvdChvYmplY3QgPSBhbGxfYWNjX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpLG1heC5jdXRvZmYgPSAxMDApCgpgYGAKCgoKCgoKCgoKCgoKCgoKCgoK