Functions
library(stringi)
library(reticulate)
source_from_github(repositoy = "DEG_functions",version = "0.2.24")
source_from_github(repositoy = "cNMF_functions",version = "0.3.91",script_name = "cnmf_function_Harmony.R")
no_neg <- function(x) {
x = x + abs(min(x))
x
}
sum_2_one <- function(x) {
x =x/sum(x)
x
}
Data
xeno = readRDS("./Data/10x_xeno_1000.Rds")
lung = readRDS("./Data/lung_cancercells_withTP_onlyPatients.rds")
lung_patients = lung$patient.ident %>% unique() %>% as.character()
lung_patients_filtered = lung_patients[!(lung_patients %in% c("X1055new","X1099"))] # remove patients with less than 100 malignant cells
lung = subset(x = lung,subset = patient.ident %in% lung_patients_filtered)
Models 2K vargenes
import pickle
from cnmf import cNMF
f = open('./Data/cnmf/cnmf_objects/models_2Kvargenes_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/cNMF_models_Varnorm_Harmony_2Kvargenes.k_selection.png")
knitr::include_graphics(plot_path)

gep
scores for all NMF k’s
density_threshold = 0.1
usage_norm3, gep_scores3, _, _ = cnmf_obj.load_results(K=3, density_threshold=density_threshold)
usage_norm4, gep_scores4, _, _ = cnmf_obj.load_results(K=4, density_threshold=density_threshold)
usage_norm5, gep_scores5, _, _ = cnmf_obj.load_results(K=5, density_threshold=density_threshold)
gep_scores3 = py$gep_scores3
gep_scores4 = py$gep_scores4
gep_scores5 = py$gep_scores5
usage_norm3 = py$usage_norm3
usage_norm4 = py$usage_norm4
usage_norm5 = py$usage_norm5
all_usage_norm = list(usage_norm3 = usage_norm3, usage_norm4 = usage_norm4, usage_norm5 = usage_norm5)
for (usage_num in seq_along(all_usage_norm)) {
usage = all_usage_norm[[usage_num]]
#add each metagene to metadata
for (i in 1:ncol(usage)) {
metage_metadata = usage %>% dplyr::select(i)
xeno = AddMetaData(object = xeno,metadata = metage_metadata,col.name = paste0("gep",i))
}
if (usage_num==1) {
print_tab(FeaturePlot(object = xeno,features = paste0("gep",1:ncol(usage)),ncol = 2),title = usage_num)
}else{ print_tab(FeaturePlot(object = xeno,features = paste0("gep",1:ncol(usage)),ncol = 3),title = usage_num)}
}
1

2

3

NA
all NMF
k’s enrichment
all_gep_scores = list(gep_scores3 = gep_scores3, gep_scores4 = gep_scores4, gep_scores5 = gep_scores5)
genesets <- msigdb_download("Homo sapiens",category="H") %>% append( msigdb_download("Homo sapiens",category="C2",subcategory = "CP:KEGG"))
genesets[["HIF_targets"]] = hif_targets
genesets_env = gsets$new(genesets, name="my genesets", version="v1.0")
for (gep_num in seq_along(all_gep_scores)) {
gep = all_gep_scores[[gep_num]]
ranked_list = list()
for (col in (gep)) {
lst = col %>% setNames(rownames(gep)) %>% sort(decreasing = TRUE)
ranked_list %<>% append(list(lst))
}
names(ranked_list) = paste0("gep",1:ncol(gep))
hyp_obj <- hypeR(ranked_list, genesets_env, test="kstest", fdr=0.05, plotting=F,background = rownames(gep_scores3))
print_tab(
hyp_dots(hyp_obj,size_by = "significance",abrv = 100,merge = T)+
scale_color_continuous(low = "red", high = "black", guide = guide_colorbar(reverse = TRUE))
,title = gep_num)
}
1
Scale for ‘colour’ is already present. Adding another scale for
‘colour’, which will replace the existing scale.

2
Scale for ‘colour’ is already present. Adding another scale for
‘colour’, which will replace the existing scale.

3
Scale for ‘colour’ is already present. Adding another scale for
‘colour’, which will replace the existing scale.

NA
programs
enrichment
gep_scores = py$gep_scores3
usage_norm= py$usage_norm3
# or:
gep_scores = readRDS("/sci/labs/yotamd/lab_share/avishai.wizel/R_projects/EGFR/Data/cnmf/harmony_models_gep_scores.rds")
names (gep_scores) = c("Hypoxia","TNFa","Cell_cycle")
plt_list = list()
for (program in names (gep_scores)) {
p = ggplot(gep_scores, aes(x=!!ensym(program))) +
geom_density()+xlab(program)+
geom_vline(
aes(xintercept=sort(gep_scores[,program],TRUE)[200] ,color="top200"),
linetype="dashed", size=1)+
geom_vline(
aes(xintercept=sort(gep_scores[,program],TRUE)[100] ,color="top100"),
linetype="dashed", size=1)+
geom_vline(
aes(xintercept=sort(gep_scores[,program],TRUE)[50] ,color="top50"),
linetype="dashed", size=1)+
geom_vline(
aes(xintercept=sort(gep_scores[,program],TRUE)[150] ,color="top150"),
linetype="dashed", size=1)+
scale_color_manual(name = "top n genes", values = c(top200 = "blue",top100 = "red",top150 = "yellow",top50 = "green"))
plt_list[[program]] <- p
}
ggarrange(plotlist = plt_list)

Programs hallmark
enrichment
ntop = 150
plt_list = list()
hif_targets_set = data.frame(gs_name = "hif_targets",gene_symbol = hif_targets)
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),ntop) #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 = hif_targets_set)
plt_list[[i]] = res$plt
}
gridExtra::grid.arrange(grobs = plt_list)

program 2 all cp
ntop = 150
i = 2
top_genes = gep_scores %>% arrange(desc(gep_scores[i])) #sort by score a
top = head(rownames(top_genes),ntop) #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)
print(res$plt)

NA
NA
Program 2 all
msigdb
ntop = 150
i = 2
top_genes = gep_scores %>% arrange(desc(gep_scores[i])) #sort by score a
top = head(rownames(top_genes),ntop) #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 = all_pathways)
print(res$plt)
xeno = FindVariableFeatures(object = xeno,nfeatures = 2000)
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]
Test with expr after
harmony
import numpy as np
import scanpy as sc
expr_after_harmony = sc.read_h5ad('./Data/cnmf/xeno_Harmony_NoNeg_2Kvargenes.h5ad').to_df()
tpm = compute_tpm(expr_after_harmony)
cnmf_genes = expr_after_harmony.keys().to_list()
usage_by_calc = get_usage_from_score(counts=expr_after_harmony,tpm=tpm,genes=cnmf_genes,cnmf_obj=cnmf_obj,k=3)
Check if original cNMF
score is like the calculated score
usage_by_calc = py$usage_by_calc
usage_norm = py$usage_norm3
cor(usage_by_calc,usage_norm)
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=3)
/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_usage = py$usage_by_calc
programs expression
names (xeno_usage) = c("Hypoxia","TNFa","Cell_cycle")
#add each metagene to metadata
for (i in 1:ncol(xeno_usage)) {
metage_metadata = xeno_usage %>% dplyr::select(i)
xeno = AddMetaData(object = xeno,metadata = metage_metadata,col.name = names(xeno_usage)[i])
}
print_tab(plt = FeaturePlot(object = xeno,features = colnames(xeno_usage)),title = "umap expression")
umap expression

NA
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 = c("Hypoxia","TNFa","Cell_cycle"))
Hypoxia per patient

Hypoxia

TNFa per patient

TNFa

Cell_cycle per patient

Cell_cycle

NA
DotPlot(object = xeno, features = c("Hypoxia","TNFa","Cell_cycle"),group.by = 'treatment')

CC
signature
# get genes and plot cor heatmap
plot_genes_cor <- function(dataset,hallmark_name,num_of_clusters,geneIds = NULL) {
if(!is_null(geneIds)){
geneIds = geneIds
}
else{
geneIds= genesets[[hallmark_name]]@geneIds
}
hallmars_exp = FetchData(object = dataset,vars = c(geneIds))
hallmars_exp = hallmars_exp[,colSums(hallmars_exp[])>0] #remove no expression genes
hallmark_cor = cor(hallmars_exp)
pht1 = pheatmap(mat = hallmark_cor,silent = T)
# make annotations
num_of_clusters = num_of_clusters
clustering_distance = "euclidean"
myannotation = as.data.frame(cutree(pht1[["tree_row"]], k = num_of_clusters)) #split into k clusters
names(myannotation)[1] = "cluster"
myannotation$cluster = as.factor(myannotation$cluster)
palette1 <-brewer.pal(num_of_clusters, "Paired")
names(palette1) = unique(myannotation$cluster)
ann_colors = list (cluster = palette1)
annotation = list(ann_colors = ann_colors, myannotation = myannotation)
#choose colors
colors <- c(seq(-1,1,by=0.01))
my_palette <- c("blue",colorRampPalette(colors = c("blue", "white", "red"))
(n = length(colors)-3), "red")
print_tab(plt =
pheatmap(mat = hallmark_cor,annotation_col = annotation[["myannotation"]], annotation_colors = annotation[["ann_colors"]], clustering_distance_rows = clustering_distance,clustering_distance_cols = clustering_distance,color = my_palette,breaks = colors,show_rownames = F,show_colnames = F)
,title = "genes expression heatmap")
return(annotation)
}
hallmark_name = "HALLMARK_G2M_CHECKPOINT"
annotation = plot_genes_cor(dataset = xeno,hallmark_name = hallmark_name,num_of_clusters = 4)
Warning in FetchData.Seurat(object = dataset, vars = c(geneIds)) :
The following requested variables were not found: AC027237.1,
AC091021.1, PTTG3P ## genes expression heatmap {.unnumbered }

NA
#choose clusters
chosen_clusters = c(1,2)
print (paste("chosen_clusters = ", chosen_clusters %>% toString()))
1 “chosen_clusters = 1, 2”
#UMAP expression of signature
chosen_genes = annotation[["myannotation"]] %>% dplyr::filter(cluster %in% chosen_clusters) %>% rownames() #take relevant genes
score <- apply(xeno@assays$RNA@data[chosen_genes,],2,mean)
xeno=AddMetaData(xeno,score,hallmark_name)
scoresAndIndices <- getPathwayScores(xeno@assays$RNA@data, chosen_genes)
xeno=AddMetaData(xeno,scoresAndIndices$pathwayScores,paste0(hallmark_name,"_sipsic"))
print_tab(FeaturePlot(object = xeno, features = paste0(hallmark_name,"_sipsic")),title = "Expression")
Expression

print_tab(FeaturePlot(object = xeno, features = hallmark_name),title = "Expression")
Expression

NA
#plot signature distribution
cc_scores = FetchData(object = xeno,vars = "HALLMARK_G2M_CHECKPOINT")
plt = ggplot(cc_scores, aes(x=HALLMARK_G2M_CHECKPOINT)) +
geom_density()+
geom_vline(
aes(xintercept=mean(cc_scores$HALLMARK_G2M_CHECKPOINT) + sd(cc_scores$HALLMARK_G2M_CHECKPOINT) ,color="1 SD"),
linetype="dashed", size=1)+
geom_vline(
aes(xintercept=mean(cc_scores$HALLMARK_G2M_CHECKPOINT) + 2*sd(cc_scores$HALLMARK_G2M_CHECKPOINT) ,color="2 SD"),
linetype="dashed", size=1)
print_tab(plt = plt,title = "dist")
# Plot assignment
cc_scores = cc_scores %>% mutate(is_cycling = if_else(condition =
HALLMARK_G2M_CHECKPOINT > mean(HALLMARK_G2M_CHECKPOINT)
+sd(HALLMARK_G2M_CHECKPOINT),
true = "cycling",
false = "non_cycling"))
xeno = AddMetaData(object = xeno,metadata = cc_scores$is_cycling,col.name = "is_cycling")
print_tab(plt = DimPlot(object = xeno,group.by = "is_cycling") , title = "assignment 1 sd")
# cc_scores = cc_scores %>% mutate(is_cycling = if_else(condition =
# HALLMARK_G2M_CHECKPOINT > mean(HALLMARK_G2M_CHECKPOINT) + 2*sd(cc_scores$HALLMARK_G2M_CHECKPOINT),
# true = "cycling",
# false = "non_cycling"))
# xeno = AddMetaData(object = xeno,metadata = cc_scores$is_cycling,col.name = "is_cycling")
# print_tab(plt = DimPlot(object = xeno,group.by = "is_cycling") , title = "assignment 2 sd")
df = FetchData(object = xeno,vars = c("is_cycling","treatment")) %>%
filter (treatment %in% c("NT","OSI")) %>%
droplevels()
test = fisher.test(table(df))
library(ggstatsplot)
plt = ggbarstats(
df, is_cycling, treatment,
results.subtitle = FALSE,
subtitle = paste0(
"Fisher's exact test", ", p-value = ",
round(test$p.value,13))
)
print_tab(plt = plt,title = "fisher")
correlation to nmf
program
cor_res = cor(xeno$Cell_cycle,xeno[[hallmark_name]])
print(paste("correlation of TNFa program to", hallmark_name,":", cor_res))
[1] "correlation of TNFa program to HALLMARK_G2M_CHECKPOINT : 0.926264346933643"
cor_res = cor(xeno$Cell_cycle,xeno[[paste0(hallmark_name,"_sipsic")]])
print(paste("correlation of Cell_cycle program to", paste0(hallmark_name,"_sipsic"),":", cor_res))
[1] "correlation of Cell_cycle program to HALLMARK_G2M_CHECKPOINT_sipsic : 0.929443708586434"
TNFa
signature
hallmark_name = "HALLMARK_TNFA_SIGNALING_VIA_NFKB"
annotation = plot_genes_cor(dataset = xeno,hallmark_name = hallmark_name,num_of_clusters = 6)
Warning in FetchData.Seurat(object = dataset, vars = c(geneIds)) :
The following requested variables were not found: CCN1 ## genes
expression heatmap {.unnumbered }

NA
chosen_clusters = c(1,5)
print (paste("chosen_clusters = ", chosen_clusters %>% toString()))
1 “chosen_clusters = 1, 5”
#UMAP expression of signature
chosen_genes = annotation[["myannotation"]] %>% dplyr::filter(cluster %in% chosen_clusters) %>% rownames() #take relevant genes
score <- apply(xeno@assays$RNA@data[chosen_genes,],2,mean)
xeno=AddMetaData(xeno,score,hallmark_name)
scoresAndIndices <- getPathwayScores(xeno@assays$RNA@data, chosen_genes)
xeno=AddMetaData(xeno,scoresAndIndices$pathwayScores,paste0(hallmark_name,"_sipsic"))
print_tab(FeaturePlot(object = xeno, features = paste0(hallmark_name,"_sipsic")),title = "Expression")
Expression

print_tab(FeaturePlot(object = xeno, features = hallmark_name),title = "Expression")
Expression

NA
correlation to nmf
program
cor_res = cor(xeno$TNFa,xeno[[hallmark_name]])
print(paste("correlation of TNFa program to", hallmark_name,":", cor_res))
[1] "correlation of TNFa program to HALLMARK_TNFA_SIGNALING_VIA_NFKB : 0.471118461766633"
cor_res = cor(xeno$TNFa,xeno[[paste0(hallmark_name,"_sipsic")]])
print(paste("correlation of TNFa program to", paste0(hallmark_name,"_sipsic"),":", cor_res))
[1] "correlation of TNFa program to HALLMARK_TNFA_SIGNALING_VIA_NFKB_sipsic : 0.441336272152405"
cc_scores = FetchData(object = xeno,vars = hallmark_name)
plt = ggplot(cc_scores, aes(x=HALLMARK_TNFA_SIGNALING_VIA_NFKB)) +
geom_density()+
geom_vline(
aes(xintercept=mean(HALLMARK_TNFA_SIGNALING_VIA_NFKB) + sd(HALLMARK_TNFA_SIGNALING_VIA_NFKB) ,color="1 SD"),
linetype="dashed", size=1)+
geom_vline(
aes(xintercept=mean(HALLMARK_TNFA_SIGNALING_VIA_NFKB) + 2*sd(HALLMARK_TNFA_SIGNALING_VIA_NFKB) ,color="2 SD"),
linetype="dashed", size=1)+
geom_vline(
aes(xintercept=mean(HALLMARK_TNFA_SIGNALING_VIA_NFKB) ,color="mean"),
linetype="dashed", size=1)
print_tab(plt = plt,title = "dist")
weighted ks test
genesets <- msigdb_download("Homo sapiens",category="H") %>% append( msigdb_download("Homo sapiens",category="C2",subcategory = "CP:KEGG"))
genesets[["HIF_targets"]] = hif_targets
genesets = gsets$new(genesets, name="my geneset", version="v1.0")
ranked_list1 = gep_scores %>% select(1) %>% as_vector() %>% setNames(rownames(gep_scores)) %>% sort(decreasing = TRUE)
ranked_list2 = gep_scores %>% select(2) %>% as_vector() %>% setNames(rownames(gep_scores)) %>% sort(decreasing = TRUE)
ranked_list3 = gep_scores %>% select(3) %>% as_vector() %>% setNames(rownames(gep_scores)) %>% sort(decreasing = TRUE)
ranked_list = list("gep1" = ranked_list1, "gep2" = ranked_list2,"gep3" = ranked_list3)
hyp_obj <- hypeR(ranked_list, genesets, test="kstest", fdr=0.05, plotting=F,background = rownames(gep_scores))
plots = hyp_dots(hyp_obj,size_by = "none")
print_tab(plt = plots[[1]],title = "GEP 1")
GEP 1

