cnmf
programs are by plate
from cnmf import cNMF
import pickle
nfeatures = "2K"
f = open('./Data/cNMF/HMSC_cNMF_' + nfeatures+ 'vargenes/cnmf_obj.pckl', 'rb')
cnmf_obj = pickle.load(f)
f.close()
selected_k = 4
density_threshold = 0.1
cnmf_obj.consensus(k=selected_k, density_threshold=density_threshold,show_clustering=True)
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/lib/python3.7/site-packages/scanpy/preprocessing/_simple.py:843: UserWarning: Received a view of an AnnData. Making a copy.
view_to_actual(adata)
usage_norm, gep_scores, gep_tpm, topgenes = cnmf_obj.load_results(K=selected_k, density_threshold=density_threshold)
gep_scores = py$gep_scores
gep_tpm = py$gep_tpm
all_metagenes= py$usage_norm
# Make metagene names
for (i in 1:ncol(all_metagenes)) {
colnames(all_metagenes)[i] = "metagene." %>% paste0(i)
}
#add each metagene to metadata
for (i in 1:ncol(all_metagenes)) {
metage_metadata = all_metagenes %>% select(i)
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = metage_metadata)
}
print_tab(plt =
FeaturePlot(object = acc1_cancer_cells,features = colnames(all_metagenes),combine = T),
title = "metagenes expression")
umap by plate

NA
nmf_vs_plate = FetchData(object = acc1_cancer_cells,vars = c("metagene.1","metagene.2", "orig.ident"))
# myb_vs_cnv $cnv.cluster = as.character(myb_vs_cnv $cnv.cluster )
plt = ggboxplot(nmf_vs_plate, x = "orig.ident", y = "metagene.1",
palette = "jco",
add = "jitter")+
stat_compare_means(method = "wilcox.test",comparisons = list(c("ACC.plate2","ACC1.P3")))+
ggboxplot(nmf_vs_plate, x = "orig.ident", y = "metagene.2",
palette = "jco",
add = "jitter")+
stat_compare_means(method = "wilcox.test",comparisons = list(c("ACC.plate2","ACC1.P3")))
print_tab(plt = plt, title = "plates diff")
plates diff

NA
acc1_cancer_cells = SetIdent(object = acc1_cancer_cells, value = "orig.ident")
print_tab(plt =
DotPlot(object = acc1_cancer_cells, features = c("metagene.1","metagene.2","metagene.3","metagene.4"),scale = F)
,title = "dot plot")
dot plot

NA
regress
nFeature_RNA
#regress nFeature_RNA
acc1_cancer_cells <- ScaleData(acc1_cancer_cells, vars.to.regress = c("nFeature_RNA","percent.mt","nCount_RNA"))
acc1_cancer_cells <- RunPCA(acc1_cancer_cells, features = VariableFeatures(object = acc1_cancer_cells))
ElbowPlot(acc1_cancer_cells, ndims = 50) # checking the dimensionality

pc2use = 1:10
acc1_cancer_cells <- FindNeighbors(acc1_cancer_cells, dims = pc2use)
acc1_cancer_cells <- FindClusters(acc1_cancer_cells, resolution = 0.5)
acc1_cancer_cells <- RunUMAP(acc1_cancer_cells, dims = pc2use)
DimPlot(acc1_cancer_cells,pt.size = 1,group.by = "orig.ident")

cNMF
library(reticulate)
#write expression
nfeatures = 2000
nfeatures_name = (nfeatures/1000) %>% as.character()
acc1_cancer_cells = FindVariableFeatures(object = acc1_cancer_cells,nfeatures = nfeatures)
vargenes = VariableFeatures(object = acc1_cancer_cells)
hmsc_scaled_expression = FetchData(object = acc1_cancer_cells,vars = vargenes, slot = "scale.data")
hmsc_scaled_expression[hmsc_scaled_expression<0]= 0
write.table(x = hmsc_scaled_expression ,file = paste0('./Data/cNMF/hmsc_scaled_expression',nfeatures_name,'Kvargenes.txt'),sep = "\t")
from cnmf import cNMF
import numpy as np
nfeatures_name = r.nfeatures_name
name = 'HMSC_cNMF_scaled_'+nfeatures_name+'Kvargenes'
outdir = './Data/cNMF'
K_range = np.arange(3,10)
cnmf_obj = cNMF(output_dir=outdir, name=name)
counts_fn='./Data/cNMF/hmsc_scaled_expression'+nfeatures_name+'Kvargenes.txt'
tpm_fn = counts_fn ## This is a weird case where because this dataset is not 3' end umi sequencing, we opted to use the TPM matrix as the input matrix rather than the count matrix
cnmf_obj.prepare(counts_fn=counts_fn, components=K_range, seed=14,tpm_fn=tpm_fn)
cnmf_obj.factorize(worker_i=0, total_workers=1)
cnmf_obj.combine()
cnmf_obj.k_selection_plot()
Save object
# import pickle
# f = open('./Data/cNMF/HMSC_cNMF_scaled_'+nfeatures_name+'Kvargenes/cnmf_obj.pckl', 'wb')
# pickle.dump(cnmf_obj, f)
# f.close()
Load object
from cnmf import cNMF
import pickle
nfeatures = "2K"
f = open('./Data/cNMF/HMSC_cNMF_scaled_' + nfeatures+ 'vargenes/cnmf_obj.pckl', 'rb')
cnmf_obj = pickle.load(f)
f.close()
knitr::include_graphics('./Data/cNMF/HMSC_cNMF_scaled_2Kvargenes/HMSC_cNMF_scaled_2Kvargenes.k_selection.png')

selected_k = 4
density_threshold = 0.1
cnmf_obj.consensus(k=selected_k, density_threshold=density_threshold,show_clustering=True)
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/lib/python3.7/site-packages/scanpy/preprocessing/_simple.py:843: UserWarning: Received a view of an AnnData. Making a copy.
view_to_actual(adata)
usage_norm, gep_scores, gep_tpm, topgenes = cnmf_obj.load_results(K=selected_k, density_threshold=density_threshold)
gep_scores = py$gep_scores
gep_tpm = py$gep_tpm
all_metagenes= py$usage_norm
regress
nFeature_RNA programs
# Make metagene names
for (i in 1:ncol(all_metagenes)) {
colnames(all_metagenes)[i] = "metagene." %>% paste0(i)
}
#add each metagene to metadata
for (i in 1:ncol(all_metagenes)) {
metage_metadata = all_metagenes %>% select(i)
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = metage_metadata)
}
print_tab(plt =
FeaturePlot(object = acc1_cancer_cells,features = colnames(all_metagenes),combine = T),
title = "metagenes expression")
umap by plate

NA
nmf_vs_plate = FetchData(object = acc1_cancer_cells,vars = c("metagene.1","metagene.2", "orig.ident"))
# myb_vs_cnv $cnv.cluster = as.character(myb_vs_cnv $cnv.cluster )
plt = ggboxplot(nmf_vs_plate, x = "orig.ident", y = "metagene.1",
palette = "jco",
add = "jitter")+
stat_compare_means(method = "wilcox.test",comparisons = list(c("ACC.plate2","ACC1.P3")))+
ggboxplot(nmf_vs_plate, x = "orig.ident", y = "metagene.2",
palette = "jco",
add = "jitter")+
stat_compare_means(method = "wilcox.test",comparisons = list(c("ACC.plate2","ACC1.P3")))
print_tab(plt = plt, title = "plates diff")
plates diff

