Workshop Overview
High-throughput sequencing is now established as a standard technique
for many functional genomics studies; allowing the researcher to compare
and contrast the transcriptomes of many individuals to obtain biological
insight. A high-volume of data are generated from these experimental
techniques and thus require robust and reproducible tools to be employed
in the analysis.
In this workshop, you will be learning how to analyse RNA-seq count
data, using R. This will include reading the data into R, quality
control and performing differential expression analysis and gene set
testing, with a focus on the well-respected DESEq2 analysis workflow.
You will learn how to generate common plots for analysis and
visualisation of gene expression data, such as boxplots and
heatmaps.
Learning outcomes
After this workshop the successful learner will have a practical
understanding of:
- Exploring RNA-seq count data and importing these data into R
- Normalisation strategies for RNA-seq counts
- Quality Assessment of counts
- Identifying outliers, batch effects and sample mix-ups
- Using the DESeq2 package to assess differential expression
- Construction and interpretation of common visualisations
- Using annotation packages to query biological databases
- Methodology behind gene set testing and enrichment analysis
Teaching Style
- Pre-written markdown and compiled HTML files
- links from the google doc
- Live-teaching sessions to create a new markdown to document the
analysis
- including “breakout sessions” for exercise and questions
Setup
If you are following these notes online independent of our of our
workshops, you will need to install some R packages before you start. To
do this, copy the following into the RStudio console and press enter
source("https://raw.githubusercontent.com/sheffield-bioinformatics-core/rnaseq-r-online/main/install_bioc_packages.R")
To check that everything worked, now copy and paste the following
command. It should print messages to the screen to say that all the
packages were installed
source("https://raw.githubusercontent.com/sheffield-bioinformatics-core/rnaseq-r-online/main/check_packages.R")
You will also need to download a zip file containing some example
RNA-seq data. The zip file also has R markdown files that you can follow
along with
Once you have downloaded the zip file, you will need to
extract the files to a location on your computer that you would
like to work from. We recommend that you create an RStudio
project from the directory that the files are extracted to.
File -> New Project ->
Existing Directory
Learning objectives - Session 1
- Exploring count data and importing these data into R
- Normalisation strategies for RNA-seq counts
- Quality Assessment of counts
- Identifying outliers, batch effects and sample mix-ups
Introduction
Measuring gene expression on a genome-wide scale has become common
practice over the last two decades or so, with microarrays predominantly
used pre-2008. With the advent of next generation sequencing technology
in 2008, an increasing number of scientists use this technology to
measure and understand changes in gene expression in often complex
systems. As sequencing costs have decreased, using RNA-Seq to
simultaneously measure the expression of tens of thousands of genes for
multiple samples has never been easier. The cost of these experiments
has now moved from generating the data to storing and analysing it.
There are many steps involved in analysing an RNA-Seq experiment.
Workflow image from Harvard Bioinformatics Core
Analysing an RNAseq experiment begins with sequencing reads. These
are large (typically several Gb) that contain information on the
sequences that have been generated for each biological sample; one fastq
(or pair of fastqs) for each sample. Each set of four lines describe one
sequence (called a “read”).
A typical RNA-seq experiment will have 10 - 30 million reads in a
fastq file, with each read about 100 bases long
@D0UW5ACXX120511:8:1204:6261:40047/1
AATGTTTATGTTCTTAAATTTTAGTTGTATATGTGAATCTTTGTAGTTTTTGCTAAAATACTAAGTAATTTATATAAAAGTGAGTTAAGAGATTTTTCTGA
+
CCCFFFFFHHHHHJJJJJIJJJJJIJJHIIJIJIJJIJJJIJJHIIHIJJJJJJBEGIHIJICGIDICFGIJJJIIJJGJ>F>GAGCGEEHEHHEEFFFD>
As the fastq files are large, we tend to analyse them using
command-line software and a computing cluster. The traditional
workflow for RNA-seq compares the sequences to a reference genome
to see which genomic region each read matches the best.
Again, this requires more memory than a typical laptop or desktop
machine so is performed on a remote computer with large memory. The
resulting file is called a bam and records the best genomic
match for each read. However, as we are interested in gene expression we
want to relate these mappings to the positions of genes.
A variety of different counting methods can determine how many reads
overlap each known gene region. These are know as the raw counts and are
the kind of data we will start with today.
Recent tools for RNA-seq analysis (e.g. salmon
,
kallisto
) do not require the time-consuming step of
whole-genome alignment to be performed, and can therefore produce
gene-level counts in a much faster time frame. They not require the
creation of large bam files, which is useful if constrained by file
space (e.g. if using Galaxy).
Although we will be following a workflow that uses the
DESeq2
package, an alternative workflow is available based
on the edgeR
and limma packages.
Example dataset
The data for this tutorial comes from the paper, Induction of
fibroblast senescence generates a non-fibrogenic myofibroblast phenotype
that differentially impacts on cancer prognosis..
Cancer associated fibroblasts characterized by an myofibroblastic
phenotype play a major role in the tumour microenvironment, being
associated with poor prognosis. We found that this cell population is
made in part by senescent fibroblasts in vivo. As senescent fibroblasts
and myofibroblasts have been shown to share similar tumour promoting
functions in vitro we compared the transcriptosomes of these two
fibroblast types and performed RNA-seq of human foetal foreskin
fibroblasts 2 (HFFF2) treated with 2ng/ml TGF-beta-1 to induce
myofibroblast differentiation or 10Gy gamma irradiation to induce
senescence. We isolated RNA 7 days upon this treatments changing the
medium 3 days before the RNA extraction.
The sequencing reads for this experiment were uploaded to ArrayExpress
and processed using salmon
. However, the workflow we will
describe can be applied to other sources of count data.
Reading in the count data
Output from salmon
Eventually we will be using the DESeq2 Bioconductor package for
differential expression analysis. The specific method of importing your
count data into DESeq2 depends on the workflow used to generate the
counts. The DESeq2
vignette gives many different use-cases. For instance, if you have
your counts in a single file (e.g. if following our Galaxy tutorials)
you can follow the example later in this tutorial.
Overview
After following our R introductory workshops (https://sbc.shef.ac.uk/r-online/part1.nb.html),
we should be familiar with reading files into R. As we will see, the
count data we want to import and text files with a simple structure.
However, an RNA-seq experiment may consist of several large files and
the techniques we have seen for reading files generally only read a
single file at a time. There are often Bioconductor packages or reading
input data more efficiently.
We are going to use the tximport
package to import our count data into R and collapse the data to the
gene level. This requires us to run a function in the following
form:-
txi <- tximport(files=..., type="salmon", tx2gene=...)
So we will need to define the files that we want to import and a
transcript mapping data frame. The transcript mapping takes the
form:-
| TXNAME | GENEID
1| ENST00000456328.2 | ENSG00000223972.5
2| ENST00000450305.2 | ENSG00000223972.5
3| ENST00000473358.1 | ENSG00000243485.5
4| ENST00000469289.1 | ENSG00000243485.5
5| ENST00000607096.1 | ENSG00000284332.1
6| ENST00000606857.1 | ENSG00000268020.3
tximport
is able to import counts produced by different
software, and different workflows are described for each in the tximport
vignette.
Identifying the files
The samples from this study have been quantified using
salmon
, which is beyond the scope of this workshop. Note
that the salmon analysis produced many other files (e.g. log files), but
we will only need the quant.sf.gz
files for analysis.
The function we are going to use to import the salmon files requires
a vector
comprising the paths to the files that are to be
imported. The code will be written assuming the following folder
structure:-
- The salmon files are found in a folder called
salmon_quant
- The output for each sample is in a separate folder; named according
to sample name
- The salmon file has the name
quant.sf.gz
(these have
been zipped to save space)
The files can be found using the in-built function
list.files
. (?list.files
for help on this
function). We can also name each item in the vector according to the
directory name. These names will be used eventually to name the columns
of our count matrices.
dirs <- list.files("salmon_quant/")
quant_files <- list.files("salmon_quant/",
pattern="quant.sf.gz",
recursive = TRUE,
full.names = TRUE)
names(quant_files) <- dirs
quant_files
Our differential expression analysis will be carried-out at the
gene-level, so we need to perform an additional level of
summarisation before we can proceed. Effectively we need to sum the
counts for all transcripts that belong to each gene. The
mappings from transcript to genes can be obtained by a
pre-built database (such as Ensembl). We have provided such data in a
csv file that we can read in (tx2gene.csv
). See the
Appendix if you want to know the details
tx2gene <- read_csv("tx2gene.csv")
head(tx2gene)
We can now import our dataset with the tximport
package.
library(tximport)
txi <- tximport(quant_files,
type="salmon",
tx2gene = tx2gene)
The resulting object is a “list” structure in R which
contains a number of components that we can access using a
$
operator
The raw counts can be found using txi$counts
. The names
of each column are the sample names. If we had not defined the
names
of the quant_files
vector the columns
would just have numbers which would not be useful for downstream
analysis.
head(txi$counts)
`
Quality control of the imported counts
We will be using the DESeq2
library to analyse this
dataset. Along with the counts and metadata, a design for the
experiment also needs to be specified. This will define how the
differential expression analysis is carried out, but can be changed at a
later stage so for now we will use Treated
as our factor of
interest.
Printing the contents of dds
to the screen gives details
of how the data are represented.
library(DESeq2)
dds <- DESeqDataSetFromTximport(txi,
colData = sampleinfo,
design = ~Treated)
dds
The object contains all the counts which can be retrieved using the
counts
function.
head(counts(dds))
Whereas colData
will display the meta data that has been
stored with the object.
colData(dds)
Individual columns from the metadata can also be accessed and printed
using the $
notation
dds$condition
dds$Treated
The metadata columns can also be re-assigned. This useful in this
case because we can see that condition
is not stored as a
factor, which could cause problems later on when we want to use
condition
in the differential expression.
dds$condition <- as.factor(dds$condition)
Visualising library sizes
We can look at a few different plots to check that the data is good
quality, and that the samples are behaving as we would expect. First, we
can check how many reads we have for each sample in the
DESeqDataSet
. The counts themselves are accessed using the
assay
function; giving a matrix of counts. The sum of a
particular column is therefore the total number of reads for that
sample.
sum(counts(dds)[,1])
A convenience function colSums
exists for calculating
the sum of each column in a matrix, returning a vector
as a
result.
colSums(counts(dds))
Exercise
- Use an appropriate function from
dplyr
to add a column
containing the number of reads for each sample to the
sampleinfo
data frame.
- Produce a bar plot to show the Millions of reads for each sample
(see below)
- EXTRA The following message appears when we run
tximport
:-
transcripts missing from tx2gene: 1
. Look back at the
column headings of the tx2gene
data frame and determine why
this might be. How can we write the code to read
tx2gene.csv
differently and avoid this message.
Visualising count distributions
We typically use a boxplot
to visualise difference the
distributions of the columns of a numeric data frame. Applying the
boxplot
function to the raw counts from our dataset reveals
something about the nature of the data; the distributions are dominated
by a few genes with very large counts. We are using the base
boxplot
function here because the count data are not in the
long data format required by ggplot2
.
boxplot(counts(dds))
We can use the vst
or rlog
function from
DESeq2
to compensate for the effect of different library
sizes and put the data on the log\(_2\)
scale. The effect is to remove the dependence of the variance on the
mean, particularly the high variance of the logarithm of count data when
the mean is low. For more details see the DESeq2
vignette
# Get log2 counts
vsd <- vst(dds,blind=TRUE)
# Check distributions of samples using boxplots
boxplot(assay(vsd), xlab="", ylab="Log2 counts per million",las=2,main="Normalised Distributions")
abline(h=median(assay(vsd)), col="blue")
Principal components Analysis (PCA)
The (Principal
Components Analysis) PCA plot, shows the samples in the 2D plane
spanned by their first two principal components. A PCA is an example of
an unsupervised analysis, where we don’t need to specify the groups. If
your experiment is well-controlled and has worked well, what we hope to
see is that the greatest sources of variation in the data correspond to
the treatments/groups we are interested in. It is also an incredibly
useful tool for quality control and checking for outliers.
DESeq2
has a convenient plotPCA
function
for making the PCA plot, which makes use of the ggplot2
graphics package.
plotPCA(vsd,intgroup="condition")
There is also an option to return the values used in the plot for
further exploration and customisation. Here we join the PCA results to
the meta-data (sampleinfo
) for plotting.
library(dplyr)
library(ggplot2)
plotPCA(vsd,intgroup="Treated",returnData = TRUE) %>%
dplyr::rename(Run = name) %>%
left_join(sampleinfo) %>%
ggplot(aes(x = PC1, y = PC2,col=group)) + geom_point()
Exercise
- Is the
plotPCA
plot based on all genes in the dataset?
How can we change how many genes are used for the PCA analysis? Does
this significantly change the plot? (HINT: check the documentation for
the plotPCA
function.)
- Verify that the samples are separated based on the type of treatment
applied
- What problems can you see with the metadata?
- Can you label the identify of each sample? Look for help on
geom_text
if you haven’t used it before
Note about batch effects
In our unsupervised analysis we should see that the main source of
variation is due to biological effects, and not technical variation such
as when the libraries were sequenced. If we do observe high technical
variation in our data, it is not a complete disaster provided that we
have designed our experiment properly. In particular the sva
Bioconductor package can correct for batch effects provided that
representatives of the groups of interest appear in each batch.
Alternatively, the batch or confounding factor may be incorporated into
the differential expression analysis.
Below is an example of a PCA that might potentially worrying:-
Count matrix input
Another common workflow is where the raw counts are presented in the
form of a single file with one column for each sample and one row for
each gene.
Such a file is provided in the raw_counts
folder.
count_file <- "raw_counts/raw_counts_matrix.tsv"
counts <- read.delim(count_file)
head(counts)
The function DESeqDatasetFromMatrix
can now be used to
create the DESeq2 object. From the help
(?DESeqDatasetFromMatrix
) we can see that it requires
similar arguments to before; a count object, sample information and a
design. However, an error is printed with the code below:-
dds_from_mat <- DESeqDataSetFromMatrix(counts,
colData = sampleinfo,
design = ~Treated)
Error in DESeqDataSetFromMatrix(counts, colData = sampleinfo, design = ~Treated) : ncol(countData) == nrow(colData) is not TRUE
The error message is telling using that the number of columns in our
count data is not the same as the number of rows in our sample
information. This is indeed the case.
ncol(counts)
nrow(sampleinfo)
A workaround is to modify our counts so that the gene names appear as
row names, and then remove the gene names column. We can use
the []
approach from base R to extract and remove
columns.
rownames(counts) <- counts[,1]
counts <- counts[,-1]
dds_from_mat <- DESeqDataSetFromMatrix(counts,
colData = sampleinfo,
design = ~Treated)
dds_from_mat
However, there is an simpler approach in this case. The function
includes a tidy
argument which can be set to
TRUE
in the situation where gene names appear as the first
column.
counts <- read.delim(count_file)
dds_from_mat <- DESeqDataSetFromMatrix(counts,
colData = sampleinfo,
design = ~Treated,
tidy=TRUE)
dds_from_mat
Exercise for next time
- Re-create the
DESeqDataset
object to include the
corrected sample information
- Re-run the plotPCA function on the new data and verify that the
sample groups now look correct
Appendix
Defining the transcript mapping
In order for tximport
to give gene-level
counts, we need to supply a data frame that can be used to associate
each transcript name with a gene identifier. It is important to
use a transcript file that corresponds to the name genome build as the
file used to count the transcripts.
We can check if the gtf
file exists in the directory we
expect by running the file.exists
function; returning
TRUE
or FALSE
gtf_file <- "Mus_musculus.GRCm38.91.chr.gtf.gz"
file.exists(gtf_file)
If required, we can download from the Ensembl FTP site.
download.file("ftp://ftp.ensembl.org/pub/release-91/gtf/mus_musculus/Mus_musculus.GRCm38.91.chr.gtf.gz",destfile = gtf_file)
Note on analysing your own data
If analysing your own data, you will have to locate the gtf file on
the Ensembl FTP site. If you enter
ftp://ftp.ensembl.org/pub/release-91/gtf
into a web browser
you will be able to navigate the site and find your organism of
interest. By right-clicking on the name of the gtf you will be able to
copy the URL and then paste into RStudio.
gtf_file <- "ensembl_ref/my_ref.gtf"
download.file(PASTE_LINK_FROM_ENSEMBL_HERE,destfile = gtf_file)
Creating a transcript database
The Bioconductor website provides many pre-built transcript databases
for some organisms (Human, Mouse, Rat etc) which provide transcript
definitions and allow users to query the locations of particular genes,
exons and other genomic features. You may find a pre-built package that
already has the transcript locations required to create the transcript
mapping file. Check out the annotation section of the Bioconductor
website - http://bioconductor.org/packages/release/BiocViews.html#___AnnotationData
and look for packages starting TxDb...
However, it is quite easy to build such a database if we have a
gtf
file using the GenomicFeatures
infrastructure.
## Could take a few minutes to run the makeTxDbFromGFF command
library(GenomicFeatures)
txdb <- makeTxDbFromGFF(gtf_file)
The database has a number of predefined “keys” and “columns” that
have to be specified when creating a query
keytypes(txdb)
columns(txdb)
Sometimes we would want to query the positions for a limited set of
selected genes (perhaps the results of a differential-expression
analysis), but in this case we want the gene names that correspond to
every transcript in the database. To get the names of all transcripts we
can use the keys
function. We then compose the query using
the select
function to return a data frame
k <- keys(txdb, keytype="TXNAME")
tx_map <- select(txdb, keys = k, columns="GENEID", keytype = "TXNAME")
head(tx_map)
Acknowledgement
Original Authors: Belinda Phipson, Anna Trigos, Matt Ritchie,
Maria Doyle, Harriet Dashnow, Charity Law, Stephane
Ballereau, Oscar Rueda, Ashley Sawle Based on the course RNAseq
analysis in R delivered on May 11/12th 2016 and modified by Cancer
Research Uk Cambridge Centre for the Functional
Genomics Autumn School 2017
LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgUk5BLXNlcSBkYXRhIGluIFIiCmF1dGhvcjogIk1vZHVsZSBDb29yZGluYXRvciBNYXJrIER1bm5pbmciCmRhdGU6ICdgciBmb3JtYXQoU3lzLnRpbWUoKSwgIkxhc3QgbW9kaWZpZWQ6ICVkICViICVZIilgJwpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICBjc3M6IHN0eWxlc2hlZXRzL3N0eWxlcy5jc3MKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgo8aW1nIHNyYz0iaW1hZ2VzL2xvZ28tc20ucG5nIiBzdHlsZT0icG9zaXRpb246YWJzb2x1dGU7dG9wOjQwcHg7cmlnaHQ6MTBweDsiIHdpZHRoPSIyMDAiIC8+CgoKYGBge3Iga25pdHJPcHRzLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFKQpgYGAKCiMgV29ya3Nob3AgT3ZlcnZpZXcKCkhpZ2gtdGhyb3VnaHB1dCBzZXF1ZW5jaW5nIGlzIG5vdyBlc3RhYmxpc2hlZCBhcyBhIHN0YW5kYXJkIHRlY2huaXF1ZSBmb3IgbWFueSBmdW5jdGlvbmFsIGdlbm9taWNzIHN0dWRpZXM7IGFsbG93aW5nIHRoZSByZXNlYXJjaGVyIHRvIGNvbXBhcmUgYW5kIGNvbnRyYXN0IHRoZSB0cmFuc2NyaXB0b21lcyBvZiBtYW55IGluZGl2aWR1YWxzIHRvIG9idGFpbiBiaW9sb2dpY2FsIGluc2lnaHQuIEEgaGlnaC12b2x1bWUgb2YgZGF0YSBhcmUgZ2VuZXJhdGVkIGZyb20gdGhlc2UgZXhwZXJpbWVudGFsIHRlY2huaXF1ZXMgYW5kIHRodXMgcmVxdWlyZSByb2J1c3QgYW5kIHJlcHJvZHVjaWJsZSB0b29scyB0byBiZSBlbXBsb3llZCBpbiB0aGUgYW5hbHlzaXMuCgpJbiB0aGlzIHdvcmtzaG9wLCB5b3Ugd2lsbCBiZSBsZWFybmluZyBob3cgdG8gYW5hbHlzZSBSTkEtc2VxIGNvdW50IGRhdGEsIHVzaW5nIFIuIFRoaXMgd2lsbCBpbmNsdWRlIHJlYWRpbmcgdGhlIGRhdGEgaW50byBSLCBxdWFsaXR5IGNvbnRyb2wgYW5kIHBlcmZvcm1pbmcgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgYW5kIGdlbmUgc2V0IHRlc3RpbmcsIHdpdGggYSBmb2N1cyBvbiB0aGUgd2VsbC1yZXNwZWN0ZWQgREVTRXEyIGFuYWx5c2lzIHdvcmtmbG93LiBZb3Ugd2lsbCBsZWFybiBob3cgdG8gZ2VuZXJhdGUgY29tbW9uIHBsb3RzIGZvciBhbmFseXNpcyBhbmQgdmlzdWFsaXNhdGlvbiBvZiBnZW5lIGV4cHJlc3Npb24gZGF0YSwgc3VjaCBhcyBib3hwbG90cyBhbmQgaGVhdG1hcHMuIAoKCjxkaXYgY2xhc3M9ImluZm9ybWF0aW9uIj4KV2Ugd2lsbCBiZSBkaXNjdXNzaW5nICoqQnVsayoqIFJOQS1zZXEgb25seSwgYWx0aG91Z2ggc29tZSBvZiB0aGUgbWV0aG9kcyBhbmQgdGVjaG5pcXVlcyB3aWxsIGJlIGFwcGxpY2FibGUgdG8gKnNpbmdsZS1jZWxsKiBSTkEtc2VxLiBXZSBhcmUgcGxhbm5pbmcgc2luZ2xlLWNlbGwgUk5BLXNlcSB0cmFpbmluZyBpbiB0aGUgZnV0dXJlLiBJbiB0aGUgbWVhbnRpbWUsIHRoZSBob21lcGFnZSBmb3IgU2V1cmF0IChhIHBvcHVsYXIgUiBwYWNrYWdlIGZvciBzaW5nbGUtY2VsbCBhbmFseXNpcykgaGFzIGxvdHMgb2YgdXNlZnVsIHR1dG9yaWFscy4KCi0gW0FuYWx5c2lzIG9mIFNpbmdsZS1jZWxsIFJOQS1zZXEgZGF0YSAtIEphbiAyMHRoIDIwMjNdKGh0dHBzOi8vc2JjLnNoZWYuYWMudWsvdHJhaW5pbmcvc2luZ2xlLWNlbGwtMjAyMy0wMS0zMC8pCi0gW1NldXJhdCBIb21lIHBhZ2VdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvKQoKPC9kaXY+CgojIyBMZWFybmluZyBvdXRjb21lcwoKQWZ0ZXIgdGhpcyB3b3Jrc2hvcCB0aGUgc3VjY2Vzc2Z1bCBsZWFybmVyIHdpbGwgaGF2ZSBhIHByYWN0aWNhbCB1bmRlcnN0YW5kaW5nIG9mOgoKLSBFeHBsb3JpbmcgUk5BLXNlcSBjb3VudCBkYXRhIGFuZCBpbXBvcnRpbmcgdGhlc2UgZGF0YSBpbnRvIFIKLSBOb3JtYWxpc2F0aW9uIHN0cmF0ZWdpZXMgZm9yIFJOQS1zZXEgY291bnRzCi0gUXVhbGl0eSBBc3Nlc3NtZW50IG9mIGNvdW50cwotIElkZW50aWZ5aW5nIG91dGxpZXJzLCBiYXRjaCBlZmZlY3RzIGFuZCBzYW1wbGUgbWl4LXVwcwotIFVzaW5nIHRoZSBERVNlcTIgcGFja2FnZSB0byBhc3Nlc3MgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24KLSBDb25zdHJ1Y3Rpb24gYW5kIGludGVycHJldGF0aW9uIG9mIGNvbW1vbiB2aXN1YWxpc2F0aW9ucwotIFVzaW5nIGFubm90YXRpb24gcGFja2FnZXMgdG8gcXVlcnkgYmlvbG9naWNhbCBkYXRhYmFzZXMKLSBNZXRob2RvbG9neSBiZWhpbmQgZ2VuZSBzZXQgdGVzdGluZyBhbmQgZW5yaWNobWVudCBhbmFseXNpcwoKIyMgVGVhY2hpbmcgU3R5bGUKCi0gUHJlLXdyaXR0ZW4gKm1hcmtkb3duKiBhbmQgY29tcGlsZWQgSFRNTCBmaWxlcwogICsgbGlua3MgZnJvbSB0aGUgZ29vZ2xlIGRvYwotIExpdmUtdGVhY2hpbmcgc2Vzc2lvbnMgdG8gY3JlYXRlIGEgbmV3IG1hcmtkb3duIHRvIGRvY3VtZW50IHRoZSBhbmFseXNpcwogICsgaW5jbHVkaW5nICJicmVha291dCBzZXNzaW9ucyIgZm9yIGV4ZXJjaXNlIGFuZCBxdWVzdGlvbnMKCiMjIFNldHVwCgpJZiB5b3UgYXJlIGZvbGxvd2luZyB0aGVzZSBub3RlcyBvbmxpbmUgaW5kZXBlbmRlbnQgb2Ygb3VyIG9mIG91ciB3b3Jrc2hvcHMsIHlvdSB3aWxsIG5lZWQgdG8gaW5zdGFsbCBzb21lIFIgcGFja2FnZXMgYmVmb3JlIHlvdSBzdGFydC4gVG8gZG8gdGhpcywgY29weSB0aGUgZm9sbG93aW5nIGludG8gdGhlIFJTdHVkaW8gY29uc29sZSBhbmQgcHJlc3MgZW50ZXIKCmBgYHtyfQpzb3VyY2UoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zaGVmZmllbGQtYmlvaW5mb3JtYXRpY3MtY29yZS9ybmFzZXEtci1vbmxpbmUvbWFpbi9pbnN0YWxsX2Jpb2NfcGFja2FnZXMuUiIpCmBgYAoKVG8gY2hlY2sgdGhhdCBldmVyeXRoaW5nIHdvcmtlZCwgbm93IGNvcHkgYW5kIHBhc3RlIHRoZSBmb2xsb3dpbmcgY29tbWFuZC4gSXQgc2hvdWxkIHByaW50IG1lc3NhZ2VzIHRvIHRoZSBzY3JlZW4gdG8gc2F5IHRoYXQgYWxsIHRoZSBwYWNrYWdlcyB3ZXJlIGluc3RhbGxlZAoKYGBge3J9CnNvdXJjZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3NoZWZmaWVsZC1iaW9pbmZvcm1hdGljcy1jb3JlL3JuYXNlcS1yLW9ubGluZS9tYWluL2NoZWNrX3BhY2thZ2VzLlIiKQoKYGBgCgpZb3Ugd2lsbCBhbHNvIG5lZWQgdG8gZG93bmxvYWQgYSB6aXAgZmlsZSBjb250YWluaW5nIHNvbWUgZXhhbXBsZSBSTkEtc2VxIGRhdGEuIFRoZSB6aXAgZmlsZSBhbHNvIGhhcyBSIG1hcmtkb3duIGZpbGVzIHRoYXQgeW91IGNhbiBmb2xsb3cgYWxvbmcgd2l0aAoKLSBbRGF0YSBEb3dubG9hZCBsaW5rXShodHRwczovL2RyaXZlLmdvb2dsZS5jb20vZmlsZS9kLzE3OHlaU2FHMXRUcjNha2VOMERRMmYxUHUyck1vSmtCVC92aWV3P3VzcD1zaGFyaW5nKQoKT25jZSB5b3UgaGF2ZSBkb3dubG9hZGVkIHRoZSB6aXAgZmlsZSwgeW91IHdpbGwgbmVlZCB0byAqZXh0cmFjdCogdGhlIGZpbGVzIHRvIGEgbG9jYXRpb24gb24geW91ciBjb21wdXRlciB0aGF0IHlvdSB3b3VsZCBsaWtlIHRvIHdvcmsgZnJvbS4gV2UgcmVjb21tZW5kIHRoYXQgeW91IGNyZWF0ZSBhbiAqKlJTdHVkaW8gcHJvamVjdCoqIGZyb20gdGhlIGRpcmVjdG9yeSB0aGF0IHRoZSBmaWxlcyBhcmUgZXh0cmFjdGVkIHRvLgoKKipGaWxlKiogLT4gKipOZXcgUHJvamVjdCoqIC0+ICoqRXhpc3RpbmcgRGlyZWN0b3J5KioKCiFbXShpbWFnZXMvbmV3X3Byb2oucG5nKQoKCiMgTGVhcm5pbmcgb2JqZWN0aXZlcyAtIFNlc3Npb24gMQoKLSBFeHBsb3JpbmcgY291bnQgZGF0YSBhbmQgaW1wb3J0aW5nIHRoZXNlIGRhdGEgaW50byBSCi0gTm9ybWFsaXNhdGlvbiBzdHJhdGVnaWVzIGZvciBSTkEtc2VxIGNvdW50cwotIFF1YWxpdHkgQXNzZXNzbWVudCBvZiBjb3VudHMKLSBJZGVudGlmeWluZyBvdXRsaWVycywgYmF0Y2ggZWZmZWN0cyBhbmQgc2FtcGxlIG1peC11cHMKCgojIEludHJvZHVjdGlvbgoKTWVhc3VyaW5nIGdlbmUgZXhwcmVzc2lvbiBvbiBhIGdlbm9tZS13aWRlIHNjYWxlIGhhcyBiZWNvbWUgY29tbW9uIHByYWN0aWNlIG92ZXIgdGhlIGxhc3QgdHdvIGRlY2FkZXMgb3Igc28sIHdpdGggbWljcm9hcnJheXMgcHJlZG9taW5hbnRseSB1c2VkIHByZS0yMDA4LiBXaXRoIHRoZSBhZHZlbnQgb2YgbmV4dCBnZW5lcmF0aW9uIHNlcXVlbmNpbmcgdGVjaG5vbG9neSBpbiAyMDA4LCBhbiBpbmNyZWFzaW5nIG51bWJlciBvZiBzY2llbnRpc3RzIHVzZSB0aGlzIHRlY2hub2xvZ3kgdG8gbWVhc3VyZSBhbmQgdW5kZXJzdGFuZCBjaGFuZ2VzIGluIGdlbmUgZXhwcmVzc2lvbiBpbiBvZnRlbiBjb21wbGV4IHN5c3RlbXMuIEFzIHNlcXVlbmNpbmcgY29zdHMgaGF2ZSBkZWNyZWFzZWQsIHVzaW5nIFJOQS1TZXEgdG8gc2ltdWx0YW5lb3VzbHkgbWVhc3VyZSB0aGUgZXhwcmVzc2lvbiBvZiB0ZW5zIG9mIHRob3VzYW5kcyBvZiBnZW5lcyBmb3IgbXVsdGlwbGUgc2FtcGxlcyBoYXMgbmV2ZXIgYmVlbiBlYXNpZXIuIFRoZSBjb3N0IG9mIHRoZXNlIGV4cGVyaW1lbnRzIGhhcyBub3cgbW92ZWQgZnJvbSBnZW5lcmF0aW5nIHRoZSBkYXRhIHRvIHN0b3JpbmcgYW5kIGFuYWx5c2luZyBpdC4KClRoZXJlIGFyZSBtYW55IHN0ZXBzIGludm9sdmVkIGluIGFuYWx5c2luZyBhbiBSTkEtU2VxIGV4cGVyaW1lbnQuIAoKIVtdKGh0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL0ludHJvLXRvLXJuYXNlcS1ocGMtZ3QvaW1nL2FsaWdubWVudGZyZWVfd29ya2Zsb3dfYXVnMjAxNy5wbmcpCgoKV29ya2Zsb3cgaW1hZ2UgZnJvbSBIYXJ2YXJkIEJpb2luZm9ybWF0aWNzIENvcmUKCkFuYWx5c2luZyBhbiBSTkFzZXEgZXhwZXJpbWVudCBiZWdpbnMgd2l0aCBzZXF1ZW5jaW5nIHJlYWRzLiBUaGVzZSBhcmUgbGFyZ2UgKHR5cGljYWxseSBzZXZlcmFsIEdiKSB0aGF0IGNvbnRhaW4gaW5mb3JtYXRpb24gb24gdGhlIHNlcXVlbmNlcyB0aGF0IGhhdmUgYmVlbiBnZW5lcmF0ZWQgZm9yIGVhY2ggYmlvbG9naWNhbCBzYW1wbGU7IG9uZSBmYXN0cSAob3IgcGFpciBvZiBmYXN0cXMpIGZvciBlYWNoIHNhbXBsZS4gRWFjaCBzZXQgb2YgZm91ciBsaW5lcyBkZXNjcmliZSBvbmUgc2VxdWVuY2UgKGNhbGxlZCBhICJyZWFkIikuCgpBIHR5cGljYWwgUk5BLXNlcSBleHBlcmltZW50IHdpbGwgaGF2ZSAxMCAtIDMwIG1pbGxpb24gcmVhZHMgaW4gYSBmYXN0cSBmaWxlLCB3aXRoIGVhY2ggcmVhZCBhYm91dCAxMDAgYmFzZXMgbG9uZwoKYGBgCkBEMFVXNUFDWFgxMjA1MTE6ODoxMjA0OjYyNjE6NDAwNDcvMQpBQVRHVFRUQVRHVFRDVFRBQUFUVFRUQUdUVEdUQVRBVEdUR0FBVENUVFRHVEFHVFRUVFRHQ1RBQUFBVEFDVEFBR1RBQVRUVEFUQVRBQUFBR1RHQUdUVEFBR0FHQVRUVFRUQ1RHQQorCkNDQ0ZGRkZGSEhISEhKSkpKSklKSkpKSklKSkhJSUpJSklKSklKSkpJSkpISUlISUpKSkpKSkJFR0lISUpJQ0dJRElDRkdJSkpKSUlKSkdKPkY+R0FHQ0dFRUhFSEhFRUZGRkQ+CmBgYAoKQXMgdGhlIGZhc3RxIGZpbGVzIGFyZSBsYXJnZSwgd2UgdGVuZCB0byBhbmFseXNlIHRoZW0gdXNpbmcgY29tbWFuZC1saW5lIHNvZnR3YXJlIGFuZCBhIGNvbXB1dGluZyBjbHVzdGVyLiBUaGUgKnRyYWRpdGlvbmFsIHdvcmtmbG93IGZvciBSTkEtc2VxKiBjb21wYXJlcyB0aGUgc2VxdWVuY2VzIHRvIGEgcmVmZXJlbmNlIGdlbm9tZSB0byBzZWUgd2hpY2ggZ2Vub21pYyByZWdpb24gZWFjaCByZWFkIG1hdGNoZXMgdGhlIGJlc3QuCgo8aW1nIHNyYz0iaHR0cHM6Ly90cmFpbmluZy5nYWxheHlwcm9qZWN0Lm9yZy90cmFpbmluZy1tYXRlcmlhbC90b3BpY3Mvc2VxdWVuY2UtYW5hbHlzaXMvaW1hZ2VzL21hcHBpbmcvbWFwcGluZy5wbmciLz4KCkFnYWluLCB0aGlzIHJlcXVpcmVzIG1vcmUgbWVtb3J5IHRoYW4gYSB0eXBpY2FsIGxhcHRvcCBvciBkZXNrdG9wIG1hY2hpbmUgc28gaXMgcGVyZm9ybWVkIG9uIGEgcmVtb3RlIGNvbXB1dGVyIHdpdGggbGFyZ2UgbWVtb3J5LiBUaGUgcmVzdWx0aW5nIGZpbGUgaXMgY2FsbGVkIGEgKmJhbSogYW5kIHJlY29yZHMgdGhlIGJlc3QgZ2Vub21pYyBtYXRjaCBmb3IgZWFjaCByZWFkLiBIb3dldmVyLCBhcyB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBnZW5lIGV4cHJlc3Npb24gd2Ugd2FudCB0byByZWxhdGUgdGhlc2UgbWFwcGluZ3MgdG8gdGhlIHBvc2l0aW9ucyBvZiBnZW5lcy4KCgpBIHZhcmlldHkgb2YgZGlmZmVyZW50IGNvdW50aW5nIG1ldGhvZHMgY2FuIGRldGVybWluZSBob3cgbWFueSByZWFkcyBvdmVybGFwIGVhY2gga25vd24gZ2VuZSByZWdpb24uIFRoZXNlIGFyZSBrbm93IGFzIHRoZSByYXcgY291bnRzIGFuZCBhcmUgdGhlIGtpbmQgb2YgZGF0YSB3ZSB3aWxsIHN0YXJ0IHdpdGggdG9kYXkuCgo8aW1nIHNyYz0iaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3NoZWZmaWVsZC1iaW9pbmZvcm1hdGljcy1jb3JlL3JuYXNlcS1iZWdpbm5lcnMvbWFpbi9tZWRpYS9odHNlcS5wbmciLz4KClJlY2VudCB0b29scyBmb3IgUk5BLXNlcSBhbmFseXNpcyAoZS5nLiBgc2FsbW9uYCwgYGthbGxpc3RvYCkgZG8gbm90IHJlcXVpcmUgdGhlIHRpbWUtY29uc3VtaW5nIHN0ZXAgb2Ygd2hvbGUtZ2Vub21lIGFsaWdubWVudCB0byBiZSBwZXJmb3JtZWQsIGFuZCBjYW4gdGhlcmVmb3JlIHByb2R1Y2UgZ2VuZS1sZXZlbCBjb3VudHMgaW4gYSBtdWNoIGZhc3RlciB0aW1lIGZyYW1lLiBUaGV5IG5vdCByZXF1aXJlIHRoZSBjcmVhdGlvbiBvZiBsYXJnZSBiYW0gZmlsZXMsIHdoaWNoIGlzIHVzZWZ1bCBpZiBjb25zdHJhaW5lZCBieSBmaWxlIHNwYWNlICAoZS5nLiBpZiB1c2luZyBHYWxheHkpLgoKPGRpdiBjbGFzcz0iaW5mb3JtYXRpb24iPgpJZiB5b3Ugd2FudCB0byBrbm93IG1vcmUgYWJvdXQgZ2Vub21lIGFsaWdubWVudCBhbmQgY29tbWFuZC1saW5lIHRvb2xzIHdlIGhhdmUgYW4gdXBjb21pbmcgd29ya3Nob3AuIFdlIGFsc28gaGF2ZSBhIHdvcmtzaG9wIG9uIHVzaW5nIHRoZSBmcmVlIG9ubGluZSByZXNvdXJjZSBHYWxheHkgZm9yIFJOQS1zZXEgcHJlLXByb2Nlc3NpbmcKCi0gW1JOQS1zZXEgcHJlLXByb2Nlc3NpbmcgaW4gR2FsYXh5XShodHRwczovL3NiYy5zaGVmLmFjLnVrL25nc19pbnRyb193b3Jrc2hvcC8wMy1ybmEtc2VxLm5iLmh0bWwpCi0gW1JOQS1zZXEgcHJlLXByb2Nlc3Npbmcgd2l0aCBjb21tYW5kLWxpbmVdKGh0dHBzOi8vc2JjLnNoZWYuYWMudWsvdHJhaW5pbmcvY29tbWFuZC1saW5lLTIwMjMtMDEtMTYvKQoKPC9kaXY+CgpBbHRob3VnaCB3ZSB3aWxsIGJlIGZvbGxvd2luZyBhIHdvcmtmbG93IHRoYXQgdXNlcyB0aGUgYERFU2VxMmAgcGFja2FnZSwgYW4gYWx0ZXJuYXRpdmUgd29ya2Zsb3cgaXMgYXZhaWxhYmxlIGJhc2VkIG9uIHRoZSBbZWRnZVIgYW5kIGxpbW1hIHBhY2thZ2VzXShodHRwczovL2Jpb2NvbmR1Y3Rvci5naXRodWIuaW8vQmlvY1dvcmtzaG9wcy9ybmEtc2VxLWFuYWx5c2lzLWlzLWVhc3ktYXMtMS0yLTMtd2l0aC1saW1tYS1nbGltbWEtYW5kLWVkZ2VyLmh0bWwpLgoKIyMjIEV4YW1wbGUgZGF0YXNldAoKVGhlIGRhdGEgZm9yIHRoaXMgdHV0b3JpYWwgY29tZXMgZnJvbSB0aGUgcGFwZXIsIFsqSW5kdWN0aW9uIG9mIGZpYnJvYmxhc3Qgc2VuZXNjZW5jZSBnZW5lcmF0ZXMgYSBub24tZmlicm9nZW5pYyBteW9maWJyb2JsYXN0IHBoZW5vdHlwZSB0aGF0IGRpZmZlcmVudGlhbGx5IGltcGFjdHMgb24gY2FuY2VyIHByb2dub3Npcy4qXShodHRwOi8vZXVyb3BlcG1jLm9yZy9hcnRpY2xlL01FRC8yNzk5Mjg1NikuIAoKPiBDYW5jZXIgYXNzb2NpYXRlZCBmaWJyb2JsYXN0cyBjaGFyYWN0ZXJpemVkIGJ5IGFuIG15b2ZpYnJvYmxhc3RpYyBwaGVub3R5cGUgcGxheSBhIG1ham9yIHJvbGUgaW4gdGhlIHR1bW91ciBtaWNyb2Vudmlyb25tZW50LCBiZWluZyBhc3NvY2lhdGVkIHdpdGggcG9vciBwcm9nbm9zaXMuIFdlIGZvdW5kIHRoYXQgdGhpcyBjZWxsIHBvcHVsYXRpb24gaXMgbWFkZSBpbiBwYXJ0IGJ5IHNlbmVzY2VudCBmaWJyb2JsYXN0cyBpbiB2aXZvLiBBcyBzZW5lc2NlbnQgZmlicm9ibGFzdHMgYW5kIG15b2ZpYnJvYmxhc3RzIGhhdmUgYmVlbiBzaG93biB0byBzaGFyZSBzaW1pbGFyIHR1bW91ciBwcm9tb3RpbmcgZnVuY3Rpb25zIGluIHZpdHJvIHdlIGNvbXBhcmVkIHRoZSB0cmFuc2NyaXB0b3NvbWVzIG9mIHRoZXNlIHR3byBmaWJyb2JsYXN0IHR5cGVzIGFuZCBwZXJmb3JtZWQgUk5BLXNlcSBvZiBodW1hbiBmb2V0YWwgZm9yZXNraW4gZmlicm9ibGFzdHMgMiAoSEZGRjIpIHRyZWF0ZWQgd2l0aCAybmcvbWwgVEdGLWJldGEtMSB0byBpbmR1Y2UgbXlvZmlicm9ibGFzdCBkaWZmZXJlbnRpYXRpb24gb3IgMTBHeSBnYW1tYSBpcnJhZGlhdGlvbiB0byBpbmR1Y2Ugc2VuZXNjZW5jZS4gV2UgaXNvbGF0ZWQgUk5BIDcgZGF5cyB1cG9uIHRoaXMgdHJlYXRtZW50cyBjaGFuZ2luZyB0aGUgbWVkaXVtIDMgZGF5cyBiZWZvcmUgdGhlIFJOQSBleHRyYWN0aW9uLgoKVGhlIHNlcXVlbmNpbmcgcmVhZHMgZm9yIHRoaXMgZXhwZXJpbWVudCB3ZXJlIHVwbG9hZGVkIHRvIFtBcnJheUV4cHJlc3NdKGh0dHBzOi8vd3d3LmViaS5hYy51ay9hcnJheWV4cHJlc3MvZXhwZXJpbWVudHMvRS1NVEFCLTMxMDEvKSBhbmQgcHJvY2Vzc2VkIHVzaW5nIGBzYWxtb25gLiBIb3dldmVyLCB0aGUgd29ya2Zsb3cgd2Ugd2lsbCBkZXNjcmliZSBjYW4gYmUgYXBwbGllZCB0byBvdGhlciBzb3VyY2VzIG9mIGNvdW50IGRhdGEuCgo8ZGl2IGNsYXNzPSJpbmZvcm1hdGlvbiI+CkEgcmVhbGx5IHVzZWZ1bCByZXNvdXJjZSBmb3Igb2J0YWluaW5nIHJhdyBzZXF1ZW5jaW5nIHJlYWRzIGlzIHNyYWV4cGxvcmVyLiBHaXZlbiBhIEdFTywgQXJyYXlFeHByZXNzIG9mIFNSQSBkYXRhc2V0IG5hbWUgaXQgd2lsbCBnaXZlIHlvdSBsaW5rcyB0byBkb3dubG9hZCB0aGUgcmF3IGZhc3RxIGZpbGVzCgotIFtTUkEgRXhwbG9yZXJdKGh0dHBzOi8vc3JhLWV4cGxvcmVyLmluZm8vKQoKPC9kaXY+CgoKIyBPYnRhaW5pbmcgdGhlIG1ldGFkYXRhCgpXZSByZWZlciB0byAqbWV0YWRhdGEqIGFzIHRoZSBkYXRhIHRoYXQgZGVzY3JpYmVzIHRoZSBiaW9sb2dpY2FsIGFuZCB0ZWNobmljYWwgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSBzYW1wbGVzIHdlIGhhdmUgc2VxdWVuY2VkLiBFeGFtcGxlcyBvZiB2YXJpYWJsZXMgcmVjb3JkZWQgaW4gdGhlIG1ldGFkYXRhIG1pZ2h0IGluY2x1ZGUuCgotIHR1bW91ciAvIG5vcm1hbCBzdGF0dXMKLSBjZWxsIGxpbmUKLSBhZ2UKLSBnZW5kZXIKLSBkYXRlIG9mIGNvbGxlY3Rpb24KLSBsaXR0ZXIKCldlIGluY2x1ZGUgdGhlIHNhbXBsZSBncm91cHMgdGhhdCB3ZSB3YW50IHRvIGNvbXBhcmUsIGFuZCBhbnkgcG90ZW50aWFsICpjb25mb3VuZGluZyBmYWN0b3JzKiB0aGF0IHdlIG1pZ2h0IG5lZWQgdG8gYWRkcmVzcyBhcyBwYXJ0IG9mIG91ciBxdWFsaXR5IGFzc2Vzc21lbnQuIFRoZSBtZXRhZGF0YSBpcyBzdG9yZWQgaW4gYSBzcHJlYWRzaGVldCBhbmQgdHlwaWNhbGx5IGVudGVyZWQgYnktaGFuZC4gV2hlbiBjcmVhdGluZyBzdWNoIGRhdGEgd2Ugc2hvdWxkIGJlIG1pbmRmdWwgb2Ygc29tZSBiZXN0LXByYWN0aWNlIGd1aWRlbGluZXMgdGhhdCB3aWxsIG1ha2Ugb3VyIGRhdGEgZWFzaWVyIHRvIHJlYWQgaW50byBSLgoKPGRpdiBjbGFzcz0iaW5mb3JtYXRpb24iPgpTZWUgaGVyZSBmb3IgYSByb3VuZC11cCBvZiBjb21tb24gZXJyb3JzIHRvIGJlIGF2b2lkaW5nIHdoZW4gY3JlYXRpbmcgc3ByZWFkc2hlZXRzCgotIFtEYXRhIENhcnBlbnRyeSBsZXNzb24gb24gc3ByZWFkc2hlZXQgZXJyb3JzXShodHRwczovL2RhdGFjYXJwZW50cnkub3JnL3NwcmVhZHNoZWV0LWVjb2xvZ3ktbGVzc29uLzAyLWNvbW1vbi1taXN0YWtlcykKPC9kaXY+CgpUaGUgYHNhbXBsZUluZm8udHh0YCBpbiB0aGUgYG1ldGFfZGF0YWAgZm9sZGVyIGNvbnRhaW5zIGJhc2ljIGluZm9ybWF0aW9uIGFib3V0IHRoZSBzYW1wbGVzIHRoYXQgd2Ugd2lsbCBuZWVkIGZvciB0aGUgYW5hbHlzaXMgdG9kYXkuIFRoaXMgaW5jbHVkZXMgdGhlIElEIGZvciB0aGUgc2FtcGxlIGZyb20gU1JBLCBhbiBJRCBhc3NpZ25lZCBieSB0aGUgcmVzZWFyY2hlciwgYW5kIHRoZSBjZWxsIHR5cGUgYW5kIGRldmVsb3BtZW50YWwgc3RhZ2UgZm9yIGVhY2ggc2FtcGxlLgoKYGBge3IgbG9hZFNhbXBsZUluZm99CiMgUmVhZCB0aGUgc2FtcGxlIGluZm9ybWF0aW9uIGludG8gUgpsaWJyYXJ5KHJlYWRyKQpzYW1wbGVpbmZvIDwtIHJlYWRfY3N2KCJtZXRhX2RhdGEvc2FtcGxlSW5mby5jc3YiKQpWaWV3KHNhbXBsZWluZm8pCnNhbXBsZWluZm8KYGBgCgoKCiMgUmVhZGluZyBpbiB0aGUgY291bnQgZGF0YQoKCiMjIE91dHB1dCBmcm9tIHNhbG1vbgoKRXZlbnR1YWxseSB3ZSB3aWxsIGJlIHVzaW5nIHRoZSBERVNlcTIgQmlvY29uZHVjdG9yIHBhY2thZ2UgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLiBUaGUgc3BlY2lmaWMgbWV0aG9kIG9mIGltcG9ydGluZyB5b3VyIGNvdW50IGRhdGEgaW50byBERVNlcTIgZGVwZW5kcyBvbiB0aGUgd29ya2Zsb3cgdXNlZCB0byBnZW5lcmF0ZSB0aGUgY291bnRzLiBUaGUgW0RFU2VxMiB2aWduZXR0ZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI2lucHV0LWRhdGEpIGdpdmVzIG1hbnkgZGlmZmVyZW50IHVzZS1jYXNlcy4gRm9yIGluc3RhbmNlLCBpZiB5b3UgaGF2ZSB5b3VyIGNvdW50cyBpbiBhIHNpbmdsZSBmaWxlIChlLmcuIGlmIGZvbGxvd2luZyBvdXIgR2FsYXh5IHR1dG9yaWFscykgeW91IGNhbiBmb2xsb3cgdGhlIGV4YW1wbGUgbGF0ZXIgaW4gdGhpcyB0dXRvcmlhbC4KCiMjIyBPdmVydmlldwoKQWZ0ZXIgZm9sbG93aW5nIG91ciBSIGludHJvZHVjdG9yeSB3b3Jrc2hvcHMgKFtodHRwczovL3NiYy5zaGVmLmFjLnVrL3Itb25saW5lL3BhcnQxLm5iLmh0bWxdKGh0dHBzOi8vc2JjLnNoZWYuYWMudWsvci1vbmxpbmUvcGFydDEubmIuaHRtbCkpLCB3ZSBzaG91bGQgYmUgZmFtaWxpYXIgd2l0aCByZWFkaW5nIGZpbGVzIGludG8gUi4gQXMgd2Ugd2lsbCBzZWUsIHRoZSBjb3VudCBkYXRhIHdlIHdhbnQgdG8gaW1wb3J0IGFuZCB0ZXh0IGZpbGVzIHdpdGggYSBzaW1wbGUgc3RydWN0dXJlLiBIb3dldmVyLCBhbiBSTkEtc2VxIGV4cGVyaW1lbnQgbWF5IGNvbnNpc3Qgb2Ygc2V2ZXJhbCBsYXJnZSBmaWxlcyBhbmQgdGhlIHRlY2huaXF1ZXMgd2UgaGF2ZSBzZWVuIGZvciByZWFkaW5nIGZpbGVzIGdlbmVyYWxseSBvbmx5IHJlYWQgYSBzaW5nbGUgZmlsZSBhdCBhIHRpbWUuIFRoZXJlIGFyZSBvZnRlbiBCaW9jb25kdWN0b3IgcGFja2FnZXMgb3IgcmVhZGluZyBpbnB1dCBkYXRhIG1vcmUgZWZmaWNpZW50bHkuCgpXZSBhcmUgZ29pbmcgdG8gdXNlIHRoZSBbYHR4aW1wb3J0YF0oaHR0cDovL2R4LmRvaS5vcmcvMTAuMTI2ODgvZjEwMDByZXNlYXJjaC43NTYzLjEpIHBhY2thZ2UgdG8gaW1wb3J0IG91ciBjb3VudCBkYXRhIGludG8gUiBhbmQgY29sbGFwc2UgdGhlIGRhdGEgdG8gdGhlICpnZW5lIGxldmVsKi4gVGhpcyByZXF1aXJlcyB1cyB0byBydW4gYSBmdW5jdGlvbiBpbiB0aGUgZm9sbG93aW5nIGZvcm06LQoKYGBge3IgZXZhbD1GQUxTRX0KdHhpIDwtIHR4aW1wb3J0KGZpbGVzPS4uLiwgdHlwZT0ic2FsbW9uIiwgdHgyZ2VuZT0uLi4pCmBgYAoKU28gd2Ugd2lsbCBuZWVkIHRvIGRlZmluZSB0aGUgZmlsZXMgdGhhdCB3ZSB3YW50IHRvIGltcG9ydCBhbmQgYSB0cmFuc2NyaXB0IG1hcHBpbmcgZGF0YSBmcmFtZS4gVGhlIHRyYW5zY3JpcHQgbWFwcGluZyB0YWtlcyB0aGUgZm9ybTotCgpgYGAKIHwgVFhOQU1FIHwgR0VORUlECjF8IEVOU1QwMDAwMDQ1NjMyOC4yIHwgIEVOU0cwMDAwMDIyMzk3Mi41CjJ8IEVOU1QwMDAwMDQ1MDMwNS4yIHwgRU5TRzAwMDAwMjIzOTcyLjUKM3wgRU5TVDAwMDAwNDczMzU4LjEgfCBFTlNHMDAwMDAyNDM0ODUuNQo0fCBFTlNUMDAwMDA0NjkyODkuMSB8IEVOU0cwMDAwMDI0MzQ4NS41CjV8IEVOU1QwMDAwMDYwNzA5Ni4xIHwgRU5TRzAwMDAwMjg0MzMyLjEKNnwgRU5TVDAwMDAwNjA2ODU3LjEgfCBFTlNHMDAwMDAyNjgwMjAuMwpgYGAKCmB0eGltcG9ydGAgaXMgYWJsZSB0byBpbXBvcnQgY291bnRzIHByb2R1Y2VkIGJ5IGRpZmZlcmVudCBzb2Z0d2FyZSwgYW5kIGRpZmZlcmVudCB3b3JrZmxvd3MgYXJlIGRlc2NyaWJlZCBmb3IgZWFjaCBpbiB0aGUgW3R4aW1wb3J0IHZpZ25ldHRlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvdHhpbXBvcnQvaW5zdC9kb2MvdHhpbXBvcnQuaHRtbCkuCgojIyMgSWRlbnRpZnlpbmcgdGhlIGZpbGVzCgpUaGUgc2FtcGxlcyBmcm9tIHRoaXMgc3R1ZHkgaGF2ZSBiZWVuICpxdWFudGlmaWVkKiB1c2luZyBgc2FsbW9uYCwgd2hpY2ggaXMgYmV5b25kIHRoZSBzY29wZSBvZiB0aGlzIHdvcmtzaG9wLiBOb3RlIHRoYXQgdGhlIHNhbG1vbiBhbmFseXNpcyBwcm9kdWNlZCBtYW55IG90aGVyIGZpbGVzIChlLmcuIGxvZyBmaWxlcyksIGJ1dCB3ZSB3aWxsIG9ubHkgbmVlZCB0aGUgYHF1YW50LnNmLmd6YCBmaWxlcyBmb3IgYW5hbHlzaXMuCgpUaGUgZnVuY3Rpb24gd2UgYXJlIGdvaW5nIHRvIHVzZSB0byBpbXBvcnQgdGhlIHNhbG1vbiBmaWxlcyByZXF1aXJlcyBhIGB2ZWN0b3JgIGNvbXByaXNpbmcgdGhlIHBhdGhzIHRvIHRoZSBmaWxlcyB0aGF0IGFyZSB0byBiZSBpbXBvcnRlZC4gVGhlIGNvZGUgd2lsbCBiZSB3cml0dGVuIGFzc3VtaW5nIHRoZSBmb2xsb3dpbmcgZm9sZGVyIHN0cnVjdHVyZTotCgohW10oaW1hZ2VzL3NhbG1vbl9kaXIucG5nKQoKLSBUaGUgc2FsbW9uIGZpbGVzIGFyZSBmb3VuZCBpbiBhIGZvbGRlciBjYWxsZWQgYHNhbG1vbl9xdWFudGAKLSBUaGUgb3V0cHV0IGZvciBlYWNoIHNhbXBsZSBpcyBpbiBhIHNlcGFyYXRlIGZvbGRlcjsgbmFtZWQgYWNjb3JkaW5nIHRvIHNhbXBsZSBuYW1lCi0gVGhlIHNhbG1vbiBmaWxlIGhhcyB0aGUgbmFtZSBgcXVhbnQuc2YuZ3pgICh0aGVzZSBoYXZlIGJlZW4gemlwcGVkIHRvIHNhdmUgc3BhY2UpCgpUaGUgZmlsZXMgY2FuIGJlIGZvdW5kIHVzaW5nIHRoZSBpbi1idWlsdCBmdW5jdGlvbiBgbGlzdC5maWxlc2AuIChgP2xpc3QuZmlsZXNgIGZvciBoZWxwIG9uIHRoaXMgZnVuY3Rpb24pLiBXZSBjYW4gYWxzbyBuYW1lIGVhY2ggaXRlbSBpbiB0aGUgdmVjdG9yIGFjY29yZGluZyB0byB0aGUgZGlyZWN0b3J5IG5hbWUuIFRoZXNlIG5hbWVzIHdpbGwgYmUgdXNlZCBldmVudHVhbGx5IHRvIG5hbWUgdGhlIGNvbHVtbnMgb2Ygb3VyIGNvdW50IG1hdHJpY2VzLgoKYGBge3J9CmRpcnMgPC0gbGlzdC5maWxlcygic2FsbW9uX3F1YW50LyIpCnF1YW50X2ZpbGVzIDwtIGxpc3QuZmlsZXMoInNhbG1vbl9xdWFudC8iLAogICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm49InF1YW50LnNmLmd6IiwKICAgICAgICAgICAgICAgICAgICAgICAgICByZWN1cnNpdmUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGwubmFtZXMgPSBUUlVFKQpuYW1lcyhxdWFudF9maWxlcykgPC0gZGlycwpxdWFudF9maWxlcwpgYGAKCjxkaXYgY2xhc3M9ImluZm9ybWF0aW9uIj4KSWYgcnVubmluZyB0aGlzIGNvZGUgb24geW91ciBvd24gZGF0YSwgeW91IG1heSBuZWVkIHRvIGFkanVzdCB0aGUgY29kZSB0byBjcmVhdGUgYHF1YW50X2ZpbGVzYCBhY2NvcmRpbmdseS4KPC9kaXY+CgoKT3VyIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHdpbGwgYmUgY2FycmllZC1vdXQgYXQgdGhlICpnZW5lLWxldmVsKiwgc28gd2UgbmVlZCB0byBwZXJmb3JtIGFuIGFkZGl0aW9uYWwgbGV2ZWwgb2Ygc3VtbWFyaXNhdGlvbiBiZWZvcmUgd2UgY2FuIHByb2NlZWQuIEVmZmVjdGl2ZWx5IHdlIG5lZWQgdG8gc3VtIHRoZSBjb3VudHMgZm9yIGFsbCB0cmFuc2NyaXB0cyB0aGF0IGJlbG9uZyB0byBlYWNoIGdlbmUuIFRoZSAqbWFwcGluZ3MqIGZyb20gdHJhbnNjcmlwdCB0byBnZW5lcyBjYW4gYmUgb2J0YWluZWQgYnkgYSBwcmUtYnVpbHQgZGF0YWJhc2UgKHN1Y2ggYXMgRW5zZW1ibCkuIFdlIGhhdmUgcHJvdmlkZWQgc3VjaCBkYXRhIGluIGEgY3N2IGZpbGUgdGhhdCB3ZSBjYW4gcmVhZCBpbiAoYHR4MmdlbmUuY3N2YCkuIFNlZSB0aGUgQXBwZW5kaXggaWYgeW91IHdhbnQgdG8ga25vdyB0aGUgZGV0YWlscwoKCmBgYHtyfQp0eDJnZW5lIDwtIHJlYWRfY3N2KCJ0eDJnZW5lLmNzdiIpCmhlYWQodHgyZ2VuZSkKYGBgCldlIGNhbiBub3cgaW1wb3J0IG91ciBkYXRhc2V0IHdpdGggdGhlIGB0eGltcG9ydGAgcGFja2FnZS4gCgpgYGB7cn0KbGlicmFyeSh0eGltcG9ydCkKdHhpIDwtIHR4aW1wb3J0KHF1YW50X2ZpbGVzLAogICAgICAgICAgICAgICAgdHlwZT0ic2FsbW9uIiwKICAgICAgICAgICAgICAgIHR4MmdlbmUgPSB0eDJnZW5lKQpgYGAKClRoZSByZXN1bHRpbmcgb2JqZWN0IGlzIGEgIipsaXN0KiIgc3RydWN0dXJlIGluIFIgd2hpY2ggY29udGFpbnMgYSBudW1iZXIgb2YgY29tcG9uZW50cyB0aGF0IHdlIGNhbiBhY2Nlc3MgdXNpbmcgYSBgJGAgb3BlcmF0b3IKClRoZSByYXcgY291bnRzIGNhbiBiZSBmb3VuZCB1c2luZyBgdHhpJGNvdW50c2AuIFRoZSBuYW1lcyBvZiBlYWNoIGNvbHVtbiBhcmUgdGhlIHNhbXBsZSBuYW1lcy4gSWYgd2UgaGFkIG5vdCBkZWZpbmVkIHRoZSBgbmFtZXNgIG9mIHRoZSBgcXVhbnRfZmlsZXNgIHZlY3RvciB0aGUgY29sdW1ucyB3b3VsZCBqdXN0IGhhdmUgbnVtYmVycyB3aGljaCB3b3VsZCBub3QgYmUgdXNlZnVsIGZvciBkb3duc3RyZWFtIGFuYWx5c2lzLgoKYGBge3J9CmhlYWQodHhpJGNvdW50cykKYGBgCmAKCiMgUXVhbGl0eSBjb250cm9sIG9mIHRoZSBpbXBvcnRlZCBjb3VudHMKCldlIHdpbGwgYmUgdXNpbmcgdGhlIGBERVNlcTJgIGxpYnJhcnkgdG8gYW5hbHlzZSB0aGlzIGRhdGFzZXQuIEFsb25nIHdpdGggdGhlIGNvdW50cyBhbmQgbWV0YWRhdGEsIGEgKmRlc2lnbiogZm9yIHRoZSBleHBlcmltZW50IGFsc28gbmVlZHMgdG8gYmUgc3BlY2lmaWVkLiBUaGlzIHdpbGwgZGVmaW5lIGhvdyB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgaXMgY2FycmllZCBvdXQsIGJ1dCBjYW4gYmUgY2hhbmdlZCBhdCBhIGxhdGVyIHN0YWdlIHNvIGZvciBub3cgd2Ugd2lsbCB1c2UgYFRyZWF0ZWRgIGFzIG91ciBmYWN0b3Igb2YgaW50ZXJlc3QuCgoKUHJpbnRpbmcgdGhlIGNvbnRlbnRzIG9mIGBkZHNgIHRvIHRoZSBzY3JlZW4gZ2l2ZXMgZGV0YWlscyBvZiBob3cgdGhlIGRhdGEgYXJlIHJlcHJlc2VudGVkLgoKYGBge3IgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShERVNlcTIpCmRkcyA8LSBERVNlcURhdGFTZXRGcm9tVHhpbXBvcnQodHhpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gc2FtcGxlaW5mbywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+VHJlYXRlZCkKZGRzCmBgYAoKClRoZSBvYmplY3QgY29udGFpbnMgYWxsIHRoZSBjb3VudHMgd2hpY2ggY2FuIGJlIHJldHJpZXZlZCB1c2luZyB0aGUgYGNvdW50c2AgZnVuY3Rpb24uIApgYGB7cn0KaGVhZChjb3VudHMoZGRzKSkKYGBgCgpXaGVyZWFzIGBjb2xEYXRhYCB3aWxsIGRpc3BsYXkgdGhlIG1ldGEgZGF0YSB0aGF0IGhhcyBiZWVuIHN0b3JlZCB3aXRoIHRoZSBvYmplY3QuCgpgYGB7cn0KY29sRGF0YShkZHMpCmBgYAoKSW5kaXZpZHVhbCBjb2x1bW5zIGZyb20gdGhlIG1ldGFkYXRhIGNhbiBhbHNvIGJlIGFjY2Vzc2VkIGFuZCBwcmludGVkIHVzaW5nIHRoZSBgJGAgbm90YXRpb24KCmBgYHtyfQpkZHMkY29uZGl0aW9uCmRkcyRUcmVhdGVkCmBgYAoKVGhlIG1ldGFkYXRhIGNvbHVtbnMgY2FuIGFsc28gYmUgcmUtYXNzaWduZWQuIFRoaXMgdXNlZnVsIGluIHRoaXMgY2FzZSBiZWNhdXNlIHdlIGNhbiBzZWUgdGhhdCBgY29uZGl0aW9uYCBpcyBub3Qgc3RvcmVkIGFzIGEgZmFjdG9yLCB3aGljaCBjb3VsZCBjYXVzZSBwcm9ibGVtcyBsYXRlciBvbiB3aGVuIHdlIHdhbnQgdG8gdXNlIGBjb25kaXRpb25gIGluIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4KCmBgYHtyfQpkZHMkY29uZGl0aW9uIDwtIGFzLmZhY3RvcihkZHMkY29uZGl0aW9uKQpgYGAKCgojIyMgVmlzdWFsaXNpbmcgbGlicmFyeSBzaXplcwoKV2UgY2FuIGxvb2sgYXQgYSBmZXcgZGlmZmVyZW50IHBsb3RzIHRvIGNoZWNrIHRoYXQgdGhlIGRhdGEgaXMgZ29vZCBxdWFsaXR5LCBhbmQgdGhhdCB0aGUgc2FtcGxlcyBhcmUgYmVoYXZpbmcgYXMgd2Ugd291bGQgZXhwZWN0LiBGaXJzdCwgd2UgY2FuIGNoZWNrIGhvdyBtYW55IHJlYWRzIHdlIGhhdmUgZm9yIGVhY2ggc2FtcGxlIGluIHRoZSBgREVTZXFEYXRhU2V0YC4gVGhlIGNvdW50cyB0aGVtc2VsdmVzIGFyZSBhY2Nlc3NlZCB1c2luZyB0aGUgYGFzc2F5YCBmdW5jdGlvbjsgZ2l2aW5nIGEgbWF0cml4IG9mIGNvdW50cy4gVGhlIHN1bSBvZiBhIHBhcnRpY3VsYXIgY29sdW1uIGlzIHRoZXJlZm9yZSB0aGUgdG90YWwgbnVtYmVyIG9mIHJlYWRzIGZvciB0aGF0IHNhbXBsZS4KCmBgYHtyfQpzdW0oY291bnRzKGRkcylbLDFdKQpgYGAKCkEgY29udmVuaWVuY2UgZnVuY3Rpb24gYGNvbFN1bXNgIGV4aXN0cyBmb3IgY2FsY3VsYXRpbmcgdGhlIHN1bSBvZiBlYWNoIGNvbHVtbiBpbiBhIG1hdHJpeCwgcmV0dXJuaW5nIGEgYHZlY3RvcmAgYXMgYSByZXN1bHQuCgpgYGB7ciBkZ2VMaWJyYXJ5U2l6ZXN9CmNvbFN1bXMoY291bnRzKGRkcykpCgpgYGAKCgojIyBFeGVyY2lzZQoKPGRpdiBjbGFzcz0iZXhlcmNpc2UiPgotIFVzZSBhbiBhcHByb3ByaWF0ZSBmdW5jdGlvbiBmcm9tIGBkcGx5cmAgdG8gYWRkIGEgY29sdW1uIGNvbnRhaW5pbmcgdGhlIG51bWJlciBvZiByZWFkcyBmb3IgZWFjaCBzYW1wbGUgdG8gdGhlIGBzYW1wbGVpbmZvYCBkYXRhIGZyYW1lLgotIFByb2R1Y2UgYSBiYXIgcGxvdCB0byBzaG93IHRoZSBNaWxsaW9ucyBvZiByZWFkcyBmb3IgZWFjaCBzYW1wbGUgKHNlZSBiZWxvdykKLSAqKkVYVFJBKiogVGhlIGZvbGxvd2luZyBtZXNzYWdlIGFwcGVhcnMgd2hlbiB3ZSBydW4gYHR4aW1wb3J0YDotIGB0cmFuc2NyaXB0cyBtaXNzaW5nIGZyb20gdHgyZ2VuZTogMWAuIExvb2sgYmFjayBhdCB0aGUgY29sdW1uIGhlYWRpbmdzIG9mIHRoZSBgdHgyZ2VuZWAgZGF0YSBmcmFtZSBhbmQgZGV0ZXJtaW5lIHdoeSB0aGlzIG1pZ2h0IGJlLiBIb3cgY2FuIHdlIHdyaXRlIHRoZSBjb2RlIHRvIHJlYWQgYHR4MmdlbmUuY3N2YCBkaWZmZXJlbnRseSBhbmQgYXZvaWQgdGhpcyBtZXNzYWdlLgo8L2Rpdj4KCiFbXShpbWFnZXMvbGliX3NpemUucG5nKQoKCiMjIyBWaXN1YWxpc2luZyBjb3VudCBkaXN0cmlidXRpb25zCgpXZSB0eXBpY2FsbHkgdXNlIGEgYGJveHBsb3RgIHRvIHZpc3VhbGlzZSBkaWZmZXJlbmNlIHRoZSBkaXN0cmlidXRpb25zIG9mIHRoZSBjb2x1bW5zIG9mIGEgbnVtZXJpYyBkYXRhIGZyYW1lLiBBcHBseWluZyB0aGUgYGJveHBsb3RgIGZ1bmN0aW9uIHRvIHRoZSByYXcgY291bnRzIGZyb20gb3VyIGRhdGFzZXQgcmV2ZWFscyBzb21ldGhpbmcgYWJvdXQgdGhlIG5hdHVyZSBvZiB0aGUgZGF0YTsgdGhlIGRpc3RyaWJ1dGlvbnMgYXJlIGRvbWluYXRlZCBieSBhIGZldyBnZW5lcyB3aXRoIHZlcnkgbGFyZ2UgY291bnRzLiBXZSBhcmUgdXNpbmcgdGhlIGJhc2UgYGJveHBsb3RgIGZ1bmN0aW9uIGhlcmUgYmVjYXVzZSB0aGUgY291bnQgZGF0YSBhcmUgbm90IGluIHRoZSAqbG9uZyogZGF0YSBmb3JtYXQgcmVxdWlyZWQgYnkgYGdncGxvdDJgLgoKYGBge3J9CmJveHBsb3QoY291bnRzKGRkcykpCmBgYAoKCldlIGNhbiB1c2UgdGhlIGB2c3RgIG9yIGBybG9nYCBmdW5jdGlvbiBmcm9tIGBERVNlcTJgdG8gY29tcGVuc2F0ZSBmb3IgdGhlIGVmZmVjdCBvZiBkaWZmZXJlbnQgbGlicmFyeSBzaXplcyBhbmQgcHV0IHRoZSBkYXRhIG9uIHRoZSBsb2ckXzIkIHNjYWxlLiBUaGUgZWZmZWN0IGlzIHRvIHJlbW92ZSB0aGUgZGVwZW5kZW5jZSBvZiB0aGUgdmFyaWFuY2Ugb24gdGhlIG1lYW4sIHBhcnRpY3VsYXJseSB0aGUgaGlnaCB2YXJpYW5jZSBvZiB0aGUgbG9nYXJpdGhtIG9mIGNvdW50IGRhdGEgd2hlbiB0aGUgbWVhbiBpcyBsb3cuIEZvciBtb3JlIGRldGFpbHMgc2VlIHRoZSBbREVTZXEyIHZpZ25ldHRlXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwjY291bnQtZGF0YS10cmFuc2Zvcm1hdGlvbnMpCgoKCmBgYHtyfQojIEdldCBsb2cyIGNvdW50cwp2c2QgPC0gdnN0KGRkcyxibGluZD1UUlVFKQojIENoZWNrIGRpc3RyaWJ1dGlvbnMgb2Ygc2FtcGxlcyB1c2luZyBib3hwbG90cwpib3hwbG90KGFzc2F5KHZzZCksIHhsYWI9IiIsIHlsYWI9IkxvZzIgY291bnRzIHBlciBtaWxsaW9uIixsYXM9MixtYWluPSJOb3JtYWxpc2VkIERpc3RyaWJ1dGlvbnMiKQoKYWJsaW5lKGg9bWVkaWFuKGFzc2F5KHZzZCkpLCBjb2w9ImJsdWUiKQpgYGAKCgoKIyMgUHJpbmNpcGFsIGNvbXBvbmVudHMgQW5hbHlzaXMgKFBDQSkgCgo8ZGl2IGNsYXNzPSJpbmZvcm1hdGlvbiI+ClNlZSBoZXJlIGZvciBhIG5pY2UgZXhwbGFuYXRpb24gb2YgUENBCgotIFtodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PTBKcDRnc2ZPTE1zXShodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PTBKcDRnc2ZPTE1zKQo8L2Rpdj4KClRoZSBbKFByaW5jaXBhbCBDb21wb25lbnRzIEFuYWx5c2lzKSBQQ0FdKGh0dHA6Ly9zZXRvc2EuaW8vZXYvcHJpbmNpcGFsLWNvbXBvbmVudC1hbmFseXNpcy8pIHBsb3QsIHNob3dzIHRoZSBzYW1wbGVzIGluIHRoZSAyRCBwbGFuZSBzcGFubmVkIGJ5IHRoZWlyIGZpcnN0IHR3byBwcmluY2lwYWwgY29tcG9uZW50cy4gQSBQQ0EgaXMgYW4gZXhhbXBsZSBvZiBhbiB1bnN1cGVydmlzZWQgYW5hbHlzaXMsIHdoZXJlIHdlIGRvbuKAmXQgbmVlZCB0byBzcGVjaWZ5IHRoZSBncm91cHMuIElmIHlvdXIgZXhwZXJpbWVudCBpcyB3ZWxsLWNvbnRyb2xsZWQgYW5kIGhhcyB3b3JrZWQgd2VsbCwgd2hhdCB3ZSBob3BlIHRvIHNlZSBpcyB0aGF0IHRoZSBncmVhdGVzdCBzb3VyY2VzIG9mIHZhcmlhdGlvbiBpbiB0aGUgZGF0YSBjb3JyZXNwb25kIHRvIHRoZSB0cmVhdG1lbnRzL2dyb3VwcyB3ZSBhcmUgaW50ZXJlc3RlZCBpbi4gSXQgaXMgYWxzbyBhbiBpbmNyZWRpYmx5IHVzZWZ1bCB0b29sIGZvciBxdWFsaXR5IGNvbnRyb2wgYW5kIGNoZWNraW5nIGZvciBvdXRsaWVycy4KCmBERVNlcTJgIGhhcyBhIGNvbnZlbmllbnQgYHBsb3RQQ0FgIGZ1bmN0aW9uIGZvciBtYWtpbmcgdGhlIFBDQSBwbG90LCB3aGljaCBtYWtlcyB1c2Ugb2YgdGhlIGBnZ3Bsb3QyYCBncmFwaGljcyBwYWNrYWdlLiAKCgpgYGB7cn0KcGxvdFBDQSh2c2QsaW50Z3JvdXA9ImNvbmRpdGlvbiIpCmBgYAoKVGhlcmUgaXMgYWxzbyBhbiBvcHRpb24gdG8gcmV0dXJuIHRoZSB2YWx1ZXMgdXNlZCBpbiB0aGUgcGxvdCBmb3IgZnVydGhlciBleHBsb3JhdGlvbiBhbmQgY3VzdG9taXNhdGlvbi4gSGVyZSB3ZSBqb2luIHRoZSBQQ0EgcmVzdWx0cyB0byB0aGUgbWV0YS1kYXRhIChgc2FtcGxlaW5mb2ApIGZvciBwbG90dGluZy4KCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCnBsb3RQQ0EodnNkLGludGdyb3VwPSJUcmVhdGVkIixyZXR1cm5EYXRhID0gVFJVRSkgJT4lIAogIGRwbHlyOjpyZW5hbWUoUnVuID0gbmFtZSkgJT4lIAogIGxlZnRfam9pbihzYW1wbGVpbmZvKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gUEMxLCB5ID0gUEMyLGNvbD1ncm91cCkpICsgZ2VvbV9wb2ludCgpCmBgYAoKIyMgRXhlcmNpc2UKCjxkaXYgY2xhc3M9ImV4ZXJjaXNlIj4KCi0gSXMgdGhlIGBwbG90UENBYCBwbG90IGJhc2VkIG9uIGFsbCBnZW5lcyBpbiB0aGUgZGF0YXNldD8gSG93IGNhbiB3ZSBjaGFuZ2UgaG93IG1hbnkgZ2VuZXMgYXJlIHVzZWQgZm9yIHRoZSBQQ0EgYW5hbHlzaXM/IERvZXMgdGhpcyBzaWduaWZpY2FudGx5IGNoYW5nZSB0aGUgcGxvdD8gKEhJTlQ6IGNoZWNrIHRoZSBkb2N1bWVudGF0aW9uIGZvciB0aGUgYHBsb3RQQ0FgIGZ1bmN0aW9uLikKLSBWZXJpZnkgdGhhdCB0aGUgc2FtcGxlcyBhcmUgc2VwYXJhdGVkIGJhc2VkIG9uIHRoZSB0eXBlIG9mIHRyZWF0bWVudCBhcHBsaWVkCi0gV2hhdCBwcm9ibGVtcyBjYW4geW91IHNlZSB3aXRoIHRoZSBtZXRhZGF0YT8KLSBDYW4geW91IGxhYmVsIHRoZSBpZGVudGlmeSBvZiBlYWNoIHNhbXBsZT8gTG9vayBmb3IgaGVscCBvbiBgZ2VvbV90ZXh0YGlmIHlvdSBoYXZlbid0IHVzZWQgaXQgYmVmb3JlCjwvZGl2PgoKIyMjIE5vdGUgYWJvdXQgYmF0Y2ggZWZmZWN0cwoKSW4gb3VyIHVuc3VwZXJ2aXNlZCBhbmFseXNpcyB3ZSBzaG91bGQgc2VlIHRoYXQgdGhlIG1haW4gc291cmNlIG9mIHZhcmlhdGlvbiBpcyBkdWUgdG8gYmlvbG9naWNhbCBlZmZlY3RzLCBhbmQgbm90IHRlY2huaWNhbCB2YXJpYXRpb24gc3VjaCBhcyB3aGVuIHRoZSBsaWJyYXJpZXMgd2VyZSBzZXF1ZW5jZWQuIElmIHdlIGRvIG9ic2VydmUgaGlnaCB0ZWNobmljYWwgdmFyaWF0aW9uIGluIG91ciBkYXRhLCBpdCBpcyBub3QgYSBjb21wbGV0ZSBkaXNhc3RlciBwcm92aWRlZCB0aGF0IHdlIGhhdmUgZGVzaWduZWQgb3VyIGV4cGVyaW1lbnQgcHJvcGVybHkuIEluIHBhcnRpY3VsYXIgdGhlIFtzdmEgQmlvY29uZHVjdG9yIHBhY2thZ2VdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL3N2YS9pbnN0L2RvYy9zdmEucGRmKSBjYW4gY29ycmVjdCBmb3IgYmF0Y2ggZWZmZWN0cyBwcm92aWRlZCB0aGF0IHJlcHJlc2VudGF0aXZlcyBvZiB0aGUgZ3JvdXBzIG9mIGludGVyZXN0IGFwcGVhciBpbiBlYWNoIGJhdGNoLiBBbHRlcm5hdGl2ZWx5LCB0aGUgYmF0Y2ggb3IgY29uZm91bmRpbmcgZmFjdG9yIG1heSBiZSBpbmNvcnBvcmF0ZWQgaW50byB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMuCgpCZWxvdyBpcyBhbiBleGFtcGxlIG9mIGEgUENBIHRoYXQgbWlnaHQgcG90ZW50aWFsbHkgd29ycnlpbmc6LQoKIVtdKGltYWdlcy9iYXRjaF9lZmZlY3QucG5nKQoKCiMjIyBDb3JyZWN0aW5nIHRoZSBzYW1wbGUgaW5mb3JtYXRpb24KClRoZSBwZXJzb24gY3JlYXRpbmcgdGhlIHNhbXBsZSBzaGVldCBoYXMgYmVlbiBpbmNvbnNpc3RlbnQgYWJvdXQgdGhlIHdheSB0aGF0IHZhbHVlcyBvZiBgVHJlYXRlZGAgaGF2ZSBiZWVuIGVudGVyZWQgaW50byB0aGUgbWV0YWRhdGEuIFN1Y2ggZXJyb3JzIGNhbiBiZSBhbm5veWluZyB3aGVuIGxhYmVsaW5nIHBsb3RzLCBidXQgaGF2ZSBtb3JlIHNlcmlvdXMgY29uc2VxdWVuY2VzIHdoZW4gYXR0ZW1wdGluZyB0byBmaXQgc3RhdGlzdGljYWwgbW9kZWxzIHRvIHRoZSBkYXRhLgoKSGVyZSBhcmUgYSBzZXQgb2YgY29tbWFuZHMgdG8gY3JlYXRlIGFuIHVwZGF0ZWQgc2FtcGxlIHNoZWV0IHVzaW5nIHRoZSBgc3RyaW5ncmAgcGFja2FnZSAocGFydCBvZiBgdGlkeXZlcnNlYCkuIFdlIHdyaXRlIGEgbmV3IGZpbGUgcmF0aGVyIHRoYW4gb3Zlci13cml0aW5nIHRoZSBleGlzdGluZyBvbmUuCgpgYGB7ciBjb3JyZWN0U2FtcGxlU2hlZXR9CmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShkcGx5cikKc2FtcGxlaW5mbyAlPiUgCm11dGF0ZShjb25kaXRpb24gPSBzdHJfdG9fdXBwZXIoY29uZGl0aW9uKSkgJT4lIAp3cml0ZV90c3YoZmlsZT0ibWV0YV9kYXRhL3NhbXBsZUluZm9fY29ycmVjdGVkLnR4dCIpCmBgYAoKCgojIENvdW50IG1hdHJpeCBpbnB1dAoKQW5vdGhlciBjb21tb24gd29ya2Zsb3cgaXMgd2hlcmUgdGhlIHJhdyBjb3VudHMgYXJlIHByZXNlbnRlZCBpbiB0aGUgZm9ybSBvZiBhIHNpbmdsZSBmaWxlIHdpdGggb25lIGNvbHVtbiBmb3IgZWFjaCBzYW1wbGUgYW5kIG9uZSByb3cgZm9yIGVhY2ggZ2VuZS4KClN1Y2ggYSBmaWxlIGlzIHByb3ZpZGVkIGluIHRoZSBgcmF3X2NvdW50c2AgZm9sZGVyLgoKYGBge3J9CmNvdW50X2ZpbGUgPC0gInJhd19jb3VudHMvcmF3X2NvdW50c19tYXRyaXgudHN2Igpjb3VudHMgPC0gcmVhZC5kZWxpbShjb3VudF9maWxlKQpoZWFkKGNvdW50cykKCmBgYAoKVGhlIGZ1bmN0aW9uIGBERVNlcURhdGFzZXRGcm9tTWF0cml4YCBjYW4gbm93IGJlIHVzZWQgdG8gY3JlYXRlIHRoZSBERVNlcTIgb2JqZWN0LiBGcm9tIHRoZSBoZWxwIChgP0RFU2VxRGF0YXNldEZyb21NYXRyaXhgKSB3ZSBjYW4gc2VlIHRoYXQgaXQgcmVxdWlyZXMgc2ltaWxhciBhcmd1bWVudHMgdG8gYmVmb3JlOyBhIGNvdW50IG9iamVjdCwgc2FtcGxlIGluZm9ybWF0aW9uIGFuZCBhIGRlc2lnbi4gSG93ZXZlciwgYW4gZXJyb3IgaXMgcHJpbnRlZCB3aXRoIHRoZSBjb2RlIGJlbG93Oi0KCmBgYHtyIGV2YWw9RkFMU0V9CmRkc19mcm9tX21hdCA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KGNvdW50cywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gc2FtcGxlaW5mbywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+VHJlYXRlZCkKYGBgCgpgYGAKRXJyb3IgaW4gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudHMsIGNvbERhdGEgPSBzYW1wbGVpbmZvLCBkZXNpZ24gPSB+VHJlYXRlZCkgOiBuY29sKGNvdW50RGF0YSkgPT0gbnJvdyhjb2xEYXRhKSBpcyBub3QgVFJVRQpgYGAKClRoZSBlcnJvciBtZXNzYWdlIGlzIHRlbGxpbmcgdXNpbmcgdGhhdCB0aGUgbnVtYmVyIG9mIGNvbHVtbnMgaW4gb3VyIGNvdW50IGRhdGEgaXMgbm90IHRoZSBzYW1lIGFzIHRoZSBudW1iZXIgb2Ygcm93cyBpbiBvdXIgc2FtcGxlIGluZm9ybWF0aW9uLiBUaGlzIGlzIGluZGVlZCB0aGUgY2FzZS4KCmBgYHtyfQpuY29sKGNvdW50cykKbnJvdyhzYW1wbGVpbmZvKQpgYGAKCkEgd29ya2Fyb3VuZCBpcyB0byBtb2RpZnkgb3VyIGNvdW50cyBzbyB0aGF0IHRoZSBnZW5lIG5hbWVzIGFwcGVhciBhcyAqcm93IG5hbWVzKiwgYW5kIHRoZW4gcmVtb3ZlIHRoZSBnZW5lIG5hbWVzIGNvbHVtbi4gV2UgY2FuIHVzZSB0aGUgYFtdYCBhcHByb2FjaCBmcm9tIGJhc2UgUiB0byBleHRyYWN0IGFuZCByZW1vdmUgY29sdW1ucy4KCmBgYHtyfQpyb3duYW1lcyhjb3VudHMpIDwtIGNvdW50c1ssMV0KY291bnRzIDwtIGNvdW50c1ssLTFdCgpkZHNfZnJvbV9tYXQgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudHMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IHNhbXBsZWluZm8sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduID0gflRyZWF0ZWQpCmRkc19mcm9tX21hdApgYGAKCkhvd2V2ZXIsIHRoZXJlIGlzIGFuIHNpbXBsZXIgYXBwcm9hY2ggaW4gdGhpcyBjYXNlLiBUaGUgZnVuY3Rpb24gaW5jbHVkZXMgYSBgdGlkeWAgYXJndW1lbnQgd2hpY2ggY2FuIGJlIHNldCB0byBgVFJVRWAgaW4gdGhlIHNpdHVhdGlvbiB3aGVyZSBnZW5lIG5hbWVzIGFwcGVhciBhcyB0aGUgZmlyc3QgY29sdW1uLgoKYGBge3J9CmNvdW50cyA8LSByZWFkLmRlbGltKGNvdW50X2ZpbGUpCmRkc19mcm9tX21hdCA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KGNvdW50cywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gc2FtcGxlaW5mbywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+VHJlYXRlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpZHk9VFJVRSkKZGRzX2Zyb21fbWF0CgpgYGAKCgoKIyBFeGVyY2lzZSBmb3IgbmV4dCB0aW1lCgo8ZGl2IGNsYXNzPSJleGVyY2lzZSI+CgotIFJlLWNyZWF0ZSB0aGUgYERFU2VxRGF0YXNldGAgb2JqZWN0IHRvIGluY2x1ZGUgdGhlIGNvcnJlY3RlZCBzYW1wbGUgaW5mb3JtYXRpb24KLSBSZS1ydW4gdGhlIHBsb3RQQ0EgZnVuY3Rpb24gb24gdGhlIG5ldyBkYXRhIGFuZCB2ZXJpZnkgdGhhdCB0aGUgc2FtcGxlIGdyb3VwcyBub3cgbG9vayBjb3JyZWN0Cgo8L2Rpdj4KCiMgQXBwZW5kaXgKCiMjIyBEZWZpbmluZyB0aGUgdHJhbnNjcmlwdCBtYXBwaW5nCgoKSW4gb3JkZXIgZm9yIGB0eGltcG9ydGAgdG8gZ2l2ZSAqZ2VuZS1sZXZlbCogY291bnRzLCB3ZSBuZWVkIHRvIHN1cHBseSBhIGRhdGEgZnJhbWUgdGhhdCBjYW4gYmUgdXNlZCB0byBhc3NvY2lhdGUgZWFjaCB0cmFuc2NyaXB0IG5hbWUgd2l0aCBhIGdlbmUgaWRlbnRpZmllci4gKipJdCBpcyBpbXBvcnRhbnQgdG8gdXNlIGEgdHJhbnNjcmlwdCBmaWxlIHRoYXQgY29ycmVzcG9uZHMgdG8gdGhlIG5hbWUgZ2Vub21lIGJ1aWxkIGFzIHRoZSBmaWxlIHVzZWQgdG8gY291bnQgdGhlIHRyYW5zY3JpcHRzKiouIAoKV2UgY2FuIGNoZWNrIGlmIHRoZSBgZ3RmYCBmaWxlIGV4aXN0cyBpbiB0aGUgZGlyZWN0b3J5IHdlIGV4cGVjdCBieSBydW5uaW5nIHRoZSBgZmlsZS5leGlzdHNgIGZ1bmN0aW9uOyByZXR1cm5pbmcgYFRSVUVgIG9yIGBGQUxTRWAKCmBgYHtyfQpndGZfZmlsZSA8LSAiTXVzX211c2N1bHVzLkdSQ20zOC45MS5jaHIuZ3RmLmd6IgpmaWxlLmV4aXN0cyhndGZfZmlsZSkKYGBgCgpJZiByZXF1aXJlZCwgd2UgY2FuIGRvd25sb2FkIGZyb20gdGhlIEVuc2VtYmwgRlRQIHNpdGUuIAoKCmBgYHtyIGV2YWw9RkFMU0V9IApkb3dubG9hZC5maWxlKCJmdHA6Ly9mdHAuZW5zZW1ibC5vcmcvcHViL3JlbGVhc2UtOTEvZ3RmL211c19tdXNjdWx1cy9NdXNfbXVzY3VsdXMuR1JDbTM4LjkxLmNoci5ndGYuZ3oiLGRlc3RmaWxlID0gZ3RmX2ZpbGUpCgpgYGAKCgojIyMgTm90ZSBvbiBhbmFseXNpbmcgeW91ciBvd24gZGF0YQoKIVtdKGltYWdlcy9kb3dubG9hZF9ndGYucG5nKQoKSWYgYW5hbHlzaW5nIHlvdXIgb3duIGRhdGEsIHlvdSB3aWxsIGhhdmUgdG8gbG9jYXRlIHRoZSBndGYgZmlsZSBvbiB0aGUgRW5zZW1ibCBGVFAgc2l0ZS4gSWYgeW91IGVudGVyIGBmdHA6Ly9mdHAuZW5zZW1ibC5vcmcvcHViL3JlbGVhc2UtOTEvZ3RmYCBpbnRvIGEgd2ViIGJyb3dzZXIgeW91IHdpbGwgYmUgYWJsZSB0byBuYXZpZ2F0ZSB0aGUgc2l0ZSBhbmQgZmluZCB5b3VyIG9yZ2FuaXNtIG9mIGludGVyZXN0LiBCeSByaWdodC1jbGlja2luZyBvbiB0aGUgbmFtZSBvZiB0aGUgZ3RmIHlvdSB3aWxsIGJlIGFibGUgdG8gY29weSB0aGUgVVJMIGFuZCB0aGVuIHBhc3RlIGludG8gUlN0dWRpby4KCmBgYHtyIGV2YWw9RkFMU0V9Cmd0Zl9maWxlIDwtICJlbnNlbWJsX3JlZi9teV9yZWYuZ3RmIgpkb3dubG9hZC5maWxlKFBBU1RFX0xJTktfRlJPTV9FTlNFTUJMX0hFUkUsZGVzdGZpbGUgPSBndGZfZmlsZSkKYGBgCgojIyMgQ3JlYXRpbmcgYSB0cmFuc2NyaXB0IGRhdGFiYXNlCgpUaGUgQmlvY29uZHVjdG9yIHdlYnNpdGUgcHJvdmlkZXMgbWFueSBwcmUtYnVpbHQgdHJhbnNjcmlwdCBkYXRhYmFzZXMgZm9yIHNvbWUgb3JnYW5pc21zIChIdW1hbiwgTW91c2UsIFJhdCBldGMpICB3aGljaCBwcm92aWRlIHRyYW5zY3JpcHQgZGVmaW5pdGlvbnMgYW5kIGFsbG93IHVzZXJzIHRvIHF1ZXJ5IHRoZSBsb2NhdGlvbnMgb2YgcGFydGljdWxhciBnZW5lcywgZXhvbnMgYW5kIG90aGVyIGdlbm9taWMgZmVhdHVyZXMuIFlvdSBtYXkgZmluZCBhIHByZS1idWlsdCBwYWNrYWdlIHRoYXQgYWxyZWFkeSBoYXMgdGhlIHRyYW5zY3JpcHQgbG9jYXRpb25zIHJlcXVpcmVkIHRvIGNyZWF0ZSB0aGUgdHJhbnNjcmlwdCBtYXBwaW5nIGZpbGUuIENoZWNrIG91dCB0aGUgYW5ub3RhdGlvbiBzZWN0aW9uIG9mIHRoZSBCaW9jb25kdWN0b3Igd2Vic2l0ZSAtIGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvQmlvY1ZpZXdzLmh0bWwjX19fQW5ub3RhdGlvbkRhdGEgYW5kIGxvb2sgZm9yIHBhY2thZ2VzIHN0YXJ0aW5nIGBUeERiLi4uYAoKSG93ZXZlciwgaXQgaXMgcXVpdGUgZWFzeSB0byBidWlsZCBzdWNoIGEgZGF0YWJhc2UgaWYgd2UgaGF2ZSBhIGBndGZgIGZpbGUgdXNpbmcgdGhlIGBHZW5vbWljRmVhdHVyZXNgIGluZnJhc3RydWN0dXJlLgoKYGBge3IgbWVzc2FnZT1GQUxTRSxldmFsPUZBTFNFfQojIyBDb3VsZCB0YWtlIGEgZmV3IG1pbnV0ZXMgdG8gcnVuIHRoZSBtYWtlVHhEYkZyb21HRkYgY29tbWFuZApsaWJyYXJ5KEdlbm9taWNGZWF0dXJlcykKdHhkYiA8LSBtYWtlVHhEYkZyb21HRkYoZ3RmX2ZpbGUpCmBgYAoKVGhlIGRhdGFiYXNlIGhhcyBhIG51bWJlciBvZiBwcmVkZWZpbmVkICJrZXlzIiBhbmQgImNvbHVtbnMiIHRoYXQgaGF2ZSB0byBiZSBzcGVjaWZpZWQgd2hlbiBjcmVhdGluZyBhIHF1ZXJ5CgpgYGB7cixldmFsPUZBTFNFfQprZXl0eXBlcyh0eGRiKQpgYGAKCmBgYHtyLGV2YWw9RkFMU0V9CmNvbHVtbnModHhkYikKYGBgCgpTb21ldGltZXMgd2Ugd291bGQgd2FudCB0byBxdWVyeSB0aGUgcG9zaXRpb25zIGZvciBhIGxpbWl0ZWQgc2V0IG9mIHNlbGVjdGVkIGdlbmVzIChwZXJoYXBzIHRoZSByZXN1bHRzIG9mIGEgZGlmZmVyZW50aWFsLWV4cHJlc3Npb24gYW5hbHlzaXMpLCBidXQgaW4gdGhpcyBjYXNlIHdlIHdhbnQgdGhlIGdlbmUgbmFtZXMgdGhhdCBjb3JyZXNwb25kIHRvIGV2ZXJ5IHRyYW5zY3JpcHQgaW4gdGhlIGRhdGFiYXNlLiBUbyBnZXQgdGhlIG5hbWVzIG9mIGFsbCB0cmFuc2NyaXB0cyB3ZSBjYW4gdXNlIHRoZSBga2V5c2AgZnVuY3Rpb24uIFdlIHRoZW4gY29tcG9zZSB0aGUgcXVlcnkgdXNpbmcgdGhlIGBzZWxlY3RgIGZ1bmN0aW9uIHRvIHJldHVybiBhIGRhdGEgZnJhbWUKCmBgYHtyLGV2YWw9RkFMU0V9CmsgPC0ga2V5cyh0eGRiLCBrZXl0eXBlPSJUWE5BTUUiKQp0eF9tYXAgPC0gc2VsZWN0KHR4ZGIsIGtleXMgPSBrLCBjb2x1bW5zPSJHRU5FSUQiLCBrZXl0eXBlID0gIlRYTkFNRSIpCmhlYWQodHhfbWFwKQpgYGAKCgojIEFja25vd2xlZGdlbWVudCAKKipPcmlnaW5hbCBBdXRob3JzOiBCZWxpbmRhIFBoaXBzb24sIEFubmEgVHJpZ29zLCBNYXR0IFJpdGNoaWUsIE1hcmlhIERveWxlLCBIYXJyaWV0IERhc2hub3csIENoYXJpdHkgTGF3KiosICoqU3RlcGhhbmUgQmFsbGVyZWF1LCBPc2NhciBSdWVkYSwgQXNobGV5IFNhd2xlKioKQmFzZWQgb24gdGhlIGNvdXJzZSBbUk5Bc2VxIGFuYWx5c2lzIGluIFJdKGh0dHA6Ly9jb21iaW5lLWF1c3RyYWxpYS5naXRodWIuaW8vMjAxNi0wNS0xMS1STkFzZXEvKSBkZWxpdmVyZWQgb24gTWF5IDExLzEydGggMjAxNiBhbmQgbW9kaWZpZWQgYnkgQ2FuY2VyIFJlc2VhcmNoIFVrIENhbWJyaWRnZSBDZW50cmUgZm9yIHRoZSBbRnVuY3Rpb25hbCBHZW5vbWljcyBBdXR1bW4gU2Nob29sIDIwMTddKGh0dHBzOi8vYmlvaW5mb3JtYXRpY3MtY29yZS1zaGFyZWQtdHJhaW5pbmcuZ2l0aHViLmlvL2NydWstYXV0dW1uLXNjaG9vbC0yMDE3LykKCgoKCg==