In this module, we will learn:
- What a p-value does (and does not!) represent
- How to modify and fit our DE model
- How to generate tables of DE statistics
- The importance multiple hypothesis corrections
Differential Expression Workflow
Now we will consider what we learned from our QC results before
testing for differential expression between samples in our groups of
interest.
DESeq2 Model Fitting
Based on our PCA results, we’ll copy our DESeq object and revise our
original model design to add a covariate for “batch”. Then, we’ll fit
our model using the DESeq
function and take a look at the
objects we generate. This command applies the model to our data, using
the sample information supplied and model dds_batch
object
so can take some time to run.
# access model slot from original `dds_filtered` DESeq2 object
design(dds_filtered)
~condition
# copy original object and then modify model
dds_batch_filtered = dds_filtered
design(dds_batch_filtered) <- ~ condition + batch
# after updating model, fit model with `DESeq` function
?DESeq
dds_batch_fitted = DESeq(dds_batch_filtered)
dds_batch_fitted
class: DESeqDataSet
dim: 16249 6
metadata(1): version
assays(4): counts mu H cooks
rownames(16249): ENSMUSG00000000001 ENSMUSG00000000028 ...
ENSMUSG00000118651 ENSMUSG00000118653
rowData names(26): baseMean baseVar ... deviance maxCooks
colnames(6): sample_A sample_B ... sample_E sample_F
colData names(4): genotype condition batch sizeFactor
While we won’t be able to delve into model options in detail, more
complex model designs including adding “interaction terms” between
multiple group labels, are helpfully described in this support thread
as well as in the DESeq2
vignette.
Click for example of model design check
When including multiple terms in our model it’s helpful to check the
corresponding design matrix to ensure that our batches are not
confounded, which would cause DESeq2
to return a
model not full rank
error when attempting to fit the model.
We can do that using the model.matrix
function, providing
our intended model and our sample information.
model.matrix(~ condition + batch, samplesheet_batch)
(Intercept) conditiondeficient batchDay2
sample_A 1 0 0
sample_B 1 0 1
sample_C 1 0 1
sample_D 1 1 0
sample_E 1 1 1
sample_F 1 1 1
attr(,"assign")
[1] 0 1 2
attr(,"contrasts")
attr(,"contrasts")$condition
[1] "contr.treatment"
attr(,"contrasts")$batch
[1] "contr.treatment"
When we look at the outputs, we can see that all of the returned columns
have values (1
) included. If our model was not full rank,
then we would see a columns with no values (all 0
)
returned.
Dispersion models and possible warning messages
Depending on the data set you are analyzing, you may see a warning
that the default ‘parametric’ dispersion model so a local regression was
substituted. When seeing this warning, we recommend looking at a
dispersion plot with the plotDispEsts(dds)
function, but as
this bioconductor
thread discusses, other visualizations of our data might be more
helpful and/or easier to interpret why the data doesn’t fit the default
parametric model.
Click for code for a plot of dispersion estimates
We can visualize the dispersion estimates with the
plotDispEsts
function. This plot shows the the DESeq2
normalization results for our data, which centers on shrinking the
variance across all genes to better fit the expected spread at a given
expression level.
plotDispEsts(dds_batch_fitted)
Above is the raw data plotted in black, the fitted (or expected)
dispersion in red, and the normalized data with scaled variance in blue.
Since we have fairly small sample sizes for each condition, we see
shrinkage for many genes but a reasonable correlation between the
expression level and dispersions.
This HBC
tutorial has a more detailed overview of estimating size factors,
estimating gene dispersion, and the shrinkage procedure, as well as
examples of concerning dispersion plots that may suggest reassessing
quality of the experimental data.
Notice that there is now more information in the
DESeqDataSet
object than there was prior to our
normalization. There is information about the model fit and about the
library size normalization. DESeq2 will use this information when we
perform the test for differential expression.
The DESeq()
function is actually doing three things
automatically for us. It calculates:
- The size factors to normalize for library size with
estimateSizeFactors(dds_filtered)
,
- Dispersion estimates to shrink the dispersions with
estimateDispersions(dds_filtered)
, and
- The Wald test statistics with
nbinomWaldTest(dds_filtered)
.
The resultsNames()
function returns the names of the
estimated effects of the model. Note that the results are stored based
on the levels we set in the sample sheet and that the convention is to
always have “case” first versus “control” second.
resultsNames(dds_batch_fitted)
[1] "Intercept" "condition_deficient_vs_control"
[3] "batch_Day2_vs_Day1"
The results include the single comparison representing the two levels
of condition
. If there were more levels in the
condition
column, there would be more results listed here
because DESeq2 would implicitly compare all other levels to the
reference level.
Checkpoint: If you see the same results when you
execute resultsNames(dds_batch_fitted)
, please indicate
with the green ‘yes’ button. Otherwise, please use the red ‘x’ button to
get help
Testing for DE
Before showing the code for generating our tables of 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 start 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.
Now, consider needing to make this decision for 20,000 genes. Even if
all the genes had clear separation, that would take a lot of time.
However, it’s reasonable to expect to see a lot of genes with more
ambiguous expression like Gene 2.
We need a formal, reproducible, way to make this
decision!
Thankfully statistics provides the formality we want, but for any
statistical test we need to clearly state what we are testing. When
testing for differential expression we assume that for any particular
gene, there is no difference in expression between conditions.
Statisticians would call this the “null
hypothesis”.
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 allow us to determine if a
gene is differentially expressed. We need a second
number to give us an idea of how extreme that statistic is among the
distribution of possible statistics, which is the
“p-value”. The “p-value” along with
choosing a threshold will inform us if we can reject the null
hypothesis and classify a gene as differentially expressed.
Imagine shuffling the group labels and recomputing the Wald 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 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
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_deficient_vs_control = results(dds_batch_fitted, name = 'condition_deficient_vs_control')
head(results_deficient_vs_control)
log2 fold change (MLE): condition deficient vs control
Wald test p-value: condition deficient vs control
DataFrame with 6 rows and 6 columns
baseMean log2FoldChange lfcSE stat pvalue
<numeric> <numeric> <numeric> <numeric> <numeric>
ENSMUSG00000000001 1489.83039 0.278343 0.148278 1.877165 0.0604955
ENSMUSG00000000028 1748.93544 0.221671 0.128835 1.720588 0.0853256
ENSMUSG00000000031 2151.87715 0.135823 0.284313 0.477724 0.6328467
ENSMUSG00000000037 24.91672 0.599542 0.562067 1.066673 0.2861195
ENSMUSG00000000049 7.78377 -1.227258 1.148327 -1.068735 0.2851890
ENSMUSG00000000056 19653.54030 -0.201183 0.167344 -1.202215 0.2292803
padj
<numeric>
ENSMUSG00000000001 0.324515
ENSMUSG00000000028 0.385578
ENSMUSG00000000031 0.866812
ENSMUSG00000000037 NA
ENSMUSG00000000049 NA
ENSMUSG00000000056 0.596345
If we look at the results table, we see that the row names are gene
identifiers (in this case ENSEMBL IDs because that’s what the GTF we
used in the call to RSEM+STAR) and then we see the following
columns:
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
deficient
over the denominator group, control
.
If the value is positive, that means the expression of that gene is
greater across the deficient
samples than across the
control
samples. If the value is negative, that means the
expression of that gene is greater across the deficient
samples.
lfcSE
is the standard error for the log2 fold change
estimate.
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. deficient
/ control
) is:
alt_results_deficient_vs_control = results(dds_batch_fitted, contrast = c('condition', 'deficient', 'control'))
head(alt_results_deficient_vs_control)
log2 fold change (MLE): condition deficient vs control
Wald test p-value: condition deficient vs control
DataFrame with 6 rows and 6 columns
baseMean log2FoldChange lfcSE stat pvalue
<numeric> <numeric> <numeric> <numeric> <numeric>
ENSMUSG00000000001 1489.83039 0.278343 0.148278 1.877165 0.0604955
ENSMUSG00000000028 1748.93544 0.221671 0.128835 1.720588 0.0853256
ENSMUSG00000000031 2151.87715 0.135823 0.284313 0.477724 0.6328467
ENSMUSG00000000037 24.91672 0.599542 0.562067 1.066673 0.2861195
ENSMUSG00000000049 7.78377 -1.227258 1.148327 -1.068735 0.2851890
ENSMUSG00000000056 19653.54030 -0.201183 0.167344 -1.202215 0.2292803
padj
<numeric>
ENSMUSG00000000001 0.324515
ENSMUSG00000000028 0.385578
ENSMUSG00000000031 0.866812
ENSMUSG00000000037 NA
ENSMUSG00000000049 NA
ENSMUSG00000000056 0.596345
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.
If we continue to look at the results table, after the
lfcSE
column we see:
stat
is the calculated Wald statistic for that
gene.
pvalue
is the nominal significance that we
described earlier.
padj
is the adjusted p-value (also known as a
“q-value”) and is what we use for determining significantly differently
expressed genes.
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 reported in the table is the result of a single test for
a single gene. As stated earlier - with a significance cut-off of
p-value < 0.05, we’re expecting a 5% chance it is a false positive.
However the more genes we test, the greater chance we have of seeing a
significant results by chance.
The multiple hypothesis problem
For example, if we consider running the Wald test for 20 genes
instead of just three, we might see something like this where
Gene N
has a p-value below our threshold of 0.05. If we see
this pattern, do we expect that Gene N
is actually
differentially expressed (e.g. a true positive)?
What if we tested 100 genes? How many genes would we expect to have
p-values < 0.05, just by chance?
What if we tested 10,000 genes? We would expect to see 500 genes with
p-values < 0.05, just by chance!
So if we are testing 20,000 genes for differential
expression, we would expect to see ~1,000 genes (5%) with statistical
significance, just by chance.
The FDR solution
To address this multiple
hypothesis correction can be performed. While there are a few
approaches, the default method in DESeq2 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 find 500 genes that are
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 correction.
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.
Save fitted model and data as .rds
It can be useful to save key R objects to file as we proceed through
our analysis - before we do that, let’s look at the documentation for
the save
function to see if it does what we want.
?save
As we can see from the documentation the function save
writes an “external representation” of R objects that can be read back
from the file at a later date by using the function load
or
attach
in most cases. We’ll proceed with saving our
dds_batch_fitted
object, creating a subdirectory first.
dir.create("outputs/Robjs", recursive=TRUE)
Warning in dir.create("outputs/Robjs", recursive = TRUE): 'outputs/Robjs'
already exists
save(dds_batch_fitted,
file="outputs/Robjs/dds_batch_fitted.rds")
Summary
In this section, we:
- Fitted our DESeq2 model
- 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
- Saved an Robject from our environment to file
Now that we’ve generated our differential comparisons and have an
understanding of our results, including multiple hypothesis correction,
we can determine how many genes are differentially expressed between our
conditions and how to visualize our results.
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.
LS0tCnRpdGxlOiAiREUgVGVzdGluZyIKYXV0aG9yOiAiVU0gQmlvaW5mb3JtYXRpY3MgQ29yZSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgICAgICAgaHRtbF9kb2N1bWVudDoKICAgICAgICAgICAgaW5jbHVkZXM6CiAgICAgICAgICAgICAgICBpbl9oZWFkZXI6IGhlYWRlci5odG1sCiAgICAgICAgICAgIHRoZW1lOiBwYXBlcgogICAgICAgICAgICB0b2M6IHRydWUKICAgICAgICAgICAgdG9jX2RlcHRoOiA0CiAgICAgICAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICAgICAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICAgICAgICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICAgICAgICAgIG1hcmtkb3duOiBHRk0KICAgICAgICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CmJvZHksIHRkIHsKICAgZm9udC1zaXplOiAxOHB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDEycHg7Cn0KcHJlIHsKICBmb250LXNpemU6IDEycHgKfQo8L3N0eWxlPgoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0Kc291cmNlKCIuLi9iaW4vY2h1bmstb3B0aW9ucy5SIikKa25pdHJfZmlnX3BhdGgoIjEwLSIpCmBgYAoKSW4gdGhpcyBtb2R1bGUsIHdlIHdpbGwgbGVhcm46CgoqIFdoYXQgYSBwLXZhbHVlIGRvZXMgKGFuZCBkb2VzIG5vdCEpIHJlcHJlc2VudAoqIEhvdyB0byBtb2RpZnkgYW5kIGZpdCBvdXIgREUgbW9kZWwKKiBIb3cgdG8gZ2VuZXJhdGUgdGFibGVzIG9mIERFIHN0YXRpc3RpY3MKKiBUaGUgaW1wb3J0YW5jZSBtdWx0aXBsZSBoeXBvdGhlc2lzIGNvcnJlY3Rpb25zCgo8YnI+CgpgYGB7ciBNb2R1bGVzLCBldmFsPVRSVUUsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRwbHlyKQojIGxvYWQoInJkYXRhL1J1bm5pbmdEYXRhLlJEYXRhIikKYGBgCgojIERpZmZlcmVudGlhbCBFeHByZXNzaW9uIFdvcmtmbG93IHsudW5saXN0ZWQgLnVubnVtYmVyZWR9CgpOb3cgd2Ugd2lsbCBjb25zaWRlciB3aGF0IHdlIGxlYXJuZWQgZnJvbSBvdXIgUUMgcmVzdWx0cyBiZWZvcmUgdGVzdGluZyBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYmV0d2VlbiBzYW1wbGVzIGluIG91ciBncm91cHMgb2YgaW50ZXJlc3QuCgohW10oLi9pbWFnZXMvd2F5ZmluZGVyL3dheWZpbmRlci1iYXRjaEFuZENvdmFycy5wbmcpe3dpZHRoPTc1JX0KCi0tLQoKCgojIERFU2VxMiBNb2RlbCBGaXR0aW5nCgpCYXNlZCBvbiBvdXIgUENBIHJlc3VsdHMsIHdlJ2xsIGNvcHkgb3VyIERFU2VxIG9iamVjdCBhbmQgcmV2aXNlIG91ciBvcmlnaW5hbCBtb2RlbCBkZXNpZ24gdG8gYWRkIGEgY292YXJpYXRlIGZvciAiYmF0Y2giLiBUaGVuLCB3ZSdsbCBmaXQgb3VyIG1vZGVsIHVzaW5nIHRoZSBgREVTZXFgIGZ1bmN0aW9uIGFuZCB0YWtlIGEgbG9vayBhdCB0aGUgb2JqZWN0cyB3ZSBnZW5lcmF0ZS4gVGhpcyBjb21tYW5kIGFwcGxpZXMgdGhlIG1vZGVsIHRvIG91ciBkYXRhLCB1c2luZyB0aGUgc2FtcGxlIGluZm9ybWF0aW9uIHN1cHBsaWVkIGFuZCBtb2RlbCAgYGRkc19iYXRjaGAgb2JqZWN0IHNvIGNhbiB0YWtlIHNvbWUgdGltZSB0byBydW4uCgpgYGB7ciBGaXRNb2RlbEJhdGNoLCBtZXNzYWdlPUZBTFNFfQoKIyAgYWNjZXNzIG1vZGVsIHNsb3QgZnJvbSBvcmlnaW5hbCBgZGRzX2ZpbHRlcmVkYCBERVNlcTIgb2JqZWN0CmRlc2lnbihkZHNfZmlsdGVyZWQpCgojIGNvcHkgb3JpZ2luYWwgb2JqZWN0IGFuZCB0aGVuIG1vZGlmeSBtb2RlbApkZHNfYmF0Y2hfZmlsdGVyZWQgPSBkZHNfZmlsdGVyZWQKZGVzaWduKGRkc19iYXRjaF9maWx0ZXJlZCkgPC0gfiBjb25kaXRpb24gKyBiYXRjaAoKIyBhZnRlciB1cGRhdGluZyBtb2RlbCwgZml0IG1vZGVsIHdpdGggYERFU2VxYCBmdW5jdGlvbgo/REVTZXEKZGRzX2JhdGNoX2ZpdHRlZCA9IERFU2VxKGRkc19iYXRjaF9maWx0ZXJlZCkKZGRzX2JhdGNoX2ZpdHRlZApgYGAKCjwhLS0gYWRkIGFzIGJvbnVzOiBBY2Nlc3NpbmcgZnVuY3Rpb25zIGF2YWlsYWJsZSBmb3Igc3BlY2lmaWMgb2JqZWN0IHR5cGVzIGh0dHBzOi8vcnB1YnMuY29tL21pa2Vsb3ZlL3BrZ21ldGhvZHMgCiMgc2VlIHdoYXQgZnVuY3Rpb25zIGFyZSBhdmFpbGFibGUgZm9yIGFjY2Vzc2luZy9tb2RpZnlpbmcgb3VyIERFU2VxRGF0YVNldCBvYmplY3RzCnNob3dNZXRob2RzKGNsYXNzZXM9IkRFU2VxRGF0YVNldCIsIHdoZXJlPWdldE5hbWVzcGFjZSgiREVTZXEyIikpLS0+IAoKCldoaWxlIHdlIHdvbid0IGJlIGFibGUgdG8gZGVsdmUgaW50byBtb2RlbCBvcHRpb25zIGluIGRldGFpbCwgbW9yZSBjb21wbGV4IG1vZGVsIGRlc2lnbnMgaW5jbHVkaW5nIGFkZGluZyAiaW50ZXJhY3Rpb24gdGVybXMiIGJldHdlZW4gbXVsdGlwbGUgZ3JvdXAgbGFiZWxzLCBhcmUgaGVscGZ1bGx5IGRlc2NyaWJlZCBpbiBbdGhpcyBzdXBwb3J0IHRocmVhZF0oaHR0cHM6Ly9zdXBwb3J0LmJpb2NvbmR1Y3Rvci5vcmcvcC85ODYyOC8pIGFzIHdlbGwgYXMgaW4gdGhlIFtERVNlcTIgdmlnbmV0dGVdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCNpbnRlcmFjdGlvbnMpLgoKPGRldGFpbHM+CiAgICA8c3VtbWFyeT4qQ2xpY2sgZm9yIGV4YW1wbGUgb2YgbW9kZWwgZGVzaWduIGNoZWNrKjwvc3VtbWFyeT4KICAgIFdoZW4gaW5jbHVkaW5nIG11bHRpcGxlIHRlcm1zIGluIG91ciBtb2RlbCBpdCdzIGhlbHBmdWwgdG8gY2hlY2sgdGhlIGNvcnJlc3BvbmRpbmcgZGVzaWduIG1hdHJpeCB0byBlbnN1cmUgdGhhdCBvdXIgYmF0Y2hlcyBhcmUgbm90IGNvbmZvdW5kZWQsIHdoaWNoIHdvdWxkIGNhdXNlIGBERVNlcTJgIHRvIHJldHVybiBhIGBtb2RlbCBub3QgZnVsbCByYW5rYCBlcnJvciB3aGVuIGF0dGVtcHRpbmcgdG8gZml0IHRoZSBtb2RlbC4gV2UgY2FuIGRvIHRoYXQgdXNpbmcgdGhlIGBtb2RlbC5tYXRyaXhgIGZ1bmN0aW9uLCBwcm92aWRpbmcgb3VyIGludGVuZGVkIG1vZGVsIGFuZCBvdXIgc2FtcGxlIGluZm9ybWF0aW9uLiAKCmBgYHtyIFRlc3RJbnRlcmFjdGlvbnN9Cm1vZGVsLm1hdHJpeCh+IGNvbmRpdGlvbiArIGJhdGNoLCBzYW1wbGVzaGVldF9iYXRjaCkKYGBgCiAgV2hlbiB3ZSBsb29rIGF0IHRoZSBvdXRwdXRzLCB3ZSBjYW4gc2VlIHRoYXQgYWxsIG9mIHRoZSByZXR1cm5lZCBjb2x1bW5zIGhhdmUgdmFsdWVzIChgMWApIGluY2x1ZGVkLiBJZiBvdXIgbW9kZWwgd2FzIG5vdCBmdWxsIHJhbmssIHRoZW4gd2Ugd291bGQgc2VlIGEgY29sdW1ucyB3aXRoIG5vIHZhbHVlcyAoYWxsIGAwYCkgcmV0dXJuZWQuCjwvZGV0YWlscz4KPGJyPgoKPGRldGFpbHM+CiAgICA8c3VtbWFyeT4qRGlzcGVyc2lvbiBtb2RlbHMgYW5kIHBvc3NpYmxlIHdhcm5pbmcgbWVzc2FnZXMqPC9zdW1tYXJ5PgoKICAgRGVwZW5kaW5nIG9uIHRoZSBkYXRhIHNldCB5b3UgYXJlIGFuYWx5emluZywgeW91IG1heSBzZWUgYSB3YXJuaW5nIHRoYXQgdGhlIGRlZmF1bHQgJ3BhcmFtZXRyaWMnIGRpc3BlcnNpb24gbW9kZWwgc28gYSBsb2NhbCByZWdyZXNzaW9uIHdhcyBzdWJzdGl0dXRlZC4gV2hlbiBzZWVpbmcgdGhpcyB3YXJuaW5nLCB3ZSByZWNvbW1lbmQgbG9va2luZyBhdCBhIGRpc3BlcnNpb24gcGxvdCB3aXRoIHRoZSBgcGxvdERpc3BFc3RzKGRkcylgIGZ1bmN0aW9uLCBidXQgYXMgW3RoaXMgYmlvY29uZHVjdG9yIHRocmVhZF0oaHR0cHM6Ly9zdXBwb3J0LmJpb2NvbmR1Y3Rvci5vcmcvcC8xMDc5MzcvKSBkaXNjdXNzZXMsIG90aGVyIHZpc3VhbGl6YXRpb25zIG9mIG91ciBkYXRhIG1pZ2h0IGJlIG1vcmUgaGVscGZ1bCBhbmQvb3IgZWFzaWVyIHRvIGludGVycHJldCB3aHkgdGhlIGRhdGEgZG9lc24ndCBmaXQgdGhlIGRlZmF1bHQgcGFyYW1ldHJpYyBtb2RlbC4KCjwvZGV0YWlscz4KPGJyPgo8YnI+Cgo8ZGV0YWlscz4KICAgIDxzdW1tYXJ5PipDbGljayBmb3IgY29kZSBmb3IgYSBwbG90IG9mIGRpc3BlcnNpb24gZXN0aW1hdGVzKjwvc3VtbWFyeT4KICAgIFdlIGNhbiB2aXN1YWxpemUgdGhlICoqZGlzcGVyc2lvbiBlc3RpbWF0ZXMqKiB3aXRoIHRoZSBgcGxvdERpc3BFc3RzYCBmdW5jdGlvbi4gVGhpcyBwbG90IHNob3dzIHRoZSB0aGUgREVTZXEyIG5vcm1hbGl6YXRpb24gcmVzdWx0cyBmb3Igb3VyIGRhdGEsIHdoaWNoIGNlbnRlcnMgb24gc2hyaW5raW5nIHRoZSB2YXJpYW5jZSBhY3Jvc3MgYWxsIGdlbmVzIHRvIGJldHRlciBmaXQgdGhlIGV4cGVjdGVkIHNwcmVhZCBhdCBhIGdpdmVuIGV4cHJlc3Npb24gbGV2ZWwuCmBgYHtyIENoZWNrRGlzcGVyc2lvbnN9CnBsb3REaXNwRXN0cyhkZHNfYmF0Y2hfZml0dGVkKQpgYGAKICAgIAogICBBYm92ZSBpcyB0aGUgcmF3IGRhdGEgcGxvdHRlZCBpbiBibGFjaywgdGhlIGZpdHRlZCAob3IgZXhwZWN0ZWQpIGRpc3BlcnNpb24gaW4gcmVkLCBhbmQgdGhlIG5vcm1hbGl6ZWQgZGF0YSB3aXRoIHNjYWxlZCB2YXJpYW5jZSBpbiBibHVlLiBTaW5jZSB3ZSBoYXZlIGZhaXJseSBzbWFsbCBzYW1wbGUgc2l6ZXMgZm9yIGVhY2ggY29uZGl0aW9uLCB3ZSBzZWUgc2hyaW5rYWdlIGZvciBtYW55IGdlbmVzIGJ1dCBhIHJlYXNvbmFibGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgZXhwcmVzc2lvbiBsZXZlbCBhbmQgZGlzcGVyc2lvbnMuCgogICBUaGlzIFtIQkMgdHV0b3JpYWxdKGh0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL0RHRV93b3Jrc2hvcC9sZXNzb25zLzA0X0RHRV9ERVNlcTJfYW5hbHlzaXMuaHRtbCkgaGFzIGEgbW9yZSBkZXRhaWxlZCBvdmVydmlldyBvZiBlc3RpbWF0aW5nIHNpemUgZmFjdG9ycywgZXN0aW1hdGluZyBnZW5lIGRpc3BlcnNpb24sIGFuZCB0aGUgc2hyaW5rYWdlIHByb2NlZHVyZSwgYXMgd2VsbCBhcyBleGFtcGxlcyBvZiBjb25jZXJuaW5nIGRpc3BlcnNpb24gcGxvdHMgdGhhdCBtYXkgc3VnZ2VzdCByZWFzc2Vzc2luZyBxdWFsaXR5IG9mIHRoZSBleHBlcmltZW50YWwgZGF0YS4KPC9kZXRhaWxzPgo8YnI+CgpOb3RpY2UgdGhhdCB0aGVyZSBpcyBub3cgbW9yZSBpbmZvcm1hdGlvbiBpbiB0aGUgYERFU2VxRGF0YVNldGAgb2JqZWN0IHRoYW4gdGhlcmUgd2FzIHByaW9yIHRvIG91ciBub3JtYWxpemF0aW9uLiBUaGVyZSBpcyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgbW9kZWwgZml0IGFuZCBhYm91dCB0aGUgbGlicmFyeSBzaXplIG5vcm1hbGl6YXRpb24uIERFU2VxMiB3aWxsIHVzZSB0aGlzIGluZm9ybWF0aW9uIHdoZW4gd2UgcGVyZm9ybSB0aGUgdGVzdCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24uCgpUaGUgYERFU2VxKClgIGZ1bmN0aW9uIGlzIGFjdHVhbGx5IGRvaW5nIHRocmVlIHRoaW5ncyBhdXRvbWF0aWNhbGx5IGZvciB1cy4gSXQgY2FsY3VsYXRlczoKCjEuIFRoZSBzaXplIGZhY3RvcnMgdG8gbm9ybWFsaXplIGZvciBsaWJyYXJ5IHNpemUgd2l0aCBgZXN0aW1hdGVTaXplRmFjdG9ycyhkZHNfZmlsdGVyZWQpYCwKMi4gRGlzcGVyc2lvbiBlc3RpbWF0ZXMgdG8gc2hyaW5rIHRoZSBkaXNwZXJzaW9ucyB3aXRoIGBlc3RpbWF0ZURpc3BlcnNpb25zKGRkc19maWx0ZXJlZClgLCBhbmQKMy4gVGhlIFdhbGQgdGVzdCBzdGF0aXN0aWNzIHdpdGggYG5iaW5vbVdhbGRUZXN0KGRkc19maWx0ZXJlZClgLgoKVGhlIGByZXN1bHRzTmFtZXMoKWAgZnVuY3Rpb24gcmV0dXJucyB0aGUgbmFtZXMgb2YgdGhlIGVzdGltYXRlZCBlZmZlY3RzIG9mIHRoZSBtb2RlbC4gTm90ZSB0aGF0ICB0aGUgcmVzdWx0cyBhcmUgc3RvcmVkIGJhc2VkIG9uIHRoZSBsZXZlbHMgd2Ugc2V0IGluIHRoZSBzYW1wbGUgc2hlZXQgYW5kIHRoYXQgdGhlIGNvbnZlbnRpb24gaXMgdG8gYWx3YXlzIGhhdmUgImNhc2UiIGZpcnN0IHZlcnN1cyAiY29udHJvbCIgc2Vjb25kLgoKYGBge3IgRml0TW9kZWxTdGFuZGFyZENoZWNrMSwgZXZhbD1UUlVFfQpyZXN1bHRzTmFtZXMoZGRzX2JhdGNoX2ZpdHRlZCkKYGBgCgpUaGUgcmVzdWx0cyBpbmNsdWRlIHRoZSBzaW5nbGUgY29tcGFyaXNvbiByZXByZXNlbnRpbmcgdGhlIHR3byBsZXZlbHMgb2YgYGNvbmRpdGlvbmAuIElmIHRoZXJlIHdlcmUgbW9yZSBsZXZlbHMgaW4gdGhlIGBjb25kaXRpb25gIGNvbHVtbiwgdGhlcmUgd291bGQgYmUgbW9yZSByZXN1bHRzIGxpc3RlZCBoZXJlIGJlY2F1c2UgREVTZXEyIHdvdWxkIGltcGxpY2l0bHkgY29tcGFyZSBhbGwgb3RoZXIgbGV2ZWxzIHRvIHRoZSByZWZlcmVuY2UgbGV2ZWwuCgoKKipDaGVja3BvaW50Kio6ICpJZiB5b3Ugc2VlIHRoZSBzYW1lIHJlc3VsdHMgd2hlbiB5b3UgZXhlY3V0ZSBgcmVzdWx0c05hbWVzKGRkc19iYXRjaF9maXR0ZWQpYCwgcGxlYXNlIGluZGljYXRlIHdpdGggdGhlIGdyZWVuICd5ZXMnIGJ1dHRvbi4gT3RoZXJ3aXNlLCBwbGVhc2UgdXNlIHRoZSByZWQgJ3gnIGJ1dHRvbiB0byBnZXQgaGVscCoKCgojIFRlc3RpbmcgZm9yIERFCgpCZWZvcmUgc2hvd2luZyB0aGUgY29kZSBmb3IgZ2VuZXJhdGluZyBvdXIgdGFibGVzIG9mIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHJlc3VsdHMsIGxldCdzIHdhbGsgdGhyb3VnaCBzb21lIHRveSBkYXRhIGFuZCBkaXNjdXNzIG91ciBpbnR1aXRpb24sIGl0cyBsaW1pdGF0aW9ucywgYW5kIHdoYXQgd2UgY2FuIGRvIHRvIGZvcm1hbGl6ZSBvdXIgdGhvdWdodCBhcm91bmQgZGV0ZXJtaW5pbmcgaWYgYSBnZW5lIGlzIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZC4KCkxldCdzIHN0YXJ0IGJ5IGxvb2tpbmcgYXQgZXhwcmVzc2lvbiBib3hwbG90cyBmb3IgYSBmZXcgImdlbmVzIiB3aGVyZSB3ZSd2ZSBtYWRlIHVwIHRoZSBkYXRhLiBJbiBlYWNoIHBsb3QsIHdlIGFyZSBjb21wYXJpbmcgdGhlIGV4cHJlc3Npb24gbGV2ZWxzIChvbiB0aGUgeS1heGlzKSBmb3Igc2FtcGxlcyAoZWFjaCBwb2ludCkgcmVwcmVzZW50aW5nIGEgV1QgKHJlZCkgYW5kIEtPICh0ZWFsKSBjb25kaXRpb24uIFRoZSBib3hwbG90IHNob3dzIHRoZSAyNSUgLSA3NSUgZGlzdHJpYnV0aW9uLCBhbG9uZyB3aXRoIG91dGxpZXJzLCB3aXRoIGEgYmFyIHJlcHJlc2VudGluZyB0aGUgbWVkaWFuIHZhbHVlLCBhbmQgYSBibGFjay1jcm9zc2VkLXBvaW50IHJlcHJlc2VudGluZyB0aGUgbWVhbi4KCkZvciB0aGUgZmlyc3QgZXhhbXBsZSwgd2UgYXNrOiBEb2VzIHRoZSBLTyBhZmZlY3QgdGhlIGV4cHJlc3Npb24gb2YgdGhlIGdlbmU/CgohW10oLi9pbWFnZXMvTW9kdWxlMTBfc3RhdF9wbG90X0dlbmVfMy5wbmcpe3dpZHRoPTc1JX0KCkl0IHNlZW1zIHByZXR0eSBjbGVhciB0aGF0IHRoZXJlIGlzIGEgbGFyZ2UgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBtZWFucyBvZiB0aGUgdHdvIGdyb3VwcywgYW5kIHdpdGhpbi1ncm91cCBzcHJlYWQgaXMgcXVpdGUgbG93LiBTbyB0aGUgYW5zd2VyIHRvIHRoZSBxdWVzdGlvbiBpcyBsaWtlbHkgIlllcyIuCgpDb25zaWRlciBhIHNlY29uZCBleGFtcGxlOgoKIVtdKC4vaW1hZ2VzL01vZHVsZTEwX3N0YXRfcGxvdF9HZW5lXzEucG5nKXt3aWR0aD03NSV9CgpIZXJlIHRoZXJlIGlzbid0IGEgbGFyZ2UgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBtZWFucyBvZiB0aGUgdHdvIGdyb3VwcywgYW5kIHRoZXJlIGlzIHF1aXRlIGEgYml0IG9mIHNwcmVhZCB3aXRoaW4tZ3JvdXAuIFRoZSBhbnN3ZXIgaGVyZSBpcyBsaWtlbHkgIk5vIi4KCkZpbmFsbHksIGNvbnNpZGVyIGEgdGhpcmQgZXhhbXBsZToKCiFbXSguL2ltYWdlcy9Nb2R1bGUxMF9zdGF0X3Bsb3RfR2VuZV8yLnBuZyl7d2lkdGg9NzUlfQoKVGhlIG1lYW5zIGFyZSBub3Qgc28gY2xvc2UgdG8gZWFjaCBvdGhlciBpbiB0aGlzIGV4YW1wbGUsIHRob3VnaCB0aGVyZSBpcyBzdGlsbCBxdWl0ZSBhIGJpdCBvZiBzcHJlYWQuIFRoaXMgaXMgZXhhbXBsZSBpcyBwZXJoYXBzIGxlc3MgY2xlYXIuCgpMb29raW5nIGF0IGFsbCB0aHJlZSBvZiB0aGUgaHlwb3RoZXRpY2FsIGdlbmVzIHRvZ2V0aGVyLCB3ZSBzZWUgdGhlbSBpbiByZWxhdGlvbiB0byBvbmUgYW5vdGhlciBhbmQgaG93IHRoZXkgc3BhbiB0aGUgcmFuZ2UgZnJvbSBObywgdG8gTWF5YmU/LCB0byBEZWZpbml0ZWx5LgoKIVtdKC4vaW1hZ2VzL01vZHVsZTEwX3N0YXRfcGxvdC5wbmcpCgpOb3csIGNvbnNpZGVyIG5lZWRpbmcgdG8gbWFrZSB0aGlzIGRlY2lzaW9uIGZvciAyMCwwMDAgZ2VuZXMuIEV2ZW4gaWYgYWxsIHRoZSBnZW5lcyBoYWQgY2xlYXIgc2VwYXJhdGlvbiwgdGhhdCB3b3VsZCB0YWtlIGEgbG90IG9mIHRpbWUuIEhvd2V2ZXIsIGl0J3MgcmVhc29uYWJsZSB0byBleHBlY3QgdG8gc2VlIGEgbG90IG9mIGdlbmVzIHdpdGggbW9yZSBhbWJpZ3VvdXMgZXhwcmVzc2lvbiBsaWtlIEdlbmUgMi4KCioqV2UgbmVlZCBhIGZvcm1hbCwgcmVwcm9kdWNpYmxlLCB3YXkgdG8gbWFrZSB0aGlzIGRlY2lzaW9uISoqCgpUaGFua2Z1bGx5IHN0YXRpc3RpY3MgcHJvdmlkZXMgdGhlIGZvcm1hbGl0eSB3ZSB3YW50LCBidXQgZm9yIGFueSBzdGF0aXN0aWNhbCB0ZXN0IHdlIG5lZWQgdG8gY2xlYXJseSBzdGF0ZSB3aGF0IHdlIGFyZSB0ZXN0aW5nLiAgV2hlbiB0ZXN0aW5nIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiB3ZSBhc3N1bWUgdGhhdCBmb3IgYW55IHBhcnRpY3VsYXIgZ2VuZSwgdGhlcmUgaXMgbm8gZGlmZmVyZW5jZSBpbiBleHByZXNzaW9uIGJldHdlZW4gY29uZGl0aW9ucy4gU3RhdGlzdGljaWFucyB3b3VsZCBjYWxsIHRoaXMgdGhlICoqIm51bGwgaHlwb3RoZXNpcyIqKi4KCkZvciBlYWNoIGdlbmUsIERFU2VxMiBjb21wdXRlcyBhICJXYWxkIHN0YXRpc3RpYyIgd2hpY2ggaXMgYSBzaW5nbGUgbnVtYmVyIGVuY2Fwc3VsYXRpbmcgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIG1lYW5zIGFuZCB0aGUgc3ByZWFkIG9mIHRoZSBncm91cHMuIEhvd2V2ZXIsIHRoaXMgbnVtYmVyIGFsb25lIGRvZXNuJ3QgYWxsb3cgdXMgdG8gZGV0ZXJtaW5lIGlmIGEgZ2VuZSBpcyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQuIFdlIG5lZWQgYSAqKnNlY29uZCoqIG51bWJlciB0byBnaXZlIHVzIGFuIGlkZWEgb2YgaG93IGV4dHJlbWUgdGhhdCBzdGF0aXN0aWMgaXMgYW1vbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwb3NzaWJsZSBzdGF0aXN0aWNzLCB3aGljaCBpcyB0aGUgKioicC12YWx1ZSIqKi4gVGhlICoqInAtdmFsdWUiKiogYWxvbmcgd2l0aCBjaG9vc2luZyBhIHRocmVzaG9sZCB3aWxsIGluZm9ybSB1cyBpZiB3ZSBjYW4gKipyZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyoqIGFuZCBjbGFzc2lmeSBhIGdlbmUgYXMgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLgoKSW1hZ2luZSBzaHVmZmxpbmcgdGhlIGdyb3VwIGxhYmVscyBhbmQgcmVjb21wdXRpbmcgdGhlIFdhbGQgc3RhdGlzdGljIG92ZXIgYW5kIG92ZXIgYWdhaW4uIFlvdSdkIGdldCBhIGRpc3RyaWJ1dGlvbiBvZiBzdGF0aXN0aWNzIHRoYXQgd291bGQgbG9vayBzaW1pbGFyIHRvIGEgbm9ybWFsIGN1cnZlLiBUaGUgcC12YWx1ZSBlc3NlbnRpYWxseSB0ZWxscyB5b3UgaG93IGxpa2VseSB5b3UgYXJlIHRvIGhhdmUgc2VlbiB0aGUgc3RhdGlzdGljIGJ5IGNoYW5jZS4gU28gd2hlbiB3ZSBzZXQgYSBwLXZhbHVlID0gMC4wNSBhcyBhIHRocmVzaG9sZCwgd2UncmUgc2F5aW5nLCAidGhlcmUgaXMgYSA1JSBjaGFuY2UgSSdkIHNlZSBzb21ldGhpbmcgdGhpcyBleHRyZW1lIHdoZW4gdGhlcmUgd2FzIGFjdHVhbGx5IG5vIGVmZmVjdCIuIFNvIHRoZSBldmlkZW5jZSBpcyAqKnN0cm9uZyoqLCBidXQgbm90ICoqaXJvbmNsYWQqKi4KCgoKCiMgR2VuZXJhdGluZyBERSBSZXN1bHRzCgpUaGVyZSBpcyBvbmx5IHRoZSBvbmUgY29tcGFyaXNvbiBpbiB0aGUgcmVzdWx0cywgc28gd2Ugd2lsbCByZWZlciB0byBpdCBpbiB0aGUgYG5hbWVgIHBhcmFtZXRlciBvZiB0aGUgYHJlc3VsdHMoKWAgZnVuY3Rpb24sIGFuZCBhc3NpZ24gdGhlIHJlc3VsdCBhcyBhbiBvYmplY3QuCgpgYGB7ciBTZXRzUmVzdWx0fQpyZXN1bHRzX2RlZmljaWVudF92c19jb250cm9sID0gcmVzdWx0cyhkZHNfYmF0Y2hfZml0dGVkLCBuYW1lID0gJ2NvbmRpdGlvbl9kZWZpY2llbnRfdnNfY29udHJvbCcpCmhlYWQocmVzdWx0c19kZWZpY2llbnRfdnNfY29udHJvbCkKYGBgCgpJZiB3ZSBsb29rIGF0IHRoZSByZXN1bHRzIHRhYmxlLCB3ZSBzZWUgdGhhdCB0aGUgcm93IG5hbWVzIGFyZSBnZW5lIGlkZW50aWZpZXJzIChpbiB0aGlzIGNhc2UgRU5TRU1CTCBJRHMgYmVjYXVzZSB0aGF0J3Mgd2hhdCB0aGUgR1RGIHdlIHVzZWQgaW4gdGhlIGNhbGwgdG8gUlNFTStTVEFSKSBhbmQgdGhlbiB3ZSBzZWUgdGhlIGZvbGxvd2luZyBjb2x1bW5zOgoKMS4gYGJhc2VNZWFuYCBpcyB0aGUgYXZlcmFnZSBvZiB0aGUgbm9ybWFsaXplZCBjb3VudCB2YWx1ZXMsIGRpdmlkZWQgYnkgc2l6ZSBmYWN0b3JzIGFuZCB0YWtlbiBvdmVyIGFsbCBzYW1wbGVzLCBhbmQgY2FuIGJlIGludGVycHJldGVkIGFzIHRoZSByZWxhdGl2ZSBleHByZXNzaW9uIGxldmVsIG9mIHRoYXQgZ2VuZSBhY3Jvc3MgYWxsIHNhbXBsZXMuCjIuIGBsb2cyRm9sZENoYW5nZWAgaXMgdGhlIGxvZzIgdHJhbnNmb3JtZWQgcmF0aW8gb2YgdGhlIGV4cHJlc3Npb24gb2YgdGhlIG51bWVyYXRvciBncm91cCAoZmlyc3QgZ3JvdXApIG92ZXIgdGhlIGRlbm9taW5hdG9yIGdyb3VwIChzZWNvbmQgZ3JvdXAgYWZ0ZXIgInZzIikuIE5vdGUgdGhhdCBpbiBvdXIgY29tcGFyaXNvbiwgdGhlIGBsb2cyRm9sZENoYW5nZWAgY29sdW1uIGNvbXBhcmVzIHRoZSBleHByZXNzaW9uIG9mIGBkZWZpY2llbnRgIG92ZXIgdGhlIGRlbm9taW5hdG9yIGdyb3VwLCBgY29udHJvbGAuIElmIHRoZSB2YWx1ZSBpcyBwb3NpdGl2ZSwgdGhhdCBtZWFucyB0aGUgZXhwcmVzc2lvbiBvZiB0aGF0IGdlbmUgaXMgZ3JlYXRlciBhY3Jvc3MgdGhlIGBkZWZpY2llbnRgIHNhbXBsZXMgdGhhbiBhY3Jvc3MgdGhlIGBjb250cm9sYCBzYW1wbGVzLiBJZiB0aGUgdmFsdWUgaXMgbmVnYXRpdmUsIHRoYXQgbWVhbnMgdGhlIGV4cHJlc3Npb24gb2YgdGhhdCBnZW5lIGlzIGdyZWF0ZXIgYWNyb3NzIHRoZSBgZGVmaWNpZW50YCBzYW1wbGVzLgozLiBgbGZjU0VgIGlzIHRoZSBzdGFuZGFyZCBlcnJvciBmb3IgdGhlIGxvZzIgZm9sZCBjaGFuZ2UgZXN0aW1hdGUuCgo+ICMgTm90ZTogYHJlc3VsdHMoKWAgZGVmYXVsdHMgey51bmxpc3RlZCAudW5udW1iZXJlZH0KPiBJZiBubyBhcmd1bWVudHMgYXJlIHBhc3NlZCB0byBgcmVzdWx0cygpYCwgdGhlbiB0aGUgbG9nMiBmb2xkIGNoYW5nZXMgYW5kIFdhbGQgdGVzdCBwLXZhbHVlIHdpbGwgYmUgZm9yIHRoZSAqKmxhc3QgdmFyaWFibGUqKiBpbiB0aGUgZGVzaWduIGZvcm11bGEsIGFuZCBpZiB0aGlzIGlzIGEgZmFjdG9yLCB0aGUgY29tcGFyaXNvbiB3aWxsIGJlIHRoZSAqKmxhc3QgbGV2ZWwqKiBvdmVyIHRoZSAqKnJlZmVyZW5jZSBsZXZlbCoqLiBJZiB5b3Ugc3BlY2lmeSBgbmFtZWAsIGFzIHdlIGRpZCBhYm92ZSwgdGhlbiB0aGUgYmVoYXZpb3IgaXMgZ2l2ZW4gYnkgdGhlIG5hbWUgdXNlZCBmcm9tIGByZXN1bHRzTmFtZXMoKWAuCgpUaGVyZSBhcmUgbXVsdGlwbGUgd2F5cyB0byBzcGVjaWZ5IHRoZSB0ZXN0IHRvIGJlIGRvbmUgdXNpbmcgdGhlIGByZXN1bHRzKClgIGZ1bmN0aW9uLiBJdCBpcyBlc3BlY2lhbGx5IGhlbHBmdWwgdG8ga25vdyB0aGlzIHdoZW4gZml0dGluZyBtb3JlIGNvbXBsZXggbW9kZWxzIGFuZCB0ZXN0aW5nIG1vcmUgY29tcGxleCBjb250cmFzdHMuIFRvIGRlbW9uc3RyYXRlIHRoaXMsIGNvbnNpZGVyIHRoaXMgZGVzY3JpcHRpb24gZnJvbSB0aGUgaGVscCBmb3IgYHJlc3VsdHMoKWA6Cgo+IGBjb250cmFzdGA6IGEgY2hhcmFjdGVyIHZlY3RvciB3aXRoIGV4YWN0bHkgdGhyZWUgZWxlbWVudHM6IHRoZSBuYW1lIG9mIGEgZmFjdG9yIGluIHRoZSBkZXNpZ24gZm9ybXVsYSwgdGhlIG5hbWUgb2YgdGhlIG51bWVyYXRvciBsZXZlbCBmb3IgdGhlIGZvbGQgY2hhbmdlLCBhbmQgdGhlIG5hbWUgb2YgdGhlIGRlbm9taW5hdG9yIGxldmVsIGZvciB0aGUgZm9sZCBjaGFuZ2UKClNvIGFuIGFsdGVybmF0aXZlIHdheSB0byB0ZXN0IHRoZSBzYW1lIGNvbnRyYXN0IGFzIGFib3ZlIChpLmUuICBgZGVmaWNpZW50YCAvIGBjb250cm9sYCkgaXM6CgpgYGB7ciBTZXRzUmVzdWx0c0FsdH0KYWx0X3Jlc3VsdHNfZGVmaWNpZW50X3ZzX2NvbnRyb2wgPSByZXN1bHRzKGRkc19iYXRjaF9maXR0ZWQsIGNvbnRyYXN0ID0gYygnY29uZGl0aW9uJywgJ2RlZmljaWVudCcsICdjb250cm9sJykpCmhlYWQoYWx0X3Jlc3VsdHNfZGVmaWNpZW50X3ZzX2NvbnRyb2wpCmBgYAoKVGhpcyB3YXkgb2YgY2FsbGluZyBgcmVzdWx0cygpYCBpcyBlc3BlY2lhbGx5IGhlbHBmdWwgd2hlbiB0aGUgbGV2ZWxzIG9mIHRoZSBjb2x1bW4gb2YgaW50ZXJlc3QgY29udGFpbiBtb3JlIHRoYW4gdHdvIGxldmVscyBiZWNhdXNlIHlvdSBjYW4gc3BlY2lmeSBleGFjdGx5IHdoaWNoIGxldmVscyB0byB0ZXN0IHdpdGggbGl0dGxlIGNvbmZ1c2lvbi4KCklmIHdlIGNvbnRpbnVlIHRvIGxvb2sgYXQgdGhlIHJlc3VsdHMgdGFibGUsIGFmdGVyIHRoZSBgbGZjU0VgIGNvbHVtbiB3ZSBzZWU6Cgo0LiBgc3RhdGAgaXMgdGhlIGNhbGN1bGF0ZWQgV2FsZCBzdGF0aXN0aWMgZm9yIHRoYXQgZ2VuZS4KNS4gYHB2YWx1ZWAgaXMgdGhlICpub21pbmFsKiBzaWduaWZpY2FuY2UgdGhhdCB3ZSBkZXNjcmliZWQgZWFybGllci4KNi4gYHBhZGpgIGlzIHRoZSAqYWRqdXN0ZWQgcC12YWx1ZSogKGFsc28ga25vd24gYXMgYSAicS12YWx1ZSIpIGFuZCBpcyB3aGF0IHdlIHVzZSBmb3IgZGV0ZXJtaW5pbmcgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnRseSBleHByZXNzZWQgZ2VuZXMuCgoKPiAjIFF1ZXN0aW9uIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cj4gV2h5IHNob3VsZCB3ZSB1c2UgdmFsdWVzIGZyb20gYHBhZGpgIGluc3RlYWQgb2YgdGhlIGBwdmFsdWVgPyBQb3N0IGluIHRoZSBTbGFjayB0aHJlYWQuCgo8YnI+CgojIyBNdWx0aXBsZSBoeXBvdGhlc2lzIHRlc3RpbmcgYW5kIEZEUiBjb3JyZWN0aW9uCgoKRWFjaCBwLXZhbHVlIHJlcG9ydGVkIGluIHRoZSB0YWJsZSBpcyB0aGUgcmVzdWx0IG9mIGEgc2luZ2xlIHRlc3QgZm9yIGEgc2luZ2xlIGdlbmUuIEFzIHN0YXRlZCBlYXJsaWVyIC0gd2l0aCBhIHNpZ25pZmljYW5jZSBjdXQtb2ZmIG9mIHAtdmFsdWUgPCAwLjA1LCB3ZSdyZSBleHBlY3RpbmcgYSA1JSBjaGFuY2UgaXQgaXMgYSBmYWxzZSBwb3NpdGl2ZS4gSG93ZXZlciB0aGUgbW9yZSBnZW5lcyB3ZSB0ZXN0LCB0aGUgZ3JlYXRlciBjaGFuY2Ugd2UgaGF2ZSBvZiBzZWVpbmcgYSBzaWduaWZpY2FudCByZXN1bHRzIGJ5IGNoYW5jZS4gCgojIyMgVGhlIG11bHRpcGxlIGh5cG90aGVzaXMgcHJvYmxlbQoKRm9yIGV4YW1wbGUsIGlmIHdlIGNvbnNpZGVyIHJ1bm5pbmcgdGhlIFdhbGQgdGVzdCBmb3IgMjAgZ2VuZXMgaW5zdGVhZCBvZiBqdXN0IHRocmVlLCB3ZSBtaWdodCBzZWUgc29tZXRoaW5nIGxpa2UgdGhpcyB3aGVyZSBgR2VuZSBOYCBoYXMgYSBwLXZhbHVlIGJlbG93IG91ciB0aHJlc2hvbGQgb2YgMC4wNS4gSWYgd2Ugc2VlIHRoaXMgcGF0dGVybiwgZG8gd2UgZXhwZWN0IHRoYXQgYEdlbmUgTmAgaXMgYWN0dWFsbHkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIChlLmcuIGEgdHJ1ZSBwb3NpdGl2ZSk/Cgo8YnI+CiFbXSguL2ltYWdlcy9GRFItMS5wbmcpe3dpZHRoPTc1JX0KPGJyPgoKV2hhdCBpZiB3ZSB0ZXN0ZWQgMTAwIGdlbmVzPyBIb3cgbWFueSBnZW5lcyB3b3VsZCB3ZSBleHBlY3QgdG8gaGF2ZSBwLXZhbHVlcyA8IDAuMDUsIGp1c3QgYnkgY2hhbmNlPwoKPGJyPgohW10oLi9pbWFnZXMvRkRSLTIucG5nKXt3aWR0aD03NSV9Cjxicj4KCldoYXQgaWYgd2UgdGVzdGVkIDEwLDAwMCBnZW5lcz8gV2Ugd291bGQgZXhwZWN0IHRvIHNlZSA1MDAgZ2VuZXMgd2l0aCBwLXZhbHVlcyA8IDAuMDUsIGp1c3QgYnkgY2hhbmNlIQoKPGJyPgohW10oLi9pbWFnZXMvRkRSLTMucG5nKXt3aWR0aD03NSV9Cjxicj4KCioqU28gaWYgd2UgYXJlIHRlc3RpbmcgMjAsMDAwIGdlbmVzIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiwgd2Ugd291bGQgZXhwZWN0IHRvIHNlZSB+MSwwMDAgZ2VuZXMgKDUlKSB3aXRoIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZSwganVzdCBieSBjaGFuY2UuKioKPGJyPgoKIyMjIFRoZSBGRFIgc29sdXRpb24KClRvIGFkZHJlc3MgdGhpcyBbbXVsdGlwbGUgaHlwb3RoZXNpcyBjb3JyZWN0aW9uXShodHRwczovL211bHRpdGhyZWFkZWQuc3RpdGNoZml4LmNvbS9ibG9nLzIwMTUvMTAvMTUvbXVsdGlwbGUtaHlwb3RoZXNpcy10ZXN0aW5nLykgY2FuIGJlIHBlcmZvcm1lZC4gV2hpbGUgdGhlcmUgYXJlIGEgZmV3IGFwcHJvYWNoZXMsIHRoZSBkZWZhdWx0IG1ldGhvZCBpbiBERVNlcTIgaXMgdGhlIEZhbHNlIERpc2NvdmVyeSBSYXRlIChGRFIpIChbQmVuamFtaW5pIGFuZCBIb2NoYmVyZyAoMTk5NSldKGh0dHBzOi8vcnNzLm9ubGluZWxpYnJhcnkud2lsZXkuY29tL2RvaS8xMC4xMTExL2ouMjUxNy02MTYxLjE5OTUudGIwMjAzMS54KSkuCgpUaGUgZGVmYXVsdCBGRFIgcmF0ZSBjdXRvZmYgZm9yIG91ciBhbmFseXNlcyBpcyAwLjA1LCBtZWFuaW5nIHRoZSBwcm9wb3J0aW9uIG9mIGZhbHNlIHBvc2l0aXZlcyBhbW9uZ3N0IG91ciAqZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzKiBpcyBjb250cm9sbGVkIHRvIDUlLiBTbyBpZiB3ZSBmaW5kIDUwMCBnZW5lcyB0aGF0IGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgd2l0aCB0aGlzIEZEUiBjdXRvZmYsIHdlIGV4cGVjdCBvbmx5IDI1IG9mIHRoZW0gdG8gYmUgZmFsc2UgcG9zaXRpdmVzLiBERVNlcTIgdmlnbmV0dGUncyBpbmNsdWRlcyBhIFtmdXJ0aGVyIGRpc2N1c3Npb24gb2YgZmlsdGVyaW5nIGFuZCBtdWx0aXBsZSB0ZXN0aW5nIGNvcnJlY3Rpb25dKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCNpbmRlcGVuZGVudC1maWx0ZXJpbmctYW5kLW11bHRpcGxlLXRlc3RpbmcpLgoKPiAjIE5vdGUgb24gYHBhZGpgIHZhbHVlcyBzZXQgdG8gTkEgey51bmxpc3RlZCAudW5udW1iZXJlZH0KPgo+IEFzIGRpc2N1c3NlZCBpbiB0aGUgW0hCQyB0dXRvcmlhbF0oaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvMDVfREdFX0RFU2VxMl9hbmFseXNpczIuaHRtbCkgYXMgd2VsbCBhcyB0aGUgW0RFU2VxMiB2aWduZXR0ZV0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI2ktd2FudC10by1iZW5jaG1hcmstZGVzZXEyLWNvbXBhcmluZy10by1vdGhlci1kZS10b29scy4pLCBERVNlcTIgcmVkdWNlcyB0aGUgbnVtYmVyIG9mIGdlbmVzIHRoYXQgd2lsbCBiZSB0ZXN0ZWQgYnkgcmVtb3ZpbmcgZ2VuZXMgd2l0aCBsb3cgbnVtYmVyIG9mIGNvdW50cyBhbmQgb3V0bGllciBzYW1wbGVzLgo+Cj4gKiBJZiB3aXRoaW4gYSByb3csIGFsbCBzYW1wbGVzIGhhdmUgemVybyBjb3VudHMsIHRoZSBiYXNlTWVhbiBjb2x1bW4gd2lsbCBiZSB6ZXJvLCBhbmQgdGhlIGxvZzIgZm9sZCBjaGFuZ2UgZXN0aW1hdGVzLCBwLXZhbHVlIGFuZCBhZGp1c3RlZCBwLXZhbHVlIHdpbGwgYWxsIGJlIHNldCB0byBOQS4KPiAqIElmIGEgcm93IGNvbnRhaW5zIGEgc2FtcGxlIHdpdGggYW4gZXh0cmVtZSBjb3VudCBvdXRsaWVyIHRoZW4gdGhlIHAtdmFsdWUgYW5kIGFkanVzdGVkIHAtdmFsdWUgd2lsbCBiZSBzZXQgdG8gTkEuIFRoZXNlIG91dGxpZXIgY291bnRzIGFyZSBkZXRlY3RlZCBieSBbQ29va+KAmXMgZGlzdGFuY2VdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0Nvb2slMjdzX2Rpc3RhbmNlKS4KPiAqIElmIGEgcm93IGlzIGZpbHRlcmVkIGJ5IGF1dG9tYXRpYyBpbmRlcGVuZGVudCBmaWx0ZXJpbmcsIGUuZy4gZm9yIGhhdmluZyBhIGxvdyBtZWFuIG5vcm1hbGl6ZWQgY291bnQsIHRoZW4gb25seSB0aGUgYWRqdXN0ZWQgcC12YWx1ZSB3aWxsIGJlIHNldCB0byBOQS4KCgoKIyMgU2F2ZSBmaXR0ZWQgbW9kZWwgYW5kIGRhdGEgYXMgLnJkcwoKSXQgY2FuIGJlIHVzZWZ1bCB0byBzYXZlIGtleSBSIG9iamVjdHMgdG8gZmlsZSBhcyB3ZSBwcm9jZWVkIHRocm91Z2ggb3VyIGFuYWx5c2lzIC0gYmVmb3JlIHdlIGRvIHRoYXQsIGxldCdzIGxvb2sgYXQgdGhlIGRvY3VtZW50YXRpb24gZm9yIHRoZSBgc2F2ZWAgZnVuY3Rpb24gdG8gc2VlIGlmIGl0IGRvZXMgd2hhdCB3ZSB3YW50LgoKYGBge3IgU2F2ZURvY3VtZW50YXRpb259Cj9zYXZlCmBgYAoKQXMgd2UgY2FuIHNlZSBmcm9tIHRoZSBkb2N1bWVudGF0aW9uIHRoZSBmdW5jdGlvbiBgc2F2ZWAgd3JpdGVzIGFuICJleHRlcm5hbCByZXByZXNlbnRhdGlvbiIgb2YgUiBvYmplY3RzIHRoYXQgY2FuIGJlIHJlYWQgYmFjayBmcm9tIHRoZSBmaWxlIGF0IGEgbGF0ZXIgZGF0ZSBieSB1c2luZyB0aGUgZnVuY3Rpb24gYGxvYWRgIG9yIGBhdHRhY2hgIGluIG1vc3QgY2FzZXMuIFdlJ2xsIHByb2NlZWQgd2l0aCBzYXZpbmcgb3VyIGBkZHNfYmF0Y2hfZml0dGVkYCBvYmplY3QsIGNyZWF0aW5nIGEgc3ViZGlyZWN0b3J5IGZpcnN0LgoKYGBge3IgV3JpdGVGaXRNb2RlbE9iamVjdH0KZGlyLmNyZWF0ZSgib3V0cHV0cy9Sb2JqcyIsIHJlY3Vyc2l2ZT1UUlVFKQpzYXZlKGRkc19iYXRjaF9maXR0ZWQsCiAgICAgICAgICBmaWxlPSJvdXRwdXRzL1JvYmpzL2Rkc19iYXRjaF9maXR0ZWQucmRzIikKYGBgCgoKCi0tLQoKIyBTdW1tYXJ5CgpJbiB0aGlzIHNlY3Rpb24sIHdlOgoKKiBGaXR0ZWQgb3VyIERFU2VxMiBtb2RlbAoqIFBlcmZvcm1lZCBzdGF0aXN0aWNhbCB0ZXN0cyBmb3IgY29tcGFyaXNvbnMgb2YgaW50ZXJlc3QKKiBHZW5lcmF0ZWQgdGFibGVzIG9mIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHJlc3VsdHMgLSBpLmUuIGZvbGQgY2hhbmdlcyBhbmQgYWRqdXN0ZWQgcHZhbHVlcyBmb3IgZWFjaCBnZW5lIGluIGRhdGFzZXQKKiBEaXNjdXNzZWQgaW1wb3J0YW5jZSBhbmQgYXBwbGljYXRpb24gb2YgbXVsdGlwbGUgaHlwb3RoZXNpcyBjb3JyZWN0aW9uCiogU2F2ZWQgYW4gUm9iamVjdCBmcm9tIG91ciBlbnZpcm9ubWVudCB0byBmaWxlCgpOb3cgdGhhdCB3ZSd2ZSBnZW5lcmF0ZWQgb3VyIGRpZmZlcmVudGlhbCBjb21wYXJpc29ucyBhbmQgaGF2ZSBhbiB1bmRlcnN0YW5kaW5nIG9mIG91ciByZXN1bHRzLCBpbmNsdWRpbmcgbXVsdGlwbGUgaHlwb3RoZXNpcyBjb3JyZWN0aW9uLCB3ZSBjYW4gZGV0ZXJtaW5lIGhvdyBtYW55IGdlbmVzIGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYmV0d2VlbiBvdXIgY29uZGl0aW9ucyBhbmQgaG93IHRvIHZpc3VhbGl6ZSBvdXIgcmVzdWx0cy4KCi0tLQoKIyBTb3VyY2VzCgoqIEhCQyBER0UgdHJhaW5pbmcgbW9kdWxlLCBwYXJ0IDE6IGh0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL0RHRV93b3Jrc2hvcC9sZXNzb25zLzA0X0RHRV9ERVNlcTJfYW5hbHlzaXMuaHRtbAoqIEhCQyBER0UgdHJhaW5pbmcgbW9kdWxlLCBwYXJ0IDI6IGh0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL0RHRV93b3Jrc2hvcC9sZXNzb25zLzA1X0RHRV9ERVNlcTJfYW5hbHlzaXMyLmh0bWwKKiBERVNlcTIgdmlnbmV0dGU6IGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCNkaWZmZXJlbnRpYWwtZXhwcmVzc2lvbi1hbmFseXNpcwoKCgpgYGB7ciBXcml0ZU91dC5SRGF0YSwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIEhpZGRlbiBjb2RlIGJsb2NrIHRvIHdyaXRlIG91dCBkYXRhIGZvciBrbml0dGluZwojIHNhdmUuaW1hZ2UoZmlsZSA9ICJyZGF0YS9SdW5uaW5nRGF0YS5SRGF0YSIpCmBgYAoKLS0tCgpUaGVzZSBtYXRlcmlhbHMgaGF2ZSBiZWVuIGFkYXB0ZWQgYW5kIGV4dGVuZGVkIGZyb20gbWF0ZXJpYWxzIGxpc3RlZCBhYm92ZS4gVGhlc2UgYXJlIG9wZW4gYWNjZXNzIG1hdGVyaWFscyBkaXN0cmlidXRlZCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIFtDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uIGxpY2Vuc2UgKENDIEJZIDQuMCldKGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzQuMC8pLCB3aGljaCBwZXJtaXRzIHVucmVzdHJpY3RlZCB1c2UsIGRpc3RyaWJ1dGlvbiwgYW5kIHJlcHJvZHVjdGlvbiBpbiBhbnkgbWVkaXVtLCBwcm92aWRlZCB0aGUgb3JpZ2luYWwgYXV0aG9yIGFuZCBzb3VyY2UgYXJlIGNyZWRpdGVkLgoKPGJyLz4KPGJyLz4KPGhyLz4KfCBbUHJldmlvdXMgbGVzc29uXShNb2R1bGUwOV9TYW1wbGVRQ1Zpei5odG1sKSB8IFtUb3Agb2YgdGhpcyBsZXNzb25dKCN0b3ApIHwgW05leHQgbGVzc29uXShNb2R1bGUxMV9ERVZpc3VhbGl6YXRpb25zLmh0bWwpIHwKfCA6LS0tIHwgOi0tLS06IHwgLS0tOiB8Cg==