Document creation Libraries
library(DESeq2)
library(Rsubread)
library(tidyverse)
library(pheatmap)
library(RColorBrewer)
library(ggplot2)
library(ggrepel)
library(ggpubr)
library(cluster)
library(factoextra)
library(amap)
library(NbClust)
library(dplyr)
Set working directory to local workspace Save R markup, featurecounts results and full gtf there RNA featurecounts generated with ‘Homo_sapiens.GRCh38.99.gtf.gz’ file
setwd("/Users/kuroko/Dropbox (Partners HealthCare)/Weinstock NTC Collab/Experiments/TCL Cell Lines/Data/ALCL/Full R project scripts")
getwd()
[1] "/Users/kuroko/Dropbox (Partners HealthCare)/Weinstock NTC Collab/Experiments/TCL Cell Lines/Data/ALCL/Full R project scripts"
RNA
Load featurecounts results, configure as data frame object Setup metadata information and check naming consistency Remove non-counts columns in fc object
fc <- read.table("RNA_ALCL_wholegenecounts", sep="\t", header=TRUE, row.names=1)
fc2 <- as.data.frame(fc)
orig_names <- colnames(fc2[6:14])
colData_rna <- data.frame(row.names=orig_names, sample=c("KARPAS299", "L82", "SUPM2", "SUDHL1", "KIJK", "SR786", "FEPD", "DL40", "MAC2A"), subtype=c("ALK+ALCL", "ALK+ALCL", "ALK+ALCL", "ALK+ALCL", "ALK+ALCL", "ALK+ALCL", "ALK-ALCL", "ALK-ALCL", "ALK-ALCL"))
all(rownames(colData_rna) %in% colnames(fc2))
[1] TRUE
fc3 <- subset(fc2, select=-c(Chr, Start, End, Strand, Length))
colnames(fc3) <- c("KARPAS299", "L82", "SUPM2", "SUDHL1", "KIJK", "SR786", "FEPD", "DL40", "MAC2A")
Create DEseq object and save file Generate normalization factor with DEseq default
dds_rna <- DESeqDataSetFromMatrix(countData=fc3, colData=colData_rna, design=~sample)
Warning in DESeqDataSet(se, design = design, ignoreRank) :
some variables in design formula are characters, converting to factors
dds_rna <- estimateSizeFactors(dds_rna)
deseq_sizes_rna <- as.data.frame(sizeFactors(dds_rna))
row.names(deseq_sizes_rna) <- c("KARPAS299", "L82", "SUPM2", "SUDHL1", "KIJK", "SR786", "FEPD", "DL40", "MAC2A")
deseq_sizes_rna
saveRDS(dds_rna, file="RNA_dds.rds")
Generate norm counts files and save to local directory
rawcounts_rna <- as.data.frame(counts(dds_rna, normalized=FALSE))
normcounts_rna <- as.data.frame(counts(dds_rna, normalized=TRUE))
colnames(rawcounts_rna) <- c("KARPAS299", "L82", "SUPM2", "SUDHL1", "KIJK", "SR786", "FEPD", "DL40", "MAC2A")
colnames(normcounts_rna) <- c("KARPAS299", "L82", "SUPM2", "SUDHL1", "KIJK", "SR786", "FEPD", "DL40", "MAC2A")
#rownames(rawcounts_rna) <- sapply(strsplit(rownames(rawcounts_rna), split="[.]"), head, 1)
#rownames(normcounts_rna) <- sapply(strsplit(rownames(normcounts_rna), split="[.]"), head, 1)
write.table(rawcounts_rna, file="RNA_rawcounts.txt", sep="\t", col.names=TRUE, row.names=TRUE, quote=FALSE)
write.table(normcounts_rna, file="RNA_normcounts.txt", sep="\t", col.names=TRUE, row.names=TRUE, quote=FALSE)
Data transformation for plot generation Top 500 contributors is the default for PCA Set ‘ntop’ to number of observations in the dds object
rld_rna <- rlog(dds_rna, blind=TRUE)
sampleDistsrna <- dist(t(assay(rld_rna)))
sampleDistMtxrna <- as.matrix(sampleDistsrna)
rownames(sampleDistMtxrna) <- paste0(rld_rna$sample)
colnames(sampleDistMtxrna) <- NULL
#Generate PCA plot (intgroup set equal to desired metadata variable)
plotPCA(rld_rna, intgroup = "subtype", ntop=60676) + ggtitle("RNA-seq PCA") + scale_color_manual(breaks = c("ALK+ALCL","ALK-ALCL"), values=c("chartreuse3", "cadetblue")) + geom_text_repel(label=rownames(sampleDistMtxrna), max.overlaps=50)

#top 500 PCA
plotPCA(rld_rna, intgroup = "subtype") + ggtitle("RNA-seq PCA Top 500") + scale_color_manual(breaks = c("ALK+ALCL","ALK-ALCL"), values=c("chartreuse3", "cadetblue")) + geom_text_repel(label=rownames(sampleDistMtxrna), max.overlaps=50)

#top 1000
plotPCA(rld_rna, intgroup = "subtype", ntop=1000) + ggtitle("RNA-seq PCA Top 1000") + scale_color_manual(breaks = c("ALK+ALCL","ALK-ALCL"), values=c("chartreuse3", "cadetblue")) + geom_text_repel(label=rownames(sampleDistMtxrna), max.overlaps=50)

#top 5000
plotPCA(rld_rna, intgroup = "subtype", ntop=5000) + ggtitle("RNA-seq PCA Top 5000") + scale_color_manual(breaks = c("ALK+ALCL","ALK-ALCL"), values=c("chartreuse3", "cadetblue")) + geom_text_repel(label=rownames(sampleDistMtxrna), max.overlaps=50)

#top 10000
plotPCA(rld_rna, intgroup = "subtype", ntop=10000) + ggtitle("RNA-seq PCA Top 10000") + scale_color_manual(breaks = c("ALK+ALCL","ALK-ALCL"), values=c("chartreuse3", "cadetblue")) + geom_text_repel(label=rownames(sampleDistMtxrna), max.overlaps=50)

#top 15000
plotPCA(rld_rna, intgroup = "subtype", ntop=15000) + ggtitle("RNA-seq PCA Top 15000") + scale_color_manual(breaks = c("ALK+ALCL","ALK-ALCL"), values=c("chartreuse3", "cadetblue")) + geom_text_repel(label=rownames(sampleDistMtxrna), max.overlaps=50)

Additional dendrogram plots
colors <- colorRampPalette(rev(brewer.pal(9, "Blues")))(255)
pheatmap(sampleDistMtxrna, col=colors, border_color=NA, show_colnames=FALSE, show_rownames=TRUE,
cluster_rows=TRUE, cluster_cols=TRUE, main="ALCL RNA Sample Distance Clustering")

plot(hclust(sampleDistsrna), labels=rownames(sampleDistMtxrna), main="ALCL RNA Sample Distance Dendrogram")

Assemble counts results
avg_rna <- data.frame(EnsemblID=rownames(normcounts_rna))
avg_rna$KARPAS299 <- (normcounts_rna$KARPAS299)
avg_rna$L82 <- (normcounts_rna$L82)
avg_rna$SUPM2 <- (normcounts_rna$SUPM2)
avg_rna$SUDHL1 <- (normcounts_rna$SUDHL1)
avg_rna$KIJK <- (normcounts_rna$KIJK)
avg_rna$SR786 <- (normcounts_rna$SR786)
avg_rna$FEPD <- (normcounts_rna$FEPD)
avg_rna$DL40 <- (normcounts_rna$DL40)
avg_rna$MAC2A <- (normcounts_rna$MAC2A)
avg_rna$max <- apply(avg_rna[c("KARPAS299", "L82", "SUPM2", "SUDHL1", "KIJK", "SR786", "FEPD", "DL40", "MAC2A")], 1, max, na.rm=TRUE)
rel_rna <- data.frame(EnsemblID=avg_rna$EnsemblID)
rel_rna$KARPAS299 <- avg_rna$KARPAS299 / avg_rna$max
rel_rna$L82 <- avg_rna$L82 / avg_rna$max
rel_rna$SUPM2 <- avg_rna$SUPM2 / avg_rna$max
rel_rna$SUDHL1 <- avg_rna$SUDHL1 / avg_rna$max
rel_rna$KIJK <- avg_rna$KIJK / avg_rna$max
rel_rna$SR786 <- avg_rna$SR786 / avg_rna$max
rel_rna$FEPD <- avg_rna$FEPD / avg_rna$max
rel_rna$DL40 <- avg_rna$DL40 / avg_rna$max
rel_rna$MAC2A <- avg_rna$MAC2A / avg_rna$max
Data transform for variance assessment and sub-clustering
rv1 <- rowVars(assay(rld_rna))
select1 <- order(rv1, decreasing=TRUE)
pc1 <- prcomp(t(assay(rld_rna)[select1,]))
loadings1 <- as.data.frame(pc1$rotation)
aload1 <- abs(loadings1)
pcRank <- data.frame("EnsemblID"=rownames(loadings1), "PCA.rank"=seq.int(nrow(loadings1)))
sweep(aload1, 2, colSums(aload1), "/")
peakVar <- data.frame("EnsemblID"=rownames(assay(rld_rna)))
peakVar$rlog.variance <- rowVars(as.matrix(assay(rld_rna)))
peakVar <- merge(pcRank, peakVar, by.x="EnsemblID", by.y="EnsemblID")
ggplot(peakVar, aes(x=PCA.rank, y=rlog.variance)) + geom_line() + geom_vline(xintercept=c(500,1000,2000,5000,10000,10000,15000), linetype="dotted") + ggtitle("ALCL Variance per PC Contributor")

#add plot of fraction of explained variance.. running sum / total vs rank
peakVar <- peakVar[order(peakVar$PCA.rank),]
peakVar$cumulative.Variance <- cumsum(peakVar$rlog.variance)/sum(peakVar$rlog.variance)
ggplot(peakVar, aes(x=PCA.rank, y=cumulative.Variance)) + geom_line() + geom_vline(xintercept=c(500,1000,2000,5000,10000,10000,15000), linetype="dotted") + ggtitle("ALCL Cumulative Variance per PC Rank")

PCA top contributors selection (5K to 20K)
rv <- rowVars(assay(rld_rna))
select <- order(rv, decreasing=TRUE)[seq_len(min(1000, length(rv)))]
#Changing the 1000 here will select a Different number of top contributors if desired
pc <- prcomp(t(assay(rld_rna)[select,]))
loadings <- as.data.frame(pc$rotation)
aload <- abs(loadings)
write.table(sweep(aload, 2, colSums(aload), "/"), file="Top 1000 ALCL RNA PCA Contributors.txt")
#Clean up variables
rm(rv)
rm(select)
rm(pc)
rm(loadings)
#Merge top 10000 elements with their annotations
top1000 <- rel_rna[(rel_rna$EnsemblID %in% rownames(aload)),]
write.table(top10000, file="Top1000.txt", sep='\t')
RNA_rel <- rel_rna[(rel_rna$EnsemblID %in% top1000$EnsemblID),]
saveRDS(RNA_rel, file="RNA_rel.rds")
colnames(RNA_rel) <- c("EnsemblID", "KARPAS299.gene", "L82.gene", "SUPM2.gene", "SUDHL1.gene", "KIJK.gene", "SR786.gene", "FEPD.gene", "DL40.gene", "MAC2A.gene")
head(top1000)
head(RNA_rel)
#Gap Stat Calculation
#set.seed(42)
#This sets the random number generator seed so it will always produce the same result. You can pick whatever number you'd like.
gap_stat <- clusGap(RNA_rel[2:10], FUN = kmeans, nstart = 20, K.max = 24, B = 50)
Clustering k = 1,2,..., K.max (= 24): .. done
Bootstrapping, b = 1,2,..., B (= 50) [one "." per sample]:
.................................................. 50
#... = number of samples + 1
#Cluster Stat Plots
fviz_nbclust(top10000[2:10], kmeans, method="wss", k.max=24) + theme_minimal() + ggtitle("Elbow Plot")

fviz_gap_stat(gap_stat) + theme_minimal() + ggtitle("Gap Statistic")

fviz_nbclust(top10000[2:10], kmeans, method="silhouette", k.max=24) + theme_minimal() + ggtitle("Silhouette Plot")

#k-means Clustering
CLARAk2 <- clara(top1000[2:10],2,medoids.x=TRUE)
CLARAk3 <- clara(top1000[2:10],3,medoids.x=TRUE)
CLARAk4 <- clara(top1000[2:10],4,medoids.x=TRUE)
CLARAk5 <- clara(top1000[2:10],5,medoids.x=TRUE)
CLARAk6 <- clara(top1000[2:10],6,medoids.x=TRUE)
CLARAk7 <- clara(top1000[2:10],7,medoids.x=TRUE)
CLARAk8 <- clara(top1000[2:10],8,medoids.x=TRUE)
top1000$k2 <- CLARAk2$clustering
top1000$k3 <- CLARAk3$clustering
top1000$k4 <- CLARAk4$clustering
top1000$k5 <- CLARAk5$clustering
top1000$k6 <- CLARAk6$clustering
top1000$k7 <- CLARAk7$clustering
top1000$k8 <- CLARAk8$clustering
#Displaying Heatmaps
#k-means 20
pheatmap(top1000[order(top1000[, 10+1]),][,2:10], cluster_cols=TRUE, cluster_rows=FALSE, border_color=NA, show_rownames=FALSE, annotation_row=top1000[10+1])

#k-means 26
pheatmap(top1000[order(top1000[, 10+2]),][,2:10], cluster_cols=TRUE, cluster_rows=FALSE, border_color=NA, show_rownames=FALSE, annotation_row=top1000[10+2])

#k-means 30
pheatmap(top1000[order(top1000[, 10+3]),][,2:10], cluster_cols=TRUE, cluster_rows=FALSE, border_color=NA, show_rownames=FALSE, annotation_row=top1000[10+3])

#k-means 36
pheatmap(top1000[order(top1000[, 10+4]),][,2:10], cluster_cols=TRUE, cluster_rows=FALSE, border_color=NA, show_rownames=FALSE, annotation_row=top1000[10+4])

#k-means 15
pheatmap(top1000[order(top1000[, 10+5]),][,2:10], cluster_cols=TRUE, cluster_rows=FALSE, border_color=NA, show_rownames=FALSE, annotation_row=top1000[10+5])

#k-means 25
pheatmap(top1000[order(top1000[, 10+6]),][,2:10], cluster_cols=TRUE, cluster_rows=FALSE, border_color=NA, show_rownames=FALSE, annotation_row=top1000[10+6])

#k-means 22
pheatmap(top1000[order(top1000[, 10+7]),][,2:10], cluster_cols=TRUE, cluster_rows=FALSE, border_color=NA, show_rownames=FALSE, annotation_row=top1000[10+7])