print_tab(plt = plots[[2]],title = "GEP 2")
GEP 2

print_tab(plt = plots[[3]],title = "GEP 3")
GEP 3

NA
APC
signature
hallmark_name = "KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION"
annotation = plot_genes_cor(dataset = xeno,hallmark_name = hallmark_name,num_of_clusters = 4,canocical = T)
genes expression heatmap

NA
chosen_clusters = c(1,3)
print (paste("chosen_clusters = ", chosen_clusters %>% toString()))
1 “chosen_clusters = 1, 3”
#UMAP expression of signature
chosen_genes = annotation[["myannotation"]] %>% dplyr::filter(cluster %in% chosen_clusters) %>% rownames() #take relevant genes
score <- apply(xeno@assays$RNA@data[chosen_genes,],2,mean)
xeno=AddMetaData(xeno,score,hallmark_name)
scoresAndIndices <- getPathwayScores(xeno@assays$RNA@data, chosen_genes)
xeno=AddMetaData(xeno,scoresAndIndices$pathwayScores,paste0(hallmark_name,"_sipsic"))
print_tab(FeaturePlot(object = xeno, features = paste0(hallmark_name,"_sipsic")),title = "Expression")
Expression

print_tab(FeaturePlot(object = xeno, features = hallmark_name),title = "Expression")
Expression

NA
correlatino to
nmf
cor_res = cor(xeno$TNFa,xeno[[hallmark_name]])
print(paste("correlation of TNFa program to", hallmark_name,":", cor_res))
[1] "correlation of TNFa program to KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION : 0.396759488432559"
cor_res = cor(xeno$TNFa,xeno[[paste0(hallmark_name,"_sipsic")]])
print(paste("correlation of TNFa program to", paste0(hallmark_name,"_sipsic"),":", cor_res))
[1] "correlation of TNFa program to KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION_sipsic : 0.333142851072643"
Signatures regulation
metagenes_mean_compare(dataset = xeno,time.point_var = "treatment",prefix = "model",patient.ident_var = "orig.ident",pre_on = c("NT","OSI","res"),programs = c("TNFa","HALLMARK_TNFA_SIGNALING_VIA_NFKB","KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION"))
TNFa per patient

TNFa

HALLMARK_TNFA_SIGNALING_VIA_NFKB per patient

HALLMARK_TNFA_SIGNALING_VIA_NFKB

KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION per
patient

KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION

NA
intersection
top_ot = gep_scores3[order(gep_scores3[,2],decreasing = T),2,drop = F]%>% head(200) %>% rownames()
hallmark_name = "KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION"
geneIds= genesets[[hallmark_name]]
chosen_genes = intersect(geneIds,top_ot)
print(chosen_genes)
1 “CD74” “HLA-DMA” “HLA-DPA1” “HLA-DPB1”
“HLA-DRA” “HLA-DRB1” “HLA-DRB5”
score <- apply(xeno@assays$RNA@data[chosen_genes,],2,mean)
xeno=AddMetaData(xeno,score,paste0(hallmark_name,"intersected"))
scoresAndIndices <- getPathwayScores(xeno@assays$RNA@data, chosen_genes)
xeno=AddMetaData(xeno,scoresAndIndices$pathwayScores,paste0(hallmark_name,"_intersected_sipsic"))
print_tab(FeaturePlot(object = xeno, features = paste0(hallmark_name,"_intersected_sipsic")),title = "Expression")
Expression
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

print_tab(FeaturePlot(object = xeno, features = paste0(hallmark_name,"intersected")),title = "Expression")
Expression

NA
cor_res = cor(xeno$TNFa,xeno[[paste0(hallmark_name,"intersected")]])
print(paste("correlation of TNFa program to", paste0(hallmark_name,"intersected"),":", cor_res))
[1] "correlation of TNFa program to KEGG_ANTIGEN_PROCESSING_AND_PRESENTATIONintersected : 0.536876281844422"
top_ot = gep_scores3[order(gep_scores3[,2],decreasing = T),2,drop = F]%>% head(200) %>% rownames()
hallmark_name = "HALLMARK_TNFA_SIGNALING_VIA_NFKB"
geneIds= genesets[[hallmark_name]]
chosen_genes = intersect(geneIds,top_ot)
print(chosen_genes)
[1] "ATF3" "BCL6" "BTG2" "CEBPD" "CXCL2" "EGR1" "EGR2" "FOS" "FOSB" "GADD45B" "IER2" "JUN"
[13] "JUNB" "KLF2" "NR4A1" "NR4A2" "NR4A3" "REL" "RHOB" "SAT1" "SOCS3" "ZFP36"
score <- apply(xeno@assays$RNA@data[chosen_genes,],2,mean)
xeno=AddMetaData(xeno,score,paste0(hallmark_name,"intersected"))
scoresAndIndices <- getPathwayScores(xeno@assays$RNA@data, chosen_genes)
xeno=AddMetaData(xeno,scoresAndIndices$pathwayScores,paste0(hallmark_name,"_intersected_sipsic"))
print_tab(FeaturePlot(object = xeno, features = paste0(hallmark_name,"_intersected_sipsic")),title = "Expression")
## Expression {.unnumbered }

print_tab(FeaturePlot(object = xeno, features = paste0(hallmark_name,"intersected")),title = "Expression")
## Expression {.unnumbered }

NA
cor_res = cor(xeno$TNFa,xeno[[paste0(hallmark_name,"intersected")]])
print(paste("correlation of TNFa program to", paste0(hallmark_name,"intersected"),":", cor_res))
[1] "correlation of TNFa program to HALLMARK_TNFA_SIGNALING_VIA_NFKBintersected : 0.488904752032433"
Norm
pathways
sum_scores= xeno$HIF_targets+xeno$KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION+xeno$HALLMARK_G2M_CHECKPOINT
new_score = xeno$KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION/ sum_scores
xeno %<>% AddMetaData(metadata = new_score,col.name = "KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION_norm")
new_score = (xeno$HIF_targets/ sum_scores)%>% replace(is.na(.), 0)
xeno %<>% AddMetaData(metadata = new_score,col.name = "HIF_targets_norm")
new_score = xeno$HALLMARK_G2M_CHECKPOINT/ sum_scores
xeno %<>% AddMetaData(metadata = new_score,col.name = "HALLMARK_G2M_norm")
FeaturePlot(object = xeno,features = c("HALLMARK_G2M_norm", "KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION_norm","HIF_targets_norm"))

metagenes_mean_compare(dataset = xeno,time.point_var = "treatment",prefix = "model",patient.ident_var = "orig.ident",pre_on = c("NT","OSI","res"),programs = c("TNFa","HALLMARK_TNFA_SIGNALING_VIA_NFKB","KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION","KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION_norm"))
TNFa per patient

TNFa

HALLMARK_TNFA_SIGNALING_VIA_NFKB per patient

HALLMARK_TNFA_SIGNALING_VIA_NFKB

KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION per
patient

KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION

KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION_norm per
patient

KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION_norm

NA
program 2
clustering
num_of_clusters = 7
annotation = plot_genes_cor(dataset = xeno,hallmark_name = NULL,num_of_clusters = num_of_clusters,geneIds = top_ot)
## genes expression heatmap {.unnumbered }

NA
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(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)
cor_res = cor(xeno$TNFa,xeno[[paste0("cluster",chosen_clusters)]])
# print(paste("correlation of TNFa program to", paste0("cluster",chosen_clusters),":", cor_res))
}
1

2

3

4

5

6

7

NA
correlation of all
clusters
for (chosen_clusters in 1:num_of_clusters) {
chosen_genes = annotation[["myannotation"]] %>% dplyr::filter(cluster == chosen_clusters) %>% rownames() #take relevant genes
cor_res = cor(xeno$TNFa,xeno[[paste0("cluster",chosen_clusters)]])
print(paste("correlation of TNFa program to", paste0("cluster",chosen_clusters),":", cor_res))
}
[1] "correlation of TNFa program to cluster1 : 0.196505816390455"
[1] "correlation of TNFa program to cluster2 : 0.366147384813019"
[1] "correlation of TNFa program to cluster3 : 0.532136383102456"
[1] "correlation of TNFa program to cluster4 : 0.534315691094927"
[1] "correlation of TNFa program to cluster5 : 0.697969009217511"
[1] "correlation of TNFa program to cluster6 : 0.326695267801052"
[1] "correlation of TNFa program to cluster7 : 0.0360547219515362"
clusters 4 and 5
chosen_clusters = c(4,5)
chosen_genes = annotation[["myannotation"]] %>% dplyr::filter(cluster %in% chosen_clusters) %>% rownames() #take relevant genes
hyp_obj <- hypeR(chosen_genes, genesets_env, test = "hypergeometric", fdr=1, plotting=F,background = rownames(gep_scores))
scoresAndIndices <- getPathwayScores(xeno@assays$RNA@data, chosen_genes)
score_name = paste0("cluster_", paste0(chosen_clusters, collapse = "_"))
xeno=AddMetaData(xeno,scoresAndIndices$pathwayScores,score_name)
hyp_dots(hyp_obj,size_by = "none")+
FeaturePlot(object = xeno,features = score_name)

cor_res = cor(xeno$TNFa,xeno[[score_name]])
print(paste("correlation of TNFa program to", score_name,":", cor_res))
[1] "correlation of TNFa program to cluster_4_5 : 0.754961916533887"
Title
library(biomaRt)
ensembl = useEnsembl(biomart="ensembl", dataset="hsapiens_gene_ensembl")
for (chosen_clusters in 1:num_of_clusters) {
chosen_genes = annotation[["myannotation"]] %>% dplyr::filter(cluster == chosen_clusters) %>% rownames() #take relevant genes
genedesc <- getBM(attributes=c('external_gene_name','wikigene_description',"interpro_description"), filters = 'external_gene_name', values = chosen_genes, mart =ensembl)
print_tab(genedesc,title = chosen_clusters)
}
Programs regulation
metagenes_mean_compare(dataset = xeno,time.point_var = "treatment",prefix = "model",patient.ident_var = "orig.ident",pre_on = c("NT","OSI","res"),programs = c("TNFa","HALLMARK_TNFA_SIGNALING_VIA_NFKB","KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION","KEGG_ANTIGEN_PROCESSING_AND_PRESENTATIONintersected","cluster_4_5","cluster7"))
TNFa per patient

TNFa

HALLMARK_TNFA_SIGNALING_VIA_NFKB per patient

HALLMARK_TNFA_SIGNALING_VIA_NFKB

KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION per
patient

KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION

KEGG_ANTIGEN_PROCESSING_AND_PRESENTATIONintersected
per patient

KEGG_ANTIGEN_PROCESSING_AND_PRESENTATIONintersected

cluster_4_5 per patient

cluster_4_5

cluster7 per patient

cluster7

