In this module, we will learn:

  • How to ‘unblind’ our samples
  • How to ensure high quality count data with filtering


Differential Expression Workflow

Here we will setup the inputs needed to initialize DESeq2 before testing for differential expression.


Sample Information

As introduced at the beginning of the workshop, we have downloaded and prepared data from an existing publication (Zhang et al., 2019), wherein one goal is to understand the gene expression differences in wild-type mice that that were “iron replete” and fed a control diet (we label these “control”) and “iron deficient” (we label these “deficient”).

Read Sample Table

Our next step will be to describe the samples within our R session, so that we make the proper comparisons with DESeq2. Let’s check the sample names from the count table.

colnames(count_table)
[1] "sample_A" "sample_B" "sample_C" "sample_D" "sample_E" "sample_F"

From the columns of count_table, we can see our samples are blinded, e.g. the sample names don’t clearly correspond to treatment groups. We will need to specify which sample IDs connect to which experimental conditions.

Typically sample phenotype data, including experimental conditions, are stored as Excel or CSV files that we can read into R, and then use when creating a DESeq2 object. If you are unfamilar with CSV files or how to generate them, there are tutorials available to guide you through the process.

Tip: Sample naming conventions

Use only alpha-numeric characters (A-Z, a-z, 0-9), and separate parts of the name with underscores (_) or dots (.). Do not begin sample names with numbers.

We’ll load our ‘pre-made’ sample information sheet, samplesheet_batch.csv, to unblind our samples.

samplesheet_batch = read.table("data/samplesheet_batch.csv",
                       sep = ",",
                       header = TRUE,
                       row.names = 1)

Alternatively, we could manually create a sample sheet, ensuring that the sample IDs match our count table.

We can look at our sample sheet information with head().

head(samplesheet_batch)
         genotype condition batch
sample_A       wt   control  Day1
sample_B       wt   control  Day2
sample_C       wt   control  Day2
sample_D       wt deficient  Day1
sample_E       wt deficient  Day2
sample_F       wt deficient  Day2

Checkpoint: If you have loaded samplesheet, please indicate with the green ‘check’ button. Otherwise, please use the red ‘x’ button to have the command repeated

In this example data, all mice are of a wild-type genotype with three samples from control diet mice (labeled as “control”) and three samples from iron deficient diet fed mice (labeled as “deficient”). Again, for larger experiments, you may want to examine the coding of the samples and how many group labels are present using the unique() function and the $ operator to specify the relevant column.

unique(samplesheet_batch$condition)
[1] "control"   "deficient"

Replicates in RNA-seq experiments

Question In this experiment, we have samples from three mice per each condition. Do we have enough replicates?

The goal of our analysis is to separate the “interesting” biological contributions from the “uninteresting” technical or other contributions that either cannot be or were not controlled in the experimental design. The more sources of variation, such as samples coming from heterogenous tissues or experiments with incomplete knockdowns, the more replicates (>3) are recommended.

Image of technical, biological, and experimental contributors to gene expression, from HBC training materials
Image of technical, biological, and experimental contributors to gene expression, from HBC training materials

For a more in depth discussion of experimental design considerations, particularly for the number of replicates, please read A Beginner’s Guide to Analysis of RNA Sequencing Data or papers like this one by Hart et al that focus on estimating statistical power for RNA-seq experiments.

Sample table formatting

Next, we’ll format our table so that we have the appropriate data type (an ordered factor) for DESeq2 to recognize our treatment groups and appropriately compare samples.

# tidy version
samplesheet_ready <- samplesheet_batch %>% 
  mutate(condition = factor(condition, levels = c('control', 'deficient')),
         batch = factor(batch, levels = c('Day1', 'Day2')))

unique(samplesheet_ready$condition)
[1] control   deficient
Levels: control deficient

Notice that we set “control” as the first level as the factor for the condition column. This ensures that the “control” (or “reference”) group is the denominator in the default comparisons in our DESeq2 model.

Before we proceed, we need to make sure that the sample labels (column names) in the count table match the sample information table (row names), including the order. If the sample labels don’t match, then we will see an error and need to correct the labels prior to proceeding. Checking the sample information table is extremely important to ensure that the correct samples are grouped together for comparisons.

all(colnames(count_table) == rownames(samplesheet_ready))
[1] TRUE

This line of code checks if both the identity and order match between our count_table and our samplesheet. If, in the course of using your own data, this returns FALSE, try using the match() function to rearrange the columns of count_table (or the rows of samplesheet) to get them to match.

Checkpoint: If you your sample info check returns TRUE, please indicate with the green ‘yes’ button. Otherwise, please use the red ‘x’ button to have the command repeated

Creating DESeq2 object

Bioconductor software packages often define and use custom structures to store data in a way that suits biological data, such as illustrated below from Huber et al. 2015.

A breakdown of the SummarizedExperiment class.
A breakdown of the SummarizedExperiment class.

These custom data structures have pre-defined data slots, which hold specific types/classes of data and therefore can be more easily accessed by functions from the same package.

To create the DESeqDataSet we need two important inputs:

  1. A table that assigns the condition labels for each sample (that we read in and formatted)
  2. A raw count matrix

Is our count matrix from our RSEM outputs considered a “raw” count matrix by DESeq2?

If we think back to the RSEM outputs, the ‘expected_counts’ table may include fractional amounts due to how the alignment tool resolves reads mapping to multiple locuses). However, recent versions of DESeq2 handle this without rounding.

In addition to the count_table and the samplesheet_ready. We will also need a design formula to specify our model.

Making model choices