–RNA END–
PRO
#Prepare dREG peak saf for featureCounts
dREG <- read.table("ALCL_s0.5p0.025_filtered.bed")
colnames(dREG) <- c("chromosome", "start", "end", "dREGpeakID", "dREGscore", "strand", "centerStart",
"centerEnd")
write.table(dREG, file="dREG readtable.txt", sep="\t", quote=F, row.names=F)
dREGsaf <- data.frame("GeneID"=dREG$dREGpeakID, "Chr"=dREG$chromosome, "Start"=dREG$start, "End"=dREG$end, "Strand"="+")
write.table(dREGsaf, file="dREG.saf", sep="\t", quote=F, row.names=F)
#Load sample SAM file list
#profiles <- c("")
#Run feautreCounts
#fc_dREG <- featureCounts(profiles, annot.ext=dREGsaf, isGTFAnnotationFile=FALSE, minMQS=10, countChimericFragments=FALSE, isPairedEnd=FALSE, strandSpecific=0, nthreads=8)
fc_pro <- read.table("PRO_ALCL_dREGcounts", header=T, row.names=1)
fc_pro <- as.data.frame(fc_pro)
fc_pro <- subset(fc_pro, select=-c(Chr, Start, End, Strand, Length))
colnames(fc_pro) <- c("KARPAS299", "L82", "SUPM2", "SUDHL1", "KIJK", "SR786", "FEPD", "DL40", "MAC2A")
#Load sample metadata (can add as much sample metadata as desired including cell type, drug treatment, etc)
colData_pro <- data.frame(row.names=colnames(fc_pro$counts), sample=c("KARPAS299", "L82", "SUPM2", "SUDHL1", "KIJK", "SR786", "FEPD", "DL40", "MAC2A"), subtype=c("ALK+ALCL", "ALK+ALCL", "ALK+ALCL", "ALK+ALCL", "ALK+ALCL", "ALK+ALCL", "ALK-ALCL", "ALK-ALCL", "ALK-ALCL"))
all((colData_pro$sample) %in% colnames(fc_pro))
[1] TRUE
#Create DESeq object and generate normalized counts
dds_pro <- DESeqDataSetFromMatrix(countData=fc_pro, colData=colData_pro, design=~sample)
Warning in DESeqDataSet(se, design = design, ignoreRank) :
some variables in design formula are characters, converting to factors
dds_pro <- estimateSizeFactors(dds_pro)
deseq_sizes_pro <- as.data.frame(sizeFactors(dds_pro))
deseq_sizes_pro
variable.names(dds_pro)
[1] "KARPAS299" "L82" "SUPM2" "SUDHL1" "KIJK" "SR786" "FEPD" "DL40"
[9] "MAC2A"
keep <- rowMax(counts(dds_pro, normalize=TRUE)) >= 20
dds_pro <- dds_pro[keep,]
saveRDS(dds_pro, file="dREG_dds.rds")
dREG_readFilt <- dREG[dREG$dREGpeakID %in% rownames(dds_pro),]
write.table(dREG_readFilt, file="dREGpeaks.readFilt.bed", sep="\t", col.names=FALSE, row.names=FALSE,
quote=FALSE)
Generate norm counts files and save to local directory
rawcounts_pro <- as.data.frame(counts(dds_pro, normalized=FALSE))
normcounts_pro <- as.data.frame(counts(dds_pro, normalized=TRUE))
colnames(rawcounts_pro) <- c("KARPAS299", "L82", "SUPM2", "SUDHL1", "KIJK", "SR786", "FEPD", "DL40", "MAC2A")
colnames(normcounts_pro) <- c("KARPAS299", "L82", "SUPM2", "SUDHL1", "KIJK", "SR786", "FEPD", "DL40", "MAC2A")
write.table(rawcounts_pro, file="pro_rawcounts.txt", sep="\t", col.names=TRUE, row.names=TRUE, quote=FALSE)
write.table(normcounts_pro, file="pro_normcounts.txt", sep="\t", col.names=TRUE, row.names=TRUE, quote=FALSE)
Data transformation for plot generation Top 500 contributors is the default for PCA Set ‘ntop’ to number of observations in the dds object
rld_pro <- rlog(dds_pro, blind=TRUE)
sampleDistspro <- dist(t(assay(rld_pro)))
sampleDistMtxpro <- as.matrix(sampleDistspro)
rownames(sampleDistMtxpro) <- paste0(rld_pro$sample)
colnames(sampleDistMtxpro) <- NULL
#Generate PCA plot (intgroup set equal to desired metadata variable)
plotPCA(rld_pro, intgroup = "subtype", ntop=62744) + ggtitle("pro-seq PCA") + scale_color_manual(breaks = c("ALK+ALCL", "ALK-ALCL"), values=c("chartreuse3", "cadetblue")) + geom_text_repel(label=rownames(sampleDistMtxpro), max.overlaps=50)

#top 500 PCA
plotPCA(rld_pro, intgroup = "subtype") + ggtitle("pro-seq PCA Top 500") + scale_color_manual(breaks = c("ALK+ALCL", "ALK-ALCL"), values=c("chartreuse3", "cadetblue")) + geom_text_repel(label=rownames(sampleDistMtxpro), max.overlaps=50)

#top 1000 PCA
plotPCA(rld_pro, intgroup = "subtype", ntop=1000) + ggtitle("pro-seq PCA Top 1000") + scale_color_manual(breaks = c("ALK+ALCL", "ALK-ALCL"), values=c("chartreuse3", "cadetblue")) + geom_text_repel(label=rownames(sampleDistMtxpro), max.overlaps=50)

#top 5000 PCA
plotPCA(rld_pro, intgroup = "subtype", ntop=5000) + ggtitle("pro-seq PCA Top 5000") + scale_color_manual(breaks = c("ALK+ALCL", "ALK-ALCL"), values=c("chartreuse3", "cadetblue")) + geom_text_repel(label=rownames(sampleDistMtxpro), max.overlaps=50)

#top 10000 PCA
plotPCA(rld_pro, intgroup = "subtype", ntop=10000) + ggtitle("pro-seq PCA Top 10000") + scale_color_manual(breaks = c("ALK+ALCL", "ALK-ALCL"), values=c("chartreuse3", "cadetblue")) + geom_text_repel(label=rownames(sampleDistMtxpro), max.overlaps=50)

#top 15000 PCA
plotPCA(rld_pro, intgroup = "subtype", ntop=15000) + ggtitle("pro-seq PCA Top 15000") + scale_color_manual(breaks = c("ALK+ALCL", "ALK-ALCL"), values=c("chartreuse3", "cadetblue")) + geom_text_repel(label=rownames(sampleDistMtxpro), max.overlaps=50)

#top 20000 PCA
plotPCA(rld_pro, intgroup = "subtype", ntop=20000) + ggtitle("pro-seq PCA Top 20000") + scale_color_manual(breaks = c("ALK+ALCL", "ALK-ALCL"), values=c("chartreuse3", "cadetblue")) + geom_text_repel(label=rownames(sampleDistMtxpro), max.overlaps=50)

Additional dendrogram plots
colors <- colorRampPalette(rev(brewer.pal(9, "Blues")))(255)
pheatmap(sampleDistMtxpro, col=colors, border_color=NA, show_colnames=FALSE, show_rownames=TRUE,
cluster_rows=TRUE, cluster_cols=TRUE, main="ALCL Sample Distance Clustering")

plot(hclust(sampleDistspro), labels=rownames(sampleDistMtxpro), main="ALCL Sample Distance Dendrogram")

Assemble counts results
avg_pro <- data.frame(peakID=rownames(normcounts_pro))
avg_pro$KARPAS299 <- (normcounts_pro$KARPAS299)
avg_pro$L82 <- (normcounts_pro$L82)
avg_pro$SUPM2 <- (normcounts_pro$SUPM2)
avg_pro$SUDHL1 <- (normcounts_pro$SUDHL1)
avg_pro$KIJK <- (normcounts_pro$KIJK)
avg_pro$SR786 <- (normcounts_pro$SR786)
avg_pro$FEPD <- (normcounts_pro$FEPD)
avg_pro$DL40 <- (normcounts_pro$DL40)
avg_pro$MAC2A <- (normcounts_pro$MAC2A)
avg_pro$max <- apply(avg_pro[c("KARPAS299", "L82", "SUPM2", "SUDHL1", "KIJK", "SR786", "FEPD", "DL40", "MAC2A")], 1, max, na.rm=TRUE)
rel_pro <- data.frame(peakID=avg_pro$peakID)
rel_pro$KARPAS299 <- avg_pro$KARPAS299 / avg_pro$max
rel_pro$L82 <- avg_pro$L82 / avg_pro$max
rel_pro$SUPM2 <- avg_pro$SUPM2 / avg_pro$max
rel_pro$SUDHL1 <- avg_pro$SUDHL1 / avg_pro$max
rel_pro$KIJK <- avg_pro$KIJK / avg_pro$max
rel_pro$SR786 <- avg_pro$SR786 / avg_pro$max
rel_pro$FEPD <- avg_pro$FEPD / avg_pro$max
rel_pro$DL40 <- avg_pro$DL40 / avg_pro$max
rel_pro$MAC2A <- avg_pro$MAC2A / avg_pro$max
PCA top contributors selection (5000 tried first)
dREG_annot <- read.table("ALCL_p1000_dREGpeak_annotation.txt", sep="\t", header=TRUE)
PRO_rel <- rel_pro
#check this
colnames(PRO_rel) <- c("dREGpeakID", "KARPAS299.enhancer", "L82.enhancer", "SUPM2.enhancer", "SUDHL1.enhancer", "KIJK.enhancer", "SR786.enhancer", "FEPD.enhancer", "DL40.enhancer", "MAC2A.ehancer")
saveRDS(PRO_rel, file="pro_rel.rds")
head(PRO_rel)
rv <- rowVars(assay(rld_pro))
select <- order(rv, decreasing=TRUE)[seq_len(min(5000, length(rv)))]
#Changing the 5000 here will select a Different number of top contributors if desired
pc <- prcomp(t(assay(rld_pro)[select,]))
loadings <- as.data.frame(pc$rotation)
aload <- abs(loadings)
write.table(sweep(aload, 2, colSums(aload), "/"), file="Top 5000 pro PCA Contributors.txt")
#Clean up variables
rm(rv)
rm(select)
rm(pc)
rm(loadings)
#Merge top 5000 elements with their annotations
top5000 <- dREG_annot[(dREG_annot$peakID %in% rownames(aload)),]
write.table(top5000, file="Top5000.txt", sep='\t')
ggplot(top5000, aes(x=factor(1), fill=genomicCategory))+geom_bar(width=1)+coord_polar("y") + ggtitle("Top 5000 PCA Contributors dREGpeak Annotations")

ggplot(dREG_annot, aes(x=factor(1), fill=genomicCategory))+geom_bar(width=1)+coord_polar("y") + ggtitle("All dREGpeak Annotations")

#filter results down by top contributors
pro_5000_rel <- rel_pro[(rel_pro$peakID %in% top5000$peakID),]
#Gap Stat Calculation
#set.seed(42)
#This sets the random number generator seed so it will always produce the same result. You can pick whatever number you'd like.
gap_stat <- clusGap(pro_5000_rel[2:10], FUN = kmeans, nstart = 20, K.max = 24, B = 50)
Clustering k = 1,2,..., K.max (= 24): .. done
Bootstrapping, b = 1,2,..., B (= 50) [one "." per sample]:
.................................................. 50
#... = number of samples + 1
#Cluster Stat Plots
fviz_nbclust(pro_5000_rel[2:10], kmeans, method="wss", k.max=24) + theme_minimal() + ggtitle("Elbow Plot")

fviz_gap_stat(gap_stat) + theme_minimal() + ggtitle("Gap Statistic")

fviz_nbclust(pro_5000_rel[2:10], kmeans, method="silhouette", k.max=24) + theme_minimal() + ggtitle("Silhouette Plot")

#k-means Clustering
CLARAk4 <- clara(pro_5000_rel[2:10],4,medoids.x=TRUE)
CLARAk5 <- clara(pro_5000_rel[2:10],5,medoids.x=TRUE)
CLARAk6 <- clara(pro_5000_rel[2:10],6,medoids.x=TRUE)
CLARAk7 <- clara(pro_5000_rel[2:10],7,medoids.x=TRUE)
CLARAk8 <- clara(pro_5000_rel[2:10],8,medoids.x=TRUE)
pro_5000_rel$k4 <- CLARAk4$clustering
pro_5000_rel$k5 <- CLARAk5$clustering
pro_5000_rel$k6 <- CLARAk6$clustering
pro_5000_rel$k7 <- CLARAk7$clustering
pro_5000_rel$k8 <- CLARAk8$clustering
#Displaying Heatmaps
#k-means 4
pheatmap(pro_5000_rel[order(pro_5000_rel[, 10+1]),][,2:10], cluster_cols=TRUE, cluster_rows=FALSE, border_color=NA, show_rownames=FALSE, annotation_row=pro_5000_rel[10+1])

#k-means 5
pheatmap(pro_5000_rel[order(pro_5000_rel[, 10+2]),][,2:10], cluster_cols=TRUE, cluster_rows=FALSE, border_color=NA, show_rownames=FALSE, annotation_row=pro_5000_rel[10+2])

#k-means 6
pheatmap(pro_5000_rel[order(pro_5000_rel[, 10+3]),][,2:10], cluster_cols=TRUE, cluster_rows=FALSE, border_color=NA, show_rownames=FALSE, annotation_row=pro_5000_rel[10+3])

#k-means 7
pheatmap(pro_5000_rel[order(pro_5000_rel[, 10+4]),][,2:10], cluster_cols=TRUE, cluster_rows=FALSE, border_color=NA, show_rownames=FALSE, annotation_row=pro_5000_rel[10+4])

#k-means 8
pheatmap(pro_5000_rel[order(pro_5000_rel[, 10+5]),][,2:10], cluster_cols=TRUE, cluster_rows=FALSE, border_color=NA, show_rownames=FALSE, annotation_row=pro_5000_rel[10+5])

