Functions
library(stringi)
library(reticulate)
source_from_github(repositoy = "DEG_functions",version = "0.2.47")
ℹ SHA-1 hash of file is f5bb1cd741d13bded83fe3b6fd43169e29731216
source_from_github(repositoy = "cNMF_functions",version = "0.4.04",script_name = "cnmf_functions_V3.R")
ℹ SHA-1 hash of file is a735d9bfb5bc55fcb203f83a7240b9713fc080d3
source_from_github(repositoy = "sc_general_functions",version = "0.1.28",script_name = "functions.R")
ℹ SHA-1 hash of file is 737683e4e0a82ffa22769bbd4a0842a271fe63fc
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)
xeno = readRDS("./Data/10x_xeno_1000.Rds")
genesets <- msigdb_download("Homo sapiens",category="H")
hif_targets = c("LDHA", "PPFIA4", "PFKFB4", "AK4", "ERO1A", "BNIP3L", "BNIP3", #https://pubmed.ncbi.nlm.nih.gov/36384128/
"PFKL", "ENO1", "MIR210HG", "TPI1", "HILPDA", "PGK1", "INSIG2",
"AK4P1", "AC114803.1", "ENO2", "FUT11", "EGLN3", "NDRG1", "BNIP3P1",
"WDR54", "OVOL1-AS1", "STC2", "DDX41", "C4orf47", "GYS1", "ANKRD37",
"BICDL2", "P4HA1", "PDK1", "OSMR", "PKM", "LDHAP5", "AL158201.1",
"PFKP", "MIR210", "NLRP3P1", "GSX1", "AL109946.1", "PGAM1", "SLC16A3",
"ISM2", "TCAF2", "ARID3A", "KDM3A", "JMJD6", "C4orf3")
genesets[["HIF_targets"]] = hif_targets
Patients log2(TPM)
scoresAndIndices_patients_tpm = list()
i=0
fun <- function(x,data) {
i<<-i+1
message(paste("pathway "),i)
all_patients = c()
for (patient in unique(lung$patient.ident)) {
patient_data = subset(lung,subset = patient.ident == patient)
res = getPathwayScores((2^patient_data@assays$RNA@data)-1, unlist(x))[["pathwayScores"]]
all_patients = c(all_patients,res)
}
scoresAndIndices_patients_tpm = append(scoresAndIndices_patients_tpm,all_patients)
}
scoresAndIndices_patients_tpm = lapply(genesets,data=lung, FUN = fun)
for (pathway_name in names(scoresAndIndices_patients_tpm)) {
lung %<>% AddMetaData(metadata = unlist(scoresAndIndices_patients_tpm[[pathway_name]]),col.name = pathway_name)
}
signf_plot_pre_vs_on<- function(dataset,programs,patient.ident_var,prefix,pre_on,test,time.point_var) {
final_df = data.frame()
for (metegene in programs) {
genes_by_tp = FetchData(object = dataset,vars = metegene) %>% rowSums() %>% as.data.frame() #mean expression
names(genes_by_tp)[1] = metegene
genes_by_tp = cbind(genes_by_tp,FetchData(object = dataset,vars = c(patient.ident_var,time.point_var))) # add id and time points
genes_by_tp_forPlot = genes_by_tp %>% mutate(!!ensym(patient.ident_var) := paste(prefix,genes_by_tp[,patient.ident_var])) #add "model" before each model/patient
fm <- as.formula(paste(metegene, "~", time.point_var)) #make formula to plot
#plot and split by patient:
stat.test = compare_means(formula = fm ,data = genes_by_tp_forPlot,method = test,group.by = patient.ident_var,p.adjust.method = "fdr")%>%
dplyr::filter(group1 == pre_on[1] & group2 == pre_on[2]) #filter for pre vs on treatment only
final_df = rbind(final_df,stat.test)
}
return(final_df)
}
final_df = signf_plot_pre_vs_on(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 = names(scoresAndIndices) )
final_df = reshape2::dcast(final_df, patient.ident ~.y.,value.var = "p.adj") %>% column_to_rownames("patient.ident")
sig_heatmap(all_patients_result = t(final_df) %>% as.data.frame(),title = "ad")
all_pathways = list()
for (hallmark in names(scoresAndIndices_patients_tpm)) {
data = FetchData(object = lung,vars = c(hallmark, "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 / mean2
all_patients[[patient]] = fc
}
all_pathways[[hallmark]] = all_patients
}
a = as.data.frame(lapply(all_pathways, unlist))
a = log2(t(a) %>% as.data.frame())
breaks <- c(seq(-1,1,by=0.1))
colors_for_plot <- colorRampPalette(colors = c("blue", "white", "red"))(n = length(breaks))
pheatmap(a,color = colors_for_plot,breaks = breaks,display_numbers = T)
Warning in grSoftVersion() :
unable to load shared object '/usr/local/lib/R/modules//R_X11.so':
libXt.so.6: cannot open shared object file: No such file or directory

Patients TPM
scoresAndIndices_patients_logtpm = list()
i=0
fun <- function(x,data) {
i<<-i+1
message(paste("pathway "),i)
all_patients = c()
for (patient in unique(lung$patient.ident)) {
patient_data = subset(lung,subset = patient.ident == patient)
res = getPathwayScores((2^patient_data@assays$RNA@data)-1, unlist(x))[["pathwayScores"]]
all_patients = c(all_patients,res)
}
scoresAndIndices_patients_logtpm = append(scoresAndIndices_patients_logtpm,all_patients)
}
scoresAndIndices_patients_logtpm = lapply(genesets,data=lung, FUN = fun)
for (pathway_name in names(scoresAndIndices_patients_logtpm)) {
lung %<>% AddMetaData(metadata = unlist(scoresAndIndices_patients_logtpm[[pathway_name]]),col.name = pathway_name)
}
all_pathways = list()
for (hallmark in names(scoresAndIndices_patients_tpm)) {
data = FetchData(object = lung,vars = c(hallmark, "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 / mean2
all_patients[[patient]] = fc
}
all_pathways[[hallmark]] = all_patients
}
a = as.data.frame(lapply(all_pathways, unlist))
a = log2(t(a) %>% as.data.frame())
breaks <- c(seq(-1,1,by=0.1))
colors_for_plot <- colorRampPalette(colors = c("blue", "white", "red"))(n = length(breaks))
pheatmap(a,color = colors_for_plot,breaks = breaks,display_numbers = T)

Xeno TPM
scoresAndIndices = list()
i=0
fun <- function(x,dataset) {
i<<-i+1
message(paste("pathway "),i)
all_patients = c()
for (patient in unique(dataset$orig.ident)) {
patient_data = subset(dataset,subset = orig.ident == patient)
res = getPathwayScores((2^patient_data@assays$RNA@data)-1, unlist(x))[["pathwayScores"]]
all_patients = c(all_patients,res)
}
scoresAndIndices = append(scoresAndIndices,all_patients)
}
scoresAndIndices = lapply(genesets,dataset=xeno, FUN = fun)
for (pathway_name in names(scoresAndIndices)) {
xeno %<>% AddMetaData(metadata = unlist(scoresAndIndices[[pathway_name]]),col.name = pathway_name)
}
all_pathways = list()
for (hallmark in names(scoresAndIndices)) {
data = FetchData(object = lung,vars = c(hallmark, "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 / mean2
all_patients[[patient]] = fc
}
all_pathways[[hallmark]] = all_patients
}
a = as.data.frame(lapply(all_pathways, unlist))
a = log2(t(a) %>% as.data.frame())
breaks <- c(seq(-1,1,by=0.1))
colors_for_plot <- colorRampPalette(colors = c("blue", "white", "red"))(n = length(breaks))
pheatmap(a,color = colors_for_plot,breaks = breaks,display_numbers = T)

Xeno log2(TPM)
scoresAndIndices = list()
i=0
fun <- function(x,dataset) {
i<<-i+1
message(paste("pathway "),i)
all_patients = c()
for (patient in unique(dataset$orig.ident)) {
patient_data = subset(dataset,subset = orig.ident == patient)
res = getPathwayScores((2^patient_data@assays$RNA@data)-1, unlist(x))[["pathwayScores"]]
all_patients = c(all_patients,res)
}
scoresAndIndices = append(scoresAndIndices,all_patients)
}
scoresAndIndices = lapply(genesets[1:2],dataset=xeno, FUN = fun)
for (pathway_name in names(scoresAndIndices)) {
xeno %<>% AddMetaData(metadata = unlist(scoresAndIndices[[pathway_name]]),col.name = pathway_name)
}
all_pathways = list()
for (hallmark in names(scoresAndIndices)) {
data = FetchData(object = lung,vars = c(hallmark, "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 / mean2
all_patients[[patient]] = fc
}
all_pathways[[hallmark]] = all_patients
}
a = as.data.frame(lapply(all_pathways, unlist))
a = log2(t(a) %>% as.data.frame())
breaks <- c(seq(-1,1,by=0.1))
colors_for_plot <- colorRampPalette(colors = c("blue", "white", "red"))(n = length(breaks))
pheatmap(a,color = colors_for_plot,breaks = breaks,display_numbers = T)
LS0tCnRpdGxlOiAnYHIgcnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLHJlcGxhY2VtZW50ID0gIiIpYCcgCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19jb2xsYXBzZTogeWVzCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvY19kZXB0aDogMQotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Ci5tYWluLWNvbnRhaW5lciB7CiAgbWF4LXdpZHRoOiA4NSUgIWltcG9ydGFudDsKICBtYXJnaW46IGF1dG87Cn0KPC9zdHlsZT4KCgojIEZ1bmN0aW9ucwoKYGBge3J9CmxpYnJhcnkoc3RyaW5naSkKbGlicmFyeShyZXRpY3VsYXRlKQpzb3VyY2VfZnJvbV9naXRodWIocmVwb3NpdG95ID0gIkRFR19mdW5jdGlvbnMiLHZlcnNpb24gPSAiMC4yLjQ3IikKc291cmNlX2Zyb21fZ2l0aHViKHJlcG9zaXRveSA9ICJjTk1GX2Z1bmN0aW9ucyIsdmVyc2lvbiA9ICIwLjQuMDQiLHNjcmlwdF9uYW1lID0gImNubWZfZnVuY3Rpb25zX1YzLlIiKQpzb3VyY2VfZnJvbV9naXRodWIocmVwb3NpdG95ID0gInNjX2dlbmVyYWxfZnVuY3Rpb25zIix2ZXJzaW9uID0gIjAuMS4yOCIsc2NyaXB0X25hbWUgPSAiZnVuY3Rpb25zLlIiKQpgYGAKCiMgRGF0YQoKYGBge3J9Cmx1bmcgPSByZWFkUkRTKCIuL0RhdGEvbHVuZ19jYW5jZXJjZWxsc193aXRoVFBfb25seVBhdGllbnRzLnJkcyIpCmx1bmdfcGF0aWVudHMgPSBsdW5nJHBhdGllbnQuaWRlbnQgJT4lIHVuaXF1ZSgpICU+JSBhcy5jaGFyYWN0ZXIoKQpsdW5nX3BhdGllbnRzX2ZpbHRlcmVkID0gbHVuZ19wYXRpZW50c1shKGx1bmdfcGF0aWVudHMgJWluJSBjKCJYMTA1NW5ldyIsIlgxMDk5IikpXSAjIHJlbW92ZSBwYXRpZW50cyB3aXRoIGxlc3MgdGhhbiAxMDAgbWFsaWduYW50IGNlbGxzCmx1bmcgPSBzdWJzZXQoeCA9IGx1bmcsc3Vic2V0ID0gcGF0aWVudC5pZGVudCAlaW4lIGx1bmdfcGF0aWVudHNfZmlsdGVyZWQpCnhlbm8gPSByZWFkUkRTKCIuL0RhdGEvMTB4X3hlbm9fMTAwMC5SZHMiKQoKCmdlbmVzZXRzIDwtIG1zaWdkYl9kb3dubG9hZCgiSG9tbyBzYXBpZW5zIixjYXRlZ29yeT0iSCIpIApoaWZfdGFyZ2V0cyA9IGMoIkxESEEiLCAiUFBGSUE0IiwgIlBGS0ZCNCIsICJBSzQiLCAiRVJPMUEiLCAiQk5JUDNMIiwgIkJOSVAzIiwgICNodHRwczovL3B1Ym1lZC5uY2JpLm5sbS5uaWguZ292LzM2Mzg0MTI4LwoiUEZLTCIsICJFTk8xIiwgIk1JUjIxMEhHIiwgIlRQSTEiLCAiSElMUERBIiwgIlBHSzEiLCAiSU5TSUcyIiwgCiJBSzRQMSIsICJBQzExNDgwMy4xIiwgIkVOTzIiLCAiRlVUMTEiLCAiRUdMTjMiLCAiTkRSRzEiLCAiQk5JUDNQMSIsIAoiV0RSNTQiLCAiT1ZPTDEtQVMxIiwgIlNUQzIiLCAiRERYNDEiLCAiQzRvcmY0NyIsICJHWVMxIiwgIkFOS1JEMzciLCAKIkJJQ0RMMiIsICJQNEhBMSIsICJQREsxIiwgIk9TTVIiLCAiUEtNIiwgIkxESEFQNSIsICJBTDE1ODIwMS4xIiwgCiJQRktQIiwgIk1JUjIxMCIsICJOTFJQM1AxIiwgIkdTWDEiLCAiQUwxMDk5NDYuMSIsICJQR0FNMSIsICJTTEMxNkEzIiwgCiJJU00yIiwgIlRDQUYyIiwgIkFSSUQzQSIsICJLRE0zQSIsICJKTUpENiIsICJDNG9yZjMiKQpnZW5lc2V0c1tbIkhJRl90YXJnZXRzIl1dID0gaGlmX3RhcmdldHMKYGBgCgojIFBhdGllbnRzIGxvZzIoVFBNKQpgYGB7cn0KCnNjb3Jlc0FuZEluZGljZXNfcGF0aWVudHNfdHBtID0gbGlzdCgpCmk9MApmdW4gPC0gZnVuY3Rpb24oeCxkYXRhKSB7CiAgICBpPDwtaSsxCiAgICBtZXNzYWdlKHBhc3RlKCJwYXRod2F5ICIpLGkpCiAgICBhbGxfcGF0aWVudHMgPSBjKCkKICAgIGZvciAocGF0aWVudCBpbiB1bmlxdWUobHVuZyRwYXRpZW50LmlkZW50KSkgewogICAgICAgIHBhdGllbnRfZGF0YSA9IHN1YnNldChsdW5nLHN1YnNldCA9IHBhdGllbnQuaWRlbnQgPT0gcGF0aWVudCkKICAgICAgICByZXMgPSBnZXRQYXRod2F5U2NvcmVzKCgyXnBhdGllbnRfZGF0YUBhc3NheXMkUk5BQGRhdGEpLTEsIHVubGlzdCh4KSlbWyJwYXRod2F5U2NvcmVzIl1dCiAgICAgICAgYWxsX3BhdGllbnRzID0gYyhhbGxfcGF0aWVudHMscmVzKQogICAgfQogICAgc2NvcmVzQW5kSW5kaWNlc19wYXRpZW50c190cG0gPSBhcHBlbmQoc2NvcmVzQW5kSW5kaWNlc19wYXRpZW50c190cG0sYWxsX3BhdGllbnRzKQoKCn0KCgpzY29yZXNBbmRJbmRpY2VzX3BhdGllbnRzX3RwbSA9IGxhcHBseShnZW5lc2V0cyxkYXRhPWx1bmcsIEZVTiA9IGZ1bikgCgoKYGBgCgpgYGB7cn0KZm9yIChwYXRod2F5X25hbWUgaW4gbmFtZXMoc2NvcmVzQW5kSW5kaWNlc19wYXRpZW50c190cG0pKSB7CiAgbHVuZyAlPD4lIEFkZE1ldGFEYXRhKG1ldGFkYXRhID0gdW5saXN0KHNjb3Jlc0FuZEluZGljZXNfcGF0aWVudHNfdHBtW1twYXRod2F5X25hbWVdXSksY29sLm5hbWUgPSBwYXRod2F5X25hbWUpCgp9CmBgYAoKCgoKCmBgYHtyfQpzaWduZl9wbG90X3ByZV92c19vbjwtIGZ1bmN0aW9uKGRhdGFzZXQscHJvZ3JhbXMscGF0aWVudC5pZGVudF92YXIscHJlZml4LHByZV9vbix0ZXN0LHRpbWUucG9pbnRfdmFyKSB7CiAgICBmaW5hbF9kZiA9IGRhdGEuZnJhbWUoKQogICAgZm9yIChtZXRlZ2VuZSBpbiBwcm9ncmFtcykgewogICAgICBnZW5lc19ieV90cCA9IEZldGNoRGF0YShvYmplY3QgPSBkYXRhc2V0LHZhcnMgPSBtZXRlZ2VuZSkgJT4lIHJvd1N1bXMoKSAlPiUgYXMuZGF0YS5mcmFtZSgpICNtZWFuIGV4cHJlc3Npb24KICAgICAgbmFtZXMoZ2VuZXNfYnlfdHApWzFdID0gbWV0ZWdlbmUKICAgICAgZ2VuZXNfYnlfdHAgPSBjYmluZChnZW5lc19ieV90cCxGZXRjaERhdGEob2JqZWN0ID0gZGF0YXNldCx2YXJzID0gYyhwYXRpZW50LmlkZW50X3Zhcix0aW1lLnBvaW50X3ZhcikpKSAjIGFkZCBpZCBhbmQgdGltZSBwb2ludHMKICAgICAgCiAgICAgIAogICAgICBnZW5lc19ieV90cF9mb3JQbG90ID0gIGdlbmVzX2J5X3RwICU+JSBtdXRhdGUoISFlbnN5bShwYXRpZW50LmlkZW50X3ZhcikgOj0gcGFzdGUocHJlZml4LGdlbmVzX2J5X3RwWyxwYXRpZW50LmlkZW50X3Zhcl0pKSAjYWRkICJtb2RlbCIgYmVmb3JlICBlYWNoIG1vZGVsL3BhdGllbnQKICAgICAgZm0gPC0gYXMuZm9ybXVsYShwYXN0ZShtZXRlZ2VuZSwgIn4iLCB0aW1lLnBvaW50X3ZhcikpICNtYWtlIGZvcm11bGEgdG8gcGxvdAogICAgICAKICAgICAgI3Bsb3QgYW5kIHNwbGl0IGJ5IHBhdGllbnQ6ICAgCiAgICAgIHN0YXQudGVzdCA9IGNvbXBhcmVfbWVhbnMoZm9ybXVsYSA9IGZtICxkYXRhID0gZ2VuZXNfYnlfdHBfZm9yUGxvdCxtZXRob2QgPSB0ZXN0LGdyb3VwLmJ5ID0gcGF0aWVudC5pZGVudF92YXIscC5hZGp1c3QubWV0aG9kID0gImZkciIpJT4lIAogICAgICAgICAgICAgIGRwbHlyOjpmaWx0ZXIoZ3JvdXAxID09IHByZV9vblsxXSAmIGdyb3VwMiA9PSBwcmVfb25bMl0pICAjZmlsdGVyIGZvciBwcmUgdnMgb24gdHJlYXRtZW50IG9ubHkKICAgICAgZmluYWxfZGYgPSByYmluZChmaW5hbF9kZixzdGF0LnRlc3QpCiAgICB9CiAgICByZXR1cm4oZmluYWxfZGYpCn0KCmZpbmFsX2RmID0gc2lnbmZfcGxvdF9wcmVfdnNfb24oZGF0YXNldCA9IGx1bmcsdGltZS5wb2ludF92YXIgPSAidGltZS5wb2ludCIscHJlZml4ID0gInBhdGllbnQiLHBhdGllbnQuaWRlbnRfdmFyID0gInBhdGllbnQuaWRlbnQiLHByZV9vbiA9IGMoInByZS10cmVhdG1lbnQiLCJvbi10cmVhdG1lbnQiKSx0ZXN0ID0gIndpbGNveC50ZXN0Iixwcm9ncmFtcyA9IG5hbWVzKHNjb3Jlc0FuZEluZGljZXMpICkKZmluYWxfZGYgPSByZXNoYXBlMjo6ZGNhc3QoZmluYWxfZGYsIHBhdGllbnQuaWRlbnQgIH4ueS4sdmFsdWUudmFyID0gInAuYWRqIikgJT4lIGNvbHVtbl90b19yb3duYW1lcygicGF0aWVudC5pZGVudCIpCgpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD04fQpzaWdfaGVhdG1hcChhbGxfcGF0aWVudHNfcmVzdWx0ID0gdChmaW5hbF9kZikgJT4lIGFzLmRhdGEuZnJhbWUoKSx0aXRsZSA9ICJhZCIpCgpgYGAKCgpgYGB7cn0KYWxsX3BhdGh3YXlzID0gbGlzdCgpCmZvciAoaGFsbG1hcmsgaW4gbmFtZXMoc2NvcmVzQW5kSW5kaWNlc19wYXRpZW50c190cG0pKSB7CiAgZGF0YSA9IEZldGNoRGF0YShvYmplY3QgPSBsdW5nLHZhcnMgPSBjKGhhbGxtYXJrLCAidGltZS5wb2ludCIsICJwYXRpZW50LmlkZW50IikpCiAgYWxsX3BhdGllbnRzID0gbGlzdCgpCiAgZm9yIChwYXRpZW50IGluIHVuaXF1ZShsdW5nJHBhdGllbnQuaWRlbnQpKSB7CiAgICBtZWFuMSA9IGRhdGEgJT4lIGZpbHRlcihwYXRpZW50LmlkZW50ID09IHBhdGllbnQsIHRpbWUucG9pbnQgPT0gInByZS10cmVhdG1lbnQiKSAlPiUgcHVsbCgxKSAlPiUgbWVhbigpCiAgICBtZWFuMiA9IGRhdGEgJT4lIGZpbHRlcihwYXRpZW50LmlkZW50ID09IHBhdGllbnQsIHRpbWUucG9pbnQgPT0gIm9uLXRyZWF0bWVudCIpICU+JSBwdWxsKDEpICU+JSBtZWFuKCkKICAgIGZjID0gbWVhbjEgLyBtZWFuMgogICAgYWxsX3BhdGllbnRzW1twYXRpZW50XV0gPSBmYwogIH0KICBhbGxfcGF0aHdheXNbW2hhbGxtYXJrXV0gPSBhbGxfcGF0aWVudHMKfQoKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTl9CmEgPSBhcy5kYXRhLmZyYW1lKGxhcHBseShhbGxfcGF0aHdheXMsIHVubGlzdCkpCmEgPSBsb2cyKHQoYSkgJT4lIGFzLmRhdGEuZnJhbWUoKSkKYnJlYWtzIDwtIGMoc2VxKC0xLDEsYnk9MC4xKSkKY29sb3JzX2Zvcl9wbG90IDwtIGNvbG9yUmFtcFBhbGV0dGUoY29sb3JzID0gYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkobiA9IGxlbmd0aChicmVha3MpKQoKcGhlYXRtYXAoYSxjb2xvciA9IGNvbG9yc19mb3JfcGxvdCxicmVha3MgPSBicmVha3MsZGlzcGxheV9udW1iZXJzID0gVCkKCmBgYAoKIyBQYXRpZW50cyBUUE0KCmBgYHtyfQoKc2NvcmVzQW5kSW5kaWNlc19wYXRpZW50c19sb2d0cG0gPSBsaXN0KCkKaT0wCmZ1biA8LSBmdW5jdGlvbih4LGRhdGEpIHsKICAgIGk8PC1pKzEKICAgIG1lc3NhZ2UocGFzdGUoInBhdGh3YXkgIiksaSkKICAgIGFsbF9wYXRpZW50cyA9IGMoKQogICAgZm9yIChwYXRpZW50IGluIHVuaXF1ZShsdW5nJHBhdGllbnQuaWRlbnQpKSB7CiAgICAgICAgcGF0aWVudF9kYXRhID0gc3Vic2V0KGx1bmcsc3Vic2V0ID0gcGF0aWVudC5pZGVudCA9PSBwYXRpZW50KQogICAgICAgIHJlcyA9IGdldFBhdGh3YXlTY29yZXMoKDJecGF0aWVudF9kYXRhQGFzc2F5cyRSTkFAZGF0YSktMSwgdW5saXN0KHgpKVtbInBhdGh3YXlTY29yZXMiXV0KICAgICAgICBhbGxfcGF0aWVudHMgPSBjKGFsbF9wYXRpZW50cyxyZXMpCiAgICB9CiAgICBzY29yZXNBbmRJbmRpY2VzX3BhdGllbnRzX2xvZ3RwbSA9IGFwcGVuZChzY29yZXNBbmRJbmRpY2VzX3BhdGllbnRzX2xvZ3RwbSxhbGxfcGF0aWVudHMpCgoKfQoKCnNjb3Jlc0FuZEluZGljZXNfcGF0aWVudHNfbG9ndHBtID0gbGFwcGx5KGdlbmVzZXRzLGRhdGE9bHVuZywgRlVOID0gZnVuKSAKCgpgYGAKCmBgYHtyfQpmb3IgKHBhdGh3YXlfbmFtZSBpbiBuYW1lcyhzY29yZXNBbmRJbmRpY2VzX3BhdGllbnRzX2xvZ3RwbSkpIHsKICBsdW5nICU8PiUgQWRkTWV0YURhdGEobWV0YWRhdGEgPSB1bmxpc3Qoc2NvcmVzQW5kSW5kaWNlc19wYXRpZW50c19sb2d0cG1bW3BhdGh3YXlfbmFtZV1dKSxjb2wubmFtZSA9IHBhdGh3YXlfbmFtZSkKCn0KYGBgCgpgYGB7cn0KYWxsX3BhdGh3YXlzID0gbGlzdCgpCmZvciAoaGFsbG1hcmsgaW4gbmFtZXMoc2NvcmVzQW5kSW5kaWNlc19wYXRpZW50c190cG0pKSB7CiAgZGF0YSA9IEZldGNoRGF0YShvYmplY3QgPSBsdW5nLHZhcnMgPSBjKGhhbGxtYXJrLCAidGltZS5wb2ludCIsICJwYXRpZW50LmlkZW50IikpCiAgYWxsX3BhdGllbnRzID0gbGlzdCgpCiAgZm9yIChwYXRpZW50IGluIHVuaXF1ZShsdW5nJHBhdGllbnQuaWRlbnQpKSB7CiAgICBtZWFuMSA9IGRhdGEgJT4lIGZpbHRlcihwYXRpZW50LmlkZW50ID09IHBhdGllbnQsIHRpbWUucG9pbnQgPT0gInByZS10cmVhdG1lbnQiKSAlPiUgcHVsbCgxKSAlPiUgbWVhbigpCiAgICBtZWFuMiA9IGRhdGEgJT4lIGZpbHRlcihwYXRpZW50LmlkZW50ID09IHBhdGllbnQsIHRpbWUucG9pbnQgPT0gIm9uLXRyZWF0bWVudCIpICU+JSBwdWxsKDEpICU+JSBtZWFuKCkKICAgIGZjID0gbWVhbjEgLyBtZWFuMgogICAgYWxsX3BhdGllbnRzW1twYXRpZW50XV0gPSBmYwogIH0KICBhbGxfcGF0aHdheXNbW2hhbGxtYXJrXV0gPSBhbGxfcGF0aWVudHMKfQoKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTl9CmEgPSBhcy5kYXRhLmZyYW1lKGxhcHBseShhbGxfcGF0aHdheXMsIHVubGlzdCkpCmEgPSBsb2cyKHQoYSkgJT4lIGFzLmRhdGEuZnJhbWUoKSkKYnJlYWtzIDwtIGMoc2VxKC0xLDEsYnk9MC4xKSkKY29sb3JzX2Zvcl9wbG90IDwtIGNvbG9yUmFtcFBhbGV0dGUoY29sb3JzID0gYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkobiA9IGxlbmd0aChicmVha3MpKQoKcGhlYXRtYXAoYSxjb2xvciA9IGNvbG9yc19mb3JfcGxvdCxicmVha3MgPSBicmVha3MsZGlzcGxheV9udW1iZXJzID0gVCkKCmBgYAoKIyBYZW5vIFRQTQpgYGB7cn0KCnNjb3Jlc0FuZEluZGljZXMgPSBsaXN0KCkKaT0wCmZ1biA8LSBmdW5jdGlvbih4LGRhdGFzZXQpIHsKICAgIGk8PC1pKzEKICAgIG1lc3NhZ2UocGFzdGUoInBhdGh3YXkgIiksaSkKICAgIGFsbF9wYXRpZW50cyA9IGMoKQogICAgZm9yIChwYXRpZW50IGluIHVuaXF1ZShkYXRhc2V0JG9yaWcuaWRlbnQpKSB7CiAgICAgICAgcGF0aWVudF9kYXRhID0gc3Vic2V0KGRhdGFzZXQsc3Vic2V0ID0gb3JpZy5pZGVudCA9PSBwYXRpZW50KQogICAgICAgIHJlcyA9IGdldFBhdGh3YXlTY29yZXMoKDJecGF0aWVudF9kYXRhQGFzc2F5cyRSTkFAZGF0YSktMSwgdW5saXN0KHgpKVtbInBhdGh3YXlTY29yZXMiXV0KICAgICAgICBhbGxfcGF0aWVudHMgPSBjKGFsbF9wYXRpZW50cyxyZXMpCiAgICB9CiAgICBzY29yZXNBbmRJbmRpY2VzID0gYXBwZW5kKHNjb3Jlc0FuZEluZGljZXMsYWxsX3BhdGllbnRzKQoKCn0KCgpzY29yZXNBbmRJbmRpY2VzID0gbGFwcGx5KGdlbmVzZXRzLGRhdGFzZXQ9eGVubywgRlVOID0gZnVuKSAKCgpgYGAKCgpgYGB7cn0KZm9yIChwYXRod2F5X25hbWUgaW4gbmFtZXMoc2NvcmVzQW5kSW5kaWNlcykpIHsKICB4ZW5vICU8PiUgQWRkTWV0YURhdGEobWV0YWRhdGEgPSB1bmxpc3Qoc2NvcmVzQW5kSW5kaWNlc1tbcGF0aHdheV9uYW1lXV0pLGNvbC5uYW1lID0gcGF0aHdheV9uYW1lKQp9CmBgYAoKCgpgYGB7cn0KYWxsX3BhdGh3YXlzID0gbGlzdCgpCmZvciAoaGFsbG1hcmsgaW4gbmFtZXMoc2NvcmVzQW5kSW5kaWNlcykpIHsKICBkYXRhID0gRmV0Y2hEYXRhKG9iamVjdCA9IGx1bmcsdmFycyA9IGMoaGFsbG1hcmssICJ0aW1lLnBvaW50IiwgInBhdGllbnQuaWRlbnQiKSkKICBhbGxfcGF0aWVudHMgPSBsaXN0KCkKICBmb3IgKHBhdGllbnQgaW4gdW5pcXVlKGx1bmckcGF0aWVudC5pZGVudCkpIHsKICAgIG1lYW4xID0gZGF0YSAlPiUgZmlsdGVyKHBhdGllbnQuaWRlbnQgPT0gcGF0aWVudCwgdGltZS5wb2ludCA9PSAicHJlLXRyZWF0bWVudCIpICU+JSBwdWxsKDEpICU+JSBtZWFuKCkKICAgIG1lYW4yID0gZGF0YSAlPiUgZmlsdGVyKHBhdGllbnQuaWRlbnQgPT0gcGF0aWVudCwgdGltZS5wb2ludCA9PSAib24tdHJlYXRtZW50IikgJT4lIHB1bGwoMSkgJT4lIG1lYW4oKQogICAgZmMgPSBtZWFuMSAvIG1lYW4yCiAgICBhbGxfcGF0aWVudHNbW3BhdGllbnRdXSA9IGZjCiAgfQogIGFsbF9wYXRod2F5c1tbaGFsbG1hcmtdXSA9IGFsbF9wYXRpZW50cwp9CgpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9OX0KYSA9IGFzLmRhdGEuZnJhbWUobGFwcGx5KGFsbF9wYXRod2F5cywgdW5saXN0KSkKYSA9IGxvZzIodChhKSAlPiUgYXMuZGF0YS5mcmFtZSgpKQpicmVha3MgPC0gYyhzZXEoLTEsMSxieT0wLjEpKQpjb2xvcnNfZm9yX3Bsb3QgPC0gY29sb3JSYW1wUGFsZXR0ZShjb2xvcnMgPSBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKShuID0gbGVuZ3RoKGJyZWFrcykpCgpwaGVhdG1hcChhLGNvbG9yID0gY29sb3JzX2Zvcl9wbG90LGJyZWFrcyA9IGJyZWFrcyxkaXNwbGF5X251bWJlcnMgPSBUKQoKYGBgCiMgWGVubyBsb2cyKFRQTSkKYGBge3J9CgpzY29yZXNBbmRJbmRpY2VzID0gbGlzdCgpCmk9MApmdW4gPC0gZnVuY3Rpb24oeCxkYXRhc2V0KSB7CiAgICBpPDwtaSsxCiAgICBtZXNzYWdlKHBhc3RlKCJwYXRod2F5ICIpLGkpCiAgICBhbGxfcGF0aWVudHMgPSBjKCkKICAgIGZvciAocGF0aWVudCBpbiB1bmlxdWUoZGF0YXNldCRvcmlnLmlkZW50KSkgewogICAgICAgIHBhdGllbnRfZGF0YSA9IHN1YnNldChkYXRhc2V0LHN1YnNldCA9IG9yaWcuaWRlbnQgPT0gcGF0aWVudCkKICAgICAgICByZXMgPSBnZXRQYXRod2F5U2NvcmVzKCgyXnBhdGllbnRfZGF0YUBhc3NheXMkUk5BQGRhdGEpLTEsIHVubGlzdCh4KSlbWyJwYXRod2F5U2NvcmVzIl1dCiAgICAgICAgYWxsX3BhdGllbnRzID0gYyhhbGxfcGF0aWVudHMscmVzKQogICAgfQogICAgc2NvcmVzQW5kSW5kaWNlcyA9IGFwcGVuZChzY29yZXNBbmRJbmRpY2VzLGFsbF9wYXRpZW50cykKCgp9CgoKc2NvcmVzQW5kSW5kaWNlcyA9IGxhcHBseShnZW5lc2V0c1sxOjJdLGRhdGFzZXQ9eGVubywgRlVOID0gZnVuKSAKCgpgYGAKCgpgYGB7cn0KZm9yIChwYXRod2F5X25hbWUgaW4gbmFtZXMoc2NvcmVzQW5kSW5kaWNlcykpIHsKICB4ZW5vICU8PiUgQWRkTWV0YURhdGEobWV0YWRhdGEgPSB1bmxpc3Qoc2NvcmVzQW5kSW5kaWNlc1tbcGF0aHdheV9uYW1lXV0pLGNvbC5uYW1lID0gcGF0aHdheV9uYW1lKQp9CmBgYAoKCgpgYGB7cn0KYWxsX3BhdGh3YXlzID0gbGlzdCgpCmZvciAoaGFsbG1hcmsgaW4gbmFtZXMoc2NvcmVzQW5kSW5kaWNlcykpIHsKICBkYXRhID0gRmV0Y2hEYXRhKG9iamVjdCA9IGx1bmcsdmFycyA9IGMoaGFsbG1hcmssICJ0aW1lLnBvaW50IiwgInBhdGllbnQuaWRlbnQiKSkKICBhbGxfcGF0aWVudHMgPSBsaXN0KCkKICBmb3IgKHBhdGllbnQgaW4gdW5pcXVlKGx1bmckcGF0aWVudC5pZGVudCkpIHsKICAgIG1lYW4xID0gZGF0YSAlPiUgZmlsdGVyKHBhdGllbnQuaWRlbnQgPT0gcGF0aWVudCwgdGltZS5wb2ludCA9PSAicHJlLXRyZWF0bWVudCIpICU+JSBwdWxsKDEpICU+JSBtZWFuKCkKICAgIG1lYW4yID0gZGF0YSAlPiUgZmlsdGVyKHBhdGllbnQuaWRlbnQgPT0gcGF0aWVudCwgdGltZS5wb2ludCA9PSAib24tdHJlYXRtZW50IikgJT4lIHB1bGwoMSkgJT4lIG1lYW4oKQogICAgZmMgPSBtZWFuMSAvIG1lYW4yCiAgICBhbGxfcGF0aWVudHNbW3BhdGllbnRdXSA9IGZjCiAgfQogIGFsbF9wYXRod2F5c1tbaGFsbG1hcmtdXSA9IGFsbF9wYXRpZW50cwp9CgpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9OX0KYSA9IGFzLmRhdGEuZnJhbWUobGFwcGx5KGFsbF9wYXRod2F5cywgdW5saXN0KSkKYSA9IGxvZzIodChhKSAlPiUgYXMuZGF0YS5mcmFtZSgpKQpicmVha3MgPC0gYyhzZXEoLTEsMSxieT0wLjEpKQpjb2xvcnNfZm9yX3Bsb3QgPC0gY29sb3JSYW1wUGFsZXR0ZShjb2xvcnMgPSBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKShuID0gbGVuZ3RoKGJyZWFrcykpCgpwaGVhdG1hcChhLGNvbG9yID0gY29sb3JzX2Zvcl9wbG90LGJyZWFrcyA9IGJyZWFrcyxkaXNwbGF5X251bWJlcnMgPSBUKQoKYGBgCgo8c2NyaXB0IHNyYz0iaHR0cHM6Ly9oeXBvdGhlcy5pcy9lbWJlZC5qcyIgYXN5bmM+PC9zY3JpcHQ+Cgo=