##################
# LOAD LIBRARIES #
##################
suppressWarnings({suppressMessages({suppressPackageStartupMessages({
library(tidyverse)
library(edgeR)
library(openxlsx)
library(scales)
library(igraph)
library(fgsea)
library(RColorBrewer)
#remotes::install_github("czarnewski/niceRplots",force=T)
library(niceRplots)
}) }) })
#########
# PATHS #
#########
result_dir <- "./Suppl.Tbl/"
if( isFALSE(dir.exists(result_dir)) ) { dir.create(result_dir,recursive = TRUE) }
#############
# LODA DATA #
#############
datasets_all_samples <- readRDS("../../results/03_normalize_data_output/datasets_all_samples.RDS")
metadata <- read.csv("../../data/metadata.csv",row.names = 1, stringsAsFactors = F)
sample_use <- metadata$ID
group_annotation <- factor(setNames(metadata$Luminal_gr,metadata$ID))
gr <- "Luminal"
SNN_bacteria <- read.csv("../../results/04_clustering_output/bacteria_SNN_graph.csv",row.names = 1)
bac_communities <- as.matrix(read.csv("../../results/04_clustering_output/bacterial_communities.csv",row.names = 2))[,2]
picrust_data <- read.table("../../results/05_picrust_output/out_2021-01-20/KO_metagenome_out/pred_metagenome_unstrat.tsv", header = T,row.names = 1)
ko_names <- readLines('../../resources/picrust_data/ko_info.tsv')
kegg_names <- read.delim('../../resources/picrust_data/KEGG_pathways_info.tsv',header = F)
gmt_list <- fgsea::gmtPathways("../../resources/picrust_data/KEGG_pathways_to_KO.tsv")
source("../../code/enrichment_function.R")
AA <- c("(?<!Phenyl)Alanine " = "Ala", "Arginine" = "Arg", "Asparagine" = "Asn", "Aspartic acid" = "Asp", "Cysteine" = "Cys", "Glutamic acid" = "Glu", "Glutamine" = "Gln", "Glycine" = "Gly", "Histidine" = "His", "Hydroxyproline" = "Hyp", "Isoleucine" = "Ile", "Leucine" = "Leu", "Lysine" = "Lys", "Methionine" = "Met", "Phenylalanine" = "Phe", "Proline" = "Pro", "Pyroglutamatic" = "Glp", "Serine" = "Ser", "Threonine" = "Thr", "Tryptophan" = "Trp", "Tyrosine" = "Tyr", "Valine" = "Val")
AA_ <- set_names(AA, map_chr(names(AA), ~ paste0("(?i)", .))) # make case insensitive
#################
# COLOR PALETTS #
#################
pal <- c( "#0072B2", "#009E73","#D55E00", "#CC79A7", "#E69F00", "#999999")
taxa_pal <- c(RColorBrewer::brewer.pal(8,"Pastel2"),RColorBrewer::brewer.pal(8,"Pastel1"),"grey90")
bact_pal <- c('#88CCEE', '#44AA99', '#117733', '#332288', '#DDCC77', '#999933','#CC6677', '#882255', '#AA4499', '#DDDDDD')
layout(matrix(c(1,1,2,2,2,
1,1,3,3,3,
1,1,4,4,4,
5,6,7,8,9,
5,6,7,8,10),
nrow = 5,ncol = 5,byrow = T), widths = c(.6,1.5,.2,1.9,2),
heights = c(.68,.56,.56,1,1.5))
figlabels <- letters
### A
#########################
# BACTERIAL COMMUNITIES #
#########################
bac_communities <- factor(paste0("BC",sprintf("%02d",bac_communities)))
gB <- graph_from_adjacency_matrix(as.matrix(SNN_bacteria), mode = "undirected",diag = F,weighted = T)
set.seed(1)
lB <- layout_nicely(gB,niter=3000,start.temp=30)
par(mar=c(0,0,3.5,5))
plot( gB , vertex.label.cex=0.000001 , vertex.color = bact_pal[factor(bac_communities)] ,
edge.width= ( E(gB)$weight / max(E(gB)$weight)) ,
vertex.size=10,
edge.color=colorRampPalette(c("grey95","black"))(90) [ round( E(gB)$weight / max(E(gB)$weight) * 88 )+1 ] ,layout=lB)
title(main = " Bacterial Communities- Louvain", line = 1, cex.main = 1)
legend(par("usr")[2],par("usr")[4],title.adj = 0,
legend = levels(factor(bac_communities)),xjust = 0,yjust = 1,
bty = "n",pch = 21,pt.bg = bact_pal,pt.cex = 1,xpd=T)
#add label
add_letter(figlabels[1]); figlabels <- figlabels[-1]
### B
###################################
# WET SMEAR BACTERIAL VALIDATION #
###################################
par(mar=c(1,5,4,1)) #b,l,t,r
x <- datasets_all_samples[["ASV_Luminal_normalized"]]
xL <- rowsum(x , grepl("L.",rownames(x)) )[2,] / sum(grepl("L.",rownames(x)))
xM <- rowsum(x , grepl("Mobiluncus",rownames(x)) )[2,] / sum(grepl("Mobiluncus",rownames(x)))
xG <- rowsum(x , grepl("Gardnerella",rownames(x)) )[2,] / sum(grepl("Gardnerella",rownames(x)))
mL <- metadata$BV_Lactobacillus_v3 ; mL[is.na(mL)] <- 0
mM <- metadata$BV_Monbilicus_v3 ; mM[is.na(mM)] <- 0
mG <- metadata$BV_Vaginal_Garda_v3 ; mG[is.na(mG)] <- 0
barlist( data = rbind(WS=mL ,
"16S counts"=xL),
genes = c("WS","16S counts"),
clustering = group_annotation,srt=20,
draw_mean_lines=F,col = pal)
mtext("Lactobacillus", side=3, las = 1, cex = .6, line = .5,xpd=T)
mtext("Wet Smear Bacteria Validation", side=3, las = 1, cex = .7, line =1.5,xpd=T, font = 2)
#add label
add_letter(figlabels[1]); figlabels <- figlabels[-1]
par(mar=c(1,5,2,1)) #b,l,t,r
barlist( data = rbind(WS=mM ,
"16S counts"=xM),
genes = c("WS","16S counts"),
clustering = group_annotation,srt=20,
draw_mean_lines=F,col = pal)
mtext("Mobiluncus", side=3, las = 1, cex = .6, line = .5,xpd=T)
barlist( data = rbind(WS=mG ,
"16S counts"=xG),
genes = c("WS","16S counts"),
clustering = group_annotation,srt=20,
draw_mean_lines=F,col = pal)
mtext("Gardnerella", side=3, las = 1, cex = .6, line = .5,xpd=T)
### C
#####################
# BACTERIAL PICRUST #
#####################
picrust_data <- round(picrust_data[ rowSums(picrust_data>5)>=10, ])
TRX <- datasets_all_samples[["Tissue_RNAseq_V3_normalized"]]
sample_use <- intersect(colnames(TRX)[colSums(TRX)!=0], colnames(picrust_data))
ko_names <- strsplit(ko_names,"\t")
ko_names <- setNames(unlist(lapply(ko_names,function(x){x[2]})), unlist(lapply(ko_names,function(x){x[1]})))
ko_names <- sub(" [[].*","",ko_names)
ko_names <- sub(".*; ","",ko_names)
named_picrust_data <- rowsum( picrust_data , group = ko_names[ rownames(picrust_data) ] )
named_picrust_data <- round(named_picrust_data[ rowSums(named_picrust_data>5)>=5, ])
design <- model.matrix(~ group_annotation+Contraception+HIVstatus,
data=metadata[sample_use,])
y <- DGEList(counts=named_picrust_data[,sample_use])
y <- calcNormFactors(y,method = "TMM")
y <- estimateGLMCommonDisp(y)
y <- estimateGLMTagwiseDisp(y)
fit <- glmFit(y, design)
lrt <- glmLRT(fit,coef=2:5)
top <- topTags(lrt,adjust.method = "BH",n = "all",sort.by = "p.value")[[1]]
colnames(top) <- sub("group_annotation","",colnames(top))
top <- cbind(LogFC.intercept=0,top) # with intercept
top_dge <- (top$FDR < 1e-5) #& (rowSums( abs(top[,grep("logFC",colnames(top))]) >= log2(1.5) ) >= 1)
top_dge <- rownames(top)[ top_dge ]
top_picrust_data <- edgeR::cpm(y)[top_dge, ]
n <- colnames(top_picrust_data)
top_picrust_data <- t(apply(top_picrust_data,1,function(x){scale(x,T,T)}))
top_picrust_data[top_picrust_data > 5] <- 5
colnames(top_picrust_data) <- n
# Dendogram
par(mar=c(4,.1,4,.2)) #b,l,t,r
h <- hclust( as.dist( (1- cor(t(top_picrust_data)))/2 ), method = "ward.D2")
plot( rev(as.dendrogram(h)) ,xlim=c(max(h$height),-1), horiz = T, leaflab = "none",xaxs="i",yaxs="i",axes=T)
title(xlab="Height", line=2)
cutoff <- 4
abline(v=cutoff,xpd=F,col="red",lty=2)
gene_module <- cutree(h, h = cutoff)
#add label
add_letter(figlabels[1]); figlabels <- figlabels[-1]
points( rep(-.7,length(gene_module)),
seq(length(gene_module),1,length.out = length(gene_module)),
col=taxa_pal[factor(gene_module[h$order])],
pch=15,cex=1,xpd=F)
# Heatmap
image( t(top_picrust_data[h$order,][nrow(top_picrust_data):1,order(group_annotation[sample_use])]),
col = colorRampPalette(c("navy","navy","grey95","firebrick4","firebrick4") )(99),
breaks = seq(-5,5,length.out = 100),axes=F)
mtext(" Differentially expressed KEGG Ontology terms and pathways",
side=3, las = 1, cex = .7, line =1.5,xpd=T, font = 2)
title(xlab="Study Groups", line=1)
# Study Group brackets
end <- table(pal[factor(group_annotation)[ order(group_annotation[sample_use])]] )
end <- end[order(factor(names(end), levels = pal))]
end <- map_dbl(cumsum(end), ~ (.x * (par("usr")[2])/ncol(picrust_data[sample_use])-.015) )
start <- c(0, map_dbl(end[1:4], ~.x+0.017))
end <- c(end[1:length(end)-1], par("usr")[2]-.005)
axis(1, at = c(start, end), label = F, pos =1.021 , xpd=T, col="white",col.ticks="black")
map2(start, end, ~lines(x=c(.y, .x), y= c(1.021, 1.021), xpd=T, cex=.8))
# text labels
t <- map2(start, end, ~(.x+.y)/2)
map2(t,levels(factor(group_annotation)), ~text(x=.x, y=1.037, .y,xpd=T, cex=.8))
# Meta bar info
par(mar=c(3,.1,2,.1)) #bottom, left, top, right
map(c(.0021, .006), ~points( seq(0,1,length.out = length(sample_use)),
rep(par("usr")[4],length(sample_use))+.x, pch=15, cex=.3,xpd=F,
col=pal[factor(group_annotation[sample_use])[ order(group_annotation[sample_use])]]) )
points( seq(0,1,length.out = ncol(top_picrust_data)),
rep(par("usr")[3],ncol(top_picrust_data))-.008, pch=15, xpd=F,cex=.3,
col= c("tomato","orange","#d7d7d7")[as.factor(metadata$BV_Diagnosis_v3)][order( group_annotation[sample_use])] )
points( seq(0,1,length.out = ncol(top_picrust_data)),
rep(par("usr")[3],ncol(top_picrust_data))-.024, pch=15, xpd=F,cex=.3,
col= c("#d7d7d7","tomato")[as.factor(metadata$HIVstatus)][order(group_annotation[sample_use])])
names(gmt_list) <- kegg_names [ match ( names(gmt_list), as.character(kegg_names[,1]) ) , 2 ]
gmt_list <- lapply(gmt_list , function(x){
na.omit(ko_names[x])
})
res_list <- lapply( unique( gene_module[h$order] ),function(x){
temp <- compute_enrichment(genes = names(gene_module)[gene_module==x],
gmt_list = gmt_list,
min_terms_pathway = 10,
max_terms_pathway = 300,
min_overlap = 3,
sort_by_pvalue = T)
temp <- temp[!grepl("REGULATION",rownames(temp)),]
return(temp)
} )
res <- map(res_list, ~rownames_to_column(.x, var = "Terms")) %>%
set_names(., paste0("module_", seq_along(res_list)))
names(res_list) <- unique( gene_module[h$order] )
pvalues <- unlist(lapply(res_list,function(x){ -log10(as.numeric(x$pvalue)) [1:3] }))
pvalues[is.na(pvalues)] <- 0
terms <- unlist(lapply(res_list,function(x){ rownames(x) [1:3] }))
terms[is.na(terms)] <- ""
terms <- stringr::str_replace_all(string = terms,pattern= AA_)
genes <- unlist(lapply(res_list,function(x){ x$genes [1:3] }))
genes[is.na(genes)] <- ""
#pvalues <- setNames(pvalues,paste0(terms," (",genes,")"))
pvalues <- setNames(pvalues,paste0(terms,""))
module_color <- unlist(lapply(names(res_list),function(x){ rep(x,3) }))
temp <- factor( rev(gene_module), levels = unique( gene_module[h$order] ))
df <- data.frame(temp,temp)
par(mar=c(4,0,4,.1)) #bottom, left, top, right
quietly(plot_sankey)( df, pal = taxa_pal[as.numeric(levels(temp))], use_w2 = F, plot_labels = T,gapv = .01,gap2v = 0 ,xaxs="i",yaxs="i",plot_weights = F)$output
# par("usr")(xmin, ymin, xmax, ymax)
text(par("usr")[1],par("usr")[3]-.008,labels = " BV",cex=.6, xpd=T)
text(par("usr")[1],par("usr")[3]-.024,labels = " HIV",cex=.6, xpd=T)
par(mar=c(4,.1,4,7))
barplot( rev(pvalues) , horiz = T , col= taxa_pal[factor(rev(module_color))],
xaxs="i",yaxs="i",xlim=c(0,1.2*max(pvalues)),ylab="",las=1,names.arg = "")
title(xlab="-log10(p-value)", line=2)
abline(v=2,xpd=F,col="red",lty=2)
points( rev(pvalues), seq(1,length(pvalues) )*1.2-.5,pch=21,bg=taxa_pal[factor(rev(module_color))] )
text( rev(pvalues)+strwidth("M") , seq(1,length(pvalues) )*1.2-.5, adj=0,cex=1,xpd=T,
labels = rev(gsub("\\(\\)","",names(pvalues)) ),xpd=T)
text( rev(pvalues)+strwidth("M") , seq(1,length(pvalues) )*1.2-.5, adj=0,cex=1,
labels = rev(names(pvalues)),col = paste0(taxa_pal[factor(rev(module_color))],90) ,xpd=T)
### D
################
# PICRUST UMAP #
################
# based on 108 samples used in manuscript:
par(mar=c(2.5,2.5,3,4)) #b,l,t,r
set.seed(1)
UMAP_picrust <- uwot::umap(t(named_picrust_data[,sample_use]),n_neighbors = 20,
metric = "correlation",min_dist = .2,spread = .4,
repulsion_strength = .4,negative_sample_rate = 3)
plot(UMAP_picrust,bg=pal[factor(metadata$Luminal_gr)],
pch=21,frame=F,axes=F)
title(main = "Top Functional Profile- UMAP", line = 0.5, cex.main = 1)
#mtext("UMAP1", side=1, line=0.7, cex=0.8)
#mtext("UMAP2", side=2, line=0.7, cex=0.8)
legend(par("usr")[2],par("usr")[4],title.adj = 0,
legend = levels(factor(metadata$Luminal_gr)),xjust = 0,yjust = 1,
bty = "n",pch = 21,pt.bg = pal,pt.cex = 1,xpd=T, cex=1)
#add label
add_letter(figlabels[1]); figlabels <- figlabels[-1]
empty_plot()

