Workflow Overview


wayfinder

Introduction

After removing low-quality cells from the data, the next task is the normalization and variance stabilization of the counts for downstream analysis.

Variation in scRNA-seq data comes from biological sources:

  • Differences in cell type or state
  • Differences in response to treatment

And from technical sources:

  • Fluctuations in cellular RNA content
  • Efficiency in lysis and reverse transcription
  • Stochastic sampling during sequencing

A. The counts in the feature barcode matrix are a blend of technical and biological effects.
B. Technical effects can distort or mask the biological effects of interest; this can confound downstream analyses.
C. Seurat can model the technical effects based on overall patterns in expression across all cells.
D. Once these technical effects are minimized, the remaining signal is primarily biological variance.


It is important to control for the technical sources of variation without removing the biological sources of variation. A key driver of technical variation is cellular sequencing depth (that is, the number of UMIs sequenced per cell). In the figure below, Sample A (left, red reads) is more deeply sequenced than Sample B (right, green reads). In a test for differential expression, we want to account for the difference in sequencing depth to avoid erroneously calling a gene differentially expressed.

Image: Different sequencing depths can erroneously imply differential expression. Source: HBC training materials.
Image: Different sequencing depths can erroneously imply differential expression. Source: HBC training materials.

Objectives

  • Understand why normalization is needed.
  • Describe the essence of the SCTransform() method.
  • Understand normalization options.
  • Normalize the counts with SCTransform().

Normalization

We will use the SCTransform() function, which uses a generalized linear model (GLM) framework to account for cell-level sequencing depth while also stabilizing the variance of the counts. Let’s get the normalization started, as it takes a little time, and then we can explain what it’s doing and why it is an improvement on alternate methods.

Layers of a Seurat object

The assays in a Seurat v5 object store data in “layers”. When we first read the data in, we saw there was only one layer in the RNA assay. In order to run SCTransform() in Seurat v5, we have to separate the sample-wise data into layers with the following command:

##### Day 1 - Normalization 

# Separate sample data into layers  ---------------------------------------
geo_so[['RNA']] = split(geo_so[['RNA']], f = geo_so$orig.ident)
geo_so
An object of class Seurat 
26489 features across 31560 samples within 1 assay 
Active assay: RNA (26489 features, 0 variable features)
 12 layers present: counts.HO.Day0.replicate1, counts.HO.Day0.replicate2, counts.HO.Day0.replicate3, counts.HO.Day0.replicate4, counts.HO.Day21.replicate1, counts.HO.Day21.replicate2, counts.HO.Day21.replicate3, counts.HO.Day21.replicate4, counts.HO.Day7.replicate1, counts.HO.Day7.replicate2, counts.HO.Day7.replicate3, counts.HO.Day7.replicate4

We see the 12 layers containing the count data for each of the samples. Our running schematic has now changed:

Image: Schematic splitting the counts layer of the RNA assay by sample.
Image: Schematic splitting the counts layer of the RNA assay by sample.

Next, run SCTransform():

# Normalize the data with SCTransform  ------------------------------------
geo_so = SCTransform(geo_so)

Normalization explained

To get the idea of what SCTransform() is doing, we’ll consider a simplified example.

Consider two cells that are identical in terms of their type, expression, etc. Imagine that we put them through the microfluidic device and then sequenced the RNA content.

If we were to plot the total cell UMI count against a particular gene UMI count, in an ideal world, the points should be directly on top of each other because the cells had identical expression and everything done to measure that expression went perfectly.

However, we don’t live in a perfect world. We will likely observe the cells have different total cell UMI counts as well as different gene UMI counts. This difference, given that the cells were identical, can be attributed to technical factors. For example, efficiency in lysis or in reverse transcription, or the stoachastic sampling that occurs during sequencing.

It is these technical factors that normalization seeks to correct for, getting us back to the “true” expression state.

Imagine doing this for thousands of cells. We would get a point cloud like the above. Importantly, that point cloud has structure. There is a relationship between the total cell UMI count and the gene UMI count for each gene.

We could fit a line through the point cloud, where we estimate the intercept, the slope, and the error.

The residuals, or the distance from the point to the line, represents the expression of the gene less the total cell UMI count influence.

In other words, the residuals represent the biological variance, and the regression removes the technical variance. Note now that the residuals are the about the same for the two cells.

Additional details

The full description and justification of the SCTransform() function are provided in two excellent papers:

  • Hafemeister & Satija, Normalization and variance stabilization of single-cell RNA-seq data using regularized negative binomial regression, 2019, Genome Biology (link)

  • Choudhary & Satija, Comparison and evaluation of statistical error models for scRNA-seq, 2022, Genome Biology (link)