–PRO END–
Combined PRO and RNA analysis
Load DESeq objects and annotations
#dds_rna <- readRDS("RNA_dds.rds")
#dds_pro <- readRDS("dREG_dds.rds")
dREGann <- read.table("ALCL_p1000_m1000000_gene-centric_closest-dREGpeaks.txt", sep="\t", header=T)
write.table(deseq_sizes_pro, file="DEseq size factors.txt", sep="\t", quote=F, row.names=T)
Make prelim dataframes
norm_rna <- as.data.frame(counts(dds_rna, normalized=TRUE))
rld_rna2 <- rlog(dds_rna, blind=TRUE)
colnames(norm_rna) <- dds_rna$sample #Simplified names from dds metadata
rel_rna2 <- data.frame("geneID"=rownames(norm_rna))
rel_rna2$max.gene.counts <- apply(norm_rna[1:ncol(norm_rna)], 1, max, na.rm=TRUE)
i=1
while (i<=ncol(norm_rna)){
rel_rna2[paste0(dds_rna$sample[i],".gene.rel")] <- norm_rna[,i]/rel_rna2$max.gene.counts
i=i+1
}
#reorder
rel_rna2 <- rel_rna2[,c(1,3:11,2)]
norm_pro <- as.data.frame(counts(dds_pro, normalized=TRUE))
colnames(norm_pro) <- dds_pro$sample #Simplified names from dds metadata
norm_pro <- norm_pro[names(norm_rna)] #reorder sample columns to match RNA-seq data
colnames(PRO_rel)[1] <- ("closest_peak")
#rel_pro2 <- data.frame("closest_peak"=rownames(norm_pro))
PRO_rel$max.dREG.counts <- apply(norm_pro[1:ncol(norm_pro)], 1, max, na.rm=TRUE)
#PRO_rel <- PRO_rel[,c(1,27,2:26)] #reorder columns
#i=1
#while (i<=ncol(norm_pro)){
# rel_pro2[paste0(dds_pro$sample[i],".dREG.rel")] <- norm_pro[,i]/rel_pro2$max.dREG.counts
# i=i+1
#}
Selecting gene PCA contributors by variance
#Select top 1000 PCA contributors
rv <- rowVars(assay(rld_rna2))
select <- order(rv, decreasing=TRUE)[seq_len(min(1000, length(rv)))] #Changing the 500 here will select a different number of top contributors if desired
pc <- prcomp(t(assay(rld_rna2)[select,]))
loadings <- as.data.frame(pc$rotation)
aload <- abs(loadings)
sweep(aload, 2, colSums(aload), "/")
#Clean up variables
rm(rv)
rm(select)
rm(pc)
rm(loadings)
#Merge top 500 elements with their annotations
top1k_rel_rna <- rel_rna2[(rel_rna2$geneID %in% rownames(aload)),] #filter rel gene dataframe for top 10k
Determining clusters (k=3)
CLARA <- clara(top1k_rel_rna[2:10],3,medoids.x=TRUE)
top1k_rel_rna$k3 <- CLARA$clustering
pheatmap(top1k_rel_rna[order(top1k_rel_rna[,11]),][,2:10], cluster_cols=TRUE, cluster_rows=FALSE, border_color=NA, show_rownames=FALSE, annotation_row=top1k_rel_rna[11])

Data integration
gene_res <- rel_rna2[(!rel_rna2$geneID %in% top1k_rel_rna$geneID),] #filter out genes not in top 10k
gene_res$k3 <- NA #add value for genes not clustered
gene_res <- rbind(top1k_rel_rna, gene_res) #combine with top 10k genes with cluster designations
#gene_res$geneID <- sapply(strsplit(gene_res$geneID, split="[.]"), head, 1) #simplify geneIDs by removing version numbers
comb_res <- unique(merge(dREGann, gene_res, by.x="geneID", by.y="geneID", all.y=T))
colnames(comb_res)[1] <- "geneID"
comb_res <- merge(comb_res, PRO_rel, by.x="closest_peak", by.y="closest_peak", all.x=T) #combine with dREG peak data, keep genes without a closest dREG peak call
comb_res <- comb_res[,c(2:9,13:23,1,10:12,24:33)] #reorder columns
Gene-to-Enhancer concordance analysis – this generates a stat ranging from -1 (anti-correlated) to 0 (not correlated) to +1 (correlated) reported as Spearman rho
#note this will produce a lot of errors for rows that don't have a gene and a an enhancer, so I've silenced them here
spear <- c()
i <- 1
while(i<=nrow(comb_res)){
speari <- cor(as.numeric(factor(comb_res[i,9:17])),as.numeric(factor(comb_res[i,24:32])),method="spearman")
spear <- c(spear, speari)
i <- i+1
}
comb_res$Spearman.rho <- spear
Save output
write.table(comb_res, file="ALCL.final.table.txt", sep="\t", quote=F, row.names=F)
dREGcountsann <- merge(PRO_rel, dREG_readFilt, by.x="closest_peak", by.y="dREGpeakID")
dREGcountsann <- dREGcountsann[,c(1,12:18,2:11)]
write.table(dREGcountsann, file="ALCL.dREGs.txt", sep="\t", quote=F, row.names=F)
Correct output to include metadata for genes without nearest enhancer calls
library(dplyr)
#remove columns with annotation issue
comb_res_woEnh <- comb_res %>% filter(is.na(geneName))
comb_res_wEnh <- comb_res %>% filter(!is.na(geneName))
#Load original unfiltered annotations
comp_gtf <- read.table("Homo_sapiens.GRCh38.99.ucsc.gtf", sep="\t")
colnames(comp_gtf) <- c("chr", "source", "type", "start", "end", "score", "strand", "phase", "attributes")
#Function to extract metadata from gtf annotation
extract_attributes <- function(gtf_attributes, att_of_interest){
att <- strsplit(as.character(gtf_attributes), "; ")
att <- gsub("\"","",unlist(att))
att <- gsub(";","",unlist(att))
if(!is.null(unlist(strsplit(att[grep(att_of_interest, att)], " ")))){
return( unlist(strsplit(att[grep(att_of_interest, att)], " "))[2])
}else{
return(NA)}
}
#Extract desired information from gtf (geneID, geneName, biotype)
comp_gene <- filter(comp_gtf, type == "gene")
comp_gene$geneID <- unlist(lapply(comp_gene$attributes, extract_attributes, "gene_id"))
comp_gene$geneName <- unlist(lapply(comp_gene$attributes, extract_attributes, "gene_name"))
comp_gene$biotype <- unlist(lapply(comp_gene$attributes, extract_attributes, "gene_biotype"))
#Remove offending columns
#geneName,biotype,attributes,chr,start,end,strand
comb_res_woEnh <- comb_res_woEnh[,c(1,9:34)]
#Add back information
geneInfo <- data.frame("geneID"=comp_gene$geneID, "geneName"=comp_gene$geneName, "biotype"=comp_gene$biotype, "attributes"=comp_gene$attributes, "chr"=comp_gene$chr, "start"=comp_gene$start, "end"=comp_gene$end, "strand"=comp_gene$strand)
comb_res_woEnh_corr <- merge(geneInfo, comb_res_woEnh, by.x="geneID", by.y="geneID", all.y=T)
#Modify geneName and peak_counts
comb_res_woEnh_corr$peak_count <- 0
comb_res_woEnh_corr$geneName <- paste0(comb_res_woEnh_corr$geneName)
#Correcting geneNames in original
comb_res_wEnh$geneName <- substring(comb_res_wEnh$geneName, 1)
comb_res_wEnh$geneName <- paste0(comb_res_wEnh$geneName)
#Combining it all together
comb_res_corr <- rbind(comb_res_woEnh_corr, comb_res_wEnh)
write.table(comb_res_corr, file="ALCL.combined.results.corrected.txt", sep="\t", quote=F, row.names=F)
Combining full annotation to plot based on genomic category
filtered_res <- merge(dREG_annot, comb_res_corr, by.x="peakID", by.y="closest_peak", all.y=F)
write.table(filtered_res, file="Overlaping contribtors.txt", sep='\t', quote=F, row.names=F)
ggplot(filtered_res, aes(x=factor(1), fill=genomicCategory))+geom_bar(width=1)+coord_polar("y") + ggtitle("Overlapping PCA Contributors for Gene Expression and dREGpeak Annotations")

