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

Resources and data files

This material has been created using the following resources:
http://www.statsci.org/smyth/pubs/QLedgeRPreprint.pdf (Lun, Chen, and Smyth 2016)
http://monashbioinformaticsplatform.github.io/RNAseq-DE-analysis-with-R/99-RNAseq_DE_analysis_with_R.html

Data files downloaded from:
ftp://ftp.ncbi.nlm.nih.gov/geo/series/GSE60nnn/GSE60450/suppl/GSE60450_Lactation-GenewiseCounts.txt.gz http://bioinf.wehi.edu.au/software/MSigDB/mouse_c2_v5.rdata http://bioinf.wehi.edu.au/software/MSigDB/mouse_H_v5.rdata

Data files:
sampleinfo.txt
GSE60450_Lactation-GenewiseCounts.txt
mouse_c2_v5.rdata
mouse_H_v5.rdata

Data files available from: https://figshare.com/s/1d788fd384d33e913a2a You should download these files and place them in your /data directory.

Packages used: limma, edgeR, gplots, org.Mm.eg.db, RColorBrewer, Glimma

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

Format the data

We will be manipulating and reformating the counts matrix into a suitable format for downstream analysis. The first two columns in the seqdata dataframe contain annotation information. We need to make a new matrix containing only the counts, but we can store the gene identifiers (the EntrezGeneID column) as rownames. We will add more annotation information about each gene later on in the workshop.

Let’s create a new data object, countdata, that contains only the counts for the 12 samples.

# Remove first two columns from seqdata
countdata <- seqdata[,-(1:2)]

# Store EntrezGeneID as rownames
rownames(countdata) <- seqdata[,1]

View(countdata)
head(countdata)

Now take a look at the column names

colnames(countdata)

These are the sample names which are pretty long so we’ll shorten these to contain only the relevant information about each sample. We will use the substr command to extract the first 7 characters and use these as the colnames.

substr("ThisIsAString", start=1, stop=5)
# using substr, you extract the characters starting at position 1 and stopping at position 7 of the colnames
colnames(countdata) <- substr(colnames(countdata), 1, 7)
View(countdata)

Note that the column names are now the same as SampleName in the sampleinfo file. This is good because it means our sample information in sampleinfo is in the same order as the columns in countdata.

table(colnames(countdata)==sampleinfo$SampleName)

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

  1. Plot the counts-per-million versus counts for the second sample.
  2. Add a vertical line at 0.5 and a horizontal line at 10.
  3. 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

  1. Redo the plots choosing your own colours.
  2. 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.
  3. 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

  1. Change the colour scheme to “PiYG” and redo the heatmap. Try ?RColorBrewer and see what other colour schemes are available.
  2. Change the sample names to group using the labCol argument
  3. 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