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)
download.file()
sce <- readRDS("/Users/jg/Desktop/sce_15_cuomo.rds")
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,]

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 —


9 Normalization (continued)

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. For instance, when inspecting these metadata

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

we may select cells from the same biological population as follows:

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

Next, we may assess the effect of normalization like we did previously for the Macosko dataset.

10 Feature selection

rownames(sce) <- rowData(sce)$Ensembl # will be more convenient downstream

10.1 Highly variable genes

Obtain the top 10% of genes that have a high variance with respect to their mean.

10.2 High deviance genes

Obtain the top n genes that have a high deviance statistic.

10.3 Seurat VST

Use Seurat to obtain highly variable genes according to the VST procedure.

11 Dimensionality reduction

11.1 The most basic DR

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.

11.2 Linear dimensionality reduction: PCA

11.2.1 PCA with feature selection

You should already observe a clear-cut clustering of the cells!

11.3 A generalization of PCA for exponential family distributions

11.4 Non-linear dimensionality reduction: T-SNE

Create a t-SNE based on the first 30 components of the PCA. Evaluate the resulting 2D plot very carefully! Color the cells based on the day variable (proxy for cell type), but also on other relevant metadata. What do you observe?

11.5 Non-linear dimensionality reduction: UMAP

Create a UMAP based on the first 30 components of the PCA. Evaluate the resulting 2D plot very carefully! Color the cells based on the day variable (proxy for cell type), but also on other relevant metadata. What do you observe?

