Acknowledgements

Material created by Peter Mac Data Science.

Objectives

  • Demonstrate how R can be used to visualise RNA-Seq data with volcano plots
  • Demonstrate some base R syntax (ifelse(), c(), %in%)
  • Utilize dplyr’s mutate() to add columns to tables.
  • Utilize ggplot2’s geom_point() to create a volcano plot
  • Utilize ggrepel’s ggplot_text_repel() to repel overlapping labels

Introduction

In this tutorial, we will learn some R through creating volcano plots from an RNA-seq experiment.

Volcano plots are commonly used to display the results of RNA-seq or other omics experiments. A volcano plot is a type of scatterplot that shows statistical significance (P value) versus magnitude of change (fold change). It enables quick visual identification of genes with large fold changes that are also statistically significant. These may be the most biologically significant genes. In a volcano plot, the most upregulated genes are towards the right, the most downregulated genes are towards the left, and the most statistically significant genes are towards the top.

RNA-seq dataset

We will again use the published RNA-seq data from the Nature Cell Biology paper by Fu et al. 2015. This study examined expression in basal and luminal cells from mice at different stages (virgin, pregnant and lactating).

To create the volcano plot we will work with the differential expression results. We will use the results from comparing the luminal cells from the mammary gland of the pregnant versus the luminal cells from the lactating mice. This is a file that contains the log fold changes (logFC) and P values for the genes. If a gene has a positive logFC value, it means it is upregulated in the luminal cells of the pregnant mice compared to the luminal cells from the lactating mice. If a gene has a negative logFC value, it means it is downregulated in the luminal cells of the pregnant mice compared to the luminal cells from the lactating mice.

   

Packages

We have already seen some tidyverse packages: readr for reading in files, ggplot2 for plotting. Today we will use those packages again aswell as another really useful tidyverse package called dplyr that can be used to manipulate tables. We will also use a non-tidyverse R package called ggrepel.

Loading the data

First let’s open a new R script. From the top menu in RStudio: File > New File > R Script. Let’s save it as volcano_plots.R.

We will begin by loading in the packages that we need. These are already installed for you but if you need to install them on your own computer you can use install.packages().

library(tidyverse)
library(ggrepel)

Next we load in our RNA-seq data. The file we will use is tab-separated, so we will use the read_tsv() function from the tidyverse readr package to read it in. We will store the contents of our file in an object called de_results.

de_results <- read_tsv("data/limma-voom_luminalpregnant-luminallactate.tsv.gz")
Parsed with column specification:
cols(
  ENTREZID = col_double(),
  SYMBOL = col_character(),
  GENENAME = col_character(),
  logFC = col_double(),
  AveExpr = col_double(),
  t = col_double(),
  P.Value = col_double(),
  adj.P.Val = col_double(),
  B = col_double()
)

There should be 15,804 rows and 8 columns. We can see that in the Environment tab on the right.

We can type de_results to have a look at the data.

de_results

We can’t see all the columns here but we can look at all the data with View() or use the R function colnames() if we just want to see the column names.

View(de_results)
colnames(de_results)

Creating a volcano plot

To make a volcano plot we make a scatterplot using geom_point() and plot the log fold change (logFC) vs the negative log10 P value. Why do we use the negative log 10 P value and not just the P value? Let’s have a look at what happens if we plot the P value as is versus the logFC. We use the columns called logFC and P.Value.

ggplot(data=de_results, mapping=aes(x=logFC, y=P.Value)) +
    geom_point()

Genes with small P values are the most significant and in this plot they are all squashed at the bottom of the plot, not very easy to see. Let’s see what happens when we use log10().

ggplot(data=de_results, mapping=aes(x=logFC, y=log10(P.Value))) +
    geom_point()

Looks better but this still has the most significant genes at the bottom and the usual way is to plot the most significant at the top so we use the negative log10 (-log10). Let’s try that.

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value))) +
    geom_point()

We could shange the size of the points if we wanted, by adding size=. We can add that into the geom_point().

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value))) +
    geom_point(size=7)

If we want all the points to be the same size we don’t put size= inside aes(). However if we wanted to map the size of the points to a column in our dataset then we would put it inside aes() as shown below.

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value))) +
    geom_point(mapping=aes(size=logFC))

We could change the shape of the points with shape=. For a triangle shape we add shape=2. See here for the codes for the different possible shapes.

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value))) +
    geom_point(shape=2)

We could change the colour of all the points with colour=.

ggplot(data=de_results, mapping=aes(logFC, -log10(P.Value))) +
    geom_point(colour="red")

We could change the size and the shape and colour.

ggplot(data=de_results, mapping=aes(logFC, -log10(P.Value))) +
    geom_point(size=7, shape=2, colour="red")

But maybe that doesn’t look so good.

Highlighting significant genes

Using a P value threshold

Let’s colour genes that are significant. We will call genes significantly differentially expressed if they have an adjusted P Value below 0.05. An adjusted P value means the P value has been adjusted for multiple testing, as we are testing many thousands of genes. This is what we filter on in RNA-Seq.

Remember we saw in the previous tutorial that we can use a column to colour features of our data with fill= or col=. In this case we are colouring points so we use col=.

We will add a column that says whether genes are significant or not. To add columns we use dplyr’s mutate().

Let’s have a look at mutate first to see how it works. We give mutate our data (de_results), then a name for the column we want to create.

Let’s make a column called signif (for significance). Then we put what we want to have in the column in brackets after the column name. For example, if we wanted to label every gene as significant we could write below. We’ll try running it and store it in an object called testing as we’re just seeing how it works.

testing <- mutate(de_results, signif=("Signif"))

We can use View() to have a look at dataset. There should be a new column called signif at the end with “Signif” in every row. mutate always adds the column to the end of the table.

View(testing)

Exercise

  • Add another column called labels to the testing object that contains the value “Labels” in every row.

But obviously not all genes are significant. We want to only call genes significant if they have a P value below 0.05. We can use an R function called ifelse() to say if our adj.P.Val is below 0.05 add “Signif” to the sig column, otherwise add “Not signif”.

We can take a look at the ifelse help to see how it works. The help tells us the Usage is ifelse(test, yes, no). Our “test” is whether our gene has an adj.P.Val < 0.05, if the answer is yes, we’ll add “Signif” into the column, if the answer is no, then we’ll add “Not signif”. We’ll save the output as de_results (this overwrites the original de_results object).

de_results <- mutate(de_results, signif=ifelse(adj.P.Val < 0.05, "Signif", "Not signif"))

Let’s take a look at de_results again now. We should see we have a new column at the end called “signif”.

View(de_results)

In View() we can sort on the signif column to check we see both Signif and Not signif entries.

Now that we have a column that flags whether the genes are significant or not we can use that to colour the significant genes by adding col=signif to our ggplot.

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value), col=signif)) +
    geom_point()

We might want to change the colours.

There are built-in colour palettes, that can be handy to use, where the sets of colours are predefined. scale_colour_brewer() is a popular one (there is also scale_fill_brewer()). You can take a look at the help for scale_colour_brewer() to see what pallettes are available. There is also an R colours cheatsheet that shows the colours of the palettes here.

There’s one called “Dark2”, let’s have a look at that.

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value), col=signif)) +
  geom_point() +
  scale_colour_brewer(palette = "Dark2")

There’s one called “Set1”, let’s have a look at that.

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value), col=signif)) +
  geom_point() +
  scale_colour_brewer(palette = "Set1")

Or we could choose to set the colours manually. We could decide to colour the non-significant genes grey and the significant genes red. We are using col= so to specify our own colours we add + scale_colour_manual(values=)) (as we will see, we can keep adding layers to our plot with +). If we were using fill= we would use + scale_fill_manual(values=))

To use two colours we add + scale_colour_manual(values=c("red", "grey")). Note that here we see the function c() for the first time. We use function extremely often in R when we have multiple items that we are combining. Here we have two colours we want to use, so we need to use c() to combine them to give to values=. Thus we need to add values=c("red", "grey").

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value), col=signif)) +
  geom_point() +
  scale_colour_manual(values=c("red", "grey"))

Hmm this is the wrong way around, our significant points are grey. We could change the order of the colours in values=.

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value), col=signif)) +
  geom_point() +
  scale_colour_manual(values=c("grey", "red"))

Or we could specify which value in our signif column we want to map to each colour.

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value), col=signif)) +
  geom_point() +
  scale_colour_manual(values=c("Signif"="red", "Not signif"="grey"))

Exercise

  • Colour the volcanoplot using two different colours of your choice. You can use one of the palettes or type colours() to see what colours are available. Choose two nice colours or two ugly ones. The colours can be seen in the R colours cheatsheet here.

Using P value and logFC thresholds

We could colour our significant genes that are downregulated and the genes that are upregulated using separate colours. These are the genes upregulated in the luminal cells from the pregnant mice compared to the luminal cells from the lactating mice. To do this we can change our signif column and have three values instead of two. We could have “Up”“,”Down" and “Not signif”.

Let’s colour significant genes > logFC of 1, red and < logFC -1, blue. To do this we change our signif column by adding another ifelse(). We are asking: if genes are significantly up, add the value “Up” otherwise if the genes are significantly down, add the value “Down” otherwise add the value “Not signif”.

To ask if are genes have an adj.P.Value < 0.05 and also have a logFC > 1, we use the syntax adj.P.Val < 0.05 & logFC > 1, note the &. If they are < 0.05 and have a logFC < -1 we use adj.P.Val < 0.05 & logFC > 1. We write this criteria as below and save as de_results again.

de_results <- mutate(de_results, signif=ifelse((adj.P.Val < 0.05 & logFC > 1), "Up", ifelse((adj.P.Val < 0.05 & logFC < -1), "Down", "Not signif")))

Let’s take a look at the output.

View(de_results)

Now that we have our signif column with three values, “Up”, “Down”, “Not signif”, we can colour the volcano plot points, up - red, not signif - grey, and down - blue.

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value), col=signif)) +
  geom_point() +
  scale_colour_manual(values=c("Up"="red", "Not signif"="grey", "Down"="blue"))

Labelling genes

Labelling genes of interest

We can label one or more genes of interest in a volcano plot. This enables us to visualize where these genes are in terms of significance and in comparison to the other genes. In the original paper using this dataset, there is a heatmap of 31 genes in Figure 6b. These genes are a set of 30 cytokines/growth factor identified as differentially expressed, and the authors’ main gene of interest, Mcl1. These genes are provided in the volcano genes file and shown below. We will label these genes in the volcano plot. We’ll read in the genes and store it in an object called goi.

