Solutions to this workshop can be found here

Intro to plotting

This class will show you a tiny bit of plotting using the built-in R functions, but will pretty quickly veer into a very popular R package called ggplot2, which is often referred to as just “ggplot”. This is likely the first place in this course where you will see things that are very easy to do in R that would be much more complicated tasks (or maybe even impossible) in excel.

The basic structure of a plot

Let’s load in and consider the iris dataset that we played with when learning about dataframes. We had loaded this in from a .csv file, but actually this dataframe is a built-in dataset in R: we can refer to it by just typing iris.

print(iris)

Let’s use base R to plot the Sepal Length vs Sepal Width for all the data

plot(iris$Sepal.Length, iris$Sepal.Width)

Pretty straightforward; the command is plot(x, y).

# Try making a plot of iris Sepal Width vs Petal Width

You can further modify the plot if you want to change the way the points look, etc. As I mentioned, we won’t be going deep into the details of the regular R plot function.

Intro to ggplot

Let’s try to use ggplot to plot the same data. First, install ggplot2 (if you haven’t already done so) and load it into your R session.

# uncomment the line below and install ggplot2 only if you haven't already
#install.packages('ggplot2')

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

The same plot as the one we made above is actually a bit more complicated to put together in ggplot:

ggplot(data = iris, mapping = aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point()

The above contains the components that are the bare minimum of what we need for a ggplot plot; we can add more on later, but let’s dissect the parts of this command:

ggplot(data = <DATA>, mapping = aes(<Mapping>)) +
        <GEOM_FUNCTION>()
  • arguments:
    • data: the dataframe you want to plot
    • mapping: Any variables from your data that affect plot output, listed in aes( )
  • commands:
    • ggplot( ): required start of every ggplot command. Contains any options that we want to apply to the whole plot (which can be nothing)
    • geom_{something}( ): how you’re plotting the data. Here, we want to plot points, so we’re using geom_point; there are tons of different geoms available, one for each type of plot you might want to make.

Arguments like data and mapping can go in the parentheses after the geom, producing the same plot as above:

ggplot() +
  geom_point(data = iris, mapping = aes(x = Sepal.Length, y = Sepal.Width))

But there are specific situations in which it’s better to do this (we’ll see them later)

Modifying geom properties

We can also pass additional arguments to the geom: useful ones to know are:

  • color: line color; for the default shape used in geom_point, this actually colors the inside of the shape as well
  • fill: the fill color inside a shape
  • size: point size or line thickness
  • shape: for points, this is the shape; for lines, this is the line pattern or dashyness
  • alpha: transparency level, with 0 being totally transparent and 1 being a solid, opaque color

For example:

ggplot(data = iris, mapping = aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point(color = 'blue', fill = 'yellow', shape = 23, alpha = 0.33, size = 5)

Why are some of these rhombuses darker than others?

Note that any arguments that universally affect the properties of the points, lines, etc that we’re plotting, like the ones we used above, must be passed to the relevant geom, not to the ggplot( ) command. This is because the geom is in charge of making the points!

# use ggplot to create a plot of iris Sepal Width vs Petal Width, with violet
# semi-transparent points

Mapping lots of variables

The plot we made above isn’t really all that useful. It’s great to see the data across all three species on one plot, but if we’re looking at this data, we’re probably actually interested in how these species differ from each other. So how do we make ggplot visually separate the points by species?

Remember that the mapping argument deals with any properties of the plot that depend on variables in the supplied data frame. So we can modify our original code like this:

ggplot(data = iris, mapping = aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_point(alpha = 0.33)
# Can also be written as:
ggplot(data = iris) +
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width, color = Species), alpha = 0.33)

Notice that the plot above uses both a variable-dependent color (based on the iris dataframe’s Species column), which goes inside aes( ), and a variable-independent alpha value that applies to the whole geom_point command and goes outside aes( )

Also, notice that you got a legend for free! You didn’t have to tell ggplot how to make it, or what info to include in it; it knows automatically based on how you set up your mapping.

Depending on context, you can make color, fill, shape, size or alpha variable-dependent. Some of these (color, fill, shape) obviously make more sense for categorical variables, while others (alpha, size) make more sense for continuous variables, but ggplot will only rarely stop you from making aesthetically and data representationally questionable choices here.

Let’s try an exercise:

# Based on the code above, make a plot where Sepal.Length is on the x axis,
# Sepal.Width is on the y axis, all the points are colored red, the shape of the
# point depends on the Species, and the point size depends on Petal.Width

Questionable usefulness, but hey, it’s possible and pretty easy…

Stacking multiple geoms

One of the places where ggplot really shines is when you want to combine multiple data representations on one plot. For example, I really like topology-style contour plots, which ggplot can make with geom_density2d. Once we know how to make a basic plot, and combining a contour plot with a plot the individual data points is super easy in ggplot:

# note, the first two lines are just our plot from above
ggplot(data = iris, mapping = aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_density2d() +
  geom_point(alpha = 0.33)

Notice that the alpha argument we provided only applies to geom_point, so the contour lines don’t show any transparency. However, any arguments provided to mapping in an aes( ) statement in the ggplot( ) command apply across all geoms. (Also, notice that when we add a geom, ggplot automatically updates our legend!)

One really powerful application of this is that we can actually make each geom( ) represent a different aspect of the same data. Let’s say we’d like our datapoints to be colored by species, but we’d also like to see a contour plot of sepal length vs width across all the species. To do this, we’re going to have to move our mapping calls inside the geoms, since we now want each geom to map the data differently:

# Removed alpha for simplicity
# Made contour plot line color black (default is blue)
ggplot(data = iris) +
  geom_density2d(mapping = aes(x = Sepal.Length, y = Sepal.Width), color = 'black') +
  geom_point(mapping = aes(x = Sepal.Length, y = Sepal.Width, color = Species))

This plot shows that mapping actually controls not just where to plot the data points and how they should look aesthetically, but also how the data is grouped when it’s represented in the plot. Notice that in the first contour plot, the statistics needed to plot the contours were computed separately for each species. However, when we removed species from the aes( ) being used by geom_density2d, the data was no longer separated by species for any of the stats calculated for this geom, and they’re instead calculated across all the points in the dataset.

Let’s try an exercise. A really useful kind of plot you can make while exploring data is a density plot, which shows pretty much a normalized, smoothed histogram of your data using geom_density. For example, if we want to get an idea of what the distribution of Petal Lengths in our dataset is, we can run:

# Density plot to see the distribution of Petal Lengths in our data
ggplot(data = iris) +
  geom_density(mapping = aes(x = Petal.Length))

Now repeat this plot, but overlaying the density plot for each species on this plot that shows the distribution across all 3 species’ data:

# Make a density plot that shows both the distribution of Petal.Length in all
# the data together in one color, and the distribution for each species'
# Petal.Length each in its own color

# Bonus: Change the linetype of the species' density plots so that each species
# has the same dashed line, but the line representing results across all the
# data is solid

Aside: ggplot objects

ggplot actually creates objects that we can store as variables and add onto. So, for example, we can do this:

basic_iris_plot <-
  ggplot(data = iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_point()
print(basic_iris_plot)
# let's add another geom to this plot
iris_plot_with_contours <-
  basic_iris_plot + geom_density2d()
print(iris_plot_with_contours)

themes and other options we can change

ggplot also allows a huge amount of control over other aspects of the plot (e.g. titles, axis labeling and scale, overall plot look, etc). For most of these, ggplot actually allows multiple equivalent ways to achieve the same effect.

axes + titles

Adding a title to a plot can be achieved using ggtitle()

basic_iris_plot +
  ggtitle('Iris Sepals')

We can also modify the axis properties directly

basic_iris_plot +
  ggtitle('Iris Sepals') +
  scale_x_continuous(name = 'Sepal Length',
                     limits = c(0,10)) +
  scale_y_log10(name = 'Sepal Width',
                breaks = c(2,3,4))

There’s a few things going on here:

  • scale_x_continuous( ) and scale_y_log10( ): set the scale of the x and y axes. For continuous variables, they can also be plotted on a square root scale, reversed, and various other transformations. For discrete variables, use scale_x_discrete( ) and scale_y_discrete( )
  • name: the axis label
  • limits: the bounds on the axis, must be provided as a 2-number vector
  • breaks: manually assign where the tickmarks go
  • labels: for discrete variables, this can be used to rename the categories along your axis

legend

You can modify the legend in a similar way to the other mappings (e.g. the axes); for example, if we want to modify the way the thing mapped to ‘color’ on our plot is represented, we can use scale_color_discrete( ), or, if we want to manually change the values assigned to each category (e.g. the colors), scale_color_manual( ):

basic_iris_plot +
  scale_color_manual(values=c("violet", "blue", "gray"),
                     name="Iris Species",
                     labels=c("Bristle-Pointed Iris", "Blue Flag", "Virginia Iris"))

We can also change the position of the legend using theme( ) (which can actually control nearly every other aesthetic aspect of the plot, such as font size, which axes get labels/tickmarks, etc).

basic_iris_plot +
  scale_color_manual(values=c("violet", "blue", "gray"),
                     name="Iris Species",
                     labels=c("Bristle-Pointed Iris", "Blue Flag", "Virginia Iris")) +
  theme(legend.position = 'bottom')

themes

Finally, the overall appearance of the graph can be changed by selecting a custom ‘theme’; this is a bit confusing, since these are distinct from the theme( ) command used above.

basic_iris_plot +
  scale_color_manual(values=c("violet", "blue", "gray"),
                     name="Iris Species",
                     labels=c("Bristle-Pointed Iris", "Blue Flag", "Virginia Iris")) +
  theme(legend.position = 'bottom') +
  theme_bw()

The structure of data that ggplot can plot

As you’ve seen, ggplot provides users with the power to easily change the appearance of the plot, and the statistics calculated, based on any single column in the dataframe containing the data to be plotted. But this also results in some pretty rigid rules about how your data needs to be organized. Namely, data for ggplot should be in tidy format:

Let’s take a look at what that means. Compare the iris dataframe we’ve been using to the iris3 data, which comes with R and contains the same data:

iris_3_df <- data.frame(iris3)
print(iris_3_df)

Notice that in this modified version of the iris dataset, there is a single row containing data on plants from each of the three iris species. This is not a completely crazy thing to do: maybe our experiment consisted of 50 individual pots, each of which had a plant from every species, and we collected the data on a pot-by-pot level. But organizing the data in this way makes directly graphing it with ggplot a real pain, at least if we want to compare species with each other. We no longer have a ‘species’ column, or neat columns for other mappings we might be interested in (e.g. Sepal Width).

Here’s an attempt to make do with what we have:

ggplot(data = iris_3_df) +
  geom_point(mapping = aes(x = Sepal.L..Setosa, y = Sepal.W..Setosa), color = 'red') +
  geom_point(mapping = aes(x = Sepal.L..Versicolor, y = Sepal.W..Versicolor), color = 'blue') +
  geom_point(mapping = aes(x = Sepal.L..Virginica, y = Sepal.W..Virginica), color = 'green') +
  scale_x_continuous(name = 'Sepal Length') +
  scale_y_continuous(name = 'Sepal Width')

Some things still work well automatically (e.g. ggplot scales axes for us), but it’s a lot more effort to do this, and if we wanted to have a legend on this plot (or had more than a few categories we were interested in), it would be a complete nightmare.

When putting together data to plot, we need to think very carefully about what exactly constitutes a single ‘observation’, and what the ‘variables’ are that we want to use for mapping.

The tidyr package (which, like ggplot2, is part of the tidyverse package) has some really great functions for re-organizing data that looks like iris3 into a ‘tidy’ dataframe, and if you find yourself facing data that isn’t organized the right way for your plot, I really suggest looking over David Gresham’s tidyverse tutorial.

Why ggplot

Using ggplot for paper figures

Because ggplot does a great job of separating aesthetic properties of the plot from what is being plotted, we can create a theme that defines how our plots look in e.g. paper figures and apply it to all our plots after generating them.

First, let’s set a theme for our plots. Because we want our figures to look nice and consistent for the paper, there’s a lot of options we can specify here.

final_figure_ggplot_theme <- 
  theme(plot.title=element_text(size=16,face='bold'),
        plot.margin=unit(c(12,12,12,12),'pt'),
        panel.background=element_rect(fill='white'),
        panel.grid.major=element_line(color='grey',size=0.3),
        axis.line = element_line(color="black", size = 0.5),
        legend.title=element_blank(),
        legend.justification=c(0,1),
        legend.key = element_rect(fill='white'),
        legend.key.height = unit(2,'line'),
        legend.text=element_text(size=12,face='bold'),
        axis.text.x=element_text(size=12,face='bold'),
        axis.text.y=element_text(size=12,face='bold'),
        axis.title.x=element_text(size=14,face='bold'),
        axis.title.y=element_text(size=14,face='bold',angle=90)) +
  theme_bw()

Next, let’s create some plots. We don’t have to worry about appearances here; let’s just make sure the data shows up the way we want it to.

# a figure with the iris data
sepal_points <-
  ggplot(data = iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_point()

sepal_trends <-
  ggplot(data = iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_smooth(method = 'lm')

# make boxplot with overlaid points randomly placed off-center and
# text labels placed at y = 0.75
petal_boxplot <-
  ggplot(data = iris, aes(x = Species, y = Petal.Width, color = Species)) +
  geom_boxplot() +
  geom_text(aes(label = Species), y = 0.75) +
  geom_point(position = 'jitter')

We can print these figures to look at them:

print(sepal_points)
print(sepal_trends)
print(petal_boxplot)

Useful, but not that neat.

Now, let’s apply our figure theme:

sepal_points_figure <- sepal_points + final_figure_ggplot_theme
print(sepal_points_figure)
sepal_trends_figure <- sepal_trends + final_figure_ggplot_theme
print(sepal_trends_figure)
petal_boxplot_figure <- petal_boxplot + final_figure_ggplot_theme
print(petal_boxplot_figure)

There are a few ways to save the figure, but this is probably the easiest:

ggsave(file = 'sepal_points_figure.pdf',
       plot = sepal_points_figure, width = 6.5, height = 4,
       useDingbats=FALSE)

(If saving as a pdf, useDingbats=FALSE is a must and will prevent a ggplot disaster from unfolding. If you load the cowplot package described below, it replaces ggsave with its own function that does this by default)

Because we specified our font sizes in final_figure_ggplot_theme, they will be consistent across plots, regardless of the size we decide to save them at.

Other cool things

combining multiple data frames on one plot

ggplot makes it super easy to combine multiple datasets on one plot, assuming they have the relevant variables (dataframe columns) in common. Let’s break up the iris dataframe to see how this works:

iris_nonvirginca <- subset(iris, Species != 'virginica')
iris_virginica_petals <- subset(iris, Species == 'virginica')[, c('Petal.Width', 'Species')]
print(iris_nonvirginca)
print(iris_virginica_petals)

We now have two dataframes, containing data on different species, and with only a subset of the data in one that is contained in the other (the petal widths and species). But if petal width and species is what we want to plot, this isn’t a problem for ggplot:

ggplot() +
  geom_boxplot(data = iris_nonvirginca, aes(x = Species, y = Petal.Width, color = Species)) +
  geom_boxplot(data = iris_virginica_petals, aes(x = Species, y = Petal.Width, color = Species))

facets

Another great tool ggplot provides is faceting. This allows you to separate data into subplots based on a column (or multiple columns):

basic_iris_plot +
  facet_wrap( ~ Species)

Notice that the x-axes are consistent among these plots.

Lots of additional packages!

Because ggplot is so popular, there’s been a ton of additional packages written that build on top of it. Here are two examples.

gganimate

Add animations to plots

cowplot

Arrange your plots for publication figures

install.packages('cowplot')
library(cowplot)
plot_grid(sepal_points_figure, sepal_trends_figure, petal_boxplot_figure,
          labels = "AUTO")
library(cowplot)
top_row <- plot_grid(sepal_points_figure, labels = 'A')
bottom_row <- plot_grid(sepal_trends_figure, petal_boxplot_figure, labels = c('B', 'C'))
plot_grid(top_row, bottom_row, nrow = 2)
LS0tCnRpdGxlOiAiSW50cm8gUiBDb3Vyc2UsIFdvcmtzaG9wIDY6IFBsb3R0aW5nICh3aXRoIGdncGxvdDIpIgpzdWJ0aXRsZTogfAogICAgfCAgIC0gcGxvdHRpbmcgd2l0aCBiYXNlIFIKICAgIHwgICAtIGludHJvZHVjdGlvbiB0byBnZ3Bsb3QKICAgIHwgICAtIGNvbWJpbmluZyBtdWx0aXBsZSBkYXRhIHJlcHJlc2VudGF0aW9ucyBpbiBnZ3Bsb3QKICAgIHwgICAtIGNoYW5naW5nIHRoZSBhcHBlYXJhbmNlIG9mIHBsb3RzCiAgICB8ICAgLSBvcmdhbml6aW5nIGRhdGEgYWNjb3JkaW5nIHRvICJ0aWR5IiBwcmluY2lwbGVzCiAgICB8ICAgLSBlYXN5LCByZXByb2R1Y2libGUgcGFwZXIgZmlndXJlcyB3aXRoIGdncGxvdAphdXRob3I6CiAgLSBFdWdlbmUgUGxhdnNraW4Kb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGRlcHRoOiAzCiAgICB0aWR5OiB5ZXMKICAgIHRvYzogeWVzCi0tLQoqKlNvbHV0aW9ucyB0byB0aGlzIHdvcmtzaG9wIGNhbiBiZSBmb3VuZCBbaGVyZV0oU29sdXRpb25zX1dvcmtzaG9wXzYubmIuaHRtbCkqKgoKIyBJbnRybyB0byBwbG90dGluZwoKVGhpcyBjbGFzcyB3aWxsIHNob3cgeW91IGEgKnRpbnkqIGJpdCBvZiBwbG90dGluZyB1c2luZyB0aGUgYnVpbHQtaW4gUiBmdW5jdGlvbnMsIGJ1dCB3aWxsIHByZXR0eSBxdWlja2x5IHZlZXIgaW50byBhIHZlcnkgcG9wdWxhciBSIHBhY2thZ2UgY2FsbGVkICoqZ2dwbG90MioqLCB3aGljaCBpcyBvZnRlbiByZWZlcnJlZCB0byBhcyBqdXN0ICJnZ3Bsb3QiLiBUaGlzIGlzIGxpa2VseSB0aGUgZmlyc3QgcGxhY2UgaW4gdGhpcyBjb3Vyc2Ugd2hlcmUgeW91IHdpbGwgc2VlIHRoaW5ncyB0aGF0IGFyZSAqdmVyeSogZWFzeSB0byBkbyBpbiBSIHRoYXQgd291bGQgYmUgbXVjaCBtb3JlIGNvbXBsaWNhdGVkIHRhc2tzIChvciBtYXliZSBldmVuIGltcG9zc2libGUpIGluIGV4Y2VsLgoKIyMgVGhlIGJhc2ljIHN0cnVjdHVyZSBvZiBhIHBsb3QKCkxldCdzIGxvYWQgaW4gYW5kIGNvbnNpZGVyIHRoZSAqaXJpcyogZGF0YXNldCB0aGF0IHdlIHBsYXllZCB3aXRoIHdoZW4gbGVhcm5pbmcgYWJvdXQgZGF0YWZyYW1lcy4gV2UgaGFkIGxvYWRlZCB0aGlzIGluIGZyb20gYSAqLmNzdiogZmlsZSwgYnV0IGFjdHVhbGx5IHRoaXMgZGF0YWZyYW1lIGlzIGEgYnVpbHQtaW4gZGF0YXNldCBpbiBSOiB3ZSBjYW4gcmVmZXIgdG8gaXQgYnkganVzdCB0eXBpbmcgYGlyaXNgLgoKYGBge3J9CnByaW50KGlyaXMpCmBgYAoKTGV0J3MgdXNlIGJhc2UgUiB0byBwbG90IHRoZSBTZXBhbCBMZW5ndGggdnMgU2VwYWwgV2lkdGggZm9yIGFsbCB0aGUgZGF0YQpgYGB7cn0KcGxvdChpcmlzJFNlcGFsLkxlbmd0aCwgaXJpcyRTZXBhbC5XaWR0aCkKYGBgCgpQcmV0dHkgc3RyYWlnaHRmb3J3YXJkOyB0aGUgY29tbWFuZCBpcyBgcGxvdCh4LCB5KWAuCgpgYGB7cn0KIyBUcnkgbWFraW5nIGEgcGxvdCBvZiBpcmlzIFNlcGFsIFdpZHRoIHZzIFBldGFsIFdpZHRoCgpgYGAKCllvdSBjYW4gZnVydGhlciBtb2RpZnkgdGhlIHBsb3QgaWYgeW91IHdhbnQgdG8gY2hhbmdlIHRoZSB3YXkgdGhlIHBvaW50cyBsb29rLCBldGMuIEFzIEkgbWVudGlvbmVkLCB3ZSB3b24ndCBiZSBnb2luZyBkZWVwIGludG8gdGhlIGRldGFpbHMgb2YgdGhlIHJlZ3VsYXIgUiBwbG90IGZ1bmN0aW9uLgoKIyMgSW50cm8gdG8gZ2dwbG90CgpMZXQncyB0cnkgdG8gdXNlIGdncGxvdCB0byBwbG90IHRoZSBzYW1lIGRhdGEuIEZpcnN0LCBpbnN0YWxsICoqZ2dwbG90MioqIChpZiB5b3UgaGF2ZW4ndCBhbHJlYWR5IGRvbmUgc28pIGFuZCBsb2FkIGl0IGludG8geW91ciBSIHNlc3Npb24uCmBgYHtyfQojIHVuY29tbWVudCB0aGUgbGluZSBiZWxvdyBhbmQgaW5zdGFsbCBnZ3Bsb3QyIG9ubHkgaWYgeW91IGhhdmVuJ3QgYWxyZWFkeQojaW5zdGFsbC5wYWNrYWdlcygnZ2dwbG90MicpCgojIGxvYWQgdGhlIGdncGxvdDIgbGlicmFyeSBpbnRvIHRoZSBjdXJyZW50IFIgc2Vzc2lvbgpsaWJyYXJ5KGdncGxvdDIpCmBgYAoKVGhlIHNhbWUgcGxvdCBhcyB0aGUgb25lIHdlIG1hZGUgYWJvdmUgaXMgYWN0dWFsbHkgYSBiaXQgbW9yZSBjb21wbGljYXRlZCB0byBwdXQgdG9nZXRoZXIgaW4gZ2dwbG90OgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBpcmlzLCBtYXBwaW5nID0gYWVzKHggPSBTZXBhbC5MZW5ndGgsIHkgPSBTZXBhbC5XaWR0aCkpICsKICBnZW9tX3BvaW50KCkKYGBgCgpUaGUgYWJvdmUgY29udGFpbnMgdGhlIGNvbXBvbmVudHMgdGhhdCBhcmUgdGhlIGJhcmUgbWluaW11bSBvZiB3aGF0IHdlIG5lZWQgZm9yIGEgZ2dwbG90IHBsb3Q7IHdlIGNhbiBhZGQgbW9yZSBvbiBsYXRlciwgYnV0IGxldCdzIGRpc3NlY3QgdGhlIHBhcnRzIG9mIHRoaXMgY29tbWFuZDoKYGBge30KZ2dwbG90KGRhdGEgPSA8REFUQT4sIG1hcHBpbmcgPSBhZXMoPE1hcHBpbmc+KSkgKwogICAgICAgIDxHRU9NX0ZVTkNUSU9OPigpCmBgYAoKKiBhcmd1bWVudHM6CiAgICArICoqZGF0YSoqOiB0aGUgZGF0YWZyYW1lIHlvdSB3YW50IHRvIHBsb3QKICAgICsgKiptYXBwaW5nKio6IEFueSB2YXJpYWJsZXMgZnJvbSB5b3VyIGRhdGEgdGhhdCBhZmZlY3QgcGxvdCBvdXRwdXQsIGxpc3RlZCBpbiAqYWVzKCApKgoqIGNvbW1hbmRzOgogICAgKyAqKmdncGxvdCggKSoqOiByZXF1aXJlZCBzdGFydCBvZiBldmVyeSBnZ3Bsb3QgY29tbWFuZC4gQ29udGFpbnMgYW55IG9wdGlvbnMgdGhhdCB3ZSB3YW50IHRvIGFwcGx5IHRvIHRoZSB3aG9sZSBwbG90ICh3aGljaCBjYW4gYmUgbm90aGluZykKICAgICsgKipnZW9tX3tzb21ldGhpbmd9KCApKio6ICpob3cqIHlvdSdyZSBwbG90dGluZyB0aGUgZGF0YS4gSGVyZSwgd2Ugd2FudCB0byBwbG90IHBvaW50cywgc28gd2UncmUgdXNpbmcgKipnZW9tX3BvaW50Kio7IHRoZXJlIGFyZSAqdG9ucyogb2YgZGlmZmVyZW50IGdlb21zIGF2YWlsYWJsZSwgb25lIGZvciBlYWNoIHR5cGUgb2YgcGxvdCB5b3UgbWlnaHQgd2FudCB0byBtYWtlLgoKQXJndW1lbnRzIGxpa2UgKmRhdGEqIGFuZCAqbWFwcGluZyogY2FuIGdvIGluIHRoZSBwYXJlbnRoZXNlcyBhZnRlciB0aGUgZ2VvbSwgcHJvZHVjaW5nIHRoZSBzYW1lIHBsb3QgYXMgYWJvdmU6CmBgYHtyfQpnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gaXJpcywgbWFwcGluZyA9IGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgpKQpgYGAKQnV0IHRoZXJlIGFyZSBzcGVjaWZpYyBzaXR1YXRpb25zIGluIHdoaWNoIGl0J3MgYmV0dGVyIHRvIGRvIHRoaXMgKHdlJ2xsIHNlZSB0aGVtIGxhdGVyKQoKIyMgTW9kaWZ5aW5nICoqZ2VvbSoqIHByb3BlcnRpZXMKCldlIGNhbiBhbHNvIHBhc3MgYWRkaXRpb25hbCBhcmd1bWVudHMgdG8gdGhlIGdlb206IHVzZWZ1bCBvbmVzIHRvIGtub3cgYXJlOgoKKiBfX2NvbG9yX186IGxpbmUgY29sb3I7IGZvciB0aGUgZGVmYXVsdCBzaGFwZSB1c2VkIGluIGdlb21fcG9pbnQsIHRoaXMgYWN0dWFsbHkgY29sb3JzIHRoZSBpbnNpZGUgb2YgdGhlIHNoYXBlIGFzIHdlbGwKKiBfX2ZpbGxfXzogdGhlIGZpbGwgY29sb3IgaW5zaWRlIGEgc2hhcGUKKiBfX3NpemVfXzogcG9pbnQgc2l6ZSBvciBsaW5lIHRoaWNrbmVzcwoqIF9fc2hhcGVfXzogZm9yIHBvaW50cywgdGhpcyBpcyB0aGUgc2hhcGU7IGZvciBsaW5lcywgdGhpcyBpcyB0aGUgbGluZSBwYXR0ZXJuIG9yIGRhc2h5bmVzcwoqIF9fYWxwaGFfXzogdHJhbnNwYXJlbmN5IGxldmVsLCB3aXRoIDAgYmVpbmcgdG90YWxseSB0cmFuc3BhcmVudCBhbmQgMSBiZWluZyBhIHNvbGlkLCBvcGFxdWUgY29sb3IKCkZvciBleGFtcGxlOgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBpcmlzLCBtYXBwaW5nID0gYWVzKHggPSBTZXBhbC5MZW5ndGgsIHkgPSBTZXBhbC5XaWR0aCkpICsKICBnZW9tX3BvaW50KGNvbG9yID0gJ2JsdWUnLCBmaWxsID0gJ3llbGxvdycsIHNoYXBlID0gMjMsIGFscGhhID0gMC4zMywgc2l6ZSA9IDUpCmBgYApXaHkgYXJlIHNvbWUgb2YgdGhlc2UgcmhvbWJ1c2VzIGRhcmtlciB0aGFuIG90aGVycz8KCk5vdGUgdGhhdCBhbnkgYXJndW1lbnRzIHRoYXQgdW5pdmVyc2FsbHkgYWZmZWN0IHRoZSBwcm9wZXJ0aWVzIG9mIHRoZSBwb2ludHMsIGxpbmVzLCBldGMgdGhhdCB3ZSdyZSBwbG90dGluZywgbGlrZSB0aGUgb25lcyB3ZSB1c2VkIGFib3ZlLCAqbXVzdCogYmUgcGFzc2VkIHRvIHRoZSByZWxldmFudCBnZW9tLCBub3QgdG8gdGhlIGdncGxvdCggKSBjb21tYW5kLiAqKlRoaXMgaXMgYmVjYXVzZSB0aGUgZ2VvbSBpcyBpbiBjaGFyZ2Ugb2YgbWFraW5nIHRoZSBwb2ludHMhKioKCmBgYHtyfQojIHVzZSBnZ3Bsb3QgdG8gY3JlYXRlIGEgcGxvdCBvZiBpcmlzIFNlcGFsIFdpZHRoIHZzIFBldGFsIFdpZHRoLCB3aXRoIHZpb2xldAojIHNlbWktdHJhbnNwYXJlbnQgcG9pbnRzCgpgYGAKCiMjIE1hcHBpbmcgbG90cyBvZiB2YXJpYWJsZXMKClRoZSBwbG90IHdlIG1hZGUgYWJvdmUgaXNuJ3QgcmVhbGx5IGFsbCB0aGF0IHVzZWZ1bC4gSXQncyBncmVhdCB0byBzZWUgdGhlIGRhdGEgYWNyb3NzIGFsbCB0aHJlZSBzcGVjaWVzIG9uIG9uZSBwbG90LCBidXQgaWYgd2UncmUgbG9va2luZyBhdCB0aGlzIGRhdGEsIHdlJ3JlIHByb2JhYmx5IGFjdHVhbGx5IGludGVyZXN0ZWQgaW4gaG93IHRoZXNlIHNwZWNpZXMgZGlmZmVyIGZyb20gZWFjaCBvdGhlci4gU28gaG93IGRvIHdlIG1ha2UgZ2dwbG90IHZpc3VhbGx5IHNlcGFyYXRlIHRoZSBwb2ludHMgYnkgc3BlY2llcz8KClJlbWVtYmVyIHRoYXQgdGhlICoqbWFwcGluZyoqIGFyZ3VtZW50IGRlYWxzIHdpdGggKmFueSBwcm9wZXJ0aWVzIG9mIHRoZSBwbG90IHRoYXQgZGVwZW5kIG9uIHZhcmlhYmxlcyBpbiB0aGUgc3VwcGxpZWQgZGF0YSBmcmFtZSouIFNvIHdlIGNhbiBtb2RpZnkgb3VyIG9yaWdpbmFsIGNvZGUgbGlrZSB0aGlzOgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBpcmlzLCBtYXBwaW5nID0gYWVzKHggPSBTZXBhbC5MZW5ndGgsIHkgPSBTZXBhbC5XaWR0aCwgY29sb3IgPSBTcGVjaWVzKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjMzKQpgYGAKYGBge3IsIGV2YWwgPSBGQUxTRX0KIyBDYW4gYWxzbyBiZSB3cml0dGVuIGFzOgpnZ3Bsb3QoZGF0YSA9IGlyaXMpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCBjb2xvciA9IFNwZWNpZXMpLCBhbHBoYSA9IDAuMzMpCmBgYApOb3RpY2UgdGhhdCB0aGUgcGxvdCBhYm92ZSB1c2VzICpib3RoKiBhIHZhcmlhYmxlLWRlcGVuZGVudCBjb2xvciAoYmFzZWQgb24gdGhlIGlyaXMgZGF0YWZyYW1lJ3MgKlNwZWNpZXMqIGNvbHVtbiksIHdoaWNoIGdvZXMgaW5zaWRlICphZXMoICkqLCBhbmQgYSB2YXJpYWJsZS1pbmRlcGVuZGVudCBhbHBoYSB2YWx1ZSB0aGF0IGFwcGxpZXMgdG8gdGhlIHdob2xlIGdlb21fcG9pbnQgY29tbWFuZCBhbmQgZ29lcyBvdXRzaWRlICphZXMoICkqCgpBbHNvLCBub3RpY2UgdGhhdCB5b3UgZ290IGEgbGVnZW5kIGZvciAqZnJlZSohIFlvdSBkaWRuJ3QgaGF2ZSB0byB0ZWxsIGdncGxvdCBob3cgdG8gbWFrZSBpdCwgb3Igd2hhdCBpbmZvIHRvIGluY2x1ZGUgaW4gaXQ7IGl0IGtub3dzIGF1dG9tYXRpY2FsbHkgYmFzZWQgb24gaG93IHlvdSBzZXQgdXAgeW91ciAqKm1hcHBpbmcqKi4KCkRlcGVuZGluZyBvbiBjb250ZXh0LCB5b3UgY2FuIG1ha2UgY29sb3IsIGZpbGwsIHNoYXBlLCBzaXplIG9yIGFscGhhIHZhcmlhYmxlLWRlcGVuZGVudC4gU29tZSBvZiB0aGVzZSAoY29sb3IsIGZpbGwsIHNoYXBlKSBvYnZpb3VzbHkgbWFrZSBtb3JlIHNlbnNlIGZvciBjYXRlZ29yaWNhbCB2YXJpYWJsZXMsIHdoaWxlIG90aGVycyAoYWxwaGEsIHNpemUpIG1ha2UgbW9yZSBzZW5zZSBmb3IgY29udGludW91cyB2YXJpYWJsZXMsIGJ1dCAqZ2dwbG90KiB3aWxsIG9ubHkgcmFyZWx5IHN0b3AgeW91IGZyb20gbWFraW5nIGFlc3RoZXRpY2FsbHkgYW5kIGRhdGEgcmVwcmVzZW50YXRpb25hbGx5IHF1ZXN0aW9uYWJsZSBjaG9pY2VzIGhlcmUuCgpMZXQncyB0cnkgYW4gZXhlcmNpc2U6IAoKYGBge3J9CiMgQmFzZWQgb24gdGhlIGNvZGUgYWJvdmUsIG1ha2UgYSBwbG90IHdoZXJlIFNlcGFsLkxlbmd0aCBpcyBvbiB0aGUgeCBheGlzLAojIFNlcGFsLldpZHRoIGlzIG9uIHRoZSB5IGF4aXMsIGFsbCB0aGUgcG9pbnRzIGFyZSBjb2xvcmVkIHJlZCwgdGhlIHNoYXBlIG9mIHRoZQojIHBvaW50IGRlcGVuZHMgb24gdGhlIFNwZWNpZXMsIGFuZCB0aGUgcG9pbnQgc2l6ZSBkZXBlbmRzIG9uIFBldGFsLldpZHRoCgpgYGAKUXVlc3Rpb25hYmxlIHVzZWZ1bG5lc3MsIGJ1dCBoZXksIGl0J3MgcG9zc2libGUgYW5kIHByZXR0eSBlYXN5Li4uCgojIyBTdGFja2luZyBtdWx0aXBsZSAqKmdlb21zKioKCk9uZSBvZiB0aGUgcGxhY2VzIHdoZXJlIGdncGxvdCByZWFsbHkgc2hpbmVzIGlzIHdoZW4geW91IHdhbnQgdG8gY29tYmluZSBtdWx0aXBsZSBkYXRhIHJlcHJlc2VudGF0aW9ucyBvbiBvbmUgcGxvdC4gRm9yIGV4YW1wbGUsIEkgKnJlYWxseSogbGlrZSB0b3BvbG9neS1zdHlsZSBjb250b3VyIHBsb3RzLCB3aGljaCBnZ3Bsb3QgY2FuIG1ha2Ugd2l0aCAqKmdlb21fZGVuc2l0eTJkKiouIE9uY2Ugd2Uga25vdyBob3cgdG8gbWFrZSBhIGJhc2ljIHBsb3QsIGFuZCBjb21iaW5pbmcgYSBjb250b3VyIHBsb3Qgd2l0aCBhIHBsb3QgdGhlIGluZGl2aWR1YWwgZGF0YSBwb2ludHMgaXMgc3VwZXIgZWFzeSBpbiBnZ3Bsb3Q6CmBgYHtyfQojIG5vdGUsIHRoZSBmaXJzdCB0d28gbGluZXMgYXJlIGp1c3Qgb3VyIHBsb3QgZnJvbSBhYm92ZQpnZ3Bsb3QoZGF0YSA9IGlyaXMsIG1hcHBpbmcgPSBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCBjb2xvciA9IFNwZWNpZXMpKSArCiAgZ2VvbV9kZW5zaXR5MmQoKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMzMpCmBgYApOb3RpY2UgdGhhdCB0aGUgKmFscGhhKiBhcmd1bWVudCB3ZSBwcm92aWRlZCBvbmx5IGFwcGxpZXMgdG8gZ2VvbV9wb2ludCwgc28gdGhlIGNvbnRvdXIgbGluZXMgZG9uJ3Qgc2hvdyBhbnkgdHJhbnNwYXJlbmN5LiBIb3dldmVyLCBhbnkgYXJndW1lbnRzIHByb3ZpZGVkIHRvICoqbWFwcGluZyoqIGluIGFuIGFlcyggKSBzdGF0ZW1lbnQgaW4gdGhlICoqZ2dwbG90KCApKiogY29tbWFuZCBhcHBseSBhY3Jvc3MgYWxsIGdlb21zLiAoQWxzbywgbm90aWNlIHRoYXQgd2hlbiB3ZSBhZGQgYSBnZW9tLCBnZ3Bsb3QgYXV0b21hdGljYWxseSB1cGRhdGVzIG91ciBsZWdlbmQhKQoKT25lIHJlYWxseSBwb3dlcmZ1bCBhcHBsaWNhdGlvbiBvZiB0aGlzIGlzIHRoYXQgd2UgY2FuIGFjdHVhbGx5IG1ha2UgZWFjaCBnZW9tKCApIHJlcHJlc2VudCBhIGRpZmZlcmVudCBhc3BlY3Qgb2YgdGhlIHNhbWUgZGF0YS4gTGV0J3Mgc2F5IHdlJ2QgbGlrZSBvdXIgZGF0YXBvaW50cyB0byBiZSBjb2xvcmVkIGJ5IHNwZWNpZXMsIGJ1dCB3ZSdkIGFsc28gbGlrZSB0byBzZWUgYSBjb250b3VyIHBsb3Qgb2Ygc2VwYWwgbGVuZ3RoIHZzIHdpZHRoICphY3Jvc3MgYWxsIHRoZSBzcGVjaWVzKi4gVG8gZG8gdGhpcywgd2UncmUgZ29pbmcgdG8gaGF2ZSB0byBtb3ZlIG91ciAqKm1hcHBpbmcqKiBjYWxscyBpbnNpZGUgdGhlIGdlb21zLCBzaW5jZSB3ZSBub3cgd2FudCBlYWNoIGdlb20gdG8gbWFwIHRoZSBkYXRhIGRpZmZlcmVudGx5OgpgYGB7cn0KIyBSZW1vdmVkIGFscGhhIGZvciBzaW1wbGljaXR5CiMgTWFkZSBjb250b3VyIHBsb3QgbGluZSBjb2xvciBibGFjayAoZGVmYXVsdCBpcyBibHVlKQpnZ3Bsb3QoZGF0YSA9IGlyaXMpICsKICBnZW9tX2RlbnNpdHkyZChtYXBwaW5nID0gYWVzKHggPSBTZXBhbC5MZW5ndGgsIHkgPSBTZXBhbC5XaWR0aCksIGNvbG9yID0gJ2JsYWNrJykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgsIGNvbG9yID0gU3BlY2llcykpCmBgYApUaGlzIHBsb3Qgc2hvd3MgdGhhdCAqKm1hcHBpbmcqKiBhY3R1YWxseSBjb250cm9scyBub3QganVzdCB3aGVyZSB0byBwbG90IHRoZSBkYXRhIHBvaW50cyBhbmQgaG93IHRoZXkgc2hvdWxkIGxvb2sgYWVzdGhldGljYWxseSwgYnV0IGFsc28gaG93IHRoZSBkYXRhIGlzIGdyb3VwZWQgd2hlbiBpdCdzIHJlcHJlc2VudGVkIGluIHRoZSBwbG90LiBOb3RpY2UgdGhhdCBpbiB0aGUgZmlyc3QgY29udG91ciBwbG90LCB0aGUgc3RhdGlzdGljcyBuZWVkZWQgdG8gcGxvdCB0aGUgY29udG91cnMgd2VyZSBjb21wdXRlZCBzZXBhcmF0ZWx5IGZvciBlYWNoIHNwZWNpZXMuIEhvd2V2ZXIsIHdoZW4gd2UgcmVtb3ZlZCBzcGVjaWVzIGZyb20gdGhlIGFlcyggKSBiZWluZyB1c2VkIGJ5IGdlb21fZGVuc2l0eTJkLCB0aGUgZGF0YSB3YXMgbm8gbG9uZ2VyIHNlcGFyYXRlZCBieSBzcGVjaWVzIGZvciBhbnkgb2YgdGhlIHN0YXRzIGNhbGN1bGF0ZWQgZm9yIHRoaXMgZ2VvbSwgYW5kIHRoZXkncmUgaW5zdGVhZCBjYWxjdWxhdGVkIGFjcm9zcyBhbGwgdGhlIHBvaW50cyBpbiB0aGUgZGF0YXNldC4KCkxldCdzIHRyeSBhbiBleGVyY2lzZS4gQSByZWFsbHkgdXNlZnVsIGtpbmQgb2YgcGxvdCB5b3UgY2FuIG1ha2Ugd2hpbGUgZXhwbG9yaW5nIGRhdGEgaXMgYSAqZGVuc2l0eSogcGxvdCwgd2hpY2ggc2hvd3MgcHJldHR5IG11Y2ggYSBub3JtYWxpemVkLCBzbW9vdGhlZCBoaXN0b2dyYW0gb2YgeW91ciBkYXRhIHVzaW5nICoqZ2VvbV9kZW5zaXR5KiouIEZvciBleGFtcGxlLCBpZiB3ZSB3YW50IHRvIGdldCBhbiBpZGVhIG9mIHdoYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBQZXRhbCBMZW5ndGhzIGluIG91ciBkYXRhc2V0IGlzLCB3ZSBjYW4gcnVuOgpgYGB7cn0KIyBEZW5zaXR5IHBsb3QgdG8gc2VlIHRoZSBkaXN0cmlidXRpb24gb2YgUGV0YWwgTGVuZ3RocyBpbiBvdXIgZGF0YQpnZ3Bsb3QoZGF0YSA9IGlyaXMpICsKICBnZW9tX2RlbnNpdHkobWFwcGluZyA9IGFlcyh4ID0gUGV0YWwuTGVuZ3RoKSkKYGBgCgpOb3cgcmVwZWF0IHRoaXMgcGxvdCwgYnV0IG92ZXJsYXlpbmcgdGhlIGRlbnNpdHkgcGxvdCBmb3IgZWFjaCBzcGVjaWVzIG9uIHRoaXMgcGxvdCB0aGF0IHNob3dzIHRoZSBkaXN0cmlidXRpb24gYWNyb3NzIGFsbCAzIHNwZWNpZXMnIGRhdGE6CmBgYHtyfQojIE1ha2UgYSBkZW5zaXR5IHBsb3QgdGhhdCBzaG93cyBib3RoIHRoZSBkaXN0cmlidXRpb24gb2YgUGV0YWwuTGVuZ3RoIGluIGFsbAojIHRoZSBkYXRhIHRvZ2V0aGVyIGluIG9uZSBjb2xvciwgYW5kIHRoZSBkaXN0cmlidXRpb24gZm9yIGVhY2ggc3BlY2llcycKIyBQZXRhbC5MZW5ndGggZWFjaCBpbiBpdHMgb3duIGNvbG9yCgojIEJvbnVzOiBDaGFuZ2UgdGhlIGxpbmV0eXBlIG9mIHRoZSBzcGVjaWVzJyBkZW5zaXR5IHBsb3RzIHNvIHRoYXQgZWFjaCBzcGVjaWVzCiMgaGFzIHRoZSBzYW1lIGRhc2hlZCBsaW5lLCBidXQgdGhlIGxpbmUgcmVwcmVzZW50aW5nIHJlc3VsdHMgYWNyb3NzIGFsbCB0aGUKIyBkYXRhIGlzIHNvbGlkCmBgYAoKIyMgQXNpZGU6IGdncGxvdCBvYmplY3RzCgpnZ3Bsb3QgYWN0dWFsbHkgY3JlYXRlcyBvYmplY3RzIHRoYXQgd2UgY2FuIHN0b3JlIGFzIHZhcmlhYmxlcyBhbmQgYWRkIG9udG8uIFNvLCBmb3IgZXhhbXBsZSwgd2UgY2FuIGRvIHRoaXM6CmBgYHtyfQpiYXNpY19pcmlzX3Bsb3QgPC0KICBnZ3Bsb3QoZGF0YSA9IGlyaXMsIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgsIGNvbG9yID0gU3BlY2llcykpICsKICBnZW9tX3BvaW50KCkKcHJpbnQoYmFzaWNfaXJpc19wbG90KQojIGxldCdzIGFkZCBhbm90aGVyIGdlb20gdG8gdGhpcyBwbG90CmlyaXNfcGxvdF93aXRoX2NvbnRvdXJzIDwtCiAgYmFzaWNfaXJpc19wbG90ICsgZ2VvbV9kZW5zaXR5MmQoKQpwcmludChpcmlzX3Bsb3Rfd2l0aF9jb250b3VycykKYGBgCgoqKioKCiMjIHRoZW1lcyBhbmQgb3RoZXIgb3B0aW9ucyB3ZSBjYW4gY2hhbmdlCgpnZ3Bsb3QgYWxzbyBhbGxvd3MgYSBodWdlIGFtb3VudCBvZiBjb250cm9sIG92ZXIgb3RoZXIgYXNwZWN0cyBvZiB0aGUgcGxvdCAoZS5nLiB0aXRsZXMsIGF4aXMgbGFiZWxpbmcgYW5kIHNjYWxlLCBvdmVyYWxsIHBsb3QgbG9vaywgZXRjKS4gRm9yIG1vc3Qgb2YgdGhlc2UsIGdncGxvdCBhY3R1YWxseSBhbGxvd3MgbXVsdGlwbGUgZXF1aXZhbGVudCB3YXlzIHRvIGFjaGlldmUgdGhlIHNhbWUgZWZmZWN0LgoKIyMjIGF4ZXMgKyB0aXRsZXMKCkFkZGluZyBhIHRpdGxlIHRvIGEgcGxvdCBjYW4gYmUgYWNoaWV2ZWQgdXNpbmcgZ2d0aXRsZSgpCmBgYHtyfQpiYXNpY19pcmlzX3Bsb3QgKwogIGdndGl0bGUoJ0lyaXMgU2VwYWxzJykKYGBgCgpXZSBjYW4gYWxzbyBtb2RpZnkgdGhlIGF4aXMgcHJvcGVydGllcyBkaXJlY3RseQpgYGB7cn0KYmFzaWNfaXJpc19wbG90ICsKICBnZ3RpdGxlKCdJcmlzIFNlcGFscycpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZSA9ICdTZXBhbCBMZW5ndGgnLAogICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAsMTApKSArCiAgc2NhbGVfeV9sb2cxMChuYW1lID0gJ1NlcGFsIFdpZHRoJywKICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMiwzLDQpKQpgYGAKVGhlcmUncyBhIGZldyB0aGluZ3MgZ29pbmcgb24gaGVyZToKCiogX19zY2FsZV94X2NvbnRpbnVvdXMoIClfXyBhbmQgX19zY2FsZV95X2xvZzEwKCApX186IHNldCB0aGUgc2NhbGUgb2YgdGhlIHggYW5kIHkgYXhlcy4gRm9yIGNvbnRpbnVvdXMgdmFyaWFibGVzLCB0aGV5IGNhbiBhbHNvIGJlIHBsb3R0ZWQgb24gYSBzcXVhcmUgcm9vdCBzY2FsZSwgcmV2ZXJzZWQsIGFuZCB2YXJpb3VzIG90aGVyIHRyYW5zZm9ybWF0aW9ucy4gRm9yIGRpc2NyZXRlIHZhcmlhYmxlcywgdXNlICoqc2NhbGVfeF9kaXNjcmV0ZSggKSoqIGFuZCAqKnNjYWxlX3lfZGlzY3JldGUoICkqKgoqIF9fbmFtZV9fOiB0aGUgYXhpcyBsYWJlbAoqIF9fbGltaXRzX186IHRoZSBib3VuZHMgb24gdGhlIGF4aXMsIG11c3QgYmUgcHJvdmlkZWQgYXMgYSAyLW51bWJlciB2ZWN0b3IKKiBfX2JyZWFrc19fOiBtYW51YWxseSBhc3NpZ24gd2hlcmUgdGhlIHRpY2ttYXJrcyBnbwoqIF9fbGFiZWxzX186IGZvciBkaXNjcmV0ZSB2YXJpYWJsZXMsIHRoaXMgY2FuIGJlIHVzZWQgdG8gcmVuYW1lIHRoZSBjYXRlZ29yaWVzIGFsb25nIHlvdXIgYXhpcwoKIyMjIGxlZ2VuZAoKWW91IGNhbiBtb2RpZnkgdGhlIGxlZ2VuZCBpbiBhIHNpbWlsYXIgd2F5IHRvIHRoZSBvdGhlciBtYXBwaW5ncyAoZS5nLiB0aGUgYXhlcyk7IGZvciBleGFtcGxlLCBpZiB3ZSB3YW50IHRvIG1vZGlmeSB0aGUgd2F5IHRoZSB0aGluZyBtYXBwZWQgdG8gJ2NvbG9yJyBvbiBvdXIgcGxvdCBpcyByZXByZXNlbnRlZCwgd2UgY2FuIHVzZSAqKnNjYWxlX2NvbG9yX2Rpc2NyZXRlKCApKiosIG9yLCBpZiB3ZSB3YW50IHRvIG1hbnVhbGx5IGNoYW5nZSB0aGUgdmFsdWVzIGFzc2lnbmVkIHRvIGVhY2ggY2F0ZWdvcnkgKGUuZy4gdGhlIGNvbG9ycyksICoqc2NhbGVfY29sb3JfbWFudWFsKCApKio6CmBgYHtyfQpiYXNpY19pcmlzX3Bsb3QgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygidmlvbGV0IiwgImJsdWUiLCAiZ3JheSIpLAogICAgICAgICAgICAgICAgICAgICBuYW1lPSJJcmlzIFNwZWNpZXMiLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiQnJpc3RsZS1Qb2ludGVkIElyaXMiLCAiQmx1ZSBGbGFnIiwgIlZpcmdpbmlhIElyaXMiKSkKYGBgCldlIGNhbiBhbHNvIGNoYW5nZSB0aGUgcG9zaXRpb24gb2YgdGhlIGxlZ2VuZCB1c2luZyAqKnRoZW1lKCApKiogKHdoaWNoIGNhbiBhY3R1YWxseSBjb250cm9sIG5lYXJseSBldmVyeSBvdGhlciBhZXN0aGV0aWMgYXNwZWN0IG9mIHRoZSBwbG90LCBzdWNoIGFzIGZvbnQgc2l6ZSwgd2hpY2ggYXhlcyBnZXQgbGFiZWxzL3RpY2ttYXJrcywgZXRjKS4KCmBgYHtyfQpiYXNpY19pcmlzX3Bsb3QgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygidmlvbGV0IiwgImJsdWUiLCAiZ3JheSIpLAogICAgICAgICAgICAgICAgICAgICBuYW1lPSJJcmlzIFNwZWNpZXMiLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiQnJpc3RsZS1Qb2ludGVkIElyaXMiLCAiQmx1ZSBGbGFnIiwgIlZpcmdpbmlhIElyaXMiKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nKQpgYGAKCiMjIyB0aGVtZXMKCkZpbmFsbHksIHRoZSBvdmVyYWxsIGFwcGVhcmFuY2Ugb2YgdGhlIGdyYXBoIGNhbiBiZSBjaGFuZ2VkIGJ5IHNlbGVjdGluZyBhIGN1c3RvbSAndGhlbWUnOyB0aGlzIGlzIGEgYml0IGNvbmZ1c2luZywgc2luY2UgdGhlc2UgYXJlIGRpc3RpbmN0IGZyb20gdGhlICoqdGhlbWUoICkqKiBjb21tYW5kIHVzZWQgYWJvdmUuCmBgYHtyfQpiYXNpY19pcmlzX3Bsb3QgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygidmlvbGV0IiwgImJsdWUiLCAiZ3JheSIpLAogICAgICAgICAgICAgICAgICAgICBuYW1lPSJJcmlzIFNwZWNpZXMiLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiQnJpc3RsZS1Qb2ludGVkIElyaXMiLCAiQmx1ZSBGbGFnIiwgIlZpcmdpbmlhIElyaXMiKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nKSArCiAgdGhlbWVfYncoKQpgYGAKCioqKgoKIyBUaGUgc3RydWN0dXJlIG9mIGRhdGEgdGhhdCBnZ3Bsb3QgY2FuIHBsb3QKCkFzIHlvdSd2ZSBzZWVuLCBnZ3Bsb3QgcHJvdmlkZXMgdXNlcnMgd2l0aCB0aGUgcG93ZXIgdG8gZWFzaWx5IGNoYW5nZSB0aGUgYXBwZWFyYW5jZSBvZiB0aGUgcGxvdCwgYW5kIHRoZSBzdGF0aXN0aWNzIGNhbGN1bGF0ZWQsIGJhc2VkIG9uIGFueSBzaW5nbGUgY29sdW1uIGluIHRoZSBkYXRhZnJhbWUgY29udGFpbmluZyB0aGUgZGF0YSB0byBiZSBwbG90dGVkLiBCdXQgdGhpcyBhbHNvIHJlc3VsdHMgaW4gc29tZSBwcmV0dHkgcmlnaWQgcnVsZXMgYWJvdXQgaG93IHlvdXIgZGF0YSBuZWVkcyB0byBiZSBvcmdhbml6ZWQuIE5hbWVseSwgZGF0YSBmb3IgZ2dwbG90IHNob3VsZCBiZSBpbiBbdGlkeSBmb3JtYXRdKGh0dHBzOi8vbGVhcm4uZ2VuY29yZS5iaW8ubnl1LmVkdS90aWR5dmVyc2UvKToKCiogZWFjaCB2YXJpYWJsZSBtdXN0IGhhdmUgaXRzIG93biBjb2x1bW4KKiBlYWNoIG9ic2VydmF0aW9uIG11c3QgaGF2ZSBpdHMgb3duIHJvdyAoYnV0IHdoYXQncyBhbiBvYnNlcnZhdGlvbj8pCiogZWFjaCB2YWx1ZSBtdXN0IGhhdmUgaXRzIG93biBjZWxsCgpMZXQncyB0YWtlIGEgbG9vayBhdCB3aGF0IHRoYXQgbWVhbnMuIENvbXBhcmUgdGhlICppcmlzKiBkYXRhZnJhbWUgd2UndmUgYmVlbiB1c2luZyB0byB0aGUgKmlyaXMzKiBkYXRhLCB3aGljaCBjb21lcyB3aXRoIFIgYW5kIGNvbnRhaW5zIHRoZSBzYW1lIGRhdGE6CmBgYHtyfQppcmlzXzNfZGYgPC0gZGF0YS5mcmFtZShpcmlzMykKcHJpbnQoaXJpc18zX2RmKQpgYGAKCk5vdGljZSB0aGF0IGluIHRoaXMgbW9kaWZpZWQgdmVyc2lvbiBvZiB0aGUgaXJpcyBkYXRhc2V0LCB0aGVyZSBpcyBhIHNpbmdsZSByb3cgY29udGFpbmluZyBkYXRhIG9uIHBsYW50cyBmcm9tIGVhY2ggb2YgdGhlIHRocmVlIGlyaXMgc3BlY2llcy4gVGhpcyBpcyBub3QgYSBjb21wbGV0ZWx5IGNyYXp5IHRoaW5nIHRvIGRvOiBtYXliZSBvdXIgZXhwZXJpbWVudCBjb25zaXN0ZWQgb2YgNTAgaW5kaXZpZHVhbCBwb3RzLCBlYWNoIG9mIHdoaWNoIGhhZCBhIHBsYW50IGZyb20gZXZlcnkgc3BlY2llcywgYW5kIHdlIGNvbGxlY3RlZCB0aGUgZGF0YSBvbiBhIHBvdC1ieS1wb3QgbGV2ZWwuIEJ1dCBvcmdhbml6aW5nIHRoZSBkYXRhIGluIHRoaXMgd2F5IG1ha2VzIGRpcmVjdGx5IGdyYXBoaW5nIGl0IHdpdGggZ2dwbG90IGEgcmVhbCBwYWluLCBhdCBsZWFzdCBpZiB3ZSB3YW50IHRvIGNvbXBhcmUgc3BlY2llcyB3aXRoIGVhY2ggb3RoZXIuIFdlIG5vIGxvbmdlciBoYXZlIGEgJ3NwZWNpZXMnIGNvbHVtbiwgb3IgbmVhdCBjb2x1bW5zIGZvciBvdGhlciBtYXBwaW5ncyB3ZSBtaWdodCBiZSBpbnRlcmVzdGVkIGluIChlLmcuIFNlcGFsIFdpZHRoKS4KCkhlcmUncyBhbiBhdHRlbXB0IHRvIG1ha2UgZG8gd2l0aCB3aGF0IHdlIGhhdmU6CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGlyaXNfM19kZikgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gU2VwYWwuTC4uU2V0b3NhLCB5ID0gU2VwYWwuVy4uU2V0b3NhKSwgY29sb3IgPSAncmVkJykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gU2VwYWwuTC4uVmVyc2ljb2xvciwgeSA9IFNlcGFsLlcuLlZlcnNpY29sb3IpLCBjb2xvciA9ICdibHVlJykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gU2VwYWwuTC4uVmlyZ2luaWNhLCB5ID0gU2VwYWwuVy4uVmlyZ2luaWNhKSwgY29sb3IgPSAnZ3JlZW4nKSArCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnU2VwYWwgTGVuZ3RoJykgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gJ1NlcGFsIFdpZHRoJykKYGBgClNvbWUgdGhpbmdzIHN0aWxsIHdvcmsgd2VsbCBhdXRvbWF0aWNhbGx5IChlLmcuIGdncGxvdCBzY2FsZXMgYXhlcyBmb3IgdXMpLCBidXQgaXQncyBhIGxvdCBtb3JlIGVmZm9ydCB0byBkbyB0aGlzLCBhbmQgaWYgd2Ugd2FudGVkIHRvIGhhdmUgYSBsZWdlbmQgb24gdGhpcyBwbG90IChvciBoYWQgbW9yZSB0aGFuIGEgZmV3IGNhdGVnb3JpZXMgd2Ugd2VyZSBpbnRlcmVzdGVkIGluKSwgaXQgd291bGQgYmUgYSBjb21wbGV0ZSBuaWdodG1hcmUuCgpXaGVuIHB1dHRpbmcgdG9nZXRoZXIgZGF0YSB0byBwbG90LCB3ZSBuZWVkIHRvIHRoaW5rIHZlcnkgY2FyZWZ1bGx5IGFib3V0IHdoYXQgZXhhY3RseSBjb25zdGl0dXRlcyBhIHNpbmdsZSAnb2JzZXJ2YXRpb24nLCBhbmQgd2hhdCB0aGUgJ3ZhcmlhYmxlcycgYXJlIHRoYXQgd2Ugd2FudCB0byB1c2UgZm9yIG1hcHBpbmcuIAoKVGhlICoqdGlkeXIqKiBwYWNrYWdlICh3aGljaCwgbGlrZSAqKmdncGxvdDIqKiwgaXMgcGFydCBvZiB0aGUgKip0aWR5dmVyc2UqKiBwYWNrYWdlKSBoYXMgc29tZSByZWFsbHkgZ3JlYXQgZnVuY3Rpb25zIGZvciByZS1vcmdhbml6aW5nIGRhdGEgdGhhdCBsb29rcyBsaWtlICppcmlzMyogaW50byBhICd0aWR5JyBkYXRhZnJhbWUsIGFuZCBpZiB5b3UgZmluZCB5b3Vyc2VsZiBmYWNpbmcgZGF0YSB0aGF0IGlzbid0IG9yZ2FuaXplZCB0aGUgcmlnaHQgd2F5IGZvciB5b3VyIHBsb3QsIEkgcmVhbGx5IHN1Z2dlc3QgbG9va2luZyBvdmVyIFtEYXZpZCBHcmVzaGFtJ3MgdGlkeXZlcnNlIHR1dG9yaWFsXShodHRwczovL2xlYXJuLmdlbmNvcmUuYmlvLm55dS5lZHUvdGlkeXZlcnNlLykuCgojIFdoeSBnZ3Bsb3QKCiogX19lYXN5IGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXNfXzogc2VwYXJhdGlvbiBvZiB2YXJpYWJsZSBtYXBwaW5nIGZyb20gdmlzdWFsIHJlcHJlc2VudGF0aW9uIChlLmcuIGdlb21zKSBtYWtlcyBpdCBlYXN5IHRvIHRyeSBkaWZmZXJlbnQgd2F5cyBvZiBwbG90dGluZyBkYXRhCiogX19hdXRvbWF0aW9uIG9mIHRoZSBib3Jpbmcgc3R1ZmZfXzogZ2VuZXJhdGlvbiBvZiBsZWdlbmRzLCBheGlzIGJvdW5kcywgZXRjIGlzIGRvbmUgdmVyeSB3ZWxsIGF1dG9tYXRpY2FsbHkgYmFzZWQgb24gdGhlIGRhdGEgYW5kIHZhcmlhYmxlcyB5b3UncmUgcGxvdHRpbmcgKGJ1dCB5b3UgY2FuIGhhdmUgZmluZXIgY29udHJvbCBvZiBpdCBpZiB5b3UnZCBsaWtlKQoqIF9fYXV0b21hdGVkIHN0YXRzX186IGxvdHMgb2YgZ2VvbXMgdGhhdCBjYWxjdWxhdGUgYW5kIGRpc3BsYXkgc3RhdGlzdGljcyBvZiB5b3VyIGRhdGEuIFRoZXNlIHN0YXRpc3RpY3MgYXJlIGF1dG9tYXRpY2FsbHkgY2FsY3VsYXRlZCBiYXNlZCBvbiB5b3VyIGdyb3VwaW5nIG9mIHRoZSBkYXRhLCBob3cgeW91IHNwZWNpZnkgeW91ciBheGVzIChlLmcuIGxpbmVhciB2cyBsb2cgc2NhbGUpLCBldGMuCiAgICArICpnZW9tX2RlbnNpdHkqIGFuZCAqZ2VvbV9kZW5zaXR5MmQqIGZvciBkZW5zaXR5IGVzdGltYXRpb24KICAgICsgKmdlb21fc21vb3RoKiBmb3IgdHJlbmRsaW5lcyB3aXRoIGVycm9yIHJpYmJvbnMKICAgICsgW3N0YXRfc3VtbWFyeV0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3N0YXRfc3VtbWFyeS5odG1sKSBmb3Igb3RoZXIgd2F5cyB0byBiaW4gYW5kIHN1bW1hcml6ZSBkYXRhKQoqIF9fcGxvdHMgYXMgb2JqZWN0c19fOiBzdG9yaW5nIHBsb3RzIGFzIG9iamVjdHMgYWxsb3dzIHRoZW0gdG8gYmUgZWFzaWx5IG1vZGlmaWVkIGFuZCBjb21iaW5lZCBpbnRvIGZpZ3VyZXMKKiBfX2luY3JlZGlibHkgaGVscGZ1bCBvbmxpbmUgZXhhbXBsZXNfXzogdGhlICp0aWR5dmVyc2UqIHdlYnNpdGUgY29udGFpbnMgYW4gaW5jcmVkaWJsZSBvbmxpbmUgbWFudWFsIHdpdGggZXhwbGFuYXRpb25zIGFuZCBjbGVhciBleGFtcGxlcyBmb3IgbmVhcmx5IGV2ZXJ5dGhpbmcgeW91IG1pZ2h0IHdhbnQgdG8gZG8gaW4gZ2dwbG90OiBbaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL10oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlLykKCiMjIFVzaW5nIGdncGxvdCBmb3IgcGFwZXIgZmlndXJlcwoKQmVjYXVzZSBnZ3Bsb3QgZG9lcyBhIGdyZWF0IGpvYiBvZiBzZXBhcmF0aW5nIGFlc3RoZXRpYyBwcm9wZXJ0aWVzIG9mIHRoZSBwbG90IGZyb20gd2hhdCBpcyBiZWluZyBwbG90dGVkLCB3ZSBjYW4gY3JlYXRlIGEgdGhlbWUgdGhhdCBkZWZpbmVzIGhvdyBvdXIgcGxvdHMgbG9vayBpbiBlLmcuIHBhcGVyIGZpZ3VyZXMgYW5kIGFwcGx5IGl0IHRvIGFsbCBvdXIgcGxvdHMgYWZ0ZXIgZ2VuZXJhdGluZyB0aGVtLgoKRmlyc3QsIGxldCdzIHNldCBhIHRoZW1lIGZvciBvdXIgcGxvdHMuIEJlY2F1c2Ugd2Ugd2FudCBvdXIgZmlndXJlcyB0byBsb29rIG5pY2UgYW5kIGNvbnNpc3RlbnQgZm9yIHRoZSBwYXBlciwgdGhlcmUncyBhIGxvdCBvZiBvcHRpb25zIHdlIGNhbiBzcGVjaWZ5IGhlcmUuCmBgYHtyfQpmaW5hbF9maWd1cmVfZ2dwbG90X3RoZW1lIDwtIAogIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTYsZmFjZT0nYm9sZCcpLAogICAgICAgIHBsb3QubWFyZ2luPXVuaXQoYygxMiwxMiwxMiwxMiksJ3B0JyksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbD0nd2hpdGUnKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yPWVsZW1lbnRfbGluZShjb2xvcj0nZ3JleScsc2l6ZT0wLjMpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvcj0iYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uPWMoMCwxKSwKICAgICAgICBsZWdlbmQua2V5ID0gZWxlbWVudF9yZWN0KGZpbGw9J3doaXRlJyksCiAgICAgICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDIsJ2xpbmUnKSwKICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMixmYWNlPSdib2xkJyksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTIsZmFjZT0nYm9sZCcpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9J2JvbGQnKSwKICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF90ZXh0KHNpemU9MTQsZmFjZT0nYm9sZCcpLAogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X3RleHQoc2l6ZT0xNCxmYWNlPSdib2xkJyxhbmdsZT05MCkpICsKICB0aGVtZV9idygpCmBgYAoKTmV4dCwgbGV0J3MgY3JlYXRlIHNvbWUgcGxvdHMuIFdlIGRvbid0IGhhdmUgdG8gd29ycnkgYWJvdXQgYXBwZWFyYW5jZXMgaGVyZTsgbGV0J3MganVzdCBtYWtlIHN1cmUgdGhlIGRhdGEgc2hvd3MgdXAgdGhlIHdheSB3ZSB3YW50IGl0IHRvLgpgYGB7cn0KIyBhIGZpZ3VyZSB3aXRoIHRoZSBpcmlzIGRhdGEKc2VwYWxfcG9pbnRzIDwtCiAgZ2dwbG90KGRhdGEgPSBpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCBjb2xvciA9IFNwZWNpZXMpKSArCiAgZ2VvbV9wb2ludCgpCgpzZXBhbF90cmVuZHMgPC0KICBnZ3Bsb3QoZGF0YSA9IGlyaXMsIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgsIGNvbG9yID0gU3BlY2llcykpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nKQoKIyBtYWtlIGJveHBsb3Qgd2l0aCBvdmVybGFpZCBwb2ludHMgcmFuZG9tbHkgcGxhY2VkIG9mZi1jZW50ZXIgYW5kCiMgdGV4dCBsYWJlbHMgcGxhY2VkIGF0IHkgPSAwLjc1CnBldGFsX2JveHBsb3QgPC0KICBnZ3Bsb3QoZGF0YSA9IGlyaXMsIGFlcyh4ID0gU3BlY2llcywgeSA9IFBldGFsLldpZHRoLCBjb2xvciA9IFNwZWNpZXMpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBTcGVjaWVzKSwgeSA9IDAuNzUpICsKICBnZW9tX3BvaW50KHBvc2l0aW9uID0gJ2ppdHRlcicpCmBgYAoKV2UgY2FuIHByaW50IHRoZXNlIGZpZ3VyZXMgdG8gbG9vayBhdCB0aGVtOgpgYGB7cn0KcHJpbnQoc2VwYWxfcG9pbnRzKQpwcmludChzZXBhbF90cmVuZHMpCnByaW50KHBldGFsX2JveHBsb3QpCmBgYApVc2VmdWwsIGJ1dCBub3QgdGhhdCBuZWF0LgoKTm93LCBsZXQncyBhcHBseSBvdXIgZmlndXJlIHRoZW1lOgpgYGB7cn0Kc2VwYWxfcG9pbnRzX2ZpZ3VyZSA8LSBzZXBhbF9wb2ludHMgKyBmaW5hbF9maWd1cmVfZ2dwbG90X3RoZW1lCnByaW50KHNlcGFsX3BvaW50c19maWd1cmUpCnNlcGFsX3RyZW5kc19maWd1cmUgPC0gc2VwYWxfdHJlbmRzICsgZmluYWxfZmlndXJlX2dncGxvdF90aGVtZQpwcmludChzZXBhbF90cmVuZHNfZmlndXJlKQpwZXRhbF9ib3hwbG90X2ZpZ3VyZSA8LSBwZXRhbF9ib3hwbG90ICsgZmluYWxfZmlndXJlX2dncGxvdF90aGVtZQpwcmludChwZXRhbF9ib3hwbG90X2ZpZ3VyZSkKYGBgCgpUaGVyZSBhcmUgYSBmZXcgd2F5cyB0byBzYXZlIHRoZSBmaWd1cmUsIGJ1dCB0aGlzIGlzIHByb2JhYmx5IHRoZSBlYXNpZXN0OgpgYGB7cn0KZ2dzYXZlKGZpbGUgPSAnc2VwYWxfcG9pbnRzX2ZpZ3VyZS5wZGYnLAogICAgICAgcGxvdCA9IHNlcGFsX3BvaW50c19maWd1cmUsIHdpZHRoID0gNi41LCBoZWlnaHQgPSA0LAogICAgICAgdXNlRGluZ2JhdHM9RkFMU0UpCmBgYAooSWYgc2F2aW5nIGFzIGEgcGRmLCB1c2VEaW5nYmF0cz1GQUxTRSBpcyBhICoqbXVzdCoqIGFuZCB3aWxsIHByZXZlbnQgYSBnZ3Bsb3QgZGlzYXN0ZXIgZnJvbSB1bmZvbGRpbmcuIElmIHlvdSBsb2FkIHRoZSBjb3dwbG90IHBhY2thZ2UgZGVzY3JpYmVkIGJlbG93LCBpdCByZXBsYWNlcyAqZ2dzYXZlKiB3aXRoIGl0cyBvd24gZnVuY3Rpb24gdGhhdCBkb2VzIHRoaXMgYnkgZGVmYXVsdCkKCkJlY2F1c2Ugd2Ugc3BlY2lmaWVkIG91ciBmb250IHNpemVzIGluIGZpbmFsX2ZpZ3VyZV9nZ3Bsb3RfdGhlbWUsIHRoZXkgd2lsbCBiZSBjb25zaXN0ZW50IGFjcm9zcyBwbG90cywgcmVnYXJkbGVzcyBvZiB0aGUgc2l6ZSB3ZSBkZWNpZGUgdG8gc2F2ZSB0aGVtIGF0LgoKIyBPdGhlciBjb29sIHRoaW5ncwoKIyMgY29tYmluaW5nIG11bHRpcGxlIGRhdGEgZnJhbWVzIG9uIG9uZSBwbG90CgpnZ3Bsb3QgbWFrZXMgaXQgc3VwZXIgZWFzeSB0byBjb21iaW5lIG11bHRpcGxlIGRhdGFzZXRzIG9uIG9uZSBwbG90LCBhc3N1bWluZyB0aGV5IGhhdmUgdGhlIHJlbGV2YW50IHZhcmlhYmxlcyAoZGF0YWZyYW1lIGNvbHVtbnMpIGluIGNvbW1vbi4gTGV0J3MgYnJlYWsgdXAgdGhlIGlyaXMgZGF0YWZyYW1lIHRvIHNlZSBob3cgdGhpcyB3b3JrczoKYGBge3J9CmlyaXNfbm9udmlyZ2luY2EgPC0gc3Vic2V0KGlyaXMsIFNwZWNpZXMgIT0gJ3ZpcmdpbmljYScpCmlyaXNfdmlyZ2luaWNhX3BldGFscyA8LSBzdWJzZXQoaXJpcywgU3BlY2llcyA9PSAndmlyZ2luaWNhJylbLCBjKCdQZXRhbC5XaWR0aCcsICdTcGVjaWVzJyldCnByaW50KGlyaXNfbm9udmlyZ2luY2EpCnByaW50KGlyaXNfdmlyZ2luaWNhX3BldGFscykKYGBgCldlIG5vdyBoYXZlIHR3byBkYXRhZnJhbWVzLCBjb250YWluaW5nIGRhdGEgb24gZGlmZmVyZW50IHNwZWNpZXMsIGFuZCB3aXRoIG9ubHkgYSBzdWJzZXQgb2YgdGhlIGRhdGEgaW4gb25lIHRoYXQgaXMgY29udGFpbmVkIGluIHRoZSBvdGhlciAodGhlIHBldGFsIHdpZHRocyBhbmQgc3BlY2llcykuIEJ1dCBpZiBwZXRhbCB3aWR0aCBhbmQgc3BlY2llcyBpcyB3aGF0IHdlIHdhbnQgdG8gcGxvdCwgdGhpcyBpc24ndCBhIHByb2JsZW0gZm9yIGdncGxvdDoKYGBge3J9CmdncGxvdCgpICsKICBnZW9tX2JveHBsb3QoZGF0YSA9IGlyaXNfbm9udmlyZ2luY2EsIGFlcyh4ID0gU3BlY2llcywgeSA9IFBldGFsLldpZHRoLCBjb2xvciA9IFNwZWNpZXMpKSArCiAgZ2VvbV9ib3hwbG90KGRhdGEgPSBpcmlzX3ZpcmdpbmljYV9wZXRhbHMsIGFlcyh4ID0gU3BlY2llcywgeSA9IFBldGFsLldpZHRoLCBjb2xvciA9IFNwZWNpZXMpKQpgYGAKCiMjIGZhY2V0cwoKQW5vdGhlciBncmVhdCB0b29sIGdncGxvdCBwcm92aWRlcyBpcyBmYWNldGluZy4gVGhpcyBhbGxvd3MgeW91IHRvIHNlcGFyYXRlIGRhdGEgaW50byBzdWJwbG90cyBiYXNlZCBvbiBhIGNvbHVtbiAob3IgbXVsdGlwbGUgY29sdW1ucyk6CmBgYHtyfQpiYXNpY19pcmlzX3Bsb3QgKwogIGZhY2V0X3dyYXAoIH4gU3BlY2llcykKYGBgCk5vdGljZSB0aGF0IHRoZSB4LWF4ZXMgYXJlIGNvbnNpc3RlbnQgYW1vbmcgdGhlc2UgcGxvdHMuCgojIyBMb3RzIG9mIGFkZGl0aW9uYWwgcGFja2FnZXMhCgpCZWNhdXNlIGdncGxvdCBpcyBzbyBwb3B1bGFyLCB0aGVyZSdzIGJlZW4gYSB0b24gb2YgYWRkaXRpb25hbCBwYWNrYWdlcyB3cml0dGVuIHRoYXQgYnVpbGQgb24gdG9wIG9mIGl0LiBIZXJlIGFyZSB0d28gZXhhbXBsZXMuCgojIyMgZ2dhbmltYXRlCgpBZGQgYW5pbWF0aW9ucyB0byBwbG90cwpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCByZXN1bHRzID0gRkFMU0V9Cmluc3RhbGwucGFja2FnZXMoJ2dpZnNraScpCmluc3RhbGwucGFja2FnZXMoJ2dnYW5pbWF0ZScpCmxpYnJhcnkoZ2dhbmltYXRlKQphbmltYXRlZF9pcmlzX3Bsb3QgPC0KICBiYXNpY19pcmlzX3Bsb3QgKyB0cmFuc2l0aW9uX3N0YXRlcyhTcGVjaWVzKQojIHNhdmUgcGxvdCB3aXRoIDMgZnJhbWVzLCBwbGF5IGF0IDEgZnJhbWUgcGVyIHNlY29uZAphbmltX3NhdmUoImFuaW1hdGVkX2lyaXNfcGxvdC5naWYiLCBhbmltYXRlZF9pcmlzX3Bsb3QsIG5mcmFtZXMgPSA0LCBmcHMgPSAxKQojIHJlbmRlciBwbG90IHVzaW5nICFbXShwbG90X3BhdGgpIG91dHNpZGUgb2YgY29kZSBjaHVuawpgYGAKCiMjIyBjb3dwbG90CgpBcnJhbmdlIHlvdXIgcGxvdHMgZm9yIHB1YmxpY2F0aW9uIGZpZ3VyZXMKYGBge3J9Cmluc3RhbGwucGFja2FnZXMoJ2Nvd3Bsb3QnKQpsaWJyYXJ5KGNvd3Bsb3QpCnBsb3RfZ3JpZChzZXBhbF9wb2ludHNfZmlndXJlLCBzZXBhbF90cmVuZHNfZmlndXJlLCBwZXRhbF9ib3hwbG90X2ZpZ3VyZSwKICAgICAgICAgIGxhYmVscyA9ICJBVVRPIikKYGBgCgpgYGB7cn0KbGlicmFyeShjb3dwbG90KQp0b3Bfcm93IDwtIHBsb3RfZ3JpZChzZXBhbF9wb2ludHNfZmlndXJlLCBsYWJlbHMgPSAnQScpCmJvdHRvbV9yb3cgPC0gcGxvdF9ncmlkKHNlcGFsX3RyZW5kc19maWd1cmUsIHBldGFsX2JveHBsb3RfZmlndXJlLCBsYWJlbHMgPSBjKCdCJywgJ0MnKSkKcGxvdF9ncmlkKHRvcF9yb3csIGJvdHRvbV9yb3csIG5yb3cgPSAyKQpgYGAK