LS0tCnRpdGxlOiAnTm9ybWFsaXphdGlvbiwgZmVhdHVyZSBzZWxlY3Rpb24gYW5kIGRpbWVuc2lvbiByZWR1Y3Rpb24gZm9yIHRoZSBDdW9tbyBkYXRhc2V0JwphdXRob3I6ICJLb2VuIFZhbiBkZW4gQmVyZ2UgYW5kIEplcm9lbiBHaWxpcyIKZGF0ZTogIjMwLzExLzIwMjEiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKIyBJbnRyb2R1Y3Rpb24KCldlIGhlcmUgbWFrZSB1c2Ugb2YgdGhlIHB1YmxpY2F0aW9uIG9mIEFubmEgQ3VvbW8gZXQgYWwuCihsYXN0IGF1dGhvciBPbGl2ZXIgU3RlZ2xlKSwgd2hpY2ggd2Ugd2lsbCByZWZlciB0byBhcyB0aGUgYGlQU0MgZGF0YXNldGAuIFRoZSAKcGFwZXIgdGhhdCBkZXNjcmliZXMgdGhpcyBkYXRhc2V0IGNhbiBiZSBmb3VuZCB1c2luZyB0aGlzIApbbGlua10oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE0NjctMDIwLTE0NDU3LXopLgoKSW4gdGhlIGV4cGVyaW1lbnQsIHRoZSBhdXRob3JzIGhhcnZlc3RlZCBpbmR1Y2VkIHBsdXJpcG90ZW50IHN0ZW0gY2VsbHMgKGlQU0NzKQpmcm9tIDEyNSBoZWFsdGh5IGh1bWFuIGRvbm9ycy4gVGhlc2UgY2VsbHMgd2VyZSB1c2VkIHRvIHN0dWR5IHRoZSBlbmRvZGVybSAKZGlmZmVyZW50aWF0aW9uIHByb2Nlc3MuIEluIHRoaXMgcHJvY2VzcywgaVBTQ3MgZGlmZmVyZW50aWF0ZSB0byBlbmRvZGVybSBjZWxscywKYSBwcm9jZXNzIHdoaWNoIHRha2VzIGFwcHJveGltYXRlbHkgdGhyZWUgZGF5cy4gQXMgc3VjaCwgdGhlIGF1dGhvcnMgCmN1bHRlcmVkIHRoZSBpUFNDcyBjZWxsIGxpbmVzIGFuZCBhbGxvd2VkIGZvciBkaWZmZXJlbnRpYXRpb24gZm9yIHRocmVlIGRheXMuIApEdXJpbmcgdGhlIGV4cGVyaW1lbnQsIGNlbGxzIHdlcmUgaGFydmVzdGVkIGF0IGZvdXIgZGlmZmVyZW50IHRpbWUgcG9pbnRzOiAKZGF5MCAoZGlyZWN0bHkgYXQgdG8gaW5jdWJhdGlvbiksIGRheTEsIGRheTIgYW5kIGRheTMuIEtub3dpbmcgdGhlIHByb2Nlc3Mgb2YgCmVuZG9kZXJtIGRpZmZlcmVudGlhdGlvbiwgdGhlc2UgdGltZSBwb2ludHMgc2hvdWxkIGNvcnJlc3BvbmQgd2l0aCBkaWZmZXJlbnQgCmNlbGwgdHlwZXM6IGRheTAgYXJlICh1bmRpZmZlcmVudGlhdGVkKSBpUFNDcywgZGF5MSBhcmUgbWVzZW5kb2Rlcm0gY2VsbHMsIGRheTIKYXJlICJpbnRlcm1lZGlhdGUiIGNlbGxzIGFuZCBkYXkzIGFyZSBmdWxseSBkaWZmZXJlbnRpYXRlZCBlbmRvZGVybSBjZWxscy4KClRoaXMgZGF0YXNldCB3YXMgZ2VuZXJhdGVkIHVzaW5nIHRoZSAqKlNNQVJULVNlcTIqKiBzY1JOQS1zZXEgcHJvdG9jb2wuCgpUaGUgZmluYWwgZ29hbCBvZiB0aGUgZXhwZXJpbWVudCB3YXMgdG8gY2hhcmFjdGVyaXplIHBvcHVsYXRpb24gdmFyaWF0aW9uIGluIHRoZQpwcm9jZXNzIG9mIGVuZG9kZXJtIGRpZmZlcmVudGlhdGlvbi4KCiMgRG93bmxvYWQgZGF0YQoKRm9yIHRoaXMgbGFiIHNlc3Npb24sIHdlIHdpbGwgd29yayB3aXRoIGEgc3Vic2V0IG9mIHRoZSBkYXRhLCBpLmUuLCB0aGUgZGF0YQpmb3IgdGhlIGZpcnN0IChhbHBoYWJldGljYWxseSkgMTUgcGF0aWVudHMgaW4gdGhlIGV4cGVyaW1lbnQuIFRoZXNlIGNhbiBiZQpkb3dubG9hZGVkIHRocm91Z2ggdGhlICpiZWxuZXQgZmlsZXNlbmRlciogbGluayBwcm92aWRlZCB0aHJvdWdoIGVtYWlsLApodHRwczovL2ZpbGVzZW5kZXIuYmVsbmV0LmJlLz9zPWRvd25sb2FkJnRva2VuPWViODEzNmRmLTY3ZDMtNDg2OS1iMmE5LWY2NTc2NzA1NGU4MS4KClRoZSBkYXRhIG9yaWdpbmFsICgxMjUgcGF0aWVudCkgY291bGQgYmUgZG93bmxvYWRlZCBmcm9tIApbWmVub2RvXShodHRwczovL3plbm9kby5vcmcvcmVjb3JkLzM2MjUwMjQjLllXZmFodGxCeEIxKS4gQXQgdGhlIGJvdHRvbSBvZiB0aGlzCndlYi1wYWdlLCB3ZSBjYW4gZG93bmxvYWQgdGhlIGZpbGVzIGByYXdfY291bnRzLmNzdi56aXBgIGFuZCAKYGNlbGxfbWV0YWRhdGFfY29scy50c3ZgIGFuZCBzdG9yZSB0aGVzZSBmaWxlcyBsb2NhbGx5LiBXZSBkbyBub3QgcmVjb21tZW5kIApkb2luZyB0aGlzIGR1cmluZyB0aGUgbGFiIHNlc3Npb24sIHRvIGF2b2lkIG92ZXJsb2FkaW5nIHRoZSBzeXN0ZW0uCgojIEltcG9ydCBkYXRhCgpGaXJzdCB3ZSByZWFkIGluIHRoZSBjb3VudCBtYXRyaXg6CgpgYGB7cixldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KFNpbmdsZUNlbGxFeHBlcmltZW50KQpkb3dubG9hZC5maWxlKCkKc2NlIDwtIHJlYWRSRFMoIi9Vc2Vycy9qZy9EZXNrdG9wL3NjZV8xNV9jdW9tby5yZHMiKQpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CnNjZQpgYGAKCiMgRXhwbG9yZSBtZXRhZGF0YQoKRXhwbG9yYXRpb24gb2YgdGhlIG1ldGFkYXRhIGlzIGVzc2VudGlhbCB0byBnZXQgYSBiZXR0ZXIgaWRlYSBvZiB3aGF0IHRoZQpleHBlcmltZW50IHdhcyBhYm91dCBhbmQgaG93IGl0IHdhcyBvcmdhbml6ZWQuCgpgYGB7cixldmFsPUZBTFNFfQpjb2xEYXRhKHNjZSlbMTo1LDE6MTBdCmNvbG5hbWVzKGNvbERhdGEoc2NlKSkKYGBgCgpBcyBzdGF0ZWQgaW4gdGhlIHBhcGVyLCBjZWxscyB3ZXJlIHNhbXBsZWQgb24gNCB0aW1lIHBvaW50cy4gRWFjaCBvZiB0aGVzZSAKdGltZSBwb2ludHMgaXMgZXhwZWN0ZWQgdG8gY29ycmVzcG9uZCB3aXRoIGRpZmZlcmVudCBjZWxsIHR5cGVzIChkYXkwID0gaVBTQywKZGF5MSA9IG1lc2VuZG9kZXJtLCBkYXkyID0gaW50ZXJtZWRpYXRlIGFuZCBkYXkzID0gZW5kb2Rlcm0pLgoKYGBge3IsZXZhbD1GQUxTRX0KdGFibGUoY29sRGF0YShzY2UpJGRheSkKYGBgCgpBcyBzdGF0ZWQgaW4gdGhlIHBhcGVyLCBjZWxscyB3ZXJlIGhhcnZlc3RlZCBmcm9tIDEyNSBwYXRpZW50cy4gSGVyZSwgd2UgYXJlCndvcmtpbmcgb24gYSBzdWJzZXQgd2l0aCAxNSBwYXRpZW50cy4gVGhlIG51bWJlciBvZiBjZWxscyBoYXJ2ZXN0ZWQgcGVyIHBhdGllbnQgCihvdmVyIGFsbCB0aW1lIHBvaW50cykgcmFuZ2VzIGZyb20gMzEgdG8gNjM3LgoKYGBge3IsZXZhbD1GQUxTRX0KbGVuZ3RoKHRhYmxlKGNvbERhdGEoc2NlKSRkb25vcikpICMgbnVtYmVyIG9mIGRvbm9ycwpyYW5nZSh0YWJsZShjb2xEYXRhKHNjZSkkZG9ub3IpKSAjIGNlbGxzIHBlciBkb25vcgpgYGAKCkJlbG93LCB3ZSBsb29rIGhvdyBtYW55IGNlbGxzIGFyZSBoYXJ2ZXN0IHBlciBwYXRlbnQgYW5kIHBlciB0aW1lIHBvaW50LgoKYGBge3IsZXZhbD1GQUxTRX0KdGFibGUoY29sRGF0YShzY2UpJGRvbm9yLGNvbERhdGEoc2NlKSRkYXkpCmBgYAoKV2Ugc2VlIHRoYXQgZm9yIG1hbnkgcGF0aWVudHMgdGhlIGRhdGEgaXMgY29tcGxldGUsIGkuZS4gY2VsbHMgd2VyZSBzYW1wbGVkCm9uIGFsbCB0aW1lIHBvaW50cy4KClByYWN0aWNhbGx5LCB0aGUgY2VsbHMgd2VyZSBwcmVwYXJlZCBpbiAyOCBiYXRjaGVzLiBTaW5jZSB3ZSBoZXJlIG9ubHkgbG9vawphdCBhIHN1YnNldCBvZiB0aGUgZGF0YSwgd2Ugc2VlIHRoYXQgb25seSAxNCBvZiB0aGVzZSBiYXRjaGVzIGFyZSByZXByZXNlbnRlZCAKaGVyZS4KCmBgYHtyLGV2YWw9RkFMU0V9Cmxlbmd0aCh0YWJsZShjb2xEYXRhKHNjZSkkZXhwZXJpbWVudCkpCnRhYmxlKGNvbERhdGEoc2NlKSRleHBlcmltZW50LCBjb2xEYXRhKHNjZSkkZGF5KQpgYGAKCiMgT2J0YWluaW5nIGFuZCBpbmNsdWRpbmcgcm93RGF0YQoKVGhlIGByb3dEYXRhYCBzbG90IG9mIGEgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3QgYWxsb3dzIGZvciBzdG9yaW5nIAppbmZvcm1hdGlvbiBvbiB0aGUgZmVhdHVyZXMsIGkuZS4gdGhlIGdlbmVzLCBpbiBhIGRhdGFzZXQuIEluIG91ciBvYmplY3QsCnRoZSBgcm93RGF0YWAgc2xvdCBjdXJyZW50bHkgY29udGFpbnMgdGhlIGZvbGxvd2luZzoKCmBgYHtyLGV2YWw9RkFMU0V9CmhlYWQocm93RGF0YShzY2UpKQpgYGAKClRvIGltcHJvdmUgb3VyIGdlbmUtbGV2ZWwgaW5mb3JtYXRpb24sIHdlIG1heToKCjEuIFNwbGl0IGBWMWAgaW50byB0d28gY29sdW1ucywgb25lIHdpdGggdGhlIEVOU0VNQkwgSUQgYW5kIHRoZSBvdGhlciB3aXRoIAp0aGUgZ2VuZSBzeW1ib2wuCgoyLiBEaXNwbGF5IHdoaWNoIGNocm9tb3NvbWUgdGhlIGdlbmUgaXMgbG9jYXRlZAoKTWFueSBtb3JlIG9wdGlvbnMgYXJlIHBvc3NpYmxlLCBidXQgYXJlIG5vdCBuZWNlc3NhcnkgZm9yIHVzIHJpZ2h0IG5vdy4KCmBgYHtyLGV2YWw9RkFMU0V9CnJvd0RhdGEoc2NlKSA8LSBkYXRhLmZyYW1lKEVuc2VtYmwgPSBnc3ViKCJfLioiLCAiIiwgcm93RGF0YShzY2UpJFYxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgU3ltYm9sID0gZ3N1YigiXlteX10qXyIsICIiLCByb3dEYXRhKHNjZSkkVjEpKQpoZWFkKHJvd0RhdGEoc2NlKSkKYGBgCgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsZXZhbD1GQUxTRX0KbGlicmFyeSgiYmlvbWFSdCIpCmVuc2VtYmw3NSA8LSB1c2VFbnNlbWJsKGJpb21hcnQgPSAnZ2VuZXMnLCAKICAgICAgICAgICAgICAgICAgICAgICAgZGF0YXNldCA9ICdoc2FwaWVuc19nZW5lX2Vuc2VtYmwnLAogICAgICAgICAgICAgICAgICAgICAgICB2ZXJzaW9uID0gNzUpCgpHZW5lSW5mbyA8LSBnZXRCTShhdHRyaWJ1dGVzID0gYygiZW5zZW1ibF9nZW5lX2lkIiwgIyBUbyBtYXRjaCB3aXRoIHJvd25hbWVzIFNDRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY2hyb21vc29tZV9uYW1lIiksICMgSW5mbyBvbiBjaHJvbW9zZQogICAgICAgICAgICAgICAgICBtYXJ0ID0gZW5zZW1ibDc1KQpHZW5lSW5mbyA8LSBHZW5lSW5mb1ttYXRjaChyb3dEYXRhKHNjZSkkRW5zZW1ibCwgR2VuZUluZm8kZW5zZW1ibF9nZW5lX2lkKSxdCgpyb3dEYXRhKHNjZSkgPC0gY2JpbmQocm93RGF0YShzY2UpLCBHZW5lSW5mbykKaGVhZChyb3dEYXRhKHNjZSkpCmFsbChyb3dEYXRhKHNjZSkkRW5zZW1ibCA9PSByb3dEYXRhKHNjZSkkZW5zZW1ibF9nZW5lX2lkKSAKIyBpZGVudGljYWwsIGFzIGRlc2lyZWQsIHNvIHdlIGNvdWxkIG9wdGlvbmFsbHkgcmVtb3ZlIG9uZSBvZiB0aGUgdHdvCmBgYAoKIyBGaWx0ZXJpbmcgbm9uLWluZm9ybWF0aXZlIGdlbmVzCgpMZXQgdXMgZmlyc3QgdHJ5IHRoZSB2ZXJ5IHNpbXBsZSBhbmQgdmVyeSBsZW5pZW50IGZpbHRlcmluZyBjcml0ZXJpb24gdGhhdCB3ZQphZG9wdGVkIGZvciB0aGUgTWFjb3NrbyBkYXRhc2V0LgoKYGBge3IsZXZhbD1GQUxTRX0Ka2VlcCA8LSByb3dTdW1zKGFzc2F5cyhzY2UpJGNvdW50cyA+IDApID4gMTAKdGFibGUoa2VlcCkKCnNjZSA8LSBzY2Vba2VlcCxdCmBgYAoKV2Ugc2VlIHRoYXQgdGhpcyBmaWx0ZXJpbmcgc3RyYXRlZ3kgZG9lcyBub3QgcmVtb3ZlIGFueSBnZW5lcyBmb3IgdGhpcyBkYXRhc2V0LgpJbiBnZW5lcmFsLCBkYXRhc2V0cyBmcm9tIHBsYXRlLWJhc2VkIHNjUk5BLXNlcSBkYXRhc2V0IGhhdmUgYSBmYXIgaGlnaGVyCnNlcXVlbmNpbmcgZGVwdGggdGhhbiBkYXRhIGZyb20gZHJvcGxldC1iYXNlZCBwcm90b2NvbHMuIEFzIHJlcXVpcmluZyBhIG1pbmltdW0KZXhwcmVzc2lvbiBvZiAxIGNvdW50IGluIGF0IGxlYXN0IDEwIGNlbGxzIGlzIGEgdmVyeSBsZW5pZW50IGNyaXRlcmlvbiBpZiB3ZSAKY29uc2lkZXIgdGhhdCB3ZSBoYXZlIDM2LjAwMCBjZWxscywgd2Ugc2hvdWxkIGNvbnNpZGVyIGFkb3B0aW5nIGEgbW9yZSBzdHJpbmdlbnQKZmlsdGVyaW5nIGNyaXRlcml1bSwgbGlrZSB0aGUgYGZpbHRlckJ5RXhwcmAgZnJvbSBgZWRnZVJgOgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsZXZhbD1GQUxTRX0KbGlicmFyeShlZGdlUikKCnRhYmxlKGNvbERhdGEoc2NlKSRkYXkpCgprZWVwMiA8LSBlZGdlUjo6ZmlsdGVyQnlFeHByKHk9c2NlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gY29sRGF0YShzY2UpJGRheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uY291bnQgPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5wcm9wID0gMC40KQp0YWJsZShrZWVwMikKCnNjZSA8LSBzY2Vba2VlcDIsXQpgYGAKCiMgUXVhbGl0eSBjb250cm9sCgojIyBDYWxjdWxhdGUgUUMgdmFyaWFibGVzCgpgYGB7cixldmFsPUZBTFNFfQpsaWJyYXJ5KHNjYXRlcikKCiMgY2hlY2sgRVJDQyBzcGlrZS1pbiB0cmFuc2NyaXB0cwpzdW0oZ3JlcGwoIl5FUkNDLSIsIHJvd0RhdGEoc2NlKSRTeW1ib2wpKSAjIG5vIHNwaWtlLWluIHRyYW5zY3JpcHRzIGF2YWlsYWJsZQoKaXMubWl0byA8LSBncmVwbCgiXk1UIiwgcm93RGF0YShzY2UpJGNocm9tb3NvbWVfbmFtZSkKc3VtKGlzLm1pdG8pICMgMTMgbWl0b2Nob25kcmlhbCBnZW5lcwoKZGYgPC0gcGVyQ2VsbFFDTWV0cmljcyhzY2UsIHN1YnNldHM9bGlzdChNaXRvPWlzLm1pdG8pKQpoZWFkKGRmKQoKIyMgYWRkIHRoZSBRQyB2YXJpYWJsZXMgdG8gc2NlIG9iamVjdApjb2xEYXRhKHNjZSkgPC0gY2JpbmQoY29sRGF0YShzY2UpLCBkZikKYGBgCgojIyBFeHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzCgpJbiB0aGUgZmlndXJlIGJlbG93LCB3ZSBzZWUgdGhhdCBzZXZlcmFsIGNlbGxzIGhhdmUgYSB2ZXJ5IGxvdyBudW1iZXIgb2YgCmV4cHJlc3NlZCBnZW5lcywgYW5kIHdoZXJlIG1vc3Qgb2YgdGhlIG1vbGVjdWxlcyBhcmUgZGVyaXZlZCBmcm9tIAptaXRvY2hvbmRyaWFsIGdlbmVzLiBUaGlzIGluZGljYXRlcyBsaWtlbHkgZGFtYWdlZCBjZWxscywgcHJlc3VtYWJseSBiZWNhdXNlIApvZiBsb3NzIG9mIGN5dG9wbGFzbWljIFJOQSBmcm9tIHBlcmZvcmF0ZWQgY2VsbHMsIHNvIHdlIHNob3VsZCByZW1vdmUgdGhlc2UgZm9yIAp0aGUgZG93bnN0cmVhbSBhbmFseXNpcy4KCmBgYHtyLGV2YWw9RkFMU0V9CiMgTnVtYmVyIG9mIGdlbmVzIHZzIGxpYnJhcnkgc2l6ZQpwbG90Q29sRGF0YShzY2UsIHggPSAic3VtIiwgeT0iZGV0ZWN0ZWQiLCBjb2xvdXJfYnk9ImRheSIpIAoKIyBNaXRvY2hvbmRyaWFsIGdlbmVzCnBsb3RDb2xEYXRhKHNjZSwgeCA9ICJkZXRlY3RlZCIsIHk9InN1YnNldHNfTWl0b19wZXJjZW50IiwgY29sb3VyX2J5PSJkYXkiKQpgYGAKCiMjIFFDIHVzaW5nIGFkYXB0aXZlIHRocmVzaG9sZHMKCkJlbG93LCB3ZSByZW1vdmUgY2VsbHMgdGhhdCBhcmUgb3V0bHlpbmcgd2l0aCByZXNwZWN0IHRvCgogMS4gQSBsb3cgc2VxdWVuY2luZyBkZXB0aCAobnVtYmVyIG9mIFVNSXMpOwogMi4gQSBsb3cgbnVtYmVyIG9mIGdlbmVzIGRldGVjdGVkOwogMy4gQSBoaWdoIHBlcmNlbnRhZ2Ugb2YgcmVhZHMgZnJvbSBtaXRvY2hvbmRyaWFsIGdlbmVzLgogCldlIHJlbW92ZSBhIHRvdGFsIG9mICQzMDEkIGNlbGxzLCBtYWlubHkgZHVlIHRvIGxvdyBzZXF1ZW5jaW5nIGRlcHRoIGFuZApsb3cgbnVtYmVyIG9mIGdlbmVzIGRldGVjdGVkLgoKYGBge3IsZXZhbD1GQUxTRX0KbG93TGliIDwtIGlzT3V0bGllcihkZiRzdW0sIHR5cGU9Imxvd2VyIiwgbG9nPVRSVUUpCmxvd0ZlYXR1cmVzIDwtIGlzT3V0bGllcihkZiRkZXRlY3RlZCwgdHlwZT0ibG93ZXIiLCBsb2c9VFJVRSkKaGlnaE1pdG8gPC0gaXNPdXRsaWVyKGRmJHN1YnNldHNfTWl0b19wZXJjZW50LCB0eXBlPSJoaWdoZXIiKQoKdGFibGUobG93TGliKQp0YWJsZShsb3dGZWF0dXJlcykKdGFibGUoaGlnaE1pdG8pCgpkaXNjYXJkQ2VsbHMgPC0gKGxvd0xpYiB8IGxvd0ZlYXR1cmVzIHwgaGlnaE1pdG8pCnRhYmxlKGRpc2NhcmRDZWxscykKY29sRGF0YShzY2UpJGRpc2NhcmRDZWxscyA8LSBkaXNjYXJkQ2VsbHMKCiMgdmlzdWFsaXplIGNlbGxzIHRvIGJlIHJlbW92ZWQKcGxvdENvbERhdGEoc2NlLCB4ID0gImRldGVjdGVkIiwgeT0ic3Vic2V0c19NaXRvX3BlcmNlbnQiLCBjb2xvdXJfYnkgPSAiZGlzY2FyZENlbGxzIikKcGxvdENvbERhdGEoc2NlLCB4ID0gInN1bSIsIHk9ImRldGVjdGVkIiwgY29sb3VyX2J5PSJkaXNjYXJkQ2VsbHMiKQpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CiMgdmlzdWFsaXplIGNlbGxzIHRvIGJlIHJlbW92ZWQKcGxvdENvbERhdGEoc2NlLCB4ID0gImRldGVjdGVkIiwgeT0ic3Vic2V0c19NaXRvX3BlcmNlbnQiLCBjb2xvdXJfYnkgPSAiZG9ub3IiKQpwbG90Q29sRGF0YShzY2UsIHggPSAic3VtIiwgeT0iZGV0ZWN0ZWQiLCBjb2xvdXJfYnk9ImRvbm9yIikKYGBgCgpgYGB7cixldmFsPUZBTFNFfQojIHZpc3VhbGl6ZSBjZWxscyB0byBiZSByZW1vdmVkCnBsb3RDb2xEYXRhKHNjZSwgeCA9ICJkZXRlY3RlZCIsIHk9InN1YnNldHNfTWl0b19wZXJjZW50IiwgY29sb3VyX2J5ID0gImV4cGVyaW1lbnQiKQpwbG90Q29sRGF0YShzY2UsIHggPSAic3VtIiwgeT0iZGV0ZWN0ZWQiLCBjb2xvdXJfYnk9ImV4cGVyaW1lbnQiKQpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CnRhYmxlKHNjZSRkb25vciwgc2NlJGRpc2NhcmRDZWxscykKdGFibGUoc2NlJGRvbm9yLCBzY2UkZGlzY2FyZENlbGxzKS9yb3dTdW1zKHRhYmxlKHNjZSRkb25vciwgc2NlJGRpc2NhcmRDZWxscykpCiNmcmFjdGlvbnMgb2YgcmVtb3ZlZCBjZWxscyBwZXIgZG9ub3IKYGBgCgpNb3N0IHJlbW92ZWQgY2VsbHMgKGZyYWN0aW9uKSBhcmUgZnJvbSBwYXRpZW50cyBgZGl4aGAgYW5kIGBiYWJ6YC4KCmBgYHtyLGV2YWw9RkFMU0V9CnRhYmxlKHNjZSRleHBlcmltZW50LCBzY2UkZGlzY2FyZENlbGxzKQp0YWJsZShzY2UkZXhwZXJpbWVudCwgc2NlJGRvbm9yKQpgYGAKCk1vc3QgcmVtb3ZlZCBjZWxscyAoZnJhY3Rpb24pIGFyZSBmcm9tIHBhdGllbnRzIGBkaXhoYCBhbmQgYGJhYnpgLgpNb3N0IGxvdyBsaWJyYXJ5IHNpemVzIHNlZW0gdG8gY29tZSBmcm9tIHBhdGllbnQgYGRpeGhgOyBmb3IgcGF0aWVudCBgYmFiemAKdGhlIGVmZmVjdCBpcyBsZXNzIHByb25vdW5jZWQuCgpgYGB7cixldmFsPUZBTFNFfQpwbG90Q29sRGF0YShzY2VbLHNjZSRkb25vcj09ImRpeGgiXSwgeCA9ICJzdW0iLCB5PSJkZXRlY3RlZCIpCnBsb3RDb2xEYXRhKHNjZVssc2NlJGRvbm9yPT0iYmFieiJdLCB4ID0gInN1bSIsIHk9ImRldGVjdGVkIikKYGBgCgpBcyBzdWNoLCB3ZSBhcmUgbWFpbmx5IHJlbW92aW5nIGNlbGxzIGZyb20gc3BlY2lmaWMgcGF0aWVudHMgYW5kIHRoZSByZXNwZWN0aXZlCmJhdGNoZXMgaW4gd2hpY2ggdGhleSB3ZXJlIHNlcXVlbmNlZC4gSG93ZXZlciwgd2Ugd2FudCB0byBiZSBjYXJlZnVsOyB3ZSBvbmx5CndhbnQgdG8gcmVtb3ZlIHRlY2huaWNhbCBhcnRlZmFjdHMsIHdoaWxlIHJldGFpbmluZyBhcyBtdWNoIG9mIHRoZSBiaW9sb2d5IGFzCnBvc3NpYmxlLiBJbiBvdXIgZXhwbG9yYXRvcnkgZmlndXJlLCB3ZSBzZWUgdGhhdCB0aGUgY2VsbHMgd2UgYXJlIHJlbW92aW5nIGJhc2VkCm9uIHRoZSBudW1iZXIgb2YgZ2VuZXMgZGV0ZWN0ZWQsIGFyZSBxdWl0ZSBmYXIgYXBhcnQgZnJvbSB0aGUgYnVsayBvZiB0aGUgZGF0YQpjbG91ZDsgYXMgc3VjaCwgdGhlc2UgY2VsbHMgYXJlIGluZGVlZCBzdXNwaWNpb3VzLiBGb3IgdGhlIGNyaXRlcmlvbiBvZgpsaWJyYXJ5IHNpemUsIHdlIHNlZSB0aGF0IHRoZSBjZWxscyByZW1vdmVkIHRoZXJlIGFyZSBzdGlsbCBzdHJvbmdseSBjb25uZWN0ZWQKdG8gdGhlIGRhdGEgY2xvdWQuIEFzIHN1Y2gsIHdlIG1heSB3YW50IHRvIHJlbGF4IHRoZSBmaWx0ZXJpbmcgY3JpdGVyaW9uIHRoZXJlIGEKbGl0dGxlIGJpdC4gV2hlbiB3ZSB0aGluayBvZiBob3cgdGhlIGFkYXB0aXZlIHRocmVzaG9sZCBzdHJhdGVneSB3b3Jrcywgd2UKbWF5IHdhbnQgdG8gcmVtb3ZlIGNlbGxzIHRoYXQgYXJlIDRNQURzIGF3YXkgZnJvbSB0aGUgY2VudGVyLCByYXRoZXIgdGhhbgp0aGUgZGVmYXVsdCAzIE1BRHMuCgpgYGB7cixldmFsPUZBTFNFfQojIHByZXZpb3VzbHkKbG93TGliIDwtIGlzT3V0bGllcihkZiRzdW0sIHR5cGU9Imxvd2VyIiwgbG9nPVRSVUUpCnRhYmxlKGxvd0xpYikKCiMgYWZ0ZXIgc2VlaW5nIGFwcHJvcHJpYXRlIGV4cGxvcmF0b3J5IGZpZ3VyZQpsb3dMaWIgPC0gaXNPdXRsaWVyKGRmJHN1bSwgbm1hZHM9NCwgdHlwZT0ibG93ZXIiLCBsb2c9VFJVRSkKdGFibGUobG93TGliKQoKZGlzY2FyZENlbGxzIDwtIChsb3dMaWIgfCBsb3dGZWF0dXJlcyB8IGhpZ2hNaXRvKQp0YWJsZShkaXNjYXJkQ2VsbHMpCmNvbERhdGEoc2NlKSRkaXNjYXJkQ2VsbHMgPC0gZGlzY2FyZENlbGxzCmBgYAoKTm90ZSB0aGF0IHRoZXNlIHN0ZXBzIGFyZSBub3QgZXhhY3Q7IGRpZmZlcmVudCBhbmFseXN0cyB3aWxsIGNvbWUgd2l0aCBkaWZmZXJlbnQKZmlsdGVyaW5nIGNyaXRlcmlhIGZvciBtYW55IG9mIHRoZSBzdGVwcy4gVGhlIGtleSBpZGVhcyBhcmUgdGhhdAp3ZSBsZXQgYXBwcm9wcmlhdGUgZXhwbG9yYXRvcnkgZmlndXJlcyBndWlkZSB1cyB0byBtYWtlIHJlYXNvbmFibGUgY2hvaWNlczsKaS5lLiwgd2UgbG9vayBhdCB0aGUgZGF0YSByYXRoZXIgdGhhbiBibGluZGx5IGZvbGxvd2luZyBhIHN0YW5kYXJkaXplZCBwaXBlbGluZQp0aGF0IG1heSB3b3JrIHdlbGwgaW4gbWFueSBjYXNlcywgYnV0IG1heWJlIG5vdCBvdXIgcGFydGljdWxhciBkYXRhc2V0LgoKYGBge3IsZXZhbD1GQUxTRX0KIyByZW1vdmUgY2VsbHMgaWRlbnRpZmllZCB1c2luZyBhZGFwdGl2ZSB0aHJlc2hvbGRzCnNjZSA8LSBzY2VbLCAhY29sRGF0YShzY2UpJGRpc2NhcmRDZWxsc10KYGBgCgojIyBJZGVudGlmeWluZyBhbmQgcmVtb3ZpbmcgZW1wdHkgZHJvcGxldHMKClRoaXMgZG9lcyBub3QgbWFrZSBtdWNoIHNlbnNlIGZvciBwbGF0ZS1iYXNlZCBkYXRhISBXaGlsZSB3ZSBjb3VsZCBpbWFnaW5lIHRoZQpwcmVzZW5jZSBvZiBlbXB0eSBwbGF0ZSB3ZWxscyBmb3IgdGhlIFNNQVJULVNlcTIgZXhwZXJpbWVudCwgdGhlc2UgYXJlIHR5cGljYWxseQpkZXRlY3RlZCBhbmQgcmVtb3ZlZCB1cHN0cmVhbSBvZiBvdXIgYW5hbHlzaXMgdXNpbmcgbGFzZXIgdGVjaG5vbG9neS4KCiMjIElkZW50aWZ5aW5nIGFuZCByZW1vdmluZyBkb3VibGV0cwoKQWdhaW4sIHRoaXMgc3RlcCB0eXBpY2FsbHkgZG9lcyBub3QgbWFrZSBtdWNoIHNlbnNlIGZvciBwbGF0ZS1iYXNlZCAKZGF0YS4gV2hpbGUgd2UgY291bGQgaW1hZ2luZSB0aGUgcHJlc2VuY2Ugb2YgcGxhdGUgd2VsbHMgaW4gd2hpY2ggbW9yZSB0aGFuIHR3bwpjZWxscyBhcmUgcHJlc2VudCBmb3IgdGhlIFNNQVJULVNlcTIgZXhwZXJpbWVudCwgdGhlc2UgYXJlIGFnYWluIHR5cGljYWxseQpkZXRlY3RlZCBhbmQgcmVtb3ZlZCB1cHN0cmVhbSBvZiBvdXIgYW5hbHlzaXMgdXNpbmcgbGFzZXIgdGVjaG5vbG9neS4KCklmIHdlIHdvdWxkIHVzZQpbc2NEYmxGaW5kZXJdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy8zLjE0L2Jpb2MvaHRtbC9zY0RibEZpbmRlci5odG1sKSAKdG8gZGV0ZWN0IGRvdWJsZXQgY2VsbHMgb24gdGhpcyBkYXRhc2V0LCB3ZSB3b3VsZCBzZWUgdGhhdCBvbmx5IGEgdmVyeSBzbWFsbCAKZnJhY3Rpb24gb2YgdGhlIGNlbGxzIHdvdWxkIGJlIHJlbW92ZWQuIFRoZXNlIHJlbW92ZWQgY2VsbHMgY291bGQgY29ycmVzcG9uZCB0byAKYWN0dWFsIGRvdWJsZXRzIHRoYXQgd2VyZSBtaXNzZWQgYnkgdGhlIHVwc3RyZWFtIGRvdWJsZXQgZGV0ZWN0aW9uIHRlY2hub2xvZ3ksCm9yIG1vcmUgbGlrZWx5IGNvdWxkIGNvcnJlc3BvbmQgdG8gZmFsc2UgcG9zaXRpdmUgZG91YmxldHMgb2YgdGhlIGBzY0RibEZpbmRlcmAKZG91YmxldCBkZXRlY3Rpb24gcHJvY2VkdXJlLgoKIyBOb3JtYWxpemF0aW9uCgpGb3Igbm9ybWFsaXphdGlvbiwgdGhlIHNpemUgZmFjdG9ycyAkc19pJCBjb21wdXRlZCBoZXJlIGFyZSBzaW1wbHkgc2NhbGVkIApsaWJyYXJ5IHNpemVzOgoKXFsgTl9pID0gXHN1bV9nIFlfe2dpfSBcXQpcWyBzX2kgPSBOX2kgLyBcYmFye059X2kgXF0KCmBgYHtyLGV2YWw9RkFMU0V9CnNjZSA8LSBsb2dOb3JtQ291bnRzKHNjZSkKCiMgbm90ZSB3ZSBhbHNvIHJldHVybmVkIGxvZyBjb3VudHM6IHNlZSB0aGUgYWRkaXRpb25hbCBsb2djb3VudHMgYXNzYXkuCnNjZQoKIyB5b3UgY2FuIGV4dHJhY3Qgc2l6ZSBmYWN0b3JzIHVzaW5nCnNmIDwtIGxpYnJhcnlTaXplRmFjdG9ycyhzY2UpCm1lYW4oc2YpICMgZXF1YWwgdG8gMSBkdWUgdG8gc2NhbGluZy4KcGxvdCh4PSBsb2coY29sU3Vtcyhhc3NheXMoc2NlKSRjb3VudHMpKSwgCiAgICAgeT1zZikKYGBgCgotLS0KCi0tLSBlbmQgbGFiIHNlc3Npb24gMSAtLS0KCi0tLQoKIyBOb3JtYWxpemF0aW9uIChjb250aW51ZWQpCgpWaXN1YWxpemUgdGhlIGVmZmVjdCBvZiBub3JtYWxpemF0aW9uIHVzaW5nIE1ELXBsb3RzLgoKSW4gb3JkZXIgdG8gb25seSB3b3JrIHdpdGggY2VsbHMgdGhhdCBjb3VsZCBiZSBjb25zaWRlcmVkIHRlY2huaWNhbCByZXBsaWNhdGVzLAp3ZSBzaG91bGQgc2VsZWN0IGNlbGxzIGNvbWluZyBmcm9tIHRoZSBzYW1lIGJpb2xvZ2ljYWwgZ3JvdXAuIFdlIGNhbiBkbyB0aGlzCmFwcHJveGltYXRlbHkgYnkgc2VsZWN0aW5nIGNlbGxzIGZyb20gdGhlIHNhbWUgc2FtcGxpbmcgZGF5LCB0aGUgc2FtZSAKZG9ub3IgYW5kIHRoZSBzYW1lIHNlcXVlbmNpbmcgZXhwZXJpbWVudC4gRm9yIGluc3RhbmNlLCB3aGVuIGluc3BlY3RpbmcgdGhlc2UKbWV0YWRhdGEKCmBgYHtyLGV2YWw9RkFMU0V9CnRhYmxlKHNjZSRkb25vciwgc2NlJGRheSkKdGFibGUoc2NlJGV4cGVyaW1lbnQsIHNjZSRkYXkpCnRhYmxlKHNjZSRleHBlcmltZW50LCBzY2UkZG9ub3IpCmBgYAoKd2UgbWF5IHNlbGVjdCBjZWxscyBmcm9tIHRoZSBzYW1lIGJpb2xvZ2ljYWwgcG9wdWxhdGlvbiBhcyBmb2xsb3dzOgoKYGBge3IsZXZhbD1GQUxTRX0KY3MgPC0gY29sU3Vtcyhhc3NheXMoc2NlKSRjb3VudHNbLHdoaWNoKHNjZSRkYXkgPT0gImRheTIiICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjZSRkb25vciA9PSAiZWlwbCIpXSkKY3Nbb3JkZXIoY3MsIGRlY3JlYXNpbmcgPSBUUlVFKV1bYygxLDEwLDEwMCldCmBgYAoKTmV4dCwgd2UgbWF5IGFzc2VzcyB0aGUgZWZmZWN0IG9mIG5vcm1hbGl6YXRpb24gbGlrZSB3ZSBkaWQgcHJldmlvdXNseSBmb3IKdGhlIE1hY29za28gZGF0YXNldC4KCiMgRmVhdHVyZSBzZWxlY3Rpb24KCmBgYHtyLGV2YWw9RkFMU0V9CnJvd25hbWVzKHNjZSkgPC0gcm93RGF0YShzY2UpJEVuc2VtYmwgIyB3aWxsIGJlIG1vcmUgY29udmVuaWVudCBkb3duc3RyZWFtCmBgYAoKIyMgSGlnaGx5IHZhcmlhYmxlIGdlbmVzCgpPYnRhaW4gdGhlIHRvcCAxMCUgb2YgZ2VuZXMgdGhhdCBoYXZlIGEgaGlnaCB2YXJpYW5jZSB3aXRoIHJlc3BlY3QgdG8gdGhlaXIgCm1lYW4uCgojIyBIaWdoIGRldmlhbmNlIGdlbmVzCgpPYnRhaW4gdGhlIHRvcCBuIGdlbmVzIHRoYXQgaGF2ZSBhIGhpZ2ggZGV2aWFuY2Ugc3RhdGlzdGljLgoKIyMgU2V1cmF0IFZTVAoKVXNlIFNldXJhdCB0byBvYnRhaW4gaGlnaGx5IHZhcmlhYmxlIGdlbmVzIGFjY29yZGluZyB0byB0aGUgVlNUIHByb2NlZHVyZS4KCiMgRGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uCgojIyBUaGUgbW9zdCBiYXNpYyBEUgoKSnVzdCBieSBsb29raW5nIGF0IHRoZSB0b3AgdHdvIGdlbmVzIGJhc2VkIG9uIG91ciBmZWF0dXJlIHNlbGVjdGlvbiBjcml0ZXJpb24sIAp3ZSBjYW4gYWxyZWFkeSBzZWUgc29tZSBzZXBhcmF0aW9uIGFjY29yZGluZyB0byB0aGUgY2VsbCB0eXBlOyB0aGUgY2VsbHMgCmNvbG9yZWQgcmVkIHNlZW0gdG8gaGF2ZSBsb3cgZXhwcmVzc2lvbiB2YWx1ZXMgZm9yIGJvdGggZ2VuZXMsIGFuZCBhcmUgdGhvc2UKc29sZWx5IHJldGlyZXZlZCBpbiB0aGUgYm90dG9tIGxlZnQgb2YgdGhlIHgteSBwbGFuZS4KCiMjIExpbmVhciBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb246IFBDQQoKIyMjIFBDQSB3aXRoIGZlYXR1cmUgc2VsZWN0aW9uCgpZb3Ugc2hvdWxkIGFscmVhZHkgb2JzZXJ2ZSBhIGNsZWFyLWN1dCBjbHVzdGVyaW5nIG9mIHRoZSBjZWxscyEKCiMjIEEgZ2VuZXJhbGl6YXRpb24gb2YgUENBIGZvciBleHBvbmVudGlhbCBmYW1pbHkgZGlzdHJpYnV0aW9ucwoKCiMjIE5vbi1saW5lYXIgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uOiBULVNORQoKQ3JlYXRlIGEgdC1TTkUgYmFzZWQgb24gdGhlIGZpcnN0IDMwIGNvbXBvbmVudHMgb2YgdGhlIFBDQS4gCkV2YWx1YXRlIHRoZSByZXN1bHRpbmcgMkQgcGxvdCB2ZXJ5IGNhcmVmdWxseSEgQ29sb3IgdGhlIGNlbGxzIGJhc2VkIG9uIHRoZQpkYXkgdmFyaWFibGUgKHByb3h5IGZvciBjZWxsIHR5cGUpLCBidXQgYWxzbyBvbiBvdGhlciByZWxldmFudCBtZXRhZGF0YS4KV2hhdCBkbyB5b3Ugb2JzZXJ2ZT8KCiMjIE5vbi1saW5lYXIgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uOiBVTUFQCgpDcmVhdGUgYSBVTUFQIGJhc2VkIG9uIHRoZSBmaXJzdCAzMCBjb21wb25lbnRzIG9mIHRoZSBQQ0EuIApFdmFsdWF0ZSB0aGUgcmVzdWx0aW5nIDJEIHBsb3QgdmVyeSBjYXJlZnVsbHkhIENvbG9yIHRoZSBjZWxscyBiYXNlZCBvbiB0aGUKZGF5IHZhcmlhYmxlIChwcm94eSBmb3IgY2VsbCB0eXBlKSwgYnV0IGFsc28gb24gb3RoZXIgcmVsZXZhbnQgbWV0YWRhdGEuCldoYXQgZG8geW91IG9ic2VydmU/CgoK