goi <- read_tsv("data/volcano_genes")
Parsed with column specification:
cols(
  GeneID = col_character()
)

Let’s take a look at what’s in goi

goi

goi contains gene symbols stored in a column. We need to get these symbols out of the column format. We can do that with dplyr’s pull(). We give pull() the goi object and the name of the column we want to pull the values from. We’ll store the symbols in an object called goi_syms.

goi_syms <- pull(goi, GeneID)

Take a look.

goi_syms
 [1] "Mcl1"   "Hbegf"  "Tgfb2"  "Cxcl16" "Csf1"   "Pdgfb"  "Edn1"   "Lif"    "Kitl"   "Bmp1"   "Pdgfa"  "Cmtm3" 
[13] "Cx3cl1" "Ctgf"   "Wnt5a"  "Ptn"    "Spp1"   "Bmp3"   "Cmtm8"  "Gmfg"   "Cxcl2"  "Cxcl3"  "Il15"   "Egf"   
[25] "Cmtm7"  "Il34"   "Pdgfd"  "Nov"    "Cmtm6"  "Ccl28"  "Cxcl1" 

We’ll add another columns for these labels, let’s call it mygenes. We do this similar to what we did for the top 10 genes.

de_results <- mutate(de_results, mygenes=ifelse(SYMBOL %in% goi_syms, SYMBOL, ""))

Let’s make the volcano plot and label this custom set of genes. We add + geom_text() and add the labels into that. Note we are using + again and adding another layer to our plot. This time we are adding a layer of text (labels). We put labels=mygenes inside aes() in geom_text() as we are mapping to a column in our data.

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value), col=signif)) +
  geom_point() +
  scale_colour_manual(values=c("Up"="red", "Not signif"="grey", "Down"="blue")) +
  geom_text(mapping=aes(label=mygenes))

The legend now has a legend for the labels (the letter “a”) overlapping the original legend but we can remove that by adding show.legend=FALSE.

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value), col=signif)) +
  geom_point() +
  scale_colour_manual(values=c("Up"="red", "Not signif"="grey", "Down"="blue")) +
  geom_text(mapping=aes(label=mygenes), show.legend = FALSE)

The labels also don’t look great as the labels are overlapping but we can fix that. We can replace + geom_text() with + geom_text_repel() from the package ggrepel.

Note that running geom_text_repel() on the server can be very slow (take minutes), think it may be the same issue reported in Stack Overflow here, but it should run a lot quicker on your laptop/desktop.

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value), col=signif)) +
  geom_point() +
  scale_colour_manual(values=c("Up"="red", "Not signif"="grey", "Down"="blue")) +
  geom_text_repel(mapping=aes(label=mygenes))

If we want, we can colour just the points red and blue, and leave the labels black. We do this by adding the col=signif into the geom_point() aes() instead of in the ggplot() aes().

ggplot(data=de_results, mapping=aes(logFC, -log10(P.Value))) +
  geom_point(aes(col=signif)) +
  scale_colour_manual(values=c("Up"="red", "Not signif"="grey", "Down"="blue")) +
  geom_text_repel(mapping=aes(label=mygenes), show.legend = FALSE)

Labelling top significant genes

We could also label the top significant genes with the gene names so we can see what they are. We have gene symbols in our de_results so we could use those.

Let’s label the top 10 most significant genes. To do this we first identify the top 10 genes by P.Value. The file is already sorted by P value and remember head() shows us the top 6 lines in a file. If we look at the help for ?head we see that there is a default argument n = 6L, this is why it returns 6 lines. We can change this to 10L to get the top 10 lines. We will store this at an object called top10.

top10 <- head(de_results, n = 10L)

Take a look.

top10

Exercise

  • Make a volcano plot labelling the top 10 genes. This is very similar to what we did for the genes of interest (goi) e.g. you will need to extract the gene symbols from the top10 info and store them in an object called top10_syms, then add a column called e.g. top10 to the de_results that says which genes to label.

Modifying the plot

Legend

What if we want the categories in the legend in a different order, for example, Up, Not signif, Down. We can addbreaks= with the order we want into scale_color_manual(). Note that here we use c() again to pass multiple values to breaks.

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value), col=signif)) +
  geom_point() +
  scale_colour_manual(values=c("Up"="red", "Not signif"="grey", "Down"="blue"), breaks=c("Up", "Down", "Not signif"))

We can change the name of the legend by adding name= into scale_colour_manual.

ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value), col=signif)) +
  geom_point() +
  scale_colour_manual(name="My new name", values=c("Up"="red", "Not signif"="grey", "Down"="blue"))

We can also make the plot look nicer, for example, adjusting the x axis limits and label, adding a title and changing the background.

First let’s store our current plot in an object called p, to make it easier to see what we’re changing.

p <- ggplot(data=de_results, mapping=aes(x=logFC, y=-log10(P.Value))) +
  geom_point(mapping=aes(col=signif)) +
  scale_colour_manual(values=c("Up"="red", "Not signif"="grey", "Down"="blue"))

Note that when we store the plot in an object it doesn’t print out the plot. If we want to see the plot we type the name of the object p.

p

Axis limits

We can adjust the x axis so it’s the same limit on the right and left with scale_x_continuous(). If we had categories (discrete values) on the x axis we would use scale_x_discrete() instead. There are also equivalents for the y axis (scale_y_continuous() and scale_y_discrete()).

p <- p + scale_x_continuous(limits=c(-10, 10))
p

Axis labels

We can change the axis labels with labs(). To change the x axis label we use labs(x="New name"). To change the y axis label we use labs(y="New name").

p <- p + labs(x="Log2 fold change")
p

Title

We can add a title with labs().

p <- p + labs(title="Luminal pregnant vs lactating")
p

Themes

We can remove the grey background and grid lines. To do this we modify the ggplot theme. Themes are the non-data parts of the plot.

There are also a lot of built-in themes. Let’s have a look at a couple of the more widely used themes. We won’t save these (we won’t use p <-) we’ll just print them to have a look. The default ggplot theme is theme_grey().

p + theme_bw()

p + theme_minimal()

There are many themes available, you can see some in the R graph gallery.

We can also modify parts of the theme individually. We can remove the grey background and grid lines with the code below.

p <- p + theme(panel.background = element_blank(), 
               panel.grid.major = element_blank(), 
               panel.grid.minor = element_blank())
p

We can add axis lines.

p <- p + theme(axis.line = element_line(size=0.2, colour = "black"))
p

We can remove the legend completely.

p <- p + theme(legend.position = "none")
p

We can centre the title.

p <- p + theme(plot.title = element_text(hjust = 0.5))
p

Exercise

Key Points

  • We can make a volcano plot with geom_point() by plotting the log fold change (logFC) vs the negative log10 P value
  • We can add columns with dplyr’s mutate()
  • To colour or label specific points we add a column to specify which points to colour or label
  • We can use ifelse() to test if values meet specified conditions
  • We use c() to combine multiple values
  • We use %in% to check if a value is in a set of values
  • We can use dplyr’s pull() to pull a set of values out of a column
  • We can use geom_text() to add text to a plot
  • We can use the ggrepel package to repel overlapping labels
  • We use themes to modify the non-data parts of a ggplot
LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFIiCmF1dGhvcjogIk1hcmlhIERveWxlIGFuZCBMaXogQ2hyaXN0aWUiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCICVZJylgIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogNApzdWJ0aXRsZTogdmlzdWFsaXNpbmcgUk5BLXNlcSBkYXRhIHdpdGggdGhlIHRpZHl2ZXJzZSAodm9sY2FubyBwbG90KQotLS0KCiMjIyMgQWNrbm93bGVkZ2VtZW50cwpNYXRlcmlhbCBjcmVhdGVkIGJ5IFBldGVyIE1hYyBEYXRhIFNjaWVuY2UuCgojIyBPYmplY3RpdmVzCgoqIERlbW9uc3RyYXRlIGhvdyBSIGNhbiBiZSB1c2VkIHRvIHZpc3VhbGlzZSBSTkEtU2VxIGRhdGEgd2l0aCB2b2xjYW5vIHBsb3RzCiogRGVtb25zdHJhdGUgc29tZSBiYXNlIFIgc3ludGF4IChgaWZlbHNlKClgLCBgYygpYCwgYCVpbiVgKQoqIFV0aWxpemUgZHBseXIncyBgbXV0YXRlKClgIHRvIGFkZCBjb2x1bW5zIHRvIHRhYmxlcy4KKiBVdGlsaXplIGdncGxvdDIncyBgZ2VvbV9wb2ludCgpYCB0byBjcmVhdGUgYSB2b2xjYW5vIHBsb3QKKiBVdGlsaXplIGdncmVwZWwncyBgZ2dwbG90X3RleHRfcmVwZWxgKCkgdG8gcmVwZWwgb3ZlcmxhcHBpbmcgbGFiZWxzCgojIyBJbnRyb2R1Y3Rpb24KCkluIHRoaXMgdHV0b3JpYWwsIHdlIHdpbGwgbGVhcm4gc29tZSBSIHRocm91Z2ggY3JlYXRpbmcgdm9sY2FubyBwbG90cyBmcm9tIGFuIFJOQS1zZXEgZXhwZXJpbWVudC4KClZvbGNhbm8gcGxvdHMgYXJlIGNvbW1vbmx5IHVzZWQgdG8gZGlzcGxheSB0aGUgcmVzdWx0cyBvZiBSTkEtc2VxIG9yIG90aGVyIG9taWNzIGV4cGVyaW1lbnRzLiBBIHZvbGNhbm8gcGxvdCBpcyBhIHR5cGUgb2Ygc2NhdHRlcnBsb3QgdGhhdCBzaG93cyBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UgKFAgdmFsdWUpIHZlcnN1cyBtYWduaXR1ZGUgb2YgY2hhbmdlIChmb2xkIGNoYW5nZSkuIEl0IGVuYWJsZXMgcXVpY2sgdmlzdWFsIGlkZW50aWZpY2F0aW9uIG9mIGdlbmVzIHdpdGggbGFyZ2UgZm9sZCBjaGFuZ2VzIHRoYXQgYXJlIGFsc28gc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4gVGhlc2UgbWF5IGJlIHRoZSBtb3N0IGJpb2xvZ2ljYWxseSBzaWduaWZpY2FudCBnZW5lcy4gSW4gYSB2b2xjYW5vIHBsb3QsIHRoZSBtb3N0IHVwcmVndWxhdGVkIGdlbmVzIGFyZSB0b3dhcmRzIHRoZSByaWdodCwgdGhlIG1vc3QgZG93bnJlZ3VsYXRlZCBnZW5lcyBhcmUgdG93YXJkcyB0aGUgbGVmdCwgYW5kIHRoZSBtb3N0IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgZ2VuZXMgYXJlIHRvd2FyZHMgdGhlIHRvcC4KCiMjIyBSTkEtc2VxIGRhdGFzZXQKCldlIHdpbGwgYWdhaW4gdXNlIHRoZSBwdWJsaXNoZWQgUk5BLXNlcSBkYXRhIGZyb20gdGhlIE5hdHVyZSBDZWxsIEJpb2xvZ3kgcGFwZXIgYnkgW0Z1IGV0IGFsLiAyMDE1XShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3B1Ym1lZC8yNTczMDQ3MikuIFRoaXMgc3R1ZHkgZXhhbWluZWQgZXhwcmVzc2lvbiBpbiBiYXNhbCBhbmQgbHVtaW5hbCBjZWxscyBmcm9tIG1pY2UgYXQgZGlmZmVyZW50IHN0YWdlcyAodmlyZ2luLCBwcmVnbmFudCBhbmQgbGFjdGF0aW5nKS4gCgohW10oaW1hZ2VzL21vdXNlX2V4cC5wbmcpCgpUbyBjcmVhdGUgdGhlIHZvbGNhbm8gcGxvdCB3ZSB3aWxsIHdvcmsgd2l0aCB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcmVzdWx0cy4gV2Ugd2lsbCB1c2UgdGhlIHJlc3VsdHMgZnJvbSBjb21wYXJpbmcgdGhlIGx1bWluYWwgY2VsbHMgZnJvbSB0aGUgbWFtbWFyeSBnbGFuZCBvZiB0aGUgcHJlZ25hbnQgdmVyc3VzIHRoZSBsdW1pbmFsIGNlbGxzIGZyb20gdGhlIGxhY3RhdGluZyBtaWNlLiBUaGlzIGlzIGEgZmlsZSB0aGF0IGNvbnRhaW5zIHRoZSBsb2cgZm9sZCBjaGFuZ2VzIChsb2dGQykgYW5kIFAgdmFsdWVzIGZvciB0aGUgZ2VuZXMuIElmIGEgZ2VuZSBoYXMgYSBwb3NpdGl2ZSBsb2dGQyB2YWx1ZSwgaXQgbWVhbnMgaXQgaXMgdXByZWd1bGF0ZWQgaW4gdGhlIGx1bWluYWwgY2VsbHMgb2YgdGhlIHByZWduYW50IG1pY2UgY29tcGFyZWQgdG8gdGhlIGx1bWluYWwgY2VsbHMgZnJvbSB0aGUgbGFjdGF0aW5nIG1pY2UuIElmIGEgZ2VuZSBoYXMgYSBuZWdhdGl2ZSBsb2dGQyB2YWx1ZSwgaXQgbWVhbnMgaXQgaXMgZG93bnJlZ3VsYXRlZCBpbiB0aGUgbHVtaW5hbCBjZWxscyBvZiB0aGUgcHJlZ25hbnQgbWljZSBjb21wYXJlZCB0byB0aGUgbHVtaW5hbCBjZWxscyBmcm9tIHRoZSBsYWN0YXRpbmcgbWljZS4KClwgIApcICAKCiMjIyBQYWNrYWdlcwoKV2UgaGF2ZSBhbHJlYWR5IHNlZW4gc29tZSAqKnRpZHl2ZXJzZSoqIHBhY2thZ2VzOiAqKnJlYWRyKiogZm9yIHJlYWRpbmcgaW4gZmlsZXMsICoqZ2dwbG90MioqIGZvciBwbG90dGluZy4gVG9kYXkgd2Ugd2lsbCB1c2UgdGhvc2UgcGFja2FnZXMgYWdhaW4gYXN3ZWxsIGFzIGFub3RoZXIgcmVhbGx5IHVzZWZ1bCB0aWR5dmVyc2UgcGFja2FnZSBjYWxsZWQgKipkcGx5cioqIHRoYXQgY2FuIGJlIHVzZWQgdG8gbWFuaXB1bGF0ZSB0YWJsZXMuIFdlIHdpbGwgYWxzbyB1c2UgYSBub24tdGlkeXZlcnNlIFIgcGFja2FnZSBjYWxsZWQgW2dncmVwZWxdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ3JlcGVsL3ZpZ25ldHRlcy9nZ3JlcGVsLmh0bWwpLgoKIyMgTG9hZGluZyB0aGUgZGF0YQoKRmlyc3QgbGV0J3Mgb3BlbiBhIG5ldyBSIHNjcmlwdC4gRnJvbSB0aGUgdG9wIG1lbnUgaW4gUlN0dWRpbzogYEZpbGUgPiBOZXcgRmlsZSA+IFIgU2NyaXB0YC4KTGV0J3Mgc2F2ZSBpdCBhcyBgdm9sY2Fub19wbG90cy5SYC4KCldlIHdpbGwgYmVnaW4gYnkgbG9hZGluZyBpbiB0aGUgcGFja2FnZXMgdGhhdCB3ZSBuZWVkLiBUaGVzZSBhcmUgYWxyZWFkeSBpbnN0YWxsZWQgZm9yIHlvdSBidXQgaWYgeW91IG5lZWQgdG8gaW5zdGFsbCB0aGVtIG9uIHlvdXIgb3duIGNvbXB1dGVyIHlvdSBjYW4gdXNlIGBpbnN0YWxsLnBhY2thZ2VzKClgLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdncmVwZWwpCmBgYAoKTmV4dCB3ZSBsb2FkIGluIG91ciBSTkEtc2VxIGRhdGEuIFRoZSBmaWxlIHdlIHdpbGwgdXNlIGlzIHRhYi1zZXBhcmF0ZWQsIHNvIHdlIHdpbGwgdXNlIHRoZSBgcmVhZF90c3YoKWAgZnVuY3Rpb24gZnJvbSB0aGUgdGlkeXZlcnNlIHJlYWRyIHBhY2thZ2UgdG8gcmVhZCBpdCBpbi4gV2Ugd2lsbCBzdG9yZSB0aGUgY29udGVudHMgb2Ygb3VyIGZpbGUgaW4gYW4gb2JqZWN0IGNhbGxlZCBgZGVfcmVzdWx0c2AuCgpgYGB7cn0KZGVfcmVzdWx0cyA8LSByZWFkX3RzdigiZGF0YS9saW1tYS12b29tX2x1bWluYWxwcmVnbmFudC1sdW1pbmFsbGFjdGF0ZS50c3YuZ3oiKQpgYGAKClRoZXJlIHNob3VsZCBiZSAxNSw4MDQgcm93cyBhbmQgOCBjb2x1bW5zLiBXZSBjYW4gc2VlIHRoYXQgaW4gdGhlIEVudmlyb25tZW50IHRhYiBvbiB0aGUgcmlnaHQuCgpXZSBjYW4gdHlwZSBgZGVfcmVzdWx0c2AgdG8gaGF2ZSBhIGxvb2sgYXQgdGhlIGRhdGEuCmBgYHtyfQpkZV9yZXN1bHRzCmBgYAoKV2UgY2FuJ3Qgc2VlIGFsbCB0aGUgY29sdW1ucyBoZXJlIGJ1dCB3ZSBjYW4gbG9vayBhdCBhbGwgdGhlIGRhdGEgd2l0aCBgVmlldygpYCBvciB1c2UgdGhlIFIgZnVuY3Rpb24gYGNvbG5hbWVzKClgIGlmIHdlIGp1c3Qgd2FudCB0byBzZWUgdGhlIGNvbHVtbiBuYW1lcy4KCmBgYHtyIGV2YWw9RkFMU0V9ClZpZXcoZGVfcmVzdWx0cykKYGBgCgpgYGB7ciBldmFsPUZBTFNFfQpjb2xuYW1lcyhkZV9yZXN1bHRzKQpgYGAKCiMjIENyZWF0aW5nIGEgdm9sY2FubyBwbG90CgpUbyBtYWtlIGEgdm9sY2FubyBwbG90IHdlIG1ha2UgYSBzY2F0dGVycGxvdCB1c2luZyBgZ2VvbV9wb2ludCgpYCBhbmQgcGxvdCB0aGUgbG9nIGZvbGQgY2hhbmdlIChsb2dGQykgdnMgdGhlIG5lZ2F0aXZlIGxvZzEwIFAgdmFsdWUuIFdoeSBkbyB3ZSB1c2UgdGhlIG5lZ2F0aXZlIGxvZyAxMCBQIHZhbHVlIGFuZCBub3QganVzdCB0aGUgUCB2YWx1ZT8gTGV0J3MgaGF2ZSBhIGxvb2sgYXQgd2hhdCBoYXBwZW5zIGlmIHdlIHBsb3QgdGhlIFAgdmFsdWUgYXMgaXMgdmVyc3VzIHRoZSBsb2dGQy4gV2UgdXNlIHRoZSBjb2x1bW5zIGNhbGxlZCBsb2dGQyBhbmQgUC5WYWx1ZS4gCgpgYGB7cn0KZ2dwbG90KGRhdGE9ZGVfcmVzdWx0cywgbWFwcGluZz1hZXMoeD1sb2dGQywgeT1QLlZhbHVlKSkgKwogICAgZ2VvbV9wb2ludCgpCmBgYApHZW5lcyB3aXRoIHNtYWxsIFAgdmFsdWVzIGFyZSB0aGUgbW9zdCBzaWduaWZpY2FudCBhbmQgaW4gdGhpcyBwbG90IHRoZXkgYXJlIGFsbCBzcXVhc2hlZCBhdCB0aGUgYm90dG9tIG9mIHRoZSBwbG90LCBub3QgdmVyeSBlYXN5IHRvIHNlZS4gTGV0J3Mgc2VlIHdoYXQgaGFwcGVucyB3aGVuIHdlIHVzZSBgbG9nMTAoKWAuCgpgYGB7cn0KZ2dwbG90KGRhdGE9ZGVfcmVzdWx0cywgbWFwcGluZz1hZXMoeD1sb2dGQywgeT1sb2cxMChQLlZhbHVlKSkpICsKICAgIGdlb21fcG9pbnQoKQpgYGAKCkxvb2tzIGJldHRlciBidXQgdGhpcyBzdGlsbCBoYXMgdGhlIG1vc3Qgc2lnbmlmaWNhbnQgZ2VuZXMgYXQgdGhlIGJvdHRvbSBhbmQgdGhlIHVzdWFsIHdheSBpcyB0byBwbG90IHRoZSBtb3N0IHNpZ25pZmljYW50IGF0IHRoZSB0b3Agc28gd2UgdXNlIHRoZSBuZWdhdGl2ZSBsb2cxMCAoLWxvZzEwKS4gTGV0J3MgdHJ5IHRoYXQuCgpgYGB7cn0KZ2dwbG90KGRhdGE9ZGVfcmVzdWx0cywgbWFwcGluZz1hZXMoeD1sb2dGQywgeT0tbG9nMTAoUC5WYWx1ZSkpKSArCiAgICBnZW9tX3BvaW50KCkKYGBgCgpXZSBjb3VsZCBzaGFuZ2UgdGhlIHNpemUgb2YgdGhlIHBvaW50cyBpZiB3ZSB3YW50ZWQsIGJ5IGFkZGluZyBgc2l6ZT1gLiBXZSBjYW4gYWRkIHRoYXQgaW50byB0aGUgZ2VvbV9wb2ludCgpLiAKCmBgYHtyfQpnZ3Bsb3QoZGF0YT1kZV9yZXN1bHRzLCBtYXBwaW5nPWFlcyh4PWxvZ0ZDLCB5PS1sb2cxMChQLlZhbHVlKSkpICsKICAgIGdlb21fcG9pbnQoc2l6ZT03KQpgYGAKCklmIHdlIHdhbnQgYWxsIHRoZSBwb2ludHMgdG8gYmUgdGhlIHNhbWUgc2l6ZSB3ZSBkb24ndCBwdXQgc2l6ZT0gaW5zaWRlIGBhZXMoKWAuIEhvd2V2ZXIgaWYgd2Ugd2FudGVkIHRvIG1hcCB0aGUgc2l6ZSBvZiB0aGUgcG9pbnRzIHRvIGEgY29sdW1uIGluIG91ciBkYXRhc2V0IHRoZW4gd2Ugd291bGQgcHV0IGl0IGluc2lkZSBgYWVzKClgIGFzIHNob3duIGJlbG93LiAKCmBgYHtyfQpnZ3Bsb3QoZGF0YT1kZV9yZXN1bHRzLCBtYXBwaW5nPWFlcyh4PWxvZ0ZDLCB5PS1sb2cxMChQLlZhbHVlKSkpICsKICAgIGdlb21fcG9pbnQobWFwcGluZz1hZXMoc2l6ZT1sb2dGQykpCmBgYAoKV2UgY291bGQgY2hhbmdlIHRoZSBzaGFwZSBvZiB0aGUgcG9pbnRzIGB3aXRoIHNoYXBlPWAuIEZvciBhIHRyaWFuZ2xlIHNoYXBlIHdlIGFkZCBgc2hhcGU9MmAuIFNlZSBbaGVyZV0oaHR0cDovL3d3dy5zdGhkYS5jb20vZW5nbGlzaC93aWtpL2dncGxvdDItcG9pbnQtc2hhcGVzKSBmb3IgdGhlIGNvZGVzIGZvciB0aGUgZGlmZmVyZW50IHBvc3NpYmxlIHNoYXBlcy4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1kZV9yZXN1bHRzLCBtYXBwaW5nPWFlcyh4PWxvZ0ZDLCB5PS1sb2cxMChQLlZhbHVlKSkpICsKICAgIGdlb21fcG9pbnQoc2hhcGU9MikKYGBgCgpXZSBjb3VsZCBjaGFuZ2UgdGhlIGNvbG91ciBvZiBhbGwgdGhlIHBvaW50cyB3aXRoIGBjb2xvdXI9YC4KYGBge3J9CmdncGxvdChkYXRhPWRlX3Jlc3VsdHMsIG1hcHBpbmc9YWVzKGxvZ0ZDLCAtbG9nMTAoUC5WYWx1ZSkpKSArCiAgICBnZW9tX3BvaW50KGNvbG91cj0icmVkIikKYGBgCgpXZSBjb3VsZCBjaGFuZ2UgdGhlIHNpemUgYW5kIHRoZSBzaGFwZSBhbmQgY29sb3VyLgpgYGB7cn0KZ2dwbG90KGRhdGE9ZGVfcmVzdWx0cywgbWFwcGluZz1hZXMobG9nRkMsIC1sb2cxMChQLlZhbHVlKSkpICsKICAgIGdlb21fcG9pbnQoc2l6ZT03LCBzaGFwZT0yLCBjb2xvdXI9InJlZCIpCmBgYAoKQnV0IG1heWJlIHRoYXQgZG9lc24ndCBsb29rIHNvIGdvb2QuCgojIyBIaWdobGlnaHRpbmcgc2lnbmlmaWNhbnQgZ2VuZXMKCiMjIyBVc2luZyBhIFAgdmFsdWUgdGhyZXNob2xkCgpMZXQncyBjb2xvdXIgZ2VuZXMgdGhhdCBhcmUgc2lnbmlmaWNhbnQuIFdlIHdpbGwgY2FsbCBnZW5lcyBzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBpZiB0aGV5IGhhdmUgYW4gYWRqdXN0ZWQgUCBWYWx1ZSBiZWxvdyAwLjA1LiBBbiBhZGp1c3RlZCBQIHZhbHVlIG1lYW5zIHRoZSBQIHZhbHVlIGhhcyBiZWVuIGFkanVzdGVkIGZvciBtdWx0aXBsZSB0ZXN0aW5nLCBhcyB3ZSBhcmUgdGVzdGluZyBtYW55IHRob3VzYW5kcyBvZiBnZW5lcy4gVGhpcyBpcyB3aGF0IHdlIGZpbHRlciBvbiBpbiBSTkEtU2VxLgoKUmVtZW1iZXIgd2Ugc2F3IGluIHRoZSBwcmV2aW91cyB0dXRvcmlhbCB0aGF0IHdlIGNhbiB1c2UgYSBjb2x1bW4gdG8gY29sb3VyIGZlYXR1cmVzIG9mIG91ciBkYXRhIHdpdGggYGZpbGw9YCBvciBgY29sPWAuIEluIHRoaXMgY2FzZSB3ZSBhcmUgY29sb3VyaW5nIHBvaW50cyBzbyB3ZSB1c2UgYGNvbD1gLiAKCldlIHdpbGwgYWRkIGEgY29sdW1uIHRoYXQgc2F5cyB3aGV0aGVyIGdlbmVzIGFyZSBzaWduaWZpY2FudCBvciBub3QuIFRvIGFkZCBjb2x1bW5zIHdlIHVzZSBkcGx5cidzIGBtdXRhdGUoKWAuCgpMZXQncyBoYXZlIGEgbG9vayBhdCBgbXV0YXRlYCBmaXJzdCB0byBzZWUgaG93IGl0IHdvcmtzLiBXZSBnaXZlIG11dGF0ZSBvdXIgZGF0YSAoYGRlX3Jlc3VsdHNgKSwgdGhlbiBhIG5hbWUgZm9yIHRoZSBjb2x1bW4gd2Ugd2FudCB0byBjcmVhdGUuIAoKTGV0J3MgbWFrZSBhIGNvbHVtbiBjYWxsZWQgc2lnbmlmIChmb3Igc2lnbmlmaWNhbmNlKS4gVGhlbiB3ZSBwdXQgd2hhdCB3ZSB3YW50IHRvIGhhdmUgaW4gdGhlIGNvbHVtbiBpbiBicmFja2V0cyBhZnRlciB0aGUgY29sdW1uIG5hbWUuIEZvciBleGFtcGxlLCBpZiB3ZSB3YW50ZWQgdG8gbGFiZWwgZXZlcnkgZ2VuZSBhcyBzaWduaWZpY2FudCB3ZSBjb3VsZCB3cml0ZSBiZWxvdy4gV2UnbGwgdHJ5IHJ1bm5pbmcgaXQgYW5kIHN0b3JlIGl0IGluIGFuIG9iamVjdCBjYWxsZWQgYHRlc3RpbmdgIGFzIHdlJ3JlIGp1c3Qgc2VlaW5nIGhvdyBpdCB3b3Jrcy4KCmBgYHtyfQp0ZXN0aW5nIDwtIG11dGF0ZShkZV9yZXN1bHRzLCBzaWduaWY9KCJTaWduaWYiKSkKYGBgCgpXZSBjYW4gdXNlIFZpZXcoKSB0byBoYXZlIGEgbG9vayBhdCBkYXRhc2V0LiBUaGVyZSBzaG91bGQgYmUgYSBuZXcgY29sdW1uIGNhbGxlZCBzaWduaWYgYXQgdGhlIGVuZCB3aXRoICJTaWduaWYiIGluIGV2ZXJ5IHJvdy4gbXV0YXRlIGFsd2F5cyBhZGRzIHRoZSBjb2x1bW4gdG8gdGhlICplbmQqIG9mIHRoZSB0YWJsZS4KCmBgYHtyIGV2YWw9RkFMU0V9ClZpZXcodGVzdGluZykKYGBgCgojIyMjIEV4ZXJjaXNlCgoqIEFkZCBhbm90aGVyIGNvbHVtbiBjYWxsZWQgbGFiZWxzIHRvIHRoZSBgdGVzdGluZ2Agb2JqZWN0IHRoYXQgY29udGFpbnMgdGhlIHZhbHVlICJMYWJlbHMiIGluIGV2ZXJ5IHJvdy4KCkJ1dCBvYnZpb3VzbHkgbm90IGFsbCBnZW5lcyBhcmUgc2lnbmlmaWNhbnQuIFdlIHdhbnQgdG8gb25seSBjYWxsIGdlbmVzIHNpZ25pZmljYW50IGlmIHRoZXkgaGF2ZSBhIFAgdmFsdWUgYmVsb3cgMC4wNS4gV2UgY2FuIHVzZSBhbiBSIGZ1bmN0aW9uIGNhbGxlZCBgaWZlbHNlKClgIHRvIHNheSBpZiBvdXIgYWRqLlAuVmFsIGlzIGJlbG93IDAuMDUgYWRkICJTaWduaWYiIHRvIHRoZSBzaWcgY29sdW1uLCBvdGhlcndpc2UgYWRkICJOb3Qgc2lnbmlmIi4KCldlIGNhbiB0YWtlIGEgbG9vayBhdCB0aGUgaWZlbHNlIGhlbHAgdG8gc2VlIGhvdyBpdCB3b3Jrcy4gVGhlIGhlbHAgdGVsbHMgdXMgdGhlIFVzYWdlIGlzIGBpZmVsc2UodGVzdCwgeWVzLCBubylgLiBPdXIgInRlc3QiIGlzIHdoZXRoZXIgb3VyIGdlbmUgaGFzIGFuIGFkai5QLlZhbCA8IDAuMDUsIGlmIHRoZSBhbnN3ZXIgaXMgeWVzLCB3ZSdsbCBhZGQgIlNpZ25pZiIgaW50byB0aGUgY29sdW1uLCBpZiB0aGUgYW5zd2VyIGlzIG5vLCB0aGVuIHdlJ2xsIGFkZCAiTm90IHNpZ25pZiIuIFdlJ2xsIHNhdmUgdGhlIG91dHB1dCBhcyBgZGVfcmVzdWx0c2AgKHRoaXMgb3ZlcndyaXRlcyB0aGUgb3JpZ2luYWwgYGRlX3Jlc3VsdHNgIG9iamVjdCkuCgpgYGB7cn0KZGVfcmVzdWx0cyA8LSBtdXRhdGUoZGVfcmVzdWx0cywgc2lnbmlmPWlmZWxzZShhZGouUC5WYWwgPCAwLjA1LCAiU2lnbmlmIiwgIk5vdCBzaWduaWYiKSkKYGBgCgpMZXQncyB0YWtlIGEgbG9vayBhdCBgZGVfcmVzdWx0c2AgYWdhaW4gbm93LiBXZSBzaG91bGQgc2VlIHdlIGhhdmUgYSBuZXcgY29sdW1uIGF0IHRoZSBlbmQgY2FsbGVkICJzaWduaWYiLgoKYGBge3IgZXZhbD1GQUxTRX0KVmlldyhkZV9yZXN1bHRzKQpgYGAKCkluIFZpZXcoKSB3ZSBjYW4gc29ydCBvbiB0aGUgc2lnbmlmIGNvbHVtbiB0byBjaGVjayB3ZSBzZWUgYm90aCBTaWduaWYgYW5kIE5vdCBzaWduaWYgZW50cmllcy4KCk5vdyB0aGF0IHdlIGhhdmUgYSBjb2x1bW4gdGhhdCBmbGFncyB3aGV0aGVyIHRoZSBnZW5lcyBhcmUgc2lnbmlmaWNhbnQgb3Igbm90IHdlIGNhbiB1c2UgdGhhdCB0byBjb2xvdXIgdGhlIHNpZ25pZmljYW50IGdlbmVzIGJ5IGFkZGluZyBgY29sPXNpZ25pZmAgdG8gb3VyIGdncGxvdC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1kZV9yZXN1bHRzLCBtYXBwaW5nPWFlcyh4PWxvZ0ZDLCB5PS1sb2cxMChQLlZhbHVlKSwgY29sPXNpZ25pZikpICsKICAgIGdlb21fcG9pbnQoKQpgYGAKCldlIG1pZ2h0IHdhbnQgdG8gY2hhbmdlIHRoZSBjb2xvdXJzLiAKClRoZXJlIGFyZSBidWlsdC1pbiBjb2xvdXIgcGFsZXR0ZXMsIHRoYXQgY2FuIGJlIGhhbmR5IHRvIHVzZSwgd2hlcmUgdGhlIHNldHMgb2YgY29sb3VycyBhcmUgcHJlZGVmaW5lZC4gYHNjYWxlX2NvbG91cl9icmV3ZXIoKWAgaXMgYSBwb3B1bGFyIG9uZSAodGhlcmUgaXMgYWxzbyBgc2NhbGVfZmlsbF9icmV3ZXIoKWApLiBZb3UgY2FuIHRha2UgYSBsb29rIGF0IHRoZSBoZWxwIGZvciBgc2NhbGVfY29sb3VyX2JyZXdlcigpYCB0byBzZWUgd2hhdCBwYWxsZXR0ZXMgYXJlIGF2YWlsYWJsZS4gVGhlcmUgaXMgYWxzbyBhbiBSIGNvbG91cnMgY2hlYXRzaGVldCB0aGF0IHNob3dzIHRoZSBjb2xvdXJzIG9mIHRoZSBwYWxldHRlcyBbaGVyZV0oaHR0cHM6Ly93d3cubmNlYXMudWNzYi5lZHUvfmZyYXppZXIvUlNwYXRpYWxHdWlkZXMvY29sb3JQYWxldHRlQ2hlYXRzaGVldC5wZGYpLgoKVGhlcmUncyBvbmUgY2FsbGVkICJEYXJrMiIsIGxldCdzIGhhdmUgYSBsb29rIGF0IHRoYXQuCgpgYGB7cn0KZ2dwbG90KGRhdGE9ZGVfcmVzdWx0cywgbWFwcGluZz1hZXMoeD1sb2dGQywgeT0tbG9nMTAoUC5WYWx1ZSksIGNvbD1zaWduaWYpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKQpgYGAKClRoZXJlJ3Mgb25lIGNhbGxlZCAiU2V0MSIsIGxldCdzIGhhdmUgYSBsb29rIGF0IHRoYXQuCgpgYGB7cn0KZ2dwbG90KGRhdGE9ZGVfcmVzdWx0cywgbWFwcGluZz1hZXMoeD1sb2dGQywgeT0tbG9nMTAoUC5WYWx1ZSksIGNvbD1zaWduaWYpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpCmBgYAoKT3Igd2UgY291bGQgY2hvb3NlIHRvIHNldCB0aGUgY29sb3VycyBtYW51YWxseS4gV2UgY291bGQgZGVjaWRlIHRvIGNvbG91ciB0aGUgbm9uLXNpZ25pZmljYW50IGdlbmVzIGdyZXkgYW5kIHRoZSBzaWduaWZpY2FudCBnZW5lcyByZWQuIFdlIGFyZSB1c2luZyBgY29sPWAgc28gdG8gc3BlY2lmeSBvdXIgb3duIGNvbG91cnMgd2UgYWRkIGArIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPSkpYCAoYXMgd2Ugd2lsbCBzZWUsIHdlIGNhbiBrZWVwIGFkZGluZyBsYXllcnMgdG8gb3VyIHBsb3Qgd2l0aCBgK2ApLiBJZiB3ZSB3ZXJlIHVzaW5nIGBmaWxsPWAgd2Ugd291bGQgdXNlIGArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0pKWAKClRvIHVzZSB0d28gY29sb3VycyB3ZSBhZGQgYCsgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9YygicmVkIiwgImdyZXkiKSlgLiBOb3RlIHRoYXQgaGVyZSB3ZSBzZWUgdGhlIGZ1bmN0aW9uIGBjKClgIGZvciB0aGUgZmlyc3QgdGltZS4gV2UgdXNlIGZ1bmN0aW9uIGV4dHJlbWVseSBvZnRlbiBpbiBSIHdoZW4gd2UgaGF2ZSBtdWx0aXBsZSBpdGVtcyB0aGF0IHdlIGFyZSAqY29tYmluaW5nKi4gSGVyZSB3ZSBoYXZlIHR3byBjb2xvdXJzIHdlIHdhbnQgdG8gdXNlLCBzbyB3ZSBuZWVkIHRvIHVzZSBgYygpYCB0byBjb21iaW5lIHRoZW0gdG8gZ2l2ZSB0byBgdmFsdWVzPWAuIFRodXMgd2UgbmVlZCB0byBhZGQgYHZhbHVlcz1jKCJyZWQiLCAiZ3JleSIpYC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1kZV9yZXN1bHRzLCBtYXBwaW5nPWFlcyh4PWxvZ0ZDLCB5PS1sb2cxMChQLlZhbHVlKSwgY29sPXNpZ25pZikpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPWMoInJlZCIsICJncmV5IikpCmBgYAoKSG1tIHRoaXMgaXMgdGhlIHdyb25nIHdheSBhcm91bmQsIG91ciBzaWduaWZpY2FudCBwb2ludHMgYXJlIGdyZXkuIFdlIGNvdWxkIGNoYW5nZSB0aGUgb3JkZXIgb2YgdGhlIGNvbG91cnMgaW4gYHZhbHVlcz1gLiAKCmBgYHtyfQpnZ3Bsb3QoZGF0YT1kZV9yZXN1bHRzLCBtYXBwaW5nPWFlcyh4PWxvZ0ZDLCB5PS1sb2cxMChQLlZhbHVlKSwgY29sPXNpZ25pZikpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPWMoImdyZXkiLCAicmVkIikpCmBgYAoKCk9yIHdlIGNvdWxkIHNwZWNpZnkgd2hpY2ggdmFsdWUgaW4gb3VyIHNpZ25pZiBjb2x1bW4gd2Ugd2FudCB0byBtYXAgdG8gZWFjaCBjb2xvdXIuCgpgYGB7cn0KZ2dwbG90KGRhdGE9ZGVfcmVzdWx0cywgbWFwcGluZz1hZXMoeD1sb2dGQywgeT0tbG9nMTAoUC5WYWx1ZSksIGNvbD1zaWduaWYpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1jKCJTaWduaWYiPSJyZWQiLCAiTm90IHNpZ25pZiI9ImdyZXkiKSkKYGBgCgojIyMjIEV4ZXJjaXNlCgoqIENvbG91ciB0aGUgdm9sY2Fub3Bsb3QgdXNpbmcgdHdvIGRpZmZlcmVudCBjb2xvdXJzIG9mIHlvdXIgY2hvaWNlLiBZb3UgY2FuIHVzZSBvbmUgb2YgdGhlIHBhbGV0dGVzIG9yIHR5cGUgYGNvbG91cnMoKWAgdG8gc2VlIHdoYXQgY29sb3VycyBhcmUgYXZhaWxhYmxlLiBDaG9vc2UgdHdvIG5pY2UgY29sb3VycyBvciB0d28gdWdseSBvbmVzLiBUaGUgY29sb3VycyBjYW4gYmUgc2VlbiBpbiB0aGUgUiBjb2xvdXJzIGNoZWF0c2hlZXQgW2hlcmVdKGh0dHBzOi8vd3d3Lm5jZWFzLnVjc2IuZWR1L35mcmF6aWVyL1JTcGF0aWFsR3VpZGVzL2NvbG9yUGFsZXR0ZUNoZWF0c2hlZXQucGRmKS4KCiMjIyBVc2luZyBQIHZhbHVlIGFuZCBsb2dGQyB0aHJlc2hvbGRzCgpXZSBjb3VsZCBjb2xvdXIgb3VyIHNpZ25pZmljYW50IGdlbmVzIHRoYXQgYXJlIGRvd25yZWd1bGF0ZWQgYW5kIHRoZSBnZW5lcyB0aGF0IGFyZSB1cHJlZ3VsYXRlZCB1c2luZyBzZXBhcmF0ZSBjb2xvdXJzLiBUaGVzZSBhcmUgdGhlIGdlbmVzIHVwcmVndWxhdGVkIGluIHRoZSBsdW1pbmFsIGNlbGxzIGZyb20gdGhlIHByZWduYW50IG1pY2UgY29tcGFyZWQgdG8gdGhlIGx1bWluYWwgY2VsbHMgZnJvbSB0aGUgbGFjdGF0aW5nIG1pY2UuIFRvIGRvIHRoaXMgd2UgY2FuIGNoYW5nZSBvdXIgc2lnbmlmIGNvbHVtbiBhbmQgaGF2ZSB0aHJlZSB2YWx1ZXMgaW5zdGVhZCBvZiB0d28uIFdlIGNvdWxkIGhhdmUgIlVwIiIsICJEb3duIiBhbmQgIk5vdCBzaWduaWYiLgoKTGV0J3MgY29sb3VyIHNpZ25pZmljYW50IGdlbmVzID4gbG9nRkMgb2YgMSwgcmVkIGFuZCA8IGxvZ0ZDIC0xLCBibHVlLiBUbyBkbyB0aGlzIHdlIGNoYW5nZSBvdXIgc2lnbmlmIGNvbHVtbiBieSBhZGRpbmcgYW5vdGhlciBgaWZlbHNlKClgLiBXZSBhcmUgYXNraW5nOgppZiBnZW5lcyBhcmUgc2lnbmlmaWNhbnRseSB1cCwgYWRkIHRoZSB2YWx1ZSAiVXAiCm90aGVyd2lzZSBpZiB0aGUgZ2VuZXMgYXJlIHNpZ25pZmljYW50bHkgZG93biwgYWRkIHRoZSB2YWx1ZSAiRG93biIKb3RoZXJ3aXNlIGFkZCB0aGUgdmFsdWUgIk5vdCBzaWduaWYiLgoKVG8gYXNrIGlmIGFyZSBnZW5lcyBoYXZlIGFuIGFkai5QLlZhbHVlIDwgMC4wNSBhbmQgYWxzbyBoYXZlIGEgbG9nRkMgPiAxLCB3ZSB1c2UgdGhlIHN5bnRheCBgYWRqLlAuVmFsIDwgMC4wNSAmIGxvZ0ZDID4gMWAsIG5vdGUgdGhlIGAmYC4gSWYgdGhleSBhcmUgPCAwLjA1IGFuZCBoYXZlIGEgbG9nRkMgPCAtMSB3ZSB1c2UgYGFkai5QLlZhbCA8IDAuMDUgJiBsb2dGQyA+IDFgLiBXZSB3cml0ZSB0aGlzIGNyaXRlcmlhIGFzIGJlbG93IGFuZCBzYXZlIGFzIGBkZV9yZXN1bHRzYCBhZ2Fpbi4KCmBgYHtyfQpkZV9yZXN1bHRzIDwtIG11dGF0ZShkZV9yZXN1bHRzLCBzaWduaWY9aWZlbHNlKChhZGouUC5WYWwgPCAwLjA1ICYgbG9nRkMgPiAxKSwgIlVwIiwgaWZlbHNlKChhZGouUC5WYWwgPCAwLjA1ICYgbG9nRkMgPCAtMSksICJEb3duIiwgIk5vdCBzaWduaWYiKSkpCmBgYAoKTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIG91dHB1dC4KCmBgYHtyIGV2YWw9RkFMU0V9ClZpZXcoZGVfcmVzdWx0cykKYGBgCgpOb3cgdGhhdCB3ZSBoYXZlIG91ciBzaWduaWYgY29sdW1uIHdpdGggdGhyZWUgdmFsdWVzLCAiVXAiLCAiRG93biIsICJOb3Qgc2lnbmlmIiwgd2UgY2FuIGNvbG91ciB0aGUgdm9sY2FubyBwbG90IHBvaW50cywgdXAgLSByZWQsIG5vdCBzaWduaWYgLSBncmV5LCBhbmQgZG93biAtIGJsdWUuCgpgYGB7cn0KZ2dwbG90KGRhdGE9ZGVfcmVzdWx0cywgbWFwcGluZz1hZXMoeD1sb2dGQywgeT0tbG9nMTAoUC5WYWx1ZSksIGNvbD1zaWduaWYpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1jKCJVcCI9InJlZCIsICJOb3Qgc2lnbmlmIj0iZ3JleSIsICJEb3duIj0iYmx1ZSIpKQpgYGAKCiMjIExhYmVsbGluZyBnZW5lcwoKCiMjIyBMYWJlbGxpbmcgZ2VuZXMgb2YgaW50ZXJlc3QKCldlIGNhbiBsYWJlbCBvbmUgb3IgbW9yZSBnZW5lcyBvZiBpbnRlcmVzdCBpbiBhIHZvbGNhbm8gcGxvdC4gVGhpcyBlbmFibGVzIHVzIHRvIHZpc3VhbGl6ZSB3aGVyZSB0aGVzZSBnZW5lcyBhcmUgaW4gdGVybXMgb2Ygc2lnbmlmaWNhbmNlIGFuZCBpbiBjb21wYXJpc29uIHRvIHRoZSBvdGhlciBnZW5lcy4gSW4gdGhlIG9yaWdpbmFsIHBhcGVyIHVzaW5nIHRoaXMgZGF0YXNldCwgdGhlcmUgaXMgYSBoZWF0bWFwIG9mIDMxIGdlbmVzIGluIEZpZ3VyZSA2Yi4gVGhlc2UgZ2VuZXMgYXJlIGEgc2V0IG9mIDMwIGN5dG9raW5lcy9ncm93dGggZmFjdG9yIGlkZW50aWZpZWQgYXMgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLCBhbmQgdGhlIGF1dGhvcnPigJkgbWFpbiBnZW5lIG9mIGludGVyZXN0LCBNY2wxLiBUaGVzZSBnZW5lcyBhcmUgcHJvdmlkZWQgaW4gdGhlIHZvbGNhbm8gZ2VuZXMgZmlsZSBhbmQgc2hvd24gYmVsb3cuIFdlIHdpbGwgbGFiZWwgdGhlc2UgZ2VuZXMgaW4gdGhlIHZvbGNhbm8gcGxvdC4gV2UnbGwgcmVhZCBpbiB0aGUgZ2VuZXMgYW5kIHN0b3JlIGl0IGluIGFuIG9iamVjdCBjYWxsZWQgYGdvaWAuCgpgYGB7cn0KZ29pIDwtIHJlYWRfdHN2KCJkYXRhL3ZvbGNhbm9fZ2VuZXMiKQpgYGAKCkxldCdzIHRha2UgYSBsb29rIGF0IHdoYXQncyBpbiBgZ29pYAoKYGBge3J9CmdvaQpgYGAKCmBnb2lgIGNvbnRhaW5zIGdlbmUgc3ltYm9scyBzdG9yZWQgaW4gYSBjb2x1bW4uIFdlIG5lZWQgdG8gZ2V0IHRoZXNlIHN5bWJvbHMgb3V0IG9mIHRoZSBjb2x1bW4gZm9ybWF0LiBXZSBjYW4gZG8gdGhhdCB3aXRoIGRwbHlyJ3MgYHB1bGwoKWAuIFdlIGdpdmUgYHB1bGwoKWAgdGhlIGBnb2lgIG9iamVjdCBhbmQgdGhlIG5hbWUgb2YgdGhlIGNvbHVtbiB3ZSB3YW50IHRvIHB1bGwgdGhlIHZhbHVlcyBmcm9tLiBXZSdsbCBzdG9yZSB0aGUgc3ltYm9scyBpbiBhbiBvYmplY3QgY2FsbGVkIGBnb2lfc3ltc2AuCgpgYGB7cn0KZ29pX3N5bXMgPC0gcHVsbChnb2ksIEdlbmVJRCkKYGBgCgpUYWtlIGEgbG9vay4KCmBgYHtyfQpnb2lfc3ltcwpgYGAKCldlJ2xsIGFkZCBhbm90aGVyIGNvbHVtbnMgZm9yIHRoZXNlIGxhYmVscywgbGV0J3MgY2FsbCBpdCBteWdlbmVzLiBXZSBkbyB0aGlzIHNpbWlsYXIgdG8gd2hhdCB3ZSBkaWQgZm9yIHRoZSB0b3AgMTAgZ2VuZXMuCgpgYGB7cn0KZGVfcmVzdWx0cyA8LSBtdXRhdGUoZGVfcmVzdWx0cywgbXlnZW5lcz1pZmVsc2UoU1lNQk9MICVpbiUgZ29pX3N5bXMsIFNZTUJPTCwgIiIpKQpgYGAKCkxldCdzIG1ha2UgdGhlIHZvbGNhbm8gcGxvdCBhbmQgbGFiZWwgdGhpcyBjdXN0b20gc2V0IG9mIGdlbmVzLiBXZSBhZGQgYCsgZ2VvbV90ZXh0KClgIGFuZCBhZGQgdGhlIGxhYmVscyBpbnRvIHRoYXQuIE5vdGUgd2UgYXJlIHVzaW5nIGArYCBhZ2FpbiBhbmQgYWRkaW5nIGFub3RoZXIgbGF5ZXIgdG8gb3VyIHBsb3QuIFRoaXMgdGltZSB3ZSBhcmUgYWRkaW5nIGEgbGF5ZXIgb2YgdGV4dCAobGFiZWxzKS4gV2UgcHV0IGBsYWJlbHM9bXlnZW5lc2AgaW5zaWRlIGBhZXMoKWAgaW4gYGdlb21fdGV4dCgpYCBhcyB3ZSBhcmUgbWFwcGluZyB0byBhIGNvbHVtbiBpbiBvdXIgZGF0YS4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1kZV9yZXN1bHRzLCBtYXBwaW5nPWFlcyh4PWxvZ0ZDLCB5PS1sb2cxMChQLlZhbHVlKSwgY29sPXNpZ25pZikpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPWMoIlVwIj0icmVkIiwgIk5vdCBzaWduaWYiPSJncmV5IiwgIkRvd24iPSJibHVlIikpICsKICBnZW9tX3RleHQobWFwcGluZz1hZXMobGFiZWw9bXlnZW5lcykpCmBgYAoKVGhlIGxlZ2VuZCBub3cgaGFzIGEgbGVnZW5kIGZvciB0aGUgbGFiZWxzICh0aGUgbGV0dGVyICJhIikgb3ZlcmxhcHBpbmcgdGhlIG9yaWdpbmFsIGxlZ2VuZCBidXQgd2UgY2FuIHJlbW92ZSB0aGF0IGJ5IGFkZGluZyBgc2hvdy5sZWdlbmQ9RkFMU0VgLgoKYGBge3J9CmdncGxvdChkYXRhPWRlX3Jlc3VsdHMsIG1hcHBpbmc9YWVzKHg9bG9nRkMsIHk9LWxvZzEwKFAuVmFsdWUpLCBjb2w9c2lnbmlmKSkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9YygiVXAiPSJyZWQiLCAiTm90IHNpZ25pZiI9ImdyZXkiLCAiRG93biI9ImJsdWUiKSkgKwogIGdlb21fdGV4dChtYXBwaW5nPWFlcyhsYWJlbD1teWdlbmVzKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkKYGBgCgpUaGUgbGFiZWxzIGFsc28gZG9uJ3QgbG9vayBncmVhdCBhcyB0aGUgbGFiZWxzIGFyZSBvdmVybGFwcGluZyBidXQgd2UgY2FuIGZpeCB0aGF0LiBXZSBjYW4gcmVwbGFjZSBgKyBnZW9tX3RleHQoKWAgd2l0aCBgKyBnZW9tX3RleHRfcmVwZWwoKWAgZnJvbSB0aGUgcGFja2FnZSAqKmdncmVwZWwqKi4KCipOb3RlIHRoYXQgcnVubmluZyBgZ2VvbV90ZXh0X3JlcGVsKClgIG9uIHRoZSBzZXJ2ZXIgY2FuIGJlIHZlcnkgc2xvdyAodGFrZSBtaW51dGVzKSwgdGhpbmsgaXQgbWF5IGJlIHRoZSBzYW1lIGlzc3VlIHJlcG9ydGVkIGluIFN0YWNrIE92ZXJmbG93IFtoZXJlXShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy81NTk0MjQ5OC9wbG90LXdpdGgtZ2dyZXBlbC1sYWJlbHMtcmVuZGVycy12ZXJ5LXNsb3dseSksIGJ1dCBpdCBzaG91bGQgcnVuIGEgbG90IHF1aWNrZXIgb24geW91ciBsYXB0b3AvZGVza3RvcC4qCgpgYGB7cn0KZ2dwbG90KGRhdGE9ZGVfcmVzdWx0cywgbWFwcGluZz1hZXMoeD1sb2dGQywgeT0tbG9nMTAoUC5WYWx1ZSksIGNvbD1zaWduaWYpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1jKCJVcCI9InJlZCIsICJOb3Qgc2lnbmlmIj0iZ3JleSIsICJEb3duIj0iYmx1ZSIpKSArCiAgZ2VvbV90ZXh0X3JlcGVsKG1hcHBpbmc9YWVzKGxhYmVsPW15Z2VuZXMpKQpgYGAKCklmIHdlIHdhbnQsIHdlIGNhbiBjb2xvdXIganVzdCB0aGUgcG9pbnRzIHJlZCBhbmQgYmx1ZSwgYW5kIGxlYXZlIHRoZSBsYWJlbHMgYmxhY2suIFdlIGRvIHRoaXMgYnkgYWRkaW5nIHRoZSBgY29sPXNpZ25pZmAgaW50byB0aGUgYGdlb21fcG9pbnQoKWAgYGFlcygpYCBpbnN0ZWFkIG9mIGluIHRoZSBgZ2dwbG90KClgIGBhZXMoKWAuCgpgYGB7cn0KZ2dwbG90KGRhdGE9ZGVfcmVzdWx0cywgbWFwcGluZz1hZXMobG9nRkMsIC1sb2cxMChQLlZhbHVlKSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2w9c2lnbmlmKSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPWMoIlVwIj0icmVkIiwgIk5vdCBzaWduaWYiPSJncmV5IiwgIkRvd24iPSJibHVlIikpICsKICBnZW9tX3RleHRfcmVwZWwobWFwcGluZz1hZXMobGFiZWw9bXlnZW5lcyksIHNob3cubGVnZW5kID0gRkFMU0UpCmBgYAoKIyMjIExhYmVsbGluZyB0b3Agc2lnbmlmaWNhbnQgZ2VuZXMKCldlIGNvdWxkIGFsc28gbGFiZWwgdGhlIHRvcCBzaWduaWZpY2FudCBnZW5lcyB3aXRoIHRoZSBnZW5lIG5hbWVzIHNvIHdlIGNhbiBzZWUgd2hhdCB0aGV5IGFyZS4gV2UgaGF2ZSBnZW5lIHN5bWJvbHMgaW4gb3VyIGBkZV9yZXN1bHRzYCBzbyB3ZSBjb3VsZCB1c2UgdGhvc2UuIAoKTGV0J3MgbGFiZWwgdGhlIHRvcCAxMCBtb3N0IHNpZ25pZmljYW50IGdlbmVzLiBUbyBkbyB0aGlzIHdlIGZpcnN0IGlkZW50aWZ5IHRoZSB0b3AgMTAgZ2VuZXMgYnkgUC5WYWx1ZS4gVGhlIGZpbGUgaXMgYWxyZWFkeSBzb3J0ZWQgYnkgUCB2YWx1ZSBhbmQgcmVtZW1iZXIgaGVhZCgpIHNob3dzIHVzIHRoZSB0b3AgNiBsaW5lcyBpbiBhIGZpbGUuIElmIHdlIGxvb2sgYXQgdGhlIGhlbHAgZm9yIGA/aGVhZGAgd2Ugc2VlIHRoYXQgdGhlcmUgaXMgYSBkZWZhdWx0IGFyZ3VtZW50IGBuID0gNkxgLCB0aGlzIGlzIHdoeSBpdCByZXR1cm5zIDYgbGluZXMuIFdlIGNhbiBjaGFuZ2UgdGhpcyB0byAxMEwgdG8gZ2V0IHRoZSB0b3AgMTAgbGluZXMuIFdlIHdpbGwgc3RvcmUgdGhpcyBhdCBhbiBvYmplY3QgY2FsbGVkIGB0b3AxMGAuCgpgYGB7cn0KdG9wMTAgPC0gaGVhZChkZV9yZXN1bHRzLCBuID0gMTBMKQpgYGAKClRha2UgYSBsb29rLgoKYGBge3J9CnRvcDEwCmBgYAoKIyMjIyBFeGVyY2lzZQoKKiBNYWtlIGEgdm9sY2FubyBwbG90IGxhYmVsbGluZyB0aGUgdG9wIDEwIGdlbmVzLiBUaGlzIGlzIHZlcnkgc2ltaWxhciB0byB3aGF0IHdlIGRpZCBmb3IgdGhlIGdlbmVzIG9mIGludGVyZXN0IChgZ29pYCkgZS5nLiB5b3Ugd2lsbCBuZWVkIHRvIGV4dHJhY3QgdGhlIGdlbmUgc3ltYm9scyBmcm9tIHRoZSBgdG9wMTBgIGluZm8gYW5kIHN0b3JlIHRoZW0gaW4gYW4gb2JqZWN0IGNhbGxlZCBgdG9wMTBfc3ltc2AsIHRoZW4gYWRkIGEgY29sdW1uIGNhbGxlZCBlLmcuIHRvcDEwIHRvIHRoZSBkZV9yZXN1bHRzIHRoYXQgc2F5cyB3aGljaCBnZW5lcyB0byBsYWJlbC4KCiMjIE1vZGlmeWluZyB0aGUgcGxvdAoKIyMjIExlZ2VuZAoKV2hhdCBpZiB3ZSB3YW50IHRoZSBjYXRlZ29yaWVzIGluIHRoZSBsZWdlbmQgaW4gYSBkaWZmZXJlbnQgb3JkZXIsIGZvciBleGFtcGxlLCBVcCwgTm90IHNpZ25pZiwgRG93bi4gV2UgY2FuIGFkZGAgYnJlYWtzPWAgd2l0aCB0aGUgb3JkZXIgd2Ugd2FudCBpbnRvIGBzY2FsZV9jb2xvcl9tYW51YWwoKWAuIE5vdGUgdGhhdCBoZXJlIHdlIHVzZSBgYygpYCBhZ2FpbiB0byBwYXNzIG11bHRpcGxlIHZhbHVlcyB0byBicmVha3MuCgpgYGB7cn0KZ2dwbG90KGRhdGE9ZGVfcmVzdWx0cywgbWFwcGluZz1hZXMoeD1sb2dGQywgeT0tbG9nMTAoUC5WYWx1ZSksIGNvbD1zaWduaWYpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1jKCJVcCI9InJlZCIsICJOb3Qgc2lnbmlmIj0iZ3JleSIsICJEb3duIj0iYmx1ZSIpLCBicmVha3M9YygiVXAiLCAiRG93biIsICJOb3Qgc2lnbmlmIikpCmBgYAoKV2UgY2FuIGNoYW5nZSB0aGUgbmFtZSBvZiB0aGUgbGVnZW5kIGJ5IGFkZGluZyBgbmFtZT1gIGludG8gYHNjYWxlX2NvbG91cl9tYW51YWxgLgoKYGBge3J9CmdncGxvdChkYXRhPWRlX3Jlc3VsdHMsIG1hcHBpbmc9YWVzKHg9bG9nRkMsIHk9LWxvZzEwKFAuVmFsdWUpLCBjb2w9c2lnbmlmKSkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbChuYW1lPSJNeSBuZXcgbmFtZSIsIHZhbHVlcz1jKCJVcCI9InJlZCIsICJOb3Qgc2lnbmlmIj0iZ3JleSIsICJEb3duIj0iYmx1ZSIpKQpgYGAKCldlIGNhbiBhbHNvIG1ha2UgdGhlIHBsb3QgbG9vayBuaWNlciwgZm9yIGV4YW1wbGUsIGFkanVzdGluZyB0aGUgeCBheGlzIGxpbWl0cyBhbmQgbGFiZWwsIGFkZGluZyBhIHRpdGxlIGFuZCBjaGFuZ2luZyB0aGUgYmFja2dyb3VuZC4gCgpGaXJzdCBsZXQncyBzdG9yZSBvdXIgY3VycmVudCBwbG90IGluIGFuIG9iamVjdCBjYWxsZWQgYHBgLCB0byBtYWtlIGl0IGVhc2llciB0byBzZWUgd2hhdCB3ZSdyZSBjaGFuZ2luZy4KCmBgYHtyfQpwIDwtIGdncGxvdChkYXRhPWRlX3Jlc3VsdHMsIG1hcHBpbmc9YWVzKHg9bG9nRkMsIHk9LWxvZzEwKFAuVmFsdWUpKSkgKwogIGdlb21fcG9pbnQobWFwcGluZz1hZXMoY29sPXNpZ25pZikpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1jKCJVcCI9InJlZCIsICJOb3Qgc2lnbmlmIj0iZ3JleSIsICJEb3duIj0iYmx1ZSIpKQpgYGAKCk5vdGUgdGhhdCB3aGVuIHdlIHN0b3JlIHRoZSBwbG90IGluIGFuIG9iamVjdCBpdCBkb2Vzbid0IHByaW50IG91dCB0aGUgcGxvdC4gSWYgd2Ugd2FudCB0byBzZWUgdGhlIHBsb3Qgd2UgdHlwZSB0aGUgbmFtZSBvZiB0aGUgb2JqZWN0IGBwYC4KCmBgYHtyfQpwCmBgYAoKIyMjIEF4aXMgbGltaXRzCgpXZSBjYW4gYWRqdXN0IHRoZSB4IGF4aXMgc28gaXQncyB0aGUgc2FtZSBsaW1pdCBvbiB0aGUgcmlnaHQgYW5kIGxlZnQgd2l0aCBgc2NhbGVfeF9jb250aW51b3VzKClgLiBJZiB3ZSBoYWQgY2F0ZWdvcmllcyAoZGlzY3JldGUgdmFsdWVzKSBvbiB0aGUgeCBheGlzIHdlIHdvdWxkIHVzZSBgc2NhbGVfeF9kaXNjcmV0ZSgpYCBpbnN0ZWFkLiBUaGVyZSBhcmUgYWxzbyBlcXVpdmFsZW50cyBmb3IgdGhlIHkgYXhpcyAoYHNjYWxlX3lfY29udGludW91cygpYCBhbmQgYHNjYWxlX3lfZGlzY3JldGUoKWApLgoKYGBge3J9CnAgPC0gcCArIHNjYWxlX3hfY29udGludW91cyhsaW1pdHM9YygtMTAsIDEwKSkKcApgYGAKCiMjIyBBeGlzIGxhYmVscwoKV2UgY2FuIGNoYW5nZSB0aGUgYXhpcyBsYWJlbHMgd2l0aCBgbGFicygpYC4gVG8gY2hhbmdlIHRoZSB4IGF4aXMgbGFiZWwgd2UgdXNlIGBsYWJzKHg9Ik5ldyBuYW1lIilgLiBUbyBjaGFuZ2UgdGhlIHkgYXhpcyBsYWJlbCB3ZSB1c2UgYGxhYnMoeT0iTmV3IG5hbWUiKWAuCgpgYGB7cn0KcCA8LSBwICsgbGFicyh4PSJMb2cyIGZvbGQgY2hhbmdlIikKcApgYGAKCgojIyMgVGl0bGUKCldlIGNhbiBhZGQgYSB0aXRsZSB3aXRoIGBsYWJzKClgLgoKYGBge3J9CnAgPC0gcCArIGxhYnModGl0bGU9Ikx1bWluYWwgcHJlZ25hbnQgdnMgbGFjdGF0aW5nIikKcApgYGAKCgojIyMgVGhlbWVzCgpXZSBjYW4gcmVtb3ZlIHRoZSBncmV5IGJhY2tncm91bmQgYW5kIGdyaWQgbGluZXMuIFRvIGRvIHRoaXMgd2UgbW9kaWZ5IHRoZSBnZ3Bsb3QgdGhlbWUuIFRoZW1lcyBhcmUgdGhlIG5vbi1kYXRhIHBhcnRzIG9mIHRoZSBwbG90LiAKClRoZXJlIGFyZSBhbHNvIGEgbG90IG9mIGJ1aWx0LWluIHRoZW1lcy4gTGV0J3MgaGF2ZSBhIGxvb2sgYXQgYSBjb3VwbGUgb2YgdGhlIG1vcmUgd2lkZWx5IHVzZWQgdGhlbWVzLiBXZSB3b24ndCBzYXZlIHRoZXNlICh3ZSB3b24ndCB1c2UgYHAgPC1gKSB3ZSdsbCBqdXN0IHByaW50IHRoZW0gdG8gaGF2ZSBhIGxvb2suIFRoZSBkZWZhdWx0IGdncGxvdCB0aGVtZSBpcyBgdGhlbWVfZ3JleSgpLmAKCmBgYHtyfQpwICsgdGhlbWVfYncoKQpgYGAKYGBge3J9CnAgKyB0aGVtZV9taW5pbWFsKCkKYGBgCgpUaGVyZSBhcmUgbWFueSB0aGVtZXMgYXZhaWxhYmxlLCB5b3UgY2FuIHNlZSBzb21lIGluIHRoZSBbUiBncmFwaCBnYWxsZXJ5XShodHRwczovL3d3dy5yLWdyYXBoLWdhbGxlcnkuY29tLzE5Mi1nZ3Bsb3QtdGhlbWVzLykuCgpXZSBjYW4gYWxzbyBtb2RpZnkgcGFydHMgb2YgdGhlIHRoZW1lIGluZGl2aWR1YWxseS4gV2UgY2FuIHJlbW92ZSB0aGUgZ3JleSBiYWNrZ3JvdW5kIGFuZCBncmlkIGxpbmVzIHdpdGggdGhlIGNvZGUgYmVsb3cuCgpgYGB7cn0KcCA8LSBwICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQpwCmBgYAoKV2UgY2FuIGFkZCBheGlzIGxpbmVzLgoKYGBge3J9CnAgPC0gcCArIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShzaXplPTAuMiwgY29sb3VyID0gImJsYWNrIikpCnAKYGBgCgpXZSBjYW4gcmVtb3ZlIHRoZSBsZWdlbmQgY29tcGxldGVseS4KCmBgYHtyfQpwIDwtIHAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCnAKYGBgCgpXZSBjYW4gY2VudHJlIHRoZSB0aXRsZS4gCgpgYGB7cn0KcCA8LSBwICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCnAKYGBgCgojIyMjIEV4ZXJjaXNlCgoqIE1ha2UgYSB2b2xjYW5vIHBsb3QgZm9yIHRoZSBiYXNhbCBjZWxscyB1c2luZyB0aGUgZmlsZSAiaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZC8yNTk2MzgyL2ZpbGVzL2xpbW1hLXZvb21fYmFzYWxwcmVnbmFudC1iYXNhbGxhY3RhdGUiLiBDb2xvdXIgZ2VuZXMgd2l0aCBhZGouUC52YWx1ZSA8IDAuMDEgYW5kIGEgbG9nRkMgb2YgPjIgKGFuZCA8IC0yKS4gWW91IGNhbiBjaG9vc2UgdG8gdXNlIGFueSBjb2xvdXJzIGFuZCBtb2RpZnkgaXQgd2hhdGV2ZXIgd2F5IHlvdSBsaWtlLgoKCiMjIEtleSBQb2ludHMKLSBXZSBjYW4gbWFrZSBhIHZvbGNhbm8gcGxvdCB3aXRoIGBnZW9tX3BvaW50KClgIGJ5IHBsb3R0aW5nIHRoZSBsb2cgZm9sZCBjaGFuZ2UgKGxvZ0ZDKSB2cyB0aGUgbmVnYXRpdmUgbG9nMTAgUCB2YWx1ZQotIFdlIGNhbiBhZGQgY29sdW1ucyB3aXRoIGRwbHlyJ3MgYG11dGF0ZSgpYAotIFRvIGNvbG91ciBvciBsYWJlbCBzcGVjaWZpYyBwb2ludHMgd2UgYWRkIGEgY29sdW1uIHRvIHNwZWNpZnkgd2hpY2ggcG9pbnRzIHRvIGNvbG91ciBvciBsYWJlbAotIFdlIGNhbiB1c2UgYGlmZWxzZSgpYCB0byB0ZXN0IGlmIHZhbHVlcyBtZWV0IHNwZWNpZmllZCBjb25kaXRpb25zCi0gV2UgdXNlIGBjKClgIHRvIGNvbWJpbmUgbXVsdGlwbGUgdmFsdWVzCi0gV2UgdXNlIGAlaW4lYCB0byBjaGVjayBpZiBhIHZhbHVlIGlzIGluIGEgc2V0IG9mIHZhbHVlcwotIFdlIGNhbiB1c2UgZHBseXIncyBgcHVsbCgpYCB0byBwdWxsIGEgc2V0IG9mIHZhbHVlcyBvdXQgb2YgYSBjb2x1bW4KLSBXZSBjYW4gdXNlIGBnZW9tX3RleHQoKWAgdG8gYWRkIHRleHQgdG8gYSBwbG90Ci0gV2UgY2FuIHVzZSB0aGUgZ2dyZXBlbCBwYWNrYWdlIHRvIHJlcGVsIG92ZXJsYXBwaW5nIGxhYmVscwotIFdlIHVzZSB0aGVtZXMgdG8gbW9kaWZ5IHRoZSBub24tZGF0YSBwYXJ0cyBvZiBhIGdncGxvdAo=