Data

xeno = readRDS("./Data/10x_xeno_1000.Rds")
by_expression = F #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 = hallmark_genes)

}else{
    message('By usage matrix')

  all_metagenes= py$usage_norm}
By usage matrix

min max after calculating metagenes

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

LS0tCnRpdGxlOiAiWGVub19jbm1mX2FuYWx5c2lzIgphdXRob3I6ICJBdmlzaGFpIFdpemVsIgpkYXRlOiAnYHIgU3lzLnRpbWUoKWAnCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKLS0tCiMjIERhdGEKYGBge3J9Cnhlbm8gPSByZWFkUkRTKCIuL0RhdGEvMTB4X3hlbm9fMTAwMC5SZHMiKQpgYGAKCgoKYGBge3J9CmJ5X2V4cHJlc3Npb24gPSBGICNjYWxjdWxhdGUgbWV0YWdlbmVzIGJ5IG11bHRpcGxpZSBleHByZXNzaW9uIGluIGdlbmVzIGNvZWYsIG9yIGJ5IGNubWYgdXNhZ2UKc3VmZml4ID0gIjJLdmFyZ2VuZXMiCmBgYAoKYGBge3B5dGhvbn0Kc3VmZml4ID0gci5zdWZmaXgKaW1wb3J0IHBpY2tsZQpmcm9tIGNubWYgaW1wb3J0IGNOTUYKZiA9IG9wZW4oJy4vRGF0YS9jbm1mL2NubWZfb2JqZWN0cy9tb2RlbHNfJyArIHN1ZmZpeCArICdfY25tZl9vYmoucGNrbCcsICdyYicpCmNubWZfb2JqID0gcGlja2xlLmxvYWQoZikKZi5jbG9zZSgpCmBgYAoKIyMgRnVuY3Rpb25zCgpgYGB7cn0KbGlicmFyeShzdHJpbmdpKQpsaWJyYXJ5KHJldGljdWxhdGUpCnNvdXJjZV9mcm9tX2dpdGh1YihyZXBvc2l0b3kgPSAiREVHX2Z1bmN0aW9ucyIsdmVyc2lvbiA9ICIwLjIuMjQiKQpzb3VyY2VfZnJvbV9naXRodWIocmVwb3NpdG95ID0gImNOTUZfZnVuY3Rpb25zIix2ZXJzaW9uID0gIjAuMy42MyIsc2NyaXB0X25hbWUgPSAiY25tZl9mdW5jdGlvbl9IYXJtb255LlIiKQpgYGAKPCEtLSAjIyBLIHNlbGVjdGlvbiBwbG90IC0tPgo8IS0tICFbQ2FwdGlvbiBmb3IgdGhlIHBpY3R1cmUuXSgvc2NpL2xhYnMveW90YW1kL2xhYl9zaGFyZS9hdmlzaGFpLndpemVsL1JfcHJvamVjdHMvRUdGUi9EYXRhL2NubWYvY05NRl9wYXRpZW50c19WYXJub3JtX0hhcm1vbnlfMkt2YXJnZW5lcy9jTk1GX3BhdGllbnRzX1Zhcm5vcm1fSGFybW9ueV8yS3ZhcmdlbmVzLmtfc2VsZWN0aW9uLnBuZykgLS0+CgoKYGBge3J9CnNlbGVjdGVkX2sgPSAzCnN1ZmZpeCA9IHBhc3RlKHN1ZmZpeCxwYXN0ZTAoc2VsZWN0ZWRfaywibm1mSyIpLHNlcD0iXyIpCnByaW50KHN1ZmZpeCkKYGBgCgoKYGBge3B5dGhvbn0Kc2VsZWN0ZWRfayA9IGludChyLnNlbGVjdGVkX2spCmRlbnNpdHlfdGhyZXNob2xkID0gMC4xCiMgY25tZl9vYmouY29uc2Vuc3VzKGs9c2VsZWN0ZWRfaywgZGVuc2l0eV90aHJlc2hvbGQ9ZGVuc2l0eV90aHJlc2hvbGQpCnVzYWdlX25vcm0sIGdlcF9zY29yZXMsIGdlcF90cG0sIHRvcGdlbmVzID0gY25tZl9vYmoubG9hZF9yZXN1bHRzKEs9c2VsZWN0ZWRfaywgZGVuc2l0eV90aHJlc2hvbGQ9ZGVuc2l0eV90aHJlc2hvbGQpCmBgYAoKYGBge3J9CmdlcF9zY29yZXM9IHB5JGdlcF9zY29yZXMKYGBgCgpgYGB7cn0KCmh5cG94aWFfZ2VuZXMgPSBtc2lnZGJyKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwgY2F0ZWdvcnkgPSAiSCIpICU+JSBmaWx0ZXIoZ3JlcGwoJ0hBTExNQVJLX0hZUE9YSUEnLCBnc19uYW1lKSkgJT4lIHB1bGwoImdlbmVfc3ltYm9sIikgCnRuZmFfZ2VuZXMgPSBtc2lnZGJyKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwgY2F0ZWdvcnkgPSAiSCIpICU+JSBmaWx0ZXIoZ3JlcGwoJ0hBTExNQVJLX1RORkFfU0lHTkFMSU5HX1ZJQV9ORktCJywgZ3NfbmFtZSkpICU+JSBwdWxsKCJnZW5lX3N5bWJvbCIpIAoKY2VsbF9jeWNsZV9nZW5lcyA9IG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiLCBjYXRlZ29yeSA9ICJIIikgJT4lIGZpbHRlcihncmVwbCgnSEFMTE1BUktfRzJNX0NIRUNLUE9JTlQnLCBnc19uYW1lKSkgJT4lIHB1bGwoImdlbmVfc3ltYm9sIikgCgpoYWxsbWFya19nZW5lcyA9IGxpc3QoaHlwb3hpYV9nZW5lcyx0bmZhX2dlbmVzLGNlbGxfY3ljbGVfZ2VuZXMpCmlmIChieV9leHByZXNzaW9uKXsKICBtZXNzYWdlKCdCeSBleHByZXNzaW9uJykKICAKICBub19uZWcgPC0gZnVuY3Rpb24oeCkgewogIHggPSB4ICsgYWJzKG1pbih4KSkKICB4CiAgfQogIGdlcF9zY29yZXNfbm9ybT0gZ2VwX3Njb3JlcwpnZXBfc2NvcmVzX25vcm0gPSBhcHBseShnZXBfc2NvcmVzX25vcm0sIE1BUkdJTiA9IDIsIEZVTiA9IG5vX25lZyklPiUgYXMuZGF0YS5mcmFtZSgpCmdlcF9zY29yZXNfbm9ybSA9IHN1bTJvbmUoZ2VwX3Njb3Jlc19ub3JtKQoKIyBnZXBfc2NvcmVzX25vcm0gPSBhcHBseShnZXBfc2NvcmVzLCBNQVJHSU4gPSAyLCBGVU4gPSBtaW5fbWF4X25vcm1hbGl6ZSklPiUgYXMuZGF0YS5mcmFtZSgpCiMgZ2VwX3Njb3Jlc19ub3JtID0gc3VtMm9uZShnZXBfc2NvcmVzX25vcm0pCiAgYWxsX21ldGFnZW5lcyA9IGV4cHJlc3Npb25fbXVsdChnZXBfc2NvcmVzID0gZ2VwX3Njb3Jlc19ub3JtLGRhdGFzZXQgPSB4ZW5vLHRvcF9nZW5lcyA9IFQsbWF4X2dlbmVzID0gRix6X3Njb3JlICA9IEYsaGFsbG1hcmtfZ2VuZXMgPSBoYWxsbWFya19nZW5lcykKCn1lbHNlewogICAgbWVzc2FnZSgnQnkgdXNhZ2UgbWF0cml4JykKCiAgYWxsX21ldGFnZW5lcz0gcHkkdXNhZ2Vfbm9ybX0KYGBgCm1pbiBtYXggYWZ0ZXIgY2FsY3VsYXRpbmcgbWV0YWdlbmVzCmBgYHtyfQpnZXBfc2NvcmVzX25vcm0gPSBnZXBfc2NvcmVzCmdlcF9zY29yZXNfbm9ybSA9IGFwcGx5KGdlcF9zY29yZXNfbm9ybSwgTUFSR0lOID0gMiwgRlVOID0gbm9fbmVnKSU+JSBhcy5kYXRhLmZyYW1lKCkKIyAKIyBnZXBfc2NvcmVzX25vcm0gPSBhcHBseShnZXBfc2NvcmVzLCBNQVJHSU4gPSAyLCBGVU4gPSBtaW5fbWF4X25vcm1hbGl6ZSklPiUgYXMuZGF0YS5mcmFtZSgpCmdlcF9zY29yZXNfbm9ybSA9IHN1bTJvbmUoZ2VwX3Njb3Jlc19ub3JtKQojIGdlcF9zY29yZXNfbm9ybSA9IHNjYWxlKGdlcF9zY29yZXNfbm9ybSkKCmFsbF9tZXRhZ2VuZXMgPSBleHByZXNzaW9uX2ludmVyc2lvbihnZXBfc2NvcmVzID0gZ2VwX3Njb3Jlc19ub3JtICU+JSBhcy5tYXRyaXgoKSxkYXRhc2V0ID0geGVubykgJT4lIHQoKSAlPiUgYXMuZGF0YS5mcmFtZSgpCiMgZ2VwX3Njb3Jlc19ub3JtID0gYXBwbHkoZ2VwX3Njb3JlcywgTUFSR0lOID0gMiwgRlVOID0gbm9fbmVnKSU+JSBhcy5kYXRhLmZyYW1lKCkKCmFsbF9tZXRhZ2VuZXMgPSBhcHBseShhbGxfbWV0YWdlbmVzLCBNQVJHSU4gPSAyLCBGVU4gPSBtaW5fbWF4X25vcm1hbGl6ZSklPiUgYXMuZGF0YS5mcmFtZSgpCiMgYWxsX21ldGFnZW5lcyA9IHN1bTJvbmUoYWxsX21ldGFnZW5lcykKCmBgYAoKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KI01ha2UgbWV0YWdlbmUgbmFtZXMKZm9yIChpIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpW2ldID0gIm1ldGFnZW5lLiIgJT4lIHBhc3RlMChpKQp9CgojYWRkIGVhY2ggbWV0YWdlbmUgdG8gbWV0YWRhdGEKZm9yIChpIGluIDE6bmNvbChhbGxfbWV0YWdlbmVzKSkgewogIG1ldGFnZV9tZXRhZGF0YSA9IGFsbF9tZXRhZ2VuZXMgJT4lIGRwbHlyOjpzZWxlY3QoaSkKICB4ZW5vID0gQWRkTWV0YURhdGEob2JqZWN0ID0geGVubyxtZXRhZGF0YSA9IG1ldGFnZV9tZXRhZGF0YSkKfQoKRmVhdHVyZVBsb3Qob2JqZWN0ID0geGVubyxmZWF0dXJlcyA9IGNvbG5hbWVzKGFsbF9tZXRhZ2VuZXMpKQoKYGBgCgoKCgojIyBFbnJpY2htZW50IGFuYWx5c2lzIGJ5IHRvcCAyMDAgZ2VuZXMgb2YgZWFjaCBwcm9ncmFtCgpgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04LCByZXN1bHRzPSdoaWRlJ30KaGlmX3RhcmdldHNfc2V0ID0gZGF0YS5mcmFtZShnc19uYW1lID0gImhpZl90YXJnZXRzIixnZW5lX3N5bWJvbCA9IGhpZl90YXJnZXRzKQpnZXBfc2NvcmVzID0gcHkkZ2VwX3Njb3JlcwpwbHRfbGlzdCA9IGxpc3QoKQpmb3IgKGkgaW4gMTpuY29sKGdlcF9zY29yZXMpKSB7CiAgdG9wX2dlbmVzID0gZ2VwX3Njb3JlcyAgJT4lICBhcnJhbmdlKGRlc2MoZ2VwX3Njb3Jlc1tpXSkpICNzb3J0IGJ5IHNjb3JlIGEKICB0b3AgPSBoZWFkKHJvd25hbWVzKHRvcF9nZW5lcyksMjAwKSAjdGFrZSB0b3AgdG9wX2dlbmVzX251bQogIHJlcyA9IGdlbmVzX3ZlY19lbnJpY2htZW50KGdlbmVzID0gdG9wLGJhY2tncm91bmQgPSByb3duYW1lcyhnZXBfc2NvcmVzKSxob21lciA9IFQsdGl0bGUgPSAKICAgICAgICAgICAgICAgICAgICBpLHNpbGVudCA9IFQscmV0dXJuX2FsbCA9IFQsY3VzdG9tX3BhdGh3YXlzICA9IGhpZl90YXJnZXRzX3NldCkKICAgCiAgcGx0X2xpc3RbW2ldXSA9IHJlcyRwbHQKfQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IHBsdF9saXN0KQpgYGAKCiMjIEVucmljaG1lbnQgYW5hbHlzaXMgYnkgc2VsZWN0aW5nIGdlbmVzIHVzaW5nICJtYXgiIG1ldGhvZApgYGB7ciBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD03LCByZXN1bHRzPSdoaWRlJ30KIyBnZXBfc2NvcmVzID0gcHkkZ2VwX3Njb3JlcwpwbHRfbGlzdCA9IGxpc3QoKQoKbGlicmFyeShOTUYpCnRvcF9mZWF0dXJlcyA9IGV4dHJhY3RGZWF0dXJlcyhvYmplY3QgPSBnZXBfc2NvcmVzICU+JSBkYXRhLm1hdHJpeCgpLG1ldGhvZCA9Im1heCIpCmZvciAoaSBpbiAxOmxlbmd0aCh0b3BfZmVhdHVyZXMpKSB7CiAgdG9wX2ZlYXR1cmVzW1tpXV09IHJvd25hbWVzKGdlcF9zY29yZXMpW3RvcF9mZWF0dXJlc1tbaV1dXQp9Cgpmb3IgKGkgaW4gMTpuY29sKGdlcF9zY29yZXMpKSB7CnRvcCA9IHRvcF9mZWF0dXJlc1tpXSAlPiUgdW5saXN0KCkKICB0cnkoeyAKIHJlcyA9IGdlbmVzX3ZlY19lbnJpY2htZW50KGdlbmVzID0gdG9wLGJhY2tncm91bmQgPSByb3duYW1lcyhnZXBfc2NvcmVzKSxob21lciA9IFQsdGl0bGUgPSAKICAgICAgICAgICAgICAgICAgICBpLHNpbGVudCA9IFQscmV0dXJuX2FsbCA9IFQpCiAgICAgIHBsdF9saXN0W1tpXV0gPSByZXMkcGx0CiAgICB9LCBzaWxlbnQ9VFJVRSkKfQpwbHRfbGlzdCA9IEZpbHRlcihOZWdhdGUoaXMubnVsbCksIHBsdF9saXN0KSAjcmVtb3ZlIG51bGwgcGxvdHMKCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IHBsdF9saXN0KQoKYGBgCgoKCgoKIyMgRW5yaWNoZWQgaW4gdGltZSBwb2ludApgYGB7cn0KbGFyZ2VyX2J5ID0gMS41Cnhlbm8gPSBwcm9ncmFtX2Fzc2lnbm1lbnQoZGF0YXNldCA9IHhlbm8sbGFyZ2VyX2J5ID0gbGFyZ2VyX2J5LHByb2dyYW1fbmFtZXMgPSBjb2xuYW1lcyhhbGxfbWV0YWdlbmVzKSkKYGBgICAgCgojIFBlcmNlbnRhZ2VzIHsudGFic2V0fQoKCmBgYHtyIGVjaG89VFJVRSwgcmVzdWx0cz0nYXNpcyd9CnAgPSBjZWxsX3BlcmNlbnRhZ2UoZGF0YXNldCA9IHhlbm8sdGltZS5wb2ludF92YXIgPSAidHJlYXRtZW50IixieV9wcm9ncmFtID0gVCkKcHJpbnRfdGFiKHBsdCA9IHAsdGl0bGUgPSAiYnkgcHJvZ3JhbSIpCnAgPSBjZWxsX3BlcmNlbnRhZ2UoZGF0YXNldCA9IHhlbm8sdGltZS5wb2ludF92YXIgPSAidHJlYXRtZW50IixieV90cCA9IFQpCnByaW50X3RhYihwbHQgPSBwLHRpdGxlID0gImJ5IHRpbWVwb2ludCIpCgoKYGBgCgoKYGBge3J9Cnhlbm8gPSBTZXRJZGVudChvYmplY3QgPSB4ZW5vLHZhbHVlID0gInByb2dyYW0uYXNzaWdubWVudCIpCnhlbm8gID0gUmVuYW1lSWRlbnRzKG9iamVjdCA9IHhlbm8sIm1ldGFnZW5lLjEiID0gIkh5cG94aWEiKQp4ZW5vICA9IFJlbmFtZUlkZW50cyhvYmplY3QgPSB4ZW5vLCJtZXRhZ2VuZS4yIiA9ICJUTkZhIikKeGVubyAgPSBSZW5hbWVJZGVudHMob2JqZWN0ID0geGVubywibWV0YWdlbmUuMyIgPSAiY2VsbF9jeWNsZSIpCgp4ZW5vJHByb2dyYW0uYXNzaWdubWVudCA9IElkZW50cyhvYmplY3QgPSB4ZW5vKQpgYGAKCiMgVU1BUFMgey50YWJzZXR9CmBgYHtyIGVjaG89VFJVRSwgcmVzdWx0cz0nYXNpcyd9CiMgRGltUGxvdCh4ZW5vLGdyb3VwLmJ5ID0gInByb2dyYW0uYXNzaWdubWVudCIsY29scyA9IGMoInJlZCIsImdyZWVuIiwiZ3JleSIpKQpwcmludF90YWIocGx0ID0gCiAgICAgICAgICAgIERpbVBsb3QoeGVubyxncm91cC5ieSA9ICJwcm9ncmFtLmFzc2lnbm1lbnQiLGNvbHMgPSBjKEh5cG94aWEgPSAicmVkIixUTkZhID0gImdyZWVuIixjZWxsX2N5Y2xlID0gImJsdWUiLCJOQSIgPSAiZ3JleSIpKQogICAgICAgICAgLHRpdGxlID0gIm9yaWcuaWRlbnQiKQpwcmludF90YWIocGx0ID0gCiAgICAgICAgICAgICAgRGltUGxvdCh4ZW5vLGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiKQogICAgICAgICAgLHRpdGxlID0gInByb2dyYW0uYXNzaWdubWVudCIpCnByaW50X3RhYihwbHQgPSAKICAgICAgICAgICAgRGltUGxvdCh4ZW5vLGdyb3VwLmJ5ID0gInRyZWF0bWVudCIpCiAgICAgICAgICAsdGl0bGUgPSAidHJlYXRtZW50IikKCiMgRGltUGxvdCh4ZW5vLGdyb3VwLmJ5ID0gInByb2dyYW0uYXNzaWdubWVudCIsY29scyA9IGMoInJlZCIsImRhcmtncmVlbiIsImJsdWUiLCJ5ZWxsb3ciLCJibGFjayIsImdyZXkiKSkKYGBgCgojIFNjb3JlIHJlZ3VsYXRpb24gey50YWJzZXR9CmBgYHtyIGVjaG89VFJVRSwgcmVzdWx0cz0nYXNpcyd9CgppID0gMQpmb3IgKG1ldGVnZW5lIGluIGMoIm1ldGFnZW5lLjEiLCJtZXRhZ2VuZS4yIikpIHsKCiAgZ2VuZXNfYnlfdHAgPSBGZXRjaERhdGEob2JqZWN0ID0geGVubyx2YXJzID0gbWV0ZWdlbmUpICU+JSByb3dTdW1zKCkgJT4lIGFzLmRhdGEuZnJhbWUoKSAjbWVhbiBleHByZXNzaW9uCiAgbmFtZXMoZ2VuZXNfYnlfdHApWzFdID0gIk1ldGFnZW5lX21lYW4iCiAgCiAgZ2VuZXNfYnlfdHAgPSBjYmluZChnZW5lc19ieV90cCxGZXRjaERhdGEob2JqZWN0ID0geGVubyx2YXJzID0gYygib3JpZy5pZGVudCIsInRyZWF0bWVudCIpKSkgIyBhZGQgdmFycwogIAogIGdlbmVzX2J5X3RwX2ZvclBsb3QgPSAgZ2VuZXNfYnlfdHAgJT4lIG11dGF0ZShvcmlnLmlkZW50ID0gcGFzdGUoIm1vZGVsIixvcmlnLmlkZW50KSkgI2FkZCAibW9kZWwiIGJlZm9yZSBtb2RlbCBudW0KICBteV9jb21wYXJpc29ucyA9IGxpc3QoIGMoIk5UIiwgIk9TSSIpICkKICBwbHQgPSBnZ3Bsb3QoZ2VuZXNfYnlfdHBfZm9yUGxvdCwgYWVzKHg9dHJlYXRtZW50LCB5PU1ldGFnZW5lX21lYW4pKSArCiAgICBnZW9tX2JveHBsb3QoKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCBtYXgoZ2VuZXNfYnlfdHBfZm9yUGxvdFsxXSkqMS4xKSkrICNzY2FsZSBheGlzCiAgICBmYWNldF93cmFwKH5vcmlnLmlkZW50LCBucm93ID0gMiwgc3RyaXAucG9zaXRpb24gPSAidG9wIikrCiAgICBzdGF0X2NvbXBhcmVfbWVhbnMoY29tcGFyaXNvbnMgPSBteV9jb21wYXJpc29ucyxtZXRob2QgPSAid2lsY294LnRlc3QiKSArICMgQWRkIHBhaXJ3aXNlIGNvbXBhcmlzb25zIHAtdmFsdWUKICB5bGFiKHBhc3RlKG1ldGVnZW5lLCJtZWFuIikpCgogIAogIHByaW50X3RhYihwbHQgPSBwbHQsdGl0bGUgPSBjKG1ldGVnZW5lLCJwZXIgcGF0aWVudCIpKQoKICBwbHQgPSBnZ3Bsb3QoZ2VuZXNfYnlfdHBfZm9yUGxvdCwgYWVzKHg9dHJlYXRtZW50LCB5PU1ldGFnZW5lX21lYW4pKSArCiAgICBnZW9tX2JveHBsb3QoKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCBtYXgoZ2VuZXNfYnlfdHBfZm9yUGxvdFsxXSkqMS4xKSkrICNzY2FsZSBheGlzCiAgICBzdGF0X2NvbXBhcmVfbWVhbnMoY29tcGFyaXNvbnMgPSBteV9jb21wYXJpc29ucyxtZXRob2QgPSAid2lsY294LnRlc3QiKSArICMgQWRkIHBhaXJ3aXNlIGNvbXBhcmlzb25zIHAtdmFsdWUKICB5bGFiKHBhc3RlKG1ldGVnZW5lLCJtZWFuIikpCiAgCiAgcHJpbnRfdGFiKHBsdCA9IHBsdCx0aXRsZSA9IG1ldGVnZW5lKQogICAgaSA9IGkrMQp9CgpgYGAK