Functions
Data
genesets_cp <- msigdb_download("Homo sapiens",category="C2",subcategory = "CP:BIOCARTA ") %>% append( msigdb_download("Homo sapiens",category="C2",subcategory = "CP:KEGG"))
Error in h(simpleError(msg, call)) :
error in evaluating the argument 'x' in selecting a method for function 'append': unknown subcategory
cell.labels = names(H1975Oct23)
condition = str_extract(cell.labels, "osiPersistors|comboPersistors|osi|ctrl|roxa")
metadata = data.frame(condition = condition, row.names = colnames(H1975Oct23))
library(DESeq2)
dds <- DESeqDataSetFromMatrix(countData = round(H1975Oct23),
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] 9065
vst = vst(dds1, 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)
dds_H1975_OCT23 = dds
dds = dds_H1975_OCT23
Top variable genes
heatmap
genes <- head(order(rowVars(assay(dds)), decreasing = TRUE), 1000)
mat <- H1975Oct23[ genes, ]
mat <- t(scale(t(mat)))
anno <- as.data.frame(mat)
library(ComplexHeatmap)
library(ggplot2)
Heatmap(mat, cluster_rows = T, cluster_columns = F, column_labels = colnames(anno), name = "fpkm Z-score",row_names_gp = gpar(fontsize = 0))

DEG
FC
cpVSop <- results(dds,contrast = c("condition","comboPersistors","osiPersistors")) %>% as.data.frame()
roxaVSctrl <- results(dds,contrast = c("condition","roxa","ctrl")) %>% as.data.frame()
osiVSctrl <- results(dds,contrast = c("condition","osi","ctrl")) %>% as.data.frame()
diff_genes = data.frame(row.names = rownames(cpVSop), cpVSop_FC = cpVSop$log2FoldChange,roxaVSctrl_FC = roxaVSctrl$log2FoldChange, cpVSop_padj = cpVSop$padj)
cpVSop = cpVSop[order(cpVSop$log2FoldChange, cpVSop$padj,decreasing = T),] #order by FC, ties bt padj
ranked_vec = cpVSop[,"log2FoldChange"]%>% setNames(rownames(cpVSop)) %>% na.omit() # make named vector
hyp_obj <- hypeR_fgsea(ranked_vec, genesets, up_only = F)
Warning in preparePathwaysAndStats(pathways, stats, minSize, maxSize,
gseaParam, : There are ties in the preranked stats (10.89% of the list).
The order of those tied genes will be arbitrary, which may produce
unexpected results.
plt = hyp_dots(hyp_obj,merge = F)
plt1 = plt$up+ aes(size=nes)+ggtitle("up in comboPersistor")
plt2 = plt$dn+ aes(size=abs(nes))+ggtitle("up in osiPersistors")
print_tab(plt1+plt2,title = "cpVSop")
cpVSop

roxaVSctrl = roxaVSctrl[order(roxaVSctrl$log2FoldChange, roxaVSctrl$padj,decreasing = T),] #order by FC, ties bt padj
ranked_vec = roxaVSctrl[,"log2FoldChange"]%>% setNames(rownames(roxaVSctrl)) %>% na.omit() # make named vector
hyp_obj <- hypeR_fgsea(ranked_vec, genesets, up_only = F)
Warning in preparePathwaysAndStats(pathways, stats, minSize, maxSize,
gseaParam, : There are ties in the preranked stats (19.69% of the list).
The order of those tied genes will be arbitrary, which may produce
unexpected results.
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 = "roxa VS ctrl")
roxa VS ctrl

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

NA
CC upregulted in cp VS
op
osiVSctrl_genes <- results(dds,contrast = c("condition","osi","ctrl")) %>% as.data.frame() %>% filter(log2FoldChange>0 & padj < 0.05) %>% rownames()
cpVSctrl_genes <- results(dds,contrast = c("condition","comboPersistors","ctrl")) %>% as.data.frame() %>% filter(log2FoldChange>0 & padj < 0.05) %>% rownames()
roxaVSctrl_genes = roxaVSctrl %>% filter(log2FoldChange>0 & padj < 0.05)%>% rownames()
DEG
shrinked FC
dds$condition = relevel(dds$condition, ref = "osiPersistors")
dds <- nbinomWaldTest(dds)
cpVSop <- lfcShrink(dds,coef = "condition_comboPersistors_vs_osiPersistors") %>% 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","comboPersistors","osiPersistors")) %>% 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")
up
print_tab(down_genes_df,title = "down")
down
NA
H1975_up_genes = up_genes
H1975_down_genes = down_genes
hyp_obj <- hypeR(up_genes, genesets, test = "hypergeometric", fdr=1, plotting=F,background = rownames(H1975Oct23))
plt1 = hyp_dots(hyp_obj,title = "up in comboVSosi but not in roxaVSctrl")
hyp_obj <- hypeR(down_genes, genesets, test = "hypergeometric", fdr=1, plotting=F,background = rownames(H1975Oct23))
plt2 = hyp_dots(hyp_obj,title = "down in comboVSosi but not in roxaVSctrl")
plt1 + plt2

print_tab(data.frame(up_genes[up_genes %in% genesets$HALLMARK_E2F_TARGETS]),title = "up genes in E2F")
up genes in E2F
print_tab(data.frame(up_genes[up_genes %in% genesets$HALLMARK_HYPOXIA]),title = "up genes in Hypoxia")
Expression heatmap
# select the 50 most differentially expressed genes
genes <- c("DUSP6","MKI67")
mat <- H1975Oct23[ genes, ]
mat <- t(scale(t(mat)))
anno <- as.data.frame(mat)
library(ComplexHeatmap)
library(ggplot2)
p = Heatmap(mat, cluster_rows = F, cluster_columns = F, column_labels = colnames(anno), name = "fpkm Z-score")
print_tab(plt = p,title = "markers")
markers

genes <- hif_targets
mat <- H1975Oct23[genes, ] %>% filter(rowSums(across(where(is.numeric)))!=0)
mat <- t(scale(t(mat)))
anno <- as.data.frame(mat)
p = Heatmap(mat, cluster_rows = T, cluster_columns = F, column_labels = colnames(anno), name = "fpkm Z-score",column_title = "HIF targets",row_names_gp = gpar(fontsize = 8))
print_tab(plt = p,title = "HIF targets")
HIF targets

