Introduction to R - Part II

If you have previously-attended this course and are reviewing the notes, please be aware of some re-organisation of the materials in this section. Some of the later examples have been moved to Part 3.

We should have loaded the readr library and imported an example dataset into R

library(readr)
gapminder <- read_csv("raw_data/gapminder.csv")

Manipulating Columns

We are going to use functions from the dplyr package to manipulate the data frame we have just created. It is perfectly possible to work with data frames using the functions provided as part of “base R”. However, many find it easy to read and write code using dplyr.

There are many more functions available in dplyr than we will cover today. An overview of all functions is given in a cheatsheet.

  • dplyr cheatsheet. The cheatsheet is also available through the RStudio Help menu.

Before using any of these functions, we need to load the library:-

library(dplyr)

selecting columns

We can access the columns of a data frame using the select function.

by name

Firstly, we can select column by name, by adding bare column names (i.e. not requiring quote marks around the name) after the name of the data frame, separated by a , .

select(gapminder, country, continent)

As we have to type the column names manually (no auto-complete!), we have to make sure we type the name exactly as it appears in the data. If select sees a name that doesn’t exist in the data frame it should give an informative message Error: Can't subset columns that don't exist.

We can also omit columns from the ouput by putting a minus (-) in front of the column name. Note that this is not the same as removing the column from the data permanently.

select(gapminder, -country)

range of columns

A range of columns can be selected by the : operator.

select(gapminder, lifeExp:gdpPercap)

helper functions

There are a number of helper functions can be employed if we are unsure about the exact name of the column.

select(gapminder, starts_with("co"))
select(gapminder, contains("life"))
# selecting the last and penultimate columns
select(gapminder, last_col(1),last_col())

It is also possible to use the column number in the selection.

select(gapminder, 4:6)

Question: Why might using the number of the column sometimes be problematic? Consider what might happen if you wrote code to select the last column of data from a file using the column number. What would happen if the number of columns in the file was not constant?

The select function can be used with just a single column name - in a similar manner to the $ operation we saw last time. However, select always returns a data frame whereas $ gives a vector. Compare the output of the following code chunks

select(gapminder, pop)
gapminder$pop

The consequence of this is that you cannot use functions such as mean in combination with select

pops <- select(gapminder, pop)
mean(pops)

In the next session we will see how to calculate summary statistics on particular columns in our data. For now, a useful function is pull that will return the correct type of data required for a function such as mean.

pops <- pull(gapminder,pop)
mean(pops)
[1] 29601212

Restricting rows with filter

So far we have been returning all the rows in the output. We can use what we call a logical test to filter the rows in a data frame. This logical test will be applied to each row and give either a TRUE or FALSE result. When filtering, only rows with a TRUE result get returned.

For example we filter for rows where the lifeExp variable is less than 40.

filter(gapminder, lifeExp < 40)

Internally, R creates a vector of TRUE or FALSE; one for each row in the data frame. This is then used to decide which rows to display.

Testing for equality can be done using ==. This will only give TRUE for entries that are exactly the same as the test string.

filter(gapminder, country == "Zambia")

N.B. For partial matches, the grepl function and / or regular expressions (if you know them) can be used.

filter(gapminder, grepl("land", country))

We can also test if rows are not equal to a value using !=

filter(gapminder, continent != "Europe")

testing more than one condition

There are a couple of ways of testing for more than one pattern. The first uses an or | statement. i.e. testing if the value of country is Zambia or the value is Zimbabwe. Remember to use double = sign to test for string equality; ==.

filter(gapminder, country == "Zambia" | country == "Zimbabwe")

The %in% function is a convenient function for testing which items in a vector correspond to a defined set of values.

filter(gapminder, country %in% c("Zambia", "Zimbabwe"))

We can require that both tests are TRUE, e.g. which years in Zambia had a life expectancy less than 40, by separating conditional statements by a ,. This performs an AND test so only rows that meet both conditions are returned.

filter(gapminder, country == "Zambia", lifeExp < 40)

You may have noticed that filter will always output the same number of columns as the input data frame. filter never changes the columns that are displayed. There are ways of using filter in conjunction with select as we will see later.




Exercise

  • Create a subset of the data where the population less than a million in the year 2002
  • Create a subset of the data where the life expectancy is greater than 75 in the years prior to 1987
  • Create a subset of the European data where the life expectancy is between 75 and 80 in the years 2002 or 2007.
  • If you are finished with these, try to explore alternative ways of performing the same filtering



Manipulating the values in a column / creating new columns

As well as selecting existing columns in the data frame, new columns can be created and existing ones manipulated using the mutate function. Typically a function or mathematical expression is applied to data in existing columns by row, and the result either stored in a new column or reassigned to an existing one. In other words, the number of values returned by the function must be the same as the number of input values. Multiple mutations can be performed in one line of code.

Here, we create a new column of population in millions (PopInMillions) and round lifeExp to the nearest integer.

mutate(gapminder, PopInMillions = pop / 1e6,
       lifeExp = round(lifeExp))

Note that we haven’t actually changed our gapminder data frame. If we wanted to make the new columns permanent, we would have to create a new variable.

If we want to rename existing columns, and not create any extra columns, we can use the rename function.

rename(gapminder, GDP=gdpPercap)

Ordering / sorting

The whole data frame can be re-ordered according to the values in one column using the arrange function. So to order the table according to population size:-

arrange(gapminder, pop)

The default is smallest --> largest but we can change this using the desc function

arrange(gapminder, desc(pop))

arrange also works on character vectors, arrange them alpha-numerically.

arrange(gapminder, desc(country))

We can even order by more than one condition

arrange(gapminder, year, pop)
arrange(gapminder, year, continent, pop)

Saving data frames

A final point on data frames is that we can write them to disk once we have done our data processing.

Let’s create a folder in which to store such processed, analysis ready data

dir.create("out_data",showWarnings = FALSE)
## showWarnings will stop a message from appearing if the directory already exists
byWealth <- arrange(gapminder, desc(gdpPercap))
# check the output before writing
head(byWealth)
write_csv(byWealth, file = "out_data/by_wealth.csv")

We will now try an exercise that involves using several steps of these operations




Exercise

  • Filter the data to include just observations from the year 2002
  • Re-arrange the table so that the countries from each continent are ordered according to decreasing wealth. i.e. the wealthiest countries first
  • Select all the columns apart from year
  • Write the data frame out to a file in out_data/ folder



“Piping”

As have have just seen, we will often need to perform an analysis, or clean a dataset, using several dplyr functions in sequence. e.g. filtering, mutating, then selecting columns of interest (possibly followed by plotting - see shortly).

As a small example; if we wanted to filter our results to just Europe the continent column becomes redundant so we might as well remove it.

The following is perfectly valid R code, but invites the user to make mistakes and copy-and-paste erros when writing it. We also have to create multiple copies of the same data frame.

tmp <- filter(gapminder, continent == "Europe")
tmp2 <- select(tmp, -continent)
tmp2

(Those familiar with Unix may recall that commands can be joined with a pipe; |)

In R, dplyr commands to be linked together and form a workflow. The symbol %>% is pronounced then. With a %>% the input to a function is assumed to be the output of the previous line. All the dplyr functions that we have seen so far take a data frame as an input and return an altered data frame as an output, so are amenable to this type of programming.

The example we gave of filtering just the European countries and removing the continent column becomes:-

filter(gapminder, continent=="Europe") %>% 
  select(-continent)



Exercise

  • Re-write your solution to the previous exercise, but using the %>% symbol



Plotting

The R language has extensive graphical capabilities.

Graphics in R may be created by many different methods including base graphics and more advanced plotting packages such as lattice.

The ggplot2 package was created by Hadley Wickham and provides a intuitive plotting system to rapidly generate publication quality graphics.

ggplot2 builds on the concept of the “Grammar of Graphics” (Wilkinson 2005, Bertin 1983) which describes a consistent syntax for the construction of a wide range of complex graphics by a concise description of their components.

Why use ggplot2?

The structured syntax and high level of abstraction used by ggplot2 should allow for the user to concentrate on the visualisations instead of creating the underlying code.

On top of this central philosophy ggplot2 has:

  • Increased flexibility over many plotting systems.
  • An advanced theme system for professional/publication level graphics.
  • Large developer base – Many libraries extending its flexibility.
  • Large user base – Great documentation and active mailing list.

It is always useful to think about the message you want to convey and the appropriate plot before writing any R code. Resources like data-to-viz.com should help.

With some practice, ggplot2 makes it easier to go from the figure you are imagining in our head (or on paper) to a publication-ready image in R.

As with dplyr, we won’t have time to cover all details of ggplot2. This is however a useful cheatsheet that can be printed as a reference. The cheatsheet is also available through the RStudio Help menu.

Basic plot types

A plot in ggplot2 is created with the following type of command

ggplot(data = <DATA>, mapping = aes(<MAPPINGS>)) +  <GEOM_FUNCTION>()

So we need to specify

  • The data to be used in graph
  • Mappings of data to the graph (aesthetic mapping)
  • What type of graph we want to use (The geom to use).

Lets say that we want to explore the relationship between GDP and Life Expectancy. We might start with the hypothesis that richer countries have higher life expectancy. A sensible choice of plot would be a scatter plot with gdp on the x-axis and life expectancy on the y-axis.

The first stage is to specify our dataset

library(ggplot2)
ggplot(data = gapminder)

For the aesthetics, as a bare minimum we will map the gdpPercap and lifeExp to the x- and y-axis of the plot. Some progress is made; we at least get axes

ggplot(data = gapminder,aes(x=gdpPercap, y=lifeExp))

That created the axes, but we still need to define how to display our points on the plot. As we have continuous data for both the x- and y-axis, geom_point is a good choice.

ggplot(data = gapminder,aes(x=gdpPercap, y=lifeExp)) + geom_point()

The geom we use will depend on what kind of data we have (continuous, categorical etc)

  • geom_point() - Scatter plots
  • geom_line() - Line plots
  • geom_smooth() - Fitted line plots
  • geom_bar() - Bar plots
  • geom_boxplot() - Boxplots
  • geom_jitter() - Jitter to plots
  • geom_histogram() - Histogram plots
  • geom_density() - Density plots
  • geom_text() - Text to plots
  • geom_errorbar() - Errorbars to plots
  • geom_violin() - Violin plots
  • geom_tile() - for “heatmap”-like plots

