Reproducible Research
Today we’ll be exploring some RNA-seq data that is fairly representative of what we see in the core and start with input files similar to the count table you generated in the previous steps and similar to one of the outputs currently being delivered by the Advanced Sequencing Core.
[Warm-Up]
To get started, let’s get a sense of what approaches you already use to compare gene expression:
- If you’ve run a qPCR assay and enjoyed all the pipetting, put up a green ‘check’. If you’ve run a qPCR assay and needed some time away from the multichannels, put up a red ‘x’ from the reaction panel.
- If cloning a gene reporter construct (ie: promoter + GFP), worked for you the first time, put up a green check. If you have a freezer box full of cloning attempts, put up a red ‘x’.
- If you are ready to learn about bioinformatic tools for comparing gene expression, put up a green check. If you have some questions that should be addressed before we get started, use the ‘raise hand’ reaction.
Logging into the RStudio server
First navigate to server address bfx-workshop01.med.umich.edu
, using a web browser. You may see a sign-in prompt screen, similar to shown below:
Next, use your unique name and the same password used yesterday to log-in to the server from the command line. You should now see some familiar RStudio panels in your web browser window:
Creating our code file
First, we’ll create a new code file, using the toolbar at the top of our window and clicking the icon that looks like a white square with a small green plus symbol.
From the drop down menu, select the first option named ‘R Script’.
A new window should pop up in your console.
Checkpoint: Please use the red ‘x’ button in your zoom reaction panel if you would like these steps repeated and the green ‘check’ if you see a new pane with a code file named Untitled1
Best practices for file organization
As widely discussed including in a Nobel, 2009 review, file organization and data stewardship are an important parts of reproducible research.
To follow best practices for file organization for bioinformatics/computational projects, we will need to make sure there are separate locations for:
- Raw data
- Code
- Output files
Such as illustrated in this figure from the Noble review:
To organize our files for our analysis today, we’ll first navigate into the RSD_R
directory.
We can see that there is only one folder, data
:
However, if we check our working directory, what do we see?
getwd()
Since we aren’t working in RSD_R
we will need to set our working directory before creating any new directories.
setwd("~/RSD_R")
Before moving forward, let’s double check that we’re in the right place.
getwd()
After we’ve set our working directory, we’ll use the blue floppy disc icon save our ‘Untitled1’ file as “DE_Analysis.R” in the RSD_R
directory.
This new “DE_Analysis.R” will serve as a record of the analysis steps we follow for the remainder of the workshop.
Then we’ll create a new directory called outputs
to use later in our code.
dir.create("./DE_outputs")
## Warning in dir.create("./DE_outputs"): './DE_outputs' already exists
Checkpoint: Please use the green ‘check’ if you have saved your code file and see both the data
and outputs
directories your RSD_R
folder and the red ‘x’ if you do not.
Code execution shortcut reminder
Ctrl-Enter is a standard shortcut in Rstudio to send the current line (or selected lines) to the console. If you see an >
, then R has executed the command. If you see a +
, this means that the command is not complete and R is waiting (usually for a )
).
Click for review of R conventions for object names
R has some restrictions for naming objects:
- Cannot start with numbers
- Cannot include dashes
- Cannot have spaces
- Should not be identical to a named function
- Dots & underscores will work but are better to avoid
Check package installations
Several packages have already been installed on the server, so we can load them into our R session now. To do that we’ll use the library
function to load the required packages.
library(DESeq2)
library(ggplot2)
library(tidyr)
library(dplyr)
library(matrixStats)
library(ggrepel)
library(pheatmap)
library(RColorBrewer)
library(data.table)
Note: We expect to see some red messages in your console while these packages are loading
R/RStudio has great resources for getting help, including code ‘cheatsheets’ and package vignettes, like for tidyr.
Since we loaded the libraries into our R session, we can see documentation out using the ?
operator.
?`DESeq2-package`
Checkpoint: If you see the R documentation for DESeq2
pop up in your ‘help’ panel on the right, please indicate with the green ‘check’ button. If not please use the red ‘x’ button.
Tools for Differential Gene Expression analysis
As discussed during the webinar, a common application for bulk RNA-seq is to test for differential expression between conditions or treatments, using statistical approaches that are appropriate for biological data.
While there are several tools that can be used for differential expression comparisons, we will use DESeq2 in our analysis today.
DESeq2 is one of two tools, along with EdgeR, considered ‘best practice’ for differential expression. Both tools apply similar methods that account for the distributions we expect to see for RNA-seq and are fairly stringent in calling differentially expressed genes, lowering the risk of investigating genes that were really false positives (e.g. don’t really have different expression between treatment groups and therefore are not relevant to the biological process).
Additionally, DESeq2
also has an excellent vignette click link to open from Love, Anders, and Huber, from which our workflow is partially adapted, and is a good resource when analyzing your own data (see also: Love, Anders, and Huber. Genome Biology. 2014.).
Click for additional resources regarding statistical testing and tool comparison for RNA-seq data
To learn more about statistical testing and what distributions best model the behavior of RNA-seq data, a good resource is this EdX lecture by Rafael Irizarry or this lecture by Kasper Hansen. Another helpful guide is this Comparative Study for Differential Expression Analysis by Zhang et al. from 2014.
DESeq2 assumptions and requirements
A key assumption of DESeq2 is that for most experiments biological variance is much greater than technical variance (especially if best practices for quality RNA isolation are followed, including DNase treatment!).
Since calculating variance is key to the statistical approach used for DESeq2, if we tried to compare two treatment groups with less than two replicates, we would get an error (as shown in this blog post). Without replicates, there can’t be statistical significance (e.g. p-values), but qualitative approaches are an option, like looking at the top expressed genes after normalization.
Replicates in RNA-seq experiments
A frequent question for RNA-seq projects is “How many replicates do I need?”.
The general goal of our analyses is to separate the “interesting” biological contributions from the “uninteresting” technical or other contributions that either cannot be or were not controlled in the experimental design. The more sources of variation, such as samples coming from heterogenous tissues or experiments with incomplete knockdowns, the more replicates (>3) are recommended.
For a more in depth discussion of experimental design considerations, particularly for the number of replicates, please read A Beginner’s Guide to Analysis of RNA Sequencing Data and papers like this one by Hart et al that focus on estimating statistical power for RNA-seq experiments.
Sequencing depth recommendations
A related experimental design consideration is how much sequencing depth should be generated per sample. This figure shared by Illumina in their technical talks is helpful to understand the relative importance of replicates versus sequencing depth.
Generally, for the human and mouse genomes, the recommendation is 30-40 million reads per sample for polyA library preps to capture both highly expressed (abundant) and more lowly expressed (rarer) transcripts, assuming that ~25,000 protein-coding genes would be measured. However, as the image above shows, sequencing depth has less of an impact than number of replicates in detecting differentially expressed genes (DEGs).
[Exercise]: Building a better understanding of differential expression analysis
- Post a comment below regarding what key question/misconception regarding designing an RNA-seq experiment we were able to address OR
- Post a question that that was NOT addressed but that you hope we will address in the later modules OR
- Add a thumbs up to your favorite comment(s) to upvote it
Raw data as input
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_trimmed.txt
which is similar to what would be generated in the alignment steps yesterday (and what you could receive from AGC). We’ll discuss later a few normalizations that can be helpful for us to understand how much a gene is expressed within or between samples, but normalized data should not be used as an input for DESeq2.
CountTable <- read.table("data/gene_expected_count_trimmed.txt", header = TRUE, row.names = 1)
head(CountTable, n=2) # look at the top of the table
## Sample_116498 Sample_116499 Sample_116500 Sample_116501 Sample_116502
## ENSMUSG00000000001 8256 6680 7532 5122 6684
## ENSMUSG00000000003 0 0 0 0 0
## Sample_116503 Sample_116504 Sample_116505 Sample_116506 Sample_116507
## ENSMUSG00000000001 8047 6446 5559 5443 5906
## ENSMUSG00000000003 0 0 0 0 0
## Sample_116508 Sample_116509
## ENSMUSG00000000001 5771 4792
## ENSMUSG00000000003 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 fractions due to how the alignment tool resolves reads that map to multiple locuses). Since DESeq2 requires whole numbers, if we try to use the RSEM ouputs directly, DESeq2 will give us an error.
First let’s check the count table in a different way, to see if our table includes fractions.
tail(CountTable, n=2)
## Sample_116498 Sample_116499 Sample_116500 Sample_116501 Sample_116502
## ENSMUSG00000118577 752.33 613.24 417.04 412.63 429.74
## ENSMUSG00000118578 34.49 20.58 12.10 14.88 14.05
## Sample_116503 Sample_116504 Sample_116505 Sample_116506 Sample_116507
## ENSMUSG00000118577 553.50 479.16 825.36 520.06 383.61
## ENSMUSG00000118578 34.62 18.57 14.01 11.14 4.69
## Sample_116508 Sample_116509
## ENSMUSG00000118577 404.31 353.35
## ENSMUSG00000118578 11.81 7.74
[Question]: To resolve this discrepancy between the RSEM outputs and expected input for DESeq2, what could we do?
To round down all the columns of our CountTable
that include count data (all columns since we set the gene names to be our row names), we can use the round
()` function.
CountTable <- round(CountTable)
tail(CountTable, n=2) # now whole numbers
## Sample_116498 Sample_116499 Sample_116500 Sample_116501 Sample_116502
## ENSMUSG00000118577 752 613 417 413 430
## ENSMUSG00000118578 34 21 12 15 14
## Sample_116503 Sample_116504 Sample_116505 Sample_116506 Sample_116507
## ENSMUSG00000118577 554 479 825 520 384
## ENSMUSG00000118578 35 19 14 11 5
## Sample_116508 Sample_116509
## ENSMUSG00000118577 404 353
## ENSMUSG00000118578 12 8
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
Getting help
R/Rstudio has 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 also has a helpful 10 tips for biologists learning bioinformatics included in their resources.
LS0tCnRpdGxlOiAiRGF5IDIgLSBNb2R1bGUgMDY6IEFuYWx5c2lzIFNldHVwICYgSW50cm9kdWN0aW9uIHRvIERFU2VxMiIKYXV0aG9yOiAiVU0gQmlvaW5mb3JtYXRpY3MgQ29yZSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgICAgICAgaHRtbF9kb2N1bWVudDoKICAgICAgICAgICAgaW5jbHVkZXM6CiAgICAgICAgICAgICAgICBpbl9oZWFkZXI6IGhlYWRlci5odG1sCiAgICAgICAgICAgIHRoZW1lOiBwYXBlcgogICAgICAgICAgICB0b2M6IHRydWUKICAgICAgICAgICAgdG9jX2RlcHRoOiA0CiAgICAgICAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICAgICAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgICAgICAgICAgZmlnX2NhcHRpb246IHRydWUKICAgICAgICAgICAgbWFya2Rvd246IEdGTQogICAgICAgICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpib2R5eyAvKiBOb3JtYWwgICovCiAgICAgIGZvbnQtc2l6ZTogMTRwdDsKICB9CnByZSB7CiAgZm9udC1zaXplOiAxMnB0Cn0KY29kZS5yewogIGZvbnQtc2l6ZTogMTJwdDsKfQo8L3N0eWxlPgoKPCEtLS0gQWxsb3cgdGhlIHBhZ2UgdG8gYmUgd2lkZXIgLS0tPgo8c3R5bGU+CiAgICBib2R5IC5tYWluLWNvbnRhaW5lciB7CiAgICAgICAgbWF4LXdpZHRoOiAxMjAwcHg7CiAgICB9Cjwvc3R5bGU+CgoKPiAjIE9iamVjdGl2ZXM6ICAgIAo+ICogT3ZlcnZpZXcgb2YgcmVwcm9kdWNpYmxlIHJlc2VhcmNoICYgYW5hbHlzaXMgc2V0dXAKPiAqIEJyb2FkIGludHJvZHVjdGlvbiB0byBERVNlcTIgJiB3aHkgaXQgaXMgd2lkZWx5IHVzZWQgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGNvbXBhcmlzb25zCj4gKiBIb3cgdG8gaW1wb3J0IGFuZCByZXZpZXcgZ2VuZSBjb3VudCB0YWJsZQoKCiMgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gV29ya2Zsb3cKClRvZGF5IHdlIHdpbGwgcHJvY2VlZCB0aHJvdWdoIGtleSBzdGVwcyBpbiBhIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLCBzdGFydGluZyBmcm9tIGEgY291bnQgdGFibGUgdGhhdCdzIHNpbWlsYXIgdG8gd2hhdCB5b3UgZ2VuZXJhdGVkIGluIHRoZSBmaXJzdCBoYWxmIG9mIHRoZSB3b3Jrc2hvcCBhbmQgc2ltaWxhciB0byB0aGUgb25lIG9mIHRoZSBvdXRwdXRzIGluY2x1ZGVkIGluIHRoZSBbZGF0YSB0aGF0IHRoZSBBZHZhbmNlZCBHZW5vbWljcyBDb3JlIGRlbGl2ZXJzXShodHRwczovL2JyY2YubWVkaWNpbmUudW1pY2guZWR1L2NvcmVzL2FkdmFuY2VkLWdlbm9taWNzL2RhdGEtZGVsaXZlcnkvKSBmb3IgUk5BLXNlcSBsaWJyYXJpZXMuCgohW10oLi9pbWFnZXMvd2F5ZmluZGVyL3dheWZpbmRlci0wNC5wbmcpe3dpZHRoPTk1JX0KCiMgUmVwcm9kdWNpYmxlIFJlc2VhcmNoCgpUb2RheSB3ZSdsbCBiZSBleHBsb3Jpbmcgc29tZSBSTkEtc2VxIGRhdGEgdGhhdCBpcyBmYWlybHkgcmVwcmVzZW50YXRpdmUgb2Ygd2hhdCB3ZSBzZWUgaW4gdGhlIGNvcmUgYW5kIHN0YXJ0IHdpdGggaW5wdXQgZmlsZXMgc2ltaWxhciB0byB0aGUgY291bnQgdGFibGUgeW91IGdlbmVyYXRlZCBpbiB0aGUgcHJldmlvdXMgc3RlcHMgYW5kIHNpbWlsYXIgdG8gb25lIG9mIHRoZSBvdXRwdXRzIGN1cnJlbnRseSBiZWluZyBkZWxpdmVyZWQgYnkgdGhlIEFkdmFuY2VkIFNlcXVlbmNpbmcgQ29yZS4KCiMjIyMgW1dhcm0tVXBdCgpUbyBnZXQgc3RhcnRlZCwgbGV0J3MgZ2V0IGEgc2Vuc2Ugb2Ygd2hhdCBhcHByb2FjaGVzIHlvdSBhbHJlYWR5IHVzZSB0byBjb21wYXJlIGdlbmUgZXhwcmVzc2lvbjogICAgCgoqIElmIHlvdSd2ZSBydW4gYSBxUENSIGFzc2F5IGFuZCBlbmpveWVkIGFsbCB0aGUgcGlwZXR0aW5nLCBwdXQgdXAgYSBncmVlbiAnY2hlY2snLiBJZiB5b3UndmUgcnVuIGEgcVBDUiBhc3NheSBhbmQgbmVlZGVkIHNvbWUgdGltZSBhd2F5IGZyb20gdGhlIG11bHRpY2hhbm5lbHMsIHB1dCB1cCBhIHJlZCAneCcgZnJvbSB0aGUgcmVhY3Rpb24gcGFuZWwuICAgIAoqIElmIGNsb25pbmcgYSBnZW5lIHJlcG9ydGVyIGNvbnN0cnVjdCAoaWU6IHByb21vdGVyICsgR0ZQKSwgd29ya2VkIGZvciB5b3UgdGhlIGZpcnN0IHRpbWUsIHB1dCB1cCBhIGdyZWVuIGNoZWNrLiBJZiB5b3UgaGF2ZSBhIGZyZWV6ZXIgYm94IGZ1bGwgb2YgY2xvbmluZyBhdHRlbXB0cywgcHV0IHVwIGEgcmVkICd4Jy4gICAgCiogSWYgeW91IGFyZSByZWFkeSB0byBsZWFybiBhYm91dCBiaW9pbmZvcm1hdGljIHRvb2xzIGZvciBjb21wYXJpbmcgZ2VuZSBleHByZXNzaW9uLCBwdXQgdXAgYSBncmVlbiBjaGVjay4gSWYgeW91IGhhdmUgc29tZSBxdWVzdGlvbnMgdGhhdCBzaG91bGQgYmUgYWRkcmVzc2VkIGJlZm9yZSB3ZSBnZXQgc3RhcnRlZCwgdXNlIHRoZSAncmFpc2UgaGFuZCcgcmVhY3Rpb24uICAKCgojIyBMb2dnaW5nIGludG8gdGhlIFJTdHVkaW8gc2VydmVyCgpGaXJzdCBuYXZpZ2F0ZSB0byBzZXJ2ZXIgYWRkcmVzcyBgYmZ4LXdvcmtzaG9wMDEubWVkLnVtaWNoLmVkdWAsIHVzaW5nIGEgd2ViIGJyb3dzZXIuIFlvdSBtYXkgc2VlIGEgc2lnbi1pbiBwcm9tcHQgc2NyZWVuLCBzaW1pbGFyIHRvIHNob3duIGJlbG93OgoKPGJyPgohW10oLi9pbWFnZXMvUlN0dWRpb0xvZ0luUHJvbXB0LnBuZyl7d2lkdGg9NTAlfQo8YnI+CgpOZXh0LCB1c2UgeW91ciB1bmlxdWUgbmFtZSBhbmQgdGhlIHNhbWUgcGFzc3dvcmQgdXNlZCB5ZXN0ZXJkYXkgdG8gbG9nLWluIHRvIHRoZSBzZXJ2ZXIgZnJvbSB0aGUgY29tbWFuZCBsaW5lLiBZb3Ugc2hvdWxkIG5vdyBzZWUgc29tZSBmYW1pbGlhciBSU3R1ZGlvIHBhbmVscyBpbiB5b3VyIHdlYiBicm93c2VyIHdpbmRvdzoKCjxicj4KIVtdKC4vaW1hZ2VzL1JTdHVkaW9TZXJ2ZXJXaW5kb3cucG5nKXt3aWR0aD01MCV9Cjxicj4KCiMjIENyZWF0aW5nIG91ciBjb2RlIGZpbGUKCkZpcnN0LCB3ZSdsbCBjcmVhdGUgYSBuZXcgY29kZSBmaWxlLCB1c2luZyB0aGUgdG9vbGJhciBhdCB0aGUgdG9wIG9mIG91ciB3aW5kb3cgYW5kIGNsaWNraW5nIHRoZSBpY29uIHRoYXQgbG9va3MgbGlrZSBhICoqd2hpdGUgc3F1YXJlIHdpdGggYSBzbWFsbCBncmVlbiBwbHVzIHN5bWJvbCoqLgoKRnJvbSB0aGUgZHJvcCBkb3duIG1lbnUsIHNlbGVjdCB0aGUgZmlyc3Qgb3B0aW9uIG5hbWVkICdSIFNjcmlwdCcuCgpBIG5ldyB3aW5kb3cgc2hvdWxkIHBvcCB1cCBpbiB5b3VyIGNvbnNvbGUuIAoKKipDaGVja3BvaW50Kio6ICpQbGVhc2UgdXNlIHRoZSByZWQgJ3gnIGJ1dHRvbiBpbiB5b3VyIHpvb20gcmVhY3Rpb24gcGFuZWwgaWYgeW91IHdvdWxkIGxpa2UgdGhlc2Ugc3RlcHMgcmVwZWF0ZWQgYW5kIHRoZSBncmVlbiAnY2hlY2snIGlmIHlvdSBzZWUgYSBuZXcgcGFuZSB3aXRoIGEgY29kZSBmaWxlIG5hbWVkIGBVbnRpdGxlZDFgKgoKCiMjIEJlc3QgcHJhY3RpY2VzIGZvciBmaWxlIG9yZ2FuaXphdGlvbgoKQXMgd2lkZWx5IGRpc2N1c3NlZCBpbmNsdWRpbmcgaW4gYSBbTm9iZWwsIDIwMDldKGh0dHBzOi8vam91cm5hbHMucGxvcy5vcmcvcGxvc2NvbXBiaW9sL2FydGljbGU/aWQ9MTAuMTM3MS9qb3VybmFsLnBjYmkuMTAwMDQyNCkgcmV2aWV3LCBmaWxlIG9yZ2FuaXphdGlvbiBhbmQgZGF0YSBzdGV3YXJkc2hpcCBhcmUgYW4gaW1wb3J0YW50IHBhcnRzIG9mIHJlcHJvZHVjaWJsZSByZXNlYXJjaC4KClRvIGZvbGxvdyBiZXN0IHByYWN0aWNlcyBmb3IgZmlsZSBvcmdhbml6YXRpb24gZm9yIGJpb2luZm9ybWF0aWNzL2NvbXB1dGF0aW9uYWwgcHJvamVjdHMsIHdlIHdpbGwgbmVlZCB0byBtYWtlIHN1cmUgdGhlcmUgYXJlIHNlcGFyYXRlIGxvY2F0aW9ucyBmb3I6CgoqIFJhdyBkYXRhCiogQ29kZQoqIE91dHB1dCBmaWxlcwoKU3VjaCBhcyBpbGx1c3RyYXRlZCBpbiB0aGlzIGZpZ3VyZSBmcm9tIHRoZSBOb2JsZSByZXZpZXc6CgohW10oLi9pbWFnZXMvTm9ibGUyMDA5X2RhdGFwcm9qZWN0cy5wbmcpe3dpZHRoPTc1JX0KClRvIG9yZ2FuaXplIG91ciBmaWxlcyBmb3Igb3VyIGFuYWx5c2lzIHRvZGF5LCB3ZSdsbCBmaXJzdCBuYXZpZ2F0ZSBpbnRvIHRoZSBgUlNEX1JgIGRpcmVjdG9yeS4KCldlIGNhbiBzZWUgdGhhdCB0aGVyZSBpcyBvbmx5IG9uZSBmb2xkZXIsIGBkYXRhYDoKCjxicj4KIVtdKC4vaW1hZ2VzL1JTRF9SX3NjcmVlbnNob3QucG5nKXt3aWR0aD01MCV9Cjxicj4KCkhvd2V2ZXIsIGlmIHdlIGNoZWNrIG91ciB3b3JraW5nIGRpcmVjdG9yeSwgd2hhdCBkbyB3ZSBzZWU/CmBgYHtyIFNldHVwRGlyZWN0b3JpZXMsIGVjaG89VFJVRSwgZXZhbD1GQUxTRX0KZ2V0d2QoKQpgYGAKClNpbmNlIHdlIGFyZW4ndCB3b3JraW5nIGluIGBSU0RfUmAgd2Ugd2lsbCBuZWVkIHRvIHNldCBvdXIgd29ya2luZyBkaXJlY3RvcnkgYmVmb3JlIGNyZWF0aW5nIGFueSBuZXcgZGlyZWN0b3JpZXMuCmBgYHtyIFNldFdvcmtpbmdEaXIsIGVjaG8gPSBUUlVFLCBldmFsPUZBTFNFfQpzZXR3ZCgifi9SU0RfUiIpCmBgYAoKQmVmb3JlIG1vdmluZyBmb3J3YXJkLCBsZXQncyBkb3VibGUgY2hlY2sgdGhhdCB3ZSdyZSBpbiB0aGUgcmlnaHQgcGxhY2UuCmBgYHtyIENoZWNrRGlyZWN0b3JpZXMsIGV2YWw9RkFMU0V9CmdldHdkKCkKYGBgCgpBZnRlciB3ZSd2ZSBzZXQgb3VyIHdvcmtpbmcgZGlyZWN0b3J5LCB3ZSdsbCB1c2UgdGhlIGJsdWUgZmxvcHB5IGRpc2MgaWNvbiBzYXZlIG91ciAnVW50aXRsZWQxJyBmaWxlIGFzICJERV9BbmFseXNpcy5SIiBpbiB0aGUgYFJTRF9SYCBkaXJlY3RvcnkuCgpUaGlzIG5ldyAiREVfQW5hbHlzaXMuUiIgd2lsbCBzZXJ2ZSBhcyBhIHJlY29yZCBvZiB0aGUgYW5hbHlzaXMgc3RlcHMgd2UgZm9sbG93IGZvciB0aGUgcmVtYWluZGVyIG9mIHRoZSB3b3Jrc2hvcC4KClRoZW4gd2UnbGwgY3JlYXRlIGEgbmV3IGRpcmVjdG9yeSBjYWxsZWQgYG91dHB1dHNgIHRvIHVzZSBsYXRlciBpbiBvdXIgY29kZS4KYGBge3IgQ3JlYXRlT3V0cHV0c30KZGlyLmNyZWF0ZSgiLi9ERV9vdXRwdXRzIikKYGBgCgoKKipDaGVja3BvaW50Kio6ICpQbGVhc2UgdXNlIHRoZSBncmVlbiAnY2hlY2snIGlmIHlvdSBoYXZlIHNhdmVkIHlvdXIgY29kZSBmaWxlIGFuZCBzZWUgYm90aCB0aGUgYGRhdGFgIGFuZCBgb3V0cHV0c2AgZGlyZWN0b3JpZXMgeW91ciBgUlNEX1JgIGZvbGRlciBhbmQgdGhlIHJlZCAneCcgaWYgeW91IGRvIG5vdC4qCgojIyMjIENvZGUgZXhlY3V0aW9uIHNob3J0Y3V0IHJlbWluZGVyCgoqKkN0cmwtRW50ZXIqKiBpcyBhIHN0YW5kYXJkIHNob3J0Y3V0IGluIFJzdHVkaW8gdG8gc2VuZCB0aGUgY3VycmVudCBsaW5lIChvciBzZWxlY3RlZCBsaW5lcykgdG8gdGhlIGNvbnNvbGUuIElmIHlvdSBzZWUgYW4gYD5gLCB0aGVuIFIgaGFzIGV4ZWN1dGVkIHRoZSBjb21tYW5kLiBJZiB5b3Ugc2VlIGEgYCtgLCB0aGlzIG1lYW5zIHRoYXQgdGhlIGNvbW1hbmQgaXMgbm90IGNvbXBsZXRlIGFuZCBSIGlzIHdhaXRpbmcgKHVzdWFsbHkgZm9yIGEgYClgKS4KCjxkZXRhaWxzPgogICAgPHN1bW1hcnk+KkNsaWNrIGZvciByZXZpZXcgb2YgUiBjb252ZW50aW9ucyBmb3Igb2JqZWN0IG5hbWVzKjwvc3VtbWFyeT4KICAgIFIgaGFzIHNvbWUgcmVzdHJpY3Rpb25zIGZvciBuYW1pbmcgb2JqZWN0czoKICAgIAogICAgKiBDYW5ub3Qgc3RhcnQgd2l0aCBudW1iZXJzCiAgICAqIENhbm5vdCBpbmNsdWRlIGRhc2hlcwogICAgKiBDYW5ub3QgaGF2ZSBzcGFjZXMKICAgICogU2hvdWxkIG5vdCBiZSBpZGVudGljYWwgdG8gYSBuYW1lZCBmdW5jdGlvbgogICAgKiBEb3RzICYgdW5kZXJzY29yZXMgd2lsbCB3b3JrIGJ1dCBhcmUgYmV0dGVyIHRvIGF2b2lkCjwvZGV0YWlscz4KCi0tLQoKIyMgQ2hlY2sgcGFja2FnZSBpbnN0YWxsYXRpb25zCgpTZXZlcmFsICBwYWNrYWdlcyBoYXZlIGFscmVhZHkgYmVlbiBpbnN0YWxsZWQgb24gdGhlIHNlcnZlciwgc28gd2UgY2FuIGxvYWQgdGhlbSBpbnRvIG91ciBSIHNlc3Npb24gbm93LiBUbyBkbyB0aGF0IHdlJ2xsIHVzZSB0aGUgYGxpYnJhcnlgIGZ1bmN0aW9uIHRvIGxvYWQgdGhlIHJlcXVpcmVkIHBhY2thZ2VzLgoKYGBge3IgTW9kdWxlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz0gRkFMU0UsIGV2YWw9VFJVRX0KbGlicmFyeShERVNlcTIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKbGlicmFyeShtYXRyaXhTdGF0cykKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShkYXRhLnRhYmxlKQpgYGAKCgoqTm90ZTogV2UgZXhwZWN0IHRvIHNlZSBzb21lIHJlZCBtZXNzYWdlcyBpbiB5b3VyIGNvbnNvbGUgd2hpbGUgdGhlc2UgcGFja2FnZXMgYXJlIGxvYWRpbmcqCgpSL1JTdHVkaW8gaGFzIGdyZWF0IHJlc291cmNlcyBmb3IgZ2V0dGluZyBoZWxwLCBpbmNsdWRpbmcgW2NvZGUgJ2NoZWF0c2hlZXRzJ10oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTYvMTAvci1jaGVhdC1zaGVldC0zLnBkZikgYW5kIHBhY2thZ2UgdmlnbmV0dGVzLCBsaWtlIGZvciBbdGlkeXJdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy90aWR5ci92aWduZXR0ZXMvdGlkeS1kYXRhLmh0bWwpLgoKU2luY2Ugd2UgbG9hZGVkIHRoZSBsaWJyYXJpZXMgaW50byBvdXIgUiBzZXNzaW9uLCB3ZSBjYW4gc2VlIGRvY3VtZW50YXRpb24gb3V0IHVzaW5nIHRoZSBgP2Agb3BlcmF0b3IuCmBgYHtyIENoZWNrRG9jdW1lbnRhaW9ufQo/YERFU2VxMi1wYWNrYWdlYApgYGAKCioqQ2hlY2twb2ludCoqOiAqSWYgeW91IHNlZSB0aGUgUiBkb2N1bWVudGF0aW9uIGZvciBgREVTZXEyYCBwb3AgdXAgaW4geW91ciAnaGVscCcgcGFuZWwgb24gdGhlIHJpZ2h0LCBwbGVhc2UgaW5kaWNhdGUgd2l0aCB0aGUgZ3JlZW4gJ2NoZWNrJyBidXR0b24uIElmIG5vdCBwbGVhc2UgdXNlIHRoZSByZWQgJ3gnIGJ1dHRvbi4qCgotLS0tCgojIFRvb2xzIGZvciBEaWZmZXJlbnRpYWwgR2VuZSBFeHByZXNzaW9uIGFuYWx5c2lzCgpBcyBkaXNjdXNzZWQgZHVyaW5nIHRoZSB3ZWJpbmFyLCBhIGNvbW1vbiBhcHBsaWNhdGlvbiBmb3IgYnVsayBSTkEtc2VxIGlzIHRvIHRlc3QgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGJldHdlZW4gY29uZGl0aW9ucyBvciB0cmVhdG1lbnRzLCB1c2luZyBzdGF0aXN0aWNhbCBhcHByb2FjaGVzIHRoYXQgYXJlIGFwcHJvcHJpYXRlIGZvciBiaW9sb2dpY2FsIGRhdGEuCgpXaGlsZSB0aGVyZSBhcmUgc2V2ZXJhbCB0b29scyB0aGF0IGNhbiBiZSB1c2VkIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBjb21wYXJpc29ucywgd2Ugd2lsbCB1c2UgW0RFU2VxMl0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL0RFU2VxMi5odG1sKSBpbiBvdXIgYW5hbHlzaXMgdG9kYXkuCgpERVNlcTIgaXMgb25lIG9mIHR3byB0b29scywgYWxvbmcgd2l0aCBbRWRnZVJdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9lZGdlUi5odG1sKSwgY29uc2lkZXJlZCBbJ2Jlc3QgcHJhY3RpY2UnXShodHRwczovL2JtY2Jpb2luZm9ybWF0aWNzLmJpb21lZGNlbnRyYWwuY29tL2FydGljbGVzLzEwLjExODYvMTQ3MS0yMTA1LTE0LTkxKSBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24uIEJvdGggdG9vbHMgYXBwbHkgc2ltaWxhciBtZXRob2RzIHRoYXQgYWNjb3VudCBmb3IgdGhlIGRpc3RyaWJ1dGlvbnMgd2UgZXhwZWN0IHRvIHNlZSBmb3IgUk5BLXNlcSBhbmQgYXJlIGZhaXJseSBzdHJpbmdlbnQgaW4gY2FsbGluZyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMsIGxvd2VyaW5nIHRoZSByaXNrIG9mIGludmVzdGlnYXRpbmcgZ2VuZXMgdGhhdCB3ZXJlIHJlYWxseSBmYWxzZSBwb3NpdGl2ZXMgKGUuZy4gZG9uJ3QgcmVhbGx5IGhhdmUgZGlmZmVyZW50IGV4cHJlc3Npb24gYmV0d2VlbiB0cmVhdG1lbnQgZ3JvdXBzIGFuZCB0aGVyZWZvcmUgYXJlIG5vdCByZWxldmFudCB0byB0aGUgYmlvbG9naWNhbCBwcm9jZXNzKS4KCkFkZGl0aW9uYWxseSwgYERFU2VxMmAgYWxzbyBoYXMgYW4KW2V4Y2VsbGVudCB2aWduZXR0ZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sKSAqY2xpY2sgbGluayB0byBvcGVuKgpmcm9tIExvdmUsIEFuZGVycywgYW5kIEh1YmVyLCBmcm9tIHdoaWNoIG91ciB3b3JrZmxvdyBpcyBwYXJ0aWFsbHkgYWRhcHRlZCwgYW5kIGlzIGEgZ29vZCByZXNvdXJjZSB3aGVuIGFuYWx5emluZyB5b3VyIG93biBkYXRhCihzZWUgYWxzbzogW0xvdmUsIEFuZGVycywgYW5kIEh1YmVyLiBfR2Vub21lIEJpb2xvZ3lfLiAyMDE0Ll0oaHR0cHM6Ly9kb2kub3JnLzEwLjExODYvczEzMDU5LTAxNC0wNTUwLTgpKS4KCgo8ZGV0YWlscz4KICAgIDxzdW1tYXJ5PipDbGljayBmb3IgYWRkaXRpb25hbCByZXNvdXJjZXMgcmVnYXJkaW5nIHN0YXRpc3RpY2FsIHRlc3RpbmcgYW5kIHRvb2wgY29tcGFyaXNvbiBmb3IgUk5BLXNlcSBkYXRhKjwvc3VtbWFyeT4KICAgIFRvIGxlYXJuIG1vcmUgYWJvdXQgc3RhdGlzdGljYWwgdGVzdGluZyBhbmQgd2hhdCBkaXN0cmlidXRpb25zIGJlc3QgbW9kZWwgdGhlIGJlaGF2aW9yIG9mIFJOQS1zZXEgZGF0YSwgYSBnb29kIHJlc291cmNlIGlzIHRoaXMgW0VkWCBsZWN0dXJlIGJ5IFJhZmFlbCBJcml6YXJyeV0oaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1ISzdXS3NMM2MydyZmZWF0dXJlPXlvdXR1LmJlKSBvciB0aGlzIFtsZWN0dXJlIGJ5IEthc3BlciBIYW5zZW5dKGh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9QzhSTnZXdTdwQXcpLiBBbm90aGVyIGhlbHBmdWwgZ3VpZGUgaXMgdGhpcyBbQ29tcGFyYXRpdmUgU3R1ZHkgZm9yIERpZmZlcmVudGlhbCBFeHByZXNzaW9uIEFuYWx5c2lzIGJ5IFpoYW5nIGV0IGFsLl0oaHR0cHM6Ly9qb3VybmFscy5wbG9zLm9yZy9wbG9zb25lL2FydGljbGU/aWQ9MTAuMTM3MS9qb3VybmFsLnBvbmUuMDEwMzIwNykgZnJvbSAyMDE0Lgo8L2RldGFpbHM+CgoKIyMgREVTZXEyIGFzc3VtcHRpb25zIGFuZCByZXF1aXJlbWVudHMKCkEga2V5IGFzc3VtcHRpb24gb2YgREVTZXEyIGlzIHRoYXQgZm9yIG1vc3QgZXhwZXJpbWVudHMgKmJpb2xvZ2ljYWwgdmFyaWFuY2UgaXMgbXVjaCBncmVhdGVyIHRoYW4gdGVjaG5pY2FsIHZhcmlhbmNlKiAoZXNwZWNpYWxseSBpZiBbYmVzdCBwcmFjdGljZXNdKGh0dHBzOi8vd3d3LnR4Z2VuLnRhbXUuZWR1L2ZhcS9ybmEtaXNvbGF0aW9uLWJlc3QtcHJhY3RpY2VzLykgZm9yIFtxdWFsaXR5IFJOQSBpc29sYXRpb25dKGh0dHBzOi8vd3d3LmJpb2NvbXBhcmUuY29tL0JlbmNoLVRpcHMvMTI4NzkwLUZvdXItVGlwcy1mb3ItUGVyZmVjdGluZy1STkEtSXNvbGF0aW9uLykgYXJlIGZvbGxvd2VkLCBpbmNsdWRpbmcgRE5hc2UgdHJlYXRtZW50ISkuCgoKU2luY2UgY2FsY3VsYXRpbmcgdmFyaWFuY2UgaXMga2V5IHRvIHRoZSBzdGF0aXN0aWNhbCBhcHByb2FjaCB1c2VkIGZvciBERVNlcTIsIGlmIHdlIHRyaWVkIHRvIGNvbXBhcmUgdHdvIHRyZWF0bWVudCBncm91cHMgd2l0aCBsZXNzIHRoYW4gKip0d28qKiByZXBsaWNhdGVzLCB3ZSB3b3VsZCBnZXQgYW4gZXJyb3IgKGFzIHNob3duIGluIFt0aGlzIGJsb2cgcG9zdF0oaHR0cHM6Ly9zdXBwb3J0LmJpb2NvbmR1Y3Rvci5vcmcvcC84OTc0Ni8pKS4gV2l0aG91dCByZXBsaWNhdGVzLCB0aGVyZSBjYW4ndCBiZSBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UgKGUuZy4gcC12YWx1ZXMpLCBidXQgKnF1YWxpdGF0aXZlKiBhcHByb2FjaGVzIGFyZSBhbiBvcHRpb24sIGxpa2UgbG9va2luZyBhdCB0aGUgdG9wIGV4cHJlc3NlZCBnZW5lcyBhZnRlciBub3JtYWxpemF0aW9uLgoKIyMjIFJlcGxpY2F0ZXMgaW4gUk5BLXNlcSBleHBlcmltZW50cwoKQSBmcmVxdWVudCBxdWVzdGlvbiBmb3IgUk5BLXNlcSBwcm9qZWN0cyBpcyAiSG93IG1hbnkgcmVwbGljYXRlcyBkbyBJIG5lZWQ/Ii4KClRoZSBnZW5lcmFsIGdvYWwgb2Ygb3VyIGFuYWx5c2VzIGlzIHRvIHNlcGFyYXRlIHRoZSDigJxpbnRlcmVzdGluZ+KAnSBiaW9sb2dpY2FsIGNvbnRyaWJ1dGlvbnMgZnJvbSB0aGUg4oCcdW5pbnRlcmVzdGluZ+KAnSB0ZWNobmljYWwgb3Igb3RoZXIgY29udHJpYnV0aW9ucyB0aGF0IGVpdGhlciBjYW5ub3QgYmUgb3Igd2VyZSBub3QgY29udHJvbGxlZCBpbiB0aGUgZXhwZXJpbWVudGFsIGRlc2lnbi4gVGhlIG1vcmUgc291cmNlcyBvZiB2YXJpYXRpb24sIHN1Y2ggYXMgc2FtcGxlcyBjb21pbmcgZnJvbSBoZXRlcm9nZW5vdXMgdGlzc3VlcyBvciBleHBlcmltZW50cyB3aXRoIGluY29tcGxldGUga25vY2tkb3ducywgdGhlIG1vcmUgcmVwbGljYXRlcyAoPjMpIGFyZSByZWNvbW1lbmRlZC4gIAoKCiFbSW1hZ2Ugb2YgdGVjaG5pY2FsLCBiaW9sb2dpY2FsLCBhbmQgZXhwZXJpbWVudGFsIGNvbnRyaWJ1dG9ycyB0byBnZW5lIGV4cHJlc3Npb24sIGZyb20gSEJDIHRyYWluaW5nIG1hdGVyaWFsc10oLi9pbWFnZXMvZGVfdmFyaWF0aW9uLnBuZyl7d2lkdGg9NzUlfQoKCkZvciBhIG1vcmUgaW4gZGVwdGggZGlzY3Vzc2lvbiBvZiBleHBlcmltZW50YWwgZGVzaWduIGNvbnNpZGVyYXRpb25zLCBwYXJ0aWN1bGFybHkgZm9yIHRoZSBudW1iZXIgb2YgcmVwbGljYXRlcywgcGxlYXNlIHJlYWQgW0EgQmVnaW5uZXLigJlzIEd1aWRlIHRvIEFuYWx5c2lzIG9mIFJOQSBTZXF1ZW5jaW5nIERhdGFdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzYwOTYzNDYvKSBhbmQgcGFwZXJzIGxpa2UgdGhpcyBvbmUgYnkgW0hhcnQgZXQgYWxdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzM4NDI4ODQvKSB0aGF0IGZvY3VzIG9uIGVzdGltYXRpbmcgc3RhdGlzdGljYWwgcG93ZXIgZm9yIFJOQS1zZXEgZXhwZXJpbWVudHMuCgojIyMjIFNlcXVlbmNpbmcgZGVwdGggcmVjb21tZW5kYXRpb25zCgpBIHJlbGF0ZWQgZXhwZXJpbWVudGFsIGRlc2lnbiBjb25zaWRlcmF0aW9uIGlzIGhvdyBtdWNoIHNlcXVlbmNpbmcgZGVwdGggc2hvdWxkIGJlIGdlbmVyYXRlZCBwZXIgc2FtcGxlLiBUaGlzIGZpZ3VyZSBzaGFyZWQgYnkgSWxsdW1pbmEgaW4gdGhlaXIgdGVjaG5pY2FsIHRhbGtzIGlzIGhlbHBmdWwgdG8gdW5kZXJzdGFuZCB0aGUgcmVsYXRpdmUgaW1wb3J0YW5jZSBvZiByZXBsaWNhdGVzIHZlcnN1cyBzZXF1ZW5jaW5nIGRlcHRoLgoKCiFbSWxsdW1pbmEncyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZWNvdmVyeSBhY3Jvc3MgcmVwbGljYXRlIG51bWJlciBhbmQgc2VxdWVuY2luZyBkZXB0aF0oLi9pbWFnZXMvZGVfcmVwbGljYXRlc19pbWcucG5nKXt3aWR0aD03NSV9CgpHZW5lcmFsbHksIGZvciB0aGUgaHVtYW4gYW5kIG1vdXNlIGdlbm9tZXMsIHRoZSByZWNvbW1lbmRhdGlvbiBpcyAzMC00MCBtaWxsaW9uIHJlYWRzIHBlciBzYW1wbGUgZm9yIHBvbHlBIGxpYnJhcnkgcHJlcHMgdG8gY2FwdHVyZSBib3RoIGhpZ2hseSBleHByZXNzZWQgKGFidW5kYW50KSBhbmQgbW9yZSBsb3dseSBleHByZXNzZWQgKHJhcmVyKSB0cmFuc2NyaXB0cywgYXNzdW1pbmcgdGhhdCB+MjUsMDAwIHByb3RlaW4tY29kaW5nIGdlbmVzIHdvdWxkIGJlIG1lYXN1cmVkLiBIb3dldmVyLCBhcyB0aGUgaW1hZ2UgYWJvdmUgc2hvd3MsIHNlcXVlbmNpbmcgZGVwdGggaGFzIGxlc3Mgb2YgYW4gaW1wYWN0IHRoYW4gbnVtYmVyIG9mIHJlcGxpY2F0ZXMgaW4gZGV0ZWN0aW5nIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyAoREVHcykuCgojIyMjIFtFeGVyY2lzZV06IEJ1aWxkaW5nIGEgYmV0dGVyIHVuZGVyc3RhbmRpbmcgb2YgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMKCjEpIFBvc3QgYSBjb21tZW50IGJlbG93IHJlZ2FyZGluZyB3aGF0IGtleSBxdWVzdGlvbi9taXNjb25jZXB0aW9uIHJlZ2FyZGluZyBkZXNpZ25pbmcgYW4gUk5BLXNlcSBleHBlcmltZW50IHdlIHdlcmUgYWJsZSB0byBhZGRyZXNzICoqT1IqKiAgIAoxKSBQb3N0IGEgcXVlc3Rpb24gdGhhdCB0aGF0IHdhcyBOT1QgYWRkcmVzc2VkIGJ1dCB0aGF0IHlvdSBob3BlIHdlIHdpbGwgYWRkcmVzcyBpbiB0aGUgbGF0ZXIgbW9kdWxlcyAqKk9SKiogICAgCjEpIEFkZCBhIHRodW1icyB1cCB0byB5b3VyIGZhdm9yaXRlIGNvbW1lbnQocykgdG8gdXB2b3RlIGl0ICAgIAoKLS0tLQoKCiMjIyBSYXcgZGF0YSBhcyBpbnB1dAoKQW5vdGhlciBrZXkgYXNzdW1wdGlvbiBmb3IgREVTZXEyIGlzIHRoYXQgdGhlIGFuYWx5c2lzIHdpbGwgc3RhcnQgd2l0aCBbdW4tbm9ybWFsaXplZCBjb3VudHNdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCN3aHktdW4tbm9ybWFsaXplZC1jb3VudHMpLgoKVG8gYmVnaW4gb3VyIGFuYWx5c2lzLCB3ZSdsbCByZWFkIGluIHRoZSAqKnJhdyoqIGNvdW50IGRhdGEgZmlsZSwgYGdlbmVfZXhwZWN0ZWRfY291bnRfdHJpbW1lZC50eHRgIHdoaWNoIGlzIHNpbWlsYXIgdG8gd2hhdCB3b3VsZCBiZSBnZW5lcmF0ZWQgaW4gdGhlIGFsaWdubWVudCBzdGVwcyB5ZXN0ZXJkYXkgKGFuZCB3aGF0IHlvdSBjb3VsZCByZWNlaXZlIGZyb20gQUdDKS4gV2UnbGwgZGlzY3VzcyBsYXRlciBhIGZldyBub3JtYWxpemF0aW9ucyB0aGF0IGNhbiBiZSBoZWxwZnVsIGZvciB1cyB0byB1bmRlcnN0YW5kIGhvdyBtdWNoIGEgZ2VuZSBpcyBleHByZXNzZWQgd2l0aGluIG9yIGJldHdlZW4gc2FtcGxlcywgYnV0IG5vcm1hbGl6ZWQgZGF0YSAqKnNob3VsZCBub3QqKiBiZSB1c2VkIGFzIGFuIGlucHV0IGZvciBERVNlcTIuCgpgYGB7ciBEYXRhVGFibGUsIGV2YWw9VFJVRSwgZWNobz1GQUxTRX0KIyMgbmVlZCB0byBpbmNsdWRlIHBhdGhzIHJlbGF0aXZlIHRvIHNpdGUgYnVpbGQgbG9jYXRpb25zIGJ1dCBrZWVwIGNvZGUgYmxvY2sgaGlkZGVuCkNvdW50VGFibGUgPC0gcmVhZC50YWJsZSgiLi4vZGF0YS9nZW5lX2V4cGVjdGVkX2NvdW50X3RyaW1tZWQudHh0IiwgaGVhZGVyID0gVFJVRSwgcm93Lm5hbWVzID0gMSkKYGBgCgpgYGB7ciBEYXRhVGFibGUyLCBldmFsPUZBTFNFfQpDb3VudFRhYmxlIDwtIHJlYWQudGFibGUoImRhdGEvZ2VuZV9leHBlY3RlZF9jb3VudF90cmltbWVkLnR4dCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpCmBgYApgYGB7ciBEYXRhVGFibGVDaGVjaywgZXZhbD1UUlVFfQpoZWFkKENvdW50VGFibGUsIG49MikgIyBsb29rIGF0IHRoZSB0b3Agb2YgdGhlIHRhYmxlCmBgYAoKTm93IHRoYXQgdGhlIGZpbGUgaXMgcmVhZCBpbnRvIFIsIG5vdGUgdGhhdCB3ZSd2ZSBjcmVhdGVkIGEgZGF0YSBmcmFtZSB0aGF0IGluY2x1ZGVzICdnZW5lIGlkcycgaW4gRU5TRU1CTCBmb3JtYXQgYXMgcm93bmFtZXMgYW5kIGNvdW50IGRhdGEgZnJvbSB0d2VsdmUgZGlmZmVyZW50IHNhbXBsZXMuCgojIyMjIFtFeGVyY2lzZV06IFJTRU0gb3V0cHV0cyB2ZXJzdXMgREVTZXEyIGlucHV0IHJlcXVpcmVtZW50cwoKSWYgd2UgdGhpbmsgYmFjayB0byB0aGUgUlNFTSBvdXRwdXRzLCB0aGUgJ2V4cGVjdGVkX2NvdW50cycgdGFibGUgbWF5IGluY2x1ZGUgZnJhY3Rpb25zIGR1ZSB0byBob3cgdGhlIGFsaWdubWVudCB0b29sIHJlc29sdmVzIHJlYWRzIHRoYXQgbWFwIHRvIG11bHRpcGxlIGxvY3VzZXMpLiBTaW5jZSBERVNlcTIgcmVxdWlyZXMgd2hvbGUgbnVtYmVycywgaWYgd2UgdHJ5IHRvIHVzZSB0aGUgUlNFTSBvdXB1dHMgZGlyZWN0bHksIERFU2VxMiB3aWxsIGdpdmUgdXMgYW4gZXJyb3IuCgpGaXJzdCBsZXQncyBjaGVjayB0aGUgY291bnQgdGFibGUgaW4gYSBkaWZmZXJlbnQgd2F5LCB0byBzZWUgaWYgb3VyIHRhYmxlIGluY2x1ZGVzIGZyYWN0aW9ucy4KYGBge3IgUlNFTW91dHB1dH0KdGFpbChDb3VudFRhYmxlLCBuPTIpCmBgYAoKKltRdWVzdGlvbl06IFRvIHJlc29sdmUgdGhpcyBkaXNjcmVwYW5jeSBiZXR3ZWVuIHRoZSBSU0VNIG91dHB1dHMgYW5kIGV4cGVjdGVkIGlucHV0IGZvciBERVNlcTIsIHdoYXQgY291bGQgd2UgZG8/KgoKVG8gcm91bmQgZG93biBhbGwgdGhlIGNvbHVtbnMgb2Ygb3VyIGBDb3VudFRhYmxlYCB0aGF0IGluY2x1ZGUgY291bnQgZGF0YSAoYWxsIGNvbHVtbnMgc2luY2Ugd2Ugc2V0IHRoZSBnZW5lIG5hbWVzIHRvIGJlIG91ciByb3cgbmFtZXMpLCB3ZSBjYW4gdXNlIHRoZSBgcm91bmRgKClgIGZ1bmN0aW9uLgpgYGB7ciBSb3VuZFJhd0NvdW50c30KQ291bnRUYWJsZSA8LSByb3VuZChDb3VudFRhYmxlKQp0YWlsKENvdW50VGFibGUsIG49MikgIyBub3cgd2hvbGUgbnVtYmVycwpgYGAKCkFuIGltcG9ydGFudCBub3RlIGlzIHRoYXQgdGhlcmUgYXJlIHNldmVyYWwgYm9udXMgY29udGVudCBzZWN0aW9ucyBvbiB0aGUgaW5zdHJ1Y3Rpb24gcGFnZXMsIGxpa2UgdGhlIHR3byBiZWxvdyB0aGF0IHdlIHdpbGwgbm90IGJlIGNvdmVyaW5nIGluIHRoaXMgd29ya3Nob3AsIGJ1dCB0aGF0IG1heSBoYXZlIHVzZWZ1bCBjb250ZXh0IG9yIGJlIGhlbHBmdWwgd2hlbiB5b3UgcmV2aWV3IHRoaXMgbWF0ZXJpYWwuCgo8ZGV0YWlscz4KICAgIDxzdW1tYXJ5PipDbGljayBmb3IgYWx0ZXJuYXRpdmUgREVTZXEyIGlucHV0IG9wdGlvbnMgZm9yIFJTRU0gb3V0cHV0cyo8L3N1bW1hcnk+CiAgICBUaGUgcGFja2FnZSBgdHhpbXBvcnRgIGlzIGFub3RoZXIgb3B0aW9uW3JlY29tbWVuZGVkIHRoZSBERVNlcTIgIGF1dGhvcnNdKGh0dHBzOi8vc3VwcG9ydC5iaW9jb25kdWN0b3Iub3JnL3AvOTA2NzIvKSB0byByZWFkIGluIHRoZSBSU0VNIGV4cGVjdGVkX2NvdW50cywgYXMgdGhpcyAgcGFja2FnZSBhbGxvd3MgZm9yIHRoZSBhdmVyYWdlIHRyYW5zY3JpcHQgbGVuZ3RoIHBlciBnZW5lIHRvIGJlIHVzZWQgaW4gdGhlIERFIGFuYWx5c2lzIGFuZCwgYXMgW2Rlc2NyaWJlZCBieSB0aGUgYXV0aG9yXShodHRwczovL3N1cHBvcnQuYmlvY29uZHVjdG9yLm9yZy9wLzg4NzYzLyksIHRoZSBgdHhpbXBvcnQtdG8tREVTZXFEYXRhU2V0YCBjb25zdHJ1Y3RvciBmdW5jdGlvbiByb3VuZCB0aGUgbm9uLWludGVnZXIgZGF0YSBnZW5lcmF0ZWQgYnkgUlNFTSB0byB3aG9sZSBudW1iZXJzLgoKPC9kZXRhaWxzPgoKPGRldGFpbHM+CiAgICA8c3VtbWFyeT4qQ2xpY2sgZm9yIGNvbXBhcmlzb24gb2YgUk5BLXNlcSBkYXRhIGFuZCBtaWNyb2FycmF5IGRhdGEqPC9zdW1tYXJ5PgogICAgV2l0aCBbaGlnaGVyIHNlbnNpdGl2aXR5LCBncmVhdGVyIGZsZXhpYmxpdHksIGFuZCBkZWNyZWFzaW5nIGNvc3RdKGh0dHBzOi8vd3d3LmlsbHVtaW5hLmNvbS9zY2llbmNlL3RlY2hub2xvZ3kvbmV4dC1nZW5lcmF0aW9uLXNlcXVlbmNpbmcvbWljcm9hcnJheS1ybmEtc2VxLWNvbXBhcmlzb24uaHRtbCksIHNlcXVlbmNpbmcgaGFzIGxhcmdlbHkgcmVwbGFjZWQgbWljcm9hcnJheSBhc3NheXMgZm9yIG1lYXN1cmluZyBnZW5lIGV4cHJlc3Npb24uIEEga2V5IGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcGxhdGZvcm1zIGlzIHRoYXQgbWljcm9hcnJheXMgbWVhc3VyZSBpbnRlbnNpdGllcyBhbmQgYXJlIHRoZXJlZm9yZSAqY29udGlub3VzKiBkYXRhIHdoaWxlIHRoZSBjb3VudCBkYXRhIGZyb20gc2VxdWVuY2luZyBpcyAqZGlzY3JldGUqLiBBIG1vcmUgZGV0YWlsZWQgY29tcGFyaXNvbiBiZXR3ZWVuIG1pY3JvYXJyYXlzIGFuZCBzZXF1ZW5jaW5nIHRlY2hub2xvZ2llcy9hbmFseXNpcyBpcyBvdXRsaW5lZCBpbiBbdGhlIG9ubGluZSBtYXRlcmlhbHMgZm9yIFBlbm4gU3RhdGUncyBTVEFUNTU1IGNvdXJzZV0oaHR0cHM6Ly9vbmxpbmUuc3RhdC5wc3UuZWR1L3N0YXQ1NTUvbm9kZS8zMC8pCgo8L2RldGFpbHM+CgoKIyMgR2V0dGluZyBoZWxwCgpSL1JzdHVkaW8gaGFzIGEgc3Ryb25nIGNvbW11bml0eSBjb21wb25lbnQgc28gaWYgeW91IGFyZSBnZXR0aW5nIGFuIGVycm9yIG9yIHdvbmRlcmluZyBob3cgdG8gbWFrZSBhIGNvbW1hbmQgd29yayBvciBob3cgdG8gcGVyZm9ybSBhIHNwZWNpZmljIHRhc2ssIHRoZXJlIGlzIGxpa2VseSBhbHJlYWR5IGEgc29sdXRpb24gb3V0IHRoZXJlLiBSZW1lbWJlciB0aGF0IEdvb2dsZSBpcyB5b3VyIGZyaWVuZCwgYWx0aG91Z2ggaXQgY2FuIHNvbWV0aW1lcyBiZSBhIGNoYWxsZW5nZSB0byBmaWd1cmUgb3V0ICp3aGF0IHRvIHNlYXJjaCBmb3IqLiBLZXkgcGFydHMgb2YgYSBzdWNjZXNzZnVsIHNlYXJjaDoKCiogUGFja2FnZSBvciBjb21tYW5kIHJ1bgoqIGBSYCBvciBgQmlvY29uZHVjdG9yYAoqIFRoZSBlcnJvciBtZXNzYWdlIGlmIHRoZXJlIGlzIG9uZQoqIFZlcnNpb24gaW5mb3JtYXRpb24KCkhvdyB0byBnZXQgc2Vzc2lvbiBpbmZvcm1hdGlvbiB0byBhaWQgaW4gYSBzZWFyY2g6CmBgYHtyIFNlc3Npb24gaW5mbywgZXZhbCA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CnNlc3Npb25JbmZvKCkKYGBgCgoKSGlnaGx5IHJlY29tbWVuZCB1c2luZyByZXNvdXJjZXMgbGlrZSBbQmlvY29uZHVjdG9yIFN1cHBvcnRdKGh0dHBzOi8vc3VwcG9ydC5iaW9jb25kdWN0b3Iub3JnLyksIFtCaW9zdGFyc10oaHR0cHM6Ly93d3cuYmlvc3RhcnMub3JnLyksIGFuZCBbU3RhY2sgT3ZlcmZsb3ddKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zL3RhZ2dlZC9yKSwgaW5jbHVkaW5nIHRocmVhZHMgb24gc3BlY2lmaWMgcGFja2FnZXMgb3IgY29tbW9uIGJpb2luZm9ybWF0aWMgdGFza3MuCgpJIHBlcnNvbmFsbHkgdXNlIG9uZSBvciBtb3JlIG9mIHRoZXNlIHJlc291cmNlcyAqKmV2ZXJ5IGRheSoqLgoKMTB4IGFsc28gaGFzIGEgaGVscGZ1bCBbMTAgdGlwcyBmb3IgYmlvbG9naXN0cyBsZWFybmluZyAgYmlvaW5mb3JtYXRpY3NdKGh0dHBzOi8vd3d3LjEweGdlbm9taWNzLmNvbS9yZXNvdXJjZXMvYW5hbHlzaXMtZ3VpZGVzLzEwLXRpcHMtZm9yLWJpb2xvZ2lzdHMtbGVhcm5pbmctYmlvaW5mb3JtYXRpY3MpIGluY2x1ZGVkIGluIHRoZWlyIHJlc291cmNlcy4KCiMgU3VtbWFyeQoKSW4gdGhpcyBzZWN0aW9uLCB3ZToKCiogU2V0IHVwIG91ciBjb21wdXRlIGVudmlyb25tZW50CiogTGVhcm5lZCBhYm91dCBERVNlcTIKKiBMb2FkZWQgb3VyIHJhdyBjb3VudCB0YWJsZXMgKGlucHV0IGZpbGUgMSkKCk5vdyB0aGF0IHdlIGhhdmUgb3VyIGNvdW50IGRhdGEgcHJvY2Vzc2VkLCB3ZSBjYW4gbW92ZSBvbiB0byAidW5ibGluZGluZyIgb3VyIGRhdGEsIGFzIHRoZSBzYW1wbGUgbmFtZXMgYXJlIHVuaXF1ZSBpZGVudGlmaWVycyBnZW5lcmF0ZWQgYnkgYSBzZXF1ZW5jaW5nIGNlbnRlciBhbmQgbm90IHZlcnkgaW5mb3JtYXRpdmUgYXMgZmFyIGFzIG91ciBleHBlcmltZW50YWwgY29uZGl0aW9ucy4KCgotLS0KCiMgU291cmNlcwojIyBUcmFpbmluZyByZXNvdXJjZXMgdXNlZCB0byBkZXZlbG9wIG1hdGVyaWFscwoqIEhCQyBER0Ugc2V0dXA6IGh0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL0RHRV93b3Jrc2hvcC9sZXNzb25zLzAxX0RHRV9zZXR1cF9hbmRfb3ZlcnZpZXcuaHRtbCAgIAoqIEhCQyBDb3VudCBOb3JtYWxpemF0aW9uOiBodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9ER0Vfd29ya3Nob3AvbGVzc29ucy8wMl9ER0VfY291bnRfbm9ybWFsaXphdGlvbi5odG1sICAgCiogREVTZXEyIHN0YW5kYXJkIHZpZ25ldHRlOiBodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwgICAKKiBERVNlcTIgYmVnaW5uZXJzIHZpZ25ldHRlOiBodHRwczovL2Jpb2MuaXNtLmFjLmpwL3BhY2thZ2VzLzIuMTQvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL2JlZ2lubmVyLnBkZiAgIAoqIEJpb2NvbmR1Y3RvciBSTkEtc2VxIFdvcmtmbG93czogaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9oZWxwL2NvdXJzZS1tYXRlcmlhbHMvMjAxNS9MZWFybkJpb2NvbmR1Y3RvckZlYjIwMTUvQjAyLjFfUk5BU2VxLmh0bWwgICAKKiBDQ0RMIEdhc3RyaWMgY2FuY2VyIHRyYWluaW5nIG1hdGVyaWFsczogaHR0cHM6Ly9hbGV4c2xlbW9uYWRlLmdpdGh1Yi5pby90cmFpbmluZy1tb2R1bGVzL1JOQS1zZXEvMDMtZ2FzdHJpY19jYW5jZXJfZXhwbG9yYXRvcnkubmIuaHRtbAoqIENDREwgTmV1cm9ibGFzdG9tYSB0cmFpbmluZyBtYXRlcmlhbHM6IGh0dHBzOi8vYWxleHNsZW1vbmFkZS5naXRodWIuaW8vdHJhaW5pbmctbW9kdWxlcy9STkEtc2VxLzA1LW5iX2NlbGxfbGluZV9ERVNlcTIubmIuaHRtbAoKCmBgYHtyIFdyaXRlT3V0LlJEYXRhLCBldmFsPVRSVUUsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiNIaWRkZW4gY29kZSBibG9jayB0byB3cml0ZSBvdXQgZGF0YSBmb3Iga25pdHRpbmcKCmRpci5jcmVhdGUoInJkYXRhIiwgc2hvd1dhcm5pbmdzPUZBTFNFKQpzYXZlLmltYWdlKGZpbGUgPSAicmRhdGEvUnVubmluZ0RhdGEuUkRhdGEiKQojSG93IHRvIGxvYWQgZm9yIG5leHQgc2VnbWVudAojbG9hZCgicmRhdGEvUnVubmluZ0RhdGEuUkRhdGEiKQpgYGAKCi0tLQoKVGhlc2UgbWF0ZXJpYWxzIGhhdmUgYmVlbiBhZGFwdGVkIGFuZCBleHRlbmRlZCBmcm9tIG1hdGVyaWFscyBsaXN0ZWQgYWJvdmUuIFRoZXNlIGFyZSBvcGVuIGFjY2VzcyBtYXRlcmlhbHMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBbQ3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbiBsaWNlbnNlIChDQyBCWSA0LjApXShodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS80LjAvKSwgd2hpY2ggcGVybWl0cyB1bnJlc3RyaWN0ZWQgdXNlLCBkaXN0cmlidXRpb24sIGFuZCByZXByb2R1Y3Rpb24gaW4gYW55IG1lZGl1bSwgcHJvdmlkZWQgdGhlIG9yaWdpbmFsIGF1dGhvciBhbmQgc291cmNlIGFyZSBjcmVkaXRlZC4K