1 Data

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)
suffix ="xeno_genes_0-5sigma_2-7theta_100iter_26_9"
from cnmf import cNMF
suffix = r.suffix
import pickle
f = open('./Data/cnmf/cnmf_objects/patients_' + suffix + '_cnmf_obj.pckl', 'rb')
cnmf_obj = pickle.load(f)
f.close()

2 Functions

library(stringi)
library(reticulate)
source_from_github(repositoy = "DEG_functions",version = "0.2.47")
source_from_github(repositoy = "cNMF_functions",version = "0.4.02",script_name = "cnmf_functions_V3.R")
source_from_github(repositoy = "sc_general_functions",version = "0.1.28",script_name = "functions.R")
genesets <- msigdb_download("Homo sapiens",category="H") %>% append( msigdb_download("Homo sapiens",category="C2",subcategory = "CP"))
genesets[["HIF_targets"]] = hif_targets

genesets_go <- msigdb_gsets("Homo sapiens", "C5", "GO:BP", clean=TRUE)
DimPlot(lung,group.by = "patient.ident",shuffle = T)+DimPlot(lung,group.by = "time.point",shuffle = T )

3 K selection plot

plot_path = paste0("/sci/labs/yotamd/lab_share/avishai.wizel/R_projects/EGFR/Data/cnmf/cNMF_patients_Varnorm_Harmony_",suffix,"/cNMF_patients_Varnorm_Harmony_",suffix,".k_selection.png")
knitr::include_graphics(plot_path)
cnmf_obj.consensus(k=3, density_threshold=0.1,show_clustering=True)
cnmf_obj.consensus(k=6, density_threshold=0.1,show_clustering=True)
cnmf_obj.consensus(k=7, density_threshold=0.1,show_clustering=True)
cnmf_obj.consensus(k=8, density_threshold=0.1,show_clustering=True)

4 gep scores for all NMF k’s

density_threshold = 0.1
usage_norm3, gep_scores3, gep_tpm3, topgenes = cnmf_obj.load_results(K=3, density_threshold=density_threshold)
usage_norm6, gep_scores6, gep_tpm6, topgenes = cnmf_obj.load_results(K=6, density_threshold=density_threshold)
usage_norm7, gep_scores7, gep_tpm7, topgenes = cnmf_obj.load_results(K=7, density_threshold=density_threshold)
usage_norm8, gep_scores8, gep_tpm8, topgenes = cnmf_obj.load_results(K=8, density_threshold=density_threshold)

5 gsea of each program

gep_scores3 = py$gep_scores3
gep_scores6 = py$gep_scores6
gep_scores7 = py$gep_scores7
gep_scores8 = py$gep_scores8

gep_tpm8 = py$gep_tpm8
top_genes = py$topgenes
  for (col in seq_along(gep_scores8)) {
     ranked_vec = gep_scores8[,col] %>% setNames(rownames(gep_scores8)) %>% sort(decreasing = TRUE) 
     hyp_obj <- hypeR_fgsea(ranked_vec, genesets,up_only = T)
     print_tab(hyp_dots(hyp_obj,title = paste("program",col))+ aes(size=nes),title = paste0("gep",col))
  }

gep1

gep2

gep3

gep4

gep5

Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj$genesets, : There were 1 pathways for which P-values were not calculated properly due to unbalanced (positive and negative) gene-level statistic values. For such pathways pval, padj, NES, log2err are set to NA. You can try to increase the value of the argument nPermSimple (for example set it nPermSimple = 10000) ## gep6 {.unnumbered }

gep7

gep8

NA

programs_main_pathways = list(gep1 = 1:2, gep2 = 1:3,gep3 = 1:4)

6 Calculate usage by counts before Harmony

# get expression with genes in cnmf input
genes = rownames(gep_scores8)
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(verbose = F)
        used   (Mb) gc trigger    (Mb)   max used    (Mb)

Ncells 16160973 863.1 23764145 1269.2 23764145 1269.2 Vcells 835781914 6376.6 2506002394 19119.3 1899752628 14494.0


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=8,sumTo1=True)
/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.
usage_by_calc = py$usage_by_calc
colnames(usage_by_calc) = c("autoimmune","TNFa.NFkB", "hypoxia","unknown2", "cell_cycle1", "cell_cycle2","INFg","unknown")
# colnames(usage_by_calc) = paste0("gep",1:8)
#add each metagene to metadata
for (i  in 1:ncol(usage_by_calc )) {
  metagene_metadata = usage_by_calc [,i,drop=F]
  lung = AddMetaData(object = lung,metadata = metagene_metadata,col.name = colnames(usage_by_calc)[i])
}

