Workflow Overview
Introduction
In this section, we will use principal component analysis (PCA) to
reduce the dimensionality of the data, first by grouping correlated
features and second by excluding correlated features that are less
likely to correspond to interesting biological variance.
|
A. Clustering the expression patterns directly on the feature-barcode
count matrix is impractical because the expression patterns in the
counts are often weak, repetitive, and diffuse (scattered across many,
many genes). Also clustering cells across 20k dimensions (i.e. 20k
genes) is extremely computationally intensive. B. Principal
Component Analysis considers (PCA) the count data from new orientations
that maximize the variance across the data. In this way, PCA replaces
the original count matrix with new matrix of principal component (PC)
values for each cell. C. Because PCA ranks the PCs from most
variance to least, the truly discriminating PCs will be a small subset
of top-most PCs. Focusing only on the top 10-30 PCs instead of 20k genes
tells the same story, but cuts through 99.9% of the clutter making
clustering about 1000x faster.
|
After filtering and normalization, our next goal is to cluster the
cells according to their expression profiles. However, even after
filtering, we expect to have about 20,000 cells assayed with
approximately 21,000 genes measured per cell. This means that we are
working very “high-dimensional” data.
High dimensional data presents two challenges:
- The scale of the data makes analysis steps costly and slow
- There is likely uninteresting variation across our data that we want
to exclude
Similar to the previous sections, the process of parameter selection
after PCA is often iterative and while we will be showing a single
option, multiple parameter choices may need to be tested.
Objectives
- Understand why PCA is used to reduce high-dimensional single-cell
data prior to integration and clustering
- Use the
RunPCA()
function to generate principal
components for our data
- Choose an appropriate number of principal components to capture
variance in our data
- Use
IntegrateLayers
to integrate across our data prior
to clustering
What is PCA?
In practical terms, Principal component analysis (PCA) creates a
simpler, more concise version of data while preserving the same “story”.
For example, we can think of a full single-cell data set as similar to
the full Lord of the Rings trilogy - large and complex with quite a bit
of detail that might be repeated and isn’t necessary to understand the
overall plot and make running the analysis prohibitively slow and
costly.
Like a Cliff notes version or a movie pitch for the book series, the
PCs capture correlated expression and reduce the size and complexity of
the data, while still allowing the overall structure of the data - that
we would expect to be related to cell-types or plot points in the
analogy - to be represented, while also making the computation
faster.
PCA example
To understand how PCA works for single-cell data, we can consider a
smaller dataset measuring the expression of four genes measured in just
two cells, we could plot the expression of those four genes, with data
from one cell plotted on the x-axis and the second cell plotted on the
y-axis.
If we wanted to represent the most variation across the data, we
would draw a diagonal line between gene B and gene C - this would
represent the first principal component (PC). However, this line doesn’t
capture all the variance in this data as the genes also vary above and
below the line, so we could draw another line (at a right angle to the
first) representing the second most variation in the data - which would
represent the second principal component (PC).
We’ll skip the process of generating all possible PCs, but running
PCA on our data will result in a score for each cell for each PC and
each gene will have a weight or loading for a given PC. By definition,
the PCs capture the greatest factors of heterogeneity in decreasing
order in the data set (source).
Why PCA is useful for single-cell data
In contrast to bulk RNA-seq, where the majority of the variance in a
given dataset are usually explained by the first and second PC, for
single-cell data we expect that many more PCs are contributing to the
overall variance.
However, we can assume that biological processes affect multiple
genes in a coordinated way. Therefore the top PCs likely represent
biological structure rather than random technical or biological noise,
which affects each gene independently and and we can use the top several
PCs to approximate the full data set in downstream analysis (source).
This reduction in dimensionality, for example from 10,000 cells x
19,000 genes to 20 PCs, allows us to select PCs that are more likely to
correspond to biological variation related to the expected cell
types/subtypes and make the downstream clustering computationally
feasible.
More context using PCA for single-cell
data
To read more on PCA, please refer to the HBC
- Theory of PCA content, from which this section is adapted and the
original source material for that content, specifically Josh Starmer’s
StatQuest video.
For additional detail on , the OSCA chapter on Principal
components analysis includes a more descriptive overview.
Sparsity in single-cell data
In addition to the “high-dimensionality” expected for single-cell
data, we expect the data to be both “sparse” and “noisy”. Sparse in the
sense that many genes will either not be expressed or not measured in
many of the cells and have zero values. Noisy due to biological
variability and the practical limitations of both capture and sequencing
depth (source).
Note on zero “inflation” of single-cell data
Single-cell data is sometimes described as “zero inflated”, however
work by Svensson
has challenged that characterization and argued that the higher number
of zeros observed in scRNA-seq compared to bulk RNA-seq is more likely
due to biological variance and lower sequencing saturation than
technical artifacts. Work by Choi
et al. (2020) support that zeros in single-cell data are due to
biology but Jiang et
al. (2022) delves more into the “controversy” zero inflation,
including common approaches for handling zeros and the downstream
impacts.
For sparse, high-dimensional, biological data, we expect
many genes to have correlated expression as they would be impacted by
the same biological process and for other genes to have either low (more
noisy) or similar expression across the cell population and therefore
not likely to correspond to cell-type/subtypes.
So how do we determine what and how many genes to use before
classifying cells into clusters based on their expression?
More detail on dimensionality reduction
The Ouyang
Lab has a “gentle introduction” section of their materials that goes
into greater details on dimensionality reduction including how similar
strategies are used in deep learning models. Additionaly, the OSCA book
has a chapter
on Dimensionality reduction that has useful context.
Run PCA on our dataset
Since PCA is sensitive to scale, we will run it on the SCT normalized
assay (reference).
We will name the reduction in an informative way to keep them clear for
us in the future. In this case, our data has not yet been integrated,
but it has been SCT normalized. So we will name the reduction
unintegrated.sct.pca
. Note that SCTTransform()
returned a set of highly variable genes, and the RunPCA()
function will use this subset to determine the PCs and genes associated
with those PCs.
##### Day 2 - PCA and Integration
# Build a PCA and add it to the Seurat object ----------------------------
geo_so = RunPCA(geo_so, reduction.name = 'unintegrated.sct.pca')
geo_so
An object of class Seurat
46955 features across 31560 samples within 2 assays
Active assay: SCT (20466 features, 3000 variable features)
3 layers present: counts, data, scale.data
1 other assay present: RNA
1 dimensional reduction calculated: unintegrated.sct.pca
After running the command in the console, we should see that the
geo_so
Seurat object has a new reduction. Viewed in our
running schematic:
Image: Schematic after RunPCA().
We can then visualize the first several PCs using the
DimHeatmp()
function, which orders both cells and features
(genes) according to their PCA scores and allows us to see some general
patterns in the data. Here we will specify that the first 24 PCs be
included in the figure and that 500 cells are randomly subsetted for the
plot. Note - you may need to increase your plot window size to
resolve errors regarding figure margins.
# Build heatmaps of PCAs -------------------------------------------------
# Plot cell by gene heatmaps for first several PCs
DimHeatmap(geo_so, dims=1, cells=500, balanced=TRUE, reduction = 'unintegrated.sct.pca') # look at first PC alone first
DimHeatmap(geo_so, dims=1:18, cells=500, balanced=TRUE, reduction = 'unintegrated.sct.pca') # first 18 PCs
# note - need to use png() to write to file because this isn't a ggplot
png(filename = 'results/figures/qc_pca_heatmap.png', width = 12, height = 40, units = 'in', res = 300)
DimHeatmap(geo_so, dims=1:18, cells=500, balanced=TRUE, reduction = 'unintegrated.sct.pca')
dev.off()
As we scroll down in this plot, we see less and less structure in our
plots indicating that no only is less variation is being represented by
larger PCs overall but also that those PCs are less likely to correspond
to variation of interest, e.g. corresponding to cell types/subtypes.
What genes are contributing the most to each PC?
We can also visualize the loadings for genes contributing to each
principal components using Seurat provided functions source.
We can highlight genes loaded for dimenstions of interest using using
VizDimLoadings()
:
# Visualize gene loadings for PC1 and PC2 ---------------------------------
VizDimLoadings(geo_so, dims = 1:2, reduction = 'unintegrated.sct.pca')
ggsave(filename = 'results/figures/qc_pca_loadings.png', width = 12, height = 6, units = 'in')
How
does Seurat use PCA scores?
Per the Ho
Lab’s materials - “To overcome the extensive technical noise in any
single feature for scRNA-seq data, Seurat clusters cells based on their
PCA scores, with each PC essentially representing a ‘metafeature’ that
combines information across a correlated feature set. The top principal
components therefore represent a robust compression of the dataset.”
Visualizing relative contributions of each PC
One way to evaluate the PCs to include is by looking at an elbow
plot, which shows the percent variance explained by successive PCs.
We’ll use the ElbowPlot()
function to do this, specifying
that the first 50 PCs be plotted.
# Visualize how many PCs to include using an elbow plot -------------------
ElbowPlot(geo_so, ndims = 50, reduction = 'unintegrated.sct.pca')
ggsave(filename = 'results/figures/qc_sct_elbow_plot.png', width = 8, height = 8, units = 'in')
In this plot, we could arbitrarily choose a number along the x-axis
that looks like a sharp change in the variance from one PC to the next,
that is, an elbow. Of course, while that’s often the recommendation in
tutorials, the choice of where the point of the “elbow” is, is not
always obvious, and this plot is no different. We could also try to
quantify our choice.
Choosing the number of significant PCs for dimensionality
reduction
As we discussed above, we don’t want to use all the PCs generated for
a single cell data set but instead want to select a subset to limit the
downstream steps to variation that’s more likely to correspond to cell
types or subtypes. So how many PCs should be used?
A good starting point for determining how many PCs to select for your
single-cell analysis is to understand the “resolution” of your
biological question. Is answering your biological question dependent on
identifying rarer cell types or specific subtypes? Or are broader
cell-types more relevant?
For this dataset, we are expecting a diversity of cell types and cell
populations that mediate wound healing, but are also that are part of
the aberrant transition to bone, which might be more rare in the
population. So for this dataset, we might want to consider starting with
more PCS rather than too few PCs to start. Again, in a full analysis
workflow, our selection at this step might be more of a starting point
for further iterations than a final decision.
Using a (crude) optimization to select a starting point
Instead of choosing based on the elbow plot by sight alone, we can
try to quantify our choice. Here we create a function to return a
minimum PCs based on two possible metrics (cumulative variation or above
a minium step size) and then we’ll apply that function to our data:
# Estimate optimal PCs for clustering with a function -------------------------
optimal_pcs = function(so, reduction) {
# quantitative check for number of PCs to include
pct = so@reductions[[reduction]]@stdev / sum(so@reductions[[reduction]]@stdev) * 100
cum = cumsum(pct)
co1 = which(cum > 90 & pct < 5)[1]
co2 = sort(which((pct[1:length(pct)-1] - pct[2:length(pct)]) > .1), decreasing = T)[1] + 1
pcs = min(co1, co2)
return(pcs)
}
# Apply function to our data
pcs = optimal_pcs(geo_so, 'unintegrated.sct.pca')
pcs
[1] 16
# Based on the heatmap, we'll modify to 13 PCs
pcs = 13
Again, this number is likely a starting point and may need to be
revised depending on the outcome of the downstream steps.
Other more quanitative parameter selection
options
While outside the scope of this workshop, there are community efforts
to develop more sophisticated methods to select an appropriate number of
PCs, like the chooseR
package findPC
package or using
clustering trees to evaluate the stability of parameter choices.
Visualizing our PC results
In addition to selecting a reduced number of PCs to represent our
data, we can also visualize , similarly to how we often look at samples
for bulk RNA-seq using the DimPlot()
function, with each
cell plotted along the selected PCs and colored by a selected meta-data
column:
?DimPlot # default dims = c(1,2)
# Visualize PCA (PC1 and PC2, unintegrated) -------------------------------
DimPlot(geo_so, reduction = 'unintegrated.sct.pca', group.by = 'orig.ident') # first label by sample
DimPlot(geo_so, reduction = 'unintegrated.sct.pca', group.by = 'day') # then label by day
ggsave(filename = 'results/figures/qc_pca_plot_unintegrated_sct_day.png', width = 7, height = 6, units = 'in') # save
In the first plot with each cell labeled by the original sample name,
it looks like there might be some batches but it’s hard to distinguish.
From the second by day
, we can see that even after
normalization there seems to be variation that corresponds more to a
batch effect related to the day status than interesting biological
variation. This suggests that clustering our data using our selected
number of PCs, integration should be performed to mitigate these
differences and better align our data.
Integrate Layers
Based on our PCA plot and often a standard part of multiple sample
scRNA-seq analysis, we will integrate our data across all samples.
Currently, each sample is stored as a layer in our geo_so
object, Since earlier in the workshop, we used
SCTransform()
to normalize our data, select variable genes
and then have generated a PCA reduction, we can use the
IntegrateLayers()
function to do that.
The details of how integration works depends on the method, but for
the RPCA approach that we’ll be using here - the process is similar to
the canonical correlation analysis (CCA) approach illustration above (source).
However, RPCA is more efficient (faster) to run and better preserves
distinct cell identities between samples (source).
As described in the corresponding Seurat
tutorial, each dataset is projected into the others’ PCA space and
constrain the anchors
New to Seurat v5 are improvements that make selecting different
integration methods much easier. The results of each alternative
integration method are stored within the same Seurat
object, which makes comparing downstream effects much easier. So, if we
wanted to run the RPCAIntegration
method, we would run
(but we won’t here):
### DO NOT RUN ###
geo_so = IntegrateLayers(
object = geo_so,
method = RPCAIntegration,
orig.reduction = 'unintegrated.sct.pca',
normalization.method = 'SCT',
new.reduction = 'integrated.sct.rpca')
Note that the integration is run on the layers, which from our prior
steps each correspond to a single sample, in our geo_so
Seurat object. For additional information, refer to the Seurat v5
vignette on integrative analysis (link)
provides examples of each integration method. Note, that the vignette
code uses the NormalizeData()
, ScaleData()
,
FindVariableFeatures()
pipeline, so their
IntegrateLayers()
call does not include
normalization.method = 'SCT'
, as ours must.
Note we have specified the unintegrated reduction
unintegrated.sct.pca
, which is what
IntegrateLayers()
operates on, along with the
SCT
assay. Let’s take a look to see what’s different about
the Seurat
object:
Load integrated data
Because the IntegrateLayers()
function takes a while to
run, we will simply load the RPCA integrated geo_so
object
from a file we have previously generated.
geo_so = readRDS('/home/workshop/rcavalca/ISC_R/results/rdata/geo_so_sct_integrated.rds')
# Check our updated object that we've read in from file -------------------
# Observe that we now have a new reduction, `integrated.sct.rpca`
geo_so
An object of class Seurat
46955 features across 31560 samples within 2 assays
Active assay: SCT (20466 features, 3000 variable features)
3 layers present: counts, data, scale.data
1 other assay present: RNA
2 dimensional reductions calculated: unintegrated.sct.pca, integrated.sct.rpca
Viewed in our running schematic:
Image: Schematic after IntegrateLayers().
We can also confirm that our integration method has helped to correct
the Day
effects we saw in the initial PCA plots
# Visualize PCA (PC1 and PC2, integrated) ---------------------------------
DimPlot(geo_so, reduction = 'integrated.sct.rpca', group.by = 'day')
ggsave(filename = 'results/figures/qc_pca_plot_integrated_sct_day.png', width = 7, height = 6, units = 'in')
Save our progress
Before we move on, let’s save our updated Seurat object to file:
# Save updated seurat object to file -------------------------------------
saveRDS(geo_so, file = 'results/rdata/geo_so_sct_integrated.rds')
Other integration methods
After normalizing the data with SCTransform()
and
performed the dimension reduction with RunPCA()
,
alternatively we could also use the CCA
integration method
with:
### DO NOT RUN ###
geo_so = IntegrateLayers(
object = geo_so,
method = CCAIntegration,
orig.reduction = 'unintegrated.sct.pca',
normalization.method = 'SCT',
new.reduction = 'integrated.sct.cca')
Summary
In this section, we:
- Discussed how PCA is used in scRNA-seq analysis.
- Demonstrated some visualizations after computing PCA.
- Discussed how to decide how many PCs to use in downstream
analysis.
- Integrated the data.
Next steps: Clustering and projection
These materials have been adapted and extended from materials listed
above. These are open access materials distributed under the terms of
the Creative
Commons Attribution license (CC BY 4.0), which permits unrestricted
use, distribution, and reproduction in any medium, provided the original
author and source are credited.
LS0tCnRpdGxlOiAiUENBIGFuZCBJbnRlZ3JhdGlvbiIKYXV0aG9yOiAiVU0gQmlvaW5mb3JtYXRpY3MgQ29yZSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgICAgICAgaHRtbF9kb2N1bWVudDoKICAgICAgICAgICAgaW5jbHVkZXM6CiAgICAgICAgICAgICAgICBpbl9oZWFkZXI6IGhlYWRlci5odG1sCiAgICAgICAgICAgIHRoZW1lOiBwYXBlcgogICAgICAgICAgICB0b2M6IHRydWUKICAgICAgICAgICAgdG9jX2RlcHRoOiA0CiAgICAgICAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICAgICAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICAgICAgICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICAgICAgICAgIG1hcmtkb3duOiBHRk0KICAgICAgICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CmJvZHksIHRkIHsKICAgZm9udC1zaXplOiAxOHB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDEycHg7Cn0KcHJlIHsKICBmb250LXNpemU6IDEycHgKfQoKdGFibGUuZmlnLCB0aC5maWcsIHRkLmZpZyB7CiAgYm9yZGVyOiAxcHggc29saWQgYmxhY2s7CiAgYm9yZGVyLWNvbGxhcHNlOiBjb2xsYXBzZTsKICBwYWRkaW5nOiAxNXB4Owp9Cjwvc3R5bGU+CgpgYGB7ciBrbGlwcHksIGVjaG89RkFMU0UsIGluY2x1ZGU9VFJVRX0Ka2xpcHB5OjprbGlwcHkobGFuZyA9IGMoInIiLCAibWFya2Rvd24iLCAiYmFzaCIpLCBwb3NpdGlvbiA9IGMoInRvcCIsICJyaWdodCIpKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnNvdXJjZSgiLi4vYmluL2NodW5rLW9wdGlvbnMuUiIpCmtuaXRyX2ZpZ19wYXRoKCIwNC1QQ0FhbmRJbnRlZ3JhdGlvbi8wNC0iKQpgYGAKIyBXb3JrZmxvdyBPdmVydmlldyB7LnVubGlzdGVkIC51bm51bWJlcmVkfQoKPGJyLz4KPGltZyBzcmM9ImltYWdlcy93YXlmaW5kZXIvd2F5ZmluZGVyLnBuZyIgYWx0PSJ3YXlmaW5kZXIiIHN0eWxlPSJoZWlnaHQ6IDQwMHB4OyIvPgo8YnIvPgo8YnIvPgoKIyBJbnRyb2R1Y3Rpb24gCgpJbiB0aGlzIHNlY3Rpb24sIHdlIHdpbGwgdXNlIHByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgKFBDQSkgdG8gcmVkdWNlIHRoZSBkaW1lbnNpb25hbGl0eSBvZiB0aGUgZGF0YSwgZmlyc3QgYnkgZ3JvdXBpbmcgY29ycmVsYXRlZCBmZWF0dXJlcyBhbmQgc2Vjb25kIGJ5IGV4Y2x1ZGluZyBjb3JyZWxhdGVkIGZlYXR1cmVzIHRoYXQgYXJlIGxlc3MgbGlrZWx5IHRvIGNvcnJlc3BvbmQgdG8gaW50ZXJlc3RpbmcgYmlvbG9naWNhbCB2YXJpYW5jZS4KCjx0YWJsZSBjbGFzcz0nZmlnJz4KPHRyIGNsYXNzPSdmaWcnPjx0ZCBjbGFzcz0nZmlnJz4hW10oaW1hZ2VzL2dyYXBoaWNhbF9hYnN0cmFjdHMvZ3JhcGhpY2FsX2Fic3RyYWN0X3BjYS5wbmcpPC90ZD48L3RyPgo8dHIgY2xhc3M9J2ZpZyc+PHRkIGNsYXNzPSdmaWcnPkEuIENsdXN0ZXJpbmcgdGhlIGV4cHJlc3Npb24gcGF0dGVybnMgZGlyZWN0bHkgb24gdGhlIGZlYXR1cmUtYmFyY29kZSBjb3VudCBtYXRyaXggaXMgaW1wcmFjdGljYWwgYmVjYXVzZSB0aGUgZXhwcmVzc2lvbiBwYXR0ZXJucyBpbiB0aGUgY291bnRzIGFyZSBvZnRlbiB3ZWFrLCByZXBldGl0aXZlLCBhbmQgZGlmZnVzZSAoc2NhdHRlcmVkIGFjcm9zcyBtYW55LCBtYW55IGdlbmVzKS4gQWxzbyBjbHVzdGVyaW5nIGNlbGxzIGFjcm9zcyAyMGsgZGltZW5zaW9ucyAoaS5lLiAyMGsgZ2VuZXMpIGlzIGV4dHJlbWVseSBjb21wdXRhdGlvbmFsbHkgaW50ZW5zaXZlLjxici8+CkIuIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgY29uc2lkZXJzIChQQ0EpIHRoZSBjb3VudCBkYXRhIGZyb20gbmV3IG9yaWVudGF0aW9ucyB0aGF0IG1heGltaXplIHRoZSB2YXJpYW5jZSBhY3Jvc3MgdGhlIGRhdGEuIEluIHRoaXMgd2F5LCBQQ0EgcmVwbGFjZXMgdGhlIG9yaWdpbmFsIGNvdW50IG1hdHJpeCB3aXRoIG5ldyBtYXRyaXggb2YgcHJpbmNpcGFsIGNvbXBvbmVudCAoUEMpIHZhbHVlcyBmb3IgZWFjaCBjZWxsLjxici8+CkMuIEJlY2F1c2UgUENBIHJhbmtzIHRoZSBQQ3MgZnJvbSBtb3N0IHZhcmlhbmNlIHRvIGxlYXN0LCB0aGUgdHJ1bHkgZGlzY3JpbWluYXRpbmcgUENzIHdpbGwgYmUgYSBzbWFsbCBzdWJzZXQgb2YgdG9wLW1vc3QgUENzLiBGb2N1c2luZyBvbmx5IG9uIHRoZSB0b3AgMTAtMzAgUENzIGluc3RlYWQgb2YgMjBrIGdlbmVzIHRlbGxzIHRoZSBzYW1lIHN0b3J5LCBidXQgY3V0cyB0aHJvdWdoIDk5LjklIG9mIHRoZSBjbHV0dGVyIG1ha2luZyBjbHVzdGVyaW5nIGFib3V0IDEwMDB4IGZhc3Rlci4KPC90ZD48L3RyPgo8L3RhYmxlPgo8YnIvPgoKQWZ0ZXIgZmlsdGVyaW5nIGFuZCBub3JtYWxpemF0aW9uLCBvdXIgbmV4dCBnb2FsIGlzIHRvIGNsdXN0ZXIgdGhlIGNlbGxzIGFjY29yZGluZyB0byB0aGVpciBleHByZXNzaW9uIHByb2ZpbGVzLiBIb3dldmVyLCBldmVuIGFmdGVyIGZpbHRlcmluZywgd2UgZXhwZWN0IHRvIGhhdmUgYWJvdXQgMjAsMDAwIGNlbGxzIGFzc2F5ZWQgd2l0aCBhcHByb3hpbWF0ZWx5IDIxLDAwMCBnZW5lcyBtZWFzdXJlZCBwZXIgY2VsbC4gVGhpcyBtZWFucyB0aGF0IHdlIGFyZSB3b3JraW5nIHZlcnkgImhpZ2gtZGltZW5zaW9uYWwiIGRhdGEuIAoKSGlnaCBkaW1lbnNpb25hbCBkYXRhIHByZXNlbnRzIHR3byBjaGFsbGVuZ2VzOgoKLSBUaGUgc2NhbGUgb2YgdGhlIGRhdGEgbWFrZXMgYW5hbHlzaXMgc3RlcHMgY29zdGx5IGFuZCBzbG93ICAgICAKLSBUaGVyZSBpcyBsaWtlbHkgdW5pbnRlcmVzdGluZyB2YXJpYXRpb24gYWNyb3NzIG91ciBkYXRhIHRoYXQgd2Ugd2FudCB0byBleGNsdWRlICAgIAoKClNpbWlsYXIgdG8gdGhlIHByZXZpb3VzIHNlY3Rpb25zLCB0aGUgcHJvY2VzcyBvZiBwYXJhbWV0ZXIgc2VsZWN0aW9uIGFmdGVyIFBDQSBpcyBvZnRlbiBpdGVyYXRpdmUgYW5kIHdoaWxlIHdlIHdpbGwgYmUgc2hvd2luZyBhIHNpbmdsZSBvcHRpb24sIG11bHRpcGxlIHBhcmFtZXRlciBjaG9pY2VzIG1heSBuZWVkIHRvIGJlIHRlc3RlZC4gIAoKCiMjIE9iamVjdGl2ZXMKCi0gVW5kZXJzdGFuZCB3aHkgUENBIGlzIHVzZWQgdG8gcmVkdWNlIGhpZ2gtZGltZW5zaW9uYWwgc2luZ2xlLWNlbGwgZGF0YSBwcmlvciB0byBpbnRlZ3JhdGlvbiBhbmQgY2x1c3RlcmluZyAgIAotIFVzZSB0aGUgYFJ1blBDQSgpYCBmdW5jdGlvbiB0byBnZW5lcmF0ZSBwcmluY2lwYWwgY29tcG9uZW50cyBmb3Igb3VyIGRhdGEgICAgCi0gQ2hvb3NlIGFuIGFwcHJvcHJpYXRlIG51bWJlciBvZiBwcmluY2lwYWwgY29tcG9uZW50cyB0byBjYXB0dXJlIHZhcmlhbmNlIGluIG91ciBkYXRhICAgCi0gVXNlIGBJbnRlZ3JhdGVMYXllcnNgIHRvIGludGVncmF0ZSBhY3Jvc3Mgb3VyIGRhdGEgcHJpb3IgdG8gY2x1c3RlcmluZyAgIAoKLS0tCgpgYGB7ciwgcmVhZF9yZHNfaGlkZGVuLCBlY2hvID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQppZighZXhpc3RzKCdnZW9fc28nKSkgewogIGxpYnJhcnkoU2V1cmF0KQogIGxpYnJhcnkoQlBDZWxscykKICBsaWJyYXJ5KHRpZHl2ZXJzZSkKCiAgb3B0aW9ucyhmdXR1cmUuZ2xvYmFscy5tYXhTaXplID0gMWU5KQoKICBnZW9fc28gPSByZWFkUkRTKCdyZXN1bHRzL3JkYXRhL2dlb19zb19zY3Rfbm9ybWFsaXplZC5yZHMnKQp9CmBgYAoKCjwhLS1CZWZvcmUgdGhpcyBzZWN0aW9uIC0gRGF5IDE6IFN0YXJ0aW5nIHcvIFNldXJhdCwgSW5pdGlhbCBRQywgJiBCYXRjaCBjb3JyZWN0aW9uIChTQ3RyYW5zZm9ybSktLT4KCjwhLS1JbnN0cnVjdGlvbiBOb3RlOiBVc2luZyBpbnRlZ3JhdGVkIGRhdGEgaGVyZSBhbmQgd2lsbCBjb21wYXJlIHRvIHVuaW50ZWdyYXRlZCByZXN1bHRzIGluIG5leHQgc2VjdGlvbiwgc2ltaWxhciB0byAgW2hlcmVdKGh0dHBzOi8vYm1jYmlvaW5mb3JtYXRpY3MuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9zMTI4NTktMDIxLTAzOTU3LTQpPy0tPgoKCgojIFdoYXQgaXMgUENBPwoKPCEtLS0gR2l2ZSBzb21lIGNvbnRleHQgYW5kIGJhY2tncm91bmQgb24gUENBCi0gV2hhdCBpcyBQQ0EKLSBXaHkgZG8gd2UgdXNlIGl0IAotLS0+Cgo8IS0tLSBhZGQgUENBIGlsbHVzdHJhdGlvbiBoZXJlPyAtLS0+CkluIHByYWN0aWNhbCB0ZXJtcywgUHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcyAoUENBKSBjcmVhdGVzIGEgc2ltcGxlciwgbW9yZSBjb25jaXNlIHZlcnNpb24gb2YgZGF0YSB3aGlsZSBwcmVzZXJ2aW5nIHRoZSBzYW1lICJzdG9yeSIuIEZvciBleGFtcGxlLCB3ZSBjYW4gdGhpbmsgb2YgYSBmdWxsIHNpbmdsZS1jZWxsIGRhdGEgc2V0IGFzIHNpbWlsYXIgdG8gdGhlIGZ1bGwgTG9yZCBvZiB0aGUgUmluZ3MgdHJpbG9neSAtIGxhcmdlIGFuZCBjb21wbGV4IHdpdGggcXVpdGUgYSBiaXQgb2YgZGV0YWlsIHRoYXQgbWlnaHQgYmUgcmVwZWF0ZWQgYW5kIGlzbid0IG5lY2Vzc2FyeSB0byB1bmRlcnN0YW5kIHRoZSBvdmVyYWxsIHBsb3QgYW5kIG1ha2UgcnVubmluZyB0aGUgYW5hbHlzaXMgcHJvaGliaXRpdmVseSBzbG93IGFuZCBjb3N0bHkuIAoKTGlrZSBhIENsaWZmIG5vdGVzIHZlcnNpb24gb3IgYSBtb3ZpZSBwaXRjaCBmb3IgdGhlIGJvb2sgc2VyaWVzLCB0aGUgUENzIGNhcHR1cmUgY29ycmVsYXRlZCBleHByZXNzaW9uIGFuZCByZWR1Y2UgdGhlIHNpemUgYW5kIGNvbXBsZXhpdHkgb2YgdGhlIGRhdGEsIHdoaWxlIHN0aWxsIGFsbG93aW5nIHRoZSBvdmVyYWxsIHN0cnVjdHVyZSBvZiB0aGUgZGF0YSAtIHRoYXQgd2Ugd291bGQgZXhwZWN0IHRvIGJlIHJlbGF0ZWQgdG8gY2VsbC10eXBlcyBvciBwbG90IHBvaW50cyBpbiB0aGUgYW5hbG9neSAtIHRvIGJlIHJlcHJlc2VudGVkLCB3aGlsZSBhbHNvIG1ha2luZyB0aGUgY29tcHV0YXRpb24gZmFzdGVyLgoKIVtdKC4vaW1hZ2VzL2N1cnJpY3VsdW0vMDQtUENBYW5kSW50ZWdyYXRpb24vUENBX3B1cnBvc2UucG5nKQoKIyMjIFBDQSBleGFtcGxlCgpUbyB1bmRlcnN0YW5kIGhvdyBQQ0Egd29ya3MgZm9yIHNpbmdsZS1jZWxsIGRhdGEsIHdlIGNhbiBjb25zaWRlciBhIHNtYWxsZXIgZGF0YXNldCBtZWFzdXJpbmcgdGhlIGV4cHJlc3Npb24gb2YgZm91ciBnZW5lcyBtZWFzdXJlZCBpbiBqdXN0IHR3byBjZWxscywgd2UgY291bGQgcGxvdCB0aGUgZXhwcmVzc2lvbiBvZiB0aG9zZSBmb3VyIGdlbmVzLCB3aXRoIGRhdGEgZnJvbSBvbmUgY2VsbCBwbG90dGVkIG9uIHRoZSB4LWF4aXMgYW5kIHRoZSBzZWNvbmQgY2VsbCBwbG90dGVkIG9uIHRoZSB5LWF4aXMuCgohW0Egc2ltcGxlIFBDQSBleGFtcGxlIChmcm9tIEhCQyldKC4vaW1hZ2VzL2N1cnJpY3VsdW0vMDQtUENBYW5kSW50ZWdyYXRpb24vSEJDLVBDQV8yc2FtcGxlX2dlbmVzLnBuZykKPCEtLS0gVXBkYXRlIHBsb3QgdG8gcmVwbGFjZSAiU2FtcGxlIiB3aXRoICJjZWxsIiBpbiBmaWd1cmUgYW5kIHRhYmxlIC0tLT4KCklmIHdlIHdhbnRlZCB0byByZXByZXNlbnQgdGhlIG1vc3QgdmFyaWF0aW9uIGFjcm9zcyB0aGUgZGF0YSwgd2Ugd291bGQgZHJhdyBhIGRpYWdvbmFsIGxpbmUgYmV0d2VlbiBnZW5lIEIgYW5kIGdlbmUgQyAtIHRoaXMgd291bGQgcmVwcmVzZW50IHRoZSBmaXJzdCBwcmluY2lwYWwgY29tcG9uZW50IChQQykuIEhvd2V2ZXIsIHRoaXMgbGluZSBkb2Vzbid0IGNhcHR1cmUgYWxsIHRoZSB2YXJpYW5jZSBpbiB0aGlzIGRhdGEgYXMgdGhlIGdlbmVzIGFsc28gdmFyeSBhYm92ZSBhbmQgYmVsb3cgdGhlIGxpbmUsIHNvIHdlIGNvdWxkIGRyYXcgYW5vdGhlciBsaW5lIChhdCBhIHJpZ2h0IGFuZ2xlIHRvIHRoZSBmaXJzdCkgcmVwcmVzZW50aW5nIHRoZSBzZWNvbmQgbW9zdCB2YXJpYXRpb24gaW4gdGhlIGRhdGEgLSB3aGljaCB3b3VsZCByZXByZXNlbnQgdGhlIHNlY29uZCBwcmluY2lwYWwgY29tcG9uZW50IChQQykuIAoKIVtQQ0EgZ2VuZSBsb2FkaW5ncyAoZnJvbSBIQkMpXSguL2ltYWdlcy9jdXJyaWN1bHVtLzA0LVBDQWFuZEludGVncmF0aW9uL0hCQy1QQ0FfMnNhbXBsZV92YXJpYXRpb24zLnBuZykKPCEtLS0gVXBkYXRlIHBsb3QgdG8gcmVwbGFjZSAiU2FtcGxlIiB3aXRoICJjZWxsIiBpbiBmaWd1cmUgLS0tPiAKCjwhLS0tIDxDb25zaWRlciBtb3ZpbmcgdG8gZHJvcGRvd24gYW5kIGFkZGluZyBmaWd1cmUgdG8gc2hvdyBsb2FkaW5ncz4gSG93ZXZlciwgYXMgd2UgY2FuIHNlZSBpbiB0aGUgZXhhbXBsZSBiZWxvdywgZ2VuZXMgbmVhciB0aGUgZW5kIG9mIGVhY2ggbGluZSBhcmUgdGhvc2Ugd2l0aCB0aGUgZ3JlYXRlc3QgaW5mbHVlbmNlIG9uIHRoZSBkaXJlY3Rpb24gYW5kIGxlbmd0aCBvZiB0aGUgUEMuLS0+CgoKV2UnbGwgc2tpcCB0aGUgcHJvY2VzcyBvZiBnZW5lcmF0aW5nIGFsbCBwb3NzaWJsZSBQQ3MsIGJ1dCBydW5uaW5nIFBDQSBvbiBvdXIgZGF0YSB3aWxsIHJlc3VsdCBpbiBhIHNjb3JlIGZvciBlYWNoIGNlbGwgZm9yIGVhY2ggUEMgYW5kIGVhY2ggZ2VuZSB3aWxsIGhhdmUgYSB3ZWlnaHQgb3IgbG9hZGluZyBmb3IgYSBnaXZlbiBQQy4gQnkgZGVmaW5pdGlvbiwgdGhlIFBDcyBjYXB0dXJlIHRoZSBncmVhdGVzdCBmYWN0b3JzIG9mIGhldGVyb2dlbmVpdHkgaW4gZGVjcmVhc2luZyBvcmRlciBpbiB0aGUgZGF0YSBzZXQgWyhzb3VyY2UpXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xMy9PU0NBLmJhc2ljL2RpbWVuc2lvbmFsaXR5LXJlZHVjdGlvbi5odG1sI3ByaW5jaXBhbC1jb21wb25lbnRzLWFuYWx5c2lzKS4KCiMjIFdoeSBQQ0EgaXMgdXNlZnVsIGZvciBzaW5nbGUtY2VsbCBkYXRhCgpJbiBjb250cmFzdCB0byBidWxrIFJOQS1zZXEsIHdoZXJlIHRoZSBtYWpvcml0eSBvZiB0aGUgdmFyaWFuY2UgaW4gYSBnaXZlbiBkYXRhc2V0IGFyZSB1c3VhbGx5IGV4cGxhaW5lZCBieSB0aGUgZmlyc3QgYW5kIHNlY29uZCBQQywgZm9yIHNpbmdsZS1jZWxsIGRhdGEgd2UgZXhwZWN0IHRoYXQgbWFueSBtb3JlIFBDcyBhcmUgY29udHJpYnV0aW5nIHRvIHRoZSBvdmVyYWxsIHZhcmlhbmNlLiAKCkhvd2V2ZXIsIHdlIGNhbiBhc3N1bWUgdGhhdCBiaW9sb2dpY2FsIHByb2Nlc3NlcyBhZmZlY3QgbXVsdGlwbGUgZ2VuZXMgaW4gYSBjb29yZGluYXRlZCB3YXkuIFRoZXJlZm9yZSB0aGUgdG9wIFBDcyBsaWtlbHkgcmVwcmVzZW50IGJpb2xvZ2ljYWwgc3RydWN0dXJlIHJhdGhlciB0aGFuIHJhbmRvbSB0ZWNobmljYWwgb3IgYmlvbG9naWNhbCBub2lzZSwgd2hpY2ggYWZmZWN0cyBlYWNoIGdlbmUgaW5kZXBlbmRlbnRseSBhbmQgYW5kIHdlIGNhbiB1c2UgdGhlIHRvcCBzZXZlcmFsIFBDcyB0byBhcHByb3hpbWF0ZSB0aGUgZnVsbCBkYXRhIHNldCBpbiBkb3duc3RyZWFtIGFuYWx5c2lzIChbc291cmNlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xMi9PU0NBL2RpbWVuc2lvbmFsaXR5LXJlZHVjdGlvbi5odG1sI3ByaW5jaXBhbC1jb21wb25lbnRzLWFuYWx5c2lzKSkuCgpUaGlzIHJlZHVjdGlvbiBpbiBkaW1lbnNpb25hbGl0eSwgZm9yIGV4YW1wbGUgZnJvbSAxMCwwMDAgY2VsbHMgeCAxOSwwMDAgZ2VuZXMgdG8gMjAgUENzLCBhbGxvd3MgdXMgdG8gc2VsZWN0IFBDcyB0aGF0IGFyZSBtb3JlIGxpa2VseSB0byBjb3JyZXNwb25kIHRvIGJpb2xvZ2ljYWwgdmFyaWF0aW9uIHJlbGF0ZWQgdG8gdGhlIGV4cGVjdGVkIGNlbGwgdHlwZXMvc3VidHlwZXMgYW5kIG1ha2UgdGhlIGRvd25zdHJlYW0gY2x1c3RlcmluZyBjb21wdXRhdGlvbmFsbHkgZmVhc2libGUuCgo+ICMjIyMgTW9yZSBjb250ZXh0IHVzaW5nIFBDQSBmb3Igc2luZ2xlLWNlbGwgZGF0YSB7LnVubGlzdGVkIC51bm51bWJlcmVkfQo+Cj4gVG8gcmVhZCBtb3JlIG9uIFBDQSwgcGxlYXNlIHJlZmVyIHRvIHRoZSBbSEJDIC0gVGhlb3J5IG9mIFBDQSBjb250ZW50XShodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9zY1JOQS1zZXFfb25saW5lL2xlc3NvbnMvMDVfdGhlb3J5X29mX1BDQS5odG1sKSwgZnJvbSB3aGljaCB0aGlzIHNlY3Rpb24gaXMgYWRhcHRlZCBhbmQgdGhlIG9yaWdpbmFsIHNvdXJjZSBtYXRlcmlhbCBmb3IgdGhhdCBjb250ZW50LCBzcGVjaWZpY2FsbHkgW0pvc2ggU3Rhcm1lcidzIFN0YXRRdWVzdCB2aWRlb10oaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1fVVZIbmVCVUJXMCkuCj4KPiBGb3IgYWRkaXRpb25hbCBkZXRhaWwgb24gLCB0aGUgT1NDQSBjaGFwdGVyIG9uIFtQcmluY2lwYWwgY29tcG9uZW50cyBhbmFseXNpc10oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL2Jvb2tzLzMuMTUvT1NDQS5iYXNpYy9kaW1lbnNpb25hbGl0eS1yZWR1Y3Rpb24uaHRtbCkgaW5jbHVkZXMgYSBtb3JlIGRlc2NyaXB0aXZlIG92ZXJ2aWV3LgoKIyMjIFNwYXJzaXR5IGluIHNpbmdsZS1jZWxsIGRhdGEKCkluIGFkZGl0aW9uIHRvIHRoZSAiaGlnaC1kaW1lbnNpb25hbGl0eSIgZXhwZWN0ZWQgZm9yIHNpbmdsZS1jZWxsIGRhdGEsIHdlIGV4cGVjdCB0aGUgZGF0YSB0byBiZSBib3RoICJzcGFyc2UiIGFuZCAibm9pc3kiLiBTcGFyc2UgaW4gdGhlIHNlbnNlIHRoYXQgbWFueSBnZW5lcyB3aWxsIGVpdGhlciBub3QgYmUgZXhwcmVzc2VkIG9yIG5vdCBtZWFzdXJlZCBpbiBtYW55IG9mIHRoZSBjZWxscyBhbmQgaGF2ZSB6ZXJvIHZhbHVlcy4gTm9pc3kgZHVlIHRvIGJpb2xvZ2ljYWwgdmFyaWFiaWxpdHkgYW5kIHRoZSBwcmFjdGljYWwgbGltaXRhdGlvbnMgb2YgYm90aCBjYXB0dXJlIGFuZCBzZXF1ZW5jaW5nIGRlcHRoIFsoc291cmNlKV0oaHR0cHM6Ly9vdXlhbmdsYWIuY29tL3NpbmdsZWNlbGwvYmFzaWMuaHRtbCNhLWdlbnRsZS1pbnRyb2R1Y3Rpb24tdG8tZHIpLgoKPiAqTm90ZSBvbiB6ZXJvICJpbmZsYXRpb24iIG9mIHNpbmdsZS1jZWxsIGRhdGEqCj4KPiBTaW5nbGUtY2VsbCBkYXRhIGlzIHNvbWV0aW1lcyBkZXNjcmliZWQgYXMgInplcm8gaW5mbGF0ZWQiLCBob3dldmVyIHdvcmsgYnkgW1N2ZW5zc29uXShodHRwczovL3d3dy5iaW9yeGl2Lm9yZy9jb250ZW50LzEwLjExMDEvNTgyMDY0djEuZnVsbCkgaGFzIGNoYWxsZW5nZWQgdGhhdCBjaGFyYWN0ZXJpemF0aW9uIGFuZCBhcmd1ZWQgdGhhdCB0aGUgaGlnaGVyIG51bWJlciBvZiB6ZXJvcyBvYnNlcnZlZCBpbiBzY1JOQS1zZXEgY29tcGFyZWQgdG8gYnVsayBSTkEtc2VxIGlzIG1vcmUgbGlrZWx5IGR1ZSB0byBiaW9sb2dpY2FsIHZhcmlhbmNlIGFuZCBsb3dlciBzZXF1ZW5jaW5nIHNhdHVyYXRpb24gdGhhbiB0ZWNobmljYWwgYXJ0aWZhY3RzLiBXb3JrIGJ5IFtDaG9pIGV0IGFsLiAoMjAyMCldKGh0dHBzOi8vZ2Vub21lYmlvbG9neS5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2L3MxMzA1OS0wMjAtMDIxMDMtMikgc3VwcG9ydCB0aGF0IHplcm9zIGluIHNpbmdsZS1jZWxsIGRhdGEgYXJlIGR1ZSB0byBiaW9sb2d5IGJ1dCBbSmlhbmcgZXQgYWwuICgyMDIyKV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DODc4MzQ3Mi8pIGRlbHZlcyBtb3JlIGludG8gdGhlICJjb250cm92ZXJzeSIgemVybyBpbmZsYXRpb24sIGluY2x1ZGluZyBjb21tb24gYXBwcm9hY2hlcyBmb3IgaGFuZGxpbmcgemVyb3MgYW5kIHRoZSBkb3duc3RyZWFtIGltcGFjdHMuCgpGb3Igc3BhcnNlLCBoaWdoLWRpbWVuc2lvbmFsLCBfYmlvbG9naWNhbF8gZGF0YSwgd2UgZXhwZWN0IG1hbnkgZ2VuZXMgdG8gaGF2ZSBjb3JyZWxhdGVkIGV4cHJlc3Npb24gYXMgdGhleSB3b3VsZCBiZSBpbXBhY3RlZCBieSB0aGUgc2FtZSBiaW9sb2dpY2FsIHByb2Nlc3MgYW5kIGZvciBvdGhlciBnZW5lcyB0byBoYXZlIGVpdGhlciBsb3cgKG1vcmUgbm9pc3kpIG9yIHNpbWlsYXIgZXhwcmVzc2lvbiBhY3Jvc3MgdGhlIGNlbGwgcG9wdWxhdGlvbiBhbmQgdGhlcmVmb3JlIG5vdCBsaWtlbHkgdG8gY29ycmVzcG9uZCB0byBjZWxsLXR5cGUvc3VidHlwZXMuIAoKU28gaG93IGRvIHdlIGRldGVybWluZSB3aGF0IGFuZCBob3cgbWFueSBnZW5lcyB0byB1c2UgYmVmb3JlIGNsYXNzaWZ5aW5nIGNlbGxzIGludG8gY2x1c3RlcnMgYmFzZWQgb24gdGhlaXIgZXhwcmVzc2lvbj8KCjxkZXRhaWxzPgogICAgPHN1bW1hcnk+Kk1vcmUgZGV0YWlsIG9uIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiAqPC9zdW1tYXJ5PgogICAgVGhlIFtPdXlhbmcgTGFiIGhhcyBhICJnZW50bGUgaW50cm9kdWN0aW9uIiBzZWN0aW9uIG9mIHRoZWlyIG1hdGVyaWFsc10oaHR0cHM6Ly9vdXlhbmdsYWIuY29tL3NpbmdsZWNlbGwvYmFzaWMuaHRtbCNhLWdlbnRsZS1pbnRyb2R1Y3Rpb24tdG8tZHIpIHRoYXQgZ29lcyBpbnRvIGdyZWF0ZXIgZGV0YWlscyBvbiBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gaW5jbHVkaW5nIGhvdyBzaW1pbGFyIHN0cmF0ZWdpZXMgYXJlIHVzZWQgaW4gZGVlcCBsZWFybmluZyBtb2RlbHMuIEFkZGl0aW9uYWx5LCB0aGUgT1NDQSBib29rIGhhcyBhIFtjaGFwdGVyIG9uIERpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbl0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL2Jvb2tzLzMuMTUvT1NDQS5iYXNpYy9kaW1lbnNpb25hbGl0eS1yZWR1Y3Rpb24uaHRtbCkgdGhhdCBoYXMgdXNlZnVsIGNvbnRleHQuIAo8L2RldGFpbHM+Cjxicj4KCjxicj4KCiMgUnVuIFBDQSBvbiBvdXIgZGF0YXNldCAgICAKCjwhLS0tIEFkZCBpbnRyb2R1Y3Rpb24gdG8gZnVuY3Rpb24sIGluY2x1ZGluZyBsaW5rIHRvIGRvY3VtZW50YXRpb24gLS0tPgoKU2luY2UgUENBIGlzIHNlbnNpdGl2ZSB0byBzY2FsZSwgd2Ugd2lsbCBydW4gaXQgb24gdGhlIFNDVCBub3JtYWxpemVkIGFzc2F5IChbcmVmZXJlbmNlXShodHRwczovL291eWFuZ2xhYi5jb20vc2luZ2xlY2VsbC9iYXNpYy5odG1sI3BjYS1wcmluY2lwYWwtY29tcG9uZW50LWFuYWx5c2lzKSkuIFdlIHdpbGwgbmFtZSB0aGUgcmVkdWN0aW9uIGluIGFuIGluZm9ybWF0aXZlIHdheSB0byBrZWVwIHRoZW0gY2xlYXIgZm9yIHVzIGluIHRoZSBmdXR1cmUuIEluIHRoaXMgY2FzZSwgb3VyIGRhdGEgaGFzIG5vdCB5ZXQgYmVlbiBpbnRlZ3JhdGVkLCBidXQgaXQgaGFzIGJlZW4gU0NUIG5vcm1hbGl6ZWQuIFNvIHdlIHdpbGwgbmFtZSB0aGUgcmVkdWN0aW9uIGB1bmludGVncmF0ZWQuc2N0LnBjYWAuIE5vdGUgdGhhdCBgU0NUVHJhbnNmb3JtKClgIHJldHVybmVkIGEgc2V0IG9mIGhpZ2hseSB2YXJpYWJsZSBnZW5lcywgYW5kIHRoZSBgUnVuUENBKClgIGZ1bmN0aW9uIHdpbGwgdXNlIHRoaXMgc3Vic2V0IHRvIGRldGVybWluZSB0aGUgUENzIGFuZCBnZW5lcyBhc3NvY2lhdGVkIHdpdGggdGhvc2UgUENzLgoKCmBgYHtyLCBydW5fcGNhLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyMjIyMgRGF5IDIgLSBQQ0EgYW5kIEludGVncmF0aW9uCgojIEJ1aWxkIGEgUENBIGFuZCBhZGQgaXQgdG8gdGhlIFNldXJhdCBvYmplY3QgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KZ2VvX3NvID0gUnVuUENBKGdlb19zbywgcmVkdWN0aW9uLm5hbWUgPSAndW5pbnRlZ3JhdGVkLnNjdC5wY2EnKQpnZW9fc28KYGBgCgpBZnRlciBydW5uaW5nIHRoZSBjb21tYW5kIGluIHRoZSBjb25zb2xlLCB3ZSBzaG91bGQgc2VlIHRoYXQgdGhlIGBnZW9fc29gIFNldXJhdCBvYmplY3QgaGFzIGEgbmV3IHJlZHVjdGlvbi4gVmlld2VkIGluIG91ciBydW5uaW5nIHNjaGVtYXRpYzoKCiFbSW1hZ2U6IFNjaGVtYXRpYyBhZnRlciBSdW5QQ0EoKS5dKGltYWdlcy9zZXVyYXRfc2NoZW1hdGljL1NsaWRlNi5wbmcpCgpXZSBjYW4gdGhlbiB2aXN1YWxpemUgdGhlIGZpcnN0IHNldmVyYWwgUENzIHVzaW5nIHRoZSBgRGltSGVhdG1wKClgIGZ1bmN0aW9uLCB3aGljaCBvcmRlcnMgYm90aCBjZWxscyBhbmQgZmVhdHVyZXMgKGdlbmVzKSBhY2NvcmRpbmcgdG8gdGhlaXIgUENBIHNjb3JlcyBhbmQgYWxsb3dzIHVzIHRvIHNlZSBzb21lIGdlbmVyYWwgcGF0dGVybnMgaW4gdGhlIGRhdGEuIEhlcmUgd2Ugd2lsbCBzcGVjaWZ5IHRoYXQgdGhlIGZpcnN0IDI0IFBDcyBiZSBpbmNsdWRlZCBpbiB0aGUgZmlndXJlIGFuZCB0aGF0IDUwMCBjZWxscyBhcmUgcmFuZG9tbHkgc3Vic2V0dGVkIGZvciB0aGUgcGxvdC4gX05vdGUgLSB5b3UgbWF5IG5lZWQgdG8gaW5jcmVhc2UgeW91ciBwbG90IHdpbmRvdyBzaXplIHRvIHJlc29sdmUgZXJyb3JzIHJlZ2FyZGluZyBmaWd1cmUgbWFyZ2lucy5fICAKCmBgYHtyLCBwY2FfaGVhdG1hcF9wbG90LCBldmFsPUZBTFNFfQojIEJ1aWxkIGhlYXRtYXBzIG9mIFBDQXMgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBQbG90IGNlbGwgYnkgZ2VuZSBoZWF0bWFwcyBmb3IgZmlyc3Qgc2V2ZXJhbCBQQ3MKRGltSGVhdG1hcChnZW9fc28sIGRpbXM9MSwgY2VsbHM9NTAwLCBiYWxhbmNlZD1UUlVFLCByZWR1Y3Rpb24gPSAndW5pbnRlZ3JhdGVkLnNjdC5wY2EnKSAjIGxvb2sgYXQgZmlyc3QgUEMgYWxvbmUgZmlyc3QKRGltSGVhdG1hcChnZW9fc28sIGRpbXM9MToxOCwgY2VsbHM9NTAwLCBiYWxhbmNlZD1UUlVFLCByZWR1Y3Rpb24gPSAndW5pbnRlZ3JhdGVkLnNjdC5wY2EnKSAjIGZpcnN0IDE4IFBDcwoKIyBub3RlIC0gbmVlZCB0byB1c2UgcG5nKCkgdG8gd3JpdGUgdG8gZmlsZSBiZWNhdXNlIHRoaXMgaXNuJ3QgYSBnZ3Bsb3QKcG5nKGZpbGVuYW1lID0gJ3Jlc3VsdHMvZmlndXJlcy9xY19wY2FfaGVhdG1hcC5wbmcnLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA0MCwgdW5pdHMgPSAnaW4nLCByZXMgPSAzMDApCiAgRGltSGVhdG1hcChnZW9fc28sIGRpbXM9MToxOCwgY2VsbHM9NTAwLCBiYWxhbmNlZD1UUlVFLCByZWR1Y3Rpb24gPSAndW5pbnRlZ3JhdGVkLnNjdC5wY2EnKQpkZXYub2ZmKCkKYGBgCgpgYGB7ciwgcGNhX2hlYXRtYXBfb25lLCBlY2hvPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIG91dC53aWR0aD0nNTAlJywgZmlnLmNhcD0nRGltSGVhdG1hcCBmb3IgUEMxIGFsb25lJ30KIyBsb29rIGF0IGZpcnN0IFBDIGFsb25lIGZpcnN0CkRpbUhlYXRtYXAoZ2VvX3NvLCBkaW1zPTEsIGNlbGxzPTUwMCwgYmFsYW5jZWQ9VFJVRSwgcmVkdWN0aW9uID0gJ3VuaW50ZWdyYXRlZC5zY3QucGNhJykgCgpgYGAKCgpgYGB7ciwgcGNhX2hlYXRtYXBfMTgsIGVjaG89RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmNhcD0nRGltSGVhdG1hcCBmb3IgUEMxLTE4J30KIyBQbG90IGNlbGwgYnkgZ2VuZSBoZWF0bWFwcyBmb3IgZmlyc3Qgc2V2ZXJhbCBQQ3MgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgZmlyc3QgMTggUENzCkRpbUhlYXRtYXAoZ2VvX3NvLCBkaW1zPTE6MTgsIGNlbGxzPTUwMCwgYmFsYW5jZWQ9VFJVRSwgcmVkdWN0aW9uID0gJ3VuaW50ZWdyYXRlZC5zY3QucGNhJykgCmBgYAoKQXMgd2Ugc2Nyb2xsIGRvd24gaW4gdGhpcyBwbG90LCB3ZSBzZWUgbGVzcyBhbmQgbGVzcyBzdHJ1Y3R1cmUgaW4gb3VyIHBsb3RzIGluZGljYXRpbmcgdGhhdCBubyBvbmx5IGlzIGxlc3MgdmFyaWF0aW9uIGlzIGJlaW5nIHJlcHJlc2VudGVkIGJ5IGxhcmdlciBQQ3Mgb3ZlcmFsbCBidXQgYWxzbyB0aGF0IHRob3NlIFBDcyBhcmUgbGVzcyBsaWtlbHkgdG8gY29ycmVzcG9uZCB0byB2YXJpYXRpb24gb2YgaW50ZXJlc3QsIGUuZy4gY29ycmVzcG9uZGluZyB0byBjZWxsIHR5cGVzL3N1YnR5cGVzLgoKCjxkZXRhaWxzPgogIDxzdW1tYXJ5PipXaGF0IGdlbmVzIGFyZSBjb250cmlidXRpbmcgdGhlIG1vc3QgdG8gZWFjaCBQQz8qPC9zdW1tYXJ5PgogIFdlIGNhbiBhbHNvIHZpc3VhbGl6ZSB0aGUgbG9hZGluZ3MgZm9yIGdlbmVzIGNvbnRyaWJ1dGluZyB0byBlYWNoIHByaW5jaXBhbCBjb21wb25lbnRzIHVzaW5nIFNldXJhdCBwcm92aWRlZCBmdW5jdGlvbnMgW3NvdXJjZV0oaHR0cHM6Ly9ob2xhYi1oa3UuZ2l0aHViLmlvL0Z1bmRhbWVudGFsLXNjUk5BL2Rvd25zdHJlYW0uaHRtbCNwZXJmb3JtLWxpbmVhci1kaW1lbnNpb25hbC1yZWR1Y3Rpb24pLgogIAogIFdlIGNhbiBoaWdobGlnaHQgZ2VuZXMgbG9hZGVkIGZvciBkaW1lbnN0aW9ucyBvZiBpbnRlcmVzdCB1c2luZyB1c2luZyBgVml6RGltTG9hZGluZ3MoKWA6CgpgYGB7ciwgcGNhX2xvYWRpbmdfcGxvdCwgZXZhbCA9IFRSVUUsIGZpZy5zaG93PSdob2xkJ30KIyBWaXN1YWxpemUgZ2VuZSBsb2FkaW5ncyBmb3IgUEMxIGFuZCBQQzIgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClZpekRpbUxvYWRpbmdzKGdlb19zbywgZGltcyA9IDE6MiwgcmVkdWN0aW9uID0gJ3VuaW50ZWdyYXRlZC5zY3QucGNhJykKZ2dzYXZlKGZpbGVuYW1lID0gJ3Jlc3VsdHMvZmlndXJlcy9xY19wY2FfbG9hZGluZ3MucG5nJywgd2lkdGggPSAxMiwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAnaW4nKQoKYGBgCjwvZGV0YWlscz4KPGJyPgo8L2JyPgoKPiAjIyMjIEhvdyBkb2VzIFNldXJhdCB1c2UgUENBIHNjb3Jlcz8gey51bmxpc3RlZCAudW5udW1iZXJlZH0KPiAKPiBQZXIgdGhlIFtIbyBMYWIncyBtYXRlcmlhbHNdKGh0dHBzOi8vaG9sYWItaGt1LmdpdGh1Yi5pby9GdW5kYW1lbnRhbC1zY1JOQS9kb3duc3RyZWFtLmh0bWwjcGVyZm9ybS1saW5lYXItZGltZW5zaW9uYWwtcmVkdWN0aW9uKSAtICJUbyBvdmVyY29tZSB0aGUgZXh0ZW5zaXZlIHRlY2huaWNhbCBub2lzZSBpbiBhbnkgc2luZ2xlIGZlYXR1cmUgZm9yIHNjUk5BLXNlcSBkYXRhLCBTZXVyYXQgY2x1c3RlcnMgY2VsbHMgYmFzZWQgb24gdGhlaXIgUENBIHNjb3Jlcywgd2l0aCBlYWNoIFBDIGVzc2VudGlhbGx5IHJlcHJlc2VudGluZyBhIOKAmG1ldGFmZWF0dXJl4oCZIHRoYXQgY29tYmluZXMgaW5mb3JtYXRpb24gYWNyb3NzIGEgY29ycmVsYXRlZCBmZWF0dXJlIHNldC4gVGhlIHRvcCBwcmluY2lwYWwgY29tcG9uZW50cyB0aGVyZWZvcmUgcmVwcmVzZW50IGEgcm9idXN0IGNvbXByZXNzaW9uIG9mIHRoZSBkYXRhc2V0LiIgCjwhLS0gTm90ZSwgbWF5IHdhbnQgdG8gZWRpdC9yZW1vdmUgYWJvdmUgc2VjdGlvbiAtLT4KCgoKIyMjIFZpc3VhbGl6aW5nIHJlbGF0aXZlIGNvbnRyaWJ1dGlvbnMgb2YgZWFjaCBQQwoKCk9uZSB3YXkgdG8gZXZhbHVhdGUgdGhlIFBDcyB0byBpbmNsdWRlIGlzIGJ5IGxvb2tpbmcgYXQgYW4gZWxib3cgcGxvdCwgd2hpY2ggc2hvd3MgdGhlIHBlcmNlbnQgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IHN1Y2Nlc3NpdmUgUENzLiBXZSdsbCB1c2UgdGhlIGBFbGJvd1Bsb3QoKWAgZnVuY3Rpb24gdG8gZG8gdGhpcywgc3BlY2lmeWluZyB0aGF0IHRoZSBmaXJzdCA1MCBQQ3MgYmUgcGxvdHRlZC4gCgpgYGB7ciwgZWxib3dfcGxvdCwgZmlnLnNob3c9J2hvbGQnLCBvdXQud2lkdGg9JzgwJSd9CiMgVmlzdWFsaXplIGhvdyBtYW55IFBDcyB0byBpbmNsdWRlIHVzaW5nIGFuIGVsYm93IHBsb3QgLS0tLS0tLS0tLS0tLS0tLS0tLQpFbGJvd1Bsb3QoZ2VvX3NvLCBuZGltcyA9IDUwLCByZWR1Y3Rpb24gPSAndW5pbnRlZ3JhdGVkLnNjdC5wY2EnKQpnZ3NhdmUoZmlsZW5hbWUgPSAncmVzdWx0cy9maWd1cmVzL3FjX3NjdF9lbGJvd19wbG90LnBuZycsIHdpZHRoID0gOCwgaGVpZ2h0ID0gOCwgdW5pdHMgPSAnaW4nKQpgYGAKCkluIHRoaXMgcGxvdCwgd2UgY291bGQgYXJiaXRyYXJpbHkgY2hvb3NlIGEgbnVtYmVyIGFsb25nIHRoZSB4LWF4aXMgdGhhdCBsb29rcyBsaWtlIGEgc2hhcnAgY2hhbmdlIGluIHRoZSB2YXJpYW5jZSBmcm9tIG9uZSBQQyB0byB0aGUgbmV4dCwgdGhhdCBpcywgYW4gZWxib3cuIE9mIGNvdXJzZSwgd2hpbGUgdGhhdCdzIG9mdGVuIHRoZSByZWNvbW1lbmRhdGlvbiBpbiB0dXRvcmlhbHMsIHRoZSBjaG9pY2Ugb2Ygd2hlcmUgdGhlIHBvaW50IG9mIHRoZSAiZWxib3ciIGlzLCBpcyBub3QgYWx3YXlzIG9idmlvdXMsIGFuZCB0aGlzIHBsb3QgaXMgbm8gZGlmZmVyZW50LiBXZSBjb3VsZCBhbHNvIHRyeSB0byBxdWFudGlmeSBvdXIgY2hvaWNlLgoKIyMgQ2hvb3NpbmcgdGhlIG51bWJlciBvZiBzaWduaWZpY2FudCBQQ3MgZm9yIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbgoKPCEtLSBTZWN0aW9uIG1heSBzdGlsbCBuZWVkIHRvIGJlIGVkaXRlZCBtb3JlICYgY29uc2lkZXIgYWRkaW5nIGEgZmlndXJlIC0tPiAKQXMgd2UgZGlzY3Vzc2VkIGFib3ZlLCB3ZSBkb24ndCB3YW50IHRvIHVzZSBhbGwgdGhlIFBDcyBnZW5lcmF0ZWQgZm9yIGEgc2luZ2xlIGNlbGwgZGF0YSBzZXQgYnV0IGluc3RlYWQgd2FudCB0byBzZWxlY3QgYSBzdWJzZXQgdG8gbGltaXQgdGhlIGRvd25zdHJlYW0gc3RlcHMgdG8gdmFyaWF0aW9uIHRoYXQncyBtb3JlIGxpa2VseSB0byBjb3JyZXNwb25kIHRvIGNlbGwgdHlwZXMgb3Igc3VidHlwZXMuIFNvIGhvdyBtYW55IFBDcyBzaG91bGQgYmUgdXNlZD8KCkEgZ29vZCBzdGFydGluZyBwb2ludCBmb3IgZGV0ZXJtaW5pbmcgaG93IG1hbnkgUENzIHRvIHNlbGVjdCBmb3IgeW91ciBzaW5nbGUtY2VsbCBhbmFseXNpcyBpcyB0byB1bmRlcnN0YW5kIHRoZSAicmVzb2x1dGlvbiIgb2YgeW91ciBiaW9sb2dpY2FsIHF1ZXN0aW9uLiBJcyBhbnN3ZXJpbmcgeW91ciBiaW9sb2dpY2FsIHF1ZXN0aW9uIGRlcGVuZGVudCBvbiBpZGVudGlmeWluZyByYXJlciBjZWxsIHR5cGVzIG9yIHNwZWNpZmljIHN1YnR5cGVzPyBPciBhcmUgYnJvYWRlciBjZWxsLXR5cGVzIG1vcmUgcmVsZXZhbnQ/CgpGb3IgdGhpcyBkYXRhc2V0LCB3ZSBhcmUgZXhwZWN0aW5nIGEgZGl2ZXJzaXR5IG9mIGNlbGwgdHlwZXMgYW5kIGNlbGwgcG9wdWxhdGlvbnMgdGhhdCBtZWRpYXRlIHdvdW5kIGhlYWxpbmcsIGJ1dCBhcmUgYWxzbyB0aGF0IGFyZSBwYXJ0IG9mIHRoZSBhYmVycmFudCB0cmFuc2l0aW9uIHRvIGJvbmUsIHdoaWNoIG1pZ2h0IGJlIG1vcmUgcmFyZSBpbiB0aGUgcG9wdWxhdGlvbi4gU28gZm9yIHRoaXMgZGF0YXNldCwgd2UgbWlnaHQgd2FudCB0byBjb25zaWRlciBzdGFydGluZyB3aXRoIG1vcmUgUENTIHJhdGhlciB0aGFuIHRvbyBmZXcgUENzIHRvIHN0YXJ0LiBBZ2FpbiwgaW4gYSBmdWxsIGFuYWx5c2lzIHdvcmtmbG93LCBvdXIgc2VsZWN0aW9uIGF0IHRoaXMgc3RlcCBtaWdodCBiZSBtb3JlIG9mIGEgc3RhcnRpbmcgcG9pbnQgZm9yIGZ1cnRoZXIgaXRlcmF0aW9ucyB0aGFuIGEgZmluYWwgZGVjaXNpb24uIAoKPCEtLSBTZWN0aW9uIG1heSBuZWVkIG1vcmUgZWRpdGluZwpSZWxhdGVkIC0gaG93IGltcG9ydGFudCBpcyB0aGF0IGRlY2lzaW9uIHRvIHRoZSBkb3duc3RyZWFtIGltcGFjdCAoZS5nLiBob3cgbXVjaCBkb2VzIGNoYW5naW5nIHRoZSBudW1iZXIgb2YgUENzIGNoYW5nZSB0aGUgY2x1c3RlcmluZyk/Ci0tPiAKCgojIyMgVXNpbmcgYSAoY3J1ZGUpIG9wdGltaXphdGlvbiB0byBzZWxlY3QgYSBzdGFydGluZyBwb2ludAoKSW5zdGVhZCBvZiBjaG9vc2luZyBiYXNlZCBvbiB0aGUgZWxib3cgcGxvdCBieSBzaWdodCBhbG9uZSwgd2UgY2FuIHRyeSB0byBxdWFudGlmeSBvdXIgY2hvaWNlLiBIZXJlIHdlIGNyZWF0ZSBhIGZ1bmN0aW9uIHRvIHJldHVybiBhIG1pbmltdW0gUENzIGJhc2VkIG9uIHR3byBwb3NzaWJsZSBtZXRyaWNzIChjdW11bGF0aXZlIHZhcmlhdGlvbiBvciBhYm92ZSBhIG1pbml1bSBzdGVwIHNpemUpIGFuZCB0aGVuIHdlJ2xsIGFwcGx5IHRoYXQgZnVuY3Rpb24gdG8gb3VyIGRhdGE6CgpgYGB7ciwgb3B0aW1hbF9wY3N9CiMgRXN0aW1hdGUgb3B0aW1hbCBQQ3MgZm9yIGNsdXN0ZXJpbmcgd2l0aCBhIGZ1bmN0aW9uIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0Kb3B0aW1hbF9wY3MgPSBmdW5jdGlvbihzbywgcmVkdWN0aW9uKSB7CiAgICAjIHF1YW50aXRhdGl2ZSBjaGVjayBmb3IgbnVtYmVyIG9mIFBDcyB0byBpbmNsdWRlCiAgICBwY3QgPSBzb0ByZWR1Y3Rpb25zW1tyZWR1Y3Rpb25dXUBzdGRldiAvIHN1bShzb0ByZWR1Y3Rpb25zW1tyZWR1Y3Rpb25dXUBzdGRldikgKiAxMDAKICAgIGN1bSA9IGN1bXN1bShwY3QpCiAgICBjbzEgPSB3aGljaChjdW0gPiA5MCAmIHBjdCA8IDUpWzFdCiAgICBjbzIgPSBzb3J0KHdoaWNoKChwY3RbMTpsZW5ndGgocGN0KS0xXSAtIHBjdFsyOmxlbmd0aChwY3QpXSkgPiAuMSksIGRlY3JlYXNpbmcgPSBUKVsxXSArIDEKICAgIHBjcyA9IG1pbihjbzEsIGNvMikgCiAgICAKICAgIHJldHVybihwY3MpCn0KCiMgQXBwbHkgZnVuY3Rpb24gdG8gb3VyIGRhdGEKcGNzID0gb3B0aW1hbF9wY3MoZ2VvX3NvLCAndW5pbnRlZ3JhdGVkLnNjdC5wY2EnKQpwY3MKCiMgQmFzZWQgb24gdGhlIGhlYXRtYXAsIHdlJ2xsIG1vZGlmeSB0byAxMyBQQ3MKcGNzID0gMTMKYGBgCgoKQWdhaW4sIHRoaXMgbnVtYmVyIGlzIGxpa2VseSBhIHN0YXJ0aW5nIHBvaW50IGFuZCBtYXkgbmVlZCB0byBiZSByZXZpc2VkIGRlcGVuZGluZyBvbiB0aGUgb3V0Y29tZSBvZiB0aGUgZG93bnN0cmVhbSBzdGVwcy4gCgo+ICMjIyMgT3RoZXIgbW9yZSBxdWFuaXRhdGl2ZSBwYXJhbWV0ZXIgc2VsZWN0aW9uIG9wdGlvbnMgey51bmxpc3RlZCAudW5udW1iZXJlZH0KPiAKPiBXaGlsZSBvdXRzaWRlIHRoZSBzY29wZSBvZiB0aGlzIHdvcmtzaG9wLCB0aGVyZSBhcmUgY29tbXVuaXR5IGVmZm9ydHMgdG8gZGV2ZWxvcCBtb3JlIHNvcGhpc3RpY2F0ZWQgbWV0aG9kcyB0byBzZWxlY3QgYW4gYXBwcm9wcmlhdGUgbnVtYmVyIG9mIFBDcywgbGlrZSB0aGUgW2Nob29zZVIgcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL3JicGF0dDIwMTkvY2hvb3NlUikgW2ZpbmRQQyBwYWNrYWdlXShodHRwczovL2FjYWRlbWljLm91cC5jb20vYmlvaW5mb3JtYXRpY3MvYXJ0aWNsZS8zOC8xMC8yOTQ5LzY1NjUzMTQpIG9yIFt1c2luZyBjbHVzdGVyaW5nIHRyZWVzXShodHRwczovL2xhemFwcGkuaWQuYXUvcG9zdHMvMjAxNy0wNy0xOS1idWlsZGluZy1hLWNsdXN0ZXJpbmctdHJlZS8pIHRvIGV2YWx1YXRlIHRoZSBzdGFiaWxpdHkgb2YgcGFyYW1ldGVyIGNob2ljZXMuCgoKIyMjIFZpc3VhbGl6aW5nIG91ciBQQyByZXN1bHRzCgpJbiBhZGRpdGlvbiB0byBzZWxlY3RpbmcgYSByZWR1Y2VkIG51bWJlciBvZiBQQ3MgdG8gcmVwcmVzZW50IG91ciBkYXRhLCB3ZSBjYW4gYWxzbyB2aXN1YWxpemUgLCBzaW1pbGFybHkgdG8gaG93IHdlIG9mdGVuIGxvb2sgYXQgc2FtcGxlcyBmb3IgYnVsayBSTkEtc2VxIHVzaW5nIHRoZSBgRGltUGxvdCgpYCBmdW5jdGlvbiwgd2l0aCBlYWNoIGNlbGwgcGxvdHRlZCBhbG9uZyB0aGUgc2VsZWN0ZWQgUENzIGFuZCBjb2xvcmVkIGJ5IGEgc2VsZWN0ZWQgbWV0YS1kYXRhIGNvbHVtbjoKCn5+fgo/RGltUGxvdCAjIGRlZmF1bHQgZGltcyA9IGMoMSwyKQp+fn4KCmBgYHtyLCBwY2FfbG9hZGluZ19wbG90cywgbWVzc2FnZT1GQUxTRSwgZmlnLnNob3c9J2hvbGQnfQojIFZpc3VhbGl6ZSBQQ0EgKFBDMSBhbmQgUEMyLCB1bmludGVncmF0ZWQpIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KRGltUGxvdChnZW9fc28sIHJlZHVjdGlvbiA9ICd1bmludGVncmF0ZWQuc2N0LnBjYScsIGdyb3VwLmJ5ID0gJ29yaWcuaWRlbnQnKSAjIGZpcnN0IGxhYmVsIGJ5IHNhbXBsZQpEaW1QbG90KGdlb19zbywgcmVkdWN0aW9uID0gJ3VuaW50ZWdyYXRlZC5zY3QucGNhJywgZ3JvdXAuYnkgPSAnZGF5JykgIyB0aGVuIGxhYmVsIGJ5IGRheQpnZ3NhdmUoZmlsZW5hbWUgPSAncmVzdWx0cy9maWd1cmVzL3FjX3BjYV9wbG90X3VuaW50ZWdyYXRlZF9zY3RfZGF5LnBuZycsIHdpZHRoID0gNywgaGVpZ2h0ID0gNiwgdW5pdHMgPSAnaW4nKSAjIHNhdmUgCmBgYAoKSW4gdGhlIGZpcnN0IHBsb3Qgd2l0aCBlYWNoIGNlbGwgbGFiZWxlZCBieSB0aGUgb3JpZ2luYWwgc2FtcGxlIG5hbWUsIGl0IGxvb2tzIGxpa2UgdGhlcmUgbWlnaHQgYmUgc29tZSBiYXRjaGVzIGJ1dCBpdCdzIGhhcmQgdG8gZGlzdGluZ3Vpc2guIEZyb20gdGhlIHNlY29uZCBieSBgZGF5YCwgd2UgY2FuIHNlZSB0aGF0IGV2ZW4gYWZ0ZXIgbm9ybWFsaXphdGlvbiB0aGVyZSBzZWVtcyB0byBiZSB2YXJpYXRpb24gdGhhdCBjb3JyZXNwb25kcyBtb3JlIHRvIGEgYmF0Y2ggZWZmZWN0IHJlbGF0ZWQgdG8gdGhlIGRheSBzdGF0dXMgdGhhbiBpbnRlcmVzdGluZyBiaW9sb2dpY2FsIHZhcmlhdGlvbi4gVGhpcyBzdWdnZXN0cyB0aGF0IGNsdXN0ZXJpbmcgb3VyIGRhdGEgdXNpbmcgb3VyIHNlbGVjdGVkIG51bWJlciBvZiBQQ3MsIGludGVncmF0aW9uIHNob3VsZCBiZSBwZXJmb3JtZWQgdG8gbWl0aWdhdGUgdGhlc2UgZGlmZmVyZW5jZXMgYW5kIGJldHRlciBhbGlnbiBvdXIgZGF0YS4KCgojIEludGVncmF0ZSBMYXllcnMKCgpCYXNlZCBvbiBvdXIgUENBIHBsb3QgYW5kIG9mdGVuIGEgc3RhbmRhcmQgcGFydCBvZiBtdWx0aXBsZSBzYW1wbGUgc2NSTkEtc2VxIGFuYWx5c2lzLCB3ZSB3aWxsIGludGVncmF0ZSBvdXIgZGF0YSBhY3Jvc3MgYWxsIHNhbXBsZXMuIEN1cnJlbnRseSwgZWFjaCBzYW1wbGUgaXMgc3RvcmVkIGFzIGEgbGF5ZXIgaW4gb3VyIGBnZW9fc29gIG9iamVjdCwgU2luY2UgZWFybGllciBpbiB0aGUgd29ya3Nob3AsIHdlIHVzZWQgYFNDVHJhbnNmb3JtKClgIHRvIG5vcm1hbGl6ZSBvdXIgZGF0YSwgc2VsZWN0IHZhcmlhYmxlIGdlbmVzIGFuZCB0aGVuIGhhdmUgZ2VuZXJhdGVkIGEgUENBIHJlZHVjdGlvbiwgd2UgY2FuIHVzZSB0aGUgYEludGVncmF0ZUxheWVycygpYCBmdW5jdGlvbiB0byBkbyB0aGF0LgoKIVtDQ0EgaW50ZWdyYXRpb24gaWxsdXN0cmF0aW9uLCBtb2RpZmllZCBmcm9tIFN0dWFydCBhbmQgQnV0bGVyLCAyMDE4IC1odHRwczovL2RvaS5vcmcvMTAuMTEwMS80NjAxNDddKC4vaW1hZ2VzL2N1cnJpY3VsdW0vMDQtUENBYW5kSW50ZWdyYXRpb24vSEJDLUNDQS1JbnRlZ3JhdGlvbl9zaW1wbGlmaWVkLnBuZykKClRoZSBkZXRhaWxzIG9mIGhvdyBpbnRlZ3JhdGlvbiB3b3JrcyBkZXBlbmRzIG9uIHRoZSBtZXRob2QsIGJ1dCBmb3IgdGhlIFJQQ0EgYXBwcm9hY2ggdGhhdCB3ZSdsbCBiZSB1c2luZyBoZXJlIC0gdGhlIHByb2Nlc3MgaXMgc2ltaWxhciB0byB0aGUgY2Fub25pY2FsIGNvcnJlbGF0aW9uIGFuYWx5c2lzIChDQ0EpIGFwcHJvYWNoIGlsbHVzdHJhdGlvbiBhYm92ZSBbKHNvdXJjZSldKGh0dHBzOi8vd3d3LmJpb3J4aXYub3JnL2NvbnRlbnQvMTAuMTEwMS8yMDIxLjA4LjA0LjQ1MzU3OXYxLmZ1bGwucGRmKS4gSG93ZXZlciwgUlBDQSBpcyBtb3JlIGVmZmljaWVudCAoZmFzdGVyKSB0byBydW4gYW5kIGJldHRlciBwcmVzZXJ2ZXMgZGlzdGluY3QgY2VsbCBpZGVudGl0aWVzIGJldHdlZW4gc2FtcGxlcyBbKHNvdXJjZSldKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxNTkyLTAyMS0wMTMzNi04KS4gQXMgZGVzY3JpYmVkIGluIHRoZSBjb3JyZXNwb25kaW5nIFtTZXVyYXQgdHV0b3JpYWxdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvYXJ0aWNsZXMvaW50ZWdyYXRpb25fcnBjYS5odG1sKSwgZWFjaCBkYXRhc2V0IGlzIHByb2plY3RlZCBpbnRvIHRoZSBvdGhlcnMnIFBDQSBzcGFjZSBhbmQgY29uc3RyYWluIHRoZSBhbmNob3JzIAoKTmV3IHRvIFNldXJhdCB2NSBhcmUgaW1wcm92ZW1lbnRzIHRoYXQgbWFrZSBzZWxlY3RpbmcgZGlmZmVyZW50IGludGVncmF0aW9uIG1ldGhvZHMgbXVjaCBlYXNpZXIuIFRoZSByZXN1bHRzIG9mIGVhY2ggYWx0ZXJuYXRpdmUgaW50ZWdyYXRpb24gbWV0aG9kIGFyZSBzdG9yZWQgd2l0aGluIHRoZSBzYW1lIGBTZXVyYXRgIG9iamVjdCwgd2hpY2ggbWFrZXMgY29tcGFyaW5nIGRvd25zdHJlYW0gZWZmZWN0cyBtdWNoIGVhc2llci4gIFNvLCBpZiB3ZSB3YW50ZWQgdG8gcnVuIHRoZSBgUlBDQUludGVncmF0aW9uYCBtZXRob2QsIHdlIHdvdWxkIHJ1biAoKipidXQgd2Ugd29uJ3QgaGVyZSoqKToKCmBgYHtyLCBpbnRlZ3JhdGVfbGF5ZXJzLCBjYWNoZSA9IFRSVUUsIGNhY2hlLmxhenkgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGVjaG89RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiMjIyBIaWRkZW4gY29kZSBibG9jayB0byBnZW5lcmF0ZSBpbnRlZ3JhdGVkIGRhdGEKZ2VvX3NvID0gSW50ZWdyYXRlTGF5ZXJzKAogIG9iamVjdCA9IGdlb19zbywgCiAgbWV0aG9kID0gUlBDQUludGVncmF0aW9uLCAKICBvcmlnLnJlZHVjdGlvbiA9ICd1bmludGVncmF0ZWQuc2N0LnBjYScsCiAgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAnU0NUJywKICBuZXcucmVkdWN0aW9uID0gJ2ludGVncmF0ZWQuc2N0LnJwY2EnKQpgYGAKCmBgYHtyX25vX2tsaXBweSwgaW50ZWdyYXRlX2xheWVyc19kb19ub3RfcnVuLCBldmFsPUZBTFNFLCBpbmNsdWRlPVRSVUV9CiMjIyBETyBOT1QgUlVOICMjIwpnZW9fc28gPSBJbnRlZ3JhdGVMYXllcnMoCiAgb2JqZWN0ID0gZ2VvX3NvLCAKICBtZXRob2QgPSBSUENBSW50ZWdyYXRpb24sIAogIG9yaWcucmVkdWN0aW9uID0gJ3VuaW50ZWdyYXRlZC5zY3QucGNhJywKICBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICdTQ1QnLAogIG5ldy5yZWR1Y3Rpb24gPSAnaW50ZWdyYXRlZC5zY3QucnBjYScpCmBgYAoKCk5vdGUgdGhhdCB0aGUgaW50ZWdyYXRpb24gaXMgcnVuIG9uIHRoZSBsYXllcnMsIHdoaWNoIGZyb20gb3VyIHByaW9yIHN0ZXBzIGVhY2ggY29ycmVzcG9uZCB0byBhIHNpbmdsZSBzYW1wbGUsIGluIG91ciBgZ2VvX3NvYCBTZXVyYXQgb2JqZWN0LiBGb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiwgcmVmZXIgdG8gdGhlIFNldXJhdCB2NSB2aWduZXR0ZSBvbiBpbnRlZ3JhdGl2ZSBhbmFseXNpcyAoW2xpbmtdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvYXJ0aWNsZXMvc2V1cmF0NV9pbnRlZ3JhdGlvbiNwZXJmb3JtLXN0cmVhbWxpbmVkLW9uZS1saW5lLWludGVncmF0aXZlLWFuYWx5c2lzKSkgcHJvdmlkZXMgZXhhbXBsZXMgb2YgZWFjaCBpbnRlZ3JhdGlvbiBtZXRob2QuIE5vdGUsIHRoYXQgdGhlIHZpZ25ldHRlIGNvZGUgdXNlcyB0aGUgYE5vcm1hbGl6ZURhdGEoKWAsIGBTY2FsZURhdGEoKWAsIGBGaW5kVmFyaWFibGVGZWF0dXJlcygpYCBwaXBlbGluZSwgc28gdGhlaXIgYEludGVncmF0ZUxheWVycygpYCBjYWxsIGRvZXMgbm90IGluY2x1ZGUgYG5vcm1hbGl6YXRpb24ubWV0aG9kID0gJ1NDVCdgLCBhcyBvdXJzIG11c3QuCgpOb3RlIHdlIGhhdmUgc3BlY2lmaWVkIHRoZSB1bmludGVncmF0ZWQgcmVkdWN0aW9uIGB1bmludGVncmF0ZWQuc2N0LnBjYWAsIHdoaWNoIGlzIHdoYXQgYEludGVncmF0ZUxheWVycygpYCBvcGVyYXRlcyBvbiwgYWxvbmcgd2l0aCB0aGUgYFNDVGAgYXNzYXkuIExldCdzIHRha2UgYSBsb29rIHRvIHNlZSB3aGF0J3MgZGlmZmVyZW50IGFib3V0IHRoZSBgU2V1cmF0YCBvYmplY3Q6Cgo+ICoqTG9hZCBpbnRlZ3JhdGVkIGRhdGEqKgo+IAo+IEJlY2F1c2UgdGhlIGBJbnRlZ3JhdGVMYXllcnMoKWAgZnVuY3Rpb24gdGFrZXMgYSB3aGlsZSB0byBydW4sIHdlIHdpbGwgc2ltcGx5IGxvYWQgdGhlIFJQQ0EgaW50ZWdyYXRlZCBgZ2VvX3NvYCBvYmplY3QgZnJvbSBhIGZpbGUgd2UgaGF2ZSBwcmV2aW91c2x5IGdlbmVyYXRlZC4KPiAKPiBgYGB7ciwgZXZhbCA9IEZBTFNFfQo+IGdlb19zbyA9IHJlYWRSRFMoJy9ob21lL3dvcmtzaG9wL3JjYXZhbGNhL0lTQ19SL3Jlc3VsdHMvcmRhdGEvZ2VvX3NvX3NjdF9pbnRlZ3JhdGVkLnJkcycpCj4gYGBgCj4gCgpgYGB7ciwgcHJldmlld19zZXVyYXR9CiMgQ2hlY2sgb3VyIHVwZGF0ZWQgb2JqZWN0IHRoYXQgd2UndmUgcmVhZCBpbiBmcm9tIGZpbGUgLS0tLS0tLS0tLS0tLS0tLS0tLSAKIyBPYnNlcnZlIHRoYXQgd2Ugbm93IGhhdmUgYSBuZXcgcmVkdWN0aW9uLCBgaW50ZWdyYXRlZC5zY3QucnBjYWAKZ2VvX3NvIApgYGAKClZpZXdlZCBpbiBvdXIgcnVubmluZyBzY2hlbWF0aWM6CgohW0ltYWdlOiBTY2hlbWF0aWMgYWZ0ZXIgSW50ZWdyYXRlTGF5ZXJzKCkuXShpbWFnZXMvc2V1cmF0X3NjaGVtYXRpYy9TbGlkZTcucG5nKQoKV2UgY2FuIGFsc28gY29uZmlybSB0aGF0IG91ciBpbnRlZ3JhdGlvbiBtZXRob2QgaGFzIGhlbHBlZCB0byBjb3JyZWN0IHRoZSBgRGF5YCBlZmZlY3RzIHdlIHNhdyBpbiB0aGUgaW5pdGlhbCBQQ0EgcGxvdHMKCmBgYHtyLCBjaGVja19pbnRlZ3JhdGlvbiwgZmlnLnNob3c9J2hvbGQnfQojIFZpc3VhbGl6ZSBQQ0EgKFBDMSBhbmQgUEMyLCBpbnRlZ3JhdGVkKSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KRGltUGxvdChnZW9fc28sIHJlZHVjdGlvbiA9ICdpbnRlZ3JhdGVkLnNjdC5ycGNhJywgZ3JvdXAuYnkgPSAnZGF5JykKZ2dzYXZlKGZpbGVuYW1lID0gJ3Jlc3VsdHMvZmlndXJlcy9xY19wY2FfcGxvdF9pbnRlZ3JhdGVkX3NjdF9kYXkucG5nJywgd2lkdGggPSA3LCBoZWlnaHQgPSA2LCB1bml0cyA9ICdpbicpCmBgYAoKPCEtLS0gY29uZmlybWVkIGluIHRlc3RpbmcgdGhhdCBQQ0EgcGxvdCBpcyB1cGRhdGVkIGlmIHJ1biBvbiBjYXRjaGVkIGdlb19zbyBvYmplY3QgLS0tPgoKIyBTYXZlIG91ciBwcm9ncmVzcwoKQmVmb3JlIHdlIG1vdmUgb24sIGxldCdzIHNhdmUgb3VyIHVwZGF0ZWQgU2V1cmF0IG9iamVjdCB0byBmaWxlOgoKYGBge3IsIHNhdmVfcmRzX2hpZGRlbiwgZWNobyA9IEZBTFNFfQppZighZmlsZS5leGlzdHMoJ3Jlc3VsdHMvcmRhdGEvZ2VvX3NvX3NjdF9pbnRlZ3JhdGVkLnJkcycpKSB7CiAgc2F2ZVJEUyhnZW9fc28sIGZpbGUgPSAncmVzdWx0cy9yZGF0YS9nZW9fc29fc2N0X2ludGVncmF0ZWQucmRzJykKfQpgYGAKCmBgYHtyLCBzYXZlX3JkcywgZXZhbCA9IEZBTFNFfQojIFNhdmUgdXBkYXRlZCBzZXVyYXQgb2JqZWN0IHRvIGZpbGUgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0Kc2F2ZVJEUyhnZW9fc28sIGZpbGUgPSAncmVzdWx0cy9yZGF0YS9nZW9fc29fc2N0X2ludGVncmF0ZWQucmRzJykKYGBgCgo+ICoqT3RoZXIgaW50ZWdyYXRpb24gbWV0aG9kcyoqCj4gCj4gQWZ0ZXIgbm9ybWFsaXppbmcgdGhlIGRhdGEgd2l0aCBgU0NUcmFuc2Zvcm0oKWAgYW5kIHBlcmZvcm1lZCB0aGUgZGltZW5zaW9uIHJlZHVjdGlvbiB3aXRoIGBSdW5QQ0EoKWAsIGFsdGVybmF0aXZlbHkgd2UgY291bGQgYWxzbyB1c2UgdGhlIGBDQ0FgIGludGVncmF0aW9uIG1ldGhvZCB3aXRoOgo+IAo+IGBgYHtyX25vX2tsaXBweSwgZXZhbCA9IEZBTFNFfQo+ICMjIyBETyBOT1QgUlVOICMjIwo+IGdlb19zbyA9IEludGVncmF0ZUxheWVycygKPiAgICAgb2JqZWN0ID0gZ2VvX3NvLCAKPiAgICAgbWV0aG9kID0gQ0NBSW50ZWdyYXRpb24sIAo+ICAgICBvcmlnLnJlZHVjdGlvbiA9ICd1bmludGVncmF0ZWQuc2N0LnBjYScsCj4gICAgIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gJ1NDVCcsCj4gICAgIG5ldy5yZWR1Y3Rpb24gPSAnaW50ZWdyYXRlZC5zY3QuY2NhJykKPiBgYGAKPgoKIyBTdW1tYXJ5CgpJbiB0aGlzIHNlY3Rpb24sIHdlOgoKLSBEaXNjdXNzZWQgaG93IFBDQSBpcyB1c2VkIGluIHNjUk5BLXNlcSBhbmFseXNpcy4KLSBEZW1vbnN0cmF0ZWQgc29tZSB2aXN1YWxpemF0aW9ucyBhZnRlciBjb21wdXRpbmcgUENBLgotIERpc2N1c3NlZCBob3cgdG8gZGVjaWRlIGhvdyBtYW55IFBDcyB0byB1c2UgaW4gZG93bnN0cmVhbSBhbmFseXNpcy4KLSBJbnRlZ3JhdGVkIHRoZSBkYXRhLgoKTmV4dCBzdGVwczogQ2x1c3RlcmluZyBhbmQgcHJvamVjdGlvbgoKLS0tLQoKVGhlc2UgbWF0ZXJpYWxzIGhhdmUgYmVlbiBhZGFwdGVkIGFuZCBleHRlbmRlZCBmcm9tIG1hdGVyaWFscyBsaXN0ZWQgYWJvdmUuIFRoZXNlIGFyZSBvcGVuIGFjY2VzcyBtYXRlcmlhbHMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBbQ3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbiBsaWNlbnNlIChDQyBCWSA0LjApXShodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS80LjAvKSwgd2hpY2ggcGVybWl0cyB1bnJlc3RyaWN0ZWQgdXNlLCBkaXN0cmlidXRpb24sIGFuZCByZXByb2R1Y3Rpb24gaW4gYW55IG1lZGl1bSwgcHJvdmlkZWQgdGhlIG9yaWdpbmFsIGF1dGhvciBhbmQgc291cmNlIGFyZSBjcmVkaXRlZC4KCjxici8+Cjxici8+Cjxoci8+CnwgW1ByZXZpb3VzIGxlc3Nvbl0oMDMtTm9ybWFsaXphdGlvbi5odG1sKSB8IFtUb3Agb2YgdGhpcyBsZXNzb25dKCN0b3ApIHwgW05leHQgbGVzc29uXSgwNS1Qcm9qZWN0aW9uQW5kQ2x1c3RlcmluZy5odG1sKSB8CnwgOi0tLSB8IDotLS0tOiB8IC0tLTogfAo=