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.

We will be discussing Bulk RNA-seq only, although some of the methods and techniques will be applicable to single-cell RNA-seq. We are planning single-cell RNA-seq training in the future. In the meantime, the homepage for Seurat (a popular R package for single-cell analysis) has lots of useful tutorials.

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

If you want to know more about genome alignment and command-line tools we have an upcoming workshop. We also have a workshop on using the free online resource Galaxy for RNA-seq pre-processing

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.

A really useful resource for obtaining raw sequencing reads is sraexplorer. Given a GEO, ArrayExpress of SRA dataset name it will give you links to download the raw fastq files

Obtaining the metadata

We refer to metadata as the data that describes the biological and technical characteristics of the samples we have sequenced. Examples of variables recorded in the metadata might include.

  • tumour / normal status
  • cell line
  • age
  • gender
  • date of collection
  • litter

We include the sample groups that we want to compare, and any potential confounding factors that we might need to address as part of our quality assessment. The metadata is stored in a spreadsheet and typically entered by-hand. When creating such data we should be mindful of some best-practice guidelines that will make our data easier to read into R.

See here for a round-up of common errors to be avoiding when creating spreadsheets

The sampleInfo.txt in the meta_data folder contains basic information about the samples that we will need for the analysis today. This includes the ID for the sample from SRA, an ID assigned by the researcher, and the cell type and developmental stage for each sample.

# Read the sample information into R
library(readr)
sampleinfo <- read_csv("meta_data/sampleInfo.csv")
View(sampleinfo)
sampleinfo

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

If running this code on your own data, you may need to adjust the code to create quant_files accordingly.

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 DESeq2to 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)

See here for a nice explanation of 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_textif 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:-

Correcting the sample information

The person creating the sample sheet has been inconsistent about the way that values of Treated have been entered into the metadata. Such errors can be annoying when labeling plots, but have more serious consequences when attempting to fit statistical models to the data.

Here are a set of commands to create an updated sample sheet using the stringr package (part of tidyverse). We write a new file rather than over-writing the existing one.

library(stringr)
library(dplyr)
sampleinfo %>% 
mutate(condition = str_to_upper(condition)) %>% 
write_tsv(file="meta_data/sampleInfo_corrected.txt")

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