NA
acc1_cancer_cells = SetIdent(object = acc1_cancer_cells, value = "orig.ident")
print_tab(plt =
DotPlot(object = acc1_cancer_cells, features = c("metagene.1","metagene.2","metagene.3","metagene.4"),scale = F)
,title = "dot plot")
dot plot

NA
Harmony
acc1_cancer_cells = readRDS(file = "./Data/acc1_cancer_cells_15KnCount_V4.RDS")
sc <- import('scanpy', convert = FALSE)
gene_expression = t(as.matrix(GetAssayData(acc1_cancer_cells,slot='data')))
gene_expression = 2**gene_expression #convert log2(TPM+1) to TPM+1
gene_expression = gene_expression-1 #convert TPM+1 to TPM
adata <- sc$AnnData(
X = gene_expression,
obs = acc1_cancer_cells[[]],
var = GetAssay(acc1_cancer_cells)[[]]
)
rm(gene_expression)
# %matplotlib inline
# %load_ext autoreload
# %autoreload 2
import scipy
import scanpy as sc
import matplotlib.pyplot as plt
import harmonypy
from harmonypy import run_harmony
import sys
from cnmf import cNMF
import numpy as np
import pandas as pd
from sklearn.decomposition import NMF
import seaborn as sns
import yaml
## copied from harmonypy.py
def moe_correct_ridge(Z_orig, Z_cos, Z_corr, R, W, K, Phi_Rk, Phi_moe, lamb):
Z_corr = Z_orig.copy()
for i in range(K):
Phi_Rk = np.multiply(Phi_moe, R[i,:])
x = np.dot(Phi_Rk, Phi_moe.T) + lamb
W = np.dot(np.dot(np.linalg.inv(x), Phi_Rk), Z_orig.T)
W[0,:] = 0 # do not remove the intercept
Z_corr -= np.dot(W.T, Phi_Rk)
Z_cos = Z_corr / np.linalg.norm(Z_corr, ord=2, axis=0)
return Z_cos, Z_corr, W, Phi_Rk
Scale high var genes+
PCA
adata = r.adata
# sc.pp.normalize_per_cell(adata) #Data is already normlized
adata.var['highly_variable'] = adata.var['vst.variable']
adata = adata[:,adata.var['highly_variable']].copy()
sc.pp.scale(adata, zero_center=False, max_value=50)
sc.tl.pca(adata, use_highly_variable=True)
sc.pl.pca_variance_ratio(adata, log=True)
#Run harmony
harmony_res = harmonypy.run_harmony(adata.obsm['X_pca'], adata.obs, 'orig.ident',plot_convergence = True, theta=3.5)
Adujst
correction
X = np.array(adata[:,adata.var['highly_variable']].X)
_, X_corr, _, _ = moe_correct_ridge(X.T, None, None, harmony_res.R, None, harmony_res.K,
None, harmony_res.Phi_moe, harmony_res.lamb)
X_corr = X_corr.T
non negative
correction
adata_corr = adata[:,adata.var['highly_variable']].copy()
X_corr_nonneg = X_corr.copy()
X_corr_nonneg[X_corr_nonneg<0]= 0
adata_corr_nonneg = adata_corr.copy()
adata_corr_nonneg.X = X_corr_nonneg
import to seurat for
plotting UMAP
exprs <- t(py$adata_corr_nonneg$X)
colnames(exprs) <- py$adata$obs_names$to_list()
rownames(exprs) <- py$adata$var_names$to_list()
# Create the Seurat object
lung_corr_nonneg <- CreateSeuratObject(counts = exprs)
lung_corr_nonneg$orig.ident <- py$adata$obs["orig.ident"]
PCA after
correction
lung_corr_nonneg@assays$RNA@scale.data = exprs
lung_corr_nonneg <- RunPCA(lung_corr_nonneg, features = rownames(lung_corr_nonneg),verbose = F)
ElbowPlot(lung_corr_nonneg)
Run dim
reduction
UMAP after
correction
DimPlot(lung_corr_nonneg, reduction = "umap",group.by = "orig.ident")
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

DimPlot(lung_corr_nonneg, reduction = "umap")

sc.write('./Data/cNMF/acc1CancerCells_Harmony_NoNeg.h5ad', adata_corr_nonneg)
cNMF:
name = 'HMSC_cNMF_harmony_2Kvargenes'
outdir = './Data/cNMF'
K_range = np.arange(3,10)
cnmf_obj = cNMF(output_dir=outdir, name=name)
counts_fn='./Data/cNMF/acc1CancerCells_Harmony_NoNeg.h5ad'
tpm_fn = counts_fn
cnmf_obj.prepare(counts_fn=counts_fn, components=K_range, n_iter=10, seed=14,tpm_fn=None,densify=True)
cnmf_obj.factorize(worker_i=0, total_workers=1)
cnmf_obj.combine()
cnmf_obj.k_selection_plot()
knitr::include_graphics("./Data/cNMF/HMSC_cNMF_harmony_2Kvargenes/HMSC_cNMF_harmony_2Kvargenes.k_selection.png")
Save object
#import pickle
#f = open('./Data/cNMF/HMSC_cNMF_harmony_2Kvargenes/cnmf_obj.pckl', 'wb')
#pickle.dump(cnmf_obj, f)
#f.close()
Load object
from cnmf import cNMF
import pickle
nfeatures = "2K"
f = open('./Data/cNMF/HMSC_cNMF_harmony_2Kvargenes/cnmf_obj.pckl', 'rb')
cnmf_obj = pickle.load(f)
f.close()
selected_k = 3
density_threshold = 0.1
cnmf_obj.consensus(k=selected_k, density_threshold=density_threshold,show_clustering=True)
usage_norm, gep_scores, gep_tpm, topgenes = cnmf_obj.load_results(K=selected_k, density_threshold=density_threshold)
gep_scores = py$gep_scores
gep_tpm = py$gep_tpm
all_metagenes= py$usage_norm
Harmony
results
# Make metagene names
for (i in 1:ncol(all_metagenes)) {
colnames(all_metagenes)[i] = "metagene." %>% paste0(i)
}
#add each metagene to metadata
for (i in 1:ncol(all_metagenes)) {
metage_metadata = all_metagenes %>% select(i)
acc1_cancer_cells = AddMetaData(object = acc1_cancer_cells,metadata = metage_metadata)
}
print_tab(plt =
FeaturePlot(object = acc1_cancer_cells,features = colnames(all_metagenes),combine = T),
title = "metagenes expression")
UMAP

all_markers = all_markers %>%
mutate(fdr = p.adjust(p_val,method = "fdr"))%>% #add fdr
dplyr::filter(fdr<0.05) %>% dplyr::filter(abs(avg_log2FC)>1)
canonical_pathways = msigdbr(species = "Homo sapiens", category = "C2") %>% dplyr::filter(gs_subcat != "CGP") %>% dplyr::distinct(gs_name, gene_symbol)
plt_list = list()
for (cluster_num in unique(all_markers$cluster)) {
deg = all_markers %>% dplyr::filter(cluster == cluster_num)
p = enrichment_analysis(markers = deg, background = rownames(lung_corr_nonneg), ident.1 = paste("cluster ",
cluster_num),
ident.2 =paste("cluster ", cluster_num),fdr_Cutoff = 0.05,custom_pathways = canonical_pathways)
plt_list[[cluster_num]] = p
}
print_tab(plt = ggarrange(plotlist = plt_list),title = " clusters enrichment")
clusters enrichment

