Major cell annotation
set.seed(3)
annot = readRDS('annot.Nov.rds') %>% Toch()
annot=as.factor(annot)
annot.pal <- setNames(sample(rainbow(length(levels(annot)),v=0.75,s=0.65)),levels(annot));
#annot.pal = readRDS('annot.pal.rds')
annot.pal['Monocyte progenitor'] = "blue"
annot.palf <- function(n) return(annot.pal)
a2=con$plotGraph(alpha=0.2,size=0.1,groups=annot,font.size=c(3.9,4.3),plot.na=F,palette=annot.palf)
a2

Marker gene expression
library(cowplot)
Attaching package: ‘cowplot’
The following object is masked from ‘package:ggpubr’:
get_legend
gs=c(
'Ms4a1,Ly6d,Cd79a,CD74,
Mcpt8, Prss34, Ms4a2,
Bst2, Irf8, Siglech, Cox6a2,
Car2, Hemgn, Ctse, Cpox, Atpif,
Cebpe, Fcnb,
Cd34, Prtn3, Mpo, Elane, Mpl,
S100a4, Ccl6, Ctss, Lyz2,
Ly6c2, S100a10,
Mmp8, Ifitm6, S100a11, S100a8, S100a9,
Vpreb1, Vpreb3, Dntt, Mzb,
Cd3d, Ccl5, Thy1, Cd3g, Il7r')
gs = strsplit(gsub('\n','',gs),'[,]')[[1]]
gs = gsub(' ','',gs)
cname=names(annot)
aexp=t(p2all$counts)
gs=intersect(gs,rownames(aexp))
#cname=intersect(cname,colnames(aexp))
p=Dotfig(gs,aexp[,cname],annot[cname],cols = c("blue","white", "red"))+xlab('')+ylab('')
[1] "blue"
[1] "white"
[1] "red"
p

ggsave('F2E.marker.pdf',p,height = 10,width=7)
s.sampType = Toch(s.sampType)
s.sampType[s.sampType=='Local']='Image-seq'
s.sampType[s.sampType=='TET-AR']='WCBM'
s.sampType = as.factor(s.sampType)
s.sampType = s.sampType[names(annot)]
a2=con$plotGraph(alpha=0.2,size=0.1,groups=annot,font.size=c(3.9,4.3),plot.na=F,palette=annot.palf)
a3=con$plotGraph(groups=s.samp, mark.groups=F, alpha=0.05,size=0.1, show.legend=T,plot.na=F)
a4=con$plotGraph(groups=s.sampType, mark.groups=F, alpha=0.05,size=0.1, show.legend=T,plot.na=F,palette=function(n) return(rev(rainbow(2))[1:n]))
a3=a3+ theme(legend.position=c(0.08,0.79),legend.title=element_text(size=0), legend.text=element_text(size=10), legend.key.size = unit(0.8, 'lines'))+ guides(color = guide_legend(override.aes = list(size=3.4,alpha=1)))
a4=a4+ theme(legend.position=c(0.82,0.92),legend.title=element_text(size=0), legend.text=element_text(size=12), legend.key.size = unit(0.8, 'lines'))+ guides(color = guide_legend(override.aes = list(size=4,alpha=1)))
a3

a4

f1=names(s.sampType[s.sampType=='WCBM'])
f2=names(s.sampType[s.sampType=='Image-seq'])
a1=con$plotGraph(alpha=0.3,size=0.15,groups=annot[f1],font.size=c(3.9,4.3),plot.na=F,palette=annot.palf,title='WCBM')
a2=con$plotGraph(alpha=0.3,size=0.15,groups=annot[f2],font.size=c(3.9,4.3),plot.na=F,palette=annot.palf,title='Image-seq')
b = cowplot::plot_grid(plotlist = list(a1,a2), ncol = 2, nrow = 1)
b

NA
NA
cell porportions
samp=con$getDatasetPerCell()
cname=intersect(names(annot),names(samp))
ano2=data.frame('Cell'=annot[cname],'SampleType'=samp[cname])
# Annotation vs sample
tmp2 <- acast(ano2, Cell ~ SampleType, fun.aggregate=length)
Using SampleType as value column: use value.var to override.
tmp3 <- (sweep(tmp2, 2, colSums(tmp2), FUN='/'))
tmp4 <- melt(tmp3)
#head(tmp4)
names(tmp4) <- c('cell', 'sample','pc.of.sample')
p=ggplot(tmp4, aes(x=sample, fill=cell, y = pc.of.sample)) +
geom_bar(stat='identity', position='fill') +
scale_fill_manual(values=annot.pal)+ coord_flip()+
theme(axis.text.x = element_text(angle = 90, hjust = 1),
axis.title.y=element_blank(),
axis.title.x=element_blank())
p=p +theme(axis.text.x = element_blank(), axis.ticks = element_blank(),panel.background = element_blank())
p

cname = names(annot)
cname = intersect(cname,names(s.sampType))
ano2=data.frame('Cell'=annot[cname],'SampleType'=s.sampType2[cname])
# Annotation vs sample
tmp2 <- acast(ano2, Cell ~ SampleType, fun.aggregate=length)
Using SampleType as value column: use value.var to override.
tmp3 <- (sweep(tmp2, 2, colSums(tmp2), FUN='/'))
tmp4 <- melt(tmp3)
#head(tmp4)
names(tmp4) <- c('cell', 'sample','pc.of.sample')
p=ggplot(tmp4, aes(y=pc.of.sample, fill=cell, x = sample)) +theme_bw()+
geom_bar(stat='identity', position='fill') +
scale_fill_manual(values=annot.pal)+
theme(axis.text.x = element_text(angle = 90, hjust = 1),
axis.title.y=element_blank(),panel.border = element_blank(),
axis.title.x=element_blank())
p

p2all = readRDS('raw.v2_p2combined.rds')
fcol=rainbow(2)
names(fcol)=unique(s.sampType2)
fcol
WCBM Image-seq
"#FF0000" "#00FFFF"
Comparision of data quality; total UMI per cell
cname = intersect(names(s.sampType),rownames(p2all$misc$rawCounts))
x=t(p2all$misc$rawCounts)[,cname]
cellano=s.sampType[cname]
cell=cellano[colnames(x)]
cols=colSums(as.matrix(x))
expG=apply(x,2,function(x) length(x[x>0]))
dat2=data.frame('cells'=cell,'y'=log10(cols))
p1=ggplot(dat2, aes(x=cells, y=y,fill=cell)) +
geom_violin(outlier.shape = -1,width=0.5,position=position_dodge(width=0.1))+theme_classic()+
#geom_point(data = dat1,size=0.5,color=adjustcolor(1,alpha=0.3), position = position_jitterdodge(0.3))+
theme(axis.title.x=element_blank(),
axis.text.x= element_text(angle = 45, hjust = 1))+
theme( axis.text.y = element_text(angle = 90, hjust = 0.5))+ylab('total UMI per cell (log10)')+theme(legend.position = 'none')+
scale_fill_manual(values=fcol)
Warning: Ignoring unknown parameters: outlier.shape
p1

number of expressed genes per cell
dat1=data.frame('cells'=cell,'y'=expG)
p1=ggplot(dat1, aes(x=cells, y=y,fill=cell)) + theme_classic()+
geom_violin(outlier.shape = -1,width=0.5,position=position_dodge(width=0.1))+
#geom_point(data = dat1,size=0.5,color=adjustcolor(1,alpha=0.3), position = position_jitterdodge(0.3))+
theme(axis.title.x=element_blank(),
axis.text.x= element_text(angle = 45, hjust = 1))+
theme( axis.text.y = element_text(angle = 90, hjust = 0.5))+ylab('number of expressed genes')+theme(legend.position = 'none')+
scale_fill_manual(values=fcol)
Warning: Ignoring unknown parameters: outlier.shape
p1

NA
NA
nLocal = names(s.sampType[s.sampType=='Image-seq'])
nW = names(s.sampType[s.sampType=='WCBM'])
deL=p2all$getDifferentialGenes(groups=annot[nLocal],z.threshold = 2)
Warning in p2all$getDifferentialGenes(groups = annot[nLocal], z.threshold = 2) :
cluster vector doesn't specify groups for all of the cells, dropping missing cells from comparison
deW=p2all$getDifferentialGenes(groups=annot[nW],z.threshold = 2)
Warning in p2all$getDifferentialGenes(groups = annot[nW], z.threshold = 2) :
cluster vector doesn't specify groups for all of the cells, dropping missing cells from comparison
gl1 <- unname(unlist(lapply(deL, function(x) {
x=x[x$Z>0,]
x <- x[order(x$Z,decreasing=T),]
head(rownames(x),n=200)
})))
gl2 <- unname(unlist(lapply(deW, function(x) {
x=x[x$Z>0,]
x <- x[order(x$Z,decreasing=T),]
head(rownames(x),n=200)
})))
gs=unique(c(gl1,gl2))
length(gs)
[1] 1108
cname = intersect(names(annot),names(s.sampType))
ano2 = paste(annot[cname],s.sampType[cname])
names(ano2) = cname
t.exp=p2all$counts[names(ano2),gl1]
cma2=apply(t.exp,2,function(x) tapply(x,ano2,mean))
#dim(cma2)
cma.cor.spearman <- cor(as.matrix(t(cma2)),method='spearman')
hc.spearman2 <- hclust(as.dist(1-cma.cor.spearman))
cma.cor.spearman2 = cma.cor.spearman[grepl('Image-seq',rownames(cma.cor.spearman)),grepl('Image-seq',colnames(cma.cor.spearman))]
n1 = rownames(cma.cor.spearman2)
n2=colnames(cma.cor.spearman2)
n1=gsub(' Image-seq','',n1)
n2=gsub(' WCBM','',n1)
rownames(cma.cor.spearman2) = n1
colnames(cma.cor.spearman2) = n2
pheatmap(cma.cor.spearman2,treeheight_row=25,treeheight_col=25,height=5,width=5.2)

annot.pal['AML']='red'
annot.palf <- function(n) return(annot.pal)
GFP expression in High Burden and Low Burden mice
#p21 High Burden
#p22 Low Burden
a1=embeddingPlot(p22$embeddings$PCA$tSNE, groups = ano2,plot.theme = theme,font.size = c(4.4,5.5),palette = annot.palf)
a2=embeddingPlot(p22$embeddings$PCA$tSNE, colors = p22$counts[,'egfp'],plot.theme = theme)
a3=embeddingPlot(p21$embeddings$PCA$tSNE, groups = ano2,plot.theme = theme,font.size = c(4.4,5.5),palette = annot.palf)
t=rep(0,nrow(p21$counts))
names(t) = rownames(p21$counts)
a4=embeddingPlot(p21$embeddings$PCA$tSNE, colors = t,plot.theme = theme)
a1

a2

a3

a4

ggsave('high.ano.pdf',a1,height=4,width=4)
ggsave('low.ano.pdf',a3,height=4,width=4)
ggsave('high.GFP.pdf',a2,height=4,width=4)
ggsave('low.GFP.pdf',a4,height=4,width=4)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCk1ham9yIGNlbGwgYW5ub3RhdGlvbiAKCmBgYHtyIGZpZy53aWR0aD0gNCwgZmlnLmhlaWdodD00fQpzZXQuc2VlZCgzKQphbm5vdCAgPSByZWFkUkRTKCdhbm5vdC5Ob3YucmRzJykgJT4lIFRvY2goKQoKYW5ub3Q9YXMuZmFjdG9yKGFubm90KQphbm5vdC5wYWwgPC0gc2V0TmFtZXMoc2FtcGxlKHJhaW5ib3cobGVuZ3RoKGxldmVscyhhbm5vdCkpLHY9MC43NSxzPTAuNjUpKSxsZXZlbHMoYW5ub3QpKTsKI2Fubm90LnBhbCA9IHJlYWRSRFMoJ2Fubm90LnBhbC5yZHMnKQoKYW5ub3QucGFsWydNb25vY3l0ZSBwcm9nZW5pdG9yJ10gPSAiYmx1ZSIKYW5ub3QucGFsZiA8LSBmdW5jdGlvbihuKSByZXR1cm4oYW5ub3QucGFsKQoKYTI9Y29uJHBsb3RHcmFwaChhbHBoYT0wLjIsc2l6ZT0wLjEsZ3JvdXBzPWFubm90LGZvbnQuc2l6ZT1jKDMuOSw0LjMpLHBsb3QubmE9RixwYWxldHRlPWFubm90LnBhbGYpCmEyCmBgYAoKCk1hcmtlciBnZW5lIGV4cHJlc3Npb24gCmBgYHtyIGZpZy53aWR0aD0gNS41LCBmaWcuaGVpZ2h0PTh9CgpsaWJyYXJ5KGNvd3Bsb3QpCgpncz1jKAogICdNczRhMSxMeTZkLENkNzlhLENENzQsCiAgTWNwdDgsIFByc3MzNCwgTXM0YTIsCiAgQnN0MiwgSXJmOCwgU2lnbGVjaCwgQ294NmEyLAogIENhcjIsIEhlbWduLCBDdHNlLCBDcG94LCBBdHBpZiwKICBDZWJwZSwgRmNuYiwKICBDZDM0LCBQcnRuMywgTXBvLCBFbGFuZSwgTXBsLAogIFMxMDBhNCwgQ2NsNiwgQ3RzcywgTHl6MiwKICBMeTZjMiwgUzEwMGExMCwKICBNbXA4LCBJZml0bTYsIFMxMDBhMTEsIFMxMDBhOCwgUzEwMGE5LAogIFZwcmViMSwgVnByZWIzLCBEbnR0LCBNemIsCiAgQ2QzZCwgQ2NsNSwgVGh5MSwgQ2QzZywgSWw3cicpCgpncyA9IHN0cnNwbGl0KGdzdWIoJ1xuJywnJyxncyksJ1ssXScpW1sxXV0KZ3MgPSBnc3ViKCcgJywnJyxncykKY25hbWU9bmFtZXMoYW5ub3QpCmFleHA9dChwMmFsbCRjb3VudHMpCmdzPWludGVyc2VjdChncyxyb3duYW1lcyhhZXhwKSkKI2NuYW1lPWludGVyc2VjdChjbmFtZSxjb2xuYW1lcyhhZXhwKSkKcD1Eb3RmaWcoZ3MsYWV4cFssY25hbWVdLGFubm90W2NuYW1lXSxjb2xzID0gYygiYmx1ZSIsIndoaXRlIiwgInJlZCIpKSt4bGFiKCcnKSt5bGFiKCcnKQpwCgpnZ3NhdmUoJ0YyRS5tYXJrZXIucGRmJyxwLGhlaWdodCA9IDEwLHdpZHRoPTcpCgpgYGAKCmBgYHtyfQpzLnNhbXBUeXBlID0gVG9jaChzLnNhbXBUeXBlKQpzLnNhbXBUeXBlW3Muc2FtcFR5cGU9PSdMb2NhbCddPSdJbWFnZS1zZXEnCnMuc2FtcFR5cGVbcy5zYW1wVHlwZT09J1RFVC1BUiddPSdXQ0JNJwpzLnNhbXBUeXBlID0gYXMuZmFjdG9yKHMuc2FtcFR5cGUpCnMuc2FtcFR5cGUgPSBzLnNhbXBUeXBlW25hbWVzKGFubm90KV0KYGBgCgoKCmBgYHtyIGZpZy53aWR0aD0gNCwgZmlnLmhlaWdodD00fQoKYTI9Y29uJHBsb3RHcmFwaChhbHBoYT0wLjIsc2l6ZT0wLjEsZ3JvdXBzPWFubm90LGZvbnQuc2l6ZT1jKDMuOSw0LjMpLHBsb3QubmE9RixwYWxldHRlPWFubm90LnBhbGYpCmEzPWNvbiRwbG90R3JhcGgoZ3JvdXBzPXMuc2FtcCwgbWFyay5ncm91cHM9RiwgYWxwaGE9MC4wNSxzaXplPTAuMSwgc2hvdy5sZWdlbmQ9VCxwbG90Lm5hPUYpCmE0PWNvbiRwbG90R3JhcGgoZ3JvdXBzPXMuc2FtcFR5cGUsIG1hcmsuZ3JvdXBzPUYsIGFscGhhPTAuMDUsc2l6ZT0wLjEsIHNob3cubGVnZW5kPVQscGxvdC5uYT1GLHBhbGV0dGU9ZnVuY3Rpb24obikgcmV0dXJuKHJldihyYWluYm93KDIpKVsxOm5dKSkKCmEzPWEzKyB0aGVtZShsZWdlbmQucG9zaXRpb249YygwLjA4LDAuNzkpLGxlZ2VuZC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0wKSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTApLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuOCwgJ2xpbmVzJykpKyBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPTMuNCxhbHBoYT0xKSkpCmE0PWE0KyB0aGVtZShsZWdlbmQucG9zaXRpb249YygwLjgyLDAuOTIpLGxlZ2VuZC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0wKSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuOCwgJ2xpbmVzJykpKyBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPTQsYWxwaGE9MSkpKQoKCmEzCmE0CgpgYGAKCgoKYGBge3IgZmlnLndpZHRoPSA4LCBmaWcuaGVpZ2h0PTR9CgoKZjE9bmFtZXMocy5zYW1wVHlwZVtzLnNhbXBUeXBlPT0nV0NCTSddKQpmMj1uYW1lcyhzLnNhbXBUeXBlW3Muc2FtcFR5cGU9PSdJbWFnZS1zZXEnXSkKCmExPWNvbiRwbG90R3JhcGgoYWxwaGE9MC4zLHNpemU9MC4xNSxncm91cHM9YW5ub3RbZjFdLGZvbnQuc2l6ZT1jKDMuOSw0LjMpLHBsb3QubmE9RixwYWxldHRlPWFubm90LnBhbGYsdGl0bGU9J1dDQk0nKQphMj1jb24kcGxvdEdyYXBoKGFscGhhPTAuMyxzaXplPTAuMTUsZ3JvdXBzPWFubm90W2YyXSxmb250LnNpemU9YygzLjksNC4zKSxwbG90Lm5hPUYscGFsZXR0ZT1hbm5vdC5wYWxmLHRpdGxlPSdJbWFnZS1zZXEnKQogIAoKYiA9IGNvd3Bsb3Q6OnBsb3RfZ3JpZChwbG90bGlzdCA9IGxpc3QoYTEsYTIpLCBuY29sID0gMiwgbnJvdyA9IDEpCiAgCmIKICAKICAKYGBgCgoKY2VsbCBwb3Jwb3J0aW9ucwpgYGB7ciBmaWcud2lkdGg9IDcsIGZpZy5oZWlnaHQ9NH0KCnNhbXA9Y29uJGdldERhdGFzZXRQZXJDZWxsKCkKCmNuYW1lPWludGVyc2VjdChuYW1lcyhhbm5vdCksbmFtZXMoc2FtcCkpCgphbm8yPWRhdGEuZnJhbWUoJ0NlbGwnPWFubm90W2NuYW1lXSwnU2FtcGxlVHlwZSc9c2FtcFtjbmFtZV0pCgojIEFubm90YXRpb24gdnMgc2FtcGxlCnRtcDIgPC0gYWNhc3QoYW5vMiwgQ2VsbCB+IFNhbXBsZVR5cGUsIGZ1bi5hZ2dyZWdhdGU9bGVuZ3RoKQp0bXAzIDwtIChzd2VlcCh0bXAyLCAyLCBjb2xTdW1zKHRtcDIpLCBGVU49Jy8nKSkKdG1wNCA8LSBtZWx0KHRtcDMpCiNoZWFkKHRtcDQpCm5hbWVzKHRtcDQpIDwtIGMoJ2NlbGwnLCAnc2FtcGxlJywncGMub2Yuc2FtcGxlJykKCgpwPWdncGxvdCh0bXA0LCBhZXMoeD1zYW1wbGUsIGZpbGw9Y2VsbCwgeSA9IHBjLm9mLnNhbXBsZSkpICsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHBvc2l0aW9uPSdmaWxsJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1hbm5vdC5wYWwpKyBjb29yZF9mbGlwKCkrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSwKICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCkpCgpwPXAgICt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSxwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQpwCmBgYAoKCgpgYGB7ciBmaWcud2lkdGg9IDUsIGZpZy5oZWlnaHQ9NH0KCgpjbmFtZSA9IG5hbWVzKGFubm90KQpjbmFtZSA9IGludGVyc2VjdChjbmFtZSxuYW1lcyhzLnNhbXBUeXBlKSkKYW5vMj1kYXRhLmZyYW1lKCdDZWxsJz1hbm5vdFtjbmFtZV0sJ1NhbXBsZVR5cGUnPXMuc2FtcFR5cGVbY25hbWVdKQoKIyBBbm5vdGF0aW9uIHZzIHNhbXBsZQp0bXAyIDwtIGFjYXN0KGFubzIsIENlbGwgfiBTYW1wbGVUeXBlLCBmdW4uYWdncmVnYXRlPWxlbmd0aCkKdG1wMyA8LSAoc3dlZXAodG1wMiwgMiwgY29sU3Vtcyh0bXAyKSwgRlVOPScvJykpCnRtcDQgPC0gbWVsdCh0bXAzKQojaGVhZCh0bXA0KQpuYW1lcyh0bXA0KSA8LSBjKCdjZWxsJywgJ3NhbXBsZScsJ3BjLm9mLnNhbXBsZScpCgoKcD1nZ3Bsb3QodG1wNCwgYWVzKHk9cGMub2Yuc2FtcGxlLCBmaWxsPWNlbGwsIHggPSBzYW1wbGUpKSArdGhlbWVfYncoKSsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHBvc2l0aW9uPSdmaWxsJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1hbm5vdC5wYWwpKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSksCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSxwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSkKCnAKYGBgCgoKYGBge3J9CnAyYWxsID0gcmVhZFJEUygncmF3LnYyX3AyY29tYmluZWQucmRzJykKYGBgCgoKCmBgYHtyfQpmY29sPXJhaW5ib3coMikKbmFtZXMoZmNvbCk9dW5pcXVlKHMuc2FtcFR5cGUpCmZjb2wKYGBgCgoKQ29tcGFyaXNpb24gb2YgZGF0YSBxdWFsaXR5OyB0b3RhbCBVTUkgcGVyIGNlbGwKYGBge3IgZmlnLndpZHRoPSA0LCBmaWcuaGVpZ2h0PTR9CgoKY25hbWUgPSBpbnRlcnNlY3QobmFtZXMocy5zYW1wVHlwZSkscm93bmFtZXMocDJhbGwkbWlzYyRyYXdDb3VudHMpKQp4PXQocDJhbGwkbWlzYyRyYXdDb3VudHMpWyxjbmFtZV0KY2VsbGFubz1zLnNhbXBUeXBlW2NuYW1lXQoKY2VsbD1jZWxsYW5vW2NvbG5hbWVzKHgpXQpjb2xzPWNvbFN1bXMoYXMubWF0cml4KHgpKQpleHBHPWFwcGx5KHgsMixmdW5jdGlvbih4KSBsZW5ndGgoeFt4PjBdKSkKCgpkYXQyPWRhdGEuZnJhbWUoJ2NlbGxzJz1jZWxsLCd5Jz1sb2cxMChjb2xzKSkKCnAxPWdncGxvdChkYXQyLCBhZXMoeD1jZWxscywgeT15LGZpbGw9Y2VsbCkpICsgCiAgZ2VvbV92aW9saW4ob3V0bGllci5zaGFwZSA9IC0xLHdpZHRoPTAuNSxwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjEpKSt0aGVtZV9jbGFzc2ljKCkrCiAgI2dlb21fcG9pbnQoZGF0YSA9IGRhdDEsc2l6ZT0wLjUsY29sb3I9YWRqdXN0Y29sb3IoMSxhbHBoYT0wLjMpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcmRvZGdlKDAuMykpKwogIHRoZW1lKGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lng9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSsKICB0aGVtZSggYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAwLjUpKSt5bGFiKCd0b3RhbCBVTUkgcGVyIGNlbGwgKGxvZzEwKScpK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWZjb2wpCgpwMQpgYGAKCm51bWJlciBvZiBleHByZXNzZWQgZ2VuZXMgcGVyIGNlbGwKYGBge3IgZmlnLndpZHRoPSA0LCBmaWcuaGVpZ2h0PTR9CgpkYXQxPWRhdGEuZnJhbWUoJ2NlbGxzJz1jZWxsLCd5Jz1leHBHKQoKcDE9Z2dwbG90KGRhdDEsIGFlcyh4PWNlbGxzLCB5PXksZmlsbD1jZWxsKSkgKyB0aGVtZV9jbGFzc2ljKCkrCiAgZ2VvbV92aW9saW4ob3V0bGllci5zaGFwZSA9IC0xLHdpZHRoPTAuNSxwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjEpKSsKICAjZ2VvbV9wb2ludChkYXRhID0gZGF0MSxzaXplPTAuNSxjb2xvcj1hZGp1c3Rjb2xvcigxLGFscGhhPTAuMyksIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyZG9kZ2UoMC4zKSkrCiAgdGhlbWUoYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueD0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpKwogIHRoZW1lKCBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDAuNSkpK3lsYWIoJ251bWJlciBvZiBleHByZXNzZWQgZ2VuZXMnKSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1mY29sKQoKcDEKCgpgYGAKCgoKCgoKCmBgYHtyfQpuTG9jYWwgPSBuYW1lcyhzLnNhbXBUeXBlW3Muc2FtcFR5cGU9PSdJbWFnZS1zZXEnXSkKblcgPSBuYW1lcyhzLnNhbXBUeXBlW3Muc2FtcFR5cGU9PSdXQ0JNJ10pCmRlTD1wMmFsbCRnZXREaWZmZXJlbnRpYWxHZW5lcyhncm91cHM9YW5ub3RbbkxvY2FsXSx6LnRocmVzaG9sZCA9IDIpCmRlVz1wMmFsbCRnZXREaWZmZXJlbnRpYWxHZW5lcyhncm91cHM9YW5ub3RbblddLHoudGhyZXNob2xkID0gMikKCmBgYAoKCgpgYGB7cn0KZ2wxIDwtIHVubmFtZSh1bmxpc3QobGFwcGx5KGRlTCwgZnVuY3Rpb24oeCkgewogIHg9eFt4JFo+MCxdCiAgeCA8LSB4W29yZGVyKHgkWixkZWNyZWFzaW5nPVQpLF0KICBoZWFkKHJvd25hbWVzKHgpLG49MjAwKQp9KSkpCgpnbDIgPC0gdW5uYW1lKHVubGlzdChsYXBwbHkoZGVXLCBmdW5jdGlvbih4KSB7CiAgeD14W3gkWj4wLF0KICB4IDwtIHhbb3JkZXIoeCRaLGRlY3JlYXNpbmc9VCksXQogIGhlYWQocm93bmFtZXMoeCksbj0yMDApCn0pKSkKYGBgCgoKCmBgYHtyfQpncz11bmlxdWUoYyhnbDEsZ2wyKSkKbGVuZ3RoKGdzKQpgYGAKCgpgYGB7cn0KY25hbWUgPSBpbnRlcnNlY3QobmFtZXMoYW5ub3QpLG5hbWVzKHMuc2FtcFR5cGUpKQphbm8yID0gcGFzdGUoYW5ub3RbY25hbWVdLHMuc2FtcFR5cGVbY25hbWVdKQpuYW1lcyhhbm8yKSA9IGNuYW1lCgoKCnQuZXhwPXAyYWxsJGNvdW50c1tuYW1lcyhhbm8yKSxnbDFdCgpjbWEyPWFwcGx5KHQuZXhwLDIsZnVuY3Rpb24oeCkgdGFwcGx5KHgsYW5vMixtZWFuKSkKI2RpbShjbWEyKQoKY21hLmNvci5zcGVhcm1hbiA8LSBjb3IoYXMubWF0cml4KHQoY21hMikpLG1ldGhvZD0nc3BlYXJtYW4nKQpoYy5zcGVhcm1hbjIgPC0gaGNsdXN0KGFzLmRpc3QoMS1jbWEuY29yLnNwZWFybWFuKSkKCmBgYAoKCgoKYGBge3J9CmNtYS5jb3Iuc3BlYXJtYW4yID0gY21hLmNvci5zcGVhcm1hbltncmVwbCgnSW1hZ2Utc2VxJyxyb3duYW1lcyhjbWEuY29yLnNwZWFybWFuKSksZ3JlcGwoJ1dDQk0nLGNvbG5hbWVzKGNtYS5jb3Iuc3BlYXJtYW4pKV0KCmBgYAoKCgpgYGB7cn0KbjEgPSByb3duYW1lcyhjbWEuY29yLnNwZWFybWFuMikKbjI9Y29sbmFtZXMoY21hLmNvci5zcGVhcm1hbjIpCm4xPWdzdWIoJyBJbWFnZS1zZXEnLCcnLG4xKQpuMj1nc3ViKCcgV0NCTScsJycsbjEpCnJvd25hbWVzKGNtYS5jb3Iuc3BlYXJtYW4yKSA9IG4xCmNvbG5hbWVzKGNtYS5jb3Iuc3BlYXJtYW4yKSA9IG4yCmBgYAoKYGBge3IgZmlnLndpZHRoPSA3LCBmaWcuaGVpZ2h0PTZ9CgpwaGVhdG1hcChjbWEuY29yLnNwZWFybWFuMix0cmVlaGVpZ2h0X3Jvdz0yNSx0cmVlaGVpZ2h0X2NvbD0yNSxoZWlnaHQ9NSx3aWR0aD01LjIpCgpgYGAKCgpgYGB7cn0KYW5ub3QucGFsWydBTUwnXT0ncmVkJwoKYW5ub3QucGFsZiA8LSBmdW5jdGlvbihuKSByZXR1cm4oYW5ub3QucGFsKQoKCmBgYAoKCgpHRlAgZXhwcmVzc2lvbiBpbiBIaWdoIEJ1cmRlbiBhbmQgTG93IEJ1cmRlbiBtaWNlCmBgYHtyIGZpZy53aWR0aD0gNCwgZmlnLmhlaWdodD00fQoKI3AyMSBIaWdoIEJ1cmRlbgojcDIyIExvdyBCdXJkZW4KCmExPWVtYmVkZGluZ1Bsb3QocDIyJGVtYmVkZGluZ3MkUENBJHRTTkUsIGdyb3VwcyA9IGFubzIscGxvdC50aGVtZSA9IHRoZW1lLGZvbnQuc2l6ZSA9IGMoNC40LDUuNSkscGFsZXR0ZSA9IGFubm90LnBhbGYpCmEyPWVtYmVkZGluZ1Bsb3QocDIyJGVtYmVkZGluZ3MkUENBJHRTTkUsIGNvbG9ycyA9IHAyMiRjb3VudHNbLCdlZ2ZwJ10scGxvdC50aGVtZSA9IHRoZW1lKQoKYTM9ZW1iZWRkaW5nUGxvdChwMjEkZW1iZWRkaW5ncyRQQ0EkdFNORSwgZ3JvdXBzID0gYW5vMixwbG90LnRoZW1lID0gdGhlbWUsZm9udC5zaXplID0gYyg0LjQsNS41KSxwYWxldHRlID0gYW5ub3QucGFsZikKCnQ9cmVwKDAsbnJvdyhwMjEkY291bnRzKSkKbmFtZXModCkgPSByb3duYW1lcyhwMjEkY291bnRzKQphND1lbWJlZGRpbmdQbG90KHAyMSRlbWJlZGRpbmdzJFBDQSR0U05FLCBjb2xvcnMgPSB0LHBsb3QudGhlbWUgPSB0aGVtZSkKCmExCmEyCmEzCmE0Cmdnc2F2ZSgnaGlnaC5hbm8ucGRmJyxhMSxoZWlnaHQ9NCx3aWR0aD00KQpnZ3NhdmUoJ2xvdy5hbm8ucGRmJyxhMyxoZWlnaHQ9NCx3aWR0aD00KQpnZ3NhdmUoJ2hpZ2guR0ZQLnBkZicsYTIsaGVpZ2h0PTQsd2lkdGg9NCkKZ2dzYXZlKCdsb3cuR0ZQLnBkZicsYTQsaGVpZ2h0PTQsd2lkdGg9NCkKCmBgYAoKCgo=