Boxplots are commonly used to visualise the distributions of continuous data. We have to use a categorical variable on the x-axis such as continent or country (not advisable in this case as there are too many different values).

The order of the boxes along the x-axis is dictated by the order of categories in the factor; with the default for names being alphabetical order.

ggplot(gapminder, aes(x = continent, y=gdpPercap)) + geom_boxplot()

ggplot(gapminder, aes(x = gdpPercap)) + geom_histogram()

Producing a barplot of counts only requires an x variable. The counts will be generated by R.

ggplot(gapminder, aes(x=continent)) + geom_bar()

The height of the bars can also be mapped directly to numeric variables in the data frame if the geom_col function is used instead.

In the below plot the axis labels will be messy and difficult to read. This is something that can be customised with some of the ggplot2 options we will explore later.

gapminder2002 <- filter(gapminder, year==2002,continent=="Americas")
ggplot(gapminder2002, aes(x=country,y=gdpPercap)) + geom_col()

Where appropriate, we can add multiple layers of geoms to the plot. For instance, a criticism of the boxplot is that it does not show all the data. We can rectify this by overlaying the individual points.

ggplot(gapminder, aes(x = continent, y=gdpPercap)) + geom_boxplot() + geom_point()

ggplot(gapminder, aes(x = continent, y=gdpPercap)) + geom_boxplot() + geom_jitter(width=0.1)




Exercises

  • The violin plot is a popular alternative to the boxplot. Create a violin plot with geom_violin to visualise the differences in GDP between different continents.
  • Create a subset of the gapminder data frame containing just the rows for your country of birth
  • Has there been an increase in life expectancy over time?
    • visualise the trend using a scatter plot (geom_point), line graph (geom_line) or smoothed line (geom_smooth).
  • What happens when you modify the geom_boxplot example to compare the gdp distributions for different years?
    • Look at the message ggplot2 prints above the plot and try to modify the code to give a separate boxplot for each year



As we have seen already, ggplot offers an interface to create many popular plot types. It is up to the user to decide what the best way to visualise the data.

Customising the plot appearance

Our plots are a bit dreary at the moment, but one way to add colour is to add a col argument to the geom_point function. The value can be any of the pre-defined colour names in R. These are displayed in this handy online reference. Red, Green, Blue of Hex values can also be given.

ggplot(gapminder, aes(x = gdpPercap, y=lifeExp)) + geom_point(col="red")

# Use the Hex codes from Farrow and Ball: https://convertingcolors.com/list/farrow-ball.html
# (cook's blue)

ggplot(gapminder, aes(x = gdpPercap, y=lifeExp)) + geom_point(col="#6A90B4")

However, a powerful feature of ggplot2 is that colours are treated as aesthetics of the plot. In other words we can use a column in our dataset.

Let’s say that we want points on our plot to be coloured according to continent. We add an extra argument to the definition of aesthetics to define the mapping. ggplot2 will even decide on colours and create a legend for us.

ggplot(gapminder, aes(x = gdpPercap, y=lifeExp,col=continent)) + geom_point()

It will even choose a continuous or discrete colour scale based on the data type. We have already seen that ggplot2 is treat our year column as numerical data; which is probably not very useful for visualisation.

ggplot(gapminder, aes(x = gdpPercap, y=lifeExp,col=year)) + geom_point()

We can force ggplot2 to treat year as categorical data by using as.factor when creating the aesthetics.

ggplot(gapminder, aes(x = gdpPercap, y=lifeExp,col=as.factor(year))) + geom_point()

When used in the construction of a boxplot, the col argument will change the colour of the lines. To change the colour of the boxes we have to use fill.

ggplot(gapminder, aes(x = continent, y=gdpPercap,fill=continent)) + geom_boxplot()




Exercises before the next session

  • Using the filter function, find all countries that start with the letter Z
    • Hint: You can find the first letter of each country using the substr function. The mutate function can then be used to add a new column to the data.
  • Use geom_tile to create a heatmap visualising life expectancy over time for European countries. You will need to work out what aesthetics to specify for a geom_tile plot



An example plot is shown on the compiled notes.

Appendix

Adding text to a plot

Annotations can be added to a plot using the flexible annotate function documented here. This presumes that you know the coordinates that you want to add the annotations at.

p<- ggplot(gapminder, aes(x = gdpPercap, y = lifeExp,col=continent)) + geom_point()
p + annotate("text", x = 90000,y=60, label="Some text")

Highlighting particular points of interest using a rectangle.

p + annotate("rect", xmin=25000, xmax=120000,ymin=50,ymax=75,alpha=0.2)

We can also map directly from a column in our dataset to the label aesthetic. However, this will label all the points which is rather cluttered in our case

ggplot(gapminder, aes(x = gdpPercap, y = lifeExp,col=continent,label=country)) + geom_point() + geom_text()

Instead, we could use a different dataset when we create the text labels with geom_text. Here we filter the gapminder dataset to only countries with gdpPercap greater than 57000 and only these points get labeled. We can also set the text colours to a particular value rather than using the original colour mappings for the plot (based on continent).

p + geom_text(data = filter(gapminder, gdpPercap > 57000), 
              aes(x = gdpPercap, y = lifeExp,label=country),col="black")

p + geom_text(data = filter(gapminder, gdpPercap > 25000, lifeExp < 75), 
              aes(x = gdpPercap, y = lifeExp,label=country),col="black",size=3) + annotate("rect", xmin=25000, xmax=120000,ymin=50,ymax=75,alpha=0.2)

Comment about the axis scale

The plot of gdpPercap vs lifeExp on the original scale seems to be influenced by the outlier observations (which we now know are observations from Kuwait). In such situations it may be possible to transform the scale of one axis for visualisation purposes. One such transformation is log10, which we can apply with the scale_x_log10 function. Others include scale_x_log2, scale_x_sqrt and equivalents for the y axis.

p + scale_x_log10()

By splitting the plot by continents we see more clearly which continents have a more linear relationship. At the moment this is useful for visualisation purposes, if we wanted to obtain summaries from the data we would need the techniques in the next section.

p + scale_x_log10() + geom_smooth(method="lm",col="black") + facet_wrap(~continent)

