Preliminaries

Make sure to move to the proper working directory and that the file NYC_Sub_borough_Area.dbf is in that directory.

Also, activate the tidyverse and foreign packages using the library command (the tidyverse package includes ggplot2):

library(tidyverse)
library(foreign)

We will continue with the data for the NYC sub-boroughs. If you did not save the data frame from the previous lab, then read it in from NYC_Sub_borough_Area.dbf and add the manbronx variable if you don’t have it. If you saved the data frame using saveRDS, then you can load it directly using readRDS.

Below follow the commands to start from scratch (check the previous lab for explanations):

nyc.data <- read.dbf("NYC_Sub_borough_Area.dbf")
nyc.data <- as_tibble(nyc.data)
nyc.data <- nyc.data %>% mutate(manbronx = if_else((code > 300 & code < 311) | (code > 100 & code < 111),"Select","Rest"))
nyc.data

Spiffing up the Graph

So far, we have used the defaults for the various descriptive aspects of the graph, such as axis labels, title, etc. All these can be specified (in great detail). Only the very basics will be covered here, but the options and combinations are virtually endless.

To illustrate these features, we will continue to use the scatterplot of kids2000 on pubast00, with manbronx as a categorical variable to define subgroups.

Axis labels

The default setting for the axis labels is to use the variable name. Sometimes, this is not very informative. To set the labels explicitly, we use the xlab and ylab functions These are added in the same way as actual layers, using the + notation and with the respective options in parentheses (do not use an = sign).

For example, with our default scatter plot, we can set xlab("Percent HH with Children") and ylab("Percent Public Assistance") (note, the font and font size can be specified as well, but we don’t go that far).

As in the previous lab, we assign the main ggplot command to the object g to save us some typing.

g <- ggplot(data=nyc.data,aes(x=kids2000, y=pubast00))
g + geom_point() +
  geom_smooth() +
  xlab("Percent HH with Children") +
  ylab("Percent Public Assistance")

Title

A title is added to the graph by means of the ggtitle command. Again, enter the desired title in parentheses and enclosed by quotes. For example, we can add ggtitle("Example Scatter Plot") (we also keep the axis labels):

g + geom_point() +
  geom_smooth() +
  xlab("Percent HH with Children") +
  ylab("Percent Public Assistance") +
  ggtitle("Example Scatter Plot")

The default is to have the title left-aligned. Often, one may want it centered above the graph. Again, this can be customized. We can override the basic settings in the theme command. For example, we adjust the plot.title (of course, you need to know what everything is called). Specifically, we set the element_text property’s horizontal justification (hjust) to 0.5. Specifically, we use theme(plot.title = element_text(hjust = 0.5)). This centers the title. The number of other refinements is near infinite and beyond our scope at this point.

g + geom_point() +
  geom_smooth() +
  xlab("Percent HH with Children") +
  ylab("Percent Public Assistance") +
  ggtitle("Example Scatter Plot") +
  theme(plot.title = element_text(hjust = 0.5))

Legend

The default legend title is the variable name, in our previous examples, that was manbronx. To set the legend title explicitly, we need the labs command. For example, we set aes(color=manbronx) for the points and the linear smoother (method="lm"), and the legend to labs(color="Selection").

g + geom_point(aes(color=manbronx)) +
  geom_smooth(aes(color=manbronx),method="lm",se=FALSE) +
  xlab("Percent HH with Children") +
  ylab("Percent Public Assistance") +
  ggtitle("Example Scatter Plot") +
  theme(plot.title = element_text(hjust = 0.5)) +
  labs(color="Selection")

Theme

Every graph has a theme, which sets the main parameters for its appearance. The default theme with the grey grids, separated by white lines is theme_grey( ). If we want to change this, we can specify one of the other themes. For example, a classic graph a la base R plot, without background shading or grid lines is theme_classic( ). In order to obtain this specialized look, we set the associated theme command.

g + geom_point(aes(color=manbronx)) +
  geom_smooth(aes(color=manbronx),method="lm",se=FALSE) +
  xlab("Percent HH with Children") +
  ylab("Percent Public Assistance") +
  ggtitle("Example Scatter Plot") +
  labs(color="Selection") +
  theme_classic()

There are seven built-in themes as well as several contributed ones. Another built-in example is theme_minimal( ), shown next.

g + geom_point(aes(color=manbronx)) +
  geom_smooth(aes(color=manbronx),method="lm",se=FALSE) +
  xlab("Percent HH with Children") +
  ylab("Percent Public Assistance") +
  ggtitle("Example Scatter Plot") +
  labs(color="Selection") +
  theme_minimal()

In addition, the package ggthemes contains several additional themes that look extremely professional.

You will have to install the package first, if you don’t have it. Once installed, invoke it with the library command.

library(ggthemes)

Then, for example, using theme_tufte( ):

g + geom_point(aes(color=manbronx)) +
  geom_smooth(aes(color=manbronx),method="lm",se=FALSE) +
  xlab("Percent HH with Children") +
  ylab("Percent Public Assistance") +
  ggtitle("Example Scatter Plot") +
  labs(color="Selection") +
  theme_tufte()

Each of the themes has specific options for further customization, so that the possibilities are nearly limitless.

Conditional Plots

Conditional plots are a major feature of the functionality of ggplot, where they are referred to as facetting, or small multiples. This is implemented in the facet_wrap and facet_grid functions. The difference between the two is that facet_wrap is based on a single conditioning variable (so, essentially one-dimensional), whereas facet_grid typically has two conditioning variables (as a grid of sub-plots).

In ggplot, the conditioning is based on a categorical variable that needs to be available in the data set. The facetting formula does not evaluate functions, so the conditioning categories need to be computed beforehand. In our case, we already have the manbronx variable.

Facet Wrap

The facet_wrap function creates multiple graphs for subsets of the data as determined by a conditioning variable. There are several options, such as explicitly setting the number of rows and columns, but we still stick with the bare defaults to illustrate the principle.

For example, to condition on manbronx, we use facet_wrap(~ manbronx).

g +
  geom_point() +
  geom_smooth(method="lm") +
  facet_wrap(~manbronx)

Rearranging the facet graphs

The facet_wrap function has several options to explicitly set the number of rows or columns in which the subgraphs should be displayed. For example, using nrow = 2 arranges the graphs vertically.

g +
  geom_point() +
  geom_smooth(method="lm") +
  facet_wrap(~manbronx,nrow=2)

Facet Grid

The main difference between the two facetting approaches is that facet_grid is explicitly two-dimensional. This requires two variables to set the conditions, one of the vertical dimension (y) and one for the horizontal dimension (x).

As mentioned earlier, in ggplot, the conditioning is based on a categorical variable that needs to be available in the data set. In our example, we only have one, so we will need to create a second one.

There are three so-called helper functions to make this easy: cut_interval, cut_width, and cut_number. For example, with cut_number we pass the variable, e.g., hhziz00, and the number of categories, say n = 2. This creates the new variable as an R factor, giving the intervals that resulted from the cut. Note that the variable needs to be accessed in the usual way, using [[ ]] or $ and assigned back to the data frame (this is not a mutate command, although you could create a factor that way as well).

For example, we create a new variable cut.hhsiz using a quantile classification with two categories (the variable will be split on the median value), by setting n=2. We need to use the $ notation to ensure that the new variable is added to the relevant data set. Since we only have 55 observations, we can easily list the full set of values to verify. Internally, they are stored as factors (hence, the summary of the Levels at the end of the listing).

nyc.data$cut.hhsiz <- cut_number(nyc.data$hhsiz00,n=2)
nyc.data$cut.hhsiz
 [1] (2.72,3.2]  [1.57,2.72] (2.72,3.2]  [1.57,2.72] [1.57,2.72] (2.72,3.2]  (2.72,3.2]  [1.57,2.72] [1.57,2.72]
[10] [1.57,2.72] [1.57,2.72] (2.72,3.2]  (2.72,3.2]  [1.57,2.72] (2.72,3.2]  (2.72,3.2]  [1.57,2.72] [1.57,2.72]
[19] [1.57,2.72] [1.57,2.72] [1.57,2.72] [1.57,2.72] [1.57,2.72] [1.57,2.72] [1.57,2.72] [1.57,2.72] (2.72,3.2] 
[28] (2.72,3.2]  (2.72,3.2]  (2.72,3.2]  (2.72,3.2]  (2.72,3.2]  [1.57,2.72] (2.72,3.2]  [1.57,2.72] [1.57,2.72]
[37] (2.72,3.2]  (2.72,3.2]  (2.72,3.2]  (2.72,3.2]  [1.57,2.72] [1.57,2.72] [1.57,2.72] (2.72,3.2]  (2.72,3.2] 
[46] (2.72,3.2]  (2.72,3.2]  (2.72,3.2]  (2.72,3.2]  [1.57,2.72] (2.72,3.2]  (2.72,3.2]  [1.57,2.72] [1.57,2.72]
[55] [1.57,2.72]
Levels: [1.57,2.72] (2.72,3.2]

We are now ready to create a 2 by 2 grid of sub-plots using the cut.hhsiz variable to condition on the vertical axis (resulting in different rows of graphs) and manbronx to condition on the horizontal axis (resulting in different columns of graphs). We set facet_grid(cut.hhsiz ~ manbronx) after the commands for the scatter plot and linear smooth.

Note that you need to spell out the full ggplot command, since the object g does not contain the new variable we created. Alternatively, you can re-define g.

You can change the labels, add a title, etc., but we will skip that from now on to just concentrate on the graphs.

ggplot(data=nyc.data,aes(x=kids2000, y=pubast00)) +
  geom_point() +
  geom_smooth(method="lm",se=FALSE) +
  facet_grid(cut.hhsiz ~ manbronx)

Histogram

Default Histogram

We start with the simple histogram command for the kids2009 variable.

The geom for a histogram is geom_histogram. In contrast to most plots in ggplot, only one variable needs to be passed. The general setup for ggplot is to think of the graph as a two-dimensional representation, with the x variable for the x axis and the y variable for the y-axis. In a histogram, the vertical axis is by default taken to be the count of the observations in each bin.

The three pieces we need to create the plot are the data set (data), nyc.data, the aesthetic (aes), kids2009 (only x by default, with y as the count), and the geom, geom_histogram. The command is as follows, with all the other settings left to their default:

ggplot(data=nyc.data,aes(kids2009)) +
  geom_histogram()

Adjusting the number of bins

The graph gives a warning that the default number of 30 bins is inappropriate. The standard way in ggplot is to adjust the number of bins indirectly, by means of the binwidth option, i.e., the range of values that make up a bin, in the units of the variable under consideration. Instead, I prefer to use the option bins, which sets the number of bins directly (the bin width is then obtained by dividing the range by the number of bins).

For example, to set the number of bins to 7, we set bins=7 as an option to geom_histogram.

ggplot(data=nyc.data,aes(kids2009)) +
  geom_histogram(bins=7)

Frequency on vertical axis

As mentioned, the default is to give the number of observations in each bin (the count) as the value on the vertical axis. In order to obtain the frequency on the vertical axis, the y variable needs to be set to ..density.., as in aes(x= kids2009, y = ..density..). In all other respects, the histogram is the same. Here, we illustrate the use of bindwidth by passing binwidth=5 as an option to geom_histogram.

ggplot(data=nyc.data,aes(kids2009,y=..density..)) +
  geom_histogram(binwidth=5)

By group

As before, we can now create a distinction between the distribution for the two subsets in the data determined by the manbronx variable. First, we set the aesthetic color equal to this variable as aes(color=manbronx) in the geom_histogram command.

ggplot(data=nyc.data,aes(kids2009)) +
  geom_histogram(aes(color=manbronx),bins=7)

The result is not exactly what we had in mind. The reason is that color determines the outline of the graph, but not the internal color, or fill. Instead, with geom_histogram(aes(fill=manbronx)), we get the bars stacked on top of each other with a different color for each subset.

ggplot(data=nyc.data,aes(kids2009)) +
  geom_histogram(aes(fill=manbronx),bins=7)

Still not that great at showing the distinction between the two subsets. Now, we resort to facet_wrap to create a separate histogram for each subset. We highlight the distinction even more by setting the fill color to manbronx, as we just did. The facet_wrap( ~ manbronx) will yield two histograms, in a different color, and with a legend.

ggplot(data=nyc.data,aes(kids2009)) +
  geom_histogram(aes(fill=manbronx),bins=7) +
  facet_wrap(~ manbronx)

You can now spiff up these graphs with axis labels, a title, a label for the legend, and even a custom theme.

Frequency Polygon