A benefit of the this framework for normalization is that the model can include other terms which can account for unwanted technical variation. One example of this is the percent mitochondrial reads (percent.mt). The vars.to.regress parameter of SCTransform() is used for this purpose. See the documentation for details.

Other normalizations

From the Seurat documentation, the SCTransform() function “replaces NormalizeData(), ScaleData(), and FindVariableFeatures()”. This chain of functions is referred to as the “log-normalization procedure”. You may see these three commands in other vignettes, and even in other Seurat vignettes (source). In the two papers referenced above, the authors show how the log-normalization procedure does not always fully account for cell sequencing depth and overdispersion. Therefore, we urge you to use this alternative pipeline with caution.

Normalization, continued

By now, SCTransform() should have finished running, so let’s take a look at the result:

# Examine Seurat Object  --------------------------------------------------
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

We now observe changes to our running schematic:

Image: Schematic after SCTransform().
Image: Schematic after SCTransform().

Note that we have a new assay, SCT which has three layers: counts, data, and scale.data. Also note additional columns in meta.data and the change of the active.assay to SCT. SCTransform() has also determined the common variable features across the cells to be used in our downstream analysis. Viewed in our running schematic:

Save our progress

Let’s save this normalized form of our Seurat object.

# Save Seurat object  -----------------------------------------------------
saveRDS(geo_so, file = 'results/rdata/geo_so_sct_normalized.rds')

Summary

In this section we have run the SCTransform() function to account for variation in cell sequencing depth and to stabilize the variance of the counts.