FeaturePlot(object = lung,features = colnames(usage_by_calc),ncol = 3)

7 Regulation

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",programs = colnames(usage_by_calc)[1:5],without_split = F)

autoimmune per patient

TNFa.NFkB per patient

hypoxia per patient

hypoxia2 per patient

cell_cycle1 per patient

NA

DotPlot.2(object = lung, features =  colnames(usage_by_calc)[1:5],group.by  = 'time.point',scale = T)+
  guides(size = guide_legend(title = "Cells expressing (%)"),color = guide_colorbar(title = "Average Score"))

all_pathways = list()
for (pathway in colnames(usage_by_calc)) {
  data = FetchData(object = lung,vars = c(pathway, "time.point", "patient.ident"))
  all_patients = list()
  for (patient in unique(lung$patient.ident)) {
    mean1 = data %>% filter(patient.ident == patient, time.point == "pre-treatment") %>% pull(1) %>% mean()
    mean2 = data %>% filter(patient.ident == patient, time.point == "on-treatment") %>% pull(1) %>% mean()
    fc =(mean1+1) / (mean2+1)
    all_patients[[patient]] = fc
  }
  all_pathways[[pathway]] = all_patients
}


mat = as.data.frame(lapply(all_pathways, unlist))
mat = log2(t(mat) %>% as.data.frame())
breaks <- c(seq(-1,1,by=0.05))
colors_for_plot <- colorRampPalette(colors = c("blue", "white", "red"))(n = length(breaks)-1); colors_for_plot[20:21] = "white"

pheatmap::pheatmap(mat,color = colors_for_plot,breaks = breaks,display_numbers = T,main = "log2(FC) pre/on")

ranked_vec = gep_scores8[,2] %>% setNames(rownames(gep_scores8)) %>% sort(decreasing = TRUE) 
hyp_obj <- hypeR_fgsea(ranked_vec, genesets)
le_genes_tnfa =  hyp_obj$data %>% filter(label == "HALLMARK_TNFA_SIGNALING_VIA_NFKB") %>% pull("le") %>% strsplit(",") %>% unlist()

ranked_vec = gep_scores8[,3] %>% setNames(rownames(gep_scores8)) %>% sort(decreasing = TRUE) 
hyp_obj <- hypeR_fgsea(ranked_vec, genesets)
le_genes_hif =  hyp_obj$data %>% filter(label == "HIF_targets") %>% pull("le") %>% strsplit(",") %>% unlist()

gene_list = list(autoimmune_genes = genesets$KEGG_AUTOIMMUNE_THYROID_DISEASE, TNFa_genes = genesets$HALLMARK_TNFA_SIGNALING_VIA_NFKB, hif_targets = hif_targets,E2F_genes = genesets$HALLMARK_E2F_TARGETS,gep2_top = gep_scores8  %>%  arrange(desc(gep_scores8[2])) %>% rownames() %>% head(50),TNFa_le = le_genes_tnfa,hif_le = le_genes_hif,autoimmune_le  = le_genes_autoimmune)
# lung = ScaleData(object = lung,features = unlist(gene_list))
for (i in seq_along(gene_list)) {
  genes = gene_list[[i]]
  name = names(gene_list)[i]
  scores = FetchData(object = lung,vars = genes,slot = "data")  %>%  2^. %>% magrittr::subtract(1) %>%  rowMeans() #use  TPM
    # scores = FetchData(object = lung,vars = genes,slot = "data")  %>% rowMeans() #use log TPM

  # scores = expression[,genes] %>% rowMeans() #use TPM 

  lung %<>% AddMetaData(metadata = scores,col.name = name)

}
Warning in FetchData.Seurat(object = lung, vars = genes, slot = "data") :
  The following requested variables were not found: HLA-DRB3, HLA-DRB4, IFNA10, IFNA14, IFNA16, IFNA17, IFNA4, IFNA6, IFNA7, IFNA8
Warning in FetchData.Seurat(object = lung, vars = genes, slot = "data") :
  The following requested variables were not found: CCN1
Warning in FetchData.Seurat(object = lung, vars = genes, slot = "data") :
  The following requested variables were not found: AC114803.1, BNIP3P1, OVOL1-AS1, BICDL2, LDHAP5, AL158201.1, NLRP3P1, GSX1, AL109946.1
Warning in FetchData.Seurat(object = lung, vars = genes, slot = "data") :
  The following requested variables were not found: CNOT9, H2AX, H2AZ1, JPT1, MRE11
cor(lung$TNFa.NFkB, lung$gep2_top)
[1] 0.7171286
cor(lung$TNFa.NFkB, lung$TNFa_le)
[1] 0.3031179
cor(lung$TNFa.NFkB, lung$TNFa_genes)
[1] 0.1956391
cat ("\n")
cor(lung$hypoxia, lung$hif_targets)
[1] 0.5026576
cor(lung$hypoxia, lung$hif_le)
[1] 0.4716419

