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)
── Attaching packages ────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 3.0.0     ✔ purrr   0.2.5
✔ tibble  1.4.2     ✔ dplyr   0.7.6
✔ tidyr   0.8.1     ✔ stringr 1.3.1
✔ readr   1.1.1     ✔ forcats 0.3.0
── Conflicts ───────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(foreign)

A quick introduction to ggplot

We will be using the commands in the ggplot2 package for the descriptive statistics plots. There are many options to create nice looking graphs in R, including the functionality in base R, but we chose ggplot2 for its clean logic and its implementation of a grammar for graphics.1

An in-depth introduction to ggplot is beyond the current scope, but a quick overview can be found in the Data Visualization chapter of Wickham and Grolemund’s R for Data Science book, and full details are covered in Wickham’s ggplot2: elegant graphics for data analysis (2nd Edition) (Springer Verlag, 2016).

The logic behind ggplot is an implementation of Wilkinson’s grammar for graphics, using the concept of layers. These are the components that make up a plot, such as a data set, aesthetic mappings (variables for different aspects of the graph, such as the x and y-axes, colors, shapes, etc.), statistical transformations, a geometric object and position adjustments. Several layers can be drawn on top of each other, providing the ability to create incredibly complex graphs.

For now, the main parts to concentrate on are the data set and the aesthetics, or aes. The latter are typically (at least) the variables to be plotted. These are usually declared in the main ggplot command, e.g., ggplot(dataset,aes(x=var1,y=var2)) and apply to all the following layers. However, they can also be specified for each layer individually.

Next follow one or more geometric objects, geom_* and various adjustments, added to the first command by means of a plus sign.

The terminology may seem a little unfamiliar at first, but as long as you remember that aes are the variables and the geom_* are the plot types, you will be on your way.

Fundamentals - A Scatter Plot

The scatter plot shows the relationship between two variables as points with cartesian (x, y) coordinates matching the value for each variable, one on the x-axis, the other on the y-axis.

Reading in the data

First, we use read.dbf from the foreign package to read the data from NYC_Sub_borough_Area.dbf into a data frame nyc.data.

nyc.data <- read.dbf("NYC_Sub_borough_Area.dbf")

Next, we turn this into a tibble (not absolutely necessary, but useful) using as_tibble and assign it back to the same object.

nyc.data <- as_tibble(nyc.data)

We can now print out the contents (there are 55 observations and 34 variables).

nyc.data

We check the variables using the names command.

names(nyc.data)
 [1] "bor_subb"   "name"       "code"       "subborough"
 [5] "forhis06"   "forhis07"   "forhis08"   "forhis09"  
 [9] "forwh06"    "forwh07"    "forwh08"    "forwh09"   
[13] "hhsiz1990"  "hhsiz00"    "hhsiz02"    "hhsiz05"   
[17] "hhsiz08"    "kids2000"   "kids2005"   "kids2006"  
[21] "kids2007"   "kids2008"   "kids2009"   "rent2002"  
[25] "rent2005"   "rent2008"   "rentpct02"  "rentpct05" 
[29] "rentpct08"  "pubast90"   "pubast00"   "yrhom02"   
[33] "yrhom05"    "yrhom08"   

We will start by using the variables kids2000 (percentage households with children under 18 in 2000) and pubast00 (percentage households receiving public assistance in 2000). Before we move to the graphs, we check out the characteristics of their distribution using the familiar summary command. Remember, to pass a variable to summary you need to identify it with its data frame, either using the [[ ]] notation, or the $ notation. Using the latter yields:

summary(nyc.data$kids2000)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  8.382  30.301  38.228  36.040  42.773  55.367 
summary(nyc.data$pubast00)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.8981  3.3860  6.8781  8.4372 11.5369 23.4318 

Data - the data frame

The first aspect that needs to be set for any graph is which data frame contains the variables to be plotted. This is passed as the first argument to the ggplot command, as ggplot(data=dataframe, ... ), ignoring all other arguments for now (by itself, this command will not do anything). For simplicity sake, the argument data is often not mentioned explicitly, but for now we will spell out everything in full. In our example, the argument would be data=nyc.data.

Aesthetics - the variables

The aesthetics, or aes are the basic visual ways in which values that correspond to given variables are represented in a two-dimensional graph (on the screen or on paper). These include the position according to the x and y axis, a size, a color, and a shape. All these elements can be associated with a variable, or set to a constant.

The aesthetics are set by passing the required arguments to the expression aes( ) in the parentheses (it is easy to forget this initially). For example, for our scatter plot, we will set the x-axis to kids2000 and the y-axis to pubast00, as in aes(x = kids2000,y = pubast00). Note that you don’t need to put parentheses around the variable names (that is a characteristics of all functions in the tidyverse, as we saw for the data frame manipulation).

For now, we will just use these two aspects of the graph. We will return to color, size and shape later.

Geom - the type of graph

So far, we have just set up the main parameters for a graph. These are passed as arguments to the ggplot command. Think of them as the rules that apply to all the layers in the graph. The graph itself is constructed by adding different layers, each corresponding to a geometric object or to other aspects of the graph, such as labels, text annotations, styles, etc.

Each type of geometric object is given by a geom_xxx command. Each of these geom functions has built-in options to compute statistics, and, if needed, transformations, to be able to map the values in the variables to a geometric object in the graph.

For the scatter plot, the geometric object is a point, corresponding to the location for the x and y variable, represented by the geom_point geom.

A basic scatter plot

We are now ready to construct our first graph, a bare bones scatter plot. In ggplot the different layers are added together by means of a + symbol. For the scatter plot, we have two elements: the main ggplot command that specifies the data frame and variables for the x and y axes (the aes); and the command to specify geom_point. Make sure to have the + sign at the end of the line (and not on the next line), and don’t forget the parentheses following geom_point( ). If all goes well, you will create the default scatter plot.

ggplot(data=nyc.data,aes(x = kids2000,y = pubast00)) +
  geom_point()

Color

While the scatter plot in essence depicts two variables, it is often useful to distinguish between groups of observations (in spatial lingo, we will call this spatial heterogeneity). There are several ways to implement this in ggplot, but first we will illustrate the use of color (we will consider shape and size next).

Color is an aesthetic. It can be set to a constant, or matched with a variable. In the case of a constant, we simply set it to an accepted color value, such as in color = "red". Typically, it is specified as part of the geom to which it pertains.

Color as a constant

Things can get a bit confusing, since the constant color can be specified as an option of the geom, without specifying the aes. For example, setting geom_point(color="red") will make all the points red.

ggplot(data=nyc.data,aes(x=kids2000,y=pubast00)) +
  geom_point(color="red")

Note the subtle difference in thinking with setting aes(color="red") for geom_point. The result is the same, but in essence now we don’t consider the color to be an option, but one of the aesthetics assigned to a constant value instead of to a variable. Remember to use the quotes around red. Otherwise, red is considered to be a variable, and since there is no such variable in our data frame, it will generate an error.

ggplot(data=nyc.data,aes(x = kids2000,y = pubast00)) + 
  geom_point(aes(color="red"))

Notice the difference in that now a legend is added with the value for the color (later, we will see how to customize the heading for the legend). This highlights how color is an aesthetic, in this case mapped to a constant value.

Color as a variable

We can now use the same principle to tie the value of color to a variable instead of a constant. For example, say we want to distinguish between the points corresponding to sub-boroughs in the Bronx and Manhatten and those in the rest of the city. We assign the aesthetic color to the variable manbronx (note, no quotes since it is a variable name that is in the data frame).

ggplot(data=nyc.data,aes(x = kids2000, y = pubast00)) +
  geom_point(aes(color=manbronx))

Now, the legend heading is manbronx (the variable we specified) and the value for the two colors is given.

Alternatively, we could also have specified the color aesthetic in the main ggplot command, by means of ggplot(data=nyc.data,aes(x = kids2000, y = pubast00, color=manbronx)).

ggplot(data=nyc.data,aes(x = kids2000, y = pubast00, color=manbronx)) +
  geom_point()

The result is the same, but there is an important difference. By specifying the color aesthetic in the main command, we specify it for all following layers. In our simple example, there is only one layer, so it doesn’t really make a difference, but typically it makes more sense to specify aesthetics other than x and y specific to each geom. As we will see later, a graph is made up of several geoms, each potentially with their own aesthetics.

Saving a Graph

Writing a graph to a file

In our discussion so far, the graphs are drawn to the screen and then disappear. To save a ggplot graph to a file for publication, there are two ways to proceed. One is the classic R approach, in which first a device is opened, e.g., by means of a pdf command for a pdf file (each type of file has its own command). Next, the plot commands are entered, and finally the device is turned off by means of dev.off().

Note that it is always a good idea to specify the dimension of the graph (in inches). If not, the results can be unexpected.

For example, to save our scatter plot in a pdf file scatter1.pdf with a height and width of 3 inches, we first set pdf("scatter1.pdf",height=3,width=3). Next we enter our usual ggplot commands. Note that nothing will appear on the screen. Finally, we turn the “device” (i.e., the file) off by means of dev.off().

pdf("scatter1.pdf",height=3,width=3)
g + geom_point(aes(color=manbronx))
dev.off()
null device 
          1 

