Acknowledgements

Material created by Peter Mac Data Science with some content remixed from CRUK Cambridge and Data Carpentry.

Objectives

  • Demonstrate how R and ggplot2 can be used to visualise data, using RNA-seq as an example
  • Demonstrate basic R syntax (<-, dim(), head(), View(), str(), summary())
  • Demonstrate how to use ggplot2 to create bar plots, density plots, box plots and violin plots

Introduction

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

   

RNA-seq dataset

We will create some plots using 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). There are 2 samples per group and 6 groups, 12 samples in total. In this first session we will generate some plots to explore the data, to see if it looks ok.

We will use the raw counts here. These are the counts of reads for each gene for each sample. The higher the number of counts the more the gene is expressed. Some of the plots we will make are based on those in the COMBINE RNA-seq R workshop, except here we will create plots using ggplot2 from the tidyverse.

   

Tidyverse

www.tidyverse.org

www.tidyverse.org

The tidyverse is a collection of packages that includes ggplot2 and we will introduce you to some of these packages in this course.

     

Tidyverse packages

Tidyverse packages

     

The tidyverse makes data science faster, easier and more fun.

     

The tidyverse package is already installed for you on the server but if you need to install it on your own computer you can use install.packages('tidyverse').
     

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 first_plots.R.

We will begin by loading in the packages that we need.

library(tidyverse)

library() is the function in R that we use to load packages. We will see many functions in the course. Functions are “canned scripts” that automate more complicated sets of commands. Many functions are predefined, or can be made available by importing R packages. A function usually takes one or more inputs called arguments. Here tidyverse is the argument to the library() function. Note that functions require parentheses after the function name.

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. Tsv stands for tab-separated values. There is also a read_csv() function for csv files.

To see what the read_tsv() function (or any function in R) does, type a ? before the name and the help will appear in the Help panel on the right in RStudio. Or you can search the function name in the Help panel search box.

?read_tsv

We will use the counts file called counts.tsv.gz that’s in a folder called data i.e. the path to the file should be data/counts.tsv.gz.

We can read the counts file into R with the command below. We’ll store the contents of the counts file in an object called counts. This stores the file contents in R’s memory making it easier to use.

counts <- read_tsv("data/counts.tsv.gz")
Parsed with column specification:
cols(
  Sample = col_character(),
  CellType = col_character(),
  Status = col_character(),
  SYMBOL = col_character(),
  Counts = col_double()
)