NA
print_tab(plt = DimPlot(acc1_cancer_cells,pt.size = 1,group.by = "orig.ident")
,title = "umap by plate")
nmf_vs_plate = FetchData(object = acc1_cancer_cells,vars = c("metagene.1","metagene.2", "orig.ident"))
# myb_vs_cnv $cnv.cluster = as.character(myb_vs_cnv $cnv.cluster )
plt = ggboxplot(nmf_vs_plate, x = "orig.ident", y = "metagene.1",
palette = "jco",
add = "jitter")+
stat_compare_means(method = "wilcox.test",comparisons = list(c("ACC.plate2","ACC1.P3")))+
ggboxplot(nmf_vs_plate, x = "orig.ident", y = "metagene.2",
palette = "jco",
add = "jitter")+
stat_compare_means(method = "wilcox.test",comparisons = list(c("ACC.plate2","ACC1.P3")))
print_tab(plt = plt, title = "plates diff")
plates diff

NA
acc1_cancer_cells = SetIdent(object = acc1_cancer_cells, value = "orig.ident")
print_tab(plt =
DotPlot(object = acc1_cancer_cells, features = c("metagene.1","metagene.2","metagene.3","metagene.4"),scale = F)
,title = "dot plot")
plt_list = list()
for (i in 1:ncol(gep_scores)) {
top_genes = gep_scores %>% arrange(desc(gep_scores[i])) #sort by score a
top = head(rownames(top_genes),100) #take top top_genes_num
res = genes_vec_enrichment(genes = top,background = rownames(gep_scores),homer = T,title =
i,silent = T,return_all = T)
plt_list[[i]] = res$plt
}
print_tab(plt = ggarrange(plotlist = plt_list),title = " geps enrichment")