all_pathways = list()
for (pathway in names(gene_list)) {
  data = FetchData(object = lung,vars = c(pathway, "time.point", "patient.ident"))
  all_patients = list()
  for (patient in unique(lung$patient.ident)) {
    mean1 = data %>% filter(patient.ident == patient, time.point == "pre-treatment") %>% pull(1) %>% mean()
    mean2 = data %>% filter(patient.ident == patient, time.point == "on-treatment") %>% pull(1) %>% mean()
    fc =(mean1+1) / (mean2+1)
    all_patients[[patient]] = fc
  }
  all_pathways[[pathway]] = all_patients
}


a = as.data.frame(lapply(all_pathways, unlist))
a = log2(t(a) %>% as.data.frame())
breaks <- c(seq(-1,1,by=0.05))
colors_for_plot <- colorRampPalette(colors = c("blue", "white", "red"))(n = length(breaks)-1); colors_for_plot[20:21] = "white"
pheatmap::pheatmap(a,color = colors_for_plot,breaks = breaks,display_numbers = T,main = "log2(FC) pre/on")

LS0tCnRpdGxlOiAnYHIgcnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLHJlcGxhY2VtZW50ID0gIiIpYCcgCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19jb2xsYXBzZTogeWVzCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvY19kZXB0aDogMQotLS0KCgojIERhdGEKCmBgYHtyfQpsdW5nID0gcmVhZFJEUygiLi9EYXRhL2x1bmdfY2FuY2VyY2VsbHNfd2l0aFRQX29ubHlQYXRpZW50cy5yZHMiKQpsdW5nX3BhdGllbnRzID0gbHVuZyRwYXRpZW50LmlkZW50ICU+JSB1bmlxdWUoKSAlPiUgYXMuY2hhcmFjdGVyKCkKbHVuZ19wYXRpZW50c19maWx0ZXJlZCA9IGx1bmdfcGF0aWVudHNbIShsdW5nX3BhdGllbnRzICVpbiUgYygiWDEwNTVuZXciLCJYMTA5OSIpKV0gIyByZW1vdmUgcGF0aWVudHMgd2l0aCBsZXNzIHRoYW4gMTAwIG1hbGlnbmFudCBjZWxscwpsdW5nID0gc3Vic2V0KHggPSBsdW5nLHN1YnNldCA9IHBhdGllbnQuaWRlbnQgJWluJSBsdW5nX3BhdGllbnRzX2ZpbHRlcmVkKQpgYGAKCgpgYGB7cn0Kc3VmZml4ID0ieGVub19nZW5lc18wLTVzaWdtYV8yLTd0aGV0YV8xMDBpdGVyXzI2XzkiCmBgYAoKCmBgYHtweXRob259CmZyb20gY25tZiBpbXBvcnQgY05NRgpzdWZmaXggPSByLnN1ZmZpeAppbXBvcnQgcGlja2xlCmYgPSBvcGVuKCcuL0RhdGEvY25tZi9jbm1mX29iamVjdHMvcGF0aWVudHNfJyArIHN1ZmZpeCArICdfY25tZl9vYmoucGNrbCcsICdyYicpCmNubWZfb2JqID0gcGlja2xlLmxvYWQoZikKZi5jbG9zZSgpCmBgYAoKCgojIEZ1bmN0aW9ucwoKYGBge3J9CmxpYnJhcnkoc3RyaW5naSkKbGlicmFyeShyZXRpY3VsYXRlKQpzb3VyY2VfZnJvbV9naXRodWIocmVwb3NpdG95ID0gIkRFR19mdW5jdGlvbnMiLHZlcnNpb24gPSAiMC4yLjQ3IikKc291cmNlX2Zyb21fZ2l0aHViKHJlcG9zaXRveSA9ICJjTk1GX2Z1bmN0aW9ucyIsdmVyc2lvbiA9ICIwLjQuMDIiLHNjcmlwdF9uYW1lID0gImNubWZfZnVuY3Rpb25zX1YzLlIiKQpzb3VyY2VfZnJvbV9naXRodWIocmVwb3NpdG95ID0gInNjX2dlbmVyYWxfZnVuY3Rpb25zIix2ZXJzaW9uID0gIjAuMS4yOCIsc2NyaXB0X25hbWUgPSAiZnVuY3Rpb25zLlIiKQpgYGAKCmBgYHtyfQpnZW5lc2V0cyA8LSBtc2lnZGJfZG93bmxvYWQoIkhvbW8gc2FwaWVucyIsY2F0ZWdvcnk9IkgiKSAlPiUgYXBwZW5kKCBtc2lnZGJfZG93bmxvYWQoIkhvbW8gc2FwaWVucyIsY2F0ZWdvcnk9IkMyIixzdWJjYXRlZ29yeSA9ICJDUDpLRUdHIikpCmdlbmVzZXRzW1siSElGX3RhcmdldHMiXV0gPSBoaWZfdGFyZ2V0cwoKZ2VuZXNldHNfZ28gPC0gbXNpZ2RiX2dzZXRzKCJIb21vIHNhcGllbnMiLCAiQzUiLCAiR086QlAiLCBjbGVhbj1UUlVFKQoKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMn0KRGltUGxvdChsdW5nLGdyb3VwLmJ5ID0gInBhdGllbnQuaWRlbnQiLHNodWZmbGUgPSBUKStEaW1QbG90KGx1bmcsZ3JvdXAuYnkgPSAidGltZS5wb2ludCIsc2h1ZmZsZSA9IFQgKQpgYGAKCiMgSyBzZWxlY3Rpb24gcGxvdApgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00fQpwbG90X3BhdGggPSBwYXN0ZTAoIi9zY2kvbGFicy95b3RhbWQvbGFiX3NoYXJlL2F2aXNoYWkud2l6ZWwvUl9wcm9qZWN0cy9FR0ZSL0RhdGEvY25tZi9jTk1GX3BhdGllbnRzX1Zhcm5vcm1fSGFybW9ueV8iLHN1ZmZpeCwiL2NOTUZfcGF0aWVudHNfVmFybm9ybV9IYXJtb255XyIsc3VmZml4LCIua19zZWxlY3Rpb24ucG5nIikKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGxvdF9wYXRoKQpgYGAKCgpgYGB7cHl0aG9ufQpjbm1mX29iai5jb25zZW5zdXMoaz0zLCBkZW5zaXR5X3RocmVzaG9sZD0wLjEsc2hvd19jbHVzdGVyaW5nPVRydWUpCmNubWZfb2JqLmNvbnNlbnN1cyhrPTYsIGRlbnNpdHlfdGhyZXNob2xkPTAuMSxzaG93X2NsdXN0ZXJpbmc9VHJ1ZSkKY25tZl9vYmouY29uc2Vuc3VzKGs9NywgZGVuc2l0eV90aHJlc2hvbGQ9MC4xLHNob3dfY2x1c3RlcmluZz1UcnVlKQpjbm1mX29iai5jb25zZW5zdXMoaz04LCBkZW5zaXR5X3RocmVzaG9sZD0wLjEsc2hvd19jbHVzdGVyaW5nPVRydWUpCmBgYAoKCiMgZ2VwIHNjb3JlcyBmb3IgYWxsIE5NRiBrJ3MKYGBge3B5dGhvbn0KZGVuc2l0eV90aHJlc2hvbGQgPSAwLjEKdXNhZ2Vfbm9ybTMsIGdlcF9zY29yZXMzLCBnZXBfdHBtMywgdG9wZ2VuZXMgPSBjbm1mX29iai5sb2FkX3Jlc3VsdHMoSz0zLCBkZW5zaXR5X3RocmVzaG9sZD1kZW5zaXR5X3RocmVzaG9sZCkKdXNhZ2Vfbm9ybTYsIGdlcF9zY29yZXM2LCBnZXBfdHBtNiwgdG9wZ2VuZXMgPSBjbm1mX29iai5sb2FkX3Jlc3VsdHMoSz02LCBkZW5zaXR5X3RocmVzaG9sZD1kZW5zaXR5X3RocmVzaG9sZCkKdXNhZ2Vfbm9ybTcsIGdlcF9zY29yZXM3LCBnZXBfdHBtNywgdG9wZ2VuZXMgPSBjbm1mX29iai5sb2FkX3Jlc3VsdHMoSz03LCBkZW5zaXR5X3RocmVzaG9sZD1kZW5zaXR5X3RocmVzaG9sZCkKdXNhZ2Vfbm9ybTgsIGdlcF9zY29yZXM4LCBnZXBfdHBtOCwgdG9wZ2VuZXMgPSBjbm1mX29iai5sb2FkX3Jlc3VsdHMoSz04LCBkZW5zaXR5X3RocmVzaG9sZD1kZW5zaXR5X3RocmVzaG9sZCkKCmBgYAoKIyBnc2VhIG9mIGVhY2ggcHJvZ3JhbSB7LnRhYnNldH0KYGBge3IgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9OCwgcmVzdWx0cz0nYXNpcyd9CmdlcF9zY29yZXMzID0gcHkkZ2VwX3Njb3JlczMKZ2VwX3Njb3JlczYgPSBweSRnZXBfc2NvcmVzNgpnZXBfc2NvcmVzNyA9IHB5JGdlcF9zY29yZXM3CmdlcF9zY29yZXM4ID0gcHkkZ2VwX3Njb3JlczgKCmdlcF90cG04ID0gcHkkZ2VwX3RwbTgKdG9wX2dlbmVzID0gcHkkdG9wZ2VuZXMKYGBgCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KICBmb3IgKGNvbCBpbiBzZXFfYWxvbmcoZ2VwX3Njb3JlczgpKSB7CiAgICAgcmFua2VkX3ZlYyA9IGdlcF9zY29yZXM4Wyxjb2xdICU+JSBzZXROYW1lcyhyb3duYW1lcyhnZXBfc2NvcmVzOCkpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUUlVFKSAKICAgICBoeXBfb2JqIDwtIGh5cGVSX2Znc2VhKHJhbmtlZF92ZWMsIGdlbmVzZXRzLHVwX29ubHkgPSBUKQogICAgIHByaW50X3RhYihoeXBfZG90cyhoeXBfb2JqLHRpdGxlID0gcGFzdGUoInByb2dyYW0iLGNvbCkpKyBhZXMoc2l6ZT1uZXMpLHRpdGxlID0gcGFzdGUwKCJnZXAiLGNvbCkpCiAgfQpgYGAKCgoKYGBge3J9CnByb2dyYW1zX21haW5fcGF0aHdheXMgPSBsaXN0KGdlcDEgPSAxOjIsIGdlcDIgPSAxOjMsZ2VwMyA9IDE6NCkKYGBgCgojIENhbGN1bGF0ZSB1c2FnZSBieSBjb3VudHMgYmVmb3JlIEhhcm1vbnkKYGBge3IgZWNobz1UUlVFLCByZXN1bHRzPSdhc2lzJ30KIyBnZXQgZXhwcmVzc2lvbiB3aXRoIGdlbmVzIGluIGNubWYgaW5wdXQKZ2VuZXMgPSByb3duYW1lcyhnZXBfc2NvcmVzOCkKbHVuZ19leHByZXNzaW9uID0gdChhcy5tYXRyaXgoR2V0QXNzYXlEYXRhKGx1bmcsc2xvdD0nZGF0YScpKSkgCmx1bmdfZXhwcmVzc2lvbiA9IDIqKmx1bmdfZXhwcmVzc2lvbiAjY29udmVydCBmcm9tIGxvZzIodHBtKzEpIHRvIHRwbQpsdW5nX2V4cHJlc3Npb24gPSBsdW5nX2V4cHJlc3Npb24tMQpsdW5nX2V4cHJlc3Npb24gPSBsdW5nX2V4cHJlc3Npb25bLGdlbmVzXSAlPiUgYXMuZGF0YS5mcmFtZSgpCgphbGxfMF9nZW5lcyA9IGNvbG5hbWVzKGx1bmdfZXhwcmVzc2lvbilbY29sU3VtcyhsdW5nX2V4cHJlc3Npb249PTAsIG5hLnJtPVRSVUUpPT1ucm93KGx1bmdfZXhwcmVzc2lvbildICNkZWxldGUgcm93cyB0aGF0IGhhdmUgYWxsIDAKZ2VuZXMgPSBnZW5lc1shZ2VuZXMgJWluJSBhbGxfMF9nZW5lc10KbHVuZ19leHByZXNzaW9uID0gbHVuZ19leHByZXNzaW9uWywhY29sbmFtZXMobHVuZ19leHByZXNzaW9uKSAlaW4lIGFsbF8wX2dlbmVzXQpnYyh2ZXJib3NlID0gRikKYGBgCgoKYGBge3B5dGhvbn0KCmx1bmdfZXhwcmVzc2lvbiA9IHIubHVuZ19leHByZXNzaW9uCmdlbmVzID0gci5nZW5lcwp1c2FnZV9ieV9jYWxjID0gZ2V0X3VzYWdlX2Zyb21fc2NvcmUoY291bnRzPWx1bmdfZXhwcmVzc2lvbix0cG09bHVuZ19leHByZXNzaW9uLGdlbmVzPWdlbmVzLGNubWZfb2JqPWNubWZfb2JqLGs9OCxzdW1UbzE9VHJ1ZSkKCmBgYAoKYGBge3J9CnVzYWdlX2J5X2NhbGMgPSBweSR1c2FnZV9ieV9jYWxjCmBgYAoKYGBge3J9CmNvbG5hbWVzKHVzYWdlX2J5X2NhbGMpID0gYygiYXV0b2ltbXVuZSIsIlRORmEuTkZrQiIsICJoeXBveGlhIiwidW5rbm93bjIiLCAiY2VsbF9jeWNsZTEiLCAiY2VsbF9jeWNsZTIiLCJJTkZnIiwidW5rbm93biIpCmBgYAoKCmBgYHtyIGVjaG89VFJVRSwgZmlnLmhlaWdodD05LCBmaWcud2lkdGg9MTIsIHJlc3VsdHM9J2FzaXMnfQojIGNvbG5hbWVzKHVzYWdlX2J5X2NhbGMpID0gcGFzdGUwKCJnZXAiLDE6OCkKI2FkZCBlYWNoIG1ldGFnZW5lIHRvIG1ldGFkYXRhCmZvciAoaSAgaW4gMTpuY29sKHVzYWdlX2J5X2NhbGMgKSkgewogIG1ldGFnZW5lX21ldGFkYXRhID0gdXNhZ2VfYnlfY2FsYyBbLGksZHJvcD1GXQogIGx1bmcgPSBBZGRNZXRhRGF0YShvYmplY3QgPSBsdW5nLG1ldGFkYXRhID0gbWV0YWdlbmVfbWV0YWRhdGEsY29sLm5hbWUgPSBjb2xuYW1lcyh1c2FnZV9ieV9jYWxjKVtpXSkKfQoKRmVhdHVyZVBsb3Qob2JqZWN0ID0gbHVuZyxmZWF0dXJlcyA9IGNvbG5hbWVzKHVzYWdlX2J5X2NhbGMpLG5jb2wgPSAzKQoKYGBgCiMgUmVndWxhdGlvbiB7LnRhYnNldH0KCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQptZXRhZ2VuZXNfbWVhbl9jb21wYXJlKGRhdGFzZXQgPSBsdW5nLHRpbWUucG9pbnRfdmFyID0gInRpbWUucG9pbnQiLHByZWZpeCA9ICJwYXRpZW50IixwYXRpZW50LmlkZW50X3ZhciA9ICJwYXRpZW50LmlkZW50IixwcmVfb24gPSBjKCJwcmUtdHJlYXRtZW50Iiwib24tdHJlYXRtZW50IiksdGVzdCA9ICJ3aWxjb3gudGVzdCIscHJvZ3JhbXMgPSBjb2xuYW1lcyh1c2FnZV9ieV9jYWxjKVsxOjVdLHdpdGhvdXRfc3BsaXQgPSBGKQpgYGAKYGBge3IgZmlnLndpZHRoPTh9CkRvdFBsb3QuMihvYmplY3QgPSBsdW5nLCBmZWF0dXJlcyA9ICBjb2xuYW1lcyh1c2FnZV9ieV9jYWxjKVsxOjVdLGdyb3VwLmJ5ICA9ICd0aW1lLnBvaW50JyxzY2FsZSA9IFQpKwogIGd1aWRlcyhzaXplID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIkNlbGxzIGV4cHJlc3NpbmcgKCUpIiksY29sb3IgPSBndWlkZV9jb2xvcmJhcih0aXRsZSA9ICJBdmVyYWdlIFNjb3JlIikpCmBgYAoKCgpgYGB7cn0KYWxsX3BhdGh3YXlzID0gbGlzdCgpCmZvciAocGF0aHdheSBpbiBjb2xuYW1lcyh1c2FnZV9ieV9jYWxjKSkgewogIGRhdGEgPSBGZXRjaERhdGEob2JqZWN0ID0gbHVuZyx2YXJzID0gYyhwYXRod2F5LCAidGltZS5wb2ludCIsICJwYXRpZW50LmlkZW50IikpCiAgYWxsX3BhdGllbnRzID0gbGlzdCgpCiAgZm9yIChwYXRpZW50IGluIHVuaXF1ZShsdW5nJHBhdGllbnQuaWRlbnQpKSB7CiAgICBtZWFuMSA9IGRhdGEgJT4lIGZpbHRlcihwYXRpZW50LmlkZW50ID09IHBhdGllbnQsIHRpbWUucG9pbnQgPT0gInByZS10cmVhdG1lbnQiKSAlPiUgcHVsbCgxKSAlPiUgbWVhbigpCiAgICBtZWFuMiA9IGRhdGEgJT4lIGZpbHRlcihwYXRpZW50LmlkZW50ID09IHBhdGllbnQsIHRpbWUucG9pbnQgPT0gIm9uLXRyZWF0bWVudCIpICU+JSBwdWxsKDEpICU+JSBtZWFuKCkKICAgIGZjID0obWVhbjErMSkgLyAobWVhbjIrMSkKICAgIGFsbF9wYXRpZW50c1tbcGF0aWVudF1dID0gZmMKICB9CiAgYWxsX3BhdGh3YXlzW1twYXRod2F5XV0gPSBhbGxfcGF0aWVudHMKfQoKCm1hdCA9IGFzLmRhdGEuZnJhbWUobGFwcGx5KGFsbF9wYXRod2F5cywgdW5saXN0KSkKbWF0ID0gbG9nMih0KG1hdCkgJT4lIGFzLmRhdGEuZnJhbWUoKSkKYnJlYWtzIDwtIGMoc2VxKC0xLDEsYnk9MC4wNSkpCmNvbG9yc19mb3JfcGxvdCA8LSBjb2xvclJhbXBQYWxldHRlKGNvbG9ycyA9IGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpKG4gPSBsZW5ndGgoYnJlYWtzKS0xKTsgY29sb3JzX2Zvcl9wbG90WzIwOjIxXSA9ICJ3aGl0ZSIKCnBoZWF0bWFwOjpwaGVhdG1hcChtYXQsY29sb3IgPSBjb2xvcnNfZm9yX3Bsb3QsYnJlYWtzID0gYnJlYWtzLGRpc3BsYXlfbnVtYmVycyA9IFQsbWFpbiA9ICJsb2cyKEZDKSBwcmUvb24iKQoKYGBgCmBgYHtyfQpyYW5rZWRfdmVjID0gZ2VwX3Njb3JlczhbLDJdICU+JSBzZXROYW1lcyhyb3duYW1lcyhnZXBfc2NvcmVzOCkpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUUlVFKSAKaHlwX29iaiA8LSBoeXBlUl9mZ3NlYShyYW5rZWRfdmVjLCBnZW5lc2V0cykKbGVfZ2VuZXNfdG5mYSA9ICBoeXBfb2JqJGRhdGEgJT4lIGZpbHRlcihsYWJlbCA9PSAiSEFMTE1BUktfVE5GQV9TSUdOQUxJTkdfVklBX05GS0IiKSAlPiUgcHVsbCgibGUiKSAlPiUgc3Ryc3BsaXQoIiwiKSAlPiUgdW5saXN0KCkKCnJhbmtlZF92ZWMgPSBnZXBfc2NvcmVzOFssM10gJT4lIHNldE5hbWVzKHJvd25hbWVzKGdlcF9zY29yZXM4KSkgJT4lIHNvcnQoZGVjcmVhc2luZyA9IFRSVUUpIApoeXBfb2JqIDwtIGh5cGVSX2Znc2VhKHJhbmtlZF92ZWMsIGdlbmVzZXRzKQpsZV9nZW5lc19oaWYgPSAgaHlwX29iaiRkYXRhICU+JSBmaWx0ZXIobGFiZWwgPT0gIkhJRl90YXJnZXRzIikgJT4lIHB1bGwoImxlIikgJT4lIHN0cnNwbGl0KCIsIikgJT4lIHVubGlzdCgpCmBgYAoKYGBge3J9CgpnZW5lX2xpc3QgPSBsaXN0KGF1dG9pbW11bmVfZ2VuZXMgPSBnZW5lc2V0cyRLRUdHX0FVVE9JTU1VTkVfVEhZUk9JRF9ESVNFQVNFLCBUTkZhX2dlbmVzID0gZ2VuZXNldHMkSEFMTE1BUktfVE5GQV9TSUdOQUxJTkdfVklBX05GS0IsIGhpZl90YXJnZXRzID0gaGlmX3RhcmdldHMsRTJGX2dlbmVzID0gZ2VuZXNldHMkSEFMTE1BUktfRTJGX1RBUkdFVFMsZ2VwMl90b3AgPSBnZXBfc2NvcmVzOCAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3JlczhbMl0pKSAlPiUgcm93bmFtZXMoKSAlPiUgaGVhZCg1MCksVE5GYV9sZSA9IGxlX2dlbmVzX3RuZmEsaGlmX2xlID0gbGVfZ2VuZXNfaGlmLGF1dG9pbW11bmVfbGUgID0gbGVfZ2VuZXNfYXV0b2ltbXVuZSkKIyBsdW5nID0gU2NhbGVEYXRhKG9iamVjdCA9IGx1bmcsZmVhdHVyZXMgPSB1bmxpc3QoZ2VuZV9saXN0KSkKZm9yIChpIGluIHNlcV9hbG9uZyhnZW5lX2xpc3QpKSB7CiAgZ2VuZXMgPSBnZW5lX2xpc3RbW2ldXQogIG5hbWUgPSBuYW1lcyhnZW5lX2xpc3QpW2ldCiAgc2NvcmVzID0gRmV0Y2hEYXRhKG9iamVjdCA9IGx1bmcsdmFycyA9IGdlbmVzLHNsb3QgPSAiZGF0YSIpICAlPiUgIDJeLiAlPiUgbWFncml0dHI6OnN1YnRyYWN0KDEpICU+JSAgcm93TWVhbnMoKSAjdXNlICBUUE0KICAgICMgc2NvcmVzID0gRmV0Y2hEYXRhKG9iamVjdCA9IGx1bmcsdmFycyA9IGdlbmVzLHNsb3QgPSAiZGF0YSIpICAlPiUgcm93TWVhbnMoKSAjdXNlIGxvZyBUUE0KCiAgIyBzY29yZXMgPSBleHByZXNzaW9uWyxnZW5lc10gJT4lIHJvd01lYW5zKCkgI3VzZSBUUE0gCgogIGx1bmcgJTw+JSBBZGRNZXRhRGF0YShtZXRhZGF0YSA9IHNjb3Jlcyxjb2wubmFtZSA9IG5hbWUpCgp9CgoKCmBgYApgYGB7cn0KY29yKGx1bmckVE5GYS5ORmtCLCBsdW5nJGdlcDJfdG9wKQpjb3IobHVuZyRUTkZhLk5Ga0IsIGx1bmckVE5GYV9sZSkKY29yKGx1bmckVE5GYS5ORmtCLCBsdW5nJFRORmFfZ2VuZXMpCmNhdCAoIlxuIikKY29yKGx1bmckaHlwb3hpYSwgbHVuZyRoaWZfdGFyZ2V0cykKY29yKGx1bmckaHlwb3hpYSwgbHVuZyRoaWZfbGUpCmBgYAoKCmBgYHtyfQoKYWxsX3BhdGh3YXlzID0gbGlzdCgpCmZvciAocGF0aHdheSBpbiBuYW1lcyhnZW5lX2xpc3QpKSB7CiAgZGF0YSA9IEZldGNoRGF0YShvYmplY3QgPSBsdW5nLHZhcnMgPSBjKHBhdGh3YXksICJ0aW1lLnBvaW50IiwgInBhdGllbnQuaWRlbnQiKSkKICBhbGxfcGF0aWVudHMgPSBsaXN0KCkKICBmb3IgKHBhdGllbnQgaW4gdW5pcXVlKGx1bmckcGF0aWVudC5pZGVudCkpIHsKICAgIG1lYW4xID0gZGF0YSAlPiUgZmlsdGVyKHBhdGllbnQuaWRlbnQgPT0gcGF0aWVudCwgdGltZS5wb2ludCA9PSAicHJlLXRyZWF0bWVudCIpICU+JSBwdWxsKDEpICU+JSBtZWFuKCkKICAgIG1lYW4yID0gZGF0YSAlPiUgZmlsdGVyKHBhdGllbnQuaWRlbnQgPT0gcGF0aWVudCwgdGltZS5wb2ludCA9PSAib24tdHJlYXRtZW50IikgJT4lIHB1bGwoMSkgJT4lIG1lYW4oKQogICAgZmMgPShtZWFuMSsxKSAvIChtZWFuMisxKQogICAgYWxsX3BhdGllbnRzW1twYXRpZW50XV0gPSBmYwogIH0KICBhbGxfcGF0aHdheXNbW3BhdGh3YXldXSA9IGFsbF9wYXRpZW50cwp9CgoKYSA9IGFzLmRhdGEuZnJhbWUobGFwcGx5KGFsbF9wYXRod2F5cywgdW5saXN0KSkKYSA9IGxvZzIodChhKSAlPiUgYXMuZGF0YS5mcmFtZSgpKQpicmVha3MgPC0gYyhzZXEoLTEsMSxieT0wLjA1KSkKY29sb3JzX2Zvcl9wbG90IDwtIGNvbG9yUmFtcFBhbGV0dGUoY29sb3JzID0gYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkobiA9IGxlbmd0aChicmVha3MpLTEpOyBjb2xvcnNfZm9yX3Bsb3RbMjA6MjFdID0gIndoaXRlIgpwaGVhdG1hcDo6cGhlYXRtYXAoYSxjb2xvciA9IGNvbG9yc19mb3JfcGxvdCxicmVha3MgPSBicmVha3MsZGlzcGxheV9udW1iZXJzID0gVCxtYWluID0gImxvZzIoRkMpIHByZS9vbiIpCgpgYGAKCgo8c2NyaXB0IHNyYz0iaHR0cHM6Ly9oeXBvdGhlcy5pcy9lbWJlZC5qcyIgYXN5bmM+PC9zY3JpcHQ+CgoK