Sample Visualizatons for Quality Control
On Monday, we discussed aspects of quality control assessment at the sequencing data level. Today we will outline sample-level and gene-level quality control assessments to determine what we should expect for our differential expression comparisons.
To do this, we will first assess the similarity of our samples by using principal component analysis (PCA). This will allow us to determine how well patterns in the data fits our expectations from the experiments design and possible sources of variation.
Other common visualizations that we generate for our analyses include expression heatmaps, sample correlation heatmaps, and boxplots of raw and/or normalized counts, the code for which (due to time restrictions) can be found as bonus content through the materials for today and in the bonus content module.
Principle Component Analysis (PCA) Plots
A common and very useful plot for evaluating how well our samples cluster by treatment groups are Principle Component Analysis (PCA) plots. PCA is used to emphasize variation and bring out patterns in large datasets by using dimensionality redution.
This image from a helpful step by step explaination of PCA helps to illustrate the principle component projections for two genes measured in approximately 60 mouse samples. Generally, this process is repeated and after each gene’s contribution to a principle component or weight is determined, the expression and weight are summed across genes for each sample to calculate a value for each principle component.
Note: A more detailed overview of the PCA procedure is outlined in a Harvard Chan Bioinformatic Core training module and is based on a more thorough description presented in a StatQuest’s video. Additionally, this TowardsDataScience blog post goes through the math behind PCAs.
Interpreting PCA plots
For most bulk RNA-seq experiments, we expect the majority of the total variance to be explained by the first two or three principle components.
In this plot, which is similar to what we’ll generate below for our data, where principle component 1 (PC1) explains ~80% of the variance in our data while principle component 2 (PC2) explains ~12% of the variance, which fits that expections.
[Question]: How might we interpret the variance explained by each principle component in the context of the labeled sample points?
For more information, this helpful overview of PCA basics walks through both the generation and interpretation of PCA plots.
Evaluating batch effects or confounders
PCA plots are also useful for evaluating the impact of factors other than the experimental treatment or group.
At times, batch effects can be quite obvious, such as this example from the DESeq2 vignette, where samples within each treatment group look staggered into two subgroups.
If we color only by sequencing run type (paired-end vs. single-end), we see that PC2 (29% of variance) is primarily explained by this technical covariate.
However, the samples are clearly seperated by experimental condition on PC1 and since we have non-confounded batches, if we saw this pattern in our data we could incorporate the technical covariate into our model design, such as outlined in the DESeq2 vignette.
Click for complex design discussion
In experiments with more complex designs, such as when there are interesecting/multiple treatment conditions, it can be less clear what covariants are influencing expression, such as illustrated from this documenation for a microarray analysis tool. From the PCA labeled by experimental treatment, we see that samples from the treatment group do not cluster together and that there is high variance across all treatment groups. However, when the plot is color coded by the technical batches of probe labeling, we see that the patterns in the data are better explained by batch than the experimental conditions.
Plot Setup
We’ve already loaded the libraries we need for this module. We’ll follow best practices and create new a subdirectory to organize our output figures, as well as a variable to store a descriptor of the dataset we are looking at so we can more easily recycle our plotting functions.
plotPath = './DE_outputs/figures/'
dir.create(plotPath, showWarnings = FALSE)
Comparison <- "Gtype.Tx" # descriptor for the dataset we are looking at
PCA Plot initialization
Below, we will plot the rlog normalized data and generate the PCA projections for the top 500 using the plotPCA
function from DESeq2, specifying Gtype.Tx
as the condition of interest, and view the simple plot generated by the function.
p.all <- plotPCA(rld, intgroup = c('Gtype.Tx'), ntop = 500)
p.all
We also see that samples in both control
groups are fairly tightly grouped, while samples within each Tx
group do not cluster as tightly.
[Question]: What can we take away from this PCA plot? Does it look like there are batch effects? Would we expect more DE genes for comparisons between ko.control and wt.control samples OR wt.Tx and ko.Tx samples?
Next, we’ll save this plot to file.
pdf(file = paste0(plotPath, 'PCAplot_rlog_', Comparison, '.pdf'), onefile = TRUE)
p.all
dev.off()
## quartz_off_screen
## 2
Checkpoint: If generated and saved the p.all
PCA plot, please indicate with the green ‘check’ button. Otherwise, please use use the red ‘x’ button in your zoom reaction panel to have this step repeated.
[Breakout Exercise 1] - Imagine a heatmap
It can be useful to visualize to visualize how our samples organize along with the expression pattern of individual genes. We will create a heatmap of the top most variable genes using the pheatmap
function.
Before the exercise, let’s look at the help page using the ?
symbol.
?pheatmap
If we scroll down to the examples, we can test some of them out:
# copied from the pheatmap documentation
test = matrix(rnorm(200), 20, 10)
test[1:10, seq(1, 10, 2)] = test[1:10, seq(1, 10, 2)] + 3
test[11:20, seq(2, 10, 2)] = test[11:20, seq(2, 10, 2)] + 2
test[15:20, seq(2, 10, 2)] = test[15:20, seq(2, 10, 2)] + 4
colnames(test) = paste("Test", 1:10, sep = "")
rownames(test) = paste("Gene", 1:20, sep = "")
pheatmap(test)
Now that we’ve gotten a sense of what pheatmap
can generate, let’ navigate to our first breakout exercise
Transferring our plot to our local computer
Rstudio server allows us to download files through the interactive file panel on the right side. If we navigate into the plot subfolder and select the PCAplot_rlog_Gtype.Tx.pdf
or the file, we can then click the blue gear symbol labeled More
and select Export...
. We should see a prompt regarding the name of the file and if we click Download
the file should show up in your local “Downloads” folder.
Optional - Scree Plot example
Bonus: Click for example code for generating a ScreePlot
A screeplot is a way to visualize the variance explained by all principle components. To generate a scree plot, the PCA results need to be used independently of plotting, such as described by this statquest post and replicated below.
# generate PCA loadings
pca <- prcomp(t(assay(rld)), scale=TRUE)
## get the scree information
pca.var <- pca$sdev^2
scree <- pca.var/sum(pca.var)
p <- barplot((scree[1:10]*100), main="Scree Plot", xlab="Principal Component", ylab="Percent Variation")
print(p)
## [,1]
## [1,] 0.7
## [2,] 1.9
## [3,] 3.1
## [4,] 4.3
## [5,] 5.5
## [6,] 6.7
## [7,] 7.9
## [8,] 9.1
## [9,] 10.3
## [10,] 11.5
We can see that the majority (~65%) of the variance across our samples is explained by the first three principle components, giving us some additional confidence regarding the quality of our data. In these scree plot examples from BioTuring, the plot on the left fits what we would expect for a dataset with high signal from the experimental treatment, where the majority of the variance is explained by the first few principle components. The plot on the right illustrates a scenario where the variance is distributed across many components, which could be due to low signal from the experimental treatment, complex experimental design, or confounding factors. image:
LS0tCnRpdGxlOiAiRGF5IDMgLSBNb2R1bGUgMDk6IFNhbXBsZSBWaXN1YWxpemF0aW9ucyBmb3IgUXVhbGl0eSBDb250cm9sIgphdXRob3I6ICJVTSBCaW9pbmZvcm1hdGljcyBDb3JlIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICAgICAgICBodG1sX2RvY3VtZW50OgogICAgICAgICAgICBpbmNsdWRlczoKICAgICAgICAgICAgICAgIGluX2hlYWRlcjogaGVhZGVyLmh0bWwKICAgICAgICAgICAgdGhlbWU6IHBhcGVyCiAgICAgICAgICAgIHRvYzogdHJ1ZQogICAgICAgICAgICB0b2NfZGVwdGg6IDQKICAgICAgICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgICAgICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgICAgICAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgICAgICAgICBtYXJrZG93bjogR0ZNCiAgICAgICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CmJvZHl7IC8qIE5vcm1hbCAgKi8KICAgICAgZm9udC1zaXplOiAxNHB0OwogIH0KcHJlIHsKICBmb250LXNpemU6IDEycHQKfQpjb2RlLnJ7CiAgZm9udC1zaXplOiAxMnB0Owp9Cjwvc3R5bGU+Cgo8IS0tLSBBbGxvdyB0aGUgcGFnZSB0byBiZSB3aWRlciAtLS0+CjxzdHlsZT4KICAgIGJvZHkgLm1haW4tY29udGFpbmVyIHsKICAgICAgICBtYXgtd2lkdGg6IDEyMDBweDsKICAgIH0KPC9zdHlsZT4KCj4gIyBPYmplY3RpdmVzOiAgICAgCj4gKiBHZW5lcmF0ZSBjb21tb24gUUMgdmlzdWFsaXphdGlvbnMgICAKPiAqIFVuZGVyc3RhbmQgaG93IHRvIGludGVycHJldCBRQyB2aXN1YWxpemF0aW9ucyAgICAKPiAqIFVuZGVyc3RhbmQgd2hlbiB0byByZXZpc2UgdGhlIG1vZGVsIHVzZWQgaW4gdGhlIERFU2VxMiBpbml0aWFsaXphdGlvbiAgICAKPiAqIFVuZGVyc3RhbmQgdGhlIHBpdGZhbGxzIG9mIHBvc3QtaG9jIGFuYWx5c2lzICAgICAKPiAqIERlc2NyaWJlIHRoZSBjYXVzZXMgYW5kIGltcGxpY2F0aW9ucyBvZiBiYXRjaCBlZmZlY3Qgb3Igb3RoZXIgUUMgaXNzdWVzIGluIGFuIFJOQS1TZXEgZXhwZXJpbWVudCAgICAgCgoKYGBge3IgTW9kdWxlcywgZXZhbD1UUlVFLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KG1hdHJpeFN0YXRzKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkocGhlYXRtYXApCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsb2FkKCJyZGF0YS9SdW5uaW5nRGF0YS5SRGF0YSIpCmBgYAoKCiMgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gV29ya2Zsb3cKClByaW9yIHRvIHRlc3RpbmcgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGJldHdlZW4gb3VyIGNvbXBhcmlzb25zIG9mIGludGVyZXN0LCB3ZSdsbCBmaXJzdCBnZW5lcmF0ZSBwbG90cyB0aGF0IHdpbGwgYXNzZXNzIGhvdyB3ZWxsIG91ciBzYW1wbGVzIG1hdGNoIHVwIHdpdGggb3VyIGV4cGVjdGF0aW9ucyAoYmFzZWQgb24gdGhlaXIgdHJlYXRtZW50IGdyb3VwcykgYW5kIHdoYXQgd2UgbWlnaHQgZXhwZWN0IHRvIHNlZSBmcm9tIG91ciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBjb21wYXJpc29ucy4KCiFbXSguL2ltYWdlcy93YXlmaW5kZXIvd2F5ZmluZGVyLTA1LnBuZyl7d2lkdGg9NzUlfQoKLS0tCgojIFNhbXBsZSBWaXN1YWxpemF0b25zIGZvciBRdWFsaXR5IENvbnRyb2wgICAgIAoKT24gTW9uZGF5LCB3ZSBkaXNjdXNzZWQgYXNwZWN0cyBvZiBxdWFsaXR5IGNvbnRyb2wgYXNzZXNzbWVudCBhdCB0aGUgc2VxdWVuY2luZyBkYXRhIGxldmVsLiBUb2RheSB3ZSB3aWxsIG91dGxpbmUgc2FtcGxlLWxldmVsIGFuZCBnZW5lLWxldmVsIHF1YWxpdHkgY29udHJvbCBhc3Nlc3NtZW50cyB0byBkZXRlcm1pbmUgd2hhdCB3ZSBzaG91bGQgZXhwZWN0IGZvciBvdXIgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gY29tcGFyaXNvbnMuCgpUbyBkbyB0aGlzLCB3ZSB3aWxsIGZpcnN0IGFzc2VzcyB0aGUgc2ltaWxhcml0eSBvZiBvdXIgc2FtcGxlcyBieSB1c2luZyBwcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIChQQ0EpLiBUaGlzIHdpbGwgYWxsb3cgdXMgdG8gZGV0ZXJtaW5lIGhvdyB3ZWxsIHBhdHRlcm5zIGluIHRoZSBkYXRhIGZpdHMgb3VyIGV4cGVjdGF0aW9ucyBmcm9tIHRoZSBleHBlcmltZW50cyBkZXNpZ24gYW5kIHBvc3NpYmxlIHNvdXJjZXMgb2YgdmFyaWF0aW9uLgoKT3RoZXIgY29tbW9uIHZpc3VhbGl6YXRpb25zIHRoYXQgd2UgZ2VuZXJhdGUgZm9yIG91ciBhbmFseXNlcyBpbmNsdWRlIGV4cHJlc3Npb24gaGVhdG1hcHMsIHNhbXBsZSBjb3JyZWxhdGlvbiBoZWF0bWFwcywgYW5kIGJveHBsb3RzIG9mIHJhdyBhbmQvb3Igbm9ybWFsaXplZCBjb3VudHMsIHRoZSBjb2RlIGZvciB3aGljaCAoZHVlIHRvIHRpbWUgcmVzdHJpY3Rpb25zKSBjYW4gYmUgZm91bmQgYXMgYm9udXMgY29udGVudCB0aHJvdWdoIHRoZSBtYXRlcmlhbHMgZm9yIHRvZGF5IGFuZCBpbiB0aGUgYm9udXMgY29udGVudCBtb2R1bGUuCgojIyBQcmluY2lwbGUgQ29tcG9uZW50IEFuYWx5c2lzIChQQ0EpIFBsb3RzCgpBIGNvbW1vbiBhbmQgdmVyeSB1c2VmdWwgcGxvdCBmb3IgZXZhbHVhdGluZyBob3cgd2VsbCBvdXIgc2FtcGxlcyBjbHVzdGVyIGJ5IHRyZWF0bWVudCBncm91cHMgYXJlIFByaW5jaXBsZSBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkgcGxvdHMuIFBDQSBpcyB1c2VkIHRvIGVtcGhhc2l6ZSB2YXJpYXRpb24gYW5kIGJyaW5nIG91dCBwYXR0ZXJucyBpbiBsYXJnZSBkYXRhc2V0cyBieSB1c2luZyBkaW1lbnNpb25hbGl0eSByZWR1dGlvbi4KClRoaXMgaW1hZ2UgZnJvbQpbYSBoZWxwZnVsIHN0ZXAgYnkgc3RlcCBleHBsYWluYXRpb24gb2YgUENBXShodHRwczovL2Jsb2cuYmlvdHVyaW5nLmNvbS8yMDE4LzA2LzE0L3ByaW5jaXBhbC1jb21wb25lbnQtYW5hbHlzaXMtZXhwbGFpbmVkLXNpbXBseS8pIGhlbHBzIHRvIGlsbHVzdHJhdGUgdGhlIHByaW5jaXBsZSBjb21wb25lbnQgcHJvamVjdGlvbnMgZm9yIHR3byBnZW5lcyBtZWFzdXJlZCBpbiBhcHByb3hpbWF0ZWx5IDYwIG1vdXNlIHNhbXBsZXMuIEdlbmVyYWxseSwgdGhpcyBwcm9jZXNzIGlzIHJlcGVhdGVkIGFuZCBhZnRlciBlYWNoIGdlbmUncyBjb250cmlidXRpb24gdG8gYSBwcmluY2lwbGUgY29tcG9uZW50IG9yIHdlaWdodCBpcyBkZXRlcm1pbmVkLCB0aGUgZXhwcmVzc2lvbiBhbmQgd2VpZ2h0IGFyZSBzdW1tZWQgYWNyb3NzIGdlbmVzIGZvciBlYWNoIHNhbXBsZSB0byBjYWxjdWxhdGUgYSB2YWx1ZSBmb3IgZWFjaCBwcmluY2lwbGUgY29tcG9uZW50LgoKCiFbXSguL2ltYWdlcy9CbG9nX3BjYV82Yi5wbmcpCgo+KipOb3RlKio6IEEgbW9yZSBkZXRhaWxlZCBvdmVydmlldyBvZiB0aGUgUENBIHByb2NlZHVyZSBpcyBvdXRsaW5lZCBpbiBbYSBIYXJ2YXJkIENoYW4gQmlvaW5mb3JtYXRpYyBDb3JlIHRyYWluaW5nIG1vZHVsZV0oaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvcHJpbmNpcGFsX2NvbXBvbmVudF9hbmFseXNpcy5odG1sKSBhbmQgaXMgYmFzZWQgb24gYSBtb3JlIHRob3JvdWdoIGRlc2NyaXB0aW9uIHByZXNlbnRlZCBpbiBhIFtTdGF0UXVlc3TigJlzIHZpZGVvXShodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PV9VVkhuZUJVQlcwKS4gQWRkaXRpb25hbGx5LCB0aGlzIFtUb3dhcmRzRGF0YVNjaWVuY2UgYmxvZyBwb3N0XShodHRwczovL3Rvd2FyZHNkYXRhc2NpZW5jZS5jb20vcHJpbmNpcGFsLWNvbXBvbmVudC1hbmFseXNpcy0zYzM5ZmJmNWNiOWQpIGdvZXMgdGhyb3VnaCB0aGUgbWF0aCBiZWhpbmQgUENBcy4KCiMjIyBJbnRlcnByZXRpbmcgUENBIHBsb3RzCgpGb3IgbW9zdCBidWxrIFJOQS1zZXEgZXhwZXJpbWVudHMsIHdlIGV4cGVjdCB0aGUgbWFqb3JpdHkgb2YgdGhlIHRvdGFsIHZhcmlhbmNlIHRvIGJlIGV4cGxhaW5lZCBieSB0aGUgZmlyc3QgdHdvIG9yIHRocmVlIHByaW5jaXBsZSBjb21wb25lbnRzLgoKIVtdKGltYWdlcy9QQ0FwbG90X0ZhbmN5X3Jsb2dfa28uVHgucG5nKXt3aWR0aD03NSV9CgpJbiB0aGlzIHBsb3QsIHdoaWNoIGlzIHNpbWlsYXIgdG8gd2hhdCB3ZSdsbCBnZW5lcmF0ZSBiZWxvdyBmb3Igb3VyIGRhdGEsIHdoZXJlIHByaW5jaXBsZSBjb21wb25lbnQgMSAoUEMxKSBleHBsYWlucyB+ODAlIG9mIHRoZSB2YXJpYW5jZSBpbiBvdXIgZGF0YSB3aGlsZSBwcmluY2lwbGUgY29tcG9uZW50IDIgKFBDMikgZXhwbGFpbnMgfjEyJSBvZiB0aGUgdmFyaWFuY2UsIHdoaWNoIGZpdHMgdGhhdCBleHBlY3Rpb25zLgoKKltRdWVzdGlvbl06IEhvdyBtaWdodCB3ZSBpbnRlcnByZXQgdGhlIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBlYWNoIHByaW5jaXBsZSBjb21wb25lbnQgaW4gdGhlIGNvbnRleHQgb2YgdGhlIGxhYmVsZWQgc2FtcGxlIHBvaW50cz8qCgoKRm9yIG1vcmUgaW5mb3JtYXRpb24sIHRoaXMgW2hlbHBmdWwgb3ZlcnZpZXcgb2YgUENBIGJhc2ljc10oaHR0cHM6Ly9ibG9nLmJpb3R1cmluZy5jb20vMjAxOC8wNi8xNC9wcmluY2lwYWwtY29tcG9uZW50LWFuYWx5c2lzLWV4cGxhaW5lZC1zaW1wbHkvKSB3YWxrcyB0aHJvdWdoIGJvdGggdGhlIGdlbmVyYXRpb24gYW5kIGludGVycHJldGF0aW9uIG9mIFBDQSBwbG90cy4gICAgCgoKIyMjIEV2YWx1YXRpbmcgYmF0Y2ggZWZmZWN0cyBvciBjb25mb3VuZGVycwoKUENBIHBsb3RzIGFyZSBhbHNvIHVzZWZ1bCBmb3IgZXZhbHVhdGluZyB0aGUgaW1wYWN0IG9mIGZhY3RvcnMgb3RoZXIgdGhhbiB0aGUgZXhwZXJpbWVudGFsIHRyZWF0bWVudCBvciBncm91cC4KCkF0IHRpbWVzLCBiYXRjaCBlZmZlY3RzIGNhbiBiZSBxdWl0ZSBvYnZpb3VzLCBzdWNoIGFzIHRoaXMgZXhhbXBsZSBmcm9tIHRoZSBbREVTZXEyIHZpZ25ldHRlXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwpLCB3aGVyZSBzYW1wbGVzIHdpdGhpbiBlYWNoIHRyZWF0bWVudCBncm91cCBsb29rIHN0YWdnZXJlZCBpbnRvIHR3byBzdWJncm91cHMuCgoKIVtdKC4vaW1hZ2VzL1BDQTFfREVTZXEyVmlnbmV0dGUucG5nKQoKSWYgd2UgY29sb3Igb25seSBieSBzZXF1ZW5jaW5nIHJ1biB0eXBlIChwYWlyZWQtZW5kIHZzLiBzaW5nbGUtZW5kKSwgd2Ugc2VlIHRoYXQgUEMyICgyOSUgb2YgdmFyaWFuY2UpIGlzIHByaW1hcmlseSBleHBsYWluZWQgYnkgdGhpcyB0ZWNobmljYWwgY292YXJpYXRlLgoKIVtdKC4vaW1hZ2VzL1BDQTJfREVTZXEyVmlnbmV0dGUucG5nKQoKSG93ZXZlciwgdGhlIHNhbXBsZXMgYXJlIGNsZWFybHkgc2VwZXJhdGVkIGJ5IGV4cGVyaW1lbnRhbCBjb25kaXRpb24gb24gUEMxICoqYW5kKiogc2luY2Ugd2UgaGF2ZSBub24tY29uZm91bmRlZCBiYXRjaGVzLCBpZiB3ZSBzYXcgdGhpcyBwYXR0ZXJuIGluIG91ciBkYXRhIHdlIGNvdWxkIGluY29ycG9yYXRlIHRoZSB0ZWNobmljYWwgY292YXJpYXRlIGludG8gb3VyIG1vZGVsIGRlc2lnbiwgc3VjaCBhcyBvdXRsaW5lZCBpbiB0aGUgW0RFU2VxMiB2aWduZXR0ZV0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL0RFU2VxMi5odG1sI211bHRpLWZhY3Rvci1kZXNpZ25zKS4KCjxkZXRhaWxzPgogICAgPHN1bW1hcnk+KkNsaWNrIGZvciBjb21wbGV4IGRlc2lnbiBkaXNjdXNzaW9uKjwvc3VtbWFyeT4KICAgIEluIGV4cGVyaW1lbnRzIHdpdGggbW9yZSBjb21wbGV4IGRlc2lnbnMsIHN1Y2ggYXMgd2hlbiB0aGVyZSBhcmUgaW50ZXJlc2VjdGluZy9tdWx0aXBsZSB0cmVhdG1lbnQgY29uZGl0aW9ucywgaXQgY2FuIGJlIGxlc3MgY2xlYXIgd2hhdCBjb3ZhcmlhbnRzIGFyZSBpbmZsdWVuY2luZyBleHByZXNzaW9uLCBzdWNoIGFzIGlsbHVzdHJhdGVkIGZyb20gW3RoaXMgZG9jdW1lbmF0aW9uIGZvciBhIG1pY3JvYXJyYXkgYW5hbHlzaXMgdG9vbF0oaHR0cDovL3d3dy5tb2xtaW5lLmNvbS9tYWdtYS9nbG9iYWxfYW5hbHlzaXMvYmF0Y2hfZWZmZWN0Lmh0bWwpLgogICAgRnJvbSB0aGUgUENBIGxhYmVsZWQgYnkgZXhwZXJpbWVudGFsIHRyZWF0bWVudCwgd2Ugc2VlIHRoYXQgc2FtcGxlcyBmcm9tIHRoZSB0cmVhdG1lbnQgZ3JvdXAgZG8gbm90IGNsdXN0ZXIgdG9nZXRoZXIgYW5kIHRoYXQgdGhlcmUgaXMgaGlnaCB2YXJpYW5jZSBhY3Jvc3MgYWxsIHRyZWF0bWVudCBncm91cHMuCiAgICAhW10oLi9pbWFnZXMvYmF0Y2hfZXgxYi5qcGcpCiAgICBIb3dldmVyLCB3aGVuIHRoZSBwbG90IGlzIGNvbG9yIGNvZGVkIGJ5IHRoZSB0ZWNobmljYWwgYmF0Y2hlcyBvZiBwcm9iZSBsYWJlbGluZywgd2Ugc2VlIHRoYXQgdGhlIHBhdHRlcm5zIGluIHRoZSBkYXRhIGFyZSBiZXR0ZXIgZXhwbGFpbmVkIGJ5IGJhdGNoIHRoYW4gdGhlIGV4cGVyaW1lbnRhbCBjb25kaXRpb25zLgogICAgIVtdKC4vaW1hZ2VzL2JhdGNoX2V4MWMuanBnKQo8L2RldGFpbHM+CgoKIyMgUGxvdCBTZXR1cAoKV2UndmUgYWxyZWFkeSBsb2FkZWQgdGhlIGxpYnJhcmllcyB3ZSBuZWVkIGZvciB0aGlzIG1vZHVsZS4gV2UnbGwgZm9sbG93IGJlc3QgcHJhY3RpY2VzIGFuZCBjcmVhdGUgbmV3IGEgc3ViZGlyZWN0b3J5IHRvIG9yZ2FuaXplIG91ciBvdXRwdXQgZmlndXJlcywgYXMgd2VsbCBhcyBhIHZhcmlhYmxlIHRvIHN0b3JlIGEgZGVzY3JpcHRvciBvZiB0aGUgZGF0YXNldCB3ZSBhcmUgbG9va2luZyBhdCBzbyB3ZSBjYW4gbW9yZSBlYXNpbHkgcmVjeWNsZSBvdXIgcGxvdHRpbmcgZnVuY3Rpb25zLgoKYGBge3IgU2V0dXBkaXJlY3Rvcnl9CnBsb3RQYXRoID0gJy4vREVfb3V0cHV0cy9maWd1cmVzLycKZGlyLmNyZWF0ZShwbG90UGF0aCwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCkNvbXBhcmlzb24gPC0gIkd0eXBlLlR4IiAjIGRlc2NyaXB0b3IgZm9yIHRoZSBkYXRhc2V0IHdlIGFyZSBsb29raW5nIGF0CmBgYAoKIyMgUENBIFBsb3QgaW5pdGlhbGl6YXRpb24KCkJlbG93LCB3ZSB3aWxsIHBsb3QgdGhlIHJsb2cgbm9ybWFsaXplZCBkYXRhIGFuZCBnZW5lcmF0ZSB0aGUgUENBIHByb2plY3Rpb25zIGZvciB0aGUgdG9wIDUwMCB1c2luZyB0aGUgYHBsb3RQQ0FgIGZ1bmN0aW9uIGZyb20gREVTZXEyLCBzcGVjaWZ5aW5nIGBHdHlwZS5UeGAgYXMgdGhlIGNvbmRpdGlvbiBvZiBpbnRlcmVzdCwgYW5kIHZpZXcgdGhlIHNpbXBsZSBwbG90IGdlbmVyYXRlZCBieSB0aGUgZnVuY3Rpb24uCgpgYGB7ciBQQ0FybG9nM30KcC5hbGwgPC0gcGxvdFBDQShybGQsIGludGdyb3VwID0gYygnR3R5cGUuVHgnKSwgbnRvcCA9IDUwMCkKcC5hbGwKYGBgCgpXZSBhbHNvIHNlZSB0aGF0IHNhbXBsZXMgaW4gYm90aCBgY29udHJvbGAgZ3JvdXBzIGFyZSBmYWlybHkgdGlnaHRseSBncm91cGVkLCB3aGlsZSBzYW1wbGVzIHdpdGhpbiBlYWNoIGBUeGAgZ3JvdXAgZG8gbm90IGNsdXN0ZXIgYXMgdGlnaHRseS4gCgoqW1F1ZXN0aW9uXTogV2hhdCBjYW4gd2UgdGFrZSBhd2F5IGZyb20gdGhpcyBQQ0EgcGxvdD8gRG9lcyBpdCBsb29rIGxpa2UgdGhlcmUgYXJlIGJhdGNoIGVmZmVjdHM/IFdvdWxkIHdlIGV4cGVjdCBtb3JlIERFIGdlbmVzIGZvciBjb21wYXJpc29ucyBiZXR3ZWVuIGtvLmNvbnRyb2wgYW5kIHd0LmNvbnRyb2wgc2FtcGxlcyBPUiB3dC5UeCBhbmQga28uVHggc2FtcGxlcz8qCgpOZXh0LCB3ZSdsbCBzYXZlIHRoaXMgcGxvdCB0byBmaWxlLgpgYGB7ciBQQ0FybG9nMX0KcGRmKGZpbGUgPSBwYXN0ZTAocGxvdFBhdGgsICdQQ0FwbG90X3Jsb2dfJywgQ29tcGFyaXNvbiwgJy5wZGYnKSwgb25lZmlsZSA9IFRSVUUpCnAuYWxsCmRldi5vZmYoKQpgYGAKCioqQ2hlY2twb2ludCoqOiAqSWYgZ2VuZXJhdGVkIGFuZCBzYXZlZCB0aGUgYHAuYWxsYCBQQ0EgcGxvdCwgcGxlYXNlIGluZGljYXRlIHdpdGggdGhlIGdyZWVuICdjaGVjaycgYnV0dG9uLiBPdGhlcndpc2UsIHBsZWFzZSB1c2UgIHVzZSB0aGUgcmVkICd4JyBidXR0b24gaW4geW91ciB6b29tIHJlYWN0aW9uIHBhbmVsIHRvIGhhdmUgdGhpcyBzdGVwIHJlcGVhdGVkLioKCiMjIFtCcmVha291dCBFeGVyY2lzZSAxXSAtIEltYWdpbmUgYSBoZWF0bWFwCgpJdCBjYW4gYmUgdXNlZnVsIHRvIHZpc3VhbGl6ZSB0byB2aXN1YWxpemUgaG93IG91ciBzYW1wbGVzIG9yZ2FuaXplIGFsb25nIHdpdGggdGhlIGV4cHJlc3Npb24gcGF0dGVybiBvZiBpbmRpdmlkdWFsIGdlbmVzLiBXZSB3aWxsIGNyZWF0ZSBhIGhlYXRtYXAgb2YgdGhlIHRvcCBtb3N0IHZhcmlhYmxlIGdlbmVzIHVzaW5nIHRoZSBgcGhlYXRtYXBgIGZ1bmN0aW9uLgoKQmVmb3JlIHRoZSBleGVyY2lzZSwgbGV0J3MgbG9vayBhdCB0aGUgaGVscCBwYWdlIHVzaW5nIHRoZSBgP2Agc3ltYm9sLgpgYGB7ciBwaGVhdERvYywgZXZhbD1GQUxTRX0KP3BoZWF0bWFwCmBgYAoKSWYgd2Ugc2Nyb2xsIGRvd24gdG8gdGhlIGV4YW1wbGVzLCB3ZSBjYW4gdGVzdCBzb21lIG9mIHRoZW0gb3V0OgpgYGB7ciB0ZXN0cGhlYXR9CiMgY29waWVkIGZyb20gdGhlIHBoZWF0bWFwIGRvY3VtZW50YXRpb24KdGVzdCA9IG1hdHJpeChybm9ybSgyMDApLCAyMCwgMTApCnRlc3RbMToxMCwgc2VxKDEsIDEwLCAyKV0gPSB0ZXN0WzE6MTAsIHNlcSgxLCAxMCwgMildICsgMwp0ZXN0WzExOjIwLCBzZXEoMiwgMTAsIDIpXSA9IHRlc3RbMTE6MjAsIHNlcSgyLCAxMCwgMildICsgMgp0ZXN0WzE1OjIwLCBzZXEoMiwgMTAsIDIpXSA9IHRlc3RbMTU6MjAsIHNlcSgyLCAxMCwgMildICsgNApjb2xuYW1lcyh0ZXN0KSA9IHBhc3RlKCJUZXN0IiwgMToxMCwgc2VwID0gIiIpCnJvd25hbWVzKHRlc3QpID0gcGFzdGUoIkdlbmUiLCAxOjIwLCBzZXAgPSAiIikKCgpwaGVhdG1hcCh0ZXN0KQpgYGAKCk5vdyB0aGF0IHdlJ3ZlIGdvdHRlbiBhIHNlbnNlIG9mIHdoYXQgYHBoZWF0bWFwYCBjYW4gZ2VuZXJhdGUsIGxldCcgbmF2aWdhdGUgdG8gb3VyIGZpcnN0IFticmVha291dCBleGVyY2lzZV0oaHR0cHM6Ly91bWljaC1icmNmLWJpb2luZi5naXRodWIuaW8vMjAyMi0wMy0xNC11bWljaC1ybmFzZXEtZGVteXN0aWZpZWQvaHRtbC9Nb2R1bGUwOWFfYnJlYWtvdXQuaHRtbCkKPGJyPgo8YnI+CgojIyBUcmFuc2ZlcnJpbmcgb3VyIHBsb3QgdG8gb3VyIGxvY2FsIGNvbXB1dGVyCgpSc3R1ZGlvIHNlcnZlciBhbGxvd3MgdXMgdG8gZG93bmxvYWQgZmlsZXMgdGhyb3VnaCB0aGUgaW50ZXJhY3RpdmUgZmlsZSBwYW5lbCBvbiB0aGUgcmlnaHQgc2lkZS4gSWYgd2UgbmF2aWdhdGUgaW50byB0aGUgcGxvdCBzdWJmb2xkZXIgYW5kIHNlbGVjdCB0aGUgYFBDQXBsb3RfcmxvZ19HdHlwZS5UeC5wZGZgIG9yIHRoZSAgZmlsZSwgd2UgY2FuIHRoZW4gY2xpY2sgdGhlIGJsdWUgZ2VhciBzeW1ib2wgbGFiZWxlZCBgTW9yZWAgYW5kIHNlbGVjdCBgRXhwb3J0Li4uYC4gV2Ugc2hvdWxkIHNlZSBhIHByb21wdCByZWdhcmRpbmcgdGhlIG5hbWUgb2YgdGhlIGZpbGUgYW5kIGlmIHdlIGNsaWNrIGBEb3dubG9hZGAgdGhlIGZpbGUgc2hvdWxkIHNob3cgdXAgaW4geW91ciBsb2NhbCAiRG93bmxvYWRzIiBmb2xkZXIuCgojIyMgT3B0aW9uYWwgLSBTY3JlZSBQbG90IGV4YW1wbGUgCgo8ZGV0YWlscz4KICAgIDxzdW1tYXJ5PipCb251czogQ2xpY2sgZm9yIGV4YW1wbGUgY29kZSBmb3IgZ2VuZXJhdGluZyBhIFNjcmVlUGxvdCo8L3N1bW1hcnk+CiAgICAgQSBzY3JlZXBsb3QgaXMgYSB3YXkgdG8gdmlzdWFsaXplIHRoZSB2YXJpYW5jZSBleHBsYWluZWQgYnkgYWxsIHByaW5jaXBsZSBjb21wb25lbnRzLgogICAgIFRvIGdlbmVyYXRlIGEgc2NyZWUgcGxvdCwgdGhlIFBDQSByZXN1bHRzIG5lZWQgdG8gYmUgdXNlZCBpbmRlcGVuZGVudGx5IG9mIHBsb3R0aW5nLCBzdWNoIGFzIGRlc2NyaWJlZCBieSBbdGhpcyBzdGF0cXVlc3QgcG9zdF0oaHR0cHM6Ly9zdGF0cXVlc3Qub3JnL3BjYS1jbGVhcmx5LWV4cGxhaW5lZC8pIGFuZCByZXBsaWNhdGVkIGJlbG93LgpgYGB7ciBTY3JlZVBsb3R9CiMgZ2VuZXJhdGUgUENBIGxvYWRpbmdzCnBjYSA8LSBwcmNvbXAodChhc3NheShybGQpKSwgc2NhbGU9VFJVRSkKCiMjIGdldCB0aGUgc2NyZWUgaW5mb3JtYXRpb24KcGNhLnZhciA8LSBwY2Ekc2Rldl4yCnNjcmVlIDwtIHBjYS52YXIvc3VtKHBjYS52YXIpCnAgPC0gYmFycGxvdCgoc2NyZWVbMToxMF0qMTAwKSwgbWFpbj0iU2NyZWUgUGxvdCIsIHhsYWI9IlByaW5jaXBhbCBDb21wb25lbnQiLCB5bGFiPSJQZXJjZW50IFZhcmlhdGlvbiIpCnByaW50KHApCmBgYAogICAgV2UgY2FuIHNlZSB0aGF0IHRoZSBtYWpvcml0eSAofjY1JSkgb2YgdGhlIHZhcmlhbmNlIGFjcm9zcyBvdXIgc2FtcGxlcyBpcyBleHBsYWluZWQgYnkgdGhlIGZpcnN0IHRocmVlIHByaW5jaXBsZSBjb21wb25lbnRzLCBnaXZpbmcgdXMgc29tZSBhZGRpdGlvbmFsIGNvbmZpZGVuY2UgcmVnYXJkaW5nIHRoZSBxdWFsaXR5IG9mIG91ciBkYXRhLgogICAgSW4gdGhlc2Ugc2NyZWUgcGxvdCBleGFtcGxlcyBmcm9tIEJpb1R1cmluZywgdGhlIHBsb3Qgb24gdGhlIGxlZnQgZml0cyB3aGF0IHdlIHdvdWxkIGV4cGVjdCBmb3IgYSBkYXRhc2V0IHdpdGggaGlnaCBzaWduYWwgZnJvbSB0aGUgZXhwZXJpbWVudGFsIHRyZWF0bWVudCwgd2hlcmUgdGhlIG1ham9yaXR5IG9mIHRoZSB2YXJpYW5jZSBpcyBleHBsYWluZWQgYnkgdGhlIGZpcnN0IGZldyBwcmluY2lwbGUgY29tcG9uZW50cy4gVGhlIHBsb3Qgb24gdGhlIHJpZ2h0IGlsbHVzdHJhdGVzIGEgc2NlbmFyaW8gd2hlcmUgdGhlIHZhcmlhbmNlIGlzIGRpc3RyaWJ1dGVkIGFjcm9zcyBtYW55IGNvbXBvbmVudHMsIHdoaWNoIGNvdWxkIGJlIGR1ZSB0byBsb3cgc2lnbmFsIGZyb20gdGhlIGV4cGVyaW1lbnRhbCB0cmVhdG1lbnQsIGNvbXBsZXggZXhwZXJpbWVudGFsIGRlc2lnbiwgb3IgY29uZm91bmRpbmcgZmFjdG9ycy4KaW1hZ2U6ICFbXSguL2ltYWdlcy9wcm9wb3J0aW9uLW9mLXZhcmlhbmNlLWJsb2ctaG9yei5qcGcpCjwvZGV0YWlscz4KCgo8YnI+CgojIFN1bW1hcnkKCkluIHRoaXMgc2VjdGlvbiwgd2U6ICAgIAoKKiBEaXNjdXNzZWQgdmFyaWFuY2Ugd2l0aGluIHRyZWF0bWVudCBncm91cHMgICAgIAoqIERpc2N1c3NlZCB0ZWNobmljYWwgYXJ0aWZhY3RzLCBpbmNsdWRpbmcgYmF0Y2hlcwoqIExlYXJuZWQgdG8gZ2VuZXJhdGUgUENBIHBsb3RzCgoKCi0tLQoKCiMgU291cmNlcyBVc2VkICAgIAoqIEhCQyBRQyB0dXRvcmlhbDogaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vREdFX3dvcmtzaG9wL2xlc3NvbnMvMDNfREdFX1FDX2FuYWx5c2lzLmh0bWwgICAgCiogRGV0YWlsZWQgSGVhdG1hcCB0dXRvcmlhbCBmcm9tIEdhbGF4eTogaHR0cHM6Ly90cmFpbmluZy5nYWxheHlwcm9qZWN0Lm9yZy90cmFpbmluZy1tYXRlcmlhbC90b3BpY3MvdHJhbnNjcmlwdG9taWNzL3R1dG9yaWFscy9ybmEtc2VxLXZpei13aXRoLWhlYXRtYXAyL3R1dG9yaWFsLmh0bWwgICAKKiBQQ0EgT3ZlcnZpZXc6IGh0dHBzOi8vYmxvZy5iaW90dXJpbmcuY29tLzIwMTgvMDYvMTQvcHJpbmNpcGFsLWNvbXBvbmVudC1hbmFseXNpcy1leHBsYWluZWQtc2ltcGx5LyAgICAgCgoKCmBgYHtyIFdyaXRlT3V0LlJEYXRhLCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojSGlkZGVuIGNvZGUgYmxvY2sgdG8gd3JpdGUgb3V0IGRhdGEgZm9yIGtuaXR0aW5nCnNhdmUuaW1hZ2UoZmlsZSA9ICJyZGF0YS9SdW5uaW5nRGF0YS5SRGF0YSIpCmBgYAoKCgotLS0KCgpUaGVzZSBtYXRlcmlhbHMgaGF2ZSBiZWVuIGFkYXB0ZWQgYW5kIGV4dGVuZGVkIGZyb20gbWF0ZXJpYWxzIGxpc3RlZCBhYm92ZS4gVGhlc2UgYXJlIG9wZW4gYWNjZXNzIG1hdGVyaWFscyBkaXN0cmlidXRlZCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIFtDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uIGxpY2Vuc2UgKENDIEJZIDQuMCldKGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzQuMC8pLCB3aGljaCBwZXJtaXRzIHVucmVzdHJpY3RlZCB1c2UsIGRpc3RyaWJ1dGlvbiwgYW5kIHJlcHJvZHVjdGlvbiBpbiBhbnkgbWVkaXVtLCBwcm92aWRlZCB0aGUgb3JpZ2luYWwgYXV0aG9yIGFuZCBzb3VyY2UgYXJlIGNyZWRpdGVkLgo=