Functions
# modify dotplot to be without exponent of the expression
newDef <- deparse(DotPlot)
newDef[57] = " return(mean(x = x))"
newDef[136] = " yes = \"Identity\", no = \"Split Identity\")) + cowplot::theme_cowplot()" #add package name
newDef[95] = " data.use <- data.use" # remove log
DotPlot.2 <- eval(parse(text=newDef))
Data
library("readxl")
GSE17708 <- read_excel("./Data/EMT_pathways/GSE17708_Keshamouni_TGFB1_logs.xls",sheet = 1,progress = T,cell_cols("BC")) %>% dplyr::slice(3:n())
-
\
/
-
GSE17708_genes <- read_excel("./Data/EMT_pathways/GSE17708_Keshamouni_TGFB1_logs.xls",sheet = 1,progress = T,cell_cols("B"))
-
\
/
-
GSE17708 = cbind(GSE17708,GSE17708_genes)
GSE17708_72H_UP_df = GSE17708 %>% set_names(c("deg","gene")) %>% filter(deg > 1) %>% dplyr::select(gene) #take up genes
GSE17708_72H_UP = GSE17708_72H_UP_df %>% filter(!gene == "---") %>% pull(gene) # remove na gene symbols
GSE17708_72H_UP = GSE17708_72H_UP %>% strsplit(split = " /// ") %>% unlist() %>% unique() # split ambiguous genes
bayers_EMT = c("ZEB1 LIX1L VIM AXL MMPT ANTXR2 C3orf21 FN1 NRP1 TGFBI GALNT5 PPARG FN1 HNMT CARD6 RBPMS TNFRSF21 TMEM45B MPP7 SSH3 MTAC2D1 MUC1 EPPK1 SHROOM3 EPN3 PRSS22 AP1M2 SH3YL1 KLC3 SERINC2 EVPL FXYD3 CLDN4 CRB3 LRRC54 MAPK13 EPPK1 GALNT3 STAP2 AP1M2 DSP ELMO3 KRTCAP3 MAL2 F11R GPR110 GPT56 KRT19 GRHL1 BSPRY C1orf116 S100A14 SPINT2 ANKRD22 ST14 GRHL2 PRR5 BSPRY TJP3 TACSTD2 CDH3 C1orf172 CDS1 PRR5 MPZL2 INADL EPN3 RBM35A TMC4 ITGB6 TMEM125 EPHA1 CDS1 ENPP5 ST14 EPB41L5 ERBB3 RAB25 PRSS8 TMEM30B CLDN7 RBM35A TACSTD1 CDS1 SCNN1A CDH1") %>% strsplit(split = "\t") %>% unlist() %>% unique()
bayers_mes = bayers_EMT[1:16]
bayers_epi = bayers_EMT[17:length(bayers_EMT)]
hEMT = c("PDPN, ITGA5, ITGA6, TGFBI, LAMC2, MMP10, LAMA3, CDH13, SERPINE1, P4HA2, TNC, MMP1")%>% strsplit(split = ", ") %>% unlist() %>% unique()
epi_genes = c("CDH1, DSP, OCLN, CRB3")%>% strsplit(split = ", ") %>% unlist() %>% unique()
mes_genes = c("VIM, CDH2, FOXC2, SNAI1, SNAI2, TWIST1, FN1, ITGB6, MMP2, MMP3, MMP9, SOX10, GSC, ZEB1, ZEB2, TWIST2")%>% strsplit(split = ", ") %>% unlist() %>% unique()
hallmark_emt = genesets$HALLMARK_EPITHELIAL_MESENCHYMAL_TRANSITION
calculate score- scaled
data
gene_list = list(hallmark_emt = hallmark_emt, GSE17708_72H_UP = GSE17708_72H_UP,bayers_mes = bayers_mes,bayers_epi = bayers_epi,Tagli_hEMT = hEMT,Tagli_epi = epi_genes,Tagli_mes = mes_genes)
idx=1
for (dataset in list(xeno,lung)) {
# dataset = ScaleData(object = dataset,features = unlist(gene_list))
for (i in seq_along(gene_list)) {
genes = gene_list[[i]]
genes = genes[genes %in% rownames(dataset)]
name = names(gene_list)[i]
scores = dataset@assays$RNA@scale.data[genes,] %>% colMeans()
dataset %<>% AddMetaData(metadata = scores,col.name = name)
}
dataset$Tagli_emt = dataset$Tagli_mes - dataset$Tagli_epi
dataset$bayers_EMT = dataset$bayers_mes - dataset$bayers_epi
if (idx == 1 ) {
xeno = dataset
}
if (idx==2) {
lung = dataset
}
idx = idx+1
}
x_names <-emt_pathways <- c("hallmark_emt","GSE17708_72H_UP","bayers_EMT","Tagli_emt","Tagli_hEMT")
x_names[1] = "Hallmark \n EMT"
x_names[2] = "GSE17708\n72H_UP"
p1 = DotPlot.2(object = xeno, features = emt_pathways,group.by = 'treatment',scale = T,scale.by = "size")+
guides(size = guide_legend(title = "Cells expressing (%)"),color = guide_colorbar(title = "Average Score")) +
theme(axis.text.x = element_text( size = 10))+ scale_x_discrete(labels= x_names)+ggtitle("Models")+ scale_size_binned()
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.
p2 = DotPlot.2(object = lung, features = emt_pathways,group.by = 'time.point',scale = T,scale.by = "size")+
guides(size = guide_legend(title = "Cells expressing (%)"),color = guide_colorbar(title = "Average Score")) +
theme(axis.text.x = element_text( size = 10))+ scale_x_discrete(labels= x_names)+ggtitle("Patients")+ scale_size_binned()
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.
p1 + p2