Combination completed, further investigation into clusters ongoing - 28JAN22
LS0tCnRpdGxlOiAiQ29tYmluZWQgUFJPX1JOQSB3ZWIgYWNjZXNzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCkRvY3VtZW50IGNyZWF0aW9uCkxpYnJhcmllcwpgYGB7cn0KbGlicmFyeShERVNlcTIpCmxpYnJhcnkoUnN1YnJlYWQpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KGNsdXN0ZXIpCmxpYnJhcnkoZmFjdG9leHRyYSkKbGlicmFyeShhbWFwKQpsaWJyYXJ5KE5iQ2x1c3QpCmxpYnJhcnkoZHBseXIpCmBgYAoKU2V0IHdvcmtpbmcgZGlyZWN0b3J5IHRvIGxvY2FsIHdvcmtzcGFjZQpTYXZlIFIgbWFya3VwLCBmZWF0dXJlY291bnRzIHJlc3VsdHMgYW5kIGZ1bGwgZ3RmIHRoZXJlClJOQSBmZWF0dXJlY291bnRzIGdlbmVyYXRlZCB3aXRoICdIb21vX3NhcGllbnMuR1JDaDM4Ljk5Lmd0Zi5neicgZmlsZQpgYGB7cn0Kc2V0d2QoIi9Vc2Vycy9rdXJva28vRHJvcGJveCAoUGFydG5lcnMgSGVhbHRoQ2FyZSkvV2VpbnN0b2NrIE5UQyBDb2xsYWIvRXhwZXJpbWVudHMvVENMIENlbGwgTGluZXMvRGF0YS9BTENML0Z1bGwgUiBwcm9qZWN0IHNjcmlwdHMiKQpnZXR3ZCgpCmBgYAoKUk5BCgpMb2FkIGZlYXR1cmVjb3VudHMgcmVzdWx0cywgY29uZmlndXJlIGFzIGRhdGEgZnJhbWUgb2JqZWN0ClNldHVwIG1ldGFkYXRhIGluZm9ybWF0aW9uIGFuZCBjaGVjayBuYW1pbmcgY29uc2lzdGVuY3kKUmVtb3ZlIG5vbi1jb3VudHMgY29sdW1ucyBpbiBmYyBvYmplY3QKYGBge3J9CmZjIDwtIHJlYWQudGFibGUoIlJOQV9BTENMX3dob2xlZ2VuZWNvdW50cyIsIHNlcD0iXHQiLCBoZWFkZXI9VFJVRSwgcm93Lm5hbWVzPTEpCmZjMiA8LSBhcy5kYXRhLmZyYW1lKGZjKQpvcmlnX25hbWVzIDwtIGNvbG5hbWVzKGZjMls2OjE0XSkKY29sRGF0YV9ybmEgPC0gZGF0YS5mcmFtZShyb3cubmFtZXM9b3JpZ19uYW1lcywgc2FtcGxlPWMoIktBUlBBUzI5OSIsICJMODIiLCAiU1VQTTIiLCAiU1VESEwxIiwgIktJSksiLCAiU1I3ODYiLCAiRkVQRCIsICJETDQwIiwgIk1BQzJBIiksIHN1YnR5cGU9YygiQUxLK0FMQ0wiLCAiQUxLK0FMQ0wiLCAiQUxLK0FMQ0wiLCAiQUxLK0FMQ0wiLCAiQUxLK0FMQ0wiLCAiQUxLK0FMQ0wiLCAiQUxLLUFMQ0wiLCAiQUxLLUFMQ0wiLCAiQUxLLUFMQ0wiKSkKYWxsKHJvd25hbWVzKGNvbERhdGFfcm5hKSAlaW4lIGNvbG5hbWVzKGZjMikpCmZjMyA8LSBzdWJzZXQoZmMyLCBzZWxlY3Q9LWMoQ2hyLCBTdGFydCwgRW5kLCBTdHJhbmQsIExlbmd0aCkpCmNvbG5hbWVzKGZjMykgPC0gYygiS0FSUEFTMjk5IiwgIkw4MiIsICJTVVBNMiIsICJTVURITDEiLCAiS0lKSyIsICJTUjc4NiIsICJGRVBEIiwgIkRMNDAiLCAiTUFDMkEiKQpgYGAKCkNyZWF0ZSBERXNlcSBvYmplY3QgYW5kIHNhdmUgZmlsZSAKR2VuZXJhdGUgbm9ybWFsaXphdGlvbiBmYWN0b3Igd2l0aCBERXNlcSBkZWZhdWx0CmBgYHtyfQpkZHNfcm5hIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhPWZjMywgY29sRGF0YT1jb2xEYXRhX3JuYSwgZGVzaWduPX5zYW1wbGUpCmRkc19ybmEgPC0gZXN0aW1hdGVTaXplRmFjdG9ycyhkZHNfcm5hKQpkZXNlcV9zaXplc19ybmEgPC0gYXMuZGF0YS5mcmFtZShzaXplRmFjdG9ycyhkZHNfcm5hKSkKcm93Lm5hbWVzKGRlc2VxX3NpemVzX3JuYSkgPC0gYygiS0FSUEFTMjk5IiwgIkw4MiIsICJTVVBNMiIsICJTVURITDEiLCAiS0lKSyIsICJTUjc4NiIsICJGRVBEIiwgIkRMNDAiLCAiTUFDMkEiKQpkZXNlcV9zaXplc19ybmEKc2F2ZVJEUyhkZHNfcm5hLCBmaWxlPSJSTkFfZGRzLnJkcyIpCmBgYAoKR2VuZXJhdGUgbm9ybSBjb3VudHMgZmlsZXMgYW5kIHNhdmUgdG8gbG9jYWwgZGlyZWN0b3J5CmBgYHtyfQpyYXdjb3VudHNfcm5hIDwtIGFzLmRhdGEuZnJhbWUoY291bnRzKGRkc19ybmEsIG5vcm1hbGl6ZWQ9RkFMU0UpKQpub3JtY291bnRzX3JuYSA8LSBhcy5kYXRhLmZyYW1lKGNvdW50cyhkZHNfcm5hLCBub3JtYWxpemVkPVRSVUUpKQpjb2xuYW1lcyhyYXdjb3VudHNfcm5hKSA8LSBjKCJLQVJQQVMyOTkiLCAiTDgyIiwgIlNVUE0yIiwgIlNVREhMMSIsICJLSUpLIiwgIlNSNzg2IiwgIkZFUEQiLCAiREw0MCIsICJNQUMyQSIpCmNvbG5hbWVzKG5vcm1jb3VudHNfcm5hKSA8LSBjKCJLQVJQQVMyOTkiLCAiTDgyIiwgIlNVUE0yIiwgIlNVREhMMSIsICJLSUpLIiwgIlNSNzg2IiwgIkZFUEQiLCAiREw0MCIsICJNQUMyQSIpCiNyb3duYW1lcyhyYXdjb3VudHNfcm5hKSA8LSBzYXBwbHkoc3Ryc3BsaXQocm93bmFtZXMocmF3Y291bnRzX3JuYSksIHNwbGl0PSJbLl0iKSwgaGVhZCwgMSkKI3Jvd25hbWVzKG5vcm1jb3VudHNfcm5hKSA8LSBzYXBwbHkoc3Ryc3BsaXQocm93bmFtZXMobm9ybWNvdW50c19ybmEpLCBzcGxpdD0iWy5dIiksIGhlYWQsIDEpCndyaXRlLnRhYmxlKHJhd2NvdW50c19ybmEsIGZpbGU9IlJOQV9yYXdjb3VudHMudHh0Iiwgc2VwPSJcdCIsIGNvbC5uYW1lcz1UUlVFLCByb3cubmFtZXM9VFJVRSwgcXVvdGU9RkFMU0UpCndyaXRlLnRhYmxlKG5vcm1jb3VudHNfcm5hLCBmaWxlPSJSTkFfbm9ybWNvdW50cy50eHQiLCBzZXA9Ilx0IiwgY29sLm5hbWVzPVRSVUUsIHJvdy5uYW1lcz1UUlVFLCBxdW90ZT1GQUxTRSkKYGBgCgpEYXRhIHRyYW5zZm9ybWF0aW9uIGZvciBwbG90IGdlbmVyYXRpb24KVG9wIDUwMCBjb250cmlidXRvcnMgaXMgdGhlIGRlZmF1bHQgZm9yIFBDQSAKU2V0ICdudG9wJyB0byBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIHRoZSBkZHMgb2JqZWN0CmBgYHtyfQpybGRfcm5hIDwtIHJsb2coZGRzX3JuYSwgYmxpbmQ9VFJVRSkKc2FtcGxlRGlzdHNybmEgPC0gZGlzdCh0KGFzc2F5KHJsZF9ybmEpKSkKc2FtcGxlRGlzdE10eHJuYSA8LSBhcy5tYXRyaXgoc2FtcGxlRGlzdHNybmEpCnJvd25hbWVzKHNhbXBsZURpc3RNdHhybmEpIDwtIHBhc3RlMChybGRfcm5hJHNhbXBsZSkKY29sbmFtZXMoc2FtcGxlRGlzdE10eHJuYSkgPC0gTlVMTAojR2VuZXJhdGUgUENBIHBsb3QgKGludGdyb3VwIHNldCBlcXVhbCB0byBkZXNpcmVkIG1ldGFkYXRhIHZhcmlhYmxlKQpwbG90UENBKHJsZF9ybmEsIGludGdyb3VwID0gInN1YnR5cGUiLCBudG9wPTYwNjc2KSArIGdndGl0bGUoIlJOQS1zZXEgUENBIikgKyBzY2FsZV9jb2xvcl9tYW51YWwoYnJlYWtzID0gYygiQUxLK0FMQ0wiLCJBTEstQUxDTCIpLCB2YWx1ZXM9YygiY2hhcnRyZXVzZTMiLCAiY2FkZXRibHVlIikpICsgZ2VvbV90ZXh0X3JlcGVsKGxhYmVsPXJvd25hbWVzKHNhbXBsZURpc3RNdHhybmEpLCBtYXgub3ZlcmxhcHM9NTApCiN0b3AgNTAwIFBDQQpwbG90UENBKHJsZF9ybmEsIGludGdyb3VwID0gInN1YnR5cGUiKSArIGdndGl0bGUoIlJOQS1zZXEgUENBIFRvcCA1MDAiKSArIHNjYWxlX2NvbG9yX21hbnVhbChicmVha3MgPSBjKCJBTEsrQUxDTCIsIkFMSy1BTENMIiksIHZhbHVlcz1jKCJjaGFydHJldXNlMyIsICJjYWRldGJsdWUiKSkgKyBnZW9tX3RleHRfcmVwZWwobGFiZWw9cm93bmFtZXMoc2FtcGxlRGlzdE10eHJuYSksIG1heC5vdmVybGFwcz01MCkKI3RvcCAxMDAwCnBsb3RQQ0EocmxkX3JuYSwgaW50Z3JvdXAgPSAic3VidHlwZSIsIG50b3A9MTAwMCkgKyBnZ3RpdGxlKCJSTkEtc2VxIFBDQSBUb3AgMTAwMCIpICsgc2NhbGVfY29sb3JfbWFudWFsKGJyZWFrcyA9IGMoIkFMSytBTENMIiwiQUxLLUFMQ0wiKSwgdmFsdWVzPWMoImNoYXJ0cmV1c2UzIiwgImNhZGV0Ymx1ZSIpKSArIGdlb21fdGV4dF9yZXBlbChsYWJlbD1yb3duYW1lcyhzYW1wbGVEaXN0TXR4cm5hKSwgbWF4Lm92ZXJsYXBzPTUwKQojdG9wIDUwMDAKcGxvdFBDQShybGRfcm5hLCBpbnRncm91cCA9ICJzdWJ0eXBlIiwgbnRvcD01MDAwKSArIGdndGl0bGUoIlJOQS1zZXEgUENBIFRvcCA1MDAwIikgKyBzY2FsZV9jb2xvcl9tYW51YWwoYnJlYWtzID0gYygiQUxLK0FMQ0wiLCJBTEstQUxDTCIpLCB2YWx1ZXM9YygiY2hhcnRyZXVzZTMiLCAiY2FkZXRibHVlIikpICsgZ2VvbV90ZXh0X3JlcGVsKGxhYmVsPXJvd25hbWVzKHNhbXBsZURpc3RNdHhybmEpLCBtYXgub3ZlcmxhcHM9NTApCiN0b3AgMTAwMDAKcGxvdFBDQShybGRfcm5hLCBpbnRncm91cCA9ICJzdWJ0eXBlIiwgbnRvcD0xMDAwMCkgKyBnZ3RpdGxlKCJSTkEtc2VxIFBDQSBUb3AgMTAwMDAiKSArIHNjYWxlX2NvbG9yX21hbnVhbChicmVha3MgPSBjKCJBTEsrQUxDTCIsIkFMSy1BTENMIiksIHZhbHVlcz1jKCJjaGFydHJldXNlMyIsICJjYWRldGJsdWUiKSkgKyBnZW9tX3RleHRfcmVwZWwobGFiZWw9cm93bmFtZXMoc2FtcGxlRGlzdE10eHJuYSksIG1heC5vdmVybGFwcz01MCkKI3RvcCAxNTAwMApwbG90UENBKHJsZF9ybmEsIGludGdyb3VwID0gInN1YnR5cGUiLCBudG9wPTE1MDAwKSArIGdndGl0bGUoIlJOQS1zZXEgUENBIFRvcCAxNTAwMCIpICsgc2NhbGVfY29sb3JfbWFudWFsKGJyZWFrcyA9IGMoIkFMSytBTENMIiwiQUxLLUFMQ0wiKSwgdmFsdWVzPWMoImNoYXJ0cmV1c2UzIiwgImNhZGV0Ymx1ZSIpKSArIGdlb21fdGV4dF9yZXBlbChsYWJlbD1yb3duYW1lcyhzYW1wbGVEaXN0TXR4cm5hKSwgbWF4Lm92ZXJsYXBzPTUwKQpgYGAKCkFkZGl0aW9uYWwgZGVuZHJvZ3JhbSBwbG90cwpgYGB7cn0KY29sb3JzIDwtIGNvbG9yUmFtcFBhbGV0dGUocmV2KGJyZXdlci5wYWwoOSwgIkJsdWVzIikpKSgyNTUpCnBoZWF0bWFwKHNhbXBsZURpc3RNdHhybmEsIGNvbD1jb2xvcnMsIGJvcmRlcl9jb2xvcj1OQSwgc2hvd19jb2xuYW1lcz1GQUxTRSwgc2hvd19yb3duYW1lcz1UUlVFLAogICAgICAgICBjbHVzdGVyX3Jvd3M9VFJVRSwgY2x1c3Rlcl9jb2xzPVRSVUUsIG1haW49IkFMQ0wgUk5BIFNhbXBsZSBEaXN0YW5jZSBDbHVzdGVyaW5nIikKcGxvdChoY2x1c3Qoc2FtcGxlRGlzdHNybmEpLCBsYWJlbHM9cm93bmFtZXMoc2FtcGxlRGlzdE10eHJuYSksIG1haW49IkFMQ0wgUk5BIFNhbXBsZSBEaXN0YW5jZSBEZW5kcm9ncmFtIikKYGBgCgpBc3NlbWJsZSBjb3VudHMgcmVzdWx0cwpgYGB7cn0KYXZnX3JuYSA8LSBkYXRhLmZyYW1lKEVuc2VtYmxJRD1yb3duYW1lcyhub3JtY291bnRzX3JuYSkpCmF2Z19ybmEkS0FSUEFTMjk5IDwtIChub3JtY291bnRzX3JuYSRLQVJQQVMyOTkpCmF2Z19ybmEkTDgyIDwtIChub3JtY291bnRzX3JuYSRMODIpCmF2Z19ybmEkU1VQTTIgPC0gKG5vcm1jb3VudHNfcm5hJFNVUE0yKQphdmdfcm5hJFNVREhMMSA8LSAobm9ybWNvdW50c19ybmEkU1VESEwxKQphdmdfcm5hJEtJSksgPC0gKG5vcm1jb3VudHNfcm5hJEtJSkspCmF2Z19ybmEkU1I3ODYgPC0gKG5vcm1jb3VudHNfcm5hJFNSNzg2KQphdmdfcm5hJEZFUEQgPC0gKG5vcm1jb3VudHNfcm5hJEZFUEQpCmF2Z19ybmEkREw0MCA8LSAobm9ybWNvdW50c19ybmEkREw0MCkKYXZnX3JuYSRNQUMyQSA8LSAobm9ybWNvdW50c19ybmEkTUFDMkEpCgphdmdfcm5hJG1heCA8LSBhcHBseShhdmdfcm5hW2MoIktBUlBBUzI5OSIsICJMODIiLCAiU1VQTTIiLCAiU1VESEwxIiwgIktJSksiLCAiU1I3ODYiLCAiRkVQRCIsICJETDQwIiwgIk1BQzJBIildLCAxLCBtYXgsIG5hLnJtPVRSVUUpCnJlbF9ybmEgPC0gZGF0YS5mcmFtZShFbnNlbWJsSUQ9YXZnX3JuYSRFbnNlbWJsSUQpCnJlbF9ybmEkS0FSUEFTMjk5IDwtIGF2Z19ybmEkS0FSUEFTMjk5IC8gYXZnX3JuYSRtYXgKcmVsX3JuYSRMODIgPC0gYXZnX3JuYSRMODIgLyBhdmdfcm5hJG1heApyZWxfcm5hJFNVUE0yIDwtIGF2Z19ybmEkU1VQTTIgLyBhdmdfcm5hJG1heApyZWxfcm5hJFNVREhMMSA8LSBhdmdfcm5hJFNVREhMMSAvIGF2Z19ybmEkbWF4CnJlbF9ybmEkS0lKSyA8LSBhdmdfcm5hJEtJSksgLyBhdmdfcm5hJG1heApyZWxfcm5hJFNSNzg2IDwtIGF2Z19ybmEkU1I3ODYgLyBhdmdfcm5hJG1heApyZWxfcm5hJEZFUEQgPC0gYXZnX3JuYSRGRVBEIC8gYXZnX3JuYSRtYXgKcmVsX3JuYSRETDQwIDwtIGF2Z19ybmEkREw0MCAvIGF2Z19ybmEkbWF4CnJlbF9ybmEkTUFDMkEgPC0gYXZnX3JuYSRNQUMyQSAvIGF2Z19ybmEkbWF4CmBgYAoKRGF0YSB0cmFuc2Zvcm0gZm9yIHZhcmlhbmNlIGFzc2Vzc21lbnQgYW5kIHN1Yi1jbHVzdGVyaW5nCmBgYHtyfQpydjEgPC0gcm93VmFycyhhc3NheShybGRfcm5hKSkKc2VsZWN0MSA8LSBvcmRlcihydjEsIGRlY3JlYXNpbmc9VFJVRSkKcGMxIDwtIHByY29tcCh0KGFzc2F5KHJsZF9ybmEpW3NlbGVjdDEsXSkpCmxvYWRpbmdzMSA8LSBhcy5kYXRhLmZyYW1lKHBjMSRyb3RhdGlvbikKYWxvYWQxIDwtIGFicyhsb2FkaW5nczEpCnBjUmFuayA8LSBkYXRhLmZyYW1lKCJFbnNlbWJsSUQiPXJvd25hbWVzKGxvYWRpbmdzMSksICJQQ0EucmFuayI9c2VxLmludChucm93KGxvYWRpbmdzMSkpKQpzd2VlcChhbG9hZDEsIDIsIGNvbFN1bXMoYWxvYWQxKSwgIi8iKQoKcGVha1ZhciA8LSBkYXRhLmZyYW1lKCJFbnNlbWJsSUQiPXJvd25hbWVzKGFzc2F5KHJsZF9ybmEpKSkKcGVha1ZhciRybG9nLnZhcmlhbmNlIDwtIHJvd1ZhcnMoYXMubWF0cml4KGFzc2F5KHJsZF9ybmEpKSkKcGVha1ZhciA8LSBtZXJnZShwY1JhbmssIHBlYWtWYXIsIGJ5Lng9IkVuc2VtYmxJRCIsIGJ5Lnk9IkVuc2VtYmxJRCIpCmdncGxvdChwZWFrVmFyLCBhZXMoeD1QQ0EucmFuaywgeT1ybG9nLnZhcmlhbmNlKSkgKyBnZW9tX2xpbmUoKSArIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKDUwMCwxMDAwLDIwMDAsNTAwMCwxMDAwMCwxMDAwMCwxNTAwMCksIGxpbmV0eXBlPSJkb3R0ZWQiKSArIGdndGl0bGUoIkFMQ0wgVmFyaWFuY2UgcGVyIFBDIENvbnRyaWJ1dG9yIikKCiNhZGQgcGxvdCBvZiBmcmFjdGlvbiBvZiBleHBsYWluZWQgdmFyaWFuY2UuLiBydW5uaW5nIHN1bSAvIHRvdGFsIHZzIHJhbmsKcGVha1ZhciA8LSBwZWFrVmFyW29yZGVyKHBlYWtWYXIkUENBLnJhbmspLF0KcGVha1ZhciRjdW11bGF0aXZlLlZhcmlhbmNlIDwtIGN1bXN1bShwZWFrVmFyJHJsb2cudmFyaWFuY2UpL3N1bShwZWFrVmFyJHJsb2cudmFyaWFuY2UpCmdncGxvdChwZWFrVmFyLCBhZXMoeD1QQ0EucmFuaywgeT1jdW11bGF0aXZlLlZhcmlhbmNlKSkgKyBnZW9tX2xpbmUoKSArIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKDUwMCwxMDAwLDIwMDAsNTAwMCwxMDAwMCwxMDAwMCwxNTAwMCksIGxpbmV0eXBlPSJkb3R0ZWQiKSArIGdndGl0bGUoIkFMQ0wgQ3VtdWxhdGl2ZSBWYXJpYW5jZSBwZXIgUEMgUmFuayIpCmBgYAoKUENBIHRvcCBjb250cmlidXRvcnMgc2VsZWN0aW9uICg1SyB0byAyMEspCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnJ2IDwtIHJvd1ZhcnMoYXNzYXkocmxkX3JuYSkpCnNlbGVjdCA8LSBvcmRlcihydiwgZGVjcmVhc2luZz1UUlVFKVtzZXFfbGVuKG1pbigxMDAwLCBsZW5ndGgocnYpKSldIAojQ2hhbmdpbmcgdGhlIDEwMDAgaGVyZSB3aWxsIHNlbGVjdCBhIERpZmZlcmVudCBudW1iZXIgb2YgdG9wIGNvbnRyaWJ1dG9ycyBpZiBkZXNpcmVkCnBjIDwtIHByY29tcCh0KGFzc2F5KHJsZF9ybmEpW3NlbGVjdCxdKSkKbG9hZGluZ3MgPC0gYXMuZGF0YS5mcmFtZShwYyRyb3RhdGlvbikKYWxvYWQgPC0gYWJzKGxvYWRpbmdzKQp3cml0ZS50YWJsZShzd2VlcChhbG9hZCwgMiwgY29sU3VtcyhhbG9hZCksICIvIiksIGZpbGU9IlRvcCAxMDAwIEFMQ0wgUk5BIFBDQSBDb250cmlidXRvcnMudHh0IikKI0NsZWFuIHVwIHZhcmlhYmxlcwpybShydikKcm0oc2VsZWN0KQpybShwYykKcm0obG9hZGluZ3MpCiNNZXJnZSB0b3AgMTAwMDAgZWxlbWVudHMgd2l0aCB0aGVpciBhbm5vdGF0aW9ucwp0b3AxMDAwIDwtIHJlbF9ybmFbKHJlbF9ybmEkRW5zZW1ibElEICVpbiUgcm93bmFtZXMoYWxvYWQpKSxdCndyaXRlLnRhYmxlKHRvcDEwMDAwLCBmaWxlPSJUb3AxMDAwLnR4dCIsIHNlcD0nXHQnKQoKUk5BX3JlbCA8LSByZWxfcm5hWyhyZWxfcm5hJEVuc2VtYmxJRCAlaW4lIHRvcDEwMDAkRW5zZW1ibElEKSxdCnNhdmVSRFMoUk5BX3JlbCwgZmlsZT0iUk5BX3JlbC5yZHMiKQpjb2xuYW1lcyhSTkFfcmVsKSA8LSBjKCJFbnNlbWJsSUQiLCAiS0FSUEFTMjk5LmdlbmUiLCAiTDgyLmdlbmUiLCAiU1VQTTIuZ2VuZSIsICJTVURITDEuZ2VuZSIsICJLSUpLLmdlbmUiLCAiU1I3ODYuZ2VuZSIsICJGRVBELmdlbmUiLCAiREw0MC5nZW5lIiwgIk1BQzJBLmdlbmUiKQpoZWFkKHRvcDEwMDApCmhlYWQoUk5BX3JlbCkKI0dhcCBTdGF0IENhbGN1bGF0aW9uCiNzZXQuc2VlZCg0MikgCiNUaGlzIHNldHMgdGhlIHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9yIHNlZWQgc28gaXQgd2lsbCBhbHdheXMgcHJvZHVjZSB0aGUgc2FtZSByZXN1bHQuIFlvdSBjYW4gcGljayB3aGF0ZXZlciBudW1iZXIgeW91J2QgbGlrZS4KZ2FwX3N0YXQgPC0gY2x1c0dhcChSTkFfcmVsWzI6MTBdLCBGVU4gPSBrbWVhbnMsIG5zdGFydCA9IDIwLCBLLm1heCA9IDI0LCBCID0gNTApIAojLi4uID0gbnVtYmVyIG9mIHNhbXBsZXMgKyAxCiNDbHVzdGVyIFN0YXQgUGxvdHMKZnZpel9uYmNsdXN0KHRvcDEwMDAwWzI6MTBdLCBrbWVhbnMsIG1ldGhvZD0id3NzIiwgay5tYXg9MjQpICsgdGhlbWVfbWluaW1hbCgpICsgZ2d0aXRsZSgiRWxib3cgUGxvdCIpCmZ2aXpfZ2FwX3N0YXQoZ2FwX3N0YXQpICsgdGhlbWVfbWluaW1hbCgpICsgZ2d0aXRsZSgiR2FwIFN0YXRpc3RpYyIpCmZ2aXpfbmJjbHVzdCh0b3AxMDAwMFsyOjEwXSwga21lYW5zLCBtZXRob2Q9InNpbGhvdWV0dGUiLCBrLm1heD0yNCkgKyB0aGVtZV9taW5pbWFsKCkgKyBnZ3RpdGxlKCJTaWxob3VldHRlIFBsb3QiKQojay1tZWFucyBDbHVzdGVyaW5nCkNMQVJBazIgPC0gY2xhcmEodG9wMTAwMFsyOjEwXSwyLG1lZG9pZHMueD1UUlVFKQpDTEFSQWszIDwtIGNsYXJhKHRvcDEwMDBbMjoxMF0sMyxtZWRvaWRzLng9VFJVRSkKQ0xBUkFrNCA8LSBjbGFyYSh0b3AxMDAwWzI6MTBdLDQsbWVkb2lkcy54PVRSVUUpCkNMQVJBazUgPC0gY2xhcmEodG9wMTAwMFsyOjEwXSw1LG1lZG9pZHMueD1UUlVFKQpDTEFSQWs2IDwtIGNsYXJhKHRvcDEwMDBbMjoxMF0sNixtZWRvaWRzLng9VFJVRSkKQ0xBUkFrNyA8LSBjbGFyYSh0b3AxMDAwWzI6MTBdLDcsbWVkb2lkcy54PVRSVUUpCkNMQVJBazggPC0gY2xhcmEodG9wMTAwMFsyOjEwXSw4LG1lZG9pZHMueD1UUlVFKQoKdG9wMTAwMCRrMiA8LSBDTEFSQWsyJGNsdXN0ZXJpbmcKdG9wMTAwMCRrMyA8LSBDTEFSQWszJGNsdXN0ZXJpbmcKdG9wMTAwMCRrNCA8LSBDTEFSQWs0JGNsdXN0ZXJpbmcKdG9wMTAwMCRrNSA8LSBDTEFSQWs1JGNsdXN0ZXJpbmcKdG9wMTAwMCRrNiA8LSBDTEFSQWs2JGNsdXN0ZXJpbmcKdG9wMTAwMCRrNyA8LSBDTEFSQWs3JGNsdXN0ZXJpbmcKdG9wMTAwMCRrOCA8LSBDTEFSQWs4JGNsdXN0ZXJpbmcKCgojRGlzcGxheWluZyBIZWF0bWFwcwojay1tZWFucyAyMApwaGVhdG1hcCh0b3AxMDAwW29yZGVyKHRvcDEwMDBbLCAxMCsxXSksXVssMjoxMF0sIGNsdXN0ZXJfY29scz1UUlVFLCBjbHVzdGVyX3Jvd3M9RkFMU0UsIGJvcmRlcl9jb2xvcj1OQSwgc2hvd19yb3duYW1lcz1GQUxTRSwgYW5ub3RhdGlvbl9yb3c9dG9wMTAwMFsxMCsxXSkKI2stbWVhbnMgMjYKcGhlYXRtYXAodG9wMTAwMFtvcmRlcih0b3AxMDAwWywgMTArMl0pLF1bLDI6MTBdLCBjbHVzdGVyX2NvbHM9VFJVRSwgY2x1c3Rlcl9yb3dzPUZBTFNFLCBib3JkZXJfY29sb3I9TkEsIHNob3dfcm93bmFtZXM9RkFMU0UsIGFubm90YXRpb25fcm93PXRvcDEwMDBbMTArMl0pCiNrLW1lYW5zIDMwCnBoZWF0bWFwKHRvcDEwMDBbb3JkZXIodG9wMTAwMFssIDEwKzNdKSxdWywyOjEwXSwgY2x1c3Rlcl9jb2xzPVRSVUUsIGNsdXN0ZXJfcm93cz1GQUxTRSwgYm9yZGVyX2NvbG9yPU5BLCBzaG93X3Jvd25hbWVzPUZBTFNFLCBhbm5vdGF0aW9uX3Jvdz10b3AxMDAwWzEwKzNdKQojay1tZWFucyAzNgpwaGVhdG1hcCh0b3AxMDAwW29yZGVyKHRvcDEwMDBbLCAxMCs0XSksXVssMjoxMF0sIGNsdXN0ZXJfY29scz1UUlVFLCBjbHVzdGVyX3Jvd3M9RkFMU0UsIGJvcmRlcl9jb2xvcj1OQSwgc2hvd19yb3duYW1lcz1GQUxTRSwgYW5ub3RhdGlvbl9yb3c9dG9wMTAwMFsxMCs0XSkKI2stbWVhbnMgMTUKcGhlYXRtYXAodG9wMTAwMFtvcmRlcih0b3AxMDAwWywgMTArNV0pLF1bLDI6MTBdLCBjbHVzdGVyX2NvbHM9VFJVRSwgY2x1c3Rlcl9yb3dzPUZBTFNFLCBib3JkZXJfY29sb3I9TkEsIHNob3dfcm93bmFtZXM9RkFMU0UsIGFubm90YXRpb25fcm93PXRvcDEwMDBbMTArNV0pCiNrLW1lYW5zIDI1CnBoZWF0bWFwKHRvcDEwMDBbb3JkZXIodG9wMTAwMFssIDEwKzZdKSxdWywyOjEwXSwgY2x1c3Rlcl9jb2xzPVRSVUUsIGNsdXN0ZXJfcm93cz1GQUxTRSwgYm9yZGVyX2NvbG9yPU5BLCBzaG93X3Jvd25hbWVzPUZBTFNFLCBhbm5vdGF0aW9uX3Jvdz10b3AxMDAwWzEwKzZdKQojay1tZWFucyAyMiAKcGhlYXRtYXAodG9wMTAwMFtvcmRlcih0b3AxMDAwWywgMTArN10pLF1bLDI6MTBdLCBjbHVzdGVyX2NvbHM9VFJVRSwgY2x1c3Rlcl9yb3dzPUZBTFNFLCBib3JkZXJfY29sb3I9TkEsIHNob3dfcm93bmFtZXM9RkFMU0UsIGFubm90YXRpb25fcm93PXRvcDEwMDBbMTArN10pCmBgYAoKLS1STkEgRU5ELS0KClBSTwoKYGBge3J9CiNQcmVwYXJlIGRSRUcgcGVhayBzYWYgZm9yIGZlYXR1cmVDb3VudHMKZFJFRyA8LSByZWFkLnRhYmxlKCJBTENMX3MwLjVwMC4wMjVfZmlsdGVyZWQuYmVkIikKY29sbmFtZXMoZFJFRykgPC0gYygiY2hyb21vc29tZSIsICJzdGFydCIsICJlbmQiLCAiZFJFR3BlYWtJRCIsICJkUkVHc2NvcmUiLCAic3RyYW5kIiwgImNlbnRlclN0YXJ0IiwKICAgICAgICAgICAgICAgICAgICAiY2VudGVyRW5kIikKd3JpdGUudGFibGUoZFJFRywgZmlsZT0iZFJFRyByZWFkdGFibGUudHh0Iiwgc2VwPSJcdCIsIHF1b3RlPUYsIHJvdy5uYW1lcz1GKQpkUkVHc2FmIDwtIGRhdGEuZnJhbWUoIkdlbmVJRCI9ZFJFRyRkUkVHcGVha0lELCAiQ2hyIj1kUkVHJGNocm9tb3NvbWUsICJTdGFydCI9ZFJFRyRzdGFydCwgIkVuZCI9ZFJFRyRlbmQsICJTdHJhbmQiPSIrIikKd3JpdGUudGFibGUoZFJFR3NhZiwgZmlsZT0iZFJFRy5zYWYiLCBzZXA9Ilx0IiwgcXVvdGU9Riwgcm93Lm5hbWVzPUYpCiNMb2FkIHNhbXBsZSBTQU0gZmlsZSBsaXN0CiNwcm9maWxlcyA8LSBjKCIiKQoKI1J1biBmZWF1dHJlQ291bnRzCiNmY19kUkVHIDwtIGZlYXR1cmVDb3VudHMocHJvZmlsZXMsIGFubm90LmV4dD1kUkVHc2FmLCBpc0dURkFubm90YXRpb25GaWxlPUZBTFNFLCBtaW5NUVM9MTAsIGNvdW50Q2hpbWVyaWNGcmFnbWVudHM9RkFMU0UsIGlzUGFpcmVkRW5kPUZBTFNFLCBzdHJhbmRTcGVjaWZpYz0wLCBudGhyZWFkcz04KQpmY19wcm8gPC0gcmVhZC50YWJsZSgiUFJPX0FMQ0xfZFJFR2NvdW50cyIsIGhlYWRlcj1ULCByb3cubmFtZXM9MSkKZmNfcHJvIDwtIGFzLmRhdGEuZnJhbWUoZmNfcHJvKQpmY19wcm8gPC0gc3Vic2V0KGZjX3Bybywgc2VsZWN0PS1jKENociwgU3RhcnQsIEVuZCwgU3RyYW5kLCBMZW5ndGgpKQpjb2xuYW1lcyhmY19wcm8pIDwtIGMoIktBUlBBUzI5OSIsICJMODIiLCAiU1VQTTIiLCAiU1VESEwxIiwgIktJSksiLCAiU1I3ODYiLCAiRkVQRCIsICJETDQwIiwgIk1BQzJBIikKCiNMb2FkIHNhbXBsZSBtZXRhZGF0YSAoY2FuIGFkZCBhcyBtdWNoIHNhbXBsZSBtZXRhZGF0YSBhcyBkZXNpcmVkIGluY2x1ZGluZyBjZWxsIHR5cGUsIGRydWcgdHJlYXRtZW50LCBldGMpCmNvbERhdGFfcHJvIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzPWNvbG5hbWVzKGZjX3BybyRjb3VudHMpLCBzYW1wbGU9YygiS0FSUEFTMjk5IiwgIkw4MiIsICJTVVBNMiIsICJTVURITDEiLCAiS0lKSyIsICJTUjc4NiIsICJGRVBEIiwgIkRMNDAiLCAiTUFDMkEiKSwgc3VidHlwZT1jKCJBTEsrQUxDTCIsICJBTEsrQUxDTCIsICJBTEsrQUxDTCIsICJBTEsrQUxDTCIsICJBTEsrQUxDTCIsICJBTEsrQUxDTCIsICJBTEstQUxDTCIsICJBTEstQUxDTCIsICJBTEstQUxDTCIpKQphbGwoKGNvbERhdGFfcHJvJHNhbXBsZSkgJWluJSBjb2xuYW1lcyhmY19wcm8pKQoKI0NyZWF0ZSBERVNlcSBvYmplY3QgYW5kIGdlbmVyYXRlIG5vcm1hbGl6ZWQgY291bnRzCmRkc19wcm8gPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGE9ZmNfcHJvLCBjb2xEYXRhPWNvbERhdGFfcHJvLCBkZXNpZ249fnNhbXBsZSkKZGRzX3BybyA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzKGRkc19wcm8pCmRlc2VxX3NpemVzX3BybyA8LSBhcy5kYXRhLmZyYW1lKHNpemVGYWN0b3JzKGRkc19wcm8pKQpkZXNlcV9zaXplc19wcm8KdmFyaWFibGUubmFtZXMoZGRzX3BybykKCmtlZXAgPC0gcm93TWF4KGNvdW50cyhkZHNfcHJvLCBub3JtYWxpemU9VFJVRSkpID49IDIwCmRkc19wcm8gPC0gZGRzX3Byb1trZWVwLF0Kc2F2ZVJEUyhkZHNfcHJvLCBmaWxlPSJkUkVHX2Rkcy5yZHMiKQpkUkVHX3JlYWRGaWx0IDwtIGRSRUdbZFJFRyRkUkVHcGVha0lEICVpbiUgcm93bmFtZXMoZGRzX3BybyksXQp3cml0ZS50YWJsZShkUkVHX3JlYWRGaWx0LCBmaWxlPSJkUkVHcGVha3MucmVhZEZpbHQuYmVkIiwgc2VwPSJcdCIsIGNvbC5uYW1lcz1GQUxTRSwgcm93Lm5hbWVzPUZBTFNFLAogICAgICAgICAgICBxdW90ZT1GQUxTRSkKYGBgCgpHZW5lcmF0ZSBub3JtIGNvdW50cyBmaWxlcyBhbmQgc2F2ZSB0byBsb2NhbCBkaXJlY3RvcnkKYGBge3J9CnJhd2NvdW50c19wcm8gPC0gYXMuZGF0YS5mcmFtZShjb3VudHMoZGRzX3Bybywgbm9ybWFsaXplZD1GQUxTRSkpCm5vcm1jb3VudHNfcHJvIDwtIGFzLmRhdGEuZnJhbWUoY291bnRzKGRkc19wcm8sIG5vcm1hbGl6ZWQ9VFJVRSkpCmNvbG5hbWVzKHJhd2NvdW50c19wcm8pIDwtIGMoIktBUlBBUzI5OSIsICJMODIiLCAiU1VQTTIiLCAiU1VESEwxIiwgIktJSksiLCAiU1I3ODYiLCAiRkVQRCIsICJETDQwIiwgIk1BQzJBIikKY29sbmFtZXMobm9ybWNvdW50c19wcm8pIDwtIGMoIktBUlBBUzI5OSIsICJMODIiLCAiU1VQTTIiLCAiU1VESEwxIiwgIktJSksiLCAiU1I3ODYiLCAiRkVQRCIsICJETDQwIiwgIk1BQzJBIikKd3JpdGUudGFibGUocmF3Y291bnRzX3BybywgZmlsZT0icHJvX3Jhd2NvdW50cy50eHQiLCBzZXA9Ilx0IiwgY29sLm5hbWVzPVRSVUUsIHJvdy5uYW1lcz1UUlVFLCBxdW90ZT1GQUxTRSkKd3JpdGUudGFibGUobm9ybWNvdW50c19wcm8sIGZpbGU9InByb19ub3JtY291bnRzLnR4dCIsIHNlcD0iXHQiLCBjb2wubmFtZXM9VFJVRSwgcm93Lm5hbWVzPVRSVUUsIHF1b3RlPUZBTFNFKQpgYGAKCkRhdGEgdHJhbnNmb3JtYXRpb24gZm9yIHBsb3QgZ2VuZXJhdGlvbgpUb3AgNTAwIGNvbnRyaWJ1dG9ycyBpcyB0aGUgZGVmYXVsdCBmb3IgUENBIApTZXQgJ250b3AnIHRvIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gdGhlIGRkcyBvYmplY3QKYGBge3J9CnJsZF9wcm8gPC0gcmxvZyhkZHNfcHJvLCBibGluZD1UUlVFKQpzYW1wbGVEaXN0c3BybyA8LSBkaXN0KHQoYXNzYXkocmxkX3BybykpKQpzYW1wbGVEaXN0TXR4cHJvIDwtIGFzLm1hdHJpeChzYW1wbGVEaXN0c3BybykKcm93bmFtZXMoc2FtcGxlRGlzdE10eHBybykgPC0gcGFzdGUwKHJsZF9wcm8kc2FtcGxlKQpjb2xuYW1lcyhzYW1wbGVEaXN0TXR4cHJvKSA8LSBOVUxMCiNHZW5lcmF0ZSBQQ0EgcGxvdCAoaW50Z3JvdXAgc2V0IGVxdWFsIHRvIGRlc2lyZWQgbWV0YWRhdGEgdmFyaWFibGUpCnBsb3RQQ0EocmxkX3BybywgaW50Z3JvdXAgPSAic3VidHlwZSIsIG50b3A9NjI3NDQpICsgZ2d0aXRsZSgicHJvLXNlcSBQQ0EiKSArIHNjYWxlX2NvbG9yX21hbnVhbChicmVha3MgPSBjKCJBTEsrQUxDTCIsICJBTEstQUxDTCIpLCB2YWx1ZXM9YygiY2hhcnRyZXVzZTMiLCAiY2FkZXRibHVlIikpICsgZ2VvbV90ZXh0X3JlcGVsKGxhYmVsPXJvd25hbWVzKHNhbXBsZURpc3RNdHhwcm8pLCBtYXgub3ZlcmxhcHM9NTApCiN0b3AgNTAwIFBDQQpwbG90UENBKHJsZF9wcm8sIGludGdyb3VwID0gInN1YnR5cGUiKSArIGdndGl0bGUoInByby1zZXEgUENBIFRvcCA1MDAiKSArIHNjYWxlX2NvbG9yX21hbnVhbChicmVha3MgPSBjKCJBTEsrQUxDTCIsICJBTEstQUxDTCIpLCB2YWx1ZXM9YygiY2hhcnRyZXVzZTMiLCAiY2FkZXRibHVlIikpICsgZ2VvbV90ZXh0X3JlcGVsKGxhYmVsPXJvd25hbWVzKHNhbXBsZURpc3RNdHhwcm8pLCBtYXgub3ZlcmxhcHM9NTApCiN0b3AgMTAwMCBQQ0EKcGxvdFBDQShybGRfcHJvLCBpbnRncm91cCA9ICJzdWJ0eXBlIiwgbnRvcD0xMDAwKSArIGdndGl0bGUoInByby1zZXEgUENBIFRvcCAxMDAwIikgKyBzY2FsZV9jb2xvcl9tYW51YWwoYnJlYWtzID0gYygiQUxLK0FMQ0wiLCAiQUxLLUFMQ0wiKSwgdmFsdWVzPWMoImNoYXJ0cmV1c2UzIiwgImNhZGV0Ymx1ZSIpKSArIGdlb21fdGV4dF9yZXBlbChsYWJlbD1yb3duYW1lcyhzYW1wbGVEaXN0TXR4cHJvKSwgbWF4Lm92ZXJsYXBzPTUwKQojdG9wIDUwMDAgUENBCnBsb3RQQ0EocmxkX3BybywgaW50Z3JvdXAgPSAic3VidHlwZSIsIG50b3A9NTAwMCkgKyBnZ3RpdGxlKCJwcm8tc2VxIFBDQSBUb3AgNTAwMCIpICsgc2NhbGVfY29sb3JfbWFudWFsKGJyZWFrcyA9IGMoIkFMSytBTENMIiwgIkFMSy1BTENMIiksIHZhbHVlcz1jKCJjaGFydHJldXNlMyIsICJjYWRldGJsdWUiKSkgKyBnZW9tX3RleHRfcmVwZWwobGFiZWw9cm93bmFtZXMoc2FtcGxlRGlzdE10eHBybyksIG1heC5vdmVybGFwcz01MCkKI3RvcCAxMDAwMCBQQ0EKcGxvdFBDQShybGRfcHJvLCBpbnRncm91cCA9ICJzdWJ0eXBlIiwgbnRvcD0xMDAwMCkgKyBnZ3RpdGxlKCJwcm8tc2VxIFBDQSBUb3AgMTAwMDAiKSArIHNjYWxlX2NvbG9yX21hbnVhbChicmVha3MgPSBjKCJBTEsrQUxDTCIsICJBTEstQUxDTCIpLCB2YWx1ZXM9YygiY2hhcnRyZXVzZTMiLCAiY2FkZXRibHVlIikpICsgZ2VvbV90ZXh0X3JlcGVsKGxhYmVsPXJvd25hbWVzKHNhbXBsZURpc3RNdHhwcm8pLCBtYXgub3ZlcmxhcHM9NTApCiN0b3AgMTUwMDAgUENBCnBsb3RQQ0EocmxkX3BybywgaW50Z3JvdXAgPSAic3VidHlwZSIsIG50b3A9MTUwMDApICsgZ2d0aXRsZSgicHJvLXNlcSBQQ0EgVG9wIDE1MDAwIikgKyBzY2FsZV9jb2xvcl9tYW51YWwoYnJlYWtzID0gYygiQUxLK0FMQ0wiLCAiQUxLLUFMQ0wiKSwgdmFsdWVzPWMoImNoYXJ0cmV1c2UzIiwgImNhZGV0Ymx1ZSIpKSArIGdlb21fdGV4dF9yZXBlbChsYWJlbD1yb3duYW1lcyhzYW1wbGVEaXN0TXR4cHJvKSwgbWF4Lm92ZXJsYXBzPTUwKQojdG9wIDIwMDAwIFBDQQpwbG90UENBKHJsZF9wcm8sIGludGdyb3VwID0gInN1YnR5cGUiLCBudG9wPTIwMDAwKSArIGdndGl0bGUoInByby1zZXEgUENBIFRvcCAyMDAwMCIpICsgc2NhbGVfY29sb3JfbWFudWFsKGJyZWFrcyA9IGMoIkFMSytBTENMIiwgIkFMSy1BTENMIiksIHZhbHVlcz1jKCJjaGFydHJldXNlMyIsICJjYWRldGJsdWUiKSkgKyBnZW9tX3RleHRfcmVwZWwobGFiZWw9cm93bmFtZXMoc2FtcGxlRGlzdE10eHBybyksIG1heC5vdmVybGFwcz01MCkKYGBgCgpBZGRpdGlvbmFsIGRlbmRyb2dyYW0gcGxvdHMKYGBge3J9CmNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKHJldihicmV3ZXIucGFsKDksICJCbHVlcyIpKSkoMjU1KQpwaGVhdG1hcChzYW1wbGVEaXN0TXR4cHJvLCBjb2w9Y29sb3JzLCBib3JkZXJfY29sb3I9TkEsIHNob3dfY29sbmFtZXM9RkFMU0UsIHNob3dfcm93bmFtZXM9VFJVRSwKICAgICAgICAgY2x1c3Rlcl9yb3dzPVRSVUUsIGNsdXN0ZXJfY29scz1UUlVFLCBtYWluPSJBTENMIFNhbXBsZSBEaXN0YW5jZSBDbHVzdGVyaW5nIikKcGxvdChoY2x1c3Qoc2FtcGxlRGlzdHNwcm8pLCBsYWJlbHM9cm93bmFtZXMoc2FtcGxlRGlzdE10eHBybyksIG1haW49IkFMQ0wgU2FtcGxlIERpc3RhbmNlIERlbmRyb2dyYW0iKQpgYGAKCkFzc2VtYmxlIGNvdW50cyByZXN1bHRzCmBgYHtyfQphdmdfcHJvIDwtIGRhdGEuZnJhbWUocGVha0lEPXJvd25hbWVzKG5vcm1jb3VudHNfcHJvKSkKYXZnX3BybyRLQVJQQVMyOTkgPC0gKG5vcm1jb3VudHNfcHJvJEtBUlBBUzI5OSkKYXZnX3BybyRMODIgPC0gKG5vcm1jb3VudHNfcHJvJEw4MikKYXZnX3BybyRTVVBNMiA8LSAobm9ybWNvdW50c19wcm8kU1VQTTIpCmF2Z19wcm8kU1VESEwxIDwtIChub3JtY291bnRzX3BybyRTVURITDEpCmF2Z19wcm8kS0lKSyA8LSAobm9ybWNvdW50c19wcm8kS0lKSykKYXZnX3BybyRTUjc4NiA8LSAobm9ybWNvdW50c19wcm8kU1I3ODYpCmF2Z19wcm8kRkVQRCA8LSAobm9ybWNvdW50c19wcm8kRkVQRCkKYXZnX3BybyRETDQwIDwtIChub3JtY291bnRzX3BybyRETDQwKQphdmdfcHJvJE1BQzJBIDwtIChub3JtY291bnRzX3BybyRNQUMyQSkKCmF2Z19wcm8kbWF4IDwtIGFwcGx5KGF2Z19wcm9bYygiS0FSUEFTMjk5IiwgIkw4MiIsICJTVVBNMiIsICJTVURITDEiLCAiS0lKSyIsICJTUjc4NiIsICJGRVBEIiwgIkRMNDAiLCAiTUFDMkEiKV0sIDEsIG1heCwgbmEucm09VFJVRSkKcmVsX3BybyA8LSBkYXRhLmZyYW1lKHBlYWtJRD1hdmdfcHJvJHBlYWtJRCkKcmVsX3BybyRLQVJQQVMyOTkgPC0gYXZnX3BybyRLQVJQQVMyOTkgLyBhdmdfcHJvJG1heApyZWxfcHJvJEw4MiA8LSBhdmdfcHJvJEw4MiAvIGF2Z19wcm8kbWF4CnJlbF9wcm8kU1VQTTIgPC0gYXZnX3BybyRTVVBNMiAvIGF2Z19wcm8kbWF4CnJlbF9wcm8kU1VESEwxIDwtIGF2Z19wcm8kU1VESEwxIC8gYXZnX3BybyRtYXgKcmVsX3BybyRLSUpLIDwtIGF2Z19wcm8kS0lKSyAvIGF2Z19wcm8kbWF4CnJlbF9wcm8kU1I3ODYgPC0gYXZnX3BybyRTUjc4NiAvIGF2Z19wcm8kbWF4CnJlbF9wcm8kRkVQRCA8LSBhdmdfcHJvJEZFUEQgLyBhdmdfcHJvJG1heApyZWxfcHJvJERMNDAgPC0gYXZnX3BybyRETDQwIC8gYXZnX3BybyRtYXgKcmVsX3BybyRNQUMyQSA8LSBhdmdfcHJvJE1BQzJBIC8gYXZnX3BybyRtYXgKCmBgYAoKUENBIHRvcCBjb250cmlidXRvcnMgc2VsZWN0aW9uICg1MDAwIHRyaWVkIGZpcnN0KQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkUkVHX2Fubm90IDwtIHJlYWQudGFibGUoIkFMQ0xfcDEwMDBfZFJFR3BlYWtfYW5ub3RhdGlvbi50eHQiLCBzZXA9Ilx0IiwgaGVhZGVyPVRSVUUpCgpQUk9fcmVsIDwtIHJlbF9wcm8KI2NoZWNrIHRoaXMKY29sbmFtZXMoUFJPX3JlbCkgPC0gYygiZFJFR3BlYWtJRCIsICJLQVJQQVMyOTkuZW5oYW5jZXIiLCAiTDgyLmVuaGFuY2VyIiwgIlNVUE0yLmVuaGFuY2VyIiwgIlNVREhMMS5lbmhhbmNlciIsICJLSUpLLmVuaGFuY2VyIiwgICJTUjc4Ni5lbmhhbmNlciIsICJGRVBELmVuaGFuY2VyIiwgIkRMNDAuZW5oYW5jZXIiLCAgIk1BQzJBLmVoYW5jZXIiKQpzYXZlUkRTKFBST19yZWwsIGZpbGU9InByb19yZWwucmRzIikgCmhlYWQoUFJPX3JlbCkKCnJ2IDwtIHJvd1ZhcnMoYXNzYXkocmxkX3BybykpCnNlbGVjdCA8LSBvcmRlcihydiwgZGVjcmVhc2luZz1UUlVFKVtzZXFfbGVuKG1pbig1MDAwLCBsZW5ndGgocnYpKSldIAojQ2hhbmdpbmcgdGhlIDUwMDAgaGVyZSB3aWxsIHNlbGVjdCBhIERpZmZlcmVudCBudW1iZXIgb2YgdG9wIGNvbnRyaWJ1dG9ycyBpZiBkZXNpcmVkCnBjIDwtIHByY29tcCh0KGFzc2F5KHJsZF9wcm8pW3NlbGVjdCxdKSkKbG9hZGluZ3MgPC0gYXMuZGF0YS5mcmFtZShwYyRyb3RhdGlvbikKYWxvYWQgPC0gYWJzKGxvYWRpbmdzKQp3cml0ZS50YWJsZShzd2VlcChhbG9hZCwgMiwgY29sU3VtcyhhbG9hZCksICIvIiksIGZpbGU9IlRvcCA1MDAwIHBybyBQQ0EgQ29udHJpYnV0b3JzLnR4dCIpCiNDbGVhbiB1cCB2YXJpYWJsZXMKcm0ocnYpCnJtKHNlbGVjdCkKcm0ocGMpCnJtKGxvYWRpbmdzKQojTWVyZ2UgdG9wIDUwMDAgZWxlbWVudHMgd2l0aCB0aGVpciBhbm5vdGF0aW9ucwp0b3A1MDAwIDwtIGRSRUdfYW5ub3RbKGRSRUdfYW5ub3QkcGVha0lEICVpbiUgcm93bmFtZXMoYWxvYWQpKSxdCndyaXRlLnRhYmxlKHRvcDUwMDAsIGZpbGU9IlRvcDUwMDAudHh0Iiwgc2VwPSdcdCcpCmdncGxvdCh0b3A1MDAwLCBhZXMoeD1mYWN0b3IoMSksIGZpbGw9Z2Vub21pY0NhdGVnb3J5KSkrZ2VvbV9iYXIod2lkdGg9MSkrY29vcmRfcG9sYXIoInkiKSArIGdndGl0bGUoIlRvcCA1MDAwIFBDQSBDb250cmlidXRvcnMgZFJFR3BlYWsgQW5ub3RhdGlvbnMiKQpnZ3Bsb3QoZFJFR19hbm5vdCwgYWVzKHg9ZmFjdG9yKDEpLCBmaWxsPWdlbm9taWNDYXRlZ29yeSkpK2dlb21fYmFyKHdpZHRoPTEpK2Nvb3JkX3BvbGFyKCJ5IikgKyBnZ3RpdGxlKCJBbGwgZFJFR3BlYWsgQW5ub3RhdGlvbnMiKQoKI2ZpbHRlciByZXN1bHRzIGRvd24gYnkgdG9wIGNvbnRyaWJ1dG9ycwpwcm9fNTAwMF9yZWwgPC0gcmVsX3Byb1socmVsX3BybyRwZWFrSUQgJWluJSB0b3A1MDAwJHBlYWtJRCksXQojR2FwIFN0YXQgQ2FsY3VsYXRpb24KI3NldC5zZWVkKDQyKSAKI1RoaXMgc2V0cyB0aGUgcmFuZG9tIG51bWJlciBnZW5lcmF0b3Igc2VlZCBzbyBpdCB3aWxsIGFsd2F5cyBwcm9kdWNlIHRoZSBzYW1lIHJlc3VsdC4gWW91IGNhbiBwaWNrIHdoYXRldmVyIG51bWJlciB5b3UnZCBsaWtlLgpnYXBfc3RhdCA8LSBjbHVzR2FwKHByb181MDAwX3JlbFsyOjEwXSwgRlVOID0ga21lYW5zLCBuc3RhcnQgPSAyMCwgSy5tYXggPSAyNCwgQiA9IDUwKSAKIy4uLiA9IG51bWJlciBvZiBzYW1wbGVzICsgMQojQ2x1c3RlciBTdGF0IFBsb3RzCmZ2aXpfbmJjbHVzdChwcm9fNTAwMF9yZWxbMjoxMF0sIGttZWFucywgbWV0aG9kPSJ3c3MiLCBrLm1heD0yNCkgKyB0aGVtZV9taW5pbWFsKCkgKyBnZ3RpdGxlKCJFbGJvdyBQbG90IikKZnZpel9nYXBfc3RhdChnYXBfc3RhdCkgKyB0aGVtZV9taW5pbWFsKCkgKyBnZ3RpdGxlKCJHYXAgU3RhdGlzdGljIikKZnZpel9uYmNsdXN0KHByb181MDAwX3JlbFsyOjEwXSwga21lYW5zLCBtZXRob2Q9InNpbGhvdWV0dGUiLCBrLm1heD0yNCkgKyB0aGVtZV9taW5pbWFsKCkgKyBnZ3RpdGxlKCJTaWxob3VldHRlIFBsb3QiKQojay1tZWFucyBDbHVzdGVyaW5nCkNMQVJBazQgPC0gY2xhcmEocHJvXzUwMDBfcmVsWzI6MTBdLDQsbWVkb2lkcy54PVRSVUUpCkNMQVJBazUgPC0gY2xhcmEocHJvXzUwMDBfcmVsWzI6MTBdLDUsbWVkb2lkcy54PVRSVUUpCkNMQVJBazYgPC0gY2xhcmEocHJvXzUwMDBfcmVsWzI6MTBdLDYsbWVkb2lkcy54PVRSVUUpCkNMQVJBazcgPC0gY2xhcmEocHJvXzUwMDBfcmVsWzI6MTBdLDcsbWVkb2lkcy54PVRSVUUpCkNMQVJBazggPC0gY2xhcmEocHJvXzUwMDBfcmVsWzI6MTBdLDgsbWVkb2lkcy54PVRSVUUpCgpwcm9fNTAwMF9yZWwkazQgPC0gQ0xBUkFrNCRjbHVzdGVyaW5nCnByb181MDAwX3JlbCRrNSA8LSBDTEFSQWs1JGNsdXN0ZXJpbmcKcHJvXzUwMDBfcmVsJGs2IDwtIENMQVJBazYkY2x1c3RlcmluZwpwcm9fNTAwMF9yZWwkazcgPC0gQ0xBUkFrNyRjbHVzdGVyaW5nCnByb181MDAwX3JlbCRrOCA8LSBDTEFSQWs4JGNsdXN0ZXJpbmcKCiNEaXNwbGF5aW5nIEhlYXRtYXBzCiNrLW1lYW5zIDQKcGhlYXRtYXAocHJvXzUwMDBfcmVsW29yZGVyKHByb181MDAwX3JlbFssIDEwKzFdKSxdWywyOjEwXSwgY2x1c3Rlcl9jb2xzPVRSVUUsIGNsdXN0ZXJfcm93cz1GQUxTRSwgYm9yZGVyX2NvbG9yPU5BLCBzaG93X3Jvd25hbWVzPUZBTFNFLCBhbm5vdGF0aW9uX3Jvdz1wcm9fNTAwMF9yZWxbMTArMV0pCiNrLW1lYW5zIDUKcGhlYXRtYXAocHJvXzUwMDBfcmVsW29yZGVyKHByb181MDAwX3JlbFssIDEwKzJdKSxdWywyOjEwXSwgY2x1c3Rlcl9jb2xzPVRSVUUsIGNsdXN0ZXJfcm93cz1GQUxTRSwgYm9yZGVyX2NvbG9yPU5BLCBzaG93X3Jvd25hbWVzPUZBTFNFLCBhbm5vdGF0aW9uX3Jvdz1wcm9fNTAwMF9yZWxbMTArMl0pCiNrLW1lYW5zIDYKcGhlYXRtYXAocHJvXzUwMDBfcmVsW29yZGVyKHByb181MDAwX3JlbFssIDEwKzNdKSxdWywyOjEwXSwgY2x1c3Rlcl9jb2xzPVRSVUUsIGNsdXN0ZXJfcm93cz1GQUxTRSwgYm9yZGVyX2NvbG9yPU5BLCBzaG93X3Jvd25hbWVzPUZBTFNFLCBhbm5vdGF0aW9uX3Jvdz1wcm9fNTAwMF9yZWxbMTArM10pCiNrLW1lYW5zIDcKcGhlYXRtYXAocHJvXzUwMDBfcmVsW29yZGVyKHByb181MDAwX3JlbFssIDEwKzRdKSxdWywyOjEwXSwgY2x1c3Rlcl9jb2xzPVRSVUUsIGNsdXN0ZXJfcm93cz1GQUxTRSwgYm9yZGVyX2NvbG9yPU5BLCBzaG93X3Jvd25hbWVzPUZBTFNFLCBhbm5vdGF0aW9uX3Jvdz1wcm9fNTAwMF9yZWxbMTArNF0pCiNrLW1lYW5zIDgKcGhlYXRtYXAocHJvXzUwMDBfcmVsW29yZGVyKHByb181MDAwX3JlbFssIDEwKzVdKSxdWywyOjEwXSwgY2x1c3Rlcl9jb2xzPVRSVUUsIGNsdXN0ZXJfcm93cz1GQUxTRSwgYm9yZGVyX2NvbG9yPU5BLCBzaG93X3Jvd25hbWVzPUZBTFNFLCBhbm5vdGF0aW9uX3Jvdz1wcm9fNTAwMF9yZWxbMTArNV0pCmBgYAoKLS1QUk8gRU5ELS0KCkNvbWJpbmVkIFBSTyBhbmQgUk5BIGFuYWx5c2lzCgpMb2FkIERFU2VxIG9iamVjdHMgYW5kIGFubm90YXRpb25zCmBgYHtyfQojZGRzX3JuYSA8LSByZWFkUkRTKCJSTkFfZGRzLnJkcyIpCiNkZHNfcHJvIDwtIHJlYWRSRFMoImRSRUdfZGRzLnJkcyIpCmRSRUdhbm4gPC0gcmVhZC50YWJsZSgiQUxDTF9wMTAwMF9tMTAwMDAwMF9nZW5lLWNlbnRyaWNfY2xvc2VzdC1kUkVHcGVha3MudHh0Iiwgc2VwPSJcdCIsIGhlYWRlcj1UKQp3cml0ZS50YWJsZShkZXNlcV9zaXplc19wcm8sIGZpbGU9IkRFc2VxIHNpemUgZmFjdG9ycy50eHQiLCBzZXA9Ilx0IiwgcXVvdGU9Riwgcm93Lm5hbWVzPVQpCmBgYAoKTWFrZSBwcmVsaW0gZGF0YWZyYW1lcwpgYGB7cn0Kbm9ybV9ybmEgPC0gYXMuZGF0YS5mcmFtZShjb3VudHMoZGRzX3JuYSwgbm9ybWFsaXplZD1UUlVFKSkKcmxkX3JuYTIgPC0gcmxvZyhkZHNfcm5hLCBibGluZD1UUlVFKQoKY29sbmFtZXMobm9ybV9ybmEpIDwtIGRkc19ybmEkc2FtcGxlICNTaW1wbGlmaWVkIG5hbWVzIGZyb20gZGRzIG1ldGFkYXRhCgpyZWxfcm5hMiA8LSBkYXRhLmZyYW1lKCJnZW5lSUQiPXJvd25hbWVzKG5vcm1fcm5hKSkKcmVsX3JuYTIkbWF4LmdlbmUuY291bnRzIDwtIGFwcGx5KG5vcm1fcm5hWzE6bmNvbChub3JtX3JuYSldLCAxLCBtYXgsIG5hLnJtPVRSVUUpCmk9MQp3aGlsZSAoaTw9bmNvbChub3JtX3JuYSkpewogIHJlbF9ybmEyW3Bhc3RlMChkZHNfcm5hJHNhbXBsZVtpXSwiLmdlbmUucmVsIildIDwtIG5vcm1fcm5hWyxpXS9yZWxfcm5hMiRtYXguZ2VuZS5jb3VudHMKICBpPWkrMQp9CiNyZW9yZGVyCnJlbF9ybmEyIDwtIHJlbF9ybmEyWyxjKDEsMzoxMSwyKV0KCm5vcm1fcHJvIDwtIGFzLmRhdGEuZnJhbWUoY291bnRzKGRkc19wcm8sIG5vcm1hbGl6ZWQ9VFJVRSkpCgpjb2xuYW1lcyhub3JtX3BybykgPC0gZGRzX3BybyRzYW1wbGUgI1NpbXBsaWZpZWQgbmFtZXMgZnJvbSBkZHMgbWV0YWRhdGEKCm5vcm1fcHJvIDwtIG5vcm1fcHJvW25hbWVzKG5vcm1fcm5hKV0gI3Jlb3JkZXIgc2FtcGxlIGNvbHVtbnMgdG8gbWF0Y2ggUk5BLXNlcSBkYXRhCgpjb2xuYW1lcyhQUk9fcmVsKVsxXSA8LSAoImNsb3Nlc3RfcGVhayIpCiNyZWxfcHJvMiA8LSBkYXRhLmZyYW1lKCJjbG9zZXN0X3BlYWsiPXJvd25hbWVzKG5vcm1fcHJvKSkKUFJPX3JlbCRtYXguZFJFRy5jb3VudHMgPC0gYXBwbHkobm9ybV9wcm9bMTpuY29sKG5vcm1fcHJvKV0sIDEsIG1heCwgbmEucm09VFJVRSkKI1BST19yZWwgPC0gUFJPX3JlbFssYygxLDI3LDI6MjYpXSAjcmVvcmRlciBjb2x1bW5zCiNpPTEKI3doaWxlIChpPD1uY29sKG5vcm1fcHJvKSl7CiMgIHJlbF9wcm8yW3Bhc3RlMChkZHNfcHJvJHNhbXBsZVtpXSwiLmRSRUcucmVsIildIDwtIG5vcm1fcHJvWyxpXS9yZWxfcHJvMiRtYXguZFJFRy5jb3VudHMKIyAgaT1pKzEKI30KYGBgCgpTZWxlY3RpbmcgZ2VuZSBQQ0EgY29udHJpYnV0b3JzIGJ5IHZhcmlhbmNlCmBgYHtyfQojU2VsZWN0IHRvcCAxMDAwIFBDQSBjb250cmlidXRvcnMKcnYgPC0gcm93VmFycyhhc3NheShybGRfcm5hMikpCnNlbGVjdCA8LSBvcmRlcihydiwgZGVjcmVhc2luZz1UUlVFKVtzZXFfbGVuKG1pbigxMDAwLCBsZW5ndGgocnYpKSldICNDaGFuZ2luZyB0aGUgNTAwIGhlcmUgd2lsbCBzZWxlY3QgYSBkaWZmZXJlbnQgbnVtYmVyIG9mIHRvcCBjb250cmlidXRvcnMgaWYgZGVzaXJlZApwYyA8LSBwcmNvbXAodChhc3NheShybGRfcm5hMilbc2VsZWN0LF0pKQpsb2FkaW5ncyA8LSBhcy5kYXRhLmZyYW1lKHBjJHJvdGF0aW9uKQphbG9hZCA8LSBhYnMobG9hZGluZ3MpCnN3ZWVwKGFsb2FkLCAyLCBjb2xTdW1zKGFsb2FkKSwgIi8iKQogCiNDbGVhbiB1cCB2YXJpYWJsZXMKcm0ocnYpCnJtKHNlbGVjdCkKcm0ocGMpCnJtKGxvYWRpbmdzKQogCiNNZXJnZSB0b3AgNTAwIGVsZW1lbnRzIHdpdGggdGhlaXIgYW5ub3RhdGlvbnMKdG9wMWtfcmVsX3JuYSA8LSByZWxfcm5hMlsocmVsX3JuYTIkZ2VuZUlEICVpbiUgcm93bmFtZXMoYWxvYWQpKSxdICNmaWx0ZXIgcmVsIGdlbmUgZGF0YWZyYW1lIGZvciB0b3AgMTBrCmBgYAoKRGV0ZXJtaW5pbmcgY2x1c3RlcnMgKGs9MykKYGBge3J9CkNMQVJBIDwtIGNsYXJhKHRvcDFrX3JlbF9ybmFbMjoxMF0sMyxtZWRvaWRzLng9VFJVRSkKCnRvcDFrX3JlbF9ybmEkazMgPC0gQ0xBUkEkY2x1c3RlcmluZwoKcGhlYXRtYXAodG9wMWtfcmVsX3JuYVtvcmRlcih0b3Axa19yZWxfcm5hWywxMV0pLF1bLDI6MTBdLCBjbHVzdGVyX2NvbHM9VFJVRSwgY2x1c3Rlcl9yb3dzPUZBTFNFLCBib3JkZXJfY29sb3I9TkEsIHNob3dfcm93bmFtZXM9RkFMU0UsIGFubm90YXRpb25fcm93PXRvcDFrX3JlbF9ybmFbMTFdKQpgYGAKCkRhdGEgaW50ZWdyYXRpb24KYGBge3J9CmdlbmVfcmVzIDwtIHJlbF9ybmEyWyghcmVsX3JuYTIkZ2VuZUlEICVpbiUgdG9wMWtfcmVsX3JuYSRnZW5lSUQpLF0gI2ZpbHRlciBvdXQgZ2VuZXMgbm90IGluIHRvcCAxMGsKZ2VuZV9yZXMkazMgPC0gTkEgI2FkZCB2YWx1ZSBmb3IgZ2VuZXMgbm90IGNsdXN0ZXJlZApnZW5lX3JlcyA8LSByYmluZCh0b3Axa19yZWxfcm5hLCBnZW5lX3JlcykgI2NvbWJpbmUgd2l0aCB0b3AgMTBrIGdlbmVzIHdpdGggY2x1c3RlciBkZXNpZ25hdGlvbnMKI2dlbmVfcmVzJGdlbmVJRCA8LSBzYXBwbHkoc3Ryc3BsaXQoZ2VuZV9yZXMkZ2VuZUlELCBzcGxpdD0iWy5dIiksIGhlYWQsIDEpICNzaW1wbGlmeSBnZW5lSURzIGJ5IHJlbW92aW5nIHZlcnNpb24gbnVtYmVycwoKY29tYl9yZXMgPC0gdW5pcXVlKG1lcmdlKGRSRUdhbm4sIGdlbmVfcmVzLCBieS54PSJnZW5lSUQiLCBieS55PSJnZW5lSUQiLCBhbGwueT1UKSkKY29sbmFtZXMoY29tYl9yZXMpWzFdIDwtICJnZW5lSUQiIApjb21iX3JlcyA8LSBtZXJnZShjb21iX3JlcywgUFJPX3JlbCwgYnkueD0iY2xvc2VzdF9wZWFrIiwgYnkueT0iY2xvc2VzdF9wZWFrIiwgYWxsLng9VCkgI2NvbWJpbmUgd2l0aCBkUkVHIHBlYWsgZGF0YSwga2VlcCBnZW5lcyB3aXRob3V0IGEgY2xvc2VzdCBkUkVHIHBlYWsgY2FsbAoKY29tYl9yZXMgPC0gY29tYl9yZXNbLGMoMjo5LDEzOjIzLDEsMTA6MTIsMjQ6MzMpXSAjcmVvcmRlciBjb2x1bW5zCmBgYAoKR2VuZS10by1FbmhhbmNlciBjb25jb3JkYW5jZSBhbmFseXNpcyAtLSB0aGlzIGdlbmVyYXRlcyBhIHN0YXQgcmFuZ2luZyBmcm9tIC0xIChhbnRpLWNvcnJlbGF0ZWQpIHRvIDAgKG5vdCBjb3JyZWxhdGVkKSB0byArMSAoY29ycmVsYXRlZCkgcmVwb3J0ZWQgYXMgU3BlYXJtYW4gcmhvCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQojbm90ZSB0aGlzIHdpbGwgcHJvZHVjZSBhIGxvdCBvZiBlcnJvcnMgZm9yIHJvd3MgdGhhdCBkb24ndCBoYXZlIGEgZ2VuZSBhbmQgYSBhbiBlbmhhbmNlciwgc28gSSd2ZSBzaWxlbmNlZCB0aGVtIGhlcmUKc3BlYXIgPC0gYygpCmkgPC0gMQp3aGlsZShpPD1ucm93KGNvbWJfcmVzKSl7CiAgc3BlYXJpIDwtIGNvcihhcy5udW1lcmljKGZhY3Rvcihjb21iX3Jlc1tpLDk6MTddKSksYXMubnVtZXJpYyhmYWN0b3IoY29tYl9yZXNbaSwyNDozMl0pKSxtZXRob2Q9InNwZWFybWFuIikKICBzcGVhciA8LSBjKHNwZWFyLCBzcGVhcmkpCiAgaSA8LSBpKzEKfQoKY29tYl9yZXMkU3BlYXJtYW4ucmhvIDwtIHNwZWFyCmBgYAoKU2F2ZSBvdXRwdXQKYGBge3J9CndyaXRlLnRhYmxlKGNvbWJfcmVzLCBmaWxlPSJBTENMLmZpbmFsLnRhYmxlLnR4dCIsIHNlcD0iXHQiLCBxdW90ZT1GLCByb3cubmFtZXM9RikKZFJFR2NvdW50c2FubiA8LSBtZXJnZShQUk9fcmVsLCBkUkVHX3JlYWRGaWx0LCBieS54PSJjbG9zZXN0X3BlYWsiLCBieS55PSJkUkVHcGVha0lEIikKZFJFR2NvdW50c2FubiA8LSBkUkVHY291bnRzYW5uWyxjKDEsMTI6MTgsMjoxMSldCndyaXRlLnRhYmxlKGRSRUdjb3VudHNhbm4sIGZpbGU9IkFMQ0wuZFJFR3MudHh0Iiwgc2VwPSJcdCIsIHF1b3RlPUYsIHJvdy5uYW1lcz1GKQpgYGAKCkNvcnJlY3Qgb3V0cHV0IHRvIGluY2x1ZGUgbWV0YWRhdGEgZm9yIGdlbmVzIHdpdGhvdXQgbmVhcmVzdCBlbmhhbmNlciBjYWxscwpgYGB7cn0KbGlicmFyeShkcGx5cikKCiNyZW1vdmUgY29sdW1ucyB3aXRoIGFubm90YXRpb24gaXNzdWUKY29tYl9yZXNfd29FbmggPC0gY29tYl9yZXMgJT4lIGZpbHRlcihpcy5uYShnZW5lTmFtZSkpCmNvbWJfcmVzX3dFbmggPC0gY29tYl9yZXMgJT4lIGZpbHRlcighaXMubmEoZ2VuZU5hbWUpKQoKI0xvYWQgb3JpZ2luYWwgdW5maWx0ZXJlZCBhbm5vdGF0aW9ucwpjb21wX2d0ZiA8LSByZWFkLnRhYmxlKCJIb21vX3NhcGllbnMuR1JDaDM4Ljk5LnVjc2MuZ3RmIiwgc2VwPSJcdCIpCmNvbG5hbWVzKGNvbXBfZ3RmKSA8LSBjKCJjaHIiLCAic291cmNlIiwgInR5cGUiLCAic3RhcnQiLCAiZW5kIiwgInNjb3JlIiwgInN0cmFuZCIsICJwaGFzZSIsICJhdHRyaWJ1dGVzIikKCiNGdW5jdGlvbiB0byBleHRyYWN0IG1ldGFkYXRhIGZyb20gZ3RmIGFubm90YXRpb24KZXh0cmFjdF9hdHRyaWJ1dGVzIDwtIGZ1bmN0aW9uKGd0Zl9hdHRyaWJ1dGVzLCBhdHRfb2ZfaW50ZXJlc3QpewogIGF0dCA8LSBzdHJzcGxpdChhcy5jaGFyYWN0ZXIoZ3RmX2F0dHJpYnV0ZXMpLCAiOyAiKQogIGF0dCA8LSBnc3ViKCJcIiIsIiIsdW5saXN0KGF0dCkpCiAgYXR0IDwtIGdzdWIoIjsiLCIiLHVubGlzdChhdHQpKQogIGlmKCFpcy5udWxsKHVubGlzdChzdHJzcGxpdChhdHRbZ3JlcChhdHRfb2ZfaW50ZXJlc3QsIGF0dCldLCAiICIpKSkpewogICAgcmV0dXJuKCB1bmxpc3Qoc3Ryc3BsaXQoYXR0W2dyZXAoYXR0X29mX2ludGVyZXN0LCBhdHQpXSwgIiAiKSlbMl0pCiAgfWVsc2V7CiAgICByZXR1cm4oTkEpfQp9CgojRXh0cmFjdCBkZXNpcmVkIGluZm9ybWF0aW9uIGZyb20gZ3RmIChnZW5lSUQsIGdlbmVOYW1lLCBiaW90eXBlKQpjb21wX2dlbmUgPC0gZmlsdGVyKGNvbXBfZ3RmLCB0eXBlID09ICJnZW5lIikKY29tcF9nZW5lJGdlbmVJRCA8LSB1bmxpc3QobGFwcGx5KGNvbXBfZ2VuZSRhdHRyaWJ1dGVzLCBleHRyYWN0X2F0dHJpYnV0ZXMsICJnZW5lX2lkIikpCmNvbXBfZ2VuZSRnZW5lTmFtZSA8LSB1bmxpc3QobGFwcGx5KGNvbXBfZ2VuZSRhdHRyaWJ1dGVzLCBleHRyYWN0X2F0dHJpYnV0ZXMsICJnZW5lX25hbWUiKSkKY29tcF9nZW5lJGJpb3R5cGUgPC0gdW5saXN0KGxhcHBseShjb21wX2dlbmUkYXR0cmlidXRlcywgZXh0cmFjdF9hdHRyaWJ1dGVzLCAiZ2VuZV9iaW90eXBlIikpCgojUmVtb3ZlIG9mZmVuZGluZyBjb2x1bW5zCiNnZW5lTmFtZSxiaW90eXBlLGF0dHJpYnV0ZXMsY2hyLHN0YXJ0LGVuZCxzdHJhbmQKY29tYl9yZXNfd29FbmggPC0gY29tYl9yZXNfd29FbmhbLGMoMSw5OjM0KV0KCiNBZGQgYmFjayBpbmZvcm1hdGlvbgpnZW5lSW5mbyA8LSBkYXRhLmZyYW1lKCJnZW5lSUQiPWNvbXBfZ2VuZSRnZW5lSUQsICJnZW5lTmFtZSI9Y29tcF9nZW5lJGdlbmVOYW1lLCAiYmlvdHlwZSI9Y29tcF9nZW5lJGJpb3R5cGUsICJhdHRyaWJ1dGVzIj1jb21wX2dlbmUkYXR0cmlidXRlcywgImNociI9Y29tcF9nZW5lJGNociwgInN0YXJ0Ij1jb21wX2dlbmUkc3RhcnQsICJlbmQiPWNvbXBfZ2VuZSRlbmQsICJzdHJhbmQiPWNvbXBfZ2VuZSRzdHJhbmQpCmNvbWJfcmVzX3dvRW5oX2NvcnIgPC0gbWVyZ2UoZ2VuZUluZm8sIGNvbWJfcmVzX3dvRW5oLCBieS54PSJnZW5lSUQiLCBieS55PSJnZW5lSUQiLCBhbGwueT1UKQoKI01vZGlmeSBnZW5lTmFtZSBhbmQgcGVha19jb3VudHMKY29tYl9yZXNfd29FbmhfY29yciRwZWFrX2NvdW50IDwtIDAKY29tYl9yZXNfd29FbmhfY29yciRnZW5lTmFtZSA8LSBwYXN0ZTAoY29tYl9yZXNfd29FbmhfY29yciRnZW5lTmFtZSkKCiNDb3JyZWN0aW5nIGdlbmVOYW1lcyBpbiBvcmlnaW5hbApjb21iX3Jlc193RW5oJGdlbmVOYW1lIDwtIHN1YnN0cmluZyhjb21iX3Jlc193RW5oJGdlbmVOYW1lLCAxKQpjb21iX3Jlc193RW5oJGdlbmVOYW1lIDwtIHBhc3RlMChjb21iX3Jlc193RW5oJGdlbmVOYW1lKQoKI0NvbWJpbmluZyBpdCBhbGwgdG9nZXRoZXIKY29tYl9yZXNfY29yciA8LSByYmluZChjb21iX3Jlc193b0VuaF9jb3JyLCBjb21iX3Jlc193RW5oKQoKd3JpdGUudGFibGUoY29tYl9yZXNfY29yciwgZmlsZT0iQUxDTC5jb21iaW5lZC5yZXN1bHRzLmNvcnJlY3RlZC50eHQiLCBzZXA9Ilx0IiwgcXVvdGU9Riwgcm93Lm5hbWVzPUYpCmBgYAoKQ29tYmluaW5nIGZ1bGwgYW5ub3RhdGlvbiB0byBwbG90IGJhc2VkIG9uIGdlbm9taWMgY2F0ZWdvcnkKYGBge3J9CmZpbHRlcmVkX3JlcyA8LSBtZXJnZShkUkVHX2Fubm90LCBjb21iX3Jlc19jb3JyLCBieS54PSJwZWFrSUQiLCBieS55PSJjbG9zZXN0X3BlYWsiLCBhbGwueT1GKQp3cml0ZS50YWJsZShmaWx0ZXJlZF9yZXMsIGZpbGU9Ik92ZXJsYXBpbmcgY29udHJpYnRvcnMudHh0Iiwgc2VwPSdcdCcsIHF1b3RlPUYsIHJvdy5uYW1lcz1GKQpnZ3Bsb3QoZmlsdGVyZWRfcmVzLCBhZXMoeD1mYWN0b3IoMSksIGZpbGw9Z2Vub21pY0NhdGVnb3J5KSkrZ2VvbV9iYXIod2lkdGg9MSkrY29vcmRfcG9sYXIoInkiKSArIGdndGl0bGUoIk92ZXJsYXBwaW5nIFBDQSBDb250cmlidXRvcnMgZm9yIEdlbmUgRXhwcmVzc2lvbiBhbmQgZFJFR3BlYWsgQW5ub3RhdGlvbnMiKQpgYGAKCgpDb21iaW5hdGlvbiBjb21wbGV0ZWQsIGZ1cnRoZXIgaW52ZXN0aWdhdGlvbiBpbnRvIGNsdXN0ZXJzIG9uZ29pbmcgLSAyOEpBTjIy