Functions
library(stringi)
library(reticulate)
source_from_github(repositoy = "DEG_functions",version = "0.2.53")
ℹ SHA-1 hash of file is 8ed4e9017cead897a7d352226a5413f1b5fc0cb7
Welcome to enrichR
Checking connection ...
Enrichr ... Connection is Live!
FlyEnrichr ... Connection is available!
WormEnrichr ... Connection is available!
YeastEnrichr ... Connection is available!
FishEnrichr ... Connection is available!
source_from_github(repositoy = "cNMF_functions",version = "0.4.04",script_name = "cnmf_functions_V3.R")
ℹ SHA-1 hash of file is a735d9bfb5bc55fcb203f83a7240b9713fc080d3
source_from_github(repositoy = "sc_general_functions",version = "0.1.34",script_name = "functions.R")
ℹ SHA-1 hash of file is 55839fb7db3fdb3e61d8d423b0b3129bfa1d777b
Data
genesets <- msigdb_download("Homo sapiens",category="H") %>% append( msigdb_download("Homo sapiens",category="C2",subcategory = "CP:KEGG"))
genesets[["HIF_targets"]] = hif_targets
DimPlot(xeno,group.by = "orig.ident")+ DimPlot(xeno,group.by = "treatment",shuffle = T)

from cnmf import cNMF
import pickle
f = open('./Data/cnmf/cnmf_objects/models_2Kvargenes_all_K_cnmf_obj.pckl', 'rb')
cnmf_obj = pickle.load(f)
f.close()
K selection plot
plot_path = paste0("/sci/labs/yotamd/lab_share/avishai.wizel/R_projects/EGFR/Data/cNMF/cNMF_models_Varnorm_Harmony_2Kvargenes_all_K/cNMF_models_Varnorm_Harmony_2Kvargenes_all_K.k_selection.png")
knitr::include_graphics(plot_path)
Warning in knitr::include_graphics(plot_path) :
It is highly recommended to use relative paths for images. You had absolute paths: "/sci/labs/yotamd/lab_share/avishai.wizel/R_projects/EGFR/Data/cNMF/cNMF_models_Varnorm_Harmony_2Kvargenes_all_K/cNMF_models_Varnorm_Harmony_2Kvargenes_all_K.k_selection.png"

k = 5
density_threshold = 0.1
cnmf_obj.consensus(k=k, density_threshold=density_threshold,show_clustering=True)
usage_norm, gep_scores, _, _ = cnmf_obj.load_results(K=k, density_threshold=density_threshold)
usage_norm5_xeno = py$usage_norm
gep_scores5_xeno = py$gep_scores
gep_scores = gep_scores5_xeno
usage_norm = usage_norm5_xeno
NMF usage
for (i in 1:ncol(usage_norm)) {
metage_metadata = usage_norm %>% dplyr::select(i)
xeno = AddMetaData(object = xeno,metadata = metage_metadata,col.name = paste0("gep",i))
}
Note: Using an external vector in selections is ambiguous. ℹ Use
all_of(i) instead of i to silence this
message. ℹ See https://tidyselect.r-lib.org/reference/faq-external-vector.html.
This message is displayed once per session.
FeaturePlot(object = xeno,features = paste0("gep",1:ncol(usage_norm)),ncol = 3)

Programs
GSEA
for (col in seq_along(gep_scores)) {
ranked_vec = gep_scores[,col] %>% setNames(rownames(gep_scores)) %>% sort(decreasing = TRUE)
hyp_obj <- hypeR_fgsea(ranked_vec, genesets)
print_tab(hyp_dots(hyp_obj,title = paste("program",col))+ aes(size=nes),title = paste0("gep",col))
}
gep1

gep2

gep3

gep4

gep5

NA
xeno = FindVariableFeatures(object = xeno,nfeatures = 2000)
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%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
xeno_vargenes = VariableFeatures(object = xeno)
xeno_expression = FetchData(object = xeno,vars = xeno_vargenes,slot='counts')
all_0_genes = colnames(xeno_expression)[colSums(xeno_expression==0, na.rm=TRUE)==nrow(xeno_expression)] #delete rows that have all 0
xeno_vargenes = xeno_vargenes[!xeno_vargenes %in% all_0_genes]
calculate score for
Xeno
import numpy as np
import scanpy as sc
xeno_expression = r.xeno_expression
xeno_vargenes = r.xeno_vargenes
tpm = compute_tpm(xeno_expression)
usage_by_calc = get_usage_from_score(counts=xeno_expression,tpm=tpm,genes=xeno_vargenes, cnmf_obj=cnmf_obj,k=k)
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/bin/python3.7:7: FutureWarning: X.dtype being converted to np.float32 from float64. In the next version of anndata (0.9) conversion will not be automatic. Pass dtype explicitly to avoid this warning. Pass `AnnData(X, dtype=X.dtype, ...)` to get the future behavour.
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/bin/python3.7:8: FutureWarning: X.dtype being converted to np.float32 from float64. In the next version of anndata (0.9) conversion will not be automatic. Pass dtype explicitly to avoid this warning. Pass `AnnData(X, dtype=X.dtype, ...)` to get the future behavour.
xeno_5_metagenes = py$usage_by_calc
all_metagenes = xeno_5_metagenes
colnames(all_metagenes) = c("IFNa","immune_response", "hypoxia","cell_cycle","unknown")
programs
expression
#add each metagene to metadata
for (i in 1:ncol(all_metagenes)) {
metagene_metadata = all_metagenes[,i,drop=F]
xeno = AddMetaData(object = xeno,metadata = metagene_metadata,col.name = names(all_metagenes)[i])
}
FeaturePlot(object = xeno,features = colnames(all_metagenes),ncol = 3)

NA
NA
Programs dotplot
DotPlot.2(object = xeno, features = colnames(all_metagenes),group.by = 'treatment',scale = T)+
guides(size = guide_legend(title = "Cells expressing (%)"),color = guide_colorbar(title = "Average Score"))

all_pathways = list()
for (pathway in colnames(all_metagenes)) {
data = FetchData(object = xeno,vars = c(pathway, "treatment", "orig.ident"))
all_patients = list()
for (patient in unique(xeno$orig.ident)) {
mean1 = data %>% filter(orig.ident == patient, treatment == "NT") %>% pull(1) %>% mean()
mean2 = data %>% filter(orig.ident == patient, treatment == "OSI") %>% pull(1) %>% mean()
fc =(mean1+1) / (mean2+1)
all_patients[[patient]] = fc
}
all_pathways[[pathway]] = all_patients
}
mat = as.data.frame(lapply(all_pathways, unlist))
mat = log2(t(mat) %>% as.data.frame())
breaks <- c(seq(-1,1,by=0.1))
colors_for_plot <- colorRampPalette(colors = c("blue", "white", "red"))(n = length(breaks))
pheatmap::pheatmap(mat,color = colors_for_plot,breaks = breaks,display_numbers = T,main = "log2(FC) pre/on")

NMF
programs regulation
metagenes_mean_compare(dataset = xeno,time.point_var = "treatment",prefix = "model",patient.ident_var = "orig.ident",pre_on = c("NT","OSI"),test = "wilcox.test",programs = colnames(all_metagenes)[1:4],without_split = F)
IFNa per patient

immune_response per patient

hypoxia per patient

cell_cycle per patient

NA
known genes score
ranked_vec = gep_scores[,1] %>% setNames(rownames(gep_scores)) %>% sort(decreasing = TRUE)
hyp_obj <- hypeR_fgsea(ranked_vec, genesets)
le_genes_ifna = hyp_obj$data %>% filter(label == "HALLMARK_INTERFERON_ALPHA_RESPONSE") %>% pull("le") %>% strsplit(",") %>% unlist()
ranked_vec = gep_scores[,2] %>% setNames(rownames(gep_scores)) %>% sort(decreasing = TRUE)
hyp_obj <- hypeR_fgsea(ranked_vec, genesets)
le_genes_tnfa = hyp_obj$data %>% filter(label == "HALLMARK_TNFA_SIGNALING_VIA_NFKB") %>% pull("le") %>% strsplit(",") %>% unlist()
ranked_vec = gep_scores[,3] %>% setNames(rownames(gep_scores)) %>% sort(decreasing = TRUE)
hyp_obj <- hypeR_fgsea(ranked_vec, genesets)
le_genes_hif = hyp_obj$data %>% filter(label == "HIF_targets") %>% pull("le") %>% strsplit(",") %>% unlist()
gene_list = list(IFNa_genes = genesets$HALLMARK_INTERFERON_ALPHA_RESPONSE, TNFa_genes = genesets$HALLMARK_TNFA_SIGNALING_VIA_NFKB, hif_targets = hif_targets,E2F_genes = genesets$HALLMARK_E2F_TARGETS,gep2_top = gep_scores %>% arrange(desc(gep_scores[2])) %>% rownames() %>% head(200),TNFa_le = le_genes_tnfa,hif_le = le_genes_hif,IFNa_le = le_genes_ifna)
# xeno = ScaleData(object = xeno,features = unlist(gene_list))
for (i in seq_along(gene_list)) {
genes = gene_list[[i]]
name = names(gene_list)[i]
scores = FetchData(object = xeno,vars = genes,slot = "data") %>% expm1() %>% rowMeans() #use TPM
# scores = FetchData(object = xeno,vars = genes,slot = "data") %>% rowMeans() #use TPM
xeno %<>% AddMetaData(metadata = scores,col.name = name)
}
Warning in FetchData.Seurat(object = xeno, vars = genes, slot = "data") :
The following requested variables were not found: WARS1
Warning in FetchData.Seurat(object = xeno, vars = genes, slot = "data") :
The following requested variables were not found: CCN1
Warning in FetchData.Seurat(object = xeno, vars = genes, slot = "data") :
The following requested variables were not found: AK4P1, BNIP3P1, LDHAP5, AL158201.1, MIR210, NLRP3P1, AL109946.1
Warning in FetchData.Seurat(object = xeno, vars = genes, slot = "data") :
The following requested variables were not found: H2AX, H2AZ1
cor(xeno$immune_response, xeno$gep2_top)
[1] 0.6360318
cor(xeno$immune_response, xeno$TNFa_genes)
[1] 0.3350869
cor(xeno$immune_response, xeno$TNFa_le)
[1] 0.5579202
cat ("\n")
cor(xeno$hypoxia, xeno$hif_targets)
[1] 0.6395481
cor(xeno$hypoxia, xeno$hif_le)
[1] 0.4968675
p = DotPlot(object = xeno, features = names(gene_list),group.by = 'treatment',scale = T)+
guides(size = guide_legend(title = "Cells expressing (%)"),color = guide_colorbar(title = "Average Score"))
p+scale_radius()
all_pathways = list()
for (pathway in names(gene_list)) {
data = FetchData(object = xeno,vars = c(pathway, "treatment", "orig.ident"))
all_patients = list()
for (patient in unique(xeno$orig.ident)) {
mean1 = data %>% filter(orig.ident == patient, treatment == "NT") %>% pull(1) %>% mean()
mean2 = data %>% filter(orig.ident == patient, treatment == "OSI") %>% pull(1) %>% mean()
fc =(mean1+1) / (mean2+1)
all_patients[[patient]] = fc
}
all_pathways[[pathway]] = all_patients
}
a = as.data.frame(lapply(all_pathways, unlist))
a = log2(t(a) %>% as.data.frame())
breaks <- c(seq(-1,1,by=0.05))
colors_for_plot <- colorRampPalette(colors = c("blue", "white", "red"))(n = length(breaks)-1); colors_for_plot[20:21] = "white"
pheatmap::pheatmap(a,color = colors_for_plot,breaks = breaks,display_numbers = T,main = "log2(FC) pre/on")

