1 Parameters

2 Functions


library(stringi)
library(reticulate)
source_from_github(repositoy = "DEG_functions",version = "0.2.24")
source_from_github(repositoy = "cNMF_functions",version = "0.3.84",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())

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

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

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

names (gep_scores) = c("Hypoxia","TNFa","Cell_cycle")

library(highcharter) 
options(highcharter.theme = hc_theme_smpl(tooltip = list(valueDecimals = 2)))

for (program  in names (gep_scores)) {
  print(
        hchart(
      density(gep_scores[,program]), 
      type = "area", name = program
      )
  )
}
for (program  in names (gep_scores)) {
  print(
    ggplot(gep_scores, aes(gep_scores[,program])) + stat_ecdf(geom = "step")

  )
}
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_expression = FetchData(object = xeno_expression,vars = vargenes)
Error in UseMethod(generic = "FetchData", object = object) : 
  no applicable method for 'FetchData' applied to an object of class "data.frame"

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

6 Check if original cNMF score is like the calculated score

# cnmf_genes = py$cnmf_genes %>% as.data.frame()
# a = vargenes %>% as.data.frame()
usage_by_calc = py$usage_by_calc
usage_by_calc = usage_by_calc[,c(3,2,1)]
usage_norm = py$usage_norm
cor(usage_by_calc,usage_norm)
           1          2          3
3  0.9994225 -0.6424758 -0.3558304
2 -0.6333332  0.9995869 -0.4933343
1 -0.3536662 -0.4929504  0.9998665

7 calculate score for Xeno

def compute_tpm(input_counts):
    """
    Default TPM normalization
    """
    tpm = input_counts.copy()
    sc.pp.normalize_per_cell(tpm, counts_per_cell_after=1e6,copy=True)
    return(tpm)
all_metagenes = py$usage_by_calc

8 programs expression

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)
  xeno = AddMetaData(object = xeno,metadata = metage_metadata)
}

print_tab(plt = FeaturePlot(object = xeno,features = colnames(all_metagenes)),title = "umap expression")

umap expression

NA

9 programs regulation

metagenes_mean_compare(dataset = xeno,time.point_var = "treatment",prefix = "model",patient.ident_var = "orig.ident",pre_on = c("NT","OSI"),test = "wilcox.test",programs = c("Hypoxia","TNFa","Cell_cycle"))

Hypoxia per patient

Hypoxia

TNFa per patient

TNFa

Cell_cycle per patient

Cell_cycle

NA

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

program.assignment

print_tab(plt = 
              DimPlot(xeno,group.by = "orig.ident")
          ,title = "orig.ident",subtitle_num = 2)

orig.ident

print_tab(plt = 
            DimPlot(xeno,group.by = "treatment")
          ,title = "treatment",subtitle_num = 2)

treatment

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)

by program

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)

by time point

NA

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)

11 Patients programs expression

