Intro

~2 Minute Setup

Create your R Notebook for today and double check that your workspace is clear from last time.

Load the important packages for today:

#' You only need to run this once
install.packages("scales")
install.packages("ggthemes")
library("ggplot2")
library("lsa2017")
library("tidyverse")
library("broom")
I_jean <- read.delim("http://bit.ly/avml_ggplot2_data")

Why Plot

We are having a lesson on plotting in the middle of a course on R modelling because it is essential for you to plot your data before you try to model it. I would go so far as to say that if you haven’t made a lot of graphs of your data, and have only looked at averages, correlations, and linear model results, that you don’t really understand your data.

There’s a classic illustration of this called Anscombe’s quartet, which when plotted looks like three very distinctive patterns.

But if you fit linear models to them, they have nearly identical statistical properties.

fit_lm <- function(df){
  lm(y ~ x, data = df)
}
anscomb_models <- tidy_anscombe %>%
                    group_by(series)%>%
                    nest()%>%
                    mutate(model = map(data, fit_lm),
                           model_param_df = map(model, tidy),
                           model_glance = map(model, glance))
anscomb_models %>%
  unnest(model_param_df)%>%
  arrange(term)
anscomb_models %>%
  unnest(model_glance)

It has been even more humorously illustrated recently that you can produce data sets of almost any arbitrary shape that have nearly identical statistical properties.

Same Stats, Different Graphs: Generating Datasets with Varied Appearance and Identical Statistics through Simulated Annealing

Thinking about Plotting

It’s important to think of your figures as a report of your data. Try to take as much care in producing your plots as you do your writing, or reporting of your statistics. They are as important as (or for some readers, more important than) anything else in your paper.

“Accuracy”

When making a plot, you should strive for accuracy in:

  • Accurately representing the properties of numbers.
  • Accurately representing the nature of your data.

Take this very simple data set:

group value
A 2
B 5

For our purposes, these numbers have three properties.

  1. Order: 2 < 5, or A < B
  2. Magnitude: 5 = 2.5 \(\times\) 2, or B = 2.5 \(\times\) A
  3. Contextual Magnitude: If A and B are bars, and these are measure of the cost of a pint, then A must be a real dive (and a good deal), and B must be a little bit better, but still not too fancy. If A and B are people, and these are their number of legs, then A has an unsurprising number of legs and B has a surprising number of legs.

Here is an example of an inaccurate plot:

It successfully captures the order of A and B, but fails to capture the correct magnitude of the difference. The magnitude of the difference is thrown off because the y-axis doesn’t start at 0. In this plot, the B line is 7\(\times\) longer than the A line, but the actual magnitude of the difference is 2.5\(\times\). This produces a “lie factor” of \(\frac{7}{2.5} = 2.8\).

This isn’t just a hypothetical problem either. For example, British electoral mailers are notorious for the inaccurately portraying the magnitude of differences.

Both academic researchers and the producers of these political mailers may counter by saying

But the axes were labelled accurately!

If readers would understand your data better if they ignored the graphical elements of your plot, then your plot is net-negative to accurate communication, and I can only assume that accuracy was never your primary goal anyway.

Think about degree of abstraction.

Another fraught issue in academic papers is how to accurately convey the volume of their data. For example, in the iy_ah data set, you might be tempted to plot all of the data.

iy_ah %>% 
  ggplot(aes(F2, F1, color = plt_vclass))+
    geom_point(alpha = 0.5)+
    scale_y_reverse()+
    scale_x_reverse()+
    scale_color_brewer(palette = "Dark2")+
    theme_minimal()

Or, you might be tempted to plot a summary of some sort.

iy_ah %>%
  group_by(plt_vclass) %>%
  summarise(F1_mean = mean(F1),
            F2_mean = mean(F2),
            F1_hi = F1_mean + 2*(sd(F1)),
            F1_lo = F1_mean - 2*(sd(F1)),
            F2_hi = F2_mean + 2*(sd(F2)),            
            F2_lo = F2_mean - 2*(sd(F2))) -> iy_ah_summ
ggplot(iy_ah_summ, aes(F2_mean, F1_mean, color = plt_vclass))+
    geom_point()+
    geom_segment(aes(y = F1_mean, 
                     yend = F1_mean, 
                     x = F2_lo, 
                     xend = F2_hi))+
      geom_segment(aes(y = F1_lo, 
                     yend = F1_hi, 
                     x = F2_mean, 
                     xend = F2_mean))+
    stat_ellipse(data = iy_ah, aes(x = F2, y = F1))+
    scale_color_brewer(palette = "Dark2")+
    scale_y_reverse()+
    scale_x_reverse()+  
    theme_minimal()

Neither of these is really optimal. The first one looks like you’ve got a billion data points, but really, most of these are reptition of the same vowel from the same speakers. The second one is an ok-ish, summary, but hides more than it shows.

What I tend to do is calculate means per-speaker, and then plot those means:

iy_ah %>% 
  group_by(idstring, plt_vclass)%>%
  summarise(F1 = mean(F1),
            F2 = mean(F2),
            n = n())%>%
  ggplot(aes(F2, F1, color = plt_vclass))+
    geom_point(alpha = 0.5, aes(size = n))+
    scale_y_reverse()+
    scale_x_reverse()+
    scale_size_area()+
    scale_color_brewer(palette = "Dark2")+
    theme_minimal()

This is often a more accurate portrayal of how much data you have, since there is a lot of data from a smaller number of speakers.

ggplot2 basic concepts

Layers, Aesthetics, Geometries and Statistics

The first thing we’re going to do is build up to creating this plot, which is a visualization of the I_jean data frame (which you should have loaded in the setup block).

Layers

You should hopefully start looking at figures like this one like many of us look at the image below.

Those of use familiar with this kind of media know that the picture of the libarary is not what was originally capture by my phone. Rather there are multiple layers of effects, filters and text on top of the base image, which produce the final image. And in fact, some of these layers are crucially ordered. For example, the text would look different if it was added to the image first, and then the filters, instead of vice versa.

So too with the ggplot2 plot above. These plots are constructed out of layers. Every component of the graph, from the underlying data it’s plotting, to the coordinate system it’s plotted on, to the statistical summaries overlaid on top, to the axis labels, are layers in the plot. The consequence of this is that your use of ggplot2 will probably involve iterative addition of layer upon layer until you’re pleased with the results.

Aesthetics

The graphical properties which encode the data you’re presenting are the aesthetics of the plot. These include things like

  • x position
  • y position
  • size of elements
  • shape of elements
  • color of elements

Geometries

The primary visual items on the plots are called geometries and include things like

  • points
  • lines
  • line segments
  • bars
  • text

Some of these geometries have their own specific aesthetic settings. For example,

  • points
    • point shape
  • text
    • text labels
  • lines
    • line weight
    • line type

Statistics

You’ll also frequently want to plot statistics overlaid on top of, or instead of the raw data. Some of these include

  • Smoothing and regression lines
  • One and two dimensional binning
  • Mean and medians with confidence intervals.

The aesthetics, geometries and statistics constitute the most important layers of a plot, but for fine tuning a plot for publication, there are a number of other things you’ll want to adjust. The most common one of these are the scales, which encompass things like

  • A logarithmic x or y axis
  • Customized color scales
  • Customized point shapes, or linetypes

We’ll review many of these components as we build up the plot, and will circle back to more of them for greater detail.

Building the Plot

First, let’s refresh our memories of the graph we want to build.

This plot is composed of nine layers, which can be subdivided into five layer types. It’s not important for you to memorize these layer types, but it helps to structure the discussion.

Layers

The data layer

Every ggplot2 plot has a data layer, which defines the data set to plot, and the basic mappings of data to aesthetic elements. The data layer created with the functions ggplot() and aes(), and looks like this

ggplot(data, aes(...))

The first argument to ggplot() is a data frame (it must be a data frame), and its second argument is aes(). You’re never going to use aes() in any other context except for inside of other ggplot2 functions, so it might be best not to think of aes() as its own function, but rather as a special way of defining data-to-aesthetic mappings.

For the plot from above, we’ll be using data from the I_jean data frame, which looks like this:

head(I_jean)

I’ve decided that an interesting relationship in this data is between the vowel duration (Dur_msec) and the normalized F1 of the vowel (F1.n). Specifically, I’d like to map Dur_msec to the x-axis, and F1.n to the y-axis. Here’s the ggplot2 code.

  p <- ggplot(I_jean, aes(x=Dur_msec, y=F1.n))
  p

You can think of this plot as the base image, before we’ve added any extra layers, text or instagram filters to it. An important conceptual issue is that you are able to assign plots to variables (in this case, p). When you do this assignment, nothing special happens. But if you print out p, R will generate the plot.

The geometries layer

The next step, after defining the basic data-to-aesthetic mappings, is to add geometries to the data. We’ll discuss geometries in more detail below, but for now, we’ll add one of the simplest: points.

  p <- p + geom_point()
  p

There are a few things to take away from this step. First and foremost, the way you add new layers, of any kind, to a plot is with the + operator. And, as we’ll see in a moment, there’s no need to only add them one at a time. You can string together any number of layers to add to a plot, separated by +.

The next thing to notice is that all layers you add to a plot are, technically, functions. We didn’t pass any arguments to geom_point(), so the resulting plot represents the default behavior: solid black circular points.

If for no good reason at all we wanted to use a different point shape in the plot, we could specify it inside of geom_point().

ggplot(I_jean, aes(x=Dur_msec, y=F1.n)) +
  geom_point(shape = 3)

Or, if we wanted to use larger, red points, we could specify that in geom_point() as well.

ggplot(I_jean, aes(x=Dur_msec, y=F1.n)) +
  geom_point(color = "red", size = 3)

Speaking of defaults, we can see a few of the default setting of ggplot2 on display here. Most striking is the light grey background, with white grid lines. Opinion varies on whether or not this is aesthetically or technically pleasing, but don’t worry, it’s adjustable.

Another default is to label the x and y axes with the column names from the data frame. I’ll inject a bit of best practice advice here, and tell you to always change the axis names. It’s nearly guaranteed that your data frame column names will make for very poor axis labels. We’ll cover how to do that shortly.

Finally, note that we didn’t need to tell geom_point() about the x and y axes. This may seem trivial, but it’s a really important, and powerful aspect of ggplot2. When you add any layer at all to a plot, it will inherit the data-to-aesthetic mappings which were defined in the data layer. We’ll discuss inheritance, and how to override, or define new data-to-aesthetic mappings within any geom.

The statistics layer

The final figure also includes a smoothing line, which is one of many possible statistical layers we can add to a plot.

  p <- p + stat_smooth()
  p

We’ll go over the default behavior of stat_smooth() below, but in this plot, the smoothing line represents a loess smooth, and the semi-transparent ribbon surrounding the solid line is the 95% confidence interval.

One important thing to realize is that it’s not necessary to include the points in order to add a smoothing line. Here’s what the plot would look like with the points omitted.

ggplot(I_jean, aes(x = Dur_msec, y = F1.n))+
  stat_smooth()

Notice how the y-axis has zoomed in to just include the range of the smoothing line and standard error.

Scale transformations

I also wanted to make some alterations to the default x and y axis scales. For example, the y-axis is currently running in reverse to the intuitive direction of F1. Higher vowels have lower F1 values, so we want to flip the y-axis. Additionally, durations are typically best displayed along a logarithmic scale, so we should convert the x-axis as well.

p <- p + scale_x_log10(breaks = c(50, 100, 200, 300, 400))+
         scale_y_reverse()
p

It’s worth noting that the smoothing line here is calculated over the transformed data.

Cosmetic alterations

Finally, I wanted to make some cosmetic adjustments to the plot. For example, the x-axis label “Dur_msec” is not quite as useful as “Vowel duration (msec)” would be. I also added a title to the plot, and changed the color theme to black and white.

p <- p + ylab("Normalized F1")+
         xlab("Vowel duration (msec)")+
         theme_bw()+
         ggtitle("394 tokens of 'I' from one speaker")
p


Here’s all the layers, put together all at the same time.

ggplot(I_jean, aes(x=Dur_msec, y=F1.n))+
  geom_point()+
  stat_smooth()+
  scale_x_log10(breaks = c(50, 100, 200, 300, 400))+
  scale_y_reverse()+
  ylab("Normalized F1")+
  xlab("Vowel duration (msec)")+
  theme_bw()+
  ggtitle("394 tokens of 'I' from one speaker")

Idiom

As with pipes %>%, I recommend putting a new line after every + in a chain of ggplot2 layers, and indenting the following line two spaces.

Elaborating & More Options

For better or worse, ggplot2 plots are easilly identifiable, and you’ll inevitablly start seeing them everywhere now. Sometimes you’ll see a plot that does something cool that you didn’t realize you could do in ggplot2. When that happens to me, I google “how do you X in ggplot2”, and it usually works.

Aesthetics

In ggplot2, aesthetics are the graphical elements which are mapped to data, and they are defined with aes(). To some extent, the aesthetics you need to define are dependent on the geometries you want to use, because line segments have different geometric properties than points, for example. However, there is also a great deal of uniformity in the aesthetics used across geometries. Here is a list of the most common aesthetics you’ll want to define.

  • x
    • x-axis location
  • y
    • y-axis location
  • color
    • The color of lines, points, and the outside borders of two dimensional geometries (polygons, bars, etc.). Hadley Wickham, the primary ggplot2 developer, is from New Zealand, so colour is also supported!
  • fill
    • The fill color of two dimensional geometries.
  • size
    • The size of points, or the weight of lines and borders of two dimensional geometries.
  • shape
    • This is specific to points, and defines the point shape. This is one of the few aesthetics to which you can’t map a continuous variable.
  • linetype
    • This defines the line type of any kind of line, path, or border of a two dimensional geometry. This is another aesthetic which cannot be mapped to a continuous variable.
  • alpha
    • This defines the opacity of any geometric property. It’s less commonly mapped to data, and more often hard coded to a single value as a solution for overplotting.
  • xend, yend
    • You’ll use these more rarely, usually when plotting a line segment, or arrow. The beginning of the line segment will be located at x, y, and the end of the line segment will be located at xend,yend.
  • ymin, ymax, (xmin, xmax)
    • ymin and ymax are reserved for geometries which are devoted to representing ranges of data, like error bars, and ribbons. For the most part, these will be expressed along the y-axis, but xmin and xmax are utilized for some geometries as well.

The most important thing to keep in mind about aesthetics is not what they’re called, though, but how they are inherited by the layers. Let’s start by mapping the Word to color. % of the tokens are just “I”, so lets create a subset of the data that excludes “I” so it doesn’t visually swamp the plot.

Mapping Data to Aesthetics

I_subset <- subset(I_jean, Word != "I")
ggplot(I_subset, aes(Dur_msec, F1.n, color = Word))+
  geom_point()

Each point is now colored according to the word it corresponds to. ggplot2 has automatically generated a color palette of the right type and size, based on the data mapped to color, and created a legend to the side. As with everything, the specific color palette we use is adjustable, which will be discussed in more detail below under Scales. There are positive features of the default color palette, but some people don’t like it aesthetically, and they can be hard for some colorblind readers. They’ll also all print to the same shade of grey!

Inheritance

If we add one more geometry (a line), we see that it also inherits the mapping of Word to color.

ggplot(I_subset, aes(Dur_msec, F1.n, color = Word))+
  geom_point()+
  geom_line()

There are a few important things to take note of in this plot. First, you can see that we have actually added four lines to the plot, one for each color. In most cases, when you map categorical data to an aesthetic like color, you are also defining sub-groupings of the data, and ggplot2 will draw a lines, calculate statistics, etc. separately for every sub-grouping of the data.

The second important thing to notice is that geom_line() joins up points as they are ordered along the x-axis, not according to their order in the original data frame. There is a geom which will join up points that way called geom_path().

The point here, though, is that it is possible to define data-to-aesthetic mappings inside of geom functions, also by using aes(). Here, instead of mapping Word to color inside of ggplot(), we’ll do it inside of geom_point().

ggplot(I_subset, aes(Dur_msec, F1.n))+
  geom_point(aes(color = Word))+
  geom_line()

The points are still colored according to the word, but there is only one, black line. We can also try passing aes(color = Word) to geom_line().

ggplot(I_subset, aes(Dur_msec, F1.n))+
  geom_point()+
  geom_line( aes(color = Word))

Now, the lines are colored according to the word, but the points are all black. This brings up the all important point about aesthetics:

Geoms inherit aesthetic mappings from the ggplot() data layer, and not from any other layer.

Grouping

Let’s look at the effect of mapping Word to color on the calculation of statistics, like smoothing lines. Note, inside of stat_smooth() I’ve said se = F to turn off the display of standard errors.

ggplot(I_subset, aes(Dur_msec, F1.n, color=Word))+
  geom_point()+
  stat_smooth(se = F)

Just like separate lines were drawn for each group as defined by color=Word, ggplot2 has calculated separate smoothers for each subset. If we had only passed color=Word to geom_point(), though, stat_smooth() would not have inherited that mapping, resulting in a single smoother being calculated.

  ggplot(I_subset, aes(Dur_msec, F1.n))+
    geom_point(aes(color=Word))+
    stat_smooth(se = F)

It’s important to understand that when you map categorical variables to an aesthetic that you’re also defining sub-groupings. For example, if we map Word to shape, instead of color, the point shapes will now represent the word.

ggplot(I_subset, aes(Dur_msec, F1.n, shape=Word))+
  geom_point()

Now if we add a smoother to this plot, even though shape isn’t defined for lines, the smoother will still plot a different smoothing curve for each sub-grouping.

ggplot(I_subset, aes(Dur_msec, F1.n, shape=Word))+
  geom_point()+
  stat_smooth(se = F)

If you really only wanted a single smoother line for all of the data in this case, one solution would be to move the shape=Word mapping from the data layer to the geom_point() layer. But in most cases, it’s actually more desirable to override the aesthetic mapping. We can do this with the special aesthetic group.

group does exactly what it sounds like it ought to: it defines groups of data. When you want to override groups defined in the data layer, you can do so by saying group=1.

ggplot(I_subset, aes(Dur_msec, F1.n, shape=Word))+
  geom_point()+
  stat_smooth(se = F, aes(group = 1))

The effect it has on stat_smooth() is that just a single smoother is calculated. If we come back to color = Word, and then draw a line with group = 1, the effect is that we draw one line that varies in color.

ggplot(I_subset, aes(Dur_msec, F1.n, color=Word))+
  geom_line(aes(group = 1))

More aesthetics and their use.

So far, we’ve only mapped categorical variables to color, but it’s also possible to map continuous variables to color. Here we’ll redundantly map F1.n to both y and color.

ggplot(I_jean, aes(Dur_msec, F1.n, color = F1.n))+
  geom_point()

Another important aesthetics distinction is between color and fill. If we wanted to create a bar chart of word frequencies, we could do so by mapping Word to the x-axis, and adding geom_bar() without any y-axis variable defined.

ggplot(I_jean, aes(Word))+
  geom_bar()

If you also wanted to color the bars according to the word, your first instinct would probably be to map color = Word. But the result is that only the colors of the bars’ outlines are mapped to Word.

ggplot(I_jean, aes(Word, color = Word))+
  geom_bar()

What is probably more advisable is to map Word to fill, which control the filling color of two dimensional geoms.

ggplot(I_jean, aes(Word, fill = Word))+
  geom_bar()

As you might have figured out now, it’s technically possible to map the fill color of bars to one variable, and the outline color to different variable. My advice is to never do such a thing, because the results almost always come out a jumbled mess. Instead, I would suggest setting the color of the bars to black. I find it more pleasing to the eye, and helps to emphasize the divisions between bars when they’re stacked. Compare this plot:

ggplot(I_subset, aes(Name, fill = Word))+
  geom_bar()

to this one.

ggplot(I_subset, aes(Name, fill = Word))+
  geom_bar(color = "black")

Geometries

So far, we’ve used the following geometries:

  • geom_point()
  • geom_line()
  • geom_bar()

All geometries begin with geom_, meaning you can get a full list using apropos().

apropos("^geom_")
 [1] "geom_abline"     "geom_area"       "geom_bar"       
 [4] "geom_bin2d"      "geom_blank"      "geom_boxplot"   
 [7] "geom_col"        "geom_contour"    "geom_count"     
[10] "geom_crossbar"   "geom_curve"      "geom_density"   
[13] "geom_density_2d" "geom_density2d"  "geom_dotplot"   
[16] "geom_errorbar"   "geom_errorbarh"  "geom_freqpoly"  
[19] "geom_hex"        "geom_histogram"  "geom_hline"     
[22] "geom_jitter"     "geom_label"      "geom_line"      
[25] "geom_linerange"  "geom_map"        "geom_path"      
[28] "geom_point"      "geom_pointrange" "geom_polygon"   
[31] "geom_qq"         "geom_quantile"   "geom_raster"    
[34] "geom_rect"       "geom_ribbon"     "geom_rug"       
[37] "geom_segment"    "geom_smooth"     "geom_spoke"     
[40] "geom_step"       "geom_text"       "geom_tile"      
[43] "geom_violin"     "geom_vline"     

