Identification of differential acceessibilities
First, the region x sample count matrix was loaded in R and removed
non-acceessible regions in most of the samples
inge = read.table("ChIP-seq raw technical reps n4 RPMI C1q.txt")
library(edgeR)
cpms = cpm(inge)
keep = rowSums(cpms > 1) >= 2
inge = inge[keep,]
Because this is a multifactor design and donor variability should be
removed by GLM. Therefore, we make a design matrix and donor-variability
is removed.
coldata =data.frame(row.names = colnames(inge))
coldata$donor = c(rep("donor1",2),rep("donor2",2),rep("donor3",2),rep("donor4",2))
coldata$condition = rep(c("RPMI","C1q"),4)
design = model.matrix( ~ donor + condition, coldata)
design
(Intercept) donordonor2 donordonor3 donordonor4 conditionRPMI
RPMIn1 1 0 0 0 1
C1qn1 1 0 0 0 0
RPMIn2 1 1 0 0 1
C1qn2 1 1 0 0 0
RPMIn3 1 0 1 0 1
C1qn3 1 0 1 0 0
RPMIn4 1 0 0 1 1
C1qn4 1 0 0 1 0
attr(,"assign")
[1] 0 1 1 1 2
attr(,"contrasts")
attr(,"contrasts")$donor
[1] "contr.treatment"
attr(,"contrasts")$condition
[1] "contr.treatment"
d = DGEList(counts = inge, group = rep(c("RPMI","C1q"),4),sample = coldata)
d = calcNormFactors(d)
plotMDS(d, labels = rownames(coldata),col = c("darkgreen","blue")[factor(coldata$condition)])
d2 = estimateGLMTrendedDisp(d, design)
d2 = estimateGLMTagwiseDisp(d2, design)
f = glmFit(d2, design)
de = glmLRT(f, coef = 5)
tt = topTags(de, n = nrow(d))
head(tt$table)
Keep in mind that C1q is 0 and RPMI is 1 in this design matrix and
this means that logFC > 0 is downregulated (i.e.Ā this program
calculats RMPI/C1q, not C1q/RPMI)
A good thing is that C1q and RPMI samples are likely clustered
seperately in the MDS plot. But, the result suggests that the most of
regions are not FDR<0.05. Now we checked the number of differential
accessibilities in various threshold.
library(ggplot2)
das = tt$table
rownames(das) = paste0("chr",rownames(das))
#FDR < 0.05
tmp1 = nrow(subset(das,FDR < 0.05 & logFC < 0))
tmp2 = nrow(subset(das,FDR < 0.05 & logFC > 0))
#Pvalue < 0.01
tmp3 = nrow(subset(das,PValue < 0.01 & logFC < 0))
tmp4 = nrow(subset(das,PValue < 0.01 & logFC > 0))
#Pvalue < 0.05
tmp5 = nrow(subset(das,PValue < 0.05 & logFC < 0))
tmp6 = nrow(subset(das,PValue < 0.05 & logFC > 0))
df = data.frame(threshold=rep(c("FDR0.05","Pval0.01","Pval0.05"),each=2),Up_Down=rep(c("Up","Down"),3),Number=c(tmp1,-tmp2,tmp3,-tmp4,tmp5,-tmp6))
df$Up_Down = factor(df$Up_Down,levels = c("Up","Down"))
p = ggplot(df,aes(x= threshold,y=Number,fill=Up_Down)) + geom_bar(stat = "identity")
p + theme_minimal() + coord_flip()
As FDR< 0.05 threshold gives us almost no regions, we set P val
<0.01 as a threshold. We may not be able to say āsignificantlyā
differential accessibilities, but we will see a trend of differential
accessibilities induced by C1q.
Peak annotation
Annotation of all peaks
Now we identified differential accessibility between RPMI and C1q
samples. Next step is to annotate peaks. To annotate, I use ChIPseeker.
The coordinate notion in the original data is without āchrā and some
software does not accept this format. So, I add āchrā in the coordinate
before starting annotation. First, we look at all differential
accessibilities (Upregulated and downregulated) and then check up- and
down-regulated regions indivisually.
Here I show the peaksā genomic context and some enriched ontologies
in the text format and dotplot.
library(GenomicRanges)
library(ChIPseeker)
library(TxDb.Hsapiens.UCSC.hg38.knownGene)
library(org.Hs.eg.db)
library(clusterProfiler)
das = subset(tt$table, PValue < 0.01)
rownames(das) =paste0("chr",rownames(das))
gr = GRanges(rownames(das))
gr_df = as.data.frame(gr)
peakAnno <- annotatePeak(gr, tssRegion=c(-3000, 3000),TxDb=TxDb.Hsapiens.UCSC.hg38.knownGene, annoDb="org.Hs.eg.db")
>> preparing features information... 2024-01-29 14:11:46
>> identifying nearest features... 2024-01-29 14:11:46
>> calculating distance from peak to TSS... 2024-01-29 14:11:47
>> assigning genomic annotation... 2024-01-29 14:11:47
>> adding gene annotation... 2024-01-29 14:11:51
'select()' returned 1:many mapping between keys and columns
>> assigning chromosome lengths 2024-01-29 14:11:51
>> done... 2024-01-29 14:11:52
plotAnnoPie(peakAnno)
gene <- seq2gene(gr, tssRegion = c(-1000, 1000), flankDistance = 3000, TxDb=TxDb.Hsapiens.UCSC.hg38.knownGene)
pathway2 = enrichGO(gene,OrgDb = org.Hs.eg.db,ont="BP",readable = T)
head(pathway2, 5)
dotplot(pathway2)
write.table(as.data.frame(pathway2),"EnrichedOntology_Pval0.01.txt",sep = "\t",quote = F)
#checking peakAnno and das have same order
tmp = paste(as.data.frame(peakAnno)$seqnames,as.data.frame(peakAnno)$start,sep =":")
tmp = paste(tmp,as.data.frame(peakAnno)$end, sep="-")
if( sum(tmp== rownames(das))== nrow(das)){
das = cbind(das,as.data.frame(peakAnno))
write.table(das,"DiffAcc_PVal0.01.txt",sep = "\t",quote = F)
}
Annotation of upregulated peaks only
## Upegulated
das = subset(tt$table, PValue < 0.01 & logFC <0)
rownames(das) =paste0("chr",rownames(das))
gr = GRanges(rownames(das))
gr_df = as.data.frame(gr)
peakAnno <- annotatePeak(gr, tssRegion=c(-3000, 3000),TxDb=TxDb.Hsapiens.UCSC.hg38.knownGene, annoDb="org.Hs.eg.db")
>> preparing features information... 2024-01-29 14:12:12
>> identifying nearest features... 2024-01-29 14:12:12
>> calculating distance from peak to TSS... 2024-01-29 14:12:12
>> assigning genomic annotation... 2024-01-29 14:12:12
>> adding gene annotation... 2024-01-29 14:12:17
'select()' returned 1:many mapping between keys and columns
>> assigning chromosome lengths 2024-01-29 14:12:17
>> done... 2024-01-29 14:12:17
plotAnnoPie(peakAnno)
gene <- seq2gene(gr, tssRegion = c(-1000, 1000), flankDistance = 3000, TxDb=TxDb.Hsapiens.UCSC.hg38.knownGene)
pathway2 = enrichGO(gene,OrgDb = org.Hs.eg.db,ont="BP",readable = T)
head(pathway2, 5)
dotplot(pathway2)
write.table(as.data.frame(pathway2),"EnrichedOntology_Pval0.01_Up.txt",sep = "\t",quote = F)
#checking peakAnno and das have same order
tmp = paste(as.data.frame(peakAnno)$seqnames,as.data.frame(peakAnno)$start,sep =":")
tmp = paste(tmp,as.data.frame(peakAnno)$end, sep="-")
if( sum(tmp== rownames(das))== nrow(das)){
das = cbind(das,as.data.frame(peakAnno))
write.table(das,"DiffAcc_PVal0.01_Up.txt",sep = "\t",quote = F)
}
Annotation of downregulated peaks only
## Downregulated
das = subset(tt$table, PValue < 0.01 & logFC >0)
rownames(das) =paste0("chr",rownames(das))
gr = GRanges(rownames(das))
gr_df = as.data.frame(gr)
peakAnno <- annotatePeak(gr, tssRegion=c(-3000, 3000),TxDb=TxDb.Hsapiens.UCSC.hg38.knownGene, annoDb="org.Hs.eg.db")
plotAnnoPie(peakAnno)
gene <- seq2gene(gr, tssRegion = c(-1000, 1000), flankDistance = 3000, TxDb=TxDb.Hsapiens.UCSC.hg38.knownGene)
pathway2 = enrichGO(gene,OrgDb = org.Hs.eg.db,ont="BP",readable = T)
head(pathway2, 5)
dotplot(pathway2)
write.table(as.data.frame(pathway2),"EnrichedOntology_Pval0.01_Down.txt",sep = "\t",quote = F)
#checking peakAnno and das have same order
tmp = paste(as.data.frame(peakAnno)$seqnames,as.data.frame(peakAnno)$start,sep =":")
tmp = paste(tmp,as.data.frame(peakAnno)$end, sep="-")
if( sum(tmp== rownames(das))== nrow(das)){
das = cbind(das,as.data.frame(peakAnno))
write.table(das,"DiffAcc_PVal0.01_Down.txt",sep = "\t",quote = F)
}
LS0tCnRpdGxlOiAiUmFwaGHDq2wgQzFxIGRhdGEgYW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6ICJZdXRha2EgTmVnaXNoaSAoeXV0YWthQG1obGFuZ2FsYWIub3JnKSIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgotLS0KCgpUaGlzIHBhZ2UgZGVzY3JpYmVkIHRoZSBjb2RlIGFuZCB0aGUgcmVzdWx0IG9mIFtDMXEgZGF0YV0oaHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2ZpbGUvZC8xUlVVNjZKRkJMNkJxV1AxTXJydWN4S3VRdG51WlFpUUMvdmlldz91c3A9c2hhcmluZykgc2hhcmVkIGJ5IEluZ2UuCiAKIyMgSWRlbnRpZmljYXRpb24gb2YgZGlmZmVyZW50aWFsIGFjY2Vlc3NpYmlsaXRpZXMKCkZpcnN0LCB0aGUgcmVnaW9uIHggc2FtcGxlIGNvdW50IG1hdHJpeCB3YXMgbG9hZGVkIGluIFIgYW5kIHJlbW92ZWQgbm9uLWFjY2Vlc3NpYmxlIHJlZ2lvbnMgaW4gbW9zdCBvZiB0aGUgc2FtcGxlcwoKYGBge3J9CmluZ2UgPSByZWFkLnRhYmxlKCJDaElQLXNlcSByYXcgdGVjaG5pY2FsIHJlcHMgbjQgUlBNSSBDMXEudHh0IikKbGlicmFyeShlZGdlUikKY3BtcyA9IGNwbShpbmdlKQprZWVwID0gcm93U3VtcyhjcG1zID4gMSkgPj0gMgppbmdlID0gaW5nZVtrZWVwLF0KYGBgCgoKQmVjYXVzZSB0aGlzIGlzIGEgbXVsdGlmYWN0b3IgZGVzaWduIGFuZCBkb25vciB2YXJpYWJpbGl0eSBzaG91bGQgYmUgcmVtb3ZlZCBieSBHTE0uIFRoZXJlZm9yZSwgd2UgbWFrZSBhIGRlc2lnbiBtYXRyaXggYW5kIGRvbm9yLXZhcmlhYmlsaXR5IGlzIHJlbW92ZWQuICAKCmBgYHtyfQpjb2xkYXRhID1kYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGNvbG5hbWVzKGluZ2UpKQpjb2xkYXRhJGRvbm9yID0gYyhyZXAoImRvbm9yMSIsMikscmVwKCJkb25vcjIiLDIpLHJlcCgiZG9ub3IzIiwyKSxyZXAoImRvbm9yNCIsMikpCmNvbGRhdGEkY29uZGl0aW9uID0gcmVwKGMoIlJQTUkiLCJDMXEiKSw0KQpkZXNpZ24gPSBtb2RlbC5tYXRyaXgoIH4gZG9ub3IgKyBjb25kaXRpb24sIGNvbGRhdGEpCmRlc2lnbgpkID0gREdFTGlzdChjb3VudHMgPSBpbmdlLCBncm91cCA9IHJlcChjKCJSUE1JIiwiQzFxIiksNCksc2FtcGxlID0gY29sZGF0YSkKZCA9IGNhbGNOb3JtRmFjdG9ycyhkKQpwbG90TURTKGQsIGxhYmVscyA9IHJvd25hbWVzKGNvbGRhdGEpLGNvbCA9IGMoImRhcmtncmVlbiIsImJsdWUiKVtmYWN0b3IoY29sZGF0YSRjb25kaXRpb24pXSkKZDIgPSBlc3RpbWF0ZUdMTVRyZW5kZWREaXNwKGQsIGRlc2lnbikKZDIgPSBlc3RpbWF0ZUdMTVRhZ3dpc2VEaXNwKGQyLCBkZXNpZ24pCmYgPSBnbG1GaXQoZDIsIGRlc2lnbikKZGUgPSBnbG1MUlQoZiwgY29lZiA9IDUpCnR0ID0gdG9wVGFncyhkZSwgbiA9IG5yb3coZCkpCmhlYWQodHQkdGFibGUpCmBgYApLZWVwIGluIG1pbmQgdGhhdCBDMXEgaXMgMCBhbmQgUlBNSSBpcyAxIGluIHRoaXMgZGVzaWduIG1hdHJpeCBhbmQgdGhpcyBtZWFucyB0aGF0IGxvZ0ZDID4gMCBpcyBkb3ducmVndWxhdGVkIChpLmUuIHRoaXMgcHJvZ3JhbSBjYWxjdWxhdHMgUk1QSS9DMXEsIG5vdCBDMXEvUlBNSSkKCkEgZ29vZCB0aGluZyBpcyB0aGF0IEMxcSBhbmQgUlBNSSBzYW1wbGVzIGFyZSBsaWtlbHkgY2x1c3RlcmVkIHNlcGVyYXRlbHkgaW4gdGhlIE1EUyBwbG90LiBCdXQsIHRoZSByZXN1bHQgc3VnZ2VzdHMgdGhhdCB0aGUgbW9zdCBvZiByZWdpb25zIGFyZSBub3QgRkRSPDAuMDUuIE5vdyB3ZSBjaGVja2VkIHRoZSBudW1iZXIgb2YgZGlmZmVyZW50aWFsIGFjY2Vzc2liaWxpdGllcyBpbiB2YXJpb3VzIHRocmVzaG9sZC4KCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpkYXMgPSB0dCR0YWJsZQpyb3duYW1lcyhkYXMpID0gcGFzdGUwKCJjaHIiLHJvd25hbWVzKGRhcykpCgojRkRSIDwgMC4wNQp0bXAxID0gbnJvdyhzdWJzZXQoZGFzLEZEUiA8IDAuMDUgJiBsb2dGQyA8IDApKQp0bXAyID0gbnJvdyhzdWJzZXQoZGFzLEZEUiA8IDAuMDUgJiBsb2dGQyA+IDApKQoKI1B2YWx1ZSA8IDAuMDEKdG1wMyA9IG5yb3coc3Vic2V0KGRhcyxQVmFsdWUgPCAwLjAxICYgbG9nRkMgPCAwKSkKdG1wNCA9IG5yb3coc3Vic2V0KGRhcyxQVmFsdWUgPCAwLjAxICYgbG9nRkMgPiAwKSkKCiNQdmFsdWUgPCAwLjA1CnRtcDUgPSBucm93KHN1YnNldChkYXMsUFZhbHVlIDwgMC4wNSAmIGxvZ0ZDIDwgMCkpCnRtcDYgPSBucm93KHN1YnNldChkYXMsUFZhbHVlIDwgMC4wNSAmIGxvZ0ZDID4gMCkpCgpkZiA9IGRhdGEuZnJhbWUodGhyZXNob2xkPXJlcChjKCJGRFIwLjA1IiwiUHZhbDAuMDEiLCJQdmFsMC4wNSIpLGVhY2g9MiksVXBfRG93bj1yZXAoYygiVXAiLCJEb3duIiksMyksTnVtYmVyPWModG1wMSwtdG1wMix0bXAzLC10bXA0LHRtcDUsLXRtcDYpKSAKZGYkVXBfRG93biA9IGZhY3RvcihkZiRVcF9Eb3duLGxldmVscyA9IGMoIlVwIiwiRG93biIpKQoKcCA9IGdncGxvdChkZixhZXMoeD0gdGhyZXNob2xkLHk9TnVtYmVyLGZpbGw9VXBfRG93bikpICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpCnAgKyB0aGVtZV9taW5pbWFsKCkgKyBjb29yZF9mbGlwKCkKYGBgCgpBcyBGRFI8IDAuMDUgdGhyZXNob2xkIGdpdmVzIHVzIGFsbW9zdCBubyByZWdpb25zLCB3ZSBzZXQgUCB2YWwgPDAuMDEgYXMgYSB0aHJlc2hvbGQuIFdlIG1heSBub3QgYmUgYWJsZSB0byBzYXkgInNpZ25pZmljYW50bHkiIGRpZmZlcmVudGlhbCBhY2Nlc3NpYmlsaXRpZXMsIGJ1dCB3ZSB3aWxsIHNlZSBhIHRyZW5kIG9mIGRpZmZlcmVudGlhbCBhY2Nlc3NpYmlsaXRpZXMgaW5kdWNlZCBieSBDMXEuCgojIyBQZWFrIGFubm90YXRpb24KIyMjIEFubm90YXRpb24gb2YgYWxsIHBlYWtzCgpOb3cgd2UgaWRlbnRpZmllZCBkaWZmZXJlbnRpYWwgYWNjZXNzaWJpbGl0eSBiZXR3ZWVuIFJQTUkgYW5kIEMxcSBzYW1wbGVzLiBOZXh0IHN0ZXAgaXMgdG8gYW5ub3RhdGUgcGVha3MuIFRvIGFubm90YXRlLCBJIHVzZSBbQ2hJUHNlZWtlcl0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvQ2hJUHNlZWtlci9pbnN0L2RvYy9DaElQc2Vla2VyLmh0bWwpLgpUaGUgY29vcmRpbmF0ZSBub3Rpb24gaW4gdGhlIG9yaWdpbmFsIGRhdGEgaXMgd2l0aG91dCAiY2hyIiBhbmQgc29tZSBzb2Z0d2FyZSBkb2VzIG5vdCBhY2NlcHQgdGhpcyBmb3JtYXQuIFNvLCBJIGFkZCAiY2hyIiBpbiB0aGUgY29vcmRpbmF0ZSBiZWZvcmUgc3RhcnRpbmcgYW5ub3RhdGlvbi4KRmlyc3QsIHdlIGxvb2sgYXQgYWxsIGRpZmZlcmVudGlhbCBhY2Nlc3NpYmlsaXRpZXMgKFVwcmVndWxhdGVkIGFuZCBkb3ducmVndWxhdGVkKSBhbmQgdGhlbiBjaGVjayB1cC0gYW5kIGRvd24tcmVndWxhdGVkIHJlZ2lvbnMgaW5kaXZpc3VhbGx5LgoKSGVyZSBJIHNob3cgdGhlIHBlYWtzJyBnZW5vbWljIGNvbnRleHQgYW5kIHNvbWUgZW5yaWNoZWQgb250b2xvZ2llcyBpbiB0aGUgdGV4dCBmb3JtYXQgYW5kIGRvdHBsb3QuCgpgYGB7cn0KbGlicmFyeShHZW5vbWljUmFuZ2VzKQpsaWJyYXJ5KENoSVBzZWVrZXIpCmxpYnJhcnkoVHhEYi5Ic2FwaWVucy5VQ1NDLmhnMzgua25vd25HZW5lKQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmRhcyA9IHN1YnNldCh0dCR0YWJsZSwgIFBWYWx1ZSAgPCAwLjAxKQpyb3duYW1lcyhkYXMpID1wYXN0ZTAoImNociIscm93bmFtZXMoZGFzKSkKZ3IgPSBHUmFuZ2VzKHJvd25hbWVzKGRhcykpCmdyX2RmID0gYXMuZGF0YS5mcmFtZShncikKcGVha0Fubm8gPC0gYW5ub3RhdGVQZWFrKGdyLCB0c3NSZWdpb249YygtMzAwMCwgMzAwMCksVHhEYj1UeERiLkhzYXBpZW5zLlVDU0MuaGczOC5rbm93bkdlbmUsIGFubm9EYj0ib3JnLkhzLmVnLmRiIikKcGxvdEFubm9QaWUocGVha0Fubm8pCmdlbmUgPC0gc2VxMmdlbmUoZ3IsIHRzc1JlZ2lvbiA9IGMoLTEwMDAsIDEwMDApLCBmbGFua0Rpc3RhbmNlID0gMzAwMCwgVHhEYj1UeERiLkhzYXBpZW5zLlVDU0MuaGczOC5rbm93bkdlbmUpCnBhdGh3YXkyID0gZW5yaWNoR08oZ2VuZSxPcmdEYiA9IG9yZy5Icy5lZy5kYixvbnQ9IkJQIixyZWFkYWJsZSA9IFQpCmhlYWQocGF0aHdheTIsIDUpCmRvdHBsb3QocGF0aHdheTIpCndyaXRlLnRhYmxlKGFzLmRhdGEuZnJhbWUocGF0aHdheTIpLCJFbnJpY2hlZE9udG9sb2d5X1B2YWwwLjAxLnR4dCIsc2VwID0gIlx0IixxdW90ZSA9IEYpCiNjaGVja2luZyBwZWFrQW5ubyBhbmQgZGFzIGhhdmUgc2FtZSBvcmRlcgp0bXAgPSBwYXN0ZShhcy5kYXRhLmZyYW1lKHBlYWtBbm5vKSRzZXFuYW1lcyxhcy5kYXRhLmZyYW1lKHBlYWtBbm5vKSRzdGFydCxzZXAgPSI6IikKdG1wID0gcGFzdGUodG1wLGFzLmRhdGEuZnJhbWUocGVha0Fubm8pJGVuZCwgc2VwPSItIikKaWYoIHN1bSh0bXA9PSByb3duYW1lcyhkYXMpKT09IG5yb3coZGFzKSl7CiAgZGFzID0gY2JpbmQoZGFzLGFzLmRhdGEuZnJhbWUocGVha0Fubm8pKQogIHdyaXRlLnRhYmxlKGRhcywiRGlmZkFjY19QVmFsMC4wMS50eHQiLHNlcCA9ICJcdCIscXVvdGUgPSBGKQp9CmBgYAojIyMgQW5ub3RhdGlvbiBvZiB1cHJlZ3VsYXRlZCBwZWFrcyBvbmx5CgpgYGB7cn0KIyMgVXBlZ3VsYXRlZApkYXMgPSBzdWJzZXQodHQkdGFibGUsICBQVmFsdWUgIDwgMC4wMSAmIGxvZ0ZDIDwwKQpyb3duYW1lcyhkYXMpID1wYXN0ZTAoImNociIscm93bmFtZXMoZGFzKSkKZ3IgPSBHUmFuZ2VzKHJvd25hbWVzKGRhcykpCmdyX2RmID0gYXMuZGF0YS5mcmFtZShncikKcGVha0Fubm8gPC0gYW5ub3RhdGVQZWFrKGdyLCB0c3NSZWdpb249YygtMzAwMCwgMzAwMCksVHhEYj1UeERiLkhzYXBpZW5zLlVDU0MuaGczOC5rbm93bkdlbmUsIGFubm9EYj0ib3JnLkhzLmVnLmRiIikKcGxvdEFubm9QaWUocGVha0Fubm8pCmdlbmUgPC0gc2VxMmdlbmUoZ3IsIHRzc1JlZ2lvbiA9IGMoLTEwMDAsIDEwMDApLCBmbGFua0Rpc3RhbmNlID0gMzAwMCwgVHhEYj1UeERiLkhzYXBpZW5zLlVDU0MuaGczOC5rbm93bkdlbmUpCnBhdGh3YXkyID0gZW5yaWNoR08oZ2VuZSxPcmdEYiA9IG9yZy5Icy5lZy5kYixvbnQ9IkJQIixyZWFkYWJsZSA9IFQpCmhlYWQocGF0aHdheTIsIDUpCmRvdHBsb3QocGF0aHdheTIpCndyaXRlLnRhYmxlKGFzLmRhdGEuZnJhbWUocGF0aHdheTIpLCJFbnJpY2hlZE9udG9sb2d5X1B2YWwwLjAxX1VwLnR4dCIsc2VwID0gIlx0IixxdW90ZSA9IEYpCiNjaGVja2luZyBwZWFrQW5ubyBhbmQgZGFzIGhhdmUgc2FtZSBvcmRlcgp0bXAgPSBwYXN0ZShhcy5kYXRhLmZyYW1lKHBlYWtBbm5vKSRzZXFuYW1lcyxhcy5kYXRhLmZyYW1lKHBlYWtBbm5vKSRzdGFydCxzZXAgPSI6IikKdG1wID0gcGFzdGUodG1wLGFzLmRhdGEuZnJhbWUocGVha0Fubm8pJGVuZCwgc2VwPSItIikKaWYoIHN1bSh0bXA9PSByb3duYW1lcyhkYXMpKT09IG5yb3coZGFzKSl7CiAgZGFzID0gY2JpbmQoZGFzLGFzLmRhdGEuZnJhbWUocGVha0Fubm8pKQogIHdyaXRlLnRhYmxlKGRhcywiRGlmZkFjY19QVmFsMC4wMV9VcC50eHQiLHNlcCA9ICJcdCIscXVvdGUgPSBGKQp9CmBgYAoKIyMjIEFubm90YXRpb24gb2YgZG93bnJlZ3VsYXRlZCBwZWFrcyBvbmx5CgpgYGB7cn0KIyMgRG93bnJlZ3VsYXRlZApkYXMgPSBzdWJzZXQodHQkdGFibGUsICBQVmFsdWUgIDwgMC4wMSAmIGxvZ0ZDID4wKQpyb3duYW1lcyhkYXMpID1wYXN0ZTAoImNociIscm93bmFtZXMoZGFzKSkKZ3IgPSBHUmFuZ2VzKHJvd25hbWVzKGRhcykpCmdyX2RmID0gYXMuZGF0YS5mcmFtZShncikKcGVha0Fubm8gPC0gYW5ub3RhdGVQZWFrKGdyLCB0c3NSZWdpb249YygtMzAwMCwgMzAwMCksVHhEYj1UeERiLkhzYXBpZW5zLlVDU0MuaGczOC5rbm93bkdlbmUsIGFubm9EYj0ib3JnLkhzLmVnLmRiIikKcGxvdEFubm9QaWUocGVha0Fubm8pCmdlbmUgPC0gc2VxMmdlbmUoZ3IsIHRzc1JlZ2lvbiA9IGMoLTEwMDAsIDEwMDApLCBmbGFua0Rpc3RhbmNlID0gMzAwMCwgVHhEYj1UeERiLkhzYXBpZW5zLlVDU0MuaGczOC5rbm93bkdlbmUpCnBhdGh3YXkyID0gZW5yaWNoR08oZ2VuZSxPcmdEYiA9IG9yZy5Icy5lZy5kYixvbnQ9IkJQIixyZWFkYWJsZSA9IFQpCmhlYWQocGF0aHdheTIsIDUpCmRvdHBsb3QocGF0aHdheTIpCndyaXRlLnRhYmxlKGFzLmRhdGEuZnJhbWUocGF0aHdheTIpLCJFbnJpY2hlZE9udG9sb2d5X1B2YWwwLjAxX0Rvd24udHh0IixzZXAgPSAiXHQiLHF1b3RlID0gRikKI2NoZWNraW5nIHBlYWtBbm5vIGFuZCBkYXMgaGF2ZSBzYW1lIG9yZGVyCnRtcCA9IHBhc3RlKGFzLmRhdGEuZnJhbWUocGVha0Fubm8pJHNlcW5hbWVzLGFzLmRhdGEuZnJhbWUocGVha0Fubm8pJHN0YXJ0LHNlcCA9IjoiKQp0bXAgPSBwYXN0ZSh0bXAsYXMuZGF0YS5mcmFtZShwZWFrQW5ubykkZW5kLCBzZXA9Ii0iKQppZiggc3VtKHRtcD09IHJvd25hbWVzKGRhcykpPT0gbnJvdyhkYXMpKXsKICBkYXMgPSBjYmluZChkYXMsYXMuZGF0YS5mcmFtZShwZWFrQW5ubykpCiAgd3JpdGUudGFibGUoZGFzLCJEaWZmQWNjX1BWYWwwLjAxX0Rvd24udHh0IixzZXAgPSAiXHQiLHF1b3RlID0gRikKfQpgYGAKCgojIyMgVGhlIGZpbGVzIHdyb3RlIGluIHRoZSB0ZXh0IGZvcm1hdCBhcmUgYXZhaWxhYmxlIGZyb20gaGVyZQoKMS4gW0Fubm90YXRpb24gb2YgYWxsIHBlYWtzXShodHRwczovL2RyaXZlLmdvb2dsZS5jb20vZmlsZS9kLzFCUF8xZkd5M3R0VWVQMEFZN1QzX3lPWmFRX1JDZnNTTy92aWV3P3VzcD1zaGFyaW5nKQoKMi4gW0VucmljaGVkIEdPcyBvZiBhbGwgcGVha3NdKGh0dHBzOi8vZHJpdmUuZ29vZ2xlLmNvbS9maWxlL2QvMXRLSFlFRWEtcmUxSGJSRVhDNDhBYldkcHJwcGh0LWg3L3ZpZXc/dXNwPXNoYXJpbmcpCgozLiBbQW5ub3RhdGlvbiBvZiB1cHJlZ3VsYXRlZCBwZWFrc10oaHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2ZpbGUvZC8xeVRFMm9mWlpDQ3I4UTlaTWZDU1lVTl9PaGlfdjRjWW8vdmlldz91c3A9c2hhcmluZykKCjQuIFtFbnJpY2hlZCBHT3Mgb2YgdXByZWd1bGF0ZWQgcGVha3NdKGh0dHBzOi8vZHJpdmUuZ29vZ2xlLmNvbS9maWxlL2QvMWNMRlNINy1CaXdfd1ZHRlpmTXN4TUZXaFRyWnE3Z2dwL3ZpZXc/dXNwPXNoYXJpbmcpCgo1LiBbQW5ub3RhdGlvbiBvZiBkb3dyZWd1bGF0ZWQgcGVha3NdKGh0dHBzOi8vZHJpdmUuZ29vZ2xlLmNvbS9maWxlL2QvMWhsQnM5azBTQnZNOUJoN0U2ZmFxdGo1d2NXSjBPd1FKL3ZpZXc/dXNwPXNoYXJpbmcpCgo2LiBbRW5yaWNoZWQgR09zIG9mIGRvd25yZWd1bGF0ZWQgcGVha3NdKGh0dHBzOi8vZHJpdmUuZ29vZ2xlLmNvbS9maWxlL2QvMTZTSVMwQjZJb21JeXAtbU96cVczWmprNWNBY2J3N3FyL3ZpZXc/dXNwPXNoYXJpbmcpCg==