library(DESeq2)
library(RColorBrewer)
library(pheatmap)
library(tidyverse)
library(airway)
We will be using aiway package for sample data. It has data from a
RNA-seq experiment wherein airway smooth muscle cells were treated with
dexamethasone (Himes et al. 2014). Glucocorticoids is the main
ingredient of dexamethasone and is used mainly by people with asthma to
reduce inflammation of the airways. For each of the four cell lines, we
have a treated and an untreated sample. For more description of the
experiment see the PubMed entry 24926665 and for raw data see the GEO
entry GSE52778.
setwd("D:/shilpa/RNAseq/datacamp")
data(airway)
sample_meta <- as.data.frame(colData(airway))
sample_meta <- sample_meta[,c(2,3)]
sample_meta$dex <- gsub('trt', 'treated', sample_meta$dex)
sample_meta$dex <- gsub('untrt', 'untreated', sample_meta$dex)
names(sample_meta) <- c('cellLine', 'dexamethasone')
write.table(sample_meta, file = "sample_meta.csv", sep = ',', col.names = T, row.names = T, quote = F)
counts_data <- assay(airway)
write.table(counts_data, file = 'counts_data.csv', sep = ',', col.names = T, row.names = T, quote = F)
The overall Design of the experiment is as follows: mRNA profiles
obtained via RNA-Seq for four primary human airway smooth muscle cell
lines that were treated with dexamethasone or were left untreated. The
counts data comes from RNAseq experiments which are preprocessed for QC
steps, mapping and the final alignment is used to make the count
matrix).
raw_count_matrix <-read.csv('counts_data.csv')
meta_data <- read.csv('sample_meta.csv')
We shall study expression of the airway muscle genes here, let’s see
how our data looks like!
head(raw_count_matrix)
str(raw_count_matrix)
'data.frame': 64102 obs. of 8 variables:
$ SRR1039508: int 679 0 467 260 60 0 3251 1433 519 394 ...
$ SRR1039509: int 448 0 515 211 55 0 3679 1062 380 236 ...
$ SRR1039512: int 873 0 621 263 40 2 6177 1733 595 464 ...
$ SRR1039513: int 408 0 365 164 35 0 4252 881 493 175 ...
$ SRR1039516: int 1138 0 587 245 78 1 6721 1424 820 658 ...
$ SRR1039517: int 1047 0 799 331 63 0 11027 1439 714 584 ...
$ SRR1039520: int 770 0 417 233 76 0 5176 1359 696 360 ...
$ SRR1039521: int 572 0 508 229 60 0 7995 1109 704 269 ...
While there are many packages for DE analysis, we are going to use
DESeq2. It has one of more popular ones and its vignette is very
helpful. DESeq2 models the gene expression (count matrix data) by
“negetive binomial distribution”.
What is a count matrix: - Count matrix represents number of reads
matching the exons of each gene. Barcodes (for each cell) and UMI (for
each gene) information is used to generate this matrix. - The negative
binomial model is commonly used to model RNA-seq counts data - Highest
frequency near zero i.e. many genes have low number of counts - The
expression range is between zero to inf (no max limit) - For discrete
numbers i.e. counts, one could also use poisson but there is too much
variation in the data than poisson can handle hence neg bino is used
NOTE: DESeq2 model internally corrects for the library size so
transformed or normalized values such as counts scaled by library size
should not be used as input.
we shall now construct the DESeqDataSet object!
#check if the rownames in metadata match with the columns names from counts_matrix and if they are in the same ORDER
all(colnames(raw_count_matrix) %in% rownames(meta_data))
[1] TRUE
all(colnames(raw_count_matrix) == rownames(meta_data))
[1] TRUE
#this is True in our case so we can use this metadata for the DESeqDataSet object
dds <- DESeqDataSetFromMatrix(countData = raw_count_matrix,
colData = meta_data,
design = ~ dexamethasone)
Warning: some variables in design formula are characters, converting to factors
# design formula is used to estimate the dispersions and to estimate the log2 fold changes of the model
dds
class: DESeqDataSet
dim: 64102 8
metadata(1): version
assays(1): counts
rownames(64102): ENSG00000000003 ENSG00000000005 ... LRG_98 LRG_99
rowData names(0):
colnames(8): SRR1039508 SRR1039509 ... SRR1039520 SRR1039521
colData names(2): cellLine dexamethasone
##In case they were not matching, use the following to get matched indexes and then rearrange the metadata
##Use the match() function to reorder the counts matrix
##reorder_idx <- match(rownames(metadata), colnames(raw_count_matrix))
##Reorder the columns of the count data
##reordered_raw_count_matrix <- raw_count_matrix[ , reorder_idx]
There are 4 ways of constructing DESeqDataSet depending on which
pipeline is used upstream. We have count matrix here so we use
‘countData’ while constructing dds.
# pre-filtering: removing rows with low gene counts ; keeping rows that have at least 10 reads total. This reduces the number from around 60,000 to 20,000
keep <- rowSums(counts(dds)) >= 10
dds <- dds[keep,]
#We want the differential expression analysis as compared with "untreated"
dds$dexamethasone <- relevel(dds$dexamethasone, ref = "untreated")
Now we can explore the data with DESeq2 functions and perform the
differential expression analysis.
NORMALIZING COUNTS WITH DESeq2 -> normalized for library size
while accounting for library composition (library size is the total
number of gene counts per sample)
dds <- estimateSizeFactors(dds)
normalized_counts <- counts(dds, normalized=TRUE)
estimateSizeFactors: During this step two technical variations are
taken into account. library size (sequencing depth) & library
composition (need to normalise the counts based on size factor- default
calculation is based on median of ratios method). (One needs to correct
for gene length bias also in case we are comparing different genes. But
here we are comparing against condition and not genes.)
We will be using the normalized counts to explore similarities in
gene expression between each of our samples. To do this we use
clustering after using dimensional reduction.
vsd_trans <- vst(dds, blind = TRUE)
vsd_trans
class: DESeqTransform
dim: 22369 8
metadata(1): version
assays(1): ''
rownames(22369): ENSG00000000003 ENSG00000000419 ... ENSG00000273487 ENSG00000273488
rowData names(4): baseMean baseVar allZero dispFit
colnames(8): SRR1039508 SRR1039509 ... SRR1039520 SRR1039521
colData names(3): cellLine dexamethasone sizeFactor
Before clustering, we need to log transform the normalized counts to
improve the visualization of the clustering and hence our understanding.
we can use DESeq2’s vst() function for this. vst stands for Variance
stabilizing transformation. The transformed data should be approximated
variance stabilized and also includes correction for size factors or
normalization factors. The transformed data is on the log2 scale for
large counts. The blind=TRUE argument specifies that the transformation
should be blind to the sample information given in the design formula;
this argument should be specified when performing quality
assessment.
From vsd, we need to extract the VST-trasformed normalized counts as
a matrix.
vsd_mat <- assay(vsd_trans)
Then, we need to compute the pairwise correlation values between each
pair of samples
vsd_cor <- cor(vsd_mat)
Now we do Hierarchical clustering
pheatmap(vsd_cor, annotation = select(meta_data, dexamethasone))

This heatmap shows us that the treated and untreated cluster together
(but not very well!). Hopefully our differentially expressed genes are
driving this separation.
We will crosscheck this with PCA (QC step 2)
plotPCA(vsd_trans, intgroup="dexamethasone")

