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 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 are the same used earlier in the workshop and were provided
via email. However, if you forget yours, a helper can retrieve it for
you if you 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 a count table
similar to what generated in the previous steps and 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 more
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
Getting started
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 [AKA Posit] 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 a few normalizations that can be helpful for us
to understand how much genes are expressed within or between samples,
but raw data should always 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:
counts_rounded = 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 the DESeq2 package
- Read in 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.
LS0tCnRpdGxlOiAiTW9kdWxlIDA2OiBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiAoREUpIEludHJvZHVjdGlvbiIKYXV0aG9yOiAiVU0gQmlvaW5mb3JtYXRpY3MgQ29yZSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgICAgICAgaHRtbF9kb2N1bWVudDoKICAgICAgICAgICAgaW5jbHVkZXM6CiAgICAgICAgICAgICAgICBpbl9oZWFkZXI6IGhlYWRlci5odG1sCiAgICAgICAgICAgIHRoZW1lOiBwYXBlcgogICAgICAgICAgICB0b2M6IHRydWUKICAgICAgICAgICAgdG9jX2RlcHRoOiA0CiAgICAgICAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICAgICAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICAgICAgICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICAgICAgICAgIG1hcmtkb3duOiBHRk0KICAgICAgICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CmJvZHksIHRkIHsKICAgZm9udC1zaXplOiAxOHB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDEycHg7Cn0KcHJlIHsKICBmb250LXNpemU6IDEycHgKfQo8L3N0eWxlPgoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0Kc291cmNlKCIuLi9iaW4vY2h1bmstb3B0aW9ucy5SIikKa25pdHJfZmlnX3BhdGgoIjA2LSIpCmBgYAoKPiAjIE9iamVjdGl2ZXMgey51bmxpc3RlZCAudW5udW1iZXJlZH0KPiAqIE92ZXJ2aWV3IG9mIHJlcHJvZHVjaWJsZSByZXNlYXJjaCAmIGFuYWx5c2lzIHNldHVwCj4gKiBCcm9hZCBpbnRyb2R1Y3Rpb24gdG8gREVTZXEyICYgd2h5IGl0IGlzIHdpZGVseSB1c2VkIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBjb21wYXJpc29ucwo+ICogSG93IHRvIGltcG9ydCBhbmQgcmV2aWV3IGdlbmUgY291bnQgdGFibGUKCgojIERpZmZlcmVudGlhbCBFeHByZXNzaW9uIFdvcmtmbG93IHsudW5saXN0ZWQgLnVubnVtYmVyZWR9CgpUb2RheSB3ZSB3aWxsIHByb2NlZWQgdGhyb3VnaCBrZXkgc3RlcHMgaW4gYSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcywgc3RhcnRpbmcgZnJvbSBhIGNvdW50IHRhYmxlIHRoYXQncyBzaW1pbGFyIHRvIHdoYXQgeW91IGdlbmVyYXRlZCBpbiB0aGUgZmlyc3QgaGFsZiBvZiB0aGUgd29ya3Nob3AgYW5kIG9uZSBvZiB0aGUgb3V0cHV0cyBpbmNsdWRlZCBpbiB0aGUgW2RhdGEgdGhhdCB0aGUgQWR2YW5jZWQgR2Vub21pY3MgQ29yZSBkZWxpdmVyc10oaHR0cHM6Ly9icmNmLm1lZGljaW5lLnVtaWNoLmVkdS9jb3Jlcy9hZHZhbmNlZC1nZW5vbWljcy9kYXRhLWRlbGl2ZXJ5LykgZm9yIFJOQS1zZXEgbGlicmFyaWVzLgoKPGltZyBzcmM9ImltYWdlcy93YXlmaW5kZXIvd2F5ZmluZGVyLURFQW5hbHlzaXNTZXR1cC5wbmciIGFsdD0id2F5ZmluZGVyIiBzdHlsZT0id2lkdGg6IDEwMDBweDsiLz4KCiMgR2V0dGluZyBTdGFydGVkIGluIFJTdHVkaW8KCiMjIExvZyBvbiB0byBSU3R1ZGlvIFNlcnZlcgoKT3BlbiBhIHdlYiBicm93c2VyIHRvIHRoZSBmb2xsb3dpbmcgVVJMOiBbaHR0cDovL2JmeC13b3Jrc2hvcDAxLm1lZC51bWljaC5lZHVdKGh0dHA6Ly9iZngtd29ya3Nob3AwMS5tZWQudW1pY2guZWR1KQoKWW91IHNob3VsZCBub3cgYmUgbG9va2luZyBhdCBhIHBhZ2UgdGhhdCB3aWxsIGFsbG93IHlvdSB0byBsb2dpbiB0byB0aGUgUlN0dWRpbyBzZXJ2ZXI6Cgo8aW1nIHNyYz0iaW1hZ2VzL3JzdHVkaW9fbG9naW5fc2NyZWVuLnBuZyIgYWx0PSJyc3R1ZGlvIGRlZmF1bHQgc2Vzc2lvbiIgc3R5bGU9IndpZHRoOiAxMDAwcHg7Ii8+CgpFbnRlciB5b3VyIHVzZXIgY3JlZGVudGlhbHMgYW5kIGNsaWNrIDxrYmQ+U2lnbiBJbjwva2JkPi4gVGhlIGNyZWRlbnRpYWxzIGFyZSB0aGUgc2FtZSB1c2VkIGVhcmxpZXIgaW4gdGhlIHdvcmtzaG9wIGFuZCB3ZXJlIHByb3ZpZGVkIHZpYSBlbWFpbC4gSG93ZXZlciwgaWYgeW91IGZvcmdldCB5b3VycywgYSBoZWxwZXIgY2FuIHJldHJpZXZlIGl0IGZvciB5b3UgaWYgeW91IGFzayBpbiBTbGFjay4gWW91IHNob3VsZCBub3cgc2VlIHRoZSBSU3R1ZGlvIGludGVyZmFjZToKCjxpbWcgc3JjPSJpbWFnZXMvcnN0dWRpb19zZXNzaW9uX2RlZmF1bHQucG5nIiBhbHQ9InJzdHVkaW8gZGVmYXVsdCBzZXNzaW9uIiBzdHlsZT0id2lkdGg6MTAwMHB4OyIvPgoKIyMgQ3JlYXRlIGFuIFJTdHVkaW8gUHJvamVjdAoKSW4gdGhlIENvbXB1dGF0aW9uYWwgRm91bmRhdGlvbiB3b3Jrc2hvcCB3ZSBjcmVhdGVkIGEgcHJvamVjdCByb290ZWQgaW4gYSBwYXJ0aWN1bGFyIGRpcmVjdG9yeSBhbmQgc3RvcmVkIG91ciBkYXRhLCBzY3JpcHRzIGFuZCBvdXRwdXQgd2l0aGluIHRoYXQgcHJvamVjdC4gTGV0J3MgZG8gdGhlIHNhbWUgdGhpbmcsIGJ1dCBmb3IgdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHBhcnQgb2YgdGhpcyB3b3Jrc2hvcC4KCjEuIFRvIGNyZWF0ZSBhIHByb2plY3QsIGdvIHRvIHRoZSA8a2JkPkZpbGU8L2tiZD4gbWVudSwgYW5kIGNsaWNrIDxrYmQ+TmV3IFByb2plY3QuLi48L2tiZD4uIFRoZSBmb2xsb3dpbmcgd2luZG93IHdpbGwgYXBwZWFyOgoKPGltZyBzcmM9ImltYWdlcy9uZXdfcHJvamVjdF93aW5kb3cucG5nIiBhbHQ9Im5ldyBwcm9qZWN0IHdpbmRvdyIgc3R5bGU9IndpZHRoOiA2MDBweDsiLz4KCjIuIEluIHRoaXMgd2luZG93LCBzZWxlY3QgPGtiZD5FeGlzdGluZyBEaXJlY3Rvcnk8L2tiZD4uIEZvciAiUHJvamVjdCB3b3JraW5nIGRpcmVjdG9yeSIsIGNsaWNrIDxrYmQ+QnJvd3NlLi4uPC9rYmQ+LCBzZWxlY3QgdGhlICJSU0RfUiIgZm9sZGVyLCBhbmQgY2xpY2sgPGtiZD5DaG9vc2U8L2tiZD4uIFRoaXMgd2lsbCB1c2UgdGhlIGAvaG9tZS93b3Jrc2hvcC91c2VyL1JTRF9SYCBmb2xkZXIgYXMgdGhlIHByb2plY3QgZGlyZWN0b3J5LgoKMy4gRmluYWxseSBjbGljayA8a2JkPkNyZWF0ZSBQcm9qZWN0PC9rYmQ+LiBJbiB0aGUgIkZpbGVzIiB0YWIgb2YgeW91ciBvdXRwdXQgcGFuZSAobW9yZSBhYm91dCB0aGUgUlN0dWRpbyBsYXlvdXQgaW4gYSBtb21lbnQpLCB5b3Ugc2hvdWxkIHNlZSBhbiBSU3R1ZGlvIHByb2plY3QgZmlsZSwgKipSU0RfUi5ScHJvaioqLiBBbGwgUlN0dWRpbyBwcm9qZWN0cyBlbmQgd2l0aCB0aGUgIioqLlJwcm9qKioiIGZpbGUgZXh0ZW5zaW9uLgoKTm90ZSB0aGF0IHRoZXJlIGlzIGFscmVhZHkgYSBgZGF0YS9gIGZvbGRlciB3aGljaCBjb250YWlucyB0aGUgZGF0YSB3ZSB3aWxsIHVzZSBmb3IgdGhlc2UgbGVzc29ucy4KCiMjIENyZWF0aW5nIGFuIFIgc2NyaXB0CgpMZXQncyBjcmVhdGUgYW4gUiBzY3JpcHQgZmlsZSB0byBzdG9yZSBhbGwgdGhlIGNvZGUgd2UncmUgZ29pbmcgdG8gcnVuIGluIHRoZSBmb3J0aGNvbWluZyBsZXNzb25zLgoKLSBDbGljayB0aGUgPGtiZD5GaWxlPC9rYmQ+IG1lbnUgYW5kIHNlbGVjdCA8a2JkPk5ldyBGaWxlPC9rYmQ+IGFuZCB0aGVuIDxrYmQ+UiBTY3JpcHQ8L2tiZD4uCi0gQmVmb3JlIHdlIGdvIGFueSBmdXJ0aGVyLCBzYXZlIHlvdXIgc2NyaXB0IGJ5IGNsaWNraW5nIHRoZSBzYXZlL2Rpc2sgaWNvbiB0aGF0IGlzIGluIHRoZSBiYXIgYWJvdmUgdGhlIGZpcnN0IGxpbmUgaW4gdGhlIHNjcmlwdCBlZGl0b3IsIG9yIGNsaWNrIHRoZSA8a2JkPkZpbGU8L2tiZD4gbWVudSBhbmQgc2VsZWN0IDxrYmQ+U2F2ZTwva2JkPi4KLSBJbiB0aGUgIlNhdmUgRmlsZSIgd2luZG93IHRoYXQgb3BlbnMsIHNlbGVjdCA8a2JkPk5ldyBGb2xkZXI8L2tiZD4uIE5hbWUgaXQgKioic2NyaXB0cyIqKi4KLSBGaW5hbGx5LCBuYW1lIHlvdXIgZmlsZSAqKiJkaWZmZXgiKiogaW4gdGhlICJGaWxlIG5hbWUiIGZpZWxkLgoKVGhlIG5ldyBzY3JpcHQgKipkaWZmZXguUioqIGlzIG5vdyBpbiB0aGUgYHNjcmlwdHNgIGZvbGRlci4gWW91IGNhbiBzZWUgdGhhdCBieSBjbGlja2luZyB0aGUgYHNjcmlwdHNgIGZvbGRlciBpbiB0aGUgIkZpbGVzIiBwYW5lLiBBbmQgeW91IGNhbiBnbyBiYWNrIHVwIHRvIHRoZSBtYWluIHByb2plY3QgZm9sZGVyIGJ5IGNsaWNraW5nIHRoZSBgLi5gIHRvIHRoZSByaWdodCBvZiB0aGUgdXAgYXJyb3cgaW4gdGhlICJGaWxlcyIgcGFuZS4gQnkgY29udmVudGlvbiwgUiBzY3JpcHRzIGVuZCB3aXRoIHRoZSBmaWxlIGV4dGVuc2lvbiAqKi5SKiouCgojIFJlcHJvZHVjaWJsZSByZXNlYXJjaAoKVG9kYXkgd2UnbGwgYmUgZXhwbG9yaW5nIHNvbWUgUk5BLXNlcSBkYXRhIHRoYXQgaXMgZmFpcmx5IHJlcHJlc2VudGF0aXZlIG9mIHdoYXQgd2Ugc2VlIGluIHRoZSBjb3JlIGFuZCBzdGFydCB3aXRoIGEgY291bnQgdGFibGUgc2ltaWxhciB0byB3aGF0IGdlbmVyYXRlZCBpbiB0aGUgcHJldmlvdXMgc3RlcHMgYW5kIG9uZSBvZiB0aGUgb3V0cHV0cyBjdXJyZW50bHkgYmVpbmcgZGVsaXZlcmVkIGJ5IHRoZSBBZHZhbmNlZCBTZXF1ZW5jaW5nIENvcmUuCgojIyBGaWxlIG9yZ2FuaXphdGlvbgoKQXMgd2lkZWx5IGRpc2N1c3NlZCwgaW5jbHVkaW5nIGluIGEgcmV2aWV3IGJ5IFtOb2JlbCwgMjAwOV0oaHR0cHM6Ly9qb3VybmFscy5wbG9zLm9yZy9wbG9zY29tcGJpb2wvYXJ0aWNsZT9pZD0xMC4xMzcxL2pvdXJuYWwucGNiaS4xMDAwNDI0KSwgZmlsZSBvcmdhbml6YXRpb24gYW5kIGRhdGEgc3Rld2FyZHNoaXAgYXJlIGFuIGltcG9ydGFudCBwYXJ0cyBvZiByZXByb2R1Y2libGUgcmVzZWFyY2guCgpUbyBmb2xsb3cgYmVzdCBwcmFjdGljZXMgZm9yIGZpbGUgb3JnYW5pemF0aW9uIGZvciBiaW9pbmZvcm1hdGljcy9jb21wdXRhdGlvbmFsIHByb2plY3RzLCB3ZSBzaG91bGQgbWFrZSBkaXN0aW5jdCBsb2NhdGlvbnMgZm9yOgoKKiBSYXcgZGF0YQoqIENvZGUKKiBPdXRwdXQKClN1Y2ggYXMgaWxsdXN0cmF0ZWQgaW4gdGhpcyBmaWd1cmUgZnJvbSB0aGUgTm9ibGUgcmV2aWV3OgoKPGltZyBzcmM9ImltYWdlcy9Ob2JsZTIwMDlfZGF0YXByb2plY3RzLnBuZyIgYWx0PSJuZXcgcHJvamVjdCB3aW5kb3ciIHN0eWxlPSJ3aWR0aDogNjAwcHg7Ii8+CgpUbyBvcmdhbml6ZSBvdXIgZmlsZXMgZm9yIG91ciBhbmFseXNpcyB0b2RheSwgd2UnbGwgY3JlYXRlIHNvbWUgbW9yZSBkaXJlY3RvcmllcyBpbiB0aGUgYFJTRF9SYCBmb2xkZXIgd2hpY2ggaXMgbm93IG91ciBwcm9qZWN0IHJvb3QgYW5kIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkuIFdlIGNhbiBhbHJlYWR5IHNlZSB0aGF0IHRoZXJlIGEgYGRhdGEvYCBmb2xkZXIgYW5kIHdlIGp1c3QgY3JlYXRlZCB0aGUgYHNjcmlwdHMvYCBmb2xkZXIgd2l0aCB0aGUgUlN0dWRpbyBHVUkuIExldCdzIGNyZWF0ZSBhbiBgb3V0cHV0cy9gIGZvbGRlciB1c2luZyB0aGUgYGRpci5jcmVhdGUoKWAgZnVuY3Rpb24gaW4gUi4KCmBgYHtyIGNyZWF0ZV9kaXJzLCBldmFsID0gRkFMU0V9CmRpci5jcmVhdGUoJ291dHB1dHMnKQpkaXIuY3JlYXRlKCdvdXRwdXRzL2ZpZ3VyZXMnKQpkaXIuY3JlYXRlKCdvdXRwdXRzL3RhYmxlcycpCmBgYAoKKipDaGVja3BvaW50Kio6ICpQbGVhc2UgdXNlIHRoZSBncmVlbiAnY2hlY2snIGlmIHlvdSBoYXZlIHNhdmVkIHlvdXIgY29kZSBmaWxlIGFuZCBzZWUgdGhlIGBkYXRhYCwgYHNjcmlwdHNgLCBhbmQgYG91dHB1dHNgIGRpcmVjdG9yaWVzIGluIHRoZSBgUlNEX1JgIGZvbGRlciBhbmQgdGhlIHJlZCAneCcgaWYgeW91IGRvIG5vdC4qCgo+ICMjIFJlbWluZGVyOiBSU3R1ZGlvIGNvZGUgZXhlY3V0aW9uIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cj4KPiA8a2JkPkN0cmw8L2tiZD4rPGtiZD5FbnRlcjwva2JkPiBpcyBhIHN0YW5kYXJkIHNob3J0Y3V0IGluIFJTdHVkaW8gdG8gc2VuZCB0aGUgY3VycmVudCBsaW5lIChvciBzZWxlY3RlZCBsaW5lcykgdG8gdGhlIGNvbnNvbGUuIElmIHlvdSBzZWUgYD5gIGluIHRoZSBDb25zb2xlLCB0aGVuIFIgaGFzIGV4ZWN1dGVkIHRoZSBjb21tYW5kLiBJZiB5b3Ugc2VlIGEgYCtgLCB0aGlzIG1lYW5zIHRoYXQgdGhlIGNvbW1hbmQgaXMgbm90IGNvbXBsZXRlLCBSIHRoaW5rcyB0aGVyZSBpcyBtb3JlIHRvIHlvdXIgY29tbWFuZC4gWW91IGNhbiB1c2UgdGhlIDxrbWQ+ZXNjPC9rbWQ+IHRvIGdldCBvdXQgb2YgdGhpcyBzdGF0ZS4KCj4gIyMgUmVtaW5kZXI6IE9iamVjdCBuYW1pbmcgY29udmVudGlvbnMgey51bmxpc3RlZCAudW5udW1iZXJlZH0KPiAqIENhbm5vdCBzdGFydCB3aXRoIG51bWJlcnMKPiAqIENhbm5vdCBpbmNsdWRlIGRhc2hlcwo+ICogQ2Fubm90IGhhdmUgc3BhY2VzCj4gKiBTaG91bGQgbm90IGJlIGlkZW50aWNhbCB0byBhIG5hbWVkIGZ1bmN0aW9uCj4gKiBEb3RzIGFuZCB1bmRlcnNjb3JlcyBjYW4gc2VwYXJhdGUgcGFydHMgb2YgbmFtZXMsIGFsdGVybmF0aXZlbHkgQ2FtZWxDYXNlIGFjY29tcGxpc2hlcyB0aGlzCgotLS0tCgojIFRvb2xzIGZvciBEaWZmZXJlbnRpYWwgR2VuZSBFeHByZXNzaW9uIGFuYWx5c2lzCgpBcyBkaXNjdXNzZWQgZHVyaW5nIHRoZSB3ZWJpbmFyLCBidWxrIFJOQS1zZXEgY2FuIGJlIHVzZWQgdG8gdGVzdCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYmV0d2VlbiBjb25kaXRpb25zIG9yIHRyZWF0bWVudHMsIHVzaW5nIHN0YXRpc3RpY2FsIGFwcHJvYWNoZXMgdGhhdCBhcmUgYXBwcm9wcmlhdGUgZm9yIGJpb2xvZ2ljYWwgZGF0YS4KCldoaWxlIHRoZXJlIGFyZSBzZXZlcmFsIHRvb2xzIHRoYXQgY2FuIGJlIHVzZWQgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGNvbXBhcmlzb25zLCB3ZSB3aWxsIHVzZSBbREVTZXEyXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvREVTZXEyLmh0bWwpIGluIG91ciBhbmFseXNpcyB0b2RheS4KCkRFU2VxMiBpcyBvbmUgb2YgdHdvIHRvb2xzLCBhbG9uZyB3aXRoIFtlZGdlUl0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL2VkZ2VSLmh0bWwpLCBjb25zaWRlcmVkIFsnYmVzdCBwcmFjdGljZSddKGh0dHBzOi8vYm1jYmlvaW5mb3JtYXRpY3MuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni8xNDcxLTIxMDUtMTQtOTEpIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBjb21wYXJpc29ucy4gQm90aCB0b29scyBhcHBseSBzaW1pbGFyIG1ldGhvZHMgdGhhdCBhY2NvdW50IGZvciB0aGUgZGlzdHJpYnV0aW9ucyB3ZSBleHBlY3QgdG8gc2VlIGZvciBSTkEtc2VxIGFuZCBhcmUgZmFpcmx5IHN0cmluZ2VudCBpbiBjYWxsaW5nIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcywgbG93ZXJpbmcgdGhlIHJpc2sgb2YgaW52ZXN0aWdhdGluZyBnZW5lcyB0aGF0IHdlcmUgcmVhbGx5IGZhbHNlIHBvc2l0aXZlcyAoZS5nLiBkb24ndCBhY3R1YWxseSBoYXZlIGRpZmZlcmVudCBleHByZXNzaW9uIGJldHdlZW4gdHJlYXRtZW50IGdyb3VwcyBhbmQgdGhlcmVmb3JlIGFyZSBub3QgcmVsZXZhbnQgdG8gdGhlIGJpb2xvZ2ljYWwgcHJvY2VzcykuCgpBZGRpdGlvbmFsbHksIERFU2VxMiBhbHNvIGhhcyBhbiBleGNlbGxlbnQgdmlnbmV0dGUgWyhsaW5rKV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sKQpmcm9tIExvdmUsIEFuZGVycywgYW5kIEh1YmVyLCBmcm9tIHdoaWNoIG91ciB3b3JrZmxvdyBpcyBwYXJ0aWFsbHkgYWRhcHRlZCwgYW5kIGlzIGEgZ29vZCByZXNvdXJjZSB3aGVuIGFuYWx5emluZyB5b3VyIG93biBkYXRhCihzZWUgYWxzbzogW0xvdmUsIEFuZGVycywgYW5kIEh1YmVyLiBfR2Vub21lIEJpb2xvZ3lfLiAyMDE0Ll0oaHR0cHM6Ly9kb2kub3JnLzEwLjExODYvczEzMDU5LTAxNC0wNTUwLTgpKS4KCgojIyBERVNlcTIgYXNzdW1wdGlvbnMgYW5kIHJlcXVpcmVtZW50cwoKQSBrZXkgYXNzdW1wdGlvbiBvZiBERVNlcTIgaXMgdGhhdCAqYmlvbG9naWNhbCB2YXJpYW5jZSBpcyBtdWNoIGdyZWF0ZXIgdGhhbiB0ZWNobmljYWwgdmFyaWFuY2UqIChlc3BlY2lhbGx5IGlmIFtiZXN0IHByYWN0aWNlc10oaHR0cHM6Ly93d3cudHhnZW4udGFtdS5lZHUvZmFxL3JuYS1pc29sYXRpb24tYmVzdC1wcmFjdGljZXMvKSBmb3IgW3F1YWxpdHkgUk5BIGlzb2xhdGlvbl0oaHR0cHM6Ly93d3cuYmlvY29tcGFyZS5jb20vQmVuY2gtVGlwcy8xMjg3OTAtRm91ci1UaXBzLWZvci1QZXJmZWN0aW5nLVJOQS1Jc29sYXRpb24vKSBhcmUgZm9sbG93ZWQsIGluY2x1ZGluZyBETmFzZSB0cmVhdG1lbnQhKS4KClNpbmNlIGNhbGN1bGF0aW5nIHZhcmlhbmNlIGlzIGtleSB0byB0aGUgc3RhdGlzdGljYWwgYXBwcm9hY2ggdXNlZCBmb3IgREVTZXEyLCBpZiB3ZSB0cmllZCB0byBjb21wYXJlIHR3byB0cmVhdG1lbnQgZ3JvdXBzIHdpdGggbGVzcyB0aGFuICoqdHdvKiogcmVwbGljYXRlcywgd2Ugd291bGQgZ2V0IGFuIGVycm9yIChhcyBzaG93biBpbiBbdGhpcyBibG9nIHBvc3RdKGh0dHBzOi8vc3VwcG9ydC5iaW9jb25kdWN0b3Iub3JnL3AvODk3NDYvKSkuIFdpdGhvdXQgcmVwbGljYXRlcywgdGhlcmUgY2FuJ3QgYmUgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIChlLmcuIHAtdmFsdWVzKSwgYnV0ICpxdWFsaXRhdGl2ZSogYXBwcm9hY2hlcyBhcmUgYW4gb3B0aW9uLCBsaWtlIGxvb2tpbmcgYXQgdGhlIHRvcCBleHByZXNzZWQgZ2VuZXMgYWZ0ZXIgbm9ybWFsaXphdGlvbi4KCjxkZXRhaWxzPgogICAgPHN1bW1hcnk+KkNsaWNrIGZvciBhZGRpdGlvbmFsIHJlc291cmNlcyByZWdhcmRpbmcgc3RhdGlzdGljYWwgdGVzdGluZyBhbmQgdG9vbCBjb21wYXJpc29uIGZvciBSTkEtc2VxIGRhdGEqPC9zdW1tYXJ5PgogICAgVG8gbGVhcm4gbW9yZSBhYm91dCBzdGF0aXN0aWNhbCB0ZXN0aW5nIGFuZCB3aGF0IGRpc3RyaWJ1dGlvbnMgYmVzdCBtb2RlbCB0aGUgYmVoYXZpb3Igb2YgUk5BLXNlcSBkYXRhLCBhIGdvb2QgcmVzb3VyY2UgaXMgdGhpcyBbRWRYIGxlY3R1cmUgYnkgUmFmYWVsIElyaXphcnJ5XShodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PUhLN1dLc0wzYzJ3JmZlYXR1cmU9eW91dHUuYmUpIG9yIHRoaXMgW2xlY3R1cmUgYnkgS2FzcGVyIEhhbnNlbl0oaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1DOFJOdld1N3BBdykuIEFub3RoZXIgaGVscGZ1bCBndWlkZSBpcyB0aGlzIFtDb21wYXJhdGl2ZSBTdHVkeSBmb3IgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gQW5hbHlzaXMgYnkgWmhhbmcgZXQgYWwuXShodHRwczovL2pvdXJuYWxzLnBsb3Mub3JnL3Bsb3NvbmUvYXJ0aWNsZT9pZD0xMC4xMzcxL2pvdXJuYWwucG9uZS4wMTAzMjA3KSBmcm9tIDIwMTQuCjwvZGV0YWlscz4KPGJyPgoKPiAjIyBFeGVyY2lzZTogQnVpbGRpbmcgYSBiZXR0ZXIgdW5kZXJzdGFuZGluZyBvZiBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyB7LnVubGlzdGVkIC51bm51bWJlcmVkfQo+Cj4gLSBQb3N0IGEgcXVlc3Rpb24gdGhhdCB0aGF0IHdhcyBOT1QgYWRkcmVzc2VkIGJ1dCB0aGF0IHlvdSBob3BlIHdlIHdpbGwgYWRkcmVzcyBpbiB0aGUgbGF0ZXIgbW9kdWxlcyAqKk9SKioKPiAtIEFkZCBhIHRodW1icyB1cCB0byB5b3VyIGZhdm9yaXRlIGNvbW1lbnQocykgdG8gdXB2b3RlIGl0CgotLS0tCgojIEdldHRpbmcgc3RhcnRlZAoKIyMgTG9hZCBQYWNrYWdlcwoKU2V2ZXJhbCBwYWNrYWdlcyBoYXZlIGFscmVhZHkgYmVlbiBpbnN0YWxsZWQgb24gdGhlIHNlcnZlciwgc28gd2UgY2FuIGxvYWQgdGhlbSBpbnRvIG91ciBSIHNlc3Npb24gbm93LiBUbyBkbyB0aGF0IHdlJ2xsIHVzZSB0aGUgYGxpYnJhcnlgIGZ1bmN0aW9uIHRvIGxvYWQgdGhlIHJlcXVpcmVkIHBhY2thZ2VzLgoKYGBge3IgTW9kdWxlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz0gRkFMU0UsIGV2YWw9VFJVRX0KbGlicmFyeShERVNlcTIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKbGlicmFyeShtYXRyaXhTdGF0cykKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShkYXRhLnRhYmxlKQpgYGAKCipOb3RlOiBXZSBleHBlY3QgdG8gc2VlIHNvbWUgcmVkIG1lc3NhZ2VzIGluIHlvdXIgY29uc29sZSB3aGlsZSB0aGVzZSBwYWNrYWdlcyBhcmUgbG9hZGluZyoKClIvUlN0dWRpbyBbQUtBIFBvc2l0XSBoYXMgZ3JlYXQgcmVzb3VyY2VzIGZvciBnZXR0aW5nIGhlbHAsIGluY2x1ZGluZyBbY29kZSAnY2hlYXRzaGVldHMnXShodHRwczovL3Bvc2l0LmNvL3Jlc291cmNlcy9jaGVhdHNoZWV0cy8pIGFuZCBwYWNrYWdlIHZpZ25ldHRlcywgbGlrZSBmb3IgW3RpZHlyXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdGlkeXIvdmlnbmV0dGVzL3RpZHktZGF0YS5odG1sKS4KClNpbmNlIHdlIGxvYWRlZCB0aGUgbGlicmFyaWVzIGludG8gb3VyIFIgc2Vzc2lvbiwgd2UgY2FuIHNlZSBkb2N1bWVudGF0aW9uIG91dCB1c2luZyB0aGUgYD9gIG9wZXJhdG9yLgpgYGB7ciBDaGVja0RvY3VtZW50YWlvbn0KP2BERVNlcTItcGFja2FnZWAKYGBgCgoqKkNoZWNrcG9pbnQqKjogKklmIHlvdSBzZWUgdGhlIFIgZG9jdW1lbnRhdGlvbiBmb3IgREVTZXEyIHBvcCB1cCBpbiB5b3VyICdoZWxwJyBwYW5lbCBvbiB0aGUgcmlnaHQsIHBsZWFzZSBpbmRpY2F0ZSB3aXRoIHRoZSBncmVlbiAnY2hlY2snIGJ1dHRvbi4gSWYgbm90IHBsZWFzZSB1c2UgdGhlIHJlZCAneCcgYnV0dG9uLioKCiMjIFJlYWQgQ291bnRzCgpBbm90aGVyIGtleSBhc3N1bXB0aW9uIGZvciBERVNlcTIgaXMgdGhhdCB0aGUgYW5hbHlzaXMgd2lsbCBzdGFydCB3aXRoIFt1bi1ub3JtYWxpemVkIGNvdW50c10oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI3doeS11bi1ub3JtYWxpemVkLWNvdW50cykuCgpUbyBiZWdpbiBvdXIgYW5hbHlzaXMsIHdlJ2xsIHJlYWQgaW4gdGhlICoqcmF3KiogY291bnQgZGF0YSBmaWxlLCBgZ2VuZV9leHBlY3RlZF9jb3VudC50eHRgIHdoaWNoIGlzIHNpbWlsYXIgdG8gd2hhdCB3b3VsZCBiZSBnZW5lcmF0ZWQgaW4gdGhlIGFsaWdubWVudCBzdGVwcyAoYW5kIHdoYXQgeW91IHdvdWxkIHJlY2VpdmUgZnJvbSBBR0MpLiBXZSdsbCBkaXNjdXNzIGEgZmV3IG5vcm1hbGl6YXRpb25zIHRoYXQgY2FuIGJlIGhlbHBmdWwgZm9yIHVzIHRvIHVuZGVyc3RhbmQgaG93IG11Y2ggZ2VuZXMgYXJlIGV4cHJlc3NlZCB3aXRoaW4gb3IgYmV0d2VlbiBzYW1wbGVzLCBidXQgcmF3IGRhdGEgKipzaG91bGQgYWx3YXlzKiogYmUgdXNlZCBhcyBhbiBpbnB1dCBmb3IgREVTZXEyLgoKYGBge3IgRGF0YVRhYmxlLCBldmFsPVRSVUUsIGVjaG89RkFMU0V9CiMjIG5lZWQgdG8gaW5jbHVkZSBwYXRocyByZWxhdGl2ZSB0byBzaXRlIGJ1aWxkIGxvY2F0aW9ucyBidXQga2VlcCBjb2RlIGJsb2NrIGhpZGRlbgpjb3VudF90YWJsZSA9IHJlYWQudGFibGUoIi4uL2RhdGEvZ2VuZV9leHBlY3RlZF9jb3VudC50eHQiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKQpgYGAKCmBgYHtyIERhdGFUYWJsZTIsIGV2YWw9RkFMU0V9CmNvdW50X3RhYmxlID0gcmVhZC50YWJsZSgiZGF0YS9nZW5lX2V4cGVjdGVkX2NvdW50LnR4dCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpCmBgYAoKYGBge3IgRGF0YVRhYmxlQ2hlY2ssIGV2YWw9VFJVRX0KaGVhZChjb3VudF90YWJsZSwgbj0yKSAjIGxvb2sgYXQgdGhlIHRvcCBvZiB0aGUgdGFibGUKYGBgCgpOb3cgdGhhdCB0aGUgZmlsZSBpcyByZWFkIGludG8gUiwgbm90ZSB0aGF0IHdlJ3ZlIGNyZWF0ZWQgYSBkYXRhIGZyYW1lIHRoYXQgaW5jbHVkZXMgJ2dlbmUgaWRzJyBpbiBFTlNFTUJMIGZvcm1hdCBhcyByb3duYW1lcyBhbmQgY291bnQgZGF0YSBmcm9tIHR3ZWx2ZSBkaWZmZXJlbnQgc2FtcGxlcy4KCj4gIyBFeGVyY2lzZTogUlNFTSBvdXRwdXRzIHZlcnN1cyBERVNlcTIgaW5wdXQgcmVxdWlyZW1lbnRzIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cj4KPiBJZiB3ZSB0aGluayBiYWNrIHRvIHRoZSBSU0VNIG91dHB1dHMsIHRoZSAnZXhwZWN0ZWRfY291bnRzJyB0YWJsZSBtYXkgaW5jbHVkZSBmcmFjdGlvbmFsIGFtb3VudHMgZHVlIHRvIGhvdyB0aGUgYWxpZ25tZW50IHRvb2wgcmVzb2x2ZXMgcmVhZHMgbWFwcGluZyB0byBtdWx0aXBsZSBsb2N1c2VzKS4gREVTZXEyIGlzIGV4cGVjdGluZyB0aGUgY291bnQgbWF0cml4IHRvIGNvbnNpc3Qgb25seSBvZiB3aG9sZSBudW1iZXJzLiBSZWNlbnQgdmVyc2lvbnMgb2YgREVTZXEyIHdpbGwgaGFuZGxlIHRoaXMgZm9yIHVzLCBidXQgcHJldmlvdXMgdmVyc2lvbnMgZGlkIG5vdC4KPgo+IFdoYXQgZnVuY3Rpb24sIHRoYXQgd2Ugc2F3IGVhcmx5IG9uIGluIHRoZSBDb21wdXRhdGlvbmFsIEZvdW5kYXRpb25zIFIgbGVzc29ucywgY291bGQgd2UgdXNlIHRvIGNvbnZlcnQgZnJhY3Rpb25hbCB2YWx1ZXMgaW4gYGNvdW50X3RhYmxlYCB0byBpbnRlZ2Vycz8KCjxkZXRhaWxzPgo8c3VtbWFyeT5Tb2x1dGlvbjwvc3VtbWFyeT4KCldlIGNhbiB1c2UgdGhlIGByb3VuZCgpYCBmdW5jdGlvbjoKCmBgYHtyIHJvdW5kIGNvdW50X3RhYmxlfQpjb3VudHNfcm91bmRlZCA9IHJvdW5kKGNvdW50X3RhYmxlKQpgYGAKPC9kZXRhaWxzPgo8YnI+CgojIEdldHRpbmcgaGVscAoKUiBhbmQgUlN0dWRpbyBoYXZlIGEgc3Ryb25nIGNvbW11bml0eSBjb21wb25lbnQgc28gaWYgeW91IGFyZSBnZXR0aW5nIGFuIGVycm9yIG9yIHdvbmRlcmluZyBob3cgdG8gbWFrZSBhIGNvbW1hbmQgd29yayBvciBob3cgdG8gcGVyZm9ybSBhIHNwZWNpZmljIHRhc2ssIHRoZXJlIGlzIGxpa2VseSBhbHJlYWR5IGEgc29sdXRpb24gb3V0IHRoZXJlLiBSZW1lbWJlciB0aGF0IEdvb2dsZSBpcyB5b3VyIGZyaWVuZCwgYWx0aG91Z2ggaXQgY2FuIHNvbWV0aW1lcyBiZSBhIGNoYWxsZW5nZSB0byBmaWd1cmUgb3V0ICp3aGF0IHRvIHNlYXJjaCBmb3IqLiBLZXkgcGFydHMgb2YgYSBzdWNjZXNzZnVsIHNlYXJjaDoKCiogUGFja2FnZSBvciBjb21tYW5kIHJ1bgoqIGBSYCBvciBgQmlvY29uZHVjdG9yYAoqIFRoZSBlcnJvciBtZXNzYWdlIGlmIHRoZXJlIGlzIG9uZQoqIFZlcnNpb24gaW5mb3JtYXRpb24KCkhvdyB0byBnZXQgc2Vzc2lvbiBpbmZvcm1hdGlvbiB0byBhaWQgaW4gYSBzZWFyY2g6CmBgYHtyIFNlc3Npb24gaW5mbywgZXZhbCA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CnNlc3Npb25JbmZvKCkKYGBgCgpIaWdobHkgcmVjb21tZW5kIHVzaW5nIHJlc291cmNlcyBsaWtlIFtCaW9jb25kdWN0b3IgU3VwcG9ydF0oaHR0cHM6Ly9zdXBwb3J0LmJpb2NvbmR1Y3Rvci5vcmcvKSwgW0Jpb3N0YXJzXShodHRwczovL3d3dy5iaW9zdGFycy5vcmcvKSwgYW5kIFtTdGFjayBPdmVyZmxvd10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvdGFnZ2VkL3IpLCBpbmNsdWRpbmcgdGhyZWFkcyBvbiBzcGVjaWZpYyBwYWNrYWdlcyBvciBjb21tb24gYmlvaW5mb3JtYXRpYyB0YXNrcy4KCkkgcGVyc29uYWxseSB1c2Ugb25lIG9yIG1vcmUgb2YgdGhlc2UgcmVzb3VyY2VzICoqZXZlcnkgZGF5KiouCgoxMHggR2Vub21pY3MgaGFzIGEgaGVscGZ1bCBbMTAgdGlwcyBmb3IgYmlvbG9naXN0cyBsZWFybmluZyAgYmlvaW5mb3JtYXRpY3NdKGh0dHBzOi8vd3d3LjEweGdlbm9taWNzLmNvbS9yZXNvdXJjZXMvYW5hbHlzaXMtZ3VpZGVzLzEwLXRpcHMtZm9yLWJpb2xvZ2lzdHMtbGVhcm5pbmctYmlvaW5mb3JtYXRpY3MpIGluY2x1ZGVkIGluIHRoZWlyIHJlc291cmNlcy4KCiMgU3VtbWFyeQoKSW4gdGhpcyBzZWN0aW9uLCB3ZToKCiogU2V0IHVwIG91ciBjb21wdXRlIGVudmlyb25tZW50CiogTGVhcm5lZCBhYm91dCB0aGUgREVTZXEyIHBhY2thZ2UKKiBSZWFkIGluIGEgcmF3IGNvdW50IHRhYmxlIGFuZCBzYXZlZCBpdCBhcyBhIGRhdGEgZnJhbWUKCk5vdyB0aGF0IHdlIGhhdmUgb3VyIGNvdW50IGRhdGEgcHJvY2Vzc2VkLCB3ZSBjYW4gbW92ZSBvbiB0byAidW5ibGluZGluZyIgb3VyIGRhdGEsIGFzIHRoZSBzYW1wbGUgbmFtZXMgYXJlIHVuaXF1ZSBpZGVudGlmaWVycyBnZW5lcmF0ZWQgYnkgYSBzZXF1ZW5jaW5nIGNlbnRlciBhbmQgbm90IHZlcnkgaW5mb3JtYXRpdmUgYXMgZmFyIGFzIG91ciBleHBlcmltZW50YWwgY29uZGl0aW9ucy4KCi0tLQoKIyBPcHRpb25hbCBjb250ZW50CgpBbiBpbXBvcnRhbnQgbm90ZSBpcyB0aGF0IHRoZXJlIGFyZSBzZXZlcmFsIGJvbnVzIGNvbnRlbnQgc2VjdGlvbnMgb24gdGhlIGluc3RydWN0aW9uIHBhZ2VzLCBsaWtlIHRoZSB0d28gYmVsb3cgdGhhdCB3ZSB3aWxsIG5vdCBiZSBjb3ZlcmluZyBpbiB0aGlzIHdvcmtzaG9wLCBidXQgdGhhdCBtYXkgaGF2ZSB1c2VmdWwgY29udGV4dCBvciBiZSBoZWxwZnVsIHdoZW4geW91IHJldmlldyB0aGlzIG1hdGVyaWFsLgoKPGRldGFpbHM+CiAgICA8c3VtbWFyeT4qQ2xpY2sgZm9yIGFsdGVybmF0aXZlIERFU2VxMiBpbnB1dCBvcHRpb25zIGZvciBSU0VNIG91dHB1dHMqPC9zdW1tYXJ5PgogICAgVGhlIHBhY2thZ2UgYHR4aW1wb3J0YCBpcyBhbm90aGVyIG9wdGlvbltyZWNvbW1lbmRlZCB0aGUgREVTZXEyICBhdXRob3JzXShodHRwczovL3N1cHBvcnQuYmlvY29uZHVjdG9yLm9yZy9wLzkwNjcyLykgdG8gcmVhZCBpbiB0aGUgUlNFTSBleHBlY3RlZF9jb3VudHMsIGFzIHRoaXMgIHBhY2thZ2UgYWxsb3dzIGZvciB0aGUgYXZlcmFnZSB0cmFuc2NyaXB0IGxlbmd0aCBwZXIgZ2VuZSB0byBiZSB1c2VkIGluIHRoZSBERSBhbmFseXNpcyBhbmQsIGFzIFtkZXNjcmliZWQgYnkgdGhlIGF1dGhvcl0oaHR0cHM6Ly9zdXBwb3J0LmJpb2NvbmR1Y3Rvci5vcmcvcC84ODc2My8pLCB0aGUgYHR4aW1wb3J0LXRvLURFU2VxRGF0YVNldGAgY29uc3RydWN0b3IgZnVuY3Rpb24gcm91bmQgdGhlIG5vbi1pbnRlZ2VyIGRhdGEgZ2VuZXJhdGVkIGJ5IFJTRU0gdG8gd2hvbGUgbnVtYmVycy4KPC9kZXRhaWxzPgo8YnI+CjxkZXRhaWxzPgogICAgPHN1bW1hcnk+KkNsaWNrIGZvciBjb21wYXJpc29uIG9mIFJOQS1zZXEgZGF0YSBhbmQgbWljcm9hcnJheSBkYXRhKjwvc3VtbWFyeT4KICAgIFdpdGggW2hpZ2hlciBzZW5zaXRpdml0eSwgZ3JlYXRlciBmbGV4aWJsaXR5LCBhbmQgZGVjcmVhc2luZyBjb3N0XShodHRwczovL3d3dy5pbGx1bWluYS5jb20vc2NpZW5jZS90ZWNobm9sb2d5L25leHQtZ2VuZXJhdGlvbi1zZXF1ZW5jaW5nL21pY3JvYXJyYXktcm5hLXNlcS1jb21wYXJpc29uLmh0bWwpLCBzZXF1ZW5jaW5nIGhhcyBsYXJnZWx5IHJlcGxhY2VkIG1pY3JvYXJyYXkgYXNzYXlzIGZvciBtZWFzdXJpbmcgZ2VuZSBleHByZXNzaW9uLiBBIGtleSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHBsYXRmb3JtcyBpcyB0aGF0IG1pY3JvYXJyYXlzIG1lYXN1cmUgaW50ZW5zaXRpZXMgYW5kIGFyZSB0aGVyZWZvcmUgKmNvbnRpbm91cyogZGF0YSB3aGlsZSB0aGUgY291bnQgZGF0YSBmcm9tIHNlcXVlbmNpbmcgaXMgKmRpc2NyZXRlKi4gQSBtb3JlIGRldGFpbGVkIGNvbXBhcmlzb24gYmV0d2VlbiBtaWNyb2FycmF5cyBhbmQgc2VxdWVuY2luZyB0ZWNobm9sb2dpZXMvYW5hbHlzaXMgaXMgb3V0bGluZWQgaW4gW3RoZSBvbmxpbmUgbWF0ZXJpYWxzIGZvciBQZW5uIFN0YXRlJ3MgU1RBVDU1NSBjb3Vyc2VdKGh0dHBzOi8vb25saW5lLnN0YXQucHN1LmVkdS9zdGF0NTU1L25vZGUvMzAvKQoKPC9kZXRhaWxzPgo8YnI+CgojIFNvdXJjZXMKClRyYWluaW5nIHJlc291cmNlcyB1c2VkIHRvIGRldmVsb3AgbWF0ZXJpYWxzOgoKKiBIQkMgREdFIHNldHVwOiBodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9ER0Vfd29ya3Nob3AvbGVzc29ucy8wMV9ER0Vfc2V0dXBfYW5kX292ZXJ2aWV3Lmh0bWwKKiBIQkMgQ291bnQgTm9ybWFsaXphdGlvbjogaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvMDJfREdFX2NvdW50X25vcm1hbGl6YXRpb24uaHRtbAoqIERFU2VxMiBzdGFuZGFyZCB2aWduZXR0ZTogaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sCiogREVTZXEyIGJlZ2lubmVycyB2aWduZXR0ZTogaHR0cHM6Ly9iaW9jLmlzbS5hYy5qcC9wYWNrYWdlcy8yLjE0L2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9iZWdpbm5lci5wZGYKKiBCaW9jb25kdWN0b3IgUk5BLXNlcSBXb3JrZmxvd3M6IGh0dHBzOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvaGVscC9jb3Vyc2UtbWF0ZXJpYWxzLzIwMTUvTGVhcm5CaW9jb25kdWN0b3JGZWIyMDE1L0IwMi4xX1JOQVNlcS5odG1sCiogQ0NETCBHYXN0cmljIGNhbmNlciB0cmFpbmluZyBtYXRlcmlhbHM6IGh0dHBzOi8vYWxleHNsZW1vbmFkZS5naXRodWIuaW8vdHJhaW5pbmctbW9kdWxlcy9STkEtc2VxLzAzLWdhc3RyaWNfY2FuY2VyX2V4cGxvcmF0b3J5Lm5iLmh0bWwKKiBDQ0RMIE5ldXJvYmxhc3RvbWEgdHJhaW5pbmcgbWF0ZXJpYWxzOiBodHRwczovL2FsZXhzbGVtb25hZGUuZ2l0aHViLmlvL3RyYWluaW5nLW1vZHVsZXMvUk5BLXNlcS8wNS1uYl9jZWxsX2xpbmVfREVTZXEyLm5iLmh0bWwKCgpgYGB7ciBXcml0ZU91dC5SRGF0YSwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojSGlkZGVuIGNvZGUgYmxvY2sgdG8gd3JpdGUgb3V0IGRhdGEgZm9yIGtuaXR0aW5nCgojIGRpci5jcmVhdGUoInJkYXRhIiwgc2hvd1dhcm5pbmdzPUZBTFNFKQojIHNhdmUuaW1hZ2UoZmlsZSA9ICJyZGF0YS9SdW5uaW5nRGF0YS5SRGF0YSIpCiMgSG93IHRvIGxvYWQgZm9yIG5leHQgc2VnbWVudAojIGxvYWQoInJkYXRhL1J1bm5pbmdEYXRhLlJEYXRhIikKYGBgCgotLS0KClRoZXNlIG1hdGVyaWFscyBoYXZlIGJlZW4gYWRhcHRlZCBhbmQgZXh0ZW5kZWQgZnJvbSBtYXRlcmlhbHMgbGlzdGVkIGFib3ZlLiBUaGVzZSBhcmUgb3BlbiBhY2Nlc3MgbWF0ZXJpYWxzIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgW0NyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24gbGljZW5zZSAoQ0MgQlkgNC4wKV0oaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvNC4wLyksIHdoaWNoIHBlcm1pdHMgdW5yZXN0cmljdGVkIHVzZSwgZGlzdHJpYnV0aW9uLCBhbmQgcmVwcm9kdWN0aW9uIGluIGFueSBtZWRpdW0sIHByb3ZpZGVkIHRoZSBvcmlnaW5hbCBhdXRob3IgYW5kIHNvdXJjZSBhcmUgY3JlZGl0ZWQuCg==