suppressMessages(library(limma))
suppressMessages(library(cowplot))
suppressMessages(library(data.table))
suppressMessages(library(reshape2))
suppressMessages(library(clusterProfiler))
suppressMessages(library(org.Hs.eg.db))
cbPalette <- c("#999999", "#E69F00", "#56B4E9", "#009E73",
"#F0E442", "#0072B2", "#D55E00", "#CC79A7")
Utility Functions
plotIntensitiesForCohort <- function(object, title, foreground=TRUE, takeLog=TRUE){
disease <- grepl(".*CAC.*", colnames(object))
control <- grepl(".*HCpool*", colnames(object))
values.disease <- object[, disease]
values.control <- object[, control]
if (foreground){
log2RowMeans.disease <- log2(rowMeans(as.data.frame(values.disease$E, row.names=0)))
log2RowMeans.control <- log2(rowMeans(as.data.frame(values.control$E, row.names=0)))
}
else{
log2RowMeans.disease <- log2(rowMeans(as.data.frame(values.disease$Eb, row.names=0)))
log2RowMeans.control <- log2(rowMeans(as.data.frame(values.control$Eb, row.names=0)))
}
k <- list(log2RowMeans.disease, log2RowMeans.control)
grp <- c(rep("Cancer", length(disease)),
rep("Control", length(control)))
df <- data.frame(x=unlist(k), grp=grp)
if (takeLog){
g = ggplot(df,aes(x = grp, y = log2(x) )) +
geom_boxplot(aes(fill=factor(grp))) +
scale_fill_manual(breaks = c("Cancer", "Control"),
labels = c("Cancer", "Control"),
values = cbPalette[6:7]) +
xlab("Cohort") +
ylab("log2 foreground intensities") +
ggtitle(title)
return (g)
}
if (takeLog == FALSE){
g = ggplot(df,aes(x = grp, y = x)) +
geom_boxplot(aes(fill=factor(grp))) +
scale_fill_manual(breaks = c("Cancer", "Control"),
labels = c("Cancer", "Control"),
values = cbPalette[6:7]) +
xlab("Cohort") +
ylab("log2 foreground intensities") +
ggtitle(title)
return (g)
}
}
log2TransformIntensities <- function(E)
{
return (log2(rowMeans(as.data.frame(E, row.names=0))))
}
'%nin%' <- Negate('%in%')
palette12 <- c('#a6cee3',
'#1f78b4','#b2df8a',
'#33a02c', '#fb9a99','#e31a1c',
'#fdbf6f', '#ff7f00', '#cab2d6', '#6a3d9a', '#ffff99', '#b15928')
plotIntensitiesForCohortByProbe <- function(object, title){
disease <- grepl(".*CAC.*", colnames(object))
control <- grepl(".*HCpool*", colnames(object))
disease <- object[, disease]
control <- object[, control]
controlProbes <- object$genes$ID %in% c('Control')
bufferProbes <- object$genes$Name %in% c('buffer', 'Buffer')
## Some control probes seem to be labelled as 'Buffer' in name column
## so we will treat them as buffer only
controlProbes <- setdiff(controlProbes, bufferProbes)
otherProbes <- object$genes$ID %nin% c('Control') & object$genes$Name %nin% c('buffer', 'Buffer')
disease.E <- disease$E
control.E <- control$E
disease.Eb <- disease$Eb
control.Eb <- control$Eb
disease.E.control <- disease.E[controlProbes,]
control.E.control <- control.E[controlProbes,]
disease.E.buffer <- disease.E[bufferProbes,]
control.E.buffer <- control.E[bufferProbes,]
disease.E.others <- disease.E[controlProbes,]
control.E.others <- control.E[controlProbes,]
disease.Eb.control <- disease.Eb[controlProbes,]
control.Eb.control <- control.Eb[controlProbes,]
disease.Eb.buffer <- disease.Eb[bufferProbes,]
control.Eb.buffer <- control.Eb[bufferProbes,]
disease.Eb.others <- disease.Eb[controlProbes,]
control.Eb.others <- control.Eb[controlProbes,]
k <- list(log2TransformIntensities(disease.E.control),
log2TransformIntensities(control.E.control),
log2TransformIntensities(disease.E.buffer),
log2TransformIntensities(control.E.buffer),
#log2TransformIntensities(disease.E.others),
#log2TransformIntensities(control.E.others),
log2TransformIntensities(disease.Eb.control),
log2TransformIntensities(control.Eb.control),
log2TransformIntensities(disease.Eb.buffer),
log2TransformIntensities(control.Eb.buffer)#,
#log2TransformIntensities(disease.Eb.others),
#log2TransformIntensities(control.Eb.others)
)
grp <- c(rep("Disease-Foreground-ControlProbes", length(disease)),
rep("Control-Foreground-ControlProbes", length(control)),
rep("Disease-Foreground-BufferProbes", length(disease)),
rep("Control-Foreground-BufferProbes", length(control)),
#rep("Disease-Foreground-OtherProbes", length(disease)),
#rep("Control-Foreground-OtherProbes", length(control)),
rep("Disease-Background-ControlProbes", length(disease)),
rep("Control-Background-ControlProbes", length(control)),
rep("Disease-Background-BufferProbes", length(disease)),
rep("Control-Background-BufferProbes", length(control))#,
#rep("Disease-Background-OtherProbes", length(disease)),
#rep("Control-Background-OtherProbes", length(control))
)
df <- data.frame(x=unlist(k), grp=grp)
g = ggplot(df,aes(x = grp, y = log2(x) )) +
geom_boxplot(aes(fill=factor(grp))) +
scale_fill_manual(breaks = c("Disease-Foreground-ControlProbes",
"Control-Foreground-ControlProbes",
"Disease-Foreground-BufferProbes",
"Control-Foreground-BufferProbes",
#"Disease-Foreground-OtherProbes",
#"Control-Foreground-OtherProbes",
"Disease-Background-ControlProbes",
"Control-Background-ControlProbes",
"Disease-Background-BufferProbes",
"Control-Background-BufferProbes"),
#"Disease-Background-OtherProbes",
#"Control-Background-OtherProbes"),
labels =c("Disease-Foreground-ControlProbes",
"Control-Foreground-ControlProbes",
"Disease-Foreground-BufferProbes",
"Control-Foreground-BufferProbes",
#"Disease-Foreground-OtherProbes",
#"Control-Foreground-OtherProbes",
"Disease-Background-ControlProbes",
"Control-Background-ControlProbes",
"Disease-Background-BufferProbes",
"Control-Background-BufferProbes"),
#"Disease-Background-OtherProbes",
#"Control-Background-OtherProbes"),
values = palette12) +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
xlab("Cohort") +
ylab("log2 foreground intensities") +
ggtitle(title)
return (g)
}
plotForegroundIntensities <- function(object, title){
neg <- as.data.frame(object$E)
g = ggplot(data = melt(neg), aes(x=variable, y=log2(value)) ) + geom_boxplot(aes(fill=variable)) +xlab("Samples 1-85") + ylab("log2 foreground") + ggtitle(title)
ggsave(filename=paste(outputDirectory,title,".png", sep=""), plot=g)
}
plotControls <- function(object, title){
neg <- as.data.frame(object$E)
g = ggplot(data = melt(neg), aes(x=variable, y=log2(value)) ) + geom_boxplot(aes(fill=variable)) +xlab("Samples 1-85") + ylab("log2 foreground") + ggtitle(title)
ggsave(filename=paste(outputDirectory,title,".png", sep=""), plot=g)
}
get_entrez <- function(x){
bitr(x, fromType = 'SYMBOL', toType = 'ENTREZID', OrgDb = org.Hs.eg.db)$ENTREZID
}
get_name <- function(x){
bitr(x, fromType = 'ENTREZID', toType = 'SYMBOL', OrgDb = org.Hs.eg.db)$SYMBOL
}
writeEKG <- function(ekg, prefix){
ekg <- as.data.frame(ekg)
ekg.geneID <- strsplit(as.character(ekg$geneID), '/', fixed=TRUE)
ekg.genes <- lapply(ekg.geneID, get_name)
ekg$geneID <- ekg.genes
ekg$geneID <- vapply(ekg$geneID , paste, collapse = ", ", character(1L))
write.table(ekg, file.path('..','results', paste(prefix, 'tsv', sep='.')))
}
writeego <- function(ego, prefix){
ego <- as.data.frame(ego)
ego.geneID <- strsplit(as.character(ego$geneID), '/', fixed=TRUE)
ego.genes <- lapply(ego.geneID, get_name)
ego$geneID <- ego.genes
ego$geneID <- vapply(ego$geneID , paste, collapse = ", ", character(1L))
write.table(ego, file.path('..','results', paste(prefix, 'tsv', sep='.')))
}
Targets
createShortName <- function (longFileName){
splitted <- strsplit(longFileName, split = '_')
return (paste(splitted[3], splitted[4], sep = '_'))
}
limmaCy5 <- 'F635 Median'
limmaCy5b <- 'B635 Median'
targetFile <- file.path('..', 'design_file.tsv')
targetDir <- file.path('..', 'data_new')
targets <- fread(targetFile)
targets$shortName <- lapply(targets$FileName, createShortName)
RG <- read.maimages( targets$FileName,
source="genepix.custom",
green.only=TRUE,
path = targetDir,
columns=list(G=limmaCy5, Gb=limmaCy5b),
verbose = FALSE)
RG$E <- RG$E[with(RG$genes, order(Block, Column, Row)), ]
RG$genes <- RG$genes[with(RG$genes, order(Block, Column, Row)), ]
Foreground Intensity Distribution
plotIntensitiesForCohort(RG, 'Foreground intensities')