`The pdf file will be added to your working directory.

Saving with ggsave

In addition to the standard R approach, ggplot also has the ggsave command, which does the same thing. It requires the name for the output file, but derives the proper format from the file extension. For example, an output file with a png file extension will create a png file, and similarly for pdf, etc.

The second argument specifies the plot. It is optional, and when not specified, the last plot is saved. Again, it is a good idea to specify the width and height (in inches). In addition, for raster files, the dots per inch (dpi) can be set as well. The default is 300, which is fine for most use cases, but for high resolution graphs, one can set the dpi to 600.

For example, we assign our scatter plot to scat1, and then save it using ggsave("scatter2.png",scat1,height=3,width=3,dpi=600):

scat1 <- g + geom_point(aes(color=manbronx))
ggsave("scatter2.png",scat1,height=3,width=3,dpi=600)

The file will be added to the working directory.

To see the plot on your screen, simply type its name (scat1).

scat1

Saving an R object

Finally, yet another approach to keep a plot object is to assign the plot commands to a variable and then save this to disk, using the standard R command saveRDS. This can later be brought back into an R session using readRDS. To save the plot, we need to specify a file name with an .rds file extension, for example, scatter1.rds.

saveRDS(scat1,"scatter1.rds")

At some later point (or in a different R session), we can then read the object with readRDS and plot it. Note that we do not need to assign it to the same variable name as before. For example, here we call the graph object newplot.

newplot <- readRDS("scatter1.rds")
newplot

Shape

A further aesthetic is the shape of the geometric object. R has a number of pre-defined shapes with (to the novice) obscure codes, assigned by the pch command.2

Again, we can assign the aesthetic shape to either a constant (one of the codes) or to a variable.

Setting the shape to a constant

Similar to how we set the color above, we can add an option shape=17 to the geom_point command. This will generate a scatter plot with the points represented as triangles.

g + geom_point(shape=12)

Setting the shape to a variable

Again, similar to how we operated above for the color option, we can assign the aesthetic shape to a variable. However, unlike what works for a constant, we cannot just specify shape=manbronx for example. Instead, we need to explicitly enclose this as an option to the aesthetic, as aes(shape=manbronx). The result yields a scatter plot with dots for Rest and triangles for Select, with a legend showing how the shapes are assigned to different values of the variable manbronx.

g + geom_point(aes(shape=manbronx))

Now, we can get a bit fancier and assign both color and shape to manbronx.

g + geom_point(aes(color=manbronx, shape=manbronx))

Size

As before, we can assign the size aesthetic to a constant or to a variable.

Setting size to a constant

We may decide that the default size of the points is not appropriate for our purposes, and we can set it as an option to a different value (a multiple of the default). For example, to make the points three times as large, we set size=3 as an option to geom_point.

g + geom_point(aes(color=manbronx),size=3)

Setting size to a variable

We can also specify the size as an aesthetic (i.e., in the aes specification) to vary with the values of a variable. However, this does not always work as expected. For example, if we set size = manbronx, we get a warning that Using size for a discrete varible is not advised, as below.

g + geom_point(aes(color=manbronx,size=manbronx))

There are other ways to set the size for specific values of a discrete variable, but that’s beyond our current scope.

A bubble plot

When we set the size aesthetic to a continuous variable, we can visualize the interaction among three variables, in a so-called bubble plot. For example, with color=manbronx, as before, and now size=hhsize00 (median household size in 2000), we obtain the following bubble plot.

g + geom_point(aes(color=manbronx,size=hhsiz00))

As is no surprise, the size of the circles grows roughly with the percentage households with kids, which is to be expected.

Smoothing the Scatter Plot

Showing all the points in a scatter plot is fine, but we are typically interested in detecting broad patterns in the data. To that effect, we smooth the data points by fitting a curve to them.

In ggplot, this is implemented through the geom_smooth command. There are two main methods (other custom methods can be added), i.e., a linear fit and a local regression or loess fit. The latter follows local changes in the association between x and y more closely. We consider each in turn.

Linear fit

A linear fit is obtained by means of the method="lm" (note the quotes around lm). This is a classical least squares fit. However, this is not the default. If no method is specified for geom_smooth, the loess method will be used.

Also, by default, an error band is shown in gray corresponding to the standard error of the fit (a wider band means less precise prediction). We use the default setting for geom_point and add (+) another layer with `geom_smooth(method=“lm”).

g + geom_point() +
  geom_smooth(method="lm")

We can always change the default settings by specifying some options explicitly. For example, to change the color of the line to “red” and to turn off the error band, we use geom_smooth(method="lm",color="red",se=FALSE) (don’t forget the quotes around red).

g + geom_point() +
  geom_smooth(method="lm",color="red",se=FALSE)

Subplots

In the same way as we assigned a color aesthetic to the points, we can also assign a color aesthetic to the linear fit. This will result in a separate fit for each of the categories, with a separate color for each. For example, we set aes(color=manbronx) in both the geom_point and geom_smooth layer. This yields not only different colors for the points, but also for the corresponding linear fits: one fit for each subgroup in the data.

g + geom_point(aes(color=manbronx)) +
  geom_smooth(aes(color=manbronx),method="lm",se=FALSE)

Loess fit

Finally, the local regression fit, which is the default. As a result, we don’t need to specify any options in the geom_smooth command (but don’t forget the parentheses).

g + geom_point() +
  geom_smooth()

Again, we can use the options and aesthetics to customize the graph. For example, using the loess fit with different colors for the two subsets of the data (and without the standard error) yields:

g + geom_point(aes(color=manbronx)) +
  geom_smooth(aes(color=manbronx),se=FALSE)

Extra - setting the bandwidth for the loess smooth

As in any local regression method, an important parameter is how much of the data is used in the local fit, the so-called span. This is typically set to 2/3 of the data by default. A narrower span will yield a spikier curve that emphasizes local changes.

For example, we can illustrate the difference between the default and a smoother with a span = 0.4 and one with a span=0.9. We turn off the confidence interval by setting se = FALSE.

g + geom_point() +
  geom_smooth(se=FALSE) +
  geom_smooth(color="red",span=0.4,se=FALSE) +
  geom_smooth(color="green",span=0.9,se=FALSE)

This also highlights how a graph is constructed by adding layers. You can change the order in which the layers are drawn by changing their position in the command. For example, to have the default curve on top, we add it as the last layer.

g + geom_point() +
  geom_smooth(color="red",span=0.4,se=FALSE) +
  geom_smooth(color="green",span=0.9,se=FALSE) +
  geom_smooth(se=FALSE)


  1. Note that, strictly speaking, the package is ggplot2, i.e., the second iteration of the ggplot package, but the commands use ggplot. From now on, I will use ggplot to refer to both.

  2. For a full list of the codes, see, for example, the sthda site.