DotPlot.2(object = xeno, features = emt_pathways,group.by = 'orig.ident',split.by = "treatment", scale = T,scale.by = "size",cols = "Blues")+
guides(size = guide_legend(title = "Cells expressing (%)"),color = guide_colorbar(title = "Average Score")) +
theme(axis.text.x = element_text( size = 10))+ scale_x_discrete(labels= x_names)+ggtitle("Models")+ scale_size_binned()
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.

data = FetchData(object = lung,vars = c("time.point",emt_pathways))
df = reshape2::melt(data,value.name = "logTPM") %>% dplyr::rename(gene = variable)
stat.test <- df %>%
group_by(gene) %>%
wilcox_test(logTPM ~ time.point,comparisons = list(c("pre-treatment","on-treatment"))) %>%
mutate(y.position = 5)
stat.test
stat.test <- stat.test %>%
add_xy_position(x = "gene", dodge = 0.8)
ggboxplot(
df,
x = "gene",
y = "logTPM",
color = "time.point",
palette = "jco",
add = "jitter"
)+ stat_pvalue_manual(stat.test,y.position = 4, label = "{p.adj.signif}",remove.bracket =F)+ geom_violin(aes(fill = time.point), trim = FALSE)

calculate score- log
tpm
gene_list = list(hallmark_emt = hallmark_emt, GSE17708_72H_UP = GSE17708_72H_UP,bayers_mes = bayers_mes,bayers_epi = bayers_epi,Tagli_hEMT = hEMT,Tagli_epi = epi_genes,Tagli_mes = mes_genes)
idx=1
for (dataset in list(xeno,lung)) {
# dataset = ScaleData(object = dataset,features = unlist(gene_list))
for (i in seq_along(gene_list)) {
genes = gene_list[[i]]
genes = genes[genes %in% rownames(dataset)]
name = names(gene_list)[i]
scores = dataset@assays$RNA@data[genes,] %>% colMeans()
dataset %<>% AddMetaData(metadata = scores,col.name = name)
}
dataset$Tagli_emt = dataset$Tagli_mes - dataset$Tagli_epi
dataset$bayers_EMT = dataset$bayers_mes - dataset$bayers_epi
if (idx == 1 ) {
xeno = dataset
}
if (idx==2) {
lung = dataset
}
idx = idx+1
}
x_names <-emt_pathways <- c("hallmark_emt","GSE17708_72H_UP","bayers_EMT","Tagli_emt","Tagli_hEMT")
x_names[1] = "Hallmark \n EMT"
x_names[2] = "GSE17708\n72H_UP"
p1 = DotPlot.2(object = xeno, features = emt_pathways,group.by = 'treatment',scale = T,scale.by = "size")+
guides(size = guide_legend(title = "Cells expressing (%)"),color = guide_colorbar(title = "Average Score")) +
theme(axis.text.x = element_text( size = 10))+ scale_x_discrete(labels= x_names)+ggtitle("Models")+ scale_size_binned()
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.
p2 = DotPlot.2(object = lung, features = emt_pathways,group.by = 'time.point',scale = T,scale.by = "size")+
guides(size = guide_legend(title = "Cells expressing (%)"),color = guide_colorbar(title = "Average Score")) +
theme(axis.text.x = element_text( size = 10))+ scale_x_discrete(labels= x_names)+ggtitle("Patients")+ scale_size_binned()
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.
p1 + p2

