In this module, we will learn:

  • How to generate & interpret common QC visualizations
  • Implications of batch effect or other technical variablity
  • When to revise the model used in the DESeq2 initialization


Differential Expression Workflow

Prior to testing for differential expression between our comparisons of interest, we’ll first generate plots that will assess how well our samples match up with our expectations (based on their treatment groups) and what we might expect to see from our differential expression comparisons.


Quality Control Visualizations

We have already examined quality control assessment at the sequencing level with FastQC and MultiQC. Now, we will examine sample-level and gene-level quality control for our expression data.

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 fit our expectations from the experiments design and examine 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 throughout the materials for today or in the bonus content module.

Principal Component Analysis

A common and very useful plot for evaluating how well our samples cluster by treatment groups are Principal Component Analysis (PCA) plots. PCA is used to bring out patterns in large and complex datasets using dimensionality reduction.

As described in more detail in this blogpost from BioTuring with a helpful step by step explaination, PCA has three key attributes:

  • Reduces the high dimensionality of a dataset into a few components.
  • Each principle component captures the maximum remaining variation in a dataset (as illustrated for PC1 and PC2 in the figure below).
  • Each principle component is defined by different weights of the original dimensions (e.g. gene expression).
Figure from BioTuring - principle component analysis explained simply
Figure from BioTuring - principle component analysis explained simply

A more detailed overview of the PCA procedure is outlined in a Harvard Chan Bioinformatic Core training module, which 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 principal components. We see this in an example plot from the DESeq2 vignette:

Question

How might we interpret the variance explained by each principal component in the context of the labeled sample points?

Evaluating batch effects or possible confounders

PCA plots are also useful for evaluating the impact of “uninteresting” sources of variance, like library preparation or sequencer differences. Evidence of batch effects can sometimes be quite obvious. Let’s look at the PCA plot from the DESeq2 vigentte again but re-colored to highlight structure in the data.

It turns out this experiment contained samples sequenced single-end and paired-end. If we add an attribute to the plot for the sequencing type (paired-end vs. single-end), we see that in this example PC2 (29% of variance) is primarily explained by this technical covariate.

However, since the samples are clearly separated by experimental condition on PC1 and there are balanced 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.


Create a PCA

We’ve already loaded the libraries we need for this module. We have also thought ahead in the previous module and created the outputs/figures and outputs/tables directories.

Below, we will plot the rlog normalized data and generate the PCA projections for the top 500 using the plotPCA function from DESeq2, first specifying condition as the condition of interest, and view the simple plot generated by the function.

# create PCA plot, labeled by condition
pca_plot = plotPCA(rld, intgroup = c('condition'), ntop = 500)
using ntop=500 top features by variance
pca_plot

The samples don’t appear to cluster too tightly on their condition, but we do observe that they separate in PC2. With real data, it is often the case that data doesn’t cluster as well as you’d expect, or that the covariate of interest is not associated with the first (or second or third) principal component. That doesn’t necessarily mean the experiment is a failure, but it does raise questions such as “What is associated with PC1?”.

Next, let’s plot the same data but now labeling by the batch column from our metadata and adding a title with ggtitle:

# create PCA plot with batches labeled
pca_plot_batch = plotPCA(rld, intgroup = c('batch'), ntop = 500) + ggtitle("Batch labeled - iron deficiency data")
using ntop=500 top features by variance
pca_plot_batch

Check-in and looking forward

Based on this plot, should we reconsider our original model that only included the condition column?**

Next, let’s save these plot as files in our outputs/figures folder to revisit later. Since both plots are ggplot objects – you can see this with class(pca_plot)– we can use ggsave().

# save PCA plots to file
ggsave(
    filename = file.path('outputs', 'figures', 'PCA_rlog_condition.pdf'),
    plot = pca_plot_batch,
    width = 6, height = 6, units = 'in')

ggsave(
    filename = file.path('outputs', 'figures', 'PCA_rlog_batch.pdf'),
    plot = pca_plot_batch,
    width = 6, height = 6, units = 'in')

Checkpoint: If you generated and saved the pca_plot PCA plot, please indicate with the green ‘check’ button. Otherwise, please use the red ‘x’ button in your zoom reaction panel and post any commands AND error messages to slack.

While the pca_plot object is a ggplot- we can also assign the underlying data generated by the plotPCA function to an object to make plot customization easier as well as calculate the percent variance of each component

# create table of underlying PCA data that can be used for more customized plots
pcaData <- plotPCA(rld, intgroup=c("condition"), returnData=TRUE)
using ntop=500 top features by variance
percentVar <- round(100 * attr(pcaData, "percentVar")) # store PC axes (% variance)

head(pcaData) # see the data
                PC1       PC2     group condition     name
sample_A  -5.842793 -9.228278   control   control sample_A
sample_B   6.184870 -2.568785   control   control sample_B
sample_C   5.317918 -2.683508   control   control sample_C
sample_D -18.170356  5.027729 deficient deficient sample_D
sample_E   5.116193  1.010066 deficient deficient sample_E
sample_F   7.394167  8.442776 deficient deficient sample_F
str(pcaData) # check the structure
'data.frame':   6 obs. of  5 variables:
 $ PC1      : num  -5.84 6.18 5.32 -18.17 5.12 ...
 $ PC2      : num  -9.23 -2.57 -2.68 5.03 1.01 ...
 $ group    : Factor w/ 2 levels "control","deficient": 1 1 1 2 2 2
 $ condition: Factor w/ 2 levels "control","deficient": 1 1 1 2 2 2
 $ name     : chr  "sample_A" "sample_B" "sample_C" "sample_D" ...
 - attr(*, "percentVar")= num [1:2] 0.582 0.223

With this table of PCA statistics, we can use what we learned at the end of the Computational Foundations Workshop to customize plot as we might see necessary.

What are some modifications we might want to make? [use slack poll to rank]:

Examples:

  • Change the overall theme of the plot
  • Add an informative plot title to make interpretation/sharing easier
  • Add labels to show which samples correspond to which points
  • Make our color palette color-blind friendly
  • Use shape instead of color of the points to indicate groups on the PCA plot


Customizing a PCA

Before working independently on an exercise, we’ll work together to build a command to plot our pcaData with more familiar ggplot2 syntax.

In this example, we’ll still use color to indicate our groups but we’ll customize the plot to have the same black and white theme that we used in Computational Foundations and add an informative title. Recall that ggplot2 adds plot components in layers, and we can add additional layers with the + sign.

# create custom plot object
PCACustom <- ggplot(pcaData, aes(PC1, PC2, color=condition)) +
  geom_point(size=3) +
  coord_fixed() +
  theme_bw() + 
  labs(title = "PC1 and PC2 for iron deficient mouse samples")

# add percentVar labels to *displayed plot*
PCACustom + 
  xlab(paste0("PC1: ",percentVar[1],"% variance")) +
  ylab(paste0("PC2: ",percentVar[2],"% variance"))

# add percentVar labels to *stored plot object*
PCACustom2 <- PCACustom + 
  xlab(paste0("PC1: ",percentVar[1],"% variance")) +
  ylab(paste0("PC2: ",percentVar[2],"% variance"))

Now we have our PCA plotted with ggplot functions that might look more familiar.

Independent Exercise - Customize a PCA

(10-15 minutes)

Like earlier - we’ll plan to work independently in the main room. This is a time to read function documentation, test out ideas, make mistakes, and use a search engine to look up errors or possible example solutions. Please post any questions or errors that arise to slack. We’ll review possible solutions together at the end.

