1 batch correction - GSM3872442 set

GSM3872442 is a single PBMMC sample sequenced as a pool of two libraries: SRR9264351 and SRR9264352.

We will use this sample to illustrate batch correction.

qcPlotDirBit <- "NormPlots"
setName <- "GSM3872442"

projDir <- "/mnt/scratcha/bioinformatics/baller01/20200511_FernandesM_ME_crukBiSs2020"
outDirBit <- "AnaWiSce/Attempt1"

1.1  Prepare data

Load object

setSuf <- ""

# Read object in:
tmpFn <- sprintf("%s/%s/Robjects/%s_sce_nz_postQc%s.Rds", projDir, outDirBit, "caron", setSuf)
sce <- readRDS(tmpFn)

Select the GSM3872442 cells:

sample1.nz.sce <- SingleCellExperiment(list(counts=counts(sce[, sce$Run %in% c("SRR9264351")])),
                                       colData=colData(sce[, sce$Run %in% c("SRR9264351")]))

sample2.nz.sce <- SingleCellExperiment(list(counts=counts(sce[, sce$Run %in% c("SRR9264352")])),
                                       colData=colData(sce[, sce$Run %in% c("SRR9264352")]))

1.2  Normalise each separately and re-pool

sample1.clusters <- quickCluster(sample1.nz.sce, method="igraph")
sample1.nz.sce <- computeSumFactors(sample1.nz.sce, min.mean=0.1, cluster=sample1.clusters)
sample1.nz.sce <- logNormCounts(sample1.nz.sce)

sample2.clusters <- quickCluster(sample2.nz.sce, method="igraph")
sample2.nz.sce <- computeSumFactors(sample2.nz.sce, min.mean=0.1, cluster=sample2.clusters)
sample2.nz.sce <- logNormCounts(sample2.nz.sce)

Re-pool:

# recombine the normalized samples together
all.samp.exprs <- do.call(cbind,
                          list("SRR9264351"=exprs(sample1.nz.sce),
                               "SRR9264352"=exprs(sample2.nz.sce)))
colnames(all.samp.exprs) <- c(as.character(colData(sample1.nz.sce)$Barcode),
                              as.character(colData(sample2.nz.sce)$Barcode))

For the PCA we want to quickly select the genes that are most informative. We will use the top 2000 genes with the highest variance.

gene.variances <- apply(all.samp.exprs, 1, var)
names(gene.variances) <- rownames(all.samp.exprs)
highly.variable.genes <- names(gene.variances[order(gene.variances, decreasing=TRUE)])[1:2000]

Perform PCA:

# we need to use a fast approximate algorithm for PCA on large data sets
# this algorithm has a stochastic component, so we need to fix the seed number to get the same result each time
set.seed(42)
separate.hvg.pca <- irlba::prcomp_irlba(t(all.samp.exprs[highly.variable.genes, ]), n=5) # we only need a few components
separate.hvg.pcs <- as.data.frame(separate.hvg.pca$x) # extract the principal components
separate.hvg.pcs$Cell <- colnames(all.samp.exprs) # set the sample column as the cell IDs

# combine the PCs with the sample information into a single data frame for plotting
samples.info <- data.frame("Cell"=colnames(all.samp.exprs),
                           "Run"=c(rep("SRR9264351", ncol(sample1.nz.sce)), 
                                   rep("SRR9264352", ncol(sample2.nz.sce))))

# merge the two data frames together
separate.pca.merge <- merge(separate.hvg.pcs, samples.info, by='Cell')

Plot PC1-PC2 plane, with cells colored by ‘Run’ (and sized according to library size):

sce.sep <- cbind(sample1.nz.sce, sample2.nz.sce)
sce.sep <- runPCA(sce.sep)
plotPCA(sce.sep, colour_by="Run", size_by = "sum")

sce.sep <- runTSNE(sce.sep, dimred="PCA")
plotTSNE(sce.sep, colour_by="Run", size_by = "sum")

sce.sep <- runUMAP(sce.sep, dimred="PCA")
plotUMAP(sce.sep, colour_by="Run", size_by = "sum")

1.3 Normalise batches together

sample3.nz.sce <- SingleCellExperiment(list(counts=counts(sce[, sce$Run %in% c("SRR9264351", "SRR9264352")])),
                                       colData=colData(sce[, sce$Run %in% c("SRR9264351", "SRR9264352")]))

sample3.clusters <- quickCluster(sample3.nz.sce, method="igraph")
sample3.nz.sce <- computeSumFactors(sample3.nz.sce, min.mean=0.1, cluster=sample3.clusters)
sample3.nz.sce <- logNormCounts(sample3.nz.sce)

pool.exprs <- exprs(sample3.nz.sce)
colnames(pool.exprs) <- gsub(colData(sample3.nz.sce)$Barcode, pattern="-", replacement=".")

Find the 2000 genes with the highest variance:

gene.variances <- apply(pool.exprs, 1, var)
names(gene.variances) <- rownames(pool.exprs)
highly.variable.genes <- names(gene.variances[order(gene.variances, decreasing=TRUE)])[1:2000]

Perform PCA:

# we need to use a fast approximate algorithm for PCA on large data sets
# this algorithm has a stochastic component, so we need to fix the seed number to get the same result each time
set.seed(42)
combined.hvg.pca <- irlba::prcomp_irlba(t(pool.exprs[highly.variable.genes, ]), n=5) # we only need a few components
combined.hvg.pcs <- as.data.frame(combined.hvg.pca$x) # extract the principal components
combined.hvg.pcs$Cell <- colnames(pool.exprs) # set the sample column as the cell IDs

# combine the PCs with the sample information into a single data frame for plotting
samples.info <- data.frame("Cell"=colnames(pool.exprs),
                           "Run"=colData(sample3.nz.sce)$Run)

# merge the two data frames together
combined.pca.merge <- merge(combined.hvg.pcs, samples.info, by='Cell')

Plot PC1-PC2 plane, with cells colored by ‘Run’ (and sized according to library size):

sample3.nz.sce <- runPCA(sample3.nz.sce)
plotPCA(sample3.nz.sce, colour_by="Run", size_by = "sum")

sample3.nz.sce <- runTSNE(sample3.nz.sce, dimred="PCA")
plotTSNE(sample3.nz.sce, colour_by="Run", size_by = "sum")

sample3.nz.sce <- runUMAP(sample3.nz.sce, dimred="PCA")
plotUMAP(sample3.nz.sce, colour_by="Run", size_by = "sum")

1.4 Batch correction

sample3.nz.sce$Run <- factor(sample3.nz.sce$Run)
sample3.nz.sce$batch <- sample3.nz.sce$Run
sce <- sample3.nz.sce

1.4.1 Gaussian (normal) linear models

Limma

suppressMessages(require(limma))
lm_design_batch <- model.matrix(~0 + batch, data = colData(sce))
fit_lm_batch <- lmFit(logcounts(sce), lm_design_batch)
resids_lm_batch <- residuals(fit_lm_batch, logcounts(sce))
assay(sce, "lm_batch") <- resids_lm_batch

reducedDim(sce, "PCA_lm_batch") <- reducedDim(
  runPCA(sce, exprs_values = "lm_batch"), "PCA")

plotReducedDim(sce, dimred = "PCA_lm_batch",
        colour_by = "batch", 
        size_by = "sum",
        shape_by = "Sample.Name"
        ) +
  ggtitle("LM - regress out batch")

scePreSct <- sce

1.5 SCTransform

1.5.1 Batch only

First make a copy of the SCE object (we will need one later).

# have log lib size
sce$log10sum <- log10(sce$sum)
sceOrig <- sce
counts <- counts(sce)
colnames(counts) <- colData(sce)$Barcode

### Genes expressed in at least 5 cells will be kept
sctnorm_data <- sctransform::vst(umi = counts, min_cells = 5,
                                 cell_attr = as.data.frame(colData(sce))[,c("log10sum", "batch")],
                                 latent_var = c("batch"),
                 return_gene_attr = TRUE,
                 return_cell_attr = TRUE,
                 show_progress = FALSE)
Warning in sqrt(1/i): NaNs produced



Check model used:

# model:
print(sctnorm_data$model_str)
[1] "y ~ batch"

Check new values (here 3 rows and 3 columns only):

sctnorm_data$y[1:3,1:3]
                AAACCTGCACTTCGAA-9 AAACCTGCAGACGCAA-9 AAACCTGTCATCACCC-9
ENSG00000237491         -0.1869210         -0.1869210         -0.1869210
ENSG00000225880         -0.1085787         -0.1085787         -0.1085787
ENSG00000230368         -0.2034637         -0.2034637         -0.2034637

Check object:

sce
class: SingleCellExperiment 
dim: 18372 2059 
metadata(0):
assays(3): counts logcounts lm_batch
rownames(18372): ENSG00000238009 ENSG00000237491 ... ENSG00000275063
  ENSG00000271254
rowData names(0):
colnames: NULL
colData names(22): Sample Barcode ... batch log10sum
reducedDimNames(4): PCA TSNE UMAP PCA_lm_batch
altExpNames(0):

Some genes were not included in the transformation and excluded from the output, so we will remove them from the SCE object too.

# exclude genes that were not used in the transformation: 
tmpInd <- which(rownames(sce) %in% rownames(sctnorm_data$y))
cols.meta <- colData(sceOrig)
rows.meta <- rowData(sceOrig)

new.counts <- counts(sceOrig)[tmpInd, ]
sce <- SingleCellExperiment(list(counts=new.counts))

# reset the column data on the new object
colData(sce) <- cols.meta
rowData(sce) <- rows.meta[tmpInd, ]

We now copy the transformation output to the SCE object:

vstMat <- as(sctnorm_data$y[rownames(sce),], "dgCMatrix")
all(colnames(vstMat) == sce$Barcode)
[1] TRUE
dim(vstMat)
[1] 13848  2059
colnames(vstMat) <- NULL
assay(sce, "sctrans_norm_batchOnly") <- vstMat # as(vst_out$y[rownames(sce),], "dgCMatrix")

