Data import
First, let’s load all the packages we will need to analyse the data.
library(DESeq2)
library(tidyverse)
package ‘dplyr’ was built under R version 3.5.1
Mouse mammary gland dataset
The data for this tutorial comes from a Nature Cell Biology paper, EGF-mediated induction of Mcl-1 at the switch to lactation is essential for alveolar cell survival (Fu et al. 2015). The raw data (sequence reads) can be downloaded from SRA under SRP045534, and processed data (counts) can be downloaded from Gene Expression Omnibus database (GEO) under accession number GSE60450. Please see Supplementary material for instructions on downloading raw files from SRA and aligning fastq using HISAT2.
This study examines the expression profiles of basal stem-cell enriched cells (B) and committed luminal cells (L) in the mammary gland of virgin, pregnant and lactating mice. Six groups are present, with one for each combination of cell type and mouse status. Each group contains two biological replicates.
Reading in the count data
The raw reads were aligned using HISAT2 (Kim, Langmead, and Salzberg 2015) to the GRCm38 mouse reference genome from Ensembl. featureCounts (Liao, Smyth, and Shi 2014) was used to count reads against the Ensembl gene annotation and generate a counts matrix (as described in Section 1).
First we need to read the data into R from the file in the data directory.
# Read the data into R
seqdata <- read.delim("data/GSE60450_Lactation.featureCounts",
comment = "#",
stringsAsFactors=F)
head(seqdata)
In the seqdata
object each row represents a gene. The columns contains:
1 Geneid - Ensembl ID
2-5 Chr, Start, End, Strand - Genomic locations of exons of the gene
6 Length - Transcript length of the gene
7-18 One column for each sample with the count of how many reads were assigned
to each gene by featureCounts.
A quick intro to dplyr
One of the most complex aspects of learning to work with data in R
is getting to grips with subsetting and manipulating data tables. The package dplyr
(Wickham et al. 2018) was developed to make this process more intuitive than it is using standard base R
processes. It also makes use of a new symbol %>%
, called the “pipe”, which makes the code a bit tidier.
We are introducing this because it makes many of the processes we will look at later much simpler. Importantly it also results in code that is much easier to read and understand.
Let’s have quick look at this by playing with our sampleinfo
table.
Suppose we wanted a new sample table that:
- Just includes the “basal” samples
- Only has the columns “CellType” and “Group”
- Renames the “CellType” column as “Cell”
In base R
we would do the something like:
newTable <- sampleinfo
basal <- which(newTable$CellType=="basal")
newTable <- newTable[basal, ]
newTable <- newTable[basal, c("CellType", "Group")]
colnames(newTable)[1] <- "Cell"
With dplyr
we can use two new functions - filter
and select
- for the first two steps:
newTable <- sampleinfo
newTable <- filter(newTable, CellType=="basal")
newTable <- select(newTable, CellType, Group)
There’s no need to quote the column names as dplyr intelligently interprets the arguments it’s passes as belonging to the data table columns.
Rather than repeatedly reassigning newTable <- f(newTable)
as above, we can use the pipe - %>%
. This takes the output of one function and “pipes” it into the first argument of the next function so that we don’t have to keep specifying the object we are working with:
newTable <- sampleinfo %>%
filter(CellType=="basal") %>%
select(CellType, Group) %>%
rename(Cell=CellType)
This is a fairly trivial example and the benefits may not be immediately obvious, but once you get used to using dplyr
(and the other related “tidyverse” packages, such as stringr
) you’ll find it much more powerful and easy to use than base R.
We will encounter a few more dplyr
commands during the course, we will explain their use as we come to them.
Quality assessment
Before moving on to doing the actually differential expression analysis it important do assess the quality of our data.
Library sizes bar plot
First, we can plot how many reads we have for each sample. Whilst normalisation can account for imbalance in coverage across the samples, extreme differences may be indicative of underlying problems in the samples.
librarySizes <- colSums(countdata)
barplot(librarySizes,
names=names(librarySizes),
las=2,
main="Barplot of library sizes")
abline(h=20e6, lty=2)
Count distribution boxplots
Count data is not normally distributed, so if we want to examine the distributions of the raw counts it is helpful to transform the data on to a log scale. Typically we use a log2
transformation, however, because the data is count data and will contain many 0
s we need to add a count of 1 to every value in order to prevent attempting log2(0)
from creating errors.
# Get log2 counts per million
logcounts <- log2(countdata + 1)
We’ll check the distribution of read counts using a boxplot and well add some colour to see if there is any difference between sample groups.
# make a colour vector
statusCol <- as.numeric(factor(sampleinfo$Status)) + 1
# Check distributions of samples using boxplots
boxplot(logcounts,
xlab="",
ylab="Log2(Counts)",
las=2,
col=statusCol)
# Let's add a blue horizontal line that corresponds to the median logCPM
abline(h=median(as.matrix(logcounts)), col="blue")
From the boxplots we see that overall the density distributions of raw log-intensities are not identical but still not very different. If a sample is really far above or below the blue horizontal line we may need to investigate that sample further.
Challenge 1
- Use the
DESeq2
function rlog
to transform the count data. This function also normalises for library size.
- Plot the count distribution boxplots with this data How has this effected the count distributions?
Principle Component Analysis
A principle components analysis (PCA) is an example of an unsupervised analysis, where we don’t specify the grouping of the samples. If your experiment is well controlled and has worked well, we should that replicate samples cluster closely, whilst the greatest sources of variation in the data should be between treatments/sample groups. It is also an incredibly useful tool for checking for outliers and batch effects.
To run the PCA we should first normalise our data for library size and transform to a log scale.DESeq2 provides two commands that can be used to do this, here we will use the command rlog
. rlog
performs a log2 scale transformation in a way that compensates for differences between samples for genes with low read count and also normalizes between samples for library size.
You can read more about rlog
, it’s alternative vst
and the comparison between the two here.
To plot the PCA results we will use the autoplot
function from the ggfortify
package (Tang, Horikoshi, and Li 2016). ggfortify
is built on top of ggplot2
and is able to recognise common statistical objects such as PCA results or linear model results and automatically generate summary plot of the results in an appropriate manner.
library(ggfortify)
rlogcounts <- rlog(countdata)
# run PCA
pcDat <- prcomp(t(rlogcounts))
# plot PCA
autoplot(pcDat)
# Lets add colour to look at the clustering for Status
autoplot(pcDat,
data = sampleinfo,
colour="Status",
size=5)
# and now status
# Lets add colour to look at the clustering for Cell Type
autoplot(pcDat,
data = sampleinfo,
colour="CellType",
size=5)
# We could use shape for one of the factors
autoplot(pcDat,
data = sampleinfo,
colour="Status",
shape="CellType",
size=5)
# Specify some clearer shapes to use that have a black outline and use fill
autoplot(pcDat,
data = sampleinfo,
fill="Status",
shape="CellType",
size=5) +
scale_shape_manual(values=c(21, 24)) +
guides(fill = guide_legend(override.aes=list(shape=22)))
Discussion
Look at the last PCA plot. What is the greatest source of variation? Is there something strange going on with the samples? Let’s identify these samples:
# setting shape to FALSE causes the plot to default to using the labels
autoplot(pcDat,
data = sampleinfo,
colour="CellType",
shape=FALSE,
label.size=6)
The mislabelled samples are MCL1.DG, which is labelled as luminal but should be basal, and MCL1.LA, which is labelled as basal but should be luminal. Let’s fix the sample sheet…
sampleinfo <- sampleinfo %>%
mutate(CellType=ifelse(Sample=="MCL1.DG", "basal", CellType)) %>%
mutate(CellType=ifelse(Sample=="MCL1.LA", "luminal", CellType)) %>%
mutate(Group=str_c(CellType, ".", Group))
…and export it so that we have the correct version for later use.
write_csv(sampleinfo, "results/SampleInfo_Corrected.txt")
Let’s look at the PCA now.
autoplot(pcDat,
data = sampleinfo,
fill="Status",
shape="CellType",
size=5) +
scale_shape_manual(values=c(21, 24)) +
guides(fill = guide_legend(override.aes=list(shape=22)))
Discussion
What is the greatest source of variation in the data (i.e. what does dimension 1 represent)? What is the second greatest source of variation in the data?
Replicate samples from the same group cluster together in the plot, while samples from different groups form separate clusters. This indicates that the differences between groups are larger than those within groups, i.e., differential expression is greater than the variance and can be detected. The differences between virgin, pregnant and lactating are greater for luminal cells than for basal.
Clustering in the PCA plot can be used to motivate changes to the design matrix in light of potential batch effects. For example, imagine that the first replicate of each group was prepared at a separate time from the second replicate. If the PCA plot showed separation of samples by time, it might be worthwhile including time in the down stream analysis to account for the time-based effect.
PCA beyond the first two dimensions
autoplot
plots the first two dimensions as a default, however you can plot higher dimensions using the x
and y
arguments.
autoplot(pcDat,
data = sampleinfo,
fill = "Status",
shape = "CellType",
size = 5,
x = 2,
y = 3) +
scale_shape_manual(values=c(21, 24)) +
guides(fill = guide_legend(override.aes=list(shape=22)))
Interactive MDS Plot with Glimma
Another alternative is to generate a Multidimensional scaling (MDS) plot. MDS is similar to PCA, in MDS the distance between each pair of samples in the MDS plot is calculated as the ‘leading fold change’, which is defined as the root-mean-square of the largest 500 log2-fold changes between that pair of samples. The Glimma package creates interactive plots that allow the use to explore the different dimensions.
library(Glimma)
glMDSPlot(rlogcounts,
labels = sampleinfo$Sample,
groups = sampleinfo[,c("CellType", "Status")],
folder = "mds")
Glimma was created to make interactive versions of some of the popular plots from the limma package. At present it can be used to obtain MDS plots. The output of glMDSPlot
is an html page (/mds/MDS-Plot.html) that shows the MDS plot on the left, and the amount of variation explained by each dimension in a barplot on the right. The user can hover over points to find out sample information, and switch between successive dimensions in the MDS plot by clicking on the bars in the barplot. The default MDS plots shows dimensions 1 and 2.
Hierarchical clustering with heatmaps
An alternative to PCA plots for examining relationships between samples is using hierarchical clustering. Heatmaps are a nice visualisation to examine hierarchical clustering of your samples. We can do this using the heatmap.2
function from the gplots package. In this example heatmap.2
calculates a matrix of euclidean distances from the logcounts
object.
The RColorBrewer package has nicer colour schemes, accessed using the brewer.pal
function. “RdYlBu” is a common choice, and “Spectral” is also nice.
Note:The png
function will create a png file to save the plots created straight after, and will close this file when dev.off()
is called. To see your plots interactively, simply omit those two lines.
We don’t want to plot a heatmap of all 22013 genes, so let’s select data for the 500 most variable genes and plot the heatmap.
# We estimate the variance for each row in the logcounts matrix
countVar <- apply(rlogcounts, 1, var)
# Get the row numbers for the top 500 most variable genes
highVar <- order(countVar, decreasing=TRUE)[1:500]
# Subset logcounts matrix
hmDat <- rlogcounts[highVar,]
library(gplots)
library(RColorBrewer)
# Get some nicer colours
mypalette <- brewer.pal(11, "RdYlBu")
# http://colorbrewer2.org/#type=sequential&scheme=BuGn&n=3
morecols <- colorRampPalette(mypalette)
# Set up colour vector for celltype variable
col.cell <- c("purple","orange")[sampleinfo$CellType]
# Plot the heatmap
heatmap.2(hmDat,
col=rev(morecols(50)),
trace="column",
main="Top 500 most variable genes across samples",
ColSideColors=col.cell,scale="row")
Challenge 2
Redo the heatmap using the top 500 LEAST variable genes.
Change the colour scheme to “PiYG” and redo the heatmap. Try brewer.pal.info
and display.brewer.all
to see what other colour schemes are available.
Change the sample names to group
using the labCol
argument
Remove the gene names from the righthand side of the plot using labRow
Convert counts to DESeqDataSet object
Next we’ll create a DESeqDataSet
object. This is an object used by DESeq2
to store count data. It has a number of slots for storing count data, sample information, the model design for the differential expression analysis, and various other parameters about the data.
In the simplest form we need to provide counts, sample information and a design formula. For now we just want to perform some QC so we will provide a simple model where would just be concerned with contrasting the differential expression between cell types. We can change the model later if we want to.
# first lets check that our rows and columns match
all(sampleinfo$Sample == colnames(countdata))
[1] TRUE
# create the design formula
design <- as.formula(~ CellType)
# create the DESeqDataSet object
ddsObj <- DESeqDataSetFromMatrix(countData = countdata,
colData = sampleinfo,
design = design)
some variables in design formula are characters, converting to factors
Normalisation
The estimateSizeFactors
command of DESeq2
applies the “median ratio method” of normalisation(Huber 2010). This generates a set of normalization factors and adds them to the ddsObj
in the colcolData
slot.
# Apply normalisation to DDS object
ddsObj <- estimateSizeFactors(ddsObj)
Take a look at the normalisation factors for these samples.
ddsObj@colData$sizeFactor
MCL1.DG MCL1.DH MCL1.DI MCL1.DJ MCL1.DK MCL1.DL MCL1.LA
1.3001316 1.1931245 1.2176601 1.0775276 0.9804917 0.9739114 1.2819584
MCL1.LB MCL1.LC MCL1.LD MCL1.LE MCL1.LF
1.3645962 1.0348558 0.9240333 0.5722305 0.5795097
A normalization factor below one indicates that the library size will be scaled down, as there is more suppression (i.e., composition bias) in that library relative to the other libraries. This is also equivalent to scaling the counts upwards in that sample. Conversely, a factor above one scales up the library size and is equivalent to downscaling the counts.
The MCL1.LE and MCL1.LF have much smaller normalisation factors, and MCL1.LA and MCL1.LB have the largest. If we plot MAplots using the plotMA
in limma
[Ritchie2015] function for these samples, we should be able to see the composition bias problem.
The rlog
data (logcounts
) has already been normalised for both library size and composition bias. Let’s have a look at the raw countsdata
; we will need to log2 scale it first.
library(limma)
logcounts <- log2(countdata + 1)
par(mfrow=c(1,2))
plotMA(logcounts, array = 7)
abline(h=0,col="grey")
plotMA(logcounts, array = 11)
abline(h=0,col="grey")
The MA plots show average expression (mean: x-axis) against log-fold-changes (difference: y-axis). Because our ddsObj
object contains the normalisation factors, if we redo these plots using ddsObj
, we should see the composition bias problem has been solved.
normalizedCounts <- counts(ddsObj, normalized=TRUE)
logNormalizedCounts <- log2(normalizedCounts + 1)
par(mfrow=c(1,2))
plotMA(logNormalizedCounts, array = 7)
abline(h=0,col="grey")
plotMA(logNormalizedCounts, array = 11)
abline(h=0,col="grey")
Challenge 3
Plot the biased and unbiased MA plots both samples side by side to see the before and after normalisation.
Export data
We can save a few data objects to use later so we don’t have to rerun everything
save(countdata, sampleinfo, file="results/preprocessing.RData")
LS0tCnRpdGxlOiAiUk5BLXNlcSBhbmFseXNpcyBpbiBSIgpzdWJ0aXRsZTogIlByZS1wcm9jZXNzc2luZyBSTkEtc2VxIGRhdGEiCmF1dGhvcjogIlN0ZXBoYW5lIEJhbGxlcmVhdSwgTWFyayBEdW5uaW5nLCBBYmJpIEVkd2FyZHMsIE9zY2FyIFJ1ZWRhLCBBc2hsZXkgU2F3bGUiCmRhdGU6ICdgciBmb3JtYXQoU3lzLnRpbWUoKSwgIkxhc3QgbW9kaWZpZWQ6ICVkICViICVZIilgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCm1pbnV0ZXM6IDMwMApsYXlvdXQ6IHBhZ2UKYmlibGlvZ3JhcGh5OiByZWYuYmliCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKIyBJbnRyb2R1Y3Rpb24KCkluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIGJlZ2luIHRoZSBwcm9jZXNzIG9mIGFuYWx5c2luZyB0aGUgUk5Bc2VxIGluIFIuIEluIHRoZQpuZXh0IHNlY3Rpb24gd2Ugd2lsbCB1c2UgREVTZXEyIGZvciBkaWZmZXJlbnRpYWwgYW5hbHlzaXMuIEJlZm9yZSB3ZQpkbyB0aGF0IHdlIG5lZWQgdG86CgoqIGltcG9ydCBvdXIgY291bnRzIGludG8gUgoqIG1hbmlwdWxhdGUgdGhlIGltcG9ydGVkIGRhdGEgc28gdGhhdCBpdCBpcyBpbiB0aGUgY29ycmVjdCBmb3JtYXQgZm9yIERFU2VxMgoqIGZpbHRlciBvdXQgdW53YW50ZWQgZ2VuZXMKKiBydW4gc29tZSBpbml0aWFsIFFDIG9uIHRoZSByYXcgY291bnQgZGF0YQoKV2Ugd2lsbCBhbHNvIGxvb2sgYXQgdGhlIGVmZmVjdHMgb2Ygbm9ybWFsaXNhdGlvbiBmb3IgY29tcG9zaXRpb24gYmlhcy4KCkEgZGV0YWlsZWQgYW5hbHlzaXMgd29ya2Zsb3csIHJlY29tbWVuZGVkIGJ5IHRoZSBhdXRob3JzIG9mIERFU2VxMiBjYW4gYmUgZm91bmQKb24gW3RoZSBCaW9uY29uZHVjdG9yIAp3ZWJzaXRlXShodHRwOi8vbWFzdGVyLmJpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS93b3JrZmxvd3MvdmlnbmV0dGVzL3JuYXNlcUdlbmUvaW5zdC9kb2Mvcm5hc2VxR2VuZS5odG1sKS4KCiMgRGF0YSBpbXBvcnQKCkZpcnN0LCBsZXQncyBsb2FkIGFsbCB0aGUgcGFja2FnZXMgd2Ugd2lsbCBuZWVkIHRvIGFuYWx5c2UgdGhlIGRhdGEuCgpgYGB7ciBzZXR1cCwgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKIyMgTW91c2UgbWFtbWFyeSBnbGFuZCBkYXRhc2V0CgpUaGUgZGF0YSBmb3IgdGhpcyB0dXRvcmlhbCBjb21lcyBmcm9tIGEgTmF0dXJlIENlbGwgQmlvbG9neSBwYXBlciwKWypFR0YtbWVkaWF0ZWQgaW5kdWN0aW9uIG9mIE1jbC0xIGF0IHRoZSBzd2l0Y2ggdG8gbGFjdGF0aW9uIGlzIGVzc2VudGlhbCBmb3IKYWx2ZW9sYXIgY2VsbCBzdXJ2aXZhbCpdKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvbmNiMzExNykKW0BGdTIwMTVdLiBUaGUgcmF3IGRhdGEgKHNlcXVlbmNlIHJlYWRzKSBjYW4gYmUgZG93bmxvYWRlZCBmcm9tIFNSQSB1bmRlcgpbU1JQMDQ1NTM0XShodHRwczovL3RyYWNlLm5jYmkubmxtLm5paC5nb3YvVHJhY2VzL3NyYS9zcmEuY2dpP3N0dWR5PVNSUDA0NTUzNCksCmFuZCBwcm9jZXNzZWQgZGF0YSAoY291bnRzKSBjYW4gYmUgZG93bmxvYWRlZCBmcm9tIEdlbmUgRXhwcmVzc2lvbiBPbW5pYnVzIApkYXRhYmFzZSAoR0VPKSB1bmRlciBhY2Nlc3Npb24gbnVtYmVyCltHU0U2MDQ1MF0oaHR0cDovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU0U2MDQ1MCkuIFBsZWFzZQpzZWUgW1N1cHBsZW1lbnRhcnkgCm1hdGVyaWFsXSguLi9TdXBwbGVtZW50YXJ5X01hdGVyaWFscy9TMV9HZXR0aW5nX3Jhd19yZWFkc19mcm9tX1NSQS5odG1sKSBmb3IgCmluc3RydWN0aW9ucyBvbiBkb3dubG9hZGluZyByYXcgZmlsZXMgZnJvbSBTUkEgYW5kIGFsaWduaW5nIGZhc3RxIHVzaW5nIEhJU0FUMi4KClRoaXMgc3R1ZHkgZXhhbWluZXMgdGhlIGV4cHJlc3Npb24gcHJvZmlsZXMgb2YgYmFzYWwgc3RlbS1jZWxsIGVucmljaGVkIGNlbGxzCihCKSBhbmQgY29tbWl0dGVkIGx1bWluYWwgY2VsbHMgKEwpIGluIHRoZSBtYW1tYXJ5IGdsYW5kIG9mIHZpcmdpbiwgcHJlZ25hbnQKYW5kIGxhY3RhdGluZyBtaWNlLiBTaXggZ3JvdXBzIGFyZSBwcmVzZW50LCB3aXRoIG9uZSBmb3IgZWFjaCBjb21iaW5hdGlvbiBvZgpjZWxsIHR5cGUgYW5kIG1vdXNlIHN0YXR1cy4gRWFjaCBncm91cCBjb250YWlucyB0d28gYmlvbG9naWNhbCByZXBsaWNhdGVzLgoKIyMgUmVhZGluZyBpbiB0aGUgc2FtcGxlIG1ldGFkYXRhCgpUaGUgYHNhbXBsZWluZm9gIGZpbGUgY29udGFpbnMgYmFzaWMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHNhbXBsZXMgdGhhdCB3ZSB3aWxsCm5lZWQgZm9yIHRoZSBhbmFseXNpcyB0b2RheS4KCmBgYHtyIGxvYWRTYW1wbGVJbmZvLCBtZXNzYWdlID0gRkFMU0V9CiMgUmVhZCB0aGUgc2FtcGxlIGluZm9ybWF0aW9uIGludG8gYSBkYXRhIGZyYW1lCnNhbXBsZWluZm8gPC0gcmVhZC5kZWxpbSgiZGF0YS9TYW1wbGVJbmZvLnR4dCIsIHN0cmluZ3NBc0ZhY3RvcnM9RikKc2FtcGxlaW5mbwpgYGAKCiMjIFJlYWRpbmcgaW4gdGhlICBjb3VudCBkYXRhCgpUaGUgcmF3IHJlYWRzIHdlcmUgYWxpZ25lZCB1c2luZyBISVNBVDIgW0BLaW0yMDE1XSB0byB0aGUgR1JDbTM4IG1vdXNlIHJlZmVyZW5jZQpnZW5vbWUgZnJvbSBFbnNlbWJsLiBmZWF0dXJlQ291bnRzIFtATGlhbzIwMTRdIHdhcyB1c2VkIHRvIGNvdW50IHJlYWRzIGFnYWluc3QKdGhlIEVuc2VtYmwgZ2VuZSBhbm5vdGF0aW9uIGFuZCBnZW5lcmF0ZSBhIGNvdW50cyBtYXRyaXggKGFzIGRlc2NyaWJlZCBpbiAKW1NlY3Rpb24gMV0oMDFfQ291bnRpbmcuaHRtbCkpLgoKRmlyc3Qgd2UgbmVlZCB0byByZWFkIHRoZSBkYXRhIGludG8gUiBmcm9tIHRoZSBmaWxlIGluIHRoZSAqZGF0YSogZGlyZWN0b3J5LgoKYGBge3IgbG9hZERhdGEsIG1lc3NhZ2UgPSBGQUxTRX0KIyBSZWFkIHRoZSBkYXRhIGludG8gUgpzZXFkYXRhIDwtIHJlYWQuZGVsaW0oImRhdGEvR1NFNjA0NTBfTGFjdGF0aW9uLmZlYXR1cmVDb3VudHMiLCAKICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSAiIyIsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzPUYpCmhlYWQoc2VxZGF0YSkKYGBgCgpJbiB0aGUgYHNlcWRhdGFgIG9iamVjdCBlYWNoIHJvdyByZXByZXNlbnRzIGEgZ2VuZS4gVGhlIGNvbHVtbnMgY29udGFpbnM6CgogICAxICpHZW5laWQqIC0gRW5zZW1ibCBJRCAgCiAyLTUgKkNociosICpTdGFydCosICpFbmQqLCAqU3RyYW5kKiAtIEdlbm9taWMgbG9jYXRpb25zIG9mIGV4b25zIG9mIHRoZSBnZW5lICAKICAgNiAqTGVuZ3RoKiAtIFRyYW5zY3JpcHQgbGVuZ3RoIG9mIHRoZSBnZW5lICAKNy0xOCBPbmUgY29sdW1uIGZvciBlYWNoIHNhbXBsZSB3aXRoIHRoZSBjb3VudCBvZiBob3cgbWFueSByZWFkcyB3ZXJlIGFzc2lnbmVkICAKdG8gZWFjaCBnZW5lIGJ5IGZlYXR1cmVDb3VudHMuCgojIyMgQSBxdWljayBpbnRybyB0byBgZHBseXJgCgpPbmUgb2YgdGhlIG1vc3QgY29tcGxleCBhc3BlY3RzIG9mIGxlYXJuaW5nIHRvIHdvcmsgd2l0aCBkYXRhIGluIGBSYCBpcyAKZ2V0dGluZyB0byBncmlwcyB3aXRoIHN1YnNldHRpbmcgYW5kIG1hbmlwdWxhdGluZyBkYXRhIHRhYmxlcy4gVGhlIHBhY2thZ2UgCmBkcGx5cmAgW0BXaWNraGFtMjAxOF0gd2FzIGRldmVsb3BlZCB0byBtYWtlIHRoaXMgcHJvY2VzcyBtb3JlIGludHVpdGl2ZSB0aGFuIGl0CmlzIHVzaW5nIHN0YW5kYXJkIGJhc2UgYFJgIHByb2Nlc3Nlcy4gSXQgYWxzbyBtYWtlcyB1c2Ugb2YgYSBuZXcgc3ltYm9sIGAlPiVgLApjYWxsZWQgdGhlICJwaXBlIiwgd2hpY2ggbWFrZXMgdGhlIGNvZGUgYSBiaXQgdGlkaWVyLiAKCldlIGFyZSBpbnRyb2R1Y2luZyB0aGlzIGJlY2F1c2UgaXQgbWFrZXMgbWFueSBvZiB0aGUgcHJvY2Vzc2VzIHdlIHdpbGwgbG9vayBhdCAKbGF0ZXIgKiptdWNoIHNpbXBsZXIqKi4gSW1wb3J0YW50bHkgaXQgYWxzbyByZXN1bHRzIGluIGNvZGUgdGhhdCBpcyBtdWNoIGVhc2llcgp0byByZWFkIGFuZCB1bmRlcnN0YW5kLgoKTGV0J3MgaGF2ZSBxdWljayBsb29rIGF0IHRoaXMgYnkgcGxheWluZyB3aXRoIG91ciBgc2FtcGxlaW5mb2AgdGFibGUuCgpTdXBwb3NlIHdlIHdhbnRlZCBhIG5ldyBzYW1wbGUgdGFibGUgdGhhdDoKCjEuIEp1c3QgaW5jbHVkZXMgdGhlICJiYXNhbCIgc2FtcGxlcwoyLiBPbmx5IGhhcyB0aGUgY29sdW1ucyAiQ2VsbFR5cGUiIGFuZCAiR3JvdXAiCjMuIFJlbmFtZXMgdGhlICJDZWxsVHlwZSIgY29sdW1uIGFzICJDZWxsIgoKSW4gYmFzZSBgUmAgd2Ugd291bGQgZG8gdGhlIHNvbWV0aGluZyBsaWtlOgoKYGBge3IgYmFzZVIsIGV2YWw9RkFMU0V9Cm5ld1RhYmxlIDwtIHNhbXBsZWluZm8KCmJhc2FsIDwtIHdoaWNoKG5ld1RhYmxlJENlbGxUeXBlPT0iYmFzYWwiKQpuZXdUYWJsZSA8LSBuZXdUYWJsZVtiYXNhbCwgXQoKbmV3VGFibGUgPC0gbmV3VGFibGVbYmFzYWwsIGMoIkNlbGxUeXBlIiwgIkdyb3VwIildCgpjb2xuYW1lcyhuZXdUYWJsZSlbMV0gPC0gIkNlbGwiCmBgYAoKV2l0aCBgZHBseXJgIHdlIGNhbiB1c2UgdHdvIG5ldyBmdW5jdGlvbnMgLSBgZmlsdGVyYCBhbmQgYHNlbGVjdGAgLSBmb3IgdGhlCmZpcnN0IHR3byBzdGVwczoKCmBgYHtyIGRwbHlyLCBldmFsPUZBTFNFfQpuZXdUYWJsZSA8LSBzYW1wbGVpbmZvCm5ld1RhYmxlIDwtIGZpbHRlcihuZXdUYWJsZSwgQ2VsbFR5cGU9PSJiYXNhbCIpCm5ld1RhYmxlIDwtIHNlbGVjdChuZXdUYWJsZSwgQ2VsbFR5cGUsIEdyb3VwKQpgYGAKClRoZXJlJ3Mgbm8gbmVlZCB0byBxdW90ZSB0aGUgY29sdW1uIG5hbWVzIGFzIGRwbHlyIGludGVsbGlnZW50bHkgaW50ZXJwcmV0cwp0aGUgYXJndW1lbnRzIGl0J3MgcGFzc2VzIGFzIGJlbG9uZ2luZyB0byB0aGUgZGF0YSB0YWJsZSBjb2x1bW5zLgoKUmF0aGVyIHRoYW4gcmVwZWF0ZWRseSByZWFzc2lnbmluZyBgbmV3VGFibGUgPC0gZihuZXdUYWJsZSlgIGFzIGFib3ZlLCB3ZSBjYW4gCnVzZSB0aGUgcGlwZSAtIGAlPiVgLiBUaGlzIHRha2VzIHRoZSBvdXRwdXQgb2Ygb25lIGZ1bmN0aW9uIGFuZCAiKnBpcGVzKiIgaXQKaW50byB0aGUgZmlyc3QgYXJndW1lbnQgb2YgdGhlIG5leHQgZnVuY3Rpb24gc28gdGhhdCB3ZSBkb24ndCBoYXZlIHRvIGtlZXAKc3BlY2lmeWluZyB0aGUgb2JqZWN0IHdlIGFyZSB3b3JraW5nIHdpdGg6CgpgYGB7ciBwaXBlLCBldmFsPUZBTFNFfQpuZXdUYWJsZSA8LSBzYW1wbGVpbmZvICU+JQogICAgZmlsdGVyKENlbGxUeXBlPT0iYmFzYWwiKSAlPiUKICAgIHNlbGVjdChDZWxsVHlwZSwgR3JvdXApICU+JSAKICAgIHJlbmFtZShDZWxsPUNlbGxUeXBlKQpgYGAKClRoaXMgaXMgYSBmYWlybHkgdHJpdmlhbCBleGFtcGxlIGFuZCB0aGUgYmVuZWZpdHMgbWF5IG5vdCBiZSBpbW1lZGlhdGVseSAKb2J2aW91cywgYnV0IG9uY2UgeW91IGdldCB1c2VkIHRvIHVzaW5nIGBkcGx5cmAgKGFuZCB0aGUgb3RoZXIgcmVsYXRlZAoidGlkeXZlcnNlIiBwYWNrYWdlcywgc3VjaCBhcyBgc3RyaW5ncmApIHlvdSdsbCBmaW5kIGl0IG11Y2ggbW9yZSBwb3dlcmZ1bCBhbmQgCmVhc3kgdG8gdXNlIHRoYW4gYmFzZSBSLiAKCldlIHdpbGwgZW5jb3VudGVyIGEgZmV3IG1vcmUgYGRwbHlyYCBjb21tYW5kcyBkdXJpbmcgdGhlIGNvdXJzZSwgd2Ugd2lsbCBleHBsYWluCnRoZWlyIHVzZSBhcyB3ZSBjb21lIHRvIHRoZW0uCgojIyBGb3JtYXQgdGhlIGRhdGEKCldlIHdpbGwgYmUgbWFuaXB1bGF0aW5nIGFuZCByZWZvcm1hdGluZyB0aGUgY291bnRzIG1hdHJpeCBpbnRvIGEgc3VpdGFibGUKZm9ybWF0IGZvciBERVNlcTIuCgpUaGUgYHNlcWRhdGFgIGlzIGEgYGRhdGFmcmFtZWAgaW4gd2hpY2ggdGhlIGZpcnN0IHNpeCBjb2x1bW5zIGNvbnRhaW4gYW5ub3RhdGlvbgppbmZvcm1hdGlvbiBhbmQgdGhlIHJlbWFpbmluZyBjb2x1bW5zIGNvbnRhaW4gdGhlIGNvdW50IGRhdGEuCgpERVNlcTIgcmVxdWlyZXMgYSBzaW1wbGUgb2JqZWN0IGNvbnRhaW5pbmcgb25seSB0aGUgY291bnQgZGF0YSwgd2UnbGwga2VlcCB0aGUKZ2VuZSBJRCBieSBzZXR0aW5nIHRoZW0gYXMgdGhlIHJvdyBuYW1lcy4KCkxldCdzIGNyZWF0ZSBuZXcgY291bnRzIGRhdGEgb2JqZWN0LCBgY291bnRkYXRhYCwgdGhhdCBjb250YWlucyBvbmx5IHRoZQpjb3VudHMgZm9yIHRoZSAxMiBzYW1wbGVzLiAgCgpPdXIgYHNhbXBsZWluZm9gIG9iamVjdCBjb250YWlucyBhIGNvbHVtbiB3aXRoIHRoZSBzYW1wbGUgbmFtZXMuIFdlIHNob3VsZAphZGp1c3QgdGhlIGNvbHVtbiBuYW1lcyBvZiBvdXIgbWF0cml4IHRvIG1hdGNoIHRoZW0gLSB3ZSBqdXN0IG5lZWQgdG8gcmVtb3ZlCnRoZSBgLmJhbWAgc3VmZml4LiAKCkl0IGlzIGFsc28gKmNyaXRpY2FsKiB0byBlbnN1cmUgdGhhdCB0aGUgc2FtcGxlcyBpbiB0aGUgY29sdW1ucyBhcmUgaW4gdGhlIHNhbWUKb3JkZXIgYXMgdGhlIHJvd3Mgb2YgYHNhbXBsZWluZm9gLiBXaGVuIHdlIGxvYWQgdGhlc2Ugb2JqZWN0cyBpbnRvIERFU2VxMiBmb3IgCnRoZSBhbmFseXNpcyBpdCB3aWxsIG5vdCBndWVzcyB3aGljaCByb3cgb2YgdGhlIGBzYW1wbGVpbmZvYCBiZWxvbmdzIHRvIHdoaWNoCmNvbHVtbiBvZiB0aGUgY291bnRzIG1hdHJpeCwgaXQgd2lsbCBhc3N1bWUgdGhlIHNhbWUgb3JkZXIuCgpXZSdsbCB1c2UgdG8gbmV3IGBkcGx5cmAgY29tbWFuZHM6CgoqIGBjb2x1bW5zX3RvX3Jvd25hbWVzYCB0byBzZXQgdGhlIHJvd25hbWVzIHVzaW5nIGEgbmFtZWQgY29sdW1uCiogYHJlbmFtZV9hbGxgIHdoaWNoIGFsbG93cyB0byByZW5hbWUgYWxsIHRoZSBjb2x1bW5zIHVzaW5nIGEgc3RyaW5nIGZ1bmN0aW9uCgpgYGB7ciBjcmVhdGVDb3VudE1hdHJpeH0KY291bnRkYXRhIDwtIHNlcWRhdGEgJT4lCiAgICBjb2x1bW5fdG9fcm93bmFtZXMoIkdlbmVpZCIpICU+JSAjIHR1cm4gdGhlIGdlbmVpZCBjb2x1bW4gaW50byByb3duYW1lcwogICAgcmVuYW1lX2FsbChzdHJfcmVtb3ZlLCAiLmJhbSIpICU+JSAjIHJlbW92ZSB0aGUgIi5iYW0iIGZyb20gdGhlIGNvbHVtbiBuYW1lcwogICAgc2VsZWN0KHNhbXBsZWluZm8kU2FtcGxlKSAlPiUgIyBrZWVwIHNhbXBsZSBjb2x1bW5zIHVzaW5nIHNhbXBsZWluZm8kU2FtcGxlCiAgICBhcy5tYXRyaXgoKQoKaGVhZChjb3VudGRhdGEpCmBgYAoKSGVyZSwgd2UgdXNlZCBgc3RyX3JlbW92ZWAgdG8gcmVtb3ZlIHRoZSB1bndhbnRlZCBzdWZmaXggZnJvbSB0aGUgY29sdW1uIG5hbWVzLgpUaGUgYHN0cmluZ3JgIHBhY2thZ2UgaGFzIGEgbG90cyBvZiB1c2VmdWwgZnVuY3Rpb25zIGZvciBtYW5pcHVsYXRpbmcgc3RyaW5ncyAKKHRleHQpLCBlLmcuIGBzdHJfcmVwbGFjZWAgb3IgYHN0cl9leHRyYWN0YC4KCgojIEZpbHRlcmluZyB0aGUgZ2VuZXMKCkZvciBtYW55IGFuYWx5c2lzIG1ldGhvZHMgaXQgaXMgYWR2aXNhYmxlIHRvIGZpbHRlciBvdXQgYXMgbWFueSBnZW5lcyBhcyAKcG9zc2libGUgcHJpb3IgdG8gc3RhcnRpbmcgdGhlIGFuYWx5c2lzIGluIG9yZGVyIHRvIGRlY3JlYXNlIHRoZSBpbXBhY3Qgb24gZmFzbGUKZGlzY292ZXJ5IHJhdGVzIHdoZW4gYXBwbHlpbmcgbXVsdGlwbGUgdGVzdGluZyBjb3JyZWN0aW9uLiBUaGlzIGlzIG5vcm1hbGx5IGRvbmUKYnkgZmlsdGVyaW5nIG91dCBnZW5lcyB3aXRoIGxvdyBudW1iZXJzIG9mIHJlYWRzLCB3aGljaCBhcmUgbGlrZWx5IHRvIGJlIAp1bmluZm9ybWF0aXZlLgoKV2l0aCBgREVTZXFgIHRoaXMgaXMgbm90IG5lY2Vzc2FyeSBhcyBpdCBhcHBsaWVzIGEgcHJvY2VzcyBpdCBjYWxscyBgaW5kZXBlbmRlbnQKZmlsdGVyaW5nYCBkdXJpbmcgdGhlIGFuYWx5c2lzIHByb2Nlc3MuIE9uIHRoZSBvdGhlciBoYW5kLCBzb21lIGZpbHRlcmluZyBmb3IgCmdlbmVzIHRoYXQgYXJlIHZlcnkgbG93bHkgZXhwcmVzc2VkIGRvZXMgcmVkdWNlIHRoZSBzaXplIG9mIHRoZSBkYXRhIG1hdHJpeCwgCm1lYW5pbmcgdGhhdCBsZXNzIG1lbW9yeSBpcyByZXF1aXJlZCBhbmQgcHJvY2Vzc2luZyBzdGVwcyBhcmUgY2FycmllZCBvdXQgCmZhc3Rlci4KCldlIHdpbGwga2VlcCBhbGwgZ2VuZXMgd2hlcmUgdGhlIHRvdGFsIG51bWJlciBvZiByZWFkcyBhY3Jvc3MgYWxsIHNhbXBsZXMgaXMgCmdyZWF0ZXIgdGhhbiA1LgoKYGBge3IgZmlsdGVyR2VuZXN9CmRpbShjb3VudGRhdGEpCmtlZXAgPC0gcm93U3Vtcyhjb3VudGRhdGEpID4gNQpjb3VudGRhdGEgPC0gY291bnRkYXRhW2tlZXAsXQpkaW0oY291bnRkYXRhKQpgYGAKCiMgUXVhbGl0eSBhc3Nlc3NtZW50CgpCZWZvcmUgbW92aW5nIG9uIHRvIGRvaW5nIHRoZSBhY3R1YWxseSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBpdCAKaW1wb3J0YW50IGRvIGFzc2VzcyB0aGUgcXVhbGl0eSBvZiBvdXIgZGF0YS4KCiMjIExpYnJhcnkgc2l6ZXMgYmFyIHBsb3QKCkZpcnN0LCB3ZSBjYW4gcGxvdCBob3cgbWFueSByZWFkcyB3ZSBoYXZlIGZvciBlYWNoIHNhbXBsZS4gV2hpbHN0IG5vcm1hbGlzYXRpb24gCmNhbiBhY2NvdW50IGZvciBpbWJhbGFuY2UgaW4gY292ZXJhZ2UgYWNyb3NzIHRoZSBzYW1wbGVzLCBleHRyZW1lIGRpZmZlcmVuY2VzIAptYXkgYmUgaW5kaWNhdGl2ZSBvZiB1bmRlcmx5aW5nIHByb2JsZW1zIGluIHRoZSBzYW1wbGVzLgoKYGBge3IgbGlicmFyeVNpemVzfQpsaWJyYXJ5U2l6ZXMgPC0gY29sU3Vtcyhjb3VudGRhdGEpCmJhcnBsb3QobGlicmFyeVNpemVzLCAKICAgICAgICBuYW1lcz1uYW1lcyhsaWJyYXJ5U2l6ZXMpLCAKICAgICAgICBsYXM9MiwgCiAgICAgICAgbWFpbj0iQmFycGxvdCBvZiBsaWJyYXJ5IHNpemVzIikKYWJsaW5lKGg9MjBlNiwgbHR5PTIpCmBgYAoKIyMgQ291bnQgZGlzdHJpYnV0aW9uIGJveHBsb3RzCgpDb3VudCBkYXRhIGlzIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZCwgc28gaWYgd2Ugd2FudCB0byBleGFtaW5lIHRoZQpkaXN0cmlidXRpb25zIG9mIHRoZSByYXcgY291bnRzIGl0IGlzIGhlbHBmdWwgdG8gdHJhbnNmb3JtIHRoZSBkYXRhIG9uIHRvIGEgbG9nCnNjYWxlLiBUeXBpY2FsbHkgd2UgdXNlIGEgYGxvZzJgIHRyYW5zZm9ybWF0aW9uLCBob3dldmVyLCBiZWNhdXNlIHRoZSBkYXRhIGlzCmNvdW50IGRhdGEgYW5kIHdpbGwgY29udGFpbiBtYW55IGAwYHMgd2UgbmVlZCB0byBhZGQgYSBjb3VudCBvZiAxIHRvIGV2ZXJ5IAp2YWx1ZSBpbiBvcmRlciB0byBwcmV2ZW50IGF0dGVtcHRpbmcgYGxvZzIoMClgIGZyb20gY3JlYXRpbmcgZXJyb3JzLgoKYGBge3IgbG9nVHJhbnNmb3JtfQojIEdldCBsb2cyIGNvdW50cyBwZXIgbWlsbGlvbgpsb2djb3VudHMgPC0gbG9nMihjb3VudGRhdGEgKyAxKQpgYGAKCldlJ2xsIGNoZWNrIHRoZSBkaXN0cmlidXRpb24gb2YgcmVhZCBjb3VudHMgdXNpbmcgYSBib3hwbG90IGFuZCB3ZWxsIGFkZCBzb21lCmNvbG91ciB0byBzZWUgaWYgdGhlcmUgaXMgYW55IGRpZmZlcmVuY2UgYmV0d2VlbiBzYW1wbGUgZ3JvdXBzLgoKYGBge3IgcGxvdExvZ0NvdW50c30KIyBtYWtlIGEgY29sb3VyIHZlY3RvcgpzdGF0dXNDb2wgPC0gYXMubnVtZXJpYyhmYWN0b3Ioc2FtcGxlaW5mbyRTdGF0dXMpKSArIDEKIyBDaGVjayBkaXN0cmlidXRpb25zIG9mIHNhbXBsZXMgdXNpbmcgYm94cGxvdHMKYm94cGxvdChsb2djb3VudHMsIAogICAgICAgIHhsYWI9IiIsIAogICAgICAgIHlsYWI9IkxvZzIoQ291bnRzKSIsCiAgICAgICAgbGFzPTIsCiAgICAgICAgY29sPXN0YXR1c0NvbCkKIyBMZXQncyBhZGQgYSBibHVlIGhvcml6b250YWwgbGluZSB0aGF0IGNvcnJlc3BvbmRzIHRvIHRoZSBtZWRpYW4gbG9nQ1BNCmFibGluZShoPW1lZGlhbihhcy5tYXRyaXgobG9nY291bnRzKSksIGNvbD0iYmx1ZSIpCmBgYAoKRnJvbSB0aGUgYm94cGxvdHMgd2Ugc2VlIHRoYXQgb3ZlcmFsbCB0aGUgZGVuc2l0eSBkaXN0cmlidXRpb25zIG9mIHJhdwpsb2ctaW50ZW5zaXRpZXMgYXJlIG5vdCBpZGVudGljYWwgYnV0IHN0aWxsIG5vdCB2ZXJ5IGRpZmZlcmVudC4gSWYgYSBzYW1wbGUgaXMKcmVhbGx5IGZhciBhYm92ZSBvciBiZWxvdyB0aGUgYmx1ZSBob3Jpem9udGFsIGxpbmUgd2UgbWF5IG5lZWQgdG8gaW52ZXN0aWdhdGUKdGhhdCBzYW1wbGUgZnVydGhlci4KCj4gIyMjIENoYWxsZW5nZSAxCj4KPiAxLiBVc2UgdGhlIGBERVNlcTJgIGZ1bmN0aW9uIGBybG9nYCB0byB0cmFuc2Zvcm0gdGhlIGNvdW50IGRhdGEuIFRoaXMgZnVuY3Rpb24KPiBhbHNvIG5vcm1hbGlzZXMgZm9yIGxpYnJhcnkgc2l6ZS4KPiAyLiBQbG90IHRoZSBjb3VudCBkaXN0cmlidXRpb24gYm94cGxvdHMgd2l0aCB0aGlzIGRhdGEKPiBIb3cgaGFzIHRoaXMgZWZmZWN0ZWQgdGhlIGNvdW50IGRpc3RyaWJ1dGlvbnM/CgpgYGB7ciBzb2x1dGlvbkNoYWxsZW5nZTF9CgoKYGBgCgoKIyMgUHJpbmNpcGxlIENvbXBvbmVudCBBbmFseXNpcwoKQSBwcmluY2lwbGUgY29tcG9uZW50cyBhbmFseXNpcyAoUENBKSBpcyBhbiBleGFtcGxlIG9mIGFuIHVuc3VwZXJ2aXNlZCBhbmFseXNpcywKd2hlcmUgd2UgZG9uJ3Qgc3BlY2lmeSB0aGUgZ3JvdXBpbmcgb2YgdGhlIHNhbXBsZXMuIElmIHlvdXIgZXhwZXJpbWVudCBpcyB3ZWxsCmNvbnRyb2xsZWQgYW5kIGhhcyB3b3JrZWQgd2VsbCwgd2Ugc2hvdWxkIHRoYXQgcmVwbGljYXRlIHNhbXBsZXMgY2x1c3RlciAKY2xvc2VseSwgd2hpbHN0IHRoZSBncmVhdGVzdCBzb3VyY2VzIG9mIHZhcmlhdGlvbiBpbiB0aGUgZGF0YSBzaG91bGQgYmUgYmV0d2Vlbgp0cmVhdG1lbnRzL3NhbXBsZSBncm91cHMuIEl0IGlzIGFsc28gYW4gaW5jcmVkaWJseSB1c2VmdWwgdG9vbCBmb3IgY2hlY2tpbmcgZm9yIApvdXRsaWVycyBhbmQgYmF0Y2ggZWZmZWN0cy4KClRvIHJ1biB0aGUgUENBIHdlIHNob3VsZCBmaXJzdCBub3JtYWxpc2Ugb3VyIGRhdGEgZm9yIGxpYnJhcnkgc2l6ZSBhbmQgdHJhbnNmb3JtCnRvIGEgbG9nIHNjYWxlLkRFU2VxMiBwcm92aWRlcyB0d28gY29tbWFuZHMgdGhhdCBjYW4gYmUgdXNlZCB0byBkbyB0aGlzLCBoZXJlIHdlCndpbGwgdXNlIHRoZSBjb21tYW5kIGBybG9nYC4gYHJsb2dgIHBlcmZvcm1zIGEgbG9nMiBzY2FsZSB0cmFuc2Zvcm1hdGlvbiBpbiBhIAp3YXkgdGhhdCBjb21wZW5zYXRlcyBmb3IgZGlmZmVyZW5jZXMgYmV0d2VlbiBzYW1wbGVzIGZvciBnZW5lcyB3aXRoIGxvdyByZWFkIApjb3VudCBhbmQgYWxzbyBub3JtYWxpemVzIGJldHdlZW4gc2FtcGxlcyBmb3IgbGlicmFyeSBzaXplLiAKCllvdSBjYW4gcmVhZCBtb3JlIGFib3V0IGBybG9nYCwgaXQncyBhbHRlcm5hdGl2ZSBgdnN0YCBhbmQgdGhlIGNvbXBhcmlzb24KYmV0d2VlbiB0aGUgdHdvIApbaGVyZV0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI2NvdW50LWRhdGEtdHJhbnNmb3JtYXRpb25zKS4gCgpUbyBwbG90IHRoZSBQQ0EgcmVzdWx0cyB3ZSB3aWxsIHVzZSB0aGUgYGF1dG9wbG90YCBmdW5jdGlvbiBmcm9tIHRoZSBgZ2dmb3J0aWZ5YApwYWNrYWdlIFtAVGFuZzIwMTZdLiBgZ2dmb3J0aWZ5YCBpcyBidWlsdCBvbiB0b3Agb2YgYGdncGxvdDJgIGFuZCBpcyBhYmxlIHRvIApyZWNvZ25pc2UgY29tbW9uIHN0YXRpc3RpY2FsIG9iamVjdHMgc3VjaCBhcyBQQ0EgcmVzdWx0cyBvciBsaW5lYXIgbW9kZWwgcmVzdWx0cyAKYW5kIGF1dG9tYXRpY2FsbHkgZ2VuZXJhdGUgc3VtbWFyeSBwbG90IG9mIHRoZSByZXN1bHRzIGluIGFuIGFwcHJvcHJpYXRlIG1hbm5lci4KCmBgYHtyIHBjYVBsb3QsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoPTYuNSwgZmlnLmhlaWdodD01LCBmaWcuYWxpZ249ImNlbnRlciJ9CmxpYnJhcnkoZ2dmb3J0aWZ5KQoKcmxvZ2NvdW50cyA8LSBybG9nKGNvdW50ZGF0YSkKCiMgcnVuIFBDQQpwY0RhdCA8LSBwcmNvbXAodChybG9nY291bnRzKSkKIyBwbG90IFBDQQphdXRvcGxvdChwY0RhdCkKIyBMZXRzIGFkZCBjb2xvdXIgdG8gbG9vayBhdCB0aGUgY2x1c3RlcmluZyBmb3IgU3RhdHVzCmF1dG9wbG90KHBjRGF0LAogICAgICAgICBkYXRhID0gc2FtcGxlaW5mbywgCiAgICAgICAgIGNvbG91cj0iU3RhdHVzIiwgCiAgICAgICAgIHNpemU9NSkKIyBhbmQgbm93IHN0YXR1cwojIExldHMgYWRkIGNvbG91ciB0byBsb29rIGF0IHRoZSBjbHVzdGVyaW5nIGZvciBDZWxsIFR5cGUKYXV0b3Bsb3QocGNEYXQsCiAgICAgICAgIGRhdGEgPSBzYW1wbGVpbmZvLCAKICAgICAgICAgY29sb3VyPSJDZWxsVHlwZSIsIAogICAgICAgICBzaXplPTUpCiMgV2UgY291bGQgdXNlIHNoYXBlIGZvciBvbmUgb2YgdGhlIGZhY3RvcnMKYXV0b3Bsb3QocGNEYXQsCiAgICAgICAgIGRhdGEgPSBzYW1wbGVpbmZvLCAKICAgICAgICAgY29sb3VyPSJTdGF0dXMiLCAKICAgICAgICAgc2hhcGU9IkNlbGxUeXBlIiwKICAgICAgICAgc2l6ZT01KQojIFNwZWNpZnkgc29tZSBjbGVhcmVyIHNoYXBlcyB0byB1c2UgdGhhdCBoYXZlIGEgYmxhY2sgb3V0bGluZSBhbmQgdXNlIGZpbGwKYXV0b3Bsb3QocGNEYXQsCiAgICAgICAgIGRhdGEgPSBzYW1wbGVpbmZvLCAKICAgICAgICAgZmlsbD0iU3RhdHVzIiwgCiAgICAgICAgIHNoYXBlPSJDZWxsVHlwZSIsCiAgICAgICAgIHNpemU9NSkgKwogICAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jKDIxLCAyNCkpICsKICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcz1saXN0KHNoYXBlPTIyKSkpCmBgYAoKPiAjIyMgRGlzY3Vzc2lvbgo+Cj4gTG9vayBhdCB0aGUgbGFzdCBQQ0EgcGxvdC4KPiBXaGF0IGlzIHRoZSBncmVhdGVzdCBzb3VyY2Ugb2YgdmFyaWF0aW9uPwo+IElzIHRoZXJlIHNvbWV0aGluZyBzdHJhbmdlIGdvaW5nIG9uIHdpdGggdGhlIHNhbXBsZXM/Cj4gTGV0J3MgaWRlbnRpZnkgdGhlc2Ugc2FtcGxlczoKCmBgYHtyIGJhZFNhbXBsZXMsIGZpZy53aWR0aD02LjUsIGZpZy5oZWlnaHQ9NSwgZmlnLmFsaWduPSJjZW50ZXIifQojIHNldHRpbmcgc2hhcGUgdG8gRkFMU0UgY2F1c2VzIHRoZSBwbG90IHRvIGRlZmF1bHQgdG8gdXNpbmcgdGhlIGxhYmVscwphdXRvcGxvdChwY0RhdCwKICAgICAgICAgZGF0YSA9IHNhbXBsZWluZm8sIAogICAgICAgICBjb2xvdXI9IkNlbGxUeXBlIiwgCiAgICAgICAgIHNoYXBlPUZBTFNFLAogICAgICAgICBsYWJlbC5zaXplPTYpCmBgYAoKVGhlIG1pc2xhYmVsbGVkIHNhbXBsZXMgYXJlICpNQ0wxLkRHKiwgd2hpY2ggaXMgbGFiZWxsZWQgYXMgKmx1bWluYWwqIGJ1dCBzaG91bGQKYmUgKmJhc2FsKiwgYW5kICpNQ0wxLkxBKiwgd2hpY2ggaXMgbGFiZWxsZWQgYXMgKmJhc2FsKiBidXQgc2hvdWxkIGJlICpsdW1pbmFsKi4KTGV0J3MgZml4IHRoZSBzYW1wbGUgc2hlZXQuLi4KCmBgYHtyIGNvcnJlY3RTYW1wbGVTaGVldH0Kc2FtcGxlaW5mbyA8LSBzYW1wbGVpbmZvICU+JSAKICAgIG11dGF0ZShDZWxsVHlwZT1pZmVsc2UoU2FtcGxlPT0iTUNMMS5ERyIsICJiYXNhbCIsIENlbGxUeXBlKSkgJT4lIAogICAgbXV0YXRlKENlbGxUeXBlPWlmZWxzZShTYW1wbGU9PSJNQ0wxLkxBIiwgImx1bWluYWwiLCBDZWxsVHlwZSkpICU+JSAKICAgIG11dGF0ZShHcm91cD1zdHJfYyhDZWxsVHlwZSwgIi4iLCBHcm91cCkpCmBgYAoKLi4uYW5kIGV4cG9ydCBpdCBzbyB0aGF0IHdlIGhhdmUgdGhlIGNvcnJlY3QgdmVyc2lvbiBmb3IgbGF0ZXIgdXNlLgoKYGBge3IsIGV4cG9ydFNhbXBsZVNoZWV0LCBldmFsPUZBTFNFfQp3cml0ZV9jc3Yoc2FtcGxlaW5mbywgInJlc3VsdHMvU2FtcGxlSW5mb19Db3JyZWN0ZWQudHh0IikKYGBgCgpMZXQncyBsb29rIGF0IHRoZSBQQ0Egbm93LgoKYGBge3IgY29ycmVjdGVkUENBLCBmaWcud2lkdGg9Ni41LCBmaWcuaGVpZ2h0PTUsIGZpZy5hbGlnbj0iY2VudGVyIn0KYXV0b3Bsb3QocGNEYXQsCiAgICAgICAgIGRhdGEgPSBzYW1wbGVpbmZvLCAKICAgICAgICAgZmlsbD0iU3RhdHVzIiwgCiAgICAgICAgIHNoYXBlPSJDZWxsVHlwZSIsCiAgICAgICAgIHNpemU9NSkgKwogICAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jKDIxLCAyNCkpICsKICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcz1saXN0KHNoYXBlPTIyKSkpCmBgYAoKPiAjIyMgRGlzY3Vzc2lvbgo+Cj4gV2hhdCBpcyB0aGUgZ3JlYXRlc3Qgc291cmNlIG9mIHZhcmlhdGlvbiBpbiB0aGUgZGF0YSAoaS5lLiB3aGF0IGRvZXMgZGltZW5zaW9uIDEgcmVwcmVzZW50KT8KPiBXaGF0IGlzIHRoZSBzZWNvbmQgZ3JlYXRlc3Qgc291cmNlIG9mIHZhcmlhdGlvbiBpbiB0aGUgZGF0YT8KPgoKUmVwbGljYXRlIHNhbXBsZXMgZnJvbSB0aGUgc2FtZSBncm91cCBjbHVzdGVyIHRvZ2V0aGVyIGluIHRoZSBwbG90LCB3aGlsZSAKc2FtcGxlcyBmcm9tIGRpZmZlcmVudCBncm91cHMgZm9ybSBzZXBhcmF0ZSBjbHVzdGVycy4gVGhpcyBpbmRpY2F0ZXMgdGhhdCB0aGUKZGlmZmVyZW5jZXMgYmV0d2VlbiBncm91cHMgYXJlIGxhcmdlciB0aGFuIHRob3NlIHdpdGhpbiBncm91cHMsIGkuZS4sCmRpZmZlcmVudGlhbCBleHByZXNzaW9uIGlzIGdyZWF0ZXIgdGhhbiB0aGUgdmFyaWFuY2UgYW5kIGNhbiBiZSBkZXRlY3RlZC4gVGhlCmRpZmZlcmVuY2VzIGJldHdlZW4gdmlyZ2luLCBwcmVnbmFudCBhbmQgbGFjdGF0aW5nIGFyZSBncmVhdGVyIGZvciBsdW1pbmFsIGNlbGxzCnRoYW4gZm9yIGJhc2FsLgoKQ2x1c3RlcmluZyBpbiB0aGUgUENBIHBsb3QgY2FuIGJlIHVzZWQgdG8gbW90aXZhdGUgY2hhbmdlcyB0byB0aGUgZGVzaWduCm1hdHJpeCBpbiBsaWdodCBvZiBwb3RlbnRpYWwgYmF0Y2ggZWZmZWN0cy4gRm9yIGV4YW1wbGUsIGltYWdpbmUgdGhhdCB0aGUKZmlyc3QgcmVwbGljYXRlIG9mIGVhY2ggZ3JvdXAgd2FzIHByZXBhcmVkIGF0IGEgc2VwYXJhdGUgdGltZSBmcm9tIHRoZSBzZWNvbmQKcmVwbGljYXRlLiBJZiB0aGUgUENBIHBsb3Qgc2hvd2VkIHNlcGFyYXRpb24gb2Ygc2FtcGxlcyBieSB0aW1lLCBpdCBtaWdodCBiZQp3b3J0aHdoaWxlIGluY2x1ZGluZyB0aW1lIGluIHRoZSBkb3duIHN0cmVhbSBhbmFseXNpcyB0byBhY2NvdW50IGZvciB0aGUKdGltZS1iYXNlZCBlZmZlY3QuCgojIyBQQ0EgYmV5b25kIHRoZSBmaXJzdCB0d28gZGltZW5zaW9ucwoKYGF1dG9wbG90YCBwbG90cyB0aGUgZmlyc3QgdHdvIGRpbWVuc2lvbnMgYXMgYSBkZWZhdWx0LCBob3dldmVyIHlvdSBjYW4gcGxvdCBoaWdoZXIgZGltZW5zaW9ucyB1c2luZyB0aGUgYHhgIGFuZCBgeWAgYXJndW1lbnRzLgoKYGBge3IgcGxvdFBDQTNhbmQ0LCBmaWcud2lkdGg9Ni41LCBmaWcuaGVpZ2h0PTUsIGZpZy5hbGlnbj0iY2VudGVyIn0KYXV0b3Bsb3QocGNEYXQsCiAgICAgICAgIGRhdGEgPSBzYW1wbGVpbmZvLCAKICAgICAgICAgZmlsbCA9ICJTdGF0dXMiLCAKICAgICAgICAgc2hhcGUgPSAiQ2VsbFR5cGUiLAogICAgICAgICBzaXplID0gNSwKICAgICAgICAgeCA9IDIsCiAgICAgICAgIHkgPSAzKSArCiAgICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWMoMjEsIDI0KSkgKwogICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzPWxpc3Qoc2hhcGU9MjIpKSkKYGBgCgojIyBJbnRlcmFjdGl2ZSBNRFMgUGxvdCB3aXRoIEdsaW1tYQoKQW5vdGhlciBhbHRlcm5hdGl2ZSBpcyB0byBnZW5lcmF0ZSBhIE11bHRpZGltZW5zaW9uYWwgc2NhbGluZyAoTURTKSBwbG90LiBNRFMgCmlzIHNpbWlsYXIgdG8gUENBLCBpbiBNRFMgdGhlIGRpc3RhbmNlIGJldHdlZW4gZWFjaCBwYWlyIG9mIHNhbXBsZXMgaW4gdGhlIE1EUwpwbG90IGlzIGNhbGN1bGF0ZWQgYXMgdGhlICdsZWFkaW5nIGZvbGQgY2hhbmdlJywgd2hpY2ggaXMgZGVmaW5lZCBhcyB0aGUKcm9vdC1tZWFuLXNxdWFyZSBvZiB0aGUgbGFyZ2VzdCA1MDAgbG9nMi1mb2xkIGNoYW5nZXMgYmV0d2VlbiB0aGF0IHBhaXIgb2YKc2FtcGxlcy4gVGhlICpHbGltbWEqIHBhY2thZ2UgY3JlYXRlcyBpbnRlcmFjdGl2ZSBwbG90cyB0aGF0IGFsbG93IHRoZSB1c2UgdG8gCmV4cGxvcmUgdGhlIGRpZmZlcmVudCBkaW1lbnNpb25zLgoKYGBge3IgZ2xpbW1hTURTLCBldmFsPUZBTFNFfQpsaWJyYXJ5KEdsaW1tYSkKZ2xNRFNQbG90KHJsb2djb3VudHMsIAogICAgICAgICAgbGFiZWxzID0gc2FtcGxlaW5mbyRTYW1wbGUsIAogICAgICAgICAgZ3JvdXBzID0gc2FtcGxlaW5mb1ssYygiQ2VsbFR5cGUiLCAiU3RhdHVzIildLCAKICAgICAgICAgIGZvbGRlciA9ICJtZHMiKQpgYGAKCipHbGltbWEqIHdhcyBjcmVhdGVkIHRvIG1ha2UgaW50ZXJhY3RpdmUgdmVyc2lvbnMgb2Ygc29tZSBvZiB0aGUgcG9wdWxhciBwbG90cwpmcm9tIHRoZSAqbGltbWEqIHBhY2thZ2UuIEF0IHByZXNlbnQgaXQgY2FuIGJlIHVzZWQgdG8gb2J0YWluIE1EUyBwbG90cy4gVGhlCm91dHB1dCBvZiBgZ2xNRFNQbG90YCBpcyBhbiBodG1sIHBhZ2UgKC9tZHMvTURTLVBsb3QuaHRtbCkgdGhhdCBzaG93cyB0aGUgTURTCnBsb3Qgb24gdGhlIGxlZnQsIGFuZCB0aGUgYW1vdW50IG9mIHZhcmlhdGlvbiBleHBsYWluZWQgYnkgZWFjaCBkaW1lbnNpb24gaW4gYQpiYXJwbG90IG9uIHRoZSByaWdodC4gVGhlIHVzZXIgY2FuIGhvdmVyIG92ZXIgcG9pbnRzIHRvIGZpbmQgb3V0IHNhbXBsZQppbmZvcm1hdGlvbiwgYW5kIHN3aXRjaCBiZXR3ZWVuIHN1Y2Nlc3NpdmUgZGltZW5zaW9ucyBpbiB0aGUgTURTIHBsb3QgYnkgCmNsaWNraW5nIG9uIHRoZSBiYXJzIGluIHRoZSBiYXJwbG90LiBUaGUgZGVmYXVsdCBNRFMgcGxvdHMgc2hvd3MgZGltZW5zaW9ucyAxIAphbmQgMi4KCiMjIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIHdpdGggaGVhdG1hcHMKCkFuIGFsdGVybmF0aXZlIHRvIFBDQSBwbG90cyBmb3IgZXhhbWluaW5nIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBzYW1wbGVzIGlzCnVzaW5nIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLiBIZWF0bWFwcyBhcmUgYSBuaWNlIHZpc3VhbGlzYXRpb24gdG8gZXhhbWluZQpoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBvZiB5b3VyIHNhbXBsZXMuIFdlIGNhbiBkbyB0aGlzIHVzaW5nIHRoZSBgaGVhdG1hcC4yYApmdW5jdGlvbiBmcm9tIHRoZSAqZ3Bsb3RzKiBwYWNrYWdlLiBJbiB0aGlzIGV4YW1wbGUgYGhlYXRtYXAuMmAgY2FsY3VsYXRlcyBhCm1hdHJpeCBvZiBldWNsaWRlYW4gZGlzdGFuY2VzIGZyb20gdGhlIGBsb2djb3VudHNgIG9iamVjdC4KClRoZSAqUkNvbG9yQnJld2VyKiBwYWNrYWdlIGhhcyBuaWNlciBjb2xvdXIgc2NoZW1lcywgYWNjZXNzZWQgdXNpbmcgdGhlCmBicmV3ZXIucGFsYCBmdW5jdGlvbi4gIlJkWWxCdSIgaXMgYSBjb21tb24gY2hvaWNlLCBhbmQgIlNwZWN0cmFsIiBpcyBhbHNvCm5pY2UuCgpOb3RlOlRoZSBgcG5nYCBmdW5jdGlvbiB3aWxsIGNyZWF0ZSBhIHBuZyBmaWxlIHRvIHNhdmUgdGhlIHBsb3RzIGNyZWF0ZWQKc3RyYWlnaHQgYWZ0ZXIsIGFuZCB3aWxsIGNsb3NlIHRoaXMgZmlsZSB3aGVuIGBkZXYub2ZmKClgIGlzIGNhbGxlZC4gVG8gc2VlCnlvdXIgcGxvdHMgaW50ZXJhY3RpdmVseSwgc2ltcGx5IG9taXQgdGhvc2UgdHdvIGxpbmVzLgoKV2UgZG9uJ3Qgd2FudCB0byBwbG90IGEgaGVhdG1hcCBvZiBhbGwgMjIwMTMgZ2VuZXMsIHNvIGxldCdzIHNlbGVjdCBkYXRhIGZvciB0aGUgCjUwMCBtb3N0IHZhcmlhYmxlIGdlbmVzIGFuZCBwbG90IHRoZSBoZWF0bWFwLgoKYGBge3IgZ2V0SE1EYXRhfQojIFdlIGVzdGltYXRlIHRoZSB2YXJpYW5jZSBmb3IgZWFjaCByb3cgaW4gdGhlIGxvZ2NvdW50cyBtYXRyaXgKY291bnRWYXIgPC0gYXBwbHkocmxvZ2NvdW50cywgMSwgdmFyKQojIEdldCB0aGUgcm93IG51bWJlcnMgZm9yIHRoZSB0b3AgNTAwIG1vc3QgdmFyaWFibGUgZ2VuZXMKaGlnaFZhciA8LSBvcmRlcihjb3VudFZhciwgZGVjcmVhc2luZz1UUlVFKVsxOjUwMF0KIyBTdWJzZXQgbG9nY291bnRzIG1hdHJpeApobURhdCA8LSBybG9nY291bnRzW2hpZ2hWYXIsXQpgYGAKCmBgYHtyIHBsb3RITSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkoZ3Bsb3RzKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKCiMgR2V0IHNvbWUgbmljZXIgY29sb3VycwpteXBhbGV0dGUgPC0gYnJld2VyLnBhbCgxMSwgIlJkWWxCdSIpCiMgaHR0cDovL2NvbG9yYnJld2VyMi5vcmcvI3R5cGU9c2VxdWVudGlhbCZzY2hlbWU9QnVHbiZuPTMKbW9yZWNvbHMgPC0gY29sb3JSYW1wUGFsZXR0ZShteXBhbGV0dGUpCiMgU2V0IHVwIGNvbG91ciB2ZWN0b3IgZm9yIGNlbGx0eXBlIHZhcmlhYmxlCmNvbC5jZWxsIDwtIGMoInB1cnBsZSIsIm9yYW5nZSIpW3NhbXBsZWluZm8kQ2VsbFR5cGVdCgojIFBsb3QgdGhlIGhlYXRtYXAKaGVhdG1hcC4yKGhtRGF0LCAKICAgICAgICAgIGNvbD1yZXYobW9yZWNvbHMoNTApKSwKICAgICAgICAgIHRyYWNlPSJjb2x1bW4iLCAKICAgICAgICAgIG1haW49IlRvcCA1MDAgbW9zdCB2YXJpYWJsZSBnZW5lcyBhY3Jvc3Mgc2FtcGxlcyIsCiAgICAgICAgICBDb2xTaWRlQ29sb3JzPWNvbC5jZWxsLHNjYWxlPSJyb3ciKQpgYGAKCj4gIyMjIENoYWxsZW5nZSAyICB7LmNoYWxsZW5nZX0KPgo+IFJlZG8gdGhlIGhlYXRtYXAgdXNpbmcgdGhlIHRvcCA1MDAgTEVBU1QgdmFyaWFibGUgZ2VuZXMuICAKPiBDaGFuZ2UgdGhlIGNvbG91ciBzY2hlbWUgdG8gIlBpWUciIGFuZCByZWRvIHRoZSBoZWF0bWFwLiBUcnkgYGJyZXdlci5wYWwuaW5mb2AgCj4gYW5kIGBkaXNwbGF5LmJyZXdlci5hbGxgIHRvIHNlZSB3aGF0IG90aGVyIGNvbG91ciBzY2hlbWVzIGFyZSBhdmFpbGFibGUuICAKPiBDaGFuZ2UgdGhlIHNhbXBsZSBuYW1lcyB0byBgZ3JvdXBgIHVzaW5nIHRoZSBgbGFiQ29sYCBhcmd1bWVudCAgCj4gUmVtb3ZlIHRoZSBnZW5lIG5hbWVzIGZyb20gdGhlIHJpZ2h0aGFuZCBzaWRlIG9mIHRoZSBwbG90IHVzaW5nIGBsYWJSb3dgICAKCmBgYHtyIHNvbHV0aW9uQ2hhbGxlbmdlMiwgZWNobz1GQUxTRSwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9Nn0KCgoKCmBgYAoKCi0tLS0tCgoKIyBDb252ZXJ0IGNvdW50cyB0byAqKkRFU2VxRGF0YVNldCoqIG9iamVjdAoKTmV4dCB3ZSdsbCBjcmVhdGUgYSBgREVTZXFEYXRhU2V0YCBvYmplY3QuIFRoaXMgaXMgYW4gb2JqZWN0IHVzZWQgYnkgYERFU2VxMmAgdG8Kc3RvcmUgY291bnQgZGF0YS4gSXQgaGFzIGEgbnVtYmVyIG9mIHNsb3RzIGZvciBzdG9yaW5nIGNvdW50IGRhdGEsIHNhbXBsZSAKaW5mb3JtYXRpb24sIHRoZSBtb2RlbCBkZXNpZ24gZm9yIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcywgYW5kCnZhcmlvdXMgb3RoZXIgcGFyYW1ldGVycyBhYm91dCB0aGUgZGF0YS4KCkluIHRoZSBzaW1wbGVzdCBmb3JtIHdlIG5lZWQgdG8gcHJvdmlkZSBjb3VudHMsIHNhbXBsZSBpbmZvcm1hdGlvbiBhbmQgYSBkZXNpZ24KZm9ybXVsYS4gRm9yIG5vdyB3ZSBqdXN0IHdhbnQgdG8gcGVyZm9ybSBzb21lIFFDIHNvIHdlIHdpbGwgcHJvdmlkZSBhIHNpbXBsZSAKbW9kZWwgd2hlcmUgd291bGQganVzdCBiZSBjb25jZXJuZWQgd2l0aCBjb250cmFzdGluZyB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24KYmV0d2VlbiBjZWxsIHR5cGVzLiBXZSBjYW4gY2hhbmdlIHRoZSBtb2RlbCBsYXRlciBpZiB3ZSB3YW50IHRvLgoKYGBge3IgbWFrZUREU09ian0KIyBmaXJzdCBsZXRzIGNoZWNrIHRoYXQgb3VyIHJvd3MgYW5kIGNvbHVtbnMgbWF0Y2gKYWxsKHNhbXBsZWluZm8kU2FtcGxlID09IGNvbG5hbWVzKGNvdW50ZGF0YSkpCiMgY3JlYXRlIHRoZSBkZXNpZ24gZm9ybXVsYQpkZXNpZ24gPC0gYXMuZm9ybXVsYSh+IENlbGxUeXBlKQojIGNyZWF0ZSB0aGUgREVTZXFEYXRhU2V0IG9iamVjdApkZHNPYmogPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBjb3VudGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSBzYW1wbGVpbmZvLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSBkZXNpZ24pCmBgYAoKIyMgTm9ybWFsaXNhdGlvbgoKVGhlIGBlc3RpbWF0ZVNpemVGYWN0b3JzYCBjb21tYW5kIG9mIGBERVNlcTJgIGFwcGxpZXMgdGhlICJtZWRpYW4gcmF0aW8gbWV0aG9kIgpvZiBub3JtYWxpc2F0aW9uW0BIdWJlcjIwMTBdLiBUaGlzIGdlbmVyYXRlcyBhIHNldCBvZiBub3JtYWxpemF0aW9uIGZhY3RvcnMgYW5kIAphZGRzIHRoZW0gdG8gdGhlIGBkZHNPYmpgIGluIHRoZSBgY29sY29sRGF0YWAgc2xvdC4KCmBgYHtyIGVzdGltYXRlU2l6ZUZhY3RvcnN9CiMgQXBwbHkgbm9ybWFsaXNhdGlvbiB0byBERFMgb2JqZWN0CmRkc09iaiA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzKGRkc09iaikKYGBgCgpUYWtlIGEgbG9vayBhdCB0aGUgbm9ybWFsaXNhdGlvbiBmYWN0b3JzIGZvciB0aGVzZSBzYW1wbGVzLgoKYGBge3Igdml6Tm9ybUZhY3RvcnN9CmRkc09iakBjb2xEYXRhJHNpemVGYWN0b3IKYGBgCgpBIG5vcm1hbGl6YXRpb24gZmFjdG9yIGJlbG93IG9uZSBpbmRpY2F0ZXMgdGhhdCB0aGUgbGlicmFyeSBzaXplIHdpbGwgYmUgc2NhbGVkCmRvd24sIGFzIHRoZXJlIGlzIG1vcmUgc3VwcHJlc3Npb24gKGkuZS4sIGNvbXBvc2l0aW9uIGJpYXMpIGluIHRoYXQgbGlicmFyeQpyZWxhdGl2ZSB0byB0aGUgb3RoZXIgbGlicmFyaWVzLiBUaGlzIGlzIGFsc28gZXF1aXZhbGVudCB0byBzY2FsaW5nIHRoZSBjb3VudHMKdXB3YXJkcyBpbiB0aGF0IHNhbXBsZS4gQ29udmVyc2VseSwgYSBmYWN0b3IgYWJvdmUgb25lIHNjYWxlcyB1cCB0aGUgbGlicmFyeQpzaXplIGFuZCBpcyBlcXVpdmFsZW50IHRvIGRvd25zY2FsaW5nIHRoZSBjb3VudHMuCgpUaGUgTUNMMS5MRSBhbmQgTUNMMS5MRiBoYXZlIG11Y2ggc21hbGxlciBub3JtYWxpc2F0aW9uIGZhY3RvcnMsIGFuZCBNQ0wxLkxBIGFuZApNQ0wxLkxCIGhhdmUgdGhlIGxhcmdlc3QuIElmIHdlIHBsb3QgTUFwbG90cyB1c2luZyB0aGUgYHBsb3RNQWAgaW4gYGxpbW1hYCBbUml0Y2hpZTIwMTVdIGZ1bmN0aW9uIGZvciB0aGVzZSBzYW1wbGVzLCB3ZSBzaG91bGQgYmUgYWJsZSB0byBzZWUgdGhlIApjb21wb3NpdGlvbiBiaWFzIHByb2JsZW0uIAoKVGhlIGBybG9nYCBkYXRhIChgbG9nY291bnRzYCkgaGFzIGFscmVhZHkgYmVlbiBub3JtYWxpc2VkIGZvciBib3RoIGxpYnJhcnkgc2l6ZSAKYW5kIGNvbXBvc2l0aW9uIGJpYXMuIExldCdzIGhhdmUgYSBsb29rIGF0IHRoZSByYXcgYGNvdW50c2RhdGFgOyB3ZSB3aWxsIG5lZWQgdG8gbG9nMiBzY2FsZSBpdCBmaXJzdC4KCmBgYHtyIHBsb3RSYXdNQSwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTAsIG1lc3NhZ2UgPSBGQUxTRX0KbGlicmFyeShsaW1tYSkKbG9nY291bnRzIDwtIGxvZzIoY291bnRkYXRhICsgMSkKCnBhcihtZnJvdz1jKDEsMikpCnBsb3RNQShsb2djb3VudHMsIGFycmF5ID0gNykKYWJsaW5lKGg9MCxjb2w9ImdyZXkiKQpwbG90TUEobG9nY291bnRzLCBhcnJheSA9IDExKQphYmxpbmUoaD0wLGNvbD0iZ3JleSIpCmBgYAoKVGhlIE1BIHBsb3RzIHNob3cgYXZlcmFnZSBleHByZXNzaW9uIChtZWFuOiB4LWF4aXMpIGFnYWluc3QKbG9nLWZvbGQtY2hhbmdlcyAoZGlmZmVyZW5jZTogeS1heGlzKS4gIEJlY2F1c2Ugb3VyIGBkZHNPYmpgIG9iamVjdCBjb250YWlucwp0aGUgbm9ybWFsaXNhdGlvbiBmYWN0b3JzLCBpZiB3ZSByZWRvIHRoZXNlIHBsb3RzIHVzaW5nIGBkZHNPYmpgLCB3ZSBzaG91bGQgc2VlCnRoZSBjb21wb3NpdGlvbiBiaWFzIHByb2JsZW0gaGFzIGJlZW4gc29sdmVkLgoKYGBge3IgcGxvdE5vcm1lZE1BLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMH0Kbm9ybWFsaXplZENvdW50cyA8LSBjb3VudHMoZGRzT2JqLCBub3JtYWxpemVkPVRSVUUpIApsb2dOb3JtYWxpemVkQ291bnRzIDwtIGxvZzIobm9ybWFsaXplZENvdW50cyArIDEpCgpwYXIobWZyb3c9YygxLDIpKQpwbG90TUEobG9nTm9ybWFsaXplZENvdW50cywgYXJyYXkgPSA3KQphYmxpbmUoaD0wLGNvbD0iZ3JleSIpCnBsb3RNQShsb2dOb3JtYWxpemVkQ291bnRzLCBhcnJheSA9IDExKQphYmxpbmUoaD0wLGNvbD0iZ3JleSIpCmBgYAoKPiAjIyMgQ2hhbGxlbmdlIDMKPgo+IFBsb3QgdGhlIGJpYXNlZCBhbmQgdW5iaWFzZWQgTUEgcGxvdHMgYm90aCBzYW1wbGVzIHNpZGUgYnkgc2lkZSB0byBzZWUgdGhlIAo+IGJlZm9yZSBhbmQgYWZ0ZXIgbm9ybWFsaXNhdGlvbi4KPgoKYGBge3Igc29sdXRpb25DaGFsbGVuZ2UzLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CgpgYGAKCiMjIEV4cG9ydCBkYXRhCgoqKldlIGNhbiBzYXZlIGEgZmV3IGRhdGEgb2JqZWN0cyB0byB1c2UgbGF0ZXIgc28gd2UgZG9uJ3QgaGF2ZSB0byByZXJ1biAKZXZlcnl0aGluZyoqCgpgYGB7ciBzYXZlRGF0YSwgZXZhbD1GfQpzYXZlKGNvdW50ZGF0YSwgc2FtcGxlaW5mbywgZmlsZT0icmVzdWx0cy9wcmVwcm9jZXNzaW5nLlJEYXRhIikKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBSZWZlcmVuY2VzCg==