Parameters
Functions
library(stringi)
library(reticulate)
source_from_github(repositoy = "DEG_functions",version = "0.2.24")
source_from_github(repositoy = "cNMF_functions",version = "0.3.85",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
}
# import python functions:
import types
get_norm_counts = r.get_norm_counts
code_obj = compile(get_norm_counts, '<string>', 'exec')
get_norm_counts = types.FunctionType(code_obj.co_consts[0], globals())
get_usage_from_score = r.get_usage_from_score
code_obj = compile(get_usage_from_score, '<string>', 'exec')
get_usage_from_score = types.FunctionType(code_obj.co_consts[0], globals())
compute_tpm = r.compute_tpm
code_obj = compile(compute_tpm, '<string>', 'exec')
compute_tpm = types.FunctionType(code_obj.co_consts[0], globals())
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
suffix = r.suffix
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()
# gep_scores = readRDS("/sci/labs/yotamd/lab_share/avishai.wizel/R_projects/EGFR/Data/cnmf/harmony_models_gep_scores.rds")
selected_k = 3
density_threshold = 0.1
# cnmf_obj.consensus(k=selected_k, density_threshold=density_threshold)
usage_norm, gep_scores, gep_tpm, topgenes = cnmf_obj.load_results(K=selected_k, density_threshold=density_threshold)
programs
enrichment
gep_scores = py$gep_scores
gep_tpm = py$gep_tpm
usage_norm= py$usage_norm
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)

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)

xeno = FindVariableFeatures(object = xeno,nfeatures = 2000)
Calculating gene variances
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
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_norm
cor(usage_by_calc,usage_norm)
calculate score for
Xeno
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.
all_metagenes = py$usage_by_calc
programs
expression
names (all_metagenes) = c("Hypoxia","TNFa","Cell_cycle")
#add each metagene to metadata
for (i in 1:ncol(all_metagenes)) {
metage_metadata = all_metagenes %>% dplyr::select(i)
# metage_metadata = scale(metage_metadata)
xeno = AddMetaData(object = xeno,metadata = metage_metadata,col.name = names(all_metagenes)[i])
}
print_tab(plt = FeaturePlot(object = xeno,features = colnames(all_metagenes)),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"))
program
assignment
larger_by = 1.5
xeno = program_assignment(dataset = xeno,larger_by = larger_by,program_names = colnames(all_metagenes))
print_tab(plt =
DimPlot(xeno,group.by = "program.assignment",cols = c(Hypoxia = "red",TNFa = "green",Cell_cycle = "blue","NA" = "grey"))
,title = "program.assignment",subtitle_num = 2)
print_tab(plt =
DimPlot(xeno,group.by = "orig.ident")
,title = "orig.ident",subtitle_num = 2)
print_tab(plt =
DimPlot(xeno,group.by = "treatment")
,title = "treatment",subtitle_num = 2)
p = cell_percentage(dataset = xeno,time.point_var = "treatment",by_program = T,x_order = c("NT","OSI","res"))
print_tab(plt = p,title = "by program",subtitle_num = 2)
p = cell_percentage(dataset = xeno,time.point_var = "treatment",by_tp = T,x_order =c("Hypoxia","TNFa","Cell_cycle","NA"))
print_tab(plt = p,title = "by time point",subtitle_num = 2)
print_tab(plt =
DimPlot(xeno,group.by = "program.assignment",cols = c(Hypoxia = "red",TNFa = "green",Cell_cycle = "blue","NA" = "grey"))
,title = "program.assignment",subtitle_num = 3)
print_tab(plt =
DimPlot(xeno,group.by = "orig.ident")
,title = "orig.ident",subtitle_num = 3)
print_tab(plt =
DimPlot(xeno,group.by = "treatment")
,title = "treatment",subtitle_num = 3)
p = cell_percentage(dataset = xeno,time.point_var = "treatment",by_program = T,x_order = c("NT","OSI","res"))
print_tab(plt = p,title = "by program",subtitle_num = 3)
p = cell_percentage(dataset = xeno,time.point_var = "treatment",by_tp = T,x_order =c("Hypoxia","TNFa","Cell_cycle","NA"))
print_tab(plt = p,title = "by time point",subtitle_num = 3)
Patients programs expression
lung = FindVariableFeatures(object = lung,nfeatures = 2000)
genes = rownames(lung)[rownames(lung) %in% VariableFeatures(object = xeno)[1:2000]]
lung_expression = t(as.matrix(GetAssayData(lung,slot='data')))
lung_expression = 2**lung_expression #convert from log2(tpm+1) to tpm
lung_expression = lung_expression-1
lung_expression = lung_expression[,genes] %>% as.data.frame()
all_0_genes = colnames(lung_expression)[colSums(lung_expression==0, na.rm=TRUE)==nrow(lung_expression)] #delete rows that have all 0
genes = genes[!genes %in% all_0_genes]
lung_expression = lung_expression[,!colnames(lung_expression) %in% all_0_genes]
gc()
lung_expression = r.lung_expression
genes = r.genes
usage_by_calc = get_usage_from_score(counts=lung_expression,tpm=lung_expression,genes=genes,cnmf_obj=cnmf_obj,k=3)
all_metagenes = py$usage_by_calc
all_metagenes = all_metagenes[,c(3,2,1)]
names (all_metagenes) = c("Hypoxia","TNFa","Cell_cycle")
#add each metagene to metadata
for (i in 1:ncol(all_metagenes)) {
metage_metadata = all_metagenes %>% dplyr::select(i)
lung = AddMetaData(object = lung,metadata = metage_metadata)
}
FeaturePlot(object = lung,features = colnames(all_metagenes))
metagenes_mean_compare(dataset = lung,time.point_var = "time.point",prefix = "patient",patient.ident_var = "patient.ident",pre_on = c("pre-treatment","on-treatment"),test = "wilcox.test")
metagenes_mean_compare(dataset = lung,time.point_var = "time.point",prefix = "patient",patient.ident_var = "patient.ident",pre_on = c("pre-treatment","on-treatment"))
lung program
assignment
larger_by = 1.25
lung = program_assignment(dataset = lung,larger_by = larger_by,program_names = colnames(all_metagenes))
print_tab(plt =
DimPlot(lung,group.by = "program.assignment",cols = c(Hypoxia = "red",TNFa = "green",Cell_cycle = "blue","NA" = "grey"))
,title = "program.assignment",subtitle_num = 3)
print_tab(plt =
DimPlot(lung,group.by = "patient.ident")
,title = "patient.ident",subtitle_num = 3)
print_tab(plt =
DimPlot(lung,group.by = "time.point")
,title = "time.point",subtitle_num = 3)
p = cell_percentage(dataset = lung,time.point_var = "time.point",by_program = T,x_order = c("pre-treatment","on-treatment","resistant"))
print_tab(plt = p,title = "by program",subtitle_num = 3)
p = cell_percentage(dataset = lung,time.point_var = "time.point",by_tp = T,x_order =c("Hypoxia","TNFa","Cell_cycle","NA"))
print_tab(plt = p,title = "by time point",subtitle_num = 3)
top_genes = gep_scores %>% arrange(desc(gep_scores["Hypoxia"])) #sort by score a
top = head(rownames(top_genes),200) #take top top_genes_num
expr = xeno_expression[,colnames(xeno_expression) %in% top]
expr_cor = cor(expr)
pht1 = pheatmap(expr_cor,show_colnames = F,show_rownames = F, silent = T)
num_of_clusters = 4
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)
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 = expr_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 = "Hypoxia")
top_genes = gep_scores %>% arrange(desc(gep_scores["TNFa"])) #sort by score a
top = head(rownames(top_genes),200) #take top top_genes_num
expr = xeno_expression[,colnames(xeno_expression) %in% top]
expr_cor = cor(expr)
pht1 = pheatmap(expr_cor,show_colnames = F,show_rownames = F, silent = T)
num_of_clusters = 4
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)
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 = expr_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 = "TNFa")
top_genes = gep_scores %>% arrange(desc(gep_scores["Cell_cycle"])) #sort by score a
top = head(rownames(top_genes),200) #take top top_genes_num
expr = xeno_expression[,colnames(xeno_expression) %in% top]
expr_cor = cor(expr)
pht1 = pheatmap(expr_cor,show_colnames = F,show_rownames = F, silent = T)
num_of_clusters = 4
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)
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 = expr_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 = "Cell_cycle")
top_genes = gep_scores %>% arrange(desc(gep_scores["Hypoxia"])) #sort by score a
hypoxia_genes = head(rownames(top_genes),20) #take top top_genes_num
intersect(cluster_3_genes,hypoxia_genes)
library(ggvenn)
all = list(hypoxia_genes = hypoxia_genes, hif_targets = cluster_3_genes)
ggvenn(
all
)
HIF_targets- Hypoxia correlation
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_hypoxia2[,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")
}
Warning in FetchData.Seurat(object = xeno, vars = c(genes)) : The
following requested variables were not found: AK4P1, BNIP3P1, LDHAP5,
AL158201.1, MIR210, NLRP3P1, AL109946.1 geom_smooth() using
formula ‘y ~ x’ geom_smooth() using formula ‘y ~ x’ ##
geom_bin2d {.unnumbered }

geom_smooth() using formula ‘y ~ x’
geom_smooth() using formula ‘y ~ x’ ## geom_bin2d
{.unnumbered }

geom_smooth() using formula ‘y ~ x’
geom_smooth() using formula ‘y ~ x’ ## geom_bin2d
{.unnumbered }

NA
UMAPS
hif_targets_by_tp = FetchData(object = xeno,vars = c(hif_targets)) %>% rowSums() %>% as.data.frame() #mean expression
Warning in FetchData.Seurat(object = xeno, vars = c(hif_targets)) :
The following requested variables were not found: AK4P1, BNIP3P1, LDHAP5, AL158201.1, MIR210, NLRP3P1, AL109946.1
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()
hif_targets_by_tp = FetchData(object = xeno,vars = c(hif_targets)) %>% rowSums() %>% as.data.frame() #mean expression
Warning in FetchData.Seurat(object = xeno, vars = c(hif_targets)) :
The following requested variables were not found: AK4P1, BNIP3P1, LDHAP5, AL158201.1, MIR210, NLRP3P1, AL109946.1
xeno = AddMetaData(object = xeno, metadata = hif_targets_by_tp,col.name = "HIF_targets_score")
high_hif_low_hypoxia_cells = data.frame( high_HIF_low_Hypoxia = high_hif_low_hypoxia_cells)
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","Hypoxia","Cell_cycle" ))

