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

Overview

  • Reading in table of counts
  • Filtering lowly expressed genes
  • Quality control
  • Normalisation for composition bias

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. Analysing an RNAseq experiment begins with sequencing reads. Traditionally, these are aligned to a reference genome, then the number of reads mapped to each gene can be counted. More modern approaches such as salmon quantify transcripts directly and do not require genome aligment to have taken place. Either approach results in a table of counts, which is what we perform statistical analyses on in R. While mapping and counting are important and necessary tasks, this session will be starting from the count data and getting stuck into analysis.

We will be following a workflow that uses the DESeq2 package. An alternative and well-respected workflow is based on the edgeR and limma packages.

The TCGA datasets

The data for this tutorial comes from a Bioinformatics paper, Alternative preprocessing of RNA-Sequencing data in The Cancer Genome Atlas leads to improved analysis results where the authors re-analysed published TCGA data using an alternative pipeline. Crucially they made available raw counts that we will use to demonstrate full the RNA-seq processing pipeline from raw counts.

The raw counts for tumours and normals are available on GEO. A selection of prostate tumours with Gleason grade 6 and 9, and normals were selected to give a realistic dataset.

Importing the metadata and counts

Although RNA-seq datasets generate large amounts of data, once the preliminary steps of alignment and counting have been performed that data are small enough that we can comfortably handle them on a standard laptop or desktop using R.

The raw counts for this dataset can be found in the file "selected_prostate_tcga_raw.tsv". It is a good idea to keep your analysis project organised, with the raw and processed data usually found in separate folders. In our case, the counts are found in the folder raw_data. When importing the data we have to specify a path. If in doubt, the function file.exists can be used to see if we have specified a valid path. At this stage we don’t need to use any specialised packages to read the data into R, so read_tsv will do. The data can be inspected using head

library(tidyverse)
file.exists("raw_data/selected_prostate_tcga_raw.tsv")
[1] TRUE
raw <- read_tsv("raw_data/selected_prostate_tcga_raw.tsv")
head(raw)

In the data frame we have created each row represents a gene, and the columns correspond to different biological samples. The numbers represent the counts for each gene in every sample. i.e. the number of reads that overlap with that gene. For analysis we will need to convert into a matrix containing only numerical data and label each row with a gene name. The convenient pull function is used to extract a column from a tidy data frame.

genes <- pull(raw,X)
cts <- as.matrix(raw[,-1])
rownames(cts) <- genes

The count matrix we have generated is not particularly useful on it’s own. We need to collect data on which biological groups each sample belongs to, along with any other relevant metadata or clinical variables.

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 TCGA ID, a shortened patient ID and the gleason grade.

# Read the sample information into R
sampleinfo <- read.delim("meta_data/sampleInfo.tsv")
View(sampleinfo)

Quality control of the counts

We will be using the DESeq2 library to analyse this dataset. As part of the DESeq2 vignette you will see examples of importing count data from different sources. In all cases, raw count data are expected. We will use the DESeqDataSetFromMatrix function which is applicable to how the data have been processed.

The function requires the counts and sample information to be specified along with a design for the experiment. This will define how the differential expression analysis is carried out, but can be changed at a later stage so we will use Status for now as our factor of interest.

library(DESeq2)
  dds <- DESeqDataSetFromMatrix(countData = cts, 
                                colData = sampleinfo,
                                design = ~Status)

The object contains all the metadata for the experiment, along with the counts.

dds
class: DESeqDataSet 
dim: 23368 30 
metadata(1): version
assays(1): counts
rownames(23368): 1/2-SBSRNA4 A1BG ... ZZZ3 tAKR
rowData names(0):
colnames(30): TCGA.CH.5753.01A.11R.1580.07 TCGA.CH.5761.01A.11R.1580.07 ... TCGA.HC.8258.11A.01R.2263.07
  TCGA.HC.8260.11A.01R.2263.07
colData names(4): SampleName ID gleason_score Status
colData(dds)
DataFrame with 30 rows and 4 columns
                                               SampleName       ID gleason_score   Status
                                                 <factor> <factor>      <factor> <factor>
TCGA.CH.5753.01A.11R.1580.07 TCGA.CH.5753.01A.11R.1580.07     5753            G9   Tumour
TCGA.CH.5761.01A.11R.1580.07 TCGA.CH.5761.01A.11R.1580.07     5761            G9   Tumour
TCGA.EJ.5507.01A.01R.1580.07 TCGA.EJ.5507.01A.01R.1580.07     5507            g9   Tumour
TCGA.FC.A8O0.01A.41R.A37L.07 TCGA.FC.A8O0.01A.41R.A37L.07     A8O0            G6   Tumour
TCGA.G9.6371.01A.11R.1789.07 TCGA.G9.6371.01A.11R.1789.07     6371            G6   Tumour
...                                                   ...      ...           ...      ...
TCGA.G9.6499.11A.02R.1965.07 TCGA.G9.6499.11A.02R.1965.07       N6        Normal   Normal
TCGA.HC.7740.11A.01R.2118.07 TCGA.HC.7740.11A.01R.2118.07       N7        Normal   Normal
TCGA.HC.7745.11A.01R.2118.07 TCGA.HC.7745.11A.01R.2118.07       N8        Normal   Normal
TCGA.HC.8258.11A.01R.2263.07 TCGA.HC.8258.11A.01R.2263.07       N9        Normal   Normal
TCGA.HC.8260.11A.01R.2263.07 TCGA.HC.8260.11A.01R.2263.07      N10        Normal   Normal

Visualising library sizes

We can look at a few different plots to check that the data is good quality, and that the samples are 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(assay(dds)[,1])
[1] 47672339

A convenience function colSums exists for calculating the sum of each column in a matrix, returning a vector as a result.

colSums(assay(dds))
TCGA.CH.5753.01A.11R.1580.07 TCGA.CH.5761.01A.11R.1580.07 TCGA.EJ.5507.01A.01R.1580.07 TCGA.FC.A8O0.01A.41R.A37L.07 
                    47672339                     57156336                     58667203                     39769926 
TCGA.G9.6371.01A.11R.1789.07 TCGA.G9.6499.01A.12R.1965.07 TCGA.H9.A6BX.01A.31R.A311.07 TCGA.HC.7077.01A.11R.1965.07 
                    64394489                     58631571                     42888011                     62373213 
TCGA.HC.7081.01A.11R.1965.07 TCGA.HC.7209.01A.11R.2118.07 TCGA.HC.7748.01A.11R.2118.07 TCGA.HC.A631.01A.11R.A29R.07 
                    39636489                     52560684                     79219319                     41568448 
TCGA.HC.A632.01A.11R.A29R.07 TCGA.J4.A83K.01A.11R.A352.07 TCGA.KK.A6E8.01A.11R.A31N.07 TCGA.QU.A6IO.01A.11R.A31N.07 
                    39738414                     56396813                     44584711                     59283921 
TCGA.QU.A6IP.01A.11R.A31N.07 TCGA.VP.A87H.01A.11R.A352.07 TCGA.YL.A8SJ.01B.11R.A37L.07 TCGA.ZG.A8QX.01A.11R.A37L.07 
                    36879627                     43592402                     34421700                     50001153 