Another common plot to depict a univariate distribution is the frequency polygon, obtained with geom_freqpoly. It is essentically the same as a histogram, but uses a set of connected points instead of the bars for the histogram. Again, we have to specify a binwidth. A bare bones example is given below, using the same commands as for the histogram, but with geom_freqpoly.

ggplot(data=nyc.data,aes(kids2009)) +
  geom_freqpoly(binwidth=5)

With the frequency polygon it is very easy to show the distribution for different subgroups in the data, for example, by setting aes(color=manbronx) (instead of using facet_wrap). We use bins=7 instead of binwidth to highlight the contrast with a histogram with the same options.

ggplot(data=nyc.data,aes(kids2009)) +
  geom_freqpoly(aes(color=manbronx),bins=7)

Box Plot

The box plot, also referred to as Tukey’s box and whisker plot, is an alternative way to visualize the distribution of a single variable, with a focus on descriptive statistics such as quartiles and the median. The corresponding geom is geom_boxplot. We continue our example using the kids2009 variable. We first consider the default option, then move on to illustrate a few optional settings.

Default Settings

The minimal arguments to create a boxplot are the data set and the x and y variables passed to aes. As mentioned above, the logic behind the graphs in ggplot is two-dimensional, so both x and y need to be specified. The x variable is used to create separate box plots for different subsets of the data. In our simple example, we don’t need this feature, so we set the x variable to empty, i.e., " “. The y variable is the actual variable of interest, kids2009. The resulting graph is shown below.

ggplot(data=nyc.data,aes(x="",y=kids2009)) +
  geom_boxplot()

Note the outlier shown as a dot (point) at the bottom of the graph. The box shows the first quartile, the median, and the third quartile. The lines connect the upper and lower fences, and any outliers are shown as distinct points.

By group

The design of the box plot in ggplot is really geared to show the distribution for different subgroups. Each subgroup gets a box plot, arranged side by side for easy comparison.