LS0tCnRpdGxlOiAiUiBDcmFzaCBDb3Vyc2UiCmF1dGhvcjogIk1hcmsgRHVubmluZyIKZGF0ZTogJ2ByIGZvcm1hdChTeXMudGltZSgpLCAiTGFzdCBtb2RpZmllZDogJWQgJWIgJVkiKWAnCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNzczogc3R5bGVzaGVldHMvc3R5bGVzLmNzcwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCiMgSW50cm9kdWN0aW9uIHRvIFIgLSBQYXJ0IElJCgo8aW1nIHNyYz0iaW1hZ2VzL2xvZ28tc20ucG5nIiBzdHlsZT0icG9zaXRpb246YWJzb2x1dGU7dG9wOjQwcHg7cmlnaHQ6MTBweDsiIHdpZHRoPSIyMDAiIC8+Cgo8ZGl2IGNsYXNzPSJ3YXJuaW5nIj4KSWYgeW91IGhhdmUgcHJldmlvdXNseS1hdHRlbmRlZCB0aGlzIGNvdXJzZSBhbmQgYXJlIHJldmlld2luZyB0aGUgbm90ZXMsIHBsZWFzZSBiZSBhd2FyZSBvZiBzb21lIHJlLW9yZ2FuaXNhdGlvbiBvZiB0aGUgbWF0ZXJpYWxzIGluIHRoaXMgc2VjdGlvbi4gU29tZSBvZiB0aGUgbGF0ZXIgZXhhbXBsZXMgaGF2ZSBiZWVuIG1vdmVkIHRvIFBhcnQgMy4KPC9kaXY+CgoKV2Ugc2hvdWxkIGhhdmUgbG9hZGVkIHRoZSBgcmVhZHJgIGxpYnJhcnkgYW5kIGltcG9ydGVkIGFuIGV4YW1wbGUgZGF0YXNldCBpbnRvIFIKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkocmVhZHIpCmdhcG1pbmRlciA8LSByZWFkX2NzdigicmF3X2RhdGEvZ2FwbWluZGVyLmNzdiIpCmBgYAoKCiMjIE1hbmlwdWxhdGluZyBDb2x1bW5zCgpXZSBhcmUgZ29pbmcgdG8gdXNlIGZ1bmN0aW9ucyBmcm9tIHRoZSAqKmBkcGx5cmAqKiBwYWNrYWdlIHRvICoqbWFuaXB1bGF0ZSB0aGUgZGF0YSBmcmFtZSoqIHdlIGhhdmUganVzdCBjcmVhdGVkLiBJdCBpcyBwZXJmZWN0bHkgcG9zc2libGUgdG8gd29yayB3aXRoIGRhdGEgZnJhbWVzIHVzaW5nIHRoZSBmdW5jdGlvbnMgcHJvdmlkZWQgYXMgcGFydCBvZiAiKmJhc2UgUioiLiBIb3dldmVyLCBtYW55IGZpbmQgaXQgZWFzeSB0byByZWFkIGFuZCB3cml0ZSBjb2RlIHVzaW5nIGBkcGx5cmAuCgpUaGVyZSBhcmUgKiptYW55IG1vcmUgZnVuY3Rpb25zIGF2YWlsYWJsZSBpbiBgZHBseXJgKiogdGhhbiB3ZSB3aWxsIGNvdmVyIHRvZGF5LiBBbiBvdmVydmlldyBvZiBhbGwgZnVuY3Rpb25zIGlzIGdpdmVuIGluIGEgY2hlYXRzaGVldC4KCjxkaXYgY2xhc3M9ImluZm9ybWF0aW9uIj4KCi0gW2RwbHlyIGNoZWF0c2hlZXRdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE1LzAyL2RhdGEtd3JhbmdsaW5nLWNoZWF0c2hlZXQucGRmKS4gVGhlIGNoZWF0c2hlZXQgaXMgYWxzbyBhdmFpbGFibGUgdGhyb3VnaCB0aGUgUlN0dWRpbyBIZWxwIG1lbnUuCgo8L2Rpdj4KCkJlZm9yZSB1c2luZyBhbnkgb2YgdGhlc2UgZnVuY3Rpb25zLCB3ZSBuZWVkIHRvIGxvYWQgdGhlIGxpYnJhcnk6LSAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZHBseXIpCmBgYAoKCiMjIyBgc2VsZWN0YGluZyBjb2x1bW5zCgoKV2UgY2FuICoqYWNjZXNzIHRoZSBjb2x1bW5zKiogb2YgYSBkYXRhIGZyYW1lIHVzaW5nIHRoZSBgc2VsZWN0YCBmdW5jdGlvbi4gCgojIyMjIGJ5IG5hbWUKCkZpcnN0bHksIHdlIGNhbiBzZWxlY3QgY29sdW1uIGJ5IG5hbWUsIGJ5IGFkZGluZyBiYXJlIGNvbHVtbiBuYW1lcyAoaS5lLiBub3QgcmVxdWlyaW5nIHF1b3RlIG1hcmtzIGFyb3VuZCB0aGUgbmFtZSkgYWZ0ZXIgdGhlIG5hbWUgb2YgdGhlIGRhdGEgZnJhbWUsIHNlcGFyYXRlZCBieSBhIGAsYCAuIAoKYGBge3J9CnNlbGVjdChnYXBtaW5kZXIsIGNvdW50cnksIGNvbnRpbmVudCkKYGBgCgo8ZGl2IGNsYXNzPSJ3YXJuaW5nIj4KQXMgd2UgaGF2ZSB0byB0eXBlIHRoZSBjb2x1bW4gbmFtZXMgbWFudWFsbHkgKG5vIGF1dG8tY29tcGxldGUhKSwgd2UgaGF2ZSB0byBtYWtlIHN1cmUgd2UgdHlwZSB0aGUgbmFtZSBleGFjdGx5IGFzIGl0IGFwcGVhcnMgaW4gdGhlIGRhdGEuIElmIGBzZWxlY3RgIHNlZXMgYSBuYW1lIHRoYXQgZG9lc24ndCBleGlzdCBpbiB0aGUgZGF0YSBmcmFtZSBpdCBzaG91bGQgZ2l2ZSBhbiBpbmZvcm1hdGl2ZSBtZXNzYWdlCmBFcnJvcjogQ2FuJ3Qgc3Vic2V0IGNvbHVtbnMgdGhhdCBkb24ndCBleGlzdC5gCjwvZGl2PgoKV2UgY2FuIGFsc28gb21pdCBjb2x1bW5zIGZyb20gdGhlIG91cHV0IGJ5IHB1dHRpbmcgYSBtaW51cyAoYC1gKSBpbiBmcm9udCBvZiB0aGUgY29sdW1uIG5hbWUuIE5vdGUgdGhhdCB0aGlzIGlzIG5vdCB0aGUgc2FtZSBhcyByZW1vdmluZyB0aGUgY29sdW1uIGZyb20gdGhlIGRhdGEgcGVybWFuZW50bHkuCgpgYGB7cn0Kc2VsZWN0KGdhcG1pbmRlciwgLWNvdW50cnkpCmBgYAoKIyMjIyByYW5nZSBvZiBjb2x1bW5zCgpBIHJhbmdlIG9mIGNvbHVtbnMgY2FuIGJlIHNlbGVjdGVkIGJ5IHRoZSBgOmAgb3BlcmF0b3IuCgpgYGB7cn0Kc2VsZWN0KGdhcG1pbmRlciwgbGlmZUV4cDpnZHBQZXJjYXApCmBgYAoKIyMjIyBoZWxwZXIgZnVuY3Rpb25zIAoKVGhlcmUgYXJlIGEgbnVtYmVyIG9mIGhlbHBlciBmdW5jdGlvbnMgY2FuIGJlIGVtcGxveWVkIGlmIHdlIGFyZSB1bnN1cmUgYWJvdXQgdGhlIGV4YWN0IG5hbWUgb2YgdGhlIGNvbHVtbi4KCmBgYHtyfQpzZWxlY3QoZ2FwbWluZGVyLCBzdGFydHNfd2l0aCgiY28iKSkKc2VsZWN0KGdhcG1pbmRlciwgY29udGFpbnMoImxpZmUiKSkKIyBzZWxlY3RpbmcgdGhlIGxhc3QgYW5kIHBlbnVsdGltYXRlIGNvbHVtbnMKc2VsZWN0KGdhcG1pbmRlciwgbGFzdF9jb2woMSksbGFzdF9jb2woKSkKYGBgCgpJdCBpcyBhbHNvIHBvc3NpYmxlIHRvIHVzZSB0aGUgY29sdW1uIG51bWJlciBpbiB0aGUgc2VsZWN0aW9uLgoKYGBge3J9CnNlbGVjdChnYXBtaW5kZXIsIDQ6NikKYGBgCgo8ZGl2IGNsYXNzPSJleGVyY2lzZSI+CioqUXVlc3Rpb24qKjogV2h5IG1pZ2h0IHVzaW5nIHRoZSBudW1iZXIgb2YgdGhlIGNvbHVtbiBzb21ldGltZXMgYmUgcHJvYmxlbWF0aWM/IENvbnNpZGVyIHdoYXQgbWlnaHQgaGFwcGVuIGlmIHlvdSB3cm90ZSBjb2RlIHRvIHNlbGVjdCB0aGUgbGFzdCBjb2x1bW4gb2YgZGF0YSBmcm9tIGEgZmlsZSB1c2luZyB0aGUgY29sdW1uIG51bWJlci4gV2hhdCB3b3VsZCBoYXBwZW4gaWYgdGhlIG51bWJlciBvZiBjb2x1bW5zIGluIHRoZSBmaWxlIHdhcyBub3QgY29uc3RhbnQ/CjwvZGl2PgoKVGhlIGBzZWxlY3RgIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHdpdGgganVzdCBhIHNpbmdsZSBjb2x1bW4gbmFtZSAtIGluIGEgc2ltaWxhciBtYW5uZXIgdG8gdGhlIGAkYCBvcGVyYXRpb24gd2Ugc2F3IGxhc3QgdGltZS4gSG93ZXZlciwgYHNlbGVjdGAgYWx3YXlzIHJldHVybnMgYSAqZGF0YSBmcmFtZSogd2hlcmVhcyBgJGAgZ2l2ZXMgYSB2ZWN0b3IuIENvbXBhcmUgdGhlIG91dHB1dCBvZiB0aGUgZm9sbG93aW5nIGNvZGUgY2h1bmtzCgpgYGB7cn0Kc2VsZWN0KGdhcG1pbmRlciwgcG9wKQpgYGAKCmBgYHtyIGV2YWw9RkFMU0V9CmdhcG1pbmRlciRwb3AKYGBgCgpUaGUgY29uc2VxdWVuY2Ugb2YgdGhpcyBpcyB0aGF0IHlvdSBjYW5ub3QgdXNlIGZ1bmN0aW9ucyBzdWNoIGFzIGBtZWFuYCBpbiBjb21iaW5hdGlvbiB3aXRoIGBzZWxlY3RgCgpgYGB7ciBldmFsPUZBTFNFfQpwb3BzIDwtIHNlbGVjdChnYXBtaW5kZXIsIHBvcCkKbWVhbihwb3BzKQpgYGAKCkluIHRoZSBuZXh0IHNlc3Npb24gd2Ugd2lsbCBzZWUgaG93IHRvIGNhbGN1bGF0ZSBzdW1tYXJ5IHN0YXRpc3RpY3Mgb24gcGFydGljdWxhciBjb2x1bW5zIGluIG91ciBkYXRhLiBGb3Igbm93LCBhIHVzZWZ1bCBmdW5jdGlvbiBpcyBgcHVsbGAgdGhhdCB3aWxsIHJldHVybiB0aGUgY29ycmVjdCB0eXBlIG9mIGRhdGEgcmVxdWlyZWQgZm9yIGEgZnVuY3Rpb24gc3VjaCBhcyBgbWVhbmAuCgpgYGB7cn0KcG9wcyA8LSBwdWxsKGdhcG1pbmRlcixwb3ApCm1lYW4ocG9wcykKYGBgCgoKCiMjIFJlc3RyaWN0aW5nIHJvd3Mgd2l0aCBmaWx0ZXIKClNvIGZhciB3ZSBoYXZlIGJlZW4gcmV0dXJuaW5nIGFsbCB0aGUgcm93cyBpbiB0aGUgb3V0cHV0LiBXZSBjYW4gdXNlIHdoYXQgd2UgY2FsbCBhICoqbG9naWNhbCB0ZXN0KiogdG8gKipmaWx0ZXIgdGhlIHJvd3MqKiBpbiBhIGRhdGEgZnJhbWUuIFRoaXMgbG9naWNhbCB0ZXN0IHdpbGwgYmUgYXBwbGllZCB0byBlYWNoIHJvdyBhbmQgZ2l2ZSBlaXRoZXIgYSBgVFJVRWAgb3IgYEZBTFNFYCByZXN1bHQuIFdoZW4gZmlsdGVyaW5nLCAqKm9ubHkgcm93cyB3aXRoIGEgYFRSVUVgIHJlc3VsdCBnZXQgcmV0dXJuZWQqKi4KCkZvciBleGFtcGxlIHdlIGZpbHRlciBmb3Igcm93cyB3aGVyZSB0aGUgYGxpZmVFeHBgIHZhcmlhYmxlIGlzIGxlc3MgdGhhbiA0MC4gCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgbGlmZUV4cCA8IDQwKQpgYGAKCkludGVybmFsbHksIFIgY3JlYXRlcyBhICp2ZWN0b3IqIG9mIGBUUlVFYCBvciBgRkFMU0VgOyBvbmUgZm9yIGVhY2ggcm93IGluIHRoZSBkYXRhIGZyYW1lLiBUaGlzIGlzIHRoZW4gdXNlZCB0byBkZWNpZGUgd2hpY2ggcm93cyB0byBkaXNwbGF5LgoKVGVzdGluZyBmb3IgZXF1YWxpdHkgY2FuIGJlIGRvbmUgdXNpbmcgYD09YC4gVGhpcyB3aWxsIG9ubHkgZ2l2ZSBgVFJVRWAgZm9yIGVudHJpZXMgdGhhdCBhcmUgKmV4YWN0bHkqIHRoZSBzYW1lIGFzIHRoZSB0ZXN0IHN0cmluZy4gCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgY291bnRyeSA9PSAiWmFtYmlhIikKYGBgCgpOLkIuIEZvciBwYXJ0aWFsIG1hdGNoZXMsIHRoZSBgZ3JlcGxgIGZ1bmN0aW9uIGFuZCAvIG9yICpyZWd1bGFyIGV4cHJlc3Npb25zKiAoaWYgeW91IGtub3cgdGhlbSkgY2FuIGJlIHVzZWQuCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgZ3JlcGwoImxhbmQiLCBjb3VudHJ5KSkKYGBgCgpXZSBjYW4gYWxzbyB0ZXN0IGlmIHJvd3MgYXJlICpub3QqIGVxdWFsIHRvIGEgdmFsdWUgdXNpbmcgIGAhPWAgCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgY29udGluZW50ICE9ICJFdXJvcGUiKQpgYGAKCiMjIyB0ZXN0aW5nIG1vcmUgdGhhbiBvbmUgY29uZGl0aW9uCgpUaGVyZSBhcmUgYSBjb3VwbGUgb2Ygd2F5cyBvZiB0ZXN0aW5nIGZvciBtb3JlIHRoYW4gb25lIHBhdHRlcm4uIFRoZSBmaXJzdCB1c2VzIGFuICpvciogYHxgIHN0YXRlbWVudC4gaS5lLiB0ZXN0aW5nIGlmIHRoZSB2YWx1ZSBvZiBgY291bnRyeWAgaXMgYFphbWJpYWAgKm9yKiB0aGUgdmFsdWUgaXMgYFppbWJhYndlYC4gUmVtZW1iZXIgdG8gdXNlIGRvdWJsZSBgPWAgc2lnbiB0byB0ZXN0IGZvciBzdHJpbmcgZXF1YWxpdHk7IGA9PWAuCgoKYGBge3J9CmZpbHRlcihnYXBtaW5kZXIsIGNvdW50cnkgPT0gIlphbWJpYSIgfCBjb3VudHJ5ID09ICJaaW1iYWJ3ZSIpCmBgYAoKClRoZSBgJWluJWAgZnVuY3Rpb24gaXMgYSBjb252ZW5pZW50IGZ1bmN0aW9uIGZvciB0ZXN0aW5nIHdoaWNoIGl0ZW1zIGluIGEgdmVjdG9yIGNvcnJlc3BvbmQgdG8gYSBkZWZpbmVkIHNldCBvZiB2YWx1ZXMuCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgY291bnRyeSAlaW4lIGMoIlphbWJpYSIsICJaaW1iYWJ3ZSIpKQpgYGAKCgpXZSBjYW4gcmVxdWlyZSB0aGF0IGJvdGggdGVzdHMgYXJlIGBUUlVFYCwgIGUuZy4gd2hpY2ggeWVhcnMgaW4gWmFtYmlhIGhhZCBhIGxpZmUgZXhwZWN0YW5jeSBsZXNzIHRoYW4gNDAsIGJ5IHNlcGFyYXRpbmcgY29uZGl0aW9uYWwgc3RhdGVtZW50cyBieSBhIGAsYC4gVGhpcyBwZXJmb3JtcyBhbiAqQU5EKiB0ZXN0IHNvIG9ubHkgcm93cyB0aGF0IG1lZXQgYm90aCBjb25kaXRpb25zIGFyZSByZXR1cm5lZC4KCmBgYHtyfQpmaWx0ZXIoZ2FwbWluZGVyLCBjb3VudHJ5ID09ICJaYW1iaWEiLCBsaWZlRXhwIDwgNDApCmBgYAoKPGRpdiBjbGFzcz0iaW5mb3JtYXRpb24iPgpZb3UgbWF5IGhhdmUgbm90aWNlZCB0aGF0IGBmaWx0ZXJgIHdpbGwgYWx3YXlzIG91dHB1dCB0aGUgc2FtZSBudW1iZXIgb2YgY29sdW1ucyBhcyB0aGUgaW5wdXQgZGF0YSBmcmFtZS4gYGZpbHRlcmAgbmV2ZXIgY2hhbmdlcyB0aGUgY29sdW1ucyB0aGF0IGFyZSBkaXNwbGF5ZWQuIFRoZXJlIGFyZSB3YXlzIG9mIHVzaW5nIGBmaWx0ZXJgIGluIGNvbmp1bmN0aW9uIHdpdGggYHNlbGVjdGAgYXMgd2Ugd2lsbCBzZWUgbGF0ZXIuCjwvZGl2PgoKKioqKioqCioqKioqKgoqKioqKioKCiMjIEV4ZXJjaXNlCjxkaXYgY2xhc3M9ImV4ZXJjaXNlIj4KLSBDcmVhdGUgYSBzdWJzZXQgb2YgdGhlIGRhdGEgd2hlcmUgdGhlIHBvcHVsYXRpb24gbGVzcyB0aGFuIGEgbWlsbGlvbiBpbiB0aGUgeWVhciAyMDAyCi0gQ3JlYXRlIGEgc3Vic2V0IG9mIHRoZSBkYXRhIHdoZXJlIHRoZSBsaWZlIGV4cGVjdGFuY3kgaXMgZ3JlYXRlciB0aGFuIDc1IGluIHRoZSB5ZWFycyBwcmlvciB0byAxOTg3Ci0gQ3JlYXRlIGEgc3Vic2V0IG9mIHRoZSBFdXJvcGVhbiBkYXRhIHdoZXJlIHRoZSBsaWZlIGV4cGVjdGFuY3kgaXMgYmV0d2VlbiA3NSBhbmQgODAgaW4gdGhlIHllYXJzIDIwMDIgb3IgMjAwNy4KLSBJZiB5b3UgYXJlIGZpbmlzaGVkIHdpdGggdGhlc2UsIHRyeSB0byBleHBsb3JlIGFsdGVybmF0aXZlIHdheXMgb2YgcGVyZm9ybWluZyB0aGUgc2FtZSBmaWx0ZXJpbmcKPC9kaXY+CioqKioqKgoqKioqKioKKioqKioqCgoKIyMgTWFuaXB1bGF0aW5nIHRoZSB2YWx1ZXMgaW4gYSBjb2x1bW4gLyBjcmVhdGluZyBuZXcgY29sdW1ucwoKQXMgd2VsbCBhcyBzZWxlY3RpbmcgZXhpc3RpbmcgY29sdW1ucyBpbiB0aGUgZGF0YSBmcmFtZSwgbmV3IGNvbHVtbnMgY2FuIGJlIGNyZWF0ZWQgYW5kIGV4aXN0aW5nIG9uZXMgbWFuaXB1bGF0ZWQgdXNpbmcgdGhlIGBtdXRhdGVgIGZ1bmN0aW9uLiBUeXBpY2FsbHkgYSBmdW5jdGlvbiBvciBtYXRoZW1hdGljYWwgZXhwcmVzc2lvbiBpcyBhcHBsaWVkIHRvIGRhdGEgaW4gZXhpc3RpbmcgY29sdW1ucyBieSByb3csIGFuZCB0aGUgcmVzdWx0IGVpdGhlciBzdG9yZWQgaW4gYSBuZXcgY29sdW1uIG9yIHJlYXNzaWduZWQgdG8gYW4gZXhpc3Rpbmcgb25lLiBJbiBvdGhlciB3b3JkcywgdGhlIG51bWJlciBvZiB2YWx1ZXMgcmV0dXJuZWQgYnkgdGhlIGZ1bmN0aW9uIG11c3QgYmUgdGhlIHNhbWUgYXMgdGhlIG51bWJlciBvZiBpbnB1dCB2YWx1ZXMuIE11bHRpcGxlIG11dGF0aW9ucyBjYW4gYmUgcGVyZm9ybWVkIGluIG9uZSBsaW5lIG9mIGNvZGUuCgpIZXJlLCB3ZSBjcmVhdGUgYSBuZXcgY29sdW1uIG9mIHBvcHVsYXRpb24gaW4gbWlsbGlvbnMgKGBQb3BJbk1pbGxpb25zYCkgYW5kIHJvdW5kIGBsaWZlRXhwYCB0byB0aGUgbmVhcmVzdCBpbnRlZ2VyLgoKYGBge3J9Cm11dGF0ZShnYXBtaW5kZXIsIFBvcEluTWlsbGlvbnMgPSBwb3AgLyAxZTYsCiAgICAgICBsaWZlRXhwID0gcm91bmQobGlmZUV4cCkpCmBgYApOb3RlIHRoYXQgd2UgaGF2ZW4ndCBhY3R1YWxseSBjaGFuZ2VkIG91ciBgZ2FwbWluZGVyYCBkYXRhIGZyYW1lLiBJZiB3ZSB3YW50ZWQgdG8gbWFrZSB0aGUgbmV3IGNvbHVtbnMgcGVybWFuZW50LCB3ZSB3b3VsZCBoYXZlIHRvIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZS4KCklmIHdlIHdhbnQgdG8gcmVuYW1lIGV4aXN0aW5nIGNvbHVtbnMsIGFuZCBub3QgY3JlYXRlIGFueSBleHRyYSBjb2x1bW5zLCB3ZSBjYW4gdXNlIHRoZSBgcmVuYW1lYCBmdW5jdGlvbi4KCmBgYHtyfQpyZW5hbWUoZ2FwbWluZGVyLCBHRFA9Z2RwUGVyY2FwKQpgYGAKCgojIyBPcmRlcmluZyAvIHNvcnRpbmcKClRoZSB3aG9sZSBkYXRhIGZyYW1lIGNhbiBiZSByZS1vcmRlcmVkIGFjY29yZGluZyB0byB0aGUgdmFsdWVzIGluIG9uZSBjb2x1bW4gdXNpbmcgdGhlIGBhcnJhbmdlYCBmdW5jdGlvbi4gU28gdG8gb3JkZXIgdGhlIHRhYmxlIGFjY29yZGluZyB0byBwb3B1bGF0aW9uIHNpemU6LQoKYGBge3J9CmFycmFuZ2UoZ2FwbWluZGVyLCBwb3ApCmBgYAoKClRoZSBkZWZhdWx0IGlzIGBzbWFsbGVzdCAtLT4gbGFyZ2VzdGAgYnV0IHdlIGNhbiBjaGFuZ2UgdGhpcyB1c2luZyB0aGUgYGRlc2NgIGZ1bmN0aW9uCgpgYGB7cn0KYXJyYW5nZShnYXBtaW5kZXIsIGRlc2MocG9wKSkKYGBgCgpgYXJyYW5nZWAgYWxzbyB3b3JrcyBvbiBjaGFyYWN0ZXIgdmVjdG9ycywgYXJyYW5nZSB0aGVtIGFscGhhLW51bWVyaWNhbGx5LgoKYGBge3J9CmFycmFuZ2UoZ2FwbWluZGVyLCBkZXNjKGNvdW50cnkpKQpgYGAKCldlIGNhbiBldmVuIG9yZGVyIGJ5IG1vcmUgdGhhbiBvbmUgY29uZGl0aW9uCgpgYGB7cn0KYXJyYW5nZShnYXBtaW5kZXIsIHllYXIsIHBvcCkKYGBgCgoKYGBge3J9CmFycmFuZ2UoZ2FwbWluZGVyLCB5ZWFyLCBjb250aW5lbnQsIHBvcCkKYGBgCgojIyBTYXZpbmcgZGF0YSBmcmFtZXMKCkEgZmluYWwgcG9pbnQgb24gZGF0YSBmcmFtZXMgaXMgdGhhdCB3ZSBjYW4gd3JpdGUgdGhlbSB0byBkaXNrIG9uY2Ugd2UgaGF2ZSBkb25lIG91ciBkYXRhIHByb2Nlc3NpbmcuIAoKTGV0J3MgY3JlYXRlIGEgZm9sZGVyIGluIHdoaWNoIHRvIHN0b3JlIHN1Y2ggcHJvY2Vzc2VkLCBhbmFseXNpcyByZWFkeSBkYXRhCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZGlyLmNyZWF0ZSgib3V0X2RhdGEiLHNob3dXYXJuaW5ncyA9IEZBTFNFKQojIyBzaG93V2FybmluZ3Mgd2lsbCBzdG9wIGEgbWVzc2FnZSBmcm9tIGFwcGVhcmluZyBpZiB0aGUgZGlyZWN0b3J5IGFscmVhZHkgZXhpc3RzCmBgYAoKCmBgYHtyfQpieVdlYWx0aCA8LSBhcnJhbmdlKGdhcG1pbmRlciwgZGVzYyhnZHBQZXJjYXApKQojIGNoZWNrIHRoZSBvdXRwdXQgYmVmb3JlIHdyaXRpbmcKaGVhZChieVdlYWx0aCkKd3JpdGVfY3N2KGJ5V2VhbHRoLCBmaWxlID0gIm91dF9kYXRhL2J5X3dlYWx0aC5jc3YiKQpgYGAKCldlIHdpbGwgbm93IHRyeSBhbiBleGVyY2lzZSB0aGF0IGludm9sdmVzIHVzaW5nIHNldmVyYWwgc3RlcHMgb2YgdGhlc2Ugb3BlcmF0aW9ucwoKKioqKioqCioqKioqKgoqKioqKioKCiMjIEV4ZXJjaXNlCjxkaXYgY2xhc3M9ImV4ZXJjaXNlIj4KLSBGaWx0ZXIgdGhlIGRhdGEgdG8gaW5jbHVkZSBqdXN0IG9ic2VydmF0aW9ucyBmcm9tIHRoZSB5ZWFyIDIwMDIKLSBSZS1hcnJhbmdlIHRoZSB0YWJsZSBzbyB0aGF0IHRoZSBjb3VudHJpZXMgZnJvbSBlYWNoIGNvbnRpbmVudCBhcmUgb3JkZXJlZCBhY2NvcmRpbmcgdG8gZGVjcmVhc2luZyB3ZWFsdGguIGkuZS4gdGhlIHdlYWx0aGllc3QgY291bnRyaWVzIGZpcnN0Ci0gU2VsZWN0IGFsbCB0aGUgY29sdW1ucyBhcGFydCBmcm9tIHllYXIgCi0gV3JpdGUgdGhlIGRhdGEgZnJhbWUgb3V0IHRvIGEgZmlsZSBpbiBgb3V0X2RhdGEvYCBmb2xkZXIKPC9kaXY+CmBgYHtyIGVjaG89RkFMU0V9CgoKCmBgYAoKCioqKioqKgoqKioqKioKKioqKioqCgoKIyMgIlBpcGluZyIKCkFzIGhhdmUgaGF2ZSBqdXN0IHNlZW4sIHdlIHdpbGwgb2Z0ZW4gbmVlZCB0byBwZXJmb3JtIGFuIGFuYWx5c2lzLCBvciBjbGVhbiBhIGRhdGFzZXQsIHVzaW5nIHNldmVyYWwgYGRwbHlyYCBmdW5jdGlvbnMgaW4gc2VxdWVuY2UuIGUuZy4gZmlsdGVyaW5nLCBtdXRhdGluZywgdGhlbiBzZWxlY3RpbmcgY29sdW1ucyBvZiBpbnRlcmVzdCAocG9zc2libHkgZm9sbG93ZWQgYnkgcGxvdHRpbmcgLSBzZWUgc2hvcnRseSkuCgpBcyBhIHNtYWxsIGV4YW1wbGU7IGlmIHdlIHdhbnRlZCB0byBmaWx0ZXIgb3VyIHJlc3VsdHMgdG8ganVzdCBFdXJvcGUgdGhlIGBjb250aW5lbnRgIGNvbHVtbiBiZWNvbWVzIHJlZHVuZGFudCBzbyB3ZSBtaWdodCBhcyB3ZWxsIHJlbW92ZSBpdC4KClRoZSBmb2xsb3dpbmcgaXMgcGVyZmVjdGx5IHZhbGlkIFIgY29kZSwgYnV0IGludml0ZXMgdGhlIHVzZXIgdG8gbWFrZSBtaXN0YWtlcyBhbmQgY29weS1hbmQtcGFzdGUgZXJyb3Mgd2hlbiB3cml0aW5nIGl0LiBXZSBhbHNvIGhhdmUgdG8gY3JlYXRlIG11bHRpcGxlIGNvcGllcyBvZiB0aGUgc2FtZSBkYXRhIGZyYW1lLgoKYGBge3J9CnRtcCA8LSBmaWx0ZXIoZ2FwbWluZGVyLCBjb250aW5lbnQgPT0gIkV1cm9wZSIpCnRtcDIgPC0gc2VsZWN0KHRtcCwgLWNvbnRpbmVudCkKdG1wMgpgYGAKCihUaG9zZSBmYW1pbGlhciB3aXRoIFVuaXggbWF5IHJlY2FsbCB0aGF0IGNvbW1hbmRzIGNhbiBiZSBqb2luZWQgd2l0aCBhIHBpcGU7IGB8YCkKCkluIFIsIGBkcGx5cmAgY29tbWFuZHMgdG8gYmUgbGlua2VkIHRvZ2V0aGVyIGFuZCBmb3JtIGEgd29ya2Zsb3cuIFRoZSBzeW1ib2wgYCU+JWAgaXMgcHJvbm91bmNlZCAqKnRoZW4qKi4gV2l0aCBhIGAlPiUgYCB0aGUgaW5wdXQgdG8gYSBmdW5jdGlvbiBpcyBhc3N1bWVkIHRvIGJlIHRoZSBvdXRwdXQgb2YgdGhlIHByZXZpb3VzIGxpbmUuIEFsbCB0aGUgYGRwbHlyYCBmdW5jdGlvbnMgdGhhdCB3ZSBoYXZlIHNlZW4gc28gZmFyIHRha2UgYSBkYXRhIGZyYW1lIGFzIGFuIGlucHV0IGFuZCByZXR1cm4gYW4gYWx0ZXJlZCBkYXRhIGZyYW1lIGFzIGFuIG91dHB1dCwgc28gYXJlIGFtZW5hYmxlIHRvIHRoaXMgdHlwZSBvZiBwcm9ncmFtbWluZy4KClRoZSBleGFtcGxlIHdlIGdhdmUgb2YgZmlsdGVyaW5nIGp1c3QgdGhlIEV1cm9wZWFuIGNvdW50cmllcyBhbmQgcmVtb3ZpbmcgdGhlIGBjb250aW5lbnRgIGNvbHVtbiBiZWNvbWVzOi0KCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgY29udGluZW50PT0iRXVyb3BlIikgJT4lIAogIHNlbGVjdCgtY29udGluZW50KQpgYGAKCgoqKioqKioKKioqKioqCioqKioqKgoKIyMjIyBFeGVyY2lzZQoKPGRpdiBjbGFzcz0iZXhlcmNpc2UiPgotIFJlLXdyaXRlIHlvdXIgc29sdXRpb24gdG8gdGhlIHByZXZpb3VzIGV4ZXJjaXNlLCBidXQgdXNpbmcgdGhlIGAgJT4lIGAgc3ltYm9sCgo8L2Rpdj4KCmBgYHtyfQoKCmBgYAoKCioqKioqKgoqKioqKioKKioqKioqCgojIFBsb3R0aW5nCgpUaGUgUiBsYW5ndWFnZSBoYXMgZXh0ZW5zaXZlIGdyYXBoaWNhbCBjYXBhYmlsaXRpZXMuCgpHcmFwaGljcyBpbiBSIG1heSBiZSBjcmVhdGVkIGJ5IG1hbnkgZGlmZmVyZW50IG1ldGhvZHMgaW5jbHVkaW5nIGJhc2UgZ3JhcGhpY3MgYW5kIG1vcmUgYWR2YW5jZWQgcGxvdHRpbmcgcGFja2FnZXMgc3VjaCBhcyBsYXR0aWNlLgoKVGhlIGBnZ3Bsb3QyYCBwYWNrYWdlIHdhcyBjcmVhdGVkIGJ5IEhhZGxleSBXaWNraGFtIGFuZCBwcm92aWRlcyBhIGludHVpdGl2ZSBwbG90dGluZyBzeXN0ZW0gdG8gcmFwaWRseSBnZW5lcmF0ZSBwdWJsaWNhdGlvbiBxdWFsaXR5IGdyYXBoaWNzLgoKYGdncGxvdDJgIGJ1aWxkcyBvbiB0aGUgY29uY2VwdCBvZiB0aGUg4oCcR3JhbW1hciBvZiBHcmFwaGljc+KAnSAoV2lsa2luc29uIDIwMDUsIEJlcnRpbiAxOTgzKSB3aGljaCBkZXNjcmliZXMgYSBjb25zaXN0ZW50IHN5bnRheCBmb3IgdGhlIGNvbnN0cnVjdGlvbiBvZiBhIHdpZGUgcmFuZ2Ugb2YgY29tcGxleCBncmFwaGljcyBieSBhIGNvbmNpc2UgZGVzY3JpcHRpb24gb2YgdGhlaXIgY29tcG9uZW50cy4KCiMjIFdoeSB1c2UgZ2dwbG90Mj8KClRoZSBzdHJ1Y3R1cmVkIHN5bnRheCBhbmQgaGlnaCBsZXZlbCBvZiBhYnN0cmFjdGlvbiB1c2VkIGJ5IGdncGxvdDIgc2hvdWxkIGFsbG93IGZvciB0aGUgdXNlciB0byBjb25jZW50cmF0ZSBvbiB0aGUgdmlzdWFsaXNhdGlvbnMgaW5zdGVhZCBvZiBjcmVhdGluZyB0aGUgdW5kZXJseWluZyBjb2RlLgoKT24gdG9wIG9mIHRoaXMgY2VudHJhbCBwaGlsb3NvcGh5IGdncGxvdDIgaGFzOgoKLSBJbmNyZWFzZWQgZmxleGliaWxpdHkgb3ZlciBtYW55IHBsb3R0aW5nIHN5c3RlbXMuCi0gQW4gYWR2YW5jZWQgdGhlbWUgc3lzdGVtIGZvciBwcm9mZXNzaW9uYWwvcHVibGljYXRpb24gbGV2ZWwgZ3JhcGhpY3MuCi0gTGFyZ2UgZGV2ZWxvcGVyIGJhc2Ug4oCTIE1hbnkgbGlicmFyaWVzIGV4dGVuZGluZyBpdHMgZmxleGliaWxpdHkuCi0gTGFyZ2UgdXNlciBiYXNlIOKAkyBHcmVhdCBkb2N1bWVudGF0aW9uIGFuZCBhY3RpdmUgbWFpbGluZyBsaXN0LgoKPGRpdiBjbGFzcz0iaW5mb3JtYXRpb24iPgoKSXQgaXMgYWx3YXlzIHVzZWZ1bCB0byB0aGluayBhYm91dCB0aGUgbWVzc2FnZSB5b3Ugd2FudCB0byBjb252ZXkgYW5kIHRoZSBhcHByb3ByaWF0ZSBwbG90IGJlZm9yZSB3cml0aW5nIGFueSBSIGNvZGUuIFJlc291cmNlcyBsaWtlIFtkYXRhLXRvLXZpei5jb21dKGh0dHBzOi8vd3d3LmRhdGEtdG8tdml6LmNvbS8pIHNob3VsZCBoZWxwLgo8L2Rpdj4KCldpdGggc29tZSBwcmFjdGljZSwgYGdncGxvdDJgIG1ha2VzIGl0IGVhc2llciB0byBnbyBmcm9tIHRoZSBmaWd1cmUgeW91IGFyZSBpbWFnaW5pbmcgaW4gb3VyIGhlYWQgKG9yIG9uIHBhcGVyKSB0byBhIHB1YmxpY2F0aW9uLXJlYWR5IGltYWdlIGluIFIuCgo8ZGl2IGNsYXNzPSJpbmZvcm1hdGlvbiI+CkFzIHdpdGggYGRwbHlyYCwgd2Ugd29uJ3QgaGF2ZSB0aW1lIHRvIGNvdmVyIGFsbCBkZXRhaWxzIG9mIGBnZ3Bsb3QyYC4gVGhpcyBpcyBob3dldmVyIGEgdXNlZnVsIFtjaGVhdHNoZWV0XShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcnN0dWRpby9jaGVhdHNoZWV0cy9tYWluL2RhdGEtdmlzdWFsaXphdGlvbi5wZGYpIHRoYXQgY2FuIGJlIHByaW50ZWQgYXMgYSByZWZlcmVuY2UuIFRoZSBjaGVhdHNoZWV0IGlzIGFsc28gYXZhaWxhYmxlIHRocm91Z2ggdGhlIFJTdHVkaW8gSGVscCBtZW51Lgo8L2Rpdj4KCiMjIEJhc2ljIHBsb3QgdHlwZXMKCkEgcGxvdCBpbiBgZ2dwbG90MmAgaXMgY3JlYXRlZCB3aXRoIHRoZSBmb2xsb3dpbmcgdHlwZSBvZiBjb21tYW5kCgpgYGAKZ2dwbG90KGRhdGEgPSA8REFUQT4sIG1hcHBpbmcgPSBhZXMoPE1BUFBJTkdTPikpICsgIDxHRU9NX0ZVTkNUSU9OPigpCmBgYAoKU28gd2UgbmVlZCB0byBzcGVjaWZ5CgotIFRoZSBkYXRhIHRvIGJlIHVzZWQgaW4gZ3JhcGgKLSBNYXBwaW5ncyBvZiBkYXRhIHRvIHRoZSBncmFwaCAoKmFlc3RoZXRpYyogbWFwcGluZykKLSBXaGF0IHR5cGUgb2YgZ3JhcGggd2Ugd2FudCB0byB1c2UgKFRoZSAqZ2VvbSogdG8gdXNlKS4KCkxldHMgc2F5IHRoYXQgd2Ugd2FudCB0byBleHBsb3JlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBHRFAgYW5kIExpZmUgRXhwZWN0YW5jeS4gV2UgbWlnaHQgc3RhcnQgd2l0aCB0aGUgaHlwb3RoZXNpcyB0aGF0IHJpY2hlciBjb3VudHJpZXMgaGF2ZSBoaWdoZXIgbGlmZSBleHBlY3RhbmN5LiBBIHNlbnNpYmxlIGNob2ljZSBvZiBwbG90IHdvdWxkIGJlIGEgKnNjYXR0ZXIgcGxvdCogd2l0aCBnZHAgb24gdGhlIHgtYXhpcyBhbmQgbGlmZSBleHBlY3RhbmN5IG9uIHRoZSB5LWF4aXMuCgpUaGUgZmlyc3Qgc3RhZ2UgaXMgdG8gc3BlY2lmeSBvdXIgZGF0YXNldAoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXIpCmBgYAoKRm9yIHRoZSBhZXN0aGV0aWNzLCBhcyBhIGJhcmUgbWluaW11bSB3ZSB3aWxsIG1hcCB0aGUgYGdkcFBlcmNhcGAgYW5kIGBsaWZlRXhwYCB0byB0aGUgeC0gYW5kIHktYXhpcyBvZiB0aGUgcGxvdC4gU29tZSBwcm9ncmVzcyBpcyBtYWRlOyB3ZSBhdCBsZWFzdCBnZXQgYXhlcwoKYGBge3J9CmdncGxvdChkYXRhID0gZ2FwbWluZGVyLGFlcyh4PWdkcFBlcmNhcCwgeT1saWZlRXhwKSkKYGBgCgpUaGF0IGNyZWF0ZWQgdGhlIGF4ZXMsIGJ1dCB3ZSBzdGlsbCBuZWVkIHRvIGRlZmluZSBob3cgdG8gZGlzcGxheSBvdXIgcG9pbnRzIG9uIHRoZSBwbG90LiBBcyB3ZSBoYXZlIGNvbnRpbnVvdXMgZGF0YSBmb3IgYm90aCB0aGUgeC0gYW5kIHktYXhpcywgYGdlb21fcG9pbnRgIGlzIGEgZ29vZCBjaG9pY2UuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXIsYWVzKHg9Z2RwUGVyY2FwLCB5PWxpZmVFeHApKSArIGdlb21fcG9pbnQoKQpgYGAKCgoKClRoZSAqZ2VvbSogd2UgdXNlIHdpbGwgZGVwZW5kIG9uIHdoYXQga2luZCBvZiBkYXRhIHdlIGhhdmUgKGNvbnRpbnVvdXMsIGNhdGVnb3JpY2FsIGV0YykKCi0gYGdlb21fcG9pbnQoKWAgLSBTY2F0dGVyIHBsb3RzCi0gYGdlb21fbGluZSgpYCAtIExpbmUgcGxvdHMKLSBgZ2VvbV9zbW9vdGgoKWAgLSBGaXR0ZWQgbGluZSBwbG90cwotIGBnZW9tX2JhcigpYCAtIEJhciBwbG90cwotIGBnZW9tX2JveHBsb3QoKWAgLSBCb3hwbG90cwotIGBnZW9tX2ppdHRlcigpYCAtIEppdHRlciB0byBwbG90cwotIGBnZW9tX2hpc3RvZ3JhbSgpYCAtIEhpc3RvZ3JhbSBwbG90cwotIGBnZW9tX2RlbnNpdHkoKWAgLSBEZW5zaXR5IHBsb3RzCi0gYGdlb21fdGV4dCgpYCAtIFRleHQgdG8gcGxvdHMKLSBgZ2VvbV9lcnJvcmJhcigpYCAtIEVycm9yYmFycyB0byBwbG90cwotIGBnZW9tX3Zpb2xpbigpYCAtIFZpb2xpbiBwbG90cwotIGBnZW9tX3RpbGUoKWAgLSBmb3IgImhlYXRtYXAiLWxpa2UgcGxvdHMKCgpCb3hwbG90cyBhcmUgY29tbW9ubHkgdXNlZCB0byB2aXN1YWxpc2UgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgY29udGludW91cyBkYXRhLiBXZSBoYXZlIHRvIHVzZSBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIG9uIHRoZSB4LWF4aXMgc3VjaCBhcyBgY29udGluZW50YCBvciBgY291bnRyeWAgKG5vdCBhZHZpc2FibGUgaW4gdGhpcyBjYXNlIGFzIHRoZXJlIGFyZSB0b28gbWFueSBkaWZmZXJlbnQgdmFsdWVzKS4gCgpUaGUgb3JkZXIgb2YgdGhlIGJveGVzIGFsb25nIHRoZSB4LWF4aXMgaXMgZGljdGF0ZWQgYnkgdGhlIG9yZGVyIG9mIGNhdGVnb3JpZXMgaW4gdGhlIGZhY3Rvcjsgd2l0aCB0aGUgZGVmYXVsdCBmb3IgbmFtZXMgYmVpbmcgYWxwaGFiZXRpY2FsIG9yZGVyLgoKYGBge3J9CmdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gY29udGluZW50LCB5PWdkcFBlcmNhcCkpICsgZ2VvbV9ib3hwbG90KCkKYGBgCgoKYGBge3J9CmdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gZ2RwUGVyY2FwKSkgKyBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKUHJvZHVjaW5nIGEgYmFycGxvdCBvZiBjb3VudHMgb25seSByZXF1aXJlcyBhbiBgeGAgdmFyaWFibGUuIFRoZSBjb3VudHMgd2lsbCBiZSBnZW5lcmF0ZWQgYnkgUi4gCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHg9Y29udGluZW50KSkgKyBnZW9tX2JhcigpCmBgYAoKVGhlIGhlaWdodCBvZiB0aGUgYmFycyBjYW4gYWxzbyBiZSBtYXBwZWQgZGlyZWN0bHkgdG8gbnVtZXJpYyB2YXJpYWJsZXMgaW4gdGhlIGRhdGEgZnJhbWUgaWYgdGhlIGBnZW9tX2NvbGAgZnVuY3Rpb24gaXMgdXNlZCBpbnN0ZWFkLiAKCkluIHRoZSBiZWxvdyBwbG90IHRoZSBheGlzIGxhYmVscyB3aWxsIGJlIG1lc3N5IGFuZCBkaWZmaWN1bHQgdG8gcmVhZC4gVGhpcyBpcyBzb21ldGhpbmcgdGhhdCBjYW4gYmUgY3VzdG9taXNlZCB3aXRoIHNvbWUgb2YgdGhlIGBnZ3Bsb3QyYCBvcHRpb25zIHdlIHdpbGwgZXhwbG9yZSBsYXRlci4KCmBgYHtyfQpnYXBtaW5kZXIyMDAyIDwtIGZpbHRlcihnYXBtaW5kZXIsIHllYXI9PTIwMDIsY29udGluZW50PT0iQW1lcmljYXMiKQpnZ3Bsb3QoZ2FwbWluZGVyMjAwMiwgYWVzKHg9Y291bnRyeSx5PWdkcFBlcmNhcCkpICsgZ2VvbV9jb2woKQpgYGAKCldoZXJlIGFwcHJvcHJpYXRlLCB3ZSBjYW4gYWRkIG11bHRpcGxlIGxheWVycyBvZiBgZ2VvbWBzIHRvIHRoZSBwbG90LiBGb3IgaW5zdGFuY2UsIGEgY3JpdGljaXNtIG9mIHRoZSBib3hwbG90IGlzIHRoYXQgaXQgZG9lcyBub3Qgc2hvdyBhbGwgdGhlIGRhdGEuIFdlIGNhbiByZWN0aWZ5IHRoaXMgYnkgb3ZlcmxheWluZyB0aGUgaW5kaXZpZHVhbCBwb2ludHMuCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBjb250aW5lbnQsIHk9Z2RwUGVyY2FwKSkgKyBnZW9tX2JveHBsb3QoKSArIGdlb21fcG9pbnQoKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGNvbnRpbmVudCwgeT1nZHBQZXJjYXApKSArIGdlb21fYm94cGxvdCgpICsgZ2VvbV9qaXR0ZXIod2lkdGg9MC4xKQpgYGAKCgoqKioqKioKKioqKioqCioqKioqKgoKIyMjIEV4ZXJjaXNlcwoKPGRpdiBjbGFzcz0iZXhlcmNpc2UiPgotIFRoZSB2aW9saW4gcGxvdCBpcyBhIHBvcHVsYXIgYWx0ZXJuYXRpdmUgdG8gdGhlIGJveHBsb3QuIENyZWF0ZSBhIHZpb2xpbiBwbG90IHdpdGggYGdlb21fdmlvbGluYCB0byB2aXN1YWxpc2UgdGhlIGRpZmZlcmVuY2VzIGluIEdEUCBiZXR3ZWVuIGRpZmZlcmVudCBjb250aW5lbnRzLgotIENyZWF0ZSBhIHN1YnNldCBvZiB0aGUgYGdhcG1pbmRlcmAgZGF0YSBmcmFtZSBjb250YWluaW5nIGp1c3QgdGhlIHJvd3MgZm9yIHlvdXIgY291bnRyeSBvZiBiaXJ0aAotIEhhcyB0aGVyZSBiZWVuIGFuIGluY3JlYXNlIGluIGxpZmUgZXhwZWN0YW5jeSBvdmVyIHRpbWU/CiAgICArIHZpc3VhbGlzZSB0aGUgdHJlbmQgdXNpbmcgYSBzY2F0dGVyIHBsb3QgKGBnZW9tX3BvaW50YCksIGxpbmUgZ3JhcGggKGBnZW9tX2xpbmVgKSBvciBzbW9vdGhlZCBsaW5lIChgZ2VvbV9zbW9vdGhgKS4KLSBXaGF0IGhhcHBlbnMgd2hlbiB5b3UgbW9kaWZ5IHRoZSBgZ2VvbV9ib3hwbG90YCBleGFtcGxlIHRvIGNvbXBhcmUgdGhlIGdkcCBkaXN0cmlidXRpb25zIGZvciBkaWZmZXJlbnQgeWVhcnM/CiAgICArIExvb2sgYXQgdGhlIG1lc3NhZ2UgYGdncGxvdDJgIHByaW50cyBhYm92ZSB0aGUgcGxvdCBhbmQgdHJ5IHRvIG1vZGlmeSB0aGUgY29kZSB0byBnaXZlIGEgc2VwYXJhdGUgYm94cGxvdCBmb3IgZWFjaCB5ZWFyCjwvZGl2PgoKKioqKioqCioqKioqKgoqKioqKioKCgpgYGB7cn0KCmBgYAoKCgoKCkFzIHdlIGhhdmUgc2VlbiBhbHJlYWR5LCBgZ2dwbG90YCBvZmZlcnMgYW4gaW50ZXJmYWNlIHRvIGNyZWF0ZSBtYW55IHBvcHVsYXIgcGxvdCB0eXBlcy4gSXQgaXMgdXAgdG8gdGhlIHVzZXIgdG8gZGVjaWRlIHdoYXQgdGhlIGJlc3Qgd2F5IHRvIHZpc3VhbGlzZSB0aGUgZGF0YS4KCgoKCiMjIEN1c3RvbWlzaW5nIHRoZSBwbG90IGFwcGVhcmFuY2UKCk91ciBwbG90cyBhcmUgYSBiaXQgZHJlYXJ5IGF0IHRoZSBtb21lbnQsIGJ1dCBvbmUgd2F5IHRvIGFkZCBjb2xvdXIgaXMgdG8gYWRkIGEgYGNvbGAgYXJndW1lbnQgdG8gdGhlIGBnZW9tX3BvaW50YCBmdW5jdGlvbi4gVGhlIHZhbHVlIGNhbiBiZSBhbnkgb2YgdGhlIHByZS1kZWZpbmVkIGNvbG91ciBuYW1lcyBpbiBSLiBUaGVzZSBhcmUgZGlzcGxheWVkIGluIHRoaXMgW2hhbmR5IG9ubGluZSByZWZlcmVuY2VdKGh0dHA6Ly93d3cuc3RhdC5jb2x1bWJpYS5lZHUvfnR6aGVuZy9maWxlcy9SY29sb3IucGRmKS4gKlIqZWQsICpHKnJlZW4sICpCKmx1ZSBvZiAqSGV4KiB2YWx1ZXMgY2FuIGFsc28gYmUgZ2l2ZW4uCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBnZHBQZXJjYXAsIHk9bGlmZUV4cCkpICsgZ2VvbV9wb2ludChjb2w9InJlZCIpCmBgYAoKYGBge3J9CiMgVXNlIHRoZSBIZXggY29kZXMgZnJvbSBGYXJyb3cgYW5kIEJhbGw6IGh0dHBzOi8vY29udmVydGluZ2NvbG9ycy5jb20vbGlzdC9mYXJyb3ctYmFsbC5odG1sCiMgKGNvb2sncyBibHVlKQoKZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBnZHBQZXJjYXAsIHk9bGlmZUV4cCkpICsgZ2VvbV9wb2ludChjb2w9IiM2QTkwQjQiKQpgYGAKCgpIb3dldmVyLCBhIHBvd2VyZnVsIGZlYXR1cmUgb2YgYGdncGxvdDJgIGlzIHRoYXQgY29sb3VycyBhcmUgdHJlYXRlZCBhcyBhZXN0aGV0aWNzIG9mIHRoZSBwbG90LiBJbiBvdGhlciB3b3JkcyB3ZSBjYW4gdXNlIGEgY29sdW1uIGluIG91ciBkYXRhc2V0LgoKTGV0J3Mgc2F5IHRoYXQgd2Ugd2FudCBwb2ludHMgb24gb3VyIHBsb3QgdG8gYmUgY29sb3VyZWQgYWNjb3JkaW5nIHRvIGNvbnRpbmVudC4gV2UgYWRkIGFuIGV4dHJhIGFyZ3VtZW50IHRvIHRoZSBkZWZpbml0aW9uIG9mIGFlc3RoZXRpY3MgdG8gZGVmaW5lIHRoZSBtYXBwaW5nLiBgZ2dwbG90MmAgd2lsbCBldmVuIGRlY2lkZSBvbiBjb2xvdXJzIGFuZCBjcmVhdGUgYSBsZWdlbmQgZm9yIHVzLgoKYGBge3J9CmdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gZ2RwUGVyY2FwLCB5PWxpZmVFeHAsY29sPWNvbnRpbmVudCkpICsgZ2VvbV9wb2ludCgpCmBgYAoKSXQgd2lsbCBldmVuIGNob29zZSBhIGNvbnRpbnVvdXMgb3IgZGlzY3JldGUgY29sb3VyIHNjYWxlIGJhc2VkIG9uIHRoZSBkYXRhIHR5cGUuIFdlIGhhdmUgYWxyZWFkeSBzZWVuIHRoYXQgYGdncGxvdDJgIGlzIHRyZWF0IG91ciBgeWVhcmAgY29sdW1uIGFzIG51bWVyaWNhbCBkYXRhOyB3aGljaCBpcyBwcm9iYWJseSBub3QgdmVyeSB1c2VmdWwgZm9yIHZpc3VhbGlzYXRpb24uCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBnZHBQZXJjYXAsIHk9bGlmZUV4cCxjb2w9eWVhcikpICsgZ2VvbV9wb2ludCgpCmBgYAoKV2UgY2FuIGZvcmNlIGBnZ3Bsb3QyYCB0byB0cmVhdCBgeWVhcmAgYXMgY2F0ZWdvcmljYWwgZGF0YSBieSB1c2luZyBgYXMuZmFjdG9yYCB3aGVuIGNyZWF0aW5nIHRoZSBhZXN0aGV0aWNzLgoKYGBge3J9CmdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gZ2RwUGVyY2FwLCB5PWxpZmVFeHAsY29sPWFzLmZhY3Rvcih5ZWFyKSkpICsgZ2VvbV9wb2ludCgpCmBgYAoKV2hlbiB1c2VkIGluIHRoZSBjb25zdHJ1Y3Rpb24gb2YgYSBib3hwbG90LCB0aGUgYGNvbGAgYXJndW1lbnQgd2lsbCBjaGFuZ2UgdGhlIGNvbG91ciBvZiB0aGUgbGluZXMuIFRvIGNoYW5nZSB0aGUgY29sb3VyIG9mIHRoZSBib3hlcyB3ZSBoYXZlIHRvIHVzZSBgZmlsbGAuCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBjb250aW5lbnQsIHk9Z2RwUGVyY2FwLGZpbGw9Y29udGluZW50KSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCgoqKioqKioKKioqKioqCioqKioqKgoKIyMjIEV4ZXJjaXNlcyBiZWZvcmUgdGhlIG5leHQgc2Vzc2lvbgo8ZGl2IGNsYXNzPSJleGVyY2lzZSI+Ci0gVXNpbmcgdGhlIGBmaWx0ZXJgIGZ1bmN0aW9uLCBmaW5kIGFsbCBjb3VudHJpZXMgdGhhdCBzdGFydCB3aXRoIHRoZSBsZXR0ZXIgWgogICsgSGludDogWW91IGNhbiBmaW5kIHRoZSBmaXJzdCBsZXR0ZXIgb2YgZWFjaCBjb3VudHJ5IHVzaW5nIHRoZSBgc3Vic3RyYCBmdW5jdGlvbi4gVGhlIGBtdXRhdGVgIGZ1bmN0aW9uIGNhbiB0aGVuIGJlIHVzZWQgdG8gYWRkIGEgbmV3IGNvbHVtbiB0byB0aGUgZGF0YS4KLSBVc2UgYGdlb21fdGlsZWAgdG8gY3JlYXRlIGEgaGVhdG1hcCB2aXN1YWxpc2luZyBsaWZlIGV4cGVjdGFuY3kgb3ZlciB0aW1lIGZvciBFdXJvcGVhbiBjb3VudHJpZXMuIFlvdSB3aWxsIG5lZWQgdG8gd29yayBvdXQgd2hhdCBgYWVzYHRoZXRpY3MgdG8gc3BlY2lmeSBmb3IgYSBgZ2VvbV90aWxlYCBwbG90CjwvZGl2PgoKKioqKioqCioqKioqKgoqKioqKioKCkFuIGV4YW1wbGUgcGxvdCBpcyBzaG93biBvbiB0aGUgY29tcGlsZWQgbm90ZXMuCgohW10oaW1hZ2VzL3RpbGVfZXhhbXBsZS5wbmcpCgoKIyMgQXBwZW5kaXgKCiMjIyBBZGRpbmcgdGV4dCB0byBhIHBsb3QKCkFubm90YXRpb25zIGNhbiBiZSBhZGRlZCB0byBhIHBsb3QgdXNpbmcgdGhlIGZsZXhpYmxlIGBhbm5vdGF0ZWAgZnVuY3Rpb24gZG9jdW1lbnRlZCBbaGVyZV0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2Fubm90YXRlLmh0bWwpLiBUaGlzIHByZXN1bWVzIHRoYXQgeW91IGtub3cgdGhlIGNvb3JkaW5hdGVzIHRoYXQgeW91IHdhbnQgdG8gYWRkIHRoZSBhbm5vdGF0aW9ucyBhdC4KCmBgYHtyfQpwPC0gZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwLGNvbD1jb250aW5lbnQpKSArIGdlb21fcG9pbnQoKQpwICsgYW5ub3RhdGUoInRleHQiLCB4ID0gOTAwMDAseT02MCwgbGFiZWw9IlNvbWUgdGV4dCIpCmBgYAoKSGlnaGxpZ2h0aW5nIHBhcnRpY3VsYXIgcG9pbnRzIG9mIGludGVyZXN0IHVzaW5nIGEgcmVjdGFuZ2xlLgoKYGBge3J9CnAgKyBhbm5vdGF0ZSgicmVjdCIsIHhtaW49MjUwMDAsIHhtYXg9MTIwMDAwLHltaW49NTAseW1heD03NSxhbHBoYT0wLjIpCmBgYAoKCldlIGNhbiBhbHNvIG1hcCBkaXJlY3RseSBmcm9tIGEgY29sdW1uIGluIG91ciBkYXRhc2V0IHRvIHRoZSBgbGFiZWxgIGFlc3RoZXRpYy4gSG93ZXZlciwgdGhpcyB3aWxsIGxhYmVsIGFsbCB0aGUgcG9pbnRzIHdoaWNoIGlzIHJhdGhlciBjbHV0dGVyZWQgaW4gb3VyIGNhc2UKCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHAsY29sPWNvbnRpbmVudCxsYWJlbD1jb3VudHJ5KSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3RleHQoKQpgYGAKCkluc3RlYWQsIHdlIGNvdWxkIHVzZSBhIGRpZmZlcmVudCBkYXRhc2V0IHdoZW4gd2UgY3JlYXRlIHRoZSB0ZXh0IGxhYmVscyB3aXRoIGBnZW9tX3RleHRgLiBIZXJlIHdlIGZpbHRlciB0aGUgYGdhcG1pbmRlcmAgZGF0YXNldCB0byBvbmx5IGNvdW50cmllcyB3aXRoIGBnZHBQZXJjYXBgIGdyZWF0ZXIgdGhhbiBgNTcwMDBgIGFuZCBvbmx5IHRoZXNlIHBvaW50cyBnZXQgbGFiZWxlZC4gV2UgY2FuIGFsc28gc2V0IHRoZSB0ZXh0IGNvbG91cnMgdG8gYSBwYXJ0aWN1bGFyIHZhbHVlIHJhdGhlciB0aGFuIHVzaW5nIHRoZSBvcmlnaW5hbCBjb2xvdXIgbWFwcGluZ3MgZm9yIHRoZSBwbG90IChiYXNlZCBvbiBjb250aW5lbnQpLgoKYGBge3J9CnAgKyBnZW9tX3RleHQoZGF0YSA9IGZpbHRlcihnYXBtaW5kZXIsIGdkcFBlcmNhcCA+IDU3MDAwKSwgCiAgICAgICAgICAgICAgYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwLGxhYmVsPWNvdW50cnkpLGNvbD0iYmxhY2siKQpgYGAKCmBgYHtyfQpwICsgZ2VvbV90ZXh0KGRhdGEgPSBmaWx0ZXIoZ2FwbWluZGVyLCBnZHBQZXJjYXAgPiAyNTAwMCwgbGlmZUV4cCA8IDc1KSwgCiAgICAgICAgICAgICAgYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwLGxhYmVsPWNvdW50cnkpLGNvbD0iYmxhY2siLHNpemU9MykgKyBhbm5vdGF0ZSgicmVjdCIsIHhtaW49MjUwMDAsIHhtYXg9MTIwMDAwLHltaW49NTAseW1heD03NSxhbHBoYT0wLjIpCmBgYAoKCiMjIyBDb21tZW50IGFib3V0IHRoZSBheGlzIHNjYWxlCgpUaGUgcGxvdCBvZiBgZ2RwUGVyY2FwYCB2cyBgbGlmZUV4cGAgb24gdGhlIG9yaWdpbmFsIHNjYWxlIHNlZW1zIHRvIGJlIGluZmx1ZW5jZWQgYnkgdGhlIG91dGxpZXIgb2JzZXJ2YXRpb25zICh3aGljaCB3ZSBub3cga25vdyBhcmUgb2JzZXJ2YXRpb25zIGZyb20gYEt1d2FpdGApLiBJbiBzdWNoIHNpdHVhdGlvbnMgaXQgbWF5IGJlIHBvc3NpYmxlIHRvIHRyYW5zZm9ybSB0aGUgc2NhbGUgb2Ygb25lIGF4aXMgZm9yIHZpc3VhbGlzYXRpb24gcHVycG9zZXMuIE9uZSBzdWNoIHRyYW5zZm9ybWF0aW9uIGlzIGBsb2cxMGAsIHdoaWNoIHdlIGNhbiBhcHBseSB3aXRoIHRoZSBgc2NhbGVfeF9sb2cxMGAgZnVuY3Rpb24uIE90aGVycyBpbmNsdWRlIGBzY2FsZV94X2xvZzJgLCBgc2NhbGVfeF9zcXJ0YCBhbmQgZXF1aXZhbGVudHMgZm9yIHRoZSB5IGF4aXMuCgpgYGB7cn0KcCArIHNjYWxlX3hfbG9nMTAoKQpgYGAKCkJ5IHNwbGl0dGluZyB0aGUgcGxvdCBieSBjb250aW5lbnRzIHdlIHNlZSBtb3JlIGNsZWFybHkgd2hpY2ggY29udGluZW50cyBoYXZlIGEgbW9yZSBsaW5lYXIgcmVsYXRpb25zaGlwLiBBdCB0aGUgbW9tZW50IHRoaXMgaXMgdXNlZnVsIGZvciB2aXN1YWxpc2F0aW9uIHB1cnBvc2VzLCBpZiB3ZSB3YW50ZWQgdG8gb2J0YWluIHN1bW1hcmllcyBmcm9tIHRoZSBkYXRhIHdlIHdvdWxkIG5lZWQgdGhlIHRlY2huaXF1ZXMgaW4gdGhlIG5leHQgc2VjdGlvbi4KCmBgYHtyfQpwICsgc2NhbGVfeF9sb2cxMCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsY29sPSJibGFjayIpICsgZmFjZXRfd3JhcCh+Y29udGluZW50KQpgYGAKCg==