Also copy ‘logcounts’:

assayX <- "logcounts"
tmpAssay <- assay(sceOrig, assayX)
assay(sce, assayX) <- tmpAssay[tmpInd, ]

Diagnostic plots are shown below:

sctransform::plot_model_pars(sctnorm_data)

The reduced dimension plots below show improved mixing of cells from the two sets:

reducedDim(sce, "PCA_sctrans_norm_batchOnly") <- reducedDim(
  runPCA(sce, exprs_values = "sctrans_norm_batchOnly"), "PCA"
)
plotReducedDim(
  sce,
  dimred = "PCA_sctrans_norm_batchOnly",
  colour_by = "batch",
  size_by = "sum",
  shape_by = "Sample.Name"
) + ggtitle("PCA plot: sctransform normalization - batch only") 

sce <- runTSNE(sce, dimred="PCA_sctrans_norm_batchOnly", name="TSNE_sctrans_norm_batchOnly")
plotReducedDim(
  sce,
  dimred = "TSNE_sctrans_norm_batchOnly",
  colour_by = "batch",
  size_by = "sum",
  shape_by = "Sample.Name"
) + ggtitle("TSNE plot: sctransform normalization - batch only") 

sce <- runUMAP(sce, dimred="PCA_sctrans_norm_batchOnly", name="UMAP_sctrans_norm_batchOnly")
plotReducedDim(
  sce,
  dimred = "UMAP_sctrans_norm_batchOnly",
  colour_by = "batch",
  size_by = "sum",
  shape_by = "Sample.Name"
) + ggtitle("UMAP plot: sctransform normalization - batch only") 

Keep copy of SCE object for later:

sce_batchOnly <- sce

1.5.2 Both library size and batch

Use the copy of the SCE object made earlier.

sce <- sceOrig

Some cells are very different from the rest.

### Genes expressed in at least 5 cells will be kept
counts <- counts(sce)
class(counts)
[1] "dgCMatrix"
attr(,"package")
[1] "Matrix"
dgCMatrix
colnames(counts) <- colData(sce)$Barcode

sctnorm_data <- sctransform::vst(umi = counts, min_cells = 5,
                                 cell_attr = as.data.frame(colData(sce))[,c("log10sum", "batch")],
                                 latent_var = c("log10sum", "batch"),
                 return_gene_attr = TRUE,
                 return_cell_attr = TRUE,
                 show_progress = FALSE)

Check model used:

print(sctnorm_data$model_str)
[1] "y ~ log10sum + batch"

Discard genes that were not used in the transformation.

# exclude genes that were not used in the transformation: 
tmpInd <- which(rownames(sce) %in% rownames(sctnorm_data$y))
cols.meta <- colData(sceOrig)
rows.meta <- rowData(sceOrig)

new.counts <- counts(sceOrig)[tmpInd, ]
sce <- SingleCellExperiment(list(counts=new.counts))

# reset the column data on the new object
colData(sce) <- cols.meta
rowData(sce) <- rows.meta[tmpInd, ]

Copy the transformation output to the SCE object.

vstMat <- as(sctnorm_data$y[rownames(sce),], "dgCMatrix")
all(colnames(vstMat) == sce$Barcode)
[1] TRUE
colnames(vstMat) <- NULL
assay(sce, "sctrans_norm") <- vstMat

Show diagnostic plots:

sctransform::plot_model_pars(sctnorm_data)

Show reduced dimension plots and check for improved mixing of cells from the two sets:

reducedDim(sce, "PCA_sctrans_norm") <- reducedDim(
  runPCA(sce, exprs_values = "sctrans_norm")
)
plotReducedDim(
  sce,
  dimred = "PCA_sctrans_norm",
  colour_by = "batch",
  size_by = "sum",
  shape_by = "Sample.Name"
) + ggtitle("PCA plot: sctransform normalization") 

sce <- runTSNE(sce, dimred="PCA_sctrans_norm", name="TSNE_sctrans_norm")
plotReducedDim(
  sce,
  dimred = "TSNE_sctrans_norm",
  colour_by = "batch",
  size_by = "sum",
  shape_by = "Sample.Name"
) + ggtitle("TSNE plot: sctransform normalization") 

sce <- runUMAP(sce, dimred="PCA_sctrans_norm", name="UMAP_sctrans_norm")
plotReducedDim(
  sce,
  dimred = "UMAP_sctrans_norm",
  colour_by = "batch",
  size_by = "sum",
  shape_by = "Sample.Name"
) + ggtitle("UMAP plot: sctransform normalization") 

Add PCA_sctrans_norm_batchOnly (same cells, only genes may differ)

reducedDim(sce, "PCA_sctrans_norm_batchOnly") <- reducedDim(sce_batchOnly, "PCA_sctrans_norm_batchOnly")
reducedDim(sce, "TSNE_sctrans_norm_batchOnly") <- reducedDim(sce_batchOnly, "TSNE_sctrans_norm_batchOnly")
reducedDim(sce, "UMAP_sctrans_norm_batchOnly") <- reducedDim(sce_batchOnly, "UMAP_sctrans_norm_batchOnly")
scePostSct <- sce

1.6 mnnCorrect

1.6.1  Check presence of batch effect

Same as above but with batchelor commands to make the two batches and identify highly variable genes for faster dimensionality reduction.

sce <- sample3.nz.sce
library(batchelor)
# Mind assayNames()
sce1 <- sce[, sce$Run == "SRR9264351"]
sce2 <- sce[, sce$Run == "SRR9264352"]
library(scran)
dec1 <- modelGeneVar(sce1)
dec2 <- modelGeneVar(sce2)
combined.dec <- combineVar(dec1, dec2)
chosen.hvgs <- combined.dec$bio > 0
summary(chosen.hvgs)
   Mode   FALSE    TRUE 
logical    8942    9430 

As a diagnostic, we check that there actually is a batch effect across these datasets by checking that they cluster separately. Here, we combine the two SingleCellExperiment objects without any correction using the NoCorrectParam() flag, and we informally verify that cells from different batches are separated using a t-SNE plot.

There is a moderate batch effect.

library(scater)
combined <- correctExperiments(A=sce1, B=sce2, PARAM=NoCorrectParam())
Warning in correctExperiments(A = sce1, B = sce2, PARAM = NoCorrectParam()):
ignoring 'colData' fields overlapping 'batchCorrect' output
combined <- runPCA(combined, subset_row=chosen.hvgs)
combined <- runTSNE(combined, dimred="PCA")
combined <- runUMAP(combined, dimred="PCA")
plotPCA(combined, colour_by="batch")

plotTSNE(combined, colour_by="batch")

plotUMAP(combined, colour_by="batch")

reducedDim(sce, "PCA_noCor") <- reducedDim(combined, "PCA")
reducedDim(sce, "TSNE_noCor") <- reducedDim(combined, "TSNE")
reducedDim(sce, "UMAP_noCor") <- reducedDim(combined, "UMAP")

1.6.2 Correct batch effect with mnnCorrect

This is the initial method. It uses gene expression values to identify cells with similar expression patterns in both batches.

Let us get the normalised counts:

batch1 <- logcounts(sce1)
batch2 <- logcounts(sce2)
y <- batchelor::mnnCorrect(
          batch1, batch2,  
      #subset.row = fewer.hvgs,
      correct.all = TRUE,
          k = 20,
          sigma = 0.1,
          cos.norm.in = TRUE,
          svd.dim = 2
        )

Copy the corrected values to the SCE object:

assay(sce, "mnn") <- assay(y, "corrected")

Show reduced dimension plots and check for improved mixing of cells from the two sets:

sce <- runPCA(sce, exprs_values = "mnn")
plotPCA(sce, colour_by="batch")

reducedDim(sce, "PCA_mnn") <- reducedDim(sce, "PCA")
sce <- runTSNE(sce, dimred="PCA_mnn")
plotTSNE(sce, colour_by="batch")

reducedDim(sce, "TSNE_mnn") <- reducedDim(sce, "TSNE")
sce <- runUMAP(sce, dimred="PCA_mnn")
plotUMAP(sce, colour_by="batch")

reducedDim(sce, "UMAP_mnn") <- reducedDim(sce, "UMAP")

1.7 fastMNN

This method is faster than mnnCorrect as it identifies nearest neighbours after dimensionality reduction.

fx <- batchelor::fastMNN(
                      sce,
              #correct.all = TRUE,
                      batch = sce$Run
            )
class(fx)
[1] "SingleCellExperiment"
attr(,"package")
[1] "SingleCellExperiment"
SingleCellExperiment

Copy the corrected values to the SCE object:

# fastMNN may drop some genes
# so we may not be able to keep the outcome in 'assay'
assay(sce, "fastmnn") <- assay(fx, "reconstructed")

Show reduced dimension plots and check for improved mixing of cells from the two sets:

fastmnn_pca <- runPCA(assay(sce, "fastmnn"), rank=2) # slow
reducedDim(sce, "PCA_fastmnn") <- fastmnn_pca$rotation
plotReducedDim(
  sce,
  dimred = "PCA_fastmnn",
  colour_by = "batch",
  size_by = "sum",
  shape_by = "Sample.Name"
) + ggtitle("PCA plot: fastMNN") 

sce <- runTSNE(sce, dimred="PCA_fastmnn")
plotTSNE(sce, colour_by="batch")

reducedDim(sce, "TSNE_fastmnn") <- reducedDim(sce, "TSNE")
sce <- runUMAP(sce, dimred="PCA_fastmnn")
plotUMAP(sce, colour_by="batch")

reducedDim(sce, "UMAP_fastmnn") <- reducedDim(sce, "UMAP")

1.8 Harmony

