1 Introduction

We here make use of the publication of Anna Cuomo et al. (last author Oliver Stegle), which we will refer to as the iPSC dataset. The paper that describes this dataset can be found using this link.

In the experiment, the authors harvested induced pluripotent stem cells (iPSCs) from 125 healthy human donors. These cells were used to study the endoderm differentiation process. In this process, iPSCs differentiate to endoderm cells, a process which takes approximately three days. As such, the authors cultered the iPSCs cell lines and allowed for differentiation for three days. During the experiment, cells were harvested at four different time points: day0 (directly at to incubation), day1, day2 and day3. Knowing the process of endoderm differentiation, these time points should correspond with different cell types: day0 are (undifferentiated) iPSCs, day1 are mesendoderm cells, day2 are “intermediate” cells and day3 are fully differentiated endoderm cells.

This dataset was generated using the SMART-Seq2 scRNA-seq protocol.

The final goal of the experiment was to characterize population variation in the process of endoderm differentiation.

2 Download data

For this lab session, we will work with a subset of the data, i.e., the data for the first (alphabetically) 15 patients in the experiment. These can be downloaded through the belnet filesender link provided through email, https://filesender.belnet.be/?s=download&token=eb8136df-67d3-4869-b2a9-f65767054e81.

The data original (125 patient) could be downloaded from Zenodo. At the bottom of this web-page, we can download the files raw_counts.csv.zip and cell_metadata_cols.tsv and store these files locally. We do not recommend doing this during the lab session, to avoid overloading the system.

3 Import data

First we read in the count matrix:

library(SingleCellExperiment)
sce <- readRDS("/Users/jg/Desktop/sce_15_cuomo.rds") # change to YOUR path!
sce

4 Explore metadata

Exploration of the metadata is essential to get a better idea of what the experiment was about and how it was organized.

colData(sce)[1:5,1:10]
colnames(colData(sce))

As stated in the paper, cells were sampled on 4 time points. Each of these time points is expected to correspond with different cell types (day0 = iPSC, day1 = mesendoderm, day2 = intermediate and day3 = endoderm).

table(colData(sce)$day)

As stated in the paper, cells were harvested from 125 patients. Here, we are working on a subset with 15 patients. The number of cells harvested per patient (over all time points) ranges from 31 to 637.

length(table(colData(sce)$donor)) # number of donors
range(table(colData(sce)$donor)) # cells per donor

Below, we look how many cells are harvest per patent and per time point.

table(colData(sce)$donor,colData(sce)$day)

We see that for many patients the data is complete, i.e. cells were sampled on all time points.

Practically, the cells were prepared in 28 batches. Since we here only look at a subset of the data, we see that only 14 of these batches are represented here.

length(table(colData(sce)$experiment))
table(colData(sce)$experiment, colData(sce)$day)

5 Obtaining and including rowData

The rowData slot of a SingleCellExperiment object allows for storing information on the features, i.e. the genes, in a dataset. In our object, the rowData slot currently contains the following:

head(rowData(sce))

To improve our gene-level information, we may:

  1. Split V1 into two columns, one with the ENSEMBL ID and the other with the gene symbol.

  2. Display which chromosome the gene is located

Many more options are possible, but are not necessary for us right now.

rowData(sce) <- data.frame(Ensembl = gsub("_.*", "", rowData(sce)$V1),
                           Symbol = gsub("^[^_]*_", "", rowData(sce)$V1))
head(rowData(sce))
library("biomaRt")
ensembl75 <- useEnsembl(biomart = 'genes', 
                        dataset = 'hsapiens_gene_ensembl',
                        version = 75)

GeneInfo <- getBM(attributes = c("ensembl_gene_id", # To match with rownames SCE
                                 "chromosome_name"), # Info on chromose
                  mart = ensembl75)
GeneInfo <- GeneInfo[match(rowData(sce)$Ensembl, GeneInfo$ensembl_gene_id),]

rowData(sce) <- cbind(rowData(sce), GeneInfo)
head(rowData(sce))
all(rowData(sce)$Ensembl == rowData(sce)$ensembl_gene_id) 
# identical, as desired, so we could optionally remove one of the two

6 Filtering non-informative genes

Let us first try the very simple and very lenient filtering criterion that we adopted for the Macosko dataset.

keep <- rowSums(assays(sce)$counts > 0) > 10
table(keep)

sce <- sce[keep,]

We see that this filtering strategy does not remove any genes for this dataset. In general, datasets from plate-based scRNA-seq dataset have a far higher sequencing depth than data from droplet-based protocols. As requiring a minimum expression of 1 count in at least 10 cells is a very lenient criterion if we consider that we have 36.000 cells, we should consider adopting a more stringent filtering criterium, like the filterByExpr from edgeR:

library(edgeR)

table(colData(sce)$day)

keep2 <- edgeR::filterByExpr(y=sce,
                             group = colData(sce)$day,
                             min.count = 5,
                             min.prop = 0.4)
table(keep2)

sce <- sce[keep2,]
xas <- rowSums(assay(sce) > 0)
yas <- rowSums(assay(sce))


plot(x = xas,
     y = yas,
     log = "y",
     pch = 19,
     cex=0.3,
     col = as.factor(keep2))

7 Quality control

7.1 Calculate QC variables

library(scater)

# check ERCC spike-in transcripts
sum(grepl("^ERCC-", rowData(sce)$Symbol)) # no spike-in transcripts available

is.mito <- grepl("^MT", rowData(sce)$chromosome_name)
sum(is.mito) # 13 mitochondrial genes

df <- perCellQCMetrics(sce, subsets=list(Mito=is.mito))
head(df)

## add the QC variables to sce object
colData(sce) <- cbind(colData(sce), df)

7.2 Exploratory data analysis

In the figure below, we see that several cells have a very low number of expressed genes, and where most of the molecules are derived from mitochondrial genes. This indicates likely damaged cells, presumably because of loss of cytoplasmic RNA from perforated cells, so we should remove these for the downstream analysis.

# Number of genes vs library size
plotColData(sce, x = "sum", y="detected", colour_by="day") 

# Mitochondrial genes
plotColData(sce, x = "detected", y="subsets_Mito_percent", colour_by="day")

7.3 QC using adaptive thresholds

Below, we remove cells that are outlying with respect to

  1. A low sequencing depth (number of UMIs);
  2. A low number of genes detected;
  3. A high percentage of reads from mitochondrial genes.

We remove a total of \(301\) cells, mainly due to low sequencing depth and low number of genes detected.

lowLib <- isOutlier(df$sum, type="lower", log=TRUE)
lowFeatures <- isOutlier(df$detected, type="lower", log=TRUE)
highMito <- isOutlier(df$subsets_Mito_percent, type="higher")

table(lowLib)
table(lowFeatures)
table(highMito)

discardCells <- (lowLib | lowFeatures | highMito)
table(discardCells)
colData(sce)$discardCells <- discardCells

# visualize cells to be removed
plotColData(sce, x = "detected", y="subsets_Mito_percent", colour_by = "discardCells")
plotColData(sce, x = "sum", y="detected", colour_by="discardCells")
# visualize cells to be removed
plotColData(sce, x = "detected", y="subsets_Mito_percent", colour_by = "donor")
plotColData(sce, x = "sum", y="detected", colour_by="donor")
# visualize cells to be removed
plotColData(sce, x = "detected", y="subsets_Mito_percent", colour_by = "experiment")
plotColData(sce, x = "sum", y="detected", colour_by="experiment")
table(sce$donor, sce$discardCells)
table(sce$donor, sce$discardCells)/rowSums(table(sce$donor, sce$discardCells))
#fractions of removed cells per donor

Most removed cells (fraction) are from patients dixh and babz.

table(sce$experiment, sce$discardCells)
table(sce$experiment, sce$donor)

Most removed cells (fraction) are from patients dixh and babz. Most low library sizes seem to come from patient dixh; for patient babz the effect is less pronounced.

plotColData(sce[,sce$donor=="dixh"], x = "sum", y="detected")
plotColData(sce[,sce$donor=="babz"], x = "sum", y="detected")

As such, we are mainly removing cells from specific patients and the respective batches in which they were sequenced. However, we want to be careful; we only want to remove technical artefacts, while retaining as much of the biology as possible. In our exploratory figure, we see that the cells we are removing based on the number of genes detected, are quite far apart from the bulk of the data cloud; as such, these cells are indeed suspicious. For the criterion of library size, we see that the cells removed there are still strongly connected to the data cloud. As such, we may want to relax the filtering criterion there a little bit. When we think of how the adaptive threshold strategy works, we may want to remove cells that are 4MADs away from the center, rather than the default 3 MADs.

# previously
lowLib <- isOutlier(df$sum, type="lower", log=TRUE)
table(lowLib)

# after seeing appropriate exploratory figure
lowLib <- isOutlier(df$sum, nmads=4, type="lower", log=TRUE)
table(lowLib)

discardCells <- (lowLib | lowFeatures | highMito)
table(discardCells)
colData(sce)$discardCells <- discardCells

Note that these steps are not exact; different analysts will come with different filtering criteria for many of the steps. The key ideas are that we let appropriate exploratory figures guide us to make reasonable choices; i.e., we look at the data rather than blindly following a standardized pipeline that may work well in many cases, but maybe not our particular dataset.

# remove cells identified using adaptive thresholds
sce <- sce[, !colData(sce)$discardCells]

7.4 Identifying and removing empty droplets

This does not make much sense for plate-based data! While we could imagine the presence of empty plate wells for the SMART-Seq2 experiment, these are typically detected and removed upstream of our analysis using laser technology.

7.5 Identifying and removing doublets

Again, this step typically does not make much sense for plate-based data. While we could imagine the presence of plate wells in which more than two cells are present for the SMART-Seq2 experiment, these are again typically detected and removed upstream of our analysis using laser technology.

If we would use scDblFinder to detect doublet cells on this dataset, we would see that only a very small fraction of the cells would be removed. These removed cells could correspond to actual doublets that were missed by the upstream doublet detection technology, or more likely could correspond to false positive doublets of the scDblFinder doublet detection procedure.

8 Normalization

For normalization, the size factors \(s_i\) computed here are simply scaled library sizes:

\[ N_i = \sum_g Y_{gi} \] \[ s_i = N_i / \bar{N}_i \]

sce <- logNormCounts(sce)

# note we also returned log counts: see the additional logcounts assay.
sce