Background Intensity Distribution
plotIntensitiesForCohort(RG, 'Background intensities', FALSE)

Are Controls/Buffer spots different from each other or the regular probes?
plotIntensitiesForCohortByProbe(RG, 'Probewise intensities')

Background correction and normalization
controlProbes <- RG$genes$ID %in% c('Control')
bufferProbes <- RG$genes$Name %in% c('buffer', 'Buffer')
## Some control probes seem to be labelled as 'Buffer' in name column
## so we will treat them as buffer only
controlProbes <- setdiff(controlProbes, bufferProbes)
otherProbes <- RG$genes$ID %nin% c('Control') & RG$genes$Name %nin% c('buffer', 'Buffer')
status <- rep(NULL, length(RG$genes$ID))
status[controlProbes] <- 'Control'
status[bufferProbes] <- 'Buffer'
status[otherProbes] <- 'Regular'
## Neqc normalization using buffer genes as control
RG.neqc <- neqc(RG, status=status, negctrl = 'Buffer', regular='Regular', offset=50, robust = TRUE)
Correlation between duplicate spots
f <- factor(paste(targets$Condition, targets$Sex, sep="."))
design <- model.matrix(~0+f)
colnames(design) <- levels(f)
corfit <- duplicateCorrelation(RG.neqc, design, ndups=2)#, spacing = 1)
corfit$consensus.correlation
[1] 0.8019561
boxplot(tanh(corfit$atanh.correlations))

Calling DE genes
cont.matrix <- makeContrasts(CancervsHealthy=((cancer.female + cancer.male)/2-healthy.pooled),
levels=design)
fit <- lmFit(RG.neqc,
design, ndups=2,
correlation=corfit$consensus)
fit <- contrasts.fit(fit, cont.matrix)
fit <- eBayes(fit)
top <- topTable(fit, number = length(RG.neqc$genes$ID))
top.sig <- subset(top, adj.P.Val < 0.05)
write.csv(top, '../results/cancer_vs_healthy_neqc.all.csv', row.names = F)
write.csv(top.sig, '../results/cancer_vs_healthy_neqc.significant.csv', row.names = F)
GO enrichment of significant DE genes
genes <- top.sig$Name
genes.entrez <- get_entrez(genes)
ekg <- enrichGO(gene = genes.entrez,
OrgDb = org.Hs.eg.db,
pAdjustMethod = 'BH',
pvalueCutoff = 0.2,
qvalueCutoff = 0.2)
barplot(ekg)

