This document is intended to help you quickly learn how to perform basic data visualization using ggplot2.

An accompanying YouTube playlist that walks through this document is available by following the link here. I have also included direct, relevant video links throughout the file (e.g., immediately after the the relevant heading). An html version of this document can be downloaded to your current working direction (run getwd() in the Console to see where this is) by running the following command:

download.file("https://raw.githubusercontent.com/jfrench/DataWrangleViz/master/03-basic-data-viz-with-ggplot2.nb.html",
              "03-basic-data-viz-with-ggplot2.nb.html")

The raw R Markdown code used to generate the html file can be obtained by toggling the “Code” box in the upper-right corner of the html file and selecting “Download Rmd”.

Introduction to basic data visualization with ggplot2 (Video: YouTube, Panopto)

In this module we discuss how to perform basic data visualization.

We will use ggplot2 (Wickham 2016; Wickham et al. 2022) to construct the graphics in this module.

ggplot2 has become increasingly popular for producing flexible graphics within an elegant framework.

We start by loading the ggplot2 package.

library(ggplot2)

Tips for good graphics (Video: YouTube, Panopto)

Data graphics should be as informative as possible.

You will have to decide what is needed in a graphic to make it as informative as possible.

Complicated graphics may present far more information than we are able to process, such as in the example below.

Graphics ecosystems in R (Video: YouTube, Panopto)

There are three main graphics ecosystems in R:

  1. base
  2. lattice
  3. ggplot2

base graphics are traditional S-like graphics.

lattice graphics are an implementation of Trellis graphics (Becker, Cleveland, and Shyu 1996) for R.

ggplot2 is a layered graphical system based on implementing the Grammar of Graphics (Wilkinson 2005).

We demonstrate use of the three plotting ecosystems using the penguins data set from the palmerpenguins package (Horst, Hill, and Gorman 2020), which will be discussed in more detail later. The code below creates grouped scatter plots comparing bill length (mm) and body mass (g) of different penguin species using each of the ecosystems.

# load data
data(penguins, package = "palmerpenguins")
# base graphics
plot(bill_length_mm ~ body_mass_g, data = penguins, col = penguins$species)
legend(x = "topleft", legend = levels(penguins$species), col = c("black", "red",
    "green"), pch = 1)

# lattice graphics
lattice::xyplot(bill_length_mm ~ body_mass_g, data = penguins, group = species, auto.key = TRUE)

# ggplot2 graphics
ggplot2::ggplot(data = penguins) + ggplot2::geom_point(mapping = aes(x = body_mass_g,
    y = bill_length_mm, color = species, shape = species))
Warning: Removed 2 rows containing missing values (geom_point).

Basic ingredients (Video: YouTube, Panopto)

There are 4 main components needed to produce a graphic using ggplot2.

  1. A data frame containing your data.
    • Each column should be a variable and each row should be an observation of data.
  2. A ggplot object.
    • This is initialized using the ggplot function.
  3. A geometric object.
    • These are called “geoms” for short.
    • geoms indicate the geometric object used to visualize the data. E.g., points, lines, polygons etc. More generally, geoms indicate the type of plot that is desired, e.g., histogram, density, or boxplot, which aren’t exactly a simple geometric argument.
  4. An aesthetic.
    • An aesthetic mapping indicates what role a variable plays in the plot.
    • e.g., which variable will play the “x” variable in the plot, the “y” variable in the plot, control the “color” of the observations, etc.

In R for Data Science, Wickham and Grolemund (2017) provide the following template for creating graphics using ggplot2:

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

<DATA>, <GEOM_FUNCTION>, and <MAPPINGS> are placeholders that you replace with the data frame, geometric object, and aesthetic mappings you want to use in your specific plot.

An explanation of the template above:

  1. Every ggplot starts with a call to the ggplot function.
    • You can create a blank plot by running ggplot() in the Console.
  2. Generally, you pass your data frame to the ggplot through the data argument.
    • You can also pass data inside the <GEOM_FUNCTION> when you intend to use multiple geometric objects with different data sources.
  3. The <GEOM_FUNCTION> indicates the geometric object you want to use in the plot.
  4. The <MAPPINGS> describes the aesthetics mappings you want to use for this particular geometry.
    • One of the reasons ggplot2 is so powerful is that you can use different mappings for different geometric objects.
  5. The + symbol is used to add a layer to your graphic.
    • If your code spans multiple lines, then you need to make sure all lines but the last end with the + operator to stack the layers properly.

Some geometric objects

There are many geometric objects available in ggplot2. A complete list may be (currently) found at https://ggplot2.tidyverse.org/reference/. A partial list of geometric objects that I frequently use is found below in Table 1.

Table 1: Common geometries provided by ggplot2.
data_dimensionality geometry purpose
1d geom_bar Draws a bar chart.
1d geom_density Draws a density plot.
1d geom_histogram Draws a histogram.
1d geom_boxplot Draws a boxplot.
1d geom_violin Draws a violin plot.
1d geom_qq Draws a quantile-quantile plot.
2d geom_point Draws points. Used for scatter plots.
2d geom_path, geom_line Connects observations. Used for line plots.
2d geom_segment Draws straight lines between points.
2d geom_curve Draws curved lines between points.
2d geom_smooth Draws a ‘smooth’ fitted model of the data.
2d geom_density2d Draws 2d contours of density estimate for two variables.
2d geom_density2d_filled Draws 2d contours of density estimate for two variables with colors.
3d geom_contour Draws 2d contours of 3d data.
3d geom_contour_filled Draws 2d contours of 3d data (colored).
NA geom_abline, geom_hline, geom_vline Draws diagonal, horizontal, and vertical lines.

Some aesthetic mappings

Aesthetic mappings are unique to each geometric object. Some of the most common ones that show up in many geoms are provided below in Table 2.

Table 2: Common aesthetics provided by ggplot2.
aesthetic purpose
x Controls the x-variable in the plot.
y Controls the y-variable in the plot.
alpha Controls the transparency of the object.
color, colour Controls the colors of the object.
fill Controls the color of the interior of an object.
group Controls how the data are grouped.
linetype Controls the type of line used to draw the object.
size Controls the size of the drawn object.
shape Controls the shape of the object.

Simple examples

The penguins data (Video: YouTube, Panopto)

We will use the penguins data set in the palmerpenguins package (Horst, Hill, and Gorman 2020) to illustrate some of the ways that ggplot2 can be used.

The penguins data set provides data related to various penguin species measured in the Palmer Archipelago (Antarctica), originally provided by Gorman, Williams, and Fraser (2014). We start by loading the data into memory.

data(penguins, package = "palmerpenguins")

The data set includes 344 observations of 8 variables. The variables are:

  • species: a factor indicating the penguin species
  • island: a factor indicating the island the penguin was observed
  • bill_length_mm: a numeric variable indicating the bill length in millimeters
  • bill_depth_mm: a numeric variable indicating the bill depth in millimeters
  • flipper_length_mm: an integer variable indicating the flipper length in millimeters
  • body_mass_g: an integer variable indicating the body mass in grams
  • sex: a factor indicating the penguin sex (female, male)
  • year: an integer denoting the study year the penguin was observed (2007, 2008, or 2009)

Bar chart (Video: YouTube, Panopto)

We construct a bar chart of the species. A bar chart uses bars to indicate the number of values each level of a factor includes. Alternatively, the bars can indicate the relative frequency (i.e., proportion) of values having each level of the factor.

ggplot(penguins) + geom_bar(aes(x = species))

Relatively speaking, we can see that Adelie penguins were most frequently observed, followed by Gentoo penguins, and then Chinstrap penguins.

We can color the bars by species for visual clarity by specifying the fill aesthetic.

ggplot(penguins) + geom_bar(aes(x = species, fill = species))

This doesn’t add any new information to the graphic but does make it more visually appealing. We could remove the legend to simplify the graphic, if we wanted.

ggplot(penguins) + geom_bar(aes(x = species, fill = species)) + theme(legend.position = "none")

Histogram (Video: YouTube, Panopto)

A histogram is used to display the distribution of a continuous numeric variable. The range of the variable is partitioned into classes. The number of observations falling in each class is counted. A histogram draws a bar for each class with height corresponding to the number of observations in that class.

Let’s construct a histogram of bill length.

ggplot(penguins) + geom_histogram(aes(x = bill_length_mm))
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 2 rows containing non-finite values (stat_bin).

The histogram is multimodal (i.e., has multiple prominent peaks), having 2 or 3 prominent peaks. There may be sub-populations we should distinguish. It isn’t easy to identify sub-populations with histograms unless the sub-populations are split into separate histograms (which we will learn to do later with “facets”) or to make the bars semi-transparent for the different sub-populations.

Density plot (Video: YouTube, Panopto)

A density plot is often preferred to a histogram because it’s more flexible, though it often provides similar information. A density plot is essentially a smoothed version of a histogram.

A density plot is appropriate for displaying the distribution of a continuous numeric variable and indicates the values for which the data is more “dense”. More specifically, density plots indicate the values of data you are most likely to observe. A higher density region means you are more likely to observe data with values in that region. There is a statistical definition we will not go into.

A simple density plot is shown below. We can’t learn a lot because of the sub-populations previously mentioned.

ggplot(penguins) + geom_density(aes(x = bill_length_mm))
Warning: Removed 2 rows containing non-finite values (stat_density).

There are at least two modes in the bill_length_mm density plot.

Let’s create separate densities for the different penguin species by specifying the color aesthetic to scale different colors to the different levels of species.

ggplot(penguins) + geom_density(aes(x = bill_length_mm, color = species))
Warning: Removed 2 rows containing non-finite values (stat_density).

There are clear differences in the bill length of Adelie, Chinstrap, and Gentoo penguins. The bill length for the Adelie penguins is about 35 mm and closer to 47 mm for the Gentoo penguins. The Chinstrap penguins have the greatest bill length, in general. It appears the Chinstrap penguins may have a further sub-population.

A weakness of the previous graphic is that the legend distinguishing the different species is a bit subtle. The colored line around the border may not be easy to distinguish for everyone. We could use a different linetype for each species or we could use different color and linetype for each species. We consider the different results below.

ggplot(penguins) + geom_density(aes(x = bill_length_mm, linetype = species))
Warning: Removed 2 rows containing non-finite values (stat_density).

ggplot(penguins) + geom_density(aes(x = bill_length_mm, col = species, linetype = species))
Warning: Removed 2 rows containing non-finite values (stat_density).

An even better option is to fill the densities for each species with different colors. However, the density curves will overlap, masking some of the information.

ggplot(penguins) + geom_density(aes(x = bill_length_mm, fill = species))
Warning: Removed 2 rows containing non-finite values (stat_density).

To address the masking issue, we can control the transparency of the colors using the alpha aesthetic. This value, between 0 and 1, controls how transparent the objects are. 0 means transparent, 1 means completely opaque. We will set alpha = 0.3. Notice that we specify this OUTSIDE the aesthetic mapping since we are controlling the aesthetic manually instead of having ggplot2 scale the provided variable.

ggplot(penguins) + geom_density(aes(x = bill_length_mm, fill = species), alpha = 0.3)
Warning: Removed 2 rows containing non-finite values (stat_density).

Scaling

When a variable is mapped to an aesthetic, ggplot2 will assign a unique value to each unique level of the variable. This process is known as scaling.

Scaling is often not very exciting, but it can be important when customizing the look of a ggplot.

When a variable is mapped to a non x or y aesthetic, ggplot2 will automatically add a legend to indicate how the variable was scaled.

Boxplots (Video: YouTube, Panopto)

A boxplot is a robust display of information for a continuous numeric variable. Boxplots are robust because they rely on robust statistics not easily affected by outliers.

In general, a boxplot:

  • Draws a box extending from Q1 (the 0.25 quantile) to Q3 (the 0.75 quantile) of the data.
  • A line between Q1 and Q3 indicates the median (the 0.5 quantile) of the data.
  • A “whisker” extends from Q1 to the smallest observation that is not an outlier.
  • A “whisker” extends from Q3 to the largest observation that is not an outlier.
  • An outlier is typically defined as an observation smaller than Q1 - 1.5 (Q3 - Q1) or larger than Q3 + 1.5 (Q3 - Q1).

Parallel boxplots are an effective tool for comparing differences between a variable across multiple levels of a factor variable.

Consider parallel boxplots of bill length distinguished by species.

ggplot(penguins) + geom_boxplot(aes(y = bill_length_mm, x = species))
Warning: Removed 2 rows containing non-finite values (stat_boxplot).

We can see that the typical responses for the Adelie penguins tend to be lower than for the Chinstrap and Gentoo penguins. The Chinstrap penguins tend to have slightly longer beaks that the Gentoo penguins. All three penguins species seem to have similar variability (i.e., the spread of the data is similar).

A weakness of boxplots is that they throw away a lot of information. e.g., We don’t see the two sub-populations in the Chinstrap penguins that we noticed earlier when looking at density plots.

Adding a fill color to the boxplots (while removing the legend) might be a more visually appealing graphic.

ggplot(penguins) + geom_boxplot(aes(y = bill_length_mm, x = species, fill = species)) +
    theme(legend.position = "none")
Warning: Removed 2 rows containing non-finite values (stat_boxplot).

Violin plots (Video: YouTube, Panopto)

A violin plot is like a cross between a boxplot and a density plot. A violin plot unites a density with its mirror image and then displays the unified object like a boxplot.

We construct violin plots of bill length for each penguin species.

ggplot(penguins) + geom_violin(aes(x = species, y = bill_length_mm))
Warning: Removed 2 rows containing non-finite values (stat_ydensity).

While the Adelie bill lengths are approximately symmetric and unimodal, both the Chinstrap and Gentoo bill lengths are bimodal and asymmetric.

Scatter plots (Video: YouTube, Panopto)

A scatter plot draws the locations of (x, y) positions based on two numeric variables. In general, we use scatter plots to learn information about the association between the two variables.

  • A positive association is present when both variables tend to increase together.
  • A negative association is present when one variable tends to decrease when the other increases.
  • A linear association is present when the relationship between the two variables is a straight line.
  • A nonlinear association is present when the relationship between the two variables is a curve.
  • It is possible that no clear association is present in the data.

Let’s consider a scatter plot of bill_length_mm versus body_mass_g.

ggplot(penguins) + geom_point(aes(x = body_mass_g, y = bill_length_mm))
Warning: Removed 2 rows containing missing values (geom_point).

We see a positive association in the graphic above. As body_mass_g increases, bill_length_mm also tends to increase. However, it appears that there may be some observations in the upper left part of the graph that do not match the overall linear trend.

This could mean:

  1. The data are noisy, which simply means the patterns aren’t as strong and clear as we would like.
  2. There is a third variable that not accounted for in our plot but that has a relationship with the other two variables.
    • When we are not accounting for this variable, it is called a lurking variable.
    • When we account for a lurking variable in our analysis, it becomes a confounding variable.
    • We are defining lurking and confounding variables in a general sense and not with statistical rigor.

Let’s consider an analysis of body_mass_g and bill_length_mm that accounts for species. Specifically, we will use the color aesthetic so that each penguin species is shown in a different color.

ggplot(penguins) + geom_point(aes(x = body_mass_g, y = bill_length_mm, color = species))
Warning: Removed 2 rows containing missing values (geom_point).

Accounting for species in the analysis shows clear patterns for the three penguin species. Chinstrap penguins tend to have smaller body mass but longer bill lengths. Adelie penguins have smaller body mass and bill lengths. Gentoo penguins tend to have larger body mass and bill lengths than the other two species.

As previously mentioned, we want our plots to be as clear as possible. Some individuals have difficulty interpreting colors in graphics. We can add another distinguishing characteristic for the different species. In this case, we will change the shape of the plotted points for each species. This provides another aid for correctly interpreting the graphic.

ggplot(penguins) + geom_point(aes(x = body_mass_g, y = bill_length_mm, color = species,
    shape = species))
Warning: Removed 2 rows containing missing values (geom_point).

Adding smooths (Video: YouTube, Panopto)

A smooth in ggplot2 is a model that that attempts to estimate the average relationship between a response variable (typically the y variable) and one or more predictor variables (typically the x and possibly other variables)

Adding a “smooth” to a plot can make relationships between variables even clearer. There are many different smoothing methods.

A smoothing layer can be added to a plot using geom_smooth. Many methods can be used for the smooth. ggplot2 takes the methods lm (linear model), glm (generalized linear model), gam (generalized additive model), loess (local fitting), or a function (e.g., MASS::rlm for a robust linear model). If you don’t know what these are, do not add them to your plot because they won’t help you to explain the data.

A simple linear regression model fits the straight line that minimizes the squared deviations between the y values and the estimated line. We add simple linear regression line to the scatter plot by adding a geom_smooth layer.

ggplot(penguins) + geom_point(aes(x = body_mass_g, y = bill_length_mm, color = species,
    shape = species)) + geom_smooth(aes(x = body_mass_g, y = bill_length_mm), method = "lm")
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 2 rows containing non-finite values (stat_smooth).
Warning: Removed 2 rows containing missing values (geom_point).

This only adds a single line across all groups, which clearly doesn’t explain the relationship when accounting for species. To add a separate line for each species, we need to include that in the aesthetic mapping for geom_smooth. If we scale the species variable using the color aesthetic, then a simple linear regression line will be estimated for each species in the color that matches the scatter plot.

ggplot(penguins) + geom_point(aes(x = body_mass_g, y = bill_length_mm, color = species,
    shape = species)) + geom_smooth(aes(x = body_mass_g, y = bill_length_mm, color = species),
    method = "lm")
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 2 rows containing non-finite values (stat_smooth).
Warning: Removed 2 rows containing missing values (geom_point).

Using common aesthetics across geometries (Video: YouTube, Panopto)

Notice that we had to specify the aesthetic mappings in each geom. This seems redundant.

If you specify an aesthetic mapping in a geom, then the aesthetic mapping is local and only applies to that particular geom. If you want to specify a global aesthetic mapping that applies across all geoms, then you can specify the mapping in the ggplot function.

Here is a simpler version of the previous plot.

ggplot(data = penguins, mapping = aes(x = body_mass_g, y = bill_length_mm, color = species)) +
    geom_point(aes(shape = species)) + geom_smooth(method = "lm")
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 2 rows containing non-finite values (stat_smooth).
Warning: Removed 2 rows containing missing values (geom_point).

Customizing plots (Video: YouTube, Panopto)

Unsurprisingly, the graphics produced by ggplot2 can be greatly customized. We consider some of the basic customizations below.

Assigning a plot

One of the strengths of ggplot2 is that you can easily build a plot layer by layer. In this case, it is useful to assign the current plot a name that can then be added to later.

Note: When you assign a ggplot a name, it will NOT be displayed, as is typical when assigning objects in R. You have to then print the object using the print function or by simply running the object name in the Console. Let’s assign the name ggp to the basic penguin scatter plot displaying bill length versus body mass, using colors and shapes to distinguish between different species.

ggp <- ggplot(data = penguins, mapping = aes(x = body_mass_g, y = bill_length_mm,
    color = species)) + geom_point(aes(shape = species))
ggp
Warning: Removed 2 rows containing missing values (geom_point).

We’ll customize this plot below.

Axis labels and titles

The x-axis labels, y-axis labels, and title of a plot are controlled using the xlab, ylab, and ggtitle functions, respectively. Let’s customize the penguin scatter plot previously discussed. We’ll update the ggp object to include our improvements then print the result.

ggp <- ggp + xlab("body mass (g)") + ylab("bill length (mm)") + ggtitle("Penguin body characteristics")
ggp
Warning: Removed 2 rows containing missing values (geom_point).

Axis limits

The x-axis and y-axis limits are controlled using the xlim and ylim functions. Each function takes the lower and upper limit you want to set, e.g., xlim(0, 100). Let’s change the axes for the penguin scatter plot previously discussed. There’s nothing wrong with the previous limits, but we’ll extend them a bit for demonstration.

ggp + xlim(2000, 7000) + ylim(0, 80)
Warning: Removed 2 rows containing missing values (geom_point).

Manual aesthetics

Aesthetics can be set manually in a geom by specifying the aesthetic directly in the geom but outside the aes function. Let’s change the shape of the points for a scatter plot, increase the size of the points, and manually change the color of the points.

ggplot(data = penguins) + geom_point(aes(x = body_mass_g, y = bill_length_mm), shape = 15,
    size = 5, color = "blue")
