Solutions to this workshop can be found here

Review

# use ggplot to make a histogram of iris sepal width across all species

# use ggplot to make a plot of iris petal length vs sepal length, colored by
# Species

# repeat the scatterplot above, but have the species label appear on every point

Subsetting data

One of the most powerful things R can do easily and quickly is to select part of your data based on some criteria you provide using the subset() function. This can be immensely useful. Let’s say we’re interested in iris data, but don’t really care about the Iris setosa plants. Let’s create a subset of our data that excludes those plants. To set the conditions for subsetting, you will have to remember something from our lesson on logicals.

# create a subset with data on all species *except* setosa
iris_no_setosa <- subset(iris, Species != 'setosa')

Look at this new dataframe. Notice that in the code above, subset (like ggplot) knows what we mean by Species; because we already tell is which dataframe to work with, it figures that Species is just one of the columns in this dataframe. Recall that != means ‘not equal to’.

Try this on your own. Create a data frame of all the plants where sepal width is greater than 3.5.

# create a subset of the data, iris_wide_sepal, that includes all plants for
# which sepal width is greater than 3.5

# look at the data frame. what do you notice about these plants (esp their species)?

sidenote: %in%

A really useful logical operator in R is %in%; it checks that the thing before it is a member of the list of things after it.

# TRUE statement
'a' %in% c('a', 'b', 'c')

# FALSE statement
'e' %in% c('a', 'b', 'c')

%in% can be incredibly useful in subsetting.

# another way to subset data with non-setosa species
iris_no_setosa_2 <- subset(iris, Species %in% c('versicolor', 'virginica'))

Getting useful info about your data

It can be incredibly helpful to quickly get summary info for your data in an accessible, R-readable format. We’ve already calculated some ‘summary statistics’ (mean) for every column, and we saw how ggplot easily lets us split our plot up by categories (eg through using different colors for each one). Now let’s do the same with summary statistics.

Using the summary function

A really useful function for quickly learning about the properties of your data is summary(). It shows you the mean, median, and quantiles of your numeric data, as well as counts for each data category. Try this:

iris_summary <- summary(iris)
print(iris_summary)

One reason summary() is often not particularly useful is that it summarizes all the data in your dataframe. Notice that it’s not splitting the summaries by species, which is probably what we would want to happen.

One way to get around this is through subsetting. Make a subset of the iris data that only contains data for the setosa species, and then run the summary function on this subset.

# use subset() to make a dataframe, setosa_df, that contains only the data where
# Species is setosa
# (hint: you want the Species column to equal 'setosa')

# pass the summary data for setosa_df into a new variable called setosa_summary

# print out setosa_summary

There are a couple of important limitations to the approach we just used. First of all, we have to do this to one category at a time. This may not be so bad for 3 species, but imagine we had a dataset containing 50 species (or other categories we were interested in). Second, if you look in your Environment window, you’ll notice that neither iris_summary or setosa_summary are dataframes. The summary function produces a weird R data type called a table, which is easy to look at but a pain to do anything with.

View(iris_summary)

For example, if you wanted to plot the results of the summary functions for a bunch of species using ggplot, it would be very annoying to do.

Summarizing data with plyr

The problem

Ideally, we want to summarize complicated data in a compact, readable, graphable way, probably as a dataframe. We also don’t want to have to subset the data individually for every member of every category we want to split by in our analysis (e.g. subset for every iris species and then find the mean of some column); this may be simply annoying for 3 irises, but becomes impossible for many datasets (e.g. imagine trying to get the mean and standard deviation of the expression level of every gene in a single-cell RNAseq experiment).

One of the things that made ggplot so helpful was its ability to automatically split data by category. We don’t have to say “Plot this species in red, this other one in blue, etc…” We just specify the columns of the dataframe that we want the data grouped by, and R does the rest. If any stats need to be calculated (e.g. in geom_smooth or geom_boxplot), they are calculated within the separate groups specified by the columns that we want to categorize data by.

Although R has built-in functions that do this, we’re going to turn to yet another package, plyr, which makes this process simpler.

# uncomment the line below and install plyr if you haven't already done so
#install.packages('plyr')

# load the plyr library into the current R session
library(plyr)

split-apply-combine and basic plyr functionality

The basic philosophy behind plyr is the idea that for many problems, the solution falls into the pattern of split-apply-combine: you want to split the data based on some categories, apply a function (usually calculating some kind of summary statistic) to each resulting dataset, and combine the results back together.

There are various ways to use plyr’s __ply functions, and the built-in documentation doesn’t offer much help here. If you’re interested in diving deeper into this, I can’t recommend this tutorial strongly enough: plyr class from Jenny Bryan’s UBC’s STAT545 course

The application I’m showing you here is the one that makes the most sense to me; it looks something like this:

output_df <-
  ddply(<input_df>,
        ~ <column names to sort by>,
        summarize,
        <output_column> = <output_function(input_column)>)

Here, the ~ represents “as a function of”; this is common to many R functions, as you will see below!

plyr command names are based on the data they take as input and output; there are lots of options here, but I don’t want to go into too many of them. For now, let’s work with the iris data, and try to get a dataframe out containing some summary stats by species. Since we are going from dataframe (d) to dataframe (d), the function we want to use is ddply (if we were using a dataframe as an output and an array as output, we’d use daply, etc).

For example, let’s try to create a dataframe that contains the mean petal length of each iris species:

iris_petal_length_summary <-
  ddply(iris, ~ Species, summarize, mean_Petal_Length = mean(Petal.Length))

Creating multiple columns of summary data

You can use this format to create multiple new columns using multiple functions; just separate these by commas, like so:

iris_petal_length_summary_2 <-
  ddply(iris,
        ~ Species,
        summarize,
        mean_Petal_Length = mean(Petal.Length),
        sd_Petal_Length = sd(Petal.Length))

Our summary is now a dataframe, and we can easily retrieve the mean petal length for any species

Let’s try a challenge:

# Use ddply to create a dataframe, iris_sepal_width_summary, that contains the
# mean and standard error (SE) of each species' sepal width
# SE = standard deviation / sqrt(count - 1)

I think what helps me think about the solution to the above is that, in effect, ddply treats each Species as its own little dataframe, as if you were manually subsetting the iris dataframe by Species one-by-one and calculating the output column values. This is split-apply-combine in action.

Splitting by multiple categories

Another really great thing about summarizing data this way is that we don’t need to limit ourselves to splitting it by a single category. Imagine we had a new dataset that included flower color for each plant, and we were interested in looking at flower color alongside species in our summary statistics.

# create new iris df
color_iris <- iris

# add flower color as alternating 'blue' and 'purple'
color_iris$Flower.Color <- rep(c('blue', 'purple'), times = nrow(iris)/2)

We can now use ddply just as before, but splitting by a list of categories separated by +, rather than by a single category:

iris_petal_length_summary_3 <-
  ddply(color_iris,
        ~ Species + Flower.Color,
        summarize,
        mean_Petal_Length = mean(Petal.Length),
        sd_Petal_Length = sd(Petal.Length))

Doing stats in R

R provides a ton of built-in functions that make simple statistics very easy to calculate. However, the really amazing thing is the huge number of additional packages that allow R users to perform complex statistical analysis on their data. I hope that learning the basics of R will help break down the barriers that prevent many researchers from accessing and performing this more sophisticated statistical analysis. You will likely find if you keep learning R that, past a certain point, you’re learning more about the statistics behind some of these packages than about the programming; that’s exactly the idea. Once you get past the steep learning curve in the beginning, R makes the coding easy, and you’re freed up to think carefully about data analysis without the constraints of whether or not there’s a built-in excel function to perform a specific comparison, test, etc.

We’re going to look at a few built-in R functions for statistical tests that are commonly done in R, but I encourage you to look into some of the resources at the end of this section on your own time to go beyond these.

t-tests

Remember that t-tests compare two groups to determine whether their means are significantly different. As with all statistical tests, there are a ton of assumptions that go into these tests, and you should always learn about these before running the test. Nevertheless, let’s try to perform a t-test to compare Petal Length between two species in our iris data.

Running t-tests on vectors of data

The simplest way to run a t-test in R looks like t.test(<vector_1>, <vector_2>) where vector_1 and vector_2 contain the data we want to compare.

data_1 <- c(1, 2, 4, 6)
data_2 <- c(8, 3, 4, 5)
t.test(data_1, data_2)

Lots of useful info here: we get a p-value (the different between the two datasets is not statistically significant at a p = 0.05 cutoff), the means, the confidence interval for the true difference between the means, etc.

I STRONGLY encourage you to dig into the documentation for this test (and any other statistical test you run in R) before using it. For example, if you run ?t.test, you’ll see that the default for this function is to assume unequal variance between the two samples (this is different from other statistical software). You can also see that you can change lots of parameters, including running a paired t-test, changing the “confidence level” for the reported interval of the true difference between the means, etc.

# Run a paired t-test to compare sepal width to petal width across all the iris
# data

Running t-tests on tidy dataframes

Running t.test this way is great if your data is originally in vectors (or if you’re comparing columns in a dataframe), but can be a pain if your data is in a ‘tidy’ data frame, like the iris data. Fortunately, R has your back; you can also run

t.test(<data to compare column> ~ <category column>, data = <input_dataframe>)

Let’s try this:

t.test(Petal.Width ~ Species, data = iris)

Ooops… what happened?

# Run t.test as above, but correctly, in order to perform a t-test comparing
# mean petal width between two species in the iris dataframe

(Again, note the use of ~ to mean “as a function of” here.)

Extracting data from t-tests

The printouts that running t.test() produces are great if you want to just run a single t-test and get a value, but that is often not the case. In a lot of situations, we want some of the values that are being printed out here saved as variables of then own for future use. Or maybe you want to get fancy and use ddply to run lots of t-tests at once. R allows us to do this for any statistical test we run, although figuring out how to do this often requires digging a bit through the documentation and/or online blogs. If you’re trying to figure out how to extract info from commonly used test functions and packages, I recommend google before anything else.