Harmony [Korsunsky2018fast] is a newer batch correction method, which is designed to operate on PC space. The algorithm proceeds to iteratively cluster the cells, with the objective function formulated to promote cells from multiple datasets within each cluster. Once a clustering is obtained, the positions of the centroids of each dataset are obtained on a per-cluster basis and the coordinates are corrected. This procedure is iterated until convergence. Harmony comes with a theta parameter that controls the degree of batch correction (higher values lead to more dataset integration), and can account for multiple experimental and biological factors on input (see variant of the ‘Hemberg course’).

library(harmony)
Loading required package: Rcpp
reducedDim(sce, "PCA_logcounts") <- reducedDim(
  runPCA(sce, exprs_values = "logcounts")
)

#Seeing how the end result of Harmony is an altered dimensional reduction space created on the basis of PCA, we plot the obtained manifold here and exclude it from the rest of the follow-ups in the section.

pca <- as.matrix(reducedDim(sce, "PCA_logcounts"))
harmony_emb <- HarmonyMatrix(pca, sce$batch, theta=2, do_pca=FALSE)
Harmony 1/10
Harmony 2/10
Harmony 3/10
Harmony 4/10
Harmony 5/10
Harmony 6/10
Harmony 7/10
Harmony converged after 7 iterations
reducedDim(sce, "harmony") <- harmony_emb

plotReducedDim(
    sce,
    dimred = 'harmony',
    colour_by = "batch",
    size_by = "sum",
    shape_by = "Sample.Name"
)

