Initialization

library(pagoda2)
library(Matrix)
library(ggplot2)
library(cowplot)
library(dplyr)
library(ggrepel)
require(parallel)
library(ggrastr)
library(conos)

Load Rahul’s 2019 integration pbmc dataset, as described here: https://satijalab.org/signac/articles/pbmc_vignette.html

#so <- readRDS("pbmc_10k_v3.rds")
load("seurat.RData") # cd, cd.var,meta, umap
ann <- as.factor(setNames(meta$celltype,rownames(meta)))
p2 <- Pagoda2$new(cd,log.scale=T, n.cores=30)
#p2 <- con$samples$pbmc60
p2$adjustVariance(plot=T,gam.k=10)
p2$calculatePcaReduction(nPcs=50,n.odgenes=3e3)
p2$makeKnnGraph(k=40,type='PCA',center=T,distance='cosine');
p2$getEmbedding(type='PCA',embeddingType='tSNE',n.cores=30)
a1 <- p2$plotEmbedding(type='PCA',embeddingType='tSNE',groups=ann,alpha=0.4, font.size=c(3,4))
#pdf(file='pmbc10k_tsne.pdf',width=3,height=3); print(a1); dev.off()
a1

p2$getKnnClusters(type='PCA',method=leiden.community,r=2,name='leiden')
p2$plotEmbedding(type='PCA',embeddingType='tSNE',clusterType='leiden',title='clusters (tSNE)')

de <- p2$getDifferentialGenes(type='PCA',verbose=T,groups=as.factor(ann),append.auc=T,upregulated.only = T)

Variance ranking and expression patterns

x <- p2$misc$varinfo
x$rank <- rank(-1*x$res)
x <- x[order(x$lp,decreasing=T),]
x <- x[order(x$res,decreasing=T),]
x <- x[order(x$lp,decreasing=F),]
x <- x[x$rank<5e3,]
ggplot(x,aes(x=rank,y=res)) + geom_point(color=adjustcolor(2,alpha=0.4),size=0.2) +theme_bw() + xlab("overdispersed gene rank") + ylab("residual variance")

gns <- c("S100A9",'HES4',"GAS6")
#gns <- rownames(x)[c(1,2000,2003)]
po <- lapply(gns,function(g) {
  p2$plotEmbedding(gene=g,size=0.2,alpha=0.7,raster=T,raster.dpi=300)+
  #conos::embeddingPlot(p2$embeddings$PCA$tSNE,colors=p2$counts[,g],size=0.2,alpha=0.7,raster=T,raster.height = 3,raster.width=3)+
  annotate('text',x=-Inf,y=Inf,vjust=1.2,hjust=0,label=g,size=6)+
  theme(panel.border = element_rect(color = 1, size=0.2,linetype=1),axis.line=element_blank()) +  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
})
pp <- plot_grid(plotlist=c(list(a1),po),nrow=1)
pp

pdf(file='pmbc10k_expr.pdf',width=6,height=2); print(plot_grid(plotlist = po,nrow=1)); dev.off()
null device 
          1 
y <- de[['pDC']]
y <- y[order(y$AUC,decreasing = T),]
y$rank <- match(rownames(y),rownames(x))
head(y[,c(1,2,6,10)],100)
x <- p2$misc$varinfo
x$rank <- rank(-1*x$res)
#x$rank <- rank(x$lp)
x <- x[order(x$lp,decreasing=F),]
x <- x[x$rank<5e3,]
x$name <- ''
for(g in gns) x[g,'name'] <- g
ggplot(x,aes(x=rank,y=res,label=name)) + geom_point(color=adjustcolor(2,alpha=0.4),size=0.2) +theme_bw() + xlab("overdispersed gene rank") + ylab("residual variance")+geom_text_repel()

x <- p2$misc$varinfo
x$rank <- rank(-1*x$res)
x$rank <- rank(x$lp)
x <- x[order(x$lp,decreasing=F),]
x <- x[x$rank<5e3,]
x$name <- ''
x$signif <- x$lpa<log(0.05)
for(g in gns) x[g,'name'] <- g
pv <- ggplot(x,aes(x=rank,y=res,label=name,color=signif)) + geom_point(size=0.1) +theme_bw() + xlab("gene overdispersion rank") + ylab("residual variance")+geom_text_repel(color=1) + geom_vline(xintercept = sum(x$lpa<log(0.05)),linetype=2,color=8) + guides(color='none') + scale_color_manual(values=adjustcolor(c('gray40','red'),alpha=0.15))
pv

gns <- c("CD14",'FYB1',"CD8A")

gns <- rownames(x)[c(1:16)+1016] # IGHG4
po <- lapply(gns,function(g) {
  p2$plotEmbedding(gene=g,size=0.1,alpha=0.6,raster=T,raster.dpi=300) +
  #conos::embeddingPlot(p2$embeddings$PCA$tSNE,colors=p2$counts[,g],size=0.1,alpha=0.6,raster=T,raster.dpi=300,theme=theme_bw())+
  annotate('text',x=-Inf,y=Inf,vjust=1.2,hjust=0,label=g,size=6)
})
pp <- plot_grid(plotlist=po,nrow=4)
pp

Use a simpler linear scale varnorm:

p2l <- Pagoda2$new(cd,log.scale=F, n.cores=30)
p2l$adjustVariance(plot=T,gam.k=10)
gns <- c("CD14",'FYB1',"CD8A")
x <- p2l$misc$varinfo
x$rank <- rank(-1*x$res)
x$rank <- rank(x$lp)
x <- x[order(x$lp,decreasing=F),]
x <- x[x$rank<5e3,]
x$name <- ''
x$signif <- x$lpa<log(0.05)
for(g in gns) x[g,'name'] <- g
pv <- ggplot(x,aes(x=rank,y=res,label=name,color=signif)) + geom_point_rast(size=0.1,raster.width=3,raster.height=2.8) +theme_bw() + xlab("gene overdispersion rank") + ylab("residual variance")+geom_text_repel(color=1) + geom_vline(xintercept = sum(x$lpa<log(0.05)),linetype=2,color=8) + guides(color='none') + scale_color_manual(values=adjustcolor(c('gray40','red'),alpha=0.15)) +ylim(0,4)
Ignoring unknown parameters: raster.width, raster.height
pv

pdf(file='resvar.pdf',width=3,height=2.8); print(pv); dev.off()
x <- p2l$misc$varinfo
x$rank <- rank(x$lp)
x <- x[order(x$lp,decreasing=F),]
x$name <- ''
x$signif <- x$lpa<log(0.05)
x <- na.omit(x)
for(g in gns) x[g,'name'] <- g
pmv <- ggplot(x,aes(x=m,y=v,label=name,color=signif)) + geom_point_rast(size=0.2,raster.width=3,raster.height=2.8) +theme_bw() + xlab("observed mean (log)") + ylab("observed variance (log)")+geom_text_repel(color=1,box.padding = 0.7) + guides(color='none') + scale_color_manual(values=c(adjustcolor('gray40',alpha=0.1),adjustcolor('red',alpha=0.15))) + geom_smooth(span=0.2,aes(x=m,y=v),color='blue',size=0.2)
Ignoring unknown parameters: raster.width, raster.height
pmv

pdf(file='pbmc10k_varnorm.pdf',width=3,height=2.8); print(pmv); dev.off()

Theoretical mean-variance

Downsample cells to a fixed size

# subsamples a cell 
subsample.cell <- function(m,depth) {
  if(sum(m)<depth) stop('not enough molecules')
  as.numeric(rmultinom(1,depth,m/sum(m)))
}