We’ll talk about the design formula more later but for now it’s important to understand that the design specifies the relevant column(s) in the metadata table and how they should be used when fitting a model for our data.

For our dataset we have one experimental condition column, which is named condition. Although the design formula is required, it is not used at this step.

## Create DESeq object, line by line
dds = DESeqDataSetFromMatrix(countData = count_table,
                              colData = samplesheet_ready,
                              design = ~ condition)
dds
class: DESeqDataSet 
dim: 55492 6 
metadata(1): version
assays(1): counts
rownames(55492): ENSMUSG00000000001 ENSMUSG00000000003 ... mcherry
  tdtomato
rowData names(0):
colnames(6): sample_A sample_B ... sample_E sample_F
colData names(3): genotype condition batch

Notice that printing the dds object helpfully shows us some helpful information:

  • The dimension (number of genes by number of samples),
  • The gene identifiers,
  • The sample identifiers,
  • The additional column names giving information about the samples

Checkpoint: If you see dds in your environment panel, please indicate with the green ‘check’ button. Otherwise, please use use the red ‘x’ button in your zoom reaction panel to have this step repeated. You can use the red ‘x’ to be put in a breakout room for help


Pre-filtering

While not necessary, pre-filtering helps to not only reduce the size of the DESeq2 object, but also gives you a sense of how many genes were reasonably measured for your samples.

Here we will filter out any genes that have less than 10 counts across any of the samples. This is a fairly standard level of filtering, but the data could be filtered more less or more depending on quality control metrics from alignments and sequencing depth or total number of samples. Since the DESeq2 object isn’t compatible with tidyverse functions, we’ll take a different approach:

keep = rowSums(counts(dds)) >= 10
dds_filtered = dds[keep,]
dds_filtered
class: DESeqDataSet 
dim: 16249 6 
metadata(1): version
assays(1): counts
rownames(16249): ENSMUSG00000000001 ENSMUSG00000000028 ...
  ENSMUSG00000118651 ENSMUSG00000118653
rowData names(0):
colnames(6): sample_A sample_B ... sample_E sample_F
colData names(3): genotype condition batch

Notice the dds_filtered object has less elements than the unfiltered dds object, indicating that a number of genes were not measured in our experiment.

Checkpoint: Questions?

Summary

In this section, we:

  • Loaded the necessary input files into our R session
  • Discussed the importance of biological replicates
  • Initialized a DESeq2 data set
  • Filtered our data to remove genes that were poorly measured

Now that we’ve created our DESeq2 objects, including specifying what model is appropriate for our data, and filtered our data, we can proceed with assessing the impact of the experimental conditions on gene expression for our samples.


Sources

