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:
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”.
Graphics ecosystems in R (Video: YouTube, Panopto)
There are three main graphics ecosystems in R:
- base
- lattice
- ggplot2
base graphics are traditional S-like graphics.
- Run
?graphics-package
in the Console for more details.
- These are the graphics you get by default when you use the
plot
function, the hist
function, the boxplot
function, etc.
lattice graphics are an implementation of Trellis graphics (Becker, Cleveland, and Shyu 1996) for R.
- Run
?lattice
in the Console for additional information about the lattice package.
- The lattice package focuses on elegantly plotting multivariate data and makes it easy to distinguish different levels of
factor
data.
- lattice and base graphics do not interact with each other (changing aspects of one graphics system has no impact on the other.)
ggplot2 is a layered graphical system based on implementing the Grammar of Graphics (Wilkinson 2005).
- It has gained widespread popularity because of its friendliness for visual exploration of data by data scientists.
- It provides an elegant approach for constructing complex plots in a systematic way.
- Run
?ggplot2-package
in the Console for more information.
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).

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
)
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")

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.
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.
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).

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.
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:
- The data are noisy, which simply means the patterns aren’t as strong and clear as we would like.
- 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).

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 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.
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
.
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).

LS0tDQp0aXRsZTogIkJhc2ljIGRhdGEgdmlzdWFsaXphdGlvbiB3aXRoIGdncGxvdDIiDQphdXRob3I6ICJKb3NodWEgRnJlbmNoIg0KZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJVktJW0tJWQnKWAiDQpvdXRwdXQ6DQogIGJvb2tkb3duOjpodG1sX25vdGVib29rMjoNCiAgICBudW1iZXJfc2VjdGlvbnM6IEZBTFNFDQpiaWJsaW9ncmFwaHk6DQotIGR3di5iaWINCi0gcGFja2FnZXNfYmFzaWNfZGF0YV92aXouYmliDQotLS0NCg0KVGhpcyBkb2N1bWVudCBpcyBpbnRlbmRlZCB0byBoZWxwIHlvdSBxdWlja2x5IGxlYXJuIGhvdyB0byBwZXJmb3JtIGJhc2ljIGRhdGEgdmlzdWFsaXphdGlvbiB1c2luZyAqKmdncGxvdDIqKi4NCg0KQW4gYWNjb21wYW55aW5nIFlvdVR1YmUgcGxheWxpc3QgdGhhdCB3YWxrcyB0aHJvdWdoIHRoaXMgZG9jdW1lbnQgaXMgYXZhaWxhYmxlIGJ5IGZvbGxvd2luZyB0aGUgbGluayBbaGVyZV0oaHR0cHM6Ly95b3V0dWJlLmNvbS9wbGF5bGlzdD9saXN0PVBMa3JKckxzN3hmYlZiSTFXSHM4RmZrT29GdnRpSTZFWGQpLiBJIGhhdmUgYWxzbyBpbmNsdWRlZCBkaXJlY3QsIHJlbGV2YW50IHZpZGVvIGxpbmtzIHRocm91Z2hvdXQgdGhlIGZpbGUgKGUuZy4sIGltbWVkaWF0ZWx5IGFmdGVyIHRoZSB0aGUgcmVsZXZhbnQgaGVhZGluZykuIEFuIGh0bWwgdmVyc2lvbiBvZiB0aGlzIGRvY3VtZW50IGNhbiBiZSBkb3dubG9hZGVkIHRvIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdGlvbiAocnVuIGBnZXR3ZCgpYCBpbiB0aGUgQ29uc29sZSB0byBzZWUgd2hlcmUgdGhpcyBpcykgYnkgcnVubmluZyB0aGUgZm9sbG93aW5nIGNvbW1hbmQ6DQpgYGB7ciwgZXZhbD1GQUxTRX0NCmRvd25sb2FkLmZpbGUoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9qZnJlbmNoL0RhdGFXcmFuZ2xlVml6L21hc3Rlci8wMy1iYXNpYy1kYXRhLXZpei13aXRoLWdncGxvdDIubmIuaHRtbCIsDQogICAgICAgICAgICAgICIwMy1iYXNpYy1kYXRhLXZpei13aXRoLWdncGxvdDIubmIuaHRtbCIpDQpgYGANClRoZSByYXcgUiBNYXJrZG93biBjb2RlIHVzZWQgdG8gZ2VuZXJhdGUgdGhlIGh0bWwgZmlsZSBjYW4gYmUgb2J0YWluZWQgYnkgdG9nZ2xpbmcgdGhlICJDb2RlIiBib3ggaW4gdGhlIHVwcGVyLXJpZ2h0IGNvcm5lciBvZiB0aGUgaHRtbCBmaWxlIGFuZCBzZWxlY3RpbmcgIkRvd25sb2FkIFJtZCIuDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICB0aWR5ID0gVFJVRQ0KKQ0KYGBgDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCiMgYXV0b21hdGljYWxseSBjcmVhdGUgYSBiaWIgZGF0YWJhc2UgZm9yIFIgcGFja2FnZXMNCmtuaXRyOjp3cml0ZV9iaWIoYygNCiAgLnBhY2thZ2VzKCksICdnZ3Bsb3QyJywgJ3BhdGNod29yaycsICdwYWxtZXJwZW5ndWlucycsDQogICdIaXN0RGF0YScsICd2aXJpZGlzJw0KKSwgJ3BhY2thZ2VzX2Jhc2ljX2RhdGFfdml6LmJpYicpDQpgYGANCg0KIyMgSW50cm9kdWN0aW9uIHRvIGJhc2ljIGRhdGEgdmlzdWFsaXphdGlvbiB3aXRoICoqZ2dwbG90MioqIChWaWRlbzogW1lvdVR1YmVdKGh0dHBzOi8veW91dHUuYmUvbHBsZ1Y1S1Q2MnMpLCBbUGFub3B0b10oaHR0cHM6Ly91Y2RlbnZlci5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9WaWV3ZXIuYXNweD9pZD1mNDRhOGE4NC01MGRlLTQxYzYtOGU5ZS1hZjBmMDE0NTQ5NjEpKQ0KDQpJbiB0aGlzIG1vZHVsZSB3ZSBkaXNjdXNzIGhvdyB0byBwZXJmb3JtIGJhc2ljIGRhdGEgdmlzdWFsaXphdGlvbi4NCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuL2ZpZ3VyZXMvZ2dwbG90Ml9sb2dvLnBuZyIpDQpgYGANCg0KV2Ugd2lsbCB1c2UgKipnZ3Bsb3QyKiogW0BnZ3Bsb3QyMjAxNjsgQFItZ2dwbG90Ml0gdG8gY29uc3RydWN0IHRoZSBncmFwaGljcyBpbiB0aGlzIG1vZHVsZS4NCg0KKipnZ3Bsb3QyKiogaGFzIGJlY29tZSBpbmNyZWFzaW5nbHkgcG9wdWxhciBmb3IgcHJvZHVjaW5nIGZsZXhpYmxlIGdyYXBoaWNzIHdpdGhpbiBhbiBlbGVnYW50IGZyYW1ld29yay4NCg0KV2Ugc3RhcnQgYnkgbG9hZGluZyB0aGUgKipnZ3Bsb3QyKiogcGFja2FnZS4NCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpgYGANCg0KYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0NCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQpsaWJyYXJ5KEhpc3REYXRhKQ0KYGBgDQoNCiMjIFRpcHMgZm9yIGdvb2QgZ3JhcGhpY3MgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9jUGZ0TlktaUtDRSksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPWYyMTMwMjIzLTNhNmMtNDY3MS1hYWRkLWFmMGYwMTRjMTFiNikpDQoNCkRhdGEgZ3JhcGhpY3Mgc2hvdWxkIGJlIGFzICppbmZvcm1hdGl2ZSogYXMgcG9zc2libGUuDQoNCi0gR3JhcGhpY3MgZG8gKm5vdCogIG5lZWQgdG8gY29udGFpbiBhcyBtdWNoICppbmZvcm1hdGlvbiogYXMgcG9zc2libGUuDQotIE1ha2UgaXQgYXMgZWFzeSB0byB1bmRlcnN0YW5kIHRoZSBpbmZvcm1hdGlvbiB5b3Ugd2FudCB0byByZXZlYWwgYWJvdXQgdGhlIGRhdGEuDQotIFNpbXBsZXIgZ3JhcGhpY3MgYXJlIGVhc2llciB0byB1bmRlcnN0YW5kIGFuZCBzaG91bGQgYmUgcHJlZmVycmVkLg0KLSBBZGRpbmcgY29tcGxleGl0eSB0byBhIGdyYXBoaWMgY2FuIHNvbWV0aW1lcyByZXZlYWwgZmFyIG1vcmUgYWJvdXQgdGhlIGRhdGEgdGhhbiBhIHNpbXBsZSBncmFwaGljLg0KLSBNYWtlIHN1cmUgbGFiZWxzIGFuZCB0ZXh0IGFyZSBsYXJnZSBlbm91Z2ggdG8gZWFzaWx5IHJlYWQuDQotIE1ha2Ugc3VyZSBjb2xvcnMgYXJlIGRpc3Rpbmd1aXNoYWJsZSBhbmQgY29sb3JibGluZCBmcmllbmRseSAoZS5nLiwgdXNpbmcgdGhlICoqdmlyaWRpcyoqIFtAUi12aXJpZGlzXSBwYWNrYWdlKS4NCg0KWW91IHdpbGwgaGF2ZSB0byBkZWNpZGUgd2hhdCBpcyBuZWVkZWQgaW4gYSBncmFwaGljIHRvIG1ha2UgaXQgYXMgaW5mb3JtYXRpdmUNCmFzIHBvc3NpYmxlLg0KDQpDb21wbGljYXRlZCBncmFwaGljcyBtYXkgcHJlc2VudCBmYXIgbW9yZSBpbmZvcm1hdGlvbiB0aGFuIHdlIGFyZSBhYmxlIHRvIHByb2Nlc3MsIHN1Y2ggYXMgaW4gdGhlIGV4YW1wbGUgYmVsb3cuDQoNCmBgYHtyLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShwYWxtZXJwZW5ndWlucykNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocGxvdGx5KQ0KaGVhdnlfcGxvdCA8LSANCiAgZ2dwbG90KHBlbmd1aW5zKSArDQogIGdlb21fcG9pbnQoYWVzKHggPSBib2R5X21hc3NfZywNCiAgICAgICAgICAgICAgICAgeSA9IGJpbGxfbGVuZ3RoX21tLA0KICAgICAgICAgICAgICAgICBjb2wgPSBzcGVjaWVzLA0KICAgICAgICAgICAgICAgICBzaGFwZSA9IGlzbGFuZCwNCiAgICAgICAgICAgICAgICAgc2l6ZSA9IGZsaXBwZXJfbGVuZ3RoX21tKSkgKw0KICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAuNSwgMi41KSkgKw0KICBnZW9tX3Ntb290aChkYXRhID0gcGVuZ3VpbnMsDQogICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGJvZHlfbWFzc19nLA0KICAgICAgICAgICAgICAgICAgeSA9IGJpbGxfbGVuZ3RoX21tLA0KICAgICAgICAgICAgICAgICAgY29sID0gc3BlY2llcyksDQogICAgICAgICAgICAgIGZvcm11bGEgPSB5IH4geCwNCiAgICAgICAgICAgICAgbWV0aG9kID0gImxvZXNzIikgKyANCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IHBlbmd1aW5zLA0KICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBib2R5X21hc3NfZywNCiAgICAgICAgICAgICAgICAgIHkgPSBiaWxsX2xlbmd0aF9tbSwNCiAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gc2V4KSwNCiAgICAgICAgICAgICAgZm9ybXVsYSA9IHkgfiB4LA0KICAgICAgICAgICAgICBtZXRob2QgPSAibG0iKQ0KZ2dwbG90bHkoaGVhdnlfcGxvdCkNCmBgYA0KDQojIyBHcmFwaGljcyBlY29zeXN0ZW1zIGluIFIgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9SMTJZQWpTemUwSSksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPWUwZGE5NjZlLTk3NTgtNGY4Ni04YzE2LWFmMGYwMTRlMWUxMCkpDQoNClRoZXJlIGFyZSB0aHJlZSBtYWluIGdyYXBoaWNzIGVjb3N5c3RlbXMgaW4gUjoNCg0KMS4gKipiYXNlKioNCjIuICoqbGF0dGljZSoqDQozLiAqKmdncGxvdDIqKg0KDQoqKmJhc2UqKiBncmFwaGljcyBhcmUgdHJhZGl0aW9uYWwgUy1saWtlIGdyYXBoaWNzLg0KDQoqIFJ1biBgP2dyYXBoaWNzLXBhY2thZ2VgIGluIHRoZSBDb25zb2xlIGZvciBtb3JlIGRldGFpbHMuDQoqIFRoZXNlIGFyZSB0aGUgZ3JhcGhpY3MgeW91IGdldCBieSBkZWZhdWx0IHdoZW4geW91IHVzZSB0aGUgYHBsb3RgIGZ1bmN0aW9uLCB0aGUgYGhpc3RgIGZ1bmN0aW9uLCB0aGUgYGJveHBsb3RgIGZ1bmN0aW9uLCBldGMuDQoNCioqbGF0dGljZSoqIGdyYXBoaWNzIGFyZSBhbiBpbXBsZW1lbnRhdGlvbiBvZiBUcmVsbGlzIGdyYXBoaWNzIFtAQmVja2VyQ2xldmVsYW5kU2h5dTE5OTZdIGZvciBSLg0KDQoqIFJ1biBgP2xhdHRpY2VgIGluIHRoZSBDb25zb2xlIGZvciBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGFib3V0IHRoZSAqKmxhdHRpY2UqKiBwYWNrYWdlLg0KKiBUaGUgKipsYXR0aWNlKiogcGFja2FnZSBmb2N1c2VzIG9uIGVsZWdhbnRseSBwbG90dGluZyBtdWx0aXZhcmlhdGUgZGF0YSBhbmQgbWFrZXMgaXQgZWFzeSB0byBkaXN0aW5ndWlzaCBkaWZmZXJlbnQgbGV2ZWxzIG9mIGBmYWN0b3JgIGRhdGEuDQoqICoqbGF0dGljZSoqIGFuZCAqKmJhc2UqKiBncmFwaGljcyBkbyBub3QgaW50ZXJhY3Qgd2l0aCBlYWNoIG90aGVyIChjaGFuZ2luZyBhc3BlY3RzIG9mIG9uZSBncmFwaGljcyBzeXN0ZW0gaGFzIG5vIGltcGFjdCBvbiB0aGUgb3RoZXIuKQ0KDQoqKmdncGxvdDIqKiBpcyBhIGxheWVyZWQgZ3JhcGhpY2FsIHN5c3RlbSBiYXNlZCBvbiBpbXBsZW1lbnRpbmcgdGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MgW0BXaWxraW5zb24yMDA1XS4NCg0KKiBJdCBoYXMgZ2FpbmVkIHdpZGVzcHJlYWQgcG9wdWxhcml0eSBiZWNhdXNlIG9mIGl0cyBmcmllbmRsaW5lc3MgZm9yIHZpc3VhbCBleHBsb3JhdGlvbiBvZiBkYXRhIGJ5IGRhdGEgc2NpZW50aXN0cy4NCiogSXQgcHJvdmlkZXMgYW4gZWxlZ2FudCBhcHByb2FjaCBmb3IgY29uc3RydWN0aW5nIGNvbXBsZXggcGxvdHMgaW4gYSBzeXN0ZW1hdGljIHdheS4NCiogUnVuIGA/Z2dwbG90Mi1wYWNrYWdlYCBpbiB0aGUgQ29uc29sZSBmb3IgbW9yZSBpbmZvcm1hdGlvbi4NCg0KV2UgZGVtb25zdHJhdGUgdXNlIG9mIHRoZSB0aHJlZSBwbG90dGluZyBlY29zeXN0ZW1zIHVzaW5nIHRoZSBgcGVuZ3VpbnNgIGRhdGEgc2V0IGZyb20gdGhlICoqcGFsbWVycGVuZ3VpbnMqKiBwYWNrYWdlIFtAUi1wYWxtZXJwZW5ndWluc10sIHdoaWNoIHdpbGwgYmUgZGlzY3Vzc2VkIGluIG1vcmUgZGV0YWlsIGxhdGVyLiBUaGUgY29kZSBiZWxvdyBjcmVhdGVzIGdyb3VwZWQgc2NhdHRlciBwbG90cyBjb21wYXJpbmcgYmlsbCBsZW5ndGggKG1tKSBhbmQgYm9keSBtYXNzIChnKSBvZiBkaWZmZXJlbnQgcGVuZ3VpbiBzcGVjaWVzIHVzaW5nIGVhY2ggb2YgdGhlIGVjb3N5c3RlbXMuDQoNCmBgYHtyfQ0KIyBsb2FkIGRhdGENCmRhdGEocGVuZ3VpbnMsIHBhY2thZ2UgPSAicGFsbWVycGVuZ3VpbnMiKQ0KIyBiYXNlIGdyYXBoaWNzDQpwbG90KGJpbGxfbGVuZ3RoX21tIH4gYm9keV9tYXNzX2csIGRhdGEgPSBwZW5ndWlucywgY29sID0gcGVuZ3VpbnMkc3BlY2llcykNCmxlZ2VuZCh4ID0gInRvcGxlZnQiLCBsZWdlbmQgPSBsZXZlbHMocGVuZ3VpbnMkc3BlY2llcyksDQogICAgICAgY29sID0gYygiYmxhY2siLCAicmVkIiwgImdyZWVuIiksIHBjaCA9IDEpDQojIGxhdHRpY2UgZ3JhcGhpY3MNCmxhdHRpY2U6Onh5cGxvdChiaWxsX2xlbmd0aF9tbSB+IGJvZHlfbWFzc19nLCBkYXRhID0gcGVuZ3VpbnMsIGdyb3VwID0gc3BlY2llcywNCiAgICAgICAgICAgICAgICBhdXRvLmtleSA9IFRSVUUpDQojIGdncGxvdDIgZ3JhcGhpY3MNCmdncGxvdDI6OmdncGxvdChkYXRhID0gcGVuZ3VpbnMpICsNCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBib2R5X21hc3NfZywgeSA9IGJpbGxfbGVuZ3RoX21tLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBzcGVjaWVzLCBzaGFwZSA9IHNwZWNpZXMpKQ0KYGBgDQoNCiMjIEJhc2ljIGluZ3JlZGllbnRzIChWaWRlbzogW1lvdVR1YmUgXShodHRwczovL3lvdXR1LmJlL2s2YTN2TW9CWFN3KSwgW1Bhbm9wdG9dKGh0dHBzOi8vdWNkZW52ZXIuaG9zdGVkLnBhbm9wdG8uY29tL1Bhbm9wdG8vUGFnZXMvVmlld2VyLmFzcHg/aWQ9NTc0ZGY1YTktMzI5NC00ZDZmLThmOWUtYWYwZjAxNGU4OTI2KSkNCg0KVGhlcmUgYXJlIDQgbWFpbiBjb21wb25lbnRzIG5lZWRlZCB0byBwcm9kdWNlIGEgZ3JhcGhpYyB1c2luZyAqKmdncGxvdDIqKi4NCg0KMS4gQSBkYXRhIGZyYW1lIGNvbnRhaW5pbmcgeW91ciBkYXRhLg0KICAgICogRWFjaCBjb2x1bW4gc2hvdWxkIGJlIGEgdmFyaWFibGUgYW5kIGVhY2ggcm93IHNob3VsZCBiZSBhbiBvYnNlcnZhdGlvbiBvZiBkYXRhLg0KMi4gQSBgZ2dwbG90YCBvYmplY3QuDQogICAgKiBUaGlzIGlzIGluaXRpYWxpemVkIHVzaW5nIHRoZSBgZ2dwbG90YCBmdW5jdGlvbi4NCjMuIEEgZ2VvbWV0cmljIG9iamVjdC4NCiAgICAqIFRoZXNlIGFyZSBjYWxsZWQgImdlb21zIiBmb3Igc2hvcnQuDQogICAgKiBnZW9tcyBpbmRpY2F0ZSB0aGUgZ2VvbWV0cmljIG9iamVjdCB1c2VkIHRvIHZpc3VhbGl6ZSB0aGUgZGF0YS4gRS5nLiwgcG9pbnRzLCBsaW5lcywgcG9seWdvbnMgZXRjLiBNb3JlIGdlbmVyYWxseSwgZ2VvbXMgaW5kaWNhdGUgdGhlIHR5cGUgb2YgcGxvdCB0aGF0IGlzIGRlc2lyZWQsIGUuZy4sIGhpc3RvZ3JhbSwgZGVuc2l0eSwgb3IgYm94cGxvdCwgd2hpY2ggYXJlbid0IGV4YWN0bHkgYSBzaW1wbGUgZ2VvbWV0cmljIGFyZ3VtZW50Lg0KNC4gQW4gYWVzdGhldGljLg0KICAgICogQW4gYWVzdGhldGljIG1hcHBpbmcgaW5kaWNhdGVzIHdoYXQgcm9sZSBhIHZhcmlhYmxlIHBsYXlzIGluIHRoZSBwbG90Lg0KICAgICogZS5nLiwgd2hpY2ggdmFyaWFibGUgd2lsbCBwbGF5IHRoZSAieCIgdmFyaWFibGUgaW4gdGhlIHBsb3QsIHRoZSAieSIgdmFyaWFibGUgaW4gdGhlIHBsb3QsIGNvbnRyb2wgdGhlICJjb2xvciIgb2YgdGhlIG9ic2VydmF0aW9ucywgZXRjLg0KICANCkluICpSIGZvciBEYXRhIFNjaWVuY2UqLCBAcjRkczIwMTcgcHJvdmlkZSB0aGUgZm9sbG93aW5nIHRlbXBsYXRlIGZvciBjcmVhdGluZyBncmFwaGljcyB1c2luZyAqKmdncGxvdDIqKjoNCmBgYHtyLCBldmFsPUZBTFNFLCB0aWR5PUZBTFNFfQ0KZ2dwbG90KGRhdGEgPSA8REFUQT4pICsgPEdFT01fRlVOQ1RJT04+KG1hcHBpbmcgPSBhZXMoPE1BUFBJTkdTPikpDQpgYGANCg0KYDxEQVRBPmAsIGA8R0VPTV9GVU5DVElPTj5gLCBhbmQgYDxNQVBQSU5HUz5gIGFyZSBwbGFjZWhvbGRlcnMgdGhhdCB5b3UgcmVwbGFjZSB3aXRoIHRoZSBkYXRhIGZyYW1lLCBnZW9tZXRyaWMgb2JqZWN0LCBhbmQgYWVzdGhldGljIG1hcHBpbmdzIHlvdSB3YW50IHRvIHVzZSBpbiB5b3VyIHNwZWNpZmljIHBsb3QuDQoNCkFuIGV4cGxhbmF0aW9uIG9mIHRoZSB0ZW1wbGF0ZSBhYm92ZToNCg0KMS4gRXZlcnkgYGdncGxvdGAgc3RhcnRzIHdpdGggYSBjYWxsIHRvIHRoZSBgZ2dwbG90YCBmdW5jdGlvbi4NCiAgICAqIFlvdSBjYW4gY3JlYXRlIGEgYmxhbmsgcGxvdCBieSBydW5uaW5nIGBnZ3Bsb3QoKWAgaW4gdGhlIENvbnNvbGUuDQoyLiBHZW5lcmFsbHksIHlvdSBwYXNzIHlvdXIgZGF0YSBmcmFtZSB0byB0aGUgYGdncGxvdGAgdGhyb3VnaCB0aGUgYGRhdGFgIGFyZ3VtZW50Lg0KICAgICogWW91IGNhbiBhbHNvIHBhc3MgYGRhdGFgIGluc2lkZSB0aGUgYDxHRU9NX0ZVTkNUSU9OPmAgd2hlbiB5b3UgaW50ZW5kIHRvIHVzZSBtdWx0aXBsZSBnZW9tZXRyaWMgb2JqZWN0cyB3aXRoIGRpZmZlcmVudCBkYXRhIHNvdXJjZXMuDQozLiBUaGUgYDxHRU9NX0ZVTkNUSU9OPmAgaW5kaWNhdGVzIHRoZSBnZW9tZXRyaWMgb2JqZWN0IHlvdSB3YW50IHRvIHVzZSBpbiB0aGUgcGxvdC4NCjQuIFRoZSBgPE1BUFBJTkdTPmAgZGVzY3JpYmVzIHRoZSBhZXN0aGV0aWNzIG1hcHBpbmdzIHlvdSB3YW50IHRvIHVzZSBmb3IgdGhpcyBwYXJ0aWN1bGFyIGdlb21ldHJ5Lg0KICAgICogT25lIG9mIHRoZSByZWFzb25zICoqZ2dwbG90MioqIGlzIHNvIHBvd2VyZnVsIGlzIHRoYXQgeW91IGNhbiB1c2UgZGlmZmVyZW50IG1hcHBpbmdzIGZvciBkaWZmZXJlbnQgZ2VvbWV0cmljIG9iamVjdHMuDQo1LiBUaGUgYCtgIHN5bWJvbCBpcyB1c2VkIHRvIGFkZCBhIGxheWVyIHRvIHlvdXIgZ3JhcGhpYy4NCiAgICAqIElmIHlvdXIgY29kZSBzcGFucyBtdWx0aXBsZSBsaW5lcywgdGhlbiB5b3UgbmVlZCB0byBtYWtlIHN1cmUgYWxsIGxpbmVzIGJ1dCB0aGUgbGFzdCBlbmQgd2l0aCB0aGUgYCtgIG9wZXJhdG9yIHRvIHN0YWNrIHRoZSBsYXllcnMgcHJvcGVybHkuDQoNCiMjIyBTb21lIGdlb21ldHJpYyBvYmplY3RzDQoNClRoZXJlIGFyZSBtYW55IGdlb21ldHJpYyBvYmplY3RzIGF2YWlsYWJsZSBpbiAqKmdncGxvdDIqKi4gQSBjb21wbGV0ZSBsaXN0IG1heSBiZSAoY3VycmVudGx5KSBmb3VuZCBhdCBbaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL10oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlLykuIEEgcGFydGlhbCBsaXN0IG9mIGdlb21ldHJpYyBvYmplY3RzIHRoYXQgSSBmcmVxdWVudGx5IHVzZSBpcyBmb3VuZCBiZWxvdyBpbiBUYWJsZSBcQHJlZih0YWI6Z2VvbWV0cnktdGFibGUpLg0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCiMgY3JlYXRlIGdlb21ldHJ5IHRhYmxlDQpkYXRhX2RpbWVuc2lvbmFsaXR5IDwtIHJlcChjKCIxZCIsICIyZCIsICIzZCIsICJOQSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpbWVzID0gYyg2LCA3LCAyLCAxKSkNCmdlb21ldHJ5IDwtIGMoImBnZW9tX2JhcmAiLCAiYGdlb21fZGVuc2l0eWAiLA0KICAgICAgICAgICAgICAiYGdlb21faGlzdG9ncmFtYCIsICJgZ2VvbV9ib3hwbG90YCIsDQogICAgICAgICAgICAgICJgZ2VvbV92aW9saW5gIiwgImBnZW9tX3FxYCIsICJgZ2VvbV9wb2ludGAiLA0KICAgICAgICAgICAgICAiYGdlb21fcGF0aGAsIGBnZW9tX2xpbmVgIiwNCiAgICAgICAgICAgICAgImBnZW9tX3NlZ21lbnRgIiwNCiAgICAgICAgICAgICAgImBnZW9tX2N1cnZlYCIsICJgZ2VvbV9zbW9vdGhgIiwNCiAgICAgICAgICAgICAgImBnZW9tX2RlbnNpdHkyZGAiLCAiYGdlb21fZGVuc2l0eTJkX2ZpbGxlZGAiLA0KICAgICAgICAgICAgICAiYGdlb21fY29udG91cmAiLCAiYGdlb21fY29udG91cl9maWxsZWRgIiwNCiAgICAgICAgICAgICAgImBnZW9tX2FibGluZWAsIGBnZW9tX2hsaW5lYCwgYGdlb21fdmxpbmVgIikNCnB1cnBvc2UgPC0gYygiRHJhd3MgYSBiYXIgY2hhcnQuIiwgIkRyYXdzIGEgZGVuc2l0eSBwbG90LiIsDQogICAgICAgICAgICAgIkRyYXdzIGEgaGlzdG9ncmFtLiIsICJEcmF3cyBhIGJveHBsb3QuIiwgIkRyYXdzIGEgdmlvbGluIHBsb3QuIiwNCiAgICAgICAgICAgICAiRHJhd3MgYSBxdWFudGlsZS1xdWFudGlsZSBwbG90LiIsDQogICAgICAgICAgICAgIkRyYXdzIHBvaW50cy4gVXNlZCBmb3Igc2NhdHRlciBwbG90cy4iLA0KICAgICAgICAgICAgICJDb25uZWN0cyBvYnNlcnZhdGlvbnMuIFVzZWQgZm9yIGxpbmUgcGxvdHMuIiwNCiAgICAgICAgICAgICAiRHJhd3Mgc3RyYWlnaHQgbGluZXMgYmV0d2VlbiBwb2ludHMuIiwNCiAgICAgICAgICAgICAiRHJhd3MgY3VydmVkIGxpbmVzIGJldHdlZW4gcG9pbnRzLiIsDQogICAgICAgICAgICAgIkRyYXdzIGEgJ3Ntb290aCcgZml0dGVkIG1vZGVsIG9mIHRoZSBkYXRhLiIsDQogICAgICAgICAgICAgIkRyYXdzIDJkIGNvbnRvdXJzIG9mIGRlbnNpdHkgZXN0aW1hdGUgZm9yIHR3byB2YXJpYWJsZXMuIiwNCiAgICAgICAgICAgICAiRHJhd3MgMmQgY29udG91cnMgb2YgZGVuc2l0eSBlc3RpbWF0ZSBmb3IgdHdvIHZhcmlhYmxlcyB3aXRoIGNvbG9ycy4iLA0KICAgICAgICAgICAgICJEcmF3cyAyZCBjb250b3VycyBvZiAzZCBkYXRhLiIsDQogICAgICAgICAgICAgIkRyYXdzIDJkIGNvbnRvdXJzIG9mIDNkIGRhdGEgKGNvbG9yZWQpLiIsDQogICAgICAgICAgICAgIkRyYXdzIGRpYWdvbmFsLCBob3Jpem9udGFsLCBhbmQgdmVydGljYWwgbGluZXMuIikNCmdlb21ldHJ5X2RmIDwtIGRhdGEuZnJhbWUoZGF0YV9kaW1lbnNpb25hbGl0eSwgZ2VvbWV0cnksIHB1cnBvc2UpDQpgYGANCmBgYHtyIGdlb21ldHJ5LXRhYmxlLCBlY2hvPUZBTFNFfQ0Ka25pdHI6OmthYmxlKGdlb21ldHJ5X2RmLCBjYXB0aW9uID0gIkNvbW1vbiBnZW9tZXRyaWVzIHByb3ZpZGVkIGJ5ICoqZ2dwbG90MioqLiIsICkNCmBgYA0KDQojIyMgU29tZSBhZXN0aGV0aWMgbWFwcGluZ3MNCg0KQWVzdGhldGljIG1hcHBpbmdzIGFyZSB1bmlxdWUgdG8gZWFjaCBnZW9tZXRyaWMgb2JqZWN0LiBTb21lIG9mIHRoZSBtb3N0IGNvbW1vbiBvbmVzIHRoYXQgc2hvdyB1cCBpbiBtYW55IGdlb21zIGFyZSBwcm92aWRlZCBiZWxvdyBpbiBUYWJsZSBcQHJlZih0YWI6YWVzdGhldGljLXRhYmxlKS4NCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQojIGNyZWF0ZSBhZXN0aGV0aWMgdGFibGUNCmFlc3RoZXRpYyA8LSBjKCJgeGAiLCAiYHlgIiwgImBhbHBoYWAiLCAiYGNvbG9yYCwgYGNvbG91cmAiLA0KICAgICAgICAgICAgICAgImBmaWxsYCIsICJgZ3JvdXBgIiwgImBsaW5ldHlwZWAiLCAiYHNpemVgIiwNCiAgICAgICAgICAgICAgICJgc2hhcGVgIikNCnB1cnBvc2UgPC0gYygiQ29udHJvbHMgdGhlIHgtdmFyaWFibGUgaW4gdGhlIHBsb3QuIiwNCiAgICAgICAgICAgICAiQ29udHJvbHMgdGhlIHktdmFyaWFibGUgaW4gdGhlIHBsb3QuIiwNCiAgICAgICAgICAgICAiQ29udHJvbHMgdGhlIHRyYW5zcGFyZW5jeSBvZiB0aGUgb2JqZWN0LiIsDQogICAgICAgICAgICAgIkNvbnRyb2xzIHRoZSBjb2xvcnMgb2YgdGhlIG9iamVjdC4iLA0KICAgICAgICAgICAgICJDb250cm9scyB0aGUgY29sb3Igb2YgdGhlIGludGVyaW9yIG9mIGFuIG9iamVjdC4iLA0KICAgICAgICAgICAgICJDb250cm9scyBob3cgdGhlIGRhdGEgYXJlIGdyb3VwZWQuIiwNCiAgICAgICAgICAgICAiQ29udHJvbHMgdGhlIHR5cGUgb2YgbGluZSB1c2VkIHRvIGRyYXcgdGhlIG9iamVjdC4iLA0KICAgICAgICAgICAgICJDb250cm9scyB0aGUgc2l6ZSBvZiB0aGUgZHJhd24gb2JqZWN0LiIsDQogICAgICAgICAgICAgIkNvbnRyb2xzIHRoZSBzaGFwZSBvZiB0aGUgb2JqZWN0LiIpDQphZXN0aGV0aWNfZGYgPC0gZGF0YS5mcmFtZShhZXN0aGV0aWMsIHB1cnBvc2UpDQpgYGANCmBgYHtyIGFlc3RoZXRpYy10YWJsZSwgZWNobyA9IEZBTFNFfQ0Ka25pdHI6OmthYmxlKGFlc3RoZXRpY19kZiwgY2FwdGlvbiA9ICJDb21tb24gYWVzdGhldGljcyBwcm92aWRlZCBieSAqKmdncGxvdDIqKi4iLCApDQpgYGANCg0KIyMgU2ltcGxlIGV4YW1wbGVzDQoNCiMjIyBUaGUgYHBlbmd1aW5zYCBkYXRhIChWaWRlbzogW1lvdVR1YmVdKGh0dHBzOi8veW91dHUuYmUvWHBzVjY1dW02VWMpLCBbUGFub3B0b10oaHR0cHM6Ly91Y2RlbnZlci5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9WaWV3ZXIuYXNweD9pZD04YTFiOGQ5MS04ZTkwLTQ4MmItODUxMi1hZjBmMDE0ZTg4ZTkpKQ0KV2Ugd2lsbCB1c2UgdGhlIGBwZW5ndWluc2AgZGF0YSBzZXQgaW4gdGhlICoqcGFsbWVycGVuZ3VpbnMqKiBwYWNrYWdlIFtAUi1wYWxtZXJwZW5ndWluc10gdG8gaWxsdXN0cmF0ZSBzb21lIG9mIHRoZSB3YXlzIHRoYXQgKipnZ3Bsb3QyKiogY2FuIGJlIHVzZWQuIA0KDQpUaGUgYHBlbmd1aW5zYCBkYXRhIHNldCBwcm92aWRlcyBkYXRhIHJlbGF0ZWQgdG8gdmFyaW91cyBwZW5ndWluIHNwZWNpZXMgbWVhc3VyZWQgaW4gdGhlIFBhbG1lciBBcmNoaXBlbGFnbyAoQW50YXJjdGljYSksIG9yaWdpbmFsbHkgcHJvdmlkZWQgYnkgQEdvcm1hbkV0QWwyMDE0LiBXZSBzdGFydCBieSBsb2FkaW5nIHRoZSBkYXRhIGludG8gbWVtb3J5Lg0KDQpgYGB7cn0NCmRhdGEocGVuZ3VpbnMsIHBhY2thZ2UgPSAicGFsbWVycGVuZ3VpbnMiKQ0KYGBgDQoNClRoZSBkYXRhIHNldCBpbmNsdWRlcyBgciBucm93KHBlbmd1aW5zKWAgb2JzZXJ2YXRpb25zIG9mIGByIG5jb2wocGVuZ3VpbnMpYCB2YXJpYWJsZXMuIFRoZSB2YXJpYWJsZXMgYXJlOg0KDQoqIGBzcGVjaWVzYDogYSBgZmFjdG9yYCBpbmRpY2F0aW5nIHRoZSBwZW5ndWluIHNwZWNpZXMNCiogYGlzbGFuZGA6IGEgYGZhY3RvcmAgaW5kaWNhdGluZyB0aGUgaXNsYW5kIHRoZSBwZW5ndWluIHdhcyBvYnNlcnZlZA0KKiBgYmlsbF9sZW5ndGhfbW1gOiBhIGBudW1lcmljYCB2YXJpYWJsZSBpbmRpY2F0aW5nIHRoZSBiaWxsIGxlbmd0aCBpbiBtaWxsaW1ldGVycw0KKiBgYmlsbF9kZXB0aF9tbWA6IGEgYG51bWVyaWNgIHZhcmlhYmxlIGluZGljYXRpbmcgdGhlIGJpbGwgZGVwdGggaW4gbWlsbGltZXRlcnMNCiogYGZsaXBwZXJfbGVuZ3RoX21tYDogYW4gYGludGVnZXJgIHZhcmlhYmxlIGluZGljYXRpbmcgdGhlIGZsaXBwZXIgbGVuZ3RoIGluIG1pbGxpbWV0ZXJzDQoqIGBib2R5X21hc3NfZ2A6IGFuIGBpbnRlZ2VyYCB2YXJpYWJsZSBpbmRpY2F0aW5nIHRoZSBib2R5IG1hc3MgaW4gZ3JhbXMNCiogYHNleGA6IGEgYGZhY3RvcmAgaW5kaWNhdGluZyB0aGUgcGVuZ3VpbiBzZXggKGBmZW1hbGVgLCBgbWFsZWApDQoqIGB5ZWFyYDogYW4gaW50ZWdlciBkZW5vdGluZyB0aGUgc3R1ZHkgeWVhciB0aGUgcGVuZ3VpbiB3YXMgb2JzZXJ2ZWQgKGAyMDA3YCwgYDIwMDhgLCBvciBgMjAwOWApDQoNCiMjIyBCYXIgY2hhcnQgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9jQndDMVNLel9nQSksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPWVmNjRiNTM5LTViMmMtNDYyMS05OWFhLWFmMGYwMTRlODg3MikpDQpXZSBjb25zdHJ1Y3QgYSBiYXIgY2hhcnQgb2YgdGhlIGBzcGVjaWVzYC4gQSBiYXIgY2hhcnQgdXNlcyBiYXJzIHRvIGluZGljYXRlIHRoZSBudW1iZXIgb2YgdmFsdWVzIGVhY2ggbGV2ZWwgb2YgYSBgZmFjdG9yYCBpbmNsdWRlcy4gQWx0ZXJuYXRpdmVseSwgdGhlIGJhcnMgY2FuIGluZGljYXRlIHRoZSByZWxhdGl2ZSBmcmVxdWVuY3kgKGkuZS4sIHByb3BvcnRpb24pIG9mIHZhbHVlcyBoYXZpbmcgZWFjaCBsZXZlbCBvZiB0aGUgYGZhY3RvcmAuIA0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyBnZW9tX2JhcihhZXMoeCA9IHNwZWNpZXMpKQ0KYGBgDQoNClJlbGF0aXZlbHkgc3BlYWtpbmcsIHdlIGNhbiBzZWUgdGhhdCBBZGVsaWUgcGVuZ3VpbnMgd2VyZSBtb3N0IGZyZXF1ZW50bHkgb2JzZXJ2ZWQsIGZvbGxvd2VkIGJ5IEdlbnRvbyBwZW5ndWlucywgYW5kIHRoZW4gQ2hpbnN0cmFwIHBlbmd1aW5zLg0KDQpXZSBjYW4gY29sb3IgdGhlIGJhcnMgYnkgYHNwZWNpZXNgIGZvciB2aXN1YWwgY2xhcml0eSBieSBzcGVjaWZ5aW5nIHRoZSBgZmlsbGAgYWVzdGhldGljLg0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyBnZW9tX2JhcihhZXMoeCA9IHNwZWNpZXMsIGZpbGwgPSBzcGVjaWVzKSkNCmBgYA0KDQpUaGlzIGRvZXNuJ3QgYWRkIGFueSBuZXcgaW5mb3JtYXRpb24gdG8gdGhlIGdyYXBoaWMgYnV0IGRvZXMgbWFrZSBpdCBtb3JlIHZpc3VhbGx5IGFwcGVhbGluZy4gV2UgY291bGQgcmVtb3ZlIHRoZSBsZWdlbmQgdG8gc2ltcGxpZnkgdGhlIGdyYXBoaWMsIGlmIHdlIHdhbnRlZC4NCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9iYXIoYWVzKHggPSBzcGVjaWVzLCBmaWxsID0gc3BlY2llcykpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCiMjIyBIaXN0b2dyYW0gKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9nRlpwQk8zOEhVZyksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPTVmMzIzZGJjLTNkNmMtNDJiZC1hYzYyLWFmMGYwMTRlODhhYSkpDQoNCkEgKmhpc3RvZ3JhbSogaXMgdXNlZCB0byBkaXNwbGF5IHRoZSBkaXN0cmlidXRpb24gb2YgYSBjb250aW51b3VzIGBudW1lcmljYCB2YXJpYWJsZS4gVGhlIHJhbmdlIG9mIHRoZSB2YXJpYWJsZSBpcyBwYXJ0aXRpb25lZCBpbnRvIGNsYXNzZXMuIFRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGZhbGxpbmcgaW4gZWFjaCBjbGFzcyBpcyBjb3VudGVkLiBBIGhpc3RvZ3JhbSBkcmF3cyBhIGJhciBmb3IgZWFjaCBjbGFzcyB3aXRoIGhlaWdodCBjb3JyZXNwb25kaW5nIHRvIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIHRoYXQgY2xhc3MuDQoNCkxldCdzIGNvbnN0cnVjdCBhIGhpc3RvZ3JhbSBvZiBiaWxsIGxlbmd0aC4NCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBiaWxsX2xlbmd0aF9tbSkpDQpgYGANCg0KVGhlIGhpc3RvZ3JhbSBpcyBtdWx0aW1vZGFsIChpLmUuLCBoYXMgbXVsdGlwbGUgcHJvbWluZW50IHBlYWtzKSwgaGF2aW5nIDIgb3IgMyBwcm9taW5lbnQgcGVha3MuIFRoZXJlIG1heSBiZSBzdWItcG9wdWxhdGlvbnMgd2Ugc2hvdWxkIGRpc3Rpbmd1aXNoLiBJdCBpc24ndCBlYXN5IHRvIGlkZW50aWZ5IHN1Yi1wb3B1bGF0aW9ucyB3aXRoIGhpc3RvZ3JhbXMgdW5sZXNzIHRoZSBzdWItcG9wdWxhdGlvbnMgYXJlIHNwbGl0IGludG8gc2VwYXJhdGUgaGlzdG9ncmFtcyAod2hpY2ggd2Ugd2lsbCBsZWFybiB0byBkbyBsYXRlciB3aXRoICJmYWNldHMiKSBvciB0byBtYWtlIHRoZSBiYXJzIHNlbWktdHJhbnNwYXJlbnQgZm9yIHRoZSBkaWZmZXJlbnQgc3ViLXBvcHVsYXRpb25zLg0KDQojIyMgRGVuc2l0eSBwbG90IChWaWRlbzogW1lvdVR1YmVdKGh0dHBzOi8veW91dHUuYmUvVUdlSjczazZCR3MpLCBbUGFub3B0b10oaHR0cHM6Ly91Y2RlbnZlci5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9WaWV3ZXIuYXNweD9pZD0xYjc4MjM0Mi04Zjc5LTQ0OTMtYTYwZC1hZjBmMDE0ZTk0MzgpKQ0KDQpBIGRlbnNpdHkgcGxvdCBpcyBvZnRlbiBwcmVmZXJyZWQgdG8gYSBoaXN0b2dyYW0gYmVjYXVzZSBpdCdzIG1vcmUgZmxleGlibGUsIHRob3VnaCBpdCBvZnRlbiBwcm92aWRlcyBzaW1pbGFyIGluZm9ybWF0aW9uLiBBIGRlbnNpdHkgcGxvdCBpcyBlc3NlbnRpYWxseSBhIHNtb290aGVkIHZlcnNpb24gb2YgYSBoaXN0b2dyYW0uIA0KDQpBIGRlbnNpdHkgcGxvdCBpcyBhcHByb3ByaWF0ZSBmb3IgZGlzcGxheWluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIGEgY29udGludW91cyBgbnVtZXJpY2AgdmFyaWFibGUgYW5kIGluZGljYXRlcyB0aGUgdmFsdWVzIGZvciB3aGljaCB0aGUgZGF0YSBpcyBtb3JlICJkZW5zZSIuIE1vcmUgc3BlY2lmaWNhbGx5LCBkZW5zaXR5IHBsb3RzIGluZGljYXRlIHRoZSB2YWx1ZXMgb2YgZGF0YSB5b3UgYXJlIG1vc3QgbGlrZWx5IHRvIG9ic2VydmUuIEEgaGlnaGVyIGRlbnNpdHkgcmVnaW9uIG1lYW5zIHlvdSBhcmUgbW9yZSBsaWtlbHkgdG8gb2JzZXJ2ZSBkYXRhIHdpdGggdmFsdWVzIGluIHRoYXQgcmVnaW9uLiBUaGVyZSBpcyBhIHN0YXRpc3RpY2FsIGRlZmluaXRpb24gd2Ugd2lsbCBub3QgZ28gaW50by4NCg0KQSBzaW1wbGUgZGVuc2l0eSBwbG90IGlzIHNob3duIGJlbG93LiBXZSBjYW4ndCBsZWFybiBhIGxvdCBiZWNhdXNlIG9mIHRoZSBzdWItcG9wdWxhdGlvbnMgcHJldmlvdXNseSBtZW50aW9uZWQuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArIGdlb21fZGVuc2l0eShhZXMoeCA9IGJpbGxfbGVuZ3RoX21tKSkNCmBgYA0KDQpUaGVyZSBhcmUgYXQgbGVhc3QgdHdvIG1vZGVzIGluIHRoZSBgYmlsbF9sZW5ndGhfbW1gIGRlbnNpdHkgcGxvdC4NCg0KTGV0J3MgY3JlYXRlIHNlcGFyYXRlIGRlbnNpdGllcyBmb3IgdGhlIGRpZmZlcmVudCBwZW5ndWluIHNwZWNpZXMgYnkgc3BlY2lmeWluZyB0aGUgYGNvbG9yYCBhZXN0aGV0aWMgdG8gKnNjYWxlKiBkaWZmZXJlbnQgY29sb3JzIHRvIHRoZSBkaWZmZXJlbnQgbGV2ZWxzIG9mIGBzcGVjaWVzYC4NCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9kZW5zaXR5KGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIGNvbG9yID0gc3BlY2llcykpDQpgYGANCg0KVGhlcmUgYXJlIGNsZWFyIGRpZmZlcmVuY2VzIGluIHRoZSBiaWxsIGxlbmd0aCBvZiBBZGVsaWUsIENoaW5zdHJhcCwgYW5kIEdlbnRvbyBwZW5ndWlucy4gVGhlIGJpbGwgbGVuZ3RoIGZvciB0aGUgQWRlbGllIHBlbmd1aW5zIGlzIGFib3V0IDM1IG1tIGFuZCBjbG9zZXIgdG8gNDcgbW0gZm9yIHRoZSBHZW50b28gcGVuZ3VpbnMuIFRoZSBDaGluc3RyYXAgcGVuZ3VpbnMgaGF2ZSB0aGUgZ3JlYXRlc3QgYmlsbCBsZW5ndGgsIGluIGdlbmVyYWwuIEl0IGFwcGVhcnMgdGhlIENoaW5zdHJhcCBwZW5ndWlucyBtYXkgaGF2ZSBhIGZ1cnRoZXIgc3ViLXBvcHVsYXRpb24uDQoNCkEgd2Vha25lc3Mgb2YgdGhlIHByZXZpb3VzIGdyYXBoaWMgaXMgdGhhdCB0aGUgbGVnZW5kIGRpc3Rpbmd1aXNoaW5nIHRoZSBkaWZmZXJlbnQgYHNwZWNpZXNgIGlzIGEgYml0IHN1YnRsZS4gVGhlIGNvbG9yZWQgbGluZSBhcm91bmQgdGhlIGJvcmRlciBtYXkgbm90IGJlIGVhc3kgdG8gZGlzdGluZ3Vpc2ggZm9yIGV2ZXJ5b25lLiBXZSBjb3VsZCB1c2UgYSBkaWZmZXJlbnQgYGxpbmV0eXBlYCBmb3IgZWFjaCBzcGVjaWVzIG9yIHdlIGNvdWxkIHVzZSBkaWZmZXJlbnQgYGNvbG9yYCBhbmQgYGxpbmV0eXBlYCBmb3IgZWFjaCBzcGVjaWVzLiBXZSBjb25zaWRlciB0aGUgZGlmZmVyZW50IHJlc3VsdHMgYmVsb3cuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArIGdlb21fZGVuc2l0eShhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCBsaW5ldHlwZSA9IHNwZWNpZXMpKQ0KZ2dwbG90KHBlbmd1aW5zKSArIGdlb21fZGVuc2l0eShhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCBjb2wgPSBzcGVjaWVzLCBsaW5ldHlwZSA9IHNwZWNpZXMpKQ0KYGBgDQoNCkFuIGV2ZW4gYmV0dGVyIG9wdGlvbiBpcyB0byBgZmlsbGAgdGhlIGRlbnNpdGllcyBmb3IgZWFjaCBzcGVjaWVzIHdpdGggZGlmZmVyZW50IGNvbG9ycy4gSG93ZXZlciwgdGhlIGRlbnNpdHkgY3VydmVzIHdpbGwgb3ZlcmxhcCwgbWFza2luZyBzb21lIG9mIHRoZSBpbmZvcm1hdGlvbi4NCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9kZW5zaXR5KGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIGZpbGwgPSBzcGVjaWVzKSkNCmBgYA0KDQpUbyBhZGRyZXNzIHRoZSBtYXNraW5nIGlzc3VlLCB3ZSBjYW4gY29udHJvbCB0aGUgdHJhbnNwYXJlbmN5IG9mIHRoZSBjb2xvcnMgdXNpbmcgdGhlIGBhbHBoYWAgYWVzdGhldGljLiBUaGlzIHZhbHVlLCBiZXR3ZWVuIDAgYW5kIDEsIGNvbnRyb2xzIGhvdyB0cmFuc3BhcmVudCB0aGUgb2JqZWN0cyBhcmUuIGAwYCBtZWFucyB0cmFuc3BhcmVudCwgYDFgIG1lYW5zIGNvbXBsZXRlbHkgb3BhcXVlLiBXZSB3aWxsIHNldCBgYWxwaGEgPSAwLjNgLiBOb3RpY2UgdGhhdCB3ZSBzcGVjaWZ5IHRoaXMgT1VUU0lERSB0aGUgYWVzdGhldGljIG1hcHBpbmcgc2luY2Ugd2UgYXJlIGNvbnRyb2xsaW5nIHRoZSBhZXN0aGV0aWMgbWFudWFsbHkgaW5zdGVhZCBvZiBoYXZpbmcgKipnZ3Bsb3QyKiogKnNjYWxlKiB0aGUgcHJvdmlkZWQgdmFyaWFibGUuIA0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyBnZW9tX2RlbnNpdHkoYWVzKHggPSBiaWxsX2xlbmd0aF9tbSwgZmlsbCA9IHNwZWNpZXMpLCBhbHBoYSA9IDAuMykNCmBgYA0KDQojIyMgU2NhbGluZw0KDQpXaGVuIGEgdmFyaWFibGUgaXMgbWFwcGVkIHRvIGFuIGFlc3RoZXRpYywgKipnZ3Bsb3QyKiogd2lsbCBhc3NpZ24gYSB1bmlxdWUgdmFsdWUgdG8gZWFjaCB1bmlxdWUgbGV2ZWwgb2YgdGhlIHZhcmlhYmxlLiBUaGlzIHByb2Nlc3MgaXMga25vd24gYXMgKnNjYWxpbmcqLg0KDQpTY2FsaW5nIGlzIG9mdGVuIG5vdCB2ZXJ5IGV4Y2l0aW5nLCBidXQgaXQgY2FuIGJlIGltcG9ydGFudCB3aGVuIGN1c3RvbWl6aW5nIHRoZSBsb29rIG9mIGEgYGdncGxvdGAuDQoNCldoZW4gYSB2YXJpYWJsZSBpcyBtYXBwZWQgdG8gYSBub24gYHhgIG9yIGB5YCBhZXN0aGV0aWMsICoqZ2dwbG90MioqIHdpbGwgYXV0b21hdGljYWxseSBhZGQgYSBsZWdlbmQgdG8gaW5kaWNhdGUgaG93IHRoZSB2YXJpYWJsZSB3YXMgc2NhbGVkLg0KDQojIyMgQm94cGxvdHMgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9FWmprZ19oc2gtdyksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPTNmNjM2NWNiLTk0ZWItNGVmOS04MjcxLWFmMGYwMTRlOTZhMykpDQoNCkEgYm94cGxvdCBpcyBhIHJvYnVzdCBkaXNwbGF5IG9mIGluZm9ybWF0aW9uIGZvciBhIGNvbnRpbnVvdXMgYG51bWVyaWNgIHZhcmlhYmxlLiBCb3hwbG90cyBhcmUgcm9idXN0IGJlY2F1c2UgdGhleSByZWx5IG9uIHJvYnVzdCBzdGF0aXN0aWNzIG5vdCBlYXNpbHkgYWZmZWN0ZWQgYnkgb3V0bGllcnMuIA0KDQpJbiBnZW5lcmFsLCBhIGJveHBsb3Q6DQoNCiogRHJhd3MgYSBib3ggZXh0ZW5kaW5nIGZyb20gUTEgKHRoZSAwLjI1IHF1YW50aWxlKSB0byBRMyAodGhlIDAuNzUgcXVhbnRpbGUpIG9mIHRoZSBkYXRhLg0KKiBBIGxpbmUgYmV0d2VlbiBRMSBhbmQgUTMgaW5kaWNhdGVzIHRoZSBtZWRpYW4gKHRoZSAwLjUgcXVhbnRpbGUpIG9mIHRoZSBkYXRhLg0KKiBBICJ3aGlza2VyIiBleHRlbmRzIGZyb20gUTEgdG8gdGhlIHNtYWxsZXN0IG9ic2VydmF0aW9uIHRoYXQgaXMgbm90IGFuIG91dGxpZXIuDQoqIEEgIndoaXNrZXIiIGV4dGVuZHMgZnJvbSBRMyB0byB0aGUgbGFyZ2VzdCBvYnNlcnZhdGlvbiB0aGF0IGlzIG5vdCBhbiBvdXRsaWVyLg0KKiBBbiAqKm91dGxpZXIqKiBpcyB0eXBpY2FsbHkgZGVmaW5lZCBhcyBhbiBvYnNlcnZhdGlvbiBzbWFsbGVyIHRoYW4gUTEgLSAxLjUgKFEzIC0gUTEpIG9yIGxhcmdlciB0aGFuIFEzICsgMS41IChRMyAtIFExKS4NCg0KUGFyYWxsZWwgYm94cGxvdHMgYXJlIGFuIGVmZmVjdGl2ZSB0b29sIGZvciBjb21wYXJpbmcgZGlmZmVyZW5jZXMgYmV0d2VlbiBhIHZhcmlhYmxlIGFjcm9zcyBtdWx0aXBsZSBsZXZlbHMgb2YgYSBgZmFjdG9yYCB2YXJpYWJsZS4NCg0KQ29uc2lkZXIgcGFyYWxsZWwgYm94cGxvdHMgb2YgYmlsbCBsZW5ndGggZGlzdGluZ3Vpc2hlZCBieSBgc3BlY2llc2AuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArIGdlb21fYm94cGxvdChhZXMoeSA9IGJpbGxfbGVuZ3RoX21tLCB4ID0gc3BlY2llcykpDQpgYGANCg0KV2UgY2FuIHNlZSB0aGF0IHRoZSB0eXBpY2FsIHJlc3BvbnNlcyBmb3IgdGhlIEFkZWxpZSBwZW5ndWlucyB0ZW5kIHRvIGJlIGxvd2VyIHRoYW4gZm9yIHRoZSBDaGluc3RyYXAgYW5kIEdlbnRvbyBwZW5ndWlucy4gVGhlIENoaW5zdHJhcCBwZW5ndWlucyB0ZW5kIHRvIGhhdmUgc2xpZ2h0bHkgbG9uZ2VyIGJlYWtzIHRoYXQgdGhlIEdlbnRvbyBwZW5ndWlucy4gQWxsIHRocmVlIHBlbmd1aW5zIHNwZWNpZXMgc2VlbSB0byBoYXZlIHNpbWlsYXIgdmFyaWFiaWxpdHkgKGkuZS4sIHRoZSBzcHJlYWQgb2YgdGhlIGRhdGEgaXMgc2ltaWxhcikuDQoNCkEgd2Vha25lc3Mgb2YgYm94cGxvdHMgaXMgdGhhdCB0aGV5IHRocm93IGF3YXkgYSBsb3Qgb2YgaW5mb3JtYXRpb24uIGUuZy4sIFdlIGRvbid0IHNlZSB0aGUgdHdvIHN1Yi1wb3B1bGF0aW9ucyBpbiB0aGUgQ2hpbnN0cmFwIHBlbmd1aW5zIHRoYXQgd2Ugbm90aWNlZCBlYXJsaWVyIHdoZW4gbG9va2luZyBhdCBkZW5zaXR5IHBsb3RzLg0KDQpBZGRpbmcgYSBgZmlsbGAgY29sb3IgdG8gdGhlIGJveHBsb3RzICh3aGlsZSByZW1vdmluZyB0aGUgbGVnZW5kKSBtaWdodCBiZSBhIG1vcmUgdmlzdWFsbHkgYXBwZWFsaW5nIGdyYXBoaWMuDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyBnZW9tX2JveHBsb3QoYWVzKHkgPSBiaWxsX2xlbmd0aF9tbSwgeCA9IHNwZWNpZXMsIGZpbGwgPSBzcGVjaWVzKSkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KIyMjIFZpb2xpbiBwbG90cyAoVmlkZW86IFtZb3VUdWJlXShodHRwczovL3lvdXR1LmJlL1FiVldjanhZZ0hZKSwgW1Bhbm9wdG9dKGh0dHBzOi8vdWNkZW52ZXIuaG9zdGVkLnBhbm9wdG8uY29tL1Bhbm9wdG8vUGFnZXMvVmlld2VyLmFzcHg/aWQ9MjdiNTBmZjYtMmFhYS00OGVmLTg1YWMtYWYwZjAxNGVhODhkKSkNCg0KQSB2aW9saW4gcGxvdCBpcyBsaWtlIGEgY3Jvc3MgYmV0d2VlbiBhIGJveHBsb3QgYW5kIGEgZGVuc2l0eSBwbG90LiBBIHZpb2xpbiBwbG90IHVuaXRlcyBhIGRlbnNpdHkgd2l0aCBpdHMgbWlycm9yIGltYWdlIGFuZCB0aGVuIGRpc3BsYXlzIHRoZSB1bmlmaWVkIG9iamVjdCBsaWtlIGEgYm94cGxvdC4gDQoNCldlIGNvbnN0cnVjdCB2aW9saW4gcGxvdHMgb2YgYmlsbCBsZW5ndGggZm9yIGVhY2ggcGVuZ3VpbiBgc3BlY2llc2AuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArIGdlb21fdmlvbGluKGFlcyh4ID0gc3BlY2llcywgeSA9IGJpbGxfbGVuZ3RoX21tKSkNCmBgYA0KDQpXaGlsZSB0aGUgQWRlbGllIGJpbGwgbGVuZ3RocyBhcmUgYXBwcm94aW1hdGVseSBzeW1tZXRyaWMgYW5kIHVuaW1vZGFsLCBib3RoIHRoZSBDaGluc3RyYXAgYW5kIEdlbnRvbyBiaWxsIGxlbmd0aHMgYXJlIGJpbW9kYWwgYW5kIGFzeW1tZXRyaWMuDQoNCiMjIyBTY2F0dGVyIHBsb3RzIChWaWRlbzogW1lvdVR1YmVdKGh0dHBzOi8veW91dHUuYmUvRGpPUm9VRE1MRDQpLCBbUGFub3B0b10oaHR0cHM6Ly91Y2RlbnZlci5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9WaWV3ZXIuYXNweD9pZD04YjE1ZjNhNC1hYWEzLTRhNzMtYWViZC1hZjBmMDE0ZWFiMGEpKQ0KDQpBIHNjYXR0ZXIgcGxvdCBkcmF3cyB0aGUgbG9jYXRpb25zIG9mICh4LCB5KSBwb3NpdGlvbnMgYmFzZWQgb24gdHdvIGBudW1lcmljYCB2YXJpYWJsZXMuIEluIGdlbmVyYWwsIHdlIHVzZSBzY2F0dGVyIHBsb3RzIHRvIGxlYXJuIGluZm9ybWF0aW9uIGFib3V0IHRoZSAqYXNzb2NpYXRpb24qIGJldHdlZW4gdGhlIHR3byB2YXJpYWJsZXMuDQoNCiogQSBwb3NpdGl2ZSBhc3NvY2lhdGlvbiBpcyBwcmVzZW50IHdoZW4gYm90aCB2YXJpYWJsZXMgdGVuZCB0byBpbmNyZWFzZSB0b2dldGhlci4NCiogQSBuZWdhdGl2ZSBhc3NvY2lhdGlvbiBpcyBwcmVzZW50IHdoZW4gb25lIHZhcmlhYmxlIHRlbmRzIHRvIGRlY3JlYXNlIHdoZW4gdGhlIG90aGVyIGluY3JlYXNlcy4NCiogQSBsaW5lYXIgYXNzb2NpYXRpb24gaXMgcHJlc2VudCB3aGVuIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdHdvIHZhcmlhYmxlcyBpcyBhIHN0cmFpZ2h0IGxpbmUuDQoqIEEgbm9ubGluZWFyIGFzc29jaWF0aW9uIGlzIHByZXNlbnQgd2hlbiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHR3byB2YXJpYWJsZXMgaXMgYSBjdXJ2ZS4NCiogSXQgaXMgcG9zc2libGUgdGhhdCBubyBjbGVhciBhc3NvY2lhdGlvbiBpcyBwcmVzZW50IGluIHRoZSBkYXRhLg0KDQpMZXQncyBjb25zaWRlciBhIHNjYXR0ZXIgcGxvdCBvZiBgYmlsbF9sZW5ndGhfbW1gIHZlcnN1cyBgYm9keV9tYXNzX2dgLg0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyBnZW9tX3BvaW50KGFlcyh4ID0gYm9keV9tYXNzX2csIHkgPSBiaWxsX2xlbmd0aF9tbSkpDQpgYGANCg0KV2Ugc2VlIGEgcG9zaXRpdmUgYXNzb2NpYXRpb24gaW4gdGhlIGdyYXBoaWMgYWJvdmUuIEFzIGBib2R5X21hc3NfZ2AgaW5jcmVhc2VzLCBgYmlsbF9sZW5ndGhfbW1gIGFsc28gdGVuZHMgdG8gaW5jcmVhc2UuIEhvd2V2ZXIsIGl0IGFwcGVhcnMgdGhhdCB0aGVyZSBtYXkgYmUgc29tZSBvYnNlcnZhdGlvbnMgaW4gdGhlIHVwcGVyIGxlZnQgcGFydCBvZiB0aGUgZ3JhcGggdGhhdCBkbyBub3QgbWF0Y2ggdGhlIG92ZXJhbGwgbGluZWFyIHRyZW5kLg0KDQpUaGlzIGNvdWxkIG1lYW46DQoNCjEuIFRoZSBkYXRhIGFyZSAqbm9pc3kqLCB3aGljaCBzaW1wbHkgbWVhbnMgdGhlIHBhdHRlcm5zIGFyZW4ndCBhcyBzdHJvbmcgYW5kIGNsZWFyIGFzIHdlIHdvdWxkIGxpa2UuDQoyLiBUaGVyZSBpcyBhIHRoaXJkIHZhcmlhYmxlIHRoYXQgbm90IGFjY291bnRlZCBmb3IgaW4gb3VyIHBsb3QgYnV0IHRoYXQgaGFzIGEgcmVsYXRpb25zaGlwIHdpdGggdGhlIG90aGVyIHR3byB2YXJpYWJsZXMuDQogICAgKiBXaGVuIHdlIGFyZSBub3QgYWNjb3VudGluZyBmb3IgdGhpcyB2YXJpYWJsZSwgaXQgaXMgY2FsbGVkIGEgKmx1cmtpbmcgdmFyaWFibGUqLg0KICAgICogV2hlbiB3ZSBhY2NvdW50IGZvciBhIGx1cmtpbmcgdmFyaWFibGUgaW4gb3VyIGFuYWx5c2lzLCBpdCBiZWNvbWVzIGEgKmNvbmZvdW5kaW5nIHZhcmlhYmxlKi4NCiAgICAqIFdlIGFyZSBkZWZpbmluZyBsdXJraW5nIGFuZCBjb25mb3VuZGluZyB2YXJpYWJsZXMgaW4gYSBnZW5lcmFsIHNlbnNlIGFuZCBub3Qgd2l0aCBzdGF0aXN0aWNhbCByaWdvci4NCg0KTGV0J3MgY29uc2lkZXIgYW4gYW5hbHlzaXMgb2YgYGJvZHlfbWFzc19nYCBhbmQgYGJpbGxfbGVuZ3RoX21tYCB0aGF0IGFjY291bnRzIGZvciBgc3BlY2llc2AuIFNwZWNpZmljYWxseSwgd2Ugd2lsbCB1c2UgdGhlIGBjb2xvcmAgYWVzdGhldGljIHNvIHRoYXQgZWFjaCBwZW5ndWluIHNwZWNpZXMgaXMgc2hvd24gaW4gYSBkaWZmZXJlbnQgY29sb3IuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArIGdlb21fcG9pbnQoYWVzKHggPSBib2R5X21hc3NfZywgeSA9IGJpbGxfbGVuZ3RoX21tLCBjb2xvciA9IHNwZWNpZXMpKQ0KYGBgDQoNCkFjY291bnRpbmcgZm9yIGBzcGVjaWVzYCBpbiB0aGUgYW5hbHlzaXMgc2hvd3MgY2xlYXIgcGF0dGVybnMgZm9yIHRoZSB0aHJlZSBwZW5ndWluIHNwZWNpZXMuIENoaW5zdHJhcCBwZW5ndWlucyB0ZW5kIHRvIGhhdmUgc21hbGxlciBib2R5IG1hc3MgYnV0IGxvbmdlciBiaWxsIGxlbmd0aHMuIEFkZWxpZSBwZW5ndWlucyBoYXZlIHNtYWxsZXIgYm9keSBtYXNzIGFuZCBiaWxsIGxlbmd0aHMuIEdlbnRvbyBwZW5ndWlucyB0ZW5kIHRvIGhhdmUgbGFyZ2VyIGJvZHkgbWFzcyBhbmQgYmlsbCBsZW5ndGhzIHRoYW4gdGhlIG90aGVyIHR3byBzcGVjaWVzLg0KDQpBcyBwcmV2aW91c2x5IG1lbnRpb25lZCwgd2Ugd2FudCBvdXIgcGxvdHMgdG8gYmUgYXMgY2xlYXIgYXMgcG9zc2libGUuIFNvbWUgaW5kaXZpZHVhbHMgaGF2ZSBkaWZmaWN1bHR5IGludGVycHJldGluZyBjb2xvcnMgaW4gZ3JhcGhpY3MuIFdlIGNhbiBhZGQgYW5vdGhlciBkaXN0aW5ndWlzaGluZyBjaGFyYWN0ZXJpc3RpYyBmb3IgdGhlIGRpZmZlcmVudCBzcGVjaWVzLiBJbiB0aGlzIGNhc2UsIHdlIHdpbGwgY2hhbmdlIHRoZSBgc2hhcGVgIG9mIHRoZSBwbG90dGVkIHBvaW50cyBmb3IgZWFjaCBzcGVjaWVzLiBUaGlzIHByb3ZpZGVzIGFub3RoZXIgYWlkIGZvciBjb3JyZWN0bHkgaW50ZXJwcmV0aW5nIHRoZSBncmFwaGljLg0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyBnZW9tX3BvaW50KGFlcyh4ID0gYm9keV9tYXNzX2csIHkgPSBiaWxsX2xlbmd0aF9tbSwgY29sb3IgPSBzcGVjaWVzLCBzaGFwZSA9IHNwZWNpZXMpKQ0KYGBgDQoNCiMjIyBBZGRpbmcgc21vb3RocyAoVmlkZW86IFtZb3VUdWJlXShodHRwczovL3lvdXR1LmJlL1AyX3FZQlYxQnljKSwgW1Bhbm9wdG9dKGh0dHBzOi8vdWNkZW52ZXIuaG9zdGVkLnBhbm9wdG8uY29tL1Bhbm9wdG8vUGFnZXMvVmlld2VyLmFzcHg/aWQ9ZGZlY2EwY2MtNDU5MS00MzA2LTllODUtYWYwZjAxNGViOTViKSkNCg0KQSBzbW9vdGggaW4gKipnZ3Bsb3QyKiogaXMgYSBtb2RlbCB0aGF0IHRoYXQgYXR0ZW1wdHMgdG8gZXN0aW1hdGUgdGhlIGF2ZXJhZ2UgcmVsYXRpb25zaGlwIGJldHdlZW4gYSByZXNwb25zZSB2YXJpYWJsZSAodHlwaWNhbGx5IHRoZSBgeWAgdmFyaWFibGUpIGFuZCBvbmUgb3IgbW9yZSBwcmVkaWN0b3IgdmFyaWFibGVzICh0eXBpY2FsbHkgdGhlIGB4YCAgYW5kIHBvc3NpYmx5IG90aGVyIHZhcmlhYmxlcykNCg0KQWRkaW5nIGEgInNtb290aCIgdG8gYSBwbG90IGNhbiBtYWtlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB2YXJpYWJsZXMgZXZlbiBjbGVhcmVyLiBUaGVyZSBhcmUgbWFueSBkaWZmZXJlbnQgc21vb3RoaW5nIG1ldGhvZHMuDQoNCkEgc21vb3RoaW5nIGxheWVyIGNhbiBiZSBhZGRlZCB0byBhIHBsb3QgdXNpbmcgYGdlb21fc21vb3RoYC4gTWFueSBtZXRob2RzIGNhbiBiZSB1c2VkIGZvciB0aGUgc21vb3RoLiAqKmdncGxvdDIqKiB0YWtlcyB0aGUgbWV0aG9kcyBgbG1gIChsaW5lYXIgbW9kZWwpLCBgZ2xtYCAoZ2VuZXJhbGl6ZWQgbGluZWFyIG1vZGVsKSwgYGdhbWAgKGdlbmVyYWxpemVkIGFkZGl0aXZlIG1vZGVsKSwgYGxvZXNzYCAobG9jYWwgZml0dGluZyksIG9yIGEgZnVuY3Rpb24gKGUuZy4sIGBNQVNTOjpybG1gIGZvciBhIHJvYnVzdCBsaW5lYXIgbW9kZWwpLiBJZiB5b3UgZG9uJ3Qga25vdyB3aGF0IHRoZXNlIGFyZSwgZG8gbm90IGFkZCB0aGVtIHRvIHlvdXIgcGxvdCBiZWNhdXNlIHRoZXkgd29uJ3QgaGVscCB5b3UgdG8gZXhwbGFpbiB0aGUgZGF0YS4NCg0KQSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgZml0cyB0aGUgc3RyYWlnaHQgbGluZSB0aGF0IG1pbmltaXplcyB0aGUgc3F1YXJlZCBkZXZpYXRpb25zIGJldHdlZW4gdGhlIHkgdmFsdWVzIGFuZCB0aGUgZXN0aW1hdGVkIGxpbmUuIFdlIGFkZCBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbGluZSB0byB0aGUgc2NhdHRlciBwbG90IGJ5IGFkZGluZyBhIGBnZW9tX3Ntb290aGAgbGF5ZXIuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArIGdlb21fcG9pbnQoYWVzKHggPSBib2R5X21hc3NfZywgeSA9IGJpbGxfbGVuZ3RoX21tLCBjb2xvciA9IHNwZWNpZXMsIHNoYXBlID0gc3BlY2llcykpICsgZ2VvbV9zbW9vdGgoYWVzKHggPSBib2R5X21hc3NfZywgeSA9IGJpbGxfbGVuZ3RoX21tKSwgbWV0aG9kID0gImxtIikNCmBgYA0KDQpUaGlzIG9ubHkgYWRkcyBhIHNpbmdsZSBsaW5lIGFjcm9zcyBhbGwgZ3JvdXBzLCB3aGljaCBjbGVhcmx5IGRvZXNuJ3QgZXhwbGFpbiB0aGUgcmVsYXRpb25zaGlwIHdoZW4gYWNjb3VudGluZyBmb3IgYHNwZWNpZXNgLiBUbyBhZGQgYSBzZXBhcmF0ZSBsaW5lIGZvciBlYWNoIGBzcGVjaWVzYCwgd2UgbmVlZCB0byBpbmNsdWRlIHRoYXQgaW4gdGhlIGFlc3RoZXRpYyBtYXBwaW5nIGZvciBgZ2VvbV9zbW9vdGhgLiBJZiB3ZSBzY2FsZSB0aGUgYHNwZWNpZXNgIHZhcmlhYmxlIHVzaW5nIHRoZSBgY29sb3JgIGFlc3RoZXRpYywgdGhlbiBhIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBsaW5lIHdpbGwgYmUgZXN0aW1hdGVkIGZvciBlYWNoIHNwZWNpZXMgaW4gdGhlIGNvbG9yIHRoYXQgbWF0Y2hlcyB0aGUgc2NhdHRlciBwbG90Lg0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyBnZW9tX3BvaW50KGFlcyh4ID0gYm9keV9tYXNzX2csIHkgPSBiaWxsX2xlbmd0aF9tbSwgY29sb3IgPSBzcGVjaWVzLCBzaGFwZSA9IHNwZWNpZXMpKSArIGdlb21fc21vb3RoKGFlcyh4ID0gYm9keV9tYXNzX2csIHkgPSBiaWxsX2xlbmd0aF9tbSwgY29sb3IgPSBzcGVjaWVzKSwgbWV0aG9kID0gImxtIikNCmBgYA0KDQojIyMgVXNpbmcgY29tbW9uIGFlc3RoZXRpY3MgYWNyb3NzIGdlb21ldHJpZXMgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9vRldIQVRhN0FRdyksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPTQwNjVmNzQ2LTVjZjItNDVlZi1hNWQ4LWFmMGYwMTRlYmNjNykpDQoNCk5vdGljZSB0aGF0IHdlIGhhZCB0byBzcGVjaWZ5IHRoZSBhZXN0aGV0aWMgbWFwcGluZ3MgaW4gZWFjaCBnZW9tLiBUaGlzIHNlZW1zIHJlZHVuZGFudC4NCg0KSWYgeW91IHNwZWNpZnkgYW4gYWVzdGhldGljIG1hcHBpbmcgaW4gYSBnZW9tLCB0aGVuIHRoZSBhZXN0aGV0aWMgbWFwcGluZyBpcyAqbG9jYWwqIGFuZCBvbmx5IGFwcGxpZXMgdG8gdGhhdCBwYXJ0aWN1bGFyIGdlb20uIElmIHlvdSB3YW50IHRvIHNwZWNpZnkgYSAqZ2xvYmFsKiBhZXN0aGV0aWMgbWFwcGluZyB0aGF0IGFwcGxpZXMgYWNyb3NzIGFsbCBnZW9tcywgdGhlbiB5b3UgY2FuIHNwZWNpZnkgdGhlIGBtYXBwaW5nYCBpbiB0aGUgYGdncGxvdGAgZnVuY3Rpb24uDQoNCkhlcmUgaXMgYSBzaW1wbGVyIHZlcnNpb24gb2YgdGhlIHByZXZpb3VzIHBsb3QuDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgID0gcGVuZ3VpbnMsIG1hcHBpbmcgPSBhZXMoeCA9IGJvZHlfbWFzc19nLCB5ID0gYmlsbF9sZW5ndGhfbW0sIGNvbG9yID0gc3BlY2llcykpICsgZ2VvbV9wb2ludChhZXMoc2hhcGUgPSBzcGVjaWVzKSkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKQ0KYGBgDQoNCiMjIEN1c3RvbWl6aW5nIHBsb3RzIChWaWRlbzogW1lvdVR1YmVdKGh0dHBzOi8veW91dHUuYmUvUGU1cjg4MUo0cDgpLCBbUGFub3B0b10oaHR0cHM6Ly91Y2RlbnZlci5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9WaWV3ZXIuYXNweD9pZD0wZDZhNDIxZi02ZjI3LTQzYmQtYmRjYS1hZjBmMDE0ZWJlM2QpKQ0KDQpVbnN1cnByaXNpbmdseSwgdGhlIGdyYXBoaWNzIHByb2R1Y2VkIGJ5ICoqZ2dwbG90MioqIGNhbiBiZSBncmVhdGx5IGN1c3RvbWl6ZWQuIFdlIGNvbnNpZGVyIHNvbWUgb2YgdGhlIGJhc2ljIGN1c3RvbWl6YXRpb25zIGJlbG93Lg0KDQojIyMgQXNzaWduaW5nIGEgcGxvdA0KDQpPbmUgb2YgdGhlIHN0cmVuZ3RocyBvZiAqKmdncGxvdDIqKiBpcyB0aGF0IHlvdSBjYW4gZWFzaWx5IGJ1aWxkIGEgcGxvdCBsYXllciBieSBsYXllci4gSW4gdGhpcyBjYXNlLCBpdCBpcyB1c2VmdWwgdG8gYXNzaWduIHRoZSBjdXJyZW50IHBsb3QgYSBuYW1lIHRoYXQgY2FuIHRoZW4gYmUgYWRkZWQgdG8gbGF0ZXIuIA0KDQpOb3RlOiBXaGVuIHlvdSBhc3NpZ24gYSBgZ2dwbG90YCBhIG5hbWUsIGl0IHdpbGwgTk9UIGJlIGRpc3BsYXllZCwgYXMgaXMgdHlwaWNhbCB3aGVuIGFzc2lnbmluZyBvYmplY3RzIGluIFIuIFlvdSBoYXZlIHRvIHRoZW4gcHJpbnQgdGhlIG9iamVjdCB1c2luZyB0aGUgYHByaW50YCBmdW5jdGlvbiBvciBieSBzaW1wbHkgcnVubmluZyB0aGUgb2JqZWN0IG5hbWUgaW4gdGhlIENvbnNvbGUuIExldCdzIGFzc2lnbiB0aGUgbmFtZSBgZ2dwYCB0byB0aGUgYmFzaWMgcGVuZ3VpbiBzY2F0dGVyIHBsb3QgZGlzcGxheWluZyBiaWxsIGxlbmd0aCB2ZXJzdXMgYm9keSBtYXNzLCB1c2luZyBjb2xvcnMgYW5kIHNoYXBlcyB0byBkaXN0aW5ndWlzaCBiZXR3ZWVuIGRpZmZlcmVudCBzcGVjaWVzLg0KDQpgYGB7cn0NCmdncCA8LSBnZ3Bsb3QoZGF0YSAgPSBwZW5ndWlucywgbWFwcGluZyA9IGFlcyh4ID0gYm9keV9tYXNzX2csIHkgPSBiaWxsX2xlbmd0aF9tbSwgY29sb3IgPSBzcGVjaWVzKSkgKyBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IHNwZWNpZXMpKQ0KZ2dwDQpgYGANCg0KV2UnbGwgY3VzdG9taXplIHRoaXMgcGxvdCBiZWxvdy4NCg0KIyMjIEF4aXMgbGFiZWxzIGFuZCB0aXRsZXMNClRoZSB4LWF4aXMgbGFiZWxzLCB5LWF4aXMgbGFiZWxzLCBhbmQgdGl0bGUgb2YgYSBwbG90IGFyZSBjb250cm9sbGVkIHVzaW5nIHRoZSBgeGxhYmAsIGB5bGFiYCwgYW5kIGBnZ3RpdGxlYCBmdW5jdGlvbnMsIHJlc3BlY3RpdmVseS4gTGV0J3MgY3VzdG9taXplIHRoZSBwZW5ndWluIHNjYXR0ZXIgcGxvdCBwcmV2aW91c2x5IGRpc2N1c3NlZC4gV2UnbGwgdXBkYXRlIHRoZSBgZ2dwYCBvYmplY3QgdG8gaW5jbHVkZSBvdXIgaW1wcm92ZW1lbnRzIHRoZW4gcHJpbnQgdGhlIHJlc3VsdC4NCg0KYGBge3J9DQpnZ3AgPC0gZ2dwICsgeGxhYigiYm9keSBtYXNzIChnKSIpICsgeWxhYigiYmlsbCBsZW5ndGggKG1tKSIpICsgZ2d0aXRsZSgiUGVuZ3VpbiBib2R5IGNoYXJhY3RlcmlzdGljcyIpDQpnZ3ANCmBgYA0KDQojIyMgQXhpcyBsaW1pdHMNClRoZSB4LWF4aXMgYW5kIHktYXhpcyBsaW1pdHMgYXJlIGNvbnRyb2xsZWQgdXNpbmcgdGhlIGB4bGltYCBhbmQgYHlsaW1gIGZ1bmN0aW9ucy4gRWFjaCBmdW5jdGlvbiB0YWtlcyB0aGUgbG93ZXIgYW5kIHVwcGVyIGxpbWl0IHlvdSB3YW50IHRvIHNldCwgZS5nLiwgYHhsaW0oMCwgMTAwKWAuIExldCdzIGNoYW5nZSB0aGUgYXhlcyBmb3IgdGhlIHBlbmd1aW4gc2NhdHRlciBwbG90IHByZXZpb3VzbHkgZGlzY3Vzc2VkLiBUaGVyZSdzIG5vdGhpbmcgd3Jvbmcgd2l0aCB0aGUgcHJldmlvdXMgbGltaXRzLCBidXQgd2UnbGwgZXh0ZW5kIHRoZW0gYSBiaXQgZm9yIGRlbW9uc3RyYXRpb24uDQoNCmBgYHtyfQ0KZ2dwICsgeGxpbSgyMDAwLCA3MDAwKSArIHlsaW0oMCwgODApDQpgYGANCg0KIyMjIE1hbnVhbCBhZXN0aGV0aWNzDQpBZXN0aGV0aWNzIGNhbiBiZSBzZXQgbWFudWFsbHkgaW4gYSBnZW9tIGJ5IHNwZWNpZnlpbmcgdGhlIGFlc3RoZXRpYyBkaXJlY3RseSBpbiB0aGUgZ2VvbSBidXQgb3V0c2lkZSB0aGUgYGFlc2AgZnVuY3Rpb24uIExldCdzIGNoYW5nZSB0aGUgc2hhcGUgb2YgdGhlIHBvaW50cyBmb3IgYSBzY2F0dGVyIHBsb3QsIGluY3JlYXNlIHRoZSBzaXplIG9mIHRoZSBwb2ludHMsIGFuZCBtYW51YWxseSBjaGFuZ2UgdGhlIGNvbG9yIG9mIHRoZSBwb2ludHMuDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgID0gcGVuZ3VpbnMpICsgZ2VvbV9wb2ludChhZXMoeCA9IGJvZHlfbWFzc19nLCB5ID0gYmlsbF9sZW5ndGhfbW0pLCBzaGFwZSA9IDE1LCBzaXplID0gNSwgY29sb3IgPSAiYmx1ZSIpDQpgYGANCg0KIyMjIFNjYWxlIGN1c3RvbWl6YXRpb24NCg0KKlNjYWxpbmcqIGlzIHRoZSBuYW1lIG9mIHRoZSBwcm9jZXNzIHVzZWQgYnkgKipnZ3Bsb3QyKiogdG8gbWFwIGEgdmFyaWFibGUgdG8gYSB1bmlxdWUgdmFsdWUuIFRoaXMgaXMgcGFydGljdWxhcmx5IGltcG9ydGFudCBmb3IgYGZhY3RvcmAgdmFyaWFibGVzLiBGb3IgZXhhbXBsZSwgY29sb3Igc2NhbGluZyBpcyB0aGUgcHJvY2VzcyBieSB3aGljaCB0aGUgdGhlIGBjb2xvcmAgYWVzdGhldGljIGlzIG1hcHBlZCB0byBwYXJ0aWN1bGFyIGNvbG9ycy4gVGhlc2UgY2FuIGJlIGN1c3RvbWl6ZWQgdXNpbmcgdGhlIHZhcmlvdXMgYHNjYWxlXypgIGZ1bmN0aW9ucywgd2hlcmUgdGhlICJcKiIgaXMgYSBwbGFjZWhvbGRlciBmb3IgdGhlIHJlc3Qgb2YgdGhlIGZ1bmN0aW9uIG5hbWUuDQoNCkxldCdzIG1hbnVhbGx5IHNldCB0aGUgY29sb3Igc2NhbGluZyB1c2luZyB0aGUgYHNjYWxlX2NvbG9yX21hbnVhbGAgZnVuY3Rpb24uIFRoZSBjb2xvcnMgcHJvdmlkZWQgYmVsb3cgY29tZSBmcm9tIHRoZSAqRGFyazIqIGNvbG9yIHBhbGV0dGUgb2YgdGhlICpxdWFsaXRhdGl2ZSogdHlwZSBwcm92aWRlZCBieSB0aGUgQ29sb3JCcmV3ZXIgd2Vic2l0ZSAoW2h0dHBzOi8vY29sb3JicmV3ZXIyLm9yZy9dKGh0dHBzOi8vY29sb3JicmV3ZXIyLm9yZy8pKSwgd2hpY2ggcHJvdmlkZXMgY29sb3IgYWR2aWNlLiBJdCdzIHBhcnRpY3VsYXJseSBncmVhdCBmb3IgaWRlbnRpZnlpbmcgY29sb3JibGluZC1mcmllbmRseSBjb2xvciBwYWxldHRlcy4NCg0KYGBge3J9DQpnZ3AgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiIzFiOWU3NyIsICIjZDk1ZjAyIiwgIiM3NTcwYjMiKSkNCmBgYA0KDQpBbHRlcm5hdGl2ZWx5LCB3ZSBjb3VsZCBoYXZlIHVzZWQgYHNjYWxlX2NvbG9yX2JyZXdlcmAgZnVuY3Rpb24gaW5zdGVhZCwgd2hpY2ggYXV0b21hdGljYWxseSBpbmNsdWRlcyB0aGUgY29sb3IgcGFsZXR0ZXMgZGVzaWdlbmQgYnkgQ29sb3IgQnJld2VyLiBSdW4gYD9zY2FsZV9jb2xvcl9icmV3ZXJgIGZvciBtb3JlIGRldGFpbHMuDQoNCmBgYHtyfQ0KZ2dwICsgc2NhbGVfY29sb3JfYnJld2VyKHR5cGUgPSAic2VxIiwgcGFsZXR0ZSA9ICJEYXJrMiIpDQpgYGANCg0KU2NhbGluZ3MgY2FuIGJlIGN1c3RvbWl6ZWQgaW4gbWFueSB3YXlzLCBidXQgaXQgcXVpY2tseSBiZWNvbWVzIGNvbXBsaWNhdGVkLCBzbyB3ZSBkb24ndCBkaXNjdXNzIHRoZW0gZnVydGhlci4NCg0KIyMjIExlZ2VuZCBjdXN0b21pemF0aW9uDQoNClRoZSBsZWdlbmQgY2FuIGJlIGN1c3RvbWl6ZWQgaW4gbWFueSB3YXlzLiBUbyBtb3ZlIHRoZSBsb2NhdGlvbiBvZiB0aGUgbGVnZW5kLCB5b3UgY2FuIHNwZWNpZnkgdGhlIGBsZWdlbmQucG9zaXRpb25gIGFyZ3VtZW50IG9mIHRoZSBgdGhlbWVgIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCmdncCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KYGBgDQoNCllvdSBjYW4gY3VzdG9taXplIGxlZ2VuZCBzaXplLCBiYWNrZ3JvdW5kLCBuYW1lcywgY29sb3JzLCBldGMuIEZvciBzcGVjaWZpYyBpbXBsZW1lbnRhdGlvbiBkZXRhaWxzLCBpdCBpcyBwcm9iYWJseSBiZXN0IHRvIGRvIGEgd2ViIHNlYXJjaC4NCg0KIyMjIFRoZW1lcw0KDQpBIHRoZW1lIGlzIGEgY3VzdG9taXplZCBzdHlsZSB0aGF0IGNoYW5nZXMgdGhlIG92ZXJhbGwgYXBwZWFyYW5jZSBvZiB5b3VyIGdyYXBoaWMuICoqZ2dwbG90MioqIHByb3ZpZGVzIGEgbnVtYmVyIG9mIGJ1aWx0LWluIHRoZW1lcy4gU29tZSBvZiB0aGUgYnVpbHQtaW4gdGhlbWVzIGluY2x1ZGU6DQoNCiogYHRoZW1lX2dyYXlgOiB0aGUgZGVmYXVsdCB0aGVtZS4gR3JheSBiYWNrZ3JvdW5kIGFuZCB3aGl0ZSBncmlkIGxpbmVzLg0KKiBgdGhlbWVfYndgOiB3aGl0ZSBiYWNrZ3JvdW5kIGFuZCBncmF5IGdyaWQgbGluZXMuDQoqIGB0aGVtZV9jbGFzc2ljYDogd2hpdGUgYmFja2dyb3VuZCBhbmQgbm8gZ3JpZCBsaW5lcy4NCiogYHRoZW1lX21pbmltYWxgOiB3aGl0ZSBiYWNrZ3JvdW5kLCBubyBncmlkIGxpbmVzLCBubyBheGlzIGxpbmVzLg0KKiBgdGhlbWVfbGlnaHRgOiB3aGl0ZSBiYWNrZ3JvdW5kLCBncmF5IGdyaWQgbGluZXMsIGFuZCBhIGJveCBhcm91bmQgdGhlIHBsb3R0aW5nIHJlZ2lvbi4NCiogYHRoZW1lX2RhcmtgOiBlc3NlbnRpYWxseSBhICJkYXJrIiBvcHBvc2l0ZSB0byBgdGhlbWVfbGlnaHRgLg0KDQpXZSB3aWxsIHVzZSB0aGUgKipwYXRjaHdvcmsqKiBwYWNrYWdlIFtAUi1wYXRjaHdvcmtdIHRvIGVhc2lseSBjb21iaW5lIHNldmVyYWwgcGxvdHMgaW50byBhIHNpbmdsZSBncmFwaGljLiBUaGUgKipwYXRjaHdvcmsqKiBwYWNrYWdlIHVzZXMgYCgpYCB0byBncm91cCBhIHNldCBvZiBwbG90cywgYHxgIHRvIHNlcGFyYXRlIHBsb3RzIGhvcml6b250YWxseSwgYW5kIGAvYCB0byBzdGFjayBzZXRzIG9mIHBsb3RzIHZlcnRpY2FsbHkuDQoNCmBgYHtyfQ0KZ2dwX2dyYXkgPC0gZ2dwICsgdGhlbWVfZ3JheSgpICsgZ2d0aXRsZSgidGhlbWVfZ3JheSIpDQpnZ3BfYncgPC0gZ2dwICsgdGhlbWVfYncoKSArIGdndGl0bGUoInRoZW1lX2J3IikNCmdncF9jbGFzc2ljIDwtIGdncCArIHRoZW1lX2NsYXNzaWMoKSArIGdndGl0bGUoInRoZW1lX2NsYXNzaWMiKQ0KZ2dwX21pbmltYWwgPC0gZ2dwICsgdGhlbWVfbWluaW1hbCgpICsgZ2d0aXRsZSgidGhlbWVfbWluaW1hbCIpDQpnZ3BfbGlnaHQgPC0gZ2dwICsgdGhlbWVfbGlnaHQoKSArIGdndGl0bGUoInRoZW1lX2xpZ2h0IikNCmdncF9kYXJrIDwtIGdncCArIHRoZW1lX2RhcmsoKSArIGdndGl0bGUoInRoZW1lX2RhcmsiKQ0KbGlicmFyeShwYXRjaHdvcmspDQooZ2dwX2dyYXkgfCBnZ3BfYncpIC8gKGdncF9jbGFzc2ljIHwgZ2dwX21pbmltYWwpIC8gKGdncF9saWdodCB8IGdncF9kYXJrKQ0KDQpgYGANCg0KVGhlc2UgdGhlbWVzIGNhbiBiZSBjdXN0b21pemVkIGZ1cnRoZXIgYnkgY2hhbmdpbmcgdGhlIGFwcHJvcHJpYXRlIGFzcGVjdHMgb2YgdGhlIHBsb3RzLiBXZSBsZWF2ZSB0aGUgdXNlciB0byB1c2Ugd2ViIHNlYXJjaGVzIGZvciB0aGUgZGVzaXJlZCBjdXN0b21pemF0aW9ucy4NCg0KIyMgT3RoZXIgY29tcG9uZW50cyBvZiBhIGBnZ3Bsb3RgIA0KDQojIyMgRmFjZXR0aW5nIChWaWRlbzogW1lvdVR1YmVdKGh0dHBzOi8veW91dHUuYmUvNk5HckVKN3BoVEEpLCBbUGFub3B0b10oaHR0cHM6Ly91Y2RlbnZlci5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9WaWV3ZXIuYXNweD9pZD04YjA2YjI0My0wYmRlLTQwMGQtYjhkMi1hZjBmMDE1MWM2YTkpKQ0KDQoqRmFjZXR0aW5nKiBhIGRhdGEgc2V0IGNyZWF0ZXMgc2VwYXJhdGUgcGxvdHRpbmcgcGFuZWxzIGZvciBlYWNoIGxldmVsIG9mIG9uZSBvciBtb3JlIGRpc2NyZXRlIHZhcmlhYmxlcy4gRmFjZXR0aW5nIGlzIHVzZWZ1bCBmb3IgZXhhbWluaW5nIHBhdHRlcm5zIGZvciBjb21iaW5hdGlvbnMgb2YgbGV2ZWxzIGZvciBvbmUgb3IgbW9yZSBkaXNjcmV0ZSB2YXJpYWJsZXMuIFRlY2huaWNhbGx5LCBmYWNldHRpbmcgY2FuIGJlIHBlcmZvcm1lZCBmb3IgYG51bWVyaWNgIHZhcmlhYmxlcyB3aXRoIGEgcmVsYXRpdmVseSBzbWFsbCBudW1iZXIgb2YgdW5pcXVlIHZhbHVlcywgYnV0IGZvciBwcmFjdGljYWwgcmVhc29ucywgZmFjZXR0aW5nIGlzIG1vc3QgYXBwcm9wcmlhdGUgZm9yIGBmYWN0b3JgIHZhcmlhYmxlcy4gDQoNClRoZXJlIGFyZSB0d28gZnVuY3Rpb25zIGZvciBjcmVhdGluZyBmYWNldHRlZCBncmFwaGljcyBpbiAqKmdncGxvdDIqKjoNCg0KKiBgZmFjZXRfd3JhcGAgZmFjZXRzIHRoZSBwbG90cyBiYXNlZCBvbiBhIHNpbmdsZSBgZmFjdG9yYCB2YXJpYWJsZS4gVGhlIHBhbmVscyBhcmUgd3JhcHBlZCBhcm91bmQgdGhlIHBsb3R0aW5nIHdpbmRvdy4NCiogYGZhY2V0X2dyaWRgIGZvcm1zIGEgbWF0cml4IG9mIHBhbmVscyBiYXNlZCBvbiByb3cgYW5kIGNvbHVtbiBmYWNldHRpbmcgdmFyaWFibGVzLg0KDQpJbiB0aGUgcGxvdCBiZWxvdywgd2UgZmFjZXQgc2NhdHRlciBwbG90cyBvZiBgYmlsbF9sZW5ndGhfbW1gIHZlcnN1cyBgYm9keV9tYXNzX2dgIGJ5IGBzcGVjaWVzYC4NCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9wb2ludChhZXMoeCA9IGJvZHlfbWFzc19nLCB5ID0gYmlsbF9sZW5ndGhfbW0pKSArIGZhY2V0X3dyYXAofnNwZWNpZXMpDQpgYGANCg0KQnkgZGVmYXVsdCwgdGhlIHNhbWUgeCBhbmQgeSBheGVzIGFyZSB1c2VkIGluIGFsbCBwYW5lbHMuIFRoaXMgaXMgc3RhbmRhcmQgYmVjYXVzZSBpdCBmYWNpbGl0YXRlcyBkaXJlY3QgY29tcGFyaXNvbnMgYWNyb3NzIHBhbmVscy4gVGhpcyBjYW4gYmUgY3VzdG9taXplZCB1c2luZyB0aGUgYHNjYWxlc2AgYXJndW1lbnQgb2YgdGhlIGBmYWNldF8qYCBmdW5jdGlvbnMuIFRoZSBhbGxvd2FibGUgYHNjYWxlc2AgYXJndW1lbnQgdmFsdWVzIGFyZToNCg0KKiBgZml4ZWRgOiBzYW1lIHggYW5kIHkgYXhlcyBmb3IgYWxsIHBhbmVscy4gVGhlIGRlZmF1bHQuDQoqIGBmcmVlYDogaW5kaXZpZHVhbCB4IGFuZCB5IGF4ZXMgZm9yIGVhY2ggcGFuZWwNCiogYGZyZWVfeGA6IGluZGl2aWR1YWwgeCBheGVzIGZvciBlYWNoIHBhbmVsLCBjb21tb24geSBheGlzDQoqIGBmcmVlX3lgOiBpbmRpdmlkdWFsIHkgYXhlcyBmb3IgZWFjaCBwYW5lbCwgY29tbW9uIHggYXhpcw0KDQpXZSBub3cgY29uc2lkZXIgdXNlIHRoZSBgZmFjZXRfZ3JpZGAgZnVuY3Rpb24gdG8gZmFjZXQgYnkgdHdvIGBmYWN0b3JgIHZhcmlhYmxlcywgYHNwZWNpZXNgIGFuZCBgc2V4YC4gUHJpb3IgdG8gcGxvdHRpbmcsIHdlIHJlbW92ZSB0aGUgb2JzZXJ2YXRpb25zIHdpdGggYE5BYCBmb3IgdGhlIGBzZXhgIHZhcmlhYmxlLg0KDQpgYGB7cn0NCmdncGxvdChzdWJzZXQocGVuZ3VpbnMsICFpcy5uYShzZXgpKSkgKyBnZW9tX3BvaW50KGFlcyh4ID0gYm9keV9tYXNzX2csIHkgPSBiaWxsX2xlbmd0aF9tbSkpICsgZmFjZXRfZ3JpZChzcGVjaWVzIH4gc2V4KQ0KYGBgDQoNClRoZXJlIGFwcGVhcnMgdG8gYmUgZGlmZmVyZW5jZXMgaW4gdGhlIHNpemVzIG9mIHRoZSBtYWxlIGFuZCBmZW1hbGUgcGVuZ3VpbnMsIGJpdCBpdCBpcyBkaWZmaWN1bHQgdG8gY29tcGFyZSB0aGUgcGF0dGVybnMgYWNyb3NzIHBhbmVscy4gSXQgbWF5IGJlIG1vcmUgdXNlZnVsIHRvIGZhY2V0IGJ5IGBzcGVjaWVzYCBidXQgZGlzdGluZ3Vpc2ggYnkgYHNleGAgaW4gYSBzaW5nbGUgcGFuZWwuDQoNCmBgYHtyfQ0KZ2dwbG90KHN1YnNldChwZW5ndWlucywgIWlzLm5hKHNleCkpKSArIGdlb21fcG9pbnQoYWVzKHggPSBib2R5X21hc3NfZywgeSA9IGJpbGxfbGVuZ3RoX21tLCBjb2xvciA9IHNleCwgc2hhcGUgPSBzZXgpKSArIGZhY2V0X3dyYXAofiBzcGVjaWVzKQ0KYGBgDQoNCkl0IGlzIGNsZWFyIG5vdyB0aGF0IGFjcm9zcyBzcGVjaWVzLCB0aGUgbWFsZXMgdGVuZCB0byBoYXZlIGdyZWF0ZXIgYm9keSBtYXNzIGFuZCBiaWxsIGxlbmd0aCBpbiBjb21wYXJpc29uIHRvIHRoZSBmZW1hbGVzLg0KDQojIyMgU3RhdGlzdGljYWwgdHJhbnNmb3JtYXRpb25zIChWaWRlbzogW1lvdVR1YmVdKGh0dHBzOi8veW91dHUuYmUvaWE3Q1pFQmdvS2MpLCBbUGFub3B0b10oaHR0cHM6Ly91Y2RlbnZlci5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9WaWV3ZXIuYXNweD9pZD1kODQyMzQxNC1jYjIzLTRiZjQtOTg3NS1hZjBmMDE1MWM2NmYpKQ0KDQoqU3RhdGlzdGljYWwgdHJhbnNmb3JtYXRpb25zKiBhcmUgdGhlIHByb2Nlc3MgdGhhdCAqKmdncGxvdDIqKiB1c2VzIHRvIHN1bW1hcml6ZSB0aGUgZGF0YSBiZWZvcmUgcGxvdHRpbmcuIEJ5IGRlZmF1bHQsIGV2ZXJ5IGdlb20gdXNlcyBhIGRlZmF1bHQgYHN0YXRgIGFyZ3VtZW50IHRoYXQgc3VtbWFyaXplcyB0aGUgZGF0YSBiZWZvcmUgcGxvdHRpbmcuIFBlcmhhcHMgc3VycHJpc2luZ2x5LCBldmVyeSBgc3RhdF8qYCBoYXMgYSBkZWZhdWx0IGBnZW9tYCBhcmd1bWVudCB0byBzcGVjaWZ5IGhvdyB0aGUgc3VtbWFyaXplZCBkYXRhIHNob3VsZCBiZSBwbG90dGVkLg0KDQpJbiBnZW5lcmFsLCB5b3UgY2FuIHNpbXBseSBsZWFybiB0byB1c2UgdGhlIGdlb20gdGhhdCBzdW1tYXJpemVzIGFuZCBwbG90cyB0aGUgZGF0YSB0byB5b3VyIGxpa2luZy4gRGVwZW5kaW5nIG9uIHRoZSBkYXRhIHlvdSBoYXZlLCBzb21ldGltZXMgeW91IG1heSB3YW50IHRvIHNwZWNpZnkgYSBkaWZmZXJlbnQgYHN0YXRgIGFyZ3VtZW50LiBJbiBnZW5lcmFsLCBjdXN0b20gYHN0YXRgIGFyZ3VtZW50cyBhcmUgYmV5b25kIHRoZSBzY29wZSBvZiB0aGlzIHR1dG9yaWFsLg0KDQpIb3dldmVyLCB3ZSBkbyBjb25zaWRlciBhIHNpbXBsZSBleGFtcGxlIGludm9sdmluZyBiYXIgY2hhcnRzLg0KDQpCeSBkZWZhdWx0LCBgZ2VvbV9iYXJgIHVzZXMgYHN0YXRfY291bnRgIHRvIGNvdW50IHRoZSBudW1iZXIgb2YgdmFsdWVzIGhhdmluZyBlYWNoIGxldmVsIG9mIGEgYGZhY3RvcmAgdmFyaWFibGUgYW5kIHRoZW4gcGxvdHMgdGhlIGhlaWdodCBvZiBlYWNoIGJhciBhYm92ZSB0aGUgbmFtZXMgb2YgdGhlIGBmYWN0b3JgIGxldmVscy4NCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9iYXIoYWVzKHggPSBzZXgpKQ0KYGBgDQoNCkhvd2V2ZXIsIGxldCdzIHN1cHBvc2Ugb3VyIGRhdGEgZnJhbWUgYWxyZWFkeSBoYXMgY291bnRzIGZvciB0aGUgbnVtYmVyIG9mIHZhbHVlcyBpbiBlYWNoIGxldmVsLiBJbiB0aGUgY2FzZSwgd2UgbXVzdCBjaGFuZ2UgdGhlIGBzdGF0YCBhcmd1bWVudCBmb3IgYGdlb21fYmFyYCB0byBgImlkZW50aXR5ImAgKGZvciBgc3RhdF9pZGVudGl0eWApLiBXZSBkbyB0aGlzIGluIHRoZSBleGFtcGxlIGJlbG93LCB3aGVyZSB3ZSBhbHNvIGhhdmUgdG8gY2xlYXJseSBpbmRpY2F0ZSB0aGUgYHhgIHZhcmlhYmxlIGFuZCB0aGUgYHlgIHZhcmlhYmxlIGluIHRoZSBwbG90Lg0KDQpgYGB7cn0NCiMgY291bnQgdmFsdWVzIHdpdGggZWFjaCBsZXZlbA0KcGVuZ3VpbnNfY291bnQgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShwZW5ndWlucyRzZXgsIHVzZU5BID0gImlmYW55IikpDQojIHJlbmFtZSB2YXJpYWxlcw0KbmFtZXMocGVuZ3VpbnNfY291bnQpIDwtIGMoInNleCIsICJjb3VudCIpDQojIHByaW50IGRhdGEgZnJhbWUNCnBlbmd1aW5zX2NvdW50DQojIGNyZWF0ZSBwbG90DQpnZ3Bsb3QocGVuZ3VpbnNfY291bnQpICsgZ2VvbV9iYXIoYWVzKHggPSBzZXgsIHkgPSBjb3VudCksIHN0YXQgPSAiaWRlbnRpdHkiKQ0KYGBgDQoNCiMjIyBQb3NpdGlvbiANCg0KVGhlIGBwb3NpdGlvbmAgYXJndW1lbnQgb2YgYSBnZW9tIGRlc2NyaWJlcyB0aGUgcG9zaXRpb25zIG9mIHRoZSBvYmplY3RzIHByb2R1Y2VkLiBNb3N0IG9mIHRoZSB0aW1lIHRoaXMgaXMgcHJldHR5IHN0cmFpZ2h0Zm9yd2FyZCwgYnV0IHRoZXJlIGNhbiBiZSB0aW1lcyB3aGVuIGl0IGlzIGhlbHBmdWwgKG9yIHVuaGVscGZ1bCkgdG8gY3VzdG9taXplIGBwb3NpdGlvbmAuDQoNCiMjIyMgSml0dGVyaW5nIGRhdGEgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9SdjNvTlFRSUtJdyksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPWZiOWYxZWI0LWE4NmEtNDZiMC1iOGM1LWFmMGYwMTUxYzYzYykpDQpTb21ldGltZXMsIGRhdGEgdmFsdWVzL2luZm9ybWF0aW9uIG92ZXJsYXAsIHdoaWNoIG1ha2VzIGludGVycHJldGluZyB0aGUgcGxvdCBtb3JlIGRpZmZpY3VsdC4gSW4gdGhhdCBjYXNlLCBpdCBjYW4gYmUgdXNlZnVsIHRvICJqaXR0ZXIiIHRoZSBkYXRhIHNvIHRoYXQgdGhlIHZhbHVlcy9pbmZvcm1hdGlvbiBhcmUgbm90IG92ZXJsYXBwaW5nLiBUaGUgZGF0YSBhcmUgbm90IHRlY2huaWNhbGx5IGFzIGFjY3VyYXRlLCBidXQgdGhlIGxhcmdlLXNjYWxlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF0YSBpcyBlYXNpZXIgdG8gaW50ZXJwcmV0Lg0KDQpUaGUgYEdhbHRvbmAgZGF0YSBzZXQgaW4gdGhlICoqSGlzdERhdGEqKiBwYWNrYWdlIFtAUi1IaXN0RGF0YV0gaW5jbHVkZXMgOTI4IG9ic2VydmF0aW9ucyBvZiAyIHZhcmlhbGVzLiBUaGUgdmFyaWFibGVzIGFyZToNCg0KKiBgcGFyZW50YDogYSBgbnVtZXJpY2AgdmFyaWFibGUgaW5kaWNhdGluZyB0aGUgYXZlcmFnZSBoZWlnaHQgb2YgYSBjaGlsZCdzIG1vdGhlciBhbmQgZmF0aGVyIChpbikuDQoqIGBjaGlsZGA6IGEgYG51bWVyaWNgIHZhcmlhYmxlIGluZGljYXRpbmcgdGhlIGhlaWdodCBvZiBlYWNoIGNoaWxkIChpbikuDQoNClRoZSBkYXRhIGFyZSByb3VuZGVkIHRvIHRoZSBuZWFyZXN0IDAuMSBpbmNoLiBBZGRpdGlvbmFsIGluZm9ybWF0aW9uIGNhbiBiZSBmb3VuZCBieSBydW5uaW5nIGA/SGlzdERhdGE6OkdhbHRvbmAgaW4gdGhlIENvbnNvbGUuIA0KDQpVbmZvcnR1bmF0ZWx5LCB0aGUgcm91bmRpbmcgdGhlIGRhdGEgdmFsdWVzIGxlYWRzIHRvIG92ZXJwbG90dGluZy4gQmVsb3csIHdlIGxvYWQgdGhlIGBHYWx0b25gIGRhdGEgYW5kIGNyZWF0ZSBhIHNjYXR0ZXIgcGxvdCBvZiBjaGlsZCBoZWlnaHQgdmVyc3VzIHBhcmVudHMnIGF2ZXJhZ2UgaGVpZ2h0Lg0KDQpgYGB7cn0NCmRhdGEoR2FsdG9uLCBwYWNrYWdlID0gIkhpc3REYXRhIikNCmdncGxvdChHYWx0b24pICsgZ2VvbV9wb2ludChhZXMoeCA9IHBhcmVudCwgeSA9IGNoaWxkKSkNCmBgYA0KDQpUaGVyZSBpcyBhIGxvdCBvZiBvdmVycGxvdHRpbmcgaW4gdGhpcyBncmFwaGljLiBXZSB1c2UgdGhlIGBwb3NpdGlvbmAgYXJndW1lbnQgdG8gYGppdHRlcmAgdGhlIGRhdGEgdG8gbWFrZSB0aGUgb3ZlcmFsbCBkaXN0cmlidXRpb24gY2xlYXJlci4gVGhlcmUncyBub3QgYSBsb3Qgb2YgaW5mb3JtYXRpb24gZ2FpbiBmcm9tIGppdHRlcmluZyB0aGUgZGF0YSBpbiB0aGlzIHBhcnRpY3VsYXIgZXhhbXBsZSwgYnV0IHdlIGNhbiBzZWUgdGhhdCB0aGVyZSBhcmUgbW9yZSBvYnNlcnZhdGlvbnMgaW4gdGhlIGNlbnRyYWwgcGFydCBvZiB0aGUgcGxvdC4NCg0KYGBge3J9DQpnZ3Bsb3QoR2FsdG9uKSArIGdlb21fcG9pbnQoYWVzKHggPSBwYXJlbnQsIHkgPSBjaGlsZCksIHBvc2l0aW9uID0gImppdHRlciIpDQpgYGANCg0KIyMjIyBTdGFja2luZywgZmlsbGluZywgYW5kIGRvZGdpbmcgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9xMXRUUnVSZXE3dyksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPTYyMzhjOTlmLTUyNWQtNDU4OS04NTNkLWFmMGYwMTUxYzZkYykpDQoNCkkgd2FzIHRlbXB0ZWQgdG8gdGl0bGUgdGhpcyBzZWN0aW9uLCAicGxvdHMgdGhhdCBtYWtlIG1lIGVtb3Rpb25hbCIgYmVjYXVzZSBzZXZlcmFsIG9mIHRoZSBncmFwaGljcyBiZWxvdyBjYXVzZSBtZSB0byBnbyB0aHJvdWdoIGEgcmFuZ2Ugb2YgbW9zdGx5IHVucGxlYXNhbnQgZW1vdGlvbnMgd2hlbiBJIHZpZXcgdGhlbS4NCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSdsbCBzZWUgaG93IGBzdGFja2AsIGBmaWxsYCwgYW5kIGBkb2RnZWAgY2FuIGJlIHVzZWQgaW4gdGhlIGBwb3NpdGlvbmAgYXJndW1lbnQuIFdoaWxlIHRoZXNlIGV4YW1wbGVzIGRlbW9uc3RyYXRlIHRoZWlyIHVzZSwgc29tZSBvZiB0aGUgcmVzdWx0cyBhcmUgYW4gYWJzb2x1dGUgbWVzcy4gSSB3aWxsIGNvbmNsdWRlIHdpdGggYWx0ZXJuYXRpdmUgcmVjb21tZW5kYXRpb25zLg0KDQpXaGVuIGNyZWF0aW5nIGEgYmFyIGNoYXJ0LCBhIGBmYWN0b3JgIHZhcmlhYmxlIGlzIG1hcHBlZCB0byB0aGUgYHhgIGFlc3RoZXRpYy4gSWYgYSBkaWZmZXJlbnQgYGZhY3RvcmAgdmFyaWFibGUgaXMgbWFwcGVkIHRvIHRoZSBgZmlsbGAgYWVzdGhldGljLCB0aGVuICoqZ2dwbG90MioqIHdpbGwgYHN0YWNrYCB0aGUgYmFycyBmb3IgdGhlIGBmaWxsYCB2YXJpYWJsZSBpbnNpZGUgdGhlIGJhcnMgZm9yIHRoZSBgeGAgdmFyaWFibGUuIENvbnNpZGVyIHRoZSBiYXIgY2hhcnQgYmVsb3cgaW4gd2hpY2ggd2UgInN0YWNrIiBwZW5ndWluIGBzZXhgIHdpdGhpbiB0aGUgYmFycyBmb3IgdGhlIGRpZmZlcmVudCBzcGVjaWVzLg0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyAgZ2VvbV9iYXIoYWVzKHggPSBzcGVjaWVzLCBmaWxsID0gc2V4KSkNCmBgYA0KDQpJZiB5b3UgY29tcGFyZSB0aGlzIGJhciBjaGFydCB3aXRoIHRoZSBvcmlnaW5hbCBvbmUgdGhhdCBkb2Vzbid0IGZpbGwgYnkgYHNleGAsIHRoZSBiYXIgaGVpZ2h0IGlzIGlkZW50aWNhbC4gSG93ZXZlciwgd2l0aGluIGVhY2ggc3BlY2llcywgd2UgYGZpbGxgIHRoZSBiYXJzIHRvIHNob3cgdGhlIG51bWJlciBvZiBwZW5ndWlucyB3aXRoIGVhY2ggbGV2ZWwgb2YgYHNleGAuDQoNClRoaXMgcGxvdCBpcyBkaWZmaWN1bHQgdG8gaW50ZXJwcmV0LiBJdCB0YWtlcyBhIGxvdCBvZiBtZW50YWwgZW5lcmd5IHRvIGNvbXB1dGUgYXBwcm94aW1hdGVseSBob3cgbWFueSBtYWxlLCBmZW1hbGUsIGFuZCBgTkFgIG9ic2VydmF0aW9ucyB0aGVyZSBhcmUgd2l0aGluIGVhY2ggc3BlY2llcy4gV2hpbGUgdGhlIHVwZGF0ZWQgYmFyIGNoYXJ0IGNvbXBhY3RseSBwcm92aWRlcyBhIGxvdCBvZiBpbmZvcm1hdGlvbiwgaXQgZG9lc24ndCBmYWNpbGl0YXRlIGVhc3kgaW50ZXJwcmV0YXRpb24uDQoNCklmIHdlIHdhbnRlZCB0byBjb21wYXJlIHRoZSBwcm9wb3J0aW9uIG9mIG1hbGUsIGZlbWFsZSwgYW5kIGBOQWAgcGVuZ3VpbnMgaW4gZWFjaCBzcGVjaWVzLCB3ZSBjYW4gY2hhbmdlIHRoZSBgcG9zaXRpb25gIGFyZ3VtZW50IHRvIGBmaWxsYC4gSW4gdGhhdCBjYXNlLCB0aGUgYmFycyB3aXRoaW4gZWFjaCBsZXZlbCBvZiBgc3BlY2llc2Agd2lsbCBiZSBzY2FsZWQgdG8gb25lLCBkaXJlY3RseSBmYWNpbGl0YXRpbmcgY29tcGFyaXNvbnMgYmV0d2VlbiB0aGUgY29sb3JzIHdpdGhpbiBlYWNoIGJhci4NCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgIGdlb21fYmFyKGFlcyh4ID0gc3BlY2llcywgZmlsbCA9IHNleCksIHBvc2l0aW9uID0gImZpbGwiKQ0KYGBgDQoNCkkgc3RpbGwgY29uc2lkZXIgdGhpcyBhIHBvb3IgZGlzcGxheSBvZiB0aGUgZGF0YSwgYnV0IHdlIGNhbiBzZWUgdGhhdCB0aGUgcHJvcG9ydGlvbiBvZiBtYWxlcyBpcyBjbG9zZSB0byA1MCUgZm9yIGFsbCBzcGVjaWVzLg0KDQpMYXN0bHksIGlmIHdlIGRvbid0IHdhbnQgdG8gYHN0YWNrYCBvciBgZmlsbGAgdGhlIGJhcnMsIHdlIGNhbiBoYXZlIHRoZW0gYGRvZGdlYCBlYWNoIG90aGVyLiBJbiB0aGF0IGNhc2UsIHRoZSBvdmVybGFwcGluZyBiYXJzIHdpbGwgImRvZGdlIiBlYWNoIG90aGVyIGFuZCBzaXQgbmV4dCB0byBlYWNoIG90aGVyIGluIHRoZSBwbG90Lg0KDQpgYGB7cn0NCmdncGxvdChwZW5ndWlucykgKyAgZ2VvbV9iYXIoYWVzKHggPSBzcGVjaWVzLCBmaWxsID0gc2V4KSwgcG9zaXRpb24gPSAiZG9kZ2UiKQ0KYGBgDQoNCklmIHlvdSB3YW50IHRvIGNvbXBhcmUgdGhlIG51bWJlciBvZiBwZW5ndWlucyB3aXRoIGVhY2ggYHNleGAgd2l0aGluIGVhY2ggYHNwZWNpZXNgLCB0aGlzIGdyYXBoaWMgaXNuJ3QgdG9vIGJhZC4gSG93ZXZlciwgdGhlIGJhcnMgcmVwcmVzZW50aW5nIG1hbGUgcGVuZ3VpbnMgKG9yIGFsdGVybmF0aXZlbHksIGZlbWFsZSBwZW5ndWlucykgYXJlIHNwcmVhZCBvdXQgYW5kIG1vcmUgZGlmZmljdWx0IHRvIGNvbXBhcmUgZGlyZWN0bHkgYmVjYXVzZSBvZiB0aGUgb3RoZXIgYmFycyBiZXR3ZWVuIHRoZW0uDQoNCldoaWxlIHRoZSBhYm92ZSBjaGFydHMgcHJvdmlkZSBhIGZhaXIgYW1vdW50IG9mIGluZm9ybWF0aW9uIGNvbXBhY3RseSwgdG8gZmFjaWxpdGF0ZSBlYXNlIG9mIGludGVycHJldGF0aW9uIHdlIHNob3VsZCBjb25zdHJ1Y3Qgb3VyIGdyYXBoaWNzIHRvIGhpZ2hsaWdodCB0aGUgY2hhcmFjdGVyaXN0aWMgb2YgaW1wb3J0YW5jZS4NCg0KZS5nLiwgSWYgd2Ugd2FudCB0byBjb21wYXJlIHRoZSBjb3VudHMgb2YgZWFjaCBwZW5ndWluIGBzcGVjaWVzYCB3aXRoaW4gZWFjaCBgc2V4YCwgaXQgd291bGQgYmUgYmV0dGVyIHRvIGZhY2V0IHRoZSBkYXRhIGJ5IGBzZXhgIGFuZCBkaXNwbGF5IHRoZSBjb3VudCBvZiBlYWNoIHNwZWNpZXMuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArICBnZW9tX2JhcihhZXMoeCA9IHNwZWNpZXMsIGZpbGwgPSBzcGVjaWVzKSkgKyBmYWNldF93cmFwKH4gc2V4KQ0KYGBgDQoNCkFsdGVybmF0aXZlbHksIGlmIHdlIHdhbnQgdG8gaGlnaGxpZ2h0IHRoZSBjb21wb3NpdGlvbiBvZiBgc2V4YCB3aXRoaW4gZWFjaCBgc3BlY2llc2AsIHRoZW4gSSByZWNvbW1lbmQgY3JlYXRpbmcgYSBiYXIgY2hhcnQgb2YgYHNleGAgd2hpbGUgZmFjZXR0aW5nIGJ5IGBzcGVjaWVzYC4gVGhpcyBwbG90IGlzIHF1aXRlIHNpbWlsYXIgdG8gdGhlICJkb2RnZSIgcGxvdCBhYm92ZSwgYnV0IHByb3ZpZGVzIGZ1cnRoZXIgZGlzdGluY3Rpb24gYmV0d2VlbiB0aGUgY2hhcnRzIGZvciBlYWNoIHNwZWNpZXMuDQoNCmBgYHtyfQ0KZ2dwbG90KHBlbmd1aW5zKSArICBnZW9tX2JhcihhZXMoeCA9IHNleCwgZmlsbCA9IHNleCkpICsgZmFjZXRfd3JhcCh+IHNwZWNpZXMpDQpgYGANCg0KVGhlIGV4YW1wbGVzIGFib3ZlIGFyZSBzaG93biBmb3IgZmFjdG9yIHZhcmlhYmxlcyB3aXRoIG9ubHkgdGhyZWUgbGV2ZWxzIG9mIGVhY2ggZmFjdG9yLCBhbmQgeW91IG1heSB0aGluayBJJ20gb3ZlcnJlYWN0aW5nIChhbmQgcGVyaGFwcyBJIGFtKS4gSG93ZXZlciwgdGhlIGlzc3VlcyBtZW50aW9uZWQgYWJvdmUgYXJlIG1hZ25pZmllZCB3aGVuIG91ciBgZmFjdG9yYCB2YXJpYWJsZXMgaGF2ZSBtYW55IGxldmVscy4gVG8gaWxsdXN0cmF0ZSB0aGlzIHBvaW50LCBJIHByZXNlbnQgInN0YWNrIiBhbmQgImRvZGdlIiBiYXIgY2hhcnRzIG9mIHRoZSBgZGlhbW9uZHNgIGRhdGEgaW4gdGhlICoqZ2dwbG90MioqIHBhY2thZ2UsIGluIHdoaWNoIHdlIGNvbXBhcmUgdGhlIGBjdXRgIGFuZCBgY2xhcml0eWAgb2YgZGlhbW9uZHMuIEVhY2ggdmFyaWFibGUgaGFzIGF0IGxlYXN0IDUgbGV2ZWxzLiBUaGUgbGFyZ2UgbnVtYmVyIG9mIGxldmVscyBmb3IgdGhlIGBjdXRgIGFuZCBgY2xhcml0eWAgdmFyaWFibGVzIG1ha2UgaXQgZGlmZmljdWx0IHRvIHF1aWNrbHkgaW50ZXJwcmV0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdHdvIHZhcmlhYmxlcy4gVGhlICoqcGF0Y2h3b3JrKiogcGFja2FnZSBpcyB1c2VkIHRvIGNvbWJpbmUgdGhlIHR3byBjaGFydHMgaW50byBhIHNpbmdsZSBncmFwaGljLiAoV2UgYWxzbyByb3RhdGUgdGhlIHgtYXhpcyBsYWJlbHMgOTAgZGVncmVlcyBzbyB0aGV5IGRvbid0IG92ZXJsYXAgZWFjaCBvdGhlcikuIEhvdyBlYXN5IGlzIGl0IGZvciB5b3UgdG8gaW50ZXJwcmV0IHRoZSBpbmZvcm1hdGlvbiBiZWxvdz8NCg0KYGBge3J9DQpnZ3Bfc3RhY2sgPC0gZ2dwbG90KGRpYW1vbmRzKSArIGdlb21fYmFyKGFlcyh4ID0gY3V0LCBmaWxsID0gY2xhcml0eSkpICsgZ2d0aXRsZSgic3RhY2siKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpDQpnZ3BfZG9kZ2UgPC0gZ2dwbG90KGRpYW1vbmRzKSArIGdlb21fYmFyKGFlcyh4ID0gY3V0LCBmaWxsID0gY2xhcml0eSksIHBvc2l0aW9uID0gImRvZGdlIikgKyBnZ3RpdGxlKCJkb2RnZSIpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkNCmdncF9zdGFjayArIGdncF9kb2RnZQ0KYGBgDQoNCiMjIyBDb29yZGluYXRlIFN5c3RlbXMgKFZpZGVvOiBbWW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9uYVFQd1p4a2IyTSksIFtQYW5vcHRvXShodHRwczovL3VjZGVudmVyLmhvc3RlZC5wYW5vcHRvLmNvbS9QYW5vcHRvL1BhZ2VzL1ZpZXdlci5hc3B4P2lkPTZlNzk5OTYzLTUwMGMtNGZlNi05ZmExLWFmMGYwMTUxZDFlZSkpDQoNCkEgY29vcmRpbmF0ZSBzeXN0ZW0gaXMgdXNlZCB0byBkZWZpbmUgdGhlIHBvc2l0aW9uIG9mIGNvb3JkaW5hdGVzIHJlbGF0aXZlIHRvIG9uZSBhbm90aGVyLiBCeSBkZWZhdWx0LCAqKmdncGxvdDIqKiB1c2VzIHRoZSBDYXJ0ZXNpYW4gY29vcmRpbmF0ZSBzeXN0ZW0gKFtodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9DYXJ0ZXNpYW5fY29vcmRpbmF0ZV9zeXN0ZW1dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NhcnRlc2lhbl9jb29yZGluYXRlX3N5c3RlbSkpLCB3aGljaCB0cmVhdHMgY29vcmRpbmF0ZXMgYXMgcG9pbnRzIG9uIGEgZmxhdCBwbGFuZSwgd2hpY2ggaXMgaG93IGh1bWFucyB0ZW5kIHRvIHRoaW5rIGFib3V0IGNvb3JkaW5hdGVzIGluIHJlbGF0aW9uc2hpcCB0byBvbmUgYW5vdGhlci4NCg0KT3RoZXIgY29vcmRpbmF0ZXMgc3lzdGVtcyBhcmUgY29tbW9ubHkgdXNlZCBmb3IgbWFwcyBiZWNhdXNlIHBvaW50cyBhcmUgYWN0dWFsbHkgb24gYW4gZWxsaXBzb2lkYWwgb2JqZWN0ICh0aGUgZWFydGghKSwgd2hpY2ggY29tcGxpY2F0ZXMgdGhlaXIgcmVsYXRpb25zaGlwLg0KDQpXZSBkbyBub3QgZGlzY3VzcyBjb29yZGluYXRlIHN5c3RlbXMgaW4gZGV0YWlsIGhlcmUuIFRoZSBvbmx5IGNvb3JkaW5hdGUgc3lzdGVtIEkgd2FudCB0byBtZW50aW9uZWQgaXMgdGhlICJmbGlwcGVkIiBjb29yZGluYXRlIHN5c3RlbSAoYGNvb3JkX2ZsaXBgKSwgd2hpY2ggZmxpcHMgdGhlIHggYW5kIHkgYXhlcy4gVGhpcyBpcyB1c2VmdWwgZm9yIHJvdGF0aW5nIG9iamVjdHMgaW4gYSBwbG90LiBDb25zaWRlciB0aGUgYm94cGxvdHMgYmVsb3cuDQoNCg0KYGBge3J9DQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9ib3hwbG90KGFlcyh4ID0gc3BlY2llcywgeSA9IGJvZHlfbWFzc19nLCBmaWxsID0gc3BlY2llcykpDQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9ib3hwbG90KGFlcyh4ID0gc3BlY2llcywgeSA9IGJvZHlfbWFzc19nLCBmaWxsID0gc3BlY2llcykpICsgY29vcmRfZmxpcCgpDQpgYGANCg0KIyMgUmVmZXJlbmNlcw0KICANCiAgDQogIA==