TCGA.CH.5761.11A.01R.1580.07 TCGA.EJ.7314.11A.01R.2118.07 TCGA.EJ.7327.11A.01R.2118.07 TCGA.G9.6356.11A.01R.1789.07 
                    42574845                     44524630                     64929111                     60168242 
TCGA.G9.6384.11A.01R.1858.07 TCGA.G9.6499.11A.02R.1965.07 TCGA.HC.7740.11A.01R.2118.07 TCGA.HC.7745.11A.01R.2118.07 
                    49228659                     59932457                     47425954                     51858824 
TCGA.HC.8258.11A.01R.2263.07 TCGA.HC.8260.11A.01R.2263.07 
                    73012571                     50961361 

Challenge 1

Produce a bar plot to show the Millions of reads for each sample See below for an example

Filtering non-expressed genes

The dataset is usually filtered at this stage to remove any genes that are not expressed. Although not strictly required for the DESeq2 differential expression algorithm, it can reduce the time and memory required to perform some of the analysis. Let’s say that for a gene to be “expressed” in a particular sample we need to see 5 or more counts

is_expressed <- assay(dds) >= 5

R is happy to think of logical values (TRUE or FALSE) as the integers 0 or 1. Therefore if we calculate the sum across a particular row it will give the number of samples that gene is expressed in.

sum(is_expressed[1,])
[1] 30
sum(is_expressed[2,])
[1] 30

In a similar manner to colSums, rowSums will give the sum of each row in a matrix and return the result as a vector.

df <- data.frame(Expressed = rowSums(is_expressed))
ggplot(df, aes(x=Expressed)) + geom_bar()

It seems that genes are either expressed in all samples, or not expressed at all. We will decide to keep genes that are expressed in at least 2 samples.

keep <- rowSums(assay(dds) >= 5) >= 5
table(keep)
keep
FALSE  TRUE 
 4413 18955 
dds <- dds[keep,]

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.

N.B we will use the boxplot function from “base” R as it provides a more convenient way of quickly creating boxplots from a matrix of numeric values.

boxplot(assay(dds))

A log\(_10\) (for example) transformation can be used to display the counts on a more convenient scale for visualisation