Next steps: PCA and integration


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
LS0tCnRpdGxlOiAiTm9ybWFsaXphdGlvbiIKYXV0aG9yOiAiVU0gQmlvaW5mb3JtYXRpY3MgQ29yZSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgICAgICAgaHRtbF9kb2N1bWVudDoKICAgICAgICAgICAgaW5jbHVkZXM6CiAgICAgICAgICAgICAgICBpbl9oZWFkZXI6IGhlYWRlci5odG1sCiAgICAgICAgICAgIHRoZW1lOiBwYXBlcgogICAgICAgICAgICB0b2M6IHRydWUKICAgICAgICAgICAgdG9jX2RlcHRoOiA0CiAgICAgICAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICAgICAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICAgICAgICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICAgICAgICAgIG1hcmtkb3duOiBHRk0KICAgICAgICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CmJvZHksIHRkIHsKICAgZm9udC1zaXplOiAxOHB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDEycHg7Cn0KcHJlIHsKICBmb250LXNpemU6IDEycHgKfQoKdGFibGUuZmlnLCB0aC5maWcsIHRkLmZpZyB7CiAgYm9yZGVyOiAxcHggc29saWQgYmxhY2s7CiAgYm9yZGVyLWNvbGxhcHNlOiBjb2xsYXBzZTsKICBwYWRkaW5nOiAxNXB4Owp9Cjwvc3R5bGU+CgpgYGB7ciBrbGlwcHksIGVjaG89RkFMU0UsIGluY2x1ZGU9VFJVRX0Ka2xpcHB5OjprbGlwcHkobGFuZyA9IGMoInIiLCAibWFya2Rvd24iLCAiYmFzaCIpLCBwb3NpdGlvbiA9IGMoInRvcCIsICJyaWdodCIpKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnNvdXJjZSgiLi4vYmluL2NodW5rLW9wdGlvbnMuUiIpCmtuaXRyX2ZpZ19wYXRoKCIwMy1Ob3JtYWxpemF0aW9uLzAzLSIpCmBgYAoKIyBXb3JrZmxvdyBPdmVydmlldyB7LnVubGlzdGVkIC51bm51bWJlcmVkfQoKPGJyLz4KPGltZyBzcmM9ImltYWdlcy93YXlmaW5kZXIvd2F5ZmluZGVyLnBuZyIgYWx0PSJ3YXlmaW5kZXIiIHN0eWxlPSJoZWlnaHQ6IDQwMHB4OyIvPgo8YnIvPgo8YnIvPiAKCiMgSW50cm9kdWN0aW9uIAoKQWZ0ZXIgcmVtb3ZpbmcgbG93LXF1YWxpdHkgY2VsbHMgZnJvbSB0aGUgZGF0YSwgdGhlIG5leHQgdGFzayBpcyB0aGUgbm9ybWFsaXphdGlvbiBhbmQgdmFyaWFuY2Ugc3RhYmlsaXphdGlvbiBvZiB0aGUgY291bnRzIGZvciBkb3duc3RyZWFtIGFuYWx5c2lzLiAKClZhcmlhdGlvbiBpbiBzY1JOQS1zZXEgZGF0YSBjb21lcyBmcm9tIGJpb2xvZ2ljYWwgc291cmNlczoKCi0gRGlmZmVyZW5jZXMgaW4gY2VsbCB0eXBlIG9yIHN0YXRlCi0gRGlmZmVyZW5jZXMgaW4gcmVzcG9uc2UgdG8gdHJlYXRtZW50CgpBbmQgZnJvbSB0ZWNobmljYWwgc291cmNlczoKCi0gRmx1Y3R1YXRpb25zIGluIGNlbGx1bGFyIFJOQSBjb250ZW50Ci0gRWZmaWNpZW5jeSBpbiBseXNpcyBhbmQgcmV2ZXJzZSB0cmFuc2NyaXB0aW9uCi0gU3RvY2hhc3RpYyBzYW1wbGluZyBkdXJpbmcgc2VxdWVuY2luZwoKPGJyLz4KPHRhYmxlIGNsYXNzPSdmaWcnPgo8dHIgY2xhc3M9J2ZpZyc+PHRkIGNsYXNzPSdmaWcnPiFbXShpbWFnZXMvZ3JhcGhpY2FsX2Fic3RyYWN0cy9ncmFwaGljYWxfYWJzdHJhY3Rfbm9ybWFsaXphdGlvbi5wbmcpPC90ZD48L3RyPgo8dHIgY2xhc3M9J2ZpZyc+PHRkIGNsYXNzPSdmaWcnPkEuIFRoZSBjb3VudHMgaW4gdGhlIGZlYXR1cmUgYmFyY29kZSBtYXRyaXggYXJlIGEgYmxlbmQgb2YgdGVjaG5pY2FsIGFuZCBiaW9sb2dpY2FsIGVmZmVjdHMuIDxici8+CkIuIFRlY2huaWNhbCBlZmZlY3RzIGNhbiBkaXN0b3J0IG9yIG1hc2sgdGhlIGJpb2xvZ2ljYWwgZWZmZWN0cyBvZiBpbnRlcmVzdDsgdGhpcyBjYW4gY29uZm91bmQgZG93bnN0cmVhbSBhbmFseXNlcy4gPGJyLz4KQy4gU2V1cmF0IGNhbiBtb2RlbCB0aGUgdGVjaG5pY2FsIGVmZmVjdHMgYmFzZWQgb24gb3ZlcmFsbCBwYXR0ZXJucyBpbiBleHByZXNzaW9uIGFjcm9zcyBhbGwgY2VsbHMuIDxici8+CkQuIE9uY2UgdGhlc2UgdGVjaG5pY2FsIGVmZmVjdHMgYXJlIG1pbmltaXplZCwgdGhlIHJlbWFpbmluZyBzaWduYWwgaXMgcHJpbWFyaWx5IGJpb2xvZ2ljYWwgdmFyaWFuY2UuPC90ZD48L3RyPgo8L3RhYmxlPgo8YnIvPgoKSXQgaXMgaW1wb3J0YW50IHRvIGNvbnRyb2wgZm9yIHRoZSB0ZWNobmljYWwgc291cmNlcyBvZiB2YXJpYXRpb24gd2l0aG91dCByZW1vdmluZyB0aGUgYmlvbG9naWNhbCBzb3VyY2VzIG9mIHZhcmlhdGlvbi4gQSBrZXkgZHJpdmVyIG9mIHRlY2huaWNhbCB2YXJpYXRpb24gaXMgY2VsbHVsYXIgc2VxdWVuY2luZyBkZXB0aCAodGhhdCBpcywgdGhlIG51bWJlciBvZiBVTUlzIHNlcXVlbmNlZCBwZXIgY2VsbCkuIEluIHRoZSBmaWd1cmUgYmVsb3csIFNhbXBsZSBBIChsZWZ0LCByZWQgcmVhZHMpIGlzIG1vcmUgZGVlcGx5IHNlcXVlbmNlZCB0aGFuIFNhbXBsZSBCIChyaWdodCwgZ3JlZW4gcmVhZHMpLiBJbiBhIHRlc3QgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uLCB3ZSB3YW50IHRvIGFjY291bnQgZm9yIHRoZSBkaWZmZXJlbmNlIGluIHNlcXVlbmNpbmcgZGVwdGggdG8gYXZvaWQgZXJyb25lb3VzbHkgY2FsbGluZyBhIGdlbmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLgoKIVtJbWFnZTogRGlmZmVyZW50IHNlcXVlbmNpbmcgZGVwdGhzIGNhbiBlcnJvbmVvdXNseSBpbXBseSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4gU291cmNlOiBbSEJDIHRyYWluaW5nIG1hdGVyaWFsc10oaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvMDJfREdFX2NvdW50X25vcm1hbGl6YXRpb24uaHRtbCkuXShpbWFnZXMvY3VycmljdWx1bS8wMy1Ob3JtYWxpemF0aW9uL25vcm1hbGl6YXRpb25fZGVwdGgucG5nKQoKIyMgT2JqZWN0aXZlcwoKLSBVbmRlcnN0YW5kIHdoeSBub3JtYWxpemF0aW9uIGlzIG5lZWRlZC4KLSBEZXNjcmliZSB0aGUgZXNzZW5jZSBvZiB0aGUgYFNDVHJhbnNmb3JtKClgIG1ldGhvZC4KLSBVbmRlcnN0YW5kIG5vcm1hbGl6YXRpb24gb3B0aW9ucy4KLSBOb3JtYWxpemUgdGhlIGNvdW50cyB3aXRoIGBTQ1RyYW5zZm9ybSgpYC4KCi0tLQoKYGBge3IsIHJlYWRfcmRzX2hpZGRlbiwgZWNobyA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KaWYoIWV4aXN0cygnZ2VvX3NvJykpIHsKICBsaWJyYXJ5KFNldXJhdCkKICBsaWJyYXJ5KEJQQ2VsbHMpCiAgbGlicmFyeSh0aWR5dmVyc2UpCgogIG9wdGlvbnMoZnV0dXJlLmdsb2JhbHMubWF4U2l6ZSA9IDFlOSkKCiAgZ2VvX3NvID0gcmVhZFJEUygncmVzdWx0cy9yZGF0YS9nZW9fc29fZmlsdGVyZWQucmRzJykKfQpgYGAKCiMgTm9ybWFsaXphdGlvbgoKV2Ugd2lsbCB1c2UgdGhlIGBTQ1RyYW5zZm9ybSgpYCBmdW5jdGlvbiwgd2hpY2ggdXNlcyBhIGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbCAoR0xNKSBmcmFtZXdvcmsgdG8gYWNjb3VudCBmb3IgY2VsbC1sZXZlbCBzZXF1ZW5jaW5nIGRlcHRoIHdoaWxlIGFsc28gc3RhYmlsaXppbmcgdGhlIHZhcmlhbmNlIG9mIHRoZSBjb3VudHMuIExldCdzIGdldCB0aGUgbm9ybWFsaXphdGlvbiBzdGFydGVkLCBhcyBpdCB0YWtlcyBhIGxpdHRsZSB0aW1lLCBhbmQgdGhlbiB3ZSBjYW4gZXhwbGFpbiB3aGF0IGl0J3MgZG9pbmcgYW5kIHdoeSBpdCBpcyBhbiBpbXByb3ZlbWVudCBvbiBhbHRlcm5hdGUgbWV0aG9kcy4KCiMjIExheWVycyBvZiBhIFNldXJhdCBvYmplY3QKClRoZSBgYXNzYXlzYCBpbiBhIFNldXJhdCB2NSBvYmplY3Qgc3RvcmUgZGF0YSBpbiAibGF5ZXJzIi4gV2hlbiB3ZSBmaXJzdCByZWFkIHRoZSBkYXRhIGluLCB3ZSBzYXcgdGhlcmUgd2FzIG9ubHkgb25lIGxheWVyIGluIHRoZSBSTkEgYXNzYXkuIEluIG9yZGVyIHRvIHJ1biBgU0NUcmFuc2Zvcm0oKWAgaW4gU2V1cmF0IHY1LCB3ZSBoYXZlIHRvIHNlcGFyYXRlIHRoZSBzYW1wbGUtd2lzZSBkYXRhIGludG8gbGF5ZXJzIHdpdGggdGhlIGZvbGxvd2luZyBjb21tYW5kOgoKYGBge3IsIGNvbnZlcnRfdG9fbGF5ZXJzfQojIyMjIyBEYXkgMSAtIE5vcm1hbGl6YXRpb24gCgojIFNlcGFyYXRlIHNhbXBsZSBkYXRhIGludG8gbGF5ZXJzICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KZ2VvX3NvW1snUk5BJ11dID0gc3BsaXQoZ2VvX3NvW1snUk5BJ11dLCBmID0gZ2VvX3NvJG9yaWcuaWRlbnQpCmdlb19zbwpgYGAKCldlIHNlZSB0aGUgMTIgbGF5ZXJzIGNvbnRhaW5pbmcgdGhlIGNvdW50IGRhdGEgZm9yIGVhY2ggb2YgdGhlIHNhbXBsZXMuIE91ciBydW5uaW5nIHNjaGVtYXRpYyBoYXMgbm93IGNoYW5nZWQ6CgohW0ltYWdlOiBTY2hlbWF0aWMgc3BsaXR0aW5nIHRoZSBjb3VudHMgbGF5ZXIgb2YgdGhlIFJOQSBhc3NheSBieSBzYW1wbGUuXShpbWFnZXMvc2V1cmF0X3NjaGVtYXRpYy9TbGlkZTQucG5nKQoKTmV4dCwgcnVuIGBTQ1RyYW5zZm9ybSgpYDoKCmBgYHtyLCBub3JtYWxpemUsIGNhY2hlID0gVFJVRSwgY2FjaGUubGF6eSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyBOb3JtYWxpemUgdGhlIGRhdGEgd2l0aCBTQ1RyYW5zZm9ybSAgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmdlb19zbyA9IFNDVHJhbnNmb3JtKGdlb19zbykKYGBgCgojIyBOb3JtYWxpemF0aW9uIGV4cGxhaW5lZAoKVG8gZ2V0IHRoZSBpZGVhIG9mIHdoYXQgYFNDVHJhbnNmb3JtKClgIGlzIGRvaW5nLCB3ZSdsbCBjb25zaWRlciBhIHNpbXBsaWZpZWQgZXhhbXBsZS4KCiFbXShpbWFnZXMvY3VycmljdWx1bS8wMy1Ob3JtYWxpemF0aW9uL1NsaWRlMS5wbmcpCgpDb25zaWRlciB0d28gY2VsbHMgdGhhdCBhcmUgaWRlbnRpY2FsIGluIHRlcm1zIG9mIHRoZWlyIHR5cGUsIGV4cHJlc3Npb24sIGV0Yy4gSW1hZ2luZSB0aGF0IHdlIHB1dCB0aGVtIHRocm91Z2ggdGhlIG1pY3JvZmx1aWRpYyBkZXZpY2UgYW5kIHRoZW4gc2VxdWVuY2VkIHRoZSBSTkEgY29udGVudC4KCiFbXShpbWFnZXMvY3VycmljdWx1bS8wMy1Ob3JtYWxpemF0aW9uL1NsaWRlMi5wbmcpCgpJZiB3ZSB3ZXJlIHRvIHBsb3QgdGhlIHRvdGFsIGNlbGwgVU1JIGNvdW50IGFnYWluc3QgYSBwYXJ0aWN1bGFyIGdlbmUgVU1JIGNvdW50LCBpbiBhbiBpZGVhbCB3b3JsZCwgdGhlIHBvaW50cyBzaG91bGQgYmUgZGlyZWN0bHkgb24gdG9wIG9mIGVhY2ggb3RoZXIgYmVjYXVzZSB0aGUgY2VsbHMgaGFkIGlkZW50aWNhbCBleHByZXNzaW9uIGFuZCBldmVyeXRoaW5nIGRvbmUgdG8gbWVhc3VyZSB0aGF0IGV4cHJlc3Npb24gd2VudCBwZXJmZWN0bHkuCgohW10oaW1hZ2VzL2N1cnJpY3VsdW0vMDMtTm9ybWFsaXphdGlvbi9TbGlkZTMucG5nKQoKSG93ZXZlciwgd2UgZG9uJ3QgbGl2ZSBpbiBhIHBlcmZlY3Qgd29ybGQuIFdlIHdpbGwgbGlrZWx5IG9ic2VydmUgdGhlIGNlbGxzIGhhdmUgZGlmZmVyZW50IHRvdGFsIGNlbGwgVU1JIGNvdW50cyBhcyB3ZWxsIGFzIGRpZmZlcmVudCBnZW5lIFVNSSBjb3VudHMuIFRoaXMgZGlmZmVyZW5jZSwgZ2l2ZW4gdGhhdCB0aGUgY2VsbHMgd2VyZSBpZGVudGljYWwsIGNhbiBiZSBhdHRyaWJ1dGVkIHRvIHRlY2huaWNhbCBmYWN0b3JzLiBGb3IgZXhhbXBsZSwgZWZmaWNpZW5jeSBpbiBseXNpcyBvciBpbiByZXZlcnNlIHRyYW5zY3JpcHRpb24sIG9yIHRoZSBzdG9hY2hhc3RpYyBzYW1wbGluZyB0aGF0IG9jY3VycyBkdXJpbmcgc2VxdWVuY2luZy4KCioqSXQgaXMgdGhlc2UgdGVjaG5pY2FsIGZhY3RvcnMgdGhhdCBub3JtYWxpemF0aW9uIHNlZWtzIHRvIGNvcnJlY3QgZm9yLCBnZXR0aW5nIHVzIGJhY2sgdG8gdGhlIOKAnHRydWXigJ0gZXhwcmVzc2lvbiBzdGF0ZS4qKgoKIVtdKGltYWdlcy9jdXJyaWN1bHVtLzAzLU5vcm1hbGl6YXRpb24vU2xpZGU0LnBuZykKCkltYWdpbmUgZG9pbmcgdGhpcyBmb3IgdGhvdXNhbmRzIG9mIGNlbGxzLiBXZSB3b3VsZCBnZXQgYSBwb2ludCBjbG91ZCBsaWtlIHRoZSBhYm92ZS4gSW1wb3J0YW50bHksIHRoYXQgcG9pbnQgY2xvdWQgKipoYXMgc3RydWN0dXJlKiouIFRoZXJlIGlzIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHRvdGFsIGNlbGwgVU1JIGNvdW50IGFuZCB0aGUgZ2VuZSBVTUkgY291bnQgZm9yIGVhY2ggZ2VuZS4KCiFbXShpbWFnZXMvY3VycmljdWx1bS8wMy1Ob3JtYWxpemF0aW9uL1NsaWRlNS5wbmcpCgpXZSBjb3VsZCBmaXQgYSBsaW5lIHRocm91Z2ggdGhlIHBvaW50IGNsb3VkLCB3aGVyZSB3ZSBlc3RpbWF0ZSB0aGUgaW50ZXJjZXB0LCB0aGUgc2xvcGUsIGFuZCB0aGUgZXJyb3IuCgohW10oaW1hZ2VzL2N1cnJpY3VsdW0vMDMtTm9ybWFsaXphdGlvbi9TbGlkZTYucG5nKQoKVGhlIHJlc2lkdWFscywgb3IgdGhlIGRpc3RhbmNlIGZyb20gdGhlIHBvaW50IHRvIHRoZSBsaW5lLCByZXByZXNlbnRzIHRoZSBleHByZXNzaW9uIG9mIHRoZSBnZW5lIGxlc3MgdGhlIHRvdGFsIGNlbGwgVU1JIGNvdW50IGluZmx1ZW5jZS4KCiFbXShpbWFnZXMvY3VycmljdWx1bS8wMy1Ob3JtYWxpemF0aW9uL1NsaWRlNy5wbmcpCgpJbiBvdGhlciB3b3JkcywgdGhlIHJlc2lkdWFscyByZXByZXNlbnQgdGhlIGJpb2xvZ2ljYWwgdmFyaWFuY2UsIGFuZCB0aGUgcmVncmVzc2lvbiByZW1vdmVzIHRoZSB0ZWNobmljYWwgdmFyaWFuY2UuIE5vdGUgbm93IHRoYXQgdGhlIHJlc2lkdWFscyBhcmUgdGhlIGFib3V0IHRoZSBzYW1lIGZvciB0aGUgdHdvIGNlbGxzLgoKIyMgQWRkaXRpb25hbCBkZXRhaWxzCgpUaGUgZnVsbCBkZXNjcmlwdGlvbiBhbmQganVzdGlmaWNhdGlvbiBvZiB0aGUgYFNDVHJhbnNmb3JtKClgIGZ1bmN0aW9uIGFyZSBwcm92aWRlZCBpbiB0d28gZXhjZWxsZW50IHBhcGVyczoKCi0gSGFmZW1laXN0ZXIgJiBTYXRpamEsICpOb3JtYWxpemF0aW9uIGFuZCB2YXJpYW5jZSBzdGFiaWxpemF0aW9uIG9mIHNpbmdsZS1jZWxsIFJOQS1zZXEgZGF0YSB1c2luZyByZWd1bGFyaXplZCBuZWdhdGl2ZSBiaW5vbWlhbCByZWdyZXNzaW9uKiwgMjAxOSwgR2Vub21lIEJpb2xvZ3kgKFtsaW5rXShodHRwczovL2dlbm9tZWJpb2xvZ3kuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9zMTMwNTktMDE5LTE4NzQtMSNhdmFpbGFiaWxpdHktb2YtZGF0YS1hbmQtbWF0ZXJpYWxzKSkKCi0gQ2hvdWRoYXJ5ICYgU2F0aWphLCAqQ29tcGFyaXNvbiBhbmQgZXZhbHVhdGlvbiBvZiBzdGF0aXN0aWNhbCBlcnJvciBtb2RlbHMgZm9yIHNjUk5BLXNlcSosIDIwMjIsIEdlbm9tZSBCaW9sb2d5IChbbGlua10oaHR0cHM6Ly9nZW5vbWViaW9sb2d5LmJpb21lZGNlbnRyYWwuY29tL2FydGljbGVzLzEwLjExODYvczEzMDU5LTAyMS0wMjU4NC05KSkKCkEgYmVuZWZpdCBvZiB0aGUgdGhpcyBmcmFtZXdvcmsgZm9yIG5vcm1hbGl6YXRpb24gaXMgdGhhdCB0aGUgbW9kZWwgY2FuIGluY2x1ZGUgb3RoZXIgdGVybXMgd2hpY2ggY2FuIGFjY291bnQgZm9yIHVud2FudGVkIHRlY2huaWNhbCB2YXJpYXRpb24uIE9uZSBleGFtcGxlIG9mIHRoaXMgaXMgdGhlIHBlcmNlbnQgbWl0b2Nob25kcmlhbCByZWFkcyAoYHBlcmNlbnQubXRgKS4gVGhlIGB2YXJzLnRvLnJlZ3Jlc3NgIHBhcmFtZXRlciBvZiBgU0NUcmFuc2Zvcm0oKWAgaXMgdXNlZCBmb3IgdGhpcyBwdXJwb3NlLiBTZWUgdGhlIFtkb2N1bWVudGF0aW9uXShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L3JlZmVyZW5jZS9zY3RyYW5zZm9ybSkgZm9yIGRldGFpbHMuCgojIyBPdGhlciBub3JtYWxpemF0aW9ucwoKRnJvbSB0aGUgU2V1cmF0IGRvY3VtZW50YXRpb24sIHRoZSBgU0NUcmFuc2Zvcm0oKWAgZnVuY3Rpb24gInJlcGxhY2VzIGBOb3JtYWxpemVEYXRhKClgLCBgU2NhbGVEYXRhKClgLCBhbmQgYEZpbmRWYXJpYWJsZUZlYXR1cmVzKClgIi4gVGhpcyBjaGFpbiBvZiBmdW5jdGlvbnMgaXMgcmVmZXJyZWQgdG8gYXMgdGhlICJsb2ctbm9ybWFsaXphdGlvbiBwcm9jZWR1cmUiLiBZb3UgbWF5IHNlZSB0aGVzZSB0aHJlZSBjb21tYW5kcyBpbiBvdGhlciB2aWduZXR0ZXMsIGFuZCBldmVuIGluIG90aGVyIFNldXJhdCB2aWduZXR0ZXMgKFtzb3VyY2VdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvYXJ0aWNsZXMvc2N0cmFuc2Zvcm1fdmlnbmV0dGUpKS4gSW4gdGhlIHR3byBwYXBlcnMgcmVmZXJlbmNlZCBhYm92ZSwgdGhlIGF1dGhvcnMgc2hvdyBob3cgdGhlIGxvZy1ub3JtYWxpemF0aW9uIHByb2NlZHVyZSBkb2VzIG5vdCBhbHdheXMgZnVsbHkgYWNjb3VudCBmb3IgY2VsbCBzZXF1ZW5jaW5nIGRlcHRoIGFuZCBvdmVyZGlzcGVyc2lvbi4gVGhlcmVmb3JlLCB3ZSB1cmdlIHlvdSB0byB1c2UgdGhpcyBhbHRlcm5hdGl2ZSBwaXBlbGluZSB3aXRoIGNhdXRpb24uCgojIE5vcm1hbGl6YXRpb24sIGNvbnRpbnVlZAoKQnkgbm93LCBgU0NUcmFuc2Zvcm0oKWAgc2hvdWxkIGhhdmUgZmluaXNoZWQgcnVubmluZywgc28gbGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIHJlc3VsdDoKCmBgYHtyLCBwcmV2aWV3X3NldXJhdH0KIyBFeGFtaW5lIFNldXJhdCBPYmplY3QgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmdlb19zbwpgYGAKCldlIG5vdyBvYnNlcnZlIGNoYW5nZXMgdG8gb3VyIHJ1bm5pbmcgc2NoZW1hdGljOgoKIVtJbWFnZTogU2NoZW1hdGljIGFmdGVyIFNDVHJhbnNmb3JtKCkuXShpbWFnZXMvc2V1cmF0X3NjaGVtYXRpYy9TbGlkZTUucG5nKQoKTm90ZSB0aGF0IHdlIGhhdmUgYSBuZXcgYXNzYXksIGBTQ1RgIHdoaWNoIGhhcyB0aHJlZSBsYXllcnM6IGBjb3VudHNgLCBgZGF0YWAsIGFuZCBgc2NhbGUuZGF0YWAuIEFsc28gbm90ZSBhZGRpdGlvbmFsIGNvbHVtbnMgaW4gYG1ldGEuZGF0YWAgYW5kIHRoZSBjaGFuZ2Ugb2YgdGhlIGBhY3RpdmUuYXNzYXlgIHRvIGBTQ1RgLiBgU0NUcmFuc2Zvcm0oKWAgaGFzIGFsc28gZGV0ZXJtaW5lZCB0aGUgY29tbW9uIHZhcmlhYmxlIGZlYXR1cmVzIGFjcm9zcyB0aGUgY2VsbHMgdG8gYmUgdXNlZCBpbiBvdXIgZG93bnN0cmVhbSBhbmFseXNpcy4gVmlld2VkIGluIG91ciBydW5uaW5nIHNjaGVtYXRpYzoKCiMgU2F2ZSBvdXIgcHJvZ3Jlc3MKCkxldCdzIHNhdmUgdGhpcyBub3JtYWxpemVkIGZvcm0gb2Ygb3VyIFNldXJhdCBvYmplY3QuCgpgYGB7ciwgc2F2ZV9yZHNfaGlkZGVuLCBlY2hvID0gRkFMU0V9CmlmKCFmaWxlLmV4aXN0cygncmVzdWx0cy9yZGF0YS9nZW9fc29fc2N0X25vcm1hbGl6ZWQucmRzJykpIHsKICBzYXZlUkRTKGdlb19zbywgZmlsZSA9ICdyZXN1bHRzL3JkYXRhL2dlb19zb19zY3Rfbm9ybWFsaXplZC5yZHMnKQp9CmBgYAoKYGBge3IsIHNhdmVfcmRzLCBldmFsID0gRkFMU0V9CiMgU2F2ZSBTZXVyYXQgb2JqZWN0ICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpzYXZlUkRTKGdlb19zbywgZmlsZSA9ICdyZXN1bHRzL3JkYXRhL2dlb19zb19zY3Rfbm9ybWFsaXplZC5yZHMnKQpgYGAKCiMgU3VtbWFyeQoKSW4gdGhpcyBzZWN0aW9uIHdlIGhhdmUgcnVuIHRoZSBgU0NUcmFuc2Zvcm0oKWAgZnVuY3Rpb24gdG8gYWNjb3VudCBmb3IgdmFyaWF0aW9uIGluIGNlbGwgc2VxdWVuY2luZyBkZXB0aCBhbmQgdG8gc3RhYmlsaXplIHRoZSB2YXJpYW5jZSBvZiB0aGUgY291bnRzLiAKCk5leHQgc3RlcHM6IFBDQSBhbmQgaW50ZWdyYXRpb24KCi0tLS0KClRoZXNlIG1hdGVyaWFscyBoYXZlIGJlZW4gYWRhcHRlZCBhbmQgZXh0ZW5kZWQgZnJvbSBtYXRlcmlhbHMgbGlzdGVkIGFib3ZlLiBUaGVzZSBhcmUgb3BlbiBhY2Nlc3MgbWF0ZXJpYWxzIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgW0NyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24gbGljZW5zZSAoQ0MgQlkgNC4wKV0oaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvNC4wLyksIHdoaWNoIHBlcm1pdHMgdW5yZXN0cmljdGVkIHVzZSwgZGlzdHJpYnV0aW9uLCBhbmQgcmVwcm9kdWN0aW9uIGluIGFueSBtZWRpdW0sIHByb3ZpZGVkIHRoZSBvcmlnaW5hbCBhdXRob3IgYW5kIHNvdXJjZSBhcmUgY3JlZGl0ZWQuCgo8YnIvPgo8YnIvPgo8aHIvPgp8IFtQcmV2aW91cyBsZXNzb25dKDAyLVFDYW5kRmlsdGVyaW5nLmh0bWwpIHwgW1RvcCBvZiB0aGlzIGxlc3Nvbl0oI3RvcCkgfCBbTmV4dCBsZXNzb25dKDA0LVBDQWFuZEludGVncmF0aW9uLmh0bWwpIHwKfCA6LS0tIHwgOi0tLS06IHwgLS0tOiB8Cg==