fixed.depth <- 5e3;

cdf <- apply(cd[,colSums(cd)>fixed.depth],2,subsample.cell,fixed.depth)
rownames(cdf) <- rownames(cd)
cdf <- as(cdf,'dgCMatrix')
vi <- intersect(colnames(cdf),names(ann)[ann=="CD4 Naive"])
cdf.mean <- rowSums(cdf[,vi])/length(vi)
cdf.var <- apply(cdf[,vi],1,var)
df <- data.frame(mean=cdf.mean,var=cdf.var)
p <- ggplot(df[df$mean<2,],aes(x=mean,y=var))+geom_point_rast(size=0.3,color=adjustcolor("gray40",alpha=0.3),raster.width=2,raster.height=2) + stat_smooth(method = "lm", formula = y ~ offset(x) + I(x^2), size = 1,linetype=2,col=2) +theme_bw() + geom_abline(slope=1,intercept = 0,col='darkgreen',linetype=2,size=1) + xlab('mean') + ylab("variance")
p

pdf(file='pbmc10k_stdvar.pdf',width=2,height=2); print(p); dev.off()

Log-transformed distribution

d <- de[['CD14+ Monocytes']]
g <- rownames(d)[115]; # IGSF6; LMO2; PTPRE; GLIPR1
x <- p2l$counts[names(ann)[ann=='CD14+ Monocytes'],g]*1e3
df <- data.frame(expr=x)
ggplot(df,aes(x=expr)) + geom_density(fill='#FF00003F') +theme_bw()

ggplot(df,aes(x=expr)) + geom_histogram(aes(y=..density..), colour="black", fill="wheat") +theme_bw()

ggplot(df,aes(x=log10(expr+1))) + geom_density(fill='#FF00003F') +theme_bw()

ggplot(df,aes(x=log10(expr+1))) + geom_histogram(aes(y=..density..), colour="black", fill="wheat") +theme_bw() 

a1 <- ggplot(df,aes(x=expr)) + geom_histogram( colour="black", fill="wheat") +theme_bw()  + ylab('cell frequency') + xlab('cpm (linear)') 
a2 <- ggplot(df,aes(x=log10(expr+1))) + geom_histogram(colour="black", fill="wheat") +theme_bw() + xlab('log10[ cpm+1 ]') + ylab('cell frequency') 
pp <- plot_grid(plotlist=list(a1,a2),nrow=2)
pp

pdf(file='pbmc10k_dist.pdf',width=3,height=3); print(pp); dev.off()

Poisson posterior

gn <- "GZMA"
gn <- "NPM1"
gn <- "IL32"
#gn <- "GNLY"
rng <- range(p2l$counts[,gn])/1e3
lambda.grid <- seq(0,rng[2]*1.2,length.out=200)

# sample cells
n.cells <- 5
set.seed(0)
cells1 <- sample(names(ann)[ann=='CD8 effector'],n.cells)
cells2 <- sample(names(ann)[ann=='CD8 Naive'],n.cells)

postm <- do.call(cbind,lapply(c(cells1,cells2),function(cell) {
   x<-dpois(cd[gn,cell],sum(cd[,cell])*lambda.grid)
   x/sum(x)
}))
colnames(postm) <- c(cells1,cells2);
rownames(postm) <- lambda.grid
df <- reshape2::melt(postm)
colnames(df) <- c('lambda','cell','p')
df$type <- ann[as.character(df$cell)]
#df$type <- as.factor(gsub("CD8 ","",as.character(df$type) ))

postj <- do.call(cbind,tapply(colnames(postm),ann[colnames(postm)],function(cn) {
  x <- exp(rowSums(log(postm[,cn])))
  x/sum(x)
}))
jdf <- reshape2::melt(postj);
colnames(jdf) <- c('lambda','type','p')
p <- ggplot(df,aes(x=lambda,y=p,group=cell,color=type)) + geom_line(alpha=0.3,size=0.3)+theme_bw() +
  geom_line(data=jdf,aes(x=lambda,y=p,group=type,color=type),size=1,alpha=0.8,linetype='43')+ xlim(0,0.0036) + 
  scale_color_manual(values=adjustcolor(rev(c('dodgerblue','indianred')),alpha=1)) + guides(color='none') + 
  theme(legend.position = c(0.75, 0.8),legend.background = element_rect(fill=alpha('white', 0.1))) + guides(color=guide_legend(title=element_blank())) +
  ylab('probability density') + xlab('expression magnitude') 
p

pdf(file='posteriors.pdf',width=3,height=2); print(p); dev.off()
null device 
          1 

Tree

a1 <- p2$plotEmbedding(type='PCA',embeddingType='tSNE',groups=ann,alpha=0.4, font.size=c(3,4))
#pdf(file='pmbc10k_tsne.pdf',width=3,height=3); print(a1); dev.off()
a1

source("../retina/functions_copy.R")

in 2 dimensions

emb <- p2$embeddings$PCA$tSNE
z2 <- t.ppt.tree(X=t(emb),emb=emb,M=200,lambda=100,sigma=0.02,metrics='euclidean',plot=F,output=F)
the condition has length > 1 and only the first element will be used
a1 <- p2$plotEmbedding(type='PCA',embeddingType='tSNE',groups=ann,alpha=0.4, size=0.1, font.size=c(3,4),mark.groups=T,raster=T, raster.dpi=600)+
  t.ggplot.ppt.tree(z2,emb,size=1,col=adjustcolor(1,alpha=0.3))+
  theme(panel.border = element_rect(color = 1, size=0.2,linetype=1),axis.line=element_blank())+  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
a1

pdf(file='pmbc10k_tree.pdf',width=3,height=3); print(a1); dev.off()
null device 
          1 

Dimensionality reduction and neural net figure

a1 <- p2$plotEmbedding(type='PCA',embeddingType='tSNE',groups=ann,alpha=0.4, raster=T, size=0.1, raster.dpi=600, font.size=c(3,4),title='t-SNE') +  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
pdf(file='pmbc10k_tsne.pdf',width=3,height=3); print(a1); dev.off()
null device 
          1 
a1

ICA

library(fastICA)

odgenes <- rownames(p2$misc[["varinfo"]])[(order(p2$misc[["varinfo"]]$lp, decreasing = FALSE)[1:3e3])]
x <- p2$counts[,odgenes]
x@x <- x@x * rep(p2$misc[["varinfo"]][colnames(x), "gsf"], diff(x@p))
ica <- fastICA(x,2)
a5 <- sccore::embeddingPlot(ica$S,groups=ann,alpha=0.4, font.size=c(3,4),plot.theme=theme_bw(),title='ICA')
a5

NMF

library(NMF)
nmf <- nmf(as.matrix(x),rank=2)
nmf.emb <- nmf@fit@W;
a4 <- sccore::embeddingPlot(nmf.emb,groups=ann,alpha=0.4, font.size=c(3,4),plot.theme=theme_bw(),title='NMF')
a4

PCA

a2 <- sccore::embeddingPlot(p2$reductions$PCA[,c(1,2)],groups=ann,alpha=0.4, font.size=c(3,4),plot.theme=theme_bw(),title='PCA')
a2

load autoencoder results

aer <- readRDS("auto.emb.rds")

Combined figure