This is a quite extensive list, and we won’t be able to cover them all today. Many of them are actually convenience functions for special settings of other geoms. For example, geom_histogram() is really just geom_bar() with special settings.

ggplot(I_jean, aes(F1.n))+
  geom_histogram()

Other geoms are just convenience functions for statistical layers. For example, you’ll notice geom_smooth(), which if you add it to a plot will have the same behavior of stat_smooth(), which we’ve already been using extensively.

ggplot(I_jean, aes(Dur_msec, F1.n))+
  geom_smooth()

stat_smooth() can actually plot many different kinds of smoothers. For a linear model, for example:

ggplot(I_jean, aes(Dur_msec, F1.n))+
  geom_smooth(method = 'lm')

Some special geoms

Some geoms are both unique and common enough in their usage to warrant special mention.

geom_text() and geom_label()

Adding text, and text labels to a plot, is a very common task, and is done with geom_text(). There is a special aesthetic just for geom_text() called label, which defines the column that should be used as the text label.

ggplot(I_subset, aes(Dur_msec, F1.n))+
  geom_text(aes(label = Word))

ggplot(I_subset, aes(Dur_msec, F1.n))+
  geom_label(aes(label = Word))

Positioning

Just like inheritance was the big idea for aesthetics, positioning is the big idea for geoms. For various reasons, you may want to adjust where geometries are plotted. As a solution to overplotting, for example, you may want to add some jitter to points. When dealing with bars, you need to decide whether they should be stacked, or arranged next to each other. These small adjustments

  • identity
    • This is the default in most cases, simply plotting geometries where they’re defined by x and y.
  • jitter
    • This adds some random noise either to the x position or the the y position, and is typically used just for points.
  • stack
    • This stacks geometries on top of each other. This is the default for bars
  • dodge
    • This pushes geometries out of each other’s way, to the left and right.
  • fill
    • This stacks geometries on top of each other, and expands or contracts them to fill the space between 0 and 1. Good for plotting proportions.
Jitter

Some people, like Andrew Gelman, hate boxplots.

ggplot(I_jean, aes(Word, F1.n))+
  geom_boxplot()

A frequent suggestion for a replacement to boxplots is just to plot the raw data points with some jitter. To get started, we’ll replace geom_boxplot() with geom_point().

ggplot(I_jean, aes(Word, F1.n))+
  geom_point()

And the add some jitter, by defining position = "jitter" inside of geom_point().

ggplot(I_jean, aes(Word, F1.n))+
  geom_point(position = "jitter")

In this example, you can see the benefit of jittered points over boxplots. With boxplots, there’s no hint that one category, “I” has enormously more data than the others.

As a convenience, there’s a geom called geom_jitter(), which is just a convenience function for geom_point(position = "jitter").

ggplot(I_jean, aes(Word, F1.n))+
  geom_jitter()

Scales

Scales provide you more fine grained control over over the presentation of aesthetics. For every aesthetic, there is a corresponding scale you can use. For more flexibility, you should also install and load the scales package.

  library(scales)

x and y scales

All scales begin with scale_ followed by the name of the aesthetic it controls, then followed by its sub-type. Here are all the x-axis scales, for which there are identical y-axis scales.

apropos("^scale_x_")
[1] "scale_x_continuous" "scale_x_date"       "scale_x_datetime"  
[4] "scale_x_discrete"   "scale_x_log10"      "scale_x_reverse"   
[7] "scale_x_sqrt"       "scale_x_time"      

scale_x_continuous, scale_x_discrete, scale_x_datetime and scale_x_date are the basic kinds of x and y axes you can construct in ggplot2. For the most part, ggplot2 will figure out which kind of scale to use, and you’ll only need to add one of these if you want to modify the default appearance.

scale_x_log10, scale_x_sqrt and scale_x_reverse are basic transformations to a continuous scale. Many more kinds of transformations are possible, and it’s even possible to define your own custom transformations, but these are the only ones for which there are special convenience scale_x_* functions.

scale_ arguments

There are a few basic arguments that you can pass to a scale.

  • name
    • This is the title that will be displayed on the scale, either on the axes for x and y scales, or in the legend for other scales.
  • limits
    • This defines the range of data to be presented in the plot. For discrete scales, it’ll define the order with which to display categorical factors.
  • breaks
    • These are the labeled points along the scale, either the major axis labels, or the labels on the legend.
  • labels
    • This defines the labels to use at each break point, if you want to override them
  • trans
    • This defines a numerical transformation you’d like to apply to a scale, and usually only applicable to continuous scales. We’ll talk more about transformations below.

scale_[xy]_continuous

Here’s our basic duration by F1 plot again, which has two continuous scales for the x and y axes.

ggplot(I_jean, aes(Dur_msec, F1.n))+
  geom_point()

If we want to change the x axis label, we can do so by adding scale_x_continious() and passing our desired title to name.

ggplot(I_jean, aes(Dur_msec, F1.n))+
  geom_point()+
  scale_x_continuous(name = "Vowel Duration (msec)")

Since changing the axis titles is something you’re likely to do very frequently, there are two convenience functions for this purpose with shorter names that save you typing: xlab() and ylab().

ggplot(I_jean, aes(Dur_msec, F1.n))+
  geom_point()+
  xlab("Vowel Duration (msec)")

The next most important thing you’ll want to do to x and y axis scales is transform them. For example, duration measurements tend to be left-skewed, so a log transformation is advisable. The benefit of transforming the scale over simply plotting log2(Dur_msec) is that ggplot2 will very nicely label the axis according to the original values. Compare the labels of the x-axes of these two plots.

ggplot(I_jean, aes(log2(Dur_msec), F1.n))+
  geom_point()+
  scale_x_continuous("log2 Vowel Duration (msec)")

ggplot(I_jean, aes(Dur_msec, F1.n))+
  geom_point()+
  scale_x_continuous("Vowel Duration (msec)",
                     trans = "log2")

These are all of the pre-defined transformations.

apropos("_trans$")
 [1] "asn_trans"         "atanh_trans"      
 [3] "boxcox_trans"      "coord_trans"      
 [5] "date_trans"        "exp_trans"        
 [7] "hms_trans"         "identity_trans"   
 [9] "log_trans"         "log10_trans"      
[11] "log1p_trans"       "log2_trans"       
[13] "logit_trans"       "probability_trans"
[15] "probit_trans"      "reciprocal_trans" 
[17] "reverse_trans"     "sqrt_trans"       
[19] "time_trans"       

color and fill scales

Here are all of the color scales, for all of which there is an accompanying fill scale.

apropos("^scale_color_")
 [1] "scale_color_brewer"     "scale_color_continuous"
 [3] "scale_color_discrete"   "scale_color_distiller" 
 [5] "scale_color_gradient"   "scale_color_gradient2" 
 [7] "scale_color_gradientn"  "scale_color_grey"      
 [9] "scale_color_hue"        "scale_color_identity"  
[11] "scale_color_manual"    

There are two very distinct kinds of color scales here: categorical and gradient. The use of one over the other has everything to do with the kind of data which is passed to color and fill, and they have their own specific customizations.

Categorical color scales

Here is the default categorical color scale, which is called scale_[fill/color]_hue()

ggplot(I_jean, aes(Word, Dur_msec, fill = Word))+
  stat_summary(fun.y = mean, geom = "bar")+
  scale_fill_hue()

Just like the x and y scales, you can change the title, limits, breaks and labels of this scale, which will control how it appears in the legend. Here’s an illustrative example, which actually detracts from the default

ggplot(I_jean, aes(Word, Dur_msec, fill = Word))+
  stat_summary(fun.y = mean, geom = "bar")+
  scale_fill_hue(name = "Lexical Item",
                 limits = c("I'D","I'VE","I'LL","I'M","I"),
                 labels = c("'D","'VE","'LL","'M",""))

Many people don’t like the default color scheme, but there are other available color palettes, and you can define your own. One really nice set of color palettes comes from the package RColorBrewer. You can explore the set of available color palettes available in it here. A personal favorite of mine is called Set1, which you can apply to the plot with scale_fill_brewer()

ggplot(I_jean, aes(Word, Dur_msec, fill = Word))+
  stat_summary(fun.y = mean, geom = "bar")+
  scale_fill_brewer(palette = "Set1")

If you are particularly picky, or want to express your questionable aesthetic sense to the world, there is also scale_fill_manual(), where you define an arbitrary list of colors to use for the scale.

ggplot(I_jean, aes(Word, Dur_msec, fill = Word))+
  stat_summary(fun.y = mean, geom = "bar")+
  scale_fill_manual(values=c("bisque", "chartreuse4",
                             "hotpink","yellow", "red"))

And finally, if you’re preparing a plot for publication, and you want to be sure that the colors will be distinguishable when printed in black and white, there is scale_fill_grey().

ggplot(I_jean, aes(Word, Dur_msec, fill = Word))+
  stat_summary(fun.y = mean, geom = "bar")+
  scale_fill_grey()

Gradient color scales

There are also a nice set of customizable gradient color scales. Here’s the default gradient color scale, which is called scale_fill_gradient()

ggplot(I_jean, aes(-F2.n, -F1.n))+
  stat_density2d(geom = "tile",
                 contour = F, 
                 aes(fill = ..density..))+
  scale_fill_gradient()

scale_fill_gradient() constructs a color continuum from A to B, where the lowest values in the plot will have solid A, and the highest values in the plot will have solid B, and everything else will fall along the gradient. It’s possible to override the original two colors that the gradient is built between by passing the colors you prefer to low and high.

ggplot(I_jean, aes(-F2.n, -F1.n))+
  stat_density2d(geom = "tile",
                 contour = F, 
                 aes(fill = ..density..))+
  scale_fill_gradient(low="darkblue",high="darkred")

It’s also possible to define a gradient that passes from A to B via C with scale_fill_gradient2(). Here, you define low and high, as well as a third color you want the gradient to transition through, mid. You also need to define the value the scale should treat as a midpoint.

ggplot(I_jean, aes(-F2.n, -F1.n))+
  stat_density2d(geom = "tile",
                 contour = F, 
                 aes(fill = ..density..))+
  scale_fill_gradient2(low="darkblue",
                       high="darkred",
                       mid="white",
                       midpoint=0.5)

And, finally, you’re able to define a color gradient passing through any arbitrary colors with scale_fill_gradientn(). Here’s a pretty ugly one:

ggplot(I_jean, aes(-F2.n, -F1.n))+
  stat_density2d(geom = "tile",
                 contour = F, 
                 aes(fill = ..density..))+
  scale_fill_gradientn(colours = c("bisque", 
                                   "chartreuse4",
                                   "hotpink",
                                   "yellow"))

A benefit to having scale_fill_gradientn() is that you can utilize some of R’s built in color palettes, like rainbow(), terrain.colors() and topo.colors()

ggplot(I_jean, aes(-F2.n, -F1.n))+
  stat_density2d(geom = "tile",
                 contour = F, 
                 aes(fill = ..density..))+
  scale_fill_gradientn(colours = rainbow(6))

ggplot(I_jean, aes(-F2.n, -F1.n))+
  stat_density2d(geom = "tile",
                 contour = F, 
                 aes(fill = ..density..))+
  scale_fill_gradientn(colours = terrain.colors(6))

ggplot(I_jean, aes(-F2.n, -F1.n))+
  stat_density2d(geom = "tile",
                 contour = F, 
                 aes(fill = ..density..))+
  scale_fill_gradientn(colours = topo.colors(6))

Guides

A set of new mechanics for handling the presentation of legends was introduced with version 0.9.0, including a new kind of legend for continuous color scales: the colorbar. The plots above have all used the default guide type (legend) which displays the color value for specific breaks, but not the gradient in between. The color bar guides show the entire gradient, with the breaks labeled over top.

ggplot(I_jean, aes(-F2.n, -F1.n))+
  stat_density2d(geom = "tile",
                 contour = F, 
                 aes(fill = ..density..))+
  scale_fill_gradientn(colours = rainbow(6),
                       guide = "colorbar")

shape and linetype

The shape and linetype scales are much more limited. Despite the apparent existence of scale_shape_continuous and scale_linetype_continuous, you can’t actually pass continuous variable to these aesthetics.

apropos("^scale_shape_")
[1] "scale_shape_continuous" "scale_shape_discrete"  
[3] "scale_shape_identity"   "scale_shape_manual"    
apropos("^scale_linetype_")
[1] "scale_linetype_continuous" "scale_linetype_discrete"  
[3] "scale_linetype_identity"   "scale_linetype_manual"    

The only time you’ll be likely to use the shape and linetype scales is when you want to manually control the point shape and linetypes to be used. I actually find the default shape scale to not be strongly contrastive, and prefer to contrast point types based on filled vs hollow shapes.

ggplot(I_subset, aes(Dur_msec, F1.n, shape = Word))+
  geom_point()

ggplot(I_subset, aes(Dur_msec, F1.n, shape = Word))+
  geom_point()+
  scale_shape_manual(values=c(1,1, 19, 19))

Other scales

apropos("^scale_size_")
[1] "scale_size_area"       "scale_size_continuous"
[3] "scale_size_date"       "scale_size_datetime"  
[5] "scale_size_discrete"   "scale_size_identity"  
[7] "scale_size_manual"    
apropos("^scale_alpha_")
[1] "scale_alpha_continuous" "scale_alpha_discrete"  
[3] "scale_alpha_identity"   "scale_alpha_manual"    

Faceting

A really powerful graphical technique is the small multiple, and ggplot2 allows for easy creation of small multiples via faceting. Let’s create an additional categorical variable for the entire data set (we did this already for the subset excluding “I”).

  I_jean <- I_jean %>%
                mutate(Dur_cat = Dur_msec > mean(Dur_msec))

facet_wrap

If we wanted to create an F2 x F1 plot for every word, we’d start out by creating a simple F1 x F2 plot:

ggplot(I_jean, aes(-F2.n, -F1.n ))+
  geom_point()

and then faceting by Word with facet_wrap().

ggplot(I_jean, aes(-F2.n, -F1.n ))+
  geom_point()+
  facet_wrap(~Word)

You can exercise some control about the layout of facets with ncol and nrow. For example. if you really only wanted there to be 2 columns of facets, you could make that happen with by passing ncol=2 to facet_wrap()

ggplot(I_jean, aes(-F2.n, -F1.n ))+
  geom_point()+
  facet_wrap(~Word, ncol = 2)

Or, if you wanted all the facets to be lined up in one row, you would pass nrow = 1.

ggplot(I_jean, aes(-F2.n, -F1.n ))+
  geom_point()+
  facet_wrap(~Word, nrow = 1)

By default, the ranges of the axes in each facet are fixed to be the same across all facets, and that should be changed only in very limited circumstances. You can set the x axis, y axis, or both to be free by passing the following arguments to scales inside of facet_wrap().

## Inadvisable
ggplot(I_jean, aes(-F2.n, -F1.n ))+
  geom_point()+
  facet_wrap(~Word, scales = "free_x")

## Inadvisable
ggplot(I_jean, aes(-F2.n, -F1.n ))+
  geom_point()+
  facet_wrap(~Word, scales = "free_y")

## Inadvisable
ggplot(I_jean, aes(-F2.n, -F1.n ))+
  geom_point()+
  facet_wrap(~Word, scales = "free")

facet_grid

facet_grid() is another form of faceting in two dimensions.

ggplot(I_jean, aes(-F2.n, -F1.n ))+
  geom_point()+
  facet_grid(Dur_cat~Word)

ggplot(I_jean, aes(-F2.n, -F1.n ))+
  geom_point()+
  facet_grid(Word~Dur_cat)

ggplot(I_jean, aes(-F2.n, -F1.n ))+
  geom_point()+
  facet_grid(Dur_cat~Word, scales = "free")