## [[1]]
## NULL
##
## $`#0072B2`
## NULL
##
## $`#009E73`
## NULL
##
## $`#D55E00`
## NULL
##
## $`#CC79A7`
## NULL
##
## [[1]]
## NULL
##
## $`#0072B2`
## NULL
##
## $`#009E73`
## NULL
##
## $`#D55E00`
## NULL
##
## $`#CC79A7`
## NULL
##
## [[1]]
## NULL
##
## [[2]]
## NULL
##
## [1] ""
Figure 2. Identification of bacterial communities and
functional profiles in the luminal samples. The luminal study
samples were assessed for bacterial communities and functional profiles.
a Bacterial community embedding of 5-SNN graph
clustered using Louvain community detection algorithm based on luminal
bacterial abundances. b Wet smear counts of the genera
Lactobacillus, Mobiluncus, and Gardnerella with corresponding 16S read
counts. c Differential expression analysis was applied
to PICRUSt2 predicted KO terms across the five luminal study groups.
Resulting significant (FDR < 1 × 10−5) KO terms were divided into
seven modules by hierarchical agglomerative clustering using inverse
Pearson’s correlation as distance measure and Ward’s method (“ward. D2”)
for linkage. Enrichment analysis was performed on each module and the
three most significant KEGG pathways were included in the heatmap. d
Uniform Manifold Approximation (UMAP) of the predicted KO terms
#saveRDS( y , "../../../results/Picrust_EdgeR_estimations.rds" )
top <- top %>% as_tibble(rownames="KO Terms")
pages <- list(list("Across"=top), res) %>% flatten()
write.xlsx(pages, file=paste0(result_dir,"Suppl.Tbl.02",".xlsx"))
LS0tCnRpdGxlOiAiRmlndXJlIDIuIEJhY3RlcmlhbCBDb21tdW5pdGllcyBhbmQgRnVuY3Rpb24iCmdlb21ldHJ5OiAibGVmdD0yY20scmlnaHQ9MmNtLHRvcD0yY20sYm90dG9tPTJjbSIKaGVhZGVyLWluY2x1ZGVzOiAKLSBcdXNlcGFja2FnZXtmbG9hdH0KZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCmtuaXQ6IChmdW5jdGlvbihpbnB1dEZpbGUsIG91dF9kaXIsIC4uLikgewogICAgc291cmNlKCIuLi8uLi9jb2RlL2tuaXRfZnVuY3Rpb24uUiIpOwogICAgY3VzdG9tX2tuaXQoaW5wdXRGaWxlLCAiLi4vLi4vbGFiX2Jvb2svRmlndXJlMi8iLCAuLi4pCiAgICB9KQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgcmVzdWx0cyAgICA9ICJob2xkIiwKICBtZXNzYWdlICAgID0gRkFMU0UsCiAgd2FybmluZyAgICA9IEZBTFNFLAogIGZpZy53aWR0aCAgPSA2LjY5MjkxMzM4NTgsCiAgZmlnLmFsaWduICA9ICJjZW50ZXIiLAogIGZpZy5wYXRoICAgPSAiLi9GaWd1cmVzLyIsCiAgZmlnLnByb2Nlc3MgPSBmdW5jdGlvbihmaWxlbmFtZSl7CiAgICBuZXdfZmlsZW5hbWUgPC0gc3RyaW5ncjo6c3RyX3JlbW92ZShzdHJpbmcgPSBmaWxlbmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIi0xIikKICAgIGZzOjpmaWxlX21vdmUocGF0aCA9IGZpbGVuYW1lLCBuZXdfcGF0aCA9IG5ld19maWxlbmFtZSkKICAgIGlmZWxzZShmczo6ZmlsZV9leGlzdHMobmV3X2ZpbGVuYW1lKSwgbmV3X2ZpbGVuYW1lLCBmaWxlbmFtZSkKfSkKIyBzZXR3ZCgiL1VzZXJzL3ZpbGthbC93b3JrL0Jyb2xpZGVuc193b3JrL1Byb2plY3RzL2Jyb2xpZGVuXzUzMjUvcmVwb3J0cy9tYW51c2NyaXB0IikKYGBgCgpgYGB7ciBMb2FkIGRhdGEsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMjIyMjIyMjIyMjIyMjIyMjIwojIExPQUQgTElCUkFSSUVTICMKIyMjIyMjIyMjIyMjIyMjIyMjCnN1cHByZXNzV2FybmluZ3Moe3N1cHByZXNzTWVzc2FnZXMoe3N1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeSh0aWR5dmVyc2UpCiAgbGlicmFyeShlZGdlUikKICBsaWJyYXJ5KG9wZW54bHN4KQogIGxpYnJhcnkoc2NhbGVzKQogIGxpYnJhcnkoaWdyYXBoKQogIGxpYnJhcnkoZmdzZWEpCiAgbGlicmFyeShSQ29sb3JCcmV3ZXIpCiAgI3JlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJjemFybmV3c2tpL25pY2VScGxvdHMiLGZvcmNlPVQpCiAgbGlicmFyeShuaWNlUnBsb3RzKQp9KSAgfSkgIH0pCgojIyMjIyMjIyMKIyBQQVRIUyAjCiMjIyMjIyMjIwpyZXN1bHRfZGlyIDwtICIuL1N1cHBsLlRibC8iCmlmKCBpc0ZBTFNFKGRpci5leGlzdHMocmVzdWx0X2RpcikpICkgeyBkaXIuY3JlYXRlKHJlc3VsdF9kaXIscmVjdXJzaXZlID0gVFJVRSkgfQoKIyMjIyMjIyMjIyMjIwojIExPREEgREFUQSAjCiMjIyMjIyMjIyMjIyMKZGF0YXNldHNfYWxsX3NhbXBsZXMgPC0gcmVhZFJEUygiLi4vLi4vcmVzdWx0cy8wM19ub3JtYWxpemVfZGF0YV9vdXRwdXQvZGF0YXNldHNfYWxsX3NhbXBsZXMuUkRTIikKbWV0YWRhdGEgPC0gcmVhZC5jc3YoIi4uLy4uL2RhdGEvbWV0YWRhdGEuY3N2Iixyb3cubmFtZXMgPSAxLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKc2FtcGxlX3VzZSA8LSBtZXRhZGF0YSRJRApncm91cF9hbm5vdGF0aW9uIDwtIGZhY3RvcihzZXROYW1lcyhtZXRhZGF0YSRMdW1pbmFsX2dyLG1ldGFkYXRhJElEKSkKZ3IgPC0gIkx1bWluYWwiCgpTTk5fYmFjdGVyaWEgPC0gcmVhZC5jc3YoIi4uLy4uL3Jlc3VsdHMvMDRfY2x1c3RlcmluZ19vdXRwdXQvYmFjdGVyaWFfU05OX2dyYXBoLmNzdiIscm93Lm5hbWVzID0gMSkKYmFjX2NvbW11bml0aWVzIDwtIGFzLm1hdHJpeChyZWFkLmNzdigiLi4vLi4vcmVzdWx0cy8wNF9jbHVzdGVyaW5nX291dHB1dC9iYWN0ZXJpYWxfY29tbXVuaXRpZXMuY3N2Iixyb3cubmFtZXMgPSAyKSlbLDJdCgpwaWNydXN0X2RhdGEgPC0gcmVhZC50YWJsZSgiLi4vLi4vcmVzdWx0cy8wNV9waWNydXN0X291dHB1dC9vdXRfMjAyMS0wMS0yMC9LT19tZXRhZ2Vub21lX291dC9wcmVkX21ldGFnZW5vbWVfdW5zdHJhdC50c3YiLCBoZWFkZXIgPSBULHJvdy5uYW1lcyA9IDEpCmtvX25hbWVzIDwtIHJlYWRMaW5lcygnLi4vLi4vcmVzb3VyY2VzL3BpY3J1c3RfZGF0YS9rb19pbmZvLnRzdicpCmtlZ2dfbmFtZXMgPC0gcmVhZC5kZWxpbSgnLi4vLi4vcmVzb3VyY2VzL3BpY3J1c3RfZGF0YS9LRUdHX3BhdGh3YXlzX2luZm8udHN2JyxoZWFkZXIgPSBGKQpnbXRfbGlzdCA8LSBmZ3NlYTo6Z210UGF0aHdheXMoIi4uLy4uL3Jlc291cmNlcy9waWNydXN0X2RhdGEvS0VHR19wYXRod2F5c190b19LTy50c3YiKQpzb3VyY2UoIi4uLy4uL2NvZGUvZW5yaWNobWVudF9mdW5jdGlvbi5SIikKCkFBIDwtIGMoIig/PCFQaGVueWwpQWxhbmluZSAiID0gIkFsYSIsICJBcmdpbmluZSIgPSAiQXJnIiwgIkFzcGFyYWdpbmUiID0gIkFzbiIsICJBc3BhcnRpYyBhY2lkIiA9ICJBc3AiLCAiQ3lzdGVpbmUiID0gIkN5cyIsICJHbHV0YW1pYyBhY2lkIiA9ICJHbHUiLCAiR2x1dGFtaW5lIiA9ICJHbG4iLCAiR2x5Y2luZSIgPSAiR2x5IiwgIkhpc3RpZGluZSIgPSAiSGlzIiwgIkh5ZHJveHlwcm9saW5lIiA9ICJIeXAiLCAiSXNvbGV1Y2luZSIgPSAiSWxlIiwgIkxldWNpbmUiID0gIkxldSIsICJMeXNpbmUiID0gIkx5cyIsICJNZXRoaW9uaW5lIiA9ICJNZXQiLCAiUGhlbnlsYWxhbmluZSIgPSAiUGhlIiwgIlByb2xpbmUiID0gIlBybyIsICJQeXJvZ2x1dGFtYXRpYyIgPSAiR2xwIiwgIlNlcmluZSIgPSAiU2VyIiwgIlRocmVvbmluZSIgPSAiVGhyIiwgIlRyeXB0b3BoYW4iID0gIlRycCIsICJUeXJvc2luZSIgPSAiVHlyIiwgIlZhbGluZSIgPSAiVmFsIikKQUFfIDwtIHNldF9uYW1lcyhBQSwgbWFwX2NocihuYW1lcyhBQSksIH4gcGFzdGUwKCIoP2kpIiwgLikpKSAjIG1ha2UgY2FzZSBpbnNlbnNpdGl2ZQoKIyMjIyMjIyMjIyMjIyMjIyMKIyBDT0xPUiBQQUxFVFRTICMKIyMjIyMjIyMjIyMjIyMjIyMKcGFsIDwtIGMoICIjMDA3MkIyIiwgIiMwMDlFNzMiLCIjRDU1RTAwIiwgIiNDQzc5QTciLCAiI0U2OUYwMCIsICIjOTk5OTk5IikKdGF4YV9wYWwgPC0gYyhSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoOCwiUGFzdGVsMiIpLFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCg4LCJQYXN0ZWwxIiksImdyZXk5MCIpCmJhY3RfcGFsIDwtIGMoJyM4OENDRUUnLCAnIzQ0QUE5OScsICcjMTE3NzMzJywgJyMzMzIyODgnLCAnI0REQ0M3NycsICcjOTk5OTMzJywnI0NDNjY3NycsICcjODgyMjU1JywgJyNBQTQ0OTknLCAnI0RERERERCcpCmBgYAoKCmBgYHtyIEZpZ3VyZSAyLCBmaWcuaGVpZ2h0PTh9CgpsYXlvdXQobWF0cml4KGMoMSwxLDIsMiwyLAogICAgICAgICAgICAgICAgMSwxLDMsMywzLAogICAgICAgICAgICAgICAgMSwxLDQsNCw0LAogICAgICAgICAgICAgICAgNSw2LDcsOCw5LAogICAgICAgICAgICAgICAgNSw2LDcsOCwxMCksCiAgICAgICAgICAgICAgbnJvdyA9IDUsbmNvbCA9IDUsYnlyb3cgPSBUKSwgd2lkdGhzID0gYyguNiwxLjUsLjIsMS45LDIpLAogICAgICAgaGVpZ2h0cyA9IGMoLjY4LC41NiwuNTYsMSwxLjUpKQpmaWdsYWJlbHMgPC0gbGV0dGVycwoKCiMjIyBBCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBCQUNURVJJQUwgQ09NTVVOSVRJRVMgIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmJhY19jb21tdW5pdGllcyA8LSBmYWN0b3IocGFzdGUwKCJCQyIsc3ByaW50ZigiJTAyZCIsYmFjX2NvbW11bml0aWVzKSkpCmdCIDwtIGdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeChhcy5tYXRyaXgoU05OX2JhY3RlcmlhKSwgbW9kZSA9ICJ1bmRpcmVjdGVkIixkaWFnID0gRix3ZWlnaHRlZCA9IFQpCiBzZXQuc2VlZCgxKQpsQiA8LSBsYXlvdXRfbmljZWx5KGdCLG5pdGVyPTMwMDAsc3RhcnQudGVtcD0zMCkKCnBhcihtYXI9YygwLDAsMy41LDUpKQpwbG90KCBnQiAsIHZlcnRleC5sYWJlbC5jZXg9MC4wMDAwMDEgLCB2ZXJ0ZXguY29sb3IgPSBiYWN0X3BhbFtmYWN0b3IoYmFjX2NvbW11bml0aWVzKV0gLAogICAgICBlZGdlLndpZHRoPSAgKCBFKGdCKSR3ZWlnaHQgLyBtYXgoRShnQikkd2VpZ2h0KSkgLAogICAgICB2ZXJ0ZXguc2l6ZT0xMCwKICAgICAgZWRnZS5jb2xvcj1jb2xvclJhbXBQYWxldHRlKGMoImdyZXk5NSIsImJsYWNrIikpKDkwKSBbIHJvdW5kKCBFKGdCKSR3ZWlnaHQgLyBtYXgoRShnQikkd2VpZ2h0KSAqIDg4ICkrMSBdICxsYXlvdXQ9bEIpCnRpdGxlKG1haW4gPSAiICAgICAgICAgICBCYWN0ZXJpYWwgQ29tbXVuaXRpZXMtIExvdXZhaW4iLCBsaW5lID0gMSwgY2V4Lm1haW4gPSAxKQpsZWdlbmQocGFyKCJ1c3IiKVsyXSxwYXIoInVzciIpWzRdLHRpdGxlLmFkaiA9IDAsCiAgICAgICBsZWdlbmQgPSBsZXZlbHMoZmFjdG9yKGJhY19jb21tdW5pdGllcykpLHhqdXN0ID0gMCx5anVzdCA9IDEsCiAgICAgICBidHkgPSAibiIscGNoID0gMjEscHQuYmcgPSBiYWN0X3BhbCxwdC5jZXggPSAxLHhwZD1UKQoKI2FkZCBsYWJlbAphZGRfbGV0dGVyKGZpZ2xhYmVsc1sxXSk7IGZpZ2xhYmVscyA8LSBmaWdsYWJlbHNbLTFdCgoKIyMjIEIKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBXRVQgU01FQVIgQkFDVEVSSUFMIFZBTElEQVRJT04gIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpwYXIobWFyPWMoMSw1LDQsMSkpICNiLGwsdCxyCnggPC0gZGF0YXNldHNfYWxsX3NhbXBsZXNbWyJBU1ZfTHVtaW5hbF9ub3JtYWxpemVkIl1dCnhMIDwtIHJvd3N1bSh4ICwgZ3JlcGwoIkwuIixyb3duYW1lcyh4KSkgKVsyLF0gLyBzdW0oZ3JlcGwoIkwuIixyb3duYW1lcyh4KSkpCnhNIDwtIHJvd3N1bSh4ICwgZ3JlcGwoIk1vYmlsdW5jdXMiLHJvd25hbWVzKHgpKSApWzIsXSAvIHN1bShncmVwbCgiTW9iaWx1bmN1cyIscm93bmFtZXMoeCkpKQp4RyA8LSByb3dzdW0oeCAsIGdyZXBsKCJHYXJkbmVyZWxsYSIscm93bmFtZXMoeCkpIClbMixdIC8gc3VtKGdyZXBsKCJHYXJkbmVyZWxsYSIscm93bmFtZXMoeCkpKQoKbUwgPC0gbWV0YWRhdGEkQlZfTGFjdG9iYWNpbGx1c192MyA7IG1MW2lzLm5hKG1MKV0gPC0gMAptTSA8LSBtZXRhZGF0YSRCVl9Nb25iaWxpY3VzX3YzICAgIDsgbU1baXMubmEobU0pXSA8LSAwCm1HIDwtIG1ldGFkYXRhJEJWX1ZhZ2luYWxfR2FyZGFfdjMgOyBtR1tpcy5uYShtRyldIDwtIDAKCmJhcmxpc3QoIGRhdGEgPSByYmluZChXUz1tTCAsCiAgICAgICAgICAgICAgICAgICAgICAiMTZTIGNvdW50cyI9eEwpLAogICAgICAgICBnZW5lcyA9IGMoIldTIiwiMTZTIGNvdW50cyIpLAogICAgICAgICBjbHVzdGVyaW5nID0gZ3JvdXBfYW5ub3RhdGlvbixzcnQ9MjAsCiAgICAgICAgIGRyYXdfbWVhbl9saW5lcz1GLGNvbCA9IHBhbCkKbXRleHQoIkxhY3RvYmFjaWxsdXMiLCBzaWRlPTMsIGxhcyA9IDEsIGNleCA9IC42LCBsaW5lID0gLjUseHBkPVQpCm10ZXh0KCJXZXQgU21lYXIgQmFjdGVyaWEgVmFsaWRhdGlvbiIsIHNpZGU9MywgbGFzID0gMSwgY2V4ID0gLjcsIGxpbmUgPTEuNSx4cGQ9VCwgZm9udCA9IDIpCgojYWRkIGxhYmVsCmFkZF9sZXR0ZXIoZmlnbGFiZWxzWzFdKTsgZmlnbGFiZWxzIDwtIGZpZ2xhYmVsc1stMV0KCnBhcihtYXI9YygxLDUsMiwxKSkgI2IsbCx0LHIKYmFybGlzdCggZGF0YSA9IHJiaW5kKFdTPW1NICwKICAgICAgICAgICAgICAgICAgICAgICIxNlMgY291bnRzIj14TSksCiAgICAgICAgIGdlbmVzID0gYygiV1MiLCIxNlMgY291bnRzIiksCiAgICAgICAgIGNsdXN0ZXJpbmcgPSBncm91cF9hbm5vdGF0aW9uLHNydD0yMCwKICAgICAgICAgZHJhd19tZWFuX2xpbmVzPUYsY29sID0gcGFsKQptdGV4dCgiTW9iaWx1bmN1cyIsIHNpZGU9MywgbGFzID0gMSwgY2V4ID0gLjYsIGxpbmUgPSAuNSx4cGQ9VCkKCmJhcmxpc3QoIGRhdGEgPSByYmluZChXUz1tRyAsCiAgICAgICAgICAgICAgICAgICAgICAiMTZTIGNvdW50cyI9eEcpLAogICAgICAgICBnZW5lcyA9IGMoIldTIiwiMTZTIGNvdW50cyIpLAogICAgICAgICBjbHVzdGVyaW5nID0gZ3JvdXBfYW5ub3RhdGlvbixzcnQ9MjAsCiAgICAgICAgIGRyYXdfbWVhbl9saW5lcz1GLGNvbCA9IHBhbCkKbXRleHQoIkdhcmRuZXJlbGxhIiwgc2lkZT0zLCBsYXMgPSAxLCBjZXggPSAuNiwgbGluZSA9IC41LHhwZD1UKQoKIyMjIEMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgQkFDVEVSSUFMIFBJQ1JVU1QgIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMKcGljcnVzdF9kYXRhIDwtIHJvdW5kKHBpY3J1c3RfZGF0YVsgcm93U3VtcyhwaWNydXN0X2RhdGE+NSk+PTEwLCBdKQpUUlggPC0gZGF0YXNldHNfYWxsX3NhbXBsZXNbWyJUaXNzdWVfUk5Bc2VxX1YzX25vcm1hbGl6ZWQiXV0Kc2FtcGxlX3VzZSA8LSBpbnRlcnNlY3QoY29sbmFtZXMoVFJYKVtjb2xTdW1zKFRSWCkhPTBdLCBjb2xuYW1lcyhwaWNydXN0X2RhdGEpKQoKa29fbmFtZXMgPC0gc3Ryc3BsaXQoa29fbmFtZXMsIlx0IikKa29fbmFtZXMgPC0gc2V0TmFtZXModW5saXN0KGxhcHBseShrb19uYW1lcyxmdW5jdGlvbih4KXt4WzJdfSkpLCB1bmxpc3QobGFwcGx5KGtvX25hbWVzLGZ1bmN0aW9uKHgpe3hbMV19KSkpCmtvX25hbWVzIDwtIHN1YigiIFtbXS4qIiwiIixrb19uYW1lcykKa29fbmFtZXMgPC0gc3ViKCIuKjsgIiwiIixrb19uYW1lcykKCm5hbWVkX3BpY3J1c3RfZGF0YSA8LSByb3dzdW0oIHBpY3J1c3RfZGF0YSAsIGdyb3VwID0ga29fbmFtZXNbIHJvd25hbWVzKHBpY3J1c3RfZGF0YSkgXSApCm5hbWVkX3BpY3J1c3RfZGF0YSA8LSByb3VuZChuYW1lZF9waWNydXN0X2RhdGFbIHJvd1N1bXMobmFtZWRfcGljcnVzdF9kYXRhPjUpPj01LCBdKQoKZGVzaWduIDwtIG1vZGVsLm1hdHJpeCh+IGdyb3VwX2Fubm90YXRpb24rQ29udHJhY2VwdGlvbitISVZzdGF0dXMsIAogICAgICAgICAgICAgICAgICAgICAgIGRhdGE9bWV0YWRhdGFbc2FtcGxlX3VzZSxdKQoKeSA8LSBER0VMaXN0KGNvdW50cz1uYW1lZF9waWNydXN0X2RhdGFbLHNhbXBsZV91c2VdKQp5IDwtIGNhbGNOb3JtRmFjdG9ycyh5LG1ldGhvZCA9ICJUTU0iKQp5IDwtIGVzdGltYXRlR0xNQ29tbW9uRGlzcCh5KQp5IDwtIGVzdGltYXRlR0xNVGFnd2lzZURpc3AoeSkKZml0IDwtIGdsbUZpdCh5LCBkZXNpZ24pCgpscnQgPC0gZ2xtTFJUKGZpdCxjb2VmPTI6NSkKdG9wIDwtIHRvcFRhZ3MobHJ0LGFkanVzdC5tZXRob2QgPSAiQkgiLG4gPSAiYWxsIixzb3J0LmJ5ID0gInAudmFsdWUiKVtbMV1dCmNvbG5hbWVzKHRvcCkgPC0gc3ViKCJncm91cF9hbm5vdGF0aW9uIiwiIixjb2xuYW1lcyh0b3ApKQp0b3AgPC0gY2JpbmQoTG9nRkMuaW50ZXJjZXB0PTAsdG9wKSAjIHdpdGggaW50ZXJjZXB0Cgp0b3BfZGdlIDwtICh0b3AkRkRSIDwgMWUtNSkgIyYgKHJvd1N1bXMoIGFicyh0b3BbLGdyZXAoImxvZ0ZDIixjb2xuYW1lcyh0b3ApKV0pID49IGxvZzIoMS41KSApID49IDEpCnRvcF9kZ2UgPC0gcm93bmFtZXModG9wKVsgdG9wX2RnZSBdCgp0b3BfcGljcnVzdF9kYXRhIDwtIGVkZ2VSOjpjcG0oeSlbdG9wX2RnZSwgXQpuIDwtIGNvbG5hbWVzKHRvcF9waWNydXN0X2RhdGEpCnRvcF9waWNydXN0X2RhdGEgPC0gdChhcHBseSh0b3BfcGljcnVzdF9kYXRhLDEsZnVuY3Rpb24oeCl7c2NhbGUoeCxULFQpfSkpCnRvcF9waWNydXN0X2RhdGFbdG9wX3BpY3J1c3RfZGF0YSA+IDVdIDwtIDUKY29sbmFtZXModG9wX3BpY3J1c3RfZGF0YSkgPC0gbgoKIyBEZW5kb2dyYW0KcGFyKG1hcj1jKDQsLjEsNCwuMikpICNiLGwsdCxyCmggPC0gaGNsdXN0KCBhcy5kaXN0KCAoMS0gY29yKHQodG9wX3BpY3J1c3RfZGF0YSkpKS8yICksIG1ldGhvZCA9ICJ3YXJkLkQyIikKcGxvdCggcmV2KGFzLmRlbmRyb2dyYW0oaCkpICx4bGltPWMobWF4KGgkaGVpZ2h0KSwtMSksIGhvcml6ID0gVCwgbGVhZmxhYiA9ICJub25lIix4YXhzPSJpIix5YXhzPSJpIixheGVzPVQpCnRpdGxlKHhsYWI9IkhlaWdodCIsIGxpbmU9MikKY3V0b2ZmIDwtIDQKYWJsaW5lKHY9Y3V0b2ZmLHhwZD1GLGNvbD0icmVkIixsdHk9MikKZ2VuZV9tb2R1bGUgPC0gY3V0cmVlKGgsIGggPSBjdXRvZmYpCgojYWRkIGxhYmVsCmFkZF9sZXR0ZXIoZmlnbGFiZWxzWzFdKTsgZmlnbGFiZWxzIDwtIGZpZ2xhYmVsc1stMV0KCnBvaW50cyggcmVwKC0uNyxsZW5ndGgoZ2VuZV9tb2R1bGUpKSwKICAgICAgICBzZXEobGVuZ3RoKGdlbmVfbW9kdWxlKSwxLGxlbmd0aC5vdXQgPSBsZW5ndGgoZ2VuZV9tb2R1bGUpKSwKICAgICAgICBjb2w9dGF4YV9wYWxbZmFjdG9yKGdlbmVfbW9kdWxlW2gkb3JkZXJdKV0sCiAgICAgICAgcGNoPTE1LGNleD0xLHhwZD1GKQoKIyBIZWF0bWFwCmltYWdlKCB0KHRvcF9waWNydXN0X2RhdGFbaCRvcmRlcixdW25yb3codG9wX3BpY3J1c3RfZGF0YSk6MSxvcmRlcihncm91cF9hbm5vdGF0aW9uW3NhbXBsZV91c2VdKV0pLAogICAgICAgY29sID0gY29sb3JSYW1wUGFsZXR0ZShjKCJuYXZ5IiwibmF2eSIsImdyZXk5NSIsImZpcmVicmljazQiLCJmaXJlYnJpY2s0IikgKSg5OSksCiAgICAgICBicmVha3MgPSBzZXEoLTUsNSxsZW5ndGgub3V0ID0gMTAwKSxheGVzPUYpCm10ZXh0KCIgICAgICAgICAgICAgICAgICAgICAgICBEaWZmZXJlbnRpYWxseSBleHByZXNzZWQgS0VHRyBPbnRvbG9neSB0ZXJtcyBhbmQgcGF0aHdheXMiLCAKICAgICAgc2lkZT0zLCBsYXMgPSAxLCBjZXggPSAuNywgbGluZSA9MS41LHhwZD1ULCBmb250ID0gMikKdGl0bGUoeGxhYj0iU3R1ZHkgR3JvdXBzIiwgbGluZT0xKQoKIyBTdHVkeSBHcm91cCBicmFja2V0cwplbmQgPC0gdGFibGUocGFsW2ZhY3Rvcihncm91cF9hbm5vdGF0aW9uKVsgb3JkZXIoZ3JvdXBfYW5ub3RhdGlvbltzYW1wbGVfdXNlXSldXSApCmVuZCA8LSBlbmRbb3JkZXIoZmFjdG9yKG5hbWVzKGVuZCksIGxldmVscyA9IHBhbCkpXQplbmQgPC0gbWFwX2RibChjdW1zdW0oZW5kKSwgfiAoLnggKiAocGFyKCJ1c3IiKVsyXSkvbmNvbChwaWNydXN0X2RhdGFbc2FtcGxlX3VzZV0pLS4wMTUpICkKc3RhcnQgPC0gYygwLCBtYXBfZGJsKGVuZFsxOjRdLCB+LngrMC4wMTcpKQplbmQgPC0gYyhlbmRbMTpsZW5ndGgoZW5kKS0xXSwgcGFyKCJ1c3IiKVsyXS0uMDA1KQoKYXhpcygxLCBhdCA9IGMoc3RhcnQsIGVuZCksIGxhYmVsID0gRiwgcG9zID0xLjAyMSAsIHhwZD1ULCBjb2w9IndoaXRlIixjb2wudGlja3M9ImJsYWNrIikKbWFwMihzdGFydCwgZW5kLCB+bGluZXMoeD1jKC55LCAueCksIHk9IGMoMS4wMjEsIDEuMDIxKSwgeHBkPVQsIGNleD0uOCkpCiMgdGV4dCBsYWJlbHMKdCA8LSBtYXAyKHN0YXJ0LCBlbmQsIH4oLngrLnkpLzIpCm1hcDIodCxsZXZlbHMoZmFjdG9yKGdyb3VwX2Fubm90YXRpb24pKSwgfnRleHQoeD0ueCwgeT0xLjAzNywgLnkseHBkPVQsIGNleD0uOCkpCgojIE1ldGEgYmFyIGluZm8KcGFyKG1hcj1jKDMsLjEsMiwuMSkpICNib3R0b20sIGxlZnQsIHRvcCwgcmlnaHQgIAptYXAoYyguMDAyMSwgLjAwNiksIH5wb2ludHMoIHNlcSgwLDEsbGVuZ3RoLm91dCA9IGxlbmd0aChzYW1wbGVfdXNlKSksCiAgICAgICAgcmVwKHBhcigidXNyIilbNF0sbGVuZ3RoKHNhbXBsZV91c2UpKSsueCwgcGNoPTE1LCBjZXg9LjMseHBkPUYsCiAgICAgICAgY29sPXBhbFtmYWN0b3IoZ3JvdXBfYW5ub3RhdGlvbltzYW1wbGVfdXNlXSlbIG9yZGVyKGdyb3VwX2Fubm90YXRpb25bc2FtcGxlX3VzZV0pXV0pICkKcG9pbnRzKCBzZXEoMCwxLGxlbmd0aC5vdXQgPSBuY29sKHRvcF9waWNydXN0X2RhdGEpKSwgCiAgICAgICAgcmVwKHBhcigidXNyIilbM10sbmNvbCh0b3BfcGljcnVzdF9kYXRhKSktLjAwOCwgcGNoPTE1LCB4cGQ9RixjZXg9LjMsCiAgICAgICAgY29sPSBjKCJ0b21hdG8iLCJvcmFuZ2UiLCIjZDdkN2Q3IilbYXMuZmFjdG9yKG1ldGFkYXRhJEJWX0RpYWdub3Npc192MyldW29yZGVyKCBncm91cF9hbm5vdGF0aW9uW3NhbXBsZV91c2VdKV0gKQpwb2ludHMoIHNlcSgwLDEsbGVuZ3RoLm91dCA9IG5jb2wodG9wX3BpY3J1c3RfZGF0YSkpLCAKICAgICAgICByZXAocGFyKCJ1c3IiKVszXSxuY29sKHRvcF9waWNydXN0X2RhdGEpKS0uMDI0LCBwY2g9MTUsIHhwZD1GLGNleD0uMywKICAgICAgICBjb2w9IGMoIiNkN2Q3ZDciLCJ0b21hdG8iKVthcy5mYWN0b3IobWV0YWRhdGEkSElWc3RhdHVzKV1bb3JkZXIoZ3JvdXBfYW5ub3RhdGlvbltzYW1wbGVfdXNlXSldKQoKbmFtZXMoZ210X2xpc3QpIDwtIGtlZ2dfbmFtZXMgWyBtYXRjaCAoIG5hbWVzKGdtdF9saXN0KSwgYXMuY2hhcmFjdGVyKGtlZ2dfbmFtZXNbLDFdKSApICwgMiBdCmdtdF9saXN0IDwtIGxhcHBseShnbXRfbGlzdCAsIGZ1bmN0aW9uKHgpewogIG5hLm9taXQoa29fbmFtZXNbeF0pCn0pCnJlc19saXN0IDwtIGxhcHBseSggdW5pcXVlKCBnZW5lX21vZHVsZVtoJG9yZGVyXSApLGZ1bmN0aW9uKHgpewogIHRlbXAgPC0gY29tcHV0ZV9lbnJpY2htZW50KGdlbmVzID0gbmFtZXMoZ2VuZV9tb2R1bGUpW2dlbmVfbW9kdWxlPT14XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdtdF9saXN0ID0gZ210X2xpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fdGVybXNfcGF0aHdheSA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4X3Rlcm1zX3BhdGh3YXkgPSAzMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fb3ZlcmxhcCA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb3J0X2J5X3B2YWx1ZSA9IFQpCiAgdGVtcCA8LSB0ZW1wWyFncmVwbCgiUkVHVUxBVElPTiIscm93bmFtZXModGVtcCkpLF0KICByZXR1cm4odGVtcCkKfSApCnJlcyA8LSBtYXAocmVzX2xpc3QsIH5yb3duYW1lc190b19jb2x1bW4oLngsIHZhciA9ICJUZXJtcyIpKSAlPiUgCiAgICBzZXRfbmFtZXMoLiwgcGFzdGUwKCJtb2R1bGVfIiwgIHNlcV9hbG9uZyhyZXNfbGlzdCkpKSAKCm5hbWVzKHJlc19saXN0KSA8LSB1bmlxdWUoIGdlbmVfbW9kdWxlW2gkb3JkZXJdICkKCnB2YWx1ZXMgPC0gdW5saXN0KGxhcHBseShyZXNfbGlzdCxmdW5jdGlvbih4KXsgLWxvZzEwKGFzLm51bWVyaWMoeCRwdmFsdWUpKSBbMTozXSB9KSkKcHZhbHVlc1tpcy5uYShwdmFsdWVzKV0gPC0gMAp0ZXJtcyA8LSB1bmxpc3QobGFwcGx5KHJlc19saXN0LGZ1bmN0aW9uKHgpeyByb3duYW1lcyh4KSBbMTozXSB9KSkKdGVybXNbaXMubmEodGVybXMpXSA8LSAiIgp0ZXJtcyA8LSBzdHJpbmdyOjpzdHJfcmVwbGFjZV9hbGwoc3RyaW5nID0gdGVybXMscGF0dGVybj0gQUFfKQpnZW5lcyA8LSB1bmxpc3QobGFwcGx5KHJlc19saXN0LGZ1bmN0aW9uKHgpeyB4JGdlbmVzIFsxOjNdIH0pKQpnZW5lc1tpcy5uYShnZW5lcyldIDwtICIiCgojcHZhbHVlcyA8LSBzZXROYW1lcyhwdmFsdWVzLHBhc3RlMCh0ZXJtcywiICgiLGdlbmVzLCIpIikpCnB2YWx1ZXMgPC0gc2V0TmFtZXMocHZhbHVlcyxwYXN0ZTAodGVybXMsIiIpKQptb2R1bGVfY29sb3IgPC0gdW5saXN0KGxhcHBseShuYW1lcyhyZXNfbGlzdCksZnVuY3Rpb24oeCl7IHJlcCh4LDMpIH0pKQoKdGVtcCA8LSBmYWN0b3IoIHJldihnZW5lX21vZHVsZSksIGxldmVscyA9IHVuaXF1ZSggZ2VuZV9tb2R1bGVbaCRvcmRlcl0gKSkKZGYgPC0gZGF0YS5mcmFtZSh0ZW1wLHRlbXApCnBhcihtYXI9Yyg0LDAsNCwuMSkpICNib3R0b20sIGxlZnQsIHRvcCwgcmlnaHQgIApxdWlldGx5KHBsb3Rfc2Fua2V5KSggZGYsIHBhbCA9IHRheGFfcGFsW2FzLm51bWVyaWMobGV2ZWxzKHRlbXApKV0sIHVzZV93MiA9IEYsIHBsb3RfbGFiZWxzID0gVCxnYXB2ID0gLjAxLGdhcDJ2ID0gMCAseGF4cz0iaSIseWF4cz0iaSIscGxvdF93ZWlnaHRzID0gRikkb3V0cHV0CiAKCiMgcGFyKCJ1c3IiKSh4bWluLCB5bWluLCB4bWF4LCB5bWF4KQp0ZXh0KHBhcigidXNyIilbMV0scGFyKCJ1c3IiKVszXS0uMDA4LGxhYmVscyA9ICIgICAgIEJWIixjZXg9LjYsIHhwZD1UKQp0ZXh0KHBhcigidXNyIilbMV0scGFyKCJ1c3IiKVszXS0uMDI0LGxhYmVscyA9ICIgICAgICBISVYiLGNleD0uNiwgeHBkPVQpCgpwYXIobWFyPWMoNCwuMSw0LDcpKQpiYXJwbG90KCByZXYocHZhbHVlcykgLCBob3JpeiA9IFQgLCBjb2w9IHRheGFfcGFsW2ZhY3RvcihyZXYobW9kdWxlX2NvbG9yKSldLAogICAgICAgICB4YXhzPSJpIix5YXhzPSJpIix4bGltPWMoMCwxLjIqbWF4KHB2YWx1ZXMpKSx5bGFiPSIiLGxhcz0xLG5hbWVzLmFyZyA9ICIiKQp0aXRsZSh4bGFiPSItbG9nMTAocC12YWx1ZSkiLCBsaW5lPTIpCmFibGluZSh2PTIseHBkPUYsY29sPSJyZWQiLGx0eT0yKQpwb2ludHMoIHJldihwdmFsdWVzKSwgc2VxKDEsbGVuZ3RoKHB2YWx1ZXMpICkqMS4yLS41LHBjaD0yMSxiZz10YXhhX3BhbFtmYWN0b3IocmV2KG1vZHVsZV9jb2xvcikpXSApCnRleHQoIHJldihwdmFsdWVzKStzdHJ3aWR0aCgiTSIpICwgc2VxKDEsbGVuZ3RoKHB2YWx1ZXMpICkqMS4yLS41LCBhZGo9MCxjZXg9MSx4cGQ9VCwKICAgICAgbGFiZWxzID0gcmV2KGdzdWIoIlxcKFxcKSIsIiIsbmFtZXMocHZhbHVlcykpICkseHBkPVQpCnRleHQoIHJldihwdmFsdWVzKStzdHJ3aWR0aCgiTSIpICwgc2VxKDEsbGVuZ3RoKHB2YWx1ZXMpICkqMS4yLS41LCBhZGo9MCxjZXg9MSwKICAgICAgbGFiZWxzID0gcmV2KG5hbWVzKHB2YWx1ZXMpKSxjb2wgPSBwYXN0ZTAodGF4YV9wYWxbZmFjdG9yKHJldihtb2R1bGVfY29sb3IpKV0sOTApICx4cGQ9VCkKCgojIyMgRAojIyMjIyMjIyMjIyMjIyMjCiMgUElDUlVTVCBVTUFQICMKIyMjIyMjIyMjIyMjIyMjIwoKIyBiYXNlZCBvbiAxMDggc2FtcGxlcyB1c2VkIGluIG1hbnVzY3JpcHQ6CnBhcihtYXI9YygyLjUsMi41LDMsNCkpICNiLGwsdCxyCnNldC5zZWVkKDEpClVNQVBfcGljcnVzdCA8LSB1d290Ojp1bWFwKHQobmFtZWRfcGljcnVzdF9kYXRhWyxzYW1wbGVfdXNlXSksbl9uZWlnaGJvcnMgPSAyMCwKICAgICAgICAgICAgICAgICBtZXRyaWMgPSAiY29ycmVsYXRpb24iLG1pbl9kaXN0ID0gLjIsc3ByZWFkID0gLjQsCiAgICAgICAgICAgICAgICAgcmVwdWxzaW9uX3N0cmVuZ3RoID0gLjQsbmVnYXRpdmVfc2FtcGxlX3JhdGUgPSAzKQpwbG90KFVNQVBfcGljcnVzdCxiZz1wYWxbZmFjdG9yKG1ldGFkYXRhJEx1bWluYWxfZ3IpXSwKICAgICBwY2g9MjEsZnJhbWU9RixheGVzPUYpCnRpdGxlKG1haW4gPSAiVG9wIEZ1bmN0aW9uYWwgUHJvZmlsZS0gVU1BUCIsIGxpbmUgPSAwLjUsIGNleC5tYWluID0gMSkKI210ZXh0KCJVTUFQMSIsIHNpZGU9MSwgbGluZT0wLjcsIGNleD0wLjgpCiNtdGV4dCgiVU1BUDIiLCBzaWRlPTIsIGxpbmU9MC43LCBjZXg9MC44KQpsZWdlbmQocGFyKCJ1c3IiKVsyXSxwYXIoInVzciIpWzRdLHRpdGxlLmFkaiA9IDAsCiAgICAgICBsZWdlbmQgPSBsZXZlbHMoZmFjdG9yKG1ldGFkYXRhJEx1bWluYWxfZ3IpKSx4anVzdCA9IDAseWp1c3QgPSAxLAogICAgICAgYnR5ID0gIm4iLHBjaCA9IDIxLHB0LmJnID0gcGFsLHB0LmNleCA9IDEseHBkPVQsIGNleD0xKQoKI2FkZCBsYWJlbAphZGRfbGV0dGVyKGZpZ2xhYmVsc1sxXSk7IGZpZ2xhYmVscyA8LSBmaWdsYWJlbHNbLTFdCgoKZW1wdHlfcGxvdCgpCmBgYAoKKipGaWd1cmUgMi4gSWRlbnRpZmljYXRpb24gb2YgYmFjdGVyaWFsIGNvbW11bml0aWVzIGFuZCBmdW5jdGlvbmFsIHByb2ZpbGVzIGluIHRoZSBsdW1pbmFsIHNhbXBsZXMuKiogVGhlIGx1bWluYWwgc3R1ZHkgc2FtcGxlcyB3ZXJlIGFzc2Vzc2VkIGZvciBiYWN0ZXJpYWwgY29tbXVuaXRpZXMgYW5kIGZ1bmN0aW9uYWwgcHJvZmlsZXMuICoqYSoqIEJhY3RlcmlhbCBjb21tdW5pdHkgZW1iZWRkaW5nIG9mIDUtU05OIGdyYXBoIGNsdXN0ZXJlZCB1c2luZyBMb3V2YWluIGNvbW11bml0eSBkZXRlY3Rpb24gYWxnb3JpdGhtIGJhc2VkIG9uIGx1bWluYWwgYmFjdGVyaWFsIGFidW5kYW5jZXMuICoqYioqIFdldCBzbWVhciBjb3VudHMgb2YgdGhlIGdlbmVyYSBMYWN0b2JhY2lsbHVzLCBNb2JpbHVuY3VzLCBhbmQgR2FyZG5lcmVsbGEgd2l0aCBjb3JyZXNwb25kaW5nIDE2UyByZWFkIGNvdW50cy4gKipjKiogRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgd2FzIGFwcGxpZWQgdG8gUElDUlVTdDIgcHJlZGljdGVkIEtPIHRlcm1zIGFjcm9zcyB0aGUgZml2ZSBsdW1pbmFsIHN0dWR5IGdyb3Vwcy4gUmVzdWx0aW5nIHNpZ25pZmljYW50IChGRFIgPCAxIMOXIDEw4oiSNSkgS08gdGVybXMgd2VyZSBkaXZpZGVkIGludG8gc2V2ZW4gbW9kdWxlcyBieSBoaWVyYXJjaGljYWwgYWdnbG9tZXJhdGl2ZSBjbHVzdGVyaW5nIHVzaW5nIGludmVyc2UgUGVhcnNvbuKAmXMgY29ycmVsYXRpb24gYXMgZGlzdGFuY2UgbWVhc3VyZSBhbmQgV2FyZOKAmXMgbWV0aG9kICjigJx3YXJkLiBEMuKAnSkgZm9yIGxpbmthZ2UuIEVucmljaG1lbnQgYW5hbHlzaXMgd2FzIHBlcmZvcm1lZCBvbiBlYWNoIG1vZHVsZSBhbmQgdGhlIHRocmVlIG1vc3Qgc2lnbmlmaWNhbnQgS0VHRyBwYXRod2F5cyB3ZXJlIGluY2x1ZGVkIGluIHRoZSBoZWF0bWFwLiBkIFVuaWZvcm0gTWFuaWZvbGQgQXBwcm94aW1hdGlvbiAoVU1BUCkgb2YgdGhlIHByZWRpY3RlZCBLTyB0ZXJtcwoKYGBge3IgU3VwcGwuIFRhYmxlIDIufQojc2F2ZVJEUyggeSAsICIuLi8uLi8uLi9yZXN1bHRzL1BpY3J1c3RfRWRnZVJfZXN0aW1hdGlvbnMucmRzIiApCnRvcCA8LSB0b3AgJT4lIGFzX3RpYmJsZShyb3duYW1lcz0iS08gVGVybXMiKSAKCnBhZ2VzIDwtIGxpc3QobGlzdCgiQWNyb3NzIj10b3ApLCByZXMpICU+JSBmbGF0dGVuKCkKd3JpdGUueGxzeChwYWdlcywgZmlsZT1wYXN0ZTAocmVzdWx0X2RpciwiU3VwcGwuVGJsLjAyIiwiLnhsc3giKSkKYGBgCg==