Differential Expression Workflow
So far in the workshop, we’ve taken some steps to answer the
following questions regarding RNA-seq data:
- Are there quality concerns for my sequencing data?
- How can my sequencing data be processed to remove low quality bases
and/or reads?
- How are raw sequencing data transformed into gene-level count
data?
Today we will proceed through key steps in a differential expression
(DE) 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.
At the conclusion of this workshop, we hope that you will be better
positioned to consider these key questions when planning and executing a
DE analysis:
- How do I use R/Rstudio and organize my files?
- What tools should I use to execute my DE analysis?
- How can I control for technical variation in my experimental
design?
- What is the scale of differences expected between my treatment
groups?
- Are there covariates that should be considered?
- What comparisons are relevant to my biological question?
- What outputs should I expect from a DE analysis?
Getting Started in RStudio
In this module, we will learn:
- How to set up a differential expression analysis, using reproducible
research principles
- What is DESeq2 and why it is widely used for differential expression
comparisons
- How to load and review gene count data
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 an email entitled “UM BioinfCore Workshop Login”.
However, if you forget yours, a helper can retrieve it for you if you
ask in Slack. You should now see the RStudio interface:
What are we working towards?
To refresh ourselves on the interface of RStudio, look in the lower
right pane, which lists the files and folders for the selected
directory. Click on the RSD_R
folder and then click on the
WelcomeToRSD.R
script to open it.
There should now be four panes in your RStudio window, with the
WelcomeToRSD
script in the upper left pane, this is the
“Source” or “Scripts” pane. It displays the code that we will write to
perform our analysis.
In the Scripts pane, there is a line of icons, towards the right side
of the pane there is a Run button. First highlight all the
code in the Source pane, and then click Run.
Checkpoint
What just happened?
We see in that our code has been run in the Console
panel, including some warning messages.
We see that a number of objects were created in our Environment
panel including an object named DE_results
.
We see that the Plot tab now has a volcano plot displayed - Note:
if the plot is not displayed, you may need to expand the right panel
boundary before re-running the last line of the script
What does this volcano plot show?
Now that we have seen the end results of a DE analysis, any
questions?
Create an RStudio Project
Just like in Computational Foundation workshop, let’s start our
analysis by creating a project where we can store our data, scripts and
outputs.
- 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:
- 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.
File organization for reproducible research
Part of any successful bioinformatics analysis is documentation and
file organization, which are important aspects of “reproducible
research” (see: Nobel,
2009).
To follow best practices for file organization for reproducible
research, we should make distinct locations for:
We can already see that there a data/
folder and we just
created the scripts/
folder with the RStudio GUI. To
organize our files , we’ll create some more directories in the
RSD_R
folder which is now our project root and current
working directory after creating the RStudio Project in that location,
using the dir.create()
function in R.
# create output directory and subdirectories to use for our analysis
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
Starting our analysis
Load Packages
Several packages have already been installed on the server, so we
need to load them into our R session to access the functions in those
packages. To do that we’ll use the library
function:
# load packages that we need to do our analysis into our session
library(DESeq2)
library(tidyverse)
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.
# look up the help information for one of the packages we will use
?`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 we’ll read in a 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 understanding
broad patterns in our expression data, but raw data should
always be used as an input for DESeq2.
# read in provided count data from the `data` directory
count_table = read.table("data/gene_expected_count.txt", header = TRUE, row.names = 1)
# look at the top of the table
head(count_table, n=2)
By loading in the count data, we’ve created a data frame with ‘gene
ids’ in ENSEMBL format as rownames and six columns of count data, one
for each samples.
How many genes were included in our reference? We can get a sense of
the size of our data with the str
function.
str(count_table) # look at the size & structure of the table
'data.frame': 55492 obs. of 6 variables:
$ sample_A: int 1041 0 1043 1819 19 18 14972 1 888 402 ...
$ sample_B: int 905 0 1232 914 11 1 14768 0 607 483 ...
$ sample_C: int 1296 0 1664 1618 18 4 21026 0 911 744 ...
$ sample_D: int 3481 0 2690 8618 48 24 22962 0 1689 898 ...
$ sample_E: int 1283 0 1825 1350 37 1 22263 0 738 811 ...
$ sample_F: int 1921 0 2720 1222 29 1 23622 0 1180 1261 ...
One important consideration for RNA-seq data is how many genes we
measured in our data and how well we measured them. Both are impacted by
sequencing depth or the number of reads generated per sample.
For human and mouse experiments, the recommendation is sequencing
30-40 million reads per sample to capture both highly expressed
(abundant) and lowly expressed (rarer) transcripts in the sample, as
~25,000 protein-coding genes are expected with a polyA (mRNA targeting)
library prep for these species. However, as the image below from
Illumina shows, sequencing depth has less of an impact than number of
replicates in detecting differentially expressed genes (DEGs).
Illumina’s differential expression recovery
across replicate number and sequencing depth
How many reads were generated per sample in our data? We can use the
summarize_all
function from the dplyr
package
to determine the total number of reads or “depth” per sample.
# use tidyverse functions to summarize the total counts per sample
count_table %>% summarize_all(sum)
sample_A sample_B sample_C sample_D sample_E sample_F
1 30149372 27849704 39153147 49383769 33895207 39253330
Getting help
How did we know to use the summarize_all
function to sum
each column?
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.
Search engines are your friend, which is exactly how we found that
approach
It can sometimes be a challenge to figure out what to search
for, so some key parts of a successful search are:
- 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()
We 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 also 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.
LS0tCnRpdGxlOiAiRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gKERFKSBJbnRyb2R1Y3Rpb24iCmF1dGhvcjogIlVNIEJpb2luZm9ybWF0aWNzIENvcmUiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogICAgICAgIGh0bWxfZG9jdW1lbnQ6CiAgICAgICAgICAgIGluY2x1ZGVzOgogICAgICAgICAgICAgICAgaW5faGVhZGVyOiBoZWFkZXIuaHRtbAogICAgICAgICAgICB0aGVtZTogcGFwZXIKICAgICAgICAgICAgdG9jOiB0cnVlCiAgICAgICAgICAgIHRvY19kZXB0aDogNAogICAgICAgICAgICB0b2NfZmxvYXQ6IHRydWUKICAgICAgICAgICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgICAgICAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgICAgICAgICBtYXJrZG93bjogR0ZNCiAgICAgICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpib2R5LCB0ZCB7CiAgIGZvbnQtc2l6ZTogMThweDsKfQpjb2RlLnJ7CiAgZm9udC1zaXplOiAxMnB4Owp9CnByZSB7CiAgZm9udC1zaXplOiAxMnB4Cn0KPC9zdHlsZT4KCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnNvdXJjZSgiLi4vYmluL2NodW5rLW9wdGlvbnMuUiIpCmtuaXRyX2ZpZ19wYXRoKCIwNi0iKQpgYGAKCgo8YnI+CgojIERpZmZlcmVudGlhbCBFeHByZXNzaW9uIFdvcmtmbG93IHsudW5saXN0ZWQgLnVubnVtYmVyZWR9CgpTbyBmYXIgaW4gdGhlIHdvcmtzaG9wLCB3ZSd2ZSB0YWtlbiBzb21lIHN0ZXBzIHRvIGFuc3dlciB0aGUgZm9sbG93aW5nIHF1ZXN0aW9ucyByZWdhcmRpbmcgUk5BLXNlcSBkYXRhOgoKKiBBcmUgdGhlcmUgcXVhbGl0eSBjb25jZXJucyBmb3IgbXkgc2VxdWVuY2luZyBkYXRhPwoqIEhvdyBjYW4gbXkgc2VxdWVuY2luZyBkYXRhIGJlIHByb2Nlc3NlZCB0byByZW1vdmUgbG93IHF1YWxpdHkgYmFzZXMgYW5kL29yIHJlYWRzPwoqIEhvdyBhcmUgcmF3IHNlcXVlbmNpbmcgZGF0YSB0cmFuc2Zvcm1lZCBpbnRvIGdlbmUtbGV2ZWwgY291bnQgZGF0YT8KClRvZGF5IHdlIHdpbGwgcHJvY2VlZCB0aHJvdWdoIGtleSBzdGVwcyBpbiBhIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIChERSkgYW5hbHlzaXMsIHN0YXJ0aW5nIGZyb20gYSBjb3VudCB0YWJsZSB0aGF0J3Mgc2ltaWxhciB0byB3aGF0IHlvdSBnZW5lcmF0ZWQgaW4gdGhlIGZpcnN0IGhhbGYgb2YgdGhlIHdvcmtzaG9wIGFuZCBvbmUgb2YgdGhlIG91dHB1dHMgaW5jbHVkZWQgaW4gdGhlIFtkYXRhIHRoYXQgdGhlIEFkdmFuY2VkIEdlbm9taWNzIENvcmUgZGVsaXZlcnNdKGh0dHBzOi8vYnJjZi5tZWRpY2luZS51bWljaC5lZHUvY29yZXMvYWR2YW5jZWQtZ2Vub21pY3MvZGF0YS1kZWxpdmVyeS8pIGZvciBSTkEtc2VxIGxpYnJhcmllcy4KCjxpbWcgc3JjPSJpbWFnZXMvd2F5ZmluZGVyL3dheWZpbmRlci1ERUFuYWx5c2lzU2V0dXAucG5nIiBhbHQ9IndheWZpbmRlciIgc3R5bGU9IndpZHRoOiAxMDAwcHg7Ii8+CgpBdCB0aGUgY29uY2x1c2lvbiBvZiB0aGlzIHdvcmtzaG9wLCB3ZSBob3BlIHRoYXQgeW91IHdpbGwgYmUgYmV0dGVyIHBvc2l0aW9uZWQgdG8gY29uc2lkZXIgdGhlc2Uga2V5IHF1ZXN0aW9ucyB3aGVuIHBsYW5uaW5nIGFuZCBleGVjdXRpbmcgYSBERSBhbmFseXNpczoKCiogSG93IGRvIEkgdXNlIFIvUnN0dWRpbyBhbmQgb3JnYW5pemUgbXkgZmlsZXM/CiogV2hhdCB0b29scyBzaG91bGQgSSB1c2UgdG8gZXhlY3V0ZSBteSBERSBhbmFseXNpcz8KKiBIb3cgY2FuIEkgY29udHJvbCBmb3IgdGVjaG5pY2FsIHZhcmlhdGlvbiBpbiBteSBleHBlcmltZW50YWwgZGVzaWduPwoqIFdoYXQgaXMgdGhlIHNjYWxlIG9mIGRpZmZlcmVuY2VzIGV4cGVjdGVkIGJldHdlZW4gbXkgdHJlYXRtZW50IGdyb3Vwcz8KKiBBcmUgdGhlcmUgY292YXJpYXRlcyB0aGF0IHNob3VsZCBiZSBjb25zaWRlcmVkPwoqIFdoYXQgY29tcGFyaXNvbnMgYXJlIHJlbGV2YW50IHRvIG15IGJpb2xvZ2ljYWwgcXVlc3Rpb24/CiogV2hhdCBvdXRwdXRzIHNob3VsZCBJIGV4cGVjdCBmcm9tIGEgREUgYW5hbHlzaXM/CgojIEdldHRpbmcgU3RhcnRlZCBpbiBSU3R1ZGlvCgpJbiB0aGlzIG1vZHVsZSwgd2Ugd2lsbCBsZWFybjoKCiogSG93IHRvIHNldCB1cCBhIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLCB1c2luZyByZXByb2R1Y2libGUgcmVzZWFyY2ggcHJpbmNpcGxlcyAgCiogV2hhdCBpcyBERVNlcTIgYW5kIHdoeSBpdCBpcyB3aWRlbHkgdXNlZCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gY29tcGFyaXNvbnMKKiBIb3cgdG8gbG9hZCBhbmQgcmV2aWV3IGdlbmUgY291bnQgZGF0YQoKIyMgTG9nIG9uIHRvIFJTdHVkaW8gU2VydmVyCgpPcGVuIGEgd2ViIGJyb3dzZXIgdG8gdGhlIGZvbGxvd2luZyBVUkw6IFtodHRwOi8vYmZ4LXdvcmtzaG9wMDEubWVkLnVtaWNoLmVkdV0oaHR0cDovL2JmeC13b3Jrc2hvcDAxLm1lZC51bWljaC5lZHUpCgpZb3Ugc2hvdWxkIG5vdyBiZSBsb29raW5nIGF0IGEgcGFnZSB0aGF0IHdpbGwgYWxsb3cgeW91IHRvIGxvZ2luIHRvIHRoZSBSU3R1ZGlvIHNlcnZlcjoKCjxpbWcgc3JjPSJpbWFnZXMvcnN0dWRpb19sb2dpbl9zY3JlZW4ucG5nIiBhbHQ9InJzdHVkaW8gZGVmYXVsdCBzZXNzaW9uIiBzdHlsZT0id2lkdGg6IDEwMDBweDsiLz4KCkVudGVyIHlvdXIgdXNlciBjcmVkZW50aWFscyBhbmQgY2xpY2sgPGtiZD5TaWduIEluPC9rYmQ+LiBUaGUgY3JlZGVudGlhbHMgYXJlIHRoZSBzYW1lIHVzZWQgZWFybGllciBpbiB0aGUgd29ya3Nob3AgYW5kIHdlcmUgcHJvdmlkZWQgdmlhIGFuIGVtYWlsIGVudGl0bGVkICJVTSBCaW9pbmZDb3JlIFdvcmtzaG9wIExvZ2luIi4KCkhvd2V2ZXIsIGlmIHlvdSBmb3JnZXQgeW91cnMsIGEgaGVscGVyIGNhbiByZXRyaWV2ZSBpdCBmb3IgeW91IGlmIHlvdSBhc2sgaW4gU2xhY2suIFlvdSBzaG91bGQgbm93IHNlZSB0aGUgUlN0dWRpbyBpbnRlcmZhY2U6Cgo8aW1nIHNyYz0iaW1hZ2VzL3JzdHVkaW9fc2Vzc2lvbl9kZWZhdWx0LnBuZyIgYWx0PSJyc3R1ZGlvIGRlZmF1bHQgc2Vzc2lvbiIgc3R5bGU9IndpZHRoOjEwMDBweDsiLz4KCjxicj4KCiMjIFdoYXQgYXJlIHdlIHdvcmtpbmcgdG93YXJkcz8KClRvIHJlZnJlc2ggb3Vyc2VsdmVzIG9uIHRoZSBpbnRlcmZhY2Ugb2YgUlN0dWRpbywgbG9vayBpbiB0aGUgbG93ZXIgcmlnaHQgcGFuZSwgd2hpY2ggbGlzdHMgdGhlIGZpbGVzIGFuZCBmb2xkZXJzIGZvciB0aGUgc2VsZWN0ZWQgZGlyZWN0b3J5LiBDbGljayBvbiB0aGUgYFJTRF9SYCBmb2xkZXIgYW5kIHRoZW4gY2xpY2sgb24gdGhlIGBXZWxjb21lVG9SU0QuUmAgc2NyaXB0IHRvIG9wZW4gaXQuCgpUaGVyZSBzaG91bGQgbm93IGJlIGZvdXIgcGFuZXMgaW4geW91ciBSU3R1ZGlvIHdpbmRvdywgd2l0aCB0aGUgYFdlbGNvbWVUb1JTRGAgc2NyaXB0IGluIHRoZSB1cHBlciBsZWZ0IHBhbmUsIHRoaXMgaXMgdGhlICJTb3VyY2UiIG9yICJTY3JpcHRzIiBwYW5lLiBJdCBkaXNwbGF5cyB0aGUgY29kZSB0aGF0IHdlIHdpbGwgd3JpdGUgdG8gcGVyZm9ybSBvdXIgYW5hbHlzaXMuCgo8aW1nIHNyYz0iaW1hZ2VzL1dlbGNvbWVTY3JpcHRPcGVuZWQucG5nIiBhbHQ9InJzdHVkaW8gZGVmYXVsdCBzZXNzaW9uIiBzdHlsZT0id2lkdGg6MTAwMHB4OyIvPgoKSW4gdGhlIFNjcmlwdHMgcGFuZSwgdGhlcmUgaXMgYSBsaW5lIG9mIGljb25zLCB0b3dhcmRzIHRoZSByaWdodCBzaWRlIG9mIHRoZSBwYW5lIHRoZXJlIGlzIGEgPGtiZD5SdW48L2tiZD4gYnV0dG9uLiBGaXJzdCBoaWdobGlnaHQgYWxsIHRoZSBjb2RlIGluIHRoZSBTb3VyY2UgcGFuZSwgYW5kIHRoZW4gY2xpY2sgPGtiZD5SdW48L2tiZD4uCgo8YnI+CgoqKkNoZWNrcG9pbnQqKgoKV2hhdCBqdXN0IGhhcHBlbmVkPwoKMS4gV2Ugc2VlIGluIHRoYXQgb3VyIGNvZGUgaGFzIGJlZW4gcnVuIGluIHRoZSBgQ29uc29sZWAgcGFuZWwsIGluY2x1ZGluZyBzb21lIHdhcm5pbmcgbWVzc2FnZXMuCgoxLiBXZSBzZWUgdGhhdCBhIG51bWJlciBvZiBvYmplY3RzIHdlcmUgY3JlYXRlZCBpbiBvdXIgRW52aXJvbm1lbnQgcGFuZWwgaW5jbHVkaW5nIGFuIG9iamVjdCBuYW1lZCBgREVfcmVzdWx0c2AuCgoxLiBXZSBzZWUgdGhhdCB0aGUgUGxvdCB0YWIgbm93IGhhcyBhIHZvbGNhbm8gcGxvdCBkaXNwbGF5ZWQgLSBOb3RlOiBpZiB0aGUgcGxvdCBpcyBub3QgZGlzcGxheWVkLCB5b3UgbWF5IG5lZWQgdG8gZXhwYW5kIHRoZSByaWdodCBwYW5lbCBib3VuZGFyeSBiZWZvcmUgcmUtcnVubmluZyB0aGUgbGFzdCBsaW5lIG9mIHRoZSBzY3JpcHQKCioqV2hhdCBkb2VzIHRoaXMgdm9sY2FubyBwbG90IHNob3c/KioKCipOb3cgdGhhdCB3ZSBoYXZlIHNlZW4gdGhlIGVuZCByZXN1bHRzIG9mIGEgREUgYW5hbHlzaXMsIGFueSBxdWVzdGlvbnM/KgoKPGJyPgo8YnI+CgojIyBDcmVhdGUgYW4gUlN0dWRpbyBQcm9qZWN0CgpKdXN0IGxpa2UgaW4gQ29tcHV0YXRpb25hbCBGb3VuZGF0aW9uIHdvcmtzaG9wLCBsZXQncyBzdGFydCBvdXIgYW5hbHlzaXMgYnkgY3JlYXRpbmcgYSBwcm9qZWN0IHdoZXJlIHdlIGNhbiBzdG9yZSBvdXIgZGF0YSwgc2NyaXB0cyBhbmQgb3V0cHV0cy4KCjEuIFRvIGNyZWF0ZSBhIHByb2plY3QsIGdvIHRvIHRoZSA8a2JkPkZpbGU8L2tiZD4gbWVudSwgYW5kIGNsaWNrIDxrYmQ+TmV3IFByb2plY3QuLi48L2tiZD4uIFRoZSBmb2xsb3dpbmcgd2luZG93IHdpbGwgYXBwZWFyOgoKPGltZyBzcmM9ImltYWdlcy9uZXdfcHJvamVjdF93aW5kb3cucG5nIiBhbHQ9Im5ldyBwcm9qZWN0IHdpbmRvdyIgc3R5bGU9IndpZHRoOiA2MDBweDsiLz4KCjIuIEluIHRoaXMgd2luZG93LCBzZWxlY3QgPGtiZD5FeGlzdGluZyBEaXJlY3Rvcnk8L2tiZD4uIEZvciAiUHJvamVjdCB3b3JraW5nIGRpcmVjdG9yeSIsIGNsaWNrIDxrYmQ+QnJvd3NlLi4uPC9rYmQ+LCBzZWxlY3QgdGhlICJSU0RfUiIgZm9sZGVyLCBhbmQgY2xpY2sgPGtiZD5DaG9vc2U8L2tiZD4uIFRoaXMgd2lsbCB1c2UgdGhlIGAvaG9tZS93b3Jrc2hvcC91c2VyL1JTRF9SYCBmb2xkZXIgYXMgdGhlIHByb2plY3QgZGlyZWN0b3J5LgoKMy4gRmluYWxseSBjbGljayA8a2JkPkNyZWF0ZSBQcm9qZWN0PC9rYmQ+LiBJbiB0aGUgIkZpbGVzIiB0YWIgb2YgeW91ciBvdXRwdXQgcGFuZSAobW9yZSBhYm91dCB0aGUgUlN0dWRpbyBsYXlvdXQgaW4gYSBtb21lbnQpLCB5b3Ugc2hvdWxkIHNlZSBhbiBSU3R1ZGlvIHByb2plY3QgZmlsZSwgKipSU0RfUi5ScHJvaioqLiBBbGwgUlN0dWRpbyBwcm9qZWN0cyBlbmQgd2l0aCB0aGUgIioqLlJwcm9qKioiIGZpbGUgZXh0ZW5zaW9uLgoKTm90ZSB0aGF0IHRoZXJlIGlzIGFscmVhZHkgYSBgZGF0YS9gIGZvbGRlciB3aGljaCBjb250YWlucyB0aGUgZGF0YSB3ZSB3aWxsIHVzZSBmb3IgdGhlc2UgbGVzc29ucy4KCiMjIENyZWF0aW5nIGFuIFIgc2NyaXB0CgpMZXQncyBjcmVhdGUgYW4gUiBzY3JpcHQgZmlsZToKCi0gQ2xpY2sgdGhlIDxrYmQ+RmlsZTwva2JkPiBtZW51IGFuZCBzZWxlY3QgPGtiZD5OZXcgRmlsZTwva2JkPiBhbmQgdGhlbiA8a2JkPlIgU2NyaXB0PC9rYmQ+LgotIEJlZm9yZSB3ZSBnbyBhbnkgZnVydGhlciwgc2F2ZSB5b3VyIHNjcmlwdCBieSBjbGlja2luZyB0aGUgc2F2ZS9kaXNrIGljb24gdGhhdCBpcyBpbiB0aGUgYmFyIGFib3ZlIHRoZSBmaXJzdCBsaW5lIGluIHRoZSBzY3JpcHQgZWRpdG9yLCBvciBjbGljayB0aGUgPGtiZD5GaWxlPC9rYmQ+IG1lbnUgYW5kIHNlbGVjdCA8a2JkPlNhdmU8L2tiZD4uCi0gSW4gdGhlICJTYXZlIEZpbGUiIHdpbmRvdyB0aGF0IG9wZW5zLCBzZWxlY3QgPGtiZD5OZXcgRm9sZGVyPC9rYmQ+LiBOYW1lIGl0ICoqInNjcmlwdHMiKiouCi0gRmluYWxseSwgbmFtZSB5b3VyIGZpbGUgKioiZGlmZmV4IioqIGluIHRoZSAiRmlsZSBuYW1lIiBmaWVsZC4KClRoZSBuZXcgc2NyaXB0ICoqZGlmZmV4LlIqKiBpcyBub3cgaW4gdGhlIGBzY3JpcHRzYCBmb2xkZXIuIFlvdSBjYW4gc2VlIHRoYXQgYnkgY2xpY2tpbmcgdGhlIGBzY3JpcHRzYCBmb2xkZXIgaW4gdGhlICJGaWxlcyIgcGFuZS4gQW5kIHlvdSBjYW4gZ28gYmFjayB1cCB0byB0aGUgbWFpbiBwcm9qZWN0IGZvbGRlciBieSBjbGlja2luZyB0aGUgYC4uYCB0byB0aGUgcmlnaHQgb2YgdGhlIHVwIGFycm93IGluIHRoZSAiRmlsZXMiIHBhbmUuIEJ5IGNvbnZlbnRpb24sIFIgc2NyaXB0cyBlbmQgd2l0aCB0aGUgZmlsZSBleHRlbnNpb24gKiouUioqLgoKCiMjIEZpbGUgb3JnYW5pemF0aW9uIGZvciByZXByb2R1Y2libGUgcmVzZWFyY2gKClBhcnQgb2YgYW55IHN1Y2Nlc3NmdWwgYmlvaW5mb3JtYXRpY3MgYW5hbHlzaXMgaXMgZG9jdW1lbnRhdGlvbiBhbmQgZmlsZSBvcmdhbml6YXRpb24sIHdoaWNoIGFyZSBpbXBvcnRhbnQgYXNwZWN0cyBvZiAicmVwcm9kdWNpYmxlIHJlc2VhcmNoIiAoc2VlOiBbTm9iZWwsIDIwMDldKGh0dHBzOi8vam91cm5hbHMucGxvcy5vcmcvcGxvc2NvbXBiaW9sL2FydGljbGU/aWQ9MTAuMTM3MS9qb3VybmFsLnBjYmkuMTAwMDQyNCkpLgoKVG8gZm9sbG93IGJlc3QgcHJhY3RpY2VzIGZvciBmaWxlIG9yZ2FuaXphdGlvbiBmb3IgcmVwcm9kdWNpYmxlIHJlc2VhcmNoLCB3ZSBzaG91bGQgbWFrZSBkaXN0aW5jdCBsb2NhdGlvbnMgZm9yOgoKKiBSYXcgZGF0YQoqIENvZGUKKiBPdXRwdXQKCgpXZSBjYW4gYWxyZWFkeSBzZWUgdGhhdCB0aGVyZSBhIGBkYXRhL2AgZm9sZGVyIGFuZCB3ZSBqdXN0IGNyZWF0ZWQgdGhlIGBzY3JpcHRzL2AgZm9sZGVyIHdpdGggdGhlIFJTdHVkaW8gR1VJLiBUbyBvcmdhbml6ZSBvdXIgZmlsZXMgLCB3ZSdsbCBjcmVhdGUgc29tZSBtb3JlIGRpcmVjdG9yaWVzIGluIHRoZSBgUlNEX1JgIGZvbGRlciB3aGljaCBpcyBub3cgb3VyIHByb2plY3Qgcm9vdCBhbmQgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeSBhZnRlciBjcmVhdGluZyB0aGUgUlN0dWRpbyBQcm9qZWN0IGluIHRoYXQgbG9jYXRpb24sIHVzaW5nIHRoZSBgZGlyLmNyZWF0ZSgpYCBmdW5jdGlvbiBpbiBSLgoKYGBge3IgY3JlYXRlX2RpcnMsIGV2YWwgPSBGQUxTRX0KIyBjcmVhdGUgb3V0cHV0IGRpcmVjdG9yeSBhbmQgc3ViZGlyZWN0b3JpZXMgdG8gdXNlIGZvciBvdXIgYW5hbHlzaXMKZGlyLmNyZWF0ZSgnb3V0cHV0cycpCmRpci5jcmVhdGUoJ291dHB1dHMvZmlndXJlcycpCmRpci5jcmVhdGUoJ291dHB1dHMvdGFibGVzJykKYGBgCgoqKkNoZWNrcG9pbnQqKjogKlBsZWFzZSB1c2UgdGhlIGdyZWVuICdjaGVjaycgaWYgeW91IGhhdmUgc2F2ZWQgeW91ciBjb2RlIGZpbGUgYW5kIHNlZSB0aGUgYGRhdGFgLCBgc2NyaXB0c2AsIGFuZCBgb3V0cHV0c2AgZGlyZWN0b3JpZXMgaW4gdGhlIGBSU0RfUmAgZm9sZGVyIGFuZCB0aGUgcmVkICd4JyBpZiB5b3UgZG8gbm90LioKCj4gIyMgUmVtaW5kZXI6IFJTdHVkaW8gY29kZSBleGVjdXRpb24gey51bmxpc3RlZCAudW5udW1iZXJlZH0KPgo+IDxrYmQ+Q3RybDwva2JkPis8a2JkPkVudGVyPC9rYmQ+IGlzIGEgc3RhbmRhcmQgc2hvcnRjdXQgaW4gUlN0dWRpbyB0byBzZW5kIHRoZSBjdXJyZW50IGxpbmUgKG9yIHNlbGVjdGVkIGxpbmVzKSB0byB0aGUgY29uc29sZS4gSWYgeW91IHNlZSBgPmAgaW4gdGhlIENvbnNvbGUsIHRoZW4gUiBoYXMgZXhlY3V0ZWQgdGhlIGNvbW1hbmQuIElmIHlvdSBzZWUgYSBgK2AsIHRoaXMgbWVhbnMgdGhhdCB0aGUgY29tbWFuZCBpcyBub3QgY29tcGxldGUsIFIgdGhpbmtzIHRoZXJlIGlzIG1vcmUgdG8geW91ciBjb21tYW5kLiBZb3UgY2FuIHVzZSB0aGUgPGttZD5lc2M8L2ttZD4gdG8gZ2V0IG91dCBvZiB0aGlzIHN0YXRlLgoKPiAjIyBSZW1pbmRlcjogT2JqZWN0IG5hbWluZyBjb252ZW50aW9ucyB7LnVubGlzdGVkIC51bm51bWJlcmVkfQo+ICogQ2Fubm90IHN0YXJ0IHdpdGggbnVtYmVycwo+ICogQ2Fubm90IGluY2x1ZGUgZGFzaGVzCj4gKiBDYW5ub3QgaGF2ZSBzcGFjZXMKPiAqIFNob3VsZCBub3QgYmUgaWRlbnRpY2FsIHRvIGEgbmFtZWQgZnVuY3Rpb24KPiAqIERvdHMgYW5kIHVuZGVyc2NvcmVzIGNhbiBzZXBhcmF0ZSBwYXJ0cyBvZiBuYW1lcywgYWx0ZXJuYXRpdmVseSBDYW1lbENhc2UgYWNjb21wbGlzaGVzIHRoaXMKCi0tLS0KCiMgVG9vbHMgZm9yIERpZmZlcmVudGlhbCBHZW5lIEV4cHJlc3Npb24gYW5hbHlzaXMKCldoaWxlIHRoZXJlIGFyZSBzZXZlcmFsIHRvb2xzIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBjb21wYXJpc29ucyB0aGF0IHVzZSBzdGF0aXN0aWNhbCBhcHByb2FjaGVzIGFwcHJvcHJpYXRlIGZvciBiaW9sb2dpY2FsIGRhdGEsIHdlIHdpbGwgdXNlIFtERVNlcTJdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9ERVNlcTIuaHRtbCkgaW4gb3VyIGFuYWx5c2lzIHRvZGF5LgoKREVTZXEyIGlzIG9uZSBvZiB0d28gdG9vbHMsIGFsb25nIHdpdGggW2VkZ2VSXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvZWRnZVIuaHRtbCksIHRoYXQgYXJlIGNvbnNpZGVyZWQgWydiZXN0IHByYWN0aWNlJ10oaHR0cHM6Ly9ibWNiaW9pbmZvcm1hdGljcy5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2LzE0NzEtMjEwNS0xNC05MSkgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGNvbXBhcmlzb25zLiBCb3RoIHRvb2xzIGFwcGx5IHNpbWlsYXIgbWV0aG9kcyB0aGF0IGFjY291bnQgZm9yIHRoZSAic2hhcGUiIG9mIGRhdGEgZXhwZWN0ZWQgZm9yIFJOQS1zZXEgYW5kIGFyZSBmYWlybHkgc3RyaW5nZW50IGluIGNhbGxpbmcgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLCBsb3dlcmluZyB0aGUgcmlzayBvZiBpbnZlc3RpZ2F0aW5nICJmYWxzZSBwb3NpdGl2ZSIgZ2VuZXMgKGUuZy4gZ2VuZXMgdGhhdCBkb24ndCBhY3R1YWxseSBoYXZlIGRpZmZlcmVudCBleHByZXNzaW9uIGJldHdlZW4gdHJlYXRtZW50IGdyb3VwcyBhbmQgdGhlcmVmb3JlIGFyZSBub3QgcmVsZXZhbnQgdG8gdGhlIGJpb2xvZ2ljYWwgcHJvY2VzcykuCgpBZGRpdGlvbmFsbHksIERFU2VxMiBhbHNvIGhhcyBhbiBbZXhjZWxsZW50IHZpZ25ldHRlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwpCmZyb20gTG92ZSwgQW5kZXJzLCBhbmQgSHViZXIsIGZyb20gd2hpY2ggb3VyIHdvcmtmbG93IGlzIHBhcnRpYWxseSBhZGFwdGVkLCBhbmQgaXMgYSBnb29kIHJlc291cmNlIHdoZW4gYW5hbHl6aW5nIHlvdXIgb3duIGRhdGEKKHNlZSBhbHNvOiBbTG92ZSwgQW5kZXJzLCBhbmQgSHViZXIuIF9HZW5vbWUgQmlvbG9neV8uIDIwMTQuXShodHRwczovL2RvaS5vcmcvMTAuMTE4Ni9zMTMwNTktMDE0LTA1NTAtOCkpLgoKCiMjIERFU2VxMiBhc3N1bXB0aW9ucyBhbmQgcmVxdWlyZW1lbnRzCgpBIGtleSBhc3N1bXB0aW9uIG9mIERFU2VxMiBpcyB0aGF0ICpiaW9sb2dpY2FsIHZhcmlhbmNlIGlzIG11Y2ggZ3JlYXRlciB0aGFuIHRlY2huaWNhbCB2YXJpYW5jZSogKHdoaWNoIHNob3VsZCBiZSB0cnVlIGlmIFtiZXN0IHByYWN0aWNlc10oaHR0cHM6Ly93d3cudHhnZW4udGFtdS5lZHUvZmFxL3JuYS1pc29sYXRpb24tYmVzdC1wcmFjdGljZXMvKSBmb3IgW3F1YWxpdHkgUk5BIGlzb2xhdGlvbl0oaHR0cHM6Ly93d3cuYmlvY29tcGFyZS5jb20vQmVuY2gtVGlwcy8xMjg3OTAtRm91ci1UaXBzLWZvci1QZXJmZWN0aW5nLVJOQS1Jc29sYXRpb24vKSBhcmUgZm9sbG93ZWQsIGluY2x1ZGluZyBETmFzZSB0cmVhdG1lbnQhKS4KClNpbmNlIGNhbGN1bGF0aW5nIHZhcmlhbmNlIGlzIGtleSB0byB0aGUgc3RhdGlzdGljYWwgYXBwcm9hY2ggdXNlZCBmb3IgREVTZXEyLCBpZiB3ZSB0cmllZCB0byBjb21wYXJlIHRyZWF0bWVudCBncm91cHMgd2l0aCBvbmx5IG9uZSBzYW1wbGUgZWFjaCwgd2Ugd291bGQgZ2V0IGFuIGVycm9yIChhcyBzaG93biBpbiBbdGhpcyBibG9nIHBvc3RdKGh0dHBzOi8vc3VwcG9ydC5iaW9jb25kdWN0b3Iub3JnL3AvODk3NDYvKSkuIFdpdGhvdXQgcmVwbGljYXRlcywgdGhlcmUgY2FuJ3QgYmUgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIChlLmcuIHAtdmFsdWVzKSwgYnV0ICpxdWFsaXRhdGl2ZSogYXBwcm9hY2hlcyBhcmUgYW4gb3B0aW9uLCBsaWtlIGxvb2tpbmcgYXQgdGhlIHRvcCBleHByZXNzZWQgZ2VuZXMgYWZ0ZXIgbm9ybWFsaXphdGlvbi4KCjxkZXRhaWxzPgogICAgPHN1bW1hcnk+KkNsaWNrIGZvciBhZGRpdGlvbmFsIHJlc291cmNlcyByZWdhcmRpbmcgc3RhdGlzdGljYWwgdGVzdGluZyBhbmQgdG9vbCBjb21wYXJpc29uIGZvciBSTkEtc2VxIGRhdGEqPC9zdW1tYXJ5PgogICAgVG8gbGVhcm4gbW9yZSBhYm91dCBzdGF0aXN0aWNhbCB0ZXN0aW5nIGFuZCB3aGF0IGRpc3RyaWJ1dGlvbnMgYmVzdCBtb2RlbCB0aGUgYmVoYXZpb3Igb2YgUk5BLXNlcSBkYXRhLCBhIGdvb2QgcmVzb3VyY2UgaXMgdGhpcyBbRWRYIGxlY3R1cmUgYnkgUmFmYWVsIElyaXphcnJ5XShodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PUhLN1dLc0wzYzJ3JmZlYXR1cmU9eW91dHUuYmUpIG9yIHRoaXMgW2xlY3R1cmUgYnkgS2FzcGVyIEhhbnNlbl0oaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1DOFJOdld1N3BBdykuIEFub3RoZXIgaGVscGZ1bCBndWlkZSBpcyB0aGlzIFtDb21wYXJhdGl2ZSBTdHVkeSBmb3IgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gQW5hbHlzaXMgYnkgWmhhbmcgZXQgYWwuXShodHRwczovL2pvdXJuYWxzLnBsb3Mub3JnL3Bsb3NvbmUvYXJ0aWNsZT9pZD0xMC4xMzcxL2pvdXJuYWwucG9uZS4wMTAzMjA3KSBmcm9tIDIwMTQuCjwvZGV0YWlscz4KPGJyPgoKPiAjIyBDaGVjay1pbiBhbmQgbG9va2luZyBmb3J3YXJkCj4KPiAtIFBvc3QgYSBxdWVzdGlvbiB5b3UgaG9wZSB3aWxsIGJlIGFkZHJlc3NlZCBpbiB0aGUgbGF0ZXIgbW9kdWxlcyAqKk9SKioKPiAtIEFkZCBhIHRodW1icyB1cCB0byB5b3VyIGZhdm9yaXRlIGNvbW1lbnQocykgdG8gdXB2b3RlIGl0CgotLS0tCgojIFN0YXJ0aW5nIG91ciBhbmFseXNpcwoKIyMgTG9hZCBQYWNrYWdlcwoKU2V2ZXJhbCBwYWNrYWdlcyBoYXZlIGFscmVhZHkgYmVlbiBpbnN0YWxsZWQgb24gdGhlIHNlcnZlciwgc28gd2UgbmVlZCB0byBsb2FkIHRoZW0gaW50byBvdXIgUiBzZXNzaW9uIHRvIGFjY2VzcyB0aGUgZnVuY3Rpb25zIGluIHRob3NlIHBhY2thZ2VzLiBUbyBkbyB0aGF0IHdlJ2xsIHVzZSB0aGUgYGxpYnJhcnlgIGZ1bmN0aW9uOgoKYGBge3IgTW9kdWxlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz0gRkFMU0UsIGV2YWw9VFJVRX0KIyBsb2FkIHBhY2thZ2VzIHRoYXQgd2UgbmVlZCB0byBkbyBvdXIgYW5hbHlzaXMgaW50byBvdXIgc2Vzc2lvbgpsaWJyYXJ5KERFU2VxMikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobWF0cml4U3RhdHMpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoZGF0YS50YWJsZSkKYGBgCgoqTm90ZTogV2UgZXhwZWN0IHRvIHNlZSBzb21lIHJlZCBtZXNzYWdlcyBpbiB5b3VyIGNvbnNvbGUgd2hpbGUgdGhlc2UgcGFja2FnZXMgYXJlIGxvYWRpbmcqCgpSL1JTdHVkaW8gW0FLQSBQb3NpdF0gaGFzIGdyZWF0IHJlc291cmNlcyBmb3IgZ2V0dGluZyBoZWxwLCBpbmNsdWRpbmcgW2NvZGUgJ2NoZWF0c2hlZXRzJ10oaHR0cHM6Ly9wb3NpdC5jby9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKSBhbmQgcGFja2FnZSB2aWduZXR0ZXMsIGxpa2UgZm9yIFt0aWR5cl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3RpZHlyL3ZpZ25ldHRlcy90aWR5LWRhdGEuaHRtbCkuCgpTaW5jZSB3ZSBsb2FkZWQgdGhlIGxpYnJhcmllcyBpbnRvIG91ciBSIHNlc3Npb24sIHdlIGNhbiBzZWUgZG9jdW1lbnRhdGlvbiBvdXQgdXNpbmcgdGhlIGA/YCBvcGVyYXRvci4KYGBge3IgQ2hlY2tEb2N1bWVudGFpb259CiMgbG9vayB1cCB0aGUgaGVscCBpbmZvcm1hdGlvbiBmb3Igb25lIG9mIHRoZSBwYWNrYWdlcyB3ZSB3aWxsIHVzZQo/YERFU2VxMi1wYWNrYWdlYApgYGAKCioqQ2hlY2twb2ludCoqOiAqSWYgeW91IHNlZSB0aGUgUiBkb2N1bWVudGF0aW9uIGZvciBERVNlcTIgcG9wIHVwIGluIHlvdXIgJ2hlbHAnIHBhbmVsIG9uIHRoZSByaWdodCwgcGxlYXNlIGluZGljYXRlIHdpdGggdGhlIGdyZWVuICdjaGVjaycgYnV0dG9uLiBJZiBub3QgcGxlYXNlIHVzZSB0aGUgcmVkICd4JyBidXR0b24uKgoKIyMgUmVhZCBDb3VudHMKCkFub3RoZXIga2V5IGFzc3VtcHRpb24gZm9yIERFU2VxMiBpcyB0aGF0IHRoZSBhbmFseXNpcyB3aWxsIHN0YXJ0IHdpdGggW3VuLW5vcm1hbGl6ZWQgY291bnRzXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwjd2h5LXVuLW5vcm1hbGl6ZWQtY291bnRzKS4KClRvIGJlZ2luIHdlJ2xsIHJlYWQgaW4gYSAqKnJhdyoqIGNvdW50IGRhdGEgZmlsZSwgYGdlbmVfZXhwZWN0ZWRfY291bnQudHh0YCB3aGljaCBpcyBzaW1pbGFyIHRvIHdoYXQgd291bGQgYmUgZ2VuZXJhdGVkIGluIHRoZSBhbGlnbm1lbnQgc3RlcHMgKGFuZCB3aGF0IHlvdSB3b3VsZCByZWNlaXZlIGZyb20gQUdDKS4gV2UnbGwgZGlzY3VzcyBhIGZldyBub3JtYWxpemF0aW9ucyB0aGF0IGNhbiBiZSBoZWxwZnVsIGZvciB1bmRlcnN0YW5kaW5nIGJyb2FkIHBhdHRlcm5zIGluIG91ciBleHByZXNzaW9uIGRhdGEsIGJ1dCByYXcgZGF0YSAqKnNob3VsZCBhbHdheXMqKiBiZSB1c2VkIGFzIGFuIGlucHV0IGZvciBERVNlcTIuCgpgYGB7ciBEYXRhVGFibGUsIGV2YWw9VFJVRSwgZWNobz1GQUxTRX0KIyMgaW5jbHVkZSBwYXRocyByZWxhdGl2ZSB0byBzaXRlIGJ1aWxkIGxvY2F0aW9ucyBidXQga2VlcCBjb2RlIGJsb2NrIGhpZGRlbgpjb3VudF90YWJsZSA9IHJlYWQudGFibGUoIi4uL2RhdGEvUl9kYXRhL2dlbmVfZXhwZWN0ZWRfY291bnQudHh0IiwgaGVhZGVyID0gVFJVRSwgcm93Lm5hbWVzID0gMSkKYGBgCgpgYGB7ciBEYXRhVGFibGUyLCBldmFsPUZBTFNFfQojIHJlYWQgaW4gcHJvdmlkZWQgY291bnQgZGF0YSBmcm9tIHRoZSBgZGF0YWAgZGlyZWN0b3J5CmNvdW50X3RhYmxlID0gcmVhZC50YWJsZSgiZGF0YS9nZW5lX2V4cGVjdGVkX2NvdW50LnR4dCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpCgojIGxvb2sgYXQgdGhlIHRvcCBvZiB0aGUgdGFibGUKaGVhZChjb3VudF90YWJsZSwgbj0yKSAKYGBgCgpCeSBsb2FkaW5nIGluIHRoZSBjb3VudCBkYXRhLCB3ZSd2ZSBjcmVhdGVkIGEgZGF0YSBmcmFtZSB3aXRoICdnZW5lIGlkcycgaW4gRU5TRU1CTCBmb3JtYXQgYXMgcm93bmFtZXMgYW5kIHNpeCBjb2x1bW5zIG9mIGNvdW50IGRhdGEsIG9uZSBmb3IgZWFjaCBzYW1wbGVzLgoKSG93IG1hbnkgZ2VuZXMgd2VyZSBpbmNsdWRlZCBpbiBvdXIgcmVmZXJlbmNlPyBXZSBjYW4gZ2V0IGEgc2Vuc2Ugb2YgdGhlIHNpemUgb2Ygb3VyIGRhdGEgd2l0aCB0aGUgYHN0cmAgZnVuY3Rpb24uCmBgYHtyIERhdGFUYWJsZUNoZWNrMiwgZXZhbD1UUlVFfQpzdHIoY291bnRfdGFibGUpICMgbG9vayBhdCB0aGUgc2l6ZSAmIHN0cnVjdHVyZSBvZiB0aGUgdGFibGUKYGBgCgoKT25lIGltcG9ydGFudCBjb25zaWRlcmF0aW9uIGZvciBSTkEtc2VxIGRhdGEgaXMgaG93IG1hbnkgZ2VuZXMgd2UgbWVhc3VyZWQgaW4gb3VyIGRhdGEgYW5kIGhvdyB3ZWxsIHdlIG1lYXN1cmVkIHRoZW0uIEJvdGggYXJlIGltcGFjdGVkIGJ5IHNlcXVlbmNpbmcgZGVwdGggb3IgdGhlIG51bWJlciBvZiByZWFkcyBnZW5lcmF0ZWQgcGVyIHNhbXBsZS4KCkZvciBodW1hbiBhbmQgbW91c2UgZXhwZXJpbWVudHMsIHRoZSByZWNvbW1lbmRhdGlvbiBpcyBzZXF1ZW5jaW5nIDMwLTQwIG1pbGxpb24gcmVhZHMgcGVyIHNhbXBsZSB0byBjYXB0dXJlIGJvdGggaGlnaGx5IGV4cHJlc3NlZCAoYWJ1bmRhbnQpIGFuZCBsb3dseSBleHByZXNzZWQgKHJhcmVyKSB0cmFuc2NyaXB0cyBpbiB0aGUgc2FtcGxlLCBhcyB+MjUsMDAwIHByb3RlaW4tY29kaW5nIGdlbmVzIGFyZSBleHBlY3RlZCB3aXRoIGEgcG9seUEgKG1STkEgdGFyZ2V0aW5nKSBsaWJyYXJ5IHByZXAgZm9yIHRoZXNlIHNwZWNpZXMuIEhvd2V2ZXIsIGFzIHRoZSBpbWFnZSBiZWxvdyBmcm9tIElsbHVtaW5hIHNob3dzLCBzZXF1ZW5jaW5nIGRlcHRoIGhhcyBsZXNzIG9mIGFuIGltcGFjdCB0aGFuIG51bWJlciBvZiByZXBsaWNhdGVzIGluIGRldGVjdGluZyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgKERFR3MpLgoKIVtJbGx1bWluYSdzIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHJlY292ZXJ5IGFjcm9zcyByZXBsaWNhdGUgbnVtYmVyIGFuZCBzZXF1ZW5jaW5nIGRlcHRoXShpbWFnZXMvZGVfcmVwbGljYXRlc19pbWcucG5nKXt3aWR0aD01MCV9Cgo8YnI+CgpIb3cgbWFueSByZWFkcyB3ZXJlIGdlbmVyYXRlZCBwZXIgc2FtcGxlIGluIG91ciBkYXRhPyBXZSBjYW4gdXNlIHRoZSBgc3VtbWFyaXplX2FsbGAgZnVuY3Rpb24gZnJvbSB0aGUgYGRwbHlyYCBwYWNrYWdlIHRvIGRldGVybWluZSB0aGUgdG90YWwgbnVtYmVyIG9mIHJlYWRzIG9yICJkZXB0aCIgcGVyIHNhbXBsZS4KCmBgYHtyIFN1bW1hcml6ZVJlYWRzLCBldmFsPVRSVUV9CiMgdXNlIHRpZHl2ZXJzZSBmdW5jdGlvbnMgdG8gc3VtbWFyaXplIHRoZSB0b3RhbCBjb3VudHMgcGVyIHNhbXBsZQpjb3VudF90YWJsZSAlPiUgc3VtbWFyaXplX2FsbChzdW0pCmBgYAoKIyBHZXR0aW5nIGhlbHAKCkhvdyBkaWQgd2Uga25vdyB0byB1c2UgdGhlIGBzdW1tYXJpemVfYWxsYCBmdW5jdGlvbiB0byBzdW0gZWFjaCBjb2x1bW4/CgpSIGFuZCBSU3R1ZGlvIGhhdmUgYSBzdHJvbmcgY29tbXVuaXR5IGNvbXBvbmVudCwgc28gaWYgeW91IGFyZSBnZXR0aW5nIGFuIGVycm9yIG9yIHdvbmRlcmluZyBob3cgdG8gbWFrZSBhIGNvbW1hbmQgd29yayBvciBob3cgdG8gcGVyZm9ybSBhIHNwZWNpZmljIHRhc2ssIHRoZXJlIGlzIGxpa2VseSBhbHJlYWR5IGEgc29sdXRpb24gb3V0IHRoZXJlLiBTZWFyY2ggZW5naW5lcyBhcmUgeW91ciBmcmllbmQsIHdoaWNoIGlzIGV4YWN0bHkgaG93IHdlIGZvdW5kIFt0aGF0IGFwcHJvYWNoXShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8yODg3MzA1Ny9zdW0tYWNyb3NzLW11bHRpcGxlLWNvbHVtbnMtd2l0aC1kcGx5cikKCkl0IGNhbiBzb21ldGltZXMgYmUgYSBjaGFsbGVuZ2UgdG8gZmlndXJlIG91dCAqd2hhdCB0byBzZWFyY2ggZm9yKiwgc28gc29tZSBrZXkgcGFydHMgb2YgYSBzdWNjZXNzZnVsIHNlYXJjaCBhcmU6CgoqIFBhY2thZ2Ugb3IgY29tbWFuZCBydW4KKiBgUmAgb3IgYEJpb2NvbmR1Y3RvcmAKKiBUaGUgZXJyb3IgbWVzc2FnZSBpZiB0aGVyZSBpcyBvbmUKKiBWZXJzaW9uIGluZm9ybWF0aW9uCgpIb3cgdG8gZ2V0IHNlc3Npb24gaW5mb3JtYXRpb24gdG8gYWlkIGluIGEgc2VhcmNoOgpgYGB7ciBTZXNzaW9uIGluZm8sIGV2YWwgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpzZXNzaW9uSW5mbygpCmBgYAoKV2UgaGlnaGx5IHJlY29tbWVuZCB1c2luZyByZXNvdXJjZXMgbGlrZSBbQmlvY29uZHVjdG9yIFN1cHBvcnRdKGh0dHBzOi8vc3VwcG9ydC5iaW9jb25kdWN0b3Iub3JnLyksIFtCaW9zdGFyc10oaHR0cHM6Ly93d3cuYmlvc3RhcnMub3JnLyksIGFuZCBbU3RhY2sgT3ZlcmZsb3ddKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zL3RhZ2dlZC9yKSwgaW5jbHVkaW5nIHRocmVhZHMgb24gc3BlY2lmaWMgcGFja2FnZXMgb3IgY29tbW9uIGJpb2luZm9ybWF0aWMgdGFza3MuCgpJIHBlcnNvbmFsbHkgdXNlIG9uZSBvciBtb3JlIG9mIHRoZXNlIHJlc291cmNlcyAqKmV2ZXJ5IGRheSoqLgoKMTB4IEdlbm9taWNzIGFsc28gaGFzIGEgaGVscGZ1bCBbMTAgdGlwcyBmb3IgYmlvbG9naXN0cyBsZWFybmluZyAgYmlvaW5mb3JtYXRpY3NdKGh0dHBzOi8vd3d3LjEweGdlbm9taWNzLmNvbS9yZXNvdXJjZXMvYW5hbHlzaXMtZ3VpZGVzLzEwLXRpcHMtZm9yLWJpb2xvZ2lzdHMtbGVhcm5pbmctYmlvaW5mb3JtYXRpY3MpIGluY2x1ZGVkIGluIHRoZWlyIHJlc291cmNlcy4KCiMgU3VtbWFyeQoKSW4gdGhpcyBzZWN0aW9uLCB3ZToKCiogU2V0IHVwIG91ciBjb21wdXRlIGVudmlyb25tZW50CiogTGVhcm5lZCBhYm91dCB0aGUgREVTZXEyIHBhY2thZ2UKKiBSZWFkIGluIGEgcmF3IGNvdW50IHRhYmxlIGFuZCBzYXZlZCBpdCBhcyBhIGRhdGEgZnJhbWUKCk5vdyB0aGF0IHdlIGhhdmUgb3VyIGNvdW50IGRhdGEgcHJvY2Vzc2VkLCB3ZSBjYW4gbW92ZSBvbiB0byAidW5ibGluZGluZyIgb3VyIGRhdGEsIGFzIHRoZSBzYW1wbGUgbmFtZXMgYXJlIHVuaXF1ZSBpZGVudGlmaWVycyBnZW5lcmF0ZWQgYnkgYSBzZXF1ZW5jaW5nIGNlbnRlciBhbmQgbm90IHZlcnkgaW5mb3JtYXRpdmUgYXMgZmFyIGFzIG91ciBleHBlcmltZW50YWwgY29uZGl0aW9ucy4KCi0tLQoKIyBPcHRpb25hbCBjb250ZW50CgpBbiBpbXBvcnRhbnQgbm90ZSBpcyB0aGF0IHRoZXJlIGFyZSBzZXZlcmFsIGJvbnVzIGNvbnRlbnQgc2VjdGlvbnMgb24gdGhlIGluc3RydWN0aW9uIHBhZ2VzLCBsaWtlIHRoZSB0d28gYmVsb3cgdGhhdCB3ZSB3aWxsIG5vdCBiZSBjb3ZlcmluZyBpbiB0aGlzIHdvcmtzaG9wLCBidXQgdGhhdCBtYXkgaGF2ZSB1c2VmdWwgY29udGV4dCBvciBiZSBoZWxwZnVsIHdoZW4geW91IHJldmlldyB0aGlzIG1hdGVyaWFsLgoKPGRldGFpbHM+CiAgICA8c3VtbWFyeT4qQ2xpY2sgZm9yIGFsdGVybmF0aXZlIERFU2VxMiBpbnB1dCBvcHRpb25zIGZvciBSU0VNIG91dHB1dHMqPC9zdW1tYXJ5PgogICAgVGhlIHBhY2thZ2UgYHR4aW1wb3J0YCBpcyBhbm90aGVyIG9wdGlvbltyZWNvbW1lbmRlZCB0aGUgREVTZXEyICBhdXRob3JzXShodHRwczovL3N1cHBvcnQuYmlvY29uZHVjdG9yLm9yZy9wLzkwNjcyLykgdG8gcmVhZCBpbiB0aGUgUlNFTSBleHBlY3RlZF9jb3VudHMsIGFzIHRoaXMgIHBhY2thZ2UgYWxsb3dzIGZvciB0aGUgYXZlcmFnZSB0cmFuc2NyaXB0IGxlbmd0aCBwZXIgZ2VuZSB0byBiZSB1c2VkIGluIHRoZSBERSBhbmFseXNpcyBhbmQsIGFzIFtkZXNjcmliZWQgYnkgdGhlIGF1dGhvcl0oaHR0cHM6Ly9zdXBwb3J0LmJpb2NvbmR1Y3Rvci5vcmcvcC84ODc2My8pLCB0aGUgYHR4aW1wb3J0LXRvLURFU2VxRGF0YVNldGAgY29uc3RydWN0b3IgZnVuY3Rpb24gcm91bmQgdGhlIG5vbi1pbnRlZ2VyIGRhdGEgZ2VuZXJhdGVkIGJ5IFJTRU0gdG8gd2hvbGUgbnVtYmVycy4KPC9kZXRhaWxzPgo8YnI+CjxkZXRhaWxzPgogICAgPHN1bW1hcnk+KkNsaWNrIGZvciBjb21wYXJpc29uIG9mIFJOQS1zZXEgZGF0YSBhbmQgbWljcm9hcnJheSBkYXRhKjwvc3VtbWFyeT4KICAgIFdpdGggW2hpZ2hlciBzZW5zaXRpdml0eSwgZ3JlYXRlciBmbGV4aWJsaXR5LCBhbmQgZGVjcmVhc2luZyBjb3N0XShodHRwczovL3d3dy5pbGx1bWluYS5jb20vc2NpZW5jZS90ZWNobm9sb2d5L25leHQtZ2VuZXJhdGlvbi1zZXF1ZW5jaW5nL21pY3JvYXJyYXktcm5hLXNlcS1jb21wYXJpc29uLmh0bWwpLCBzZXF1ZW5jaW5nIGhhcyBsYXJnZWx5IHJlcGxhY2VkIG1pY3JvYXJyYXkgYXNzYXlzIGZvciBtZWFzdXJpbmcgZ2VuZSBleHByZXNzaW9uLiBBIGtleSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHBsYXRmb3JtcyBpcyB0aGF0IG1pY3JvYXJyYXlzIG1lYXN1cmUgaW50ZW5zaXRpZXMgYW5kIGFyZSB0aGVyZWZvcmUgKmNvbnRpbm91cyogZGF0YSB3aGlsZSB0aGUgY291bnQgZGF0YSBmcm9tIHNlcXVlbmNpbmcgaXMgKmRpc2NyZXRlKi4gQSBtb3JlIGRldGFpbGVkIGNvbXBhcmlzb24gYmV0d2VlbiBtaWNyb2FycmF5cyBhbmQgc2VxdWVuY2luZyB0ZWNobm9sb2dpZXMvYW5hbHlzaXMgaXMgb3V0bGluZWQgaW4gW3RoZSBvbmxpbmUgbWF0ZXJpYWxzIGZvciBQZW5uIFN0YXRlJ3MgU1RBVDU1NSBjb3Vyc2VdKGh0dHBzOi8vb25saW5lLnN0YXQucHN1LmVkdS9zdGF0NTU1L25vZGUvMzAvKQoKPC9kZXRhaWxzPgo8YnI+CgojIFNvdXJjZXMKClRyYWluaW5nIHJlc291cmNlcyB1c2VkIHRvIGRldmVsb3AgbWF0ZXJpYWxzOgoKKiBIQkMgREdFIHNldHVwOiBodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9ER0Vfd29ya3Nob3AvbGVzc29ucy8wMV9ER0Vfc2V0dXBfYW5kX292ZXJ2aWV3Lmh0bWwKKiBIQkMgQ291bnQgTm9ybWFsaXphdGlvbjogaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvMDJfREdFX2NvdW50X25vcm1hbGl6YXRpb24uaHRtbAoqIERFU2VxMiBzdGFuZGFyZCB2aWduZXR0ZTogaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sCiogREVTZXEyIGJlZ2lubmVycyB2aWduZXR0ZTogaHR0cHM6Ly9iaW9jLmlzbS5hYy5qcC9wYWNrYWdlcy8yLjE0L2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9iZWdpbm5lci5wZGYKKiBCaW9jb25kdWN0b3IgUk5BLXNlcSBXb3JrZmxvd3M6IGh0dHBzOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvaGVscC9jb3Vyc2UtbWF0ZXJpYWxzLzIwMTUvTGVhcm5CaW9jb25kdWN0b3JGZWIyMDE1L0IwMi4xX1JOQVNlcS5odG1sCiogQ0NETCBHYXN0cmljIGNhbmNlciB0cmFpbmluZyBtYXRlcmlhbHM6IGh0dHBzOi8vYWxleHNsZW1vbmFkZS5naXRodWIuaW8vdHJhaW5pbmctbW9kdWxlcy9STkEtc2VxLzAzLWdhc3RyaWNfY2FuY2VyX2V4cGxvcmF0b3J5Lm5iLmh0bWwKKiBDQ0RMIE5ldXJvYmxhc3RvbWEgdHJhaW5pbmcgbWF0ZXJpYWxzOiBodHRwczovL2FsZXhzbGVtb25hZGUuZ2l0aHViLmlvL3RyYWluaW5nLW1vZHVsZXMvUk5BLXNlcS8wNS1uYl9jZWxsX2xpbmVfREVTZXEyLm5iLmh0bWwKCgpgYGB7ciBXcml0ZU91dC5SRGF0YSwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojSGlkZGVuIGNvZGUgYmxvY2sgdG8gd3JpdGUgb3V0IGRhdGEgZm9yIGtuaXR0aW5nCgojIGRpci5jcmVhdGUoInJkYXRhIiwgc2hvd1dhcm5pbmdzPUZBTFNFKQojIHNhdmUuaW1hZ2UoZmlsZSA9ICJyZGF0YS9SdW5uaW5nRGF0YS5SRGF0YSIpCiMgSG93IHRvIGxvYWQgZm9yIG5leHQgc2VnbWVudAojIGxvYWQoInJkYXRhL1J1bm5pbmdEYXRhLlJEYXRhIikKYGBgCgotLS0KClRoZXNlIG1hdGVyaWFscyBoYXZlIGJlZW4gYWRhcHRlZCBhbmQgZXh0ZW5kZWQgZnJvbSBtYXRlcmlhbHMgbGlzdGVkIGFib3ZlLiBUaGVzZSBhcmUgb3BlbiBhY2Nlc3MgbWF0ZXJpYWxzIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgW0NyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24gbGljZW5zZSAoQ0MgQlkgNC4wKV0oaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvNC4wLyksIHdoaWNoIHBlcm1pdHMgdW5yZXN0cmljdGVkIHVzZSwgZGlzdHJpYnV0aW9uLCBhbmQgcmVwcm9kdWN0aW9uIGluIGFueSBtZWRpdW0sIHByb3ZpZGVkIHRoZSBvcmlnaW5hbCBhdXRob3IgYW5kIHNvdXJjZSBhcmUgY3JlZGl0ZWQuCgo8YnIvPgo8YnIvPgo8aHIvPgp8IFtQcmV2aW91cyBsZXNzb25dKE1vZHVsZTA1X0FkZGl0aW9uYWxfRGV0YWlscy5odG1sKSB8IFtUb3Agb2YgdGhpcyBsZXNzb25dKCN0b3ApIHwgW05leHQgbGVzc29uXShNb2R1bGUwN19ERVNlcTJJbml0Lmh0bWwpIHwKfCA6LS0tIHwgOi0tLS06IHwgLS0tOiB8