Original Authors: Belinda Phipson, Anna Trigos, Matt Ritchie, Maria Doyle, Harriet Dashnow, Charity Law Based on the course RNAseq analysis in R delivered on May 11/12th 2016
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. These are aligned to a reference genome, then the number of reads mapped to each gene can be counted. This 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, today we will be starting from the count data and getting stuck into analysis.
Data import
Set up an RStudio project specifying the directory where you have saved the /data
directory.
First, let’s load all the packages we will need to analyse the data.
library(edgeR)
library(limma)
library(Glimma)
library(gplots)
library(org.Mm.eg.db)
library(RColorBrewer)
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). Both the raw data (sequence reads) and processed data (counts) can be downloaded from Gene Expression Omnibus database (GEO) under accession number GSE60450.
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.
The sampleinfo file contains basic information about the samples that we will need for the analysis today.
# Read the sample information into R
sampleinfo <- read.delim("data/SampleInfo.txt")
View(sampleinfo)
sampleinfo
Reading in the count data
We will first use the counts file as a starting point for our analysis. This data has already been aligned to the mouse genome. The command line tool featureCounts (Liao, Smyth, and Shi 2014) was used to count reads mapped to mouse genes from Refseq annotation (see the paper for details).
Let’s take a look at the data. You can use the head
command to see the first 6 lines. In RStudio the View
command will open the dataframe in a new tab. The dim
command will tell you how many rows and columns the data frame has.
# Read the data into R
seqdata <- read.delim("data/GSE60450_Lactation-GenewiseCounts.txt", stringsAsFactors = FALSE)
head(seqdata)
View(seqdata)
dim(seqdata)
The seqdata
object contains information about genes (one gene per row), the first column has the Entrez gene id, the second has the gene length and the remaining columns contain information about the number of reads aligning to the gene in each experimental sample. There are two replicates for each cell type and timepoint (detailed sample info can be found in file “GSE60450_series_matrix.txt” from the GEO website).
Filtering to remove lowly expressed genes
Genes with very low counts across all libraries provide little evidence for differential expression and they interfere with some of the statistical approximations that are used later in the pipeline. They also add to the multiple testing burden when estimating false discovery rates, reducing power to detect differentially expressed genes. These genes should be filtered out prior to further analysis.
There are a few ways to filter out lowly expressed genes. When there are biological replicates in each group, in this case we have a sample size of 2 in each group, we favour filtering on a minimum counts per million threshold present in at least 2 samples. Two represents the smallest sample size for each group in our experiment. In this dataset, we choose to retain genes if they are expressed at a counts-per-million (CPM) above 0.5 in at least two samples.
We’ll use the cpm
function from the edgeR library (M D Robinson, McCarthy, and Smyth 2010) to generate the CPM values and then filter. Note that by converting to CPMs we are normalising for the different sequencing depths for each sample.
# Obtain CPMs
myCPM <- cpm(countdata)
# Have a look at the output
head(myCPM)
# Which values in myCPM are greater than 0.5?
thresh <- myCPM > 0.5
# This produces a logical matrix with TRUEs and FALSEs
head(thresh)
# Summary of how many TRUEs there are in each row
# There are 11433 genes that have TRUEs in all 12 samples.
table(rowSums(thresh))
# we would like to keep genes that have at least 2 TRUES in each row of thresh
keep <- rowSums(thresh) >= 2
# Subset the rows of countdata to keep the more highly expressed genes
counts.keep <- countdata[keep,]
summary(keep)
dim(counts.keep)
A CPM of 0.5 is used as it corresponds to a count of 10-15 for the library sizes in this data set. If the count is any smaller, it is considered to be very low, indicating that the associated gene is not expressed in that sample. A requirement for expression in two or more libraries is used as each group contains two replicates. This ensures that a gene will be retained if it is only expressed in one group. Smaller CPM thresholds are usually appropriate for larger libraries. As a general rule, a good threshold can be chosen by identifying the CPM that corresponds to a count of 10, which in this case is about 0.5. You should filter with CPMs rather than filtering on the counts directly, as the latter does not account for differences in library sizes between samples.
# Let's have a look and see whether our threshold of 0.5 does indeed correspond to a count of about 10-15
# We will look at the first sample
plot(myCPM[,1],countdata[,1])
# Let us limit the x and y-axis so we can actually look to see what is happening at the smaller counts
plot(myCPM[,1],countdata[,1],ylim=c(0,50),xlim=c(0,3))
# Add a vertical line at 0.5 CPM
abline(v=0.5)
Challenge
- Plot the counts-per-million versus counts for the second sample.
- Add a vertical line at 0.5 and a horizontal line at 10.
- Add the lines again, colouring them blue
HINT: use the col
parameter.
Solution
Note: When in doubt, a threshold of 1 CPM in at least minimum group sample size is a good rule of thumb.
Convert counts to DGEList object
Next we’ll create a DGEList
object. This is an object used by edgeR to store count data. It has a number of slots for storing various parameters about the data.
dgeObj <- DGEList(counts.keep)
# have a look at dgeObj
dgeObj
# See what slots are stored in dgeObj
names(dgeObj)
# Library size information is stored in the samples slot
dgeObj$samples
Quality control
Now that we have got rid of the lowly expressed genes and have our counts stored in a DGEList
object, 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.
Library sizes and distribution plots
First, we can check how many reads we have for each sample in the dgeObj
.
dgeObj$samples$lib.size
We can also plot the library sizes as a barplot to see whether there are any major discrepanies between the samples more easily.
# The names argument tells the barplot to use the sample names on the x-axis
# The las argument rotates the axis names
barplot(dgeObj$samples$lib.size, names=colnames(dgeObj), las=2)
# Add a title to the plot
title("Barplot of library sizes")
Count data is not normally distributed, so if we want to examine the distributions of the raw counts we need to log the counts. Next we’ll use box plots to check the distribution of the read counts on the log2 scale. We can use the cpm
function to get log2 counts per million, which are corrected for the different library sizes. The cpm
function also adds a small offset to avoid taking log of zero.
# Get log2 counts per million
logcounts <- cpm(dgeObj,log=TRUE)
# Check distributions of samples using boxplots
boxplot(logcounts, xlab="", ylab="Log2 counts per million",las=2)
# Let's add a blue horizontal line that corresponds to the median logCPM
abline(h=median(logcounts),col="blue")
title("Boxplots of logCPMs (unnormalised)")
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.
Discussion
Do any samples appear to be different compared to the others?
Multidimensional scaling plots
By far, one of the most important plots we make when we analyse RNA-Seq data are MDSplots. An MDSplot is a visualisation of a principle components analysis, which determines the greatest sources of variation in the data. 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. We can use the plotMDS
function to create the MDS plot.
plotMDS(dgeObj)
It is a bit difficult to see exactly what is going on with the default plot, although we do see samples grouping together in pairs. To make this plot more informative, we can colour the samples according to the grouping information. We can also change the labels, or instead of labels we can have points.
# We specify the option to let us plot two plots side-by-sde
par(mfrow=c(1,2))
# Let's set up colour schemes for CellType
# How many cell types and in what order are they stored?
levels(sampleinfo$CellType)
## Let's choose purple for basal and orange for luminal
col.cell <- c("purple","orange")[sampleinfo$CellType]
data.frame(sampleinfo$CellType,col.cell)
# Redo the MDS with cell type colouring
plotMDS(dgeObj,col=col.cell)
# Let's add a legend to the plot so we know which colours correspond to which cell type
legend("topleft",fill=c("purple","orange"),legend=levels(sampleinfo$CellType))
# Add a title
title("Cell type")
# Similarly for status
levels(sampleinfo$Status)
col.status <- c("blue","red","dark green")[sampleinfo$Status]
col.status
plotMDS(dgeObj,col=col.status)
legend("topleft",fill=c("blue","red","dark green"),legend=levels(sampleinfo$Status),cex=0.8)
title("Status")
Discussion
Look at the MDS plot coloured by cell type. Is there something strange going on with the samples? Identify the two samples that don’t appear to be in the right place.
# There is a sample info corrected file in your data directory
# Old sampleinfo
sampleinfo
# I'm going to write over the sampleinfo object with the corrected sample info
sampleinfo <- read.delim("data/SampleInfo_Corrected.txt")
sampleinfo
# Redo the MDSplot with corrected information
par(mfrow=c(1,2))
col.cell <- c("purple","orange")[sampleinfo$CellType]
col.status <- c("blue","red","dark green")[sampleinfo$Status]
plotMDS(dgeObj,col=col.cell)
legend("topleft",fill=c("purple","orange"),legend=levels(sampleinfo$CellType))
title("Cell type")
plotMDS(dgeObj,col=col.status)
legend("topleft",fill=c("blue","red","dark green"),legend=levels(sampleinfo$Status),cex=0.8)
title("Status")
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?
Challenge
- Redo the plots choosing your own colours.
- Change the plotting character to a symbol instead of the column names
HINT: use pch
(plotting characters) argument. Try pch=16
and see what happens.
- Change the plotting characters such that basal samples have the value
1
and luminal samples have the value 4
and colour the points by status (lactate, pregnant, virgin)
Solution
The distance between each pair of samples in the MDS plot is calculated as the leading fold change, defined as the root-mean-square of the largest 500 log2-fold changes between that pair of samples. 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. In the MDS plot, the distance between basal samples on the left and luminal cells on the right is about 6 units, corresponding to a leading fold change of about 64-fold (2^6 = 64) between basal and luminal. The expression differences between virgin, pregnant and lactating are greater for luminal cells than for basal.
Notes
- The MDS plot can be simply generated with
plotMDS(dgeObj)
. The additional code is purely for aesthetics, to improve the visualization of the groups.
- Clustering in the MDS 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 MDS 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.
plotMDS
plots the first two dimensions as a default, however you can plot higher dimensions using the dim
argument.
# Dimension 3 appears to separate pregnant samples from the rest. Dim4?
plotMDS(dgeObj,dim=c(3,4))
Another alternative is to generate an interactive MDS plot using the Glimma package. This allows the user to interactively explore the different dimensions.
labels <- paste(sampleinfo$SampleName, sampleinfo$CellType, sampleinfo$Status)
group <- paste(sampleinfo$CellType,sampleinfo$Status,sep=".")
group <- factor(group)
glMDSPlot(dgeObj, labels=labels, groups=group, 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 and mean-difference (MD) plots, which will be covered later. 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 plotMDS
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 logCPM (logcounts
object) for the 500 most variable genes. (Note this has more complicated code than plotting principle components using plotMDS
.)
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.
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
var_genes <- apply(logcounts, 1, var)
head(var_genes)
# Get the gene names for the top 500 most variable genes
select_var <- names(sort(var_genes, decreasing=TRUE))[1:500]
head(select_var)
# Subset logcounts matrix
highly_variable_lcpm <- logcounts[select_var,]
dim(highly_variable_lcpm)
head(highly_variable_lcpm)
## Get some nicer colours
mypalette <- brewer.pal(11,"RdYlBu")
morecols <- colorRampPalette(mypalette)
# Set up colour vector for celltype variable
col.cell <- c("purple","orange")[sampleinfo$CellType]
# Plot the heatmap
heatmap.2(highly_variable_lcpm,
col=rev(morecols(50)),
trace="column",
main="Top 500 most variable genes across samples",
ColSideColors=col.cell,scale="row")
# Save the heatmap
png(file="High_var_genes.heatmap.png")
heatmap.2(highly_variable_lcpm,col=rev(morecols(50)),trace="none", main="Top 500 most variable genes\nacross samples",ColSideColors=col.cell,scale="row")
dev.off()
Challenge
- Change the colour scheme to “PiYG” and redo the heatmap. Try
?RColorBrewer
and see what other colour schemes are available.
- Change the sample names to
group
using the labCol
argument
- Redo the heatmap using the top 500 LEAST variable genes.
Solution
Normalisation for composition bias
The trimmed mean of M-values normalization method (TMM) is performed to eliminate composition biases between libraries (Mark D Robinson and Oshlack 2010). This generates a set of normalization factors, where the product of these factors and the library sizes defines the effective library size. The calcNormFactors
function in edgeR
calculates the normalization factors between libraries. TMM normalisation (and most scaling normalisation methods) scale relative to one sample.
# Apply normalisation to DGEList object
dgeObj <- calcNormFactors(dgeObj)
This will update the normalisation factors in the DGEList
object (their default values are 1). Take a look at the normalisation factors for these samples.
dgeObj$samples
The normalization factors multiply to unity across all libraries. 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 last two samples have much smaller normalisation factors, and MCL1.LA and MCL1.LB have the largest. If we plot mean difference plots using the plotMD
function for these samples, we should be able to see the composition bias problem. We will use the logcounts
, which have been normalised for library size, but not for composition bias.
par(mfrow=c(1,2))
plotMD(logcounts,column = 7)
abline(h=0,col="grey")
plotMD(logcounts,column = 11)
abline(h=0,col="grey")
The mean-difference plots show average expression (mean: x-axis) against log-fold-changes (difference: y-axis). Because our DGEList
object contains the normalisation factors, if we redo these plots using dgeObj
, we should see the composition bias problem has been solved.
par(mfrow=c(1,2))
plotMD(dgeObj,column = 7)
abline(h=0,col="grey")
plotMD(dgeObj,column = 11)
abline(h=0,col="grey")
Challenge
Plot the biased and unbiased MD plots side by side for the same sample to see the before and after TMM normalisation effect.
Solution
We need to save a few data objects to use for Day 2 so we don’t have to rerun everything
save(group,dgeObj,sampleinfo,file="Robjects/preprocessing.Rdata")
Fu, Nai Yang, Anne C Rios, Bhupinder Pal, Rina Soetanto, Aaron T L Lun, Kevin Liu, Tamara Beck, et al. 2015. “EGF-mediated induction of Mcl-1 at the switch to lactation is essential for alveolar cell survival.” Nature Cell Biology 17 (4): 365–75. doi:10.1038/ncb3117.
Liao, Yang, Gordon K Smyth, and Wei Shi. 2014. “featureCounts: an efficient general purpose program for assigning sequence reads to genomic features.” Bioinformatics (Oxford, England) 30 (7): 923–30. doi:10.1093/bioinformatics/btt656.
Lun, Aaron T L, Yunshun Chen, and Gordon K Smyth. 2016. “It’s DE-licious: A Recipe for Differential Expression Analyses of RNA-seq Experiments Using Quasi-Likelihood Methods in edgeR.” Methods in Molecular Biology (Clifton, N.J.) 1418 (January): 391–416. doi:10.1007/978-1-4939-3578-9\_19.
Robinson, M D, D J McCarthy, and G K Smyth. 2010. “edgeR: a Bioconductor package for differential expression analysis of digital gene expression data.” Bioinformatics 26 (1). Oxford Univ Press: 139–40.
Robinson, Mark D, and Alicia Oshlack. 2010. “A scaling normalization method for differential expression analysis of RNA-seq data.” Genome Biology 11 (3): R25. doi:10.1186/gb-2010-11-3-r25.
LS0tCnRpdGxlOiAiUk5BLXNlcSBhbmFseXNpcyBpbiBSIgphdXRob3I6ICJTdGVwaGFuZSBCYWxsZXJlYXUsIE1hcmsgRHVubmluZywgT3NjYXIgUnVlZGEsIEFzaGxleSBTYXdsZSIKZGF0ZTogJ2ByIGZvcm1hdChTeXMudGltZSgpLCAiTGFzdCBtb2RpZmllZDogJWQgJWIgJVkiKWAnCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwptaW51dGVzOiAzMDAKbGF5b3V0OiBwYWdlCnN1YnRpdGxlOiBQcmUtcHJvY2Vzc3NpbmcgUk5BLXNlcSBkYXRhCmJpYmxpb2dyYXBoeTogcmVmLmJpYgotLS0KCioqT3JpZ2luYWwgQXV0aG9yczogQmVsaW5kYSBQaGlwc29uLCBBbm5hIFRyaWdvcywgTWF0dCBSaXRjaGllLCBNYXJpYSBEb3lsZSwgSGFycmlldCBEYXNobm93LCBDaGFyaXR5IExhdyoqCkJhc2VkIG9uIHRoZSBjb3Vyc2UgW1JOQXNlcSBhbmFseXNpcyBpbiBSXShodHRwOi8vY29tYmluZS1hdXN0cmFsaWEuZ2l0aHViLmlvLzIwMTYtMDUtMTEtUk5Bc2VxLykgZGVsaXZlcmVkIG9uIE1heSAxMS8xMnRoIDIwMTYKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKCgojIyBSZXNvdXJjZXMgYW5kIGRhdGEgZmlsZXMKClRoaXMgbWF0ZXJpYWwgaGFzIGJlZW4gY3JlYXRlZCB1c2luZyB0aGUgZm9sbG93aW5nIHJlc291cmNlczogIApodHRwOi8vd3d3LnN0YXRzY2kub3JnL3NteXRoL3B1YnMvUUxlZGdlUlByZXByaW50LnBkZiBbQEx1bjIwMTZdICAKaHR0cDovL21vbmFzaGJpb2luZm9ybWF0aWNzcGxhdGZvcm0uZ2l0aHViLmlvL1JOQXNlcS1ERS1hbmFseXNpcy13aXRoLVIvOTktUk5Bc2VxX0RFX2FuYWx5c2lzX3dpdGhfUi5odG1sICAKCkRhdGEgZmlsZXMgZG93bmxvYWRlZCBmcm9tOiAgCmZ0cDovL2Z0cC5uY2JpLm5sbS5uaWguZ292L2dlby9zZXJpZXMvR1NFNjBubm4vR1NFNjA0NTAvc3VwcGwvR1NFNjA0NTBfTGFjdGF0aW9uLUdlbmV3aXNlQ291bnRzLnR4dC5negpodHRwOi8vYmlvaW5mLndlaGkuZWR1LmF1L3NvZnR3YXJlL01TaWdEQi9tb3VzZV9jMl92NS5yZGF0YQpodHRwOi8vYmlvaW5mLndlaGkuZWR1LmF1L3NvZnR3YXJlL01TaWdEQi9tb3VzZV9IX3Y1LnJkYXRhCgpEYXRhIGZpbGVzOiAgCnNhbXBsZWluZm8udHh0ICAKR1NFNjA0NTBfTGFjdGF0aW9uLUdlbmV3aXNlQ291bnRzLnR4dCAgCm1vdXNlX2MyX3Y1LnJkYXRhICAKbW91c2VfSF92NS5yZGF0YQoKRGF0YSBmaWxlcyBhdmFpbGFibGUgZnJvbTogW2h0dHBzOi8vZmlnc2hhcmUuY29tL3MvMWQ3ODhmZDM4NGQzM2U5MTNhMmFdKGh0dHBzOi8vZmlnc2hhcmUuY29tL3MvMWQ3ODhmZDM4NGQzM2U5MTNhMmEpCllvdSBzaG91bGQgZG93bmxvYWQgdGhlc2UgZmlsZXMgYW5kIHBsYWNlIHRoZW0gaW4geW91ciBgL2RhdGFgIGRpcmVjdG9yeS4KClBhY2thZ2VzIHVzZWQ6CmxpbW1hLAplZGdlUiwKZ3Bsb3RzLApvcmcuTW0uZWcuZGIsClJDb2xvckJyZXdlciwKR2xpbW1hCgojIyBPdmVydmlldwoKKiBSZWFkaW5nIGluIHRhYmxlIG9mIGNvdW50cwoqIEZpbHRlcmluZyBsb3dseSBleHByZXNzZWQgZ2VuZXMKKiBRdWFsaXR5IGNvbnRyb2wKKiBOb3JtYWxpc2F0aW9uIGZvciBjb21wb3NpdGlvbiBiaWFzCgoKIyMgSW50cm9kdWN0aW9uCgpNZWFzdXJpbmcgZ2VuZSBleHByZXNzaW9uIG9uIGEgZ2Vub21lLXdpZGUgc2NhbGUgaGFzIGJlY29tZSBjb21tb24gcHJhY3RpY2Ugb3ZlciB0aGUgbGFzdCB0d28gZGVjYWRlcyBvciBzbywgd2l0aCBtaWNyb2FycmF5cyBwcmVkb21pbmFudGx5IHVzZWQgcHJlLTIwMDguIFdpdGggdGhlIGFkdmVudCBvZiBuZXh0IGdlbmVyYXRpb24gc2VxdWVuY2luZyB0ZWNobm9sb2d5IGluIDIwMDgsIGFuIGluY3JlYXNpbmcgbnVtYmVyIG9mIHNjaWVudGlzdHMgdXNlIHRoaXMgdGVjaG5vbG9neSB0byBtZWFzdXJlIGFuZCB1bmRlcnN0YW5kIGNoYW5nZXMgaW4gZ2VuZSBleHByZXNzaW9uIGluIG9mdGVuIGNvbXBsZXggc3lzdGVtcy4gQXMgc2VxdWVuY2luZyBjb3N0cyBoYXZlIGRlY3JlYXNlZCwgdXNpbmcgUk5BLVNlcSB0byBzaW11bHRhbmVvdXNseSBtZWFzdXJlIHRoZSBleHByZXNzaW9uIG9mIHRlbnMgb2YgdGhvdXNhbmRzIG9mIGdlbmVzIGZvciBtdWx0aXBsZSBzYW1wbGVzIGhhcyBuZXZlciBiZWVuIGVhc2llci4gVGhlIGNvc3Qgb2YgdGhlc2UgZXhwZXJpbWVudHMgaGFzIG5vdyBtb3ZlZCBmcm9tIGdlbmVyYXRpbmcgdGhlIGRhdGEgdG8gc3RvcmluZyBhbmQgYW5hbHlzaW5nIGl0LgoKVGhlcmUgYXJlIG1hbnkgc3RlcHMgaW52b2x2ZWQgaW4gYW5hbHlzaW5nIGFuIFJOQS1TZXEgZXhwZXJpbWVudC4gQW5hbHlzaW5nIGFuIFJOQXNlcSBleHBlcmltZW50IGJlZ2lucyB3aXRoIHNlcXVlbmNpbmcgcmVhZHMuIFRoZXNlIGFyZSBhbGlnbmVkIHRvIGEgcmVmZXJlbmNlIGdlbm9tZSwgdGhlbiB0aGUgbnVtYmVyIG9mIHJlYWRzIG1hcHBlZCB0byBlYWNoIGdlbmUgY2FuIGJlIGNvdW50ZWQuIFRoaXMgcmVzdWx0cyBpbiBhIHRhYmxlIG9mIGNvdW50cywgd2hpY2ggaXMgd2hhdCB3ZSBwZXJmb3JtIHN0YXRpc3RpY2FsIGFuYWx5c2VzIG9uIGluIFIuIFdoaWxlIG1hcHBpbmcgYW5kIGNvdW50aW5nIGFyZSBpbXBvcnRhbnQgYW5kIG5lY2Vzc2FyeSB0YXNrcywgdG9kYXkgd2Ugd2lsbCBiZSBzdGFydGluZyBmcm9tIHRoZSBjb3VudCBkYXRhIGFuZCBnZXR0aW5nIHN0dWNrIGludG8gYW5hbHlzaXMuCgojIyBEYXRhIGltcG9ydAoqU2V0IHVwIGFuIFJTdHVkaW8gcHJvamVjdCBzcGVjaWZ5aW5nIHRoZSBkaXJlY3Rvcnkgd2hlcmUgeW91IGhhdmUgc2F2ZWQgdGhlIGAvZGF0YWAgZGlyZWN0b3J5Ki4KCkZpcnN0LCBsZXQncyBsb2FkIGFsbCB0aGUgcGFja2FnZXMgd2Ugd2lsbCBuZWVkIHRvIGFuYWx5c2UgdGhlIGRhdGEuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KGVkZ2VSKQpsaWJyYXJ5KGxpbW1hKQpsaWJyYXJ5KEdsaW1tYSkKbGlicmFyeShncGxvdHMpCmxpYnJhcnkob3JnLk1tLmVnLmRiKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKYGBgCgojIyMgTW91c2UgbWFtbWFyeSBnbGFuZCBkYXRhc2V0CgpUaGUgZGF0YSBmb3IgdGhpcyB0dXRvcmlhbCBjb21lcyBmcm9tIGEgTmF0dXJlIENlbGwgQmlvbG9neSBwYXBlciwgWypFR0YtbWVkaWF0ZWQgaW5kdWN0aW9uIG9mIE1jbC0xIGF0IHRoZSBzd2l0Y2ggdG8gbGFjdGF0aW9uIGlzIGVzc2VudGlhbCBmb3IgYWx2ZW9sYXIgY2VsbCBzdXJ2aXZhbCpdKGh0dHA6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wdWJtZWQvMjU3MzA0NzIpIFtARnUyMDE1XS4gQm90aCB0aGUgcmF3IGRhdGEgKHNlcXVlbmNlIHJlYWRzKSBhbmQgcHJvY2Vzc2VkIGRhdGEgKGNvdW50cykgY2FuIGJlIGRvd25sb2FkZWQgZnJvbSBHZW5lIEV4cHJlc3Npb24gT21uaWJ1cyBkYXRhYmFzZSAoR0VPKSB1bmRlciBhY2Nlc3Npb24gbnVtYmVyIFtHU0U2MDQ1MF0oaHR0cDovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU0U2MDQ1MCkuCgpUaGlzIHN0dWR5IGV4YW1pbmVzIHRoZSBleHByZXNzaW9uIHByb2ZpbGVzIG9mIGJhc2FsIHN0ZW0tY2VsbCBlbnJpY2hlZCBjZWxscyAoQikgYW5kIGNvbW1pdHRlZCBsdW1pbmFsIGNlbGxzIChMKSBpbiB0aGUgbWFtbWFyeSBnbGFuZCBvZiB2aXJnaW4sIHByZWduYW50IGFuZCBsYWN0YXRpbmcgbWljZS4gU2l4IGdyb3VwcyBhcmUgcHJlc2VudCwgd2l0aCBvbmUgZm9yIGVhY2ggY29tYmluYXRpb24gb2YgY2VsbCB0eXBlIGFuZCBtb3VzZSBzdGF0dXMuIEVhY2ggZ3JvdXAgY29udGFpbnMgdHdvIGJpb2xvZ2ljYWwgcmVwbGljYXRlcy4KClRoZSBzYW1wbGVpbmZvIGZpbGUgY29udGFpbnMgYmFzaWMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHNhbXBsZXMgdGhhdCB3ZSB3aWxsIG5lZWQgZm9yIHRoZSBhbmFseXNpcyB0b2RheS4KCmBgYHtyfQojIFJlYWQgdGhlIHNhbXBsZSBpbmZvcm1hdGlvbiBpbnRvIFIKc2FtcGxlaW5mbyA8LSByZWFkLmRlbGltKCJkYXRhL1NhbXBsZUluZm8udHh0IikKVmlldyhzYW1wbGVpbmZvKQpzYW1wbGVpbmZvCmBgYAoKIyMjIFJlYWRpbmcgaW4gdGhlICBjb3VudCBkYXRhCgpXZSB3aWxsIGZpcnN0IHVzZSB0aGUgY291bnRzIGZpbGUgYXMgYSBzdGFydGluZyBwb2ludCBmb3Igb3VyIGFuYWx5c2lzLiBUaGlzIGRhdGEgaGFzIGFscmVhZHkgYmVlbiBhbGlnbmVkIHRvIHRoZSBtb3VzZSBnZW5vbWUuIFRoZSBjb21tYW5kIGxpbmUgdG9vbCBmZWF0dXJlQ291bnRzIFtATGlhbzIwMTRdIHdhcyB1c2VkIHRvIGNvdW50IHJlYWRzIG1hcHBlZCB0byBtb3VzZSBnZW5lcyBmcm9tIFJlZnNlcSBhbm5vdGF0aW9uIChzZWUgdGhlIFtwYXBlcl0oaHR0cDovL3d3dy5uY2JpLm5sbS5uaWguZ292L3B1Ym1lZC8yNTczMDQ3MikgZm9yIGRldGFpbHMpLgoKTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGRhdGEuIFlvdSBjYW4gdXNlIHRoZSBgaGVhZGAgY29tbWFuZCB0byBzZWUgdGhlIGZpcnN0IDYgbGluZXMuIEluIFJTdHVkaW8gdGhlIGBWaWV3YCBjb21tYW5kIHdpbGwgb3BlbiB0aGUgZGF0YWZyYW1lIGluIGEgbmV3IHRhYi4gVGhlIGBkaW1gIGNvbW1hbmQgd2lsbCB0ZWxsIHlvdSBob3cgbWFueSByb3dzIGFuZCBjb2x1bW5zIHRoZSBkYXRhIGZyYW1lIGhhcy4KCmBgYHtyfQojIFJlYWQgdGhlIGRhdGEgaW50byBSCnNlcWRhdGEgPC0gcmVhZC5kZWxpbSgiZGF0YS9HU0U2MDQ1MF9MYWN0YXRpb24tR2VuZXdpc2VDb3VudHMudHh0Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpoZWFkKHNlcWRhdGEpClZpZXcoc2VxZGF0YSkKZGltKHNlcWRhdGEpCmBgYAoKVGhlIGBzZXFkYXRhYCBvYmplY3QgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgZ2VuZXMgKG9uZSBnZW5lIHBlciByb3cpLCB0aGUgZmlyc3QgY29sdW1uIGhhcyB0aGUgRW50cmV6IGdlbmUgaWQsIHRoZSBzZWNvbmQgaGFzIHRoZSBnZW5lIGxlbmd0aCBhbmQgdGhlIHJlbWFpbmluZyBjb2x1bW5zIGNvbnRhaW4gaW5mb3JtYXRpb24gYWJvdXQgdGhlIG51bWJlciBvZiByZWFkcyBhbGlnbmluZyB0byB0aGUgZ2VuZSBpbiBlYWNoIGV4cGVyaW1lbnRhbCBzYW1wbGUuIFRoZXJlIGFyZSB0d28gcmVwbGljYXRlcyBmb3IgZWFjaCBjZWxsIHR5cGUgYW5kIHRpbWVwb2ludCAoZGV0YWlsZWQgc2FtcGxlIGluZm8gY2FuIGJlIGZvdW5kIGluIGZpbGUgIkdTRTYwNDUwX3Nlcmllc19tYXRyaXgudHh0IiBmcm9tIHRoZSBbR0VPIHdlYnNpdGVdKGh0dHA6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9nZW8vcXVlcnkvYWNjLmNnaT9hY2M9R1NFNjA0NTApKS4gCgojIyMgRm9ybWF0IHRoZSBkYXRhCgpXZSB3aWxsIGJlIG1hbmlwdWxhdGluZyBhbmQgcmVmb3JtYXRpbmcgdGhlIGNvdW50cyBtYXRyaXggaW50byBhIHN1aXRhYmxlIGZvcm1hdCBmb3IgZG93bnN0cmVhbSBhbmFseXNpcy4gVGhlIGZpcnN0IHR3byBjb2x1bW5zIGluIHRoZSBgc2VxZGF0YWAgZGF0YWZyYW1lIGNvbnRhaW4gYW5ub3RhdGlvbiBpbmZvcm1hdGlvbi4gV2UgbmVlZCB0byBtYWtlIGEgbmV3IG1hdHJpeCBjb250YWluaW5nIG9ubHkgdGhlIGNvdW50cywgYnV0IHdlIGNhbiBzdG9yZSB0aGUgZ2VuZSBpZGVudGlmaWVycyAodGhlIGBFbnRyZXpHZW5lSURgIGNvbHVtbikgYXMgcm93bmFtZXMuIFdlIHdpbGwgYWRkIG1vcmUgYW5ub3RhdGlvbiBpbmZvcm1hdGlvbiBhYm91dCBlYWNoIGdlbmUgbGF0ZXIgb24gaW4gdGhlIHdvcmtzaG9wLgoKTGV0J3MgY3JlYXRlIGEgbmV3IGRhdGEgb2JqZWN0LCBgY291bnRkYXRhYCwgdGhhdCBjb250YWlucyBvbmx5IHRoZSBjb3VudHMgZm9yIHRoZSAxMiBzYW1wbGVzLiAgCgpgYGB7cn0KIyBSZW1vdmUgZmlyc3QgdHdvIGNvbHVtbnMgZnJvbSBzZXFkYXRhCmNvdW50ZGF0YSA8LSBzZXFkYXRhWywtKDE6MildCgojIFN0b3JlIEVudHJlekdlbmVJRCBhcyByb3duYW1lcwpyb3duYW1lcyhjb3VudGRhdGEpIDwtIHNlcWRhdGFbLDFdCgpWaWV3KGNvdW50ZGF0YSkKaGVhZChjb3VudGRhdGEpCmBgYAoKTm93IHRha2UgYSBsb29rIGF0IHRoZSBjb2x1bW4gbmFtZXMKCmBgYHtyfQpjb2xuYW1lcyhjb3VudGRhdGEpCmBgYAoKVGhlc2UgYXJlIHRoZSBzYW1wbGUgbmFtZXMgd2hpY2ggYXJlIHByZXR0eSBsb25nIHNvIHdlJ2xsIHNob3J0ZW4gdGhlc2UgdG8gY29udGFpbiBvbmx5IHRoZSByZWxldmFudCBpbmZvcm1hdGlvbiBhYm91dCBlYWNoIHNhbXBsZS4gV2Ugd2lsbCB1c2UgdGhlIGBzdWJzdHJgIGNvbW1hbmQgdG8gZXh0cmFjdCB0aGUgZmlyc3QgNyBjaGFyYWN0ZXJzIGFuZCB1c2UgdGhlc2UgYXMgdGhlIGNvbG5hbWVzLgoKYGBge3J9CnN1YnN0cigiVGhpc0lzQVN0cmluZyIsIHN0YXJ0PTEsIHN0b3A9NSkKIyB1c2luZyBzdWJzdHIsIHlvdSBleHRyYWN0IHRoZSBjaGFyYWN0ZXJzIHN0YXJ0aW5nIGF0IHBvc2l0aW9uIDEgYW5kIHN0b3BwaW5nIGF0IHBvc2l0aW9uIDcgb2YgdGhlIGNvbG5hbWVzCmNvbG5hbWVzKGNvdW50ZGF0YSkgPC0gc3Vic3RyKGNvbG5hbWVzKGNvdW50ZGF0YSksIDEsIDcpClZpZXcoY291bnRkYXRhKQpgYGAKCk5vdGUgdGhhdCB0aGUgY29sdW1uIG5hbWVzIGFyZSBub3cgdGhlIHNhbWUgYXMgYFNhbXBsZU5hbWVgIGluIHRoZSBgc2FtcGxlaW5mb2AgZmlsZS4gVGhpcyBpcyBnb29kIGJlY2F1c2UgaXQgbWVhbnMgb3VyIHNhbXBsZSBpbmZvcm1hdGlvbiBpbiBgc2FtcGxlaW5mb2AgaXMgaW4gdGhlIHNhbWUgb3JkZXIgYXMgdGhlIGNvbHVtbnMgaW4gYGNvdW50ZGF0YWAuCgpgYGB7cn0KdGFibGUoY29sbmFtZXMoY291bnRkYXRhKT09c2FtcGxlaW5mbyRTYW1wbGVOYW1lKQpgYGAKCgojIyBGaWx0ZXJpbmcgdG8gcmVtb3ZlIGxvd2x5IGV4cHJlc3NlZCBnZW5lcwoKR2VuZXMgd2l0aCB2ZXJ5IGxvdyBjb3VudHMgYWNyb3NzIGFsbCBsaWJyYXJpZXMgcHJvdmlkZSBsaXR0bGUgZXZpZGVuY2UgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuZCB0aGV5IGludGVyZmVyZSB3aXRoIHNvbWUgb2YgdGhlIHN0YXRpc3RpY2FsIGFwcHJveGltYXRpb25zIHRoYXQgYXJlIHVzZWQgbGF0ZXIgaW4gdGhlIHBpcGVsaW5lLiBUaGV5IGFsc28gYWRkIHRvIHRoZSBtdWx0aXBsZSB0ZXN0aW5nIGJ1cmRlbiB3aGVuIGVzdGltYXRpbmcgZmFsc2UgZGlzY292ZXJ5IHJhdGVzLCByZWR1Y2luZyBwb3dlciB0byBkZXRlY3QgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLiBUaGVzZSBnZW5lcyBzaG91bGQgYmUgZmlsdGVyZWQgb3V0IHByaW9yIHRvIGZ1cnRoZXIgYW5hbHlzaXMuCgpUaGVyZSBhcmUgYSBmZXcgd2F5cyB0byBmaWx0ZXIgb3V0IGxvd2x5IGV4cHJlc3NlZCBnZW5lcy4gV2hlbiB0aGVyZSBhcmUgYmlvbG9naWNhbCByZXBsaWNhdGVzIGluIGVhY2ggZ3JvdXAsIGluIHRoaXMgY2FzZSB3ZSBoYXZlIGEgc2FtcGxlIHNpemUgb2YgMiBpbiBlYWNoIGdyb3VwLCB3ZSBmYXZvdXIgZmlsdGVyaW5nIG9uIGEgbWluaW11bSBjb3VudHMgcGVyIG1pbGxpb24gdGhyZXNob2xkIHByZXNlbnQgaW4gYXQgbGVhc3QgMiBzYW1wbGVzLiBUd28gcmVwcmVzZW50cyB0aGUgc21hbGxlc3Qgc2FtcGxlIHNpemUgZm9yIGVhY2ggZ3JvdXAgaW4gb3VyIGV4cGVyaW1lbnQuIEluIHRoaXMgZGF0YXNldCwgd2UgY2hvb3NlIHRvIHJldGFpbiBnZW5lcyBpZiB0aGV5IGFyZSBleHByZXNzZWQgYXQgYSBjb3VudHMtcGVyLW1pbGxpb24gKENQTSkgYWJvdmUgMC41IGluIGF0IGxlYXN0IHR3byBzYW1wbGVzLgoKV2UnbGwgdXNlIHRoZSBgY3BtYCBmdW5jdGlvbiBmcm9tIHRoZSAqZWRnZVIqIGxpYnJhcnkgW0Byb2JpbnNvbjIwMTBlZGdlUl0gdG8gZ2VuZXJhdGUgdGhlIENQTSB2YWx1ZXMgYW5kIHRoZW4gZmlsdGVyLiBOb3RlIHRoYXQgYnkgY29udmVydGluZyB0byBDUE1zIHdlIGFyZSBub3JtYWxpc2luZyBmb3IgdGhlIGRpZmZlcmVudCBzZXF1ZW5jaW5nIGRlcHRocyBmb3IgZWFjaCBzYW1wbGUuCgpgYGB7cn0KIyBPYnRhaW4gQ1BNcwpteUNQTSA8LSBjcG0oY291bnRkYXRhKQojIEhhdmUgYSBsb29rIGF0IHRoZSBvdXRwdXQKaGVhZChteUNQTSkKYGBgCgoKYGBge3J9CiMgV2hpY2ggdmFsdWVzIGluIG15Q1BNIGFyZSBncmVhdGVyIHRoYW4gMC41Pwp0aHJlc2ggPC0gbXlDUE0gPiAwLjUKIyBUaGlzIHByb2R1Y2VzIGEgbG9naWNhbCBtYXRyaXggd2l0aCBUUlVFcyBhbmQgRkFMU0VzCmhlYWQodGhyZXNoKQpgYGAKCgpgYGB7cn0KCiMgU3VtbWFyeSBvZiBob3cgbWFueSBUUlVFcyB0aGVyZSBhcmUgaW4gZWFjaCByb3cKIyBUaGVyZSBhcmUgMTE0MzMgZ2VuZXMgdGhhdCBoYXZlIFRSVUVzIGluIGFsbCAxMiBzYW1wbGVzLgp0YWJsZShyb3dTdW1zKHRocmVzaCkpCgojIHdlIHdvdWxkIGxpa2UgdG8ga2VlcCBnZW5lcyB0aGF0IGhhdmUgYXQgbGVhc3QgMiBUUlVFUyBpbiBlYWNoIHJvdyBvZiB0aHJlc2gKa2VlcCA8LSByb3dTdW1zKHRocmVzaCkgPj0gMgojIFN1YnNldCB0aGUgcm93cyBvZiBjb3VudGRhdGEgdG8ga2VlcCB0aGUgbW9yZSBoaWdobHkgZXhwcmVzc2VkIGdlbmVzCmNvdW50cy5rZWVwIDwtIGNvdW50ZGF0YVtrZWVwLF0Kc3VtbWFyeShrZWVwKQpkaW0oY291bnRzLmtlZXApCmBgYAoKQSBDUE0gb2YgMC41IGlzIHVzZWQgYXMgaXQgY29ycmVzcG9uZHMgdG8gYSBjb3VudCBvZiAxMC0xNSBmb3IgdGhlIGxpYnJhcnkgc2l6ZXMgaW4gdGhpcyBkYXRhIHNldC4gSWYgdGhlIGNvdW50IGlzIGFueSBzbWFsbGVyLCBpdCBpcyBjb25zaWRlcmVkIHRvIGJlIHZlcnkgbG93LCBpbmRpY2F0aW5nIHRoYXQgdGhlIGFzc29jaWF0ZWQgZ2VuZSBpcyBub3QgZXhwcmVzc2VkIGluIHRoYXQgc2FtcGxlLiBBIHJlcXVpcmVtZW50IGZvciBleHByZXNzaW9uIGluIHR3byBvciBtb3JlIGxpYnJhcmllcyBpcyB1c2VkIGFzIGVhY2ggZ3JvdXAgY29udGFpbnMgdHdvIHJlcGxpY2F0ZXMuIFRoaXMgZW5zdXJlcyB0aGF0IGEgZ2VuZSB3aWxsIGJlIHJldGFpbmVkIGlmIGl0IGlzIG9ubHkgZXhwcmVzc2VkIGluIG9uZSBncm91cC4gU21hbGxlciBDUE0gdGhyZXNob2xkcyBhcmUgdXN1YWxseSBhcHByb3ByaWF0ZSBmb3IgbGFyZ2VyIGxpYnJhcmllcy4gQXMgYSBnZW5lcmFsIHJ1bGUsIGEgZ29vZCB0aHJlc2hvbGQgY2FuIGJlIGNob3NlbiBieSBpZGVudGlmeWluZyB0aGUgQ1BNIHRoYXQgY29ycmVzcG9uZHMgdG8gYSBjb3VudCBvZiAxMCwgd2hpY2ggaW4gdGhpcyBjYXNlIGlzIGFib3V0IDAuNS4gWW91IHNob3VsZCBmaWx0ZXIgd2l0aCBDUE1zIHJhdGhlciB0aGFuIGZpbHRlcmluZyBvbiB0aGUgY291bnRzIGRpcmVjdGx5LCBhcyB0aGUgbGF0dGVyIGRvZXMgbm90IGFjY291bnQgZm9yIGRpZmZlcmVuY2VzIGluIGxpYnJhcnkgc2l6ZXMgYmV0d2VlbiBzYW1wbGVzLgoKYGBge3J9CiMgTGV0J3MgaGF2ZSBhIGxvb2sgYW5kIHNlZSB3aGV0aGVyIG91ciB0aHJlc2hvbGQgb2YgMC41IGRvZXMgaW5kZWVkIGNvcnJlc3BvbmQgdG8gYSBjb3VudCBvZiBhYm91dCAxMC0xNQojIFdlIHdpbGwgbG9vayBhdCB0aGUgZmlyc3Qgc2FtcGxlCnBsb3QobXlDUE1bLDFdLGNvdW50ZGF0YVssMV0pCiMgTGV0IHVzIGxpbWl0IHRoZSB4IGFuZCB5LWF4aXMgc28gd2UgY2FuIGFjdHVhbGx5IGxvb2sgdG8gc2VlIHdoYXQgaXMgaGFwcGVuaW5nIGF0IHRoZSBzbWFsbGVyIGNvdW50cwpwbG90KG15Q1BNWywxXSxjb3VudGRhdGFbLDFdLHlsaW09YygwLDUwKSx4bGltPWMoMCwzKSkKIyBBZGQgYSB2ZXJ0aWNhbCBsaW5lIGF0IDAuNSBDUE0KYWJsaW5lKHY9MC41KQpgYGAKCj4gIyMgQ2hhbGxlbmdlIHsuY2hhbGxlbmdlfQo+Cj4gMS4gUGxvdCB0aGUgY291bnRzLXBlci1taWxsaW9uIHZlcnN1cyBjb3VudHMgZm9yIHRoZSBzZWNvbmQgc2FtcGxlLgo+IDEuIEFkZCBhIHZlcnRpY2FsIGxpbmUgYXQgMC41IGFuZCBhIGhvcml6b250YWwgbGluZSBhdCAxMC4KPiAxLiBBZGQgdGhlIGxpbmVzIGFnYWluLCBjb2xvdXJpbmcgdGhlbSBibHVlCj4KPiBISU5UOiB1c2UgdGhlIGBjb2xgIHBhcmFtZXRlci4KPgoKKipTb2x1dGlvbioqCmBgYHtyLGVjaG89RkFMU0V9CgpgYGAKTm90ZTogV2hlbiBpbiBkb3VidCwgYSB0aHJlc2hvbGQgb2YgMSBDUE0gaW4gYXQgbGVhc3QgKm1pbmltdW0qIGdyb3VwIHNhbXBsZSBzaXplIGlzIGEgZ29vZCBydWxlIG9mIHRodW1iLgoKIyMgQ29udmVydCBjb3VudHMgdG8gREdFTGlzdCBvYmplY3QKCk5leHQgd2UnbGwgY3JlYXRlIGEgYERHRUxpc3RgIG9iamVjdC4gVGhpcyBpcyBhbiBvYmplY3QgdXNlZCBieSAqZWRnZVIqIHRvIHN0b3JlIGNvdW50IGRhdGEuIEl0IGhhcyBhIG51bWJlciBvZiBzbG90cyBmb3Igc3RvcmluZyB2YXJpb3VzIHBhcmFtZXRlcnMgYWJvdXQgdGhlIGRhdGEuCgpgYGB7cn0KZGdlT2JqIDwtIERHRUxpc3QoY291bnRzLmtlZXApCiMgaGF2ZSBhIGxvb2sgYXQgZGdlT2JqCmRnZU9iagojIFNlZSB3aGF0IHNsb3RzIGFyZSBzdG9yZWQgaW4gZGdlT2JqCm5hbWVzKGRnZU9iaikKIyBMaWJyYXJ5IHNpemUgaW5mb3JtYXRpb24gaXMgc3RvcmVkIGluIHRoZSBzYW1wbGVzIHNsb3QKZGdlT2JqJHNhbXBsZXMKYGBgCgojIyBRdWFsaXR5IGNvbnRyb2wKCk5vdyB0aGF0IHdlIGhhdmUgZ290IHJpZCBvZiB0aGUgbG93bHkgZXhwcmVzc2VkIGdlbmVzIGFuZCBoYXZlIG91ciBjb3VudHMgc3RvcmVkIGluIGEgYERHRUxpc3RgIG9iamVjdCwgd2UgY2FuIGxvb2sgYXQgYSBmZXcgZGlmZmVyZW50IHBsb3RzIHRvIGNoZWNrIHRoYXQgdGhlIGRhdGEgaXMgZ29vZCBxdWFsaXR5LCBhbmQgdGhhdCB0aGUgc2FtcGxlcyBhcmUgYXMgd2Ugd291bGQgZXhwZWN0LgoKIyMjIExpYnJhcnkgc2l6ZXMgYW5kIGRpc3RyaWJ1dGlvbiBwbG90cwoKRmlyc3QsIHdlIGNhbiBjaGVjayBob3cgbWFueSByZWFkcyB3ZSBoYXZlIGZvciBlYWNoIHNhbXBsZSBpbiB0aGUgYGRnZU9iamAuCgpgYGB7cn0KZGdlT2JqJHNhbXBsZXMkbGliLnNpemUKYGBgCgpXZSBjYW4gYWxzbyBwbG90IHRoZSBsaWJyYXJ5IHNpemVzIGFzIGEgYmFycGxvdCB0byBzZWUgd2hldGhlciB0aGVyZSBhcmUgYW55IG1ham9yIGRpc2NyZXBhbmllcyBiZXR3ZWVuIHRoZSBzYW1wbGVzIG1vcmUgZWFzaWx5LgoKYGBge3J9CiMgVGhlIG5hbWVzIGFyZ3VtZW50IHRlbGxzIHRoZSBiYXJwbG90IHRvIHVzZSB0aGUgc2FtcGxlIG5hbWVzIG9uIHRoZSB4LWF4aXMKIyBUaGUgbGFzIGFyZ3VtZW50IHJvdGF0ZXMgdGhlIGF4aXMgbmFtZXMKYmFycGxvdChkZ2VPYmokc2FtcGxlcyRsaWIuc2l6ZSwgbmFtZXM9Y29sbmFtZXMoZGdlT2JqKSwgbGFzPTIpCiMgQWRkIGEgdGl0bGUgdG8gdGhlIHBsb3QKdGl0bGUoIkJhcnBsb3Qgb2YgbGlicmFyeSBzaXplcyIpCmBgYAoKQ291bnQgZGF0YSBpcyBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIHNvIGlmIHdlIHdhbnQgdG8gZXhhbWluZSB0aGUgZGlzdHJpYnV0aW9ucyBvZiB0aGUgcmF3IGNvdW50cyB3ZSBuZWVkIHRvIGxvZyB0aGUgY291bnRzLiBOZXh0IHdlJ2xsIHVzZSBib3ggcGxvdHMgdG8gY2hlY2sgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcmVhZCBjb3VudHMgb24gdGhlIGxvZzIgc2NhbGUuIFdlIGNhbiB1c2UgdGhlIGBjcG1gIGZ1bmN0aW9uIHRvIGdldCBsb2cyIGNvdW50cyBwZXIgbWlsbGlvbiwgd2hpY2ggYXJlIGNvcnJlY3RlZCBmb3IgdGhlIGRpZmZlcmVudCBsaWJyYXJ5IHNpemVzLiBUaGUgYGNwbWAgZnVuY3Rpb24gYWxzbyBhZGRzIGEgc21hbGwgb2Zmc2V0IHRvIGF2b2lkIHRha2luZyBsb2cgb2YgemVyby4gIAoKYGBge3J9CiMgR2V0IGxvZzIgY291bnRzIHBlciBtaWxsaW9uCmxvZ2NvdW50cyA8LSBjcG0oZGdlT2JqLGxvZz1UUlVFKQojIENoZWNrIGRpc3RyaWJ1dGlvbnMgb2Ygc2FtcGxlcyB1c2luZyBib3hwbG90cwpib3hwbG90KGxvZ2NvdW50cywgeGxhYj0iIiwgeWxhYj0iTG9nMiBjb3VudHMgcGVyIG1pbGxpb24iLGxhcz0yKQojIExldCdzIGFkZCBhIGJsdWUgaG9yaXpvbnRhbCBsaW5lIHRoYXQgY29ycmVzcG9uZHMgdG8gdGhlIG1lZGlhbiBsb2dDUE0KYWJsaW5lKGg9bWVkaWFuKGxvZ2NvdW50cyksY29sPSJibHVlIikKdGl0bGUoIkJveHBsb3RzIG9mIGxvZ0NQTXMgKHVubm9ybWFsaXNlZCkiKQpgYGAKCkZyb20gdGhlIGJveHBsb3RzIHdlIHNlZSB0aGF0IG92ZXJhbGwgdGhlIGRlbnNpdHkgZGlzdHJpYnV0aW9ucyBvZiByYXcgbG9nLWludGVuc2l0aWVzIGFyZSBub3QgaWRlbnRpY2FsIGJ1dCBzdGlsbCBub3QgdmVyeSBkaWZmZXJlbnQuIElmIGEgc2FtcGxlIGlzIHJlYWxseSBmYXIgYWJvdmUgb3IgYmVsb3cgdGhlIGJsdWUgaG9yaXpvbnRhbCBsaW5lIHdlIG1heSBuZWVkIHRvIGludmVzdGlnYXRlIHRoYXQgc2FtcGxlIGZ1cnRoZXIuCgo+ICMjIERpc2N1c3Npb24gey5jaGFsbGVuZ2V9Cj4KPiBEbyBhbnkgc2FtcGxlcyBhcHBlYXIgdG8gYmUgZGlmZmVyZW50IGNvbXBhcmVkIHRvIHRoZSBvdGhlcnM/Cj4KCgojIyMgTXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIHBsb3RzCgpCeSBmYXIsIG9uZSBvZiB0aGUgbW9zdCBpbXBvcnRhbnQgcGxvdHMgd2UgbWFrZSB3aGVuIHdlIGFuYWx5c2UgUk5BLVNlcSBkYXRhIGFyZSBNRFNwbG90cy4gQW4gTURTcGxvdCBpcyBhIHZpc3VhbGlzYXRpb24gb2YgYSBwcmluY2lwbGUgY29tcG9uZW50cyBhbmFseXNpcywgd2hpY2ggZGV0ZXJtaW5lcyB0aGUgZ3JlYXRlc3Qgc291cmNlcyBvZiB2YXJpYXRpb24gaW4gdGhlIGRhdGEuIEEgcHJpbmNpcGxlIGNvbXBvbmVudHMgYW5hbHlzaXMgaXMgYW4gZXhhbXBsZSBvZiBhbiB1bnN1cGVydmlzZWQgYW5hbHlzaXMsIHdoZXJlIHdlIGRvbid0IG5lZWQgdG8gc3BlY2lmeSB0aGUgZ3JvdXBzLiBJZiB5b3VyIGV4cGVyaW1lbnQgaXMgd2VsbCBjb250cm9sbGVkIGFuZCBoYXMgd29ya2VkIHdlbGwsIHdoYXQgd2UgaG9wZSB0byBzZWUgaXMgdGhhdCB0aGUgZ3JlYXRlc3Qgc291cmNlcyBvZiB2YXJpYXRpb24gaW4gdGhlIGRhdGEgYXJlIHRoZSB0cmVhdG1lbnRzL2dyb3VwcyB3ZSBhcmUgaW50ZXJlc3RlZCBpbi4gSXQgaXMgYWxzbyBhbiBpbmNyZWRpYmx5IHVzZWZ1bCB0b29sIGZvciBxdWFsaXR5IGNvbnRyb2wgYW5kIGNoZWNraW5nIGZvciBvdXRsaWVycy4gV2UgY2FuIHVzZSB0aGUgYHBsb3RNRFNgIGZ1bmN0aW9uIHRvIGNyZWF0ZSB0aGUgTURTIHBsb3QuCgpgYGB7cn0KcGxvdE1EUyhkZ2VPYmopCmBgYAoKSXQgaXMgYSBiaXQgZGlmZmljdWx0IHRvIHNlZSBleGFjdGx5IHdoYXQgaXMgZ29pbmcgb24gd2l0aCB0aGUgZGVmYXVsdCBwbG90LCBhbHRob3VnaCB3ZSBkbyBzZWUgc2FtcGxlcyBncm91cGluZyB0b2dldGhlciBpbiBwYWlycy4gVG8gbWFrZSB0aGlzIHBsb3QgbW9yZSBpbmZvcm1hdGl2ZSwgd2UgY2FuIGNvbG91ciB0aGUgc2FtcGxlcyBhY2NvcmRpbmcgdG8gdGhlIGdyb3VwaW5nIGluZm9ybWF0aW9uLiBXZSBjYW4gYWxzbyBjaGFuZ2UgdGhlIGxhYmVscywgb3IgaW5zdGVhZCBvZiBsYWJlbHMgd2UgY2FuIGhhdmUgcG9pbnRzLgoKYGBge3IsZmlnLmhlaWdodD01LGZpZy53aWR0aD0xMH0KIyBXZSBzcGVjaWZ5IHRoZSBvcHRpb24gdG8gbGV0IHVzIHBsb3QgdHdvIHBsb3RzIHNpZGUtYnktc2RlCnBhcihtZnJvdz1jKDEsMikpCiMgTGV0J3Mgc2V0IHVwIGNvbG91ciBzY2hlbWVzIGZvciBDZWxsVHlwZQojIEhvdyBtYW55IGNlbGwgdHlwZXMgYW5kIGluIHdoYXQgb3JkZXIgYXJlIHRoZXkgc3RvcmVkPwpsZXZlbHMoc2FtcGxlaW5mbyRDZWxsVHlwZSkKIyMgTGV0J3MgY2hvb3NlIHB1cnBsZSBmb3IgYmFzYWwgYW5kIG9yYW5nZSBmb3IgbHVtaW5hbApjb2wuY2VsbCA8LSBjKCJwdXJwbGUiLCJvcmFuZ2UiKVtzYW1wbGVpbmZvJENlbGxUeXBlXQpkYXRhLmZyYW1lKHNhbXBsZWluZm8kQ2VsbFR5cGUsY29sLmNlbGwpCgojIFJlZG8gdGhlIE1EUyB3aXRoIGNlbGwgdHlwZSBjb2xvdXJpbmcKcGxvdE1EUyhkZ2VPYmosY29sPWNvbC5jZWxsKQojIExldCdzIGFkZCBhIGxlZ2VuZCB0byB0aGUgcGxvdCBzbyB3ZSBrbm93IHdoaWNoIGNvbG91cnMgY29ycmVzcG9uZCB0byB3aGljaCBjZWxsIHR5cGUKbGVnZW5kKCJ0b3BsZWZ0IixmaWxsPWMoInB1cnBsZSIsIm9yYW5nZSIpLGxlZ2VuZD1sZXZlbHMoc2FtcGxlaW5mbyRDZWxsVHlwZSkpCiMgQWRkIGEgdGl0bGUKdGl0bGUoIkNlbGwgdHlwZSIpCgojIFNpbWlsYXJseSBmb3Igc3RhdHVzCmxldmVscyhzYW1wbGVpbmZvJFN0YXR1cykKY29sLnN0YXR1cyA8LSBjKCJibHVlIiwicmVkIiwiZGFyayBncmVlbiIpW3NhbXBsZWluZm8kU3RhdHVzXQpjb2wuc3RhdHVzCnBsb3RNRFMoZGdlT2JqLGNvbD1jb2wuc3RhdHVzKQpsZWdlbmQoInRvcGxlZnQiLGZpbGw9YygiYmx1ZSIsInJlZCIsImRhcmsgZ3JlZW4iKSxsZWdlbmQ9bGV2ZWxzKHNhbXBsZWluZm8kU3RhdHVzKSxjZXg9MC44KQp0aXRsZSgiU3RhdHVzIikKYGBgCgo+ICMjIERpc2N1c3Npb24gey5jaGFsbGVuZ2V9Cj4KPiBMb29rIGF0IHRoZSBNRFMgcGxvdCBjb2xvdXJlZCBieSBjZWxsIHR5cGUuCj4gSXMgdGhlcmUgc29tZXRoaW5nIHN0cmFuZ2UgZ29pbmcgb24gd2l0aCB0aGUgc2FtcGxlcz8KPiBJZGVudGlmeSB0aGUgdHdvIHNhbXBsZXMgdGhhdCBkb24ndCBhcHBlYXIgdG8gYmUgaW4gdGhlIHJpZ2h0IHBsYWNlLgo+CgpgYGB7cn0KIyBUaGVyZSBpcyBhIHNhbXBsZSBpbmZvIGNvcnJlY3RlZCBmaWxlIGluIHlvdXIgZGF0YSBkaXJlY3RvcnkKIyBPbGQgc2FtcGxlaW5mbwpzYW1wbGVpbmZvCiMgSSdtIGdvaW5nIHRvIHdyaXRlIG92ZXIgdGhlIHNhbXBsZWluZm8gb2JqZWN0IHdpdGggdGhlIGNvcnJlY3RlZCBzYW1wbGUgaW5mbwpzYW1wbGVpbmZvIDwtIHJlYWQuZGVsaW0oImRhdGEvU2FtcGxlSW5mb19Db3JyZWN0ZWQudHh0IikKc2FtcGxlaW5mbwpgYGAKCmBgYHtyLGZpZy5oZWlnaHQ9NSxmaWcud2lkdGg9MTB9CiMgUmVkbyB0aGUgTURTcGxvdCB3aXRoIGNvcnJlY3RlZCBpbmZvcm1hdGlvbgpwYXIobWZyb3c9YygxLDIpKQpjb2wuY2VsbCA8LSBjKCJwdXJwbGUiLCJvcmFuZ2UiKVtzYW1wbGVpbmZvJENlbGxUeXBlXQpjb2wuc3RhdHVzIDwtIGMoImJsdWUiLCJyZWQiLCJkYXJrIGdyZWVuIilbc2FtcGxlaW5mbyRTdGF0dXNdCnBsb3RNRFMoZGdlT2JqLGNvbD1jb2wuY2VsbCkKbGVnZW5kKCJ0b3BsZWZ0IixmaWxsPWMoInB1cnBsZSIsIm9yYW5nZSIpLGxlZ2VuZD1sZXZlbHMoc2FtcGxlaW5mbyRDZWxsVHlwZSkpCnRpdGxlKCJDZWxsIHR5cGUiKQpwbG90TURTKGRnZU9iaixjb2w9Y29sLnN0YXR1cykKbGVnZW5kKCJ0b3BsZWZ0IixmaWxsPWMoImJsdWUiLCJyZWQiLCJkYXJrIGdyZWVuIiksbGVnZW5kPWxldmVscyhzYW1wbGVpbmZvJFN0YXR1cyksY2V4PTAuOCkKdGl0bGUoIlN0YXR1cyIpCmBgYAoKPiAjIyBEaXNjdXNzaW9uIHsuY2hhbGxlbmdlfQo+Cj4gV2hhdCBpcyB0aGUgZ3JlYXRlc3Qgc291cmNlIG9mIHZhcmlhdGlvbiBpbiB0aGUgZGF0YSAoaS5lLiB3aGF0IGRvZXMgZGltZW5zaW9uIDEgcmVwcmVzZW50KT8KPiBXaGF0IGlzIHRoZSBzZWNvbmQgZ3JlYXRlc3Qgc291cmNlIG9mIHZhcmlhdGlvbiBpbiB0aGUgZGF0YT8KPgoKPiAjIyBDaGFsbGVuZ2Ugey5jaGFsbGVuZ2V9Cj4KPiAxLiBSZWRvIHRoZSBwbG90cyBjaG9vc2luZyB5b3VyIG93biBjb2xvdXJzLgo+IDEuIENoYW5nZSB0aGUgcGxvdHRpbmcgY2hhcmFjdGVyIHRvIGEgc3ltYm9sIGluc3RlYWQgb2YgdGhlIGNvbHVtbiBuYW1lcyAgCj4gSElOVDogdXNlIGBwY2hgICgqKnAqKmxvdHRpbmcgKipjaCoqYXJhY3RlcnMpIGFyZ3VtZW50LiBUcnkgYHBjaD0xNmAgYW5kIHNlZSB3aGF0IGhhcHBlbnMuCj4gMS4gQ2hhbmdlIHRoZSBwbG90dGluZyBjaGFyYWN0ZXJzIHN1Y2ggdGhhdCBiYXNhbCBzYW1wbGVzIGhhdmUgdGhlIHZhbHVlIGAxYCBhbmQgbHVtaW5hbCBzYW1wbGVzIGhhdmUgdGhlIHZhbHVlIGA0YCBhbmQgY29sb3VyIHRoZSBwb2ludHMgYnkgc3RhdHVzIChsYWN0YXRlLCBwcmVnbmFudCwgdmlyZ2luKQo+CgoqKlNvbHV0aW9uKioKYGBge3IgZWNobz1GQUxTRX0KYGBgCgoKVGhlIGRpc3RhbmNlIGJldHdlZW4gZWFjaCBwYWlyIG9mIHNhbXBsZXMgaW4gdGhlIE1EUyBwbG90IGlzIGNhbGN1bGF0ZWQgYXMgdGhlIGxlYWRpbmcgZm9sZCBjaGFuZ2UsIGRlZmluZWQgYXMgdGhlIHJvb3QtbWVhbi1zcXVhcmUgb2YgdGhlIGxhcmdlc3QgNTAwIGxvZzItZm9sZCBjaGFuZ2VzIGJldHdlZW4gdGhhdCBwYWlyIG9mIHNhbXBsZXMuIFJlcGxpY2F0ZSBzYW1wbGVzIGZyb20gdGhlIHNhbWUgZ3JvdXAgY2x1c3RlciB0b2dldGhlciBpbiB0aGUgcGxvdCwgd2hpbGUgc2FtcGxlcyBmcm9tIGRpZmZlcmVudCBncm91cHMgZm9ybSBzZXBhcmF0ZSBjbHVzdGVycy4gVGhpcyBpbmRpY2F0ZXMgdGhhdCB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBncm91cHMgYXJlIGxhcmdlciB0aGFuIHRob3NlIHdpdGhpbiBncm91cHMsIGkuZS4sIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGlzIGdyZWF0ZXIgdGhhbiB0aGUgdmFyaWFuY2UgYW5kIGNhbiBiZSBkZXRlY3RlZC4gSW4gdGhlIE1EUyBwbG90LCB0aGUgZGlzdGFuY2UgYmV0d2VlbiBiYXNhbCBzYW1wbGVzIG9uIHRoZSBsZWZ0IGFuZCBsdW1pbmFsIGNlbGxzIG9uIHRoZSByaWdodCBpcyBhYm91dCA2IHVuaXRzLCBjb3JyZXNwb25kaW5nIHRvIGEgbGVhZGluZyBmb2xkIGNoYW5nZSBvZiBhYm91dCA2NC1mb2xkICgyXjYgPSA2NCkgYmV0d2VlbiBiYXNhbCBhbmQgbHVtaW5hbC4gVGhlIGV4cHJlc3Npb24gZGlmZmVyZW5jZXMgYmV0d2VlbiB2aXJnaW4sIHByZWduYW50IGFuZCBsYWN0YXRpbmcgYXJlIGdyZWF0ZXIgZm9yIGx1bWluYWwgY2VsbHMgdGhhbiBmb3IgYmFzYWwuCgpOb3RlcwoKKiBUaGUgTURTIHBsb3QgY2FuIGJlIHNpbXBseSBnZW5lcmF0ZWQgd2l0aCBgcGxvdE1EUyhkZ2VPYmopYC4gVGhlIGFkZGl0aW9uYWwgY29kZSBpcyBwdXJlbHkgZm9yIGFlc3RoZXRpY3MsIHRvIGltcHJvdmUgdGhlIHZpc3VhbGl6YXRpb24gb2YgdGhlIGdyb3Vwcy4KKiBDbHVzdGVyaW5nIGluIHRoZSBNRFMgcGxvdCBjYW4gYmUgdXNlZCB0byBtb3RpdmF0ZSBjaGFuZ2VzIHRvIHRoZSBkZXNpZ24gbWF0cml4IGluIGxpZ2h0IG9mIHBvdGVudGlhbCBiYXRjaCBlZmZlY3RzLiBGb3IgZXhhbXBsZSwgaW1hZ2luZSB0aGF0IHRoZSBmaXJzdCByZXBsaWNhdGUgb2YgZWFjaCBncm91cCB3YXMgcHJlcGFyZWQgYXQgYSBzZXBhcmF0ZSB0aW1lIGZyb20gdGhlIHNlY29uZCByZXBsaWNhdGUuIElmIHRoZSBNRFMgcGxvdCBzaG93ZWQgc2VwYXJhdGlvbiBvZiBzYW1wbGVzIGJ5IHRpbWUsIGl0IG1pZ2h0IGJlIHdvcnRod2hpbGUgaW5jbHVkaW5nIHRpbWUgaW4gdGhlIGRvd24gc3RyZWFtIGFuYWx5c2lzIHRvIGFjY291bnQgZm9yIHRoZSB0aW1lLWJhc2VkIGVmZmVjdC4KCmBwbG90TURTYCBwbG90cyB0aGUgZmlyc3QgdHdvIGRpbWVuc2lvbnMgYXMgYSBkZWZhdWx0LCBob3dldmVyIHlvdSBjYW4gcGxvdCBoaWdoZXIgZGltZW5zaW9ucyB1c2luZyB0aGUgYGRpbWAgYXJndW1lbnQuCgpgYGB7cn0KIyBEaW1lbnNpb24gMyBhcHBlYXJzIHRvIHNlcGFyYXRlIHByZWduYW50IHNhbXBsZXMgZnJvbSB0aGUgcmVzdC4gRGltND8KcGxvdE1EUyhkZ2VPYmosZGltPWMoMyw0KSkKYGBgCgoKQW5vdGhlciBhbHRlcm5hdGl2ZSBpcyB0byBnZW5lcmF0ZSBhbiBpbnRlcmFjdGl2ZSBNRFMgcGxvdCB1c2luZyB0aGUgKkdsaW1tYSogcGFja2FnZS4gVGhpcyBhbGxvd3MgdGhlIHVzZXIgdG8gaW50ZXJhY3RpdmVseSBleHBsb3JlIHRoZSBkaWZmZXJlbnQgZGltZW5zaW9ucy4KCmBgYHtyfQpsYWJlbHMgPC0gcGFzdGUoc2FtcGxlaW5mbyRTYW1wbGVOYW1lLCBzYW1wbGVpbmZvJENlbGxUeXBlLCBzYW1wbGVpbmZvJFN0YXR1cykKZ3JvdXAgPC0gcGFzdGUoc2FtcGxlaW5mbyRDZWxsVHlwZSxzYW1wbGVpbmZvJFN0YXR1cyxzZXA9Ii4iKQpncm91cCA8LSBmYWN0b3IoZ3JvdXApCmdsTURTUGxvdChkZ2VPYmosIGxhYmVscz1sYWJlbHMsIGdyb3Vwcz1ncm91cCwgZm9sZGVyPSJtZHMiKQpgYGAKCipHbGltbWEqIHdhcyBjcmVhdGVkIHRvIG1ha2UgaW50ZXJhY3RpdmUgdmVyc2lvbnMgb2Ygc29tZSBvZiB0aGUgcG9wdWxhciBwbG90cyBmcm9tIHRoZSAqbGltbWEqIHBhY2thZ2UuIEF0IHByZXNlbnQgaXQgY2FuIGJlIHVzZWQgdG8gb2J0YWluIE1EUyBwbG90cyBhbmQgbWVhbi1kaWZmZXJlbmNlIChNRCkgcGxvdHMsIHdoaWNoIHdpbGwgYmUgY292ZXJlZCBsYXRlci4gVGhlIG91dHB1dCBvZiBgZ2xNRFNQbG90YCBpcyBhbiBodG1sIHBhZ2UgKC9tZHMvTURTLVBsb3QuaHRtbCkgdGhhdCBzaG93cyB0aGUgTURTIHBsb3Qgb24gdGhlIGxlZnQsIGFuZCB0aGUgYW1vdW50IG9mIHZhcmlhdGlvbiBleHBsYWluZWQgYnkgZWFjaCBkaW1lbnNpb24gaW4gYSBiYXJwbG90IG9uIHRoZSByaWdodC4gVGhlIHVzZXIgY2FuIGhvdmVyIG92ZXIgcG9pbnRzIHRvIGZpbmQgb3V0IHNhbXBsZSBpbmZvcm1hdGlvbiwgYW5kIHN3aXRjaCBiZXR3ZWVuIHN1Y2Nlc3NpdmUgZGltZW5zaW9ucyBpbiB0aGUgTURTIHBsb3QgYnkgY2xpY2tpbmcgb24gdGhlIGJhcnMgaW4gdGhlIGJhcnBsb3QuIFRoZSBkZWZhdWx0IE1EUyBwbG90cyBzaG93cyBkaW1lbnNpb25zIDEgYW5kIDIuCgojIyMgSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgd2l0aCBoZWF0bWFwcwoKQW4gYWx0ZXJuYXRpdmUgdG8gYHBsb3RNRFNgIGZvciBleGFtaW5pbmcgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHNhbXBsZXMgaXMgdXNpbmcgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcuIEhlYXRtYXBzIGFyZSBhIG5pY2UgdmlzdWFsaXNhdGlvbiB0byBleGFtaW5lIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIG9mIHlvdXIgc2FtcGxlcy4gV2UgY2FuIGRvIHRoaXMgdXNpbmcgdGhlIGBoZWF0bWFwLjJgIGZ1bmN0aW9uIGZyb20gdGhlICpncGxvdHMqIHBhY2thZ2UuIEluIHRoaXMgZXhhbXBsZSBgaGVhdG1hcC4yYCBjYWxjdWxhdGVzIGEgbWF0cml4IG9mIGV1Y2xpZGVhbiBkaXN0YW5jZXMgZnJvbSB0aGUgbG9nQ1BNIChgbG9nY291bnRzYCBvYmplY3QpIGZvciB0aGUgNTAwIG1vc3QgdmFyaWFibGUgZ2VuZXMuIChOb3RlIHRoaXMgaGFzIG1vcmUgY29tcGxpY2F0ZWQgY29kZSB0aGFuIHBsb3R0aW5nIHByaW5jaXBsZSBjb21wb25lbnRzIHVzaW5nIGBwbG90TURTYC4pCgpUaGUgKlJDb2xvckJyZXdlciogcGFja2FnZSBoYXMgbmljZXIgY29sb3VyIHNjaGVtZXMsIGFjY2Vzc2VkIHVzaW5nIHRoZSBgYnJld2VyLnBhbGAgZnVuY3Rpb24uICJSZFlsQnUiIGlzIGEgY29tbW9uIGNob2ljZSwgYW5kICJTcGVjdHJhbCIgaXMgYWxzbyBuaWNlLgoKTm90ZTpUaGUgYHBuZ2AgZnVuY3Rpb24gd2lsbCBjcmVhdGUgYSBwbmcgZmlsZSB0byBzYXZlIHRoZSBwbG90cyBjcmVhdGVkIHN0cmFpZ2h0IGFmdGVyLCBhbmQgd2lsbCBjbG9zZSB0aGlzIGZpbGUgd2hlbiBgZGV2Lm9mZigpYCBpcyBjYWxsZWQuIFRvIHNlZSB5b3VyIHBsb3RzIGludGVyYWN0aXZlbHksIHNpbXBseSBvbWl0IHRob3NlIHR3byBsaW5lcy4KCkxldCdzIHNlbGVjdCBkYXRhIGZvciB0aGUgNTAwIG1vc3QgdmFyaWFibGUgZ2VuZXMgYW5kIHBsb3QgdGhlIGhlYXRtYXAKCmBgYHtyfQojIFdlIGVzdGltYXRlIHRoZSB2YXJpYW5jZSBmb3IgZWFjaCByb3cgaW4gdGhlIGxvZ2NvdW50cyBtYXRyaXgKdmFyX2dlbmVzIDwtIGFwcGx5KGxvZ2NvdW50cywgMSwgdmFyKQpoZWFkKHZhcl9nZW5lcykKIyBHZXQgdGhlIGdlbmUgbmFtZXMgZm9yIHRoZSB0b3AgNTAwIG1vc3QgdmFyaWFibGUgZ2VuZXMKc2VsZWN0X3ZhciA8LSBuYW1lcyhzb3J0KHZhcl9nZW5lcywgZGVjcmVhc2luZz1UUlVFKSlbMTo1MDBdCmhlYWQoc2VsZWN0X3ZhcikKIyBTdWJzZXQgbG9nY291bnRzIG1hdHJpeApoaWdobHlfdmFyaWFibGVfbGNwbSA8LSBsb2djb3VudHNbc2VsZWN0X3ZhcixdCmRpbShoaWdobHlfdmFyaWFibGVfbGNwbSkKaGVhZChoaWdobHlfdmFyaWFibGVfbGNwbSkKCiMjIEdldCBzb21lIG5pY2VyIGNvbG91cnMKbXlwYWxldHRlIDwtIGJyZXdlci5wYWwoMTEsIlJkWWxCdSIpCm1vcmVjb2xzIDwtIGNvbG9yUmFtcFBhbGV0dGUobXlwYWxldHRlKQojIFNldCB1cCBjb2xvdXIgdmVjdG9yIGZvciBjZWxsdHlwZSB2YXJpYWJsZQpjb2wuY2VsbCA8LSBjKCJwdXJwbGUiLCJvcmFuZ2UiKVtzYW1wbGVpbmZvJENlbGxUeXBlXQoKIyBQbG90IHRoZSBoZWF0bWFwCmhlYXRtYXAuMihoaWdobHlfdmFyaWFibGVfbGNwbSwgCiAgICAgICAgICBjb2w9cmV2KG1vcmVjb2xzKDUwKSksCiAgICAgICAgICB0cmFjZT0iY29sdW1uIiwgCiAgICAgICAgICBtYWluPSJUb3AgNTAwIG1vc3QgdmFyaWFibGUgZ2VuZXMgYWNyb3NzIHNhbXBsZXMiLAogICAgICAgICAgQ29sU2lkZUNvbG9ycz1jb2wuY2VsbCxzY2FsZT0icm93IikKCiMgU2F2ZSB0aGUgaGVhdG1hcApwbmcoZmlsZT0iSGlnaF92YXJfZ2VuZXMuaGVhdG1hcC5wbmciKQpoZWF0bWFwLjIoaGlnaGx5X3ZhcmlhYmxlX2xjcG0sY29sPXJldihtb3JlY29scyg1MCkpLHRyYWNlPSJub25lIiwgbWFpbj0iVG9wIDUwMCBtb3N0IHZhcmlhYmxlIGdlbmVzXG5hY3Jvc3Mgc2FtcGxlcyIsQ29sU2lkZUNvbG9ycz1jb2wuY2VsbCxzY2FsZT0icm93IikKZGV2Lm9mZigpCmBgYAoKPiAjIyBDaGFsbGVuZ2Ugey5jaGFsbGVuZ2V9Cj4KPiAxLiBDaGFuZ2UgdGhlIGNvbG91ciBzY2hlbWUgdG8gIlBpWUciIGFuZCByZWRvIHRoZSBoZWF0bWFwLiBUcnkgYD9SQ29sb3JCcmV3ZXJgIGFuZCBzZWUgd2hhdCBvdGhlciBjb2xvdXIgc2NoZW1lcyBhcmUgYXZhaWxhYmxlLgo+IDEuIENoYW5nZSB0aGUgc2FtcGxlIG5hbWVzIHRvIGBncm91cGAgdXNpbmcgdGhlIGBsYWJDb2xgIGFyZ3VtZW50Cj4gMS4gUmVkbyB0aGUgaGVhdG1hcCB1c2luZyB0aGUgdG9wIDUwMCBMRUFTVCB2YXJpYWJsZSBnZW5lcy4KPgoKKipTb2x1dGlvbioqCmBgYHtyLGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTZ9CgpgYGAKCgotLS0tLQoKIyMgTm9ybWFsaXNhdGlvbiBmb3IgY29tcG9zaXRpb24gYmlhcwoKVGhlIHRyaW1tZWQgbWVhbiBvZiBNLXZhbHVlcyBub3JtYWxpemF0aW9uIG1ldGhvZCAoVE1NKSBpcyBwZXJmb3JtZWQgdG8gZWxpbWluYXRlIGNvbXBvc2l0aW9uIGJpYXNlcyBiZXR3ZWVuIGxpYnJhcmllcyBbQHJvYmluc29uMjAxMHRtbV0uIFRoaXMgZ2VuZXJhdGVzIGEgc2V0IG9mIG5vcm1hbGl6YXRpb24gZmFjdG9ycywgd2hlcmUgdGhlIHByb2R1Y3Qgb2YgdGhlc2UgZmFjdG9ycyBhbmQgdGhlIGxpYnJhcnkgc2l6ZXMgZGVmaW5lcyB0aGUgZWZmZWN0aXZlIGxpYnJhcnkgc2l6ZS4gVGhlIGBjYWxjTm9ybUZhY3RvcnNgIGZ1bmN0aW9uIGluIGBlZGdlUmAgY2FsY3VsYXRlcyB0aGUgbm9ybWFsaXphdGlvbiBmYWN0b3JzIGJldHdlZW4gbGlicmFyaWVzLiBUTU0gbm9ybWFsaXNhdGlvbiAoYW5kIG1vc3Qgc2NhbGluZyBub3JtYWxpc2F0aW9uIG1ldGhvZHMpIHNjYWxlIHJlbGF0aXZlIHRvIG9uZSBzYW1wbGUuCgpgYGB7cn0KIyBBcHBseSBub3JtYWxpc2F0aW9uIHRvIERHRUxpc3Qgb2JqZWN0CmRnZU9iaiA8LSBjYWxjTm9ybUZhY3RvcnMoZGdlT2JqKQpgYGAKClRoaXMgd2lsbCB1cGRhdGUgdGhlIG5vcm1hbGlzYXRpb24gZmFjdG9ycyBpbiB0aGUgYERHRUxpc3RgIG9iamVjdCAodGhlaXIgZGVmYXVsdCB2YWx1ZXMgYXJlIDEpLiBUYWtlIGEgbG9vayBhdCB0aGUgbm9ybWFsaXNhdGlvbiBmYWN0b3JzIGZvciB0aGVzZSBzYW1wbGVzLgoKYGBge3J9CmRnZU9iaiRzYW1wbGVzCmBgYAoKVGhlIG5vcm1hbGl6YXRpb24gZmFjdG9ycyBtdWx0aXBseSB0byB1bml0eSBhY3Jvc3MgYWxsIGxpYnJhcmllcy4gQSBub3JtYWxpemF0aW9uIGZhY3RvciBiZWxvdyBvbmUgaW5kaWNhdGVzIHRoYXQgdGhlIGxpYnJhcnkgc2l6ZSB3aWxsIGJlIHNjYWxlZCBkb3duLCBhcyB0aGVyZSBpcyBtb3JlIHN1cHByZXNzaW9uIChpLmUuLCBjb21wb3NpdGlvbiBiaWFzKSBpbiB0aGF0IGxpYnJhcnkgcmVsYXRpdmUgdG8gdGhlIG90aGVyIGxpYnJhcmllcy4gVGhpcyBpcyBhbHNvIGVxdWl2YWxlbnQgdG8gc2NhbGluZyB0aGUgY291bnRzIHVwd2FyZHMgaW4gdGhhdCBzYW1wbGUuIENvbnZlcnNlbHksIGEgZmFjdG9yIGFib3ZlIG9uZSBzY2FsZXMgdXAgdGhlIGxpYnJhcnkgc2l6ZSBhbmQgaXMgZXF1aXZhbGVudCB0byBkb3duc2NhbGluZyB0aGUgY291bnRzLgoKClRoZSBsYXN0IHR3byBzYW1wbGVzIGhhdmUgbXVjaCBzbWFsbGVyIG5vcm1hbGlzYXRpb24gZmFjdG9ycywgYW5kIE1DTDEuTEEgYW5kIE1DTDEuTEIgaGF2ZSB0aGUgbGFyZ2VzdC4gSWYgd2UgcGxvdCBtZWFuIGRpZmZlcmVuY2UgcGxvdHMgdXNpbmcgdGhlIGBwbG90TURgIGZ1bmN0aW9uIGZvciB0aGVzZSBzYW1wbGVzLCB3ZSBzaG91bGQgYmUgYWJsZSB0byBzZWUgdGhlIGNvbXBvc2l0aW9uIGJpYXMgcHJvYmxlbS4gV2Ugd2lsbCB1c2UgdGhlIGBsb2djb3VudHNgLCB3aGljaCBoYXZlIGJlZW4gbm9ybWFsaXNlZCBmb3IgbGlicmFyeSBzaXplLCBidXQgbm90IGZvciBjb21wb3NpdGlvbiBiaWFzLgoKYGBge3IsZmlnLmhlaWdodD01LGZpZy53aWR0aD0xMH0KcGFyKG1mcm93PWMoMSwyKSkKcGxvdE1EKGxvZ2NvdW50cyxjb2x1bW4gPSA3KQphYmxpbmUoaD0wLGNvbD0iZ3JleSIpCnBsb3RNRChsb2djb3VudHMsY29sdW1uID0gMTEpCmFibGluZShoPTAsY29sPSJncmV5IikKYGBgCgpUaGUgbWVhbi1kaWZmZXJlbmNlIHBsb3RzIHNob3cgYXZlcmFnZSBleHByZXNzaW9uIChtZWFuOiB4LWF4aXMpIGFnYWluc3QgbG9nLWZvbGQtY2hhbmdlcyAoZGlmZmVyZW5jZTogeS1heGlzKS4KQmVjYXVzZSBvdXIgYERHRUxpc3RgIG9iamVjdCBjb250YWlucyB0aGUgbm9ybWFsaXNhdGlvbiBmYWN0b3JzLCBpZiB3ZSByZWRvIHRoZXNlIHBsb3RzIHVzaW5nIGBkZ2VPYmpgLCB3ZSBzaG91bGQgc2VlIHRoZSBjb21wb3NpdGlvbiBiaWFzIHByb2JsZW0gaGFzIGJlZW4gc29sdmVkLgoKYGBge3IsZmlnLmhlaWdodD01LGZpZy53aWR0aD0xMH0KcGFyKG1mcm93PWMoMSwyKSkKcGxvdE1EKGRnZU9iaixjb2x1bW4gPSA3KQphYmxpbmUoaD0wLGNvbD0iZ3JleSIpCnBsb3RNRChkZ2VPYmosY29sdW1uID0gMTEpCmFibGluZShoPTAsY29sPSJncmV5IikKYGBgCgo+ICMjIENoYWxsZW5nZSB7LmNoYWxsZW5nZX0KPgo+IFBsb3QgdGhlIGJpYXNlZCBhbmQgdW5iaWFzZWQgTUQgcGxvdHMgc2lkZSBieSBzaWRlIGZvciB0aGUgc2FtZSBzYW1wbGUgdG8gc2VlIHRoZSBiZWZvcmUgYW5kIGFmdGVyIFRNTSBub3JtYWxpc2F0aW9uIGVmZmVjdC4KPgoKKipTb2x1dGlvbioqCmBgYHtyLGVjaG89RkFMU0UsZmlnLmhlaWdodD01LGZpZy53aWR0aD0xMH0KCmBgYAoKKipXZSBuZWVkIHRvIHNhdmUgYSBmZXcgZGF0YSBvYmplY3RzIHRvIHVzZSBmb3IgRGF5IDIgc28gd2UgZG9uJ3QgaGF2ZSB0byByZXJ1biBldmVyeXRoaW5nKioKCmBgYHtyfQpzYXZlKGdyb3VwLGRnZU9iaixzYW1wbGVpbmZvLGZpbGU9IlJvYmplY3RzL3ByZXByb2Nlc3NpbmcuUmRhdGEiKQpgYGAK