writeego(ekg, 'cancer_vs_healthy_neqc.significant.GO')
KEGG enrichment of significant DE genes
genes <- top.sig$Name
genes.entrez <- get_entrez(genes)
ekg <- enrichKEGG(gene = genes.entrez,
organism = 'hsa',
pAdjustMethod = 'BH',
pvalueCutoff = 0.2,
qvalueCutoff = 0.2)
barplot(ekg, showCategory = 15)

writeEKG(ekg, 'cancer_vs_healthy_neqc.significant.KEGG')
LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgQ29sb24gQ2FuY2VyIE1pY3JvYXJyYXkgRGF0YXNldHMiCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6IFNha2V0IENob3VkaGFyeSA8c2FrZXRrY0BnbWFpbC5jb20+CmRhdGU6IDEyLzExLzIwMTcKLS0tCgpgYGB7cn0Kc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGxpbW1hKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGNvd3Bsb3QpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZGF0YS50YWJsZSkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShyZXNoYXBlMikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShjbHVzdGVyUHJvZmlsZXIpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkob3JnLkhzLmVnLmRiKSkKCmNiUGFsZXR0ZSA8LSBjKCIjOTk5OTk5IiwgIiNFNjlGMDAiLCAiIzU2QjRFOSIsICIjMDA5RTczIiwgCiAgICAgICAgICAgICAgICIjRjBFNDQyIiwgIiMwMDcyQjIiLCAiI0Q1NUUwMCIsICIjQ0M3OUE3IikKCmBgYAoKIyBVdGlsaXR5IEZ1bmN0aW9ucwpgYGB7cn0KcGxvdEludGVuc2l0aWVzRm9yQ29ob3J0IDwtIGZ1bmN0aW9uKG9iamVjdCwgdGl0bGUsIGZvcmVncm91bmQ9VFJVRSwgdGFrZUxvZz1UUlVFKXsKICAgIGRpc2Vhc2UgPC0gZ3JlcGwoIi4qQ0FDLioiLCBjb2xuYW1lcyhvYmplY3QpKSAKICAgIGNvbnRyb2wgPC0gZ3JlcGwoIi4qSENwb29sKiIsIGNvbG5hbWVzKG9iamVjdCkpIAogICAgCiAgICB2YWx1ZXMuZGlzZWFzZSA8LSBvYmplY3RbLCBkaXNlYXNlXQogICAgdmFsdWVzLmNvbnRyb2wgPC0gb2JqZWN0WywgY29udHJvbF0KICAgIAogICAgaWYgKGZvcmVncm91bmQpewogICAgICAgIGxvZzJSb3dNZWFucy5kaXNlYXNlIDwtIGxvZzIocm93TWVhbnMoYXMuZGF0YS5mcmFtZSh2YWx1ZXMuZGlzZWFzZSRFLCByb3cubmFtZXM9MCkpKQogICAgICAgIGxvZzJSb3dNZWFucy5jb250cm9sIDwtIGxvZzIocm93TWVhbnMoYXMuZGF0YS5mcmFtZSh2YWx1ZXMuY29udHJvbCRFLCByb3cubmFtZXM9MCkpKQogIAogICAgfQogICAgZWxzZXsKICAgICAgICBsb2cyUm93TWVhbnMuZGlzZWFzZSA8LSBsb2cyKHJvd01lYW5zKGFzLmRhdGEuZnJhbWUodmFsdWVzLmRpc2Vhc2UkRWIsIHJvdy5uYW1lcz0wKSkpCiAgICAgICAgbG9nMlJvd01lYW5zLmNvbnRyb2wgPC0gbG9nMihyb3dNZWFucyhhcy5kYXRhLmZyYW1lKHZhbHVlcy5jb250cm9sJEViLCByb3cubmFtZXM9MCkpKQogICAgfQogICAgCiAgICBrIDwtIGxpc3QobG9nMlJvd01lYW5zLmRpc2Vhc2UsIGxvZzJSb3dNZWFucy5jb250cm9sKQogIAogICAgZ3JwIDwtIGMocmVwKCJDYW5jZXIiLCBsZW5ndGgoZGlzZWFzZSkpLAogICAgICAgICAgICAgcmVwKCJDb250cm9sIiwgbGVuZ3RoKGNvbnRyb2wpKSkKICAKICAgIGRmIDwtIGRhdGEuZnJhbWUoeD11bmxpc3QoayksIGdycD1ncnApCiAgICBpZiAodGFrZUxvZyl7CiAgICAgICAgZyA9IGdncGxvdChkZixhZXMoeCA9IGdycCwgeSA9IGxvZzIoeCkgKSkgKwogICAgICAgICAgICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9ZmFjdG9yKGdycCkpKSArIAogICAgICAgICAgICBzY2FsZV9maWxsX21hbnVhbChicmVha3MgPSBjKCJDYW5jZXIiLCAiQ29udHJvbCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkNhbmNlciIsICJDb250cm9sIiksICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGNiUGFsZXR0ZVs2OjddKSArIAogICAgICAgICAgICB4bGFiKCJDb2hvcnQiKSArIAogICAgICAgICAgICB5bGFiKCJsb2cyIGZvcmVncm91bmQgaW50ZW5zaXRpZXMiKSArIAogICAgICAgICAgICBnZ3RpdGxlKHRpdGxlKQogICAgICAgIHJldHVybiAoZykKICAgICAgICB9CiAgICBpZiAodGFrZUxvZyA9PSBGQUxTRSl7CiAgICAgICAgZyA9IGdncGxvdChkZixhZXMoeCA9IGdycCwgeSA9IHgpKSArIAogICAgICAgICAgICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9ZmFjdG9yKGdycCkpKSArCiAgICAgICAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKGJyZWFrcyA9IGMoIkNhbmNlciIsICJDb250cm9sIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiQ2FuY2VyIiwgIkNvbnRyb2wiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjYlBhbGV0dGVbNjo3XSkgKyAKICAgICAgICAgICAgeGxhYigiQ29ob3J0IikgKwogICAgICAgICAgICB5bGFiKCJsb2cyIGZvcmVncm91bmQgaW50ZW5zaXRpZXMiKSArCiAgICAgICAgICAgIGdndGl0bGUodGl0bGUpCiAgICAgICAgcmV0dXJuIChnKQogICAgICB9Cn0KbG9nMlRyYW5zZm9ybUludGVuc2l0aWVzIDwtIGZ1bmN0aW9uKEUpCnsKICByZXR1cm4gKGxvZzIocm93TWVhbnMoYXMuZGF0YS5mcmFtZShFLCByb3cubmFtZXM9MCkpKSkgCn0KCiclbmluJScgPC0gTmVnYXRlKCclaW4lJykKcGFsZXR0ZTEyIDwtIGMoJyNhNmNlZTMnLCAKICAgICAgICAgICAgICAgJyMxZjc4YjQnLCcjYjJkZjhhJywKICAgICAgICAgICAgICAgJyMzM2EwMmMnLCAnI2ZiOWE5OScsJyNlMzFhMWMnLCAKICAgICAgICAgICAgICAgJyNmZGJmNmYnLCAnI2ZmN2YwMCcsICcjY2FiMmQ2JywgJyM2YTNkOWEnLCAnI2ZmZmY5OScsICcjYjE1OTI4JykKCnBsb3RJbnRlbnNpdGllc0ZvckNvaG9ydEJ5UHJvYmUgPC0gZnVuY3Rpb24ob2JqZWN0LCB0aXRsZSl7CiAgZGlzZWFzZSA8LSBncmVwbCgiLipDQUMuKiIsIGNvbG5hbWVzKG9iamVjdCkpIAogIGNvbnRyb2wgPC0gZ3JlcGwoIi4qSENwb29sKiIsIGNvbG5hbWVzKG9iamVjdCkpIAogIAogIGRpc2Vhc2UgPC0gb2JqZWN0WywgZGlzZWFzZV0KICBjb250cm9sIDwtIG9iamVjdFssIGNvbnRyb2xdCiAgCiAgY29udHJvbFByb2JlcyA8LSBvYmplY3QkZ2VuZXMkSUQgJWluJSBjKCdDb250cm9sJykKICBidWZmZXJQcm9iZXMgPC0gb2JqZWN0JGdlbmVzJE5hbWUgJWluJSBjKCdidWZmZXInLCAnQnVmZmVyJykKICAKICAKICAjIyBTb21lIGNvbnRyb2wgcHJvYmVzIHNlZW0gdG8gYmUgbGFiZWxsZWQgYXMgJ0J1ZmZlcicgaW4gbmFtZSBjb2x1bW4KICAjIyBzbyB3ZSB3aWxsIHRyZWF0IHRoZW0gYXMgYnVmZmVyIG9ubHkKICAKICBjb250cm9sUHJvYmVzIDwtIHNldGRpZmYoY29udHJvbFByb2JlcywgYnVmZmVyUHJvYmVzKQogIG90aGVyUHJvYmVzIDwtICBvYmplY3QkZ2VuZXMkSUQgJW5pbiUgYygnQ29udHJvbCcpICYgb2JqZWN0JGdlbmVzJE5hbWUgJW5pbiUgYygnYnVmZmVyJywgJ0J1ZmZlcicpCiAgCiAgCiAgZGlzZWFzZS5FIDwtIGRpc2Vhc2UkRQogIGNvbnRyb2wuRSA8LSBjb250cm9sJEUKICAKICBkaXNlYXNlLkViIDwtIGRpc2Vhc2UkRWIKICBjb250cm9sLkViIDwtIGNvbnRyb2wkRWIKICAKICBkaXNlYXNlLkUuY29udHJvbCA8LSBkaXNlYXNlLkVbY29udHJvbFByb2JlcyxdCiAgY29udHJvbC5FLmNvbnRyb2wgPC0gY29udHJvbC5FW2NvbnRyb2xQcm9iZXMsXQogIGRpc2Vhc2UuRS5idWZmZXIgPC0gZGlzZWFzZS5FW2J1ZmZlclByb2JlcyxdCiAgY29udHJvbC5FLmJ1ZmZlciA8LSBjb250cm9sLkVbYnVmZmVyUHJvYmVzLF0KICBkaXNlYXNlLkUub3RoZXJzIDwtIGRpc2Vhc2UuRVtjb250cm9sUHJvYmVzLF0KICBjb250cm9sLkUub3RoZXJzIDwtIGNvbnRyb2wuRVtjb250cm9sUHJvYmVzLF0KICAKICAKICAKICBkaXNlYXNlLkViLmNvbnRyb2wgPC0gZGlzZWFzZS5FYltjb250cm9sUHJvYmVzLF0KICBjb250cm9sLkViLmNvbnRyb2wgPC0gY29udHJvbC5FYltjb250cm9sUHJvYmVzLF0KICBkaXNlYXNlLkViLmJ1ZmZlciA8LSBkaXNlYXNlLkViW2J1ZmZlclByb2JlcyxdCiAgY29udHJvbC5FYi5idWZmZXIgPC0gY29udHJvbC5FYltidWZmZXJQcm9iZXMsXQogIGRpc2Vhc2UuRWIub3RoZXJzIDwtIGRpc2Vhc2UuRWJbY29udHJvbFByb2JlcyxdCiAgY29udHJvbC5FYi5vdGhlcnMgPC0gY29udHJvbC5FYltjb250cm9sUHJvYmVzLF0KICAKICAKICBrIDwtIGxpc3QobG9nMlRyYW5zZm9ybUludGVuc2l0aWVzKGRpc2Vhc2UuRS5jb250cm9sKSwKICAgICAgICAgICAgbG9nMlRyYW5zZm9ybUludGVuc2l0aWVzKGNvbnRyb2wuRS5jb250cm9sKSwKICAgICAgICAgICAgbG9nMlRyYW5zZm9ybUludGVuc2l0aWVzKGRpc2Vhc2UuRS5idWZmZXIpLAogICAgICAgICAgICBsb2cyVHJhbnNmb3JtSW50ZW5zaXRpZXMoY29udHJvbC5FLmJ1ZmZlciksCiAgICAgICAgICAgICNsb2cyVHJhbnNmb3JtSW50ZW5zaXRpZXMoZGlzZWFzZS5FLm90aGVycyksCiAgICAgICAgICAgICNsb2cyVHJhbnNmb3JtSW50ZW5zaXRpZXMoY29udHJvbC5FLm90aGVycyksCiAgICAgICAgICAgIGxvZzJUcmFuc2Zvcm1JbnRlbnNpdGllcyhkaXNlYXNlLkViLmNvbnRyb2wpLAogICAgICAgICAgICBsb2cyVHJhbnNmb3JtSW50ZW5zaXRpZXMoY29udHJvbC5FYi5jb250cm9sKSwKICAgICAgICAgICAgbG9nMlRyYW5zZm9ybUludGVuc2l0aWVzKGRpc2Vhc2UuRWIuYnVmZmVyKSwKICAgICAgICAgICAgbG9nMlRyYW5zZm9ybUludGVuc2l0aWVzKGNvbnRyb2wuRWIuYnVmZmVyKSMsCiAgICAgICAgICAgICNsb2cyVHJhbnNmb3JtSW50ZW5zaXRpZXMoZGlzZWFzZS5FYi5vdGhlcnMpLAogICAgICAgICAgICAjbG9nMlRyYW5zZm9ybUludGVuc2l0aWVzKGNvbnRyb2wuRWIub3RoZXJzKQogICAgICAgICAgICApCiAgCiAgZ3JwIDwtIGMocmVwKCJEaXNlYXNlLUZvcmVncm91bmQtQ29udHJvbFByb2JlcyIsIGxlbmd0aChkaXNlYXNlKSksCiAgICAgICAgICAgcmVwKCJDb250cm9sLUZvcmVncm91bmQtQ29udHJvbFByb2JlcyIsIGxlbmd0aChjb250cm9sKSksCiAgICAgICAgICAgcmVwKCJEaXNlYXNlLUZvcmVncm91bmQtQnVmZmVyUHJvYmVzIiwgbGVuZ3RoKGRpc2Vhc2UpKSwKICAgICAgICAgICByZXAoIkNvbnRyb2wtRm9yZWdyb3VuZC1CdWZmZXJQcm9iZXMiLCBsZW5ndGgoY29udHJvbCkpLAogICAgICAgICAgICNyZXAoIkRpc2Vhc2UtRm9yZWdyb3VuZC1PdGhlclByb2JlcyIsIGxlbmd0aChkaXNlYXNlKSksCiAgICAgICAgICAgI3JlcCgiQ29udHJvbC1Gb3JlZ3JvdW5kLU90aGVyUHJvYmVzIiwgbGVuZ3RoKGNvbnRyb2wpKSwKICAgICAgICAgICAKICAgICAgICAgICByZXAoIkRpc2Vhc2UtQmFja2dyb3VuZC1Db250cm9sUHJvYmVzIiwgbGVuZ3RoKGRpc2Vhc2UpKSwKICAgICAgICAgICByZXAoIkNvbnRyb2wtQmFja2dyb3VuZC1Db250cm9sUHJvYmVzIiwgbGVuZ3RoKGNvbnRyb2wpKSwKICAgICAgICAgICByZXAoIkRpc2Vhc2UtQmFja2dyb3VuZC1CdWZmZXJQcm9iZXMiLCBsZW5ndGgoZGlzZWFzZSkpLAogICAgICAgICAgIHJlcCgiQ29udHJvbC1CYWNrZ3JvdW5kLUJ1ZmZlclByb2JlcyIsIGxlbmd0aChjb250cm9sKSkjLAogICAgICAgICAgICNyZXAoIkRpc2Vhc2UtQmFja2dyb3VuZC1PdGhlclByb2JlcyIsIGxlbmd0aChkaXNlYXNlKSksCiAgICAgICAgICAgI3JlcCgiQ29udHJvbC1CYWNrZ3JvdW5kLU90aGVyUHJvYmVzIiwgbGVuZ3RoKGNvbnRyb2wpKQogICAgICAgICAgICkKICAKICBkZiA8LSBkYXRhLmZyYW1lKHg9dW5saXN0KGspLCBncnA9Z3JwKQogIGcgPSBnZ3Bsb3QoZGYsYWVzKHggPSBncnAsIHkgPSBsb2cyKHgpICkpICsKICAgIGdlb21fYm94cGxvdChhZXMoZmlsbD1mYWN0b3IoZ3JwKSkpICsgCiAgICBzY2FsZV9maWxsX21hbnVhbChicmVha3MgPSBjKCJEaXNlYXNlLUZvcmVncm91bmQtQ29udHJvbFByb2JlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDb250cm9sLUZvcmVncm91bmQtQ29udHJvbFByb2JlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEaXNlYXNlLUZvcmVncm91bmQtQnVmZmVyUHJvYmVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvbnRyb2wtRm9yZWdyb3VuZC1CdWZmZXJQcm9iZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIkRpc2Vhc2UtRm9yZWdyb3VuZC1PdGhlclByb2JlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMiQ29udHJvbC1Gb3JlZ3JvdW5kLU90aGVyUHJvYmVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRpc2Vhc2UtQmFja2dyb3VuZC1Db250cm9sUHJvYmVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvbnRyb2wtQmFja2dyb3VuZC1Db250cm9sUHJvYmVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRpc2Vhc2UtQmFja2dyb3VuZC1CdWZmZXJQcm9iZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29udHJvbC1CYWNrZ3JvdW5kLUJ1ZmZlclByb2JlcyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIkRpc2Vhc2UtQmFja2dyb3VuZC1PdGhlclByb2JlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMiQ29udHJvbC1CYWNrZ3JvdW5kLU90aGVyUHJvYmVzIiksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPWMoIkRpc2Vhc2UtRm9yZWdyb3VuZC1Db250cm9sUHJvYmVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvbnRyb2wtRm9yZWdyb3VuZC1Db250cm9sUHJvYmVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRpc2Vhc2UtRm9yZWdyb3VuZC1CdWZmZXJQcm9iZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29udHJvbC1Gb3JlZ3JvdW5kLUJ1ZmZlclByb2JlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMiRGlzZWFzZS1Gb3JlZ3JvdW5kLU90aGVyUHJvYmVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyJDb250cm9sLUZvcmVncm91bmQtT3RoZXJQcm9iZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGlzZWFzZS1CYWNrZ3JvdW5kLUNvbnRyb2xQcm9iZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29udHJvbC1CYWNrZ3JvdW5kLUNvbnRyb2xQcm9iZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGlzZWFzZS1CYWNrZ3JvdW5kLUJ1ZmZlclByb2JlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDb250cm9sLUJhY2tncm91bmQtQnVmZmVyUHJvYmVzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMiRGlzZWFzZS1CYWNrZ3JvdW5kLU90aGVyUHJvYmVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyJDb250cm9sLUJhY2tncm91bmQtT3RoZXJQcm9iZXMiKSwKICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IHBhbGV0dGUxMikgKyAKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsKICAgIHhsYWIoIkNvaG9ydCIpICsgCiAgICB5bGFiKCJsb2cyIGZvcmVncm91bmQgaW50ZW5zaXRpZXMiKSArIAogICAgZ2d0aXRsZSh0aXRsZSkKICByZXR1cm4gKGcpCiAgCn0KCgpwbG90Rm9yZWdyb3VuZEludGVuc2l0aWVzIDwtIGZ1bmN0aW9uKG9iamVjdCwgdGl0bGUpewogIG5lZyA8LSBhcy5kYXRhLmZyYW1lKG9iamVjdCRFKQogIAogIGcgPSBnZ3Bsb3QoZGF0YSA9IG1lbHQobmVnKSwgYWVzKHg9dmFyaWFibGUsIHk9bG9nMih2YWx1ZSkpICkgKyBnZW9tX2JveHBsb3QoYWVzKGZpbGw9dmFyaWFibGUpKSAreGxhYigiU2FtcGxlcyAxLTg1IikgKyB5bGFiKCJsb2cyIGZvcmVncm91bmQiKSArIGdndGl0bGUodGl0bGUpCiAgZ2dzYXZlKGZpbGVuYW1lPXBhc3RlKG91dHB1dERpcmVjdG9yeSx0aXRsZSwiLnBuZyIsIHNlcD0iIiksIHBsb3Q9ZykKfQoKcGxvdENvbnRyb2xzIDwtIGZ1bmN0aW9uKG9iamVjdCwgdGl0bGUpewogIG5lZyA8LSBhcy5kYXRhLmZyYW1lKG9iamVjdCRFKQogIAogIGcgPSBnZ3Bsb3QoZGF0YSA9IG1lbHQobmVnKSwgYWVzKHg9dmFyaWFibGUsIHk9bG9nMih2YWx1ZSkpICkgKyBnZW9tX2JveHBsb3QoYWVzKGZpbGw9dmFyaWFibGUpKSAreGxhYigiU2FtcGxlcyAxLTg1IikgKyB5bGFiKCJsb2cyIGZvcmVncm91bmQiKSArIGdndGl0bGUodGl0bGUpCiAgZ2dzYXZlKGZpbGVuYW1lPXBhc3RlKG91dHB1dERpcmVjdG9yeSx0aXRsZSwiLnBuZyIsIHNlcD0iIiksIHBsb3Q9ZykKfQpgYGAKCgpgYGB7cn0KZ2V0X2VudHJleiA8LSBmdW5jdGlvbih4KXsKICAgYml0cih4LCBmcm9tVHlwZSA9ICdTWU1CT0wnLCB0b1R5cGUgPSAnRU5UUkVaSUQnLCBPcmdEYiA9IG9yZy5Icy5lZy5kYikkRU5UUkVaSUQKfQoKZ2V0X25hbWUgPC0gZnVuY3Rpb24oeCl7CiAgIGJpdHIoeCwgZnJvbVR5cGUgPSAnRU5UUkVaSUQnLCB0b1R5cGUgPSAnU1lNQk9MJywgT3JnRGIgPSBvcmcuSHMuZWcuZGIpJFNZTUJPTAp9Cgp3cml0ZUVLRyA8LSBmdW5jdGlvbihla2csIHByZWZpeCl7CiAgZWtnIDwtIGFzLmRhdGEuZnJhbWUoZWtnKQogIGVrZy5nZW5lSUQgPC0gc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKGVrZyRnZW5lSUQpLCAnLycsIGZpeGVkPVRSVUUpCiAgZWtnLmdlbmVzIDwtIGxhcHBseShla2cuZ2VuZUlELCBnZXRfbmFtZSkKICBla2ckZ2VuZUlEIDwtIGVrZy5nZW5lcwogIGVrZyRnZW5lSUQgPC0gdmFwcGx5KGVrZyRnZW5lSUQgLCBwYXN0ZSwgY29sbGFwc2UgPSAiLCAiLCBjaGFyYWN0ZXIoMUwpKQogIHdyaXRlLnRhYmxlKGVrZywgZmlsZS5wYXRoKCcuLicsJ3Jlc3VsdHMnLCBwYXN0ZShwcmVmaXgsICd0c3YnLCBzZXA9Jy4nKSkpCn0KCgoKd3JpdGVlZ28gPC0gZnVuY3Rpb24oZWdvLCBwcmVmaXgpewogIGVnbyA8LSBhcy5kYXRhLmZyYW1lKGVnbykKICBlZ28uZ2VuZUlEIDwtIHN0cnNwbGl0KGFzLmNoYXJhY3RlcihlZ28kZ2VuZUlEKSwgJy8nLCBmaXhlZD1UUlVFKQogIGVnby5nZW5lcyA8LSBsYXBwbHkoZWdvLmdlbmVJRCwgZ2V0X25hbWUpCiAgZWdvJGdlbmVJRCA8LSBlZ28uZ2VuZXMKICBlZ28kZ2VuZUlEIDwtIHZhcHBseShlZ28kZ2VuZUlEICwgcGFzdGUsIGNvbGxhcHNlID0gIiwgIiwgY2hhcmFjdGVyKDFMKSkKICB3cml0ZS50YWJsZShlZ28sIGZpbGUucGF0aCgnLi4nLCdyZXN1bHRzJywgcGFzdGUocHJlZml4LCAndHN2Jywgc2VwPScuJykpKQp9CmBgYAoKIyBUYXJnZXRzCmBgYHtyfQpjcmVhdGVTaG9ydE5hbWUgPC0gZnVuY3Rpb24gIChsb25nRmlsZU5hbWUpewogICAgc3BsaXR0ZWQgPC0gc3Ryc3BsaXQobG9uZ0ZpbGVOYW1lLCBzcGxpdCA9ICdfJykKICAgIHJldHVybiAocGFzdGUoc3BsaXR0ZWRbM10sIHNwbGl0dGVkWzRdLCBzZXAgPSAnXycpKQp9CmxpbW1hQ3k1IDwtICdGNjM1IE1lZGlhbicKbGltbWFDeTViIDwtICdCNjM1IE1lZGlhbicKdGFyZ2V0RmlsZSA8LSBmaWxlLnBhdGgoJy4uJywgJ2Rlc2lnbl9maWxlLnRzdicpCnRhcmdldERpciA8LSBmaWxlLnBhdGgoJy4uJywgJ2RhdGFfbmV3JykKCnRhcmdldHMgPC0gZnJlYWQodGFyZ2V0RmlsZSkKdGFyZ2V0cyRzaG9ydE5hbWUgPC0gbGFwcGx5KHRhcmdldHMkRmlsZU5hbWUsIGNyZWF0ZVNob3J0TmFtZSkKUkcgPC0gcmVhZC5tYWltYWdlcyggdGFyZ2V0cyRGaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgICAgc291cmNlPSJnZW5lcGl4LmN1c3RvbSIsIAogICAgICAgICAgICAgICAgICAgICBncmVlbi5vbmx5PVRSVUUsCiAgICAgICAgICAgICAgICAgICAgIHBhdGggPSB0YXJnZXREaXIsCiAgICAgICAgICAgICAgICAgICAgIGNvbHVtbnM9bGlzdChHPWxpbW1hQ3k1LCBHYj1saW1tYUN5NWIpLAogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpClJHJEUgPC0gUkckRVt3aXRoKFJHJGdlbmVzLCBvcmRlcihCbG9jaywgQ29sdW1uLCBSb3cpKSwgXQpSRyRnZW5lcyA8LSBSRyRnZW5lc1t3aXRoKFJHJGdlbmVzLCBvcmRlcihCbG9jaywgQ29sdW1uLCBSb3cpKSwgXQoKYGBgCgojIEZvcmVncm91bmQgSW50ZW5zaXR5IERpc3RyaWJ1dGlvbgoKYGBge3IsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD0xNX0KcGxvdEludGVuc2l0aWVzRm9yQ29ob3J0KFJHLCAnRm9yZWdyb3VuZCBpbnRlbnNpdGllcycpCmBgYAoKIyBCYWNrZ3JvdW5kIEludGVuc2l0eSBEaXN0cmlidXRpb24KCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9MTV9CnBsb3RJbnRlbnNpdGllc0ZvckNvaG9ydChSRywgJ0JhY2tncm91bmQgaW50ZW5zaXRpZXMnLCBGQUxTRSkKYGBgCgojIEFyZSBDb250cm9scy9CdWZmZXIgc3BvdHMgZGlmZmVyZW50IGZyb20gZWFjaCBvdGhlciBvciB0aGUgcmVndWxhciBwcm9iZXM/CgpgYGB7ciwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTE1fQpwbG90SW50ZW5zaXRpZXNGb3JDb2hvcnRCeVByb2JlKFJHLCAnUHJvYmV3aXNlICBpbnRlbnNpdGllcycpCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KUkcgPC0gYmFja2dyb3VuZENvcnJlY3QoUkcsIG1ldGhvZD0ibm9ybWV4cCIsIG9mZnNldD01MCkKUkckRSA8LSBub3JtYWxpemVCZXR3ZWVuQXJyYXlzKFJHJEUsIG1ldGhvZD0icXVhbnRpbGUiKQpSRyRFIDwtIGxvZzIoUkckRSkKYGBgCgojIEJhY2tncm91bmQgY29ycmVjdGlvbiBhbmQgbm9ybWFsaXphdGlvbgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGVycm9yPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpjb250cm9sUHJvYmVzIDwtIFJHJGdlbmVzJElEICVpbiUgYygnQ29udHJvbCcpCmJ1ZmZlclByb2JlcyA8LSBSRyRnZW5lcyROYW1lICVpbiUgYygnYnVmZmVyJywgJ0J1ZmZlcicpCgoKIyMgU29tZSBjb250cm9sIHByb2JlcyBzZWVtIHRvIGJlIGxhYmVsbGVkIGFzICdCdWZmZXInIGluIG5hbWUgY29sdW1uCiMjIHNvIHdlIHdpbGwgdHJlYXQgdGhlbSBhcyBidWZmZXIgb25seQoKY29udHJvbFByb2JlcyA8LSBzZXRkaWZmKGNvbnRyb2xQcm9iZXMsIGJ1ZmZlclByb2JlcykKb3RoZXJQcm9iZXMgPC0gIFJHJGdlbmVzJElEICVuaW4lIGMoJ0NvbnRyb2wnKSAmIFJHJGdlbmVzJE5hbWUgJW5pbiUgYygnYnVmZmVyJywgJ0J1ZmZlcicpCgpzdGF0dXMgPC0gcmVwKE5VTEwsIGxlbmd0aChSRyRnZW5lcyRJRCkpCnN0YXR1c1tjb250cm9sUHJvYmVzXSA8LSAnQ29udHJvbCcKc3RhdHVzW2J1ZmZlclByb2Jlc10gPC0gJ0J1ZmZlcicKc3RhdHVzW290aGVyUHJvYmVzXSA8LSAnUmVndWxhcicKCgojIyBOZXFjIG5vcm1hbGl6YXRpb24gdXNpbmcgYnVmZmVyIGdlbmVzIGFzIGNvbnRyb2wKUkcubmVxYyA8LSBuZXFjKFJHLCBzdGF0dXM9c3RhdHVzLCBuZWdjdHJsID0gJ0J1ZmZlcicsIHJlZ3VsYXI9J1JlZ3VsYXInLCBvZmZzZXQ9NTAsIHJvYnVzdCA9IFRSVUUpCgoKCmBgYAoKIyBDb3JyZWxhdGlvbiBiZXR3ZWVuIGR1cGxpY2F0ZSBzcG90cwoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGVycm9yPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpmIDwtIGZhY3RvcihwYXN0ZSh0YXJnZXRzJENvbmRpdGlvbiwgdGFyZ2V0cyRTZXgsIHNlcD0iLiIpKQpkZXNpZ24gPC0gbW9kZWwubWF0cml4KH4wK2YpCmNvbG5hbWVzKGRlc2lnbikgPC0gbGV2ZWxzKGYpCmNvcmZpdCA8LSBkdXBsaWNhdGVDb3JyZWxhdGlvbihSRy5uZXFjLCBkZXNpZ24sIG5kdXBzPTIpIywgc3BhY2luZyA9IDEpCmNvcmZpdCRjb25zZW5zdXMuY29ycmVsYXRpb24KYm94cGxvdCh0YW5oKGNvcmZpdCRhdGFuaC5jb3JyZWxhdGlvbnMpKQoKYGBgCgoKIyBDYWxsaW5nIERFIGdlbmVzCgoKCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZXJyb3I9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmNvbnQubWF0cml4IDwtIG1ha2VDb250cmFzdHMoQ2FuY2VydnNIZWFsdGh5PSgoY2FuY2VyLmZlbWFsZSArIGNhbmNlci5tYWxlKS8yLWhlYWx0aHkucG9vbGVkKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9ZGVzaWduKQpmaXQgPC0gbG1GaXQoUkcubmVxYywgCiAgICAgICAgICAgICBkZXNpZ24sIG5kdXBzPTIsCiAgICAgICAgICAgICBjb3JyZWxhdGlvbj1jb3JmaXQkY29uc2Vuc3VzKQpmaXQgPC0gY29udHJhc3RzLmZpdChmaXQsIGNvbnQubWF0cml4KQpmaXQgPC0gZUJheWVzKGZpdCkKdG9wIDwtIHRvcFRhYmxlKGZpdCwgbnVtYmVyID0gbGVuZ3RoKFJHLm5lcWMkZ2VuZXMkSUQpKQoKdG9wLnNpZyA8LSBzdWJzZXQodG9wLCBhZGouUC5WYWwgPCAwLjA1KQp3cml0ZS5jc3YodG9wLCAnLi4vcmVzdWx0cy9jYW5jZXJfdnNfaGVhbHRoeV9uZXFjLmFsbC5jc3YnLCByb3cubmFtZXMgPSBGKQp3cml0ZS5jc3YodG9wLnNpZywgJy4uL3Jlc3VsdHMvY2FuY2VyX3ZzX2hlYWx0aHlfbmVxYy5zaWduaWZpY2FudC5jc3YnLCByb3cubmFtZXMgPSBGKQpgYGAKCgojIEdPIGVucmljaG1lbnQgb2Ygc2lnbmlmaWNhbnQgREUgZ2VuZXMKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTE1fQoKZ2VuZXMgPC0gdG9wLnNpZyROYW1lCmdlbmVzLmVudHJleiA8LSBnZXRfZW50cmV6KGdlbmVzKQpla2cgPC0gZW5yaWNoR08oZ2VuZSA9IGdlbmVzLmVudHJleiwKICAgICAgICAgICAgICAgIE9yZ0RiID0gb3JnLkhzLmVnLmRiLAogICAgICAgICAgICAgICAgcEFkanVzdE1ldGhvZCA9ICdCSCcsCiAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgID0gMC4yLAogICAgICAgICAgICAgICAgcXZhbHVlQ3V0b2ZmICA9IDAuMikKYmFycGxvdChla2cpCgp3cml0ZWVnbyhla2csICdjYW5jZXJfdnNfaGVhbHRoeV9uZXFjLnNpZ25pZmljYW50LkdPJykKYGBgCiMgS0VHRyBlbnJpY2htZW50IG9mIHNpZ25pZmljYW50IERFIGdlbmVzCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZXJyb3I9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD0xNX0KZ2VuZXMgPC0gdG9wLnNpZyROYW1lCmdlbmVzLmVudHJleiA8LSBnZXRfZW50cmV6KGdlbmVzKQpla2cgPC0gZW5yaWNoS0VHRyhnZW5lID0gZ2VuZXMuZW50cmV6LAogICAgICAgICAgICAgICAgICAgIG9yZ2FuaXNtID0gJ2hzYScsCiAgICAgICAgICAgICAgICAgICAgcEFkanVzdE1ldGhvZCA9ICdCSCcsCiAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmICA9IDAuMiwKICAgICAgICAgICAgICAgICAgICBxdmFsdWVDdXRvZmYgID0gMC4yKQpiYXJwbG90KGVrZywgc2hvd0NhdGVnb3J5ID0gMTUpCndyaXRlRUtHKGVrZywgJ2NhbmNlcl92c19oZWFsdGh5X25lcWMuc2lnbmlmaWNhbnQuS0VHRycpCmBgYAo=