ALL IMPORTED FILES MUST BE IN THE SAME DIRECTORY AS THIS SCRIPT
Load required packages
library("tidyverse")
library("GenomeInfoDb")
library("deconstructSigs")
library("BSgenome.Hsapiens.UCSC.hg19")
library("RColorBrewer")
library("nnls")
library("SignatureEstimation")
library("reshape2")
library("siglasso")
library("parallel")
library("grid")
library("gridExtra")
Create mutation signature functions
run_deconstructSigs = function(x, out.dir = ".", signatures.ref = signatures.exome.cosmic.v3.may2019, all.colors = NULL, sig.type = 'SBS', bsg = NULL, ...){
# x = data.frame with columns CHROM, POS, REF, ALT and, optionally, Sample
# signatures.ref = one of signatures.nature2013, signatures.exome.cosmic.v3.may2019,
# signatures.genome.cosmic.v3.may2019, signatures.dbs.cosmic.v3.may2019,
# signatures.cosmic
# ... = arguments to pass to whichSignatures()
if(! "Sample" %in% colnames(x)){
x$Sample = "Sample"
samples = "Sample"
} else {
samples = unique(x$Sample)
}
# Same colours as before but different signature names
if(is.null(all.colors[1])){
all.colors <- c("#023FA5", "#023FA5", "#7D87B9", "#BEC1D4",
"#D6BCC0", "#BB7784", "gold", "#4A6FE3", "#8595E1", "#B5BBE3",
"#E6AFB9", "#E07B91", "#D33F6A", "#11C638", "#8DD593",
"#C6DEC7", "#EAD3C6", "#F0B98D", "#EF9708", "#0FCFC0",
"#9CDED6", "#D5EAE7", "#F3E1EB", "#F6C4E1", "#F79CD4",
"#866097", "#008941", "#A30059", "#F6C4E1", "#F79CD4",
"#866097", "#008941", "#A30059", "#008080", "#8B0000",
"#F4A460", "#663399")#, "#706563")
}
old.sigs <- c("Signature.1", "Signature.1A", "Signature.1B",
"Signature.2", "Signature.3", "Signature.4", "Signature.5",
"Signature.6", "Signature.7", "Signature.8", "Signature.9",
"Signature.10", "Signature.11", "Signature.12", "Signature.13",
"Signature.14", "Signature.15", "Signature.16", "Signature.17",
"Signature.18", "Signature.19", "Signature.20", "Signature.21",
"Signature.R1", "Signature.R2", "Signature.R3", "Signature.U1",
"Signature.U2", "Signature.22", "Signature.23", "Signature.24",
"Signature.25", "Signature.26", "Signature.27", "Signature.28",
"Signature.29", "Signature.30", "unknown")
sigs.input = mut.to.sigs.input(mut.ref = x,
sample.id = "Sample",
chr = "CHROM",
pos = "POS",
ref = "REF",
alt = "ALT",
sig.type = sig.type,
bsg = bsg)
for(sample in samples){
tryCatch(expr = {
cat(paste0("Processing ", sample, "...\n"))
if(length(samples) == 1 & sample == "Sample"){
results.dir = out.dir
} else {
results.dir = file.path(out.dir, sample)
}
prefix = paste(sample,deparse(substitute(signatures.ref)), sep="_")
if(!dir.exists(results.dir)){dir.create(results.dir, recursive = T)}
error = file(file.path(results.dir, paste0(prefix, "_error.txt")), open="wt")
sink(error, type="message")
sigs = whichSignatures(tumor.ref = sigs.input,
signatures.ref = signatures.ref,
sample.id = sample,
contexts.needed = TRUE,
...)
#pdf(file.path(results.dir, paste0(prefix, "_plots.pdf")))
# Plotting
png(file.path(results.dir, paste0(prefix, "_signature.png")),
width=1200, height=1000, units="px", pointsize=20)
plotSignatures(sigs)
dev.off()
new.sigs = setdiff(colnames(sigs$weights)[sigs$weights[1,] > 0], old.sigs)
png(file.path(results.dir, paste0(prefix, "_pie.png")),
width=960, height=960, units="px", pointsize=24)
if(length(new.sigs) > 0){
makePie(sigs, add.color=unique(all.colors)[1:length(new.sigs)])
} else {
makePie(sigs)
}
dev.off()
# Save output
write.table(x %>% dplyr::filter(Sample == sample) %>%
dplyr::filter(!duplicated(paste(CHROM, POS, REF, ALT))),
file = file.path(results.dir, paste0(prefix, "_unique_mutations.txt")),
sep="\t", quote=F, row.names=F, col.names=T)
write.table(data.frame(Signature = colnames(sigs$weights),
Percent = round(unlist(sigs$weights)*100, digits=2)),
file = file.path(results.dir, paste0(prefix, "_percentages.txt")),
quote=F, sep="\t", col.names=T, row.names=F)
write.table(sigs.input[sample,],
file = file.path(results.dir, paste0(prefix, "_trinucleotides.txt")),
row.names=F, col.names=T, sep="\t", quote=F)
}, finally = {
cat(paste0("Finished ", sample, "\n"))
})
}
cat("Finished!\n")
}
id.signature.path = "sigProfiler_ID_signatures.csv"
signatures.id.cosmic.v3.may2019 = as.data.frame(t(read.delim(id.signature.path,
sep=",", header=T, as.is=T, row.names=1)))
# Normalises variants to SBS, DBS and indels only (removes substitutions >= 3 BP in length)
norm_sbs_to_dbs = function(x, chr="CHROM", pos="POS",
ref="REF", alt="ALT", sample = "Sample"){
# colnames CHROM, POS, REF, ALT, (optional) Sample
if(! sample %in% colnames(x)){
x[,sample] = "Sample"
rm.sample = TRUE # do not return sample name, assume all variants same sample
} else {
rm.sample = FALSE
}
orig.cols = c(sample, chr, pos, ref, alt)
x = x[,orig.cols]
cols.use = c("Sample", "CHROM", "POS", "REF", "ALT")
colnames(x) = cols.use
x = x %>% arrange(Sample, CHROM, POS) %>%
filter(!duplicated(paste(Sample, CHROM, POS, REF, ALT))) %>%
mutate(Already.DBS = nchar(REF) == 2 & nchar(ALT) == 2)
indels = x %>% filter(nchar(REF) != nchar(ALT))
substitutions = x %>% filter((nchar(REF) == 1 & nchar(ALT) == 1) | Already.DBS)
dbs = substitutions %>% filter(nchar(REF) == 1 & nchar(ALT) == 1) %>%
group_by(Sample, CHROM) %>%
mutate(diff1 = abs(c(head(POS, -1) - tail(POS, -1), 0)), diff2 = abs(c(0, tail(POS, -1) - head(POS, -1)))) %>%
filter((diff1 == 1 | diff2 == 1) & ! (diff1 == 1 & diff2 == 1)) %>% # remove anything > 2 substitutions as probable indel or bad mapping
mutate(diff1 = abs(c(head(POS, -1) - tail(POS, -1), 0)), diff2 = abs(c(0, tail(POS, -1) - head(POS, -1)))) %>%
filter(diff1 == 1 | diff2 == 1) %>%
summarise(POS=min(POS), REF=paste0(REF, collapse=""), ALT = paste0(ALT, collapse="")) %>% ungroup()
sbs = substitutions %>% filter(nchar(REF) == 1 & nchar(ALT) == 1) %>%
group_by(Sample, CHROM) %>%
mutate(diff1 = abs(c(head(POS, -1) - tail(POS, -1), 0)), diff2 = abs(c(0, tail(POS, -1) - head(POS, -1)))) %>%
filter(diff1 != 1 & diff2 != 1) %>% ungroup()# remove anything with adjacent variants
if(sum(substitutions$Already.DBS) > 0){
dbs = rbind(dbs, substitutions[which(substitutions$Already.DBS),cols.use])
}
res = NULL
if(nrow(indels) > 0){
res = rbind(res, indels[,cols.use])
}
if(nrow(dbs) > 0){
res = rbind(res, dbs[,cols.use])
}
if(nrow(sbs) > 0){
res = rbind(res, sbs[,cols.use])
}
res = as.data.frame(res) %>% arrange(Sample, CHROM, POS)
res = res[,cols.use]
colnames(res) = orig.cols
return(res)
}
# Assigns InDel mutation type (see cosmic indel signatures)
calc.id.type = function(r,a,c){
ri = (nchar(c) %/% 2)
if(nchar(a) == 1){
# deletion
id.length = nchar(r) - nchar(a)
if(substr(c, ri, ri+id.length) != r){
return(NA)
}
id.type = "del"
id.seq = substr(r, 2, nchar(r))
if(substr(r,2,2) %in% c("A", "G")){
c = rev.strand(c)
id.seq = rev.strand(id.seq)
ri = ri - (id.length - 1)
while(substr(c, ri - id.length + 1, ri) == id.seq){
# left normalise reverse strand
ri = ri - id.length
}
#r = substr(c, ri, ri + id.length)
}
} else {
id.length = nchar(a) - nchar(r)
id.type = "ins"
id.seq = substr(a,2, nchar(a))
if(substr(id.seq,1,1) %in% c("A", "G")){
c = rev.strand(c)
ri = ri + 1
id.seq = rev.strand(id.seq)
}
}
while(substr(c, ri - id.length + 1, ri) == id.seq){
# left normalise
ri = ri - id.length
}
if(ri < 1){
ri = ri + id.length
}
n.reps = 0
while(substr(c, ri+1+id.length*(n.reps), ri+id.length*(n.reps+1)) == id.seq){
n.reps = n.reps + 1
}
if(id.type == "del"){
n.reps = n.reps - 1 # remove del itself
if(id.length == 1){
if(substr(id.seq, 1, 1) == "C"){
sig.index = 1 + min(n.reps, 5)
} else {
sig.index = 7 + min(n.reps, 5)
}
} else {
if(n.reps == 0){
# check microhomology
n.hom = 0
# forward
while(substr(c, ri+1+id.length, ri+1+id.length+n.hom) == substr(id.seq, 1, 1+n.hom)){
n.hom = n.hom + 1
}
# reverse
n.hom.r = 0
while(substr(c, ri - n.hom.r, ri) == substr(id.seq, id.length - n.hom.r, id.length)){
n.hom.r = n.hom.r + 1
}
n.hom = max(n.hom, n.hom.r)
if(n.hom == 0){
sig.index = 25 + min(n.reps, 5) + 6*(min(5, id.length)-2)
} else {
sig.index = 73 + min(n.hom, 5) - 1 + sum(0:(min(id.length, 5) - 2))
}
} else {
sig.index = 25 + min(n.reps, 5) + 6*(min(5, id.length)-2)
}
}
} else {
if(id.length == 1){
if(substr(id.seq, 1, 1) == "C"){
sig.index = 13 + min(n.reps, 5)
} else {
sig.index = 19 + min(n.reps, 5)
}
} else {
sig.index = 49 + min(n.reps, 5) + 6*(min(5, id.length) - 2)
}
}
return(sig.index)
}
rev.strand = function(x){
rev.bases = c("A" = "T", "T" = "A", "G" = "C", "C" = "G", "N" = "N")
y = rev(rev.bases[unlist(strsplit(x, ""))])
if(length(x) == 1){return(paste(y, collapse=""))} else {return(y)}
}
# Creates input mutation matrices for each mutation type
mut.to.sigs.input.alltypes = function(mut.ref, sample.id = "Sample", chr = "chr", pos = "pos",
ref = "ref", alt = "alt", bsg = NULL, sig.types = c("SBS", "DBS", "ID"), dbs_table = dbs_possible) {
results = lapply(sig.types, function(sig.type){
if(sig.type == "ID"){
# InDel stuff here
samples = factor(mut.ref[,sample.id])
mut.ref = mut.ref[nchar(mut.ref[,ref]) != 1 | nchar(mut.ref[,alt]) != 1,]
res = matrix(0, nrow = length(levels(samples)), ncol=83)
rownames(res) = levels(samples)
if (exists("mut.ref", mode = "list")) {
mut.full <- mut.ref
} else {
if (file.exists(mut.ref)) {
mut.full <- utils::read.table(mut.ref, sep = "\t",
header = TRUE, as.is = FALSE, check.names = FALSE)
}
else {
stop("mut.ref is neither a file nor a loaded data frame")
}
}
if(nrow(mut.full) == 0){
return(res)
}
mut <- mut.full[, c(sample.id, chr, pos, ref, alt)]
mut[, chr] <- factor(mut[, chr])
levels(mut[, chr]) <- sub("^([0-9XY])", "chr\\1", levels(mut[,
chr]))
levels(mut[, chr]) <- sub("^MT", "chrM", levels(mut[,
chr]))
levels(mut[, chr]) <- sub("^(GL[0-9]+).[0-9]", "chrUn_\\L\\1",
levels(mut[, chr]), perl = T)
if (is.null(bsg)) {
bsg = BSgenome.Hsapiens.UCSC.hg19::Hsapiens
}
unknown.regions <- levels(mut[, chr])[which(!(levels(mut[,chr]) %in% GenomeInfoDb::seqnames(bsg)))]
if (length(unknown.regions) > 0) {
unknown.regions <- paste(unknown.regions, collapse = ", ")
warning(paste("Check chr names -- not all match bsg object:\n",
unknown.regions, sep = " "))
mut <- mut[mut[, chr] %in% GenomeInfoDb::seqnames(bsg),
]
}
mut$context = BSgenome::getSeq(bsg,
mut[, chr], mut[, pos] - 6*pmax(1, nchar(mut[,ref]) - 1, nchar(mut[,alt]) - 1) + 1,
mut[, pos] + 6*pmax(1, nchar(mut[,ref]) - 1, nchar(mut[,alt]) - 1) + 1, as.character = T)
for(i in 1:nrow(mut)){
s = mut[i,sample.id]
j = match(s, levels(samples))
p = mut[i,pos]
r = mut[i,ref]
a = mut[i,alt]
#c = strsplit(mut[i,"context"], "")[[1]]
c = mut[i,"context"]
n = calc.id.type(r, a, c)
if(is.na(n)){
cat(paste0("Variant ", paste(mut[i,chr], p, r, a, sep="-"), " does not match reference! It will be ignored.\n"))
} else {
res[j,n] = res[j,n] + 1
}
}
return(res)
} else {
return(mut.to.sigs.input(mut.ref, sample.id, chr, pos, ref, alt, bsg, sig.type, dbs_table))
}
})
results = setNames(results, sig.types)
return(results)
}
# Non-negative least squares method for SignatureEstimation
decomposeNNLS = function(m, P, ...){
exposures = coef(nnls(P, m))
exposures = exposures/sum(exposures)
return(exposures)
}
# DeconstructSigs wrapper for SignatureEstimation
decomposeDeSig = function(m, P, ...){
m = as.data.frame(t(m))
rownames(m) = "Sample"
P = as.data.frame(t(P))
colnames(P) = colnames(m)
y = whichSignatures(tumor.ref = m, signatures.ref = P)
exposures = as.numeric(y$weights)
#exposures = exposures/sum(exposures)
return(exposures)
}
# SigLASSO wrapper for SignatureEstimation
decomposeSiglasso = function(m, P, ...){
counts = list(...)[["counts"]]
if(!is.null(counts)){
m = m*counts
}
m = matrix(m, ncol=1)
success = FALSE # randomly fails sometimes
while(!success){
tryCatch(expr = {
y = siglasso(sample_spectrum = m, signature = P, plot=F)
success = TRUE
}, error = function(e){},
finally = {
next
})
}
exposures = y[,1]
#exposures = exposures/sum(exposures)
return(exposures)
}
# Multiprocess version of SignatureEstimation::bootstrapSigExposures
bootstrapSigExposuresMP = function (m, P, R, mutation.count = NULL, decomposition.method = decomposeQP, num.threads = 8,
...) {
P = as.matrix(P)
if (length(m) != nrow(P))
stop("Length of vector 'm' and number of rows of matrix 'P' must be the same.")
if (any(names(m) != rownames(P)))
stop("Elements of vector 'm' and rows of matrix 'P' must have the same names (mutations types).")
if (ncol(P) == 1)
stop("Matrices 'P' must have at least 2 columns (signatures).")
if (is.null(mutation.count)) {
if (all(SignatureEstimation:::is.wholenumber(m)))
mutation.count = sum(m)
else stop("Please specify the parameter 'mutation.count' in the function call or provide mutation counts in parameter 'm'.")
}
m = m/sum(m)
K = length(m)
replicateMP = function(n, expr, num.threads = 8){
as.matrix(do.call(cbind, mclapply(integer(n), eval.parent(substitute(function(...) expr)),
mc.cores = num.threads)))
}
extra.args = list(...)
exposures = replicateMP(R, {
mutations_sampled = sample(seq(K), mutation.count, replace = TRUE,
prob = m)
m_sampled = as.numeric(table(factor(mutations_sampled,
levels = seq(K))))
m_sampled = m_sampled/sum(m_sampled)
do.call(decomposition.method, c(list(m=m_sampled, P=P), extra.args))
}, num.threads = num.threads)
rownames(exposures) = colnames(P)
colnames(exposures) = paste0("Replicate_", seq(R))
errors = apply(exposures, 2, function(e) SignatureEstimation:::FrobeniusNorm(m,
P, e))
names(errors) = colnames(exposures)
return(list(exposures = exposures, errors = errors))
}
# Specify COSMICv2 & 3 signature sets (incl.OvCa-specific COSMICv3 signatures)
all.signatures = list("DBS" = list("DBS.COSMIC.v3.may2019" = signatures.dbs.cosmic.v3.may2019,
"DBS.COSMIC.v3.may2019.HGSOvCa" = signatures.dbs.cosmic.v3.may2019[c(2,4,5,6,9),]),
"SBS" = list("SBS.COSMIC" = signatures.cosmic,
"SBS.COSMIC.exome.v3.may2019" = signatures.exome.cosmic.v3.may2019,
"SBS.COSMIC.genome.v3.may2019" = signatures.genome.cosmic.v3.may2019,
"SBS.COSMIC.exome.v3.may2019.HGSOvCa" = signatures.exome.cosmic.v3.may2019[c(1,2,3,5,11,17,23,31,40,44,45,46),],
"SBS.COSMIC.genome.v3.may2019.HGSOvCa" = signatures.genome.cosmic.v3.may2019[c(1,2,3,5,11,17,23,31,40,44,45,46),]),
"ID" = list("ID.COSMIC.v3.may2019" = signatures.id.cosmic.v3.may2019,
"ID.COSMIC.v3.may2019.HGSOvCa" = signatures.id.cosmic.v3.may2019[c(1,2,4,5,6,8,9),]))
SBS.COSMIC.exome.v3.may2019.HGSOvCa = signatures.exome.cosmic.v3.may2019[c(1,2,3,5,11,17,23,31,40,44,45,46),]
# Main function
# data = data.frame with sample, chr, pos, ref, alt columns specified
# sig.types = mutation types to test
# signature.list = list[sig.types] of signature matrices
# bootstrap.reps = number of bootstrap resampling repetitions for calculating variance
# plot.file = output file for plots (pdf)
# bsg = BSGenome object (default is hg19) for fetching reference contexts
# num.threads = number of processes for bootstrap parallelisation
# method.colours = colours for signature methods in plot, default is ggplot colours
# Returns list with signature scores and errors (and extra table for bootstraps) plus input matrices
run_sigs = function(data, sample = "Sample",
chr="CHROM", pos="POS", ref="REF", alt="ALT",
sig.types = c("SBS", "DBS", "ID"),
signature.list = all.signatures,
decomposition.methods = c("QP", "SA", "DeconstructSigs",
"SigLASSO", "NNLS"),
bootstrap.reps = 0, bsg=NULL,
plot=TRUE, plot.file = NULL,
num.threads = 8,
method.colours = NULL
){
all.decomposition.methods = list("QP" = decomposeQP,
"SA" = decomposeSA,
"DeconstructSigs" = decomposeDeSig,
"SigLASSO" = decomposeSiglasso,
"NNLS" = decomposeNNLS)
decomposition.methods = all.decomposition.methods[decomposition.methods]
if(!sample %in% colnames(data)){data[,sample] = "_"}
data = norm_sbs_to_dbs(data, chr=chr, pos=pos, ref=ref, alt=alt, sample = sample)
data[,"Sample"] = data[,sample]
sample = "Sample" # deconstructSigs mut.to.sigs.input doesn't work properly for DBS with different sample.id column for some reason
input.matrices = mut.to.sigs.input.alltypes(mut.ref = data,
chr = chr, pos=pos, ref = ref, alt=alt,
sample.id = sample, bsg=bsg,
sig.types = sig.types, dbs_table = dbs_possible)
results = list(input.matrices = input.matrices)
samples = unique(data[,sample])
for(s in samples){
print(s)
for(sig.type in sig.types){
sigs.input = input.matrices[[sig.type]]
if(is.null(sigs.input)){next}
if(!s %in% rownames(sigs.input)){next}
sigs.input = sigs.input[s,,drop=F]
if(nrow(sigs.input) > 0){
if(sum(sigs.input) < 1){
next
}
signatures = signature.list[[sig.type]]
m = as.matrix(t(sigs.input))
for(signature.name in names(signatures)){
signature = signatures[[signature.name]]
P = as.matrix(t(signature))
result.list = lapply(1:length(decomposition.methods), function(i){
decomposition.method = decomposition.methods[[i]]
decomposition.method.name = names(decomposition.methods)[i]
cat(paste0("Method: ", decomposition.method.name,
", Signature: ", signature.name,
", Mutation type: ", sig.type,
"\n"))
if(decomposition.method.name == "SigLASSO"){
if(sig.type != "SBS"){return(NULL)}
counts = sum(m)
y = findSigExposures(m, P,
decomposition.method = decomposition.method,
counts = counts)
} else {
y = findSigExposures(m, P,
decomposition.method = decomposition.method)
}
exposures = data.frame(Signature = rownames(y$exposures),
Exposure = y$exposures[,1],
Method = decomposition.method.name,
Signature.Set = signature.name,
stringsAsFactors = F)
exposures$Method = decomposition.method.name
exposures$Signature.Set = signature.name
exposures$Sample = s
errors = data.frame(Error = y$errors,
Method = decomposition.method.name,
Signature.Set = signature.name,
Sample = s,
stringsAsFactors = F)
return(list(exposures = exposures,
errors = errors))
})
exposures = do.call(rbind, lapply(result.list, function(x){
x[["exposures"]]
}))
errors = do.call(rbind, lapply(result.list, function(x){
x[["errors"]]
}))
results$exposures = rbind(results$exposures, exposures)
results$errors = rbind(results$errors, errors)
# estimate errors by bootstrapping
if(bootstrap.reps > 1){
cat("Bootstrapping...\n")
result.list = lapply(1:length(decomposition.methods), function(i){
decomposition.method = decomposition.methods[[i]]
decomposition.method.name = names(decomposition.methods)[i]
cat(paste0("Method: ", decomposition.method.name,
", Signature: ", signature.name,
", Mutation type: ", sig.type,
"\n"))
if(decomposition.method.name == "SigLASSO"){
if(sig.type != "SBS"){return(NULL)}
counts = sum(m)
y = bootstrapSigExposuresMP(m, P, R=bootstrap.reps,
decomposition.method = decomposition.method,
num.threads = num.threads,
counts=counts)
} else {
y = bootstrapSigExposuresMP(m, P, R=bootstrap.reps,
decomposition.method = decomposition.method,
num.threads = num.threads)
}
exposures = melt(data.frame(Signature = rownames(y$exposures),
y$exposures), value.name = "Exposure")
exposures$Method = decomposition.method.name
exposures$Signature.Set = signature.name
exposures$Sample = s
colnames(exposures)[2] = "Replicate"
errors = data.frame(Replicate = names(y$errors),
Error = y$errors,
Method = decomposition.method.name,
Signature.Set = signature.name,
Sample = s,
stringsAsFactors = F)
return(list(exposures = exposures,
errors = errors))
})
exposures = do.call(rbind, lapply(result.list, function(x){
x[["exposures"]]
}))
errors = do.call(rbind, lapply(result.list, function(x){
x[["errors"]]
}))
results$exposures.bootstrap = rbind(results$exposures.bootstrap, exposures)
results$errors.bootstrap = rbind(results$errors.bootstrap, errors)
}
}
}
}
}
if(plot){
plot_results(results, plot.file, method.colours)
}
return(results)
}
# Plot results
plot_results = function(results, plot.file=NULL, method.colours = NULL, width=16, height=9){
if(!is.null(plot.file)){
pdf(plot.file, width=width, height=height)
}
int_breaks_rounded <- function(x, n = 5) pretty(x, n)[round(pretty(x, n),1) %% 1 == 0] # for integer y axis breaks
for(s in unique(results$exposures$Sample)){
for(sig.type in names(results$input.matrices)){
if(s %in% rownames(results$input.matrices[[sig.type]])){
sigs.input = results$input.matrices[[sig.type]][s,]
theme_common = theme_bw() + theme(panel.grid.major.x = element_blank(),
plot.margin = unit(c(5,1,5,1), "lines"),
axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5),
axis.ticks = element_blank())
if(sig.type == "SBS"){
cols = c("#5ABCEB", "#050708", "#D33C32", "#CBCACB", "#ABCD72", "#E7C9C6")
dat = data.frame(x = as.character(1:length(sigs.input)),
y = as.numeric(sigs.input),
top.cols = gsub(".*\\[|\\].*$", "", colnames(sigs.input)),
x.labels = gsub("\\[|>..", "", colnames(sigs.input)),
stringsAsFactors = F)
for(col in c("x", "top.cols")){
dat[,col] = factor(dat[,col], levels = unique(dat[,col]))
}
p = ggplot(dat, aes(x=x,y=y,fill=top.cols)) + geom_col(show.legend = F) + scale_fill_manual(values=cols) + xlab("") + ylab("Mutation Count")
r = c(0, max(dat$y)*1.05)
expand.factor = 0.5
p = p + scale_x_discrete(labels = dat$x.labels) + scale_y_continuous(expand = expand_scale(c(-expand.factor/(1+2*expand.factor), -expand.factor/(1+2*expand.factor))), limits = c(-expand.factor*r[2], r[2]*(1+expand.factor)), breaks=int_breaks_rounded)
p = p + theme_common
top.segs = dat %>% group_by(top.cols) %>% summarise(xstart = as.numeric(head(x,1)) - 0.25, xend=as.numeric(tail(x,1)) + 0.25,y=max(dat$y)*1.10, text.x = x[ceiling(length(x)/2)])
p = p + geom_segment(data = top.segs, mapping =aes(x=xstart,xend=xend, y=y,yend=y, size=2, col=top.cols), show.legend = F) + scale_colour_manual(values=cols)
p = p + geom_text(data = top.segs, mapping = aes(x=text.x, y= max(dat$y)*1.15, label=top.cols))
p = p + coord_cartesian(clip="off")
p = p + ggtitle(paste0(s, " - SBS mutations\n\n\n"))
print(p)
} else if(sig.type == "DBS"){
cols = c("#a6cee3", "#1f78b4",
"#b2df8a", "#33a02c", "#fb9a99", "#e31a1c", "#fdbf6f",
"#ff7f00", "#cab2d6", "#6a3d9a")
dat = data.frame(x = as.character(1:length(sigs.input)),
y = as.numeric(sigs.input),
top.cols = paste0(gsub(">.*$", "", colnames(sigs.input)), ">NN"),
x.labels = gsub("^.*>", "", colnames(sigs.input)),
stringsAsFactors = F)
for(col in c("x", "top.cols")){
dat[,col] = factor(dat[,col], levels = unique(dat[,col]))
}
p = ggplot(dat, aes(x=x,y=y,fill=top.cols)) + geom_col(show.legend = F) + scale_fill_manual(values=cols) + xlab("") + ylab("Mutation Count")
r = c(0, max(dat$y)*1.05)
expand.factor = 0.5
p = p + scale_x_discrete(labels = dat$x.labels) + scale_y_continuous(expand = expand_scale(c(-expand.factor/(1+2*expand.factor), -expand.factor/(1+2*expand.factor))), limits = c(-expand.factor*r[2], r[2]*(1+expand.factor)), breaks=int_breaks_rounded)
p = p + theme_common
top.segs = dat %>% group_by(top.cols) %>% summarise(xstart = as.numeric(head(x,1)) - 0.25, xend=as.numeric(tail(x,1)) + 0.25, y=max(dat$y)*1.10, text.x = x[ceiling(length(x)/2)])
p = p + geom_segment(data = top.segs, mapping =aes(x=xstart,xend=xend, y=y,yend=y, size=2, col=top.cols), show.legend = F) + scale_colour_manual(values=cols)
p = p + geom_text(data = top.segs, mapping = aes(x=text.x, y= max(dat$y)*1.15, label=top.cols))
p = p + coord_cartesian(clip="off")
p = p + ggtitle(paste0(s, " - DBS mutations\n\n\n"))
print(p)
} else {
cols = c("#fdbf6f", "#ff7f00", "#b2df8a", "#33a02c",
colorRampPalette(colors = c("#fb9a99", "#e31a1c"))(4),
colorRampPalette(colors = c("#a6cee3", "#1f78b4"))(4),
colorRampPalette(colors = c("#cab2d6", "#6a3d9a"))(4))
dat = data.frame(x = as.character(1:length(sigs.input)),
y = as.numeric(sigs.input),
top.text1 = c(rep("1bp deletion", 12),
rep("1bp insertion", 12),
rep(">1bp deletions at repeats\n(Deletion length)", 24),
rep(">1bp insertions at repeats\n(Insertion length)", 24),
rep("Deletions with microhomology\n(Deletion length)", 11)),
top.text2 = c(rep("C", 6),
rep("T", 6),
rep("C", 6),
rep("T", 6),
rep("2", 6),
rep("3", 6),
rep("4", 6),
rep("5+", 6),
rep("2", 6),
rep("3", 6),
rep("4", 6),
rep("5+", 6),
rep("2", 1),
rep("3", 2),
rep("4", 3),
rep("5+", 5)),
x.labels = "",
bottom.text1 = c(rep(c("1", "2", "3", "4", "5", "6+"), 2),
rep(c("0", "1", "2", "3", "4", "5+"), 2),
rep(c("1", "2", "3", "4", "5", "6+"), 4),
rep(c("0", "1", "2", "3", "4", "5+"), 4),
c(1, 1:2, 1:3, 1:4, "5+")),
bottom.text2 = c(rep("Homopolymer length", 12),
rep("Homopolymer length", 12),
rep("Number of repeat units", 24),
rep("Number of repeat units", 24),
rep("Microhomology length", 11)),
stringsAsFactors = F)
dat$top.cols = paste(dat$top.text1, dat$top.text2)
for(col in c("x", "top.cols")){
dat[,col] = factor(dat[,col], levels = unique(dat[,col]))
}
top.segs = dat %>% group_by(top.cols) %>% summarise(xstart = as.numeric(head(x,1)) - 0.25, xend=as.numeric(tail(x,1)) + 0.25,
y=max(dat$y)*1.10, y2=-(0.05*max(dat$y)),
text.x = x[ceiling(length(x)/2)], text=top.text2[1], text.col = ifelse(text %in% c("T", "5+"), "white", "black"))
p = ggplot() + geom_col(data=dat, mapping = aes(x=x,y=y,fill=top.cols), show.legend = FALSE) + scale_fill_manual(values=cols) + xlab("") + ylab("Mutation Count")
r = c(0, max(dat$y)*1.05)
expand.factor = 0.5
p = p + scale_x_discrete(labels = dat$x.labels) + scale_y_continuous(expand = expand_scale(c(-expand.factor/(1+2*expand.factor), -expand.factor/(1+2*expand.factor))), limits = c(-expand.factor*r[2], r[2]*(1+expand.factor)), breaks=int_breaks_rounded)
p = p + theme_common
p = p + geom_segment(data = top.segs, mapping =aes(x=xstart,xend=xend, y=y,yend=y, size=2, col=top.cols), show.legend = FALSE)
p = p + geom_segment(data = top.segs, mapping =aes(x=xstart,xend=xend, y=y2,yend=y2, size=2, col=top.cols), show.legend = FALSE) + scale_colour_manual(values=cols)
p = p + geom_text(data = top.segs, mapping = aes(x=text.x, y=y, label=text), col=top.segs$text.col,hjust=0.5, nudge_x = ifelse((top.segs$xend - top.segs$xstart - 0.5) %% 2, 0.5, 0))
p = p + geom_text(data = dat, mapping = aes(x=x, y=-(0.1*max(dat$y)), label=bottom.text1), hjust=0.5)
annot2 = dat %>% group_by(bottom.text2, top.text1) %>% summarise(y=-0.15*max(dat$y), y2=1.17*max(dat$y), x=x[ceiling(length(x)/2)]) %>% ungroup()
p = p + geom_text(data = annot2, mapping=aes(x=x, y=y, label=bottom.text2))
p = p + geom_text(data = annot2, mapping=aes(x=x, y=y2, label=top.text1))
p = p + coord_cartesian(clip="off")
p = p + ggtitle(paste0(s, " - InDel mutations\n\n\n\n"))
print(p)
}
}
}
for(sig.set in unique(results$exposures$Signature.Set)){
all.sig.names = unique(results$exposures$Signature)
x = results$exposures %>%
filter(Signature.Set == sig.set,
Sample == s) %>%
filter(Signature %in% Signature[Exposure > 0]) %>%
mutate(Signature = factor(Signature, levels = intersect(all.sig.names, Signature)))
if("exposures.bootstrap" %in% names(results)){
# boxplot instead
y = results$exposures.bootstrap %>%
filter(Signature.Set == sig.set,
Sample == s) %>%
mutate(Signature = factor(Signature, levels=intersect(all.sig.names, Signature)))
x = x %>% mutate(Signature = factor(as.character(Signature), levels=levels(y$Signature)))
p = ggplot(y, aes(x=Signature, y=Exposure, fill=Method)) + stat_boxplot(geom='errorbar') + geom_boxplot(outlier.shape=NA) +
geom_boxplot(aes(color = Method),
fatten = NULL, fill = NA, coef = 0, outlier.shape=NA,
show.legend = F) +
scale_y_continuous(expand = expand_scale(c(0, 0.05)))
p = p + geom_point(data = x,
mapping = aes(x = Signature, y = Exposure, fill = Method, group = Method),
shape=23, position=position_dodge(width=0.75), show.legend = F) + guides(col=FALSE)
} else {
p = ggplot(x, aes(x=Signature, y=Exposure, fill=Method)) +
geom_col(position="dodge") +
scale_y_continuous(expand = expand_scale(c(0, 0.05)))
}
p = p + facet_grid(. ~ Signature, scales = "free_x")
p = p + theme_classic() +
theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5)) +
theme(strip.background = element_blank(), strip.text.x = element_blank()) +
xlab("") + ggtitle(paste0(ifelse(s != "_", paste0(s, " - "), ""), gsub("\\.", " ", sig.set)))
if(!is.null(method.colours)){
p = p + scale_fill_manual(name="Method", values = method.colours) + scale_colour_manual(name="Method", values=method.colours)
}
print(p)
if("errors.bootstrap" %in% names(results)){
p = results$errors.bootstrap %>% filter(Signature.Set == sig.set, Sample ==s) %>%
ggplot(aes(x=Method, y=Error, fill=Method)) +
stat_boxplot(geom="errorbar") + geom_boxplot(show.legend = F) + theme_classic()
} else {
p = results$errors %>% filter(Signature.Set == sig.set, Sample==s) %>%
ggplot(aes(x=Method, y=Error, col=Method)) + geom_point(show.legend = F) + theme_classic()
}
if(!is.null(method.colours)){
p = p + scale_fill_manual(name="Method", values = method.colours) + scale_colour_manual(name="Method", values=method.colours)
}
print(p + ylab("Decomposition Error"))
}
}
if(!is.null(plot.file)){
dev.off()
}
}
plot_pies = function(results, signature.sets = NULL, samples = NULL, method="DeconstructSigs", plot.file=NULL,
cols = c("#023FA5", "#023FA5", "#7D87B9", "#BEC1D4",
"#D6BCC0", "#BB7784", "gold", "#4A6FE3", "#8595E1", "#B5BBE3",
"#E6AFB9", "#E07B91", "#D33F6A", "#11C638", "#8DD593",
"#C6DEC7", "#EAD3C6", "#F0B98D", "#EF9708", "#0FCFC0",
"#9CDED6", "#D5EAE7", "#F3E1EB", "#F6C4E1", "#F79CD4",
"#866097", "#008941", "#A30059", "#F6C4E1", "#F79CD4",
"#866097", "#008941", "#A30059", "#008080", "#8B0000",
"#F4A460", "#663399")){
blank_theme <- theme_minimal()+
theme(
axis.title.x = element_blank(),
axis.title.y = element_blank(),
panel.border = element_blank(),
panel.grid=element_blank(),
axis.ticks = element_blank(),
plot.title=element_text(size=14, face="bold"),
axis.text.x = element_blank()
)
if(is.null(signature.sets[1])){
signature.sets = unique(results$exposures$Signature.Set)
}
if(is.null(samples)){
samples = unique(results$exposures$Sample)
}
if(!method %in% results$exposures$Method){
warning(paste0("Method: '", method, "' is not in these results"))
return(NULL)
}
if(!is.null(plot.file)){
pdf(plot.file, height=5, width=5*length(signature.sets))
}
for(sample in samples){
ps = lapply(signature.sets, function(signature.set){
signatures = unique(results$exposures %>% filter(Signature.Set == signature.set) %>% pull(Signature))
if(length(signatures) > length(cols)){
signature.cols = setNames(colorRampPalette(colors = cols)(length(signatures)), signatures)
} else {
signature.cols = setNames(cols[1:length(signatures)], signatures)
}
p = results$exposures %>% filter(Method == method, Sample == sample, Signature.Set == signature.set) %>%
filter(Exposure > 0) %>% ggplot(aes(x="", y=Exposure, fill=Signature)) + geom_bar(stat="identity", width=1) +
scale_fill_manual(values=signature.cols, name="") +
coord_polar("y", start=0) + blank_theme +
ggtitle(signature.set)
return(p)
})
ps[["nrow"]] = 1
do.call(grid.arrange, ps)
}
if(!is.null(plot.file)){dev.off()}
}
Import individual tumour exome somatic variants file and filter as before (see https://rpubs.com/deepsubs/thesis_tumour_exome_variants)
max.callers = 3
f <- read.table("tumour_sample_germline_exome_somatic_caller_file.tsv", header=TRUE, stringsAsFactors=FALSE, sep="\t", comment.char="", quote="") %>%
dplyr::rename("Tumour_Sample"="X.Tumour_Sample") %>%
mutate(n.callers = setNames(sapply(strsplit(unique(Identified), "-"),
function(callers){
ifelse("Intersection" %in% callers, max.callers, sum(!grepl("^filter", callers)))
}), unique(Identified))[Identified])
g <- read.delim("tumour_sample_germline_paired_exomes_HAP.tsv", header=TRUE, stringsAsFactors=FALSE, sep="\t", comment.char="") %>%
dplyr::rename("Tumour_Sample"="X.Tumour_Sample")
sort(unique(f$Tumour_Sample))
sort(unique(g$Tumour_Sample))
ViP_list <- read.delim("ViP Samples & Candidate Genes.txt", header=TRUE, stringsAsFactors=FALSE, sep="\t", comment.char="") %>%
arrange(Sample)
tumour_type <- select(ViP_list,Sample,Tumour.Type) %>% distinct() %>% rename("Sample"="Normal_Sample")
f$Normal_Sample <- as.character(f$Normal_Sample)
g$Normal_Sample <- as.character(g$Normal_Sample)
f <- left_join(f,tumour_type,by="Normal_Sample",copy=FALSE)
g0 <- left_join(g,tumour_type,by="Normal_Sample",copy=FALSE) %>% filter(CANONICAL%in%"YES")
gene_list <- select(ViP_list,Sample,SYMBOL) %>% filter(Sample%in%(f$Normal_Sample))
genes <- gene_list$SYMBOL
tumour_purity <- read.delim("Tumour Purity.txt", header=TRUE, stringsAsFactors=FALSE, sep="\t", comment.char="") %>%
select(Tumour_Sample,Purity,No_Mutations)
f <- left_join(f,tumour_purity,by="Tumour_Sample",copy=FALSE)
g0 <- left_join(g0,tumour_purity,by="Tumour_Sample",copy=FALSE)
f$QUAL <- as.numeric(f$QUAL) %>% replace_na(0)
f$TUMOUR.PMCAD <- as.numeric(f$TUMOUR.PMCAD) %>% replace_na(0)
f$TUMOUR.PMCDP <- as.numeric(f$TUMOUR.PMCDP) %>% replace_na(0)
f$TUMOUR.PMCFREQ <- as.numeric(f$TUMOUR.PMCFREQ) %>% replace_na(0)
f$NORMAL.PMCAD <- as.numeric(f$NORMAL.PMCAD) %>% replace_na(0)
f$NORMAL.PMCDP <- as.numeric(f$NORMAL.PMCDP) %>% replace_na(0)
f$NORMAL.PMCFREQ <- as.numeric(f$NORMAL.PMCFREQ) %>% replace_na(0)
f$GnomAD_v3_AF <- as.numeric(f$GnomAD_v3_AF) %>% replace_na(0)
f0 <- filter(f,!Identified%in%c("FilteredInAll")) %>%
filter(n.callers>=2) %>%
filter(Consequence_Rank<7)
f1<-filter(f0,(NORMAL.PMCAD <= 2) & (NORMAL.PMCDP >= 10) & (NORMAL.PMCFREQ < 0.01))
f2<-filter(f1,TUMOUR.PMCAD >= 5)
f3<-filter(f2,TUMOUR.PMCDP>=20)
f4<-filter(f3,TUMOUR.PMCFREQ >= (f3$Purity*0.5*(2/3)))
f5<-filter(f4,GnomAD_v2.1_non_cancer_AF <= 0.0001)
f6<-filter(f5,GnomAD_v3_AF <= 0.0001)
samples<-unique(f$Tumour_Sample)
Extract remaining unique variants and columns used for mutation signature analysis, and rename sample column
f7<-select(f6,c(CHROM,POS,REF,ALT,Variant_Type))
unique(f7$Variant_Type)
f7$Sample<-f6$Tumour_Sample
Run mutation signature functions (DeconstructSigs, SigLASSO, SA, QP, NNLS, SignatureEstimation), including count of number of variants used and filtering metrics, and output results
mut.ref<-f7
mut.ref$CHROM<-unlist(lapply(mut.ref$CHROM,function(x) paste("chr",x,sep="")))
mut.ref_edit<-mut.ref[,c("Sample","CHROM","POS","REF","ALT")]
for (sample in samples){
setwd("Tumour Mutation Signatures")
mut.ref2<-mut.ref_edit[mut.ref_edit$Sample==sample,]
mut.ref2<-unique(mut.ref2)
mutNum <- nrow(mut.ref2)
options(scipen = 999)
s <- paste("The number of variants in sample", sample, "used for mutation signature analysis is", mutNum)
t <- paste("The minimum TUMOUR.PMCAD in sample", sample, "used for filtering is", min(unique(f2$TUMOUR.PMCAD)), "and the minimum figure for the variants used for mutation signature analysis is", min(unique(f6$TUMOUR.PMCAD)))
u <- paste("The minimum TUMOUR.PMCDP in sample", sample, "used for filtering is", min(unique(f3$TUMOUR.PMCDP)), "and the minimum figure for the variants used for mutation signature analysis is", min(unique(f6$TUMOUR.PMCDP)))
v <- paste("The minimum TUMOUR.PMCFREQ in sample", sample, "used for filtering is", min(unique(f4$TUMOUR.PMCFREQ)), "and the minimum figure for the variants used for mutation signature analysis is", min(unique(f6$TUMOUR.PMCFREQ)))
w <- paste("The maximuum GnomAD_v2.1_non_cancer_AF in sample", sample, "used for filtering is", max(unique(f5$GnomAD_v2.1_non_cancer_AF)), "and the maximum figure for the variants used for mutation signature analysis is", max(unique(f6$GnomAD_v2.1_non_cancer_AF)))
x <- paste("The maximum GnomAD_v3_AF in sample", sample, "used for filtering and for the variants used for mutation signature analysis is", max(unique(f6$GnomAD_v3_AF)))
parameters <- print(c(s,t,u,v,w,x))
options(scipen = 0)
sigs.input = run_deconstructSigs(mut.ref2)
sigs.input = run_deconstructSigs(mut.ref2,
signatures.ref = SBS.COSMIC.exome.v3.may2019.HGSOvCa)
sigs.input = run_deconstructSigs(mut.ref2,
signatures.ref = signatures.cosmic)
setwd(sample)
mut.ref3 = mut.ref2[,c(3:5)]
mut.ref.chr = tibble(gsub("[chr]","", mut.ref2$CHROM))
bind_cols(mut.ref.chr,mut.ref3) %>% write_tsv(path=paste(sample,"unique_mutations_forSignal.tsv",sep="_"),col_names=FALSE)
results = run_sigs(data=mut.ref2, bootstrap.reps = 100, plot = TRUE, plot.file = paste(sample,"_signatures.pdf",sep=""))
write.table(results$exposures, file=paste(sample,"signatures_exposures.tsv",sep="_"), sep='\t', col.names=T, row.names=F, quote=F)
write.table(results$errors, file=paste(sample,"signatures_errors.tsv",sep="_"), sep='\t', col.names=T, row.names=F, quote=F)
write.table(results$exposures.bootstrap, file=paste(sample,"signatures_exposures_bootstraps.tsv",sep="_"), sep='\t', col.names=T, row.names=F, quote=F)
write.table(results$errors.bootstrap, file=paste(sample,"signatures_errors.bootstaps.tsv",sep="_"), sep='\t', col.names=T, row.names=F, quote=F)
write.table(parameters,file=paste(sample,"_parameters.txt",sep=""), quote=F,row.names = F,sep="\t")
}
LS0tCnRpdGxlOiAiVGhlc2lzIFR1bW91ciBFeG9tZSBNdXRhdGlvbmFsIFNpZ25hdHVyZSBBbmFseXNpcyBTY3JpcHQiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCkFMTCBJTVBPUlRFRCBGSUxFUyBNVVNUIEJFIElOIFRIRSBTQU1FIERJUkVDVE9SWSBBUyBUSElTIFNDUklQVAoKTG9hZCByZXF1aXJlZCBwYWNrYWdlcwpgYGB7cn0KbGlicmFyeSgidGlkeXZlcnNlIikKbGlicmFyeSgiR2Vub21lSW5mb0RiIikKbGlicmFyeSgiZGVjb25zdHJ1Y3RTaWdzIikKbGlicmFyeSgiQlNnZW5vbWUuSHNhcGllbnMuVUNTQy5oZzE5IikKbGlicmFyeSgiUkNvbG9yQnJld2VyIikKbGlicmFyeSgibm5scyIpCmxpYnJhcnkoIlNpZ25hdHVyZUVzdGltYXRpb24iKQpsaWJyYXJ5KCJyZXNoYXBlMiIpCmxpYnJhcnkoInNpZ2xhc3NvIikKbGlicmFyeSgicGFyYWxsZWwiKQpsaWJyYXJ5KCJncmlkIikKbGlicmFyeSgiZ3JpZEV4dHJhIikKYGBgCgpDcmVhdGUgbXV0YXRpb24gc2lnbmF0dXJlIGZ1bmN0aW9ucwpgYGB7cn0KcnVuX2RlY29uc3RydWN0U2lncyA9IGZ1bmN0aW9uKHgsIG91dC5kaXIgPSAiLiIsIHNpZ25hdHVyZXMucmVmID0gc2lnbmF0dXJlcy5leG9tZS5jb3NtaWMudjMubWF5MjAxOSwgYWxsLmNvbG9ycyA9IE5VTEwsIHNpZy50eXBlID0gJ1NCUycsIGJzZyA9IE5VTEwsIC4uLil7CiAgCiAgIyB4ID0gZGF0YS5mcmFtZSB3aXRoIGNvbHVtbnMgQ0hST00sIFBPUywgUkVGLCBBTFQgYW5kLCBvcHRpb25hbGx5LCBTYW1wbGUKICAjIHNpZ25hdHVyZXMucmVmID0gb25lIG9mIHNpZ25hdHVyZXMubmF0dXJlMjAxMywgc2lnbmF0dXJlcy5leG9tZS5jb3NtaWMudjMubWF5MjAxOSwKICAjICAgICAgICAgICAgICAgICAgc2lnbmF0dXJlcy5nZW5vbWUuY29zbWljLnYzLm1heTIwMTksIHNpZ25hdHVyZXMuZGJzLmNvc21pYy52My5tYXkyMDE5LAogICMgICAgICAgICAgICAgICAgICBzaWduYXR1cmVzLmNvc21pYwogICMgLi4uID0gYXJndW1lbnRzIHRvIHBhc3MgdG8gd2hpY2hTaWduYXR1cmVzKCkKICBpZighICJTYW1wbGUiICVpbiUgY29sbmFtZXMoeCkpewogICAgeCRTYW1wbGUgPSAiU2FtcGxlIgogICAgc2FtcGxlcyA9ICJTYW1wbGUiCiAgfSBlbHNlIHsKICAgIHNhbXBsZXMgPSB1bmlxdWUoeCRTYW1wbGUpCiAgfQogIAogICMgU2FtZSBjb2xvdXJzIGFzIGJlZm9yZSBidXQgZGlmZmVyZW50IHNpZ25hdHVyZSBuYW1lcwogIGlmKGlzLm51bGwoYWxsLmNvbG9yc1sxXSkpewogICAgYWxsLmNvbG9ycyA8LSBjKCIjMDIzRkE1IiwgIiMwMjNGQTUiLCAiIzdEODdCOSIsICIjQkVDMUQ0IiwgCiAgICAgICAgICAgICAgICAgICAgIiNENkJDQzAiLCAiI0JCNzc4NCIsICJnb2xkIiwgIiM0QTZGRTMiLCAiIzg1OTVFMSIsICIjQjVCQkUzIiwgCiAgICAgICAgICAgICAgICAgICAgIiNFNkFGQjkiLCAiI0UwN0I5MSIsICIjRDMzRjZBIiwgIiMxMUM2MzgiLCAiIzhERDU5MyIsIAogICAgICAgICAgICAgICAgICAgICIjQzZERUM3IiwgIiNFQUQzQzYiLCAiI0YwQjk4RCIsICIjRUY5NzA4IiwgIiMwRkNGQzAiLCAKICAgICAgICAgICAgICAgICAgICAiIzlDREVENiIsICIjRDVFQUU3IiwgIiNGM0UxRUIiLCAiI0Y2QzRFMSIsICIjRjc5Q0Q0IiwgCiAgICAgICAgICAgICAgICAgICAgIiM4NjYwOTciLCAiIzAwODk0MSIsICIjQTMwMDU5IiwgIiNGNkM0RTEiLCAiI0Y3OUNENCIsIAogICAgICAgICAgICAgICAgICAgICIjODY2MDk3IiwgIiMwMDg5NDEiLCAiI0EzMDA1OSIsICIjMDA4MDgwIiwgIiM4QjAwMDAiLCAKICAgICAgICAgICAgICAgICAgICAiI0Y0QTQ2MCIsICIjNjYzMzk5IikjLCAiIzcwNjU2MyIpCiAgfQogIAogIG9sZC5zaWdzIDwtIGMoIlNpZ25hdHVyZS4xIiwgIlNpZ25hdHVyZS4xQSIsICJTaWduYXR1cmUuMUIiLCAKICAgICAgICAgICAgICAgICJTaWduYXR1cmUuMiIsICJTaWduYXR1cmUuMyIsICJTaWduYXR1cmUuNCIsICJTaWduYXR1cmUuNSIsIAogICAgICAgICAgICAgICAgIlNpZ25hdHVyZS42IiwgIlNpZ25hdHVyZS43IiwgIlNpZ25hdHVyZS44IiwgIlNpZ25hdHVyZS45IiwgCiAgICAgICAgICAgICAgICAiU2lnbmF0dXJlLjEwIiwgIlNpZ25hdHVyZS4xMSIsICJTaWduYXR1cmUuMTIiLCAiU2lnbmF0dXJlLjEzIiwgCiAgICAgICAgICAgICAgICAiU2lnbmF0dXJlLjE0IiwgIlNpZ25hdHVyZS4xNSIsICJTaWduYXR1cmUuMTYiLCAiU2lnbmF0dXJlLjE3IiwgCiAgICAgICAgICAgICAgICAiU2lnbmF0dXJlLjE4IiwgIlNpZ25hdHVyZS4xOSIsICJTaWduYXR1cmUuMjAiLCAiU2lnbmF0dXJlLjIxIiwgCiAgICAgICAgICAgICAgICAiU2lnbmF0dXJlLlIxIiwgIlNpZ25hdHVyZS5SMiIsICJTaWduYXR1cmUuUjMiLCAiU2lnbmF0dXJlLlUxIiwgCiAgICAgICAgICAgICAgICAiU2lnbmF0dXJlLlUyIiwgIlNpZ25hdHVyZS4yMiIsICJTaWduYXR1cmUuMjMiLCAiU2lnbmF0dXJlLjI0IiwgCiAgICAgICAgICAgICAgICAiU2lnbmF0dXJlLjI1IiwgIlNpZ25hdHVyZS4yNiIsICJTaWduYXR1cmUuMjciLCAiU2lnbmF0dXJlLjI4IiwgCiAgICAgICAgICAgICAgICAiU2lnbmF0dXJlLjI5IiwgIlNpZ25hdHVyZS4zMCIsICJ1bmtub3duIikKICAKICBzaWdzLmlucHV0ID0gbXV0LnRvLnNpZ3MuaW5wdXQobXV0LnJlZiA9IHgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZS5pZCA9ICJTYW1wbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaHIgPSAiQ0hST00iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3MgPSAiUE9TIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVmID0gIlJFRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsdCA9ICJBTFQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWcudHlwZSA9IHNpZy50eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBic2cgPSBic2cpCiAgCiAgCiAgCiAgCiAgZm9yKHNhbXBsZSBpbiBzYW1wbGVzKXsKICAgIHRyeUNhdGNoKGV4cHIgPSB7CiAgICAgIGNhdChwYXN0ZTAoIlByb2Nlc3NpbmcgIiwgc2FtcGxlLCAiLi4uXG4iKSkKICAgICAgaWYobGVuZ3RoKHNhbXBsZXMpID09IDEgJiBzYW1wbGUgPT0gIlNhbXBsZSIpewogICAgICAgIHJlc3VsdHMuZGlyID0gb3V0LmRpcgogICAgICB9IGVsc2UgewogICAgICAgIHJlc3VsdHMuZGlyID0gZmlsZS5wYXRoKG91dC5kaXIsIHNhbXBsZSkKICAgICAgfQogICAgICAKICAgICAgcHJlZml4ID0gcGFzdGUoc2FtcGxlLGRlcGFyc2Uoc3Vic3RpdHV0ZShzaWduYXR1cmVzLnJlZikpLCBzZXA9Il8iKQogICAgICAKICAgICAgaWYoIWRpci5leGlzdHMocmVzdWx0cy5kaXIpKXtkaXIuY3JlYXRlKHJlc3VsdHMuZGlyLCByZWN1cnNpdmUgPSBUKX0KICAgICAgZXJyb3IgPSBmaWxlKGZpbGUucGF0aChyZXN1bHRzLmRpciwgcGFzdGUwKHByZWZpeCwgIl9lcnJvci50eHQiKSksIG9wZW49Ind0IikKICAgICAgc2luayhlcnJvciwgdHlwZT0ibWVzc2FnZSIpCiAgICAgIAogICAgICBzaWdzID0gd2hpY2hTaWduYXR1cmVzKHR1bW9yLnJlZiA9IHNpZ3MuaW5wdXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmF0dXJlcy5yZWYgPSBzaWduYXR1cmVzLnJlZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGUuaWQgPSBzYW1wbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dHMubmVlZGVkID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuLi4pCiAgICAgIAogICAgICAjcGRmKGZpbGUucGF0aChyZXN1bHRzLmRpciwgcGFzdGUwKHByZWZpeCwgIl9wbG90cy5wZGYiKSkpCiAgICAgIAogICAgICAjIFBsb3R0aW5nCiAgICAgIHBuZyhmaWxlLnBhdGgocmVzdWx0cy5kaXIsIHBhc3RlMChwcmVmaXgsICJfc2lnbmF0dXJlLnBuZyIpKSwKICAgICAgICAgIHdpZHRoPTEyMDAsIGhlaWdodD0xMDAwLCB1bml0cz0icHgiLCBwb2ludHNpemU9MjApCiAgICAgIHBsb3RTaWduYXR1cmVzKHNpZ3MpCiAgICAgIGRldi5vZmYoKQogICAgICAKICAgICAgbmV3LnNpZ3MgPSBzZXRkaWZmKGNvbG5hbWVzKHNpZ3Mkd2VpZ2h0cylbc2lncyR3ZWlnaHRzWzEsXSA+IDBdLCBvbGQuc2lncykKICAgICAgcG5nKGZpbGUucGF0aChyZXN1bHRzLmRpciwgcGFzdGUwKHByZWZpeCwgIl9waWUucG5nIikpLAogICAgICAgICAgd2lkdGg9OTYwLCBoZWlnaHQ9OTYwLCB1bml0cz0icHgiLCBwb2ludHNpemU9MjQpCiAgICAgIGlmKGxlbmd0aChuZXcuc2lncykgPiAwKXsKICAgICAgICBtYWtlUGllKHNpZ3MsIGFkZC5jb2xvcj11bmlxdWUoYWxsLmNvbG9ycylbMTpsZW5ndGgobmV3LnNpZ3MpXSkKICAgICAgfSBlbHNlIHsKICAgICAgICBtYWtlUGllKHNpZ3MpCiAgICAgIH0KICAgICAgZGV2Lm9mZigpCiAgICAgIAogICAgICAjIFNhdmUgb3V0cHV0CiAgICAgIHdyaXRlLnRhYmxlKHggJT4lIGRwbHlyOjpmaWx0ZXIoU2FtcGxlID09IHNhbXBsZSkgJT4lCiAgICAgICAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcighZHVwbGljYXRlZChwYXN0ZShDSFJPTSwgUE9TLCBSRUYsIEFMVCkpKSwKICAgICAgICAgICAgICAgICAgZmlsZSA9IGZpbGUucGF0aChyZXN1bHRzLmRpciwgcGFzdGUwKHByZWZpeCwgIl91bmlxdWVfbXV0YXRpb25zLnR4dCIpKSwKICAgICAgICAgICAgICAgICAgc2VwPSJcdCIsIHF1b3RlPUYsIHJvdy5uYW1lcz1GLCBjb2wubmFtZXM9VCkKICAgICAgCiAgICAgIAogICAgICB3cml0ZS50YWJsZShkYXRhLmZyYW1lKFNpZ25hdHVyZSA9IGNvbG5hbWVzKHNpZ3Mkd2VpZ2h0cyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUGVyY2VudCA9IHJvdW5kKHVubGlzdChzaWdzJHdlaWdodHMpKjEwMCwgZGlnaXRzPTIpKSwKICAgICAgICAgICAgICAgICAgZmlsZSA9IGZpbGUucGF0aChyZXN1bHRzLmRpciwgcGFzdGUwKHByZWZpeCwgIl9wZXJjZW50YWdlcy50eHQiKSksCiAgICAgICAgICAgICAgICAgIHF1b3RlPUYsIHNlcD0iXHQiLCBjb2wubmFtZXM9VCwgcm93Lm5hbWVzPUYpCiAgICAgIAogICAgICB3cml0ZS50YWJsZShzaWdzLmlucHV0W3NhbXBsZSxdLAogICAgICAgICAgICAgICAgICBmaWxlID0gZmlsZS5wYXRoKHJlc3VsdHMuZGlyLCBwYXN0ZTAocHJlZml4LCAiX3RyaW51Y2xlb3RpZGVzLnR4dCIpKSwKICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzPUYsIGNvbC5uYW1lcz1ULCBzZXA9Ilx0IiwgcXVvdGU9RikKICAgICAgCiAgICB9LCBmaW5hbGx5ID0gewogICAgICBjYXQocGFzdGUwKCJGaW5pc2hlZCAiLCBzYW1wbGUsICJcbiIpKQogICAgfSkKICAgIAogIH0KICBjYXQoIkZpbmlzaGVkIVxuIikKfQoKaWQuc2lnbmF0dXJlLnBhdGggPSAic2lnUHJvZmlsZXJfSURfc2lnbmF0dXJlcy5jc3YiCgpzaWduYXR1cmVzLmlkLmNvc21pYy52My5tYXkyMDE5ID0gYXMuZGF0YS5mcmFtZSh0KHJlYWQuZGVsaW0oaWQuc2lnbmF0dXJlLnBhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXA9IiwiLCBoZWFkZXI9VCwgYXMuaXM9VCwgcm93Lm5hbWVzPTEpKSkKCgojIE5vcm1hbGlzZXMgdmFyaWFudHMgdG8gU0JTLCBEQlMgYW5kIGluZGVscyBvbmx5IChyZW1vdmVzIHN1YnN0aXR1dGlvbnMgPj0gMyBCUCBpbiBsZW5ndGgpCm5vcm1fc2JzX3RvX2RicyA9IGZ1bmN0aW9uKHgsIGNocj0iQ0hST00iLCBwb3M9IlBPUyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZj0iUkVGIiwgYWx0PSJBTFQiLCBzYW1wbGUgPSAiU2FtcGxlIil7CiAgIyBjb2xuYW1lcyBDSFJPTSwgUE9TLCBSRUYsIEFMVCwgKG9wdGlvbmFsKSBTYW1wbGUKICAKICBpZighIHNhbXBsZSAlaW4lIGNvbG5hbWVzKHgpKXsKICAgIHhbLHNhbXBsZV0gPSAiU2FtcGxlIgogICAgcm0uc2FtcGxlID0gVFJVRSAjIGRvIG5vdCByZXR1cm4gc2FtcGxlIG5hbWUsIGFzc3VtZSBhbGwgdmFyaWFudHMgc2FtZSBzYW1wbGUKICB9IGVsc2UgewogICAgcm0uc2FtcGxlID0gRkFMU0UKICB9CiAgCiAgb3JpZy5jb2xzID0gYyhzYW1wbGUsIGNociwgcG9zLCByZWYsIGFsdCkKICB4ID0geFssb3JpZy5jb2xzXQogIGNvbHMudXNlID0gYygiU2FtcGxlIiwgIkNIUk9NIiwgIlBPUyIsICJSRUYiLCAiQUxUIikKICBjb2xuYW1lcyh4KSA9IGNvbHMudXNlCiAgCiAgeCA9IHggJT4lIGFycmFuZ2UoU2FtcGxlLCBDSFJPTSwgUE9TKSAlPiUKICAgIGZpbHRlcighZHVwbGljYXRlZChwYXN0ZShTYW1wbGUsIENIUk9NLCBQT1MsIFJFRiwgQUxUKSkpICU+JQogICAgbXV0YXRlKEFscmVhZHkuREJTID0gbmNoYXIoUkVGKSA9PSAyICYgbmNoYXIoQUxUKSA9PSAyKQogIAogIAogIGluZGVscyA9IHggJT4lIGZpbHRlcihuY2hhcihSRUYpICE9IG5jaGFyKEFMVCkpCiAgc3Vic3RpdHV0aW9ucyA9IHggJT4lIGZpbHRlcigobmNoYXIoUkVGKSA9PSAxICYgbmNoYXIoQUxUKSA9PSAxKSB8IEFscmVhZHkuREJTKQogIAogIGRicyA9IHN1YnN0aXR1dGlvbnMgJT4lIGZpbHRlcihuY2hhcihSRUYpID09IDEgJiBuY2hhcihBTFQpID09IDEpICU+JQogICAgZ3JvdXBfYnkoU2FtcGxlLCBDSFJPTSkgJT4lCiAgICBtdXRhdGUoZGlmZjEgPSBhYnMoYyhoZWFkKFBPUywgLTEpIC0gdGFpbChQT1MsIC0xKSwgMCkpLCBkaWZmMiA9IGFicyhjKDAsIHRhaWwoUE9TLCAtMSkgLSBoZWFkKFBPUywgLTEpKSkpICU+JQogICAgZmlsdGVyKChkaWZmMSA9PSAxIHwgZGlmZjIgPT0gMSkgJiAhIChkaWZmMSA9PSAxICYgZGlmZjIgPT0gMSkpICU+JSAjIHJlbW92ZSBhbnl0aGluZyA+IDIgc3Vic3RpdHV0aW9ucyBhcyBwcm9iYWJsZSBpbmRlbCBvciBiYWQgbWFwcGluZwogICAgbXV0YXRlKGRpZmYxID0gYWJzKGMoaGVhZChQT1MsIC0xKSAtIHRhaWwoUE9TLCAtMSksIDApKSwgZGlmZjIgPSBhYnMoYygwLCB0YWlsKFBPUywgLTEpIC0gaGVhZChQT1MsIC0xKSkpKSAlPiUKICAgIGZpbHRlcihkaWZmMSA9PSAxIHwgZGlmZjIgPT0gMSkgJT4lCiAgICBzdW1tYXJpc2UoUE9TPW1pbihQT1MpLCBSRUY9cGFzdGUwKFJFRiwgY29sbGFwc2U9IiIpLCBBTFQgPSBwYXN0ZTAoQUxULCBjb2xsYXBzZT0iIikpICU+JSB1bmdyb3VwKCkKICAKICBzYnMgPSBzdWJzdGl0dXRpb25zICU+JSBmaWx0ZXIobmNoYXIoUkVGKSA9PSAxICYgbmNoYXIoQUxUKSA9PSAxKSAlPiUKICAgIGdyb3VwX2J5KFNhbXBsZSwgQ0hST00pICU+JQogICAgbXV0YXRlKGRpZmYxID0gYWJzKGMoaGVhZChQT1MsIC0xKSAtIHRhaWwoUE9TLCAtMSksIDApKSwgZGlmZjIgPSBhYnMoYygwLCB0YWlsKFBPUywgLTEpIC0gaGVhZChQT1MsIC0xKSkpKSAlPiUKICAgIGZpbHRlcihkaWZmMSAhPSAxICYgZGlmZjIgIT0gMSkgICU+JSB1bmdyb3VwKCkjIHJlbW92ZSBhbnl0aGluZyB3aXRoIGFkamFjZW50IHZhcmlhbnRzCiAgCiAgaWYoc3VtKHN1YnN0aXR1dGlvbnMkQWxyZWFkeS5EQlMpID4gMCl7CiAgICBkYnMgPSByYmluZChkYnMsIHN1YnN0aXR1dGlvbnNbd2hpY2goc3Vic3RpdHV0aW9ucyRBbHJlYWR5LkRCUyksY29scy51c2VdKQogIH0KICAKICAKICAKICByZXMgPSBOVUxMCiAgaWYobnJvdyhpbmRlbHMpID4gMCl7CiAgICByZXMgPSByYmluZChyZXMsIGluZGVsc1ssY29scy51c2VdKQogIH0KICBpZihucm93KGRicykgPiAwKXsKICAgIHJlcyA9IHJiaW5kKHJlcywgZGJzWyxjb2xzLnVzZV0pCiAgfQogIGlmKG5yb3coc2JzKSA+IDApewogICAgcmVzID0gcmJpbmQocmVzLCBzYnNbLGNvbHMudXNlXSkKICB9CiAgcmVzID0gYXMuZGF0YS5mcmFtZShyZXMpICU+JSBhcnJhbmdlKFNhbXBsZSwgQ0hST00sIFBPUykKICByZXMgPSByZXNbLGNvbHMudXNlXQogIGNvbG5hbWVzKHJlcykgPSBvcmlnLmNvbHMKICByZXR1cm4ocmVzKQogIAp9CgojIEFzc2lnbnMgSW5EZWwgbXV0YXRpb24gdHlwZSAoc2VlIGNvc21pYyBpbmRlbCBzaWduYXR1cmVzKQpjYWxjLmlkLnR5cGUgPSBmdW5jdGlvbihyLGEsYyl7CiAgcmkgPSAobmNoYXIoYykgJS8lIDIpCiAgaWYobmNoYXIoYSkgPT0gMSl7CiAgICAjIGRlbGV0aW9uCiAgICBpZC5sZW5ndGggPSBuY2hhcihyKSAtIG5jaGFyKGEpCiAgICBpZihzdWJzdHIoYywgcmksIHJpK2lkLmxlbmd0aCkgIT0gcil7CiAgICAgIHJldHVybihOQSkKICAgIH0KICAgIAogICAgaWQudHlwZSA9ICJkZWwiCiAgICBpZC5zZXEgPSBzdWJzdHIociwgMiwgbmNoYXIocikpCiAgICBpZihzdWJzdHIociwyLDIpICVpbiUgYygiQSIsICJHIikpewogICAgICBjID0gcmV2LnN0cmFuZChjKQogICAgICBpZC5zZXEgPSByZXYuc3RyYW5kKGlkLnNlcSkKICAgICAgcmkgPSByaSAtIChpZC5sZW5ndGggLSAxKQogICAgICB3aGlsZShzdWJzdHIoYywgcmkgLSBpZC5sZW5ndGggKyAxLCByaSkgPT0gaWQuc2VxKXsKICAgICAgICAjIGxlZnQgbm9ybWFsaXNlIHJldmVyc2Ugc3RyYW5kCiAgICAgICAgcmkgPSByaSAtIGlkLmxlbmd0aAogICAgICB9CiAgICAgICNyID0gc3Vic3RyKGMsIHJpLCByaSArIGlkLmxlbmd0aCkKICAgIH0KICAgIAogICAgCiAgICAKICB9IGVsc2UgewogICAgaWQubGVuZ3RoID0gbmNoYXIoYSkgLSBuY2hhcihyKQogICAgaWQudHlwZSA9ICJpbnMiCiAgICBpZC5zZXEgPSBzdWJzdHIoYSwyLCBuY2hhcihhKSkKICAgIGlmKHN1YnN0cihpZC5zZXEsMSwxKSAlaW4lIGMoIkEiLCAiRyIpKXsKICAgICAgYyA9IHJldi5zdHJhbmQoYykKICAgICAgcmkgPSByaSArIDEKICAgICAgaWQuc2VxID0gcmV2LnN0cmFuZChpZC5zZXEpCiAgICAgIAogICAgfQogIH0KICB3aGlsZShzdWJzdHIoYywgcmkgLSBpZC5sZW5ndGggKyAxLCByaSkgPT0gaWQuc2VxKXsKICAgICMgbGVmdCBub3JtYWxpc2UKICAgIHJpID0gcmkgLSBpZC5sZW5ndGgKICB9CiAgaWYocmkgPCAxKXsKICAgIHJpID0gcmkgKyBpZC5sZW5ndGgKICB9CiAgCiAgCiAgCiAgbi5yZXBzID0gMAogIHdoaWxlKHN1YnN0cihjLCByaSsxK2lkLmxlbmd0aCoobi5yZXBzKSwgcmkraWQubGVuZ3RoKihuLnJlcHMrMSkpID09IGlkLnNlcSl7CiAgICBuLnJlcHMgPSBuLnJlcHMgKyAxCiAgfQogIAogIGlmKGlkLnR5cGUgPT0gImRlbCIpewogICAgbi5yZXBzID0gbi5yZXBzIC0gMSAjIHJlbW92ZSBkZWwgaXRzZWxmCiAgICBpZihpZC5sZW5ndGggPT0gMSl7CiAgICAgIGlmKHN1YnN0cihpZC5zZXEsIDEsIDEpID09ICJDIil7CiAgICAgICAgc2lnLmluZGV4ID0gMSArIG1pbihuLnJlcHMsIDUpCiAgICAgIH0gZWxzZSB7CiAgICAgICAgc2lnLmluZGV4ID0gNyArIG1pbihuLnJlcHMsIDUpCiAgICAgIH0KICAgIH0gZWxzZSB7CiAgICAgIGlmKG4ucmVwcyA9PSAwKXsKICAgICAgICAjIGNoZWNrIG1pY3JvaG9tb2xvZ3kKICAgICAgICBuLmhvbSA9IDAKICAgICAgICAjIGZvcndhcmQKICAgICAgICB3aGlsZShzdWJzdHIoYywgcmkrMStpZC5sZW5ndGgsIHJpKzEraWQubGVuZ3RoK24uaG9tKSA9PSBzdWJzdHIoaWQuc2VxLCAxLCAxK24uaG9tKSl7CiAgICAgICAgICBuLmhvbSA9IG4uaG9tICsgMQogICAgICAgIH0KICAgICAgICAjIHJldmVyc2UKICAgICAgICBuLmhvbS5yID0gMAogICAgICAgIHdoaWxlKHN1YnN0cihjLCByaSAtIG4uaG9tLnIsIHJpKSA9PSBzdWJzdHIoaWQuc2VxLCBpZC5sZW5ndGggLSBuLmhvbS5yLCBpZC5sZW5ndGgpKXsKICAgICAgICAgIG4uaG9tLnIgPSBuLmhvbS5yICsgMQogICAgICAgIH0KICAgICAgICBuLmhvbSA9IG1heChuLmhvbSwgbi5ob20ucikKICAgICAgICAKICAgICAgICBpZihuLmhvbSA9PSAwKXsKICAgICAgICAgIHNpZy5pbmRleCA9IDI1ICsgbWluKG4ucmVwcywgNSkgKyA2KihtaW4oNSwgaWQubGVuZ3RoKS0yKQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICBzaWcuaW5kZXggPSA3MyArIG1pbihuLmhvbSwgNSkgLSAxICsgc3VtKDA6KG1pbihpZC5sZW5ndGgsIDUpIC0gMikpCiAgICAgICAgfQogICAgICB9IGVsc2UgewogICAgICAgIHNpZy5pbmRleCA9IDI1ICsgbWluKG4ucmVwcywgNSkgKyA2KihtaW4oNSwgaWQubGVuZ3RoKS0yKQogICAgICB9CiAgICB9CiAgfSBlbHNlIHsKICAgIGlmKGlkLmxlbmd0aCA9PSAxKXsKICAgICAgaWYoc3Vic3RyKGlkLnNlcSwgMSwgMSkgPT0gIkMiKXsKICAgICAgICBzaWcuaW5kZXggPSAxMyArIG1pbihuLnJlcHMsIDUpCiAgICAgIH0gZWxzZSB7CiAgICAgICAgc2lnLmluZGV4ID0gMTkgKyBtaW4obi5yZXBzLCA1KQogICAgICB9CiAgICB9IGVsc2UgewogICAgICBzaWcuaW5kZXggPSA0OSArIG1pbihuLnJlcHMsIDUpICsgNioobWluKDUsIGlkLmxlbmd0aCkgLSAyKQogICAgfQogIH0KICAKICByZXR1cm4oc2lnLmluZGV4KQp9CgoKcmV2LnN0cmFuZCA9IGZ1bmN0aW9uKHgpewogIHJldi5iYXNlcyA9IGMoIkEiID0gIlQiLCAiVCIgPSAiQSIsICJHIiA9ICJDIiwgIkMiID0gIkciLCAiTiIgPSAiTiIpCiAgeSA9IHJldihyZXYuYmFzZXNbdW5saXN0KHN0cnNwbGl0KHgsICIiKSldKQogIGlmKGxlbmd0aCh4KSA9PSAxKXtyZXR1cm4ocGFzdGUoeSwgY29sbGFwc2U9IiIpKX0gZWxzZSB7cmV0dXJuKHkpfQp9CgojIENyZWF0ZXMgaW5wdXQgbXV0YXRpb24gbWF0cmljZXMgZm9yIGVhY2ggbXV0YXRpb24gdHlwZQptdXQudG8uc2lncy5pbnB1dC5hbGx0eXBlcyA9IGZ1bmN0aW9uKG11dC5yZWYsIHNhbXBsZS5pZCA9ICJTYW1wbGUiLCBjaHIgPSAiY2hyIiwgcG9zID0gInBvcyIsIAogICAgICAgICAgICAgICAgICAgICAgcmVmID0gInJlZiIsIGFsdCA9ICJhbHQiLCBic2cgPSBOVUxMLCBzaWcudHlwZXMgPSBjKCJTQlMiLCAiREJTIiwgIklEIiksIGRic190YWJsZSA9IGRic19wb3NzaWJsZSkgewogIHJlc3VsdHMgPSBsYXBwbHkoc2lnLnR5cGVzLCBmdW5jdGlvbihzaWcudHlwZSl7CiAgICBpZihzaWcudHlwZSA9PSAiSUQiKXsKICAgICAgIyBJbkRlbCBzdHVmZiBoZXJlCiAgICAgIHNhbXBsZXMgPSBmYWN0b3IobXV0LnJlZlssc2FtcGxlLmlkXSkKICAgICAgbXV0LnJlZiA9IG11dC5yZWZbbmNoYXIobXV0LnJlZlsscmVmXSkgIT0gMSB8IG5jaGFyKG11dC5yZWZbLGFsdF0pICE9IDEsXQogICAgICAKICAgICAgCiAgICAgIAogICAgICByZXMgPSBtYXRyaXgoMCwgbnJvdyA9IGxlbmd0aChsZXZlbHMoc2FtcGxlcykpLCBuY29sPTgzKQogICAgICByb3duYW1lcyhyZXMpID0gbGV2ZWxzKHNhbXBsZXMpCiAgICAgIAogICAgICAKICAgICAgaWYgKGV4aXN0cygibXV0LnJlZiIsIG1vZGUgPSAibGlzdCIpKSB7CiAgICAgICAgbXV0LmZ1bGwgPC0gbXV0LnJlZgogICAgICB9IGVsc2UgewogICAgICAgIGlmIChmaWxlLmV4aXN0cyhtdXQucmVmKSkgewogICAgICAgICAgbXV0LmZ1bGwgPC0gdXRpbHM6OnJlYWQudGFibGUobXV0LnJlZiwgc2VwID0gIlx0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBhcy5pcyA9IEZBTFNFLCBjaGVjay5uYW1lcyA9IEZBTFNFKQogICAgICAgIH0KICAgICAgICBlbHNlIHsKICAgICAgICAgIHN0b3AoIm11dC5yZWYgaXMgbmVpdGhlciBhIGZpbGUgbm9yIGEgbG9hZGVkIGRhdGEgZnJhbWUiKQogICAgICAgIH0KICAgICAgfQogICAgICAKICAgICAgaWYobnJvdyhtdXQuZnVsbCkgPT0gMCl7CiAgICAgICAgcmV0dXJuKHJlcykKICAgICAgfQogICAgICAKICAgICAgbXV0IDwtIG11dC5mdWxsWywgYyhzYW1wbGUuaWQsIGNociwgcG9zLCByZWYsIGFsdCldCiAgICAgIG11dFssIGNocl0gPC0gZmFjdG9yKG11dFssIGNocl0pCiAgICAgIGxldmVscyhtdXRbLCBjaHJdKSA8LSBzdWIoIl4oWzAtOVhZXSkiLCAiY2hyXFwxIiwgbGV2ZWxzKG11dFssIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hyXSkpCiAgICAgIGxldmVscyhtdXRbLCBjaHJdKSA8LSBzdWIoIl5NVCIsICJjaHJNIiwgbGV2ZWxzKG11dFssIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hyXSkpCiAgICAgIGxldmVscyhtdXRbLCBjaHJdKSA8LSBzdWIoIl4oR0xbMC05XSspLlswLTldIiwgImNoclVuX1xcTFxcMSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyhtdXRbLCBjaHJdKSwgcGVybCA9IFQpCiAgICAgIGlmIChpcy5udWxsKGJzZykpIHsKICAgICAgICBic2cgPSBCU2dlbm9tZS5Ic2FwaWVucy5VQ1NDLmhnMTk6OkhzYXBpZW5zCiAgICAgIH0KICAgICAgdW5rbm93bi5yZWdpb25zIDwtIGxldmVscyhtdXRbLCBjaHJdKVt3aGljaCghKGxldmVscyhtdXRbLGNocl0pICVpbiUgR2Vub21lSW5mb0RiOjpzZXFuYW1lcyhic2cpKSldCiAgICAgIGlmIChsZW5ndGgodW5rbm93bi5yZWdpb25zKSA+IDApIHsKICAgICAgICB1bmtub3duLnJlZ2lvbnMgPC0gcGFzdGUodW5rbm93bi5yZWdpb25zLCBjb2xsYXBzZSA9ICIsICIpCiAgICAgICAgd2FybmluZyhwYXN0ZSgiQ2hlY2sgY2hyIG5hbWVzIC0tIG5vdCBhbGwgbWF0Y2ggYnNnIG9iamVjdDpcbiIsIAogICAgICAgICAgICAgICAgICAgICAgdW5rbm93bi5yZWdpb25zLCBzZXAgPSAiICIpKQogICAgICAgIG11dCA8LSBtdXRbbXV0WywgY2hyXSAlaW4lIEdlbm9tZUluZm9EYjo6c2VxbmFtZXMoYnNnKSwgCiAgICAgICAgICAgICAgICAgICBdCiAgICAgIH0KICAgICAgbXV0JGNvbnRleHQgPSBCU2dlbm9tZTo6Z2V0U2VxKGJzZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dFssIGNocl0sIG11dFssIHBvc10gLSA2KnBtYXgoMSwgbmNoYXIobXV0WyxyZWZdKSAtIDEsIG5jaGFyKG11dFssYWx0XSkgLSAxKSArIDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRbLCBwb3NdICsgNipwbWF4KDEsIG5jaGFyKG11dFsscmVmXSkgLSAxLCBuY2hhcihtdXRbLGFsdF0pIC0gMSkgKyAxLCBhcy5jaGFyYWN0ZXIgPSBUKQoKICAgICAgCiAgICAgIAogICAgICAKICAgICAgZm9yKGkgaW4gMTpucm93KG11dCkpewogICAgICAgIAogICAgICAgIHMgPSBtdXRbaSxzYW1wbGUuaWRdCiAgICAgICAgaiA9IG1hdGNoKHMsIGxldmVscyhzYW1wbGVzKSkKICAgICAgICBwID0gbXV0W2kscG9zXQogICAgICAgIHIgPSBtdXRbaSxyZWZdCiAgICAgICAgYSA9IG11dFtpLGFsdF0KICAgICAgICAjYyA9IHN0cnNwbGl0KG11dFtpLCJjb250ZXh0Il0sICIiKVtbMV1dCiAgICAgICAgYyA9IG11dFtpLCJjb250ZXh0Il0KICAgICAgICAKICAgICAgICBuID0gY2FsYy5pZC50eXBlKHIsIGEsIGMpCiAgICAgICAgaWYoaXMubmEobikpewogICAgICAgICAgY2F0KHBhc3RlMCgiVmFyaWFudCAiLCBwYXN0ZShtdXRbaSxjaHJdLCBwLCByLCBhLCBzZXA9Ii0iKSwgIiBkb2VzIG5vdCBtYXRjaCByZWZlcmVuY2UhIEl0IHdpbGwgYmUgaWdub3JlZC5cbiIpKQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICByZXNbaixuXSA9IHJlc1tqLG5dICsgMQogICAgICAgIH0KICAgICAgICAKICAgICAgICAKICAgICAgfQogICAgICByZXR1cm4ocmVzKQogICAgfSBlbHNlIHsKICAgICAgcmV0dXJuKG11dC50by5zaWdzLmlucHV0KG11dC5yZWYsIHNhbXBsZS5pZCwgY2hyLCBwb3MsIHJlZiwgYWx0LCBic2csIHNpZy50eXBlLCBkYnNfdGFibGUpKQogICAgfQogIH0pCiAgcmVzdWx0cyA9IHNldE5hbWVzKHJlc3VsdHMsIHNpZy50eXBlcykKICByZXR1cm4ocmVzdWx0cykKfQoKIyBOb24tbmVnYXRpdmUgbGVhc3Qgc3F1YXJlcyBtZXRob2QgZm9yIFNpZ25hdHVyZUVzdGltYXRpb24KZGVjb21wb3NlTk5MUyA9IGZ1bmN0aW9uKG0sIFAsIC4uLil7CiAgZXhwb3N1cmVzID0gY29lZihubmxzKFAsIG0pKQogIGV4cG9zdXJlcyA9IGV4cG9zdXJlcy9zdW0oZXhwb3N1cmVzKQogIHJldHVybihleHBvc3VyZXMpCn0KCiMgRGVjb25zdHJ1Y3RTaWdzIHdyYXBwZXIgZm9yIFNpZ25hdHVyZUVzdGltYXRpb24KZGVjb21wb3NlRGVTaWcgPSBmdW5jdGlvbihtLCBQLCAuLi4pewogIAogIG0gPSBhcy5kYXRhLmZyYW1lKHQobSkpCiAgcm93bmFtZXMobSkgPSAiU2FtcGxlIgogIFAgPSBhcy5kYXRhLmZyYW1lKHQoUCkpCiAgY29sbmFtZXMoUCkgPSBjb2xuYW1lcyhtKQogIHkgPSB3aGljaFNpZ25hdHVyZXModHVtb3IucmVmID0gbSwgc2lnbmF0dXJlcy5yZWYgPSBQKQogIGV4cG9zdXJlcyA9IGFzLm51bWVyaWMoeSR3ZWlnaHRzKQogICNleHBvc3VyZXMgPSBleHBvc3VyZXMvc3VtKGV4cG9zdXJlcykKICByZXR1cm4oZXhwb3N1cmVzKQp9CgojIFNpZ0xBU1NPIHdyYXBwZXIgZm9yIFNpZ25hdHVyZUVzdGltYXRpb24KZGVjb21wb3NlU2lnbGFzc28gPSBmdW5jdGlvbihtLCBQLCAuLi4pewogIGNvdW50cyA9IGxpc3QoLi4uKVtbImNvdW50cyJdXQogIGlmKCFpcy5udWxsKGNvdW50cykpewogICAgbSA9IG0qY291bnRzCiAgfQogIG0gPSBtYXRyaXgobSwgbmNvbD0xKQogIHN1Y2Nlc3MgPSBGQUxTRSAjIHJhbmRvbWx5IGZhaWxzIHNvbWV0aW1lcwogIHdoaWxlKCFzdWNjZXNzKXsKICAgIHRyeUNhdGNoKGV4cHIgPSB7CiAgICAgIHkgPSBzaWdsYXNzbyhzYW1wbGVfc3BlY3RydW0gPSBtLCBzaWduYXR1cmUgPSBQLCBwbG90PUYpCiAgICAgIHN1Y2Nlc3MgPSBUUlVFCiAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpe30sCiAgICBmaW5hbGx5ID0gewogICAgICBuZXh0CiAgICB9KQogIH0KICAKICBleHBvc3VyZXMgPSB5WywxXQogICNleHBvc3VyZXMgPSBleHBvc3VyZXMvc3VtKGV4cG9zdXJlcykKICByZXR1cm4oZXhwb3N1cmVzKQp9CgojIE11bHRpcHJvY2VzcyB2ZXJzaW9uIG9mIFNpZ25hdHVyZUVzdGltYXRpb246OmJvb3RzdHJhcFNpZ0V4cG9zdXJlcwpib290c3RyYXBTaWdFeHBvc3VyZXNNUCA9IGZ1bmN0aW9uIChtLCBQLCBSLCBtdXRhdGlvbi5jb3VudCA9IE5VTEwsIGRlY29tcG9zaXRpb24ubWV0aG9kID0gZGVjb21wb3NlUVAsIG51bS50aHJlYWRzID0gOCwKICAgICAgICAgIC4uLikgewogIFAgPSBhcy5tYXRyaXgoUCkKICBpZiAobGVuZ3RoKG0pICE9IG5yb3coUCkpIAogICAgc3RvcCgiTGVuZ3RoIG9mIHZlY3RvciAnbScgYW5kIG51bWJlciBvZiByb3dzIG9mIG1hdHJpeCAnUCcgbXVzdCBiZSB0aGUgc2FtZS4iKQogIGlmIChhbnkobmFtZXMobSkgIT0gcm93bmFtZXMoUCkpKSAKICAgIHN0b3AoIkVsZW1lbnRzIG9mIHZlY3RvciAnbScgYW5kIHJvd3Mgb2YgbWF0cml4ICdQJyBtdXN0IGhhdmUgdGhlIHNhbWUgbmFtZXMgKG11dGF0aW9ucyB0eXBlcykuIikKICBpZiAobmNvbChQKSA9PSAxKSAKICAgIHN0b3AoIk1hdHJpY2VzICdQJyBtdXN0IGhhdmUgYXQgbGVhc3QgMiBjb2x1bW5zIChzaWduYXR1cmVzKS4iKQogIGlmIChpcy5udWxsKG11dGF0aW9uLmNvdW50KSkgewogICAgaWYgKGFsbChTaWduYXR1cmVFc3RpbWF0aW9uOjo6aXMud2hvbGVudW1iZXIobSkpKSAKICAgICAgbXV0YXRpb24uY291bnQgPSBzdW0obSkKICAgIGVsc2Ugc3RvcCgiUGxlYXNlIHNwZWNpZnkgdGhlIHBhcmFtZXRlciAnbXV0YXRpb24uY291bnQnIGluIHRoZSBmdW5jdGlvbiBjYWxsIG9yIHByb3ZpZGUgbXV0YXRpb24gY291bnRzIGluIHBhcmFtZXRlciAnbScuIikKICB9CiAgbSA9IG0vc3VtKG0pCiAgSyA9IGxlbmd0aChtKQogIAogIAogIHJlcGxpY2F0ZU1QID0gZnVuY3Rpb24obiwgZXhwciwgbnVtLnRocmVhZHMgPSA4KXsKICAgIGFzLm1hdHJpeChkby5jYWxsKGNiaW5kLCBtY2xhcHBseShpbnRlZ2VyKG4pLCBldmFsLnBhcmVudChzdWJzdGl0dXRlKGZ1bmN0aW9uKC4uLikgZXhwcikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1jLmNvcmVzID0gbnVtLnRocmVhZHMpKSkKICB9CiAgZXh0cmEuYXJncyA9IGxpc3QoLi4uKQogIGV4cG9zdXJlcyA9IHJlcGxpY2F0ZU1QKFIsIHsKICAgIG11dGF0aW9uc19zYW1wbGVkID0gc2FtcGxlKHNlcShLKSwgbXV0YXRpb24uY291bnQsIHJlcGxhY2UgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2IgPSBtKQogICAgbV9zYW1wbGVkID0gYXMubnVtZXJpYyh0YWJsZShmYWN0b3IobXV0YXRpb25zX3NhbXBsZWQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gc2VxKEspKSkpCiAgICBtX3NhbXBsZWQgPSBtX3NhbXBsZWQvc3VtKG1fc2FtcGxlZCkKICAgIAogICAgZG8uY2FsbChkZWNvbXBvc2l0aW9uLm1ldGhvZCwgYyhsaXN0KG09bV9zYW1wbGVkLCBQPVApLCBleHRyYS5hcmdzKSkKICB9LCBudW0udGhyZWFkcyA9IG51bS50aHJlYWRzKQogIAogIHJvd25hbWVzKGV4cG9zdXJlcykgPSBjb2xuYW1lcyhQKQogIGNvbG5hbWVzKGV4cG9zdXJlcykgPSBwYXN0ZTAoIlJlcGxpY2F0ZV8iLCBzZXEoUikpCiAgZXJyb3JzID0gYXBwbHkoZXhwb3N1cmVzLCAyLCBmdW5jdGlvbihlKSBTaWduYXR1cmVFc3RpbWF0aW9uOjo6RnJvYmVuaXVzTm9ybShtLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUCwgZSkpCiAgbmFtZXMoZXJyb3JzKSA9IGNvbG5hbWVzKGV4cG9zdXJlcykKICByZXR1cm4obGlzdChleHBvc3VyZXMgPSBleHBvc3VyZXMsIGVycm9ycyA9IGVycm9ycykpCn0KCgojIFNwZWNpZnkgQ09TTUlDdjIgJiAzIHNpZ25hdHVyZSBzZXRzIChpbmNsLk92Q2Etc3BlY2lmaWMgQ09TTUlDdjMgc2lnbmF0dXJlcykKYWxsLnNpZ25hdHVyZXMgPSBsaXN0KCJEQlMiID0gbGlzdCgiREJTLkNPU01JQy52My5tYXkyMDE5IiA9IHNpZ25hdHVyZXMuZGJzLmNvc21pYy52My5tYXkyMDE5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEQlMuQ09TTUlDLnYzLm1heTIwMTkuSEdTT3ZDYSIgPSBzaWduYXR1cmVzLmRicy5jb3NtaWMudjMubWF5MjAxOVtjKDIsNCw1LDYsOSksXSksCiAgICAgICAgICAgICAgICAgICAgICAiU0JTIiA9IGxpc3QoIlNCUy5DT1NNSUMiID0gc2lnbmF0dXJlcy5jb3NtaWMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNCUy5DT1NNSUMuZXhvbWUudjMubWF5MjAxOSIgPSBzaWduYXR1cmVzLmV4b21lLmNvc21pYy52My5tYXkyMDE5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTQlMuQ09TTUlDLmdlbm9tZS52My5tYXkyMDE5IiA9IHNpZ25hdHVyZXMuZ2Vub21lLmNvc21pYy52My5tYXkyMDE5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTQlMuQ09TTUlDLmV4b21lLnYzLm1heTIwMTkuSEdTT3ZDYSIgPSBzaWduYXR1cmVzLmV4b21lLmNvc21pYy52My5tYXkyMDE5W2MoMSwyLDMsNSwxMSwxNywyMywzMSw0MCw0NCw0NSw0NiksXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU0JTLkNPU01JQy5nZW5vbWUudjMubWF5MjAxOS5IR1NPdkNhIiA9IHNpZ25hdHVyZXMuZ2Vub21lLmNvc21pYy52My5tYXkyMDE5W2MoMSwyLDMsNSwxMSwxNywyMywzMSw0MCw0NCw0NSw0NiksXSksCiAgICAgICAgICAgICAgICAgICAgICAiSUQiID0gbGlzdCgiSUQuQ09TTUlDLnYzLm1heTIwMTkiID0gc2lnbmF0dXJlcy5pZC5jb3NtaWMudjMubWF5MjAxOSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJJRC5DT1NNSUMudjMubWF5MjAxOS5IR1NPdkNhIiA9IHNpZ25hdHVyZXMuaWQuY29zbWljLnYzLm1heTIwMTlbYygxLDIsNCw1LDYsOCw5KSxdKSkKU0JTLkNPU01JQy5leG9tZS52My5tYXkyMDE5LkhHU092Q2EgPSBzaWduYXR1cmVzLmV4b21lLmNvc21pYy52My5tYXkyMDE5W2MoMSwyLDMsNSwxMSwxNywyMywzMSw0MCw0NCw0NSw0NiksXQoKIyBNYWluIGZ1bmN0aW9uCiMgZGF0YSA9IGRhdGEuZnJhbWUgd2l0aCBzYW1wbGUsIGNociwgcG9zLCByZWYsIGFsdCBjb2x1bW5zIHNwZWNpZmllZAojIHNpZy50eXBlcyA9IG11dGF0aW9uIHR5cGVzIHRvIHRlc3QKIyBzaWduYXR1cmUubGlzdCA9IGxpc3Rbc2lnLnR5cGVzXSBvZiBzaWduYXR1cmUgbWF0cmljZXMKIyBib290c3RyYXAucmVwcyA9IG51bWJlciBvZiBib290c3RyYXAgcmVzYW1wbGluZyByZXBldGl0aW9ucyBmb3IgY2FsY3VsYXRpbmcgdmFyaWFuY2UKIyBwbG90LmZpbGUgPSBvdXRwdXQgZmlsZSBmb3IgcGxvdHMgKHBkZikKIyBic2cgPSBCU0dlbm9tZSBvYmplY3QgKGRlZmF1bHQgaXMgaGcxOSkgZm9yIGZldGNoaW5nIHJlZmVyZW5jZSBjb250ZXh0cwojIG51bS50aHJlYWRzID0gbnVtYmVyIG9mIHByb2Nlc3NlcyBmb3IgYm9vdHN0cmFwIHBhcmFsbGVsaXNhdGlvbgojIG1ldGhvZC5jb2xvdXJzID0gY29sb3VycyBmb3Igc2lnbmF0dXJlIG1ldGhvZHMgaW4gcGxvdCwgZGVmYXVsdCBpcyBnZ3Bsb3QgY29sb3VycwojIFJldHVybnMgbGlzdCB3aXRoIHNpZ25hdHVyZSBzY29yZXMgYW5kIGVycm9ycyAoYW5kIGV4dHJhIHRhYmxlIGZvciBib290c3RyYXBzKSBwbHVzIGlucHV0IG1hdHJpY2VzCnJ1bl9zaWdzID0gZnVuY3Rpb24oZGF0YSwgc2FtcGxlID0gIlNhbXBsZSIsCiAgICAgICAgICAgICAgICAgICAgY2hyPSJDSFJPTSIsIHBvcz0iUE9TIiwgcmVmPSJSRUYiLCBhbHQ9IkFMVCIsCiAgICAgICAgICAgICAgICAgICAgc2lnLnR5cGVzID0gYygiU0JTIiwgIkRCUyIsICJJRCIpLAogICAgICAgICAgICAgICAgICAgIHNpZ25hdHVyZS5saXN0ID0gYWxsLnNpZ25hdHVyZXMsCiAgICAgICAgICAgICAgICAgICAgZGVjb21wb3NpdGlvbi5tZXRob2RzID0gYygiUVAiLCAiU0EiLCAiRGVjb25zdHJ1Y3RTaWdzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTaWdMQVNTTyIsICJOTkxTIiksCiAgICAgICAgICAgICAgICAgICAgYm9vdHN0cmFwLnJlcHMgPSAwLCBic2c9TlVMTCwKICAgICAgICAgICAgICAgICAgICBwbG90PVRSVUUsIHBsb3QuZmlsZSA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgbnVtLnRocmVhZHMgPSA4LAogICAgICAgICAgICAgICAgICAgIG1ldGhvZC5jb2xvdXJzID0gTlVMTAogICAgICAgICAgICAgICAgICAgICl7CiAgYWxsLmRlY29tcG9zaXRpb24ubWV0aG9kcyA9IGxpc3QoIlFQIiA9IGRlY29tcG9zZVFQLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNBIiA9IGRlY29tcG9zZVNBLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRlY29uc3RydWN0U2lncyIgPSBkZWNvbXBvc2VEZVNpZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTaWdMQVNTTyIgPSBkZWNvbXBvc2VTaWdsYXNzbywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOTkxTIiA9IGRlY29tcG9zZU5OTFMpCiAgZGVjb21wb3NpdGlvbi5tZXRob2RzID0gYWxsLmRlY29tcG9zaXRpb24ubWV0aG9kc1tkZWNvbXBvc2l0aW9uLm1ldGhvZHNdCiAgCiAgCiAgCiAgCiAgaWYoIXNhbXBsZSAlaW4lIGNvbG5hbWVzKGRhdGEpKXtkYXRhWyxzYW1wbGVdID0gIl8ifQogIGRhdGEgPSBub3JtX3Nic190b19kYnMoZGF0YSwgY2hyPWNociwgcG9zPXBvcywgcmVmPXJlZiwgYWx0PWFsdCwgc2FtcGxlID0gc2FtcGxlKQogIGRhdGFbLCJTYW1wbGUiXSA9IGRhdGFbLHNhbXBsZV0KICBzYW1wbGUgPSAiU2FtcGxlIiAjIGRlY29uc3RydWN0U2lncyBtdXQudG8uc2lncy5pbnB1dCBkb2Vzbid0IHdvcmsgcHJvcGVybHkgZm9yIERCUyB3aXRoIGRpZmZlcmVudCBzYW1wbGUuaWQgY29sdW1uIGZvciBzb21lIHJlYXNvbgogIGlucHV0Lm1hdHJpY2VzID0gbXV0LnRvLnNpZ3MuaW5wdXQuYWxsdHlwZXMobXV0LnJlZiA9IGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICBjaHIgPSBjaHIsIHBvcz1wb3MsIHJlZiA9IHJlZiwgYWx0PWFsdCwKICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZS5pZCA9IHNhbXBsZSwgYnNnPWJzZywKICAgICAgICAgICAgICAgICAgICAgIHNpZy50eXBlcyA9IHNpZy50eXBlcywgZGJzX3RhYmxlID0gZGJzX3Bvc3NpYmxlKQogIAogIHJlc3VsdHMgPSBsaXN0KGlucHV0Lm1hdHJpY2VzID0gaW5wdXQubWF0cmljZXMpCiAgc2FtcGxlcyA9IHVuaXF1ZShkYXRhWyxzYW1wbGVdKQogIGZvcihzIGluIHNhbXBsZXMpewogICAgcHJpbnQocykKICAgIGZvcihzaWcudHlwZSBpbiBzaWcudHlwZXMpewogICAgICBzaWdzLmlucHV0ID0gaW5wdXQubWF0cmljZXNbW3NpZy50eXBlXV0KICAgICAgaWYoaXMubnVsbChzaWdzLmlucHV0KSl7bmV4dH0KICAgICAgaWYoIXMgJWluJSByb3duYW1lcyhzaWdzLmlucHV0KSl7bmV4dH0KICAgICAgc2lncy5pbnB1dCA9IHNpZ3MuaW5wdXRbcywsZHJvcD1GXQogICAgICBpZihucm93KHNpZ3MuaW5wdXQpID4gMCl7CiAgICAgICAgaWYoc3VtKHNpZ3MuaW5wdXQpICA8IDEpewogICAgICAgICAgbmV4dAogICAgICAgIH0KICAgICAgICBzaWduYXR1cmVzID0gc2lnbmF0dXJlLmxpc3RbW3NpZy50eXBlXV0KICAgICAgICBtID0gYXMubWF0cml4KHQoc2lncy5pbnB1dCkpCiAgICAgICAgZm9yKHNpZ25hdHVyZS5uYW1lIGluIG5hbWVzKHNpZ25hdHVyZXMpKXsKICAgICAgICAgIHNpZ25hdHVyZSA9IHNpZ25hdHVyZXNbW3NpZ25hdHVyZS5uYW1lXV0KICAgICAgICAgIFAgPSBhcy5tYXRyaXgodChzaWduYXR1cmUpKQogICAgICAgICAgCiAgICAgICAgICByZXN1bHQubGlzdCA9IGxhcHBseSgxOmxlbmd0aChkZWNvbXBvc2l0aW9uLm1ldGhvZHMpLCBmdW5jdGlvbihpKXsKICAgICAgICAgICAgZGVjb21wb3NpdGlvbi5tZXRob2QgPSBkZWNvbXBvc2l0aW9uLm1ldGhvZHNbW2ldXQogICAgICAgICAgICBkZWNvbXBvc2l0aW9uLm1ldGhvZC5uYW1lID0gbmFtZXMoZGVjb21wb3NpdGlvbi5tZXRob2RzKVtpXQogICAgICAgICAgICAKICAgICAgICAgICAgY2F0KHBhc3RlMCgiTWV0aG9kOiAiLCBkZWNvbXBvc2l0aW9uLm1ldGhvZC5uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICIsIFNpZ25hdHVyZTogIiwgc2lnbmF0dXJlLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgIiwgTXV0YXRpb24gdHlwZTogIiwgc2lnLnR5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgIlxuIikpCiAgICAgICAgICAgIGlmKGRlY29tcG9zaXRpb24ubWV0aG9kLm5hbWUgPT0gIlNpZ0xBU1NPIil7CiAgICAgICAgICAgICAgaWYoc2lnLnR5cGUgIT0gIlNCUyIpe3JldHVybihOVUxMKX0KICAgICAgICAgICAgICBjb3VudHMgPSBzdW0obSkKICAgICAgICAgICAgICB5ID0gZmluZFNpZ0V4cG9zdXJlcyhtLCBQLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY29tcG9zaXRpb24ubWV0aG9kID0gZGVjb21wb3NpdGlvbi5tZXRob2QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY291bnRzID0gY291bnRzKQogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgIHkgPSBmaW5kU2lnRXhwb3N1cmVzKG0sIFAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVjb21wb3NpdGlvbi5tZXRob2QgPSBkZWNvbXBvc2l0aW9uLm1ldGhvZCkKICAgICAgICAgICAgfQogICAgICAgICAgICAKICAgICAgICAgICAgZXhwb3N1cmVzID0gZGF0YS5mcmFtZShTaWduYXR1cmUgPSByb3duYW1lcyh5JGV4cG9zdXJlcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRXhwb3N1cmUgPSB5JGV4cG9zdXJlc1ssMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTWV0aG9kID0gZGVjb21wb3NpdGlvbi5tZXRob2QubmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTaWduYXR1cmUuU2V0ID0gc2lnbmF0dXJlLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgICAgICAgICAgIGV4cG9zdXJlcyRNZXRob2QgPSBkZWNvbXBvc2l0aW9uLm1ldGhvZC5uYW1lCiAgICAgICAgICAgIGV4cG9zdXJlcyRTaWduYXR1cmUuU2V0ID0gc2lnbmF0dXJlLm5hbWUKICAgICAgICAgICAgZXhwb3N1cmVzJFNhbXBsZSA9IHMKICAgICAgICAgICAgCiAgICAgICAgICAgIGVycm9ycyA9IGRhdGEuZnJhbWUoRXJyb3IgPSB5JGVycm9ycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNZXRob2QgPSBkZWNvbXBvc2l0aW9uLm1ldGhvZC5uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNpZ25hdHVyZS5TZXQgPSBzaWduYXR1cmUubmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTYW1wbGUgPSBzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogICAgICAgICAgICByZXR1cm4obGlzdChleHBvc3VyZXMgPSBleHBvc3VyZXMsCiAgICAgICAgICAgICAgICAgICAgICAgIGVycm9ycyA9IGVycm9ycykpCiAgICAgICAgICB9KQogICAgICAgICAgCiAgICAgICAgICBleHBvc3VyZXMgPSBkby5jYWxsKHJiaW5kLCBsYXBwbHkocmVzdWx0Lmxpc3QsIGZ1bmN0aW9uKHgpewogICAgICAgICAgICB4W1siZXhwb3N1cmVzIl1dCiAgICAgICAgICB9KSkKICAgICAgICAgIGVycm9ycyA9IGRvLmNhbGwocmJpbmQsIGxhcHBseShyZXN1bHQubGlzdCwgZnVuY3Rpb24oeCl7CiAgICAgICAgICAgIHhbWyJlcnJvcnMiXV0KICAgICAgICAgIH0pKQogICAgICAgICAgcmVzdWx0cyRleHBvc3VyZXMgPSByYmluZChyZXN1bHRzJGV4cG9zdXJlcywgZXhwb3N1cmVzKQogICAgICAgICAgcmVzdWx0cyRlcnJvcnMgPSByYmluZChyZXN1bHRzJGVycm9ycywgZXJyb3JzKQogICAgICAgICAgCiAgICAgICAgICAjIGVzdGltYXRlIGVycm9ycyBieSBib290c3RyYXBwaW5nCiAgICAgICAgICBpZihib290c3RyYXAucmVwcyA+IDEpewogICAgICAgICAgICBjYXQoIkJvb3RzdHJhcHBpbmcuLi5cbiIpCiAgICAgICAgICAgIHJlc3VsdC5saXN0ID0gbGFwcGx5KDE6bGVuZ3RoKGRlY29tcG9zaXRpb24ubWV0aG9kcyksIGZ1bmN0aW9uKGkpewogICAgICAgICAgICAgIGRlY29tcG9zaXRpb24ubWV0aG9kID0gZGVjb21wb3NpdGlvbi5tZXRob2RzW1tpXV0KICAgICAgICAgICAgICBkZWNvbXBvc2l0aW9uLm1ldGhvZC5uYW1lID0gbmFtZXMoZGVjb21wb3NpdGlvbi5tZXRob2RzKVtpXQogICAgICAgICAgICAgIGNhdChwYXN0ZTAoIk1ldGhvZDogIiwgZGVjb21wb3NpdGlvbi5tZXRob2QubmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICIsIFNpZ25hdHVyZTogIiwgc2lnbmF0dXJlLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAiLCBNdXRhdGlvbiB0eXBlOiAiLCBzaWcudHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICJcbiIpKQogICAgICAgICAgICAgIGlmKGRlY29tcG9zaXRpb24ubWV0aG9kLm5hbWUgPT0gIlNpZ0xBU1NPIil7CiAgICAgICAgICAgICAgICBpZihzaWcudHlwZSAhPSAiU0JTIil7cmV0dXJuKE5VTEwpfQogICAgICAgICAgICAgICAgY291bnRzID0gc3VtKG0pCiAgICAgICAgICAgICAgICB5ID0gYm9vdHN0cmFwU2lnRXhwb3N1cmVzTVAobSwgUCwgUj1ib290c3RyYXAucmVwcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVjb21wb3NpdGlvbi5tZXRob2QgPSBkZWNvbXBvc2l0aW9uLm1ldGhvZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtLnRocmVhZHMgPSBudW0udGhyZWFkcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY291bnRzPWNvdW50cykKICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgeSA9IGJvb3RzdHJhcFNpZ0V4cG9zdXJlc01QKG0sIFAsIFI9Ym9vdHN0cmFwLnJlcHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlY29tcG9zaXRpb24ubWV0aG9kID0gZGVjb21wb3NpdGlvbi5tZXRob2QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bS50aHJlYWRzID0gbnVtLnRocmVhZHMpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICAgIAogICAgICAgICAgICAgIGV4cG9zdXJlcyA9IG1lbHQoZGF0YS5mcmFtZShTaWduYXR1cmUgPSByb3duYW1lcyh5JGV4cG9zdXJlcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkkZXhwb3N1cmVzKSwgdmFsdWUubmFtZSA9ICJFeHBvc3VyZSIpCiAgICAgICAgICAgICAgZXhwb3N1cmVzJE1ldGhvZCA9IGRlY29tcG9zaXRpb24ubWV0aG9kLm5hbWUKICAgICAgICAgICAgICBleHBvc3VyZXMkU2lnbmF0dXJlLlNldCA9IHNpZ25hdHVyZS5uYW1lCiAgICAgICAgICAgICAgZXhwb3N1cmVzJFNhbXBsZSA9IHMKICAgICAgICAgICAgICBjb2xuYW1lcyhleHBvc3VyZXMpWzJdID0gIlJlcGxpY2F0ZSIKICAgICAgICAgICAgICBlcnJvcnMgPSBkYXRhLmZyYW1lKFJlcGxpY2F0ZSA9IG5hbWVzKHkkZXJyb3JzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVycm9yID0geSRlcnJvcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNZXRob2QgPSBkZWNvbXBvc2l0aW9uLm1ldGhvZC5uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU2lnbmF0dXJlLlNldCA9IHNpZ25hdHVyZS5uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU2FtcGxlID0gcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogICAgICAgICAgICAgIHJldHVybihsaXN0KGV4cG9zdXJlcyA9IGV4cG9zdXJlcywKICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvcnMgPSBlcnJvcnMpKQogICAgICAgICAgICB9KQogICAgICAgICAgICBleHBvc3VyZXMgPSBkby5jYWxsKHJiaW5kLCBsYXBwbHkocmVzdWx0Lmxpc3QsIGZ1bmN0aW9uKHgpewogICAgICAgICAgICAgIHhbWyJleHBvc3VyZXMiXV0KICAgICAgICAgICAgfSkpCiAgICAgICAgICAgIGVycm9ycyA9IGRvLmNhbGwocmJpbmQsIGxhcHBseShyZXN1bHQubGlzdCwgZnVuY3Rpb24oeCl7CiAgICAgICAgICAgICAgeFtbImVycm9ycyJdXQogICAgICAgICAgICB9KSkKICAgICAgICAgICAgcmVzdWx0cyRleHBvc3VyZXMuYm9vdHN0cmFwID0gcmJpbmQocmVzdWx0cyRleHBvc3VyZXMuYm9vdHN0cmFwLCBleHBvc3VyZXMpCiAgICAgICAgICAgIHJlc3VsdHMkZXJyb3JzLmJvb3RzdHJhcCA9IHJiaW5kKHJlc3VsdHMkZXJyb3JzLmJvb3RzdHJhcCwgZXJyb3JzKQogICAgICAgICAgfQogICAgICAgICAgCiAgICAgICAgfQogICAgICB9CiAgICB9ICAKICB9CiAgCiAgCiAgaWYocGxvdCl7CiAgICBwbG90X3Jlc3VsdHMocmVzdWx0cywgcGxvdC5maWxlLCBtZXRob2QuY29sb3VycykKICB9CiAgCiAgcmV0dXJuKHJlc3VsdHMpCn0KCgojIFBsb3QgcmVzdWx0cwpwbG90X3Jlc3VsdHMgPSBmdW5jdGlvbihyZXN1bHRzLCBwbG90LmZpbGU9TlVMTCwgbWV0aG9kLmNvbG91cnMgPSBOVUxMLCB3aWR0aD0xNiwgaGVpZ2h0PTkpewogIGlmKCFpcy5udWxsKHBsb3QuZmlsZSkpewogICAgcGRmKHBsb3QuZmlsZSwgd2lkdGg9d2lkdGgsIGhlaWdodD1oZWlnaHQpCiAgfQogIGludF9icmVha3Nfcm91bmRlZCA8LSBmdW5jdGlvbih4LCBuID0gNSkgIHByZXR0eSh4LCBuKVtyb3VuZChwcmV0dHkoeCwgbiksMSkgJSUgMSA9PSAwXSAjIGZvciBpbnRlZ2VyIHkgYXhpcyBicmVha3MKICBmb3IocyBpbiB1bmlxdWUocmVzdWx0cyRleHBvc3VyZXMkU2FtcGxlKSl7CiAgICBmb3Ioc2lnLnR5cGUgaW4gbmFtZXMocmVzdWx0cyRpbnB1dC5tYXRyaWNlcykpewogICAgICBpZihzICVpbiUgcm93bmFtZXMocmVzdWx0cyRpbnB1dC5tYXRyaWNlc1tbc2lnLnR5cGVdXSkpewogICAgICAgIHNpZ3MuaW5wdXQgPSByZXN1bHRzJGlucHV0Lm1hdHJpY2VzW1tzaWcudHlwZV1dW3MsXQogICAgICAKICAgICAgICB0aGVtZV9jb21tb24gPSB0aGVtZV9idygpICsgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYyg1LDEsNSwxKSwgImxpbmVzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSwgdmp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpKQogICAgICAgIGlmKHNpZy50eXBlID09ICJTQlMiKXsKICAgICAgICAgIAogICAgICAgICAgY29scyA9IGMoIiM1QUJDRUIiLCAiIzA1MDcwOCIsICIjRDMzQzMyIiwgIiNDQkNBQ0IiLCAiI0FCQ0Q3MiIsICIjRTdDOUM2IikKICAgICAgICAgIGRhdCA9IGRhdGEuZnJhbWUoeCA9IGFzLmNoYXJhY3RlcigxOmxlbmd0aChzaWdzLmlucHV0KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBhcy5udW1lcmljKHNpZ3MuaW5wdXQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICB0b3AuY29scyA9IGdzdWIoIi4qXFxbfFxcXS4qJCIsICIiLCBjb2xuYW1lcyhzaWdzLmlucHV0KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHgubGFiZWxzID0gZ3N1YigiXFxbfD4uLiIsICIiLCBjb2xuYW1lcyhzaWdzLmlucHV0KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogICAgICAgICAgZm9yKGNvbCBpbiBjKCJ4IiwgInRvcC5jb2xzIikpewogICAgICAgICAgICBkYXRbLGNvbF0gPSBmYWN0b3IoZGF0Wyxjb2xdLCBsZXZlbHMgPSB1bmlxdWUoZGF0Wyxjb2xdKSkKICAgICAgICAgIH0KICAgICAgICAgIHAgPSBnZ3Bsb3QoZGF0LCBhZXMoeD14LHk9eSxmaWxsPXRvcC5jb2xzKSkgKyBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEYpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNvbHMpICsgeGxhYigiIikgKyB5bGFiKCJNdXRhdGlvbiBDb3VudCIpCiAgICAgICAgICByID0gYygwLCBtYXgoZGF0JHkpKjEuMDUpCiAgICAgICAgICBleHBhbmQuZmFjdG9yID0gMC41CiAgICAgICAgICBwID0gcCArIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZGF0JHgubGFiZWxzKSArIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbmRfc2NhbGUoYygtZXhwYW5kLmZhY3Rvci8oMSsyKmV4cGFuZC5mYWN0b3IpLCAtZXhwYW5kLmZhY3Rvci8oMSsyKmV4cGFuZC5mYWN0b3IpKSksIGxpbWl0cyA9IGMoLWV4cGFuZC5mYWN0b3IqclsyXSwgclsyXSooMStleHBhbmQuZmFjdG9yKSksIGJyZWFrcz1pbnRfYnJlYWtzX3JvdW5kZWQpCiAgICAgICAgICBwID0gcCArIHRoZW1lX2NvbW1vbgogICAgICAgICAgdG9wLnNlZ3MgPSBkYXQgJT4lIGdyb3VwX2J5KHRvcC5jb2xzKSAlPiUgc3VtbWFyaXNlKHhzdGFydCA9IGFzLm51bWVyaWMoaGVhZCh4LDEpKSAtIDAuMjUsIHhlbmQ9YXMubnVtZXJpYyh0YWlsKHgsMSkpICsgMC4yNSx5PW1heChkYXQkeSkqMS4xMCwgdGV4dC54ID0geFtjZWlsaW5nKGxlbmd0aCh4KS8yKV0pCiAgICAgICAgICBwID0gcCArIGdlb21fc2VnbWVudChkYXRhID0gdG9wLnNlZ3MsIG1hcHBpbmcgPWFlcyh4PXhzdGFydCx4ZW5kPXhlbmQsIHk9eSx5ZW5kPXksIHNpemU9MiwgY29sPXRvcC5jb2xzKSwgc2hvdy5sZWdlbmQgPSBGKSArIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPWNvbHMpCiAgICAgICAgICBwID0gcCArIGdlb21fdGV4dChkYXRhID0gdG9wLnNlZ3MsIG1hcHBpbmcgPSBhZXMoeD10ZXh0LngsIHk9IG1heChkYXQkeSkqMS4xNSwgbGFiZWw9dG9wLmNvbHMpKQogICAgICAgICAgcCA9IHAgKyBjb29yZF9jYXJ0ZXNpYW4oY2xpcD0ib2ZmIikKICAgICAgICAgIHAgPSBwICsgZ2d0aXRsZShwYXN0ZTAocywgIiAtIFNCUyBtdXRhdGlvbnNcblxuXG4iKSkKICAgICAgICAgIHByaW50KHApCiAgICAgICAgICAKICAgICAgICB9IGVsc2UgaWYoc2lnLnR5cGUgPT0gIkRCUyIpewogICAgICAgICAgY29scyA9IGMoIiNhNmNlZTMiLCAiIzFmNzhiNCIsIAogICAgICAgICAgICAgICAgICAgIiNiMmRmOGEiLCAiIzMzYTAyYyIsICIjZmI5YTk5IiwgIiNlMzFhMWMiLCAiI2ZkYmY2ZiIsIAogICAgICAgICAgICAgICAgICAgIiNmZjdmMDAiLCAiI2NhYjJkNiIsICIjNmEzZDlhIikKICAgICAgICAgIGRhdCA9IGRhdGEuZnJhbWUoeCA9IGFzLmNoYXJhY3RlcigxOmxlbmd0aChzaWdzLmlucHV0KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBhcy5udW1lcmljKHNpZ3MuaW5wdXQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICB0b3AuY29scyA9IHBhc3RlMChnc3ViKCI+LiokIiwgIiIsIGNvbG5hbWVzKHNpZ3MuaW5wdXQpKSwgIj5OTiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICB4LmxhYmVscyA9IGdzdWIoIl4uKj4iLCAiIiwgY29sbmFtZXMoc2lncy5pbnB1dCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICAgICAgICAgIGZvcihjb2wgaW4gYygieCIsICJ0b3AuY29scyIpKXsKICAgICAgICAgICAgZGF0Wyxjb2xdID0gZmFjdG9yKGRhdFssY29sXSwgbGV2ZWxzID0gdW5pcXVlKGRhdFssY29sXSkpCiAgICAgICAgICB9CiAgICAgICAgICBwID0gZ2dwbG90KGRhdCwgYWVzKHg9eCx5PXksZmlsbD10b3AuY29scykpICsgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jb2xzKSArIHhsYWIoIiIpICsgeWxhYigiTXV0YXRpb24gQ291bnQiKQogICAgICAgICAgciA9IGMoMCwgbWF4KGRhdCR5KSoxLjA1KQogICAgICAgICAgZXhwYW5kLmZhY3RvciA9IDAuNQogICAgICAgICAgcCA9IHAgKyBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGRhdCR4LmxhYmVscykgKyBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5kX3NjYWxlKGMoLWV4cGFuZC5mYWN0b3IvKDErMipleHBhbmQuZmFjdG9yKSwgLWV4cGFuZC5mYWN0b3IvKDErMipleHBhbmQuZmFjdG9yKSkpLCBsaW1pdHMgPSBjKC1leHBhbmQuZmFjdG9yKnJbMl0sIHJbMl0qKDErZXhwYW5kLmZhY3RvcikpLCBicmVha3M9aW50X2JyZWFrc19yb3VuZGVkKQogICAgICAgICAgcCA9IHAgKyB0aGVtZV9jb21tb24KICAgICAgICAgIHRvcC5zZWdzID0gZGF0ICU+JSBncm91cF9ieSh0b3AuY29scykgJT4lIHN1bW1hcmlzZSh4c3RhcnQgPSBhcy5udW1lcmljKGhlYWQoeCwxKSkgLSAwLjI1LCB4ZW5kPWFzLm51bWVyaWModGFpbCh4LDEpKSArIDAuMjUsIHk9bWF4KGRhdCR5KSoxLjEwLCB0ZXh0LnggPSB4W2NlaWxpbmcobGVuZ3RoKHgpLzIpXSkKICAgICAgICAgIHAgPSBwICsgZ2VvbV9zZWdtZW50KGRhdGEgPSB0b3Auc2VncywgbWFwcGluZyA9YWVzKHg9eHN0YXJ0LHhlbmQ9eGVuZCwgeT15LHllbmQ9eSwgc2l6ZT0yLCBjb2w9dG9wLmNvbHMpLCBzaG93LmxlZ2VuZCA9IEYpICsgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9Y29scykKICAgICAgICAgIHAgPSBwICsgZ2VvbV90ZXh0KGRhdGEgPSB0b3Auc2VncywgbWFwcGluZyA9IGFlcyh4PXRleHQueCwgeT0gbWF4KGRhdCR5KSoxLjE1LCBsYWJlbD10b3AuY29scykpCiAgICAgICAgICBwID0gcCArIGNvb3JkX2NhcnRlc2lhbihjbGlwPSJvZmYiKQogICAgICAgICAgcCA9IHAgKyBnZ3RpdGxlKHBhc3RlMChzLCAiIC0gREJTIG11dGF0aW9uc1xuXG5cbiIpKQogICAgICAgICAgcHJpbnQocCkKICAgICAgICB9IGVsc2UgewogICAgICAgICAgY29scyA9IGMoIiNmZGJmNmYiLCAiI2ZmN2YwMCIsICIjYjJkZjhhIiwgIiMzM2EwMmMiLAogICAgICAgICAgICAgICAgICAgY29sb3JSYW1wUGFsZXR0ZShjb2xvcnMgPSBjKCIjZmI5YTk5IiwgIiNlMzFhMWMiKSkoNCksCiAgICAgICAgICAgICAgICAgICBjb2xvclJhbXBQYWxldHRlKGNvbG9ycyA9IGMoIiNhNmNlZTMiLCAiIzFmNzhiNCIpKSg0KSwKICAgICAgICAgICAgICAgICAgIGNvbG9yUmFtcFBhbGV0dGUoY29sb3JzID0gYygiI2NhYjJkNiIsICIjNmEzZDlhIikpKDQpKQogICAgICAgICAgCiAgICAgICAgICBkYXQgPSBkYXRhLmZyYW1lKHggPSBhcy5jaGFyYWN0ZXIoMTpsZW5ndGgoc2lncy5pbnB1dCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gYXMubnVtZXJpYyhzaWdzLmlucHV0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wLnRleHQxID0gYyhyZXAoIjFicCBkZWxldGlvbiIsIDEyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoIjFicCBpbnNlcnRpb24iLCAxMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCI+MWJwIGRlbGV0aW9ucyBhdCByZXBlYXRzXG4oRGVsZXRpb24gbGVuZ3RoKSIsIDI0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoIj4xYnAgaW5zZXJ0aW9ucyBhdCByZXBlYXRzXG4oSW5zZXJ0aW9uIGxlbmd0aCkiLCAyNCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCJEZWxldGlvbnMgd2l0aCBtaWNyb2hvbW9sb2d5XG4oRGVsZXRpb24gbGVuZ3RoKSIsIDExKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcC50ZXh0MiA9IGMocmVwKCJDIiwgNiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCJUIiwgNiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCJDIiwgNiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCJUIiwgNiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCIyIiwgNiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCIzIiwgNiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCI0IiwgNiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCI1KyIsIDYpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgiMiIsIDYpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgiMyIsIDYpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgiNCIsIDYpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgiNSsiLCA2KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoIjIiLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoIjMiLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoIjQiLCAzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoIjUrIiwgNSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICB4LmxhYmVscyA9ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBib3R0b20udGV4dDEgPSBjKHJlcChjKCIxIiwgIjIiLCAiMyIsICI0IiwgIjUiLCAiNisiKSwgMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKGMoIjAiLCAiMSIsICIyIiwgIjMiLCAiNCIsICI1KyIpLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoYygiMSIsICIyIiwgIjMiLCAiNCIsICI1IiwgIjYrIiksIDQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcChjKCIwIiwgIjEiLCAiMiIsICIzIiwgIjQiLCAiNSsiKSwgNCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygxLCAxOjIsIDE6MywgMTo0LCAiNSsiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJvdHRvbS50ZXh0MiA9IGMocmVwKCJIb21vcG9seW1lciBsZW5ndGgiLCAxMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCJIb21vcG9seW1lciBsZW5ndGgiLCAxMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCJOdW1iZXIgb2YgcmVwZWF0IHVuaXRzIiwgMjQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgiTnVtYmVyIG9mIHJlcGVhdCB1bml0cyIsIDI0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoIk1pY3JvaG9tb2xvZ3kgbGVuZ3RoIiwgMTEpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgICAgICAgICBkYXQkdG9wLmNvbHMgPSBwYXN0ZShkYXQkdG9wLnRleHQxLCBkYXQkdG9wLnRleHQyKQogICAgICAgICAgZm9yKGNvbCBpbiBjKCJ4IiwgInRvcC5jb2xzIikpewogICAgICAgICAgICBkYXRbLGNvbF0gPSBmYWN0b3IoZGF0Wyxjb2xdLCBsZXZlbHMgPSB1bmlxdWUoZGF0Wyxjb2xdKSkKICAgICAgICAgIH0KICAgICAgICAgIHRvcC5zZWdzID0gZGF0ICU+JSBncm91cF9ieSh0b3AuY29scykgJT4lIHN1bW1hcmlzZSh4c3RhcnQgPSBhcy5udW1lcmljKGhlYWQoeCwxKSkgLSAwLjI1LCB4ZW5kPWFzLm51bWVyaWModGFpbCh4LDEpKSArIDAuMjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT1tYXgoZGF0JHkpKjEuMTAsIHkyPS0oMC4wNSptYXgoZGF0JHkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0LnggPSB4W2NlaWxpbmcobGVuZ3RoKHgpLzIpXSwgdGV4dD10b3AudGV4dDJbMV0sIHRleHQuY29sID0gaWZlbHNlKHRleHQgJWluJSBjKCJUIiwgIjUrIiksICJ3aGl0ZSIsICJibGFjayIpKQogICAgICAgICAgCiAgICAgICAgICAKICAgICAgICAgIHAgPSBnZ3Bsb3QoKSArIGdlb21fY29sKGRhdGE9ZGF0LCBtYXBwaW5nID0gYWVzKHg9eCx5PXksZmlsbD10b3AuY29scyksIHNob3cubGVnZW5kID0gRkFMU0UpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNvbHMpICsgeGxhYigiIikgKyB5bGFiKCJNdXRhdGlvbiBDb3VudCIpCiAgICAgICAgICByID0gYygwLCBtYXgoZGF0JHkpKjEuMDUpCiAgICAgICAgICBleHBhbmQuZmFjdG9yID0gMC41CiAgICAgICAgICBwID0gcCArIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZGF0JHgubGFiZWxzKSArIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbmRfc2NhbGUoYygtZXhwYW5kLmZhY3Rvci8oMSsyKmV4cGFuZC5mYWN0b3IpLCAtZXhwYW5kLmZhY3Rvci8oMSsyKmV4cGFuZC5mYWN0b3IpKSksIGxpbWl0cyA9IGMoLWV4cGFuZC5mYWN0b3IqclsyXSwgclsyXSooMStleHBhbmQuZmFjdG9yKSksIGJyZWFrcz1pbnRfYnJlYWtzX3JvdW5kZWQpCiAgICAgICAgICBwID0gcCArIHRoZW1lX2NvbW1vbgogICAgICAgICAgcCA9IHAgKyBnZW9tX3NlZ21lbnQoZGF0YSA9IHRvcC5zZWdzLCBtYXBwaW5nID1hZXMoeD14c3RhcnQseGVuZD14ZW5kLCB5PXkseWVuZD15LCBzaXplPTIsIGNvbD10b3AuY29scyksIHNob3cubGVnZW5kID0gRkFMU0UpIAogICAgICAgICAgcCA9IHAgKyBnZW9tX3NlZ21lbnQoZGF0YSA9IHRvcC5zZWdzLCBtYXBwaW5nID1hZXMoeD14c3RhcnQseGVuZD14ZW5kLCB5PXkyLHllbmQ9eTIsIHNpemU9MiwgY29sPXRvcC5jb2xzKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1jb2xzKQogICAgICAgICAgcCA9IHAgKyBnZW9tX3RleHQoZGF0YSA9IHRvcC5zZWdzLCBtYXBwaW5nID0gYWVzKHg9dGV4dC54LCB5PXksIGxhYmVsPXRleHQpLCBjb2w9dG9wLnNlZ3MkdGV4dC5jb2wsaGp1c3Q9MC41LCBudWRnZV94ID0gaWZlbHNlKCh0b3Auc2VncyR4ZW5kIC0gdG9wLnNlZ3MkeHN0YXJ0IC0gMC41KSAlJSAyLCAwLjUsIDApKQogICAgICAgICAgcCA9IHAgKyBnZW9tX3RleHQoZGF0YSA9IGRhdCwgbWFwcGluZyA9IGFlcyh4PXgsIHk9LSgwLjEqbWF4KGRhdCR5KSksIGxhYmVsPWJvdHRvbS50ZXh0MSksIGhqdXN0PTAuNSkKICAgICAgICAgIGFubm90MiA9IGRhdCAlPiUgZ3JvdXBfYnkoYm90dG9tLnRleHQyLCB0b3AudGV4dDEpICU+JSBzdW1tYXJpc2UoeT0tMC4xNSptYXgoZGF0JHkpLCB5Mj0xLjE3Km1heChkYXQkeSksIHg9eFtjZWlsaW5nKGxlbmd0aCh4KS8yKV0pICU+JSB1bmdyb3VwKCkKICAgICAgICAgIHAgPSBwICsgZ2VvbV90ZXh0KGRhdGEgPSBhbm5vdDIsIG1hcHBpbmc9YWVzKHg9eCwgeT15LCBsYWJlbD1ib3R0b20udGV4dDIpKQogICAgICAgICAgcCA9IHAgKyBnZW9tX3RleHQoZGF0YSA9IGFubm90MiwgbWFwcGluZz1hZXMoeD14LCB5PXkyLCBsYWJlbD10b3AudGV4dDEpKQogICAgICAgICAgCiAgICAgICAgICBwID0gcCArIGNvb3JkX2NhcnRlc2lhbihjbGlwPSJvZmYiKQogICAgICAgICAgcCA9IHAgKyBnZ3RpdGxlKHBhc3RlMChzLCAiIC0gSW5EZWwgbXV0YXRpb25zXG5cblxuXG4iKSkKICAgICAgICAgIAogICAgICAgICAgcHJpbnQocCkKICAgICAgICB9CiAgICAgIH0KICAgIH0KICAgIGZvcihzaWcuc2V0IGluIHVuaXF1ZShyZXN1bHRzJGV4cG9zdXJlcyRTaWduYXR1cmUuU2V0KSl7CiAgICAgIGFsbC5zaWcubmFtZXMgPSB1bmlxdWUocmVzdWx0cyRleHBvc3VyZXMkU2lnbmF0dXJlKQogICAgICB4ID0gcmVzdWx0cyRleHBvc3VyZXMgJT4lCiAgICAgICAgZmlsdGVyKFNpZ25hdHVyZS5TZXQgPT0gc2lnLnNldCwKICAgICAgICAgICAgICAgU2FtcGxlID09IHMpICU+JQogICAgICAgIGZpbHRlcihTaWduYXR1cmUgJWluJSBTaWduYXR1cmVbRXhwb3N1cmUgPiAwXSkgJT4lIAogICAgICAgIG11dGF0ZShTaWduYXR1cmUgPSBmYWN0b3IoU2lnbmF0dXJlLCBsZXZlbHMgPSBpbnRlcnNlY3QoYWxsLnNpZy5uYW1lcywgU2lnbmF0dXJlKSkpCiAgICAgIGlmKCJleHBvc3VyZXMuYm9vdHN0cmFwIiAlaW4lIG5hbWVzKHJlc3VsdHMpKXsKICAgICAgICAjIGJveHBsb3QgaW5zdGVhZAogICAgICAgIHkgPSByZXN1bHRzJGV4cG9zdXJlcy5ib290c3RyYXAgJT4lIAogICAgICAgICAgZmlsdGVyKFNpZ25hdHVyZS5TZXQgPT0gc2lnLnNldCwKICAgICAgICAgICAgICAgICBTYW1wbGUgPT0gcykgJT4lIAogICAgICAgICAgbXV0YXRlKFNpZ25hdHVyZSA9IGZhY3RvcihTaWduYXR1cmUsIGxldmVscz1pbnRlcnNlY3QoYWxsLnNpZy5uYW1lcywgU2lnbmF0dXJlKSkpCiAgICAgICAgeCA9IHggJT4lIG11dGF0ZShTaWduYXR1cmUgPSBmYWN0b3IoYXMuY2hhcmFjdGVyKFNpZ25hdHVyZSksIGxldmVscz1sZXZlbHMoeSRTaWduYXR1cmUpKSkKICAgICAgICBwID0gZ2dwbG90KHksIGFlcyh4PVNpZ25hdHVyZSwgeT1FeHBvc3VyZSwgZmlsbD1NZXRob2QpKSArIHN0YXRfYm94cGxvdChnZW9tPSdlcnJvcmJhcicpICsgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGU9TkEpICsKICAgICAgICAgIGdlb21fYm94cGxvdChhZXMoY29sb3IgPSBNZXRob2QpLAogICAgICAgICAgICAgICAgICAgICAgIGZhdHRlbiA9IE5VTEwsIGZpbGwgPSBOQSwgY29lZiA9IDAsIG91dGxpZXIuc2hhcGU9TkEsCiAgICAgICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGKSArCiAgICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5kX3NjYWxlKGMoMCwgMC4wNSkpKQogICAgICAgIHAgPSBwICsgZ2VvbV9wb2ludChkYXRhID0geCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gU2lnbmF0dXJlLCB5ID0gRXhwb3N1cmUsIGZpbGwgPSBNZXRob2QsIGdyb3VwID0gTWV0aG9kKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGU9MjMsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuNzUpLCBzaG93LmxlZ2VuZCA9IEYpICsgZ3VpZGVzKGNvbD1GQUxTRSkKICAgICAgICAKICAgICAgICAKICAgICAgfSBlbHNlIHsKICAgICAgICAKICAgICAgICBwID0gZ2dwbG90KHgsIGFlcyh4PVNpZ25hdHVyZSwgeT1FeHBvc3VyZSwgZmlsbD1NZXRob2QpKSArCiAgICAgICAgICBnZW9tX2NvbChwb3NpdGlvbj0iZG9kZ2UiKSArCiAgICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5kX3NjYWxlKGMoMCwgMC4wNSkpKQogICAgICB9CiAgICAgIAogICAgICBwID0gcCArIGZhY2V0X2dyaWQoLiB+IFNpZ25hdHVyZSwgc2NhbGVzID0gImZyZWVfeCIpIAogICAgICAKICAgICAgcCA9IHAgKyB0aGVtZV9jbGFzc2ljKCkgKwogICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSwgdmp1c3QgPSAwLjUpKSArCiAgICAgICAgdGhlbWUoc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArIAogICAgICAgIHhsYWIoIiIpICsgZ2d0aXRsZShwYXN0ZTAoaWZlbHNlKHMgIT0gIl8iLCBwYXN0ZTAocywgIiAtICIpLCAiIiksIGdzdWIoIlxcLiIsICIgIiwgc2lnLnNldCkpKQogICAgICBpZighaXMubnVsbChtZXRob2QuY29sb3VycykpewogICAgICAgIHAgPSBwICsgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTWV0aG9kIiwgdmFsdWVzID0gbWV0aG9kLmNvbG91cnMpICsgc2NhbGVfY29sb3VyX21hbnVhbChuYW1lPSJNZXRob2QiLCB2YWx1ZXM9bWV0aG9kLmNvbG91cnMpCiAgICAgIH0KICAgICAgcHJpbnQocCkKICAgICAgCiAgICAgIGlmKCJlcnJvcnMuYm9vdHN0cmFwIiAlaW4lIG5hbWVzKHJlc3VsdHMpKXsKICAgICAgICBwID0gcmVzdWx0cyRlcnJvcnMuYm9vdHN0cmFwICU+JSBmaWx0ZXIoU2lnbmF0dXJlLlNldCA9PSBzaWcuc2V0LCBTYW1wbGUgPT1zKSAlPiUKICAgICAgICAgIGdncGxvdChhZXMoeD1NZXRob2QsIHk9RXJyb3IsIGZpbGw9TWV0aG9kKSkgKwogICAgICAgICAgc3RhdF9ib3hwbG90KGdlb209ImVycm9yYmFyIikgKyBnZW9tX2JveHBsb3Qoc2hvdy5sZWdlbmQgPSBGKSArIHRoZW1lX2NsYXNzaWMoKQogICAgICB9IGVsc2UgewogICAgICAgIHAgPSByZXN1bHRzJGVycm9ycyAlPiUgZmlsdGVyKFNpZ25hdHVyZS5TZXQgPT0gc2lnLnNldCwgU2FtcGxlPT1zKSAlPiUKICAgICAgICAgIGdncGxvdChhZXMoeD1NZXRob2QsIHk9RXJyb3IsIGNvbD1NZXRob2QpKSArIGdlb21fcG9pbnQoc2hvdy5sZWdlbmQgPSBGKSArIHRoZW1lX2NsYXNzaWMoKQogICAgICB9CiAgICAgIGlmKCFpcy5udWxsKG1ldGhvZC5jb2xvdXJzKSl7CiAgICAgICAgcCA9IHAgKyBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJNZXRob2QiLCB2YWx1ZXMgPSBtZXRob2QuY29sb3VycykgKyBzY2FsZV9jb2xvdXJfbWFudWFsKG5hbWU9Ik1ldGhvZCIsIHZhbHVlcz1tZXRob2QuY29sb3VycykKICAgICAgfQogICAgICBwcmludChwICsgeWxhYigiRGVjb21wb3NpdGlvbiBFcnJvciIpKQogICAgfQogIH0KICBpZighaXMubnVsbChwbG90LmZpbGUpKXsKICAgIGRldi5vZmYoKQogIH0KfQoKcGxvdF9waWVzID0gZnVuY3Rpb24ocmVzdWx0cywgc2lnbmF0dXJlLnNldHMgPSBOVUxMLCBzYW1wbGVzID0gTlVMTCwgbWV0aG9kPSJEZWNvbnN0cnVjdFNpZ3MiLCBwbG90LmZpbGU9TlVMTCwKICAgICAgICAgICAgICAgICAgICAgY29scyA9IGMoIiMwMjNGQTUiLCAiIzAyM0ZBNSIsICIjN0Q4N0I5IiwgIiNCRUMxRDQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiNENkJDQzAiLCAiI0JCNzc4NCIsICJnb2xkIiwgIiM0QTZGRTMiLCAiIzg1OTVFMSIsICIjQjVCQkUzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjRTZBRkI5IiwgIiNFMDdCOTEiLCAiI0QzM0Y2QSIsICIjMTFDNjM4IiwgIiM4REQ1OTMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiNDNkRFQzciLCAiI0VBRDNDNiIsICIjRjBCOThEIiwgIiNFRjk3MDgiLCAiIzBGQ0ZDMCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIzlDREVENiIsICIjRDVFQUU3IiwgIiNGM0UxRUIiLCAiI0Y2QzRFMSIsICIjRjc5Q0Q0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjODY2MDk3IiwgIiMwMDg5NDEiLCAiI0EzMDA1OSIsICIjRjZDNEUxIiwgIiNGNzlDRDQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiM4NjYwOTciLCAiIzAwODk0MSIsICIjQTMwMDU5IiwgIiMwMDgwODAiLCAiIzhCMDAwMCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiI0Y0QTQ2MCIsICIjNjYzMzk5IikpewogIGJsYW5rX3RoZW1lIDwtIHRoZW1lX21pbmltYWwoKSsKICAgIHRoZW1lKAogICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgICBwYW5lbC5ncmlkPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNCwgZmFjZT0iYm9sZCIpLAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKQogICAgKQogIAogIGlmKGlzLm51bGwoc2lnbmF0dXJlLnNldHNbMV0pKXsKICAgIHNpZ25hdHVyZS5zZXRzID0gdW5pcXVlKHJlc3VsdHMkZXhwb3N1cmVzJFNpZ25hdHVyZS5TZXQpCiAgfQogIGlmKGlzLm51bGwoc2FtcGxlcykpewogICAgc2FtcGxlcyA9IHVuaXF1ZShyZXN1bHRzJGV4cG9zdXJlcyRTYW1wbGUpCiAgfQogIGlmKCFtZXRob2QgJWluJSByZXN1bHRzJGV4cG9zdXJlcyRNZXRob2QpewogICAgd2FybmluZyhwYXN0ZTAoIk1ldGhvZDogJyIsIG1ldGhvZCwgIicgaXMgbm90IGluIHRoZXNlIHJlc3VsdHMiKSkKICAgIHJldHVybihOVUxMKQogIH0KICAKICBpZighaXMubnVsbChwbG90LmZpbGUpKXsKICAgIHBkZihwbG90LmZpbGUsIGhlaWdodD01LCB3aWR0aD01Kmxlbmd0aChzaWduYXR1cmUuc2V0cykpCiAgfQogIGZvcihzYW1wbGUgaW4gc2FtcGxlcyl7CiAgICBwcyA9IGxhcHBseShzaWduYXR1cmUuc2V0cywgZnVuY3Rpb24oc2lnbmF0dXJlLnNldCl7CiAgICAgIHNpZ25hdHVyZXMgPSB1bmlxdWUocmVzdWx0cyRleHBvc3VyZXMgJT4lIGZpbHRlcihTaWduYXR1cmUuU2V0ID09IHNpZ25hdHVyZS5zZXQpICU+JSBwdWxsKFNpZ25hdHVyZSkpCiAgICAgIGlmKGxlbmd0aChzaWduYXR1cmVzKSA+IGxlbmd0aChjb2xzKSl7CiAgICAgICAgc2lnbmF0dXJlLmNvbHMgPSBzZXROYW1lcyhjb2xvclJhbXBQYWxldHRlKGNvbG9ycyA9IGNvbHMpKGxlbmd0aChzaWduYXR1cmVzKSksIHNpZ25hdHVyZXMpCiAgICAgIH0gZWxzZSB7CiAgICAgICAgc2lnbmF0dXJlLmNvbHMgPSBzZXROYW1lcyhjb2xzWzE6bGVuZ3RoKHNpZ25hdHVyZXMpXSwgc2lnbmF0dXJlcykKICAgICAgfQogICAgICBwID0gcmVzdWx0cyRleHBvc3VyZXMgJT4lIGZpbHRlcihNZXRob2QgPT0gbWV0aG9kLCBTYW1wbGUgPT0gc2FtcGxlLCBTaWduYXR1cmUuU2V0ID09IHNpZ25hdHVyZS5zZXQpICU+JQogICAgICAgIGZpbHRlcihFeHBvc3VyZSA+IDApICU+JSBnZ3Bsb3QoYWVzKHg9IiIsIHk9RXhwb3N1cmUsIGZpbGw9U2lnbmF0dXJlKSkgKyBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHdpZHRoPTEpICsKICAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9c2lnbmF0dXJlLmNvbHMsIG5hbWU9IiIpICsKICAgICAgICBjb29yZF9wb2xhcigieSIsIHN0YXJ0PTApICsgYmxhbmtfdGhlbWUgKwogICAgICAgIGdndGl0bGUoc2lnbmF0dXJlLnNldCkKICAgICAgcmV0dXJuKHApCiAgICB9KQogICAgcHNbWyJucm93Il1dID0gMQogICAgZG8uY2FsbChncmlkLmFycmFuZ2UsIHBzKQogIH0KICBpZighaXMubnVsbChwbG90LmZpbGUpKXtkZXYub2ZmKCl9Cn0KYGBgCgpJbXBvcnQgaW5kaXZpZHVhbCB0dW1vdXIgZXhvbWUgc29tYXRpYyB2YXJpYW50cyBmaWxlIGFuZCBmaWx0ZXIgYXMgYmVmb3JlIChzZWUgaHR0cHM6Ly9ycHVicy5jb20vZGVlcHN1YnMvdGhlc2lzX3R1bW91cl9leG9tZV92YXJpYW50cykKYGBge3J9Cm1heC5jYWxsZXJzID0gMwpmIDwtIHJlYWQudGFibGUoInR1bW91cl9zYW1wbGVfZ2VybWxpbmVfZXhvbWVfc29tYXRpY19jYWxsZXJfZmlsZS50c3YiLCBoZWFkZXI9VFJVRSwgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSwgc2VwPSJcdCIsIGNvbW1lbnQuY2hhcj0iIiwgcXVvdGU9IiIpICU+JQogIGRwbHlyOjpyZW5hbWUoIlR1bW91cl9TYW1wbGUiPSJYLlR1bW91cl9TYW1wbGUiKSAlPiUgCiAgbXV0YXRlKG4uY2FsbGVycyA9IHNldE5hbWVzKHNhcHBseShzdHJzcGxpdCh1bmlxdWUoSWRlbnRpZmllZCksICItIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oY2FsbGVycyl7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZSgiSW50ZXJzZWN0aW9uIiAlaW4lIGNhbGxlcnMsIG1heC5jYWxsZXJzLCBzdW0oIWdyZXBsKCJeZmlsdGVyIiwgY2FsbGVycykpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KSwgdW5pcXVlKElkZW50aWZpZWQpKVtJZGVudGlmaWVkXSkKZyA8LSByZWFkLmRlbGltKCJ0dW1vdXJfc2FtcGxlX2dlcm1saW5lX3BhaXJlZF9leG9tZXNfSEFQLnRzdiIsIGhlYWRlcj1UUlVFLCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFLCBzZXA9Ilx0IiwgY29tbWVudC5jaGFyPSIiKSAlPiUgCiAgZHBseXI6OnJlbmFtZSgiVHVtb3VyX1NhbXBsZSI9IlguVHVtb3VyX1NhbXBsZSIpCnNvcnQodW5pcXVlKGYkVHVtb3VyX1NhbXBsZSkpCnNvcnQodW5pcXVlKGckVHVtb3VyX1NhbXBsZSkpClZpUF9saXN0IDwtIHJlYWQuZGVsaW0oIlZpUCBTYW1wbGVzICYgQ2FuZGlkYXRlIEdlbmVzLnR4dCIsIGhlYWRlcj1UUlVFLCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFLCBzZXA9Ilx0IiwgY29tbWVudC5jaGFyPSIiKSAlPiUgCiAgYXJyYW5nZShTYW1wbGUpCnR1bW91cl90eXBlIDwtIHNlbGVjdChWaVBfbGlzdCxTYW1wbGUsVHVtb3VyLlR5cGUpICU+JSBkaXN0aW5jdCgpICU+JSByZW5hbWUoIlNhbXBsZSI9Ik5vcm1hbF9TYW1wbGUiKQpmJE5vcm1hbF9TYW1wbGUgPC0gYXMuY2hhcmFjdGVyKGYkTm9ybWFsX1NhbXBsZSkKZyROb3JtYWxfU2FtcGxlIDwtIGFzLmNoYXJhY3RlcihnJE5vcm1hbF9TYW1wbGUpCmYgPC0gbGVmdF9qb2luKGYsdHVtb3VyX3R5cGUsYnk9Ik5vcm1hbF9TYW1wbGUiLGNvcHk9RkFMU0UpCmcwIDwtIGxlZnRfam9pbihnLHR1bW91cl90eXBlLGJ5PSJOb3JtYWxfU2FtcGxlIixjb3B5PUZBTFNFKSAlPiUgZmlsdGVyKENBTk9OSUNBTCVpbiUiWUVTIikKZ2VuZV9saXN0IDwtIHNlbGVjdChWaVBfbGlzdCxTYW1wbGUsU1lNQk9MKSAlPiUgZmlsdGVyKFNhbXBsZSVpbiUoZiROb3JtYWxfU2FtcGxlKSkKZ2VuZXMgPC0gZ2VuZV9saXN0JFNZTUJPTAp0dW1vdXJfcHVyaXR5IDwtIHJlYWQuZGVsaW0oIlR1bW91ciBQdXJpdHkudHh0IiwgaGVhZGVyPVRSVUUsIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UsIHNlcD0iXHQiLCBjb21tZW50LmNoYXI9IiIpICU+JQogIHNlbGVjdChUdW1vdXJfU2FtcGxlLFB1cml0eSxOb19NdXRhdGlvbnMpCmYgPC0gbGVmdF9qb2luKGYsdHVtb3VyX3B1cml0eSxieT0iVHVtb3VyX1NhbXBsZSIsY29weT1GQUxTRSkKZzAgPC0gbGVmdF9qb2luKGcwLHR1bW91cl9wdXJpdHksYnk9IlR1bW91cl9TYW1wbGUiLGNvcHk9RkFMU0UpCgpmJFFVQUwgPC0gYXMubnVtZXJpYyhmJFFVQUwpICU+JSByZXBsYWNlX25hKDApCmYkVFVNT1VSLlBNQ0FEIDwtIGFzLm51bWVyaWMoZiRUVU1PVVIuUE1DQUQpICU+JSByZXBsYWNlX25hKDApCmYkVFVNT1VSLlBNQ0RQIDwtIGFzLm51bWVyaWMoZiRUVU1PVVIuUE1DRFApICU+JSByZXBsYWNlX25hKDApCmYkVFVNT1VSLlBNQ0ZSRVEgPC0gYXMubnVtZXJpYyhmJFRVTU9VUi5QTUNGUkVRKSAlPiUgcmVwbGFjZV9uYSgwKQpmJE5PUk1BTC5QTUNBRCA8LSBhcy5udW1lcmljKGYkTk9STUFMLlBNQ0FEKSAlPiUgcmVwbGFjZV9uYSgwKQpmJE5PUk1BTC5QTUNEUCA8LSBhcy5udW1lcmljKGYkTk9STUFMLlBNQ0RQKSAlPiUgcmVwbGFjZV9uYSgwKQpmJE5PUk1BTC5QTUNGUkVRIDwtIGFzLm51bWVyaWMoZiROT1JNQUwuUE1DRlJFUSkgJT4lIHJlcGxhY2VfbmEoMCkKZiRHbm9tQURfdjNfQUYgPC0gYXMubnVtZXJpYyhmJEdub21BRF92M19BRikgJT4lIHJlcGxhY2VfbmEoMCkKCmYwIDwtIGZpbHRlcihmLCFJZGVudGlmaWVkJWluJWMoIkZpbHRlcmVkSW5BbGwiKSkgJT4lIAogICAgZmlsdGVyKG4uY2FsbGVycz49MikgJT4lCiAgICBmaWx0ZXIoQ29uc2VxdWVuY2VfUmFuazw3KQoKZjE8LWZpbHRlcihmMCwoTk9STUFMLlBNQ0FEIDw9IDIpICYgKE5PUk1BTC5QTUNEUCA+PSAxMCkgJiAoTk9STUFMLlBNQ0ZSRVEgPCAwLjAxKSkKZjI8LWZpbHRlcihmMSxUVU1PVVIuUE1DQUQgPj0gNSkKZjM8LWZpbHRlcihmMixUVU1PVVIuUE1DRFA+PTIwKQpmNDwtZmlsdGVyKGYzLFRVTU9VUi5QTUNGUkVRID49IChmMyRQdXJpdHkqMC41KigyLzMpKSkKZjU8LWZpbHRlcihmNCxHbm9tQURfdjIuMV9ub25fY2FuY2VyX0FGIDw9IDAuMDAwMSkKZjY8LWZpbHRlcihmNSxHbm9tQURfdjNfQUYgPD0gMC4wMDAxKQoKc2FtcGxlczwtdW5pcXVlKGYkVHVtb3VyX1NhbXBsZSkKYGBgCgpFeHRyYWN0IHJlbWFpbmluZyB1bmlxdWUgdmFyaWFudHMgYW5kIGNvbHVtbnMgdXNlZCBmb3IgbXV0YXRpb24gc2lnbmF0dXJlIGFuYWx5c2lzLCBhbmQgcmVuYW1lIHNhbXBsZSBjb2x1bW4KYGBge3J9CmY3PC1zZWxlY3QoZjYsYyhDSFJPTSxQT1MsUkVGLEFMVCxWYXJpYW50X1R5cGUpKQp1bmlxdWUoZjckVmFyaWFudF9UeXBlKQpmNyRTYW1wbGU8LWY2JFR1bW91cl9TYW1wbGUKYGBgCgpSdW4gbXV0YXRpb24gc2lnbmF0dXJlIGZ1bmN0aW9ucyAoRGVjb25zdHJ1Y3RTaWdzLCBTaWdMQVNTTywgU0EsIFFQLCBOTkxTLCBTaWduYXR1cmVFc3RpbWF0aW9uKSwgaW5jbHVkaW5nIGNvdW50IG9mIG51bWJlciBvZiB2YXJpYW50cyB1c2VkIGFuZCBmaWx0ZXJpbmcgbWV0cmljcywgYW5kIG91dHB1dCByZXN1bHRzCmBgYHtyfQptdXQucmVmPC1mNwptdXQucmVmJENIUk9NPC11bmxpc3QobGFwcGx5KG11dC5yZWYkQ0hST00sZnVuY3Rpb24oeCkgcGFzdGUoImNociIseCxzZXA9IiIpKSkgCm11dC5yZWZfZWRpdDwtbXV0LnJlZlssYygiU2FtcGxlIiwiQ0hST00iLCJQT1MiLCJSRUYiLCJBTFQiKV0KCmZvciAoc2FtcGxlIGluIHNhbXBsZXMpewogICAgICBzZXR3ZCgiVHVtb3VyIE11dGF0aW9uIFNpZ25hdHVyZXMiKQogICAgICBtdXQucmVmMjwtbXV0LnJlZl9lZGl0W211dC5yZWZfZWRpdCRTYW1wbGU9PXNhbXBsZSxdCiAgICAgIG11dC5yZWYyPC11bmlxdWUobXV0LnJlZjIpCiAgICAgIG11dE51bSA8LSBucm93KG11dC5yZWYyKQogICAgICBvcHRpb25zKHNjaXBlbiA9IDk5OSkKICAgICAgcyA8LSBwYXN0ZSgiVGhlIG51bWJlciBvZiB2YXJpYW50cyBpbiBzYW1wbGUiLCBzYW1wbGUsICJ1c2VkIGZvciBtdXRhdGlvbiBzaWduYXR1cmUgYW5hbHlzaXMgaXMiLCBtdXROdW0pCiAgICAgIHQgPC0gcGFzdGUoIlRoZSBtaW5pbXVtIFRVTU9VUi5QTUNBRCBpbiBzYW1wbGUiLCBzYW1wbGUsICJ1c2VkIGZvciBmaWx0ZXJpbmcgaXMiLCBtaW4odW5pcXVlKGYyJFRVTU9VUi5QTUNBRCkpLCAiYW5kIHRoZSBtaW5pbXVtIGZpZ3VyZSBmb3IgdGhlIHZhcmlhbnRzIHVzZWQgZm9yIG11dGF0aW9uIHNpZ25hdHVyZSBhbmFseXNpcyBpcyIsIG1pbih1bmlxdWUoZjYkVFVNT1VSLlBNQ0FEKSkpCiAgICAgIHUgPC0gcGFzdGUoIlRoZSBtaW5pbXVtIFRVTU9VUi5QTUNEUCBpbiBzYW1wbGUiLCBzYW1wbGUsICJ1c2VkIGZvciBmaWx0ZXJpbmcgaXMiLCBtaW4odW5pcXVlKGYzJFRVTU9VUi5QTUNEUCkpLCAiYW5kIHRoZSBtaW5pbXVtIGZpZ3VyZSBmb3IgdGhlIHZhcmlhbnRzIHVzZWQgZm9yIG11dGF0aW9uIHNpZ25hdHVyZSBhbmFseXNpcyBpcyIsIG1pbih1bmlxdWUoZjYkVFVNT1VSLlBNQ0RQKSkpCiAgICAgIHYgPC0gcGFzdGUoIlRoZSBtaW5pbXVtIFRVTU9VUi5QTUNGUkVRIGluIHNhbXBsZSIsIHNhbXBsZSwgInVzZWQgZm9yIGZpbHRlcmluZyBpcyIsIG1pbih1bmlxdWUoZjQkVFVNT1VSLlBNQ0ZSRVEpKSwgImFuZCB0aGUgbWluaW11bSBmaWd1cmUgZm9yIHRoZSB2YXJpYW50cyB1c2VkIGZvciBtdXRhdGlvbiBzaWduYXR1cmUgYW5hbHlzaXMgaXMiLCBtaW4odW5pcXVlKGY2JFRVTU9VUi5QTUNGUkVRKSkpCiAgICAgIHcgPC0gcGFzdGUoIlRoZSBtYXhpbXV1bSBHbm9tQURfdjIuMV9ub25fY2FuY2VyX0FGIGluIHNhbXBsZSIsIHNhbXBsZSwgInVzZWQgZm9yIGZpbHRlcmluZyBpcyIsIG1heCh1bmlxdWUoZjUkR25vbUFEX3YyLjFfbm9uX2NhbmNlcl9BRikpLCAiYW5kIHRoZSBtYXhpbXVtIGZpZ3VyZSBmb3IgdGhlIHZhcmlhbnRzIHVzZWQgZm9yIG11dGF0aW9uIHNpZ25hdHVyZSBhbmFseXNpcyBpcyIsIG1heCh1bmlxdWUoZjYkR25vbUFEX3YyLjFfbm9uX2NhbmNlcl9BRikpKQogICAgICB4IDwtIHBhc3RlKCJUaGUgbWF4aW11bSBHbm9tQURfdjNfQUYgaW4gc2FtcGxlIiwgc2FtcGxlLCAidXNlZCBmb3IgZmlsdGVyaW5nIGFuZCBmb3IgdGhlIHZhcmlhbnRzIHVzZWQgZm9yIG11dGF0aW9uIHNpZ25hdHVyZSBhbmFseXNpcyBpcyIsIG1heCh1bmlxdWUoZjYkR25vbUFEX3YzX0FGKSkpCiAgICAgIHBhcmFtZXRlcnMgPC0gcHJpbnQoYyhzLHQsdSx2LHcseCkpCiAgICAgIG9wdGlvbnMoc2NpcGVuID0gMCkKICAgICAgc2lncy5pbnB1dCA9IHJ1bl9kZWNvbnN0cnVjdFNpZ3MobXV0LnJlZjIpCiAgICAgIHNpZ3MuaW5wdXQgPSBydW5fZGVjb25zdHJ1Y3RTaWdzKG11dC5yZWYyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWduYXR1cmVzLnJlZiA9IFNCUy5DT1NNSUMuZXhvbWUudjMubWF5MjAxOS5IR1NPdkNhKQogICAgICBzaWdzLmlucHV0ID0gcnVuX2RlY29uc3RydWN0U2lncyhtdXQucmVmMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmF0dXJlcy5yZWYgPSBzaWduYXR1cmVzLmNvc21pYykKICAgICAgc2V0d2Qoc2FtcGxlKQogICAgICBtdXQucmVmMyA9IG11dC5yZWYyWyxjKDM6NSldCiAgICAgIG11dC5yZWYuY2hyID0gdGliYmxlKGdzdWIoIltjaHJdIiwiIiwgbXV0LnJlZjIkQ0hST00pKQogICAgICBiaW5kX2NvbHMobXV0LnJlZi5jaHIsbXV0LnJlZjMpICU+JSB3cml0ZV90c3YocGF0aD1wYXN0ZShzYW1wbGUsInVuaXF1ZV9tdXRhdGlvbnNfZm9yU2lnbmFsLnRzdiIsc2VwPSJfIiksY29sX25hbWVzPUZBTFNFKQogICAgICByZXN1bHRzID0gcnVuX3NpZ3MoZGF0YT1tdXQucmVmMiwgYm9vdHN0cmFwLnJlcHMgPSAxMDAsIHBsb3QgPSBUUlVFLCBwbG90LmZpbGUgPSBwYXN0ZShzYW1wbGUsIl9zaWduYXR1cmVzLnBkZiIsc2VwPSIiKSkKICAgICAgd3JpdGUudGFibGUocmVzdWx0cyRleHBvc3VyZXMsIGZpbGU9cGFzdGUoc2FtcGxlLCJzaWduYXR1cmVzX2V4cG9zdXJlcy50c3YiLHNlcD0iXyIpLCBzZXA9J1x0JywgY29sLm5hbWVzPVQsIHJvdy5uYW1lcz1GLCBxdW90ZT1GKQogICAgICB3cml0ZS50YWJsZShyZXN1bHRzJGVycm9ycywgZmlsZT1wYXN0ZShzYW1wbGUsInNpZ25hdHVyZXNfZXJyb3JzLnRzdiIsc2VwPSJfIiksIHNlcD0nXHQnLCBjb2wubmFtZXM9VCwgcm93Lm5hbWVzPUYsIHF1b3RlPUYpCiAgICAgIHdyaXRlLnRhYmxlKHJlc3VsdHMkZXhwb3N1cmVzLmJvb3RzdHJhcCwgZmlsZT1wYXN0ZShzYW1wbGUsInNpZ25hdHVyZXNfZXhwb3N1cmVzX2Jvb3RzdHJhcHMudHN2IixzZXA9Il8iKSwgc2VwPSdcdCcsIGNvbC5uYW1lcz1ULCByb3cubmFtZXM9RiwgcXVvdGU9RikKICAgICAgd3JpdGUudGFibGUocmVzdWx0cyRlcnJvcnMuYm9vdHN0cmFwLCBmaWxlPXBhc3RlKHNhbXBsZSwic2lnbmF0dXJlc19lcnJvcnMuYm9vdHN0YXBzLnRzdiIsc2VwPSJfIiksIHNlcD0nXHQnLCBjb2wubmFtZXM9VCwgcm93Lm5hbWVzPUYsIHF1b3RlPUYpCiAgICAgIHdyaXRlLnRhYmxlKHBhcmFtZXRlcnMsZmlsZT1wYXN0ZShzYW1wbGUsIl9wYXJhbWV0ZXJzLnR4dCIsc2VwPSIiKSwgcXVvdGU9Rixyb3cubmFtZXMgPSBGLHNlcD0iXHQiKQogICAgfQpgYGAK