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.

wayfinder

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:

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

rstudio default session

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.

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

  • Raw data
  • Code
  • Output

Such as illustrated in this figure from the Noble review:

new project window

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

Tools for Differential Gene Expression analysis

As discussed during the webinar, bulk RNA-seq can be used to test for differential expression between conditions or treatments, using statistical approaches that are appropriate for biological data.

While there are several tools that can be used for differential expression comparisons, we will use DESeq2 in our analysis today.

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

Additionally, DESeq2 also has an excellent vignette (link) from Love, Anders, and Huber, from which our workflow is partially adapted, and is a good resource when analyzing your own data (see also: Love, Anders, and Huber. Genome Biology. 2014.).

DESeq2 assumptions and requirements

A key assumption of DESeq2 is that biological variance is much greater than technical variance (especially if best practices for quality RNA isolation are followed, including DNase treatment!).

Since calculating variance is key to the statistical approach used for DESeq2, if we tried to compare two treatment groups with less than two replicates, we would get an error (as shown in this blog post). Without replicates, there can’t be statistical significance (e.g. p-values), but qualitative approaches are an option, like looking at the top expressed genes after normalization.

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.


Exercise: Building a better understanding of differential expression analysis

  • Post a question that that was NOT addressed but that you hope we will address in the later modules OR
  • Add a thumbs up to your favorite comment(s) to upvote it

Getting started

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.

Exercise: RSEM outputs versus DESeq2 input requirements

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