Training resources used to develop materials:


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
LS0tCnRpdGxlOiAiREUgSW5pdGlhbGl6YXRpb24iCmF1dGhvcjogIlVNIEJpb2luZm9ybWF0aWNzIENvcmUiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogICAgICAgIGh0bWxfZG9jdW1lbnQ6CiAgICAgICAgICAgIGluY2x1ZGVzOgogICAgICAgICAgICAgICAgaW5faGVhZGVyOiBoZWFkZXIuaHRtbAogICAgICAgICAgICB0aGVtZTogcGFwZXIKICAgICAgICAgICAgdG9jOiB0cnVlCiAgICAgICAgICAgIHRvY19kZXB0aDogNAogICAgICAgICAgICB0b2NfZmxvYXQ6IHRydWUKICAgICAgICAgICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgICAgICAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgICAgICAgICBtYXJrZG93bjogR0ZNCiAgICAgICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpib2R5LCB0ZCB7CiAgIGZvbnQtc2l6ZTogMThweDsKfQpjb2RlLnJ7CiAgZm9udC1zaXplOiAxMnB4Owp9CnByZSB7CiAgZm9udC1zaXplOiAxMnB4Cn0KPC9zdHlsZT4KCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnNvdXJjZSgiLi4vYmluL2NodW5rLW9wdGlvbnMuUiIpCmtuaXRyX2ZpZ19wYXRoKCIwNy0iKQpgYGAKCmBgYHtyIExvYWRSdW5uaW5nRGF0YSwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KERFU2VxMikKIyBsb2FkKCJyZGF0YS9SdW5uaW5nRGF0YS5SRGF0YSIpCmBgYAoKSW4gdGhpcyBtb2R1bGUsIHdlIHdpbGwgbGVhcm46CgoqIEhvdyB0byAndW5ibGluZCcgb3VyIHNhbXBsZXMKKiBIb3cgdG8gZW5zdXJlIGhpZ2ggcXVhbGl0eSBjb3VudCBkYXRhIHdpdGggZmlsdGVyaW5nCgo8YnI+CgojIERpZmZlcmVudGlhbCBFeHByZXNzaW9uIFdvcmtmbG93IHsudW5saXN0ZWQgLnVubnVtYmVyZWR9CgpIZXJlIHdlIHdpbGwgc2V0dXAgdGhlIGlucHV0cyBuZWVkZWQgdG8gaW5pdGlhbGl6ZSBERVNlcTIgYmVmb3JlIHRlc3RpbmcgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uLgoKIVtdKC4vaW1hZ2VzL3dheWZpbmRlci93YXlmaW5kZXItREVTZXEySW5pdC5wbmcpe3dpZHRoPTc1JX0KCi0tLQoKIyBTYW1wbGUgSW5mb3JtYXRpb24KCkFzIGludHJvZHVjZWQgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgd29ya3Nob3AsIHdlIGhhdmUgZG93bmxvYWRlZCBhbmQgcHJlcGFyZWQgZGF0YSBmcm9tIGFuIGV4aXN0aW5nIHB1YmxpY2F0aW9uIFsoWmhhbmcgZXQgYWwuLCAyMDE5KV0oaHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy80Njk3NiksIHdoZXJlaW4gb25lIGdvYWwgaXMgdG8gdW5kZXJzdGFuZCB0aGUgZ2VuZSBleHByZXNzaW9uIGRpZmZlcmVuY2VzIGluIHdpbGQtdHlwZSBtaWNlIHRoYXQgdGhhdCB3ZXJlICJpcm9uIHJlcGxldGUiIGFuZCBmZWQgYSBjb250cm9sIGRpZXQgKHdlIGxhYmVsIHRoZXNlICJjb250cm9sIikgYW5kICJpcm9uIGRlZmljaWVudCIgKHdlIGxhYmVsIHRoZXNlICJkZWZpY2llbnQiKS4KCiFbXShpbWFnZXMvTW9kdWxlMDdfdG9wX2Rvd25fZXhwZXJpbWVudC5wbmcpCgojIyBSZWFkIFNhbXBsZSBUYWJsZQoKT3VyIG5leHQgc3RlcCB3aWxsIGJlIHRvIGRlc2NyaWJlIHRoZSBzYW1wbGVzIHdpdGhpbiBvdXIgUiBzZXNzaW9uLCBzbyB0aGF0IHdlIG1ha2UgdGhlIHByb3BlciBjb21wYXJpc29ucyB3aXRoIERFU2VxMi4gTGV0J3MgY2hlY2sgdGhlIHNhbXBsZSBuYW1lcyBmcm9tIHRoZSBjb3VudCB0YWJsZS4KCmBgYHtyIENvbHVtbk5hbWVzfQpjb2xuYW1lcyhjb3VudF90YWJsZSkKYGBgCgpGcm9tIHRoZSBjb2x1bW5zIG9mIGBjb3VudF90YWJsZWAsIHdlIGNhbiBzZWUgb3VyIHNhbXBsZXMgYXJlIGJsaW5kZWQsIGUuZy4gdGhlIHNhbXBsZSBuYW1lcyBkb24ndCBjbGVhcmx5IGNvcnJlc3BvbmQgdG8gdHJlYXRtZW50IGdyb3Vwcy4gV2Ugd2lsbCBuZWVkIHRvIHNwZWNpZnkgd2hpY2ggc2FtcGxlIElEcyBjb25uZWN0IHRvIHdoaWNoIGV4cGVyaW1lbnRhbCBjb25kaXRpb25zLgoKVHlwaWNhbGx5IHNhbXBsZSBwaGVub3R5cGUgZGF0YSwgaW5jbHVkaW5nIGV4cGVyaW1lbnRhbCBjb25kaXRpb25zLCBhcmUgc3RvcmVkIGFzIEV4Y2VsIG9yIENTViBmaWxlcyB0aGF0IHdlIGNhbiByZWFkIGludG8gUiwgYW5kIHRoZW4gdXNlIHdoZW4gY3JlYXRpbmcgYSBERVNlcTIgb2JqZWN0LiBJZiB5b3UgYXJlIHVuZmFtaWxhciB3aXRoIENTViBmaWxlcyBvciBob3cgdG8gZ2VuZXJhdGUgdGhlbSwgdGhlcmUgYXJlIFt0dXRvcmlhbHNdKGh0dHBzOi8vd3d3Lndpa2lob3cuY29tL0NyZWF0ZS1hLUNTVi1GaWxlKSBhdmFpbGFibGUgdG8gZ3VpZGUgeW91IHRocm91Z2ggdGhlIHByb2Nlc3MuCgo+ICMgVGlwOiBTYW1wbGUgbmFtaW5nIGNvbnZlbnRpb25zIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cj4gVXNlIG9ubHkgYWxwaGEtbnVtZXJpYyBjaGFyYWN0ZXJzIChBLVosIGEteiwgMC05KSwgYW5kIHNlcGFyYXRlIHBhcnRzIG9mIHRoZSBuYW1lIHdpdGggdW5kZXJzY29yZXMgKGBfYCkgb3IgZG90cyAoYC5gKS4gRG8gbm90IGJlZ2luIHNhbXBsZSBuYW1lcyB3aXRoIG51bWJlcnMuCgpXZSdsbCBsb2FkIG91ciAncHJlLW1hZGUnIHNhbXBsZSBpbmZvcm1hdGlvbiBzaGVldCwgYHNhbXBsZXNoZWV0X2JhdGNoLmNzdmAsIHRvIHVuYmxpbmQgb3VyIHNhbXBsZXMuCgpgYGB7ciBTYW1wbGVzaGVldCwgZWNobyA9IEZBTFNFLCBldmFsID0gVFJVRX0Kc2FtcGxlc2hlZXRfYmF0Y2ggPSByZWFkLnRhYmxlKCIuLi9kYXRhL1JfZGF0YS9zYW1wbGVzaGVldF9iYXRjaC5jc3YiLAogICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIsIiwKICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEpCmBgYAoKYGBge3IgU2FtcGxlc2hlZXQyLCBldmFsID0gRkFMU0V9CnNhbXBsZXNoZWV0X2JhdGNoID0gcmVhZC50YWJsZSgiZGF0YS9zYW1wbGVzaGVldF9iYXRjaC5jc3YiLAogICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIsIiwKICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEpCmBgYAoKQWx0ZXJuYXRpdmVseSwgd2UgY291bGQgbWFudWFsbHkgY3JlYXRlIGEgc2FtcGxlIHNoZWV0LCBlbnN1cmluZyB0aGF0IHRoZSBzYW1wbGUgSURzIG1hdGNoIG91ciBjb3VudCB0YWJsZS4KCldlIGNhbiBsb29rIGF0IG91ciBzYW1wbGUgc2hlZXQgaW5mb3JtYXRpb24gd2l0aCBgaGVhZCgpYC4KCmBgYHtyIFNob3dTYW1wbGVzaGVldH0KaGVhZChzYW1wbGVzaGVldF9iYXRjaCkKYGBgCgoqKkNoZWNrcG9pbnQqKjogKklmIHlvdSBoYXZlIGxvYWRlZCBgc2FtcGxlc2hlZXRgLCBwbGVhc2UgaW5kaWNhdGUgd2l0aCB0aGUgZ3JlZW4gJ2NoZWNrJyBidXR0b24uIE90aGVyd2lzZSwgcGxlYXNlIHVzZSB0aGUgcmVkICd4JyBidXR0b24gdG8gaGF2ZSB0aGUgY29tbWFuZCByZXBlYXRlZCoKCgpJbiB0aGlzIGV4YW1wbGUgZGF0YSwgYWxsIG1pY2UgYXJlIG9mIGEgd2lsZC10eXBlIGdlbm90eXBlIHdpdGggdGhyZWUgc2FtcGxlcyBmcm9tIGNvbnRyb2wgZGlldCBtaWNlIChsYWJlbGVkIGFzICJjb250cm9sIikgYW5kIHRocmVlIHNhbXBsZXMgZnJvbSBpcm9uIGRlZmljaWVudCBkaWV0IGZlZCBtaWNlIChsYWJlbGVkIGFzICJkZWZpY2llbnQiKS4gQWdhaW4sIGZvciBsYXJnZXIgZXhwZXJpbWVudHMsIHlvdSBtYXkgd2FudCB0byBleGFtaW5lIHRoZSBjb2Rpbmcgb2YgdGhlIHNhbXBsZXMgYW5kIGhvdyBtYW55IGdyb3VwIGxhYmVscyBhcmUgcHJlc2VudCB1c2luZyB0aGUgYHVuaXF1ZSgpYCBmdW5jdGlvbiBhbmQgdGhlIGAkYCBvcGVyYXRvciB0byBzcGVjaWZ5IHRoZSByZWxldmFudCBjb2x1bW4uCgpgYGB7ciBUcmVhdG1lbnRHcm91cFRhYmxlfQp1bmlxdWUoc2FtcGxlc2hlZXRfYmF0Y2gkY29uZGl0aW9uKQpgYGAKCgojIyMgUmVwbGljYXRlcyBpbiBSTkEtc2VxIGV4cGVyaW1lbnRzCgo+ICoqUXVlc3Rpb24qKiBJbiB0aGlzIGV4cGVyaW1lbnQsIHdlIGhhdmUgc2FtcGxlcyBmcm9tIHRocmVlIG1pY2UgcGVyIGVhY2ggY29uZGl0aW9uLiBEbyB3ZSBoYXZlIGVub3VnaCByZXBsaWNhdGVzPwoKVGhlIGdvYWwgb2Ygb3VyIGFuYWx5c2lzIGlzIHRvIHNlcGFyYXRlIHRoZSDigJxpbnRlcmVzdGluZ+KAnSBiaW9sb2dpY2FsIGNvbnRyaWJ1dGlvbnMgZnJvbSB0aGUg4oCcdW5pbnRlcmVzdGluZ+KAnSB0ZWNobmljYWwgb3Igb3RoZXIgY29udHJpYnV0aW9ucyB0aGF0IGVpdGhlciBjYW5ub3QgYmUgb3Igd2VyZSBub3QgY29udHJvbGxlZCBpbiB0aGUgZXhwZXJpbWVudGFsIGRlc2lnbi4gVGhlIG1vcmUgc291cmNlcyBvZiB2YXJpYXRpb24sIHN1Y2ggYXMgc2FtcGxlcyBjb21pbmcgZnJvbSBoZXRlcm9nZW5vdXMgdGlzc3VlcyBvciBleHBlcmltZW50cyB3aXRoIGluY29tcGxldGUga25vY2tkb3ducywgdGhlIG1vcmUgcmVwbGljYXRlcyAoPjMpIGFyZSByZWNvbW1lbmRlZC4KCiFbSW1hZ2Ugb2YgdGVjaG5pY2FsLCBiaW9sb2dpY2FsLCBhbmQgZXhwZXJpbWVudGFsIGNvbnRyaWJ1dG9ycyB0byBnZW5lIGV4cHJlc3Npb24sIGZyb20gSEJDIHRyYWluaW5nIG1hdGVyaWFsc10oaW1hZ2VzL2RlX3ZhcmlhdGlvbi5wbmcpe3dpZHRoPTc1JX0KCkZvciBhIG1vcmUgaW4gZGVwdGggZGlzY3Vzc2lvbiBvZiBleHBlcmltZW50YWwgZGVzaWduIGNvbnNpZGVyYXRpb25zLCBwYXJ0aWN1bGFybHkgZm9yIHRoZSBudW1iZXIgb2YgcmVwbGljYXRlcywgcGxlYXNlIHJlYWQgW0EgQmVnaW5uZXLigJlzIEd1aWRlIHRvIEFuYWx5c2lzIG9mIFJOQSBTZXF1ZW5jaW5nIERhdGFdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzYwOTYzNDYvKSBvciBwYXBlcnMgbGlrZSB0aGlzIG9uZSBieSBbSGFydCBldCBhbF0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DMzg0Mjg4NC8pIHRoYXQgZm9jdXMgb24gZXN0aW1hdGluZyBzdGF0aXN0aWNhbCBwb3dlciBmb3IgUk5BLXNlcSBleHBlcmltZW50cy4KCgojIyMgU2FtcGxlIHRhYmxlIGZvcm1hdHRpbmcKCk5leHQsIHdlJ2xsIGZvcm1hdCBvdXIgdGFibGUgc28gdGhhdCB3ZSBoYXZlIHRoZSBhcHByb3ByaWF0ZSBkYXRhIHR5cGUgKGFuIG9yZGVyZWQgW2ZhY3Rvcl0oaHR0cHM6Ly9zd2NhcnBlbnRyeS5naXRodWIuaW8vci1ub3ZpY2UtaW5mbGFtbWF0aW9uLzEyLXN1cHAtZmFjdG9ycy8pKSBmb3IgREVTZXEyIHRvIHJlY29nbml6ZSBvdXIgdHJlYXRtZW50IGdyb3VwcyBhbmQgYXBwcm9wcmlhdGVseSBjb21wYXJlIHNhbXBsZXMuCgpgYGB7ciBTYW1wbGVzaGVldEZhY3Rvcn0KIyB0aWR5IHZlcnNpb24Kc2FtcGxlc2hlZXRfcmVhZHkgPC0gc2FtcGxlc2hlZXRfYmF0Y2ggJT4lIAogIG11dGF0ZShjb25kaXRpb24gPSBmYWN0b3IoY29uZGl0aW9uLCBsZXZlbHMgPSBjKCdjb250cm9sJywgJ2RlZmljaWVudCcpKSwKICAgICAgICAgYmF0Y2ggPSBmYWN0b3IoYmF0Y2gsIGxldmVscyA9IGMoJ0RheTEnLCAnRGF5MicpKSkKCnVuaXF1ZShzYW1wbGVzaGVldF9yZWFkeSRjb25kaXRpb24pCmBgYAoKPCEtLS0KYGBge3IgZWNobz1GQUxTRSwgZXZhbD1GQUxTRX0KIyBiYXNlIHZlcnNpb24gLSBhZGQgYXMgZHJvcGRvd24gb3B0aW9uIGluc3RlYWQgb2YgbWFpbiBjb250ZW50CnNhbXBsZXNoZWV0X3JlYWR5IDwtIHNhbXBsZXNoZWV0CnNhbXBsZXNoZWV0X3JlYWR5JGNvbmRpdGlvbiA9IGZhY3RvcihzYW1wbGVzaGVldF9yZWFkeSRjb25kaXRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCdjb250cm9sJywgJ2RlZmljaWVudCcpKQpzYW1wbGVzaGVldF9yZWFkeSRiYXRjaCA9IGZhY3RvcihzYW1wbGVzaGVldF9yZWFkeSRiYXRjaCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoJ0RheTEnLCAnRGF5MicpKQpgYGAKLS0+CgpOb3RpY2UgdGhhdCB3ZSBzZXQgImNvbnRyb2wiIGFzIHRoZSBmaXJzdCBsZXZlbCBhcyB0aGUgZmFjdG9yIGZvciB0aGUgYGNvbmRpdGlvbmAgY29sdW1uLiBUaGlzIGVuc3VyZXMgdGhhdCB0aGUgImNvbnRyb2wiIChvciAicmVmZXJlbmNlIikgZ3JvdXAgaXMgdGhlIGRlbm9taW5hdG9yIGluIHRoZSBkZWZhdWx0IGNvbXBhcmlzb25zIGluIG91ciBERVNlcTIgbW9kZWwuCgpCZWZvcmUgd2UgcHJvY2VlZCwgd2UgbmVlZCB0byBtYWtlIHN1cmUgdGhhdCB0aGUgc2FtcGxlIGxhYmVscyAoY29sdW1uIG5hbWVzKSBpbiB0aGUgY291bnQgdGFibGUgbWF0Y2ggdGhlIHNhbXBsZSBpbmZvcm1hdGlvbiB0YWJsZSAocm93IG5hbWVzKSwgaW5jbHVkaW5nIHRoZSBvcmRlci4gSWYgdGhlIHNhbXBsZSBsYWJlbHMgZG9uJ3QgbWF0Y2gsIHRoZW4gd2Ugd2lsbCBzZWUgYW4gZXJyb3IgYW5kIG5lZWQgdG8gY29ycmVjdCB0aGUgbGFiZWxzIHByaW9yIHRvIHByb2NlZWRpbmcuIENoZWNraW5nIHRoZSBzYW1wbGUgaW5mb3JtYXRpb24gdGFibGUgaXMgZXh0cmVtZWx5IGltcG9ydGFudCB0byBlbnN1cmUgdGhhdCB0aGUgY29ycmVjdCBzYW1wbGVzIGFyZSBncm91cGVkIHRvZ2V0aGVyIGZvciBjb21wYXJpc29ucy4KCmBgYHtyIENoZWNrU2FtcGxlc2hlZXR9CmFsbChjb2xuYW1lcyhjb3VudF90YWJsZSkgPT0gcm93bmFtZXMoc2FtcGxlc2hlZXRfcmVhZHkpKQpgYGAKVGhpcyBsaW5lIG9mIGNvZGUgY2hlY2tzIGlmIGJvdGggdGhlIGlkZW50aXR5IGFuZCBvcmRlciBtYXRjaCBiZXR3ZWVuIG91ciBgY291bnRfdGFibGVgIGFuZCBvdXIgYHNhbXBsZXNoZWV0YC4gSWYsIGluIHRoZSBjb3Vyc2Ugb2YgdXNpbmcgeW91ciBvd24gZGF0YSwgdGhpcyByZXR1cm5zIGBGQUxTRWAsIHRyeSB1c2luZyB0aGUgYG1hdGNoKClgIGZ1bmN0aW9uIHRvIHJlYXJyYW5nZSB0aGUgY29sdW1ucyBvZiBgY291bnRfdGFibGVgIChvciB0aGUgcm93cyBvZiBgc2FtcGxlc2hlZXRgKSB0byBnZXQgdGhlbSB0byBtYXRjaC4KCioqQ2hlY2twb2ludCoqOiAqSWYgeW91IHlvdXIgc2FtcGxlIGluZm8gY2hlY2sgcmV0dXJucyBgVFJVRWAsIHBsZWFzZSBpbmRpY2F0ZSB3aXRoIHRoZSBncmVlbiAneWVzJyBidXR0b24uIE90aGVyd2lzZSwgcGxlYXNlIHVzZSB0aGUgcmVkICd4JyBidXR0b24gdG8gaGF2ZSB0aGUgY29tbWFuZCByZXBlYXRlZCoKCiMgQ3JlYXRpbmcgREVTZXEyIG9iamVjdAoKQmlvY29uZHVjdG9yIHNvZnR3YXJlIHBhY2thZ2VzIG9mdGVuIGRlZmluZSBhbmQgdXNlIGN1c3RvbSBzdHJ1Y3R1cmVzIHRvIHN0b3JlIGRhdGEgaW4gYSB3YXkgdGhhdCBzdWl0cyBiaW9sb2dpY2FsIGRhdGEsIHN1Y2ggYXMgaWxsdXN0cmF0ZWQgYmVsb3cgZnJvbSBbSHViZXIgZXQgYWwuIDIwMTVdKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvbm1ldGguMzI1MikuCgohW0EgYnJlYWtkb3duIG9mIHRoZSBTdW1tYXJpemVkRXhwZXJpbWVudCBjbGFzcy5dKC4vaW1hZ2VzL1N1bW1hcml6ZWRFeHBlcmltZW50LmpwZykKClRoZXNlIGN1c3RvbSBkYXRhIHN0cnVjdHVyZXMgaGF2ZSBwcmUtZGVmaW5lZCBkYXRhIHNsb3RzLCB3aGljaCBob2xkIHNwZWNpZmljIHR5cGVzL2NsYXNzZXMgb2YgZGF0YSBhbmQgdGhlcmVmb3JlIGNhbiBiZSBtb3JlIGVhc2lseSBhY2Nlc3NlZCBieSBmdW5jdGlvbnMgZnJvbSB0aGUgc2FtZSBwYWNrYWdlLgoKVG8gY3JlYXRlIHRoZSBERVNlcURhdGFTZXQgd2UgbmVlZCB0d28gaW1wb3J0YW50IGlucHV0czoKCjEuIEEgdGFibGUgdGhhdCBhc3NpZ25zIHRoZSBjb25kaXRpb24gbGFiZWxzIGZvciBlYWNoIHNhbXBsZSAodGhhdCB3ZSByZWFkIGluIGFuZCBmb3JtYXR0ZWQpCjEuIEEgKnJhdyogY291bnQgbWF0cml4CgpJcyBvdXIgY291bnQgbWF0cml4IGZyb20gb3VyIFJTRU0gb3V0cHV0cyBjb25zaWRlcmVkIGEgInJhdyIgY291bnQgbWF0cml4IGJ5IERFU2VxMj8KCj4gSWYgd2UgdGhpbmsgYmFjayB0byB0aGUgUlNFTSBvdXRwdXRzLCB0aGUgJ2V4cGVjdGVkX2NvdW50cycgdGFibGUgbWF5IGluY2x1ZGUgZnJhY3Rpb25hbCBhbW91bnRzIGR1ZSB0byBob3cgdGhlIGFsaWdubWVudCB0b29sIHJlc29sdmVzIHJlYWRzIG1hcHBpbmcgdG8gbXVsdGlwbGUgbG9jdXNlcykuIEhvd2V2ZXIsIHJlY2VudCB2ZXJzaW9ucyBvZiBERVNlcTIgaGFuZGxlIHRoaXMgd2l0aG91dCByb3VuZGluZy4KCgpJbiBhZGRpdGlvbiB0byB0aGUgYGNvdW50X3RhYmxlYCBhbmQgdGhlIGBzYW1wbGVzaGVldF9yZWFkeWAuIFdlIHdpbGwgYWxzbyBuZWVkIGEgKipkZXNpZ24gZm9ybXVsYSoqIHRvIHNwZWNpZnkgb3VyIG1vZGVsLgoKIyMgTWFraW5nIG1vZGVsIGNob2ljZXMKCldlJ2xsIHRhbGsgYWJvdXQgdGhlIGRlc2lnbiBmb3JtdWxhIG1vcmUgbGF0ZXIgYnV0IGZvciBub3cgaXQncyBpbXBvcnRhbnQgdG8gdW5kZXJzdGFuZCB0aGF0IHRoZSBkZXNpZ24gc3BlY2lmaWVzIHRoZSByZWxldmFudCBjb2x1bW4ocykgaW4gdGhlIG1ldGFkYXRhIHRhYmxlIGFuZCBob3cgdGhleSBzaG91bGQgYmUgdXNlZCB3aGVuIGZpdHRpbmcgYSBtb2RlbCBmb3Igb3VyIGRhdGEuIAoKRm9yIG91ciBkYXRhc2V0IHdlIGhhdmUgb25lIGV4cGVyaW1lbnRhbCBjb25kaXRpb24gY29sdW1uLCB3aGljaCBpcyBuYW1lZCBgY29uZGl0aW9uYC4gQWx0aG91Z2ggdGhlIGRlc2lnbiBmb3JtdWxhIGlzIHJlcXVpcmVkLCBpdCBpcyBub3QgdXNlZCBhdCB0aGlzIHN0ZXAuCgpgYGB7ciBERVNlcTJPYmplY3R9CiMjIENyZWF0ZSBERVNlcSBvYmplY3QsIGxpbmUgYnkgbGluZQpkZHMgPSBERVNlcURhdGFTZXRGcm9tTWF0cml4KGNvdW50RGF0YSA9IGNvdW50X3RhYmxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gc2FtcGxlc2hlZXRfcmVhZHksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH4gY29uZGl0aW9uKQpkZHMKYGBgCgpOb3RpY2UgdGhhdCBwcmludGluZyB0aGUgYGRkc2Agb2JqZWN0IGhlbHBmdWxseSBzaG93cyB1cyBzb21lIGhlbHBmdWwgaW5mb3JtYXRpb246CgoqIFRoZSBkaW1lbnNpb24gKG51bWJlciBvZiBnZW5lcyBieSBudW1iZXIgb2Ygc2FtcGxlcyksCiogVGhlIGdlbmUgaWRlbnRpZmllcnMsCiogVGhlIHNhbXBsZSBpZGVudGlmaWVycywKKiBUaGUgYWRkaXRpb25hbCBjb2x1bW4gbmFtZXMgZ2l2aW5nIGluZm9ybWF0aW9uIGFib3V0IHRoZSBzYW1wbGVzCgoqKkNoZWNrcG9pbnQqKjogKklmIHlvdSBzZWUgYGRkc2AgaW4geW91ciBlbnZpcm9ubWVudCBwYW5lbCwgcGxlYXNlIGluZGljYXRlIHdpdGggdGhlIGdyZWVuICdjaGVjaycgYnV0dG9uLiBPdGhlcndpc2UsIHBsZWFzZSB1c2UgIHVzZSB0aGUgcmVkICd4JyBidXR0b24gaW4geW91ciB6b29tIHJlYWN0aW9uIHBhbmVsIHRvIGhhdmUgdGhpcyBzdGVwIHJlcGVhdGVkLiBZb3UgY2FuIHVzZSB0aGUgcmVkICd4JyB0byBiZSBwdXQgaW4gYSBicmVha291dCByb29tIGZvciBoZWxwKgoKLS0tCgoKIyBQcmUtZmlsdGVyaW5nCgpXaGlsZSBub3QgbmVjZXNzYXJ5LCBbcHJlLWZpbHRlcmluZ10oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI3ByZS1maWx0ZXJpbmcpIGhlbHBzIHRvIG5vdCBvbmx5IHJlZHVjZSB0aGUgc2l6ZSBvZiB0aGUgREVTZXEyIG9iamVjdCwgYnV0IGFsc28gZ2l2ZXMgeW91IGEgc2Vuc2Ugb2YgaG93IG1hbnkgZ2VuZXMgd2VyZSByZWFzb25hYmx5IG1lYXN1cmVkIGZvciB5b3VyIHNhbXBsZXMuCgpIZXJlIHdlIHdpbGwgZmlsdGVyIG91dCBhbnkgZ2VuZXMgdGhhdCBoYXZlIGxlc3MgdGhhbiAxMCBjb3VudHMgYWNyb3NzIGFueSBvZiB0aGUgc2FtcGxlcy4gVGhpcyBpcyBhIGZhaXJseSBzdGFuZGFyZCBsZXZlbCBvZiBmaWx0ZXJpbmcsIGJ1dCB0aGUgZGF0YSBjb3VsZCBiZSBmaWx0ZXJlZCBtb3JlIGxlc3Mgb3IgbW9yZSBkZXBlbmRpbmcgb24gcXVhbGl0eSBjb250cm9sIG1ldHJpY3MgZnJvbSBhbGlnbm1lbnRzIGFuZCBzZXF1ZW5jaW5nIGRlcHRoIG9yIHRvdGFsIG51bWJlciBvZiBzYW1wbGVzLiBTaW5jZSB0aGUgREVTZXEyIG9iamVjdCBpc24ndCBjb21wYXRpYmxlIHdpdGggYHRpZHl2ZXJzZWAgZnVuY3Rpb25zLCB3ZSdsbCB0YWtlIGEgZGlmZmVyZW50IGFwcHJvYWNoOgoKYGBge3IgUHJlRmlsdGVyfQprZWVwID0gcm93U3Vtcyhjb3VudHMoZGRzKSkgPj0gMTAKZGRzX2ZpbHRlcmVkID0gZGRzW2tlZXAsXQpkZHNfZmlsdGVyZWQKYGBgCgpOb3RpY2UgdGhlIGBkZHNfZmlsdGVyZWRgIG9iamVjdCBoYXMgbGVzcyBlbGVtZW50cyB0aGFuIHRoZSB1bmZpbHRlcmVkIGBkZHNgIG9iamVjdCwgaW5kaWNhdGluZyB0aGF0IGEgbnVtYmVyIG9mIGdlbmVzIHdlcmUgbm90IG1lYXN1cmVkIGluIG91ciBleHBlcmltZW50LgoKKipDaGVja3BvaW50Kio6ICpRdWVzdGlvbnM/KgoKCgojIFN1bW1hcnkKCkluIHRoaXMgc2VjdGlvbiwgd2U6CgoqIExvYWRlZCB0aGUgbmVjZXNzYXJ5IGlucHV0IGZpbGVzIGludG8gb3VyIFIgc2Vzc2lvbgoqIERpc2N1c3NlZCB0aGUgaW1wb3J0YW5jZSBvZiBiaW9sb2dpY2FsIHJlcGxpY2F0ZXMKKiBJbml0aWFsaXplZCBhIERFU2VxMiBkYXRhIHNldAoqIEZpbHRlcmVkIG91ciBkYXRhIHRvIHJlbW92ZSBnZW5lcyB0aGF0IHdlcmUgcG9vcmx5IG1lYXN1cmVkCgpOb3cgdGhhdCB3ZSd2ZSBjcmVhdGVkIG91ciBERVNlcTIgb2JqZWN0cywgaW5jbHVkaW5nIHNwZWNpZnlpbmcgd2hhdCBtb2RlbCBpcyBhcHByb3ByaWF0ZSBmb3Igb3VyIGRhdGEsIGFuZCBmaWx0ZXJlZCBvdXIgZGF0YSwgd2UgY2FuIHByb2NlZWQgd2l0aCBhc3Nlc3NpbmcgdGhlIGltcGFjdCBvZiB0aGUgZXhwZXJpbWVudGFsIGNvbmRpdGlvbnMgb24gZ2VuZSBleHByZXNzaW9uIGZvciBvdXIgc2FtcGxlcy4KCgotLS0KCiMgU291cmNlcwoKVHJhaW5pbmcgcmVzb3VyY2VzIHVzZWQgdG8gZGV2ZWxvcCBtYXRlcmlhbHM6CgoqIEhCQyBER0Ugc2V0dXA6IGh0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL0RHRV93b3Jrc2hvcC9sZXNzb25zLzAxX0RHRV9zZXR1cF9hbmRfb3ZlcnZpZXcuaHRtbAoqIEhCQyBDb3VudCBOb3JtYWxpemF0aW9uOiBodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9ER0Vfd29ya3Nob3AvbGVzc29ucy8wMl9ER0VfY291bnRfbm9ybWFsaXphdGlvbi5odG1sCiogREVTZXEyIHN0YW5kYXJkIHZpZ25ldHRlOiBodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwKKiBERVNlcTIgYmVnaW5uZXJzIHZpZ25ldHRlOiBodHRwczovL2Jpb2MuaXNtLmFjLmpwL3BhY2thZ2VzLzIuMTQvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL2JlZ2lubmVyLnBkZgoqIEJpb2NvbmR1Y3RvciBSTkEtc2VxIFdvcmtmbG93czogaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9oZWxwL2NvdXJzZS1tYXRlcmlhbHMvMjAxNS9MZWFybkJpb2NvbmR1Y3RvckZlYjIwMTUvQjAyLjFfUk5BU2VxLmh0bWwKKiBDQ0RMIEdhc3RyaWMgY2FuY2VyIHRyYWluaW5nIG1hdGVyaWFsczogaHR0cHM6Ly9hbGV4c2xlbW9uYWRlLmdpdGh1Yi5pby90cmFpbmluZy1tb2R1bGVzL1JOQS1zZXEvMDMtZ2FzdHJpY19jYW5jZXJfZXhwbG9yYXRvcnkubmIuaHRtbAoqIENDREwgTmV1cm9ibGFzdG9tYSB0cmFpbmluZyBtYXRlcmlhbHM6IGh0dHBzOi8vYWxleHNsZW1vbmFkZS5naXRodWIuaW8vdHJhaW5pbmctbW9kdWxlcy9STkEtc2VxLzA1LW5iX2NlbGxfbGluZV9ERVNlcTIubmIuaHRtbAoKCgpgYGB7ciBXcml0ZU91dC5SRGF0YSwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIEhpZGRlbiBjb2RlIGJsb2NrIHRvIHdyaXRlIG91dCBkYXRhIGZvciBrbml0dGluZwojIHNhdmUuaW1hZ2UoZmlsZSA9ICJyZGF0YS9SdW5uaW5nRGF0YS5SRGF0YSIpCmBgYAoKLS0tCgpUaGVzZSBtYXRlcmlhbHMgaGF2ZSBiZWVuIGFkYXB0ZWQgYW5kIGV4dGVuZGVkIGZyb20gbWF0ZXJpYWxzIGxpc3RlZCBhYm92ZS4gVGhlc2UgYXJlIG9wZW4gYWNjZXNzIG1hdGVyaWFscyBkaXN0cmlidXRlZCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIFtDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uIGxpY2Vuc2UgKENDIEJZIDQuMCldKGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzQuMC8pLCB3aGljaCBwZXJtaXRzIHVucmVzdHJpY3RlZCB1c2UsIGRpc3RyaWJ1dGlvbiwgYW5kIHJlcHJvZHVjdGlvbiBpbiBhbnkgbWVkaXVtLCBwcm92aWRlZCB0aGUgb3JpZ2luYWwgYXV0aG9yIGFuZCBzb3VyY2UgYXJlIGNyZWRpdGVkLgoKPGJyLz4KPGJyLz4KPGhyLz4KfCBbUHJldmlvdXMgbGVzc29uXShNb2R1bGUwNl9ERUFuYWx5c2lzU2V0dXAuaHRtbCkgfCBbVG9wIG9mIHRoaXMgbGVzc29uXSgjdG9wKSB8IFtOZXh0IGxlc3Nvbl0oTW9kdWxlMDhfREVTZXEyREUuaHRtbCkgfAp8IDotLS0gfCA6LS0tLTogfCAtLS06IHwK