LS0tCnRpdGxlOiAnYHIgcnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLHJlcGxhY2VtZW50ID0gIiIpYCcgCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19jb2xsYXBzZTogeWVzCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvY19kZXB0aDogMQotLS0KCiMjIFBhcmFtZXRlcnMKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CgpgYGAKCgojIyBmdW5jdGlvbnMKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CnNvdXJjZV9mcm9tX2dpdGh1YihyZXBvc2l0b3kgPSAiREVHX2Z1bmN0aW9ucyIsdmVyc2lvbiA9ICIwLjIuMyIpCmBgYAoKIyMgRGF0YQoKYGBge3J9CmFjYzFfY2FuY2VyX2NlbGxzID0gcmVhZFJEUyhmaWxlID0gIi4vRGF0YS9hY2MxX2NhbmNlcl9jZWxsc18xNUtuQ291bnRfVjQuUkRTIikKYGBgCiMgVGl0bGUgCgpgYGB7ciBlY2hvPVRSVUUsIHJlc3VsdHM9J2FzaXMnfQoKYGBgCiMgY25tZiBwcm9ncmFtcyBhcmUgYnkgcGxhdGUgey50YWJzZXR9CgpgYGB7cHl0aG9ufQpmcm9tIGNubWYgaW1wb3J0IGNOTUYKaW1wb3J0IHBpY2tsZQpuZmVhdHVyZXMgPSAiMksiCmYgPSBvcGVuKCcuL0RhdGEvY05NRi9ITVNDX2NOTUZfJyArIG5mZWF0dXJlcysgJ3ZhcmdlbmVzL2NubWZfb2JqLnBja2wnLCAncmInKQpjbm1mX29iaiA9IHBpY2tsZS5sb2FkKGYpCmYuY2xvc2UoKQpgYGAKCgpgYGB7cHl0aG9ufQpzZWxlY3RlZF9rID0gNApkZW5zaXR5X3RocmVzaG9sZCA9IDAuMQpjbm1mX29iai5jb25zZW5zdXMoaz1zZWxlY3RlZF9rLCBkZW5zaXR5X3RocmVzaG9sZD1kZW5zaXR5X3RocmVzaG9sZCxzaG93X2NsdXN0ZXJpbmc9VHJ1ZSkKdXNhZ2Vfbm9ybSwgZ2VwX3Njb3JlcywgZ2VwX3RwbSwgdG9wZ2VuZXMgPSBjbm1mX29iai5sb2FkX3Jlc3VsdHMoSz1zZWxlY3RlZF9rLCBkZW5zaXR5X3RocmVzaG9sZD1kZW5zaXR5X3RocmVzaG9sZCkKYGBgCgpgYGB7cn0KZ2VwX3Njb3JlcyA9IHB5JGdlcF9zY29yZXMKZ2VwX3RwbSA9IHB5JGdlcF90cG0KYWxsX21ldGFnZW5lcz0gcHkkdXNhZ2Vfbm9ybQpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTAsIHJlc3VsdHM9J2FzaXMnfQojIE1ha2UgbWV0YWdlbmUgbmFtZXMKZm9yIChpIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpW2ldID0gIm1ldGFnZW5lLiIgJT4lIHBhc3RlMChpKQp9CgojYWRkIGVhY2ggbWV0YWdlbmUgdG8gbWV0YWRhdGEKZm9yIChpIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIG1ldGFnZV9tZXRhZGF0YSA9IGFsbF9tZXRhZ2VuZXMgJT4lIHNlbGVjdChpKQogIGFjYzFfY2FuY2VyX2NlbGxzID0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbWV0YWRhdGEgPSBtZXRhZ2VfbWV0YWRhdGEpCn0KcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICBGZWF0dXJlUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpLGNvbWJpbmUgPSBUKSwKICAgICAgICAgIHRpdGxlID0gIm1ldGFnZW5lcyBleHByZXNzaW9uIikKYGBgCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQpwcmludF90YWIocGx0ID0gRGltUGxvdChhY2MxX2NhbmNlcl9jZWxscyxwdC5zaXplID0gMSxncm91cC5ieSA9ICJvcmlnLmlkZW50IikKICAgICAgICAgICx0aXRsZSA9ICJ1bWFwIGJ5IHBsYXRlIikKCmBgYAoKYGBge3IgcmVzdWx0cz0nYXNpcyd9Cm5tZl92c19wbGF0ZSA9IEZldGNoRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyx2YXJzID0gYygibWV0YWdlbmUuMSIsIm1ldGFnZW5lLjIiLCAib3JpZy5pZGVudCIpKQojIG15Yl92c19jbnYgJGNudi5jbHVzdGVyID0gYXMuY2hhcmFjdGVyKG15Yl92c19jbnYgJGNudi5jbHVzdGVyICkKCnBsdCA9IGdnYm94cGxvdChubWZfdnNfcGxhdGUsIHggPSAib3JpZy5pZGVudCIsIHkgPSAibWV0YWdlbmUuMSIsCiAgICAgICAgICBwYWxldHRlID0gImpjbyIsCiAgICAgICAgICBhZGQgPSAiaml0dGVyIikrCiAgc3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIsY29tcGFyaXNvbnMgPSBsaXN0KGMoIkFDQy5wbGF0ZTIiLCJBQ0MxLlAzIikpKSsKCmdnYm94cGxvdChubWZfdnNfcGxhdGUsIHggPSAib3JpZy5pZGVudCIsIHkgPSAibWV0YWdlbmUuMiIsCiAgICAgICAgICBwYWxldHRlID0gImpjbyIsCiAgICAgICAgICBhZGQgPSAiaml0dGVyIikrCiAgc3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIsY29tcGFyaXNvbnMgPSBsaXN0KGMoIkFDQy5wbGF0ZTIiLCJBQ0MxLlAzIikpKQoKcHJpbnRfdGFiKHBsdCA9IHBsdCwgdGl0bGUgPSAicGxhdGVzIGRpZmYiKQpgYGAKYGBge3IgcmVzdWx0cz0nYXNpcyd9CmFjYzFfY2FuY2VyX2NlbGxzID0gU2V0SWRlbnQob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsIHZhbHVlID0gIm9yaWcuaWRlbnQiKQpwcmludF90YWIocGx0ID0gCiAgICAgICAgICAgIERvdFBsb3Qob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsIGZlYXR1cmVzID0gYygibWV0YWdlbmUuMSIsIm1ldGFnZW5lLjIiLCJtZXRhZ2VuZS4zIiwibWV0YWdlbmUuNCIpLHNjYWxlID0gRikKICAgICAgICAgICx0aXRsZSA9ICJkb3QgcGxvdCIpCgpgYGAKCgoKIyByZWdyZXNzIG5GZWF0dXJlX1JOQSAKYGBge3J9CiNyZWdyZXNzIG5GZWF0dXJlX1JOQQphY2MxX2NhbmNlcl9jZWxscyA8LSBTY2FsZURhdGEoYWNjMV9jYW5jZXJfY2VsbHMsIHZhcnMudG8ucmVncmVzcyA9IGMoIm5GZWF0dXJlX1JOQSIsInBlcmNlbnQubXQiLCJuQ291bnRfUk5BIikpCmFjYzFfY2FuY2VyX2NlbGxzIDwtIFJ1blBDQShhY2MxX2NhbmNlcl9jZWxscywgZmVhdHVyZXMgPSBWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzKSkKYGBgCgpgYGB7cn0KRWxib3dQbG90KGFjYzFfY2FuY2VyX2NlbGxzLCBuZGltcyA9IDUwKSAjIGNoZWNraW5nIHRoZSBkaW1lbnNpb25hbGl0eSAKYGBgCgpgYGB7cn0KcGMydXNlID0gMToxMApgYGAKCgpgYGB7ciBlY2hvPVRSVUV9CmFjYzFfY2FuY2VyX2NlbGxzIDwtIEZpbmROZWlnaGJvcnMoYWNjMV9jYW5jZXJfY2VsbHMsIGRpbXMgPSBwYzJ1c2UpCmFjYzFfY2FuY2VyX2NlbGxzIDwtIEZpbmRDbHVzdGVycyhhY2MxX2NhbmNlcl9jZWxscywgcmVzb2x1dGlvbiA9IDAuNSkKYWNjMV9jYW5jZXJfY2VsbHMgPC0gUnVuVU1BUChhY2MxX2NhbmNlcl9jZWxscywgZGltcyA9IHBjMnVzZSkKYGBgCgpgYGB7cn0KRGltUGxvdChhY2MxX2NhbmNlcl9jZWxscyxwdC5zaXplID0gMSxncm91cC5ieSA9ICJvcmlnLmlkZW50IikKYGBgCgoKCgojIyBjTk1GCmBgYHtyfQpsaWJyYXJ5KHJldGljdWxhdGUpCmBgYAoKYGBge3J9CiN3cml0ZSBleHByZXNzaW9uCm5mZWF0dXJlcyA9IDIwMDAKbmZlYXR1cmVzX25hbWUgPSAobmZlYXR1cmVzLzEwMDApICU+JSBhcy5jaGFyYWN0ZXIoKQphY2MxX2NhbmNlcl9jZWxscyA9IEZpbmRWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLG5mZWF0dXJlcyA9IG5mZWF0dXJlcykKdmFyZ2VuZXMgPSBWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzKQpobXNjX3NjYWxlZF9leHByZXNzaW9uID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSB2YXJnZW5lcywgc2xvdCA9ICJzY2FsZS5kYXRhIikKaG1zY19zY2FsZWRfZXhwcmVzc2lvbltobXNjX3NjYWxlZF9leHByZXNzaW9uPDBdPSAwCgp3cml0ZS50YWJsZSh4ID0gaG1zY19zY2FsZWRfZXhwcmVzc2lvbiAsZmlsZSA9IHBhc3RlMCgnLi9EYXRhL2NOTUYvaG1zY19zY2FsZWRfZXhwcmVzc2lvbicsbmZlYXR1cmVzX25hbWUsJ0t2YXJnZW5lcy50eHQnKSxzZXAgPSAiXHQiKQpgYGAKCgoKYGBge3B5dGhvbiBldmFsPUZ9CmZyb20gY25tZiBpbXBvcnQgY05NRgppbXBvcnQgbnVtcHkgYXMgbnAKbmZlYXR1cmVzX25hbWUgPSByLm5mZWF0dXJlc19uYW1lCm5hbWUgPSAnSE1TQ19jTk1GX3NjYWxlZF8nK25mZWF0dXJlc19uYW1lKydLdmFyZ2VuZXMnCm91dGRpciA9ICcuL0RhdGEvY05NRicKS19yYW5nZSA9IG5wLmFyYW5nZSgzLDEwKQpjbm1mX29iaiA9IGNOTUYob3V0cHV0X2Rpcj1vdXRkaXIsIG5hbWU9bmFtZSkKY291bnRzX2ZuPScuL0RhdGEvY05NRi9obXNjX3NjYWxlZF9leHByZXNzaW9uJytuZmVhdHVyZXNfbmFtZSsnS3ZhcmdlbmVzLnR4dCcKdHBtX2ZuID0gY291bnRzX2ZuICMjIFRoaXMgaXMgYSB3ZWlyZCBjYXNlIHdoZXJlIGJlY2F1c2UgdGhpcyBkYXRhc2V0IGlzIG5vdCAzJyBlbmQgdW1pIHNlcXVlbmNpbmcsIHdlIG9wdGVkIHRvIHVzZSB0aGUgVFBNIG1hdHJpeCBhcyB0aGUgaW5wdXQgbWF0cml4IHJhdGhlciB0aGFuIHRoZSBjb3VudCBtYXRyaXgKCmNubWZfb2JqLnByZXBhcmUoY291bnRzX2ZuPWNvdW50c19mbiwgY29tcG9uZW50cz1LX3JhbmdlLCBzZWVkPTE0LHRwbV9mbj10cG1fZm4pCmBgYAoKYGBge3B5dGhvbiBldmFsPUZ9CmNubWZfb2JqLmZhY3Rvcml6ZSh3b3JrZXJfaT0wLCB0b3RhbF93b3JrZXJzPTEpCmBgYAoKYGBge3B5dGhvbiBldmFsPUZ9CmNubWZfb2JqLmNvbWJpbmUoKQpjbm1mX29iai5rX3NlbGVjdGlvbl9wbG90KCkKYGBgCiMjIFNhdmUgb2JqZWN0CmBgYHtweXRob24gZXZhbD1GfQojIGltcG9ydCBwaWNrbGUKIyBmID0gb3BlbignLi9EYXRhL2NOTUYvSE1TQ19jTk1GX3NjYWxlZF8nK25mZWF0dXJlc19uYW1lKydLdmFyZ2VuZXMvY25tZl9vYmoucGNrbCcsICd3YicpCiMgcGlja2xlLmR1bXAoY25tZl9vYmosIGYpCiMgZi5jbG9zZSgpCmBgYAoKCiMjIExvYWQgb2JqZWN0CmBgYHtweXRob259CmZyb20gY25tZiBpbXBvcnQgY05NRgppbXBvcnQgcGlja2xlCm5mZWF0dXJlcyA9ICIySyIKZiA9IG9wZW4oJy4vRGF0YS9jTk1GL0hNU0NfY05NRl9zY2FsZWRfJyArIG5mZWF0dXJlcysgJ3ZhcmdlbmVzL2NubWZfb2JqLnBja2wnLCAncmInKQpjbm1mX29iaiA9IHBpY2tsZS5sb2FkKGYpCmYuY2xvc2UoKQpgYGAKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygnLi9EYXRhL2NOTUYvSE1TQ19jTk1GX3NjYWxlZF8yS3ZhcmdlbmVzL0hNU0NfY05NRl9zY2FsZWRfMkt2YXJnZW5lcy5rX3NlbGVjdGlvbi5wbmcnKQpgYGAKCmBgYHtweXRob259CnNlbGVjdGVkX2sgPSA0CmRlbnNpdHlfdGhyZXNob2xkID0gMC4xCmNubWZfb2JqLmNvbnNlbnN1cyhrPXNlbGVjdGVkX2ssIGRlbnNpdHlfdGhyZXNob2xkPWRlbnNpdHlfdGhyZXNob2xkLHNob3dfY2x1c3RlcmluZz1UcnVlKQp1c2FnZV9ub3JtLCBnZXBfc2NvcmVzLCBnZXBfdHBtLCB0b3BnZW5lcyA9IGNubWZfb2JqLmxvYWRfcmVzdWx0cyhLPXNlbGVjdGVkX2ssIGRlbnNpdHlfdGhyZXNob2xkPWRlbnNpdHlfdGhyZXNob2xkKQpgYGAKCmBgYHtyfQpnZXBfc2NvcmVzID0gcHkkZ2VwX3Njb3JlcwpnZXBfdHBtID0gcHkkZ2VwX3RwbQphbGxfbWV0YWdlbmVzPSBweSR1c2FnZV9ub3JtCmBgYAoKIyByZWdyZXNzIG5GZWF0dXJlX1JOQSBwcm9ncmFtcyAgey50YWJzZXR9CgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTAsIHJlc3VsdHM9J2FzaXMnfQojIE1ha2UgbWV0YWdlbmUgbmFtZXMKZm9yIChpIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpW2ldID0gIm1ldGFnZW5lLiIgJT4lIHBhc3RlMChpKQp9CgojYWRkIGVhY2ggbWV0YWdlbmUgdG8gbWV0YWRhdGEKZm9yIChpIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIG1ldGFnZV9tZXRhZGF0YSA9IGFsbF9tZXRhZ2VuZXMgJT4lIHNlbGVjdChpKQogIGFjYzFfY2FuY2VyX2NlbGxzID0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbWV0YWRhdGEgPSBtZXRhZ2VfbWV0YWRhdGEpCn0KcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICBGZWF0dXJlUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpLGNvbWJpbmUgPSBUKSwKICAgICAgICAgIHRpdGxlID0gIm1ldGFnZW5lcyBleHByZXNzaW9uIikKYGBgCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KcHJpbnRfdGFiKHBsdCA9IERpbVBsb3QoYWNjMV9jYW5jZXJfY2VsbHMscHQuc2l6ZSA9IDEsZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpCiAgICAgICAgICAsdGl0bGUgPSAidW1hcCBieSBwbGF0ZSIpCgpgYGAKCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQpubWZfdnNfcGxhdGUgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9IGMoIm1ldGFnZW5lLjEiLCJtZXRhZ2VuZS4yIiwgIm9yaWcuaWRlbnQiKSkKIyBteWJfdnNfY252ICRjbnYuY2x1c3RlciA9IGFzLmNoYXJhY3RlcihteWJfdnNfY252ICRjbnYuY2x1c3RlciApCgpwbHQgPSBnZ2JveHBsb3Qobm1mX3ZzX3BsYXRlLCB4ID0gIm9yaWcuaWRlbnQiLCB5ID0gIm1ldGFnZW5lLjEiLAogICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLAogICAgICAgICAgYWRkID0gImppdHRlciIpKwogIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAid2lsY294LnRlc3QiLGNvbXBhcmlzb25zID0gbGlzdChjKCJBQ0MucGxhdGUyIiwiQUNDMS5QMyIpKSkrCgpnZ2JveHBsb3Qobm1mX3ZzX3BsYXRlLCB4ID0gIm9yaWcuaWRlbnQiLCB5ID0gIm1ldGFnZW5lLjIiLAogICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLAogICAgICAgICAgYWRkID0gImppdHRlciIpKwogIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAid2lsY294LnRlc3QiLGNvbXBhcmlzb25zID0gbGlzdChjKCJBQ0MucGxhdGUyIiwiQUNDMS5QMyIpKSkKCnByaW50X3RhYihwbHQgPSBwbHQsIHRpdGxlID0gInBsYXRlcyBkaWZmIikKYGBgCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KYWNjMV9jYW5jZXJfY2VsbHMgPSBTZXRJZGVudChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscywgdmFsdWUgPSAib3JpZy5pZGVudCIpCnByaW50X3RhYihwbHQgPSAKICAgICAgICAgICAgRG90UGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscywgZmVhdHVyZXMgPSBjKCJtZXRhZ2VuZS4xIiwibWV0YWdlbmUuMiIsIm1ldGFnZW5lLjMiLCJtZXRhZ2VuZS40Iiksc2NhbGUgPSBGKQogICAgICAgICAgLHRpdGxlID0gImRvdCBwbG90IikKCmBgYAoKCgoKIyBIYXJtb255CmBgYHtyfQphY2MxX2NhbmNlcl9jZWxscyA9IHJlYWRSRFMoZmlsZSA9ICIuL0RhdGEvYWNjMV9jYW5jZXJfY2VsbHNfMTVLbkNvdW50X1Y0LlJEUyIpCmBgYAoKYGBge3J9CgpzYyA8LSBpbXBvcnQoJ3NjYW5weScsIGNvbnZlcnQgPSBGQUxTRSkKZ2VuZV9leHByZXNzaW9uID0gdChhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGFjYzFfY2FuY2VyX2NlbGxzLHNsb3Q9J2RhdGEnKSkpIAoKZ2VuZV9leHByZXNzaW9uID0gMioqZ2VuZV9leHByZXNzaW9uICNjb252ZXJ0IGxvZzIoVFBNKzEpIHRvIFRQTSsxCmdlbmVfZXhwcmVzc2lvbiA9IGdlbmVfZXhwcmVzc2lvbi0xICNjb252ZXJ0IFRQTSsxIHRvIFRQTQoKYWRhdGEgPC0gc2MkQW5uRGF0YSgKICBYICAgPSBnZW5lX2V4cHJlc3Npb24sIAogIG9icyA9IGFjYzFfY2FuY2VyX2NlbGxzW1tdXSwKICB2YXIgPSBHZXRBc3NheShhY2MxX2NhbmNlcl9jZWxscylbW11dCikKcm0oZ2VuZV9leHByZXNzaW9uKQpgYGAKCmBgYHtweXRob259CiMgJW1hdHBsb3RsaWIgaW5saW5lCiMgJWxvYWRfZXh0IGF1dG9yZWxvYWQKIyAlYXV0b3JlbG9hZCAyCmltcG9ydCBzY2lweQppbXBvcnQgc2NhbnB5IGFzIHNjCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQKaW1wb3J0IGhhcm1vbnlweQpmcm9tIGhhcm1vbnlweSBpbXBvcnQgcnVuX2hhcm1vbnkKaW1wb3J0IHN5cwpmcm9tIGNubWYgaW1wb3J0IGNOTUYKCmltcG9ydCBudW1weSBhcyBucAppbXBvcnQgcGFuZGFzIGFzIHBkCmZyb20gc2tsZWFybi5kZWNvbXBvc2l0aW9uIGltcG9ydCBOTUYKaW1wb3J0IHNlYWJvcm4gYXMgc25zCmltcG9ydCB5YW1sCiMjIGNvcGllZCBmcm9tIGhhcm1vbnlweS5weQpkZWYgbW9lX2NvcnJlY3RfcmlkZ2UoWl9vcmlnLCBaX2NvcywgWl9jb3JyLCBSLCBXLCBLLCBQaGlfUmssIFBoaV9tb2UsIGxhbWIpOgogICAgWl9jb3JyID0gWl9vcmlnLmNvcHkoKQogICAgZm9yIGkgaW4gcmFuZ2UoSyk6CiAgICAgICAgUGhpX1JrID0gbnAubXVsdGlwbHkoUGhpX21vZSwgUltpLDpdKQogICAgICAgIHggPSBucC5kb3QoUGhpX1JrLCBQaGlfbW9lLlQpICsgbGFtYgogICAgICAgIFcgPSBucC5kb3QobnAuZG90KG5wLmxpbmFsZy5pbnYoeCksIFBoaV9SayksIFpfb3JpZy5UKQogICAgICAgIFdbMCw6XSA9IDAgIyBkbyBub3QgcmVtb3ZlIHRoZSBpbnRlcmNlcHQKICAgICAgICBaX2NvcnIgLT0gbnAuZG90KFcuVCwgUGhpX1JrKQogICAgWl9jb3MgPSBaX2NvcnIgLyBucC5saW5hbGcubm9ybShaX2NvcnIsIG9yZD0yLCBheGlzPTApCiAgICByZXR1cm4gWl9jb3MsIFpfY29yciwgVywgUGhpX1JrCgpgYGAKCiMjIFNjYWxlIGhpZ2ggdmFyIGdlbmVzKyBQQ0EKYGBge3B5dGhvbn0KYWRhdGEgPSByLmFkYXRhCgoKIyBzYy5wcC5ub3JtYWxpemVfcGVyX2NlbGwoYWRhdGEpICNEYXRhIGlzIGFscmVhZHkgbm9ybWxpemVkCmFkYXRhLnZhclsnaGlnaGx5X3ZhcmlhYmxlJ10gPSBhZGF0YS52YXJbJ3ZzdC52YXJpYWJsZSddCmFkYXRhID0gYWRhdGFbOixhZGF0YS52YXJbJ2hpZ2hseV92YXJpYWJsZSddXS5jb3B5KCkKCnNjLnBwLnNjYWxlKGFkYXRhLCB6ZXJvX2NlbnRlcj1GYWxzZSwgbWF4X3ZhbHVlPTUwKQpzYy50bC5wY2EoYWRhdGEsIHVzZV9oaWdobHlfdmFyaWFibGU9VHJ1ZSkKc2MucGwucGNhX3ZhcmlhbmNlX3JhdGlvKGFkYXRhLCBsb2c9VHJ1ZSkKYGBgCiNSdW4gaGFybW9ueQpgYGB7cHl0aG9ufQpoYXJtb255X3JlcyA9IGhhcm1vbnlweS5ydW5faGFybW9ueShhZGF0YS5vYnNtWydYX3BjYSddLCBhZGF0YS5vYnMsICdvcmlnLmlkZW50JyxwbG90X2NvbnZlcmdlbmNlID0gVHJ1ZSwgdGhldGE9My41KQpgYGAKCiMjIEFkdWpzdCBjb3JyZWN0aW9uCmBgYHtweXRob259ClggPSBucC5hcnJheShhZGF0YVs6LGFkYXRhLnZhclsnaGlnaGx5X3ZhcmlhYmxlJ11dLlgpCgoKXywgWF9jb3JyLCBfLCBfID0gbW9lX2NvcnJlY3RfcmlkZ2UoWC5ULCBOb25lLCBOb25lLCBoYXJtb255X3Jlcy5SLCBOb25lLCBoYXJtb255X3Jlcy5LLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5vbmUsIGhhcm1vbnlfcmVzLlBoaV9tb2UsIGhhcm1vbnlfcmVzLmxhbWIpClhfY29yciA9IFhfY29yci5UCmBgYAoKIyMgbm9uIG5lZ2F0aXZlIGNvcnJlY3Rpb24KYGBge3B5dGhvbn0KYWRhdGFfY29yciA9IGFkYXRhWzosYWRhdGEudmFyWydoaWdobHlfdmFyaWFibGUnXV0uY29weSgpCiAgClhfY29ycl9ub25uZWcgPSBYX2NvcnIuY29weSgpClhfY29ycl9ub25uZWdbWF9jb3JyX25vbm5lZzwwXT0gMAphZGF0YV9jb3JyX25vbm5lZyA9IGFkYXRhX2NvcnIuY29weSgpCmFkYXRhX2NvcnJfbm9ubmVnLlggPSBYX2NvcnJfbm9ubmVnCmBgYAojIyBpbXBvcnQgdG8gc2V1cmF0IGZvciBwbG90dGluZyBVTUFQCmBgYHtyfQpleHBycyA8LSB0KHB5JGFkYXRhX2NvcnJfbm9ubmVnJFgpCmNvbG5hbWVzKGV4cHJzKSA8LSBweSRhZGF0YSRvYnNfbmFtZXMkdG9fbGlzdCgpCnJvd25hbWVzKGV4cHJzKSA8LSBweSRhZGF0YSR2YXJfbmFtZXMkdG9fbGlzdCgpCgojIENyZWF0ZSB0aGUgU2V1cmF0IG9iamVjdApsdW5nX2NvcnJfbm9ubmVnIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBleHBycykKbHVuZ19jb3JyX25vbm5lZyRvcmlnLmlkZW50IDwtIHB5JGFkYXRhJG9ic1sib3JpZy5pZGVudCJdCgpgYGAKCiMjIFBDQSBhZnRlciBjb3JyZWN0aW9uCmBgYHtyfQpsdW5nX2NvcnJfbm9ubmVnQGFzc2F5cyRSTkFAc2NhbGUuZGF0YSA9IGV4cHJzCmx1bmdfY29ycl9ub25uZWcgPC0gUnVuUENBKGx1bmdfY29ycl9ub25uZWcsIGZlYXR1cmVzID0gcm93bmFtZXMobHVuZ19jb3JyX25vbm5lZyksdmVyYm9zZSA9IEYpCgpFbGJvd1Bsb3QobHVuZ19jb3JyX25vbm5lZykKYGBgCiMjIFJ1biBkaW0gcmVkdWN0aW9uCmBgYHtyIHJlc3VsdHM9J2hpZGUnLGluY2x1ZGU9RkFMU0V9CnBjMnVzZSA9IDE6NQpsdW5nX2NvcnJfbm9ubmVnIDwtIEZpbmROZWlnaGJvcnMobHVuZ19jb3JyX25vbm5lZywgZGltcyA9IHBjMnVzZSkKbHVuZ19jb3JyX25vbm5lZyA8LSBGaW5kQ2x1c3RlcnMobHVuZ19jb3JyX25vbm5lZywgcmVzb2x1dGlvbiA9IDAuNSkKbHVuZ19jb3JyX25vbm5lZyA8LSBSdW5VTUFQKGx1bmdfY29ycl9ub25uZWcsIGRpbXMgPSBwYzJ1c2UpCgpgYGAKCiMjIFVNQVAgYWZ0ZXIgY29ycmVjdGlvbgpgYGB7cn0KRGltUGxvdChsdW5nX2NvcnJfbm9ubmVnLCByZWR1Y3Rpb24gPSAidW1hcCIsZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpCkRpbVBsb3QobHVuZ19jb3JyX25vbm5lZywgcmVkdWN0aW9uID0gInVtYXAiKQoKYGBgCgpgYGB7cHl0aG9ufQpzYy53cml0ZSgnLi9EYXRhL2NOTUYvYWNjMUNhbmNlckNlbGxzX0hhcm1vbnlfTm9OZWcuaDVhZCcsIGFkYXRhX2NvcnJfbm9ubmVnKQpgYGAKCgpjTk1GOgoKYGBge3B5dGhvbn0KbmFtZSA9ICdITVNDX2NOTUZfaGFybW9ueV8yS3ZhcmdlbmVzJwpvdXRkaXIgPSAnLi9EYXRhL2NOTUYnCktfcmFuZ2UgPSBucC5hcmFuZ2UoMywxMCkKY25tZl9vYmogPSBjTk1GKG91dHB1dF9kaXI9b3V0ZGlyLCBuYW1lPW5hbWUpCmNvdW50c19mbj0nLi9EYXRhL2NOTUYvYWNjMUNhbmNlckNlbGxzX0hhcm1vbnlfTm9OZWcuaDVhZCcKdHBtX2ZuID0gY291bnRzX2ZuCgoKY25tZl9vYmoucHJlcGFyZShjb3VudHNfZm49Y291bnRzX2ZuLCBjb21wb25lbnRzPUtfcmFuZ2UsIG5faXRlcj0xMCwgc2VlZD0xNCx0cG1fZm49Tm9uZSxkZW5zaWZ5PVRydWUpCmBgYAoKYGBge3B5dGhvbn0KY25tZl9vYmouZmFjdG9yaXplKHdvcmtlcl9pPTAsIHRvdGFsX3dvcmtlcnM9MSkKYGBgCgpgYGB7cHl0aG9ufQpjbm1mX29iai5jb21iaW5lKCkKY25tZl9vYmoua19zZWxlY3Rpb25fcGxvdCgpCmBgYAoKYGBge3J9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuL0RhdGEvY05NRi9ITVNDX2NOTUZfaGFybW9ueV8yS3ZhcmdlbmVzL0hNU0NfY05NRl9oYXJtb255XzJLdmFyZ2VuZXMua19zZWxlY3Rpb24ucG5nIikKYGBgCgoKIyMgU2F2ZSBvYmplY3QKYGBge3B5dGhvbn0KI2ltcG9ydCBwaWNrbGUKI2YgPSBvcGVuKCcuL0RhdGEvY05NRi9ITVNDX2NOTUZfaGFybW9ueV8yS3ZhcmdlbmVzL2NubWZfb2JqLnBja2wnLCAnd2InKQojcGlja2xlLmR1bXAoY25tZl9vYmosIGYpCiNmLmNsb3NlKCkKYGBgCgoKCiMjIExvYWQgb2JqZWN0CmBgYHtweXRob259CmZyb20gY25tZiBpbXBvcnQgY05NRgppbXBvcnQgcGlja2xlCm5mZWF0dXJlcyA9ICIySyIKZiA9IG9wZW4oJy4vRGF0YS9jTk1GL0hNU0NfY05NRl9oYXJtb255XzJLdmFyZ2VuZXMvY25tZl9vYmoucGNrbCcsICdyYicpCmNubWZfb2JqID0gcGlja2xlLmxvYWQoZikKZi5jbG9zZSgpCmBgYAoKCmBgYHtweXRob259CnNlbGVjdGVkX2sgPSAzCmRlbnNpdHlfdGhyZXNob2xkID0gMC4xCmNubWZfb2JqLmNvbnNlbnN1cyhrPXNlbGVjdGVkX2ssIGRlbnNpdHlfdGhyZXNob2xkPWRlbnNpdHlfdGhyZXNob2xkLHNob3dfY2x1c3RlcmluZz1UcnVlKQp1c2FnZV9ub3JtLCBnZXBfc2NvcmVzLCBnZXBfdHBtLCB0b3BnZW5lcyA9IGNubWZfb2JqLmxvYWRfcmVzdWx0cyhLPXNlbGVjdGVkX2ssIGRlbnNpdHlfdGhyZXNob2xkPWRlbnNpdHlfdGhyZXNob2xkKQpgYGAKCmBgYHtyfQpnZXBfc2NvcmVzID0gcHkkZ2VwX3Njb3JlcwpnZXBfdHBtID0gcHkkZ2VwX3RwbQphbGxfbWV0YWdlbmVzPSBweSR1c2FnZV9ub3JtCmBgYAoKIyBIYXJtb255IHJlc3VsdHMgey50YWJzZXR9CmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMCwgcmVzdWx0cz0nYXNpcyd9CiMgTWFrZSBtZXRhZ2VuZSBuYW1lcwpmb3IgKGkgaW4gMTpuY29sKGFsbF9tZXRhZ2VuZXMpKSB7CiAgY29sbmFtZXMoYWxsX21ldGFnZW5lcylbaV0gPSAibWV0YWdlbmUuIiAlPiUgcGFzdGUwKGkpCn0KCiNhZGQgZWFjaCBtZXRhZ2VuZSB0byBtZXRhZGF0YQpmb3IgKGkgaW4gMTpuY29sKGFsbF9tZXRhZ2VuZXMpKSB7CiAgbWV0YWdlX21ldGFkYXRhID0gYWxsX21ldGFnZW5lcyAlPiUgc2VsZWN0KGkpCiAgYWNjMV9jYW5jZXJfY2VsbHMgPSBBZGRNZXRhRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxtZXRhZGF0YSA9IG1ldGFnZV9tZXRhZGF0YSkKfQpwcmludF90YWIocGx0ID0gCiAgICAgICAgICAgIEZlYXR1cmVQbG90KG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLGZlYXR1cmVzID0gY29sbmFtZXMoYWxsX21ldGFnZW5lcyksY29tYmluZSA9IFQpLAogICAgICAgICAgdGl0bGUgPSAibWV0YWdlbmVzIGV4cHJlc3Npb24iKQoKI2FkZCBlYWNoIG1ldGFnZW5lIHRvIG1ldGFkYXRhCmZvciAoaSBpbiAxOm5jb2woYWxsX21ldGFnZW5lcykpIHsKICBtZXRhZ2VfbWV0YWRhdGEgPSBhbGxfbWV0YWdlbmVzICU+JSBzZWxlY3QoaSkKICBsdW5nX2NvcnJfbm9ubmVnID0gQWRkTWV0YURhdGEob2JqZWN0ID0gbHVuZ19jb3JyX25vbm5lZyxtZXRhZGF0YSA9IG1ldGFnZV9tZXRhZGF0YSkKfQpwcmludF90YWIocGx0ID0gCiAgICAgICAgICAgIEZlYXR1cmVQbG90KG9iamVjdCA9IGx1bmdfY29ycl9ub25uZWcsZmVhdHVyZXMgPSBjb2xuYW1lcyhhbGxfbWV0YWdlbmVzKSxjb21iaW5lID0gVCksCiAgICAgICAgICB0aXRsZSA9ICJtZXRhZ2VuZXMgZXhwcmVzc2lvbiIpCmBgYApgYGB7cn0KYWxsX21hcmtlcnMgPSBGaW5kQWxsTWFya2VycyhvYmplY3QgPSBsdW5nX2NvcnJfbm9ubmVnLGZlYXR1cmVzID0gcm93bmFtZXMobHVuZ19jb3JyX25vbm5lZyksbG9nZmMudGhyZXNob2xkID0gMCxtaW4ucGN0ID0gMCxkZW5zaWZ5ID0gVCkKYGBgCgoKYGBge3IgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTIsIHJlc3VsdHM9J2FzaXMnfQpwcmludF90YWIocGx0ID0gRGltUGxvdChsdW5nX2NvcnJfbm9ubmVnLCByZWR1Y3Rpb24gPSAidW1hcCIpLHRpdGxlID0gIlVNQVAiKQoKCgphbGxfbWFya2VycyA9IGFsbF9tYXJrZXJzICU+JSAKICBtdXRhdGUoZmRyID0gcC5hZGp1c3QocF92YWwsbWV0aG9kID0gImZkciIpKSU+JSAgI2FkZCBmZHIgCiAgZHBseXI6OmZpbHRlcihmZHI8MC4wNSkgJT4lICAgZHBseXI6OmZpbHRlcihhYnMoYXZnX2xvZzJGQyk+MSkKCgpjYW5vbmljYWxfcGF0aHdheXMgPSBtc2lnZGJyKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwgY2F0ZWdvcnkgPSAiQzIiKSAlPiUgZHBseXI6OmZpbHRlcihnc19zdWJjYXQgIT0gIkNHUCIpICU+JSAgZHBseXI6OmRpc3RpbmN0KGdzX25hbWUsIGdlbmVfc3ltYm9sKSAKCnBsdF9saXN0ID0gbGlzdCgpCgpmb3IgKGNsdXN0ZXJfbnVtIGluIHVuaXF1ZShhbGxfbWFya2VycyRjbHVzdGVyKSkgewogICBkZWcgPSBhbGxfbWFya2VycyAlPiUgZHBseXI6OmZpbHRlcihjbHVzdGVyID09IGNsdXN0ZXJfbnVtKQogIAogIHAgPSBlbnJpY2htZW50X2FuYWx5c2lzKG1hcmtlcnMgPSBkZWcsIGJhY2tncm91bmQgPSByb3duYW1lcyhsdW5nX2NvcnJfbm9ubmVnKSwgaWRlbnQuMSA9ICBwYXN0ZSgiY2x1c3RlciAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9udW0pLAogICAgICAgICAgICAgICAgICAgICAgICAgIGlkZW50LjIgPXBhc3RlKCJjbHVzdGVyICIsIGNsdXN0ZXJfbnVtKSxmZHJfQ3V0b2ZmID0gMC4wNSxjdXN0b21fcGF0aHdheXMgPSBjYW5vbmljYWxfcGF0aHdheXMpCiAgIHBsdF9saXN0W1tjbHVzdGVyX251bV1dID0gcAp9CnByaW50X3RhYihwbHQgPSBnZ2FycmFuZ2UocGxvdGxpc3QgPSBwbHRfbGlzdCksdGl0bGUgPSAiIGNsdXN0ZXJzIGVucmljaG1lbnQiKQpgYGAKCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KcHJpbnRfdGFiKHBsdCA9IERpbVBsb3QoYWNjMV9jYW5jZXJfY2VsbHMscHQuc2l6ZSA9IDEsZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpCiAgICAgICAgICAsdGl0bGUgPSAidW1hcCBieSBwbGF0ZSIpCgpgYGAKCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQpubWZfdnNfcGxhdGUgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9IGMoIm1ldGFnZW5lLjEiLCJtZXRhZ2VuZS4yIiwgIm9yaWcuaWRlbnQiKSkKIyBteWJfdnNfY252ICRjbnYuY2x1c3RlciA9IGFzLmNoYXJhY3RlcihteWJfdnNfY252ICRjbnYuY2x1c3RlciApCgpwbHQgPSBnZ2JveHBsb3Qobm1mX3ZzX3BsYXRlLCB4ID0gIm9yaWcuaWRlbnQiLCB5ID0gIm1ldGFnZW5lLjEiLAogICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLAogICAgICAgICAgYWRkID0gImppdHRlciIpKwogIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAid2lsY294LnRlc3QiLGNvbXBhcmlzb25zID0gbGlzdChjKCJBQ0MucGxhdGUyIiwiQUNDMS5QMyIpKSkrCgpnZ2JveHBsb3Qobm1mX3ZzX3BsYXRlLCB4ID0gIm9yaWcuaWRlbnQiLCB5ID0gIm1ldGFnZW5lLjIiLAogICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLAogICAgICAgICAgYWRkID0gImppdHRlciIpKwogIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAid2lsY294LnRlc3QiLGNvbXBhcmlzb25zID0gbGlzdChjKCJBQ0MucGxhdGUyIiwiQUNDMS5QMyIpKSkKCnByaW50X3RhYihwbHQgPSBwbHQsIHRpdGxlID0gInBsYXRlcyBkaWZmIikKYGBgCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KYWNjMV9jYW5jZXJfY2VsbHMgPSBTZXRJZGVudChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscywgdmFsdWUgPSAib3JpZy5pZGVudCIpCnByaW50X3RhYihwbHQgPSAKICAgICAgICAgICAgRG90UGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscywgZmVhdHVyZXMgPSBjKCJtZXRhZ2VuZS4xIiwibWV0YWdlbmUuMiIsIm1ldGFnZW5lLjMiLCJtZXRhZ2VuZS40Iiksc2NhbGUgPSBGKQogICAgICAgICAgLHRpdGxlID0gImRvdCBwbG90IikKCmBgYAoKCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTgsIHJlc3VsdHM9J2hpZGUnfQpwbHRfbGlzdCA9IGxpc3QoKQpmb3IgKGkgaW4gMTpuY29sKGdlcF9zY29yZXMpKSB7CiAgdG9wX2dlbmVzID0gZ2VwX3Njb3JlcyAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3Jlc1tpXSkpICNzb3J0IGJ5IHNjb3JlIGEKICB0b3AgPSBoZWFkKHJvd25hbWVzKHRvcF9nZW5lcyksMTAwKSAjdGFrZSB0b3AgdG9wX2dlbmVzX251bQogIHJlcyA9IGdlbmVzX3ZlY19lbnJpY2htZW50KGdlbmVzID0gdG9wLGJhY2tncm91bmQgPSByb3duYW1lcyhnZXBfc2NvcmVzKSxob21lciA9IFQsdGl0bGUgPSAKICAgICAgICAgICAgICAgICAgICBpLHNpbGVudCA9IFQscmV0dXJuX2FsbCA9IFQpCiAgIAogIHBsdF9saXN0W1tpXV0gPSByZXMkcGx0Cn0KcHJpbnRfdGFiKHBsdCA9IGdnYXJyYW5nZShwbG90bGlzdCA9IHBsdF9saXN0KSx0aXRsZSA9ICIgZ2VwcyBlbnJpY2htZW50IikKYGBgCg==