0.1 Parameters

0.2 functions

0.3 Data

acc1_cancer_cells = readRDS(file = "./Data/acc1_cancer_cells_15KnCount_V4.RDS")

1 Title

2 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")

metagenes expression

NA

print_tab(plt = DimPlot(acc1_cancer_cells,pt.size = 1,group.by = "orig.ident")
          ,title = "umap by plate")

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

3 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")

3.1 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()

3.2 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()

3.3 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

4 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")

metagenes expression

NA

print_tab(plt = DimPlot(acc1_cancer_cells,pt.size = 1,group.by = "orig.ident")
          ,title = "umap by plate")

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

5 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

5.1 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)

5.2 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

5.3 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

5.4 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"]

5.5 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)

5.6 Run dim reduction

5.7 UMAP after correction

DimPlot(lung_corr_nonneg, reduction = "umap",group.by = "orig.ident")
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")

5.8 Save object

#import pickle
#f = open('./Data/cNMF/HMSC_cNMF_harmony_2Kvargenes/cnmf_obj.pckl', 'wb')
#pickle.dump(cnmf_obj, f)
#f.close()

5.9 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

6 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")

metagenes expression

NA

print_tab(plt = DimPlot(acc1_cancer_cells,pt.size = 1,group.by = "orig.ident")
          ,title = "umap by plate")

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

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
}
gridExtra::grid.arrange(grobs = plt_list)