pp <- plot_grid(plotlist=lapply(list(
  p2$plotEmbedding(type='PCA',embeddingType='tSNE',groups=ann,alpha=0.4, font.size=c(3,4),title='t-SNE',raster=T),
  sccore::embeddingPlot(p2$reductions$PCA[,c(1,2)],groups=ann,mark.groups=F,alpha=0.4, font.size=c(4,5),plot.theme=theme_bw(),title='PCA',raster=T),
  sccore::embeddingPlot(nmf.emb,groups=ann,alpha=0.4, font.size=c(3,4),mark.groups=F,plot.theme=theme_bw(),title='NMF',raster=T),
  sccore::embeddingPlot(aer[[1]],groups=ann, mark.groups=F, alpha=0.4, font.size=c(3,4),plot.theme=theme_bw(),title='autoencoder',raster=T)
  ),function(g) g+ theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())),nrow=1)
pdf(file='reductions.pdf',width=12,height=3); print(pp); dev.off();
null device 
          1 
pp

a3 <- sccore::embeddingPlot(aer[[1]],groups=ann,alpha=0.4, font.size=c(3,4),plot.theme=theme_bw(),title='autoencoder')
a3
a3 <- sccore::embeddingPlot(aer[[1]],groups=ann,alpha=0.4, font.size=c(3,4),plot.theme=theme_bw(),title='embedding learning')
a3
a1 <- p2$plotEmbedding(type='PCA',embeddingType='tSNE',groups=ann,alpha=0.4, font.size=c(3,4),title='t-SNE')
#pdf(file='pmbc10k_tsne.pdf',width=3,height=3); print(a1); dev.off()
a1

vc <- setdiff(rownames(emb),rownames(x_test))
a1 <- sccore::embeddingPlot(aer[[3]][vc,],groups=ann,alpha=0.4,size=0.2, font.size=c(3,4),plot.theme=theme_bw(),title='predicted',raster=T)
a2 <- sccore::embeddingPlot(aer[[4]][vc,],groups=ann,alpha=0.4,size=0.2, font.size=c(3,4),plot.theme=theme_bw(),title='actual',raster=T)
pp <- plot_grid(plotlist=list(a2,a1),nrow=1)
pdf(file='ae.tSNE.pdf',width=8,height=4); print(pp); dev.off();
null device 
          1 
pp

Naive/memory T cells only

odgenes <- rownames(p2$misc[["varinfo"]])[(order(p2$misc[["varinfo"]]$lp, decreasing = FALSE)[1:3e3])]
x <- p2$counts[,odgenes]
x@x <- x@x * rep(p2$misc[["varinfo"]][colnames(x), "gsf"], diff(x@p))
x <- x[ann[rownames(x)] %in% c("CD4 Naive","CD4 Memory","CD8 Naive","CD8 effector","Double negative T cell"), ]
p2t <- Pagoda2$new(t(x),log.scale=T, n.cores=30)
3955 cells, 3000 genes; normalizing ... 
using plain model 
log scale ... 
done.
p2t$adjustVariance(plot=F,gam.k=10)
calculating variance fit ...
 using gam 
211 overdispersed genes ... 211
persisting ... 
done.
p2t$calculatePcaReduction(nPcs=10,n.odgenes=3e3)
running PCA using 3000 OD genes .
.
.
.
 done
a1 <- sccore::embeddingPlot(p2t$reductions$PCA[,c(1,2)],groups=ann,mark.groups=T,alpha=0.4, font.size=c(4,5),plot.theme=theme_bw(),title='T cell PCA',raster=T)
pdf(file='tcell.pca.pdf',width=3,height=3); print(a1); dev.off();
null device 
          1 
a1