quit
lung = FindVariableFeatures(object = lung,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% [—-|—-|—-|—-|—-|—-|—-|—-|—-|—-| **************************************************|

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

11.1 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)
[1] "ERO1A" "PGK1" 
library(ggvenn)
all = list(hypoxia_genes = hypoxia_genes, hif_targets = cluster_3_genes)
ggvenn(
  all
)

12 HIF_targets- Hypoxia correlation

hif_targets_by_tp = FetchData(object = xeno,vars = c(cluster_3_genes)) %>% rowSums() %>% as.data.frame() #mean expression
hif_targets_by_tp[,2] = xeno$Hypoxia
names(hif_targets_by_tp) = c("hif_targets","hypoxia_program")

p = ggplot(hif_targets_by_tp, aes(x=hif_targets, y=hypoxia_program)) + 
  geom_point()+
  geom_smooth(method=lm)
print_tab(plt = p,title = "points")

points

geom_smooth() using formula ‘y ~ x’

p = ggplot(hif_targets_by_tp, aes(x=hif_targets, y=hypoxia_program)) + 
  # geom_point()+
  geom_smooth(method=lm) +
  stat_density_2d(geom = "point", aes(size = after_stat(density)), n = 20, contour = FALSE)+ stat_cor(method = "pearson", label.x = 20, label.y = 1.1)
print_tab(plt = p,title = "points density")

points density

geom_smooth() using formula ‘y ~ x’

p = ggplot(hif_targets_by_tp, aes(x=hif_targets, y=hypoxia_program)) + 
    geom_smooth(method=lm) +
    geom_point()+
  geom_density_2d(aes(color = ..level..)) +
  scale_color_viridis_c()
print_tab(plt = p,title = "polygon")

polygon

geom_smooth() using formula ‘y ~ x’

NA

hif_targets_score = FetchData(object = lung,vars = c(cluster_3_genes)) %>% rowSums() %>% as.data.frame() #mean expression
lung = AddMetaData(object = lung,metadata = hif_targets_score,col.name = "HIF_targets_score")
FeaturePlot(lung,features = "HIF_targets_score")
LS0tCnRpdGxlOiAnYHIgcnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLHJlcGxhY2VtZW50ID0gIiIpYCcgCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19jb2xsYXBzZTogeWVzCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvY19kZXB0aDogMQotLS0KCiMgUGFyYW1ldGVycwoKYGBge3Igd2FybmluZz1GQUxTRX0KCmBgYAoKCiMgRnVuY3Rpb25zCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQoKbGlicmFyeShzdHJpbmdpKQpsaWJyYXJ5KHJldGljdWxhdGUpCnNvdXJjZV9mcm9tX2dpdGh1YihyZXBvc2l0b3kgPSAiREVHX2Z1bmN0aW9ucyIsdmVyc2lvbiA9ICIwLjIuMjQiKQpzb3VyY2VfZnJvbV9naXRodWIocmVwb3NpdG95ID0gImNOTUZfZnVuY3Rpb25zIix2ZXJzaW9uID0gIjAuMy44NCIsc2NyaXB0X25hbWUgPSAiY25tZl9mdW5jdGlvbl9IYXJtb255LlIiKQoKbm9fbmVnIDwtIGZ1bmN0aW9uKHgpIHsKICB4ID0geCArIGFicyhtaW4oeCkpCiAgeAp9CgpzdW1fMl9vbmUgPC0gZnVuY3Rpb24oeCkgewogIHggPXgvc3VtKHgpCiAgeAp9CmBgYAoKYGBge3B5dGhvbn0KIyBpbXBvcnQgcHl0aG9uIGZ1bmN0aW9uczoKaW1wb3J0IHR5cGVzCgpnZXRfbm9ybV9jb3VudHMgID0gci5nZXRfbm9ybV9jb3VudHMKY29kZV9vYmogPSBjb21waWxlKGdldF9ub3JtX2NvdW50cywgJzxzdHJpbmc+JywgJ2V4ZWMnKQpnZXRfbm9ybV9jb3VudHMgPSB0eXBlcy5GdW5jdGlvblR5cGUoY29kZV9vYmouY29fY29uc3RzWzBdLCBnbG9iYWxzKCkpCgpnZXRfdXNhZ2VfZnJvbV9zY29yZSAgPSByLmdldF91c2FnZV9mcm9tX3Njb3JlCmNvZGVfb2JqID0gY29tcGlsZShnZXRfdXNhZ2VfZnJvbV9zY29yZSwgJzxzdHJpbmc+JywgJ2V4ZWMnKQpnZXRfdXNhZ2VfZnJvbV9zY29yZSA9IHR5cGVzLkZ1bmN0aW9uVHlwZShjb2RlX29iai5jb19jb25zdHNbMF0sIGdsb2JhbHMoKSkKCmNvbXB1dGVfdHBtICA9IHIuY29tcHV0ZV90cG0KY29kZV9vYmogPSBjb21waWxlKGNvbXB1dGVfdHBtLCAnPHN0cmluZz4nLCAnZXhlYycpCmNvbXB1dGVfdHBtID0gdHlwZXMuRnVuY3Rpb25UeXBlKGNvZGVfb2JqLmNvX2NvbnN0c1swXSwgZ2xvYmFscygpKQpgYGAKIyBEYXRhCgpgYGB7cn0KeGVubyA9IHJlYWRSRFMoIi4vRGF0YS8xMHhfeGVub18xMDAwLlJkcyIpCmx1bmcgPSByZWFkUkRTKCIuL0RhdGEvbHVuZ19jYW5jZXJjZWxsc193aXRoVFBfb25seVBhdGllbnRzLnJkcyIpCmx1bmdfcGF0aWVudHMgPSBsdW5nJHBhdGllbnQuaWRlbnQgJT4lIHVuaXF1ZSgpICU+JSBhcy5jaGFyYWN0ZXIoKQpsdW5nX3BhdGllbnRzX2ZpbHRlcmVkID0gbHVuZ19wYXRpZW50c1shKGx1bmdfcGF0aWVudHMgJWluJSBjKCJYMTA1NW5ldyIsIlgxMDk5IikpXSAjIHJlbW92ZSBwYXRpZW50cyB3aXRoIGxlc3MgdGhhbiAxMDAgbWFsaWduYW50IGNlbGxzCmx1bmcgPSBzdWJzZXQoeCA9IGx1bmcsc3Vic2V0ID0gcGF0aWVudC5pZGVudCAlaW4lIGx1bmdfcGF0aWVudHNfZmlsdGVyZWQpCmBgYAoKIyBNb2RlbHMgMksgdmFyZ2VuZXMgCgpgYGB7cHl0aG9ufQpzdWZmaXggPSByLnN1ZmZpeAppbXBvcnQgcGlja2xlCmZyb20gY25tZiBpbXBvcnQgY05NRgpmID0gb3BlbignLi9EYXRhL2NubWYvY25tZl9vYmplY3RzL21vZGVsc18yS3ZhcmdlbmVzX2NubWZfb2JqLnBja2wnLCAncmInKQpjbm1mX29iaiA9IHBpY2tsZS5sb2FkKGYpCmYuY2xvc2UoKQpgYGAKCmBgYHtyfQojIGdlcF9zY29yZXMgPSByZWFkUkRTKCIvc2NpL2xhYnMveW90YW1kL2xhYl9zaGFyZS9hdmlzaGFpLndpemVsL1JfcHJvamVjdHMvRUdGUi9EYXRhL2NubWYvaGFybW9ueV9tb2RlbHNfZ2VwX3Njb3Jlcy5yZHMiKQpgYGAKCgpgYGB7cHl0aG9ufQpzZWxlY3RlZF9rID0gMwpkZW5zaXR5X3RocmVzaG9sZCA9IDAuMQojIGNubWZfb2JqLmNvbnNlbnN1cyhrPXNlbGVjdGVkX2ssIGRlbnNpdHlfdGhyZXNob2xkPWRlbnNpdHlfdGhyZXNob2xkKQp1c2FnZV9ub3JtLCBnZXBfc2NvcmVzLCBnZXBfdHBtLCB0b3BnZW5lcyA9IGNubWZfb2JqLmxvYWRfcmVzdWx0cyhLPXNlbGVjdGVkX2ssIGRlbnNpdHlfdGhyZXNob2xkPWRlbnNpdHlfdGhyZXNob2xkKQpgYGAKCiMjIHByb2dyYW1zIGVucmljaG1lbnQKCgoKYGBge3J9CmdlcF9zY29yZXMgPSBweSRnZXBfc2NvcmVzCmdlcF90cG0gPSBweSRnZXBfdHBtCnVzYWdlX25vcm09IHB5JHVzYWdlX25vcm0KYGBgCgoKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OH0KbmFtZXMgKGdlcF9zY29yZXMpID0gYygiSHlwb3hpYSIsIlRORmEiLCJDZWxsX2N5Y2xlIikKcGx0X2xpc3QgPSBsaXN0KCkKCmZvciAocHJvZ3JhbSAgaW4gbmFtZXMgKGdlcF9zY29yZXMpKSB7CiBwID0gZ2dwbG90KGdlcF9zY29yZXMsIGFlcyh4PSEhZW5zeW0ocHJvZ3JhbSkpKSArCiAgZ2VvbV9kZW5zaXR5KCkreGxhYihwcm9ncmFtKSsKICAgZ2VvbV92bGluZSgKICAgIGFlcyh4aW50ZXJjZXB0PXNvcnQoZ2VwX3Njb3Jlc1sscHJvZ3JhbV0sVFJVRSlbMjAwXSAgLGNvbG9yPSJ0b3AyMDAiKSwKICAgICAgICAgIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTEpKwogICBnZW9tX3ZsaW5lKAogICAgYWVzKHhpbnRlcmNlcHQ9c29ydChnZXBfc2NvcmVzWyxwcm9ncmFtXSxUUlVFKVsxMDBdICAsY29sb3I9InRvcDEwMCIpLAogICAgICAgICAgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MSkrCiAgICAgIGdlb21fdmxpbmUoCiAgICBhZXMoeGludGVyY2VwdD1zb3J0KGdlcF9zY29yZXNbLHByb2dyYW1dLFRSVUUpWzUwXSAgLGNvbG9yPSJ0b3A1MCIpLAogICAgICAgICAgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MSkrCiAgICAgICAgIGdlb21fdmxpbmUoCiAgICBhZXMoeGludGVyY2VwdD1zb3J0KGdlcF9zY29yZXNbLHByb2dyYW1dLFRSVUUpWzE1MF0gICxjb2xvcj0idG9wMTUwIiksCiAgICAgICAgICBsaW5ldHlwZT0iZGFzaGVkIiwgc2l6ZT0xKSsKICAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAidG9wIG4gZ2VuZXMiLCB2YWx1ZXMgPSBjKHRvcDIwMCA9ICJibHVlIix0b3AxMDAgPSAicmVkIix0b3AxNTAgPSAieWVsbG93Iix0b3A1MCA9ICJncmVlbiIpKQogICBwbHRfbGlzdFtbcHJvZ3JhbV1dIDwtIHAKCn0KIApnZ2FycmFuZ2UocGxvdGxpc3QgPSBwbHRfbGlzdCkKCmBgYAoKCgpgYGB7cn0KbmFtZXMgKGdlcF9zY29yZXMpID0gYygiSHlwb3hpYSIsIlRORmEiLCJDZWxsX2N5Y2xlIikKCmxpYnJhcnkoaGlnaGNoYXJ0ZXIpIApvcHRpb25zKGhpZ2hjaGFydGVyLnRoZW1lID0gaGNfdGhlbWVfc21wbCh0b29sdGlwID0gbGlzdCh2YWx1ZURlY2ltYWxzID0gMikpKQoKZm9yIChwcm9ncmFtICBpbiBuYW1lcyAoZ2VwX3Njb3JlcykpIHsKICBwcmludCgKICAgICAgICBoY2hhcnQoCiAgICAgIGRlbnNpdHkoZ2VwX3Njb3Jlc1sscHJvZ3JhbV0pLCAKICAgICAgdHlwZSA9ICJhcmVhIiwgbmFtZSA9IHByb2dyYW0KICAgICAgKQogICkKfQoKCmBgYAoKCmBgYHtyfQpmb3IgKHByb2dyYW0gIGluIG5hbWVzIChnZXBfc2NvcmVzKSkgewogIHByaW50KAogICAgZ2dwbG90KGdlcF9zY29yZXMsIGFlcyhnZXBfc2NvcmVzWyxwcm9ncmFtXSkpICsgc3RhdF9lY2RmKGdlb20gPSAic3RlcCIpCgogICkKfQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTgsIHJlc3VsdHM9J2hpZGUnfQpudG9wID0gMTUwCnBsdF9saXN0ID0gbGlzdCgpCmhpZl90YXJnZXRzX3NldCA9IGRhdGEuZnJhbWUoZ3NfbmFtZSA9ICJoaWZfdGFyZ2V0cyIsZ2VuZV9zeW1ib2wgPSBoaWZfdGFyZ2V0cykKCmZvciAoaSBpbiAxOm5jb2woZ2VwX3Njb3JlcykpIHsKICB0b3BfZ2VuZXMgPSBnZXBfc2NvcmVzICAlPiUgIGFycmFuZ2UoZGVzYyhnZXBfc2NvcmVzW2ldKSkgI3NvcnQgYnkgc2NvcmUgYQogIHRvcCA9IGhlYWQocm93bmFtZXModG9wX2dlbmVzKSxudG9wKSAjdGFrZSB0b3AgdG9wX2dlbmVzX251bQogIHJlcyA9IGdlbmVzX3ZlY19lbnJpY2htZW50KGdlbmVzID0gdG9wLGJhY2tncm91bmQgPSByb3duYW1lcyhnZXBfc2NvcmVzKSxob21lciA9IFQsdGl0bGUgPSAKICAgICAgICAgICAgICAgICAgICBpLHNpbGVudCA9IFQscmV0dXJuX2FsbCA9IFQsY3VzdG9tX3BhdGh3YXlzICA9IGhpZl90YXJnZXRzX3NldCkKICAgCiAgcGx0X2xpc3RbW2ldXSA9IHJlcyRwbHQKfQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IHBsdF9saXN0KQpgYGAKCgoKYGBge3J9Cnhlbm8gPSBGaW5kVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSB4ZW5vLG5mZWF0dXJlcyA9IDIwMDApCnZhcmdlbmVzID0gVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSB4ZW5vKQoKeGVub19leHByZXNzaW9uID0gRmV0Y2hEYXRhKG9iamVjdCA9IHhlbm8sdmFycyA9IHZhcmdlbmVzLHNsb3Q9J2NvdW50cycpCmFsbF8wX2dlbmVzID0gY29sbmFtZXMoeGVub19leHByZXNzaW9uKVtjb2xTdW1zKHhlbm9fZXhwcmVzc2lvbj09MCwgbmEucm09VFJVRSk9PW5yb3coeGVub19leHByZXNzaW9uKV0gI2RlbGV0ZSByb3dzIHRoYXQgaGF2ZSBhbGwgMAp2YXJnZW5lcyA9IHZhcmdlbmVzWyF2YXJnZW5lcyAlaW4lIGFsbF8wX2dlbmVzXQoKYGBgCgoKCgojIFRlc3Qgd2l0aCBleHByIGFmdGVyIGhhcm1vbnkKYGBge3B5dGhvbn0KaW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCBzY2FucHkgYXMgc2MKCmV4cHJfYWZ0ZXJfaGFybW9ueSA9IHNjLnJlYWRfaDVhZCgnLi9EYXRhL2NubWYveGVub19IYXJtb255X05vTmVnXzJLdmFyZ2VuZXMuaDVhZCcpLnRvX2RmKCkKdHBtID0gIGNvbXB1dGVfdHBtKGV4cHJfYWZ0ZXJfaGFybW9ueSkKY25tZl9nZW5lcyA9IGV4cHJfYWZ0ZXJfaGFybW9ueS5rZXlzKCkudG9fbGlzdCgpCnVzYWdlX2J5X2NhbGMgPSBnZXRfdXNhZ2VfZnJvbV9zY29yZShjb3VudHM9ZXhwcl9hZnRlcl9oYXJtb255LHRwbT10cG0sZ2VuZXM9Y25tZl9nZW5lcyxjbm1mX29iaj1jbm1mX29iaixrPTMpCmBgYAoKIyBDaGVjayBpZiBvcmlnaW5hbCBjTk1GIHNjb3JlIGlzIGxpa2UgdGhlIGNhbGN1bGF0ZWQgc2NvcmUKYGBge3J9CnVzYWdlX2J5X2NhbGMgPSBweSR1c2FnZV9ieV9jYWxjCnVzYWdlX2J5X2NhbGMgPSB1c2FnZV9ieV9jYWxjWyxjKDMsMiwxKV0KdXNhZ2Vfbm9ybSA9IHB5JHVzYWdlX25vcm0KY29yKHVzYWdlX2J5X2NhbGMsdXNhZ2Vfbm9ybSkKYGBgCgojIGNhbGN1bGF0ZSBzY29yZSBmb3IgWGVubwpgYGB7cHl0aG9ufQppbXBvcnQgbnVtcHkgYXMgbnAKaW1wb3J0IHNjYW5weSBhcyBzYwp4ZW5vX2V4cHJlc3Npb24gPSByLnhlbm9fZXhwcmVzc2lvbgp2YXJnZW5lcyA9IHIudmFyZ2VuZXMKdHBtID0gIGNvbXB1dGVfdHBtKHhlbm9fZXhwcmVzc2lvbikKdXNhZ2VfYnlfY2FsYyA9IGdldF91c2FnZV9mcm9tX3Njb3JlKGNvdW50cz14ZW5vX2V4cHJlc3Npb24sdHBtPXRwbSxnZW5lcz12YXJnZW5lcyxjbm1mX29iaj1jbm1mX29iaixrPTMpCmBgYAoKYGBge3J9CmFsbF9tZXRhZ2VuZXMgPSBweSR1c2FnZV9ieV9jYWxjCmBgYAoKIyBwcm9ncmFtcyBleHByZXNzaW9uIHsudGFic2V0fQpgYGB7ciBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTksIHJlc3VsdHM9J2FzaXMnfQphbGxfbWV0YWdlbmVzID0gYWxsX21ldGFnZW5lc1ssYygzLDIsMSldCgpuYW1lcyAoYWxsX21ldGFnZW5lcykgPSBjKCJIeXBveGlhIiwiVE5GYSIsIkNlbGxfY3ljbGUiKQojYWRkIGVhY2ggbWV0YWdlbmUgdG8gbWV0YWRhdGEKZm9yIChpICBpbiAxOm5jb2woYWxsX21ldGFnZW5lcykpIHsKICBtZXRhZ2VfbWV0YWRhdGEgPSBhbGxfbWV0YWdlbmVzICU+JSBkcGx5cjo6c2VsZWN0KGkpCiAgeGVubyA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IHhlbm8sbWV0YWRhdGEgPSBtZXRhZ2VfbWV0YWRhdGEpCn0KCnByaW50X3RhYihwbHQgPSBGZWF0dXJlUGxvdChvYmplY3QgPSB4ZW5vLGZlYXR1cmVzID0gY29sbmFtZXMoYWxsX21ldGFnZW5lcykpLHRpdGxlID0gInVtYXAgZXhwcmVzc2lvbiIpCgoKYGBgCgoKIyBwcm9ncmFtcyByZWd1bGF0aW9uIHsudGFic2V0fQpgYGB7ciBlY2hvPVRSVUUsICByZXN1bHRzPSdhc2lzJ30KbWV0YWdlbmVzX21lYW5fY29tcGFyZShkYXRhc2V0ID0geGVubyx0aW1lLnBvaW50X3ZhciA9ICJ0cmVhdG1lbnQiLHByZWZpeCA9ICJtb2RlbCIscGF0aWVudC5pZGVudF92YXIgPSAib3JpZy5pZGVudCIscHJlX29uID0gYygiTlQiLCJPU0kiKSx0ZXN0ID0gIndpbGNveC50ZXN0Iixwcm9ncmFtcyA9IGMoIkh5cG94aWEiLCJUTkZhIiwiQ2VsbF9jeWNsZSIpKQoKYGBgCgoKCiMgcHJvZ3JhbSBhc3NpZ25tZW50IHsudGFic2V0fQpgYGB7cn0KbGFyZ2VyX2J5ID0gMS41Cnhlbm8gPSBwcm9ncmFtX2Fzc2lnbm1lbnQoZGF0YXNldCA9IHhlbm8sbGFyZ2VyX2J5ID0gbGFyZ2VyX2J5LHByb2dyYW1fbmFtZXMgPSBjb2xuYW1lcyhhbGxfbWV0YWdlbmVzKSkKYGBgIAoKYGBge3IgZWNobz1UUlVFLCByZXN1bHRzPSdhc2lzJ30KcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICBEaW1QbG90KHhlbm8sZ3JvdXAuYnkgPSAicHJvZ3JhbS5hc3NpZ25tZW50Iixjb2xzID0gYyhIeXBveGlhID0gInJlZCIsVE5GYSA9ICJncmVlbiIsQ2VsbF9jeWNsZSA9ICJibHVlIiwiTkEiID0gImdyZXkiKSkKICAgICAgICAgICx0aXRsZSA9ICJwcm9ncmFtLmFzc2lnbm1lbnQiLHN1YnRpdGxlX251bSA9IDIpCnByaW50X3RhYihwbHQgPSAKICAgICAgICAgICAgICBEaW1QbG90KHhlbm8sZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpCiAgICAgICAgICAsdGl0bGUgPSAib3JpZy5pZGVudCIsc3VidGl0bGVfbnVtID0gMikKcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICBEaW1QbG90KHhlbm8sZ3JvdXAuYnkgPSAidHJlYXRtZW50IikKICAgICAgICAgICx0aXRsZSA9ICJ0cmVhdG1lbnQiLHN1YnRpdGxlX251bSA9IDIpCgpwID0gY2VsbF9wZXJjZW50YWdlKGRhdGFzZXQgPSB4ZW5vLHRpbWUucG9pbnRfdmFyID0gInRyZWF0bWVudCIsYnlfcHJvZ3JhbSA9IFQseF9vcmRlciA9IGMoIk5UIiwiT1NJIiwicmVzIikpCnByaW50X3RhYihwbHQgPSBwLHRpdGxlID0gImJ5IHByb2dyYW0iLHN1YnRpdGxlX251bSA9IDIpCgpwID0gY2VsbF9wZXJjZW50YWdlKGRhdGFzZXQgPSB4ZW5vLHRpbWUucG9pbnRfdmFyID0gInRyZWF0bWVudCIsYnlfdHAgID0gVCx4X29yZGVyID1jKCJIeXBveGlhIiwiVE5GYSIsIkNlbGxfY3ljbGUiLCJOQSIpKQpwcmludF90YWIocGx0ID0gcCx0aXRsZSA9ICJieSB0aW1lIHBvaW50IixzdWJ0aXRsZV9udW0gPSAyKQoKCmBgYAoKYGBge3IgZWNobz1UUlVFLCByZXN1bHRzPSdhc2lzJ30KcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICBEaW1QbG90KHhlbm8sZ3JvdXAuYnkgPSAicHJvZ3JhbS5hc3NpZ25tZW50Iixjb2xzID0gYyhIeXBveGlhID0gInJlZCIsVE5GYSA9ICJncmVlbiIsQ2VsbF9jeWNsZSA9ICJibHVlIiwiTkEiID0gImdyZXkiKSkKICAgICAgICAgICx0aXRsZSA9ICJwcm9ncmFtLmFzc2lnbm1lbnQiLHN1YnRpdGxlX251bSA9IDMpCnByaW50X3RhYihwbHQgPSAKICAgICAgICAgICAgICBEaW1QbG90KHhlbm8sZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpCiAgICAgICAgICAsdGl0bGUgPSAib3JpZy5pZGVudCIsc3VidGl0bGVfbnVtID0gMykKcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICBEaW1QbG90KHhlbm8sZ3JvdXAuYnkgPSAidHJlYXRtZW50IikKICAgICAgICAgICx0aXRsZSA9ICJ0cmVhdG1lbnQiLHN1YnRpdGxlX251bSA9IDMpCgpwID0gY2VsbF9wZXJjZW50YWdlKGRhdGFzZXQgPSB4ZW5vLHRpbWUucG9pbnRfdmFyID0gInRyZWF0bWVudCIsYnlfcHJvZ3JhbSA9IFQseF9vcmRlciA9IGMoIk5UIiwiT1NJIiwicmVzIikpCnByaW50X3RhYihwbHQgPSBwLHRpdGxlID0gImJ5IHByb2dyYW0iLHN1YnRpdGxlX251bSA9IDMpCgpwID0gY2VsbF9wZXJjZW50YWdlKGRhdGFzZXQgPSB4ZW5vLHRpbWUucG9pbnRfdmFyID0gInRyZWF0bWVudCIsYnlfdHAgID0gVCx4X29yZGVyID1jKCJIeXBveGlhIiwiVE5GYSIsIkNlbGxfY3ljbGUiLCJOQSIpKQpwcmludF90YWIocGx0ID0gcCx0aXRsZSA9ICJieSB0aW1lIHBvaW50IixzdWJ0aXRsZV9udW0gPSAzKQoKCmBgYAoKIyBQYXRpZW50cyBwcm9ncmFtcyBleHByZXNzaW9uIHsudGFic2V0fQpgYGB7ciBlY2hvPVRSVUUsIHJlc3VsdHM9J2FzaXMnfQoKbHVuZyA9IEZpbmRWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IGx1bmcsbmZlYXR1cmVzID0gMjAwMCkKZ2VuZXMgPSByb3duYW1lcyhsdW5nKVtyb3duYW1lcyhsdW5nKSAlaW4lIFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0geGVubylbMToyMDAwXV0KbHVuZ19leHByZXNzaW9uID0gdChhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGx1bmcsc2xvdD0nZGF0YScpKSkgCmx1bmdfZXhwcmVzc2lvbiA9IDIqKmx1bmdfZXhwcmVzc2lvbiAjY29udmVydCBmcm9tIGxvZzIodHBtKzEpIHRvIHRwbQpsdW5nX2V4cHJlc3Npb24gPSBsdW5nX2V4cHJlc3Npb24tMQpsdW5nX2V4cHJlc3Npb24gPSBsdW5nX2V4cHJlc3Npb25bLGdlbmVzXSAlPiUgYXMuZGF0YS5mcmFtZSgpCgphbGxfMF9nZW5lcyA9IGNvbG5hbWVzKGx1bmdfZXhwcmVzc2lvbilbY29sU3VtcyhsdW5nX2V4cHJlc3Npb249PTAsIG5hLnJtPVRSVUUpPT1ucm93KGx1bmdfZXhwcmVzc2lvbildICNkZWxldGUgcm93cyB0aGF0IGhhdmUgYWxsIDAKZ2VuZXMgPSBnZW5lc1shZ2VuZXMgJWluJSBhbGxfMF9nZW5lc10KbHVuZ19leHByZXNzaW9uID0gbHVuZ19leHByZXNzaW9uWywhY29sbmFtZXMobHVuZ19leHByZXNzaW9uKSAlaW4lIGFsbF8wX2dlbmVzXQpnYygpCmBgYAoKYGBge3B5dGhvbn0KbHVuZ19leHByZXNzaW9uID0gci5sdW5nX2V4cHJlc3Npb24KZ2VuZXMgPSByLmdlbmVzCgp1c2FnZV9ieV9jYWxjID0gZ2V0X3VzYWdlX2Zyb21fc2NvcmUoY291bnRzPWx1bmdfZXhwcmVzc2lvbix0cG09bHVuZ19leHByZXNzaW9uLGdlbmVzPWdlbmVzLGNubWZfb2JqPWNubWZfb2JqLGs9MykKYGBgCgpgYGB7ciBlY2hvPVRSVUUsIHJlc3VsdHM9J2FzaXMnfQphbGxfbWV0YWdlbmVzID0gcHkkdXNhZ2VfYnlfY2FsYwphbGxfbWV0YWdlbmVzID0gYWxsX21ldGFnZW5lc1ssYygzLDIsMSldCgpuYW1lcyAoYWxsX21ldGFnZW5lcykgPSBjKCJIeXBveGlhIiwiVE5GYSIsIkNlbGxfY3ljbGUiKQojYWRkIGVhY2ggbWV0YWdlbmUgdG8gbWV0YWRhdGEKZm9yIChpICBpbiAxOm5jb2woYWxsX21ldGFnZW5lcykpIHsKICBtZXRhZ2VfbWV0YWRhdGEgPSBhbGxfbWV0YWdlbmVzICU+JSBkcGx5cjo6c2VsZWN0KGkpCiAgbHVuZyA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IGx1bmcsbWV0YWRhdGEgPSBtZXRhZ2VfbWV0YWRhdGEpCn0KCkZlYXR1cmVQbG90KG9iamVjdCA9IGx1bmcsZmVhdHVyZXMgPSBjb2xuYW1lcyhhbGxfbWV0YWdlbmVzKSkKCgoKYGBgCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTl9Cm1ldGFnZW5lc19tZWFuX2NvbXBhcmUoZGF0YXNldCA9IGx1bmcsdGltZS5wb2ludF92YXIgPSAidGltZS5wb2ludCIscHJlZml4ID0gInBhdGllbnQiLHBhdGllbnQuaWRlbnRfdmFyID0gInBhdGllbnQuaWRlbnQiLHByZV9vbiA9IGMoInByZS10cmVhdG1lbnQiLCJvbi10cmVhdG1lbnQiKSx0ZXN0ID0gIndpbGNveC50ZXN0IikKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD05fQptZXRhZ2VuZXNfbWVhbl9jb21wYXJlKGRhdGFzZXQgPSBsdW5nLHRpbWUucG9pbnRfdmFyID0gInRpbWUucG9pbnQiLHByZWZpeCA9ICJwYXRpZW50IixwYXRpZW50LmlkZW50X3ZhciA9ICJwYXRpZW50LmlkZW50IixwcmVfb24gPSBjKCJwcmUtdHJlYXRtZW50Iiwib24tdHJlYXRtZW50IikpCmBgYAoKIyMgbHVuZyBwcm9ncmFtIGFzc2lnbm1lbnR7LnRhYnNldH0KYGBge3J9Cmxhcmdlcl9ieSA9IDEuMjUKbHVuZyA9IHByb2dyYW1fYXNzaWdubWVudChkYXRhc2V0ID0gbHVuZyxsYXJnZXJfYnkgPSBsYXJnZXJfYnkscHJvZ3JhbV9uYW1lcyA9IGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpKQpgYGAgCgpgYGB7ciBlY2hvPVRSVUUsIHJlc3VsdHM9J2FzaXMnfQpwcmludF90YWIocGx0ID0gCiAgICAgICAgICAgIERpbVBsb3QobHVuZyxncm91cC5ieSA9ICJwcm9ncmFtLmFzc2lnbm1lbnQiLGNvbHMgPSBjKEh5cG94aWEgPSAicmVkIixUTkZhID0gImdyZWVuIixDZWxsX2N5Y2xlID0gImJsdWUiLCJOQSIgPSAiZ3JleSIpKQogICAgICAgICAgLHRpdGxlID0gInByb2dyYW0uYXNzaWdubWVudCIsc3VidGl0bGVfbnVtID0gMykKcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICAgIERpbVBsb3QobHVuZyxncm91cC5ieSA9ICJwYXRpZW50LmlkZW50IikKICAgICAgICAgICx0aXRsZSA9ICJwYXRpZW50LmlkZW50IixzdWJ0aXRsZV9udW0gPSAzKQpwcmludF90YWIocGx0ID0gCiAgICAgICAgICAgIERpbVBsb3QobHVuZyxncm91cC5ieSA9ICJ0aW1lLnBvaW50IikKICAgICAgICAgICx0aXRsZSA9ICJ0aW1lLnBvaW50IixzdWJ0aXRsZV9udW0gPSAzKQoKcCA9IGNlbGxfcGVyY2VudGFnZShkYXRhc2V0ID0gbHVuZyx0aW1lLnBvaW50X3ZhciA9ICJ0aW1lLnBvaW50IixieV9wcm9ncmFtID0gVCx4X29yZGVyID0gYygicHJlLXRyZWF0bWVudCIsIm9uLXRyZWF0bWVudCIsInJlc2lzdGFudCIpKQpwcmludF90YWIocGx0ID0gcCx0aXRsZSA9ICJieSBwcm9ncmFtIixzdWJ0aXRsZV9udW0gPSAzKQoKcCA9IGNlbGxfcGVyY2VudGFnZShkYXRhc2V0ID0gbHVuZyx0aW1lLnBvaW50X3ZhciA9ICJ0aW1lLnBvaW50IixieV90cCAgPSBULHhfb3JkZXIgPWMoIkh5cG94aWEiLCJUTkZhIiwiQ2VsbF9jeWNsZSIsIk5BIikpCnByaW50X3RhYihwbHQgPSBwLHRpdGxlID0gImJ5IHRpbWUgcG9pbnQiLHN1YnRpdGxlX251bSA9IDMpCgoKYGBgCmBgYHtyfQogIHRvcF9nZW5lcyA9IGdlcF9zY29yZXMgICU+JSAgYXJyYW5nZShkZXNjKGdlcF9zY29yZXNbIkh5cG94aWEiXSkpICNzb3J0IGJ5IHNjb3JlIGEKICB0b3AgPSBoZWFkKHJvd25hbWVzKHRvcF9nZW5lcyksMjAwKSAjdGFrZSB0b3AgdG9wX2dlbmVzX251bQogIGV4cHIgPSB4ZW5vX2V4cHJlc3Npb25bLGNvbG5hbWVzKHhlbm9fZXhwcmVzc2lvbikgJWluJSB0b3BdCiAgZXhwcl9jb3IgPSBjb3IoZXhwcikKCiAgcGh0MSA9IHBoZWF0bWFwKGV4cHJfY29yLHNob3dfY29sbmFtZXMgPSBGLHNob3dfcm93bmFtZXMgPSBGLCBzaWxlbnQgPSBUKQogICAgICAKICAKICBudW1fb2ZfY2x1c3RlcnMgPSA0CmNsdXN0ZXJpbmdfZGlzdGFuY2UgPSAiZXVjbGlkZWFuIgpteWFubm90YXRpb24gPSBhcy5kYXRhLmZyYW1lKGN1dHJlZShwaHQxW1sidHJlZV9yb3ciXV0sIGsgPSBudW1fb2ZfY2x1c3RlcnMpKSAjc3BsaXQgaW50byBrIGNsdXN0ZXJzCiAKbmFtZXMobXlhbm5vdGF0aW9uKVsxXSA9ICJjbHVzdGVyIgogIG15YW5ub3RhdGlvbiRjbHVzdGVyID0gYXMuZmFjdG9yKG15YW5ub3RhdGlvbiRjbHVzdGVyKQogIAogIHBhbGV0dGUxIDwtYnJld2VyLnBhbChudW1fb2ZfY2x1c3RlcnMsICJQYWlyZWQiKQoKICBuYW1lcyhwYWxldHRlMSkgPSB1bmlxdWUobXlhbm5vdGF0aW9uJGNsdXN0ZXIpCiAgYW5uX2NvbG9ycyA9IGxpc3QgKGNsdXN0ZXIgPSBwYWxldHRlMSkKICBhbm5vdGF0aW9uID0gbGlzdChhbm5fY29sb3JzID0gYW5uX2NvbG9ycywgbXlhbm5vdGF0aW9uID0gbXlhbm5vdGF0aW9uKQogIAogIGNvbG9ycyA8LSBjKHNlcSgtMSwxLGJ5PTAuMDEpKQogIG15X3BhbGV0dGUgPC0gYygiYmx1ZSIsY29sb3JSYW1wUGFsZXR0ZShjb2xvcnMgPSBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAobiA9IGxlbmd0aChjb2xvcnMpLTMpLCAicmVkIikKCgogIHByaW50X3RhYihwbHQgPSAKICAgICAgICAgICAgICAgIHBoZWF0bWFwKG1hdCA9IGV4cHJfY29yLGFubm90YXRpb25fY29sID0gIGFubm90YXRpb25bWyJteWFubm90YXRpb24iXV0sIGFubm90YXRpb25fY29sb3JzID0gYW5ub3RhdGlvbltbImFubl9jb2xvcnMiXV0sIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9IGNsdXN0ZXJpbmdfZGlzdGFuY2UsY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gY2x1c3RlcmluZ19kaXN0YW5jZSxjb2xvciA9IG15X3BhbGV0dGUsYnJlYWtzID0gY29sb3JzLHNob3dfcm93bmFtZXMgPSBGLHNob3dfY29sbmFtZXMgPSBGKQogICAgICAgICAgICAsdGl0bGUgPSAiSHlwb3hpYSIpCiAgCmBgYAoKYGBge3J9CiAgdG9wX2dlbmVzID0gZ2VwX3Njb3JlcyAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3Jlc1siVE5GYSJdKSkgI3NvcnQgYnkgc2NvcmUgYQogIHRvcCA9IGhlYWQocm93bmFtZXModG9wX2dlbmVzKSwyMDApICN0YWtlIHRvcCB0b3BfZ2VuZXNfbnVtCiAgZXhwciA9IHhlbm9fZXhwcmVzc2lvblssY29sbmFtZXMoeGVub19leHByZXNzaW9uKSAlaW4lIHRvcF0KICBleHByX2NvciA9IGNvcihleHByKQoKICBwaHQxID0gcGhlYXRtYXAoZXhwcl9jb3Isc2hvd19jb2xuYW1lcyA9IEYsc2hvd19yb3duYW1lcyA9IEYsIHNpbGVudCA9IFQpCiAgICAgIAogIAogIG51bV9vZl9jbHVzdGVycyA9IDQKY2x1c3RlcmluZ19kaXN0YW5jZSA9ICJldWNsaWRlYW4iCm15YW5ub3RhdGlvbiA9IGFzLmRhdGEuZnJhbWUoY3V0cmVlKHBodDFbWyJ0cmVlX3JvdyJdXSwgayA9IG51bV9vZl9jbHVzdGVycykpICNzcGxpdCBpbnRvIGsgY2x1c3RlcnMKIApuYW1lcyhteWFubm90YXRpb24pWzFdID0gImNsdXN0ZXIiCiAgbXlhbm5vdGF0aW9uJGNsdXN0ZXIgPSBhcy5mYWN0b3IobXlhbm5vdGF0aW9uJGNsdXN0ZXIpCiAgCiAgcGFsZXR0ZTEgPC1icmV3ZXIucGFsKG51bV9vZl9jbHVzdGVycywgIlBhaXJlZCIpCgogIG5hbWVzKHBhbGV0dGUxKSA9IHVuaXF1ZShteWFubm90YXRpb24kY2x1c3RlcikKICBhbm5fY29sb3JzID0gbGlzdCAoY2x1c3RlciA9IHBhbGV0dGUxKQogIGFubm90YXRpb24gPSBsaXN0KGFubl9jb2xvcnMgPSBhbm5fY29sb3JzLCBteWFubm90YXRpb24gPSBteWFubm90YXRpb24pCiAgCiAgY29sb3JzIDwtIGMoc2VxKC0xLDEsYnk9MC4wMSkpCiAgbXlfcGFsZXR0ZSA8LSBjKCJibHVlIixjb2xvclJhbXBQYWxldHRlKGNvbG9ycyA9IGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChuID0gbGVuZ3RoKGNvbG9ycyktMyksICJyZWQiKQoKCiAgcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICAgICAgcGhlYXRtYXAobWF0ID0gZXhwcl9jb3IsYW5ub3RhdGlvbl9jb2wgPSAgYW5ub3RhdGlvbltbIm15YW5ub3RhdGlvbiJdXSwgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vdGF0aW9uW1siYW5uX2NvbG9ycyJdXSwgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gY2x1c3RlcmluZ19kaXN0YW5jZSxjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSBjbHVzdGVyaW5nX2Rpc3RhbmNlLGNvbG9yID0gbXlfcGFsZXR0ZSxicmVha3MgPSBjb2xvcnMsc2hvd19yb3duYW1lcyA9IEYsc2hvd19jb2xuYW1lcyA9IEYpCiAgICAgICAgICAgICx0aXRsZSA9ICJUTkZhIikKICAKYGBgCgpgYGB7cn0KICB0b3BfZ2VuZXMgPSBnZXBfc2NvcmVzICAlPiUgIGFycmFuZ2UoZGVzYyhnZXBfc2NvcmVzWyJDZWxsX2N5Y2xlIl0pKSAjc29ydCBieSBzY29yZSBhCiAgdG9wID0gaGVhZChyb3duYW1lcyh0b3BfZ2VuZXMpLDIwMCkgI3Rha2UgdG9wIHRvcF9nZW5lc19udW0KICBleHByID0geGVub19leHByZXNzaW9uWyxjb2xuYW1lcyh4ZW5vX2V4cHJlc3Npb24pICVpbiUgdG9wXQogIGV4cHJfY29yID0gY29yKGV4cHIpCgogIHBodDEgPSBwaGVhdG1hcChleHByX2NvcixzaG93X2NvbG5hbWVzID0gRixzaG93X3Jvd25hbWVzID0gRiwgc2lsZW50ID0gVCkKICAgICAgCiAgCiAgbnVtX29mX2NsdXN0ZXJzID0gNApjbHVzdGVyaW5nX2Rpc3RhbmNlID0gImV1Y2xpZGVhbiIKbXlhbm5vdGF0aW9uID0gYXMuZGF0YS5mcmFtZShjdXRyZWUocGh0MVtbInRyZWVfcm93Il1dLCBrID0gbnVtX29mX2NsdXN0ZXJzKSkgI3NwbGl0IGludG8gayBjbHVzdGVycwogCm5hbWVzKG15YW5ub3RhdGlvbilbMV0gPSAiY2x1c3RlciIKICBteWFubm90YXRpb24kY2x1c3RlciA9IGFzLmZhY3RvcihteWFubm90YXRpb24kY2x1c3RlcikKICAKICBwYWxldHRlMSA8LWJyZXdlci5wYWwobnVtX29mX2NsdXN0ZXJzLCAiUGFpcmVkIikKCiAgbmFtZXMocGFsZXR0ZTEpID0gdW5pcXVlKG15YW5ub3RhdGlvbiRjbHVzdGVyKQogIGFubl9jb2xvcnMgPSBsaXN0IChjbHVzdGVyID0gcGFsZXR0ZTEpCiAgYW5ub3RhdGlvbiA9IGxpc3QoYW5uX2NvbG9ycyA9IGFubl9jb2xvcnMsIG15YW5ub3RhdGlvbiA9IG15YW5ub3RhdGlvbikKICAKICBjb2xvcnMgPC0gYyhzZXEoLTEsMSxieT0wLjAxKSkKICBteV9wYWxldHRlIDwtIGMoImJsdWUiLGNvbG9yUmFtcFBhbGV0dGUoY29sb3JzID0gYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKG4gPSBsZW5ndGgoY29sb3JzKS0zKSwgInJlZCIpCgoKICBwcmludF90YWIocGx0ID0gCiAgICAgICAgICAgICAgICBwaGVhdG1hcChtYXQgPSBleHByX2Nvcixhbm5vdGF0aW9uX2NvbCA9ICBhbm5vdGF0aW9uW1sibXlhbm5vdGF0aW9uIl1dLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm90YXRpb25bWyJhbm5fY29sb3JzIl1dLCBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSBjbHVzdGVyaW5nX2Rpc3RhbmNlLGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9IGNsdXN0ZXJpbmdfZGlzdGFuY2UsY29sb3IgPSBteV9wYWxldHRlLGJyZWFrcyA9IGNvbG9ycyxzaG93X3Jvd25hbWVzID0gRixzaG93X2NvbG5hbWVzID0gRikKICAgICAgICAgICAgLHRpdGxlID0gIkNlbGxfY3ljbGUiKQogIApgYGAKCmBgYHtyfQp0b3BfZ2VuZXMgPSBnZXBfc2NvcmVzICAlPiUgIGFycmFuZ2UoZGVzYyhnZXBfc2NvcmVzWyJIeXBveGlhIl0pKSAjc29ydCBieSBzY29yZSBhCmh5cG94aWFfZ2VuZXMgPSBoZWFkKHJvd25hbWVzKHRvcF9nZW5lcyksMjApICN0YWtlIHRvcCB0b3BfZ2VuZXNfbnVtCmludGVyc2VjdChjbHVzdGVyXzNfZ2VuZXMsaHlwb3hpYV9nZW5lcykKYGBgCgpgYGB7cn0KbGlicmFyeShnZ3Zlbm4pCmFsbCA9IGxpc3QoaHlwb3hpYV9nZW5lcyA9IGh5cG94aWFfZ2VuZXMsIGhpZl90YXJnZXRzID0gY2x1c3Rlcl8zX2dlbmVzKQpnZ3Zlbm4oCiAgYWxsCikKYGBgCgojIEhJRl90YXJnZXRzLSBIeXBveGlhIGNvcnJlbGF0aW9uICB7LnRhYnNldH0KYGBge3IgZWNobz1UUlVFLCByZXN1bHRzPSdhc2lzJ30KaGlmX3RhcmdldHNfYnlfdHAgPSBGZXRjaERhdGEob2JqZWN0ID0geGVubyx2YXJzID0gYyhjbHVzdGVyXzNfZ2VuZXMpKSAlPiUgcm93U3VtcygpICU+JSBhcy5kYXRhLmZyYW1lKCkgI21lYW4gZXhwcmVzc2lvbgpoaWZfdGFyZ2V0c19ieV90cFssMl0gPSB4ZW5vJEh5cG94aWEKbmFtZXMoaGlmX3RhcmdldHNfYnlfdHApID0gYygiaGlmX3RhcmdldHMiLCJoeXBveGlhX3Byb2dyYW0iKQoKcCA9IGdncGxvdChoaWZfdGFyZ2V0c19ieV90cCwgYWVzKHg9aGlmX3RhcmdldHMsIHk9aHlwb3hpYV9wcm9ncmFtKSkgKyAKICBnZW9tX3BvaW50KCkrCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtKQpwcmludF90YWIocGx0ID0gcCx0aXRsZSA9ICJwb2ludHMiKQoKcCA9IGdncGxvdChoaWZfdGFyZ2V0c19ieV90cCwgYWVzKHg9aGlmX3RhcmdldHMsIHk9aHlwb3hpYV9wcm9ncmFtKSkgKyAKICAjIGdlb21fcG9pbnQoKSsKICBnZW9tX3Ntb290aChtZXRob2Q9bG0pICsKICBzdGF0X2RlbnNpdHlfMmQoZ2VvbSA9ICJwb2ludCIsIGFlcyhzaXplID0gYWZ0ZXJfc3RhdChkZW5zaXR5KSksIG4gPSAyMCwgY29udG91ciA9IEZBTFNFKSsgc3RhdF9jb3IobWV0aG9kID0gInBlYXJzb24iLCBsYWJlbC54ID0gMjAsIGxhYmVsLnkgPSAxLjEpCnByaW50X3RhYihwbHQgPSBwLHRpdGxlID0gInBvaW50cyBkZW5zaXR5IikKCgpwID0gZ2dwbG90KGhpZl90YXJnZXRzX2J5X3RwLCBhZXMoeD1oaWZfdGFyZ2V0cywgeT1oeXBveGlhX3Byb2dyYW0pKSArIAogICAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtKSArCiAgICBnZW9tX3BvaW50KCkrCiAgZ2VvbV9kZW5zaXR5XzJkKGFlcyhjb2xvciA9IC4ubGV2ZWwuLikpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKQpwcmludF90YWIocGx0ID0gcCx0aXRsZSA9ICJwb2x5Z29uIikKCmBgYApgYGB7cn0KaGlmX3RhcmdldHNfc2NvcmUgPSBGZXRjaERhdGEob2JqZWN0ID0gbHVuZyx2YXJzID0gYyhjbHVzdGVyXzNfZ2VuZXMpKSAlPiUgcm93U3VtcygpICU+JSBhcy5kYXRhLmZyYW1lKCkgI21lYW4gZXhwcmVzc2lvbgpsdW5nID0gQWRkTWV0YURhdGEob2JqZWN0ID0gbHVuZyxtZXRhZGF0YSA9IGhpZl90YXJnZXRzX3Njb3JlLGNvbC5uYW1lID0gIkhJRl90YXJnZXRzX3Njb3JlIikKRmVhdHVyZVBsb3QobHVuZyxmZWF0dXJlcyA9ICJISUZfdGFyZ2V0c19zY29yZSIpCmBgYAoKPHNjcmlwdCBzcmM9Imh0dHBzOi8vaHlwb3RoZXMuaXMvZW1iZWQuanMiIGFzeW5jPjwvc2NyaXB0Pgo=