Preamble: installation of Bioconductor libraries
# install BiocManager package if not installed yet.
# BiocManager is the package installer for Bioconductor software.
if (!requireNamespace("BiocManager", quietly = TRUE))
install.packages("BiocManager")
## Installing BiocManager [1.30.16] ...
## OK [linked cache]
# install packages if not yet installed.
pkgs <- c("SingleCellExperiment",
"ExperimentHub",
"edgeR",
"biomaRt",
"DropletUtils",
"scRNAseq",
"scater",
"scuttle",
"scran",
"scry",
"BiocSingular",
"scDblFinder",
"glmpca",
"PCAtools",
"Seurat")
notInstalled <- pkgs[!pkgs %in% installed.packages()[,1]]
if(length(notInstalled) > 0){
BiocManager::install(notInstalled)
}
## 'getOption("repos")' replaces Bioconductor standard repositories, see
## '?repositories' for details
##
## replacement repositories:
## CRAN: https://cran.rstudio.com
## Bioconductor version 3.14 (BiocManager 1.30.16), R 4.1.2 (2021-11-01)
## Installing package(s) 'BiocVersion', 'SingleCellExperiment', 'ExperimentHub',
## 'edgeR', 'biomaRt', 'DropletUtils', 'scRNAseq', 'scater', 'scuttle', 'scran',
## 'scry', 'BiocSingular', 'scDblFinder', 'glmpca', 'PCAtools', 'Seurat'
## also installing the dependencies 'bit', 'sys', 'formatR', 'rjson', 'fs', 'GenomeInfoDbData', 'zlibbioc', 'bit64', 'blob', 'memoise', 'plogr', 'DT', 'generics', 'tidyselect', 'assertthat', 'askpass', 'lambda.r', 'futile.options', 'GenomicAlignments', 'restfulr', 'bitops', 'Rhtslib', 'cpp11', 'rprojroot', 'gtools', 'caTools', 'sass', 'jquerylib', 'MatrixGenerics', 'Biobase', 'GenomeInfoDb', 'XVector', 'RSQLite', 'interactiveDisplayBase', 'dplyr', 'dbplyr', 'DBI', 'filelock', 'KEGGREST', 'hms', 'prettyunits', 'openssl', 'futile.logger', 'snow', 'sparseMatrixStats', 'rhdf5filters', 'R.oo', 'R.methodsS3', 'sitmo', 'AnnotationFilter', 'rtracklayer', 'Rsamtools', 'ProtGenerics', 'Biostrings', 'RCurl', 'BiocIO', 'RcppHNSW', 'beeswarm', 'vipor', 'data.table', 'plyr', 'globals', 'listenv', 'parallelly', 'zoo', 'htmlwidgets', 'tidyr', 'lazyeval', 'crosstalk', 'purrr', 'promises', 'here', 'gplots', 'RcppArmadillo', 'httpuv', 'xtable', 'fontawesome', 'sourcetools', 'later', 'commonmark', 'bslib', 'cachem', 'spatstat.data', 'spatstat.utils', 'spatstat.sparse', 'abind', 'tensor', 'goftest', 'deldir', 'polyclip', 'FNN', 'RSpectra', 'SummarizedExperiment', 'S4Vectors', 'BiocGenerics', 'GenomicRanges', 'DelayedArray', 'AnnotationHub', 'BiocFileCache', 'curl', 'rappdirs', 'limma', 'locfit', 'Rcpp', 'XML', 'AnnotationDbi', 'progress', 'httr', 'xml2', 'IRanges', 'BiocParallel', 'DelayedMatrixStats', 'HDF5Array', 'rhdf5', 'R.utils', 'dqrng', 'beachmat', 'Rhdf5lib', 'BH', 'ensembldb', 'GenomicFeatures', 'gridExtra', 'BiocNeighbors', 'ggbeeswarm', 'viridis', 'Rtsne', 'ggrepel', 'igraph', 'statmod', 'bluster', 'metapod', 'ScaledMatrix', 'irlba', 'rsvd', 'xgboost', 'cowplot', 'reshape2', 'fitdistrplus', 'future', 'future.apply', 'ggridges', 'ica', 'leiden', 'lmtest', 'matrixStats', 'miniUI', 'patchwork', 'pbapply', 'plotly', 'png', 'RANN', 'RcppAnnoy', 'reticulate', 'ROCR', 'scattermore', 'sctransform', 'SeuratObject', 'shiny', 'spatstat.core', 'spatstat.geom', 'uwot', 'RcppEigen', 'RcppProgress'
##
## There are binary versions available but the source versions are later:
## binary source needs_compilation
## httpuv 1.6.3 1.6.4 TRUE
## BH 1.75.0-0 1.78.0-0 FALSE
## igraph 1.2.9 1.2.10 TRUE
##
##
## The downloaded binary packages are in
## /var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T//RtmpDprmjP/downloaded_packages
## installing the source packages 'GenomeInfoDbData', 'httpuv', 'BH', 'igraph', 'scRNAseq'
## Old packages: 'crayon', 'digest', 'glue', 'knitr', 'lattice', 'lifecycle',
## 'mgcv', 'mime', 'nlme', 'pillar', 'rlang', 'rmarkdown', 'stringi', 'tibble',
## 'tinytex', 'withr', 'xfun', 'Matrix'
Quality control
In quality control (QC), we check the quality of our dataset. In particular, we investigate undesirable oddities, such as low-quality cells, empty droplets or doublets.
Identifying and removing low-quality cells
There are several distinguishing features of low-quality cells that can be used in order to identify them. As described in the OSCA book book):
The library size is defined as the total sum of counts across all relevant features for each cell. Here, we will consider the relevant features to be the endogenous genes. Cells with small library sizes are of low quality as the RNA has been lost at some point during library preparation, either due to cell lysis or inefficient cDNA capture and amplification.
The number of expressed features in each cell is defined as the number of endogenous genes with non-zero counts for that cell. Any cell with very few expressed genes is likely to be of poor quality as the diverse transcript population has not been successfully captured.
We sometimes have spike-in (ERCC) transcripts available. The proportion of reads mapped to spike-in transcripts is calculated relative to the total count across all features (including spike-ins) for each cell. As the same amount of spike-in RNA should have been added to each cell, any enrichment in spike-in counts is symptomatic of loss of endogenous RNA.
In the absence of spike-in transcripts, the proportion of reads mapped to genes in the mitochondrial genome can be used. High proportions are indicative of poor-quality cells (Islam et al. 2014; Ilicic et al. 2016), presumably because of loss of cytoplasmic RNA from perforated cells. The reasoning is that, in the presence of modest damage, the holes in the cell membrane permit efflux of individual transcript molecules but are too small to allow mitochondria to escape, leading to a relative enrichment of mitochondrial transcripts. For single-nuclei RNA-seq experiments, high proportions are also useful as they can mark cells where the cytoplasm has not been successfully stripped.
Calculate QC variables
This function calculates useful QC metrics for identification and removal of potentially problematic cells. Default per-cell metrics are the sum of counts (i.e., the library size) and the number of detected features. The percentage of counts in the top features also provides a measure of library complexity.
If subsets is specified, these statistics are also computed for each subset of features. This is useful for investigating gene sets of interest, e.g., mitochondrial genes.
library(scater)
# check ERCC spike-in transcripts
sum(grepl("^ERCC-", rownames(sce))) # no spike-in transcripts available
# check mitochondrial genes
sum(rowData(sce)$chromosome_name=="MT",na.rm = TRUE) # 28 mitochondrial genes
sum(grepl("^MT-", rownames(sce))) # alternatively
is.mito <- grepl("^MT-", rownames(sce))
## calculate QC metrics
df <- perCellQCMetrics(sce, subsets=list(Mito=is.mito))
head(df)
# add QC variables to sce object
colData(sce) <- cbind(colData(sce), df)
# the QC variables have now been added to the colData of our SCE object.
head(colData(sce))
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="cluster")
# Mitochondrial genes
plotColData(sce, x = "detected", y="subsets_Mito_percent")
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.
Here we will remove cells for QC based on adaptive thresholds related to the three points from above. Adaptive trhesholds are used as opposed to fixed thresholds.
With fixed thresholds, we use fixed cut-off values for each cell to pass QC, e.g., we might consider cells to be low quality if they have library sizes below 100,000 reads; express fewer than 5,000 genes; have spike-in proportions above 10%; or have mitochondrial proportions above 10%.
With adaptive thresholds, we assume that most of the dataset consists of high-quality cells. We then identify cells that are outliers for the various QC metrics, based on the median absolute deviation (MAD) from the median value of each metric across all cells. By default, we consider a value to be anoutlier if it is more than 3 MADs from the median in the “problematic” direction. This is loosely motivated by the fact that such a filter will retain 99% of non-outlier values that follow a normal distribution. We demonstrate adopting adaptive thresholds on the Macosko dataset:
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 = "sum", y="detected", colour_by="discardCells")
plotColData(sce, x = "detected", y="subsets_Mito_percent", colour_by = "discardCells")
We removed a total of \(3423\) cells, most of which because of an outlyingly high percentage of reads from mitochondrial genes.
Identifying and removing empty droplets
Note that the removal of cells with low sequencing depth using the adaptive threshold procedure above is a way of removing empty droplets. Other approaches are possible, e.g., removing cells by statistical testing using emtpyDrops
. This does require us to specify a lower bound on the total number of UMIs, below which all cells are considered to correspond to empty droplets. This lower bound may not be trivial to derive, but the barcodeRanks
function can be useful to identify an elbow/knee point.
In brief, the steps taken by the emtpyDrops
function can be summarized as follows:
Define threshold T of total UMI counts (e.g. with the help of the barcodeRanks
function), below which cells may be considered to be from empty droplets. Call this set of cells E.
Define \(A_g\) as the total gene expression across all cells in E.
Define \(pi_g\) as the relative contribution of gene g to the ambient profile.
Calculate p-value for each cell to have a transcriptional profile similar to the ambient solution. Intuitively, a p-value below the requested alpha level would correspond to a cell for which the observed count profile strongly deviates from the count profile observed in the cells with a library size below threshold T, i.e., a non-empty droplet.
library(DropletUtils)
bcrank <- barcodeRanks(counts(sce))
# Only showing unique points for plotting speed. Duplicated ranks are a
# consequence of ties in the ranks, i.e., when cells have an equal library size.
sum(duplicated(bcrank$rank))
uniq <- !duplicated(bcrank$rank)
plot(bcrank$rank[uniq], bcrank$total[uniq], log="xy",
xlab="Rank", ylab="Total UMI count", cex.lab=1.2)
abline(h=metadata(bcrank)$inflection, col="darkgreen", lty=2)
abline(h=metadata(bcrank)$knee, col="dodgerblue", lty=2)
abline(h=350, col="orange", lty=2) # picked visually myself
legend("topright",
legend=c("Inflection", "Knee", "Empirical knee point"),
col=c("darkgreen", "dodgerblue", "orange"),
lty=2,
cex=1.2)
set.seed(100)
limit <- 350
all.out <- emptyDrops(counts(sce), lower=limit, test.ambient=TRUE)
# p-values for cells with total UMI count under the lower bound.
hist(all.out$PValue[all.out$Total <= limit & all.out$Total > 0],
xlab="P-value", main="", col="grey80", breaks=20)
# but note that it would remove a very high number of cells
length(which(all.out$FDR <= 0.01)) # retained
# so we stick to the more lenient adaptive filtering strategy
# remove cells identified using adaptive thresholds
sce <- sce[, !colData(sce)$discardCells]
Identifying and removing doublets
We will use scDblFinder to detect doublet cells.
As discussed in the theory session of last week, the steps taken by scDblFinder
can be summarized as follows:
Perform principal components analysis (PCA) on log-normalized expression counts. This allows for projecting each cell in the dataset into a 2D space (for more details on PCA, see next weeks session).
Randomly select two cells, sum their counts and normalize, and project into PCA space from step 1. In other words, artificially generate doublets and see where they are located in the 2D space.
Repeat step 2 many times (generate many artificial doublets).
Generate neighbor network in the 2D space. The network is then used to estimate a number of characteristics for each cell, in particular the proportion f artificial doublets among the nearest neighbors.
Use this information, along with other predictors, to train a classifier (gradient boosted tree) that allows for distinguishing doublets from singlets.
Note: only classifies for identifying doublets for which the two cells are from different cell type clusters.
## perform doublet detection
library(scDblFinder)
set.seed(211103)
sampleID <- unlist(lapply(strsplit(colData(sce)$cell.id, split="_"), "[[", 1))
table(sampleID)
sce <- scDblFinder(sce,
returnType="table",
samples = factor(sampleID))
table(sce$scDblFinder.class)
## visualize these scores
## explore doublet score wrt original cluster labels
boxplot(log1p(sce$scDblFinder.score) ~ factor(colData(sce)$cluster, exclude=NULL))
tab <- table(sce$scDblFinder.class, sce$cluster,
exclude=NULL)
tab
t(t(tab) / colSums(tab))
barplot(t(t(tab) / colSums(tab))[2,],
xlab = "Cluster", ylab = "Fraction of doublets")
range(sce$scDblFinder.score[sce$scDblFinder.class == "doublet" & sampleID == "r1"])
range(sce$scDblFinder.score[sce$scDblFinder.class == "singlet" & sampleID == "r1"])
# remove doublets
sce <- sce[,!sce$scDblFinder.class == "doublet"]
Normalization
Normalization aims to remove technical effects such as sequencing depth so that comparisons between cells are not confounded by them. The most commonly used normalization methods methods use scaling, where a scaling factor (also called size factor, normalization factor) is estimated for each cell. These scaling factors (e.g., the effective library size) can be included as an offset to downstream modeling procedures. This effectively allows for performing inference on the relative abundance of a gene in a cell, after accounting for library size and RNA composition differences between cells, which is much more relevant than comparing raw counts
Note that we always prefer the use of offsets over transformation of the data. In brief, such transformation would distort the mean-variance relationship in the data. For more details, we refer to the document that was discussed in theory session of last week (link)
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)
From the OSCA book: Alternatively, we may use more sophisticated approaches for variance stabilizing transformations in genomics data, e.g., DESeq2
or sctransform
. These aim to remove the mean-variance trend more effectively than the simpler transformations mentioned above, though it could be argued whether this is actually desirable. For low-coverage scRNA-seq data, there will always be a mean-variance trend under any transformation, for the simple reason that the variance must be zero when the mean count is zero. These methods also face the challenge of removing the mean-variance trend while preserving the interesting component of variation, i.e., the log-fold changes between subpopulations; this may or may not be done adequately, depending on the aggressiveness of the algorithm.
In practice, the log-transformation is a good default choice due to its simplicity and interpretability, and is what we will be using for all downstream analyses.
— end lab session 1 —
We have created a DropBox folder containing convenience files, e.g., a preprocessed SingleCellExperiment
that is the end result of lab session 1. You may want to downlaod them and import them in the code chunk below.
Normalization (continued)
Normalization is necessary to correct for several sources of technical variation:
- Differences in sequencing depth between samples. Some samples get sequenced deeper in the sense that they consist of more (mapped) reads and therefore can be considered to contain a higher amount of information, which we should be taking into account. In addition, if a sample is sequenced deeper, it is natural that the counts for each gene will be higher, jeopardizing a direct comparison of the expression counts.
- Differences in RNA population composition between samples. As an extreme example, suppose that two samples have been sequenced to the exact same depth. One sample is contaminated and has a very high concentration of the contaminant cDNA being sequenced, but otherwise the two samples are identical. Since the contaminant will be taking up a significant proportion of the reads being sequenced, the counts will not be directly comparable between the samples. Hence, we may also want to correct for differences in the composition of the RNA population of the samples (see edgeR manual chapter 2.8).
- Other technical variation such as sample-specific GC-content or transcript length effects may also be accounted for.
Below, we will visualize the normalization occurring between two cells of the same cell type (which could be considered technical repeats):
select <- sce$cluster == "1"
select[is.na(select)] <- FALSE
cs <- colSums(assays(sce)$counts[,select])
cs[order(cs, decreasing = TRUE)][c(1,10)]
Let’s take a look at how comparable two cells (replicates) of cluster 1 are. We will compare the cell with the highest library size with the cell that has the 10th 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)
We see clear bias in the comparison of the 1st and 10th most deeply sequenced cell from cell cluster 1. We see that the log fold-changes are biased downwards. This means that, on average, a gene is higher expressed in cell 1 versus cell 10. Looking at the library sizes, we can indeed see that the library size for cell 1 is 20869 counts, while it is only 14719 counts for cell 10! 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)
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)
Upon normalizing the data using size factors, we have removed bias as a consequence of differences in sequencing depth.
Note that we computed the normalized count matrix only for demonstrating the effect of such normalization. In a typical workflow, the size factors are used as offsets in the downstream models rather than to perform data transformation. In brief, such transformation would distort the mean-variance relationship in the data. For more details, we refer to the document that was touched upon in the first theory session (link)
Feature selection
As dimensions increase, shortest and farthest distances between points become nearly inseparable. In high-dimensional space, it is therefore extremely difficult to separate signal from noise.
In order to recover structure (e.g. setting up a dimension-reduced space to help us find cell-type clusters in the data), we want to move to an informative, lower-dimensional space. We will select genes which we hope are informative for recovering the biological structure. But what defines an informative gene?
Selecting genes with high variance
The simplest approach to quantifying per-gene variation is to compute the variance of the log-normalized expression values (i.e., “log-counts”) for each gene across all cells.
# calculate variance of log-normalized counts for each gene
geneVars <- genefilter::rowVars(...)
# select top 1000 highly variable genes
highVarGenes <- names(geneVars)[order(geneVars, decreasing=TRUE)[1:1e3]]
head(highVarGenes)
Selecting genes with high variation with respect to mean
While calculation of the per-gene variance is simple, feature selection requires modelling of the mean-variance relationship. Indeed, not accounting for the mean-variance structure while selecting highly variable genes will oftentimes boil down to selecting the most highly expressed genes. The log-transformation is a helpful variance stabilizing transformation, however, it is not perfect, meaning that the variance of a gene is not completely independent of its mean. Therefore, feature selection may still be driven by average expression rather than underlying biological heterogeneity.
A (rightly so) popular approach is to select genes that have a high variance with respect to their mean. Often, first an empirical mean-variance trend is fitted, upon which genes with the highest positive residuals are selected. Being intuitive, reasonable and fairly straight-forward, this method is widely used.
To account for the mean-variance effect, we use the modelGeneVar
function of the scran
package to fit a trend to the variance with respect to abundance across all genes (on log-normalized expression values of the sce object).
library(scran)
# `modelGeneVar` function description: Model the variance of the log-expression
# profiles for each gene, decomposing it into technical and biological
# components based on a fitted mean-variance trend.
dec <- modelGeneVar(...) # input a sce object with precomputed logcounts assay
head(dec)
The fitted value for each gene is used as a proxy for the technical component of variation for each gene, under the assumption that most genes exhibit a low baseline level of variation that is not biologically interesting. The biological component of variation for each gene is defined as the the residual from the trend. Ranking genes by the biological component enables identification of interesting genes for downstream analyses in a manner that accounts for the mean-variance relationship.
fitRetina <- metadata(...)
plot(x = ..., # the fitted means on the x-axis
y = ..., # the fitted variances on the y-axis
xlab="Mean of log-expression",
ylab="Variance of log-expression")
curve(fitRetina$trend(x), col="dodgerblue", add=TRUE, lwd=2) # adds a trend line
We are interested in those genes for which the variance in expression is higher than what we would expect for that gene based on its mean expression.
# get 10% most variable genes
# Based on the output statistics of modelGeneVar, select the 10% most variable genes
hvg <- getTopHVGs(stats = ...,
prop = ...) # percentage of top variable genes. Alternatively,
# n = ... could be used to select the top number of variable genes
head(hvg)
# plot these
plot(x = ..., # the fitted means on the x-axis
y = ..., # the fitted variances on the y-axis
col = c("orange", "darkseagreen3")[(names(fitRetina$mean) %in% hvg)+1],
xlab="Mean of log-expression",
ylab="Variance of log-expression")
curve(fitRetina$trend(x), col="dodgerblue", add=TRUE, lwd=2)
legend("topleft",
legend = c("Selected", "Not selected"),
col = c("darkseagreen3", "orange"),
pch = 16,
bty='n')
As a comparison, we could color the genes on this figure according to the selection that was made purely by looking at the raw variance of each gene.
As expected, we here simply select genes with a high variance, without recognizing that a high variance is typically driven by a high mean.
High deviance genes
Here, we will select genes with a high residual deviance. The idea is that we assume a null model of constant expression fraction (i.e., RNA concentration) across all cells. We subsequently calculate a goodness-of-fit statistic for each gene, assessing whether the model is a good approximation to the gene expression of the corresponding gene.
If the model fits poorly, i.e., the gene has a high deviance, the expression fraction varies significantly across the cells in our datasets.Genes with a high deviance will thus most poorly fit a null model where the relative abundance is equal for all cells, which therefore are informative.
#BiocManager::install("scry")
library(scry)
# `devianceFeatureSelection` function description: Computes a deviance statistic
# for each row feature (such as a gene) for count data based on a multinomial
# null model [...].
sce <- devianceFeatureSelection(object = ...,
assay = ...) # computed on raw counts
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")
Our plot looks similar to one displayed in the vignette of the scry package. Based on that plot, the authors suggest retaining 2.000 genes (the top 2000 based on the deviance residuals) for downstream dimensionality reduction and clustering.
Seurat VST
Another very common feature selection strategy is the variance-stabilizing transformation from the Seurat
R package, which amounts to calculating Pearson residuals from a regularized negative binomial regression model, with sequencing depth as a covariate.
Intermezzo: interoperability between SingleCellExperiment and Seurat objects
In this lecture series, we always make use of the SingleCellExperiment object and the packages available from Bioconductor. Another very popular toolbox for performing scRNA-seq data analysis is Seurat
. However, functions from Seurat
cannot be used directly to manipulate SingleCellExperiment
objects, and vice versa. Fortunately, efforts have been made to increase the interoperability between the two toolboxes.
library(Seurat)
seurat_obj <- as.Seurat(...) # sce object
seurat_obj # notice the "0 variable features"
On this object, we may use functions from the Seurat
toolbox. For instance, we may search for highly variable features using Seurat
’s VST implementation:
# Prior to detecting variable features using VST, Seurat normalizes the data.
# The `NormalizeData` function takes as input the Seurat object, and uses
# log normalization and a scale factor of 10000 as default (see help file).
seurat_obj <- Seurat::NormalizeData(object = ...,
normalization.method = ...,
scale.factor = ...)
# Next, perform feature selection based on VST
seurat_obj <- FindVariableFeatures(object = ...,
selection.method = ...)
seurat_obj # notice the "2000 variable features" (default)
head(VariableFeatures(seurat_obj)) # here they are
Dimensionality reduction
Note that, below, we color the cells using the known, true cell type label as defined in the metadata, to empirically evaluate the dimensionality reduction. In reality, we don’t know this yet at this stage.
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!
colData(sce)$cluster <- as.factor(colData(sce)$cluster)
cl <- colData(sce)$cluster
par(bty='l')
plot(x = ..., # extract the counts for the most variable gene
y = ..., # extract the counts for the second most variable gene
col = as.numeric(cl),
pch = 16, cex = 1/3,
xlab = "Most informative gene",
ylab = "Second most informative gene",
main = "Cells colored acc to cell type")
We are able to recover quite some structure. However, many cell populations remain obscure, and the plot is overcrowded.
Linear dimensionality reduction: PCA
A DR method is linear when the reduced dimensions are a linear function of the original variables. For example, in PCA, each principal component is a linear combination of genes, therefore the DR is a linear function of the original variables.
Typically, PCA is performed on log-transformed normalized counts. The log-transformation helps somewhat, but not completely, to account for the mean-variance relationship. PCA works well for bulk RNA-seq data. However, the structure of scRNA-seq data is often too complex to be visualized by a small number of PCs.
There are several R functions that allow you to perform PCA. Here, we make use of the runPCA
function of the scater
package, which has been specifically developed for performing PCA on SingleCellExperiment
objects. We calculate the top 30 principal components.
PCA with feature selection
# Perform a PCA for our data. Compute the first 30 principal components, only
# using the selected features with high variance with respect to the mean.
set.seed(1234)
sce <- runPCA(...) # you need three arguments
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 = "cluster")
While the large number of clusters in this dataset makes it hard to distinguish between all the different colors, we can already see that PCA retrieves some, but not all, of the structure in the data that was discovered by the original authors.
How many of the top PCs should we retain for downstream analyses? The choice of the number of PCs is a decision that is analogous to the choice of the number of HVGs to use. Using more PCs will retain more biological signal at the cost of including more noise that might mask said signal. On the other hand, using fewer PCs will introduce competition between different factors of variation, where weaker (but still interesting) factors may be pushed down into lower PCs and inadvertently discarded from downstream analyses.
Most analysts will simply aim to use a “reasonable” but arbitrary value, typically ranging from 10 to 50. This is often satisfactory as the later PCs explain so little variance that their inclusion or omission has no major effect.
percent.var <- attr(reducedDim(sce), "percentVar")
plot(percent.var, log="y", xlab="PC", ylab="Variance explained (%)")
plot(cumsum(percent.var), xlab="PC", ylab="Cumulative variance explained (%)")
Here, retaining ±15PCs seems reasonable. If you really prefer a more data-driven way for determining this, there are procedures available that aim to computationally identify the elbow/knee point in the variance explained per PC plot.
library(PCAtools)
chosen.elbow <- findElbowPoint(percent.var)
chosen.elbow
PCA without feature selection
Note: more features -> computationally more intensive!
set.seed(1234)
sceNoFS <- runPCA(x = ...,
ncomponents = ...,
subset_row = 1:nrow(sce))
plotPCA(...)
rm(sceNoFS) # remove the object, we don't need it anymore
While we use more information to make this PCA plot (17.887 genes) as compared to the feature selected PCA plot (642 genes), we seem to retrieve less structure in the data. This is the power of feature selection, an increase in the signal-to-noise ratio!
Effect of feature selection on PCA
First, we compare the different feature selection criteria, using the top 1000 highly variable genes for each method.
# Get top 1000 highly variable features using the highly variable genes
# (w.r.t. mean), high deviance genes and the VST strategy of Seurat
hvg1000 <- ... # extract top 1000 highly variable genes
hdg1000 <- ... # extract top 1000 genes with high deviance
vst1000 <- ... # extract top 1000 genes based on VST analysis
# HVG strategy
plotPCA(
runPCA(sce,
ncomponents = 2,
subset_row = hvg1000),
colour_by = "cluster") +
ggtitle("Highly variable genes")
# HDG strategy
plotPCA(
runPCA(sce,
ncomponents = 2,
subset_row = hdg1000),
colour_by = "cluster") +
ggtitle("High-deviance genes")
# VST strategy
plotPCA(
runPCA(sce,
ncomponents = 2,
subset_row = vst1000),
colour_by = "cluster",
) +
ggtitle("Seurat VST")
Next, we assess the sensitivity on the number of top features for the highly variable genes method;
hvg_all <- getTopHVGs(dec)
hvg2000 <- ... # extract top 2000 highly variable genes
hvg1000 <- ... # extract top 1000 highly variable genes
hvg100 <- ... # extract top 100 highly variable genes
hvg10 <- ... # extract top 10 highly variable genes
hvg5 <- ... # extract top 5 highly variable genes
plotPCA(
runPCA(sce,
ncomponents = 2),
colour_by = "cluster") +
ggtitle("All genes")
plotPCA(
runPCA(sce,
ncomponents = 2,
subset_row = hvg2000),
colour_by = "cluster") +
ggtitle("Top 2000 genes")
plotPCA(
runPCA(sce,
ncomponents = 2,
subset_row = hvg1000),
colour_by = "cluster") +
ggtitle("Top 1000 genes")
plotPCA(
runPCA(sce,
ncomponents = 2,
subset_row = hvg100),
colour_by = "cluster") +
ggtitle("Top 100 genes")
plotPCA(
runPCA(sce,
ncomponents = 2,
subset_row = hvg10),
colour_by = "cluster") +
ggtitle("Top 10 genes")
plotPCA(
runPCA(sce,
ncomponents = 2,
subset_row = hvg5),
colour_by = "cluster") +
ggtitle("Top 5 genes")
A generalization of PCA for exponential family distributions.
PCA is implicitly based on Euclidean distances, corresponding to maximizing a Gaussian likelihood, which is inappropriate for count data such as scRNA-seq. Townes et al. (2019) develop GLM-PCA, a generalization of PCA for exponential family likelihoods. They posit, using negative control data, that the data generative mechanism of UMI count data can be considered to be multinomial.
The GLM-PCA strategy is implemented in the glmpca
function of the glmpca
package.
Note that this function is quite computationally intensive. For regular PCA on log-transformed normalized counts, the underlying computations can be strongly simplified. Here, we work with the raw counts, which we assume to be Poisson distributed, requiring an iterative optimization scheme.
# runs 2min on my laptop
library(glmpca)
set.seed(211103)
poipca <- glmpca(Y = ..., # counts for the higly variable genes
L = ..., # two latent dimensions (components)
fam = ..., # assume Poisson for droplet data
minibatch = "stochastic") # used in the package vignette by
# the authors, but beyond the scope of this session.
reducedDim(sce, "PoiPCA") <- poipca$factors
plotReducedDim(...) # make sure to specify the right value for the `dimred` argument
Alternatively, we could adopt the GLM-PCA strategy using only the genes with high deviance.
# Based on the diagnostic plot from the feature selection, we would select the
# top 2000 genes with highest deviance. However, to reduce the running time of
# the function, I here select the top 500 (which still is quite slow - 5min).
hdg500 <- names(sort(rowData(sce)$binomial_deviance, decreasing = TRUE))[1:500]
...
The authors of the glmpca
package note that GLM-PCA can be slow for large datasets. Therefore, they have implemented a fast approximation of the algorithm, which first fits a null model of constant expression for each gene across all cells, and subsequently fits standard PCA to either the Pearson or deviance residuals from the null model.
However, at least for me the nullResiduals
function was extremely slow even slower than the glmpca
code above. Therefore, I suggest not running this code, but I leave it in for reference.
sce <- nullResiduals(sce, assay="counts", type="deviance")
sce <- nullResiduals(sce, assay="counts", type="pearson")
pca<-function(Y, L=2, center=TRUE, scale=TRUE){
#assumes features=rows, observations=cols
res<-prcomp(as.matrix(t(Y)), center=center, scale.=scale, rank.=L)
factors<-as.data.frame(res$x)
colnames(factors)<-paste0("dim", 1:L)
factors
}
pca_d <- pca(assay(sce[hdg,], "binomial_deviance_residuals"))
pca_d$resid_type <- "deviance_residuals"
pca_p <- pca(assay(sce[hdg,], "binomial_pearson_residuals"))
pca_p$resid_type <- "pearson_residuals"
cm <- as.data.frame(colData(sce[hdg,]))
pd <- rbind(cbind(cm, pca_d), cbind(cm, pca_p))
ggplot(pd, aes(x=dim1, y=dim2, colour=phenoid)) + geom_point() +
facet_wrap(~resid_type, scales="free", nrow=2) +
ggtitle("PCA applied to null residuals of high deviance genes")
Non-linear dimensionality reduction: t-SNE
t-SNE focuses on preserving local rather than global distances. Therefore, distances on a t-SNE reduced dimension plot can only be interpreted locally, i.e., cells that are close together in reduced dimension will have a similar transcriptome, but cells that are far away may not necessarily have a very distinct transcriptome.
Running t-SNE on a SingleCellExperiment
object can be achieved with the runTSNE
function of the scater
package. By default, this function will first perform PCA, and use the top 50 PCs as an input to the actual t-SNE algorithm. Since we already performed PCA, we may set dimred = "PCA"
as argument to the function. As such we will be performing a T-SNE on the 30 PCs we computed before. If we would like to run t-SNE only using the top 10 PCs, we could set n_dimred = 10
.
In addition, we may wish to set external_neighbors=TRUE
, which increases the speed of the algorithm for large datasets by applying a heuristic.
Note: for me this was the slowest function of the analysis (so far). If you feel like your PC/laptop had a lot of trouble with the previous step(s), you may consider not running this code, or running it on a subset of the data (e.g., randomly subsampling cells) for demonstrational purposes. Alternatively, you may consider reducing the input space for the t-SNE algorithm, e.g. by setting n_dimred = 5
.
# (For me it takes 3min30 with n_dimred = 5)
sce <- runTSNE(x = ...,
dimred = ...,
n_dimred = ...,
external_neighbors = ...)
plotTSNE(sce,
colour_by = "cluster")
Non-linear dimensionality reduction: UMAP
It is often suggested that UMAP is better than t-SNE in preserving global differences. Therefore, UMAP is also often used in analyses such as trajectory inference, where this is important.
Running UMAP on a SingleCellExperiment
object can be achieved with the runUMAP
function of the scater
package.
# Using top 10% highly variable genes and top 30 PCs
sce <- runUMAP(...)
plotUMAP(...)
# Using top 10% highly variable genes and top 10 PCs
plotUMAP(runUMAP(...),
colour_by = "cluster")
LS0tCnRpdGxlOiAnTm9ybWFsaXphdGlvbiwgZmVhdHVyZSBzZWxlY3Rpb24gYW5kIGRpbWVuc2lvbiByZWR1Y3Rpb24gZm9yIHRoZSBNYWNvc2tvIGRhdGFzZXQnCmF1dGhvcjogIktvZW4gVmFuIGRlbiBCZXJnZSBhbmQgSmVyb2VuIEdpbGlzIgpkYXRlOiAiMzAvMTEvMjAyMSIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgojIFByZWFtYmxlOiBpbnN0YWxsYXRpb24gb2YgQmlvY29uZHVjdG9yIGxpYnJhcmllcwoKYGBge3J9CiMgaW5zdGFsbCBCaW9jTWFuYWdlciBwYWNrYWdlIGlmIG5vdCBpbnN0YWxsZWQgeWV0LgojIEJpb2NNYW5hZ2VyIGlzIHRoZSBwYWNrYWdlIGluc3RhbGxlciBmb3IgQmlvY29uZHVjdG9yIHNvZnR3YXJlLgppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQogICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQoKIyBpbnN0YWxsIHBhY2thZ2VzIGlmIG5vdCB5ZXQgaW5zdGFsbGVkLgpwa2dzIDwtIGMoIlNpbmdsZUNlbGxFeHBlcmltZW50IiwKICAgICAgICAgICJFeHBlcmltZW50SHViIiwKICAgICAgICAgICJlZGdlUiIsCiAgICAgICAgICAiYmlvbWFSdCIsCiAgICAgICAgICAiRHJvcGxldFV0aWxzIiwgCiAgICAgICAgICAic2NSTkFzZXEiLCAKICAgICAgICAgICJzY2F0ZXIiLCAKICAgICAgICAgICJzY3V0dGxlIiwgCiAgICAgICAgICAic2NyYW4iLAogICAgICAgICAgInNjcnkiLAogICAgICAgICAgIkJpb2NTaW5ndWxhciIsIAogICAgICAgICAgInNjRGJsRmluZGVyIiwKICAgICAgICAgICJnbG1wY2EiLAogICAgICAgICAgIlBDQXRvb2xzIiwKICAgICAgICAgICJTZXVyYXQiKQpub3RJbnN0YWxsZWQgPC0gcGtnc1shcGtncyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywxXV0KaWYobGVuZ3RoKG5vdEluc3RhbGxlZCkgPiAwKXsKICBCaW9jTWFuYWdlcjo6aW5zdGFsbChub3RJbnN0YWxsZWQpCn0KCmBgYAoKIyBUaGUgTWFjb3NrbyBkYXRhc2V0CgpJbiB0aGlzIHdvcmtzaG9wIHNlc3Npb24sIHdlIHdpbGwgYW5hbHl6ZSB0aGUgc2luZ2xlLWNlbGwgUk5BLXNlcSBkYXRhc2V0CmZyb20gdGhlIHB1YmxpY2F0aW9uIGJ5IE1hY29za28gKmV0IGFsLiosIENlbGwgMTYxLCAxMjAy4oCTMTIxNCBmcm9tIDIwMTUKWyhsaW5rKV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5jZWxsLjIwMTUuMDUuMDAyKS4gVGhpcyBpcyB0aGUgbWFudXNjcmlwdCBpbgp3aGljaCB0aGUgZHJvcGxldCBzY1JOQS1zZXEgdGVjaG5vbG9neSAqKkRyb3Atc2VxKiogd2FzIGludHJvZHVjZWQuClNpeCB5ZWFycyBhZnRlciB0aGUgb3JpZ2luYWwgcHVibGljYXRpb24sIGRyb3Atc2VxIGlzIHN0aWxsIG9uZSBvZiB0aGUgbW9zdCAKY29tbW9ubHkgYWRvcHRlZCBzY1JOQS1zZXEgcHJvdG9jb2xzLCBhcyBldmlkZW5jZWQgYnkgdGhlCmxhcmdlIG51bWJlciBvZiBjaXRhdGlvbnMgZm9yIE1hY29za28gKmV0IGFsLiogCig0LjMwMyBjaXRhdGlvbnMgYXQgTm92ZW1iZXIgMywgMjAyMSkuCgpUaGUgYmFzaWMgaWRlYSBiZWhpbmQgdGhlIERyb3Atc2VxIHByb3RvY29sIGNhbiBiZSB0YWtlbiBmcm9tIHRoZSBncmFwaGljYWwKYWJzdHJhY3Qgb2YgdGhlIHB1YmxpY2F0aW9uLgoKYGBgCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJtYWNvc2tvX2dyYXBoaWNhbEFic3RyYWN0LmpwZWciKQpgYGAKClRoZSBzdWNjZXNzIG9mIERyb3Atc2VxIGNhbiBiZSBleHBsYWluZWQgYnkgdGhlIGZvbGxvd2luZyBhZHZhbnRhZ2VvdXMgCmZlYXR1cmVzOgoKLSBUaGUgdXNlIG9mIHVuaXF1ZSBtb2xlY3VsYXIgaWRlbnRpZmllcnMgKFVNSXMpLiBCeSB3b3JraW5nIHdpdGggVU1Jcywgb25lIGNvdW50CmNvcnJlc3BvbmRzIHRvIG9uZSBvYnNlcnZlZCBtUk5BIG1vbGVjdWxlIHByZXNlbnQgaW4gdGhlIGNlbGwuIFRoYW5rcyB0byB0aGUgdXNlCm9mIFVNSSBiYXJjb2RlcywgUENSIGFydGlmYWN0cyBhcmUgcmVkdWNlZC4KCi0gU2NhbGFiaWxpdHk6IG1pY3JvZmx1aWRpY3MgdGVjaG5vbG9neSBhbGxvd3MgZm9yIHBlcmZvcm1pbmcgdGhlIGxpYnJhcnkgcHJlcApyZWFjdGlvbnMgaW5zaWRlIG5hbm9kcm9wbGV0cywgaW4gd2hpY2ggc2luZ2xlIGNlbGxzIG1heSBiZSBjb250YWluZWQuIExpYnJhcnkgcHJlcCBvY2N1cnMgYWNyb3NzIGFsbCBkcm9wbGV0cyBzaW11bHRhbmVvdXNseS4KCi0gQ29zdDogdGhlIGV4cGVyaW1lbnQgY29zdHMgYXJvdW5kIDYuNSBjZW50cyAoVVNEKSBwZXIgY2VsbC4KCi0gU3BlZWQ6IFRoZSB2ZXJ5IGxhcmdlIGRhdGFzZXQgdGhhdCB3ZSB3aWxsIGJlIHdvcmtpbmcgd2l0aCB0b2RheSB3YXMgCmdlbmVyYXRlZCBpbiBhbiBleHBlcmltZW50IHRoYXQgdG9vayBvbmx5IDQgZGF5cy4KCkluIHRoaXMgcGFydGljdWxhciBleHBlcmltZW50LCBNYWNvc2tvICpldCBhbC4qIHNlcXVlbmNlZCA0OSwzMDAgY2VsbHMgZnJvbSB0aGUKbW91c2UgcmV0aW5hLCBpZGVudGlmeWluZyAzOSB0cmFuc2NyaXB0aW9uYWxseSBkaXN0aW5jdCBjZWxsIHBvcHVsYXRpb25zLiBUaGUKZXhwZXJpbWVudCB3YXMgcGVyZm9ybWVkIGluIDcgYmF0Y2hlcy4KCiMgRGF0YSBhdmFpbGFiaWxpdHkKCiMjIFNSQQoKVGhlIFtTZXF1ZW5jZSBSZWFkIEFyY2hpdmUgKFNSQSldKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3Yvc3JhKSBpcyB0aGUgCmxhcmdlc3QgcHVibGljbHkgYXZhaWxhYmxlIHJlcG9zaXRvcnkgb2YgaGlnaC10aHJvdWdocHV0IHNlcXVlbmNpbmcgZGF0YS4KVGhlIGRhdGEgYXJlIHN0b3JlZCBieSB0aGUgTmF0aW9uYWwgQ2VudGVyIGZvciBCaW90ZWNobm9sb2d5IEluZm9ybWF0aW9uIChOQ0JJKSAKc2VydmljZXMgYW5kIG11bHRpcGxlIGNsb3VkIHN0b3JhZ2UgcHJvdmlkZXJzLiBGcm9tIHRoaXMgd2Vic2l0ZSAicmF3IiAKc2VxdWVuY2luZyBkYXRhIGNhbiBiZSByZXRyaWV2ZWQuIEluIHByYWN0aWNlLCB0aGVzZSBhcmUgdXN1YWxseSBgLnNyYWAgZmlsZXMsCndoaWNoIGNhbiBiZSBkb3dubG9hZGVkIGFuZCBjb252ZXJ0ZWQgaW50byBGQVNUUSBmaWxlcyB1c2luZyBmdW5jdGlvbnMKZnJvbSB0aGUgYHNyYXRvb2xraXRgIHNvZnR3YXJlLiAKCkZvciBvdXIgZGF0YXNldCwgdGhlIEZBU1RRIGRhdGEgY2FuIGJlIHJldHJpZXZlZCBmcm9tIHRoaXMgCltsaW5rXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L1RyYWNlcy9zdHVkeS8/YWNjPVBSSk5BMjY3ODU3Jm89YWNjX3MlM0FhKS4KVGhlIGRhdGEgYXJlIHN0b3JlZCBhcyBvbmUgZmlsZSBwZXIgc2VxdWVuY2luZyBiYXRjaCwgd2l0aCBlYWNoIGZpbGUgCmFwcHJveGltYXRlbHkgMjBHYi4gQXMgc3VjaCwgaXQgd2lsbCBiZSB1bmZlYXNpYmxlIHRvIGRvd25sb2FkIGFuZCBwcm9jZXNzIHRoZXNlCkZBU1RRIGZpbGVzIGluIHRoaXMgcHJhY3RpY2FsIHNlc3Npb24uCgpJbnN0ZWFkLCBmb3IgZGVtb25zdHJhdGl2ZSBwdXJwb3Nlcywgd2UgaGF2ZSB0YWtlbiBhIHN1YnNhbXBsZSBvZiB0aGUgRkFTVFEKZmlsZSBmb3IgdGhlIGZpcnN0IHNlcXVlbmNpbmcgYmF0Y2ggZm9yIHlvdSB0byB3b3JrIHdpdGguIE9uIHRoaXMgc3Vic2FtcGxlLCAKd2UgbWF5IHBlcmZvcm0gYWxsIHRoZSB0YXNrcyB0aGF0IHdlIHdvdWxkIGhhdmUgcGVyZm9ybWVkIG9uIHRoZSBmdWxsIGRhdGFzZXQuClRoZSBzdGVwcyB0aGF0IGFyZSByZXF1aXJlZCBmb3IgZG93bmxvYWRpbmcgYW5kIHF1YW50aWZ5aW5nIGRyb3Atc2VxIGRhdGEKY2FuIGJlIGZvdW5kIGluIFthIFNoZWxsIHNjcmlwdCBvbiBvdXIgY29tcGFuaW9uIEdpdEh1YiByZXBvc2l0b3J5XShodHRwczovL2dpdGh1Yi5jb20vc3RhdE9taWNzL3NpbmdsZUNlbGxDb3Vyc2UvYmxvYi9tYXN0ZXIvbGFiMV9wcmVwcm9jZXNzaW5nL3ByZXByb2Nlc3NEcm9wc2VxLnNoKS4KCiMjIEdFTwoKVGhlIGRhdGFzZXQgZnJvbSBNYWNvc2tvICpldCBhbC4qIHdhcyBhbHNvIHVwbG9hZGVkIGJ5IHRoZSBhdXRob3JzIG9uIHRoZSAKR2VuZSBFeHByZXNzaW9uIE9tbmlidXMgKEdFTykgcGxhdGZvcm0gdW5kZXIgYWNjZXNzaW9uIG51bWJlciAKW0dTRTYzNDcyXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU0U2MzQ3MiksIApmcm9tIHdoaWNoIHJhdyBhbmQgcmVhZGlseS1wcm9jZXNzZWQgZGF0YSBmaWxlcyBtYXkgYmUgcmV0cmlldmVkLCBpbmNsdWRpbmc6CgoxLiAqR1NFNjM0NzJfUkFXLnRhciosIGEgOTAuNkdiIG9iamVjdCB0aGF0IGNvbnRhaW5zIHRoZSAicmF3IGRhdGEiIGZvciB0aGUKZXhwZXJpbWVudC4gSW4gdGhlIHNjUk5BLXNlcSBjb250ZXh0LCBGQVNUUSBmaWxlcyBhcmUgb2Z0ZW4gY29uc2lkZXJlZCB0aGUgcmF3CmRhdGEgZm9ybWF0LgoKMi4gKkdTRTYzNDcyX1AxNFJldGluYV9tZXJnZWRfZGlnaXRhbF9leHByZXNzaW9uLnR4dC5neiosIGEgNTAuN01iIG1hdHJpeCB0aGF0CnN0b3JlcyB0aGUgZ2VuZSBleHByZXNzaW9uIHZhbHVlcyBmb3IgZWFjaCBjZWxsLiBUaGVzZSB2YWx1ZXMgYXJlIGludGVnZXIgCmNvdW50cywgdGhhdCBkaWQgbm90IHVuZGVyZ28gYW55IHR5cGUgb2YgcHJlcHJvY2Vzc2luZyBvciBub3JtYWxpemF0aW9uLgoKMy4gKkdTRTYzNDcyX21tMTBfcmVmZXJlbmNlX21ldGFkYXRhLnRhci5neiosIGEgODYyLjlNYiBjb21wcmVzc2VkIGZvbGRlcgpjb250YWluaW5nIGluZm9ybWF0aW9uIG9uIHRoZSByZWZlcmVuY2UgZ2Vub21lIHRvIHdoaWNoIHRoZSBzY1JOQS1zZXEgcmVhZHMKd2VyZSBhbGlnbmVkIChzZWUgdGhlb3J5IHNsaWRlcykuCgo0LiAqR1NFNjM0NzJfUDE0UmV0aW5hX2xvZ0RHRS50eHQuZ3oqLCBhIDMxNi44TWIgY29tcHJlc3NlZCB0ZXh0IGZpbGUsIG5vdCBjbGVhcgp3aGF0IGl0IGNvbnRhaW5zIChyZXN1bHRzIGZyb20gYSBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIGFuYWx5c2lzLCBidXQKd2l0aCBsb2ctdHJhbnNmb3JtYXRpb24sIHNvIGxvZy1mb2xkIGNoYW5nZXMgbWF5YmU/KS4KCkFzIHN1Y2gsIGJ5IGRvd25sb2FkaW5nICpHU0U2MzQ3Ml9QMTRSZXRpbmFfbWVyZ2VkX2RpZ2l0YWxfZXhwcmVzc2lvbi50eHQuZ3oqLAp3ZSBhdm9pZCByZS1xdWFudGlmeWluZyB0aGUgZGF0YSwgaS5lLiwgdGhlIHRyYW5zbGF0aW9uIGZyb20gcmVhZHMgZnJvbSB0aGUKRkFTVFEgZmlsZXMgaW50byBhIGdlbmUtbGV2ZWwgZXhwcmVzc2lvbiB2YWx1ZXMgZm9yIGVhY2ggY2VsbC4gCgpPbmUgaXNzdWUgdGhhdCBvZnRlbiBhcmlzZXMgZnJvbSBkYXRhIGRvd25sb2FkZWQgZnJvbSBHRU8sIGlzIHRoYXQgdGhlcmUgaXMgbm8Kc3RyaWN0IHJlcXVpcmVtZW50cyBmb3Igd2hpY2ggZGF0YSBzaG91bGQgYmUgaW5jbHVkZWQgaW4gdGhlIHVwbG9hZCBieSB0aGUgCmF1dGhvcnMuIEFzIHN1Y2gsIGZyb20gbXkgcGVyc29uYWwgZXhwZXJpZW5jZSwgaXQgY2FuIG9mdGVuIGJlIHRoZSBjYXNlIHRoYXQKaW1wb3J0YW50IGluZm9ybWF0aW9uIGxpa2UgbWV0YWRhdGEgYXJlIG1pc3NpbmcsIG9yIHRoZSBjb250ZW50IG9mIHRoZSBzdWJtaXR0ZWQKZmlsZXMgaXMgdW5jbGVhci4gRXZlbiBpZiBhbGwgdGhlIHJlcXVpcmVkIGRhdGEgYXJlIGF2YWlsYWJsZSwgYXMgaXMgdGhlIGNhc2UgCmhlcmUsIHdlIHdvdWxkIHN0aWxsIG5lZWQgdG8gcGllY2UgYWxsIHRoZSBpbmZvcm1hdGlvbiB0b2dldGhlciBmcm9tIGRpZmZlcmVudCAKZmlsZXMgYW5kIGZpbGUgZm9ybWF0cyBiZWZvcmUgd2UgY2FuIHVzZSB0aGVtLgoKIyMgRXhwZXJpbWVudEh1YgoKVGhlIEJpb2NvbmR1Y3RvciAqRXhwZXJpbWVudEh1Yiogd2ViIHJlc291cmNlLCB3aGljaCBjYW4gYmUgYWNjZXNzZWQgdXNpbmcgdGhlIApbRXhwZXJpbWVudEh1Yl0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL0V4cGVyaW1lbnRIdWIuaHRtbCkgClIgcGFja2FnZSwgcHJvdmlkZXMgYSBjZW50cmFsIGxvY2F0aW9uIHdoZXJlIGN1cmF0ZWQgZGF0YSBmcm9tIGV4cGVyaW1lbnRzLCAKcHVibGljYXRpb25zIG9yIHRyYWluaW5nIGNvdXJzZXMgY2FuIGJlIGFjY2Vzc2VkLiBXaGlsZSBpdCBjb250YWlucyBmYXIgbGVzcwpkYXRhc2V0cyB0aGFuIHRoZSBTUkEgb3IgR0VPICg0OTY1IHJlY29yZHMgdG8gZGF0ZSksIHRoZXNlIGRhdGFzZXRzIGFsbCBmb2xsb3cgCnRoZSB0aWR5IGRhdGEgZm9ybWF0IG9mIEJpb2NvbmR1Y3Rvci4gTm90ZSB0aGF0IHRoZSBFeHBlcmltZW50SHViIGNvbnRhaW5zIApzZXZlcmFsIHR5cGVzIG9mIGRhdGEsIGxpa2UgYnVsayBhbmQgc2luZ2xlLWNlbGwgdHJhbnNjcmlwdG9taWNzIGRhdGEsIAptaWNyb2FycmF5cyBhbmQgbW9yZS4KClRoZSBNYWNvc2tvIGRhdGFzZXQgaXMgYXZhaWxhYmxlIGZyb20gRXhwZXJpbWVudEh1YiBhbmQgY2FuIGJlIGFjY2Vzc2VkIGFzCmZvbGxvd3M6CgpgYGB7ciwgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShFeHBlcmltZW50SHViKQplZGIgPC0gRXhwZXJpbWVudEh1YigpCgplZGJbZ3JlcCgiTWFjb3NrbyIsIGVkYiR0aXRsZSldICMgZmluZCBhY2Nlc3Npb24gbnVtYmVyIChjYW4gYmUgaW5lZmZpY2llbnQpCgplZGJfY291bnRzIDwtIGVkYltbIkVIMjY5MCJdXQplZGJfY291bnRzWzE6NSwxOjVdCgplZGJfY29sZGF0YSA8LSBlZGJbWyJFSDI2OTEiXV0KZWRiX2NvbGRhdGFbMTo1LF0KCnJtKGVkYiwgZWRiX2NvdW50cywgZWRiX2NvbGRhdGEpCmBgYAoKIyMgc2NSTkFTZXEKCkluIGFkZGl0aW9uIHRvIEV4cGVyaW1lbnRIdWIsIEJpb2NvbmR1Y3RvciBwcm92aWRlcyB0aGUgcGFja2FnZQpbc2NSTkFzZXFdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2RhdGEvZXhwZXJpbWVudC9odG1sL3NjUk5Bc2VxLmh0bWwpLgpUaGlzIHBhY2thZ2UgcHJvdmlkZXMgYW4gZXZlbiBtb3JlIHVzZXItZnJpZW5kbHkgY2xpZW50IHRvIGFjY2VzcyAob25seSkgCnNjUk5BLXNlcSBkYXRhc2V0cyBmcm9tIHRoZSBFeHBlcmltZW50SHViIHdlYiByZXNvdXJjZS4gRGF0YSByZXRyaWV2ZWQgdXNpbmcgdGhlCnNjUk5Bc2VxIHBhY2thZ2UgYXJlIHN0b3JlZCBhcyB1c2VyLWZyaWVuZGx5IGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0cywgCndpdGggdGhlIGV4cHJlc3Npb24gZGF0YSwgZ2VuZS1sZXZlbCBpbmZvcm1hdGlvbiwgY2VsbC1sZXZlbCBpbmZvcm1hdGlvbiBhbmQgCmV4cGVyaW1lbnQgbWV0YWRhdGEgYWxsIGluIHBsYWNlIGluIG9uZSBkYXRhIG9iamVjdC4gVGhlIHNjUk5BLXNlcSBwYWNrYWdlCmN1cnJlbnRseSBob2xkcyA2MSBkYXRhc2V0cywgaW5jbHVkaW5nIHRoZSBkYXRhIGZyb20gKk1hY29za28gZXQgYWwuKjoKCmBgYHtyLCBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHNjUk5Bc2VxKQpzY1JOQXNlcTo6TWFjb3Nrb1JldGluYURhdGEoKQpgYGAKCiMgSW1wb3J0IGRhdGEKClRoZSBgc2NSTkFzZXFgIHBhY2thZ2UgcHJvdmlkZXMgY29udmVuaWVudCBhY2Nlc3MgdG8gc2V2ZXJhbCBkYXRhc2V0cy4gU2VlIHRoZSBbcGFja2FnZSBCaW9jb25kdWN0b3IgcGFnZV0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9kYXRhL2V4cGVyaW1lbnQvaHRtbC9zY1JOQXNlcS5odG1sKSAKZm9yIG1vcmUgaW5mb3JtYXRpb24uCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBDb2RlIGJlbG93IG1pZ2h0IGFzayB5b3UgdG8gY3JlYXRlIGFuIEV4cGVyaW1lbnRIdWIgZGlyZWN0b3J5LiAKIyBUeXBlICd5ZXMnIGFuZCBoaXQgRW50ZXIsIHRvIGFsbG93IHRoaXMuCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KHNjUk5Bc2VxKSkKc2NlIDwtIE1hY29za29SZXRpbmFEYXRhKCkKYGBgCgojIEEgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3QKCmBgYHtyLCBldmFsPUZBTFNFfQpzY2UKYGBgCgojIyBBY2Nlc3NpbmcgZGF0YSBmcm9tIGEgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3QKClBsZWFzZSBzZWUgW0ZpZ3VyZSA0LjEgaW4gT1NDQV0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvcmVsZWFzZS9PU0NBL2RhdGEtaW5mcmFzdHJ1Y3R1cmUuaHRtbCkgCmZvciBhbiBvdmVydmlldyBvZiBhIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0LgoKYGBge3IsIGV2YWw9RkFMU0V9CiMgRGF0YTogYXNzYXlzCmFzc2F5cyhzY2UpCmFzc2F5cyhzY2UpJGNvdW50c1sxOjUsIDE6NV0KCiMgRmVhdHVyZSBtZXRhZGF0YTogcm93RGF0YQpyb3dEYXRhKHNjZSkgIyBlbXB0eSBmb3Igbm93CgojIENlbGwgbWV0YWRhdGE6IGNvbERhdGEKY29sRGF0YShzY2UpCgojIFJlZHVjZWQgZGltZW5zaW9uczogcmVkdWNlZERpbXMKcmVkdWNlZERpbXMoc2NlKSAjIGVtcHR5IGZvciBub3cKYGBgCgojIyBDcmVhdGluZyBhIG5ldyBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIG9iamVjdAoKYGBge3IsIGV2YWw9RkFMU0V9CnNjZU5ldyA8LSBTaW5nbGVDZWxsRXhwZXJpbWVudChhc3NheXMgPSBsaXN0KGNvdW50cyA9IGFzc2F5cyhzY2UpJGNvdW50cykpCnNjZU5ldwoKcm0oc2NlTmV3KQpgYGAKCiMjIFN0b3JpbmcgKG1ldGEpZGF0YSBpbiBhIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0CgpgYGB7ciwgZXZhbD1GQUxTRX0KZmFrZUdlbmVOYW1lcyA8LSBwYXN0ZTAoImdlbmUiLCAxOm5yb3coc2NlKSkKcm93RGF0YShzY2UpJGZha2VOYW1lIDwtIGZha2VHZW5lTmFtZXMKaGVhZChyb3dEYXRhKHNjZSkpCiMgUmVtb3ZlIGFnYWluIGJ5IHNldHRpbmcgdG8gTlVMTApyb3dEYXRhKHNjZSkkZmFrZU5hbWUgPC0gTlVMTAoKYXNzYXlzKHNjZSkkbG9nQ291bnRzIDwtIGxvZzFwKGFzc2F5cyhzY2UpJGNvdW50cykKYXNzYXlzKHNjZSkKYXNzYXlzKHNjZSkkbG9nQ291bnRzWzE6NSwgMTo1XQphc3NheXMoc2NlKSRsb2dDb3VudHMgPC0gTlVMTApgYGAKCiMgT2J0YWluaW5nIGFuZCBpbmNsdWRpbmcgcm93RGF0YQoKVGhlIGByb3dEYXRhYCBzbG90IG9mIGEgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3QgYWxsb3dzIGZvciBzdG9yaW5nIAppbmZvcm1hdGlvbiBvbiB0aGUgZmVhdHVyZXMsIGkuZS4gdGhlIGdlbmVzLCBpbiBhIGRhdGFzZXQuIEluIG91ciBvYmplY3QsCnRoZSBgcm93RGF0YWAgc2xvdCBpcyBlbXB0eS4KCmBgYHtyLCBldmFsPUZBTFNFfQpyb3dEYXRhKHNjZSkKYGBgCgpBcyBzdWNoLCB0aGUgb25seSBpbmZvcm1hdGlvbiB3ZSBoYXZlIG9uIHRoZSBnZW5lcyBhcmUgdGhlaXIgbmFtZXMsIHdoaWNoIGNhbgpiZSByZXRyaWV2ZWQgYXMgdGhlIGByb3duYW1lc2Agb2YgdGhlIGV4cHJlc3Npb24gbWF0cml4LgoKYGBge3IsIGV2YWw9RkFMU0V9CmhlYWQocm93bmFtZXMoc2NlKSkKYGBgCgpUaGVzZSBhcmUgdGhlIGdlbmUgbmFtZXMgKHN5bWJvbHMpLiBOb3RlIHRoYXQgaXQgbWF5IGJlIHVzZWZ1bCB0byBpbmNsdWRlIAphZGRpdGlvbmFsIGluZm9ybWF0aW9uIGluIHRoZSBgcm93RGF0YWAgc2xvdC4gRm9yIGluc3RhbmNlLCB3ZSBtYXkgd2FudCB0byAKc3RvcmU6CgotIFVuYW1iaWd1b3VzIGdlbmUgaWRlbnRpZmllcnMgKGUuZy4gZnJvbSBFTlNFTUJMKQotIE9uIHdoaWNoIGNocm9tb3NvbWUgdGhlIGdlbmUgaXMgbG9jYXRlZAotIEdlbmUgbGVuZ3RoIChnZW5vbWljIHN0YXJ0IHBvc2l0aW9uIGFuZCBlbmQgcG9zaXRpb24pCi0gT3RoZXJzLi4uCgpgYGB7ciwgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSgiYmlvbWFSdCIpCgplbnNlbWJsNzUgPC0gdXNlRW5zZW1ibChiaW9tYXJ0ID0gJ2dlbmVzJywgCiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFzZXQgPSAnbW11c2N1bHVzX2dlbmVfZW5zZW1ibCcsCiAgICAgICAgICAgICAgICAgICAgICAgIHZlcnNpb24gPSA3NSkKCmhlYWQobGlzdEF0dHJpYnV0ZXMoZW5zZW1ibDc1KSkgIyBwb3RlbnRpYWwgaW5mbyB0byBleHRyYWN0CgpnZW5lSW5mbyA8LSBnZXRCTShhdHRyaWJ1dGVzID0gYygiZW5zZW1ibF9nZW5lX2lkIiwgIyBFTlNFTUJMIHVuYW1iaWd1b3VzIGlkZW50aWZpZXIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm1naV9zeW1ib2wiLCAjIEdlbmUgc3ltYm9sICh0byBsaW5rIHdpdGggU0NFIHJvd25hbWVzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNocm9tb3NvbWVfbmFtZSIsICMgT24gd2hpY2ggY2hyb21vc29tZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RhcnRfcG9zaXRpb24iLCAjIFN0YXJ0IHBvc2l0aW9uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJlbmRfcG9zaXRpb24iKSwjIEVuZCBwb3NpdGlvbgogICAgICAgICAgICAgICAgICBtYXJ0ID0gZW5zZW1ibDc1KQoKaGVhZChnZW5lSW5mbykKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KZ2VuZUluZm8kbWdpX3N5bWJvbF91cHBlciA8LSB0b3VwcGVyKGdlbmVJbmZvJG1naV9zeW1ib2wpIAojIG1hdGNoIGJldHdlZW4gZ2VuZSBpbmZvIGFuZCByb3duYW1lcwoKc3VtKHJvd25hbWVzKHNjZSkgJWluJSBnZW5lSW5mbyRtZ2lfc3ltYm9sX3VwcGVyKQpzdW0oIXJvd25hbWVzKHNjZSkgJWluJSBnZW5lSW5mbyRtZ2lfc3ltYm9sX3VwcGVyKSAjIGxvc3QgaW4gY29udmVyc2lvbiA6KAoKcm93RGF0YShzY2UpIDwtIGdlbmVJbmZvW21hdGNoKHJvd25hbWVzKHNjZSksZ2VuZUluZm8kbWdpX3N5bWJvbF91cHBlciksXQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpoZWFkKHJvd0RhdGEoc2NlKSkKYGBgCgojIEZpbHRlcmluZyBub24taW5mb3JtYXRpdmUgZ2VuZXMKCmBgYHtyLCBldmFsPUZBTFNFfQpsaWJyYXJ5KGVkZ2VSKQoKIyBBIHZlcnkgc2ltcGxlIHN0cmF0ZWd5OiByZW1vdmUgYWxsIGdlbmVzIHRoYXQgYXJlIGV4cHJlc3NlZCBpbiBsZXNzIHRoYW4gMTAKIyBvdXQgb2YgNDkzMDAgY2VsbHMgLT4gbm90ZSB0aGF0IHRoaXMgYSB2ZXJ5IGxlbmllbnQgZmlsdGVyaW5nIGNyaXRlcml1bQprZWVwIDwtIHJvd1N1bXMoYXNzYXlzKHNjZSkkY291bnRzID4gMCkgPiAxMAp0YWJsZShrZWVwKQoKc2NlIDwtIHNjZVtrZWVwLF0KYGBgCgpOb3RlIHRoYXQgZGVkaWNhdGVkIGZ1bmN0aW9ucyBmb3IgZmlsdGVyaW5nIG91dCBsb3dseSBleHByZXNzZWQgZ2VuZXMgZXhpc3QuCk9uZSBzdWNoIGZ1bmN0aW9uIGlzIHRoZSBgZmlsdGVyQnlFeHByYCBmdW5jdGlvbiBvZiB0aGUgZWRnZVIgUiBwYWNrYWdlLgpJbiBicmllZiwgdGhlIHN0cmF0ZWd5IGtlZXBzIGdlbmVzIHRoYXQgaGF2ZSBhdCBsZWFzdCAibWluLmNvdW50IiByZWFkcyBpbiBhIAp3b3J0aHdoaWxlIG51bWJlciBzYW1wbGVzLiAKCk1vcmUgcHJlY2lzZWx5LCB0aGUgZmlsdGVyaW5nIGtlZXBzIGdlbmVzIHRoYXQgaGF2ZSBjb3VudC1wZXItbWlsbGlvbiAoQ1BNKSAKYWJvdmUgayBpbiBuIHNhbXBsZXMuIAoKayBpcyBkZXRlcm1pbmVkIGJ5IHRoZSBtaW4uY291bnQgYXJndW1lbnQgdG8gdGhlIGZ1bmN0aW9uLCBhbmQgYnkgdGhlIHNhbXBsZSAKbGlicmFyeSBzaXplcy4KCm4gaXMgZGV0ZXJtaW5lZCBieSB0aGUgZGVzaWduIG1hdHJpeC4gbiBjYW4gYmUgc2VlbiBhcyB0aGUgc21hbGxlc3QgZ3JvdXAgc2FtcGxlCnNpemUuIEEgYGdyb3VwYCBvZiBzYW1wbGVzL2NlbGxzIGNhbiBiZSBkZWZpbmVkIGFzIGNlbGxzIHRoYXQgYXJlIG1vcmUgc2ltaWxhcgp0byBvbmUgYW5vdGhlciwgZS5nLiwgZnJvbSB0aGUgc2FtZSBzZXF1ZW5jaW5nIGJhdGNoLCB0aGUgc2FtZSBwYXRpZW50Li4uLgpIZXJlIHdlIGNvdWxkIGFsc28gdXNlIHRoZSBjbHVzdGVyIGFzc2lnbm1lbnQgKGNlbGwgdHlwZSkgZm9yIGVhY2ggY2VsbCBhcyB0aGUKZ3JvdXBpbmcgdmFyaWFibGU7ICpub3RlIGhvd2V2ZXIsIHRoYXQgdGhpcyB1c3VhbGx5IGlzIG5vdCBhdmFpbGFibGUgdW50aWwgYSoKKmxhdGVyIHN0YWdlIGluIHRoZSBhbmFseXNpcyBwaXBlbGluZSogKGkuZS4sIGFmdGVyIGRpbWVuc2lvbiByZWR1Y3Rpb24gYW5kIApjbHVzdGVyaW5nLCB0b3BpY3Mgd2Ugd2lsbCBjb3ZlciBuZXh0IHNlc3Npb24uKQoKSWYgYWxsIHRoZSBncm91cCBzaXplcyBhcmUgbGFyZ2VyIHRoYW4gdGhlIGBsYXJnZS5uYCBhcmd1bWVudCBvZiB0aGUgCmZpbHRlckJ5RXhwciBmdW5jdGlvbiwgd2hpY2ggZGVmYXVsdHMgdG8gMTAsIHRoZW4gbiB3aWxsIGJlIHRha2VuIGFzCmBtaW4ucHJvcGAqIHRoZSB0aGUgbnVtYmVyIG9mIHNhbXBsZXMvY2VsbHMgaW4gdGhlIHNtYWxsZXN0IGdyb3VwLgoKTm90ZSB0aGF0IGFsbCB0aGUgZ3JvdXAgc2l6ZXMgd2lsbCBvZnRlbiBiZSBsYXJnZXIgdGhhbiB0aGUgYGxhcmdlLm5gIGluIGNhc2UKb2Ygc2luZ2xlLWNlbGwgZGF0YS4KCmBgYHtyLCBldmFsPUZBTFNFfQojIFNsb3cgKG1vcmUgdGhhbiAxbWluKSAtPiBkbyBub3QgcnVuCiMga2VlcDIgPC0gZmlsdGVyQnlFeHByKHNjZSwKIyAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBzY2UkY2x1c3RlciwKIyAgICAgICAgICAgICAgICAgICAgICAgbWluLmNvdW50ID0gMSwKIyAgICAgICAgICAgICAgICAgICAgICAgbWluLnRvdGFsLmNvdW50ID0gMTUsCiMgICAgICAgICAgICAgICAgICAgICAgIG1pbi5wcm9wID0gMC4yKQojIHRhYmxlKGtlZXAyKQpgYGAKCgojIFF1YWxpdHkgY29udHJvbAoKSW4gcXVhbGl0eSBjb250cm9sIChRQyksIHdlIGNoZWNrIHRoZSBxdWFsaXR5IG9mIG91ciBkYXRhc2V0LiBJbiBwYXJ0aWN1bGFyLCAKd2UgaW52ZXN0aWdhdGUgdW5kZXNpcmFibGUgb2RkaXRpZXMsIHN1Y2ggYXMgbG93LXF1YWxpdHkgY2VsbHMsIGVtcHR5IGRyb3BsZXRzCm9yIGRvdWJsZXRzLgoKIyMgSWRlbnRpZnlpbmcgYW5kIHJlbW92aW5nIGxvdy1xdWFsaXR5IGNlbGxzCgpUaGVyZSBhcmUgc2V2ZXJhbCBkaXN0aW5ndWlzaGluZyBmZWF0dXJlcyBvZiBsb3ctcXVhbGl0eSBjZWxscyB0aGF0IGNhbiBiZSB1c2VkCmluIG9yZGVyIHRvIGlkZW50aWZ5IHRoZW0uIFtBcyBkZXNjcmliZWQgaW4gdGhlIE9TQ0EgYm9va10oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xNC9PU0NBLmJhc2ljL3F1YWxpdHktY29udHJvbC5odG1sKSAKYm9vayk6CgoxLiBUaGUgbGlicmFyeSBzaXplIGlzIGRlZmluZWQgYXMgdGhlIHRvdGFsIHN1bSBvZiBjb3VudHMgYWNyb3NzIGFsbCAKcmVsZXZhbnQgZmVhdHVyZXMgZm9yIGVhY2ggY2VsbC4gSGVyZSwgd2Ugd2lsbCBjb25zaWRlciB0aGUgcmVsZXZhbnQgZmVhdHVyZXMgCnRvIGJlIHRoZSBlbmRvZ2Vub3VzIGdlbmVzLiBDZWxscyB3aXRoIHNtYWxsIGxpYnJhcnkgc2l6ZXMgYXJlIG9mIGxvdyBxdWFsaXR5IAphcyB0aGUgUk5BIGhhcyBiZWVuIGxvc3QgYXQgc29tZSBwb2ludCBkdXJpbmcgbGlicmFyeSBwcmVwYXJhdGlvbiwgZWl0aGVyIGR1ZSAKdG8gY2VsbCBseXNpcyBvciBpbmVmZmljaWVudCBjRE5BIGNhcHR1cmUgYW5kIGFtcGxpZmljYXRpb24uCgoyLiBUaGUgbnVtYmVyIG9mIGV4cHJlc3NlZCBmZWF0dXJlcyBpbiBlYWNoIGNlbGwgaXMgZGVmaW5lZCBhcyB0aGUgbnVtYmVyIG9mIAplbmRvZ2Vub3VzIGdlbmVzIHdpdGggbm9uLXplcm8gY291bnRzIGZvciB0aGF0IGNlbGwuIEFueSBjZWxsIHdpdGggdmVyeSAKZmV3IGV4cHJlc3NlZCBnZW5lcyBpcyBsaWtlbHkgdG8gYmUgb2YgcG9vciBxdWFsaXR5IGFzIHRoZSBkaXZlcnNlIHRyYW5zY3JpcHQgCnBvcHVsYXRpb24gaGFzIG5vdCBiZWVuIHN1Y2Nlc3NmdWxseSBjYXB0dXJlZC4KCjMuIFdlIHNvbWV0aW1lcyBoYXZlIHNwaWtlLWluIChFUkNDKSB0cmFuc2NyaXB0cyBhdmFpbGFibGUuClRoZSBwcm9wb3J0aW9uIG9mIHJlYWRzIG1hcHBlZCB0byBzcGlrZS1pbiB0cmFuc2NyaXB0cyBpcyBjYWxjdWxhdGVkIHJlbGF0aXZlIAp0byB0aGUgdG90YWwgY291bnQgYWNyb3NzIGFsbCBmZWF0dXJlcyAoaW5jbHVkaW5nIHNwaWtlLWlucykgZm9yIGVhY2ggY2VsbC4gCkFzIHRoZSBzYW1lIGFtb3VudCBvZiBzcGlrZS1pbiBSTkEgc2hvdWxkIGhhdmUgYmVlbiBhZGRlZCB0byBlYWNoIGNlbGwsIAphbnkgZW5yaWNobWVudCBpbiBzcGlrZS1pbiBjb3VudHMgaXMgc3ltcHRvbWF0aWMgb2YgbG9zcyBvZiBlbmRvZ2Vub3VzIFJOQS4KCjQuIEluIHRoZSBhYnNlbmNlIG9mIHNwaWtlLWluIHRyYW5zY3JpcHRzLCB0aGUgcHJvcG9ydGlvbiBvZiByZWFkcyBtYXBwZWQgCnRvIGdlbmVzIGluIHRoZSBtaXRvY2hvbmRyaWFsIGdlbm9tZSBjYW4gYmUgdXNlZC4gSGlnaCBwcm9wb3J0aW9ucyBhcmUgCmluZGljYXRpdmUgb2YgcG9vci1xdWFsaXR5IGNlbGxzIChJc2xhbSBldCBhbC4gMjAxNDsgSWxpY2ljIGV0IGFsLiAyMDE2KSwgCnByZXN1bWFibHkgYmVjYXVzZSBvZiBsb3NzIG9mIGN5dG9wbGFzbWljIFJOQSBmcm9tIHBlcmZvcmF0ZWQgY2VsbHMuIApUaGUgcmVhc29uaW5nIGlzIHRoYXQsIGluIHRoZSBwcmVzZW5jZSBvZiBtb2Rlc3QgZGFtYWdlLCB0aGUgaG9sZXMgaW4gdGhlIApjZWxsIG1lbWJyYW5lIHBlcm1pdCBlZmZsdXggb2YgaW5kaXZpZHVhbCB0cmFuc2NyaXB0IG1vbGVjdWxlcyBidXQgYXJlIHRvbyAKc21hbGwgdG8gYWxsb3cgbWl0b2Nob25kcmlhIHRvIGVzY2FwZSwgbGVhZGluZyB0byBhIHJlbGF0aXZlIGVucmljaG1lbnQgb2YgCm1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdHMuIEZvciBzaW5nbGUtbnVjbGVpIFJOQS1zZXEgZXhwZXJpbWVudHMsIGhpZ2ggCnByb3BvcnRpb25zIGFyZSBhbHNvIHVzZWZ1bCBhcyB0aGV5IGNhbiBtYXJrIGNlbGxzIHdoZXJlIHRoZSBjeXRvcGxhc20gaGFzIApub3QgYmVlbiBzdWNjZXNzZnVsbHkgc3RyaXBwZWQuCgojIyBDYWxjdWxhdGUgUUMgdmFyaWFibGVzCgpUaGlzIGZ1bmN0aW9uIGNhbGN1bGF0ZXMgdXNlZnVsIFFDIG1ldHJpY3MgZm9yIGlkZW50aWZpY2F0aW9uIGFuZCByZW1vdmFsIG9mIApwb3RlbnRpYWxseSBwcm9ibGVtYXRpYyBjZWxscy4gRGVmYXVsdCBwZXItY2VsbCBtZXRyaWNzIGFyZSB0aGUgc3VtIG9mIGNvdW50cyAKKGkuZS4sIHRoZSBsaWJyYXJ5IHNpemUpIGFuZCB0aGUgbnVtYmVyIG9mIGRldGVjdGVkIGZlYXR1cmVzLiBUaGUgcGVyY2VudGFnZSBvZiAKY291bnRzIGluIHRoZSB0b3AgZmVhdHVyZXMgYWxzbyBwcm92aWRlcyBhIG1lYXN1cmUgb2YgbGlicmFyeSBjb21wbGV4aXR5LgoKSWYgc3Vic2V0cyBpcyBzcGVjaWZpZWQsIHRoZXNlIHN0YXRpc3RpY3MgYXJlIGFsc28gY29tcHV0ZWQgZm9yIGVhY2ggc3Vic2V0IG9mIApmZWF0dXJlcy4gVGhpcyBpcyB1c2VmdWwgZm9yIGludmVzdGlnYXRpbmcgZ2VuZSBzZXRzIG9mIGludGVyZXN0LCBlLmcuLCAKbWl0b2Nob25kcmlhbCBnZW5lcy4KCmBgYHtyLCBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHNjYXRlcikKCiMgY2hlY2sgRVJDQyBzcGlrZS1pbiB0cmFuc2NyaXB0cwpzdW0oZ3JlcGwoIl5FUkNDLSIsIHJvd25hbWVzKHNjZSkpKSAjIG5vIHNwaWtlLWluIHRyYW5zY3JpcHRzIGF2YWlsYWJsZQoKIyBjaGVjayBtaXRvY2hvbmRyaWFsIGdlbmVzCnN1bShyb3dEYXRhKHNjZSkkY2hyb21vc29tZV9uYW1lPT0iTVQiLG5hLnJtID0gVFJVRSkgIyAyOCBtaXRvY2hvbmRyaWFsIGdlbmVzCnN1bShncmVwbCgiXk1ULSIsIHJvd25hbWVzKHNjZSkpKSAjIGFsdGVybmF0aXZlbHkKaXMubWl0byA8LSBncmVwbCgiXk1ULSIsIHJvd25hbWVzKHNjZSkpCgojIyBjYWxjdWxhdGUgUUMgbWV0cmljcwpkZiA8LSBwZXJDZWxsUUNNZXRyaWNzKHNjZSwgc3Vic2V0cz1saXN0KE1pdG89aXMubWl0bykpCmhlYWQoZGYpCgojIGFkZCBRQyB2YXJpYWJsZXMgdG8gc2NlIG9iamVjdApjb2xEYXRhKHNjZSkgPC0gY2JpbmQoY29sRGF0YShzY2UpLCBkZikKCiMgdGhlIFFDIHZhcmlhYmxlcyBoYXZlIG5vdyBiZWVuIGFkZGVkIHRvIHRoZSBjb2xEYXRhIG9mIG91ciBTQ0Ugb2JqZWN0LgpoZWFkKGNvbERhdGEoc2NlKSkKYGBgCgojIyBFeHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzCgpJbiB0aGUgZmlndXJlIGJlbG93LCB3ZSBzZWUgdGhhdCBzZXZlcmFsIGNlbGxzIGhhdmUgYSB2ZXJ5IGxvdyBudW1iZXIgb2YgCmV4cHJlc3NlZCBnZW5lcywgYW5kIHdoZXJlIG1vc3Qgb2YgdGhlIG1vbGVjdWxlcyBhcmUgZGVyaXZlZCBmcm9tIAptaXRvY2hvbmRyaWFsIGdlbmVzLiBUaGlzIGluZGljYXRlcyBsaWtlbHkgZGFtYWdlZCBjZWxscywgcHJlc3VtYWJseSBiZWNhdXNlIApvZiBsb3NzIG9mIGN5dG9wbGFzbWljIFJOQSBmcm9tIHBlcmZvcmF0ZWQgY2VsbHMsIHNvIHdlIHNob3VsZCByZW1vdmUgdGhlc2UgZm9yIAp0aGUgZG93bnN0cmVhbSBhbmFseXNpcy4KCmBgYHtyLCBldmFsPUZBTFNFfQojIE51bWJlciBvZiBnZW5lcyB2cyBsaWJyYXJ5IHNpemUKcGxvdENvbERhdGEoc2NlLCB4ID0gInN1bSIsIHk9ImRldGVjdGVkIiwgY29sb3VyX2J5PSJjbHVzdGVyIikgCgojIE1pdG9jaG9uZHJpYWwgZ2VuZXMKcGxvdENvbERhdGEoc2NlLCB4ID0gImRldGVjdGVkIiwgeT0ic3Vic2V0c19NaXRvX3BlcmNlbnQiKQpgYGAKCiMjIFFDIHVzaW5nIGFkYXB0aXZlIHRocmVzaG9sZHMKCkJlbG93LCB3ZSByZW1vdmUgY2VsbHMgdGhhdCBhcmUgb3V0bHlpbmcgd2l0aCByZXNwZWN0IHRvCgogMS4gQSBsb3cgc2VxdWVuY2luZyBkZXB0aCAobnVtYmVyIG9mIFVNSXMpOwogMi4gQSBsb3cgbnVtYmVyIG9mIGdlbmVzIGRldGVjdGVkOwogMy4gQSBoaWdoIHBlcmNlbnRhZ2Ugb2YgcmVhZHMgZnJvbSBtaXRvY2hvbmRyaWFsIGdlbmVzLgogCiBIZXJlIHdlIHdpbGwgcmVtb3ZlIGNlbGxzIGZvciBRQyBiYXNlZCBvbiAqKmFkYXB0aXZlIHRocmVzaG9sZHMqKiByZWxhdGVkIHRvCiB0aGUgdGhyZWUgcG9pbnRzIGZyb20gYWJvdmUuIEFkYXB0aXZlIHRyaGVzaG9sZHMgYXJlIHVzZWQgYXMgb3Bwb3NlZCB0bwogKipmaXhlZCB0aHJlc2hvbGRzKiouIAogCldpdGggZml4ZWQgdGhyZXNob2xkcywgd2UgdXNlIGZpeGVkIGN1dC1vZmYgdmFsdWVzIGZvciBlYWNoIGNlbGwgdG8gcGFzcyBRQywgCmUuZy4sIHdlIG1pZ2h0IGNvbnNpZGVyIGNlbGxzIHRvIGJlIGxvdyBxdWFsaXR5IGlmIHRoZXkgaGF2ZSBsaWJyYXJ5IHNpemVzIApiZWxvdyAxMDAsMDAwIHJlYWRzOyBleHByZXNzIGZld2VyIHRoYW4gNSwwMDAgZ2VuZXM7IGhhdmUgc3Bpa2UtaW4gcHJvcG9ydGlvbnMKYWJvdmUgMTAlOyBvciBoYXZlIG1pdG9jaG9uZHJpYWwgcHJvcG9ydGlvbnMgYWJvdmUgMTAlLgoKV2l0aCBhZGFwdGl2ZSB0aHJlc2hvbGRzLCB3ZSBhc3N1bWUgdGhhdCBtb3N0IG9mIHRoZSBkYXRhc2V0IGNvbnNpc3RzIG9mIApoaWdoLXF1YWxpdHkgY2VsbHMuIFdlIHRoZW4gaWRlbnRpZnkgY2VsbHMgdGhhdCBhcmUgb3V0bGllcnMgZm9yIHRoZSB2YXJpb3VzIApRQyBtZXRyaWNzLCBiYXNlZCBvbiB0aGUgbWVkaWFuIGFic29sdXRlIGRldmlhdGlvbiAoTUFEKSBmcm9tIHRoZSBtZWRpYW4gdmFsdWUKb2YgZWFjaCBtZXRyaWMgYWNyb3NzIGFsbCBjZWxscy4gQnkgZGVmYXVsdCwgd2UgY29uc2lkZXIgYSB2YWx1ZSB0byBiZSBhbm91dGxpZXIgCmlmIGl0IGlzIG1vcmUgdGhhbiAzIE1BRHMgZnJvbSB0aGUgbWVkaWFuIGluIHRoZSDigJxwcm9ibGVtYXRpY+KAnSBkaXJlY3Rpb24uIFRoaXMgCmlzIGxvb3NlbHkgbW90aXZhdGVkIGJ5IHRoZSBmYWN0IHRoYXQgc3VjaCBhIGZpbHRlciB3aWxsIHJldGFpbiA5OSUgb2YgCm5vbi1vdXRsaWVyIHZhbHVlcyB0aGF0IGZvbGxvdyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFdlIGRlbW9uc3RyYXRlIGFkb3B0aW5nCmFkYXB0aXZlIHRocmVzaG9sZHMgb24gdGhlIE1hY29za28gZGF0YXNldDoKCmBgYHtyLCBldmFsPUZBTFNFfQpsb3dMaWIgPC0gaXNPdXRsaWVyKGRmJHN1bSwgdHlwZT0ibG93ZXIiLCBsb2c9VFJVRSkKbG93RmVhdHVyZXMgPC0gaXNPdXRsaWVyKGRmJGRldGVjdGVkLCB0eXBlPSJsb3dlciIsIGxvZz1UUlVFKQpoaWdoTWl0byA8LSBpc091dGxpZXIoZGYkc3Vic2V0c19NaXRvX3BlcmNlbnQsIHR5cGU9ImhpZ2hlciIpCgp0YWJsZShsb3dMaWIpCnRhYmxlKGxvd0ZlYXR1cmVzKQp0YWJsZShoaWdoTWl0bykKCmRpc2NhcmRDZWxscyA8LSAobG93TGliIHwgbG93RmVhdHVyZXMgfCBoaWdoTWl0bykKdGFibGUoZGlzY2FyZENlbGxzKQpjb2xEYXRhKHNjZSkkZGlzY2FyZENlbGxzIDwtIGRpc2NhcmRDZWxscwoKIyB2aXN1YWxpemUgY2VsbHMgdG8gYmUgcmVtb3ZlZApwbG90Q29sRGF0YShzY2UsIHggPSAic3VtIiwgeT0iZGV0ZWN0ZWQiLCBjb2xvdXJfYnk9ImRpc2NhcmRDZWxscyIpCnBsb3RDb2xEYXRhKHNjZSwgeCA9ICJkZXRlY3RlZCIsIHk9InN1YnNldHNfTWl0b19wZXJjZW50IiwgY29sb3VyX2J5ID0gImRpc2NhcmRDZWxscyIpCmBgYAoKV2UgcmVtb3ZlZCBhIHRvdGFsIG9mICQzNDIzJCBjZWxscywgbW9zdCBvZiB3aGljaCBiZWNhdXNlIG9mIGFuIG91dGx5aW5nbHkgaGlnaCAKcGVyY2VudGFnZSBvZiByZWFkcyBmcm9tIG1pdG9jaG9uZHJpYWwgZ2VuZXMuCgojIyBJZGVudGlmeWluZyBhbmQgcmVtb3ZpbmcgZW1wdHkgZHJvcGxldHMKCk5vdGUgdGhhdCB0aGUgcmVtb3ZhbCBvZiBjZWxscyB3aXRoIGxvdyBzZXF1ZW5jaW5nIGRlcHRoIHVzaW5nIHRoZSBhZGFwdGl2ZSAKdGhyZXNob2xkIHByb2NlZHVyZSBhYm92ZSBpcyBhIHdheSBvZiByZW1vdmluZyBlbXB0eSBkcm9wbGV0cy4gCk90aGVyIGFwcHJvYWNoZXMgYXJlIHBvc3NpYmxlLCBlLmcuLCByZW1vdmluZyBjZWxscyBieSBzdGF0aXN0aWNhbCB0ZXN0aW5nIAp1c2luZyBgZW10cHlEcm9wc2AuIFRoaXMgZG9lcyByZXF1aXJlIHVzIHRvIHNwZWNpZnkgYSBsb3dlciBib3VuZCBvbiB0aGUgdG90YWwgCm51bWJlciBvZiBVTUlzLCBiZWxvdyB3aGljaCBhbGwgY2VsbHMgYXJlIGNvbnNpZGVyZWQgdG8gY29ycmVzcG9uZCB0byBlbXB0eSAKZHJvcGxldHMuIFRoaXMgbG93ZXIgYm91bmQgbWF5IG5vdCBiZSB0cml2aWFsIHRvIGRlcml2ZSwgYnV0IHRoZSBgYmFyY29kZVJhbmtzYApmdW5jdGlvbiBjYW4gYmUgdXNlZnVsIHRvIGlkZW50aWZ5IGFuIGVsYm93L2tuZWUgcG9pbnQuCgpJbiBicmllZiwgdGhlIHN0ZXBzIHRha2VuIGJ5IHRoZSBgZW10cHlEcm9wc2AgZnVuY3Rpb24gY2FuIGJlIHN1bW1hcml6ZWQgYXMgCmZvbGxvd3M6CgoxLglEZWZpbmUgdGhyZXNob2xkIFQgb2YgdG90YWwgVU1JIGNvdW50cyAoZS5nLiB3aXRoIHRoZSBoZWxwIG9mIHRoZSAKYGJhcmNvZGVSYW5rc2AgZnVuY3Rpb24pLCBiZWxvdyB3aGljaCBjZWxscyBtYXkgYmUgY29uc2lkZXJlZCB0byBiZSBmcm9tIGVtcHR5IApkcm9wbGV0cy4gQ2FsbCB0aGlzIHNldCBvZiBjZWxscyBFLgoKMi4JRGVmaW5lICRBX2ckIGFzIHRoZSB0b3RhbCBnZW5lIGV4cHJlc3Npb24gYWNyb3NzIGFsbCBjZWxscyBpbiBFLgoKMy4JRGVmaW5lICRwaV9nJCBhcyB0aGUgcmVsYXRpdmUgY29udHJpYnV0aW9uIG9mIGdlbmUgZyB0byB0aGUgYW1iaWVudCBwcm9maWxlLgoKNC4JQ2FsY3VsYXRlIHAtdmFsdWUgZm9yIGVhY2ggY2VsbCB0byBoYXZlIGEgdHJhbnNjcmlwdGlvbmFsIHByb2ZpbGUgc2ltaWxhciB0bwp0aGUgYW1iaWVudCBzb2x1dGlvbi4gSW50dWl0aXZlbHksIGEgcC12YWx1ZSBiZWxvdyB0aGUgcmVxdWVzdGVkIGFscGhhIGxldmVsIAp3b3VsZCBjb3JyZXNwb25kIHRvIGEgY2VsbCBmb3Igd2hpY2ggdGhlIG9ic2VydmVkIGNvdW50IHByb2ZpbGUgc3Ryb25nbHkgCmRldmlhdGVzIGZyb20gdGhlIGNvdW50IHByb2ZpbGUgb2JzZXJ2ZWQgaW4gdGhlIGNlbGxzIHdpdGggYSBsaWJyYXJ5IHNpemUgCmJlbG93IHRocmVzaG9sZCBULCBpLmUuLCBhIG5vbi1lbXB0eSBkcm9wbGV0LgoKYGBge3IsIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoRHJvcGxldFV0aWxzKQpiY3JhbmsgPC0gYmFyY29kZVJhbmtzKGNvdW50cyhzY2UpKQoKIyBPbmx5IHNob3dpbmcgdW5pcXVlIHBvaW50cyBmb3IgcGxvdHRpbmcgc3BlZWQuIER1cGxpY2F0ZWQgcmFua3MgYXJlIGEgCiMgY29uc2VxdWVuY2Ugb2YgdGllcyBpbiB0aGUgcmFua3MsIGkuZS4sIHdoZW4gY2VsbHMgaGF2ZSBhbiBlcXVhbCBsaWJyYXJ5IHNpemUuCnN1bShkdXBsaWNhdGVkKGJjcmFuayRyYW5rKSkKdW5pcSA8LSAhZHVwbGljYXRlZChiY3JhbmskcmFuaykKCnBsb3QoYmNyYW5rJHJhbmtbdW5pcV0sIGJjcmFuayR0b3RhbFt1bmlxXSwgbG9nPSJ4eSIsCiAgICB4bGFiPSJSYW5rIiwgeWxhYj0iVG90YWwgVU1JIGNvdW50IiwgY2V4LmxhYj0xLjIpCmFibGluZShoPW1ldGFkYXRhKGJjcmFuaykkaW5mbGVjdGlvbiwgY29sPSJkYXJrZ3JlZW4iLCBsdHk9MikKYWJsaW5lKGg9bWV0YWRhdGEoYmNyYW5rKSRrbmVlLCBjb2w9ImRvZGdlcmJsdWUiLCBsdHk9MikKYWJsaW5lKGg9MzUwLCBjb2w9Im9yYW5nZSIsIGx0eT0yKSAjIHBpY2tlZCB2aXN1YWxseSBteXNlbGYKbGVnZW5kKCJ0b3ByaWdodCIsIAogICAgICAgbGVnZW5kPWMoIkluZmxlY3Rpb24iLCAiS25lZSIsICJFbXBpcmljYWwga25lZSBwb2ludCIpLCAKICAgICAgIGNvbD1jKCJkYXJrZ3JlZW4iLCAiZG9kZ2VyYmx1ZSIsICJvcmFuZ2UiKSwgCiAgICAgICBsdHk9MiwgCiAgICAgICBjZXg9MS4yKQoKc2V0LnNlZWQoMTAwKQpsaW1pdCA8LSAzNTAgICAKYWxsLm91dCA8LSBlbXB0eURyb3BzKGNvdW50cyhzY2UpLCBsb3dlcj1saW1pdCwgdGVzdC5hbWJpZW50PVRSVUUpCiMgcC12YWx1ZXMgZm9yIGNlbGxzIHdpdGggdG90YWwgVU1JIGNvdW50IHVuZGVyIHRoZSBsb3dlciBib3VuZC4KaGlzdChhbGwub3V0JFBWYWx1ZVthbGwub3V0JFRvdGFsIDw9IGxpbWl0ICYgYWxsLm91dCRUb3RhbCA+IDBdLAogICAgeGxhYj0iUC12YWx1ZSIsIG1haW49IiIsIGNvbD0iZ3JleTgwIiwgYnJlYWtzPTIwKQoKIyBidXQgbm90ZSB0aGF0IGl0IHdvdWxkIHJlbW92ZSBhIHZlcnkgaGlnaCBudW1iZXIgb2YgY2VsbHMKbGVuZ3RoKHdoaWNoKGFsbC5vdXQkRkRSIDw9IDAuMDEpKSAjIHJldGFpbmVkCgojIHNvIHdlIHN0aWNrIHRvIHRoZSBtb3JlIGxlbmllbnQgYWRhcHRpdmUgZmlsdGVyaW5nIHN0cmF0ZWd5CiMgcmVtb3ZlIGNlbGxzIGlkZW50aWZpZWQgdXNpbmcgYWRhcHRpdmUgdGhyZXNob2xkcwpzY2UgPC0gc2NlWywgIWNvbERhdGEoc2NlKSRkaXNjYXJkQ2VsbHNdCmBgYAoKIyMgSWRlbnRpZnlpbmcgYW5kIHJlbW92aW5nIGRvdWJsZXRzCgpXZSB3aWxsIHVzZSAKW3NjRGJsRmluZGVyXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvMy4xNC9iaW9jL2h0bWwvc2NEYmxGaW5kZXIuaHRtbCkgCnRvIGRldGVjdCBkb3VibGV0IGNlbGxzLgoKQXMgZGlzY3Vzc2VkIGluIHRoZSB0aGVvcnkgc2Vzc2lvbiBvZiBsYXN0IHdlZWssIHRoZSBzdGVwcyB0YWtlbiBieSAKYHNjRGJsRmluZGVyYCBjYW4gYmUgc3VtbWFyaXplZCBhcyBmb2xsb3dzOgoKMS4gUGVyZm9ybSBwcmluY2lwYWwgY29tcG9uZW50cyBhbmFseXNpcyAoUENBKSBvbiBsb2ctbm9ybWFsaXplZCBleHByZXNzaW9uIApjb3VudHMuIFRoaXMgYWxsb3dzIGZvciBwcm9qZWN0aW5nIGVhY2ggY2VsbCBpbiB0aGUgZGF0YXNldCBpbnRvIGEgMkQgc3BhY2UKKGZvciBtb3JlIGRldGFpbHMgb24gUENBLCBzZWUgbmV4dCB3ZWVrcyBzZXNzaW9uKS4KCjIuIFJhbmRvbWx5IHNlbGVjdCB0d28gY2VsbHMsIHN1bSB0aGVpciBjb3VudHMgYW5kIG5vcm1hbGl6ZSwgYW5kIHByb2plY3QgCmludG8gUENBIHNwYWNlIGZyb20gc3RlcCAxLiBJbiBvdGhlciB3b3JkcywgYXJ0aWZpY2lhbGx5IGdlbmVyYXRlIGRvdWJsZXRzIGFuZApzZWUgd2hlcmUgdGhleSBhcmUgbG9jYXRlZCBpbiB0aGUgMkQgc3BhY2UuCgozLiBSZXBlYXQgc3RlcCAyIG1hbnkgdGltZXMgKGdlbmVyYXRlIG1hbnkgYXJ0aWZpY2lhbCBkb3VibGV0cykuCgo0LiBHZW5lcmF0ZSBuZWlnaGJvciBuZXR3b3JrIGluIHRoZSAyRCBzcGFjZS4gVGhlIG5ldHdvcmsgaXMgdGhlbiB1c2VkIHRvIAplc3RpbWF0ZSBhIG51bWJlciBvZiBjaGFyYWN0ZXJpc3RpY3MgZm9yIGVhY2ggY2VsbCwgaW4gcGFydGljdWxhciB0aGUgcHJvcG9ydGlvbiAKZiBhcnRpZmljaWFsIGRvdWJsZXRzIGFtb25nIHRoZSBuZWFyZXN0IG5laWdoYm9ycy4KCjUuIFVzZSB0aGlzIGluZm9ybWF0aW9uLCBhbG9uZyB3aXRoIG90aGVyIHByZWRpY3RvcnMsIHRvIHRyYWluIGEgY2xhc3NpZmllcgooZ3JhZGllbnQgYm9vc3RlZCB0cmVlKSB0aGF0IGFsbG93cyBmb3IgZGlzdGluZ3Vpc2hpbmcgZG91YmxldHMgZnJvbSBzaW5nbGV0cy4KCk5vdGU6IG9ubHkgY2xhc3NpZmllcyBmb3IgaWRlbnRpZnlpbmcgZG91YmxldHMgZm9yIHdoaWNoIHRoZSB0d28gY2VsbHMKYXJlIGZyb20gZGlmZmVyZW50IGNlbGwgdHlwZSBjbHVzdGVycy4KCgpgYGB7ciwgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyMgcGVyZm9ybSBkb3VibGV0IGRldGVjdGlvbgpsaWJyYXJ5KHNjRGJsRmluZGVyKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpzZXQuc2VlZCgyMTExMDMpCnNhbXBsZUlEIDwtIHVubGlzdChsYXBwbHkoc3Ryc3BsaXQoY29sRGF0YShzY2UpJGNlbGwuaWQsIHNwbGl0PSJfIiksICJbWyIsIDEpKQp0YWJsZShzYW1wbGVJRCkKc2NlIDwtIHNjRGJsRmluZGVyKHNjZSwgCiAgICAgICAgICAgICAgICAgICByZXR1cm5UeXBlPSJ0YWJsZSIsCiAgICAgICAgICAgICAgICAgICBzYW1wbGVzID0gZmFjdG9yKHNhbXBsZUlEKSkKdGFibGUoc2NlJHNjRGJsRmluZGVyLmNsYXNzKQoKCiMjIHZpc3VhbGl6ZSB0aGVzZSBzY29yZXMKIyMgZXhwbG9yZSBkb3VibGV0IHNjb3JlIHdydCBvcmlnaW5hbCBjbHVzdGVyIGxhYmVscwpib3hwbG90KGxvZzFwKHNjZSRzY0RibEZpbmRlci5zY29yZSkgfiBmYWN0b3IoY29sRGF0YShzY2UpJGNsdXN0ZXIsIGV4Y2x1ZGU9TlVMTCkpCgp0YWIgPC0gdGFibGUoc2NlJHNjRGJsRmluZGVyLmNsYXNzLCBzY2UkY2x1c3RlciwgCiAgICAgIGV4Y2x1ZGU9TlVMTCkKdGFiCnQodCh0YWIpIC8gY29sU3Vtcyh0YWIpKQoKYmFycGxvdCh0KHQodGFiKSAvIGNvbFN1bXModGFiKSlbMixdLAogICAgICAgIHhsYWIgPSAiQ2x1c3RlciIsIHlsYWIgPSAiRnJhY3Rpb24gb2YgZG91YmxldHMiKQoKcmFuZ2Uoc2NlJHNjRGJsRmluZGVyLnNjb3JlW3NjZSRzY0RibEZpbmRlci5jbGFzcyAgPT0gImRvdWJsZXQiICYgc2FtcGxlSUQgPT0gInIxIl0pCnJhbmdlKHNjZSRzY0RibEZpbmRlci5zY29yZVtzY2Ukc2NEYmxGaW5kZXIuY2xhc3MgID09ICJzaW5nbGV0IiAmIHNhbXBsZUlEID09ICJyMSJdKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQojIHJlbW92ZSBkb3VibGV0cwpzY2UgPC0gc2NlWywhc2NlJHNjRGJsRmluZGVyLmNsYXNzID09ICJkb3VibGV0Il0KYGBgCgojIE5vcm1hbGl6YXRpb24KCk5vcm1hbGl6YXRpb24gYWltcyB0byByZW1vdmUgdGVjaG5pY2FsIGVmZmVjdHMgc3VjaCBhcyBzZXF1ZW5jaW5nIGRlcHRoIHNvIHRoYXQgCmNvbXBhcmlzb25zIGJldHdlZW4gY2VsbHMgYXJlIG5vdCBjb25mb3VuZGVkIGJ5IHRoZW0uIFRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgCm5vcm1hbGl6YXRpb24gbWV0aG9kcyBtZXRob2RzIHVzZSBzY2FsaW5nLCB3aGVyZSBhIHNjYWxpbmcgZmFjdG9yIChhbHNvIGNhbGxlZCAKc2l6ZSBmYWN0b3IsIG5vcm1hbGl6YXRpb24gZmFjdG9yKSBpcyBlc3RpbWF0ZWQgZm9yIGVhY2ggY2VsbC4gVGhlc2Ugc2NhbGluZyAKZmFjdG9ycyAoZS5nLiwgdGhlIGVmZmVjdGl2ZSBsaWJyYXJ5IHNpemUpIGNhbiBiZSBpbmNsdWRlZCBhcyBhbiBvZmZzZXQKdG8gZG93bnN0cmVhbSBtb2RlbGluZyBwcm9jZWR1cmVzLiBUaGlzIGVmZmVjdGl2ZWx5IGFsbG93cyBmb3IgcGVyZm9ybWluZwppbmZlcmVuY2Ugb24gdGhlIHJlbGF0aXZlIGFidW5kYW5jZSBvZiBhIGdlbmUgaW4gYSBjZWxsLCBhZnRlciBhY2NvdW50aW5nIGZvciAKbGlicmFyeSBzaXplIGFuZCBSTkEgY29tcG9zaXRpb24gZGlmZmVyZW5jZXMgYmV0d2VlbiBjZWxscywgd2hpY2ggaXMgbXVjaCBtb3JlCnJlbGV2YW50IHRoYW4gY29tcGFyaW5nIHJhdyBjb3VudHMKCk5vdGUgdGhhdCB3ZSBhbHdheXMgcHJlZmVyIHRoZSB1c2Ugb2Ygb2Zmc2V0cyBvdmVyIHRyYW5zZm9ybWF0aW9uIG9mIHRoZSBkYXRhLgpJbiBicmllZiwgc3VjaCB0cmFuc2Zvcm1hdGlvbiB3b3VsZCBkaXN0b3J0IHRoZSBtZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcAppbiB0aGUgZGF0YS4gRm9yIG1vcmUgZGV0YWlscywgd2UgcmVmZXIgdG8gdGhlIGRvY3VtZW50IHRoYXQgd2FzIGRpc2N1c3NlZAppbiB0aGVvcnkgc2Vzc2lvbiBvZiBsYXN0IHdlZWsKKFtsaW5rXShodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8vU0dBMjEvc2VxdWVuY2luZ19zY2FsaW5nTm9ybWFsaXphdGlvbi5odG1sKSkKCkZvciBub3JtYWxpemF0aW9uLCB0aGUgc2l6ZSBmYWN0b3JzICRzX2kkIGNvbXB1dGVkIGhlcmUgYXJlIHNpbXBseSBzY2FsZWQgCmxpYnJhcnkgc2l6ZXM6ClxbIE5faSA9IFxzdW1fZyBZX3tnaX0gXF0KXFsgc19pID0gTl9pIC8gXGJhcntOfV9pIFxdCgpgYGB7ciwgZXZhbD1GQUxTRX0Kc2NlIDwtIGxvZ05vcm1Db3VudHMoc2NlKQoKIyBub3RlIHdlIGFsc28gcmV0dXJuZWQgbG9nIGNvdW50czogc2VlIHRoZSBhZGRpdGlvbmFsIGxvZ2NvdW50cyBhc3NheS4Kc2NlCgojIHlvdSBjYW4gZXh0cmFjdCBzaXplIGZhY3RvcnMgdXNpbmcKc2YgPC0gbGlicmFyeVNpemVGYWN0b3JzKHNjZSkKbWVhbihzZikgIyBlcXVhbCB0byAxIGR1ZSB0byBzY2FsaW5nLgpwbG90KHg9IGxvZyhjb2xTdW1zKGFzc2F5cyhzY2UpJGNvdW50cykpLCAKICAgICB5PXNmKQpgYGAKCkZyb20gdGhlIE9TQ0EgYm9vazoKQWx0ZXJuYXRpdmVseSwgd2UgbWF5IHVzZSBtb3JlIHNvcGhpc3RpY2F0ZWQgYXBwcm9hY2hlcyBmb3IgdmFyaWFuY2Ugc3RhYmlsaXppbmcgCnRyYW5zZm9ybWF0aW9ucyBpbiBnZW5vbWljcyBkYXRhLCBlLmcuLCBgREVTZXEyYCBvciBgc2N0cmFuc2Zvcm1gLiBUaGVzZSBhaW0gCnRvIHJlbW92ZSB0aGUgbWVhbi12YXJpYW5jZSB0cmVuZCBtb3JlIGVmZmVjdGl2ZWx5IHRoYW4gdGhlIHNpbXBsZXIgCnRyYW5zZm9ybWF0aW9ucyBtZW50aW9uZWQgYWJvdmUsIHRob3VnaCBpdCBjb3VsZCBiZSBhcmd1ZWQgd2hldGhlciB0aGlzIGlzIAphY3R1YWxseSBkZXNpcmFibGUuIEZvciBsb3ctY292ZXJhZ2Ugc2NSTkEtc2VxIGRhdGEsIHRoZXJlIHdpbGwgYWx3YXlzIGJlIGEgCm1lYW4tdmFyaWFuY2UgdHJlbmQgdW5kZXIgYW55IHRyYW5zZm9ybWF0aW9uLCBmb3IgdGhlIHNpbXBsZSByZWFzb24gdGhhdCB0aGUgCnZhcmlhbmNlIG11c3QgYmUgemVybyB3aGVuIHRoZSBtZWFuIGNvdW50IGlzIHplcm8uIFRoZXNlIG1ldGhvZHMgYWxzbyBmYWNlIHRoZSAKY2hhbGxlbmdlIG9mIHJlbW92aW5nIHRoZSBtZWFuLXZhcmlhbmNlIHRyZW5kIHdoaWxlIHByZXNlcnZpbmcgdGhlIGludGVyZXN0aW5nIApjb21wb25lbnQgb2YgdmFyaWF0aW9uLCBpLmUuLCB0aGUgbG9nLWZvbGQgY2hhbmdlcyBiZXR3ZWVuIHN1YnBvcHVsYXRpb25zOyB0aGlzIAptYXkgb3IgbWF5IG5vdCBiZSBkb25lIGFkZXF1YXRlbHksIGRlcGVuZGluZyBvbiB0aGUgYWdncmVzc2l2ZW5lc3Mgb2YgCnRoZSBhbGdvcml0aG0uCgpJbiBwcmFjdGljZSwgdGhlIGxvZy10cmFuc2Zvcm1hdGlvbiBpcyBhIGdvb2QgZGVmYXVsdCBjaG9pY2UgZHVlIHRvIGl0cyAKc2ltcGxpY2l0eSBhbmQgaW50ZXJwcmV0YWJpbGl0eSwgYW5kIGlzIHdoYXQgd2Ugd2lsbCBiZSB1c2luZyBmb3IgYWxsIApkb3duc3RyZWFtIGFuYWx5c2VzLgoKLS0tCgotLS0gZW5kIGxhYiBzZXNzaW9uIDEgLS0tCgotLS0KCldlIGhhdmUgY3JlYXRlZCBhIApbRHJvcEJveCBmb2xkZXJdKGh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3NoLzN5YzkwNjVyaTc2YjJlNy9BQUFyMHo5WkdwM0FQWi1yZUg2STBER1FhP2RsPTApIApjb250YWluaW5nIGNvbnZlbmllbmNlIGZpbGVzLCBlLmcuLCBhIHByZXByb2Nlc3NlZCBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIHRoYXQgCmlzIHRoZSBlbmQgcmVzdWx0IG9mIGxhYiBzZXNzaW9uIDEuIFlvdSBtYXkgd2FudCB0byBkb3dubGFvZCB0aGVtIGFuZCBpbXBvcnQgdGhlbSBpbiB0aGUgY29kZSBjaHVuayBiZWxvdy4KCmBgYHtyLCBldmFsPUZBTFNFfQpgYGAKCiMgTm9ybWFsaXphdGlvbiAoY29udGludWVkKQoKTm9ybWFsaXphdGlvbiBpcyBuZWNlc3NhcnkgdG8gY29ycmVjdCBmb3Igc2V2ZXJhbCBzb3VyY2VzIG9mIHRlY2huaWNhbCAKdmFyaWF0aW9uOgoKIC0gKipEaWZmZXJlbmNlcyBpbiBzZXF1ZW5jaW5nIGRlcHRoKiogYmV0d2VlbiBzYW1wbGVzLiBTb21lIHNhbXBsZXMgZ2V0IAogc2VxdWVuY2VkIGRlZXBlciBpbiB0aGUgc2Vuc2UgdGhhdCB0aGV5IGNvbnNpc3Qgb2YgbW9yZSAobWFwcGVkKSByZWFkcyBhbmQgCiB0aGVyZWZvcmUgY2FuIGJlIGNvbnNpZGVyZWQgdG8gY29udGFpbiBhIGhpZ2hlciBhbW91bnQgb2YgaW5mb3JtYXRpb24sIHdoaWNoIAogd2Ugc2hvdWxkIGJlIHRha2luZyBpbnRvIGFjY291bnQuIEluIGFkZGl0aW9uLCBpZiBhIHNhbXBsZSBpcyBzZXF1ZW5jZWQgZGVlcGVyLCAKIGl0IGlzIG5hdHVyYWwgdGhhdCB0aGUgY291bnRzIGZvciBlYWNoIGdlbmUgd2lsbCBiZSBoaWdoZXIsIGplb3BhcmRpemluZyBhIAogZGlyZWN0IGNvbXBhcmlzb24gb2YgdGhlIGV4cHJlc3Npb24gY291bnRzLgogLSAqKkRpZmZlcmVuY2VzIGluIFJOQSBwb3B1bGF0aW9uIGNvbXBvc2l0aW9uKiogYmV0d2VlbiBzYW1wbGVzLiBBcyBhbiBleHRyZW1lIAogZXhhbXBsZSwgc3VwcG9zZSB0aGF0IHR3byBzYW1wbGVzIGhhdmUgYmVlbiBzZXF1ZW5jZWQgdG8gdGhlIGV4YWN0IHNhbWUgZGVwdGguCiBPbmUgc2FtcGxlIGlzIGNvbnRhbWluYXRlZCBhbmQgaGFzIGEgdmVyeSBoaWdoIGNvbmNlbnRyYXRpb24gb2YgdGhlIAogY29udGFtaW5hbnQgY0ROQSBiZWluZyBzZXF1ZW5jZWQsIGJ1dCBvdGhlcndpc2UgdGhlIHR3byBzYW1wbGVzIGFyZSBpZGVudGljYWwuIAogU2luY2UgdGhlIGNvbnRhbWluYW50IHdpbGwgYmUgdGFraW5nIHVwIGEgc2lnbmlmaWNhbnQgcHJvcG9ydGlvbiBvZiB0aGUgcmVhZHMgCiBiZWluZyBzZXF1ZW5jZWQsIHRoZSBjb3VudHMgd2lsbCBub3QgYmUgZGlyZWN0bHkgY29tcGFyYWJsZSBiZXR3ZWVuIHRoZSAKIHNhbXBsZXMuIEhlbmNlLCB3ZSBtYXkgYWxzbyB3YW50IHRvIGNvcnJlY3QgZm9yIGRpZmZlcmVuY2VzIGluIHRoZSBjb21wb3NpdGlvbiAKIG9mIHRoZSBSTkEgcG9wdWxhdGlvbiBvZiB0aGUgc2FtcGxlcwogKHNlZSBbZWRnZVIgbWFudWFsIGNoYXB0ZXIgMi44XShodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvZWRnZVIvaW5zdC9kb2MvZWRnZVJVc2Vyc0d1aWRlLnBkZikpLgogLSAqKk90aGVyIHRlY2huaWNhbCB2YXJpYXRpb24qKiBzdWNoIGFzIHNhbXBsZS1zcGVjaWZpYyBHQy1jb250ZW50IG9yIAogdHJhbnNjcmlwdCBsZW5ndGggZWZmZWN0cyBtYXkgYWxzbyBiZSBhY2NvdW50ZWQgZm9yLgoKYGBge3IsIGV2YWw9RkFMU0V9CnRhYmxlKHNjZSRjbHVzdGVyKQpgYGAKCkJlbG93LCB3ZSB3aWxsIHZpc3VhbGl6ZSB0aGUgbm9ybWFsaXphdGlvbiBvY2N1cnJpbmcgYmV0d2VlbiB0d28gY2VsbHMgb2YgdGhlIApzYW1lIGNlbGwgdHlwZSAod2hpY2ggY291bGQgYmUgY29uc2lkZXJlZCB0ZWNobmljYWwgcmVwZWF0cyk6IAoKYGBge3IsIGV2YWw9RkFMU0V9CnNlbGVjdCA8LSBzY2UkY2x1c3RlciA9PSAiMSIKc2VsZWN0W2lzLm5hKHNlbGVjdCldIDwtIEZBTFNFCmNzIDwtIGNvbFN1bXMoYXNzYXlzKHNjZSkkY291bnRzWyxzZWxlY3RdKQpjc1tvcmRlcihjcywgZGVjcmVhc2luZyA9IFRSVUUpXVtjKDEsMTApXQpgYGAKCkxldOKAmXMgdGFrZSBhIGxvb2sgYXQgaG93IGNvbXBhcmFibGUgdHdvIGNlbGxzIChyZXBsaWNhdGVzKSBvZiBjbHVzdGVyIDEgYXJlLiAKV2Ugd2lsbCBjb21wYXJlIHRoZSBjZWxsIHdpdGggdGhlIGhpZ2hlc3QgbGlicmFyeSBzaXplIHdpdGggdGhlIGNlbGwgdGhhdCBoYXMgCnRoZSAxMHRoIGhpZ2hlc3QgbGlicmFyeSBzaXplIHVzaW5nIE1ELXBsb3RzIChtZWFuLWRpZmZlcmVuY2UgcGxvdHMsIGFzIAppbnRyb2R1Y2VkIGJ5IApbRHVkb2l0IGV0IGFsLiAoMjAwMildKGh0dHBzOi8vd3d3LmpzdG9yLm9yZy9zdGFibGUvMjQzMDcwMzg/c2VxPTEjbWV0YWRhdGFfaW5mb190YWJfY29udGVudHMpKSwgCmFsc28gc29tZXRpbWVzIHJlZmVycmVkIHRvIGFzIE1BLXBsb3RzLgoKYGBge3IsIGV2YWw9RkFMU0V9CnRhcmdldENlbGxzIDwtIG5hbWVzKGNzW29yZGVyKGNzLCBkZWNyZWFzaW5nID0gVFJVRSldW2MoMSwxMCldKQoKTSA8LSByb3dNZWFucyhhc3NheXMoc2NlKSRjb3VudHNbLHRhcmdldENlbGxzXSkKRCA8LSBhc3NheXMoc2NlKSRjb3VudHNbLHRhcmdldENlbGxzWzJdXSAvIGFzc2F5cyhzY2UpJGNvdW50c1ssdGFyZ2V0Q2VsbHNbMV1dCnBsb3QoeCA9IGxvZyhNKSwgeSA9IGxvZzIoRCksCiAgICAgcGNoID0gMTYsIGNleD0xLzMsCiAgICAgbWFpbiA9IHBhc3RlMCgiQ2VsbCAiLCB0YXJnZXRDZWxsc1syXSwgIiB2cyBjZWxsICIsIHRhcmdldENlbGxzWzFdKSwKICAgICB4bGFiID0gIkxvZyBtZWFuIiwgeWxhYiA9ICJMb2cyIGZvbGQtY2hhbmdlIiwKICAgICBidHkgPSAnbCcpCmFibGluZShoID0gMCwgY29sPSJvcmFuZ2UiLCBsd2Q9MikKCmBgYAoKV2Ugc2VlIGNsZWFyIGJpYXMgaW4gdGhlIGNvbXBhcmlzb24gb2YgdGhlIDFzdCBhbmQgMTB0aCBtb3N0IGRlZXBseSBzZXF1ZW5jZWQgCmNlbGwgZnJvbSBjZWxsIGNsdXN0ZXIgMS4gV2Ugc2VlIHRoYXQgdGhlIGxvZyBmb2xkLWNoYW5nZXMgYXJlIGJpYXNlZCBkb3dud2FyZHMuIApUaGlzIG1lYW5zIHRoYXQsIG9uIGF2ZXJhZ2UsIGEgZ2VuZSBpcyBoaWdoZXIgZXhwcmVzc2VkIGluIGNlbGwgMSB2ZXJzdXMgY2VsbCAxMC4KTG9va2luZyBhdCB0aGUgbGlicmFyeSBzaXplcywgd2UgY2FuIGluZGVlZCBzZWUgdGhhdCB0aGUgbGlicmFyeSBzaXplIGZvciAKY2VsbCAxIGlzIDIwODY5IGNvdW50cywgd2hpbGUgaXQgaXMgb25seSAxNDcxOSBjb3VudHMgZm9yIGNlbGwgMTAhIFRoaXMgaXMgYQpjbGVhciBsaWJyYXJ5IHNpemUgZWZmZWN0IHRoYXQgd2Ugc2hvdWxkIHRha2UgaW50byBhY2NvdW50LgoKYGBge3IsIGV2YWw9RkFMU0V9CiMgbm9ybWFsaXplIHRoZSBjb3VudCBkYXRhIHVzaW5nIHRoZSBwcmV2aW91c2x5IGNvbXB1dGVkICJzaXplIGZhY3RvcnMiCmFzc2F5KHNjZSwgIm5vcm1lZCIpIDwtIG5vcm1hbGl6ZUNvdW50cyhzY2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZS5mYWN0b3JzPXNmLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBzZXVkby5jb3VudD0wKQoKTSA8LSByb3dNZWFucyhhc3NheXMoc2NlKSRub3JtZWRbLHRhcmdldENlbGxzXSkKRCA8LSBhc3NheXMoc2NlKSRub3JtZWRbLHRhcmdldENlbGxzWzJdXSAvIGFzc2F5cyhzY2UpJG5vcm1lZFssdGFyZ2V0Q2VsbHNbMV1dCnBsb3QoeCA9IGxvZyhNKSwgeSA9IGxvZzIoRCksCiAgICAgcGNoID0gMTYsIGNleD0xLzMsCiAgICAgbWFpbiA9IHBhc3RlMCgiQ2VsbCAiLCB0YXJnZXRDZWxsc1syXSwgIiB2cyBjZWxsICIsIHRhcmdldENlbGxzWzFdKSwKICAgICB4bGFiID0gIkxvZyBtZWFuIiwgeWxhYiA9ICJMb2cyIGZvbGQtY2hhbmdlIiwKICAgICBidHkgPSAnbCcpCmFibGluZShoID0gMCwgY29sPSJvcmFuZ2UiLCBsd2Q9MikKYGBgCgpVcG9uIG5vcm1hbGl6aW5nIHRoZSBkYXRhIHVzaW5nIHNpemUgZmFjdG9ycywgd2UgaGF2ZSByZW1vdmVkIGJpYXMgYXMgYQpjb25zZXF1ZW5jZSBvZiBkaWZmZXJlbmNlcyBpbiBzZXF1ZW5jaW5nIGRlcHRoLgoKTm90ZSB0aGF0IHdlIGNvbXB1dGVkIHRoZSBub3JtYWxpemVkIGNvdW50IG1hdHJpeCBvbmx5IGZvciBkZW1vbnN0cmF0aW5nIHRoZQplZmZlY3Qgb2Ygc3VjaCBub3JtYWxpemF0aW9uLiBJbiBhIHR5cGljYWwgd29ya2Zsb3csIHRoZSBzaXplIGZhY3RvcnMgYXJlIHVzZWQKYXMgb2Zmc2V0cyBpbiB0aGUgZG93bnN0cmVhbSBtb2RlbHMgcmF0aGVyIHRoYW4gdG8gcGVyZm9ybSBkYXRhIHRyYW5zZm9ybWF0aW9uLgpJbiBicmllZiwgc3VjaCB0cmFuc2Zvcm1hdGlvbiB3b3VsZCBkaXN0b3J0IHRoZSBtZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcAppbiB0aGUgZGF0YS4gRm9yIG1vcmUgZGV0YWlscywgd2UgcmVmZXIgdG8gdGhlIGRvY3VtZW50IHRoYXQgd2FzIHRvdWNoZWQgdXBvbgppbiB0aGUgZmlyc3QgdGhlb3J5IHNlc3Npb24KKFtsaW5rXShodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8vU0dBMjEvc2VxdWVuY2luZ19zY2FsaW5nTm9ybWFsaXphdGlvbi5odG1sKSkKCiMgRmVhdHVyZSBzZWxlY3Rpb24KCkFzIGRpbWVuc2lvbnMgaW5jcmVhc2UsIHNob3J0ZXN0IGFuZCBmYXJ0aGVzdCBkaXN0YW5jZXMgYmV0d2VlbiBwb2ludHMgYmVjb21lCm5lYXJseSBpbnNlcGFyYWJsZS4gSW4gaGlnaC1kaW1lbnNpb25hbCBzcGFjZSwgaXQgaXMgdGhlcmVmb3JlIGV4dHJlbWVseSAKZGlmZmljdWx0IHRvIHNlcGFyYXRlIHNpZ25hbCBmcm9tIG5vaXNlLgoKSW4gb3JkZXIgdG8gcmVjb3ZlciBzdHJ1Y3R1cmUgKGUuZy4gc2V0dGluZyB1cCBhIGRpbWVuc2lvbi1yZWR1Y2VkCnNwYWNlIHRvIGhlbHAgdXMgZmluZCBjZWxsLXR5cGUgY2x1c3RlcnMgaW4gdGhlIGRhdGEpLCB3ZSB3YW50IHRvIG1vdmUgdG8gYW4KaW5mb3JtYXRpdmUsIGxvd2VyLWRpbWVuc2lvbmFsIHNwYWNlLiBXZSB3aWxsIHNlbGVjdCBnZW5lcyB3aGljaCB3ZSBob3BlIGFyZQppbmZvcm1hdGl2ZSBmb3IgcmVjb3ZlcmluZyB0aGUgYmlvbG9naWNhbCBzdHJ1Y3R1cmUuIEJ1dCB3aGF0IGRlZmluZXMgYW4gCmluZm9ybWF0aXZlIGdlbmU/CgojIyBTZWxlY3RpbmcgZ2VuZXMgd2l0aCBoaWdoIHZhcmlhbmNlCgpUaGUgc2ltcGxlc3QgYXBwcm9hY2ggdG8gcXVhbnRpZnlpbmcgcGVyLWdlbmUgdmFyaWF0aW9uIGlzIHRvIGNvbXB1dGUgdGhlIAp2YXJpYW5jZSBvZiB0aGUgbG9nLW5vcm1hbGl6ZWQgZXhwcmVzc2lvbiB2YWx1ZXMgKGkuZS4sICJsb2ctY291bnRzIikgZm9yIAplYWNoIGdlbmUgYWNyb3NzIGFsbCBjZWxscy4gCgpgYGB7cixldmFsPUZBTFNFfQojIGNhbGN1bGF0ZSB2YXJpYW5jZSBvZiBsb2ctbm9ybWFsaXplZCBjb3VudHMgZm9yIGVhY2ggZ2VuZQpnZW5lVmFycyA8LSBnZW5lZmlsdGVyOjpyb3dWYXJzKC4uLikKIyBzZWxlY3QgdG9wIDEwMDAgaGlnaGx5IHZhcmlhYmxlIGdlbmVzCmhpZ2hWYXJHZW5lcyA8LSBuYW1lcyhnZW5lVmFycylbb3JkZXIoZ2VuZVZhcnMsIGRlY3JlYXNpbmc9VFJVRSlbMToxZTNdXQpoZWFkKGhpZ2hWYXJHZW5lcykKYGBgCgojIyBTZWxlY3RpbmcgZ2VuZXMgd2l0aCBoaWdoIHZhcmlhdGlvbiB3aXRoIHJlc3BlY3QgdG8gbWVhbgoKV2hpbGUgY2FsY3VsYXRpb24gb2YgdGhlIHBlci1nZW5lIHZhcmlhbmNlIGlzIHNpbXBsZSwgZmVhdHVyZSBzZWxlY3Rpb24gcmVxdWlyZXMKbW9kZWxsaW5nIG9mIHRoZSBtZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcC4gSW5kZWVkLCBub3QgYWNjb3VudGluZyBmb3IgdGhlIAptZWFuLXZhcmlhbmNlIHN0cnVjdHVyZSB3aGlsZSBzZWxlY3RpbmcgaGlnaGx5IHZhcmlhYmxlIGdlbmVzIHdpbGwgb2Z0ZW50aW1lcyAKYm9pbCBkb3duIHRvIHNlbGVjdGluZyB0aGUgbW9zdCBoaWdobHkgZXhwcmVzc2VkIGdlbmVzLiBUaGUgbG9nLXRyYW5zZm9ybWF0aW9uIAppcyBhIGhlbHBmdWwgdmFyaWFuY2Ugc3RhYmlsaXppbmcgdHJhbnNmb3JtYXRpb24sIGhvd2V2ZXIsIGl0IGlzIG5vdCBwZXJmZWN0LCAKbWVhbmluZyB0aGF0IHRoZSB2YXJpYW5jZSBvZiBhIGdlbmUgaXMgbm90IGNvbXBsZXRlbHkgaW5kZXBlbmRlbnQgb2YgaXRzIG1lYW4uClRoZXJlZm9yZSwgZmVhdHVyZSBzZWxlY3Rpb24gbWF5IHN0aWxsIGJlIGRyaXZlbiBieSBhdmVyYWdlIGV4cHJlc3Npb24gcmF0aGVyIAp0aGFuIHVuZGVybHlpbmcgYmlvbG9naWNhbCBoZXRlcm9nZW5laXR5LgoKQSAocmlnaHRseSBzbykgcG9wdWxhciBhcHByb2FjaCBpcyB0byBzZWxlY3QgZ2VuZXMgdGhhdCBoYXZlIGEgaGlnaCB2YXJpYW5jZQp3aXRoIHJlc3BlY3QgdG8gdGhlaXIgbWVhbi4gT2Z0ZW4sIGZpcnN0IGFuIGVtcGlyaWNhbCBtZWFuLXZhcmlhbmNlIHRyZW5kIGlzCmZpdHRlZCwgdXBvbiB3aGljaCBnZW5lcyB3aXRoIHRoZSBoaWdoZXN0IHBvc2l0aXZlIHJlc2lkdWFscyBhcmUgc2VsZWN0ZWQuIApCZWluZyBpbnR1aXRpdmUsIHJlYXNvbmFibGUgYW5kIGZhaXJseSBzdHJhaWdodC1mb3J3YXJkLCB0aGlzIG1ldGhvZCBpcyB3aWRlbHkKdXNlZC4KClRvIGFjY291bnQgZm9yIHRoZSBtZWFuLXZhcmlhbmNlIGVmZmVjdCwgd2UgdXNlIHRoZSBgbW9kZWxHZW5lVmFyYCBmdW5jdGlvbiBvZgp0aGUgYHNjcmFuYCBwYWNrYWdlIHRvIGZpdCBhIHRyZW5kIHRvIHRoZSB2YXJpYW5jZSB3aXRoIHJlc3BlY3QgdG8gYWJ1bmRhbmNlCmFjcm9zcyBhbGwgZ2VuZXMgKG9uIGxvZy1ub3JtYWxpemVkIGV4cHJlc3Npb24gdmFsdWVzIG9mIHRoZSBzY2Ugb2JqZWN0KS4KCmBgYHtyLGV2YWw9RkFMU0V9CmxpYnJhcnkoc2NyYW4pCiMgYG1vZGVsR2VuZVZhcmAgZnVuY3Rpb24gZGVzY3JpcHRpb246IE1vZGVsIHRoZSB2YXJpYW5jZSBvZiB0aGUgbG9nLWV4cHJlc3Npb24gCiMgcHJvZmlsZXMgZm9yIGVhY2ggZ2VuZSwgZGVjb21wb3NpbmcgaXQgaW50byB0ZWNobmljYWwgYW5kIGJpb2xvZ2ljYWwgCiMgY29tcG9uZW50cyBiYXNlZCBvbiBhIGZpdHRlZCBtZWFuLXZhcmlhbmNlIHRyZW5kLgpkZWMgPC0gbW9kZWxHZW5lVmFyKC4uLikgIyBpbnB1dCBhIHNjZSBvYmplY3Qgd2l0aCBwcmVjb21wdXRlZCBsb2djb3VudHMgYXNzYXkKaGVhZChkZWMpCmBgYAoKVGhlIGZpdHRlZCB2YWx1ZSBmb3IgZWFjaCBnZW5lIGlzIHVzZWQgYXMgYSBwcm94eSBmb3IgdGhlIHRlY2huaWNhbCBjb21wb25lbnQgb2YKdmFyaWF0aW9uIGZvciBlYWNoIGdlbmUsIHVuZGVyIHRoZSBhc3N1bXB0aW9uIHRoYXQgbW9zdCBnZW5lcyBleGhpYml0IGEgbG93CmJhc2VsaW5lIGxldmVsIG9mIHZhcmlhdGlvbiB0aGF0IGlzIG5vdCBiaW9sb2dpY2FsbHkgaW50ZXJlc3RpbmcuIFRoZSBiaW9sb2dpY2FsCmNvbXBvbmVudCBvZiB2YXJpYXRpb24gZm9yIGVhY2ggZ2VuZSBpcyBkZWZpbmVkIGFzIHRoZSB0aGUgcmVzaWR1YWwgZnJvbSB0aGUKdHJlbmQuIFJhbmtpbmcgZ2VuZXMgYnkgdGhlIGJpb2xvZ2ljYWwgY29tcG9uZW50IGVuYWJsZXMgaWRlbnRpZmljYXRpb24gb2YKaW50ZXJlc3RpbmcgZ2VuZXMgZm9yIGRvd25zdHJlYW0gYW5hbHlzZXMgaW4gYSBtYW5uZXIgdGhhdCBhY2NvdW50cyBmb3IgdGhlCm1lYW4tdmFyaWFuY2UgcmVsYXRpb25zaGlwLgoKYGBge3IsZXZhbD1GQUxTRX0KZml0UmV0aW5hIDwtIG1ldGFkYXRhKC4uLikKcGxvdCh4ID0gLi4uLCAjIHRoZSBmaXR0ZWQgbWVhbnMgb24gdGhlIHgtYXhpcyAKICAgICB5ID0gLi4uLCAjIHRoZSBmaXR0ZWQgdmFyaWFuY2VzIG9uIHRoZSB5LWF4aXMKICAgICB4bGFiPSJNZWFuIG9mIGxvZy1leHByZXNzaW9uIiwKICAgIHlsYWI9IlZhcmlhbmNlIG9mIGxvZy1leHByZXNzaW9uIikKY3VydmUoZml0UmV0aW5hJHRyZW5kKHgpLCBjb2w9ImRvZGdlcmJsdWUiLCBhZGQ9VFJVRSwgbHdkPTIpICMgYWRkcyBhIHRyZW5kIGxpbmUKYGBgCgpXZSBhcmUgaW50ZXJlc3RlZCBpbiB0aG9zZSBnZW5lcyBmb3Igd2hpY2ggdGhlIHZhcmlhbmNlIGluIGV4cHJlc3Npb24gaXMgaGlnaGVyCnRoYW4gd2hhdCB3ZSB3b3VsZCBleHBlY3QgZm9yIHRoYXQgZ2VuZSBiYXNlZCBvbiBpdHMgbWVhbiBleHByZXNzaW9uLgoKYGBge3IsZXZhbD1GQUxTRX0KIyBnZXQgMTAlIG1vc3QgdmFyaWFibGUgZ2VuZXMKIyBCYXNlZCBvbiB0aGUgb3V0cHV0IHN0YXRpc3RpY3Mgb2YgbW9kZWxHZW5lVmFyLCBzZWxlY3QgdGhlIDEwJSBtb3N0IHZhcmlhYmxlIGdlbmVzCmh2ZyA8LSBnZXRUb3BIVkdzKHN0YXRzID0gLi4uLCAKICAgICAgICAgICAgICAgICAgcHJvcCA9IC4uLikgIyBwZXJjZW50YWdlIG9mIHRvcCB2YXJpYWJsZSBnZW5lcy4gQWx0ZXJuYXRpdmVseSwKIyBuID0gLi4uIGNvdWxkIGJlIHVzZWQgdG8gc2VsZWN0IHRoZSB0b3AgbnVtYmVyIG9mIHZhcmlhYmxlIGdlbmVzCmhlYWQoaHZnKQoKIyBwbG90IHRoZXNlIApwbG90KHggPSAuLi4sICMgdGhlIGZpdHRlZCBtZWFucyBvbiB0aGUgeC1heGlzIAogICAgIHkgPSAuLi4sICMgdGhlIGZpdHRlZCB2YXJpYW5jZXMgb24gdGhlIHktYXhpcwogICAgIGNvbCA9IGMoIm9yYW5nZSIsICJkYXJrc2VhZ3JlZW4zIilbKG5hbWVzKGZpdFJldGluYSRtZWFuKSAlaW4lIGh2ZykrMV0sCiAgICAgeGxhYj0iTWVhbiBvZiBsb2ctZXhwcmVzc2lvbiIsCiAgICB5bGFiPSJWYXJpYW5jZSBvZiBsb2ctZXhwcmVzc2lvbiIpCmN1cnZlKGZpdFJldGluYSR0cmVuZCh4KSwgY29sPSJkb2RnZXJibHVlIiwgYWRkPVRSVUUsIGx3ZD0yKQpsZWdlbmQoInRvcGxlZnQiLCAKICAgICAgIGxlZ2VuZCA9IGMoIlNlbGVjdGVkIiwgIk5vdCBzZWxlY3RlZCIpLCAKICAgICAgIGNvbCA9IGMoImRhcmtzZWFncmVlbjMiLCAib3JhbmdlIiksCiAgICAgICBwY2ggPSAxNiwKICAgICAgIGJ0eT0nbicpCmBgYAoKQXMgYSBjb21wYXJpc29uLCB3ZSBjb3VsZCBjb2xvciB0aGUgZ2VuZXMgb24gdGhpcyBmaWd1cmUgYWNjb3JkaW5nIHRvIHRoZSAKc2VsZWN0aW9uIHRoYXQgd2FzIG1hZGUgcHVyZWx5IGJ5IGxvb2tpbmcgYXQgdGhlIHJhdyB2YXJpYW5jZSBvZiBlYWNoIGdlbmUuCgpgYGB7cixldmFsPUZBTFNFfQouLi4KYGBgCgpBcyBleHBlY3RlZCwgd2UgaGVyZSBzaW1wbHkgc2VsZWN0IGdlbmVzIHdpdGggYSBoaWdoIHZhcmlhbmNlLCB3aXRob3V0IApyZWNvZ25pemluZyB0aGF0IGEgaGlnaCB2YXJpYW5jZSBpcyB0eXBpY2FsbHkgZHJpdmVuIGJ5IGEgaGlnaCBtZWFuLgoKIyMgSGlnaCBkZXZpYW5jZSBnZW5lcwoKSGVyZSwgd2Ugd2lsbCBzZWxlY3QgZ2VuZXMgd2l0aCBhIGhpZ2ggcmVzaWR1YWwgZGV2aWFuY2UuIFRoZSBpZGVhIGlzIHRoYXQgd2UgCmFzc3VtZSBhIG51bGwgbW9kZWwgb2YgY29uc3RhbnQgZXhwcmVzc2lvbiBmcmFjdGlvbiAoaS5lLiwgUk5BIGNvbmNlbnRyYXRpb24pIAphY3Jvc3MgYWxsIGNlbGxzLiBXZSBzdWJzZXF1ZW50bHkgY2FsY3VsYXRlIGEgZ29vZG5lc3Mtb2YtZml0IHN0YXRpc3RpYyBmb3IgCmVhY2ggZ2VuZSwgYXNzZXNzaW5nIHdoZXRoZXIgdGhlIG1vZGVsIGlzIGEgZ29vZCBhcHByb3hpbWF0aW9uIHRvIHRoZSBnZW5lIApleHByZXNzaW9uIG9mIHRoZSBjb3JyZXNwb25kaW5nIGdlbmUuCgpJZiB0aGUgbW9kZWwgZml0cyBwb29ybHksIGkuZS4sIHRoZSBnZW5lIGhhcyBhIGhpZ2ggZGV2aWFuY2UsIHRoZSBleHByZXNzaW9uIApmcmFjdGlvbiB2YXJpZXMgc2lnbmlmaWNhbnRseSBhY3Jvc3MgdGhlIGNlbGxzIGluIG91ciBkYXRhc2V0cy5HZW5lcyB3aXRoIGEgCmhpZ2ggZGV2aWFuY2Ugd2lsbCB0aHVzIG1vc3QgcG9vcmx5IGZpdCBhIG51bGwgbW9kZWwgd2hlcmUgdGhlIHJlbGF0aXZlCmFidW5kYW5jZSBpcyBlcXVhbCBmb3IgYWxsIGNlbGxzLCB3aGljaCB0aGVyZWZvcmUgYXJlIGluZm9ybWF0aXZlLgoKYGBge3IsIGV2YWw9RkFMU0V9CiNCaW9jTWFuYWdlcjo6aW5zdGFsbCgic2NyeSIpCmxpYnJhcnkoc2NyeSkKIyBgZGV2aWFuY2VGZWF0dXJlU2VsZWN0aW9uYCBmdW5jdGlvbiBkZXNjcmlwdGlvbjogQ29tcHV0ZXMgYSBkZXZpYW5jZSBzdGF0aXN0aWMgCiMgZm9yIGVhY2ggcm93IGZlYXR1cmUgKHN1Y2ggYXMgYSBnZW5lKSBmb3IgY291bnQgZGF0YSBiYXNlZCBvbiBhIG11bHRpbm9taWFsIAojIG51bGwgbW9kZWwgWy4uLl0uIApzY2UgPC0gZGV2aWFuY2VGZWF0dXJlU2VsZWN0aW9uKG9iamVjdCA9IC4uLiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAuLi4pICMgY29tcHV0ZWQgb24gcmF3IGNvdW50cwoKcGxvdChzb3J0KHJvd0RhdGEoc2NlKSRiaW5vbWlhbF9kZXZpYW5jZSwgZGVjcmVhc2luZyA9IFRSVUUpLCAKICAgICB0eXBlPSJsIiwgCiAgICAgeGxhYj0icmFua2VkIGdlbmVzIiwgCiAgICAgeWxhYj0iYmlub21pYWwgZGV2aWFuY2UiLCAKICAgICBtYWluPSJGZWF0dXJlIFNlbGVjdGlvbiB3aXRoIERldmlhbmNlIikKYWJsaW5lKHY9MjAwMCwgbHR5PTIsIGNvbD0icmVkIikKYGBgCgpPdXIgcGxvdCBsb29rcyBzaW1pbGFyIHRvIG9uZSBkaXNwbGF5ZWQgaW4gdGhlIApbdmlnbmV0dGUgb2YgdGhlIHNjcnkgcGFja2FnZV0oaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL3NjcnkvaW5zdC9kb2Mvc2NyeS5odG1sKS4gCkJhc2VkIG9uIHRoYXQgcGxvdCwgdGhlIGF1dGhvcnMgc3VnZ2VzdCByZXRhaW5pbmcgMi4wMDAgZ2VuZXMgKHRoZSB0b3AgMjAwMCAKYmFzZWQgb24gdGhlIGRldmlhbmNlIHJlc2lkdWFscykgZm9yIGRvd25zdHJlYW0gZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIGFuZCAKY2x1c3RlcmluZy4KCiMjIFNldXJhdCBWU1QKCkFub3RoZXIgdmVyeSBjb21tb24gZmVhdHVyZSBzZWxlY3Rpb24gc3RyYXRlZ3kgaXMgdGhlIHZhcmlhbmNlLXN0YWJpbGl6aW5nCnRyYW5zZm9ybWF0aW9uIGZyb20gdGhlIGBTZXVyYXRgIFIgcGFja2FnZSwgd2hpY2ggYW1vdW50cyB0byBjYWxjdWxhdGluZyBQZWFyc29uCnJlc2lkdWFscyBmcm9tIGEgcmVndWxhcml6ZWQgbmVnYXRpdmUgYmlub21pYWwgcmVncmVzc2lvbiBtb2RlbCwgd2l0aCBzZXF1ZW5jaW5nIApkZXB0aCBhcyBhIGNvdmFyaWF0ZS4KCioqSW50ZXJtZXp6bzogaW50ZXJvcGVyYWJpbGl0eSBiZXR3ZWVuIFNpbmdsZUNlbGxFeHBlcmltZW50IGFuZCBTZXVyYXQgb2JqZWN0cyoqCgpJbiB0aGlzIGxlY3R1cmUgc2VyaWVzLCB3ZSBhbHdheXMgbWFrZSB1c2Ugb2YgdGhlIFNpbmdsZUNlbGxFeHBlcmltZW50IG9iamVjdAphbmQgdGhlIHBhY2thZ2VzIGF2YWlsYWJsZSBmcm9tIEJpb2NvbmR1Y3Rvci4gQW5vdGhlciB2ZXJ5IHBvcHVsYXIgdG9vbGJveApmb3IgcGVyZm9ybWluZyBzY1JOQS1zZXEgZGF0YSBhbmFseXNpcyBpcyBgU2V1cmF0YC4gSG93ZXZlciwgZnVuY3Rpb25zIGZyb20KYFNldXJhdGAgY2Fubm90IGJlIHVzZWQgZGlyZWN0bHkgdG8gbWFuaXB1bGF0ZSBgU2luZ2xlQ2VsbEV4cGVyaW1lbnRgIG9iamVjdHMsCmFuZCB2aWNlIHZlcnNhLiBGb3J0dW5hdGVseSwgZWZmb3J0cyBoYXZlIGJlZW4gbWFkZSB0byBpbmNyZWFzZSB0aGUKaW50ZXJvcGVyYWJpbGl0eSBiZXR3ZWVuIHRoZSB0d28gdG9vbGJveGVzLgoKYGBge3IsIGV2YWw9RkFMU0V9CmxpYnJhcnkoU2V1cmF0KQpzZXVyYXRfb2JqIDwtIGFzLlNldXJhdCguLi4pICMgc2NlIG9iamVjdApzZXVyYXRfb2JqICMgbm90aWNlIHRoZSAiMCB2YXJpYWJsZSBmZWF0dXJlcyIKYGBgCgpPbiB0aGlzIG9iamVjdCwgd2UgbWF5IHVzZSBmdW5jdGlvbnMgZnJvbSB0aGUgYFNldXJhdGAgdG9vbGJveC4KRm9yIGluc3RhbmNlLCB3ZSBtYXkgc2VhcmNoIGZvciBoaWdobHkgdmFyaWFibGUgZmVhdHVyZXMgdXNpbmcgYFNldXJhdGAncyBWU1QKaW1wbGVtZW50YXRpb246CgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBQcmlvciB0byBkZXRlY3RpbmcgdmFyaWFibGUgZmVhdHVyZXMgdXNpbmcgVlNULCBTZXVyYXQgbm9ybWFsaXplcyB0aGUgZGF0YS4KIyBUaGUgYE5vcm1hbGl6ZURhdGFgIGZ1bmN0aW9uIHRha2VzIGFzIGlucHV0IHRoZSBTZXVyYXQgb2JqZWN0LCBhbmQgdXNlcyAKIyBsb2cgbm9ybWFsaXphdGlvbiBhbmQgYSBzY2FsZSBmYWN0b3Igb2YgMTAwMDAgYXMgZGVmYXVsdCAoc2VlIGhlbHAgZmlsZSkuCnNldXJhdF9vYmogPC0gU2V1cmF0OjpOb3JtYWxpemVEYXRhKG9iamVjdCA9IC4uLiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gLi4uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGUuZmFjdG9yID0gLi4uKQoKIyBOZXh0LCBwZXJmb3JtIGZlYXR1cmUgc2VsZWN0aW9uIGJhc2VkIG9uIFZTVApzZXVyYXRfb2JqIDwtICBGaW5kVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSAuLi4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdGlvbi5tZXRob2QgPSAuLi4pCmBgYAoKYGBge3IsZXZhbD1GQUxTRX0Kc2V1cmF0X29iaiAgIyBub3RpY2UgdGhlICIyMDAwIHZhcmlhYmxlIGZlYXR1cmVzIiAoZGVmYXVsdCkKaGVhZChWYXJpYWJsZUZlYXR1cmVzKHNldXJhdF9vYmopKSAjIGhlcmUgdGhleSBhcmUKYGBgCgojIERpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbgoKTm90ZSB0aGF0LCBiZWxvdywgd2UgY29sb3IgdGhlIGNlbGxzIHVzaW5nIHRoZSBrbm93biwgdHJ1ZSBjZWxsIHR5cGUgbGFiZWwgYXMgCmRlZmluZWQgaW4gdGhlIG1ldGFkYXRhLCB0byBlbXBpcmljYWxseSBldmFsdWF0ZSB0aGUgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uLiAKSW4gcmVhbGl0eSwgd2UgZG9uJ3Qga25vdyB0aGlzIHlldCBhdCB0aGlzIHN0YWdlLgoKIyMgVGhlIG1vc3QgYmFzaWMgRFIKCkp1c3QgYnkgbG9va2luZyBhdCB0aGUgdG9wIHR3byBnZW5lcyBiYXNlZCBvbiBvdXIgZmVhdHVyZSBzZWxlY3Rpb24gY3JpdGVyaW9uLCAKd2UgY2FuIGFscmVhZHkgc2VlIHNvbWUgc2VwYXJhdGlvbiBhY2NvcmRpbmcgdG8gdGhlIGNlbGwgdHlwZSEKCmBgYHtyLGV2YWw9RkFMU0V9CmNvbERhdGEoc2NlKSRjbHVzdGVyIDwtIGFzLmZhY3Rvcihjb2xEYXRhKHNjZSkkY2x1c3RlcikKY2wgPC0gY29sRGF0YShzY2UpJGNsdXN0ZXIKcGFyKGJ0eT0nbCcpCnBsb3QoeCA9IC4uLiwgIyBleHRyYWN0IHRoZSBjb3VudHMgZm9yIHRoZSBtb3N0IHZhcmlhYmxlIGdlbmUKICAgICB5ID0gLi4uLCAjIGV4dHJhY3QgdGhlIGNvdW50cyBmb3IgdGhlIHNlY29uZCBtb3N0IHZhcmlhYmxlIGdlbmUKICAgICBjb2wgPSBhcy5udW1lcmljKGNsKSwKICAgICBwY2ggPSAxNiwgY2V4ID0gMS8zLAogICAgIHhsYWIgPSAiTW9zdCBpbmZvcm1hdGl2ZSBnZW5lIiwKICAgICB5bGFiID0gIlNlY29uZCBtb3N0IGluZm9ybWF0aXZlIGdlbmUiLAogICAgIG1haW4gPSAiQ2VsbHMgY29sb3JlZCBhY2MgdG8gY2VsbCB0eXBlIikKYGBgCgpXZSBhcmUgYWJsZSB0byByZWNvdmVyIHF1aXRlIHNvbWUgc3RydWN0dXJlLiBIb3dldmVyLCBtYW55IGNlbGwgcG9wdWxhdGlvbnMgCnJlbWFpbiBvYnNjdXJlLCBhbmQgdGhlIHBsb3QgaXMgb3ZlcmNyb3dkZWQuCgojIyBMaW5lYXIgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uOiBQQ0EKCkEgRFIgbWV0aG9kIGlzIGxpbmVhciB3aGVuIHRoZSByZWR1Y2VkIGRpbWVuc2lvbnMgYXJlIGEgbGluZWFyIGZ1bmN0aW9uIG9mIHRoZQpvcmlnaW5hbCB2YXJpYWJsZXMuIEZvciBleGFtcGxlLCBpbiBQQ0EsIGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudCBpcyBhIGxpbmVhcgpjb21iaW5hdGlvbiBvZiBnZW5lcywgdGhlcmVmb3JlIHRoZSBEUiBpcyBhIGxpbmVhciBmdW5jdGlvbiBvZiB0aGUgb3JpZ2luYWwKdmFyaWFibGVzLgoKVHlwaWNhbGx5LCBQQ0EgaXMgcGVyZm9ybWVkIG9uIGxvZy10cmFuc2Zvcm1lZCBub3JtYWxpemVkIGNvdW50cy4gClRoZSBsb2ctdHJhbnNmb3JtYXRpb24gaGVscHMgIHNvbWV3aGF0LCBidXQgbm90IGNvbXBsZXRlbHksIHRvIGFjY291bnQgZm9yIHRoZQptZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcC4gUENBIHdvcmtzIHdlbGwgZm9yIGJ1bGsgUk5BLXNlcSBkYXRhLiBIb3dldmVyLCB0aGUKc3RydWN0dXJlIG9mIHNjUk5BLXNlcSBkYXRhIGlzIG9mdGVuIHRvbyBjb21wbGV4IHRvIGJlIHZpc3VhbGl6ZWQgYnkgYSBzbWFsbCAKbnVtYmVyIG9mIFBDcy4KClRoZXJlIGFyZSBzZXZlcmFsIFIgZnVuY3Rpb25zIHRoYXQgYWxsb3cgeW91IHRvIHBlcmZvcm0gUENBLiBIZXJlLCB3ZSBtYWtlIHVzZQpvZiB0aGUgYHJ1blBDQWAgZnVuY3Rpb24gb2YgdGhlIGBzY2F0ZXJgIHBhY2thZ2UsIHdoaWNoIGhhcyBiZWVuIHNwZWNpZmljYWxseQpkZXZlbG9wZWQgZm9yIHBlcmZvcm1pbmcgUENBIG9uIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0cy4KV2UgY2FsY3VsYXRlIHRoZSB0b3AgMzAgcHJpbmNpcGFsIGNvbXBvbmVudHMuCgojIyMgUENBIHdpdGggZmVhdHVyZSBzZWxlY3Rpb24KCmBgYHtyLGV2YWw9RkFMU0V9CiMgUGVyZm9ybSBhIFBDQSBmb3Igb3VyIGRhdGEuIENvbXB1dGUgdGhlIGZpcnN0IDMwIHByaW5jaXBhbCBjb21wb25lbnRzLCBvbmx5CiMgdXNpbmcgdGhlIHNlbGVjdGVkIGZlYXR1cmVzIHdpdGggaGlnaCB2YXJpYW5jZSB3aXRoIHJlc3BlY3QgdG8gdGhlIG1lYW4uCnNldC5zZWVkKDEyMzQpCnNjZSA8LSBydW5QQ0EoLi4uKSAjIHlvdSBuZWVkIHRocmVlIGFyZ3VtZW50cwpgYGAKClBDQSBoYXMgYmVlbiBwZXJmb3JtZWQuIFRoZSBQQ0EgaW5mb3JtYXRpb24gaGFzIGJlZW4gYXV0b21hdGljYWxseSBzdG9yZWQgaW4gdGhlCipyZWR1Y2VkRGltKiBzbG90IG9mIHRoZSBTaW5nbGVDZWxsRXhwZXJpbWVudCBvYmplY3QuCgpgYGB7cixldmFsPUZBTFNFfQpyZWR1Y2VkRGltTmFtZXMoc2NlKQpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CmhlYWQocmVkdWNlZERpbShzY2UsCiAgICAgICAgICAgICAgICB0eXBlPSJQQ0EiKSkKYGBgCgpUaGUgYHBsb3RQQ0FgIGZ1bmN0aW9uIG9mIHRoZSBgc2NhdGVyYCBwYWNrYWdlIG5vdyBhbGxvd3MgdXMgdG8gdmlzdWFsaXplCnRoZSBjZWxscyBpbiBQQ0Egc3BhY2UsIGJhc2VkIG9uIHRoZSBQQ0EgaW5mb3JtYXRpb24gc3RvcmVkIGluIG91ciBvYmplY3Q6CgpgYGB7cixldmFsPUZBTFNFfQpwbG90UENBKHNjZSwgCiAgICAgICAgY29sb3VyX2J5ID0gImNsdXN0ZXIiKQpgYGAKCldoaWxlIHRoZSBsYXJnZSBudW1iZXIgb2YgY2x1c3RlcnMgaW4gdGhpcyBkYXRhc2V0IG1ha2VzIGl0IGhhcmQgdG8gCmRpc3Rpbmd1aXNoIGJldHdlZW4gYWxsIHRoZSBkaWZmZXJlbnQgY29sb3JzLCB3ZSBjYW4gYWxyZWFkeSBzZWUgdGhhdCBQQ0EKcmV0cmlldmVzIHNvbWUsIGJ1dCBub3QgYWxsLCBvZiB0aGUgc3RydWN0dXJlIGluIHRoZSBkYXRhIHRoYXQgd2FzIGRpc2NvdmVyZWQKYnkgdGhlIG9yaWdpbmFsIGF1dGhvcnMuCgpIb3cgbWFueSBvZiB0aGUgdG9wIFBDcyBzaG91bGQgd2UgcmV0YWluIGZvciBkb3duc3RyZWFtIGFuYWx5c2VzPyBUaGUgY2hvaWNlIG9mCnRoZSBudW1iZXIgb2YgUENzIGlzIGEgZGVjaXNpb24gdGhhdCBpcyBhbmFsb2dvdXMgdG8gdGhlIGNob2ljZSBvZiB0aGUgbnVtYmVyIG9mCkhWR3MgdG8gdXNlLiBVc2luZyBtb3JlIFBDcyB3aWxsIHJldGFpbiBtb3JlIGJpb2xvZ2ljYWwgc2lnbmFsIGF0IHRoZSBjb3N0IG9mCmluY2x1ZGluZyBtb3JlIG5vaXNlIHRoYXQgbWlnaHQgbWFzayBzYWlkIHNpZ25hbC4gT24gdGhlIG90aGVyIGhhbmQsIHVzaW5nIGZld2VyClBDcyB3aWxsIGludHJvZHVjZSBjb21wZXRpdGlvbiBiZXR3ZWVuIGRpZmZlcmVudCBmYWN0b3JzIG9mIHZhcmlhdGlvbiwgd2hlcmUKd2Vha2VyIChidXQgc3RpbGwgaW50ZXJlc3RpbmcpIGZhY3RvcnMgbWF5IGJlIHB1c2hlZCBkb3duIGludG8gbG93ZXIgUENzIGFuZAppbmFkdmVydGVudGx5IGRpc2NhcmRlZCBmcm9tIGRvd25zdHJlYW0gYW5hbHlzZXMuCgpNb3N0IGFuYWx5c3RzIHdpbGwgc2ltcGx5IGFpbSB0byB1c2UgYSDigJxyZWFzb25hYmxl4oCdIGJ1dCBhcmJpdHJhcnkgdmFsdWUsCnR5cGljYWxseSByYW5naW5nIGZyb20gMTAgdG8gNTAuIFRoaXMgaXMgb2Z0ZW4gc2F0aXNmYWN0b3J5IGFzIHRoZSBsYXRlciBQQ3MKZXhwbGFpbiBzbyBsaXR0bGUgdmFyaWFuY2UgdGhhdCB0aGVpciBpbmNsdXNpb24gb3Igb21pc3Npb24gaGFzIG5vIG1ham9yIGVmZmVjdC4KCmBgYHtyLCBldmFsID0gRkFMU0V9CnBlcmNlbnQudmFyIDwtIGF0dHIocmVkdWNlZERpbShzY2UpLCAicGVyY2VudFZhciIpCnBsb3QocGVyY2VudC52YXIsIGxvZz0ieSIsIHhsYWI9IlBDIiwgeWxhYj0iVmFyaWFuY2UgZXhwbGFpbmVkICglKSIpCnBsb3QoY3Vtc3VtKHBlcmNlbnQudmFyKSwgeGxhYj0iUEMiLCB5bGFiPSJDdW11bGF0aXZlIHZhcmlhbmNlIGV4cGxhaW5lZCAoJSkiKQpgYGAKCkhlcmUsIHJldGFpbmluZyDCsTE1UENzIHNlZW1zIHJlYXNvbmFibGUuIElmIHlvdSByZWFsbHkgcHJlZmVyIGEgbW9yZSBkYXRhLWRyaXZlbgp3YXkgZm9yIGRldGVybWluaW5nIHRoaXMsIHRoZXJlIGFyZSBwcm9jZWR1cmVzIGF2YWlsYWJsZSB0aGF0IGFpbSB0byAKY29tcHV0YXRpb25hbGx5IGlkZW50aWZ5IHRoZSBlbGJvdy9rbmVlIHBvaW50IGluIHRoZSB2YXJpYW5jZSBleHBsYWluZWQgcGVyIApQQyBwbG90LgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShQQ0F0b29scykKY2hvc2VuLmVsYm93IDwtIGZpbmRFbGJvd1BvaW50KHBlcmNlbnQudmFyKQpjaG9zZW4uZWxib3cKYGBgCgojIyMgUENBIHdpdGhvdXQgZmVhdHVyZSBzZWxlY3Rpb24KCk5vdGU6IG1vcmUgZmVhdHVyZXMgLT4gY29tcHV0YXRpb25hbGx5IG1vcmUgaW50ZW5zaXZlIQoKYGBge3IsIGV2YWwgPSBGQUxTRX0Kc2V0LnNlZWQoMTIzNCkKc2NlTm9GUyA8LSBydW5QQ0EoeCA9IC4uLiwgCiAgICAgICAgICAgICAgICAgIG5jb21wb25lbnRzID0gLi4uLCAKICAgICAgICAgICAgICAgICAgc3Vic2V0X3JvdyA9IDE6bnJvdyhzY2UpKQpwbG90UENBKC4uLikKcm0oc2NlTm9GUykgIyByZW1vdmUgdGhlIG9iamVjdCwgd2UgZG9uJ3QgbmVlZCBpdCBhbnltb3JlCmBgYAoKV2hpbGUgd2UgdXNlIG1vcmUgaW5mb3JtYXRpb24gdG8gbWFrZSB0aGlzIFBDQSBwbG90ICgxNy44ODcgZ2VuZXMpIGFzIGNvbXBhcmVkCnRvIHRoZSBmZWF0dXJlIHNlbGVjdGVkIFBDQSBwbG90ICg2NDIgZ2VuZXMpLCB3ZSBzZWVtIHRvIHJldHJpZXZlIGxlc3Mgc3RydWN0dXJlCmluIHRoZSBkYXRhLiBUaGlzIGlzIHRoZSBwb3dlciBvZiBmZWF0dXJlIHNlbGVjdGlvbiwgYW4gaW5jcmVhc2UgaW4gdGhlCnNpZ25hbC10by1ub2lzZSByYXRpbyEKCiMjIyBFZmZlY3Qgb2YgZmVhdHVyZSBzZWxlY3Rpb24gb24gUENBCgpGaXJzdCwgd2UgY29tcGFyZSB0aGUgZGlmZmVyZW50IGZlYXR1cmUgc2VsZWN0aW9uIGNyaXRlcmlhLCB1c2luZyB0aGUgdG9wIDEwMDAKaGlnaGx5IHZhcmlhYmxlIGdlbmVzIGZvciBlYWNoIG1ldGhvZC4KCmBgYHtyLCBldmFsID0gRkFMU0V9CiMgR2V0IHRvcCAxMDAwIGhpZ2hseSB2YXJpYWJsZSBmZWF0dXJlcyB1c2luZyB0aGUgaGlnaGx5IHZhcmlhYmxlIGdlbmVzCiMgKHcuci50LiBtZWFuKSwgaGlnaCBkZXZpYW5jZSBnZW5lcyBhbmQgdGhlIFZTVCBzdHJhdGVneSBvZiBTZXVyYXQKCmh2ZzEwMDAgPC0gLi4uICMgZXh0cmFjdCB0b3AgMTAwMCBoaWdobHkgdmFyaWFibGUgZ2VuZXMKaGRnMTAwMCA8LSAuLi4gIyBleHRyYWN0IHRvcCAxMDAwIGdlbmVzIHdpdGggaGlnaCBkZXZpYW5jZQp2c3QxMDAwIDwtIC4uLiAjIGV4dHJhY3QgdG9wIDEwMDAgZ2VuZXMgYmFzZWQgb24gVlNUIGFuYWx5c2lzCgojIEhWRyBzdHJhdGVneQpwbG90UENBKAogICAgcnVuUENBKHNjZSwKICAgICAgICAgICBuY29tcG9uZW50cyA9IDIsIAogICAgICAgICAgIHN1YnNldF9yb3cgPSBodmcxMDAwKSwgCiAgICBjb2xvdXJfYnkgPSAiY2x1c3RlciIpICsKICBnZ3RpdGxlKCJIaWdobHkgdmFyaWFibGUgZ2VuZXMiKQoKIyBIREcgc3RyYXRlZ3kKcGxvdFBDQSgKICAgIHJ1blBDQShzY2UsCiAgICAgICAgICAgbmNvbXBvbmVudHMgPSAyLCAKICAgICAgICAgICBzdWJzZXRfcm93ID0gaGRnMTAwMCksIAogICAgY29sb3VyX2J5ID0gImNsdXN0ZXIiKSArCiAgZ2d0aXRsZSgiSGlnaC1kZXZpYW5jZSBnZW5lcyIpCgojIFZTVCBzdHJhdGVneQpwbG90UENBKAogICAgcnVuUENBKHNjZSwKICAgICAgICAgICBuY29tcG9uZW50cyA9IDIsIAogICAgICAgICAgIHN1YnNldF9yb3cgPSB2c3QxMDAwKSwgCiAgICBjb2xvdXJfYnkgPSAiY2x1c3RlciIsCiAgICApICsKICBnZ3RpdGxlKCJTZXVyYXQgVlNUIikKYGBgCgpOZXh0LCB3ZSBhc3Nlc3MgdGhlIHNlbnNpdGl2aXR5IG9uIHRoZSBudW1iZXIgb2YgdG9wIGZlYXR1cmVzIGZvciB0aGUgaGlnaGx5CnZhcmlhYmxlIGdlbmVzIG1ldGhvZDsKCmBgYHtyLCBldmFsID0gRkFMU0V9Cmh2Z19hbGwgPC0gZ2V0VG9wSFZHcyhkZWMpCgpodmcyMDAwIDwtIC4uLiAjIGV4dHJhY3QgdG9wIDIwMDAgaGlnaGx5IHZhcmlhYmxlIGdlbmVzCmh2ZzEwMDAgPC0gLi4uICMgZXh0cmFjdCB0b3AgMTAwMCBoaWdobHkgdmFyaWFibGUgZ2VuZXMKaHZnMTAwIDwtIC4uLiAjIGV4dHJhY3QgdG9wIDEwMCBoaWdobHkgdmFyaWFibGUgZ2VuZXMKaHZnMTAgPC0gLi4uICMgZXh0cmFjdCB0b3AgMTAgaGlnaGx5IHZhcmlhYmxlIGdlbmVzCmh2ZzUgPC0gLi4uICMgZXh0cmFjdCB0b3AgNSBoaWdobHkgdmFyaWFibGUgZ2VuZXMKCnBsb3RQQ0EoCiAgICBydW5QQ0Eoc2NlLAogICAgICAgICAgIG5jb21wb25lbnRzID0gMiksIAogICAgY29sb3VyX2J5ID0gImNsdXN0ZXIiKSArCiAgZ2d0aXRsZSgiQWxsIGdlbmVzIikKCnBsb3RQQ0EoCiAgICBydW5QQ0Eoc2NlLAogICAgICAgICAgIG5jb21wb25lbnRzID0gMiwgCiAgICAgICAgICAgc3Vic2V0X3JvdyA9IGh2ZzIwMDApLCAKICAgIGNvbG91cl9ieSA9ICJjbHVzdGVyIikgKwogIGdndGl0bGUoIlRvcCAyMDAwIGdlbmVzIikKCnBsb3RQQ0EoCiAgICBydW5QQ0Eoc2NlLAogICAgICAgICAgIG5jb21wb25lbnRzID0gMiwgCiAgICAgICAgICAgc3Vic2V0X3JvdyA9IGh2ZzEwMDApLCAKICAgIGNvbG91cl9ieSA9ICJjbHVzdGVyIikgKwogIGdndGl0bGUoIlRvcCAxMDAwIGdlbmVzIikKCnBsb3RQQ0EoCiAgICBydW5QQ0Eoc2NlLAogICAgICAgICAgIG5jb21wb25lbnRzID0gMiwgCiAgICAgICAgICAgc3Vic2V0X3JvdyA9IGh2ZzEwMCksIAogICAgY29sb3VyX2J5ID0gImNsdXN0ZXIiKSArCiAgZ2d0aXRsZSgiVG9wIDEwMCBnZW5lcyIpCgpwbG90UENBKAogICAgcnVuUENBKHNjZSwKICAgICAgICAgICBuY29tcG9uZW50cyA9IDIsIAogICAgICAgICAgIHN1YnNldF9yb3cgPSBodmcxMCksIAogICAgY29sb3VyX2J5ID0gImNsdXN0ZXIiKSArCiAgZ2d0aXRsZSgiVG9wIDEwIGdlbmVzIikKCnBsb3RQQ0EoCiAgICBydW5QQ0Eoc2NlLAogICAgICAgICAgIG5jb21wb25lbnRzID0gMiwgCiAgICAgICAgICAgc3Vic2V0X3JvdyA9IGh2ZzUpLCAKICAgIGNvbG91cl9ieSA9ICJjbHVzdGVyIikgKwogIGdndGl0bGUoIlRvcCA1IGdlbmVzIikKYGBgCgojIyMgUmVtYXJrcyBvbiBQQ0EKClZpc3VhbGl6YXRpb25zIG9mIHJlZHVjZWQgZGltZW5zaW9ucyBmcm9tIGxpbmVhciBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24KbWV0aG9kcyBhcmUgb2Z0ZW4gIm92ZXJjcm93ZGVkIiwgYW5kIGl0IGlzIGhhcmQgdG8gc2VlIHN0cnVjdHVyZSAoZS5nLiwgdGhlIFBDQQpwbG90IHdlIGp1c3Qgc2F3KS4gTm9uLWxpbmVhciBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gbWV0aG9kcyBjYW4gb3ZlcmNvbWUgdGhpcwpwcm9ibGVtLiBBcyB0aGUgbmFtZSBzdWdnZXN0cywgdGhlIHJlZHVjZWQgZGltZW5zaW9ucyBhcmUgYSBub24tbGluZWFyIGZ1bmN0aW9uCm9mIHRoZSBvYnNlcnZlZCBkYXRhLiBXZSB3aWxsIG5vdCBnbyBpbnRvIGRldGFpbCBhcyB0byBob3cgdGhlc2Ugd29yayB1bmRlciB0aGUKaG9vZCwgYnV0IHByb3ZpZGUgYSBmZXcgZ3VpZGVsaW5lcyBmb3IgdGhlIG1vc3QgcG9wdWxhciBtZXRob2RzLiBPZnRlbiwgdGhlIHRvcAoofjEwLTUwKSBQQ3MgYXJlIHByb3ZpZGVkIGFzIGlucHV0LgoKIyMgQSBnZW5lcmFsaXphdGlvbiBvZiBQQ0EgZm9yIGV4cG9uZW50aWFsIGZhbWlseSBkaXN0cmlidXRpb25zLgoKUENBIGlzIGltcGxpY2l0bHkgYmFzZWQgb24gRXVjbGlkZWFuIGRpc3RhbmNlcywgY29ycmVzcG9uZGluZyB0byBtYXhpbWl6aW5nIGEKR2F1c3NpYW4gbGlrZWxpaG9vZCwgd2hpY2ggaXMgaW5hcHByb3ByaWF0ZSBmb3IgY291bnQgZGF0YSBzdWNoIGFzIHNjUk5BLXNlcS4KW1Rvd25lcyBldCBhbC5dKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMzA1OS0wMTktMTg2MS02KSAoMjAxOSkgZGV2ZWxvcApHTE0tUENBLCBhIGdlbmVyYWxpemF0aW9uIG9mIFBDQSBmb3IgZXhwb25lbnRpYWwgZmFtaWx5IGxpa2VsaWhvb2RzLiBUaGV5IHBvc2l0LAp1c2luZyBuZWdhdGl2ZSBjb250cm9sIGRhdGEsIHRoYXQgdGhlIGRhdGEgZ2VuZXJhdGl2ZSBtZWNoYW5pc20gb2YgVU1JIGNvdW50CmRhdGEgY2FuIGJlIGNvbnNpZGVyZWQgdG8gYmUgbXVsdGlub21pYWwuCgpUaGUgR0xNLVBDQSBzdHJhdGVneSBpcyBpbXBsZW1lbnRlZCBpbiB0aGUgYGdsbXBjYWAgZnVuY3Rpb24gb2YgdGhlIGBnbG1wY2FgCnBhY2thZ2UuCgpOb3RlIHRoYXQgdGhpcyBmdW5jdGlvbiBpcyBxdWl0ZSBjb21wdXRhdGlvbmFsbHkgaW50ZW5zaXZlLiBGb3IgcmVndWxhciBQQ0EgCm9uIGxvZy10cmFuc2Zvcm1lZCBub3JtYWxpemVkIGNvdW50cywgdGhlIHVuZGVybHlpbmcgY29tcHV0YXRpb25zIGNhbiBiZSAKc3Ryb25nbHkgc2ltcGxpZmllZC4gSGVyZSwgd2Ugd29yayB3aXRoIHRoZSByYXcgY291bnRzLCB3aGljaCB3ZSBhc3N1bWUgdG8gCmJlICpQb2lzc29uKiBkaXN0cmlidXRlZCwgcmVxdWlyaW5nIGFuIGl0ZXJhdGl2ZSBvcHRpbWl6YXRpb24gc2NoZW1lLgoKYGBge3IsIGV2YWw9RkFMU0V9CiMgcnVucyAybWluIG9uIG15IGxhcHRvcApsaWJyYXJ5KGdsbXBjYSkKc2V0LnNlZWQoMjExMTAzKQpwb2lwY2EgPC0gZ2xtcGNhKFkgPSAuLi4sICMgY291bnRzIGZvciB0aGUgaGlnbHkgdmFyaWFibGUgZ2VuZXMKICAgICAgICAgICAgICAgICBMID0gLi4uLCAjIHR3byBsYXRlbnQgZGltZW5zaW9ucyAoY29tcG9uZW50cykKICAgICAgICAgICAgICAgICBmYW0gPSAuLi4sICMgYXNzdW1lIFBvaXNzb24gZm9yIGRyb3BsZXQgZGF0YQogICAgICAgICAgICAgICAgIG1pbmliYXRjaCA9ICJzdG9jaGFzdGljIikgIyB1c2VkIGluIHRoZSBwYWNrYWdlIHZpZ25ldHRlIGJ5IAojIHRoZSBhdXRob3JzLCBidXQgYmV5b25kIHRoZSBzY29wZSBvZiB0aGlzIHNlc3Npb24uCnJlZHVjZWREaW0oc2NlLCAiUG9pUENBIikgPC0gcG9pcGNhJGZhY3RvcnMKcGxvdFJlZHVjZWREaW0oLi4uKSAjIG1ha2Ugc3VyZSB0byBzcGVjaWZ5IHRoZSByaWdodCB2YWx1ZSBmb3IgdGhlIGBkaW1yZWRgIGFyZ3VtZW50CmBgYAoKQWx0ZXJuYXRpdmVseSwgd2UgY291bGQgYWRvcHQgdGhlIEdMTS1QQ0Egc3RyYXRlZ3kgdXNpbmcgb25seSB0aGUgZ2VuZXMgd2l0aCAKaGlnaCBkZXZpYW5jZS4KCmBgYHtyLCBldmFsID0gRkFMU0V9CiMgQmFzZWQgb24gdGhlIGRpYWdub3N0aWMgcGxvdCBmcm9tIHRoZSBmZWF0dXJlIHNlbGVjdGlvbiwgd2Ugd291bGQgc2VsZWN0IHRoZSAKIyB0b3AgMjAwMCBnZW5lcyB3aXRoIGhpZ2hlc3QgZGV2aWFuY2UuIEhvd2V2ZXIsIHRvIHJlZHVjZSB0aGUgcnVubmluZyB0aW1lIG9mCiMgdGhlIGZ1bmN0aW9uLCBJIGhlcmUgc2VsZWN0IHRoZSB0b3AgNTAwICh3aGljaCBzdGlsbCBpcyBxdWl0ZSBzbG93IC0gNW1pbikuCmhkZzUwMCA8LSBuYW1lcyhzb3J0KHJvd0RhdGEoc2NlKSRiaW5vbWlhbF9kZXZpYW5jZSwgZGVjcmVhc2luZyA9IFRSVUUpKVsxOjUwMF0KCi4uLgpgYGAKClRoZSBhdXRob3JzIG9mIHRoZSBgZ2xtcGNhYCBwYWNrYWdlIG5vdGUgdGhhdCBHTE0tUENBIGNhbiBiZSBzbG93IGZvciBsYXJnZSAKZGF0YXNldHMuIFRoZXJlZm9yZSwgdGhleSBoYXZlIGltcGxlbWVudGVkIGEgZmFzdCBhcHByb3hpbWF0aW9uIG9mIHRoZSAKYWxnb3JpdGhtLCB3aGljaCBmaXJzdCBmaXRzIGEgbnVsbCBtb2RlbCBvZiBjb25zdGFudCBleHByZXNzaW9uIGZvciBlYWNoIGdlbmUgCmFjcm9zcyBhbGwgY2VsbHMsIGFuZCBzdWJzZXF1ZW50bHkgZml0cyBzdGFuZGFyZCBQQ0EgdG8gZWl0aGVyIHRoZSBQZWFyc29uIG9yIApkZXZpYW5jZSByZXNpZHVhbHMgZnJvbSB0aGUgbnVsbCBtb2RlbC4KCkhvd2V2ZXIsIGF0IGxlYXN0IGZvciBtZSB0aGUgYG51bGxSZXNpZHVhbHNgIGZ1bmN0aW9uIHdhcyAqKmV4dHJlbWVseSBzbG93KioKZXZlbiBzbG93ZXIgdGhhbiB0aGUgYGdsbXBjYWAgY29kZSBhYm92ZS4gVGhlcmVmb3JlLCAqKkkgc3VnZ2VzdCBub3QgcnVubmluZyoqCnRoaXMgY29kZSwgYnV0IEkgbGVhdmUgaXQgaW4gZm9yIHJlZmVyZW5jZS4KCmBgYApzY2UgPC0gbnVsbFJlc2lkdWFscyhzY2UsIGFzc2F5PSJjb3VudHMiLCB0eXBlPSJkZXZpYW5jZSIpCnNjZSA8LSBudWxsUmVzaWR1YWxzKHNjZSwgYXNzYXk9ImNvdW50cyIsIHR5cGU9InBlYXJzb24iKQoKcGNhPC1mdW5jdGlvbihZLCBMPTIsIGNlbnRlcj1UUlVFLCBzY2FsZT1UUlVFKXsKICAgICNhc3N1bWVzIGZlYXR1cmVzPXJvd3MsIG9ic2VydmF0aW9ucz1jb2xzCiAgICByZXM8LXByY29tcChhcy5tYXRyaXgodChZKSksIGNlbnRlcj1jZW50ZXIsIHNjYWxlLj1zY2FsZSwgcmFuay49TCkKICAgIGZhY3RvcnM8LWFzLmRhdGEuZnJhbWUocmVzJHgpCiAgICBjb2xuYW1lcyhmYWN0b3JzKTwtcGFzdGUwKCJkaW0iLCAxOkwpCiAgICBmYWN0b3JzCn0KcGNhX2QgPC0gcGNhKGFzc2F5KHNjZVtoZGcsXSwgImJpbm9taWFsX2RldmlhbmNlX3Jlc2lkdWFscyIpKQpwY2FfZCRyZXNpZF90eXBlIDwtICJkZXZpYW5jZV9yZXNpZHVhbHMiCnBjYV9wIDwtIHBjYShhc3NheShzY2VbaGRnLF0sICJiaW5vbWlhbF9wZWFyc29uX3Jlc2lkdWFscyIpKQpwY2FfcCRyZXNpZF90eXBlIDwtICJwZWFyc29uX3Jlc2lkdWFscyIKY20gPC0gYXMuZGF0YS5mcmFtZShjb2xEYXRhKHNjZVtoZGcsXSkpCnBkIDwtIHJiaW5kKGNiaW5kKGNtLCBwY2FfZCksIGNiaW5kKGNtLCBwY2FfcCkpCmdncGxvdChwZCwgYWVzKHg9ZGltMSwgeT1kaW0yLCBjb2xvdXI9cGhlbm9pZCkpICsgZ2VvbV9wb2ludCgpICsKICBmYWNldF93cmFwKH5yZXNpZF90eXBlLCBzY2FsZXM9ImZyZWUiLCBucm93PTIpICsKICBnZ3RpdGxlKCJQQ0EgYXBwbGllZCB0byBudWxsIHJlc2lkdWFscyBvZiBoaWdoIGRldmlhbmNlIGdlbmVzIikKYGBgCgojIyBOb24tbGluZWFyIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbjogdC1TTkUKCnQtU05FIGZvY3VzZXMgb24gcHJlc2VydmluZyBsb2NhbCByYXRoZXIgdGhhbiBnbG9iYWwgZGlzdGFuY2VzLiBUaGVyZWZvcmUsCmRpc3RhbmNlcyBvbiBhIHQtU05FIHJlZHVjZWQgZGltZW5zaW9uIHBsb3QgY2FuIG9ubHkgYmUgaW50ZXJwcmV0ZWQgbG9jYWxseSwKaS5lLiwgY2VsbHMgdGhhdCBhcmUgY2xvc2UgdG9nZXRoZXIgaW4gcmVkdWNlZCBkaW1lbnNpb24gd2lsbCBoYXZlIGEgc2ltaWxhcgp0cmFuc2NyaXB0b21lLCBidXQgY2VsbHMgdGhhdCBhcmUgZmFyIGF3YXkgbWF5IG5vdCBuZWNlc3NhcmlseSBoYXZlIGEgdmVyeSBkaXN0aW5jdCB0cmFuc2NyaXB0b21lLgoKUnVubmluZyB0LVNORSBvbiBhIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgb2JqZWN0IGNhbiBiZSBhY2hpZXZlZCB3aXRoIHRoZQpgcnVuVFNORWAgZnVuY3Rpb24gb2YgdGhlIGBzY2F0ZXJgIHBhY2thZ2UuIEJ5IGRlZmF1bHQsIHRoaXMgZnVuY3Rpb24gd2lsbCBmaXJzdApwZXJmb3JtIFBDQSwgYW5kIHVzZSB0aGUgdG9wIDUwIFBDcyBhcyBhbiBpbnB1dCB0byB0aGUgYWN0dWFsIHQtU05FCmFsZ29yaXRobS4gU2luY2Ugd2UgYWxyZWFkeSBwZXJmb3JtZWQgUENBLCB3ZSBtYXkgc2V0IGBkaW1yZWQgPSAiUENBImAgYXMKYXJndW1lbnQgdG8gdGhlIGZ1bmN0aW9uLiBBcyBzdWNoIHdlIHdpbGwgYmUgcGVyZm9ybWluZyBhIFQtU05FIG9uIHRoZSAzMCBQQ3MKd2UgY29tcHV0ZWQgYmVmb3JlLiBJZiB3ZSB3b3VsZCBsaWtlIHRvIHJ1biB0LVNORSBvbmx5IHVzaW5nIHRoZSB0b3AgMTAKUENzLCB3ZSBjb3VsZCBzZXQgYG5fZGltcmVkID0gMTBgLgoKSW4gYWRkaXRpb24sIHdlIG1heSB3aXNoIHRvIHNldCBgZXh0ZXJuYWxfbmVpZ2hib3JzPVRSVUVgLCB3aGljaCBpbmNyZWFzZXMgdGhlCnNwZWVkIG9mIHRoZSBhbGdvcml0aG0gZm9yIGxhcmdlIGRhdGFzZXRzIGJ5IGFwcGx5aW5nIGEgaGV1cmlzdGljLgoKKipOb3RlOioqIGZvciBtZSB0aGlzIHdhcyB0aGUgc2xvd2VzdCBmdW5jdGlvbiBvZiB0aGUgYW5hbHlzaXMgKHNvIGZhcikuIElmIHlvdQpmZWVsIGxpa2UgeW91ciBQQy9sYXB0b3AgaGFkIGEgbG90IG9mIHRyb3VibGUgd2l0aCB0aGUgcHJldmlvdXMgc3RlcChzKSwgeW91Cm1heSBjb25zaWRlciBub3QgcnVubmluZyB0aGlzIGNvZGUsIG9yIHJ1bm5pbmcgaXQgb24gYSBzdWJzZXQgb2YgdGhlIGRhdGEKKGUuZy4sIHJhbmRvbWx5IHN1YnNhbXBsaW5nIGNlbGxzKSBmb3IgZGVtb25zdHJhdGlvbmFsIHB1cnBvc2VzLiAKQWx0ZXJuYXRpdmVseSwgeW91IG1heSBjb25zaWRlciByZWR1Y2luZyB0aGUgaW5wdXQgc3BhY2UgZm9yIHRoZSB0LVNORSAKYWxnb3JpdGhtLCBlLmcuIGJ5IHNldHRpbmcgYG5fZGltcmVkID0gNWAuCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQojIChGb3IgbWUgaXQgdGFrZXMgM21pbjMwIHdpdGggbl9kaW1yZWQgPSA1KQpzY2UgPC0gcnVuVFNORSh4ID0gLi4uLCAKICAgICAgICAgICAgICAgZGltcmVkID0gLi4uLAogICAgICAgICAgICAgICBuX2RpbXJlZCA9IC4uLiwKICAgICAgICAgICAgICAgZXh0ZXJuYWxfbmVpZ2hib3JzID0gLi4uKQpwbG90VFNORShzY2UsCiAgICAgICAgIGNvbG91cl9ieSA9ICJjbHVzdGVyIikKYGBgCgojIyBOb24tbGluZWFyIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbjogVU1BUAoKSXQgaXMgb2Z0ZW4gc3VnZ2VzdGVkIHRoYXQgVU1BUCBpcyBiZXR0ZXIgdGhhbiB0LVNORSBpbiBwcmVzZXJ2aW5nIGdsb2JhbCAKZGlmZmVyZW5jZXMuIFRoZXJlZm9yZSwgVU1BUCBpcyBhbHNvIG9mdGVuIHVzZWQgaW4gYW5hbHlzZXMgc3VjaCBhcyB0cmFqZWN0b3J5IAppbmZlcmVuY2UsIHdoZXJlIHRoaXMgaXMgaW1wb3J0YW50LgoKUnVubmluZyBVTUFQIG9uIGEgYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3QgY2FuIGJlIGFjaGlldmVkIHdpdGggdGhlCmBydW5VTUFQYCBmdW5jdGlvbiBvZiB0aGUgYHNjYXRlcmAgcGFja2FnZS4KCmBgYHtyLCBldmFsID0gRkFMU0V9CiMgVXNpbmcgdG9wIDEwJSBoaWdobHkgdmFyaWFibGUgZ2VuZXMgYW5kIHRvcCAzMCBQQ3MKc2NlIDwtIHJ1blVNQVAoLi4uKQpwbG90VU1BUCguLi4pCgojIFVzaW5nIHRvcCAxMCUgaGlnaGx5IHZhcmlhYmxlIGdlbmVzIGFuZCB0b3AgMTAgUENzCnBsb3RVTUFQKHJ1blVNQVAoLi4uKSwKICBjb2xvdXJfYnkgPSAiY2x1c3RlciIpCmBgYAoK