Objectives
- Overview of reproducible research & analysis setup
- Broad introduction to DESeq2 & why it is widely used for
differential expression comparisons
- How to import and review gene count table
Differential Expression Workflow
Today we will proceed through key steps in a differential expression
analysis, starting from a count table that’s similar to what you
generated in the first half of the workshop and similar to one of the
outputs included in the data
that the Advanced Genomics Core delivers for RNA-seq libraries.
Getting Started in RStudio
Log on to RStudio Server
Open a web browser to the following URL: http://bfx-workshop01.med.umich.edu
You should now be looking at a page that will allow you to login to
the RStudio server:
Enter your user credentials and click Sign In. The
credentials were provided via email, but if you forget yours, a helper
can retrieve it for you, just ask in Slack. You should now see the
RStudio interface:
Create an RStudio Project
In the Computational Foundation workshop we created a project rooted
in a particular directory and stored our data, scripts and output within
that project. Let’s do the same thing, but for the differential
expression part of this workshop.
- To create a project, go to the File menu, and click
New Project…. The following window will appear:
In this window, select Existing Directory. For
“Project working directory”, click Browse…, select the
“RSD_R” folder, and click Choose. This will use the
/home/workshop/user/RSD_R
folder as the project
directory.
Finally click Create Project. In the “Files” tab of
your output pane (more about the RStudio layout in a moment), you should
see an RStudio project file, RSD_R.Rproj. All RStudio
projects end with the “.Rproj” file extension.
Note that there is already a data/
folder which contains
the data we will use for these lessons.
Creating an R script
Let’s create an R script file to store all the code we’re going to
run in the forthcoming lessons.
- Click the File menu and select New File and
then R Script.
- Before we go any further, save your script by clicking the save/disk
icon that is in the bar above the first line in the script editor, or
click the File menu and select Save.
- In the “Save File” window that opens, select New Folder.
Name it “scripts”.
- Finally, name your file “diffex” in the “File name”
field.
The new script diffex.R is now in the
scripts
folder. You can see that by clicking the
scripts
folder in the “Files” pane. And you can go back up
to the main project folder by clicking the ..
to the right
of the up arrow in the “Files” pane. By convention, R scripts end with
the file extension .R.
Reproducible research
Today we’ll be exploring some RNA-seq data that is fairly
representative of what we see in the core and start with input files
similar to the count table you generated in the previous steps and
similar to one of the outputs currently being delivered by the Advanced
Sequencing Core.
File organization
As widely discussed, including in a review by Nobel,
2009, file organization and data stewardship are an important parts
of reproducible research.
To follow best practices for file organization for
bioinformatics/computational projects, we should make distinct locations
for:
Such as illustrated in this figure from the Noble review:
To organize our files for our analysis today, we’ll create some
directories in the RSD_R
folder which is now our project
root and current working directory. We can already see that there a
data/
folder and we just created the scripts/
folder with the RStudio GUI. Let’s create an outputs/
folder using the dir.create()
function in R.
dir.create('outputs')
dir.create('outputs/figures')
dir.create('outputs/tables')
Checkpoint: Please use the green ‘check’ if you
have saved your code file and see the data
,
scripts
, and outputs
directories in the
RSD_R
folder and the red ‘x’ if you do not.
Reminder: RStudio code execution
Ctrl+Enter is a standard shortcut in RStudio to
send the current line (or selected lines) to the console. If you see
>
in the Console, then R has executed the command. If
you see a +
, this means that the command is not complete, R
thinks there is more to your command. You can use the esc to
get out of this state.
Reminder: Object naming conventions
- Cannot start with numbers
- Cannot include dashes
- Cannot have spaces
- Should not be identical to a named function
- Dots and underscores can separate parts of names, alternatively
CamelCase accomplishes this
Tools for Differential Gene Expression analysis
As discussed during the webinar, a common application for bulk
RNA-seq is to test for differential expression between conditions or
treatments, using statistical approaches that are appropriate for
biological data.
While there are several tools that can be used for differential
expression comparisons, we will use DESeq2
in our analysis today.
DESeq2 is one of two tools, along with edgeR,
considered ‘best
practice’ for differential expression. Both tools apply similar
methods that account for the distributions we expect to see for RNA-seq
and are fairly stringent in calling differentially expressed genes,
lowering the risk of investigating genes that were really false
positives (e.g. don’t really have different expression between treatment
groups and therefore are not relevant to the biological process).
Additionally, DESeq2 also has an excellent vignette (link)
from Love, Anders, and Huber, from which our workflow is partially
adapted, and is a good resource when analyzing your own data (see also:
Love, Anders, and
Huber. Genome Biology. 2014.).
Click for additional resources regarding statistical testing and
tool comparison for RNA-seq data
To learn more about statistical testing and what distributions best
model the behavior of RNA-seq data, a good resource is this EdX
lecture by Rafael Irizarry or this lecture by Kasper
Hansen. Another helpful guide is this Comparative
Study for Differential Expression Analysis by Zhang et al. from
2014.
DESeq2 assumptions and requirements
A key assumption of DESeq2 is that biological variance is much
greater than technical variance (especially if best
practices for quality
RNA isolation are followed, including DNase treatment!).
Since calculating variance is key to the statistical approach used
for DESeq2, if we tried to compare two treatment groups with less than
two replicates, we would get an error (as shown in this blog post).
Without replicates, there can’t be statistical significance
(e.g. p-values), but qualitative approaches are an option, like
looking at the top expressed genes after normalization.
Replicates in RNA-seq experiments
A frequent question for RNA-seq projects is “How many replicates do I
need?”.
The general goal of our analyses 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.
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 and papers like
this one by Hart et
al that focus on estimating statistical power for RNA-seq
experiments.
Sequencing depth recommendations
A related experimental design consideration is how much sequencing
depth should be generated per sample. This figure shared by Illumina in
their technical talks is helpful to understand the relative importance
of replicates versus sequencing depth.
Generally, for human and mouse experiments, the recommendation is
30-40 million reads per sample for polyA library preps to capture both
highly expressed (abundant) and lowly expressed (rarer) transcripts,
assuming that ~25,000 protein-coding genes would be measured. However,
as the image above shows, sequencing depth has less of an impact than
number of replicates in detecting differentially expressed genes
(DEGs).
Exercise: Building a better understanding of
differential expression analysis
- Post a comment below regarding what key question/misconception
regarding designing an RNA-seq experiment we were able to address
OR
- Post a question that that was NOT addressed but that you hope we
will address in the later modules OR
- Add a thumbs up to your favorite comment(s) to upvote it
Getting started with analysis
Load Packages
Several packages have already been installed on the server, so we can
load them into our R session now. To do that we’ll use the
library
function to load the required packages.
library(DESeq2)
library(ggplot2)
library(tidyr)
library(dplyr)
library(matrixStats)
library(ggrepel)
library(pheatmap)
library(RColorBrewer)
library(data.table)
Note: We expect to see some red messages in your console while
these packages are loading
R/RStudio has great resources for getting help, including code
‘cheatsheets’ and package vignettes, like for tidyr.
Since we loaded the libraries into our R session, we can see
documentation out using the ?
operator.
?`DESeq2-package`
Checkpoint: If you see the R documentation for
DESeq2 pop up in your ‘help’ panel on the right, please indicate with
the green ‘check’ button. If not please use the red ‘x’ button.
Read Counts
Another key assumption for DESeq2 is that the analysis will start
with un-normalized
counts.
To begin our analysis, we’ll read in the raw count
data file, gene_expected_count.txt
which is similar to what
would be generated in the alignment steps (and what you would receive
from AGC). We’ll discuss later a few normalizations that can be helpful
for us to understand how much a gene is expressed within or between
samples, but normalized data should not be used as an
input for DESeq2.
count_table = read.table("data/gene_expected_count.txt", header = TRUE, row.names = 1)
head(count_table, n=2) # look at the top of the table
sample_A sample_B sample_C sample_D sample_E sample_F
ENSMUSG00000000001 1041 905 1296 3481 1283 1921
ENSMUSG00000000003 0 0 0 0 0 0
Now that the file is read into R, note that we’ve created a data
frame that includes ‘gene ids’ in ENSEMBL format as rownames and count
data from twelve different samples.
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). DESeq2 is expecting the count matrix to
consist only of whole numbers. Recent versions of DESeq2 will handle
this for us, but previous versions did not.
What function, that we saw early on in the Computational Foundations
R lessons, could we use to convert fractional values in
count_table
to integers?
Solution
We can use the round()
function:
count_table = round(count_table)
Getting help
R and RStudio have a strong community component so if you are getting
an error or wondering how to make a command work or how to perform a
specific task, there is likely already a solution out there. Remember
that Google is your friend, although it can sometimes be a challenge to
figure out what to search for. Key parts of a successful
search:
- Package or command run
R
or Bioconductor
- The error message if there is one
- Version information
How to get session information to aid in a search:
sessionInfo()
Highly recommend using resources like Bioconductor Support, Biostars, and Stack Overflow,
including threads on specific packages or common bioinformatic
tasks.
I personally use one or more of these resources every
day.
10x Genomics has a helpful 10
tips for biologists learning bioinformatics included in their
resources.
Summary
In this section, we:
- Set up our compute environment
- Learned about DESeq2
- Read a raw count table and saved it as a data frame
Now that we have our count data processed, we can move on to
“unblinding” our data, as the sample names are unique identifiers
generated by a sequencing center and not very informative as far as our
experimental conditions.
Optional content
An important note is that there are several bonus content sections on
the instruction pages, like the two below that we will not be covering
in this workshop, but that may have useful context or be helpful when
you review this material.
Click for alternative DESeq2 input options for RSEM outputs
The package tximport
is another optionrecommended the DESeq2
authors to read in the RSEM expected_counts, as this package allows
for the average transcript length per gene to be used in the DE analysis
and, as described by
the author, the tximport-to-DESeqDataSet
constructor
function round the non-integer data generated by RSEM to whole numbers.
Click for comparison of RNA-seq data and microarray data
With higher
sensitivity, greater flexiblity, and decreasing cost, sequencing has
largely replaced microarray assays for measuring gene expression. A key
difference between the platforms is that microarrays measure intensities
and are therefore continous data while the count data from
sequencing is discrete. A more detailed comparison between
microarrays and sequencing technologies/analysis is outlined in the online materials
for Penn State’s STAT555 course
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.
LS0tCnRpdGxlOiAiTW9kdWxlIDA2OiBERSBJbnRyb2R1Y3Rpb24iCmF1dGhvcjogIlVNIEJpb2luZm9ybWF0aWNzIENvcmUiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogICAgICAgIGh0bWxfZG9jdW1lbnQ6CiAgICAgICAgICAgIGluY2x1ZGVzOgogICAgICAgICAgICAgICAgaW5faGVhZGVyOiBoZWFkZXIuaHRtbAogICAgICAgICAgICB0aGVtZTogcGFwZXIKICAgICAgICAgICAgdG9jOiB0cnVlCiAgICAgICAgICAgIHRvY19kZXB0aDogNAogICAgICAgICAgICB0b2NfZmxvYXQ6IHRydWUKICAgICAgICAgICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgICAgICAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgICAgICAgICBtYXJrZG93bjogR0ZNCiAgICAgICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpib2R5LCB0ZCB7CiAgIGZvbnQtc2l6ZTogMThweDsKfQpjb2RlLnJ7CiAgZm9udC1zaXplOiAxMnB4Owp9CnByZSB7CiAgZm9udC1zaXplOiAxMnB4Cn0KPC9zdHlsZT4KCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnNvdXJjZSgiLi4vYmluL2NodW5rLW9wdGlvbnMuUiIpCmtuaXRyX2ZpZ19wYXRoKCIwNi0iKQpgYGAKCj4gIyBPYmplY3RpdmVzIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cj4gKiBPdmVydmlldyBvZiByZXByb2R1Y2libGUgcmVzZWFyY2ggJiBhbmFseXNpcyBzZXR1cAo+ICogQnJvYWQgaW50cm9kdWN0aW9uIHRvIERFU2VxMiAmIHdoeSBpdCBpcyB3aWRlbHkgdXNlZCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gY29tcGFyaXNvbnMKPiAqIEhvdyB0byBpbXBvcnQgYW5kIHJldmlldyBnZW5lIGNvdW50IHRhYmxlCgoKIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBXb3JrZmxvdyB7LnVubGlzdGVkIC51bm51bWJlcmVkfQoKVG9kYXkgd2Ugd2lsbCBwcm9jZWVkIHRocm91Z2gga2V5IHN0ZXBzIGluIGEgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMsIHN0YXJ0aW5nIGZyb20gYSBjb3VudCB0YWJsZSB0aGF0J3Mgc2ltaWxhciB0byB3aGF0IHlvdSBnZW5lcmF0ZWQgaW4gdGhlIGZpcnN0IGhhbGYgb2YgdGhlIHdvcmtzaG9wIGFuZCBzaW1pbGFyIHRvIG9uZSBvZiB0aGUgb3V0cHV0cyBpbmNsdWRlZCBpbiB0aGUgW2RhdGEgdGhhdCB0aGUgQWR2YW5jZWQgR2Vub21pY3MgQ29yZSBkZWxpdmVyc10oaHR0cHM6Ly9icmNmLm1lZGljaW5lLnVtaWNoLmVkdS9jb3Jlcy9hZHZhbmNlZC1nZW5vbWljcy9kYXRhLWRlbGl2ZXJ5LykgZm9yIFJOQS1zZXEgbGlicmFyaWVzLgoKPGltZyBzcmM9ImltYWdlcy93YXlmaW5kZXIvd2F5ZmluZGVyLURFQW5hbHlzaXNTZXR1cC5wbmciIGFsdD0id2F5ZmluZGVyIiBzdHlsZT0id2lkdGg6IDEwMDBweDsiLz4KCiMgR2V0dGluZyBTdGFydGVkIGluIFJTdHVkaW8KCiMjIExvZyBvbiB0byBSU3R1ZGlvIFNlcnZlcgoKT3BlbiBhIHdlYiBicm93c2VyIHRvIHRoZSBmb2xsb3dpbmcgVVJMOiBbaHR0cDovL2JmeC13b3Jrc2hvcDAxLm1lZC51bWljaC5lZHVdKGh0dHA6Ly9iZngtd29ya3Nob3AwMS5tZWQudW1pY2guZWR1KQoKWW91IHNob3VsZCBub3cgYmUgbG9va2luZyBhdCBhIHBhZ2UgdGhhdCB3aWxsIGFsbG93IHlvdSB0byBsb2dpbiB0byB0aGUgUlN0dWRpbyBzZXJ2ZXI6Cgo8aW1nIHNyYz0iaW1hZ2VzL3JzdHVkaW9fbG9naW5fc2NyZWVuLnBuZyIgYWx0PSJyc3R1ZGlvIGRlZmF1bHQgc2Vzc2lvbiIgc3R5bGU9IndpZHRoOiAxMDAwcHg7Ii8+CgpFbnRlciB5b3VyIHVzZXIgY3JlZGVudGlhbHMgYW5kIGNsaWNrIDxrYmQ+U2lnbiBJbjwva2JkPi4gVGhlIGNyZWRlbnRpYWxzIHdlcmUgcHJvdmlkZWQgdmlhIGVtYWlsLCBidXQgaWYgeW91IGZvcmdldCB5b3VycywgYSBoZWxwZXIgY2FuIHJldHJpZXZlIGl0IGZvciB5b3UsIGp1c3QgYXNrIGluIFNsYWNrLiBZb3Ugc2hvdWxkIG5vdyBzZWUgdGhlIFJTdHVkaW8gaW50ZXJmYWNlOgoKPGltZyBzcmM9ImltYWdlcy9yc3R1ZGlvX3Nlc3Npb25fZGVmYXVsdC5wbmciIGFsdD0icnN0dWRpbyBkZWZhdWx0IHNlc3Npb24iIHN0eWxlPSJ3aWR0aDoxMDAwcHg7Ii8+CgojIyBDcmVhdGUgYW4gUlN0dWRpbyBQcm9qZWN0CgpJbiB0aGUgQ29tcHV0YXRpb25hbCBGb3VuZGF0aW9uIHdvcmtzaG9wIHdlIGNyZWF0ZWQgYSBwcm9qZWN0IHJvb3RlZCBpbiBhIHBhcnRpY3VsYXIgZGlyZWN0b3J5IGFuZCBzdG9yZWQgb3VyIGRhdGEsIHNjcmlwdHMgYW5kIG91dHB1dCB3aXRoaW4gdGhhdCBwcm9qZWN0LiBMZXQncyBkbyB0aGUgc2FtZSB0aGluZywgYnV0IGZvciB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcGFydCBvZiB0aGlzIHdvcmtzaG9wLgoKMS4gVG8gY3JlYXRlIGEgcHJvamVjdCwgZ28gdG8gdGhlIDxrYmQ+RmlsZTwva2JkPiBtZW51LCBhbmQgY2xpY2sgPGtiZD5OZXcgUHJvamVjdC4uLjwva2JkPi4gVGhlIGZvbGxvd2luZyB3aW5kb3cgd2lsbCBhcHBlYXI6Cgo8aW1nIHNyYz0iaW1hZ2VzL25ld19wcm9qZWN0X3dpbmRvdy5wbmciIGFsdD0ibmV3IHByb2plY3Qgd2luZG93IiBzdHlsZT0id2lkdGg6IDYwMHB4OyIvPgoKMi4gSW4gdGhpcyB3aW5kb3csIHNlbGVjdCA8a2JkPkV4aXN0aW5nIERpcmVjdG9yeTwva2JkPi4gRm9yICJQcm9qZWN0IHdvcmtpbmcgZGlyZWN0b3J5IiwgY2xpY2sgPGtiZD5Ccm93c2UuLi48L2tiZD4sIHNlbGVjdCB0aGUgIlJTRF9SIiBmb2xkZXIsIGFuZCBjbGljayA8a2JkPkNob29zZTwva2JkPi4gVGhpcyB3aWxsIHVzZSB0aGUgYC9ob21lL3dvcmtzaG9wL3VzZXIvUlNEX1JgIGZvbGRlciBhcyB0aGUgcHJvamVjdCBkaXJlY3RvcnkuCgozLiBGaW5hbGx5IGNsaWNrIDxrYmQ+Q3JlYXRlIFByb2plY3Q8L2tiZD4uIEluIHRoZSAiRmlsZXMiIHRhYiBvZiB5b3VyIG91dHB1dCBwYW5lIChtb3JlIGFib3V0IHRoZSBSU3R1ZGlvIGxheW91dCBpbiBhIG1vbWVudCksIHlvdSBzaG91bGQgc2VlIGFuIFJTdHVkaW8gcHJvamVjdCBmaWxlLCAqKlJTRF9SLlJwcm9qKiouIEFsbCBSU3R1ZGlvIHByb2plY3RzIGVuZCB3aXRoIHRoZSAiKiouUnByb2oqKiIgZmlsZSBleHRlbnNpb24uCgpOb3RlIHRoYXQgdGhlcmUgaXMgYWxyZWFkeSBhIGBkYXRhL2AgZm9sZGVyIHdoaWNoIGNvbnRhaW5zIHRoZSBkYXRhIHdlIHdpbGwgdXNlIGZvciB0aGVzZSBsZXNzb25zLgoKIyMgQ3JlYXRpbmcgYW4gUiBzY3JpcHQKCkxldCdzIGNyZWF0ZSBhbiBSIHNjcmlwdCBmaWxlIHRvIHN0b3JlIGFsbCB0aGUgY29kZSB3ZSdyZSBnb2luZyB0byBydW4gaW4gdGhlIGZvcnRoY29taW5nIGxlc3NvbnMuCgotIENsaWNrIHRoZSA8a2JkPkZpbGU8L2tiZD4gbWVudSBhbmQgc2VsZWN0IDxrYmQ+TmV3IEZpbGU8L2tiZD4gYW5kIHRoZW4gPGtiZD5SIFNjcmlwdDwva2JkPi4KLSBCZWZvcmUgd2UgZ28gYW55IGZ1cnRoZXIsIHNhdmUgeW91ciBzY3JpcHQgYnkgY2xpY2tpbmcgdGhlIHNhdmUvZGlzayBpY29uIHRoYXQgaXMgaW4gdGhlIGJhciBhYm92ZSB0aGUgZmlyc3QgbGluZSBpbiB0aGUgc2NyaXB0IGVkaXRvciwgb3IgY2xpY2sgdGhlIDxrYmQ+RmlsZTwva2JkPiBtZW51IGFuZCBzZWxlY3QgPGtiZD5TYXZlPC9rYmQ+LgotIEluIHRoZSAiU2F2ZSBGaWxlIiB3aW5kb3cgdGhhdCBvcGVucywgc2VsZWN0IDxrYmQ+TmV3IEZvbGRlcjwva2JkPi4gTmFtZSBpdCAqKiJzY3JpcHRzIioqLgotIEZpbmFsbHksIG5hbWUgeW91ciBmaWxlICoqImRpZmZleCIqKiBpbiB0aGUgIkZpbGUgbmFtZSIgZmllbGQuCgpUaGUgbmV3IHNjcmlwdCAqKmRpZmZleC5SKiogaXMgbm93IGluIHRoZSBgc2NyaXB0c2AgZm9sZGVyLiBZb3UgY2FuIHNlZSB0aGF0IGJ5IGNsaWNraW5nIHRoZSBgc2NyaXB0c2AgZm9sZGVyIGluIHRoZSAiRmlsZXMiIHBhbmUuIEFuZCB5b3UgY2FuIGdvIGJhY2sgdXAgdG8gdGhlIG1haW4gcHJvamVjdCBmb2xkZXIgYnkgY2xpY2tpbmcgdGhlIGAuLmAgdG8gdGhlIHJpZ2h0IG9mIHRoZSB1cCBhcnJvdyBpbiB0aGUgIkZpbGVzIiBwYW5lLiBCeSBjb252ZW50aW9uLCBSIHNjcmlwdHMgZW5kIHdpdGggdGhlIGZpbGUgZXh0ZW5zaW9uICoqLlIqKi4KCiMgUmVwcm9kdWNpYmxlIHJlc2VhcmNoCgpUb2RheSB3ZSdsbCBiZSBleHBsb3Jpbmcgc29tZSBSTkEtc2VxIGRhdGEgdGhhdCBpcyBmYWlybHkgcmVwcmVzZW50YXRpdmUgb2Ygd2hhdCB3ZSBzZWUgaW4gdGhlIGNvcmUgYW5kIHN0YXJ0IHdpdGggaW5wdXQgZmlsZXMgc2ltaWxhciB0byB0aGUgY291bnQgdGFibGUgeW91IGdlbmVyYXRlZCBpbiB0aGUgcHJldmlvdXMgc3RlcHMgYW5kIHNpbWlsYXIgdG8gb25lIG9mIHRoZSBvdXRwdXRzIGN1cnJlbnRseSBiZWluZyBkZWxpdmVyZWQgYnkgdGhlIEFkdmFuY2VkIFNlcXVlbmNpbmcgQ29yZS4KCiMjIEZpbGUgb3JnYW5pemF0aW9uCgpBcyB3aWRlbHkgZGlzY3Vzc2VkLCBpbmNsdWRpbmcgaW4gYSByZXZpZXcgYnkgW05vYmVsLCAyMDA5XShodHRwczovL2pvdXJuYWxzLnBsb3Mub3JnL3Bsb3Njb21wYmlvbC9hcnRpY2xlP2lkPTEwLjEzNzEvam91cm5hbC5wY2JpLjEwMDA0MjQpLCBmaWxlIG9yZ2FuaXphdGlvbiBhbmQgZGF0YSBzdGV3YXJkc2hpcCBhcmUgYW4gaW1wb3J0YW50IHBhcnRzIG9mIHJlcHJvZHVjaWJsZSByZXNlYXJjaC4KClRvIGZvbGxvdyBiZXN0IHByYWN0aWNlcyBmb3IgZmlsZSBvcmdhbml6YXRpb24gZm9yIGJpb2luZm9ybWF0aWNzL2NvbXB1dGF0aW9uYWwgcHJvamVjdHMsIHdlIHNob3VsZCBtYWtlIGRpc3RpbmN0IGxvY2F0aW9ucyBmb3I6CgoqIFJhdyBkYXRhCiogQ29kZQoqIE91dHB1dAoKU3VjaCBhcyBpbGx1c3RyYXRlZCBpbiB0aGlzIGZpZ3VyZSBmcm9tIHRoZSBOb2JsZSByZXZpZXc6Cgo8aW1nIHNyYz0iaW1hZ2VzL05vYmxlMjAwOV9kYXRhcHJvamVjdHMucG5nIiBhbHQ9Im5ldyBwcm9qZWN0IHdpbmRvdyIgc3R5bGU9IndpZHRoOiA2MDBweDsiLz4KClRvIG9yZ2FuaXplIG91ciBmaWxlcyBmb3Igb3VyIGFuYWx5c2lzIHRvZGF5LCB3ZSdsbCBjcmVhdGUgc29tZSBkaXJlY3RvcmllcyBpbiB0aGUgYFJTRF9SYCBmb2xkZXIgd2hpY2ggaXMgbm93IG91ciBwcm9qZWN0IHJvb3QgYW5kIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkuIFdlIGNhbiBhbHJlYWR5IHNlZSB0aGF0IHRoZXJlIGEgYGRhdGEvYCBmb2xkZXIgYW5kIHdlIGp1c3QgY3JlYXRlZCB0aGUgYHNjcmlwdHMvYCBmb2xkZXIgd2l0aCB0aGUgUlN0dWRpbyBHVUkuIExldCdzIGNyZWF0ZSBhbiBgb3V0cHV0cy9gIGZvbGRlciB1c2luZyB0aGUgYGRpci5jcmVhdGUoKWAgZnVuY3Rpb24gaW4gUi4KCmBgYHtyIGNyZWF0ZV9kaXJzLCBldmFsID0gRkFMU0V9CmRpci5jcmVhdGUoJ291dHB1dHMnKQpkaXIuY3JlYXRlKCdvdXRwdXRzL2ZpZ3VyZXMnKQpkaXIuY3JlYXRlKCdvdXRwdXRzL3RhYmxlcycpCmBgYAoKKipDaGVja3BvaW50Kio6ICpQbGVhc2UgdXNlIHRoZSBncmVlbiAnY2hlY2snIGlmIHlvdSBoYXZlIHNhdmVkIHlvdXIgY29kZSBmaWxlIGFuZCBzZWUgdGhlIGBkYXRhYCwgYHNjcmlwdHNgLCBhbmQgYG91dHB1dHNgIGRpcmVjdG9yaWVzIGluIHRoZSBgUlNEX1JgIGZvbGRlciBhbmQgdGhlIHJlZCAneCcgaWYgeW91IGRvIG5vdC4qCgo+ICMjIFJlbWluZGVyOiBSU3R1ZGlvIGNvZGUgZXhlY3V0aW9uIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cj4KPiA8a2JkPkN0cmw8L2tiZD4rPGtiZD5FbnRlcjwva2JkPiBpcyBhIHN0YW5kYXJkIHNob3J0Y3V0IGluIFJTdHVkaW8gdG8gc2VuZCB0aGUgY3VycmVudCBsaW5lIChvciBzZWxlY3RlZCBsaW5lcykgdG8gdGhlIGNvbnNvbGUuIElmIHlvdSBzZWUgYD5gIGluIHRoZSBDb25zb2xlLCB0aGVuIFIgaGFzIGV4ZWN1dGVkIHRoZSBjb21tYW5kLiBJZiB5b3Ugc2VlIGEgYCtgLCB0aGlzIG1lYW5zIHRoYXQgdGhlIGNvbW1hbmQgaXMgbm90IGNvbXBsZXRlLCBSIHRoaW5rcyB0aGVyZSBpcyBtb3JlIHRvIHlvdXIgY29tbWFuZC4gWW91IGNhbiB1c2UgdGhlIDxrbWQ+ZXNjPC9rbWQ+IHRvIGdldCBvdXQgb2YgdGhpcyBzdGF0ZS4KCj4gIyMgUmVtaW5kZXI6IE9iamVjdCBuYW1pbmcgY29udmVudGlvbnMgey51bmxpc3RlZCAudW5udW1iZXJlZH0KPiAqIENhbm5vdCBzdGFydCB3aXRoIG51bWJlcnMKPiAqIENhbm5vdCBpbmNsdWRlIGRhc2hlcwo+ICogQ2Fubm90IGhhdmUgc3BhY2VzCj4gKiBTaG91bGQgbm90IGJlIGlkZW50aWNhbCB0byBhIG5hbWVkIGZ1bmN0aW9uCj4gKiBEb3RzIGFuZCB1bmRlcnNjb3JlcyBjYW4gc2VwYXJhdGUgcGFydHMgb2YgbmFtZXMsIGFsdGVybmF0aXZlbHkgQ2FtZWxDYXNlIGFjY29tcGxpc2hlcyB0aGlzCgotLS0tCgojIFRvb2xzIGZvciBEaWZmZXJlbnRpYWwgR2VuZSBFeHByZXNzaW9uIGFuYWx5c2lzCgpBcyBkaXNjdXNzZWQgZHVyaW5nIHRoZSB3ZWJpbmFyLCBhIGNvbW1vbiBhcHBsaWNhdGlvbiBmb3IgYnVsayBSTkEtc2VxIGlzIHRvIHRlc3QgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGJldHdlZW4gY29uZGl0aW9ucyBvciB0cmVhdG1lbnRzLCB1c2luZyBzdGF0aXN0aWNhbCBhcHByb2FjaGVzIHRoYXQgYXJlIGFwcHJvcHJpYXRlIGZvciBiaW9sb2dpY2FsIGRhdGEuCgpXaGlsZSB0aGVyZSBhcmUgc2V2ZXJhbCB0b29scyB0aGF0IGNhbiBiZSB1c2VkIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBjb21wYXJpc29ucywgd2Ugd2lsbCB1c2UgW0RFU2VxMl0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL0RFU2VxMi5odG1sKSBpbiBvdXIgYW5hbHlzaXMgdG9kYXkuCgpERVNlcTIgaXMgb25lIG9mIHR3byB0b29scywgYWxvbmcgd2l0aCBbZWRnZVJdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9lZGdlUi5odG1sKSwgY29uc2lkZXJlZCBbJ2Jlc3QgcHJhY3RpY2UnXShodHRwczovL2JtY2Jpb2luZm9ybWF0aWNzLmJpb21lZGNlbnRyYWwuY29tL2FydGljbGVzLzEwLjExODYvMTQ3MS0yMTA1LTE0LTkxKSBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24uIEJvdGggdG9vbHMgYXBwbHkgc2ltaWxhciBtZXRob2RzIHRoYXQgYWNjb3VudCBmb3IgdGhlIGRpc3RyaWJ1dGlvbnMgd2UgZXhwZWN0IHRvIHNlZSBmb3IgUk5BLXNlcSBhbmQgYXJlIGZhaXJseSBzdHJpbmdlbnQgaW4gY2FsbGluZyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMsIGxvd2VyaW5nIHRoZSByaXNrIG9mIGludmVzdGlnYXRpbmcgZ2VuZXMgdGhhdCB3ZXJlIHJlYWxseSBmYWxzZSBwb3NpdGl2ZXMgKGUuZy4gZG9uJ3QgcmVhbGx5IGhhdmUgZGlmZmVyZW50IGV4cHJlc3Npb24gYmV0d2VlbiB0cmVhdG1lbnQgZ3JvdXBzIGFuZCB0aGVyZWZvcmUgYXJlIG5vdCByZWxldmFudCB0byB0aGUgYmlvbG9naWNhbCBwcm9jZXNzKS4KCkFkZGl0aW9uYWxseSwgREVTZXEyIGFsc28gaGFzIGFuIGV4Y2VsbGVudCB2aWduZXR0ZSBbKGxpbmspXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwpCmZyb20gTG92ZSwgQW5kZXJzLCBhbmQgSHViZXIsIGZyb20gd2hpY2ggb3VyIHdvcmtmbG93IGlzIHBhcnRpYWxseSBhZGFwdGVkLCBhbmQgaXMgYSBnb29kIHJlc291cmNlIHdoZW4gYW5hbHl6aW5nIHlvdXIgb3duIGRhdGEKKHNlZSBhbHNvOiBbTG92ZSwgQW5kZXJzLCBhbmQgSHViZXIuIF9HZW5vbWUgQmlvbG9neV8uIDIwMTQuXShodHRwczovL2RvaS5vcmcvMTAuMTE4Ni9zMTMwNTktMDE0LTA1NTAtOCkpLgoKCjxkZXRhaWxzPgogICAgPHN1bW1hcnk+KkNsaWNrIGZvciBhZGRpdGlvbmFsIHJlc291cmNlcyByZWdhcmRpbmcgc3RhdGlzdGljYWwgdGVzdGluZyBhbmQgdG9vbCBjb21wYXJpc29uIGZvciBSTkEtc2VxIGRhdGEqPC9zdW1tYXJ5PgogICAgVG8gbGVhcm4gbW9yZSBhYm91dCBzdGF0aXN0aWNhbCB0ZXN0aW5nIGFuZCB3aGF0IGRpc3RyaWJ1dGlvbnMgYmVzdCBtb2RlbCB0aGUgYmVoYXZpb3Igb2YgUk5BLXNlcSBkYXRhLCBhIGdvb2QgcmVzb3VyY2UgaXMgdGhpcyBbRWRYIGxlY3R1cmUgYnkgUmFmYWVsIElyaXphcnJ5XShodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PUhLN1dLc0wzYzJ3JmZlYXR1cmU9eW91dHUuYmUpIG9yIHRoaXMgW2xlY3R1cmUgYnkgS2FzcGVyIEhhbnNlbl0oaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1DOFJOdld1N3BBdykuIEFub3RoZXIgaGVscGZ1bCBndWlkZSBpcyB0aGlzIFtDb21wYXJhdGl2ZSBTdHVkeSBmb3IgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gQW5hbHlzaXMgYnkgWmhhbmcgZXQgYWwuXShodHRwczovL2pvdXJuYWxzLnBsb3Mub3JnL3Bsb3NvbmUvYXJ0aWNsZT9pZD0xMC4xMzcxL2pvdXJuYWwucG9uZS4wMTAzMjA3KSBmcm9tIDIwMTQuCjwvZGV0YWlscz4KPGJyPgoKIyMgREVTZXEyIGFzc3VtcHRpb25zIGFuZCByZXF1aXJlbWVudHMKCkEga2V5IGFzc3VtcHRpb24gb2YgREVTZXEyIGlzIHRoYXQgKmJpb2xvZ2ljYWwgdmFyaWFuY2UgaXMgbXVjaCBncmVhdGVyIHRoYW4gdGVjaG5pY2FsIHZhcmlhbmNlKiAoZXNwZWNpYWxseSBpZiBbYmVzdCBwcmFjdGljZXNdKGh0dHBzOi8vd3d3LnR4Z2VuLnRhbXUuZWR1L2ZhcS9ybmEtaXNvbGF0aW9uLWJlc3QtcHJhY3RpY2VzLykgZm9yIFtxdWFsaXR5IFJOQSBpc29sYXRpb25dKGh0dHBzOi8vd3d3LmJpb2NvbXBhcmUuY29tL0JlbmNoLVRpcHMvMTI4NzkwLUZvdXItVGlwcy1mb3ItUGVyZmVjdGluZy1STkEtSXNvbGF0aW9uLykgYXJlIGZvbGxvd2VkLCBpbmNsdWRpbmcgRE5hc2UgdHJlYXRtZW50ISkuCgpTaW5jZSBjYWxjdWxhdGluZyB2YXJpYW5jZSBpcyBrZXkgdG8gdGhlIHN0YXRpc3RpY2FsIGFwcHJvYWNoIHVzZWQgZm9yIERFU2VxMiwgaWYgd2UgdHJpZWQgdG8gY29tcGFyZSB0d28gdHJlYXRtZW50IGdyb3VwcyB3aXRoIGxlc3MgdGhhbiAqKnR3byoqIHJlcGxpY2F0ZXMsIHdlIHdvdWxkIGdldCBhbiBlcnJvciAoYXMgc2hvd24gaW4gW3RoaXMgYmxvZyBwb3N0XShodHRwczovL3N1cHBvcnQuYmlvY29uZHVjdG9yLm9yZy9wLzg5NzQ2LykpLiBXaXRob3V0IHJlcGxpY2F0ZXMsIHRoZXJlIGNhbid0IGJlIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZSAoZS5nLiBwLXZhbHVlcyksIGJ1dCAqcXVhbGl0YXRpdmUqIGFwcHJvYWNoZXMgYXJlIGFuIG9wdGlvbiwgbGlrZSBsb29raW5nIGF0IHRoZSB0b3AgZXhwcmVzc2VkIGdlbmVzIGFmdGVyIG5vcm1hbGl6YXRpb24uCgojIyBSZXBsaWNhdGVzIGluIFJOQS1zZXEgZXhwZXJpbWVudHMKCkEgZnJlcXVlbnQgcXVlc3Rpb24gZm9yIFJOQS1zZXEgcHJvamVjdHMgaXMgIkhvdyBtYW55IHJlcGxpY2F0ZXMgZG8gSSBuZWVkPyIuCgpUaGUgZ2VuZXJhbCBnb2FsIG9mIG91ciBhbmFseXNlcyBpcyB0byBzZXBhcmF0ZSB0aGUg4oCcaW50ZXJlc3RpbmfigJ0gYmlvbG9naWNhbCBjb250cmlidXRpb25zIGZyb20gdGhlIOKAnHVuaW50ZXJlc3RpbmfigJ0gdGVjaG5pY2FsIG9yIG90aGVyIGNvbnRyaWJ1dGlvbnMgdGhhdCBlaXRoZXIgY2Fubm90IGJlIG9yIHdlcmUgbm90IGNvbnRyb2xsZWQgaW4gdGhlIGV4cGVyaW1lbnRhbCBkZXNpZ24uIFRoZSBtb3JlIHNvdXJjZXMgb2YgdmFyaWF0aW9uLCBzdWNoIGFzIHNhbXBsZXMgY29taW5nIGZyb20gaGV0ZXJvZ2Vub3VzIHRpc3N1ZXMgb3IgZXhwZXJpbWVudHMgd2l0aCBpbmNvbXBsZXRlIGtub2NrZG93bnMsIHRoZSBtb3JlIHJlcGxpY2F0ZXMgKD4zKSBhcmUgcmVjb21tZW5kZWQuCgohW0ltYWdlIG9mIHRlY2huaWNhbCwgYmlvbG9naWNhbCwgYW5kIGV4cGVyaW1lbnRhbCBjb250cmlidXRvcnMgdG8gZ2VuZSBleHByZXNzaW9uLCBmcm9tIEhCQyB0cmFpbmluZyBtYXRlcmlhbHNdKGltYWdlcy9kZV92YXJpYXRpb24ucG5nKXt3aWR0aD03NSV9CgpGb3IgYSBtb3JlIGluIGRlcHRoIGRpc2N1c3Npb24gb2YgZXhwZXJpbWVudGFsIGRlc2lnbiBjb25zaWRlcmF0aW9ucywgcGFydGljdWxhcmx5IGZvciB0aGUgbnVtYmVyIG9mIHJlcGxpY2F0ZXMsIHBsZWFzZSByZWFkIFtBIEJlZ2lubmVy4oCZcyBHdWlkZSB0byBBbmFseXNpcyBvZiBSTkEgU2VxdWVuY2luZyBEYXRhXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM2MDk2MzQ2LykgYW5kIHBhcGVycyBsaWtlIHRoaXMgb25lIGJ5IFtIYXJ0IGV0IGFsXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUMzODQyODg0LykgdGhhdCBmb2N1cyBvbiBlc3RpbWF0aW5nIHN0YXRpc3RpY2FsIHBvd2VyIGZvciBSTkEtc2VxIGV4cGVyaW1lbnRzLgoKIyMgU2VxdWVuY2luZyBkZXB0aCByZWNvbW1lbmRhdGlvbnMKCkEgcmVsYXRlZCBleHBlcmltZW50YWwgZGVzaWduIGNvbnNpZGVyYXRpb24gaXMgaG93IG11Y2ggc2VxdWVuY2luZyBkZXB0aCBzaG91bGQgYmUgZ2VuZXJhdGVkIHBlciBzYW1wbGUuIFRoaXMgZmlndXJlIHNoYXJlZCBieSBJbGx1bWluYSBpbiB0aGVpciB0ZWNobmljYWwgdGFsa3MgaXMgaGVscGZ1bCB0byB1bmRlcnN0YW5kIHRoZSByZWxhdGl2ZSBpbXBvcnRhbmNlIG9mIHJlcGxpY2F0ZXMgdmVyc3VzIHNlcXVlbmNpbmcgZGVwdGguCgohW0lsbHVtaW5hJ3MgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcmVjb3ZlcnkgYWNyb3NzIHJlcGxpY2F0ZSBudW1iZXIgYW5kIHNlcXVlbmNpbmcgZGVwdGhdKGltYWdlcy9kZV9yZXBsaWNhdGVzX2ltZy5wbmcpe3dpZHRoPTc1JX0KCkdlbmVyYWxseSwgZm9yIGh1bWFuIGFuZCBtb3VzZSBleHBlcmltZW50cywgdGhlIHJlY29tbWVuZGF0aW9uIGlzIDMwLTQwIG1pbGxpb24gcmVhZHMgcGVyIHNhbXBsZSBmb3IgcG9seUEgbGlicmFyeSBwcmVwcyB0byBjYXB0dXJlIGJvdGggaGlnaGx5IGV4cHJlc3NlZCAoYWJ1bmRhbnQpIGFuZCBsb3dseSBleHByZXNzZWQgKHJhcmVyKSB0cmFuc2NyaXB0cywgYXNzdW1pbmcgdGhhdCB+MjUsMDAwIHByb3RlaW4tY29kaW5nIGdlbmVzIHdvdWxkIGJlIG1lYXN1cmVkLiBIb3dldmVyLCBhcyB0aGUgaW1hZ2UgYWJvdmUgc2hvd3MsIHNlcXVlbmNpbmcgZGVwdGggaGFzIGxlc3Mgb2YgYW4gaW1wYWN0IHRoYW4gbnVtYmVyIG9mIHJlcGxpY2F0ZXMgaW4gZGV0ZWN0aW5nIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyAoREVHcykuCgo+ICMjIEV4ZXJjaXNlOiBCdWlsZGluZyBhIGJldHRlciB1bmRlcnN0YW5kaW5nIG9mIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cj4KPiAxLiBQb3N0IGEgY29tbWVudCBiZWxvdyByZWdhcmRpbmcgd2hhdCBrZXkgcXVlc3Rpb24vbWlzY29uY2VwdGlvbiByZWdhcmRpbmcgZGVzaWduaW5nIGFuIFJOQS1zZXEgZXhwZXJpbWVudCB3ZSB3ZXJlIGFibGUgdG8gYWRkcmVzcyAqKk9SKioKPiAyLiBQb3N0IGEgcXVlc3Rpb24gdGhhdCB0aGF0IHdhcyBOT1QgYWRkcmVzc2VkIGJ1dCB0aGF0IHlvdSBob3BlIHdlIHdpbGwgYWRkcmVzcyBpbiB0aGUgbGF0ZXIgbW9kdWxlcyAqKk9SKioKPiAzLiBBZGQgYSB0aHVtYnMgdXAgdG8geW91ciBmYXZvcml0ZSBjb21tZW50KHMpIHRvIHVwdm90ZSBpdAoKLS0tLQoKIyBHZXR0aW5nIHN0YXJ0ZWQgd2l0aCBhbmFseXNpcwoKIyMgTG9hZCBQYWNrYWdlcwoKU2V2ZXJhbCBwYWNrYWdlcyBoYXZlIGFscmVhZHkgYmVlbiBpbnN0YWxsZWQgb24gdGhlIHNlcnZlciwgc28gd2UgY2FuIGxvYWQgdGhlbSBpbnRvIG91ciBSIHNlc3Npb24gbm93LiBUbyBkbyB0aGF0IHdlJ2xsIHVzZSB0aGUgYGxpYnJhcnlgIGZ1bmN0aW9uIHRvIGxvYWQgdGhlIHJlcXVpcmVkIHBhY2thZ2VzLgoKYGBge3IgTW9kdWxlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz0gRkFMU0UsIGV2YWw9VFJVRX0KbGlicmFyeShERVNlcTIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKbGlicmFyeShtYXRyaXhTdGF0cykKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShkYXRhLnRhYmxlKQpgYGAKCipOb3RlOiBXZSBleHBlY3QgdG8gc2VlIHNvbWUgcmVkIG1lc3NhZ2VzIGluIHlvdXIgY29uc29sZSB3aGlsZSB0aGVzZSBwYWNrYWdlcyBhcmUgbG9hZGluZyoKClIvUlN0dWRpbyBoYXMgZ3JlYXQgcmVzb3VyY2VzIGZvciBnZXR0aW5nIGhlbHAsIGluY2x1ZGluZyBbY29kZSAnY2hlYXRzaGVldHMnXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNi8xMC9yLWNoZWF0LXNoZWV0LTMucGRmKSBhbmQgcGFja2FnZSB2aWduZXR0ZXMsIGxpa2UgZm9yIFt0aWR5cl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3RpZHlyL3ZpZ25ldHRlcy90aWR5LWRhdGEuaHRtbCkuCgpTaW5jZSB3ZSBsb2FkZWQgdGhlIGxpYnJhcmllcyBpbnRvIG91ciBSIHNlc3Npb24sIHdlIGNhbiBzZWUgZG9jdW1lbnRhdGlvbiBvdXQgdXNpbmcgdGhlIGA/YCBvcGVyYXRvci4KYGBge3IgQ2hlY2tEb2N1bWVudGFpb259Cj9gREVTZXEyLXBhY2thZ2VgCmBgYAoKKipDaGVja3BvaW50Kio6ICpJZiB5b3Ugc2VlIHRoZSBSIGRvY3VtZW50YXRpb24gZm9yIERFU2VxMiBwb3AgdXAgaW4geW91ciAnaGVscCcgcGFuZWwgb24gdGhlIHJpZ2h0LCBwbGVhc2UgaW5kaWNhdGUgd2l0aCB0aGUgZ3JlZW4gJ2NoZWNrJyBidXR0b24uIElmIG5vdCBwbGVhc2UgdXNlIHRoZSByZWQgJ3gnIGJ1dHRvbi4qCgojIyBSZWFkIENvdW50cwoKQW5vdGhlciBrZXkgYXNzdW1wdGlvbiBmb3IgREVTZXEyIGlzIHRoYXQgdGhlIGFuYWx5c2lzIHdpbGwgc3RhcnQgd2l0aCBbdW4tbm9ybWFsaXplZCBjb3VudHNdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCN3aHktdW4tbm9ybWFsaXplZC1jb3VudHMpLgoKVG8gYmVnaW4gb3VyIGFuYWx5c2lzLCB3ZSdsbCByZWFkIGluIHRoZSAqKnJhdyoqIGNvdW50IGRhdGEgZmlsZSwgYGdlbmVfZXhwZWN0ZWRfY291bnQudHh0YCB3aGljaCBpcyBzaW1pbGFyIHRvIHdoYXQgd291bGQgYmUgZ2VuZXJhdGVkIGluIHRoZSBhbGlnbm1lbnQgc3RlcHMgKGFuZCB3aGF0IHlvdSB3b3VsZCByZWNlaXZlIGZyb20gQUdDKS4gV2UnbGwgZGlzY3VzcyBsYXRlciBhIGZldyBub3JtYWxpemF0aW9ucyB0aGF0IGNhbiBiZSBoZWxwZnVsIGZvciB1cyB0byB1bmRlcnN0YW5kIGhvdyBtdWNoIGEgZ2VuZSBpcyBleHByZXNzZWQgd2l0aGluIG9yIGJldHdlZW4gc2FtcGxlcywgYnV0IG5vcm1hbGl6ZWQgZGF0YSAqKnNob3VsZCBub3QqKiBiZSB1c2VkIGFzIGFuIGlucHV0IGZvciBERVNlcTIuCgpgYGB7ciBEYXRhVGFibGUsIGV2YWw9VFJVRSwgZWNobz1GQUxTRX0KIyMgbmVlZCB0byBpbmNsdWRlIHBhdGhzIHJlbGF0aXZlIHRvIHNpdGUgYnVpbGQgbG9jYXRpb25zIGJ1dCBrZWVwIGNvZGUgYmxvY2sgaGlkZGVuCmNvdW50X3RhYmxlID0gcmVhZC50YWJsZSgiLi4vZGF0YS9nZW5lX2V4cGVjdGVkX2NvdW50LnR4dCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpCmBgYAoKYGBge3IgRGF0YVRhYmxlMiwgZXZhbD1GQUxTRX0KY291bnRfdGFibGUgPSByZWFkLnRhYmxlKCJkYXRhL2dlbmVfZXhwZWN0ZWRfY291bnQudHh0IiwgaGVhZGVyID0gVFJVRSwgcm93Lm5hbWVzID0gMSkKYGBgCgpgYGB7ciBEYXRhVGFibGVDaGVjaywgZXZhbD1UUlVFfQpoZWFkKGNvdW50X3RhYmxlLCBuPTIpICMgbG9vayBhdCB0aGUgdG9wIG9mIHRoZSB0YWJsZQpgYGAKCk5vdyB0aGF0IHRoZSBmaWxlIGlzIHJlYWQgaW50byBSLCBub3RlIHRoYXQgd2UndmUgY3JlYXRlZCBhIGRhdGEgZnJhbWUgdGhhdCBpbmNsdWRlcyAnZ2VuZSBpZHMnIGluIEVOU0VNQkwgZm9ybWF0IGFzIHJvd25hbWVzIGFuZCBjb3VudCBkYXRhIGZyb20gdHdlbHZlIGRpZmZlcmVudCBzYW1wbGVzLgoKPiAjIEV4ZXJjaXNlOiBSU0VNIG91dHB1dHMgdmVyc3VzIERFU2VxMiBpbnB1dCByZXF1aXJlbWVudHMgey51bmxpc3RlZCAudW5udW1iZXJlZH0KPgo+IElmIHdlIHRoaW5rIGJhY2sgdG8gdGhlIFJTRU0gb3V0cHV0cywgdGhlICdleHBlY3RlZF9jb3VudHMnIHRhYmxlIG1heSBpbmNsdWRlIGZyYWN0aW9uYWwgYW1vdW50cyBkdWUgdG8gaG93IHRoZSBhbGlnbm1lbnQgdG9vbCByZXNvbHZlcyByZWFkcyBtYXBwaW5nIHRvIG11bHRpcGxlIGxvY3VzZXMpLiBERVNlcTIgaXMgZXhwZWN0aW5nIHRoZSBjb3VudCBtYXRyaXggdG8gY29uc2lzdCBvbmx5IG9mIHdob2xlIG51bWJlcnMuIFJlY2VudCB2ZXJzaW9ucyBvZiBERVNlcTIgd2lsbCBoYW5kbGUgdGhpcyBmb3IgdXMsIGJ1dCBwcmV2aW91cyB2ZXJzaW9ucyBkaWQgbm90Lgo+Cj4gV2hhdCBmdW5jdGlvbiwgdGhhdCB3ZSBzYXcgZWFybHkgb24gaW4gdGhlIENvbXB1dGF0aW9uYWwgRm91bmRhdGlvbnMgUiBsZXNzb25zLCBjb3VsZCB3ZSB1c2UgdG8gY29udmVydCBmcmFjdGlvbmFsIHZhbHVlcyBpbiBgY291bnRfdGFibGVgIHRvIGludGVnZXJzPwoKPGRldGFpbHM+CjxzdW1tYXJ5PlNvbHV0aW9uPC9zdW1tYXJ5PgoKV2UgY2FuIHVzZSB0aGUgYHJvdW5kKClgIGZ1bmN0aW9uOgoKYGBge3Igcm91bmQgY291bnRfdGFibGV9CmNvdW50X3RhYmxlID0gcm91bmQoY291bnRfdGFibGUpCmBgYAo8L2RldGFpbHM+Cjxicj4KCiMgR2V0dGluZyBoZWxwCgpSIGFuZCBSU3R1ZGlvIGhhdmUgYSBzdHJvbmcgY29tbXVuaXR5IGNvbXBvbmVudCBzbyBpZiB5b3UgYXJlIGdldHRpbmcgYW4gZXJyb3Igb3Igd29uZGVyaW5nIGhvdyB0byBtYWtlIGEgY29tbWFuZCB3b3JrIG9yIGhvdyB0byBwZXJmb3JtIGEgc3BlY2lmaWMgdGFzaywgdGhlcmUgaXMgbGlrZWx5IGFscmVhZHkgYSBzb2x1dGlvbiBvdXQgdGhlcmUuIFJlbWVtYmVyIHRoYXQgR29vZ2xlIGlzIHlvdXIgZnJpZW5kLCBhbHRob3VnaCBpdCBjYW4gc29tZXRpbWVzIGJlIGEgY2hhbGxlbmdlIHRvIGZpZ3VyZSBvdXQgKndoYXQgdG8gc2VhcmNoIGZvciouIEtleSBwYXJ0cyBvZiBhIHN1Y2Nlc3NmdWwgc2VhcmNoOgoKKiBQYWNrYWdlIG9yIGNvbW1hbmQgcnVuCiogYFJgIG9yIGBCaW9jb25kdWN0b3JgCiogVGhlIGVycm9yIG1lc3NhZ2UgaWYgdGhlcmUgaXMgb25lCiogVmVyc2lvbiBpbmZvcm1hdGlvbgoKSG93IHRvIGdldCBzZXNzaW9uIGluZm9ybWF0aW9uIHRvIGFpZCBpbiBhIHNlYXJjaDoKYGBge3IgU2Vzc2lvbiBpbmZvLCBldmFsID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0Kc2Vzc2lvbkluZm8oKQpgYGAKCkhpZ2hseSByZWNvbW1lbmQgdXNpbmcgcmVzb3VyY2VzIGxpa2UgW0Jpb2NvbmR1Y3RvciBTdXBwb3J0XShodHRwczovL3N1cHBvcnQuYmlvY29uZHVjdG9yLm9yZy8pLCBbQmlvc3RhcnNdKGh0dHBzOi8vd3d3LmJpb3N0YXJzLm9yZy8pLCBhbmQgW1N0YWNrIE92ZXJmbG93XShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy90YWdnZWQvciksIGluY2x1ZGluZyB0aHJlYWRzIG9uIHNwZWNpZmljIHBhY2thZ2VzIG9yIGNvbW1vbiBiaW9pbmZvcm1hdGljIHRhc2tzLgoKSSBwZXJzb25hbGx5IHVzZSBvbmUgb3IgbW9yZSBvZiB0aGVzZSByZXNvdXJjZXMgKipldmVyeSBkYXkqKi4KCjEweCBHZW5vbWljcyBoYXMgYSBoZWxwZnVsIFsxMCB0aXBzIGZvciBiaW9sb2dpc3RzIGxlYXJuaW5nICBiaW9pbmZvcm1hdGljc10oaHR0cHM6Ly93d3cuMTB4Z2Vub21pY3MuY29tL3Jlc291cmNlcy9hbmFseXNpcy1ndWlkZXMvMTAtdGlwcy1mb3ItYmlvbG9naXN0cy1sZWFybmluZy1iaW9pbmZvcm1hdGljcykgaW5jbHVkZWQgaW4gdGhlaXIgcmVzb3VyY2VzLgoKIyBTdW1tYXJ5CgpJbiB0aGlzIHNlY3Rpb24sIHdlOgoKKiBTZXQgdXAgb3VyIGNvbXB1dGUgZW52aXJvbm1lbnQKKiBMZWFybmVkIGFib3V0IERFU2VxMgoqIFJlYWQgYSByYXcgY291bnQgdGFibGUgYW5kIHNhdmVkIGl0IGFzIGEgZGF0YSBmcmFtZQoKTm93IHRoYXQgd2UgaGF2ZSBvdXIgY291bnQgZGF0YSBwcm9jZXNzZWQsIHdlIGNhbiBtb3ZlIG9uIHRvICJ1bmJsaW5kaW5nIiBvdXIgZGF0YSwgYXMgdGhlIHNhbXBsZSBuYW1lcyBhcmUgdW5pcXVlIGlkZW50aWZpZXJzIGdlbmVyYXRlZCBieSBhIHNlcXVlbmNpbmcgY2VudGVyIGFuZCBub3QgdmVyeSBpbmZvcm1hdGl2ZSBhcyBmYXIgYXMgb3VyIGV4cGVyaW1lbnRhbCBjb25kaXRpb25zLgoKLS0tCgojIE9wdGlvbmFsIGNvbnRlbnQKCkFuIGltcG9ydGFudCBub3RlIGlzIHRoYXQgdGhlcmUgYXJlIHNldmVyYWwgYm9udXMgY29udGVudCBzZWN0aW9ucyBvbiB0aGUgaW5zdHJ1Y3Rpb24gcGFnZXMsIGxpa2UgdGhlIHR3byBiZWxvdyB0aGF0IHdlIHdpbGwgbm90IGJlIGNvdmVyaW5nIGluIHRoaXMgd29ya3Nob3AsIGJ1dCB0aGF0IG1heSBoYXZlIHVzZWZ1bCBjb250ZXh0IG9yIGJlIGhlbHBmdWwgd2hlbiB5b3UgcmV2aWV3IHRoaXMgbWF0ZXJpYWwuCgo8ZGV0YWlscz4KICAgIDxzdW1tYXJ5PipDbGljayBmb3IgYWx0ZXJuYXRpdmUgREVTZXEyIGlucHV0IG9wdGlvbnMgZm9yIFJTRU0gb3V0cHV0cyo8L3N1bW1hcnk+CiAgICBUaGUgcGFja2FnZSBgdHhpbXBvcnRgIGlzIGFub3RoZXIgb3B0aW9uW3JlY29tbWVuZGVkIHRoZSBERVNlcTIgIGF1dGhvcnNdKGh0dHBzOi8vc3VwcG9ydC5iaW9jb25kdWN0b3Iub3JnL3AvOTA2NzIvKSB0byByZWFkIGluIHRoZSBSU0VNIGV4cGVjdGVkX2NvdW50cywgYXMgdGhpcyAgcGFja2FnZSBhbGxvd3MgZm9yIHRoZSBhdmVyYWdlIHRyYW5zY3JpcHQgbGVuZ3RoIHBlciBnZW5lIHRvIGJlIHVzZWQgaW4gdGhlIERFIGFuYWx5c2lzIGFuZCwgYXMgW2Rlc2NyaWJlZCBieSB0aGUgYXV0aG9yXShodHRwczovL3N1cHBvcnQuYmlvY29uZHVjdG9yLm9yZy9wLzg4NzYzLyksIHRoZSBgdHhpbXBvcnQtdG8tREVTZXFEYXRhU2V0YCBjb25zdHJ1Y3RvciBmdW5jdGlvbiByb3VuZCB0aGUgbm9uLWludGVnZXIgZGF0YSBnZW5lcmF0ZWQgYnkgUlNFTSB0byB3aG9sZSBudW1iZXJzLgo8L2RldGFpbHM+Cjxicj4KPGRldGFpbHM+CiAgICA8c3VtbWFyeT4qQ2xpY2sgZm9yIGNvbXBhcmlzb24gb2YgUk5BLXNlcSBkYXRhIGFuZCBtaWNyb2FycmF5IGRhdGEqPC9zdW1tYXJ5PgogICAgV2l0aCBbaGlnaGVyIHNlbnNpdGl2aXR5LCBncmVhdGVyIGZsZXhpYmxpdHksIGFuZCBkZWNyZWFzaW5nIGNvc3RdKGh0dHBzOi8vd3d3LmlsbHVtaW5hLmNvbS9zY2llbmNlL3RlY2hub2xvZ3kvbmV4dC1nZW5lcmF0aW9uLXNlcXVlbmNpbmcvbWljcm9hcnJheS1ybmEtc2VxLWNvbXBhcmlzb24uaHRtbCksIHNlcXVlbmNpbmcgaGFzIGxhcmdlbHkgcmVwbGFjZWQgbWljcm9hcnJheSBhc3NheXMgZm9yIG1lYXN1cmluZyBnZW5lIGV4cHJlc3Npb24uIEEga2V5IGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcGxhdGZvcm1zIGlzIHRoYXQgbWljcm9hcnJheXMgbWVhc3VyZSBpbnRlbnNpdGllcyBhbmQgYXJlIHRoZXJlZm9yZSAqY29udGlub3VzKiBkYXRhIHdoaWxlIHRoZSBjb3VudCBkYXRhIGZyb20gc2VxdWVuY2luZyBpcyAqZGlzY3JldGUqLiBBIG1vcmUgZGV0YWlsZWQgY29tcGFyaXNvbiBiZXR3ZWVuIG1pY3JvYXJyYXlzIGFuZCBzZXF1ZW5jaW5nIHRlY2hub2xvZ2llcy9hbmFseXNpcyBpcyBvdXRsaW5lZCBpbiBbdGhlIG9ubGluZSBtYXRlcmlhbHMgZm9yIFBlbm4gU3RhdGUncyBTVEFUNTU1IGNvdXJzZV0oaHR0cHM6Ly9vbmxpbmUuc3RhdC5wc3UuZWR1L3N0YXQ1NTUvbm9kZS8zMC8pCgo8L2RldGFpbHM+Cjxicj4KCiMgU291cmNlcwoKVHJhaW5pbmcgcmVzb3VyY2VzIHVzZWQgdG8gZGV2ZWxvcCBtYXRlcmlhbHM6CgoqIEhCQyBER0Ugc2V0dXA6IGh0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL0RHRV93b3Jrc2hvcC9sZXNzb25zLzAxX0RHRV9zZXR1cF9hbmRfb3ZlcnZpZXcuaHRtbAoqIEhCQyBDb3VudCBOb3JtYWxpemF0aW9uOiBodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9ER0Vfd29ya3Nob3AvbGVzc29ucy8wMl9ER0VfY291bnRfbm9ybWFsaXphdGlvbi5odG1sCiogREVTZXEyIHN0YW5kYXJkIHZpZ25ldHRlOiBodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwKKiBERVNlcTIgYmVnaW5uZXJzIHZpZ25ldHRlOiBodHRwczovL2Jpb2MuaXNtLmFjLmpwL3BhY2thZ2VzLzIuMTQvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL2JlZ2lubmVyLnBkZgoqIEJpb2NvbmR1Y3RvciBSTkEtc2VxIFdvcmtmbG93czogaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9oZWxwL2NvdXJzZS1tYXRlcmlhbHMvMjAxNS9MZWFybkJpb2NvbmR1Y3RvckZlYjIwMTUvQjAyLjFfUk5BU2VxLmh0bWwKKiBDQ0RMIEdhc3RyaWMgY2FuY2VyIHRyYWluaW5nIG1hdGVyaWFsczogaHR0cHM6Ly9hbGV4c2xlbW9uYWRlLmdpdGh1Yi5pby90cmFpbmluZy1tb2R1bGVzL1JOQS1zZXEvMDMtZ2FzdHJpY19jYW5jZXJfZXhwbG9yYXRvcnkubmIuaHRtbAoqIENDREwgTmV1cm9ibGFzdG9tYSB0cmFpbmluZyBtYXRlcmlhbHM6IGh0dHBzOi8vYWxleHNsZW1vbmFkZS5naXRodWIuaW8vdHJhaW5pbmctbW9kdWxlcy9STkEtc2VxLzA1LW5iX2NlbGxfbGluZV9ERVNlcTIubmIuaHRtbAoKCmBgYHtyIFdyaXRlT3V0LlJEYXRhLCBldmFsPVRSVUUsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiNIaWRkZW4gY29kZSBibG9jayB0byB3cml0ZSBvdXQgZGF0YSBmb3Iga25pdHRpbmcKCiMgZGlyLmNyZWF0ZSgicmRhdGEiLCBzaG93V2FybmluZ3M9RkFMU0UpCiMgc2F2ZS5pbWFnZShmaWxlID0gInJkYXRhL1J1bm5pbmdEYXRhLlJEYXRhIikKIyBIb3cgdG8gbG9hZCBmb3IgbmV4dCBzZWdtZW50CiMgbG9hZCgicmRhdGEvUnVubmluZ0RhdGEuUkRhdGEiKQpgYGAKCi0tLQoKVGhlc2UgbWF0ZXJpYWxzIGhhdmUgYmVlbiBhZGFwdGVkIGFuZCBleHRlbmRlZCBmcm9tIG1hdGVyaWFscyBsaXN0ZWQgYWJvdmUuIFRoZXNlIGFyZSBvcGVuIGFjY2VzcyBtYXRlcmlhbHMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBbQ3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbiBsaWNlbnNlIChDQyBCWSA0LjApXShodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS80LjAvKSwgd2hpY2ggcGVybWl0cyB1bnJlc3RyaWN0ZWQgdXNlLCBkaXN0cmlidXRpb24sIGFuZCByZXByb2R1Y3Rpb24gaW4gYW55IG1lZGl1bSwgcHJvdmlkZWQgdGhlIG9yaWdpbmFsIGF1dGhvciBhbmQgc291cmNlIGFyZSBjcmVkaXRlZC4K