NA
HIF_targets- Hypoxia correlation
# plot correlation for every subset of hif targets
for (genes in list(hif_targets,xeno_cluster_3_genes,xeno_cluster_3_2_genes)) {
hif_targets_by_tp = FetchData(object = xeno,vars = c(genes)) %>% rowSums() %>% as.data.frame() #mean expression
hif_targets_by_tp[,2] = xeno$Hypoxia
names(hif_targets_by_tp) = c("hif_targets","hypoxia_program")
p1 = ggplot(hif_targets_by_tp, aes(x=hif_targets, y=hypoxia_program)) +
geom_point()+
geom_density_2d(aes(color = ..level..)) +
geom_smooth(method=lm) +
stat_cor(method = "pearson", label.x = 20, label.y = 1.1)+
scale_color_viridis_c()
p2 = ggplot(hif_targets_by_tp, aes(x=hif_targets, y=hypoxia_program)) +
geom_bin2d() +
theme_bw()+ scale_fill_gradientn(limits=c(0,1100), breaks=seq(0, 1100, by=200), colours=c("blue","yellow","red"))+
stat_cor(method = "pearson", label.x = 20, label.y = 1.1)+
geom_smooth(method=lm)
p = ggarrange(plotlist = list(p1,p2),nrow = 2)
print_tab(plt = p,title = "geom_bin2d")
}
UMAPS
hif_targets_by_tp = FetchData(object = xeno,vars = c(hif_targets)) %>% rowSums() %>% as.data.frame() #mean expression
hif_targets_by_tp[,2] = xeno$Hypoxia
names(hif_targets_by_tp) = c("hif_targets","hypoxia_program")
high_hif_low_hypoxia_cells = hif_targets_by_tp %>% filter(hif_targets>25 & hypoxia_program < 0.2) %>% rownames()
low_hif_high_hypoxia_cells = hif_targets_by_tp %>% filter(hif_targets<15 & hypoxia_program > 0.6) %>% rownames()
hif_targets_by_tp = FetchData(object = xeno,vars = c(hif_targets)) %>% rowSums() %>% as.data.frame() #mean expression
xeno = AddMetaData(object = xeno, metadata = hif_targets_by_tp,col.name = "HIF_targets_score")
cells_to_highlight = list(high_hif_low_hypoxia_cells = high_hif_low_hypoxia_cells, low_hif_high_hypoxia_cells = low_hif_high_hypoxia_cells)
DimPlot(object = xeno, cells.highlight = cells_to_highlight, cols.highlight = c("red","blue"), cols = "gray", order = TRUE)
FeaturePlot(object = xeno,features = c( "HIF_targets_score","Hypoxia","Cell_cycle" ))
hif_hypoxia_correlated_cells = colnames(xeno) [!colnames(xeno) %in% high_hif_low_hypoxia_cells & !colnames(xeno) %in% high_hif_low_hypoxia_cells]
hif_targets_score = FetchData(object = xeno,vars = c(hif_targets)) %>% rowSums() %>% as.data.frame() #mean expression
hif_targets_score[,2] = xeno$Hypoxia
names(hif_targets_score) = c("hif_targets","hypoxia_program")
hif_targets_score = hif_targets_score %>% mutate(type = case_when(hif_targets>25 & hypoxia_program < 0.2 ~ "high_hif_low_hypoxia",
hif_targets<15 & hypoxia_program > 0.6 ~ "low_hif_high_hypoxia",
hif_targets>25 & hypoxia_program > 0.6 ~ "high_hif_high_hypoxia",
hif_targets<15 & hypoxia_program <0.2 ~ "high_hif_high_hypoxia",
TRUE ~ "other"))
xeno = AddMetaData(object = xeno,metadata = hif_targets_score[,"type", drop = F],col.name = "score_correlation")
xeno = SetIdent(object = xeno,value = "score_correlation")
DimPlot(object = xeno,group.by = "score_correlation")
markers = FindMarkers(object = xeno, ident.1 = "high_hif_low_hypoxia",ident.2 = "high_hif_high_hypoxia",densify = T)
updeg = markers %>% filter(p_val_adj<0.05 & avg_log2FC>0) %>% rownames()
new_hif_targets = hif_targets[!hif_targets %in% updeg]
hif_targets_by_tp = FetchData(object = xeno,vars = c(new_hif_targets)) %>% rowSums() %>% as.data.frame() #mean expression
hif_targets_by_tp[,2] = xeno$Hypoxia
names(hif_targets_by_tp) = c("hif_targets","hypoxia_program")
p1 = ggplot(hif_targets_by_tp, aes(x=hif_targets, y=hypoxia_program)) +
geom_point()+
geom_density_2d(aes(color = ..level..)) +
geom_smooth(method=lm) +
stat_cor(method = "pearson", label.x = 20, label.y = 1.1)+
scale_color_viridis_c()
p2 = ggplot(hif_targets_by_tp, aes(x=hif_targets, y=hypoxia_program)) +
geom_bin2d() +
theme_bw()+ scale_fill_gradientn(limits=c(0,1100), breaks=seq(0, 1100, by=200), colours=c("blue","yellow","red"))+
stat_cor(method = "pearson", label.x = 20, label.y = 1.1)+
geom_smooth(method=lm)
p = ggarrange(plotlist = list(p1,p2),nrow = 2)
print_tab(plt = p,title = "geom_bin2d")
upreg_hif_targets = hif_targets[hif_targets %in% updeg]
upreg_hif_targets_expr = FetchData(object = xeno,vars = c(upreg_hif_targets)) %>% rowSums() %>% as.data.frame() #mean expression
xeno = AddMetaData(object = xeno, metadata = upreg_hif_targets_expr,col.name = "upreg_hif_targets_score")
FeaturePlot(object = xeno,features = "upreg_hif_targets_score")
Calculate usage
without cc in sum to 1
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=3,sumTo1=False)
all_metagenes_noSumTo1 = py$usage_by_calc
tnf_and_hypoxia = all_metagenes_noSumTo1[,1:2]
tnf_and_hypoxia = apply(X = tnf_and_hypoxia, MARGIN = 1, sum_2_one) %>% t() %>% as.data.frame()
tnf_and_hypoxia[is.na(tnf_and_hypoxia)] <- 0 #replace NAN's with 0.
# plot correlation for every subset of hif targets
for (genes in list(hif_targets,xeno_cluster_3_genes,xeno_cluster_3_2_genes)) {
hif_targets_by_tp = FetchData(object = xeno,vars = c(genes)) %>% rowSums() %>% as.data.frame() #mean expression
hif_targets_by_tp[,2] = tnf_and_hypoxia[,1]
# hif_targets_by_tp[,2] = xeno$Hypoxia
names(hif_targets_by_tp) = c("hif_targets","hypoxia_program")
p1 = ggplot(hif_targets_by_tp, aes(x=hif_targets, y=hypoxia_program)) +
geom_point()+
geom_density_2d(aes(color = ..level..)) +
geom_smooth(method=lm) +
stat_cor(method = "pearson", label.x = 20, label.y = 1.1)+
scale_color_viridis_c()
p2 = ggplot(hif_targets_by_tp, aes(x=hif_targets, y=hypoxia_program)) +
geom_bin2d() +
theme_bw()+ scale_fill_gradientn(limits=c(0,1100), breaks=seq(0, 1100, by=200), colours=c("blue","yellow","red"))+
stat_cor(method = "pearson", label.x = 20, label.y = 1.1)+
geom_smooth(method=lm)
p = ggarrange(plotlist = list(p1,p2),nrow = 2)
print_tab(plt = p,title = "geom_bin2d")
}
UMAPS
hif_targets_by_tp = FetchData(object = xeno,vars = c(hif_targets)) %>% rowSums() %>% as.data.frame() #mean expression
hif_targets_by_tp[,2] = tnf_and_hypoxia[,1]
names(hif_targets_by_tp) = c("hif_targets","hypoxia_program")
high_hif_low_hypoxia_cells = hif_targets_by_tp %>% filter(hif_targets>25 & hypoxia_program < 0.2) %>% rownames()
hif_targets_by_tp = FetchData(object = xeno,vars = c(hif_targets)) %>% rowSums() %>% as.data.frame() #mean expression
xeno = AddMetaData(object = xeno, metadata = hif_targets_by_tp,col.name = "HIF_targets_score")
xeno = AddMetaData(object = xeno, metadata = tnf_and_hypoxia[,1],col.name = "Hypoxia2")
DimPlot(object = xeno, cells.highlight = high_hif_low_hypoxia_cells, cols.highlight = "red", cols = "gray", order = TRUE)
FeaturePlot(object = xeno,features = c( "HIF_targets_score","Hypoxia2","Cell_cycle" ))
FeaturePlot(object = xeno,features = c("Hypoxia2"))
DimPlot(object = xeno,group.by = "orig.ident")
Hypoxia raw
xeno = AddMetaData(object = xeno,metadata = all_metagenes_noSumTo1[,1],col.name = "hypoxia_raw")
FeaturePlot(object = xeno,features = "hypoxia_raw") + scale_color_gradientn(colours = rainbow(5), limits = c(0, 3000))
LS0tCnRpdGxlOiAnYHIgcnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLHJlcGxhY2VtZW50ID0gIiIpYCcgCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19jb2xsYXBzZTogeWVzCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvY19kZXB0aDogMQotLS0KCgoKIyBGdW5jdGlvbnMKYGBge3Igd2FybmluZz1GQUxTRX0KCmxpYnJhcnkoc3RyaW5naSkKbGlicmFyeShyZXRpY3VsYXRlKQpzb3VyY2VfZnJvbV9naXRodWIocmVwb3NpdG95ID0gIkRFR19mdW5jdGlvbnMiLHZlcnNpb24gPSAiMC4yLjI0IikKc291cmNlX2Zyb21fZ2l0aHViKHJlcG9zaXRveSA9ICJjTk1GX2Z1bmN0aW9ucyIsdmVyc2lvbiA9ICIwLjMuOTEiLHNjcmlwdF9uYW1lID0gImNubWZfZnVuY3Rpb25fSGFybW9ueS5SIikKCm5vX25lZyA8LSBmdW5jdGlvbih4KSB7CiAgeCA9IHggKyBhYnMobWluKHgpKQogIHgKfQoKc3VtXzJfb25lIDwtIGZ1bmN0aW9uKHgpIHsKICB4ID14L3N1bSh4KQogIHgKfQoKYGBgCgoKCgojIERhdGEKYGBge3J9Cnhlbm8gPSByZWFkUkRTKCIuL0RhdGEvMTB4X3hlbm9fMTAwMC5SZHMiKQpsdW5nID0gcmVhZFJEUygiLi9EYXRhL2x1bmdfY2FuY2VyY2VsbHNfd2l0aFRQX29ubHlQYXRpZW50cy5yZHMiKQpsdW5nX3BhdGllbnRzID0gbHVuZyRwYXRpZW50LmlkZW50ICU+JSB1bmlxdWUoKSAlPiUgYXMuY2hhcmFjdGVyKCkKbHVuZ19wYXRpZW50c19maWx0ZXJlZCA9IGx1bmdfcGF0aWVudHNbIShsdW5nX3BhdGllbnRzICVpbiUgYygiWDEwNTVuZXciLCJYMTA5OSIpKV0gIyByZW1vdmUgcGF0aWVudHMgd2l0aCBsZXNzIHRoYW4gMTAwIG1hbGlnbmFudCBjZWxscwpsdW5nID0gc3Vic2V0KHggPSBsdW5nLHN1YnNldCA9IHBhdGllbnQuaWRlbnQgJWluJSBsdW5nX3BhdGllbnRzX2ZpbHRlcmVkKQpgYGAKCiMgTW9kZWxzIDJLIHZhcmdlbmVzIAoKYGBge3B5dGhvbn0KaW1wb3J0IHBpY2tsZQpmcm9tIGNubWYgaW1wb3J0IGNOTUYKZiA9IG9wZW4oJy4vRGF0YS9jbm1mL2NubWZfb2JqZWN0cy9tb2RlbHNfMkt2YXJnZW5lc19jbm1mX29iai5wY2tsJywgJ3JiJykKY25tZl9vYmogPSBwaWNrbGUubG9hZChmKQpmLmNsb3NlKCkKYGBgCgojIEsgc2VsZWN0aW9uIHBsb3QKYGBge3IgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9Mn0KcGxvdF9wYXRoID0gcGFzdGUwKCIvc2NpL2xhYnMveW90YW1kL2xhYl9zaGFyZS9hdmlzaGFpLndpemVsL1JfcHJvamVjdHMvRUdGUi9EYXRhL2NubWYvY05NRl9tb2RlbHNfVmFybm9ybV9IYXJtb255XzJLdmFyZ2VuZXMvY05NRl9tb2RlbHNfVmFybm9ybV9IYXJtb255XzJLdmFyZ2VuZXMua19zZWxlY3Rpb24ucG5nIikKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGxvdF9wYXRoKQpgYGAKCgoKIyBnZXAgc2NvcmVzIGZvciBhbGwgTk1GIGsncyB7LnRhYnNldH0KYGBge3B5dGhvbn0KZGVuc2l0eV90aHJlc2hvbGQgPSAwLjEKdXNhZ2Vfbm9ybTMsIGdlcF9zY29yZXMzLCBfLCBfID0gY25tZl9vYmoubG9hZF9yZXN1bHRzKEs9MywgZGVuc2l0eV90aHJlc2hvbGQ9ZGVuc2l0eV90aHJlc2hvbGQpCnVzYWdlX25vcm00LCBnZXBfc2NvcmVzNCwgXywgXyA9IGNubWZfb2JqLmxvYWRfcmVzdWx0cyhLPTQsIGRlbnNpdHlfdGhyZXNob2xkPWRlbnNpdHlfdGhyZXNob2xkKQp1c2FnZV9ub3JtNSwgZ2VwX3Njb3JlczUsIF8sIF8gPSBjbm1mX29iai5sb2FkX3Jlc3VsdHMoSz01LCBkZW5zaXR5X3RocmVzaG9sZD1kZW5zaXR5X3RocmVzaG9sZCkKYGBgCgpgYGB7cn0KZ2VwX3Njb3JlczMgPSBweSRnZXBfc2NvcmVzMwpnZXBfc2NvcmVzNCA9IHB5JGdlcF9zY29yZXM0CmdlcF9zY29yZXM1ID0gcHkkZ2VwX3Njb3JlczUKCnVzYWdlX25vcm0zID0gcHkkdXNhZ2Vfbm9ybTMKdXNhZ2Vfbm9ybTQgPSBweSR1c2FnZV9ub3JtNAp1c2FnZV9ub3JtNSA9IHB5JHVzYWdlX25vcm01CgpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEwLCByZXN1bHRzPSdhc2lzJ30KYWxsX3VzYWdlX25vcm0gPSBsaXN0KHVzYWdlX25vcm0zID0gdXNhZ2Vfbm9ybTMsIHVzYWdlX25vcm00ID0gdXNhZ2Vfbm9ybTQsIHVzYWdlX25vcm01ID0gdXNhZ2Vfbm9ybTUpCmZvciAodXNhZ2VfbnVtIGluIHNlcV9hbG9uZyhhbGxfdXNhZ2Vfbm9ybSkpIHsKICB1c2FnZSA9IGFsbF91c2FnZV9ub3JtW1t1c2FnZV9udW1dXQogICNhZGQgZWFjaCBtZXRhZ2VuZSB0byBtZXRhZGF0YQogIGZvciAoaSBpbiAxOm5jb2wodXNhZ2UpKSB7CiAgICBtZXRhZ2VfbWV0YWRhdGEgPSB1c2FnZSAlPiUgZHBseXI6OnNlbGVjdChpKQogICAgeGVubyA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IHhlbm8sbWV0YWRhdGEgPSBtZXRhZ2VfbWV0YWRhdGEsY29sLm5hbWUgPSBwYXN0ZTAoImdlcCIsaSkpCiAgfQogIGlmICh1c2FnZV9udW09PTEpIHsKICAgICAgcHJpbnRfdGFiKEZlYXR1cmVQbG90KG9iamVjdCA9IHhlbm8sZmVhdHVyZXMgPSBwYXN0ZTAoImdlcCIsMTpuY29sKHVzYWdlKSksbmNvbCA9IDIpLHRpdGxlID0gdXNhZ2VfbnVtKQoKICB9ZWxzZXsgcHJpbnRfdGFiKEZlYXR1cmVQbG90KG9iamVjdCA9IHhlbm8sZmVhdHVyZXMgPSBwYXN0ZTAoImdlcCIsMTpuY29sKHVzYWdlKSksbmNvbCA9IDMpLHRpdGxlID0gdXNhZ2VfbnVtKX0KfQpgYGAKIyBhbGwgTk1GIGsncyAgZW5yaWNobWVudHsudGFic2V0fQoKCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQoKYWxsX2dlcF9zY29yZXMgPSAgbGlzdChnZXBfc2NvcmVzMyA9IGdlcF9zY29yZXMzLCBnZXBfc2NvcmVzNCA9IGdlcF9zY29yZXM0LCBnZXBfc2NvcmVzNSA9IGdlcF9zY29yZXM1KQoKZ2VuZXNldHMgPC0gbXNpZ2RiX2Rvd25sb2FkKCJIb21vIHNhcGllbnMiLGNhdGVnb3J5PSJIIikgJT4lIGFwcGVuZCggbXNpZ2RiX2Rvd25sb2FkKCJIb21vIHNhcGllbnMiLGNhdGVnb3J5PSJDMiIsc3ViY2F0ZWdvcnkgPSAiQ1A6S0VHRyIpKQpnZW5lc2V0c1tbIkhJRl90YXJnZXRzIl1dID0gaGlmX3RhcmdldHMKZ2VuZXNldHNfZW52ID0gZ3NldHMkbmV3KGdlbmVzZXRzLCBuYW1lPSJteSBnZW5lc2V0cyIsIHZlcnNpb249InYxLjAiKQoKZm9yIChnZXBfbnVtIGluIHNlcV9hbG9uZyhhbGxfZ2VwX3Njb3JlcykpIHsKICBnZXAgPSBhbGxfZ2VwX3Njb3Jlc1tbZ2VwX251bV1dCiAgcmFua2VkX2xpc3QgPSBsaXN0KCkKICBmb3IgKGNvbCBpbiAoZ2VwKSkgewogICBsc3QgPSBjb2wgJT4lIHNldE5hbWVzKHJvd25hbWVzKGdlcCkpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUUlVFKSAKICAgcmFua2VkX2xpc3QgICU8PiUgYXBwZW5kKGxpc3QobHN0KSkKICB9CiAgbmFtZXMocmFua2VkX2xpc3QpID0gcGFzdGUwKCJnZXAiLDE6bmNvbChnZXApKQogIAogIAogIAogIGh5cF9vYmogPC0gaHlwZVIocmFua2VkX2xpc3QsIGdlbmVzZXRzX2VudiwgdGVzdD0ia3N0ZXN0IiwgZmRyPTAuMDUsIHBsb3R0aW5nPUYsYmFja2dyb3VuZCA9IHJvd25hbWVzKGdlcF9zY29yZXMzKSkKICBwcmludF90YWIoCiAgICBoeXBfZG90cyhoeXBfb2JqLHNpemVfYnkgPSAic2lnbmlmaWNhbmNlIixhYnJ2ID0gMTAwLG1lcmdlID0gVCkrCiAgICAgIHNjYWxlX2NvbG9yX2NvbnRpbnVvdXMobG93ID0gInJlZCIsIGhpZ2ggPSAiYmxhY2siLCBndWlkZSA9IGd1aWRlX2NvbG9yYmFyKHJldmVyc2UgPSBUUlVFKSkKICAgICx0aXRsZSA9IGdlcF9udW0pCiAgCn0KCmBgYAoKIyBwcm9ncmFtcyBlbnJpY2htZW50CgoKYGBge3J9CmdlcF9zY29yZXMgPSBweSRnZXBfc2NvcmVzMwp1c2FnZV9ub3JtPSBweSR1c2FnZV9ub3JtMwpgYGAKCmBgYHtyfQojIG9yOgpnZXBfc2NvcmVzID0gcmVhZFJEUygiL3NjaS9sYWJzL3lvdGFtZC9sYWJfc2hhcmUvYXZpc2hhaS53aXplbC9SX3Byb2plY3RzL0VHRlIvRGF0YS9jbm1mL2hhcm1vbnlfbW9kZWxzX2dlcF9zY29yZXMucmRzIikKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD04fQpuYW1lcyAoZ2VwX3Njb3JlcykgPSBjKCJIeXBveGlhIiwiVE5GYSIsIkNlbGxfY3ljbGUiKQpwbHRfbGlzdCA9IGxpc3QoKQoKZm9yIChwcm9ncmFtICBpbiBuYW1lcyAoZ2VwX3Njb3JlcykpIHsKIHAgPSBnZ3Bsb3QoZ2VwX3Njb3JlcywgYWVzKHg9ISFlbnN5bShwcm9ncmFtKSkpICsKICBnZW9tX2RlbnNpdHkoKSt4bGFiKHByb2dyYW0pKwogICBnZW9tX3ZsaW5lKAogICAgYWVzKHhpbnRlcmNlcHQ9c29ydChnZXBfc2NvcmVzWyxwcm9ncmFtXSxUUlVFKVsyMDBdICAsY29sb3I9InRvcDIwMCIpLAogICAgICAgICAgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MSkrCiAgIGdlb21fdmxpbmUoCiAgICBhZXMoeGludGVyY2VwdD1zb3J0KGdlcF9zY29yZXNbLHByb2dyYW1dLFRSVUUpWzEwMF0gICxjb2xvcj0idG9wMTAwIiksCiAgICAgICAgICBsaW5ldHlwZT0iZGFzaGVkIiwgc2l6ZT0xKSsKICAgICAgZ2VvbV92bGluZSgKICAgIGFlcyh4aW50ZXJjZXB0PXNvcnQoZ2VwX3Njb3Jlc1sscHJvZ3JhbV0sVFJVRSlbNTBdICAsY29sb3I9InRvcDUwIiksCiAgICAgICAgICBsaW5ldHlwZT0iZGFzaGVkIiwgc2l6ZT0xKSsKICAgICAgICAgZ2VvbV92bGluZSgKICAgIGFlcyh4aW50ZXJjZXB0PXNvcnQoZ2VwX3Njb3Jlc1sscHJvZ3JhbV0sVFJVRSlbMTUwXSAgLGNvbG9yPSJ0b3AxNTAiKSwKICAgICAgICAgIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTEpKwogICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJ0b3AgbiBnZW5lcyIsIHZhbHVlcyA9IGModG9wMjAwID0gImJsdWUiLHRvcDEwMCA9ICJyZWQiLHRvcDE1MCA9ICJ5ZWxsb3ciLHRvcDUwID0gImdyZWVuIikpCiAgIHBsdF9saXN0W1twcm9ncmFtXV0gPC0gcAoKfQogCmdnYXJyYW5nZShwbG90bGlzdCA9IHBsdF9saXN0KQoKYGBgCgoKCgoKIyBQcm9ncmFtcyBoYWxsbWFyayBlbnJpY2htZW50CgpgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQpudG9wID0gMTUwCnBsdF9saXN0ID0gbGlzdCgpCmhpZl90YXJnZXRzX3NldCA9IGRhdGEuZnJhbWUoZ3NfbmFtZSA9ICJoaWZfdGFyZ2V0cyIsZ2VuZV9zeW1ib2wgPSBoaWZfdGFyZ2V0cykKCmZvciAoaSBpbiAxOm5jb2woZ2VwX3Njb3JlcykpIHsKICB0b3BfZ2VuZXMgPSBnZXBfc2NvcmVzICAlPiUgIGFycmFuZ2UoZGVzYyhnZXBfc2NvcmVzW2ldKSkgI3NvcnQgYnkgc2NvcmUgYQogIHRvcCA9IGhlYWQocm93bmFtZXModG9wX2dlbmVzKSxudG9wKSAjdGFrZSB0b3AgdG9wX2dlbmVzX251bQogIHJlcyA9IGdlbmVzX3ZlY19lbnJpY2htZW50KGdlbmVzID0gdG9wLGJhY2tncm91bmQgPSByb3duYW1lcyhnZXBfc2NvcmVzKSxob21lciA9IFQsdGl0bGUgPSAKICAgICAgICAgICAgICAgICAgICBpLHNpbGVudCA9IFQscmV0dXJuX2FsbCA9IFQsY3VzdG9tX3BhdGh3YXlzICA9IGhpZl90YXJnZXRzX3NldCkKICAgCiAgcGx0X2xpc3RbW2ldXSA9IHJlcyRwbHQKfQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IHBsdF9saXN0KQpgYGAKCiMgcHJvZ3JhbSAyIGFsbCBjcApgYGB7cn0KbnRvcCA9IDE1MAppID0gMiAKCiAgdG9wX2dlbmVzID0gZ2VwX3Njb3JlcyAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3Jlc1tpXSkpICNzb3J0IGJ5IHNjb3JlIGEKICB0b3AgPSBoZWFkKHJvd25hbWVzKHRvcF9nZW5lcyksbnRvcCkgI3Rha2UgdG9wIHRvcF9nZW5lc19udW0KICByZXMgPSBnZW5lc192ZWNfZW5yaWNobWVudChnZW5lcyA9IHRvcCxiYWNrZ3JvdW5kID0gcm93bmFtZXMoZ2VwX3Njb3JlcyksaG9tZXIgPSBULHRpdGxlID0gCiAgICAgICAgICAgICAgICAgICAgaSxzaWxlbnQgPSBULHJldHVybl9hbGwgPSBULGN1c3RvbV9wYXRod2F5cyAgPSBjYW5vbmljYWxfcGF0aHdheXMpCiAgIAogcHJpbnQocmVzJHBsdCkgCgoKYGBgCgojIFByb2dyYW0gMiBhbGwgbXNpZ2RiCmBgYHtyfQpudG9wID0gMTUwCmkgPSAyIAoKICB0b3BfZ2VuZXMgPSBnZXBfc2NvcmVzICAlPiUgIGFycmFuZ2UoZGVzYyhnZXBfc2NvcmVzW2ldKSkgI3NvcnQgYnkgc2NvcmUgYQogIHRvcCA9IGhlYWQocm93bmFtZXModG9wX2dlbmVzKSxudG9wKSAjdGFrZSB0b3AgdG9wX2dlbmVzX251bQogIHJlcyA9IGdlbmVzX3ZlY19lbnJpY2htZW50KGdlbmVzID0gdG9wLGJhY2tncm91bmQgPSByb3duYW1lcyhnZXBfc2NvcmVzKSxob21lciA9IFQsdGl0bGUgPSAKICAgICAgICAgICAgICAgICAgICBpLHNpbGVudCA9IFQscmV0dXJuX2FsbCA9IFQsY3VzdG9tX3BhdGh3YXlzICA9IGFsbF9wYXRod2F5cykKICAgCiBwcmludChyZXMkcGx0KSAKCgpgYGAKCgpgYGB7cn0KeGVubyA9IEZpbmRWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IHhlbm8sbmZlYXR1cmVzID0gMjAwMCkKeGVub192YXJnZW5lcyA9IFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0geGVubykKCnhlbm9fZXhwcmVzc2lvbiA9IEZldGNoRGF0YShvYmplY3QgPSB4ZW5vLHZhcnMgPSB4ZW5vX3ZhcmdlbmVzLHNsb3Q9J2NvdW50cycpCmFsbF8wX2dlbmVzID0gY29sbmFtZXMoeGVub19leHByZXNzaW9uKVtjb2xTdW1zKHhlbm9fZXhwcmVzc2lvbj09MCwgbmEucm09VFJVRSk9PW5yb3coeGVub19leHByZXNzaW9uKV0gI2RlbGV0ZSByb3dzIHRoYXQgaGF2ZSBhbGwgMAp4ZW5vX3ZhcmdlbmVzID0geGVub192YXJnZW5lc1sheGVub192YXJnZW5lcyAlaW4lIGFsbF8wX2dlbmVzXQoKYGBgCgoKCiMgVGVzdCB3aXRoIGV4cHIgYWZ0ZXIgaGFybW9ueQpgYGB7cHl0aG9ufQojIGltcG9ydCBudW1weSBhcyBucAojIGltcG9ydCBzY2FucHkgYXMgc2MKIyAKIyBleHByX2FmdGVyX2hhcm1vbnkgPSBzYy5yZWFkX2g1YWQoJy4vRGF0YS9jbm1mL3hlbm9fSGFybW9ueV9Ob05lZ18yS3ZhcmdlbmVzLmg1YWQnKS50b19kZigpCiMgdHBtID0gIGNvbXB1dGVfdHBtKGV4cHJfYWZ0ZXJfaGFybW9ueSkKIyBjbm1mX2dlbmVzID0gZXhwcl9hZnRlcl9oYXJtb255LmtleXMoKS50b19saXN0KCkKIyB1c2FnZV9ieV9jYWxjID0gZ2V0X3VzYWdlX2Zyb21fc2NvcmUoY291bnRzPWV4cHJfYWZ0ZXJfaGFybW9ueSx0cG09dHBtLGdlbmVzPWNubWZfZ2VuZXMsY25tZl9vYmo9Y25tZl9vYmosaz0zKQpgYGAKCiMgQ2hlY2sgaWYgb3JpZ2luYWwgY05NRiBzY29yZSBpcyBsaWtlIHRoZSBjYWxjdWxhdGVkIHNjb3JlCmBgYHtyfQp1c2FnZV9ieV9jYWxjID0gcHkkdXNhZ2VfYnlfY2FsYwp1c2FnZV9ub3JtID0gcHkkdXNhZ2Vfbm9ybTMKY29yKHVzYWdlX2J5X2NhbGMsdXNhZ2Vfbm9ybSkKYGBgCgojIGNhbGN1bGF0ZSBzY29yZSBmb3IgWGVubwpgYGB7cHl0aG9ufQppbXBvcnQgbnVtcHkgYXMgbnAKaW1wb3J0IHNjYW5weSBhcyBzYwp4ZW5vX2V4cHJlc3Npb24gPSByLnhlbm9fZXhwcmVzc2lvbgp4ZW5vX3ZhcmdlbmVzID0gci54ZW5vX3ZhcmdlbmVzCnRwbSA9ICBjb21wdXRlX3RwbSh4ZW5vX2V4cHJlc3Npb24pCnVzYWdlX2J5X2NhbGMgPSBnZXRfdXNhZ2VfZnJvbV9zY29yZShjb3VudHM9eGVub19leHByZXNzaW9uLHRwbT10cG0sZ2VuZXM9eGVub192YXJnZW5lcywgY25tZl9vYmo9Y25tZl9vYmosaz0zKQpgYGAKCmBgYHtyfQp4ZW5vX3VzYWdlID0gcHkkdXNhZ2VfYnlfY2FsYwpgYGAKCiMgcHJvZ3JhbXMgZXhwcmVzc2lvbiB7LnRhYnNldH0KYGBge3IgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD05LCByZXN1bHRzPSdhc2lzJ30KCm5hbWVzICh4ZW5vX3VzYWdlKSA9IGMoIkh5cG94aWEiLCJUTkZhIiwiQ2VsbF9jeWNsZSIpCiNhZGQgZWFjaCBtZXRhZ2VuZSB0byBtZXRhZGF0YQpmb3IgKGkgIGluIDE6bmNvbCh4ZW5vX3VzYWdlKSkgewogIG1ldGFnZV9tZXRhZGF0YSA9IHhlbm9fdXNhZ2UgJT4lIGRwbHlyOjpzZWxlY3QoaSkKICB4ZW5vID0gQWRkTWV0YURhdGEob2JqZWN0ID0geGVubyxtZXRhZGF0YSA9IG1ldGFnZV9tZXRhZGF0YSxjb2wubmFtZSA9IG5hbWVzKHhlbm9fdXNhZ2UpW2ldKQp9CgpwcmludF90YWIocGx0ID0gRmVhdHVyZVBsb3Qob2JqZWN0ID0geGVubyxmZWF0dXJlcyA9IGNvbG5hbWVzKHhlbm9fdXNhZ2UpKSx0aXRsZSA9ICJ1bWFwIGV4cHJlc3Npb24iKQoKCmBgYAoKCgojIHByb2dyYW1zIHJlZ3VsYXRpb24gey50YWJzZXR9CmBgYHtyIGVjaG89VFJVRSwgIHJlc3VsdHM9J2FzaXMnfQptZXRhZ2VuZXNfbWVhbl9jb21wYXJlKGRhdGFzZXQgPSB4ZW5vLHRpbWUucG9pbnRfdmFyID0gInRyZWF0bWVudCIscHJlZml4ID0gIm1vZGVsIixwYXRpZW50LmlkZW50X3ZhciA9ICJvcmlnLmlkZW50IixwcmVfb24gPSBjKCJOVCIsIk9TSSIpLHRlc3QgPSAid2lsY294LnRlc3QiLHByb2dyYW1zID0gYygiSHlwb3hpYSIsIlRORmEiLCJDZWxsX2N5Y2xlIikpCgpgYGAKCmBgYHtyfQpEb3RQbG90KG9iamVjdCA9IHhlbm8sIGZlYXR1cmVzID0gIGMoIkh5cG94aWEiLCJUTkZhIiwiQ2VsbF9jeWNsZSIpLGdyb3VwLmJ5ICA9ICd0cmVhdG1lbnQnKQpgYGAKCgoKIyBDQyBzaWduYXR1cmUgIHsudGFic2V0fQpgYGB7ciBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEwLCByZXN1bHRzPSdhc2lzJ30KIyBnZXQgZ2VuZXMgYW5kIHBsb3QgY29yIGhlYXRtYXAKCnBsb3RfZ2VuZXNfY29yIDwtIGZ1bmN0aW9uKGRhdGFzZXQsaGFsbG1hcmtfbmFtZSxudW1fb2ZfY2x1c3RlcnMsZ2VuZUlkcyA9IE5VTEwpIHsKaWYoIWlzX251bGwoZ2VuZUlkcykpewogIGdlbmVJZHMgPSBnZW5lSWRzCn0KZWxzZXsKICBnZW5lSWRzPSBnZW5lc2V0c1tbaGFsbG1hcmtfbmFtZV1dQGdlbmVJZHMKfQpoYWxsbWFyc19leHAgPSBGZXRjaERhdGEob2JqZWN0ID0gZGF0YXNldCx2YXJzID0gYyhnZW5lSWRzKSkKaGFsbG1hcnNfZXhwID0gaGFsbG1hcnNfZXhwWyxjb2xTdW1zKGhhbGxtYXJzX2V4cFtdKT4wXSAjcmVtb3ZlIG5vIGV4cHJlc3Npb24gZ2VuZXMKaGFsbG1hcmtfY29yID0gY29yKGhhbGxtYXJzX2V4cCkKcGh0MSA9IHBoZWF0bWFwKG1hdCA9IGhhbGxtYXJrX2NvcixzaWxlbnQgPSBUKQoKIyBtYWtlIGFubm90YXRpb25zCm51bV9vZl9jbHVzdGVycyA9IG51bV9vZl9jbHVzdGVycwpjbHVzdGVyaW5nX2Rpc3RhbmNlID0gImV1Y2xpZGVhbiIKbXlhbm5vdGF0aW9uID0gYXMuZGF0YS5mcmFtZShjdXRyZWUocGh0MVtbInRyZWVfcm93Il1dLCBrID0gbnVtX29mX2NsdXN0ZXJzKSkgI3NwbGl0IGludG8gayBjbHVzdGVycwpuYW1lcyhteWFubm90YXRpb24pWzFdID0gImNsdXN0ZXIiCm15YW5ub3RhdGlvbiRjbHVzdGVyID0gYXMuZmFjdG9yKG15YW5ub3RhdGlvbiRjbHVzdGVyKQpwYWxldHRlMSA8LWJyZXdlci5wYWwobnVtX29mX2NsdXN0ZXJzLCAiUGFpcmVkIikKbmFtZXMocGFsZXR0ZTEpID0gdW5pcXVlKG15YW5ub3RhdGlvbiRjbHVzdGVyKQphbm5fY29sb3JzID0gbGlzdCAoY2x1c3RlciA9IHBhbGV0dGUxKQphbm5vdGF0aW9uID0gbGlzdChhbm5fY29sb3JzID0gYW5uX2NvbG9ycywgbXlhbm5vdGF0aW9uID0gbXlhbm5vdGF0aW9uKQoKI2Nob29zZSBjb2xvcnMKY29sb3JzIDwtIGMoc2VxKC0xLDEsYnk9MC4wMSkpCm15X3BhbGV0dGUgPC0gYygiYmx1ZSIsY29sb3JSYW1wUGFsZXR0ZShjb2xvcnMgPSBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKG4gPSBsZW5ndGgoY29sb3JzKS0zKSwgInJlZCIpCgoKcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICAgIHBoZWF0bWFwKG1hdCA9IGhhbGxtYXJrX2Nvcixhbm5vdGF0aW9uX2NvbCA9ICBhbm5vdGF0aW9uW1sibXlhbm5vdGF0aW9uIl1dLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm90YXRpb25bWyJhbm5fY29sb3JzIl1dLCBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSBjbHVzdGVyaW5nX2Rpc3RhbmNlLGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9IGNsdXN0ZXJpbmdfZGlzdGFuY2UsY29sb3IgPSBteV9wYWxldHRlLGJyZWFrcyA9IGNvbG9ycyxzaG93X3Jvd25hbWVzID0gRixzaG93X2NvbG5hbWVzID0gRikKICAgICAgICAgICAgLHRpdGxlID0gImdlbmVzIGV4cHJlc3Npb24gaGVhdG1hcCIpCnJldHVybihhbm5vdGF0aW9uKQoKfQpgYGAKCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQpoYWxsbWFya19uYW1lID0gIkhBTExNQVJLX0cyTV9DSEVDS1BPSU5UIgphbm5vdGF0aW9uID0gcGxvdF9nZW5lc19jb3IoZGF0YXNldCA9IHhlbm8saGFsbG1hcmtfbmFtZSA9IGhhbGxtYXJrX25hbWUsbnVtX29mX2NsdXN0ZXJzID0gNCkKYGBgCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KI2Nob29zZSBjbHVzdGVycwpjaG9zZW5fY2x1c3RlcnMgPSBjKDEsMikKcHJpbnQgKHBhc3RlKCJjaG9zZW5fY2x1c3RlcnMgPSAiLCBjaG9zZW5fY2x1c3RlcnMgJT4lIHRvU3RyaW5nKCkpKQoKI1VNQVAgZXhwcmVzc2lvbiBvZiBzaWduYXR1cmUKY2hvc2VuX2dlbmVzID0gYW5ub3RhdGlvbltbIm15YW5ub3RhdGlvbiJdXSAlPiUgZHBseXI6OmZpbHRlcihjbHVzdGVyICVpbiUgY2hvc2VuX2NsdXN0ZXJzKSAlPiUgcm93bmFtZXMoKSAjdGFrZSByZWxldmFudCBnZW5lcwpzY29yZSA8LSBhcHBseSh4ZW5vQGFzc2F5cyRSTkFAZGF0YVtjaG9zZW5fZ2VuZXMsXSwyLG1lYW4pCnhlbm89QWRkTWV0YURhdGEoeGVubyxzY29yZSxoYWxsbWFya19uYW1lKQpzY29yZXNBbmRJbmRpY2VzIDwtIGdldFBhdGh3YXlTY29yZXMoeGVub0Bhc3NheXMkUk5BQGRhdGEsIGNob3Nlbl9nZW5lcykKeGVubz1BZGRNZXRhRGF0YSh4ZW5vLHNjb3Jlc0FuZEluZGljZXMkcGF0aHdheVNjb3JlcyxwYXN0ZTAoaGFsbG1hcmtfbmFtZSwiX3NpcHNpYyIpKQpwcmludF90YWIoRmVhdHVyZVBsb3Qob2JqZWN0ID0geGVubywgZmVhdHVyZXMgPSBwYXN0ZTAoaGFsbG1hcmtfbmFtZSwiX3NpcHNpYyIpKSx0aXRsZSA9ICJFeHByZXNzaW9uIikKcHJpbnRfdGFiKEZlYXR1cmVQbG90KG9iamVjdCA9IHhlbm8sIGZlYXR1cmVzID0gaGFsbG1hcmtfbmFtZSksdGl0bGUgPSAiRXhwcmVzc2lvbiIpCmBgYAoKYGBge3IgZWNobz1UUlVFLCByZXN1bHRzPSdhc2lzJ30KCiNwbG90IHNpZ25hdHVyZSBkaXN0cmlidXRpb24KY2Nfc2NvcmVzID0gRmV0Y2hEYXRhKG9iamVjdCA9IHhlbm8sdmFycyA9ICJIQUxMTUFSS19HMk1fQ0hFQ0tQT0lOVCIpCnBsdCAgPSAgZ2dwbG90KGNjX3Njb3JlcywgYWVzKHg9SEFMTE1BUktfRzJNX0NIRUNLUE9JTlQpKSArCiAgZ2VvbV9kZW5zaXR5KCkrCiAgIGdlb21fdmxpbmUoCiAgICBhZXMoeGludGVyY2VwdD1tZWFuKGNjX3Njb3JlcyRIQUxMTUFSS19HMk1fQ0hFQ0tQT0lOVCkgKyBzZChjY19zY29yZXMkSEFMTE1BUktfRzJNX0NIRUNLUE9JTlQpICxjb2xvcj0iMSBTRCIpLAogICAgICAgICAgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MSkrCiAgICBnZW9tX3ZsaW5lKAogICAgYWVzKHhpbnRlcmNlcHQ9bWVhbihjY19zY29yZXMkSEFMTE1BUktfRzJNX0NIRUNLUE9JTlQpICsgMipzZChjY19zY29yZXMkSEFMTE1BUktfRzJNX0NIRUNLUE9JTlQpICxjb2xvcj0iMiBTRCIpLAogICAgICAgICAgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MSkKCnByaW50X3RhYihwbHQgPSBwbHQsdGl0bGUgPSAiZGlzdCIpCgojIFBsb3QgYXNzaWdubWVudCAKCmNjX3Njb3JlcyA9IGNjX3Njb3JlcyAlPiUgbXV0YXRlKGlzX2N5Y2xpbmcgPSBpZl9lbHNlKGNvbmRpdGlvbiA9IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBIQUxMTUFSS19HMk1fQ0hFQ0tQT0lOVCA+IG1lYW4oSEFMTE1BUktfRzJNX0NIRUNLUE9JTlQpIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICArc2QoSEFMTE1BUktfRzJNX0NIRUNLUE9JTlQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ1ZSA9ICJjeWNsaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlID0gIm5vbl9jeWNsaW5nIikpCnhlbm8gPSBBZGRNZXRhRGF0YShvYmplY3QgPSB4ZW5vLG1ldGFkYXRhID0gY2Nfc2NvcmVzJGlzX2N5Y2xpbmcsY29sLm5hbWUgPSAiaXNfY3ljbGluZyIpCnByaW50X3RhYihwbHQgPSBEaW1QbG90KG9iamVjdCA9IHhlbm8sZ3JvdXAuYnkgPSAiaXNfY3ljbGluZyIpICwgdGl0bGUgPSAiYXNzaWdubWVudCAxIHNkIikKCgojIGNjX3Njb3JlcyA9IGNjX3Njb3JlcyAlPiUgbXV0YXRlKGlzX2N5Y2xpbmcgPSBpZl9lbHNlKGNvbmRpdGlvbiA9IAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhBTExNQVJLX0cyTV9DSEVDS1BPSU5UID4gbWVhbihIQUxMTUFSS19HMk1fQ0hFQ0tQT0lOVCkgKyAyKnNkKGNjX3Njb3JlcyRIQUxMTUFSS19HMk1fQ0hFQ0tQT0lOVCksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAiY3ljbGluZyIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlID0gIm5vbl9jeWNsaW5nIikpCiMgeGVubyA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IHhlbm8sbWV0YWRhdGEgPSBjY19zY29yZXMkaXNfY3ljbGluZyxjb2wubmFtZSA9ICJpc19jeWNsaW5nIikKIyBwcmludF90YWIocGx0ID0gRGltUGxvdChvYmplY3QgPSB4ZW5vLGdyb3VwLmJ5ID0gImlzX2N5Y2xpbmciKSAsIHRpdGxlID0gImFzc2lnbm1lbnQgMiBzZCIpCgoKYGBgCgoKCgpgYGB7ciBlY2hvPVRSVUUsIHJlc3VsdHM9J2FzaXMnfQogZGYgID0gRmV0Y2hEYXRhKG9iamVjdCA9IHhlbm8sdmFycyA9IGMoImlzX2N5Y2xpbmciLCJ0cmVhdG1lbnQiKSkgJT4lIAogICAgZmlsdGVyICh0cmVhdG1lbnQgJWluJSBjKCJOVCIsIk9TSSIpKSAlPiUgCiAgICBkcm9wbGV2ZWxzKCkgCiAgdGVzdCA9IGZpc2hlci50ZXN0KHRhYmxlKGRmKSkKICAgIAogIGxpYnJhcnkoZ2dzdGF0c3Bsb3QpCgogICAgcGx0ID0gZ2diYXJzdGF0cygKICAgIGRmLCBpc19jeWNsaW5nLCB0cmVhdG1lbnQsCiAgICByZXN1bHRzLnN1YnRpdGxlID0gRkFMU0UsCiAgICBzdWJ0aXRsZSA9IHBhc3RlMCgKICAgICAgIkZpc2hlcidzIGV4YWN0IHRlc3QiLCAiLCBwLXZhbHVlID0gIiwKICAgICAgIHJvdW5kKHRlc3QkcC52YWx1ZSwxMykpCiAgICApCiAgCnByaW50X3RhYihwbHQgPSBwbHQsdGl0bGUgPSAiZmlzaGVyIikKYGBgCiMjIGNvcnJlbGF0aW9uIHRvIG5tZiBwcm9ncmFtCmBgYHtyfQpjb3JfcmVzID0gY29yKHhlbm8kQ2VsbF9jeWNsZSx4ZW5vW1toYWxsbWFya19uYW1lXV0pCnByaW50KHBhc3RlKCJjb3JyZWxhdGlvbiBvZiBUTkZhIHByb2dyYW0gdG8iLCBoYWxsbWFya19uYW1lLCI6IiwgY29yX3JlcykpCgpjb3JfcmVzID0gY29yKHhlbm8kQ2VsbF9jeWNsZSx4ZW5vW1twYXN0ZTAoaGFsbG1hcmtfbmFtZSwiX3NpcHNpYyIpXV0pCnByaW50KHBhc3RlKCJjb3JyZWxhdGlvbiBvZiBDZWxsX2N5Y2xlIHByb2dyYW0gdG8iLCBwYXN0ZTAoaGFsbG1hcmtfbmFtZSwiX3NpcHNpYyIpLCI6IiwgY29yX3JlcykpCmBgYAoKCiMgVE5GYSBzaWduYXR1cmUgIHsudGFic2V0fQoKYGBge3IgcmVzdWx0cz0nYXNpcyd9CmhhbGxtYXJrX25hbWUgPSAiSEFMTE1BUktfVE5GQV9TSUdOQUxJTkdfVklBX05GS0IiCmFubm90YXRpb24gPSBwbG90X2dlbmVzX2NvcihkYXRhc2V0ID0geGVubyxoYWxsbWFya19uYW1lID0gaGFsbG1hcmtfbmFtZSxudW1fb2ZfY2x1c3RlcnMgPSA2KQoKCmBgYAoKCmBgYHtyIGVjaG89VFJVRSwgcmVzdWx0cz0nYXNpcyd9CmNob3Nlbl9jbHVzdGVycyA9IGMoMSw1KQpwcmludCAocGFzdGUoImNob3Nlbl9jbHVzdGVycyA9ICIsIGNob3Nlbl9jbHVzdGVycyAlPiUgdG9TdHJpbmcoKSkpCgojVU1BUCBleHByZXNzaW9uIG9mIHNpZ25hdHVyZQpjaG9zZW5fZ2VuZXMgPSBhbm5vdGF0aW9uW1sibXlhbm5vdGF0aW9uIl1dICU+JSBkcGx5cjo6ZmlsdGVyKGNsdXN0ZXIgJWluJSBjaG9zZW5fY2x1c3RlcnMpICU+JSByb3duYW1lcygpICN0YWtlIHJlbGV2YW50IGdlbmVzCnNjb3JlIDwtIGFwcGx5KHhlbm9AYXNzYXlzJFJOQUBkYXRhW2Nob3Nlbl9nZW5lcyxdLDIsbWVhbikKeGVubz1BZGRNZXRhRGF0YSh4ZW5vLHNjb3JlLGhhbGxtYXJrX25hbWUpCnNjb3Jlc0FuZEluZGljZXMgPC0gZ2V0UGF0aHdheVNjb3Jlcyh4ZW5vQGFzc2F5cyRSTkFAZGF0YSwgY2hvc2VuX2dlbmVzKQp4ZW5vPUFkZE1ldGFEYXRhKHhlbm8sc2NvcmVzQW5kSW5kaWNlcyRwYXRod2F5U2NvcmVzLHBhc3RlMChoYWxsbWFya19uYW1lLCJfc2lwc2ljIikpCnByaW50X3RhYihGZWF0dXJlUGxvdChvYmplY3QgPSB4ZW5vLCBmZWF0dXJlcyA9IHBhc3RlMChoYWxsbWFya19uYW1lLCJfc2lwc2ljIikpLHRpdGxlID0gIkV4cHJlc3Npb24iKQpwcmludF90YWIoRmVhdHVyZVBsb3Qob2JqZWN0ID0geGVubywgZmVhdHVyZXMgPSBoYWxsbWFya19uYW1lKSx0aXRsZSA9ICJFeHByZXNzaW9uIikKYGBgCiMjIGNvcnJlbGF0aW9uIHRvIG5tZiBwcm9ncmFtCgpgYGB7cn0KY29yX3JlcyA9IGNvcih4ZW5vJFRORmEseGVub1tbaGFsbG1hcmtfbmFtZV1dKQpwcmludChwYXN0ZSgiY29ycmVsYXRpb24gb2YgVE5GYSBwcm9ncmFtIHRvIiwgaGFsbG1hcmtfbmFtZSwiOiIsIGNvcl9yZXMpKQoKY29yX3JlcyA9IGNvcih4ZW5vJFRORmEseGVub1tbcGFzdGUwKGhhbGxtYXJrX25hbWUsIl9zaXBzaWMiKV1dKQpwcmludChwYXN0ZSgiY29ycmVsYXRpb24gb2YgVE5GYSBwcm9ncmFtIHRvIiwgcGFzdGUwKGhhbGxtYXJrX25hbWUsIl9zaXBzaWMiKSwiOiIsIGNvcl9yZXMpKQpgYGAKCmBgYHtyIGVjaG89VFJVRSwgcmVzdWx0cz0nYXNpcyd9CmNjX3Njb3JlcyA9IEZldGNoRGF0YShvYmplY3QgPSB4ZW5vLHZhcnMgPSBoYWxsbWFya19uYW1lKQoKcGx0ICA9ICBnZ3Bsb3QoY2Nfc2NvcmVzLCBhZXMoeD1IQUxMTUFSS19UTkZBX1NJR05BTElOR19WSUFfTkZLQikpICsKICBnZW9tX2RlbnNpdHkoKSsKICAgZ2VvbV92bGluZSgKICAgIGFlcyh4aW50ZXJjZXB0PW1lYW4oSEFMTE1BUktfVE5GQV9TSUdOQUxJTkdfVklBX05GS0IpICsgc2QoSEFMTE1BUktfVE5GQV9TSUdOQUxJTkdfVklBX05GS0IpICxjb2xvcj0iMSBTRCIpLAogICAgICAgICAgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MSkrCiAgICBnZW9tX3ZsaW5lKAogICAgYWVzKHhpbnRlcmNlcHQ9bWVhbihIQUxMTUFSS19UTkZBX1NJR05BTElOR19WSUFfTkZLQikgKyAyKnNkKEhBTExNQVJLX1RORkFfU0lHTkFMSU5HX1ZJQV9ORktCKSAsY29sb3I9IjIgU0QiKSwKICAgICAgICAgIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTEpKwogICAgZ2VvbV92bGluZSgKICAgIGFlcyh4aW50ZXJjZXB0PW1lYW4oSEFMTE1BUktfVE5GQV9TSUdOQUxJTkdfVklBX05GS0IpICxjb2xvcj0ibWVhbiIpLAogICAgICAgICAgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MSkKCnByaW50X3RhYihwbHQgPSBwbHQsdGl0bGUgPSAiZGlzdCIpCmBgYAoKCgojIHdlaWdodGVkIGtzIHRlc3QgICB7LnRhYnNldH0KYGBge3IgLHJlc3VsdHM9J2FzaXMnfQoKICAKcmFua2VkX2xpc3QxID0gZ2VwX3Njb3JlcyAlPiUgc2VsZWN0KDEpICU+JSBhc192ZWN0b3IoKSAlPiUgc2V0TmFtZXMocm93bmFtZXMoZ2VwX3Njb3JlcykpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUUlVFKSAKcmFua2VkX2xpc3QyID0gZ2VwX3Njb3JlcyAlPiUgc2VsZWN0KDIpICU+JSBhc192ZWN0b3IoKSAlPiUgc2V0TmFtZXMocm93bmFtZXMoZ2VwX3Njb3JlcykpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUUlVFKSAKcmFua2VkX2xpc3QzID0gZ2VwX3Njb3JlcyAlPiUgc2VsZWN0KDMpICU+JSBhc192ZWN0b3IoKSAlPiUgc2V0TmFtZXMocm93bmFtZXMoZ2VwX3Njb3JlcykpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUUlVFKSAKCgoKcmFua2VkX2xpc3QgPSBsaXN0KCJnZXAxIiA9IHJhbmtlZF9saXN0MSwgImdlcDIiID0gcmFua2VkX2xpc3QyLCJnZXAzIiA9IHJhbmtlZF9saXN0MykKaHlwX29iaiA8LSBoeXBlUihyYW5rZWRfbGlzdCwgZ2VuZXNldHNfZW52LCB0ZXN0PSJrc3Rlc3QiLCBmZHI9MC4wNSwgcGxvdHRpbmc9RixiYWNrZ3JvdW5kID0gcm93bmFtZXMoZ2VwX3Njb3JlcykpCnBsb3RzID0gaHlwX2RvdHMoaHlwX29iaixzaXplX2J5ID0gIm5vbmUiKQpwcmludF90YWIocGx0ID0gcGxvdHNbWzFdXSx0aXRsZSA9ICJHRVAgMSIpCnByaW50X3RhYihwbHQgPSBwbG90c1tbMl1dLHRpdGxlID0gIkdFUCAyIikKcHJpbnRfdGFiKHBsdCA9IHBsb3RzW1szXV0sdGl0bGUgPSAiR0VQIDMiKQoKYGBgCgoKIyBBUEMgc2lnbmF0dXJlICB7LnRhYnNldH0KCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQpoYWxsbWFya19uYW1lID0gIktFR0dfQU5USUdFTl9QUk9DRVNTSU5HX0FORF9QUkVTRU5UQVRJT04iCmFubm90YXRpb24gPSBwbG90X2dlbmVzX2NvcihkYXRhc2V0ID0geGVubyxoYWxsbWFya19uYW1lID0gaGFsbG1hcmtfbmFtZSxudW1fb2ZfY2x1c3RlcnMgPSA0LGNhbm9jaWNhbCA9IFQpCmBgYAoKCmBgYHtyIGVjaG89VFJVRSwgcmVzdWx0cz0nYXNpcyd9CmNob3Nlbl9jbHVzdGVycyA9IGMoMSwzKQpwcmludCAocGFzdGUoImNob3Nlbl9jbHVzdGVycyA9ICIsIGNob3Nlbl9jbHVzdGVycyAlPiUgdG9TdHJpbmcoKSkpCgojVU1BUCBleHByZXNzaW9uIG9mIHNpZ25hdHVyZQpjaG9zZW5fZ2VuZXMgPSBhbm5vdGF0aW9uW1sibXlhbm5vdGF0aW9uIl1dICU+JSBkcGx5cjo6ZmlsdGVyKGNsdXN0ZXIgJWluJSBjaG9zZW5fY2x1c3RlcnMpICU+JSByb3duYW1lcygpICN0YWtlIHJlbGV2YW50IGdlbmVzCnNjb3JlIDwtIGFwcGx5KHhlbm9AYXNzYXlzJFJOQUBkYXRhW2Nob3Nlbl9nZW5lcyxdLDIsbWVhbikKeGVubz1BZGRNZXRhRGF0YSh4ZW5vLHNjb3JlLGhhbGxtYXJrX25hbWUpCnNjb3Jlc0FuZEluZGljZXMgPC0gZ2V0UGF0aHdheVNjb3Jlcyh4ZW5vQGFzc2F5cyRSTkFAZGF0YSwgY2hvc2VuX2dlbmVzKQp4ZW5vPUFkZE1ldGFEYXRhKHhlbm8sc2NvcmVzQW5kSW5kaWNlcyRwYXRod2F5U2NvcmVzLHBhc3RlMChoYWxsbWFya19uYW1lLCJfc2lwc2ljIikpCnByaW50X3RhYihGZWF0dXJlUGxvdChvYmplY3QgPSB4ZW5vLCBmZWF0dXJlcyA9IHBhc3RlMChoYWxsbWFya19uYW1lLCJfc2lwc2ljIikpLHRpdGxlID0gIkV4cHJlc3Npb24iKQpwcmludF90YWIoRmVhdHVyZVBsb3Qob2JqZWN0ID0geGVubywgZmVhdHVyZXMgPSBoYWxsbWFya19uYW1lKSx0aXRsZSA9ICJFeHByZXNzaW9uIikKYGBgCgojIyBjb3JyZWxhdGlubyB0byBubWYKYGBge3J9CmNvcl9yZXMgPSBjb3IoeGVubyRUTkZhLHhlbm9bW2hhbGxtYXJrX25hbWVdXSkKcHJpbnQocGFzdGUoImNvcnJlbGF0aW9uIG9mIFRORmEgcHJvZ3JhbSB0byIsIGhhbGxtYXJrX25hbWUsIjoiLCBjb3JfcmVzKSkKCmNvcl9yZXMgPSBjb3IoeGVubyRUTkZhLHhlbm9bW3Bhc3RlMChoYWxsbWFya19uYW1lLCJfc2lwc2ljIildXSkKcHJpbnQocGFzdGUoImNvcnJlbGF0aW9uIG9mIFRORmEgcHJvZ3JhbSB0byIsIHBhc3RlMChoYWxsbWFya19uYW1lLCJfc2lwc2ljIiksIjoiLCBjb3JfcmVzKSkKYGBgCgojIFNpZ25hdHVyZXMgcmVndWxhdGlvbiB7LnRhYnNldH0KYGBge3IgZWNobz1UUlVFLCByZXN1bHRzPSdhc2lzJ30KbWV0YWdlbmVzX21lYW5fY29tcGFyZShkYXRhc2V0ID0geGVubyx0aW1lLnBvaW50X3ZhciA9ICJ0cmVhdG1lbnQiLHByZWZpeCA9ICJtb2RlbCIscGF0aWVudC5pZGVudF92YXIgPSAib3JpZy5pZGVudCIscHJlX29uID0gYygiTlQiLCJPU0kiLCJyZXMiKSxwcm9ncmFtcyA9IGMoIlRORmEiLCJIQUxMTUFSS19UTkZBX1NJR05BTElOR19WSUFfTkZLQiIsIktFR0dfQU5USUdFTl9QUk9DRVNTSU5HX0FORF9QUkVTRU5UQVRJT04iKSkKCmBgYAogICAKIyBpbnRlcnNlY3Rpb24gey50YWJzZXR9CgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KdG9wX290ID0gZ2VwX3Njb3JlczNbb3JkZXIoZ2VwX3Njb3JlczNbLDJdLGRlY3JlYXNpbmcgPSBUKSwyLGRyb3AgPSBGXSU+JSBoZWFkKDIwMCkgJT4lIHJvd25hbWVzKCkKaGFsbG1hcmtfbmFtZSA9ICJLRUdHX0FOVElHRU5fUFJPQ0VTU0lOR19BTkRfUFJFU0VOVEFUSU9OIgpnZW5lSWRzPSBnZW5lc2V0c1tbaGFsbG1hcmtfbmFtZV1dCmNob3Nlbl9nZW5lcyA9IGludGVyc2VjdChnZW5lSWRzLHRvcF9vdCkKcHJpbnQoY2hvc2VuX2dlbmVzKQpzY29yZSA8LSBhcHBseSh4ZW5vQGFzc2F5cyRSTkFAZGF0YVtjaG9zZW5fZ2VuZXMsXSwyLG1lYW4pCnhlbm89QWRkTWV0YURhdGEoeGVubyxzY29yZSxwYXN0ZTAoaGFsbG1hcmtfbmFtZSwiaW50ZXJzZWN0ZWQiKSkKc2NvcmVzQW5kSW5kaWNlcyA8LSBnZXRQYXRod2F5U2NvcmVzKHhlbm9AYXNzYXlzJFJOQUBkYXRhLCBjaG9zZW5fZ2VuZXMpCnhlbm89QWRkTWV0YURhdGEoeGVubyxzY29yZXNBbmRJbmRpY2VzJHBhdGh3YXlTY29yZXMscGFzdGUwKGhhbGxtYXJrX25hbWUsIl9pbnRlcnNlY3RlZF9zaXBzaWMiKSkKcHJpbnRfdGFiKEZlYXR1cmVQbG90KG9iamVjdCA9IHhlbm8sIGZlYXR1cmVzID0gcGFzdGUwKGhhbGxtYXJrX25hbWUsIl9pbnRlcnNlY3RlZF9zaXBzaWMiKSksdGl0bGUgPSAiRXhwcmVzc2lvbiIpCnByaW50X3RhYihGZWF0dXJlUGxvdChvYmplY3QgPSB4ZW5vLCBmZWF0dXJlcyA9IHBhc3RlMChoYWxsbWFya19uYW1lLCJpbnRlcnNlY3RlZCIpKSx0aXRsZSA9ICJFeHByZXNzaW9uIikKYGBgCgpgYGB7cn0KY29yX3JlcyA9IGNvcih4ZW5vJFRORmEseGVub1tbcGFzdGUwKGhhbGxtYXJrX25hbWUsImludGVyc2VjdGVkIildXSkKcHJpbnQocGFzdGUoImNvcnJlbGF0aW9uIG9mIFRORmEgcHJvZ3JhbSB0byIsIHBhc3RlMChoYWxsbWFya19uYW1lLCJpbnRlcnNlY3RlZCIpLCI6IiwgY29yX3JlcykpCgpgYGAKCmBgYHtyfQp0b3Bfb3QgPSBnZXBfc2NvcmVzM1tvcmRlcihnZXBfc2NvcmVzM1ssMl0sZGVjcmVhc2luZyA9IFQpLDIsZHJvcCA9IEZdJT4lIGhlYWQoMjAwKSAlPiUgcm93bmFtZXMoKQpoYWxsbWFya19uYW1lID0gIkhBTExNQVJLX1RORkFfU0lHTkFMSU5HX1ZJQV9ORktCIgpnZW5lSWRzPSBnZW5lc2V0c1tbaGFsbG1hcmtfbmFtZV1dCmNob3Nlbl9nZW5lcyA9IGludGVyc2VjdChnZW5lSWRzLHRvcF9vdCkKcHJpbnQoY2hvc2VuX2dlbmVzKQpzY29yZSA8LSBhcHBseSh4ZW5vQGFzc2F5cyRSTkFAZGF0YVtjaG9zZW5fZ2VuZXMsXSwyLG1lYW4pCnhlbm89QWRkTWV0YURhdGEoeGVubyxzY29yZSxwYXN0ZTAoaGFsbG1hcmtfbmFtZSwiaW50ZXJzZWN0ZWQiKSkKc2NvcmVzQW5kSW5kaWNlcyA8LSBnZXRQYXRod2F5U2NvcmVzKHhlbm9AYXNzYXlzJFJOQUBkYXRhLCBjaG9zZW5fZ2VuZXMpCnhlbm89QWRkTWV0YURhdGEoeGVubyxzY29yZXNBbmRJbmRpY2VzJHBhdGh3YXlTY29yZXMscGFzdGUwKGhhbGxtYXJrX25hbWUsIl9pbnRlcnNlY3RlZF9zaXBzaWMiKSkKcHJpbnRfdGFiKEZlYXR1cmVQbG90KG9iamVjdCA9IHhlbm8sIGZlYXR1cmVzID0gcGFzdGUwKGhhbGxtYXJrX25hbWUsIl9pbnRlcnNlY3RlZF9zaXBzaWMiKSksdGl0bGUgPSAiRXhwcmVzc2lvbiIpCnByaW50X3RhYihGZWF0dXJlUGxvdChvYmplY3QgPSB4ZW5vLCBmZWF0dXJlcyA9IHBhc3RlMChoYWxsbWFya19uYW1lLCJpbnRlcnNlY3RlZCIpKSx0aXRsZSA9ICJFeHByZXNzaW9uIikKYGBgCmBgYHtyfQpjb3JfcmVzID0gY29yKHhlbm8kVE5GYSx4ZW5vW1twYXN0ZTAoaGFsbG1hcmtfbmFtZSwiaW50ZXJzZWN0ZWQiKV1dKQpwcmludChwYXN0ZSgiY29ycmVsYXRpb24gb2YgVE5GYSBwcm9ncmFtIHRvIiwgcGFzdGUwKGhhbGxtYXJrX25hbWUsImludGVyc2VjdGVkIiksIjoiLCBjb3JfcmVzKSkKCmBgYAojICBOb3JtIHBhdGh3YXlzICB7LnRhYnNldH0KYGBge3IgZmlnLndpZHRoPTEwfQpzdW1fc2NvcmVzPSB4ZW5vJEhJRl90YXJnZXRzK3hlbm8kS0VHR19BTlRJR0VOX1BST0NFU1NJTkdfQU5EX1BSRVNFTlRBVElPTit4ZW5vJEhBTExNQVJLX0cyTV9DSEVDS1BPSU5UCgpuZXdfc2NvcmUgPSB4ZW5vJEtFR0dfQU5USUdFTl9QUk9DRVNTSU5HX0FORF9QUkVTRU5UQVRJT04vIHN1bV9zY29yZXMKeGVubyAlPD4lIEFkZE1ldGFEYXRhKG1ldGFkYXRhID0gbmV3X3Njb3JlLGNvbC5uYW1lID0gIktFR0dfQU5USUdFTl9QUk9DRVNTSU5HX0FORF9QUkVTRU5UQVRJT05fbm9ybSIpCgpuZXdfc2NvcmUgPSAoeGVubyRISUZfdGFyZ2V0cy8gc3VtX3Njb3JlcyklPiUgcmVwbGFjZShpcy5uYSguKSwgMCkKeGVubyAlPD4lIEFkZE1ldGFEYXRhKG1ldGFkYXRhID0gbmV3X3Njb3JlLGNvbC5uYW1lID0gIkhJRl90YXJnZXRzX25vcm0iKQoKbmV3X3Njb3JlID0geGVubyRIQUxMTUFSS19HMk1fQ0hFQ0tQT0lOVC8gc3VtX3Njb3Jlcwp4ZW5vICU8PiUgQWRkTWV0YURhdGEobWV0YWRhdGEgPSBuZXdfc2NvcmUsY29sLm5hbWUgPSAiSEFMTE1BUktfRzJNX25vcm0iKQpGZWF0dXJlUGxvdChvYmplY3QgPSB4ZW5vLGZlYXR1cmVzID0gYygiSEFMTE1BUktfRzJNX25vcm0iLCAiS0VHR19BTlRJR0VOX1BST0NFU1NJTkdfQU5EX1BSRVNFTlRBVElPTl9ub3JtIiwiSElGX3RhcmdldHNfbm9ybSIpKQpgYGAKCmBgYHtyIGVjaG89VFJVRSwgcmVzdWx0cz0nYXNpcyd9Cm1ldGFnZW5lc19tZWFuX2NvbXBhcmUoZGF0YXNldCA9IHhlbm8sdGltZS5wb2ludF92YXIgPSAidHJlYXRtZW50IixwcmVmaXggPSAibW9kZWwiLHBhdGllbnQuaWRlbnRfdmFyID0gIm9yaWcuaWRlbnQiLHByZV9vbiA9IGMoIk5UIiwiT1NJIiwicmVzIikscHJvZ3JhbXMgPSBjKCJUTkZhIiwiSEFMTE1BUktfVE5GQV9TSUdOQUxJTkdfVklBX05GS0IiLCJLRUdHX0FOVElHRU5fUFJPQ0VTU0lOR19BTkRfUFJFU0VOVEFUSU9OIiwiS0VHR19BTlRJR0VOX1BST0NFU1NJTkdfQU5EX1BSRVNFTlRBVElPTl9ub3JtIikpCgpgYGAKCiMgcHJvZ3JhbSAyIGNsdXN0ZXJpbmcKYGBge3J9Cm51bV9vZl9jbHVzdGVycyA9IDcKYW5ub3RhdGlvbiA9IHBsb3RfZ2VuZXNfY29yKGRhdGFzZXQgPSB4ZW5vLGhhbGxtYXJrX25hbWUgPSBOVUxMLG51bV9vZl9jbHVzdGVycyA9IG51bV9vZl9jbHVzdGVycyxnZW5lSWRzID0gdG9wX290KQpgYGAKCiMgYWxsIGNsdXN0ZXJzIGV4cHJlc3Npb24gey50YWJzZXR9CmBgYHtyIHJlc3VsdHM9J2FzaXMnLGZpZy53aWR0aD0xNH0KZm9yIChjaG9zZW5fY2x1c3RlcnMgaW4gMTpudW1fb2ZfY2x1c3RlcnMpIHsKICBjaG9zZW5fZ2VuZXMgPSBhbm5vdGF0aW9uW1sibXlhbm5vdGF0aW9uIl1dICU+JSBkcGx5cjo6ZmlsdGVyKGNsdXN0ZXIgPT0gY2hvc2VuX2NsdXN0ZXJzKSAlPiUgcm93bmFtZXMoKSAjdGFrZSByZWxldmFudCBnZW5lcwogICMgcHJpbnQoY2hvc2VuX2dlbmVzKQogIGh5cF9vYmogPC0gaHlwZVIoY2hvc2VuX2dlbmVzLCBnZW5lc2V0c19lbnYsIHRlc3QgPSAiaHlwZXJnZW9tZXRyaWMiLCBmZHI9MSwgcGxvdHRpbmc9RixiYWNrZ3JvdW5kID0gcm93bmFtZXMoZ2VwX3Njb3JlcykpCgogICBzY29yZXNBbmRJbmRpY2VzIDwtIGdldFBhdGh3YXlTY29yZXMoeGVub0Bhc3NheXMkUk5BQGRhdGEsIGNob3Nlbl9nZW5lcykKICB4ZW5vPUFkZE1ldGFEYXRhKHhlbm8sc2NvcmVzQW5kSW5kaWNlcyRwYXRod2F5U2NvcmVzLHBhc3RlMCgiY2x1c3RlciIsY2hvc2VuX2NsdXN0ZXJzKSkKCiAgCiAgcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICAgIGh5cF9kb3RzKGh5cF9vYmosc2l6ZV9ieSA9ICJub25lIix0aXRsZSA9IHBhc3RlMCgiY2x1c3RlciIsY2hvc2VuX2NsdXN0ZXJzKSkrCiAgICAgICAgICAgICAgRmVhdHVyZVBsb3Qob2JqZWN0ID0geGVubyxmZWF0dXJlcyA9IHBhc3RlMCgiY2x1c3RlciIsY2hvc2VuX2NsdXN0ZXJzKSksCiAgICAgICAgICAgIHRpdGxlID0gY2hvc2VuX2NsdXN0ZXJzKQogIAogIGNvcl9yZXMgPSBjb3IoeGVubyRUTkZhLHhlbm9bW3Bhc3RlMCgiY2x1c3RlciIsY2hvc2VuX2NsdXN0ZXJzKV1dKQojIHByaW50KHBhc3RlKCJjb3JyZWxhdGlvbiBvZiBUTkZhIHByb2dyYW0gdG8iLCBwYXN0ZTAoImNsdXN0ZXIiLGNob3Nlbl9jbHVzdGVycyksIjoiLCBjb3JfcmVzKSkKCgp9CgoKYGBgCiMgY29ycmVsYXRpb24gb2YgYWxsIGNsdXN0ZXJzCmBgYHtyICxmaWcud2lkdGg9MTR9CmZvciAoY2hvc2VuX2NsdXN0ZXJzIGluIDE6bnVtX29mX2NsdXN0ZXJzKSB7CiAgY2hvc2VuX2dlbmVzID0gYW5ub3RhdGlvbltbIm15YW5ub3RhdGlvbiJdXSAlPiUgZHBseXI6OmZpbHRlcihjbHVzdGVyID09IGNob3Nlbl9jbHVzdGVycykgJT4lIHJvd25hbWVzKCkgI3Rha2UgcmVsZXZhbnQgZ2VuZXMKICBjb3JfcmVzID0gY29yKHhlbm8kVE5GYSx4ZW5vW1twYXN0ZTAoImNsdXN0ZXIiLGNob3Nlbl9jbHVzdGVycyldXSkKICBwcmludChwYXN0ZSgiY29ycmVsYXRpb24gb2YgVE5GYSBwcm9ncmFtIHRvIiwgcGFzdGUwKCJjbHVzdGVyIixjaG9zZW5fY2x1c3RlcnMpLCI6IiwgY29yX3JlcykpCgp9CgoKYGBgCiMgY2x1c3RlcnMgNCBhbmQgNSAKYGBge3IgZmlnLndpZHRoPTE0fQpjaG9zZW5fY2x1c3RlcnMgPSBjKDQsNSkKICBjaG9zZW5fZ2VuZXMgPSBhbm5vdGF0aW9uW1sibXlhbm5vdGF0aW9uIl1dICU+JSBkcGx5cjo6ZmlsdGVyKGNsdXN0ZXIgJWluJSBjaG9zZW5fY2x1c3RlcnMpICU+JSByb3duYW1lcygpICN0YWtlIHJlbGV2YW50IGdlbmVzCiAgCiAgaHlwX29iaiA8LSBoeXBlUihjaG9zZW5fZ2VuZXMsIGdlbmVzZXRzX2VudiwgdGVzdCA9ICJoeXBlcmdlb21ldHJpYyIsIGZkcj0xLCBwbG90dGluZz1GLGJhY2tncm91bmQgPSByb3duYW1lcyhnZXBfc2NvcmVzKSkKCiAgIHNjb3Jlc0FuZEluZGljZXMgPC0gZ2V0UGF0aHdheVNjb3Jlcyh4ZW5vQGFzc2F5cyRSTkFAZGF0YSwgY2hvc2VuX2dlbmVzKQogICBzY29yZV9uYW1lID0gcGFzdGUwKCJjbHVzdGVyXyIsIHBhc3RlMChjaG9zZW5fY2x1c3RlcnMsIGNvbGxhcHNlID0gIl8iKSkKICB4ZW5vPUFkZE1ldGFEYXRhKHhlbm8sc2NvcmVzQW5kSW5kaWNlcyRwYXRod2F5U2NvcmVzLHNjb3JlX25hbWUpCgogIGh5cF9kb3RzKGh5cF9vYmosc2l6ZV9ieSA9ICJub25lIikrCiAgICBGZWF0dXJlUGxvdChvYmplY3QgPSB4ZW5vLGZlYXR1cmVzID0gc2NvcmVfbmFtZSkKCiAgY29yX3JlcyA9IGNvcih4ZW5vJFRORmEseGVub1tbc2NvcmVfbmFtZV1dKQpwcmludChwYXN0ZSgiY29ycmVsYXRpb24gb2YgVE5GYSBwcm9ncmFtIHRvIiwgc2NvcmVfbmFtZSwiOiIsIGNvcl9yZXMpKQpgYGAKIyBUaXRsZSB7LnRhYnNldH0KCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KbGlicmFyeShiaW9tYVJ0KQplbnNlbWJsID0gdXNlRW5zZW1ibChiaW9tYXJ0PSJlbnNlbWJsIiwgZGF0YXNldD0iaHNhcGllbnNfZ2VuZV9lbnNlbWJsIikKCmZvciAoY2hvc2VuX2NsdXN0ZXJzIGluIDE6bnVtX29mX2NsdXN0ZXJzKSB7CiAgY2hvc2VuX2dlbmVzID0gYW5ub3RhdGlvbltbIm15YW5ub3RhdGlvbiJdXSAlPiUgZHBseXI6OmZpbHRlcihjbHVzdGVyID09IGNob3Nlbl9jbHVzdGVycykgJT4lIHJvd25hbWVzKCkgI3Rha2UgcmVsZXZhbnQgZ2VuZXMKICBnZW5lZGVzYyA8LSBnZXRCTShhdHRyaWJ1dGVzPWMoJ2V4dGVybmFsX2dlbmVfbmFtZScsJ3dpa2lnZW5lX2Rlc2NyaXB0aW9uJywiaW50ZXJwcm9fZGVzY3JpcHRpb24iKSwgZmlsdGVycyA9ICdleHRlcm5hbF9nZW5lX25hbWUnLCB2YWx1ZXMgPSBjaG9zZW5fZ2VuZXMsIG1hcnQgPWVuc2VtYmwpICAKICBwcmludF90YWIoZ2VuZWRlc2MsdGl0bGUgPSBjaG9zZW5fY2x1c3RlcnMpCn0KCgpgYGAKCiMgUHJvZ3JhbXMgcmVndWxhdGlvbiB7LnRhYnNldH0KYGBge3IgZWNobz1UUlVFLCByZXN1bHRzPSdhc2lzJ30KbWV0YWdlbmVzX21lYW5fY29tcGFyZShkYXRhc2V0ID0geGVubyx0aW1lLnBvaW50X3ZhciA9ICJ0cmVhdG1lbnQiLHByZWZpeCA9ICJtb2RlbCIscGF0aWVudC5pZGVudF92YXIgPSAib3JpZy5pZGVudCIscHJlX29uID0gYygiTlQiLCJPU0kiLCJyZXMiKSxwcm9ncmFtcyA9IGMoIlRORmEiLCJIQUxMTUFSS19UTkZBX1NJR05BTElOR19WSUFfTkZLQiIsIktFR0dfQU5USUdFTl9QUk9DRVNTSU5HX0FORF9QUkVTRU5UQVRJT04iLCJLRUdHX0FOVElHRU5fUFJPQ0VTU0lOR19BTkRfUFJFU0VOVEFUSU9OaW50ZXJzZWN0ZWQiLCJjbHVzdGVyXzRfNSIsImNsdXN0ZXI3IikpCgpgYGAKCiMgSElGX3RhcmdldHMtIEh5cG94aWEgY29ycmVsYXRpb24gIHsudGFic2V0fQpgYGB7ciBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTYsIHJlc3VsdHM9J2FzaXMnfQojIHBsb3QgY29ycmVsYXRpb24gZm9yIGV2ZXJ5IHN1YnNldCBvZiBoaWYgdGFyZ2V0cwpmb3IgKGdlbmVzIGluIGxpc3QoaGlmX3RhcmdldHMseGVub19jbHVzdGVyXzNfZ2VuZXMseGVub19jbHVzdGVyXzNfMl9nZW5lcykpIHsKICBoaWZfdGFyZ2V0c19ieV90cCA9IEZldGNoRGF0YShvYmplY3QgPSB4ZW5vLHZhcnMgPSBjKGdlbmVzKSkgJT4lIHJvd1N1bXMoKSAlPiUgYXMuZGF0YS5mcmFtZSgpICNtZWFuIGV4cHJlc3Npb24KICBoaWZfdGFyZ2V0c19ieV90cFssMl0gPSB4ZW5vJEh5cG94aWEKICAKICBuYW1lcyhoaWZfdGFyZ2V0c19ieV90cCkgPSBjKCJoaWZfdGFyZ2V0cyIsImh5cG94aWFfcHJvZ3JhbSIpCiAgCiAgCiAgCiAgcDEgPSBnZ3Bsb3QoaGlmX3RhcmdldHNfYnlfdHAsIGFlcyh4PWhpZl90YXJnZXRzLCB5PWh5cG94aWFfcHJvZ3JhbSkpICsgCiAgICAgIGdlb21fcG9pbnQoKSsKICAgIGdlb21fZGVuc2l0eV8yZChhZXMoY29sb3IgPSAuLmxldmVsLi4pKSArCiAgICBnZW9tX3Ntb290aChtZXRob2Q9bG0pICsKICAgIHN0YXRfY29yKG1ldGhvZCA9ICJwZWFyc29uIiwgbGFiZWwueCA9IDIwLCBsYWJlbC55ID0gMS4xKSsKICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpCiAgCiAgcDIgPSBnZ3Bsb3QoaGlmX3RhcmdldHNfYnlfdHAsIGFlcyh4PWhpZl90YXJnZXRzLCB5PWh5cG94aWFfcHJvZ3JhbSkpICsgCiAgICBnZW9tX2JpbjJkKCkgKwogICAgdGhlbWVfYncoKSsgc2NhbGVfZmlsbF9ncmFkaWVudG4obGltaXRzPWMoMCwxMTAwKSwgYnJlYWtzPXNlcSgwLCAxMTAwLCBieT0yMDApLCBjb2xvdXJzPWMoImJsdWUiLCJ5ZWxsb3ciLCJyZWQiKSkrIAogICAgc3RhdF9jb3IobWV0aG9kID0gInBlYXJzb24iLCBsYWJlbC54ID0gMjAsIGxhYmVsLnkgPSAxLjEpKwogICAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtKSAKICAKICBwID0gZ2dhcnJhbmdlKHBsb3RsaXN0ID0gbGlzdChwMSxwMiksbnJvdyAgPSAyKSAgCiAgCiAgcHJpbnRfdGFiKHBsdCA9IHAsdGl0bGUgPSAiZ2VvbV9iaW4yZCIpCn0KCgpgYGAKCiMgVU1BUFMKYGBge3IgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTB9CmhpZl90YXJnZXRzX2J5X3RwID0gRmV0Y2hEYXRhKG9iamVjdCA9IHhlbm8sdmFycyA9IGMoaGlmX3RhcmdldHMpKSAlPiUgcm93U3VtcygpICU+JSBhcy5kYXRhLmZyYW1lKCkgI21lYW4gZXhwcmVzc2lvbgpoaWZfdGFyZ2V0c19ieV90cFssMl0gPSB4ZW5vJEh5cG94aWEKbmFtZXMoaGlmX3RhcmdldHNfYnlfdHApID0gYygiaGlmX3RhcmdldHMiLCJoeXBveGlhX3Byb2dyYW0iKQoKaGlnaF9oaWZfbG93X2h5cG94aWFfY2VsbHMgPSBoaWZfdGFyZ2V0c19ieV90cCAlPiUgZmlsdGVyKGhpZl90YXJnZXRzPjI1ICYgaHlwb3hpYV9wcm9ncmFtIDwgMC4yKSAlPiUgcm93bmFtZXMoKQpsb3dfaGlmX2hpZ2hfaHlwb3hpYV9jZWxscyA9IGhpZl90YXJnZXRzX2J5X3RwICU+JSBmaWx0ZXIoaGlmX3RhcmdldHM8MTUgJiBoeXBveGlhX3Byb2dyYW0gPiAwLjYpICU+JSByb3duYW1lcygpCgpoaWZfdGFyZ2V0c19ieV90cCA9IEZldGNoRGF0YShvYmplY3QgPSB4ZW5vLHZhcnMgPSBjKGhpZl90YXJnZXRzKSkgJT4lIHJvd1N1bXMoKSAlPiUgYXMuZGF0YS5mcmFtZSgpICNtZWFuIGV4cHJlc3Npb24KeGVubyA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IHhlbm8sIG1ldGFkYXRhID0gaGlmX3RhcmdldHNfYnlfdHAsY29sLm5hbWUgPSAiSElGX3RhcmdldHNfc2NvcmUiKQpjZWxsc190b19oaWdobGlnaHQgPSAgbGlzdChoaWdoX2hpZl9sb3dfaHlwb3hpYV9jZWxscyA9IGhpZ2hfaGlmX2xvd19oeXBveGlhX2NlbGxzLCBsb3dfaGlmX2hpZ2hfaHlwb3hpYV9jZWxscyA9IGxvd19oaWZfaGlnaF9oeXBveGlhX2NlbGxzKQoKRGltUGxvdChvYmplY3QgPSB4ZW5vLCBjZWxscy5oaWdobGlnaHQgPSBjZWxsc190b19oaWdobGlnaHQsIGNvbHMuaGlnaGxpZ2h0ID0gYygicmVkIiwiYmx1ZSIpLCBjb2xzID0gImdyYXkiLCBvcmRlciA9IFRSVUUpCkZlYXR1cmVQbG90KG9iamVjdCA9IHhlbm8sZmVhdHVyZXMgPSBjKCAiSElGX3RhcmdldHNfc2NvcmUiLCJIeXBveGlhIiwiQ2VsbF9jeWNsZSIgKSkKCmBgYApgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KaGlmX2h5cG94aWFfY29ycmVsYXRlZF9jZWxscyA9IGNvbG5hbWVzKHhlbm8pIFshY29sbmFtZXMoeGVubykgJWluJSBoaWdoX2hpZl9sb3dfaHlwb3hpYV9jZWxscyAmICFjb2xuYW1lcyh4ZW5vKSAlaW4lIGhpZ2hfaGlmX2xvd19oeXBveGlhX2NlbGxzXQpoaWZfdGFyZ2V0c19zY29yZSA9IEZldGNoRGF0YShvYmplY3QgPSB4ZW5vLHZhcnMgPSBjKGhpZl90YXJnZXRzKSkgJT4lIHJvd1N1bXMoKSAlPiUgYXMuZGF0YS5mcmFtZSgpICNtZWFuIGV4cHJlc3Npb24KaGlmX3RhcmdldHNfc2NvcmVbLDJdID0geGVubyRIeXBveGlhCm5hbWVzKGhpZl90YXJnZXRzX3Njb3JlKSA9IGMoImhpZl90YXJnZXRzIiwiaHlwb3hpYV9wcm9ncmFtIikKaGlmX3RhcmdldHNfc2NvcmUgID0gaGlmX3RhcmdldHNfc2NvcmUgJT4lIG11dGF0ZSh0eXBlID0gY2FzZV93aGVuKGhpZl90YXJnZXRzPjI1ICYgaHlwb3hpYV9wcm9ncmFtIDwgMC4yIH4gImhpZ2hfaGlmX2xvd19oeXBveGlhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpZl90YXJnZXRzPDE1ICYgaHlwb3hpYV9wcm9ncmFtID4gMC42IH4gImxvd19oaWZfaGlnaF9oeXBveGlhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpZl90YXJnZXRzPjI1ICYgaHlwb3hpYV9wcm9ncmFtID4gMC42IH4gImhpZ2hfaGlmX2hpZ2hfaHlwb3hpYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoaWZfdGFyZ2V0czwxNSAmIGh5cG94aWFfcHJvZ3JhbSA8MC4yIH4gImhpZ2hfaGlmX2hpZ2hfaHlwb3hpYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIm90aGVyIikpCnhlbm8gPSBBZGRNZXRhRGF0YShvYmplY3QgPSB4ZW5vLG1ldGFkYXRhID0gaGlmX3RhcmdldHNfc2NvcmVbLCJ0eXBlIiwgZHJvcCA9IEZdLGNvbC5uYW1lID0gInNjb3JlX2NvcnJlbGF0aW9uIikKeGVubyA9IFNldElkZW50KG9iamVjdCA9IHhlbm8sdmFsdWUgPSAic2NvcmVfY29ycmVsYXRpb24iKQpEaW1QbG90KG9iamVjdCA9IHhlbm8sZ3JvdXAuYnkgPSAic2NvcmVfY29ycmVsYXRpb24iKQptYXJrZXJzID0gRmluZE1hcmtlcnMob2JqZWN0ID0geGVubywgaWRlbnQuMSA9ICJoaWdoX2hpZl9sb3dfaHlwb3hpYSIsaWRlbnQuMiA9ICJoaWdoX2hpZl9oaWdoX2h5cG94aWEiLGRlbnNpZnkgPSBUKQpgYGAKYGBge3IgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD02LCByZXN1bHRzPSdhc2lzJ30KdXBkZWcgPSBtYXJrZXJzICU+JSBmaWx0ZXIocF92YWxfYWRqPDAuMDUgJiBhdmdfbG9nMkZDPjApICU+JSByb3duYW1lcygpCgpuZXdfaGlmX3RhcmdldHMgPSBoaWZfdGFyZ2V0c1shaGlmX3RhcmdldHMgJWluJSB1cGRlZ10KaGlmX3RhcmdldHNfYnlfdHAgPSBGZXRjaERhdGEob2JqZWN0ID0geGVubyx2YXJzID0gYyhuZXdfaGlmX3RhcmdldHMpKSAlPiUgcm93U3VtcygpICU+JSBhcy5kYXRhLmZyYW1lKCkgI21lYW4gZXhwcmVzc2lvbgogIGhpZl90YXJnZXRzX2J5X3RwWywyXSA9IHhlbm8kSHlwb3hpYQogIAogIG5hbWVzKGhpZl90YXJnZXRzX2J5X3RwKSA9IGMoImhpZl90YXJnZXRzIiwiaHlwb3hpYV9wcm9ncmFtIikKICAKICAKICAKICBwMSA9IGdncGxvdChoaWZfdGFyZ2V0c19ieV90cCwgYWVzKHg9aGlmX3RhcmdldHMsIHk9aHlwb3hpYV9wcm9ncmFtKSkgKyAKICAgICAgZ2VvbV9wb2ludCgpKwogICAgZ2VvbV9kZW5zaXR5XzJkKGFlcyhjb2xvciA9IC4ubGV2ZWwuLikpICsKICAgIGdlb21fc21vb3RoKG1ldGhvZD1sbSkgKwogICAgc3RhdF9jb3IobWV0aG9kID0gInBlYXJzb24iLCBsYWJlbC54ID0gMjAsIGxhYmVsLnkgPSAxLjEpKwogICAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkKICAKICBwMiA9IGdncGxvdChoaWZfdGFyZ2V0c19ieV90cCwgYWVzKHg9aGlmX3RhcmdldHMsIHk9aHlwb3hpYV9wcm9ncmFtKSkgKyAKICAgIGdlb21fYmluMmQoKSArCiAgICB0aGVtZV9idygpKyBzY2FsZV9maWxsX2dyYWRpZW50bihsaW1pdHM9YygwLDExMDApLCBicmVha3M9c2VxKDAsIDExMDAsIGJ5PTIwMCksIGNvbG91cnM9YygiYmx1ZSIsInllbGxvdyIsInJlZCIpKSsgCiAgICBzdGF0X2NvcihtZXRob2QgPSAicGVhcnNvbiIsIGxhYmVsLnggPSAyMCwgbGFiZWwueSA9IDEuMSkrCiAgICBnZW9tX3Ntb290aChtZXRob2Q9bG0pIAogIAogIHAgPSBnZ2FycmFuZ2UocGxvdGxpc3QgPSBsaXN0KHAxLHAyKSxucm93ICA9IDIpICAKICAKICBwcmludF90YWIocGx0ID0gcCx0aXRsZSA9ICJnZW9tX2JpbjJkIikKCgpgYGAKCgpgYGB7cn0KdXByZWdfaGlmX3RhcmdldHMgPSBoaWZfdGFyZ2V0c1toaWZfdGFyZ2V0cyAlaW4lIHVwZGVnXQp1cHJlZ19oaWZfdGFyZ2V0c19leHByID0gRmV0Y2hEYXRhKG9iamVjdCA9IHhlbm8sdmFycyA9IGModXByZWdfaGlmX3RhcmdldHMpKSAlPiUgcm93U3VtcygpICU+JSBhcy5kYXRhLmZyYW1lKCkgI21lYW4gZXhwcmVzc2lvbgp4ZW5vID0gQWRkTWV0YURhdGEob2JqZWN0ID0geGVubywgbWV0YWRhdGEgPSB1cHJlZ19oaWZfdGFyZ2V0c19leHByLGNvbC5uYW1lID0gInVwcmVnX2hpZl90YXJnZXRzX3Njb3JlIikKRmVhdHVyZVBsb3Qob2JqZWN0ID0geGVubyxmZWF0dXJlcyA9ICJ1cHJlZ19oaWZfdGFyZ2V0c19zY29yZSIpCmBgYAoKCiMgQ2FsY3VsYXRlIHVzYWdlIHdpdGhvdXQgY2MgaW4gc3VtIHRvIDEKYGBge3B5dGhvbn0KaW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCBzY2FucHkgYXMgc2MKeGVub19leHByZXNzaW9uID0gci54ZW5vX2V4cHJlc3Npb24KeGVub192YXJnZW5lcyA9IHIueGVub192YXJnZW5lcwp0cG0gPSAgY29tcHV0ZV90cG0oeGVub19leHByZXNzaW9uKQp1c2FnZV9ieV9jYWxjID0gZ2V0X3VzYWdlX2Zyb21fc2NvcmUoY291bnRzPXhlbm9fZXhwcmVzc2lvbix0cG09dHBtLGdlbmVzPXhlbm9fdmFyZ2VuZXMsIGNubWZfb2JqPWNubWZfb2JqLGs9MyxzdW1UbzE9RmFsc2UpCmBgYAoKYGBge3J9CmFsbF9tZXRhZ2VuZXNfbm9TdW1UbzEgPSBweSR1c2FnZV9ieV9jYWxjCnRuZl9hbmRfaHlwb3hpYSA9IGFsbF9tZXRhZ2VuZXNfbm9TdW1UbzFbLDE6Ml0KdG5mX2FuZF9oeXBveGlhID0gYXBwbHkoWCA9IHRuZl9hbmRfaHlwb3hpYSwgTUFSR0lOID0gMSwgc3VtXzJfb25lKSAlPiUgdCgpICU+JSAgYXMuZGF0YS5mcmFtZSgpCnRuZl9hbmRfaHlwb3hpYVtpcy5uYSh0bmZfYW5kX2h5cG94aWEpXSA8LSAwICNyZXBsYWNlIE5BTidzIHdpdGggMC4KYGBgCgpgYGB7ciBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTYsIHJlc3VsdHM9J2FzaXMnfQojIHBsb3QgY29ycmVsYXRpb24gZm9yIGV2ZXJ5IHN1YnNldCBvZiBoaWYgdGFyZ2V0cwpmb3IgKGdlbmVzIGluIGxpc3QoaGlmX3RhcmdldHMseGVub19jbHVzdGVyXzNfZ2VuZXMseGVub19jbHVzdGVyXzNfMl9nZW5lcykpIHsKICBoaWZfdGFyZ2V0c19ieV90cCA9IEZldGNoRGF0YShvYmplY3QgPSB4ZW5vLHZhcnMgPSBjKGdlbmVzKSkgJT4lIHJvd1N1bXMoKSAlPiUgYXMuZGF0YS5mcmFtZSgpICNtZWFuIGV4cHJlc3Npb24KICBoaWZfdGFyZ2V0c19ieV90cFssMl0gPSB0bmZfYW5kX2h5cG94aWFbLDFdCiAgIyBoaWZfdGFyZ2V0c19ieV90cFssMl0gPSB4ZW5vJEh5cG94aWEKICAKICBuYW1lcyhoaWZfdGFyZ2V0c19ieV90cCkgPSBjKCJoaWZfdGFyZ2V0cyIsImh5cG94aWFfcHJvZ3JhbSIpCiAgCiAgCiAgCiAgcDEgPSBnZ3Bsb3QoaGlmX3RhcmdldHNfYnlfdHAsIGFlcyh4PWhpZl90YXJnZXRzLCB5PWh5cG94aWFfcHJvZ3JhbSkpICsgCiAgICAgIGdlb21fcG9pbnQoKSsKICAgIGdlb21fZGVuc2l0eV8yZChhZXMoY29sb3IgPSAuLmxldmVsLi4pKSArCiAgICBnZW9tX3Ntb290aChtZXRob2Q9bG0pICsKICAgIHN0YXRfY29yKG1ldGhvZCA9ICJwZWFyc29uIiwgbGFiZWwueCA9IDIwLCBsYWJlbC55ID0gMS4xKSsKICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpCiAgCiAgcDIgPSBnZ3Bsb3QoaGlmX3RhcmdldHNfYnlfdHAsIGFlcyh4PWhpZl90YXJnZXRzLCB5PWh5cG94aWFfcHJvZ3JhbSkpICsgCiAgICBnZW9tX2JpbjJkKCkgKwogICAgdGhlbWVfYncoKSsgc2NhbGVfZmlsbF9ncmFkaWVudG4obGltaXRzPWMoMCwxMTAwKSwgYnJlYWtzPXNlcSgwLCAxMTAwLCBieT0yMDApLCBjb2xvdXJzPWMoImJsdWUiLCJ5ZWxsb3ciLCJyZWQiKSkrIAogICAgc3RhdF9jb3IobWV0aG9kID0gInBlYXJzb24iLCBsYWJlbC54ID0gMjAsIGxhYmVsLnkgPSAxLjEpKwogICAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtKSAKICAKICBwID0gZ2dhcnJhbmdlKHBsb3RsaXN0ID0gbGlzdChwMSxwMiksbnJvdyAgPSAyKSAgCiAgCiAgcHJpbnRfdGFiKHBsdCA9IHAsdGl0bGUgPSAiZ2VvbV9iaW4yZCIpCn0KCgpgYGAKCiMgVU1BUFMKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OH0KaGlmX3RhcmdldHNfYnlfdHAgPSBGZXRjaERhdGEob2JqZWN0ID0geGVubyx2YXJzID0gYyhoaWZfdGFyZ2V0cykpICU+JSByb3dTdW1zKCkgJT4lIGFzLmRhdGEuZnJhbWUoKSAjbWVhbiBleHByZXNzaW9uCmhpZl90YXJnZXRzX2J5X3RwWywyXSA9IHRuZl9hbmRfaHlwb3hpYVssMV0KbmFtZXMoaGlmX3RhcmdldHNfYnlfdHApID0gYygiaGlmX3RhcmdldHMiLCJoeXBveGlhX3Byb2dyYW0iKQoKaGlnaF9oaWZfbG93X2h5cG94aWFfY2VsbHMgPSBoaWZfdGFyZ2V0c19ieV90cCAlPiUgZmlsdGVyKGhpZl90YXJnZXRzPjI1ICYgaHlwb3hpYV9wcm9ncmFtIDwgMC4yKSAlPiUgcm93bmFtZXMoKQoKaGlmX3RhcmdldHNfYnlfdHAgPSBGZXRjaERhdGEob2JqZWN0ID0geGVubyx2YXJzID0gYyhoaWZfdGFyZ2V0cykpICU+JSByb3dTdW1zKCkgJT4lIGFzLmRhdGEuZnJhbWUoKSAjbWVhbiBleHByZXNzaW9uCnhlbm8gPSBBZGRNZXRhRGF0YShvYmplY3QgPSB4ZW5vLCBtZXRhZGF0YSA9IGhpZl90YXJnZXRzX2J5X3RwLGNvbC5uYW1lID0gIkhJRl90YXJnZXRzX3Njb3JlIikKeGVubyA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IHhlbm8sIG1ldGFkYXRhID0gdG5mX2FuZF9oeXBveGlhWywxXSxjb2wubmFtZSA9ICJIeXBveGlhMiIpCgpEaW1QbG90KG9iamVjdCA9IHhlbm8sIGNlbGxzLmhpZ2hsaWdodCA9IGhpZ2hfaGlmX2xvd19oeXBveGlhX2NlbGxzLCBjb2xzLmhpZ2hsaWdodCA9ICJyZWQiLCBjb2xzID0gImdyYXkiLCBvcmRlciA9IFRSVUUpCkZlYXR1cmVQbG90KG9iamVjdCA9IHhlbm8sZmVhdHVyZXMgPSBjKCAiSElGX3RhcmdldHNfc2NvcmUiLCJIeXBveGlhMiIsIkNlbGxfY3ljbGUiICkpCkZlYXR1cmVQbG90KG9iamVjdCA9IHhlbm8sZmVhdHVyZXMgPSBjKCJIeXBveGlhMiIpKQpEaW1QbG90KG9iamVjdCA9IHhlbm8sZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpCgpgYGAKCiMgSHlwb3hpYSByYXcKYGBge3IgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9Cnhlbm8gPSBBZGRNZXRhRGF0YShvYmplY3QgPSB4ZW5vLG1ldGFkYXRhID0gYWxsX21ldGFnZW5lc19ub1N1bVRvMVssMV0sY29sLm5hbWUgPSAiaHlwb3hpYV9yYXciKQpGZWF0dXJlUGxvdChvYmplY3QgPSB4ZW5vLGZlYXR1cmVzID0gImh5cG94aWFfcmF3IikgKyBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3VycyA9IHJhaW5ib3coNSksIGxpbWl0cyA9IGMoMCwgMzAwMCkpCmBgYAoKCgo8c2NyaXB0IHNyYz0iaHR0cHM6Ly9oeXBvdGhlcy5pcy9lbWJlZC5qcyIgYXN5bmM+PC9zY3JpcHQ+Cg==