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)
download.file()
sce <- readRDS("/Users/jg/Desktop/sce_15_cuomo.rds")
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 —
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.
Feature selection
rownames(sce) <- rowData(sce)$Ensembl # will be more convenient downstream
Highly variable genes
Obtain the top 10% of genes that have a high variance with respect to their mean.
High deviance genes
Obtain the top n genes that have a high deviance statistic.
Seurat VST
Use Seurat to obtain highly variable genes according to the VST procedure.
Dimensionality reduction
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.
Linear dimensionality reduction: PCA
PCA with feature selection
You should already observe a clear-cut clustering of the cells!
A generalization of PCA for exponential family distributions
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?
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