Even though PC1 is able to separate the treated and untreated, the
variance covered by PC1 is only 41%
Now that we have explored the quality of the samples, checked for
outliers, we can go ahead and find which genes have significant
differences in expression between the treated and untreated samples.
At this stage we do not need to re-create the object since we have
not removed samples or found additional sources of variation during QC
steps.
Run DESeq
dds <- DESeq(dds)
using pre-existing size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
dds
class: DESeqDataSet
dim: 22369 8
metadata(1): version
assays(4): counts mu H cooks
rownames(22369): ENSG00000000003 ENSG00000000419 ... ENSG00000273487 ENSG00000273488
rowData names(22): baseMean baseVar ... deviance maxCooks
colnames(8): SRR1039508 SRR1039509 ... SRR1039520 SRR1039521
colData names(3): cellLine dexamethasone sizeFactor
We can see the steps executed by DESeq2:
- Estimating size factors for each gene (we had already done that so
here it is using those pre-calculated)
- Estimate dispersions for each gene
- Fitting the linear model for each gene
- Hypothesis testing
NOW WE SHOULD CHECK HOW WELL OUR DATA FITS TO DESEQ2 MODEL
Dispersion estimates are used to model the raw counts; if the
dispersions don’t follow the assumptions made by DESeq2, then the
variation in the data could be poorly estimated and our outout about
which genes are differentially expressed could be wrong. To remind
ourselves the assumptions made by DESeq2 are: - The counts data fit to
the negative binomial distribution - The dispersions should generally
decrease with increase in the mean
plotDispEsts(dds)