LS0tCnRpdGxlOiAiQ1JVSyBDSSBTdW1tZXIgU2Nob29sIDIwMjAgLSBpbnRyb2R1Y3Rpb24gdG8gc2luZ2xlLWNlbGwgUk5BLXNlcSBhbmFseXNpcyIKc3VidGl0bGU6ICdiYXRjaCBjb3JyZWN0aW9uIC0gNTAwIGNlbGxzIHBlciBzYW1wbGUnCgphdXRob3I6ICJTdGVwaGFuZSBCYWxsZXJlYXUsIFpleW5lcCBLYWxlbmRlciBBdGFrLCBLYXRhcnp5bmEgS2FuaWEiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvYzogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIGNvZGVfZm9sZGluZzogaGlkZQogIGh0bWxfYm9vazoKICAgIGNvZGVfZm9sZGluZzogaGlkZQpwYXJhbXM6CiAgb3V0RGlyQml0OiAiQW5hV2lTY2UvQXR0ZW1wdDEiCi0tLQoKIyBiYXRjaCBjb3JyZWN0aW9uIC0gR1NNMzg3MjQ0MiBzZXQKCkdTTTM4NzI0NDIgaXMgYSBzaW5nbGUgUEJNTUMgc2FtcGxlIHNlcXVlbmNlZCBhcyBhIHBvb2wgb2YgdHdvIGxpYnJhcmllczogU1JSOTI2NDM1MSBhbmQgU1JSOTI2NDM1Mi4KCldlIHdpbGwgdXNlIHRoaXMgc2FtcGxlIHRvIGlsbHVzdHJhdGUgYmF0Y2ggY29ycmVjdGlvbi4KCmBgYHtyIEdTTTM4NzI0NDJfdmFyaWFibGVzX25vcm19CnFjUGxvdERpckJpdCA8LSAiTm9ybVBsb3RzIgpzZXROYW1lIDwtICJHU00zODcyNDQyIgoKcHJvakRpciA8LSAiL21udC9zY3JhdGNoYS9iaW9pbmZvcm1hdGljcy9iYWxsZXIwMS8yMDIwMDUxMV9GZXJuYW5kZXNNX01FX2NydWtCaVNzMjAyMCIKb3V0RGlyQml0IDwtICJBbmFXaVNjZS9BdHRlbXB0MSIKYGBgCgpgYGB7ciwgaW5jbHVkZT1GQUxTRX0Kc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHNjYXRlcikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShzY3JhbikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShnZ3Bsb3QyKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGRwbHlyKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KEJpb2NTaW5ndWxhcikpCmBgYAoKIyPCoFByZXBhcmUgZGF0YQoKTG9hZCBvYmplY3QgCgpgYGB7ciBHU00zODcyNDQyX3JlYWRJbn0Kc2V0U3VmIDwtICIiCgojIFJlYWQgb2JqZWN0IGluOgp0bXBGbiA8LSBzcHJpbnRmKCIlcy8lcy9Sb2JqZWN0cy8lc19zY2VfbnpfcG9zdFFjJXMuUmRzIiwgcHJvakRpciwgb3V0RGlyQml0LCAiY2Fyb24iLCBzZXRTdWYpCnNjZSA8LSByZWFkUkRTKHRtcEZuKQpgYGAKClNlbGVjdCB0aGUgR1NNMzg3MjQ0MiBjZWxsczoKCmBgYHtyfQpzYW1wbGUxLm56LnNjZSA8LSBTaW5nbGVDZWxsRXhwZXJpbWVudChsaXN0KGNvdW50cz1jb3VudHMoc2NlWywgc2NlJFJ1biAlaW4lIGMoIlNSUjkyNjQzNTEiKV0pKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YT1jb2xEYXRhKHNjZVssIHNjZSRSdW4gJWluJSBjKCJTUlI5MjY0MzUxIildKSkKCnNhbXBsZTIubnouc2NlIDwtIFNpbmdsZUNlbGxFeHBlcmltZW50KGxpc3QoY291bnRzPWNvdW50cyhzY2VbLCBzY2UkUnVuICVpbiUgYygiU1JSOTI2NDM1MiIpXSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhPWNvbERhdGEoc2NlWywgc2NlJFJ1biAlaW4lIGMoIlNSUjkyNjQzNTIiKV0pKQpgYGAKCiMjwqBOb3JtYWxpc2UgZWFjaCBzZXBhcmF0ZWx5IGFuZCByZS1wb29sCgpgYGB7cn0Kc2FtcGxlMS5jbHVzdGVycyA8LSBxdWlja0NsdXN0ZXIoc2FtcGxlMS5uei5zY2UsIG1ldGhvZD0iaWdyYXBoIikKc2FtcGxlMS5uei5zY2UgPC0gY29tcHV0ZVN1bUZhY3RvcnMoc2FtcGxlMS5uei5zY2UsIG1pbi5tZWFuPTAuMSwgY2x1c3Rlcj1zYW1wbGUxLmNsdXN0ZXJzKQpzYW1wbGUxLm56LnNjZSA8LSBsb2dOb3JtQ291bnRzKHNhbXBsZTEubnouc2NlKQoKc2FtcGxlMi5jbHVzdGVycyA8LSBxdWlja0NsdXN0ZXIoc2FtcGxlMi5uei5zY2UsIG1ldGhvZD0iaWdyYXBoIikKc2FtcGxlMi5uei5zY2UgPC0gY29tcHV0ZVN1bUZhY3RvcnMoc2FtcGxlMi5uei5zY2UsIG1pbi5tZWFuPTAuMSwgY2x1c3Rlcj1zYW1wbGUyLmNsdXN0ZXJzKQpzYW1wbGUyLm56LnNjZSA8LSBsb2dOb3JtQ291bnRzKHNhbXBsZTIubnouc2NlKQpgYGAKClJlLXBvb2w6CgpgYGB7cn0KIyByZWNvbWJpbmUgdGhlIG5vcm1hbGl6ZWQgc2FtcGxlcyB0b2dldGhlcgphbGwuc2FtcC5leHBycyA8LSBkby5jYWxsKGNiaW5kLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QoIlNSUjkyNjQzNTEiPWV4cHJzKHNhbXBsZTEubnouc2NlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTUlI5MjY0MzUyIj1leHBycyhzYW1wbGUyLm56LnNjZSkpKQpjb2xuYW1lcyhhbGwuc2FtcC5leHBycykgPC0gYyhhcy5jaGFyYWN0ZXIoY29sRGF0YShzYW1wbGUxLm56LnNjZSkkQmFyY29kZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3Rlcihjb2xEYXRhKHNhbXBsZTIubnouc2NlKSRCYXJjb2RlKSkKYGBgCgpGb3IgdGhlIFBDQSB3ZSB3YW50IHRvIHF1aWNrbHkgc2VsZWN0IHRoZSBnZW5lcyB0aGF0IGFyZSBtb3N0IGluZm9ybWF0aXZlLiBXZSB3aWxsIHVzZSB0aGUgdG9wIDIwMDAgZ2VuZXMgd2l0aCB0aGUgaGlnaGVzdCB2YXJpYW5jZS4KCmBgYHtyfQpnZW5lLnZhcmlhbmNlcyA8LSBhcHBseShhbGwuc2FtcC5leHBycywgMSwgdmFyKQpuYW1lcyhnZW5lLnZhcmlhbmNlcykgPC0gcm93bmFtZXMoYWxsLnNhbXAuZXhwcnMpCmhpZ2hseS52YXJpYWJsZS5nZW5lcyA8LSBuYW1lcyhnZW5lLnZhcmlhbmNlc1tvcmRlcihnZW5lLnZhcmlhbmNlcywgZGVjcmVhc2luZz1UUlVFKV0pWzE6MjAwMF0KYGBgCgpQZXJmb3JtIFBDQToKCmBgYHtyfQojIHdlIG5lZWQgdG8gdXNlIGEgZmFzdCBhcHByb3hpbWF0ZSBhbGdvcml0aG0gZm9yIFBDQSBvbiBsYXJnZSBkYXRhIHNldHMKIyB0aGlzIGFsZ29yaXRobSBoYXMgYSBzdG9jaGFzdGljIGNvbXBvbmVudCwgc28gd2UgbmVlZCB0byBmaXggdGhlIHNlZWQgbnVtYmVyIHRvIGdldCB0aGUgc2FtZSByZXN1bHQgZWFjaCB0aW1lCnNldC5zZWVkKDQyKQpzZXBhcmF0ZS5odmcucGNhIDwtIGlybGJhOjpwcmNvbXBfaXJsYmEodChhbGwuc2FtcC5leHByc1toaWdobHkudmFyaWFibGUuZ2VuZXMsIF0pLCBuPTUpICMgd2Ugb25seSBuZWVkIGEgZmV3IGNvbXBvbmVudHMKc2VwYXJhdGUuaHZnLnBjcyA8LSBhcy5kYXRhLmZyYW1lKHNlcGFyYXRlLmh2Zy5wY2EkeCkgIyBleHRyYWN0IHRoZSBwcmluY2lwYWwgY29tcG9uZW50cwpzZXBhcmF0ZS5odmcucGNzJENlbGwgPC0gY29sbmFtZXMoYWxsLnNhbXAuZXhwcnMpICMgc2V0IHRoZSBzYW1wbGUgY29sdW1uIGFzIHRoZSBjZWxsIElEcwoKIyBjb21iaW5lIHRoZSBQQ3Mgd2l0aCB0aGUgc2FtcGxlIGluZm9ybWF0aW9uIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZSBmb3IgcGxvdHRpbmcKc2FtcGxlcy5pbmZvIDwtIGRhdGEuZnJhbWUoIkNlbGwiPWNvbG5hbWVzKGFsbC5zYW1wLmV4cHJzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJ1biI9YyhyZXAoIlNSUjkyNjQzNTEiLCBuY29sKHNhbXBsZTEubnouc2NlKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgiU1JSOTI2NDM1MiIsIG5jb2woc2FtcGxlMi5uei5zY2UpKSkpCgojIG1lcmdlIHRoZSB0d28gZGF0YSBmcmFtZXMgdG9nZXRoZXIKc2VwYXJhdGUucGNhLm1lcmdlIDwtIG1lcmdlKHNlcGFyYXRlLmh2Zy5wY3MsIHNhbXBsZXMuaW5mbywgYnk9J0NlbGwnKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpnZ3Bsb3Qoc2VwYXJhdGUucGNhLm1lcmdlLCBhZXMoeD1QQzEsIHk9UEMyLCBmaWxsPVJ1bikpICsKICBnZW9tX3BvaW50KHNoYXBlPTIxLCBzaXplPTMpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpQbG90IFBDMS1QQzIgcGxhbmUsIHdpdGggY2VsbHMgY29sb3JlZCBieSAnUnVuJyAoYW5kIHNpemVkIGFjY29yZGluZyB0byBsaWJyYXJ5IHNpemUpOgoKYGBge3J9CnNjZS5zZXAgPC0gY2JpbmQoc2FtcGxlMS5uei5zY2UsIHNhbXBsZTIubnouc2NlKQpzY2Uuc2VwIDwtIHJ1blBDQShzY2Uuc2VwKQpwbG90UENBKHNjZS5zZXAsIGNvbG91cl9ieT0iUnVuIiwgc2l6ZV9ieSA9ICJzdW0iKQpgYGAKCmBgYHtyfQpzY2Uuc2VwIDwtIHJ1blRTTkUoc2NlLnNlcCwgZGltcmVkPSJQQ0EiKQpwbG90VFNORShzY2Uuc2VwLCBjb2xvdXJfYnk9IlJ1biIsIHNpemVfYnkgPSAic3VtIikKYGBgCgpgYGB7cn0Kc2NlLnNlcCA8LSBydW5VTUFQKHNjZS5zZXAsIGRpbXJlZD0iUENBIikKcGxvdFVNQVAoc2NlLnNlcCwgY29sb3VyX2J5PSJSdW4iLCBzaXplX2J5ID0gInN1bSIpCmBgYAoKIyMgTm9ybWFsaXNlIGJhdGNoZXMgdG9nZXRoZXIKCmBgYHtyfQpzYW1wbGUzLm56LnNjZSA8LSBTaW5nbGVDZWxsRXhwZXJpbWVudChsaXN0KGNvdW50cz1jb3VudHMoc2NlWywgc2NlJFJ1biAlaW4lIGMoIlNSUjkyNjQzNTEiLCAiU1JSOTI2NDM1MiIpXSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhPWNvbERhdGEoc2NlWywgc2NlJFJ1biAlaW4lIGMoIlNSUjkyNjQzNTEiLCAiU1JSOTI2NDM1MiIpXSkpCgpzYW1wbGUzLmNsdXN0ZXJzIDwtIHF1aWNrQ2x1c3RlcihzYW1wbGUzLm56LnNjZSwgbWV0aG9kPSJpZ3JhcGgiKQpzYW1wbGUzLm56LnNjZSA8LSBjb21wdXRlU3VtRmFjdG9ycyhzYW1wbGUzLm56LnNjZSwgbWluLm1lYW49MC4xLCBjbHVzdGVyPXNhbXBsZTMuY2x1c3RlcnMpCnNhbXBsZTMubnouc2NlIDwtIGxvZ05vcm1Db3VudHMoc2FtcGxlMy5uei5zY2UpCgpwb29sLmV4cHJzIDwtIGV4cHJzKHNhbXBsZTMubnouc2NlKQpjb2xuYW1lcyhwb29sLmV4cHJzKSA8LSBnc3ViKGNvbERhdGEoc2FtcGxlMy5uei5zY2UpJEJhcmNvZGUsIHBhdHRlcm49Ii0iLCByZXBsYWNlbWVudD0iLiIpCmBgYAoKRmluZCB0aGUgMjAwMCBnZW5lcyB3aXRoIHRoZSBoaWdoZXN0IHZhcmlhbmNlOgoKYGBgCmdlbmUudmFyaWFuY2VzIDwtIGFwcGx5KHBvb2wuZXhwcnMsIDEsIHZhcikKbmFtZXMoZ2VuZS52YXJpYW5jZXMpIDwtIHJvd25hbWVzKHBvb2wuZXhwcnMpCmhpZ2hseS52YXJpYWJsZS5nZW5lcyA8LSBuYW1lcyhnZW5lLnZhcmlhbmNlc1tvcmRlcihnZW5lLnZhcmlhbmNlcywgZGVjcmVhc2luZz1UUlVFKV0pWzE6MjAwMF0KYGBgCgpQZXJmb3JtIFBDQToKCmBgYHtyfQojIHdlIG5lZWQgdG8gdXNlIGEgZmFzdCBhcHByb3hpbWF0ZSBhbGdvcml0aG0gZm9yIFBDQSBvbiBsYXJnZSBkYXRhIHNldHMKIyB0aGlzIGFsZ29yaXRobSBoYXMgYSBzdG9jaGFzdGljIGNvbXBvbmVudCwgc28gd2UgbmVlZCB0byBmaXggdGhlIHNlZWQgbnVtYmVyIHRvIGdldCB0aGUgc2FtZSByZXN1bHQgZWFjaCB0aW1lCnNldC5zZWVkKDQyKQpjb21iaW5lZC5odmcucGNhIDwtIGlybGJhOjpwcmNvbXBfaXJsYmEodChwb29sLmV4cHJzW2hpZ2hseS52YXJpYWJsZS5nZW5lcywgXSksIG49NSkgIyB3ZSBvbmx5IG5lZWQgYSBmZXcgY29tcG9uZW50cwpjb21iaW5lZC5odmcucGNzIDwtIGFzLmRhdGEuZnJhbWUoY29tYmluZWQuaHZnLnBjYSR4KSAjIGV4dHJhY3QgdGhlIHByaW5jaXBhbCBjb21wb25lbnRzCmNvbWJpbmVkLmh2Zy5wY3MkQ2VsbCA8LSBjb2xuYW1lcyhwb29sLmV4cHJzKSAjIHNldCB0aGUgc2FtcGxlIGNvbHVtbiBhcyB0aGUgY2VsbCBJRHMKCiMgY29tYmluZSB0aGUgUENzIHdpdGggdGhlIHNhbXBsZSBpbmZvcm1hdGlvbiBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUgZm9yIHBsb3R0aW5nCnNhbXBsZXMuaW5mbyA8LSBkYXRhLmZyYW1lKCJDZWxsIj1jb2xuYW1lcyhwb29sLmV4cHJzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJ1biI9Y29sRGF0YShzYW1wbGUzLm56LnNjZSkkUnVuKQoKIyBtZXJnZSB0aGUgdHdvIGRhdGEgZnJhbWVzIHRvZ2V0aGVyCmNvbWJpbmVkLnBjYS5tZXJnZSA8LSBtZXJnZShjb21iaW5lZC5odmcucGNzLCBzYW1wbGVzLmluZm8sIGJ5PSdDZWxsJykKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KZ2dwbG90KGNvbWJpbmVkLnBjYS5tZXJnZSwgYWVzKHg9UEMxLCB5PVBDMiwgZmlsbD1SdW4pKSArCiAgZ2VvbV9wb2ludChzaGFwZT0yMSwgc2l6ZT0zKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKUGxvdCBQQzEtUEMyIHBsYW5lLCB3aXRoIGNlbGxzIGNvbG9yZWQgYnkgJ1J1bicgKGFuZCBzaXplZCBhY2NvcmRpbmcgdG8gbGlicmFyeSBzaXplKToKCmBgYHtyfQpzYW1wbGUzLm56LnNjZSA8LSBydW5QQ0Eoc2FtcGxlMy5uei5zY2UpCnBsb3RQQ0Eoc2FtcGxlMy5uei5zY2UsIGNvbG91cl9ieT0iUnVuIiwgc2l6ZV9ieSA9ICJzdW0iKQpgYGAKCmBgYHtyfQpzYW1wbGUzLm56LnNjZSA8LSBydW5UU05FKHNhbXBsZTMubnouc2NlLCBkaW1yZWQ9IlBDQSIpCnBsb3RUU05FKHNhbXBsZTMubnouc2NlLCBjb2xvdXJfYnk9IlJ1biIsIHNpemVfYnkgPSAic3VtIikKYGBgCgpgYGB7cn0Kc2FtcGxlMy5uei5zY2UgPC0gcnVuVU1BUChzYW1wbGUzLm56LnNjZSwgZGltcmVkPSJQQ0EiKQpwbG90VU1BUChzYW1wbGUzLm56LnNjZSwgY29sb3VyX2J5PSJSdW4iLCBzaXplX2J5ID0gInN1bSIpCmBgYAoKYHIgI2tuaXRyOjprbml0X2V4aXQoKWAKCiMjIEJhdGNoIGNvcnJlY3Rpb24KCmBgYHtyfQpzYW1wbGUzLm56LnNjZSRSdW4gPC0gZmFjdG9yKHNhbXBsZTMubnouc2NlJFJ1bikKc2FtcGxlMy5uei5zY2UkYmF0Y2ggPC0gc2FtcGxlMy5uei5zY2UkUnVuCnNjZSA8LSBzYW1wbGUzLm56LnNjZQpgYGAKCiMjIyAgR2F1c3NpYW4gKG5vcm1hbCkgbGluZWFyIG1vZGVscwoKPCEtLSA3LjYuMi4xIEdhdXNzaWFuIChub3JtYWwpIGxpbmVhciBtb2RlbHMgLS0+CgpMaW1tYQoKYGBge3J9CnN1cHByZXNzTWVzc2FnZXMocmVxdWlyZShsaW1tYSkpCmxtX2Rlc2lnbl9iYXRjaCA8LSBtb2RlbC5tYXRyaXgofjAgKyBiYXRjaCwgZGF0YSA9IGNvbERhdGEoc2NlKSkKZml0X2xtX2JhdGNoIDwtIGxtRml0KGxvZ2NvdW50cyhzY2UpLCBsbV9kZXNpZ25fYmF0Y2gpCnJlc2lkc19sbV9iYXRjaCA8LSByZXNpZHVhbHMoZml0X2xtX2JhdGNoLCBsb2djb3VudHMoc2NlKSkKYXNzYXkoc2NlLCAibG1fYmF0Y2giKSA8LSByZXNpZHNfbG1fYmF0Y2gKCnJlZHVjZWREaW0oc2NlLCAiUENBX2xtX2JhdGNoIikgPC0gcmVkdWNlZERpbSgKICBydW5QQ0Eoc2NlLCBleHByc192YWx1ZXMgPSAibG1fYmF0Y2giKSwgIlBDQSIpCgpwbG90UmVkdWNlZERpbShzY2UsIGRpbXJlZCA9ICJQQ0FfbG1fYmF0Y2giLAogICAgICAgIGNvbG91cl9ieSA9ICJiYXRjaCIsIAogICAgICAgIHNpemVfYnkgPSAic3VtIiwKICAgICAgICBzaGFwZV9ieSA9ICJTYW1wbGUuTmFtZSIKICAgICAgICApICsKICBnZ3RpdGxlKCJMTSAtIHJlZ3Jlc3Mgb3V0IGJhdGNoIikKYGBgCgpgYGB7cn0Kc2NlUHJlU2N0IDwtIHNjZQpgYGAKCiMjIFNDVHJhbnNmb3JtCgojIyMgQmF0Y2ggb25seQoKRmlyc3QgbWFrZSBhIGNvcHkgb2YgdGhlIFNDRSBvYmplY3QgKHdlIHdpbGwgbmVlZCBvbmUgbGF0ZXIpLgoKYGBge3J9CiPCoGhhdmUgbG9nIGxpYiBzaXplCnNjZSRsb2cxMHN1bSA8LSBsb2cxMChzY2Ukc3VtKQpzY2VPcmlnIDwtIHNjZQpgYGAKCmBgYHtyfQpjb3VudHMgPC0gY291bnRzKHNjZSkKY29sbmFtZXMoY291bnRzKSA8LSBjb2xEYXRhKHNjZSkkQmFyY29kZQoKIyMjIEdlbmVzIGV4cHJlc3NlZCBpbiBhdCBsZWFzdCA1IGNlbGxzIHdpbGwgYmUga2VwdApzY3Rub3JtX2RhdGEgPC0gc2N0cmFuc2Zvcm06OnZzdCh1bWkgPSBjb3VudHMsIG1pbl9jZWxscyA9IDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlbGxfYXR0ciA9IGFzLmRhdGEuZnJhbWUoY29sRGF0YShzY2UpKVssYygibG9nMTBzdW0iLCAiYmF0Y2giKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhdGVudF92YXIgPSBjKCJiYXRjaCIpLAoJCQkJIHJldHVybl9nZW5lX2F0dHIgPSBUUlVFLAoJCQkJIHJldHVybl9jZWxsX2F0dHIgPSBUUlVFLAoJCQkJIHNob3dfcHJvZ3Jlc3MgPSBGQUxTRSkKYGBgCgpDaGVjayBtb2RlbCB1c2VkOgoKYGBge3J9CiMgbW9kZWw6CnByaW50KHNjdG5vcm1fZGF0YSRtb2RlbF9zdHIpCmBgYAoKQ2hlY2sgbmV3IHZhbHVlcyAoaGVyZSAzIHJvd3MgYW5kIDMgY29sdW1ucyBvbmx5KToKCmBgYHtyfQpzY3Rub3JtX2RhdGEkeVsxOjMsMTozXQpgYGAKCkNoZWNrIG9iamVjdDoKCmBgYHtyfQpzY2UKYGBgCgpTb21lIGdlbmVzIHdlcmUgbm90IGluY2x1ZGVkIGluIHRoZSB0cmFuc2Zvcm1hdGlvbiBhbmQgZXhjbHVkZWQgZnJvbSB0aGUgb3V0cHV0LCBzbyB3ZSB3aWxsIHJlbW92ZSB0aGVtIGZyb20gdGhlIFNDRSBvYmplY3QgdG9vLgoKYGBge3J9CiMgZXhjbHVkZSBnZW5lcyB0aGF0IHdlcmUgbm90IHVzZWQgaW4gdGhlIHRyYW5zZm9ybWF0aW9uOiAKdG1wSW5kIDwtIHdoaWNoKHJvd25hbWVzKHNjZSkgJWluJSByb3duYW1lcyhzY3Rub3JtX2RhdGEkeSkpCmNvbHMubWV0YSA8LSBjb2xEYXRhKHNjZU9yaWcpCnJvd3MubWV0YSA8LSByb3dEYXRhKHNjZU9yaWcpCgpuZXcuY291bnRzIDwtIGNvdW50cyhzY2VPcmlnKVt0bXBJbmQsIF0Kc2NlIDwtIFNpbmdsZUNlbGxFeHBlcmltZW50KGxpc3QoY291bnRzPW5ldy5jb3VudHMpKQoKIyByZXNldCB0aGUgY29sdW1uIGRhdGEgb24gdGhlIG5ldyBvYmplY3QKY29sRGF0YShzY2UpIDwtIGNvbHMubWV0YQpyb3dEYXRhKHNjZSkgPC0gcm93cy5tZXRhW3RtcEluZCwgXQpgYGAKCldlIG5vdyBjb3B5IHRoZSB0cmFuc2Zvcm1hdGlvbiBvdXRwdXQgdG8gdGhlIFNDRSBvYmplY3Q6CgpgYGB7cn0KdnN0TWF0IDwtIGFzKHNjdG5vcm1fZGF0YSR5W3Jvd25hbWVzKHNjZSksXSwgImRnQ01hdHJpeCIpCmFsbChjb2xuYW1lcyh2c3RNYXQpID09IHNjZSRCYXJjb2RlKQpkaW0odnN0TWF0KQoKY29sbmFtZXModnN0TWF0KSA8LSBOVUxMCmFzc2F5KHNjZSwgInNjdHJhbnNfbm9ybV9iYXRjaE9ubHkiKSA8LSB2c3RNYXQgIyBhcyh2c3Rfb3V0JHlbcm93bmFtZXMoc2NlKSxdLCAiZGdDTWF0cml4IikKYGBgCgpBbHNvIGNvcHkgJ2xvZ2NvdW50cyc6CgpgYGB7cn0KYXNzYXlYIDwtICJsb2djb3VudHMiCnRtcEFzc2F5IDwtIGFzc2F5KHNjZU9yaWcsIGFzc2F5WCkKYXNzYXkoc2NlLCBhc3NheVgpIDwtIHRtcEFzc2F5W3RtcEluZCwgXQpgYGAKCkRpYWdub3N0aWMgcGxvdHMgYXJlIHNob3duIGJlbG93OgoKYGBge3J9CnNjdHJhbnNmb3JtOjpwbG90X21vZGVsX3BhcnMoc2N0bm9ybV9kYXRhKQpgYGAKClRoZSByZWR1Y2VkIGRpbWVuc2lvbiBwbG90cyBiZWxvdyBzaG93IGltcHJvdmVkIG1peGluZyBvZiBjZWxscyBmcm9tIHRoZSB0d28gc2V0czoKCmBgYHtyfQpyZWR1Y2VkRGltKHNjZSwgIlBDQV9zY3RyYW5zX25vcm1fYmF0Y2hPbmx5IikgPC0gcmVkdWNlZERpbSgKICBydW5QQ0Eoc2NlLCBleHByc192YWx1ZXMgPSAic2N0cmFuc19ub3JtX2JhdGNoT25seSIpLCAiUENBIgopCnBsb3RSZWR1Y2VkRGltKAogIHNjZSwKICBkaW1yZWQgPSAiUENBX3NjdHJhbnNfbm9ybV9iYXRjaE9ubHkiLAogIGNvbG91cl9ieSA9ICJiYXRjaCIsCiAgc2l6ZV9ieSA9ICJzdW0iLAogIHNoYXBlX2J5ID0gIlNhbXBsZS5OYW1lIgopICsgZ2d0aXRsZSgiUENBIHBsb3Q6IHNjdHJhbnNmb3JtIG5vcm1hbGl6YXRpb24gLSBiYXRjaCBvbmx5IikgCmBgYAoKYGBge3J9CnNjZSA8LSBydW5UU05FKHNjZSwgZGltcmVkPSJQQ0Ffc2N0cmFuc19ub3JtX2JhdGNoT25seSIsIG5hbWU9IlRTTkVfc2N0cmFuc19ub3JtX2JhdGNoT25seSIpCnBsb3RSZWR1Y2VkRGltKAogIHNjZSwKICBkaW1yZWQgPSAiVFNORV9zY3RyYW5zX25vcm1fYmF0Y2hPbmx5IiwKICBjb2xvdXJfYnkgPSAiYmF0Y2giLAogIHNpemVfYnkgPSAic3VtIiwKICBzaGFwZV9ieSA9ICJTYW1wbGUuTmFtZSIKKSArIGdndGl0bGUoIlRTTkUgcGxvdDogc2N0cmFuc2Zvcm0gbm9ybWFsaXphdGlvbiAtIGJhdGNoIG9ubHkiKSAKYGBgCgpgYGB7cn0Kc2NlIDwtIHJ1blVNQVAoc2NlLCBkaW1yZWQ9IlBDQV9zY3RyYW5zX25vcm1fYmF0Y2hPbmx5IiwgbmFtZT0iVU1BUF9zY3RyYW5zX25vcm1fYmF0Y2hPbmx5IikKcGxvdFJlZHVjZWREaW0oCiAgc2NlLAogIGRpbXJlZCA9ICJVTUFQX3NjdHJhbnNfbm9ybV9iYXRjaE9ubHkiLAogIGNvbG91cl9ieSA9ICJiYXRjaCIsCiAgc2l6ZV9ieSA9ICJzdW0iLAogIHNoYXBlX2J5ID0gIlNhbXBsZS5OYW1lIgopICsgZ2d0aXRsZSgiVU1BUCBwbG90OiBzY3RyYW5zZm9ybSBub3JtYWxpemF0aW9uIC0gYmF0Y2ggb25seSIpIApgYGAKCktlZXAgY29weSBvZiBTQ0Ugb2JqZWN0IGZvciBsYXRlcjoKCmBgYHtyfQpzY2VfYmF0Y2hPbmx5IDwtIHNjZQpgYGAKCiMjIyBCb3RoIGxpYnJhcnkgc2l6ZSBhbmQgYmF0Y2gKClVzZSB0aGUgY29weSBvZiB0aGUgU0NFIG9iamVjdCBtYWRlIGVhcmxpZXIuCgpgYGB7cn0Kc2NlIDwtIHNjZU9yaWcKYGBgCgpTb21lIGNlbGxzIGFyZSB2ZXJ5IGRpZmZlcmVudCBmcm9tIHRoZSByZXN0LgoKYGBge3J9CiMjIyBHZW5lcyBleHByZXNzZWQgaW4gYXQgbGVhc3QgNSBjZWxscyB3aWxsIGJlIGtlcHQKY291bnRzIDwtIGNvdW50cyhzY2UpCmNsYXNzKGNvdW50cykKY29sbmFtZXMoY291bnRzKSA8LSBjb2xEYXRhKHNjZSkkQmFyY29kZQoKc2N0bm9ybV9kYXRhIDwtIHNjdHJhbnNmb3JtOjp2c3QodW1pID0gY291bnRzLCBtaW5fY2VsbHMgPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsX2F0dHIgPSBhcy5kYXRhLmZyYW1lKGNvbERhdGEoc2NlKSlbLGMoImxvZzEwc3VtIiwgImJhdGNoIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXRlbnRfdmFyID0gYygibG9nMTBzdW0iLCAiYmF0Y2giKSwKCQkJCSByZXR1cm5fZ2VuZV9hdHRyID0gVFJVRSwKCQkJCSByZXR1cm5fY2VsbF9hdHRyID0gVFJVRSwKCQkJCSBzaG93X3Byb2dyZXNzID0gRkFMU0UpCmBgYAoKQ2hlY2sgbW9kZWwgdXNlZDoKCmBgYHtyfQpwcmludChzY3Rub3JtX2RhdGEkbW9kZWxfc3RyKQpgYGAKCkRpc2NhcmQgZ2VuZXMgdGhhdCB3ZXJlIG5vdCB1c2VkIGluIHRoZSB0cmFuc2Zvcm1hdGlvbi4KCmBgYHtyfQojIGV4Y2x1ZGUgZ2VuZXMgdGhhdCB3ZXJlIG5vdCB1c2VkIGluIHRoZSB0cmFuc2Zvcm1hdGlvbjogCnRtcEluZCA8LSB3aGljaChyb3duYW1lcyhzY2UpICVpbiUgcm93bmFtZXMoc2N0bm9ybV9kYXRhJHkpKQpjb2xzLm1ldGEgPC0gY29sRGF0YShzY2VPcmlnKQpyb3dzLm1ldGEgPC0gcm93RGF0YShzY2VPcmlnKQoKbmV3LmNvdW50cyA8LSBjb3VudHMoc2NlT3JpZylbdG1wSW5kLCBdCnNjZSA8LSBTaW5nbGVDZWxsRXhwZXJpbWVudChsaXN0KGNvdW50cz1uZXcuY291bnRzKSkKCiMgcmVzZXQgdGhlIGNvbHVtbiBkYXRhIG9uIHRoZSBuZXcgb2JqZWN0CmNvbERhdGEoc2NlKSA8LSBjb2xzLm1ldGEKcm93RGF0YShzY2UpIDwtIHJvd3MubWV0YVt0bXBJbmQsIF0KYGBgCgpDb3B5IHRoZSB0cmFuc2Zvcm1hdGlvbiBvdXRwdXQgdG8gdGhlIFNDRSBvYmplY3QuCgpgYGB7cn0KdnN0TWF0IDwtIGFzKHNjdG5vcm1fZGF0YSR5W3Jvd25hbWVzKHNjZSksXSwgImRnQ01hdHJpeCIpCmFsbChjb2xuYW1lcyh2c3RNYXQpID09IHNjZSRCYXJjb2RlKQpjb2xuYW1lcyh2c3RNYXQpIDwtIE5VTEwKYXNzYXkoc2NlLCAic2N0cmFuc19ub3JtIikgPC0gdnN0TWF0CmBgYAoKU2hvdyBkaWFnbm9zdGljIHBsb3RzOgoKYGBge3J9CnNjdHJhbnNmb3JtOjpwbG90X21vZGVsX3BhcnMoc2N0bm9ybV9kYXRhKQpgYGAKClNob3cgcmVkdWNlZCBkaW1lbnNpb24gcGxvdHMgYW5kIGNoZWNrIGZvciBpbXByb3ZlZCBtaXhpbmcgb2YgY2VsbHMgZnJvbSB0aGUgdHdvIHNldHM6CgpgYGB7cn0KcmVkdWNlZERpbShzY2UsICJQQ0Ffc2N0cmFuc19ub3JtIikgPC0gcmVkdWNlZERpbSgKICBydW5QQ0Eoc2NlLCBleHByc192YWx1ZXMgPSAic2N0cmFuc19ub3JtIikKKQpwbG90UmVkdWNlZERpbSgKICBzY2UsCiAgZGltcmVkID0gIlBDQV9zY3RyYW5zX25vcm0iLAogIGNvbG91cl9ieSA9ICJiYXRjaCIsCiAgc2l6ZV9ieSA9ICJzdW0iLAogIHNoYXBlX2J5ID0gIlNhbXBsZS5OYW1lIgopICsgZ2d0aXRsZSgiUENBIHBsb3Q6IHNjdHJhbnNmb3JtIG5vcm1hbGl6YXRpb24iKSAKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KcmVkdWNlZERpbU5hbWVzKHNjZSkKYGBgCgpgYGB7cn0Kc2NlIDwtIHJ1blRTTkUoc2NlLCBkaW1yZWQ9IlBDQV9zY3RyYW5zX25vcm0iLCBuYW1lPSJUU05FX3NjdHJhbnNfbm9ybSIpCnBsb3RSZWR1Y2VkRGltKAogIHNjZSwKICBkaW1yZWQgPSAiVFNORV9zY3RyYW5zX25vcm0iLAogIGNvbG91cl9ieSA9ICJiYXRjaCIsCiAgc2l6ZV9ieSA9ICJzdW0iLAogIHNoYXBlX2J5ID0gIlNhbXBsZS5OYW1lIgopICsgZ2d0aXRsZSgiVFNORSBwbG90OiBzY3RyYW5zZm9ybSBub3JtYWxpemF0aW9uIikgCmBgYAoKYGBge3IsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CnJlZHVjZWREaW1OYW1lcyhzY2UpCmBgYAoKYGBge3J9CnNjZSA8LSBydW5VTUFQKHNjZSwgZGltcmVkPSJQQ0Ffc2N0cmFuc19ub3JtIiwgbmFtZT0iVU1BUF9zY3RyYW5zX25vcm0iKQpwbG90UmVkdWNlZERpbSgKICBzY2UsCiAgZGltcmVkID0gIlVNQVBfc2N0cmFuc19ub3JtIiwKICBjb2xvdXJfYnkgPSAiYmF0Y2giLAogIHNpemVfYnkgPSAic3VtIiwKICBzaGFwZV9ieSA9ICJTYW1wbGUuTmFtZSIKKSArIGdndGl0bGUoIlVNQVAgcGxvdDogc2N0cmFuc2Zvcm0gbm9ybWFsaXphdGlvbiIpIApgYGAKCmBgYHtyLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpyZWR1Y2VkRGltTmFtZXMoc2NlKQpgYGAKCkFkZCBQQ0Ffc2N0cmFuc19ub3JtX2JhdGNoT25seSAoc2FtZSBjZWxscywgb25seSBnZW5lcyBtYXkgZGlmZmVyKQoKYGBge3J9CnJlZHVjZWREaW0oc2NlLCAiUENBX3NjdHJhbnNfbm9ybV9iYXRjaE9ubHkiKSA8LSByZWR1Y2VkRGltKHNjZV9iYXRjaE9ubHksICJQQ0Ffc2N0cmFuc19ub3JtX2JhdGNoT25seSIpCnJlZHVjZWREaW0oc2NlLCAiVFNORV9zY3RyYW5zX25vcm1fYmF0Y2hPbmx5IikgPC0gcmVkdWNlZERpbShzY2VfYmF0Y2hPbmx5LCAiVFNORV9zY3RyYW5zX25vcm1fYmF0Y2hPbmx5IikKcmVkdWNlZERpbShzY2UsICJVTUFQX3NjdHJhbnNfbm9ybV9iYXRjaE9ubHkiKSA8LSByZWR1Y2VkRGltKHNjZV9iYXRjaE9ubHksICJVTUFQX3NjdHJhbnNfbm9ybV9iYXRjaE9ubHkiKQpgYGAKCmBgYHtyfQpzY2VQb3N0U2N0IDwtIHNjZQpgYGAKCiMjIG1ubkNvcnJlY3QKCjwhLS0gI2h0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL2JhdGNoZWxvci9pbnN0L2RvYy9jb3JyZWN0aW9uLmh0bWwgLS0+CgojIyPCoENoZWNrIHByZXNlbmNlIG9mIGJhdGNoIGVmZmVjdAoKU2FtZSBhcyBhYm92ZSBidXQgd2l0aCBiYXRjaGVsb3IgY29tbWFuZHMgdG8gbWFrZSB0aGUgdHdvIGJhdGNoZXMgYW5kIGlkZW50aWZ5IGhpZ2hseSB2YXJpYWJsZSBnZW5lcyBmb3IgZmFzdGVyIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbi4KCmBgYHtyfQpzY2UgPC0gc2FtcGxlMy5uei5zY2UKbGlicmFyeShiYXRjaGVsb3IpCiPCoE1pbmQgYXNzYXlOYW1lcygpCnNjZTEgPC0gc2NlWywgc2NlJFJ1biA9PSAiU1JSOTI2NDM1MSJdCnNjZTIgPC0gc2NlWywgc2NlJFJ1biA9PSAiU1JSOTI2NDM1MiJdCmBgYAoKYGBge3J9CmxpYnJhcnkoc2NyYW4pCmRlYzEgPC0gbW9kZWxHZW5lVmFyKHNjZTEpCmRlYzIgPC0gbW9kZWxHZW5lVmFyKHNjZTIpCmNvbWJpbmVkLmRlYyA8LSBjb21iaW5lVmFyKGRlYzEsIGRlYzIpCmNob3Nlbi5odmdzIDwtIGNvbWJpbmVkLmRlYyRiaW8gPiAwCnN1bW1hcnkoY2hvc2VuLmh2Z3MpCmBgYAoKQXMgYSBkaWFnbm9zdGljLCB3ZSBjaGVjayB0aGF0IHRoZXJlIGFjdHVhbGx5IGlzIGEgYmF0Y2ggZWZmZWN0IGFjcm9zcyB0aGVzZSBkYXRhc2V0cyBieSBjaGVja2luZyB0aGF0IHRoZXkgY2x1c3RlciBzZXBhcmF0ZWx5LiBIZXJlLCB3ZSBjb21iaW5lIHRoZSB0d28gU2luZ2xlQ2VsbEV4cGVyaW1lbnQgb2JqZWN0cyB3aXRob3V0IGFueSBjb3JyZWN0aW9uIHVzaW5nIHRoZSBOb0NvcnJlY3RQYXJhbSgpIGZsYWcsIGFuZCB3ZSBpbmZvcm1hbGx5IHZlcmlmeSB0aGF0IGNlbGxzIGZyb20gZGlmZmVyZW50IGJhdGNoZXMgYXJlIHNlcGFyYXRlZCB1c2luZyBhIHQtU05FIHBsb3QuCgpUaGVyZSBpcyBhIG1vZGVyYXRlIGJhdGNoIGVmZmVjdC4KCmBgYHtyfQpsaWJyYXJ5KHNjYXRlcikKY29tYmluZWQgPC0gY29ycmVjdEV4cGVyaW1lbnRzKEE9c2NlMSwgQj1zY2UyLCBQQVJBTT1Ob0NvcnJlY3RQYXJhbSgpKQpjb21iaW5lZCA8LSBydW5QQ0EoY29tYmluZWQsIHN1YnNldF9yb3c9Y2hvc2VuLmh2Z3MpCmNvbWJpbmVkIDwtIHJ1blRTTkUoY29tYmluZWQsIGRpbXJlZD0iUENBIikKY29tYmluZWQgPC0gcnVuVU1BUChjb21iaW5lZCwgZGltcmVkPSJQQ0EiKQpwbG90UENBKGNvbWJpbmVkLCBjb2xvdXJfYnk9ImJhdGNoIikKcGxvdFRTTkUoY29tYmluZWQsIGNvbG91cl9ieT0iYmF0Y2giKQpwbG90VU1BUChjb21iaW5lZCwgY29sb3VyX2J5PSJiYXRjaCIpCmBgYAoKYGBge3J9CnJlZHVjZWREaW0oc2NlLCAiUENBX25vQ29yIikgPC0gcmVkdWNlZERpbShjb21iaW5lZCwgIlBDQSIpCnJlZHVjZWREaW0oc2NlLCAiVFNORV9ub0NvciIpIDwtIHJlZHVjZWREaW0oY29tYmluZWQsICJUU05FIikKcmVkdWNlZERpbShzY2UsICJVTUFQX25vQ29yIikgPC0gcmVkdWNlZERpbShjb21iaW5lZCwgIlVNQVAiKQpgYGAKCiMjIyBDb3JyZWN0IGJhdGNoIGVmZmVjdCB3aXRoIG1ubkNvcnJlY3QKClRoaXMgaXMgdGhlIGluaXRpYWwgbWV0aG9kLiBJdCB1c2VzIGdlbmUgZXhwcmVzc2lvbiB2YWx1ZXMgdG8gaWRlbnRpZnkgY2VsbHMgd2l0aCBzaW1pbGFyIGV4cHJlc3Npb24gcGF0dGVybnMgaW4gYm90aCBiYXRjaGVzLgoKTGV0IHVzIGdldCB0aGUgbm9ybWFsaXNlZCBjb3VudHM6CgpgYGB7cn0KYmF0Y2gxIDwtIGxvZ2NvdW50cyhzY2UxKQpiYXRjaDIgPC0gbG9nY291bnRzKHNjZTIpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiMgdXNpbmcgYSBzdWJzZXQgb2YgZ2VuZXMgdG8gY29tcHV0ZSBjb3JyZWN0aW9uIGFuZCBjb3JyZWN0aW5nIGFsbCBnZW5lcwojIHJldHVybnMgYSBtYXRyaXggd2l0aCByb3duYW1lcyBvbmx5IGZvciB0aGUgZ2VuZSBzdWJzZXQsCiMgYXQgdGhlIHRvcCBvZiB0aGUgbWF0cml4CiMgcHJldmVudGluZyBjb3B5IG9mIHRoYXQgY29ycmVjdGVkIG1hdHJpeCBhcyBhbiBhc3NheSBpbiB0aGUgU0NFIG9iamVjdAoKZmV3ZXIuaHZncyA8LSBoZWFkKG9yZGVyKGNvbWJpbmVkLmRlYyRiaW8sIGRlY3JlYXNpbmc9VFJVRSksIDUwMCkKCiMgbW1uQ29ycmVjdCByZXR1cm5zIHRoZSBjb3JyZWN0ZWQgZ2VuZSBleHByZXNzaW9uIG1hdHJpeCBkaXJlY3RseQoKeCA8LSBiYXRjaGVsb3I6Om1ubkNvcnJlY3QoCiAgICAgICAgICBiYXRjaDEsIGJhdGNoMiwgIAoJICBzdWJzZXQucm93ID0gZmV3ZXIuaHZncywKCSAgY29ycmVjdC5hbGwgPSBUUlVFLAogICAgICAgICAgayA9IDIwLAogICAgICAgICAgc2lnbWEgPSAwLjEsCiAgICAgICAgICBjb3Mubm9ybS5pbiA9IFRSVUUsCiAgICAgICAgICBzdmQuZGltID0gMgogICAgICAgICkKZGltKGFzc2F5KHgsICJjb3JyZWN0ZWQiKSkKaGVhZChjb2xuYW1lcyhhc3NheSh4LCAiY29ycmVjdGVkIikpKQpoZWFkKHJvd25hbWVzKGFzc2F5KHgsICJjb3JyZWN0ZWQiKSkpCmFsbChyb3duYW1lcyhzY2UpID09IHJvd25hbWVzKHgpKQpgYGAKCmBgYHtyfQp5IDwtIGJhdGNoZWxvcjo6bW5uQ29ycmVjdCgKICAgICAgICAgIGJhdGNoMSwgYmF0Y2gyLCAgCgkgICNzdWJzZXQucm93ID0gZmV3ZXIuaHZncywKCSAgY29ycmVjdC5hbGwgPSBUUlVFLAogICAgICAgICAgayA9IDIwLAogICAgICAgICAgc2lnbWEgPSAwLjEsCiAgICAgICAgICBjb3Mubm9ybS5pbiA9IFRSVUUsCiAgICAgICAgICBzdmQuZGltID0gMgogICAgICAgICkKYGBgCgpDb3B5IHRoZSBjb3JyZWN0ZWQgdmFsdWVzIHRvIHRoZSBTQ0Ugb2JqZWN0OgoKYGBge3J9CmFzc2F5KHNjZSwgIm1ubiIpIDwtIGFzc2F5KHksICJjb3JyZWN0ZWQiKQpgYGAKClNob3cgcmVkdWNlZCBkaW1lbnNpb24gcGxvdHMgYW5kIGNoZWNrIGZvciBpbXByb3ZlZCBtaXhpbmcgb2YgY2VsbHMgZnJvbSB0aGUgdHdvIHNldHM6CgpgYGB7cn0Kc2NlIDwtIHJ1blBDQShzY2UsIGV4cHJzX3ZhbHVlcyA9ICJtbm4iKQpwbG90UENBKHNjZSwgY29sb3VyX2J5PSJiYXRjaCIpCnJlZHVjZWREaW0oc2NlLCAiUENBX21ubiIpIDwtIHJlZHVjZWREaW0oc2NlLCAiUENBIikKYGBgCgpgYGB7cn0Kc2NlIDwtIHJ1blRTTkUoc2NlLCBkaW1yZWQ9IlBDQV9tbm4iKQpwbG90VFNORShzY2UsIGNvbG91cl9ieT0iYmF0Y2giKQpyZWR1Y2VkRGltKHNjZSwgIlRTTkVfbW5uIikgPC0gcmVkdWNlZERpbShzY2UsICJUU05FIikKYGBgCgpgYGB7cn0Kc2NlIDwtIHJ1blVNQVAoc2NlLCBkaW1yZWQ9IlBDQV9tbm4iKQpwbG90VU1BUChzY2UsIGNvbG91cl9ieT0iYmF0Y2giKQpyZWR1Y2VkRGltKHNjZSwgIlVNQVBfbW5uIikgPC0gcmVkdWNlZERpbShzY2UsICJVTUFQIikKYGBgCgojIyBmYXN0TU5OCgpUaGlzIG1ldGhvZCBpcyBmYXN0ZXIgdGhhbiBtbm5Db3JyZWN0IGFzIGl0IGlkZW50aWZpZXMgbmVhcmVzdCBuZWlnaGJvdXJzIGFmdGVyIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbi4gCgpgYGB7cn0KZnggPC0gYmF0Y2hlbG9yOjpmYXN0TU5OKAogICAgICAgICAgICAgICAgICAgICAgc2NlLAoJCSAgICAgICNjb3JyZWN0LmFsbCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBiYXRjaCA9IHNjZSRSdW4KCQkJKQpjbGFzcyhmeCkKYGBgCgpDb3B5IHRoZSBjb3JyZWN0ZWQgdmFsdWVzIHRvIHRoZSBTQ0Ugb2JqZWN0OgoKYGBge3J9CiMgZmFzdE1OTiBtYXkgZHJvcCBzb21lIGdlbmVzCiMgc28gd2UgbWF5IG5vdCBiZSBhYmxlIHRvIGtlZXAgdGhlIG91dGNvbWUgaW4gJ2Fzc2F5Jwphc3NheShzY2UsICJmYXN0bW5uIikgPC0gYXNzYXkoZngsICJyZWNvbnN0cnVjdGVkIikKYGBgCgpTaG93IHJlZHVjZWQgZGltZW5zaW9uIHBsb3RzIGFuZCBjaGVjayBmb3IgaW1wcm92ZWQgbWl4aW5nIG9mIGNlbGxzIGZyb20gdGhlIHR3byBzZXRzOgoKYGBge3J9CmZhc3Rtbm5fcGNhIDwtIHJ1blBDQShhc3NheShzY2UsICJmYXN0bW5uIiksIHJhbms9MikgI8Kgc2xvdwpyZWR1Y2VkRGltKHNjZSwgIlBDQV9mYXN0bW5uIikgPC0gZmFzdG1ubl9wY2Ekcm90YXRpb24KYGBgCgpgYGB7cn0KcGxvdFJlZHVjZWREaW0oCiAgc2NlLAogIGRpbXJlZCA9ICJQQ0FfZmFzdG1ubiIsCiAgY29sb3VyX2J5ID0gImJhdGNoIiwKICBzaXplX2J5ID0gInN1bSIsCiAgc2hhcGVfYnkgPSAiU2FtcGxlLk5hbWUiCikgKyBnZ3RpdGxlKCJQQ0EgcGxvdDogZmFzdE1OTiIpIApgYGAKCmBgYHtyfQpzY2UgPC0gcnVuVFNORShzY2UsIGRpbXJlZD0iUENBX2Zhc3Rtbm4iKQpwbG90VFNORShzY2UsIGNvbG91cl9ieT0iYmF0Y2giKQpyZWR1Y2VkRGltKHNjZSwgIlRTTkVfZmFzdG1ubiIpIDwtIHJlZHVjZWREaW0oc2NlLCAiVFNORSIpCmBgYAoKYGBge3J9CnNjZSA8LSBydW5VTUFQKHNjZSwgZGltcmVkPSJQQ0FfZmFzdG1ubiIpCnBsb3RVTUFQKHNjZSwgY29sb3VyX2J5PSJiYXRjaCIpCnJlZHVjZWREaW0oc2NlLCAiVU1BUF9mYXN0bW5uIikgPC0gcmVkdWNlZERpbShzY2UsICJVTUFQIikKYGBgCgojIyBIYXJtb255CgpIYXJtb255IFtLb3JzdW5za3kyMDE4ZmFzdF0gaXMgYSBuZXdlciBiYXRjaCBjb3JyZWN0aW9uIG1ldGhvZCwgd2hpY2ggaXMgZGVzaWduZWQgdG8gb3BlcmF0ZSBvbiBQQyBzcGFjZS4gVGhlIGFsZ29yaXRobSBwcm9jZWVkcyB0byBpdGVyYXRpdmVseSBjbHVzdGVyIHRoZSBjZWxscywgd2l0aCB0aGUgb2JqZWN0aXZlIGZ1bmN0aW9uIGZvcm11bGF0ZWQgdG8gcHJvbW90ZSBjZWxscyBmcm9tIG11bHRpcGxlIGRhdGFzZXRzIHdpdGhpbiBlYWNoIGNsdXN0ZXIuIE9uY2UgYSBjbHVzdGVyaW5nIGlzIG9idGFpbmVkLCB0aGUgcG9zaXRpb25zIG9mIHRoZSBjZW50cm9pZHMgb2YgZWFjaCBkYXRhc2V0IGFyZSBvYnRhaW5lZCBvbiBhIHBlci1jbHVzdGVyIGJhc2lzIGFuZCB0aGUgY29vcmRpbmF0ZXMgYXJlIGNvcnJlY3RlZC4gVGhpcyBwcm9jZWR1cmUgaXMgaXRlcmF0ZWQgdW50aWwgY29udmVyZ2VuY2UuIEhhcm1vbnkgY29tZXMgd2l0aCBhIHRoZXRhIHBhcmFtZXRlciB0aGF0IGNvbnRyb2xzIHRoZSBkZWdyZWUgb2YgYmF0Y2ggY29ycmVjdGlvbiAoaGlnaGVyIHZhbHVlcyBsZWFkIHRvIG1vcmUgZGF0YXNldCBpbnRlZ3JhdGlvbiksIGFuZCBjYW4gYWNjb3VudCBmb3IgbXVsdGlwbGUgZXhwZXJpbWVudGFsIGFuZCBiaW9sb2dpY2FsIGZhY3RvcnMgb24gaW5wdXQgKHNlZSBbdmFyaWFudCBvZiB0aGUgJ0hlbWJlcmcgY291cnNlJ10oaHR0cHM6Ly9iaW9jZWxsZ2VuLXB1YmxpYy5zdmkuZWR1LmF1L21pZ18yMDE5X3Njcm5hc2VxLXdvcmtzaG9wL3B1YmxpYy9ub3JtYWxpemF0aW9uLWNvbmZvdW5kZXJzLWFuZC1iYXRjaC1jb3JyZWN0aW9uLmh0bWwjaGFybW9ueSkpLgoKYGBge3J9CmxpYnJhcnkoaGFybW9ueSkKCnJlZHVjZWREaW0oc2NlLCAiUENBX2xvZ2NvdW50cyIpIDwtIHJlZHVjZWREaW0oCiAgcnVuUENBKHNjZSwgZXhwcnNfdmFsdWVzID0gImxvZ2NvdW50cyIpCikKCiNTZWVpbmcgaG93IHRoZSBlbmQgcmVzdWx0IG9mIEhhcm1vbnkgaXMgYW4gYWx0ZXJlZCBkaW1lbnNpb25hbCByZWR1Y3Rpb24gc3BhY2UgY3JlYXRlZCBvbiB0aGUgYmFzaXMgb2YgUENBLCB3ZSBwbG90IHRoZSBvYnRhaW5lZCBtYW5pZm9sZCBoZXJlIGFuZCBleGNsdWRlIGl0IGZyb20gdGhlIHJlc3Qgb2YgdGhlIGZvbGxvdy11cHMgaW4gdGhlIHNlY3Rpb24uCgpwY2EgPC0gYXMubWF0cml4KHJlZHVjZWREaW0oc2NlLCAiUENBX2xvZ2NvdW50cyIpKQpoYXJtb255X2VtYiA8LSBIYXJtb255TWF0cml4KHBjYSwgc2NlJGJhdGNoLCB0aGV0YT0yLCBkb19wY2E9RkFMU0UpCnJlZHVjZWREaW0oc2NlLCAiaGFybW9ueSIpIDwtIGhhcm1vbnlfZW1iCgpwbG90UmVkdWNlZERpbSgKICAgIHNjZSwKICAgIGRpbXJlZCA9ICdoYXJtb255JywKICAgIGNvbG91cl9ieSA9ICJiYXRjaCIsCiAgICBzaXplX2J5ID0gInN1bSIsCiAgICBzaGFwZV9ieSA9ICJTYW1wbGUuTmFtZSIKKQpgYGAK