all_pathways = list()
for (pathway in emt_pathways) {
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 / mean2
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.1))
colors_for_plot <- colorRampPalette(colors = c("blue", "white", "red"))(n = length(breaks))
pheatmap::pheatmap(mat,color = colors_for_plot,breaks = breaks,display_numbers = T,main = "log2(FC) pre/on")

LS0tCnRpdGxlOiAnYHIgcnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLHJlcGxhY2VtZW50ID0gIiIpYCcgCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19jb2xsYXBzZTogeWVzCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvY19kZXB0aDogMQotLS0KCgoKIyBGdW5jdGlvbnMKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CiMgbW9kaWZ5IGRvdHBsb3QgdG8gYmUgd2l0aG91dCBleHBvbmVudCBvZiB0aGUgZXhwcmVzc2lvbgpuZXdEZWYgPC0gZGVwYXJzZShEb3RQbG90KQpuZXdEZWZbNTddID0gICIgICAgICAgICAgICByZXR1cm4obWVhbih4ID0gIHgpKSIKbmV3RGVmWzEzNl0gPSAgIiAgICAgICAgICAgIHllcyA9IFwiSWRlbnRpdHlcIiwgbm8gPSBcIlNwbGl0IElkZW50aXR5XCIpKSArIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSIgI2FkZCBwYWNrYWdlIG5hbWUKbmV3RGVmWzk1XSA9ICIgICAgICAgICAgICAgICAgZGF0YS51c2UgPC0gZGF0YS51c2UiICMgcmVtb3ZlIGxvZwoKRG90UGxvdC4yIDwtIGV2YWwocGFyc2UodGV4dD1uZXdEZWYpKQpgYGAKCiMgRGF0YQoKYGBge3J9CgpgYGAKCgpgYGB7cn0KbGlicmFyeSgicmVhZHhsIikKR1NFMTc3MDggPC0gcmVhZF9leGNlbCgiLi9EYXRhL0VNVF9wYXRod2F5cy9HU0UxNzcwOF9LZXNoYW1vdW5pX1RHRkIxX2xvZ3MueGxzIixzaGVldCA9IDEscHJvZ3Jlc3MgPSBULGNlbGxfY29scygiQkMiKSkgJT4lIGRwbHlyOjpzbGljZSgzOm4oKSkKR1NFMTc3MDhfZ2VuZXMgPC0gcmVhZF9leGNlbCgiLi9EYXRhL0VNVF9wYXRod2F5cy9HU0UxNzcwOF9LZXNoYW1vdW5pX1RHRkIxX2xvZ3MueGxzIixzaGVldCA9IDEscHJvZ3Jlc3MgPSBULGNlbGxfY29scygiQiIpKQpHU0UxNzcwOCA9IGNiaW5kKEdTRTE3NzA4LEdTRTE3NzA4X2dlbmVzKSAKR1NFMTc3MDhfNzJIX1VQX2RmID0gR1NFMTc3MDggJT4lIHNldF9uYW1lcyhjKCJkZWciLCJnZW5lIikpICU+JSBmaWx0ZXIoZGVnID4gMSkgJT4lIGRwbHlyOjpzZWxlY3QoZ2VuZSkgI3Rha2UgdXAgZ2VuZXMKR1NFMTc3MDhfNzJIX1VQID0gR1NFMTc3MDhfNzJIX1VQX2RmICU+JSBmaWx0ZXIoIWdlbmUgPT0gIi0tLSIpICU+JSBwdWxsKGdlbmUpICMgcmVtb3ZlIG5hIGdlbmUgc3ltYm9scwpHU0UxNzcwOF83MkhfVVAgPSBHU0UxNzcwOF83MkhfVVAgJT4lIHN0cnNwbGl0KHNwbGl0ID0gIiAvLy8gIikgJT4lIHVubGlzdCgpICU+JSB1bmlxdWUoKSAjIHNwbGl0IGFtYmlndW91cyBnZW5lcwpgYGAKCmBgYHtyfQpiYXllcnNfRU1UID0gYygiWkVCMQlMSVgxTAlWSU0JQVhMCU1NUFQJQU5UWFIyCUMzb3JmMjEJRk4xCU5SUDEJVEdGQkkJR0FMTlQ1CVBQQVJHCUZOMQlITk1UCUNBUkQ2CVJCUE1TCVRORlJTRjIxCVRNRU00NUIJTVBQNwlTU0gzCU1UQUMyRDEJTVVDMQlFUFBLMQlTSFJPT00zCUVQTjMJUFJTUzIyCUFQMU0yCVNIM1lMMQlLTEMzCVNFUklOQzIJRVZQTAlGWFlEMwlDTERONAlDUkIzCUxSUkM1NAlNQVBLMTMJRVBQSzEJR0FMTlQzCVNUQVAyCUFQMU0yCURTUAlFTE1PMwlLUlRDQVAzCU1BTDIJRjExUglHUFIxMTAJR1BUNTYJS1JUMTkJR1JITDEJQlNQUlkJQzFvcmYxMTYJUzEwMEExNAlTUElOVDIJQU5LUkQyMglTVDE0CUdSSEwyCVBSUjUJQlNQUlkJVEpQMwlUQUNTVEQyCUNESDMJQzFvcmYxNzIJQ0RTMQlQUlI1CU1QWkwyCUlOQURMCUVQTjMJUkJNMzVBCVRNQzQJSVRHQjYJVE1FTTEyNQlFUEhBMQlDRFMxCUVOUFA1CVNUMTQJRVBCNDFMNQlFUkJCMwlSQUIyNQlQUlNTOAlUTUVNMzBCCUNMRE43CVJCTTM1QQlUQUNTVEQxCUNEUzEJU0NOTjFBCUNESDEiKSAlPiUgc3Ryc3BsaXQoc3BsaXQgPSAiXHQiKSAlPiUgdW5saXN0KCkgJT4lIHVuaXF1ZSgpCmJheWVyc19tZXMgPSBiYXllcnNfRU1UWzE6MTZdCmJheWVyc19lcGkgPSBiYXllcnNfRU1UWzE3Omxlbmd0aChiYXllcnNfRU1UKV0KCmBgYAoKYGBge3J9CmhFTVQgPSBjKCJQRFBOLCBJVEdBNSwgSVRHQTYsIFRHRkJJLCBMQU1DMiwgTU1QMTAsIExBTUEzLCBDREgxMywgU0VSUElORTEsIFA0SEEyLCBUTkMsIE1NUDEiKSU+JSBzdHJzcGxpdChzcGxpdCA9ICIsICIpICU+JSB1bmxpc3QoKSAlPiUgdW5pcXVlKCkKZXBpX2dlbmVzID0gYygiQ0RIMSwgRFNQLCBPQ0xOLCBDUkIzIiklPiUgc3Ryc3BsaXQoc3BsaXQgPSAiLCAiKSAlPiUgdW5saXN0KCkgJT4lIHVuaXF1ZSgpCm1lc19nZW5lcyA9IGMoIlZJTSwgQ0RIMiwgRk9YQzIsIFNOQUkxLCBTTkFJMiwgVFdJU1QxLCBGTjEsIElUR0I2LCBNTVAyLCBNTVAzLCBNTVA5LCBTT1gxMCwgR1NDLCBaRUIxLCBaRUIyLCBUV0lTVDIiKSU+JSBzdHJzcGxpdChzcGxpdCA9ICIsICIpICU+JSB1bmxpc3QoKSAlPiUgdW5pcXVlKCkKYGBgCgpgYGB7cn0KaGFsbG1hcmtfZW10ID0gZ2VuZXNldHMkSEFMTE1BUktfRVBJVEhFTElBTF9NRVNFTkNIWU1BTF9UUkFOU0lUSU9OCmBgYAoKCiMgY2FsY3VsYXRlIHNjb3JlLSBzY2FsZWQgZGF0YQpgYGB7ciB9CmdlbmVfbGlzdCA9IGxpc3QoaGFsbG1hcmtfZW10ID0gaGFsbG1hcmtfZW10LCBHU0UxNzcwOF83MkhfVVAgPSBHU0UxNzcwOF83MkhfVVAsYmF5ZXJzX21lcyA9IGJheWVyc19tZXMsYmF5ZXJzX2VwaSA9IGJheWVyc19lcGksVGFnbGlfaEVNVCA9IGhFTVQsVGFnbGlfZXBpID0gZXBpX2dlbmVzLFRhZ2xpX21lcyA9IG1lc19nZW5lcykKCmlkeD0xCmZvciAoZGF0YXNldCBpbiBsaXN0KHhlbm8sbHVuZykpIHsKICAjIGRhdGFzZXQgPSBTY2FsZURhdGEob2JqZWN0ID0gZGF0YXNldCxmZWF0dXJlcyA9IHVubGlzdChnZW5lX2xpc3QpKQogIGZvciAoaSBpbiBzZXFfYWxvbmcoZ2VuZV9saXN0KSkgewogICAgZ2VuZXMgPSBnZW5lX2xpc3RbW2ldXQogICAgZ2VuZXMgPSBnZW5lc1tnZW5lcyAlaW4lIHJvd25hbWVzKGRhdGFzZXQpXQogICAgbmFtZSA9IG5hbWVzKGdlbmVfbGlzdClbaV0KICAgIHNjb3JlcyA9IGRhdGFzZXRAYXNzYXlzJFJOQUBzY2FsZS5kYXRhW2dlbmVzLF0gJT4lIGNvbE1lYW5zKCkKICAgIGRhdGFzZXQgJTw+JSBBZGRNZXRhRGF0YShtZXRhZGF0YSA9IHNjb3Jlcyxjb2wubmFtZSA9IG5hbWUpCiAgfQogIGRhdGFzZXQkVGFnbGlfZW10ID0gZGF0YXNldCRUYWdsaV9tZXMgLSBkYXRhc2V0JFRhZ2xpX2VwaQogIGRhdGFzZXQkYmF5ZXJzX0VNVCA9IGRhdGFzZXQkYmF5ZXJzX21lcyAtIGRhdGFzZXQkYmF5ZXJzX2VwaQogIGlmIChpZHggPT0gMSApIHsKICAgIHhlbm8gPSBkYXRhc2V0CiAgfSAKICBpZiAoaWR4PT0yKSB7CiAgICBsdW5nID0gZGF0YXNldAogIH0KICBpZHggPSBpZHgrMQp9Cgp4X25hbWVzIDwtZW10X3BhdGh3YXlzIDwtIGMoImhhbGxtYXJrX2VtdCIsIkdTRTE3NzA4XzcySF9VUCIsImJheWVyc19FTVQiLCJUYWdsaV9lbXQiLCJUYWdsaV9oRU1UIikKeF9uYW1lc1sxXSA9ICJIYWxsbWFyayBcbiBFTVQiCnhfbmFtZXNbMl0gPSAiR1NFMTc3MDhcbjcySF9VUCIKCgpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xNH0KcDEgPSBEb3RQbG90LjIob2JqZWN0ID0geGVubywgZmVhdHVyZXMgPSAgZW10X3BhdGh3YXlzLGdyb3VwLmJ5ICA9ICd0cmVhdG1lbnQnLHNjYWxlID0gVCxzY2FsZS5ieSA9ICJzaXplIikrCiAgZ3VpZGVzKHNpemUgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiQ2VsbHMgZXhwcmVzc2luZyAoJSkiKSxjb2xvciA9IGd1aWRlX2NvbG9yYmFyKHRpdGxlID0gIkF2ZXJhZ2UgU2NvcmUiKSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KCBzaXplID0gMTApKSsgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9IHhfbmFtZXMpK2dndGl0bGUoIk1vZGVscyIpKyBzY2FsZV9zaXplX2Jpbm5lZCgpCgoKcDIgPSBEb3RQbG90LjIob2JqZWN0ID0gbHVuZywgZmVhdHVyZXMgPSAgZW10X3BhdGh3YXlzLGdyb3VwLmJ5ICA9ICd0aW1lLnBvaW50JyxzY2FsZSA9IFQsc2NhbGUuYnkgPSAic2l6ZSIpKwogIGd1aWRlcyhzaXplID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIkNlbGxzIGV4cHJlc3NpbmcgKCUpIiksY29sb3IgPSBndWlkZV9jb2xvcmJhcih0aXRsZSA9ICJBdmVyYWdlIFNjb3JlIikpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dCggc2l6ZSA9IDEwKSkrIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPSB4X25hbWVzKStnZ3RpdGxlKCJQYXRpZW50cyIpKyBzY2FsZV9zaXplX2Jpbm5lZCgpCnAxICsgcDIKYGBgCgoKCgoKCgoKCgoKYGBge3J9CkRvdFBsb3QuMihvYmplY3QgPSB4ZW5vLCBmZWF0dXJlcyA9ICBlbXRfcGF0aHdheXMsZ3JvdXAuYnkgID0gJ29yaWcuaWRlbnQnLHNwbGl0LmJ5ID0gInRyZWF0bWVudCIsIHNjYWxlID0gVCxzY2FsZS5ieSA9ICJzaXplIixjb2xzID0gIkJsdWVzIikrCiAgZ3VpZGVzKHNpemUgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiQ2VsbHMgZXhwcmVzc2luZyAoJSkiKSxjb2xvciA9IGd1aWRlX2NvbG9yYmFyKHRpdGxlID0gIkF2ZXJhZ2UgU2NvcmUiKSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KCBzaXplID0gMTApKSsgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9IHhfbmFtZXMpK2dndGl0bGUoIk1vZGVscyIpKyBzY2FsZV9zaXplX2Jpbm5lZCgpCmBgYAoKCmBgYHtyIH0KCmRhdGEgPSBGZXRjaERhdGEob2JqZWN0ID0gbHVuZyx2YXJzID0gYygidGltZS5wb2ludCIsZW10X3BhdGh3YXlzKSkKZGYgPSByZXNoYXBlMjo6bWVsdChkYXRhLHZhbHVlLm5hbWUgPSAibG9nVFBNIikgJT4lIGRwbHlyOjpyZW5hbWUoZ2VuZSA9IHZhcmlhYmxlKQoKCnN0YXQudGVzdCA8LSBkZiAlPiUKICAgIGdyb3VwX2J5KGdlbmUpICU+JQogIHdpbGNveF90ZXN0KGxvZ1RQTSB+IHRpbWUucG9pbnQsY29tcGFyaXNvbnMgPSAgbGlzdChjKCJwcmUtdHJlYXRtZW50Iiwib24tdHJlYXRtZW50IikpKSAlPiUKICBtdXRhdGUoeS5wb3NpdGlvbiA9IDUpCgpzdGF0LnRlc3QKCnN0YXQudGVzdCA8LSBzdGF0LnRlc3QgJT4lIAogIGFkZF94eV9wb3NpdGlvbih4ID0gImdlbmUiLCBkb2RnZSA9IDAuOCkKCmdnYm94cGxvdCgKICBkZiwKICB4ID0gImdlbmUiLAogIHkgPSAibG9nVFBNIiwKICBjb2xvciA9ICJ0aW1lLnBvaW50IiwKICBwYWxldHRlID0gImpjbyIsCiAgYWRkID0gImppdHRlciIKKSsgc3RhdF9wdmFsdWVfbWFudWFsKHN0YXQudGVzdCx5LnBvc2l0aW9uID0gNCwgbGFiZWwgPSAie3AuYWRqLnNpZ25pZn0iLHJlbW92ZS5icmFja2V0ID1GKSsgZ2VvbV92aW9saW4oYWVzKGZpbGwgPSB0aW1lLnBvaW50KSwgdHJpbSA9IEZBTFNFKQoKCmBgYAoKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTV9CmxpYnJhcnkocnN0YXRpeCkKZGF0YSA9IEZldGNoRGF0YShvYmplY3QgPSBsdW5nLHZhcnMgPSBjKCJ0aW1lLnBvaW50IixlbXRfcGF0aHdheXMsInBhdGllbnQuaWRlbnQiKSkKZGYgPSByZXNoYXBlMjo6bWVsdChkYXRhLHZhbHVlLm5hbWUgPSAibG9nVFBNIikgJT4lIGRwbHlyOjpyZW5hbWUocGF0aHdheSA9IHZhcmlhYmxlKQoKc3RhdC50ZXN0IDwtIGRmICU+JQogICAgZ3JvdXBfYnkocGF0aHdheSkgJT4lCiAgd2lsY294X3Rlc3QobG9nVFBNIH4gdGltZS5wb2ludCxjb21wYXJpc29ucyA9ICBsaXN0KGMoInByZS10cmVhdG1lbnQiLCJvbi10cmVhdG1lbnQiKSkpICU+JSAKICBhZGRfeHlfcG9zaXRpb24oeCA9ICJwYXRod2F5IikgCgoKc3RhdC50ZXN0CgoKCnAgPSBnZ3Bsb3QoZGYsYWVzKCB4ID0gcGF0aWVudC5pZGVudCwgeSA9IGxvZ1RQTSkpKyAKICAgIGdlb21fdmlvbGluKHRyaW0gPSBGLGFlcyhmaWxsID0gdGltZS5wb2ludCkpICsKICBnZW9tX2JveHBsb3QoYWVzKGZpbGwgPSB0aW1lLnBvaW50KSwgd2lkdGg9LjIsIG91dGxpZXIuc2hhcGU9TkEsCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSkpICAgK3RoZW1lX21pbmltYWwoKSArIHN0YXRfcHZhbHVlX21hbnVhbChzdGF0LnRlc3QseS5wb3NpdGlvbiA9IDMsIGxhYmVsID0gIntwLmFkai5zaWduaWZ9IixyZW1vdmUuYnJhY2tldCA9IEYpCgpwK2ZhY2V0X3dyYXAodmFycyhwYXRod2F5KSkKCmBgYAoKCgoKCgojIGNhbGN1bGF0ZSBzY29yZS0gbG9nIHRwbQoKCmBgYHtyIH0KI2NhbGN1bGF0ZSBzY29yZQpnZW5lX2xpc3QgPSBsaXN0KGhhbGxtYXJrX2VtdCA9IGhhbGxtYXJrX2VtdCwgR1NFMTc3MDhfNzJIX1VQID0gR1NFMTc3MDhfNzJIX1VQLGJheWVyc19tZXMgPSBiYXllcnNfbWVzLGJheWVyc19lcGkgPSBiYXllcnNfZXBpLFRhZ2xpX2hFTVQgPSBoRU1ULFRhZ2xpX2VwaSA9IGVwaV9nZW5lcyxUYWdsaV9tZXMgPSBtZXNfZ2VuZXMpCgppZHg9MQpmb3IgKGRhdGFzZXQgaW4gbGlzdCh4ZW5vLGx1bmcpKSB7CiAgIyBkYXRhc2V0ID0gU2NhbGVEYXRhKG9iamVjdCA9IGRhdGFzZXQsZmVhdHVyZXMgPSB1bmxpc3QoZ2VuZV9saXN0KSkKICBmb3IgKGkgaW4gc2VxX2Fsb25nKGdlbmVfbGlzdCkpIHsKICAgIGdlbmVzID0gZ2VuZV9saXN0W1tpXV0KICAgIGdlbmVzID0gZ2VuZXNbZ2VuZXMgJWluJSByb3duYW1lcyhkYXRhc2V0KV0KICAgIG5hbWUgPSBuYW1lcyhnZW5lX2xpc3QpW2ldCiAgICBzY29yZXMgPSBkYXRhc2V0QGFzc2F5cyRSTkFAZGF0YVtnZW5lcyxdICU+JSBjb2xNZWFucygpCiAgICBkYXRhc2V0ICU8PiUgQWRkTWV0YURhdGEobWV0YWRhdGEgPSBzY29yZXMsY29sLm5hbWUgPSBuYW1lKQogIH0KICBkYXRhc2V0JFRhZ2xpX2VtdCA9IGRhdGFzZXQkVGFnbGlfbWVzIC0gZGF0YXNldCRUYWdsaV9lcGkKICBkYXRhc2V0JGJheWVyc19FTVQgPSBkYXRhc2V0JGJheWVyc19tZXMgLSBkYXRhc2V0JGJheWVyc19lcGkKICBpZiAoaWR4ID09IDEgKSB7CiAgICB4ZW5vID0gZGF0YXNldAogIH0gCiAgaWYgKGlkeD09MikgewogICAgbHVuZyA9IGRhdGFzZXQKICB9CiAgaWR4ID0gaWR4KzEKfQoKeF9uYW1lcyA8LWVtdF9wYXRod2F5cyA8LSBjKCJoYWxsbWFya19lbXQiLCJHU0UxNzcwOF83MkhfVVAiLCJiYXllcnNfRU1UIiwiVGFnbGlfZW10IiwiVGFnbGlfaEVNVCIpCnhfbmFtZXNbMV0gPSAiSGFsbG1hcmsgXG4gRU1UIgp4X25hbWVzWzJdID0gIkdTRTE3NzA4XG43MkhfVVAiCgoKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xNH0KcDEgPSBEb3RQbG90LjIob2JqZWN0ID0geGVubywgZmVhdHVyZXMgPSAgZW10X3BhdGh3YXlzLGdyb3VwLmJ5ICA9ICd0cmVhdG1lbnQnLHNjYWxlID0gVCxzY2FsZS5ieSA9ICJzaXplIikrCiAgZ3VpZGVzKHNpemUgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiQ2VsbHMgZXhwcmVzc2luZyAoJSkiKSxjb2xvciA9IGd1aWRlX2NvbG9yYmFyKHRpdGxlID0gIkF2ZXJhZ2UgU2NvcmUiKSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KCBzaXplID0gMTApKSsgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9IHhfbmFtZXMpK2dndGl0bGUoIk1vZGVscyIpKyBzY2FsZV9zaXplX2Jpbm5lZCgpCgoKcDIgPSBEb3RQbG90LjIob2JqZWN0ID0gbHVuZywgZmVhdHVyZXMgPSAgZW10X3BhdGh3YXlzLGdyb3VwLmJ5ICA9ICd0aW1lLnBvaW50JyxzY2FsZSA9IFQsc2NhbGUuYnkgPSAic2l6ZSIpKwogIGd1aWRlcyhzaXplID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIkNlbGxzIGV4cHJlc3NpbmcgKCUpIiksY29sb3IgPSBndWlkZV9jb2xvcmJhcih0aXRsZSA9ICJBdmVyYWdlIFNjb3JlIikpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dCggc2l6ZSA9IDEwKSkrIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPSB4X25hbWVzKStnZ3RpdGxlKCJQYXRpZW50cyIpKyBzY2FsZV9zaXplX2Jpbm5lZCgpCnAxICsgcDIKYGBgCgpgYGB7cn0KYWxsX3BhdGh3YXlzID0gbGlzdCgpCmZvciAocGF0aHdheSBpbiBlbXRfcGF0aHdheXMpIHsKICBkYXRhID0gRmV0Y2hEYXRhKG9iamVjdCA9IGx1bmcsdmFycyA9IGMocGF0aHdheSwgInRpbWUucG9pbnQiLCAicGF0aWVudC5pZGVudCIpKQogIGFsbF9wYXRpZW50cyA9IGxpc3QoKQogIGZvciAocGF0aWVudCBpbiB1bmlxdWUobHVuZyRwYXRpZW50LmlkZW50KSkgewogICAgbWVhbjEgPSBkYXRhICU+JSBmaWx0ZXIocGF0aWVudC5pZGVudCA9PSBwYXRpZW50LCB0aW1lLnBvaW50ID09ICJwcmUtdHJlYXRtZW50IikgJT4lIHB1bGwoMSkgJT4lIG1lYW4oKQogICAgbWVhbjIgPSBkYXRhICU+JSBmaWx0ZXIocGF0aWVudC5pZGVudCA9PSBwYXRpZW50LCB0aW1lLnBvaW50ID09ICJvbi10cmVhdG1lbnQiKSAlPiUgcHVsbCgxKSAlPiUgbWVhbigpCiAgICBmYyA9IG1lYW4xIC8gbWVhbjIKICAgIGFsbF9wYXRpZW50c1tbcGF0aWVudF1dID0gZmMKICB9CiAgYWxsX3BhdGh3YXlzW1twYXRod2F5XV0gPSBhbGxfcGF0aWVudHMKfQoKCm1hdCA9IGFzLmRhdGEuZnJhbWUobGFwcGx5KGFsbF9wYXRod2F5cywgdW5saXN0KSkKbWF0ID0gbG9nMih0KG1hdCkgJT4lIGFzLmRhdGEuZnJhbWUoKSkKYnJlYWtzIDwtIGMoc2VxKC0xLDEsYnk9MC4xKSkKY29sb3JzX2Zvcl9wbG90IDwtIGNvbG9yUmFtcFBhbGV0dGUoY29sb3JzID0gYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkobiA9IGxlbmd0aChicmVha3MpKQoKcGhlYXRtYXA6OnBoZWF0bWFwKG1hdCxjb2xvciA9IGNvbG9yc19mb3JfcGxvdCxicmVha3MgPSBicmVha3MsZGlzcGxheV9udW1iZXJzID0gVCxtYWluID0gImxvZzIoRkMpIHByZS9vbiIpCmBgYAoKCgoKPHNjcmlwdCBzcmM9Imh0dHBzOi8vaHlwb3RoZXMuaXMvZW1iZWQuanMiIGFzeW5jPjwvc2NyaXB0PgoK