Workflow Overview


wayfinder

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.

A simple PCA example (from HBC)

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).

PCA gene loadings (from HBC)

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().
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()
DimHeatmap for PC1 alone

DimHeatmap for PC1 alone

DimHeatmap for PC1-18

DimHeatmap for PC1-18

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.

CCA integration illustration, modified from Stuart and Butler, 2018 -https://doi.org/10.1101/460147
CCA integration illustration, modified from Stuart and Butler, 2018 -https://doi.org/10.1101/460147

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().
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.




Previous lesson Top of this lesson Next lesson
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=