LS0tCnRpdGxlOiAiUEJNQyBleGFtcGxlcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBJbml0aWFsaXphdGlvbgpgYGB7cn0KbGlicmFyeShwYWdvZGEyKQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dyZXBlbCkKcmVxdWlyZShwYXJhbGxlbCkKbGlicmFyeShnZ3Jhc3RyKQpsaWJyYXJ5KGNvbm9zKQpgYGAKCkxvYWQgUmFodWwncyAyMDE5IGludGVncmF0aW9uIHBibWMgZGF0YXNldCwgYXMgZGVzY3JpYmVkIGhlcmU6IGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zaWduYWMvYXJ0aWNsZXMvcGJtY192aWduZXR0ZS5odG1sCmBgYHtyfQojc28gPC0gcmVhZFJEUygicGJtY18xMGtfdjMucmRzIikKbG9hZCgic2V1cmF0LlJEYXRhIikgIyBjZCwgY2QudmFyLG1ldGEsIHVtYXAKYGBgCgpgYGB7cn0KYW5uIDwtIGFzLmZhY3RvcihzZXROYW1lcyhtZXRhJGNlbGx0eXBlLHJvd25hbWVzKG1ldGEpKSkKYGBgCgoKYGBge3J9CnAyIDwtIFBhZ29kYTIkbmV3KGNkLGxvZy5zY2FsZT1ULCBuLmNvcmVzPTMwKQojcDIgPC0gY29uJHNhbXBsZXMkcGJtYzYwCmBgYAoKYGBge3J9CnAyJGFkanVzdFZhcmlhbmNlKHBsb3Q9VCxnYW0uaz0xMCkKYGBgCgpgYGB7cn0KcDIkY2FsY3VsYXRlUGNhUmVkdWN0aW9uKG5QY3M9NTAsbi5vZGdlbmVzPTNlMykKcDIkbWFrZUtubkdyYXBoKGs9NDAsdHlwZT0nUENBJyxjZW50ZXI9VCxkaXN0YW5jZT0nY29zaW5lJyk7CmBgYAoKYGBge3J9CnAyJGdldEVtYmVkZGluZyh0eXBlPSdQQ0EnLGVtYmVkZGluZ1R5cGU9J3RTTkUnLG4uY29yZXM9MzApCmBgYAoKYGBge3IgZmlnLndpZHRoPTMsZmlnLmhlaWdodD0zfQphMSA8LSBwMiRwbG90RW1iZWRkaW5nKHR5cGU9J1BDQScsZW1iZWRkaW5nVHlwZT0ndFNORScsZ3JvdXBzPWFubixhbHBoYT0wLjQsIGZvbnQuc2l6ZT1jKDMsNCkpCiNwZGYoZmlsZT0ncG1iYzEwa190c25lLnBkZicsd2lkdGg9MyxoZWlnaHQ9Myk7IHByaW50KGExKTsgZGV2Lm9mZigpCmExCmBgYAoKYGBge3J9CnAyJGdldEtubkNsdXN0ZXJzKHR5cGU9J1BDQScsbWV0aG9kPWxlaWRlbi5jb21tdW5pdHkscj0yLG5hbWU9J2xlaWRlbicpCmBgYAoKCmBgYHtyIGZpZy53aWR0aD0zLGZpZy5oZWlnaHQ9M30KcDIkcGxvdEVtYmVkZGluZyh0eXBlPSdQQ0EnLGVtYmVkZGluZ1R5cGU9J3RTTkUnLGNsdXN0ZXJUeXBlPSdsZWlkZW4nLHRpdGxlPSdjbHVzdGVycyAodFNORSknKQpgYGAKCgpgYGB7cn0KZGUgPC0gcDIkZ2V0RGlmZmVyZW50aWFsR2VuZXModHlwZT0nUENBJyx2ZXJib3NlPVQsZ3JvdXBzPWFzLmZhY3Rvcihhbm4pLGFwcGVuZC5hdWM9VCx1cHJlZ3VsYXRlZC5vbmx5ID0gVCkKYGBgCgojIyBWYXJpYW5jZSByYW5raW5nIGFuZCBleHByZXNzaW9uIHBhdHRlcm5zCgpgYGB7ciBmaWcud2lkdGg9MywgZmlnLmhlaWdodD0yfQp4IDwtIHAyJG1pc2MkdmFyaW5mbwp4JHJhbmsgPC0gcmFuaygtMSp4JHJlcykKeCA8LSB4W29yZGVyKHgkbHAsZGVjcmVhc2luZz1UKSxdCnggPC0geFtvcmRlcih4JHJlcyxkZWNyZWFzaW5nPVQpLF0KeCA8LSB4W29yZGVyKHgkbHAsZGVjcmVhc2luZz1GKSxdCnggPC0geFt4JHJhbms8NWUzLF0KZ2dwbG90KHgsYWVzKHg9cmFuayx5PXJlcykpICsgZ2VvbV9wb2ludChjb2xvcj1hZGp1c3Rjb2xvcigyLGFscGhhPTAuNCksc2l6ZT0wLjIpICt0aGVtZV9idygpICsgeGxhYigib3ZlcmRpc3BlcnNlZCBnZW5lIHJhbmsiKSArIHlsYWIoInJlc2lkdWFsIHZhcmlhbmNlIikKYGBgCgoKYGBge3IgZmlnLndpZHRoPTEyLGZpZy5oZWlnaHQ9M30KZ25zIDwtIGMoIlMxMDBBOSIsJ0hFUzQnLCJHQVM2IikKI2ducyA8LSByb3duYW1lcyh4KVtjKDEsMjAwMCwyMDAzKV0KcG8gPC0gbGFwcGx5KGducyxmdW5jdGlvbihnKSB7CiAgcDIkcGxvdEVtYmVkZGluZyhnZW5lPWcsc2l6ZT0wLjIsYWxwaGE9MC43LHJhc3Rlcj1ULHJhc3Rlci5kcGk9MzAwKSsKICAjY29ub3M6OmVtYmVkZGluZ1Bsb3QocDIkZW1iZWRkaW5ncyRQQ0EkdFNORSxjb2xvcnM9cDIkY291bnRzWyxnXSxzaXplPTAuMixhbHBoYT0wLjcscmFzdGVyPVQscmFzdGVyLmhlaWdodCA9IDMscmFzdGVyLndpZHRoPTMpKwogIGFubm90YXRlKCd0ZXh0Jyx4PS1JbmYseT1JbmYsdmp1c3Q9MS4yLGhqdXN0PTAsbGFiZWw9ZyxzaXplPTYpKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvciA9IDEsIHNpemU9MC4yLGxpbmV0eXBlPTEpLGF4aXMubGluZT1lbGVtZW50X2JsYW5rKCkpICsgIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpCn0pCnBwIDwtIHBsb3RfZ3JpZChwbG90bGlzdD1jKGxpc3QoYTEpLHBvKSxucm93PTEpCnBwCmBgYAoKCmBgYHtyfQpwZGYoZmlsZT0ncG1iYzEwa19leHByLnBkZicsd2lkdGg9NixoZWlnaHQ9Mik7IHByaW50KHBsb3RfZ3JpZChwbG90bGlzdCA9IHBvLG5yb3c9MSkpOyBkZXYub2ZmKCkKYGBgCgpgYGB7cn0KeSA8LSBkZVtbJ3BEQyddXQp5IDwtIHlbb3JkZXIoeSRBVUMsZGVjcmVhc2luZyA9IFQpLF0KeSRyYW5rIDwtIG1hdGNoKHJvd25hbWVzKHkpLHJvd25hbWVzKHgpKQpoZWFkKHlbLGMoMSwyLDYsMTApXSwxMDApCmBgYAoKYGBge3IgZmlnLndpZHRoPTMsIGZpZy5oZWlnaHQ9Mn0KeCA8LSBwMiRtaXNjJHZhcmluZm8KeCRyYW5rIDwtIHJhbmsoLTEqeCRyZXMpCiN4JHJhbmsgPC0gcmFuayh4JGxwKQp4IDwtIHhbb3JkZXIoeCRscCxkZWNyZWFzaW5nPUYpLF0KeCA8LSB4W3gkcmFuazw1ZTMsXQp4JG5hbWUgPC0gJycKZm9yKGcgaW4gZ25zKSB4W2csJ25hbWUnXSA8LSBnCmdncGxvdCh4LGFlcyh4PXJhbmsseT1yZXMsbGFiZWw9bmFtZSkpICsgZ2VvbV9wb2ludChjb2xvcj1hZGp1c3Rjb2xvcigyLGFscGhhPTAuNCksc2l6ZT0wLjIpICt0aGVtZV9idygpICsgeGxhYigib3ZlcmRpc3BlcnNlZCBnZW5lIHJhbmsiKSArIHlsYWIoInJlc2lkdWFsIHZhcmlhbmNlIikrZ2VvbV90ZXh0X3JlcGVsKCkKYGBgCgpgYGB7ciBmaWcud2lkdGg9MywgZmlnLmhlaWdodD0yfQp4IDwtIHAyJG1pc2MkdmFyaW5mbwp4JHJhbmsgPC0gcmFuaygtMSp4JHJlcykKeCRyYW5rIDwtIHJhbmsoeCRscCkKeCA8LSB4W29yZGVyKHgkbHAsZGVjcmVhc2luZz1GKSxdCnggPC0geFt4JHJhbms8NWUzLF0KeCRuYW1lIDwtICcnCngkc2lnbmlmIDwtIHgkbHBhPGxvZygwLjA1KQpmb3IoZyBpbiBnbnMpIHhbZywnbmFtZSddIDwtIGcKcHYgPC0gZ2dwbG90KHgsYWVzKHg9cmFuayx5PXJlcyxsYWJlbD1uYW1lLGNvbG9yPXNpZ25pZikpICsgZ2VvbV9wb2ludChzaXplPTAuMSkgK3RoZW1lX2J3KCkgKyB4bGFiKCJnZW5lIG92ZXJkaXNwZXJzaW9uIHJhbmsiKSArIHlsYWIoInJlc2lkdWFsIHZhcmlhbmNlIikrZ2VvbV90ZXh0X3JlcGVsKGNvbG9yPTEpICsgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gc3VtKHgkbHBhPGxvZygwLjA1KSksbGluZXR5cGU9Mixjb2xvcj04KSArIGd1aWRlcyhjb2xvcj0nbm9uZScpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1hZGp1c3Rjb2xvcihjKCdncmF5NDAnLCdyZWQnKSxhbHBoYT0wLjE1KSkKcHYKYGBgCgpgYGB7ciBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD0xMH0KZ25zIDwtIGMoIkNEMTQiLCdGWUIxJywiQ0Q4QSIpCgpnbnMgPC0gcm93bmFtZXMoeClbYygxOjE2KSsxMDE2XSAjIElHSEc0CnBvIDwtIGxhcHBseShnbnMsZnVuY3Rpb24oZykgewogIHAyJHBsb3RFbWJlZGRpbmcoZ2VuZT1nLHNpemU9MC4xLGFscGhhPTAuNixyYXN0ZXI9VCxyYXN0ZXIuZHBpPTMwMCkgKwogIGFubm90YXRlKCd0ZXh0Jyx4PS1JbmYseT1JbmYsdmp1c3Q9MS4yLGhqdXN0PTAsbGFiZWw9ZyxzaXplPTYpCn0pCnBwIDwtIHBsb3RfZ3JpZChwbG90bGlzdD1wbyxucm93PTQpCnBwCmBgYAoKClVzZSBhIHNpbXBsZXIgbGluZWFyIHNjYWxlIHZhcm5vcm06CgpgYGB7cn0KcDJsIDwtIFBhZ29kYTIkbmV3KGNkLGxvZy5zY2FsZT1GLCBuLmNvcmVzPTMwKQpgYGAKCmBgYHtyfQpwMmwkYWRqdXN0VmFyaWFuY2UocGxvdD1ULGdhbS5rPTEwKQpgYGAKCgpgYGB7ciBmaWcud2lkdGg9MywgZmlnLmhlaWdodD0yfQpnbnMgPC0gYygiQ0QxNCIsJ0ZZQjEnLCJDRDhBIikKeCA8LSBwMmwkbWlzYyR2YXJpbmZvCngkcmFuayA8LSByYW5rKC0xKngkcmVzKQp4JHJhbmsgPC0gcmFuayh4JGxwKQp4IDwtIHhbb3JkZXIoeCRscCxkZWNyZWFzaW5nPUYpLF0KeCA8LSB4W3gkcmFuazw1ZTMsXQp4JG5hbWUgPC0gJycKeCRzaWduaWYgPC0geCRscGE8bG9nKDAuMDUpCmZvcihnIGluIGducykgeFtnLCduYW1lJ10gPC0gZwpwdiA8LSBnZ3Bsb3QoeCxhZXMoeD1yYW5rLHk9cmVzLGxhYmVsPW5hbWUsY29sb3I9c2lnbmlmKSkgKyBnZW9tX3BvaW50X3Jhc3Qoc2l6ZT0wLjEscmFzdGVyLndpZHRoPTMscmFzdGVyLmhlaWdodD0yLjgpICt0aGVtZV9idygpICsgeGxhYigiZ2VuZSBvdmVyZGlzcGVyc2lvbiByYW5rIikgKyB5bGFiKCJyZXNpZHVhbCB2YXJpYW5jZSIpK2dlb21fdGV4dF9yZXBlbChjb2xvcj0xKSArIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHN1bSh4JGxwYTxsb2coMC4wNSkpLGxpbmV0eXBlPTIsY29sb3I9OCkgKyBndWlkZXMoY29sb3I9J25vbmUnKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YWRqdXN0Y29sb3IoYygnZ3JheTQwJywncmVkJyksYWxwaGE9MC4xNSkpICt5bGltKDAsNCkKcHYKYGBgCgpgYGB7cn0KcGRmKGZpbGU9J3Jlc3Zhci5wZGYnLHdpZHRoPTMsaGVpZ2h0PTIuOCk7IHByaW50KHB2KTsgZGV2Lm9mZigpCmBgYAoKYGBge3IgZmlnLndpZHRoPTMsIGZpZy5oZWlnaHQ9MywgbWVzc2FnZT1GfQp4IDwtIHAybCRtaXNjJHZhcmluZm8KeCRyYW5rIDwtIHJhbmsoeCRscCkKeCA8LSB4W29yZGVyKHgkbHAsZGVjcmVhc2luZz1GKSxdCngkbmFtZSA8LSAnJwp4JHNpZ25pZiA8LSB4JGxwYTxsb2coMC4wNSkKeCA8LSBuYS5vbWl0KHgpCmZvcihnIGluIGducykgeFtnLCduYW1lJ10gPC0gZwpwbXYgPC0gZ2dwbG90KHgsYWVzKHg9bSx5PXYsbGFiZWw9bmFtZSxjb2xvcj1zaWduaWYpKSArIGdlb21fcG9pbnRfcmFzdChzaXplPTAuMixyYXN0ZXIud2lkdGg9MyxyYXN0ZXIuaGVpZ2h0PTIuOCkgK3RoZW1lX2J3KCkgKyB4bGFiKCJvYnNlcnZlZCBtZWFuIChsb2cpIikgKyB5bGFiKCJvYnNlcnZlZCB2YXJpYW5jZSAobG9nKSIpK2dlb21fdGV4dF9yZXBlbChjb2xvcj0xLGJveC5wYWRkaW5nID0gMC43KSArIGd1aWRlcyhjb2xvcj0nbm9uZScpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKGFkanVzdGNvbG9yKCdncmF5NDAnLGFscGhhPTAuMSksYWRqdXN0Y29sb3IoJ3JlZCcsYWxwaGE9MC4xNSkpKSArIGdlb21fc21vb3RoKHNwYW49MC4yLGFlcyh4PW0seT12KSxjb2xvcj0nYmx1ZScsc2l6ZT0wLjIpCnBtdgpgYGAKCmBgYHtyfQpwZGYoZmlsZT0ncGJtYzEwa192YXJub3JtLnBkZicsd2lkdGg9MyxoZWlnaHQ9Mi44KTsgcHJpbnQocG12KTsgZGV2Lm9mZigpCmBgYAoKIyMgVGhlb3JldGljYWwgbWVhbi12YXJpYW5jZQoKRG93bnNhbXBsZSBjZWxscyB0byBhIGZpeGVkIHNpemUKCmBgYHtyfQojIHN1YnNhbXBsZXMgYSBjZWxsIApzdWJzYW1wbGUuY2VsbCA8LSBmdW5jdGlvbihtLGRlcHRoKSB7CiAgaWYoc3VtKG0pPGRlcHRoKSBzdG9wKCdub3QgZW5vdWdoIG1vbGVjdWxlcycpCiAgYXMubnVtZXJpYyhybXVsdGlub20oMSxkZXB0aCxtL3N1bShtKSkpCn0KCmZpeGVkLmRlcHRoIDwtIDVlMzsKCmNkZiA8LSBhcHBseShjZFssY29sU3VtcyhjZCk+Zml4ZWQuZGVwdGhdLDIsc3Vic2FtcGxlLmNlbGwsZml4ZWQuZGVwdGgpCnJvd25hbWVzKGNkZikgPC0gcm93bmFtZXMoY2QpCmNkZiA8LSBhcyhjZGYsJ2RnQ01hdHJpeCcpCgpgYGAKCmBgYHtyfQp2aSA8LSBpbnRlcnNlY3QoY29sbmFtZXMoY2RmKSxuYW1lcyhhbm4pW2Fubj09IkNENCBOYWl2ZSJdKQpjZGYubWVhbiA8LSByb3dTdW1zKGNkZlssdmldKS9sZW5ndGgodmkpCmNkZi52YXIgPC0gYXBwbHkoY2RmWyx2aV0sMSx2YXIpCmBgYAoKCmBgYHtyIG1lc3NhZ2U9Rix3YXJuaW5nPUYsIGZpZy53aWR0aD0yLGZpZy5oZWlnaHQ9Mn0KZGYgPC0gZGF0YS5mcmFtZShtZWFuPWNkZi5tZWFuLHZhcj1jZGYudmFyKQpwIDwtIGdncGxvdChkZltkZiRtZWFuPDIsXSxhZXMoeD1tZWFuLHk9dmFyKSkrZ2VvbV9wb2ludF9yYXN0KHNpemU9MC4zLGNvbG9yPWFkanVzdGNvbG9yKCJncmF5NDAiLGFscGhhPTAuMykscmFzdGVyLndpZHRoPTIscmFzdGVyLmhlaWdodD0yKSArIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4gb2Zmc2V0KHgpICsgSSh4XjIpLCBzaXplID0gMSxsaW5ldHlwZT0yLGNvbD0yKSArdGhlbWVfYncoKSArIGdlb21fYWJsaW5lKHNsb3BlPTEsaW50ZXJjZXB0ID0gMCxjb2w9J2RhcmtncmVlbicsbGluZXR5cGU9MixzaXplPTEpICsgeGxhYignbWVhbicpICsgeWxhYigidmFyaWFuY2UiKQpwCmBgYAoKYGBge3J9CnBkZihmaWxlPSdwYm1jMTBrX3N0ZHZhci5wZGYnLHdpZHRoPTIsaGVpZ2h0PTIpOyBwcmludChwKTsgZGV2Lm9mZigpCmBgYAoKIyMgTG9nLXRyYW5zZm9ybWVkIGRpc3RyaWJ1dGlvbgoKYGBge3IgZmlnLndpZHRoPTMsZmlnLmhlaWdodD0yLCBtZXNzYWdlPUZ9CmQgPC0gZGVbWydDRDE0KyBNb25vY3l0ZXMnXV0KZyA8LSByb3duYW1lcyhkKVsxMTVdOyAjIElHU0Y2OyBMTU8yOyBQVFBSRTsgR0xJUFIxCnggPC0gcDJsJGNvdW50c1tuYW1lcyhhbm4pW2Fubj09J0NEMTQrIE1vbm9jeXRlcyddLGddKjFlMwpkZiA8LSBkYXRhLmZyYW1lKGV4cHI9eCkKZ2dwbG90KGRmLGFlcyh4PWV4cHIpKSArIGdlb21fZGVuc2l0eShmaWxsPScjRkYwMDAwM0YnKSArdGhlbWVfYncoKQpnZ3Bsb3QoZGYsYWVzKHg9ZXhwcikpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4pLCBjb2xvdXI9ImJsYWNrIiwgZmlsbD0id2hlYXQiKSArdGhlbWVfYncoKQpnZ3Bsb3QoZGYsYWVzKHg9bG9nMTAoZXhwcisxKSkpICsgZ2VvbV9kZW5zaXR5KGZpbGw9JyNGRjAwMDAzRicpICt0aGVtZV9idygpCmdncGxvdChkZixhZXMoeD1sb2cxMChleHByKzEpKSkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeT0uLmRlbnNpdHkuLiksIGNvbG91cj0iYmxhY2siLCBmaWxsPSJ3aGVhdCIpICt0aGVtZV9idygpIApgYGAKCmBgYHtyIGZpZy53aWR0aD0yLjMsIGZpZy5oZWlnaHQ9MywgbWVzc2FnZT1GfQphMSA8LSBnZ3Bsb3QoZGYsYWVzKHg9ZXhwcikpICsgZ2VvbV9oaXN0b2dyYW0oIGNvbG91cj0iYmxhY2siLCBmaWxsPSJ3aGVhdCIpICt0aGVtZV9idygpICArIHlsYWIoJ2NlbGwgZnJlcXVlbmN5JykgKyB4bGFiKCdjcG0gKGxpbmVhciknKSAKYTIgPC0gZ2dwbG90KGRmLGFlcyh4PWxvZzEwKGV4cHIrMSkpKSArIGdlb21faGlzdG9ncmFtKGNvbG91cj0iYmxhY2siLCBmaWxsPSJ3aGVhdCIpICt0aGVtZV9idygpICsgeGxhYignbG9nMTBbIGNwbSsxIF0nKSArIHlsYWIoJ2NlbGwgZnJlcXVlbmN5JykgCnBwIDwtIHBsb3RfZ3JpZChwbG90bGlzdD1saXN0KGExLGEyKSxucm93PTIpCnBwCmBgYApgYGB7cn0KcGRmKGZpbGU9J3BibWMxMGtfZGlzdC5wZGYnLHdpZHRoPTMsaGVpZ2h0PTMpOyBwcmludChwcCk7IGRldi5vZmYoKQpgYGAKCgojIyBQb2lzc29uIHBvc3RlcmlvcgoKYGBge3J9CmduIDwtICJHWk1BIgpnbiA8LSAiTlBNMSIKZ24gPC0gIklMMzIiCiNnbiA8LSAiR05MWSIKcm5nIDwtIHJhbmdlKHAybCRjb3VudHNbLGduXSkvMWUzCmxhbWJkYS5ncmlkIDwtIHNlcSgwLHJuZ1syXSoxLjIsbGVuZ3RoLm91dD0yMDApCgojIHNhbXBsZSBjZWxscwpuLmNlbGxzIDwtIDUKc2V0LnNlZWQoMCkKY2VsbHMxIDwtIHNhbXBsZShuYW1lcyhhbm4pW2Fubj09J0NEOCBlZmZlY3RvciddLG4uY2VsbHMpCmNlbGxzMiA8LSBzYW1wbGUobmFtZXMoYW5uKVthbm49PSdDRDggTmFpdmUnXSxuLmNlbGxzKQoKcG9zdG0gPC0gZG8uY2FsbChjYmluZCxsYXBwbHkoYyhjZWxsczEsY2VsbHMyKSxmdW5jdGlvbihjZWxsKSB7CiAgIHg8LWRwb2lzKGNkW2duLGNlbGxdLHN1bShjZFssY2VsbF0pKmxhbWJkYS5ncmlkKQogICB4L3N1bSh4KQp9KSkKY29sbmFtZXMocG9zdG0pIDwtIGMoY2VsbHMxLGNlbGxzMik7CnJvd25hbWVzKHBvc3RtKSA8LSBsYW1iZGEuZ3JpZApkZiA8LSByZXNoYXBlMjo6bWVsdChwb3N0bSkKY29sbmFtZXMoZGYpIDwtIGMoJ2xhbWJkYScsJ2NlbGwnLCdwJykKZGYkdHlwZSA8LSBhbm5bYXMuY2hhcmFjdGVyKGRmJGNlbGwpXQojZGYkdHlwZSA8LSBhcy5mYWN0b3IoZ3N1YigiQ0Q4ICIsIiIsYXMuY2hhcmFjdGVyKGRmJHR5cGUpICkpCgpwb3N0aiA8LSBkby5jYWxsKGNiaW5kLHRhcHBseShjb2xuYW1lcyhwb3N0bSksYW5uW2NvbG5hbWVzKHBvc3RtKV0sZnVuY3Rpb24oY24pIHsKICB4IDwtIGV4cChyb3dTdW1zKGxvZyhwb3N0bVssY25dKSkpCiAgeC9zdW0oeCkKfSkpCmpkZiA8LSByZXNoYXBlMjo6bWVsdChwb3N0aik7CmNvbG5hbWVzKGpkZikgPC0gYygnbGFtYmRhJywndHlwZScsJ3AnKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MixmaWcud2lkdGg9Myx3YXJuaW5nPUZ9CnAgPC0gZ2dwbG90KGRmLGFlcyh4PWxhbWJkYSx5PXAsZ3JvdXA9Y2VsbCxjb2xvcj10eXBlKSkgKyBnZW9tX2xpbmUoYWxwaGE9MC4zLHNpemU9MC4zKSt0aGVtZV9idygpICsKICBnZW9tX2xpbmUoZGF0YT1qZGYsYWVzKHg9bGFtYmRhLHk9cCxncm91cD10eXBlLGNvbG9yPXR5cGUpLHNpemU9MSxhbHBoYT0wLjgsbGluZXR5cGU9JzQzJykrIHhsaW0oMCwwLjAwMzYpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1hZGp1c3Rjb2xvcihyZXYoYygnZG9kZ2VyYmx1ZScsJ2luZGlhbnJlZCcpKSxhbHBoYT0xKSkgKyBndWlkZXMoY29sb3I9J25vbmUnKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC43NSwgMC44KSxsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPWFscGhhKCd3aGl0ZScsIDAuMSkpKSArIGd1aWRlcyhjb2xvcj1ndWlkZV9sZWdlbmQodGl0bGU9ZWxlbWVudF9ibGFuaygpKSkgKwogIHlsYWIoJ3Byb2JhYmlsaXR5IGRlbnNpdHknKSArIHhsYWIoJ2V4cHJlc3Npb24gbWFnbml0dWRlJykgCnAKYGBgCgpgYGB7cn0KcGRmKGZpbGU9J3Bvc3RlcmlvcnMucGRmJyx3aWR0aD0zLGhlaWdodD0yKTsgcHJpbnQocCk7IGRldi5vZmYoKQpgYGAKCgoKIyMgVHJlZQoKYGBge3IgZmlnLndpZHRoPTMsZmlnLmhlaWdodD0zfQphMSA8LSBwMiRwbG90RW1iZWRkaW5nKHR5cGU9J1BDQScsZW1iZWRkaW5nVHlwZT0ndFNORScsZ3JvdXBzPWFubixhbHBoYT0wLjQsIGZvbnQuc2l6ZT1jKDMsNCkpCiNwZGYoZmlsZT0ncG1iYzEwa190c25lLnBkZicsd2lkdGg9MyxoZWlnaHQ9Myk7IHByaW50KGExKTsgZGV2Lm9mZigpCmExCmBgYAoKYGBge3J9CnNvdXJjZSgiLi4vcmV0aW5hL2Z1bmN0aW9uc19jb3B5LlIiKQpgYGAKaW4gMiBkaW1lbnNpb25zCmBgYHtyfQplbWIgPC0gcDIkZW1iZWRkaW5ncyRQQ0EkdFNORQp6MiA8LSB0LnBwdC50cmVlKFg9dChlbWIpLGVtYj1lbWIsTT0yMDAsbGFtYmRhPTEwMCxzaWdtYT0wLjAyLG1ldHJpY3M9J2V1Y2xpZGVhbicscGxvdD1ULG91dHB1dD1GKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0zLGZpZy5oZWlnaHQ9M30KYTEgPC0gcDIkcGxvdEVtYmVkZGluZyh0eXBlPSdQQ0EnLGVtYmVkZGluZ1R5cGU9J3RTTkUnLGdyb3Vwcz1hbm4sYWxwaGE9MC40LCBzaXplPTAuMSwgZm9udC5zaXplPWMoMyw0KSxtYXJrLmdyb3Vwcz1ULHJhc3Rlcj1ULCByYXN0ZXIuZHBpPTYwMCkrCiAgdC5nZ3Bsb3QucHB0LnRyZWUoejIsZW1iLHNpemU9MSxjb2w9YWRqdXN0Y29sb3IoMSxhbHBoYT0wLjMpKSsKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAxLCBzaXplPTAuMixsaW5ldHlwZT0xKSxheGlzLmxpbmU9ZWxlbWVudF9ibGFuaygpKSsgIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpCmExCmBgYApgYGB7cn0KcGRmKGZpbGU9J3BtYmMxMGtfdHJlZS5wZGYnLHdpZHRoPTMsaGVpZ2h0PTMpOyBwcmludChhMSk7IGRldi5vZmYoKQpgYGAKCiMjIERpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBhbmQgbmV1cmFsIG5ldCBmaWd1cmUKYGBge3IgZmlnLndpZHRoPTMsZmlnLmhlaWdodD0zfQphMSA8LSBwMiRwbG90RW1iZWRkaW5nKHR5cGU9J1BDQScsZW1iZWRkaW5nVHlwZT0ndFNORScsZ3JvdXBzPWFubixhbHBoYT0wLjQsIHJhc3Rlcj1ULCBzaXplPTAuMSwgcmFzdGVyLmRwaT02MDAsIGZvbnQuc2l6ZT1jKDMsNCksdGl0bGU9J3QtU05FJykgKyAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkKcGRmKGZpbGU9J3BtYmMxMGtfdHNuZS5wZGYnLHdpZHRoPTMsaGVpZ2h0PTMpOyBwcmludChhMSk7IGRldi5vZmYoKQphMQpgYGAKCgoKIyMjIElDQQoKYGBge3J9CmxpYnJhcnkoZmFzdElDQSkKCm9kZ2VuZXMgPC0gcm93bmFtZXMocDIkbWlzY1tbInZhcmluZm8iXV0pWyhvcmRlcihwMiRtaXNjW1sidmFyaW5mbyJdXSRscCwgZGVjcmVhc2luZyA9IEZBTFNFKVsxOjNlM10pXQp4IDwtIHAyJGNvdW50c1ssb2RnZW5lc10KeEB4IDwtIHhAeCAqIHJlcChwMiRtaXNjW1sidmFyaW5mbyJdXVtjb2xuYW1lcyh4KSwgImdzZiJdLCBkaWZmKHhAcCkpCmBgYAoKYGBge3J9CmljYSA8LSBmYXN0SUNBKHgsMikKYGBgCgoKYGBge3IgZmlnLndpZHRoPTMsZmlnLmhlaWdodD0zfQphNSA8LSBzY2NvcmU6OmVtYmVkZGluZ1Bsb3QoaWNhJFMsZ3JvdXBzPWFubixhbHBoYT0wLjQsIGZvbnQuc2l6ZT1jKDMsNCkscGxvdC50aGVtZT10aGVtZV9idygpLHRpdGxlPSdJQ0EnKQphNQpgYGAKIyMjIE5NRgpgYGB7cn0KbGlicmFyeShOTUYpCm5tZiA8LSBubWYoYXMubWF0cml4KHgpLHJhbms9MikKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0zfQpubWYuZW1iIDwtIG5tZkBmaXRAVzsKYTQgPC0gc2Njb3JlOjplbWJlZGRpbmdQbG90KG5tZi5lbWIsZ3JvdXBzPWFubixhbHBoYT0wLjQsIGZvbnQuc2l6ZT1jKDMsNCkscGxvdC50aGVtZT10aGVtZV9idygpLHRpdGxlPSdOTUYnKQphNApgYGAKCiMjIyBQQ0EKYGBge3IgZmlnLndpZHRoPTMsZmlnLmhlaWdodD0zfQphMiA8LSBzY2NvcmU6OmVtYmVkZGluZ1Bsb3QocDIkcmVkdWN0aW9ucyRQQ0FbLGMoMSwyKV0sZ3JvdXBzPWFubixhbHBoYT0wLjQsIGZvbnQuc2l6ZT1jKDMsNCkscGxvdC50aGVtZT10aGVtZV9idygpLHRpdGxlPSdQQ0EnKQphMgpgYGAKbG9hZCBhdXRvZW5jb2RlciByZXN1bHRzCmBgYHtyfQphZXIgPC0gcmVhZFJEUygiYXV0by5lbWIucmRzIikKYGBgCgpDb21iaW5lZCBmaWd1cmUKYGBge3IgZmlnLndpZHRoPTEyLGZpZy5oZWlnaHQ9M30KcHAgPC0gcGxvdF9ncmlkKHBsb3RsaXN0PWxhcHBseShsaXN0KAogIHAyJHBsb3RFbWJlZGRpbmcodHlwZT0nUENBJyxlbWJlZGRpbmdUeXBlPSd0U05FJyxncm91cHM9YW5uLGFscGhhPTAuNCwgZm9udC5zaXplPWMoMyw0KSx0aXRsZT0ndC1TTkUnLHJhc3Rlcj1UKSwKICBzY2NvcmU6OmVtYmVkZGluZ1Bsb3QocDIkcmVkdWN0aW9ucyRQQ0FbLGMoMSwyKV0sZ3JvdXBzPWFubixtYXJrLmdyb3Vwcz1GLGFscGhhPTAuNCwgZm9udC5zaXplPWMoNCw1KSxwbG90LnRoZW1lPXRoZW1lX2J3KCksdGl0bGU9J1BDQScscmFzdGVyPVQpLAogIHNjY29yZTo6ZW1iZWRkaW5nUGxvdChubWYuZW1iLGdyb3Vwcz1hbm4sYWxwaGE9MC40LCBmb250LnNpemU9YygzLDQpLG1hcmsuZ3JvdXBzPUYscGxvdC50aGVtZT10aGVtZV9idygpLHRpdGxlPSdOTUYnLHJhc3Rlcj1UKSwKICBzY2NvcmU6OmVtYmVkZGluZ1Bsb3QoYWVyW1sxXV0sZ3JvdXBzPWFubiwgbWFyay5ncm91cHM9RiwgYWxwaGE9MC40LCBmb250LnNpemU9YygzLDQpLHBsb3QudGhlbWU9dGhlbWVfYncoKSx0aXRsZT0nYXV0b2VuY29kZXInLHJhc3Rlcj1UKQogICksZnVuY3Rpb24oZykgZysgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkpLG5yb3c9MSkKcGRmKGZpbGU9J3JlZHVjdGlvbnMucGRmJyx3aWR0aD0xMixoZWlnaHQ9Myk7IHByaW50KHBwKTsgZGV2Lm9mZigpOwpwcApgYGAKCgpgYGB7ciBmaWcud2lkdGg9MyxmaWcuaGVpZ2h0PTN9CmEzIDwtIHNjY29yZTo6ZW1iZWRkaW5nUGxvdChhZXJbWzFdXSxncm91cHM9YW5uLGFscGhhPTAuNCwgZm9udC5zaXplPWMoMyw0KSxwbG90LnRoZW1lPXRoZW1lX2J3KCksdGl0bGU9J2F1dG9lbmNvZGVyJykKYTMKYGBgCgpgYGB7ciBmaWcud2lkdGg9MyxmaWcuaGVpZ2h0PTN9CmEzIDwtIHNjY29yZTo6ZW1iZWRkaW5nUGxvdChhZXJbWzFdXSxncm91cHM9YW5uLGFscGhhPTAuNCwgZm9udC5zaXplPWMoMyw0KSxwbG90LnRoZW1lPXRoZW1lX2J3KCksdGl0bGU9J2VtYmVkZGluZyBsZWFybmluZycpCmEzCmBgYAoKYGBge3IgZmlnLndpZHRoPTMsZmlnLmhlaWdodD0zfQphMSA8LSBwMiRwbG90RW1iZWRkaW5nKHR5cGU9J1BDQScsZW1iZWRkaW5nVHlwZT0ndFNORScsZ3JvdXBzPWFubixhbHBoYT0wLjQsIGZvbnQuc2l6ZT1jKDMsNCksdGl0bGU9J3QtU05FJykKI3BkZihmaWxlPSdwbWJjMTBrX3RzbmUucGRmJyx3aWR0aD0zLGhlaWdodD0zKTsgcHJpbnQoYTEpOyBkZXYub2ZmKCkKYTEKYGBgCgpgYGB7ciBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD00fQp2YyA8LSBzZXRkaWZmKHJvd25hbWVzKGVtYikscm93bmFtZXMoeF90ZXN0KSkKYTEgPC0gc2Njb3JlOjplbWJlZGRpbmdQbG90KGFlcltbM11dW3ZjLF0sZ3JvdXBzPWFubixhbHBoYT0wLjQsc2l6ZT0wLjIsIGZvbnQuc2l6ZT1jKDMsNCkscGxvdC50aGVtZT10aGVtZV9idygpLHRpdGxlPSdwcmVkaWN0ZWQnLHJhc3Rlcj1UKQphMiA8LSBzY2NvcmU6OmVtYmVkZGluZ1Bsb3QoYWVyW1s0XV1bdmMsXSxncm91cHM9YW5uLGFscGhhPTAuNCxzaXplPTAuMiwgZm9udC5zaXplPWMoMyw0KSxwbG90LnRoZW1lPXRoZW1lX2J3KCksdGl0bGU9J2FjdHVhbCcscmFzdGVyPVQpCnBwIDwtIHBsb3RfZ3JpZChwbG90bGlzdD1saXN0KGEyLGExKSxucm93PTEpCnBkZihmaWxlPSdhZS50U05FLnBkZicsd2lkdGg9OCxoZWlnaHQ9NCk7IHByaW50KHBwKTsgZGV2Lm9mZigpOwpwcApgYGAKCiMgTmFpdmUvbWVtb3J5IFQgY2VsbHMgb25seQpgYGB7cn0Kb2RnZW5lcyA8LSByb3duYW1lcyhwMiRtaXNjW1sidmFyaW5mbyJdXSlbKG9yZGVyKHAyJG1pc2NbWyJ2YXJpbmZvIl1dJGxwLCBkZWNyZWFzaW5nID0gRkFMU0UpWzE6M2UzXSldCnggPC0gcDIkY291bnRzWyxvZGdlbmVzXQp4QHggPC0geEB4ICogcmVwKHAyJG1pc2NbWyJ2YXJpbmZvIl1dW2NvbG5hbWVzKHgpLCAiZ3NmIl0sIGRpZmYoeEBwKSkKeCA8LSB4W2Fubltyb3duYW1lcyh4KV0gJWluJSBjKCJDRDQgTmFpdmUiLCJDRDQgTWVtb3J5IiwiQ0Q4IE5haXZlIiwiQ0Q4IGVmZmVjdG9yIiwiRG91YmxlIG5lZ2F0aXZlIFQgY2VsbCIpLCBdCnAydCA8LSBQYWdvZGEyJG5ldyh0KHgpLGxvZy5zY2FsZT1ULCBuLmNvcmVzPTMwKQpwMnQkYWRqdXN0VmFyaWFuY2UocGxvdD1GLGdhbS5rPTEwKQpwMnQkY2FsY3VsYXRlUGNhUmVkdWN0aW9uKG5QY3M9MTAsbi5vZGdlbmVzPTNlMykKYGBgCgpgYGB7ciBmaWcud2lkdGg9MyxmaWcuaGVpZ2h0PTN9CmExIDwtIHNjY29yZTo6ZW1iZWRkaW5nUGxvdChwMnQkcmVkdWN0aW9ucyRQQ0FbLGMoMSwyKV0sZ3JvdXBzPWFubixtYXJrLmdyb3Vwcz1ULGFscGhhPTAuNCwgZm9udC5zaXplPWMoNCw1KSxwbG90LnRoZW1lPXRoZW1lX2J3KCksdGl0bGU9J1QgY2VsbCBQQ0EnLHJhc3Rlcj1UKQpwZGYoZmlsZT0ndGNlbGwucGNhLnBkZicsd2lkdGg9MyxoZWlnaHQ9Myk7IHByaW50KGExKTsgZGV2Lm9mZigpOwphMQpgYGAK