Objectives
- Generate tables of DE results
- Understand what a p-value represents.
- Understand multiple hypothesis correction application and
importance
Differential Expression Workflow
Now we test for differential expression between our groups of
interest and return a table of results.
Testing for DE
Before showing the code for generating differential expression
results, let’s walk through some toy data and discuss our intuition, its
limitations, and what we can do to formalize our thought around
determining if a gene is differentially expressed.
Let’s get a sense for our intuition by looking at expression boxplots
for a few “genes” where we’ve made up the data. In each plot, we are
comparing the expression levels (on the y-axis) for samples (each point)
representing a WT (red) and KO (teal) condition. The boxplot shows the
25% - 75% distribution, along with outliers, with a bar representing the
median value, and a black-crossed-point representing the mean.
For the first example, we ask: Does the KO affect the expression of
the gene?
It seems pretty clear that there is a large difference between the
means of the two groups, and within-group spread is quite low. So the
answer to the question is likely “Yes”.
Consider a second example:
Here there isn’t a large difference between the means of the two
groups, and there is quite a bit of spread within-group. The answer here
is likely “No”.
Finally, consider a third example:
The means are not so close to each other in this example, though
there is still quite a bit of spread. This is example is perhaps less
clear.
Looking at all three of the hypothetical genes together, we see them
in relation to one another and how they span the range from No, to
Maybe?, to Definitely.
Consider needing to make this decision for 20,000 genes. Even if all
the genes behaved in a clear-cut manner, that would take a lot of time
(or a lot of graduate students). But there are probably lots of Gene 2s
out there.
We need a formal, reproducible, way to make this
decision!
There are many tools to model expression between the groups, we have
chosen to focus on DESeq2, but know that a tool like edgeR is out there.
Both tools are statistically sound approachees to modeling and testing
for differential expression.
With any statistical test, we need to clearly state what we are
testing. For the toy data above, we assume that the KO doesn’t affect
the expression of any particular gene. Statisticians would call this the
“null hypothesis”. For the mouse data we’re working with, the null
hypothesis is that “there is no difference between the expression of the
iron deficient (minus) and normal (plus) mice”. The alternative is that
there is a difference.
For each gene, DESeq2 computes a “Wald statistic” which is a single
number encapsulating the difference in the means and the spread of the
groups. However, this number alone doesn’t tell us how to decide if a
gene is differentially expressed. We need a second
number to give us an idea of how extreme that Wald statistic is among
the distribution of Wald statistics, this is the “p-value”.
Imagine shuffling the KO and WT labels and recomputing the statistic
over and over again. You’d get a distribution of statistics that would
look similar to a normal curve. The p-value essentially tells you how
likely you are to have seen the statistic you see by chance. So when we
set a p-value = 0.05 as a threshold, we’re saying, “there is a 5% chance
I’d see something this extreme when there was actually no effect”. So
the evidence is strong, but not
ironclad.
Generating DE Results
We can check what comparisons were automatically generated during
fitting using the resultsNames()
function.
resultsNames(dds)
[1] "Intercept" "condition_minus_vs_plus"
There is only the one comparison in the results, so we will refer to
it in the name
parameter of the results()
function, and assign the result as an object.
results_minus_vs_plus = results(dds, name = 'condition_minus_vs_plus')
head(results_minus_vs_plus)
log2 fold change (MLE): condition minus vs plus
Wald test p-value: condition minus vs plus
DataFrame with 6 rows and 6 columns
baseMean log2FoldChange lfcSE stat pvalue
<numeric> <numeric> <numeric> <numeric> <numeric>
ENSMUSG00000000001 1489.83039 0.297760 0.210310 1.415815 0.156830
ENSMUSG00000000028 1748.93544 0.226421 0.176795 1.280695 0.200301
ENSMUSG00000000031 2151.87715 0.457635 0.764579 0.598545 0.549476
ENSMUSG00000000037 24.91672 0.579130 0.561259 1.031840 0.302147
ENSMUSG00000000049 7.78377 -0.899483 1.553063 -0.579167 0.562476
ENSMUSG00000000056 19653.54030 -0.174048 0.203529 -0.855151 0.392468
padj
<numeric>
ENSMUSG00000000001 0.868573
ENSMUSG00000000028 0.902900
ENSMUSG00000000031 0.995391
ENSMUSG00000000037 0.950613
ENSMUSG00000000049 0.998043
ENSMUSG00000000056 0.982479
In the results table, the row names are gene identifiers (in theis
case ENSEMBL IDs because that’s what the GTF we used in the call to
RSEM+STAR used), and the columns have the following definitions:
baseMean
is the average of the normalized count values,
divided by size factors and taken over all samples, and can be
interpreted as the relative expression level of that gene across all
samples.
log2FoldChange
is the log2 transformed ratio of the
expression of the numerator group (first group) over the denominator
group (second group after “vs”). Note that in our comparison, the
log2FoldChange
column compares the expression of the
numerator group (minus
) over the denominator group
(plus
). If the value is positive, that means the expression
of that gene is greater across the minus
samples than
across the plus
samples. If the value is negative, that
means the expression of that gene is greater across the
minus
samples.
lfcSE
is the standard error for the log2 fold change
estimate.
stat
is the calculated Wald statistic for that
gene.
pvalue
is the nominal significance for that
gene.
padj
is the adjusted p-value and is what we
use for determining significantly differently expressed genes.
Note:
results()
defaults
If no arguments are passed to results()
, then the log2
fold changes and Wald test p-value will be for the last
variable in the design formula, and if this is a factor, the
comparison will be the last level over the
reference level. If you specify name
, as
we did above, then the behavior is given by the name used from
resultsNames()
.
There are multiple ways to specify the test to be done using the
results()
function. It is especially helpful to know this
when fitting more complex models and testing more complex contrasts. To
demonstrate this, consider this description from the help for
results()
:
contrast
: a character vector with exactly three
elements: the name of a factor in the design formula, the name of the
numerator level for the fold change, and the name of the denominator
level for the fold change
So an alternative way to test the same contrast as above
(i.e. plus
/ minus
) is:
alt_results_minus_vs_plus = results(dds, contrast = c('condition', 'minus', 'plus'))
head(alt_results_minus_vs_plus)
log2 fold change (MLE): condition minus vs plus
Wald test p-value: condition minus vs plus
DataFrame with 6 rows and 6 columns
baseMean log2FoldChange lfcSE stat pvalue
<numeric> <numeric> <numeric> <numeric> <numeric>
ENSMUSG00000000001 1489.83039 0.297760 0.210310 1.415815 0.156830
ENSMUSG00000000028 1748.93544 0.226421 0.176795 1.280695 0.200301
ENSMUSG00000000031 2151.87715 0.457635 0.764579 0.598545 0.549476
ENSMUSG00000000037 24.91672 0.579130 0.561259 1.031840 0.302147
ENSMUSG00000000049 7.78377 -0.899483 1.553063 -0.579167 0.562476
ENSMUSG00000000056 19653.54030 -0.174048 0.203529 -0.855151 0.392468
padj
<numeric>
ENSMUSG00000000001 0.868573
ENSMUSG00000000028 0.902900
ENSMUSG00000000031 0.995391
ENSMUSG00000000037 0.950613
ENSMUSG00000000049 0.998043
ENSMUSG00000000056 0.982479
This way of calling results()
is especially helpful when
the levels of the column of interest contain more than two levels
because you can specify exactly which levels to test with little
confusion.
Question
Why should we use values from padj
instead of the
pvalue
? Post in the Slack thread.
Multiple hypothesis testing and FDR correction
Each p-value is the result of a single test for a single gene. With a
p-value < 0.05 significance cut-off, there is a 5% chance it is a
false positive. The more genes we test, the greater chance we have of
seeing a significant results by chance. So if we are testing
20,000 genes for differential expression, we would expect to see ~1,000
significant genes just by chance.
To address this we will correct for multiple
hypothesis testing to reduce the number of false positives. While
there are a few approaches, the default method is the False Discovery
Rate (FDR) (Benjamini
and Hochberg (1995)).
The default FDR rate cutoff for our analyses is 0.05, meaning the
proportion of false positives amongst our differentially expressed genes
is controlled to 5%. So if we call 500 genes as differentially expressed
with this FDR cutoff, we expect only 25 of them to be false positives.
DESeq2 vignette’s includes a further
discussion of filtering and multiple testing.
Note
on padj
values set to NA
As discussed in the HBC
tutorial as well as the DESeq2
vignette, DESeq2 reduces the number of genes that will be tested by
removing genes with low number of counts and outlier samples.
- If within a row, all samples have zero counts, the baseMean column
will be zero, and the log2 fold change estimates, p-value and adjusted
p-value will all be set to NA.
- If a row contains a sample with an extreme count outlier then the
p-value and adjusted p-value will be set to NA. These outlier counts are
detected by Cook’s
distance.
- If a row is filtered by automatic independent filtering, e.g. for
having a low mean normalized count, then only the adjusted p-value will
be set to NA.
Now that we’ve generated our differential comparisons and have an
understanding of our results, including multiple hypothesis correction,
we can proceed with generating summary figures and tables for our
differential expression analysis.
Summary
In this section, we:
- Performed statistical tests for comparisons of interest
- Generated tables of differential expression results - i.e. fold
changes and adjusted pvalues for each gene in dataset
- Discussed importance and application of multiple hypothesis
correction
Now that we’ve generated our differential comparisons and have an
understanding of our results, including multiple hypothesis correction,
we can proceed with generating summary figures and tables for our
differential expression analysis.
Sources
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.
LS0tCnRpdGxlOiAiTW9kdWxlIDEwOiBERSBUZXN0aW5nIgphdXRob3I6ICJVTSBCaW9pbmZvcm1hdGljcyBDb3JlIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICAgICAgICBodG1sX2RvY3VtZW50OgogICAgICAgICAgICBpbmNsdWRlczoKICAgICAgICAgICAgICAgIGluX2hlYWRlcjogaGVhZGVyLmh0bWwKICAgICAgICAgICAgdGhlbWU6IHBhcGVyCiAgICAgICAgICAgIHRvYzogdHJ1ZQogICAgICAgICAgICB0b2NfZGVwdGg6IDQKICAgICAgICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgICAgICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgICAgICAgICAgZmlnX2NhcHRpb246IHRydWUKICAgICAgICAgICAgbWFya2Rvd246IEdGTQogICAgICAgICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keSwgdGQgewogICBmb250LXNpemU6IDE4cHg7Cn0KY29kZS5yewogIGZvbnQtc2l6ZTogMTJweDsKfQpwcmUgewogIGZvbnQtc2l6ZTogMTJweAp9Cjwvc3R5bGU+CgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpzb3VyY2UoIi4uL2Jpbi9jaHVuay1vcHRpb25zLlIiKQprbml0cl9maWdfcGF0aCgiMTAtIikKYGBgCgo+ICMgT2JqZWN0aXZlcyB7LnVubGlzdGVkIC51bm51bWJlcmVkfQo+ICogR2VuZXJhdGUgdGFibGVzIG9mIERFIHJlc3VsdHMKPiAqIFVuZGVyc3RhbmQgd2hhdCBhIHAtdmFsdWUgcmVwcmVzZW50cy4KPiAqIFVuZGVyc3RhbmQgbXVsdGlwbGUgaHlwb3RoZXNpcyBjb3JyZWN0aW9uIGFwcGxpY2F0aW9uIGFuZCBpbXBvcnRhbmNlCgoKYGBge3IgTW9kdWxlcywgZXZhbD1UUlVFLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKIyBsb2FkKCJyZGF0YS9SdW5uaW5nRGF0YS5SRGF0YSIpCmBgYAoKIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBXb3JrZmxvdyB7LnVubGlzdGVkIC51bm51bWJlcmVkfQoKTm93IHdlIHRlc3QgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGJldHdlZW4gb3VyIGdyb3VwcyBvZiBpbnRlcmVzdCBhbmQgcmV0dXJuIGEgdGFibGUgb2YgcmVzdWx0cy4KCiFbXSguL2ltYWdlcy93YXlmaW5kZXIvd2F5ZmluZGVyLURFQ29tcGFyaXNvbnMucG5nKXt3aWR0aD03NSV9CgotLS0KCiMgVGVzdGluZyBmb3IgREUKCkJlZm9yZSBzaG93aW5nIHRoZSBjb2RlIGZvciBnZW5lcmF0aW5nIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHJlc3VsdHMsIGxldCdzIHdhbGsgdGhyb3VnaCBzb21lIHRveSBkYXRhIGFuZCBkaXNjdXNzIG91ciBpbnR1aXRpb24sIGl0cyBsaW1pdGF0aW9ucywgYW5kIHdoYXQgd2UgY2FuIGRvIHRvIGZvcm1hbGl6ZSBvdXIgdGhvdWdodCBhcm91bmQgZGV0ZXJtaW5pbmcgaWYgYSBnZW5lIGlzIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZC4KCkxldCdzIGdldCBhIHNlbnNlIGZvciBvdXIgaW50dWl0aW9uIGJ5IGxvb2tpbmcgYXQgZXhwcmVzc2lvbiBib3hwbG90cyBmb3IgYSBmZXcgImdlbmVzIiB3aGVyZSB3ZSd2ZSBtYWRlIHVwIHRoZSBkYXRhLiBJbiBlYWNoIHBsb3QsIHdlIGFyZSBjb21wYXJpbmcgdGhlIGV4cHJlc3Npb24gbGV2ZWxzIChvbiB0aGUgeS1heGlzKSBmb3Igc2FtcGxlcyAoZWFjaCBwb2ludCkgcmVwcmVzZW50aW5nIGEgV1QgKHJlZCkgYW5kIEtPICh0ZWFsKSBjb25kaXRpb24uIFRoZSBib3hwbG90IHNob3dzIHRoZSAyNSUgLSA3NSUgZGlzdHJpYnV0aW9uLCBhbG9uZyB3aXRoIG91dGxpZXJzLCB3aXRoIGEgYmFyIHJlcHJlc2VudGluZyB0aGUgbWVkaWFuIHZhbHVlLCBhbmQgYSBibGFjay1jcm9zc2VkLXBvaW50IHJlcHJlc2VudGluZyB0aGUgbWVhbi4KCkZvciB0aGUgZmlyc3QgZXhhbXBsZSwgd2UgYXNrOiBEb2VzIHRoZSBLTyBhZmZlY3QgdGhlIGV4cHJlc3Npb24gb2YgdGhlIGdlbmU/CgohW10oLi9pbWFnZXMvTW9kdWxlMTBfc3RhdF9wbG90X0dlbmVfMy5wbmcpe3dpZHRoPTc1JX0KCkl0IHNlZW1zIHByZXR0eSBjbGVhciB0aGF0IHRoZXJlIGlzIGEgbGFyZ2UgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBtZWFucyBvZiB0aGUgdHdvIGdyb3VwcywgYW5kIHdpdGhpbi1ncm91cCBzcHJlYWQgaXMgcXVpdGUgbG93LiBTbyB0aGUgYW5zd2VyIHRvIHRoZSBxdWVzdGlvbiBpcyBsaWtlbHkgIlllcyIuCgpDb25zaWRlciBhIHNlY29uZCBleGFtcGxlOgoKIVtdKC4vaW1hZ2VzL01vZHVsZTEwX3N0YXRfcGxvdF9HZW5lXzEucG5nKXt3aWR0aD03NSV9CgpIZXJlIHRoZXJlIGlzbid0IGEgbGFyZ2UgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBtZWFucyBvZiB0aGUgdHdvIGdyb3VwcywgYW5kIHRoZXJlIGlzIHF1aXRlIGEgYml0IG9mIHNwcmVhZCB3aXRoaW4tZ3JvdXAuIFRoZSBhbnN3ZXIgaGVyZSBpcyBsaWtlbHkgIk5vIi4KCkZpbmFsbHksIGNvbnNpZGVyIGEgdGhpcmQgZXhhbXBsZToKCiFbXSguL2ltYWdlcy9Nb2R1bGUxMF9zdGF0X3Bsb3RfR2VuZV8yLnBuZyl7d2lkdGg9NzUlfQoKVGhlIG1lYW5zIGFyZSBub3Qgc28gY2xvc2UgdG8gZWFjaCBvdGhlciBpbiB0aGlzIGV4YW1wbGUsIHRob3VnaCB0aGVyZSBpcyBzdGlsbCBxdWl0ZSBhIGJpdCBvZiBzcHJlYWQuIFRoaXMgaXMgZXhhbXBsZSBpcyBwZXJoYXBzIGxlc3MgY2xlYXIuCgpMb29raW5nIGF0IGFsbCB0aHJlZSBvZiB0aGUgaHlwb3RoZXRpY2FsIGdlbmVzIHRvZ2V0aGVyLCB3ZSBzZWUgdGhlbSBpbiByZWxhdGlvbiB0byBvbmUgYW5vdGhlciBhbmQgaG93IHRoZXkgc3BhbiB0aGUgcmFuZ2UgZnJvbSBObywgdG8gTWF5YmU/LCB0byBEZWZpbml0ZWx5LgoKIVtdKC4vaW1hZ2VzL01vZHVsZTEwX3N0YXRfcGxvdC5wbmcpCgpDb25zaWRlciBuZWVkaW5nIHRvIG1ha2UgdGhpcyBkZWNpc2lvbiBmb3IgMjAsMDAwIGdlbmVzLiBFdmVuIGlmIGFsbCB0aGUgZ2VuZXMgYmVoYXZlZCBpbiBhIGNsZWFyLWN1dCBtYW5uZXIsIHRoYXQgd291bGQgdGFrZSBhIGxvdCBvZiB0aW1lIChvciBhIGxvdCBvZiBncmFkdWF0ZSBzdHVkZW50cykuIEJ1dCB0aGVyZSBhcmUgcHJvYmFibHkgbG90cyBvZiBHZW5lIDJzIG91dCB0aGVyZS4KCioqV2UgbmVlZCBhIGZvcm1hbCwgcmVwcm9kdWNpYmxlLCB3YXkgdG8gbWFrZSB0aGlzIGRlY2lzaW9uISoqCgpUaGVyZSBhcmUgbWFueSB0b29scyB0byBtb2RlbCBleHByZXNzaW9uIGJldHdlZW4gdGhlIGdyb3Vwcywgd2UgaGF2ZSBjaG9zZW4gdG8gZm9jdXMgb24gREVTZXEyLCBidXQga25vdyB0aGF0IGEgdG9vbCBsaWtlIGVkZ2VSIGlzIG91dCB0aGVyZS4gQm90aCB0b29scyBhcmUgc3RhdGlzdGljYWxseSBzb3VuZCBhcHByb2FjaGVlcyB0byBtb2RlbGluZyBhbmQgdGVzdGluZyBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24uCgpXaXRoIGFueSBzdGF0aXN0aWNhbCB0ZXN0LCB3ZSBuZWVkIHRvIGNsZWFybHkgc3RhdGUgd2hhdCB3ZSBhcmUgdGVzdGluZy4gRm9yIHRoZSB0b3kgZGF0YSBhYm92ZSwgd2UgYXNzdW1lIHRoYXQgdGhlIEtPIGRvZXNuJ3QgYWZmZWN0IHRoZSBleHByZXNzaW9uIG9mIGFueSBwYXJ0aWN1bGFyIGdlbmUuIFN0YXRpc3RpY2lhbnMgd291bGQgY2FsbCB0aGlzIHRoZSAibnVsbCBoeXBvdGhlc2lzIi4gRm9yIHRoZSBtb3VzZSBkYXRhIHdlJ3JlIHdvcmtpbmcgd2l0aCwgdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0aGF0ICJ0aGVyZSBpcyBubyBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGV4cHJlc3Npb24gb2YgdGhlIGlyb24gZGVmaWNpZW50IChtaW51cykgYW5kIG5vcm1hbCAocGx1cykgbWljZSIuIFRoZSBhbHRlcm5hdGl2ZSBpcyB0aGF0IHRoZXJlIGlzIGEgZGlmZmVyZW5jZS4KCkZvciBlYWNoIGdlbmUsIERFU2VxMiBjb21wdXRlcyBhICJXYWxkIHN0YXRpc3RpYyIgd2hpY2ggaXMgYSBzaW5nbGUgbnVtYmVyIGVuY2Fwc3VsYXRpbmcgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIG1lYW5zIGFuZCB0aGUgc3ByZWFkIG9mIHRoZSBncm91cHMuIEhvd2V2ZXIsIHRoaXMgbnVtYmVyIGFsb25lIGRvZXNuJ3QgdGVsbCB1cyBob3cgdG8gZGVjaWRlIGlmIGEgZ2VuZSBpcyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQuIFdlIG5lZWQgYSAqKnNlY29uZCoqIG51bWJlciB0byBnaXZlIHVzIGFuIGlkZWEgb2YgaG93IGV4dHJlbWUgdGhhdCBXYWxkIHN0YXRpc3RpYyBpcyBhbW9uZyB0aGUgZGlzdHJpYnV0aW9uIG9mIFdhbGQgc3RhdGlzdGljcywgdGhpcyBpcyB0aGUgInAtdmFsdWUiLgoKSW1hZ2luZSBzaHVmZmxpbmcgdGhlIEtPIGFuZCBXVCBsYWJlbHMgYW5kIHJlY29tcHV0aW5nIHRoZSBzdGF0aXN0aWMgb3ZlciBhbmQgb3ZlciBhZ2Fpbi4gWW91J2QgZ2V0IGEgZGlzdHJpYnV0aW9uIG9mIHN0YXRpc3RpY3MgdGhhdCB3b3VsZCBsb29rIHNpbWlsYXIgdG8gYSBub3JtYWwgY3VydmUuIFRoZSBwLXZhbHVlIGVzc2VudGlhbGx5IHRlbGxzIHlvdSBob3cgbGlrZWx5IHlvdSBhcmUgdG8gaGF2ZSBzZWVuIHRoZSBzdGF0aXN0aWMgeW91IHNlZSBieSBjaGFuY2UuIFNvIHdoZW4gd2Ugc2V0IGEgcC12YWx1ZSA9IDAuMDUgYXMgYSB0aHJlc2hvbGQsIHdlJ3JlIHNheWluZywgInRoZXJlIGlzIGEgNSUgY2hhbmNlIEknZCBzZWUgc29tZXRoaW5nIHRoaXMgZXh0cmVtZSB3aGVuIHRoZXJlIHdhcyBhY3R1YWxseSBubyBlZmZlY3QiLiBTbyB0aGUgZXZpZGVuY2UgaXMgKipzdHJvbmcqKiwgYnV0IG5vdCAqKmlyb25jbGFkKiouCgojIEdlbmVyYXRpbmcgREUgUmVzdWx0cwoKV2UgY2FuIGNoZWNrIHdoYXQgY29tcGFyaXNvbnMgd2VyZSBhdXRvbWF0aWNhbGx5IGdlbmVyYXRlZCBkdXJpbmcgZml0dGluZyB1c2luZyB0aGUgYHJlc3VsdHNOYW1lcygpYCBmdW5jdGlvbi4KYGBge3IgUmVzdWx0czF9CnJlc3VsdHNOYW1lcyhkZHMpCmBgYAoKVGhlcmUgaXMgb25seSB0aGUgb25lIGNvbXBhcmlzb24gaW4gdGhlIHJlc3VsdHMsIHNvIHdlIHdpbGwgcmVmZXIgdG8gaXQgaW4gdGhlIGBuYW1lYCBwYXJhbWV0ZXIgb2YgdGhlIGByZXN1bHRzKClgIGZ1bmN0aW9uLCBhbmQgYXNzaWduIHRoZSByZXN1bHQgYXMgYW4gb2JqZWN0LgoKYGBge3IgU2V0c1Jlc3VsdH0KcmVzdWx0c19taW51c192c19wbHVzID0gcmVzdWx0cyhkZHMsIG5hbWUgPSAnY29uZGl0aW9uX21pbnVzX3ZzX3BsdXMnKQpoZWFkKHJlc3VsdHNfbWludXNfdnNfcGx1cykKYGBgCgpJbiB0aGUgcmVzdWx0cyB0YWJsZSwgdGhlIHJvdyBuYW1lcyBhcmUgZ2VuZSBpZGVudGlmaWVycyAoaW4gdGhlaXMgY2FzZSBFTlNFTUJMIElEcyBiZWNhdXNlIHRoYXQncyB3aGF0IHRoZSBHVEYgd2UgdXNlZCBpbiB0aGUgY2FsbCB0byBSU0VNK1NUQVIgdXNlZCksIGFuZCB0aGUgY29sdW1ucyBoYXZlIHRoZSBmb2xsb3dpbmcgZGVmaW5pdGlvbnM6CgoxLiBgYmFzZU1lYW5gIGlzIHRoZSBhdmVyYWdlIG9mIHRoZSBub3JtYWxpemVkIGNvdW50IHZhbHVlcywgZGl2aWRlZCBieSBzaXplIGZhY3RvcnMgYW5kIHRha2VuIG92ZXIgYWxsIHNhbXBsZXMsIGFuZCBjYW4gYmUgaW50ZXJwcmV0ZWQgYXMgdGhlIHJlbGF0aXZlIGV4cHJlc3Npb24gbGV2ZWwgb2YgdGhhdCBnZW5lIGFjcm9zcyBhbGwgc2FtcGxlcy4KMi4gYGxvZzJGb2xkQ2hhbmdlYCBpcyB0aGUgbG9nMiB0cmFuc2Zvcm1lZCByYXRpbyBvZiB0aGUgZXhwcmVzc2lvbiBvZiB0aGUgbnVtZXJhdG9yIGdyb3VwIChmaXJzdCBncm91cCkgb3ZlciB0aGUgZGVub21pbmF0b3IgZ3JvdXAgKHNlY29uZCBncm91cCBhZnRlciAidnMiKS4gTm90ZSB0aGF0IGluIG91ciBjb21wYXJpc29uLCB0aGUgYGxvZzJGb2xkQ2hhbmdlYCBjb2x1bW4gY29tcGFyZXMgdGhlIGV4cHJlc3Npb24gb2YgdGhlIG51bWVyYXRvciBncm91cCAoYG1pbnVzYCkgb3ZlciB0aGUgZGVub21pbmF0b3IgZ3JvdXAgKGBwbHVzYCkuIElmIHRoZSB2YWx1ZSBpcyBwb3NpdGl2ZSwgdGhhdCBtZWFucyB0aGUgZXhwcmVzc2lvbiBvZiB0aGF0IGdlbmUgaXMgZ3JlYXRlciBhY3Jvc3MgdGhlIGBtaW51c2Agc2FtcGxlcyB0aGFuIGFjcm9zcyB0aGUgYHBsdXNgIHNhbXBsZXMuIElmIHRoZSB2YWx1ZSBpcyBuZWdhdGl2ZSwgdGhhdCBtZWFucyB0aGUgZXhwcmVzc2lvbiBvZiB0aGF0IGdlbmUgaXMgZ3JlYXRlciBhY3Jvc3MgdGhlIGBtaW51c2Agc2FtcGxlcy4KMy4gYGxmY1NFYCBpcyB0aGUgc3RhbmRhcmQgZXJyb3IgZm9yIHRoZSBsb2cyIGZvbGQgY2hhbmdlIGVzdGltYXRlLgo0LiBgc3RhdGAgaXMgdGhlIGNhbGN1bGF0ZWQgV2FsZCBzdGF0aXN0aWMgZm9yIHRoYXQgZ2VuZS4KNS4gYHB2YWx1ZWAgaXMgdGhlICpub21pbmFsKiBzaWduaWZpY2FuY2UgZm9yIHRoYXQgZ2VuZS4KNi4gYHBhZGpgIGlzIHRoZSAqYWRqdXN0ZWQgcC12YWx1ZSogYW5kIGlzIHdoYXQgd2UgdXNlIGZvciBkZXRlcm1pbmluZyBzaWduaWZpY2FudGx5IGRpZmZlcmVudGx5IGV4cHJlc3NlZCBnZW5lcy4KCj4gIyBOb3RlOiBgcmVzdWx0cygpYCBkZWZhdWx0cyB7LnVubGlzdGVkIC51bm51bWJlcmVkfQo+IElmIG5vIGFyZ3VtZW50cyBhcmUgcGFzc2VkIHRvIGByZXN1bHRzKClgLCB0aGVuIHRoZSBsb2cyIGZvbGQgY2hhbmdlcyBhbmQgV2FsZCB0ZXN0IHAtdmFsdWUgd2lsbCBiZSBmb3IgdGhlICoqbGFzdCB2YXJpYWJsZSoqIGluIHRoZSBkZXNpZ24gZm9ybXVsYSwgYW5kIGlmIHRoaXMgaXMgYSBmYWN0b3IsIHRoZSBjb21wYXJpc29uIHdpbGwgYmUgdGhlICoqbGFzdCBsZXZlbCoqIG92ZXIgdGhlICoqcmVmZXJlbmNlIGxldmVsKiouIElmIHlvdSBzcGVjaWZ5IGBuYW1lYCwgYXMgd2UgZGlkIGFib3ZlLCB0aGVuIHRoZSBiZWhhdmlvciBpcyBnaXZlbiBieSB0aGUgbmFtZSB1c2VkIGZyb20gYHJlc3VsdHNOYW1lcygpYC4KClRoZXJlIGFyZSBtdWx0aXBsZSB3YXlzIHRvIHNwZWNpZnkgdGhlIHRlc3QgdG8gYmUgZG9uZSB1c2luZyB0aGUgYHJlc3VsdHMoKWAgZnVuY3Rpb24uIEl0IGlzIGVzcGVjaWFsbHkgaGVscGZ1bCB0byBrbm93IHRoaXMgd2hlbiBmaXR0aW5nIG1vcmUgY29tcGxleCBtb2RlbHMgYW5kIHRlc3RpbmcgbW9yZSBjb21wbGV4IGNvbnRyYXN0cy4gVG8gZGVtb25zdHJhdGUgdGhpcywgY29uc2lkZXIgdGhpcyBkZXNjcmlwdGlvbiBmcm9tIHRoZSBoZWxwIGZvciBgcmVzdWx0cygpYDoKCj4gYGNvbnRyYXN0YDogYSBjaGFyYWN0ZXIgdmVjdG9yIHdpdGggZXhhY3RseSB0aHJlZSBlbGVtZW50czogdGhlIG5hbWUgb2YgYSBmYWN0b3IgaW4gdGhlIGRlc2lnbiBmb3JtdWxhLCB0aGUgbmFtZSBvZiB0aGUgbnVtZXJhdG9yIGxldmVsIGZvciB0aGUgZm9sZCBjaGFuZ2UsIGFuZCB0aGUgbmFtZSBvZiB0aGUgZGVub21pbmF0b3IgbGV2ZWwgZm9yIHRoZSBmb2xkIGNoYW5nZQoKU28gYW4gYWx0ZXJuYXRpdmUgd2F5IHRvIHRlc3QgdGhlIHNhbWUgY29udHJhc3QgYXMgYWJvdmUgKGkuZS4gYHBsdXNgIC8gYG1pbnVzYCkgaXM6CgpgYGB7ciBTZXRzUmVzdWx0c0FsdH0KYWx0X3Jlc3VsdHNfbWludXNfdnNfcGx1cyA9IHJlc3VsdHMoZGRzLCBjb250cmFzdCA9IGMoJ2NvbmRpdGlvbicsICdtaW51cycsICdwbHVzJykpCmhlYWQoYWx0X3Jlc3VsdHNfbWludXNfdnNfcGx1cykKYGBgCgpUaGlzIHdheSBvZiBjYWxsaW5nIGByZXN1bHRzKClgIGlzIGVzcGVjaWFsbHkgaGVscGZ1bCB3aGVuIHRoZSBsZXZlbHMgb2YgdGhlIGNvbHVtbiBvZiBpbnRlcmVzdCBjb250YWluIG1vcmUgdGhhbiB0d28gbGV2ZWxzIGJlY2F1c2UgeW91IGNhbiBzcGVjaWZ5IGV4YWN0bHkgd2hpY2ggbGV2ZWxzIHRvIHRlc3Qgd2l0aCBsaXR0bGUgY29uZnVzaW9uLgoKPiAjIFF1ZXN0aW9uIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cj4gV2h5IHNob3VsZCB3ZSB1c2UgdmFsdWVzIGZyb20gYHBhZGpgIGluc3RlYWQgb2YgdGhlIGBwdmFsdWVgPyBQb3N0IGluIHRoZSBTbGFjayB0aHJlYWQuCgojIyMgTXVsdGlwbGUgaHlwb3RoZXNpcyB0ZXN0aW5nIGFuZCBGRFIgY29ycmVjdGlvbgoKRWFjaCBwLXZhbHVlIGlzIHRoZSByZXN1bHQgb2YgYSBzaW5nbGUgdGVzdCBmb3IgYSBzaW5nbGUgZ2VuZS4gV2l0aCBhIHAtdmFsdWUgPCAwLjA1IHNpZ25pZmljYW5jZSBjdXQtb2ZmLCB0aGVyZSBpcyBhIDUlIGNoYW5jZSBpdCBpcyBhIGZhbHNlIHBvc2l0aXZlLiBUaGUgbW9yZSBnZW5lcyB3ZSB0ZXN0LCB0aGUgZ3JlYXRlciBjaGFuY2Ugd2UgaGF2ZSBvZiBzZWVpbmcgYSBzaWduaWZpY2FudCByZXN1bHRzIGJ5IGNoYW5jZS4gKipTbyBpZiB3ZSBhcmUgdGVzdGluZyAyMCwwMDAgZ2VuZXMgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uLCB3ZSB3b3VsZCBleHBlY3QgdG8gc2VlIH4xLDAwMCBzaWduaWZpY2FudCBnZW5lcyBqdXN0IGJ5IGNoYW5jZS4qKgoKVG8gYWRkcmVzcyB0aGlzIHdlIHdpbGwgY29ycmVjdCBmb3IgW211bHRpcGxlIGh5cG90aGVzaXMgdGVzdGluZ10oaHR0cHM6Ly9tdWx0aXRocmVhZGVkLnN0aXRjaGZpeC5jb20vYmxvZy8yMDE1LzEwLzE1L211bHRpcGxlLWh5cG90aGVzaXMtdGVzdGluZy8pIHRvIHJlZHVjZSB0aGUgbnVtYmVyIG9mIGZhbHNlIHBvc2l0aXZlcy4gV2hpbGUgdGhlcmUgYXJlIGEgZmV3IGFwcHJvYWNoZXMsIHRoZSBkZWZhdWx0IG1ldGhvZCBpcyB0aGUgRmFsc2UgRGlzY292ZXJ5IFJhdGUgKEZEUikgKFtCZW5qYW1pbmkgYW5kIEhvY2hiZXJnICgxOTk1KV0oaHR0cHM6Ly9yc3Mub25saW5lbGlicmFyeS53aWxleS5jb20vZG9pLzEwLjExMTEvai4yNTE3LTYxNjEuMTk5NS50YjAyMDMxLngpKS4KClRoZSBkZWZhdWx0IEZEUiByYXRlIGN1dG9mZiBmb3Igb3VyIGFuYWx5c2VzIGlzIDAuMDUsIG1lYW5pbmcgdGhlIHByb3BvcnRpb24gb2YgZmFsc2UgcG9zaXRpdmVzIGFtb25nc3Qgb3VyIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBpcyBjb250cm9sbGVkIHRvIDUlLiBTbyBpZiB3ZSBjYWxsIDUwMCBnZW5lcyBhcyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgd2l0aCB0aGlzIEZEUiBjdXRvZmYsIHdlIGV4cGVjdCBvbmx5IDI1IG9mIHRoZW0gdG8gYmUgZmFsc2UgcG9zaXRpdmVzLiBERVNlcTIgdmlnbmV0dGUncyBpbmNsdWRlcyBhIFtmdXJ0aGVyIGRpc2N1c3Npb24gb2YgZmlsdGVyaW5nIGFuZCBtdWx0aXBsZSB0ZXN0aW5nXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwjaW5kZXBlbmRlbnQtZmlsdGVyaW5nLWFuZC1tdWx0aXBsZS10ZXN0aW5nKS4KCj4gIyBOb3RlIG9uIGBwYWRqYCB2YWx1ZXMgc2V0IHRvIE5BIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cj4KPiBBcyBkaXNjdXNzZWQgaW4gdGhlIFtIQkMgdHV0b3JpYWxdKGh0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL0RHRV93b3Jrc2hvcC9sZXNzb25zLzA1X0RHRV9ERVNlcTJfYW5hbHlzaXMyLmh0bWwpIGFzIHdlbGwgYXMgdGhlIFtERVNlcTIgdmlnbmV0dGVdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCNpLXdhbnQtdG8tYmVuY2htYXJrLWRlc2VxMi1jb21wYXJpbmctdG8tb3RoZXItZGUtdG9vbHMuKSwgREVTZXEyIHJlZHVjZXMgdGhlIG51bWJlciBvZiBnZW5lcyB0aGF0IHdpbGwgYmUgdGVzdGVkIGJ5IHJlbW92aW5nIGdlbmVzIHdpdGggbG93IG51bWJlciBvZiBjb3VudHMgYW5kIG91dGxpZXIgc2FtcGxlcy4KPgo+ICogSWYgd2l0aGluIGEgcm93LCBhbGwgc2FtcGxlcyBoYXZlIHplcm8gY291bnRzLCB0aGUgYmFzZU1lYW4gY29sdW1uIHdpbGwgYmUgemVybywgYW5kIHRoZSBsb2cyIGZvbGQgY2hhbmdlIGVzdGltYXRlcywgcC12YWx1ZSBhbmQgYWRqdXN0ZWQgcC12YWx1ZSB3aWxsIGFsbCBiZSBzZXQgdG8gTkEuCj4gKiBJZiBhIHJvdyBjb250YWlucyBhIHNhbXBsZSB3aXRoIGFuIGV4dHJlbWUgY291bnQgb3V0bGllciB0aGVuIHRoZSBwLXZhbHVlIGFuZCBhZGp1c3RlZCBwLXZhbHVlIHdpbGwgYmUgc2V0IHRvIE5BLiBUaGVzZSBvdXRsaWVyIGNvdW50cyBhcmUgZGV0ZWN0ZWQgYnkgW0Nvb2vigJlzIGRpc3RhbmNlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Db29rJTI3c19kaXN0YW5jZSkuCj4gKiBJZiBhIHJvdyBpcyBmaWx0ZXJlZCBieSBhdXRvbWF0aWMgaW5kZXBlbmRlbnQgZmlsdGVyaW5nLCBlLmcuIGZvciBoYXZpbmcgYSBsb3cgbWVhbiBub3JtYWxpemVkIGNvdW50LCB0aGVuIG9ubHkgdGhlIGFkanVzdGVkIHAtdmFsdWUgd2lsbCBiZSBzZXQgdG8gTkEuCgpOb3cgdGhhdCB3ZSd2ZSBnZW5lcmF0ZWQgb3VyIGRpZmZlcmVudGlhbCBjb21wYXJpc29ucyBhbmQgaGF2ZSBhbiB1bmRlcnN0YW5kaW5nIG9mIG91ciByZXN1bHRzLCBpbmNsdWRpbmcgbXVsdGlwbGUgaHlwb3RoZXNpcyBjb3JyZWN0aW9uLCB3ZSBjYW4gcHJvY2VlZCB3aXRoIGdlbmVyYXRpbmcgc3VtbWFyeSBmaWd1cmVzIGFuZCB0YWJsZXMgZm9yIG91ciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4KCi0tLQoKIyBTdW1tYXJ5CgpJbiB0aGlzIHNlY3Rpb24sIHdlOgoKKiBQZXJmb3JtZWQgc3RhdGlzdGljYWwgdGVzdHMgZm9yIGNvbXBhcmlzb25zIG9mIGludGVyZXN0CiogR2VuZXJhdGVkIHRhYmxlcyBvZiBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzIC0gaS5lLiBmb2xkIGNoYW5nZXMgYW5kIGFkanVzdGVkIHB2YWx1ZXMgZm9yIGVhY2ggZ2VuZSBpbiBkYXRhc2V0CiogRGlzY3Vzc2VkIGltcG9ydGFuY2UgYW5kIGFwcGxpY2F0aW9uIG9mIG11bHRpcGxlIGh5cG90aGVzaXMgY29ycmVjdGlvbgoKTm93IHRoYXQgd2UndmUgZ2VuZXJhdGVkIG91ciBkaWZmZXJlbnRpYWwgY29tcGFyaXNvbnMgYW5kIGhhdmUgYW4gdW5kZXJzdGFuZGluZyBvZiBvdXIgcmVzdWx0cywgaW5jbHVkaW5nIG11bHRpcGxlIGh5cG90aGVzaXMgY29ycmVjdGlvbiwgd2UgY2FuIHByb2NlZWQgd2l0aCBnZW5lcmF0aW5nIHN1bW1hcnkgZmlndXJlcyBhbmQgdGFibGVzIGZvciBvdXIgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMuCgotLS0KCiMgU291cmNlcwoKKiBIQkMgREdFIHRyYWluaW5nIG1vZHVsZSwgcGFydCAxOiBodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9ER0Vfd29ya3Nob3AvbGVzc29ucy8wNF9ER0VfREVTZXEyX2FuYWx5c2lzLmh0bWwKKiBIQkMgREdFIHRyYWluaW5nIG1vZHVsZSwgcGFydCAyOiBodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9ER0Vfd29ya3Nob3AvbGVzc29ucy8wNV9ER0VfREVTZXEyX2FuYWx5c2lzMi5odG1sCiogREVTZXEyIHZpZ25ldHRlOiBodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwjZGlmZmVyZW50aWFsLWV4cHJlc3Npb24tYW5hbHlzaXMKCi0tLQoKIVtdKC4vaW1hZ2VzL3NpZ25pZmljYW50X3hrY2QucG5nKQoKYGBge3IgV3JpdGVPdXQuUkRhdGEsIGV2YWw9VFJVRSwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBIaWRkZW4gY29kZSBibG9jayB0byB3cml0ZSBvdXQgZGF0YSBmb3Iga25pdHRpbmcKIyBzYXZlLmltYWdlKGZpbGUgPSAicmRhdGEvUnVubmluZ0RhdGEuUkRhdGEiKQpgYGAKCi0tLQoKVGhlc2UgbWF0ZXJpYWxzIGhhdmUgYmVlbiBhZGFwdGVkIGFuZCBleHRlbmRlZCBmcm9tIG1hdGVyaWFscyBsaXN0ZWQgYWJvdmUuIFRoZXNlIGFyZSBvcGVuIGFjY2VzcyBtYXRlcmlhbHMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBbQ3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbiBsaWNlbnNlIChDQyBCWSA0LjApXShodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS80LjAvKSwgd2hpY2ggcGVybWl0cyB1bnJlc3RyaWN0ZWQgdXNlLCBkaXN0cmlidXRpb24sIGFuZCByZXByb2R1Y3Rpb24gaW4gYW55IG1lZGl1bSwgcHJvdmlkZWQgdGhlIG9yaWdpbmFsIGF1dGhvciBhbmQgc291cmNlIGFyZSBjcmVkaXRlZC4K