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.
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.
Import data
First we read in the count matrix:
library(SingleCellExperiment)
sce <- readRDS("/Users/jg/Desktop/sce_15_cuomo.rds") # change to YOUR path!
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:
To improve our gene-level information, we may:
Split V1
into two columns, one with the ENSEMBL ID and the other with the gene symbol.
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
Quality control
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)
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")
QC using adaptive thresholds
Below, we remove cells that are outlying with respect to
- A low sequencing depth (number of UMIs);
- A low number of genes detected;
- 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]
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.
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.
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)
Feature selection
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')
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")
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)
Dimensionality reduction
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.
Linear dimensionality reduction: PCA
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.
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
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!
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")
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")
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==