genes <- genesets$HALLMARK_HYPOXIA
mat <- H1975Oct23[genes, ] %>% filter(rowSums(across(where(is.numeric)))!=0)
mat <- t(scale(t(mat)))
anno <- as.data.frame(mat)
p = Heatmap(mat, cluster_rows = T, cluster_columns = F, column_labels = colnames(anno), name = "fpkm Z-score",column_title = "HALLMARK_HYPOXIA",row_names_gp =gpar(fontsize = 0))
print_tab(plt = p,title = "HALLMARK_HYPOXIA")
HALLMARK_HYPOXIA

NA
G2M heatmap
print_tab(plt = p,title = "HALLMARK_G2M_CHECKPOINT")
HALLMARK_G2M_CHECKPOINT

NA
Enrichment analysis
chosen_genes = split %>% dplyr::filter(cluster == 1) %>% rownames() #take relevant genes
hyp_obj <- hypeR(chosen_genes, genesets_wikipathways, test = "hypergeometric", fdr=1, plotting=F,background = rownames(H1975Oct23))
p1 = hyp_dots(hyp_obj,size_by = "none",title = "cluster 1")
chosen_genes = split %>% dplyr::filter(cluster == 4) %>% rownames() #take relevant genes
hyp_obj <- hypeR(chosen_genes, genesets_wikipathways, test = "hypergeometric", fdr=1, plotting=F,background = rownames(H1975Oct23))
p2 = hyp_dots(hyp_obj,size_by = "none",title = "cluster 4")
wiki_p = (p1/p2)
print_tab(plt = wiki_p,title = "wikipathways")
wikipathways

chosen_genes = split %>% dplyr::filter(cluster == 1) %>% rownames() #take relevant genes
hyp_obj <- hypeR(chosen_genes, genesets_biocarta, test = "hypergeometric", fdr=1, plotting=F,background = rownames(H1975Oct23))
p1 = hyp_dots(hyp_obj,size_by = "none",title = "cluster 1")
chosen_genes = split %>% dplyr::filter(cluster == 4) %>% rownames() #take relevant genes
hyp_obj <- hypeR(chosen_genes, genesets_biocarta, test = "hypergeometric", fdr=1, plotting=F,background = rownames(H1975Oct23))
p2 = hyp_dots(hyp_obj,size_by = "none",title = "cluster 4")
wiki_p = (p1/p2)
print_tab(plt = wiki_p,title = "biocarta")
biocarta

chosen_genes = split %>% dplyr::filter(cluster == 1) %>% rownames() #take relevant genes
hyp_obj <- hypeR(chosen_genes, genesets_pid, test = "hypergeometric", fdr=1, plotting=F,background = rownames(H1975Oct23))
p1 = hyp_dots(hyp_obj,size_by = "none",title = "cluster 1")
chosen_genes = split %>% dplyr::filter(cluster == 4) %>% rownames() #take relevant genes
hyp_obj <- hypeR(chosen_genes, genesets_pid, test = "hypergeometric", fdr=1, plotting=F,background = rownames(H1975Oct23))
p2 = hyp_dots(hyp_obj,size_by = "none",title = "cluster 4")
wiki_p = (p1/p2)
print_tab(plt = wiki_p,title = "pid")
pid

chosen_genes = split %>% dplyr::filter(cluster == 1) %>% rownames() #take relevant genes
hyp_obj <- hypeR(chosen_genes, genesets_REACTOME, test = "hypergeometric", fdr=1, plotting=F,background = rownames(H1975Oct23))
p1 = hyp_dots(hyp_obj,size_by = "none",title = "cluster 1")
chosen_genes = split %>% dplyr::filter(cluster == 4) %>% rownames() #take relevant genes
hyp_obj <- hypeR(chosen_genes, genesets_REACTOME, test = "hypergeometric", fdr=1, plotting=F,background = rownames(H1975Oct23))
p2 = hyp_dots(hyp_obj,size_by = "none",title = "cluster 4")
wiki_p = (p1/p2)
print_tab(plt = wiki_p,title = "REACTOME")
REACTOME

NA
for (chosen_clusters in 1:length(unique(split$cluster))) {
chosen_genes = split %>% dplyr::filter(cluster == chosen_clusters) %>% rownames() #take relevant genes
hyp_obj <- hypeR(chosen_genes, genesets_wikipathways, test = "hypergeometric", fdr=1, plotting=F,background = rownames(H1975Oct23))
print(hyp_dots(hyp_obj,size_by = "none"))
}
Distance plot
vsd <- vst(dds, blind=FALSE)
sampleDists <- dist(t(assay(vsd)))
library("RColorBrewer")
sampleDistMatrix <- as.matrix(sampleDists)
colnames(sampleDistMatrix) <- NULL
colors <- colorRampPalette( rev(brewer.pal(9, "Blues")) )(255)
pheatmap(sampleDistMatrix,
clustering_distance_rows=sampleDists,
clustering_distance_cols=sampleDists,
col=colors)