LS0tCnRpdGxlOiAiUGxvdHRpbmcgd2l0aCBnZ3Bsb3QyIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogbm9uZQogICAgY3NzOiAuLi9jdXN0b20uY3NzCiAgICB0aGVtZTogZmxhdGx5CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAnMycKLS0tCgoKCgojIEludHJvCgoKPGRpdiBjbGFzcyA9ICJib3ggYnJlYWsiPgo8c3BhbiBjbGFzcyA9ICdiaWctbGFiZWwnPn4yIE1pbnV0ZSBTZXR1cDwvc3Bhbj4KCkNyZWF0ZSB5b3VyIFIgTm90ZWJvb2sgZm9yIHRvZGF5IGFuZCBkb3VibGUgY2hlY2sgdGhhdCB5b3VyIHdvcmtzcGFjZSBpcyBjbGVhciBmcm9tIGxhc3QgdGltZS4KCkxvYWQgdGhlIGltcG9ydGFudCBwYWNrYWdlcyBmb3IgdG9kYXk6CgpgYGB7ciBldmFsID0gRn0KIycgWW91IG9ubHkgbmVlZCB0byBydW4gdGhpcyBvbmNlCmluc3RhbGwucGFja2FnZXMoInNjYWxlcyIpCmluc3RhbGwucGFja2FnZXMoImdndGhlbWVzIikKYGBgCgoKCmBgYHtyfQpsaWJyYXJ5KCJnZ3Bsb3QyIikKbGlicmFyeSgibHNhMjAxNyIpCmxpYnJhcnkoInRpZHl2ZXJzZSIpCmxpYnJhcnkoImJyb29tIikKSV9qZWFuIDwtIHJlYWQuZGVsaW0oImh0dHA6Ly9iaXQubHkvYXZtbF9nZ3Bsb3QyX2RhdGEiKQpgYGAKCjwvZGl2PgoKCgoKCiMjIFdoeSBQbG90CgpXZSBhcmUgaGF2aW5nIGEgbGVzc29uIG9uIHBsb3R0aW5nIGluIHRoZSBtaWRkbGUgb2YgYSBjb3Vyc2Ugb24gUiBtb2RlbGxpbmcgYmVjYXVzZSBpdCBpcyAqZXNzZW50aWFsKiBmb3IgeW91IHRvIHBsb3QgeW91ciBkYXRhIGJlZm9yZSB5b3UgdHJ5IHRvIG1vZGVsIGl0LiBJIHdvdWxkIGdvIHNvIGZhciBhcyB0byBzYXkgdGhhdCBpZiB5b3UgaGF2ZW4ndCBtYWRlIGEgKmxvdCogb2YgZ3JhcGhzIG9mIHlvdXIgZGF0YSwgYW5kIGhhdmUgb25seSBsb29rZWQgYXQgYXZlcmFnZXMsIGNvcnJlbGF0aW9ucywgYW5kIGxpbmVhciBtb2RlbCByZXN1bHRzLCB0aGF0IHlvdSBkb24ndCByZWFsbHkgdW5kZXJzdGFuZCB5b3VyIGRhdGEuIAoKVGhlcmUncyBhIGNsYXNzaWMgaWxsdXN0cmF0aW9uIG9mIHRoaXMgY2FsbGVkIEFuc2NvbWJlJ3MgcXVhcnRldCwgd2hpY2ggd2hlbiBwbG90dGVkIGxvb2tzIGxpa2UgdGhyZWUgdmVyeSBkaXN0aW5jdGl2ZSBwYXR0ZXJucy4KCmBgYHtyIGVjaG8gPSBGLCBkZXYgPSAnc3ZnJ30KdGlkeV9hbnNjb21iZSA8LSBhbnNjb21iZSAlPiUKICAgICAgICAgICAgICAgICAgICBtdXRhdGUoaWR4ID0gMTpuKCkpICU+JQogICAgICAgICAgICAgICAgICAgIGdhdGhlcihrZXksIHZhbHVlLCB4MTp5NCklPiUKICAgICAgICAgICAgICAgICAgICBzZXBhcmF0ZShrZXksIGludG8gPSBjKCJ2YXJpYWJsZSIsICJzZXJpZXMiKSwgc2VwID0gMSklPiUKICAgICAgICAgICAgICAgICAgICBzcHJlYWQodmFyaWFibGUsIHZhbHVlKQoKdGlkeV9hbnNjb21iZSAlPiUKICBnZ3Bsb3QoYWVzKHgsIHkpKSArIAogICAgZ2VvbV9wb2ludCgpICsgCiAgICBmYWNldF93cmFwKH5zZXJpZXMpCmBgYAoKQnV0IGlmIHlvdSBmaXQgbGluZWFyIG1vZGVscyB0byB0aGVtLCB0aGV5IGhhdmUgbmVhcmx5IGlkZW50aWNhbCBzdGF0aXN0aWNhbCBwcm9wZXJ0aWVzLgoKYGBge3J9CmZpdF9sbSA8LSBmdW5jdGlvbihkZil7CiAgbG0oeSB+IHgsIGRhdGEgPSBkZikKfQphbnNjb21iX21vZGVscyA8LSB0aWR5X2Fuc2NvbWJlICU+JQogICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5KHNlcmllcyklPiUKICAgICAgICAgICAgICAgICAgICBuZXN0KCklPiUKICAgICAgICAgICAgICAgICAgICBtdXRhdGUobW9kZWwgPSBtYXAoZGF0YSwgZml0X2xtKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWxfcGFyYW1fZGYgPSBtYXAobW9kZWwsIHRpZHkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbF9nbGFuY2UgPSBtYXAobW9kZWwsIGdsYW5jZSkpCgphbnNjb21iX21vZGVscyAlPiUKICB1bm5lc3QobW9kZWxfcGFyYW1fZGYpJT4lCiAgYXJyYW5nZSh0ZXJtKQpgYGAKYGBge3J9CmFuc2NvbWJfbW9kZWxzICU+JQogIHVubmVzdChtb2RlbF9nbGFuY2UpCmBgYAoKSXQgaGFzIGJlZW4gZXZlbiBtb3JlIGh1bW9yb3VzbHkgaWxsdXN0cmF0ZWQgcmVjZW50bHkgdGhhdCB5b3UgY2FuIHByb2R1Y2UgZGF0YSBzZXRzIG9mIGFsbW9zdCBhbnkgYXJiaXRyYXJ5IHNoYXBlIHRoYXQgaGF2ZSBuZWFybHkgaWRlbnRpY2FsIHN0YXRpc3RpY2FsIHByb3BlcnRpZXMuCgohW10oZmlndXJlcy9EaW5vU2VxdWVudGlhbFNtYWxsZXIuZ2lmKQpbU2FtZSBTdGF0cywgRGlmZmVyZW50IEdyYXBoczogR2VuZXJhdGluZyBEYXRhc2V0cyB3aXRoIFZhcmllZCBBcHBlYXJhbmNlIGFuZCBJZGVudGljYWwgU3RhdGlzdGljcyB0aHJvdWdoIFNpbXVsYXRlZCBBbm5lYWxpbmddKGh0dHBzOi8vd3d3LmF1dG9kZXNrcmVzZWFyY2guY29tL3B1YmxpY2F0aW9ucy9zYW1lc3RhdHMpCgojIyBUaGlua2luZyBhYm91dCBQbG90dGluZwoKSXQncyBpbXBvcnRhbnQgdG8gdGhpbmsgb2YgeW91ciBmaWd1cmVzIGFzIGEgKnJlcG9ydCogb2YgeW91ciBkYXRhLiBUcnkgdG8gdGFrZSBhcyBtdWNoIGNhcmUgaW4gcHJvZHVjaW5nIHlvdXIgcGxvdHMgYXMgeW91IGRvIHlvdXIgd3JpdGluZywgb3IgcmVwb3J0aW5nIG9mIHlvdXIgc3RhdGlzdGljcy4gVGhleSBhcmUgYXMgaW1wb3J0YW50IGFzIChvciBmb3Igc29tZSByZWFkZXJzLCBtb3JlIGltcG9ydGFudCB0aGFuKSBhbnl0aGluZyBlbHNlIGluIHlvdXIgcGFwZXIuCgoKIyMgIkFjY3VyYWN5IgoKV2hlbiBtYWtpbmcgYSBwbG90LCB5b3Ugc2hvdWxkIHN0cml2ZSBmb3IgYWNjdXJhY3kgaW46CgotIEFjY3VyYXRlbHkgcmVwcmVzZW50aW5nIHRoZSBwcm9wZXJ0aWVzIG9mIG51bWJlcnMuCi0gQWNjdXJhdGVseSByZXByZXNlbnRpbmcgdGhlIG5hdHVyZSBvZiB5b3VyIGRhdGEuCgpUYWtlIHRoaXMgdmVyeSBzaW1wbGUgZGF0YSBzZXQ6Cgo8ZGl2IHN0eWxlID0gIndpZHRoOjUwJSI+Cgp8IGdyb3VwIHwgdmFsdWUgfAp8IC0tLS06IHwgLS0tLTogfAp8IEEgfCAyIHwKfCBCIHwgNSB8Cgo8L2Rpdj4KCkZvciBvdXIgcHVycG9zZXMsIHRoZXNlIG51bWJlcnMgaGF2ZSB0aHJlZSBwcm9wZXJ0aWVzLgoKMS4gKipPcmRlcioqOiAyIDwgNSwgb3IgQSA8IEIKMi4gKipNYWduaXR1ZGUqKjogNSA9IDIuNSAkXHRpbWVzJCAyLCBvciBCID0gMi41ICRcdGltZXMkIEEKMy4gKipDb250ZXh0dWFsIE1hZ25pdHVkZSoqOiBJZiBBIGFuZCBCIGFyZSBiYXJzLCBhbmQgdGhlc2UgYXJlIG1lYXN1cmUgb2YgdGhlIGNvc3Qgb2YgYSBwaW50LCB0aGVuIEEgbXVzdCBiZSBhIHJlYWwgZGl2ZSAoYW5kIGEgZ29vZCBkZWFsKSwgYW5kIEIgbXVzdCBiZSBhIGxpdHRsZSBiaXQgYmV0dGVyLCBidXQgc3RpbGwgbm90IHRvbyBmYW5jeS4gSWYgQSBhbmQgQiBhcmUgcGVvcGxlLCBhbmQgdGhlc2UgYXJlIHRoZWlyIG51bWJlciBvZiBsZWdzLCB0aGVuIEEgaGFzIGFuIHVuc3VycHJpc2luZyBudW1iZXIgb2YgbGVncyBhbmQgQiBoYXMgYSBzdXJwcmlzaW5nIG51bWJlciBvZiBsZWdzLgoKSGVyZSBpcyBhbiBleGFtcGxlIG9mIGFuIGluYWNjdXJhdGUgcGxvdDoKCmBgYHtyIGZpZy53aWR0aCA9IDUvMiwgZmlnLmhlaWdodCA9IDUvMiwgZWNobyA9IEZ9Cm51bSA8LSBkYXRhLmZyYW1lKGdyb3VwID0gYygiQSIsICJCIiksCiAgICAgICAgICAgICAgICAgIHZhbHVlID0gYygyLCA1KSkKCmdncGxvdChudW0sIGFlcyhncm91cCwgdmFsdWUpKSsKICAgIGdlb21fc2VnbWVudChhZXMoeGVuZCA9IGdyb3VwLCB5PTEuNSwgeWVuZD12YWx1ZSksIHNpemU9MTApKwogICAgdGhlbWVfbWluaW1hbCgpCmBgYAoKSXQgc3VjY2Vzc2Z1bGx5IGNhcHR1cmVzIHRoZSAqb3JkZXIqIG9mIEEgYW5kIEIsIGJ1dCBmYWlscyB0byBjYXB0dXJlIHRoZSBjb3JyZWN0ICptYWduaXR1ZGUqIG9mIHRoZSBkaWZmZXJlbmNlLiBUaGUgbWFnbml0dWRlIG9mIHRoZSBkaWZmZXJlbmNlIGlzIHRocm93biBvZmYgYmVjYXVzZSB0aGUgeS1heGlzIGRvZXNuJ3Qgc3RhcnQgYXQgMC4gSW4gdGhpcyBwbG90LCB0aGUgQiBsaW5lIGlzIGByICg1LTEuNSkvKDItMS41KWAkXHRpbWVzJCBsb25nZXIgdGhhbiB0aGUgQSBsaW5lLCBidXQgdGhlIGFjdHVhbCBtYWduaXR1ZGUgb2YgdGhlIGRpZmZlcmVuY2UgaXMgMi41JFx0aW1lcyQuIFRoaXMgcHJvZHVjZXMgYSAibGllIGZhY3RvciIgb2YgJFxmcmFjezd9ezIuNX0gPSBgciA3LzIuNWAkLgoKVGhpcyBpc24ndCBqdXN0IGEgaHlwb3RoZXRpY2FsIHByb2JsZW0gZWl0aGVyLiBGb3IgZXhhbXBsZSwgQnJpdGlzaCBlbGVjdG9yYWwgbWFpbGVycyBhcmUgbm90b3Jpb3VzIGZvciB0aGUgaW5hY2N1cmF0ZWx5IHBvcnRyYXlpbmcgdGhlIG1hZ25pdHVkZSBvZiBkaWZmZXJlbmNlcy4KCiFbXShmaWd1cmVzL2luYWNjdXJhdGUucG5nKQoKCmBgYHtyIGVjaG8gPSBGfQpzY290IDwtIGRhdGFfZnJhbWUocGFydHkgPSBjKCJDb25zZXJ2YXRpdmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTTlAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMaWIgRGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGFib3VyIiksCiAgICAgICAgICAgICAgICAgICBtcHMgPSBjKDEsIDcsIDEyLCAzOSkpCmdncGxvdChzY290LCBhZXMocGFydHksIG1wcywgZmlsbCA9IHBhcnR5KSkrCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsYWNrIikrCiAgeGxpbShjKCJDb25zZXJ2YXRpdmUiLAogICAgICAgICAiU05QIiwKICAgICAgICAgIkxpYiBEZW0iLAogICAgICAgICAiTGFib3VyIikpKwogIHNjYWxlX2ZpbGxfbWFudWFsKGxpbWl0cz0gYygiQ29uc2VydmF0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNOUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMaWIgRGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxhYm91ciIpLAogICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIiMwMDg3ZGMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiNGRkY5NUQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiNGREJCMzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiNkNTAwMDAiKSkrCiAgICB0aGVtZV9taW5pbWFsKCkKYGBgCgpCb3RoIGFjYWRlbWljIHJlc2VhcmNoZXJzIGFuZCB0aGUgcHJvZHVjZXJzIG9mIHRoZXNlIHBvbGl0aWNhbCBtYWlsZXJzIG1heSBjb3VudGVyIGJ5IHNheWluZwoKPiBCdXQgdGhlIGF4ZXMgd2VyZSBsYWJlbGxlZCBhY2N1cmF0ZWx5IQoKSWYgcmVhZGVycyB3b3VsZCB1bmRlcnN0YW5kIHlvdXIgZGF0YSBiZXR0ZXIgaWYgdGhleSAqaWdub3JlZCB0aGUgZ3JhcGhpY2FsIGVsZW1lbnRzIG9mIHlvdXIgcGxvdCosIHRoZW4geW91ciBwbG90IGlzIG5ldC1uZWdhdGl2ZSB0byBhY2N1cmF0ZSBjb21tdW5pY2F0aW9uLCBhbmQgSSBjYW4gb25seSBhc3N1bWUgdGhhdCBhY2N1cmFjeSB3YXMgbmV2ZXIgeW91ciBwcmltYXJ5IGdvYWwgYW55d2F5LiAKCiMjIFRoaW5rIGFib3V0IGRlZ3JlZSBvZiBhYnN0cmFjdGlvbi4KCkFub3RoZXIgZnJhdWdodCBpc3N1ZSBpbiBhY2FkZW1pYyBwYXBlcnMgaXMgaG93IHRvIGFjY3VyYXRlbHkgY29udmV5IHRoZSB2b2x1bWUgb2YgdGhlaXIgZGF0YS4gRm9yIGV4YW1wbGUsIGluIHRoZSBgaXlfYWhgIGRhdGEgc2V0LCB5b3UgbWlnaHQgYmUgdGVtcHRlZCB0byBwbG90ICphbGwqIG9mIHRoZSBkYXRhLgoKYGBge3J9Cml5X2FoICU+JSAKICBnZ3Bsb3QoYWVzKEYyLCBGMSwgY29sb3IgPSBwbHRfdmNsYXNzKSkrCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC41KSsKICAgIHNjYWxlX3lfcmV2ZXJzZSgpKwogICAgc2NhbGVfeF9yZXZlcnNlKCkrCiAgICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpKwogICAgdGhlbWVfbWluaW1hbCgpCmBgYAoKT3IsIHlvdSBtaWdodCBiZSB0ZW1wdGVkIHRvIHBsb3QgYSBzdW1tYXJ5IG9mIHNvbWUgc29ydC4KCmBgYHtyfQppeV9haCAlPiUKICBncm91cF9ieShwbHRfdmNsYXNzKSAlPiUKICBzdW1tYXJpc2UoRjFfbWVhbiA9IG1lYW4oRjEpLAogICAgICAgICAgICBGMl9tZWFuID0gbWVhbihGMiksCiAgICAgICAgICAgIEYxX2hpID0gRjFfbWVhbiArIDIqKHNkKEYxKSksCiAgICAgICAgICAgIEYxX2xvID0gRjFfbWVhbiAtIDIqKHNkKEYxKSksCiAgICAgICAgICAgIEYyX2hpID0gRjJfbWVhbiArIDIqKHNkKEYyKSksICAgICAgICAgICAgCiAgICAgICAgICAgIEYyX2xvID0gRjJfbWVhbiAtIDIqKHNkKEYyKSkpIC0+IGl5X2FoX3N1bW0KCmdncGxvdChpeV9haF9zdW1tLCBhZXMoRjJfbWVhbiwgRjFfbWVhbiwgY29sb3IgPSBwbHRfdmNsYXNzKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICBnZW9tX3NlZ21lbnQoYWVzKHkgPSBGMV9tZWFuLCAKICAgICAgICAgICAgICAgICAgICAgeWVuZCA9IEYxX21lYW4sIAogICAgICAgICAgICAgICAgICAgICB4ID0gRjJfbG8sIAogICAgICAgICAgICAgICAgICAgICB4ZW5kID0gRjJfaGkpKSsKICAgICAgZ2VvbV9zZWdtZW50KGFlcyh5ID0gRjFfbG8sIAogICAgICAgICAgICAgICAgICAgICB5ZW5kID0gRjFfaGksIAogICAgICAgICAgICAgICAgICAgICB4ID0gRjJfbWVhbiwgCiAgICAgICAgICAgICAgICAgICAgIHhlbmQgPSBGMl9tZWFuKSkrCiAgICBzdGF0X2VsbGlwc2UoZGF0YSA9IGl5X2FoLCBhZXMoeCA9IEYyLCB5ID0gRjEpKSsKICAgIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikrCiAgICBzY2FsZV95X3JldmVyc2UoKSsKICAgIHNjYWxlX3hfcmV2ZXJzZSgpKyAgCiAgICB0aGVtZV9taW5pbWFsKCkKYGBgCgpOZWl0aGVyIG9mIHRoZXNlIGlzIHJlYWxseSBvcHRpbWFsLiBUaGUgZmlyc3Qgb25lICpsb29rcyogbGlrZSB5b3UndmUgZ290IGEgYmlsbGlvbiBkYXRhIHBvaW50cywgYnV0IHJlYWxseSwgbW9zdCBvZiB0aGVzZSBhcmUgcmVwdGl0aW9uIG9mIHRoZSBzYW1lIHZvd2VsIGZyb20gdGhlIHNhbWUgc3BlYWtlcnMuIFRoZSBzZWNvbmQgb25lIGlzIGFuIG9rLWlzaCwgc3VtbWFyeSwgYnV0IGhpZGVzIG1vcmUgdGhhbiBpdCBzaG93cy4KCldoYXQgSSB0ZW5kIHRvIGRvIGlzIGNhbGN1bGF0ZSBtZWFucyBwZXItc3BlYWtlciwgYW5kIHRoZW4gcGxvdCB0aG9zZSBtZWFuczoKCmBgYHtyfQppeV9haCAlPiUgCiAgZ3JvdXBfYnkoaWRzdHJpbmcsIHBsdF92Y2xhc3MpJT4lCiAgc3VtbWFyaXNlKEYxID0gbWVhbihGMSksCiAgICAgICAgICAgIEYyID0gbWVhbihGMiksCiAgICAgICAgICAgIG4gPSBuKCkpJT4lCiAgZ2dwbG90KGFlcyhGMiwgRjEsIGNvbG9yID0gcGx0X3ZjbGFzcykpKwogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSwgYWVzKHNpemUgPSBuKSkrCiAgICBzY2FsZV95X3JldmVyc2UoKSsKICAgIHNjYWxlX3hfcmV2ZXJzZSgpKwogICAgc2NhbGVfc2l6ZV9hcmVhKCkrCiAgICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpKwogICAgdGhlbWVfbWluaW1hbCgpCmBgYAoKVGhpcyBpcyBvZnRlbiBhIG1vcmUgYWNjdXJhdGUgcG9ydHJheWFsIG9mIGhvdyBtdWNoIGRhdGEgeW91IGhhdmUsIHNpbmNlIHRoZXJlIGlzIGEgbG90IG9mIGRhdGEgZnJvbSBhIHNtYWxsZXIgbnVtYmVyIG9mIHNwZWFrZXJzLgoKIyBgZ2dwbG90MmAgYmFzaWMgY29uY2VwdHMKCiMjIExheWVycywgQWVzdGhldGljcywgR2VvbWV0cmllcyBhbmQgU3RhdGlzdGljcwpUaGUgZmlyc3QgdGhpbmcgd2UncmUgZ29pbmcgdG8gZG8gaXMgYnVpbGQgdXAgdG8gY3JlYXRpbmcgdGhpcyBwbG90LCB3aGljaCBpcyBhIHZpc3VhbGl6YXRpb24gb2YgdGhlIGBJX2plYW5gIGRhdGEgZnJhbWUgKHdoaWNoIHlvdSBzaG91bGQgaGF2ZSBsb2FkZWQgaW4gdGhlIHNldHVwIGJsb2NrKS4KCmBgYHtyIGVjaG8gPSBGLCBtZXNzYWdlID0gRn0KICBnZ3Bsb3QoSV9qZWFuLCBhZXMoeD1EdXJfbXNlYywgeT1GMS5uKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICBzdGF0X3Ntb290aCgpKwogICAgc2NhbGVfeF9sb2cxMChicmVha3MgPSBjKDUwLCAxMDAsMjAwLDMwMCw0MDApKSsKICAgIHNjYWxlX3lfcmV2ZXJzZSgpKwogICAgeWxhYigiTm9ybWFsaXplZCBGMSIpKwogICAgeGxhYigiVm93ZWwgZHVyYXRpb24gKG1zZWMpIikrCiAgICB0aGVtZV9idygpKwogICAgZ2d0aXRsZSggIjM5NCB0b2tlbnMgb2YgJ0knIGZyb20gb25lIHNwZWFrZXIiKQpgYGAKCiMjIyBMYXllcnMKCllvdSBzaG91bGQgaG9wZWZ1bGx5IHN0YXJ0IGxvb2tpbmcgYXQgZmlndXJlcyBsaWtlIHRoaXMgb25lIGxpa2UgbWFueSBvZiB1cyBsb29rIGF0IHRoZSBpbWFnZSBiZWxvdy4KCjxkaXYgY2xhc3MgPSAnaGFsZi1pbWcnPgohW10oZmlndXJlcy9FbmxpZ2h0OS5qcGcpCjwvZGl2PgoKVGhvc2Ugb2YgdXNlIGZhbWlsaWFyIHdpdGggdGhpcyBraW5kIG9mIG1lZGlhIGtub3cgdGhhdCB0aGUgcGljdHVyZSBvZiB0aGUgbGliYXJhcnkgaXMgbm90IHdoYXQgd2FzIG9yaWdpbmFsbHkgY2FwdHVyZSBieSBteSBwaG9uZS4gUmF0aGVyIHRoZXJlIGFyZSBtdWx0aXBsZSBsYXllcnMgb2YgZWZmZWN0cywgZmlsdGVycyBhbmQgdGV4dCBvbiB0b3Agb2YgdGhlIGJhc2UgaW1hZ2UsIHdoaWNoIHByb2R1Y2UgdGhlIGZpbmFsIGltYWdlLiBBbmQgaW4gZmFjdCwgc29tZSBvZiB0aGVzZSBsYXllcnMgYXJlIGNydWNpYWxseSBvcmRlcmVkLiBGb3IgZXhhbXBsZSwgdGhlIHRleHQgd291bGQgbG9vayBkaWZmZXJlbnQgaWYgaXQgd2FzIGFkZGVkIHRvIHRoZSBpbWFnZSBmaXJzdCwgYW5kIHRoZW4gdGhlIGZpbHRlcnMsIGluc3RlYWQgb2YgdmljZSB2ZXJzYS4KCgpTbyB0b28gd2l0aCB0aGUgYGdncGxvdDJgIHBsb3QgYWJvdmUuIFRoZXNlIHBsb3RzIGFyZSBjb25zdHJ1Y3RlZCBvdXQgb2YgX19sYXllcnNfXy4gRXZlcnkgY29tcG9uZW50IG9mIHRoZSBncmFwaCwgZnJvbSB0aGUgdW5kZXJseWluZyBkYXRhIGl0J3MgcGxvdHRpbmcsIHRvIHRoZSBjb29yZGluYXRlIHN5c3RlbSBpdCdzIHBsb3R0ZWQgb24sIHRvIHRoZSBzdGF0aXN0aWNhbCBzdW1tYXJpZXMgb3ZlcmxhaWQgb24gdG9wLCB0byB0aGUgYXhpcyBsYWJlbHMsIGFyZSBsYXllcnMgaW4gdGhlIHBsb3QuIFRoZSBjb25zZXF1ZW5jZSBvZiB0aGlzIGlzIHRoYXQgeW91ciB1c2Ugb2YgYGdncGxvdDJgIHdpbGwgcHJvYmFibHkgaW52b2x2ZSBpdGVyYXRpdmUgYWRkaXRpb24gb2YgbGF5ZXIgdXBvbiBsYXllciB1bnRpbCB5b3UncmUgcGxlYXNlZCB3aXRoIHRoZSByZXN1bHRzLgoKCiMjIyBBZXN0aGV0aWNzCgpUaGUgZ3JhcGhpY2FsIHByb3BlcnRpZXMgd2hpY2ggZW5jb2RlIHRoZSBkYXRhIHlvdSdyZSBwcmVzZW50aW5nIGFyZSB0aGUgX19hZXN0aGV0aWNzX18gb2YgdGhlIHBsb3QuIFRoZXNlIGluY2x1ZGUgdGhpbmdzIGxpa2UKCi0geCBwb3NpdGlvbgotIHkgcG9zaXRpb24KLSBzaXplIG9mIGVsZW1lbnRzCi0gc2hhcGUgb2YgZWxlbWVudHMKLSBjb2xvciBvZiBlbGVtZW50cwoKCiMjIyBHZW9tZXRyaWVzCgpUaGUgcHJpbWFyeSB2aXN1YWwgaXRlbXMgb24gdGhlIHBsb3RzIGFyZSBjYWxsZWQgX19nZW9tZXRyaWVzX18gYW5kIGluY2x1ZGUgdGhpbmdzIGxpa2UKCiogcG9pbnRzCiogbGluZXMKKiBsaW5lIHNlZ21lbnRzCiogYmFycwoqIHRleHQKClNvbWUgb2YgdGhlc2UgZ2VvbWV0cmllcyBoYXZlIHRoZWlyIG93biBzcGVjaWZpYyBhZXN0aGV0aWMgc2V0dGluZ3MuIEZvciBleGFtcGxlLAoKKiBwb2ludHMKICAgICogcG9pbnQgc2hhcGUKKiB0ZXh0CiAgICAqIHRleHQgbGFiZWxzCiogbGluZXMKICAgICogbGluZSB3ZWlnaHQKICAgICogbGluZSB0eXBlCiAgCiMjIyBTdGF0aXN0aWNzCgpZb3UnbGwgYWxzbyBmcmVxdWVudGx5IHdhbnQgdG8gcGxvdCBfX3N0YXRpc3RpY3NfXyBvdmVybGFpZCBvbiB0b3Agb2YsIG9yIGluc3RlYWQgb2YgdGhlIHJhdyBkYXRhLiBTb21lIG9mIHRoZXNlIGluY2x1ZGUKCiogU21vb3RoaW5nIGFuZCByZWdyZXNzaW9uIGxpbmVzCiogT25lIGFuZCB0d28gZGltZW5zaW9uYWwgYmlubmluZwoqIE1lYW4gYW5kIG1lZGlhbnMgd2l0aCBjb25maWRlbmNlIGludGVydmFscy4KCgotLS0tCgpUaGUgX19hZXN0aGV0aWNzX18sIF9fZ2VvbWV0cmllc19fIGFuZCBfX3N0YXRpc3RpY3NfXyBjb25zdGl0dXRlIHRoZSBtb3N0IGltcG9ydGFudCBfX2xheWVyc19fIG9mIGEgcGxvdCwgYnV0IGZvciBmaW5lIHR1bmluZyBhIHBsb3QgZm9yIHB1YmxpY2F0aW9uLCB0aGVyZSBhcmUgYSBudW1iZXIgb2Ygb3RoZXIgdGhpbmdzIHlvdSdsbCB3YW50IHRvIGFkanVzdC4gVGhlIG1vc3QgY29tbW9uIG9uZSBvZiB0aGVzZSBhcmUgdGhlIF9fc2NhbGVzX18sIHdoaWNoIGVuY29tcGFzcyB0aGluZ3MgbGlrZQoKKiBBIGxvZ2FyaXRobWljIHggb3IgeSBheGlzCiogQ3VzdG9taXplZCBjb2xvciBzY2FsZXMKKiBDdXN0b21pemVkIHBvaW50IHNoYXBlcywgb3IgbGluZXR5cGVzCgpXZSdsbCByZXZpZXcgbWFueSBvZiB0aGVzZSBjb21wb25lbnRzIGFzIHdlIGJ1aWxkIHVwIHRoZSBwbG90LCBhbmQgd2lsbCBjaXJjbGUgYmFjayB0byBtb3JlIG9mIHRoZW0gZm9yIGdyZWF0ZXIgZGV0YWlsLgoKCgojIEJ1aWxkaW5nIHRoZSBQbG90CgpGaXJzdCwgbGV0J3MgcmVmcmVzaCBvdXIgbWVtb3JpZXMgb2YgdGhlIGdyYXBoIHdlIHdhbnQgdG8gYnVpbGQuCgpgYGB7ciBlY2hvID0gRiwgbWVzc2FnZSA9IEZ9CiAgZ2dwbG90KElfamVhbiwgYWVzKHg9RHVyX21zZWMsIHk9RjEubikpKwogICAgZ2VvbV9wb2ludCgpKwogICAgc3RhdF9zbW9vdGgoKSsKICAgIHNjYWxlX3hfbG9nMTAoYnJlYWtzID0gYyg1MCwgMTAwLDIwMCwzMDAsNDAwKSkrCiAgICBzY2FsZV95X3JldmVyc2UoKSsKICAgIHlsYWIoIk5vcm1hbGl6ZWQgRjEiKSsKICAgIHhsYWIoIlZvd2VsIGR1cmF0aW9uIChtc2VjKSIpKwogICAgdGhlbWVfYncoKSsKICAgIGdndGl0bGUoICIzOTQgdG9rZW5zIG9mICdJJyBmcm9tIG9uZSBzcGVha2VyIikKYGBgCgpUaGlzIHBsb3QgaXMgY29tcG9zZWQgb2YgbmluZSBsYXllcnMsIHdoaWNoIGNhbiBiZSBzdWJkaXZpZGVkIGludG8gZml2ZSBsYXllciB0eXBlcy4gSXQncyBub3QgaW1wb3J0YW50IGZvciB5b3UgdG8gbWVtb3JpemUgdGhlc2UgbGF5ZXIgdHlwZXMsIGJ1dCBpdCBoZWxwcyB0byBzdHJ1Y3R1cmUgdGhlIGRpc2N1c3Npb24uCgojIyBMYXllcnMKCiMjIyBUaGUgZGF0YSBsYXllcgoKRXZlcnkgYGdncGxvdDJgIHBsb3QgaGFzIGEgZGF0YSBsYXllciwgd2hpY2ggZGVmaW5lcyB0aGUgZGF0YSBzZXQgdG8gcGxvdCwgYW5kIHRoZSBiYXNpYyBtYXBwaW5ncyBvZiBkYXRhIHRvIGFlc3RoZXRpYyBlbGVtZW50cy4gVGhlIGRhdGEgbGF5ZXIgY3JlYXRlZCB3aXRoIHRoZSBmdW5jdGlvbnMgYGdncGxvdCgpYCBhbmQgYGFlcygpYCwgYW5kIGxvb2tzIGxpa2UgdGhpcwoKYGBge3IgZXZhbD1GfQpnZ3Bsb3QoZGF0YSwgYWVzKC4uLikpCmBgYAoKVGhlIGZpcnN0IGFyZ3VtZW50IHRvIGBnZ3Bsb3QoKWAgaXMgYSBkYXRhIGZyYW1lIChpdCBfbXVzdF8gYmUgYSBkYXRhIGZyYW1lKSwgYW5kIGl0cyBzZWNvbmQgYXJndW1lbnQgaXMgYGFlcygpYC4gWW91J3JlIG5ldmVyIGdvaW5nIHRvIHVzZSBgYWVzKClgIGluIGFueSBvdGhlciBjb250ZXh0IGV4Y2VwdCBmb3IgaW5zaWRlIG9mIG90aGVyIGBnZ3Bsb3QyYCBmdW5jdGlvbnMsIHNvIGl0IG1pZ2h0IGJlIGJlc3Qgbm90IHRvIHRoaW5rIG9mIGBhZXMoKWAgYXMgaXRzIG93biBmdW5jdGlvbiwgYnV0IHJhdGhlciBhcyBhIHNwZWNpYWwgd2F5IG9mIGRlZmluaW5nIGRhdGEtdG8tYWVzdGhldGljIG1hcHBpbmdzLgoKRm9yIHRoZSBwbG90IGZyb20gYWJvdmUsIHdlJ2xsIGJlIHVzaW5nIGRhdGEgZnJvbSB0aGUgYElfamVhbmAgZGF0YSBmcmFtZSwgd2hpY2ggbG9va3MgbGlrZSB0aGlzOgpgYGB7cn0KaGVhZChJX2plYW4pCmBgYAoKCkkndmUgZGVjaWRlZCB0aGF0IGFuIGludGVyZXN0aW5nIHJlbGF0aW9uc2hpcCBpbiB0aGlzIGRhdGEgaXMgYmV0d2VlbiB0aGUgdm93ZWwgZHVyYXRpb24gKGBEdXJfbXNlY2ApIGFuZCB0aGUgbm9ybWFsaXplZCBGMSBvZiB0aGUgdm93ZWwgKGBGMS5uYCkuIFNwZWNpZmljYWxseSwgSSdkIGxpa2UgdG8gbWFwIGBEdXJfbXNlY2AgdG8gdGhlIHgtYXhpcywgYW5kIGBGMS5uYCB0byB0aGUgeS1heGlzLiBIZXJlJ3MgdGhlIGBnZ3Bsb3QyYCBjb2RlLgoKYGBge3IgZGF0YV9sYXllcn0KICBwIDwtIGdncGxvdChJX2plYW4sIGFlcyh4PUR1cl9tc2VjLCB5PUYxLm4pKQogIHAKYGBgCgpZb3UgY2FuIHRoaW5rIG9mIHRoaXMgcGxvdCBhcyB0aGUgYmFzZSBpbWFnZSwgYmVmb3JlIHdlJ3ZlIGFkZGVkIGFueSBleHRyYSBsYXllcnMsIHRleHQgb3IgaW5zdGFncmFtIGZpbHRlcnMgdG8gaXQuIEFuIGltcG9ydGFudCBjb25jZXB0dWFsIGlzc3VlIGlzIHRoYXQgeW91IGFyZSBhYmxlIHRvIGFzc2lnbiBwbG90cyB0byB2YXJpYWJsZXMgKGluIHRoaXMgY2FzZSwgYHBgKS4gV2hlbiB5b3UgZG8gdGhpcyBhc3NpZ25tZW50LCBub3RoaW5nIHNwZWNpYWwgaGFwcGVucy4gQnV0IGlmIHlvdSBwcmludCBvdXQgYHBgLCBSIHdpbGwgZ2VuZXJhdGUgdGhlIHBsb3QuIAoKCiMjIyBUaGUgZ2VvbWV0cmllcyBsYXllcgoKVGhlIG5leHQgc3RlcCwgYWZ0ZXIgZGVmaW5pbmcgdGhlIGJhc2ljIGRhdGEtdG8tYWVzdGhldGljIG1hcHBpbmdzLCBpcyB0byBhZGQgZ2VvbWV0cmllcyB0byB0aGUgZGF0YS4gV2UnbGwgZGlzY3VzcyBnZW9tZXRyaWVzIGluIG1vcmUgZGV0YWlsIGJlbG93LCBidXQgZm9yIG5vdywgd2UnbGwgYWRkIG9uZSBvZiB0aGUgc2ltcGxlc3Q6IHBvaW50cy4KCmBgYHtyIGZpZy5wb3MgPSAiY2VudGVyIixmaWcud2lkdGggPSA4LzEuNSwgZmlnLmhlaWdodD01LzEuNX0KICBwIDwtIHAgKyBnZW9tX3BvaW50KCkKICBwCmBgYAoKVGhlcmUgYXJlIGEgZmV3IHRoaW5ncyB0byB0YWtlIGF3YXkgZnJvbSB0aGlzIHN0ZXAuIEZpcnN0IGFuZCBmb3JlbW9zdCwgdGhlIHdheSB5b3UgYWRkIG5ldyBsYXllcnMsIG9mIGFueSBraW5kLCB0byBhIHBsb3QgaXMgd2l0aCB0aGUgYCtgIG9wZXJhdG9yLiBBbmQsIGFzIHdlJ2xsIHNlZSBpbiBhIG1vbWVudCwgdGhlcmUncyBubyBuZWVkIHRvIG9ubHkgYWRkIHRoZW0gb25lIGF0IGEgdGltZS4gWW91IGNhbiBzdHJpbmcgdG9nZXRoZXIgYW55IG51bWJlciBvZiBsYXllcnMgdG8gYWRkIHRvIGEgcGxvdCwgc2VwYXJhdGVkIGJ5IGArYC4KClRoZSBuZXh0IHRoaW5nIHRvIG5vdGljZSBpcyB0aGF0IGFsbCBsYXllcnMgeW91IGFkZCB0byBhIHBsb3QgYXJlLCB0ZWNobmljYWxseSwgZnVuY3Rpb25zLiBXZSBkaWRuJ3QgcGFzcyBhbnkgYXJndW1lbnRzIHRvIGBnZW9tX3BvaW50KClgLCBzbyB0aGUgcmVzdWx0aW5nIHBsb3QgcmVwcmVzZW50cyB0aGUgZGVmYXVsdCBiZWhhdmlvcjogc29saWQgYmxhY2sgY2lyY3VsYXIgcG9pbnRzLgoKSWYgZm9yIG5vIGdvb2QgcmVhc29uIGF0IGFsbCB3ZSB3YW50ZWQgdG8gdXNlIGEgZGlmZmVyZW50IHBvaW50IHNoYXBlIGluIHRoZSBwbG90LCB3ZSBjb3VsZCBzcGVjaWZ5IGl0IGluc2lkZSBvZiBgZ2VvbV9wb2ludCgpYC4KCmBgYHtyIGZpZy5wb3MgPSAiY2VudGVyIixmaWcud2lkdGggPSA4LzEuNSwgZmlnLmhlaWdodD01LzEuNSwgdGlkeSA9IEZ9CmdncGxvdChJX2plYW4sIGFlcyh4PUR1cl9tc2VjLCB5PUYxLm4pKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDMpCmBgYAoKT3IsIGlmIHdlIHdhbnRlZCB0byB1c2UgbGFyZ2VyLCByZWQgcG9pbnRzLCB3ZSBjb3VsZCBzcGVjaWZ5IHRoYXQgaW4gYGdlb21fcG9pbnQoKWAgYXMgd2VsbC4KYGBge3IgZmlnLnBvcyA9ICJjZW50ZXIiLGZpZy53aWR0aCA9IDgvMS41LCBmaWcuaGVpZ2h0PTUvMS41LCB0aWR5ID0gRn0KZ2dwbG90KElfamVhbiwgYWVzKHg9RHVyX21zZWMsIHk9RjEubikpICsKICBnZW9tX3BvaW50KGNvbG9yID0gInJlZCIsIHNpemUgPSAzKQpgYGAKClNwZWFraW5nIG9mIGRlZmF1bHRzLCB3ZSBjYW4gc2VlIGEgZmV3IG9mIHRoZSBkZWZhdWx0IHNldHRpbmcgb2YgYGdncGxvdDJgIG9uIGRpc3BsYXkgaGVyZS4gTW9zdCBzdHJpa2luZyBpcyB0aGUgbGlnaHQgZ3JleSBiYWNrZ3JvdW5kLCB3aXRoIHdoaXRlIGdyaWQgbGluZXMuIE9waW5pb24gdmFyaWVzIG9uIHdoZXRoZXIgb3Igbm90IHRoaXMgaXMgYWVzdGhldGljYWxseSBvciB0ZWNobmljYWxseSBwbGVhc2luZywgYnV0IGRvbid0IHdvcnJ5LCBpdCdzIGFkanVzdGFibGUuIAoKQW5vdGhlciBkZWZhdWx0IGlzIHRvIGxhYmVsIHRoZSB4IGFuZCB5IGF4ZXMgd2l0aCB0aGUgY29sdW1uIG5hbWVzIGZyb20gdGhlIGRhdGEgZnJhbWUuIEknbGwgaW5qZWN0IGEgYml0IG9mIGJlc3QgcHJhY3RpY2UgYWR2aWNlIGhlcmUsIGFuZCB0ZWxsIHlvdSB0byBfYWx3YXlzXyBjaGFuZ2UgdGhlIGF4aXMgbmFtZXMuIEl0J3MgbmVhcmx5IGd1YXJhbnRlZWQgdGhhdCB5b3VyIGRhdGEgZnJhbWUgY29sdW1uIG5hbWVzIHdpbGwgbWFrZSBmb3IgdmVyeSBwb29yIGF4aXMgbGFiZWxzLiBXZSdsbCBjb3ZlciBob3cgdG8gZG8gdGhhdCBzaG9ydGx5LgoKRmluYWxseSwgbm90ZSB0aGF0IHdlIGRpZG4ndCBuZWVkIHRvIHRlbGwgYGdlb21fcG9pbnQoKWAgYWJvdXQgdGhlIHggYW5kIHkgYXhlcy4gVGhpcyBtYXkgc2VlbSB0cml2aWFsLCBidXQgaXQncyBhIHJlYWxseSBpbXBvcnRhbnQsIGFuZCBwb3dlcmZ1bCBhc3BlY3Qgb2YgYGdncGxvdDJgLiBXaGVuIHlvdSBhZGQgYW55IGxheWVyIGF0IGFsbCB0byBhIHBsb3QsIGl0IHdpbGwgX19pbmhlcml0X18gdGhlIGRhdGEtdG8tYWVzdGhldGljIG1hcHBpbmdzIHdoaWNoIHdlcmUgZGVmaW5lZCBpbiB0aGUgZGF0YSBsYXllci4gV2UnbGwgZGlzY3VzcyBpbmhlcml0YW5jZSwgYW5kIGhvdyB0byBvdmVycmlkZSwgb3IgZGVmaW5lIG5ldyBkYXRhLXRvLWFlc3RoZXRpYyBtYXBwaW5ncyB3aXRoaW4gYW55IGdlb20uCgoKIyMjIFRoZSBzdGF0aXN0aWNzIGxheWVyCgpUaGUgZmluYWwgZmlndXJlIGFsc28gaW5jbHVkZXMgYSBzbW9vdGhpbmcgbGluZSwgd2hpY2ggaXMgb25lIG9mIG1hbnkgcG9zc2libGUgc3RhdGlzdGljYWwgbGF5ZXJzIHdlIGNhbiBhZGQgdG8gYSBwbG90LgpgYGB7ciBmaWcud2lkdGggPSA4LzEuNSwgZmlnLmhlaWdodD01LzEuNX0KICBwIDwtIHAgKyBzdGF0X3Ntb290aCgpCiAgcApgYGAKCldlJ2xsIGdvIG92ZXIgdGhlIGRlZmF1bHQgYmVoYXZpb3Igb2YgYHN0YXRfc21vb3RoKClgIGJlbG93LCBidXQgaW4gdGhpcyBwbG90LCB0aGUgc21vb3RoaW5nIGxpbmUgcmVwcmVzZW50cyBhIGxvZXNzIHNtb290aCwgYW5kIHRoZSBzZW1pLXRyYW5zcGFyZW50IHJpYmJvbiBzdXJyb3VuZGluZyB0aGUgc29saWQgbGluZSBpcyB0aGUgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwuCgpPbmUgaW1wb3J0YW50IHRoaW5nIHRvIHJlYWxpemUgaXMgdGhhdCBpdCdzIG5vdCBuZWNlc3NhcnkgdG8gaW5jbHVkZSB0aGUgcG9pbnRzIGluIG9yZGVyIHRvIGFkZCBhIHNtb290aGluZyBsaW5lLiBIZXJlJ3Mgd2hhdCB0aGUgcGxvdCB3b3VsZCBsb29rIGxpa2Ugd2l0aCB0aGUgcG9pbnRzIG9taXR0ZWQuCgpgYGB7ciBmaWcud2lkdGggPSA4LzEuNSwgZmlnLmhlaWdodD01LzEuNSwgdGlkeSA9Rn0KZ2dwbG90KElfamVhbiwgYWVzKHggPSBEdXJfbXNlYywgeSA9IEYxLm4pKSsKICBzdGF0X3Ntb290aCgpCmBgYAoKTm90aWNlIGhvdyB0aGUgeS1heGlzIGhhcyB6b29tZWQgaW4gdG8ganVzdCBpbmNsdWRlIHRoZSByYW5nZSBvZiB0aGUgc21vb3RoaW5nIGxpbmUgYW5kIHN0YW5kYXJkIGVycm9yLgoKIyMjIFNjYWxlIHRyYW5zZm9ybWF0aW9ucwpJIGFsc28gd2FudGVkIHRvIG1ha2Ugc29tZSBhbHRlcmF0aW9ucyB0byB0aGUgZGVmYXVsdCB4IGFuZCB5IGF4aXMgc2NhbGVzLiBGb3IgZXhhbXBsZSwgdGhlIHktYXhpcyBpcyBjdXJyZW50bHkgcnVubmluZyBpbiByZXZlcnNlIHRvIHRoZSBpbnR1aXRpdmUgZGlyZWN0aW9uIG9mIEYxLiBfSGlnaGVyXyB2b3dlbHMgaGF2ZSBfbG93ZXJfIEYxIHZhbHVlcywgc28gd2Ugd2FudCB0byBmbGlwIHRoZSB5LWF4aXMuIEFkZGl0aW9uYWxseSwgZHVyYXRpb25zIGFyZSB0eXBpY2FsbHkgYmVzdCBkaXNwbGF5ZWQgYWxvbmcgYSBsb2dhcml0aG1pYyBzY2FsZSwgc28gd2Ugc2hvdWxkIGNvbnZlcnQgdGhlIHgtYXhpcyBhcyB3ZWxsLgoKCmBgYHtyIHRpZHkgPSBGLGZpZy53aWR0aCA9IDgvMS41LCBmaWcuaGVpZ2h0PTUvMS41fQpwIDwtIHAgKyBzY2FsZV94X2xvZzEwKGJyZWFrcyA9IGMoNTAsIDEwMCwgMjAwLCAzMDAsIDQwMCkpKwogICAgICAgICBzY2FsZV95X3JldmVyc2UoKQpwCmBgYAoKSXQncyB3b3J0aCBub3RpbmcgdGhhdCB0aGUgc21vb3RoaW5nIGxpbmUgaGVyZSBpcyBjYWxjdWxhdGVkIG92ZXIgdGhlIF90cmFuc2Zvcm1lZF8gZGF0YS4KCgojIyMgQ29zbWV0aWMgYWx0ZXJhdGlvbnMKRmluYWxseSwgSSB3YW50ZWQgdG8gbWFrZSBzb21lIGNvc21ldGljIGFkanVzdG1lbnRzIHRvIHRoZSBwbG90LiBGb3IgZXhhbXBsZSwgdGhlIHgtYXhpcyBsYWJlbCAiRHVyX21zZWMiIGlzIG5vdCBxdWl0ZSBhcyB1c2VmdWwgYXMgIlZvd2VsIGR1cmF0aW9uIChtc2VjKSIgd291bGQgYmUuIEkgYWxzbyBhZGRlZCBhIHRpdGxlIHRvIHRoZSBwbG90LCBhbmQgY2hhbmdlZCB0aGUgY29sb3IgdGhlbWUgdG8gYmxhY2sgYW5kIHdoaXRlLgoKYGBge3IgdGlkeSA9IEYsZmlnLndpZHRoID0gOC8xLjUsIGZpZy5oZWlnaHQ9NS8xLjV9CnAgPC0gcCArIHlsYWIoIk5vcm1hbGl6ZWQgRjEiKSsKICAgICAgICAgeGxhYigiVm93ZWwgZHVyYXRpb24gKG1zZWMpIikrCiAgICAgICAgIHRoZW1lX2J3KCkrCiAgICAgICAgIGdndGl0bGUoIjM5NCB0b2tlbnMgb2YgJ0knIGZyb20gb25lIHNwZWFrZXIiKQpwCmBgYAoKX19fCgpIZXJlJ3MgYWxsIHRoZSBsYXllcnMsIHB1dCB0b2dldGhlciBhbGwgYXQgdGhlIHNhbWUgdGltZS4KCmBgYHtyIG1lc3NhZ2UgPSBGLCB0aWR5ID0gRn0KZ2dwbG90KElfamVhbiwgYWVzKHg9RHVyX21zZWMsIHk9RjEubikpKwogIGdlb21fcG9pbnQoKSsKICBzdGF0X3Ntb290aCgpKwogIHNjYWxlX3hfbG9nMTAoYnJlYWtzID0gYyg1MCwgMTAwLCAyMDAsIDMwMCwgNDAwKSkrCiAgc2NhbGVfeV9yZXZlcnNlKCkrCiAgeWxhYigiTm9ybWFsaXplZCBGMSIpKwogIHhsYWIoIlZvd2VsIGR1cmF0aW9uIChtc2VjKSIpKwogIHRoZW1lX2J3KCkrCiAgZ2d0aXRsZSgiMzk0IHRva2VucyBvZiAnSScgZnJvbSBvbmUgc3BlYWtlciIpCmBgYAoKPGRpdiBjbGFzcyA9ICJib3ggaWRpb20iPgo8c3BhbiBjbGFzcyA9ICdsYWJlbCc+SWRpb208L3NwYW4+CgpBcyB3aXRoIHBpcGVzIGAlPiVgLCBJIHJlY29tbWVuZCBwdXR0aW5nIGEgbmV3IGxpbmUgYWZ0ZXIgZXZlcnkgYCtgIGluIGEgY2hhaW4gb2YgYGdncGxvdDJgIGxheWVycywgYW5kIGluZGVudGluZyB0aGUgZm9sbG93aW5nIGxpbmUgdHdvIHNwYWNlcy4KCjwvZGl2PgoKIyBFbGFib3JhdGluZyAmIE1vcmUgT3B0aW9ucwoKRm9yIGJldHRlciBvciB3b3JzZSwgYGdncGxvdDJgIHBsb3RzIGFyZSBlYXNpbGx5IGlkZW50aWZpYWJsZSwgYW5kIHlvdSdsbCBpbmV2aXRhYmxseSBzdGFydCBzZWVpbmcgdGhlbSBldmVyeXdoZXJlIG5vdy4gU29tZXRpbWVzIHlvdSdsbCBzZWUgYSBwbG90IHRoYXQgZG9lcyBzb21ldGhpbmcgY29vbCB0aGF0IHlvdSBkaWRuJ3QgcmVhbGl6ZSB5b3UgY291bGQgZG8gaW4gYGdncGxvdDJgLiBXaGVuIHRoYXQgaGFwcGVucyB0byBtZSwgSSBnb29nbGUgImhvdyBkbyB5b3UgWCBpbiBnZ3Bsb3QyIiwgYW5kIGl0IHVzdWFsbHkgd29ya3MuCgojIyBBZXN0aGV0aWNzCgpJbiBgZ2dwbG90MmAsIF9fYWVzdGhldGljc19fIGFyZSB0aGUgZ3JhcGhpY2FsIGVsZW1lbnRzIHdoaWNoIGFyZSBtYXBwZWQgdG8gZGF0YSwgYW5kIHRoZXkgYXJlIGRlZmluZWQgd2l0aCBgYWVzKClgLiBUbyBzb21lIGV4dGVudCwgdGhlIGFlc3RoZXRpY3MgeW91IG5lZWQgdG8gZGVmaW5lIGFyZSBkZXBlbmRlbnQgb24gdGhlIGdlb21ldHJpZXMgeW91IHdhbnQgdG8gdXNlLCBiZWNhdXNlIGxpbmUgc2VnbWVudHMgaGF2ZSBkaWZmZXJlbnQgZ2VvbWV0cmljIHByb3BlcnRpZXMgdGhhbiBwb2ludHMsIGZvciBleGFtcGxlLiBIb3dldmVyLCB0aGVyZSBpcyBhbHNvIGEgZ3JlYXQgZGVhbCBvZiB1bmlmb3JtaXR5IGluIHRoZSBhZXN0aGV0aWNzIHVzZWQgYWNyb3NzIGdlb21ldHJpZXMuIEhlcmUgaXMgYSBsaXN0IG9mIHRoZSBtb3N0IGNvbW1vbiBhZXN0aGV0aWNzIHlvdSdsbCB3YW50IHRvIGRlZmluZS4KCiogYHhgCiAgICAqIHgtYXhpcyBsb2NhdGlvbgoqIGB5YAogICAgKiB5LWF4aXMgbG9jYXRpb24KKiBgY29sb3JgCiAgICAqIFRoZSBjb2xvciBvZiBsaW5lcywgcG9pbnRzLCBhbmQgdGhlIG91dHNpZGUgYm9yZGVycyBvZiB0d28gZGltZW5zaW9uYWwgZ2VvbWV0cmllcyAocG9seWdvbnMsCiAgICAgICBiYXJzLCBldGMuKS4gSGFkbGV5IFdpY2toYW0sIHRoZSBwcmltYXJ5IGBnZ3Bsb3QyYCBkZXZlbG9wZXIsIGlzIGZyb20gTmV3IFplYWxhbmQsIHNvCiAgICAgICBgY29sb3VyYCBpcyBhbHNvIHN1cHBvcnRlZCEKKiBgZmlsbGAKICAgICogVGhlIGZpbGwgY29sb3Igb2YgdHdvIGRpbWVuc2lvbmFsIGdlb21ldHJpZXMuCiogYHNpemVgCiAgICAqIFRoZSBzaXplIG9mIHBvaW50cywgb3IgdGhlIHdlaWdodCBvZiBsaW5lcyBhbmQgYm9yZGVycyBvZiB0d28gZGltZW5zaW9uYWwgZ2VvbWV0cmllcy4KKiBgc2hhcGVgCiAgICAqIFRoaXMgaXMgc3BlY2lmaWMgdG8gcG9pbnRzLCBhbmQgZGVmaW5lcyB0aGUgcG9pbnQgc2hhcGUuIFRoaXMgaXMgb25lIG9mIHRoZSBmZXcgYWVzdGhldGljcwogICAgICB0byB3aGljaCB5b3UgY2FuJ3QgbWFwIGEgY29udGludW91cyB2YXJpYWJsZS4KKiBgbGluZXR5cGVgCiAgICAqIFRoaXMgZGVmaW5lcyB0aGUgbGluZSB0eXBlIG9mIGFueSBraW5kIG9mIGxpbmUsIHBhdGgsIG9yIGJvcmRlciBvZiBhIHR3byBkaW1lbnNpb25hbCBnZW9tZXRyeS4KICAgICAgIFRoaXMgaXMgYW5vdGhlciBhZXN0aGV0aWMgd2hpY2ggY2Fubm90IGJlIG1hcHBlZCB0byBhIGNvbnRpbnVvdXMgdmFyaWFibGUuCiogYGFscGhhYAogICAgKiBUaGlzIGRlZmluZXMgdGhlIG9wYWNpdHkgb2YgYW55IGdlb21ldHJpYyBwcm9wZXJ0eS4gSXQncyBsZXNzIGNvbW1vbmx5IG1hcHBlZCB0byBkYXRhLCBhbmQgbW9yZQogICAgICBvZnRlbiBoYXJkIGNvZGVkIHRvIGEgc2luZ2xlIHZhbHVlIGFzIGEgc29sdXRpb24gZm9yIG92ZXJwbG90dGluZy4KKiBgeGVuZGAsIGB5ZW5kYAogICAgKiBZb3UnbGwgdXNlIHRoZXNlIG1vcmUgcmFyZWx5LCB1c3VhbGx5IHdoZW4gcGxvdHRpbmcgYSBsaW5lIHNlZ21lbnQsIG9yIGFycm93LiBUaGUgYmVnaW5uaW5nCiAgICAgIG9mIHRoZSBsaW5lIHNlZ21lbnQgd2lsbCBiZSBsb2NhdGVkIGF0IGB4YCwgYHlgLCBhbmQgdGhlIGVuZCBvZiB0aGUgbGluZSBzZWdtZW50IHdpbGwgYmUgCiAgICAgIGxvY2F0ZWQgYXQgYHhlbmRgLGB5ZW5kYC4gCiogYHltaW5gLCBgeW1heGAsIChgeG1pbmAsIGB4bWF4YCkKICAgICogYHltaW5gIGFuZCBgeW1heGAgYXJlIHJlc2VydmVkIGZvciBnZW9tZXRyaWVzIHdoaWNoIGFyZSBkZXZvdGVkIHRvIHJlcHJlc2VudGluZyAKICAgICAgX3Jhbmdlc18gb2YgZGF0YSwgbGlrZSBlcnJvciBiYXJzLCBhbmQgcmliYm9ucy4gRm9yIHRoZSBtb3N0IHBhcnQsIHRoZXNlIHdpbGwgYmUgZXhwcmVzc2VkCiAgICAgIGFsb25nIHRoZSB5LWF4aXMsIGJ1dCBgeG1pbmAgYW5kIGB4bWF4YCBhcmUgdXRpbGl6ZWQgZm9yIHNvbWUgZ2VvbWV0cmllcyBhcyB3ZWxsLgoKVGhlIG1vc3QgaW1wb3J0YW50IHRoaW5nIHRvIGtlZXAgaW4gbWluZCBhYm91dCBhZXN0aGV0aWNzIGlzIG5vdCB3aGF0IHRoZXkncmUgY2FsbGVkLCB0aG91Z2gsIGJ1dCBob3cgdGhleSBhcmUgaW5oZXJpdGVkIGJ5IHRoZSBsYXllcnMuIExldCdzIHN0YXJ0IGJ5IG1hcHBpbmcgdGhlIGBXb3JkYCB0byBgY29sb3JgLiBgciBwcmludChyb3VuZChtYXgodGFibGUoSV9qZWFuJFdvcmQpKS9ucm93KElfamVhbikqMTAwKSlgJSBvZiB0aGUgdG9rZW5zIGFyZSBqdXN0ICJJIiwgc28gbGV0cyBjcmVhdGUgYSBzdWJzZXQgb2YgdGhlIGRhdGEgdGhhdCBleGNsdWRlcyAiSSIgc28gaXQgZG9lc24ndCB2aXN1YWxseSBzd2FtcCB0aGUgcGxvdC4KCiMjIyBNYXBwaW5nIERhdGEgdG8gQWVzdGhldGljcwoKYGBge3J9Cklfc3Vic2V0IDwtIHN1YnNldChJX2plYW4sIFdvcmQgIT0gIkkiKQpgYGAKCmBgYHtyIHRpZHkgPSBGLCBmaWcud2lkdGggPSA4LzEuMiwgZmlnLmhlaWdodD01LzEuMn0KZ2dwbG90KElfc3Vic2V0LCBhZXMoRHVyX21zZWMsIEYxLm4sIGNvbG9yID0gV29yZCkpKwogIGdlb21fcG9pbnQoKQpgYGAKCkVhY2ggcG9pbnQgaXMgbm93IGNvbG9yZWQgYWNjb3JkaW5nIHRvIHRoZSB3b3JkIGl0IGNvcnJlc3BvbmRzIHRvLiBgZ2dwbG90MmAgaGFzIGF1dG9tYXRpY2FsbHkgZ2VuZXJhdGVkIGEgY29sb3IgcGFsZXR0ZSBvZiB0aGUgcmlnaHQgdHlwZSBhbmQgc2l6ZSwgYmFzZWQgb24gdGhlIGRhdGEgbWFwcGVkIHRvIGBjb2xvcmAsIGFuZCBjcmVhdGVkIGEgbGVnZW5kIHRvIHRoZSBzaWRlLiBBcyB3aXRoIGV2ZXJ5dGhpbmcsIHRoZSBzcGVjaWZpYyBjb2xvciBwYWxldHRlIHdlIHVzZSBpcyBhZGp1c3RhYmxlLCB3aGljaCB3aWxsIGJlIGRpc2N1c3NlZCBpbiBtb3JlIGRldGFpbCBiZWxvdyB1bmRlciBTY2FsZXMuIFRoZXJlIGFyZSBwb3NpdGl2ZSBmZWF0dXJlcyBvZiB0aGUgZGVmYXVsdCBjb2xvciBwYWxldHRlLCBidXQgc29tZSBwZW9wbGUgZG9uJ3QgbGlrZSBpdCBhZXN0aGV0aWNhbGx5LCBhbmQgdGhleSBjYW4gYmUgaGFyZCBmb3Igc29tZSBjb2xvcmJsaW5kIHJlYWRlcnMuIFRoZXknbGwgYWxzbyBhbGwgcHJpbnQgdG8gdGhlIHNhbWUgc2hhZGUgb2YgZ3JleSEKCiMjIyBJbmhlcml0YW5jZQoKSWYgd2UgYWRkIG9uZSBtb3JlIGdlb21ldHJ5IChhIGxpbmUpLCB3ZSBzZWUgdGhhdCBpdCBhbHNvIGluaGVyaXRzIHRoZSBtYXBwaW5nIG9mIGBXb3JkYCB0byBgY29sb3JgLgoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDgvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQpnZ3Bsb3QoSV9zdWJzZXQsIGFlcyhEdXJfbXNlYywgRjEubiwgY29sb3IgPSBXb3JkKSkrCiAgZ2VvbV9wb2ludCgpKwogIGdlb21fbGluZSgpCmBgYAoKVGhlcmUgYXJlIGEgZmV3IGltcG9ydGFudCB0aGluZ3MgdG8gdGFrZSBub3RlIG9mIGluIHRoaXMgcGxvdC4gRmlyc3QsIHlvdSBjYW4gc2VlIHRoYXQgd2UgaGF2ZSBhY3R1YWxseSBhZGRlZCBmb3VyIGxpbmVzIHRvIHRoZSBwbG90LCBvbmUgZm9yIGVhY2ggY29sb3IuIEluIG1vc3QgY2FzZXMsIHdoZW4geW91IG1hcCBjYXRlZ29yaWNhbCBkYXRhIHRvIGFuIGFlc3RoZXRpYyBsaWtlIGBjb2xvcmAsIHlvdSBhcmUgYWxzbyBkZWZpbmluZyBzdWItZ3JvdXBpbmdzIG9mIHRoZSBkYXRhLCBhbmQgYGdncGxvdDJgIHdpbGwgZHJhdyBhIGxpbmVzLCBjYWxjdWxhdGUgc3RhdGlzdGljcywgZXRjLiBzZXBhcmF0ZWx5IGZvciBldmVyeSBzdWItZ3JvdXBpbmcgb2YgdGhlIGRhdGEuCgpUaGUgc2Vjb25kIGltcG9ydGFudCB0aGluZyB0byBub3RpY2UgaXMgdGhhdCBgZ2VvbV9saW5lKClgIGpvaW5zIHVwIHBvaW50cyBhcyB0aGV5IGFyZSBvcmRlcmVkIGFsb25nIHRoZSB4LWF4aXMsIF9ub3RfIGFjY29yZGluZyB0byB0aGVpciBvcmRlciBpbiB0aGUgb3JpZ2luYWwgZGF0YSBmcmFtZS4gVGhlcmUgaXMgYSBnZW9tIHdoaWNoIHdpbGwgam9pbiB1cCBwb2ludHMgdGhhdCB3YXkgY2FsbGVkIGBnZW9tX3BhdGgoKWAuCgpUaGUgcG9pbnQgaGVyZSwgdGhvdWdoLCBpcyB0aGF0IGl0IGlzIHBvc3NpYmxlIHRvIGRlZmluZSBkYXRhLXRvLWFlc3RoZXRpYyBtYXBwaW5ncyBpbnNpZGUgb2YgZ2VvbSBmdW5jdGlvbnMsIGFsc28gYnkgdXNpbmcgYGFlcygpYC4gSGVyZSwgaW5zdGVhZCBvZiBtYXBwaW5nIGBXb3JkYCB0byBgY29sb3JgIGluc2lkZSBvZiBgZ2dwbG90KClgLCB3ZSdsbCBkbyBpdCBpbnNpZGUgb2YgYGdlb21fcG9pbnQoKWAuCgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoID0gOC8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CmdncGxvdChJX3N1YnNldCwgYWVzKER1cl9tc2VjLCBGMS5uKSkrCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBXb3JkKSkrCiAgZ2VvbV9saW5lKCkKYGBgCgpUaGUgcG9pbnRzIGFyZSBzdGlsbCBjb2xvcmVkIGFjY29yZGluZyB0byB0aGUgd29yZCwgYnV0IHRoZXJlIGlzIG9ubHkgb25lLCBibGFjayBsaW5lLiBXZSBjYW4gYWxzbyB0cnkgcGFzc2luZyBgYWVzKGNvbG9yID0gV29yZClgIHRvIGBnZW9tX2xpbmUoKWAuCgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoID0gOC8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CmdncGxvdChJX3N1YnNldCwgYWVzKER1cl9tc2VjLCBGMS5uKSkrCiAgZ2VvbV9wb2ludCgpKwogIGdlb21fbGluZSggYWVzKGNvbG9yID0gV29yZCkpCmBgYApOb3csIHRoZSBsaW5lcyBhcmUgY29sb3JlZCBhY2NvcmRpbmcgdG8gdGhlIHdvcmQsIGJ1dCB0aGUgcG9pbnRzIGFyZSBhbGwgYmxhY2suIFRoaXMgYnJpbmdzIHVwIHRoZSBhbGwgaW1wb3J0YW50IHBvaW50IGFib3V0IGFlc3RoZXRpY3M6CgpfX0dlb21zIGluaGVyaXQgYWVzdGhldGljIG1hcHBpbmdzIGZyb20gdGhlIGBnZ3Bsb3QoKWAgZGF0YSBsYXllciwgYW5kIG5vdCBmcm9tIGFueSBvdGhlciBsYXllci5fXwoKIyMjIEdyb3VwaW5nCkxldCdzIGxvb2sgYXQgdGhlIGVmZmVjdCBvZiBtYXBwaW5nIGBXb3JkYCB0byBgY29sb3JgIG9uIHRoZSBjYWxjdWxhdGlvbiBvZiBzdGF0aXN0aWNzLCBsaWtlIHNtb290aGluZyBsaW5lcy4gTm90ZSwgaW5zaWRlIG9mIGBzdGF0X3Ntb290aCgpYCBJJ3ZlIHNhaWQgYHNlID0gRmAgdG8gdHVybiBvZmYgdGhlIGRpc3BsYXkgb2Ygc3RhbmRhcmQgZXJyb3JzLgoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDgvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yLCB3YXJuaW5nID0gRn0KZ2dwbG90KElfc3Vic2V0LCBhZXMoRHVyX21zZWMsIEYxLm4sIGNvbG9yPVdvcmQpKSsKICBnZW9tX3BvaW50KCkrCiAgc3RhdF9zbW9vdGgoc2UgPSBGKQpgYGAKCkp1c3QgbGlrZSBzZXBhcmF0ZSBsaW5lcyB3ZXJlIGRyYXduIGZvciBlYWNoIGdyb3VwIGFzIGRlZmluZWQgYnkgYGNvbG9yPVdvcmRgLCBgZ2dwbG90MmAgaGFzIGNhbGN1bGF0ZWQgc2VwYXJhdGUgc21vb3RoZXJzIGZvciBlYWNoIHN1YnNldC4gSWYgd2UgaGFkIG9ubHkgcGFzc2VkIGBjb2xvcj1Xb3JkYCB0byBgZ2VvbV9wb2ludCgpYCwgdGhvdWdoLCBgc3RhdF9zbW9vdGgoKWAgd291bGQgbm90IGhhdmUgaW5oZXJpdGVkIHRoYXQgbWFwcGluZywgcmVzdWx0aW5nIGluIGEgc2luZ2xlIHNtb290aGVyIGJlaW5nIGNhbGN1bGF0ZWQuCgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoID0gOC8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CiAgZ2dwbG90KElfc3Vic2V0LCBhZXMoRHVyX21zZWMsIEYxLm4pKSsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPVdvcmQpKSsKICAgIHN0YXRfc21vb3RoKHNlID0gRikKYGBgCgpJdCdzIGltcG9ydGFudCB0byB1bmRlcnN0YW5kIHRoYXQgd2hlbiB5b3UgbWFwIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB0byBhbiBhZXN0aGV0aWMgdGhhdCB5b3UncmUgYWxzbyBkZWZpbmluZyBzdWItZ3JvdXBpbmdzLiBGb3IgZXhhbXBsZSwgaWYgd2UgbWFwIGBXb3JkYCB0byBgc2hhcGVgLCBpbnN0ZWFkIG9mIGBjb2xvcmAsIHRoZSBwb2ludCBzaGFwZXMgd2lsbCBub3cgcmVwcmVzZW50IHRoZSB3b3JkLgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoID0gOC8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CmdncGxvdChJX3N1YnNldCwgYWVzKER1cl9tc2VjLCBGMS5uLCBzaGFwZT1Xb3JkKSkrCiAgZ2VvbV9wb2ludCgpCmBgYAoKTm93IGlmIHdlIGFkZCBhIHNtb290aGVyIHRvIHRoaXMgcGxvdCwgX2V2ZW4gdGhvdWdoIGBzaGFwZWAgaXNuJ3QgZGVmaW5lZCBmb3IgbGluZXNfLCB0aGUgc21vb3RoZXIgd2lsbCBzdGlsbCBwbG90IGEgZGlmZmVyZW50IHNtb290aGluZyBjdXJ2ZSBmb3IgZWFjaCBzdWItZ3JvdXBpbmcuCmBgYHtyIHRpZHkgPSBGLCBmaWcud2lkdGggPSA4LzEuMiwgZmlnLmhlaWdodD01LzEuMiwgd2FybmluZz1GfQpnZ3Bsb3QoSV9zdWJzZXQsIGFlcyhEdXJfbXNlYywgRjEubiwgc2hhcGU9V29yZCkpKwogIGdlb21fcG9pbnQoKSsKICBzdGF0X3Ntb290aChzZSA9IEYpCmBgYAoKSWYgeW91IHJlYWxseSBvbmx5IHdhbnRlZCBhIHNpbmdsZSBzbW9vdGhlciBsaW5lIGZvciBhbGwgb2YgdGhlIGRhdGEgaW4gdGhpcyBjYXNlLCBvbmUgc29sdXRpb24gd291bGQgYmUgdG8gbW92ZSB0aGUgYHNoYXBlPVdvcmRgIG1hcHBpbmcgZnJvbSB0aGUgZGF0YSBsYXllciB0byB0aGUgYGdlb21fcG9pbnQoKWAgbGF5ZXIuIEJ1dCBpbiBtb3N0IGNhc2VzLCBpdCdzIGFjdHVhbGx5IG1vcmUgZGVzaXJhYmxlIHRvIG92ZXJyaWRlIHRoZSBhZXN0aGV0aWMgbWFwcGluZy4gV2UgY2FuIGRvIHRoaXMgd2l0aCB0aGUgc3BlY2lhbCBhZXN0aGV0aWMgYGdyb3VwYC4KCmBncm91cGAgZG9lcyBleGFjdGx5IHdoYXQgaXQgc291bmRzIGxpa2UgaXQgb3VnaHQgdG86IGl0IGRlZmluZXMgZ3JvdXBzIG9mIGRhdGEuIFdoZW4geW91IHdhbnQgdG8gb3ZlcnJpZGUgZ3JvdXBzIGRlZmluZWQgaW4gdGhlIGRhdGEgbGF5ZXIsIHlvdSBjYW4gZG8gc28gYnkgc2F5aW5nIGBncm91cD0xYC4KYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDgvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQpnZ3Bsb3QoSV9zdWJzZXQsIGFlcyhEdXJfbXNlYywgRjEubiwgc2hhcGU9V29yZCkpKwogIGdlb21fcG9pbnQoKSsKICBzdGF0X3Ntb290aChzZSA9IEYsIGFlcyhncm91cCA9IDEpKQpgYGAKClRoZSBlZmZlY3QgaXQgaGFzIG9uIGBzdGF0X3Ntb290aCgpYCBpcyB0aGF0IGp1c3QgYSBzaW5nbGUgc21vb3RoZXIgaXMgY2FsY3VsYXRlZC4gSWYgd2UgY29tZSBiYWNrIHRvIGBjb2xvciA9IFdvcmRgLCBhbmQgdGhlbiBkcmF3IGEgbGluZSB3aXRoIGBncm91cCA9IDFgLCB0aGUgZWZmZWN0IGlzIHRoYXQgd2UgZHJhdyBvbmUgbGluZSB0aGF0IHZhcmllcyBpbiBjb2xvci4KYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDgvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQpnZ3Bsb3QoSV9zdWJzZXQsIGFlcyhEdXJfbXNlYywgRjEubiwgY29sb3I9V29yZCkpKwogIGdlb21fbGluZShhZXMoZ3JvdXAgPSAxKSkKYGBgCgoKIyMjIE1vcmUgYWVzdGhldGljcyBhbmQgdGhlaXIgdXNlLgoKU28gZmFyLCB3ZSd2ZSBvbmx5IG1hcHBlZCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgdG8gYGNvbG9yYCwgYnV0IGl0J3MgYWxzbyBwb3NzaWJsZSB0byBtYXAgY29udGludW91cyB2YXJpYWJsZXMgdG8gYGNvbG9yYC4gSGVyZSB3ZSdsbCByZWR1bmRhbnRseSBtYXAgYEYxLm5gIHRvIGJvdGggYHlgIGFuZCBgY29sb3JgLgoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDgvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQpnZ3Bsb3QoSV9qZWFuLCBhZXMoRHVyX21zZWMsIEYxLm4sIGNvbG9yID0gRjEubikpKwogIGdlb21fcG9pbnQoKQpgYGAKCkFub3RoZXIgaW1wb3J0YW50IGFlc3RoZXRpY3MgZGlzdGluY3Rpb24gaXMgYmV0d2VlbiBgY29sb3JgIGFuZCBgZmlsbGAuIElmIHdlIHdhbnRlZCB0byBjcmVhdGUgYSBiYXIgY2hhcnQgb2Ygd29yZCBmcmVxdWVuY2llcywgd2UgY291bGQgZG8gc28gYnkgbWFwcGluZyBgV29yZGAgdG8gdGhlIHgtYXhpcywgYW5kIGFkZGluZyBgZ2VvbV9iYXIoKWAgd2l0aG91dCBhbnkgeS1heGlzIHZhcmlhYmxlIGRlZmluZWQuCgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoID0gOC8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CmdncGxvdChJX2plYW4sIGFlcyhXb3JkKSkrCiAgZ2VvbV9iYXIoKQpgYGAKCklmIHlvdSBhbHNvIHdhbnRlZCB0byBjb2xvciB0aGUgYmFycyBhY2NvcmRpbmcgdG8gdGhlIHdvcmQsIHlvdXIgZmlyc3QgaW5zdGluY3Qgd291bGQgcHJvYmFibHkgYmUgdG8gbWFwIGBjb2xvciA9IFdvcmRgLiBCdXQgdGhlIHJlc3VsdCBpcyB0aGF0IG9ubHkgdGhlIGNvbG9ycyBvZiB0aGUgYmFycycgb3V0bGluZXMgYXJlIG1hcHBlZCB0byBgV29yZGAuCgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoID0gOC8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CmdncGxvdChJX2plYW4sIGFlcyhXb3JkLCBjb2xvciA9IFdvcmQpKSsKICBnZW9tX2JhcigpCmBgYAoKCldoYXQgaXMgcHJvYmFibHkgbW9yZSBhZHZpc2FibGUgaXMgdG8gbWFwIGBXb3JkYCB0byBgZmlsbGAsIHdoaWNoIGNvbnRyb2wgdGhlIGZpbGxpbmcgY29sb3Igb2YgdHdvIGRpbWVuc2lvbmFsIGdlb21zLgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoID0gOC8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CmdncGxvdChJX2plYW4sIGFlcyhXb3JkLCBmaWxsID0gV29yZCkpKwogIGdlb21fYmFyKCkKYGBgCgpBcyB5b3UgbWlnaHQgaGF2ZSBmaWd1cmVkIG91dCBub3csIGl0J3MgdGVjaG5pY2FsbHkgcG9zc2libGUgdG8gbWFwIHRoZSBgZmlsbGAgY29sb3Igb2YgYmFycyB0byBvbmUgdmFyaWFibGUsIGFuZCB0aGUgb3V0bGluZSBgY29sb3JgIHRvIGRpZmZlcmVudCB2YXJpYWJsZS4gTXkgYWR2aWNlIGlzIHRvIG5ldmVyIGRvIHN1Y2ggYSB0aGluZywgYmVjYXVzZSB0aGUgcmVzdWx0cyBhbG1vc3QgYWx3YXlzIGNvbWUgb3V0IGEganVtYmxlZCBtZXNzLiBJbnN0ZWFkLCBJIHdvdWxkIHN1Z2dlc3Qgc2V0dGluZyB0aGUgYGNvbG9yYCBvZiB0aGUgYmFycyB0byBibGFjay4gSSBmaW5kIGl0IG1vcmUgcGxlYXNpbmcgdG8gdGhlIGV5ZSwgYW5kIGhlbHBzIHRvIGVtcGhhc2l6ZSB0aGUgZGl2aXNpb25zIGJldHdlZW4gYmFycyB3aGVuIHRoZXkncmUgc3RhY2tlZC4gQ29tcGFyZSB0aGlzIHBsb3Q6CgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoID0gNi8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CmdncGxvdChJX3N1YnNldCwgYWVzKE5hbWUsIGZpbGwgPSBXb3JkKSkrCiAgZ2VvbV9iYXIoKQpgYGAKCnRvIHRoaXMgb25lLgoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDYvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQpnZ3Bsb3QoSV9zdWJzZXQsIGFlcyhOYW1lLCBmaWxsID0gV29yZCkpKwogIGdlb21fYmFyKGNvbG9yID0gImJsYWNrIikKYGBgCgojIyBHZW9tZXRyaWVzClNvIGZhciwgd2UndmUgdXNlZCB0aGUgZm9sbG93aW5nIGdlb21ldHJpZXM6CgoqIGBnZW9tX3BvaW50KClgCiogYGdlb21fbGluZSgpYAoqIGBnZW9tX2JhcigpYAoKQWxsIGdlb21ldHJpZXMgYmVnaW4gd2l0aCBgZ2VvbV9gLCBtZWFuaW5nIHlvdSBjYW4gZ2V0IGEgZnVsbCBsaXN0IHVzaW5nIGBhcHJvcG9zKClgLiAKCmBgYHtyfQphcHJvcG9zKCJeZ2VvbV8iKQpgYGAKClRoaXMgaXMgYSBxdWl0ZSBleHRlbnNpdmUgbGlzdCwgYW5kIHdlIHdvbid0IGJlIGFibGUgdG8gY292ZXIgdGhlbSBhbGwgdG9kYXkuIE1hbnkgb2YgdGhlbSBhcmUgYWN0dWFsbHkgY29udmVuaWVuY2UgZnVuY3Rpb25zIGZvciBzcGVjaWFsIHNldHRpbmdzIG9mIG90aGVyIGdlb21zLiBGb3IgZXhhbXBsZSwgYGdlb21faGlzdG9ncmFtKClgIGlzIHJlYWxseSBqdXN0IGBnZW9tX2JhcigpYCB3aXRoIHNwZWNpYWwgc2V0dGluZ3MuIAoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDYvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQpnZ3Bsb3QoSV9qZWFuLCBhZXMoRjEubikpKwogIGdlb21faGlzdG9ncmFtKCkKYGBgCgpPdGhlciBnZW9tcyBhcmUganVzdCBjb252ZW5pZW5jZSBmdW5jdGlvbnMgZm9yIHN0YXRpc3RpY2FsIGxheWVycy4gRm9yIGV4YW1wbGUsIHlvdSdsbCBub3RpY2UgYGdlb21fc21vb3RoKClgLCB3aGljaCBpZiB5b3UgYWRkIGl0IHRvIGEgcGxvdCB3aWxsIGhhdmUgdGhlIHNhbWUgYmVoYXZpb3Igb2YgYHN0YXRfc21vb3RoKClgLCB3aGljaCB3ZSd2ZSBhbHJlYWR5IGJlZW4gdXNpbmcgZXh0ZW5zaXZlbHkuCgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoID0gNi8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CmdncGxvdChJX2plYW4sIGFlcyhEdXJfbXNlYywgRjEubikpKwogIGdlb21fc21vb3RoKCkKYGBgCgpgc3RhdF9zbW9vdGgoKWAgY2FuIGFjdHVhbGx5IHBsb3QgbWFueSBkaWZmZXJlbnQga2luZHMgb2Ygc21vb3RoZXJzLiBGb3IgYSBsaW5lYXIgbW9kZWwsIGZvciBleGFtcGxlOgoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDYvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQpnZ3Bsb3QoSV9qZWFuLCBhZXMoRHVyX21zZWMsIEYxLm4pKSsKICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nKQpgYGAKCiMjIyBTb21lIHNwZWNpYWwgZ2VvbXMKClNvbWUgZ2VvbXMgYXJlIGJvdGggdW5pcXVlIGFuZCBjb21tb24gZW5vdWdoIGluIHRoZWlyIHVzYWdlIHRvIHdhcnJhbnQgc3BlY2lhbCBtZW50aW9uLiAKCgojIyMjIyBgZ2VvbV90ZXh0KClgIGFuZCBgZ2VvbV9sYWJlbCgpYAoKQWRkaW5nIHRleHQsIGFuZCB0ZXh0IGxhYmVscyB0byBhIHBsb3QsIGlzIGEgdmVyeSBjb21tb24gdGFzaywgYW5kIGlzIGRvbmUgd2l0aCBgZ2VvbV90ZXh0KClgLiBUaGVyZSBpcyBhIHNwZWNpYWwgYWVzdGhldGljIGp1c3QgZm9yIGBnZW9tX3RleHQoKWAgY2FsbGVkIGBsYWJlbGAsIHdoaWNoIGRlZmluZXMgdGhlIGNvbHVtbiB0aGF0IHNob3VsZCBiZSB1c2VkIGFzIHRoZSB0ZXh0IGxhYmVsLgoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDgvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQpnZ3Bsb3QoSV9zdWJzZXQsIGFlcyhEdXJfbXNlYywgRjEubikpKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBXb3JkKSkKYGBgCgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoID0gOC8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CmdncGxvdChJX3N1YnNldCwgYWVzKER1cl9tc2VjLCBGMS5uKSkrCiAgZ2VvbV9sYWJlbChhZXMobGFiZWwgPSBXb3JkKSkKYGBgCgojIyMgUG9zaXRpb25pbmcKCkp1c3QgbGlrZSBpbmhlcml0YW5jZSB3YXMgdGhlIGJpZyBpZGVhIGZvciBhZXN0aGV0aWNzLCBwb3NpdGlvbmluZyBpcyB0aGUgYmlnIGlkZWEgZm9yIGdlb21zLiBGb3IgdmFyaW91cyByZWFzb25zLCB5b3UgbWF5IHdhbnQgdG8gYWRqdXN0IHdoZXJlIGdlb21ldHJpZXMgYXJlIHBsb3R0ZWQuIEFzIGEgc29sdXRpb24gdG8gb3ZlcnBsb3R0aW5nLCBmb3IgZXhhbXBsZSwgeW91IG1heSB3YW50IHRvIGFkZCBzb21lIGppdHRlciB0byBwb2ludHMuIFdoZW4gZGVhbGluZyB3aXRoIGJhcnMsIHlvdSBuZWVkIHRvIGRlY2lkZSB3aGV0aGVyIHRoZXkgc2hvdWxkIGJlIHN0YWNrZWQsIG9yIGFycmFuZ2VkIG5leHQgdG8gZWFjaCBvdGhlci4gVGhlc2Ugc21hbGwgYWRqdXN0bWVudHMgCgoqIGlkZW50aXR5CiAgICAqIFRoaXMgaXMgdGhlIGRlZmF1bHQgaW4gbW9zdCBjYXNlcywgc2ltcGx5IHBsb3R0aW5nIGdlb21ldHJpZXMgd2hlcmUgdGhleSdyZSBkZWZpbmVkIGJ5IHggYW5kIHkuCiogaml0dGVyCiAgICAqIFRoaXMgYWRkcyBzb21lIHJhbmRvbSBub2lzZSBlaXRoZXIgdG8gdGhlIHggcG9zaXRpb24gb3IgdGhlIHRoZSB5IHBvc2l0aW9uLCBhbmQgaXMgdHlwaWNhbGx5CiAgICAgdXNlZCBqdXN0IGZvciBwb2ludHMuCiogc3RhY2sKICAgICogVGhpcyBzdGFja3MgZ2VvbWV0cmllcyBvbiB0b3Agb2YgZWFjaCBvdGhlci4gVGhpcyBpcyB0aGUgZGVmYXVsdCBmb3IgYmFycwoqIGRvZGdlCiAgICAqIFRoaXMgcHVzaGVzIGdlb21ldHJpZXMgb3V0IG9mIGVhY2ggb3RoZXIncyB3YXksIHRvIHRoZSBsZWZ0IGFuZCByaWdodC4KKiBmaWxsCiAgICAqIFRoaXMgc3RhY2tzIGdlb21ldHJpZXMgb24gdG9wIG9mIGVhY2ggb3RoZXIsIGFuZCBleHBhbmRzIG9yIGNvbnRyYWN0cyB0aGVtIHRvIGZpbGwgdGhlIHNwYWNlCiAgICBiZXR3ZWVuIDAgYW5kIDEuIEdvb2QgZm9yIHBsb3R0aW5nIHByb3BvcnRpb25zLgoKIyMjIyMgSml0dGVyClNvbWUgcGVvcGxlLCBsaWtlIFtBbmRyZXcgR2VsbWFuXShodHRwOi8vYW5kcmV3Z2VsbWFuLmNvbS8yMDA5LzEwL2JldHRlcl90aGFuX2FfYi8pLCBoYXRlIGJveHBsb3RzLgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoID0gOC8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CmdncGxvdChJX2plYW4sIGFlcyhXb3JkLCBGMS5uKSkrCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgpBIGZyZXF1ZW50IHN1Z2dlc3Rpb24gZm9yIGEgcmVwbGFjZW1lbnQgdG8gYm94cGxvdHMgaXMganVzdCB0byBwbG90IHRoZSByYXcgZGF0YSBwb2ludHMgd2l0aCBzb21lIGppdHRlci4gVG8gZ2V0IHN0YXJ0ZWQsIHdlJ2xsIHJlcGxhY2UgYGdlb21fYm94cGxvdCgpYCB3aXRoIGBnZW9tX3BvaW50KClgLgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoID0gOC8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CmdncGxvdChJX2plYW4sIGFlcyhXb3JkLCBGMS5uKSkrCiAgZ2VvbV9wb2ludCgpCmBgYAoKQW5kIHRoZSBhZGQgc29tZSBqaXR0ZXIsIGJ5IGRlZmluaW5nIGBwb3NpdGlvbiA9ICJqaXR0ZXIiYCBpbnNpZGUgb2YgYGdlb21fcG9pbnQoKWAuCmBgYHtyIHRpZHkgPSBGLCBmaWcud2lkdGggPSA4LzEuMiwgZmlnLmhlaWdodD01LzEuMn0KZ2dwbG90KElfamVhbiwgYWVzKFdvcmQsIEYxLm4pKSsKICBnZW9tX3BvaW50KHBvc2l0aW9uID0gImppdHRlciIpCmBgYAoKSW4gdGhpcyBleGFtcGxlLCB5b3UgY2FuIHNlZSB0aGUgYmVuZWZpdCBvZiBqaXR0ZXJlZCBwb2ludHMgb3ZlciBib3hwbG90cy4gV2l0aCBib3hwbG90cywgdGhlcmUncyBubyBoaW50IHRoYXQgb25lIGNhdGVnb3J5LCAiSSIgaGFzIGVub3Jtb3VzbHkgbW9yZSBkYXRhIHRoYW4gdGhlIG90aGVycy4KCgpBcyBhIGNvbnZlbmllbmNlLCB0aGVyZSdzIGEgZ2VvbSBjYWxsZWQgYGdlb21faml0dGVyKClgLCB3aGljaCBpcyBqdXN0IGEgY29udmVuaWVuY2UgZnVuY3Rpb24gZm9yIGBnZW9tX3BvaW50KHBvc2l0aW9uID0gImppdHRlciIpYC4KYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDgvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQpnZ3Bsb3QoSV9qZWFuLCBhZXMoV29yZCwgRjEubikpKwogIGdlb21faml0dGVyKCkKYGBgCgoKCiMgU2NhbGVzIApTY2FsZXMgcHJvdmlkZSB5b3UgbW9yZSBmaW5lIGdyYWluZWQgY29udHJvbCBvdmVyIG92ZXIgdGhlIHByZXNlbnRhdGlvbiBvZiBhZXN0aGV0aWNzLiBGb3IgZXZlcnkgYWVzdGhldGljLCB0aGVyZSBpcyBhIGNvcnJlc3BvbmRpbmcgc2NhbGUgeW91IGNhbiB1c2UuCkZvciBtb3JlIGZsZXhpYmlsaXR5LCB5b3Ugc2hvdWxkIGFsc28gaW5zdGFsbCBhbmQgbG9hZCB0aGUgYHNjYWxlc2AgcGFja2FnZS4KCmBgYHtyfQogIGxpYnJhcnkoc2NhbGVzKQpgYGAKCiMjIHggYW5kIHkgc2NhbGVzCkFsbCBzY2FsZXMgYmVnaW4gd2l0aCBgc2NhbGVfYCBmb2xsb3dlZCBieSB0aGUgbmFtZSBvZiB0aGUgYWVzdGhldGljIGl0IGNvbnRyb2xzLCB0aGVuIGZvbGxvd2VkIGJ5IGl0cyBzdWItdHlwZS4gSGVyZSBhcmUgYWxsIHRoZSB4LWF4aXMgc2NhbGVzLCBmb3Igd2hpY2ggdGhlcmUgYXJlIGlkZW50aWNhbCB5LWF4aXMgc2NhbGVzLgoKCmBgYHtyfQphcHJvcG9zKCJec2NhbGVfeF8iKQpgYGAKCmBzY2FsZV94X2NvbnRpbnVvdXNgLCBgc2NhbGVfeF9kaXNjcmV0ZWAsIGBzY2FsZV94X2RhdGV0aW1lYCBhbmQgYHNjYWxlX3hfZGF0ZWAgYXJlIHRoZSBiYXNpYyBraW5kcyBvZiB4IGFuZCB5IGF4ZXMgeW91IGNhbiBjb25zdHJ1Y3QgaW4gYGdncGxvdDJgLiBGb3IgdGhlIG1vc3QgcGFydCwgYGdncGxvdDJgIHdpbGwgZmlndXJlIG91dCB3aGljaCBraW5kIG9mIHNjYWxlIHRvIHVzZSwgYW5kIHlvdSdsbCBvbmx5IG5lZWQgdG8gYWRkIG9uZSBvZiB0aGVzZSBpZiB5b3Ugd2FudCB0byBtb2RpZnkgdGhlIGRlZmF1bHQgYXBwZWFyYW5jZS4KCmBzY2FsZV94X2xvZzEwYCwgYHNjYWxlX3hfc3FydGAgYW5kIGBzY2FsZV94X3JldmVyc2VgIGFyZSBiYXNpYyB0cmFuc2Zvcm1hdGlvbnMgdG8gYSBjb250aW51b3VzIHNjYWxlLiBNYW55IG1vcmUga2luZHMgb2YgdHJhbnNmb3JtYXRpb25zIGFyZSBwb3NzaWJsZSwgYW5kIGl0J3MgZXZlbiBwb3NzaWJsZSB0byBkZWZpbmUgeW91ciBvd24gY3VzdG9tIHRyYW5zZm9ybWF0aW9ucywgYnV0IHRoZXNlIGFyZSB0aGUgb25seSBvbmVzIGZvciB3aGljaCB0aGVyZSBhcmUgc3BlY2lhbCBjb252ZW5pZW5jZSBgc2NhbGVfeF8qYCBmdW5jdGlvbnMuCgojIyMgYHNjYWxlX2AgYXJndW1lbnRzClRoZXJlIGFyZSBhIGZldyBiYXNpYyBhcmd1bWVudHMgdGhhdCB5b3UgY2FuIHBhc3MgdG8gYSBzY2FsZS4KCiogYG5hbWVgCiAgICAqIFRoaXMgaXMgdGhlIHRpdGxlIHRoYXQgd2lsbCBiZSBkaXNwbGF5ZWQgb24gdGhlIHNjYWxlLCBlaXRoZXIgb24gdGhlIGF4ZXMgZm9yIHggYW5kIHkgc2NhbGVzLCBvciBpbiB0aGUgbGVnZW5kIGZvciBvdGhlciBzY2FsZXMuCiogYGxpbWl0c2AKICAgICogVGhpcyBkZWZpbmVzIHRoZSByYW5nZSBvZiBkYXRhIHRvIGJlIHByZXNlbnRlZCBpbiB0aGUgcGxvdC4gRm9yIGRpc2NyZXRlIHNjYWxlcywgaXQnbGwgZGVmaW5lIHRoZSBvcmRlciB3aXRoIHdoaWNoIHRvIGRpc3BsYXkgY2F0ZWdvcmljYWwgZmFjdG9ycy4KKiBgYnJlYWtzYAogICAgKiBUaGVzZSBhcmUgdGhlIGxhYmVsZWQgcG9pbnRzIGFsb25nIHRoZSBzY2FsZSwgZWl0aGVyIHRoZSBtYWpvciBheGlzIGxhYmVscywgb3IgdGhlIGxhYmVscyBvbiB0aGUgbGVnZW5kLgoqIGBsYWJlbHNgCiAgICAqIFRoaXMgZGVmaW5lcyB0aGUgbGFiZWxzIHRvIHVzZSBhdCBlYWNoIGJyZWFrIHBvaW50LCBpZiB5b3Ugd2FudCB0byBvdmVycmlkZSB0aGVtCiogYHRyYW5zYAogICAgKiBUaGlzIGRlZmluZXMgYSBudW1lcmljYWwgdHJhbnNmb3JtYXRpb24geW91J2QgbGlrZSB0byBhcHBseSB0byBhIHNjYWxlLCBhbmQgdXN1YWxseSBvbmx5IGFwcGxpY2FibGUgdG8gY29udGludW91cyBzY2FsZXMuIFdlJ2xsIHRhbGsgbW9yZSBhYm91dCB0cmFuc2Zvcm1hdGlvbnMgYmVsb3cuCgoKIyMjIGBzY2FsZV9beHldX2NvbnRpbnVvdXNgCgpIZXJlJ3Mgb3VyIGJhc2ljIGR1cmF0aW9uIGJ5IEYxIHBsb3QgYWdhaW4sIHdoaWNoIGhhcyB0d28gY29udGludW91cyBzY2FsZXMgZm9yIHRoZSB4IGFuZCB5IGF4ZXMuCgoKYGBge3IgZmlnLndpZHRoID0gOC8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CmdncGxvdChJX2plYW4sIGFlcyhEdXJfbXNlYywgRjEubikpKwogIGdlb21fcG9pbnQoKQpgYGAKCgoKSWYgd2Ugd2FudCB0byBjaGFuZ2UgdGhlIHggYXhpcyBsYWJlbCwgd2UgY2FuIGRvIHNvIGJ5IGFkZGluZyBgc2NhbGVfeF9jb250aW5pb3VzKClgIGFuZCBwYXNzaW5nIG91ciBkZXNpcmVkIHRpdGxlIHRvIGBuYW1lYC4KCmBgYHtyfQpnZ3Bsb3QoSV9qZWFuLCBhZXMoRHVyX21zZWMsIEYxLm4pKSsKICBnZW9tX3BvaW50KCkrCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAiVm93ZWwgRHVyYXRpb24gKG1zZWMpIikKYGBgCgoKU2luY2UgY2hhbmdpbmcgdGhlIGF4aXMgdGl0bGVzIGlzIHNvbWV0aGluZyB5b3UncmUgbGlrZWx5IHRvIGRvIHZlcnkgZnJlcXVlbnRseSwgdGhlcmUgYXJlIHR3byBjb252ZW5pZW5jZSBmdW5jdGlvbnMgZm9yIHRoaXMgcHVycG9zZSB3aXRoIHNob3J0ZXIgbmFtZXMgdGhhdCBzYXZlIHlvdSB0eXBpbmc6IGB4bGFiKClgIGFuZCBgeWxhYigpYC4KCmBgYHtyfQpnZ3Bsb3QoSV9qZWFuLCBhZXMoRHVyX21zZWMsIEYxLm4pKSsKICBnZW9tX3BvaW50KCkrCiAgeGxhYigiVm93ZWwgRHVyYXRpb24gKG1zZWMpIikKYGBgCgoKClRoZSBuZXh0IG1vc3QgaW1wb3J0YW50IHRoaW5nIHlvdSdsbCB3YW50IHRvIGRvIHRvIHggYW5kIHkgYXhpcyBzY2FsZXMgaXMgdHJhbnNmb3JtIHRoZW0uIEZvciBleGFtcGxlLCBkdXJhdGlvbiBtZWFzdXJlbWVudHMgdGVuZCB0byBiZSBsZWZ0LXNrZXdlZCwgc28gYSBsb2cgdHJhbnNmb3JtYXRpb24gaXMgYWR2aXNhYmxlLiBUaGUgYmVuZWZpdCBvZiB0cmFuc2Zvcm1pbmcgdGhlIHNjYWxlIG92ZXIgc2ltcGx5IHBsb3R0aW5nIGBsb2cyKER1cl9tc2VjKWAgaXMgdGhhdCBgZ2dwbG90MmAgd2lsbCB2ZXJ5IG5pY2VseSBsYWJlbCB0aGUgYXhpcyBhY2NvcmRpbmcgdG8gdGhlIG9yaWdpbmFsIHZhbHVlcy4gQ29tcGFyZSB0aGUgbGFiZWxzIG9mIHRoZSB4LWF4ZXMgb2YgdGhlc2UgdHdvIHBsb3RzLgoKYGBge3IgdGlkeSA9IEYsZmlnLndpZHRoID0gOC8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CmdncGxvdChJX2plYW4sIGFlcyhsb2cyKER1cl9tc2VjKSwgRjEubikpKwogIGdlb21fcG9pbnQoKSsKICBzY2FsZV94X2NvbnRpbnVvdXMoImxvZzIgVm93ZWwgRHVyYXRpb24gKG1zZWMpIikKCmBgYAoKCmBgYHtyIGZpZy53aWR0aCA9IDgvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQpnZ3Bsb3QoSV9qZWFuLCBhZXMoRHVyX21zZWMsIEYxLm4pKSsKICBnZW9tX3BvaW50KCkrCiAgc2NhbGVfeF9jb250aW51b3VzKCJWb3dlbCBEdXJhdGlvbiAobXNlYykiLAogICAgICAgICAgICAgICAgICAgICB0cmFucyA9ICJsb2cyIikKYGBgCgoKICBUaGVzZSBhcmUgYWxsIG9mIHRoZSBwcmUtZGVmaW5lZCB0cmFuc2Zvcm1hdGlvbnMuCgoKYGBge3J9CmFwcm9wb3MoIl90cmFucyQiKQpgYGAKCgoKIyMgY29sb3IgYW5kIGZpbGwgc2NhbGVzCkhlcmUgYXJlIGFsbCBvZiB0aGUgY29sb3Igc2NhbGVzLCBmb3IgYWxsIG9mIHdoaWNoIHRoZXJlIGlzIGFuIGFjY29tcGFueWluZyBmaWxsIHNjYWxlLgoKYGBge3J9CmFwcm9wb3MoIl5zY2FsZV9jb2xvcl8iKQpgYGAKCgpUaGVyZSBhcmUgdHdvIHZlcnkgZGlzdGluY3Qga2luZHMgb2YgY29sb3Igc2NhbGVzIGhlcmU6IGNhdGVnb3JpY2FsIGFuZCBncmFkaWVudC4gVGhlIHVzZSBvZiBvbmUgb3ZlciB0aGUgb3RoZXIgaGFzIGV2ZXJ5dGhpbmcgdG8gZG8gd2l0aCB0aGUga2luZCBvZiBkYXRhIHdoaWNoIGlzIHBhc3NlZCB0byBgY29sb3JgIGFuZCBgZmlsbGAsIGFuZCB0aGV5IGhhdmUgdGhlaXIgb3duIHNwZWNpZmljIGN1c3RvbWl6YXRpb25zLgoKIyMjIENhdGVnb3JpY2FsIGNvbG9yIHNjYWxlcwpIZXJlIGlzIHRoZSBkZWZhdWx0IGNhdGVnb3JpY2FsIGNvbG9yIHNjYWxlLCB3aGljaCBpcyBjYWxsZWQgYHNjYWxlX1tmaWxsL2NvbG9yXV9odWUoKWAKCmBgYHtyIHRpZHkgPSBGLGZpZy53aWR0aCA9IDYvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yIH0KZ2dwbG90KElfamVhbiwgYWVzKFdvcmQsIER1cl9tc2VjLCBmaWxsID0gV29yZCkpKwogIHN0YXRfc3VtbWFyeShmdW4ueSA9IG1lYW4sIGdlb20gPSAiYmFyIikrCiAgc2NhbGVfZmlsbF9odWUoKQpgYGAKCkp1c3QgbGlrZSB0aGUgeCBhbmQgeSBzY2FsZXMsIHlvdSBjYW4gY2hhbmdlIHRoZSB0aXRsZSwgbGltaXRzLCBicmVha3MgYW5kIGxhYmVscyBvZiB0aGlzIHNjYWxlLCB3aGljaCB3aWxsIGNvbnRyb2wgaG93IGl0IGFwcGVhcnMgaW4gdGhlIGxlZ2VuZC4gSGVyZSdzIGFuIGlsbHVzdHJhdGl2ZSBleGFtcGxlLCB3aGljaCBhY3R1YWxseSBkZXRyYWN0cyBmcm9tIHRoZSBkZWZhdWx0CgpgYGB7ciB0aWR5ID0gRixmaWcud2lkdGggPSA2LzEuMiwgZmlnLmhlaWdodD01LzEuMiB9CmdncGxvdChJX2plYW4sIGFlcyhXb3JkLCBEdXJfbXNlYywgZmlsbCA9IFdvcmQpKSsKICBzdGF0X3N1bW1hcnkoZnVuLnkgPSBtZWFuLCBnZW9tID0gImJhciIpKwogIHNjYWxlX2ZpbGxfaHVlKG5hbWUgPSAiTGV4aWNhbCBJdGVtIiwKICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKCJJJ0QiLCJJJ1ZFIiwiSSdMTCIsIkknTSIsIkkiKSwKICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCInRCIsIidWRSIsIidMTCIsIidNIiwiIikpCmBgYAoKTWFueSBwZW9wbGUgZG9uJ3QgbGlrZSB0aGUgZGVmYXVsdCBjb2xvciBzY2hlbWUsIGJ1dCB0aGVyZSBhcmUgb3RoZXIgYXZhaWxhYmxlIGNvbG9yIHBhbGV0dGVzLCBhbmQgeW91IGNhbiBkZWZpbmUgeW91ciBvd24uIE9uZSByZWFsbHkgbmljZSBzZXQgb2YgY29sb3IgcGFsZXR0ZXMgY29tZXMgZnJvbSB0aGUgcGFja2FnZSBgUkNvbG9yQnJld2VyYC4gWW91IGNhbiBbZXhwbG9yZSB0aGUgc2V0IG9mIGF2YWlsYWJsZSBjb2xvciBwYWxldHRlcyBhdmFpbGFibGUgaW4gaXQgaGVyZV0oaHR0cDovL2NvbG9yYnJld2VyMi5vcmcvKS4gQSBwZXJzb25hbCBmYXZvcml0ZSBvZiBtaW5lIGlzIGNhbGxlZCBgU2V0MWAsIHdoaWNoIHlvdSBjYW4gYXBwbHkgdG8gdGhlIHBsb3Qgd2l0aCBgc2NhbGVfZmlsbF9icmV3ZXIoKWAKCmBgYHtyIHRpZHkgPSBGLGZpZy53aWR0aCA9IDYvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yIH0KZ2dwbG90KElfamVhbiwgYWVzKFdvcmQsIER1cl9tc2VjLCBmaWxsID0gV29yZCkpKwogIHN0YXRfc3VtbWFyeShmdW4ueSA9IG1lYW4sIGdlb20gPSAiYmFyIikrCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikKYGBgCgpJZiB5b3UgYXJlIHBhcnRpY3VsYXJseSBwaWNreSwgb3Igd2FudCB0byBleHByZXNzIHlvdXIgcXVlc3Rpb25hYmxlIGFlc3RoZXRpYyBzZW5zZSB0byB0aGUgd29ybGQsIHRoZXJlIGlzIGFsc28gYHNjYWxlX2ZpbGxfbWFudWFsKClgLCB3aGVyZSB5b3UgZGVmaW5lIGFuIGFyYml0cmFyeSBsaXN0IG9mIGNvbG9ycyB0byB1c2UgZm9yIHRoZSBzY2FsZS4KCmBgYHtyIHRpZHkgPSBGLGZpZy53aWR0aCA9IDYvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yIH0KZ2dwbG90KElfamVhbiwgYWVzKFdvcmQsIER1cl9tc2VjLCBmaWxsID0gV29yZCkpKwogIHN0YXRfc3VtbWFyeShmdW4ueSA9IG1lYW4sIGdlb20gPSAiYmFyIikrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoImJpc3F1ZSIsICJjaGFydHJldXNlNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhvdHBpbmsiLCJ5ZWxsb3ciLCAicmVkIikpCmBgYAoKQW5kIGZpbmFsbHksIGlmIHlvdSdyZSBwcmVwYXJpbmcgYSBwbG90IGZvciBwdWJsaWNhdGlvbiwgYW5kIHlvdSB3YW50IHRvIGJlIHN1cmUgdGhhdCB0aGUgY29sb3JzIHdpbGwgYmUgZGlzdGluZ3Vpc2hhYmxlIHdoZW4gcHJpbnRlZCBpbiBibGFjayBhbmQgd2hpdGUsIHRoZXJlIGlzIGBzY2FsZV9maWxsX2dyZXkoKWAuCgoKYGBge3IgdGlkeSA9IEYsZmlnLndpZHRoID0gNi8xLjIsIGZpZy5oZWlnaHQ9NS8xLjIgfQpnZ3Bsb3QoSV9qZWFuLCBhZXMoV29yZCwgRHVyX21zZWMsIGZpbGwgPSBXb3JkKSkrCiAgc3RhdF9zdW1tYXJ5KGZ1bi55ID0gbWVhbiwgZ2VvbSA9ICJiYXIiKSsKICBzY2FsZV9maWxsX2dyZXkoKQpgYGAKCiMjIyBHcmFkaWVudCBjb2xvciBzY2FsZXMKClRoZXJlIGFyZSBhbHNvIGEgbmljZSBzZXQgb2YgY3VzdG9taXphYmxlIGdyYWRpZW50IGNvbG9yIHNjYWxlcy4gSGVyZSdzIHRoZSBkZWZhdWx0IGdyYWRpZW50IGNvbG9yIHNjYWxlLCB3aGljaCBpcyBjYWxsZWQgYHNjYWxlX2ZpbGxfZ3JhZGllbnQoKWAKCmBgYHtyIHRpZHkgPSBGLCBmaWcud2lkdGg9Ni8xLjIsIGZpZy5oZWlnaHQgPSA1LzEuMn0KZ2dwbG90KElfamVhbiwgYWVzKC1GMi5uLCAtRjEubikpKwogIHN0YXRfZGVuc2l0eTJkKGdlb20gPSAidGlsZSIsCiAgICAgICAgICAgICAgICAgY29udG91ciA9IEYsIAogICAgICAgICAgICAgICAgIGFlcyhmaWxsID0gLi5kZW5zaXR5Li4pKSsKICBzY2FsZV9maWxsX2dyYWRpZW50KCkKYGBgCgoKYHNjYWxlX2ZpbGxfZ3JhZGllbnQoKWAgY29uc3RydWN0cyBhIGNvbG9yIGNvbnRpbnV1bSBmcm9tIEEgdG8gQiwgd2hlcmUgdGhlIGxvd2VzdCB2YWx1ZXMgaW4gdGhlIHBsb3Qgd2lsbCBoYXZlIHNvbGlkIEEsIGFuZCB0aGUgaGlnaGVzdCB2YWx1ZXMgaW4gdGhlIHBsb3Qgd2lsbCBoYXZlIHNvbGlkIEIsIGFuZCBldmVyeXRoaW5nIGVsc2Ugd2lsbCBmYWxsIGFsb25nIHRoZSBncmFkaWVudC4gSXQncyBwb3NzaWJsZSB0byBvdmVycmlkZSB0aGUgb3JpZ2luYWwgdHdvIGNvbG9ycyB0aGF0IHRoZSBncmFkaWVudCBpcyBidWlsdCBiZXR3ZWVuIGJ5IHBhc3NpbmcgdGhlIGNvbG9ycyB5b3UgcHJlZmVyIHRvIGBsb3dgIGFuZCBgaGlnaGAuCgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoPTYvMS4yLCBmaWcuaGVpZ2h0ID0gNS8xLjJ9CmdncGxvdChJX2plYW4sIGFlcygtRjIubiwgLUYxLm4pKSsKICBzdGF0X2RlbnNpdHkyZChnZW9tID0gInRpbGUiLAogICAgICAgICAgICAgICAgIGNvbnRvdXIgPSBGLCAKICAgICAgICAgICAgICAgICBhZXMoZmlsbCA9IC4uZGVuc2l0eS4uKSkrCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3c9ImRhcmtibHVlIixoaWdoPSJkYXJrcmVkIikKYGBgCgoKSXQncyBhbHNvIHBvc3NpYmxlIHRvIGRlZmluZSBhIGdyYWRpZW50IHRoYXQgcGFzc2VzIGZyb20gQSB0byBCIHZpYSBDIHdpdGggYHNjYWxlX2ZpbGxfZ3JhZGllbnQyKClgLiBIZXJlLCB5b3UgZGVmaW5lIGBsb3dgIGFuZCBgaGlnaGAsIGFzIHdlbGwgYXMgYSB0aGlyZCBjb2xvciB5b3Ugd2FudCB0aGUgZ3JhZGllbnQgdG8gdHJhbnNpdGlvbiB0aHJvdWdoLCBgbWlkYC4gWW91IGFsc28gbmVlZCB0byBkZWZpbmUgdGhlIHZhbHVlIHRoZSBzY2FsZSBzaG91bGQgdHJlYXQgYXMgYSBtaWRwb2ludC4gCgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoPTYvMS4yLCBmaWcuaGVpZ2h0ID0gNS8xLjJ9CmdncGxvdChJX2plYW4sIGFlcygtRjIubiwgLUYxLm4pKSsKICBzdGF0X2RlbnNpdHkyZChnZW9tID0gInRpbGUiLAogICAgICAgICAgICAgICAgIGNvbnRvdXIgPSBGLCAKICAgICAgICAgICAgICAgICBhZXMoZmlsbCA9IC4uZGVuc2l0eS4uKSkrCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIobG93PSJkYXJrYmx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgaGlnaD0iZGFya3JlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgbWlkPSJ3aGl0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgbWlkcG9pbnQ9MC41KQpgYGAKCkFuZCwgZmluYWxseSwgeW91J3JlIGFibGUgdG8gZGVmaW5lIGEgY29sb3IgZ3JhZGllbnQgcGFzc2luZyB0aHJvdWdoIGFueSBhcmJpdHJhcnkgY29sb3JzIHdpdGggYHNjYWxlX2ZpbGxfZ3JhZGllbnRuKClgLiBIZXJlJ3MgYSBwcmV0dHkgdWdseSBvbmU6CgoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aD02LzEuMiwgZmlnLmhlaWdodCA9IDUvMS4yfQpnZ3Bsb3QoSV9qZWFuLCBhZXMoLUYyLm4sIC1GMS5uKSkrCiAgc3RhdF9kZW5zaXR5MmQoZ2VvbSA9ICJ0aWxlIiwKICAgICAgICAgICAgICAgICBjb250b3VyID0gRiwgCiAgICAgICAgICAgICAgICAgYWVzKGZpbGwgPSAuLmRlbnNpdHkuLikpKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnMgPSBjKCJiaXNxdWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY2hhcnRyZXVzZTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJob3RwaW5rIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAieWVsbG93IikpCmBgYAoKQSBiZW5lZml0IHRvIGhhdmluZyBgc2NhbGVfZmlsbF9ncmFkaWVudG4oKWAgaXMgdGhhdCB5b3UgY2FuIHV0aWxpemUgc29tZSBvZiBSJ3MgYnVpbHQgaW4gY29sb3IgcGFsZXR0ZXMsIGxpa2UgYHJhaW5ib3coKWAsIGB0ZXJyYWluLmNvbG9ycygpYCBhbmQgYHRvcG8uY29sb3JzKClgIAoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aD02LzEuMiwgZmlnLmhlaWdodCA9IDUvMS4yfQpnZ3Bsb3QoSV9qZWFuLCBhZXMoLUYyLm4sIC1GMS5uKSkrCiAgc3RhdF9kZW5zaXR5MmQoZ2VvbSA9ICJ0aWxlIiwKICAgICAgICAgICAgICAgICBjb250b3VyID0gRiwgCiAgICAgICAgICAgICAgICAgYWVzKGZpbGwgPSAuLmRlbnNpdHkuLikpKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnMgPSByYWluYm93KDYpKQpgYGAKCgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoPTYvMS4yLCBmaWcuaGVpZ2h0ID0gNS8xLjJ9CmdncGxvdChJX2plYW4sIGFlcygtRjIubiwgLUYxLm4pKSsKICBzdGF0X2RlbnNpdHkyZChnZW9tID0gInRpbGUiLAogICAgICAgICAgICAgICAgIGNvbnRvdXIgPSBGLCAKICAgICAgICAgICAgICAgICBhZXMoZmlsbCA9IC4uZGVuc2l0eS4uKSkrCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IHRlcnJhaW4uY29sb3JzKDYpKQpgYGAKCgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoPTYvMS4yLCBmaWcuaGVpZ2h0ID0gNS8xLjJ9CmdncGxvdChJX2plYW4sIGFlcygtRjIubiwgLUYxLm4pKSsKICBzdGF0X2RlbnNpdHkyZChnZW9tID0gInRpbGUiLAogICAgICAgICAgICAgICAgIGNvbnRvdXIgPSBGLCAKICAgICAgICAgICAgICAgICBhZXMoZmlsbCA9IC4uZGVuc2l0eS4uKSkrCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IHRvcG8uY29sb3JzKDYpKQpgYGAKCgojIyMgR3VpZGVzCkEgc2V0IG9mIG5ldyBtZWNoYW5pY3MgZm9yIGhhbmRsaW5nIHRoZSBwcmVzZW50YXRpb24gb2YgbGVnZW5kcyB3YXMgaW50cm9kdWNlZCB3aXRoIHZlcnNpb24gMC45LjAsIGluY2x1ZGluZyBhIG5ldyBraW5kIG9mIGxlZ2VuZCBmb3IgY29udGludW91cyBjb2xvciBzY2FsZXM6IHRoZSBjb2xvcmJhci4gVGhlIHBsb3RzIGFib3ZlIGhhdmUgYWxsIHVzZWQgdGhlIGRlZmF1bHQgZ3VpZGUgdHlwZSAobGVnZW5kKSB3aGljaCBkaXNwbGF5cyB0aGUgY29sb3IgdmFsdWUgZm9yIHNwZWNpZmljIGJyZWFrcywgYnV0IG5vdCB0aGUgZ3JhZGllbnQgaW4gYmV0d2Vlbi4gVGhlIGNvbG9yIGJhciBndWlkZXMgc2hvdyB0aGUgZW50aXJlIGdyYWRpZW50LCB3aXRoIHRoZSBicmVha3MgbGFiZWxlZCBvdmVyIHRvcC4KCmBgYHtyIHRpZHkgPSBGLCBmaWcud2lkdGg9Ni8xLjIsIGZpZy5oZWlnaHQgPSA1LzEuMn0KZ2dwbG90KElfamVhbiwgYWVzKC1GMi5uLCAtRjEubikpKwogIHN0YXRfZGVuc2l0eTJkKGdlb20gPSAidGlsZSIsCiAgICAgICAgICAgICAgICAgY29udG91ciA9IEYsIAogICAgICAgICAgICAgICAgIGFlcyhmaWxsID0gLi5kZW5zaXR5Li4pKSsKICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzID0gcmFpbmJvdyg2KSwKICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9ICJjb2xvcmJhciIpCmBgYAoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aD02LzEuMiwgZmlnLmhlaWdodCA9IDUvMS4yfQpnZ3Bsb3QoSV9qZWFuLCBhZXMoLUYyLm4sIC1GMS5uKSkrCiAgc3RhdF9kZW5zaXR5MmQoZ2VvbSA9ICJ0aWxlIiwKICAgICAgICAgICAgICAgICBjb250b3VyID0gRiwgCiAgICAgICAgICAgICAgICAgYWVzKGZpbGwgPSAuLmRlbnNpdHkuLikpKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnMgPSByYWluYm93KDYpLAogICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gImxlZ2VuZCIpCmBgYAoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aD02LzEuMiwgZmlnLmhlaWdodCA9IDUvMS4yfQpnZ3Bsb3QoSV9qZWFuLCBhZXMoLUYyLm4sIC1GMS5uKSkrCiAgc3RhdF9kZW5zaXR5MmQoZ2VvbSA9ICJ0aWxlIiwKICAgICAgICAgICAgICAgICBjb250b3VyID0gRiwgCiAgICAgICAgICAgICAgICAgYWVzKGZpbGwgPSAuLmRlbnNpdHkuLikpKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnMgPSByYWluYm93KDYpLAogICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gIm5vbmUiKQpgYGAKCiMjIHNoYXBlIGFuZCBsaW5ldHlwZQpUaGUgc2hhcGUgYW5kIGxpbmV0eXBlIHNjYWxlcyBhcmUgbXVjaCBtb3JlIGxpbWl0ZWQuIERlc3BpdGUgdGhlIGFwcGFyZW50IGV4aXN0ZW5jZSBvZiBgc2NhbGVfc2hhcGVfY29udGludW91c2AgYW5kIGBzY2FsZV9saW5ldHlwZV9jb250aW51b3VzYCwgeW91IGNhbid0IGFjdHVhbGx5IHBhc3MgY29udGludW91cyB2YXJpYWJsZSB0byB0aGVzZSBhZXN0aGV0aWNzLiAKYGBge3J9CmFwcm9wb3MoIl5zY2FsZV9zaGFwZV8iKQpgYGAKCmBgYHtyfQphcHJvcG9zKCJec2NhbGVfbGluZXR5cGVfIikKYGBgCgpUaGUgb25seSB0aW1lIHlvdSdsbCBiZSBsaWtlbHkgdG8gdXNlIHRoZSBzaGFwZSBhbmQgbGluZXR5cGUgc2NhbGVzIGlzIHdoZW4geW91IHdhbnQgdG8gbWFudWFsbHkgY29udHJvbCB0aGUgcG9pbnQgc2hhcGUgYW5kIGxpbmV0eXBlcyB0byBiZSB1c2VkLiBJIGFjdHVhbGx5IGZpbmQgdGhlIGRlZmF1bHQgc2hhcGUgc2NhbGUgdG8gbm90IGJlIHN0cm9uZ2x5IGNvbnRyYXN0aXZlLCBhbmQgcHJlZmVyIHRvIGNvbnRyYXN0IHBvaW50IHR5cGVzIGJhc2VkIG9uIGZpbGxlZCB2cyBob2xsb3cgc2hhcGVzLgoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDgvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQpnZ3Bsb3QoSV9zdWJzZXQsIGFlcyhEdXJfbXNlYywgRjEubiwgc2hhcGUgPSBXb3JkKSkrCiAgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDgvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQpnZ3Bsb3QoSV9zdWJzZXQsIGFlcyhEdXJfbXNlYywgRjEubiwgc2hhcGUgPSBXb3JkKSkrCiAgZ2VvbV9wb2ludCgpKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9YygxLDEsIDE5LCAxOSkpCmBgYAoKCgoKIyMgT3RoZXIgc2NhbGVzCgpgYGB7cn0KYXByb3BvcygiXnNjYWxlX3NpemVfIikKYGBgCgoKYGBge3J9CmFwcm9wb3MoIl5zY2FsZV9hbHBoYV8iKQpgYGAKCiMgRmFjZXRpbmcKQSByZWFsbHkgcG93ZXJmdWwgZ3JhcGhpY2FsIHRlY2huaXF1ZSBpcyBbdGhlIHNtYWxsIG11bHRpcGxlXShodHRwOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1NtYWxsX211bHRpcGxlKSwgYW5kIGBnZ3Bsb3QyYCBhbGxvd3MgZm9yIGVhc3kgY3JlYXRpb24gb2Ygc21hbGwgbXVsdGlwbGVzIHZpYSBmYWNldGluZy4gTGV0J3MgY3JlYXRlIGFuIGFkZGl0aW9uYWwgY2F0ZWdvcmljYWwgdmFyaWFibGUgZm9yIHRoZSBlbnRpcmUgZGF0YSBzZXQgKHdlIGRpZCB0aGlzIGFscmVhZHkgZm9yIHRoZSBzdWJzZXQgZXhjbHVkaW5nICJJIikuCgpgYGB7cn0KICBJX2plYW4gPC0gSV9qZWFuICU+JQogICAgICAgICAgICAgICAgbXV0YXRlKER1cl9jYXQgPSBEdXJfbXNlYyA+IG1lYW4oRHVyX21zZWMpKQoKYGBgCgojIyBgZmFjZXRfd3JhcGAKSWYgd2Ugd2FudGVkIHRvIGNyZWF0ZSBhbiBGMiB4IEYxIHBsb3QgZm9yIGV2ZXJ5IHdvcmQsIHdlJ2Qgc3RhcnQgb3V0IGJ5IGNyZWF0aW5nIGEgc2ltcGxlIEYxIHggRjIgcGxvdDoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDgvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQpnZ3Bsb3QoSV9qZWFuLCBhZXMoLUYyLm4sIC1GMS5uICkpKwogIGdlb21fcG9pbnQoKQpgYGAKYW5kIHRoZW4gZmFjZXRpbmcgYnkgYFdvcmRgIHdpdGggYGZhY2V0X3dyYXAoKWAuCmBgYHtyIHRpZHkgPSBGLCBmaWcud2lkdGggPSA4LzEuMiwgZmlnLmhlaWdodD01LzEuMn0KZ2dwbG90KElfamVhbiwgYWVzKC1GMi5uLCAtRjEubiApKSsKICBnZW9tX3BvaW50KCkrCiAgZmFjZXRfd3JhcCh+V29yZCkKYGBgCgoKWW91IGNhbiBleGVyY2lzZSBzb21lIGNvbnRyb2wgYWJvdXQgdGhlIGxheW91dCBvZiBmYWNldHMgd2l0aCBgbmNvbGAgYW5kIGBucm93YC4gRm9yIGV4YW1wbGUuIGlmIHlvdSByZWFsbHkgb25seSB3YW50ZWQgdGhlcmUgdG8gYmUgMiBjb2x1bW5zIG9mIGZhY2V0cywgeW91IGNvdWxkIG1ha2UgdGhhdCBoYXBwZW4gd2l0aCBieSBwYXNzaW5nIGBuY29sPTJgIHRvIGBmYWNldF93cmFwKClgCgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoID0gOC8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CmdncGxvdChJX2plYW4sIGFlcygtRjIubiwgLUYxLm4gKSkrCiAgZ2VvbV9wb2ludCgpKwogIGZhY2V0X3dyYXAofldvcmQsIG5jb2wgPSAyKQpgYGAKCk9yLCBpZiB5b3Ugd2FudGVkIGFsbCB0aGUgZmFjZXRzIHRvIGJlIGxpbmVkIHVwIGluIG9uZSByb3csIHlvdSB3b3VsZCBwYXNzIGBucm93ID0gMWAuCmBgYHtyIHRpZHkgPSBGLCBmaWcud2lkdGggPSA4LzEuMiwgZmlnLmhlaWdodD01LzEuMn0KZ2dwbG90KElfamVhbiwgYWVzKC1GMi5uLCAtRjEubiApKSsKICBnZW9tX3BvaW50KCkrCiAgZmFjZXRfd3JhcCh+V29yZCwgbnJvdyA9IDEpCmBgYAoKQnkgZGVmYXVsdCwgdGhlIHJhbmdlcyBvZiB0aGUgYXhlcyBpbiBlYWNoIGZhY2V0IGFyZSBmaXhlZCB0byBiZSB0aGUgc2FtZSBhY3Jvc3MgYWxsIGZhY2V0cywgYW5kIHRoYXQgc2hvdWxkIGJlIGNoYW5nZWQgb25seSBpbiB2ZXJ5IGxpbWl0ZWQgY2lyY3Vtc3RhbmNlcy4gWW91IGNhbiBzZXQgdGhlIHggYXhpcywgeSBheGlzLCBvciBib3RoIHRvIGJlIGZyZWUgYnkgcGFzc2luZyB0aGUgZm9sbG93aW5nIGFyZ3VtZW50cyB0byBgc2NhbGVzYCBpbnNpZGUgb2YgYGZhY2V0X3dyYXAoKWAuCmBgYHtyIHRpZHkgPSBGLCBmaWcud2lkdGggPSA4LzEuMiwgZmlnLmhlaWdodD01LzEuMn0KIyMgSW5hZHZpc2FibGUKZ2dwbG90KElfamVhbiwgYWVzKC1GMi5uLCAtRjEubiApKSsKICBnZW9tX3BvaW50KCkrCiAgZmFjZXRfd3JhcCh+V29yZCwgc2NhbGVzID0gImZyZWVfeCIpCmBgYAoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDgvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQojIyBJbmFkdmlzYWJsZQpnZ3Bsb3QoSV9qZWFuLCBhZXMoLUYyLm4sIC1GMS5uICkpKwogIGdlb21fcG9pbnQoKSsKICBmYWNldF93cmFwKH5Xb3JkLCBzY2FsZXMgPSAiZnJlZV95IikKYGBgCgoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDgvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQojIyBJbmFkdmlzYWJsZQpnZ3Bsb3QoSV9qZWFuLCBhZXMoLUYyLm4sIC1GMS5uICkpKwogIGdlb21fcG9pbnQoKSsKICBmYWNldF93cmFwKH5Xb3JkLCBzY2FsZXMgPSAiZnJlZSIpCmBgYAoKIyMgYGZhY2V0X2dyaWRgCmBmYWNldF9ncmlkKClgIGlzIGFub3RoZXIgZm9ybSBvZiBmYWNldGluZyBpbiB0d28gZGltZW5zaW9ucy4gCgoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDgvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQpnZ3Bsb3QoSV9qZWFuLCBhZXMoLUYyLm4sIC1GMS5uICkpKwogIGdlb21fcG9pbnQoKSsKICBmYWNldF9ncmlkKER1cl9jYXR+V29yZCkKYGBgCgoKYGBge3IgdGlkeSA9IEYsIGZpZy53aWR0aCA9IDgvMS4yLCBmaWcuaGVpZ2h0PTUvMS4yfQpnZ3Bsb3QoSV9qZWFuLCBhZXMoLUYyLm4sIC1GMS5uICkpKwogIGdlb21fcG9pbnQoKSsKICBmYWNldF9ncmlkKFdvcmR+RHVyX2NhdCkKYGBgCgpgYGB7ciB0aWR5ID0gRiwgZmlnLndpZHRoID0gOC8xLjIsIGZpZy5oZWlnaHQ9NS8xLjJ9CmdncGxvdChJX2plYW4sIGFlcygtRjIubiwgLUYxLm4gKSkrCiAgZ2VvbV9wb2ludCgpKwogIGZhY2V0X2dyaWQoRHVyX2NhdH5Xb3JkLCBzY2FsZXMgPSAiZnJlZSIpCmBgYAoKCgoK