About the dispersion plot: The goal behind estimating dispersions is
to account for variability between biological replicates. To accurately
model counts for each gene, we should also have accurate estimations of
variability between replicates in the same group. (In another words,
dispersion is how far the observed count will be from the mean value for
a gene)
X axis is mean normalized count per gene, y axis is dispersion per
gene, plotted for all the cells. Internally a function estimates
dispersions for a given neg binomial distribution. We generally do not
have those many biological replicates per gene, that’s where this
function comes in handy.
It assumes that genes with similar expression levels will also have
similar dispersion values. With this assumption: - Maximum likelihood
estimation is used to get dispersion estimates for each gene. - Fit
curve to gene-wise dispersion estimates - Shrink gene-wise dispersion
estimates towards values predicted by the curve
Variance = mean + dispersion*mean^2. (Dispersion of 0.01 means 10%
variability between replicates)
Plotting the dispersion estimates is a useful diagnostic. The
dispersion plot which we see here is a typical one with the final
estimates shrunk from the gene-wise estimates towards the fitted
estimates. Some gene-wise estimates are flagged (open circles) as
outliers and not shrunk towards the fitted value. (DESeq2 will assume
that these outliers do not follow the modelling assumptions because they
have additional variability which could not be accounted for by bio/tech
replicates)
From the dispersion plot we can see that fit of our data to the model
is good.
Dispersion plots can also be used to detect outlier genes in a
particular cell. Outlier Genes with high dispersion in comparison to the
mean might indicate biases in sequencing depth. However, some
Housekeeping genes could have high dispersion.
To read more about this fitting please refer: https://rdrr.io/bioc/DESeq2/man/estimateDispersions.html
To improve the fold change estimates for our data, we want to take
our results and shrink the log2 fold changes. DESeq2 has the lfcShrink()
function to generate the shrunken log2 foldchanges.
res = results(dds)
results_shrunk = lfcShrink(dds=dds, res=res, coef=2)
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
sequence count data: removing the noise and preserving large differences.
Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895
To improve the estimated fold changes we use log2 foldchange
shrinkage. For genes with low amounts of information available,
shrinkage uses information from all genes to generate more likely,
lower, log2 fold change estimates, similar to what we did with
dispersions.
#res0.01 <- results(dds,alpha = 0.01)
#To get descriptions for the columns in the results table, we can use the mcols()
mcols(results_shrunk)
DataFrame with 5 rows and 2 columns
type description
<character> <character>
baseMean intermediate mean of normalized c..
log2FoldChange results log2 fold change (MA..
lfcSE results posterior SD: dexame..
pvalue results Wald test p-value: d..
padj results BH adjusted p-values
baseMean is the mean value across all samples , log2FoldChange are
the shrunken log2 foldchanges, lfcSE is standard error of the fold
change estimates, and pvalue is from the Wald statistics output from the
Wald test for differential expression, and padj is adjusted p-value from
Benjamini-Hochberg (BH) calculation.
Multiple test correction is performed by DESeq2 using BH-method, to
adjust p-values for multiple testing and control the proportion of false
positives relative to true.
To reduce the number of genes tested, DESeq2 automatically filters
out genes unlikely to be truly differentially expressed prior to
testing, such as genes with zero counts across all samples, genes with
low mean values across all samples, and genes with extreme count
outliers. We can see the filtered genes in the results tables
represented by an NA in the p-adjusted column.
results_shrunk
log2 fold change (MAP): dexamethasone treated vs untreated
Wald test p-value: dexamethasone treated vs untreated
DataFrame with 22369 rows and 5 columns
baseMean log2FoldChange lfcSE pvalue padj
<numeric> <numeric> <numeric> <numeric> <numeric>
ENSG00000000003 708.5979 -0.2988204 0.168602 0.0286865 0.138470
ENSG00000000419 520.2963 0.1819520 0.097212 0.0430857 0.182998
ENSG00000000457 237.1621 0.0278170 0.113964 0.7876795 0.929805
ENSG00000000460 57.9324 -0.0501544 0.200164 0.6976669 0.894231
ENSG00000000971 5817.3108 0.2680312 0.236750 0.0883626 0.297042
... ... ... ... ... ...
ENSG00000273483 2.68955 0.0327453 0.255748 0.5797949 NA
ENSG00000273485 1.28646 0.0069044 0.255446 0.8854003 NA
ENSG00000273486 15.45244 -0.0309889 0.223294 0.7902460 0.930697
ENSG00000273487 8.16327 0.2218426 0.351895 0.0771216 0.271627
ENSG00000273488 8.58437 0.0381769 0.239650 0.7023421 0.896550
summary(results_shrunk)
out of 22369 with nonzero total read count
adjusted p-value < 0.1
LFC > 0 (up) : 1884, 8.4%
LFC < 0 (down) : 1502, 6.7%
outliers [1] : 51, 0.23%
low counts [2] : 3903, 17%
(mean count < 4)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results
# contrasts
#resultsNames(dds_run)
IN the summary table we see that with the chosen parameters, we have
around 8.4% gene upregulated (log2 fold change > 0) and 6.7% genes
downregulated.
VISUALIZING RESULTS
# MA plot
plotMA(res)
abline(h=c(-1,1), col="red", lwd=2)

To explore our results, the MA plot can be helpful. The MA plot shows
the mean of the normalized counts versus the log2 fold changes for all
genes tested. Note the large log2 foldchanges, particularly for genes
with lower mean count values. These fold changes are unlikely to be as
accurate for genes that have little information associated with them,
such as genes with low numbers of counts or high dispersion values. To
improve the estimated fold changes we use log2 foldchange shrinkage.
plotMA(results_shrunk)
abline(h=c(-1,1), col="red", lwd=2)

Compare the MA plot before and after the shrinkage and observe that
in the shrunk ones we see more restricted log2 foldchange values,
especially for lowly expressed genes. These shrunken log2 foldchanges
should be more accurate; however, shrinking the log2 foldchanges will
not affect the number of differentially expressed genes returned, only
the log2 fold change values.
resB = results(dds, lfcThreshold=0.58, alpha = 0.01)
results_shrunkB = lfcShrink(dds=dds, res=resB, coef=2)
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
sequence count data: removing the noise and preserving large differences.
Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895
If we wanted to return the genes most likely to be biologically
relevant, we could also include a log2 fold change threshold.
Let’s say we want a threshold of 1.5; log2(1.5) is 0.58, we add this
to our results() function. While using any log2 fold change cut-off
increases the risk of losing biologically relevant genes, by using a
very small log2 foldchange threshold, we are hoping to reduce the risk
that the genes more biologically meaningful.
summary(results_shrunkB)
out of 22369 with nonzero total read count
adjusted p-value < 0.01
LFC > 0 (up) : 147, 0.66%
LFC < 0 (down) : 83, 0.37%
outliers [1] : 51, 0.23%
low counts [2] : 4336, 19%
(mean count < 5)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results
To know which genes are over and under expressing, we will explore
annotables package to quickly obtain gene names for the Ensembl gene
IDs.
res_p0.05 <- data.frame(res) %>% mutate(threshold = padj < 0.05)
# Create the volcano plot
ggplot(res_p0.05) +
geom_point(aes(x = log2FoldChange, y = -log10(padj), color = threshold)) +
xlab("log2 fold change") +
ylab("-log10 adjusted p-value") +
theme(legend.position = "none",
plot.title = element_text(size = rel(1.5), hjust = 0.5),
axis.title = element_text(size = rel(1.25)))

LS0tDQp0aXRsZTogIkFuYWx5c2lzIG9mIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gd2l0aCBERVNlcSBpbiBSIE5vdGVib29rIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KYGBge3J9DQpsaWJyYXJ5KERFU2VxMikNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShwaGVhdG1hcCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShhaXJ3YXkpDQpgYGANCg0KV2Ugd2lsbCBiZSB1c2luZyBhaXdheSBwYWNrYWdlIGZvciBzYW1wbGUgZGF0YS4gSXQgaGFzIGRhdGEgZnJvbSBhIFJOQS1zZXEgZXhwZXJpbWVudCB3aGVyZWluIGFpcndheSBzbW9vdGggbXVzY2xlIGNlbGxzIHdlcmUgdHJlYXRlZCB3aXRoIGRleGFtZXRoYXNvbmUgKEhpbWVzIGV0IGFsLiAyMDE0KS4gR2x1Y29jb3J0aWNvaWRzIGlzIHRoZSBtYWluIGluZ3JlZGllbnQgb2YgZGV4YW1ldGhhc29uZSBhbmQgaXMgdXNlZCBtYWlubHkgYnkgcGVvcGxlIHdpdGggYXN0aG1hIHRvIHJlZHVjZSBpbmZsYW1tYXRpb24gb2YgdGhlIGFpcndheXMuIA0KRm9yIGVhY2ggb2YgdGhlIGZvdXIgY2VsbCBsaW5lcywgd2UgaGF2ZSBhIHRyZWF0ZWQgYW5kIGFuIHVudHJlYXRlZCBzYW1wbGUuIEZvciBtb3JlIGRlc2NyaXB0aW9uIG9mIHRoZSBleHBlcmltZW50IHNlZSB0aGUgUHViTWVkIGVudHJ5IDI0OTI2NjY1IGFuZCBmb3IgcmF3IGRhdGEgc2VlIHRoZSBHRU8gZW50cnkgR1NFNTI3NzguDQoNCmBgYHtyfQ0Kc2V0d2QoIkQ6L3NoaWxwYS9STkFzZXEvZGF0YWNhbXAiKQ0KZGF0YShhaXJ3YXkpDQpzYW1wbGVfbWV0YSA8LSBhcy5kYXRhLmZyYW1lKGNvbERhdGEoYWlyd2F5KSkNCnNhbXBsZV9tZXRhIDwtIHNhbXBsZV9tZXRhWyxjKDIsMyldDQpzYW1wbGVfbWV0YSRkZXggPC0gZ3N1YigndHJ0JywgJ3RyZWF0ZWQnLCBzYW1wbGVfbWV0YSRkZXgpDQpzYW1wbGVfbWV0YSRkZXggPC0gZ3N1YigndW50cnQnLCAndW50cmVhdGVkJywgc2FtcGxlX21ldGEkZGV4KQ0KbmFtZXMoc2FtcGxlX21ldGEpIDwtIGMoJ2NlbGxMaW5lJywgJ2RleGFtZXRoYXNvbmUnKQ0Kd3JpdGUudGFibGUoc2FtcGxlX21ldGEsIGZpbGUgPSAic2FtcGxlX21ldGEuY3N2Iiwgc2VwID0gJywnLCBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBULCBxdW90ZSA9IEYpDQoNCmNvdW50c19kYXRhIDwtIGFzc2F5KGFpcndheSkNCndyaXRlLnRhYmxlKGNvdW50c19kYXRhLCBmaWxlID0gJ2NvdW50c19kYXRhLmNzdicsIHNlcCA9ICcsJywgY29sLm5hbWVzID0gVCwgcm93Lm5hbWVzID0gVCwgcXVvdGUgPSBGKQ0KYGBgDQoNClRoZSBvdmVyYWxsIERlc2lnbiBvZiB0aGUgZXhwZXJpbWVudCBpcyBhcyBmb2xsb3dzOiBtUk5BIHByb2ZpbGVzIG9idGFpbmVkIHZpYSBSTkEtU2VxIGZvciBmb3VyIHByaW1hcnkgaHVtYW4gYWlyd2F5IHNtb290aCBtdXNjbGUgY2VsbCBsaW5lcyB0aGF0IHdlcmUgdHJlYXRlZCB3aXRoIGRleGFtZXRoYXNvbmUgb3Igd2VyZSBsZWZ0IHVudHJlYXRlZC4gDQpUaGUgY291bnRzIGRhdGEgY29tZXMgZnJvbSBSTkFzZXEgZXhwZXJpbWVudHMgd2hpY2ggYXJlIHByZXByb2Nlc3NlZCBmb3IgUUMgc3RlcHMsIG1hcHBpbmcgYW5kIHRoZSBmaW5hbCBhbGlnbm1lbnQgaXMgdXNlZCB0byBtYWtlIHRoZSBjb3VudCBtYXRyaXgpLg0KDQpgYGB7cn0NCnJhd19jb3VudF9tYXRyaXggPC1yZWFkLmNzdignY291bnRzX2RhdGEuY3N2JykNCm1ldGFfZGF0YSA8LSByZWFkLmNzdignc2FtcGxlX21ldGEuY3N2JykNCmBgYA0KDQpXZSBzaGFsbCBzdHVkeSBleHByZXNzaW9uIG9mIHRoZSBhaXJ3YXkgbXVzY2xlIGdlbmVzIGhlcmUsIGxldCdzIHNlZSBob3cgb3VyIGRhdGEgbG9va3MgbGlrZSENCg0KYGBge3J9DQpoZWFkKHJhd19jb3VudF9tYXRyaXgpDQpgYGANCmBgYHtyfQ0Kc3RyKHJhd19jb3VudF9tYXRyaXgpDQoNCmBgYA0KV2hpbGUgdGhlcmUgYXJlIG1hbnkgcGFja2FnZXMgZm9yIERFIGFuYWx5c2lzLCB3ZSBhcmUgZ29pbmcgdG8gdXNlIERFU2VxMi4gSXQgaGFzIG9uZSBvZiBtb3JlIHBvcHVsYXIgb25lcyBhbmQgaXRzIHZpZ25ldHRlIGlzIHZlcnkgaGVscGZ1bC4NCkRFU2VxMiBtb2RlbHMgdGhlIGdlbmUgZXhwcmVzc2lvbiAoY291bnQgbWF0cml4IGRhdGEpIGJ5ICJuZWdldGl2ZSBiaW5vbWlhbCBkaXN0cmlidXRpb24iLg0KDQpXaGF0IGlzIGEgY291bnQgbWF0cml4OiANCi0gQ291bnQgbWF0cml4IHJlcHJlc2VudHMgbnVtYmVyIG9mIHJlYWRzIG1hdGNoaW5nIHRoZSBleG9ucyBvZiBlYWNoIGdlbmUuIEJhcmNvZGVzIChmb3IgZWFjaCBjZWxsKSBhbmQgVU1JIChmb3IgZWFjaCBnZW5lKSBpbmZvcm1hdGlvbiBpcyB1c2VkIHRvIGdlbmVyYXRlIHRoaXMgbWF0cml4Lg0KLSBUaGUgbmVnYXRpdmUgYmlub21pYWwgbW9kZWwgaXMgY29tbW9ubHkgdXNlZCB0byBtb2RlbCBSTkEtc2VxIGNvdW50cyBkYXRhDQotIEhpZ2hlc3QgZnJlcXVlbmN5IG5lYXIgemVybyBpLmUuIG1hbnkgZ2VuZXMgaGF2ZSBsb3cgbnVtYmVyIG9mIGNvdW50cw0KLSBUaGUgZXhwcmVzc2lvbiByYW5nZSBpcyBiZXR3ZWVuIHplcm8gdG8gaW5mIChubyBtYXggbGltaXQpDQotIEZvciBkaXNjcmV0ZSBudW1iZXJzIGkuZS4gY291bnRzLCBvbmUgY291bGQgYWxzbyB1c2UgcG9pc3NvbiBidXQgdGhlcmUgaXMgdG9vIG11Y2ggdmFyaWF0aW9uIGluIHRoZSBkYXRhIHRoYW4gcG9pc3NvbiBjYW4gaGFuZGxlIGhlbmNlIG5lZyBiaW5vIGlzIHVzZWQgDQoNCk5PVEU6IERFU2VxMiBtb2RlbCBpbnRlcm5hbGx5IGNvcnJlY3RzIGZvciB0aGUgbGlicmFyeSBzaXplIHNvIHRyYW5zZm9ybWVkIG9yIG5vcm1hbGl6ZWQgdmFsdWVzIHN1Y2ggYXMgY291bnRzIHNjYWxlZCBieSBsaWJyYXJ5IHNpemUgc2hvdWxkIG5vdCBiZSB1c2VkIGFzIGlucHV0Lg0KDQp3ZSBzaGFsbCBub3cgY29uc3RydWN0IHRoZSBERVNlcURhdGFTZXQgb2JqZWN0IQ0KDQpgYGB7cn0NCiNjaGVjayBpZiB0aGUgcm93bmFtZXMgaW4gbWV0YWRhdGEgbWF0Y2ggd2l0aCB0aGUgY29sdW1ucyBuYW1lcyBmcm9tIGNvdW50c19tYXRyaXggYW5kIGlmIHRoZXkgYXJlIGluIHRoZSBzYW1lIE9SREVSDQphbGwoY29sbmFtZXMocmF3X2NvdW50X21hdHJpeCkgJWluJSByb3duYW1lcyhtZXRhX2RhdGEpKQ0KYWxsKGNvbG5hbWVzKHJhd19jb3VudF9tYXRyaXgpID09IHJvd25hbWVzKG1ldGFfZGF0YSkpDQoNCiN0aGlzIGlzIFRydWUgaW4gb3VyIGNhc2Ugc28gd2UgY2FuIHVzZSB0aGlzIG1ldGFkYXRhIGZvciB0aGUgREVTZXFEYXRhU2V0IG9iamVjdCANCg0KZGRzIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhID0gcmF3X2NvdW50X21hdHJpeCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IG1ldGFfZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduID0gfiBkZXhhbWV0aGFzb25lKQ0KDQojIGRlc2lnbiBmb3JtdWxhIGlzIHVzZWQgdG8gZXN0aW1hdGUgdGhlIGRpc3BlcnNpb25zIGFuZCB0byBlc3RpbWF0ZSB0aGUgbG9nMiBmb2xkIGNoYW5nZXMgb2YgdGhlIG1vZGVsDQoNCmRkcw0KDQojI0luIGNhc2UgdGhleSB3ZXJlIG5vdCBtYXRjaGluZywgdXNlIHRoZSBmb2xsb3dpbmcgdG8gZ2V0IG1hdGNoZWQgaW5kZXhlcyBhbmQgdGhlbiByZWFycmFuZ2UgdGhlIG1ldGFkYXRhDQojI1VzZSB0aGUgbWF0Y2goKSBmdW5jdGlvbiB0byByZW9yZGVyIHRoZSBjb3VudHMgbWF0cml4DQojI3Jlb3JkZXJfaWR4IDwtIG1hdGNoKHJvd25hbWVzKG1ldGFkYXRhKSwgY29sbmFtZXMocmF3X2NvdW50X21hdHJpeCkpDQojI1Jlb3JkZXIgdGhlIGNvbHVtbnMgb2YgdGhlIGNvdW50IGRhdGENCiMjcmVvcmRlcmVkX3Jhd19jb3VudF9tYXRyaXggPC0gcmF3X2NvdW50X21hdHJpeFsgLCByZW9yZGVyX2lkeF0NCmBgYA0KVGhlcmUgYXJlIDQgd2F5cyBvZiBjb25zdHJ1Y3RpbmcgREVTZXFEYXRhU2V0IGRlcGVuZGluZyBvbiB3aGljaCBwaXBlbGluZSBpcyB1c2VkIHVwc3RyZWFtLiBXZSBoYXZlIGNvdW50IG1hdHJpeCBoZXJlIHNvIHdlIHVzZSAnY291bnREYXRhJyB3aGlsZSBjb25zdHJ1Y3RpbmcgZGRzLg0KDQpgYGB7cn0NCiMgcHJlLWZpbHRlcmluZzogcmVtb3Zpbmcgcm93cyB3aXRoIGxvdyBnZW5lIGNvdW50cyA7IGtlZXBpbmcgcm93cyB0aGF0IGhhdmUgYXQgbGVhc3QgMTAgcmVhZHMgdG90YWwuIFRoaXMgcmVkdWNlcyB0aGUgbnVtYmVyIGZyb20gYXJvdW5kIDYwLDAwMCB0byAyMCwwMDANCmtlZXAgPC0gcm93U3Vtcyhjb3VudHMoZGRzKSkgPj0gMTANCmRkcyA8LSBkZHNba2VlcCxdDQoNCiNXZSB3YW50IHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBhcyBjb21wYXJlZCB3aXRoICJ1bnRyZWF0ZWQiDQpkZHMkZGV4YW1ldGhhc29uZSA8LSByZWxldmVsKGRkcyRkZXhhbWV0aGFzb25lLCByZWYgPSAidW50cmVhdGVkIikNCg0KYGBgDQoNCk5vdyB3ZSBjYW4gZXhwbG9yZSB0aGUgZGF0YSB3aXRoIERFU2VxMiBmdW5jdGlvbnMgYW5kIHBlcmZvcm0gdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCk5PUk1BTElaSU5HIENPVU5UUyBXSVRIIERFU2VxMg0KLT4gbm9ybWFsaXplZCBmb3IgbGlicmFyeSBzaXplIHdoaWxlIGFjY291bnRpbmcgZm9yIGxpYnJhcnkgY29tcG9zaXRpb24gKGxpYnJhcnkgc2l6ZSBpcyB0aGUgdG90YWwgbnVtYmVyIG9mIGdlbmUgY291bnRzIHBlciBzYW1wbGUpDQoNCmBgYHtyfQ0KZGRzIDwtIGVzdGltYXRlU2l6ZUZhY3RvcnMoZGRzKQ0Kbm9ybWFsaXplZF9jb3VudHMgPC0gY291bnRzKGRkcywgbm9ybWFsaXplZD1UUlVFKQ0KDQpgYGANCg0KZXN0aW1hdGVTaXplRmFjdG9yczogRHVyaW5nIHRoaXMgc3RlcCB0d28gdGVjaG5pY2FsIHZhcmlhdGlvbnMgYXJlIHRha2VuIGludG8gYWNjb3VudC4NCmxpYnJhcnkgc2l6ZSAoc2VxdWVuY2luZyBkZXB0aCkgJiBsaWJyYXJ5IGNvbXBvc2l0aW9uIChuZWVkIHRvIG5vcm1hbGlzZSB0aGUgY291bnRzIGJhc2VkIG9uIHNpemUgZmFjdG9yLSBkZWZhdWx0IGNhbGN1bGF0aW9uIGlzIGJhc2VkIG9uIG1lZGlhbiBvZiByYXRpb3MgbWV0aG9kKS4NCihPbmUgbmVlZHMgdG8gY29ycmVjdCBmb3IgZ2VuZSBsZW5ndGggYmlhcyBhbHNvIGluIGNhc2Ugd2UgYXJlIGNvbXBhcmluZyBkaWZmZXJlbnQgZ2VuZXMuIEJ1dCBoZXJlIHdlIGFyZSBjb21wYXJpbmcgYWdhaW5zdCBjb25kaXRpb24gYW5kIG5vdCBnZW5lcy4pDQoNCldlIHdpbGwgYmUgdXNpbmcgdGhlIG5vcm1hbGl6ZWQgY291bnRzIHRvIGV4cGxvcmUgc2ltaWxhcml0aWVzIGluIGdlbmUgZXhwcmVzc2lvbiBiZXR3ZWVuIGVhY2ggb2Ygb3VyIHNhbXBsZXMuIFRvIGRvIHRoaXMgd2UgdXNlIGNsdXN0ZXJpbmcgYWZ0ZXIgdXNpbmcgZGltZW5zaW9uYWwgcmVkdWN0aW9uLiANCg0KYGBge3J9DQp2c2RfdHJhbnMgPC0gdnN0KGRkcywgYmxpbmQgPSBUUlVFKQ0KdnNkX3RyYW5zDQpgYGANCg0KQmVmb3JlIGNsdXN0ZXJpbmcsIHdlIG5lZWQgdG8gbG9nIHRyYW5zZm9ybSB0aGUgbm9ybWFsaXplZCBjb3VudHMgdG8gaW1wcm92ZSB0aGUgdmlzdWFsaXphdGlvbiBvZiB0aGUgY2x1c3RlcmluZyBhbmQgaGVuY2Ugb3VyIHVuZGVyc3RhbmRpbmcuIHdlIGNhbiB1c2UgREVTZXEyJ3MgdnN0KCkgZnVuY3Rpb24gZm9yIHRoaXMuIHZzdCBzdGFuZHMgZm9yIFZhcmlhbmNlIHN0YWJpbGl6aW5nIHRyYW5zZm9ybWF0aW9uLiBUaGUgdHJhbnNmb3JtZWQgZGF0YSBzaG91bGQgYmUgYXBwcm94aW1hdGVkIHZhcmlhbmNlIHN0YWJpbGl6ZWQgYW5kIGFsc28gaW5jbHVkZXMgY29ycmVjdGlvbiBmb3Igc2l6ZSBmYWN0b3JzIG9yIG5vcm1hbGl6YXRpb24gZmFjdG9ycy4gVGhlIHRyYW5zZm9ybWVkIGRhdGEgaXMgb24gdGhlIGxvZzIgc2NhbGUgZm9yIGxhcmdlIGNvdW50cy4NClRoZSBibGluZD1UUlVFIGFyZ3VtZW50IHNwZWNpZmllcyB0aGF0IHRoZSB0cmFuc2Zvcm1hdGlvbiBzaG91bGQgYmUgYmxpbmQgdG8gdGhlIHNhbXBsZSBpbmZvcm1hdGlvbiBnaXZlbiBpbiB0aGUgZGVzaWduIGZvcm11bGE7IHRoaXMgYXJndW1lbnQgc2hvdWxkIGJlIHNwZWNpZmllZCB3aGVuIHBlcmZvcm1pbmcgcXVhbGl0eSBhc3Nlc3NtZW50Lg0KDQpGcm9tIHZzZCwgd2UgbmVlZCB0byBleHRyYWN0IHRoZSBWU1QtdHJhc2Zvcm1lZCBub3JtYWxpemVkIGNvdW50cyBhcyBhIG1hdHJpeC4NCg0KYGBge3J9DQp2c2RfbWF0IDwtIGFzc2F5KHZzZF90cmFucykNCmBgYA0KVGhlbiwgd2UgbmVlZCB0byBjb21wdXRlIHRoZSBwYWlyd2lzZSBjb3JyZWxhdGlvbiB2YWx1ZXMgYmV0d2VlbiBlYWNoIHBhaXIgb2Ygc2FtcGxlcw0KYGBge3J9DQp2c2RfY29yIDwtIGNvcih2c2RfbWF0KQ0KDQpgYGANCk5vdyB3ZSBkbyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZw0KYGBge3J9DQpwaGVhdG1hcCh2c2RfY29yLCBhbm5vdGF0aW9uID0gc2VsZWN0KG1ldGFfZGF0YSwgZGV4YW1ldGhhc29uZSkpIA0KDQpgYGANCg0KVGhpcyBoZWF0bWFwIHNob3dzIHVzIHRoYXQgdGhlIHRyZWF0ZWQgYW5kIHVudHJlYXRlZCBjbHVzdGVyIHRvZ2V0aGVyIChidXQgbm90IHZlcnkgd2VsbCEpLg0KSG9wZWZ1bGx5IG91ciBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgYXJlIGRyaXZpbmcgdGhpcyBzZXBhcmF0aW9uLg0KDQpXZSB3aWxsIGNyb3NzY2hlY2sgdGhpcyB3aXRoIFBDQSAoUUMgc3RlcCAyKQ0KDQpgYGB7cn0NCnBsb3RQQ0EodnNkX3RyYW5zLCBpbnRncm91cD0iZGV4YW1ldGhhc29uZSIpDQoNCmBgYA0KDQpFdmVuIHRob3VnaCBQQzEgaXMgYWJsZSB0byBzZXBhcmF0ZSB0aGUgdHJlYXRlZCBhbmQgdW50cmVhdGVkLCB0aGUgdmFyaWFuY2UgY292ZXJlZCBieSBQQzEgaXMgb25seSA0MSUNCg0KTm93IHRoYXQgd2UgaGF2ZSBleHBsb3JlZCB0aGUgcXVhbGl0eSBvZiB0aGUgc2FtcGxlcywgY2hlY2tlZCBmb3Igb3V0bGllcnMsIHdlIGNhbiBnbyBhaGVhZCBhbmQgZmluZCB3aGljaCBnZW5lcyBoYXZlIHNpZ25pZmljYW50IGRpZmZlcmVuY2VzIGluIGV4cHJlc3Npb24gYmV0d2VlbiB0aGUgdHJlYXRlZCBhbmQgdW50cmVhdGVkIHNhbXBsZXMuDQogDQpBdCB0aGlzIHN0YWdlIHdlIGRvIG5vdCBuZWVkIHRvIHJlLWNyZWF0ZSB0aGUgb2JqZWN0IHNpbmNlIHdlIGhhdmUgbm90IHJlbW92ZWQgc2FtcGxlcyBvciBmb3VuZCBhZGRpdGlvbmFsIHNvdXJjZXMgb2YgdmFyaWF0aW9uIGR1cmluZyBRQyBzdGVwcy4NCg0KUnVuIERFU2VxDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpgYGB7cn0NCmRkcyA8LSBERVNlcShkZHMpDQpkZHMNCmBgYA0KDQpXZSBjYW4gc2VlIHRoZSBzdGVwcyBleGVjdXRlZCBieSBERVNlcTI6DQoNCi0gIEVzdGltYXRpbmcgc2l6ZSBmYWN0b3JzIGZvciBlYWNoIGdlbmUgKHdlIGhhZCBhbHJlYWR5IGRvbmUgdGhhdCBzbyBoZXJlIGl0IGlzIHVzaW5nIHRob3NlIHByZS1jYWxjdWxhdGVkKQ0KLSAgRXN0aW1hdGUgZGlzcGVyc2lvbnMgZm9yIGVhY2ggZ2VuZQ0KLSAgRml0dGluZyB0aGUgbGluZWFyIG1vZGVsIGZvciBlYWNoIGdlbmUNCiAtIEh5cG90aGVzaXMgdGVzdGluZw0KIA0KTk9XIFdFIFNIT1VMRCBDSEVDSyBIT1cgV0VMTCBPVVIgREFUQSBGSVRTIFRPIERFU0VRMiBNT0RFTA0KDQpEaXNwZXJzaW9uIGVzdGltYXRlcyBhcmUgdXNlZCB0byBtb2RlbCB0aGUgcmF3IGNvdW50czsgaWYgdGhlIGRpc3BlcnNpb25zIGRvbid0IGZvbGxvdyB0aGUgYXNzdW1wdGlvbnMgbWFkZSBieSBERVNlcTIsIHRoZW4gdGhlIHZhcmlhdGlvbiBpbiB0aGUgZGF0YSBjb3VsZCBiZSBwb29ybHkgZXN0aW1hdGVkIGFuZCBvdXIgb3V0b3V0IGFib3V0IHdoaWNoIGdlbmVzIGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgY291bGQgYmUgd3JvbmcuDQpUbyByZW1pbmQgb3Vyc2VsdmVzIHRoZSBhc3N1bXB0aW9ucyBtYWRlIGJ5IERFU2VxMiBhcmU6DQotIFRoZSBjb3VudHMgZGF0YSBmaXQgdG8gdGhlIG5lZ2F0aXZlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbg0KLSBUaGUgZGlzcGVyc2lvbnMgc2hvdWxkIGdlbmVyYWxseSBkZWNyZWFzZSB3aXRoIGluY3JlYXNlIGluIHRoZSBtZWFuDQoNCmBgYHtyfQ0KcGxvdERpc3BFc3RzKGRkcykNCmBgYA0KQWJvdXQgdGhlIGRpc3BlcnNpb24gcGxvdDogDQpUaGUgZ29hbCBiZWhpbmQgZXN0aW1hdGluZyBkaXNwZXJzaW9ucyBpcyB0byBhY2NvdW50IGZvciB2YXJpYWJpbGl0eSBiZXR3ZWVuIGJpb2xvZ2ljYWwgcmVwbGljYXRlcy4gDQpUbyBhY2N1cmF0ZWx5IG1vZGVsIGNvdW50cyBmb3IgZWFjaCBnZW5lLCB3ZSBzaG91bGQgYWxzbyBoYXZlIGFjY3VyYXRlIGVzdGltYXRpb25zIG9mIHZhcmlhYmlsaXR5IGJldHdlZW4gcmVwbGljYXRlcyBpbiB0aGUgc2FtZSBncm91cC4NCihJbiBhbm90aGVyIHdvcmRzLCBkaXNwZXJzaW9uIGlzIGhvdyBmYXIgdGhlIG9ic2VydmVkIGNvdW50IHdpbGwgYmUgZnJvbSB0aGUgbWVhbiB2YWx1ZSBmb3IgYSBnZW5lKQ0KDQpYIGF4aXMgaXMgbWVhbiBub3JtYWxpemVkIGNvdW50IHBlciBnZW5lLCB5IGF4aXMgaXMgZGlzcGVyc2lvbiBwZXIgZ2VuZSwgcGxvdHRlZCBmb3IgYWxsIHRoZSBjZWxscy4gDQpJbnRlcm5hbGx5IGEgZnVuY3Rpb24gZXN0aW1hdGVzIGRpc3BlcnNpb25zIGZvciBhIGdpdmVuIG5lZyBiaW5vbWlhbCBkaXN0cmlidXRpb24uDQpXZSBnZW5lcmFsbHkgZG8gbm90IGhhdmUgdGhvc2UgbWFueSBiaW9sb2dpY2FsIHJlcGxpY2F0ZXMgcGVyIGdlbmUsIHRoYXQncyB3aGVyZSB0aGlzIGZ1bmN0aW9uIGNvbWVzIGluIGhhbmR5Lg0KDQpJdCBhc3N1bWVzIHRoYXQgZ2VuZXMgd2l0aCBzaW1pbGFyIGV4cHJlc3Npb24gbGV2ZWxzIHdpbGwgYWxzbyBoYXZlIHNpbWlsYXIgZGlzcGVyc2lvbiB2YWx1ZXMuIFdpdGggdGhpcyBhc3N1bXB0aW9uOg0KLSBNYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGlvbiBpcyB1c2VkIHRvIGdldCBkaXNwZXJzaW9uIGVzdGltYXRlcyBmb3IgZWFjaCBnZW5lLg0KLSBGaXQgY3VydmUgdG8gZ2VuZS13aXNlIGRpc3BlcnNpb24gZXN0aW1hdGVzDQotIFNocmluayBnZW5lLXdpc2UgZGlzcGVyc2lvbiBlc3RpbWF0ZXMgdG93YXJkcyB2YWx1ZXMgcHJlZGljdGVkIGJ5IHRoZSBjdXJ2ZQ0KDQpWYXJpYW5jZSA9IG1lYW4gKyBkaXNwZXJzaW9uKm1lYW5eMi4NCihEaXNwZXJzaW9uIG9mIDAuMDEgbWVhbnMgMTAlIHZhcmlhYmlsaXR5IGJldHdlZW4gcmVwbGljYXRlcykNCg0KUGxvdHRpbmcgdGhlIGRpc3BlcnNpb24gZXN0aW1hdGVzIGlzIGEgdXNlZnVsIGRpYWdub3N0aWMuIFRoZSBkaXNwZXJzaW9uIHBsb3Qgd2hpY2ggd2Ugc2VlIGhlcmUgaXMgYSB0eXBpY2FsIG9uZSB3aXRoIHRoZSBmaW5hbCBlc3RpbWF0ZXMgc2hydW5rIGZyb20gdGhlIGdlbmUtd2lzZSBlc3RpbWF0ZXMgdG93YXJkcyB0aGUgZml0dGVkIGVzdGltYXRlcy4gU29tZSBnZW5lLXdpc2UgZXN0aW1hdGVzIGFyZSBmbGFnZ2VkIChvcGVuIGNpcmNsZXMpIGFzIG91dGxpZXJzIGFuZCBub3Qgc2hydW5rIHRvd2FyZHMgdGhlIGZpdHRlZCB2YWx1ZS4gKERFU2VxMiB3aWxsIGFzc3VtZSB0aGF0IHRoZXNlIG91dGxpZXJzIGRvIG5vdCBmb2xsb3cgdGhlIG1vZGVsbGluZyBhc3N1bXB0aW9ucyBiZWNhdXNlIHRoZXkgaGF2ZSBhZGRpdGlvbmFsIHZhcmlhYmlsaXR5IHdoaWNoIGNvdWxkIG5vdCBiZSBhY2NvdW50ZWQgZm9yIGJ5IGJpby90ZWNoIHJlcGxpY2F0ZXMpDQoNCkZyb20gdGhlIGRpc3BlcnNpb24gcGxvdCB3ZSBjYW4gc2VlIHRoYXQgZml0IG9mIG91ciBkYXRhIHRvIHRoZSBtb2RlbCBpcyBnb29kLg0KDQpEaXNwZXJzaW9uIHBsb3RzIGNhbiBhbHNvIGJlIHVzZWQgdG8gZGV0ZWN0IG91dGxpZXIgZ2VuZXMgaW4gYSBwYXJ0aWN1bGFyIGNlbGwuIE91dGxpZXIgR2VuZXMgd2l0aCBoaWdoIGRpc3BlcnNpb24gaW4gY29tcGFyaXNvbiB0byB0aGUgbWVhbiBtaWdodCBpbmRpY2F0ZSBiaWFzZXMgaW4gc2VxdWVuY2luZyBkZXB0aC4gSG93ZXZlciwgc29tZSBIb3VzZWtlZXBpbmcgZ2VuZXMgY291bGQgaGF2ZSAgaGlnaCBkaXNwZXJzaW9uLg0KDQpUbyByZWFkIG1vcmUgYWJvdXQgdGhpcyBmaXR0aW5nIHBsZWFzZSByZWZlcjogaHR0cHM6Ly9yZHJyLmlvL2Jpb2MvREVTZXEyL21hbi9lc3RpbWF0ZURpc3BlcnNpb25zLmh0bWwNCg0KVG8gaW1wcm92ZSB0aGUgZm9sZCBjaGFuZ2UgZXN0aW1hdGVzIGZvciBvdXIgZGF0YSwgd2Ugd2FudCB0byB0YWtlIG91ciByZXN1bHRzIGFuZCBzaHJpbmsgdGhlIGxvZzIgZm9sZCBjaGFuZ2VzLiBERVNlcTIgaGFzIHRoZSBsZmNTaHJpbmsoKSBmdW5jdGlvbiB0byBnZW5lcmF0ZSB0aGUgc2hydW5rZW4gbG9nMiBmb2xkY2hhbmdlcy4NCg0KYGBge3J9DQpyZXMgPSByZXN1bHRzKGRkcykNCnJlc3VsdHNfc2hydW5rID0gbGZjU2hyaW5rKGRkcz1kZHMsIHJlcz1yZXMsIGNvZWY9MikNCmBgYA0KVG8gaW1wcm92ZSB0aGUgZXN0aW1hdGVkIGZvbGQgY2hhbmdlcyB3ZSB1c2UgbG9nMiBmb2xkY2hhbmdlIHNocmlua2FnZS4gRm9yIGdlbmVzIHdpdGggbG93IGFtb3VudHMgb2YgaW5mb3JtYXRpb24gYXZhaWxhYmxlLCBzaHJpbmthZ2UgdXNlcyBpbmZvcm1hdGlvbiBmcm9tIGFsbCBnZW5lcyB0byBnZW5lcmF0ZSBtb3JlIGxpa2VseSwgbG93ZXIsIGxvZzIgZm9sZCBjaGFuZ2UgZXN0aW1hdGVzLCBzaW1pbGFyIHRvIHdoYXQgd2UgZGlkIHdpdGggZGlzcGVyc2lvbnMuDQoNCg0KYGBge3J9DQojcmVzMC4wMSA8LSByZXN1bHRzKGRkcyxhbHBoYSA9IDAuMDEpDQojVG8gZ2V0IGRlc2NyaXB0aW9ucyBmb3IgdGhlIGNvbHVtbnMgaW4gdGhlIHJlc3VsdHMgdGFibGUsIHdlIGNhbiB1c2UgdGhlIG1jb2xzKCkNCm1jb2xzKHJlc3VsdHNfc2hydW5rKQ0KDQpgYGANCmJhc2VNZWFuIGlzIHRoZSBtZWFuIHZhbHVlIGFjcm9zcyBhbGwgc2FtcGxlcyAsIGxvZzJGb2xkQ2hhbmdlIGFyZSB0aGUgc2hydW5rZW4gbG9nMiBmb2xkY2hhbmdlcywgbGZjU0UgaXMgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIGZvbGQgY2hhbmdlIGVzdGltYXRlcywgYW5kIHB2YWx1ZSBpcyBmcm9tIHRoZSBXYWxkIHN0YXRpc3RpY3Mgb3V0cHV0IGZyb20gdGhlIFdhbGQgdGVzdCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24sIGFuZCBwYWRqIGlzIGFkanVzdGVkIHAtdmFsdWUgZnJvbSBCZW5qYW1pbmktSG9jaGJlcmcgKEJIKSBjYWxjdWxhdGlvbi4NCg0KTXVsdGlwbGUgdGVzdCBjb3JyZWN0aW9uIGlzIHBlcmZvcm1lZCBieSBERVNlcTIgdXNpbmcgQkgtbWV0aG9kLCB0byBhZGp1c3QgcC12YWx1ZXMgZm9yIG11bHRpcGxlIHRlc3RpbmcgYW5kIGNvbnRyb2wgdGhlIHByb3BvcnRpb24gb2YgZmFsc2UgcG9zaXRpdmVzIHJlbGF0aXZlIHRvIHRydWUuIA0KDQpUbyByZWR1Y2UgdGhlIG51bWJlciBvZiBnZW5lcyB0ZXN0ZWQsIERFU2VxMiBhdXRvbWF0aWNhbGx5IGZpbHRlcnMgb3V0IGdlbmVzIHVubGlrZWx5IHRvIGJlIHRydWx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBwcmlvciB0byB0ZXN0aW5nLCBzdWNoIGFzIGdlbmVzIHdpdGggemVybyBjb3VudHMgYWNyb3NzIGFsbCBzYW1wbGVzLCBnZW5lcyB3aXRoIGxvdyBtZWFuIHZhbHVlcyBhY3Jvc3MgYWxsIHNhbXBsZXMsIGFuZCBnZW5lcyB3aXRoIGV4dHJlbWUgY291bnQgb3V0bGllcnMuIFdlIGNhbiBzZWUgdGhlIGZpbHRlcmVkIGdlbmVzIGluIHRoZSByZXN1bHRzIHRhYmxlcyByZXByZXNlbnRlZCBieSBhbiBOQSBpbiB0aGUgcC1hZGp1c3RlZCBjb2x1bW4uDQoNCg0KYGBge3J9DQpyZXN1bHRzX3NocnVuaw0Kc3VtbWFyeShyZXN1bHRzX3NocnVuaykNCg0KYGBgDQoNCklOIHRoZSBzdW1tYXJ5IHRhYmxlIHdlIHNlZSB0aGF0IHdpdGggdGhlIGNob3NlbiBwYXJhbWV0ZXJzLCB3ZSBoYXZlIGFyb3VuZCA4LjQlIGdlbmUgdXByZWd1bGF0ZWQgKGxvZzIgZm9sZCBjaGFuZ2UgPiAwKSBhbmQgNi43JSBnZW5lcyBkb3ducmVndWxhdGVkLg0KDQpWSVNVQUxJWklORyBSRVNVTFRTDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCg0KYGBge3J9DQojIE1BIHBsb3QNCnBsb3RNQShyZXMpDQphYmxpbmUoaD1jKC0xLDEpLCBjb2w9InJlZCIsIGx3ZD0yKQ0KYGBgDQoNClRvIGV4cGxvcmUgb3VyIHJlc3VsdHMsIHRoZSBNQSBwbG90IGNhbiBiZSBoZWxwZnVsLiBUaGUgTUEgcGxvdCBzaG93cyB0aGUgbWVhbiBvZiB0aGUgbm9ybWFsaXplZCBjb3VudHMgdmVyc3VzIHRoZSBsb2cyIGZvbGQgY2hhbmdlcyBmb3IgYWxsIGdlbmVzIHRlc3RlZC4gTm90ZSB0aGUgbGFyZ2UgbG9nMiBmb2xkY2hhbmdlcywgcGFydGljdWxhcmx5IGZvciBnZW5lcyB3aXRoIGxvd2VyIG1lYW4gY291bnQgdmFsdWVzLiBUaGVzZSBmb2xkIGNoYW5nZXMgYXJlIHVubGlrZWx5IHRvIGJlIGFzIGFjY3VyYXRlIGZvciBnZW5lcyB0aGF0IGhhdmUgbGl0dGxlIGluZm9ybWF0aW9uIGFzc29jaWF0ZWQgd2l0aCB0aGVtLCBzdWNoIGFzIGdlbmVzIHdpdGggbG93IG51bWJlcnMgb2YgY291bnRzIG9yIGhpZ2ggZGlzcGVyc2lvbiB2YWx1ZXMuDQpUbyBpbXByb3ZlIHRoZSBlc3RpbWF0ZWQgZm9sZCBjaGFuZ2VzIHdlIHVzZSBsb2cyIGZvbGRjaGFuZ2Ugc2hyaW5rYWdlLiANCg0KYGBge3J9DQpwbG90TUEocmVzdWx0c19zaHJ1bmspDQphYmxpbmUoaD1jKC0xLDEpLCBjb2w9InJlZCIsIGx3ZD0yKQ0KYGBgDQpDb21wYXJlIHRoZSBNQSBwbG90IGJlZm9yZSBhbmQgYWZ0ZXIgdGhlIHNocmlua2FnZSBhbmQgb2JzZXJ2ZSB0aGF0IGluIHRoZSBzaHJ1bmsgb25lcyB3ZSBzZWUgbW9yZSByZXN0cmljdGVkIGxvZzIgZm9sZGNoYW5nZSB2YWx1ZXMsIGVzcGVjaWFsbHkgZm9yIGxvd2x5IGV4cHJlc3NlZCBnZW5lcy4gVGhlc2Ugc2hydW5rZW4gbG9nMiBmb2xkY2hhbmdlcyBzaG91bGQgYmUgbW9yZSBhY2N1cmF0ZTsgaG93ZXZlciwgc2hyaW5raW5nIHRoZSBsb2cyIGZvbGRjaGFuZ2VzIHdpbGwgbm90IGFmZmVjdCB0aGUgbnVtYmVyIG9mIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyByZXR1cm5lZCwgb25seSB0aGUgbG9nMiBmb2xkIGNoYW5nZSB2YWx1ZXMuIA0KDQpgYGB7cn0NCnJlc0IgPSByZXN1bHRzKGRkcywgbGZjVGhyZXNob2xkPTAuNTgsIGFscGhhID0gMC4wMSkNCnJlc3VsdHNfc2hydW5rQiA9IGxmY1NocmluayhkZHM9ZGRzLCByZXM9cmVzQiwgY29lZj0yKQ0KDQpgYGANCg0KSWYgd2Ugd2FudGVkIHRvIHJldHVybiB0aGUgZ2VuZXMgbW9zdCBsaWtlbHkgdG8gYmUgYmlvbG9naWNhbGx5IHJlbGV2YW50LCB3ZSBjb3VsZCBhbHNvIGluY2x1ZGUgYSBsb2cyIGZvbGQgY2hhbmdlIHRocmVzaG9sZC4gDQoNCkxldCdzIHNheSB3ZSB3YW50IGEgdGhyZXNob2xkIG9mIDEuNTsgbG9nMigxLjUpIGlzIDAuNTgsIHdlIGFkZCB0aGlzIHRvIG91ciByZXN1bHRzKCkgZnVuY3Rpb24uDQpXaGlsZSB1c2luZyBhbnkgbG9nMiBmb2xkIGNoYW5nZSBjdXQtb2ZmIGluY3JlYXNlcyB0aGUgcmlzayBvZiBsb3NpbmcgYmlvbG9naWNhbGx5IHJlbGV2YW50IGdlbmVzLCBieSB1c2luZyBhIHZlcnkgc21hbGwgbG9nMiBmb2xkY2hhbmdlIHRocmVzaG9sZCwgd2UgYXJlIGhvcGluZyB0byByZWR1Y2UgdGhlIHJpc2sgdGhhdCB0aGUgZ2VuZXMgbW9yZSBiaW9sb2dpY2FsbHkgbWVhbmluZ2Z1bC4NCg0KDQpgYGB7cn0NCnN1bW1hcnkocmVzdWx0c19zaHJ1bmtCKQ0KDQpgYGANClRvIGtub3cgd2hpY2ggZ2VuZXMgYXJlIG92ZXIgYW5kIHVuZGVyIGV4cHJlc3NpbmcsIHdlIHdpbGwgZXhwbG9yZSBhbm5vdGFibGVzIHBhY2thZ2UgdG8gcXVpY2tseSBvYnRhaW4gZ2VuZSBuYW1lcyBmb3IgdGhlIEVuc2VtYmwgZ2VuZSBJRHMuDQoNCg0KYGBge3J9DQoNCnJlc19wMC4wNSA8LSBkYXRhLmZyYW1lKHJlcykgJT4lIG11dGF0ZSh0aHJlc2hvbGQgPSBwYWRqIDwgMC4wNSkNCg0KIyBDcmVhdGUgdGhlIHZvbGNhbm8gcGxvdA0KZ2dwbG90KHJlc19wMC4wNSkgKyANCiAgICAgICAgZ2VvbV9wb2ludChhZXMoeCA9IGxvZzJGb2xkQ2hhbmdlLCB5ID0gLWxvZzEwKHBhZGopLCBjb2xvciA9IHRocmVzaG9sZCkpICsgDQogICAgICAgIHhsYWIoImxvZzIgZm9sZCBjaGFuZ2UiKSArIA0KICAgICAgICB5bGFiKCItbG9nMTAgYWRqdXN0ZWQgcC12YWx1ZSIpICsgDQogICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgDQogICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgxLjUpLCBoanVzdCA9IDAuNSksIA0KICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMS4yNSkpKQ0KDQpgYGANCg0KDQo=