Try doing the following to the pca_plot, starting with the “most popular” request and moving on to other customizations if you have time:

  • Add a title and subtitle to the plot
  • Update the color palette to be color-blind friendly
  • Add labels to show which samples correspond to which points
  • Use shape instead of color to indicate groups on the PCA plot.
  • Challenge: Change the legend title to “Iron Status”.

Link to exercise

Possible solutions to exercise prompts

Here are examples of some possible approaches:

  • Add a title and subtitle to the ggplot plot
# add title and subtitle
PCACustom2 + 
  labs(title = "Iron Supplemented Mice", subtitle = "PCA of top 500 genes")
  • Add labels to show which samples correspond to which points
# display with labels
PCACustom2 + 
  geom_text_repel(aes(label = name), 
                  point.padding = 0.5, 
                  box.padding = 0.5)
  • Make our color palette more color-blind friendly
# look at pre-made color palettes from RColorBrewer
display.brewer.all(colorblindFriendly = TRUE)
# use RColorBrewer palette
PCACustom2 + 
  scale_colour_brewer(palette = "Set2")

# OR customize using manual color palette
# The R Cookbook palette with grey:
cbPalette <- c("#999999", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7")

# To use for line and point colors, add manual color scaling with custom palette
PCACustom2 + 
  scale_colour_manual(values=cbPalette[2:3])
  • Use shape instead of color to indicate groups on the PCA plot.
# generate new aesthetic mapping (with default shapes selected)
ggplot(pcaData, aes(PC1, PC2, shape=condition)) +
  geom_point(size=3) +
  coord_fixed() +
  theme_bw() + 
  xlab(paste0("PC1: ",percentVar[1],"% variance")) +
  ylab(paste0("PC2: ",percentVar[2],"% variance"))

# generate new aesthetic mapping (with manually selected shapes)
ggplot(pcaData, aes(PC1, PC2, shape=condition)) +
  geom_point(size=3) +
  scale_shape_manual(values = c(1, 4)) +
  coord_fixed() +
  theme_bw() + 
  xlab(paste0("PC1: ",percentVar[1],"% variance")) +
  ylab(paste0("PC2: ",percentVar[2],"% variance"))
  • Challenge: Change the legend title to “Iron Status”
#  customize label for colour mapping
PCACustom2 + 
  guides(colour=guide_legend(title="Iron supplementation status")) 

# alternatively specify label for aesthetic mapping
PCACustom2 + 
  labs(colour="Iron supplementation status")



Download plots

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_condition.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 content

Click for example code for generating a ScreePlot visualization of variance across all principle components

A screeplot is a way to visualize the variance explained by all principal 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
pcaLoadings = prcomp(t(assay(rld)), scale. = TRUE)

## get the scree information
pca.var = pcaLoadings$sdev^2
scree = pca.var/sum(pca.var)
barplot((scree[1:10]*100), main="Scree Plot", xlab="Principal Component", ylab="Percent Variation")

We can see that the majority (~65%) of the variance across our samples is explained by the first three principal 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 principal 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:


Summary

In this section, we:

  • Discussed variance within treatment groups
  • Discussed technical artifacts, including batches
  • Learned to generate PCA plots

Sources


These materials have been adapted and extended from materials listed above. These are open access materials distributed under the terms of the Creative Commons Attribution license (CC BY 4.0), which permits unrestricted use, distribution, and reproduction in any medium, provided the original author and source are credited.




Previous lesson Top of this lesson Next lesson
LS0tCnRpdGxlOiAiU2FtcGxlIFF1YWxpdHkgQ29udHJvbCIKYXV0aG9yOiAiVU0gQmlvaW5mb3JtYXRpY3MgQ29yZSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgICAgICAgaHRtbF9kb2N1bWVudDoKICAgICAgICAgICAgaW5jbHVkZXM6CiAgICAgICAgICAgICAgICBpbl9oZWFkZXI6IGhlYWRlci5odG1sCiAgICAgICAgICAgIHRoZW1lOiBwYXBlcgogICAgICAgICAgICB0b2M6IHRydWUKICAgICAgICAgICAgdG9jX2RlcHRoOiA0CiAgICAgICAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICAgICAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICAgICAgICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICAgICAgICAgIG1hcmtkb3duOiBHRk0KICAgICAgICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CmJvZHksIHRkIHsKICAgZm9udC1zaXplOiAxOHB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDEycHg7Cn0KcHJlIHsKICBmb250LXNpemU6IDEycHgKfQo8L3N0eWxlPgoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0Kc291cmNlKCIuLi9iaW4vY2h1bmstb3B0aW9ucy5SIikKa25pdHJfZmlnX3BhdGgoIjA5LSIpCmBgYAoKSW4gdGhpcyBtb2R1bGUsIHdlIHdpbGwgbGVhcm46CgoqIEhvdyB0byBnZW5lcmF0ZSAmIGludGVycHJldCBjb21tb24gUUMgdmlzdWFsaXphdGlvbnMKKiBJbXBsaWNhdGlvbnMgb2YgYmF0Y2ggZWZmZWN0IG9yIG90aGVyIHRlY2huaWNhbCB2YXJpYWJsaXR5CiogV2hlbiB0byByZXZpc2UgdGhlIG1vZGVsIHVzZWQgaW4gdGhlIERFU2VxMiBpbml0aWFsaXphdGlvbgoKCjxicj4KCmBgYHtyIE1vZHVsZXMsIGV2YWw9VFJVRSwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShERVNlcTIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKbGlicmFyeShtYXRyaXhTdGF0cykKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKIyBsb2FkKCJyZGF0YS9SdW5uaW5nRGF0YS5SRGF0YSIpCmBgYAoKIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBXb3JrZmxvdyB7LnVubGlzdGVkIC51bm51bWJlcmVkfQoKUHJpb3IgdG8gdGVzdGluZyBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYmV0d2VlbiBvdXIgY29tcGFyaXNvbnMgb2YgaW50ZXJlc3QsIHdlJ2xsIGZpcnN0IGdlbmVyYXRlIHBsb3RzIHRoYXQgd2lsbCBhc3Nlc3MgaG93IHdlbGwgb3VyIHNhbXBsZXMgbWF0Y2ggdXAgd2l0aCBvdXIgZXhwZWN0YXRpb25zIChiYXNlZCBvbiB0aGVpciB0cmVhdG1lbnQgZ3JvdXBzKSBhbmQgd2hhdCB3ZSBtaWdodCBleHBlY3QgdG8gc2VlIGZyb20gb3VyIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGNvbXBhcmlzb25zLgoKIVtdKC4vaW1hZ2VzL3dheWZpbmRlci93YXlmaW5kZXItU2FtcGxlUUNWaXoucG5nKXt3aWR0aD03NSV9CgotLS0KCiMgUXVhbGl0eSBDb250cm9sIFZpc3VhbGl6YXRpb25zCgpXZSBoYXZlIGFscmVhZHkgZXhhbWluZWQgcXVhbGl0eSBjb250cm9sIGFzc2Vzc21lbnQgYXQgdGhlIHNlcXVlbmNpbmcgbGV2ZWwgd2l0aCBgRmFzdFFDYCBhbmQgYE11bHRpUUNgLiBOb3csIHdlIHdpbGwgZXhhbWluZSBzYW1wbGUtbGV2ZWwgYW5kIGdlbmUtbGV2ZWwgcXVhbGl0eSBjb250cm9sIGZvciBvdXIgZXhwcmVzc2lvbiBkYXRhLgoKVG8gZG8gdGhpcywgd2Ugd2lsbCBmaXJzdCBhc3Nlc3MgdGhlIHNpbWlsYXJpdHkgb2Ygb3VyIHNhbXBsZXMgYnkgdXNpbmcgcHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcyAoUENBKS4gVGhpcyB3aWxsIGFsbG93IHVzIHRvIGRldGVybWluZSBob3cgd2VsbCBwYXR0ZXJucyBpbiB0aGUgZGF0YSBmaXQgb3VyIGV4cGVjdGF0aW9ucyBmcm9tIHRoZSBleHBlcmltZW50cyBkZXNpZ24gYW5kIGV4YW1pbmUgcG9zc2libGUgc291cmNlcyBvZiB2YXJpYXRpb24uCgpPdGhlciBjb21tb24gdmlzdWFsaXphdGlvbnMgdGhhdCB3ZSBnZW5lcmF0ZSBmb3Igb3VyIGFuYWx5c2VzIGluY2x1ZGUgZXhwcmVzc2lvbiBoZWF0bWFwcywgc2FtcGxlIGNvcnJlbGF0aW9uIGhlYXRtYXBzLCBhbmQgYm94cGxvdHMgb2YgcmF3IGFuZC9vciBub3JtYWxpemVkIGNvdW50cywgdGhlIGNvZGUgZm9yIHdoaWNoIChkdWUgdG8gdGltZSByZXN0cmljdGlvbnMpIGNhbiBiZSBmb3VuZCBhcyBib251cyBjb250ZW50IHRocm91Z2hvdXQgdGhlIG1hdGVyaWFscyBmb3IgdG9kYXkgb3IgaW4gdGhlIGJvbnVzIGNvbnRlbnQgbW9kdWxlLgoKIyBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzCgpBIGNvbW1vbiBhbmQgdmVyeSB1c2VmdWwgcGxvdCBmb3IgZXZhbHVhdGluZyBob3cgd2VsbCBvdXIgc2FtcGxlcyBjbHVzdGVyIGJ5IHRyZWF0bWVudCBncm91cHMgYXJlIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkgcGxvdHMuIFBDQSBpcyB1c2VkIHRvIGJyaW5nIG91dCBwYXR0ZXJucyBpbiBsYXJnZSBhbmQgY29tcGxleCBkYXRhc2V0cyB1c2luZyBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24uCgpBcyBkZXNjcmliZWQgaW4gbW9yZSBkZXRhaWwgaW4gW3RoaXMgYmxvZ3Bvc3QgZnJvbSBCaW9UdXJpbmcgd2l0aCBhIGhlbHBmdWwgc3RlcCBieSBzdGVwIGV4cGxhaW5hdGlvbiBdKGh0dHBzOi8vYmxvZy5iaW90dXJpbmcuY29tLzIwMTgvMDYvMTQvcHJpbmNpcGFsLWNvbXBvbmVudC1hbmFseXNpcy1leHBsYWluZWQtc2ltcGx5LyksIFBDQSBoYXMgdGhyZWUga2V5IGF0dHJpYnV0ZXM6CgoqIFJlZHVjZXMgdGhlIGhpZ2ggZGltZW5zaW9uYWxpdHkgb2YgYSBkYXRhc2V0IGludG8gYSBmZXcgY29tcG9uZW50cy4gIAoqIEVhY2ggcHJpbmNpcGxlIGNvbXBvbmVudCBjYXB0dXJlcyB0aGUgbWF4aW11bSByZW1haW5pbmcgdmFyaWF0aW9uIGluIGEgZGF0YXNldCAoYXMgaWxsdXN0cmF0ZWQgZm9yIFBDMSBhbmQgUEMyIGluIHRoZSBmaWd1cmUgYmVsb3cpLiAgCiogRWFjaCBwcmluY2lwbGUgY29tcG9uZW50IGlzIGRlZmluZWQgYnkgZGlmZmVyZW50IHdlaWdodHMgb2YgdGhlIG9yaWdpbmFsIGRpbWVuc2lvbnMgKGUuZy4gZ2VuZSBleHByZXNzaW9uKS4gIAoKIVtGaWd1cmUgZnJvbSBCaW9UdXJpbmcgLSBwcmluY2lwbGUgY29tcG9uZW50IGFuYWx5c2lzIGV4cGxhaW5lZCBzaW1wbHldKC4vaW1hZ2VzL0Jsb2dfcGNhXzZiLnBuZykKCgpBIG1vcmUgZGV0YWlsZWQgb3ZlcnZpZXcgb2YgdGhlIFBDQSBwcm9jZWR1cmUgaXMgb3V0bGluZWQgaW4gW2EgSGFydmFyZCBDaGFuIEJpb2luZm9ybWF0aWMgQ29yZSB0cmFpbmluZyBtb2R1bGVdKGh0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL0RHRV93b3Jrc2hvcC9sZXNzb25zL3ByaW5jaXBhbF9jb21wb25lbnRfYW5hbHlzaXMuaHRtbCksIHdoaWNoIGlzIGJhc2VkIG9uIGEgbW9yZSB0aG9yb3VnaCBkZXNjcmlwdGlvbiBwcmVzZW50ZWQgaW4gYSBbU3RhdFF1ZXN04oCZcyB2aWRlb10oaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1fVVZIbmVCVUJXMCkuIEFkZGl0aW9uYWxseSwgdGhpcyBbVG93YXJkc0RhdGFTY2llbmNlIGJsb2cgcG9zdF0oaHR0cHM6Ly90b3dhcmRzZGF0YXNjaWVuY2UuY29tL3ByaW5jaXBhbC1jb21wb25lbnQtYW5hbHlzaXMtM2MzOWZiZjVjYjlkKSBnb2VzIHRocm91Z2ggdGhlIG1hdGggYmVoaW5kIFBDQXMuCgoKIyMgSW50ZXJwcmV0aW5nIFBDQSBwbG90cwoKRm9yIG1vc3QgYnVsayBSTkEtc2VxIGV4cGVyaW1lbnRzLCB3ZSBleHBlY3QgdGhlIG1ham9yaXR5IG9mIHRoZSB0b3RhbCB2YXJpYW5jZSB0byBiZSBleHBsYWluZWQgYnkgdGhlIGZpcnN0IHR3byBvciB0aHJlZSBwcmluY2lwYWwgY29tcG9uZW50cy4gV2Ugc2VlIHRoaXMgaW4gYW4gZXhhbXBsZSBwbG90IGZyb20gdGhlIFtERVNlcTIgdmlnbmV0dGVdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCk6CgohW10oLi9pbWFnZXMvUENBMF9ERVNlcTJWaWduZXR0ZS5wbmcpCgo+ICMgUXVlc3Rpb24gey51bmxpc3RlZCAudW5udW1iZXJlZH0KPgo+IEhvdyBtaWdodCB3ZSBpbnRlcnByZXQgdGhlIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBlYWNoIHByaW5jaXBhbCBjb21wb25lbnQgaW4gdGhlIGNvbnRleHQgb2YgdGhlIGxhYmVsZWQgc2FtcGxlIHBvaW50cz8KCgojIyBFdmFsdWF0aW5nIGJhdGNoIGVmZmVjdHMgb3IgcG9zc2libGUgY29uZm91bmRlcnMKClBDQSBwbG90cyBhcmUgYWxzbyB1c2VmdWwgZm9yIGV2YWx1YXRpbmcgdGhlIGltcGFjdCBvZiAidW5pbnRlcmVzdGluZyIgc291cmNlcyBvZiB2YXJpYW5jZSwgbGlrZSBsaWJyYXJ5IHByZXBhcmF0aW9uIG9yIHNlcXVlbmNlciBkaWZmZXJlbmNlcy4gRXZpZGVuY2Ugb2YgYmF0Y2ggZWZmZWN0cyBjYW4gc29tZXRpbWVzIGJlIHF1aXRlIG9idmlvdXMuIExldCdzIGxvb2sgYXQgdGhlIFBDQSBwbG90IGZyb20gdGhlIERFU2VxMiB2aWdlbnR0ZSBhZ2FpbiBidXQgcmUtY29sb3JlZCB0byBoaWdobGlnaHQgc3RydWN0dXJlIGluIHRoZSBkYXRhLgoKIVtdKC4vaW1hZ2VzL1BDQTFfREVTZXEyVmlnbmV0dGUucG5nKQoKSXQgdHVybnMgb3V0IHRoaXMgZXhwZXJpbWVudCBjb250YWluZWQgc2FtcGxlcyBzZXF1ZW5jZWQgc2luZ2xlLWVuZCBhbmQgcGFpcmVkLWVuZC4gSWYgd2UgYWRkIGFuIGF0dHJpYnV0ZSB0byB0aGUgcGxvdCBmb3IgdGhlIHNlcXVlbmNpbmcgdHlwZSAocGFpcmVkLWVuZCB2cy4gc2luZ2xlLWVuZCksIHdlIHNlZSB0aGF0IGluIHRoaXMgZXhhbXBsZSBQQzIgKDI5JSBvZiB2YXJpYW5jZSkgaXMgcHJpbWFyaWx5IGV4cGxhaW5lZCBieSB0aGlzIHRlY2huaWNhbCBjb3ZhcmlhdGUuCgohW10oLi9pbWFnZXMvUENBMl9ERVNlcTJWaWduZXR0ZS5wbmcpCgpIb3dldmVyLCBzaW5jZSB0aGUgc2FtcGxlcyBhcmUgY2xlYXJseSBzZXBhcmF0ZWQgYnkgZXhwZXJpbWVudGFsIGNvbmRpdGlvbiBvbiBQQzEgKiphbmQqKiB0aGVyZSBhcmUgYmFsYW5jZWQgYmF0Y2hlcywgaWYgd2Ugc2F3IHRoaXMgcGF0dGVybiBpbiBvdXIgZGF0YSB3ZSBjb3VsZCBpbmNvcnBvcmF0ZSB0aGUgdGVjaG5pY2FsIGNvdmFyaWF0ZSBpbnRvIG91ciBtb2RlbCBkZXNpZ24sIHN1Y2ggYXMgb3V0bGluZWQgaW4gdGhlIFtERVNlcTIgdmlnbmV0dGVdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL0RFU2VxMi9pbnN0L2RvYy9ERVNlcTIuaHRtbCNtdWx0aS1mYWN0b3ItZGVzaWducykuCgo8ZGV0YWlscz4KICAgIDxzdW1tYXJ5PipDbGljayBmb3IgY29tcGxleCBkZXNpZ24gZGlzY3Vzc2lvbio8L3N1bW1hcnk+CiAgICBJbiBleHBlcmltZW50cyB3aXRoIG1vcmUgY29tcGxleCBkZXNpZ25zLCBzdWNoIGFzIHdoZW4gdGhlcmUgYXJlIGludGVyZXNlY3RpbmcvbXVsdGlwbGUgdHJlYXRtZW50IGNvbmRpdGlvbnMsIGl0IGNhbiBiZSBsZXNzIGNsZWFyIHdoYXQgY292YXJpYW50cyBhcmUgaW5mbHVlbmNpbmcgZXhwcmVzc2lvbiwgc3VjaCBhcyBpbGx1c3RyYXRlZCBmcm9tIFt0aGlzIGRvY3VtZW5hdGlvbiBmb3IgYSBtaWNyb2FycmF5IGFuYWx5c2lzIHRvb2xdKGh0dHA6Ly93d3cubW9sbWluZS5jb20vbWFnbWEvZ2xvYmFsX2FuYWx5c2lzL2JhdGNoX2VmZmVjdC5odG1sKS4KICAgIEZyb20gdGhlIFBDQSBsYWJlbGVkIGJ5IGV4cGVyaW1lbnRhbCB0cmVhdG1lbnQsIHdlIHNlZSB0aGF0IHNhbXBsZXMgZnJvbSB0aGUgdHJlYXRtZW50IGdyb3VwIGRvIG5vdCBjbHVzdGVyIHRvZ2V0aGVyIGFuZCB0aGF0IHRoZXJlIGlzIGhpZ2ggdmFyaWFuY2UgYWNyb3NzIGFsbCB0cmVhdG1lbnQgZ3JvdXBzLgogICAgIVtdKC4vaW1hZ2VzL2JhdGNoX2V4MWIuanBnKQogICAgSG93ZXZlciwgd2hlbiB0aGUgcGxvdCBpcyBjb2xvciBjb2RlZCBieSB0aGUgdGVjaG5pY2FsIGJhdGNoZXMgb2YgcHJvYmUgbGFiZWxpbmcsIHdlIHNlZSB0aGF0IHRoZSBwYXR0ZXJucyBpbiB0aGUgZGF0YSBhcmUgYmV0dGVyIGV4cGxhaW5lZCBieSBiYXRjaCB0aGFuIHRoZSBleHBlcmltZW50YWwgY29uZGl0aW9ucy4KICAgICFbXSguL2ltYWdlcy9iYXRjaF9leDFjLmpwZykKPC9kZXRhaWxzPgo8YnI+CgojIENyZWF0ZSBhIFBDQQoKV2UndmUgYWxyZWFkeSBsb2FkZWQgdGhlIGxpYnJhcmllcyB3ZSBuZWVkIGZvciB0aGlzIG1vZHVsZS4gV2UgaGF2ZSBhbHNvIHRob3VnaHQgYWhlYWQgaW4gdGhlIHByZXZpb3VzIG1vZHVsZSBhbmQgY3JlYXRlZCB0aGUgYG91dHB1dHMvZmlndXJlc2AgYW5kIGBvdXRwdXRzL3RhYmxlc2AgZGlyZWN0b3JpZXMuCgpCZWxvdywgd2Ugd2lsbCBwbG90IHRoZSBybG9nIG5vcm1hbGl6ZWQgZGF0YSBhbmQgZ2VuZXJhdGUgdGhlIFBDQSBwcm9qZWN0aW9ucyBmb3IgdGhlIHRvcCA1MDAgdXNpbmcgdGhlIGBwbG90UENBYCBmdW5jdGlvbiBmcm9tIERFU2VxMiwgZmlyc3Qgc3BlY2lmeWluZyBgY29uZGl0aW9uYCBhcyB0aGUgY29uZGl0aW9uIG9mIGludGVyZXN0LCBhbmQgdmlldyB0aGUgc2ltcGxlIHBsb3QgZ2VuZXJhdGVkIGJ5IHRoZSBmdW5jdGlvbi4KCmBgYHtyIFBDQXJsb2czfQojIGNyZWF0ZSBQQ0EgcGxvdCwgbGFiZWxlZCBieSBjb25kaXRpb24KcGNhX3Bsb3QgPSBwbG90UENBKHJsZCwgaW50Z3JvdXAgPSBjKCdjb25kaXRpb24nKSwgbnRvcCA9IDUwMCkKcGNhX3Bsb3QKYGBgCgpUaGUgc2FtcGxlcyBkb24ndCBhcHBlYXIgdG8gY2x1c3RlciB0b28gdGlnaHRseSBvbiB0aGVpciBgY29uZGl0aW9uYCwgYnV0IHdlIGRvIG9ic2VydmUgdGhhdCB0aGV5IHNlcGFyYXRlIGluIFBDMi4gV2l0aCByZWFsIGRhdGEsIGl0IGlzIG9mdGVuIHRoZSBjYXNlIHRoYXQgZGF0YSBkb2Vzbid0IGNsdXN0ZXIgYXMgd2VsbCBhcyB5b3UnZCBleHBlY3QsIG9yIHRoYXQgdGhlIGNvdmFyaWF0ZSBvZiBpbnRlcmVzdCBpcyBub3QgYXNzb2NpYXRlZCB3aXRoIHRoZSBmaXJzdCAob3Igc2Vjb25kIG9yIHRoaXJkKSBwcmluY2lwYWwgY29tcG9uZW50LiBUaGF0IGRvZXNuJ3QgbmVjZXNzYXJpbHkgbWVhbiB0aGUgZXhwZXJpbWVudCBpcyBhIGZhaWx1cmUsIGJ1dCBpdCBkb2VzIHJhaXNlIHF1ZXN0aW9ucyBzdWNoIGFzICJXaGF0IGlzIGFzc29jaWF0ZWQgd2l0aCBQQzE/Ii4KCk5leHQsIGxldCdzIHBsb3QgdGhlIHNhbWUgZGF0YSBidXQgbm93IGxhYmVsaW5nIGJ5IHRoZSBiYXRjaCBjb2x1bW4gZnJvbSBvdXIgbWV0YWRhdGEgYW5kIGFkZGluZyBhIHRpdGxlIHdpdGggYGdndGl0bGVgOgpgYGB7ciBCYXRjaFBDQX0KIyBjcmVhdGUgUENBIHBsb3Qgd2l0aCBiYXRjaGVzIGxhYmVsZWQKcGNhX3Bsb3RfYmF0Y2ggPSBwbG90UENBKHJsZCwgaW50Z3JvdXAgPSBjKCdiYXRjaCcpLCBudG9wID0gNTAwKSArIGdndGl0bGUoIkJhdGNoIGxhYmVsZWQgLSBpcm9uIGRlZmljaWVuY3kgZGF0YSIpCnBjYV9wbG90X2JhdGNoCmBgYAoKPiAjIyBDaGVjay1pbiBhbmQgbG9va2luZyBmb3J3YXJkIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9Cj4KPiBCYXNlZCBvbiB0aGlzIHBsb3QsIHNob3VsZCB3ZSByZWNvbnNpZGVyIG91ciBvcmlnaW5hbCBtb2RlbCB0aGF0IG9ubHkgaW5jbHVkZWQgdGhlIGBjb25kaXRpb25gIGNvbHVtbj8qKgoKCk5leHQsIGxldCdzIHNhdmUgdGhlc2UgcGxvdCBhcyBmaWxlcyBpbiBvdXIgYG91dHB1dHMvZmlndXJlc2AgZm9sZGVyIHRvIHJldmlzaXQgbGF0ZXIuIFNpbmNlIGJvdGggcGxvdHMgYXJlIGBnZ3Bsb3RgIG9iamVjdHMgLS0geW91IGNhbiBzZWUgdGhpcyB3aXRoIGBjbGFzcyhwY2FfcGxvdClgLS0gd2UgY2FuIHVzZSBgZ2dzYXZlKClgLgoKYGBge3Igc2F2ZV9wY2FfZ2dzYXZlLCBldmFsID0gRkFMU0V9CiMgc2F2ZSBQQ0EgcGxvdHMgdG8gZmlsZQpnZ3NhdmUoCiAgICBmaWxlbmFtZSA9IGZpbGUucGF0aCgnb3V0cHV0cycsICdmaWd1cmVzJywgJ1BDQV9ybG9nX2NvbmRpdGlvbi5wZGYnKSwKICAgIHBsb3QgPSBwY2FfcGxvdF9iYXRjaCwKICAgIHdpZHRoID0gNiwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAnaW4nKQoKZ2dzYXZlKAogICAgZmlsZW5hbWUgPSBmaWxlLnBhdGgoJ291dHB1dHMnLCAnZmlndXJlcycsICdQQ0FfcmxvZ19iYXRjaC5wZGYnKSwKICAgIHBsb3QgPSBwY2FfcGxvdF9iYXRjaCwKICAgIHdpZHRoID0gNiwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAnaW4nKQpgYGAKCgoKKipDaGVja3BvaW50Kio6ICpJZiB5b3UgZ2VuZXJhdGVkIGFuZCBzYXZlZCB0aGUgYHBjYV9wbG90YCBQQ0EgcGxvdCwgcGxlYXNlIGluZGljYXRlIHdpdGggdGhlIGdyZWVuICdjaGVjaycgYnV0dG9uLiBPdGhlcndpc2UsIHBsZWFzZSB1c2UgdGhlIHJlZCAneCcgYnV0dG9uIGluIHlvdXIgem9vbSByZWFjdGlvbiBwYW5lbCBhbmQgcG9zdCBhbnkgY29tbWFuZHMgQU5EIGVycm9yIG1lc3NhZ2VzIHRvIHNsYWNrLioKCgpXaGlsZSB0aGUgYHBjYV9wbG90YCBvYmplY3QgaXMgYSBgZ2dwbG90YC0gd2UgY2FuIGFsc28gYXNzaWduIHRoZSB1bmRlcmx5aW5nIGRhdGEgZ2VuZXJhdGVkIGJ5IHRoZSBgcGxvdFBDQWAgZnVuY3Rpb24gdG8gYW4gb2JqZWN0IHRvIG1ha2UgcGxvdCBjdXN0b21pemF0aW9uIGVhc2llciBhcyB3ZWxsIGFzIGNhbGN1bGF0ZSB0aGUgcGVyY2VudCB2YXJpYW5jZSBvZiBlYWNoIGNvbXBvbmVudCAKCmBgYHtyIFBDQVRhYmxlfQojIGNyZWF0ZSB0YWJsZSBvZiB1bmRlcmx5aW5nIFBDQSBkYXRhIHRoYXQgY2FuIGJlIHVzZWQgZm9yIG1vcmUgY3VzdG9taXplZCBwbG90cwpwY2FEYXRhIDwtIHBsb3RQQ0EocmxkLCBpbnRncm91cD1jKCJjb25kaXRpb24iKSwgcmV0dXJuRGF0YT1UUlVFKQpwZXJjZW50VmFyIDwtIHJvdW5kKDEwMCAqIGF0dHIocGNhRGF0YSwgInBlcmNlbnRWYXIiKSkgIyBzdG9yZSBQQyBheGVzICglIHZhcmlhbmNlKQoKaGVhZChwY2FEYXRhKSAjIHNlZSB0aGUgZGF0YQpzdHIocGNhRGF0YSkgIyBjaGVjayB0aGUgc3RydWN0dXJlCmBgYAoKCldpdGggdGhpcyB0YWJsZSBvZiBQQ0Egc3RhdGlzdGljcywgd2UgY2FuIHVzZSB3aGF0IHdlIGxlYXJuZWQgYXQgdGhlIGVuZCBvZiB0aGUgQ29tcHV0YXRpb25hbCBGb3VuZGF0aW9ucyBXb3Jrc2hvcCB0byBjdXN0b21pemUgcGxvdCBhcyB3ZSBtaWdodCBzZWUgbmVjZXNzYXJ5LiAKCioqV2hhdCBhcmUgc29tZSBtb2RpZmljYXRpb25zIHdlIG1pZ2h0IHdhbnQgdG8gbWFrZT8qKiBbdXNlIHNsYWNrIHBvbGwgdG8gcmFua106CgoqKkV4YW1wbGVzOioqCgotIENoYW5nZSB0aGUgb3ZlcmFsbCB0aGVtZSBvZiB0aGUgcGxvdAotIEFkZCBhbiBpbmZvcm1hdGl2ZSBwbG90IHRpdGxlIHRvIG1ha2UgaW50ZXJwcmV0YXRpb24vc2hhcmluZyBlYXNpZXIKLSBBZGQgbGFiZWxzIHRvIHNob3cgd2hpY2ggc2FtcGxlcyBjb3JyZXNwb25kIHRvIHdoaWNoIHBvaW50cwotIE1ha2Ugb3VyIGNvbG9yIHBhbGV0dGUgY29sb3ItYmxpbmQgZnJpZW5kbHkKLSBVc2Ugc2hhcGUgaW5zdGVhZCBvZiBjb2xvciBvZiB0aGUgcG9pbnRzIHRvIGluZGljYXRlIGdyb3VwcyBvbiB0aGUgUENBIHBsb3QKCjxicj4KCiMjIEN1c3RvbWl6aW5nIGEgUENBCgpCZWZvcmUgd29ya2luZyBpbmRlcGVuZGVudGx5IG9uIGFuIGV4ZXJjaXNlLCB3ZSdsbCB3b3JrIHRvZ2V0aGVyIHRvIGJ1aWxkIGEgY29tbWFuZCB0byBwbG90IG91ciBgcGNhRGF0YWAgd2l0aCBtb3JlIGZhbWlsaWFyIGBnZ3Bsb3QyYCBzeW50YXguIAoKSW4gdGhpcyBleGFtcGxlLCB3ZSdsbCBzdGlsbCB1c2UgY29sb3IgdG8gaW5kaWNhdGUgb3VyIGdyb3VwcyBidXQgd2UnbGwgY3VzdG9taXplIHRoZSBwbG90IHRvIGhhdmUgdGhlIHNhbWUgYmxhY2sgYW5kIHdoaXRlIHRoZW1lIHRoYXQgd2UgdXNlZCBpbiBDb21wdXRhdGlvbmFsIEZvdW5kYXRpb25zIGFuZCBhZGQgYW4gaW5mb3JtYXRpdmUgdGl0bGUuIFJlY2FsbCB0aGF0IGBnZ3Bsb3QyYCBhZGRzIHBsb3QgY29tcG9uZW50cyBpbiBsYXllcnMsIGFuZCB3ZSBjYW4gYWRkIGFkZGl0aW9uYWwgbGF5ZXJzIHdpdGggdGhlIGArYCBzaWduLgoKYGBge3IgUENBQ3VzdG9tRGVtb30KIyBjcmVhdGUgY3VzdG9tIHBsb3Qgb2JqZWN0ClBDQUN1c3RvbSA8LSBnZ3Bsb3QocGNhRGF0YSwgYWVzKFBDMSwgUEMyLCBjb2xvcj1jb25kaXRpb24pKSArCiAgZ2VvbV9wb2ludChzaXplPTMpICsKICBjb29yZF9maXhlZCgpICsKICB0aGVtZV9idygpICsgCiAgbGFicyh0aXRsZSA9ICJQQzEgYW5kIFBDMiBmb3IgaXJvbiBkZWZpY2llbnQgbW91c2Ugc2FtcGxlcyIpCgojIGFkZCBwZXJjZW50VmFyIGxhYmVscyB0byAqZGlzcGxheWVkIHBsb3QqClBDQUN1c3RvbSArIAogIHhsYWIocGFzdGUwKCJQQzE6ICIscGVyY2VudFZhclsxXSwiJSB2YXJpYW5jZSIpKSArCiAgeWxhYihwYXN0ZTAoIlBDMjogIixwZXJjZW50VmFyWzJdLCIlIHZhcmlhbmNlIikpCgojIGFkZCBwZXJjZW50VmFyIGxhYmVscyB0byAqc3RvcmVkIHBsb3Qgb2JqZWN0KgpQQ0FDdXN0b20yIDwtIFBDQUN1c3RvbSArIAogIHhsYWIocGFzdGUwKCJQQzE6ICIscGVyY2VudFZhclsxXSwiJSB2YXJpYW5jZSIpKSArCiAgeWxhYihwYXN0ZTAoIlBDMjogIixwZXJjZW50VmFyWzJdLCIlIHZhcmlhbmNlIikpCgpgYGAKCk5vdyB3ZSBoYXZlIG91ciBQQ0EgcGxvdHRlZCB3aXRoIGdncGxvdCBmdW5jdGlvbnMgdGhhdCBtaWdodCBsb29rIG1vcmUgZmFtaWxpYXIuCgoKCj4gIyMgSW5kZXBlbmRlbnQgRXhlcmNpc2UgLSBDdXN0b21pemUgYSBQQ0EgCj4gKDEwLTE1IG1pbnV0ZXMpCj4gCj4gTGlrZSBlYXJsaWVyIC0gd2UnbGwgcGxhbiB0byB3b3JrIGluZGVwZW5kZW50bHkgaW4gdGhlIG1haW4gcm9vbS4gVGhpcyBpcyBhIHRpbWUgdG8gcmVhZCBmdW5jdGlvbiBkb2N1bWVudGF0aW9uLCB0ZXN0IG91dCBpZGVhcywgbWFrZSBtaXN0YWtlcywgYW5kIHVzZSBhIHNlYXJjaCBlbmdpbmUgdG8gbG9vayB1cCBlcnJvcnMgb3IgcG9zc2libGUgZXhhbXBsZSBzb2x1dGlvbnMuIFBsZWFzZSBwb3N0IGFueSBxdWVzdGlvbnMgb3IgZXJyb3JzIHRoYXQgYXJpc2UgdG8gc2xhY2suIFdlJ2xsIHJldmlldyBwb3NzaWJsZSBzb2x1dGlvbnMgdG9nZXRoZXIgYXQgdGhlIGVuZC4KPiAKPiAKPiBUcnkgZG9pbmcgdGhlIGZvbGxvd2luZyB0byB0aGUgYHBjYV9wbG90YCwgc3RhcnRpbmcgd2l0aCB0aGUgIm1vc3QgcG9wdWxhciIgcmVxdWVzdCBhbmQgbW92aW5nIG9uIHRvIG90aGVyIGN1c3RvbWl6YXRpb25zIGlmIHlvdSBoYXZlIHRpbWU6Cj4KPiAqIEFkZCBhIHRpdGxlIGFuZCBzdWJ0aXRsZSB0byB0aGUgcGxvdAo+ICogVXBkYXRlIHRoZSBjb2xvciBwYWxldHRlIHRvIGJlIGNvbG9yLWJsaW5kIGZyaWVuZGx5Cj4gKiBBZGQgbGFiZWxzIHRvIHNob3cgd2hpY2ggc2FtcGxlcyBjb3JyZXNwb25kIHRvIHdoaWNoIHBvaW50cwo+ICogVXNlIHNoYXBlIGluc3RlYWQgb2YgY29sb3IgdG8gaW5kaWNhdGUgZ3JvdXBzIG9uIHRoZSBQQ0EgcGxvdC4KPiAqIF9DaGFsbGVuZ2VfOiBDaGFuZ2UgdGhlIGxlZ2VuZCB0aXRsZSB0byAiSXJvbiBTdGF0dXMiLiAKPgo+IFtMaW5rIHRvIGV4ZXJjaXNlXShNb2R1bGUwOWFfYnJlYWtvdXQuaHRtbCkKCgo8ZGV0YWlscz4KPHN1bW1hcnk+UG9zc2libGUgc29sdXRpb25zIHRvIGV4ZXJjaXNlIHByb21wdHM8L3N1bW1hcnk+CgpIZXJlIGFyZSBleGFtcGxlcyBvZiBzb21lIHBvc3NpYmxlIGFwcHJvYWNoZXM6CgoqIEFkZCBhIHRpdGxlIGFuZCBzdWJ0aXRsZSB0byB0aGUgZ2dwbG90IHBsb3QKYGBge3IsIGV2YWw9IEZBTFNFfQojIGFkZCB0aXRsZSBhbmQgc3VidGl0bGUKUENBQ3VzdG9tMiArIAogIGxhYnModGl0bGUgPSAiSXJvbiBTdXBwbGVtZW50ZWQgTWljZSIsIHN1YnRpdGxlID0gIlBDQSBvZiB0b3AgNTAwIGdlbmVzIikKYGBgCgoKKiBBZGQgbGFiZWxzIHRvIHNob3cgd2hpY2ggc2FtcGxlcyBjb3JyZXNwb25kIHRvIHdoaWNoIHBvaW50cwoKYGBge3IsIGV2YWw9IEZBTFNFfQojIGRpc3BsYXkgd2l0aCBsYWJlbHMKUENBQ3VzdG9tMiArIAogIGdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBuYW1lKSwgCiAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSAwLjUsIAogICAgICAgICAgICAgICAgICBib3gucGFkZGluZyA9IDAuNSkKYGBgCgoKKiBNYWtlIG91ciBjb2xvciBwYWxldHRlIG1vcmUgY29sb3ItYmxpbmQgZnJpZW5kbHkKCmBgYHtyLCBldmFsPSBGQUxTRX0KIyBsb29rIGF0IHByZS1tYWRlIGNvbG9yIHBhbGV0dGVzIGZyb20gUkNvbG9yQnJld2VyCmRpc3BsYXkuYnJld2VyLmFsbChjb2xvcmJsaW5kRnJpZW5kbHkgPSBUUlVFKQojIHVzZSBSQ29sb3JCcmV3ZXIgcGFsZXR0ZQpQQ0FDdXN0b20yICsgCiAgc2NhbGVfY29sb3VyX2JyZXdlcihwYWxldHRlID0gIlNldDIiKQoKIyBPUiBjdXN0b21pemUgdXNpbmcgbWFudWFsIGNvbG9yIHBhbGV0dGUKIyBUaGUgUiBDb29rYm9vayBwYWxldHRlIHdpdGggZ3JleToKY2JQYWxldHRlIDwtIGMoIiM5OTk5OTkiLCAiI0U2OUYwMCIsICIjNTZCNEU5IiwgIiMwMDlFNzMiLCAiI0YwRTQ0MiIsICIjMDA3MkIyIiwgIiNENTVFMDAiLCAiI0NDNzlBNyIpCgojIFRvIHVzZSBmb3IgbGluZSBhbmQgcG9pbnQgY29sb3JzLCBhZGQgbWFudWFsIGNvbG9yIHNjYWxpbmcgd2l0aCBjdXN0b20gcGFsZXR0ZQpQQ0FDdXN0b20yICsgCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9Y2JQYWxldHRlWzI6M10pCmBgYAoKCiogVXNlIHNoYXBlIGluc3RlYWQgb2YgY29sb3IgdG8gaW5kaWNhdGUgZ3JvdXBzIG9uIHRoZSBQQ0EgcGxvdC4gCgpgYGB7ciwgZXZhbD0gRkFMU0V9CiMgZ2VuZXJhdGUgbmV3IGFlc3RoZXRpYyBtYXBwaW5nICh3aXRoIGRlZmF1bHQgc2hhcGVzIHNlbGVjdGVkKQpnZ3Bsb3QocGNhRGF0YSwgYWVzKFBDMSwgUEMyLCBzaGFwZT1jb25kaXRpb24pKSArCiAgZ2VvbV9wb2ludChzaXplPTMpICsKICBjb29yZF9maXhlZCgpICsKICB0aGVtZV9idygpICsgCiAgeGxhYihwYXN0ZTAoIlBDMTogIixwZXJjZW50VmFyWzFdLCIlIHZhcmlhbmNlIikpICsKICB5bGFiKHBhc3RlMCgiUEMyOiAiLHBlcmNlbnRWYXJbMl0sIiUgdmFyaWFuY2UiKSkKCiMgZ2VuZXJhdGUgbmV3IGFlc3RoZXRpYyBtYXBwaW5nICh3aXRoIG1hbnVhbGx5IHNlbGVjdGVkIHNoYXBlcykKZ2dwbG90KHBjYURhdGEsIGFlcyhQQzEsIFBDMiwgc2hhcGU9Y29uZGl0aW9uKSkgKwogIGdlb21fcG9pbnQoc2l6ZT0zKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMSwgNCkpICsKICBjb29yZF9maXhlZCgpICsKICB0aGVtZV9idygpICsgCiAgeGxhYihwYXN0ZTAoIlBDMTogIixwZXJjZW50VmFyWzFdLCIlIHZhcmlhbmNlIikpICsKICB5bGFiKHBhc3RlMCgiUEMyOiAiLHBlcmNlbnRWYXJbMl0sIiUgdmFyaWFuY2UiKSkKYGBgCgoKKiBfQ2hhbGxlbmdlXzogQ2hhbmdlIHRoZSBsZWdlbmQgdGl0bGUgdG8gIklyb24gU3RhdHVzIgoKYGBge3IsIGV2YWw9IEZBTFNFfQojICBjdXN0b21pemUgbGFiZWwgZm9yIGNvbG91ciBtYXBwaW5nClBDQUN1c3RvbTIgKyAKICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZCh0aXRsZT0iSXJvbiBzdXBwbGVtZW50YXRpb24gc3RhdHVzIikpIAoKIyBhbHRlcm5hdGl2ZWx5IHNwZWNpZnkgbGFiZWwgZm9yIGFlc3RoZXRpYyBtYXBwaW5nClBDQUN1c3RvbTIgKyAKICBsYWJzKGNvbG91cj0iSXJvbiBzdXBwbGVtZW50YXRpb24gc3RhdHVzIikKYGBgCgo8L2RldGFpbHM+Cjxicj4KCgotLS0tCgoKIyBEb3dubG9hZCBwbG90cwoKUnN0dWRpbyBzZXJ2ZXIgYWxsb3dzIHVzIHRvIGRvd25sb2FkIGZpbGVzIHRocm91Z2ggdGhlIGludGVyYWN0aXZlIGZpbGUgcGFuZWwgb24gdGhlIHJpZ2h0IHNpZGUuIElmIHdlIG5hdmlnYXRlIGludG8gdGhlIHBsb3Qgc3ViZm9sZGVyIGFuZCBzZWxlY3QgdGhlIGBQQ0FwbG90X3Jsb2dfY29uZGl0aW9uLnBkZmAgb3IgdGhlICBmaWxlLCB3ZSBjYW4gdGhlbiBjbGljayB0aGUgYmx1ZSBnZWFyIHN5bWJvbCBsYWJlbGVkIGBNb3JlYCBhbmQgc2VsZWN0IGBFeHBvcnQuLi5gLiBXZSBzaG91bGQgc2VlIGEgcHJvbXB0IHJlZ2FyZGluZyB0aGUgbmFtZSBvZiB0aGUgZmlsZSBhbmQgaWYgd2UgY2xpY2sgYERvd25sb2FkYCB0aGUgZmlsZSBzaG91bGQgc2hvdyB1cCBpbiB5b3VyIGxvY2FsICJEb3dubG9hZHMiIGZvbGRlci4KCiMgT3B0aW9uYWwgY29udGVudAoKPGRldGFpbHM+CiAgICA8c3VtbWFyeT4qQ2xpY2sgZm9yIGV4YW1wbGUgY29kZSBmb3IgZ2VuZXJhdGluZyBhIFNjcmVlUGxvdCB2aXN1YWxpemF0aW9uIG9mIHZhcmlhbmNlIGFjcm9zcyBhbGwgcHJpbmNpcGxlIGNvbXBvbmVudHMqPC9zdW1tYXJ5PgogICAgIEEgc2NyZWVwbG90IGlzIGEgd2F5IHRvIHZpc3VhbGl6ZSB0aGUgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGFsbCBwcmluY2lwYWwgY29tcG9uZW50cy4KICAgICBUbyBnZW5lcmF0ZSBhIHNjcmVlIHBsb3QsIHRoZSBQQ0EgcmVzdWx0cyBuZWVkIHRvIGJlIHVzZWQgaW5kZXBlbmRlbnRseSBvZiBwbG90dGluZywgc3VjaCBhcyBkZXNjcmliZWQgYnkgW3RoaXMgc3RhdHF1ZXN0IHBvc3RdKGh0dHBzOi8vc3RhdHF1ZXN0Lm9yZy9wY2EtY2xlYXJseS1leHBsYWluZWQvKSBhbmQgcmVwbGljYXRlZCBiZWxvdy4KCmBgYHtyIFNjcmVlUGxvdH0KIyBnZW5lcmF0ZSBQQ0EgbG9hZGluZ3MKcGNhTG9hZGluZ3MgPSBwcmNvbXAodChhc3NheShybGQpKSwgc2NhbGUuID0gVFJVRSkKCiMjIGdldCB0aGUgc2NyZWUgaW5mb3JtYXRpb24KcGNhLnZhciA9IHBjYUxvYWRpbmdzJHNkZXZeMgpzY3JlZSA9IHBjYS52YXIvc3VtKHBjYS52YXIpCmJhcnBsb3QoKHNjcmVlWzE6MTBdKjEwMCksIG1haW49IlNjcmVlIFBsb3QiLCB4bGFiPSJQcmluY2lwYWwgQ29tcG9uZW50IiwgeWxhYj0iUGVyY2VudCBWYXJpYXRpb24iKQpgYGAKCiAgICBXZSBjYW4gc2VlIHRoYXQgdGhlIG1ham9yaXR5ICh+NjUlKSBvZiB0aGUgdmFyaWFuY2UgYWNyb3NzIG91ciBzYW1wbGVzIGlzIGV4cGxhaW5lZCBieSB0aGUgZmlyc3QgdGhyZWUgcHJpbmNpcGFsIGNvbXBvbmVudHMsIGdpdmluZyB1cyBzb21lIGFkZGl0aW9uYWwgY29uZmlkZW5jZSByZWdhcmRpbmcgdGhlIHF1YWxpdHkgb2Ygb3VyIGRhdGEuCiAgICBJbiB0aGVzZSBzY3JlZSBwbG90IGV4YW1wbGVzIGZyb20gQmlvVHVyaW5nLCB0aGUgcGxvdCBvbiB0aGUgbGVmdCBmaXRzIHdoYXQgd2Ugd291bGQgZXhwZWN0IGZvciBhIGRhdGFzZXQgd2l0aCBoaWdoIHNpZ25hbCBmcm9tIHRoZSBleHBlcmltZW50YWwgdHJlYXRtZW50LCB3aGVyZSB0aGUgbWFqb3JpdHkgb2YgdGhlIHZhcmlhbmNlIGlzIGV4cGxhaW5lZCBieSB0aGUgZmlyc3QgZmV3IHByaW5jaXBhbCBjb21wb25lbnRzLiBUaGUgcGxvdCBvbiB0aGUgcmlnaHQgaWxsdXN0cmF0ZXMgYSBzY2VuYXJpbyB3aGVyZSB0aGUgdmFyaWFuY2UgaXMgZGlzdHJpYnV0ZWQgYWNyb3NzIG1hbnkgY29tcG9uZW50cywgd2hpY2ggY291bGQgYmUgZHVlIHRvIGxvdyBzaWduYWwgZnJvbSB0aGUgZXhwZXJpbWVudGFsIHRyZWF0bWVudCwgY29tcGxleCBleHBlcmltZW50YWwgZGVzaWduLCBvciBjb25mb3VuZGluZyBmYWN0b3JzLgppbWFnZTogIVtdKC4vaW1hZ2VzL3Byb3BvcnRpb24tb2YtdmFyaWFuY2UtYmxvZy1ob3J6LmpwZykKPC9kZXRhaWxzPgo8YnI+CgojIFN1bW1hcnkKCkluIHRoaXMgc2VjdGlvbiwgd2U6CgoqIERpc2N1c3NlZCB2YXJpYW5jZSB3aXRoaW4gdHJlYXRtZW50IGdyb3VwcwoqIERpc2N1c3NlZCB0ZWNobmljYWwgYXJ0aWZhY3RzLCBpbmNsdWRpbmcgYmF0Y2hlcwoqIExlYXJuZWQgdG8gZ2VuZXJhdGUgUENBIHBsb3RzCgotLS0KCiMgU291cmNlcwoKKiBIQkMgUUMgdHV0b3JpYWw6IGh0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL0RHRV93b3Jrc2hvcC9sZXNzb25zLzAzX0RHRV9RQ19hbmFseXNpcy5odG1sCiogRGV0YWlsZWQgSGVhdG1hcCB0dXRvcmlhbCBmcm9tIEdhbGF4eTogaHR0cHM6Ly90cmFpbmluZy5nYWxheHlwcm9qZWN0Lm9yZy90cmFpbmluZy1tYXRlcmlhbC90b3BpY3MvdHJhbnNjcmlwdG9taWNzL3R1dG9yaWFscy9ybmEtc2VxLXZpei13aXRoLWhlYXRtYXAyL3R1dG9yaWFsLmh0bWwKKiBQQ0EgT3ZlcnZpZXc6IGh0dHBzOi8vYmxvZy5iaW90dXJpbmcuY29tLzIwMTgvMDYvMTQvcHJpbmNpcGFsLWNvbXBvbmVudC1hbmFseXNpcy1leHBsYWluZWQtc2ltcGx5LwoKYGBge3IgV3JpdGVPdXQuUkRhdGEsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNIaWRkZW4gY29kZSBibG9jayB0byB3cml0ZSBvdXQgZGF0YSBmb3Iga25pdHRpbmcKIyBzYXZlLmltYWdlKGZpbGUgPSAicmRhdGEvUnVubmluZ0RhdGEuUkRhdGEiKQpgYGAKCi0tLQoKClRoZXNlIG1hdGVyaWFscyBoYXZlIGJlZW4gYWRhcHRlZCBhbmQgZXh0ZW5kZWQgZnJvbSBtYXRlcmlhbHMgbGlzdGVkIGFib3ZlLiBUaGVzZSBhcmUgb3BlbiBhY2Nlc3MgbWF0ZXJpYWxzIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgW0NyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24gbGljZW5zZSAoQ0MgQlkgNC4wKV0oaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvNC4wLyksIHdoaWNoIHBlcm1pdHMgdW5yZXN0cmljdGVkIHVzZSwgZGlzdHJpYnV0aW9uLCBhbmQgcmVwcm9kdWN0aW9uIGluIGFueSBtZWRpdW0sIHByb3ZpZGVkIHRoZSBvcmlnaW5hbCBhdXRob3IgYW5kIHNvdXJjZSBhcmUgY3JlZGl0ZWQuCgo8YnIvPgo8YnIvPgo8aHIvPgp8IFtQcmV2aW91cyBsZXNzb25dKE1vZHVsZTA4X0RFU2VxMkRFLmh0bWwpIHwgW1RvcCBvZiB0aGlzIGxlc3Nvbl0oI3RvcCkgfCBbTmV4dCBsZXNzb25dKE1vZHVsZTEwX0RFQ29tcGFyaXNvbnMuaHRtbCkgfAp8IDotLS0gfCA6LS0tLTogfCAtLS06IHw=