Functions
Data
genesets <- msigdb_download("Homo sapiens",category="H") %>% append( msigdb_download("Homo sapiens",category="C2",subcategory = "CP:KEGG"))
xeno_bulk = read.table(
file = "./Data/osiRoxa_bulk/Oct23/gene_fpkm.xls",
sep = "\t",
header = TRUE
)
rownames(xeno_bulk) = make.unique(xeno_bulk[,"gene_name",drop=T])
xeno_bulk = xeno_bulk[,26:37]
cell.labels = names(xeno_bulk)
condition = str_extract(cell.labels, "osi|combo|ctrl|roxa")
metadata = data.frame(condition = condition, row.names = colnames(xeno_bulk))
library(DESeq2)
dds <- DESeqDataSetFromMatrix(countData = round(xeno_bulk),
colData = metadata,
design = ~condition)
converting counts to integer mode
Warning in DESeqDataSet(se, design = design, ignoreRank) :
some variables in design formula are characters, converting to factors
PCA
nrow(dds)
[1] 32780
dds1 <- dds[ rowSums(counts(dds)) >= 3, ]
nrow(dds1)
[1] 9259
vst = vst(dds, blind=FALSE)
library("ggfortify")
PCAdata <- prcomp(t(assay(vst)))
autoplot(PCAdata, data = metadata,colour = "condition",label = FALSE, main="PCA") # Show dots

DESeq
dds <- DESeq(dds)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
DEG
cpVSop <- results(dds,contrast = c("condition","combo","osi")) %>% as.data.frame()
roxaVSctrl <- results(dds,contrast = c("condition","roxa","ctrl")) %>% as.data.frame()
diff_genes = data.frame(row.names = rownames(cpVSop), cpVSop_FC = cpVSop$log2FoldChange,roxaVSctrl_FC = roxaVSctrl$log2FoldChange, cpVSop_padj = cpVSop$padj)
library(DT)
combo_vs_ctrl = results(dds,contrast = c("condition","combo","ctrl")) %>% as.data.frame()
roxa_vs_ctrl = results(dds,contrast = c("condition","roxa","ctrl")) %>% as.data.frame()
osi_vs_ctrl = results(dds,contrast = c("condition","osi","ctrl")) %>% as.data.frame()
combo_vs_roxa = results(dds,contrast = c("condition","combo","roxa")) %>% as.data.frame()
osi_vs_roxa = results(dds,contrast = c("condition","osi","roxa")) %>% as.data.frame()
combo_vs_osi <- results(dds,contrast = c("condition","combo","osi")) %>% as.data.frame()
dt1 = datatable(roxa_vs_ctrl %>% filter(padj <0.05),caption = "significant roxa VS ctrl")
dt2= datatable(combo_vs_ctrl %>% filter(padj <0.05),caption = "significant combo vs ctrl")
dt3 = datatable(osi_vs_ctrl%>% filter(padj <0.05),caption = "significant osi_vs_ctrl")
dt4 =datatable(combo_vs_roxa %>% filter(padj <0.05),caption = "significant combo_vs_roxa")
dt5 =datatable(osi_vs_roxa %>% filter(padj <0.05),caption = "significant osi_vs_roxa")
dt6 =datatable(combo_vs_osi %>% filter(padj <0.05),caption = "significant combo VS osi")
print_tab(plt = dt1,title = "significant roxa VS ctrl")
## significant roxa VS ctrl {.unnumbered }
print_tab(plt = dt2,title = "significant combo vs ctrl")
## significant combo vs ctrl {.unnumbered }
print_tab(plt = dt3,title = "significant osi VS ctrl")
## significant osi VS ctrl {.unnumbered }
print_tab(plt = dt4,title = "significant combo VS roxa")
## significant combo VS roxa {.unnumbered }
print_tab(plt = dt5,title = "significant osi VS roxa")
## significant osi VS roxa {.unnumbered }
print_tab(plt = dt6,title = "significant combo VS osi")
## significant combo VS osi {.unnumbered }
NA