# you can extract size factors using
sf <- librarySizeFactors(sce)
mean(sf) # equal to 1 due to scaling.
plot(x= log(colSums(assays(sce)$counts)), 
     y=sf)

— end lab session 1 —


Visualize the effect of normalization using MD-plots.

In order to only work with cells that could be considered technical replicates, we should select cells coming from the same biological group. We can do this approximately by selecting cells from the same sampling day, the same donor and the same sequencing experiment.

table(sce$donor, sce$day)
table(sce$experiment, sce$day)
table(sce$experiment, sce$donor)

Below, we will visualize the normalization occurring between two cells of the same cell type - donor/experiment combination (which could be considered technical repeats):

cs <- colSums(assays(sce)$counts[,which(sce$day == "day2" &
                                        sce$donor == "eipl")])
cs[order(cs, decreasing = TRUE)][c(1,10,100)]

Let’s take a look at how comparable two cells (replicates) of this biological group are. We will compare the cell with the highest library size with the cell that has the 10th and 100th highest library size using MD-plots (mean-difference plots, as introduced by Dudoit et al. (2002)), also sometimes referred to as MA-plots.

targetCells <- names(cs[order(cs, decreasing = TRUE)][c(1,10)])

M <- rowMeans(assays(sce)$counts[,targetCells])
D <- assays(sce)$counts[,targetCells[2]] / assays(sce)$counts[,targetCells[1]]
plot(x = log(M), y = log2(D),
     pch = 16, cex=1/3,
     main = paste0("Cell ", targetCells[2], " vs cell ", targetCells[1]),
     xlab = "Log mean", ylab = "Log2 fold-change",
     bty = 'l')
abline(h = 0, col="orange", lwd=2)

targetCells <- names(cs[order(cs, decreasing = TRUE)][c(1,100)])

M <- rowMeans(assays(sce)$counts[,targetCells])
D <- assays(sce)$counts[,targetCells[2]] / assays(sce)$counts[,targetCells[1]]
plot(x = log(M), y = log2(D),
     pch = 16, cex=1/3,
     main = paste0("Cell ", targetCells[2], " vs cell ", targetCells[1]),
     xlab = "Log mean", ylab = "Log2 fold-change",
     bty = 'l')
abline(h = 0, col="orange", lwd=2)

We see clear bias in the comparison of the 1st and 10th/100th most deeply sequenced cell from this day/patient combination. We see that the log f old-changes are biased downwards. This means that, on average, a gene is lower expressed in cell 1 versus cell 10 (and cell 100). Looking at the library sizes, we can indeed see that the library size for cell 1 is 1.200.691 counts, while it is only 701.550 counts for cell 10 and 468.762 counts for cell 100! This is a clear library size effect that we should take into account.

# normalize the count data using the previously computed "size factors"
assay(sce, "normed") <- normalizeCounts(sce, 
                                        log=FALSE,
                                        size.factors=sf, 
                                        pseudo.count=0)

targetCells <- names(cs[order(cs, decreasing = TRUE)][c(1,10)])

M <- rowMeans(assays(sce)$normed[,targetCells])
D <- assays(sce)$normed[,targetCells[2]] / assays(sce)$normed[,targetCells[1]]
plot(x = log(M), y = log2(D),
     pch = 16, cex=1/3,
     main = paste0("Cell ", targetCells[2], " vs cell ", targetCells[1]),
     xlab = "Log mean", ylab = "Log2 fold-change",
     bty = 'l')
abline(h = 0, col="orange", lwd=2)

targetCells <- names(cs[order(cs, decreasing = TRUE)][c(1,100)])

M <- rowMeans(assays(sce)$normed[,targetCells])
D <- assays(sce)$normed[,targetCells[2]] / assays(sce)$normed[,targetCells[1]]
plot(x = log(M), y = log2(D),
     pch = 16, cex=1/3,
     main = paste0("Cell ", targetCells[2], " vs cell ", targetCells[1]),
     xlab = "Log mean", ylab = "Log2 fold-change",
     bty = 'l')
abline(h = 0, col="orange", lwd=2)

9 Feature selection

9.1 Highly variable genes

library(scran)
rownames(sce) <- rowData(sce)$Ensembl
dec <- modelGeneVar(sce)
head(dec)
fit <- metadata(dec)
plot(fit$mean, fit$var, 
     xlab="Mean of log-expression",
    ylab="Variance of log-expression")
curve(fit$trend(x), col="dodgerblue", add=TRUE, lwd=2)
# get 10% highly variable genes
hvg <- getTopHVGs(dec, 
                  prop=0.1)
head(hvg)

# plot these 
plot(fit$mean, fit$var, 
     col = c("orange", "darkseagreen3")[(names(fit$mean) %in% hvg)+1],
     xlab="Mean of log-expression",
    ylab="Variance of log-expression")
curve(fit$trend(x), col="dodgerblue", add=TRUE, lwd=2)
legend("topleft", 
       legend = c("Selected", "Not selected"), 
       col = c("darkseagreen3", "orange"),
       pch = 16,
       bty='n')

9.2 High deviance genes

#BiocManager::install("scry")
library(scry)
sce <- devianceFeatureSelection(object = sce, 
                                assay = "counts", 
                                sorted = FALSE)

plot(sort(rowData(sce)$binomial_deviance, decreasing = TRUE), 
     type="l", 
     xlab="ranked genes", 
     ylab="binomial deviance", 
     main="Feature Selection with Deviance")
abline(v=2000, lty=2, col="red")

9.3 Seurat VST

library(Seurat)
rownames(sce) <- rowData(sce)$Ensembl
seurat_obj <- as.Seurat(sce)
seurat_obj # notice the "0 variable features"
seurat_obj <- Seurat::NormalizeData(seurat_obj, 
                                    normalization.method = "LogNormalize", 
                                    scale.factor = 10000)

seurat_obj <-  FindVariableFeatures(object = seurat_obj,
                                    selection.method = "vst")
seurat_obj  # notice the "2000 variable features" (default)
head(VariableFeatures(seurat_obj)) # here they are
rm(seurat_obj)

10 Dimensionality reduction

10.1 The most basic DR

colData(sce)$day <- as.factor(colData(sce)$day)
day <- colData(sce)$day
par(bty='l')
plot(x = assays(sce)$counts[hvg[1],],
     y = assays(sce)$counts[hvg[2],],
     col = as.numeric(day),
     pch = 16, cex = 1/3,
     xlab = "Most informative gene",
     ylab = "Second most informative gene",
     main = "Cells colored acc to cell type")

Just by looking at the top two genes based on our feature selection criterion, we can already see some separation according to the cell type; the cells colored red seem to have low expression values for both genes, and are those solely retireved in the bottom left of the x-y plane.

10.2 Linear dimensionality reduction: PCA

10.2.1 PCA with feature selection

set.seed(1234)
sce <- runPCA(sce, 
              ncomponents=30, 
              subset_row=hvg)

PCA has been performed. The PCA information has been automatically stored in the reducedDim slot of the SingleCellExperiment object.

reducedDimNames(sce)
head(reducedDim(sce,
           type="PCA"))

The plotPCA function of the scater package now allows us to visualize the cells in PCA space, based on the PCA information stored in our object:

plotPCA(sce, 
        colour_by = "day")

We observe a clear-cut clustering of the cells!

percent.var <- attr(reducedDim(sce), "percentVar")
plot(percent.var, log="y", xlab="PC", ylab="Variance explained (%)")

Here, retaining ±15PCs seems reasonable. If you really prefer a more data-driven way for determining this,

library(PCAtools)
chosen.elbow <- findElbowPoint(percent.var)
chosen.elbow

10.2.2 PCA without feature selection

set.seed(1234)
sceNoFS <- runPCA(sce, 
                  ncomponents=30, 
                  subset_row=1:nrow(sce))
plotPCA(sceNoFS, colour_by = "day")
rm(sceNoFS)

While we use more information to make this PCA plot (10.374 genes) as compared to the feature selected PCA plot (506 genes), we seem to retrieve less structure in the data. Indeed, we are here not able to distinguish between day2 and day3 cells. This is the power of feature selection, an increase in the signal-to-noise ratio!

10.3 A generalization of PCA for exponential family distributions.

library(glmpca)
set.seed(211103)
poipca <- glmpca(Y = assays(sce)$counts[hvg,],
                 L = 2, 
                 fam = "poi",
                 minibatch = "stochastic")
reducedDim(sce, "PoiPCA") <- poipca$factors
plotReducedDim(sce, 
               dimred="PoiPCA",
               colour_by = "day")

10.4 Non-linear dimensionality reduction: T-SNE

set.seed(564654)
sce <- runTSNE(sce, 
               dimred = 'PCA',
               external_neighbors=TRUE)
plotTSNE(sce,
         colour_by = "day")

plotTSNE(sce,
         colour_by = "donor")

plotTSNE(sce,
         colour_by = "experiment")

10.5 Non-linear dimensionality reduction: UMAP

set.seed(65187)
sce <- runUMAP(sce, 
               dimred = 'PCA', 
               external_neighbors = TRUE)
plotUMAP(sce,
         colour_by = "day")
plotUMAP(sce,
         colour_by = "donor")
plotUMAP(sce,
         colour_by = "experiment")
