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.

wayfinder

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:

rstudio default session

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:

rstudio default session


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.

rstudio default session

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?

  1. We see in that our code has been run in the Console panel, including some warning messages.

  2. We see that a number of objects were created in our Environment panel including an object named DE_results.

  3. 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.

  1. To create a project, go to the File menu, and click New Project…. The following window will appear:

new project window

  1. 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.

  2. 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:

  • Raw data
  • Code
  • Output

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

Tools for Differential Gene Expression analysis

While there are several tools for differential expression comparisons that use statistical approaches appropriate for biological data, we will use DESeq2 in our analysis today.

DESeq2 is one of two tools, along with edgeR, that are considered ‘best practice’ for differential expression comparisons. Both tools apply similar methods that account for the “shape” of data expected for RNA-seq and are fairly stringent in calling differentially expressed genes, lowering the risk of investigating “false positive” genes (e.g. genes that don’t actually have different expression between treatment groups and therefore are not relevant to the biological process).

Additionally, DESeq2 also has an excellent vignette 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.).

DESeq2 assumptions and requirements

A key assumption of DESeq2 is that biological variance is much greater than technical variance (which should be true 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 treatment groups with only one sample each, 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.

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.


Check-in and looking forward

  • Post a question you hope will be addressed in the later modules OR
  • Add a thumbs up to your favorite comment(s) to upvote it

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
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.




Previous lesson Top of this lesson Next lesson
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