Data

xeno = readRDS("./Data/10x_xeno_1000.Rds")
by_expression = T #calculate metagenes by multiplie expression in genes coef, or by cnmf usage
suffix = "2Kvargenes"
suffix = r.suffix
import pickle
from cnmf import cNMF
f = open('./Data/cnmf/cnmf_objects/models_' + suffix + '_cnmf_obj.pckl', 'rb')
cnmf_obj = pickle.load(f)
f.close()

Functions

library(stringi)
library(reticulate)
source_from_github(repositoy = "DEG_functions",version = "0.2.24")
source_from_github(repositoy = "cNMF_functions",version = "0.3.63",script_name = "cnmf_function_Harmony.R")
selected_k = 3
suffix = paste(suffix,paste0(selected_k,"nmfK"),sep="_")
print(suffix)
[1] "2Kvargenes_3nmfK"
selected_k = int(r.selected_k)
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)
gep_scores= py$gep_scores

hypoxia_genes = msigdbr(species = "Homo sapiens", category = "H") %>% filter(grepl('HALLMARK_HYPOXIA', gs_name)) %>% pull("gene_symbol") 
tnfa_genes = msigdbr(species = "Homo sapiens", category = "H") %>% filter(grepl('HALLMARK_TNFA_SIGNALING_VIA_NFKB', gs_name)) %>% pull("gene_symbol") 

cell_cycle_genes = msigdbr(species = "Homo sapiens", category = "H") %>% filter(grepl('HALLMARK_G2M_CHECKPOINT', gs_name)) %>% pull("gene_symbol") 

hallmark_genes = list(hypoxia_genes,tnfa_genes,cell_cycle_genes)
if (by_expression){
  message('By expression')
  
  no_neg <- function(x) {
  x = x + abs(min(x))
  x
  }
  gep_scores_norm= gep_scores
gep_scores_norm = apply(gep_scores_norm, MARGIN = 2, FUN = no_neg)%>% as.data.frame()
gep_scores_norm = sum2one(gep_scores_norm)

# gep_scores_norm = apply(gep_scores, MARGIN = 2, FUN = min_max_normalize)%>% as.data.frame()
# gep_scores_norm = sum2one(gep_scores_norm)
  all_metagenes = expression_mult(gep_scores = gep_scores_norm,dataset = xeno,top_genes = T,max_genes = F,z_score  = F,hallmark_genes = NULL)

}else{
    message('By usage matrix')

  all_metagenes= py$usage_norm}
By expression
gep_scores_norm = gep_scores
gep_scores_norm = apply(gep_scores_norm, MARGIN = 2, FUN = no_neg)%>% as.data.frame()
# 
# gep_scores_norm = apply(gep_scores, MARGIN = 2, FUN = min_max_normalize)%>% as.data.frame()
gep_scores_norm = sum2one(gep_scores_norm)
# gep_scores_norm = scale(gep_scores_norm)

all_metagenes = expression_inversion(gep_scores = gep_scores_norm %>% as.matrix(),dataset = xeno) %>% t() %>% as.data.frame()
# gep_scores_norm = apply(gep_scores, MARGIN = 2, FUN = no_neg)%>% as.data.frame()

all_metagenes = apply(all_metagenes, MARGIN = 2, FUN = min_max_normalize)%>% as.data.frame()
# all_metagenes = sum2one(all_metagenes)
#Make metagene names
for (i in 1:ncol(all_metagenes)) {
  colnames(all_metagenes)[i] = "metagene." %>% paste0(i)
}

#add each metagene to metadata
for (i in 1:ncol(all_metagenes)) {
  metage_metadata = all_metagenes %>% dplyr::select(i)
  xeno = AddMetaData(object = xeno,metadata = metage_metadata)
}

FeaturePlot(object = xeno,features = colnames(all_metagenes))

Enrichment analysis by top 200 genes of each program