In R we use <- to assign values to objects. <- is the assignment operator. It assigns values on the right to objects on the left. So to create an object, we need to give it a name (e.g. counts), followed by the assignment operator<-`, and the value we want to give it. We can read in a file from a path on our computer on on the web and use this as the value. Note that we need to put quotes (“”) around file paths.

Assignment operator shortcut

In RStudio, typing Alt + - (push Alt at the same time as the - key) will write <- in a single keystroke in a PC, while typing > Option + - (push Option at the same time as the - key) does the same in a Mac.

The value of counts is the contents of the counts file. There is some information output by read_tsv on column specifications, this is the data type that read_tsv has guessed is contained in each column, we will discuss this more later.

Naming objects

Objects can be given any name, such as counts, rnaseq_counts or x, but some recommendations on naming objects are:

  • You want your object names to be explicit and not too long.
  • They cannot start with a number (2x is not valid, but x2 is).
  • They cannot contain spaces.
  • R is case sensitive (e.g., counts is different from Counts).
  • There are some names that cannot be used because they are the names of fundamental functions in R (e.g.,if, else, for, see here for a complete list). In general, even if it’s allowed, it’s best to not use other function names (e.g., c, T, mean, data, df, weights). If in doubt, check the help to see if the name is already in use.
  • It’s also best to avoid dots (.) within an object name as in my.dataset. There are many functions in R with dots in their names for historical reasons, but because dots have a special meaning in R (for methods) and other programming languages, it’s best to avoid them. Underscores (_) would be a better separator, as recommended in the tidyverse style guide.

Getting to know the data

When assigning a value to an object, R does not print the value. For example, here we don’t see what’s in the counts file. But there are ways we can look at the data.

We can type the name of the object and this will print the first few lines and some information, such as number of rows.

counts

We can use dim() to see the dimensions of an object, the number of rows and columns.

dim(counts)
[1] 326148      5

This show us there are 326,148 rows and 5 columns.

In the Environment Tab in the top right panel in RStudio we can also see the number of rows and columns in the objects we have in our session.

We can also take a look the first few lines with head(). This shows us the first 6 lines.

head(counts)

We can look at the last few lines with tail(). This shows us the last 6 lines. This can be useful to check the bottom of the file, that it looks ok.

tail(counts)

Or we can see the whole file with View().

View(counts)

Other useful commands for checking data are str() and summary().

str() shows us the structure of our data. It shows us what columns there are, the first few entries, and what data type they are e.g. character or numbers (double or integer).

str(counts)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   326148 obs. of  5 variables:
 $ Sample  : chr  "DG" "DG" "DG" "DG" ...
 $ CellType: chr  "basal" "basal" "basal" "basal" ...
 $ Status  : chr  "virgin" "virgin" "virgin" "virgin" ...
 $ SYMBOL  : chr  "Pzp" "Aanat" "Aatk" "Abca1" ...
 $ Counts  : num  1 1 746 3307 19 ...
 - attr(*, "spec")=
  .. cols(
  ..   Sample = col_character(),
  ..   CellType = col_character(),
  ..   Status = col_character(),
  ..   SYMBOL = col_character(),
  ..   Counts = col_double()
  .. )

summary() generates summary statistics of our data. For numeric columns (columns of type double or integer) it outputs statistics such as the min, max, mean and median. For character columns it shows us the length (how many rows).

summary(counts)
    Sample            CellType            Status             SYMBOL              Counts         
 Length:326148      Length:326148      Length:326148      Length:326148      Min.   :      0.0  
 Class :character   Class :character   Class :character   Class :character   1st Qu.:      0.0  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   Median :     14.0  
                                                                             Mean   :    824.9  
                                                                             3rd Qu.:    402.0  
                                                                             Max.   :2887969.0  

Try running the above commands yourself.

Plotting with ggplot2

ggplot2 is a plotting package that makes it simple to create complex plots. One really great benefit of ggplot2 versus the older base R plotting is that we only need to make minimal changes if the underlying data change or if we decide to change our plot type, for example, from a bar plot to a box plot. This helps in creating publication quality plots with minimal amounts of adjustments and tweaking.

ggplot2 likes data in the ‘long’ format, also called ‘tidy’ format, i.e., a column for every variable, and a row for every observation. Well-structured data will save you lots of time when making figures with ggplot2. We will discuss tidy data more later in the course.

ggplot graphics are built step by step by adding new elements. Adding layers in this fashion allows for extensive flexibility and customization of plots.

To build a ggplot, we use the following basic template that can be used for different types of plots:

ggplot(data=, mapping=aes()) + geom_ ()
Error in geom_() : could not find function "geom_"

Three things are required for a ggplot:

  1. The data
  2. The mapping of variables (columns) in the data to visual properties (called aesthetics in ggplot2) of objects in the plot
  3. The type of plot – this is called a geom in ggplot2 terminology

There are different geoms we can use to create different types of plot e.g. bar plot versus box plot, to see some of the geoms available see the ggplot2 help or the handy ggplot2 cheatsheet.

Our first ggplot

Before we start plotting the RNA-seq data we’ll demonstrate how ggplot works using one of the built-in datasets in R. You can see what built-in datasets there are by typing data(). We will use the dataset called women that contains the average heights and weights for American women.

First take a look at the dataset.

women
   height weight
1      58    115
2      59    117
3      60    120
4      61    123
5      62    126
6      63    129
7      64    132
8      65    135
9      66    139
10     67    142
11     68    146
12     69    150
13     70    154
14     71    159
15     72    164

Let’s make a scatterplot. We give ggplot() the dataset (women) and in aes() we say which columns are the x and y axis values. To say what type of plot we want we add a geom using the + operator. This must go at the end of the line, and not at the beginning or ggplot2 will give an error.

ggplot(data=women, mapping=aes(x=height, y=weight)) + 
  geom_point()

We have generated our first plot!

Now we will explore the RNA-Seq data using different types of plots.

Bar plots

How many counts do we have for each sample?

We can make bar plots to visualise this. To do this we will use geom_bar().

You might think we specify the x= and y= values as before with geom_point() but look what happens.

ggplot(data=counts, mapping=aes(x=Sample, y=Counts)) +
  geom_bar()
Error: stat_count() must not be used with a y aesthetic.

We get an error Error: stat_count() must not be used with a y aesthetic.

We can use the bioinformatician’s good friend Google to try to figure out why. Through Google we can find an answer on Stack Overflow (Stack Overflow is another good friend of bioinformaticians) https://stackoverflow.com/questions/39679057/r-ggplot2-stat-count-must-not-be-used-with-a-y-aesthetic-error-in-bar-graph/39679104. As pointed out in that post, we could specify y=Counts if we had already calculated the value we wanted to plot (the total counts for each sample). However, our input data contains the counts for each gene for each sample. We want to sum these gene counts for each sample. Let’s take a look at the help for geom_bar() (as shown in that Stack Overflow post).

?geom_bar

It says “geom_bar() makes the height of the bar proportional to the number of cases in each group (or if the weight aesthetic is supplied, the sum of the weights).” So we can specify x=Sample and weight=Counts and ggplot will automatically sum the counts for each sample for us.

ggplot(data=counts, mapping=aes(x=Sample, weight=Counts)) +
  geom_bar() 

This shows us we have 20-25 million counts per sample, none are very different.

What if we would like to add some colour to the plot, for example, a different colour bar for each sample.

If we look at the geom_bar help again we can see under the heading called “Aesthetics” that there’s an option for colour. Let’s try adding that to our plot. We’ll specify we want to map the Sample column to colour=. As we are mapping colour to a column in our data we need to put this inside the aes().

ggplot(data=counts, mapping=aes(x=Sample, weight=Counts, colour=Sample)) +
  geom_bar() 

Hmm colouring the edges wasn’t quite what we had in mind. Look at the help for geom_bar to see what other aesthetic we could use. Let’s try fill= instead.

ggplot(data=counts, mapping=aes(x=Sample, weight=Counts, fill=Sample)) +
  geom_bar() 

That looks better. fill= is used to fill in areas in ggplot2 plots, whereas colour= is used to colour lines and points.

A really nice feature about ggplot is that we can easily colour by another variable e.g. cell type (basal vs luminal) by simply changing the column we give to fill=.

ggplot(data=counts, mapping=aes(x=Sample, weight=Counts, fill=CellType)) +
  geom_bar() 

Or we can colour by status (virgin vs pregnant vs lactating).

ggplot(data=counts, mapping=aes(x=Sample, weight=Counts, fill=Status)) +
  geom_bar() 

For more details on bar plots see this R bloggers post. R bloggers is a useful resource for R news and tutorials, you can subscribe to their mailing list for free.

Box plots

In addition to viewing the number of counts per sample it is also useful to visualise the distribution of the counts. This helps us to compare the samples and check if any look unusual. We can make box plots to visualise the distribution of counts for each sample.

Let’s try that.

ggplot(data=counts, mapping=aes(x=Sample, y=Counts)) +
  geom_boxplot()

That looks weird. It’s because we have some genes with extremely high counts. To make it easier to visualise the distributions we usually plot the logarithm of RNA-seq counts. We’ll plot the Sample on the X axis and log2 Counts on the y axis. We can log the Counts within the aes().

ggplot(data=counts, mapping=aes(x=Sample, y=log2(Counts))) +
  geom_boxplot()

Note that we get a warning here about rows containing non-finite values being removed. This is because some of the genes have a count of zero in the samples and a log of zero is undefined. Usually we would filter lowly expressed genes before creating the box plots so we may not always get a warning like this. But here we are just demonstrating the different types of plots that can be made.

The box plots show that the distributions of the samples are not identical but they are not very different.

Exercise

Colour the box plots by Sample, then CellType, then Status.

Violin plots

Box plots are useful summaries, but hide the shape of the distribution. For example, if the distribution is bimodal, we would not see it in a boxplot. An alternative to the boxplot is the violin plot, where the shape (of the density of points) is drawn. See here for an example of how differences in distribution may be hidden in box plots but revealed with violin plots.

Exercise

Make violin plots instead of box plots using geom_violin() and colour by Sample, then CellType, then Status. How do the violin plots for the samples compare?

Density plots

We can also visualise distributions with density plots. In this case we specify x=log2(Counts) (no need to specify y=) and geom_density() will create a density plot of the counts.

ggplot(data=counts, mapping=aes(x=log2(Counts))) +
  geom_density()

We can add a small number to every count (we’ll choose 0.25) in order to visualise the amount of genes with counts of zero in this plot.

ggplot(data=counts, mapping=aes(x=log2(Counts+0.25))) +
  geom_density()

Note the large peak that appears on the left. This is the large number of genes with zero counts. In an RNA-seq analysis filtering lowly expressed genes would remove or reduce that peak.

We can colour by sample using fill=.

ggplot(data=counts, mapping=aes(x=log2(Counts+0.25), fill=Sample)) +
  geom_density() 

If we would like to colour the lines in the plot instead of the areas we can use colour= instead of fill=.

ggplot(data=counts, mapping=aes(x=log2(Counts+0.25), colour=Sample)) +
  geom_density() 

Why is there a line across the bottom of the plot?

We can use Google and Stack Overflow again to find out why: https://stackoverflow.com/questions/49593634/unintended-line-across-x-axis-of-density-plot-r. It is because geom_density() plots a polygon shape. If we don’t want that line we can adapt the code given in that Stack Overflow post, using geom_line(stat="density").

ggplot(data=counts, mapping=aes(x=log2(Counts+0.25), colour=Sample)) +
  geom_line(stat="density")

Statistical transformations

Many graphs, like scatterplots and line plots, plot the raw values from your dataset; other graphs, like density plots and bar plots, calculate new values to plot.

  • bar plots and histograms (geom_bar, geom_histogram, geom_freqpoly) bin your data and then plot bin counts
  • density plots (geom_density) compute and draw kernel density estimate, which is a smoothed version of the histogram
  • smoothing functions (geom_smooth) fit a model to your data and then plot predictions from the model
  • box plots (geom_boxplot) compute a robust summary of the distribution and display a specially formatted box

The algorithm used to calculate new values for a graph is called a stat

Saving plots

We can output plots to pdf using pdf() followed by dev.off(). We put our plot code after the call to pdf() and before closing the plot device with dev.off().

Let’s save our last plot.

pdf("myplot.pdf")
ggplot(data=counts, mapping=aes(x=log2(Counts+0.25), colour=Sample)) +
  geom_line(stat="density")
dev.off()

One last thing

It is recommended to use bar plots only for visualising single values, for example, the total counts per sample here. Dynamite plots, a type of bar plot commonly used by scientists and in journals, are not recommended, see this post called “Dynamite plots must die” for an explanation. Box plots, violin plots and density plots can be a better way to visualise distributions of data.

Exercise

  • Read in the normalised counts (from “data/normcounts.tsv.gz”). This is the counts data after filtering lowly expressed genes and normalising for differences in sequencing depth and composition bias.
  • How many rows and columns are in the file?
  • What are the column names?
  • In the first row, what is the sample id, the gene symbol and the value for the counts?
  • In the last row, what is the sample id, the gene symbol and the value for the counts?
  • What are the minimum, maximum, mean and median values in the Norm_counts column?
  • Make as many plots as you can from bar plots, box plots, violin plots and density plots using the normalised counts (some starter code is below)
  • Make separate plots coloured by Sample, CellType, Status, where appropriate.
  • Do you notice any differences with the normalised counts versus the unnormalised counts that we used previously (counts dataset)?
norm_counts <- read_tsv()
ggplot(data= , mapping=aes(x= , weight= , fill= )) +
  geom_bar() 
ggplot(data= , mapping=aes(x= , y= , fill= )) +
  geom_boxplot() 
ggplot(data= , mapping=aes(x= , y= , fill= )) +
  geom_violin()
ggplot(data= , mapping=aes(x= , colour= )) +
  geom_density() 

Key Points

  • We use the library() function to load packages we want to use. Note that they need to be already installed with e.g. install.packages().
  • We can read in tab-separated files with read_tsv().
  • We can check our data with the functions dim(), head(), tail(), View(), str() and summary().
  • ggplot2 is a plotting package that makes it simple to create complex plots.
  • ggplot2 plots have 3 components: data (dataset), mapping (columns to plot) and geom (type of plot).
  • The + sign is used to add geoms (and new layers as we will see later in the course). It must be placed at the end of the preceding line. If it is added at the beginning of the line, ggplot2 will return an error message.
  • ggplot2 plots can be easily coloured by columns in the dataset. fill= can be used to colour areas and colour= to colour lines.
  • A pdf of the plot can be generated with pdf() and dev.off()
LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFIiCmF1dGhvcjogIk1hcmlhIERveWxlIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiAlWScpYCIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDIKc3VidGl0bGU6IHZpc3VhbGlzaW5nIFJOQS1zZXEgZGF0YSB3aXRoIGdncGxvdDIKLS0tClwgIApcICAKCiMjIyMgQWNrbm93bGVkZ2VtZW50cwpNYXRlcmlhbCBjcmVhdGVkIGJ5IFBldGVyIE1hYyBEYXRhIFNjaWVuY2Ugd2l0aCBzb21lIGNvbnRlbnQgcmVtaXhlZCBmcm9tIFtDUlVLIENhbWJyaWRnZV0oaHR0cDovL2Jpb2luZm9ybWF0aWNzLWNvcmUtc2hhcmVkLXRyYWluaW5nLmdpdGh1Yi5pby9yLWludGVybWVkaWF0ZS8pIGFuZCBbRGF0YSBDYXJwZW50cnldKGh0dHBzOi8vZGF0YWNhcnBlbnRyeS5vcmcvUi1lY29sb2d5LWxlc3Nvbi8wNC12aXN1YWxpemF0aW9uLWdncGxvdDIuaHRtbCkuCgojIyBPYmplY3RpdmVzCiAgCiogRGVtb25zdHJhdGUgaG93IFIgYW5kIGdncGxvdDIgY2FuIGJlIHVzZWQgdG8gdmlzdWFsaXNlIGRhdGEsIHVzaW5nIFJOQS1zZXEgYXMgYW4gZXhhbXBsZQoqIERlbW9uc3RyYXRlIGJhc2ljIFIgc3ludGF4IChgPC1gLCBgZGltKClgLCBgaGVhZCgpYCwgYFZpZXcoKWAsIGBzdHIoKWAsIGBzdW1tYXJ5KClgKQoqIERlbW9uc3RyYXRlIGhvdyB0byB1c2UgZ2dwbG90MiB0byBjcmVhdGUgYmFyIHBsb3RzLCBkZW5zaXR5IHBsb3RzLCBib3ggcGxvdHMgYW5kIHZpb2xpbiBwbG90cwoKIyMgSW50cm9kdWN0aW9uCgpJbiB0aGlzIHR1dG9yaWFsLCB3ZSB3aWxsIGxlYXJuIHNvbWUgUiB0aHJvdWdoIGNyZWF0aW5nIHBsb3RzIHRvIHZpc3VhbGlzZSBkYXRhIGZyb20gYW4gUk5BLXNlcSBleHBlcmltZW50LgoKXCAgClwgIAoKIyMjIFJOQS1zZXEgZGF0YXNldAoKV2Ugd2lsbCBjcmVhdGUgc29tZSBwbG90cyB1c2luZyBwdWJsaXNoZWQgUk5BLXNlcSBkYXRhIGZyb20gdGhlIE5hdHVyZSBDZWxsIEJpb2xvZ3kgcGFwZXIgYnkgW0Z1IGV0IGFsLiAyMDE1XShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3B1Ym1lZC8yNTczMDQ3MikuIFRoaXMgc3R1ZHkgZXhhbWluZWQgZXhwcmVzc2lvbiBpbiBiYXNhbCBhbmQgbHVtaW5hbCBjZWxscyBmcm9tIG1pY2UgYXQgZGlmZmVyZW50IHN0YWdlcyAodmlyZ2luLCBwcmVnbmFudCBhbmQgbGFjdGF0aW5nKS4gVGhlcmUgYXJlIDIgc2FtcGxlcyBwZXIgZ3JvdXAgYW5kIDYgZ3JvdXBzLCAxMiBzYW1wbGVzIGluIHRvdGFsLiBJbiB0aGlzIGZpcnN0IHNlc3Npb24gd2Ugd2lsbCBnZW5lcmF0ZSBzb21lIHBsb3RzIHRvIGV4cGxvcmUgdGhlIGRhdGEsIHRvIHNlZSBpZiBpdCBsb29rcyBvay4KCiFbXShpbWFnZXMvbW91c2VfZXhwLnBuZykKCldlIHdpbGwgdXNlIHRoZSByYXcgY291bnRzIGhlcmUuIFRoZXNlIGFyZSB0aGUgY291bnRzIG9mIHJlYWRzIGZvciBlYWNoIGdlbmUgZm9yIGVhY2ggc2FtcGxlLiBUaGUgaGlnaGVyIHRoZSBudW1iZXIgb2YgY291bnRzIHRoZSBtb3JlIHRoZSBnZW5lIGlzIGV4cHJlc3NlZC4gU29tZSBvZiB0aGUgcGxvdHMgd2Ugd2lsbCBtYWtlIGFyZSBiYXNlZCBvbiB0aG9zZSBpbiB0aGUgW0NPTUJJTkUgUk5BLXNlcSBSIHdvcmtzaG9wXShodHRwOi8vY29tYmluZS1hdXN0cmFsaWEuZ2l0aHViLmlvL1JOQXNlcS1SLzA2LXJuYXNlcS1kYXkxLmh0bWwpLCBleGNlcHQgaGVyZSB3ZSB3aWxsIGNyZWF0ZSBwbG90cyB1c2luZyAqKmdncGxvdDIqKiBmcm9tIHRoZSAqKnRpZHl2ZXJzZSoqLgoKXCAgClwgIAoKIyMjIFRpZHl2ZXJzZQoKIVt3d3cudGlkeXZlcnNlLm9yZ10oaW1hZ2VzL3RpZHl2ZXJzZS5wbmcpe3dpZHRoPTEwMCUgfQoKVGhlICoqdGlkeXZlcnNlKiogaXMgYSBjb2xsZWN0aW9uIG9mIHBhY2thZ2VzIHRoYXQgaW5jbHVkZXMgICoqYGdncGxvdDJgKiogYW5kIHdlIHdpbGwgaW50cm9kdWNlIHlvdSB0byBzb21lIG9mIHRoZXNlIHBhY2thZ2VzIGluIHRoaXMgY291cnNlLgoKXCAgClwgIApcICAKCiFbVGlkeXZlcnNlIHBhY2thZ2VzXShpbWFnZXMvdGlkeXZlcnNlX3BhY2thZ2VzLmpwZWcpe3dpZHRoPTUwJSB9CgpcICAKXCAgClwgCgoqKlRoZSBbdGlkeXZlcnNlXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnLykgbWFrZXMgZGF0YSBzY2llbmNlIGZhc3RlciwgZWFzaWVyIGFuZCBtb3JlIGZ1bi4qKgoKXCAgClwgIApcICAKClRoZSB0aWR5dmVyc2UgcGFja2FnZSBpcyBhbHJlYWR5IGluc3RhbGxlZCBmb3IgeW91IG9uIHRoZSBzZXJ2ZXIgYnV0IGlmIHlvdSBuZWVkIHRvIGluc3RhbGwgaXQgb24geW91ciBvd24gY29tcHV0ZXIgeW91IGNhbiB1c2UgYGluc3RhbGwucGFja2FnZXMoJ3RpZHl2ZXJzZScpYC4gIApcICAKXCAgClwgIAoKIyMgTG9hZGluZyB0aGUgZGF0YQoKRmlyc3QgbGV0J3Mgb3BlbiBhIG5ldyBSIHNjcmlwdC4gRnJvbSB0aGUgdG9wIG1lbnUgaW4gUlN0dWRpbzogYEZpbGUgPiBOZXcgRmlsZSA+IFIgU2NyaXB0YC4KTGV0J3Mgc2F2ZSBpdCBhcyBgZmlyc3RfcGxvdHMuUmAuCgpXZSB3aWxsIGJlZ2luIGJ5IGxvYWRpbmcgaW4gdGhlIHBhY2thZ2VzIHRoYXQgd2UgbmVlZC4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpgbGlicmFyeSgpYCBpcyB0aGUgKipmdW5jdGlvbioqIGluIFIgdGhhdCB3ZSB1c2UgdG8gbG9hZCBwYWNrYWdlcy4gV2Ugd2lsbCBzZWUgbWFueSBmdW5jdGlvbnMgaW4gdGhlIGNvdXJzZS4gRnVuY3Rpb25zIGFyZSAiY2FubmVkIHNjcmlwdHMiIHRoYXQgYXV0b21hdGUgbW9yZSBjb21wbGljYXRlZCBzZXRzIG9mIGNvbW1hbmRzLiBNYW55IGZ1bmN0aW9ucyBhcmUgcHJlZGVmaW5lZCwgb3IgY2FuIGJlIG1hZGUgYXZhaWxhYmxlIGJ5IGltcG9ydGluZyBSICpwYWNrYWdlcyouIEEgZnVuY3Rpb24gdXN1YWxseSB0YWtlcyBvbmUgb3IgbW9yZSBpbnB1dHMgY2FsbGVkICphcmd1bWVudHMqLiBIZXJlIHRpZHl2ZXJzZSBpcyB0aGUgYXJndW1lbnQgdG8gdGhlIGBsaWJyYXJ5KClgIGZ1bmN0aW9uLiBOb3RlIHRoYXQgZnVuY3Rpb25zIHJlcXVpcmUgcGFyZW50aGVzZXMgYWZ0ZXIgdGhlIGZ1bmN0aW9uIG5hbWUuCgpUaGUgZmlsZSB3ZSB3aWxsIHVzZSBpcyB0YWItc2VwYXJhdGVkLCBzbyB3ZSB3aWxsIHVzZSB0aGUgYHJlYWRfdHN2KClgIGZ1bmN0aW9uIGZyb20gdGhlIHRpZHl2ZXJzZSAqKmByZWFkcmAqKiBwYWNrYWdlIHRvIHJlYWQgaXQgaW4uIFRzdiBzdGFuZHMgZm9yIHRhYi1zZXBhcmF0ZWQgdmFsdWVzLiBUaGVyZSBpcyBhbHNvIGEgYHJlYWRfY3N2KClgIGZ1bmN0aW9uIGZvciBjc3YgZmlsZXMuCgpUbyBzZWUgd2hhdCB0aGUgYHJlYWRfdHN2KClgIGZ1bmN0aW9uIChvciBhbnkgZnVuY3Rpb24gaW4gUikgZG9lcywgdHlwZSBhIGA/YCBiZWZvcmUgdGhlIG5hbWUgYW5kIHRoZSBoZWxwIHdpbGwgYXBwZWFyIGluIHRoZSBIZWxwIHBhbmVsIG9uIHRoZSByaWdodCBpbiBSU3R1ZGlvLiBPciB5b3UgY2FuIHNlYXJjaCB0aGUgZnVuY3Rpb24gbmFtZSBpbiB0aGUgSGVscCBwYW5lbCBzZWFyY2ggYm94LgoKYGBge3IsIGV2YWw9RkFMU0V9Cj9yZWFkX3RzdgpgYGAKCldlIHdpbGwgdXNlIHRoZSBjb3VudHMgZmlsZSBjYWxsZWQgYGNvdW50cy50c3YuZ3pgIHRoYXQncyBpbiBhIGZvbGRlciBjYWxsZWQgYGRhdGFgIGkuZS4gdGhlIHBhdGggdG8gdGhlIGZpbGUgc2hvdWxkIGJlIGBkYXRhL2NvdW50cy50c3YuZ3pgLgoKV2UgY2FuIHJlYWQgdGhlIGNvdW50cyBmaWxlIGludG8gUiB3aXRoIHRoZSBjb21tYW5kIGJlbG93LiBXZSdsbCBzdG9yZSB0aGUgY29udGVudHMgb2YgdGhlIGNvdW50cyBmaWxlIGluIGFuICoqb2JqZWN0KiogY2FsbGVkIGBjb3VudHNgLiBUaGlzIHN0b3JlcyB0aGUgZmlsZSBjb250ZW50cyBpbiBSJ3MgbWVtb3J5IG1ha2luZyBpdCBlYXNpZXIgdG8gdXNlLgoKYGBge3J9CmNvdW50cyA8LSByZWFkX3RzdigiZGF0YS9jb3VudHMudHN2Lmd6IikKYGBgCgpJbiBSIHdlIHVzZSBgPC1gIHRvIGFzc2lnbiB2YWx1ZXMgdG8gb2JqZWN0cy4gYDwtYCBpcyB0aGUgKiphc3NpZ25tZW50IG9wZXJhdG9yKiouICBJdCBhc3NpZ25zIHZhbHVlcyBvbiB0aGUgcmlnaHQgdG8gb2JqZWN0cyBvbiB0aGUgbGVmdC4gU28gdG8gY3JlYXRlIGFuIG9iamVjdCwgd2UgbmVlZCB0byBnaXZlIGl0IGEgbmFtZSAoZS5nLiBgY291bnRzKSwgZm9sbG93ZWQgYnkgdGhlIGFzc2lnbm1lbnQgb3BlcmF0b3IgYDwtYCwgYW5kIHRoZSB2YWx1ZSB3ZSB3YW50IHRvIGdpdmUgaXQuIFdlIGNhbiByZWFkIGluIGEgZmlsZSBmcm9tIGEgcGF0aCBvbiBvdXIgY29tcHV0ZXIgb24gb24gdGhlIHdlYiBhbmQgdXNlIHRoaXMgYXMgdGhlIHZhbHVlLiBOb3RlIHRoYXQgd2UgbmVlZCB0byBwdXQgcXVvdGVzICgiIikgYXJvdW5kIGZpbGUgcGF0aHMuCgo+ICMjIyMgQXNzaWdubWVudCBvcGVyYXRvciBzaG9ydGN1dAo+IEluIFJTdHVkaW8sIHR5cGluZyA8a2JkPkFsdDwva2JkPiArIDxrYmQ+LTwva2JkPiAocHVzaCA8a2JkPkFsdDwva2JkPiBhdCB0aGUKPiBzYW1lIHRpbWUgYXMgdGhlIDxrYmQ+LTwva2JkPiBrZXkpIHdpbGwgd3JpdGUgYCA8LSBgIGluIGEgc2luZ2xlIGtleXN0cm9rZSBpbiBhIFBDLCB3aGlsZSB0eXBpbmcgPiA8a2JkPk9wdGlvbjwva2JkPiArIDxrYmQ+LTwva2JkPiAocHVzaCA8a2JkPk9wdGlvbjwva2JkPiBhdCB0aGUKPiBzYW1lIHRpbWUgYXMgdGhlIDxrYmQ+LTwva2JkPiBrZXkpIGRvZXMgdGhlIHNhbWUgaW4gYSBNYWMuIAoKVGhlIHZhbHVlIG9mIGBjb3VudHNgIGlzIHRoZSBjb250ZW50cyBvZiB0aGUgY291bnRzIGZpbGUuIFRoZXJlIGlzIHNvbWUgaW5mb3JtYXRpb24gb3V0cHV0IGJ5IGByZWFkX3RzdmAgb24gY29sdW1uIHNwZWNpZmljYXRpb25zLCB0aGlzIGlzIHRoZSBkYXRhIHR5cGUgdGhhdCBgcmVhZF90c3ZgIGhhcyBndWVzc2VkIGlzIGNvbnRhaW5lZCBpbiBlYWNoIGNvbHVtbiwgd2Ugd2lsbCBkaXNjdXNzIHRoaXMgbW9yZSBsYXRlci4KCj4gIyMjIyBOYW1pbmcgb2JqZWN0cwo+IE9iamVjdHMgY2FuIGJlIGdpdmVuIGFueSBuYW1lLCBzdWNoIGFzIGBjb3VudHNgLCBgcm5hc2VxX2NvdW50c2Agb3IgYHhgLCBidXQgc29tZSByZWNvbW1lbmRhdGlvbnMgb24gbmFtaW5nIG9iamVjdHMgYXJlOgo+Cj4gICogWW91IHdhbnQgeW91ciBvYmplY3QgbmFtZXMgdG8gYmUgZXhwbGljaXQgYW5kIG5vdCB0b28gbG9uZy4gCj4gICogVGhleSBjYW5ub3Qgc3RhcnQgd2l0aCBhIG51bWJlciAoYDJ4YCBpcyBub3QgdmFsaWQsIGJ1dCBgeDJgIGlzKS4gCj4gICogVGhleSBjYW5ub3QgY29udGFpbiBzcGFjZXMuCj4gICogUiBpcyBjYXNlIHNlbnNpdGl2ZSAoZS5nLiwgYGNvdW50c2AgaXMgZGlmZmVyZW50IGZyb20gYENvdW50c2ApLiAKPiAgKiBUaGVyZSBhcmUgc29tZSBuYW1lcyB0aGF0IGNhbm5vdCBiZSB1c2VkIGJlY2F1c2UgdGhleSBhcmUgdGhlIG5hbWVzIG9mIGZ1bmRhbWVudGFsIGZ1bmN0aW9ucyBpbiBSIChlLmcuLGBpZmAsIGBlbHNlYCwgYGZvcmAsIHNlZQo+IFtoZXJlXShodHRwczovL3N0YXQuZXRoei5jaC9SLW1hbnVhbC9SLWRldmVsL2xpYnJhcnkvYmFzZS9odG1sL1Jlc2VydmVkLmh0bWwpCj4gZm9yIGEgY29tcGxldGUgbGlzdCkuIEluIGdlbmVyYWwsIGV2ZW4gaWYgaXQncyBhbGxvd2VkLCBpdCdzIGJlc3QgdG8gbm90IHVzZQo+IG90aGVyIGZ1bmN0aW9uIG5hbWVzIChlLmcuLCBgY2AsIGBUYCwgYG1lYW5gLCBgZGF0YWAsIGBkZmAsIGB3ZWlnaHRzYCkuIElmIGluCj4gZG91YnQsIGNoZWNrIHRoZSBoZWxwIHRvIHNlZSBpZiB0aGUgbmFtZSBpcyBhbHJlYWR5IGluIHVzZS4gCj4gICogSXQncyBhbHNvIGJlc3QgdG8gYXZvaWQgZG90cyAoYC5gKSB3aXRoaW4gYW4gb2JqZWN0IG5hbWUgYXMgaW4gYG15LmRhdGFzZXRgLiBUaGVyZSBhcmUgbWFueSBmdW5jdGlvbnMgaW4gUiB3aXRoIGRvdHMgaW4gdGhlaXIgbmFtZXMgZm9yIGhpc3RvcmljYWwgcmVhc29ucywgYnV0IGJlY2F1c2UgZG90cyBoYXZlIGEgc3BlY2lhbCBtZWFuaW5nIGluIFIgKGZvciBtZXRob2RzKSBhbmQgb3RoZXIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzLCBpdCdzIGJlc3QgdG8gYXZvaWQgdGhlbS4gVW5kZXJzY29yZXMgKGBfYCkgd291bGQgYmUgYSBiZXR0ZXIgc2VwYXJhdG9yLCBhcyByZWNvbW1lbmRlZCBpbiB0aGUgW3RpZHl2ZXJzZSBzdHlsZSBndWlkZV0oaHR0cHM6Ly9zdHlsZS50aWR5dmVyc2Uub3JnL3N5bnRheC5odG1sI29iamVjdC1uYW1lKS4KCiMjIEdldHRpbmcgdG8ga25vdyB0aGUgZGF0YQoKV2hlbiBhc3NpZ25pbmcgYSB2YWx1ZSB0byBhbiBvYmplY3QsIFIgZG9lcyBub3QgcHJpbnQgdGhlIHZhbHVlLiBGb3IgZXhhbXBsZSwgaGVyZSB3ZSBkb24ndCBzZWUgd2hhdCdzIGluIHRoZSBjb3VudHMgZmlsZS4gQnV0IHRoZXJlIGFyZSB3YXlzIHdlIGNhbiBsb29rIGF0IHRoZSBkYXRhLiAKCldlIGNhbiB0eXBlIHRoZSBuYW1lIG9mIHRoZSBvYmplY3QgYW5kIHRoaXMgd2lsbCBwcmludCB0aGUgZmlyc3QgZmV3IGxpbmVzIGFuZCBzb21lIGluZm9ybWF0aW9uLCBzdWNoIGFzIG51bWJlciBvZiByb3dzLgoKYGBge3J9CmNvdW50cwpgYGAKCldlIGNhbiB1c2UgYGRpbSgpYCB0byBzZWUgdGhlIGRpbWVuc2lvbnMgb2YgYW4gb2JqZWN0LCB0aGUgbnVtYmVyIG9mIHJvd3MgYW5kIGNvbHVtbnMuCgpgYGB7cn0KZGltKGNvdW50cykKYGBgCgpUaGlzIHNob3cgdXMgdGhlcmUgYXJlIDMyNiwxNDggcm93cyBhbmQgNSBjb2x1bW5zLgoKSW4gdGhlIEVudmlyb25tZW50IFRhYiBpbiB0aGUgdG9wIHJpZ2h0IHBhbmVsIGluIFJTdHVkaW8gd2UgY2FuIGFsc28gc2VlIHRoZSBudW1iZXIgb2Ygcm93cyBhbmQgY29sdW1ucyBpbiB0aGUgb2JqZWN0cyB3ZSBoYXZlIGluIG91ciBzZXNzaW9uLgoKV2UgY2FuIGFsc28gdGFrZSBhIGxvb2sgdGhlIGZpcnN0IGZldyBsaW5lcyB3aXRoIGBoZWFkKClgLiBUaGlzIHNob3dzIHVzIHRoZSBmaXJzdCA2IGxpbmVzLgoKYGBge3J9CmhlYWQoY291bnRzKQpgYGAKCldlIGNhbiBsb29rIGF0IHRoZSBsYXN0IGZldyBsaW5lcyB3aXRoIGB0YWlsKClgLiBUaGlzIHNob3dzIHVzIHRoZSBsYXN0IDYgbGluZXMuIFRoaXMgY2FuIGJlIHVzZWZ1bCB0byBjaGVjayB0aGUgYm90dG9tIG9mIHRoZSBmaWxlLCB0aGF0IGl0IGxvb2tzIG9rLgpgYGB7cn0KdGFpbChjb3VudHMpCmBgYAoKT3Igd2UgY2FuIHNlZSB0aGUgd2hvbGUgZmlsZSB3aXRoIGBWaWV3KClgLgoKYGBge3IgZXZhbD1GQUxTRX0KVmlldyhjb3VudHMpCmBgYAoKCk90aGVyIHVzZWZ1bCBjb21tYW5kcyBmb3IgY2hlY2tpbmcgZGF0YSBhcmUgYHN0cigpYCBhbmQgYHN1bW1hcnkoKWAuCgpgc3RyKClgIHNob3dzIHVzIHRoZSBzdHJ1Y3R1cmUgb2Ygb3VyIGRhdGEuIEl0IHNob3dzIHVzIHdoYXQgY29sdW1ucyB0aGVyZSBhcmUsIHRoZSBmaXJzdCBmZXcgZW50cmllcywgYW5kIHdoYXQgZGF0YSB0eXBlIHRoZXkgYXJlIGUuZy4gY2hhcmFjdGVyIG9yIG51bWJlcnMgKGRvdWJsZSBvciBpbnRlZ2VyKS4KCmBgYHtyfQpzdHIoY291bnRzKQpgYGAKCmBzdW1tYXJ5KClgIGdlbmVyYXRlcyBzdW1tYXJ5IHN0YXRpc3RpY3Mgb2Ygb3VyIGRhdGEuIEZvciBudW1lcmljIGNvbHVtbnMgKGNvbHVtbnMgb2YgdHlwZSBkb3VibGUgb3IgaW50ZWdlcikgaXQgb3V0cHV0cyBzdGF0aXN0aWNzIHN1Y2ggYXMgdGhlIG1pbiwgbWF4LCBtZWFuIGFuZCBtZWRpYW4uICBGb3IgY2hhcmFjdGVyIGNvbHVtbnMgaXQgc2hvd3MgdXMgdGhlIGxlbmd0aCAoaG93IG1hbnkgcm93cykuCgoKYGBge3J9CnN1bW1hcnkoY291bnRzKQpgYGAKClRyeSBydW5uaW5nIHRoZSBhYm92ZSBjb21tYW5kcyB5b3Vyc2VsZi4gCgoKIyMgUGxvdHRpbmcgd2l0aCAqKmBnZ3Bsb3QyYCoqCgoqKmBnZ3Bsb3QyYCoqIGlzIGEgcGxvdHRpbmcgcGFja2FnZSB0aGF0IG1ha2VzIGl0IHNpbXBsZSB0byBjcmVhdGUgY29tcGxleCBwbG90cy4gT25lIHJlYWxseSBncmVhdCBiZW5lZml0IG9mIGdncGxvdDIgdmVyc3VzIHRoZSBvbGRlciBiYXNlIFIgcGxvdHRpbmcgaXMgdGhhdCB3ZSBvbmx5IG5lZWQgdG8gbWFrZSBtaW5pbWFsIGNoYW5nZXMgaWYgdGhlIHVuZGVybHlpbmcgZGF0YSBjaGFuZ2Ugb3IgaWYgd2UgZGVjaWRlIHRvIGNoYW5nZSBvdXIgcGxvdCB0eXBlLCBmb3IgZXhhbXBsZSwgZnJvbSBhIGJhciBwbG90IHRvIGEgYm94IHBsb3QuIFRoaXMgaGVscHMgaW4gY3JlYXRpbmcgcHVibGljYXRpb24gcXVhbGl0eSBwbG90cyB3aXRoIG1pbmltYWwgYW1vdW50cyBvZiBhZGp1c3RtZW50cyBhbmQgdHdlYWtpbmcuCgoqKmBnZ3Bsb3QyYCoqIGxpa2VzIGRhdGEgaW4gdGhlICdsb25nJyBmb3JtYXQsIGFsc28gY2FsbGVkICd0aWR5JyBmb3JtYXQsIGkuZS4sIGEgY29sdW1uIGZvciBldmVyeSB2YXJpYWJsZSwgYW5kIGEgcm93IGZvciBldmVyeSBvYnNlcnZhdGlvbi4gV2VsbC1zdHJ1Y3R1cmVkIGRhdGEgd2lsbCBzYXZlIHlvdSBsb3RzIG9mIHRpbWUKd2hlbiBtYWtpbmcgZmlndXJlcyB3aXRoICoqYGdncGxvdDJgKiouIFdlIHdpbGwgZGlzY3VzcyB0aWR5IGRhdGEgbW9yZSBsYXRlciBpbiB0aGUgY291cnNlLgoKZ2dwbG90IGdyYXBoaWNzIGFyZSBidWlsdCBzdGVwIGJ5IHN0ZXAgYnkgYWRkaW5nIG5ldyBlbGVtZW50cy4gQWRkaW5nIGxheWVycyBpbgp0aGlzIGZhc2hpb24gYWxsb3dzIGZvciBleHRlbnNpdmUgZmxleGliaWxpdHkgYW5kIGN1c3RvbWl6YXRpb24gb2YgcGxvdHMuCgpUbyBidWlsZCBhIGdncGxvdCwgd2UgdXNlIHRoZSBmb2xsb3dpbmcgYmFzaWMgdGVtcGxhdGUgdGhhdCBjYW4gYmUgdXNlZCBmb3IgZGlmZmVyZW50IHR5cGVzIG9mIHBsb3RzOgoKYGBge3IsIGVycm9yPVRSVUV9CmdncGxvdChkYXRhPSwgbWFwcGluZz1hZXMoKSkgKyBnZW9tXyAoKQpgYGAKClRocmVlIHRoaW5ncyBhcmUgcmVxdWlyZWQgZm9yIGEgZ2dwbG90OgoKMS4gVGhlIGRhdGEKMi4gVGhlIG1hcHBpbmcgb2YgdmFyaWFibGVzIChjb2x1bW5zKSBpbiB0aGUgZGF0YSB0byB2aXN1YWwgcHJvcGVydGllcyAoY2FsbGVkIGFlc3RoZXRpY3MgaW4gZ2dwbG90Mikgb2Ygb2JqZWN0cyBpbiB0aGUgcGxvdAozLiBUaGUgdHlwZSBvZiBwbG90IOKAkyB0aGlzIGlzIGNhbGxlZCBhIGdlb20gaW4gZ2dwbG90MiB0ZXJtaW5vbG9neQoKVGhlcmUgYXJlIGRpZmZlcmVudCBnZW9tcyB3ZSBjYW4gdXNlIHRvIGNyZWF0ZSBkaWZmZXJlbnQgdHlwZXMgb2YgcGxvdCBlLmcuIGJhciBwbG90IHZlcnN1cyBib3ggcGxvdCwgdG8gc2VlIHNvbWUgb2YgdGhlIGdlb21zIGF2YWlsYWJsZSBzZWUgdGhlIGdncGxvdDIgaGVscCBvciB0aGUgaGFuZHkgW2dncGxvdDIgY2hlYXRzaGVldF0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvcmF3L21hc3Rlci9kYXRhLXZpc3VhbGl6YXRpb24tMi4xLnBkZikuCgojI091ciBmaXJzdCBnZ3Bsb3QKCkJlZm9yZSB3ZSBzdGFydCBwbG90dGluZyB0aGUgUk5BLXNlcSBkYXRhIHdlJ2xsIGRlbW9uc3RyYXRlIGhvdyBnZ3Bsb3Qgd29ya3MgdXNpbmcgb25lIG9mIHRoZSBidWlsdC1pbiBkYXRhc2V0cyBpbiBSLiBZb3UgY2FuIHNlZSB3aGF0IGJ1aWx0LWluIGRhdGFzZXRzIHRoZXJlIGFyZSBieSB0eXBpbmcgYGRhdGEoKWAuIFdlIHdpbGwgdXNlIHRoZSBkYXRhc2V0IGNhbGxlZCBgd29tZW5gIHRoYXQgY29udGFpbnMgdGhlIGF2ZXJhZ2UgaGVpZ2h0cyBhbmQgd2VpZ2h0cyBmb3IgQW1lcmljYW4gd29tZW4uCgpGaXJzdCB0YWtlIGEgbG9vayBhdCB0aGUgZGF0YXNldC4KCmBgYHtyfQp3b21lbgpgYGAKCkxldCdzIG1ha2UgYSBzY2F0dGVycGxvdC4gV2UgZ2l2ZSBgZ2dwbG90KClgIHRoZSBkYXRhc2V0IChgd29tZW5gKSBhbmQgaW4gYGFlcygpYCB3ZSBzYXkgd2hpY2ggY29sdW1ucyBhcmUgdGhlIHggYW5kIHkgYXhpcyB2YWx1ZXMuIFRvIHNheSB3aGF0IHR5cGUgb2YgcGxvdCB3ZSB3YW50IHdlIGFkZCBhIGdlb20gdXNpbmcgdGhlIGArYCBvcGVyYXRvci4gVGhpcyBtdXN0IGdvIGF0IHRoZSBlbmQgb2YgdGhlIGxpbmUsIGFuZCBub3QgYXQgdGhlIGJlZ2lubmluZyBvciBnZ3Bsb3QyIHdpbGwgZ2l2ZSBhbiBlcnJvci4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT13b21lbiwgbWFwcGluZz1hZXMoeD1oZWlnaHQsIHk9d2VpZ2h0KSkgKyAKICBnZW9tX3BvaW50KCkKYGBgCgpXZSBoYXZlIGdlbmVyYXRlZCBvdXIgZmlyc3QgcGxvdCEKCk5vdyB3ZSB3aWxsIGV4cGxvcmUgdGhlIFJOQS1TZXEgZGF0YSB1c2luZyBkaWZmZXJlbnQgdHlwZXMgb2YgcGxvdHMuCgojIyBCYXIgcGxvdHMKCkhvdyBtYW55IGNvdW50cyBkbyB3ZSBoYXZlIGZvciBlYWNoIHNhbXBsZT8KICAKV2UgY2FuIG1ha2UgYmFyIHBsb3RzIHRvIHZpc3VhbGlzZSB0aGlzLiBUbyBkbyB0aGlzIHdlIHdpbGwgdXNlIGBnZW9tX2JhcigpYC4KCllvdSBtaWdodCB0aGluayB3ZSBzcGVjaWZ5IHRoZSBgeD1gIGFuZCBgeT1gIHZhbHVlcyBhcyBiZWZvcmUgd2l0aCBgZ2VvbV9wb2ludCgpYCBidXQgbG9vayB3aGF0IGhhcHBlbnMuCgpgYGB7ciwgZXJyb3I9VFJVRX0KZ2dwbG90KGRhdGE9Y291bnRzLCBtYXBwaW5nPWFlcyh4PVNhbXBsZSwgeT1Db3VudHMpKSArCiAgZ2VvbV9iYXIoKQpgYGAKCldlIGdldCBhbiBlcnJvciBgRXJyb3I6IHN0YXRfY291bnQoKSBtdXN0IG5vdCBiZSB1c2VkIHdpdGggYSB5IGFlc3RoZXRpYy5gCgpXZSBjYW4gdXNlIHRoZSBiaW9pbmZvcm1hdGljaWFuJ3MgZ29vZCBmcmllbmQgR29vZ2xlIHRvIHRyeSB0byBmaWd1cmUgb3V0IHdoeS4gVGhyb3VnaCBHb29nbGUgd2UgY2FuIGZpbmQgYW4gYW5zd2VyIG9uIFN0YWNrIE92ZXJmbG93IChTdGFjayBPdmVyZmxvdyBpcyBhbm90aGVyIGdvb2QgZnJpZW5kIG9mIGJpb2luZm9ybWF0aWNpYW5zKSBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8zOTY3OTA1Ny9yLWdncGxvdDItc3RhdC1jb3VudC1tdXN0LW5vdC1iZS11c2VkLXdpdGgtYS15LWFlc3RoZXRpYy1lcnJvci1pbi1iYXItZ3JhcGgvMzk2NzkxMDQuIEFzIHBvaW50ZWQgb3V0IGluIHRoYXQgcG9zdCwgd2UgY291bGQgc3BlY2lmeSBgeT1Db3VudHNgIGlmIHdlIGhhZCBhbHJlYWR5IGNhbGN1bGF0ZWQgdGhlIHZhbHVlIHdlIHdhbnRlZCB0byBwbG90ICh0aGUgdG90YWwgY291bnRzIGZvciBlYWNoIHNhbXBsZSkuIEhvd2V2ZXIsIG91ciBpbnB1dCBkYXRhIGNvbnRhaW5zIHRoZSBjb3VudHMgZm9yICplYWNoIGdlbmUqIGZvciBlYWNoIHNhbXBsZS4gV2Ugd2FudCB0byAqKnN1bSoqIHRoZXNlIGdlbmUgY291bnRzIGZvciBlYWNoIHNhbXBsZS4gTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGhlbHAgZm9yIGBnZW9tX2JhcigpYCAoYXMgc2hvd24gaW4gdGhhdCBTdGFjayBPdmVyZmxvdyBwb3N0KS4KCmBgYHtyIGV2YWw9RkFMU0V9Cj9nZW9tX2JhcgpgYGAKCkl0IHNheXMgImdlb21fYmFyKCkgbWFrZXMgdGhlIGhlaWdodCBvZiB0aGUgYmFyIHByb3BvcnRpb25hbCB0byB0aGUgbnVtYmVyIG9mIGNhc2VzIGluIGVhY2ggZ3JvdXAgKG9yIGlmIHRoZSB3ZWlnaHQgYWVzdGhldGljIGlzIHN1cHBsaWVkLCB0aGUgc3VtIG9mIHRoZSB3ZWlnaHRzKS4iIFNvIHdlIGNhbiBzcGVjaWZ5IGB4PVNhbXBsZWAgYW5kIGB3ZWlnaHQ9Q291bnRzYCBhbmQgZ2dwbG90IHdpbGwgYXV0b21hdGljYWxseSBzdW0gdGhlIGNvdW50cyBmb3IgZWFjaCBzYW1wbGUgZm9yIHVzLgoKYGBge3J9CmdncGxvdChkYXRhPWNvdW50cywgbWFwcGluZz1hZXMoeD1TYW1wbGUsIHdlaWdodD1Db3VudHMpKSArCiAgZ2VvbV9iYXIoKSAKYGBgCgpUaGlzIHNob3dzIHVzIHdlIGhhdmUgMjAtMjUgbWlsbGlvbiBjb3VudHMgcGVyIHNhbXBsZSwgbm9uZSBhcmUgdmVyeSBkaWZmZXJlbnQuCgpXaGF0IGlmIHdlIHdvdWxkIGxpa2UgdG8gYWRkIHNvbWUgY29sb3VyIHRvIHRoZSBwbG90LCBmb3IgZXhhbXBsZSwgYSBkaWZmZXJlbnQgY29sb3VyIGJhciBmb3IgZWFjaCBzYW1wbGUuIAoKSWYgd2UgbG9vayBhdCB0aGUgYGdlb21fYmFyYCBoZWxwIGFnYWluIHdlIGNhbiBzZWUgdW5kZXIgdGhlIGhlYWRpbmcgY2FsbGVkICJBZXN0aGV0aWNzIiB0aGF0IHRoZXJlJ3MgYW4gb3B0aW9uIGZvciBjb2xvdXIuIExldCdzIHRyeSBhZGRpbmcgdGhhdCB0byBvdXIgcGxvdC4gV2UnbGwgc3BlY2lmeSB3ZSB3YW50IHRvIG1hcCB0aGUgU2FtcGxlIGNvbHVtbiB0byBgY29sb3VyPWAuIEFzIHdlIGFyZSBtYXBwaW5nIGNvbG91ciB0byBhIGNvbHVtbiBpbiBvdXIgZGF0YSB3ZSBuZWVkIHRvIHB1dCB0aGlzIGluc2lkZSB0aGUgYGFlcygpYC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1jb3VudHMsIG1hcHBpbmc9YWVzKHg9U2FtcGxlLCB3ZWlnaHQ9Q291bnRzLCBjb2xvdXI9U2FtcGxlKSkgKwogIGdlb21fYmFyKCkgCmBgYAoKSG1tIGNvbG91cmluZyB0aGUgZWRnZXMgd2FzbuKAmXQgcXVpdGUgd2hhdCB3ZSBoYWQgaW4gbWluZC4gTG9vayBhdCB0aGUgaGVscCBmb3IgYGdlb21fYmFyYCB0byBzZWUgd2hhdCBvdGhlciBhZXN0aGV0aWMgd2UgY291bGQgdXNlLiBMZXQncyB0cnkgYGZpbGw9YCBpbnN0ZWFkLgoKYGBge3J9CmdncGxvdChkYXRhPWNvdW50cywgbWFwcGluZz1hZXMoeD1TYW1wbGUsIHdlaWdodD1Db3VudHMsIGZpbGw9U2FtcGxlKSkgKwogIGdlb21fYmFyKCkgCmBgYAoKVGhhdCBsb29rcyBiZXR0ZXIuIGBmaWxsPWAgaXMgdXNlZCB0byAqKmZpbGwqKiBpbiBhcmVhcyBpbiBnZ3Bsb3QyIHBsb3RzLCB3aGVyZWFzIGBjb2xvdXI9YCBpcyB1c2VkIHRvIGNvbG91ciBsaW5lcyBhbmQgcG9pbnRzLgoKQSByZWFsbHkgbmljZSBmZWF0dXJlIGFib3V0IGdncGxvdCBpcyB0aGF0IHdlIGNhbiBlYXNpbHkgY29sb3VyIGJ5IGFub3RoZXIgdmFyaWFibGUgZS5nLiBjZWxsIHR5cGUgKGJhc2FsIHZzIGx1bWluYWwpIGJ5IHNpbXBseSBjaGFuZ2luZyB0aGUgY29sdW1uIHdlIGdpdmUgdG8gYGZpbGw9YC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1jb3VudHMsIG1hcHBpbmc9YWVzKHg9U2FtcGxlLCB3ZWlnaHQ9Q291bnRzLCBmaWxsPUNlbGxUeXBlKSkgKwogIGdlb21fYmFyKCkgCmBgYAoKT3Igd2UgY2FuIGNvbG91ciBieSBzdGF0dXMgKHZpcmdpbiB2cyBwcmVnbmFudCB2cyBsYWN0YXRpbmcpLgoKYGBge3J9CmdncGxvdChkYXRhPWNvdW50cywgbWFwcGluZz1hZXMoeD1TYW1wbGUsIHdlaWdodD1Db3VudHMsIGZpbGw9U3RhdHVzKSkgKwogIGdlb21fYmFyKCkgCmBgYAoKRm9yIG1vcmUgZGV0YWlscyBvbiBiYXIgcGxvdHMgc2VlIHRoaXMgW1IgYmxvZ2dlcnMgcG9zdF0oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vZGV0YWlsZWQtZ3VpZGUtdG8tdGhlLWJhci1jaGFydC1pbi1yLXdpdGgtZ2dwbG90LykuIFIgYmxvZ2dlcnMgaXMgYSB1c2VmdWwgcmVzb3VyY2UgZm9yIFIgbmV3cyBhbmQgdHV0b3JpYWxzLCB5b3UgY2FuIHN1YnNjcmliZSB0byB0aGVpciBtYWlsaW5nIGxpc3QgZm9yIGZyZWUuCgojIyBCb3ggcGxvdHMKCkluIGFkZGl0aW9uIHRvIHZpZXdpbmcgdGhlIG51bWJlciBvZiBjb3VudHMgcGVyIHNhbXBsZSBpdCBpcyBhbHNvIHVzZWZ1bCB0byB2aXN1YWxpc2UgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgY291bnRzLiBUaGlzIGhlbHBzIHVzIHRvIGNvbXBhcmUgdGhlIHNhbXBsZXMgYW5kIGNoZWNrIGlmIGFueSBsb29rIHVudXN1YWwuIFdlIGNhbiBtYWtlIGJveCBwbG90cyB0byB2aXN1YWxpc2UgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjb3VudHMgZm9yIGVhY2ggc2FtcGxlLgoKTGV0J3MgdHJ5IHRoYXQuCgpgYGB7cn0KZ2dwbG90KGRhdGE9Y291bnRzLCBtYXBwaW5nPWFlcyh4PVNhbXBsZSwgeT1Db3VudHMpKSArCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgpUaGF0IGxvb2tzIHdlaXJkLiBJdCdzIGJlY2F1c2Ugd2UgaGF2ZSBzb21lIGdlbmVzIHdpdGggZXh0cmVtZWx5IGhpZ2ggY291bnRzLiBUbyBtYWtlIGl0IGVhc2llciB0byB2aXN1YWxpc2UgdGhlIGRpc3RyaWJ1dGlvbnMgd2UgdXN1YWxseSBwbG90IHRoZSBsb2dhcml0aG0gb2YgUk5BLXNlcSBjb3VudHMuIFdlJ2xsIHBsb3QgdGhlIFNhbXBsZSBvbiB0aGUgWCBheGlzIGFuZCBsb2cyIENvdW50cyBvbiB0aGUgeSBheGlzLiBXZSBjYW4gbG9nIHRoZSBDb3VudHMgd2l0aGluIHRoZSBgYWVzKClgLgoKYGBge3J9CmdncGxvdChkYXRhPWNvdW50cywgbWFwcGluZz1hZXMoeD1TYW1wbGUsIHk9bG9nMihDb3VudHMpKSkgKwogIGdlb21fYm94cGxvdCgpCmBgYAoKTm90ZSB0aGF0IHdlIGdldCBhIHdhcm5pbmcgaGVyZSBhYm91dCByb3dzIGNvbnRhaW5pbmcgbm9uLWZpbml0ZSB2YWx1ZXMgYmVpbmcgcmVtb3ZlZC4gVGhpcyBpcyBiZWNhdXNlIHNvbWUgb2YgdGhlIGdlbmVzIGhhdmUgYSBjb3VudCBvZiB6ZXJvIGluIHRoZSBzYW1wbGVzIGFuZCBhIGxvZyBvZiB6ZXJvIGlzIHVuZGVmaW5lZC4gVXN1YWxseSB3ZSB3b3VsZCBmaWx0ZXIgbG93bHkgZXhwcmVzc2VkIGdlbmVzIGJlZm9yZSBjcmVhdGluZyB0aGUgYm94IHBsb3RzIHNvIHdlIG1heSBub3QgYWx3YXlzIGdldCBhIHdhcm5pbmcgbGlrZSB0aGlzLiBCdXQgaGVyZSB3ZSBhcmUganVzdCBkZW1vbnN0cmF0aW5nIHRoZSBkaWZmZXJlbnQgdHlwZXMgb2YgcGxvdHMgdGhhdCBjYW4gYmUgbWFkZS4KClRoZSBib3ggcGxvdHMgc2hvdyB0aGF0IHRoZSBkaXN0cmlidXRpb25zIG9mIHRoZSBzYW1wbGVzIGFyZSBub3QgaWRlbnRpY2FsIGJ1dCB0aGV5IGFyZSBub3QgdmVyeSBkaWZmZXJlbnQuCgojIyMjIEV4ZXJjaXNlCgpDb2xvdXIgdGhlIGJveCBwbG90cyBieSBTYW1wbGUsIHRoZW4gQ2VsbFR5cGUsIHRoZW4gU3RhdHVzLgoKCiMjIFZpb2xpbiBwbG90cwoKQm94IHBsb3RzIGFyZSB1c2VmdWwgc3VtbWFyaWVzLCBidXQgaGlkZSB0aGUgKnNoYXBlKiBvZiB0aGUgZGlzdHJpYnV0aW9uLiBGb3IgZXhhbXBsZSwgaWYgdGhlIGRpc3RyaWJ1dGlvbiBpcyBiaW1vZGFsLCB3ZSB3b3VsZCBub3Qgc2VlIGl0IGluIGEgYm94cGxvdC4gQW4gYWx0ZXJuYXRpdmUgdG8gdGhlIGJveHBsb3QgaXMgdGhlIHZpb2xpbiBwbG90LCB3aGVyZSB0aGUgc2hhcGUgKG9mIHRoZSBkZW5zaXR5IG9mIHBvaW50cykgaXMgZHJhd24uIFNlZSBbaGVyZV0oaHR0cHM6Ly9ibG9nLmJpb3R1cmluZy5jb20vMjAxOC8wNS8xNi81LXJlYXNvbnMteW91LXNob3VsZC11c2UtYS12aW9saW4tZ3JhcGgvKSBmb3IgYW4gZXhhbXBsZSBvZiBob3cgZGlmZmVyZW5jZXMgaW4gZGlzdHJpYnV0aW9uIG1heSBiZSBoaWRkZW4gaW4gYm94IHBsb3RzIGJ1dCByZXZlYWxlZCB3aXRoIHZpb2xpbiBwbG90cy4KCiMjIyMgRXhlcmNpc2UKCk1ha2UgdmlvbGluIHBsb3RzIGluc3RlYWQgb2YgYm94IHBsb3RzIHVzaW5nIGBnZW9tX3Zpb2xpbigpYCBhbmQgY29sb3VyIGJ5IFNhbXBsZSwgdGhlbiBDZWxsVHlwZSwgdGhlbiBTdGF0dXMuCkhvdyBkbyB0aGUgdmlvbGluIHBsb3RzIGZvciB0aGUgc2FtcGxlcyBjb21wYXJlPwoKCiMjIERlbnNpdHkgcGxvdHMKCldlIGNhbiBhbHNvIHZpc3VhbGlzZSBkaXN0cmlidXRpb25zIHdpdGggZGVuc2l0eSBwbG90cy4gSW4gdGhpcyBjYXNlIHdlIHNwZWNpZnkgYHg9bG9nMihDb3VudHMpYCAobm8gbmVlZCB0byBzcGVjaWZ5IGB5PWApIGFuZCBgZ2VvbV9kZW5zaXR5KClgIHdpbGwgY3JlYXRlIGEgZGVuc2l0eSBwbG90IG9mIHRoZSBjb3VudHMuCgpgYGB7cn0KZ2dwbG90KGRhdGE9Y291bnRzLCBtYXBwaW5nPWFlcyh4PWxvZzIoQ291bnRzKSkpICsKICBnZW9tX2RlbnNpdHkoKQpgYGAKCldlIGNhbiBhZGQgYSBzbWFsbCBudW1iZXIgdG8gZXZlcnkgY291bnQgKHdlJ2xsIGNob29zZSAwLjI1KSBpbiBvcmRlciB0byB2aXN1YWxpc2UgdGhlIGFtb3VudCBvZiBnZW5lcyB3aXRoIGNvdW50cyBvZiB6ZXJvIGluIHRoaXMgcGxvdC4KCgpgYGB7cn0KZ2dwbG90KGRhdGE9Y291bnRzLCBtYXBwaW5nPWFlcyh4PWxvZzIoQ291bnRzKzAuMjUpKSkgKwogIGdlb21fZGVuc2l0eSgpCmBgYAoKTm90ZSB0aGUgbGFyZ2UgcGVhayB0aGF0IGFwcGVhcnMgb24gdGhlIGxlZnQuIFRoaXMgaXMgdGhlIGxhcmdlIG51bWJlciBvZiBnZW5lcyB3aXRoIHplcm8gY291bnRzLiBJbiBhbiBSTkEtc2VxIGFuYWx5c2lzIGZpbHRlcmluZyBsb3dseSBleHByZXNzZWQgZ2VuZXMgd291bGQgcmVtb3ZlIG9yIHJlZHVjZSB0aGF0IHBlYWsuCgpXZSBjYW4gY29sb3VyIGJ5IHNhbXBsZSB1c2luZyBgZmlsbD1gLgoKYGBge3J9CmdncGxvdChkYXRhPWNvdW50cywgbWFwcGluZz1hZXMoeD1sb2cyKENvdW50cyswLjI1KSwgZmlsbD1TYW1wbGUpKSArCiAgZ2VvbV9kZW5zaXR5KCkgCmBgYAoKSWYgd2Ugd291bGQgbGlrZSB0byBjb2xvdXIgdGhlIGxpbmVzIGluIHRoZSBwbG90IGluc3RlYWQgb2YgdGhlIGFyZWFzIHdlIGNhbiB1c2UgYGNvbG91cj1gIGluc3RlYWQgb2YgYGZpbGw9YC4gCgpgYGB7cn0KZ2dwbG90KGRhdGE9Y291bnRzLCBtYXBwaW5nPWFlcyh4PWxvZzIoQ291bnRzKzAuMjUpLCBjb2xvdXI9U2FtcGxlKSkgKwogIGdlb21fZGVuc2l0eSgpIApgYGAKCldoeSBpcyB0aGVyZSBhIGxpbmUgYWNyb3NzIHRoZSBib3R0b20gb2YgdGhlIHBsb3Q/IAoKV2UgY2FuIHVzZSBHb29nbGUgYW5kIFN0YWNrIE92ZXJmbG93IGFnYWluIHRvIGZpbmQgb3V0IHdoeTogaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNDk1OTM2MzQvdW5pbnRlbmRlZC1saW5lLWFjcm9zcy14LWF4aXMtb2YtZGVuc2l0eS1wbG90LXIuIEl0IGlzIGJlY2F1c2UgYGdlb21fZGVuc2l0eSgpYCBwbG90cyBhIHBvbHlnb24gc2hhcGUuIElmIHdlIGRvbid0IHdhbnQgdGhhdCBsaW5lIHdlIGNhbiBhZGFwdCB0aGUgY29kZSBnaXZlbiBpbiB0aGF0IFN0YWNrIE92ZXJmbG93IHBvc3QsIHVzaW5nIGBnZW9tX2xpbmUoc3RhdD0iZGVuc2l0eSIpYC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1jb3VudHMsIG1hcHBpbmc9YWVzKHg9bG9nMihDb3VudHMrMC4yNSksIGNvbG91cj1TYW1wbGUpKSArCiAgZ2VvbV9saW5lKHN0YXQ9ImRlbnNpdHkiKQpgYGAKCj4gIyMjIyBTdGF0aXN0aWNhbCB0cmFuc2Zvcm1hdGlvbnMKPiBNYW55IGdyYXBocywgbGlrZSBzY2F0dGVycGxvdHMgYW5kIGxpbmUgcGxvdHMsIHBsb3QgdGhlIHJhdyB2YWx1ZXMgZnJvbSB5b3VyIGRhdGFzZXQ7IG90aGVyIGdyYXBocywgbGlrZSBkZW5zaXR5IHBsb3RzIGFuZCBiYXIgcGxvdHMsIGNhbGN1bGF0ZSBuZXcgdmFsdWVzIHRvIHBsb3QuCj4KPiAqIGJhciBwbG90cyBhbmQgaGlzdG9ncmFtcyAoZ2VvbV9iYXIsIGdlb21faGlzdG9ncmFtLCBnZW9tX2ZyZXFwb2x5KSBiaW4geW91ciBkYXRhIGFuZCB0aGVuIHBsb3QgYmluIGNvdW50cwo+ICogZGVuc2l0eSBwbG90cyAoZ2VvbV9kZW5zaXR5KSBjb21wdXRlIGFuZCBkcmF3IGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlLCB3aGljaCBpcyBhIHNtb290aGVkIHZlcnNpb24gb2YgdGhlIGhpc3RvZ3JhbQo+ICogc21vb3RoaW5nIGZ1bmN0aW9ucyAoZ2VvbV9zbW9vdGgpIGZpdCBhIG1vZGVsIHRvIHlvdXIgZGF0YSBhbmQgdGhlbiBwbG90IHByZWRpY3Rpb25zIGZyb20gdGhlIG1vZGVsCj4gKiBib3ggcGxvdHMgKGdlb21fYm94cGxvdCkgY29tcHV0ZSBhIHJvYnVzdCBzdW1tYXJ5IG9mIHRoZSBkaXN0cmlidXRpb24gYW5kIGRpc3BsYXkgYSBzcGVjaWFsbHkgZm9ybWF0dGVkIGJveAo+Cj4gVGhlIGFsZ29yaXRobSB1c2VkIHRvIGNhbGN1bGF0ZSBuZXcgdmFsdWVzIGZvciBhIGdyYXBoIGlzIGNhbGxlZCBhIHN0YXQKCgojIyBTYXZpbmcgcGxvdHMKCldlIGNhbiBvdXRwdXQgcGxvdHMgdG8gcGRmIHVzaW5nIGBwZGYoKWAgZm9sbG93ZWQgYnkgYGRldi5vZmYoKWAuIFdlIHB1dCBvdXIgcGxvdCBjb2RlIGFmdGVyIHRoZSBjYWxsIHRvIGBwZGYoKWAgYW5kIGJlZm9yZSBjbG9zaW5nIHRoZSBwbG90IGRldmljZSB3aXRoIGBkZXYub2ZmKClgLgoKTGV0J3Mgc2F2ZSBvdXIgbGFzdCBwbG90LgoKYGBge3IsIGV2YWw9RkFMU0V9CnBkZigibXlwbG90LnBkZiIpCmdncGxvdChkYXRhPWNvdW50cywgbWFwcGluZz1hZXMoeD1sb2cyKENvdW50cyswLjI1KSwgY29sb3VyPVNhbXBsZSkpICsKICBnZW9tX2xpbmUoc3RhdD0iZGVuc2l0eSIpCmRldi5vZmYoKQpgYGAKCiMjIE9uZSBsYXN0IHRoaW5nCkl0IGlzIHJlY29tbWVuZGVkIHRvIHVzZSBiYXIgcGxvdHMgb25seSBmb3IgdmlzdWFsaXNpbmcgKnNpbmdsZSB2YWx1ZXMqLCBmb3IgZXhhbXBsZSwgdGhlIHRvdGFsIGNvdW50cyBwZXIgc2FtcGxlIGhlcmUuIER5bmFtaXRlIHBsb3RzLCBhIHR5cGUgb2YgYmFyIHBsb3QgY29tbW9ubHkgdXNlZCBieSBzY2llbnRpc3RzIGFuZCBpbiBqb3VybmFscywgYXJlIG5vdCByZWNvbW1lbmRlZCwgc2VlIHRoaXMgcG9zdCBjYWxsZWQgWyJEeW5hbWl0ZSBwbG90cyBtdXN0IGRpZSJdKGh0dHBzOi8vc2ltcGx5c3RhdGlzdGljcy5vcmcvMjAxOS8wMi8yMS9keW5hbWl0ZS1wbG90cy1tdXN0LWRpZS8pIGZvciBhbiBleHBsYW5hdGlvbi4gQm94IHBsb3RzLCB2aW9saW4gcGxvdHMgYW5kIGRlbnNpdHkgcGxvdHMgY2FuIGJlIGEgYmV0dGVyIHdheSB0byB2aXN1YWxpc2UgZGlzdHJpYnV0aW9ucyBvZiBkYXRhLgoKIyMjIyBFeGVyY2lzZQoqIFJlYWQgaW4gdGhlIG5vcm1hbGlzZWQgY291bnRzIChmcm9tICJkYXRhL25vcm1jb3VudHMudHN2Lmd6IikuIFRoaXMgaXMgdGhlIGBjb3VudHNgIGRhdGEgYWZ0ZXIgZmlsdGVyaW5nIGxvd2x5IGV4cHJlc3NlZCBnZW5lcyBhbmQgbm9ybWFsaXNpbmcgZm9yIGRpZmZlcmVuY2VzIGluIHNlcXVlbmNpbmcgZGVwdGggYW5kIGNvbXBvc2l0aW9uIGJpYXMuCiogSG93IG1hbnkgcm93cyBhbmQgY29sdW1ucyBhcmUgaW4gdGhlIGZpbGU/CiogV2hhdCBhcmUgdGhlIGNvbHVtbiBuYW1lcz8KKiBJbiB0aGUgZmlyc3Qgcm93LCB3aGF0IGlzIHRoZSBzYW1wbGUgaWQsIHRoZSBnZW5lIHN5bWJvbCBhbmQgdGhlIHZhbHVlIGZvciB0aGUgY291bnRzPwoqIEluIHRoZSBsYXN0IHJvdywgd2hhdCBpcyB0aGUgc2FtcGxlIGlkLCB0aGUgZ2VuZSBzeW1ib2wgYW5kIHRoZSB2YWx1ZSBmb3IgdGhlIGNvdW50cz8KKiBXaGF0IGFyZSB0aGUgbWluaW11bSwgbWF4aW11bSwgbWVhbiBhbmQgbWVkaWFuIHZhbHVlcyBpbiB0aGUgTm9ybV9jb3VudHMgY29sdW1uPwoqIE1ha2UgYXMgbWFueSBwbG90cyBhcyB5b3UgY2FuIGZyb20gYmFyIHBsb3RzLCBib3ggcGxvdHMsIHZpb2xpbiBwbG90cyBhbmQgZGVuc2l0eSBwbG90cyB1c2luZyB0aGUgbm9ybWFsaXNlZCBjb3VudHMgKHNvbWUgc3RhcnRlciBjb2RlIGlzIGJlbG93KQoqIE1ha2Ugc2VwYXJhdGUgcGxvdHMgY29sb3VyZWQgYnkgU2FtcGxlLCBDZWxsVHlwZSwgU3RhdHVzLCB3aGVyZSBhcHByb3ByaWF0ZS4KKiBEbyB5b3Ugbm90aWNlIGFueSBkaWZmZXJlbmNlcyB3aXRoIHRoZSBub3JtYWxpc2VkIGNvdW50cyB2ZXJzdXMgdGhlIHVubm9ybWFsaXNlZCBjb3VudHMgdGhhdCB3ZSB1c2VkIHByZXZpb3VzbHkgKGBjb3VudHNgIGRhdGFzZXQpPwoKICAKYGBge3IsIGV2YWw9RkFMU0V9Cm5vcm1fY291bnRzIDwtIHJlYWRfdHN2KCkKYGBgCgoKYGBge3IsIGV2YWw9RkFMU0V9CmdncGxvdChkYXRhPSAsIG1hcHBpbmc9YWVzKHg9ICwgd2VpZ2h0PSAsIGZpbGw9ICkpICsKICBnZW9tX2JhcigpIApgYGAKIAoKYGBge3IsIGV2YWw9RkFMU0V9CmdncGxvdChkYXRhPSAsIG1hcHBpbmc9YWVzKHg9ICwgeT0gLCBmaWxsPSApKSArCiAgZ2VvbV9ib3hwbG90KCkgCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQpnZ3Bsb3QoZGF0YT0gLCBtYXBwaW5nPWFlcyh4PSAsIHk9ICwgZmlsbD0gKSkgKwogIGdlb21fdmlvbGluKCkKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KZ2dwbG90KGRhdGE9ICwgbWFwcGluZz1hZXMoeD0gLCBjb2xvdXI9ICkpICsKICBnZW9tX2RlbnNpdHkoKSAKYGBgCgoKIyMgS2V5IFBvaW50cwotIFdlIHVzZSB0aGUgYGxpYnJhcnkoKWAgZnVuY3Rpb24gdG8gbG9hZCBwYWNrYWdlcyB3ZSB3YW50IHRvIHVzZS4gTm90ZSB0aGF0IHRoZXkgbmVlZCB0byBiZSBhbHJlYWR5IGluc3RhbGxlZCB3aXRoIGUuZy4gYGluc3RhbGwucGFja2FnZXMoKWAuIAotIFdlIGNhbiByZWFkIGluIHRhYi1zZXBhcmF0ZWQgZmlsZXMgd2l0aCBgcmVhZF90c3YoKWAuCi0gV2UgY2FuIGNoZWNrIG91ciBkYXRhIHdpdGggdGhlIGZ1bmN0aW9ucyBgZGltKClgLCBgaGVhZCgpYCwgYHRhaWwoKWAsIGBWaWV3KClgLCBgc3RyKClgIGFuZCBgc3VtbWFyeSgpYC4KLSAqKmBnZ3Bsb3QyYCoqIGlzIGEgcGxvdHRpbmcgcGFja2FnZSB0aGF0IG1ha2VzIGl0IHNpbXBsZSB0byBjcmVhdGUgY29tcGxleCBwbG90cy4KLSBnZ3Bsb3QyIHBsb3RzIGhhdmUgMyBjb21wb25lbnRzOiBkYXRhIChkYXRhc2V0KSwgbWFwcGluZyAoY29sdW1ucyB0byBwbG90KSBhbmQgZ2VvbSAodHlwZSBvZiBwbG90KS4KLSBUaGUgYCtgIHNpZ24gaXMgdXNlZCB0byBhZGQgZ2VvbXMgKGFuZCBuZXcgbGF5ZXJzIGFzIHdlIHdpbGwgc2VlIGxhdGVyIGluIHRoZSBjb3Vyc2UpLiBJdCBtdXN0IGJlIHBsYWNlZCBhdCB0aGUgZW5kIG9mIHRoZSBwcmVjZWRpbmcgbGluZS4gSWYgaXQgaXMgYWRkZWQgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgbGluZSwgKipgZ2dwbG90MmAqKiB3aWxsIHJldHVybiBhbiBlcnJvciBtZXNzYWdlLgotIGdncGxvdDIgcGxvdHMgY2FuIGJlIGVhc2lseSBjb2xvdXJlZCBieSBjb2x1bW5zIGluIHRoZSBkYXRhc2V0LiBgZmlsbD1gIGNhbiBiZSB1c2VkIHRvIGNvbG91ciBhcmVhcyBhbmQgYGNvbG91cj1gIHRvIGNvbG91ciBsaW5lcy4KLSBBIHBkZiBvZiB0aGUgcGxvdCBjYW4gYmUgZ2VuZXJhdGVkIHdpdGggYHBkZigpYCBhbmQgYGRldi5vZmYoKWAK