GSEA
plot_hyper <- function(genes_df,ident.1,ident.2) {
genes_df = cpVSop[order(genes_df$log2FoldChange, genes_df$padj, decreasing = T), ] #order by FC, ties bt padj
ranked_vec = genes_df[, "log2FoldChange"] %>% setNames(rownames(genes_df)) %>% na.omit() # make named vector
hyp_obj <- hypeR_fgsea(ranked_vec, genesets, up_only = F)
plt = hyp_dots(hyp_obj, merge = F)
plt1 = plt$up + aes(size = nes) + ggtitle(paste("up in" ,ident.1))
plt2 = plt$dn + aes(size = abs(nes)) + ggtitle(paste("up in" ,ident.2))
print_tab(plt1 + plt2, title = "title")
}
plot_hyper(combo_vs_ctrl,ident.1 = "combo",ident.2 = "ctrl")
Warning in preparePathwaysAndStats(pathways, stats, minSize, maxSize,
gseaParam, : There are ties in the preranked stats (15.25% of the list).
The order of those tied genes will be arbitrary, which may produce
unexpected results. ## title {.unnumbered }

plot_hyper(osi_vs_ctrl,ident.1 = "osi",ident.2 = "ctrl")
Warning in preparePathwaysAndStats(pathways, stats, minSize, maxSize,
gseaParam, : There are ties in the preranked stats (15.25% of the list).
The order of those tied genes will be arbitrary, which may produce
unexpected results. ## title {.unnumbered }

plot_hyper(combo_vs_roxa,ident.1 = "combo",ident.2 = "roxa")
Warning in preparePathwaysAndStats(pathways, stats, minSize, maxSize,
gseaParam, : There are ties in the preranked stats (15.25% of the list).
The order of those tied genes will be arbitrary, which may produce
unexpected results. ## title {.unnumbered }

plot_hyper(osi_vs_roxa,ident.1 = "osi",ident.2 = "roxa")
Warning in preparePathwaysAndStats(pathways, stats, minSize, maxSize,
gseaParam, : There are ties in the preranked stats (15.25% of the list).
The order of those tied genes will be arbitrary, which may produce
unexpected results. ## title {.unnumbered }

NA
HALLMARK_HYPOXIA genes in combo_vs_ctrl_genes
genesets$HALLMARK_HYPOXIA[genesets$HALLMARK_HYPOXIA %in% combo_vs_ctrl_genes]
[1] "BGN" "FOSL2" "GPC1" "KLHL24" "NDST1" "S100A4"
DEG
shrinked FC
dds$condition = relevel(dds$condition, ref = "osi")
dds <- nbinomWaldTest(dds)
cpVSop <- lfcShrink(dds,coef = "condition_combo_vs_osi") %>% as.data.frame()
dds$condition = relevel(dds$condition, ref = "ctrl")
dds <- nbinomWaldTest(dds)
roxaVSctrl <- lfcShrink(dds,coef = "condition_roxa_vs_ctrl") %>% as.data.frame()
diff_genes = data.frame(row.names = rownames(cpVSop), cpVSop_FC = cpVSop$log2FoldChange,roxaVSctrl_FC = roxaVSctrl$log2FoldChange, cpVSop_padj = cpVSop$padj)
ranked_vec = diff_genes[, 1] %>% setNames(rownames(diff_genes)) %>% sort(decreasing = TRUE)
hyp_obj <- hypeR_fgsea(ranked_vec, genesets, up_only = F)
plt = hyp_dots(hyp_obj,merge = F)
plt1 = plt$up+ aes(size=nes)+ggtitle("up in comboPersistor") + theme( axis.text.y = element_text(size=10))
plt2 = plt$dn+ aes(size=abs(nes))+ggtitle("up in osiPersistors") + theme(axis.text.y = element_text(size=10))
print_tab(plt1+plt2,title = "cpVSop")
ranked_vec = diff_genes[, 2] %>% setNames(rownames(diff_genes)) %>% sort(decreasing = TRUE)
hyp_obj <- hypeR_fgsea(ranked_vec, genesets, up_only = F)
plt = hyp_dots(hyp_obj,merge = F)
plt1 = plt$up+ aes(size=nes)+ggtitle("up in roxa")
plt2 = plt$dn+ aes(size=abs(nes))+ggtitle("up in ctrl")
print_tab(plt1+plt2,title = "cpVSop")
DEG in
comboVSosi but not in roxaVSctrl
cpVSop <- results(dds,contrast = c("condition","combo","osi")) %>% as.data.frame()
roxaVSctrl <- results(dds,contrast = c("condition","roxa","ctrl")) %>% as.data.frame()
diff_genes = data.frame(row.names = rownames(cpVSop), cpVSop_FC = 2**cpVSop$log2FoldChange,roxaVSctrl_FC = 2**roxaVSctrl$log2FoldChange, cpVSop_padj = cpVSop$padj)
up_genes_df = diff_genes %>% filter(cpVSop_FC > 2 & roxaVSctrl_FC<1.2 & cpVSop_padj<0.05)
down_genes_df = diff_genes %>% filter(cpVSop_FC < 0.5 & roxaVSctrl_FC>0.8 & cpVSop_padj<0.05)
up_genes = diff_genes %>% filter(cpVSop_FC > 2 & roxaVSctrl_FC<1.2 & cpVSop_padj<0.05) %>% rownames()
down_genes = diff_genes %>% filter(cpVSop_FC < 0.5 & roxaVSctrl_FC>0.8 & cpVSop_padj<0.1)%>% rownames()
print_tab(up_genes_df,title = "up")
print_tab(down_genes_df,title = "down")
cpVSop <- results(dds,contrast = c("condition","roxa","osi")) %>% as.data.frame() %>% filter(padj<0.05)
LS0tCnRpdGxlOiAnYHIgcnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLHJlcGxhY2VtZW50ID0gIiIpYCcgCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19jb2xsYXBzZTogeWVzCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvY19kZXB0aDogMQotLS0KCgoKIyBGdW5jdGlvbnMKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmBgYAoKIyBEYXRhCgpgYGB7cn0KZ2VuZXNldHMgPC0gbXNpZ2RiX2Rvd25sb2FkKCJIb21vIHNhcGllbnMiLGNhdGVnb3J5PSJIIikgJT4lIGFwcGVuZCggbXNpZ2RiX2Rvd25sb2FkKCJIb21vIHNhcGllbnMiLGNhdGVnb3J5PSJDMiIsc3ViY2F0ZWdvcnkgPSAiQ1A6S0VHRyIpKQp4ZW5vX2J1bGsgPSByZWFkLnRhYmxlKAogIGZpbGUgPSAiLi9EYXRhL29zaVJveGFfYnVsay9PY3QyMy9nZW5lX2Zwa20ueGxzIiwKICBzZXAgPSAiXHQiLAogIGhlYWRlciA9IFRSVUUKKQpyb3duYW1lcyh4ZW5vX2J1bGspID0gbWFrZS51bmlxdWUoeGVub19idWxrWywiZ2VuZV9uYW1lIixkcm9wPVRdKQp4ZW5vX2J1bGsgPSB4ZW5vX2J1bGtbLDI2OjM3XQoKYGBgCgpgYGB7cn0KY2VsbC5sYWJlbHMgPSBuYW1lcyh4ZW5vX2J1bGspCmNvbmRpdGlvbiA9IHN0cl9leHRyYWN0KGNlbGwubGFiZWxzLCAib3NpfGNvbWJvfGN0cmx8cm94YSIpCm1ldGFkYXRhID0gZGF0YS5mcmFtZShjb25kaXRpb24gPSBjb25kaXRpb24sIHJvdy5uYW1lcyA9IGNvbG5hbWVzKHhlbm9fYnVsaykpCmBgYAoKYGBge3J9CmxpYnJhcnkoREVTZXEyKQpkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSByb3VuZCh4ZW5vX2J1bGspLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gbWV0YWRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH5jb25kaXRpb24pCmBgYAoKCiMgUENBCmBgYHtyfQpucm93KGRkcykKZGRzMSA8LSBkZHNbIHJvd1N1bXMoY291bnRzKGRkcykpID49IDMsIF0KbnJvdyhkZHMxKQpgYGAKCmBgYHtyfQp2c3QgPSB2c3QoZGRzLCBibGluZD1GQUxTRSkKYGBgCgpgYGB7cn0KbGlicmFyeSgiZ2dmb3J0aWZ5IikKUENBZGF0YSA8LSBwcmNvbXAodChhc3NheSh2c3QpKSkKYXV0b3Bsb3QoUENBZGF0YSwgZGF0YSA9IG1ldGFkYXRhLGNvbG91ciA9ICJjb25kaXRpb24iLGxhYmVsID0gRkFMU0UsIG1haW49IlBDQSIpICMgU2hvdyBkb3RzCgpgYGAKIyBERVNlcQpgYGB7cn0KZGRzIDwtIERFU2VxKGRkcykKYGBgCgojIERFRyB7LnRhYnNldH0KYGBge3J9CmNwVlNvcCA8LSByZXN1bHRzKGRkcyxjb250cmFzdCA9IGMoImNvbmRpdGlvbiIsImNvbWJvIiwib3NpIikpICAlPiUgYXMuZGF0YS5mcmFtZSgpCnJveGFWU2N0cmwgPC0gcmVzdWx0cyhkZHMsY29udHJhc3QgPSBjKCJjb25kaXRpb24iLCJyb3hhIiwiY3RybCIpKSAgJT4lIGFzLmRhdGEuZnJhbWUoKQpkaWZmX2dlbmVzID0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSByb3duYW1lcyhjcFZTb3ApLCBjcFZTb3BfRkMgPSBjcFZTb3AkbG9nMkZvbGRDaGFuZ2Uscm94YVZTY3RybF9GQyA9IHJveGFWU2N0cmwkbG9nMkZvbGRDaGFuZ2UsICBjcFZTb3BfcGFkaiA9IGNwVlNvcCRwYWRqKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KERUKQpjb21ib192c19jdHJsID0gcmVzdWx0cyhkZHMsY29udHJhc3QgPSBjKCJjb25kaXRpb24iLCJjb21ibyIsImN0cmwiKSkgICU+JSBhcy5kYXRhLmZyYW1lKCkKcm94YV92c19jdHJsID0gcmVzdWx0cyhkZHMsY29udHJhc3QgPSBjKCJjb25kaXRpb24iLCJyb3hhIiwiY3RybCIpKSAgJT4lIGFzLmRhdGEuZnJhbWUoKQpvc2lfdnNfY3RybCA9IHJlc3VsdHMoZGRzLGNvbnRyYXN0ID0gYygiY29uZGl0aW9uIiwib3NpIiwiY3RybCIpKSAgJT4lIGFzLmRhdGEuZnJhbWUoKQoKY29tYm9fdnNfcm94YSA9IHJlc3VsdHMoZGRzLGNvbnRyYXN0ID0gYygiY29uZGl0aW9uIiwiY29tYm8iLCJyb3hhIikpICAlPiUgYXMuZGF0YS5mcmFtZSgpCm9zaV92c19yb3hhID0gcmVzdWx0cyhkZHMsY29udHJhc3QgPSBjKCJjb25kaXRpb24iLCJvc2kiLCJyb3hhIikpICAlPiUgYXMuZGF0YS5mcmFtZSgpCmNvbWJvX3ZzX29zaSA8LSByZXN1bHRzKGRkcyxjb250cmFzdCA9IGMoImNvbmRpdGlvbiIsImNvbWJvIiwib3NpIikpICAlPiUgYXMuZGF0YS5mcmFtZSgpCgpkdDEgPSBkYXRhdGFibGUocm94YV92c19jdHJsICU+JSBmaWx0ZXIocGFkaiA8MC4wNSksY2FwdGlvbiA9ICJzaWduaWZpY2FudCByb3hhIFZTIGN0cmwiKQpkdDI9IGRhdGF0YWJsZShjb21ib192c19jdHJsICU+JSBmaWx0ZXIocGFkaiA8MC4wNSksY2FwdGlvbiA9ICJzaWduaWZpY2FudCBjb21ibyB2cyBjdHJsIikKZHQzID0gZGF0YXRhYmxlKG9zaV92c19jdHJsJT4lIGZpbHRlcihwYWRqIDwwLjA1KSxjYXB0aW9uID0gInNpZ25pZmljYW50IG9zaV92c19jdHJsIikKZHQ0ID1kYXRhdGFibGUoY29tYm9fdnNfcm94YSAlPiUgZmlsdGVyKHBhZGogPDAuMDUpLGNhcHRpb24gPSAic2lnbmlmaWNhbnQgY29tYm9fdnNfcm94YSIpCmR0NSA9ZGF0YXRhYmxlKG9zaV92c19yb3hhICU+JSBmaWx0ZXIocGFkaiA8MC4wNSksY2FwdGlvbiA9ICJzaWduaWZpY2FudCBvc2lfdnNfcm94YSIpCmR0NiA9ZGF0YXRhYmxlKGNvbWJvX3ZzX29zaSAlPiUgZmlsdGVyKHBhZGogPDAuMDUpLGNhcHRpb24gPSAic2lnbmlmaWNhbnQgY29tYm8gVlMgb3NpIikKcHJpbnRfdGFiKHBsdCA9IGR0MSx0aXRsZSA9ICJzaWduaWZpY2FudCByb3hhIFZTIGN0cmwiKQpwcmludF90YWIocGx0ID0gZHQyLHRpdGxlID0gInNpZ25pZmljYW50IGNvbWJvIHZzIGN0cmwiKQpwcmludF90YWIocGx0ID0gZHQzLHRpdGxlID0gInNpZ25pZmljYW50IG9zaSBWUyBjdHJsIikKcHJpbnRfdGFiKHBsdCA9IGR0NCx0aXRsZSA9ICJzaWduaWZpY2FudCBjb21ibyBWUyByb3hhIikKcHJpbnRfdGFiKHBsdCA9IGR0NSx0aXRsZSA9ICJzaWduaWZpY2FudCBvc2kgVlMgcm94YSIpCnByaW50X3RhYihwbHQgPSBkdDYsdGl0bGUgPSAic2lnbmlmaWNhbnQgY29tYm8gVlMgb3NpIikKCmBgYAoKYGBge3IgZmlnLndpZHRoPTEwfQoKb3NpVlNjdHJsX2dlbmVzIDwtIHJlc3VsdHMoZGRzLGNvbnRyYXN0ID0gYygiY29uZGl0aW9uIiwib3NpIiwiY3RybCIpKSAgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgZmlsdGVyKGxvZzJGb2xkQ2hhbmdlPjAgJiBwYWRqIDwgMC4wNSkgJT4lIHJvd25hbWVzKCkKY29tYm9fdnNfY3RybF9nZW5lcyA9IGNvbWJvX3ZzX2N0cmwgJT4lIGZpbHRlcihsb2cyRm9sZENoYW5nZT4wICYgcGFkaiA8IDAuMDUpJT4lIHJvd25hbWVzKCkKCmxpYnJhcnkoZ2d2ZW5uKQpkZWdfZ2VuZXMgPSBsaXN0KG9zaVZTY3RybF9nZW5lcyA9IG9zaVZTY3RybF9nZW5lcywgY29tYm9fdnNfY3RybF9nZW5lcyA9IGNvbWJvX3ZzX2N0cmxfZ2VuZXMpCmdndmVubihkZWdfZ2VuZXMsYXV0b19zY2FsZSA9IFQpCmBgYAojIEdTRUEKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTMsIHJlc3VsdHM9J2FzaXMnfQpwbG90X2h5cGVyIDwtIGZ1bmN0aW9uKGdlbmVzX2RmLGlkZW50LjEsaWRlbnQuMikgewogIGdlbmVzX2RmID0gY3BWU29wW29yZGVyKGdlbmVzX2RmJGxvZzJGb2xkQ2hhbmdlLCBnZW5lc19kZiRwYWRqLCBkZWNyZWFzaW5nID0gVCksIF0gI29yZGVyIGJ5IEZDLCB0aWVzIGJ0IHBhZGoKICByYW5rZWRfdmVjID0gZ2VuZXNfZGZbLCAibG9nMkZvbGRDaGFuZ2UiXSAlPiUgc2V0TmFtZXMocm93bmFtZXMoZ2VuZXNfZGYpKSAlPiUgbmEub21pdCgpICMgbWFrZSBuYW1lZCB2ZWN0b3IKICAKICBoeXBfb2JqIDwtIGh5cGVSX2Znc2VhKHJhbmtlZF92ZWMsIGdlbmVzZXRzLCB1cF9vbmx5ID0gRikKICBwbHQgPSBoeXBfZG90cyhoeXBfb2JqLCBtZXJnZSA9IEYpCiAgcGx0MSA9IHBsdCR1cCArIGFlcyhzaXplID0gbmVzKSArIGdndGl0bGUocGFzdGUoInVwIGluIiAsaWRlbnQuMSkpCiAgcGx0MiA9IHBsdCRkbiArIGFlcyhzaXplID0gYWJzKG5lcykpICsgZ2d0aXRsZShwYXN0ZSgidXAgaW4iICxpZGVudC4yKSkKICBwcmludF90YWIocGx0MSArIHBsdDIsIHRpdGxlID0gInRpdGxlIikKfQoKcGxvdF9oeXBlcihjb21ib192c19jdHJsLGlkZW50LjEgPSAiY29tYm8iLGlkZW50LjIgPSAiY3RybCIpCnBsb3RfaHlwZXIob3NpX3ZzX2N0cmwsaWRlbnQuMSA9ICJvc2kiLGlkZW50LjIgPSAiY3RybCIpCnBsb3RfaHlwZXIoY29tYm9fdnNfcm94YSxpZGVudC4xID0gImNvbWJvIixpZGVudC4yID0gInJveGEiKQpwbG90X2h5cGVyKG9zaV92c19yb3hhLGlkZW50LjEgPSAib3NpIixpZGVudC4yID0gInJveGEiKQoKYGBgCkhBTExNQVJLX0hZUE9YSUEgZ2VuZXMgaW4gY29tYm9fdnNfY3RybF9nZW5lcwpgYGB7cn0KZ2VuZXNldHMkSEFMTE1BUktfSFlQT1hJQVtnZW5lc2V0cyRIQUxMTUFSS19IWVBPWElBICVpbiUgY29tYm9fdnNfY3RybF9nZW5lc10gCmBgYAoKIyBERUcgc2hyaW5rZWQgRkMgey50YWJzZXR9CmBgYHtyfQpkZHMkY29uZGl0aW9uID0gcmVsZXZlbChkZHMkY29uZGl0aW9uLCByZWYgPSAib3NpIikKZGRzIDwtIG5iaW5vbVdhbGRUZXN0KGRkcykKY3BWU29wIDwtIGxmY1NocmluayhkZHMsY29lZiA9ICJjb25kaXRpb25fY29tYm9fdnNfb3NpIikgICU+JSBhcy5kYXRhLmZyYW1lKCkKCmRkcyRjb25kaXRpb24gPSByZWxldmVsKGRkcyRjb25kaXRpb24sIHJlZiA9ICJjdHJsIikKZGRzIDwtIG5iaW5vbVdhbGRUZXN0KGRkcykKcm94YVZTY3RybCA8LSBsZmNTaHJpbmsoZGRzLGNvZWYgID0gImNvbmRpdGlvbl9yb3hhX3ZzX2N0cmwiKSAgJT4lIGFzLmRhdGEuZnJhbWUoKQoKCmRpZmZfZ2VuZXMgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IHJvd25hbWVzKGNwVlNvcCksIGNwVlNvcF9GQyA9IGNwVlNvcCRsb2cyRm9sZENoYW5nZSxyb3hhVlNjdHJsX0ZDID0gcm94YVZTY3RybCRsb2cyRm9sZENoYW5nZSwgIGNwVlNvcF9wYWRqID0gY3BWU29wJHBhZGopCgpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMyxyZXN1bHRzPSdhc2lzJ30KcmFua2VkX3ZlYyA9IGRpZmZfZ2VuZXNbLCAxXSAlPiUgc2V0TmFtZXMocm93bmFtZXMoZGlmZl9nZW5lcykpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUUlVFKQpoeXBfb2JqIDwtIGh5cGVSX2Znc2VhKHJhbmtlZF92ZWMsIGdlbmVzZXRzLCB1cF9vbmx5ID0gRikKcGx0ID0gaHlwX2RvdHMoaHlwX29iaixtZXJnZSA9IEYpCnBsdDEgPSBwbHQkdXArIGFlcyhzaXplPW5lcykrZ2d0aXRsZSgidXAgaW4gY29tYm9QZXJzaXN0b3IiKSArIHRoZW1lKCAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpCnBsdDIgPSBwbHQkZG4rIGFlcyhzaXplPWFicyhuZXMpKStnZ3RpdGxlKCJ1cCBpbiBvc2lQZXJzaXN0b3JzIikgKyB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkKcHJpbnRfdGFiKHBsdDErcGx0Mix0aXRsZSA9ICJjcFZTb3AiKQoKcmFua2VkX3ZlYyA9IGRpZmZfZ2VuZXNbLCAyXSAlPiUgc2V0TmFtZXMocm93bmFtZXMoZGlmZl9nZW5lcykpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUUlVFKQpoeXBfb2JqIDwtIGh5cGVSX2Znc2VhKHJhbmtlZF92ZWMsIGdlbmVzZXRzLCB1cF9vbmx5ID0gRikKcGx0ID0gaHlwX2RvdHMoaHlwX29iaixtZXJnZSA9IEYpCnBsdDEgPSBwbHQkdXArIGFlcyhzaXplPW5lcykrZ2d0aXRsZSgidXAgaW4gcm94YSIpCnBsdDIgPSBwbHQkZG4rIGFlcyhzaXplPWFicyhuZXMpKStnZ3RpdGxlKCJ1cCBpbiBjdHJsIikKcHJpbnRfdGFiKHBsdDErcGx0Mix0aXRsZSA9ICJjcFZTb3AiKQoKYGBgCgojIERFRyBpbiBjb21ib1ZTb3NpIGJ1dCBub3QgaW4gcm94YVZTY3RybCB7LnRhYnNldH0KCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQpjcFZTb3AgPC0gcmVzdWx0cyhkZHMsY29udHJhc3QgPSBjKCJjb25kaXRpb24iLCJjb21ibyIsIm9zaSIpKSAgJT4lIGFzLmRhdGEuZnJhbWUoKQpyb3hhVlNjdHJsIDwtIHJlc3VsdHMoZGRzLGNvbnRyYXN0ID0gYygiY29uZGl0aW9uIiwicm94YSIsImN0cmwiKSkgICU+JSBhcy5kYXRhLmZyYW1lKCkKZGlmZl9nZW5lcyA9IGRhdGEuZnJhbWUocm93Lm5hbWVzID0gcm93bmFtZXMoY3BWU29wKSwgY3BWU29wX0ZDID0gMioqY3BWU29wJGxvZzJGb2xkQ2hhbmdlLHJveGFWU2N0cmxfRkMgPSAyKipyb3hhVlNjdHJsJGxvZzJGb2xkQ2hhbmdlLCAgY3BWU29wX3BhZGogPSBjcFZTb3AkcGFkaikKdXBfZ2VuZXNfZGYgPSAgZGlmZl9nZW5lcyAlPiUgZmlsdGVyKGNwVlNvcF9GQyA+IDIgJiByb3hhVlNjdHJsX0ZDPDEuMiAmIGNwVlNvcF9wYWRqPDAuMDUpIApkb3duX2dlbmVzX2RmID0gZGlmZl9nZW5lcyAlPiUgZmlsdGVyKGNwVlNvcF9GQyA8IDAuNSAmIHJveGFWU2N0cmxfRkM+MC44ICYgY3BWU29wX3BhZGo8MC4wNSkKdXBfZ2VuZXMgPSBkaWZmX2dlbmVzICU+JSBmaWx0ZXIoY3BWU29wX0ZDID4gMiAmIHJveGFWU2N0cmxfRkM8MS4yICYgY3BWU29wX3BhZGo8MC4wNSkgJT4lIHJvd25hbWVzKCkKZG93bl9nZW5lcyA9IGRpZmZfZ2VuZXMgJT4lIGZpbHRlcihjcFZTb3BfRkMgPCAwLjUgJiByb3hhVlNjdHJsX0ZDPjAuOCAmIGNwVlNvcF9wYWRqPDAuMSklPiUgcm93bmFtZXMoKQoKcHJpbnRfdGFiKHVwX2dlbmVzX2RmLHRpdGxlID0gInVwIikKcHJpbnRfdGFiKGRvd25fZ2VuZXNfZGYsdGl0bGUgPSAiZG93biIpCmBgYApgYGB7cn0KY3BWU29wIDwtIHJlc3VsdHMoZGRzLGNvbnRyYXN0ID0gYygiY29uZGl0aW9uIiwicm94YSIsIm9zaSIpKSAgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgZmlsdGVyKHBhZGo8MC4wNSkKYGBgCgo8c2NyaXB0IHNyYz0iaHR0cHM6Ly9oeXBvdGhlcy5pcy9lbWJlZC5qcyIgYXN5bmM+PC9zY3JpcHQ+Cgo=