hif_targets_set = data.frame(gs_name = "hif_targets",gene_symbol = hif_targets)
gep_scores = py$gep_scores
plt_list = list()
for (i in 1:ncol(gep_scores)) {
  top_genes = gep_scores  %>%  arrange(desc(gep_scores[i])) #sort by score a
  top = head(rownames(top_genes),200) #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)

Enrichment analysis by selecting genes using “max” method

Enriched in time point

larger_by = 1.5
xeno = program_assignment(dataset = xeno,larger_by = larger_by,program_names = colnames(all_metagenes))

Percentages

p = cell_percentage(dataset = xeno,time.point_var = "treatment",by_program = T)
print_tab(plt = p,title = "by program")

by program

p = cell_percentage(dataset = xeno,time.point_var = "treatment",by_tp = T)
print_tab(plt = p,title = "by timepoint")

by timepoint

NA

xeno = SetIdent(object = xeno,value = "program.assignment")
xeno  = RenameIdents(object = xeno,"metagene.1" = "Hypoxia")
xeno  = RenameIdents(object = xeno,"metagene.2" = "TNFa")
xeno  = RenameIdents(object = xeno,"metagene.3" = "cell_cycle")

xeno$program.assignment = Idents(object = xeno)

UMAPS

# DimPlot(xeno,group.by = "program.assignment",cols = c("red","green","grey"))
print_tab(plt = 
            DimPlot(xeno,group.by = "program.assignment",cols = c(Hypoxia = "red",TNFa = "green",cell_cycle = "blue","NA" = "grey"))
          ,title = "orig.ident")

orig.ident

print_tab(plt = 
              DimPlot(xeno,group.by = "orig.ident")
          ,title = "program.assignment")

program.assignment

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

treatment

# DimPlot(xeno,group.by = "program.assignment",cols = c("red","darkgreen","blue","yellow","black","grey"))

Score regulation


i = 1
for (metegene in c("metagene.1","metagene.2")) {

  genes_by_tp = FetchData(object = xeno,vars = metegene) %>% rowSums() %>% as.data.frame() #mean expression
  names(genes_by_tp)[1] = "Metagene_mean"
  
  genes_by_tp = cbind(genes_by_tp,FetchData(object = xeno,vars = c("orig.ident","treatment"))) # add vars
  
  genes_by_tp_forPlot =  genes_by_tp %>% mutate(orig.ident = paste("model",orig.ident)) #add "model" before model num
  my_comparisons = list( c("NT", "OSI") )
  plt = ggplot(genes_by_tp_forPlot, aes(x=treatment, y=Metagene_mean)) +
    geom_boxplot() +
    scale_y_continuous(limits = c(0, max(genes_by_tp_forPlot[1])*1.1))+ #scale axis
    facet_wrap(~orig.ident, nrow = 2, strip.position = "top")+
    stat_compare_means(comparisons = my_comparisons,method = "wilcox.test") + # Add pairwise comparisons p-value
  ylab(paste(metegene,"mean"))

  
  print_tab(plt = plt,title = c(metegene,"per patient"))

  plt = ggplot(genes_by_tp_forPlot, aes(x=treatment, y=Metagene_mean)) +
    geom_boxplot() +
    scale_y_continuous(limits = c(0, max(genes_by_tp_forPlot[1])*1.1))+ #scale axis
    stat_compare_means(comparisons = my_comparisons,method = "wilcox.test") + # Add pairwise comparisons p-value
  ylab(paste(metegene,"mean"))
  
  print_tab(plt = plt,title = metegene)
    i = i+1
}

metagene.1 per patient

metagene.1

metagene.2 per patient

metagene.2

NA

LS0tCnRpdGxlOiAiWGVub19jbm1mX2FuYWx5c2lzIgphdXRob3I6ICJBdmlzaGFpIFdpemVsIgpkYXRlOiAnYHIgU3lzLnRpbWUoKWAnCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKLS0tCiMjIERhdGEKYGBge3J9Cnhlbm8gPSByZWFkUkRTKCIuL0RhdGEvMTB4X3hlbm9fMTAwMC5SZHMiKQpgYGAKCgoKYGBge3J9CmJ5X2V4cHJlc3Npb24gPSBUICNjYWxjdWxhdGUgbWV0YWdlbmVzIGJ5IG11bHRpcGxpZSBleHByZXNzaW9uIGluIGdlbmVzIGNvZWYsIG9yIGJ5IGNubWYgdXNhZ2UKc3VmZml4ID0gIjJLdmFyZ2VuZXMiCmBgYAoKYGBge3B5dGhvbn0Kc3VmZml4ID0gci5zdWZmaXgKaW1wb3J0IHBpY2tsZQpmcm9tIGNubWYgaW1wb3J0IGNOTUYKZiA9IG9wZW4oJy4vRGF0YS9jbm1mL2NubWZfb2JqZWN0cy9tb2RlbHNfJyArIHN1ZmZpeCArICdfY25tZl9vYmoucGNrbCcsICdyYicpCmNubWZfb2JqID0gcGlja2xlLmxvYWQoZikKZi5jbG9zZSgpCmBgYAoKIyMgRnVuY3Rpb25zCgpgYGB7cn0KbGlicmFyeShzdHJpbmdpKQpsaWJyYXJ5KHJldGljdWxhdGUpCnNvdXJjZV9mcm9tX2dpdGh1YihyZXBvc2l0b3kgPSAiREVHX2Z1bmN0aW9ucyIsdmVyc2lvbiA9ICIwLjIuMjQiKQpzb3VyY2VfZnJvbV9naXRodWIocmVwb3NpdG95ID0gImNOTUZfZnVuY3Rpb25zIix2ZXJzaW9uID0gIjAuMy42MyIsc2NyaXB0X25hbWUgPSAiY25tZl9mdW5jdGlvbl9IYXJtb255LlIiKQpgYGAKPCEtLSAjIyBLIHNlbGVjdGlvbiBwbG90IC0tPgo8IS0tICFbQ2FwdGlvbiBmb3IgdGhlIHBpY3R1cmUuXSgvc2NpL2xhYnMveW90YW1kL2xhYl9zaGFyZS9hdmlzaGFpLndpemVsL1JfcHJvamVjdHMvRUdGUi9EYXRhL2NubWYvY05NRl9wYXRpZW50c19WYXJub3JtX0hhcm1vbnlfMkt2YXJnZW5lcy9jTk1GX3BhdGllbnRzX1Zhcm5vcm1fSGFybW9ueV8yS3ZhcmdlbmVzLmtfc2VsZWN0aW9uLnBuZykgLS0+CgoKYGBge3J9CnNlbGVjdGVkX2sgPSAzCnN1ZmZpeCA9IHBhc3RlKHN1ZmZpeCxwYXN0ZTAoc2VsZWN0ZWRfaywibm1mSyIpLHNlcD0iXyIpCnByaW50KHN1ZmZpeCkKYGBgCgoKYGBge3B5dGhvbn0Kc2VsZWN0ZWRfayA9IGludChyLnNlbGVjdGVkX2spCmRlbnNpdHlfdGhyZXNob2xkID0gMC4xCiMgY25tZl9vYmouY29uc2Vuc3VzKGs9c2VsZWN0ZWRfaywgZGVuc2l0eV90aHJlc2hvbGQ9ZGVuc2l0eV90aHJlc2hvbGQpCnVzYWdlX25vcm0sIGdlcF9zY29yZXMsIGdlcF90cG0sIHRvcGdlbmVzID0gY25tZl9vYmoubG9hZF9yZXN1bHRzKEs9c2VsZWN0ZWRfaywgZGVuc2l0eV90aHJlc2hvbGQ9ZGVuc2l0eV90aHJlc2hvbGQpCmBgYAoKYGBge3J9CmdlcF9zY29yZXM9IHB5JGdlcF9zY29yZXMKYGBgCgpgYGB7cn0KCmh5cG94aWFfZ2VuZXMgPSBtc2lnZGJyKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwgY2F0ZWdvcnkgPSAiSCIpICU+JSBmaWx0ZXIoZ3JlcGwoJ0hBTExNQVJLX0hZUE9YSUEnLCBnc19uYW1lKSkgJT4lIHB1bGwoImdlbmVfc3ltYm9sIikgCnRuZmFfZ2VuZXMgPSBtc2lnZGJyKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwgY2F0ZWdvcnkgPSAiSCIpICU+JSBmaWx0ZXIoZ3JlcGwoJ0hBTExNQVJLX1RORkFfU0lHTkFMSU5HX1ZJQV9ORktCJywgZ3NfbmFtZSkpICU+JSBwdWxsKCJnZW5lX3N5bWJvbCIpIAoKY2VsbF9jeWNsZV9nZW5lcyA9IG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiLCBjYXRlZ29yeSA9ICJIIikgJT4lIGZpbHRlcihncmVwbCgnSEFMTE1BUktfRzJNX0NIRUNLUE9JTlQnLCBnc19uYW1lKSkgJT4lIHB1bGwoImdlbmVfc3ltYm9sIikgCgpoYWxsbWFya19nZW5lcyA9IGxpc3QoaHlwb3hpYV9nZW5lcyx0bmZhX2dlbmVzLGNlbGxfY3ljbGVfZ2VuZXMpCmlmIChieV9leHByZXNzaW9uKXsKICBtZXNzYWdlKCdCeSBleHByZXNzaW9uJykKICAKICBub19uZWcgPC0gZnVuY3Rpb24oeCkgewogIHggPSB4ICsgYWJzKG1pbih4KSkKICB4CiAgfQogIGdlcF9zY29yZXNfbm9ybT0gZ2VwX3Njb3JlcwpnZXBfc2NvcmVzX25vcm0gPSBhcHBseShnZXBfc2NvcmVzX25vcm0sIE1BUkdJTiA9IDIsIEZVTiA9IG5vX25lZyklPiUgYXMuZGF0YS5mcmFtZSgpCmdlcF9zY29yZXNfbm9ybSA9IHN1bTJvbmUoZ2VwX3Njb3Jlc19ub3JtKQoKIyBnZXBfc2NvcmVzX25vcm0gPSBhcHBseShnZXBfc2NvcmVzLCBNQVJHSU4gPSAyLCBGVU4gPSBtaW5fbWF4X25vcm1hbGl6ZSklPiUgYXMuZGF0YS5mcmFtZSgpCiMgZ2VwX3Njb3Jlc19ub3JtID0gc3VtMm9uZShnZXBfc2NvcmVzX25vcm0pCiAgYWxsX21ldGFnZW5lcyA9IGV4cHJlc3Npb25fbXVsdChnZXBfc2NvcmVzID0gZ2VwX3Njb3Jlc19ub3JtLGRhdGFzZXQgPSB4ZW5vLHRvcF9nZW5lcyA9IFQsbWF4X2dlbmVzID0gRix6X3Njb3JlICA9IEYsaGFsbG1hcmtfZ2VuZXMgPSBOVUxMKQoKfWVsc2V7CiAgICBtZXNzYWdlKCdCeSB1c2FnZSBtYXRyaXgnKQoKICBhbGxfbWV0YWdlbmVzPSBweSR1c2FnZV9ub3JtfQpgYGAKYGBge3J9CmdlcF9zY29yZXNfbm9ybSA9IGdlcF9zY29yZXMKZ2VwX3Njb3Jlc19ub3JtID0gYXBwbHkoZ2VwX3Njb3Jlc19ub3JtLCBNQVJHSU4gPSAyLCBGVU4gPSBub19uZWcpJT4lIGFzLmRhdGEuZnJhbWUoKQojIAojIGdlcF9zY29yZXNfbm9ybSA9IGFwcGx5KGdlcF9zY29yZXMsIE1BUkdJTiA9IDIsIEZVTiA9IG1pbl9tYXhfbm9ybWFsaXplKSU+JSBhcy5kYXRhLmZyYW1lKCkKZ2VwX3Njb3Jlc19ub3JtID0gc3VtMm9uZShnZXBfc2NvcmVzX25vcm0pCiMgZ2VwX3Njb3Jlc19ub3JtID0gc2NhbGUoZ2VwX3Njb3Jlc19ub3JtKQoKYWxsX21ldGFnZW5lcyA9IGV4cHJlc3Npb25faW52ZXJzaW9uKGdlcF9zY29yZXMgPSBnZXBfc2NvcmVzX25vcm0gJT4lIGFzLm1hdHJpeCgpLGRhdGFzZXQgPSB4ZW5vKSAlPiUgdCgpICU+JSBhcy5kYXRhLmZyYW1lKCkKIyBnZXBfc2NvcmVzX25vcm0gPSBhcHBseShnZXBfc2NvcmVzLCBNQVJHSU4gPSAyLCBGVU4gPSBub19uZWcpJT4lIGFzLmRhdGEuZnJhbWUoKQoKYWxsX21ldGFnZW5lcyA9IGFwcGx5KGFsbF9tZXRhZ2VuZXMsIE1BUkdJTiA9IDIsIEZVTiA9IG1pbl9tYXhfbm9ybWFsaXplKSU+JSBhcy5kYXRhLmZyYW1lKCkKIyBhbGxfbWV0YWdlbmVzID0gc3VtMm9uZShhbGxfbWV0YWdlbmVzKQoKYGBgCgoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQojTWFrZSBtZXRhZ2VuZSBuYW1lcwpmb3IgKGkgaW4gMTpuY29sKGFsbF9tZXRhZ2VuZXMpKSB7CiAgY29sbmFtZXMoYWxsX21ldGFnZW5lcylbaV0gPSAibWV0YWdlbmUuIiAlPiUgcGFzdGUwKGkpCn0KCiNhZGQgZWFjaCBtZXRhZ2VuZSB0byBtZXRhZGF0YQpmb3IgKGkgaW4gMTpuY29sKGFsbF9tZXRhZ2VuZXMpKSB7CiAgbWV0YWdlX21ldGFkYXRhID0gYWxsX21ldGFnZW5lcyAlPiUgZHBseXI6OnNlbGVjdChpKQogIHhlbm8gPSBBZGRNZXRhRGF0YShvYmplY3QgPSB4ZW5vLG1ldGFkYXRhID0gbWV0YWdlX21ldGFkYXRhKQp9CgpGZWF0dXJlUGxvdChvYmplY3QgPSB4ZW5vLGZlYXR1cmVzID0gY29sbmFtZXMoYWxsX21ldGFnZW5lcykpCgpgYGAKCgoKCiMjIEVucmljaG1lbnQgYW5hbHlzaXMgYnkgdG9wIDIwMCBnZW5lcyBvZiBlYWNoIHByb2dyYW0KCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTgsIHJlc3VsdHM9J2hpZGUnfQpoaWZfdGFyZ2V0c19zZXQgPSBkYXRhLmZyYW1lKGdzX25hbWUgPSAiaGlmX3RhcmdldHMiLGdlbmVfc3ltYm9sID0gaGlmX3RhcmdldHMpCmdlcF9zY29yZXMgPSBweSRnZXBfc2NvcmVzCnBsdF9saXN0ID0gbGlzdCgpCmZvciAoaSBpbiAxOm5jb2woZ2VwX3Njb3JlcykpIHsKICB0b3BfZ2VuZXMgPSBnZXBfc2NvcmVzICAlPiUgIGFycmFuZ2UoZGVzYyhnZXBfc2NvcmVzW2ldKSkgI3NvcnQgYnkgc2NvcmUgYQogIHRvcCA9IGhlYWQocm93bmFtZXModG9wX2dlbmVzKSwyMDApICN0YWtlIHRvcCB0b3BfZ2VuZXNfbnVtCiAgcmVzID0gZ2VuZXNfdmVjX2VucmljaG1lbnQoZ2VuZXMgPSB0b3AsYmFja2dyb3VuZCA9IHJvd25hbWVzKGdlcF9zY29yZXMpLGhvbWVyID0gVCx0aXRsZSA9IAogICAgICAgICAgICAgICAgICAgIGksc2lsZW50ID0gVCxyZXR1cm5fYWxsID0gVCxjdXN0b21fcGF0aHdheXMgID0gaGlmX3RhcmdldHNfc2V0KQogICAKICBwbHRfbGlzdFtbaV1dID0gcmVzJHBsdAp9CmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKGdyb2JzID0gcGx0X2xpc3QpCmBgYAoKIyMgRW5yaWNobWVudCBhbmFseXNpcyBieSBzZWxlY3RpbmcgZ2VuZXMgdXNpbmcgIm1heCIgbWV0aG9kCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTcsIHJlc3VsdHM9J2hpZGUnfQojIGdlcF9zY29yZXMgPSBweSRnZXBfc2NvcmVzCnBsdF9saXN0ID0gbGlzdCgpCgpsaWJyYXJ5KE5NRikKdG9wX2ZlYXR1cmVzID0gZXh0cmFjdEZlYXR1cmVzKG9iamVjdCA9IGdlcF9zY29yZXMgJT4lIGRhdGEubWF0cml4KCksbWV0aG9kID0ibWF4IikKZm9yIChpIGluIDE6bGVuZ3RoKHRvcF9mZWF0dXJlcykpIHsKICB0b3BfZmVhdHVyZXNbW2ldXT0gcm93bmFtZXMoZ2VwX3Njb3JlcylbdG9wX2ZlYXR1cmVzW1tpXV1dCn0KCmZvciAoaSBpbiAxOm5jb2woZ2VwX3Njb3JlcykpIHsKdG9wID0gdG9wX2ZlYXR1cmVzW2ldICU+JSB1bmxpc3QoKQogIHRyeSh7IAogcmVzID0gZ2VuZXNfdmVjX2VucmljaG1lbnQoZ2VuZXMgPSB0b3AsYmFja2dyb3VuZCA9IHJvd25hbWVzKGdlcF9zY29yZXMpLGhvbWVyID0gVCx0aXRsZSA9IAogICAgICAgICAgICAgICAgICAgIGksc2lsZW50ID0gVCxyZXR1cm5fYWxsID0gVCkKICAgICAgcGx0X2xpc3RbW2ldXSA9IHJlcyRwbHQKICAgIH0sIHNpbGVudD1UUlVFKQp9CnBsdF9saXN0ID0gRmlsdGVyKE5lZ2F0ZShpcy5udWxsKSwgcGx0X2xpc3QpICNyZW1vdmUgbnVsbCBwbG90cwoKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKGdyb2JzID0gcGx0X2xpc3QpCgpgYGAKCgoKCgojIyBFbnJpY2hlZCBpbiB0aW1lIHBvaW50CmBgYHtyfQpsYXJnZXJfYnkgPSAxLjUKeGVubyA9IHByb2dyYW1fYXNzaWdubWVudChkYXRhc2V0ID0geGVubyxsYXJnZXJfYnkgPSBsYXJnZXJfYnkscHJvZ3JhbV9uYW1lcyA9IGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpKQpgYGAgICAKCiMgUGVyY2VudGFnZXMgey50YWJzZXR9CgoKYGBge3IgZWNobz1UUlVFLCByZXN1bHRzPSdhc2lzJ30KcCA9IGNlbGxfcGVyY2VudGFnZShkYXRhc2V0ID0geGVubyx0aW1lLnBvaW50X3ZhciA9ICJ0cmVhdG1lbnQiLGJ5X3Byb2dyYW0gPSBUKQpwcmludF90YWIocGx0ID0gcCx0aXRsZSA9ICJieSBwcm9ncmFtIikKcCA9IGNlbGxfcGVyY2VudGFnZShkYXRhc2V0ID0geGVubyx0aW1lLnBvaW50X3ZhciA9ICJ0cmVhdG1lbnQiLGJ5X3RwID0gVCkKcHJpbnRfdGFiKHBsdCA9IHAsdGl0bGUgPSAiYnkgdGltZXBvaW50IikKCgpgYGAKCgpgYGB7cn0KeGVubyA9IFNldElkZW50KG9iamVjdCA9IHhlbm8sdmFsdWUgPSAicHJvZ3JhbS5hc3NpZ25tZW50IikKeGVubyAgPSBSZW5hbWVJZGVudHMob2JqZWN0ID0geGVubywibWV0YWdlbmUuMSIgPSAiSHlwb3hpYSIpCnhlbm8gID0gUmVuYW1lSWRlbnRzKG9iamVjdCA9IHhlbm8sIm1ldGFnZW5lLjIiID0gIlRORmEiKQp4ZW5vICA9IFJlbmFtZUlkZW50cyhvYmplY3QgPSB4ZW5vLCJtZXRhZ2VuZS4zIiA9ICJjZWxsX2N5Y2xlIikKCnhlbm8kcHJvZ3JhbS5hc3NpZ25tZW50ID0gSWRlbnRzKG9iamVjdCA9IHhlbm8pCmBgYAoKIyBVTUFQUyB7LnRhYnNldH0KYGBge3IgZWNobz1UUlVFLCByZXN1bHRzPSdhc2lzJ30KIyBEaW1QbG90KHhlbm8sZ3JvdXAuYnkgPSAicHJvZ3JhbS5hc3NpZ25tZW50Iixjb2xzID0gYygicmVkIiwiZ3JlZW4iLCJncmV5IikpCnByaW50X3RhYihwbHQgPSAKICAgICAgICAgICAgRGltUGxvdCh4ZW5vLGdyb3VwLmJ5ID0gInByb2dyYW0uYXNzaWdubWVudCIsY29scyA9IGMoSHlwb3hpYSA9ICJyZWQiLFRORmEgPSAiZ3JlZW4iLGNlbGxfY3ljbGUgPSAiYmx1ZSIsIk5BIiA9ICJncmV5IikpCiAgICAgICAgICAsdGl0bGUgPSAib3JpZy5pZGVudCIpCnByaW50X3RhYihwbHQgPSAKICAgICAgICAgICAgICBEaW1QbG90KHhlbm8sZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpCiAgICAgICAgICAsdGl0bGUgPSAicHJvZ3JhbS5hc3NpZ25tZW50IikKcHJpbnRfdGFiKHBsdCA9IAogICAgICAgICAgICBEaW1QbG90KHhlbm8sZ3JvdXAuYnkgPSAidHJlYXRtZW50IikKICAgICAgICAgICx0aXRsZSA9ICJ0cmVhdG1lbnQiKQoKIyBEaW1QbG90KHhlbm8sZ3JvdXAuYnkgPSAicHJvZ3JhbS5hc3NpZ25tZW50Iixjb2xzID0gYygicmVkIiwiZGFya2dyZWVuIiwiYmx1ZSIsInllbGxvdyIsImJsYWNrIiwiZ3JleSIpKQpgYGAKCiMgU2NvcmUgcmVndWxhdGlvbiB7LnRhYnNldH0KYGBge3IgZWNobz1UUlVFLCByZXN1bHRzPSdhc2lzJ30KCmkgPSAxCmZvciAobWV0ZWdlbmUgaW4gYygibWV0YWdlbmUuMSIsIm1ldGFnZW5lLjIiKSkgewoKICBnZW5lc19ieV90cCA9IEZldGNoRGF0YShvYmplY3QgPSB4ZW5vLHZhcnMgPSBtZXRlZ2VuZSkgJT4lIHJvd1N1bXMoKSAlPiUgYXMuZGF0YS5mcmFtZSgpICNtZWFuIGV4cHJlc3Npb24KICBuYW1lcyhnZW5lc19ieV90cClbMV0gPSAiTWV0YWdlbmVfbWVhbiIKICAKICBnZW5lc19ieV90cCA9IGNiaW5kKGdlbmVzX2J5X3RwLEZldGNoRGF0YShvYmplY3QgPSB4ZW5vLHZhcnMgPSBjKCJvcmlnLmlkZW50IiwidHJlYXRtZW50IikpKSAjIGFkZCB2YXJzCiAgCiAgZ2VuZXNfYnlfdHBfZm9yUGxvdCA9ICBnZW5lc19ieV90cCAlPiUgbXV0YXRlKG9yaWcuaWRlbnQgPSBwYXN0ZSgibW9kZWwiLG9yaWcuaWRlbnQpKSAjYWRkICJtb2RlbCIgYmVmb3JlIG1vZGVsIG51bQogIG15X2NvbXBhcmlzb25zID0gbGlzdCggYygiTlQiLCAiT1NJIikgKQogIHBsdCA9IGdncGxvdChnZW5lc19ieV90cF9mb3JQbG90LCBhZXMoeD10cmVhdG1lbnQsIHk9TWV0YWdlbmVfbWVhbikpICsKICAgIGdlb21fYm94cGxvdCgpICsKICAgIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIG1heChnZW5lc19ieV90cF9mb3JQbG90WzFdKSoxLjEpKSsgI3NjYWxlIGF4aXMKICAgIGZhY2V0X3dyYXAofm9yaWcuaWRlbnQsIG5yb3cgPSAyLCBzdHJpcC5wb3NpdGlvbiA9ICJ0b3AiKSsKICAgIHN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zLG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIpICsgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogIHlsYWIocGFzdGUobWV0ZWdlbmUsIm1lYW4iKSkKCiAgCiAgcHJpbnRfdGFiKHBsdCA9IHBsdCx0aXRsZSA9IGMobWV0ZWdlbmUsInBlciBwYXRpZW50IikpCgogIHBsdCA9IGdncGxvdChnZW5lc19ieV90cF9mb3JQbG90LCBhZXMoeD10cmVhdG1lbnQsIHk9TWV0YWdlbmVfbWVhbikpICsKICAgIGdlb21fYm94cGxvdCgpICsKICAgIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIG1heChnZW5lc19ieV90cF9mb3JQbG90WzFdKSoxLjEpKSsgI3NjYWxlIGF4aXMKICAgIHN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zLG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIpICsgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogIHlsYWIocGFzdGUobWV0ZWdlbmUsIm1lYW4iKSkKICAKICBwcmludF90YWIocGx0ID0gcGx0LHRpdGxlID0gbWV0ZWdlbmUpCiAgICBpID0gaSsxCn0KCmBgYAo=