LS0tCnRpdGxlOiAnYHIgcnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLHJlcGxhY2VtZW50ID0gIiIpYCcgCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19jb2xsYXBzZTogeWVzCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvY19kZXB0aDogMQotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Ci5tYWluLWNvbnRhaW5lciB7CiAgbWF4LXdpZHRoOiA4NSUgIWltcG9ydGFudDsKICBtYXJnaW46IGF1dG87Cn0KPC9zdHlsZT4KCiMgRnVuY3Rpb25zCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpgYGAKCiMgRGF0YQoKCmBgYHtyfQpnZW5lc2V0cyA8LSBtc2lnZGJfZG93bmxvYWQoIkhvbW8gc2FwaWVucyIsY2F0ZWdvcnk9IkgiKSAlPiUgYXBwZW5kKCBtc2lnZGJfZG93bmxvYWQoIkhvbW8gc2FwaWVucyIsY2F0ZWdvcnk9IkMyIixzdWJjYXRlZ29yeSA9ICJDUDpLRUdHIikpCmdlbmVzZXRzX2Jpb2NhcnRhIDwtICBtc2lnZGJfZG93bmxvYWQoIkhvbW8gc2FwaWVucyIsY2F0ZWdvcnk9IkMyIixzdWJjYXRlZ29yeSA9ICJDUDpCSU9DQVJUQSIpCmdlbmVzZXRzX3BpZCA8LSAgbXNpZ2RiX2Rvd25sb2FkKCJIb21vIHNhcGllbnMiLGNhdGVnb3J5PSJDMiIsc3ViY2F0ZWdvcnkgPSAiQ1A6UElEIikKIGdlbmVzZXRzX1JFQUNUT01FIDwtIG1zaWdkYl9kb3dubG9hZCgiSG9tbyBzYXBpZW5zIixjYXRlZ29yeT0iQzIiLHN1YmNhdGVnb3J5ID0gIkNQOlJFQUNUT01FIikKZ2VuZXNldHNfd2lraXBhdGh3YXlzPC0gbXNpZ2RiX2Rvd25sb2FkKCJIb21vIHNhcGllbnMiLGNhdGVnb3J5PSJDMiIsc3ViY2F0ZWdvcnkgPSAiQ1A6V0lLSVBBVEhXQVlTIikKCmEgPSBtc2lnZGJfZG93bmxvYWQoIkhvbW8gc2FwaWVucyIsY2F0ZWdvcnk9IkMyIixzdWJjYXRlZ29yeSA9ICJDUCIpCkgxOTc1T2N0MjMgPSByZWFkLnRhYmxlKAogIGZpbGUgPSAiLi9EYXRhL29zaVJveGFfYnVsay9PY3QyMy9nZW5lX2Zwa20ueGxzIiwKICBzZXAgPSAiXHQiLAogIGhlYWRlciA9IFRSVUUKKQpyb3duYW1lcyhIMTk3NU9jdDIzKSA9IG1ha2UudW5pcXVlKEgxOTc1T2N0MjNbLCJnZW5lX25hbWUiLGRyb3A9VF0pCkgxOTc1T2N0MjMgPSBIMTk3NU9jdDIzWywyOjE2XQpuYW1lcyAoSDE5NzVPY3QyMykgPSBnc3ViKHggPSBuYW1lcyhIMTk3NU9jdDIzKSxwYXR0ZXJuID0gIl9DIixyZXBsYWNlbWVudCA9ICJfY3RybCIpJT4lIGdzdWIocGF0dGVybiA9ICJwX09SIixyZXBsYWNlbWVudCA9ICJfY29tYm9QZXJzaXN0b3JzIikgJT4lIGdzdWIocGF0dGVybiA9ICJwX08iLHJlcGxhY2VtZW50ID0gIl9vc2lQZXJzaXN0b3JzIikgJT4lIGdzdWIocGF0dGVybiA9ICJfUiIscmVwbGFjZW1lbnQgPSAiX3JveGEiKSU+JSBnc3ViKCxwYXR0ZXJuID0gIl9PIixyZXBsYWNlbWVudCA9ICJfb3NpIikKCmBgYAoKYGBge3J9CmNlbGwubGFiZWxzID0gbmFtZXMoSDE5NzVPY3QyMykKY29uZGl0aW9uID0gc3RyX2V4dHJhY3QoY2VsbC5sYWJlbHMsICJvc2lQZXJzaXN0b3JzfGNvbWJvUGVyc2lzdG9yc3xvc2l8Y3RybHxyb3hhIikKbWV0YWRhdGEgPSBkYXRhLmZyYW1lKGNvbmRpdGlvbiA9IGNvbmRpdGlvbiwgcm93Lm5hbWVzID0gY29sbmFtZXMoSDE5NzVPY3QyMykpCmBgYAoKYGBge3J9CmxpYnJhcnkoREVTZXEyKQpkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSByb3VuZChIMTk3NU9jdDIzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IG1ldGFkYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+Y29uZGl0aW9uKQpgYGAKCgojIFBDQQpgYGB7cn0KbnJvdyhkZHMpCmRkczEgPC0gZGRzWyByb3dTdW1zKGNvdW50cyhkZHMpKSA+PSAzLCBdCm5yb3coZGRzMSkKYGBgCgpgYGB7cn0KdnN0ID0gdnN0KGRkczEsIGJsaW5kPUZBTFNFKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KCJnZ2ZvcnRpZnkiKQpQQ0FkYXRhIDwtIHByY29tcCh0KGFzc2F5KHZzdCkpKQphdXRvcGxvdChQQ0FkYXRhLCBkYXRhID0gbWV0YWRhdGEsY29sb3VyID0gImNvbmRpdGlvbiIsbGFiZWwgPSBGQUxTRSwgbWFpbj0iUENBIikgIyBTaG93IGRvdHMKCmBgYAojIERFU2VxCmBgYHtyfQpkZHMgPC0gREVTZXEoZGRzKQpkZHNfSDE5NzVfT0NUMjMgPSBkZHMKYGBgCgpgYGB7cn0KZGRzID0gZGRzX0gxOTc1X09DVDIzCmBgYAoKCiMgVG9wIHZhcmlhYmxlIGdlbmVzIGhlYXRtYXAKYGBge3J9CmdlbmVzIDwtIGhlYWQob3JkZXIocm93VmFycyhhc3NheShkZHMpKSwgZGVjcmVhc2luZyA9IFRSVUUpLCAxMDAwKQoKbWF0IDwtIEgxOTc1T2N0MjNbIGdlbmVzLCBdCm1hdCA8LSB0KHNjYWxlKHQobWF0KSkpCmFubm8gPC0gYXMuZGF0YS5mcmFtZShtYXQpCgpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKSAKbGlicmFyeShnZ3Bsb3QyKSAKSGVhdG1hcChtYXQsIGNsdXN0ZXJfcm93cyA9IFQsIGNsdXN0ZXJfY29sdW1ucyA9IEYsIGNvbHVtbl9sYWJlbHMgPSBjb2xuYW1lcyhhbm5vKSwgbmFtZSA9ICJmcGttIFotc2NvcmUiLHJvd19uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAwKSkgCmBgYAoKIyBERUcgRkMgey50YWJzZXR9CmBgYHtyfQpjcFZTb3AgPC0gcmVzdWx0cyhkZHMsY29udHJhc3QgPSBjKCJjb25kaXRpb24iLCJjb21ib1BlcnNpc3RvcnMiLCJvc2lQZXJzaXN0b3JzIikpICAlPiUgYXMuZGF0YS5mcmFtZSgpCnJveGFWU2N0cmwgPC0gcmVzdWx0cyhkZHMsY29udHJhc3QgPSBjKCJjb25kaXRpb24iLCJyb3hhIiwiY3RybCIpKSAgJT4lIGFzLmRhdGEuZnJhbWUoKQpvc2lWU2N0cmwgPC0gcmVzdWx0cyhkZHMsY29udHJhc3QgPSBjKCJjb25kaXRpb24iLCJvc2kiLCJjdHJsIikpICAlPiUgYXMuZGF0YS5mcmFtZSgpCgpkaWZmX2dlbmVzID0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSByb3duYW1lcyhjcFZTb3ApLCBjcFZTb3BfRkMgPSBjcFZTb3AkbG9nMkZvbGRDaGFuZ2Uscm94YVZTY3RybF9GQyA9IHJveGFWU2N0cmwkbG9nMkZvbGRDaGFuZ2UsICBjcFZTb3BfcGFkaiA9IGNwVlNvcCRwYWRqKQpgYGAKCgpgYGB7ciBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMywgcmVzdWx0cz0nYXNpcyd9CmNwVlNvcCA9IGNwVlNvcFtvcmRlcihjcFZTb3AkbG9nMkZvbGRDaGFuZ2UsIGNwVlNvcCRwYWRqLGRlY3JlYXNpbmcgPSBUKSxdICNvcmRlciBieSBGQywgdGllcyBidCBwYWRqCnJhbmtlZF92ZWMgPSBjcFZTb3BbLCJsb2cyRm9sZENoYW5nZSJdJT4lIHNldE5hbWVzKHJvd25hbWVzKGNwVlNvcCkpICU+JSBuYS5vbWl0KCkgIyBtYWtlIG5hbWVkIHZlY3RvcgoKaHlwX29iaiA8LSBoeXBlUl9mZ3NlYShyYW5rZWRfdmVjLCBnZW5lc2V0cywgdXBfb25seSA9IEYpCnBsdCA9IGh5cF9kb3RzKGh5cF9vYmosbWVyZ2UgPSBGKQpwbHQxID0gcGx0JHVwKyBhZXMoc2l6ZT1uZXMpK2dndGl0bGUoInVwIGluIGNvbWJvUGVyc2lzdG9yIikKcGx0MiA9IHBsdCRkbisgYWVzKHNpemU9YWJzKG5lcykpK2dndGl0bGUoInVwIGluIG9zaVBlcnNpc3RvcnMiKQpwcmludF90YWIocGx0MStwbHQyLHRpdGxlID0gImNwVlNvcCIpCgoKcm94YVZTY3RybCA9IHJveGFWU2N0cmxbb3JkZXIocm94YVZTY3RybCRsb2cyRm9sZENoYW5nZSwgcm94YVZTY3RybCRwYWRqLGRlY3JlYXNpbmcgPSBUKSxdICNvcmRlciBieSBGQywgdGllcyBidCBwYWRqCnJhbmtlZF92ZWMgPSByb3hhVlNjdHJsWywibG9nMkZvbGRDaGFuZ2UiXSU+JSBzZXROYW1lcyhyb3duYW1lcyhyb3hhVlNjdHJsKSkgJT4lIG5hLm9taXQoKSAgIyBtYWtlIG5hbWVkIHZlY3RvcgoKaHlwX29iaiA8LSBoeXBlUl9mZ3NlYShyYW5rZWRfdmVjLCBnZW5lc2V0cywgdXBfb25seSA9IEYpCnBsdCA9IGh5cF9kb3RzKGh5cF9vYmosbWVyZ2UgPSBGKQpwbHQxID0gcGx0JHVwKyBhZXMoc2l6ZT1uZXMpK2dndGl0bGUoInVwIGluIHJveGEiKQpwbHQyID0gcGx0JGRuKyBhZXMoc2l6ZT1hYnMobmVzKSkrZ2d0aXRsZSgidXAgaW4gY3RybCIpCnByaW50X3RhYihwbHQxK3BsdDIsdGl0bGUgPSAicm94YSBWUyBjdHJsIikKCm9zaVZTY3RybCA9IG9zaVZTY3RybFtvcmRlcihvc2lWU2N0cmwkbG9nMkZvbGRDaGFuZ2UsIG9zaVZTY3RybCRwYWRqLGRlY3JlYXNpbmcgPSBUKSxdICNvcmRlciBieSBGQywgdGllcyBidCBwYWRqCnJhbmtlZF92ZWMgPSBvc2lWU2N0cmxbLCJsb2cyRm9sZENoYW5nZSJdJT4lIHNldE5hbWVzKHJvd25hbWVzKG9zaVZTY3RybCkpICU+JSBuYS5vbWl0KCkgICMgbWFrZSBuYW1lZCB2ZWN0b3IKCmh5cF9vYmogPC0gaHlwZVJfZmdzZWEocmFua2VkX3ZlYywgZ2VuZXNldHMsIHVwX29ubHkgPSBGKQpwbHQgPSBoeXBfZG90cyhoeXBfb2JqLG1lcmdlID0gRikKcGx0MSA9IHBsdCR1cCsgYWVzKHNpemU9bmVzKStnZ3RpdGxlKCJ1cCBpbiBvc2kiKQpwbHQyID0gcGx0JGRuKyBhZXMoc2l6ZT1hYnMobmVzKSkrZ2d0aXRsZSgidXAgaW4gY3RybCIpCnByaW50X3RhYihwbHQxK3BsdDIsdGl0bGUgPSAib3NpIFZTIGN0cmwiKQoKYGBgCgoKIyBDQyB1cHJlZ3VsdGVkIGluIGNwIFZTIG9wCmBgYHtyfQpvc2lWU2N0cmxfZ2VuZXMgPC0gcmVzdWx0cyhkZHMsY29udHJhc3QgPSBjKCJjb25kaXRpb24iLCJvc2kiLCJjdHJsIikpICAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBmaWx0ZXIobG9nMkZvbGRDaGFuZ2U+MCAmIHBhZGogPCAwLjA1KSAlPiUgcm93bmFtZXMoKQpjcFZTY3RybF9nZW5lcyA8LSByZXN1bHRzKGRkcyxjb250cmFzdCA9IGMoImNvbmRpdGlvbiIsImNvbWJvUGVyc2lzdG9ycyIsImN0cmwiKSkgICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIGZpbHRlcihsb2cyRm9sZENoYW5nZT4wICYgcGFkaiA8IDAuMDUpICU+JSByb3duYW1lcygpCnJveGFWU2N0cmxfZ2VuZXMgPSByb3hhVlNjdHJsICU+JSBmaWx0ZXIobG9nMkZvbGRDaGFuZ2U+MCAmIHBhZGogPCAwLjA1KSU+JSByb3duYW1lcygpCgpgYGAKCiMgREVHIHNocmlua2VkIEZDIHsudGFic2V0fQpgYGB7cn0KZGRzJGNvbmRpdGlvbiA9IHJlbGV2ZWwoZGRzJGNvbmRpdGlvbiwgcmVmID0gIm9zaVBlcnNpc3RvcnMiKQpkZHMgPC0gbmJpbm9tV2FsZFRlc3QoZGRzKQpjcFZTb3AgPC0gbGZjU2hyaW5rKGRkcyxjb2VmID0gImNvbmRpdGlvbl9jb21ib1BlcnNpc3RvcnNfdnNfb3NpUGVyc2lzdG9ycyIpICAlPiUgYXMuZGF0YS5mcmFtZSgpCgpkZHMkY29uZGl0aW9uID0gcmVsZXZlbChkZHMkY29uZGl0aW9uLCByZWYgPSAiY3RybCIpCmRkcyA8LSBuYmlub21XYWxkVGVzdChkZHMpCnJveGFWU2N0cmwgPC0gbGZjU2hyaW5rKGRkcyxjb2VmICA9ICJjb25kaXRpb25fcm94YV92c19jdHJsIikgICU+JSBhcy5kYXRhLmZyYW1lKCkKCgpkaWZmX2dlbmVzID0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSByb3duYW1lcyhjcFZTb3ApLCBjcFZTb3BfRkMgPSBjcFZTb3AkbG9nMkZvbGRDaGFuZ2Uscm94YVZTY3RybF9GQyA9IHJveGFWU2N0cmwkbG9nMkZvbGRDaGFuZ2UsICBjcFZTb3BfcGFkaiA9IGNwVlNvcCRwYWRqKQoKYGBgCgoKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTMscmVzdWx0cz0nYXNpcyd9CnJhbmtlZF92ZWMgPSBkaWZmX2dlbmVzWywgMV0gJT4lIHNldE5hbWVzKHJvd25hbWVzKGRpZmZfZ2VuZXMpKSAlPiUgc29ydChkZWNyZWFzaW5nID0gVFJVRSkKaHlwX29iaiA8LSBoeXBlUl9mZ3NlYShyYW5rZWRfdmVjLCBnZW5lc2V0cywgdXBfb25seSA9IEYpCnBsdCA9IGh5cF9kb3RzKGh5cF9vYmosbWVyZ2UgPSBGKQpwbHQxID0gcGx0JHVwKyBhZXMoc2l6ZT1uZXMpK2dndGl0bGUoInVwIGluIGNvbWJvUGVyc2lzdG9yIikgKyB0aGVtZSggIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTApKQpwbHQyID0gcGx0JGRuKyBhZXMoc2l6ZT1hYnMobmVzKSkrZ2d0aXRsZSgidXAgaW4gb3NpUGVyc2lzdG9ycyIpICsgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpCnByaW50X3RhYihwbHQxK3BsdDIsdGl0bGUgPSAiY3BWU29wIikKCnJhbmtlZF92ZWMgPSBkaWZmX2dlbmVzWywgMl0gJT4lIHNldE5hbWVzKHJvd25hbWVzKGRpZmZfZ2VuZXMpKSAlPiUgc29ydChkZWNyZWFzaW5nID0gVFJVRSkKaHlwX29iaiA8LSBoeXBlUl9mZ3NlYShyYW5rZWRfdmVjLCBnZW5lc2V0cywgdXBfb25seSA9IEYpCnBsdCA9IGh5cF9kb3RzKGh5cF9vYmosbWVyZ2UgPSBGKQpwbHQxID0gcGx0JHVwKyBhZXMoc2l6ZT1uZXMpK2dndGl0bGUoInVwIGluIHJveGEiKQpwbHQyID0gcGx0JGRuKyBhZXMoc2l6ZT1hYnMobmVzKSkrZ2d0aXRsZSgidXAgaW4gY3RybCIpCnByaW50X3RhYihwbHQxK3BsdDIsdGl0bGUgPSAiY3BWU29wIikKCmBgYAoKCiMgREVHIGluIGNvbWJvVlNvc2kgYnV0IG5vdCBpbiByb3hhVlNjdHJsIHsudGFic2V0fQoKYGBge3IgcmVzdWx0cz0nYXNpcyd9CmNwVlNvcCA8LSByZXN1bHRzKGRkcyxjb250cmFzdCA9IGMoImNvbmRpdGlvbiIsImNvbWJvUGVyc2lzdG9ycyIsIm9zaVBlcnNpc3RvcnMiKSkgICU+JSBhcy5kYXRhLmZyYW1lKCkKcm94YVZTY3RybCA8LSByZXN1bHRzKGRkcyxjb250cmFzdCA9IGMoImNvbmRpdGlvbiIsInJveGEiLCJjdHJsIikpICAlPiUgYXMuZGF0YS5mcmFtZSgpCmRpZmZfZ2VuZXMgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IHJvd25hbWVzKGNwVlNvcCksIGNwVlNvcF9GQyA9IDIqKmNwVlNvcCRsb2cyRm9sZENoYW5nZSxyb3hhVlNjdHJsX0ZDID0gMioqcm94YVZTY3RybCRsb2cyRm9sZENoYW5nZSwgIGNwVlNvcF9wYWRqID0gY3BWU29wJHBhZGopCnVwX2dlbmVzX2RmID0gIGRpZmZfZ2VuZXMgJT4lIGZpbHRlcihjcFZTb3BfRkMgPiAyICYgcm94YVZTY3RybF9GQzwxLjIgJiBjcFZTb3BfcGFkajwwLjA1KSAKZG93bl9nZW5lc19kZiA9IGRpZmZfZ2VuZXMgJT4lIGZpbHRlcihjcFZTb3BfRkMgPCAwLjUgJiByb3hhVlNjdHJsX0ZDPjAuOCAmIGNwVlNvcF9wYWRqPDAuMDUpCnVwX2dlbmVzID0gZGlmZl9nZW5lcyAlPiUgZmlsdGVyKGNwVlNvcF9GQyA+IDIgJiByb3hhVlNjdHJsX0ZDPDEuMiAmIGNwVlNvcF9wYWRqPDAuMDUpICU+JSByb3duYW1lcygpCmRvd25fZ2VuZXMgPSBkaWZmX2dlbmVzICU+JSBmaWx0ZXIoY3BWU29wX0ZDIDwgMC41ICYgcm94YVZTY3RybF9GQz4wLjggJiBjcFZTb3BfcGFkajwwLjEpJT4lIHJvd25hbWVzKCkKCnByaW50X3RhYih1cF9nZW5lc19kZix0aXRsZSA9ICJ1cCIpCnByaW50X3RhYihkb3duX2dlbmVzX2RmLHRpdGxlID0gImRvd24iKQpgYGAKYGBge3J9CkgxOTc1X3VwX2dlbmVzID0gdXBfZ2VuZXMKSDE5NzVfZG93bl9nZW5lcyA9IGRvd25fZ2VuZXMKCmBgYAoKIyB7LX0KCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEzLHJlc3VsdHM9J2FzaXMnfQpoeXBfb2JqIDwtIGh5cGVSKHVwX2dlbmVzLCBnZW5lc2V0cywgdGVzdCA9ICJoeXBlcmdlb21ldHJpYyIsIGZkcj0xLCBwbG90dGluZz1GLGJhY2tncm91bmQgPSByb3duYW1lcyhIMTk3NU9jdDIzKSkKcGx0MSA9IGh5cF9kb3RzKGh5cF9vYmosdGl0bGUgPSAidXAgaW4gY29tYm9WU29zaSBidXQgbm90IGluIHJveGFWU2N0cmwiKQogCgpoeXBfb2JqIDwtIGh5cGVSKGRvd25fZ2VuZXMsIGdlbmVzZXRzLCB0ZXN0ID0gImh5cGVyZ2VvbWV0cmljIiwgZmRyPTEsIHBsb3R0aW5nPUYsYmFja2dyb3VuZCA9IHJvd25hbWVzKEgxOTc1T2N0MjMpKQpwbHQyID0gaHlwX2RvdHMoaHlwX29iaix0aXRsZSA9ICJkb3duIGluIGNvbWJvVlNvc2kgYnV0IG5vdCBpbiByb3hhVlNjdHJsIikKCnBsdDEgKyBwbHQyCmBgYApgYGB7ciByZXN1bHRzPSdhc2lzJ30KcHJpbnRfdGFiKGRhdGEuZnJhbWUodXBfZ2VuZXNbdXBfZ2VuZXMgJWluJSBnZW5lc2V0cyRIQUxMTUFSS19FMkZfVEFSR0VUU10pLHRpdGxlID0gInVwIGdlbmVzIGluIEUyRiIpCnByaW50X3RhYihkYXRhLmZyYW1lKHVwX2dlbmVzW3VwX2dlbmVzICVpbiUgZ2VuZXNldHMkSEFMTE1BUktfSFlQT1hJQV0pLHRpdGxlID0gInVwIGdlbmVzIGluIEh5cG94aWEiKQoKCmBgYAoKIyBFeHByZXNzaW9uIGhlYXRtYXAgey50YWJzZXR9CmBgYHtyIGZpZy5oZWlnaHQ9NiwgcmVzdWx0cz0nYXNpcyd9CiMgc2VsZWN0IHRoZSA1MCBtb3N0IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyAKZ2VuZXMgPC0gYygiRFVTUDYiLCJNS0k2NyIpCm1hdCA8LSBIMTk3NU9jdDIzWyBnZW5lcywgXQptYXQgPC0gdChzY2FsZSh0KG1hdCkpKQphbm5vIDwtIGFzLmRhdGEuZnJhbWUobWF0KQoKbGlicmFyeShDb21wbGV4SGVhdG1hcCkgCmxpYnJhcnkoZ2dwbG90MikgCnAgPSBIZWF0bWFwKG1hdCwgY2x1c3Rlcl9yb3dzID0gRiwgY2x1c3Rlcl9jb2x1bW5zID0gRiwgY29sdW1uX2xhYmVscyA9IGNvbG5hbWVzKGFubm8pLCBuYW1lID0gImZwa20gWi1zY29yZSIpIApwcmludF90YWIocGx0ID0gcCx0aXRsZSA9ICJtYXJrZXJzIikKCmdlbmVzIDwtIGhpZl90YXJnZXRzCm1hdCA8LSBIMTk3NU9jdDIzW2dlbmVzLCBdICU+JSBmaWx0ZXIocm93U3VtcyhhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykpKSE9MCkKbWF0IDwtIHQoc2NhbGUodChtYXQpKSkKYW5ubyA8LSBhcy5kYXRhLmZyYW1lKG1hdCkKCiAKcCA9IEhlYXRtYXAobWF0LCBjbHVzdGVyX3Jvd3MgPSBULCBjbHVzdGVyX2NvbHVtbnMgPSBGLCBjb2x1bW5fbGFiZWxzID0gY29sbmFtZXMoYW5ubyksIG5hbWUgPSAiZnBrbSBaLXNjb3JlIixjb2x1bW5fdGl0bGUgPSAiSElGIHRhcmdldHMiLHJvd19uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSA4KSkKCnByaW50X3RhYihwbHQgPSBwLHRpdGxlID0gIkhJRiB0YXJnZXRzIikKCgoKZ2VuZXMgPC0gZ2VuZXNldHMkSEFMTE1BUktfSFlQT1hJQQptYXQgPC0gSDE5NzVPY3QyM1tnZW5lcywgXSAlPiUgZmlsdGVyKHJvd1N1bXMoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpKSkhPTApCm1hdCA8LSB0KHNjYWxlKHQobWF0KSkpCmFubm8gPC0gYXMuZGF0YS5mcmFtZShtYXQpCgoKcCA9IEhlYXRtYXAobWF0LCBjbHVzdGVyX3Jvd3MgPSBULCBjbHVzdGVyX2NvbHVtbnMgPSBGLCBjb2x1bW5fbGFiZWxzID0gY29sbmFtZXMoYW5ubyksIG5hbWUgPSAiZnBrbSBaLXNjb3JlIixjb2x1bW5fdGl0bGUgPSAiSEFMTE1BUktfSFlQT1hJQSIscm93X25hbWVzX2dwID1ncGFyKGZvbnRzaXplID0gMCkpIAoKcHJpbnRfdGFiKHBsdCA9IHAsdGl0bGUgPSAiSEFMTE1BUktfSFlQT1hJQSIpCgoKCmBgYAojIEcyTSBoZWF0bWFwCmBgYHtyIGZpZy5oZWlnaHQ9NiwgcmVzdWx0cz0nYXNpcyd9CmdlbmVzIDwtIGdlbmVzZXRzJEhBTExNQVJLX0cyTV9DSEVDS1BPSU5UCm1hdCA8LSBIMTk3NU9jdDIzW2dlbmVzLCBdICU+JSBmaWx0ZXIocm93U3VtcyhhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykpKSE9MCkKbWF0IDwtIHQoc2NhbGUodChtYXQpKSkKYW5ubyA8LSBhcy5kYXRhLmZyYW1lKG1hdCkKCnNwbGl0ID0gZGF0YS5mcmFtZSggY2x1c3RlciA9IGN1dHJlZShoY2x1c3QoZGlzdChtYXQpKSwgayA9IDYpKQpoYSA9IHJvd0Fubm90YXRpb24oY2x1c3RlciA9IGFzLmNoYXJhY3RlcihzcGxpdCRjbHVzdGVyKSxjb2wgPSBsaXN0KGNsdXN0ZXIgPSBzdHJ1Y3R1cmUocmFpbmJvdyhuID0gNiksIG5hbWVzPXVuaXF1ZShzcGxpdCRjbHVzdGVyKSkpKQpwID0gSGVhdG1hcChtYXQsIGNsdXN0ZXJfcm93cyA9IFQsIGNsdXN0ZXJfY29sdW1ucyA9IEYsIGNvbHVtbl9sYWJlbHMgPSBjb2xuYW1lcyhhbm5vKSwgbmFtZSA9ICJmcGttIFotc2NvcmUiLGNvbHVtbl90aXRsZSA9ICJIQUxMTUFSS19HMk1fQ0hFQ0tQT0lOVCIscm93X25hbWVzX2dwID1ncGFyKGZvbnRzaXplID0gMCkscmlnaHRfYW5ub3RhdGlvbiA9IGhhKQoKcHJpbnRfdGFiKHBsdCA9IHAsdGl0bGUgPSAiSEFMTE1BUktfRzJNX0NIRUNLUE9JTlQiKQoKYGBgCiMgRW5yaWNobWVudCBhbmFseXNpcyB7LnRhYnNldH0KYGBge3IgZmlnLmhlaWdodD03LHJlc3VsdHM9J2FzaXMnfQpjaG9zZW5fZ2VuZXMgPSBzcGxpdCAlPiUgZHBseXI6OmZpbHRlcihjbHVzdGVyID09IDEpICU+JSByb3duYW1lcygpICN0YWtlIHJlbGV2YW50IGdlbmVzCmh5cF9vYmogPC0gaHlwZVIoY2hvc2VuX2dlbmVzLCBnZW5lc2V0c193aWtpcGF0aHdheXMsIHRlc3QgPSAiaHlwZXJnZW9tZXRyaWMiLCBmZHI9MSwgcGxvdHRpbmc9RixiYWNrZ3JvdW5kID0gcm93bmFtZXMoSDE5NzVPY3QyMykpCnAxID0gaHlwX2RvdHMoaHlwX29iaixzaXplX2J5ID0gIm5vbmUiLHRpdGxlID0gImNsdXN0ZXIgMSIpCmNob3Nlbl9nZW5lcyA9IHNwbGl0ICU+JSBkcGx5cjo6ZmlsdGVyKGNsdXN0ZXIgPT0gNCkgJT4lIHJvd25hbWVzKCkgI3Rha2UgcmVsZXZhbnQgZ2VuZXMKaHlwX29iaiA8LSBoeXBlUihjaG9zZW5fZ2VuZXMsIGdlbmVzZXRzX3dpa2lwYXRod2F5cywgdGVzdCA9ICJoeXBlcmdlb21ldHJpYyIsIGZkcj0xLCBwbG90dGluZz1GLGJhY2tncm91bmQgPSByb3duYW1lcyhIMTk3NU9jdDIzKSkKcDIgPSBoeXBfZG90cyhoeXBfb2JqLHNpemVfYnkgPSAibm9uZSIsdGl0bGUgPSAiY2x1c3RlciA0IikKCndpa2lfcCA9IChwMS9wMikKcHJpbnRfdGFiKHBsdCA9IHdpa2lfcCx0aXRsZSA9ICJ3aWtpcGF0aHdheXMiKQoKCmNob3Nlbl9nZW5lcyA9IHNwbGl0ICU+JSBkcGx5cjo6ZmlsdGVyKGNsdXN0ZXIgPT0gMSkgJT4lIHJvd25hbWVzKCkgI3Rha2UgcmVsZXZhbnQgZ2VuZXMKaHlwX29iaiA8LSBoeXBlUihjaG9zZW5fZ2VuZXMsIGdlbmVzZXRzX2Jpb2NhcnRhLCB0ZXN0ID0gImh5cGVyZ2VvbWV0cmljIiwgZmRyPTEsIHBsb3R0aW5nPUYsYmFja2dyb3VuZCA9IHJvd25hbWVzKEgxOTc1T2N0MjMpKQpwMSA9IGh5cF9kb3RzKGh5cF9vYmosc2l6ZV9ieSA9ICJub25lIix0aXRsZSA9ICJjbHVzdGVyIDEiKQpjaG9zZW5fZ2VuZXMgPSBzcGxpdCAlPiUgZHBseXI6OmZpbHRlcihjbHVzdGVyID09IDQpICU+JSByb3duYW1lcygpICN0YWtlIHJlbGV2YW50IGdlbmVzCmh5cF9vYmogPC0gaHlwZVIoY2hvc2VuX2dlbmVzLCBnZW5lc2V0c19iaW9jYXJ0YSwgdGVzdCA9ICJoeXBlcmdlb21ldHJpYyIsIGZkcj0xLCBwbG90dGluZz1GLGJhY2tncm91bmQgPSByb3duYW1lcyhIMTk3NU9jdDIzKSkKcDIgPSBoeXBfZG90cyhoeXBfb2JqLHNpemVfYnkgPSAibm9uZSIsdGl0bGUgPSAiY2x1c3RlciA0IikKCndpa2lfcCA9IChwMS9wMikKcHJpbnRfdGFiKHBsdCA9IHdpa2lfcCx0aXRsZSA9ICJiaW9jYXJ0YSIpCgpjaG9zZW5fZ2VuZXMgPSBzcGxpdCAlPiUgZHBseXI6OmZpbHRlcihjbHVzdGVyID09IDEpICU+JSByb3duYW1lcygpICN0YWtlIHJlbGV2YW50IGdlbmVzCmh5cF9vYmogPC0gaHlwZVIoY2hvc2VuX2dlbmVzLCBnZW5lc2V0c19waWQsIHRlc3QgPSAiaHlwZXJnZW9tZXRyaWMiLCBmZHI9MSwgcGxvdHRpbmc9RixiYWNrZ3JvdW5kID0gcm93bmFtZXMoSDE5NzVPY3QyMykpCnAxID0gaHlwX2RvdHMoaHlwX29iaixzaXplX2J5ID0gIm5vbmUiLHRpdGxlID0gImNsdXN0ZXIgMSIpCmNob3Nlbl9nZW5lcyA9IHNwbGl0ICU+JSBkcGx5cjo6ZmlsdGVyKGNsdXN0ZXIgPT0gNCkgJT4lIHJvd25hbWVzKCkgI3Rha2UgcmVsZXZhbnQgZ2VuZXMKaHlwX29iaiA8LSBoeXBlUihjaG9zZW5fZ2VuZXMsIGdlbmVzZXRzX3BpZCwgdGVzdCA9ICJoeXBlcmdlb21ldHJpYyIsIGZkcj0xLCBwbG90dGluZz1GLGJhY2tncm91bmQgPSByb3duYW1lcyhIMTk3NU9jdDIzKSkKcDIgPSBoeXBfZG90cyhoeXBfb2JqLHNpemVfYnkgPSAibm9uZSIsdGl0bGUgPSAiY2x1c3RlciA0IikKCndpa2lfcCA9IChwMS9wMikKcHJpbnRfdGFiKHBsdCA9IHdpa2lfcCx0aXRsZSA9ICJwaWQiKQoKY2hvc2VuX2dlbmVzID0gc3BsaXQgJT4lIGRwbHlyOjpmaWx0ZXIoY2x1c3RlciA9PSAxKSAlPiUgcm93bmFtZXMoKSAjdGFrZSByZWxldmFudCBnZW5lcwpoeXBfb2JqIDwtIGh5cGVSKGNob3Nlbl9nZW5lcywgZ2VuZXNldHNfUkVBQ1RPTUUsIHRlc3QgPSAiaHlwZXJnZW9tZXRyaWMiLCBmZHI9MSwgcGxvdHRpbmc9RixiYWNrZ3JvdW5kID0gcm93bmFtZXMoSDE5NzVPY3QyMykpCnAxID0gaHlwX2RvdHMoaHlwX29iaixzaXplX2J5ID0gIm5vbmUiLHRpdGxlID0gImNsdXN0ZXIgMSIpCmNob3Nlbl9nZW5lcyA9IHNwbGl0ICU+JSBkcGx5cjo6ZmlsdGVyKGNsdXN0ZXIgPT0gNCkgJT4lIHJvd25hbWVzKCkgI3Rha2UgcmVsZXZhbnQgZ2VuZXMKaHlwX29iaiA8LSBoeXBlUihjaG9zZW5fZ2VuZXMsIGdlbmVzZXRzX1JFQUNUT01FLCB0ZXN0ID0gImh5cGVyZ2VvbWV0cmljIiwgZmRyPTEsIHBsb3R0aW5nPUYsYmFja2dyb3VuZCA9IHJvd25hbWVzKEgxOTc1T2N0MjMpKQpwMiA9IGh5cF9kb3RzKGh5cF9vYmosc2l6ZV9ieSA9ICJub25lIix0aXRsZSA9ICJjbHVzdGVyIDQiKQoKd2lraV9wID0gKHAxL3AyKQpwcmludF90YWIocGx0ID0gd2lraV9wLHRpdGxlID0gIlJFQUNUT01FIikKCmBgYApgYGB7cn0KZm9yIChjaG9zZW5fY2x1c3RlcnMgaW4gMTpsZW5ndGgodW5pcXVlKHNwbGl0JGNsdXN0ZXIpKSkgewpjaG9zZW5fZ2VuZXMgPSBzcGxpdCAlPiUgZHBseXI6OmZpbHRlcihjbHVzdGVyID09IGNob3Nlbl9jbHVzdGVycykgJT4lIHJvd25hbWVzKCkgI3Rha2UgcmVsZXZhbnQgZ2VuZXMKaHlwX29iaiA8LSBoeXBlUihjaG9zZW5fZ2VuZXMsIGdlbmVzZXRzX3dpa2lwYXRod2F5cywgdGVzdCA9ICJoeXBlcmdlb21ldHJpYyIsIGZkcj0xLCBwbG90dGluZz1GLGJhY2tncm91bmQgPSByb3duYW1lcyhIMTk3NU9jdDIzKSkKcHJpbnQoaHlwX2RvdHMoaHlwX29iaixzaXplX2J5ID0gIm5vbmUiKSkKICAKICB9CmBgYAoKCgoKIyBEaXN0YW5jZSBwbG90CmBgYHtyfQp2c2QgPC0gdnN0KGRkcywgYmxpbmQ9RkFMU0UpCnNhbXBsZURpc3RzIDwtIGRpc3QodChhc3NheSh2c2QpKSkKbGlicmFyeSgiUkNvbG9yQnJld2VyIikKc2FtcGxlRGlzdE1hdHJpeCA8LSBhcy5tYXRyaXgoc2FtcGxlRGlzdHMpCmNvbG5hbWVzKHNhbXBsZURpc3RNYXRyaXgpIDwtIE5VTEwKY29sb3JzIDwtIGNvbG9yUmFtcFBhbGV0dGUoIHJldihicmV3ZXIucGFsKDksICJCbHVlcyIpKSApKDI1NSkKcGhlYXRtYXAoc2FtcGxlRGlzdE1hdHJpeCwKICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzPXNhbXBsZURpc3RzLAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHM9c2FtcGxlRGlzdHMsCiAgICAgICAgIGNvbD1jb2xvcnMpCmBgYAo8c2NyaXB0IHNyYz0iaHR0cHM6Ly9oeXBvdGhlcy5pcy9lbWJlZC5qcyIgYXN5bmM+PC9zY3JpcHQ+Cgo=