For example, we now set `aes(x=manbronx,y=kids2009) to obtain two box plots.

ggplot(data=nyc.data,aes(x=manbronx,y=kids2009)) +
  geom_boxplot()

Note how the two categories are listed as labels on the x-axis. Compare this to using facet_wrap( ~ manbronx) with x=" " in the main ggplot command.

ggplot(data=nyc.data,aes(x="",y=kids2009)) +
  geom_boxplot() +
  facet_wrap( ~ manbronx)

It is the same graph, but now the x-axis label is x (as in the generic box plot), and the categories are listed at the top of each graph.

Bells and whistles

As is, the default box plot is pretty rudimentary. We will illustrate the power of ggplot by adding a number of features to the plot in order provide further information. First, add a title using ggtitle. As we did for the histogram, we will center the title over the graph. We keep the x-variable set to manbronx to obtain two box plots and set the label for the y-axis to “Percent HH with Children”.

ggplot(data=nyc.data,aes(x=manbronx,y=kids2009)) +
  geom_boxplot() +
  ylab("Percent HH with Children") +
  ggtitle("Example Box Plot") +
  theme(plot.title = element_text(hjust=0.5))

Next, we want to give the box plot a color, following the variable manbronx. This is accomplished with the color (for the outlines) and fill (for the inside of the box) options to aes for geom_boxplot. We add the legend label as Selection as we did before. However, because we now have two features by subset, we need to specify the legend for both, as labs(color="Selection",fill="Selection").

Also, the box plots do not show the fences as separate lines, the way they do in many other software packages, e.g., as in GeoDa. This can be remedied, but not quite in the same way as in GeoDa. In ggplot, the fences are drawn at the location of the extreme values, the ymin and ymax, and not at the location of the fence cut-off values, as in GeoDa. The fences are obtained from the stat_boxplot function, by passing the geom as errorbar.

One final refinement. In GeoDa, the box plot also shows the locations of the actual observations as points on the central axis. We obtain the same effect by adding geom_point with color blue. We draw the points first, and the box plot on top of it, using the layers logic. However, we want to make sure that the central box doesn’t mask the points, which it does when the transparency is kept as the default. To accomplish this, we set the alpha level for both points and box plot at 0.5.

The result is now a quite fancy graph that provides a lot of information on the relative distribution of the percentage households with children in the two subsets of the data. This level of refinement comes at the expense of having to specify eight lines of code.

ggplot(data=nyc.data,aes(x=manbronx,y=kids2009)) +
  geom_point(color="blue",alpha=0.5) +
  geom_boxplot(aes(color=manbronx,fill=manbronx),alpha=0.5) +
  stat_boxplot(geom="errorbar") +
  ylab("Percent HH with Children") +
  labs(color="Selection",fill="Selection") +
  ggtitle("Example Box Plot") +
  theme(plot.title = element_text(hjust=0.5))

Violin Plot

A violin plot is a hybrid of a box plot and a density plot, showing regions of the data where more observations are found. The basic setup is the same as for a box plot (i.e., setting x = "" for a single plot), but now using the geom geom_violin.

ggplot(data=nyc.data,aes(x="",y=kids2009)) +
  geom_violin()

Now, again, with some bells and whistles. We show the violin plot separately for each subset by specifying aes(x=manbronx,y=kids2009) in the main ggplot command. Then we plot the individual points using geom_point with color="blue" and alpha=0.5. We set the color and fill for the geom_violin to manbronx, just as we did for the box plot. Finally, we finish with setting the y-axis label, the legend label and the title.

ggplot(data=nyc.data,aes(x=manbronx,y=kids2009)) +
  geom_point(color="blue",alpha=0.5) +
  geom_violin(aes(color=manbronx,fill=manbronx),alpha=0.5) +
  ylab("Percent HH with Children") +
  labs(color="Selection",fill="Selection") +
  ggtitle("Example Violin Plot") +
  theme(plot.title = element_text(hjust=0.5))

Tidy Data and Graphs

Often, the data are not in a format that allows for us to create the graphs that we want. For example, what if we wanted to show, for each neighborhood, how the stability of the neighborhood (the years households resided in the neighborhood) changed over the years. We have this information in three variables, i.e., yrhom02, yrhom05, yrhom08. But how could we show these for each neighborhood, when the aesthetics want us to define one x and one y variable. The reason this doesn’t work is that the data set is not in a tidy format, where each row pertains to a single data point. In our case, that means we need both the neighborhood and the year for the variable yrhom, something that is called a panel data format (both space and time).

I will briefly illustrate some of the features of the tidyverse, specifically the operation to gather observations such as different columns are turned into rows.

To keep things simple, we first create a new data frame stable, which is a subset of the variables: code, name, yrhom02, yrhom05, yrhom08, and manbronx. We use the pipe and the select command. To check the result, we print the contents of the new data frame.

stable <- nyc.data %>% select(code,name,yrhom02,yrhom05,yrhom08,manbronx)
stable

The new data frame only contains information on yrhom, the median number of years households lived in the neighborhood. So, really, the different yrhomxx variables pertain to different years of the same thing. To simplify matters, and to illustrate how to deal with non-standard variables in the tidyverse, we will use rename to turn the three variable names into the respective years. Because the years are non-standard variable names, we need to put them in quotes, as `2002`, etc. We use the pipe to rename the three variables, and print the data frame to check the result.

stable <- stable %>% rename(`2002`=yrhom02,`2005`=yrhom05,`2008`=yrhom08)
stable

Now, we illustrate the very powerful (but not always easy to understand) gather function. What we want to do is to create a new variable to designate the observations for different years. Those years are 2002, 2005, and 2008. Right now, these are separate columns with the information on neighborhood stability for each neighborhood in the respective year. What we want is a data frame where each observation has both a neighborhood and a year, and a value for the stability (i.e., the median year households lived there). To do this by hand, we would have to copy the neighborhood code, name and manbronx classification, and then manually enter the year and the corresponding value for yrhom. For each neighborhood, we would have three rows, one for each year. In addition to the neighborhood code, name and manbronx classification, we would have two extra columns: one with the year, and one with the value for the stability (years lived in neighborhood).

In a tidy data frame, the name for the column with the years (i.e., the observations we are gathering) is called a key. We have to give this a name that makes sense, e.g., key=year. The values for the observations on year will be the column headings of the variables we want to gather, i.e., the three years 2002, 2005 and 2008. The values that we are interested in are the number of years resided in the neighborhood, but these don’t currently have a variable name. They are contained under the 2002, 2005 and 2008 variable headings. The name for this new variable is called a value. Again, we have to specify a name for it, e.g., value=stable. So now, we have two pieces: we have the name for the new variable that will label the extra observations by year (the key), and we have a name for the variable that will contain the relevant values (the value). Now, we still need to specify where those values will come from. These are the three columns 2002, 2005, and 2008.

The command is gather(key=year,value=stable,2002,2005,2008). What this does is create a new column for year, a new column for stable, it assigns the values of 2002, 2005 and 2008 to year, and takes the matching values and assigns them to the corresponding row for the column stable.

Let’s again use the pipe and assign the result to a new data frame stneigh.

stneigh <- stable %>% gather(key=year,value=stable,`2002`,`2005`,`2008`)
stneigh

We can now create a plot of the value of stable for each of the three years in each of the neighborhoods. One way (but not a very pretty one) is to do this as a scatter plot using geom_point with x=stable and y=name. For example, using theme_classic() and giving the points a different color for each year.

ggplot(data=stneigh,aes(x=stable,y=name)) +
  geom_point(aes(color=year)) +
  theme_classic()

This can be much improved upon, but I leave that as a so-called exercise.

Alternatively, it is now very easy to create box plot for each year next to each other, by setting x=year and y=stable for the box plot command.

ggplot(data=stneigh,aes(x=year,y=stable)) +
  geom_boxplot()

This is just a simple example of how gather can be used to turn a so-called wide data set into a narrow form. Similarly, the command spread can be used to do the reverse. That is beyond our current scope, but check the documentation for details.

LS0tCnRpdGxlOiAiU3RhdGlzdGljYWwgR3JhcGhzICgyKSIKYXV0aG9yOiAiTHVjIEFuc2VsaW4iCmRhdGU6ICIxMS8wNC8yMDE4IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyBQcmVsaW1pbmFyaWVzCgpNYWtlIHN1cmUgdG8gbW92ZSB0byB0aGUgcHJvcGVyIHdvcmtpbmcgZGlyZWN0b3J5IGFuZCB0aGF0IHRoZSBmaWxlIAoqKk5ZQ19TdWJfYm9yb3VnaF9BcmVhLmRiZioqIGlzIGluIHRoYXQgZGlyZWN0b3J5LgoKQWxzbywgYWN0aXZhdGUgdGhlICoqdGlkeXZlcnNlKiogYW5kICoqZm9yZWlnbioqIHBhY2thZ2VzIHVzaW5nIHRoZSBgbGlicmFyeWAgY29tbWFuZAoodGhlICoqdGlkeXZlcnNlKiogcGFja2FnZSBpbmNsdWRlcyAqKmdncGxvdDIqKik6CgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZm9yZWlnbikKYGBgCgpXZSB3aWxsIGNvbnRpbnVlIHdpdGggdGhlIGRhdGEgZm9yIHRoZSBOWUMgc3ViLWJvcm91Z2hzLiBJZiB5b3UgZGlkIG5vdCBzYXZlIHRoZSBkYXRhIGZyYW1lIGZyb20gdGhlIHByZXZpb3VzCmxhYiwgdGhlbiByZWFkIGl0IGluIGZyb20gKipOWUNfU3ViX2Jvcm91Z2hfQXJlYS5kYmYqKiAgYW5kICBhZGQgdGhlICoqbWFuYnJvbngqKiB2YXJpYWJsZSBpZiB5b3UgZG9uJ3QgaGF2ZSBpdC4KSWYgeW91IHNhdmVkIHRoZSBkYXRhIGZyYW1lIHVzaW5nIGBzYXZlUkRTYCwgdGhlbiB5b3UgY2FuIGxvYWQgaXQgZGlyZWN0bHkgdXNpbmcgYHJlYWRSRFNgLgoKQmVsb3cgZm9sbG93IHRoZSBjb21tYW5kcyB0byBzdGFydCBmcm9tIHNjcmF0Y2ggKGNoZWNrIHRoZSBwcmV2aW91cyBsYWIgZm9yIGV4cGxhbmF0aW9ucyk6CgpgYGB7cn0KbnljLmRhdGEgPC0gcmVhZC5kYmYoIk5ZQ19TdWJfYm9yb3VnaF9BcmVhLmRiZiIpCm55Yy5kYXRhIDwtIGFzX3RpYmJsZShueWMuZGF0YSkKbnljLmRhdGEgPC0gbnljLmRhdGEgJT4lIG11dGF0ZShtYW5icm9ueCA9IGlmX2Vsc2UoKGNvZGUgPiAzMDAgJiBjb2RlIDwgMzExKSB8IChjb2RlID4gMTAwICYgY29kZSA8IDExMSksIlNlbGVjdCIsIlJlc3QiKSkKbnljLmRhdGEKYGBgCgojIyBTcGlmZmluZyB1cCB0aGUgR3JhcGgKClNvIGZhciwgd2UgaGF2ZSB1c2VkIHRoZSBkZWZhdWx0cyBmb3IgdGhlIHZhcmlvdXMgZGVzY3JpcHRpdmUgYXNwZWN0cyBvZiB0aGUgZ3JhcGgsCnN1Y2ggYXMgYXhpcyBsYWJlbHMsIHRpdGxlLCBldGMuIEFsbCB0aGVzZSBjYW4gYmUgc3BlY2lmaWVkIChpbiBncmVhdCBkZXRhaWwpLiBPbmx5CnRoZSB2ZXJ5IGJhc2ljcyB3aWxsIGJlIGNvdmVyZWQgaGVyZSwgYnV0IHRoZSBvcHRpb25zIGFuZCBjb21iaW5hdGlvbnMgYXJlIHZpcnR1YWxseQplbmRsZXNzLgoKVG8gaWxsdXN0cmF0ZSB0aGVzZSBmZWF0dXJlcywgd2Ugd2lsbCBjb250aW51ZSB0byB1c2UgdGhlIHNjYXR0ZXJwbG90IG9mICoqa2lkczIwMDAqKiBvbgoqKnB1YmFzdDAwKiosIHdpdGggKiptYW5icm9ueCoqIGFzIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgdG8gZGVmaW5lIHN1Ymdyb3Vwcy4KCiMjIyBBeGlzIGxhYmVscwpUaGUgZGVmYXVsdCBzZXR0aW5nIGZvciB0aGUgYXhpcyBsYWJlbHMgaXMgdG8gdXNlIHRoZSB2YXJpYWJsZSBuYW1lLiBTb21ldGltZXMsIHRoaXMgaXMKbm90IHZlcnkgaW5mb3JtYXRpdmUuIFRvIHNldCB0aGUgbGFiZWxzIGV4cGxpY2l0bHksIHdlIHVzZSB0aGUgYHhsYWJgIGFuZCBgeWxhYmAgZnVuY3Rpb25zClRoZXNlIGFyZSBhZGRlZCBpbiB0aGUgc2FtZSB3YXkgYXMgYWN0dWFsIGxheWVycywgdXNpbmcgdGhlICsgbm90YXRpb24gYW5kIHdpdGgKdGhlIHJlc3BlY3RpdmUgb3B0aW9ucyBpbiBwYXJlbnRoZXNlcyAoZG8gbm90IHVzZSBhbiA9IHNpZ24pLgoKRm9yIGV4YW1wbGUsIHdpdGggb3VyIGRlZmF1bHQgc2NhdHRlciBwbG90LCB3ZSBjYW4gc2V0IGB4bGFiKCJQZXJjZW50IEhIIHdpdGggQ2hpbGRyZW4iKWAKYW5kIGB5bGFiKCJQZXJjZW50IFB1YmxpYyBBc3Npc3RhbmNlIilgIChub3RlLCB0aGUgZm9udCBhbmQgZm9udCBzaXplIGNhbiBiZSBzcGVjaWZpZWQKYXMgd2VsbCwgYnV0IHdlIGRvbid0IGdvIHRoYXQgZmFyKS4KCkFzIGluIHRoZSBwcmV2aW91cyBsYWIsIHdlIGFzc2lnbiB0aGUgbWFpbiBgZ2dwbG90YCBjb21tYW5kIHRvIHRoZSBvYmplY3QgKipnKiogdG8gc2F2ZQp1cyBzb21lIHR5cGluZy4KCmBgYHtyfQpnIDwtIGdncGxvdChkYXRhPW55Yy5kYXRhLGFlcyh4PWtpZHMyMDAwLCB5PXB1YmFzdDAwKSkKYGBgCgpgYGB7cn0KZyArIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoKSArCiAgeGxhYigiUGVyY2VudCBISCB3aXRoIENoaWxkcmVuIikgKwogIHlsYWIoIlBlcmNlbnQgUHVibGljIEFzc2lzdGFuY2UiKQpgYGAKCiMjIyBUaXRsZQoKQSB0aXRsZSBpcyBhZGRlZCB0byB0aGUgZ3JhcGggYnkgbWVhbnMgb2YgdGhlIGBnZ3RpdGxlYCBjb21tYW5kLiBBZ2FpbiwgZW50ZXIgdGhlIGRlc2lyZWQKdGl0bGUgaW4gcGFyZW50aGVzZXMgYW5kIGVuY2xvc2VkIGJ5IHF1b3Rlcy4gRm9yIGV4YW1wbGUsIHdlIGNhbiBhZGQgYGdndGl0bGUoIkV4YW1wbGUgU2NhdHRlciBQbG90IilgCih3ZSBhbHNvIGtlZXAgdGhlIGF4aXMgbGFiZWxzKToKCmBgYHtyfQpnICsgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aCgpICsKICB4bGFiKCJQZXJjZW50IEhIIHdpdGggQ2hpbGRyZW4iKSArCiAgeWxhYigiUGVyY2VudCBQdWJsaWMgQXNzaXN0YW5jZSIpICsKICBnZ3RpdGxlKCJFeGFtcGxlIFNjYXR0ZXIgUGxvdCIpCmBgYAoKVGhlIGRlZmF1bHQgaXMgdG8gaGF2ZSB0aGUgdGl0bGUgbGVmdC1hbGlnbmVkLiBPZnRlbiwgb25lIG1heSB3YW50IGl0IGNlbnRlcmVkIGFib3ZlIHRoZSBncmFwaC4KQWdhaW4sIHRoaXMgY2FuIGJlIGN1c3RvbWl6ZWQuIFdlIGNhbiAKb3ZlcnJpZGUgdGhlIGJhc2ljIHNldHRpbmdzIGluIHRoZSBgdGhlbWVgIGNvbW1hbmQuIApGb3IgZXhhbXBsZSwgd2UgYWRqdXN0IHRoZSBgcGxvdC50aXRsZWAgKG9mIGNvdXJzZSwgeW91IG5lZWQgdG8ga25vdwp3aGF0IGV2ZXJ5dGhpbmcgaXMgY2FsbGVkKS4gU3BlY2lmaWNhbGx5LCB3ZSBzZXQgdGhlIGBlbGVtZW50X3RleHRgIHByb3BlcnR5J3MKaG9yaXpvbnRhbCBqdXN0aWZpY2F0aW9uIChgaGp1c3RgKSB0byAwLjUuIFNwZWNpZmljYWxseSwgd2UKdXNlIGB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSlgLiBUaGlzIGNlbnRlcnMgdGhlIHRpdGxlLiBUaGUgbnVtYmVyCm9mIG90aGVyIHJlZmluZW1lbnRzIGlzIG5lYXIgaW5maW5pdGUgYW5kIGJleW9uZCBvdXIgc2NvcGUgYXQgdGhpcyBwb2ludC4KCmBgYHtyfQpnICsgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aCgpICsKICB4bGFiKCJQZXJjZW50IEhIIHdpdGggQ2hpbGRyZW4iKSArCiAgeWxhYigiUGVyY2VudCBQdWJsaWMgQXNzaXN0YW5jZSIpICsKICBnZ3RpdGxlKCJFeGFtcGxlIFNjYXR0ZXIgUGxvdCIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKYGBgCgoKIyMjIExlZ2VuZAoKVGhlIGRlZmF1bHQgbGVnZW5kIHRpdGxlIGlzIHRoZSB2YXJpYWJsZSBuYW1lLCBpbiBvdXIgcHJldmlvdXMgZXhhbXBsZXMsIHRoYXQgd2FzICoqbWFuYnJvbngqKi4gVG8Kc2V0IHRoZSBsZWdlbmQgdGl0bGUgZXhwbGljaXRseSwgd2UgIG5lZWQgdGhlIGBsYWJzYCBjb21tYW5kLiBGb3IgZXhhbXBsZSwgd2Ugc2V0IGBhZXMoY29sb3I9bWFuYnJvbngpYApmb3IgdGhlIHBvaW50cyBhbmQgdGhlIGxpbmVhciBzbW9vdGhlciAoYG1ldGhvZD0ibG0iYCksIGFuZCB0aGUgbGVnZW5kIHRvIGBsYWJzKGNvbG9yPSJTZWxlY3Rpb24iKWAuCgpgYGB7cn0KZyArIGdlb21fcG9pbnQoYWVzKGNvbG9yPW1hbmJyb254KSkgKwogIGdlb21fc21vb3RoKGFlcyhjb2xvcj1tYW5icm9ueCksbWV0aG9kPSJsbSIsc2U9RkFMU0UpICsKICB4bGFiKCJQZXJjZW50IEhIIHdpdGggQ2hpbGRyZW4iKSArCiAgeWxhYigiUGVyY2VudCBQdWJsaWMgQXNzaXN0YW5jZSIpICsKICBnZ3RpdGxlKCJFeGFtcGxlIFNjYXR0ZXIgUGxvdCIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKwogIGxhYnMoY29sb3I9IlNlbGVjdGlvbiIpCmBgYAoKIyMjIFRoZW1lCgpFdmVyeSBncmFwaCBoYXMgYSB0aGVtZSwgd2hpY2ggc2V0cyB0aGUgbWFpbiBwYXJhbWV0ZXJzIGZvciBpdHMgYXBwZWFyYW5jZS4KVGhlIGRlZmF1bHQgdGhlbWUgd2l0aCB0aGUgZ3JleSBncmlkcywgc2VwYXJhdGVkIGJ5IHdoaXRlIGxpbmVzIGlzCmB0aGVtZV9ncmV5KCApYC4gSWYgd2Ugd2FudCB0byBjaGFuZ2UgdGhpcywgd2UgY2FuIHNwZWNpZnkgb25lIG9mIHRoZSBvdGhlcgp0aGVtZXMuIEZvciBleGFtcGxlLCBhIGNsYXNzaWMgZ3JhcGggYSBsYSBiYXNlIFIgcGxvdCwgd2l0aG91dCBiYWNrZ3JvdW5kCnNoYWRpbmcgb3IgZ3JpZCBsaW5lcyBpcyBgdGhlbWVfY2xhc3NpYyggKWAuIEluIG9yZGVyIHRvIG9idGFpbiB0aGlzIHNwZWNpYWxpemVkICpsb29rKiwgd2Ugc2V0IHRoZSBhc3NvY2lhdGVkIGB0aGVtZWAgY29tbWFuZC4KCmBgYHtyfQpnICsgZ2VvbV9wb2ludChhZXMoY29sb3I9bWFuYnJvbngpKSArCiAgZ2VvbV9zbW9vdGgoYWVzKGNvbG9yPW1hbmJyb254KSxtZXRob2Q9ImxtIixzZT1GQUxTRSkgKwogIHhsYWIoIlBlcmNlbnQgSEggd2l0aCBDaGlsZHJlbiIpICsKICB5bGFiKCJQZXJjZW50IFB1YmxpYyBBc3Npc3RhbmNlIikgKwogIGdndGl0bGUoIkV4YW1wbGUgU2NhdHRlciBQbG90IikgKwogIGxhYnMoY29sb3I9IlNlbGVjdGlvbiIpICsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCgpUaGVyZSBhcmUgc2V2ZW4gYnVpbHQtaW4gdGhlbWVzIGFzIHdlbGwgYXMgc2V2ZXJhbCBjb250cmlidXRlZApvbmVzLiBBbm90aGVyIGJ1aWx0LWluIGV4YW1wbGUgaXMgYHRoZW1lX21pbmltYWwoIClgLCBzaG93biBuZXh0LgoKYGBge3J9CmcgKyBnZW9tX3BvaW50KGFlcyhjb2xvcj1tYW5icm9ueCkpICsKICBnZW9tX3Ntb290aChhZXMoY29sb3I9bWFuYnJvbngpLG1ldGhvZD0ibG0iLHNlPUZBTFNFKSArCiAgeGxhYigiUGVyY2VudCBISCB3aXRoIENoaWxkcmVuIikgKwogIHlsYWIoIlBlcmNlbnQgUHVibGljIEFzc2lzdGFuY2UiKSArCiAgZ2d0aXRsZSgiRXhhbXBsZSBTY2F0dGVyIFBsb3QiKSArCiAgbGFicyhjb2xvcj0iU2VsZWN0aW9uIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCkluIGFkZGl0aW9uLCB0aGUgcGFja2FnZSAqKmdndGhlbWVzKiogY29udGFpbnMgc2V2ZXJhbCBhZGRpdGlvbmFsIHRoZW1lcyB0aGF0IGxvb2sKZXh0cmVtZWx5IHByb2Zlc3Npb25hbC4gCgpZb3Ugd2lsbCBoYXZlIHRvIGluc3RhbGwgdGhlIHBhY2thZ2UgZmlyc3QsIGlmIHlvdSBkb24ndCBoYXZlIGl0LiBPbmNlIGluc3RhbGxlZCwgaW52b2tlCml0IHdpdGggdGhlIGBsaWJyYXJ5YCBjb21tYW5kLgoKYGBge3J9CmxpYnJhcnkoZ2d0aGVtZXMpCmBgYAoKVGhlbiwgZm9yIGV4YW1wbGUsIHVzaW5nIGB0aGVtZV90dWZ0ZSggKWA6CgpgYGB7cn0KZyArIGdlb21fcG9pbnQoYWVzKGNvbG9yPW1hbmJyb254KSkgKwogIGdlb21fc21vb3RoKGFlcyhjb2xvcj1tYW5icm9ueCksbWV0aG9kPSJsbSIsc2U9RkFMU0UpICsKICB4bGFiKCJQZXJjZW50IEhIIHdpdGggQ2hpbGRyZW4iKSArCiAgeWxhYigiUGVyY2VudCBQdWJsaWMgQXNzaXN0YW5jZSIpICsKICBnZ3RpdGxlKCJFeGFtcGxlIFNjYXR0ZXIgUGxvdCIpICsKICBsYWJzKGNvbG9yPSJTZWxlY3Rpb24iKSArCiAgdGhlbWVfdHVmdGUoKQpgYGAKCkVhY2ggb2YgdGhlIHRoZW1lcyBoYXMgc3BlY2lmaWMgb3B0aW9ucyBmb3IgZnVydGhlciBjdXN0b21pemF0aW9uLCBzbyB0aGF0IHRoZSBwb3NzaWJpbGl0aWVzCmFyZSBuZWFybHkgbGltaXRsZXNzLgoKIyMgQ29uZGl0aW9uYWwgUGxvdHMKCkNvbmRpdGlvbmFsIHBsb3RzIGFyZSBhIG1ham9yIGZlYXR1cmUgb2YgdGhlIGZ1bmN0aW9uYWxpdHkgb2YgKipnZ3Bsb3QqKiwgd2hlcmUgdGhleSBhcmUgcmVmZXJyZWQgdG8gYXMgKmZhY2V0dGluZyosIG9yICpzbWFsbCBtdWx0aXBsZXMqLiBUaGlzIGlzIGltcGxlbWVudGVkIGluIHRoZSBgZmFjZXRfd3JhcGAgYW5kIGBmYWNldF9ncmlkYCBmdW5jdGlvbnMuIFRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHR3byBpcyB0aGF0IGBmYWNldF93cmFwYCBpcyBiYXNlZCBvbiBhIHNpbmdsZSBjb25kaXRpb25pbmcgdmFyaWFibGUgKHNvLCBlc3NlbnRpYWxseSBvbmUtZGltZW5zaW9uYWwpLCB3aGVyZWFzCmBmYWNldF9ncmlkYCB0eXBpY2FsbHkgaGFzIHR3byBjb25kaXRpb25pbmcgdmFyaWFibGVzIChhcyBhIGdyaWQgb2Ygc3ViLXBsb3RzKS4KCkluICoqZ2dwbG90KiosIHRoZSBjb25kaXRpb25pbmcgaXMgYmFzZWQgb24gYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSB0aGF0IG5lZWRzIHRvIGJlIGF2YWlsYWJsZSBpbiB0aGUgZGF0YSBzZXQuIFRoZSBmYWNldHRpbmcgZm9ybXVsYSBkb2VzIG5vdCBldmFsdWF0ZSBmdW5jdGlvbnMsIHNvIHRoZSBjb25kaXRpb25pbmcgY2F0ZWdvcmllcyBuZWVkIHRvIGJlIGNvbXB1dGVkIGJlZm9yZWhhbmQuCkluIG91ciBjYXNlLCB3ZSBhbHJlYWR5IGhhdmUgdGhlICoqbWFuYnJvbngqKiB2YXJpYWJsZS4KCiMjIyBGYWNldCBXcmFwCgpUaGUgYGZhY2V0X3dyYXBgIGZ1bmN0aW9uIGNyZWF0ZXMgbXVsdGlwbGUgZ3JhcGhzIGZvciBzdWJzZXRzIG9mIHRoZSBkYXRhIGFzIGRldGVybWluZWQgYnkKYSBjb25kaXRpb25pbmcgdmFyaWFibGUuIFRoZXJlIGFyZSBzZXZlcmFsIG9wdGlvbnMsIHN1Y2ggYXMgZXhwbGljaXRseSBzZXR0aW5nIHRoZSBudW1iZXIgb2YKcm93cyBhbmQgY29sdW1ucywgYnV0IHdlIHN0aWxsIHN0aWNrIHdpdGggdGhlIGJhcmUgZGVmYXVsdHMgdG8gaWxsdXN0cmF0ZSB0aGUgcHJpbmNpcGxlLgoKRm9yIGV4YW1wbGUsIHRvIGNvbmRpdGlvbiBvbiAqKm1hbmJyb254KiosIHdlIHVzZSBgZmFjZXRfd3JhcCh+IG1hbmJyb254KWAuCgpgYGB7cn0KZyArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIikgKwogIGZhY2V0X3dyYXAofm1hbmJyb254KQpgYGAKCiMjIyMgU2lkZWJhciAtLSBSIGZvcm11bGFzCgpBbiBpbXBvcnRhbnQgZmVhdHVyZSBvZiBSIGlzIHRoZSBzby1jYWxsZWQgKmZvcm11bGEqLCB3aGljaCBpcyBhIHdheSB0byBleHByZXNzIGEgcmVsYXRpb25zaGlwCmFtb25nIHZhcmlhYmxlcy4gRm9yIGV4YW1wbGUsIGZvciBhIGxpbmVhciByZWxhdGlvbnNoaXAgc3VjaCBhcyB5ID0gYSArIGJ4ICh0aGUgbGluZWFyIHNtb290aGVyKSwKdGhlIGZvcm11bGEgd291bGQgYmUgYHkgfiB4YC4gVGhlIGB+YCB0YWtlcyB0aGUgcGxhY2Ugb2YgdGhlIGVxdWFsIHNpZ24sIHRoZSBjb25zdGFudCBpcyBhc3N1bWVkCmJ5IGRlZmF1bHQsIGFuZCB0aGUgcGFyYW1ldGVycyBhcmUKbm90IGxpc3RlZCBmb3IgYSBsaW5lYXIgZnVuY3Rpb24gKGluIG90aGVyIHNpdHVhdGlvbnMgdGhleSBtYXkgYmUpLiBUaGUgd2F5IHRoZSBjb25kaXRpb25pbmcgdmFyaWFibGVzIGFyZSAKc3BlY2lmaWVkIGZvciB0aGUgZmFjZXQgZ3JhcGhzIGlzIHNpbWlsYXI6IHRoZSB2ZXJ0aWNhbCBjb25kaXRpb25pbmcgdmFyaWFibGUgY29tZXMgZmlyc3QsIHRoZSBob3Jpem9udGFsCmNvbmRpdGlvbmluZyB2YXJpYWJsZSBuZXh0LiBJZiB0aGVyZSBpcyBvbmx5IG9uZSwgYXMgaW4gYGZhY2V0X3dyYXBgLCAKaXQgZ29lcyBvbiB0aGUgcmlnaHQgaGFuZCBzaWRlIG9mIHRoZSBgfmAgc2lnbi4KClNvLCBpbiBvdXIgZXhhbXBsZSwgd2hlcmUgd2UgY29uZGl0aW9uIG9uICoqbWFuYnJvbngqKiwgdGhlICpmb3JtdWxhKiBpcyBgIH4gbWFuYnJvbnggYCAobm90aGluZyBvbiB0aGUgCmxlZnQgc2lkZSBvZiBgfmApLgoKCiMjIyMgUmVhcnJhbmdpbmcgdGhlIGZhY2V0IGdyYXBocwoKVGhlIGBmYWNldF93cmFwYCBmdW5jdGlvbiBoYXMgc2V2ZXJhbCBvcHRpb25zIHRvIGV4cGxpY2l0bHkgc2V0IHRoZSBudW1iZXIgb2Ygcm93cyBvciBjb2x1bW5zCmluIHdoaWNoIHRoZSBzdWJncmFwaHMgc2hvdWxkIGJlIGRpc3BsYXllZC4gRm9yIGV4YW1wbGUsIHVzaW5nIGBucm93ID0gMmAgYXJyYW5nZXMgdGhlIGdyYXBocwp2ZXJ0aWNhbGx5LgoKYGBge3J9CmcgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpICsKICBmYWNldF93cmFwKH5tYW5icm9ueCxucm93PTIpCmBgYAoKIyMjIEZhY2V0IEdyaWQKVGhlIG1haW4gZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0d28gZmFjZXR0aW5nIGFwcHJvYWNoZXMgaXMgdGhhdCBgZmFjZXRfZ3JpZGAgaXMgZXhwbGljaXRseSB0d28tZGltZW5zaW9uYWwuIFRoaXMKcmVxdWlyZXMgdHdvIHZhcmlhYmxlcyB0byBzZXQgdGhlIGNvbmRpdGlvbnMsIG9uZSBvZiB0aGUgdmVydGljYWwgZGltZW5zaW9uICh5KSBhbmQgb25lIGZvciB0aGUgaG9yaXpvbnRhbApkaW1lbnNpb24gKHgpLgoKQXMgbWVudGlvbmVkIGVhcmxpZXIsIGluICoqZ2dwbG90KiosIHRoZSBjb25kaXRpb25pbmcgaXMgYmFzZWQgb24gYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSB0aGF0IG5lZWRzIHRvIGJlIGF2YWlsYWJsZSBpbiB0aGUgZGF0YSBzZXQuIEluIG91ciBleGFtcGxlLCB3ZSBvbmx5IGhhdmUgb25lLCBzbyB3ZSB3aWxsIG5lZWQgdG8gY3JlYXRlIGEgc2Vjb25kIG9uZS4KClRoZXJlIGFyZSB0aHJlZSBzby1jYWxsZWQgaGVscGVyIGZ1bmN0aW9ucyB0byBtYWtlIHRoaXMgZWFzeTogYGN1dF9pbnRlcnZhbGAsCmBjdXRfd2lkdGhgLCBhbmQgYGN1dF9udW1iZXJgLiBGb3IgZXhhbXBsZSwgd2l0aCBgY3V0X251bWJlcmAgd2UgcGFzcyB0aGUgdmFyaWFibGUsIGUuZy4sICoqaGh6aXowMCoqLCBhbmQgdGhlIG51bWJlciBvZiBjYXRlZ29yaWVzLCBzYXkgYG4gPSAyYC4gVGhpcyBjcmVhdGVzIHRoZSBuZXcgdmFyaWFibGUgYXMgYW4gUiBgZmFjdG9yYCwgZ2l2aW5nIHRoZSBpbnRlcnZhbHMgdGhhdCByZXN1bHRlZCBmcm9tIHRoZSBjdXQuIE5vdGUgdGhhdCB0aGUgdmFyaWFibGUgbmVlZHMgdG8gYmUgYWNjZXNzZWQgaW4gdGhlIHVzdWFsIHdheSwgdXNpbmcgW1sgXV0gb3IgJCBhbmQgYXNzaWduZWQgYmFjawp0byB0aGUgZGF0YSBmcmFtZSAodGhpcyBpcyBub3QgYSBgbXV0YXRlYCBjb21tYW5kLCBhbHRob3VnaCB5b3UgY291bGQgY3JlYXRlIGEgZmFjdG9yIHRoYXQgd2F5IGFzIHdlbGwpLgoKRm9yIGV4YW1wbGUsIHdlIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSAqKmN1dC5oaHNpeioqIHVzaW5nIGEgcXVhbnRpbGUgY2xhc3NpZmljYXRpb24gd2l0aCB0d28gY2F0ZWdvcmllcyAodGhlIHZhcmlhYmxlIHdpbGwgYmUgc3BsaXQgb24gdGhlIG1lZGlhbgp2YWx1ZSksIGJ5IHNldHRpbmcgYG49MmAuIFdlIG5lZWQgdG8gdXNlIHRoZSBgJGAgbm90YXRpb24gdG8gZW5zdXJlIHRoYXQgdGhlIG5ldyB2YXJpYWJsZSBpcyBhZGRlZCB0byB0aGUgcmVsZXZhbnQgZGF0YSBzZXQuIFNpbmNlIHdlIG9ubHkgaGF2ZSA1NSBvYnNlcnZhdGlvbnMsIHdlIGNhbiBlYXNpbHkgbGlzdCB0aGUKZnVsbCBzZXQgb2YgdmFsdWVzIHRvIHZlcmlmeS4gSW50ZXJuYWxseSwgdGhleSBhcmUgc3RvcmVkIGFzICpmYWN0b3JzKiAoaGVuY2UsCnRoZSBzdW1tYXJ5IG9mIHRoZSBgTGV2ZWxzYCBhdCB0aGUgZW5kIG9mIHRoZSBsaXN0aW5nKS4KCmBgYHtyfQpueWMuZGF0YSRjdXQuaGhzaXogPC0gY3V0X251bWJlcihueWMuZGF0YSRoaHNpejAwLG49MikKbnljLmRhdGEkY3V0Lmhoc2l6CmBgYAoKV2UgYXJlIG5vdyByZWFkeSB0byBjcmVhdGUgYSAyIGJ5IDIgZ3JpZCBvZiBzdWItcGxvdHMgdXNpbmcgdGhlICoqY3V0Lmhoc2l6KiogdmFyaWFibGUgdG8gY29uZGl0aW9uCm9uIHRoZSB2ZXJ0aWNhbCBheGlzIChyZXN1bHRpbmcgaW4gZGlmZmVyZW50IHJvd3Mgb2YgZ3JhcGhzKSBhbmQgKiptYW5icm9ueCoqIHRvIGNvbmRpdGlvbiBvbiB0aGUKaG9yaXpvbnRhbCBheGlzIChyZXN1bHRpbmcgaW4gZGlmZmVyZW50IGNvbHVtbnMgb2YgZ3JhcGhzKS4gV2Ugc2V0IGBmYWNldF9ncmlkKGN1dC5oaHNpeiB+IG1hbmJyb254KWAgYWZ0ZXIKdGhlIGNvbW1hbmRzIGZvciB0aGUgc2NhdHRlciBwbG90IGFuZCBsaW5lYXIgc21vb3RoLgoKTm90ZSB0aGF0IHlvdSBuZWVkIHRvIHNwZWxsIG91dCB0aGUgZnVsbCBgZ2dwbG90YCBjb21tYW5kLCBzaW5jZSB0aGUgb2JqZWN0ICoqZyoqIGRvZXMgbm90IGNvbnRhaW4KdGhlIG5ldyB2YXJpYWJsZSB3ZSBjcmVhdGVkLiBBbHRlcm5hdGl2ZWx5LCB5b3UgY2FuIHJlLWRlZmluZSAqKmcqKi4KCllvdSBjYW4gY2hhbmdlIHRoZSBsYWJlbHMsIGFkZCBhIHRpdGxlLCBldGMuLCBidXQgd2Ugd2lsbCBza2lwIHRoYXQgZnJvbSBub3cgb24gdG8ganVzdApjb25jZW50cmF0ZSBvbiB0aGUgZ3JhcGhzLgoKYGBge3J9CmdncGxvdChkYXRhPW55Yy5kYXRhLGFlcyh4PWtpZHMyMDAwLCB5PXB1YmFzdDAwKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsc2U9RkFMU0UpICsKICBmYWNldF9ncmlkKGN1dC5oaHNpeiB+IG1hbmJyb254KQpgYGAKCgoKIyMgSGlzdG9ncmFtCgojIyMgRGVmYXVsdCBIaXN0b2dyYW0KCldlIHN0YXJ0IHdpdGggdGhlIHNpbXBsZSBoaXN0b2dyYW0gY29tbWFuZCBmb3IgdGhlICoqa2lkczIwMDkqKiB2YXJpYWJsZS4KClRoZSBgZ2VvbWAgZm9yIGEgaGlzdG9ncmFtIGlzIGBnZW9tX2hpc3RvZ3JhbWAuIEluIGNvbnRyYXN0IHRvIG1vc3QgcGxvdHMgaW4KKipnZ3Bsb3QqKiwgb25seSBvbmUgdmFyaWFibGUgbmVlZHMgdG8gYmUgcGFzc2VkLiBUaGUgZ2VuZXJhbCBzZXR1cCBmb3IgKipnZ3Bsb3QqKiBpcyB0byB0aGluayBvZiB0aGUgZ3JhcGggYXMgYSB0d28tZGltZW5zaW9uYWwgcmVwcmVzZW50YXRpb24sIHdpdGggdGhlIHggdmFyaWFibGUgZm9yIHRoZSB4IGF4aXMgYW5kIHRoZSB5IHZhcmlhYmxlIGZvciB0aGUgeS1heGlzLiBJbiBhIGhpc3RvZ3JhbSwgdGhlIHZlcnRpY2FsIGF4aXMgaXMgYnkgZGVmYXVsdCB0YWtlbiB0byBiZSB0aGUgKipjb3VudCoqIG9mIHRoZSBvYnNlcnZhdGlvbnMgaW4gZWFjaCBiaW4uCgpUaGUgdGhyZWUgcGllY2VzIHdlIG5lZWQgdG8gY3JlYXRlIHRoZSBwbG90IGFyZSB0aGUgZGF0YSBzZXQgKGBkYXRhYCksICoqbnljLmRhdGEqKiwgdGhlCmFlc3RoZXRpYyAoYGFlc2ApLCAqKmtpZHMyMDA5KiogKG9ubHkgeCBieSBkZWZhdWx0LCB3aXRoIHkgYXMgdGhlIGNvdW50KSwgYW5kIHRoZSBnZW9tLCBgZ2VvbV9oaXN0b2dyYW1gLiBUaGUgY29tbWFuZCBpcyBhcyBmb2xsb3dzLAp3aXRoIGFsbCB0aGUgb3RoZXIgc2V0dGluZ3MgbGVmdCB0byB0aGVpciBkZWZhdWx0OgoKYGBge3J9CmdncGxvdChkYXRhPW55Yy5kYXRhLGFlcyhraWRzMjAwOSkpICsKICBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKIyMjIEFkanVzdGluZyB0aGUgbnVtYmVyIG9mIGJpbnMKClRoZSBncmFwaCBnaXZlcyBhIHdhcm5pbmcgdGhhdCB0aGUgZGVmYXVsdCBudW1iZXIgb2YgMzAgYmlucyBpcyBpbmFwcHJvcHJpYXRlLgpUaGUgc3RhbmRhcmQgd2F5IGluICoqZ2dwbG90KiogaXMgdG8gYWRqdXN0IHRoZSBudW1iZXIgb2YgYmlucyBpbmRpcmVjdGx5LCBieQptZWFucyBvZiB0aGUgYGJpbndpZHRoYCBvcHRpb24sIGkuZS4sIHRoZSByYW5nZSBvZiB2YWx1ZXMgdGhhdCBtYWtlIHVwIGEgYmluLAppbiB0aGUgdW5pdHMgb2YgdGhlIHZhcmlhYmxlIHVuZGVyIGNvbnNpZGVyYXRpb24uIEluc3RlYWQsIEkgcHJlZmVyIHRvIHVzZSB0aGUgb3B0aW9uIGBiaW5zYCwgCndoaWNoIHNldHMgdGhlIG51bWJlciBvZiBiaW5zIGRpcmVjdGx5ICh0aGUgYmluIHdpZHRoIGlzIHRoZW4gb2J0YWluZWQgYnkgZGl2aWRpbmcgdGhlIHJhbmdlCmJ5IHRoZSBudW1iZXIgb2YgYmlucykuCgpGb3IgZXhhbXBsZSwgdG8gc2V0IHRoZSBudW1iZXIgb2YgYmlucyB0byA3LCB3ZSBzZXQgYGJpbnM9N2AgYXMgYW4gb3B0aW9uIHRvIGBnZW9tX2hpc3RvZ3JhbWAuCgpgYGB7cn0KZ2dwbG90KGRhdGE9bnljLmRhdGEsYWVzKGtpZHMyMDA5KSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnM9NykKYGBgCgojIyMgRnJlcXVlbmN5IG9uIHZlcnRpY2FsIGF4aXMKCkFzIG1lbnRpb25lZCwgdGhlIGRlZmF1bHQgaXMgdG8gZ2l2ZSB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBlYWNoIGJpbiAodGhlICoqY291bnQqKikgYXMKdGhlIHZhbHVlIG9uIHRoZSB2ZXJ0aWNhbCBheGlzLiBJbiBvcmRlciB0byBvYnRhaW4KdGhlIGZyZXF1ZW5jeSBvbiB0aGUgdmVydGljYWwgYXhpcywgdGhlIHkgdmFyaWFibGUgbmVlZHMgdG8gYmUgc2V0IHRvIGAuLmRlbnNpdHkuLmAsCmFzIGluIGBhZXMoeD0ga2lkczIwMDksIHkgPSAuLmRlbnNpdHkuLilgLiBJbiBhbGwgb3RoZXIgcmVzcGVjdHMsIHRoZSBoaXN0b2dyYW0gaXMgdGhlIHNhbWUuCkhlcmUsIHdlIGlsbHVzdHJhdGUgdGhlICB1c2Ugb2YgYGJpbmR3aWR0aGAgYnkgcGFzc2luZyBgYmlud2lkdGg9NWAgYXMgYW4gb3B0aW9uIHRvIGBnZW9tX2hpc3RvZ3JhbWAuCgoKYGBge3J9CmdncGxvdChkYXRhPW55Yy5kYXRhLGFlcyhraWRzMjAwOSx5PS4uZGVuc2l0eS4uKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTUpCmBgYAoKCgojIyMgQnkgZ3JvdXAKCkFzIGJlZm9yZSwgd2UgY2FuIG5vdyBjcmVhdGUgYSBkaXN0aW5jdGlvbiBiZXR3ZWVuIHRoZSBkaXN0cmlidXRpb24gZm9yIHRoZSB0d28gc3Vic2V0cwppbiB0aGUgZGF0YSBkZXRlcm1pbmVkIGJ5IHRoZSAqKm1hbmJyb254KiogdmFyaWFibGUuIEZpcnN0LCB3ZSBzZXQgdGhlIGFlc3RoZXRpYyBgY29sb3JgIGVxdWFsCnRvIHRoaXMgdmFyaWFibGUgYXMgYGFlcyhjb2xvcj1tYW5icm9ueClgIGluIHRoZSBgZ2VvbV9oaXN0b2dyYW1gIGNvbW1hbmQuCgpgYGB7cn0KZ2dwbG90KGRhdGE9bnljLmRhdGEsYWVzKGtpZHMyMDA5KSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyhjb2xvcj1tYW5icm9ueCksYmlucz03KQpgYGAKClRoZSByZXN1bHQgaXMgbm90IGV4YWN0bHkgd2hhdCB3ZSBoYWQgaW4gbWluZC4gVGhlIHJlYXNvbiBpcyB0aGF0IGNvbG9yIGRldGVybWluZXMgdGhlIG91dGxpbmUKb2YgdGhlIGdyYXBoLCBidXQgbm90IHRoZSBpbnRlcm5hbCBjb2xvciwgb3IgYGZpbGxgLiBJbnN0ZWFkLCB3aXRoIGBnZW9tX2hpc3RvZ3JhbShhZXMoZmlsbD1tYW5icm9ueCkpYCwKd2UgZ2V0IHRoZSBiYXJzIHN0YWNrZWQgb24gdG9wIG9mIGVhY2ggb3RoZXIgd2l0aCBhIGRpZmZlcmVudCBjb2xvciBmb3IgZWFjaCBzdWJzZXQuCgpgYGB7cn0KZ2dwbG90KGRhdGE9bnljLmRhdGEsYWVzKGtpZHMyMDA5KSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyhmaWxsPW1hbmJyb254KSxiaW5zPTcpCmBgYAoKU3RpbGwgbm90IHRoYXQgZ3JlYXQgYXQgc2hvd2luZyB0aGUgZGlzdGluY3Rpb24gYmV0d2VlbiB0aGUgdHdvIHN1YnNldHMuIE5vdywgd2UgcmVzb3J0IHRvIGBmYWNldF93cmFwYAp0byBjcmVhdGUgYSBzZXBhcmF0ZSBoaXN0b2dyYW0gZm9yIGVhY2ggc3Vic2V0LiBXZSBoaWdobGlnaHQgdGhlIGRpc3RpbmN0aW9uIGV2ZW4gbW9yZSBieSBzZXR0aW5nCnRoZSBmaWxsIGNvbG9yIHRvICoqbWFuYnJvbngqKiwgYXMgd2UganVzdCBkaWQuIFRoZSBgZmFjZXRfd3JhcCggfiBtYW5icm9ueClgIHdpbGwgeWllbGQgdHdvIApoaXN0b2dyYW1zLCBpbiBhIGRpZmZlcmVudCBjb2xvciwgYW5kIHdpdGggYSBsZWdlbmQuCgoKYGBge3J9CmdncGxvdChkYXRhPW55Yy5kYXRhLGFlcyhraWRzMjAwOSkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoZmlsbD1tYW5icm9ueCksYmlucz03KSArCiAgZmFjZXRfd3JhcCh+IG1hbmJyb254KQpgYGAKCllvdSBjYW4gbm93IHNwaWZmIHVwIHRoZXNlIGdyYXBocyB3aXRoIGF4aXMgbGFiZWxzLCBhIHRpdGxlLCBhIGxhYmVsIGZvciB0aGUgbGVnZW5kLCBhbmQKZXZlbiBhIGN1c3RvbSB0aGVtZS4KCiMjIEZyZXF1ZW5jeSBQb2x5Z29uCgpBbm90aGVyIGNvbW1vbiBwbG90IHRvIGRlcGljdCBhIHVuaXZhcmlhdGUgZGlzdHJpYnV0aW9uIGlzIHRoZSAqZnJlcXVlbmN5IHBvbHlnb24qLApvYnRhaW5lZCB3aXRoIGBnZW9tX2ZyZXFwb2x5YC4gSXQgaXMKZXNzZW50aWNhbGx5IHRoZSBzYW1lIGFzIGEgaGlzdG9ncmFtLCBidXQgdXNlcyBhIHNldCBvZiBjb25uZWN0ZWQgcG9pbnRzIGluc3RlYWQgb2YKdGhlIGJhcnMgZm9yIHRoZSBoaXN0b2dyYW0uIEFnYWluLCB3ZSBoYXZlIHRvIHNwZWNpZnkgYSBgYmlud2lkdGhgLiBBIGJhcmUgYm9uZXMKZXhhbXBsZSBpcyBnaXZlbiBiZWxvdywgdXNpbmcgdGhlIHNhbWUgY29tbWFuZHMgYXMgZm9yIHRoZSBoaXN0b2dyYW0sIGJ1dCB3aXRoCmBnZW9tX2ZyZXFwb2x5YC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1ueWMuZGF0YSxhZXMoa2lkczIwMDkpKSArCiAgZ2VvbV9mcmVxcG9seShiaW53aWR0aD01KQpgYGAKCldpdGggdGhlIGZyZXF1ZW5jeSBwb2x5Z29uIGl0IGlzIHZlcnkgZWFzeSB0byBzaG93IHRoZSBkaXN0cmlidXRpb24gZm9yIGRpZmZlcmVudCAKc3ViZ3JvdXBzICBpbiB0aGUgZGF0YSwgZm9yIGV4YW1wbGUsIGJ5ICBzZXR0aW5nIGBhZXMoY29sb3I9bWFuYnJvbngpYCAoaW5zdGVhZCBvZiB1c2luZyBgZmFjZXRfd3JhcGApLgpXZSB1c2UgYGJpbnM9N2AgaW5zdGVhZCBvZiBgYmlud2lkdGhgIHRvIGhpZ2hsaWdodCB0aGUgY29udHJhc3Qgd2l0aCBhIGhpc3RvZ3JhbSB3aXRoCnRoZSBzYW1lIG9wdGlvbnMuCgpgYGB7cn0KZ2dwbG90KGRhdGE9bnljLmRhdGEsYWVzKGtpZHMyMDA5KSkgKwogIGdlb21fZnJlcXBvbHkoYWVzKGNvbG9yPW1hbmJyb254KSxiaW5zPTcpCmBgYAoKIyMgQm94IFBsb3QKClRoZSBib3ggcGxvdCwgYWxzbyByZWZlcnJlZCB0byBhcyBUdWtleSdzIGJveCBhbmQgd2hpc2tlciBwbG90LCBpcyBhbiBhbHRlcm5hdGl2ZSB3YXkgdG8gdmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gb2YgYSBzaW5nbGUgdmFyaWFibGUsIHdpdGggYSBmb2N1cyBvbiBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIHN1Y2ggYXMgcXVhcnRpbGVzIGFuZCB0aGUgbWVkaWFuLiBUaGUgCmNvcnJlc3BvbmRpbmcgZ2VvbSBpcyBgZ2VvbV9ib3hwbG90YC4gV2UgY29udGludWUgb3VyIGV4YW1wbGUgdXNpbmcgdGhlICoqa2lkczIwMDkqKiB2YXJpYWJsZS4gV2UgZmlyc3QgY29uc2lkZXIgdGhlIGRlZmF1bHQgb3B0aW9uLCB0aGVuIG1vdmUgb24gdG8gaWxsdXN0cmF0ZSBhIGZldyBvcHRpb25hbCBzZXR0aW5ncy4KCiMjIyBEZWZhdWx0IFNldHRpbmdzClRoZSBtaW5pbWFsIGFyZ3VtZW50cyB0byBjcmVhdGUgYSBib3hwbG90IGFyZSB0aGUgYGRhdGFgIHNldCBhbmQgdGhlIHggYW5kIHkKdmFyaWFibGVzIHBhc3NlZCB0byBgYWVzYC4gQXMgbWVudGlvbmVkIGFib3ZlLCB0aGUgbG9naWMgYmVoaW5kIHRoZSBncmFwaHMgaW4gKipnZ3Bsb3QqKiBpcyB0d28tZGltZW5zaW9uYWwsIHNvIGJvdGggeCBhbmQgeSBuZWVkIHRvIGJlIHNwZWNpZmllZC4gVGhlIHggdmFyaWFibGUgaXMgdXNlZCB0byBjcmVhdGUgc2VwYXJhdGUgYm94IHBsb3RzIGZvciBkaWZmZXJlbnQgc3Vic2V0cyBvZiB0aGUgZGF0YS4gSW4gb3VyIHNpbXBsZSBleGFtcGxlLCB3ZSBkb24ndCBuZWVkIHRoaXMgZmVhdHVyZSwgc28gd2Ugc2V0IHRoZSB4IHZhcmlhYmxlIHRvIGVtcHR5LCBpLmUuLCAiICIuClRoZSB5IHZhcmlhYmxlIGlzIHRoZSBhY3R1YWwgdmFyaWFibGUgb2YgaW50ZXJlc3QsICoqa2lkczIwMDkqKi4gVGhlIHJlc3VsdGluZyBncmFwaCBpcyBzaG93biBiZWxvdy4KCgpgYGB7cn0KZ2dwbG90KGRhdGE9bnljLmRhdGEsYWVzKHg9IiIseT1raWRzMjAwOSkpICsKICBnZW9tX2JveHBsb3QoKQpgYGAKCk5vdGUgdGhlIG91dGxpZXIgc2hvd24gYXMgYSBkb3QgKHBvaW50KSBhdCB0aGUgYm90dG9tIG9mIHRoZSBncmFwaC4gVGhlIGJveCBzaG93cyB0aGUgZmlyc3QgcXVhcnRpbGUsCnRoZSBtZWRpYW4sIGFuZCB0aGUgdGhpcmQgcXVhcnRpbGUuIFRoZSBsaW5lcyBjb25uZWN0IHRoZSB1cHBlciBhbmQgbG93ZXIgZmVuY2VzLCBhbmQgYW55IG91dGxpZXJzCmFyZSBzaG93biBhcyBkaXN0aW5jdCBwb2ludHMuCgojIyMgQnkgZ3JvdXAKClRoZSBkZXNpZ24gb2YgdGhlIGJveCBwbG90IGluICoqZ2dwbG90KiogaXMgcmVhbGx5IGdlYXJlZCB0byBzaG93IHRoZSBkaXN0cmlidXRpb24gZm9yCmRpZmZlcmVudCBzdWJncm91cHMuIEVhY2ggc3ViZ3JvdXAgZ2V0cyBhIGJveCBwbG90LCBhcnJhbmdlZCBzaWRlIGJ5IHNpZGUgZm9yIGVhc3kgY29tcGFyaXNvbi4KCkZvciBleGFtcGxlLCB3ZSBub3cgc2V0IGBhZXMoeD1tYW5icm9ueCx5PWtpZHMyMDA5KSB0byBvYnRhaW4gdHdvIGJveCBwbG90cy4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1ueWMuZGF0YSxhZXMoeD1tYW5icm9ueCx5PWtpZHMyMDA5KSkgKwogIGdlb21fYm94cGxvdCgpCmBgYAoKTm90ZSBob3cgdGhlIHR3byBjYXRlZ29yaWVzIGFyZSBsaXN0ZWQgYXMgbGFiZWxzIG9uIHRoZSB4LWF4aXMuIENvbXBhcmUgdGhpcyB0bwp1c2luZyBgZmFjZXRfd3JhcCggfiBtYW5icm9ueClgIHdpdGggYHg9IiAiYCBpbiB0aGUgbWFpbiBgZ2dwbG90YCBjb21tYW5kLgoKCgpgYGB7cn0KZ2dwbG90KGRhdGE9bnljLmRhdGEsYWVzKHg9IiIseT1raWRzMjAwOSkpICsKICBnZW9tX2JveHBsb3QoKSArCiAgZmFjZXRfd3JhcCggfiBtYW5icm9ueCkKYGBgCgpJdCBpcyB0aGUgc2FtZSBncmFwaCwgYnV0IG5vdyB0aGUgeC1heGlzIGxhYmVsIGlzICoqeCoqIChhcyBpbiB0aGUgZ2VuZXJpYyBib3ggcGxvdCksIGFuZCB0aGUKY2F0ZWdvcmllcyBhcmUgbGlzdGVkIGF0IHRoZSB0b3Agb2YgZWFjaCBncmFwaC4KCiMjIyBCZWxscyBhbmQgd2hpc3RsZXMKCkFzIGlzLCB0aGUgZGVmYXVsdCBib3ggcGxvdCBpcyBwcmV0dHkgcnVkaW1lbnRhcnkuIFdlIHdpbGwgaWxsdXN0cmF0ZSB0aGUgcG93ZXIgb2YgKipnZ3Bsb3QqKiBieSBhZGRpbmcgYSBudW1iZXIgb2YgZmVhdHVyZXMgdG8gdGhlIHBsb3QgaW4gb3JkZXIgcHJvdmlkZSBmdXJ0aGVyIGluZm9ybWF0aW9uLiBGaXJzdCwgYWRkIGEgdGl0bGUgdXNpbmcgYGdndGl0bGVgLiBBcyB3ZSBkaWQgZm9yIHRoZSBoaXN0b2dyYW0sIHdlIHdpbGwgY2VudGVyIHRoZSB0aXRsZSBvdmVyIHRoZSBncmFwaC4gV2Uga2VlcCB0aGUgeC12YXJpYWJsZSBzZXQgdG8gKiptYW5icm9ueCoqIHRvIG9idGFpbiB0d28KYm94IHBsb3RzIGFuZCBzZXQgdGhlIGxhYmVsIGZvciB0aGUgIHktYXhpcyB0byAiUGVyY2VudCBISCB3aXRoIENoaWxkcmVuIi4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1ueWMuZGF0YSxhZXMoeD1tYW5icm9ueCx5PWtpZHMyMDA5KSkgKwogIGdlb21fYm94cGxvdCgpICsKICB5bGFiKCJQZXJjZW50IEhIIHdpdGggQ2hpbGRyZW4iKSArCiAgZ2d0aXRsZSgiRXhhbXBsZSBCb3ggUGxvdCIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSkpCmBgYAoKTmV4dCwgd2Ugd2FudCB0byBnaXZlIHRoZSBib3ggcGxvdCBhIGNvbG9yLCBmb2xsb3dpbmcgdGhlIHZhcmlhYmxlICoqbWFuYnJvbngqKi4gVGhpcyBpcyBhY2NvbXBsaXNoZWQgd2l0aCB0aGUgCmBjb2xvcmAgKGZvciB0aGUgb3V0bGluZXMpIGFuZCBgZmlsbGAgKGZvciB0aGUgaW5zaWRlIG9mIHRoZSBib3gpIG9wdGlvbnMgdG8gYGFlc2AgZm9yIGBnZW9tX2JveHBsb3RgLgpXZSBhZGQgdGhlIGxlZ2VuZCBsYWJlbCBhcyAqKlNlbGVjdGlvbioqIGFzIHdlIGRpZCBiZWZvcmUuIEhvd2V2ZXIsIGJlY2F1c2Ugd2Ugbm93IGhhdmUgdHdvIGZlYXR1cmVzCmJ5IHN1YnNldCwgd2UgbmVlZCB0byBzcGVjaWZ5IHRoZSBsZWdlbmQgZm9yIGJvdGgsIGFzIGBsYWJzKGNvbG9yPSJTZWxlY3Rpb24iLGZpbGw9IlNlbGVjdGlvbiIpYC4KCkFsc28sIHRoZSBib3ggcGxvdHMgZG8gbm90IHNob3cgdGhlIGZlbmNlcyBhcyBzZXBhcmF0ZSBsaW5lcywgdGhlIHdheSB0aGV5IGRvIGluIG1hbnkKb3RoZXIgc29mdHdhcmUgcGFja2FnZXMsIGUuZy4sIGFzIGluIEdlb0RhLiBUaGlzIGNhbgpiZSByZW1lZGllZCwgYnV0IG5vdCBxdWl0ZSBpbiB0aGUgc2FtZSB3YXkgYXMgaW4gR2VvRGEuIEluICoqZ2dwbG90KiosIHRoZSBmZW5jZXMgYXJlCmRyYXduIGF0IHRoZSBsb2NhdGlvbiBvZiB0aGUgZXh0cmVtZSB2YWx1ZXMsIHRoZSAqKnltaW4qKiBhbmQgKip5bWF4KiosCmFuZCBub3QgYXQgdGhlIGxvY2F0aW9uIG9mIHRoZSBmZW5jZSBjdXQtb2ZmIHZhbHVlcywgYXMgaW4gR2VvRGEuIFRoZSBmZW5jZXMgYXJlCm9idGFpbmVkIGZyb20gdGhlIGBzdGF0X2JveHBsb3RgIGZ1bmN0aW9uLCBieSBwYXNzaW5nIHRoZSBgZ2VvbWAgYXMgYGVycm9yYmFyYC4KCk9uZSBmaW5hbCByZWZpbmVtZW50LiBJbiBHZW9EYSwgdGhlIGJveCBwbG90IGFsc28gc2hvd3MgdGhlIGxvY2F0aW9ucyBvZiB0aGUgYWN0dWFsIG9ic2VydmF0aW9ucyBhcyBwb2ludHMgb24gdGhlIGNlbnRyYWwgYXhpcy4gV2Ugb2J0YWluIHRoZSBzYW1lIGVmZmVjdCBieSBhZGRpbmcKYGdlb21fcG9pbnRgIHdpdGggYGNvbG9yYCAqKmJsdWUqKi4gV2UgZHJhdyB0aGUgcG9pbnRzIGZpcnN0LCBhbmQgdGhlIGJveCBwbG90IG9uCnRvcCBvZiBpdCwgdXNpbmcgdGhlIGxheWVycyBsb2dpYy4gSG93ZXZlciwgd2Ugd2FudCB0byBtYWtlIHN1cmUgdGhhdCB0aGUgY2VudHJhbCBib3gKZG9lc24ndCBtYXNrIHRoZSBwb2ludHMsIHdoaWNoIGl0IGRvZXMgd2hlbiB0aGUgKnRyYW5zcGFyZW5jeSogaXMga2VwdCBhcyB0aGUgZGVmYXVsdC4KVG8gYWNjb21wbGlzaCB0aGlzLCB3ZSBzZXQgdGhlIGBhbHBoYWAgbGV2ZWwgZm9yIGJvdGggcG9pbnRzIGFuZCBib3ggcGxvdCBhdCAqKjAuNSoqLgoKVGhlIHJlc3VsdCBpcyBub3cgYSBxdWl0ZSBmYW5jeSBncmFwaCB0aGF0IHByb3ZpZGVzIGEgbG90IG9mIGluZm9ybWF0aW9uIG9uIHRoZSByZWxhdGl2ZQpkaXN0cmlidXRpb24gb2YgdGhlIHBlcmNlbnRhZ2UgaG91c2Vob2xkcyB3aXRoIGNoaWxkcmVuIGluIHRoZSB0d28gc3Vic2V0cyBvZiB0aGUgZGF0YS4KVGhpcyBsZXZlbCBvZiByZWZpbmVtZW50IGNvbWVzIGF0IHRoZSBleHBlbnNlIG9mIGhhdmluZyB0byBzcGVjaWZ5IGVpZ2h0IGxpbmVzIG9mIGNvZGUuCgpgYGB7cn0KZ2dwbG90KGRhdGE9bnljLmRhdGEsYWVzKHg9bWFuYnJvbngseT1raWRzMjAwOSkpICsKICBnZW9tX3BvaW50KGNvbG9yPSJibHVlIixhbHBoYT0wLjUpICsKICBnZW9tX2JveHBsb3QoYWVzKGNvbG9yPW1hbmJyb254LGZpbGw9bWFuYnJvbngpLGFscGhhPTAuNSkgKwogIHN0YXRfYm94cGxvdChnZW9tPSJlcnJvcmJhciIpICsKICB5bGFiKCJQZXJjZW50IEhIIHdpdGggQ2hpbGRyZW4iKSArCiAgbGFicyhjb2xvcj0iU2VsZWN0aW9uIixmaWxsPSJTZWxlY3Rpb24iKSArCiAgZ2d0aXRsZSgiRXhhbXBsZSBCb3ggUGxvdCIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSkpCmBgYAoKIyMgVmlvbGluIFBsb3QKCkEgdmlvbGluIHBsb3QgaXMgYSBoeWJyaWQgb2YgYSBib3ggcGxvdCBhbmQgYSBkZW5zaXR5IHBsb3QsIHNob3dpbmcgcmVnaW9ucyBvZiB0aGUKZGF0YSB3aGVyZSBtb3JlIG9ic2VydmF0aW9ucyBhcmUgZm91bmQuIFRoZSBiYXNpYyBzZXR1cCBpcyB0aGUgc2FtZSBhcyBmb3IgYSBib3ggcGxvdAooaS5lLiwgc2V0dGluZyBgeCA9ICIiYCBmb3IgYSBzaW5nbGUgcGxvdCksIGJ1dCBub3cgdXNpbmcgdGhlIGdlb20gYGdlb21fdmlvbGluYC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1ueWMuZGF0YSxhZXMoeD0iIix5PWtpZHMyMDA5KSkgKwogIGdlb21fdmlvbGluKCkKYGBgCgpOb3csIGFnYWluLCB3aXRoIHNvbWUgYmVsbHMgYW5kIHdoaXN0bGVzLiBXZSBzaG93IHRoZSB2aW9saW4gcGxvdCBzZXBhcmF0ZWx5IGZvciBlYWNoIHN1YnNldApieSBzcGVjaWZ5aW5nIGBhZXMoeD1tYW5icm9ueCx5PWtpZHMyMDA5KWAgaW4gdGhlIG1haW4gYGdncGxvdGAgY29tbWFuZC4gVGhlbiB3ZSBwbG90IHRoZQppbmRpdmlkdWFsIHBvaW50cyB1c2luZyBgZ2VvbV9wb2ludGAgd2l0aCBgY29sb3I9ImJsdWUiYCBhbmQgYGFscGhhPTAuNWAuIFdlIHNldCB0aGUKYGNvbG9yYCBhbmQgYGZpbGxgIGZvciB0aGUgYGdlb21fdmlvbGluYCB0byAqKm1hbmJyb254KiosIGp1c3QgYXMgd2UgZGlkIGZvciB0aGUgYm94IHBsb3QuCkZpbmFsbHksIHdlIGZpbmlzaCB3aXRoIHNldHRpbmcgdGhlIHktYXhpcyBsYWJlbCwgdGhlIGxlZ2VuZCBsYWJlbCBhbmQgdGhlIHRpdGxlLgoKCmBgYHtyfQpnZ3Bsb3QoZGF0YT1ueWMuZGF0YSxhZXMoeD1tYW5icm9ueCx5PWtpZHMyMDA5KSkgKwogIGdlb21fcG9pbnQoY29sb3I9ImJsdWUiLGFscGhhPTAuNSkgKwogIGdlb21fdmlvbGluKGFlcyhjb2xvcj1tYW5icm9ueCxmaWxsPW1hbmJyb254KSxhbHBoYT0wLjUpICsKICB5bGFiKCJQZXJjZW50IEhIIHdpdGggQ2hpbGRyZW4iKSArCiAgbGFicyhjb2xvcj0iU2VsZWN0aW9uIixmaWxsPSJTZWxlY3Rpb24iKSArCiAgZ2d0aXRsZSgiRXhhbXBsZSBWaW9saW4gUGxvdCIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSkpCmBgYAoKIyMgVGlkeSBEYXRhIGFuZCBHcmFwaHMKCk9mdGVuLCB0aGUgZGF0YSBhcmUgbm90IGluIGEgZm9ybWF0IHRoYXQgYWxsb3dzIGZvciB1cyB0byBjcmVhdGUgdGhlIGdyYXBocyB0aGF0IHdlIHdhbnQuCkZvciBleGFtcGxlLCB3aGF0IGlmIHdlIHdhbnRlZCB0byBzaG93LCBmb3IgZWFjaCBuZWlnaGJvcmhvb2QsIGhvdyB0aGUgc3RhYmlsaXR5IG9mIHRoZQpuZWlnaGJvcmhvb2QgKHRoZSB5ZWFycyBob3VzZWhvbGRzIHJlc2lkZWQgaW4gdGhlIG5laWdoYm9yaG9vZCkgY2hhbmdlZCBvdmVyIHRoZSB5ZWFycy4KV2UgaGF2ZSB0aGlzIGluZm9ybWF0aW9uIGluIHRocmVlIHZhcmlhYmxlcywgaS5lLiwgKip5cmhvbTAyKiosICoqeXJob20wNSoqLCAqKnlyaG9tMDgqKi4KQnV0IGhvdyBjb3VsZCB3ZSBzaG93IHRoZXNlIGZvciBlYWNoIG5laWdoYm9yaG9vZCwgd2hlbiB0aGUgYWVzdGhldGljcyB3YW50IHVzIHRvIApkZWZpbmUgb25lIHggYW5kIG9uZSB5IHZhcmlhYmxlLiBUaGUgcmVhc29uIHRoaXMgZG9lc24ndCB3b3JrIGlzIHRoYXQgdGhlIGRhdGEgc2V0IGlzCm5vdCBpbiBhICp0aWR5KiBmb3JtYXQsIHdoZXJlIGVhY2ggcm93IHBlcnRhaW5zIHRvIGEgc2luZ2xlIGRhdGEgcG9pbnQuIEluIG91ciBjYXNlLAp0aGF0IG1lYW5zIHdlIG5lZWQgYm90aCB0aGUgbmVpZ2hib3Job29kIGFuZCB0aGUgeWVhciBmb3IgdGhlIHZhcmlhYmxlIHlyaG9tLCBzb21ldGhpbmcKdGhhdCBpcyBjYWxsZWQgYSBwYW5lbCBkYXRhIGZvcm1hdCAoYm90aCBzcGFjZSBhbmQgdGltZSkuCgpJIHdpbGwgYnJpZWZseSBpbGx1c3RyYXRlIHNvbWUgb2YgdGhlIGZlYXR1cmVzIG9mIHRoZSB0aWR5dmVyc2UsIHNwZWNpZmljYWxseSB0aGUgb3BlcmF0aW9uCnRvICpnYXRoZXIqIG9ic2VydmF0aW9ucyBzdWNoIGFzIGRpZmZlcmVudCBjb2x1bW5zIGFyZSB0dXJuZWQgaW50byByb3dzLgoKVG8ga2VlcCB0aGluZ3Mgc2ltcGxlLCB3ZSBmaXJzdCBjcmVhdGUgYSBuZXcgZGF0YSBmcmFtZSAqKnN0YWJsZSoqLCB3aGljaCBpcyBhIHN1YnNldApvZiB0aGUgdmFyaWFibGVzOiAqKmNvZGUqKiwgKipuYW1lKiosICoqeXJob20wMioqLCAqKnlyaG9tMDUqKiwgKip5cmhvbTA4KiosIGFuZCAqKm1hbmJyb254KiouCldlIHVzZSB0aGUgcGlwZSBhbmQgdGhlIGBzZWxlY3RgIGNvbW1hbmQuIApUbyBjaGVjayB0aGUgcmVzdWx0LCB3ZSBwcmludCB0aGUgY29udGVudHMgb2YgdGhlIG5ldyBkYXRhIGZyYW1lLgoKYGBge3J9CnN0YWJsZSA8LSBueWMuZGF0YSAlPiUgc2VsZWN0KGNvZGUsbmFtZSx5cmhvbTAyLHlyaG9tMDUseXJob20wOCxtYW5icm9ueCkKc3RhYmxlCmBgYAoKVGhlIG5ldyBkYXRhIGZyYW1lIG9ubHkgY29udGFpbnMgaW5mb3JtYXRpb24gb24gKip5cmhvbSoqLCB0aGUgbWVkaWFuIG51bWJlciBvZiB5ZWFycyBob3VzZWhvbGRzIGxpdmVkIGluIHRoZQpuZWlnaGJvcmhvb2QuIFNvLCByZWFsbHksIHRoZSBkaWZmZXJlbnQgKip5cmhvbXh4KiogdmFyaWFibGVzIHBlcnRhaW4gdG8gZGlmZmVyZW50IHllYXJzIG9mIHRoZSBzYW1lIHRoaW5nLgpUbyBzaW1wbGlmeSBtYXR0ZXJzLCBhbmQgdG8gaWxsdXN0cmF0ZSBob3cgdG8gZGVhbCB3aXRoIG5vbi1zdGFuZGFyZCB2YXJpYWJsZXMgaW4gdGhlICp0aWR5dmVyc2UqLCB3ZSB3aWxsCnVzZSBgcmVuYW1lYCB0byB0dXJuIHRoZSB0aHJlZSB2YXJpYWJsZSBuYW1lcyBpbnRvIHRoZSByZXNwZWN0aXZlIHllYXJzLiBCZWNhdXNlIHRoZSB5ZWFycyBhcmUgbm9uLXN0YW5kYXJkCnZhcmlhYmxlIG5hbWVzLCB3ZSBuZWVkIHRvIHB1dCB0aGVtIGluIHF1b3RlcywgYXMgXGAyMDAyXGAsIGV0Yy4gV2UgdXNlIHRoZSBwaXBlIHRvIHJlbmFtZSB0aGUgdGhyZWUgdmFyaWFibGVzLAphbmQgcHJpbnQgdGhlIGRhdGEgZnJhbWUgdG8gY2hlY2sgdGhlIHJlc3VsdC4KCmBgYHtyfQpzdGFibGUgPC0gc3RhYmxlICU+JSByZW5hbWUoYDIwMDJgPXlyaG9tMDIsYDIwMDVgPXlyaG9tMDUsYDIwMDhgPXlyaG9tMDgpCnN0YWJsZQpgYGAKCk5vdywgd2UgaWxsdXN0cmF0ZSB0aGUgdmVyeSBwb3dlcmZ1bCAoYnV0IG5vdCBhbHdheXMgZWFzeSB0byB1bmRlcnN0YW5kKSBgZ2F0aGVyYCBmdW5jdGlvbi4gV2hhdCB3ZSB3YW50IHRvCmRvIGlzIHRvIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSB0byBkZXNpZ25hdGUgdGhlIG9ic2VydmF0aW9ucyBmb3IgZGlmZmVyZW50IHllYXJzLiBUaG9zZSB5ZWFycyBhcmUgYDIwMDJgLCBgMjAwNWAsIGFuZApgMjAwOGAuIFJpZ2h0IG5vdywgdGhlc2UgYXJlIHNlcGFyYXRlIGNvbHVtbnMgd2l0aCB0aGUgaW5mb3JtYXRpb24gb24gbmVpZ2hib3Job29kIHN0YWJpbGl0eSBmb3IgZWFjaCBuZWlnaGJvcmhvb2QKaW4gdGhlIHJlc3BlY3RpdmUgeWVhci4gV2hhdCB3ZSB3YW50IGlzIGEgZGF0YSBmcmFtZSB3aGVyZSBlYWNoICpvYnNlcnZhdGlvbiogaGFzIGJvdGggYSBuZWlnaGJvcmhvb2QgYW5kIGEgeWVhciwgYW5kIAphIHZhbHVlIGZvciB0aGUgc3RhYmlsaXR5IChpLmUuLCB0aGUgbWVkaWFuIHllYXIgaG91c2Vob2xkcyBsaXZlZCB0aGVyZSkuIFRvIGRvIHRoaXMgYnkgaGFuZCwgd2Ugd291bGQgaGF2ZSB0byBjb3B5CnRoZSBuZWlnaGJvcmhvb2QgY29kZSwgbmFtZSBhbmQgbWFuYnJvbnggY2xhc3NpZmljYXRpb24sIGFuZCB0aGVuIG1hbnVhbGx5IGVudGVyIHRoZSB5ZWFyIGFuZCB0aGUgY29ycmVzcG9uZGluZyB2YWx1ZQpmb3IgKip5cmhvbSoqLiBGb3IgZWFjaCBuZWlnaGJvcmhvb2QsIHdlIHdvdWxkIGhhdmUgdGhyZWUgcm93cywgb25lIGZvciBlYWNoIHllYXIuIEluIGFkZGl0aW9uIHRvIHRoZSBuZWlnaGJvcmhvb2QKY29kZSwgbmFtZSBhbmQgbWFuYnJvbnggY2xhc3NpZmljYXRpb24sIHdlIHdvdWxkIGhhdmUgdHdvIGV4dHJhIGNvbHVtbnM6IG9uZSB3aXRoIHRoZSB5ZWFyLCBhbmQgb25lIHdpdGggdGhlIAp2YWx1ZSBmb3IgdGhlICpzdGFiaWxpdHkqICh5ZWFycyBsaXZlZCBpbiBuZWlnaGJvcmhvb2QpLiAKCkluIGEgKnRpZHkqIGRhdGEgZnJhbWUsIHRoZSBuYW1lIGZvciB0aGUgY29sdW1uIHdpdGggdGhlIHllYXJzIChpLmUuLCB0aGUgb2JzZXJ2YXRpb25zIHdlIGFyZSAqZ2F0aGVyaW5nKikgaXMgY2FsbGVkIGEgYGtleWAuCldlIGhhdmUgdG8gZ2l2ZSB0aGlzIGEgbmFtZSB0aGF0IG1ha2VzIHNlbnNlLCBlLmcuLCBga2V5PXllYXJgLiBUaGUgdmFsdWVzIGZvciB0aGUgb2JzZXJ2YXRpb25zIG9uIGB5ZWFyYCB3aWxsIGJlCnRoZSBjb2x1bW4gaGVhZGluZ3Mgb2YgdGhlIHZhcmlhYmxlcyB3ZSB3YW50IHRvICpnYXRoZXIqLCBpLmUuLCB0aGUgdGhyZWUgeWVhcnMgYDIwMDJgLCBgMjAwNWAgYW5kIGAyMDA4YC4KVGhlIHZhbHVlcyB0aGF0IHdlIGFyZSBpbnRlcmVzdGVkIGluIGFyZSB0aGUgbnVtYmVyIG9mIHllYXJzIHJlc2lkZWQgaW4gdGhlIG5laWdoYm9yaG9vZCwgYnV0IHRoZXNlIGRvbid0CmN1cnJlbnRseSBoYXZlIGEgdmFyaWFibGUgbmFtZS4gVGhleSBhcmUgY29udGFpbmVkIHVuZGVyIHRoZSBgMjAwMmAsIGAyMDA1YCBhbmQgYDIwMDhgIHZhcmlhYmxlIGhlYWRpbmdzLgpUaGUgbmFtZSBmb3IgdGhpcyBuZXcgdmFyaWFibGUgaXMgY2FsbGVkIGEgYHZhbHVlYC4gQWdhaW4sIHdlIGhhdmUgdG8gc3BlY2lmeSBhIG5hbWUgZm9yIGl0LCBlLmcuLApgdmFsdWU9c3RhYmxlYC4gU28gbm93LCB3ZSBoYXZlIHR3byBwaWVjZXM6IHdlIGhhdmUgdGhlIG5hbWUgZm9yIHRoZSBuZXcgdmFyaWFibGUgdGhhdCB3aWxsIGxhYmVsIHRoZSBleHRyYQpvYnNlcnZhdGlvbnMgYnkgeWVhciAodGhlIGBrZXlgKSwgYW5kIHdlIGhhdmUgYSBuYW1lIGZvciB0aGUgdmFyaWFibGUgdGhhdCB3aWxsIGNvbnRhaW4gdGhlIHJlbGV2YW50CnZhbHVlcyAodGhlIGB2YWx1ZWApLiBOb3csIHdlIHN0aWxsIG5lZWQgdG8gc3BlY2lmeSB3aGVyZSB0aG9zZSB2YWx1ZXMgd2lsbCBjb21lIGZyb20uIFRoZXNlIGFyZSB0aGUgCnRocmVlIGNvbHVtbnMgYDIwMDJgLCBgMjAwNWAsIGFuZCBgMjAwOGAuCgpUaGUgY29tbWFuZCBpcyBgZ2F0aGVyKGtleT15ZWFyLHZhbHVlPXN0YWJsZSxgMjAwMmAsYDIwMDVgLGAyMDA4YClgLiBXaGF0IHRoaXMgZG9lcyBpcyBjcmVhdGUgYSBuZXcgY29sdW1uCmZvciBgeWVhcmAsIGEgbmV3IGNvbHVtbiBmb3IgYHN0YWJsZWAsIGl0IGFzc2lnbnMgdGhlIHZhbHVlcyBvZiBgMjAwMmAsIGAyMDA1YCBhbmQgYDIwMDhgIHRvIGB5ZWFyYCwgYW5kCnRha2VzIHRoZSBtYXRjaGluZyB2YWx1ZXMgYW5kIGFzc2lnbnMgdGhlbSB0byB0aGUgY29ycmVzcG9uZGluZyByb3cgZm9yIHRoZSBjb2x1bW4gYHN0YWJsZWAuCgpMZXQncyBhZ2FpbiB1c2UgdGhlIHBpcGUgYW5kIGFzc2lnbiB0aGUgcmVzdWx0IHRvIGEgbmV3IGRhdGEgZnJhbWUgKipzdG5laWdoKiouCgpgYGB7cn0Kc3RuZWlnaCA8LSBzdGFibGUgJT4lIGdhdGhlcihrZXk9eWVhcix2YWx1ZT1zdGFibGUsYDIwMDJgLGAyMDA1YCxgMjAwOGApCnN0bmVpZ2gKYGBgCgpXZSBjYW4gbm93IGNyZWF0ZSBhIHBsb3Qgb2YgdGhlIHZhbHVlIG9mICoqc3RhYmxlKiogZm9yIGVhY2ggb2YgdGhlIHRocmVlIHllYXJzIGluIGVhY2ggb2YgdGhlIG5laWdoYm9yaG9vZHMuCk9uZSB3YXkgKGJ1dCBub3QgYSB2ZXJ5IHByZXR0eSBvbmUpIGlzIHRvIGRvIHRoaXMgYXMgYSBzY2F0dGVyIHBsb3QgdXNpbmcgYGdlb21fcG9pbnRgIHdpdGggYHg9c3RhYmxlYCBhbmQKYHk9bmFtZWAuIEZvciBleGFtcGxlLCB1c2luZyBgdGhlbWVfY2xhc3NpYygpYCBhbmQgZ2l2aW5nIHRoZSBwb2ludHMgYSBkaWZmZXJlbnQgY29sb3IgZm9yIGVhY2ggeWVhci4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1zdG5laWdoLGFlcyh4PXN0YWJsZSx5PW5hbWUpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9eWVhcikpICsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCgpUaGlzIGNhbiBiZSBtdWNoIGltcHJvdmVkIHVwb24sIGJ1dCBJIGxlYXZlIHRoYXQgYXMgYSBzby1jYWxsZWQgKmV4ZXJjaXNlKi4KCkFsdGVybmF0aXZlbHksIGl0IGlzIG5vdyB2ZXJ5IGVhc3kgdG8gY3JlYXRlIGJveCBwbG90IGZvciBlYWNoIHllYXIgbmV4dCB0byBlYWNoIG90aGVyLApieSBzZXR0aW5nIGB4PXllYXJgIGFuZCBgeT1zdGFibGVgIGZvciB0aGUgYm94IHBsb3QgY29tbWFuZC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1zdG5laWdoLGFlcyh4PXllYXIseT1zdGFibGUpKSArCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgpUaGlzIGlzIGp1c3QgYSBzaW1wbGUgZXhhbXBsZSBvZiBob3cgYGdhdGhlcmAgY2FuIGJlIHVzZWQgdG8gdHVybiBhIHNvLWNhbGxlZCAqd2lkZSogZGF0YSBzZXQgaW50byBhCipuYXJyb3cqIGZvcm0uIFNpbWlsYXJseSwgdGhlIGNvbW1hbmQgYHNwcmVhZGAgY2FuIGJlIHVzZWQgdG8gZG8gdGhlIHJldmVyc2UuIFRoYXQgaXMgYmV5b25kIG91ciBjdXJyZW50CnNjb3BlLCBidXQgY2hlY2sgdGhlIGRvY3VtZW50YXRpb24gZm9yIGRldGFpbHMuCgoKCgoKCgoKCg==