Calculate usage
without cc in sum to 1
Python 3.7.12 (/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/bin/python3.7)
Reticulate 1.24 REPL -- A Python interpreter in R.
Enter 'exit' or 'quit' to exit the REPL and return to R.
def get_usage_from_score(counts,tpm, genes,cnmf_obj,k):
import anndata as ad
import scanpy as sc
import numpy as np
from sklearn.decomposition import non_negative_factorization
import pandas as pd
counts_adata = ad.AnnData(counts)
tpm_adata = ad.AnnData(tpm)
norm_counts = get_norm_counts(counts=counts_adata,tpm=tpm_adata,high_variance_genes_filter=np.array(genes)) #norm counts like cnmf
spectra = cnmf_obj.get_median_spectra(k=k) #get score
spectra = spectra[spectra.columns.intersection(genes)] #remove genes not in @genes
spectra = spectra.T.reindex(norm_counts.to_df().columns).T #reorder spectra genes like norm_counts
usage_by_calc,_,_ = non_negative_factorization(X=norm_counts.X, H = spectra.values, update_H=False,n_components = k,max_iter=1000,init ='random')
usage_by_calc = pd.DataFrame(usage_by_calc, index=counts.index, columns=spectra.index) #insert to df+add names
# usage_by_calc = usage_by_calc.div(usage_by_calc.sum(axis=1), axis=0) # sum rows to 1
reorder = usage_by_calc.sum(axis=0).sort_values(ascending=False)
usage_by_calc = usage_by_calc.loc[:, reorder.index]
return(usage_by_calc)
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.
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")
}
Warning in FetchData.Seurat(object = xeno, vars = c(genes)) : The
following requested variables were not found: AK4P1, BNIP3P1, LDHAP5,
AL158201.1, MIR210, NLRP3P1, AL109946.1 geom_smooth() using
formula ‘y ~ x’ geom_smooth() using formula ‘y ~ x’ ##
geom_bin2d {.unnumbered }

geom_smooth() using formula ‘y ~ x’
geom_smooth() using formula ‘y ~ x’ ## geom_bin2d
{.unnumbered }

geom_smooth() using formula ‘y ~ x’
geom_smooth() using formula ‘y ~ x’ ## geom_bin2d
{.unnumbered }

NA
UMAPS
hif_targets_by_tp = FetchData(object = xeno,vars = c(hif_targets)) %>% rowSums() %>% as.data.frame() #mean expression
Warning in FetchData.Seurat(object = xeno, vars = c(hif_targets)) :
The following requested variables were not found: AK4P1, BNIP3P1, LDHAP5, AL158201.1, MIR210, NLRP3P1, AL109946.1
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
Warning in FetchData.Seurat(object = xeno, vars = c(hif_targets)) :
The following requested variables were not found: AK4P1, BNIP3P1, LDHAP5, AL158201.1, MIR210, NLRP3P1, AL109946.1
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")

Per patient
# plot correlation for every subset of hif targets
for (patient in xeno$orig.ident %>% unique()) {
patient_srt = subset(x = xeno, subset = orig.ident == patient)
hif_targets_by_tp = FetchData(object = patient_srt,vars = c(hif_targets)) %>% rowSums() %>% as.data.frame() #mean expression
tnf_and_hypoxia_patient = tnf_and_hypoxia %>% filter(rownames(tnf_and_hypoxia) %in% colnames(patient_srt)) #filter for patient
hif_targets_by_tp[,2] = tnf_and_hypoxia_patient[,1]
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()+ggtitle(patient)
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 = patient)
}
Warning in FetchData.Seurat(object = patient_srt, vars =
c(hif_targets)) : The following requested variables were not found:
AK4P1, BNIP3P1, LDHAP5, AL158201.1, MIR210, NLRP3P1, AL109946.1
geom_smooth() using formula ‘y ~ x’
geom_smooth() using formula ‘y ~ x’ ## 119 {.unnumbered
}

Warning in FetchData.Seurat(object = patient_srt, vars =
c(hif_targets)) : The following requested variables were not found:
AK4P1, BNIP3P1, LDHAP5, AL158201.1, MIR210, NLRP3P1, AL109946.1
geom_smooth() using formula ‘y ~ x’
geom_smooth() using formula ‘y ~ x’ ## PC9 {.unnumbered
}

Warning in FetchData.Seurat(object = patient_srt, vars =
c(hif_targets)) : The following requested variables were not found:
AK4P1, BNIP3P1, LDHAP5, AL158201.1, MIR210, NLRP3P1, AL109946.1
geom_smooth() using formula ‘y ~ x’
geom_smooth() using formula ‘y ~ x’ ## 1109 {.unnumbered
}

Warning in FetchData.Seurat(object = patient_srt, vars =
c(hif_targets)) : The following requested variables were not found:
AK4P1, BNIP3P1, LDHAP5, AL158201.1, MIR210, NLRP3P1, AL109946.1
geom_smooth() using formula ‘y ~ x’
geom_smooth() using formula ‘y ~ x’ ## 1071 {.unnumbered
}

Warning in FetchData.Seurat(object = patient_srt, vars =
c(hif_targets)) : The following requested variables were not found:
AK4P1, BNIP3P1, LDHAP5, AL158201.1, MIR210, NLRP3P1, AL109946.1
geom_smooth() using formula ‘y ~ x’
geom_smooth() using formula ‘y ~ x’ ## 1157 {.unnumbered
}

Warning in FetchData.Seurat(object = patient_srt, vars =
c(hif_targets)) : The following requested variables were not found:
AK4P1, BNIP3P1, LDHAP5, AL158201.1, MIR210, NLRP3P1, AL109946.1
geom_smooth() using formula ‘y ~ x’
geom_smooth() using formula ‘y ~ x’ ## 1068 {.unnumbered
}

NA
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))
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