LS0tCnRpdGxlOiAiU3RhdGlzdGljYWwgR3JhcGhzICgxKSIKYXV0aG9yOiAiTHVjIEFuc2VsaW4iCmRhdGU6ICIxMC8yNi8yMDE4IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyBQcmVsaW1pbmFyaWVzCgpNYWtlIHN1cmUgdG8gbW92ZSB0byB0aGUgcHJvcGVyIHdvcmtpbmcgZGlyZWN0b3J5IGFuZCB0aGF0IHRoZSBmaWxlIAoqKk5ZQ19TdWJfYm9yb3VnaF9BcmVhLmRiZioqIGlzIGluIHRoYXQgZGlyZWN0b3J5LgoKQWxzbywgYWN0aXZhdGUgdGhlICoqdGlkeXZlcnNlKiogYW5kICoqZm9yZWlnbioqIHBhY2thZ2VzIHVzaW5nIHRoZSBgbGlicmFyeWAgY29tbWFuZAoodGhlICoqdGlkeXZlcnNlKiogcGFja2FnZSBpbmNsdWRlcyAqKmdncGxvdDIqKik6CgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZm9yZWlnbikKYGBgCgojIyBBIHF1aWNrIGludHJvZHVjdGlvbiB0byAqKmdncGxvdCoqIApXZSB3aWxsIGJlIHVzaW5nIHRoZSBjb21tYW5kcyBpbiB0aGUgKipnZ3Bsb3QyKiogcGFja2FnZSBmb3IgdGhlIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgcGxvdHMuIFRoZXJlIGFyZSBtYW55IG9wdGlvbnMgdG8gY3JlYXRlIG5pY2UgbG9va2luZyBncmFwaHMgaW4gUiwgaW5jbHVkaW5nIHRoZSBmdW5jdGlvbmFsaXR5IGluIGJhc2UgUiwgYnV0IHdlIGNob3NlICoqZ2dwbG90MioqIGZvciBpdHMgY2xlYW4gbG9naWMgYW5kIGl0cyBpbXBsZW1lbnRhdGlvbiBvZiBhIGdyYW1tYXIgZm9yIGdyYXBoaWNzLl5bTm90ZSB0aGF0LCBzdHJpY3RseSBzcGVha2luZywgdGhlIHBhY2thZ2UgaXMgKipnZ3Bsb3QyKiosIGkuZS4sIHRoZSBzZWNvbmQgaXRlcmF0aW9uIG9mIHRoZSAqKmdncGxvdCoqIHBhY2thZ2UsIGJ1dCB0aGUgY29tbWFuZHMgdXNlICoqZ2dwbG90KiouIEZyb20gbm93IG9uLCBJIHdpbGwgdXNlICoqZ2dwbG90KiogdG8gcmVmZXIgdG8gYm90aC5dCgpBbiBpbi1kZXB0aCBpbnRyb2R1Y3Rpb24gdG8gKipnZ3Bsb3QqKiBpcyBiZXlvbmQgdGhlIGN1cnJlbnQgc2NvcGUsIGJ1dCBhIHF1aWNrIG92ZXJ2aWV3CmNhbiBiZSBmb3VuZCBpbiB0aGUgW0RhdGEgVmlzdWFsaXphdGlvbl0oaHR0cDovL3I0ZHMuaGFkLmNvLm56L2RhdGEtdmlzdWFsaXNhdGlvbi5odG1sKSBjaGFwdGVyIG9mIFdpY2toYW0gYW5kIEdyb2xlbXVuZCdzICpSIGZvciBEYXRhIFNjaWVuY2UqIGJvb2ssIGFuZCBmdWxsIGRldGFpbHMKYXJlIGNvdmVyZWQgaW4gV2lja2hhbSdzICpnZ3Bsb3QyOiBlbGVnYW50IGdyYXBoaWNzIGZvciBkYXRhIGFuYWx5c2lzICgybmQgRWRpdGlvbikqIChTcHJpbmdlciBWZXJsYWcsIDIwMTYpLgoKVGhlIGxvZ2ljIGJlaGluZCAqKmdncGxvdCoqIGlzIGFuIGltcGxlbWVudGF0aW9uIG9mIFdpbGtpbnNvbidzICpncmFtbWFyIGZvciBncmFwaGljcyosCnVzaW5nIHRoZSBjb25jZXB0IG9mICpsYXllcnMqLiBUaGVzZSBhcmUgdGhlIGNvbXBvbmVudHMgdGhhdCBtYWtlIHVwIGEgcGxvdCwgc3VjaCBhcyBhICpkYXRhIHNldCosICphZXN0aGV0aWMgbWFwcGluZ3MqICh2YXJpYWJsZXMgZm9yIGRpZmZlcmVudCBhc3BlY3RzIG9mIHRoZSBncmFwaCwgc3VjaAphcyB0aGUgeCBhbmQgeS1heGVzLCBjb2xvcnMsIHNoYXBlcywgZXRjLiksICpzdGF0aXN0aWNhbCB0cmFuc2Zvcm1hdGlvbnMqLCBhCipnZW9tZXRyaWMgb2JqZWN0KiBhbmQgcG9zaXRpb24gYWRqdXN0bWVudHMuIFNldmVyYWwgbGF5ZXJzIGNhbiBiZSBkcmF3biBvbiB0b3Agb2YgZWFjaCBvdGhlciwgcHJvdmlkaW5nIHRoZSBhYmlsaXR5IHRvIGNyZWF0ZSBpbmNyZWRpYmx5IGNvbXBsZXggZ3JhcGhzLgoKRm9yIG5vdywgdGhlIG1haW4gcGFydHMgdG8gY29uY2VudHJhdGUgb24gYXJlIHRoZSBkYXRhIHNldCBhbmQgdGhlIGFlc3RoZXRpY3MsCm9yIGBhZXNgLiBUaGUgbGF0dGVyIGFyZSB0eXBpY2FsbHkgKGF0IGxlYXN0KSB0aGUgdmFyaWFibGVzIHRvIGJlIHBsb3R0ZWQuIFRoZXNlCmFyZSB1c3VhbGx5IGRlY2xhcmVkIGluIHRoZSBtYWluICoqZ2dwbG90KiogY29tbWFuZCwgZS5nLiwgYGdncGxvdChkYXRhc2V0LGFlcyh4PXZhcjEseT12YXIyKSlgIGFuZCBhcHBseSB0byBhbGwgdGhlIGZvbGxvd2luZyBsYXllcnMuIEhvd2V2ZXIsCnRoZXkgY2FuIGFsc28gYmUgc3BlY2lmaWVkIGZvciBlYWNoIGxheWVyIGluZGl2aWR1YWxseS4KCk5leHQgZm9sbG93IG9uZSBvciBtb3JlIGdlb21ldHJpYyBvYmplY3RzLCBgZ2VvbV8qYCBhbmQgdmFyaW91cyBhZGp1c3RtZW50cywgCmFkZGVkIHRvIHRoZSBmaXJzdCBjb21tYW5kIGJ5IG1lYW5zIG9mIGEgcGx1cyBzaWduLgoKVGhlIHRlcm1pbm9sb2d5IG1heSBzZWVtIGEgbGl0dGxlIHVuZmFtaWxpYXIgYXQgZmlyc3QsIGJ1dCBhcyBsb25nIGFzIHlvdSByZW1lbWJlciB0aGF0CmBhZXNgIGFyZSB0aGUgdmFyaWFibGVzIGFuZCB0aGUgYGdlb21fKmAgYXJlIHRoZSBwbG90IHR5cGVzLCB5b3Ugd2lsbCBiZSBvbiB5b3VyIHdheS4KCiMjIEZ1bmRhbWVudGFscyAtIEEgU2NhdHRlciBQbG90CgpUaGUgc2NhdHRlciBwbG90IHNob3dzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0d28gdmFyaWFibGVzIGFzIHBvaW50cyB3aXRoIGNhcnRlc2lhbiAKKHgsIHkpIGNvb3JkaW5hdGVzIG1hdGNoaW5nIHRoZSB2YWx1ZSBmb3IgZWFjaCB2YXJpYWJsZSwgb25lIG9uIHRoZSB4LWF4aXMsIHRoZQpvdGhlciBvbiB0aGUgeS1heGlzLiAKCiMjIyBSZWFkaW5nIGluIHRoZSBkYXRhCgpGaXJzdCwgd2UgdXNlIGByZWFkLmRiZmAgZnJvbSB0aGUgKipmb3JlaWduKiogcGFja2FnZSB0byByZWFkIHRoZSBkYXRhIApmcm9tICoqTllDX1N1Yl9ib3JvdWdoX0FyZWEuZGJmKiogaW50byBhIGRhdGEgZnJhbWUgKipueWMuZGF0YSoqLgoKYGBge3J9Cm55Yy5kYXRhIDwtIHJlYWQuZGJmKCJOWUNfU3ViX2Jvcm91Z2hfQXJlYS5kYmYiKQpgYGAKCk5leHQsIHdlIHR1cm4gdGhpcyBpbnRvIGEgdGliYmxlIChub3QgYWJzb2x1dGVseSBuZWNlc3NhcnksIGJ1dCB1c2VmdWwpIHVzaW5nCmBhc190aWJibGVgIGFuZCBhc3NpZ24gaXQgYmFjayB0byB0aGUgc2FtZSBvYmplY3QuCgpgYGB7cn0KbnljLmRhdGEgPC0gYXNfdGliYmxlKG55Yy5kYXRhKQpgYGAKCldlIGNhbiBub3cgcHJpbnQgb3V0IHRoZSBjb250ZW50cyAodGhlcmUgYXJlIDU1IG9ic2VydmF0aW9ucyBhbmQgMzQgdmFyaWFibGVzKS4KCmBgYHtyfQpueWMuZGF0YQpgYGAKCldlIGNoZWNrIHRoZSB2YXJpYWJsZXMgdXNpbmcgdGhlIGBuYW1lc2AgY29tbWFuZC4KCmBgYHtyfQpuYW1lcyhueWMuZGF0YSkKYGBgCgpXZSB3aWxsIHN0YXJ0IGJ5IHVzaW5nIHRoZSB2YXJpYWJsZXMgKipraWRzMjAwMCoqIChwZXJjZW50YWdlIGhvdXNlaG9sZHMgd2l0aCBjaGlsZHJlbiB1bmRlciAxOCBpbiAyMDAwKSBhbmQKKipwdWJhc3QwMCoqIChwZXJjZW50YWdlIGhvdXNlaG9sZHMgcmVjZWl2aW5nIHB1YmxpYyBhc3Npc3RhbmNlIGluIDIwMDApLiBCZWZvcmUgd2UgbW92ZSB0byB0aGUgCmdyYXBocywgd2UgY2hlY2sgb3V0IHRoZSBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlaXIgZGlzdHJpYnV0aW9uIHVzaW5nIHRoZSBmYW1pbGlhciBgc3VtbWFyeWAgY29tbWFuZC4KUmVtZW1iZXIsIHRvIHBhc3MgYSB2YXJpYWJsZSB0byBgc3VtbWFyeWAgeW91IG5lZWQgdG8gaWRlbnRpZnkgaXQgd2l0aCBpdHMgZGF0YSBmcmFtZSwgZWl0aGVyIHVzaW5nCnRoZSBbWyBdXSBub3RhdGlvbiwgb3IgdGhlICQgbm90YXRpb24uIFVzaW5nIHRoZSBsYXR0ZXIgeWllbGRzOgoKYGBge3J9CnN1bW1hcnkobnljLmRhdGEka2lkczIwMDApCmBgYAoKYGBge3J9CnN1bW1hcnkobnljLmRhdGEkcHViYXN0MDApCmBgYAoKIyMjIERhdGEgLSB0aGUgZGF0YSBmcmFtZQoKVGhlIGZpcnN0IGFzcGVjdCB0aGF0IG5lZWRzIHRvIGJlIHNldCBmb3IgYW55IGdyYXBoIGlzIHdoaWNoIGRhdGEgZnJhbWUgY29udGFpbnMKdGhlIHZhcmlhYmxlcyB0byBiZSBwbG90dGVkLiBUaGlzIGlzIHBhc3NlZCBhcyB0aGUgZmlyc3QgYXJndW1lbnQgdG8gdGhlIGBnZ3Bsb3RgCmNvbW1hbmQsIGFzIGBnZ3Bsb3QoZGF0YT1kYXRhZnJhbWUsIC4uLiApYCwgaWdub3JpbmcgYWxsIG90aGVyIGFyZ3VtZW50cyBmb3Igbm93CihieSBpdHNlbGYsIHRoaXMgY29tbWFuZCB3aWxsIG5vdCBkbyBhbnl0aGluZykuIEZvciBzaW1wbGljaXR5IHNha2UsIHRoZSBhcmd1bWVudApgZGF0YWAgaXMgb2Z0ZW4gbm90IG1lbnRpb25lZCBleHBsaWNpdGx5LCBidXQgZm9yIG5vdyB3ZSB3aWxsIHNwZWxsIG91dCBldmVyeXRoaW5nCmluIGZ1bGwuIEluIG91ciBleGFtcGxlLCB0aGUgYXJndW1lbnQgd291bGQgYmUgYGRhdGE9bnljLmRhdGFgLgoKCiMjIyBBZXN0aGV0aWNzIC0gdGhlIHZhcmlhYmxlcwoKVGhlIGFlc3RoZXRpY3MsIG9yIGBhZXNgIGFyZSB0aGUgYmFzaWMgdmlzdWFsIHdheXMgaW4gd2hpY2ggdmFsdWVzIHRoYXQgY29ycmVzcG9uZCB0bwpnaXZlbiB2YXJpYWJsZXMgYXJlIHJlcHJlc2VudGVkIGluIGEgdHdvLWRpbWVuc2lvbmFsIGdyYXBoIChvbiB0aGUgc2NyZWVuIG9yIG9uIHBhcGVyKS4KVGhlc2UgaW5jbHVkZSB0aGUgcG9zaXRpb24gYWNjb3JkaW5nIHRvIHRoZSB4IGFuZCB5IGF4aXMsIGEgc2l6ZSwgYSBjb2xvciwgYW5kIGEgc2hhcGUuCkFsbCB0aGVzZSBlbGVtZW50cyBjYW4gYmUgYXNzb2NpYXRlZCB3aXRoIGEgdmFyaWFibGUsIG9yIHNldCB0byBhIGNvbnN0YW50LgoKVGhlIGFlc3RoZXRpY3MgYXJlIHNldCBieSBwYXNzaW5nIHRoZSByZXF1aXJlZCBhcmd1bWVudHMgdG8gdGhlIGV4cHJlc3Npb24KYGFlcyggIClgIGluIHRoZSBwYXJlbnRoZXNlcyAoaXQgaXMgZWFzeSB0byBmb3JnZXQgdGhpcyBpbml0aWFsbHkpLiBGb3IgZXhhbXBsZSwKZm9yIG91ciBzY2F0dGVyIHBsb3QsIHdlIHdpbGwgc2V0IHRoZSB4LWF4aXMgdG8gKipraWRzMjAwMCoqIGFuZCB0aGUgeS1heGlzCnRvICoqcHViYXN0MDAqKiwgYXMgaW4gYGFlcyh4ID0ga2lkczIwMDAseSA9IHB1YmFzdDAwKWAuIE5vdGUgdGhhdCB5b3UgZG9uJ3QgbmVlZCB0bwpwdXQgcGFyZW50aGVzZXMgYXJvdW5kIHRoZSB2YXJpYWJsZSBuYW1lcyAodGhhdCBpcyBhIGNoYXJhY3RlcmlzdGljcyBvZiBhbGwgZnVuY3Rpb25zCmluIHRoZSB0aWR5dmVyc2UsIGFzIHdlIHNhdyBmb3IgdGhlIGRhdGEgZnJhbWUgbWFuaXB1bGF0aW9uKS4KCkZvciBub3csIHdlIHdpbGwganVzdCB1c2UgdGhlc2UgdHdvIGFzcGVjdHMgb2YgdGhlIGdyYXBoLiBXZSB3aWxsIHJldHVybiB0byBjb2xvciwKc2l6ZSBhbmQgc2hhcGUgbGF0ZXIuCgojIyMgR2VvbSAtIHRoZSB0eXBlIG9mIGdyYXBoCgpTbyBmYXIsIHdlIGhhdmUganVzdCBzZXQgdXAgdGhlIG1haW4gcGFyYW1ldGVycyBmb3IgYSBncmFwaC4gVGhlc2UgYXJlIHBhc3NlZAphcyBhcmd1bWVudHMgdG8gdGhlIGBnZ3Bsb3RgIGNvbW1hbmQuIFRoaW5rIG9mIHRoZW0gYXMgdGhlIHJ1bGVzIHRoYXQgYXBwbHkgdG8gYWxsCnRoZSBsYXllcnMgaW4gdGhlIGdyYXBoLiBUaGUgZ3JhcGggaXRzZWxmIGlzIGNvbnN0cnVjdGVkIGJ5IGFkZGluZyBkaWZmZXJlbnQgbGF5ZXJzLAplYWNoIGNvcnJlc3BvbmRpbmcgdG8gYSBnZW9tZXRyaWMgb2JqZWN0IG9yIHRvIG90aGVyIGFzcGVjdHMgb2YgdGhlIGdyYXBoLCBzdWNoCmFzIGxhYmVscywgdGV4dCBhbm5vdGF0aW9ucywgc3R5bGVzLCBldGMuCgpFYWNoIHR5cGUgb2YgZ2VvbWV0cmljIG9iamVjdCBpcyBnaXZlbiBieSBhIGBnZW9tX3h4eGAgY29tbWFuZC4gRWFjaCBvZiB0aGVzZQpnZW9tIGZ1bmN0aW9ucyBoYXMgYnVpbHQtaW4gb3B0aW9ucyB0byBjb21wdXRlIHN0YXRpc3RpY3MsIGFuZCwgaWYgbmVlZGVkLAp0cmFuc2Zvcm1hdGlvbnMsIHRvIGJlIGFibGUgdG8gbWFwIHRoZSB2YWx1ZXMgaW4gdGhlIHZhcmlhYmxlcyB0byBhIGdlb21ldHJpYwpvYmplY3QgaW4gdGhlIGdyYXBoLgoKRm9yIHRoZSBzY2F0dGVyIHBsb3QsIHRoZSBnZW9tZXRyaWMgb2JqZWN0IGlzIGEgcG9pbnQsIGNvcnJlc3BvbmRpbmcgdG8gdGhlCmxvY2F0aW9uIGZvciB0aGUgeCBhbmQgeSB2YXJpYWJsZSwgcmVwcmVzZW50ZWQgYnkgdGhlIGBnZW9tX3BvaW50YCBnZW9tLgoKIyMjIEEgYmFzaWMgc2NhdHRlciBwbG90CgpXZSBhcmUgbm93IHJlYWR5IHRvIGNvbnN0cnVjdCBvdXIgZmlyc3QgZ3JhcGgsIGEgYmFyZSBib25lcyBzY2F0dGVyIHBsb3QuIEluCioqZ2dwbG90KiogdGhlIGRpZmZlcmVudCBsYXllcnMgYXJlIGFkZGVkIHRvZ2V0aGVyIGJ5IG1lYW5zIG9mIGEgKyBzeW1ib2wuCkZvciB0aGUgc2NhdHRlciBwbG90LCB3ZSBoYXZlIHR3byBlbGVtZW50czogdGhlIG1haW4gYGdncGxvdGAgY29tbWFuZCB0aGF0CnNwZWNpZmllcyB0aGUgZGF0YSBmcmFtZSBhbmQgdmFyaWFibGVzIGZvciB0aGUgeCBhbmQgeSBheGVzICh0aGUgYGFlc2ApOwphbmQgdGhlIGNvbW1hbmQgdG8gc3BlY2lmeSBgZ2VvbV9wb2ludGAuIE1ha2Ugc3VyZSB0byBoYXZlIHRoZSArIHNpZ24gYXQgdGhlCmVuZCBvZiB0aGUgbGluZSAoYW5kIG5vdCBvbiB0aGUgbmV4dCBsaW5lKSwgYW5kIGRvbid0IGZvcmdldCB0aGUgcGFyZW50aGVzZXMKZm9sbG93aW5nIGBnZW9tX3BvaW50KCApYC4gSWYgYWxsIGdvZXMgd2VsbCwgeW91IHdpbGwgY3JlYXRlIHRoZSBkZWZhdWx0IApzY2F0dGVyIHBsb3QuCgpgYGB7cn0KZ2dwbG90KGRhdGE9bnljLmRhdGEsYWVzKHggPSBraWRzMjAwMCx5ID0gcHViYXN0MDApKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKIyMjIyBTaWRlYmFyIDEgLS0gQ3JlYXRpbmcgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZQoKQmVmb3JlIHdlIG1vdmUgb24sIHdlIHdpbGwgY3JlYXRlIGFuIGFkZGl0aW9uYWwgdmFyaWFibGUgdGhhdCB3aWxsIGFsbG93IHVzIHRvCmlsbHVzdHJhdGUgc29tZSBmdXJ0aGVyIGZlYXR1cmVzIG9mIHRoZSBncmFwaC4KCldlIHdpbGwgY3JlYXRlIGEgc2VwYXJhdGUgY2F0ZWdvcnkgZm9yIHRoZSBzdWItYm9yb3VnaHMgaW4gTWFuaGF0dGFuIGFuZCB0aGUgQnJvbnguIE5vcm1hbGx5LAp3ZSB3b3VsZCBkbyB0aGlzIGFzIGEgZ3JhcGhpY2FsIHNlbGVjdGlvbi4gSG93ZXZlciwgYXMgaXQgdHVybnMgb3V0LCB0aGUgc3ViLWJvcm91Z2hzIGluIE1hbmhhdHRhbiBoYXZlIGEgKipjb2RlKiogZnJvbSAzMDEgdG8gMzEwLCBhbmQgdGhlIHN1Yi1ib3JvdWdocwppbiB0aGUgQnJvbnggaGF2ZSBhICoqY29kZSoqIGZyb20gMTAxIHRvIDExMC4gCgpXZSB3aWxsIHByb2NlZWQgYnkgdXNpbmcgdGhlIGBtdXRhdGVgIGNvbW1hbmQgdG8gY3JlYXRlIGEgbmV3IHZhcmlhYmxlICoqbWFuYnJvbngqKiB0aGF0IG1hdGNoZXMgdGhpcyBzZWxlY3Rpb24uIApXZSBhbHNvIHVzZSB0aGUgYGlmX2Vsc2VgIGNvbW1hbmQgZnJvbSAqKmRwbHlyKiogdG8gY3JlYXRlIHZhbHVlcyBmb3IgdGhlIG5ldyB2YXJpYWJsZSBvZiAqKlNlbGVjdCoqIHdoZW4gdGhlIGNvbmRpdGlvbiBpcyB0cnVlLCBhbmQgKipSZXN0Kiogd2hlbiB0aGUgY29uZGl0aW9uIGlzIGZhbHNlLiBUaGUgY29uZGl0aW9uIGNoZWNrcyB3aGV0aGVyIHRoZSB2YWx1ZXMgZm9yICoqY29kZSoqIGFyZSBiZXR3ZWVuIDMwMCBhbmQgMzExICh1c2luZyB0aGUgc3ltYm9sIGAmYCBmb3IgdGhlIGxvZ2ljYWwgKiphbmQqKiksICoqb3IqKiAodXNpbmcgdGhlIHN5bWJvbCBgfGApIGJldHdlZW4KMTAwIGFuZCAxMTEsIHRoZSBjb2RlcyBmb3IgTWFuaGF0dGFuIGFuZCB0aGUgQnJvbnggKGNoZWNrIHRoZSBkb2N1bWVudGF0aW9uIGlmIHlvdSBhcmUgbm90CnN1cmUgaG93IGBpZl9lbHNlYCB3b3JrcykuCgpXZSB1c2UgdGhlIHBpcGUgdG8gcGFzcyB0aGUgZGF0YSBmcmFtZSAqKm55Yy5kYXRhKiogdG8gdGhlIGBtdXRhdGVgIGNvbW1hbmQgYW5kIGFzc2lnbiAgdGhlIHJlc3VsdApiYWNrIHRvICoqbnljLmRhdGEqKiAoaW4gZWZmZWN0IGFkZGluZyB0aGUgdmFyaWFibGUgdG8gdGhlIGRhdGEgZnJhbWUpLgoKYGBge3J9Cm55Yy5kYXRhIDwtIG55Yy5kYXRhICU+JSBtdXRhdGUobWFuYnJvbnggPSBpZl9lbHNlKChjb2RlID4gMzAwICYgY29kZSA8IDMxMSkgfCAoY29kZSA+IDEwMCAmIGNvZGUgPCAxMTEpLCJTZWxlY3QiLCJSZXN0IikpCmBgYAoKQXMgYSBjaGVjaywgd2UgbGlzdCB0aGUgdmFsdWVzCmZvciBvdXIgbmV3IHZhcmlhYmxlIChzaW5jZSB0aGVyZSBhcmUgb25seSA1NSBvYnNlcnZhdGlvbnMsIHRoaXMgaXMgbm90IHRvbyBvbmVyb3VzKS4KCmBgYHtyfQpueWMuZGF0YSRtYW5icm9ueApgYGAKCiMjIENvbG9yCgpXaGlsZSB0aGUgc2NhdHRlciBwbG90IGluIGVzc2VuY2UgZGVwaWN0cyB0d28gdmFyaWFibGVzLCBpdCBpcyBvZnRlbiB1c2VmdWwgdG8gZGlzdGluZ3Vpc2gKYmV0d2VlbiBncm91cHMgb2Ygb2JzZXJ2YXRpb25zIChpbiBzcGF0aWFsIGxpbmdvLCB3ZSB3aWxsIGNhbGwgdGhpcyBzcGF0aWFsIGhldGVyb2dlbmVpdHkpLgpUaGVyZSBhcmUgc2V2ZXJhbCB3YXlzIHRvIGltcGxlbWVudCB0aGlzIGluICoqZ2dwbG90KiosIGJ1dCBmaXJzdCB3ZSB3aWxsIGlsbHVzdHJhdGUgdGhlCnVzZSBvZiBgY29sb3JgICh3ZSB3aWxsIGNvbnNpZGVyIGBzaGFwZWAgYW5kIGBzaXplYCBuZXh0KS4KCkNvbG9yIGlzIGFuIGFlc3RoZXRpYy4gSXQgY2FuIGJlIHNldCB0byBhIGNvbnN0YW50LCBvciBtYXRjaGVkIHdpdGggYSB2YXJpYWJsZS4KSW4gdGhlIGNhc2Ugb2YgYSBjb25zdGFudCwgd2Ugc2ltcGx5IHNldCBpdCB0byBhbiBhY2NlcHRlZCBjb2xvciB2YWx1ZSwgc3VjaCBhcyBpbiBgY29sb3IgPSAicmVkImAuClR5cGljYWxseSwgaXQgaXMgc3BlY2lmaWVkIGFzIHBhcnQgb2YgdGhlIGBnZW9tYCB0byB3aGljaCBpdCBwZXJ0YWlucy4KCiMjIyBDb2xvciBhcyBhIGNvbnN0YW50CgpUaGluZ3MgY2FuIGdldCBhIGJpdCBjb25mdXNpbmcsIHNpbmNlIHRoZSBjb25zdGFudCBjb2xvciBjYW4gYmUgc3BlY2lmaWVkIGFzIGFuIG9wdGlvbgpvZiB0aGUgZ2VvbSwgd2l0aG91dCBzcGVjaWZ5aW5nIHRoZSBgYWVzYC4gRm9yIGV4YW1wbGUsIHNldHRpbmcgYGdlb21fcG9pbnQoY29sb3I9InJlZCIpYCB3aWxsCm1ha2UgYWxsIHRoZSBwb2ludHMgcmVkLgoKYGBge3J9CmdncGxvdChkYXRhPW55Yy5kYXRhLGFlcyh4PWtpZHMyMDAwLHk9cHViYXN0MDApKSArCiAgZ2VvbV9wb2ludChjb2xvcj0icmVkIikKYGBgCgpOb3RlIHRoZSBzdWJ0bGUgZGlmZmVyZW5jZSBpbiB0aGlua2luZyB3aXRoIHNldHRpbmcgYGFlcyhjb2xvcj0icmVkIilgIGZvciBgZ2VvbV9wb2ludGAuIFRoZSByZXN1bHQKaXMgdGhlIHNhbWUsIGJ1dCBpbiBlc3NlbmNlIG5vdyB3ZSBkb24ndCBjb25zaWRlciB0aGUgY29sb3IgdG8gYmUgYW4gb3B0aW9uLCBidXQgb25lIG9mIHRoZQphZXN0aGV0aWNzIGFzc2lnbmVkIHRvIGEgY29uc3RhbnQgdmFsdWUgaW5zdGVhZCBvZiB0byBhIHZhcmlhYmxlLiBSZW1lbWJlciB0byB1c2UgdGhlIHF1b3RlcyBhcm91bmQKcmVkLiBPdGhlcndpc2UsIGByZWRgIGlzIGNvbnNpZGVyZWQgdG8gYmUgYSB2YXJpYWJsZSwgYW5kIHNpbmNlIHRoZXJlIGlzIG5vIHN1Y2ggdmFyaWFibGUgaW4gCm91ciBkYXRhIGZyYW1lLCBpdCB3aWxsIGdlbmVyYXRlIGFuIGVycm9yLgoKYGBge3J9CmdncGxvdChkYXRhPW55Yy5kYXRhLGFlcyh4ID0ga2lkczIwMDAseSA9IHB1YmFzdDAwKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvcj0icmVkIikpCmBgYAoKTm90aWNlIHRoZSBkaWZmZXJlbmNlIGluIHRoYXQgbm93IGEgbGVnZW5kIGlzIGFkZGVkIHdpdGggdGhlIHZhbHVlIGZvciB0aGUgY29sb3IKKGxhdGVyLCB3ZSB3aWxsIHNlZSBob3cgdG8gY3VzdG9taXplIHRoZSBoZWFkaW5nIGZvciB0aGUgbGVnZW5kKS4gVGhpcyBoaWdobGlnaHRzCmhvdyBgY29sb3JgIGlzIGFuIGFlc3RoZXRpYywgaW4gdGhpcyAgY2FzZSBtYXBwZWQgdG8gYSBjb25zdGFudCB2YWx1ZS4KCiMjIyBDb2xvciBhcyBhIHZhcmlhYmxlCgpXZSBjYW4gbm93IHVzZSB0aGUgc2FtZSBwcmluY2lwbGUgdG8gdGllIHRoZSB2YWx1ZSBvZiBgY29sb3JgIHRvIGEgdmFyaWFibGUgaW5zdGVhZApvZiBhIGNvbnN0YW50LiBGb3IgZXhhbXBsZSwgc2F5IHdlIHdhbnQgdG8gZGlzdGluZ3Vpc2ggYmV0d2VlbiB0aGUgcG9pbnRzCmNvcnJlc3BvbmRpbmcgdG8gc3ViLWJvcm91Z2hzIGluIHRoZSBCcm9ueCBhbmQgTWFuaGF0dGVuIGFuZCB0aG9zZSBpbiB0aGUgcmVzdCBvZiB0aGUgY2l0eS4KV2UgYXNzaWduIHRoZSBhZXN0aGV0aWMgYGNvbG9yYCB0byB0aGUgdmFyaWFibGUgbWFuYnJvbnggKG5vdGUsIG5vIHF1b3RlcyBzaW5jZSBpdAppcyBhIHZhcmlhYmxlIG5hbWUgdGhhdCBpcyBpbiB0aGUgZGF0YSBmcmFtZSkuCgpgYGB7cn0KZ2dwbG90KGRhdGE9bnljLmRhdGEsYWVzKHggPSBraWRzMjAwMCwgeSA9IHB1YmFzdDAwKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPW1hbmJyb254KSkKYGBgCgpOb3csIHRoZSBsZWdlbmQgaGVhZGluZyBpcyAqKm1hbmJyb254KiogKHRoZSB2YXJpYWJsZSB3ZSBzcGVjaWZpZWQpIGFuZCB0aGUgdmFsdWUgZm9yCnRoZSB0d28gY29sb3JzIGlzIGdpdmVuLgoKQWx0ZXJuYXRpdmVseSwgd2UgY291bGQgYWxzbyBoYXZlIHNwZWNpZmllZCB0aGUgYGNvbG9yYCBhZXN0aGV0aWMgaW4gdGhlIG1haW4gYGdncGxvdGAKY29tbWFuZCwgYnkgbWVhbnMgb2YgYGdncGxvdChkYXRhPW55Yy5kYXRhLGFlcyh4ID0ga2lkczIwMDAsIHkgPSBwdWJhc3QwMCwgY29sb3I9bWFuYnJvbngpKWAuCgpgYGB7cn0KZ2dwbG90KGRhdGE9bnljLmRhdGEsYWVzKHggPSBraWRzMjAwMCwgeSA9IHB1YmFzdDAwLCBjb2xvcj1tYW5icm9ueCkpICsKICBnZW9tX3BvaW50KCkKYGBgCgpUaGUgcmVzdWx0IGlzIHRoZSBzYW1lLCBidXQgdGhlcmUgaXMgYW4gaW1wb3J0YW50IGRpZmZlcmVuY2UuIEJ5IHNwZWNpZnlpbmcgdGhlIGNvbG9yCmFlc3RoZXRpYyBpbiB0aGUgbWFpbiBjb21tYW5kLCB3ZSBzcGVjaWZ5IGl0IGZvciBhbGwgZm9sbG93aW5nIGxheWVycy4gSW4gb3VyIHNpbXBsZQpleGFtcGxlLCB0aGVyZSBpcyBvbmx5IG9uZSBsYXllciwgc28gaXQgZG9lc24ndCByZWFsbHkgbWFrZSBhIGRpZmZlcmVuY2UsIGJ1dCB0eXBpY2FsbHkKaXQgbWFrZXMgbW9yZSBzZW5zZSB0byBzcGVjaWZ5IGFlc3RoZXRpY3Mgb3RoZXIgdGhhbiB4IGFuZCB5ICBzcGVjaWZpYyB0byBlYWNoIGdlb20uCkFzIHdlIHdpbGwgc2VlIGxhdGVyLCBhIGdyYXBoIGlzIG1hZGUgdXAgb2Ygc2V2ZXJhbCBnZW9tcywgZWFjaCBwb3RlbnRpYWxseSB3aXRoCnRoZWlyIG93biBhZXN0aGV0aWNzLgoKIyMjIyBTaWRlYmFyIDIgLS0gYXNzaWduaW5nIChwYXJ0IG9mKSBhIGdyYXBoIHRvIGFuIG9iamVjdAoKQnkgbm93LCB5b3UgbWF5IGhhdmUgYmVndW4gdG8gZ2V0IGEgbGl0dGxlIHRpcmVkIG9mIHJldHlwaW5nIHRoZSBmaXJzdCBsaW5lIGluIHRoZSAKYGdncGxvdGAgY29tbWFuZC4gU2luY2UgZXZlcnl0aGluZyBpbiBSIGlzIGFuIG9iamVjdCwgd2UgY2FuIGFzc2lnbiB0aGUgcGxvdCwgb3IgcGFydApvZiBhIHBsb3QgdG8gYSB2YXJpYWJsZS4gVHlwaWNhbGx5LCBpdCBpcyBiZXN0IHRvIGxpbWl0IHRoaXMgdG8gdGhvc2UgcGFydCBvZiB0aGUKZ3JhcGggdGhhdCB3aWxsIGJlIHJlcGVhdGVkIGVhY2ggdGltZSwgYW5kIG5vdCB0byBlYWNoIGluZGl2aWR1YWwgb3B0aW9uIHRoYXQKbWF5IGJlIGNoYW5nZWQgd2hpbGUgZXhwZXJpbWVudGluZy4KCkZvciBleGFtcGxlLCB3ZSBjYW4gYXNzaWduIHRoZSBmaXJzdCBsaW5lIG9mIHRoZSBwbG90IHRvIHRoZSBvYmplY3QgKipnKiosIHNwZWNpZnlpbmcKdGhlIGRhdGEgYW5kIHRoZSB4IGFuZCB5IGFlc3RoZXRpY3MgZm9yIGFsbCBmb2xsb3dpbmcgbGF5ZXJzLgoKYGBge3J9CmcgPC0gZ2dwbG90KGRhdGE9bnljLmRhdGEsYWVzKHggPSBraWRzMjAwMCx5ID0gcHViYXN0MDApKQpgYGAKCldlIG5vdyBjYW4gY3JlYXRlIG91ciBjdXN0b20gZ3JhcGggd2l0aCAgZGlmZmVyZW50IGNvbG9ycyBmb3IgdGhlIHR3byBhcmVhcyBieQphZGRpbmcgdGhlIGBnZW9tX3BvaW50YCBjb21tYW5kIHRvIHRoZSBvYmplY3QgKipnKiouCgpgYGB7cn0KZyArIGdlb21fcG9pbnQoYWVzKGNvbG9yPW1hbmJyb254KSkKYGBgCgojIyBTYXZpbmcgYSBHcmFwaAoKIyMjIFdyaXRpbmcgYSBncmFwaCB0byBhIGZpbGUKCkluIG91ciBkaXNjdXNzaW9uIHNvIGZhciwgdGhlIGdyYXBocyBhcmUgZHJhd24gdG8gdGhlIHNjcmVlbiBhbmQgdGhlbiBkaXNhcHBlYXIuClRvIHNhdmUgYSAqKmdncGxvdCoqIGdyYXBoIHRvIGEgZmlsZSBmb3IgcHVibGljYXRpb24sIHRoZXJlIGFyZSB0d28gd2F5cyB0byBwcm9jZWVkLgpPbmUgaXMgdGhlIGNsYXNzaWMgUiBhcHByb2FjaCwgaW4gd2hpY2ggZmlyc3QgYSAqKmRldmljZSoqIGlzIG9wZW5lZCwgZS5nLiwgYnkgbWVhbnMKb2YgYSBgcGRmYCBjb21tYW5kIGZvciBhIHBkZiBmaWxlIChlYWNoIHR5cGUgb2YgZmlsZSBoYXMgaXRzIG93biBjb21tYW5kKS4gTmV4dCwgdGhlIHBsb3QgY29tbWFuZHMgYXJlIGVudGVyZWQsIGFuZCBmaW5hbGx5IHRoZSBkZXZpY2UKaXMgdHVybmVkIG9mZiBieSBtZWFucyBvZiBgZGV2Lm9mZigpYC4KCk5vdGUgdGhhdCBpdCBpcyBhbHdheXMgYSBnb29kIGlkZWEgdG8gc3BlY2lmeSB0aGUgZGltZW5zaW9uIG9mIHRoZSBncmFwaCAoaW4gaW5jaGVzKS4KSWYgbm90LCB0aGUgcmVzdWx0cyBjYW4gYmUgdW5leHBlY3RlZC4KCkZvciBleGFtcGxlLCB0byBzYXZlIG91ciBzY2F0dGVyIHBsb3QgaW4gYSBwZGYgZmlsZSAqKnNjYXR0ZXIxLnBkZioqIHdpdGggYSBoZWlnaHQKYW5kIHdpZHRoIG9mIDMgaW5jaGVzLCB3ZSBmaXJzdCBzZXQgYHBkZigic2NhdHRlcjEucGRmIixoZWlnaHQ9Myx3aWR0aD0zKWAuIE5leHQKd2UgZW50ZXIgb3VyIHVzdWFsIGdncGxvdCBjb21tYW5kcy4gTm90ZSB0aGF0IG5vdGhpbmcgd2lsbCBhcHBlYXIgb24gdGhlIHNjcmVlbi4KRmluYWxseSwgd2UgdHVybiB0aGUgImRldmljZSIgKGkuZS4sIHRoZSBmaWxlKSBvZmYgYnkgbWVhbnMgb2YgYGRldi5vZmYoKWAuCgpgYGB7cn0KcGRmKCJzY2F0dGVyMS5wZGYiLGhlaWdodD0zLHdpZHRoPTMpCmcgKyBnZW9tX3BvaW50KGFlcyhjb2xvcj1tYW5icm9ueCkpCmRldi5vZmYoKQpgYGAKCmBUaGUgcGRmIGZpbGUgd2lsbCBiZSBhZGRlZCB0byB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5LgoKIyMjIFNhdmluZyB3aXRoIGBnZ3NhdmVgCgpJbiBhZGRpdGlvbiB0byB0aGUgc3RhbmRhcmQgUiBhcHByb2FjaCwgKipnZ3Bsb3QqKiBhbHNvIGhhcyB0aGUgYGdnc2F2ZWAgY29tbWFuZCwKd2hpY2ggZG9lcyB0aGUgc2FtZSB0aGluZy4gSXQgcmVxdWlyZXMgdGhlIG5hbWUgZm9yIHRoZSBvdXRwdXQgZmlsZSwgYnV0IGRlcml2ZXMKdGhlIHByb3BlciBmb3JtYXQgZnJvbSB0aGUgZmlsZSBleHRlbnNpb24uIEZvciBleGFtcGxlLCBhbiBvdXRwdXQgZmlsZSB3aXRoIGEgKipwbmcqKiBmaWxlIGV4dGVuc2lvbiB3aWxsIGNyZWF0ZSBhIHBuZyBmaWxlLCBhbmQgc2ltaWxhcmx5IGZvciBwZGYsIGV0Yy4KClRoZSBzZWNvbmQgYXJndW1lbnQgc3BlY2lmaWVzIHRoZSAqKnBsb3QqKi4gSXQgaXMgb3B0aW9uYWwsIGFuZCB3aGVuIG5vdCBzcGVjaWZpZWQsIHRoZSBsYXN0IHBsb3QgaXMgc2F2ZWQuIEFnYWluLCBpdCBpcyBhIGdvb2QgaWRlYSB0byBzcGVjaWZ5IHRoZSBgd2lkdGhgIGFuZCBgaGVpZ2h0YCAoaW4gaW5jaGVzKS4gSW4gYWRkaXRpb24sIGZvciByYXN0ZXIgZmlsZXMsIHRoZSBkb3RzIHBlciBpbmNoIChgZHBpYCkgY2FuIGJlIHNldCBhcyB3ZWxsLiBUaGUgZGVmYXVsdCBpcyAzMDAsIHdoaWNoIGlzIGZpbmUgZm9yIG1vc3QgdXNlIGNhc2VzLCBidXQgZm9yIGhpZ2ggcmVzb2x1dGlvbiBncmFwaHMsIG9uZSBjYW4gc2V0IHRoZSBkcGkgdG8gNjAwLgoKRm9yIGV4YW1wbGUsIHdlIGFzc2lnbiBvdXIgc2NhdHRlciBwbG90IHRvIHNjYXQxLCBhbmQgdGhlbiBzYXZlIGl0IAp1c2luZyAgYGdnc2F2ZSgic2NhdHRlcjIucG5nIixzY2F0MSxoZWlnaHQ9Myx3aWR0aD0zLGRwaT02MDApYDoKCmBgYHtyfQpzY2F0MSA8LSBnICsgZ2VvbV9wb2ludChhZXMoY29sb3I9bWFuYnJvbngpKQpnZ3NhdmUoInNjYXR0ZXIyLnBuZyIsc2NhdDEsaGVpZ2h0PTMsd2lkdGg9MyxkcGk9NjAwKQpgYGAKClRoZSBmaWxlIHdpbGwgYmUgYWRkZWQgdG8gdGhlIHdvcmtpbmcgZGlyZWN0b3J5LgoKVG8gc2VlIHRoZSBwbG90IG9uIHlvdXIgc2NyZWVuLCBzaW1wbHkgdHlwZSBpdHMgbmFtZSAoc2NhdDEpLgoKYGBge3J9CnNjYXQxCmBgYAoKIyMjICBTYXZpbmcgYW4gUiBvYmplY3QKCkZpbmFsbHksIHlldCBhbm90aGVyIGFwcHJvYWNoIHRvIGtlZXAgYSBwbG90IG9iamVjdCBpcyB0byBhc3NpZ24gdGhlIHBsb3QgY29tbWFuZHMKdG8gYSB2YXJpYWJsZSBhbmQgdGhlbiBzYXZlIHRoaXMgdG8gZGlzaywgdXNpbmcgdGhlIHN0YW5kYXJkIFIgY29tbWFuZCBgc2F2ZVJEU2AuIFRoaXMKY2FuIGxhdGVyIGJlIGJyb3VnaHQgYmFjayBpbnRvIGFuIFIgc2Vzc2lvbiB1c2luZyBgcmVhZFJEU2AuIFRvIHNhdmUgdGhlIHBsb3QsCndlIG5lZWQgdG8gc3BlY2lmeSBhIGZpbGUgbmFtZSB3aXRoIGFuICoqLnJkcyoqIGZpbGUgZXh0ZW5zaW9uLCBmb3IgZXhhbXBsZSwgKipzY2F0dGVyMS5yZHMqKi4KCmBgYHtyfQpzYXZlUkRTKHNjYXQxLCJzY2F0dGVyMS5yZHMiKQpgYGAKCkF0IHNvbWUgbGF0ZXIgcG9pbnQgKG9yIGluIGEgZGlmZmVyZW50IFIgc2Vzc2lvbiksIHdlIGNhbiB0aGVuIHJlYWQgdGhlIG9iamVjdCB3aXRoIGByZWFkUkRTYCBhbmQgcGxvdCBpdC4gTm90ZSB0aGF0IHdlIGRvIG5vdCBuZWVkIHRvIGFzc2lnbiBpdCB0byB0aGUgc2FtZSB2YXJpYWJsZSBuYW1lIGFzIGJlZm9yZS4KRm9yIGV4YW1wbGUsIGhlcmUgd2UgY2FsbCB0aGUgZ3JhcGggb2JqZWN0ICoqbmV3cGxvdCoqLgoKYGBge3J9Cm5ld3Bsb3QgPC0gcmVhZFJEUygic2NhdHRlcjEucmRzIikKbmV3cGxvdApgYGAKCiMjIFNoYXBlCgpBIGZ1cnRoZXIgYWVzdGhldGljIGlzIHRoZSBzaGFwZSBvZiB0aGUgZ2VvbWV0cmljIG9iamVjdC4gUiBoYXMgYSBudW1iZXIgb2YgcHJlLWRlZmluZWQKc2hhcGVzIHdpdGggKHRvIHRoZSBub3ZpY2UpIG9ic2N1cmUgY29kZXMsIGFzc2lnbmVkIGJ5IHRoZSBgcGNoYCBjb21tYW5kLl5bRm9yIGEgZnVsbCBsaXN0Cm9mIHRoZSBjb2Rlcywgc2VlLCBmb3IgZXhhbXBsZSwgdGhlIFtzdGhkYSBzaXRlXShodHRwOi8vd3d3LnN0aGRhLmNvbS9lbmdsaXNoL3dpa2kvZ2dwbG90Mi1wb2ludC1zaGFwZXMpLl0KCkFnYWluLCB3ZSBjYW4gYXNzaWduIHRoZSBhZXN0aGV0aWMgc2hhcGUgdG8gZWl0aGVyIGEgY29uc3RhbnQgKG9uZSBvZiB0aGUgY29kZXMpIG9yIHRvIGEgdmFyaWFibGUuCgojIyMgU2V0dGluZyB0aGUgc2hhcGUgdG8gYSBjb25zdGFudAoKU2ltaWxhciB0byBob3cgd2Ugc2V0IHRoZSBjb2xvciBhYm92ZSwgd2UgY2FuIGFkZCBhbiBvcHRpb24gYHNoYXBlPTE3YCB0byB0aGUgYGdlb21fcG9pbnRgIGNvbW1hbmQuClRoaXMgd2lsbCBnZW5lcmF0ZSBhIHNjYXR0ZXIgcGxvdCB3aXRoIHRoZSBwb2ludHMgcmVwcmVzZW50ZWQgYXMgdHJpYW5nbGVzLgoKCmBgYHtyfQpnICsgZ2VvbV9wb2ludChzaGFwZT0xMikKYGBgCgojIyMgU2V0dGluZyB0aGUgc2hhcGUgdG8gYSB2YXJpYWJsZQoKQWdhaW4sIHNpbWlsYXIgdG8gaG93IHdlIG9wZXJhdGVkIGFib3ZlIGZvciB0aGUgY29sb3Igb3B0aW9uLCB3ZSBjYW4gYXNzaWduIHRoZSBhZXN0aGV0aWMKc2hhcGUgdG8gYSB2YXJpYWJsZS4gSG93ZXZlciwgdW5saWtlIHdoYXQgd29ya3MgZm9yIGEgY29uc3RhbnQsIHdlIGNhbm5vdCBqdXN0IHNwZWNpZnkgYHNoYXBlPW1hbmJyb254YApmb3IgZXhhbXBsZS4gSW5zdGVhZCwgd2UgbmVlZCB0byBleHBsaWNpdGx5IGVuY2xvc2UgdGhpcyBhcyBhbiBvcHRpb24gdG8gdGhlIGFlc3RoZXRpYywgYXMgYGFlcyhzaGFwZT1tYW5icm9ueClgLgpUaGUgcmVzdWx0IHlpZWxkcyBhIHNjYXR0ZXIgcGxvdCB3aXRoIGRvdHMgZm9yICoqUmVzdCoqIGFuZCB0cmlhbmdsZXMgZm9yICoqU2VsZWN0KiosIHdpdGggYSBsZWdlbmQKc2hvd2luZyBob3cgdGhlIHNoYXBlcyBhcmUgYXNzaWduZWQgdG8gZGlmZmVyZW50IHZhbHVlcyBvZiB0aGUgdmFyaWFibGUgKiptYW5icm9ueCoqLgoKCmBgYHtyfQpnICsgZ2VvbV9wb2ludChhZXMoc2hhcGU9bWFuYnJvbngpKQpgYGAKCk5vdywgd2UgY2FuIGdldCBhIGJpdCBmYW5jaWVyIGFuZCBhc3NpZ24gYm90aCBjb2xvciBhbmQgc2hhcGUgdG8gKiptYW5icm9ueCoqLgoKCmBgYHtyfQpnICsgZ2VvbV9wb2ludChhZXMoY29sb3I9bWFuYnJvbngsIHNoYXBlPW1hbmJyb254KSkKYGBgCgojIyBTaXplCgpBcyBiZWZvcmUsIHdlIGNhbiBhc3NpZ24gdGhlIHNpemUgYWVzdGhldGljIHRvIGEgY29uc3RhbnQgb3IgdG8gYSB2YXJpYWJsZS4KCiMjIyBTZXR0aW5nIHNpemUgdG8gYSBjb25zdGFudAoKV2UgbWF5IGRlY2lkZSB0aGF0IHRoZSBkZWZhdWx0IHNpemUgb2YgdGhlIHBvaW50cyBpcyBub3QgYXBwcm9wcmlhdGUgZm9yIG91ciBwdXJwb3NlcywKYW5kIHdlIGNhbiBzZXQgaXQgYXMgYW4gb3B0aW9uIHRvIGEgZGlmZmVyZW50IHZhbHVlIChhIG11bHRpcGxlIG9mIHRoZSBkZWZhdWx0KS4KRm9yIGV4YW1wbGUsIHRvIG1ha2UgdGhlIHBvaW50cyB0aHJlZSB0aW1lcyBhcyBsYXJnZSwgd2Ugc2V0IGBzaXplPTNgIGFzIGFuIG9wdGlvbgp0byBgZ2VvbV9wb2ludGAuCgoKYGBge3J9CmcgKyBnZW9tX3BvaW50KGFlcyhjb2xvcj1tYW5icm9ueCksc2l6ZT0zKQpgYGAKCiMjIyBTZXR0aW5nIHNpemUgdG8gYSB2YXJpYWJsZQoKV2UgY2FuIGFsc28gc3BlY2lmeSB0aGUgc2l6ZSBhcyBhbiBhZXN0aGV0aWMgKGkuZS4sIGluIHRoZSBgYWVzYCBzcGVjaWZpY2F0aW9uKSB0byB2YXJ5IHdpdGgKdGhlIHZhbHVlcyBvZiBhIHZhcmlhYmxlLiBIb3dldmVyLCB0aGlzIGRvZXMgbm90IGFsd2F5cyB3b3JrIGFzIGV4cGVjdGVkLiBGb3IgZXhhbXBsZSwgaWYKd2Ugc2V0IGBzaXplID0gbWFuYnJvbnhgLCB3ZSBnZXQgYSB3YXJuaW5nIHRoYXQgKlVzaW5nIHNpemUgZm9yIGEgZGlzY3JldGUgdmFyaWJsZSBpcyBub3QgYWR2aXNlZCosCmFzIGJlbG93LgoKCmBgYHtyfQpnICsgZ2VvbV9wb2ludChhZXMoY29sb3I9bWFuYnJvbngsc2l6ZT1tYW5icm9ueCkpCmBgYAoKVGhlcmUgYXJlIG90aGVyIHdheXMgdG8gc2V0IHRoZSBzaXplIGZvciBzcGVjaWZpYyB2YWx1ZXMgb2YgYSBkaXNjcmV0ZSB2YXJpYWJsZSwgYnV0IHRoYXQncyBiZXlvbmQKb3VyIGN1cnJlbnQgc2NvcGUuCgoKIyMjIEEgYnViYmxlIHBsb3QKCldoZW4gd2Ugc2V0IHRoZSBgc2l6ZWAgYWVzdGhldGljIHRvIGEgY29udGludW91cyB2YXJpYWJsZSwgd2UgY2FuIHZpc3VhbGl6ZSB0aGUgaW50ZXJhY3Rpb24KYW1vbmcgdGhyZWUgdmFyaWFibGVzLCBpbiBhIHNvLWNhbGxlZCAqYnViYmxlIHBsb3QqLiBGb3IgZXhhbXBsZSwgd2l0aCBgY29sb3I9bWFuYnJvbnhgLCBhcyBiZWZvcmUsCmFuZCBub3cgYHNpemU9aGhzaXplMDBgIChtZWRpYW4gaG91c2Vob2xkIHNpemUgaW4gMjAwMCksIHdlIG9idGFpbiB0aGUgZm9sbG93aW5nIGJ1YmJsZSBwbG90LgoKYGBge3J9CmcgKyBnZW9tX3BvaW50KGFlcyhjb2xvcj1tYW5icm9ueCxzaXplPWhoc2l6MDApKQpgYGAKCkFzIGlzIG5vIHN1cnByaXNlLCB0aGUgc2l6ZSBvZiB0aGUgY2lyY2xlcyBncm93cyByb3VnaGx5IHdpdGggdGhlIHBlcmNlbnRhZ2UgaG91c2Vob2xkcyB3aXRoIGtpZHMsIHdoaWNoCmlzIHRvIGJlIGV4cGVjdGVkLgoKIyMgU21vb3RoaW5nIHRoZSBTY2F0dGVyIFBsb3QKClNob3dpbmcgYWxsIHRoZSBwb2ludHMgaW4gYSBzY2F0dGVyIHBsb3QgaXMgZmluZSwgYnV0IHdlIGFyZSB0eXBpY2FsbHkgaW50ZXJlc3RlZCBpbiBkZXRlY3RpbmcKYnJvYWQgcGF0dGVybnMgaW4gdGhlIGRhdGEuIFRvIHRoYXQgZWZmZWN0LCB3ZSAqc21vb3RoKiB0aGUgZGF0YSBwb2ludHMgYnkgZml0dGluZyBhIGN1cnZlIHRvIHRoZW0uCgpJbiAqKmdncGxvdCoqLCB0aGlzIGlzIGltcGxlbWVudGVkIHRocm91Z2ggdGhlIGBnZW9tX3Ntb290aGAgY29tbWFuZC4gVGhlcmUgYXJlIHR3byBtYWluIG1ldGhvZHMKKG90aGVyIGN1c3RvbSBtZXRob2RzIGNhbiBiZSBhZGRlZCksIGkuZS4sIGEgbGluZWFyIGZpdCBhbmQgYSBsb2NhbCByZWdyZXNzaW9uIG9yICpsb2VzcyogZml0LiBUaGUgbGF0dGVyCmZvbGxvd3MgbG9jYWwgY2hhbmdlcyBpbiB0aGUgYXNzb2NpYXRpb24gYmV0d2VlbiB4IGFuZCB5IG1vcmUgY2xvc2VseS4gV2UgY29uc2lkZXIgZWFjaCBpbiB0dXJuLgoKIyMjIExpbmVhciBmaXQKCkEgbGluZWFyIGZpdCBpcyBvYnRhaW5lZCBieSBtZWFucyBvZiB0aGUgYG1ldGhvZD0ibG0iYCAobm90ZSB0aGUgcXVvdGVzIGFyb3VuZCBsbSkuIFRoaXMgaXMgYQpjbGFzc2ljYWwgbGVhc3Qgc3F1YXJlcyBmaXQuIEhvd2V2ZXIsIHRoaXMgaXMgbm90CnRoZSBkZWZhdWx0LiBJZiBubyBtZXRob2QgaXMgc3BlY2lmaWVkIGZvciBgZ2VvbV9zbW9vdGhgLCB0aGUgYGxvZXNzYCBtZXRob2Qgd2lsbCBiZSB1c2VkLgoKQWxzbywgYnkgZGVmYXVsdCwgYW4gZXJyb3IgYmFuZCBpcyBzaG93biBpbiBncmF5IGNvcnJlc3BvbmRpbmcgdG8gdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBmaXQKKGEgd2lkZXIgYmFuZCBtZWFucyBsZXNzIHByZWNpc2UgcHJlZGljdGlvbikuIFdlIHVzZSB0aGUgZGVmYXVsdCBzZXR0aW5nIGZvciBgZ2VvbV9wb2ludGAgYW5kIAphZGQgKCspIGFub3RoZXIgbGF5ZXIgd2l0aCBgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpLgoKYGBge3J9CmcgKyBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKQpgYGAKCldlIGNhbiBhbHdheXMgY2hhbmdlIHRoZSBkZWZhdWx0IHNldHRpbmdzIGJ5IHNwZWNpZnlpbmcgc29tZSBvcHRpb25zIGV4cGxpY2l0bHkuIEZvciBleGFtcGxlLCB0bwpjaGFuZ2UgdGhlIGNvbG9yIG9mIHRoZSBsaW5lIHRvICJyZWQiIGFuZCB0byB0dXJuIG9mZiB0aGUgZXJyb3IgYmFuZCwgd2UgdXNlCmBnZW9tX3Ntb290aChtZXRob2Q9ImxtIixjb2xvcj0icmVkIixzZT1GQUxTRSlgIChkb24ndCBmb3JnZXQgdGhlIHF1b3RlcyBhcm91bmQgcmVkKS4KCmBgYHtyfQpnICsgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIixjb2xvcj0icmVkIixzZT1GQUxTRSkKYGBgCgojIyMjIFN1YnBsb3RzCgpJbiB0aGUgc2FtZSB3YXkgYXMgd2UgYXNzaWduZWQgYSBjb2xvciBhZXN0aGV0aWMgdG8gdGhlIHBvaW50cywgd2UgY2FuIGFsc28gYXNzaWduIGEgY29sb3IKYWVzdGhldGljIHRvIHRoZSBsaW5lYXIgZml0LiBUaGlzIHdpbGwgcmVzdWx0IGluIGEgc2VwYXJhdGUgZml0IGZvciBlYWNoIG9mIHRoZSBjYXRlZ29yaWVzLAp3aXRoIGEgc2VwYXJhdGUgY29sb3IgZm9yIGVhY2guIEZvciBleGFtcGxlLCB3ZSBzZXQgYGFlcyhjb2xvcj1tYW5icm9ueClgIGluIGJvdGgKdGhlIGBnZW9tX3BvaW50YCBhbmQgYGdlb21fc21vb3RoYCBsYXllci4gVGhpcyB5aWVsZHMgbm90IG9ubHkgZGlmZmVyZW50IGNvbG9ycyBmb3IgCnRoZSBwb2ludHMsIGJ1dCBhbHNvIGZvciB0aGUgY29ycmVzcG9uZGluZyBsaW5lYXIgZml0czogb25lIGZpdCBmb3IgZWFjaCBzdWJncm91cCBpbgp0aGUgZGF0YS4KCgpgYGB7cn0KZyArIGdlb21fcG9pbnQoYWVzKGNvbG9yPW1hbmJyb254KSkgKwogIGdlb21fc21vb3RoKGFlcyhjb2xvcj1tYW5icm9ueCksbWV0aG9kPSJsbSIsc2U9RkFMU0UpCmBgYAoKIyMjIExvZXNzIGZpdAoKRmluYWxseSwgdGhlIGxvY2FsIHJlZ3Jlc3Npb24gZml0LCB3aGljaCBpcyB0aGUgZGVmYXVsdC4gQXMgYSByZXN1bHQsIHdlIGRvbid0IG5lZWQgdG8gc3BlY2lmeQphbnkgb3B0aW9ucyBpbiB0aGUgYGdlb21fc21vb3RoYCBjb21tYW5kIChidXQgZG9uJ3QgZm9yZ2V0IHRoZSBwYXJlbnRoZXNlcykuCgpgYGB7cn0KZyArIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoKQpgYGAKCgpBZ2Fpbiwgd2UgY2FuIHVzZSB0aGUgb3B0aW9ucyBhbmQgYWVzdGhldGljcyB0byBjdXN0b21pemUgdGhlIGdyYXBoLiBGb3IgZXhhbXBsZSwgdXNpbmcgdGhlIGBsb2Vzc2AgZml0IAp3aXRoIGRpZmZlcmVudCBjb2xvcnMgZm9yIHRoZSB0d28gc3Vic2V0cyBvZiB0aGUgZGF0YSAoYW5kIHdpdGhvdXQgdGhlIHN0YW5kYXJkIGVycm9yKSB5aWVsZHM6CgpgYGB7cn0KZyArIGdlb21fcG9pbnQoYWVzKGNvbG9yPW1hbmJyb254KSkgKwogIGdlb21fc21vb3RoKGFlcyhjb2xvcj1tYW5icm9ueCksc2U9RkFMU0UpCmBgYAoKIyMjIyBFeHRyYSAtIHNldHRpbmcgdGhlIGJhbmR3aWR0aCBmb3IgdGhlIGxvZXNzIHNtb290aAoKQXMgaW4gYW55IGxvY2FsIHJlZ3Jlc3Npb24gbWV0aG9kLCBhbiBpbXBvcnRhbnQgcGFyYW1ldGVyIGlzIGhvdyBtdWNoIG9mIHRoZSBkYXRhIGlzIHVzZWQgaW4gdGhlIGxvY2FsIGZpdCwgdGhlIHNvLWNhbGxlZCBgc3BhbmAuIFRoaXMgaXMgdHlwaWNhbGx5IHNldCB0byAyLzMgb2YgdGhlIGRhdGEgYnkgZGVmYXVsdC4gQSBuYXJyb3dlciBzcGFuIHdpbGwgeWllbGQgYSBzcGlraWVyIApjdXJ2ZSB0aGF0IGVtcGhhc2l6ZXMgbG9jYWwgY2hhbmdlcy4gCgpGb3IgZXhhbXBsZSwgd2UgY2FuIGlsbHVzdHJhdGUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgZGVmYXVsdCBhbmQgYSBzbW9vdGhlciB3aXRoIGEgYHNwYW4gPSAwLjRgCmFuZCBvbmUgd2l0aCBhIGBzcGFuPTAuOWAuIFdlIHR1cm4gb2ZmIHRoZSBjb25maWRlbmNlCmludGVydmFsIGJ5IHNldHRpbmcgYHNlID0gRkFMU0VgLiAKCgpgYGB7cn0KZyArIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoc2U9RkFMU0UpICsKICBnZW9tX3Ntb290aChjb2xvcj0icmVkIixzcGFuPTAuNCxzZT1GQUxTRSkgKwogIGdlb21fc21vb3RoKGNvbG9yPSJncmVlbiIsc3Bhbj0wLjksc2U9RkFMU0UpCmBgYAoKVGhpcyBhbHNvIGhpZ2hsaWdodHMgaG93IGEgZ3JhcGggaXMgY29uc3RydWN0ZWQgYnkgYWRkaW5nIGxheWVycy4KWW91IGNhbiBjaGFuZ2UgdGhlIG9yZGVyIGluIHdoaWNoIHRoZSBsYXllcnMgYXJlIGRyYXduIGJ5IGNoYW5naW5nIHRoZWlyIHBvc2l0aW9uIGluIHRoZSBjb21tYW5kLiBGb3IgZXhhbXBsZSwKdG8gaGF2ZSB0aGUgZGVmYXVsdCBjdXJ2ZSBvbiB0b3AsIHdlIGFkZCBpdCBhcyB0aGUgbGFzdCBsYXllci4KCgpgYGB7cn0KZyArIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoY29sb3I9InJlZCIsc3Bhbj0wLjQsc2U9RkFMU0UpICsKICBnZW9tX3Ntb290aChjb2xvcj0iZ3JlZW4iLHNwYW49MC45LHNlPUZBTFNFKSArCiAgZ2VvbV9zbW9vdGgoc2U9RkFMU0UpCmBgYAoKCgoKCgoKCgoKCgoKCgoKCgo=