The first step is to save the results of t.test to a variable.

#  Re-run our example t-test from above.
t_test_results <- t.test(data_1, data_2)

So, what is this thing? If you look over in the Environment box, you can see that it shows up as a “List”; this is basically a special kind of vector, where every element has its own name, and can be a numeric type, a character type, or something more complicated (you can even have a list of dataframes). We can get some help from either the View or the summary functions.

View(t_test_results)
summary(t_test_results)

This gives us a list of the names of the variables that the t_test_results list holds. Elements of a list can be accessed via $, just like columns in a data frame! So now that we know what the different items in this list are called, we can save the ones we want.

# save the p-value of this comparison into a variable
diff_p_val <- t_test_results$p.value

# save the 95% confidence interval of the true difference between the means into
# a variable

Performing anova

Another really common statistical test in biology is Analysis of Variance (ANOVA), which tests whether the means of multiple populations differ significantly (so it can be thought of as a t-test for multiple populations at once). ANOVA can be run using the aov() function in R.

NB: In addition to aov(), R also has a function called anova(), which does not run ANOVA (but can instead be used for model comparison). Make sure you’re running the correct function.

To run ANOVA on a tidy dataframe in R, we simply have to run:

aov(Petal.Width ~ Species, data = iris)

However, unlike the output for t.test(), the default output of aov() is missing some key info (e.g. a p-value). This is the case for a lot of statistical tests in R, and in those situations, the summary() function comes to the rescue:

anova_results <- aov(Petal.Width ~ Species, data = iris)
summary(anova_results)

As with t-tests above, you can extract attributes of interest from anova_results with a little bit of work.

Linear models

Fitting linear models is also very straightforward in R. Let’s say we wanted to know whether Petal Length depended on Sepal Width in irises. (Note that this is not a good way to frame this question: these variables may be correlated, but it is probably silly to think as one of them as being “independent”, and the other being “dependent” on it; nevertheless, let’s frame the problem this way for demonstration purposes.)

We can easily model Petal Length as a function of Sepal Width using the lm() function; note the structure of this function call is identical to aov() and t.test() above.

silly_model <- lm(Petal.Length ~ Sepal.Width, data = iris)
summary(silly_model)

You can see that both the intercept (corresponding to Petal.Width when Sepal.Width is 0) and slope (here corresponding to the effect on Petal.Length of a 1-centimeter increase in Sepal.Width) are statistically significant, although the R-squared value is not great. (By the way, we can extract a vector containing the intercept and slope using silly_model$coefficients, and the R-squared value using summary(silly_model)$r.squared).

Notice that although we performed this linear model knowing it didn’t really make sense, R had no issue running it. R will basically let you calculate whatever, but it’s up to you to decide whether or not what you’re calculating makes sense.

Let’s take this silly example one step further and look at this data and the fit. Always plot things when possible!.

Reminder: you can plot a linear regression to your data in ggplot, with a confidence interval, using geom_smooth(method = 'lm')

# Use ggplot to create a plot of Sepal.Width on the x-axis, and Petal.Width on
# the y-axis; include all datapoints, colored by Species, and a *single*
# trendline, generated using method = lm, across all the samples
# (regardless of species)

Does the strong negative slope here make sense? What’s going on? (This is a very common problem called Simpson’s Paradox, which often confounds correlations and linear models.)

Maybe we can do a better job within the confines of our silly example by including terms for Species in our linear model?

# create a new variable, silly_model_with_species, containing a linear model
# that models Petal.Width as a function of Sepal.Width and Species, and print
# out its summary
# Hint: see how we split data by multiple categories in ddply earlier

What is the “Intercept” here? If we look at the slope terms, we can see that the first Species in the list (setosa) is treated as the “Intercept” Species. Therefore, the Intercept is what the model calculates the petal width of a setosa flower would be if its sepal width were 0. The rest of the terms here are considered slopes: each extra centimeter of sepal width increases petal width by ~0.5cm (the relationship between sepal width and petal length is now positive, which I think makes more sense considering the plot we just made). The petal length for a given sepal width is ~3cm higher in versicolor than in setosa, and ~4cm higher in virginica than setosa.

NB: If you try to make ggplot fit a smooth line for every species individually, you’ll notice that it’s actually fitting a different slope for Sepal.Width for every species, rather than a common slope across all species, as we are here. This is also possible when running lm(), and if you’re interested in doing this kind of analysis, I strongly recommend reading some online tutorials, especially the one from Coding Club below.

More complex statistical analysis

There are a ton of really great R packages for statistical analysis. Some are specialized for particular types of data (e.g. FACS, RNA-seq, etc), while others are more general (e.g. lme4 and brms, which allow you to run mixed-effect linear models, accounting for nested structures of sources of experimental noise).

In addition to “just googling” things, here are two resources for learning statistical programming in R that I love:

LS0tCnRpdGxlOiAiSW50cm8gUiBDb3Vyc2UsIFdvcmtzaG9wIDc6IFN1bW1hcml6aW5nIGRhdGEgYW5kIGRvaW5nIGJhc2ljIHN0YXRzIgpzdWJ0aXRsZTogfAogICAgfCAgIC0gc3Vic2V0dGluZyBkYXRhCiAgICB8ICAgLSBzdW1tYXJpemluZyB5b3VyIGRhdGEKICAgIHwgICAtIHNwbGl0dGluZyBkYXRhIGJ5IGNhdGVnb3JpZXMgdG8gY2FsY3VsYXRlIHN0YXRzCiAgICB8ICAgLSB0dGVzdHMsIGFub3ZhLCBsaW5lYXIgbW9kZWxzCiAgICB8ICAgLSByZXNvdXJjZXMgZm9yIGxlYXJuaW5nIHRvIGRvIHN0YXRpc3RpY3MgaW4gUgphdXRob3I6CiAgLSBFdWdlbmUgUGxhdnNraW4Kb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGRlcHRoOiAzCiAgICB0aWR5OiB5ZXMKICAgIHRvYzogeWVzCi0tLQoqKlNvbHV0aW9ucyB0byB0aGlzIHdvcmtzaG9wIGNhbiBiZSBmb3VuZCBbaGVyZV0oU29sdXRpb25zX1dvcmtzaG9wXzcubmIuaHRtbCkqKgoKIyBSZXZpZXcKYGBge3J9CiMgdXNlIGdncGxvdCB0byBtYWtlIGEgaGlzdG9ncmFtIG9mIGlyaXMgc2VwYWwgd2lkdGggYWNyb3NzIGFsbCBzcGVjaWVzCgojIHVzZSBnZ3Bsb3QgdG8gbWFrZSBhIHBsb3Qgb2YgaXJpcyBwZXRhbCBsZW5ndGggdnMgc2VwYWwgbGVuZ3RoLCBjb2xvcmVkIGJ5CiMgU3BlY2llcwoKIyByZXBlYXQgdGhlIHNjYXR0ZXJwbG90IGFib3ZlLCBidXQgaGF2ZSB0aGUgc3BlY2llcyBsYWJlbCBhcHBlYXIgb24gZXZlcnkgcG9pbnQKCgpgYGAKCiMgU3Vic2V0dGluZyBkYXRhCgpPbmUgb2YgdGhlIG1vc3QgcG93ZXJmdWwgdGhpbmdzIFIgY2FuIGRvIGVhc2lseSBhbmQgcXVpY2tseSBpcyB0byBzZWxlY3QgcGFydCBvZiB5b3VyIGRhdGEgYmFzZWQgb24gc29tZSBjcml0ZXJpYSB5b3UgcHJvdmlkZSB1c2luZyB0aGUgYHN1YnNldCgpYCBmdW5jdGlvbi4gVGhpcyBjYW4gYmUgaW1tZW5zZWx5IHVzZWZ1bC4gTGV0J3Mgc2F5IHdlJ3JlIGludGVyZXN0ZWQgaW4gaXJpcyBkYXRhLCBidXQgZG9uJ3QgcmVhbGx5IGNhcmUgYWJvdXQgdGhlICpJcmlzIHNldG9zYSogcGxhbnRzLiBMZXQncyBjcmVhdGUgYSBzdWJzZXQgb2Ygb3VyIGRhdGEgdGhhdCBleGNsdWRlcyB0aG9zZSBwbGFudHMuIFRvIHNldCB0aGUgY29uZGl0aW9ucyBmb3Igc3Vic2V0dGluZywgeW91IHdpbGwgaGF2ZSB0byByZW1lbWJlciBzb21ldGhpbmcgZnJvbSBvdXIgbGVzc29uIG9uIGxvZ2ljYWxzLgpgYGB7cn0KIyBjcmVhdGUgYSBzdWJzZXQgd2l0aCBkYXRhIG9uIGFsbCBzcGVjaWVzICpleGNlcHQqIHNldG9zYQppcmlzX25vX3NldG9zYSA8LSBzdWJzZXQoaXJpcywgU3BlY2llcyAhPSAnc2V0b3NhJykKYGBgCkxvb2sgYXQgdGhpcyBuZXcgZGF0YWZyYW1lLiBOb3RpY2UgdGhhdCBpbiB0aGUgY29kZSBhYm92ZSwgc3Vic2V0IChsaWtlIGdncGxvdCkga25vd3Mgd2hhdCB3ZSBtZWFuIGJ5IGBTcGVjaWVzYDsgYmVjYXVzZSB3ZSBhbHJlYWR5IHRlbGwgaXMgd2hpY2ggZGF0YWZyYW1lIHRvIHdvcmsgd2l0aCwgaXQgZmlndXJlcyB0aGF0IFNwZWNpZXMgaXMganVzdCBvbmUgb2YgdGhlIGNvbHVtbnMgaW4gdGhpcyBkYXRhZnJhbWUuIFJlY2FsbCB0aGF0IGAhPWAgbWVhbnMgJ25vdCBlcXVhbCB0bycuCgpUcnkgdGhpcyBvbiB5b3VyIG93bi4gQ3JlYXRlIGEgZGF0YSBmcmFtZSBvZiBhbGwgdGhlIHBsYW50cyB3aGVyZSBzZXBhbCB3aWR0aCBpcyBncmVhdGVyIHRoYW4gMy41LgpgYGB7cn0KIyBjcmVhdGUgYSBzdWJzZXQgb2YgdGhlIGRhdGEsIGlyaXNfd2lkZV9zZXBhbCwgdGhhdCBpbmNsdWRlcyBhbGwgcGxhbnRzIGZvcgojIHdoaWNoIHNlcGFsIHdpZHRoIGlzIGdyZWF0ZXIgdGhhbiAzLjUKCiMgbG9vayBhdCB0aGUgZGF0YSBmcmFtZS4gd2hhdCBkbyB5b3Ugbm90aWNlIGFib3V0IHRoZXNlIHBsYW50cyAoZXNwIHRoZWlyIHNwZWNpZXMpPwoKYGBgCgojIyMgc2lkZW5vdGU6ICVpbiUKCkEgcmVhbGx5IHVzZWZ1bCBsb2dpY2FsIG9wZXJhdG9yIGluIFIgaXMgYCVpbiVgOyBpdCBjaGVja3MgdGhhdCB0aGUgdGhpbmcgYmVmb3JlIGl0IGlzIGEgbWVtYmVyIG9mIHRoZSBsaXN0IG9mIHRoaW5ncyBhZnRlciBpdC4KYGBge3J9CiMgVFJVRSBzdGF0ZW1lbnQKJ2EnICVpbiUgYygnYScsICdiJywgJ2MnKQoKIyBGQUxTRSBzdGF0ZW1lbnQKJ2UnICVpbiUgYygnYScsICdiJywgJ2MnKQpgYGAKCgpgJWluJWAgY2FuIGJlIGluY3JlZGlibHkgdXNlZnVsIGluIHN1YnNldHRpbmcuCmBgYHtyfQojIGFub3RoZXIgd2F5IHRvIHN1YnNldCBkYXRhIHdpdGggbm9uLXNldG9zYSBzcGVjaWVzCmlyaXNfbm9fc2V0b3NhXzIgPC0gc3Vic2V0KGlyaXMsIFNwZWNpZXMgJWluJSBjKCd2ZXJzaWNvbG9yJywgJ3ZpcmdpbmljYScpKQpgYGAKCgojIEdldHRpbmcgdXNlZnVsIGluZm8gYWJvdXQgeW91ciBkYXRhCgpJdCBjYW4gYmUgaW5jcmVkaWJseSBoZWxwZnVsIHRvIHF1aWNrbHkgZ2V0IHN1bW1hcnkgaW5mbyBmb3IgeW91ciBkYXRhIGluIGFuIGFjY2Vzc2libGUsIFItcmVhZGFibGUgZm9ybWF0LiBXZSd2ZSBhbHJlYWR5IGNhbGN1bGF0ZWQgc29tZSAnc3VtbWFyeSBzdGF0aXN0aWNzJyAoYG1lYW5gKSBmb3IgZXZlcnkgY29sdW1uLCBhbmQgd2Ugc2F3IGhvdyBnZ3Bsb3QgZWFzaWx5IGxldHMgdXMgc3BsaXQgb3VyIHBsb3QgdXAgYnkgY2F0ZWdvcmllcyAoKmVnKiB0aHJvdWdoIHVzaW5nIGRpZmZlcmVudCBjb2xvcnMgZm9yIGVhY2ggb25lKS4gTm93IGxldCdzIGRvIHRoZSBzYW1lIHdpdGggc3VtbWFyeSBzdGF0aXN0aWNzLgoKIyMgVXNpbmcgdGhlIHN1bW1hcnkgZnVuY3Rpb24KCkEgcmVhbGx5IHVzZWZ1bCBmdW5jdGlvbiBmb3IgcXVpY2tseSBsZWFybmluZyBhYm91dCB0aGUgcHJvcGVydGllcyBvZiB5b3VyIGRhdGEgaXMgYHN1bW1hcnkoKWAuIEl0IHNob3dzIHlvdSB0aGUgbWVhbiwgbWVkaWFuLCBhbmQgcXVhbnRpbGVzIG9mIHlvdXIgbnVtZXJpYyBkYXRhLCBhcyB3ZWxsIGFzIGNvdW50cyBmb3IgZWFjaCBkYXRhIGNhdGVnb3J5LiBUcnkgdGhpczoKCmBgYHtyfQppcmlzX3N1bW1hcnkgPC0gc3VtbWFyeShpcmlzKQpwcmludChpcmlzX3N1bW1hcnkpCmBgYAoKT25lIHJlYXNvbiBgc3VtbWFyeSgpYCBpcyBvZnRlbiBub3QgcGFydGljdWxhcmx5IHVzZWZ1bCBpcyB0aGF0IGl0IHN1bW1hcml6ZXMgKmFsbCogdGhlIGRhdGEgaW4geW91ciBkYXRhZnJhbWUuIE5vdGljZSB0aGF0IGl0J3Mgbm90IHNwbGl0dGluZyB0aGUgc3VtbWFyaWVzIGJ5IHNwZWNpZXMsIHdoaWNoIGlzIHByb2JhYmx5IHdoYXQgd2Ugd291bGQgd2FudCB0byBoYXBwZW4uCgpPbmUgd2F5IHRvIGdldCBhcm91bmQgdGhpcyBpcyB0aHJvdWdoIHN1YnNldHRpbmcuIE1ha2UgYSBzdWJzZXQgb2YgdGhlIGlyaXMgZGF0YSB0aGF0IG9ubHkgY29udGFpbnMgZGF0YSBmb3IgdGhlICpzZXRvc2EqIHNwZWNpZXMsIGFuZCB0aGVuIHJ1biB0aGUgYHN1bW1hcnlgIGZ1bmN0aW9uIG9uIHRoaXMgc3Vic2V0LgpgYGB7cn0KIyB1c2Ugc3Vic2V0KCkgdG8gbWFrZSBhIGRhdGFmcmFtZSwgc2V0b3NhX2RmLCB0aGF0IGNvbnRhaW5zIG9ubHkgdGhlIGRhdGEgd2hlcmUKIyBTcGVjaWVzIGlzIHNldG9zYQojIChoaW50OiB5b3Ugd2FudCB0aGUgU3BlY2llcyBjb2x1bW4gdG8gZXF1YWwgJ3NldG9zYScpCgojIHBhc3MgdGhlIHN1bW1hcnkgZGF0YSBmb3Igc2V0b3NhX2RmIGludG8gYSBuZXcgdmFyaWFibGUgY2FsbGVkIHNldG9zYV9zdW1tYXJ5CgojIHByaW50IG91dCBzZXRvc2Ffc3VtbWFyeQoKYGBgCgpUaGVyZSBhcmUgYSBjb3VwbGUgb2YgaW1wb3J0YW50IGxpbWl0YXRpb25zIHRvIHRoZSBhcHByb2FjaCB3ZSBqdXN0IHVzZWQuIEZpcnN0IG9mIGFsbCwgd2UgaGF2ZSB0byBkbyB0aGlzIHRvIG9uZSBjYXRlZ29yeSBhdCBhIHRpbWUuIFRoaXMgbWF5IG5vdCBiZSBzbyBiYWQgZm9yIDMgc3BlY2llcywgYnV0IGltYWdpbmUgd2UgaGFkIGEgZGF0YXNldCBjb250YWluaW5nIDUwIHNwZWNpZXMgKG9yIG90aGVyIGNhdGVnb3JpZXMgd2Ugd2VyZSBpbnRlcmVzdGVkIGluKS4gU2Vjb25kLCBpZiB5b3UgbG9vayBpbiB5b3VyICoqRW52aXJvbm1lbnQqKiB3aW5kb3csIHlvdSdsbCBub3RpY2UgdGhhdCBuZWl0aGVyIGlyaXNfc3VtbWFyeSBvciBzZXRvc2Ffc3VtbWFyeSBhcmUgZGF0YWZyYW1lcy4gVGhlIGBzdW1tYXJ5YCBmdW5jdGlvbiBwcm9kdWNlcyBhIHdlaXJkIFIgZGF0YSB0eXBlIGNhbGxlZCBhICp0YWJsZSosIHdoaWNoIGlzIGVhc3kgdG8gbG9vayBhdCBidXQgYSBwYWluIHRvIGRvIGFueXRoaW5nIHdpdGguCmBgYHtyfQpWaWV3KGlyaXNfc3VtbWFyeSkKYGBgCkZvciBleGFtcGxlLCBpZiB5b3Ugd2FudGVkIHRvIHBsb3QgdGhlIHJlc3VsdHMgb2YgdGhlIHN1bW1hcnkgZnVuY3Rpb25zIGZvciBhIGJ1bmNoIG9mIHNwZWNpZXMgdXNpbmcgZ2dwbG90LCBpdCB3b3VsZCBiZSB2ZXJ5IGFubm95aW5nIHRvIGRvLgoKIyBTdW1tYXJpemluZyBkYXRhIHdpdGggcGx5cgoKIyMgVGhlIHByb2JsZW0KSWRlYWxseSwgd2Ugd2FudCB0byBzdW1tYXJpemUgY29tcGxpY2F0ZWQgZGF0YSBpbiBhIGNvbXBhY3QsIHJlYWRhYmxlLCBncmFwaGFibGUgd2F5LCBwcm9iYWJseSBhcyBhIGRhdGFmcmFtZS4gV2UgYWxzbyBkb24ndCB3YW50IHRvIGhhdmUgdG8gc3Vic2V0IHRoZSBkYXRhIGluZGl2aWR1YWxseSBmb3IgZXZlcnkgbWVtYmVyIG9mIGV2ZXJ5IGNhdGVnb3J5IHdlIHdhbnQgdG8gc3BsaXQgYnkgaW4gb3VyIGFuYWx5c2lzIChlLmcuIHN1YnNldCBmb3IgZXZlcnkgaXJpcyBzcGVjaWVzIGFuZCB0aGVuIGZpbmQgdGhlIG1lYW4gb2Ygc29tZSBjb2x1bW4pOyB0aGlzIG1heSBiZSBzaW1wbHkgYW5ub3lpbmcgZm9yIDMgaXJpc2VzLCBidXQgYmVjb21lcyBpbXBvc3NpYmxlIGZvciBtYW55IGRhdGFzZXRzIChlLmcuIGltYWdpbmUgdHJ5aW5nIHRvIGdldCB0aGUgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBleHByZXNzaW9uIGxldmVsIG9mIGV2ZXJ5IGdlbmUgaW4gYSBzaW5nbGUtY2VsbCBSTkFzZXEgZXhwZXJpbWVudCkuCgpPbmUgb2YgdGhlIHRoaW5ncyB0aGF0IG1hZGUgZ2dwbG90IHNvIGhlbHBmdWwgd2FzIGl0cyBhYmlsaXR5IHRvIGF1dG9tYXRpY2FsbHkgc3BsaXQgZGF0YSBieSBjYXRlZ29yeS4gV2UgZG9uJ3QgaGF2ZSB0byBzYXkgIlBsb3QgdGhpcyBzcGVjaWVzIGluIHJlZCwgdGhpcyBvdGhlciBvbmUgaW4gYmx1ZSwgZXRjLi4uIiBXZSBqdXN0IHNwZWNpZnkgdGhlIGNvbHVtbnMgb2YgdGhlIGRhdGFmcmFtZSB0aGF0IHdlIHdhbnQgdGhlIGRhdGEgZ3JvdXBlZCBieSwgYW5kIFIgZG9lcyB0aGUgcmVzdC4gSWYgYW55IHN0YXRzIG5lZWQgdG8gYmUgY2FsY3VsYXRlZCAoZS5nLiBpbiAqKmdlb21fc21vb3RoKiogb3IgKipnZW9tX2JveHBsb3QqKiksIHRoZXkgYXJlIGNhbGN1bGF0ZWQgd2l0aGluIHRoZSBzZXBhcmF0ZSBncm91cHMgc3BlY2lmaWVkIGJ5IHRoZSBjb2x1bW5zIHRoYXQgd2Ugd2FudCB0byBjYXRlZ29yaXplIGRhdGEgYnkuCgpBbHRob3VnaCBSIGhhcyBidWlsdC1pbiBmdW5jdGlvbnMgdGhhdCBkbyB0aGlzLCB3ZSdyZSBnb2luZyB0byB0dXJuIHRvIHlldCBhbm90aGVyIHBhY2thZ2UsICoqcGx5cioqLCB3aGljaCBtYWtlcyB0aGlzIHByb2Nlc3Mgc2ltcGxlci4KCmBgYHtyfQojIHVuY29tbWVudCB0aGUgbGluZSBiZWxvdyBhbmQgaW5zdGFsbCBwbHlyIGlmIHlvdSBoYXZlbid0IGFscmVhZHkgZG9uZSBzbwojaW5zdGFsbC5wYWNrYWdlcygncGx5cicpCgojIGxvYWQgdGhlIHBseXIgbGlicmFyeSBpbnRvIHRoZSBjdXJyZW50IFIgc2Vzc2lvbgpsaWJyYXJ5KHBseXIpCmBgYAoKIyMgKnNwbGl0LWFwcGx5LWNvbWJpbmUqIGFuZCBiYXNpYyAqKnBseXIqKiBmdW5jdGlvbmFsaXR5ClRoZSBiYXNpYyBwaGlsb3NvcGh5IGJlaGluZCAqKnBseXIqKiBpcyB0aGUgaWRlYSB0aGF0IGZvciBtYW55IHByb2JsZW1zLCB0aGUgc29sdXRpb24gZmFsbHMgaW50byB0aGUgcGF0dGVybiBvZiAqc3BsaXQtYXBwbHktY29tYmluZSo6IHlvdSB3YW50IHRvICpzcGxpdCogdGhlIGRhdGEgYmFzZWQgb24gc29tZSBjYXRlZ29yaWVzLCBhcHBseSBhIGZ1bmN0aW9uICh1c3VhbGx5IGNhbGN1bGF0aW5nIHNvbWUga2luZCBvZiBzdW1tYXJ5IHN0YXRpc3RpYykgdG8gZWFjaCByZXN1bHRpbmcgZGF0YXNldCwgYW5kICpjb21iaW5lKiB0aGUgcmVzdWx0cyBiYWNrIHRvZ2V0aGVyLgoKVGhlcmUgYXJlIHZhcmlvdXMgd2F5cyB0byB1c2UgKipwbHlyKioncyBgX19wbHlgIGZ1bmN0aW9ucywgYW5kIHRoZSBidWlsdC1pbiBkb2N1bWVudGF0aW9uIGRvZXNuJ3Qgb2ZmZXIgbXVjaCBoZWxwIGhlcmUuIElmIHlvdSdyZSBpbnRlcmVzdGVkIGluIGRpdmluZyBkZWVwZXIgaW50byB0aGlzLCBJIGNhbid0IHJlY29tbWVuZCB0aGlzIHR1dG9yaWFsIHN0cm9uZ2x5IGVub3VnaDoKWyoqcGx5cioqIGNsYXNzIGZyb20gSmVubnkgQnJ5YW4ncyBVQkMncyBTVEFUNTQ1IGNvdXJzZV0oaHR0cHM6Ly93d3cuc3RhdC51YmMuY2Evfmplbm55L1NUQVQ1NDVBL2Jsb2NrMDRfZGF0YUFnZ3JlZ2F0aW9uLmh0bWwpCgpUaGUgYXBwbGljYXRpb24gSSdtIHNob3dpbmcgeW91IGhlcmUgaXMgdGhlIG9uZSB0aGF0IG1ha2VzIHRoZSBtb3N0IHNlbnNlIHRvIG1lOyBpdCBsb29rcyBzb21ldGhpbmcgbGlrZSB0aGlzOgpgYGB7fQpvdXRwdXRfZGYgPC0KICBkZHBseSg8aW5wdXRfZGY+LAogICAgICAgIH4gPGNvbHVtbiBuYW1lcyB0byBzb3J0IGJ5PiwKICAgICAgICBzdW1tYXJpemUsCiAgICAgICAgPG91dHB1dF9jb2x1bW4+ID0gPG91dHB1dF9mdW5jdGlvbihpbnB1dF9jb2x1bW4pPikKYGBgCkhlcmUsIHRoZSBgfmAgcmVwcmVzZW50cyAiYXMgYSBmdW5jdGlvbiBvZiI7IHRoaXMgaXMgY29tbW9uIHRvIG1hbnkgUiBmdW5jdGlvbnMsIGFzIHlvdSB3aWxsIHNlZSBiZWxvdyEKCioqcGx5cioqIGNvbW1hbmQgbmFtZXMgYXJlIGJhc2VkIG9uIHRoZSBkYXRhIHRoZXkgdGFrZSBhcyBpbnB1dCBhbmQgb3V0cHV0OyB0aGVyZSBhcmUgbG90cyBvZiBvcHRpb25zIGhlcmUsIGJ1dCBJIGRvbid0IHdhbnQgdG8gZ28gaW50byB0b28gbWFueSBvZiB0aGVtLiBGb3Igbm93LCBsZXQncyB3b3JrIHdpdGggdGhlIGlyaXMgZGF0YSwgYW5kIHRyeSB0byBnZXQgYSBkYXRhZnJhbWUgb3V0IGNvbnRhaW5pbmcgc29tZSBzdW1tYXJ5IHN0YXRzIGJ5IHNwZWNpZXMuIFNpbmNlIHdlIGFyZSBnb2luZyBmcm9tIGRhdGFmcmFtZSAoKmQqKSB0byBkYXRhZnJhbWUgKCpkKiksIHRoZSBmdW5jdGlvbiB3ZSB3YW50IHRvIHVzZSBpcyBgZGRwbHlgIChpZiB3ZSB3ZXJlIHVzaW5nIGEgZGF0YWZyYW1lIGFzIGFuIG91dHB1dCBhbmQgYW4gYXJyYXkgYXMgb3V0cHV0LCB3ZSdkIHVzZSBgZGFwbHlgLCBldGMpLgoKRm9yIGV4YW1wbGUsIGxldCdzIHRyeSB0byBjcmVhdGUgYSBkYXRhZnJhbWUgdGhhdCBjb250YWlucyB0aGUgbWVhbiBwZXRhbCBsZW5ndGggb2YgZWFjaCBpcmlzIHNwZWNpZXM6CmBgYHtyfQppcmlzX3BldGFsX2xlbmd0aF9zdW1tYXJ5IDwtCiAgZGRwbHkoaXJpcywgfiBTcGVjaWVzLCBzdW1tYXJpemUsIG1lYW5fUGV0YWxfTGVuZ3RoID0gbWVhbihQZXRhbC5MZW5ndGgpKQpgYGAKCiMjIENyZWF0aW5nIG11bHRpcGxlIGNvbHVtbnMgb2Ygc3VtbWFyeSBkYXRhCgpZb3UgY2FuIHVzZSB0aGlzIGZvcm1hdCB0byBjcmVhdGUgbXVsdGlwbGUgbmV3IGNvbHVtbnMgdXNpbmcgbXVsdGlwbGUgZnVuY3Rpb25zOyBqdXN0IHNlcGFyYXRlIHRoZXNlIGJ5IGNvbW1hcywgbGlrZSBzbzoKYGBge3J9CmlyaXNfcGV0YWxfbGVuZ3RoX3N1bW1hcnlfMiA8LQogIGRkcGx5KGlyaXMsCiAgICAgICAgfiBTcGVjaWVzLAogICAgICAgIHN1bW1hcml6ZSwKICAgICAgICBtZWFuX1BldGFsX0xlbmd0aCA9IG1lYW4oUGV0YWwuTGVuZ3RoKSwKICAgICAgICBzZF9QZXRhbF9MZW5ndGggPSBzZChQZXRhbC5MZW5ndGgpKQpgYGAKT3VyIHN1bW1hcnkgaXMgbm93IGEgZGF0YWZyYW1lLCBhbmQgd2UgY2FuIGVhc2lseSByZXRyaWV2ZSB0aGUgbWVhbiBwZXRhbCBsZW5ndGggZm9yIGFueSBzcGVjaWVzCgpMZXQncyB0cnkgYSBjaGFsbGVuZ2U6CmBgYHtyfQojIFVzZSBkZHBseSB0byBjcmVhdGUgYSBkYXRhZnJhbWUsIGlyaXNfc2VwYWxfd2lkdGhfc3VtbWFyeSwgdGhhdCBjb250YWlucyB0aGUKIyBtZWFuIGFuZCBzdGFuZGFyZCBlcnJvciAoU0UpIG9mIGVhY2ggc3BlY2llcycgc2VwYWwgd2lkdGgKIyBTRSA9IHN0YW5kYXJkIGRldmlhdGlvbiAvIHNxcnQoY291bnQgLSAxKQoKYGBgCkkgdGhpbmsgd2hhdCBoZWxwcyBtZSB0aGluayBhYm91dCB0aGUgc29sdXRpb24gdG8gdGhlIGFib3ZlIGlzIHRoYXQsIGluIGVmZmVjdCwgYGRkcGx5YCB0cmVhdHMgZWFjaCBTcGVjaWVzIGFzIGl0cyBvd24gbGl0dGxlIGRhdGFmcmFtZSwgYXMgaWYgeW91IHdlcmUgbWFudWFsbHkgc3Vic2V0dGluZyB0aGUgaXJpcyBkYXRhZnJhbWUgYnkgU3BlY2llcyBvbmUtYnktb25lIGFuZCBjYWxjdWxhdGluZyB0aGUgb3V0cHV0IGNvbHVtbiB2YWx1ZXMuIFRoaXMgaXMgKnNwbGl0LWFwcGx5LWNvbWJpbmUqIGluIGFjdGlvbi4KCiMjIFNwbGl0dGluZyBieSBtdWx0aXBsZSBjYXRlZ29yaWVzCgpBbm90aGVyIHJlYWxseSBncmVhdCB0aGluZyBhYm91dCBzdW1tYXJpemluZyBkYXRhIHRoaXMgd2F5IGlzIHRoYXQgd2UgZG9uJ3QgbmVlZCB0byBsaW1pdCBvdXJzZWx2ZXMgdG8gc3BsaXR0aW5nIGl0IGJ5IGEgc2luZ2xlIGNhdGVnb3J5LiBJbWFnaW5lIHdlIGhhZCBhIG5ldyBkYXRhc2V0IHRoYXQgaW5jbHVkZWQgZmxvd2VyIGNvbG9yIGZvciBlYWNoIHBsYW50LCBhbmQgd2Ugd2VyZSBpbnRlcmVzdGVkIGluIGxvb2tpbmcgYXQgZmxvd2VyIGNvbG9yIGFsb25nc2lkZSBzcGVjaWVzIGluIG91ciBzdW1tYXJ5IHN0YXRpc3RpY3MuCgpgYGB7cn0KIyBjcmVhdGUgbmV3IGlyaXMgZGYKY29sb3JfaXJpcyA8LSBpcmlzCgojIGFkZCBmbG93ZXIgY29sb3IgYXMgYWx0ZXJuYXRpbmcgJ2JsdWUnIGFuZCAncHVycGxlJwpjb2xvcl9pcmlzJEZsb3dlci5Db2xvciA8LSByZXAoYygnYmx1ZScsICdwdXJwbGUnKSwgdGltZXMgPSBucm93KGlyaXMpLzIpCmBgYAoKV2UgY2FuIG5vdyB1c2UgZGRwbHkganVzdCBhcyBiZWZvcmUsIGJ1dCBzcGxpdHRpbmcgYnkgYSBsaXN0IG9mIGNhdGVnb3JpZXMgc2VwYXJhdGVkIGJ5IGArYCwgcmF0aGVyIHRoYW4gYnkgYSBzaW5nbGUgY2F0ZWdvcnk6CmBgYHtyfQppcmlzX3BldGFsX2xlbmd0aF9zdW1tYXJ5XzMgPC0KICBkZHBseShjb2xvcl9pcmlzLAogICAgICAgIH4gU3BlY2llcyArIEZsb3dlci5Db2xvciwKICAgICAgICBzdW1tYXJpemUsCiAgICAgICAgbWVhbl9QZXRhbF9MZW5ndGggPSBtZWFuKFBldGFsLkxlbmd0aCksCiAgICAgICAgc2RfUGV0YWxfTGVuZ3RoID0gc2QoUGV0YWwuTGVuZ3RoKSkKYGBgCgojIERvaW5nIHN0YXRzIGluIFIKClIgcHJvdmlkZXMgYSB0b24gb2YgYnVpbHQtaW4gZnVuY3Rpb25zIHRoYXQgbWFrZSBzaW1wbGUgc3RhdGlzdGljcyB2ZXJ5IGVhc3kgdG8gY2FsY3VsYXRlLiBIb3dldmVyLCB0aGUgcmVhbGx5IGFtYXppbmcgdGhpbmcgaXMgdGhlIGh1Z2UgbnVtYmVyIG9mIGFkZGl0aW9uYWwgcGFja2FnZXMgdGhhdCBhbGxvdyBSIHVzZXJzIHRvIHBlcmZvcm0gY29tcGxleCBzdGF0aXN0aWNhbCBhbmFseXNpcyBvbiB0aGVpciBkYXRhLiBJIGhvcGUgdGhhdCBsZWFybmluZyB0aGUgYmFzaWNzIG9mIFIgd2lsbCBoZWxwIGJyZWFrIGRvd24gdGhlIGJhcnJpZXJzIHRoYXQgcHJldmVudCBtYW55IHJlc2VhcmNoZXJzIGZyb20gYWNjZXNzaW5nIGFuZCBwZXJmb3JtaW5nIHRoaXMgbW9yZSBzb3BoaXN0aWNhdGVkIHN0YXRpc3RpY2FsIGFuYWx5c2lzLiBZb3Ugd2lsbCBsaWtlbHkgZmluZCBpZiB5b3Uga2VlcCBsZWFybmluZyBSIHRoYXQsIHBhc3QgYSBjZXJ0YWluIHBvaW50LCB5b3UncmUgbGVhcm5pbmcgbW9yZSBhYm91dCB0aGUgc3RhdGlzdGljcyBiZWhpbmQgc29tZSBvZiB0aGVzZSBwYWNrYWdlcyB0aGFuIGFib3V0IHRoZSBwcm9ncmFtbWluZzsgdGhhdCdzIGV4YWN0bHkgdGhlIGlkZWEuIE9uY2UgeW91IGdldCBwYXN0IHRoZSBzdGVlcCBsZWFybmluZyBjdXJ2ZSBpbiB0aGUgYmVnaW5uaW5nLCBSIG1ha2VzIHRoZSBjb2RpbmcgZWFzeSwgYW5kIHlvdSdyZSBmcmVlZCB1cCB0byB0aGluayBjYXJlZnVsbHkgYWJvdXQgZGF0YSBhbmFseXNpcyB3aXRob3V0IHRoZSBjb25zdHJhaW50cyBvZiB3aGV0aGVyIG9yIG5vdCB0aGVyZSdzIGEgYnVpbHQtaW4gZXhjZWwgZnVuY3Rpb24gdG8gcGVyZm9ybSBhIHNwZWNpZmljIGNvbXBhcmlzb24sIHRlc3QsIGV0Yy4KCldlJ3JlIGdvaW5nIHRvIGxvb2sgYXQgYSBmZXcgYnVpbHQtaW4gUiBmdW5jdGlvbnMgZm9yIHN0YXRpc3RpY2FsIHRlc3RzIHRoYXQgYXJlIGNvbW1vbmx5IGRvbmUgaW4gUiwgYnV0IEkgZW5jb3VyYWdlIHlvdSB0byBsb29rIGludG8gc29tZSBvZiB0aGUgcmVzb3VyY2VzIGF0IHRoZSBlbmQgb2YgdGhpcyBzZWN0aW9uIG9uIHlvdXIgb3duIHRpbWUgdG8gZ28gYmV5b25kIHRoZXNlLgoKIyMgdC10ZXN0cwoKUmVtZW1iZXIgdGhhdCB0LXRlc3RzIGNvbXBhcmUgdHdvIGdyb3VwcyB0byBkZXRlcm1pbmUgd2hldGhlciB0aGVpciBtZWFucyBhcmUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQuIEFzIHdpdGggYWxsIHN0YXRpc3RpY2FsIHRlc3RzLCB0aGVyZSBhcmUgYSB0b24gb2YgYXNzdW1wdGlvbnMgdGhhdCBnbyBpbnRvIHRoZXNlIHRlc3RzLCBhbmQgeW91IHNob3VsZCBhbHdheXMgbGVhcm4gYWJvdXQgdGhlc2UgYmVmb3JlIHJ1bm5pbmcgdGhlIHRlc3QuIE5ldmVydGhlbGVzcywgbGV0J3MgdHJ5IHRvIHBlcmZvcm0gYSB0LXRlc3QgdG8gY29tcGFyZSBQZXRhbCBMZW5ndGggYmV0d2VlbiB0d28gc3BlY2llcyBpbiBvdXIgaXJpcyBkYXRhLgoKIyMjIFJ1bm5pbmcgdC10ZXN0cyBvbiB2ZWN0b3JzIG9mIGRhdGEKClRoZSBzaW1wbGVzdCB3YXkgdG8gcnVuIGEgdC10ZXN0IGluIFIgbG9va3MgbGlrZSBgdC50ZXN0KDx2ZWN0b3JfMT4sIDx2ZWN0b3JfMj4pYCB3aGVyZSAqdmVjdG9yXzEqIGFuZCAqdmVjdG9yXzIqIGNvbnRhaW4gdGhlIGRhdGEgd2Ugd2FudCB0byBjb21wYXJlLgoKYGBge3J9CmRhdGFfMSA8LSBjKDEsIDIsIDQsIDYpCmRhdGFfMiA8LSBjKDgsIDMsIDQsIDUpCnQudGVzdChkYXRhXzEsIGRhdGFfMikKYGBgCkxvdHMgb2YgdXNlZnVsIGluZm8gaGVyZTogd2UgZ2V0IGEgcC12YWx1ZSAodGhlIGRpZmZlcmVudCBiZXR3ZWVuIHRoZSB0d28gZGF0YXNldHMgaXMgbm90IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgYXQgYSAqcCA9IDAuMDUqIGN1dG9mZiksIHRoZSBtZWFucywgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIHRoZSB0cnVlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbWVhbnMsIGV0Yy4KCkkgKipTVFJPTkdMWSoqIGVuY291cmFnZSB5b3UgdG8gZGlnIGludG8gdGhlIGRvY3VtZW50YXRpb24gZm9yIHRoaXMgdGVzdCAoYW5kIGFueSBvdGhlciBzdGF0aXN0aWNhbCB0ZXN0IHlvdSBydW4gaW4gUikgYmVmb3JlIHVzaW5nIGl0LiBGb3IgZXhhbXBsZSwgaWYgeW91IHJ1biBgP3QudGVzdGAsIHlvdSdsbCBzZWUgdGhhdCB0aGUgZGVmYXVsdCBmb3IgdGhpcyBmdW5jdGlvbiBpcyB0byBhc3N1bWUgdW5lcXVhbCB2YXJpYW5jZSBiZXR3ZWVuIHRoZSB0d28gc2FtcGxlcyAodGhpcyBpcyBkaWZmZXJlbnQgZnJvbSBvdGhlciBzdGF0aXN0aWNhbCBzb2Z0d2FyZSkuIFlvdSBjYW4gYWxzbyBzZWUgdGhhdCB5b3UgY2FuIGNoYW5nZSBsb3RzIG9mIHBhcmFtZXRlcnMsIGluY2x1ZGluZyBydW5uaW5nIGEgcGFpcmVkIHQtdGVzdCwgY2hhbmdpbmcgdGhlICJjb25maWRlbmNlIGxldmVsIiBmb3IgdGhlIHJlcG9ydGVkIGludGVydmFsIG9mIHRoZSB0cnVlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbWVhbnMsIGV0Yy4KCmBgYHtyfQojIFJ1biBhIHBhaXJlZCB0LXRlc3QgdG8gY29tcGFyZSBzZXBhbCB3aWR0aCB0byBwZXRhbCB3aWR0aCBhY3Jvc3MgYWxsIHRoZSBpcmlzCiMgZGF0YQoKYGBgCgojIyMgUnVubmluZyB0LXRlc3RzIG9uIHRpZHkgZGF0YWZyYW1lcwoKUnVubmluZyB0LnRlc3QgdGhpcyB3YXkgaXMgZ3JlYXQgaWYgeW91ciBkYXRhIGlzIG9yaWdpbmFsbHkgaW4gdmVjdG9ycyAob3IgaWYgeW91J3JlIGNvbXBhcmluZyBjb2x1bW5zIGluIGEgZGF0YWZyYW1lKSwgYnV0IGNhbiBiZSBhIHBhaW4gaWYgeW91ciBkYXRhIGlzIGluIGEgJ3RpZHknIGRhdGEgZnJhbWUsIGxpa2UgdGhlIGlyaXMgZGF0YS4gRm9ydHVuYXRlbHksIFIgaGFzIHlvdXIgYmFjazsgeW91IGNhbiBhbHNvIHJ1bgpgYGB7fQp0LnRlc3QoPGRhdGEgdG8gY29tcGFyZSBjb2x1bW4+IH4gPGNhdGVnb3J5IGNvbHVtbj4sIGRhdGEgPSA8aW5wdXRfZGF0YWZyYW1lPikKYGBgCgpMZXQncyB0cnkgdGhpczoKYGBge3J9CnQudGVzdChQZXRhbC5XaWR0aCB+IFNwZWNpZXMsIGRhdGEgPSBpcmlzKQpgYGAKT29vcHMuLi4gd2hhdCBoYXBwZW5lZD8KCmBgYHtyfQojIFJ1biB0LnRlc3QgYXMgYWJvdmUsIGJ1dCBjb3JyZWN0bHksIGluIG9yZGVyIHRvIHBlcmZvcm0gYSB0LXRlc3QgY29tcGFyaW5nCiMgbWVhbiBwZXRhbCB3aWR0aCBiZXR3ZWVuIHR3byBzcGVjaWVzIGluIHRoZSBpcmlzIGRhdGFmcmFtZQoKYGBgCihBZ2Fpbiwgbm90ZSB0aGUgdXNlIG9mIGB+YCB0byBtZWFuICJhcyBhIGZ1bmN0aW9uIG9mIiBoZXJlLikKCiMjIyBFeHRyYWN0aW5nIGRhdGEgZnJvbSB0LXRlc3RzCgpUaGUgcHJpbnRvdXRzIHRoYXQgcnVubmluZyBgdC50ZXN0KClgIHByb2R1Y2VzIGFyZSBncmVhdCBpZiB5b3Ugd2FudCB0byBqdXN0IHJ1biBhIHNpbmdsZSB0LXRlc3QgYW5kIGdldCBhIHZhbHVlLCBidXQgdGhhdCBpcyBvZnRlbiBub3QgdGhlIGNhc2UuIEluIGEgbG90IG9mIHNpdHVhdGlvbnMsIHdlIHdhbnQgc29tZSBvZiB0aGUgdmFsdWVzIHRoYXQgYXJlIGJlaW5nIHByaW50ZWQgb3V0IGhlcmUgc2F2ZWQgYXMgdmFyaWFibGVzIG9mIHRoZW4gb3duIGZvciBmdXR1cmUgdXNlLiBPciBtYXliZSB5b3Ugd2FudCB0byBnZXQgZmFuY3kgYW5kIHVzZSBkZHBseSB0byBydW4gbG90cyBvZiB0LXRlc3RzIGF0IG9uY2UuIFIgYWxsb3dzIHVzIHRvIGRvIHRoaXMgZm9yIGFueSBzdGF0aXN0aWNhbCB0ZXN0IHdlIHJ1biwgYWx0aG91Z2ggZmlndXJpbmcgb3V0IGhvdyB0byBkbyB0aGlzIG9mdGVuIHJlcXVpcmVzIGRpZ2dpbmcgYSBiaXQgdGhyb3VnaCB0aGUgZG9jdW1lbnRhdGlvbiBhbmQvb3Igb25saW5lIGJsb2dzLiBJZiB5b3UncmUgdHJ5aW5nIHRvIGZpZ3VyZSBvdXQgaG93IHRvIGV4dHJhY3QgaW5mbyBmcm9tIGNvbW1vbmx5IHVzZWQgdGVzdCBmdW5jdGlvbnMgYW5kIHBhY2thZ2VzLCBJIHJlY29tbWVuZCBnb29nbGUgYmVmb3JlIGFueXRoaW5nIGVsc2UuCgpUaGUgZmlyc3Qgc3RlcCBpcyB0byBzYXZlIHRoZSByZXN1bHRzIG9mIHQudGVzdCB0byBhIHZhcmlhYmxlLgpgYGB7cn0KIyAgUmUtcnVuIG91ciBleGFtcGxlIHQtdGVzdCBmcm9tIGFib3ZlLgp0X3Rlc3RfcmVzdWx0cyA8LSB0LnRlc3QoZGF0YV8xLCBkYXRhXzIpCmBgYAoKU28sIHdoYXQgaXMgdGhpcyB0aGluZz8gSWYgeW91IGxvb2sgb3ZlciBpbiB0aGUgKipFbnZpcm9ubWVudCoqIGJveCwgeW91IGNhbiBzZWUgdGhhdCBpdCBzaG93cyB1cCBhcyBhICJMaXN0IjsgdGhpcyBpcyBiYXNpY2FsbHkgYSBzcGVjaWFsIGtpbmQgb2YgdmVjdG9yLCB3aGVyZSBldmVyeSBlbGVtZW50IGhhcyBpdHMgb3duIG5hbWUsIGFuZCBjYW4gYmUgYSBudW1lcmljIHR5cGUsIGEgY2hhcmFjdGVyIHR5cGUsIG9yIHNvbWV0aGluZyBtb3JlIGNvbXBsaWNhdGVkICh5b3UgY2FuIGV2ZW4gaGF2ZSBhIGxpc3Qgb2YgZGF0YWZyYW1lcykuIFdlIGNhbiBnZXQgc29tZSBoZWxwIGZyb20gZWl0aGVyIHRoZSBgVmlld2Agb3IgdGhlIGBzdW1tYXJ5YCBmdW5jdGlvbnMuCmBgYHtyfQpWaWV3KHRfdGVzdF9yZXN1bHRzKQpzdW1tYXJ5KHRfdGVzdF9yZXN1bHRzKQpgYGAKVGhpcyBnaXZlcyB1cyBhIGxpc3Qgb2YgdGhlIG5hbWVzIG9mIHRoZSB2YXJpYWJsZXMgdGhhdCB0aGUgdF90ZXN0X3Jlc3VsdHMgbGlzdCBob2xkcy4gRWxlbWVudHMgb2YgYSBsaXN0IGNhbiBiZSBhY2Nlc3NlZCB2aWEgJCwganVzdCBsaWtlIGNvbHVtbnMgaW4gYSBkYXRhIGZyYW1lISBTbyBub3cgdGhhdCB3ZSBrbm93IHdoYXQgdGhlIGRpZmZlcmVudCBpdGVtcyBpbiB0aGlzIGxpc3QgYXJlIGNhbGxlZCwgd2UgY2FuIHNhdmUgdGhlIG9uZXMgd2Ugd2FudC4KCmBgYHtyfQojIHNhdmUgdGhlIHAtdmFsdWUgb2YgdGhpcyBjb21wYXJpc29uIGludG8gYSB2YXJpYWJsZQpkaWZmX3BfdmFsIDwtIHRfdGVzdF9yZXN1bHRzJHAudmFsdWUKCiMgc2F2ZSB0aGUgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgb2YgdGhlIHRydWUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBtZWFucyBpbnRvCiMgYSB2YXJpYWJsZQoKYGBgCgojIyBQZXJmb3JtaW5nIGFub3ZhCgpBbm90aGVyIHJlYWxseSBjb21tb24gc3RhdGlzdGljYWwgdGVzdCBpbiBiaW9sb2d5IGlzIEFuYWx5c2lzIG9mIFZhcmlhbmNlIChBTk9WQSksIHdoaWNoIHRlc3RzIHdoZXRoZXIgdGhlIG1lYW5zIG9mIG11bHRpcGxlIHBvcHVsYXRpb25zIGRpZmZlciBzaWduaWZpY2FudGx5IChzbyBpdCBjYW4gYmUgdGhvdWdodCBvZiBhcyBhIHQtdGVzdCBmb3IgbXVsdGlwbGUgcG9wdWxhdGlvbnMgYXQgb25jZSkuIEFOT1ZBIGNhbiBiZSBydW4gdXNpbmcgdGhlIGBhb3YoKWAgZnVuY3Rpb24gaW4gUi4KCioqTkIqKjogSW4gYWRkaXRpb24gdG8gYGFvdigpYCwgUiBhbHNvIGhhcyBhIGZ1bmN0aW9uIGNhbGxlZCBgYW5vdmEoKWAsIHdoaWNoIGRvZXMgKm5vdCogcnVuIEFOT1ZBIChidXQgY2FuIGluc3RlYWQgYmUgdXNlZCBmb3IgbW9kZWwgY29tcGFyaXNvbikuIE1ha2Ugc3VyZSB5b3UncmUgcnVubmluZyB0aGUgY29ycmVjdCBmdW5jdGlvbi4KClRvIHJ1biBBTk9WQSBvbiBhIHRpZHkgZGF0YWZyYW1lIGluIFIsIHdlIHNpbXBseSBoYXZlIHRvIHJ1bjoKYGBge3J9CmFvdihQZXRhbC5XaWR0aCB+IFNwZWNpZXMsIGRhdGEgPSBpcmlzKQpgYGAKCkhvd2V2ZXIsIHVubGlrZSB0aGUgb3V0cHV0IGZvciBgdC50ZXN0KClgLCB0aGUgZGVmYXVsdCBvdXRwdXQgb2YgYGFvdigpYCBpcyBtaXNzaW5nIHNvbWUga2V5IGluZm8gKGUuZy4gYSBwLXZhbHVlKS4gVGhpcyBpcyB0aGUgY2FzZSBmb3IgYSBsb3Qgb2Ygc3RhdGlzdGljYWwgdGVzdHMgaW4gUiwgYW5kIGluIHRob3NlIHNpdHVhdGlvbnMsIHRoZSBgc3VtbWFyeSgpYCBmdW5jdGlvbiBjb21lcyB0byB0aGUgcmVzY3VlOgpgYGB7cn0KYW5vdmFfcmVzdWx0cyA8LSBhb3YoUGV0YWwuV2lkdGggfiBTcGVjaWVzLCBkYXRhID0gaXJpcykKc3VtbWFyeShhbm92YV9yZXN1bHRzKQpgYGAKCkFzIHdpdGggdC10ZXN0cyBhYm92ZSwgeW91IGNhbiBleHRyYWN0IGF0dHJpYnV0ZXMgb2YgaW50ZXJlc3QgZnJvbSBgYW5vdmFfcmVzdWx0c2Agd2l0aCBhIGxpdHRsZSBiaXQgb2Ygd29yay4KCiMjIExpbmVhciBtb2RlbHMKCkZpdHRpbmcgbGluZWFyIG1vZGVscyBpcyBhbHNvIHZlcnkgc3RyYWlnaHRmb3J3YXJkIGluIFIuIExldCdzIHNheSB3ZSB3YW50ZWQgdG8ga25vdyB3aGV0aGVyIFBldGFsIExlbmd0aCBkZXBlbmRlZCBvbiBTZXBhbCBXaWR0aCBpbiBpcmlzZXMuIChOb3RlIHRoYXQgdGhpcyBpcyAqbm90KiBhIGdvb2Qgd2F5IHRvIGZyYW1lIHRoaXMgcXVlc3Rpb246IHRoZXNlIHZhcmlhYmxlcyBtYXkgYmUgY29ycmVsYXRlZCwgYnV0IGl0IGlzIHByb2JhYmx5IHNpbGx5IHRvIHRoaW5rIGFzIG9uZSBvZiB0aGVtIGFzIGJlaW5nICJpbmRlcGVuZGVudCIsIGFuZCB0aGUgb3RoZXIgYmVpbmcgImRlcGVuZGVudCIgb24gaXQ7IG5ldmVydGhlbGVzcywgbGV0J3MgZnJhbWUgdGhlIHByb2JsZW0gdGhpcyB3YXkgZm9yIGRlbW9uc3RyYXRpb24gcHVycG9zZXMuKQoKV2UgY2FuIGVhc2lseSBtb2RlbCBQZXRhbCBMZW5ndGggYXMgYSBmdW5jdGlvbiBvZiBTZXBhbCBXaWR0aCB1c2luZyB0aGUgYGxtKClgIGZ1bmN0aW9uOyBub3RlIHRoZSBzdHJ1Y3R1cmUgb2YgdGhpcyBmdW5jdGlvbiBjYWxsIGlzIGlkZW50aWNhbCB0byBgYW92KClgIGFuZCBgdC50ZXN0KClgIGFib3ZlLgpgYGB7cn0Kc2lsbHlfbW9kZWwgPC0gbG0oUGV0YWwuTGVuZ3RoIH4gU2VwYWwuV2lkdGgsIGRhdGEgPSBpcmlzKQpzdW1tYXJ5KHNpbGx5X21vZGVsKQpgYGAKWW91IGNhbiBzZWUgdGhhdCBib3RoIHRoZSBpbnRlcmNlcHQgKGNvcnJlc3BvbmRpbmcgdG8gUGV0YWwuV2lkdGggd2hlbiBTZXBhbC5XaWR0aCBpcyAwKSBhbmQgc2xvcGUgKGhlcmUgY29ycmVzcG9uZGluZyB0byB0aGUgZWZmZWN0IG9uIFBldGFsLkxlbmd0aCBvZiBhIDEtY2VudGltZXRlciBpbmNyZWFzZSBpbiBTZXBhbC5XaWR0aCkgYXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQsIGFsdGhvdWdoIHRoZSBSLXNxdWFyZWQgdmFsdWUgaXMgbm90IGdyZWF0LiAoQnkgdGhlIHdheSwgd2UgY2FuIGV4dHJhY3QgYSB2ZWN0b3IgY29udGFpbmluZyB0aGUgaW50ZXJjZXB0IGFuZCBzbG9wZSB1c2luZyBgc2lsbHlfbW9kZWwkY29lZmZpY2llbnRzYCwgYW5kIHRoZSBSLXNxdWFyZWQgdmFsdWUgdXNpbmcgYHN1bW1hcnkoc2lsbHlfbW9kZWwpJHIuc3F1YXJlZGApLgoKTm90aWNlIHRoYXQgYWx0aG91Z2ggd2UgcGVyZm9ybWVkIHRoaXMgbGluZWFyIG1vZGVsIGtub3dpbmcgaXQgZGlkbid0IHJlYWxseSBtYWtlIHNlbnNlLCBSIGhhZCBubyBpc3N1ZSBydW5uaW5nIGl0LiBSIHdpbGwgYmFzaWNhbGx5IGxldCB5b3UgY2FsY3VsYXRlIHdoYXRldmVyLCBidXQgaXQncyB1cCB0byB5b3UgdG8gZGVjaWRlIHdoZXRoZXIgb3Igbm90IHdoYXQgeW91J3JlIGNhbGN1bGF0aW5nIG1ha2VzIHNlbnNlLgoKTGV0J3MgdGFrZSB0aGlzIHNpbGx5IGV4YW1wbGUgb25lIHN0ZXAgZnVydGhlciBhbmQgbG9vayBhdCB0aGlzIGRhdGEgYW5kIHRoZSBmaXQuICoqQWx3YXlzIHBsb3QgdGhpbmdzIHdoZW4gcG9zc2libGUhKiouCgpSZW1pbmRlcjogeW91IGNhbiBwbG90IGEgbGluZWFyIHJlZ3Jlc3Npb24gdG8geW91ciBkYXRhIGluIGdncGxvdCwgd2l0aCBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwsIHVzaW5nIGBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nKWAKYGBge3J9CiMgVXNlIGdncGxvdCB0byBjcmVhdGUgYSBwbG90IG9mIFNlcGFsLldpZHRoIG9uIHRoZSB4LWF4aXMsIGFuZCBQZXRhbC5XaWR0aCBvbgojIHRoZSB5LWF4aXM7IGluY2x1ZGUgYWxsIGRhdGFwb2ludHMsIGNvbG9yZWQgYnkgU3BlY2llcywgYW5kIGEgKnNpbmdsZSoKIyB0cmVuZGxpbmUsIGdlbmVyYXRlZCB1c2luZyBtZXRob2QgPSBsbSwgYWNyb3NzIGFsbCB0aGUgc2FtcGxlcwojIChyZWdhcmRsZXNzIG9mIHNwZWNpZXMpCgpgYGAKRG9lcyB0aGUgc3Ryb25nIG5lZ2F0aXZlIHNsb3BlIGhlcmUgbWFrZSBzZW5zZT8gV2hhdCdzIGdvaW5nIG9uPyAoVGhpcyBpcyBhIHZlcnkgY29tbW9uIHByb2JsZW0gY2FsbGVkIFsqKlNpbXBzb24ncyBQYXJhZG94KipdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1NpbXBzb24lMjdzX3BhcmFkb3gpLCB3aGljaCBvZnRlbiBjb25mb3VuZHMgY29ycmVsYXRpb25zIGFuZCBsaW5lYXIgbW9kZWxzLikKCk1heWJlIHdlIGNhbiBkbyBhIGJldHRlciBqb2Igd2l0aGluIHRoZSBjb25maW5lcyBvZiBvdXIgc2lsbHkgZXhhbXBsZSBieSBpbmNsdWRpbmcgdGVybXMgZm9yIFNwZWNpZXMgaW4gb3VyIGxpbmVhciBtb2RlbD8KYGBge3J9CiMgY3JlYXRlIGEgbmV3IHZhcmlhYmxlLCBzaWxseV9tb2RlbF93aXRoX3NwZWNpZXMsIGNvbnRhaW5pbmcgYSBsaW5lYXIgbW9kZWwKIyB0aGF0IG1vZGVscyBQZXRhbC5XaWR0aCBhcyBhIGZ1bmN0aW9uIG9mIFNlcGFsLldpZHRoIGFuZCBTcGVjaWVzLCBhbmQgcHJpbnQKIyBvdXQgaXRzIHN1bW1hcnkKIyBIaW50OiBzZWUgaG93IHdlIHNwbGl0IGRhdGEgYnkgbXVsdGlwbGUgY2F0ZWdvcmllcyBpbiBkZHBseSBlYXJsaWVyCgpgYGAKV2hhdCBpcyB0aGUgIkludGVyY2VwdCIgaGVyZT8gSWYgd2UgbG9vayBhdCB0aGUgc2xvcGUgdGVybXMsIHdlIGNhbiBzZWUgdGhhdCB0aGUgZmlyc3QgU3BlY2llcyBpbiB0aGUgbGlzdCAoKnNldG9zYSopIGlzIHRyZWF0ZWQgYXMgdGhlICJJbnRlcmNlcHQiIFNwZWNpZXMuIFRoZXJlZm9yZSwgdGhlIEludGVyY2VwdCBpcyB3aGF0IHRoZSBtb2RlbCBjYWxjdWxhdGVzIHRoZSBwZXRhbCB3aWR0aCBvZiBhICpzZXRvc2EqIGZsb3dlciB3b3VsZCBiZSBpZiBpdHMgc2VwYWwgd2lkdGggd2VyZSAwLiBUaGUgcmVzdCBvZiB0aGUgdGVybXMgaGVyZSBhcmUgY29uc2lkZXJlZCBzbG9wZXM6IGVhY2ggZXh0cmEgY2VudGltZXRlciBvZiBzZXBhbCB3aWR0aCBpbmNyZWFzZXMgcGV0YWwgd2lkdGggYnkgfjAuNWNtICh0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gc2VwYWwgd2lkdGggYW5kIHBldGFsIGxlbmd0aCBpcyBub3cgcG9zaXRpdmUsIHdoaWNoIEkgdGhpbmsgbWFrZXMgbW9yZSBzZW5zZSBjb25zaWRlcmluZyB0aGUgcGxvdCB3ZSBqdXN0IG1hZGUpLiBUaGUgcGV0YWwgbGVuZ3RoIGZvciBhIGdpdmVuIHNlcGFsIHdpZHRoIGlzIH4zY20gaGlnaGVyIGluICp2ZXJzaWNvbG9yKiB0aGFuIGluICpzZXRvc2EqLCBhbmQgfjRjbSBoaWdoZXIgaW4gKnZpcmdpbmljYSogdGhhbiAqc2V0b3NhKi4KCioqTkIqKjogSWYgeW91IHRyeSB0byBtYWtlIGdncGxvdCBmaXQgYSBzbW9vdGggbGluZSBmb3IgZXZlcnkgc3BlY2llcyBpbmRpdmlkdWFsbHksIHlvdSdsbCBub3RpY2UgdGhhdCBpdCdzIGFjdHVhbGx5IGZpdHRpbmcgYSBkaWZmZXJlbnQgc2xvcGUgZm9yIFNlcGFsLldpZHRoIGZvciBldmVyeSBzcGVjaWVzLCByYXRoZXIgdGhhbiBhIGNvbW1vbiBzbG9wZSBhY3Jvc3MgYWxsIHNwZWNpZXMsIGFzIHdlIGFyZSBoZXJlLiBUaGlzIGlzIGFsc28gcG9zc2libGUgd2hlbiBydW5uaW5nIGBsbSgpYCwgYW5kIGlmIHlvdSdyZSBpbnRlcmVzdGVkIGluIGRvaW5nIHRoaXMga2luZCBvZiBhbmFseXNpcywgSSBzdHJvbmdseSByZWNvbW1lbmQgcmVhZGluZyBzb21lIG9ubGluZSB0dXRvcmlhbHMsIGVzcGVjaWFsbHkgdGhlIG9uZSBmcm9tIENvZGluZyBDbHViIGJlbG93LgoKIyBNb3JlIGNvbXBsZXggc3RhdGlzdGljYWwgYW5hbHlzaXMKClRoZXJlIGFyZSBhIHRvbiBvZiByZWFsbHkgZ3JlYXQgUiBwYWNrYWdlcyBmb3Igc3RhdGlzdGljYWwgYW5hbHlzaXMuIFNvbWUgYXJlIHNwZWNpYWxpemVkIGZvciBwYXJ0aWN1bGFyIHR5cGVzIG9mIGRhdGEgKGUuZy4gRkFDUywgUk5BLXNlcSwgZXRjKSwgd2hpbGUgb3RoZXJzIGFyZSBtb3JlIGdlbmVyYWwgKGUuZy4gKipsbWU0KiogYW5kICoqYnJtcyoqLCB3aGljaCBhbGxvdyB5b3UgdG8gcnVuIG1peGVkLWVmZmVjdCBsaW5lYXIgbW9kZWxzLCBhY2NvdW50aW5nIGZvciBuZXN0ZWQgc3RydWN0dXJlcyBvZiBzb3VyY2VzIG9mIGV4cGVyaW1lbnRhbCBub2lzZSkuCgpJbiBhZGRpdGlvbiB0byAianVzdCBnb29nbGluZyIgdGhpbmdzLCBoZXJlIGFyZSB0d28gcmVzb3VyY2VzIGZvciBsZWFybmluZyBzdGF0aXN0aWNhbCBwcm9ncmFtbWluZyBpbiBSIHRoYXQgSSBsb3ZlOgoKKiBbVW5pdmVyc2l0eSBvZiBFZGluYnVyZ2ggQ29kaW5nIENsdWJdKGh0dHBzOi8vb3VyY29kaW5nY2x1Yi5naXRodWIuaW8vdHV0b3JpYWxzKTogR3JlYXQgMS0yIGhvdXIgdHV0b3JpYWxzIG9uIGV2ZXJ5dGhpbmcgZnJvbSBiYXNpY3Mgb2YgY29kaW5nIHRvIGNvbXBsZXggc3RhdGlzdGljYWwgYW5hbHlzaXMsIG1vc3RseSBpbiBSIGFuZCBQeXRob24uIFRoZXNlIGFyZSBhbWF6aW5nIHJlc291cmNlcyBmb3IgZ2V0dGluZyBhbiBpbnRyb2R1Y3Rpb24gdG8gdW5kZXJzdGFuZGluZyBhbmQgY29kaW5nIG9uZS1vZmYgc3RhdGlzdGljYWwgYXBwcm9hY2hlcy4KKiBbUmljaGFyZCBNY0VscmVhdGgncyAqU3RhdGlzdGljYWwgUmV0aGlua2luZypdKGh0dHBzOi8veGNlbGFiLm5ldC9ybS9zdGF0aXN0aWNhbC1yZXRoaW5raW5nLyk6IEFuIGludGVybWVkaWF0ZS1sZXZlbCBzdGF0aXN0aWNzIGJvb2sgdGhhdCBJIHJlYWxseSBlbmpveWVkLiBJdCB3YWxrcyB0aHJvdWdoIHN0YXRpc3RpY2FsIGFuYWx5c2lzIG1vc3RseSBmcm9tIHNjcmF0Y2gsIGZyYW1pbmcgZXZlcnl0aGluZyBpbiBhIGJheWVzaWFuIGNvbnRleHQgYW5kIHdpdGggY29uc3RhbnQgZXhhbXBsZXMgdGhhdCBjYW4gYmUgcnVuIGluIFIuIElmIHlvdSBhcmUgaW50ZXJlc3RlZCBpbiBnYWluaW5nIGEgZGVlcGVyIHVuZGVyc3RhbmRpbmcgb2Ygc3RhdGlzdGljcywgdGhpcyBpcyBhIGdyZWF0IHJlc291cmNlIChhbmQgYWxsIHRoZSBsZWN0dXJlcyBmcm9tIGhpcyBjb3Vyc2UgYXNzb2NpYXRlZCB3aXRoIHRoaXMgYm9vayBhcmUgW2ZyZWUgb25saW5lXShodHRwczovL3d3dy55b3V0dWJlLmNvbS9jaGFubmVsL1VDTkpLNl9EWnZjTXFOU3pRZEVrenZ6QS92aWRlb3MpISkK