LS0tCnRpdGxlOiAnYHIgcnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLHJlcGxhY2VtZW50ID0gIiIpYCcgCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19jb2xsYXBzZTogeWVzCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvY19kZXB0aDogMQotLS0KCiMgUGFyYW1ldGVycwoKYGBge3Igd2FybmluZz1GQUxTRX0KCmBgYAoKCiMgRnVuY3Rpb25zCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQoKbGlicmFyeShzdHJpbmdpKQpsaWJyYXJ5KHJldGljdWxhdGUpCnNvdXJjZV9mcm9tX2dpdGh1YihyZXBvc2l0b3kgPSAiREVHX2Z1bmN0aW9ucyIsdmVyc2lvbiA9ICIwLjIuMjQiKQpzb3VyY2VfZnJvbV9naXRodWIocmVwb3NpdG95ID0gImNOTUZfZnVuY3Rpb25zIix2ZXJzaW9uID0gIjAuMy44NSIsc2NyaXB0X25hbWUgPSAiY25tZl9mdW5jdGlvbl9IYXJtb255LlIiKQoKbm9fbmVnIDwtIGZ1bmN0aW9uKHgpIHsKICB4ID0geCArIGFicyhtaW4oeCkpCiAgeAp9CgpzdW1fMl9vbmUgPC0gZnVuY3Rpb24oeCkgewogIHggPXgvc3VtKHgpCiAgeAp9CmBgYAoKYGBge3B5dGhvbn0KIyBpbXBvcnQgcHl0aG9uIGZ1bmN0aW9uczoKaW1wb3J0IHR5cGVzCgpnZXRfbm9ybV9jb3VudHMgID0gci5nZXRfbm9ybV9jb3VudHMKY29kZV9vYmogPSBjb21waWxlKGdldF9ub3JtX2NvdW50cywgJzxzdHJpbmc+JywgJ2V4ZWMnKQpnZXRfbm9ybV9jb3VudHMgPSB0eXBlcy5GdW5jdGlvblR5cGUoY29kZV9vYmouY29fY29uc3RzWzBdLCBnbG9iYWxzKCkpCgpnZXRfdXNhZ2VfZnJvbV9zY29yZSAgPSByLmdldF91c2FnZV9mcm9tX3Njb3JlCmNvZGVfb2JqID0gY29tcGlsZShnZXRfdXNhZ2VfZnJvbV9zY29yZSwgJzxzdHJpbmc+JywgJ2V4ZWMnKQpnZXRfdXNhZ2VfZnJvbV9zY29yZSA9IHR5cGVzLkZ1bmN0aW9uVHlwZShjb2RlX29iai5jb19jb25zdHNbMF0sIGdsb2JhbHMoKSkKCmNvbXB1dGVfdHBtICA9IHIuY29tcHV0ZV90cG0KY29kZV9vYmogPSBjb21waWxlKGNvbXB1dGVfdHBtLCAnPHN0cmluZz4nLCAnZXhlYycpCmNvbXB1dGVfdHBtID0gdHlwZXMuRnVuY3Rpb25UeXBlKGNvZGVfb2JqLmNvX2NvbnN0c1swXSwgZ2xvYmFscygpKQpgYGAKIyBEYXRhCgpgYGB7cn0KeGVubyA9IHJlYWRSRFMoIi4vRGF0YS8xMHhfeGVub18xMDAwLlJkcyIpCmx1bmcgPSByZWFkUkRTKCIuL0RhdGEvbHVuZ19jYW5jZXJjZWxsc193aXRoVFBfb25seVBhdGllbnRzLnJkcyIpCmx1bmdfcGF0aWVudHMgPSBsdW5nJHBhdGllbnQuaWRlbnQgJT4lIHVuaXF1ZSgpICU+JSBhcy5jaGFyYWN0ZXIoKQpsdW5nX3BhdGllbnRzX2ZpbHRlcmVkID0gbHVuZ19wYXRpZW50c1shKGx1bmdfcGF0aWVudHMgJWluJSBjKCJYMTA1NW5ldyIsIlgxMDk5IikpXSAjIHJlbW92ZSBwYXRpZW50cyB3aXRoIGxlc3MgdGhhbiAxMDAgbWFsaWduYW50IGNlbGxzCmx1bmcgPSBzdWJzZXQoeCA9IGx1bmcsc3Vic2V0ID0gcGF0aWVudC5pZGVudCAlaW4lIGx1bmdfcGF0aWVudHNfZmlsdGVyZWQpCmBgYAoKIyBNb2RlbHMgMksgdmFyZ2VuZXMgCgpgYGB7cHl0aG9ufQpzdWZmaXggPSByLnN1ZmZpeAppbXBvcnQgcGlja2xlCmZyb20gY25tZiBpbXBvcnQgY05NRgpmID0gb3BlbignLi9EYXRhL2NubWYvY25tZl9vYmplY3RzL21vZGVsc18yS3ZhcmdlbmVzX2NubWZfb2JqLnBja2wnLCAncmInKQpjbm1mX29iaiA9IHBpY2tsZS5sb2FkKGYpCmYuY2xvc2UoKQpgYGAKCmBgYHtyfQojIGdlcF9zY29yZXMgPSByZWFkUkRTKCIvc2NpL2xhYnMveW90YW1kL2xhYl9zaGFyZS9hdmlzaGFpLndpemVsL1JfcHJvamVjdHMvRUdGUi9EYXRhL2NubWYvaGFybW9ueV9tb2RlbHNfZ2VwX3Njb3Jlcy5yZHMiKQpgYGAKCgpgYGB7cHl0aG9ufQpzZWxlY3RlZF9rID0gMwpkZW5zaXR5X3RocmVzaG9sZCA9IDAuMQojIGNubWZfb2JqLmNvbnNlbnN1cyhrPXNlbGVjdGVkX2ssIGRlbnNpdHlfdGhyZXNob2xkPWRlbnNpdHlfdGhyZXNob2xkKQp1c2FnZV9ub3JtLCBnZXBfc2NvcmVzLCBnZXBfdHBtLCB0b3BnZW5lcyA9IGNubWZfb2JqLmxvYWRfcmVzdWx0cyhLPXNlbGVjdGVkX2ssIGRlbnNpdHlfdGhyZXNob2xkPWRlbnNpdHlfdGhyZXNob2xkKQpgYGAKCiMgcHJvZ3JhbXMgZW5yaWNobWVudAoKCgpgYGB7cn0KZ2VwX3Njb3JlcyA9IHB5JGdlcF9zY29yZXMKZ2VwX3RwbSA9IHB5JGdlcF90cG0KdXNhZ2Vfbm9ybT0gcHkkdXNhZ2Vfbm9ybQpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD04fQpuYW1lcyAoZ2VwX3Njb3JlcykgPSBjKCJIeXBveGlhIiwiVE5GYSIsIkNlbGxfY3ljbGUiKQpwbHRfbGlzdCA9IGxpc3QoKQoKZm9yIChwcm9ncmFtICBpbiBuYW1lcyAoZ2VwX3Njb3JlcykpIHsKIHAgPSBnZ3Bsb3QoZ2VwX3Njb3JlcywgYWVzKHg9ISFlbnN5bShwcm9ncmFtKSkpICsKICBnZW9tX2RlbnNpdHkoKSt4bGFiKHByb2dyYW0pKwogICBnZW9tX3ZsaW5lKAogICAgYWVzKHhpbnRlcmNlcHQ9c29ydChnZXBfc2NvcmVzWyxwcm9ncmFtXSxUUlVFKVsyMDBdICAsY29sb3I9InRvcDIwMCIpLAogICAgICAgICAgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MSkrCiAgIGdlb21fdmxpbmUoCiAgICBhZXMoeGludGVyY2VwdD1zb3J0KGdlcF9zY29yZXNbLHByb2dyYW1dLFRSVUUpWzEwMF0gICxjb2xvcj0idG9wMTAwIiksCiAgICAgICAgICBsaW5ldHlwZT0iZGFzaGVkIiwgc2l6ZT0xKSsKICAgICAgZ2VvbV92bGluZSgKICAgIGFlcyh4aW50ZXJjZXB0PXNvcnQoZ2VwX3Njb3Jlc1sscHJvZ3JhbV0sVFJVRSlbNTBdICAsY29sb3I9InRvcDUwIiksCiAgICAgICAgICBsaW5ldHlwZT0iZGFzaGVkIiwgc2l6ZT0xKSsKICAgICAgICAgZ2VvbV92bGluZSgKICAgIGFlcyh4aW50ZXJjZXB0PXNvcnQoZ2VwX3Njb3Jlc1sscHJvZ3JhbV0sVFJVRSlbMTUwXSAgLGNvbG9yPSJ0b3AxNTAiKSwKICAgICAgICAgIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTEpKwogICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJ0b3AgbiBnZW5lcyIsIHZhbHVlcyA9IGModG9wMjAwID0gImJsdWUiLHRvcDEwMCA9ICJyZWQiLHRvcDE1MCA9ICJ5ZWxsb3ciLHRvcDUwID0gImdyZWVuIikpCiAgIHBsdF9saXN0W1twcm9ncmFtXV0gPC0gcAoKfQogCmdnYXJyYW5nZShwbG90bGlzdCA9IHBsdF9saXN0KQoKYGBgCgoKCgoKCgpgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04LCByZXN1bHRzPSdoaWRlJ30KbnRvcCA9IDE1MApwbHRfbGlzdCA9IGxpc3QoKQpoaWZfdGFyZ2V0c19zZXQgPSBkYXRhLmZyYW1lKGdzX25hbWUgPSAiaGlmX3RhcmdldHMiLGdlbmVfc3ltYm9sID0gaGlmX3RhcmdldHMpCgpmb3IgKGkgaW4gMTpuY29sKGdlcF9zY29yZXMpKSB7CiAgdG9wX2dlbmVzID0gZ2VwX3Njb3JlcyAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3Jlc1tpXSkpICNzb3J0IGJ5IHNjb3JlIGEKICB0b3AgPSBoZWFkKHJvd25hbWVzKHRvcF9nZW5lcyksbnRvcCkgI3Rha2UgdG9wIHRvcF9nZW5lc19udW0KICByZXMgPSBnZW5lc192ZWNfZW5yaWNobWVudChnZW5lcyA9IHRvcCxiYWNrZ3JvdW5kID0gcm93bmFtZXMoZ2VwX3Njb3JlcyksaG9tZXIgPSBULHRpdGxlID0gCiAgICAgICAgICAgICAgICAgICAgaSxzaWxlbnQgPSBULHJldHVybl9hbGwgPSBULGN1c3RvbV9wYXRod2F5cyAgPSBoaWZfdGFyZ2V0c19zZXQpCiAgIAogIHBsdF9saXN0W1tpXV0gPSByZXMkcGx0Cn0KZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JvYnMgPSBwbHRfbGlzdCkKYGBgCgoKCmBgYHtyfQp4ZW5vID0gRmluZFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0geGVubyxuZmVhdHVyZXMgPSAyMDAwKQp4ZW5vX3ZhcmdlbmVzID0gVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSB4ZW5vKQoKeGVub19leHByZXNzaW9uID0gRmV0Y2hEYXRhKG9iamVjdCA9IHhlbm8sdmFycyA9IHhlbm9fdmFyZ2VuZXMsc2xvdD0nY291bnRzJykKYWxsXzBfZ2VuZXMgPSBjb2xuYW1lcyh4ZW5vX2V4cHJlc3Npb24pW2NvbFN1bXMoeGVub19leHByZXNzaW9uPT0wLCBuYS5ybT1UUlVFKT09bnJvdyh4ZW5vX2V4cHJlc3Npb24pXSAjZGVsZXRlIHJvd3MgdGhhdCBoYXZlIGFsbCAwCnhlbm9fdmFyZ2VuZXMgPSB4ZW5vX3ZhcmdlbmVzWyF4ZW5vX3ZhcmdlbmVzICVpbiUgYWxsXzBfZ2VuZXNdCgpgYGAKCgoKCiMgVGVzdCB3aXRoIGV4cHIgYWZ0ZXIgaGFybW9ueQpgYGB7cHl0aG9ufQppbXBvcnQgbnVtcHkgYXMgbnAKaW1wb3J0IHNjYW5weSBhcyBzYwoKZXhwcl9hZnRlcl9oYXJtb255ID0gc2MucmVhZF9oNWFkKCcuL0RhdGEvY25tZi94ZW5vX0hhcm1vbnlfTm9OZWdfMkt2YXJnZW5lcy5oNWFkJykudG9fZGYoKQp0cG0gPSAgY29tcHV0ZV90cG0oZXhwcl9hZnRlcl9oYXJtb255KQpjbm1mX2dlbmVzID0gZXhwcl9hZnRlcl9oYXJtb255LmtleXMoKS50b19saXN0KCkKdXNhZ2VfYnlfY2FsYyA9IGdldF91c2FnZV9mcm9tX3Njb3JlKGNvdW50cz1leHByX2FmdGVyX2hhcm1vbnksdHBtPXRwbSxnZW5lcz1jbm1mX2dlbmVzLGNubWZfb2JqPWNubWZfb2JqLGs9MykKYGBgCgojIENoZWNrIGlmIG9yaWdpbmFsIGNOTUYgc2NvcmUgaXMgbGlrZSB0aGUgY2FsY3VsYXRlZCBzY29yZQpgYGB7cn0KdXNhZ2VfYnlfY2FsYyA9IHB5JHVzYWdlX2J5X2NhbGMKdXNhZ2Vfbm9ybSA9IHB5JHVzYWdlX25vcm0KY29yKHVzYWdlX2J5X2NhbGMsdXNhZ2Vfbm9ybSkKYGBgCgojIGNhbGN1bGF0ZSBzY29yZSBmb3IgWGVubwpgYGB7cHl0aG9ufQppbXBvcnQgbnVtcHkgYXMgbnAKaW1wb3J0IHNjYW5weSBhcyBzYwp4ZW5vX2V4cHJlc3Npb24gPSByLnhlbm9fZXhwcmVzc2lvbgp4ZW5vX3ZhcmdlbmVzID0gci54ZW5vX3ZhcmdlbmVzCnRwbSA9ICBjb21wdXRlX3RwbSh4ZW5vX2V4cHJlc3Npb24pCnVzYWdlX2J5X2NhbGMgPSBnZXRfdXNhZ2VfZnJvbV9zY29yZShjb3VudHM9eGVub19leHByZXNzaW9uLHRwbT10cG0sZ2VuZXM9eGVub192YXJnZW5lcywgY25tZl9vYmo9Y25tZl9vYmosaz0zKQpgYGAKCmBgYHtyfQphbGxfbWV0YWdlbmVzID0gcHkkdXNhZ2VfYnlfY2FsYwpgYGAKCiMgcHJvZ3JhbXMgZXhwcmVzc2lvbiB7LnRhYnNldH0KYGBge3IgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD05LCByZXN1bHRzPSdhc2lzJ30KCm5hbWVzIChhbGxfbWV0YWdlbmVzKSA9IGMoIkh5cG94aWEiLCJUTkZhIiwiQ2VsbF9jeWNsZSIpCiNhZGQgZWFjaCBtZXRhZ2VuZSB0byBtZXRhZGF0YQpmb3IgKGkgIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIG1ldGFnZV9tZXRhZGF0YSA9IGFsbF9tZXRhZ2VuZXMgJT4lIGRwbHlyOjpzZWxlY3QoaSkKICAjIG1ldGFnZV9tZXRhZGF0YSA9IHNjYWxlKG1ldGFnZV9tZXRhZGF0YSkKICB4ZW5vID0gQWRkTWV0YURhdGEob2JqZWN0ID0geGVubyxtZXRhZGF0YSA9IG1ldGFnZV9tZXRhZGF0YSxjb2wubmFtZSA9IG5hbWVzKGFsbF9tZXRhZ2VuZXMpW2ldKQp9CgpwcmludF90YWIocGx0ID0gRmVhdHVyZVBsb3Qob2JqZWN0ID0geGVubyxmZWF0dXJlcyA9IGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpKSx0aXRsZSA9ICJ1bWFwIGV4cHJlc3Npb24iKQoKCmBgYAoKCgojIHByb2dyYW1zIHJlZ3VsYXRpb24gey50YWJzZXR9CmBgYHtyIGVjaG89VFJVRSwgIHJlc3VsdHM9J2FzaXMnfQptZXRhZ2VuZXNfbWVhbl9jb21wYXJlKGRhdGFzZXQgPSB4ZW5vLHRpbWUucG9pbnRfdmFyID0gInRyZWF0bWVudCIscHJlZml4ID0gIm1vZGVsIixwYXRpZW50LmlkZW50X3ZhciA9ICJvcmlnLmlkZW50IixwcmVfb24gPSBjKCJOVCIsIk9TSSIpLHRlc3QgPSAid2lsY294LnRlc3QiLHByb2dyYW1zID0gYygiSHlwb3hpYSIsIlRORmEiLCJDZWxsX2N5Y2xlIikpCgpgYGAKCgoKIyBwcm9ncmFtIGFzc2lnbm1lbnQgey50YWJzZXR9CmBgYHtyfQpsYXJnZXJfYnkgPSAxLjUKeGVubyA9IHByb2dyYW1fYXNzaWdubWVudChkYXRhc2V0ID0geGVubyxsYXJnZXJfYnkgPSBsYXJnZXJfYnkscHJvZ3JhbV9uYW1lcyA9IGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpKQpgYGAgCgpgYGB7ciBlY2hvPVRSVUUsIHJlc3VsdHM9J2FzaXMnfQpwcmludF90YWIocGx0ID0gCiAgICAgICAgICAgIERpbVBsb3QoeGVubyxncm91cC5ieSA9ICJwcm9ncmFtLmFzc2lnbm1lbnQiLGNvbHMgPSBjKEh5cG94aWEgPSAicmVkIixUTkZhID0gImdyZWVuIixDZWxsX2N5Y2xlID0gImJsdWUiLCJOQSIgPSAiZ3JleSIpKQogICAgICAgICAgLHRpdGxlID0gInByb2dyYW0uYXNzaWdubWVudCIsc3VidGl0bGVfbnVtID0gMikKcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICAgIERpbVBsb3QoeGVubyxncm91cC5ieSA9ICJvcmlnLmlkZW50IikKICAgICAgICAgICx0aXRsZSA9ICJvcmlnLmlkZW50IixzdWJ0aXRsZV9udW0gPSAyKQpwcmludF90YWIocGx0ID0gCiAgICAgICAgICAgIERpbVBsb3QoeGVubyxncm91cC5ieSA9ICJ0cmVhdG1lbnQiKQogICAgICAgICAgLHRpdGxlID0gInRyZWF0bWVudCIsc3VidGl0bGVfbnVtID0gMikKCnAgPSBjZWxsX3BlcmNlbnRhZ2UoZGF0YXNldCA9IHhlbm8sdGltZS5wb2ludF92YXIgPSAidHJlYXRtZW50IixieV9wcm9ncmFtID0gVCx4X29yZGVyID0gYygiTlQiLCJPU0kiLCJyZXMiKSkKcHJpbnRfdGFiKHBsdCA9IHAsdGl0bGUgPSAiYnkgcHJvZ3JhbSIsc3VidGl0bGVfbnVtID0gMikKCnAgPSBjZWxsX3BlcmNlbnRhZ2UoZGF0YXNldCA9IHhlbm8sdGltZS5wb2ludF92YXIgPSAidHJlYXRtZW50IixieV90cCAgPSBULHhfb3JkZXIgPWMoIkh5cG94aWEiLCJUTkZhIiwiQ2VsbF9jeWNsZSIsIk5BIikpCnByaW50X3RhYihwbHQgPSBwLHRpdGxlID0gImJ5IHRpbWUgcG9pbnQiLHN1YnRpdGxlX251bSA9IDIpCgoKYGBgCgpgYGB7ciBlY2hvPVRSVUUsIHJlc3VsdHM9J2FzaXMnfQpwcmludF90YWIocGx0ID0gCiAgICAgICAgICAgIERpbVBsb3QoeGVubyxncm91cC5ieSA9ICJwcm9ncmFtLmFzc2lnbm1lbnQiLGNvbHMgPSBjKEh5cG94aWEgPSAicmVkIixUTkZhID0gImdyZWVuIixDZWxsX2N5Y2xlID0gImJsdWUiLCJOQSIgPSAiZ3JleSIpKQogICAgICAgICAgLHRpdGxlID0gInByb2dyYW0uYXNzaWdubWVudCIsc3VidGl0bGVfbnVtID0gMykKcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICAgIERpbVBsb3QoeGVubyxncm91cC5ieSA9ICJvcmlnLmlkZW50IikKICAgICAgICAgICx0aXRsZSA9ICJvcmlnLmlkZW50IixzdWJ0aXRsZV9udW0gPSAzKQpwcmludF90YWIocGx0ID0gCiAgICAgICAgICAgIERpbVBsb3QoeGVubyxncm91cC5ieSA9ICJ0cmVhdG1lbnQiKQogICAgICAgICAgLHRpdGxlID0gInRyZWF0bWVudCIsc3VidGl0bGVfbnVtID0gMykKCnAgPSBjZWxsX3BlcmNlbnRhZ2UoZGF0YXNldCA9IHhlbm8sdGltZS5wb2ludF92YXIgPSAidHJlYXRtZW50IixieV9wcm9ncmFtID0gVCx4X29yZGVyID0gYygiTlQiLCJPU0kiLCJyZXMiKSkKcHJpbnRfdGFiKHBsdCA9IHAsdGl0bGUgPSAiYnkgcHJvZ3JhbSIsc3VidGl0bGVfbnVtID0gMykKCnAgPSBjZWxsX3BlcmNlbnRhZ2UoZGF0YXNldCA9IHhlbm8sdGltZS5wb2ludF92YXIgPSAidHJlYXRtZW50IixieV90cCAgPSBULHhfb3JkZXIgPWMoIkh5cG94aWEiLCJUTkZhIiwiQ2VsbF9jeWNsZSIsIk5BIikpCnByaW50X3RhYihwbHQgPSBwLHRpdGxlID0gImJ5IHRpbWUgcG9pbnQiLHN1YnRpdGxlX251bSA9IDMpCgoKYGBgCgojIFBhdGllbnRzIHByb2dyYW1zIGV4cHJlc3Npb24gey50YWJzZXR9CmBgYHtyIGVjaG89VFJVRSwgcmVzdWx0cz0nYXNpcyd9CgpsdW5nID0gRmluZFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0gbHVuZyxuZmVhdHVyZXMgPSAyMDAwKQpnZW5lcyA9IHJvd25hbWVzKGx1bmcpW3Jvd25hbWVzKGx1bmcpICVpbiUgVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSB4ZW5vKVsxOjIwMDBdXQpsdW5nX2V4cHJlc3Npb24gPSB0KGFzLm1hdHJpeChHZXRBc3NheURhdGEobHVuZyxzbG90PSdkYXRhJykpKSAKbHVuZ19leHByZXNzaW9uID0gMioqbHVuZ19leHByZXNzaW9uICNjb252ZXJ0IGZyb20gbG9nMih0cG0rMSkgdG8gdHBtCmx1bmdfZXhwcmVzc2lvbiA9IGx1bmdfZXhwcmVzc2lvbi0xCmx1bmdfZXhwcmVzc2lvbiA9IGx1bmdfZXhwcmVzc2lvblssZ2VuZXNdICU+JSBhcy5kYXRhLmZyYW1lKCkKCmFsbF8wX2dlbmVzID0gY29sbmFtZXMobHVuZ19leHByZXNzaW9uKVtjb2xTdW1zKGx1bmdfZXhwcmVzc2lvbj09MCwgbmEucm09VFJVRSk9PW5yb3cobHVuZ19leHByZXNzaW9uKV0gI2RlbGV0ZSByb3dzIHRoYXQgaGF2ZSBhbGwgMApnZW5lcyA9IGdlbmVzWyFnZW5lcyAlaW4lIGFsbF8wX2dlbmVzXQpsdW5nX2V4cHJlc3Npb24gPSBsdW5nX2V4cHJlc3Npb25bLCFjb2xuYW1lcyhsdW5nX2V4cHJlc3Npb24pICVpbiUgYWxsXzBfZ2VuZXNdCmdjKCkKYGBgCgpgYGB7cHl0aG9ufQpsdW5nX2V4cHJlc3Npb24gPSByLmx1bmdfZXhwcmVzc2lvbgpnZW5lcyA9IHIuZ2VuZXMKCnVzYWdlX2J5X2NhbGMgPSBnZXRfdXNhZ2VfZnJvbV9zY29yZShjb3VudHM9bHVuZ19leHByZXNzaW9uLHRwbT1sdW5nX2V4cHJlc3Npb24sZ2VuZXM9Z2VuZXMsY25tZl9vYmo9Y25tZl9vYmosaz0zKQpgYGAKCmBgYHtyIGVjaG89VFJVRSwgcmVzdWx0cz0nYXNpcyd9CmFsbF9tZXRhZ2VuZXMgPSBweSR1c2FnZV9ieV9jYWxjCmFsbF9tZXRhZ2VuZXMgPSBhbGxfbWV0YWdlbmVzWyxjKDMsMiwxKV0KCm5hbWVzIChhbGxfbWV0YWdlbmVzKSA9IGMoIkh5cG94aWEiLCJUTkZhIiwiQ2VsbF9jeWNsZSIpCiNhZGQgZWFjaCBtZXRhZ2VuZSB0byBtZXRhZGF0YQpmb3IgKGkgIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIG1ldGFnZV9tZXRhZGF0YSA9IGFsbF9tZXRhZ2VuZXMgJT4lIGRwbHlyOjpzZWxlY3QoaSkKICBsdW5nID0gQWRkTWV0YURhdGEob2JqZWN0ID0gbHVuZyxtZXRhZGF0YSA9IG1ldGFnZV9tZXRhZGF0YSkKfQoKRmVhdHVyZVBsb3Qob2JqZWN0ID0gbHVuZyxmZWF0dXJlcyA9IGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpKQoKCgpgYGAKYGBge3IgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9OX0KbWV0YWdlbmVzX21lYW5fY29tcGFyZShkYXRhc2V0ID0gbHVuZyx0aW1lLnBvaW50X3ZhciA9ICJ0aW1lLnBvaW50IixwcmVmaXggPSAicGF0aWVudCIscGF0aWVudC5pZGVudF92YXIgPSAicGF0aWVudC5pZGVudCIscHJlX29uID0gYygicHJlLXRyZWF0bWVudCIsIm9uLXRyZWF0bWVudCIpLHRlc3QgPSAid2lsY294LnRlc3QiKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTl9Cm1ldGFnZW5lc19tZWFuX2NvbXBhcmUoZGF0YXNldCA9IGx1bmcsdGltZS5wb2ludF92YXIgPSAidGltZS5wb2ludCIscHJlZml4ID0gInBhdGllbnQiLHBhdGllbnQuaWRlbnRfdmFyID0gInBhdGllbnQuaWRlbnQiLHByZV9vbiA9IGMoInByZS10cmVhdG1lbnQiLCJvbi10cmVhdG1lbnQiKSkKYGBgCgojIGx1bmcgcHJvZ3JhbSBhc3NpZ25tZW50CmBgYHtyfQpsYXJnZXJfYnkgPSAxLjI1Cmx1bmcgPSBwcm9ncmFtX2Fzc2lnbm1lbnQoZGF0YXNldCA9IGx1bmcsbGFyZ2VyX2J5ID0gbGFyZ2VyX2J5LHByb2dyYW1fbmFtZXMgPSBjb2xuYW1lcyhhbGxfbWV0YWdlbmVzKSkKYGBgIAoKYGBge3IgZWNobz1UUlVFLCByZXN1bHRzPSdhc2lzJ30KcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICBEaW1QbG90KGx1bmcsZ3JvdXAuYnkgPSAicHJvZ3JhbS5hc3NpZ25tZW50Iixjb2xzID0gYyhIeXBveGlhID0gInJlZCIsVE5GYSA9ICJncmVlbiIsQ2VsbF9jeWNsZSA9ICJibHVlIiwiTkEiID0gImdyZXkiKSkKICAgICAgICAgICx0aXRsZSA9ICJwcm9ncmFtLmFzc2lnbm1lbnQiLHN1YnRpdGxlX251bSA9IDMpCnByaW50X3RhYihwbHQgPSAKICAgICAgICAgICAgICBEaW1QbG90KGx1bmcsZ3JvdXAuYnkgPSAicGF0aWVudC5pZGVudCIpCiAgICAgICAgICAsdGl0bGUgPSAicGF0aWVudC5pZGVudCIsc3VidGl0bGVfbnVtID0gMykKcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICBEaW1QbG90KGx1bmcsZ3JvdXAuYnkgPSAidGltZS5wb2ludCIpCiAgICAgICAgICAsdGl0bGUgPSAidGltZS5wb2ludCIsc3VidGl0bGVfbnVtID0gMykKCnAgPSBjZWxsX3BlcmNlbnRhZ2UoZGF0YXNldCA9IGx1bmcsdGltZS5wb2ludF92YXIgPSAidGltZS5wb2ludCIsYnlfcHJvZ3JhbSA9IFQseF9vcmRlciA9IGMoInByZS10cmVhdG1lbnQiLCJvbi10cmVhdG1lbnQiLCJyZXNpc3RhbnQiKSkKcHJpbnRfdGFiKHBsdCA9IHAsdGl0bGUgPSAiYnkgcHJvZ3JhbSIsc3VidGl0bGVfbnVtID0gMykKCnAgPSBjZWxsX3BlcmNlbnRhZ2UoZGF0YXNldCA9IGx1bmcsdGltZS5wb2ludF92YXIgPSAidGltZS5wb2ludCIsYnlfdHAgID0gVCx4X29yZGVyID1jKCJIeXBveGlhIiwiVE5GYSIsIkNlbGxfY3ljbGUiLCJOQSIpKQpwcmludF90YWIocGx0ID0gcCx0aXRsZSA9ICJieSB0aW1lIHBvaW50IixzdWJ0aXRsZV9udW0gPSAzKQoKCmBgYApgYGB7cn0KICB0b3BfZ2VuZXMgPSBnZXBfc2NvcmVzICAlPiUgIGFycmFuZ2UoZGVzYyhnZXBfc2NvcmVzWyJIeXBveGlhIl0pKSAjc29ydCBieSBzY29yZSBhCiAgdG9wID0gaGVhZChyb3duYW1lcyh0b3BfZ2VuZXMpLDIwMCkgI3Rha2UgdG9wIHRvcF9nZW5lc19udW0KICBleHByID0geGVub19leHByZXNzaW9uWyxjb2xuYW1lcyh4ZW5vX2V4cHJlc3Npb24pICVpbiUgdG9wXQogIGV4cHJfY29yID0gY29yKGV4cHIpCgogIHBodDEgPSBwaGVhdG1hcChleHByX2NvcixzaG93X2NvbG5hbWVzID0gRixzaG93X3Jvd25hbWVzID0gRiwgc2lsZW50ID0gVCkKICAgICAgCiAgCiAgbnVtX29mX2NsdXN0ZXJzID0gNApjbHVzdGVyaW5nX2Rpc3RhbmNlID0gImV1Y2xpZGVhbiIKbXlhbm5vdGF0aW9uID0gYXMuZGF0YS5mcmFtZShjdXRyZWUocGh0MVtbInRyZWVfcm93Il1dLCBrID0gbnVtX29mX2NsdXN0ZXJzKSkgI3NwbGl0IGludG8gayBjbHVzdGVycwogCm5hbWVzKG15YW5ub3RhdGlvbilbMV0gPSAiY2x1c3RlciIKICBteWFubm90YXRpb24kY2x1c3RlciA9IGFzLmZhY3RvcihteWFubm90YXRpb24kY2x1c3RlcikKICAKICBwYWxldHRlMSA8LWJyZXdlci5wYWwobnVtX29mX2NsdXN0ZXJzLCAiUGFpcmVkIikKCiAgbmFtZXMocGFsZXR0ZTEpID0gdW5pcXVlKG15YW5ub3RhdGlvbiRjbHVzdGVyKQogIGFubl9jb2xvcnMgPSBsaXN0IChjbHVzdGVyID0gcGFsZXR0ZTEpCiAgYW5ub3RhdGlvbiA9IGxpc3QoYW5uX2NvbG9ycyA9IGFubl9jb2xvcnMsIG15YW5ub3RhdGlvbiA9IG15YW5ub3RhdGlvbikKICAKICBjb2xvcnMgPC0gYyhzZXEoLTEsMSxieT0wLjAxKSkKICBteV9wYWxldHRlIDwtIGMoImJsdWUiLGNvbG9yUmFtcFBhbGV0dGUoY29sb3JzID0gYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKG4gPSBsZW5ndGgoY29sb3JzKS0zKSwgInJlZCIpCgoKICBwcmludF90YWIocGx0ID0gCiAgICAgICAgICAgICAgICBwaGVhdG1hcChtYXQgPSBleHByX2Nvcixhbm5vdGF0aW9uX2NvbCA9ICBhbm5vdGF0aW9uW1sibXlhbm5vdGF0aW9uIl1dLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm90YXRpb25bWyJhbm5fY29sb3JzIl1dLCBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSBjbHVzdGVyaW5nX2Rpc3RhbmNlLGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9IGNsdXN0ZXJpbmdfZGlzdGFuY2UsY29sb3IgPSBteV9wYWxldHRlLGJyZWFrcyA9IGNvbG9ycyxzaG93X3Jvd25hbWVzID0gRixzaG93X2NvbG5hbWVzID0gRikKICAgICAgICAgICAgLHRpdGxlID0gIkh5cG94aWEiKQogIApgYGAKCmBgYHtyfQogIHRvcF9nZW5lcyA9IGdlcF9zY29yZXMgICU+JSAgYXJyYW5nZShkZXNjKGdlcF9zY29yZXNbIlRORmEiXSkpICNzb3J0IGJ5IHNjb3JlIGEKICB0b3AgPSBoZWFkKHJvd25hbWVzKHRvcF9nZW5lcyksMjAwKSAjdGFrZSB0b3AgdG9wX2dlbmVzX251bQogIGV4cHIgPSB4ZW5vX2V4cHJlc3Npb25bLGNvbG5hbWVzKHhlbm9fZXhwcmVzc2lvbikgJWluJSB0b3BdCiAgZXhwcl9jb3IgPSBjb3IoZXhwcikKCiAgcGh0MSA9IHBoZWF0bWFwKGV4cHJfY29yLHNob3dfY29sbmFtZXMgPSBGLHNob3dfcm93bmFtZXMgPSBGLCBzaWxlbnQgPSBUKQogICAgICAKICAKICBudW1fb2ZfY2x1c3RlcnMgPSA0CmNsdXN0ZXJpbmdfZGlzdGFuY2UgPSAiZXVjbGlkZWFuIgpteWFubm90YXRpb24gPSBhcy5kYXRhLmZyYW1lKGN1dHJlZShwaHQxW1sidHJlZV9yb3ciXV0sIGsgPSBudW1fb2ZfY2x1c3RlcnMpKSAjc3BsaXQgaW50byBrIGNsdXN0ZXJzCiAKbmFtZXMobXlhbm5vdGF0aW9uKVsxXSA9ICJjbHVzdGVyIgogIG15YW5ub3RhdGlvbiRjbHVzdGVyID0gYXMuZmFjdG9yKG15YW5ub3RhdGlvbiRjbHVzdGVyKQogIAogIHBhbGV0dGUxIDwtYnJld2VyLnBhbChudW1fb2ZfY2x1c3RlcnMsICJQYWlyZWQiKQoKICBuYW1lcyhwYWxldHRlMSkgPSB1bmlxdWUobXlhbm5vdGF0aW9uJGNsdXN0ZXIpCiAgYW5uX2NvbG9ycyA9IGxpc3QgKGNsdXN0ZXIgPSBwYWxldHRlMSkKICBhbm5vdGF0aW9uID0gbGlzdChhbm5fY29sb3JzID0gYW5uX2NvbG9ycywgbXlhbm5vdGF0aW9uID0gbXlhbm5vdGF0aW9uKQogIAogIGNvbG9ycyA8LSBjKHNlcSgtMSwxLGJ5PTAuMDEpKQogIG15X3BhbGV0dGUgPC0gYygiYmx1ZSIsY29sb3JSYW1wUGFsZXR0ZShjb2xvcnMgPSBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAobiA9IGxlbmd0aChjb2xvcnMpLTMpLCAicmVkIikKCgogIHByaW50X3RhYihwbHQgPSAKICAgICAgICAgICAgICAgIHBoZWF0bWFwKG1hdCA9IGV4cHJfY29yLGFubm90YXRpb25fY29sID0gIGFubm90YXRpb25bWyJteWFubm90YXRpb24iXV0sIGFubm90YXRpb25fY29sb3JzID0gYW5ub3RhdGlvbltbImFubl9jb2xvcnMiXV0sIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9IGNsdXN0ZXJpbmdfZGlzdGFuY2UsY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gY2x1c3RlcmluZ19kaXN0YW5jZSxjb2xvciA9IG15X3BhbGV0dGUsYnJlYWtzID0gY29sb3JzLHNob3dfcm93bmFtZXMgPSBGLHNob3dfY29sbmFtZXMgPSBGKQogICAgICAgICAgICAsdGl0bGUgPSAiVE5GYSIpCiAgCmBgYAoKYGBge3J9CiAgdG9wX2dlbmVzID0gZ2VwX3Njb3JlcyAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3Jlc1siQ2VsbF9jeWNsZSJdKSkgI3NvcnQgYnkgc2NvcmUgYQogIHRvcCA9IGhlYWQocm93bmFtZXModG9wX2dlbmVzKSwyMDApICN0YWtlIHRvcCB0b3BfZ2VuZXNfbnVtCiAgZXhwciA9IHhlbm9fZXhwcmVzc2lvblssY29sbmFtZXMoeGVub19leHByZXNzaW9uKSAlaW4lIHRvcF0KICBleHByX2NvciA9IGNvcihleHByKQoKICBwaHQxID0gcGhlYXRtYXAoZXhwcl9jb3Isc2hvd19jb2xuYW1lcyA9IEYsc2hvd19yb3duYW1lcyA9IEYsIHNpbGVudCA9IFQpCiAgICAgIAogIAogIG51bV9vZl9jbHVzdGVycyA9IDQKY2x1c3RlcmluZ19kaXN0YW5jZSA9ICJldWNsaWRlYW4iCm15YW5ub3RhdGlvbiA9IGFzLmRhdGEuZnJhbWUoY3V0cmVlKHBodDFbWyJ0cmVlX3JvdyJdXSwgayA9IG51bV9vZl9jbHVzdGVycykpICNzcGxpdCBpbnRvIGsgY2x1c3RlcnMKIApuYW1lcyhteWFubm90YXRpb24pWzFdID0gImNsdXN0ZXIiCiAgbXlhbm5vdGF0aW9uJGNsdXN0ZXIgPSBhcy5mYWN0b3IobXlhbm5vdGF0aW9uJGNsdXN0ZXIpCiAgCiAgcGFsZXR0ZTEgPC1icmV3ZXIucGFsKG51bV9vZl9jbHVzdGVycywgIlBhaXJlZCIpCgogIG5hbWVzKHBhbGV0dGUxKSA9IHVuaXF1ZShteWFubm90YXRpb24kY2x1c3RlcikKICBhbm5fY29sb3JzID0gbGlzdCAoY2x1c3RlciA9IHBhbGV0dGUxKQogIGFubm90YXRpb24gPSBsaXN0KGFubl9jb2xvcnMgPSBhbm5fY29sb3JzLCBteWFubm90YXRpb24gPSBteWFubm90YXRpb24pCiAgCiAgY29sb3JzIDwtIGMoc2VxKC0xLDEsYnk9MC4wMSkpCiAgbXlfcGFsZXR0ZSA8LSBjKCJibHVlIixjb2xvclJhbXBQYWxldHRlKGNvbG9ycyA9IGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChuID0gbGVuZ3RoKGNvbG9ycyktMyksICJyZWQiKQoKCiAgcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICAgICAgcGhlYXRtYXAobWF0ID0gZXhwcl9jb3IsYW5ub3RhdGlvbl9jb2wgPSAgYW5ub3RhdGlvbltbIm15YW5ub3RhdGlvbiJdXSwgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vdGF0aW9uW1siYW5uX2NvbG9ycyJdXSwgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gY2x1c3RlcmluZ19kaXN0YW5jZSxjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSBjbHVzdGVyaW5nX2Rpc3RhbmNlLGNvbG9yID0gbXlfcGFsZXR0ZSxicmVha3MgPSBjb2xvcnMsc2hvd19yb3duYW1lcyA9IEYsc2hvd19jb2xuYW1lcyA9IEYpCiAgICAgICAgICAgICx0aXRsZSA9ICJDZWxsX2N5Y2xlIikKICAKYGBgCgpgYGB7cn0KdG9wX2dlbmVzID0gZ2VwX3Njb3JlcyAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3Jlc1siSHlwb3hpYSJdKSkgI3NvcnQgYnkgc2NvcmUgYQpoeXBveGlhX2dlbmVzID0gaGVhZChyb3duYW1lcyh0b3BfZ2VuZXMpLDIwKSAjdGFrZSB0b3AgdG9wX2dlbmVzX251bQppbnRlcnNlY3QoY2x1c3Rlcl8zX2dlbmVzLGh5cG94aWFfZ2VuZXMpCmBgYAoKYGBge3J9CmxpYnJhcnkoZ2d2ZW5uKQphbGwgPSBsaXN0KGh5cG94aWFfZ2VuZXMgPSBoeXBveGlhX2dlbmVzLCBoaWZfdGFyZ2V0cyA9IGNsdXN0ZXJfM19nZW5lcykKZ2d2ZW5uKAogIGFsbAopCmBgYAoKCgojIEhJRl90YXJnZXRzLSBIeXBveGlhIGNvcnJlbGF0aW9uICB7LnRhYnNldH0KYGBge3IgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD02LCByZXN1bHRzPSdhc2lzJ30KIyBwbG90IGNvcnJlbGF0aW9uIGZvciBldmVyeSBzdWJzZXQgb2YgaGlmIHRhcmdldHMKZm9yIChnZW5lcyBpbiBsaXN0KGhpZl90YXJnZXRzLHhlbm9fY2x1c3Rlcl8zX2dlbmVzLHhlbm9fY2x1c3Rlcl8zXzJfZ2VuZXMpKSB7CiAgaGlmX3RhcmdldHNfYnlfdHAgPSBGZXRjaERhdGEob2JqZWN0ID0geGVubyx2YXJzID0gYyhnZW5lcykpICU+JSByb3dTdW1zKCkgJT4lIGFzLmRhdGEuZnJhbWUoKSAjbWVhbiBleHByZXNzaW9uCiAgaGlmX3RhcmdldHNfYnlfdHBbLDJdID0geGVubyRIeXBveGlhCiAgCiAgbmFtZXMoaGlmX3RhcmdldHNfYnlfdHApID0gYygiaGlmX3RhcmdldHMiLCJoeXBveGlhX3Byb2dyYW0iKQogIAogIAogIAogIHAxID0gZ2dwbG90KGhpZl90YXJnZXRzX2J5X3RwLCBhZXMoeD1oaWZfdGFyZ2V0cywgeT1oeXBveGlhX3Byb2dyYW0pKSArIAogICAgICBnZW9tX3BvaW50KCkrCiAgICBnZW9tX2RlbnNpdHlfMmQoYWVzKGNvbG9yID0gLi5sZXZlbC4uKSkgKwogICAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtKSArCiAgICBzdGF0X2NvcihtZXRob2QgPSAicGVhcnNvbiIsIGxhYmVsLnggPSAyMCwgbGFiZWwueSA9IDEuMSkrCiAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKQogIAogIHAyID0gZ2dwbG90KGhpZl90YXJnZXRzX2J5X3RwLCBhZXMoeD1oaWZfdGFyZ2V0cywgeT1oeXBveGlhX3Byb2dyYW0pKSArIAogICAgZ2VvbV9iaW4yZCgpICsKICAgIHRoZW1lX2J3KCkrIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGxpbWl0cz1jKDAsMTEwMCksIGJyZWFrcz1zZXEoMCwgMTEwMCwgYnk9MjAwKSwgY29sb3Vycz1jKCJibHVlIiwieWVsbG93IiwicmVkIikpKyAKICAgIHN0YXRfY29yKG1ldGhvZCA9ICJwZWFyc29uIiwgbGFiZWwueCA9IDIwLCBsYWJlbC55ID0gMS4xKSsKICAgIGdlb21fc21vb3RoKG1ldGhvZD1sbSkgCiAgCiAgcCA9IGdnYXJyYW5nZShwbG90bGlzdCA9IGxpc3QocDEscDIpLG5yb3cgID0gMikgIAogIAogIHByaW50X3RhYihwbHQgPSBwLHRpdGxlID0gImdlb21fYmluMmQiKQp9CgoKYGBgCgoKIyBVTUFQUwpgYGB7ciBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD0xMH0KaGlmX3RhcmdldHNfYnlfdHAgPSBGZXRjaERhdGEob2JqZWN0ID0geGVubyx2YXJzID0gYyhoaWZfdGFyZ2V0cykpICU+JSByb3dTdW1zKCkgJT4lIGFzLmRhdGEuZnJhbWUoKSAjbWVhbiBleHByZXNzaW9uCmhpZl90YXJnZXRzX2J5X3RwWywyXSA9IHhlbm8kSHlwb3hpYQpuYW1lcyhoaWZfdGFyZ2V0c19ieV90cCkgPSBjKCJoaWZfdGFyZ2V0cyIsImh5cG94aWFfcHJvZ3JhbSIpCgpoaWdoX2hpZl9sb3dfaHlwb3hpYV9jZWxscyA9IGhpZl90YXJnZXRzX2J5X3RwICU+JSBmaWx0ZXIoaGlmX3RhcmdldHM+MjUgJiBoeXBveGlhX3Byb2dyYW0gPCAwLjIpICU+JSByb3duYW1lcygpCgpoaWZfdGFyZ2V0c19ieV90cCA9IEZldGNoRGF0YShvYmplY3QgPSB4ZW5vLHZhcnMgPSBjKGhpZl90YXJnZXRzKSkgJT4lIHJvd1N1bXMoKSAlPiUgYXMuZGF0YS5mcmFtZSgpICNtZWFuIGV4cHJlc3Npb24KeGVubyA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IHhlbm8sIG1ldGFkYXRhID0gaGlmX3RhcmdldHNfYnlfdHAsY29sLm5hbWUgPSAiSElGX3RhcmdldHNfc2NvcmUiKQpoaWdoX2hpZl9sb3dfaHlwb3hpYV9jZWxscyA9ICBkYXRhLmZyYW1lKCBoaWdoX0hJRl9sb3dfSHlwb3hpYSA9IGhpZ2hfaGlmX2xvd19oeXBveGlhX2NlbGxzKQoKRGltUGxvdChvYmplY3QgPSB4ZW5vLCBjZWxscy5oaWdobGlnaHQgPSBoaWdoX2hpZl9sb3dfaHlwb3hpYV9jZWxscywgY29scy5oaWdobGlnaHQgPSAicmVkIiwgY29scyA9ICJncmF5Iiwgb3JkZXIgPSBUUlVFKQpGZWF0dXJlUGxvdChvYmplY3QgPSB4ZW5vLGZlYXR1cmVzID0gYyggIkhJRl90YXJnZXRzX3Njb3JlIiwiSHlwb3hpYSIsIkNlbGxfY3ljbGUiICkpCgpgYGAKIyBDYWxjdWxhdGUgdXNhZ2Ugd2l0aG91dCBjYyBpbiBzdW0gdG8gMQpgYGB7cHl0aG9ufQpkZWYgZ2V0X3VzYWdlX2Zyb21fc2NvcmUoY291bnRzLHRwbSwgZ2VuZXMsY25tZl9vYmosayk6CiAgICAgIGltcG9ydCBhbm5kYXRhIGFzIGFkCiAgICAgIGltcG9ydCBzY2FucHkgYXMgc2MKICAgICAgaW1wb3J0IG51bXB5IGFzIG5wCiAgICAgIGZyb20gc2tsZWFybi5kZWNvbXBvc2l0aW9uIGltcG9ydCBub25fbmVnYXRpdmVfZmFjdG9yaXphdGlvbgogICAgICBpbXBvcnQgcGFuZGFzIGFzIHBkCiAgICAgIGNvdW50c19hZGF0YSA9IGFkLkFubkRhdGEoY291bnRzKQogICAgICB0cG1fYWRhdGEgPSBhZC5Bbm5EYXRhKHRwbSkKICAgICAgbm9ybV9jb3VudHMgPSBnZXRfbm9ybV9jb3VudHMoY291bnRzPWNvdW50c19hZGF0YSx0cG09dHBtX2FkYXRhLGhpZ2hfdmFyaWFuY2VfZ2VuZXNfZmlsdGVyPW5wLmFycmF5KGdlbmVzKSkgI25vcm0gY291bnRzIGxpa2UgY25tZgogICAgICBzcGVjdHJhID0gY25tZl9vYmouZ2V0X21lZGlhbl9zcGVjdHJhKGs9aykgI2dldCBzY29yZSAKICAgICAgc3BlY3RyYSA9IHNwZWN0cmFbc3BlY3RyYS5jb2x1bW5zLmludGVyc2VjdGlvbihnZW5lcyldICNyZW1vdmUgZ2VuZXMgbm90IGluIEBnZW5lcwogICAgICBzcGVjdHJhID0gc3BlY3RyYS5ULnJlaW5kZXgobm9ybV9jb3VudHMudG9fZGYoKS5jb2x1bW5zKS5UICNyZW9yZGVyIHNwZWN0cmEgZ2VuZXMgbGlrZSBub3JtX2NvdW50cwogICAgICAKICAgICAgdXNhZ2VfYnlfY2FsYyxfLF8gPSBub25fbmVnYXRpdmVfZmFjdG9yaXphdGlvbihYPW5vcm1fY291bnRzLlgsIEggPSBzcGVjdHJhLnZhbHVlcywgdXBkYXRlX0g9RmFsc2Usbl9jb21wb25lbnRzID0gayxtYXhfaXRlcj0xMDAwLGluaXQgPSdyYW5kb20nKQogICAgICB1c2FnZV9ieV9jYWxjID0gcGQuRGF0YUZyYW1lKHVzYWdlX2J5X2NhbGMsIGluZGV4PWNvdW50cy5pbmRleCwgY29sdW1ucz1zcGVjdHJhLmluZGV4KSAjaW5zZXJ0IHRvIGRmK2FkZCBuYW1lcwogICAgICAjIHVzYWdlX2J5X2NhbGMgPSB1c2FnZV9ieV9jYWxjLmRpdih1c2FnZV9ieV9jYWxjLnN1bShheGlzPTEpLCBheGlzPTApICMgc3VtIHJvd3MgdG8gMQogICAgICByZW9yZGVyID0gdXNhZ2VfYnlfY2FsYy5zdW0oYXhpcz0wKS5zb3J0X3ZhbHVlcyhhc2NlbmRpbmc9RmFsc2UpCiAgICAgIHVzYWdlX2J5X2NhbGMgPSB1c2FnZV9ieV9jYWxjLmxvY1s6LCByZW9yZGVyLmluZGV4XQogICAgICByZXR1cm4odXNhZ2VfYnlfY2FsYykKCmBgYAoKYGBge3B5dGhvbn0KaW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCBzY2FucHkgYXMgc2MKeGVub19leHByZXNzaW9uID0gci54ZW5vX2V4cHJlc3Npb24KeGVub192YXJnZW5lcyA9IHIueGVub192YXJnZW5lcwp0cG0gPSAgY29tcHV0ZV90cG0oeGVub19leHByZXNzaW9uKQp1c2FnZV9ieV9jYWxjID0gZ2V0X3VzYWdlX2Zyb21fc2NvcmUoY291bnRzPXhlbm9fZXhwcmVzc2lvbix0cG09dHBtLGdlbmVzPXhlbm9fdmFyZ2VuZXMsIGNubWZfb2JqPWNubWZfb2JqLGs9MykKYGBgCmBgYHtyfQphbGxfbWV0YWdlbmVzX25vU3VtVG8xID0gcHkkdXNhZ2VfYnlfY2FsYwp0bmZfYW5kX2h5cG94aWEgPSBhbGxfbWV0YWdlbmVzX25vU3VtVG8xWywxOjJdCnRuZl9hbmRfaHlwb3hpYSA9IGFwcGx5KFggPSB0bmZfYW5kX2h5cG94aWEsIE1BUkdJTiA9IDEsIHN1bV8yX29uZSkgJT4lIHQoKSAlPiUgIGFzLmRhdGEuZnJhbWUoKQp0bmZfYW5kX2h5cG94aWFbaXMubmEodG5mX2FuZF9oeXBveGlhKV0gPC0gMCAjcmVwbGFjZSBOQU4ncyB3aXRoIDAuCmBgYAoKYGBge3IgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD02LCByZXN1bHRzPSdhc2lzJ30KIyBwbG90IGNvcnJlbGF0aW9uIGZvciBldmVyeSBzdWJzZXQgb2YgaGlmIHRhcmdldHMKZm9yIChnZW5lcyBpbiBsaXN0KGhpZl90YXJnZXRzLHhlbm9fY2x1c3Rlcl8zX2dlbmVzLHhlbm9fY2x1c3Rlcl8zXzJfZ2VuZXMpKSB7CiAgaGlmX3RhcmdldHNfYnlfdHAgPSBGZXRjaERhdGEob2JqZWN0ID0geGVubyx2YXJzID0gYyhnZW5lcykpICU+JSByb3dTdW1zKCkgJT4lIGFzLmRhdGEuZnJhbWUoKSAjbWVhbiBleHByZXNzaW9uCiAgaGlmX3RhcmdldHNfYnlfdHBbLDJdID0gdG5mX2FuZF9oeXBveGlhWywxXQogICMgaGlmX3RhcmdldHNfYnlfdHBbLDJdID0geGVubyRIeXBveGlhCiAgCiAgbmFtZXMoaGlmX3RhcmdldHNfYnlfdHApID0gYygiaGlmX3RhcmdldHMiLCJoeXBveGlhX3Byb2dyYW0iKQogIAogIAogIAogIHAxID0gZ2dwbG90KGhpZl90YXJnZXRzX2J5X3RwLCBhZXMoeD1oaWZfdGFyZ2V0cywgeT1oeXBveGlhX3Byb2dyYW0pKSArIAogICAgICBnZW9tX3BvaW50KCkrCiAgICBnZW9tX2RlbnNpdHlfMmQoYWVzKGNvbG9yID0gLi5sZXZlbC4uKSkgKwogICAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtKSArCiAgICBzdGF0X2NvcihtZXRob2QgPSAicGVhcnNvbiIsIGxhYmVsLnggPSAyMCwgbGFiZWwueSA9IDEuMSkrCiAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKQogIAogIHAyID0gZ2dwbG90KGhpZl90YXJnZXRzX2J5X3RwLCBhZXMoeD1oaWZfdGFyZ2V0cywgeT1oeXBveGlhX3Byb2dyYW0pKSArIAogICAgZ2VvbV9iaW4yZCgpICsKICAgIHRoZW1lX2J3KCkrIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGxpbWl0cz1jKDAsMTEwMCksIGJyZWFrcz1zZXEoMCwgMTEwMCwgYnk9MjAwKSwgY29sb3Vycz1jKCJibHVlIiwieWVsbG93IiwicmVkIikpKyAKICAgIHN0YXRfY29yKG1ldGhvZCA9ICJwZWFyc29uIiwgbGFiZWwueCA9IDIwLCBsYWJlbC55ID0gMS4xKSsKICAgIGdlb21fc21vb3RoKG1ldGhvZD1sbSkgCiAgCiAgcCA9IGdnYXJyYW5nZShwbG90bGlzdCA9IGxpc3QocDEscDIpLG5yb3cgID0gMikgIAogIAogIHByaW50X3RhYihwbHQgPSBwLHRpdGxlID0gImdlb21fYmluMmQiKQp9CgoKYGBgCgojIFVNQVBTCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTh9CmhpZl90YXJnZXRzX2J5X3RwID0gRmV0Y2hEYXRhKG9iamVjdCA9IHhlbm8sdmFycyA9IGMoaGlmX3RhcmdldHMpKSAlPiUgcm93U3VtcygpICU+JSBhcy5kYXRhLmZyYW1lKCkgI21lYW4gZXhwcmVzc2lvbgpoaWZfdGFyZ2V0c19ieV90cFssMl0gPSB0bmZfYW5kX2h5cG94aWFbLDFdCm5hbWVzKGhpZl90YXJnZXRzX2J5X3RwKSA9IGMoImhpZl90YXJnZXRzIiwiaHlwb3hpYV9wcm9ncmFtIikKCmhpZ2hfaGlmX2xvd19oeXBveGlhX2NlbGxzID0gaGlmX3RhcmdldHNfYnlfdHAgJT4lIGZpbHRlcihoaWZfdGFyZ2V0cz4yNSAmIGh5cG94aWFfcHJvZ3JhbSA8IDAuMikgJT4lIHJvd25hbWVzKCkKCmhpZl90YXJnZXRzX2J5X3RwID0gRmV0Y2hEYXRhKG9iamVjdCA9IHhlbm8sdmFycyA9IGMoaGlmX3RhcmdldHMpKSAlPiUgcm93U3VtcygpICU+JSBhcy5kYXRhLmZyYW1lKCkgI21lYW4gZXhwcmVzc2lvbgp4ZW5vID0gQWRkTWV0YURhdGEob2JqZWN0ID0geGVubywgbWV0YWRhdGEgPSBoaWZfdGFyZ2V0c19ieV90cCxjb2wubmFtZSA9ICJISUZfdGFyZ2V0c19zY29yZSIpCnhlbm8gPSBBZGRNZXRhRGF0YShvYmplY3QgPSB4ZW5vLCBtZXRhZGF0YSA9IHRuZl9hbmRfaHlwb3hpYVssMV0sY29sLm5hbWUgPSAiSHlwb3hpYTIiKQoKRGltUGxvdChvYmplY3QgPSB4ZW5vLCBjZWxscy5oaWdobGlnaHQgPSBoaWdoX2hpZl9sb3dfaHlwb3hpYV9jZWxscywgY29scy5oaWdobGlnaHQgPSAicmVkIiwgY29scyA9ICJncmF5Iiwgb3JkZXIgPSBUUlVFKQpGZWF0dXJlUGxvdChvYmplY3QgPSB4ZW5vLGZlYXR1cmVzID0gYyggIkhJRl90YXJnZXRzX3Njb3JlIiwiSHlwb3hpYTIiLCJDZWxsX2N5Y2xlIiApKQpGZWF0dXJlUGxvdChvYmplY3QgPSB4ZW5vLGZlYXR1cmVzID0gYygiSHlwb3hpYTIiKSkKCmBgYAoKCmBgYHtyfQpEaW1QbG90KG9iamVjdCA9IHhlbm8sZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpCmBgYAojIFBlciBwYXRpZW50CmBgYHtyIGVjaG89VFJVRSwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9NiwgcmVzdWx0cz0nYXNpcyd9CiMgcGxvdCBjb3JyZWxhdGlvbiBmb3IgZXZlcnkgc3Vic2V0IG9mIGhpZiB0YXJnZXRzCmZvciAocGF0aWVudCBpbiB4ZW5vJG9yaWcuaWRlbnQgJT4lIHVuaXF1ZSgpKSB7CiAgcGF0aWVudF9zcnQgPSBzdWJzZXQoeCA9IHhlbm8sIHN1YnNldCA9IG9yaWcuaWRlbnQgPT0gcGF0aWVudCkKICBoaWZfdGFyZ2V0c19ieV90cCA9IEZldGNoRGF0YShvYmplY3QgPSBwYXRpZW50X3NydCx2YXJzID0gYyhoaWZfdGFyZ2V0cykpICU+JSByb3dTdW1zKCkgJT4lIGFzLmRhdGEuZnJhbWUoKSAjbWVhbiBleHByZXNzaW9uCiAgdG5mX2FuZF9oeXBveGlhX3BhdGllbnQgID0gdG5mX2FuZF9oeXBveGlhICU+JSBmaWx0ZXIocm93bmFtZXModG5mX2FuZF9oeXBveGlhKSAlaW4lIGNvbG5hbWVzKHBhdGllbnRfc3J0KSkgI2ZpbHRlciBmb3IgcGF0aWVudAogIGhpZl90YXJnZXRzX2J5X3RwWywyXSA9IHRuZl9hbmRfaHlwb3hpYV9wYXRpZW50WywxXQoKICBuYW1lcyhoaWZfdGFyZ2V0c19ieV90cCkgPSBjKCJoaWZfdGFyZ2V0cyIsImh5cG94aWFfcHJvZ3JhbSIpCiAgCiAgCiAgCiAgcDEgPSBnZ3Bsb3QoaGlmX3RhcmdldHNfYnlfdHAsIGFlcyh4PWhpZl90YXJnZXRzLCB5PWh5cG94aWFfcHJvZ3JhbSkpICsgCiAgICAgIGdlb21fcG9pbnQoKSsKICAgIGdlb21fZGVuc2l0eV8yZChhZXMoY29sb3IgPSAuLmxldmVsLi4pKSArCiAgICBnZW9tX3Ntb290aChtZXRob2Q9bG0pICsKICAgIHN0YXRfY29yKG1ldGhvZCA9ICJwZWFyc29uIiwgbGFiZWwueCA9IDIwLCBsYWJlbC55ID0gMS4xKSsKICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpK2dndGl0bGUocGF0aWVudCkKICAKICBwMiA9IGdncGxvdChoaWZfdGFyZ2V0c19ieV90cCwgYWVzKHg9aGlmX3RhcmdldHMsIHk9aHlwb3hpYV9wcm9ncmFtKSkgKyAKICAgIGdlb21fYmluMmQoKSArCiAgICB0aGVtZV9idygpKyBzY2FsZV9maWxsX2dyYWRpZW50bihsaW1pdHM9YygwLDExMDApLCBicmVha3M9c2VxKDAsIDExMDAsIGJ5PTIwMCksIGNvbG91cnM9YygiYmx1ZSIsInllbGxvdyIsInJlZCIpKSsgCiAgICBzdGF0X2NvcihtZXRob2QgPSAicGVhcnNvbiIsIGxhYmVsLnggPSAyMCwgbGFiZWwueSA9IDEuMSkrCiAgICBnZW9tX3Ntb290aChtZXRob2Q9bG0pIAogIAogIHAgPSBnZ2FycmFuZ2UocGxvdGxpc3QgPSBsaXN0KHAxLHAyKSxucm93ICA9IDIpICAKICAKICBwcmludF90YWIocGx0ID0gcCx0aXRsZSA9IHBhdGllbnQpCn0KCgpgYGAKCiMgSHlwb3hpYSByYXcKYGBge3IgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9Cnhlbm8gPSBBZGRNZXRhRGF0YShvYmplY3QgPSB4ZW5vLG1ldGFkYXRhID0gYWxsX21ldGFnZW5lc19ub1N1bVRvMVssMV0sY29sLm5hbWUgPSAiaHlwb3hpYV9yYXciKQpGZWF0dXJlUGxvdChvYmplY3QgPSB4ZW5vLGZlYXR1cmVzID0gImh5cG94aWFfcmF3IikgKyBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3VycyA9IHJhaW5ib3coNSksIGxpbWl0cyA9IGMoMCwgMzAwMCkpCmBgYAoKCgo8c2NyaXB0IHNyYz0iaHR0cHM6Ly9oeXBvdGhlcy5pcy9lbWJlZC5qcyIgYXN5bmM+PC9zY3JpcHQ+Cg==