Warning: Removed 2 rows containing missing values (geom_point).

Scale customization

Scaling is the name of the process used by ggplot2 to map a variable to a unique value. This is particularly important for factor variables. For example, color scaling is the process by which the the color aesthetic is mapped to particular colors. These can be customized using the various scale_* functions, where the “*” is a placeholder for the rest of the function name.

Let’s manually set the color scaling using the scale_color_manual function. The colors provided below come from the Dark2 color palette of the qualitative type provided by the ColorBrewer website (https://colorbrewer2.org/), which provides color advice. It’s particularly great for identifying colorblind-friendly color palettes.

ggp + scale_color_manual(values = c("#1b9e77", "#d95f02", "#7570b3"))
Warning: Removed 2 rows containing missing values (geom_point).

Alternatively, we could have used scale_color_brewer function instead, which automatically includes the color palettes desigend by Color Brewer. Run ?scale_color_brewer for more details.

ggp + scale_color_brewer(type = "seq", palette = "Dark2")
Warning: Removed 2 rows containing missing values (geom_point).

Scalings can be customized in many ways, but it quickly becomes complicated, so we don’t discuss them further.

Legend customization

The legend can be customized in many ways. To move the location of the legend, you can specify the legend.position argument of the theme function.

ggp + theme(legend.position = "bottom")
Warning: Removed 2 rows containing missing values (geom_point).

You can customize legend size, background, names, colors, etc. For specific implementation details, it is probably best to do a web search.

Themes

A theme is a customized style that changes the overall appearance of your graphic. ggplot2 provides a number of built-in themes. Some of the built-in themes include:

  • theme_gray: the default theme. Gray background and white grid lines.
  • theme_bw: white background and gray grid lines.
  • theme_classic: white background and no grid lines.
  • theme_minimal: white background, no grid lines, no axis lines.
  • theme_light: white background, gray grid lines, and a box around the plotting region.
  • theme_dark: essentially a “dark” opposite to theme_light.

We will use the patchwork package (Pedersen 2022) to easily combine several plots into a single graphic. The patchwork package uses () to group a set of plots, | to separate plots horizontally, and / to stack sets of plots vertically.

ggp_gray <- ggp + theme_gray() + ggtitle("theme_gray")
ggp_bw <- ggp + theme_bw() + ggtitle("theme_bw")
ggp_classic <- ggp + theme_classic() + ggtitle("theme_classic")
ggp_minimal <- ggp + theme_minimal() + ggtitle("theme_minimal")
ggp_light <- ggp + theme_light() + ggtitle("theme_light")
ggp_dark <- ggp + theme_dark() + ggtitle("theme_dark")
library(patchwork)
(ggp_gray | ggp_bw)/(ggp_classic | ggp_minimal)/(ggp_light | ggp_dark)
Warning: Removed 2 rows containing missing values (geom_point).
Removed 2 rows containing missing values (geom_point).
Removed 2 rows containing missing values (geom_point).
Removed 2 rows containing missing values (geom_point).
Removed 2 rows containing missing values (geom_point).
Removed 2 rows containing missing values (geom_point).

These themes can be customized further by changing the appropriate aspects of the plots. We leave the user to use web searches for the desired customizations.

Other components of a ggplot

Facetting (Video: YouTube, Panopto)

Facetting a data set creates separate plotting panels for each level of one or more discrete variables. Facetting is useful for examining patterns for combinations of levels for one or more discrete variables. Technically, facetting can be performed for numeric variables with a relatively small number of unique values, but for practical reasons, facetting is most appropriate for factor variables.

There are two functions for creating facetted graphics in ggplot2:

  • facet_wrap facets the plots based on a single factor variable. The panels are wrapped around the plotting window.
  • facet_grid forms a matrix of panels based on row and column facetting variables.

In the plot below, we facet scatter plots of bill_length_mm versus body_mass_g by species.

ggplot(penguins) + geom_point(aes(x = body_mass_g, y = bill_length_mm)) + facet_wrap(~species)
Warning: Removed 2 rows containing missing values (geom_point).

By default, the same x and y axes are used in all panels. This is standard because it facilitates direct comparisons across panels. This can be customized using the scales argument of the facet_* functions. The allowable scales argument values are:

  • fixed: same x and y axes for all panels. The default.
  • free: individual x and y axes for each panel
  • free_x: individual x axes for each panel, common y axis
  • free_y: individual y axes for each panel, common x axis

We now consider use the facet_grid function to facet by two factor variables, species and sex. Prior to plotting, we remove the observations with NA for the sex variable.

ggplot(subset(penguins, !is.na(sex))) + geom_point(aes(x = body_mass_g, y = bill_length_mm)) +
    facet_grid(species ~ sex)

There appears to be differences in the sizes of the male and female penguins, bit it is difficult to compare the patterns across panels. It may be more useful to facet by species but distinguish by sex in a single panel.

ggplot(subset(penguins, !is.na(sex))) + geom_point(aes(x = body_mass_g, y = bill_length_mm,
    color = sex, shape = sex)) + facet_wrap(~species)

It is clear now that across species, the males tend to have greater body mass and bill length in comparison to the females.

Statistical transformations (Video: YouTube, Panopto)

Statistical transformations are the process that ggplot2 uses to summarize the data before plotting. By default, every geom uses a default stat argument that summarizes the data before plotting. Perhaps surprisingly, every stat_* has a default geom argument to specify how the summarized data should be plotted.

In general, you can simply learn to use the geom that summarizes and plots the data to your liking. Depending on the data you have, sometimes you may want to specify a different stat argument. In general, custom stat arguments are beyond the scope of this tutorial.

However, we do consider a simple example involving bar charts.

By default, geom_bar uses stat_count to count the number of values having each level of a factor variable and then plots the height of each bar above the names of the factor levels.

ggplot(penguins) + geom_bar(aes(x = sex))

However, let’s suppose our data frame already has counts for the number of values in each level. In the case, we must change the stat argument for geom_bar to "identity" (for stat_identity). We do this in the example below, where we also have to clearly indicate the x variable and the y variable in the plot.

# count values with each level
penguins_count <- as.data.frame(table(penguins$sex, useNA = "ifany"))
# rename variales
names(penguins_count) <- c("sex", "count")
# print data frame
penguins_count
# create plot
ggplot(penguins_count) + geom_bar(aes(x = sex, y = count), stat = "identity")

Position

The position argument of a geom describes the positions of the objects produced. Most of the time this is pretty straightforward, but there can be times when it is helpful (or unhelpful) to customize position.

Jittering data (Video: YouTube, Panopto)

Sometimes, data values/information overlap, which makes interpreting the plot more difficult. In that case, it can be useful to “jitter” the data so that the values/information are not overlapping. The data are not technically as accurate, but the large-scale distribution of the data is easier to interpret.

The Galton data set in the HistData package (Friendly 2021) includes 928 observations of 2 variales. The variables are:

  • parent: a numeric variable indicating the average height of a child’s mother and father (in).
  • child: a numeric variable indicating the height of each child (in).

The data are rounded to the nearest 0.1 inch. Additional information can be found by running ?HistData::Galton in the Console.

Unfortunately, the rounding the data values leads to overplotting. Below, we load the Galton data and create a scatter plot of child height versus parents’ average height.

data(Galton, package = "HistData")
ggplot(Galton) + geom_point(aes(x = parent, y = child))

There is a lot of overplotting in this graphic. We use the position argument to jitter the data to make the overall distribution clearer. There’s not a lot of information gain from jittering the data in this particular example, but we can see that there are more observations in the central part of the plot.

ggplot(Galton) + geom_point(aes(x = parent, y = child), position = "jitter")

Stacking, filling, and dodging (Video: YouTube, Panopto)

I was tempted to title this section, “plots that make me emotional” because several of the graphics below cause me to go through a range of mostly unpleasant emotions when I view them.

In this section, we’ll see how stack, fill, and dodge can be used in the position argument. While these examples demonstrate their use, some of the results are an absolute mess. I will conclude with alternative recommendations.

When creating a bar chart, a factor variable is mapped to the x aesthetic. If a different factor variable is mapped to the fill aesthetic, then ggplot2 will stack the bars for the fill variable inside the bars for the x variable. Consider the bar chart below in which we “stack” penguin sex within the bars for the different species.

ggplot(penguins) + geom_bar(aes(x = species, fill = sex))

If you compare this bar chart with the original one that doesn’t fill by sex, the bar height is identical. However, within each species, we fill the bars to show the number of penguins with each level of sex.

This plot is difficult to interpret. It takes a lot of mental energy to compute approximately how many male, female, and NA observations there are within each species. While the updated bar chart compactly provides a lot of information, it doesn’t facilitate easy interpretation.

If we wanted to compare the proportion of male, female, and NA penguins in each species, we can change the position argument to fill. In that case, the bars within each level of species will be scaled to one, directly facilitating comparisons between the colors within each bar.

ggplot(penguins) + geom_bar(aes(x = species, fill = sex), position = "fill")

I still consider this a poor display of the data, but we can see that the proportion of males is close to 50% for all species.

Lastly, if we don’t want to stack or fill the bars, we can have them dodge each other. In that case, the overlapping bars will “dodge” each other and sit next to each other in the plot.

ggplot(penguins) + geom_bar(aes(x = species, fill = sex), position = "dodge")

If you want to compare the number of penguins with each sex within each species, this graphic isn’t too bad. However, the bars representing male penguins (or alternatively, female penguins) are spread out and more difficult to compare directly because of the other bars between them.

While the above charts provide a fair amount of information compactly, to facilitate ease of interpretation we should construct our graphics to highlight the characteristic of importance.

e.g., If we want to compare the counts of each penguin species within each sex, it would be better to facet the data by sex and display the count of each species.

ggplot(penguins) + geom_bar(aes(x = species, fill = species)) + facet_wrap(~sex)

Alternatively, if we want to highlight the composition of sex within each species, then I recommend creating a bar chart of sex while facetting by species. This plot is quite similar to the “dodge” plot above, but provides further distinction between the charts for each species.

ggplot(penguins) + geom_bar(aes(x = sex, fill = sex)) + facet_wrap(~species)

The examples above are shown for factor variables with only three levels of each factor, and you may think I’m overreacting (and perhaps I am). However, the issues mentioned above are magnified when our factor variables have many levels. To illustrate this point, I present “stack” and “dodge” bar charts of the diamonds data in the ggplot2 package, in which we compare the cut and clarity of diamonds. Each variable has at least 5 levels. The large number of levels for the cut and clarity variables make it difficult to quickly interpret the relationship between the two variables. The patchwork package is used to combine the two charts into a single graphic. (We also rotate the x-axis labels 90 degrees so they don’t overlap each other). How easy is it for you to interpret the information below?

ggp_stack <- ggplot(diamonds) + geom_bar(aes(x = cut, fill = clarity)) + ggtitle("stack") +
    theme(axis.text.x = element_text(angle = 90, hjust = 1))
ggp_dodge <- ggplot(diamonds) + geom_bar(aes(x = cut, fill = clarity), position = "dodge") +
    ggtitle("dodge") + theme(axis.text.x = element_text(angle = 90, hjust = 1))
ggp_stack + ggp_dodge

Coordinate Systems (Video: YouTube, Panopto)

A coordinate system is used to define the position of coordinates relative to one another. By default, ggplot2 uses the Cartesian coordinate system (https://en.wikipedia.org/wiki/Cartesian_coordinate_system), which treats coordinates as points on a flat plane, which is how humans tend to think about coordinates in relationship to one another.

Other coordinates systems are commonly used for maps because points are actually on an ellipsoidal object (the earth!), which complicates their relationship.

We do not discuss coordinate systems in detail here. The only coordinate system I want to mentioned is the “flipped” coordinate system (coord_flip), which flips the x and y axes. This is useful for rotating objects in a plot. Consider the boxplots below.

ggplot(penguins) + geom_boxplot(aes(x = species, y = body_mass_g, fill = species))
Warning: Removed 2 rows containing non-finite values (stat_boxplot).

ggplot(penguins) + geom_boxplot(aes(x = species, y = body_mass_g, fill = species)) +
    coord_flip()
Warning: Removed 2 rows containing non-finite values (stat_boxplot).

References

Becker, R. A., W. S. Cleveland, and M. J. Shyu. 1996. “The Visual Design and Control of Trellis Display.” Journal of Computational and Graphical Statistics 5 (2): 123–55.
Friendly, Michael. 2021. HistData: Data Sets from the History of Statistics and Data Visualization. https://CRAN.R-project.org/package=HistData.
Garnier, Simon. 2021. Viridis: Colorblind-Friendly Color Maps for r. https://CRAN.R-project.org/package=viridis.
Gorman, Kristen B., Tony D. Williams, and William R. Fraser. 2014. “Ecological Sexual Dimorphism and Environmental Variability Within a Community of Antarctic Penguins (Genus Pygoscelis).” PLOS ONE 9 (3): 1–14. https://doi.org/10.1371/journal.pone.0090081.
Horst, Allison, Alison Hill, and Kristen Gorman. 2020. Palmerpenguins: Palmer Archipelago (Antarctica) Penguin Data. https://CRAN.R-project.org/package=palmerpenguins.
Pedersen, Thomas Lin. 2022. Patchwork: The Composer of Plots. https://CRAN.R-project.org/package=patchwork.
Wickham, Hadley. 2016. Ggplot2: Elegant Graphics for Data Analysis. Springer-Verlag New York. https://ggplot2.tidyverse.org.
Wickham, Hadley, Winston Chang, Lionel Henry, Thomas Lin Pedersen, Kohske Takahashi, Claus Wilke, Kara Woo, Hiroaki Yutani, and Dewey Dunnington. 2022. Ggplot2: Create Elegant Data Visualisations Using the Grammar of Graphics. https://CRAN.R-project.org/package=ggplot2.
Wickham, Hadley, and Garrett Grolemund. 2017. R for Data Science. O’Reilly Media, Inc.
Wilkinson, Leland. 2005. The Grammar of Graphics (Statistics and Computing). Berlin, Heidelberg: Springer-Verlag.
LS0tDQp0aXRsZTogIkJhc2ljIGRhdGEgdmlzdWFsaXphdGlvbiB3aXRoIGdncGxvdDIiDQphdXRob3I6ICJKb3NodWEgRnJlbmNoIg0KZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJVktJW0tJWQnKWAiDQpvdXRwdXQ6DQogIGJvb2tkb3duOjpodG1sX25vdGVib29rMjoNCiAgICBudW1iZXJfc2VjdGlvbnM6IEZBTFNFDQpiaWJsaW9ncmFwaHk6DQotIGR3di5iaWINCi0gcGFja2FnZXNfYmFzaWNfZGF0YV92aXouYmliDQotLS0NCg0KVGhpcyBkb2N1bWVudCBpcyBpbnRlbmRlZCB0byBoZWxwIHlvdSBxdWlja2x5IGxlYXJuIGhvdyB0byBwZXJmb3JtIGJhc2ljIGRhdGEgdmlzdWFsaXphdGlvbiB1c2luZyAqKmdncGxvdDIqKi4NCg0KQW4gYWNjb21wYW55aW5nIFlvdVR1YmUgcGxheWxpc3QgdGhhdCB3YWxrcyB0aHJvdWdoIHRoaXMgZG9jdW1lbnQgaXMgYXZhaWxhYmxlIGJ5IGZvbGxvd2luZyB0aGUgbGluayBbaGVyZV0oaHR0cHM6Ly95b3V0dWJlLmNvbS9wbGF5bGlzdD9saXN0PVBMa3JKckxzN3hmYlZiSTFXSHM4RmZrT29GdnRpSTZFWGQpLiBJIGhhdmUgYWxzbyBpbmNsdWRlZCBkaXJlY3QsIHJlbGV2YW50IHZpZGVvIGxpbmtzIHRocm91Z2hvdXQgdGhlIGZpbGUgKGUuZy4sIGltbWVkaWF0ZWx5IGFmdGVyIHRoZSB0aGUgcmVsZXZhbnQgaGVhZGluZykuIEFuIGh0bWwgdmVyc2lvbiBvZiB0aGlzIGRvY3VtZW50IGNhbiBiZSBkb3dubG9hZGVkIHRvIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdGlvbiAocnVuIGBnZXR3ZCgpYCBpbiB0aGUgQ29uc29sZSB0byBzZWUgd2hlcmUgdGhpcyBpcykgYnkgcnVubmluZyB0aGUgZm9sbG93aW5nIGNvbW1hbmQ6DQpgYGB7ciwgZXZhbD1GQUxTRX0NCmRvd25sb2FkLmZpbGUoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9qZnJlbmNoL0RhdGFXcmFuZ2xlVml6L21hc3Rlci8wMy1iYXNpYy1kYXRhLXZpei13aXRoLWdncGxvdDIubmIuaHRtbCIsDQogICAgICAgICAgICAgICIwMy1iYXNpYy1kYXRhLXZpei13aXRoLWdncGxvdDIubmIuaHRtbCIpDQpgYGANClRoZSByYXcgUiBNYXJrZG93biBjb2RlIHVzZWQgdG8gZ2VuZXJhdGUgdGhlIGh0bWwgZmlsZSBjYW4gYmUgb2J0YWluZWQgYnkgdG9nZ2xpbmcgdGhlICJDb2RlIiBib3ggaW4gdGhlIHVwcGVyLXJpZ2h0IGNvcm5lciBvZiB0aGUgaHRtbCBmaWxlIGFuZCBzZWxlY3RpbmcgIkRvd25sb2FkIFJtZCIuDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICB0aWR5ID0gVFJVRQ0KKQ0KYGBgDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCiMgYXV0b21hdGljYWxseSBjcmVhdGUgYSBiaWIgZGF0YWJhc2UgZm9yIFIgcGFja2FnZXMNCmtuaXRyOjp3cml0ZV9iaWIoYygNCiAgLnBhY2thZ2VzKCksICdnZ3Bsb3QyJywgJ3BhdGNod29yaycsICdwYWxtZXJwZW5ndWlucycsDQogICdIaXN0RGF0YScsICd2aXJpZGlzJw0KKSwgJ3BhY2thZ2VzX2Jhc2ljX2RhdGFfdml6LmJpYicpDQpgYGANCg0KIyMgSW50cm9kdWN0aW9uIHRvIGJhc2ljIGRhdGEgdmlzdWFsaXphdGlvbiB3aXRoICoqZ2dwbG90MioqIChWaWRlbzogW1lvdVR1YmVdKGh0dHBzOi8veW91dHUuYmUvbHBsZ1Y1S1Q2MnMpLCBbUGFub3B0b10oaHR0cHM6Ly91Y2RlbnZlci5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9WaWV3ZXIuYXNweD9pZD1mNDRhOGE4NC01MGRlLTQxYzYtOGU5ZS1hZjBmMDE0NTQ5NjEpKQ0KDQpJbiB0aGlzIG1vZHVsZSB3ZSBkaXNjdXNzIGhvdyB0byBwZXJmb3JtIGJhc2ljIGRhdGEgdmlzdWFsaXphdGlvbi4NCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuL2ZpZ3VyZXMvZ2dwbG90Ml9sb2dvLnBuZyIpDQpgYGANCg0KV2Ugd2lsbCB1c2UgKipnZ3Bsb3QyKiogW0BnZ3Bsb3QyMjAxNjsgQFItZ2dwbG90Ml0gdG8gY29uc3RydWN0IHRoZSBncmFwaGljcyBpbiB0aGlzIG1vZHVsZS4NCg0KKipnZ3Bsb3QyKiogaGFzIGJlY29tZSBpbmNyZWFzaW5nbHkgcG9wdWxhciBmb3IgcHJvZHVjaW5nIGZsZXhpYmxlIGdyYXBoaWNzIHdpdGhpbiBhbiBlbGVnYW50IGZyYW1ld29yay4NCg0KV2Ugc3RhcnQgYnkgbG9hZGluZyB0aGUgKipnZ3Bsb3QyKiogcGFja2FnZS4NCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpgYGANCg0KYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0NCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQpsaWJyYXJ5KEhpc3REYXRhKQ0KYGBgDQoNCiMjIFRpcHMgZm9yIGdvb2QgZ3JhcGhpY3MgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9jUGZ0TlktaUtDRSksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPWYyMTMwMjIzLTNhNmMtNDY3MS1hYWRkLWFmMGYwMTRjMTFiNikpDQoNCkRhdGEgZ3JhcGhpY3Mgc2hvdWxkIGJlIGFzICppbmZvcm1hdGl2ZSogYXMgcG9zc2libGUuDQoNCi0gR3JhcGhpY3MgZG8gKm5vdCogIG5lZWQgdG8gY29udGFpbiBhcyBtdWNoICppbmZvcm1hdGlvbiogYXMgcG9zc2libGUuDQotIE1ha2UgaXQgYXMgZWFzeSB0byB1bmRlcnN0YW5kIHRoZSBpbmZvcm1hdGlvbiB5b3Ugd2FudCB0byByZXZlYWwgYWJvdXQgdGhlIGRhdGEuDQotIFNpbXBsZXIgZ3JhcGhpY3MgYXJlIGVhc2llciB0byB1bmRlcnN0YW5kIGFuZCBzaG91bGQgYmUgcHJlZmVycmVkLg0KLSBBZGRpbmcgY29tcGxleGl0eSB0byBhIGdyYXBoaWMgY2FuIHNvbWV0aW1lcyByZXZlYWwgZmFyIG1vcmUgYWJvdXQgdGhlIGRhdGEgdGhhbiBhIHNpbXBsZSBncmFwaGljLg0KLSBNYWtlIHN1cmUgbGFiZWxzIGFuZCB0ZXh0IGFyZSBsYXJnZSBlbm91Z2ggdG8gZWFzaWx5IHJlYWQuDQotIE1ha2Ugc3VyZSBjb2xvcnMgYXJlIGRpc3Rpbmd1aXNoYWJsZSBhbmQgY29sb3JibGluZCBmcmllbmRseSAoZS5nLiwgdXNpbmcgdGhlICoqdmlyaWRpcyoqIFtAUi12aXJpZGlzXSBwYWNrYWdlKS4NCg0KWW91IHdpbGwgaGF2ZSB0byBkZWNpZGUgd2hhdCBpcyBuZWVkZWQgaW4gYSBncmFwaGljIHRvIG1ha2UgaXQgYXMgaW5mb3JtYXRpdmUNCmFzIHBvc3NpYmxlLg0KDQpDb21wbGljYXRlZCBncmFwaGljcyBtYXkgcHJlc2VudCBmYXIgbW9yZSBpbmZvcm1hdGlvbiB0aGFuIHdlIGFyZSBhYmxlIHRvIHByb2Nlc3MsIHN1Y2ggYXMgaW4gdGhlIGV4YW1wbGUgYmVsb3cuDQoNCmBgYHtyLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShwYWxtZXJwZW5ndWlucykNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocGxvdGx5KQ0KaGVhdnlfcGxvdCA8LSANCiAgZ2dwbG90KHBlbmd1aW5zKSArDQogIGdlb21fcG9pbnQoYWVzKHggPSBib2R5X21hc3NfZywNCiAgICAgICAgICAgICAgICAgeSA9IGJpbGxfbGVuZ3RoX21tLA0KICAgICAgICAgICAgICAgICBjb2wgPSBzcGVjaWVzLA0KICAgICAgICAgICAgICAgICBzaGFwZSA9IGlzbGFuZCwNCiAgICAgICAgICAgICAgICAgc2l6ZSA9IGZsaXBwZXJfbGVuZ3RoX21tKSkgKw0KICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAuNSwgMi41KSkgKw0KICBnZW9tX3Ntb290aChkYXRhID0gcGVuZ3VpbnMsDQogICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGJvZHlfbWFzc19nLA0KICAgICAgICAgICAgICAgICAgeSA9IGJpbGxfbGVuZ3RoX21tLA0KICAgICAgICAgICAgICAgICAgY29sID0gc3BlY2llcyksDQogICAgICAgICAgICAgIGZvcm11bGEgPSB5IH4geCwNCiAgICAgICAgICAgICAgbWV0aG9kID0gImxvZXNzIikgKyANCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IHBlbmd1aW5zLA0KICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBib2R5X21hc3NfZywNCiAgICAgICAgICAgICAgICAgIHkgPSBiaWxsX2xlbmd0aF9tbSwNCiAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gc2V4KSwNCiAgICAgICAgICAgICAgZm9ybXVsYSA9IHkgfiB4LA0KICAgICAgICAgICAgICBtZXRob2QgPSAibG0iKQ0KZ2dwbG90bHkoaGVhdnlfcGxvdCkNCmBgYA0KDQojIyBHcmFwaGljcyBlY29zeXN0ZW1zIGluIFIgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9SMTJZQWpTemUwSSksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPWUwZGE5NjZlLTk3NTgtNGY4Ni04YzE2LWFmMGYwMTRlMWUxMCkpDQoNClRoZXJlIGFyZSB0aHJlZSBtYWluIGdyYXBoaWNzIGVjb3N5c3RlbXMgaW4gUjoNCg0KMS4gKipiYXNlKioNCjIuICoqbGF0dGljZSoqDQozLiAqKmdncGxvdDIqKg0KDQoqKmJhc2UqKiBncmFwaGljcyBhcmUgdHJhZGl0aW9uYWwgUy1saWtlIGdyYXBoaWNzLg0KDQoqIFJ1biBgP2dyYXBoaWNzLXBhY2thZ2VgIGluIHRoZSBDb25zb2xlIGZvciBtb3JlIGRldGFpbHMuDQoqIFRoZXNlIGFyZSB0aGUgZ3JhcGhpY3MgeW91IGdldCBieSBkZWZhdWx0IHdoZW4geW91IHVzZSB0aGUgYHBsb3RgIGZ1bmN0aW9uLCB0aGUgYGhpc3RgIGZ1bmN0aW9uLCB0aGUgYGJveHBsb3RgIGZ1bmN0aW9uLCBldGMuDQoNCioqbGF0dGljZSoqIGdyYXBoaWNzIGFyZSBhbiBpbXBsZW1lbnRhdGlvbiBvZiBUcmVsbGlzIGdyYXBoaWNzIFtAQmVja2VyQ2xldmVsYW5kU2h5dTE5OTZdIGZvciBSLg0KDQoqIFJ1biBgP2xhdHRpY2VgIGluIHRoZSBDb25zb2xlIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGFib3V0IHRoZSAqKmxhdHRpY2UqKiBwYWNrYWdlLg0KKiBUaGUgKipsYXR0aWNlKiogcGFja2FnZSBmb2N1c2VzIG9uIGVsZWdhbnRseSBwbG90dGluZyBtdWx0aXZhcmlhdGUgZGF0YSBhbmQgbWFrZXMgaXQgZWFzeSB0byBkaXN0aW5ndWlzaCBkaWZmZXJlbnQgbGV2ZWxzIG9mIGBmYWN0b3JgIGRhdGEuDQoqICoqbGF0dGljZSoqIGFuZCAqKmJhc2UqKiBncmFwaGljcyBkbyBub3QgaW50ZXJhY3Qgd2l0aCBlYWNoIG90aGVyIChjaGFuZ2luZyBhc3BlY3RzIG9mIG9uZSBncmFwaGljcyBzeXN0ZW0gaGFzIG5vIGltcGFjdCBvbiB0aGUgb3RoZXIuKQ0KDQoqKmdncGxvdDIqKiBpcyBhIGxheWVyZWQgZ3JhcGhpY2FsIHN5c3RlbSBiYXNlZCBvbiBpbXBsZW1lbnRpbmcgdGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MgW0BXaWxraW5zb24yMDA1XS4NCg0KKiBJdCBoYXMgZ2FpbmVkIHdpZGVzcHJlYWQgcG9wdWxhcml0eSBiZWNhdXNlIG9mIGl0cyBmcmllbmRsaW5lc3MgZm9yIHZpc3VhbCBleHBsb3JhdGlvbiBvZiBkYXRhIGJ5IGRhdGEgc2NpZW50aXN0cy4NCiogSXQgcHJvdmlkZXMgYW4gZWxlZ2FudCBhcHByb2FjaCBmb3IgY29uc3RydWN0aW5nIGNvbXBsZXggcGxvdHMgaW4gYSBzeXN0ZW1hdGljIHdheS4NCiogUnVuIGA/Z2dwbG90Mi1wYWNrYWdlYCBpbiB0aGUgQ29uc29sZSBmb3IgbW9yZSBpbmZvcm1hdGlvbi4NCg0KV2UgZGVtb25zdHJhdGUgdXNlIG9mIHRoZSB0aHJlZSBwbG90dGluZyBlY29zeXN0ZW1zIHVzaW5nIHRoZSBgcGVuZ3VpbnNgIGRhdGEgc2V0IGZyb20gdGhlICoqcGFsbWVycGVuZ3VpbnMqKiBwYWNrYWdlIFtAUi1wYWxtZXJwZW5ndWluc10sIHdoaWNoIHdpbGwgYmUgZGlzY3Vzc2VkIGluIG1vcmUgZGV0YWlsIGxhdGVyLiBUaGUgY29kZSBiZWxvdyBjcmVhdGVzIGdyb3VwZWQgc2NhdHRlciBwbG90cyBjb21wYXJpbmcgYmlsbCBsZW5ndGggKG1tKSBhbmQgYm9keSBtYXNzIChnKSBvZiBkaWZmZXJlbnQgcGVuZ3VpbiBzcGVjaWVzIHVzaW5nIGVhY2ggb2YgdGhlIGVjb3N5c3RlbXMuDQoNCmBgYHtyfQ0KIyBsb2FkIGRhdGENCmRhdGEocGVuZ3VpbnMsIHBhY2thZ2UgPSAicGFsbWVycGVuZ3VpbnMiKQ0KIyBiYXNlIGdyYXBoaWNzDQpwbG90KGJpbGxfbGVuZ3RoX21tIH4gYm9keV9tYXNzX2csIGRhdGEgPSBwZW5ndWlucywgY29sID0gcGVuZ3VpbnMkc3BlY2llcykNCmxlZ2VuZCh4ID0gInRvcGxlZnQiLCBsZWdlbmQgPSBsZXZlbHMocGVuZ3VpbnMkc3BlY2llcyksDQogICAgICAgY29sID0gYygiYmxhY2siLCAicmVkIiwgImdyZWVuIiksIHBjaCA9IDEpDQojIGxhdHRpY2UgZ3JhcGhpY3MNCmxhdHRpY2U6Onh5cGxvdChiaWxsX2xlbmd0aF9tbSB+IGJvZHlfbWFzc19nLCBkYXRhID0gcGVuZ3VpbnMsIGdyb3VwID0gc3BlY2llcywNCiAgICAgICAgICAgICAgICBhdXRvLmtleSA9IFRSVUUpDQojIGdncGxvdDIgZ3JhcGhpY3MNCmdncGxvdDI6OmdncGxvdChkYXRhID0gcGVuZ3VpbnMpICsNCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBib2R5X21hc3NfZywgeSA9IGJpbGxfbGVuZ3RoX21tLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBzcGVjaWVzLCBzaGFwZSA9IHNwZWNpZXMpKQ0KYGBgDQoNCiMjIEJhc2ljIGluZ3JlZGllbnRzIChWaWRlbzogW1lvdVR1YmUgXShodHRwczovL3lvdXR1LmJlL2s2YTN2TW9CWFN3KSwgW1Bhbm9wdG9dKGh0dHBzOi8vdWNkZW52ZXIuaG9zdGVkLnBhbm9wdG8uY29tL1Bhbm9wdG8vUGFnZXMvVmlld2VyLmFzcHg/aWQ9NTc0ZGY1YTktMzI5NC00ZDZmLThmOWUtYWYwZjAxNGU4OTI2KSkNCg0KVGhlcmUgYXJlIDQgbWFpbiBjb21wb25lbnRzIG5lZWRlZCB0byBwcm9kdWNlIGEgZ3JhcGhpYyB1c2luZyAqKmdncGxvdDIqKi4NCg0KMS4gQSBkYXRhIGZyYW1lIGNvbnRhaW5pbmcgeW91ciBkYXRhLg0KICAgICogRWFjaCBjb2x1bW4gc2hvdWxkIGJlIGEgdmFyaWFibGUgYW5kIGVhY2ggcm93IHNob3VsZCBiZSBhbiBvYnNlcnZhdGlvbiBvZiBkYXRhLg0KMi4gQSBgZ2dwbG90YCBvYmplY3QuDQogICAgKiBUaGlzIGlzIGluaXRpYWxpemVkIHVzaW5nIHRoZSBgZ2dwbG90YCBmdW5jdGlvbi4NCjMuIEEgZ2VvbWV0cmljIG9iamVjdC4NCiAgICAqIFRoZXNlIGFyZSBjYWxsZWQgImdlb21zIiBmb3Igc2hvcnQuDQogICAgKiBnZW9tcyBpbmRpY2F0ZSB0aGUgZ2VvbWV0cmljIG9iamVjdCB1c2VkIHRvIHZpc3VhbGl6ZSB0aGUgZGF0YS4gRS5nLiwgcG9pbnRzLCBsaW5lcywgcG9seWdvbnMgZXRjLiBNb3JlIGdlbmVyYWxseSwgZ2VvbXMgaW5kaWNhdGUgdGhlIHR5cGUgb2YgcGxvdCB0aGF0IGlzIGRlc2lyZWQsIGUuZy4sIGhpc3RvZ3JhbSwgZGVuc2l0eSwgb3IgYm94cGxvdCwgd2hpY2ggYXJlbid0IGV4YWN0bHkgYSBzaW1wbGUgZ2VvbWV0cmljIGFyZ3VtZW50Lg0KNC4gQW4gYWVzdGhldGljLg0KICAgICogQW4gYWVzdGhldGljIG1hcHBpbmcgaW5kaWNhdGVzIHdoYXQgcm9sZSBhIHZhcmlhYmxlIHBsYXlzIGluIHRoZSBwbG90Lg0KICAgICogZS5nLiwgd2hpY2ggdmFyaWFibGUgd2lsbCBwbGF5IHRoZSAieCIgdmFyaWFibGUgaW4gdGhlIHBsb3QsIHRoZSAieSIgdmFyaWFibGUgaW4gdGhlIHBsb3QsIGNvbnRyb2wgdGhlICJjb2xvciIgb2YgdGhlIG9ic2VydmF0aW9ucywgZXRjLg0KICANCkluICpSIGZvciBEYXRhIFNjaWVuY2UqLCBAcjRkczIwMTcgcHJvdmlkZSB0aGUgZm9sbG93aW5nIHRlbXBsYXRlIGZvciBjcmVhdGluZyBncmFwaGljcyB1c2luZyAqKmdncGxvdDIqKjoNCmBgYHtyLCBldmFsPUZBTFNFLCB0aWR5PUZBTFNFfQ0KZ2dwbG90KGRhdGEgPSA8REFUQT4pICsgPEdFT01fRlVOQ1RJT04+KG1hcHBpbmcgPSBhZXMoPE1BUFBJTkdTPikpDQpgYGANCg0KYDxEQVRBPmAsIGA8R0VPTV9GVU5DVElPTj5gLCBhbmQgYDxNQVBQSU5HUz5gIGFyZSBwbGFjZWhvbGRlcnMgdGhhdCB5b3UgcmVwbGFjZSB3aXRoIHRoZSBkYXRhIGZyYW1lLCBnZW9tZXRyaWMgb2JqZWN0LCBhbmQgYWVzdGhldGljIG1hcHBpbmdzIHlvdSB3YW50IHRvIHVzZSBpbiB5b3VyIHNwZWNpZmljIHBsb3QuDQoNCkFuIGV4cGxhbmF0aW9uIG9mIHRoZSB0ZW1wbGF0ZSBhYm92ZToNCg0KMS4gRXZlcnkgYGdncGxvdGAgc3RhcnRzIHdpdGggYSBjYWxsIHRvIHRoZSBgZ2dwbG90YCBmdW5jdGlvbi4NCiAgICAqIFlvdSBjYW4gY3JlYXRlIGEgYmxhbmsgcGxvdCBieSBydW5uaW5nIGBnZ3Bsb3QoKWAgaW4gdGhlIENvbnNvbGUuDQoyLiBHZW5lcmFsbHksIHlvdSBwYXNzIHlvdXIgZGF0YSBmcmFtZSB0byB0aGUgYGdncGxvdGAgdGhyb3VnaCB0aGUgYGRhdGFgIGFyZ3VtZW50Lg0KICAgICogWW91IGNhbiBhbHNvIHBhc3MgYGRhdGFgIGluc2lkZSB0aGUgYDxHRU9NX0ZVTkNUSU9OPmAgd2hlbiB5b3UgaW50ZW5kIHRvIHVzZSBtdWx0aXBsZSBnZW9tZXRyaWMgb2JqZWN0cyB3aXRoIGRpZmZlcmVudCBkYXRhIHNvdXJjZXMuDQozLiBUaGUgYDxHRU9NX0ZVTkNUSU9OPmAgaW5kaWNhdGVzIHRoZSBnZW9tZXRyaWMgb2JqZWN0IHlvdSB3YW50IHRvIHVzZSBpbiB0aGUgcGxvdC4NCjQuIFRoZSBgPE1BUFBJTkdTPmAgZGVzY3JpYmVzIHRoZSBhZXN0aGV0aWNzIG1hcHBpbmdzIHlvdSB3YW50IHRvIHVzZSBmb3IgdGhpcyBwYXJ0aWN1bGFyIGdlb21ldHJ5Lg0KICAgICogT25lIG9mIHRoZSByZWFzb25zICoqZ2dwbG90MioqIGlzIHNvIHBvd2VyZnVsIGlzIHRoYXQgeW91IGNhbiB1c2UgZGlmZmVyZW50IG1hcHBpbmdzIGZvciBkaWZmZXJlbnQgZ2VvbWV0cmljIG9iamVjdHMuDQo1LiBUaGUgYCtgIHN5bWJvbCBpcyB1c2VkIHRvIGFkZCBhIGxheWVyIHRvIHlvdXIgZ3JhcGhpYy4NCiAgICAqIElmIHlvdXIgY29kZSBzcGFucyBtdWx0aXBsZSBsaW5lcywgdGhlbiB5b3UgbmVlZCB0byBtYWtlIHN1cmUgYWxsIGxpbmVzIGJ1dCB0aGUgbGFzdCBlbmQgd2l0aCB0aGUgYCtgIG9wZXJhdG9yIHRvIHN0YWNrIHRoZSBsYXllcnMgcHJvcGVybHkuDQoNCiMjIyBTb21lIGdlb21ldHJpYyBvYmplY3RzDQoNClRoZXJlIGFyZSBtYW55IGdlb21ldHJpYyBvYmplY3RzIGF2YWlsYWJsZSBpbiAqKmdncGxvdDIqKi4gQSBjb21wbGV0ZSBsaXN0IG1heSBiZSAoY3VycmVudGx5KSBmb3VuZCBhdCBbaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL10oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlLykuIEEgcGFydGlhbCBsaXN0IG9mIGdlb21ldHJpYyBvYmplY3RzIHRoYXQgSSBmcmVxdWVudGx5IHVzZSBpcyBmb3VuZCBiZWxvdyBpbiBUYWJsZSBcQHJlZih0YWI6Z2VvbWV0cnktdGFibGUpLg0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCiMgY3JlYXRlIGdlb21ldHJ5IHRhYmxlDQpkYXRhX2RpbWVuc2lvbmFsaXR5IDwtIHJlcChjKCIxZCIsICIyZCIsICIzZCIsICJOQSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpbWVzID0gYyg2LCA3LCAyLCAxKSkNCmdlb21ldHJ5IDwtIGMoImBnZW9tX2JhcmAiLCAiYGdlb21fZGVuc2l0eWAiLA0KICAgICAgICAgICAgICAiYGdlb21faGlzdG9ncmFtYCIsICJgZ2VvbV9ib3hwbG90YCIsDQogICAgICAgICAgICAgICJgZ2VvbV92aW9saW5gIiwgImBnZW9tX3FxYCIsICJgZ2VvbV9wb2ludGAiLA0KICAgICAgICAgICAgICAiYGdlb21fcGF0aGAsIGBnZW9tX2xpbmVgIiwNCiAgICAgICAgICAgICAgImBnZW9tX3NlZ21lbnRgIiwNCiAgICAgICAgICAgICAgImBnZW9tX2N1cnZlYCIsICJgZ2VvbV9zbW9vdGhgIiwNCiAgICAgICAgICAgICAgImBnZW9tX2RlbnNpdHkyZGAiLCAiYGdlb21fZGVuc2l0eTJkX2ZpbGxlZGAiLA0KICAgICAgICAgICAgICAiYGdlb21fY29udG91cmAiLCAiYGdlb21fY29udG91cl9maWxsZWRgIiwNCiAgICAgICAgICAgICAgImBnZW9tX2FibGluZWAsIGBnZW9tX2hsaW5lYCwgYGdlb21fdmxpbmVgIikNCnB1cnBvc2UgPC0gYygiRHJhd3MgYSBiYXIgY2hhcnQuIiwgIkRyYXdzIGEgZGVuc2l0eSBwbG90LiIsDQogICAgICAgICAgICAgIkRyYXdzIGEgaGlzdG9ncmFtLiIsICJEcmF3cyBhIGJveHBsb3QuIiwgIkRyYXdzIGEgdmlvbGluIHBsb3QuIiwNCiAgICAgICAgICAgICAiRHJhd3MgYSBxdWFudGlsZS1xdWFudGlsZSBwbG90LiIsDQogICAgICAgICAgICAgIkRyYXdzIHBvaW50cy4gVXNlZCBmb3Igc2NhdHRlciBwbG90cy4iLA0KICAgICAgICAgICAgICJDb25uZWN0cyBvYnNlcnZhdGlvbnMuIFVzZWQgZm9yIGxpbmUgcGxvdHMuIiwNCiAgICAgICAgICAgICAiRHJhd3Mgc3RyYWlnaHQgbGluZXMgYmV0d2VlbiBwb2ludHMuIiwNCiAgICAgICAgICAgICAiRHJhd3MgY3VydmVkIGxpbmVzIGJldHdlZW4gcG9pbnRzLiIsDQogICAgICAgICAgICAgIkRyYXdzIGEgJ3Ntb290aCcgZml0dGVkIG1vZGVsIG9mIHRoZSBkYXRhLiIsDQogICAgICAgICAgICAgIkRyYXdzIDJkIGNvbnRvdXJzIG9mIGRlbnNpdHkgZXN0aW1hdGUgZm9yIHR3byB2YXJpYWJsZXMuIiwNCiAgICAgICAgICAgICAiRHJhd3MgMmQgY29udG91cnMgb2YgZGVuc2l0eSBlc3RpbWF0ZSBmb3IgdHdvIHZhcmlhYmxlcyB3aXRoIGNvbG9ycy4iLA0KICAgICAgICAgICAgICJEcmF3cyAyZCBjb250b3VycyBvZiAzZCBkYXRhLiIsDQogICAgICAgICAgICAgIkRyYXdzIDJkIGNvbnRvdXJzIG9mIDNkIGRhdGEgKGNvbG9yZWQpLiIsDQogICAgICAgICAgICAgIkRyYXdzIGRpYWdvbmFsLCBob3Jpem9udGFsLCBhbmQgdmVydGljYWwgbGluZXMuIikNCmdlb21ldHJ5X2RmIDwtIGRhdGEuZnJhbWUoZGF0YV9kaW1lbnNpb25hbGl0eSwgZ2VvbWV0cnksIHB1cnBvc2UpDQpgYGANCmBgYHtyIGdlb21ldHJ5LXRhYmxlLCBlY2hvPUZBTFNFfQ0Ka25pdHI6OmthYmxlKGdlb21ldHJ5X2RmLCBjYXB0aW9uID0gIkNvbW1vbiBnZW9tZXRyaWVzIHByb3ZpZGVkIGJ5ICoqZ2dwbG90MioqLiIsICkNCmBgYA0KDQojIyMgU29tZSBhZXN0aGV0aWMgbWFwcGluZ3MNCg0KQWVzdGhldGljIG1hcHBpbmdzIGFyZSB1bmlxdWUgdG8gZWFjaCBnZW9tZXRyaWMgb2JqZWN0LiBTb21lIG9mIHRoZSBtb3N0IGNvbW1vbiBvbmVzIHRoYXQgc2hvdyB1cCBpbiBtYW55IGdlb21zIGFyZSBwcm92aWRlZCBiZWxvdyBpbiBUYWJsZSBcQHJlZih0YWI6YWVzdGhldGljLXRhYmxlKS4NCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQojIGNyZWF0ZSBhZXN0aGV0aWMgdGFibGUNCmFlc3RoZXRpYyA8LSBjKCJgeGAiLCAiYHlgIiwgImBhbHBoYWAiLCAiYGNvbG9yYCwgYGNvbG91cmAiLA0KICAgICAgICAgICAgICAgImBmaWxsYCIsICJgZ3JvdXBgIiwgImBsaW5ldHlwZWAiLCAiYHNpemVgIiwNCiAgICAgICAgICAgICAgICJgc2hhcGVgIikNCnB1cnBvc2UgPC0gYygiQ29udHJvbHMgdGhlIHgtdmFyaWFibGUgaW4gdGhlIHBsb3QuIiwNCiAgICAgICAgICAgICAiQ29udHJvbHMgdGhlIHktdmFyaWFibGUgaW4gdGhlIHBsb3QuIiwNCiAgICAgICAgICAgICAiQ29udHJvbHMgdGhlIHRyYW5zcGFyZW5jeSBvZiB0aGUgb2JqZWN0LiIsDQogICAgICAgICAgICAgIkNvbnRyb2xzIHRoZSBjb2xvcnMgb2YgdGhlIG9iamVjdC4iLA0KICAgICAgICAgICAgICJDb250cm9scyB0aGUgY29sb3Igb2YgdGhlIGludGVyaW9yIG9mIGFuIG9iamVjdC4iLA0KICAgICAgICAgICAgICJDb250cm9scyBob3cgdGhlIGRhdGEgYXJlIGdyb3VwZWQuIiwNCiAgICAgICAgICAgICAiQ29udHJvbHMgdGhlIHR5cGUgb2YgbGluZSB1c2VkIHRvIGRyYXcgdGhlIG9iamVjdC4iLA0KICAgICAgICAgICAgICJDb250cm9scyB0aGUgc2l6ZSBvZiB0aGUgZHJhd24gb2JqZWN0LiIsDQogICAgICAgICAgICAgIkNvbnRyb2xzIHRoZSBzaGFwZSBvZiB0aGUgb2JqZWN0LiIpDQphZXN0aGV0aWNfZGYgPC0gZGF0YS5mcmFtZShhZXN0aGV0aWMsIHB1cnBvc2UpDQpgYGANCmBgYHtyIGFlc3RoZXRpYy10YWJsZSwgZWNobyA9IEZBTFNFfQ0Ka25pdHI6OmthYmxlKGFlc3RoZXRpY19kZiwgY2FwdGlvbiA9ICJDb21tb24gYWVzdGhldGljcyBwcm92aWRlZCBieSAqKmdncGxvdDIqKi4iLCApDQpgYGANCg0KIyMgU2ltcGxlIGV4YW1wbGVzDQoNCiMjIyBUaGUgYHBlbmd1aW5zYCBkYXRhIChWaWRlbzogW1lvdVR1YmVdKGh0dHBzOi8veW91dHUuYmUvWHBzVjY1dW02VWMpLCBbUGFub3B0b10oaHR0cHM6Ly91Y2RlbnZlci5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9WaWV3ZXIuYXNweD9pZD04YTFiOGQ5MS04ZTkwLTQ4MmItODUxMi1hZjBmMDE0ZTg4ZTkpKQ0KV2Ugd2lsbCB1c2UgdGhlIGBwZW5ndWluc2AgZGF0YSBzZXQgaW4gdGhlICoqcGFsbWVycGVuZ3VpbnMqKiBwYWNrYWdlIFtAUi1wYWxtZXJwZW5ndWluc10gdG8gaWxsdXN0cmF0ZSBzb21lIG9mIHRoZSB3YXlzIHRoYXQgKipnZ3Bsb3QyKiogY2FuIGJlIHVzZWQuIA0KDQpUaGUgYHBlbmd1aW5zYCBkYXRhIHNldCBwcm92aWRlcyBkYXRhIHJlbGF0ZWQgdG8gdmFyaW91cyBwZW5ndWluIHNwZWNpZXMgbWVhc3VyZWQgaW4gdGhlIFBhbG1lciBBcmNoaXBlbGFnbyAoQW50YXJjdGljYSksIG9yaWdpbmFsbHkgcHJvdmlkZWQgYnkgQEdvcm1hbkV0QWwyMDE0LiBXZSBzdGFydCBieSBsb2FkaW5nIHRoZSBkYXRhIGludG8gbWVtb3J5Lg0KDQpgYGB7cn0NCmRhdGEocGVuZ3VpbnMsIHBhY2thZ2UgPSAicGFsbWVycGVuZ3VpbnMiKQ0KYGBgDQoNClRoZSBkYXRhIHNldCBpbmNsdWRlcyBgciBucm93KHBlbmd1aW5zKWAgb2JzZXJ2YXRpb25zIG9mIGByIG5jb2wocGVuZ3VpbnMpYCB2YXJpYWJsZXMuIFRoZSB2YXJpYWJsZXMgYXJlOg0KDQoqIGBzcGVjaWVzYDogYSBgZmFjdG9yYCBpbmRpY2F0aW5nIHRoZSBwZW5ndWluIHNwZWNpZXMNCiogYGlzbGFuZGA6IGEgYGZhY3RvcmAgaW5kaWNhdGluZyB0aGUgaXNsYW5kIHRoZSBwZW5ndWluIHdhcyBvYnNlcnZlZA0KKiBgYmlsbF9sZW5ndGhfbW1gOiBhIGBudW1lcmljYCB2YXJpYWJsZSBpbmRpY2F0aW5nIHRoZSBiaWxsIGxlbmd0aCBpbiBtaWxsaW1ldGVycw0KKiBgYmlsbF9kZXB0aF9tbWA6IGEgYG51bWVyaWNgIHZhcmlhYmxlIGluZGljYXRpbmcgdGhlIGJpbGwgZGVwdGggaW4gbWlsbGltZXRlcnMNCiogYGZsaXBwZXJfbGVuZ3RoX21tYDogYW4gYGludGVnZXJgIHZhcmlhYmxlIGluZGljYXRpbmcgdGhlIGZsaXBwZXIgbGVuZ3RoIGluIG1pbGxpbWV0ZXJzDQoqIGBib2R5X21hc3NfZ2A6IGFuIGBpbnRlZ2VyYCB2YXJpYWJsZSBpbmRpY2F0aW5nIHRoZSBib2R5IG1hc3MgaW4gZ3JhbXMNCiogYHNleGA6IGEgYGZhY3RvcmAgaW5kaWNhdGluZyB0aGUgcGVuZ3VpbiBzZXggKGBmZW1hbGVgLCBgbWFsZWApDQoqIGB5ZWFyYDogYW4gaW50ZWdlciBkZW5vdGluZyB0aGUgc3R1ZHkgeWVhciB0aGUgcGVuZ3VpbiB3YXMgb2JzZXJ2ZWQgKGAyMDA3YCwgYDIwMDhgLCBvciBgMjAwOWApDQoNCiMjIyBCYXIgY2hhcnQgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9jQndDMVNLel9nQSksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPWVmNjRiNTM5LTViMmMtNDYyMS05OWFhLWFmMGYwMTRlODg3MikpDQpXZSBjb25zdHJ1Y3QgYSBiYXIgY2hhcnQgb2YgdGhlIGBzcGVjaWVzYC4gQSBiYXIgY2hhcnQgdXNlcyBiYXJzIHRvIGluZGljYXRlIHRoZSBudW1iZXIgb2YgdmFsdWVzIGVhY2ggbGV2ZWwgb2YgYSBgZmFjdG9yYCBpbmNsdWRlcy4gQWx0ZXJuYXRpdmVseSwgdGhlIGJhcnMgY2FuIGluZGljYXRlIHRoZSByZWxhdGl2ZSBmcmVxdWVuY3kgKGkuZS4sIHByb3BvcnRpb24pIG9mIHZhbHVlcyBoYXZpbmcgZWFjaCBsZXZlbCBvZiB0aGUgYGZhY3RvcmAuIA0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyBnZW9tX2JhcihhZXMoeCA9IHNwZWNpZXMpKQ0KYGBgDQoNClJlbGF0aXZlbHkgc3BlYWtpbmcsIHdlIGNhbiBzZWUgdGhhdCBBZGVsaWUgcGVuZ3VpbnMgd2VyZSBtb3N0IGZyZXF1ZW50bHkgb2JzZXJ2ZWQsIGZvbGxvd2VkIGJ5IEdlbnRvbyBwZW5ndWlucywgYW5kIHRoZW4gQ2hpbnN0cmFwIHBlbmd1aW5zLg0KDQpXZSBjYW4gY29sb3IgdGhlIGJhcnMgYnkgYHNwZWNpZXNgIGZvciB2aXN1YWwgY2xhcml0eSBieSBzcGVjaWZ5aW5nIHRoZSBgZmlsbGAgYWVzdGhldGljLg0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyBnZW9tX2JhcihhZXMoeCA9IHNwZWNpZXMsIGZpbGwgPSBzcGVjaWVzKSkNCmBgYA0KDQpUaGlzIGRvZXNuJ3QgYWRkIGFueSBuZXcgaW5mb3JtYXRpb24gdG8gdGhlIGdyYXBoaWMgYnV0IGRvZXMgbWFrZSBpdCBtb3JlIHZpc3VhbGx5IGFwcGVhbGluZy4gV2UgY291bGQgcmVtb3ZlIHRoZSBsZWdlbmQgdG8gc2ltcGxpZnkgdGhlIGdyYXBoaWMsIGlmIHdlIHdhbnRlZC4NCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9iYXIoYWVzKHggPSBzcGVjaWVzLCBmaWxsID0gc3BlY2llcykpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCiMjIyBIaXN0b2dyYW0gKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9nRlpwQk8zOEhVZyksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPTVmMzIzZGJjLTNkNmMtNDJiZC1hYzYyLWFmMGYwMTRlODhhYSkpDQoNCkEgKmhpc3RvZ3JhbSogaXMgdXNlZCB0byBkaXNwbGF5IHRoZSBkaXN0cmlidXRpb24gb2YgYSBjb250aW51b3VzIGBudW1lcmljYCB2YXJpYWJsZS4gVGhlIHJhbmdlIG9mIHRoZSB2YXJpYWJsZSBpcyBwYXJ0aXRpb25lZCBpbnRvIGNsYXNzZXMuIFRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGZhbGxpbmcgaW4gZWFjaCBjbGFzcyBpcyBjb3VudGVkLiBBIGhpc3RvZ3JhbSBkcmF3cyBhIGJhciBmb3IgZWFjaCBjbGFzcyB3aXRoIGhlaWdodCBjb3JyZXNwb25kaW5nIHRvIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIHRoYXQgY2xhc3MuDQoNCkxldCdzIGNvbnN0cnVjdCBhIGhpc3RvZ3JhbSBvZiBiaWxsIGxlbmd0aC4NCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBiaWxsX2xlbmd0aF9tbSkpDQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBpcyBtdWx0aW1vZGFsIChpLmUuLCBoYXMgbXVsdGlwbGUgcHJvbWluZW50IHBlYWtzKSwgaGF2aW5nIDIgb3IgMyBwcm9taW5lbnQgcGVha3MuIFRoZXJlIG1heSBiZSBzdWItcG9wdWxhdGlvbnMgd2Ugc2hvdWxkIGRpc3Rpbmd1aXNoLiBJdCBpc24ndCBlYXN5IHRvIGlkZW50aWZ5IHN1Yi1wb3B1bGF0aW9ucyB3aXRoIGhpc3RvZ3JhbXMgdW5sZXNzIHRoZSBzdWItcG9wdWxhdGlvbnMgYXJlIHNwbGl0IGludG8gc2VwYXJhdGUgaGlzdG9ncmFtcyAod2hpY2ggd2Ugd2lsbCBsZWFybiB0byBkbyBsYXRlciB3aXRoICJmYWNldHMiKSBvciB0byBtYWtlIHRoZSBiYXJzIHNlbWktdHJhbnNwYXJlbnQgZm9yIHRoZSBkaWZmZXJlbnQgc3ViLXBvcHVsYXRpb25zLg0KDQojIyMgRGVuc2l0eSBwbG90IChWaWRlbzogW1lvdVR1YmVdKGh0dHBzOi8veW91dHUuYmUvVUdlSjczazZCR3MpLCBbUGFub3B0b10oaHR0cHM6Ly91Y2RlbnZlci5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9WaWV3ZXIuYXNweD9pZD0xYjc4MjM0Mi04Zjc5LTQ0OTMtYTYwZC1hZjBmMDE0ZTk0MzgpKQ0KDQpBIGRlbnNpdHkgcGxvdCBpcyBvZnRlbiBwcmVmZXJyZWQgdG8gYSBoaXN0b2dyYW0gYmVjYXVzZSBpdCdzIG1vcmUgZmxleGlibGUsIHRob3VnaCBpdCBvZnRlbiBwcm92aWRlcyBzaW1pbGFyIGluZm9ybWF0aW9uLiBBIGRlbnNpdHkgcGxvdCBpcyBlc3NlbnRpYWxseSBhIHNtb290aGVkIHZlcnNpb24gb2YgYSBoaXN0b2dyYW0uIA0KDQpBIGRlbnNpdHkgcGxvdCBpcyBhcHByb3ByaWF0ZSBmb3IgZGlzcGxheWluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIGEgY29udGludW91cyBgbnVtZXJpY2AgdmFyaWFibGUgYW5kIGluZGljYXRlcyB0aGUgdmFsdWVzIGZvciB3aGljaCB0aGUgZGF0YSBpcyBtb3JlICJkZW5zZSIuIE1vcmUgc3BlY2lmaWNhbGx5LCBkZW5zaXR5IHBsb3RzIGluZGljYXRlIHRoZSB2YWx1ZXMgb2YgZGF0YSB5b3UgYXJlIG1vc3QgbGlrZWx5IHRvIG9ic2VydmUuIEEgaGlnaGVyIGRlbnNpdHkgcmVnaW9uIG1lYW5zIHlvdSBhcmUgbW9yZSBsaWtlbHkgdG8gb2JzZXJ2ZSBkYXRhIHdpdGggdmFsdWVzIGluIHRoYXQgcmVnaW9uLiBUaGVyZSBpcyBhIHN0YXRpc3RpY2FsIGRlZmluaXRpb24gd2Ugd2lsbCBub3QgZ28gaW50by4NCg0KQSBzaW1wbGUgZGVuc2l0eSBwbG90IGlzIHNob3duIGJlbG93LiBXZSBjYW4ndCBsZWFybiBhIGxvdCBiZWNhdXNlIG9mIHRoZSBzdWItcG9wdWxhdGlvbnMgcHJldmlvdXNseSBtZW50aW9uZWQuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArIGdlb21fZGVuc2l0eShhZXMoeCA9IGJpbGxfbGVuZ3RoX21tKSkNCmBgYA0KDQpUaGVyZSBhcmUgYXQgbGVhc3QgdHdvIG1vZGVzIGluIHRoZSBgYmlsbF9sZW5ndGhfbW1gIGRlbnNpdHkgcGxvdC4NCg0KTGV0J3MgY3JlYXRlIHNlcGFyYXRlIGRlbnNpdGllcyBmb3IgdGhlIGRpZmZlcmVudCBwZW5ndWluIHNwZWNpZXMgYnkgc3BlY2lmeWluZyB0aGUgYGNvbG9yYCBhZXN0aGV0aWMgdG8gKnNjYWxlKiBkaWZmZXJlbnQgY29sb3JzIHRvIHRoZSBkaWZmZXJlbnQgbGV2ZWxzIG9mIGBzcGVjaWVzYC4NCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9kZW5zaXR5KGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIGNvbG9yID0gc3BlY2llcykpDQpgYGANCg0KVGhlcmUgYXJlIGNsZWFyIGRpZmZlcmVuY2VzIGluIHRoZSBiaWxsIGxlbmd0aCBvZiBBZGVsaWUsIENoaW5zdHJhcCwgYW5kIEdlbnRvbyBwZW5ndWlucy4gVGhlIGJpbGwgbGVuZ3RoIGZvciB0aGUgQWRlbGllIHBlbmd1aW5zIGlzIGFib3V0IDM1IG1tIGFuZCBjbG9zZXIgdG8gNDcgbW0gZm9yIHRoZSBHZW50b28gcGVuZ3VpbnMuIFRoZSBDaGluc3RyYXAgcGVuZ3VpbnMgaGF2ZSB0aGUgZ3JlYXRlc3QgYmlsbCBsZW5ndGgsIGluIGdlbmVyYWwuIEl0IGFwcGVhcnMgdGhlIENoaW5zdHJhcCBwZW5ndWlucyBtYXkgaGF2ZSBhIGZ1cnRoZXIgc3ViLXBvcHVsYXRpb24uDQoNCkEgd2Vha25lc3Mgb2YgdGhlIHByZXZpb3VzIGdyYXBoaWMgaXMgdGhhdCB0aGUgbGVnZW5kIGRpc3Rpbmd1aXNoaW5nIHRoZSBkaWZmZXJlbnQgYHNwZWNpZXNgIGlzIGEgYml0IHN1YnRsZS4gVGhlIGNvbG9yZWQgbGluZSBhcm91bmQgdGhlIGJvcmRlciBtYXkgbm90IGJlIGVhc3kgdG8gZGlzdGluZ3Vpc2ggZm9yIGV2ZXJ5b25lLiBXZSBjb3VsZCB1c2UgYSBkaWZmZXJlbnQgYGxpbmV0eXBlYCBmb3IgZWFjaCBzcGVjaWVzIG9yIHdlIGNvdWxkIHVzZSBkaWZmZXJlbnQgYGNvbG9yYCBhbmQgYGxpbmV0eXBlYCBmb3IgZWFjaCBzcGVjaWVzLiBXZSBjb25zaWRlciB0aGUgZGlmZmVyZW50IHJlc3VsdHMgYmVsb3cuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArIGdlb21fZGVuc2l0eShhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCBsaW5ldHlwZSA9IHNwZWNpZXMpKQ0KZ2dwbG90KHBlbmd1aW5zKSArIGdlb21fZGVuc2l0eShhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCBjb2wgPSBzcGVjaWVzLCBsaW5ldHlwZSA9IHNwZWNpZXMpKQ0KYGBgDQoNCkFuIGV2ZW4gYmV0dGVyIG9wdGlvbiBpcyB0byBgZmlsbGAgdGhlIGRlbnNpdGllcyBmb3IgZWFjaCBzcGVjaWVzIHdpdGggZGlmZmVyZW50IGNvbG9ycy4gSG93ZXZlciwgdGhlIGRlbnNpdHkgY3VydmVzIHdpbGwgb3ZlcmxhcCwgbWFza2luZyBzb21lIG9mIHRoZSBpbmZvcm1hdGlvbi4NCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9kZW5zaXR5KGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIGZpbGwgPSBzcGVjaWVzKSkNCmBgYA0KDQpUbyBhZGRyZXNzIHRoZSBtYXNraW5nIGlzc3VlLCB3ZSBjYW4gY29udHJvbCB0aGUgdHJhbnNwYXJlbmN5IG9mIHRoZSBjb2xvcnMgdXNpbmcgdGhlIGBhbHBoYWAgYWVzdGhldGljLiBUaGlzIHZhbHVlLCBiZXR3ZWVuIDAgYW5kIDEsIGNvbnRyb2xzIGhvdyB0cmFuc3BhcmVudCB0aGUgb2JqZWN0cyBhcmUuIGAwYCBtZWFucyB0cmFuc3BhcmVudCwgYDFgIG1lYW5zIGNvbXBsZXRlbHkgb3BhcXVlLiBXZSB3aWxsIHNldCBgYWxwaGEgPSAwLjNgLiBOb3RpY2UgdGhhdCB3ZSBzcGVjaWZ5IHRoaXMgT1VUU0lERSB0aGUgYWVzdGhldGljIG1hcHBpbmcgc2luY2Ugd2UgYXJlIGNvbnRyb2xsaW5nIHRoZSBhZXN0aGV0aWMgbWFudWFsbHkgaW5zdGVhZCBvZiBoYXZpbmcgKipnZ3Bsb3QyKiogKnNjYWxlKiB0aGUgcHJvdmlkZWQgdmFyaWFibGUuIA0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyBnZW9tX2RlbnNpdHkoYWVzKHggPSBiaWxsX2xlbmd0aF9tbSwgZmlsbCA9IHNwZWNpZXMpLCBhbHBoYSA9IDAuMykNCmBgYA0KDQojIyMgU2NhbGluZw0KDQpXaGVuIGEgdmFyaWFibGUgaXMgbWFwcGVkIHRvIGFuIGFlc3RoZXRpYywgKipnZ3Bsb3QyKiogd2lsbCBhc3NpZ24gYSB1bmlxdWUgdmFsdWUgdG8gZWFjaCB1bmlxdWUgbGV2ZWwgb2YgdGhlIHZhcmlhYmxlLiBUaGlzIHByb2Nlc3MgaXMga25vd24gYXMgKnNjYWxpbmcqLg0KDQpTY2FsaW5nIGlzIG9mdGVuIG5vdCB2ZXJ5IGV4Y2l0aW5nLCBidXQgaXQgY2FuIGJlIGltcG9ydGFudCB3aGVuIGN1c3RvbWl6aW5nIHRoZSBsb29rIG9mIGEgYGdncGxvdGAuDQoNCldoZW4gYSB2YXJpYWJsZSBpcyBtYXBwZWQgdG8gYSBub24gYHhgIG9yIGB5YCBhZXN0aGV0aWMsICoqZ2dwbG90MioqIHdpbGwgYXV0b21hdGljYWxseSBhZGQgYSBsZWdlbmQgdG8gaW5kaWNhdGUgaG93IHRoZSB2YXJpYWJsZSB3YXMgc2NhbGVkLg0KDQojIyMgQm94cGxvdHMgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9FWmprZ19oc2gtdyksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPTNmNjM2NWNiLTk0ZWItNGVmOS04MjcxLWFmMGYwMTRlOTZhMykpDQoNCkEgYm94cGxvdCBpcyBhIHJvYnVzdCBkaXNwbGF5IG9mIGluZm9ybWF0aW9uIGZvciBhIGNvbnRpbnVvdXMgYG51bWVyaWNgIHZhcmlhYmxlLiBCb3hwbG90cyBhcmUgcm9idXN0IGJlY2F1c2UgdGhleSByZWx5IG9uIHJvYnVzdCBzdGF0aXN0aWNzIG5vdCBlYXNpbHkgYWZmZWN0ZWQgYnkgb3V0bGllcnMuIA0KDQpJbiBnZW5lcmFsLCBhIGJveHBsb3Q6DQoNCiogRHJhd3MgYSBib3ggZXh0ZW5kaW5nIGZyb20gUTEgKHRoZSAwLjI1IHF1YW50aWxlKSB0byBRMyAodGhlIDAuNzUgcXVhbnRpbGUpIG9mIHRoZSBkYXRhLg0KKiBBIGxpbmUgYmV0d2VlbiBRMSBhbmQgUTMgaW5kaWNhdGVzIHRoZSBtZWRpYW4gKHRoZSAwLjUgcXVhbnRpbGUpIG9mIHRoZSBkYXRhLg0KKiBBICJ3aGlza2VyIiBleHRlbmRzIGZyb20gUTEgdG8gdGhlIHNtYWxsZXN0IG9ic2VydmF0aW9uIHRoYXQgaXMgbm90IGFuIG91dGxpZXIuDQoqIEEgIndoaXNrZXIiIGV4dGVuZHMgZnJvbSBRMyB0byB0aGUgbGFyZ2VzdCBvYnNlcnZhdGlvbiB0aGF0IGlzIG5vdCBhbiBvdXRsaWVyLg0KKiBBbiAqKm91dGxpZXIqKiBpcyB0eXBpY2FsbHkgZGVmaW5lZCBhcyBhbiBvYnNlcnZhdGlvbiBzbWFsbGVyIHRoYW4gUTEgLSAxLjUgKFEzIC0gUTEpIG9yIGxhcmdlciB0aGFuIFEzICsgMS41IChRMyAtIFExKS4NCg0KUGFyYWxsZWwgYm94cGxvdHMgYXJlIGFuIGVmZmVjdGl2ZSB0b29sIGZvciBjb21wYXJpbmcgZGlmZmVyZW5jZXMgYmV0d2VlbiBhIHZhcmlhYmxlIGFjcm9zcyBtdWx0aXBsZSBsZXZlbHMgb2YgYSBgZmFjdG9yYCB2YXJpYWJsZS4NCg0KQ29uc2lkZXIgcGFyYWxsZWwgYm94cGxvdHMgb2YgYmlsbCBsZW5ndGggZGlzdGluZ3Vpc2hlZCBieSBgc3BlY2llc2AuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArIGdlb21fYm94cGxvdChhZXMoeSA9IGJpbGxfbGVuZ3RoX21tLCB4ID0gc3BlY2llcykpDQpgYGANCg0KV2UgY2FuIHNlZSB0aGF0IHRoZSB0eXBpY2FsIHJlc3BvbnNlcyBmb3IgdGhlIEFkZWxpZSBwZW5ndWlucyB0ZW5kIHRvIGJlIGxvd2VyIHRoYW4gZm9yIHRoZSBDaGluc3RyYXAgYW5kIEdlbnRvbyBwZW5ndWlucy4gVGhlIENoaW5zdHJhcCBwZW5ndWlucyB0ZW5kIHRvIGhhdmUgc2xpZ2h0bHkgbG9uZ2VyIGJlYWtzIHRoYXQgdGhlIEdlbnRvbyBwZW5ndWlucy4gQWxsIHRocmVlIHBlbmd1aW5zIHNwZWNpZXMgc2VlbSB0byBoYXZlIHNpbWlsYXIgdmFyaWFiaWxpdHkgKGkuZS4sIHRoZSBzcHJlYWQgb2YgdGhlIGRhdGEgaXMgc2ltaWxhcikuDQoNCkEgd2Vha25lc3Mgb2YgYm94cGxvdHMgaXMgdGhhdCB0aGV5IHRocm93IGF3YXkgYSBsb3Qgb2YgaW5mb3JtYXRpb24uIGUuZy4sIFdlIGRvbid0IHNlZSB0aGUgdHdvIHN1Yi1wb3B1bGF0aW9ucyBpbiB0aGUgQ2hpbnN0cmFwIHBlbmd1aW5zIHRoYXQgd2Ugbm90aWNlZCBlYXJsaWVyIHdoZW4gbG9va2luZyBhdCBkZW5zaXR5IHBsb3RzLg0KDQpBZGRpbmcgYSBgZmlsbGAgY29sb3IgdG8gdGhlIGJveHBsb3RzICh3aGlsZSByZW1vdmluZyB0aGUgbGVnZW5kKSBtaWdodCBiZSBhIG1vcmUgdmlzdWFsbHkgYXBwZWFsaW5nIGdyYXBoaWMuDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyBnZW9tX2JveHBsb3QoYWVzKHkgPSBiaWxsX2xlbmd0aF9tbSwgeCA9IHNwZWNpZXMsIGZpbGwgPSBzcGVjaWVzKSkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KIyMjIFZpb2xpbiBwbG90cyAoVmlkZW86IFtZb3VUdWJlXShodHRwczovL3lvdXR1LmJlL1FiVldjanhZZ0hZKSwgW1Bhbm9wdG9dKGh0dHBzOi8vdWNkZW52ZXIuaG9zdGVkLnBhbm9wdG8uY29tL1Bhbm9wdG8vUGFnZXMvVmlld2VyLmFzcHg/aWQ9MjdiNTBmZjYtMmFhYS00OGVmLTg1YWMtYWYwZjAxNGVhODhkKSkNCg0KQSB2aW9saW4gcGxvdCBpcyBsaWtlIGEgY3Jvc3MgYmV0d2VlbiBhIGJveHBsb3QgYW5kIGEgZGVuc2l0eSBwbG90LiBBIHZpb2xpbiBwbG90IHVuaXRlcyBhIGRlbnNpdHkgd2l0aCBpdHMgbWlycm9yIGltYWdlIGFuZCB0aGVuIGRpc3BsYXlzIHRoZSB1bmlmaWVkIG9iamVjdCBsaWtlIGEgYm94cGxvdC4gDQoNCldlIGNvbnN0cnVjdCB2aW9saW4gcGxvdHMgb2YgYmlsbCBsZW5ndGggZm9yIGVhY2ggcGVuZ3VpbiBgc3BlY2llc2AuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArIGdlb21fdmlvbGluKGFlcyh4ID0gc3BlY2llcywgeSA9IGJpbGxfbGVuZ3RoX21tKSkNCmBgYA0KDQpXaGlsZSB0aGUgQWRlbGllIGJpbGwgbGVuZ3RocyBhcmUgYXBwcm94aW1hdGVseSBzeW1tZXRyaWMgYW5kIHVuaW1vZGFsLCBib3RoIHRoZSBDaGluc3RyYXAgYW5kIEdlbnRvbyBiaWxsIGxlbmd0aHMgYXJlIGJpbW9kYWwgYW5kIGFzeW1tZXRyaWMuDQoNCiMjIyBTY2F0dGVyIHBsb3RzIChWaWRlbzogW1lvdVR1YmVdKGh0dHBzOi8veW91dHUuYmUvRGpPUm9VRE1MRDQpLCBbUGFub3B0b10oaHR0cHM6Ly91Y2RlbnZlci5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9WaWV3ZXIuYXNweD9pZD04YjE1ZjNhNC1hYWEzLTRhNzMtYWViZC1hZjBmMDE0ZWFiMGEpKQ0KDQpBIHNjYXR0ZXIgcGxvdCBkcmF3cyB0aGUgbG9jYXRpb25zIG9mICh4LCB5KSBwb3NpdGlvbnMgYmFzZWQgb24gdHdvIGBudW1lcmljYCB2YXJpYWJsZXMuIEluIGdlbmVyYWwsIHdlIHVzZSBzY2F0dGVyIHBsb3RzIHRvIGxlYXJuIGluZm9ybWF0aW9uIGFib3V0IHRoZSAqYXNzb2NpYXRpb24qIGJldHdlZW4gdGhlIHR3byB2YXJpYWJsZXMuDQoNCiogQSBwb3NpdGl2ZSBhc3NvY2lhdGlvbiBpcyBwcmVzZW50IHdoZW4gYm90aCB2YXJpYWJsZXMgdGVuZCB0byBpbmNyZWFzZSB0b2dldGhlci4NCiogQSBuZWdhdGl2ZSBhc3NvY2lhdGlvbiBpcyBwcmVzZW50IHdoZW4gb25lIHZhcmlhYmxlIHRlbmRzIHRvIGRlY3JlYXNlIHdoZW4gdGhlIG90aGVyIGluY3JlYXNlcy4NCiogQSBsaW5lYXIgYXNzb2NpYXRpb24gaXMgcHJlc2VudCB3aGVuIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdHdvIHZhcmlhYmxlcyBpcyBhIHN0cmFpZ2h0IGxpbmUuDQoqIEEgbm9ubGluZWFyIGFzc29jaWF0aW9uIGlzIHByZXNlbnQgd2hlbiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHR3byB2YXJpYWJsZXMgaXMgYSBjdXJ2ZS4NCiogSXQgaXMgcG9zc2libGUgdGhhdCBubyBjbGVhciBhc3NvY2lhdGlvbiBpcyBwcmVzZW50IGluIHRoZSBkYXRhLg0KDQpMZXQncyBjb25zaWRlciBhIHNjYXR0ZXIgcGxvdCBvZiBgYmlsbF9sZW5ndGhfbW1gIHZlcnN1cyBgYm9keV9tYXNzX2dgLg0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyBnZW9tX3BvaW50KGFlcyh4ID0gYm9keV9tYXNzX2csIHkgPSBiaWxsX2xlbmd0aF9tbSkpDQpgYGANCg0KV2Ugc2VlIGEgcG9zaXRpdmUgYXNzb2NpYXRpb24gaW4gdGhlIGdyYXBoaWMgYWJvdmUuIEFzIGBib2R5X21hc3NfZ2AgaW5jcmVhc2VzLCBgYmlsbF9sZW5ndGhfbW1gIGFsc28gdGVuZHMgdG8gaW5jcmVhc2UuIEhvd2V2ZXIsIGl0IGFwcGVhcnMgdGhhdCB0aGVyZSBtYXkgYmUgc29tZSBvYnNlcnZhdGlvbnMgaW4gdGhlIHVwcGVyIGxlZnQgcGFydCBvZiB0aGUgZ3JhcGggdGhhdCBkbyBub3QgbWF0Y2ggdGhlIG92ZXJhbGwgbGluZWFyIHRyZW5kLg0KDQpUaGlzIGNvdWxkIG1lYW46DQoNCjEuIFRoZSBkYXRhIGFyZSAqbm9pc3kqLCB3aGljaCBzaW1wbHkgbWVhbnMgdGhlIHBhdHRlcm5zIGFyZW4ndCBhcyBzdHJvbmcgYW5kIGNsZWFyIGFzIHdlIHdvdWxkIGxpa2UuDQoyLiBUaGVyZSBpcyBhIHRoaXJkIHZhcmlhYmxlIHRoYXQgbm90IGFjY291bnRlZCBmb3IgaW4gb3VyIHBsb3QgYnV0IHRoYXQgaGFzIGEgcmVsYXRpb25zaGlwIHdpdGggdGhlIG90aGVyIHR3byB2YXJpYWJsZXMuDQogICAgKiBXaGVuIHdlIGFyZSBub3QgYWNjb3VudGluZyBmb3IgdGhpcyB2YXJpYWJsZSwgaXQgaXMgY2FsbGVkIGEgKmx1cmtpbmcgdmFyaWFibGUqLg0KICAgICogV2hlbiB3ZSBhY2NvdW50IGZvciBhIGx1cmtpbmcgdmFyaWFibGUgaW4gb3VyIGFuYWx5c2lzLCBpdCBiZWNvbWVzIGEgKmNvbmZvdW5kaW5nIHZhcmlhYmxlKi4NCiAgICAqIFdlIGFyZSBkZWZpbmluZyBsdXJraW5nIGFuZCBjb25mb3VuZGluZyB2YXJpYWJsZXMgaW4gYSBnZW5lcmFsIHNlbnNlIGFuZCBub3Qgd2l0aCBzdGF0aXN0aWNhbCByaWdvci4NCg0KTGV0J3MgY29uc2lkZXIgYW4gYW5hbHlzaXMgb2YgYGJvZHlfbWFzc19nYCBhbmQgYGJpbGxfbGVuZ3RoX21tYCB0aGF0IGFjY291bnRzIGZvciBgc3BlY2llc2AuIFNwZWNpZmljYWxseSwgd2Ugd2lsbCB1c2UgdGhlIGBjb2xvcmAgYWVzdGhldGljIHNvIHRoYXQgZWFjaCBwZW5ndWluIHNwZWNpZXMgaXMgc2hvd24gaW4gYSBkaWZmZXJlbnQgY29sb3IuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArIGdlb21fcG9pbnQoYWVzKHggPSBib2R5X21hc3NfZywgeSA9IGJpbGxfbGVuZ3RoX21tLCBjb2xvciA9IHNwZWNpZXMpKQ0KYGBgDQoNCkFjY291bnRpbmcgZm9yIGBzcGVjaWVzYCBpbiB0aGUgYW5hbHlzaXMgc2hvd3MgY2xlYXIgcGF0dGVybnMgZm9yIHRoZSB0aHJlZSBwZW5ndWluIHNwZWNpZXMuIENoaW5zdHJhcCBwZW5ndWlucyB0ZW5kIHRvIGhhdmUgc21hbGxlciBib2R5IG1hc3MgYnV0IGxvbmdlciBiaWxsIGxlbmd0aHMuIEFkZWxpZSBwZW5ndWlucyBoYXZlIHNtYWxsZXIgYm9keSBtYXNzIGFuZCBiaWxsIGxlbmd0aHMuIEdlbnRvbyBwZW5ndWlucyB0ZW5kIHRvIGhhdmUgbGFyZ2VyIGJvZHkgbWFzcyBhbmQgYmlsbCBsZW5ndGhzIHRoYW4gdGhlIG90aGVyIHR3byBzcGVjaWVzLg0KDQpBcyBwcmV2aW91c2x5IG1lbnRpb25lZCwgd2Ugd2FudCBvdXIgcGxvdHMgdG8gYmUgYXMgY2xlYXIgYXMgcG9zc2libGUuIFNvbWUgaW5kaXZpZHVhbHMgaGF2ZSBkaWZmaWN1bHR5IGludGVycHJldGluZyBjb2xvcnMgaW4gZ3JhcGhpY3MuIFdlIGNhbiBhZGQgYW5vdGhlciBkaXN0aW5ndWlzaGluZyBjaGFyYWN0ZXJpc3RpYyBmb3IgdGhlIGRpZmZlcmVudCBzcGVjaWVzLiBJbiB0aGlzIGNhc2UsIHdlIHdpbGwgY2hhbmdlIHRoZSBgc2hhcGVgIG9mIHRoZSBwbG90dGVkIHBvaW50cyBmb3IgZWFjaCBzcGVjaWVzLiBUaGlzIHByb3ZpZGVzIGFub3RoZXIgYWlkIGZvciBjb3JyZWN0bHkgaW50ZXJwcmV0aW5nIHRoZSBncmFwaGljLg0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyBnZW9tX3BvaW50KGFlcyh4ID0gYm9keV9tYXNzX2csIHkgPSBiaWxsX2xlbmd0aF9tbSwgY29sb3IgPSBzcGVjaWVzLCBzaGFwZSA9IHNwZWNpZXMpKQ0KYGBgDQoNCiMjIyBBZGRpbmcgc21vb3RocyAoVmlkZW86IFtZb3VUdWJlXShodHRwczovL3lvdXR1LmJlL1AyX3FZQlYxQnljKSwgW1Bhbm9wdG9dKGh0dHBzOi8vdWNkZW52ZXIuaG9zdGVkLnBhbm9wdG8uY29tL1Bhbm9wdG8vUGFnZXMvVmlld2VyLmFzcHg/aWQ9ZGZlY2EwY2MtNDU5MS00MzA2LTllODUtYWYwZjAxNGViOTViKSkNCg0KQSBzbW9vdGggaW4gKipnZ3Bsb3QyKiogaXMgYSBtb2RlbCB0aGF0IHRoYXQgYXR0ZW1wdHMgdG8gZXN0aW1hdGUgdGhlIGF2ZXJhZ2UgcmVsYXRpb25zaGlwIGJldHdlZW4gYSByZXNwb25zZSB2YXJpYWJsZSAodHlwaWNhbGx5IHRoZSBgeWAgdmFyaWFibGUpIGFuZCBvbmUgb3IgbW9yZSBwcmVkaWN0b3IgdmFyaWFibGVzICh0eXBpY2FsbHkgdGhlIGB4YCAgYW5kIHBvc3NpYmx5IG90aGVyIHZhcmlhYmxlcykNCg0KQWRkaW5nIGEgInNtb290aCIgdG8gYSBwbG90IGNhbiBtYWtlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB2YXJpYWJsZXMgZXZlbiBjbGVhcmVyLiBUaGVyZSBhcmUgbWFueSBkaWZmZXJlbnQgc21vb3RoaW5nIG1ldGhvZHMuDQoNCkEgc21vb3RoaW5nIGxheWVyIGNhbiBiZSBhZGRlZCB0byBhIHBsb3QgdXNpbmcgYGdlb21fc21vb3RoYC4gTWFueSBtZXRob2RzIGNhbiBiZSB1c2VkIGZvciB0aGUgc21vb3RoLiAqKmdncGxvdDIqKiB0YWtlcyB0aGUgbWV0aG9kcyBgbG1gIChsaW5lYXIgbW9kZWwpLCBgZ2xtYCAoZ2VuZXJhbGl6ZWQgbGluZWFyIG1vZGVsKSwgYGdhbWAgKGdlbmVyYWxpemVkIGFkZGl0aXZlIG1vZGVsKSwgYGxvZXNzYCAobG9jYWwgZml0dGluZyksIG9yIGEgZnVuY3Rpb24gKGUuZy4sIGBNQVNTOjpybG1gIGZvciBhIHJvYnVzdCBsaW5lYXIgbW9kZWwpLiBJZiB5b3UgZG9uJ3Qga25vdyB3aGF0IHRoZXNlIGFyZSwgZG8gbm90IGFkZCB0aGVtIHRvIHlvdXIgcGxvdCBiZWNhdXNlIHRoZXkgd29uJ3QgaGVscCB5b3UgdG8gZXhwbGFpbiB0aGUgZGF0YS4NCg0KQSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgZml0cyB0aGUgc3RyYWlnaHQgbGluZSB0aGF0IG1pbmltaXplcyB0aGUgc3F1YXJlZCBkZXZpYXRpb25zIGJldHdlZW4gdGhlIHkgdmFsdWVzIGFuZCB0aGUgZXN0aW1hdGVkIGxpbmUuIFdlIGFkZCBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbGluZSB0byB0aGUgc2NhdHRlciBwbG90IGJ5IGFkZGluZyBhIGBnZW9tX3Ntb290aGAgbGF5ZXIuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArIGdlb21fcG9pbnQoYWVzKHggPSBib2R5X21hc3NfZywgeSA9IGJpbGxfbGVuZ3RoX21tLCBjb2xvciA9IHNwZWNpZXMsIHNoYXBlID0gc3BlY2llcykpICsgZ2VvbV9zbW9vdGgoYWVzKHggPSBib2R5X21hc3NfZywgeSA9IGJpbGxfbGVuZ3RoX21tKSwgbWV0aG9kID0gImxtIikNCmBgYA0KDQpUaGlzIG9ubHkgYWRkcyBhIHNpbmdsZSBsaW5lIGFjcm9zcyBhbGwgZ3JvdXBzLCB3aGljaCBjbGVhcmx5IGRvZXNuJ3QgZXhwbGFpbiB0aGUgcmVsYXRpb25zaGlwIHdoZW4gYWNjb3VudGluZyBmb3IgYHNwZWNpZXNgLiBUbyBhZGQgYSBzZXBhcmF0ZSBsaW5lIGZvciBlYWNoIGBzcGVjaWVzYCwgd2UgbmVlZCB0byBpbmNsdWRlIHRoYXQgaW4gdGhlIGFlc3RoZXRpYyBtYXBwaW5nIGZvciBgZ2VvbV9zbW9vdGhgLiBJZiB3ZSBzY2FsZSB0aGUgYHNwZWNpZXNgIHZhcmlhYmxlIHVzaW5nIHRoZSBgY29sb3JgIGFlc3RoZXRpYywgdGhlbiBhIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBsaW5lIHdpbGwgYmUgZXN0aW1hdGVkIGZvciBlYWNoIHNwZWNpZXMgaW4gdGhlIGNvbG9yIHRoYXQgbWF0Y2hlcyB0aGUgc2NhdHRlciBwbG90Lg0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyBnZW9tX3BvaW50KGFlcyh4ID0gYm9keV9tYXNzX2csIHkgPSBiaWxsX2xlbmd0aF9tbSwgY29sb3IgPSBzcGVjaWVzLCBzaGFwZSA9IHNwZWNpZXMpKSArIGdlb21fc21vb3RoKGFlcyh4ID0gYm9keV9tYXNzX2csIHkgPSBiaWxsX2xlbmd0aF9tbSwgY29sb3IgPSBzcGVjaWVzKSwgbWV0aG9kID0gImxtIikNCmBgYA0KDQojIyMgVXNpbmcgY29tbW9uIGFlc3RoZXRpY3MgYWNyb3NzIGdlb21ldHJpZXMgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9vRldIQVRhN0FRdyksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPTQwNjVmNzQ2LTVjZjItNDVlZi1hNWQ4LWFmMGYwMTRlYmNjNykpDQoNCk5vdGljZSB0aGF0IHdlIGhhZCB0byBzcGVjaWZ5IHRoZSBhZXN0aGV0aWMgbWFwcGluZ3MgaW4gZWFjaCBnZW9tLiBUaGlzIHNlZW1zIHJlZHVuZGFudC4NCg0KSWYgeW91IHNwZWNpZnkgYW4gYWVzdGhldGljIG1hcHBpbmcgaW4gYSBnZW9tLCB0aGVuIHRoZSBhZXN0aGV0aWMgbWFwcGluZyBpcyAqbG9jYWwqIGFuZCBvbmx5IGFwcGxpZXMgdG8gdGhhdCBwYXJ0aWN1bGFyIGdlb20uIElmIHlvdSB3YW50IHRvIHNwZWNpZnkgYSAqZ2xvYmFsKiBhZXN0aGV0aWMgbWFwcGluZyB0aGF0IGFwcGxpZXMgYWNyb3NzIGFsbCBnZW9tcywgdGhlbiB5b3UgY2FuIHNwZWNpZnkgdGhlIGBtYXBwaW5nYCBpbiB0aGUgYGdncGxvdGAgZnVuY3Rpb24uDQoNCkhlcmUgaXMgYSBzaW1wbGVyIHZlcnNpb24gb2YgdGhlIHByZXZpb3VzIHBsb3QuDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgID0gcGVuZ3VpbnMsIG1hcHBpbmcgPSBhZXMoeCA9IGJvZHlfbWFzc19nLCB5ID0gYmlsbF9sZW5ndGhfbW0sIGNvbG9yID0gc3BlY2llcykpICsgZ2VvbV9wb2ludChhZXMoc2hhcGUgPSBzcGVjaWVzKSkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKQ0KYGBgDQoNCiMjIEN1c3RvbWl6aW5nIHBsb3RzIChWaWRlbzogW1lvdVR1YmVdKGh0dHBzOi8veW91dHUuYmUvUGU1cjg4MUo0cDgpLCBbUGFub3B0b10oaHR0cHM6Ly91Y2RlbnZlci5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9WaWV3ZXIuYXNweD9pZD0wZDZhNDIxZi02ZjI3LTQzYmQtYmRjYS1hZjBmMDE0ZWJlM2QpKQ0KDQpVbnN1cnByaXNpbmdseSwgdGhlIGdyYXBoaWNzIHByb2R1Y2VkIGJ5ICoqZ2dwbG90MioqIGNhbiBiZSBncmVhdGx5IGN1c3RvbWl6ZWQuIFdlIGNvbnNpZGVyIHNvbWUgb2YgdGhlIGJhc2ljIGN1c3RvbWl6YXRpb25zIGJlbG93Lg0KDQojIyMgQXNzaWduaW5nIGEgcGxvdA0KDQpPbmUgb2YgdGhlIHN0cmVuZ3RocyBvZiAqKmdncGxvdDIqKiBpcyB0aGF0IHlvdSBjYW4gZWFzaWx5IGJ1aWxkIGEgcGxvdCBsYXllciBieSBsYXllci4gSW4gdGhpcyBjYXNlLCBpdCBpcyB1c2VmdWwgdG8gYXNzaWduIHRoZSBjdXJyZW50IHBsb3QgYSBuYW1lIHRoYXQgY2FuIHRoZW4gYmUgYWRkZWQgdG8gbGF0ZXIuIA0KDQpOb3RlOiBXaGVuIHlvdSBhc3NpZ24gYSBgZ2dwbG90YCBhIG5hbWUsIGl0IHdpbGwgTk9UIGJlIGRpc3BsYXllZCwgYXMgaXMgdHlwaWNhbCB3aGVuIGFzc2lnbmluZyBvYmplY3RzIGluIFIuIFlvdSBoYXZlIHRvIHRoZW4gcHJpbnQgdGhlIG9iamVjdCB1c2luZyB0aGUgYHByaW50YCBmdW5jdGlvbiBvciBieSBzaW1wbHkgcnVubmluZyB0aGUgb2JqZWN0IG5hbWUgaW4gdGhlIENvbnNvbGUuIExldCdzIGFzc2lnbiB0aGUgbmFtZSBgZ2dwYCB0byB0aGUgYmFzaWMgcGVuZ3VpbiBzY2F0dGVyIHBsb3QgZGlzcGxheWluZyBiaWxsIGxlbmd0aCB2ZXJzdXMgYm9keSBtYXNzLCB1c2luZyBjb2xvcnMgYW5kIHNoYXBlcyB0byBkaXN0aW5ndWlzaCBiZXR3ZWVuIGRpZmZlcmVudCBzcGVjaWVzLg0KDQpgYGB7cn0NCmdncCA8LSBnZ3Bsb3QoZGF0YSAgPSBwZW5ndWlucywgbWFwcGluZyA9IGFlcyh4ID0gYm9keV9tYXNzX2csIHkgPSBiaWxsX2xlbmd0aF9tbSwgY29sb3IgPSBzcGVjaWVzKSkgKyBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IHNwZWNpZXMpKQ0KZ2dwDQpgYGANCg0KV2UnbGwgY3VzdG9taXplIHRoaXMgcGxvdCBiZWxvdy4NCg0KIyMjIEF4aXMgbGFiZWxzIGFuZCB0aXRsZXMNClRoZSB4LWF4aXMgbGFiZWxzLCB5LWF4aXMgbGFiZWxzLCBhbmQgdGl0bGUgb2YgYSBwbG90IGFyZSBjb250cm9sbGVkIHVzaW5nIHRoZSBgeGxhYmAsIGB5bGFiYCwgYW5kIGBnZ3RpdGxlYCBmdW5jdGlvbnMsIHJlc3BlY3RpdmVseS4gTGV0J3MgY3VzdG9taXplIHRoZSBwZW5ndWluIHNjYXR0ZXIgcGxvdCBwcmV2aW91c2x5IGRpc2N1c3NlZC4gV2UnbGwgdXBkYXRlIHRoZSBgZ2dwYCBvYmplY3QgdG8gaW5jbHVkZSBvdXIgaW1wcm92ZW1lbnRzIHRoZW4gcHJpbnQgdGhlIHJlc3VsdC4NCg0KYGBge3J9DQpnZ3AgPC0gZ2dwICsgeGxhYigiYm9keSBtYXNzIChnKSIpICsgeWxhYigiYmlsbCBsZW5ndGggKG1tKSIpICsgZ2d0aXRsZSgiUGVuZ3VpbiBib2R5IGNoYXJhY3RlcmlzdGljcyIpDQpnZ3ANCmBgYA0KDQojIyMgQXhpcyBsaW1pdHMNClRoZSB4LWF4aXMgYW5kIHktYXhpcyBsaW1pdHMgYXJlIGNvbnRyb2xsZWQgdXNpbmcgdGhlIGB4bGltYCBhbmQgYHlsaW1gIGZ1bmN0aW9ucy4gRWFjaCBmdW5jdGlvbiB0YWtlcyB0aGUgbG93ZXIgYW5kIHVwcGVyIGxpbWl0IHlvdSB3YW50IHRvIHNldCwgZS5nLiwgYHhsaW0oMCwgMTAwKWAuIExldCdzIGNoYW5nZSB0aGUgYXhlcyBmb3IgdGhlIHBlbmd1aW4gc2NhdHRlciBwbG90IHByZXZpb3VzbHkgZGlzY3Vzc2VkLiBUaGVyZSdzIG5vdGhpbmcgd3Jvbmcgd2l0aCB0aGUgcHJldmlvdXMgbGltaXRzLCBidXQgd2UnbGwgZXh0ZW5kIHRoZW0gYSBiaXQgZm9yIGRlbW9uc3RyYXRpb24uDQoNCmBgYHtyfQ0KZ2dwICsgeGxpbSgyMDAwLCA3MDAwKSArIHlsaW0oMCwgODApDQpgYGANCg0KIyMjIE1hbnVhbCBhZXN0aGV0aWNzDQpBZXN0aGV0aWNzIGNhbiBiZSBzZXQgbWFudWFsbHkgaW4gYSBnZW9tIGJ5IHNwZWNpZnlpbmcgdGhlIGFlc3RoZXRpYyBkaXJlY3RseSBpbiB0aGUgZ2VvbSBidXQgb3V0c2lkZSB0aGUgYGFlc2AgZnVuY3Rpb24uIExldCdzIGNoYW5nZSB0aGUgc2hhcGUgb2YgdGhlIHBvaW50cyBmb3IgYSBzY2F0dGVyIHBsb3QsIGluY3JlYXNlIHRoZSBzaXplIG9mIHRoZSBwb2ludHMsIGFuZCBtYW51YWxseSBjaGFuZ2UgdGhlIGNvbG9yIG9mIHRoZSBwb2ludHMuDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgID0gcGVuZ3VpbnMpICsgZ2VvbV9wb2ludChhZXMoeCA9IGJvZHlfbWFzc19nLCB5ID0gYmlsbF9sZW5ndGhfbW0pLCBzaGFwZSA9IDE1LCBzaXplID0gNSwgY29sb3IgPSAiYmx1ZSIpDQpgYGANCg0KIyMjIFNjYWxlIGN1c3RvbWl6YXRpb24NCg0KKlNjYWxpbmcqIGlzIHRoZSBuYW1lIG9mIHRoZSBwcm9jZXNzIHVzZWQgYnkgKipnZ3Bsb3QyKiogdG8gbWFwIGEgdmFyaWFibGUgdG8gYSB1bmlxdWUgdmFsdWUuIFRoaXMgaXMgcGFydGljdWxhcmx5IGltcG9ydGFudCBmb3IgYGZhY3RvcmAgdmFyaWFibGVzLiBGb3IgZXhhbXBsZSwgY29sb3Igc2NhbGluZyBpcyB0aGUgcHJvY2VzcyBieSB3aGljaCB0aGUgdGhlIGBjb2xvcmAgYWVzdGhldGljIGlzIG1hcHBlZCB0byBwYXJ0aWN1bGFyIGNvbG9ycy4gVGhlc2UgY2FuIGJlIGN1c3RvbWl6ZWQgdXNpbmcgdGhlIHZhcmlvdXMgYHNjYWxlXypgIGZ1bmN0aW9ucywgd2hlcmUgdGhlICJcKiIgaXMgYSBwbGFjZWhvbGRlciBmb3IgdGhlIHJlc3Qgb2YgdGhlIGZ1bmN0aW9uIG5hbWUuDQoNCkxldCdzIG1hbnVhbGx5IHNldCB0aGUgY29sb3Igc2NhbGluZyB1c2luZyB0aGUgYHNjYWxlX2NvbG9yX21hbnVhbGAgZnVuY3Rpb24uIFRoZSBjb2xvcnMgcHJvdmlkZWQgYmVsb3cgY29tZSBmcm9tIHRoZSAqRGFyazIqIGNvbG9yIHBhbGV0dGUgb2YgdGhlICpxdWFsaXRhdGl2ZSogdHlwZSBwcm92aWRlZCBieSB0aGUgQ29sb3JCcmV3ZXIgd2Vic2l0ZSAoW2h0dHBzOi8vY29sb3JicmV3ZXIyLm9yZy9dKGh0dHBzOi8vY29sb3JicmV3ZXIyLm9yZy8pKSwgd2hpY2ggcHJvdmlkZXMgY29sb3IgYWR2aWNlLiBJdCdzIHBhcnRpY3VsYXJseSBncmVhdCBmb3IgaWRlbnRpZnlpbmcgY29sb3JibGluZC1mcmllbmRseSBjb2xvciBwYWxldHRlcy4NCg0KYGBge3J9DQpnZ3AgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiIzFiOWU3NyIsICIjZDk1ZjAyIiwgIiM3NTcwYjMiKSkNCmBgYA0KDQpBbHRlcm5hdGl2ZWx5LCB3ZSBjb3VsZCBoYXZlIHVzZWQgYHNjYWxlX2NvbG9yX2JyZXdlcmAgZnVuY3Rpb24gaW5zdGVhZCwgd2hpY2ggYXV0b21hdGljYWxseSBpbmNsdWRlcyB0aGUgY29sb3IgcGFsZXR0ZXMgZGVzaWdlbmQgYnkgQ29sb3IgQnJld2VyLiBSdW4gYD9zY2FsZV9jb2xvcl9icmV3ZXJgIGZvciBtb3JlIGRldGFpbHMuDQoNCmBgYHtyfQ0KZ2dwICsgc2NhbGVfY29sb3JfYnJld2VyKHR5cGUgPSAic2VxIiwgcGFsZXR0ZSA9ICJEYXJrMiIpDQpgYGANCg0KU2NhbGluZ3MgY2FuIGJlIGN1c3RvbWl6ZWQgaW4gbWFueSB3YXlzLCBidXQgaXQgcXVpY2tseSBiZWNvbWVzIGNvbXBsaWNhdGVkLCBzbyB3ZSBkb24ndCBkaXNjdXNzIHRoZW0gZnVydGhlci4NCg0KIyMjIExlZ2VuZCBjdXN0b21pemF0aW9uDQoNClRoZSBsZWdlbmQgY2FuIGJlIGN1c3RvbWl6ZWQgaW4gbWFueSB3YXlzLiBUbyBtb3ZlIHRoZSBsb2NhdGlvbiBvZiB0aGUgbGVnZW5kLCB5b3UgY2FuIHNwZWNpZnkgdGhlIGBsZWdlbmQucG9zaXRpb25gIGFyZ3VtZW50IG9mIHRoZSBgdGhlbWVgIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCmdncCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KYGBgDQoNCllvdSBjYW4gY3VzdG9taXplIGxlZ2VuZCBzaXplLCBiYWNrZ3JvdW5kLCBuYW1lcywgY29sb3JzLCBldGMuIEZvciBzcGVjaWZpYyBpbXBsZW1lbnRhdGlvbiBkZXRhaWxzLCBpdCBpcyBwcm9iYWJseSBiZXN0IHRvIGRvIGEgd2ViIHNlYXJjaC4NCg0KIyMjIFRoZW1lcw0KDQpBIHRoZW1lIGlzIGEgY3VzdG9taXplZCBzdHlsZSB0aGF0IGNoYW5nZXMgdGhlIG92ZXJhbGwgYXBwZWFyYW5jZSBvZiB5b3VyIGdyYXBoaWMuICoqZ2dwbG90MioqIHByb3ZpZGVzIGEgbnVtYmVyIG9mIGJ1aWx0LWluIHRoZW1lcy4gU29tZSBvZiB0aGUgYnVpbHQtaW4gdGhlbWVzIGluY2x1ZGU6DQoNCiogYHRoZW1lX2dyYXlgOiB0aGUgZGVmYXVsdCB0aGVtZS4gR3JheSBiYWNrZ3JvdW5kIGFuZCB3aGl0ZSBncmlkIGxpbmVzLg0KKiBgdGhlbWVfYndgOiB3aGl0ZSBiYWNrZ3JvdW5kIGFuZCBncmF5IGdyaWQgbGluZXMuDQoqIGB0aGVtZV9jbGFzc2ljYDogd2hpdGUgYmFja2dyb3VuZCBhbmQgbm8gZ3JpZCBsaW5lcy4NCiogYHRoZW1lX21pbmltYWxgOiB3aGl0ZSBiYWNrZ3JvdW5kLCBubyBncmlkIGxpbmVzLCBubyBheGlzIGxpbmVzLg0KKiBgdGhlbWVfbGlnaHRgOiB3aGl0ZSBiYWNrZ3JvdW5kLCBncmF5IGdyaWQgbGluZXMsIGFuZCBhIGJveCBhcm91bmQgdGhlIHBsb3R0aW5nIHJlZ2lvbi4NCiogYHRoZW1lX2RhcmtgOiBlc3NlbnRpYWxseSBhICJkYXJrIiBvcHBvc2l0ZSB0byBgdGhlbWVfbGlnaHRgLg0KDQpXZSB3aWxsIHVzZSB0aGUgKipwYXRjaHdvcmsqKiBwYWNrYWdlIFtAUi1wYXRjaHdvcmtdIHRvIGVhc2lseSBjb21iaW5lIHNldmVyYWwgcGxvdHMgaW50byBhIHNpbmdsZSBncmFwaGljLiBUaGUgKipwYXRjaHdvcmsqKiBwYWNrYWdlIHVzZXMgYCgpYCB0byBncm91cCBhIHNldCBvZiBwbG90cywgYHxgIHRvIHNlcGFyYXRlIHBsb3RzIGhvcml6b250YWxseSwgYW5kIGAvYCB0byBzdGFjayBzZXRzIG9mIHBsb3RzIHZlcnRpY2FsbHkuDQoNCmBgYHtyfQ0KZ2dwX2dyYXkgPC0gZ2dwICsgdGhlbWVfZ3JheSgpICsgZ2d0aXRsZSgidGhlbWVfZ3JheSIpDQpnZ3BfYncgPC0gZ2dwICsgdGhlbWVfYncoKSArIGdndGl0bGUoInRoZW1lX2J3IikNCmdncF9jbGFzc2ljIDwtIGdncCArIHRoZW1lX2NsYXNzaWMoKSArIGdndGl0bGUoInRoZW1lX2NsYXNzaWMiKQ0KZ2dwX21pbmltYWwgPC0gZ2dwICsgdGhlbWVfbWluaW1hbCgpICsgZ2d0aXRsZSgidGhlbWVfbWluaW1hbCIpDQpnZ3BfbGlnaHQgPC0gZ2dwICsgdGhlbWVfbGlnaHQoKSArIGdndGl0bGUoInRoZW1lX2xpZ2h0IikNCmdncF9kYXJrIDwtIGdncCArIHRoZW1lX2RhcmsoKSArIGdndGl0bGUoInRoZW1lX2RhcmsiKQ0KbGlicmFyeShwYXRjaHdvcmspDQooZ2dwX2dyYXkgfCBnZ3BfYncpIC8gKGdncF9jbGFzc2ljIHwgZ2dwX21pbmltYWwpIC8gKGdncF9saWdodCB8IGdncF9kYXJrKQ0KDQpgYGANCg0KVGhlc2UgdGhlbWVzIGNhbiBiZSBjdXN0b21pemVkIGZ1cnRoZXIgYnkgY2hhbmdpbmcgdGhlIGFwcHJvcHJpYXRlIGFzcGVjdHMgb2YgdGhlIHBsb3RzLiBXZSBsZWF2ZSB0aGUgdXNlciB0byB1c2Ugd2ViIHNlYXJjaGVzIGZvciB0aGUgZGVzaXJlZCBjdXN0b21pemF0aW9ucy4NCg0KIyMgT3RoZXIgY29tcG9uZW50cyBvZiBhIGBnZ3Bsb3RgIA0KDQojIyMgRmFjZXR0aW5nIChWaWRlbzogW1lvdVR1YmVdKGh0dHBzOi8veW91dHUuYmUvNk5HckVKN3BoVEEpLCBbUGFub3B0b10oaHR0cHM6Ly91Y2RlbnZlci5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9WaWV3ZXIuYXNweD9pZD04YjA2YjI0My0wYmRlLTQwMGQtYjhkMi1hZjBmMDE1MWM2YTkpKQ0KDQoqRmFjZXR0aW5nKiBhIGRhdGEgc2V0IGNyZWF0ZXMgc2VwYXJhdGUgcGxvdHRpbmcgcGFuZWxzIGZvciBlYWNoIGxldmVsIG9mIG9uZSBvciBtb3JlIGRpc2NyZXRlIHZhcmlhYmxlcy4gRmFjZXR0aW5nIGlzIHVzZWZ1bCBmb3IgZXhhbWluaW5nIHBhdHRlcm5zIGZvciBjb21iaW5hdGlvbnMgb2YgbGV2ZWxzIGZvciBvbmUgb3IgbW9yZSBkaXNjcmV0ZSB2YXJpYWJsZXMuIFRlY2huaWNhbGx5LCBmYWNldHRpbmcgY2FuIGJlIHBlcmZvcm1lZCBmb3IgYG51bWVyaWNgIHZhcmlhYmxlcyB3aXRoIGEgcmVsYXRpdmVseSBzbWFsbCBudW1iZXIgb2YgdW5pcXVlIHZhbHVlcywgYnV0IGZvciBwcmFjdGljYWwgcmVhc29ucywgZmFjZXR0aW5nIGlzIG1vc3QgYXBwcm9wcmlhdGUgZm9yIGBmYWN0b3JgIHZhcmlhYmxlcy4gDQoNClRoZXJlIGFyZSB0d28gZnVuY3Rpb25zIGZvciBjcmVhdGluZyBmYWNldHRlZCBncmFwaGljcyBpbiAqKmdncGxvdDIqKjoNCg0KKiBgZmFjZXRfd3JhcGAgZmFjZXRzIHRoZSBwbG90cyBiYXNlZCBvbiBhIHNpbmdsZSBgZmFjdG9yYCB2YXJpYWJsZS4gVGhlIHBhbmVscyBhcmUgd3JhcHBlZCBhcm91bmQgdGhlIHBsb3R0aW5nIHdpbmRvdy4NCiogYGZhY2V0X2dyaWRgIGZvcm1zIGEgbWF0cml4IG9mIHBhbmVscyBiYXNlZCBvbiByb3cgYW5kIGNvbHVtbiBmYWNldHRpbmcgdmFyaWFibGVzLg0KDQpJbiB0aGUgcGxvdCBiZWxvdywgd2UgZmFjZXQgc2NhdHRlciBwbG90cyBvZiBgYmlsbF9sZW5ndGhfbW1gIHZlcnN1cyBgYm9keV9tYXNzX2dgIGJ5IGBzcGVjaWVzYC4NCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9wb2ludChhZXMoeCA9IGJvZHlfbWFzc19nLCB5ID0gYmlsbF9sZW5ndGhfbW0pKSArIGZhY2V0X3dyYXAofnNwZWNpZXMpDQpgYGANCg0KQnkgZGVmYXVsdCwgdGhlIHNhbWUgeCBhbmQgeSBheGVzIGFyZSB1c2VkIGluIGFsbCBwYW5lbHMuIFRoaXMgaXMgc3RhbmRhcmQgYmVjYXVzZSBpdCBmYWNpbGl0YXRlcyBkaXJlY3QgY29tcGFyaXNvbnMgYWNyb3NzIHBhbmVscy4gVGhpcyBjYW4gYmUgY3VzdG9taXplZCB1c2luZyB0aGUgYHNjYWxlc2AgYXJndW1lbnQgb2YgdGhlIGBmYWNldF8qYCBmdW5jdGlvbnMuIFRoZSBhbGxvd2FibGUgYHNjYWxlc2AgYXJndW1lbnQgdmFsdWVzIGFyZToNCg0KKiBgZml4ZWRgOiBzYW1lIHggYW5kIHkgYXhlcyBmb3IgYWxsIHBhbmVscy4gVGhlIGRlZmF1bHQuDQoqIGBmcmVlYDogaW5kaXZpZHVhbCB4IGFuZCB5IGF4ZXMgZm9yIGVhY2ggcGFuZWwNCiogYGZyZWVfeGA6IGluZGl2aWR1YWwgeCBheGVzIGZvciBlYWNoIHBhbmVsLCBjb21tb24geSBheGlzDQoqIGBmcmVlX3lgOiBpbmRpdmlkdWFsIHkgYXhlcyBmb3IgZWFjaCBwYW5lbCwgY29tbW9uIHggYXhpcw0KDQpXZSBub3cgY29uc2lkZXIgdXNlIHRoZSBgZmFjZXRfZ3JpZGAgZnVuY3Rpb24gdG8gZmFjZXQgYnkgdHdvIGBmYWN0b3JgIHZhcmlhYmxlcywgYHNwZWNpZXNgIGFuZCBgc2V4YC4gUHJpb3IgdG8gcGxvdHRpbmcsIHdlIHJlbW92ZSB0aGUgb2JzZXJ2YXRpb25zIHdpdGggYE5BYCBmb3IgdGhlIGBzZXhgIHZhcmlhYmxlLg0KDQpgYGB7cn0NCmdncGxvdChzdWJzZXQocGVuZ3VpbnMsICFpcy5uYShzZXgpKSkgKyBnZW9tX3BvaW50KGFlcyh4ID0gYm9keV9tYXNzX2csIHkgPSBiaWxsX2xlbmd0aF9tbSkpICsgZmFjZXRfZ3JpZChzcGVjaWVzIH4gc2V4KQ0KYGBgDQoNClRoZXJlIGFwcGVhcnMgdG8gYmUgZGlmZmVyZW5jZXMgaW4gdGhlIHNpemVzIG9mIHRoZSBtYWxlIGFuZCBmZW1hbGUgcGVuZ3VpbnMsIGJpdCBpdCBpcyBkaWZmaWN1bHQgdG8gY29tcGFyZSB0aGUgcGF0dGVybnMgYWNyb3NzIHBhbmVscy4gSXQgbWF5IGJlIG1vcmUgdXNlZnVsIHRvIGZhY2V0IGJ5IGBzcGVjaWVzYCBidXQgZGlzdGluZ3Vpc2ggYnkgYHNleGAgaW4gYSBzaW5nbGUgcGFuZWwuDQoNCmBgYHtyfQ0KZ2dwbG90KHN1YnNldChwZW5ndWlucywgIWlzLm5hKHNleCkpKSArIGdlb21fcG9pbnQoYWVzKHggPSBib2R5X21hc3NfZywgeSA9IGJpbGxfbGVuZ3RoX21tLCBjb2xvciA9IHNleCwgc2hhcGUgPSBzZXgpKSArIGZhY2V0X3dyYXAofiBzcGVjaWVzKQ0KYGBgDQoNCkl0IGlzIGNsZWFyIG5vdyB0aGF0IGFjcm9zcyBzcGVjaWVzLCB0aGUgbWFsZXMgdGVuZCB0byBoYXZlIGdyZWF0ZXIgYm9keSBtYXNzIGFuZCBiaWxsIGxlbmd0aCBpbiBjb21wYXJpc29uIHRvIHRoZSBmZW1hbGVzLg0KDQojIyMgU3RhdGlzdGljYWwgdHJhbnNmb3JtYXRpb25zIChWaWRlbzogW1lvdVR1YmVdKGh0dHBzOi8veW91dHUuYmUvaWE3Q1pFQmdvS2MpLCBbUGFub3B0b10oaHR0cHM6Ly91Y2RlbnZlci5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9WaWV3ZXIuYXNweD9pZD1kODQyMzQxNC1jYjIzLTRiZjQtOTg3NS1hZjBmMDE1MWM2NmYpKQ0KDQoqU3RhdGlzdGljYWwgdHJhbnNmb3JtYXRpb25zKiBhcmUgdGhlIHByb2Nlc3MgdGhhdCAqKmdncGxvdDIqKiB1c2VzIHRvIHN1bW1hcml6ZSB0aGUgZGF0YSBiZWZvcmUgcGxvdHRpbmcuIEJ5IGRlZmF1bHQsIGV2ZXJ5IGdlb20gdXNlcyBhIGRlZmF1bHQgYHN0YXRgIGFyZ3VtZW50IHRoYXQgc3VtbWFyaXplcyB0aGUgZGF0YSBiZWZvcmUgcGxvdHRpbmcuIFBlcmhhcHMgc3VycHJpc2luZ2x5LCBldmVyeSBgc3RhdF8qYCBoYXMgYSBkZWZhdWx0IGBnZW9tYCBhcmd1bWVudCB0byBzcGVjaWZ5IGhvdyB0aGUgc3VtbWFyaXplZCBkYXRhIHNob3VsZCBiZSBwbG90dGVkLg0KDQpJbiBnZW5lcmFsLCB5b3UgY2FuIHNpbXBseSBsZWFybiB0byB1c2UgdGhlIGdlb20gdGhhdCBzdW1tYXJpemVzIGFuZCBwbG90cyB0aGUgZGF0YSB0byB5b3VyIGxpa2luZy4gRGVwZW5kaW5nIG9uIHRoZSBkYXRhIHlvdSBoYXZlLCBzb21ldGltZXMgeW91IG1heSB3YW50IHRvIHNwZWNpZnkgYSBkaWZmZXJlbnQgYHN0YXRgIGFyZ3VtZW50LiBJbiBnZW5lcmFsLCBjdXN0b20gYHN0YXRgIGFyZ3VtZW50cyBhcmUgYmV5b25kIHRoZSBzY29wZSBvZiB0aGlzIHR1dG9yaWFsLg0KDQpIb3dldmVyLCB3ZSBkbyBjb25zaWRlciBhIHNpbXBsZSBleGFtcGxlIGludm9sdmluZyBiYXIgY2hhcnRzLg0KDQpCeSBkZWZhdWx0LCBgZ2VvbV9iYXJgIHVzZXMgYHN0YXRfY291bnRgIHRvIGNvdW50IHRoZSBudW1iZXIgb2YgdmFsdWVzIGhhdmluZyBlYWNoIGxldmVsIG9mIGEgYGZhY3RvcmAgdmFyaWFibGUgYW5kIHRoZW4gcGxvdHMgdGhlIGhlaWdodCBvZiBlYWNoIGJhciBhYm92ZSB0aGUgbmFtZXMgb2YgdGhlIGBmYWN0b3JgIGxldmVscy4NCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9iYXIoYWVzKHggPSBzZXgpKQ0KYGBgDQoNCkhvd2V2ZXIsIGxldCdzIHN1cHBvc2Ugb3VyIGRhdGEgZnJhbWUgYWxyZWFkeSBoYXMgY291bnRzIGZvciB0aGUgbnVtYmVyIG9mIHZhbHVlcyBpbiBlYWNoIGxldmVsLiBJbiB0aGUgY2FzZSwgd2UgbXVzdCBjaGFuZ2UgdGhlIGBzdGF0YCBhcmd1bWVudCBmb3IgYGdlb21fYmFyYCB0byBgImlkZW50aXR5ImAgKGZvciBgc3RhdF9pZGVudGl0eWApLiBXZSBkbyB0aGlzIGluIHRoZSBleGFtcGxlIGJlbG93LCB3aGVyZSB3ZSBhbHNvIGhhdmUgdG8gY2xlYXJseSBpbmRpY2F0ZSB0aGUgYHhgIHZhcmlhYmxlIGFuZCB0aGUgYHlgIHZhcmlhYmxlIGluIHRoZSBwbG90Lg0KDQpgYGB7cn0NCiMgY291bnQgdmFsdWVzIHdpdGggZWFjaCBsZXZlbA0KcGVuZ3VpbnNfY291bnQgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShwZW5ndWlucyRzZXgsIHVzZU5BID0gImlmYW55IikpDQojIHJlbmFtZSB2YXJpYWxlcw0KbmFtZXMocGVuZ3VpbnNfY291bnQpIDwtIGMoInNleCIsICJjb3VudCIpDQojIHByaW50IGRhdGEgZnJhbWUNCnBlbmd1aW5zX2NvdW50DQojIGNyZWF0ZSBwbG90DQpnZ3Bsb3QocGVuZ3VpbnNfY291bnQpICsgZ2VvbV9iYXIoYWVzKHggPSBzZXgsIHkgPSBjb3VudCksIHN0YXQgPSAiaWRlbnRpdHkiKQ0KYGBgDQoNCiMjIyBQb3NpdGlvbiANCg0KVGhlIGBwb3NpdGlvbmAgYXJndW1lbnQgb2YgYSBnZW9tIGRlc2NyaWJlcyB0aGUgcG9zaXRpb25zIG9mIHRoZSBvYmplY3RzIHByb2R1Y2VkLiBNb3N0IG9mIHRoZSB0aW1lIHRoaXMgaXMgcHJldHR5IHN0cmFpZ2h0Zm9yd2FyZCwgYnV0IHRoZXJlIGNhbiBiZSB0aW1lcyB3aGVuIGl0IGlzIGhlbHBmdWwgKG9yIHVuaGVscGZ1bCkgdG8gY3VzdG9taXplIGBwb3NpdGlvbmAuDQoNCiMjIyMgSml0dGVyaW5nIGRhdGEgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9SdjNvTlFRSUtJdyksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPWZiOWYxZWI0LWE4NmEtNDZiMC1iOGM1LWFmMGYwMTUxYzYzYykpDQpTb21ldGltZXMsIGRhdGEgdmFsdWVzL2luZm9ybWF0aW9uIG92ZXJsYXAsIHdoaWNoIG1ha2VzIGludGVycHJldGluZyB0aGUgcGxvdCBtb3JlIGRpZmZpY3VsdC4gSW4gdGhhdCBjYXNlLCBpdCBjYW4gYmUgdXNlZnVsIHRvICJqaXR0ZXIiIHRoZSBkYXRhIHNvIHRoYXQgdGhlIHZhbHVlcy9pbmZvcm1hdGlvbiBhcmUgbm90IG92ZXJsYXBwaW5nLiBUaGUgZGF0YSBhcmUgbm90IHRlY2huaWNhbGx5IGFzIGFjY3VyYXRlLCBidXQgdGhlIGxhcmdlLXNjYWxlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF0YSBpcyBlYXNpZXIgdG8gaW50ZXJwcmV0Lg0KDQpUaGUgYEdhbHRvbmAgZGF0YSBzZXQgaW4gdGhlICoqSGlzdERhdGEqKiBwYWNrYWdlIFtAUi1IaXN0RGF0YV0gaW5jbHVkZXMgOTI4IG9ic2VydmF0aW9ucyBvZiAyIHZhcmlhbGVzLiBUaGUgdmFyaWFibGVzIGFyZToNCg0KKiBgcGFyZW50YDogYSBgbnVtZXJpY2AgdmFyaWFibGUgaW5kaWNhdGluZyB0aGUgYXZlcmFnZSBoZWlnaHQgb2YgYSBjaGlsZCdzIG1vdGhlciBhbmQgZmF0aGVyIChpbikuDQoqIGBjaGlsZGA6IGEgYG51bWVyaWNgIHZhcmlhYmxlIGluZGljYXRpbmcgdGhlIGhlaWdodCBvZiBlYWNoIGNoaWxkIChpbikuDQoNClRoZSBkYXRhIGFyZSByb3VuZGVkIHRvIHRoZSBuZWFyZXN0IDAuMSBpbmNoLiBBZGRpdGlvbmFsIGluZm9ybWF0aW9uIGNhbiBiZSBmb3VuZCBieSBydW5uaW5nIGA/SGlzdERhdGE6OkdhbHRvbmAgaW4gdGhlIENvbnNvbGUuIA0KDQpVbmZvcnR1bmF0ZWx5LCB0aGUgcm91bmRpbmcgdGhlIGRhdGEgdmFsdWVzIGxlYWRzIHRvIG92ZXJwbG90dGluZy4gQmVsb3csIHdlIGxvYWQgdGhlIGBHYWx0b25gIGRhdGEgYW5kIGNyZWF0ZSBhIHNjYXR0ZXIgcGxvdCBvZiBjaGlsZCBoZWlnaHQgdmVyc3VzIHBhcmVudHMnIGF2ZXJhZ2UgaGVpZ2h0Lg0KDQpgYGB7cn0NCmRhdGEoR2FsdG9uLCBwYWNrYWdlID0gIkhpc3REYXRhIikNCmdncGxvdChHYWx0b24pICsgZ2VvbV9wb2ludChhZXMoeCA9IHBhcmVudCwgeSA9IGNoaWxkKSkNCmBgYA0KDQpUaGVyZSBpcyBhIGxvdCBvZiBvdmVycGxvdHRpbmcgaW4gdGhpcyBncmFwaGljLiBXZSB1c2UgdGhlIGBwb3NpdGlvbmAgYXJndW1lbnQgdG8gYGppdHRlcmAgdGhlIGRhdGEgdG8gbWFrZSB0aGUgb3ZlcmFsbCBkaXN0cmlidXRpb24gY2xlYXJlci4gVGhlcmUncyBub3QgYSBsb3Qgb2YgaW5mb3JtYXRpb24gZ2FpbiBmcm9tIGppdHRlcmluZyB0aGUgZGF0YSBpbiB0aGlzIHBhcnRpY3VsYXIgZXhhbXBsZSwgYnV0IHdlIGNhbiBzZWUgdGhhdCB0aGVyZSBhcmUgbW9yZSBvYnNlcnZhdGlvbnMgaW4gdGhlIGNlbnRyYWwgcGFydCBvZiB0aGUgcGxvdC4NCg0KYGBge3J9DQpnZ3Bsb3QoR2FsdG9uKSArIGdlb21fcG9pbnQoYWVzKHggPSBwYXJlbnQsIHkgPSBjaGlsZCksIHBvc2l0aW9uID0gImppdHRlciIpDQpgYGANCg0KIyMjIyBTdGFja2luZywgZmlsbGluZywgYW5kIGRvZGdpbmcgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9xMXRUUnVSZXE3dyksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPTYyMzhjOTlmLTUyNWQtNDU4OS04NTNkLWFmMGYwMTUxYzZkYykpDQoNCkkgd2FzIHRlbXB0ZWQgdG8gdGl0bGUgdGhpcyBzZWN0aW9uLCAicGxvdHMgdGhhdCBtYWtlIG1lIGVtb3Rpb25hbCIgYmVjYXVzZSBzZXZlcmFsIG9mIHRoZSBncmFwaGljcyBiZWxvdyBjYXVzZSBtZSB0byBnbyB0aHJvdWdoIGEgcmFuZ2Ugb2YgbW9zdGx5IHVucGxlYXNhbnQgZW1vdGlvbnMgd2hlbiBJIHZpZXcgdGhlbS4NCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSdsbCBzZWUgaG93IGBzdGFja2AsIGBmaWxsYCwgYW5kIGBkb2RnZWAgY2FuIGJlIHVzZWQgaW4gdGhlIGBwb3NpdGlvbmAgYXJndW1lbnQuIFdoaWxlIHRoZXNlIGV4YW1wbGVzIGRlbW9uc3RyYXRlIHRoZWlyIHVzZSwgc29tZSBvZiB0aGUgcmVzdWx0cyBhcmUgYW4gYWJzb2x1dGUgbWVzcy4gSSB3aWxsIGNvbmNsdWRlIHdpdGggYWx0ZXJuYXRpdmUgcmVjb21tZW5kYXRpb25zLg0KDQpXaGVuIGNyZWF0aW5nIGEgYmFyIGNoYXJ0LCBhIGBmYWN0b3JgIHZhcmlhYmxlIGlzIG1hcHBlZCB0byB0aGUgYHhgIGFlc3RoZXRpYy4gSWYgYSBkaWZmZXJlbnQgYGZhY3RvcmAgdmFyaWFibGUgaXMgbWFwcGVkIHRvIHRoZSBgZmlsbGAgYWVzdGhldGljLCB0aGVuICoqZ2dwbG90MioqIHdpbGwgYHN0YWNrYCB0aGUgYmFycyBmb3IgdGhlIGBmaWxsYCB2YXJpYWJsZSBpbnNpZGUgdGhlIGJhcnMgZm9yIHRoZSBgeGAgdmFyaWFibGUuIENvbnNpZGVyIHRoZSBiYXIgY2hhcnQgYmVsb3cgaW4gd2hpY2ggd2UgInN0YWNrIiBwZW5ndWluIGBzZXhgIHdpdGhpbiB0aGUgYmFycyBmb3IgdGhlIGRpZmZlcmVudCBzcGVjaWVzLg0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyAgZ2VvbV9iYXIoYWVzKHggPSBzcGVjaWVzLCBmaWxsID0gc2V4KSkNCmBgYA0KDQpJZiB5b3UgY29tcGFyZSB0aGlzIGJhciBjaGFydCB3aXRoIHRoZSBvcmlnaW5hbCBvbmUgdGhhdCBkb2Vzbid0IGZpbGwgYnkgYHNleGAsIHRoZSBiYXIgaGVpZ2h0IGlzIGlkZW50aWNhbC4gSG93ZXZlciwgd2l0aGluIGVhY2ggc3BlY2llcywgd2UgYGZpbGxgIHRoZSBiYXJzIHRvIHNob3cgdGhlIG51bWJlciBvZiBwZW5ndWlucyB3aXRoIGVhY2ggbGV2ZWwgb2YgYHNleGAuDQoNClRoaXMgcGxvdCBpcyBkaWZmaWN1bHQgdG8gaW50ZXJwcmV0LiBJdCB0YWtlcyBhIGxvdCBvZiBtZW50YWwgZW5lcmd5IHRvIGNvbXB1dGUgYXBwcm94aW1hdGVseSBob3cgbWFueSBtYWxlLCBmZW1hbGUsIGFuZCBgTkFgIG9ic2VydmF0aW9ucyB0aGVyZSBhcmUgd2l0aGluIGVhY2ggc3BlY2llcy4gV2hpbGUgdGhlIHVwZGF0ZWQgYmFyIGNoYXJ0IGNvbXBhY3RseSBwcm92aWRlcyBhIGxvdCBvZiBpbmZvcm1hdGlvbiwgaXQgZG9lc24ndCBmYWNpbGl0YXRlIGVhc3kgaW50ZXJwcmV0YXRpb24uDQoNCklmIHdlIHdhbnRlZCB0byBjb21wYXJlIHRoZSBwcm9wb3J0aW9uIG9mIG1hbGUsIGZlbWFsZSwgYW5kIGBOQWAgcGVuZ3VpbnMgaW4gZWFjaCBzcGVjaWVzLCB3ZSBjYW4gY2hhbmdlIHRoZSBgcG9zaXRpb25gIGFyZ3VtZW50IHRvIGBmaWxsYC4gSW4gdGhhdCBjYXNlLCB0aGUgYmFycyB3aXRoaW4gZWFjaCBsZXZlbCBvZiBgc3BlY2llc2Agd2lsbCBiZSBzY2FsZWQgdG8gb25lLCBkaXJlY3RseSBmYWNpbGl0YXRpbmcgY29tcGFyaXNvbnMgYmV0d2VlbiB0aGUgY29sb3JzIHdpdGhpbiBlYWNoIGJhci4NCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgIGdlb21fYmFyKGFlcyh4ID0gc3BlY2llcywgZmlsbCA9IHNleCksIHBvc2l0aW9uID0gImZpbGwiKQ0KYGBgDQoNCkkgc3RpbGwgY29uc2lkZXIgdGhpcyBhIHBvb3IgZGlzcGxheSBvZiB0aGUgZGF0YSwgYnV0IHdlIGNhbiBzZWUgdGhhdCB0aGUgcHJvcG9ydGlvbiBvZiBtYWxlcyBpcyBjbG9zZSB0byA1MCUgZm9yIGFsbCBzcGVjaWVzLg0KDQpMYXN0bHksIGlmIHdlIGRvbid0IHdhbnQgdG8gYHN0YWNrYCBvciBgZmlsbGAgdGhlIGJhcnMsIHdlIGNhbiBoYXZlIHRoZW0gYGRvZGdlYCBlYWNoIG90aGVyLiBJbiB0aGF0IGNhc2UsIHRoZSBvdmVybGFwcGluZyBiYXJzIHdpbGwgImRvZGdlIiBlYWNoIG90aGVyIGFuZCBzaXQgbmV4dCB0byBlYWNoIG90aGVyIGluIHRoZSBwbG90Lg0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyAgZ2VvbV9iYXIoYWVzKHggPSBzcGVjaWVzLCBmaWxsID0gc2V4KSwgcG9zaXRpb24gPSAiZG9kZ2UiKQ0KYGBgDQoNCklmIHlvdSB3YW50IHRvIGNvbXBhcmUgdGhlIG51bWJlciBvZiBwZW5ndWlucyB3aXRoIGVhY2ggYHNleGAgd2l0aGluIGVhY2ggYHNwZWNpZXNgLCB0aGlzIGdyYXBoaWMgaXNuJ3QgdG9vIGJhZC4gSG93ZXZlciwgdGhlIGJhcnMgcmVwcmVzZW50aW5nIG1hbGUgcGVuZ3VpbnMgKG9yIGFsdGVybmF0aXZlbHksIGZlbWFsZSBwZW5ndWlucykgYXJlIHNwcmVhZCBvdXQgYW5kIG1vcmUgZGlmZmljdWx0IHRvIGNvbXBhcmUgZGlyZWN0bHkgYmVjYXVzZSBvZiB0aGUgb3RoZXIgYmFycyBiZXR3ZWVuIHRoZW0uDQoNCldoaWxlIHRoZSBhYm92ZSBjaGFydHMgcHJvdmlkZSBhIGZhaXIgYW1vdW50IG9mIGluZm9ybWF0aW9uIGNvbXBhY3RseSwgdG8gZmFjaWxpdGF0ZSBlYXNlIG9mIGludGVycHJldGF0aW9uIHdlIHNob3VsZCBjb25zdHJ1Y3Qgb3VyIGdyYXBoaWNzIHRvIGhpZ2hsaWdodCB0aGUgY2hhcmFjdGVyaXN0aWMgb2YgaW1wb3J0YW5jZS4NCg0KZS5nLiwgSWYgd2Ugd2FudCB0byBjb21wYXJlIHRoZSBjb3VudHMgb2YgZWFjaCBwZW5ndWluIGBzcGVjaWVzYCB3aXRoaW4gZWFjaCBgc2V4YCwgaXQgd291bGQgYmUgYmV0dGVyIHRvIGZhY2V0IHRoZSBkYXRhIGJ5IGBzZXhgIGFuZCBkaXNwbGF5IHRoZSBjb3VudCBvZiBlYWNoIHNwZWNpZXMuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArICBnZW9tX2JhcihhZXMoeCA9IHNwZWNpZXMsIGZpbGwgPSBzcGVjaWVzKSkgKyBmYWNldF93cmFwKH4gc2V4KQ0KYGBgDQoNCkFsdGVybmF0aXZlbHksIGlmIHdlIHdhbnQgdG8gaGlnaGxpZ2h0IHRoZSBjb21wb3NpdGlvbiBvZiBgc2V4YCB3aXRoaW4gZWFjaCBgc3BlY2llc2AsIHRoZW4gSSByZWNvbW1lbmQgY3JlYXRpbmcgYSBiYXIgY2hhcnQgb2YgYHNleGAgd2hpbGUgZmFjZXR0aW5nIGJ5IGBzcGVjaWVzYC4gVGhpcyBwbG90IGlzIHF1aXRlIHNpbWlsYXIgdG8gdGhlICJkb2RnZSIgcGxvdCBhYm92ZSwgYnV0IHByb3ZpZGVzIGZ1cnRoZXIgZGlzdGluY3Rpb24gYmV0d2VlbiB0aGUgY2hhcnRzIGZvciBlYWNoIHNwZWNpZXMuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArICBnZW9tX2JhcihhZXMoeCA9IHNleCwgZmlsbCA9IHNleCkpICsgZmFjZXRfd3JhcCh+IHNwZWNpZXMpDQpgYGANCg0KVGhlIGV4YW1wbGVzIGFib3ZlIGFyZSBzaG93biBmb3IgZmFjdG9yIHZhcmlhYmxlcyB3aXRoIG9ubHkgdGhyZWUgbGV2ZWxzIG9mIGVhY2ggZmFjdG9yLCBhbmQgeW91IG1heSB0aGluayBJJ20gb3ZlcnJlYWN0aW5nIChhbmQgcGVyaGFwcyBJIGFtKS4gSG93ZXZlciwgdGhlIGlzc3VlcyBtZW50aW9uZWQgYWJvdmUgYXJlIG1hZ25pZmllZCB3aGVuIG91ciBgZmFjdG9yYCB2YXJpYWJsZXMgaGF2ZSBtYW55IGxldmVscy4gVG8gaWxsdXN0cmF0ZSB0aGlzIHBvaW50LCBJIHByZXNlbnQgInN0YWNrIiBhbmQgImRvZGdlIiBiYXIgY2hhcnRzIG9mIHRoZSBgZGlhbW9uZHNgIGRhdGEgaW4gdGhlICoqZ2dwbG90MioqIHBhY2thZ2UsIGluIHdoaWNoIHdlIGNvbXBhcmUgdGhlIGBjdXRgIGFuZCBgY2xhcml0eWAgb2YgZGlhbW9uZHMuIEVhY2ggdmFyaWFibGUgaGFzIGF0IGxlYXN0IDUgbGV2ZWxzLiBUaGUgbGFyZ2UgbnVtYmVyIG9mIGxldmVscyBmb3IgdGhlIGBjdXRgIGFuZCBgY2xhcml0eWAgdmFyaWFibGVzIG1ha2UgaXQgZGlmZmljdWx0IHRvIHF1aWNrbHkgaW50ZXJwcmV0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdHdvIHZhcmlhYmxlcy4gVGhlICoqcGF0Y2h3b3JrKiogcGFja2FnZSBpcyB1c2VkIHRvIGNvbWJpbmUgdGhlIHR3byBjaGFydHMgaW50byBhIHNpbmdsZSBncmFwaGljLiAoV2UgYWxzbyByb3RhdGUgdGhlIHgtYXhpcyBsYWJlbHMgOTAgZGVncmVlcyBzbyB0aGV5IGRvbid0IG92ZXJsYXAgZWFjaCBvdGhlcikuIEhvdyBlYXN5IGlzIGl0IGZvciB5b3UgdG8gaW50ZXJwcmV0IHRoZSBpbmZvcm1hdGlvbiBiZWxvdz8NCg0KYGBge3J9DQpnZ3Bfc3RhY2sgPC0gZ2dwbG90KGRpYW1vbmRzKSArIGdlb21fYmFyKGFlcyh4ID0gY3V0LCBmaWxsID0gY2xhcml0eSkpICsgZ2d0aXRsZSgic3RhY2siKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpDQpnZ3BfZG9kZ2UgPC0gZ2dwbG90KGRpYW1vbmRzKSArIGdlb21fYmFyKGFlcyh4ID0gY3V0LCBmaWxsID0gY2xhcml0eSksIHBvc2l0aW9uID0gImRvZGdlIikgKyBnZ3RpdGxlKCJkb2RnZSIpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkNCmdncF9zdGFjayArIGdncF9kb2RnZQ0KYGBgDQoNCiMjIyBDb29yZGluYXRlIFN5c3RlbXMgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9uYVFQd1p4a2IyTSksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPTZlNzk5OTYzLTUwMGMtNGZlNi05ZmExLWFmMGYwMTUxZDFlZSkpDQoNCkEgY29vcmRpbmF0ZSBzeXN0ZW0gaXMgdXNlZCB0byBkZWZpbmUgdGhlIHBvc2l0aW9uIG9mIGNvb3JkaW5hdGVzIHJlbGF0aXZlIHRvIG9uZSBhbm90aGVyLiBCeSBkZWZhdWx0LCAqKmdncGxvdDIqKiB1c2VzIHRoZSBDYXJ0ZXNpYW4gY29vcmRpbmF0ZSBzeXN0ZW0gKFtodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9DYXJ0ZXNpYW5fY29vcmRpbmF0ZV9zeXN0ZW1dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NhcnRlc2lhbl9jb29yZGluYXRlX3N5c3RlbSkpLCB3aGljaCB0cmVhdHMgY29vcmRpbmF0ZXMgYXMgcG9pbnRzIG9uIGEgZmxhdCBwbGFuZSwgd2hpY2ggaXMgaG93IGh1bWFucyB0ZW5kIHRvIHRoaW5rIGFib3V0IGNvb3JkaW5hdGVzIGluIHJlbGF0aW9uc2hpcCB0byBvbmUgYW5vdGhlci4NCg0KT3RoZXIgY29vcmRpbmF0ZXMgc3lzdGVtcyBhcmUgY29tbW9ubHkgdXNlZCBmb3IgbWFwcyBiZWNhdXNlIHBvaW50cyBhcmUgYWN0dWFsbHkgb24gYW4gZWxsaXBzb2lkYWwgb2JqZWN0ICh0aGUgZWFydGghKSwgd2hpY2ggY29tcGxpY2F0ZXMgdGhlaXIgcmVsYXRpb25zaGlwLg0KDQpXZSBkbyBub3QgZGlzY3VzcyBjb29yZGluYXRlIHN5c3RlbXMgaW4gZGV0YWlsIGhlcmUuIFRoZSBvbmx5IGNvb3JkaW5hdGUgc3lzdGVtIEkgd2FudCB0byBtZW50aW9uZWQgaXMgdGhlICJmbGlwcGVkIiBjb29yZGluYXRlIHN5c3RlbSAoYGNvb3JkX2ZsaXBgKSwgd2hpY2ggZmxpcHMgdGhlIHggYW5kIHkgYXhlcy4gVGhpcyBpcyB1c2VmdWwgZm9yIHJvdGF0aW5nIG9iamVjdHMgaW4gYSBwbG90LiBDb25zaWRlciB0aGUgYm94cGxvdHMgYmVsb3cuDQoNCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9ib3hwbG90KGFlcyh4ID0gc3BlY2llcywgeSA9IGJvZHlfbWFzc19nLCBmaWxsID0gc3BlY2llcykpDQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9ib3hwbG90KGFlcyh4ID0gc3BlY2llcywgeSA9IGJvZHlfbWFzc19nLCBmaWxsID0gc3BlY2llcykpICsgY29vcmRfZmxpcCgpDQpgYGANCg0KIyMgUmVmZXJlbmNlcw0KICANCiAgDQogIA==