metagenes_mean_compare(dataset = xeno,time.point_var = "treatment",prefix = "model",patient.ident_var = "orig.ident",pre_on = c("NT","OSI"),test = "wilcox.test",programs = "hif_le",without_split = F)
## hif_le per patient {.unnumbered }

NA
for (model in unique(xeno$orig.ident)) {
cell_name = FetchData(xeno,vars = "orig.ident") %>% filter(orig.ident == model) %>% rownames()
data = FetchData(object = xeno,vars = colnames(all_metagenes),cells = cell_name)%>% scale() %>% t()
annotation = FetchData(object = xeno,vars = c("treatment","orig.ident"),cells = cell_name) %>% arrange(treatment)
col = list(treatment = c("NT" = "red", "OSI" = "green", "res" = "blue"),orig.ident = c(model = "grey"));names(col[[2]]) = model
column_ha = HeatmapAnnotation(df = annotation,col = col)
data = data[,rownames(annotation)]
print(ComplexHeatmap::Heatmap(data,show_column_names = F,show_row_names = T,cluster_rows = F,name = "Z-score expression",use_raster = T,cluster_columns = F,
row_names_gp = grid::gpar(fontsize = 10), top_annotation = column_ha))
}
# data = FetchData(object = xeno,vars = colnames(all_metagenes))%>% scale() %>% t()
# annotation = FetchData(object = xeno,vars = c("treatment","orig.ident")) %>% arrange(treatment)
# column_ha = HeatmapAnnotation(df = annotation)
# data = data[,rownames(annotation)]
# ComplexHeatmap::Heatmap(data,show_column_names = F,show_row_names = T,cluster_rows = F,name = "Z-score expression",use_raster = T,cluster_columns = F,
# row_names_gp = grid::gpar(fontsize = 10), top_annotation = column_ha)
library(ComplexHeatmap)
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),50) #take top top_genes_num
data = FetchData(object = xeno,vars = top)%>% scale() %>% t()
metagene_data = FetchData(object = xeno,vars = colnames(all_metagenes)[i],cells = colnames(xeno),slot = "data")
metagene_data$names = rownames(metagene_data)
col_list = list(circlize::colorRamp2(c(0, 1), c("white", "red"))); names(col_list) = colnames(all_metagenes)[i]
column_ha = HeatmapAnnotation(df = metagene_data,col = col_list)
print(ComplexHeatmap::Heatmap(data,show_column_names = F,show_row_names = T,cluster_rows = F,name = "Z-score expression",use_raster = T,cluster_columns = F))
a = DoHeatmap(object = xeno,features = top,group.by = "treatment",combine = F,cells = colnames(xeno),slot = "data")
print(a[[1]]+
geom_xsidetile(data = metagene_data,aes(x = names,y = "immune_response",xfill = `immune_response`,width=5, height=15)))
}
LE
genes programs UMAP
for (col in seq_along(gep_scores[1:4])) {
ranked_vec = gep_scores[,col] %>% setNames(rownames(gep_scores)) %>% sort(decreasing = TRUE)
hyp_obj <- hypeR_fgsea(ranked_vec, genesets)
for (pathway in programs_main_pathways[[col]]) {
le_genes = hyp_obj$data %>% filter(label == pathway) %>% pull("le") %>% strsplit(",") %>% unlist()
scoresAndIndices = getPathwayScores(dataMatrix = xeno@assays$RNA@data,pathwayGenes = le_genes)
pathway_name = paste0(pathway,"_le")
xeno=AddMetaData(xeno,scoresAndIndices$pathwayScores,col.name = pathway_name)
print_tab(FeaturePlot(object = xeno,features = pathway_name),title = pathway_name)
}
}
LE
genes programs regulation
metagenes_mean_compare(dataset = xeno,time.point_var = "treatment",prefix = "model",patient.ident_var = "orig.ident",pre_on = c("NT","OSI"),test = "wilcox.test",programs = programs_main_pathways %>% unlist() %>% paste0("_le"),without_split = F)
col=2
ranked_vec = gep_scores[, col] %>% setNames(rownames(gep_scores)) %>% sort(decreasing = TRUE)
print (paste("running gep",col))
hyp_obj <-hypeR_fgsea(ranked_vec, genesets_go, up_only = T)
print(hyp_dots(hyp_obj, title = paste("program", col), abrv = 70) + aes(size =nes))
Top program 2 genes
expression correlation
top_ot = gep_scores [order(gep_scores [,2],decreasing = T),2,drop = F]%>% head(200) %>% rownames()
num_of_clusters = 7
annotation = plot_genes_cor(dataset = xeno,num_of_clusters = num_of_clusters,geneIds = top_ot,height = 3)
program
2 all clusters expression
for (chosen_clusters in 1:num_of_clusters) {
chosen_genes = annotation %>% dplyr::filter(cluster == chosen_clusters) %>% rownames() #take relevant genes
# print(chosen_genes)
hyp_obj <- hypeR(chosen_genes, genesets, test = "hypergeometric", fdr=1, plotting=F,background = rownames(xeno_5_gep_scores))
scoresAndIndices <- getPathwayScores(xeno@assays$RNA@data, chosen_genes)
xeno=AddMetaData(xeno,scoresAndIndices$pathwayScores,paste0("cluster",chosen_clusters))
print_tab(plt =
hyp_dots(hyp_obj,size_by = "none",title = paste0("cluster",chosen_clusters))+
FeaturePlot(object = xeno,features = paste0("cluster",chosen_clusters)),
title = chosen_clusters)
}
Correlation of
clusters
for (chosen_clusters in 1:num_of_clusters) {
cor_res = cor(xeno$TNFa,xeno[[paste0("cluster",chosen_clusters)]])
print(paste("correlation of TNFa program to", paste0("cluster",chosen_clusters),":", cor_res))
}
clusters_idents = c("cluster1", "KEGG_OXIDATIVE_PHOSPHORYLATION","HALLMARK_TNFA_SIGNALING_VIA_NFKB","KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION","HALLMARK_P53_PATHWAY","cluster6","cluster7")
program 2 intersected
genes
programs_of_cluster = c()
for (chosen_clusters in 1:num_of_clusters) {
chosen_genes = annotation[["myannotation"]] %>% dplyr::filter(cluster == chosen_clusters) %>% rownames() #take relevant genes
pathway_name = clusters_idents[chosen_clusters]
if (!startsWith(x = pathway_name,prefix = "cluster")){
chosen_genes = (chosen_genes) %>% intersect(genesets[[pathway_name]])
pathway_name = paste0(pathway_name,"_cluster")
}
programs_of_cluster = c(programs_of_cluster,pathway_name)
print(pathway_name)
print(chosen_genes)
cat("\n")
scoresAndIndices <- getPathwayScores(xeno@assays$RNA@data, chosen_genes)
xeno=AddMetaData(xeno,scoresAndIndices$pathwayScores,pathway_name)
}
program
2 intersected pathway regulation
metagenes_mean_compare(dataset = xeno,time.point_var = "treatment",prefix = "model",patient.ident_var = "orig.ident",pre_on = c("NT","OSI"),test = "wilcox.test",programs = programs_of_cluster,without_split = F)
program 2 significant
plot
signf_plot_pre_vs_on<- function(dataset,programs,patient.ident_var,prefix,pre_on,test,time.point_var) {
final_df = data.frame()
for (metegene in programs) {
genes_by_tp = FetchData(object = dataset,vars = metegene) %>% rowSums() %>% as.data.frame() #mean expression
names(genes_by_tp)[1] = metegene
genes_by_tp = cbind(genes_by_tp,FetchData(object = dataset,vars = c(patient.ident_var,time.point_var))) # add id and time points
genes_by_tp_forPlot = genes_by_tp %>% mutate(!!ensym(patient.ident_var) := paste(prefix,genes_by_tp[,patient.ident_var])) #add "model" before each model/patient
fm <- as.formula(paste(metegene, "~", time.point_var)) #make formula to plot
#plot and split by patient:
stat.test = compare_means(formula = fm ,data = genes_by_tp_forPlot,method = test,group.by = patient.ident_var)%>%
dplyr::filter(group1 == pre_on[1] & group2 == pre_on[2]) #filter for pre vs on treatment only
final_df = rbind(final_df,stat.test)
}
return(final_df)
}
undebug(signf_plot_pre_vs_on)
final_df = signf_plot_pre_vs_on(dataset = xeno,time.point_var = "treatment",prefix = "model",patient.ident_var = "orig.ident",pre_on = c("NT","OSI"),test = "wilcox.test",programs = programs_of_cluster )
final_df = reshape2::dcast(final_df, orig.ident ~.y.,value.var = "p.adj") %>% column_to_rownames("orig.ident")
sig_heatmap(all_patients_result = final_df,title = "ad")
Top program 3 genes
expression correlation
top_hypoxia = gep_scores [order(gep_scores [,3],decreasing = T),2,drop = F]%>% head(200) %>% rownames()
num_of_clusters = 4
annotation = plot_genes_cor(dataset = xeno,hallmark_name = NULL,num_of_clusters = num_of_clusters,geneIds = top_hypoxia)
program
3 all clusters expression
for (chosen_clusters in 1:num_of_clusters) {
chosen_genes = annotation[["myannotation"]] %>% dplyr::filter(cluster == chosen_clusters) %>% rownames() #take relevant genes
# print(chosen_genes)
hyp_obj <- hypeR(chosen_genes, genesets_env, test = "hypergeometric", fdr=1, plotting=F,background = rownames(xeno_5_gep_scores))
scoresAndIndices <- getPathwayScores(xeno@assays$RNA@data, chosen_genes)
xeno=AddMetaData(xeno,scoresAndIndices$pathwayScores,paste0("cluster",chosen_clusters))
print_tab(plt =
hyp_dots(hyp_obj,size_by = "none",title = paste0("cluster",chosen_clusters))+
FeaturePlot(object = xeno,features = paste0("cluster",chosen_clusters)),
title = chosen_clusters)
}
Correlation of
clusters
for (chosen_clusters in 1:num_of_clusters) {
cor_res = cor(xeno$hypoxia,xeno[[paste0("cluster",chosen_clusters)]])
print(paste("correlation of hypoxia program to", paste0("cluster",chosen_clusters),":", cor_res))
}
clusters_idents = c("HALLMARK_HYPOXIA", "HIF_targets","cluster3","cluster4")
programs_of_cluster = c()
for (chosen_clusters in 1:num_of_clusters) {
chosen_genes = annotation[["myannotation"]] %>% dplyr::filter(cluster == chosen_clusters) %>% rownames() #take relevant genes
pathway_name = clusters_idents[chosen_clusters]
if (!startsWith(x = pathway_name,prefix = "cluster")){
chosen_genes = (chosen_genes) %>% intersect(genesets[[pathway_name]])
pathway_name = paste0(pathway_name,"_cluster")
}
programs_of_cluster = c(programs_of_cluster,pathway_name)
print(pathway_name)
print(chosen_genes)
cat("\n")
scoresAndIndices <- getPathwayScores(xeno@assays$RNA@data, chosen_genes)
xeno=AddMetaData(xeno,scoresAndIndices$pathwayScores,pathway_name)
}
program
3 intersected pathway regulation
metagenes_mean_compare(dataset = xeno,time.point_var = "treatment",prefix = "model",patient.ident_var = "orig.ident",pre_on = c("NT","OSI"),test = "wilcox.test",programs = programs_of_cluster,without_split = F)
Top program 3 genes
expression correlation
top_cc = gep_scores [order(gep_scores [,4],decreasing = T),2,drop = F]%>% head(200) %>% rownames()
num_of_clusters = 4
annotation = plot_genes_cor(dataset = xeno,hallmark_name = NULL,num_of_clusters = num_of_clusters,geneIds = top_cc)
program
3 all clusters expression
for (chosen_clusters in 1:num_of_clusters) {
chosen_genes = annotation[["myannotation"]] %>% dplyr::filter(cluster == chosen_clusters) %>% rownames() #take relevant genes
# print(chosen_genes)
hyp_obj <- hypeR(chosen_genes, genesets_env, test = "hypergeometric", fdr=1, plotting=F,background = rownames(xeno_5_gep_scores))
scoresAndIndices <- getPathwayScores(xeno@assays$RNA@data, chosen_genes)
xeno=AddMetaData(xeno,scoresAndIndices$pathwayScores,paste0("cluster",chosen_clusters))
print_tab(plt =
hyp_dots(hyp_obj,size_by = "none",title = paste0("cluster",chosen_clusters))+
FeaturePlot(object = xeno,features = paste0("cluster",chosen_clusters)),
title = chosen_clusters)
}
LS0tCnRpdGxlOiAnYHIgcnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLHJlcGxhY2VtZW50ID0gIiIpYCcgCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19jb2xsYXBzZTogeWVzCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvY19kZXB0aDogMQotLS0KCgoKIyBGdW5jdGlvbnMKCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQoKbGlicmFyeShzdHJpbmdpKQpsaWJyYXJ5KHJldGljdWxhdGUpCnNvdXJjZV9mcm9tX2dpdGh1YihyZXBvc2l0b3kgPSAiREVHX2Z1bmN0aW9ucyIsdmVyc2lvbiA9ICIwLjIuNTMiKQpzb3VyY2VfZnJvbV9naXRodWIocmVwb3NpdG95ID0gImNOTUZfZnVuY3Rpb25zIix2ZXJzaW9uID0gIjAuNC4wNCIsc2NyaXB0X25hbWUgPSAiY25tZl9mdW5jdGlvbnNfVjMuUiIpCnNvdXJjZV9mcm9tX2dpdGh1YihyZXBvc2l0b3kgPSAic2NfZ2VuZXJhbF9mdW5jdGlvbnMiLHZlcnNpb24gPSAiMC4xLjM0IixzY3JpcHRfbmFtZSA9ICJmdW5jdGlvbnMuUiIpCgpgYGAKCgojIERhdGEKCmBgYHtyfQpnZW5lc2V0cyA8LSBtc2lnZGJfZG93bmxvYWQoIkhvbW8gc2FwaWVucyIsY2F0ZWdvcnk9IkgiKSAlPiUgYXBwZW5kKCBtc2lnZGJfZG93bmxvYWQoIkhvbW8gc2FwaWVucyIsY2F0ZWdvcnk9IkMyIixzdWJjYXRlZ29yeSA9ICJDUDpLRUdHIikpCmdlbmVzZXRzW1siSElGX3RhcmdldHMiXV0gPSBoaWZfdGFyZ2V0cwpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTEyfQpEaW1QbG90KHhlbm8sZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpKyBEaW1QbG90KHhlbm8sZ3JvdXAuYnkgPSAidHJlYXRtZW50IixzaHVmZmxlID0gVCkKYGBgCgoKYGBge3B5dGhvbn0KZnJvbSBjbm1mIGltcG9ydCBjTk1GCmltcG9ydCBwaWNrbGUKZiA9IG9wZW4oJy4vRGF0YS9jbm1mL2NubWZfb2JqZWN0cy9tb2RlbHNfMkt2YXJnZW5lc19hbGxfS19jbm1mX29iai5wY2tsJywgJ3JiJykKY25tZl9vYmogPSBwaWNrbGUubG9hZChmKQpmLmNsb3NlKCkKYGBgCgojIEsgc2VsZWN0aW9uIHBsb3QKYGBge3IgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9Mn0KcGxvdF9wYXRoID0gcGFzdGUwKCIvc2NpL2xhYnMveW90YW1kL2xhYl9zaGFyZS9hdmlzaGFpLndpemVsL1JfcHJvamVjdHMvRUdGUi9EYXRhL2NOTUYvY05NRl9tb2RlbHNfVmFybm9ybV9IYXJtb255XzJLdmFyZ2VuZXNfYWxsX0svY05NRl9tb2RlbHNfVmFybm9ybV9IYXJtb255XzJLdmFyZ2VuZXNfYWxsX0sua19zZWxlY3Rpb24ucG5nIikKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGxvdF9wYXRoKQpgYGAKCmBgYHtweXRob259CmsgPSA1CmRlbnNpdHlfdGhyZXNob2xkID0gMC4xIApjbm1mX29iai5jb25zZW5zdXMoaz1rLCBkZW5zaXR5X3RocmVzaG9sZD1kZW5zaXR5X3RocmVzaG9sZCxzaG93X2NsdXN0ZXJpbmc9VHJ1ZSkKdXNhZ2Vfbm9ybSwgZ2VwX3Njb3JlcywgXywgXyA9IGNubWZfb2JqLmxvYWRfcmVzdWx0cyhLPWssIGRlbnNpdHlfdGhyZXNob2xkPWRlbnNpdHlfdGhyZXNob2xkKQpgYGAKCmBgYHtyfQp1c2FnZV9ub3JtNV94ZW5vICA9IHB5JHVzYWdlX25vcm0KZ2VwX3Njb3JlczVfeGVubyA9IHB5JGdlcF9zY29yZXMKYGBgCgpgYGB7cn0KZ2VwX3Njb3JlcyA9IGdlcF9zY29yZXM1X3hlbm8KdXNhZ2Vfbm9ybSA9IHVzYWdlX25vcm01X3hlbm8KYGBgCgojIE5NRiB1c2FnZQpgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMiwgcmVzdWx0cz0nYXNpcyd9CiAgZm9yIChpIGluIDE6bmNvbCh1c2FnZV9ub3JtKSkgewogICAgbWV0YWdlX21ldGFkYXRhID0gdXNhZ2Vfbm9ybSAlPiUgZHBseXI6OnNlbGVjdChpKQogICAgeGVubyA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IHhlbm8sbWV0YWRhdGEgPSBtZXRhZ2VfbWV0YWRhdGEsY29sLm5hbWUgPSBwYXN0ZTAoImdlcCIsaSkpCiAgfQogIAogIEZlYXR1cmVQbG90KG9iamVjdCA9IHhlbm8sZmVhdHVyZXMgPSBwYXN0ZTAoImdlcCIsMTpuY29sKHVzYWdlX25vcm0pKSxuY29sID0gMykKYGBgCiMgUHJvZ3JhbXMgR1NFQSB7LnRhYnNldH0KCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQogIGZvciAoY29sIGluIHNlcV9hbG9uZyhnZXBfc2NvcmVzKSkgewogICAgIHJhbmtlZF92ZWMgPSBnZXBfc2NvcmVzWyxjb2xdICU+JSBzZXROYW1lcyhyb3duYW1lcyhnZXBfc2NvcmVzKSkgJT4lIHNvcnQoZGVjcmVhc2luZyA9IFRSVUUpIAogICAgIGh5cF9vYmogPC0gaHlwZVJfZmdzZWEocmFua2VkX3ZlYywgZ2VuZXNldHMpCiAgICAgICBwcmludF90YWIoaHlwX2RvdHMoaHlwX29iaix0aXRsZSA9IHBhc3RlKCJwcm9ncmFtIixjb2wpKSsgYWVzKHNpemU9bmVzKSx0aXRsZSA9IHBhc3RlMCgiZ2VwIixjb2wpKQogIH0KCgoKYGBgCgoKCmBgYHtyfQp4ZW5vID0gRmluZFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0geGVubyxuZmVhdHVyZXMgPSAyMDAwKQp4ZW5vX3ZhcmdlbmVzID0gVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSB4ZW5vKQoKeGVub19leHByZXNzaW9uID0gRmV0Y2hEYXRhKG9iamVjdCA9IHhlbm8sdmFycyA9IHhlbm9fdmFyZ2VuZXMsc2xvdD0nY291bnRzJykKYWxsXzBfZ2VuZXMgPSBjb2xuYW1lcyh4ZW5vX2V4cHJlc3Npb24pW2NvbFN1bXMoeGVub19leHByZXNzaW9uPT0wLCBuYS5ybT1UUlVFKT09bnJvdyh4ZW5vX2V4cHJlc3Npb24pXSAjZGVsZXRlIHJvd3MgdGhhdCBoYXZlIGFsbCAwCnhlbm9fdmFyZ2VuZXMgPSB4ZW5vX3ZhcmdlbmVzWyF4ZW5vX3ZhcmdlbmVzICVpbiUgYWxsXzBfZ2VuZXNdCgpgYGAKCgojIGNhbGN1bGF0ZSBzY29yZSBmb3IgWGVubwpgYGB7cHl0aG9ufQppbXBvcnQgbnVtcHkgYXMgbnAKaW1wb3J0IHNjYW5weSBhcyBzYwp4ZW5vX2V4cHJlc3Npb24gPSByLnhlbm9fZXhwcmVzc2lvbgp4ZW5vX3ZhcmdlbmVzID0gci54ZW5vX3ZhcmdlbmVzCnRwbSA9ICBjb21wdXRlX3RwbSh4ZW5vX2V4cHJlc3Npb24pCnVzYWdlX2J5X2NhbGMgPSBnZXRfdXNhZ2VfZnJvbV9zY29yZShjb3VudHM9eGVub19leHByZXNzaW9uLHRwbT10cG0sZ2VuZXM9eGVub192YXJnZW5lcywgY25tZl9vYmo9Y25tZl9vYmosaz1rKQpgYGAKCmBgYHtyfQp4ZW5vXzVfbWV0YWdlbmVzID0gcHkkdXNhZ2VfYnlfY2FsYwpgYGAKCmBgYHtyfQphbGxfbWV0YWdlbmVzID0geGVub181X21ldGFnZW5lcwpjb2xuYW1lcyhhbGxfbWV0YWdlbmVzKSA9IGMoIklGTmEiLCJpbW11bmVfcmVzcG9uc2UiLCAiaHlwb3hpYSIsImNlbGxfY3ljbGUiLCJ1bmtub3duIikKCmBgYAoKIyBwcm9ncmFtcyBleHByZXNzaW9uCmBgYHtyIGVjaG89VFJVRSwgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTIsIHJlc3VsdHM9J2FzaXMnfQoKI2FkZCBlYWNoIG1ldGFnZW5lIHRvIG1ldGFkYXRhCmZvciAoaSAgaW4gMTpuY29sKGFsbF9tZXRhZ2VuZXMpKSB7CiAgbWV0YWdlbmVfbWV0YWRhdGEgPSBhbGxfbWV0YWdlbmVzWyxpLGRyb3A9Rl0KICB4ZW5vID0gQWRkTWV0YURhdGEob2JqZWN0ID0geGVubyxtZXRhZGF0YSA9IG1ldGFnZW5lX21ldGFkYXRhLGNvbC5uYW1lID0gbmFtZXMoYWxsX21ldGFnZW5lcylbaV0pCn0KCkZlYXR1cmVQbG90KG9iamVjdCA9IHhlbm8sZmVhdHVyZXMgPSBjb2xuYW1lcyhhbGxfbWV0YWdlbmVzKSxuY29sID0gMykKCgpgYGAKIyBQcm9ncmFtcyBkb3RwbG90CmBgYHtyIGZpZy53aWR0aD04fQpEb3RQbG90LjIob2JqZWN0ID0geGVubywgZmVhdHVyZXMgPSAgY29sbmFtZXMoYWxsX21ldGFnZW5lcyksZ3JvdXAuYnkgID0gJ3RyZWF0bWVudCcsc2NhbGUgPSBUKSsKICBndWlkZXMoc2l6ZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJDZWxscyBleHByZXNzaW5nICglKSIpLGNvbG9yID0gZ3VpZGVfY29sb3JiYXIodGl0bGUgPSAiQXZlcmFnZSBTY29yZSIpKQpgYGAKCgoKYGBge3J9CmFsbF9wYXRod2F5cyA9IGxpc3QoKQpmb3IgKHBhdGh3YXkgaW4gY29sbmFtZXMoYWxsX21ldGFnZW5lcykpIHsKICBkYXRhID0gRmV0Y2hEYXRhKG9iamVjdCA9IHhlbm8sdmFycyA9IGMocGF0aHdheSwgInRyZWF0bWVudCIsICJvcmlnLmlkZW50IikpCiAgYWxsX3BhdGllbnRzID0gbGlzdCgpCiAgZm9yIChwYXRpZW50IGluIHVuaXF1ZSh4ZW5vJG9yaWcuaWRlbnQpKSB7CiAgICBtZWFuMSA9IGRhdGEgJT4lIGZpbHRlcihvcmlnLmlkZW50ID09IHBhdGllbnQsIHRyZWF0bWVudCA9PSAiTlQiKSAlPiUgcHVsbCgxKSAlPiUgbWVhbigpCiAgICBtZWFuMiA9IGRhdGEgJT4lIGZpbHRlcihvcmlnLmlkZW50ID09IHBhdGllbnQsIHRyZWF0bWVudCA9PSAiT1NJIikgJT4lIHB1bGwoMSkgJT4lIG1lYW4oKQogICAgZmMgPShtZWFuMSsxKSAvIChtZWFuMisxKQogICAgYWxsX3BhdGllbnRzW1twYXRpZW50XV0gPSBmYwogIH0KICBhbGxfcGF0aHdheXNbW3BhdGh3YXldXSA9IGFsbF9wYXRpZW50cwp9CgoKbWF0ID0gYXMuZGF0YS5mcmFtZShsYXBwbHkoYWxsX3BhdGh3YXlzLCB1bmxpc3QpKQptYXQgPSBsb2cyKHQobWF0KSAlPiUgYXMuZGF0YS5mcmFtZSgpKQpicmVha3MgPC0gYyhzZXEoLTEsMSxieT0wLjEpKQpjb2xvcnNfZm9yX3Bsb3QgPC0gY29sb3JSYW1wUGFsZXR0ZShjb2xvcnMgPSBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKShuID0gbGVuZ3RoKGJyZWFrcykpCgpwaGVhdG1hcDo6cGhlYXRtYXAobWF0LGNvbG9yID0gY29sb3JzX2Zvcl9wbG90LGJyZWFrcyA9IGJyZWFrcyxkaXNwbGF5X251bWJlcnMgPSBULG1haW4gPSAibG9nMihGQykgcHJlL29uIikKCmBgYAoKCiMgTk1GIHByb2dyYW1zIHJlZ3VsYXRpb24gIHsudGFic2V0fQoKYGBge3IgZWNobz1UUlVFLCAgcmVzdWx0cz0nYXNpcyd9Cm1ldGFnZW5lc19tZWFuX2NvbXBhcmUoZGF0YXNldCA9IHhlbm8sdGltZS5wb2ludF92YXIgPSAidHJlYXRtZW50IixwcmVmaXggPSAibW9kZWwiLHBhdGllbnQuaWRlbnRfdmFyID0gIm9yaWcuaWRlbnQiLHByZV9vbiA9IGMoIk5UIiwiT1NJIiksdGVzdCA9ICJ3aWxjb3gudGVzdCIscHJvZ3JhbXMgPSBjb2xuYW1lcyhhbGxfbWV0YWdlbmVzKVsxOjRdLHdpdGhvdXRfc3BsaXQgPSBGKQpgYGAKCgoKIyBrbm93biBnZW5lcyBzY29yZQpgYGB7cn0KCnJhbmtlZF92ZWMgPSBnZXBfc2NvcmVzWywxXSAlPiUgc2V0TmFtZXMocm93bmFtZXMoZ2VwX3Njb3JlcykpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUUlVFKSAKaHlwX29iaiA8LSBoeXBlUl9mZ3NlYShyYW5rZWRfdmVjLCBnZW5lc2V0cykKbGVfZ2VuZXNfaWZuYSA9ICBoeXBfb2JqJGRhdGEgJT4lIGZpbHRlcihsYWJlbCA9PSAiSEFMTE1BUktfSU5URVJGRVJPTl9BTFBIQV9SRVNQT05TRSIpICU+JSBwdWxsKCJsZSIpICU+JSBzdHJzcGxpdCgiLCIpICU+JSB1bmxpc3QoKQoKcmFua2VkX3ZlYyA9IGdlcF9zY29yZXNbLDJdICU+JSBzZXROYW1lcyhyb3duYW1lcyhnZXBfc2NvcmVzKSkgJT4lIHNvcnQoZGVjcmVhc2luZyA9IFRSVUUpIApoeXBfb2JqIDwtIGh5cGVSX2Znc2VhKHJhbmtlZF92ZWMsIGdlbmVzZXRzKQpsZV9nZW5lc190bmZhID0gIGh5cF9vYmokZGF0YSAlPiUgZmlsdGVyKGxhYmVsID09ICJIQUxMTUFSS19UTkZBX1NJR05BTElOR19WSUFfTkZLQiIpICU+JSBwdWxsKCJsZSIpICU+JSBzdHJzcGxpdCgiLCIpICU+JSB1bmxpc3QoKQoKcmFua2VkX3ZlYyA9IGdlcF9zY29yZXNbLDNdICU+JSBzZXROYW1lcyhyb3duYW1lcyhnZXBfc2NvcmVzKSkgJT4lIHNvcnQoZGVjcmVhc2luZyA9IFRSVUUpIApoeXBfb2JqIDwtIGh5cGVSX2Znc2VhKHJhbmtlZF92ZWMsIGdlbmVzZXRzKQpsZV9nZW5lc19oaWYgPSAgaHlwX29iaiRkYXRhICU+JSBmaWx0ZXIobGFiZWwgPT0gIkhJRl90YXJnZXRzIikgJT4lIHB1bGwoImxlIikgJT4lIHN0cnNwbGl0KCIsIikgJT4lIHVubGlzdCgpCgoKZ2VuZV9saXN0ID0gbGlzdChJRk5hX2dlbmVzID0gZ2VuZXNldHMkSEFMTE1BUktfSU5URVJGRVJPTl9BTFBIQV9SRVNQT05TRSwgVE5GYV9nZW5lcyA9IGdlbmVzZXRzJEhBTExNQVJLX1RORkFfU0lHTkFMSU5HX1ZJQV9ORktCLCBoaWZfdGFyZ2V0cyA9IGhpZl90YXJnZXRzLEUyRl9nZW5lcyA9IGdlbmVzZXRzJEhBTExNQVJLX0UyRl9UQVJHRVRTLGdlcDJfdG9wID0gZ2VwX3Njb3JlcyAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3Jlc1syXSkpICU+JSByb3duYW1lcygpICU+JSBoZWFkKDIwMCksVE5GYV9sZSA9IGxlX2dlbmVzX3RuZmEsaGlmX2xlID0gbGVfZ2VuZXNfaGlmLElGTmFfbGUgID0gbGVfZ2VuZXNfaWZuYSkKIyB4ZW5vID0gU2NhbGVEYXRhKG9iamVjdCA9IHhlbm8sZmVhdHVyZXMgPSB1bmxpc3QoZ2VuZV9saXN0KSkKZm9yIChpIGluIHNlcV9hbG9uZyhnZW5lX2xpc3QpKSB7CiAgZ2VuZXMgPSBnZW5lX2xpc3RbW2ldXQogIG5hbWUgPSBuYW1lcyhnZW5lX2xpc3QpW2ldCiAgc2NvcmVzID0gRmV0Y2hEYXRhKG9iamVjdCA9IHhlbm8sdmFycyA9IGdlbmVzLHNsb3QgPSAiZGF0YSIpICAlPiUgIGV4cG0xKCkgICU+JSAgcm93TWVhbnMoKSAjdXNlICBUUE0KICAgICMgc2NvcmVzID0gRmV0Y2hEYXRhKG9iamVjdCA9IHhlbm8sdmFycyA9IGdlbmVzLHNsb3QgPSAiZGF0YSIpICAlPiUgIHJvd01lYW5zKCkgI3VzZSAgVFBNCgogIHhlbm8gJTw+JSBBZGRNZXRhRGF0YShtZXRhZGF0YSA9IHNjb3Jlcyxjb2wubmFtZSA9IG5hbWUpCgp9CgoKCgoKYGBgCgpgYGB7cn0KY29yKHhlbm8kaW1tdW5lX3Jlc3BvbnNlLCB4ZW5vJGdlcDJfdG9wKQpjb3IoeGVubyRpbW11bmVfcmVzcG9uc2UsIHhlbm8kVE5GYV9nZW5lcykKY29yKHhlbm8kaW1tdW5lX3Jlc3BvbnNlLCB4ZW5vJFRORmFfbGUpCmNhdCAoIlxuIikKCmNvcih4ZW5vJGh5cG94aWEsIHhlbm8kaGlmX3RhcmdldHMpCmNvcih4ZW5vJGh5cG94aWEsIHhlbm8kaGlmX2xlKQpgYGAKCgoKCgpgYGB7cn0KCnAgPSBEb3RQbG90KG9iamVjdCA9IHhlbm8sIGZlYXR1cmVzID0gIG5hbWVzKGdlbmVfbGlzdCksZ3JvdXAuYnkgID0gJ3RyZWF0bWVudCcsc2NhbGUgPSBUKSsKICBndWlkZXMoc2l6ZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJDZWxscyBleHByZXNzaW5nICglKSIpLGNvbG9yID0gZ3VpZGVfY29sb3JiYXIodGl0bGUgPSAiQXZlcmFnZSBTY29yZSIpKQogcCtzY2FsZV9yYWRpdXMoKQpgYGAKCgpgYGB7cn0KCmFsbF9wYXRod2F5cyA9IGxpc3QoKQpmb3IgKHBhdGh3YXkgaW4gbmFtZXMoZ2VuZV9saXN0KSkgewogIGRhdGEgPSBGZXRjaERhdGEob2JqZWN0ID0geGVubyx2YXJzID0gYyhwYXRod2F5LCAidHJlYXRtZW50IiwgIm9yaWcuaWRlbnQiKSkKICBhbGxfcGF0aWVudHMgPSBsaXN0KCkKICBmb3IgKHBhdGllbnQgaW4gdW5pcXVlKHhlbm8kb3JpZy5pZGVudCkpIHsKICAgIG1lYW4xID0gZGF0YSAlPiUgZmlsdGVyKG9yaWcuaWRlbnQgPT0gcGF0aWVudCwgdHJlYXRtZW50ID09ICJOVCIpICU+JSBwdWxsKDEpICU+JSBtZWFuKCkKICAgIG1lYW4yID0gZGF0YSAlPiUgZmlsdGVyKG9yaWcuaWRlbnQgPT0gcGF0aWVudCwgdHJlYXRtZW50ID09ICJPU0kiKSAlPiUgcHVsbCgxKSAlPiUgbWVhbigpCiAgICBmYyA9KG1lYW4xKzEpIC8gKG1lYW4yKzEpCiAgICBhbGxfcGF0aWVudHNbW3BhdGllbnRdXSA9IGZjCiAgfQogIGFsbF9wYXRod2F5c1tbcGF0aHdheV1dID0gYWxsX3BhdGllbnRzCn0KCgphID0gYXMuZGF0YS5mcmFtZShsYXBwbHkoYWxsX3BhdGh3YXlzLCB1bmxpc3QpKQphID0gbG9nMih0KGEpICU+JSBhcy5kYXRhLmZyYW1lKCkpCmJyZWFrcyA8LSBjKHNlcSgtMSwxLGJ5PTAuMDUpKQpjb2xvcnNfZm9yX3Bsb3QgPC0gY29sb3JSYW1wUGFsZXR0ZShjb2xvcnMgPSBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKShuID0gbGVuZ3RoKGJyZWFrcyktMSk7IGNvbG9yc19mb3JfcGxvdFsyMDoyMV0gPSAid2hpdGUiCnBoZWF0bWFwOjpwaGVhdG1hcChhLGNvbG9yID0gY29sb3JzX2Zvcl9wbG90LGJyZWFrcyA9IGJyZWFrcyxkaXNwbGF5X251bWJlcnMgPSBULG1haW4gPSAibG9nMihGQykgcHJlL29uIikKCmBgYAoKCgoKYGBge3J9Cm1ldGFnZW5lc19tZWFuX2NvbXBhcmUoZGF0YXNldCA9IHhlbm8sdGltZS5wb2ludF92YXIgPSAidHJlYXRtZW50IixwcmVmaXggPSAibW9kZWwiLHBhdGllbnQuaWRlbnRfdmFyID0gIm9yaWcuaWRlbnQiLHByZV9vbiA9IGMoIk5UIiwiT1NJIiksdGVzdCA9ICJ3aWxjb3gudGVzdCIscHJvZ3JhbXMgPSAiaGlmX2xlIix3aXRob3V0X3NwbGl0ID0gRikKYGBgCgoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTJ9CmZvciAobW9kZWwgaW4gdW5pcXVlKHhlbm8kb3JpZy5pZGVudCkpIHsKICBjZWxsX25hbWUgPSBGZXRjaERhdGEoeGVubyx2YXJzID0gIm9yaWcuaWRlbnQiKSAlPiUgZmlsdGVyKG9yaWcuaWRlbnQgPT0gbW9kZWwpICU+JSByb3duYW1lcygpCiAgZGF0YSA9IEZldGNoRGF0YShvYmplY3QgPSB4ZW5vLHZhcnMgPSBjb2xuYW1lcyhhbGxfbWV0YWdlbmVzKSxjZWxscyA9IGNlbGxfbmFtZSklPiUgc2NhbGUoKSAlPiUgdCgpIAogIGFubm90YXRpb24gPSBGZXRjaERhdGEob2JqZWN0ID0geGVubyx2YXJzID0gYygidHJlYXRtZW50Iiwib3JpZy5pZGVudCIpLGNlbGxzID0gY2VsbF9uYW1lKSAlPiUgYXJyYW5nZSh0cmVhdG1lbnQpCiAgY29sID0gbGlzdCh0cmVhdG1lbnQgPSBjKCJOVCIgPSAicmVkIiwgIk9TSSIgPSAiZ3JlZW4iLCAicmVzIiA9ICJibHVlIiksb3JpZy5pZGVudCA9IGMobW9kZWwgPSAiZ3JleSIpKTtuYW1lcyhjb2xbWzJdXSkgPSBtb2RlbAogIGNvbHVtbl9oYSA9IEhlYXRtYXBBbm5vdGF0aW9uKGRmID0gYW5ub3RhdGlvbixjb2wgPSBjb2wpIAogIGRhdGEgPSBkYXRhWyxyb3duYW1lcyhhbm5vdGF0aW9uKV0KICBwcmludChDb21wbGV4SGVhdG1hcDo6SGVhdG1hcChkYXRhLHNob3dfY29sdW1uX25hbWVzID0gRixzaG93X3Jvd19uYW1lcyA9IFQsY2x1c3Rlcl9yb3dzID0gRixuYW1lID0gIlotc2NvcmUgZXhwcmVzc2lvbiIsdXNlX3Jhc3RlciA9IFQsY2x1c3Rlcl9jb2x1bW5zID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICByb3dfbmFtZXNfZ3AgPSBncmlkOjpncGFyKGZvbnRzaXplID0gMTApLCB0b3BfYW5ub3RhdGlvbiA9IGNvbHVtbl9oYSkpCgp9CgojIGRhdGEgPSBGZXRjaERhdGEob2JqZWN0ID0geGVubyx2YXJzID0gY29sbmFtZXMoYWxsX21ldGFnZW5lcykpJT4lIHNjYWxlKCkgJT4lIHQoKSAKIyBhbm5vdGF0aW9uID0gRmV0Y2hEYXRhKG9iamVjdCA9IHhlbm8sdmFycyA9IGMoInRyZWF0bWVudCIsIm9yaWcuaWRlbnQiKSkgJT4lIGFycmFuZ2UodHJlYXRtZW50KQojIGNvbHVtbl9oYSA9IEhlYXRtYXBBbm5vdGF0aW9uKGRmID0gYW5ub3RhdGlvbikgCiMgZGF0YSA9IGRhdGFbLHJvd25hbWVzKGFubm90YXRpb24pXQojIENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKGRhdGEsc2hvd19jb2x1bW5fbmFtZXMgPSBGLHNob3dfcm93X25hbWVzID0gVCxjbHVzdGVyX3Jvd3MgPSBGLG5hbWUgPSAiWi1zY29yZSBleHByZXNzaW9uIix1c2VfcmFzdGVyID0gVCxjbHVzdGVyX2NvbHVtbnMgPSBGLAojICAgICAgICAgICAgICAgICAgICAgICAgIHJvd19uYW1lc19ncCA9IGdyaWQ6OmdwYXIoZm9udHNpemUgPSAxMCksIHRvcF9hbm5vdGF0aW9uID0gY29sdW1uX2hhKQoKCmBgYAoKYGBge3IgZmlnLmhlaWdodD01fQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpmb3IgKGkgaW4gMTpuY29sKGdlcF9zY29yZXMpKSB7CiAgdG9wX2dlbmVzID0gZ2VwX3Njb3JlcyAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3Jlc1tpXSkpICNzb3J0IGJ5IHNjb3JlIGEKICB0b3AgPSBoZWFkKHJvd25hbWVzKHRvcF9nZW5lcyksNTApICN0YWtlIHRvcCB0b3BfZ2VuZXNfbnVtCiAgZGF0YSA9IEZldGNoRGF0YShvYmplY3QgPSB4ZW5vLHZhcnMgPSB0b3ApJT4lIHNjYWxlKCkgJT4lIHQoKSAKICBtZXRhZ2VuZV9kYXRhID0gRmV0Y2hEYXRhKG9iamVjdCA9IHhlbm8sdmFycyA9IGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpW2ldLGNlbGxzID0gY29sbmFtZXMoeGVubyksc2xvdCA9ICJkYXRhIikKICBtZXRhZ2VuZV9kYXRhJG5hbWVzID0gcm93bmFtZXMobWV0YWdlbmVfZGF0YSkKICBjb2xfbGlzdCA9IGxpc3QoY2lyY2xpemU6OmNvbG9yUmFtcDIoYygwLCAxKSwgYygid2hpdGUiLCAicmVkIikpKTsgbmFtZXMoY29sX2xpc3QpID0gY29sbmFtZXMoYWxsX21ldGFnZW5lcylbaV0KICBjb2x1bW5faGEgPSBIZWF0bWFwQW5ub3RhdGlvbihkZiA9IG1ldGFnZW5lX2RhdGEsY29sID0gY29sX2xpc3QpCiAgcHJpbnQoQ29tcGxleEhlYXRtYXA6OkhlYXRtYXAoZGF0YSxzaG93X2NvbHVtbl9uYW1lcyA9IEYsc2hvd19yb3dfbmFtZXMgPSBULGNsdXN0ZXJfcm93cyA9IEYsbmFtZSA9ICJaLXNjb3JlIGV4cHJlc3Npb24iLHVzZV9yYXN0ZXIgPSBULGNsdXN0ZXJfY29sdW1ucyA9IEYpKQogICAgCiAgCiAgYSA9IERvSGVhdG1hcChvYmplY3QgPSB4ZW5vLGZlYXR1cmVzID0gdG9wLGdyb3VwLmJ5ID0gInRyZWF0bWVudCIsY29tYmluZSA9IEYsY2VsbHMgPSBjb2xuYW1lcyh4ZW5vKSxzbG90ID0gImRhdGEiKQogIHByaW50KGFbWzFdXSsKICAgICAgICAgIGdlb21feHNpZGV0aWxlKGRhdGEgPSBtZXRhZ2VuZV9kYXRhLGFlcyh4ID0gbmFtZXMseSA9ICJpbW11bmVfcmVzcG9uc2UiLHhmaWxsID0gYGltbXVuZV9yZXNwb25zZWAsd2lkdGg9NSwgaGVpZ2h0PTE1KSkpCiAgCgoKfQpgYGAKCgoKCgoKIyBMRSBnZW5lcyBwcm9ncmFtcyBVTUFQICB7LnRhYnNldH0KCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KICBmb3IgKGNvbCBpbiBzZXFfYWxvbmcoZ2VwX3Njb3Jlc1sxOjRdKSkgewogICAgIHJhbmtlZF92ZWMgPSBnZXBfc2NvcmVzWyxjb2xdICU+JSBzZXROYW1lcyhyb3duYW1lcyhnZXBfc2NvcmVzKSkgJT4lIHNvcnQoZGVjcmVhc2luZyA9IFRSVUUpIAogICAgIGh5cF9vYmogPC0gaHlwZVJfZmdzZWEocmFua2VkX3ZlYywgZ2VuZXNldHMpCiAgICAgZm9yIChwYXRod2F5IGluIHByb2dyYW1zX21haW5fcGF0aHdheXNbW2NvbF1dKSB7CiAgICAgICAgbGVfZ2VuZXMgPSAgaHlwX29iaiRkYXRhICU+JSBmaWx0ZXIobGFiZWwgPT0gcGF0aHdheSkgJT4lIHB1bGwoImxlIikgJT4lIHN0cnNwbGl0KCIsIikgJT4lIHVubGlzdCgpCiAgICAgICAgc2NvcmVzQW5kSW5kaWNlcyA9IGdldFBhdGh3YXlTY29yZXMoZGF0YU1hdHJpeCA9IHhlbm9AYXNzYXlzJFJOQUBkYXRhLHBhdGh3YXlHZW5lcyA9IGxlX2dlbmVzKQogICAgICAgIHBhdGh3YXlfbmFtZSA9IHBhc3RlMChwYXRod2F5LCJfbGUiKQogICAgICAgIHhlbm89QWRkTWV0YURhdGEoeGVubyxzY29yZXNBbmRJbmRpY2VzJHBhdGh3YXlTY29yZXMsY29sLm5hbWUgPSBwYXRod2F5X25hbWUpCiAgICAgICAgcHJpbnRfdGFiKEZlYXR1cmVQbG90KG9iamVjdCA9IHhlbm8sZmVhdHVyZXMgPSBwYXRod2F5X25hbWUpLHRpdGxlID0gcGF0aHdheV9uYW1lKQogICAgIH0KICB9CmBgYAoKCiMgTEUgZ2VuZXMgcHJvZ3JhbXMgcmVndWxhdGlvbiAgey50YWJzZXR9CgpgYGB7ciAgcmVzdWx0cz0nYXNpcyd9Cm1ldGFnZW5lc19tZWFuX2NvbXBhcmUoZGF0YXNldCA9IHhlbm8sdGltZS5wb2ludF92YXIgPSAidHJlYXRtZW50IixwcmVmaXggPSAibW9kZWwiLHBhdGllbnQuaWRlbnRfdmFyID0gIm9yaWcuaWRlbnQiLHByZV9vbiA9IGMoIk5UIiwiT1NJIiksdGVzdCA9ICJ3aWxjb3gudGVzdCIscHJvZ3JhbXMgPSBwcm9ncmFtc19tYWluX3BhdGh3YXlzICU+JSB1bmxpc3QoKSAlPiUgcGFzdGUwKCJfbGUiKSx3aXRob3V0X3NwbGl0ID0gRikKYGBgCmBgYHtyfQpjb2w9MgpyYW5rZWRfdmVjID0gZ2VwX3Njb3Jlc1ssIGNvbF0gJT4lIHNldE5hbWVzKHJvd25hbWVzKGdlcF9zY29yZXMpKSAlPiUgc29ydChkZWNyZWFzaW5nID0gVFJVRSkKcHJpbnQgKHBhc3RlKCJydW5uaW5nIGdlcCIsY29sKSkKaHlwX29iaiA8LWh5cGVSX2Znc2VhKHJhbmtlZF92ZWMsIGdlbmVzZXRzX2dvLCB1cF9vbmx5ID0gVCkKCnByaW50KGh5cF9kb3RzKGh5cF9vYmosIHRpdGxlID0gcGFzdGUoInByb2dyYW0iLCBjb2wpLCBhYnJ2ID0gNzApICsgYWVzKHNpemUgPW5lcykpCiAgCmBgYAoKIyBUb3AgcHJvZ3JhbSAyIGdlbmVzIGV4cHJlc3Npb24gY29ycmVsYXRpb24KYGBge3J9CnRvcF9vdCA9IGdlcF9zY29yZXMgW29yZGVyKGdlcF9zY29yZXMgWywyXSxkZWNyZWFzaW5nID0gVCksMixkcm9wID0gRl0lPiUgaGVhZCgyMDApICU+JSByb3duYW1lcygpCgpudW1fb2ZfY2x1c3RlcnMgPSA3CmFubm90YXRpb24gPSBwbG90X2dlbmVzX2NvcihkYXRhc2V0ID0geGVubyxudW1fb2ZfY2x1c3RlcnMgPSBudW1fb2ZfY2x1c3RlcnMsZ2VuZUlkcyA9IHRvcF9vdCxoZWlnaHQgPSAzKQoKYGBgCgojICBwcm9ncmFtIDIgYWxsIGNsdXN0ZXJzIGV4cHJlc3Npb24gey50YWJzZXR9CmBgYHtyIHJlc3VsdHM9J2FzaXMnLGZpZy53aWR0aD0xNH0KZm9yIChjaG9zZW5fY2x1c3RlcnMgaW4gMTpudW1fb2ZfY2x1c3RlcnMpIHsKICBjaG9zZW5fZ2VuZXMgPSBhbm5vdGF0aW9uICU+JSBkcGx5cjo6ZmlsdGVyKGNsdXN0ZXIgPT0gY2hvc2VuX2NsdXN0ZXJzKSAlPiUgcm93bmFtZXMoKSAjdGFrZSByZWxldmFudCBnZW5lcwogICMgcHJpbnQoY2hvc2VuX2dlbmVzKQogIGh5cF9vYmogPC0gaHlwZVIoY2hvc2VuX2dlbmVzLCBnZW5lc2V0cywgdGVzdCA9ICJoeXBlcmdlb21ldHJpYyIsIGZkcj0xLCBwbG90dGluZz1GLGJhY2tncm91bmQgPSByb3duYW1lcyh4ZW5vXzVfZ2VwX3Njb3JlcykpCgogICBzY29yZXNBbmRJbmRpY2VzIDwtIGdldFBhdGh3YXlTY29yZXMoeGVub0Bhc3NheXMkUk5BQGRhdGEsIGNob3Nlbl9nZW5lcykKICB4ZW5vPUFkZE1ldGFEYXRhKHhlbm8sc2NvcmVzQW5kSW5kaWNlcyRwYXRod2F5U2NvcmVzLHBhc3RlMCgiY2x1c3RlciIsY2hvc2VuX2NsdXN0ZXJzKSkKCiAgCiAgcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICAgIGh5cF9kb3RzKGh5cF9vYmosc2l6ZV9ieSA9ICJub25lIix0aXRsZSA9IHBhc3RlMCgiY2x1c3RlciIsY2hvc2VuX2NsdXN0ZXJzKSkrCiAgICAgICAgICAgICAgRmVhdHVyZVBsb3Qob2JqZWN0ID0geGVubyxmZWF0dXJlcyA9IHBhc3RlMCgiY2x1c3RlciIsY2hvc2VuX2NsdXN0ZXJzKSksCiAgICAgICAgICAgIHRpdGxlID0gY2hvc2VuX2NsdXN0ZXJzKQp9CgoKYGBgCgojIENvcnJlbGF0aW9uIG9mIGNsdXN0ZXJzCmBgYHtyfQpmb3IgKGNob3Nlbl9jbHVzdGVycyBpbiAxOm51bV9vZl9jbHVzdGVycykgewogIAogIGNvcl9yZXMgPSBjb3IoeGVubyRUTkZhLHhlbm9bW3Bhc3RlMCgiY2x1c3RlciIsY2hvc2VuX2NsdXN0ZXJzKV1dKQpwcmludChwYXN0ZSgiY29ycmVsYXRpb24gb2YgVE5GYSBwcm9ncmFtIHRvIiwgcGFzdGUwKCJjbHVzdGVyIixjaG9zZW5fY2x1c3RlcnMpLCI6IiwgY29yX3JlcykpCgp9CmBgYAoKCmBgYHtyfQpjbHVzdGVyc19pZGVudHMgPSBjKCJjbHVzdGVyMSIsICJLRUdHX09YSURBVElWRV9QSE9TUEhPUllMQVRJT04iLCJIQUxMTUFSS19UTkZBX1NJR05BTElOR19WSUFfTkZLQiIsIktFR0dfQU5USUdFTl9QUk9DRVNTSU5HX0FORF9QUkVTRU5UQVRJT04iLCJIQUxMTUFSS19QNTNfUEFUSFdBWSIsImNsdXN0ZXI2IiwiY2x1c3RlcjciKQpgYGAKCgojICBwcm9ncmFtIDIgaW50ZXJzZWN0ZWQgZ2VuZXMKCgoKYGBge3J9CnByb2dyYW1zX29mX2NsdXN0ZXIgPSBjKCkKZm9yIChjaG9zZW5fY2x1c3RlcnMgaW4gMTpudW1fb2ZfY2x1c3RlcnMpIHsKICBjaG9zZW5fZ2VuZXMgPSBhbm5vdGF0aW9uW1sibXlhbm5vdGF0aW9uIl1dICU+JSBkcGx5cjo6ZmlsdGVyKGNsdXN0ZXIgPT0gY2hvc2VuX2NsdXN0ZXJzKSAlPiUgcm93bmFtZXMoKSAjdGFrZSByZWxldmFudCBnZW5lcwogIHBhdGh3YXlfbmFtZSA9IGNsdXN0ZXJzX2lkZW50c1tjaG9zZW5fY2x1c3RlcnNdCiAgaWYgKCFzdGFydHNXaXRoKHggPSBwYXRod2F5X25hbWUscHJlZml4ID0gImNsdXN0ZXIiKSl7CiAgICAgIGNob3Nlbl9nZW5lcyAgPSAoY2hvc2VuX2dlbmVzKSAlPiUgaW50ZXJzZWN0KGdlbmVzZXRzW1twYXRod2F5X25hbWVdXSkKICAgICAgcGF0aHdheV9uYW1lID0gcGFzdGUwKHBhdGh3YXlfbmFtZSwiX2NsdXN0ZXIiKQogIH0KICBwcm9ncmFtc19vZl9jbHVzdGVyID0gYyhwcm9ncmFtc19vZl9jbHVzdGVyLHBhdGh3YXlfbmFtZSkKICBwcmludChwYXRod2F5X25hbWUpCiAgcHJpbnQoY2hvc2VuX2dlbmVzKQogIGNhdCgiXG4iKQogIHNjb3Jlc0FuZEluZGljZXMgPC0gZ2V0UGF0aHdheVNjb3Jlcyh4ZW5vQGFzc2F5cyRSTkFAZGF0YSwgY2hvc2VuX2dlbmVzKQogIHhlbm89QWRkTWV0YURhdGEoeGVubyxzY29yZXNBbmRJbmRpY2VzJHBhdGh3YXlTY29yZXMscGF0aHdheV9uYW1lKQoKfQpgYGAKIyAgcHJvZ3JhbSAyIGludGVyc2VjdGVkIHBhdGh3YXkgcmVndWxhdGlvbiB7LnRhYnNldH0gICAgIAoKYGBge3IgIHJlc3VsdHM9J2FzaXMnfQptZXRhZ2VuZXNfbWVhbl9jb21wYXJlKGRhdGFzZXQgPSB4ZW5vLHRpbWUucG9pbnRfdmFyID0gInRyZWF0bWVudCIscHJlZml4ID0gIm1vZGVsIixwYXRpZW50LmlkZW50X3ZhciA9ICJvcmlnLmlkZW50IixwcmVfb24gPSBjKCJOVCIsIk9TSSIpLHRlc3QgPSAid2lsY294LnRlc3QiLHByb2dyYW1zID0gcHJvZ3JhbXNfb2ZfY2x1c3Rlcix3aXRob3V0X3NwbGl0ID0gRikKYGBgCiMgIHByb2dyYW0gMiBzaWduaWZpY2FudCBwbG90ICAKCmBgYHtyIGZpZy5oZWlnaHQ9MTJ9CnNpZ25mX3Bsb3RfcHJlX3ZzX29uPC0gZnVuY3Rpb24oZGF0YXNldCxwcm9ncmFtcyxwYXRpZW50LmlkZW50X3ZhcixwcmVmaXgscHJlX29uLHRlc3QsdGltZS5wb2ludF92YXIpIHsKICAgIGZpbmFsX2RmID0gZGF0YS5mcmFtZSgpCiAgICBmb3IgKG1ldGVnZW5lIGluIHByb2dyYW1zKSB7CiAgICAgIGdlbmVzX2J5X3RwID0gRmV0Y2hEYXRhKG9iamVjdCA9IGRhdGFzZXQsdmFycyA9IG1ldGVnZW5lKSAlPiUgcm93U3VtcygpICU+JSBhcy5kYXRhLmZyYW1lKCkgI21lYW4gZXhwcmVzc2lvbgogICAgICBuYW1lcyhnZW5lc19ieV90cClbMV0gPSBtZXRlZ2VuZQogICAgICBnZW5lc19ieV90cCA9IGNiaW5kKGdlbmVzX2J5X3RwLEZldGNoRGF0YShvYmplY3QgPSBkYXRhc2V0LHZhcnMgPSBjKHBhdGllbnQuaWRlbnRfdmFyLHRpbWUucG9pbnRfdmFyKSkpICMgYWRkIGlkIGFuZCB0aW1lIHBvaW50cwogICAgICAKICAgICAgCiAgICAgIGdlbmVzX2J5X3RwX2ZvclBsb3QgPSAgZ2VuZXNfYnlfdHAgJT4lIG11dGF0ZSghIWVuc3ltKHBhdGllbnQuaWRlbnRfdmFyKSA6PSBwYXN0ZShwcmVmaXgsZ2VuZXNfYnlfdHBbLHBhdGllbnQuaWRlbnRfdmFyXSkpICNhZGQgIm1vZGVsIiBiZWZvcmUgIGVhY2ggbW9kZWwvcGF0aWVudAogICAgICBmbSA8LSBhcy5mb3JtdWxhKHBhc3RlKG1ldGVnZW5lLCAifiIsIHRpbWUucG9pbnRfdmFyKSkgI21ha2UgZm9ybXVsYSB0byBwbG90CiAgICAgIAogICAgICAjcGxvdCBhbmQgc3BsaXQgYnkgcGF0aWVudDogICAKICAgICAgc3RhdC50ZXN0ID0gY29tcGFyZV9tZWFucyhmb3JtdWxhID0gZm0gLGRhdGEgPSBnZW5lc19ieV90cF9mb3JQbG90LG1ldGhvZCA9IHRlc3QsZ3JvdXAuYnkgPSBwYXRpZW50LmlkZW50X3ZhciklPiUgCiAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcihncm91cDEgPT0gcHJlX29uWzFdICYgZ3JvdXAyID09IHByZV9vblsyXSkgICNmaWx0ZXIgZm9yIHByZSB2cyBvbiB0cmVhdG1lbnQgb25seQogICAgICBmaW5hbF9kZiA9IHJiaW5kKGZpbmFsX2RmLHN0YXQudGVzdCkKICAgIH0KICAgIHJldHVybihmaW5hbF9kZikKfQoKdW5kZWJ1ZyhzaWduZl9wbG90X3ByZV92c19vbikKZmluYWxfZGYgPSBzaWduZl9wbG90X3ByZV92c19vbihkYXRhc2V0ID0geGVubyx0aW1lLnBvaW50X3ZhciA9ICJ0cmVhdG1lbnQiLHByZWZpeCA9ICJtb2RlbCIscGF0aWVudC5pZGVudF92YXIgPSAib3JpZy5pZGVudCIscHJlX29uID0gYygiTlQiLCJPU0kiKSx0ZXN0ID0gIndpbGNveC50ZXN0Iixwcm9ncmFtcyA9IHByb2dyYW1zX29mX2NsdXN0ZXIgKQpmaW5hbF9kZiA9IHJlc2hhcGUyOjpkY2FzdChmaW5hbF9kZiwgb3JpZy5pZGVudCAgfi55Lix2YWx1ZS52YXIgPSAicC5hZGoiKSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJvcmlnLmlkZW50IikKCnNpZ19oZWF0bWFwKGFsbF9wYXRpZW50c19yZXN1bHQgPSBmaW5hbF9kZix0aXRsZSA9ICJhZCIpCmBgYAojIFRvcCBwcm9ncmFtIDMgZ2VuZXMgZXhwcmVzc2lvbiBjb3JyZWxhdGlvbgpgYGB7cn0KdG9wX2h5cG94aWEgPSBnZXBfc2NvcmVzIFtvcmRlcihnZXBfc2NvcmVzIFssM10sZGVjcmVhc2luZyA9IFQpLDIsZHJvcCA9IEZdJT4lIGhlYWQoMjAwKSAlPiUgcm93bmFtZXMoKQoKbnVtX29mX2NsdXN0ZXJzID0gNAphbm5vdGF0aW9uID0gcGxvdF9nZW5lc19jb3IoZGF0YXNldCA9IHhlbm8saGFsbG1hcmtfbmFtZSA9IE5VTEwsbnVtX29mX2NsdXN0ZXJzID0gbnVtX29mX2NsdXN0ZXJzLGdlbmVJZHMgPSB0b3BfaHlwb3hpYSkKCmBgYAojICBwcm9ncmFtIDMgYWxsIGNsdXN0ZXJzIGV4cHJlc3Npb24gey50YWJzZXR9CgpgYGB7ciByZXN1bHRzPSdhc2lzJyxmaWcud2lkdGg9MTR9CmZvciAoY2hvc2VuX2NsdXN0ZXJzIGluIDE6bnVtX29mX2NsdXN0ZXJzKSB7CiAgY2hvc2VuX2dlbmVzID0gYW5ub3RhdGlvbltbIm15YW5ub3RhdGlvbiJdXSAlPiUgZHBseXI6OmZpbHRlcihjbHVzdGVyID09IGNob3Nlbl9jbHVzdGVycykgJT4lIHJvd25hbWVzKCkgI3Rha2UgcmVsZXZhbnQgZ2VuZXMKICAjIHByaW50KGNob3Nlbl9nZW5lcykKICBoeXBfb2JqIDwtIGh5cGVSKGNob3Nlbl9nZW5lcywgZ2VuZXNldHNfZW52LCB0ZXN0ID0gImh5cGVyZ2VvbWV0cmljIiwgZmRyPTEsIHBsb3R0aW5nPUYsYmFja2dyb3VuZCA9IHJvd25hbWVzKHhlbm9fNV9nZXBfc2NvcmVzKSkKCiAgIHNjb3Jlc0FuZEluZGljZXMgPC0gZ2V0UGF0aHdheVNjb3Jlcyh4ZW5vQGFzc2F5cyRSTkFAZGF0YSwgY2hvc2VuX2dlbmVzKQogIHhlbm89QWRkTWV0YURhdGEoeGVubyxzY29yZXNBbmRJbmRpY2VzJHBhdGh3YXlTY29yZXMscGFzdGUwKCJjbHVzdGVyIixjaG9zZW5fY2x1c3RlcnMpKQoKICAKICBwcmludF90YWIocGx0ID0gCiAgICAgICAgICAgICAgaHlwX2RvdHMoaHlwX29iaixzaXplX2J5ID0gIm5vbmUiLHRpdGxlID0gcGFzdGUwKCJjbHVzdGVyIixjaG9zZW5fY2x1c3RlcnMpKSsKICAgICAgICAgICAgICBGZWF0dXJlUGxvdChvYmplY3QgPSB4ZW5vLGZlYXR1cmVzID0gcGFzdGUwKCJjbHVzdGVyIixjaG9zZW5fY2x1c3RlcnMpKSwKICAgICAgICAgICAgdGl0bGUgPSBjaG9zZW5fY2x1c3RlcnMpCgoKfQoKCmBgYAoKIyBDb3JyZWxhdGlvbiBvZiBjbHVzdGVycwpgYGB7cn0KZm9yIChjaG9zZW5fY2x1c3RlcnMgaW4gMTpudW1fb2ZfY2x1c3RlcnMpIHsKICAKICBjb3JfcmVzID0gY29yKHhlbm8kaHlwb3hpYSx4ZW5vW1twYXN0ZTAoImNsdXN0ZXIiLGNob3Nlbl9jbHVzdGVycyldXSkKcHJpbnQocGFzdGUoImNvcnJlbGF0aW9uIG9mIGh5cG94aWEgcHJvZ3JhbSB0byIsIHBhc3RlMCgiY2x1c3RlciIsY2hvc2VuX2NsdXN0ZXJzKSwiOiIsIGNvcl9yZXMpKQoKfQpgYGAKCmBgYHtyfQpjbHVzdGVyc19pZGVudHMgPSBjKCJIQUxMTUFSS19IWVBPWElBIiwgIkhJRl90YXJnZXRzIiwiY2x1c3RlcjMiLCJjbHVzdGVyNCIpCmBgYAoKYGBge3J9CnByb2dyYW1zX29mX2NsdXN0ZXIgPSBjKCkKZm9yIChjaG9zZW5fY2x1c3RlcnMgaW4gMTpudW1fb2ZfY2x1c3RlcnMpIHsKICBjaG9zZW5fZ2VuZXMgPSBhbm5vdGF0aW9uW1sibXlhbm5vdGF0aW9uIl1dICU+JSBkcGx5cjo6ZmlsdGVyKGNsdXN0ZXIgPT0gY2hvc2VuX2NsdXN0ZXJzKSAlPiUgcm93bmFtZXMoKSAjdGFrZSByZWxldmFudCBnZW5lcwogIHBhdGh3YXlfbmFtZSA9IGNsdXN0ZXJzX2lkZW50c1tjaG9zZW5fY2x1c3RlcnNdCiAgaWYgKCFzdGFydHNXaXRoKHggPSBwYXRod2F5X25hbWUscHJlZml4ID0gImNsdXN0ZXIiKSl7CiAgICAgIGNob3Nlbl9nZW5lcyAgPSAoY2hvc2VuX2dlbmVzKSAlPiUgaW50ZXJzZWN0KGdlbmVzZXRzW1twYXRod2F5X25hbWVdXSkKICAgICAgcGF0aHdheV9uYW1lID0gcGFzdGUwKHBhdGh3YXlfbmFtZSwiX2NsdXN0ZXIiKQogIH0KICBwcm9ncmFtc19vZl9jbHVzdGVyID0gYyhwcm9ncmFtc19vZl9jbHVzdGVyLHBhdGh3YXlfbmFtZSkKICBwcmludChwYXRod2F5X25hbWUpCiAgcHJpbnQoY2hvc2VuX2dlbmVzKQogIGNhdCgiXG4iKQogIHNjb3Jlc0FuZEluZGljZXMgPC0gZ2V0UGF0aHdheVNjb3Jlcyh4ZW5vQGFzc2F5cyRSTkFAZGF0YSwgY2hvc2VuX2dlbmVzKQogIHhlbm89QWRkTWV0YURhdGEoeGVubyxzY29yZXNBbmRJbmRpY2VzJHBhdGh3YXlTY29yZXMscGF0aHdheV9uYW1lKQoKfQpgYGAKIyAgcHJvZ3JhbSAzIGludGVyc2VjdGVkIHBhdGh3YXkgcmVndWxhdGlvbiB7LnRhYnNldH0gICAgIAoKYGBge3IgcmVzdWx0cz0nYXNpcyd9Cm1ldGFnZW5lc19tZWFuX2NvbXBhcmUoZGF0YXNldCA9IHhlbm8sdGltZS5wb2ludF92YXIgPSAidHJlYXRtZW50IixwcmVmaXggPSAibW9kZWwiLHBhdGllbnQuaWRlbnRfdmFyID0gIm9yaWcuaWRlbnQiLHByZV9vbiA9IGMoIk5UIiwiT1NJIiksdGVzdCA9ICJ3aWxjb3gudGVzdCIscHJvZ3JhbXMgPSBwcm9ncmFtc19vZl9jbHVzdGVyLHdpdGhvdXRfc3BsaXQgPSBGKQpgYGAKCiMgVG9wIHByb2dyYW0gMyBnZW5lcyBleHByZXNzaW9uIGNvcnJlbGF0aW9uCgpgYGB7cn0KdG9wX2NjID0gZ2VwX3Njb3JlcyBbb3JkZXIoZ2VwX3Njb3JlcyBbLDRdLGRlY3JlYXNpbmcgPSBUKSwyLGRyb3AgPSBGXSU+JSBoZWFkKDIwMCkgJT4lIHJvd25hbWVzKCkKCm51bV9vZl9jbHVzdGVycyA9IDQKYW5ub3RhdGlvbiA9IHBsb3RfZ2VuZXNfY29yKGRhdGFzZXQgPSB4ZW5vLGhhbGxtYXJrX25hbWUgPSBOVUxMLG51bV9vZl9jbHVzdGVycyA9IG51bV9vZl9jbHVzdGVycyxnZW5lSWRzID0gdG9wX2NjKQoKYGBgCiMgIHByb2dyYW0gMyBhbGwgY2x1c3RlcnMgZXhwcmVzc2lvbiB7LnRhYnNldH0KCmBgYHtyIHJlc3VsdHM9J2FzaXMnLGZpZy53aWR0aD0xNH0KZm9yIChjaG9zZW5fY2x1c3RlcnMgaW4gMTpudW1fb2ZfY2x1c3RlcnMpIHsKICBjaG9zZW5fZ2VuZXMgPSBhbm5vdGF0aW9uW1sibXlhbm5vdGF0aW9uIl1dICU+JSBkcGx5cjo6ZmlsdGVyKGNsdXN0ZXIgPT0gY2hvc2VuX2NsdXN0ZXJzKSAlPiUgcm93bmFtZXMoKSAjdGFrZSByZWxldmFudCBnZW5lcwogICMgcHJpbnQoY2hvc2VuX2dlbmVzKQogIGh5cF9vYmogPC0gaHlwZVIoY2hvc2VuX2dlbmVzLCBnZW5lc2V0c19lbnYsIHRlc3QgPSAiaHlwZXJnZW9tZXRyaWMiLCBmZHI9MSwgcGxvdHRpbmc9RixiYWNrZ3JvdW5kID0gcm93bmFtZXMoeGVub181X2dlcF9zY29yZXMpKQoKICAgc2NvcmVzQW5kSW5kaWNlcyA8LSBnZXRQYXRod2F5U2NvcmVzKHhlbm9AYXNzYXlzJFJOQUBkYXRhLCBjaG9zZW5fZ2VuZXMpCiAgeGVubz1BZGRNZXRhRGF0YSh4ZW5vLHNjb3Jlc0FuZEluZGljZXMkcGF0aHdheVNjb3JlcyxwYXN0ZTAoImNsdXN0ZXIiLGNob3Nlbl9jbHVzdGVycykpCgogIAogIHByaW50X3RhYihwbHQgPSAKICAgICAgICAgICAgICBoeXBfZG90cyhoeXBfb2JqLHNpemVfYnkgPSAibm9uZSIsdGl0bGUgPSBwYXN0ZTAoImNsdXN0ZXIiLGNob3Nlbl9jbHVzdGVycykpKwogICAgICAgICAgICAgIEZlYXR1cmVQbG90KG9iamVjdCA9IHhlbm8sZmVhdHVyZXMgPSBwYXN0ZTAoImNsdXN0ZXIiLGNob3Nlbl9jbHVzdGVycykpLAogICAgICAgICAgICB0aXRsZSA9IGNob3Nlbl9jbHVzdGVycykKICAKfQoKCmBgYAoKPHNjcmlwdCBzcmM9Imh0dHBzOi8vaHlwb3RoZXMuaXMvZW1iZWQuanMiIGFzeW5jPjwvc2NyaXB0PgoKCgo=