LS0tCnRpdGxlOiAnTm9ybWFsaXphdGlvbiwgZmVhdHVyZSBzZWxlY3Rpb24gYW5kIGRpbWVuc2lvbiByZWR1Y3Rpb24gZm9yIHRoZSBDdW9tbyBkYXRhc2V0JwphdXRob3I6ICJLb2VuIFZhbiBkZW4gQmVyZ2UgYW5kIEplcm9lbiBHaWxpcyIKZGF0ZTogIjMwLzExLzIwMjEiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKIyBJbnRyb2R1Y3Rpb24KCldlIGhlcmUgbWFrZSB1c2Ugb2YgdGhlIHB1YmxpY2F0aW9uIG9mIEFubmEgQ3VvbW8gZXQgYWwuCihsYXN0IGF1dGhvciBPbGl2ZXIgU3RlZ2xlKSwgd2hpY2ggd2Ugd2lsbCByZWZlciB0byBhcyB0aGUgYGlQU0MgZGF0YXNldGAuIFRoZSAKcGFwZXIgdGhhdCBkZXNjcmliZXMgdGhpcyBkYXRhc2V0IGNhbiBiZSBmb3VuZCB1c2luZyB0aGlzIApbbGlua10oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE0NjctMDIwLTE0NDU3LXopLgoKSW4gdGhlIGV4cGVyaW1lbnQsIHRoZSBhdXRob3JzIGhhcnZlc3RlZCBpbmR1Y2VkIHBsdXJpcG90ZW50IHN0ZW0gY2VsbHMgKGlQU0NzKQpmcm9tIDEyNSBoZWFsdGh5IGh1bWFuIGRvbm9ycy4gVGhlc2UgY2VsbHMgd2VyZSB1c2VkIHRvIHN0dWR5IHRoZSBlbmRvZGVybSAKZGlmZmVyZW50aWF0aW9uIHByb2Nlc3MuIEluIHRoaXMgcHJvY2VzcywgaVBTQ3MgZGlmZmVyZW50aWF0ZSB0byBlbmRvZGVybSBjZWxscywKYSBwcm9jZXNzIHdoaWNoIHRha2VzIGFwcHJveGltYXRlbHkgdGhyZWUgZGF5cy4gQXMgc3VjaCwgdGhlIGF1dGhvcnMgCmN1bHRlcmVkIHRoZSBpUFNDcyBjZWxsIGxpbmVzIGFuZCBhbGxvd2VkIGZvciBkaWZmZXJlbnRpYXRpb24gZm9yIHRocmVlIGRheXMuIApEdXJpbmcgdGhlIGV4cGVyaW1lbnQsIGNlbGxzIHdlcmUgaGFydmVzdGVkIGF0IGZvdXIgZGlmZmVyZW50IHRpbWUgcG9pbnRzOiAKZGF5MCAoZGlyZWN0bHkgYXQgdG8gaW5jdWJhdGlvbiksIGRheTEsIGRheTIgYW5kIGRheTMuIEtub3dpbmcgdGhlIHByb2Nlc3Mgb2YgCmVuZG9kZXJtIGRpZmZlcmVudGlhdGlvbiwgdGhlc2UgdGltZSBwb2ludHMgc2hvdWxkIGNvcnJlc3BvbmQgd2l0aCBkaWZmZXJlbnQgCmNlbGwgdHlwZXM6IGRheTAgYXJlICh1bmRpZmZlcmVudGlhdGVkKSBpUFNDcywgZGF5MSBhcmUgbWVzZW5kb2Rlcm0gY2VsbHMsIGRheTIKYXJlICJpbnRlcm1lZGlhdGUiIGNlbGxzIGFuZCBkYXkzIGFyZSBmdWxseSBkaWZmZXJlbnRpYXRlZCBlbmRvZGVybSBjZWxscy4KClRoaXMgZGF0YXNldCB3YXMgZ2VuZXJhdGVkIHVzaW5nIHRoZSAqKlNNQVJULVNlcTIqKiBzY1JOQS1zZXEgcHJvdG9jb2wuCgpUaGUgZmluYWwgZ29hbCBvZiB0aGUgZXhwZXJpbWVudCB3YXMgdG8gY2hhcmFjdGVyaXplIHBvcHVsYXRpb24gdmFyaWF0aW9uIGluIHRoZQpwcm9jZXNzIG9mIGVuZG9kZXJtIGRpZmZlcmVudGlhdGlvbi4KCiMgRG93bmxvYWQgZGF0YQoKRm9yIHRoaXMgbGFiIHNlc3Npb24sIHdlIHdpbGwgd29yayB3aXRoIGEgc3Vic2V0IG9mIHRoZSBkYXRhLCBpLmUuLCB0aGUgZGF0YQpmb3IgdGhlIGZpcnN0IChhbHBoYWJldGljYWxseSkgMTUgcGF0aWVudHMgaW4gdGhlIGV4cGVyaW1lbnQuIFRoZXNlIGNhbiBiZQpkb3dubG9hZGVkIHRocm91Z2ggdGhlICpiZWxuZXQgZmlsZXNlbmRlciogbGluayBwcm92aWRlZCB0aHJvdWdoIGVtYWlsLApodHRwczovL2ZpbGVzZW5kZXIuYmVsbmV0LmJlLz9zPWRvd25sb2FkJnRva2VuPWViODEzNmRmLTY3ZDMtNDg2OS1iMmE5LWY2NTc2NzA1NGU4MS4KClRoZSBkYXRhIG9yaWdpbmFsICgxMjUgcGF0aWVudCkgY291bGQgYmUgZG93bmxvYWRlZCBmcm9tIApbWmVub2RvXShodHRwczovL3plbm9kby5vcmcvcmVjb3JkLzM2MjUwMjQjLllXZmFodGxCeEIxKS4gQXQgdGhlIGJvdHRvbSBvZiB0aGlzCndlYi1wYWdlLCB3ZSBjYW4gZG93bmxvYWQgdGhlIGZpbGVzIGByYXdfY291bnRzLmNzdi56aXBgIGFuZCAKYGNlbGxfbWV0YWRhdGFfY29scy50c3ZgIGFuZCBzdG9yZSB0aGVzZSBmaWxlcyBsb2NhbGx5LiBXZSBkbyBub3QgcmVjb21tZW5kIApkb2luZyB0aGlzIGR1cmluZyB0aGUgbGFiIHNlc3Npb24sIHRvIGF2b2lkIG92ZXJsb2FkaW5nIHRoZSBzeXN0ZW0uCgojIEltcG9ydCBkYXRhCgpGaXJzdCB3ZSByZWFkIGluIHRoZSBjb3VudCBtYXRyaXg6CgpgYGB7ciwgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKc2NlIDwtIHJlYWRSRFMoIi9Vc2Vycy9qZy9EZXNrdG9wL3NjZV8xNV9jdW9tby5yZHMiKSAjIGNoYW5nZSB0byBZT1VSIHBhdGghCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CnNjZQpgYGAKCiMgRXhwbG9yZSBtZXRhZGF0YQoKRXhwbG9yYXRpb24gb2YgdGhlIG1ldGFkYXRhIGlzIGVzc2VudGlhbCB0byBnZXQgYSBiZXR0ZXIgaWRlYSBvZiB3aGF0IHRoZQpleHBlcmltZW50IHdhcyBhYm91dCBhbmQgaG93IGl0IHdhcyBvcmdhbml6ZWQuCgpgYGB7ciwgZXZhbD1GQUxTRX0KY29sRGF0YShzY2UpWzE6NSwxOjEwXQpjb2xuYW1lcyhjb2xEYXRhKHNjZSkpCmBgYAoKQXMgc3RhdGVkIGluIHRoZSBwYXBlciwgY2VsbHMgd2VyZSBzYW1wbGVkIG9uIDQgdGltZSBwb2ludHMuIEVhY2ggb2YgdGhlc2UgCnRpbWUgcG9pbnRzIGlzIGV4cGVjdGVkIHRvIGNvcnJlc3BvbmQgd2l0aCBkaWZmZXJlbnQgY2VsbCB0eXBlcyAoZGF5MCA9IGlQU0MsCmRheTEgPSBtZXNlbmRvZGVybSwgZGF5MiA9IGludGVybWVkaWF0ZSBhbmQgZGF5MyA9IGVuZG9kZXJtKS4KCmBgYHtyLCBldmFsPUZBTFNFfQp0YWJsZShjb2xEYXRhKHNjZSkkZGF5KQpgYGAKCkFzIHN0YXRlZCBpbiB0aGUgcGFwZXIsIGNlbGxzIHdlcmUgaGFydmVzdGVkIGZyb20gMTI1IHBhdGllbnRzLiBIZXJlLCB3ZSBhcmUKd29ya2luZyBvbiBhIHN1YnNldCB3aXRoIDE1IHBhdGllbnRzLiBUaGUgbnVtYmVyIG9mIGNlbGxzIGhhcnZlc3RlZCBwZXIgcGF0aWVudCAKKG92ZXIgYWxsIHRpbWUgcG9pbnRzKSByYW5nZXMgZnJvbSAzMSB0byA2MzcuCgpgYGB7ciwgZXZhbD1GQUxTRX0KbGVuZ3RoKHRhYmxlKGNvbERhdGEoc2NlKSRkb25vcikpICMgbnVtYmVyIG9mIGRvbm9ycwpyYW5nZSh0YWJsZShjb2xEYXRhKHNjZSkkZG9ub3IpKSAjIGNlbGxzIHBlciBkb25vcgpgYGAKCkJlbG93LCB3ZSBsb29rIGhvdyBtYW55IGNlbGxzIGFyZSBoYXJ2ZXN0IHBlciBwYXRlbnQgYW5kIHBlciB0aW1lIHBvaW50LgoKYGBge3IsIGV2YWw9RkFMU0V9CnRhYmxlKGNvbERhdGEoc2NlKSRkb25vcixjb2xEYXRhKHNjZSkkZGF5KQpgYGAKCldlIHNlZSB0aGF0IGZvciBtYW55IHBhdGllbnRzIHRoZSBkYXRhIGlzIGNvbXBsZXRlLCBpLmUuIGNlbGxzIHdlcmUgc2FtcGxlZApvbiBhbGwgdGltZSBwb2ludHMuCgpQcmFjdGljYWxseSwgdGhlIGNlbGxzIHdlcmUgcHJlcGFyZWQgaW4gMjggYmF0Y2hlcy4gU2luY2Ugd2UgaGVyZSBvbmx5IGxvb2sKYXQgYSBzdWJzZXQgb2YgdGhlIGRhdGEsIHdlIHNlZSB0aGF0IG9ubHkgMTQgb2YgdGhlc2UgYmF0Y2hlcyBhcmUgcmVwcmVzZW50ZWQgCmhlcmUuCgpgYGB7ciwgZXZhbD1GQUxTRX0KbGVuZ3RoKHRhYmxlKGNvbERhdGEoc2NlKSRleHBlcmltZW50KSkKdGFibGUoY29sRGF0YShzY2UpJGV4cGVyaW1lbnQsIGNvbERhdGEoc2NlKSRkYXkpCmBgYAoKIyBPYnRhaW5pbmcgYW5kIGluY2x1ZGluZyByb3dEYXRhCgpUaGUgYHJvd0RhdGFgIHNsb3Qgb2YgYSBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIG9iamVjdCBhbGxvd3MgZm9yIHN0b3JpbmcgCmluZm9ybWF0aW9uIG9uIHRoZSBmZWF0dXJlcywgaS5lLiB0aGUgZ2VuZXMsIGluIGEgZGF0YXNldC4gSW4gb3VyIG9iamVjdCwKdGhlIGByb3dEYXRhYCBzbG90IGN1cnJlbnRseSBjb250YWlucyB0aGUgZm9sbG93aW5nOgoKYGBge3IsIGV2YWw9RkFMU0V9CmhlYWQocm93RGF0YShzY2UpKQpgYGAKClRvIGltcHJvdmUgb3VyIGdlbmUtbGV2ZWwgaW5mb3JtYXRpb24sIHdlIG1heToKCjEuIFNwbGl0IGBWMWAgaW50byB0d28gY29sdW1ucywgb25lIHdpdGggdGhlIEVOU0VNQkwgSUQgYW5kIHRoZSBvdGhlciB3aXRoIAp0aGUgZ2VuZSBzeW1ib2wuCgoyLiBEaXNwbGF5IHdoaWNoIGNocm9tb3NvbWUgdGhlIGdlbmUgaXMgbG9jYXRlZAoKTWFueSBtb3JlIG9wdGlvbnMgYXJlIHBvc3NpYmxlLCBidXQgYXJlIG5vdCBuZWNlc3NhcnkgZm9yIHVzIHJpZ2h0IG5vdy4KCmBgYHtyLCBldmFsPUZBTFNFfQpyb3dEYXRhKHNjZSkgPC0gZGF0YS5mcmFtZShFbnNlbWJsID0gZ3N1YigiXy4qIiwgIiIsIHJvd0RhdGEoc2NlKSRWMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFN5bWJvbCA9IGdzdWIoIl5bXl9dKl8iLCAiIiwgcm93RGF0YShzY2UpJFYxKSkKaGVhZChyb3dEYXRhKHNjZSkpCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KCJiaW9tYVJ0IikKZW5zZW1ibDc1IDwtIHVzZUVuc2VtYmwoYmlvbWFydCA9ICdnZW5lcycsIAogICAgICAgICAgICAgICAgICAgICAgICBkYXRhc2V0ID0gJ2hzYXBpZW5zX2dlbmVfZW5zZW1ibCcsCiAgICAgICAgICAgICAgICAgICAgICAgIHZlcnNpb24gPSA3NSkKCkdlbmVJbmZvIDwtIGdldEJNKGF0dHJpYnV0ZXMgPSBjKCJlbnNlbWJsX2dlbmVfaWQiLCAjIFRvIG1hdGNoIHdpdGggcm93bmFtZXMgU0NFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjaHJvbW9zb21lX25hbWUiKSwgIyBJbmZvIG9uIGNocm9tb3NlCiAgICAgICAgICAgICAgICAgIG1hcnQgPSBlbnNlbWJsNzUpCkdlbmVJbmZvIDwtIEdlbmVJbmZvW21hdGNoKHJvd0RhdGEoc2NlKSRFbnNlbWJsLCBHZW5lSW5mbyRlbnNlbWJsX2dlbmVfaWQpLF0KCnJvd0RhdGEoc2NlKSA8LSBjYmluZChyb3dEYXRhKHNjZSksIEdlbmVJbmZvKQpoZWFkKHJvd0RhdGEoc2NlKSkKYWxsKHJvd0RhdGEoc2NlKSRFbnNlbWJsID09IHJvd0RhdGEoc2NlKSRlbnNlbWJsX2dlbmVfaWQpIAojIGlkZW50aWNhbCwgYXMgZGVzaXJlZCwgc28gd2UgY291bGQgb3B0aW9uYWxseSByZW1vdmUgb25lIG9mIHRoZSB0d28KYGBgCgojIEZpbHRlcmluZyBub24taW5mb3JtYXRpdmUgZ2VuZXMKCkxldCB1cyBmaXJzdCB0cnkgdGhlIHZlcnkgc2ltcGxlIGFuZCB2ZXJ5IGxlbmllbnQgZmlsdGVyaW5nIGNyaXRlcmlvbiB0aGF0IHdlCmFkb3B0ZWQgZm9yIHRoZSBNYWNvc2tvIGRhdGFzZXQuCgpgYGB7ciwgZXZhbD1GQUxTRX0Ka2VlcCA8LSByb3dTdW1zKGFzc2F5cyhzY2UpJGNvdW50cyA+IDApID4gMTAKdGFibGUoa2VlcCkKCnNjZSA8LSBzY2Vba2VlcCxdCmBgYAoKV2Ugc2VlIHRoYXQgdGhpcyBmaWx0ZXJpbmcgc3RyYXRlZ3kgZG9lcyBub3QgcmVtb3ZlIGFueSBnZW5lcyBmb3IgdGhpcyBkYXRhc2V0LgpJbiBnZW5lcmFsLCBkYXRhc2V0cyBmcm9tIHBsYXRlLWJhc2VkIHNjUk5BLXNlcSBkYXRhc2V0IGhhdmUgYSBmYXIgaGlnaGVyCnNlcXVlbmNpbmcgZGVwdGggdGhhbiBkYXRhIGZyb20gZHJvcGxldC1iYXNlZCBwcm90b2NvbHMuIEFzIHJlcXVpcmluZyBhIG1pbmltdW0KZXhwcmVzc2lvbiBvZiAxIGNvdW50IGluIGF0IGxlYXN0IDEwIGNlbGxzIGlzIGEgdmVyeSBsZW5pZW50IGNyaXRlcmlvbiBpZiB3ZSAKY29uc2lkZXIgdGhhdCB3ZSBoYXZlIDM2LjAwMCBjZWxscywgd2Ugc2hvdWxkIGNvbnNpZGVyIGFkb3B0aW5nIGEgbW9yZSBzdHJpbmdlbnQKZmlsdGVyaW5nIGNyaXRlcml1bSwgbGlrZSB0aGUgYGZpbHRlckJ5RXhwcmAgZnJvbSBgZWRnZVJgOgoKYGBge3IsIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZWRnZVIpCgp0YWJsZShjb2xEYXRhKHNjZSkkZGF5KQoKa2VlcDIgPC0gZWRnZVI6OmZpbHRlckJ5RXhwcih5PXNjZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IGNvbERhdGEoc2NlKSRkYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLmNvdW50ID0gNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4ucHJvcCA9IDAuNCkKdGFibGUoa2VlcDIpCgpzY2UgPC0gc2NlW2tlZXAyLF0KYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KeGFzIDwtIHJvd1N1bXMoYXNzYXkoc2NlKSA+IDApCnlhcyA8LSByb3dTdW1zKGFzc2F5KHNjZSkpCgoKcGxvdCh4ID0geGFzLAogICAgIHkgPSB5YXMsCiAgICAgbG9nID0gInkiLAogICAgIHBjaCA9IDE5LAogICAgIGNleD0wLjMsCiAgICAgY29sID0gYXMuZmFjdG9yKGtlZXAyKSkKYGBgCgoKIyBRdWFsaXR5IGNvbnRyb2wKCiMjIENhbGN1bGF0ZSBRQyB2YXJpYWJsZXMKCmBgYHtyLCBldmFsPUZBTFNFfQpsaWJyYXJ5KHNjYXRlcikKCiMgY2hlY2sgRVJDQyBzcGlrZS1pbiB0cmFuc2NyaXB0cwpzdW0oZ3JlcGwoIl5FUkNDLSIsIHJvd0RhdGEoc2NlKSRTeW1ib2wpKSAjIG5vIHNwaWtlLWluIHRyYW5zY3JpcHRzIGF2YWlsYWJsZQoKaXMubWl0byA8LSBncmVwbCgiXk1UIiwgcm93RGF0YShzY2UpJGNocm9tb3NvbWVfbmFtZSkKc3VtKGlzLm1pdG8pICMgMTMgbWl0b2Nob25kcmlhbCBnZW5lcwoKZGYgPC0gcGVyQ2VsbFFDTWV0cmljcyhzY2UsIHN1YnNldHM9bGlzdChNaXRvPWlzLm1pdG8pKQpoZWFkKGRmKQoKIyMgYWRkIHRoZSBRQyB2YXJpYWJsZXMgdG8gc2NlIG9iamVjdApjb2xEYXRhKHNjZSkgPC0gY2JpbmQoY29sRGF0YShzY2UpLCBkZikKYGBgCgojIyBFeHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzCgpJbiB0aGUgZmlndXJlIGJlbG93LCB3ZSBzZWUgdGhhdCBzZXZlcmFsIGNlbGxzIGhhdmUgYSB2ZXJ5IGxvdyBudW1iZXIgb2YgCmV4cHJlc3NlZCBnZW5lcywgYW5kIHdoZXJlIG1vc3Qgb2YgdGhlIG1vbGVjdWxlcyBhcmUgZGVyaXZlZCBmcm9tIAptaXRvY2hvbmRyaWFsIGdlbmVzLiBUaGlzIGluZGljYXRlcyBsaWtlbHkgZGFtYWdlZCBjZWxscywgcHJlc3VtYWJseSBiZWNhdXNlIApvZiBsb3NzIG9mIGN5dG9wbGFzbWljIFJOQSBmcm9tIHBlcmZvcmF0ZWQgY2VsbHMsIHNvIHdlIHNob3VsZCByZW1vdmUgdGhlc2UgZm9yIAp0aGUgZG93bnN0cmVhbSBhbmFseXNpcy4KCmBgYHtyLCBldmFsPUZBTFNFfQojIE51bWJlciBvZiBnZW5lcyB2cyBsaWJyYXJ5IHNpemUKcGxvdENvbERhdGEoc2NlLCB4ID0gInN1bSIsIHk9ImRldGVjdGVkIiwgY29sb3VyX2J5PSJkYXkiKSAKCiMgTWl0b2Nob25kcmlhbCBnZW5lcwpwbG90Q29sRGF0YShzY2UsIHggPSAiZGV0ZWN0ZWQiLCB5PSJzdWJzZXRzX01pdG9fcGVyY2VudCIsIGNvbG91cl9ieT0iZGF5IikKYGBgCgojIyBRQyB1c2luZyBhZGFwdGl2ZSB0aHJlc2hvbGRzCgpCZWxvdywgd2UgcmVtb3ZlIGNlbGxzIHRoYXQgYXJlIG91dGx5aW5nIHdpdGggcmVzcGVjdCB0bwoKIDEuIEEgbG93IHNlcXVlbmNpbmcgZGVwdGggKG51bWJlciBvZiBVTUlzKTsKIDIuIEEgbG93IG51bWJlciBvZiBnZW5lcyBkZXRlY3RlZDsKIDMuIEEgaGlnaCBwZXJjZW50YWdlIG9mIHJlYWRzIGZyb20gbWl0b2Nob25kcmlhbCBnZW5lcy4KIApXZSByZW1vdmUgYSB0b3RhbCBvZiAkMzAxJCBjZWxscywgbWFpbmx5IGR1ZSB0byBsb3cgc2VxdWVuY2luZyBkZXB0aCBhbmQKbG93IG51bWJlciBvZiBnZW5lcyBkZXRlY3RlZC4KCmBgYHtyLCBldmFsPUZBTFNFfQpsb3dMaWIgPC0gaXNPdXRsaWVyKGRmJHN1bSwgdHlwZT0ibG93ZXIiLCBsb2c9VFJVRSkKbG93RmVhdHVyZXMgPC0gaXNPdXRsaWVyKGRmJGRldGVjdGVkLCB0eXBlPSJsb3dlciIsIGxvZz1UUlVFKQpoaWdoTWl0byA8LSBpc091dGxpZXIoZGYkc3Vic2V0c19NaXRvX3BlcmNlbnQsIHR5cGU9ImhpZ2hlciIpCgp0YWJsZShsb3dMaWIpCnRhYmxlKGxvd0ZlYXR1cmVzKQp0YWJsZShoaWdoTWl0bykKCmRpc2NhcmRDZWxscyA8LSAobG93TGliIHwgbG93RmVhdHVyZXMgfCBoaWdoTWl0bykKdGFibGUoZGlzY2FyZENlbGxzKQpjb2xEYXRhKHNjZSkkZGlzY2FyZENlbGxzIDwtIGRpc2NhcmRDZWxscwoKIyB2aXN1YWxpemUgY2VsbHMgdG8gYmUgcmVtb3ZlZApwbG90Q29sRGF0YShzY2UsIHggPSAiZGV0ZWN0ZWQiLCB5PSJzdWJzZXRzX01pdG9fcGVyY2VudCIsIGNvbG91cl9ieSA9ICJkaXNjYXJkQ2VsbHMiKQpwbG90Q29sRGF0YShzY2UsIHggPSAic3VtIiwgeT0iZGV0ZWN0ZWQiLCBjb2xvdXJfYnk9ImRpc2NhcmRDZWxscyIpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CiMgdmlzdWFsaXplIGNlbGxzIHRvIGJlIHJlbW92ZWQKcGxvdENvbERhdGEoc2NlLCB4ID0gImRldGVjdGVkIiwgeT0ic3Vic2V0c19NaXRvX3BlcmNlbnQiLCBjb2xvdXJfYnkgPSAiZG9ub3IiKQpwbG90Q29sRGF0YShzY2UsIHggPSAic3VtIiwgeT0iZGV0ZWN0ZWQiLCBjb2xvdXJfYnk9ImRvbm9yIikKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyB2aXN1YWxpemUgY2VsbHMgdG8gYmUgcmVtb3ZlZApwbG90Q29sRGF0YShzY2UsIHggPSAiZGV0ZWN0ZWQiLCB5PSJzdWJzZXRzX01pdG9fcGVyY2VudCIsIGNvbG91cl9ieSA9ICJleHBlcmltZW50IikKcGxvdENvbERhdGEoc2NlLCB4ID0gInN1bSIsIHk9ImRldGVjdGVkIiwgY29sb3VyX2J5PSJleHBlcmltZW50IikKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KdGFibGUoc2NlJGRvbm9yLCBzY2UkZGlzY2FyZENlbGxzKQp0YWJsZShzY2UkZG9ub3IsIHNjZSRkaXNjYXJkQ2VsbHMpL3Jvd1N1bXModGFibGUoc2NlJGRvbm9yLCBzY2UkZGlzY2FyZENlbGxzKSkKI2ZyYWN0aW9ucyBvZiByZW1vdmVkIGNlbGxzIHBlciBkb25vcgpgYGAKCk1vc3QgcmVtb3ZlZCBjZWxscyAoZnJhY3Rpb24pIGFyZSBmcm9tIHBhdGllbnRzIGBkaXhoYCBhbmQgYGJhYnpgLgoKYGBge3IsIGV2YWw9RkFMU0V9CnRhYmxlKHNjZSRleHBlcmltZW50LCBzY2UkZGlzY2FyZENlbGxzKQp0YWJsZShzY2UkZXhwZXJpbWVudCwgc2NlJGRvbm9yKQpgYGAKCk1vc3QgcmVtb3ZlZCBjZWxscyAoZnJhY3Rpb24pIGFyZSBmcm9tIHBhdGllbnRzIGBkaXhoYCBhbmQgYGJhYnpgLgpNb3N0IGxvdyBsaWJyYXJ5IHNpemVzIHNlZW0gdG8gY29tZSBmcm9tIHBhdGllbnQgYGRpeGhgOyBmb3IgcGF0aWVudCBgYmFiemAKdGhlIGVmZmVjdCBpcyBsZXNzIHByb25vdW5jZWQuCgpgYGB7ciwgZXZhbD1GQUxTRX0KcGxvdENvbERhdGEoc2NlWyxzY2UkZG9ub3I9PSJkaXhoIl0sIHggPSAic3VtIiwgeT0iZGV0ZWN0ZWQiKQpwbG90Q29sRGF0YShzY2VbLHNjZSRkb25vcj09ImJhYnoiXSwgeCA9ICJzdW0iLCB5PSJkZXRlY3RlZCIpCmBgYAoKQXMgc3VjaCwgd2UgYXJlIG1haW5seSByZW1vdmluZyBjZWxscyBmcm9tIHNwZWNpZmljIHBhdGllbnRzIGFuZCB0aGUgcmVzcGVjdGl2ZQpiYXRjaGVzIGluIHdoaWNoIHRoZXkgd2VyZSBzZXF1ZW5jZWQuIEhvd2V2ZXIsIHdlIHdhbnQgdG8gYmUgY2FyZWZ1bDsgd2Ugb25seQp3YW50IHRvIHJlbW92ZSB0ZWNobmljYWwgYXJ0ZWZhY3RzLCB3aGlsZSByZXRhaW5pbmcgYXMgbXVjaCBvZiB0aGUgYmlvbG9neSBhcwpwb3NzaWJsZS4gSW4gb3VyIGV4cGxvcmF0b3J5IGZpZ3VyZSwgd2Ugc2VlIHRoYXQgdGhlIGNlbGxzIHdlIGFyZSByZW1vdmluZyBiYXNlZApvbiB0aGUgbnVtYmVyIG9mIGdlbmVzIGRldGVjdGVkLCBhcmUgcXVpdGUgZmFyIGFwYXJ0IGZyb20gdGhlIGJ1bGsgb2YgdGhlIGRhdGEKY2xvdWQ7IGFzIHN1Y2gsIHRoZXNlIGNlbGxzIGFyZSBpbmRlZWQgc3VzcGljaW91cy4gRm9yIHRoZSBjcml0ZXJpb24gb2YKbGlicmFyeSBzaXplLCB3ZSBzZWUgdGhhdCB0aGUgY2VsbHMgcmVtb3ZlZCB0aGVyZSBhcmUgc3RpbGwgc3Ryb25nbHkgY29ubmVjdGVkCnRvIHRoZSBkYXRhIGNsb3VkLiBBcyBzdWNoLCB3ZSBtYXkgd2FudCB0byByZWxheCB0aGUgZmlsdGVyaW5nIGNyaXRlcmlvbiB0aGVyZSBhCmxpdHRsZSBiaXQuIFdoZW4gd2UgdGhpbmsgb2YgaG93IHRoZSBhZGFwdGl2ZSB0aHJlc2hvbGQgc3RyYXRlZ3kgd29ya3MsIHdlCm1heSB3YW50IHRvIHJlbW92ZSBjZWxscyB0aGF0IGFyZSA0TUFEcyBhd2F5IGZyb20gdGhlIGNlbnRlciwgcmF0aGVyIHRoYW4KdGhlIGRlZmF1bHQgMyBNQURzLgoKYGBge3IsIGV2YWw9RkFMU0V9CiMgcHJldmlvdXNseQpsb3dMaWIgPC0gaXNPdXRsaWVyKGRmJHN1bSwgdHlwZT0ibG93ZXIiLCBsb2c9VFJVRSkKdGFibGUobG93TGliKQoKIyBhZnRlciBzZWVpbmcgYXBwcm9wcmlhdGUgZXhwbG9yYXRvcnkgZmlndXJlCmxvd0xpYiA8LSBpc091dGxpZXIoZGYkc3VtLCBubWFkcz00LCB0eXBlPSJsb3dlciIsIGxvZz1UUlVFKQp0YWJsZShsb3dMaWIpCgpkaXNjYXJkQ2VsbHMgPC0gKGxvd0xpYiB8IGxvd0ZlYXR1cmVzIHwgaGlnaE1pdG8pCnRhYmxlKGRpc2NhcmRDZWxscykKY29sRGF0YShzY2UpJGRpc2NhcmRDZWxscyA8LSBkaXNjYXJkQ2VsbHMKYGBgCgpOb3RlIHRoYXQgdGhlc2Ugc3RlcHMgYXJlIG5vdCBleGFjdDsgZGlmZmVyZW50IGFuYWx5c3RzIHdpbGwgY29tZSB3aXRoIGRpZmZlcmVudApmaWx0ZXJpbmcgY3JpdGVyaWEgZm9yIG1hbnkgb2YgdGhlIHN0ZXBzLiBUaGUga2V5IGlkZWFzIGFyZSB0aGF0CndlIGxldCBhcHByb3ByaWF0ZSBleHBsb3JhdG9yeSBmaWd1cmVzIGd1aWRlIHVzIHRvIG1ha2UgcmVhc29uYWJsZSBjaG9pY2VzOwppLmUuLCB3ZSBsb29rIGF0IHRoZSBkYXRhIHJhdGhlciB0aGFuIGJsaW5kbHkgZm9sbG93aW5nIGEgc3RhbmRhcmRpemVkIHBpcGVsaW5lCnRoYXQgbWF5IHdvcmsgd2VsbCBpbiBtYW55IGNhc2VzLCBidXQgbWF5YmUgbm90IG91ciBwYXJ0aWN1bGFyIGRhdGFzZXQuCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyByZW1vdmUgY2VsbHMgaWRlbnRpZmllZCB1c2luZyBhZGFwdGl2ZSB0aHJlc2hvbGRzCnNjZSA8LSBzY2VbLCAhY29sRGF0YShzY2UpJGRpc2NhcmRDZWxsc10KYGBgCgojIyBJZGVudGlmeWluZyBhbmQgcmVtb3ZpbmcgZW1wdHkgZHJvcGxldHMKClRoaXMgZG9lcyBub3QgbWFrZSBtdWNoIHNlbnNlIGZvciBwbGF0ZS1iYXNlZCBkYXRhISBXaGlsZSB3ZSBjb3VsZCBpbWFnaW5lIHRoZQpwcmVzZW5jZSBvZiBlbXB0eSBwbGF0ZSB3ZWxscyBmb3IgdGhlIFNNQVJULVNlcTIgZXhwZXJpbWVudCwgdGhlc2UgYXJlIHR5cGljYWxseQpkZXRlY3RlZCBhbmQgcmVtb3ZlZCB1cHN0cmVhbSBvZiBvdXIgYW5hbHlzaXMgdXNpbmcgbGFzZXIgdGVjaG5vbG9neS4KCiMjIElkZW50aWZ5aW5nIGFuZCByZW1vdmluZyBkb3VibGV0cwoKQWdhaW4sIHRoaXMgc3RlcCB0eXBpY2FsbHkgZG9lcyBub3QgbWFrZSBtdWNoIHNlbnNlIGZvciBwbGF0ZS1iYXNlZCAKZGF0YS4gV2hpbGUgd2UgY291bGQgaW1hZ2luZSB0aGUgcHJlc2VuY2Ugb2YgcGxhdGUgd2VsbHMgaW4gd2hpY2ggbW9yZSB0aGFuIHR3bwpjZWxscyBhcmUgcHJlc2VudCBmb3IgdGhlIFNNQVJULVNlcTIgZXhwZXJpbWVudCwgdGhlc2UgYXJlIGFnYWluIHR5cGljYWxseQpkZXRlY3RlZCBhbmQgcmVtb3ZlZCB1cHN0cmVhbSBvZiBvdXIgYW5hbHlzaXMgdXNpbmcgbGFzZXIgdGVjaG5vbG9neS4KCklmIHdlIHdvdWxkIHVzZQpbc2NEYmxGaW5kZXJdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy8zLjE0L2Jpb2MvaHRtbC9zY0RibEZpbmRlci5odG1sKSAKdG8gZGV0ZWN0IGRvdWJsZXQgY2VsbHMgb24gdGhpcyBkYXRhc2V0LCB3ZSB3b3VsZCBzZWUgdGhhdCBvbmx5IGEgdmVyeSBzbWFsbCAKZnJhY3Rpb24gb2YgdGhlIGNlbGxzIHdvdWxkIGJlIHJlbW92ZWQuIFRoZXNlIHJlbW92ZWQgY2VsbHMgY291bGQgY29ycmVzcG9uZCB0byAKYWN0dWFsIGRvdWJsZXRzIHRoYXQgd2VyZSBtaXNzZWQgYnkgdGhlIHVwc3RyZWFtIGRvdWJsZXQgZGV0ZWN0aW9uIHRlY2hub2xvZ3ksCm9yIG1vcmUgbGlrZWx5IGNvdWxkIGNvcnJlc3BvbmQgdG8gZmFsc2UgcG9zaXRpdmUgZG91YmxldHMgb2YgdGhlIGBzY0RibEZpbmRlcmAKZG91YmxldCBkZXRlY3Rpb24gcHJvY2VkdXJlLgoKIyBOb3JtYWxpemF0aW9uCgpGb3Igbm9ybWFsaXphdGlvbiwgdGhlIHNpemUgZmFjdG9ycyAkc19pJCBjb21wdXRlZCBoZXJlIGFyZSBzaW1wbHkgc2NhbGVkIApsaWJyYXJ5IHNpemVzOgoKXFsgTl9pID0gXHN1bV9nIFlfe2dpfSBcXQpcWyBzX2kgPSBOX2kgLyBcYmFye059X2kgXF0KCmBgYHtyLCBldmFsPUZBTFNFfQpzY2UgPC0gbG9nTm9ybUNvdW50cyhzY2UpCgojIG5vdGUgd2UgYWxzbyByZXR1cm5lZCBsb2cgY291bnRzOiBzZWUgdGhlIGFkZGl0aW9uYWwgbG9nY291bnRzIGFzc2F5LgpzY2UKCiMgeW91IGNhbiBleHRyYWN0IHNpemUgZmFjdG9ycyB1c2luZwpzZiA8LSBsaWJyYXJ5U2l6ZUZhY3RvcnMoc2NlKQptZWFuKHNmKSAjIGVxdWFsIHRvIDEgZHVlIHRvIHNjYWxpbmcuCnBsb3QoeD0gbG9nKGNvbFN1bXMoYXNzYXlzKHNjZSkkY291bnRzKSksIAogICAgIHk9c2YpCmBgYAoKLS0tCgotLS0gZW5kIGxhYiBzZXNzaW9uIDEgLS0tCgotLS0KClZpc3VhbGl6ZSB0aGUgZWZmZWN0IG9mIG5vcm1hbGl6YXRpb24gdXNpbmcgTUQtcGxvdHMuCgpJbiBvcmRlciB0byBvbmx5IHdvcmsgd2l0aCBjZWxscyB0aGF0IGNvdWxkIGJlIGNvbnNpZGVyZWQgdGVjaG5pY2FsIHJlcGxpY2F0ZXMsCndlIHNob3VsZCBzZWxlY3QgY2VsbHMgY29taW5nIGZyb20gdGhlIHNhbWUgYmlvbG9naWNhbCBncm91cC4gV2UgY2FuIGRvIHRoaXMKYXBwcm94aW1hdGVseSBieSBzZWxlY3RpbmcgY2VsbHMgZnJvbSB0aGUgc2FtZSBzYW1wbGluZyBkYXksIHRoZSBzYW1lIApkb25vciBhbmQgdGhlIHNhbWUgc2VxdWVuY2luZyBleHBlcmltZW50LgoKYGBge3IsIGV2YWw9RkFMU0V9CnRhYmxlKHNjZSRkb25vciwgc2NlJGRheSkKdGFibGUoc2NlJGV4cGVyaW1lbnQsIHNjZSRkYXkpCnRhYmxlKHNjZSRleHBlcmltZW50LCBzY2UkZG9ub3IpCmBgYAoKQmVsb3csIHdlIHdpbGwgdmlzdWFsaXplIHRoZSBub3JtYWxpemF0aW9uIG9jY3VycmluZyBiZXR3ZWVuIHR3byBjZWxscyBvZiB0aGUgCnNhbWUgY2VsbCB0eXBlIC0gZG9ub3IvZXhwZXJpbWVudCBjb21iaW5hdGlvbiAod2hpY2ggY291bGQgYmUgY29uc2lkZXJlZCAKdGVjaG5pY2FsIHJlcGVhdHMpOiAKCmBgYHtyLCBldmFsPUZBTFNFfQpjcyA8LSBjb2xTdW1zKGFzc2F5cyhzY2UpJGNvdW50c1ssd2hpY2goc2NlJGRheSA9PSAiZGF5MiIgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NlJGRvbm9yID09ICJlaXBsIildKQpjc1tvcmRlcihjcywgZGVjcmVhc2luZyA9IFRSVUUpXVtjKDEsMTAsMTAwKV0KYGBgCgpMZXTigJlzIHRha2UgYSBsb29rIGF0IGhvdyBjb21wYXJhYmxlIHR3byBjZWxscyAocmVwbGljYXRlcykgb2YgdGhpcyBiaW9sb2dpY2FsIGdyb3VwCmFyZS4gV2Ugd2lsbCBjb21wYXJlIHRoZSBjZWxsIHdpdGggdGhlIGhpZ2hlc3QgbGlicmFyeSBzaXplIHdpdGggdGhlIGNlbGwgdGhhdCAKaGFzIHRoZSAxMHRoIGFuZCAxMDB0aCBoaWdoZXN0IGxpYnJhcnkgc2l6ZSB1c2luZyBNRC1wbG90cyAobWVhbi1kaWZmZXJlbmNlIApwbG90cywgYXMgaW50cm9kdWNlZCBieSAKW0R1ZG9pdCBldCBhbC4gKDIwMDIpXShodHRwczovL3d3dy5qc3Rvci5vcmcvc3RhYmxlLzI0MzA3MDM4P3NlcT0xI21ldGFkYXRhX2luZm9fdGFiX2NvbnRlbnRzKSksIAphbHNvIHNvbWV0aW1lcyByZWZlcnJlZCB0byBhcyBNQS1wbG90cy4KCmBgYHtyLCBldmFsPUZBTFNFfQp0YXJnZXRDZWxscyA8LSBuYW1lcyhjc1tvcmRlcihjcywgZGVjcmVhc2luZyA9IFRSVUUpXVtjKDEsMTApXSkKCk0gPC0gcm93TWVhbnMoYXNzYXlzKHNjZSkkY291bnRzWyx0YXJnZXRDZWxsc10pCkQgPC0gYXNzYXlzKHNjZSkkY291bnRzWyx0YXJnZXRDZWxsc1syXV0gLyBhc3NheXMoc2NlKSRjb3VudHNbLHRhcmdldENlbGxzWzFdXQpwbG90KHggPSBsb2coTSksIHkgPSBsb2cyKEQpLAogICAgIHBjaCA9IDE2LCBjZXg9MS8zLAogICAgIG1haW4gPSBwYXN0ZTAoIkNlbGwgIiwgdGFyZ2V0Q2VsbHNbMl0sICIgdnMgY2VsbCAiLCB0YXJnZXRDZWxsc1sxXSksCiAgICAgeGxhYiA9ICJMb2cgbWVhbiIsIHlsYWIgPSAiTG9nMiBmb2xkLWNoYW5nZSIsCiAgICAgYnR5ID0gJ2wnKQphYmxpbmUoaCA9IDAsIGNvbD0ib3JhbmdlIiwgbHdkPTIpCgp0YXJnZXRDZWxscyA8LSBuYW1lcyhjc1tvcmRlcihjcywgZGVjcmVhc2luZyA9IFRSVUUpXVtjKDEsMTAwKV0pCgpNIDwtIHJvd01lYW5zKGFzc2F5cyhzY2UpJGNvdW50c1ssdGFyZ2V0Q2VsbHNdKQpEIDwtIGFzc2F5cyhzY2UpJGNvdW50c1ssdGFyZ2V0Q2VsbHNbMl1dIC8gYXNzYXlzKHNjZSkkY291bnRzWyx0YXJnZXRDZWxsc1sxXV0KcGxvdCh4ID0gbG9nKE0pLCB5ID0gbG9nMihEKSwKICAgICBwY2ggPSAxNiwgY2V4PTEvMywKICAgICBtYWluID0gcGFzdGUwKCJDZWxsICIsIHRhcmdldENlbGxzWzJdLCAiIHZzIGNlbGwgIiwgdGFyZ2V0Q2VsbHNbMV0pLAogICAgIHhsYWIgPSAiTG9nIG1lYW4iLCB5bGFiID0gIkxvZzIgZm9sZC1jaGFuZ2UiLAogICAgIGJ0eSA9ICdsJykKYWJsaW5lKGggPSAwLCBjb2w9Im9yYW5nZSIsIGx3ZD0yKQpgYGAKCldlIHNlZSBjbGVhciBiaWFzIGluIHRoZSBjb21wYXJpc29uIG9mIHRoZSAxc3QgYW5kIDEwdGgvMTAwdGggbW9zdCBkZWVwbHkgCnNlcXVlbmNlZCBjZWxsIGZyb20gdGhpcyBkYXkvcGF0aWVudCBjb21iaW5hdGlvbi4gV2Ugc2VlIHRoYXQgdGhlIGxvZyBmCm9sZC1jaGFuZ2VzIGFyZSBiaWFzZWQgZG93bndhcmRzLiBUaGlzIG1lYW5zIHRoYXQsIG9uIGF2ZXJhZ2UsIGEgZ2VuZSBpcyBsb3dlciAKZXhwcmVzc2VkIGluIGNlbGwgMSB2ZXJzdXMgY2VsbCAxMCAoYW5kIGNlbGwgMTAwKS4gTG9va2luZyBhdCB0aGUgbGlicmFyeSBzaXplcywgCndlIGNhbiBpbmRlZWQgc2VlIHRoYXQgdGhlIGxpYnJhcnkgc2l6ZSBmb3IgY2VsbCAxIGlzIDEuMjAwLjY5MSBjb3VudHMsIHdoaWxlIGl0CmlzIG9ubHkgNzAxLjU1MCBjb3VudHMgZm9yIGNlbGwgMTAgYW5kIDQ2OC43NjIgY291bnRzIGZvciBjZWxsIDEwMCEgVGhpcyBpcyBhCmNsZWFyIGxpYnJhcnkgc2l6ZSBlZmZlY3QgdGhhdCB3ZSBzaG91bGQgdGFrZSBpbnRvIGFjY291bnQuCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBub3JtYWxpemUgdGhlIGNvdW50IGRhdGEgdXNpbmcgdGhlIHByZXZpb3VzbHkgY29tcHV0ZWQgInNpemUgZmFjdG9ycyIKYXNzYXkoc2NlLCAibm9ybWVkIikgPC0gbm9ybWFsaXplQ291bnRzKHNjZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2c9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplLmZhY3RvcnM9c2YsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHNldWRvLmNvdW50PTApCgp0YXJnZXRDZWxscyA8LSBuYW1lcyhjc1tvcmRlcihjcywgZGVjcmVhc2luZyA9IFRSVUUpXVtjKDEsMTApXSkKCk0gPC0gcm93TWVhbnMoYXNzYXlzKHNjZSkkbm9ybWVkWyx0YXJnZXRDZWxsc10pCkQgPC0gYXNzYXlzKHNjZSkkbm9ybWVkWyx0YXJnZXRDZWxsc1syXV0gLyBhc3NheXMoc2NlKSRub3JtZWRbLHRhcmdldENlbGxzWzFdXQpwbG90KHggPSBsb2coTSksIHkgPSBsb2cyKEQpLAogICAgIHBjaCA9IDE2LCBjZXg9MS8zLAogICAgIG1haW4gPSBwYXN0ZTAoIkNlbGwgIiwgdGFyZ2V0Q2VsbHNbMl0sICIgdnMgY2VsbCAiLCB0YXJnZXRDZWxsc1sxXSksCiAgICAgeGxhYiA9ICJMb2cgbWVhbiIsIHlsYWIgPSAiTG9nMiBmb2xkLWNoYW5nZSIsCiAgICAgYnR5ID0gJ2wnKQphYmxpbmUoaCA9IDAsIGNvbD0ib3JhbmdlIiwgbHdkPTIpCgp0YXJnZXRDZWxscyA8LSBuYW1lcyhjc1tvcmRlcihjcywgZGVjcmVhc2luZyA9IFRSVUUpXVtjKDEsMTAwKV0pCgpNIDwtIHJvd01lYW5zKGFzc2F5cyhzY2UpJG5vcm1lZFssdGFyZ2V0Q2VsbHNdKQpEIDwtIGFzc2F5cyhzY2UpJG5vcm1lZFssdGFyZ2V0Q2VsbHNbMl1dIC8gYXNzYXlzKHNjZSkkbm9ybWVkWyx0YXJnZXRDZWxsc1sxXV0KcGxvdCh4ID0gbG9nKE0pLCB5ID0gbG9nMihEKSwKICAgICBwY2ggPSAxNiwgY2V4PTEvMywKICAgICBtYWluID0gcGFzdGUwKCJDZWxsICIsIHRhcmdldENlbGxzWzJdLCAiIHZzIGNlbGwgIiwgdGFyZ2V0Q2VsbHNbMV0pLAogICAgIHhsYWIgPSAiTG9nIG1lYW4iLCB5bGFiID0gIkxvZzIgZm9sZC1jaGFuZ2UiLAogICAgIGJ0eSA9ICdsJykKYWJsaW5lKGggPSAwLCBjb2w9Im9yYW5nZSIsIGx3ZD0yKQpgYGAKCgojIEZlYXR1cmUgc2VsZWN0aW9uCgojIyBIaWdobHkgdmFyaWFibGUgZ2VuZXMKCmBgYHtyLCBldmFsPUZBTFNFfQpsaWJyYXJ5KHNjcmFuKQpyb3duYW1lcyhzY2UpIDwtIHJvd0RhdGEoc2NlKSRFbnNlbWJsCmRlYyA8LSBtb2RlbEdlbmVWYXIoc2NlKQpoZWFkKGRlYykKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KZml0IDwtIG1ldGFkYXRhKGRlYykKcGxvdChmaXQkbWVhbiwgZml0JHZhciwgCiAgICAgeGxhYj0iTWVhbiBvZiBsb2ctZXhwcmVzc2lvbiIsCiAgICB5bGFiPSJWYXJpYW5jZSBvZiBsb2ctZXhwcmVzc2lvbiIpCmN1cnZlKGZpdCR0cmVuZCh4KSwgY29sPSJkb2RnZXJibHVlIiwgYWRkPVRSVUUsIGx3ZD0yKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQojIGdldCAxMCUgaGlnaGx5IHZhcmlhYmxlIGdlbmVzCmh2ZyA8LSBnZXRUb3BIVkdzKGRlYywgCiAgICAgICAgICAgICAgICAgIHByb3A9MC4xKQpoZWFkKGh2ZykKCiMgcGxvdCB0aGVzZSAKcGxvdChmaXQkbWVhbiwgZml0JHZhciwgCiAgICAgY29sID0gYygib3JhbmdlIiwgImRhcmtzZWFncmVlbjMiKVsobmFtZXMoZml0JG1lYW4pICVpbiUgaHZnKSsxXSwKICAgICB4bGFiPSJNZWFuIG9mIGxvZy1leHByZXNzaW9uIiwKICAgIHlsYWI9IlZhcmlhbmNlIG9mIGxvZy1leHByZXNzaW9uIikKY3VydmUoZml0JHRyZW5kKHgpLCBjb2w9ImRvZGdlcmJsdWUiLCBhZGQ9VFJVRSwgbHdkPTIpCmxlZ2VuZCgidG9wbGVmdCIsIAogICAgICAgbGVnZW5kID0gYygiU2VsZWN0ZWQiLCAiTm90IHNlbGVjdGVkIiksIAogICAgICAgY29sID0gYygiZGFya3NlYWdyZWVuMyIsICJvcmFuZ2UiKSwKICAgICAgIHBjaCA9IDE2LAogICAgICAgYnR5PSduJykKYGBgCgojIyBIaWdoIGRldmlhbmNlIGdlbmVzCgpgYGB7ciwgZXZhbD1GQUxTRX0KI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJzY3J5IikKbGlicmFyeShzY3J5KQpzY2UgPC0gZGV2aWFuY2VGZWF0dXJlU2VsZWN0aW9uKG9iamVjdCA9IHNjZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiY291bnRzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29ydGVkID0gRkFMU0UpCgpwbG90KHNvcnQocm93RGF0YShzY2UpJGJpbm9taWFsX2RldmlhbmNlLCBkZWNyZWFzaW5nID0gVFJVRSksIAogICAgIHR5cGU9ImwiLCAKICAgICB4bGFiPSJyYW5rZWQgZ2VuZXMiLCAKICAgICB5bGFiPSJiaW5vbWlhbCBkZXZpYW5jZSIsIAogICAgIG1haW49IkZlYXR1cmUgU2VsZWN0aW9uIHdpdGggRGV2aWFuY2UiKQphYmxpbmUodj0yMDAwLCBsdHk9MiwgY29sPSJyZWQiKQpgYGAKCiMjIFNldXJhdCBWU1QKCmBgYHtyLCBldmFsPUZBTFNFfQpsaWJyYXJ5KFNldXJhdCkKcm93bmFtZXMoc2NlKSA8LSByb3dEYXRhKHNjZSkkRW5zZW1ibApzZXVyYXRfb2JqIDwtIGFzLlNldXJhdChzY2UpCnNldXJhdF9vYmogIyBub3RpY2UgdGhlICIwIHZhcmlhYmxlIGZlYXR1cmVzIgpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpzZXVyYXRfb2JqIDwtIFNldXJhdDo6Tm9ybWFsaXplRGF0YShzZXVyYXRfb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlLmZhY3RvciA9IDEwMDAwKQoKc2V1cmF0X29iaiA8LSAgRmluZFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0gc2V1cmF0X29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpzZXVyYXRfb2JqICAjIG5vdGljZSB0aGUgIjIwMDAgdmFyaWFibGUgZmVhdHVyZXMiIChkZWZhdWx0KQpoZWFkKFZhcmlhYmxlRmVhdHVyZXMoc2V1cmF0X29iaikpICMgaGVyZSB0aGV5IGFyZQpybShzZXVyYXRfb2JqKQpgYGAKCiMgRGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uCgojIyBUaGUgbW9zdCBiYXNpYyBEUgoKYGBge3IsIGV2YWw9RkFMU0V9CmNvbERhdGEoc2NlKSRkYXkgPC0gYXMuZmFjdG9yKGNvbERhdGEoc2NlKSRkYXkpCmRheSA8LSBjb2xEYXRhKHNjZSkkZGF5CnBhcihidHk9J2wnKQpwbG90KHggPSBhc3NheXMoc2NlKSRjb3VudHNbaHZnWzFdLF0sCiAgICAgeSA9IGFzc2F5cyhzY2UpJGNvdW50c1todmdbMl0sXSwKICAgICBjb2wgPSBhcy5udW1lcmljKGRheSksCiAgICAgcGNoID0gMTYsIGNleCA9IDEvMywKICAgICB4bGFiID0gIk1vc3QgaW5mb3JtYXRpdmUgZ2VuZSIsCiAgICAgeWxhYiA9ICJTZWNvbmQgbW9zdCBpbmZvcm1hdGl2ZSBnZW5lIiwKICAgICBtYWluID0gIkNlbGxzIGNvbG9yZWQgYWNjIHRvIGNlbGwgdHlwZSIpCmBgYAoKSnVzdCBieSBsb29raW5nIGF0IHRoZSB0b3AgdHdvIGdlbmVzIGJhc2VkIG9uIG91ciBmZWF0dXJlIHNlbGVjdGlvbiBjcml0ZXJpb24sIAp3ZSBjYW4gYWxyZWFkeSBzZWUgc29tZSBzZXBhcmF0aW9uIGFjY29yZGluZyB0byB0aGUgY2VsbCB0eXBlOyB0aGUgY2VsbHMgCmNvbG9yZWQgcmVkIHNlZW0gdG8gaGF2ZSBsb3cgZXhwcmVzc2lvbiB2YWx1ZXMgZm9yIGJvdGggZ2VuZXMsIGFuZCBhcmUgdGhvc2UKc29sZWx5IHJldGlyZXZlZCBpbiB0aGUgYm90dG9tIGxlZnQgb2YgdGhlIHgteSBwbGFuZS4KCiMjIExpbmVhciBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb246IFBDQQoKIyMjIFBDQSB3aXRoIGZlYXR1cmUgc2VsZWN0aW9uCgpgYGB7ciwgZXZhbD1GQUxTRX0Kc2V0LnNlZWQoMTIzNCkKc2NlIDwtIHJ1blBDQShzY2UsIAogICAgICAgICAgICAgIG5jb21wb25lbnRzPTMwLCAKICAgICAgICAgICAgICBzdWJzZXRfcm93PWh2ZykKYGBgCgpQQ0EgaGFzIGJlZW4gcGVyZm9ybWVkLiBUaGUgUENBIGluZm9ybWF0aW9uIGhhcyBiZWVuIGF1dG9tYXRpY2FsbHkgc3RvcmVkIGluIHRoZQoqcmVkdWNlZERpbSogc2xvdCBvZiB0aGUgU2luZ2xlQ2VsbEV4cGVyaW1lbnQgb2JqZWN0LgoKYGBge3IsIGV2YWw9RkFMU0V9CnJlZHVjZWREaW1OYW1lcyhzY2UpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CmhlYWQocmVkdWNlZERpbShzY2UsCiAgICAgICAgICAgdHlwZT0iUENBIikpCmBgYAoKVGhlIGBwbG90UENBYCBmdW5jdGlvbiBvZiB0aGUgYHNjYXRlcmAgcGFja2FnZSBub3cgYWxsb3dzIHVzIHRvIHZpc3VhbGl6ZQp0aGUgY2VsbHMgaW4gUENBIHNwYWNlLCBiYXNlZCBvbiB0aGUgUENBIGluZm9ybWF0aW9uIHN0b3JlZCBpbiBvdXIgb2JqZWN0OgoKYGBge3IsIGV2YWw9RkFMU0V9CnBsb3RQQ0Eoc2NlLCAKICAgICAgICBjb2xvdXJfYnkgPSAiZGF5IikKYGBgCgpXZSBvYnNlcnZlIGEgY2xlYXItY3V0IGNsdXN0ZXJpbmcgb2YgdGhlIGNlbGxzIQoKYGBge3IsIGV2YWw9RkFMU0V9CnBlcmNlbnQudmFyIDwtIGF0dHIocmVkdWNlZERpbShzY2UpLCAicGVyY2VudFZhciIpCnBsb3QocGVyY2VudC52YXIsIGxvZz0ieSIsIHhsYWI9IlBDIiwgeWxhYj0iVmFyaWFuY2UgZXhwbGFpbmVkICglKSIpCmBgYAoKSGVyZSwgcmV0YWluaW5nIMKxMTVQQ3Mgc2VlbXMgcmVhc29uYWJsZS4gSWYgeW91IHJlYWxseSBwcmVmZXIgYSBtb3JlIGRhdGEtZHJpdmVuCndheSBmb3IgZGV0ZXJtaW5pbmcgdGhpcywgCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KFBDQXRvb2xzKQpjaG9zZW4uZWxib3cgPC0gZmluZEVsYm93UG9pbnQocGVyY2VudC52YXIpCmNob3Nlbi5lbGJvdwpgYGAKCiMjIyBQQ0Egd2l0aG91dCBmZWF0dXJlIHNlbGVjdGlvbgoKYGBge3IsIGV2YWw9RkFMU0V9CnNldC5zZWVkKDEyMzQpCnNjZU5vRlMgPC0gcnVuUENBKHNjZSwgCiAgICAgICAgICAgICAgICAgIG5jb21wb25lbnRzPTMwLCAKICAgICAgICAgICAgICAgICAgc3Vic2V0X3Jvdz0xOm5yb3coc2NlKSkKcGxvdFBDQShzY2VOb0ZTLCBjb2xvdXJfYnkgPSAiZGF5IikKcm0oc2NlTm9GUykKYGBgCgpXaGlsZSB3ZSB1c2UgbW9yZSBpbmZvcm1hdGlvbiB0byBtYWtlIHRoaXMgUENBIHBsb3QgKDEwLjM3NCBnZW5lcykgYXMgY29tcGFyZWQKdG8gdGhlIGZlYXR1cmUgc2VsZWN0ZWQgUENBIHBsb3QgKDUwNiBnZW5lcyksIHdlIHNlZW0gdG8gcmV0cmlldmUgbGVzcyBzdHJ1Y3R1cmUKaW4gdGhlIGRhdGEuIEluZGVlZCwgd2UgYXJlIGhlcmUgbm90IGFibGUgdG8gZGlzdGluZ3Vpc2ggYmV0d2VlbiBkYXkyIGFuZCBkYXkzCmNlbGxzLiBUaGlzIGlzIHRoZSBwb3dlciBvZiBmZWF0dXJlIHNlbGVjdGlvbiwgYW4gaW5jcmVhc2UgaW4gdGhlCnNpZ25hbC10by1ub2lzZSByYXRpbyEKCiMjIEEgZ2VuZXJhbGl6YXRpb24gb2YgUENBIGZvciBleHBvbmVudGlhbCBmYW1pbHkgZGlzdHJpYnV0aW9ucy4KCmBgYHtyLCBldmFsPUZBTFNFfQpsaWJyYXJ5KGdsbXBjYSkKc2V0LnNlZWQoMjExMTAzKQpwb2lwY2EgPC0gZ2xtcGNhKFkgPSBhc3NheXMoc2NlKSRjb3VudHNbaHZnLF0sCiAgICAgICAgICAgICAgICAgTCA9IDIsIAogICAgICAgICAgICAgICAgIGZhbSA9ICJwb2kiLAogICAgICAgICAgICAgICAgIG1pbmliYXRjaCA9ICJzdG9jaGFzdGljIikKcmVkdWNlZERpbShzY2UsICJQb2lQQ0EiKSA8LSBwb2lwY2EkZmFjdG9ycwpwbG90UmVkdWNlZERpbShzY2UsIAogICAgICAgICAgICAgICBkaW1yZWQ9IlBvaVBDQSIsCiAgICAgICAgICAgICAgIGNvbG91cl9ieSA9ICJkYXkiKQpgYGAKCiMjIE5vbi1saW5lYXIgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uOiBULVNORQoKYGBge3IsIGV2YWw9RkFMU0V9CnNldC5zZWVkKDU2NDY1NCkKc2NlIDwtIHJ1blRTTkUoc2NlLCAKICAgICAgICAgICAgICAgZGltcmVkID0gJ1BDQScsCiAgICAgICAgICAgICAgIGV4dGVybmFsX25laWdoYm9ycz1UUlVFKQpwbG90VFNORShzY2UsCiAgICAgICAgIGNvbG91cl9ieSA9ICJkYXkiKQoKcGxvdFRTTkUoc2NlLAogICAgICAgICBjb2xvdXJfYnkgPSAiZG9ub3IiKQoKcGxvdFRTTkUoc2NlLAogICAgICAgICBjb2xvdXJfYnkgPSAiZXhwZXJpbWVudCIpCmBgYAoKIyMgTm9uLWxpbmVhciBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb246IFVNQVAKCmBgYHtyLCBldmFsPUZBTFNFfQpzZXQuc2VlZCg2NTE4NykKc2NlIDwtIHJ1blVNQVAoc2NlLCAKICAgICAgICAgICAgICAgZGltcmVkID0gJ1BDQScsIAogICAgICAgICAgICAgICBleHRlcm5hbF9uZWlnaGJvcnMgPSBUUlVFKQpwbG90VU1BUChzY2UsCiAgICAgICAgIGNvbG91cl9ieSA9ICJkYXkiKQpwbG90VU1BUChzY2UsCiAgICAgICAgIGNvbG91cl9ieSA9ICJkb25vciIpCnBsb3RVTUFQKHNjZSwKICAgICAgICAgY29sb3VyX2J5ID0gImV4cGVyaW1lbnQiKQpgYGAKCg==