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:

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=