boxplot(log10(assay(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")
# Let's add a blue horizontal line that corresponds to the median logCPM
abline(h=median(assay(vsd)), col="blue")

Heatmap of the sample-to-sample distances

Another use of the transformed data is sample clustering. Here, we apply the dist function to the transpose of the transformed count matrix to get sample-to-sample distances.

sampleDists <- dist(t(assay(vsd)))

A heatmap of this distance matrix gives us an overview over similarities and dissimilarities between samples. By re-naming the rows and columns of the distance matrix we can make the plot easier to interpret.

library(RColorBrewer)
library(pheatmap)
sampleDistMatrix <- as.matrix(sampleDists)
rownames(sampleDistMatrix) <- colData(dds)$ID
colnames(sampleDistMatrix) <- colData(dds)$ID
colors <- colorRampPalette( rev(brewer.pal(9, "Blues")) )(255)
pheatmap(sampleDistMatrix,
         col=colors)

Principal component analysis (PCA)

Related to the distance matrix heatmap is the (Principal Components Analysis) PCA plot, which shows the samples in the 2D plane spanned by their first two principal components. A principle components analysis 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 are 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. Conveniently it will plot points according to a particular column in the metadata.

plotPCA(vsd,intgroup=c("Status"))

Challenge 2

  1. 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.)
  2. Change the intgroup parameter so that both gleason_score and Status are used for grouping. (See the documentation again)
  3. What potential problems can you spot in the data?

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.

Correcting the sample information

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

dplyr::count(sampleinfo,gleason_score)
library(stringr)
sampleinfo_corrected <- sampleinfo
sampleinfo_corrected <- mutate(sampleinfo, gleason_score = str_to_title(gleason_score)) %>%
  mutate(gleason_score = str_trim(gleason_score)) 
dplyr::count(sampleinfo_corrected,gleason_score)
write.table(sampleinfo_corrected, file="meta_data/sampleInfo_corrected.txt",sep="\t",row.names = FALSE)

Challenge 3

  1. Re-create the DESeqDataset object to include the corrected sample information
  2. Re-run the plotPCA function on the new data and verify that the sample groups now look correct

A note about “pipes” and ggplot2

The plotPCA function produces a ggplot2 plot (rather than “base” graphics) and we can also get the function to return the data used to create the plot by changing the returnData argument.

plot_data <- plotPCA(vsd,intgroup="Status",returnData=TRUE)
plot_data <- bind_cols(plot_data,sampleinfo_corrected)

With ggplot2 plot can be created by mapping various aesthetics of the plot (colour, shape, x- and y-coordinates) to columns in the data frame.

library(ggplot2)
ggplot(plot_data, aes(x = PC1,y=PC2, col=Status)) + geom_point()

Having the data in this form allows us to customise the plot in lots of ways

ggplot(plot_data, aes(x = PC1,y=PC2, col=Status,pch=gleason_score)) + geom_point(size=5)

Challenge 4

Create a PCA plot where the points are coloured by Gleason score, and making the size of the points relative to library size

LS0tCnRpdGxlOiAiUk5BLXNlcSBhbmFseXNpcyBpbiBSIgphdXRob3I6ICJNYXJrIER1bm5pbmciCmRhdGU6IEZlYnJ1YXJ5IDIwMjAKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCm1pbnV0ZXM6IDMwMApsYXlvdXQ6IHBhZ2UKc3VidGl0bGU6IFByZS1wcm9jZXNzc2luZyBSTkEtc2VxIGRhdGEKCi0tLQoKKipPcmlnaW5hbCBBdXRob3JzOiBCZWxpbmRhIFBoaXBzb24sIEFubmEgVHJpZ29zLCBNYXR0IFJpdGNoaWUsIE1hcmlhIERveWxlLCBIYXJyaWV0IERhc2hub3csIENoYXJpdHkgTGF3KiosICoqU3RlcGhhbmUgQmFsbGVyZWF1LCBPc2NhciBSdWVkYSwgQXNobGV5IFNhd2xlKioKQmFzZWQgb24gdGhlIGNvdXJzZSBbUk5Bc2VxIGFuYWx5c2lzIGluIFJdKGh0dHA6Ly9jb21iaW5lLWF1c3RyYWxpYS5naXRodWIuaW8vMjAxNi0wNS0xMS1STkFzZXEvKSBkZWxpdmVyZWQgb24gTWF5IDExLzEydGggMjAxNiBhbmQgbW9kaWZpZWQgYnkgQ2FuY2VyIFJlc2VhcmNoIFVrIENhbWJyaWRnZSBDZW50cmUgZm9yIHRoZSBbRnVuY3Rpb25hbCBHZW5vbWljcyBBdXR1bW4gU2Nob29sIDIwMTddKGh0dHBzOi8vYmlvaW5mb3JtYXRpY3MtY29yZS1zaGFyZWQtdHJhaW5pbmcuZ2l0aHViLmlvL2NydWstYXV0dW1uLXNjaG9vbC0yMDE3LykKCmBgYHtyIGtuaXRyT3B0cywgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCgoKIyMgUmVzb3VyY2VzIGFuZCBkYXRhIGZpbGVzCgpUaGlzIG1hdGVyaWFsIGhhcyBiZWVuIGNyZWF0ZWQgdXNpbmcgdGhlIGZvbGxvd2luZyByZXNvdXJjZXM6ICAKCgotIGh0dHA6Ly9tb25hc2hiaW9pbmZvcm1hdGljc3BsYXRmb3JtLmdpdGh1Yi5pby9STkFzZXEtREUtYW5hbHlzaXMtd2l0aC1SLzk5LVJOQXNlcV9ERV9hbmFseXNpc193aXRoX1IuaHRtbCAgCi0gaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sCi0gaHR0cHM6Ly9iaW9jb25kdWN0b3IuZ2l0aHViLmlvL0Jpb2NXb3Jrc2hvcHMvcm5hLXNlcS1kYXRhLWFuYWx5c2lzLXdpdGgtZGVzZXEyLmh0bWwKCgojIyBPdmVydmlldwoKKiBSZWFkaW5nIGluIHRhYmxlIG9mIGNvdW50cwoqIEZpbHRlcmluZyBsb3dseSBleHByZXNzZWQgZ2VuZXMKKiBRdWFsaXR5IGNvbnRyb2wKKiBOb3JtYWxpc2F0aW9uIGZvciBjb21wb3NpdGlvbiBiaWFzCgoKIyMgSW50cm9kdWN0aW9uCgpNZWFzdXJpbmcgZ2VuZSBleHByZXNzaW9uIG9uIGEgZ2Vub21lLXdpZGUgc2NhbGUgaGFzIGJlY29tZSBjb21tb24gcHJhY3RpY2Ugb3ZlciB0aGUgbGFzdCB0d28gZGVjYWRlcyBvciBzbywgd2l0aCBtaWNyb2FycmF5cyBwcmVkb21pbmFudGx5IHVzZWQgcHJlLTIwMDguIFdpdGggdGhlIGFkdmVudCBvZiBuZXh0IGdlbmVyYXRpb24gc2VxdWVuY2luZyB0ZWNobm9sb2d5IGluIDIwMDgsIGFuIGluY3JlYXNpbmcgbnVtYmVyIG9mIHNjaWVudGlzdHMgdXNlIHRoaXMgdGVjaG5vbG9neSB0byBtZWFzdXJlIGFuZCB1bmRlcnN0YW5kIGNoYW5nZXMgaW4gZ2VuZSBleHByZXNzaW9uIGluIG9mdGVuIGNvbXBsZXggc3lzdGVtcy4gQXMgc2VxdWVuY2luZyBjb3N0cyBoYXZlIGRlY3JlYXNlZCwgdXNpbmcgUk5BLVNlcSB0byBzaW11bHRhbmVvdXNseSBtZWFzdXJlIHRoZSBleHByZXNzaW9uIG9mIHRlbnMgb2YgdGhvdXNhbmRzIG9mIGdlbmVzIGZvciBtdWx0aXBsZSBzYW1wbGVzIGhhcyBuZXZlciBiZWVuIGVhc2llci4gVGhlIGNvc3Qgb2YgdGhlc2UgZXhwZXJpbWVudHMgaGFzIG5vdyBtb3ZlZCBmcm9tIGdlbmVyYXRpbmcgdGhlIGRhdGEgdG8gc3RvcmluZyBhbmQgYW5hbHlzaW5nIGl0LgoKVGhlcmUgYXJlIG1hbnkgc3RlcHMgaW52b2x2ZWQgaW4gYW5hbHlzaW5nIGFuIFJOQS1TZXEgZXhwZXJpbWVudC4gQW5hbHlzaW5nIGFuIFJOQXNlcSBleHBlcmltZW50IGJlZ2lucyB3aXRoIHNlcXVlbmNpbmcgcmVhZHMuIFRyYWRpdGlvbmFsbHksIHRoZXNlIGFyZSBhbGlnbmVkIHRvIGEgcmVmZXJlbmNlIGdlbm9tZSwgdGhlbiB0aGUgbnVtYmVyIG9mIHJlYWRzIG1hcHBlZCB0byBlYWNoIGdlbmUgY2FuIGJlIGNvdW50ZWQuIE1vcmUgbW9kZXJuIGFwcHJvYWNoZXMgc3VjaCBhcyBgc2FsbW9uYCBxdWFudGlmeSB0cmFuc2NyaXB0cyBkaXJlY3RseSBhbmQgZG8gbm90IHJlcXVpcmUgZ2Vub21lIGFsaWdtZW50ICB0byBoYXZlIHRha2VuIHBsYWNlLiBFaXRoZXIgYXBwcm9hY2ggcmVzdWx0cyBpbiBhIHRhYmxlIG9mIGNvdW50cywgd2hpY2ggaXMgd2hhdCB3ZSBwZXJmb3JtIHN0YXRpc3RpY2FsIGFuYWx5c2VzIG9uIGluIFIuIFdoaWxlIG1hcHBpbmcgYW5kIGNvdW50aW5nIGFyZSBpbXBvcnRhbnQgYW5kIG5lY2Vzc2FyeSB0YXNrcywgdGhpcyBzZXNzaW9uIHdpbGwgYmUgc3RhcnRpbmcgZnJvbSB0aGUgY291bnQgZGF0YSBhbmQgZ2V0dGluZyBzdHVjayBpbnRvIGFuYWx5c2lzLgoKV2Ugd2lsbCBiZSBmb2xsb3dpbmcgYSB3b3JrZmxvdyB0aGF0IHVzZXMgdGhlIGBERVNlcTJgIHBhY2thZ2UuIEFuIGFsdGVybmF0aXZlIGFuZCB3ZWxsLXJlc3BlY3RlZCB3b3JrZmxvdyBpcyBiYXNlZCBvbiB0aGUgW2VkZ2VSIGFuZCBsaW1tYSBwYWNrYWdlc10oaHR0cHM6Ly9iaW9jb25kdWN0b3IuZ2l0aHViLmlvL0Jpb2NXb3Jrc2hvcHMvcm5hLXNlcS1hbmFseXNpcy1pcy1lYXN5LWFzLTEtMi0zLXdpdGgtbGltbWEtZ2xpbW1hLWFuZC1lZGdlci5odG1sKS4KCiMjIyBUaGUgVENHQSBkYXRhc2V0cwoKVGhlIGRhdGEgZm9yIHRoaXMgdHV0b3JpYWwgY29tZXMgZnJvbSBhIEJpb2luZm9ybWF0aWNzIHBhcGVyLCBbKkFsdGVybmF0aXZlIHByZXByb2Nlc3Npbmcgb2YgUk5BLVNlcXVlbmNpbmcgZGF0YSBpbiBUaGUgQ2FuY2VyIEdlbm9tZSBBdGxhcyBsZWFkcyB0byBpbXByb3ZlZCBhbmFseXNpcyByZXN1bHRzKl0oaHR0cHM6Ly9hY2FkZW1pYy5vdXAuY29tL2Jpb2luZm9ybWF0aWNzL2FydGljbGUvMzEvMjIvMzY2Ni8yNDAxNDMpIHdoZXJlIHRoZSBhdXRob3JzIHJlLWFuYWx5c2VkIHB1Ymxpc2hlZCBUQ0dBIGRhdGEgdXNpbmcgYW4gYWx0ZXJuYXRpdmUgcGlwZWxpbmUuIENydWNpYWxseSB0aGV5IG1hZGUgYXZhaWxhYmxlIHJhdyBjb3VudHMgdGhhdCB3ZSB3aWxsIHVzZSB0byBkZW1vbnN0cmF0ZSBmdWxsIHRoZSBSTkEtc2VxIHByb2Nlc3NpbmcgcGlwZWxpbmUgZnJvbSByYXcgY291bnRzLgoKVGhlIHJhdyBjb3VudHMgZm9yIHR1bW91cnMgYW5kIG5vcm1hbHMgYXJlIGF2YWlsYWJsZSBvbiBbR0VPXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU0U2Mjk0NCkuIEEgc2VsZWN0aW9uIG9mIHByb3N0YXRlIHR1bW91cnMgd2l0aCBHbGVhc29uIGdyYWRlIDYgYW5kIDksIGFuZCBub3JtYWxzIHdlcmUgc2VsZWN0ZWQgdG8gZ2l2ZSBhIHJlYWxpc3RpYyBkYXRhc2V0LgoKIyMgSW1wb3J0aW5nIHRoZSBtZXRhZGF0YSBhbmQgY291bnRzCgpBbHRob3VnaCBSTkEtc2VxIGRhdGFzZXRzIGdlbmVyYXRlIGxhcmdlIGFtb3VudHMgb2YgZGF0YSwgb25jZSB0aGUgcHJlbGltaW5hcnkgc3RlcHMgb2YgKmFsaWdubWVudCogYW5kICpjb3VudGluZyogaGF2ZSBiZWVuIHBlcmZvcm1lZCB0aGF0IGRhdGEgYXJlIHNtYWxsIGVub3VnaCB0aGF0IHdlIGNhbiBjb21mb3J0YWJseSBoYW5kbGUgdGhlbSBvbiBhIHN0YW5kYXJkIGxhcHRvcCBvciBkZXNrdG9wIHVzaW5nIFIuIAoKVGhlIHJhdyBjb3VudHMgZm9yIHRoaXMgZGF0YXNldCBjYW4gYmUgZm91bmQgaW4gdGhlIGZpbGUgYCJzZWxlY3RlZF9wcm9zdGF0ZV90Y2dhX3Jhdy50c3YiYC4gSXQgaXMgYSBnb29kIGlkZWEgdG8ga2VlcCB5b3VyIGFuYWx5c2lzIHByb2plY3Qgb3JnYW5pc2VkLCB3aXRoIHRoZSByYXcgYW5kIHByb2Nlc3NlZCBkYXRhIHVzdWFsbHkgZm91bmQgaW4gc2VwYXJhdGUgZm9sZGVycy4gSW4gb3VyIGNhc2UsIHRoZSBjb3VudHMgYXJlIGZvdW5kIGluIHRoZSBmb2xkZXIgYHJhd19kYXRhYC4gV2hlbiBpbXBvcnRpbmcgdGhlIGRhdGEgd2UgaGF2ZSB0byBzcGVjaWZ5IGEgKnBhdGgqLiBJZiBpbiBkb3VidCwgdGhlIGZ1bmN0aW9uIGBmaWxlLmV4aXN0c2AgY2FuIGJlIHVzZWQgdG8gc2VlIGlmIHdlIGhhdmUgc3BlY2lmaWVkIGEgdmFsaWQgcGF0aC4gQXQgdGhpcyBzdGFnZSB3ZSBkb24ndCBuZWVkIHRvIHVzZSBhbnkgc3BlY2lhbGlzZWQgcGFja2FnZXMgdG8gcmVhZCB0aGUgZGF0YSBpbnRvIFIsIHNvIGByZWFkX3RzdmAgd2lsbCBkby4gIFRoZSBkYXRhIGNhbiBiZSBpbnNwZWN0ZWQgdXNpbmcgYGhlYWRgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKZmlsZS5leGlzdHMoInJhd19kYXRhL3NlbGVjdGVkX3Byb3N0YXRlX3RjZ2FfcmF3LnRzdiIpCnJhdyA8LSByZWFkX3RzdigicmF3X2RhdGEvc2VsZWN0ZWRfcHJvc3RhdGVfdGNnYV9yYXcudHN2IikKaGVhZChyYXcpCmBgYAoKSW4gdGhlIGRhdGEgZnJhbWUgd2UgaGF2ZSBjcmVhdGVkIGVhY2ggcm93IHJlcHJlc2VudHMgYSBnZW5lLCBhbmQgdGhlIGNvbHVtbnMgY29ycmVzcG9uZCB0byBkaWZmZXJlbnQgYmlvbG9naWNhbCBzYW1wbGVzLiBUaGUgbnVtYmVycyByZXByZXNlbnQgdGhlIGNvdW50cyBmb3IgZWFjaCBnZW5lIGluIGV2ZXJ5IHNhbXBsZS4gaS5lLiB0aGUgbnVtYmVyIG9mIHJlYWRzIHRoYXQgb3ZlcmxhcCB3aXRoIHRoYXQgZ2VuZS4gRm9yIGFuYWx5c2lzIHdlIHdpbGwgbmVlZCB0byBjb252ZXJ0IGludG8gYSAqbWF0cml4KiBjb250YWluaW5nIG9ubHkgbnVtZXJpY2FsIGRhdGEgYW5kIGxhYmVsIGVhY2ggcm93IHdpdGggYSBnZW5lIG5hbWUuIFRoZSBjb252ZW5pZW50IGBwdWxsYCBmdW5jdGlvbiBpcyB1c2VkIHRvIGV4dHJhY3QgYSBjb2x1bW4gZnJvbSBhIHRpZHkgZGF0YSBmcmFtZS4KCmBgYHtyfQpnZW5lcyA8LSBwdWxsKHJhdyxYKQpjdHMgPC0gYXMubWF0cml4KHJhd1ssLTFdKQpyb3duYW1lcyhjdHMpIDwtIGdlbmVzCgpgYGAKClRoZSBjb3VudCBtYXRyaXggd2UgaGF2ZSBnZW5lcmF0ZWQgaXMgbm90IHBhcnRpY3VsYXJseSB1c2VmdWwgb24gaXQncyBvd24uIFdlIG5lZWQgdG8gY29sbGVjdCBkYXRhIG9uIHdoaWNoIGJpb2xvZ2ljYWwgZ3JvdXBzIGVhY2ggc2FtcGxlIGJlbG9uZ3MgdG8sIGFsb25nIHdpdGggYW55IG90aGVyIHJlbGV2YW50ICptZXRhZGF0YSogb3IgY2xpbmljYWwgdmFyaWFibGVzLgoKVGhlIGBzYW1wbGVJbmZvLnR4dGAgaW4gdGhlIGBtZXRhX2RhdGFgIGZvbGRlciBjb250YWlucyBiYXNpYyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc2FtcGxlcyB0aGF0IHdlIHdpbGwgbmVlZCBmb3IgdGhlIGFuYWx5c2lzIHRvZGF5LiBUaGlzIGluY2x1ZGVzIHRoZSBUQ0dBIElELCBhIHNob3J0ZW5lZCBwYXRpZW50IElEIGFuZCB0aGUgZ2xlYXNvbiBncmFkZS4KCmBgYHtyIGxvYWRTYW1wbGVJbmZvfQojIFJlYWQgdGhlIHNhbXBsZSBpbmZvcm1hdGlvbiBpbnRvIFIKc2FtcGxlaW5mbyA8LSByZWFkLmRlbGltKCJtZXRhX2RhdGEvc2FtcGxlSW5mby50c3YiKQpWaWV3KHNhbXBsZWluZm8pCmBgYAoKCgoKIyMgUXVhbGl0eSBjb250cm9sIG9mIHRoZSBjb3VudHMKCldlIHdpbGwgYmUgdXNpbmcgdGhlIGBERVNlcTJgIGxpYnJhcnkgdG8gYW5hbHlzZSB0aGlzIGRhdGFzZXQuIEFzIHBhcnQgb2YgdGhlIFtERVNlcTIgdmlnbmV0dGVdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCkgeW91IHdpbGwgc2VlIGV4YW1wbGVzIG9mIGltcG9ydGluZyBjb3VudCBkYXRhIGZyb20gZGlmZmVyZW50IHNvdXJjZXMuIEluIGFsbCBjYXNlcywgW3JhdyBjb3VudCBkYXRhIGFyZSBleHBlY3RlZF0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI3doeS11bi1ub3JtYWxpemVkLWNvdW50cykuIFdlIHdpbGwgdXNlIHRoZSBgREVTZXFEYXRhU2V0RnJvbU1hdHJpeGAgZnVuY3Rpb24gd2hpY2ggaXMgYXBwbGljYWJsZSB0byBob3cgdGhlIGRhdGEgaGF2ZSBiZWVuIHByb2Nlc3NlZC4KClRoZSBmdW5jdGlvbiByZXF1aXJlcyB0aGUgY291bnRzIGFuZCBzYW1wbGUgaW5mb3JtYXRpb24gdG8gYmUgc3BlY2lmaWVkIGFsb25nIHdpdGggYSAqZGVzaWduKiBmb3IgdGhlIGV4cGVyaW1lbnQuIFRoaXMgd2lsbCBkZWZpbmUgaG93IHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBpcyBjYXJyaWVkIG91dCwgYnV0IGNhbiBiZSBjaGFuZ2VkIGF0IGEgbGF0ZXIgc3RhZ2Ugc28gd2Ugd2lsbCB1c2UgYFN0YXR1c2AgZm9yIG5vdyBhcyBvdXIgZmFjdG9yIG9mIGludGVyZXN0LgoKYGBge3IgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShERVNlcTIpCiAgZGRzIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhID0gY3RzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gc2FtcGxlaW5mbywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+U3RhdHVzKQpgYGAKClRoZSBvYmplY3QgY29udGFpbnMgYWxsIHRoZSBtZXRhZGF0YSBmb3IgdGhlIGV4cGVyaW1lbnQsIGFsb25nIHdpdGggdGhlIGNvdW50cy4gCgpgYGB7cn0KZGRzCmBgYAoKYGBge3J9CmNvbERhdGEoZGRzKQpgYGAKCgoKIyMjIFZpc3VhbGlzaW5nIGxpYnJhcnkgc2l6ZXMKCldlIGNhbiBsb29rIGF0IGEgZmV3IGRpZmZlcmVudCBwbG90cyB0byBjaGVjayB0aGF0IHRoZSBkYXRhIGlzIGdvb2QgcXVhbGl0eSwgYW5kIHRoYXQgdGhlIHNhbXBsZXMgYXJlIGFzIHdlIHdvdWxkIGV4cGVjdC4gRmlyc3QsIHdlIGNhbiBjaGVjayBob3cgbWFueSByZWFkcyB3ZSBoYXZlIGZvciBlYWNoIHNhbXBsZSBpbiB0aGUgYERFU2VxRGF0YVNldGAuIFRoZSBjb3VudHMgdGhlbXNlbHZlcyBhcmUgYWNjZXNzZWQgdXNpbmcgdGhlIGBhc3NheWAgZnVuY3Rpb247IGdpdmluZyBhIG1hdHJpeCBvZiBjb3VudHMuIFRoZSBzdW0gb2YgYSBwYXJ0aWN1bGFyIGNvbHVtbiBpcyB0aGVyZWZvcmUgdGhlIHRvdGFsIG51bWJlciBvZiByZWFkcyBmb3IgdGhhdCBzYW1wbGUuCgpgYGB7cn0Kc3VtKGFzc2F5KGRkcylbLDFdKQpgYGAKCkEgY29udmVuaWVuY2UgZnVuY3Rpb24gYGNvbFN1bXNgIGV4aXN0cyBmb3IgY2FsY3VsYXRpbmcgdGhlIHN1bSBvZiBlYWNoIGNvbHVtbiBpbiBhIG1hdHJpeCwgcmV0dXJuaW5nIGEgYHZlY3RvcmAgYXMgYSByZXN1bHQuCgpgYGB7ciBkZ2VMaWJyYXJ5U2l6ZXN9CmNvbFN1bXMoYXNzYXkoZGRzKSkKCmBgYAoKCj4gIyMgQ2hhbGxlbmdlIDEgey5jaGFsbGVuZ2V9Cj4KPiAgUHJvZHVjZSBhIGJhciBwbG90IHRvIHNob3cgdGhlIE1pbGxpb25zIG9mIHJlYWRzIGZvciBlYWNoIHNhbXBsZQo+ICBTZWUgYmVsb3cgZm9yIGFuIGV4YW1wbGUKCgohW10oaW1hZ2VzL2NoYWxsZW5nZTEucG5nKQoKCiMjIyBGaWx0ZXJpbmcgbm9uLWV4cHJlc3NlZCBnZW5lcwoKVGhlIGRhdGFzZXQgaXMgdXN1YWxseSBmaWx0ZXJlZCBhdCB0aGlzIHN0YWdlIHRvIHJlbW92ZSBhbnkgZ2VuZXMgdGhhdCBhcmUgbm90IGV4cHJlc3NlZC4gQWx0aG91Z2ggbm90IHN0cmljdGx5IHJlcXVpcmVkIGZvciB0aGUgREVTZXEyIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFsZ29yaXRobSwgaXQgY2FuIHJlZHVjZSB0aGUgdGltZSBhbmQgbWVtb3J5IHJlcXVpcmVkIHRvIHBlcmZvcm0gc29tZSBvZiB0aGUgYW5hbHlzaXMuIExldCdzIHNheSB0aGF0IGZvciBhIGdlbmUgdG8gYmUgImV4cHJlc3NlZCIgaW4gYSBwYXJ0aWN1bGFyIHNhbXBsZSB3ZSBuZWVkIHRvIHNlZSA1IG9yIG1vcmUgY291bnRzICAKCgpgYGB7cn0KaXNfZXhwcmVzc2VkIDwtIGFzc2F5KGRkcykgPj0gNQoKYGBgCgpSIGlzIGhhcHB5IHRvIHRoaW5rIG9mIGxvZ2ljYWwgdmFsdWVzIChgVFJVRWAgb3IgYEZBTFNFYCkgYXMgdGhlIGludGVnZXJzIGAwYCBvciBgMWAuIFRoZXJlZm9yZSBpZiB3ZSBjYWxjdWxhdGUgdGhlIHN1bSBhY3Jvc3MgYSBwYXJ0aWN1bGFyIHJvdyBpdCB3aWxsIGdpdmUgdGhlIG51bWJlciBvZiBzYW1wbGVzIHRoYXQgZ2VuZSBpcyBleHByZXNzZWQgaW4uCgpgYGB7cn0Kc3VtKGlzX2V4cHJlc3NlZFsxLF0pCnN1bShpc19leHByZXNzZWRbMixdKQoKYGBgCgpJbiBhIHNpbWlsYXIgbWFubmVyIHRvIGBjb2xTdW1zYCwgYHJvd1N1bXNgIHdpbGwgZ2l2ZSB0aGUgc3VtIG9mIGVhY2ggcm93IGluIGEgbWF0cml4IGFuZCByZXR1cm4gdGhlIHJlc3VsdCBhcyBhIGB2ZWN0b3JgLgoKYGBge3J9CmRmIDwtIGRhdGEuZnJhbWUoRXhwcmVzc2VkID0gcm93U3Vtcyhpc19leHByZXNzZWQpKQpnZ3Bsb3QoZGYsIGFlcyh4PUV4cHJlc3NlZCkpICsgZ2VvbV9iYXIoKQpgYGAKCkl0IHNlZW1zIHRoYXQgZ2VuZXMgYXJlIGVpdGhlciBleHByZXNzZWQgaW4gYWxsIHNhbXBsZXMsIG9yIG5vdCBleHByZXNzZWQgYXQgYWxsLiBXZSB3aWxsIGRlY2lkZSB0byBrZWVwIGdlbmVzIHRoYXQgYXJlIGV4cHJlc3NlZCBpbiBhdCBsZWFzdCAyIHNhbXBsZXMuCgpgYGB7cn0Ka2VlcCA8LSByb3dTdW1zKGFzc2F5KGRkcykgPj0gNSkgPj0gNQp0YWJsZShrZWVwKQpkZHMgPC0gZGRzW2tlZXAsXQoKYGBgCgojIyMgVmlzdWFsaXNpbmcgY291bnQgZGlzdHJpYnV0aW9ucwoKV2UgdHlwaWNhbGx5IHVzZSBhIGBib3hwbG90YCB0byB2aXN1YWxpc2UgZGlmZmVyZW5jZSB0aGUgZGlzdHJpYnV0aW9ucyBvZiB0aGUgY29sdW1ucyBvZiBhIG51bWVyaWMgZGF0YSBmcmFtZS4gQXBwbHlpbmcgdGhlIGBib3hwbG90YCBmdW5jdGlvbiB0byB0aGUgcmF3IGNvdW50cyBmcm9tIG91ciBkYXRhc2V0IHJldmVhbHMgc29tZXRoaW5nIGFib3V0IHRoZSBuYXR1cmUgb2YgdGhlIGRhdGE7IHRoZSBkaXN0cmlidXRpb25zIGFyZSBkb21pbmF0ZWQgYnkgYSBmZXcgZ2VuZXMgd2l0aCB2ZXJ5IGxhcmdlIGNvdW50cy4KCk4uQiB3ZSB3aWxsIHVzZSB0aGUgYGJveHBsb3RgIGZ1bmN0aW9uIGZyb20gImJhc2UiIFIgYXMgaXQgcHJvdmlkZXMgYSBtb3JlIGNvbnZlbmllbnQgd2F5IG9mIHF1aWNrbHkgY3JlYXRpbmcgYm94cGxvdHMgZnJvbSBhIG1hdHJpeCBvZiBudW1lcmljIHZhbHVlcy4KCmBgYHtyfQpib3hwbG90KGFzc2F5KGRkcykpCmBgYAoKQSBsb2ckXzEwJCAoZm9yIGV4YW1wbGUpIHRyYW5zZm9ybWF0aW9uIGNhbiBiZSB1c2VkIHRvIGRpc3BsYXkgdGhlIGNvdW50cyBvbiBhIG1vcmUgY29udmVuaWVudCBzY2FsZSBmb3IgdmlzdWFsaXNhdGlvbgoKYGBge3IgbWVzc2FnZT1GQUxTRX0KYm94cGxvdChsb2cxMChhc3NheShkZHMpKSkKYGBgCgoKCiBXZSBjYW4gdXNlIHRoZSBgdnN0YCBvciBgcmxvZ2AgZnVuY3Rpb24gZnJvbSBgREVTZXEyYHRvIGNvbXBlbnNhdGUgZm9yIHRoZSBlZmZlY3Qgb2YgZGlmZmVyZW50IGxpYnJhcnkgc2l6ZXMgYW5kIHB1dCB0aGUgZGF0YSBvbiB0aGUgbG9nJF8yJCBzY2FsZS4gVGhlIGVmZmVjdCBpcyB0byByZW1vdmUgdGhlIGRlcGVuZGVuY2Ugb2YgdGhlIHZhcmlhbmNlIG9uIHRoZSBtZWFuLCBwYXJ0aWN1bGFybHkgdGhlIGhpZ2ggdmFyaWFuY2Ugb2YgdGhlIGxvZ2FyaXRobSBvZiBjb3VudCBkYXRhIHdoZW4gdGhlIG1lYW4gaXMgbG93LiBGb3IgbW9yZSBkZXRhaWxzIHNlZSB0aGUgW0RFU2VxMiB2aWduZXR0ZV0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI2NvdW50LWRhdGEtdHJhbnNmb3JtYXRpb25zKQoKCgpgYGB7cn0KIyBHZXQgbG9nMiBjb3VudHMKdnNkIDwtIHZzdChkZHMsYmxpbmQ9VFJVRSkKIyBDaGVjayBkaXN0cmlidXRpb25zIG9mIHNhbXBsZXMgdXNpbmcgYm94cGxvdHMKYm94cGxvdChhc3NheSh2c2QpLCB4bGFiPSIiLCB5bGFiPSJMb2cyIGNvdW50cyBwZXIgbWlsbGlvbiIsbGFzPTIsbWFpbj0iTm9ybWFsaXNlZCBEaXN0cmlidXRpb25zIikKIyBMZXQncyBhZGQgYSBibHVlIGhvcml6b250YWwgbGluZSB0aGF0IGNvcnJlc3BvbmRzIHRvIHRoZSBtZWRpYW4gbG9nQ1BNCmFibGluZShoPW1lZGlhbihhc3NheSh2c2QpKSwgY29sPSJibHVlIikKYGBgCgojIyMgSGVhdG1hcCBvZiB0aGUgc2FtcGxlLXRvLXNhbXBsZSBkaXN0YW5jZXMKCkFub3RoZXIgdXNlIG9mIHRoZSB0cmFuc2Zvcm1lZCBkYXRhIGlzIHNhbXBsZSBjbHVzdGVyaW5nLiBIZXJlLCB3ZSBhcHBseSB0aGUgYGRpc3RgIGZ1bmN0aW9uIHRvIHRoZSAqKnRyYW5zcG9zZSoqIG9mIHRoZSB0cmFuc2Zvcm1lZCBjb3VudCBtYXRyaXggdG8gZ2V0IHNhbXBsZS10by1zYW1wbGUgZGlzdGFuY2VzLgoKCmBgYHtyfQpzYW1wbGVEaXN0cyA8LSBkaXN0KHQoYXNzYXkodnNkKSkpCmBgYAoKQSBoZWF0bWFwIG9mIHRoaXMgZGlzdGFuY2UgbWF0cml4IGdpdmVzIHVzIGFuIG92ZXJ2aWV3IG92ZXIgc2ltaWxhcml0aWVzIGFuZCBkaXNzaW1pbGFyaXRpZXMgYmV0d2VlbiBzYW1wbGVzLiBCeSByZS1uYW1pbmcgdGhlIHJvd3MgYW5kIGNvbHVtbnMgb2YgdGhlIGRpc3RhbmNlIG1hdHJpeCB3ZSBjYW4gbWFrZSB0aGUgcGxvdCBlYXNpZXIgdG8gaW50ZXJwcmV0LgoKYGBge3J9CmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KHBoZWF0bWFwKQpzYW1wbGVEaXN0TWF0cml4IDwtIGFzLm1hdHJpeChzYW1wbGVEaXN0cykKcm93bmFtZXMoc2FtcGxlRGlzdE1hdHJpeCkgPC0gY29sRGF0YShkZHMpJElECmNvbG5hbWVzKHNhbXBsZURpc3RNYXRyaXgpIDwtIGNvbERhdGEoZGRzKSRJRApjb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZSggcmV2KGJyZXdlci5wYWwoOSwgIkJsdWVzIikpICkoMjU1KQoKCnBoZWF0bWFwKHNhbXBsZURpc3RNYXRyaXgsCiAgICAgICAgIGNvbD1jb2xvcnMpCmBgYAoKCgojIyMgUHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcyAoUENBKSAKClJlbGF0ZWQgdG8gdGhlIGRpc3RhbmNlIG1hdHJpeCBoZWF0bWFwIGlzIHRoZSBbKFByaW5jaXBhbCBDb21wb25lbnRzIEFuYWx5c2lzKSBQQ0FdKGh0dHA6Ly9zZXRvc2EuaW8vZXYvcHJpbmNpcGFsLWNvbXBvbmVudC1hbmFseXNpcy8pIHBsb3QsIHdoaWNoIHNob3dzIHRoZSBzYW1wbGVzIGluIHRoZSAyRCBwbGFuZSBzcGFubmVkIGJ5IHRoZWlyIGZpcnN0IHR3byBwcmluY2lwYWwgY29tcG9uZW50cy4gQSBwcmluY2lwbGUgY29tcG9uZW50cyBhbmFseXNpcyBpcyBhbiBleGFtcGxlIG9mIGFuIHVuc3VwZXJ2aXNlZCBhbmFseXNpcywgd2hlcmUgd2UgZG9u4oCZdCBuZWVkIHRvIHNwZWNpZnkgdGhlIGdyb3Vwcy4gSWYgeW91ciBleHBlcmltZW50IGlzIHdlbGwgY29udHJvbGxlZCBhbmQgaGFzIHdvcmtlZCB3ZWxsLCB3aGF0IHdlIGhvcGUgdG8gc2VlIGlzIHRoYXQgdGhlIGdyZWF0ZXN0IHNvdXJjZXMgb2YgdmFyaWF0aW9uIGluIHRoZSBkYXRhIGFyZSB0aGUgdHJlYXRtZW50cy9ncm91cHMgd2UgYXJlIGludGVyZXN0ZWQgaW4uIEl0IGlzIGFsc28gYW4gaW5jcmVkaWJseSB1c2VmdWwgdG9vbCBmb3IgcXVhbGl0eSBjb250cm9sIGFuZCBjaGVja2luZyBmb3Igb3V0bGllcnMKCmBERVNlcTJgIGhhcyBhIGNvbnZlbmllbnQgYHBsb3RQQ0FgIGZ1bmN0aW9uIGZvciBtYWtpbmcgdGhlIFBDQSBwbG90LCB3aGljaCBtYWtlcyB1c2Ugb2YgdGhlIGBnZ3Bsb3QyYCBncmFwaGljcyBwYWNrYWdlLiBDb252ZW5pZW50bHkgaXQgd2lsbCBwbG90IHBvaW50cyBhY2NvcmRpbmcgdG8gYSBwYXJ0aWN1bGFyIGNvbHVtbiBpbiB0aGUgbWV0YWRhdGEuCgpgYGB7cn0KcGxvdFBDQSh2c2QsaW50Z3JvdXA9YygiU3RhdHVzIikpCmBgYAoKPiAjIyBDaGFsbGVuZ2UgMiB7LmNoYWxsZW5nZX0KPgo+IDEuIElzIHRoZSBgcGxvdFBDQWAgcGxvdCBiYXNlZCBvbiBhbGwgZ2VuZXMgaW4gdGhlIGRhdGFzZXQ/IEhvdyBjYW4gd2UgY2hhbmdlIGhvdyBtYW55IGdlbmVzIGFyZSB1c2VkIGZvciB0aGUgUENBIGFuYWx5c2lzPyBEb2VzIHRoaXMgc2lnbmlmaWNhbnRseSBjaGFuZ2UgdGhlIHBsb3Q/IChISU5UOiBjaGVjayB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgdGhlIGBwbG90UENBYCBmdW5jdGlvbi4pCj4gMi4gQ2hhbmdlIHRoZSBgaW50Z3JvdXBgIHBhcmFtZXRlciBzbyB0aGF0IGJvdGggZ2xlYXNvbl9zY29yZSBhbmQgU3RhdHVzIGFyZSB1c2VkIGZvciBncm91cGluZy4gKFNlZSB0aGUgZG9jdW1lbnRhdGlvbiBhZ2FpbikKPiAzLiBXaGF0IHBvdGVudGlhbCBwcm9ibGVtcyBjYW4geW91IHNwb3QgaW4gdGhlIGRhdGE/CgojIyMgTm90ZSBhYm91dCBiYXRjaCBlZmZlY3RzCgpJbiBvdXIgdW5zdXBlcnZpc2VkIGFuYWx5c2lzIHdlIHNob3VsZCBzZWUgdGhhdCB0aGUgbWFpbiBzb3VyY2Ugb2YgdmFyaWF0aW9uIGlzIGR1ZSB0byBiaW9sb2dpY2FsIGVmZmVjdHMsIGFuZCBub3QgdGVjaG5pY2FsIHZhcmlhdGlvbiBzdWNoIGFzIHdoZW4gdGhlIGxpYnJhcmllcyB3ZXJlIHNlcXVlbmNlZC4gSWYgd2UgZG8gb2JzZXJ2ZSBoaWdoIHRlY2huaWNhbCB2YXJpYXRpb24gaW4gb3VyIGRhdGEsIGl0IGlzIG5vdCBhIGNvbXBsZXRlIGRpc2FzdGVyIHByb3ZpZGVkIHRoYXQgKioqd2UgaGF2ZSBkZXNpZ25lZCBvdXIgZXhwZXJpbWVudCBwcm9wZXJseSoqLiBJbiBwYXJ0aWN1bGFyIHRoZSBbc3ZhIEJpb2NvbmR1Y3RvciBwYWNrYWdlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9zdmEvaW5zdC9kb2Mvc3ZhLnBkZikgY2FuIGNvcnJlY3QgZm9yIGJhdGNoIGVmZmVjdHMgcHJvdmlkZWQgdGhhdCByZXByZXNlbnRhdGl2ZXMgb2YgdGhlIGdyb3VwcyBvZiBpbnRlcmVzdCBhcHBlYXIgaW4gZWFjaCBiYXRjaC4gQWx0ZXJuYXRpdmVseSwgdGhlIGJhdGNoIG9yIGNvbmZvdW5kaW5nIGZhY3RvciBtYXkgYmUgaW5jb3Jwb3JhdGVkIGludG8gdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLgoKIyMjIENvcnJlY3RpbmcgdGhlIHNhbXBsZSBpbmZvcm1hdGlvbgoKClRoZSBwZXJzb24gY3JlYXRpbmcgdGhlIHNhbXBsZSBzaGVldCBoYXMgYmVlbiBpbmNvbnNpc3RlbnQgYWJvdXQgdGhlIHdheSB0aGF0IHZhbHVlcyBvZiBgZ2xlYXNvbl9zY29yZWAgaGF2ZSBiZWVuIGVudGVyZWQgaW50byB0aGUgbWV0YWRhdGEuIFN1Y2ggZXJyb3JzIGNhbiBiZSBhbm5veWluZyB3aGVuIGxhYmVsbGluZyBwbG90cywgYnV0IGhhdmUgbW9yZSBzZXJpb3VzIGNvbnNlcXVlbmNlcyB3aGVuIGF0dGVtcHRpbmcgdG8gZml0IHN0YXRpc3RpY2FsIG1vZGVscyB0byB0aGUgZGF0YS4KCmBgYHtyfQpkcGx5cjo6Y291bnQoc2FtcGxlaW5mbyxnbGVhc29uX3Njb3JlKQpgYGAKCgoKYGBge3IgY29ycmVjdFNhbXBsZVNoZWV0fQpsaWJyYXJ5KHN0cmluZ3IpCnNhbXBsZWluZm9fY29ycmVjdGVkIDwtIHNhbXBsZWluZm8KCnNhbXBsZWluZm9fY29ycmVjdGVkIDwtIG11dGF0ZShzYW1wbGVpbmZvLCBnbGVhc29uX3Njb3JlID0gc3RyX3RvX3RpdGxlKGdsZWFzb25fc2NvcmUpKSAlPiUKICBtdXRhdGUoZ2xlYXNvbl9zY29yZSA9IHN0cl90cmltKGdsZWFzb25fc2NvcmUpKSAKCmRwbHlyOjpjb3VudChzYW1wbGVpbmZvX2NvcnJlY3RlZCxnbGVhc29uX3Njb3JlKQoKd3JpdGUudGFibGUoc2FtcGxlaW5mb19jb3JyZWN0ZWQsIGZpbGU9Im1ldGFfZGF0YS9zYW1wbGVJbmZvX2NvcnJlY3RlZC50eHQiLHNlcD0iXHQiLHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCj4gIyMgQ2hhbGxlbmdlIDMgey5jaGFsbGVuZ2V9Cj4KPiAxLiBSZS1jcmVhdGUgdGhlIERFU2VxRGF0YXNldCBvYmplY3QgdG8gaW5jbHVkZSB0aGUgY29ycmVjdGVkIHNhbXBsZSBpbmZvcm1hdGlvbgo+IDIuIFJlLXJ1biB0aGUgcGxvdFBDQSBmdW5jdGlvbiBvbiB0aGUgbmV3IGRhdGEgYW5kIHZlcmlmeSB0aGF0IHRoZSBzYW1wbGUgZ3JvdXBzIG5vdyBsb29rIGNvcnJlY3QKPgoKCgojIyMgQSBub3RlIGFib3V0ICJwaXBlcyIgYW5kIGdncGxvdDIKCgpUaGUgYHBsb3RQQ0FgIGZ1bmN0aW9uIHByb2R1Y2VzIGEgYGdncGxvdDJgIHBsb3QgKHJhdGhlciB0aGFuICJiYXNlIiBncmFwaGljcykgYW5kIHdlIGNhbiBhbHNvIGdldCB0aGUgZnVuY3Rpb24gdG8gcmV0dXJuIHRoZSBkYXRhIHVzZWQgdG8gY3JlYXRlIHRoZSBwbG90IGJ5IGNoYW5naW5nIHRoZSBgcmV0dXJuRGF0YWAgYXJndW1lbnQuCgpgYGB7cn0KcGxvdF9kYXRhIDwtIHBsb3RQQ0EodnNkLGludGdyb3VwPSJTdGF0dXMiLHJldHVybkRhdGE9VFJVRSkKcGxvdF9kYXRhIDwtIGJpbmRfY29scyhwbG90X2RhdGEsc2FtcGxlaW5mb19jb3JyZWN0ZWQpCmBgYAoKV2l0aCBgZ2dwbG90MmAgcGxvdCBjYW4gYmUgY3JlYXRlZCBieSBtYXBwaW5nIHZhcmlvdXMgKmFlc3RoZXRpY3MqIG9mIHRoZSBwbG90IChjb2xvdXIsIHNoYXBlLCB4LSBhbmQgeS1jb29yZGluYXRlcykgdG8gY29sdW1ucyBpbiB0aGUgZGF0YSBmcmFtZS4KCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmdncGxvdChwbG90X2RhdGEsIGFlcyh4ID0gUEMxLHk9UEMyLCBjb2w9U3RhdHVzKSkgKyBnZW9tX3BvaW50KCkKYGBgCgpIYXZpbmcgdGhlIGRhdGEgaW4gdGhpcyBmb3JtIGFsbG93cyB1cyB0byBjdXN0b21pc2UgdGhlIHBsb3QgaW4gbG90cyBvZiB3YXlzCgpgYGB7cn0KZ2dwbG90KHBsb3RfZGF0YSwgYWVzKHggPSBQQzEseT1QQzIsIGNvbD1TdGF0dXMscGNoPWdsZWFzb25fc2NvcmUpKSArIGdlb21fcG9pbnQoc2l6ZT01KQoKYGBgCgo+ICMjIENoYWxsZW5nZSA0IHsuY2hhbGxlbmdlfQo+Cj4gQ3JlYXRlIGEgUENBIHBsb3Qgd2hlcmUgdGhlIHBvaW50cyBhcmUgY29sb3VyZWQgYnkgR2xlYXNvbiBzY29yZSwgYW5kIG1ha2luZyB0aGUgc2l6ZSBvZiB0aGUgcG9pbnRzIHJlbGF0aXZlIHRvIGxpYnJhcnkgc2l6ZQo+Cj4KCiFbXShpbWFnZXMvY2hhbGxlbmdlNC5wbmcpCgo=