LS0tCnRpdGxlOiAnYHIgcnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLHJlcGxhY2VtZW50ID0gIiIpYCcgCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19jb2xsYXBzZTogeWVzCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvY19kZXB0aDogMQotLS0KCiMjIFBhcmFtZXRlcnMKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CgpgYGAKCgojIyBmdW5jdGlvbnMKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmBgYAoKIyMgRGF0YQoKYGBge3J9CmFjYzFfY2FuY2VyX2NlbGxzID0gcmVhZFJEUyhmaWxlID0gIi4vRGF0YS9hY2MxX2NhbmNlcl9jZWxsc18xNUtuQ291bnRfVjQuUkRTIikKYGBgCiMgVGl0bGUgCgpgYGB7ciBlY2hvPVRSVUUsIHJlc3VsdHM9J2FzaXMnfQoKYGBgCiMgY25tZiBwcm9ncmFtcyBhcmUgYnkgcGxhdGUgey50YWJzZXR9CgpgYGB7cHl0aG9ufQpmcm9tIGNubWYgaW1wb3J0IGNOTUYKaW1wb3J0IHBpY2tsZQpuZmVhdHVyZXMgPSAiMksiCmYgPSBvcGVuKCcuL0RhdGEvY05NRi9ITVNDX2NOTUZfJyArIG5mZWF0dXJlcysgJ3ZhcmdlbmVzL2NubWZfb2JqLnBja2wnLCAncmInKQpjbm1mX29iaiA9IHBpY2tsZS5sb2FkKGYpCmYuY2xvc2UoKQpgYGAKCgpgYGB7cHl0aG9ufQpzZWxlY3RlZF9rID0gNApkZW5zaXR5X3RocmVzaG9sZCA9IDAuMQpjbm1mX29iai5jb25zZW5zdXMoaz1zZWxlY3RlZF9rLCBkZW5zaXR5X3RocmVzaG9sZD1kZW5zaXR5X3RocmVzaG9sZCxzaG93X2NsdXN0ZXJpbmc9VHJ1ZSkKdXNhZ2Vfbm9ybSwgZ2VwX3Njb3JlcywgZ2VwX3RwbSwgdG9wZ2VuZXMgPSBjbm1mX29iai5sb2FkX3Jlc3VsdHMoSz1zZWxlY3RlZF9rLCBkZW5zaXR5X3RocmVzaG9sZD1kZW5zaXR5X3RocmVzaG9sZCkKYGBgCgpgYGB7cn0KZ2VwX3Njb3JlcyA9IHB5JGdlcF9zY29yZXMKZ2VwX3RwbSA9IHB5JGdlcF90cG0KYWxsX21ldGFnZW5lcz0gcHkkdXNhZ2Vfbm9ybQpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTAsIHJlc3VsdHM9J2FzaXMnfQojIE1ha2UgbWV0YWdlbmUgbmFtZXMKZm9yIChpIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpW2ldID0gIm1ldGFnZW5lLiIgJT4lIHBhc3RlMChpKQp9CgojYWRkIGVhY2ggbWV0YWdlbmUgdG8gbWV0YWRhdGEKZm9yIChpIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIG1ldGFnZV9tZXRhZGF0YSA9IGFsbF9tZXRhZ2VuZXMgJT4lIHNlbGVjdChpKQogIGFjYzFfY2FuY2VyX2NlbGxzID0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbWV0YWRhdGEgPSBtZXRhZ2VfbWV0YWRhdGEpCn0KcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICBGZWF0dXJlUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpLGNvbWJpbmUgPSBUKSwKICAgICAgICAgIHRpdGxlID0gIm1ldGFnZW5lcyBleHByZXNzaW9uIikKYGBgCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQpwcmludF90YWIocGx0ID0gRGltUGxvdChhY2MxX2NhbmNlcl9jZWxscyxwdC5zaXplID0gMSxncm91cC5ieSA9ICJvcmlnLmlkZW50IikKICAgICAgICAgICx0aXRsZSA9ICJ1bWFwIGJ5IHBsYXRlIikKCmBgYAoKYGBge3IgcmVzdWx0cz0nYXNpcyd9Cm5tZl92c19wbGF0ZSA9IEZldGNoRGF0YShvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyx2YXJzID0gYygibWV0YWdlbmUuMSIsIm1ldGFnZW5lLjIiLCAib3JpZy5pZGVudCIpKQojIG15Yl92c19jbnYgJGNudi5jbHVzdGVyID0gYXMuY2hhcmFjdGVyKG15Yl92c19jbnYgJGNudi5jbHVzdGVyICkKCnBsdCA9IGdnYm94cGxvdChubWZfdnNfcGxhdGUsIHggPSAib3JpZy5pZGVudCIsIHkgPSAibWV0YWdlbmUuMSIsCiAgICAgICAgICBwYWxldHRlID0gImpjbyIsCiAgICAgICAgICBhZGQgPSAiaml0dGVyIikrCiAgc3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIsY29tcGFyaXNvbnMgPSBsaXN0KGMoIkFDQy5wbGF0ZTIiLCJBQ0MxLlAzIikpKSsKCmdnYm94cGxvdChubWZfdnNfcGxhdGUsIHggPSAib3JpZy5pZGVudCIsIHkgPSAibWV0YWdlbmUuMiIsCiAgICAgICAgICBwYWxldHRlID0gImpjbyIsCiAgICAgICAgICBhZGQgPSAiaml0dGVyIikrCiAgc3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIsY29tcGFyaXNvbnMgPSBsaXN0KGMoIkFDQy5wbGF0ZTIiLCJBQ0MxLlAzIikpKQoKcHJpbnRfdGFiKHBsdCA9IHBsdCwgdGl0bGUgPSAicGxhdGVzIGRpZmYiKQpgYGAKYGBge3IgcmVzdWx0cz0nYXNpcyd9CmFjYzFfY2FuY2VyX2NlbGxzID0gU2V0SWRlbnQob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsIHZhbHVlID0gIm9yaWcuaWRlbnQiKQpwcmludF90YWIocGx0ID0gCiAgICAgICAgICAgIERvdFBsb3Qob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsIGZlYXR1cmVzID0gYygibWV0YWdlbmUuMSIsIm1ldGFnZW5lLjIiLCJtZXRhZ2VuZS4zIiwibWV0YWdlbmUuNCIpLHNjYWxlID0gRikKICAgICAgICAgICx0aXRsZSA9ICJkb3QgcGxvdCIpCgpgYGAKCgoKIyByZWdyZXNzIG5GZWF0dXJlX1JOQSAKYGBge3J9CiNyZWdyZXNzIG5GZWF0dXJlX1JOQQphY2MxX2NhbmNlcl9jZWxscyA8LSBTY2FsZURhdGEoYWNjMV9jYW5jZXJfY2VsbHMsIHZhcnMudG8ucmVncmVzcyA9IGMoIm5GZWF0dXJlX1JOQSIsInBlcmNlbnQubXQiLCJuQ291bnRfUk5BIikpCmFjYzFfY2FuY2VyX2NlbGxzIDwtIFJ1blBDQShhY2MxX2NhbmNlcl9jZWxscywgZmVhdHVyZXMgPSBWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzKSkKYGBgCgpgYGB7cn0KRWxib3dQbG90KGFjYzFfY2FuY2VyX2NlbGxzLCBuZGltcyA9IDUwKSAjIGNoZWNraW5nIHRoZSBkaW1lbnNpb25hbGl0eSAKYGBgCgpgYGB7cn0KcGMydXNlID0gMToxMApgYGAKCgpgYGB7ciBlY2hvPVRSVUV9CmFjYzFfY2FuY2VyX2NlbGxzIDwtIEZpbmROZWlnaGJvcnMoYWNjMV9jYW5jZXJfY2VsbHMsIGRpbXMgPSBwYzJ1c2UpCmFjYzFfY2FuY2VyX2NlbGxzIDwtIEZpbmRDbHVzdGVycyhhY2MxX2NhbmNlcl9jZWxscywgcmVzb2x1dGlvbiA9IDAuNSkKYWNjMV9jYW5jZXJfY2VsbHMgPC0gUnVuVU1BUChhY2MxX2NhbmNlcl9jZWxscywgZGltcyA9IHBjMnVzZSkKYGBgCgpgYGB7cn0KRGltUGxvdChhY2MxX2NhbmNlcl9jZWxscyxwdC5zaXplID0gMSxncm91cC5ieSA9ICJvcmlnLmlkZW50IikKYGBgCgoKCgojIyBjTk1GCmBgYHtyfQpsaWJyYXJ5KHJldGljdWxhdGUpCmBgYAoKYGBge3J9CiN3cml0ZSBleHByZXNzaW9uCm5mZWF0dXJlcyA9IDIwMDAKbmZlYXR1cmVzX25hbWUgPSAobmZlYXR1cmVzLzEwMDApICU+JSBhcy5jaGFyYWN0ZXIoKQphY2MxX2NhbmNlcl9jZWxscyA9IEZpbmRWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLG5mZWF0dXJlcyA9IG5mZWF0dXJlcykKdmFyZ2VuZXMgPSBWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzKQpobXNjX3NjYWxlZF9leHByZXNzaW9uID0gRmV0Y2hEYXRhKG9iamVjdCA9IGFjYzFfY2FuY2VyX2NlbGxzLHZhcnMgPSB2YXJnZW5lcywgc2xvdCA9ICJzY2FsZS5kYXRhIikKaG1zY19zY2FsZWRfZXhwcmVzc2lvbltobXNjX3NjYWxlZF9leHByZXNzaW9uPDBdPSAwCgp3cml0ZS50YWJsZSh4ID0gaG1zY19zY2FsZWRfZXhwcmVzc2lvbiAsZmlsZSA9IHBhc3RlMCgnLi9EYXRhL2NOTUYvaG1zY19zY2FsZWRfZXhwcmVzc2lvbicsbmZlYXR1cmVzX25hbWUsJ0t2YXJnZW5lcy50eHQnKSxzZXAgPSAiXHQiKQpgYGAKCgoKYGBge3B5dGhvbiBldmFsPUZ9CmZyb20gY25tZiBpbXBvcnQgY05NRgppbXBvcnQgbnVtcHkgYXMgbnAKbmZlYXR1cmVzX25hbWUgPSByLm5mZWF0dXJlc19uYW1lCm5hbWUgPSAnSE1TQ19jTk1GX3NjYWxlZF8nK25mZWF0dXJlc19uYW1lKydLdmFyZ2VuZXMnCm91dGRpciA9ICcuL0RhdGEvY05NRicKS19yYW5nZSA9IG5wLmFyYW5nZSgzLDEwKQpjbm1mX29iaiA9IGNOTUYob3V0cHV0X2Rpcj1vdXRkaXIsIG5hbWU9bmFtZSkKY291bnRzX2ZuPScuL0RhdGEvY05NRi9obXNjX3NjYWxlZF9leHByZXNzaW9uJytuZmVhdHVyZXNfbmFtZSsnS3ZhcmdlbmVzLnR4dCcKdHBtX2ZuID0gY291bnRzX2ZuICMjIFRoaXMgaXMgYSB3ZWlyZCBjYXNlIHdoZXJlIGJlY2F1c2UgdGhpcyBkYXRhc2V0IGlzIG5vdCAzJyBlbmQgdW1pIHNlcXVlbmNpbmcsIHdlIG9wdGVkIHRvIHVzZSB0aGUgVFBNIG1hdHJpeCBhcyB0aGUgaW5wdXQgbWF0cml4IHJhdGhlciB0aGFuIHRoZSBjb3VudCBtYXRyaXgKCmNubWZfb2JqLnByZXBhcmUoY291bnRzX2ZuPWNvdW50c19mbiwgY29tcG9uZW50cz1LX3JhbmdlLCBzZWVkPTE0LHRwbV9mbj10cG1fZm4pCmBgYAoKYGBge3B5dGhvbiBldmFsPUZ9CmNubWZfb2JqLmZhY3Rvcml6ZSh3b3JrZXJfaT0wLCB0b3RhbF93b3JrZXJzPTEpCmBgYAoKYGBge3B5dGhvbiBldmFsPUZ9CmNubWZfb2JqLmNvbWJpbmUoKQpjbm1mX29iai5rX3NlbGVjdGlvbl9wbG90KCkKYGBgCiMjIFNhdmUgb2JqZWN0CmBgYHtweXRob24gZXZhbD1GfQojIGltcG9ydCBwaWNrbGUKIyBmID0gb3BlbignLi9EYXRhL2NOTUYvSE1TQ19jTk1GX3NjYWxlZF8nK25mZWF0dXJlc19uYW1lKydLdmFyZ2VuZXMvY25tZl9vYmoucGNrbCcsICd3YicpCiMgcGlja2xlLmR1bXAoY25tZl9vYmosIGYpCiMgZi5jbG9zZSgpCmBgYAoKCiMjIExvYWQgb2JqZWN0CmBgYHtweXRob259CmZyb20gY25tZiBpbXBvcnQgY05NRgppbXBvcnQgcGlja2xlCm5mZWF0dXJlcyA9ICIySyIKZiA9IG9wZW4oJy4vRGF0YS9jTk1GL0hNU0NfY05NRl9zY2FsZWRfJyArIG5mZWF0dXJlcysgJ3ZhcmdlbmVzL2NubWZfb2JqLnBja2wnLCAncmInKQpjbm1mX29iaiA9IHBpY2tsZS5sb2FkKGYpCmYuY2xvc2UoKQpgYGAKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygnLi9EYXRhL2NOTUYvSE1TQ19jTk1GX3NjYWxlZF8yS3ZhcmdlbmVzL0hNU0NfY05NRl9zY2FsZWRfMkt2YXJnZW5lcy5rX3NlbGVjdGlvbi5wbmcnKQpgYGAKCmBgYHtweXRob259CnNlbGVjdGVkX2sgPSA0CmRlbnNpdHlfdGhyZXNob2xkID0gMC4xCmNubWZfb2JqLmNvbnNlbnN1cyhrPXNlbGVjdGVkX2ssIGRlbnNpdHlfdGhyZXNob2xkPWRlbnNpdHlfdGhyZXNob2xkLHNob3dfY2x1c3RlcmluZz1UcnVlKQp1c2FnZV9ub3JtLCBnZXBfc2NvcmVzLCBnZXBfdHBtLCB0b3BnZW5lcyA9IGNubWZfb2JqLmxvYWRfcmVzdWx0cyhLPXNlbGVjdGVkX2ssIGRlbnNpdHlfdGhyZXNob2xkPWRlbnNpdHlfdGhyZXNob2xkKQpgYGAKCmBgYHtyfQpnZXBfc2NvcmVzID0gcHkkZ2VwX3Njb3JlcwpnZXBfdHBtID0gcHkkZ2VwX3RwbQphbGxfbWV0YWdlbmVzPSBweSR1c2FnZV9ub3JtCmBgYAoKIyByZWdyZXNzIG5GZWF0dXJlX1JOQSBwcm9ncmFtcyAgey50YWJzZXR9CgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTAsIHJlc3VsdHM9J2FzaXMnfQojIE1ha2UgbWV0YWdlbmUgbmFtZXMKZm9yIChpIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpW2ldID0gIm1ldGFnZW5lLiIgJT4lIHBhc3RlMChpKQp9CgojYWRkIGVhY2ggbWV0YWdlbmUgdG8gbWV0YWRhdGEKZm9yIChpIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIG1ldGFnZV9tZXRhZGF0YSA9IGFsbF9tZXRhZ2VuZXMgJT4lIHNlbGVjdChpKQogIGFjYzFfY2FuY2VyX2NlbGxzID0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbWV0YWRhdGEgPSBtZXRhZ2VfbWV0YWRhdGEpCn0KcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICBGZWF0dXJlUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpLGNvbWJpbmUgPSBUKSwKICAgICAgICAgIHRpdGxlID0gIm1ldGFnZW5lcyBleHByZXNzaW9uIikKYGBgCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KcHJpbnRfdGFiKHBsdCA9IERpbVBsb3QoYWNjMV9jYW5jZXJfY2VsbHMscHQuc2l6ZSA9IDEsZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpCiAgICAgICAgICAsdGl0bGUgPSAidW1hcCBieSBwbGF0ZSIpCgpgYGAKCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQpubWZfdnNfcGxhdGUgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9IGMoIm1ldGFnZW5lLjEiLCJtZXRhZ2VuZS4yIiwgIm9yaWcuaWRlbnQiKSkKIyBteWJfdnNfY252ICRjbnYuY2x1c3RlciA9IGFzLmNoYXJhY3RlcihteWJfdnNfY252ICRjbnYuY2x1c3RlciApCgpwbHQgPSBnZ2JveHBsb3Qobm1mX3ZzX3BsYXRlLCB4ID0gIm9yaWcuaWRlbnQiLCB5ID0gIm1ldGFnZW5lLjEiLAogICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLAogICAgICAgICAgYWRkID0gImppdHRlciIpKwogIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAid2lsY294LnRlc3QiLGNvbXBhcmlzb25zID0gbGlzdChjKCJBQ0MucGxhdGUyIiwiQUNDMS5QMyIpKSkrCgpnZ2JveHBsb3Qobm1mX3ZzX3BsYXRlLCB4ID0gIm9yaWcuaWRlbnQiLCB5ID0gIm1ldGFnZW5lLjIiLAogICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLAogICAgICAgICAgYWRkID0gImppdHRlciIpKwogIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAid2lsY294LnRlc3QiLGNvbXBhcmlzb25zID0gbGlzdChjKCJBQ0MucGxhdGUyIiwiQUNDMS5QMyIpKSkKCnByaW50X3RhYihwbHQgPSBwbHQsIHRpdGxlID0gInBsYXRlcyBkaWZmIikKYGBgCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KYWNjMV9jYW5jZXJfY2VsbHMgPSBTZXRJZGVudChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscywgdmFsdWUgPSAib3JpZy5pZGVudCIpCnByaW50X3RhYihwbHQgPSAKICAgICAgICAgICAgRG90UGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscywgZmVhdHVyZXMgPSBjKCJtZXRhZ2VuZS4xIiwibWV0YWdlbmUuMiIsIm1ldGFnZW5lLjMiLCJtZXRhZ2VuZS40Iiksc2NhbGUgPSBGKQogICAgICAgICAgLHRpdGxlID0gImRvdCBwbG90IikKCmBgYAoKCgoKIyBIYXJtb255CmBgYHtyfQphY2MxX2NhbmNlcl9jZWxscyA9IHJlYWRSRFMoZmlsZSA9ICIuL0RhdGEvYWNjMV9jYW5jZXJfY2VsbHNfMTVLbkNvdW50X1Y0LlJEUyIpCmBgYAoKYGBge3J9CgpzYyA8LSBpbXBvcnQoJ3NjYW5weScsIGNvbnZlcnQgPSBGQUxTRSkKZ2VuZV9leHByZXNzaW9uID0gdChhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGFjYzFfY2FuY2VyX2NlbGxzLHNsb3Q9J2RhdGEnKSkpIAoKZ2VuZV9leHByZXNzaW9uID0gMioqZ2VuZV9leHByZXNzaW9uICNjb252ZXJ0IGxvZzIoVFBNKzEpIHRvIFRQTSsxCmdlbmVfZXhwcmVzc2lvbiA9IGdlbmVfZXhwcmVzc2lvbi0xICNjb252ZXJ0IFRQTSsxIHRvIFRQTQoKYWRhdGEgPC0gc2MkQW5uRGF0YSgKICBYICAgPSBnZW5lX2V4cHJlc3Npb24sIAogIG9icyA9IGFjYzFfY2FuY2VyX2NlbGxzW1tdXSwKICB2YXIgPSBHZXRBc3NheShhY2MxX2NhbmNlcl9jZWxscylbW11dCikKcm0oZ2VuZV9leHByZXNzaW9uKQpgYGAKCmBgYHtweXRob259CiMgJW1hdHBsb3RsaWIgaW5saW5lCiMgJWxvYWRfZXh0IGF1dG9yZWxvYWQKIyAlYXV0b3JlbG9hZCAyCmltcG9ydCBzY2lweQppbXBvcnQgc2NhbnB5IGFzIHNjCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQKaW1wb3J0IGhhcm1vbnlweQpmcm9tIGhhcm1vbnlweSBpbXBvcnQgcnVuX2hhcm1vbnkKaW1wb3J0IHN5cwpmcm9tIGNubWYgaW1wb3J0IGNOTUYKCmltcG9ydCBudW1weSBhcyBucAppbXBvcnQgcGFuZGFzIGFzIHBkCmZyb20gc2tsZWFybi5kZWNvbXBvc2l0aW9uIGltcG9ydCBOTUYKaW1wb3J0IHNlYWJvcm4gYXMgc25zCmltcG9ydCB5YW1sCiMjIGNvcGllZCBmcm9tIGhhcm1vbnlweS5weQpkZWYgbW9lX2NvcnJlY3RfcmlkZ2UoWl9vcmlnLCBaX2NvcywgWl9jb3JyLCBSLCBXLCBLLCBQaGlfUmssIFBoaV9tb2UsIGxhbWIpOgogICAgWl9jb3JyID0gWl9vcmlnLmNvcHkoKQogICAgZm9yIGkgaW4gcmFuZ2UoSyk6CiAgICAgICAgUGhpX1JrID0gbnAubXVsdGlwbHkoUGhpX21vZSwgUltpLDpdKQogICAgICAgIHggPSBucC5kb3QoUGhpX1JrLCBQaGlfbW9lLlQpICsgbGFtYgogICAgICAgIFcgPSBucC5kb3QobnAuZG90KG5wLmxpbmFsZy5pbnYoeCksIFBoaV9SayksIFpfb3JpZy5UKQogICAgICAgIFdbMCw6XSA9IDAgIyBkbyBub3QgcmVtb3ZlIHRoZSBpbnRlcmNlcHQKICAgICAgICBaX2NvcnIgLT0gbnAuZG90KFcuVCwgUGhpX1JrKQogICAgWl9jb3MgPSBaX2NvcnIgLyBucC5saW5hbGcubm9ybShaX2NvcnIsIG9yZD0yLCBheGlzPTApCiAgICByZXR1cm4gWl9jb3MsIFpfY29yciwgVywgUGhpX1JrCgpgYGAKCiMjIFNjYWxlIGhpZ2ggdmFyIGdlbmVzKyBQQ0EKYGBge3B5dGhvbn0KYWRhdGEgPSByLmFkYXRhCgoKIyBzYy5wcC5ub3JtYWxpemVfcGVyX2NlbGwoYWRhdGEpICNEYXRhIGlzIGFscmVhZHkgbm9ybWxpemVkCmFkYXRhLnZhclsnaGlnaGx5X3ZhcmlhYmxlJ10gPSBhZGF0YS52YXJbJ3ZzdC52YXJpYWJsZSddCmFkYXRhID0gYWRhdGFbOixhZGF0YS52YXJbJ2hpZ2hseV92YXJpYWJsZSddXS5jb3B5KCkKCnNjLnBwLnNjYWxlKGFkYXRhLCB6ZXJvX2NlbnRlcj1GYWxzZSwgbWF4X3ZhbHVlPTUwKQpzYy50bC5wY2EoYWRhdGEsIHVzZV9oaWdobHlfdmFyaWFibGU9VHJ1ZSkKc2MucGwucGNhX3ZhcmlhbmNlX3JhdGlvKGFkYXRhLCBsb2c9VHJ1ZSkKYGBgCiNSdW4gaGFybW9ueQpgYGB7cHl0aG9ufQpoYXJtb255X3JlcyA9IGhhcm1vbnlweS5ydW5faGFybW9ueShhZGF0YS5vYnNtWydYX3BjYSddLCBhZGF0YS5vYnMsICdvcmlnLmlkZW50JyxwbG90X2NvbnZlcmdlbmNlID0gVHJ1ZSwgdGhldGE9My41KQpgYGAKCiMjIEFkdWpzdCBjb3JyZWN0aW9uCmBgYHtweXRob259ClggPSBucC5hcnJheShhZGF0YVs6LGFkYXRhLnZhclsnaGlnaGx5X3ZhcmlhYmxlJ11dLlgpCgoKXywgWF9jb3JyLCBfLCBfID0gbW9lX2NvcnJlY3RfcmlkZ2UoWC5ULCBOb25lLCBOb25lLCBoYXJtb255X3Jlcy5SLCBOb25lLCBoYXJtb255X3Jlcy5LLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5vbmUsIGhhcm1vbnlfcmVzLlBoaV9tb2UsIGhhcm1vbnlfcmVzLmxhbWIpClhfY29yciA9IFhfY29yci5UCmBgYAoKIyMgbm9uIG5lZ2F0aXZlIGNvcnJlY3Rpb24KYGBge3B5dGhvbn0KYWRhdGFfY29yciA9IGFkYXRhWzosYWRhdGEudmFyWydoaWdobHlfdmFyaWFibGUnXV0uY29weSgpCiAgClhfY29ycl9ub25uZWcgPSBYX2NvcnIuY29weSgpClhfY29ycl9ub25uZWdbWF9jb3JyX25vbm5lZzwwXT0gMAphZGF0YV9jb3JyX25vbm5lZyA9IGFkYXRhX2NvcnIuY29weSgpCmFkYXRhX2NvcnJfbm9ubmVnLlggPSBYX2NvcnJfbm9ubmVnCmBgYAojIyBpbXBvcnQgdG8gc2V1cmF0IGZvciBwbG90dGluZyBVTUFQCmBgYHtyfQpleHBycyA8LSB0KHB5JGFkYXRhX2NvcnJfbm9ubmVnJFgpCmNvbG5hbWVzKGV4cHJzKSA8LSBweSRhZGF0YSRvYnNfbmFtZXMkdG9fbGlzdCgpCnJvd25hbWVzKGV4cHJzKSA8LSBweSRhZGF0YSR2YXJfbmFtZXMkdG9fbGlzdCgpCgojIENyZWF0ZSB0aGUgU2V1cmF0IG9iamVjdApsdW5nX2NvcnJfbm9ubmVnIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBleHBycykKbHVuZ19jb3JyX25vbm5lZyRvcmlnLmlkZW50IDwtIHB5JGFkYXRhJG9ic1sib3JpZy5pZGVudCJdCgpgYGAKCiMjIFBDQSBhZnRlciBjb3JyZWN0aW9uCmBgYHtyfQpsdW5nX2NvcnJfbm9ubmVnQGFzc2F5cyRSTkFAc2NhbGUuZGF0YSA9IGV4cHJzCmx1bmdfY29ycl9ub25uZWcgPC0gUnVuUENBKGx1bmdfY29ycl9ub25uZWcsIGZlYXR1cmVzID0gcm93bmFtZXMobHVuZ19jb3JyX25vbm5lZyksdmVyYm9zZSA9IEYpCgpFbGJvd1Bsb3QobHVuZ19jb3JyX25vbm5lZykKYGBgCiMjIFJ1biBkaW0gcmVkdWN0aW9uCmBgYHtyIHJlc3VsdHM9J2hpZGUnLGluY2x1ZGU9RkFMU0V9CnBjMnVzZSA9IDE6NQpsdW5nX2NvcnJfbm9ubmVnIDwtIEZpbmROZWlnaGJvcnMobHVuZ19jb3JyX25vbm5lZywgZGltcyA9IHBjMnVzZSkKbHVuZ19jb3JyX25vbm5lZyA8LSBGaW5kQ2x1c3RlcnMobHVuZ19jb3JyX25vbm5lZywgcmVzb2x1dGlvbiA9IDAuNSkKbHVuZ19jb3JyX25vbm5lZyA8LSBSdW5VTUFQKGx1bmdfY29ycl9ub25uZWcsIGRpbXMgPSBwYzJ1c2UpCgpgYGAKCiMjIFVNQVAgYWZ0ZXIgY29ycmVjdGlvbgpgYGB7cn0KRGltUGxvdChsdW5nX2NvcnJfbm9ubmVnLCByZWR1Y3Rpb24gPSAidW1hcCIsZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpCmBgYAoKYGBge3B5dGhvbn0Kc2Mud3JpdGUoJy4vRGF0YS9jTk1GL2FjYzFDYW5jZXJDZWxsc19IYXJtb255X05vTmVnLmg1YWQnLCBhZGF0YV9jb3JyX25vbm5lZykKYGBgCgoKY05NRjoKCmBgYHtweXRob259Cm5hbWUgPSAnSE1TQ19jTk1GX2hhcm1vbnlfMkt2YXJnZW5lcycKb3V0ZGlyID0gJy4vRGF0YS9jTk1GJwpLX3JhbmdlID0gbnAuYXJhbmdlKDMsMTApCmNubWZfb2JqID0gY05NRihvdXRwdXRfZGlyPW91dGRpciwgbmFtZT1uYW1lKQpjb3VudHNfZm49Jy4vRGF0YS9jTk1GL2FjYzFDYW5jZXJDZWxsc19IYXJtb255X05vTmVnLmg1YWQnCnRwbV9mbiA9IGNvdW50c19mbgoKCmNubWZfb2JqLnByZXBhcmUoY291bnRzX2ZuPWNvdW50c19mbiwgY29tcG9uZW50cz1LX3JhbmdlLCBuX2l0ZXI9MTAsIHNlZWQ9MTQsdHBtX2ZuPU5vbmUsZGVuc2lmeT1UcnVlKQpgYGAKCmBgYHtweXRob259CmNubWZfb2JqLmZhY3Rvcml6ZSh3b3JrZXJfaT0wLCB0b3RhbF93b3JrZXJzPTEpCmBgYAoKYGBge3B5dGhvbn0KY25tZl9vYmouY29tYmluZSgpCmNubWZfb2JqLmtfc2VsZWN0aW9uX3Bsb3QoKQpgYGAKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9EYXRhL2NOTUYvSE1TQ19jTk1GX2hhcm1vbnlfMkt2YXJnZW5lcy9ITVNDX2NOTUZfaGFybW9ueV8yS3ZhcmdlbmVzLmtfc2VsZWN0aW9uLnBuZyIpCmBgYAoKCiMjIFNhdmUgb2JqZWN0CmBgYHtweXRob259CiNpbXBvcnQgcGlja2xlCiNmID0gb3BlbignLi9EYXRhL2NOTUYvSE1TQ19jTk1GX2hhcm1vbnlfMkt2YXJnZW5lcy9jbm1mX29iai5wY2tsJywgJ3diJykKI3BpY2tsZS5kdW1wKGNubWZfb2JqLCBmKQojZi5jbG9zZSgpCmBgYAoKCgojIyBMb2FkIG9iamVjdApgYGB7cHl0aG9ufQpmcm9tIGNubWYgaW1wb3J0IGNOTUYKaW1wb3J0IHBpY2tsZQpuZmVhdHVyZXMgPSAiMksiCmYgPSBvcGVuKCcuL0RhdGEvY05NRi9ITVNDX2NOTUZfaGFybW9ueV8yS3ZhcmdlbmVzL2NubWZfb2JqLnBja2wnLCAncmInKQpjbm1mX29iaiA9IHBpY2tsZS5sb2FkKGYpCmYuY2xvc2UoKQpgYGAKCgpgYGB7cHl0aG9ufQpzZWxlY3RlZF9rID0gMwpkZW5zaXR5X3RocmVzaG9sZCA9IDAuMQpjbm1mX29iai5jb25zZW5zdXMoaz1zZWxlY3RlZF9rLCBkZW5zaXR5X3RocmVzaG9sZD1kZW5zaXR5X3RocmVzaG9sZCxzaG93X2NsdXN0ZXJpbmc9VHJ1ZSkKdXNhZ2Vfbm9ybSwgZ2VwX3Njb3JlcywgZ2VwX3RwbSwgdG9wZ2VuZXMgPSBjbm1mX29iai5sb2FkX3Jlc3VsdHMoSz1zZWxlY3RlZF9rLCBkZW5zaXR5X3RocmVzaG9sZD1kZW5zaXR5X3RocmVzaG9sZCkKYGBgCgpgYGB7cn0KZ2VwX3Njb3JlcyA9IHB5JGdlcF9zY29yZXMKZ2VwX3RwbSA9IHB5JGdlcF90cG0KYWxsX21ldGFnZW5lcz0gcHkkdXNhZ2Vfbm9ybQpgYGAKCiMgSGFybW9ueSByZXN1bHRzIHsudGFic2V0fQpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTAsIHJlc3VsdHM9J2FzaXMnfQojIE1ha2UgbWV0YWdlbmUgbmFtZXMKZm9yIChpIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpW2ldID0gIm1ldGFnZW5lLiIgJT4lIHBhc3RlMChpKQp9CgojYWRkIGVhY2ggbWV0YWdlbmUgdG8gbWV0YWRhdGEKZm9yIChpIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIG1ldGFnZV9tZXRhZGF0YSA9IGFsbF9tZXRhZ2VuZXMgJT4lIHNlbGVjdChpKQogIGFjYzFfY2FuY2VyX2NlbGxzID0gQWRkTWV0YURhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsbWV0YWRhdGEgPSBtZXRhZ2VfbWV0YWRhdGEpCn0KcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICBGZWF0dXJlUGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscyxmZWF0dXJlcyA9IGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpLGNvbWJpbmUgPSBUKSwKICAgICAgICAgIHRpdGxlID0gIm1ldGFnZW5lcyBleHByZXNzaW9uIikKYGBgCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KcHJpbnRfdGFiKHBsdCA9IERpbVBsb3QoYWNjMV9jYW5jZXJfY2VsbHMscHQuc2l6ZSA9IDEsZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpCiAgICAgICAgICAsdGl0bGUgPSAidW1hcCBieSBwbGF0ZSIpCgpgYGAKCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQpubWZfdnNfcGxhdGUgPSBGZXRjaERhdGEob2JqZWN0ID0gYWNjMV9jYW5jZXJfY2VsbHMsdmFycyA9IGMoIm1ldGFnZW5lLjEiLCJtZXRhZ2VuZS4yIiwgIm9yaWcuaWRlbnQiKSkKIyBteWJfdnNfY252ICRjbnYuY2x1c3RlciA9IGFzLmNoYXJhY3RlcihteWJfdnNfY252ICRjbnYuY2x1c3RlciApCgpwbHQgPSBnZ2JveHBsb3Qobm1mX3ZzX3BsYXRlLCB4ID0gIm9yaWcuaWRlbnQiLCB5ID0gIm1ldGFnZW5lLjEiLAogICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLAogICAgICAgICAgYWRkID0gImppdHRlciIpKwogIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAid2lsY294LnRlc3QiLGNvbXBhcmlzb25zID0gbGlzdChjKCJBQ0MucGxhdGUyIiwiQUNDMS5QMyIpKSkrCgpnZ2JveHBsb3Qobm1mX3ZzX3BsYXRlLCB4ID0gIm9yaWcuaWRlbnQiLCB5ID0gIm1ldGFnZW5lLjIiLAogICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLAogICAgICAgICAgYWRkID0gImppdHRlciIpKwogIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAid2lsY294LnRlc3QiLGNvbXBhcmlzb25zID0gbGlzdChjKCJBQ0MucGxhdGUyIiwiQUNDMS5QMyIpKSkKCnByaW50X3RhYihwbHQgPSBwbHQsIHRpdGxlID0gInBsYXRlcyBkaWZmIikKYGBgCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KYWNjMV9jYW5jZXJfY2VsbHMgPSBTZXRJZGVudChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscywgdmFsdWUgPSAib3JpZy5pZGVudCIpCnByaW50X3RhYihwbHQgPSAKICAgICAgICAgICAgRG90UGxvdChvYmplY3QgPSBhY2MxX2NhbmNlcl9jZWxscywgZmVhdHVyZXMgPSBjKCJtZXRhZ2VuZS4xIiwibWV0YWdlbmUuMiIsIm1ldGFnZW5lLjMiLCJtZXRhZ2VuZS40Iiksc2NhbGUgPSBGKQogICAgICAgICAgLHRpdGxlID0gImRvdCBwbG90IikKCmBgYAoKIyB7LX0KCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTgsIHJlc3VsdHM9J2hpZGUnfQpwbHRfbGlzdCA9IGxpc3QoKQpmb3IgKGkgaW4gMTpuY29sKGdlcF9zY29yZXMpKSB7CiAgdG9wX2dlbmVzID0gZ2VwX3Njb3JlcyAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3Jlc1tpXSkpICNzb3J0IGJ5IHNjb3JlIGEKICB0b3AgPSBoZWFkKHJvd25hbWVzKHRvcF9nZW5lcyksMTAwKSAjdGFrZSB0b3AgdG9wX2dlbmVzX251bQogIHJlcyA9IGdlbmVzX3ZlY19lbnJpY2htZW50KGdlbmVzID0gdG9wLGJhY2tncm91bmQgPSByb3duYW1lcyhnZXBfc2NvcmVzKSxob21lciA9IFQsdGl0bGUgPSAKICAgICAgICAgICAgICAgICAgICBpLHNpbGVudCA9IFQscmV0dXJuX2FsbCA9IFQpCiAgIAogIHBsdF9saXN0W1tpXV0gPSByZXMkcGx0Cn0KZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JvYnMgPSBwbHRfbGlzdCkKYGBgCg==