Structure of
ggplot()
The opening command of any ggplot2
plot is
ggplot()
. This command simply creates a blank canvas upon
which to add layers. It “opens” the way for further layers to be added
with a +
symbol.
Typically, the command ggplot() includes the
data = argument
for the plot. This sets the default data
set to be used for subsequent layers of the plot.
This command will end with a +
after its closing
parentheses. This leaves the command “open”. The ggplot
will only execute/appear when the full command includes a final layer
without a +
at the end.
# This will create a plot that is a blank canvas
ggplot(data = linelist)
Geoms
The above code creates a blank canvas. We need to create geometries
(shapes) from our data (e.g. bar plots, histograms, scatter plots, box
plots).
This is done by adding layers of “geoms” to the initial
ggplot()
command. Many ggplot2
functions
create “geoms”. Each of these functions begins with “geom_”, so we will
refer to them generically as geom_XXXX()
.
There are many geoms in ggplot2 and many others created by fans. View
them at the ggplot2
gallery. Some common geoms
are listed below:
- Histograms -
geom_histogram()
- Bar charts -
geom_bar()
or geom_col()
- Box plots -
geom_boxplot()
- Points (e.g. scatter plots) -
geom_point()
- Line graphs -
geom_line()
or
geom_path()
- Trend lines -
geom_smooth()
We can display one or multiple geoms
in one plot. Each
is added to previous ggplot2
commands with a
+
, and they are plotted sequentially such that later
geoms
are plotted on top of previous ones.
For the complete list of currently available geoms, using the
following command in the R Console:
ls(pattern = '^geom_', env = as.environment('package:ggplot2'))
AI Generated
Desciption of Commonly Used geoms
The generative AI (large language model) can help summarize most
commonly use geoms
in ggplot. The following is a list
geoms
with more details based on the phrase list of
geoms in ggplot through the Microsoft Copilot via Mircosoft
Edge:
Geoms are the geometric objects that define the type and appearance
of the plots created by the ggplot2 package in R. Geoms can be combined
and layered to create complex and customized visualizations of data.
Geoms are specified by using geom_ functions, such as geom_point,
geom_bar, geom_line, etc.
There are many geoms available in ggplot2, each with its own
aesthetics and parameters. Some of the most common and useful geoms
are:
geom_point: This geom draws points on a plot,
and can be used to create
scatterplots, dotplots, bubble charts
, etc. It requires x
and y aesthetics, and can also take
size, shape, colour, fill
and alpha
aesthetics
to control the appearance of the points.
geom_bar and geom_col: These
geoms draw bars on a plot, and can be used to create
bar charts, histograms, lollipop charts
, etc. They require
x and y aesthetics, where y represents the height of the bars. The
difference between geom_bar
and geom_col
is
that geom_bar
uses stat_count
by default,
which counts the number of observations in each x group, while
geom_col
uses stat_identity
by default, which
takes the y values as given. These geoms can also take
size, linetype, colour, fill
and alpha aesthetics to
control the appearance of the bars.
geom_line and geom_path: These
geoms draw lines on a plot, and can be used to create
line charts, time series plots, spaghetti plots
, etc. They
require x and y aesthetics, where x represents the order of the points
along the line. The difference between geom_line
and
geom_path
is that geom_line
sorts the data by
x before plotting, while geom_path
plots the data in the
order they appear in the data frame. These geoms can also take
size, linetype, colour
and alpha
aesthetics to
control the appearance of the lines.
geom_boxplot: This geom draws boxplots on a
plot, and can be used to display the distribution of a numeric variable
across different groups. It requires x and y aesthetics, where x
represents the grouping variable and y represents the numeric variable.
It also uses stat_boxplot by default, which computes the
five-number summary
(minimum, lower quartile, median, upper
quartile and maximum) of each group. This geom can also take
size, linetype, colour, fill
and alpha
aesthetics to control the appearance of the boxplots.
geom_histogram: This geom draws histograms on a
plot, and can be used to display the distribution of a single numeric
variable. It requires x aesthetic, which represents the numeric variable
to be binned. It also uses stat_bin by default, which counts the number
of observations in each bin. This geom can also take
size, linetype, colour, fill
and alpha
aesthetics to control the appearance of the histogram.
geom_density and
geom_density_2d: These geoms draw density plots on a
plot, and can be used to display the distribution of one or two numeric
variables using smooth curves. They require x aesthetic for
one-dimensional density plots or x and y aesthetics for two-dimensional
density plots. They also use stat_density
or
stat_density_2d
by default, which estimate the probability
density function of the variables using kernel smoothing. These geoms
can also take size, linetype, colour, fill
and
alpha
aesthetics to control the appearance of the density
plots.
geom_tile and geom_bin2d: These
geoms draw tiles on a plot, and can be used to create heatmaps or
bivariate histograms of two numeric variables. They require x and y
aesthetics, which represent the numeric variables to be binned. They
also use stat_identity or stat_bin2d by default, which take or compute
the values for each tile. These geoms can also take
size, linetype, colour, fill
and alpha
aesthetics to control the appearance of the tiles.
geom_text and geom_label: These
geoms draw text or labels on a plot, and can be used to add annotations
or data labels to a plot. They require x and y aesthetics for
positioning and label aesthetic for text content. They can also take
size, colour, fill, alpha, hjust, vjust
and
parse
aesthetics to control the appearance of the text or
labels.
Mapping Data to
Plot
geom
functions require mapping (assigning) columns in
the data to components of the plot like the axes, shape colors, shape
sizes, etc. The mappings must be wrapped in the aes()
function, so we would write something like
mapping = aes(x = col1, y = col2)
.
For example, in the following example using iris data
,
Sepal Length is mapped to the x-axis, and Sepal Width is mapped to the
y-axis. After a +, the plotting commands continue. A shape is created
with the “geom” function geom_point().
ggplot(data = iris, mapping = aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point()
When creating a histogram, only one variable is used. See the
following example.
ggplot(data = iris, mapping = aes(x = Petal.Width)) +
geom_histogram(binwidth = 0.2)
Arranging Multiple
Grobs on the Same Page
In the above subsection, we create two graphs on two different pages.
Sometimes, we want to place two more graphs on the same page for
comparison purposes. In base R, we have graphic functions such as
par()
and layout()
to set up a layout for the
graphic page.
In this note, we introduce the library cowplot
to
arrange multiple graphical objects (a.k.a grobs
) on a
page.
## Name the two plots first and then call the two grobs in the layout function
## scatter plot
scatter = ggplot(data = iris, mapping = aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point()
## histogram
hist = ggplot(data = iris, mapping = aes(x = Petal.Width)) +
geom_histogram(binwidth = 0.2)
## use plot_grid() in {cowplot} to layout the two plots
plot_grid(scatter, hist, labels=c("A", "B"), ncol = 2, nrow = 1)
Plot Aesthetics
In ggplot
terminology a plot “aesthetic” has a specific
meaning. It refers to colors, sizes, transparencies, placement, etc. of
the plotted data.
Not all geoms will have the same aesthetic options
, but
many can be used by most geoms
.
Here are some examples:
shape
= Display a point with geom_point()
as a dot
, star
, triangle
, or
square
, etc.
fill
= The interior color (e.g. of a bar or
boxplot)
color
= The exterior line of a bar, boxplot, etc., or
the point color if using geom_point()
size
= Size (e.g. line thickness, point size)
alpha
= Transparency (1 = opaque, 0 = invisible)
binwidth
= Width of histogram bins
width
= Width of “bar plot” columns
linetype
= Line type (e.g. solid
,
dashed
, dotted
)
The aesthetics of plot objects can be assigned values in two
ways:
Assigned a static value (e.g. color = “blue”) to apply across all
plotted observations
Assigned to a column of the data (e.g. color = hospital) such
that the display of each observation depends on its value in that
column
We have already added binwidth to the above histogram. Next, we add
color to the histogram
# Change histogram plot line colors by groups
scatter01 <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width,
color = Species,
size = Petal.Width)) +
geom_point(alpha = 0.5)
# Overlaid histograms
hist01 <- ggplot(iris, aes(x = Petal.Width, color=Species)) +
geom_histogram(fill="navy",
alpha = 0.7,
position = "identity",
binwidth = 0.2)
## use plot_grid() in {cowplot} to lay out the two plots
plot_grid(scatter01, hist01, labels=c("A", "B"), ncol = 2, nrow = 1)
Labels in
ggplot()
Surely you will want to add or adjust the plot’s labels. These are
most easily done within the labs()
function which is added
to the plot with +
just as the geoms
were.
Within labs()
you can provide character strings to these
arguments:
x =
and y =
: The x-axis and y-axis
title (labels)
title =
: The main plot title
subtitle =
: The subtitle of the plot, in smaller
text below the title
caption =
: The caption of the plot, in bottom-right
by default
Here is a plot we made earlier, but with nicer labels:
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width,
color = Species,
size = Petal.Width)) +
geom_point(alpha = 0.5) +
labs(
x = "Sepal Length",
y = "Sepal Width",
# label for legends
size = "Sepal Length:",
color = "Species:",
title = "Association between Sepal Length and Width",
subtitle = "This is a partial scatter plot",
caption = paste("Created on", Sys.Date())) +
theme_minimal() # minimal theme
Themes in
ggplot()
The theme system in ggplot()
does not affect how the
data is rendered by geoms
, or how it is transformed by
scales. Themes
don’t change the perceptual properties of
the plot, but they do help you make the plot aesthetically pleasing or
match an existing style guide. Themes
give us control over
things like fonts, ticks, panel stripes, and backgrounds.
In other words, when creating the plot we determine how the data is
displayed, and then after it has been created we can edit every detail
of the rendering, using the theming system.
Theming System
Structure
The theming system is composed of four main components:
Theme elements specify the non-data elements that we can control.
For example,
plot.title
controls the appearance of the plot
title;
axis.ticks.x
controls the ticks on the x-axis;
legend.key.height
controls the height of the keys in
the legend.
Each element is associated with an element function, which
describes the visual properties of the element. For example,
element_text()
sets the font size, color, and face of text
elements like plot.title
.
The theme()
function which allows you to override
the default theme elements by calling element functions, like
theme(plot.title = element_text(colour = "red"))
.
Complete themes, like theme_grey()
set all of the
theme elements to values designed to work together
harmoniously.
Here are some especially common theme() arguments. You will recognize
some patterns, such as appending .x or .y to apply the change only to
one axis.
To get the complete list of themes, run the following code
#theme_get()
To make sure the plot can stand alone, we need to provide the plot
with axes, legend labels, and title, and tweak the color scale for
appropriate colors.
# adding themes
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width,
color = Species,
size = Petal.Width)) +
geom_point(alpha = 0.5) +
labs(
x = "Sepal Length",
y = "Sepal Width",
# label for legends
size = "Sepal Length:",
color = "Species:",
title = "Association between Sepal Length and Width" ) +
theme_minimal() + # minimal theme
theme( # list of themes applied to the plot
# plot title features
# font family: c("sans", "serif", "mono")
# font face: c("plain", "bold", "italic", "bold.italic")
plot.title = element_text(face = "bold",
size = 12,
family = "sans",
color = "darkred",
hjust = 0.5), # left(0),right(1)
# Labels of axes
axis.title.x = element_text(color = "red",
face = "italic",
family = "serif",
hjust = 0.5),
axis.title.y = element_text(color = "blue",
face = "bold",
vjust = 0.5),
axis.ticks = element_line(color = "red",
size = 0.5),
axis.line = element_line(color = "darkblue",
size = 1,
linetype = "solid"),
# Axis tick marks
axis.text.x = element_text(face="plain",
color="purple",
size=11,
angle=45),
axis.text.y = element_text(face="plain",
color="orange",
size=11,
angle=90),
# Features of legend
legend.background = element_rect(fill = "white",
size = 0.1,
color = "darkgreen"),
legend.justification = c(0.9, 0.8),
legend.position = "bottom",
## Panel grid
panel.grid.major = element_line(color = "lightblue",
size = 0.1),
panel.grid.minor = element_blank()
)
Complete Components
of Theme
Themes are a powerful way to customize the
non-data components
of the plots: i.e. titles, labels,
fonts, background, gridlines, and legends. To give our plots a
consistent customized look, we can define a theme function and call the
theme function in any ggplots
.
The tidyverse
official website provides a comprehensive
document on theme components in `ggplot``. https://ggplot2.tidyverse.org/reference/theme.html.
Numerous examples have illustrated how to use various theme
components.
We can define a theme function that can be reused to customize the
plots. For example, we define the following theme and use it in
different plots.
myplot.theme <- function() {
theme(
plot.title = element_text(face = "bold",
size = 12,
family = "sans",
color = "darkred",
hjust = 0.5), # left(0),right(1)
# add border 1)
panel.border = element_rect(colour = "blue",
fill = NA,
linetype = 2),
# color background 2)
panel.background = element_rect(fill = "aliceblue"),
# modify grid 3)
panel.grid.major.x = element_line(colour = "steelblue",
linetype = 3,
size = 0.5),
panel.grid.minor.x = element_blank(),
panel.grid.major.y = element_line(colour = "steelblue",
linetype = 3,
size = 0.5),
panel.grid.minor.y = element_blank(),
# modify text, axis and colour 4) and 5)
axis.text = element_text(colour = "steelblue",
face = "italic",
family = "Times New Roman"),
axis.title = element_text(colour = "steelblue",
family = "Times New Roman"),
axis.ticks = element_line(colour = "steelblue"),
# legend at the bottom 6)
legend.position = "bottom",
legend.key.size = unit(0.6, 'cm'), #change legend key size
legend.key.height = unit(0.6, 'cm'), #change legend key height
legend.key.width = unit(0.6, 'cm'), #change legend key width
legend.title = element_text(size=8), #change legend title font size
legend.text = element_text(size=8)) #change legend text font size
}
Now we use the above theme in the following scatter plots. Instead of
using the colors based on the value of species, we manually select
colors to encode the values of species. The following URL links to a PDF
document with colors in R. http://www.stat.columbia.edu/~tzheng/files/Rcolor.pdf
# Change histogram plot line colors by groups
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width,
color = factor(Species),
size = Petal.Width)) +
geom_point(alpha = 0.5) +
scale_color_manual(values=c("dodgerblue4", "darkolivegreen4","darkorchid3")) +
labs(
x = "Sepal Length",
y = "Sepal Width",
## Color and size of labels
size = "Sepal Length:",
color = "Species:",
title = "Association between Sepal Length and Width") +
myplot.theme()
Next, we plot a histogram using the same theme.
ggplot(iris, aes(x = Petal.Width, color=Species)) +
geom_histogram(fill="navy",
alpha = 0.3,
position = "identity",
binwidth = 0.2) +
scale_color_manual(values=c("dodgerblue4", "darkolivegreen4",
"darkorchid3")) +
labs(
x = "Petal Width",
color = "Species:",
title = "Distribution of Petal Width") +
myplot.theme()
Removing Chart Junks
Via Themes
We remove some unnecessary marks and channels from the chart via
theme: change the background color, grid, and plot title. Essentially,
we use a them to lay out a ggplot style without any chart junk.
myplot.theme_new <- function() {
theme(
#ggplot margins
plot.margin = margin(t = 50, # Top margin
r = 30, # Right margin
b = 30, # Bottom margin
l = 30), # Left margin
## ggplot titles
plot.title = element_text(face = "bold",
size = 12,
family = "sans",
color = "navy",
hjust = 0.5,
margin=margin(0,0,30,0)), # left(0),right(1)
# add border 1)
panel.border = element_rect(colour = NA,
fill = NA,
linetype = 2),
# color background 2)
panel.background = element_rect(fill = "#f6f6f6"),
# modify grid 3)
panel.grid.major.x = element_line(colour = 'white',
linetype = 3,
size = 0.5),
panel.grid.minor.x = element_blank(),
panel.grid.major.y = element_line(colour = 'white',
linetype = 3,
size = 0.5),
panel.grid.minor.y = element_blank(),
# modify text, axis and colour 4) and 5)
axis.text = element_text(colour = "navy",
#face = "italic",
size = 7,
#family = "Times New Roman"
),
axis.title = element_text(colour = "navy",
size = 7,
#family = "Times New Roman"
),
axis.ticks = element_line(colour = "navy"),
# legend at the bottom 6)
legend.position = "bottom",
legend.key.size = unit(0.6, 'cm'), #change legend key size
legend.key.height = unit(0.6, 'cm'), #change legend key height
legend.key.width = unit(0.6, 'cm'), #change legend key width
#legend.title = element_text(size=8), #change legend title font size
legend.title=element_blank(), # remove all legend titles
legend.key = element_rect(fill = "white"),
#####
legend.text = element_text(size=8)) #change legend text font size
}
Using the above theme, we re-plot the iris data with less irrelevant
graphical elements.
# Change histogram plot line colors by groups
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width,
color = factor(Species)), linetype = Species) +
geom_point(size = 2, alpha = 0.7) +
stat_smooth(method = lm, se=FALSE, size = 0.3) +
scale_color_manual(values=c("dodgerblue4", "darkolivegreen4", "darkorchid3")) +
labs(
x = "Sepal Length",
y = "Sepal Width",
## labels of color and size
#size = "Sepal Length",
#color = NA,
title = "Association between Sepal Length and Width") +
myplot.theme_new() +
annotate(geom="text" ,
x=6.8,
y=2,
label=paste("The Pearson correlation coefficient r = ",
round(cor(iris$Sepal.Length, iris$Sepal.Width),3)),
size = 2,
color = "navy") +
coord_fixed(1) ## This changes the aspect ratio of the graph
The Role of Color in
Effective Visualization
Substantial research shows that color plays a pivotal role in our
visual experiences. In this class, we use colors for two main purposes:
encoding and highlighting information.
We use different colors to denote different values of a variable.
There are many continuous and discrete color palettes available in
different R libraries that can be used on different occasions. However,
we need to pay very special attention to the cases when colors are used
for encoding because we see colors differently - people with vision
deficiency are less sensitive to some of the colors. The next figure
illustrates the major types of color blindness.
include_graphics("ColorBlindnessTypes.png")
What colors should you use? There are different color-picker tools
available for us to choose colors while making sure that the color
palette is accessible. Here is a set of 8 pairs of contrasting colors
that maintain their contrast for people who are colorblind.
include_graphics("r-color-palettes.png")
For example, the following three sample palettes are
colorblind-friendly.
par(mfrow=c(1,5), oma=c(0,0,0,0), mar = c(1,0.5,1,0.5))
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#648FFF")
text(0,0, "#648FFF")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#785EF0")
text(0,0, "#785EF0")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#DC267F")
text(0,0, "#DC267F")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#FE6100")
text(0,0, "#FE6100")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#FFB000")
text(0,0, "#FFB000")
par(mfrow=c(1,8), oma=c(0,0,0,0), mar = c(1,0.5,1,0.5))
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#000000")
text(0,0, "#000000", col = "white")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#E69F00")
text(0,0, "#E69F00")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#56B4E9")
text(0,0, "#56B4E9")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#009E73")
text(0,0, "#009E73")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#F0E442")
text(0,0, "#F0E442")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#0072B2")
text(0,0, "#0072B2")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#D55E00")
text(0,0, "#D55E00")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#CC79A7")
text(0,0, "#CC79A7")
par(mfrow=c(1,8), oma=c(0,0,0,0), mar = c(1,0.5,1,0.5))
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#332288")
text(0,0, "#332288", col = "white")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#117733")
text(0,0, "#117733")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#44AA99")
text(0,0, "#44AA99")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#88CCEE")
text(0,0, "#88CCEE")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#DDCC77")
text(0,0, "#DDCC77")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#CC6677")
text(0,0, "#CC6677")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#AA4499")
text(0,0, "#AA4499")
##
plot(NULL, type="n", xlim=c(-1,1), ylim=c(-1,1), axes = FALSE, xlab = "", ylab = "")
rect(xleft = -1, ybottom = -0.5, xright =1, ytop = 0.5, lty = 1, col = "#882255")
text(0,0, "#882255", col = "white")
We can find more R color palettes from https://r-charts.com/color-palettes/.
As an example, we use the above color blind friendly color scheme and
draw various density curves.
iris0 = iris
Type = c(paste(iris$Species,".Sepal.Length", sep = ""),paste(iris$Species,".Sepal.Width", sep = ""))
Measure = c(iris$Sepal.Length ,iris$Sepal.Width)
irisNew = data.frame(Type = Type, Measure = Measure)
cols1 = c("#332288","#117733","#44AA99","#88CCEE","#DDCC77","#CC6677")
cols3 = c("#AA4499","#882255")
p = ggplot() +
geom_density(data = irisNew, aes(x = Measure, color = Type), lwd = 1) +
scale_color_manual(values = cols1) +
ggtitle("Multiple Density Curves") +
theme(plot.title = element_text(hjust = 1,
face = "bold",
color = "navy"))
p
Some people like filled density curves. alpha
is a
function in the library of ggplot2
.
p1 = ggplot(data = irisNew, aes(x = Measure, color = Type, fill = Type)) +
geom_density(alpha = 0.25, lwd = 1.5) +
scale_fill_manual(values = cols1) +
ggtitle("Multiple Filled Density Curves") +
theme(plot.title = element_text(hjust = 1,
face = "bold",
color = "purple"))
p1
LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byBnZ3Bsb3QoKSINCmF1dGhvcjogIkNoZW5nIFBlbmciDQpkYXRlOiAiV2VzdCBDaGVzdGVyIFVuaXZlcnNpdHkgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIHRvY19mbG9hdDogeWVzDQogICAgZmlnX3dpZHRoOiA2DQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlDQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgZmlnX2hlaWdodDogNA0KLS0tDQoNCmBgYHs9aHRtbH0NCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCmRpdiNUT0MgbGkgew0KICAgIGxpc3Qtc3R5bGU6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLWNvbG9yOmxpZ2h0Z3JheTsNCiAgICBiYWNrZ3JvdW5kLWltYWdlOm5vbmU7DQogICAgYmFja2dyb3VuZC1yZXBlYXQ6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOjA7DQogICAgZm9udC1mYW1pbHk6IEFyaWFsLCBIZWx2ZXRpY2EsIHNhbnMtc2VyaWY7DQogICAgY29sb3I6ICM3ODBjMGM7DQp9DQoNCi8qIG1vdXNlIG92ZXIgbGluayAqLw0KZGl2I1RPQyBhOmhvdmVyIHsNCiAgY29sb3I6IHJlZDsNCn0NCg0KLyogdW52aXNpdGVkIGxpbmsgKi8NCmRpdiNUT0MgYTpsaW5rIHsNCiAgY29sb3I6IGJsdWU7DQp9DQoNCg0KDQpoMS50aXRsZSB7DQogIGZvbnQtc2l6ZTogMjRweDsNCiAgY29sb3I6IERhcmtibHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiBBcmlhbCwgSGVsdmV0aWNhLCBzYW5zLXNlcmlmOw0KICBmb250LXZhcmlhbnQtY2Fwczogbm9ybWFsOw0KfQ0KaDQuYXV0aG9yIHsgDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmg0LmRhdGUgeyANCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoMSB7DQogICAgZm9udC1zaXplOiAyNHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmgyIHsNCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDMgeyANCiAgICBmb250LXNpemU6IDE1cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQovKiB1bnZpc2l0ZWQgbGluayAqLw0KYTpsaW5rIHsNCiAgY29sb3I6IGdyZWVuOw0KfQ0KDQovKiB2aXNpdGVkIGxpbmsgKi8NCmE6dmlzaXRlZCB7DQogIGNvbG9yOiBncmVlbjsNCn0NCg0KLyogbW91c2Ugb3ZlciBsaW5rICovDQphOmhvdmVyIHsNCiAgY29sb3I6IHJlZDsNCn0NCg0KLyogc2VsZWN0ZWQgbGluayAqLw0KYTphY3RpdmUgew0KICBjb2xvcjogeWVsbG93Ow0KfQ0KDQo8L3N0eWxlPg0KYGBgDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCANCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLg0Kb3B0aW9ucyhyZXBvcyA9IGxpc3QoQ1JBTj0iaHR0cDovL2NyYW4ucnN0dWRpby5jb20vIikpDQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KICAgbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoImNvd3Bsb3QiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiY293cGxvdCIpDQogICBsaWJyYXJ5KGNvd3Bsb3QpDQp9DQppZiAoIXJlcXVpcmUoImxhdGV4MmV4cCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJsYXRleDJleHAiKQ0KICAgbGlicmFyeShsYXRleDJleHApDQp9DQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KICAgbGlicmFyeShwbG90bHkpDQp9DQppZiAoIXJlcXVpcmUoImdhcG1pbmRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJnYXBtaW5kZXIiKQ0KICAgbGlicmFyeShnYXBtaW5kZXIpDQp9DQppZiAoIXJlcXVpcmUoInBuZyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicG5nIikgICAgICAgICAgICAgIyBJbnN0YWxsIHBuZyBwYWNrYWdlDQogICAgbGlicmFyeSgicG5nIikNCn0NCmlmICghcmVxdWlyZSgiUkN1cmwiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIlJDdXJsIikgICAgICAgICAgICMgSW5zdGFsbCBSQ3VybCBwYWNrYWdlDQogICAgbGlicmFyeSgiUkN1cmwiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJjb2xvdXJwaWNrZXIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImNvbG91cnBpY2tlciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJjb2xvdXJwaWNrZXIiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnaWZza2kiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdpZnNraSIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnaWZza2kiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJtYWdpY2siKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIm1hZ2ljayIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJtYWdpY2siKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnckRldmljZXMiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdyRGV2aWNlcyIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnckRldmljZXMiKQ0KfQ0KIyMjIGdncGxvdCBhbmQgZXh0ZW5zaW9ucw0KaWYgKCFyZXF1aXJlKCJnZ3Bsb3QyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdncGxvdDIiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnZ2FuaW1hdGUiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdnYW5pbWF0ZSIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnZ2FuaW1hdGUiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnZ3JpZGdlcyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dyaWRnZXMiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ2dyaWRnZXMiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJncmFwaGljcyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ3JhcGhpY3MiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ3JhcGhpY3MiKQ0KfQ0KDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgICANCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSBUUlVFLCAgIA0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkEpDQpgYGANCg0KXA0KDQojIEludHJvZHVjdGlvbg0KDQpUaGUgZGF0YSBwcm9wZXJ0aWVzIGFyZSB0eXBpY2FsbHkgbnVtZXJpY2FsIG9yIGNhdGVnb3JpY2FsIHZhbHVlcywgd2hpbGUgdGhlIHZpc3VhbCBwcm9wZXJ0aWVzIGluY2x1ZGUgdGhlIHggYW5kIHkgcG9zaXRpb25zIG9mIHBvaW50cywgY29sb3JzIG9mIGxpbmVzLCBoZWlnaHRzIG9mIGJhcnMsIGFuZCBzbyBvbi4gVGhlIHByb2Nlc3Mgb2YgY3JlYXRpbmcgYSBkYXRhIHZpc3VhbGl6YXRpb24gaXMgdG8gbWFwIHRoZSBkYXRhIHByb3BlcnRpZXMgdG8gdmlzdWFsIHByb3BlcnRpZXMuDQoNCkluIFIncyBiYXNlIGdyYXBoaWNzIGZ1bmN0aW9ucywgZWFjaCBtYXBwaW5nIG9mIGRhdGEgcHJvcGVydGllcyB0byB2aXN1YWwgcHJvcGVydGllcyBpcyBpdHMgc3BlY2lhbCBjYXNlLiBDaGFuZ2luZyB0aGUgbWFwcGluZ3MgaW4gdGhlIGJhc2UgUiBncmFwaGljcyBtYXkgcmVxdWlyZSByZXN0cnVjdHVyaW5nIHRoZSBkYXRhIHV0aWxpemluZyBjb21wbGV0ZWx5IGRpZmZlcmVudCBwbG90dGluZyBjb21tYW5kcywgb3IgYm90aC4NCg0KT24gdGhlIG90aGVyIGhhbmQsIGBnZ3Bsb3QyYCBpcyBhIHN5c3RlbSBmb3IgZGVjbGFyYXRpdmVseSBjcmVhdGluZyBncmFwaGljcywgYmFzZWQgb24gVGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MuIFdlIHByb3ZpZGUgdGhlIGRhdGEsIGFuZCB0ZWxsIGdncGxvdDIgaG93IHRvIG1hcCB2YXJpYWJsZXMgdG8gYWVzdGhldGljcyBhbmQgd2hhdCBncmFwaGljYWwgcHJpbWl0aXZlcyB0byB1c2UsIGBnZ3Bsb3QoKWAgdGFrZXMgY2FyZSBvZiB0aGUgZGV0YWlscy4NCg0KVGhlIGdyYXBoaWMgZnVuY3Rpb25zIGluIGJhc2UgUiBhcmUgcG93ZXJmdWwsIGJ1dCBpbiBnZW5lcmFsLCBpdCBpcyBiZWxpZXZlZCB0aGF0IGBnZ3Bsb3QoKWAgaXMgYmV0dGVyLg0KDQpGb3IgdGhvc2Ugd2hvIHByb2dyYW0gaW4gUHl0aG9uLCBJdCBpcyBnb29kIHRvIGtub3cgdGhhdCBgcGxvdG5pbmVgIGlzIGFuIGltcGxlbWVudGF0aW9uIG9mIGEgZ3JhbW1hciBvZiBncmFwaGljcyBpbiAqKlB5dGhvbioqLCBpdCBpcyBiYXNlZCBvbiBgZ2dwbG90MigpYC4NCg0KRm9yIHRob3NlIHdobyBwcm9ncmFtIGluIFNBUywgdGhlIFNBUyBPRFMgZ3JhcGhpY3MgYXJlIHJvdWdobHkgYW5hbG9nb3VzIHRvIFIncyBgZ2dwbG90KClgIGFsdGhvdWdoIGl0IGlzIG5vdCBhIGRpcmVjdCBpbXBsZW1lbnRhdGlvbiBvZiBUaGUgR3JhbW1hciBvZiBHcmFwaGljcy4NCg0KIyBCYXNpY3Mgb2YgYGdncGxvdCgpYA0KDQpQbG90dGluZyB3aXRoIGBnZ3Bsb3QyYCBpcyBiYXNlZCBvbiAiYWRkaW5nIiBwbG90IGxheWVycyBhbmQgZGVzaWduIGVsZW1lbnRzIG9uIHRvcCBvZiBvbmUgYW5vdGhlciwgd2l0aCBlYWNoIGNvbW1hbmQgYWRkZWQgdG8gdGhlIHByZXZpb3VzIG9uZXMgd2l0aCBhIHBsdXMgc3ltYm9sIChgK2ApLiBUaGUgcmVzdWx0IGlzIGEgbXVsdGktbGF5ZXIgcGxvdCBvYmplY3QgdGhhdCBjYW4gYmUgc2F2ZWQsIG1vZGlmaWVkLCBwcmludGVkLCBleHBvcnRlZCwgZXRjLg0KDQpgZ2dwbG90KClgIG9iamVjdHMgY2FuIGJlIGhpZ2hseSBjb21wbGV4LCBidXQgdGhlIGJhc2ljIG9yZGVyIG9mIGxheWVycyB3aWxsIHVzdWFsbHkgbG9vayBsaWtlIHRoaXM6DQoNCjEuICBCZWdpbiB3aXRoIHRoZSBiYXNlbGluZSBgZ2dwbG90KClgIGNvbW1hbmQgLSB0aGlzICJvcGVucyIgdGhlIGdncGxvdCBhbmQgYWxsb3dzIHN1YnNlcXVlbnQgZnVuY3Rpb25zIHRvIGJlIGFkZGVkIHdpdGggYCtgLiBUeXBpY2FsbHkgdGhlIGRhdGEgc2V0IGlzIGFsc28gc3BlY2lmaWVkIGluIHRoaXMgY29tbWFuZA0KDQoyLiAgQWRkIGDigJxnZW9t4oCdYCBsYXllcnMgLSB0aGVzZSBmdW5jdGlvbnMgdmlzdWFsaXplIHRoZSBkYXRhIGFzIGdlb21ldHJpZXMgKHNoYXBlcyksIGUuZy4gYXMgYSBiYXIgZ3JhcGgsIGxpbmUgcGxvdCwgc2NhdHRlciBwbG90LCBoaXN0b2dyYW0gKG9yIGEgY29tYmluYXRpb24hKS4gVGhlc2UgZnVuY3Rpb25zIGFsbCBzdGFydCB3aXRoIGBnZW9tX2AgYXMgYSBwcmVmaXguDQoNCjMuICBBZGQgZGVzaWduIGVsZW1lbnRzIHRvIHRoZSBwbG90IHN1Y2ggYXMgYXhpcyBsYWJlbHMsIHRpdGxlcywgZm9udHMsIHNpemVzLCBjb2xvciBzY2hlbWVzLCBsZWdlbmRzLCBvciBheGVzIHJvdGF0aW9uDQoNCldlIGNhbiBjaGVjayB0aGUgdGlkeXZlcnNlIHJlZmVyZW5jZSBzaXRlIGZvciBtb3JlIGRldGFpbHMgYXQgPGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9pbmRleC5odG0+DQoNCkEgc2ltcGxlIGV4YW1wbGUgb2Ygc2tlbGV0b24gY29kZSBpcyBhcyBmb2xsb3dzLiBXZSB3aWxsIGV4cGxhaW4gZWFjaCBjb21wb25lbnQgaW4gdGhlIGNvZGUgYmVsb3cuDQoNCmBgYCAgICAgICAgIA0KIyBQbG90IGRhdGEgZnJvbSBteSBkYXRhIGNvbHVtbnMgYXMgcmVkIHBvaW50cw0KZ2dwbG90KGRhdGEgPSBteV9kYXRhKSAgKyAgICAgICAgICAgICAgICMgdXNlIHRoZSBkYXRhc2V0ICJteV9kYXRhIg0KICBnZW9tX3BvaW50KCAgICAgICAgICAgICAgICAgICAgICAgICAgICMgYWRkIGEgbGF5ZXIgb2YgcG9pbnRzIChkb3RzKQ0KICAgIG1hcHBpbmcgPSBhZXMoeCA9IGNvbDEsIHkgPSBjb2wyKSwgICMgIm1hcCIgZGF0YSBjb2x1bW4gdG8gYXhlcw0KICAgIGNvbG9yID0gInJlZCIpICArICAgICAgICAgICAgICAgICAgICMgT3RoZXIgc3BlY2lmaWNhdGlvbnMgZm9yIHRoZSBnZW9tDQogIGxhYnMoKSAgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBoZXJlIHlvdSBhZGQgdGl0bGVzLCBheGVzIGxhYmVscywgZXRjLg0KICB0aGVtZSgpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaGVyZSB5b3UgYWRqdXN0IGNvbG9yLCBmb250LCBzaXplIGV0YyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBvZiBub24tZGF0YSBwbG90IGVsZW1lbnRzIChheGVzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRpdGxlLCBldGMuKSANCmBgYA0KDQpJbiB0aGUgZm9sbG93aW5nIHNlY3Rpb25zLCB3ZSB3aWxsIGRldGFpbCBlYWNoIG9mIHRoZSBjb21wb25lbnRzIGluIHRoZSBhYm92ZSBjb2RlLg0KDQojIFN0cnVjdHVyZSBvZiBgZ2dwbG90KClgDQoNClRoZSBvcGVuaW5nIGNvbW1hbmQgb2YgYW55IGBnZ3Bsb3QyYCBwbG90IGlzIGBnZ3Bsb3QoKWAuIFRoaXMgY29tbWFuZCBzaW1wbHkgY3JlYXRlcyBhIGJsYW5rIGNhbnZhcyB1cG9uIHdoaWNoIHRvIGFkZCBsYXllcnMuIEl0ICJvcGVucyIgdGhlIHdheSBmb3IgZnVydGhlciBsYXllcnMgdG8gYmUgYWRkZWQgd2l0aCBhIGArYCBzeW1ib2wuDQoNClR5cGljYWxseSwgdGhlIGNvbW1hbmQgZ2dwbG90KCkgaW5jbHVkZXMgdGhlIGBkYXRhID0gYXJndW1lbnRgIGZvciB0aGUgcGxvdC4gVGhpcyBzZXRzIHRoZSBkZWZhdWx0IGRhdGEgc2V0IHRvIGJlIHVzZWQgZm9yIHN1YnNlcXVlbnQgbGF5ZXJzIG9mIHRoZSBwbG90Lg0KDQpUaGlzIGNvbW1hbmQgd2lsbCBlbmQgd2l0aCBhIGArYCBhZnRlciBpdHMgY2xvc2luZyBwYXJlbnRoZXNlcy4gVGhpcyBsZWF2ZXMgdGhlIGNvbW1hbmQgIm9wZW4iLiBUaGUgYGdncGxvdGAgd2lsbCBvbmx5IGV4ZWN1dGUvYXBwZWFyIHdoZW4gdGhlIGZ1bGwgY29tbWFuZCBpbmNsdWRlcyBhIGZpbmFsIGxheWVyICoqd2l0aG91dCoqIGEgYCtgIGF0IHRoZSBlbmQuDQoNCmBgYCAgICAgICAgIA0KIyBUaGlzIHdpbGwgY3JlYXRlIGEgcGxvdCB0aGF0IGlzIGEgYmxhbmsgY2FudmFzDQpnZ3Bsb3QoZGF0YSA9IGxpbmVsaXN0KQ0KYGBgDQoNCiMjIEdlb21zDQoNClRoZSBhYm92ZSBjb2RlIGNyZWF0ZXMgYSBibGFuayBjYW52YXMuIFdlIG5lZWQgdG8gY3JlYXRlIGdlb21ldHJpZXMgKHNoYXBlcykgZnJvbSBvdXIgZGF0YSAoZS5nLiBiYXIgcGxvdHMsIGhpc3RvZ3JhbXMsIHNjYXR0ZXIgcGxvdHMsIGJveCBwbG90cykuDQoNClRoaXMgaXMgZG9uZSBieSBhZGRpbmcgbGF5ZXJzIG9mICJnZW9tcyIgdG8gdGhlIGluaXRpYWwgYGdncGxvdCgpYCBjb21tYW5kLiBNYW55IGBnZ3Bsb3QyYCBmdW5jdGlvbnMgY3JlYXRlICJnZW9tcyIuIEVhY2ggb2YgdGhlc2UgZnVuY3Rpb25zIGJlZ2lucyB3aXRoICJnZW9tXF8iLCBzbyB3ZSB3aWxsIHJlZmVyIHRvIHRoZW0gZ2VuZXJpY2FsbHkgYXMgYGdlb21fWFhYWCgpYC4NCg0KVGhlcmUgYXJlIG1hbnkgZ2VvbXMgaW4gZ2dwbG90MiBhbmQgbWFueSBvdGhlcnMgY3JlYXRlZCBieSBmYW5zLiBWaWV3IHRoZW0gYXQgdGhlIGBnZ3Bsb3QyYCBnYWxsZXJ5LiBTb21lIGNvbW1vbiBgZ2VvbXNgIGFyZSBsaXN0ZWQgYmVsb3c6DQoNCi0gICBIaXN0b2dyYW1zIC0gYGdlb21faGlzdG9ncmFtKClgDQotICAgQmFyIGNoYXJ0cyAtIGBnZW9tX2JhcigpYCBvciBgZ2VvbV9jb2woKWANCi0gICBCb3ggcGxvdHMgLSBgZ2VvbV9ib3hwbG90KClgDQotICAgUG9pbnRzIChlLmcuIHNjYXR0ZXIgcGxvdHMpIC0gYGdlb21fcG9pbnQoKWANCi0gICBMaW5lIGdyYXBocyAtIGBnZW9tX2xpbmUoKWAgb3IgYGdlb21fcGF0aCgpYA0KLSAgIFRyZW5kIGxpbmVzIC0gYGdlb21fc21vb3RoKClgDQoNCldlIGNhbiBkaXNwbGF5IG9uZSBvciBtdWx0aXBsZSBgZ2VvbXNgIGluIG9uZSBwbG90LiBFYWNoIGlzIGFkZGVkIHRvIHByZXZpb3VzIGBnZ3Bsb3QyYCBjb21tYW5kcyB3aXRoIGEgYCtgLCBhbmQgdGhleSBhcmUgcGxvdHRlZCBzZXF1ZW50aWFsbHkgc3VjaCB0aGF0IGxhdGVyIGBnZW9tc2AgYXJlIHBsb3R0ZWQgb24gdG9wIG9mIHByZXZpb3VzIG9uZXMuDQoNCkZvciB0aGUgY29tcGxldGUgbGlzdCBvZiBjdXJyZW50bHkgYXZhaWxhYmxlIGdlb21zLCB1c2luZyB0aGUgZm9sbG93aW5nIGNvbW1hbmQgaW4gdGhlIFIgQ29uc29sZToNCg0KYGBgICAgICAgICAgDQpscyhwYXR0ZXJuID0gJ15nZW9tXycsIGVudiA9IGFzLmVudmlyb25tZW50KCdwYWNrYWdlOmdncGxvdDInKSkNCmBgYA0KDQojIyBBSSBHZW5lcmF0ZWQgRGVzY2lwdGlvbiBvZiBDb21tb25seSBVc2VkIGBnZW9tc2ANCg0KVGhlIGdlbmVyYXRpdmUgQUkgKGxhcmdlIGxhbmd1YWdlIG1vZGVsKSBjYW4gaGVscCBzdW1tYXJpemUgbW9zdCBjb21tb25seSB1c2UgYGdlb21zYCBpbiBnZ3Bsb3QuIFRoZSBmb2xsb3dpbmcgaXMgYSBsaXN0IGBnZW9tc2Agd2l0aCBtb3JlIGRldGFpbHMgYmFzZWQgb24gdGhlIHBocmFzZSAqKmxpc3Qgb2YgZ2VvbXMgaW4gZ2dwbG90KiogdGhyb3VnaCB0aGUgTWljcm9zb2Z0IENvcGlsb3QgdmlhIE1pcmNvc29mdCBFZGdlOg0KDQpHZW9tcyBhcmUgdGhlIGdlb21ldHJpYyBvYmplY3RzIHRoYXQgZGVmaW5lIHRoZSB0eXBlIGFuZCBhcHBlYXJhbmNlIG9mIHRoZSBwbG90cyBjcmVhdGVkIGJ5IHRoZSBnZ3Bsb3QyIHBhY2thZ2UgaW4gUi4gR2VvbXMgY2FuIGJlIGNvbWJpbmVkIGFuZCBsYXllcmVkIHRvIGNyZWF0ZSBjb21wbGV4IGFuZCBjdXN0b21pemVkIHZpc3VhbGl6YXRpb25zIG9mIGRhdGEuIEdlb21zIGFyZSBzcGVjaWZpZWQgYnkgdXNpbmcgZ2VvbVxfIGZ1bmN0aW9ucywgc3VjaCBhcyBnZW9tX3BvaW50LCBnZW9tX2JhciwgZ2VvbV9saW5lLCBldGMuDQoNClRoZXJlIGFyZSBtYW55IGdlb21zIGF2YWlsYWJsZSBpbiBnZ3Bsb3QyLCBlYWNoIHdpdGggaXRzIG93biBhZXN0aGV0aWNzIGFuZCBwYXJhbWV0ZXJzLiBTb21lIG9mIHRoZSBtb3N0IGNvbW1vbiBhbmQgdXNlZnVsIGdlb21zIGFyZToNCg0KLSAgICoqZ2VvbV9wb2ludCoqOiBUaGlzIGdlb20gZHJhd3MgcG9pbnRzIG9uIGEgcGxvdCwgYW5kIGNhbiBiZSB1c2VkIHRvIGNyZWF0ZSBgc2NhdHRlcnBsb3RzLCBkb3RwbG90cywgYnViYmxlIGNoYXJ0c2AsIGV0Yy4gSXQgcmVxdWlyZXMgeCBhbmQgeSBhZXN0aGV0aWNzLCBhbmQgY2FuIGFsc28gdGFrZSBgc2l6ZSwgc2hhcGUsIGNvbG91ciwgZmlsbGAgYW5kIGBhbHBoYWAgYWVzdGhldGljcyB0byBjb250cm9sIHRoZSBhcHBlYXJhbmNlIG9mIHRoZSBwb2ludHMuDQoNCi0gICAqKmdlb21fYmFyKiogYW5kICoqZ2VvbV9jb2wqKjogVGhlc2UgZ2VvbXMgZHJhdyBiYXJzIG9uIGEgcGxvdCwgYW5kIGNhbiBiZSB1c2VkIHRvIGNyZWF0ZSBgYmFyIGNoYXJ0cywgaGlzdG9ncmFtcywgbG9sbGlwb3AgY2hhcnRzYCwgZXRjLiBUaGV5IHJlcXVpcmUgeCBhbmQgeSBhZXN0aGV0aWNzLCB3aGVyZSB5IHJlcHJlc2VudHMgdGhlIGhlaWdodCBvZiB0aGUgYmFycy4gVGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBgZ2VvbV9iYXJgIGFuZCBgZ2VvbV9jb2xgIGlzIHRoYXQgYGdlb21fYmFyYCB1c2VzIGBzdGF0X2NvdW50YCBieSBkZWZhdWx0LCB3aGljaCBjb3VudHMgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gZWFjaCB4IGdyb3VwLCB3aGlsZSBgZ2VvbV9jb2xgIHVzZXMgYHN0YXRfaWRlbnRpdHlgIGJ5IGRlZmF1bHQsIHdoaWNoIHRha2VzIHRoZSB5IHZhbHVlcyBhcyBnaXZlbi4gVGhlc2UgZ2VvbXMgY2FuIGFsc28gdGFrZSBgc2l6ZSwgbGluZXR5cGUsIGNvbG91ciwgZmlsbGAgYW5kIGFscGhhIGFlc3RoZXRpY3MgdG8gY29udHJvbCB0aGUgYXBwZWFyYW5jZSBvZiB0aGUgYmFycy4NCg0KLSAgICoqZ2VvbV9saW5lKiogYW5kICoqZ2VvbV9wYXRoKio6IFRoZXNlIGdlb21zIGRyYXcgbGluZXMgb24gYSBwbG90LCBhbmQgY2FuIGJlIHVzZWQgdG8gY3JlYXRlIGBsaW5lIGNoYXJ0cywgdGltZSBzZXJpZXMgcGxvdHMsIHNwYWdoZXR0aSBwbG90c2AsIGV0Yy4gVGhleSByZXF1aXJlIHggYW5kIHkgYWVzdGhldGljcywgd2hlcmUgeCByZXByZXNlbnRzIHRoZSBvcmRlciBvZiB0aGUgcG9pbnRzIGFsb25nIHRoZSBsaW5lLiBUaGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGBnZW9tX2xpbmVgIGFuZCBgZ2VvbV9wYXRoYCBpcyB0aGF0IGBnZW9tX2xpbmVgIHNvcnRzIHRoZSBkYXRhIGJ5IHggYmVmb3JlIHBsb3R0aW5nLCB3aGlsZSBgZ2VvbV9wYXRoYCBwbG90cyB0aGUgZGF0YSBpbiB0aGUgb3JkZXIgdGhleSBhcHBlYXIgaW4gdGhlIGRhdGEgZnJhbWUuIFRoZXNlIGdlb21zIGNhbiBhbHNvIHRha2UgYHNpemUsIGxpbmV0eXBlLCBjb2xvdXJgIGFuZCBgYWxwaGFgIGFlc3RoZXRpY3MgdG8gY29udHJvbCB0aGUgYXBwZWFyYW5jZSBvZiB0aGUgbGluZXMuDQoNCi0gICAqKmdlb21fYm94cGxvdCoqOiBUaGlzIGdlb20gZHJhd3MgYm94cGxvdHMgb24gYSBwbG90LCBhbmQgY2FuIGJlIHVzZWQgdG8gZGlzcGxheSB0aGUgZGlzdHJpYnV0aW9uIG9mIGEgbnVtZXJpYyB2YXJpYWJsZSBhY3Jvc3MgZGlmZmVyZW50IGdyb3Vwcy4gSXQgcmVxdWlyZXMgeCBhbmQgeSBhZXN0aGV0aWNzLCB3aGVyZSB4IHJlcHJlc2VudHMgdGhlIGdyb3VwaW5nIHZhcmlhYmxlIGFuZCB5IHJlcHJlc2VudHMgdGhlIG51bWVyaWMgdmFyaWFibGUuIEl0IGFsc28gdXNlcyBzdGF0X2JveHBsb3QgYnkgZGVmYXVsdCwgd2hpY2ggY29tcHV0ZXMgdGhlIGBmaXZlLW51bWJlciBzdW1tYXJ5YCAobWluaW11bSwgbG93ZXIgcXVhcnRpbGUsIG1lZGlhbiwgdXBwZXIgcXVhcnRpbGUgYW5kIG1heGltdW0pIG9mIGVhY2ggZ3JvdXAuIFRoaXMgZ2VvbSBjYW4gYWxzbyB0YWtlIGBzaXplLCBsaW5ldHlwZSwgY29sb3VyLCBmaWxsYCBhbmQgYGFscGhhYCBhZXN0aGV0aWNzIHRvIGNvbnRyb2wgdGhlIGFwcGVhcmFuY2Ugb2YgdGhlIGJveHBsb3RzLg0KDQotICAgKipnZW9tX2hpc3RvZ3JhbSoqOiBUaGlzIGdlb20gZHJhd3MgaGlzdG9ncmFtcyBvbiBhIHBsb3QsIGFuZCBjYW4gYmUgdXNlZCB0byBkaXNwbGF5IHRoZSBkaXN0cmlidXRpb24gb2YgYSBzaW5nbGUgbnVtZXJpYyB2YXJpYWJsZS4gSXQgcmVxdWlyZXMgeCBhZXN0aGV0aWMsIHdoaWNoIHJlcHJlc2VudHMgdGhlIG51bWVyaWMgdmFyaWFibGUgdG8gYmUgYmlubmVkLiBJdCBhbHNvIHVzZXMgc3RhdF9iaW4gYnkgZGVmYXVsdCwgd2hpY2ggY291bnRzIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIGVhY2ggYmluLiBUaGlzIGdlb20gY2FuIGFsc28gdGFrZSBgc2l6ZSwgbGluZXR5cGUsIGNvbG91ciwgZmlsbGAgYW5kIGBhbHBoYWAgYWVzdGhldGljcyB0byBjb250cm9sIHRoZSBhcHBlYXJhbmNlIG9mIHRoZSBoaXN0b2dyYW0uDQoNCi0gICAqKmdlb21fZGVuc2l0eSoqIGFuZCAqKmdlb21fZGVuc2l0eV8yZCoqOiBUaGVzZSBnZW9tcyBkcmF3IGRlbnNpdHkgcGxvdHMgb24gYSBwbG90LCBhbmQgY2FuIGJlIHVzZWQgdG8gZGlzcGxheSB0aGUgZGlzdHJpYnV0aW9uIG9mIG9uZSBvciB0d28gbnVtZXJpYyB2YXJpYWJsZXMgdXNpbmcgc21vb3RoIGN1cnZlcy4gVGhleSByZXF1aXJlIHggYWVzdGhldGljIGZvciBvbmUtZGltZW5zaW9uYWwgZGVuc2l0eSBwbG90cyBvciB4IGFuZCB5IGFlc3RoZXRpY3MgZm9yIHR3by1kaW1lbnNpb25hbCBkZW5zaXR5IHBsb3RzLiBUaGV5IGFsc28gdXNlIGBzdGF0X2RlbnNpdHlgIG9yIGBzdGF0X2RlbnNpdHlfMmRgIGJ5IGRlZmF1bHQsIHdoaWNoIGVzdGltYXRlIHRoZSBwcm9iYWJpbGl0eSBkZW5zaXR5IGZ1bmN0aW9uIG9mIHRoZSB2YXJpYWJsZXMgdXNpbmcga2VybmVsIHNtb290aGluZy4gVGhlc2UgZ2VvbXMgY2FuIGFsc28gdGFrZSBgc2l6ZSwgbGluZXR5cGUsIGNvbG91ciwgZmlsbGAgYW5kIGBhbHBoYWAgYWVzdGhldGljcyB0byBjb250cm9sIHRoZSBhcHBlYXJhbmNlIG9mIHRoZSBkZW5zaXR5IHBsb3RzLg0KDQotICAgKipnZW9tX3RpbGUqKiBhbmQgKipnZW9tX2JpbjJkKio6IFRoZXNlIGdlb21zIGRyYXcgdGlsZXMgb24gYSBwbG90LCBhbmQgY2FuIGJlIHVzZWQgdG8gY3JlYXRlIGhlYXRtYXBzIG9yIGJpdmFyaWF0ZSBoaXN0b2dyYW1zIG9mIHR3byBudW1lcmljIHZhcmlhYmxlcy4gVGhleSByZXF1aXJlIHggYW5kIHkgYWVzdGhldGljcywgd2hpY2ggcmVwcmVzZW50IHRoZSBudW1lcmljIHZhcmlhYmxlcyB0byBiZSBiaW5uZWQuIFRoZXkgYWxzbyB1c2Ugc3RhdF9pZGVudGl0eSBvciBzdGF0X2JpbjJkIGJ5IGRlZmF1bHQsIHdoaWNoIHRha2Ugb3IgY29tcHV0ZSB0aGUgdmFsdWVzIGZvciBlYWNoIHRpbGUuIFRoZXNlIGdlb21zIGNhbiBhbHNvIHRha2UgYHNpemUsIGxpbmV0eXBlLCBjb2xvdXIsIGZpbGxgIGFuZCBgYWxwaGFgIGFlc3RoZXRpY3MgdG8gY29udHJvbCB0aGUgYXBwZWFyYW5jZSBvZiB0aGUgdGlsZXMuDQoNCi0gICAqKmdlb21fdGV4dCoqIGFuZCAqKmdlb21fbGFiZWwqKjogVGhlc2UgZ2VvbXMgZHJhdyB0ZXh0IG9yIGxhYmVscyBvbiBhIHBsb3QsIGFuZCBjYW4gYmUgdXNlZCB0byBhZGQgYW5ub3RhdGlvbnMgb3IgZGF0YSBsYWJlbHMgdG8gYSBwbG90LiBUaGV5IHJlcXVpcmUgeCBhbmQgeSBhZXN0aGV0aWNzIGZvciBwb3NpdGlvbmluZyBhbmQgbGFiZWwgYWVzdGhldGljIGZvciB0ZXh0IGNvbnRlbnQuIFRoZXkgY2FuIGFsc28gdGFrZSBgc2l6ZSwgY29sb3VyLCBmaWxsLCBhbHBoYSwgaGp1c3QsIHZqdXN0YCBhbmQgYHBhcnNlYCBhZXN0aGV0aWNzIHRvIGNvbnRyb2wgdGhlIGFwcGVhcmFuY2Ugb2YgdGhlIHRleHQgb3IgbGFiZWxzLg0KDQojIyBNYXBwaW5nIERhdGEgdG8gUGxvdA0KDQpgZ2VvbWAgZnVuY3Rpb25zIHJlcXVpcmUgbWFwcGluZyAoYXNzaWduaW5nKSBjb2x1bW5zIGluIHRoZSBkYXRhIHRvIGNvbXBvbmVudHMgb2YgdGhlIHBsb3QgbGlrZSB0aGUgYXhlcywgc2hhcGUgY29sb3JzLCBzaGFwZSBzaXplcywgZXRjLiBUaGUgbWFwcGluZ3MgbXVzdCBiZSB3cmFwcGVkIGluIHRoZSBgYWVzKClgIGZ1bmN0aW9uLCBzbyB3ZSB3b3VsZCB3cml0ZSBzb21ldGhpbmcgbGlrZSBgbWFwcGluZyA9IGFlcyh4ID0gY29sMSwgeSA9IGNvbDIpYC4NCg0KRm9yIGV4YW1wbGUsIGluIHRoZSBmb2xsb3dpbmcgZXhhbXBsZSB1c2luZyBgaXJpcyBkYXRhYCwgU2VwYWwgTGVuZ3RoIGlzIG1hcHBlZCB0byB0aGUgeC1heGlzLCBhbmQgU2VwYWwgV2lkdGggaXMgbWFwcGVkIHRvIHRoZSB5LWF4aXMuIEFmdGVyIGEgKywgdGhlIHBsb3R0aW5nIGNvbW1hbmRzIGNvbnRpbnVlLiBBIHNoYXBlIGlzIGNyZWF0ZWQgd2l0aCB0aGUgImdlb20iIGZ1bmN0aW9uIGdlb21fcG9pbnQoKS4NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChkYXRhID0gaXJpcywgbWFwcGluZyA9IGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgpKSArDQogIGdlb21fcG9pbnQoKQ0KYGBgDQoNCldoZW4gY3JlYXRpbmcgYSBoaXN0b2dyYW0sIG9ubHkgb25lIHZhcmlhYmxlIGlzIHVzZWQuIFNlZSB0aGUgZm9sbG93aW5nIGV4YW1wbGUuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQpnZ3Bsb3QoZGF0YSA9IGlyaXMsIG1hcHBpbmcgPSBhZXMoeCA9IFBldGFsLldpZHRoKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMikNCmBgYA0KDQojIyBBcnJhbmdpbmcgTXVsdGlwbGUgR3JvYnMgb24gdGhlIFNhbWUgUGFnZQ0KDQpJbiB0aGUgYWJvdmUgc3Vic2VjdGlvbiwgd2UgY3JlYXRlIHR3byBncmFwaHMgb24gdHdvIGRpZmZlcmVudCBwYWdlcy4gU29tZXRpbWVzLCB3ZSB3YW50IHRvIHBsYWNlIHR3byBtb3JlIGdyYXBocyBvbiB0aGUgc2FtZSBwYWdlIGZvciBjb21wYXJpc29uIHB1cnBvc2VzLiBJbiBiYXNlIFIsIHdlIGhhdmUgZ3JhcGhpYyBmdW5jdGlvbnMgc3VjaCBhcyBgcGFyKClgIGFuZCBgbGF5b3V0KClgIHRvIHNldCB1cCBhIGxheW91dCBmb3IgdGhlIGdyYXBoaWMgcGFnZS4NCg0KSW4gdGhpcyBub3RlLCB3ZSBpbnRyb2R1Y2UgdGhlIGxpYnJhcnkgYGNvd3Bsb3RgIHRvIGFycmFuZ2UgbXVsdGlwbGUgZ3JhcGhpY2FsIG9iamVjdHMgKGEuay5hIGBncm9ic2ApIG9uIGEgcGFnZS4NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCiMjIE5hbWUgdGhlIHR3byBwbG90cyBmaXJzdCBhbmQgdGhlbiBjYWxsIHRoZSB0d28gZ3JvYnMgaW4gdGhlIGxheW91dCBmdW5jdGlvbg0KIyMgc2NhdHRlciBwbG90DQpzY2F0dGVyID0gZ2dwbG90KGRhdGEgPSBpcmlzLCBtYXBwaW5nID0gYWVzKHggPSBTZXBhbC5MZW5ndGgsIHkgPSBTZXBhbC5XaWR0aCkpICsNCiAgZ2VvbV9wb2ludCgpDQojIyBoaXN0b2dyYW0NCmhpc3QgPSBnZ3Bsb3QoZGF0YSA9IGlyaXMsIG1hcHBpbmcgPSBhZXMoeCA9IFBldGFsLldpZHRoKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMikNCiMjIHVzZSBwbG90X2dyaWQoKSBpbiB7Y293cGxvdH0gdG8gbGF5b3V0IHRoZSB0d28gcGxvdHMNCnBsb3RfZ3JpZChzY2F0dGVyLCBoaXN0LCBsYWJlbHM9YygiQSIsICJCIiksIG5jb2wgPSAyLCBucm93ID0gMSkNCmBgYA0KDQojIyBQbG90IEFlc3RoZXRpY3MNCg0KSW4gYGdncGxvdGAgdGVybWlub2xvZ3kgYSBwbG90ICJhZXN0aGV0aWMiIGhhcyBhIHNwZWNpZmljIG1lYW5pbmcuIEl0IHJlZmVycyB0byBjb2xvcnMsIHNpemVzLCB0cmFuc3BhcmVuY2llcywgcGxhY2VtZW50LCBldGMuIG9mIHRoZSBwbG90dGVkIGRhdGEuIGBOb3QgYWxsIGdlb21zIHdpbGwgaGF2ZSB0aGUgc2FtZSBhZXN0aGV0aWMgb3B0aW9uc2AsIGJ1dCBtYW55IGNhbiBiZSB1c2VkIGJ5IG1vc3QgYGdlb21zYC4NCg0KSGVyZSBhcmUgc29tZSBleGFtcGxlczoNCg0KLSAgIGBzaGFwZWAgPSBEaXNwbGF5IGEgcG9pbnQgd2l0aCBgZ2VvbV9wb2ludCgpYCBhcyBhIGBkb3RgLCBgc3RhcmAsIGB0cmlhbmdsZWAsIG9yIGBzcXVhcmVgLCBldGMuDQotICAgYGZpbGxgID0gVGhlIGludGVyaW9yIGNvbG9yIChlLmcuIG9mIGEgYmFyIG9yIGJveHBsb3QpDQotICAgYGNvbG9yYCA9IFRoZSBleHRlcmlvciBsaW5lIG9mIGEgYmFyLCBib3hwbG90LCBldGMuLCBvciB0aGUgcG9pbnQgY29sb3IgaWYgdXNpbmcgYGdlb21fcG9pbnQoKWANCi0gICBgc2l6ZWAgPSBTaXplIChlLmcuIGxpbmUgdGhpY2tuZXNzLCBwb2ludCBzaXplKQ0KLSAgIGBhbHBoYWAgPSBUcmFuc3BhcmVuY3kgKDEgPSBvcGFxdWUsIDAgPSBpbnZpc2libGUpDQotICAgYGJpbndpZHRoYCA9IFdpZHRoIG9mIGhpc3RvZ3JhbSBiaW5zDQotICAgYHdpZHRoYCA9IFdpZHRoIG9mICJiYXIgcGxvdCIgY29sdW1ucw0KLSAgIGBsaW5ldHlwZWAgPSBMaW5lIHR5cGUgKGUuZy4gYHNvbGlkYCwgYGRhc2hlZGAsIGBkb3R0ZWRgKQ0KDQpUaGUgYWVzdGhldGljcyBvZiBwbG90IG9iamVjdHMgY2FuIGJlIGFzc2lnbmVkIHZhbHVlcyBpbiB0d28gd2F5czoNCg0KMS4gIEFzc2lnbmVkIGEgc3RhdGljIHZhbHVlIChlLmcuIGNvbG9yID0gImJsdWUiKSB0byBhcHBseSBhY3Jvc3MgYWxsIHBsb3R0ZWQgb2JzZXJ2YXRpb25zDQoNCjIuICBBc3NpZ25lZCB0byBhIGNvbHVtbiBvZiB0aGUgZGF0YSAoZS5nLiBjb2xvciA9IGhvc3BpdGFsKSBzdWNoIHRoYXQgdGhlIGRpc3BsYXkgb2YgZWFjaCBvYnNlcnZhdGlvbiBkZXBlbmRzIG9uIGl0cyB2YWx1ZSBpbiB0aGF0IGNvbHVtbg0KDQpXZSBoYXZlIGFscmVhZHkgYWRkZWQgYmlud2lkdGggdG8gdGhlIGFib3ZlIGhpc3RvZ3JhbS4gTmV4dCwgd2UgYWRkIGNvbG9yIHRvIHRoZSBoaXN0b2dyYW0NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NH0NCiMgQ2hhbmdlIGhpc3RvZ3JhbSBwbG90IGxpbmUgY29sb3JzIGJ5IGdyb3Vwcw0Kc2NhdHRlcjAxIDwtIGdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gU3BlY2llcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gUGV0YWwuV2lkdGgpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpDQojIE92ZXJsYWlkIGhpc3RvZ3JhbXMNCmhpc3QwMSA8LSBnZ3Bsb3QoaXJpcywgYWVzKHggPSBQZXRhbC5XaWR0aCwgY29sb3I9U3BlY2llcykpICsNCiAgICAgICAgICBnZW9tX2hpc3RvZ3JhbShmaWxsPSJuYXZ5IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjcsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gImlkZW50aXR5IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgYmlud2lkdGggPSAwLjIpDQojIyB1c2UgcGxvdF9ncmlkKCkgaW4ge2Nvd3Bsb3R9IHRvIGxheSBvdXQgdGhlIHR3byBwbG90cw0KcGxvdF9ncmlkKHNjYXR0ZXIwMSwgaGlzdDAxLCBsYWJlbHM9YygiQSIsICJCIiksIG5jb2wgPSAyLCBucm93ID0gMSkNCmBgYA0KDQojIyBMYWJlbHMgaW4gYGdncGxvdCgpYA0KDQpTdXJlbHkgeW91IHdpbGwgd2FudCB0byBhZGQgb3IgYWRqdXN0IHRoZSBwbG90J3MgbGFiZWxzLiBUaGVzZSBhcmUgbW9zdCBlYXNpbHkgZG9uZSB3aXRoaW4gdGhlIGBsYWJzKClgIGZ1bmN0aW9uIHdoaWNoIGlzIGFkZGVkIHRvIHRoZSBwbG90IHdpdGggYCtgIGp1c3QgYXMgdGhlIGBnZW9tc2Agd2VyZS4NCg0KV2l0aGluIGBsYWJzKClgIHlvdSBjYW4gcHJvdmlkZSBjaGFyYWN0ZXIgc3RyaW5ncyB0byB0aGVzZSBhcmd1bWVudHM6DQoNCi0gICBgeCA9YCBhbmQgYHkgPWA6IFRoZSB4LWF4aXMgYW5kIHktYXhpcyB0aXRsZSAobGFiZWxzKQ0KDQotICAgYHRpdGxlID1gOiBUaGUgbWFpbiBwbG90IHRpdGxlDQoNCi0gICBgc3VidGl0bGUgPWA6IFRoZSBzdWJ0aXRsZSBvZiB0aGUgcGxvdCwgaW4gc21hbGxlciB0ZXh0IGJlbG93IHRoZSB0aXRsZQ0KDQotICAgYGNhcHRpb24gPWA6IFRoZSBjYXB0aW9uIG9mIHRoZSBwbG90LCBpbiBib3R0b20tcmlnaHQgYnkgZGVmYXVsdA0KDQpIZXJlIGlzIGEgcGxvdCB3ZSBtYWRlIGVhcmxpZXIsIGJ1dCB3aXRoIG5pY2VyIGxhYmVsczoNCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gU3BlY2llcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gUGV0YWwuV2lkdGgpKSArDQogICAgICAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKw0KICAgICAgICAgICAgIGxhYnMoDQogICAgICAgICAgICAgICAgIHggPSAiU2VwYWwgTGVuZ3RoIiwNCiAgICAgICAgICAgICAgICAgeSA9ICJTZXBhbCBXaWR0aCIsDQogICAgICAgICAgICAgICAgICMgbGFiZWwgZm9yIGxlZ2VuZHMNCiAgICAgICAgICAgICAgICAgc2l6ZSA9ICJTZXBhbCBMZW5ndGg6IiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiU3BlY2llczoiLA0KICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBc3NvY2lhdGlvbiBiZXR3ZWVuIFNlcGFsIExlbmd0aCBhbmQgV2lkdGgiLA0KICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJUaGlzIGlzIGEgcGFydGlhbCBzY2F0dGVyIHBsb3QiLA0KICAgICAgICAgICAgICAgICBjYXB0aW9uID0gcGFzdGUoIkNyZWF0ZWQgb24iLCBTeXMuRGF0ZSgpKSkgKw0KICAgICAgICAgICAgIHRoZW1lX21pbmltYWwoKSAgICMgbWluaW1hbCB0aGVtZQ0KYGBgDQoNCiMgVGhlbWVzIGluIGBnZ3Bsb3QoKWANCg0KVGhlIHRoZW1lIHN5c3RlbSBpbiBgZ2dwbG90KClgIGRvZXMgbm90IGFmZmVjdCBob3cgdGhlIGRhdGEgaXMgcmVuZGVyZWQgYnkgYGdlb21zYCwgb3IgaG93IGl0IGlzIHRyYW5zZm9ybWVkIGJ5IHNjYWxlcy4gYFRoZW1lc2AgZG9uJ3QgY2hhbmdlIHRoZSBwZXJjZXB0dWFsIHByb3BlcnRpZXMgb2YgdGhlIHBsb3QsIGJ1dCB0aGV5IGRvIGhlbHAgeW91IG1ha2UgdGhlIHBsb3QgYWVzdGhldGljYWxseSBwbGVhc2luZyBvciBtYXRjaCBhbiBleGlzdGluZyBzdHlsZSBndWlkZS4gYFRoZW1lc2AgZ2l2ZSB1cyBjb250cm9sIG92ZXIgdGhpbmdzIGxpa2UgZm9udHMsIHRpY2tzLCBwYW5lbCBzdHJpcGVzLCBhbmQgYmFja2dyb3VuZHMuDQoNCkluIG90aGVyIHdvcmRzLCB3aGVuIGNyZWF0aW5nIHRoZSBwbG90IHdlIGRldGVybWluZSBob3cgdGhlIGRhdGEgaXMgZGlzcGxheWVkLCBhbmQgdGhlbiBhZnRlciBpdCBoYXMgYmVlbiBjcmVhdGVkIHdlIGNhbiBlZGl0IGV2ZXJ5IGRldGFpbCBvZiB0aGUgcmVuZGVyaW5nLCB1c2luZyB0aGUgdGhlbWluZyBzeXN0ZW0uDQoNCiMjIFRoZW1pbmcgU3lzdGVtIFN0cnVjdHVyZQ0KDQpUaGUgdGhlbWluZyBzeXN0ZW0gaXMgY29tcG9zZWQgb2YgZm91ciBtYWluIGNvbXBvbmVudHM6DQoNCi0gICBUaGVtZSBlbGVtZW50cyBzcGVjaWZ5IHRoZSBub24tZGF0YSBlbGVtZW50cyB0aGF0IHdlIGNhbiBjb250cm9sLiBGb3IgZXhhbXBsZSwNCg0KICAgIC0gICBgcGxvdC50aXRsZWAgY29udHJvbHMgdGhlIGFwcGVhcmFuY2Ugb2YgdGhlIHBsb3QgdGl0bGU7DQogICAgLSAgIGBheGlzLnRpY2tzLnhgIGNvbnRyb2xzIHRoZSB0aWNrcyBvbiB0aGUgeC1heGlzOw0KICAgIC0gICBgbGVnZW5kLmtleS5oZWlnaHRgIGNvbnRyb2xzIHRoZSBoZWlnaHQgb2YgdGhlIGtleXMgaW4gdGhlIGxlZ2VuZC4NCg0KLSAgIEVhY2ggZWxlbWVudCBpcyBhc3NvY2lhdGVkIHdpdGggYW4gZWxlbWVudCBmdW5jdGlvbiwgd2hpY2ggZGVzY3JpYmVzIHRoZSB2aXN1YWwgcHJvcGVydGllcyBvZiB0aGUgZWxlbWVudC4gRm9yIGV4YW1wbGUsIGBlbGVtZW50X3RleHQoKWAgc2V0cyB0aGUgZm9udCBzaXplLCBjb2xvciwgYW5kIGZhY2Ugb2YgdGV4dCBlbGVtZW50cyBsaWtlIGBwbG90LnRpdGxlYC4NCg0KLSAgIFRoZSBgdGhlbWUoKWAgZnVuY3Rpb24gd2hpY2ggYWxsb3dzIHlvdSB0byBvdmVycmlkZSB0aGUgZGVmYXVsdCB0aGVtZSBlbGVtZW50cyBieSBjYWxsaW5nIGVsZW1lbnQgZnVuY3Rpb25zLCBsaWtlIGB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJyZWQiKSlgLg0KDQotICAgQ29tcGxldGUgdGhlbWVzLCBsaWtlIGB0aGVtZV9ncmV5KClgIHNldCBhbGwgb2YgdGhlIHRoZW1lIGVsZW1lbnRzIHRvIHZhbHVlcyBkZXNpZ25lZCB0byB3b3JrIHRvZ2V0aGVyIGhhcm1vbmlvdXNseS4NCg0KSGVyZSBhcmUgc29tZSBlc3BlY2lhbGx5IGNvbW1vbiB0aGVtZSgpIGFyZ3VtZW50cy4gWW91IHdpbGwgcmVjb2duaXplIHNvbWUgcGF0dGVybnMsIHN1Y2ggYXMgYXBwZW5kaW5nIC54IG9yIC55IHRvIGFwcGx5IHRoZSBjaGFuZ2Ugb25seSB0byBvbmUgYXhpcy4NCg0KPGJyPg0KDQo8Y2VudGVyPjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vcGVuZ2RzY2kvc3RhNTUzL3Jhdy9tYWluL2dncGxvdC90aGVtZS5wbmciIGFsdD0ic2FtcGxlIGdncGxvdCB0aGVtZSIgaGVpZ2h0PSI0MDAiIHdpZHRoPSI2NTAiLz48L2NlbnRlcj4NCg0KPGJyPg0KDQpUbyBnZXQgdGhlIGNvbXBsZXRlIGxpc3Qgb2YgdGhlbWVzLCBydW4gdGhlIGZvbGxvd2luZyBjb2RlDQoNCmBgYHtyfQ0KI3RoZW1lX2dldCgpDQpgYGANCg0KVG8gbWFrZSBzdXJlIHRoZSBwbG90IGNhbiBzdGFuZCBhbG9uZSwgd2UgbmVlZCB0byBwcm92aWRlIHRoZSBwbG90IHdpdGggYXhlcywgbGVnZW5kIGxhYmVscywgYW5kIHRpdGxlLCBhbmQgdHdlYWsgdGhlIGNvbG9yIHNjYWxlIGZvciBhcHByb3ByaWF0ZSBjb2xvcnMuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQojIGFkZGluZyB0aGVtZXMNCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gU3BlY2llcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gUGV0YWwuV2lkdGgpKSArDQogICAgICAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKw0KICAgICAgICAgICAgIGxhYnMoDQogICAgICAgICAgICAgICAgIHggPSAiU2VwYWwgTGVuZ3RoIiwNCiAgICAgICAgICAgICAgICAgeSA9ICJTZXBhbCBXaWR0aCIsDQogICAgICAgICAgICAgICAgICMgbGFiZWwgZm9yIGxlZ2VuZHMNCiAgICAgICAgICAgICAgICAgc2l6ZSA9ICJTZXBhbCBMZW5ndGg6IiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiU3BlY2llczoiLA0KICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBc3NvY2lhdGlvbiBiZXR3ZWVuIFNlcGFsIExlbmd0aCBhbmQgV2lkdGgiICkgKw0KICAgICAgICAgICAgIHRoZW1lX21pbmltYWwoKSAgKyAgICMgbWluaW1hbCB0aGVtZQ0KICAgICAgICAgICAgIHRoZW1lKCAjIGxpc3Qgb2YgdGhlbWVzIGFwcGxpZWQgdG8gdGhlIHBsb3QNCiAgICAgICAgICAgICAgICAgICAjIHBsb3QgdGl0bGUgZmVhdHVyZXMNCiAgICAgICAgICAgICAgICAgICAjIGZvbnQgZmFtaWx5OiBjKCJzYW5zIiwgInNlcmlmIiwgIm1vbm8iKSAgDQogICAgICAgICAgICAgICAgICAgIyBmb250IGZhY2U6IGMoInBsYWluIiwgImJvbGQiLCAiaXRhbGljIiwgImJvbGQuaXRhbGljIikNCiAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gInNhbnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAwLjUpLCAjIGxlZnQoMCkscmlnaHQoMSkNCiAgICAgICAgICAgICAgICAgICAjIExhYmVscyBvZiBheGVzIA0KICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJyZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gIml0YWxpYyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJzZXJpZiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMC41KSwNCiAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAiYm9sZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZqdXN0ID0gMC41KSwNCiAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG9yID0gInJlZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuNSksDQogICAgICAgICAgICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImRhcmtibHVlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAic29saWQiKSwNCiAgICAgICAgICAgICAgICAgICAjIEF4aXMgdGljayBtYXJrcw0KICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2U9InBsYWluIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9InB1cnBsZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9MTEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuZ2xlPTQ1KSwNCiAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlPSJwbGFpbiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPSJvcmFuZ2UiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplPTExLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmdsZT05MCksDQogICAgICAgICAgICAgICAgICAgIyBGZWF0dXJlcyBvZiBsZWdlbmQNCiAgICAgICAgICAgICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuMSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2dyZWVuIiksDQogICAgICAgICAgICAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDAuOSwgMC44KSwNCiAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICAgICAgICAgICAgICAgICAjIyBQYW5lbCBncmlkDQogICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJsaWdodGJsdWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjEpLA0KICAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkNCiAgKQ0KYGBgDQoNCiMjIENvbXBsZXRlIENvbXBvbmVudHMgb2YgVGhlbWUNCg0KVGhlbWVzIGFyZSBhIHBvd2VyZnVsIHdheSB0byBjdXN0b21pemUgdGhlIGBub24tZGF0YSBjb21wb25lbnRzYCBvZiB0aGUgcGxvdHM6IGkuZS4gdGl0bGVzLCBsYWJlbHMsIGZvbnRzLCBiYWNrZ3JvdW5kLCBncmlkbGluZXMsIGFuZCBsZWdlbmRzLiBUbyBnaXZlIG91ciBwbG90cyBhIGNvbnNpc3RlbnQgY3VzdG9taXplZCBsb29rLCB3ZSBjYW4gZGVmaW5lIGEgdGhlbWUgZnVuY3Rpb24gYW5kIGNhbGwgdGhlIHRoZW1lIGZ1bmN0aW9uIGluIGFueSBgZ2dwbG90c2AuDQoNClRoZSBgdGlkeXZlcnNlYCBvZmZpY2lhbCB3ZWJzaXRlIHByb3ZpZGVzIGEgY29tcHJlaGVuc2l2ZSBkb2N1bWVudCBvbiB0aGVtZSBjb21wb25lbnRzIGluIFxgZ2dwbG90XGBcYC4gPGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS90aGVtZS5odG1sPi4gTnVtZXJvdXMgZXhhbXBsZXMgaGF2ZSBpbGx1c3RyYXRlZCBob3cgdG8gdXNlIHZhcmlvdXMgdGhlbWUgY29tcG9uZW50cy4NCg0KV2UgY2FuIGRlZmluZSBhIHRoZW1lIGZ1bmN0aW9uIHRoYXQgY2FuIGJlIHJldXNlZCB0byBjdXN0b21pemUgdGhlIHBsb3RzLiBGb3IgZXhhbXBsZSwgd2UgZGVmaW5lIHRoZSBmb2xsb3dpbmcgdGhlbWUgYW5kIHVzZSBpdCBpbiBkaWZmZXJlbnQgcGxvdHMuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQpteXBsb3QudGhlbWUgPC0gZnVuY3Rpb24oKSB7DQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSAic2FucyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya3JlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSksICMgbGVmdCgwKSxyaWdodCgxKQ0KICAgICMgYWRkIGJvcmRlciAxKQ0KICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmx1ZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gTkEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ldHlwZSA9IDIpLA0KICAgICMgY29sb3IgYmFja2dyb3VuZCAyKQ0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJhbGljZWJsdWUiKSwNCiAgICAjIG1vZGlmeSBncmlkIDMpDQogICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJzdGVlbGJsdWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuNSksDQogICAgcGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IueSA9ICBlbGVtZW50X2xpbmUoY29sb3VyID0gInN0ZWVsYmx1ZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjUpLA0KICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAjIG1vZGlmeSB0ZXh0LCBheGlzIGFuZCBjb2xvdXIgNCkgYW5kIDUpDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJzdGVlbGJsdWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJpdGFsaWMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gIlRpbWVzIE5ldyBSb21hbiIpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gInN0ZWVsYmx1ZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gIlRpbWVzIE5ldyBSb21hbiIpLA0KICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gInN0ZWVsYmx1ZSIpLA0KICAgICMgbGVnZW5kIGF0IHRoZSBib3R0b20gNikNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNiwgJ2NtJyksICNjaGFuZ2UgbGVnZW5kIGtleSBzaXplDQogICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAuNiwgJ2NtJyksICNjaGFuZ2UgbGVnZW5kIGtleSBoZWlnaHQNCiAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgwLjYsICdjbScpLCAjY2hhbmdlIGxlZ2VuZCBrZXkgd2lkdGgNCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwgI2NoYW5nZSBsZWdlbmQgdGl0bGUgZm9udCBzaXplDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT04KSkgI2NoYW5nZSBsZWdlbmQgdGV4dCBmb250IHNpemUNCn0NCmBgYA0KDQpOb3cgd2UgdXNlIHRoZSBhYm92ZSB0aGVtZSBpbiB0aGUgZm9sbG93aW5nIHNjYXR0ZXIgcGxvdHMuIEluc3RlYWQgb2YgdXNpbmcgdGhlIGNvbG9ycyBiYXNlZCBvbiB0aGUgdmFsdWUgb2Ygc3BlY2llcywgd2UgbWFudWFsbHkgc2VsZWN0IGNvbG9ycyB0byBlbmNvZGUgdGhlIHZhbHVlcyBvZiBzcGVjaWVzLiBUaGUgZm9sbG93aW5nIFVSTCBsaW5rcyB0byBhIFBERiBkb2N1bWVudCB3aXRoIGNvbG9ycyBpbiBSLiA8aHR0cDovL3d3dy5zdGF0LmNvbHVtYmlhLmVkdS9+dHpoZW5nL2ZpbGVzL1Jjb2xvci5wZGY+DQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQojIENoYW5nZSBoaXN0b2dyYW0gcGxvdCBsaW5lIGNvbG9ycyBieSBncm91cHMNCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gZmFjdG9yKFNwZWNpZXMpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSBQZXRhbC5XaWR0aCkpICsNCiAgICAgICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArDQogICAgICAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJkb2RnZXJibHVlNCIsICJkYXJrb2xpdmVncmVlbjQiLCJkYXJrb3JjaGlkMyIpKSArDQogICAgICAgICAgICAgbGFicygNCiAgICAgICAgICAgICAgICAgeCA9ICJTZXBhbCBMZW5ndGgiLA0KICAgICAgICAgICAgICAgICB5ID0gIlNlcGFsIFdpZHRoIiwNCiAgICAgICAgICAgICAgICAgIyMgQ29sb3IgYW5kIHNpemUgb2YgbGFiZWxzDQogICAgICAgICAgICAgICAgIHNpemUgPSAiU2VwYWwgTGVuZ3RoOiIsDQogICAgICAgICAgICAgICAgIGNvbG9yID0gIlNwZWNpZXM6IiwNCiAgICAgICAgICAgICAgICAgdGl0bGUgPSAiQXNzb2NpYXRpb24gYmV0d2VlbiBTZXBhbCBMZW5ndGggYW5kIFdpZHRoIikgKw0KICAgICAgICAgICAgIG15cGxvdC50aGVtZSgpDQpgYGANCg0KTmV4dCwgd2UgcGxvdCBhIGhpc3RvZ3JhbSB1c2luZyB0aGUgc2FtZSB0aGVtZS4NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChpcmlzLCBhZXMoeCA9IFBldGFsLldpZHRoLCBjb2xvcj1TcGVjaWVzKSkgKw0KICAgICAgICAgIGdlb21faGlzdG9ncmFtKGZpbGw9Im5hdnkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMywgDQogICAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSAiaWRlbnRpdHkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IDAuMikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImRvZGdlcmJsdWU0IiwgImRhcmtvbGl2ZWdyZWVuNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZGFya29yY2hpZDMiKSkgKw0KICAgICAgICAgICAgICAgbGFicygNCiAgICAgICAgICAgICAgICAgeCA9ICJQZXRhbCBXaWR0aCIsDQogICAgICAgICAgICAgICAgIGNvbG9yID0gIlNwZWNpZXM6IiwNCiAgICAgICAgICAgICAgICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFBldGFsIFdpZHRoIikgKw0KICAgICAgICAgICBteXBsb3QudGhlbWUoKQ0KYGBgDQoNCiMgQWRkaW5nIEFubm90YXRpb25zIHRvIEdyYXBoaWNzDQoNClRvIG1ha2UgdGhlIGdyYXBoaWMgbW9yZSBpbmZvcm1hdGl2ZSwgc29tZXRpbWVzIHdlIG1heSB3YW50IHRvIGFkZCBhbm5vdGF0aW9ucyB0byB0aGUgZ3JhcGhpYy4gSWYgd2UgY3JlYXRlIGEgc3RhdGlzdGljYWwgYW5kIHByb2JhYmlsaXN0aWMgZ3JhcGhpYywgb2NjYXNpb25hbGx5IHdlIG5lZWQgdG8gYWRkIG1hdGhlbWF0aWNhbCBlcXVhdGlvbnMgd2l0aCBHcmVlayBsZXR0ZXJzIHRvIHRoZSBncmFwaGljcy4NCg0KIyMgQWRkaW5nIFBsYWluIFRleHQgdG8gR3JhcGhpY3MNCg0KVG8gYWRkIHBsYWluIHRleHQgdG8gZ3JhcGhpY3MgaW4gZ2dwbG90LCB3ZSB1c2UgdGhlIGZ1bmN0aW9uIGBhbm5vdGF0ZSgpYCB3aXRoIGdpdmVuIGNvb3JkaW5hdGVzLiBGb3IgZXhhbXBsZSwgdGhlIHNjYXR0ZXIgcGxvdCBzaG93cyB0d28gc2VwYXJhdGUgZ3JvdXBzLg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KIyBDaGFuZ2UgaGlzdG9ncmFtIHBsb3QgbGluZSBjb2xvcnMgYnkgZ3JvdXBzDQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTZXBhbC5MZW5ndGgsIHkgPSBTZXBhbC5XaWR0aCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGZhY3RvcihTcGVjaWVzKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gUGV0YWwuV2lkdGgpKSArDQogICAgICAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKw0KICAgICAgICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiZG9kZ2VyYmx1ZTQiLCAiZGFya29saXZlZ3JlZW40IiwgImRhcmtvcmNoaWQzIikpICsNCiAgICAgICAgICAgICBsYWJzKA0KICAgICAgICAgICAgICAgICB4ID0gIlNlcGFsIExlbmd0aCIsDQogICAgICAgICAgICAgICAgIHkgPSAiU2VwYWwgV2lkdGgiLA0KICAgICAgICAgICAgICAgICAjIyBjb2xvciBhbmQgc2l6ZSBvZiBsYWJlbHMNCiAgICAgICAgICAgICAgICAgc2l6ZSA9ICJTZXBhbCBMZW5ndGg6IiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiU3BlY2llczoiLA0KICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBc3NvY2lhdGlvbiBiZXR3ZWVuIFNlcGFsIExlbmd0aCBhbmQgV2lkdGgiKSArDQogICAgICAgICAgICAgbXlwbG90LnRoZW1lKCkgKyANCiAgICAgICAgICAgICBhbm5vdGF0ZShnZW9tPSJ0ZXh0IiwgDQogICAgICAgICAgICAgICAgICAgICAgeD03LCANCiAgICAgICAgICAgICAgICAgICAgICB5PTQuMSwgDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWw9cGFzdGUoIlRoZSBkaXN0cmlidXRpb24gb2YgU2V0b3NhIGlzIGRpZmZlcmVudCIsIA0KICAgICAgICAgICAgICAgICAgICAgICJmcm9tIHRoYXQgb2YgVmVyc2ljb2xvciBhbmQgVmlnaW5pY2EiLCBzZXAgPSAiXG4iKSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xvcj0icmVkIiwNCiAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSkNCmBgYA0KDQpTZXZlcmFsIG90aGVyIGFsdGVybmF0aXZlcyB3ZSBjYW4gdXNlIHRvIGFkZCB0ZXh0IHRvIGdyYXBoaWNzIGNyZWF0ZWQgdXNpbmcgXGBnZ3Bsb3RcYFxgLg0KDQojIyBQYXNzaW5nIFBhcmFtZXRlcnMgaW4gQW5ub3RhdGlvbg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KIyBDaGFuZ2UgaGlzdG9ncmFtIHBsb3QgbGluZSBjb2xvcnMgYnkgZ3JvdXBzDQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTZXBhbC5MZW5ndGgsIHkgPSBTZXBhbC5XaWR0aCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGZhY3RvcihTcGVjaWVzKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gUGV0YWwuV2lkdGgpKSArDQogICAgICAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKw0KICAgICAgICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiZG9kZ2VyYmx1ZTQiLCAiZGFya29saXZlZ3JlZW40IiwgImRhcmtvcmNoaWQzIikpICsNCiAgICAgICAgICAgICBsYWJzKA0KICAgICAgICAgICAgICAgICB4ID0gIlNlcGFsIExlbmd0aCIsDQogICAgICAgICAgICAgICAgIHkgPSAiU2VwYWwgV2lkdGgiLA0KICAgICAgICAgICAgICAgICAjIyBDb2xvciBhbmQgc2l6ZSBvZiBsYWJlbHMNCiAgICAgICAgICAgICAgICAgc2l6ZSA9ICJTZXBhbCBMZW5ndGg6IiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiU3BlY2llczoiLA0KICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBc3NvY2lhdGlvbiBiZXR3ZWVuIFNlcGFsIExlbmd0aCBhbmQgV2lkdGgiKSArDQogICAgICAgICAgICAgbXlwbG90LnRoZW1lKCkgKyANCiAgICAgICAgICAgICAgYW5ub3RhdGUoZ2VvbT0idGV4dCIgLCANCiAgICAgICAgICAgICAgICAgICAgICAgeD03LCANCiAgICAgICAgICAgICAgICAgICAgICAgeT00LjQsDQogICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXBhc3RlKCJUaGUgUGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCByID0gIiwgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChjb3IoaXJpcyRTZXBhbC5MZW5ndGgsIGlyaXMkU2VwYWwuV2lkdGgpLDMpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsdWUiKQ0KYGBgDQoNClRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBiZXR3ZWVuIHNlcGFsIHdpZHRoIGFuZCBzZXBhbCBsZW5ndGggaXMgY2FsY3VsYXRlZCBkaXJlY3RseSBmcm9tIHRoZSBkYXRhIGFuZCBwYXNzZWQgdG8gdGhlIGFubm90YXRpb24gaW4gdGhlIGdyYXBoaWMuIE5vdGUgdGhhdCB3ZSB1c2VkIGEgdmVyeSBoYW5keSBhbmQgaW1wb3J0YW50IGdyYXBoaWMgZnVuY3Rpb24gYHBhc3RlKClgIHdoZW4gYWRkaW5nIHRoZSBhbm5vdGF0aW9uLg0KDQojIyBBZGRpbmcgTWF0aGVtYXRpY2FsIEVxdWF0aW9ucyB0byBHcmFwaGljcw0KDQpNYXRoZW1hdGljYWwgZXhwcmVzc2lvbnMgbWFkZSB3aXRoIHRoZSB0ZXh0IGBnZW9tc2AgdXNpbmcgYHBhcnNlID0gVFJVRWAgaW4gYGdncGxvdDJgIGhhdmUgYSBmb3JtYXQgc2ltaWxhciB0byB0aG9zZSBtYWRlIHdpdGggYHBsb3RtYXRoKClgIGFuZCBgZXhwcmVzc2lvbigpYCBpbiBiYXNlIFIsIGV4Y2VwdCB0aGF0IHRoZXkgYXJlIHN0b3JlZCBhcyBzdHJpbmdzLCByYXRoZXIgdGhhbiBhcyBleHByZXNzaW9uIG9iamVjdHMuDQoNClRvIG1peCByZWd1bGFyIHRleHQgd2l0aCBleHByZXNzaW9ucywgdXNlIHNpbmdsZSBxdW90ZXMgd2l0aGluIGRvdWJsZSBxdW90ZXMgKG9yIHZpY2UgdmVyc2EpIHRvIG1hcmsgdGhlIHBsYWluLXRleHQgcGFydHMuIEVhY2ggYmxvY2sgb2YgdGV4dCBlbmNsb3NlZCBieSB0aGUgaW5uZXIgcXVvdGVzIGlzIHRyZWF0ZWQgYXMgYSB2YXJpYWJsZSBpbiBhIG1hdGhlbWF0aWNhbCBleHByZXNzaW9uLg0KDQpCZWFyIGluIG1pbmQgdGhhdCwgaW4gUidzIHN5bnRheCBmb3IgbWF0aGVtYXRpY2FsIGV4cHJlc3Npb25zLCB3ZSAqKmNhbid0Kiogc2ltcGx5IHB1dCBhIHZhcmlhYmxlIHJpZ2h0IG5leHQgdG8gYW5vdGhlciB3aXRob3V0IHNvbWV0aGluZyBlbHNlIGluIGJldHdlZW4uIFRvIGRpc3BsYXkgdHdvIHZhcmlhYmxlcyBuZXh0IHRvIGVhY2ggb3RoZXIsIHB1dCBhIGAqYCBvcGVyYXRvciBiZXR3ZWVuIHRoZW0uIHdoZW4gYCpgIGlzIGRpc3BsYXllZCBpbiBhIGdyYXBoaWMsIGl0IGlzIHRyZWF0ZWQgYXMgYW4gaW52aXNpYmxlIG11bHRpcGxpY2F0aW9uIHNpZ24gKGZvciBhIHZpc2libGUgbXVsdGlwbGljYXRpb24gc2lnbiwgdXNlIGAlKiVgKToNCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCnguYXhpcyA8LSBzZXEoMCwgMjAsIGxlbmd0aC5vdXQgPSAxMDApDQp5LmF4aXMgPC0gKDEvc3FydCgyKnBpKSozKSpleHAoLSh4LmF4aXMtMTApXjIvKDIqOSkpDQpub3JtYWwuZGF0YSA9IGRhdGEuZnJhbWUoeD14LmF4aXMgLCB5PXkuYXhpcykNCiMjDQpnZ3Bsb3Qobm9ybWFsLmRhdGEsIGFlcyh4ID0geC5heGlzLCB5ID0geS5heGlzKSkgKyANCiAgICAgZ2VvbV9saW5lKGNvbG9yID0gImJsdWUiKSArDQogICAgIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAxLjI1KSwgeGxpbT1jKDAsMjApKSArIA0KICAgICBsYWJzKA0KICAgICAgICAgICAgICAgICB4ID0gIk5vcm1hbCBTY29yZSIsDQogICAgICAgICAgICAgICAgIHkgPSAiTm9ybWFsIERlbnNpdHkiLA0KICAgICAgICAgICAgICAgICB0aXRsZSA9ICJOb3JtYWwgRGVuc2l0eSBDdXJ2ZSIpICsNCiAgICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEwLCB5ID0gMC4yLCANCiAgICAgICAgICAgICAgIHBhcnNlID0gVFJVRSwgc2l6ZSA9IDQsDQogICAgICAgICAgICAgIGxhYmVsID0gIidGdW5jdGlvbjogICcgKiB5PT1mcmFjKDEsIHNxcnQoMipwaSkqIHNpZ21hKSAlKiUgZV57LSh4LSBtdSleMi8yfSIsDQogICAgICAgICAgICAgIGNvbG9yID0gInJlZCIpDQpgYGANCg0KIyMgQWRkaW5nIEltYWdlcyB0byBFeGlzdGluZyBnZ1Bsb3RzDQoNClRvIGVtYmVkIGEgUE5HIGltYWdlIHRvIGFuIGV4aXN0aW5nIGdyYXBoIGNyZWF0ZWQgYnkgYGdncGxvdGAsIHdlIG5lZWQgdG8gdXNlIGByZWFkUE5HKClgIGluIGxpYnJhcnkgKipwbmcqKiB0byBsb2FkIHRoZSBpbWFnZSB0byBSIGFuZCBgZ2V0VVJMY29udGVudCgpYCBpbiB0aGUgKipSQ3VybCoqIHRvIGluc2VydCB0aGUgaW1hZ2UgdG8gdGhlIGdyYXBoLg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KIyBjYXR1cmwgPC0gImh0dHBzOi8vc3RhdDU1My5zMy5hbWF6b25hd3MuY29tL2dncGxvdC9jYXQucG5nIg0KY2F0dXJsIDwtICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcGVuZ2RzY2kvc3RhNTUzL21haW4vZ2dwbG90L2NhdC5wbmciDQpteV9jYXQgPC0gIHJlYWRQTkcoZ2V0VVJMQ29udGVudChjYXR1cmwpKQ0KcmFzdGVyLmNhdCA8LSBhcy5yYXN0ZXIobXlfY2F0KSANCiMgQ2hhbmdlIGhpc3RvZ3JhbSBwbG90IGxpbmUgY29sb3JzIGJ5IGdyb3Vwcw0KZ2dwbG90KGlyaXMsIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBmYWN0b3IoU3BlY2llcyksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IFBldGFsLldpZHRoKSkgKw0KICAgICAgICAgICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsNCiAgICAgICAgICAgICBsYWJzKA0KICAgICAgICAgICAgICAgICB4ID0gIlNlcGFsIExlbmd0aCIsDQogICAgICAgICAgICAgICAgIHkgPSAiU2VwYWwgV2lkdGgiLA0KICAgICAgICAgICAgICAgICAjIyBDb2xvciBhbmQgc2l6ZSBvZiBsYWJlbHMNCiAgICAgICAgICAgICAgICAgc2l6ZSA9ICJTZXBhbCBMZW5ndGg6IiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiU3BlY2llczoiLA0KICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBc3NvY2lhdGlvbiBiZXR3ZWVuIFNlcGFsIExlbmd0aCBhbmQgV2lkdGgiKSArDQogICAgICAgICAgICAgbXlwbG90LnRoZW1lKCkgKyANCiAgICAgICAgICAgICAgYW5ub3RhdGlvbl9yYXN0ZXIocmFzdGVyLmNhdCwgNCwgNC44NSwgMy42NSwgNC41KQ0KYGBgDQoNCiMgUmVtb3ZpbmcgQ2hhcnQgSnVua3MgVmlhIFRoZW1lcw0KDQpXZSByZW1vdmUgc29tZSB1bm5lY2Vzc2FyeSBtYXJrcyBhbmQgY2hhbm5lbHMgZnJvbSB0aGUgY2hhcnQgdmlhIHRoZW1lOiBjaGFuZ2UgdGhlIGJhY2tncm91bmQgY29sb3IsIGdyaWQsIGFuZCBwbG90IHRpdGxlLiBFc3NlbnRpYWxseSwgd2UgdXNlIGEgdGhlbSB0byBsYXkgb3V0IGEgZ2dwbG90IHN0eWxlIHdpdGhvdXQgYW55IGNoYXJ0IGp1bmsuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQpteXBsb3QudGhlbWVfbmV3IDwtIGZ1bmN0aW9uKCkgew0KICB0aGVtZSgNCiAgICAjZ2dwbG90IG1hcmdpbnMNCiAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4odCA9IDUwLCAgIyBUb3AgbWFyZ2luDQogICAgICAgICAgICAgICAgICAgICAgICAgIHIgPSAzMCwgICMgUmlnaHQgbWFyZ2luDQogICAgICAgICAgICAgICAgICAgICAgICAgIGIgPSAzMCwgICMgQm90dG9tIG1hcmdpbg0KICAgICAgICAgICAgICAgICAgICAgICAgICBsID0gMzApLCAjIExlZnQgbWFyZ2luDQogICAgIyMgZ2dwbG90IHRpdGxlcw0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSAic2FucyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAibmF2eSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbj1tYXJnaW4oMCwwLDMwLDApKSwgIyBsZWZ0KDApLHJpZ2h0KDEpDQogICAgIyBhZGQgYm9yZGVyIDEpDQogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9IE5BLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IE5BLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAyKSwNCiAgICAjIGNvbG9yIGJhY2tncm91bmQgMikNCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiI2Y2ZjZmNiIpLA0KICAgICMgbW9kaWZ5IGdyaWQgMykNCiAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ3doaXRlJywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gMywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjUpLA0KICAgIHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSAgZWxlbWVudF9saW5lKGNvbG91ciA9ICd3aGl0ZScsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjUpLA0KICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAjIG1vZGlmeSB0ZXh0LCBheGlzIGFuZCBjb2xvdXIgNCkgYW5kIDUpDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJuYXZ5IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICNmYWNlID0gIml0YWxpYyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gNywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI2ZhbWlseSA9ICJUaW1lcyBOZXcgUm9tYW4iDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICksDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAibmF2eSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjZmFtaWx5ID0gIlRpbWVzIE5ldyBSb21hbiINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksDQogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAibmF2eSIpLA0KICAgICMgbGVnZW5kIGF0IHRoZSBib3R0b20gNikNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNiwgJ2NtJyksICNjaGFuZ2UgbGVnZW5kIGtleSBzaXplDQogICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAuNiwgJ2NtJyksICNjaGFuZ2UgbGVnZW5kIGtleSBoZWlnaHQNCiAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgwLjYsICdjbScpLCAjY2hhbmdlIGxlZ2VuZCBrZXkgd2lkdGgNCiAgICAjbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksICNjaGFuZ2UgbGVnZW5kIHRpdGxlIGZvbnQgc2l6ZQ0KICAgIGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksICAjIHJlbW92ZSBhbGwgbGVnZW5kIHRpdGxlcw0KICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLA0KICAgICMjIyMjDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT04KSkgI2NoYW5nZSBsZWdlbmQgdGV4dCBmb250IHNpemUNCn0NCmBgYA0KDQpVc2luZyB0aGUgYWJvdmUgdGhlbWUsIHdlIHJlLXBsb3QgdGhlIGlyaXMgZGF0YSB3aXRoIGxlc3MgaXJyZWxldmFudCBncmFwaGljYWwgZWxlbWVudHMuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTV9DQojIENoYW5nZSBoaXN0b2dyYW0gcGxvdCBsaW5lIGNvbG9ycyBieSBncm91cHMNCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBmYWN0b3IoU3BlY2llcykpLCBsaW5ldHlwZSA9IFNwZWNpZXMpICsNCiAgICAgICAgICAgICBnZW9tX3BvaW50KHNpemUgPSAyLCBhbHBoYSA9IDAuNykgKw0KICAgICAgICAgICAgIHN0YXRfc21vb3RoKG1ldGhvZCA9IGxtLCBzZT1GQUxTRSwgc2l6ZSA9IDAuMykgKw0KICAgICAgICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiZG9kZ2VyYmx1ZTQiLCAiZGFya29saXZlZ3JlZW40IiwgImRhcmtvcmNoaWQzIikpICsNCiAgICAgICAgICAgICBsYWJzKA0KICAgICAgICAgICAgICAgICB4ID0gIlNlcGFsIExlbmd0aCIsDQogICAgICAgICAgICAgICAgIHkgPSAiU2VwYWwgV2lkdGgiLA0KICAgICAgICAgICAgICAgICAjIyBsYWJlbHMgb2YgY29sb3IgYW5kIHNpemUNCiAgICAgICAgICAgICAgICAgI3NpemUgPSAiU2VwYWwgTGVuZ3RoIiwNCiAgICAgICAgICAgICAgICAgI2NvbG9yID0gTkEsDQogICAgICAgICAgICAgICAgIHRpdGxlID0gIkFzc29jaWF0aW9uIGJldHdlZW4gU2VwYWwgTGVuZ3RoIGFuZCBXaWR0aCIpICsNCiAgICAgICAgICAgICBteXBsb3QudGhlbWVfbmV3KCkgKyANCiAgICAgICAgICAgICAgYW5ub3RhdGUoZ2VvbT0idGV4dCIgLCANCiAgICAgICAgICAgICAgICAgICAgICAgeD02LjgsIA0KICAgICAgICAgICAgICAgICAgICAgICB5PTIsDQogICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXBhc3RlKCJUaGUgUGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCByID0gIiwgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChjb3IoaXJpcyRTZXBhbC5MZW5ndGgsIGlyaXMkU2VwYWwuV2lkdGgpLDMpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJuYXZ5IikgKyANCiAgICAgICAgICAgICAgIGNvb3JkX2ZpeGVkKDEpICAgICMjIFRoaXMgY2hhbmdlcyB0aGUgYXNwZWN0IHJhdGlvIG9mIHRoZSBncmFwaA0KYGBgDQoNCiMgQ29tbW9uIEV4dGVuc2lvbnMgdG8gYGdncGxvdGANCg0KVGhlcmUgYXJlIGRpZmZlcmVudCBleHRlbnNpb25zIG9mIGBnZ3Bsb3RgLiBUaGlzIHNlY3Rpb24gaW50cm9kdWNlcyB0d28gY29tbW9ubHkgdXNlZCBleHRlbnNpb25zLiBXZSB3aWxsIGFsc28gYnJpZWZseSBvdXRsaW5lIGEgZmV3IG90aGVyIGV4dGVuc2lvbnMgb2YgYGdncGxvdGAuDQoNCiMjIEFtaW5hdGVkIEdyYXBoIHdpdGggYGdnYW5pbWF0ZSgpYA0KDQpgZ2dhbmltYXRlKClgIGV4dGVuZHMgdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MgYXMgaW1wbGVtZW50ZWQgYnkgYGdncGxvdDJgIHRvIGluY2x1ZGUgdGhlIGRlc2NyaXB0aW9uIG9mIGFuaW1hdGlvbi4gSXQgZG9lcyB0aGlzIGJ5IHByb3ZpZGluZyBhIHJhbmdlIG9mIG5ldyBncmFtbWFyIGNsYXNzZXMgdGhhdCBjYW4gYmUgYWRkZWQgdG8gdGhlIHBsb3Qgb2JqZWN0IGluIG9yZGVyIHRvIGN1c3RvbWl6ZSBob3cgaXQgc2hvdWxkIGNoYW5nZSB3aXRoIHRpbWUuDQoNCi0gICBgdHJhbnNpdGlvbl8qKClgIGRlZmluZXMgaG93IHRoZSBkYXRhIHNob3VsZCBiZSBzcHJlYWQgb3V0IGFuZCBob3cgaXQgcmVsYXRlcyB0byBpdHNlbGYgYWNyb3NzIHRpbWUuDQoNCi0gICBgdmlld18qKClgIGRlZmluZXMgaG93IHRoZSBwb3NpdGlvbmFsIHNjYWxlcyBzaG91bGQgY2hhbmdlIGFsb25nIHdpdGggdGhlIGFuaW1hdGlvbi4NCg0KLSAgIGBzaGFkb3dfKigpYCBkZWZpbmVzIGhvdyBkYXRhIGZyb20gb3RoZXIgcG9pbnRzIGluIHRpbWUgc2hvdWxkIGJlIHByZXNlbnRlZCBpbiB0aGUgZ2l2ZW4gcG9pbnQgaW4gdGltZS4NCg0KLSAgIGBlbnRlcl8qKCkvZXhpdF8qKClgIGRlZmluZXMgaG93IG5ldyBkYXRhIHNob3VsZCBhcHBlYXIgYW5kIGhvdyBvbGQgZGF0YSBzaG91bGQgZGlzYXBwZWFyIGR1cmluZyB0aGUgY291cnNlIG9mIHRoZSBhbmltYXRpb24uDQoNCi0gICBgZWFzZV9hZXMoKWAgZGVmaW5lcyBob3cgZGlmZmVyZW50IGFlc3RoZXRpY3Mgc2hvdWxkIGJlIGVhc2VkIGR1cmluZyB0cmFuc2l0aW9ucy4NCg0KVGhlIGxvZ2ljIGJlaGluZCB0aGUgYGdnYW5pbWF0ZWAgaXMgdG8gY3JlYXRlIGEgc2VxdWVuY2Ugb2YgaW1hZ2VzIGFuZCB0aGVuIG1ha2UgYSBnaWYgaW1hZ2UuIFdlIG5lZWQgdG8gd3JpdGUgSFRNTCB0byBpbmNsdWRlIHRoaXMgZ2lmIGluIHRoZSBSTWFya2Rvd24gZG9jdW1lbnQuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQpsaWJyYXJ5KGdhcG1pbmRlcikNCg0KcCA8LSBnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICB5PWxpZmVFeHAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IHBvcCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSBjb3VudHJ5KSkgKw0KICAgICAgICBnZW9tX3BvaW50KGFlcyhzaXplID0gcG9wLCBpZHMgPSBjb3VudHJ5ICksDQogICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjcpICsNCiAgICAgICAgc2NhbGVfY29sb3JfdmlyaWRpc19kKCkgKyAgICAgICMgY29sb3IgcGFsbGV0cyANCiAgICAgICAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMiwgMTIpKSArDQogICAgICAgIHNjYWxlX3hfbG9nMTAoKSArDQogICAgICAgIGxhYnMoeCA9ICJHRFAgcGVyIGNhcGl0YSIsIA0KICAgICAgICAgICAgIHkgPSAiTGlmZSBleHBlY3RhbmN5IikgKw0KICAgICAgICAjIyBnZ2FuaW1hdGUgY29tbWFuZA0KICAgICAgIHRyYW5zaXRpb25fdGltZSh5ZWFyKQ0KIyMgDQphbmltX3NhdmUoIkxpZmVFeHAuZ2lmIiwgcCkNCiMgIGFuaW1hdGUocCwgcmVuZGVyZXIgPSBnaWZza2lfcmVuZGVyZXIoKSkgICMgVGhpcyBjb21tYW5kIHdpbGwgcG9wIHVwIGEgbmV3IGdyYXBoaWMgd2luZG93IHNob3dpbmcgdGhlIGFuaW1hdGlvbi4NCmBgYA0KDQo8YnI+DQoNCjxjZW50ZXI+PGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9wZW5nZHNjaS9zdGE1NTMvcmF3L21haW4vZ2dwbG90L0xpZmVFeHAuZ2lmIiBhbHQ9IkxpZmVFeHBlY3RhbmN5IEFuaW1hdGlvbiIgaGVpZ2h0PSI0NTAiIHdpZHRoPSI0MDAiLz48L2NlbnRlcj4NCg0KPGJyPg0KDQpTaW5jZSB0aGUgZ2lmIGltYWdlIGlzIG1hZGUgb2YgaW5kaXZpZHVhbCBzdGF0aWMgaW1hZ2VzLCBpdCBpcyBkaWZmZXJlbnQgZnJvbSB0aGUgaW50ZXJhY3RpdmUgcGxvdCBwcmVzZW50ZWQgaW4gdGhlIHByZXZpb3VzIHNlY3Rpb25zIHRoYXQgaGF2ZSB0aGUgY2FwYWJpbGl0eSBvZiBzaG93aW5nIG1vZGUgaW5mb3JtYXRpb24gb2YgdGhlIGRhdGEgdmlhIGhvdmVyIG1lc3NhZ2UuDQoNClRoZSBuZXh0IGdpZiBncmFwaCBjb25zaXN0cyBvZiA1IHBhbmVscywgZWFjaCByZXByZXNlbnRpbmcgYSBjb250aW5lbnQuIFRoZXkgYXJlIGFsc28gZmlnIGltYWdlcy4gVGhlcmVmb3JlLCBubyBob3ZlciBtZXNzYWdlIGlzIGF2YWlsYWJsZSBmb3IgdGhlc2UgZ2lmIGZpZ3VyZXMuDQoNCldlIHVzZSB0aGUge2dpZmtpfSBwYWNrYWdlIHRvIHJlbmRlciB0aGUgaW1hZ2VzIGluIHRoZSBmb3JtIG9mIGdpZiBhbmQgdGhlbiBpbmNsdWRlIHRoZSBnaWYgaW1hZ2UgaW50byB0aGUgUk1hcmtkb3duIGRvY3VtZW50IGRpcmVjdGx5Lg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KdyA8LSBnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoZ2RwUGVyY2FwLCBsaWZlRXhwLCANCiAgICAgICAgICAgICAgICAgIHNpemUgPSBwb3AsIGNvbG91ciA9IGNvdW50cnkpKSArDQogICAgICAgICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjcsIHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgICAgICAgICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb3VudHJ5X2NvbG9ycykgKw0KICAgICAgICAgICAjc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJkb2RnZXJibHVlNCIsICJkYXJrb2xpdmVncmVlbjQiLCJkYXJrb3JjaGlkMyIpKSArDQogICAgICAgICAgICNzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpICsNCiAgICAgICAgICAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMiwgMTIpKSArDQogICAgICAgICAgIHNjYWxlX3hfbG9nMTAoKSArDQogICAgICAgICAgICMgYnJlYWsgZG93biB0aGUgcHJldmlvdXMgc2luZ2xlIHBsb3QgYnkgY29udGluZW50IA0KICAgICAgICAgICAjIGZhY2V0X3dyYXAofmNvbnRpbmVudCkgKyAgICAgIyBjcmVhdGUgbXVsdGlwbGUgcGFuZWxzIGFjY29yZGluZyB0byB0aGUgY29udGluZW50cw0KICAgICAgICAgICAjIEhlcmUgY29tZXMgdGhlIGdnYW5pbWF0ZSBzcGVjaWZpYyBiaXRzDQogICAgICAgICAgIGxhYnModGl0bGUgPSAnWWVhcjoge2ZyYW1lX3RpbWV9JywgDQogICAgICAgICAgICAgICAgICAgIHggPSAnR0RQIHBlciBjYXBpdGEnLA0KICAgICAgICAgICAgICAgICAgICB5ID0gJ2xpZmUgZXhwZWN0YW5jeScpICsNCiAgICAgICAgICAgdHJhbnNpdGlvbl90aW1lKHllYXIpICsNCiAgICAgICAgICAgZWFzZV9hZXMoJ2xpbmVhcicpDQojIyMNCmFuaW1hdGUodywgcmVuZGVyZXIgPSBnaWZza2lfcmVuZGVyZXIoKSwNCiAgICAgICAgICByZXdpbmQgPSBUUlVFKQ0KYGBgDQoNClRoZSBhYm92ZSBjb2RlIGRvZXMgbm90IHNhdmUgdGhlIGdlbmVyYXRlZCBnaWYgaW1hZ2UgdG8gdGhlIGRvY3VtZW50IGZvbGRlciAoZGlyZWN0b3J5KS4gSWYgbmVlZCB0byBzYXZlIGl0IGZyb20gdGhlIHZpZXdlciB3aW5kb3cgdG8gdGhlIGRlc2lnbmF0ZWQgZm9sZGVyIGFuZCB0aGVuIGVtYmVkIGl0IHRvIGEgd2ViIHBhZ2UgY3JlYXRlZCBieSB0b29scyBvdGhlciB0aGFuIHRoZSBSTWFya2Rvd24uDQoNCmBgYCAgICAgICAgIA0KPGJyPg0KPGNlbnRlcj48aW1nIHNyYz0iaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Blbmdkc2NpL3N0YTU1My9tYWluL2dncGxvdC9MaWZlRXhwUmV3aW5kLmdpZiIgYWx0PSJMaWZlIEV4cGVjdGFuY3kgQW5pbWF0aW9uIFJld2luZCIgaGVpZ2h0PSI1MDAiIHdpZHRoPSI0MDAiPjwvY2VudGVyPg0KPGJyPg0KYGBgDQoNCk5leHQsIHdlIGNyZWF0ZSBhIGdyb3VwIGdpZiB1c2luZyBmYWNldF93cmFwKCkgZnVuY3Rpb24uIFRoZSBjb2RlIGlzIHRoZSBzYW1lIGFzIHRoZSBhYm92ZSBleGFtcGxlIGV4Y2VwdCBmb3Igb25lIGFkZGl0aW9uYWwgZnVuY3Rpb24gY2FsbC4NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCncgPC0gZ2dwbG90KGdhcG1pbmRlciwgYWVzKGdkcFBlcmNhcCwgbGlmZUV4cCwgDQogICAgICAgICAgICAgICAgICBzaXplID0gcG9wLCBjb2xvdXIgPSBjb3VudHJ5KSkgKw0KICAgICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC43LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogICAgICAgICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gY291bnRyeV9jb2xvcnMpICsNCiAgICAgICAgICAgI3NjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiZG9kZ2VyYmx1ZTQiLCAiZGFya29saXZlZ3JlZW40IiwiZGFya29yY2hpZDMiKSkgKw0KICAgICAgICAgICAjc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGU9IlNldDEiKSArDQogICAgICAgICAgIHNjYWxlX3NpemUocmFuZ2UgPSBjKDIsIDEyKSkgKw0KICAgICAgICAgICBzY2FsZV94X2xvZzEwKCkgKw0KICAgICAgICAgICAjIGJyZWFrIGRvd24gdGhlIHByZXZpb3VzIHNpbmdsZSBwbG90IGJ5IGNvbnRpbmVudCANCiAgICAgICAgICAgZmFjZXRfd3JhcCh+Y29udGluZW50KSArICAgICAjIGNyZWF0ZSBtdWx0aXBsZSBwYW5lbHMgYWNjb3JkaW5nIHRvIHRoZSBjb250aW5lbnRzDQogICAgICAgICAgICMgSGVyZSBjb21lcyB0aGUgZ2dhbmltYXRlLXNwZWNpZmljIGJpdHMNCiAgICAgICAgICAgbGFicyh0aXRsZSA9ICdZZWFyOiB7ZnJhbWVfdGltZX0nLCANCiAgICAgICAgICAgICAgICAgICAgeCA9ICdHRFAgcGVyIGNhcGl0YScsDQogICAgICAgICAgICAgICAgICAgIHkgPSAnbGlmZSBleHBlY3RhbmN5JykgKw0KICAgICAgICAgICB0cmFuc2l0aW9uX3RpbWUoeWVhcikgKw0KICAgICAgICAgICBlYXNlX2FlcygnbGluZWFyJykNCiMjIw0KYW5pbWF0ZSh3LCByZW5kZXJlciA9IGdpZnNraV9yZW5kZXJlcigpLA0KICAgICAgICAgIHJld2luZCA9IFRSVUUpDQpgYGANCg0KIyMgUmlkZ2V0bGluZSBQbG90IHdpdGggYGdncmlkZ2VzYCBMaWJyYXJ5DQoNClRoZSByaWRnZWxpbmUgcGxvdCBpcyBhIHVzZWZ1bCAzRCB0byBjb21wYXJlIG11bHRpcGxlIGRlbnNpdGllcy4gSXQgY3JlYXRlcyBhIDNEIGltcHJlc3Npb24gYW5kIGhhcyBnYWluZWQgaW5jcmVhc2luZyBwb3B1bGFyaXR5LiBIZXJlIHdlIHVzZSB0aGUgQ2FsaWZvcm5pYSBIb3VzaW5nIERhdGEgdGhhdCBpcyBhdmFpbGFibGUgb24gdGhlIFByb2plY3QgRGF0YSBTZXQgPGh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL2RhdGFzZXRzLyNjYWwtaG91c2luZz4uDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJ30NCkNhbEhvdXNpbmcgPSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Blbmdkc2NpL3N0YTU1My5odG1sL21haW4vZGF0YS9jYS1ob3VzaW5nLXByaWNlLmNzdiIpDQpnZ3Bsb3QoQ2FsSG91c2luZywgYWVzKHggPSBtZWRpYW5faG91c2VfdmFsdWUsIHkgPSBvY2Vhbl9wcm94aW1pdHksIGZpbGwgPSBvY2Vhbl9wcm94aW1pdHkpKSArDQogIGdlb21fZGVuc2l0eV9yaWRnZXMoKQ0KYGBgDQoNCllvdSBjYW4gcGFzcyBgc3RhdCh4KWAgb3IgYGZhY3RvcihzdGF0KHgpKWAgdG8gdGhlIGZpbGwgYXJndW1lbnQgb2YgYGFlc2AgYW5kIHVzZSBgZ2VvbV9kZW5zaXR5X3JpZGdlc19ncmFkaWVudGAgYW5kIGEgY29udGludW91cyBmaWxsIGNvbG9yIHNjYWxlIHRvIGZpbGwgZWFjaCByaWRnZWxpbmUgd2l0aCBhIGdyYWRpZW50Lg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcid9DQpnZ3Bsb3QoQ2FsSG91c2luZywgYWVzKHggPSBtZWRpYW5faG91c2VfdmFsdWUsIHkgPSBvY2Vhbl9wcm94aW1pdHksIGZpbGwgPSBzdGF0KHgpKSkgKw0KICAgICAgZ2VvbV9kZW5zaXR5X3JpZGdlc19ncmFkaWVudChqaXR0ZXJlZF9wb2ludHMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9wb2ludHNfaml0dGVyKHdpZHRoID0gMC4wNSwgaGVpZ2h0ID0gMCksDQogICAgcG9pbnRfc2hhcGUgPSAnfCcsIHBvaW50X3NpemUgPSAxLCBwb2ludF9hbHBoYSA9IDEsIGFscGhhID0gMC4zLCkgKyANCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MobmFtZSA9ICJtZWRpYW5faG91c2VfdmFsdWUiLCBvcHRpb24gPSAiQyIpIA0KYGBgDQoNCk5leHQsIHdlIGV4cGxvcmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjb250aW51b3VzIHZhcmlhYmxlcyBpbiB0aGUgaXJpcyBkYXRhIHNldC4gQXMgYW4gZXhhbXBsZSwgd2UgbWFrZSB0aGUgZm9sbG93aW5nIHJpZGdlbGluZSBwbG90IHRvIHNlZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHNlcGFsIHdpZHRocyBhY3Jvc3MgdGhlIHNwZWNpZXMuDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLldpZHRoLCB5ID0gU3BlY2llcywgZmlsbCA9IHN0YXQoeCkpKSArDQogICAgICBnZW9tX2RlbnNpdHlfcmlkZ2VzX2dyYWRpZW50KGppdHRlcmVkX3BvaW50cyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3BvaW50c19qaXR0ZXIod2lkdGggPSAwLjA1LCBoZWlnaHQgPSAwKSwNCiAgICBwb2ludF9zaGFwZSA9ICd8JywgcG9pbnRfc2l6ZSA9IDEsIHBvaW50X2FscGhhID0gMSwgYWxwaGEgPSAwLjMsKSArIA0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhuYW1lID0gIlNlcGFsIFdpZHRoIiwgb3B0aW9uID0gIkMiKSANCmBgYA0KDQpUaGUgYWJvdmUgZGlzdHJpYnV0aW9ucyBoYXZlIHNpbWlsYXIgc2hhcGVzICh2YXJpYXRpb25zKSBidXQgd2l0aCBkaWZmZXJlbnQgbWVhbnMuIFRoaXMgYWxzbyBpbmRpY2F0ZXMgdGhlIEFOT1ZBIG1vZGVsIGJldHdlZW4gc2VwYWwgd2lkdGggYW5kIHNwZWNpZXMgaXMgYXBwcm9wcmlhdGUuDQoNCiMjIE90aGVyIEV4dGVuc2lvbnMgdG8gZ2dwbG90DQoNCldlIGhhdmUgdXNlZCBnZ3Bsb3QgZXh0ZW5zaW9ucyAqKntnZ2FuaW1hdGV9KiogdG8gY3JlYXRlIGFuaW1hdGVkIGdyYXBocyBhbmQgKip7Z2dyaWRnZXN9KiogdG8gY3JlYXRlIHJpZGdlbGluZSBncmFwaHMgdG8gY29tcGFyZSBtdWx0aXBsZSBkZW5zaXRpZXMuIFRoZXJlIGFyZSBzZXZlcmFsIG90aGVyIGltcG9ydGFudCBnZ3Bsb3QgZXh0ZW5zaW9ucyB0aGF0IGVuaGFuY2UgdGhlIGJhc2ljIGdncGxvdHMuDQoNCi0gICBnZ2RlbmRybyAtIGNvbnRyb2xzIHRoZSBhcHBlYXJhbmNlIGFuZCBkaXNwbGF5IG9mIHlvdXIgY2x1c3RlciBhbmFseXNlcw0KDQotICAgZ2d0aGVtZXMgLSBjb250YWlucyB0aGVtZXMgYW5kIHNjYWxlcyB0aGF0IGVuaGFuY2UgdGhlIHN0YW5kYXJkIGdncGxvdHMuDQoNCi0gICBnZ3B1YnIgLSBtYWtlcyBpdCBlYXN5IHRvIHByb2R1Y2UgcHVibGljYXRpb24tcmVhZHkgcGxvdHMgdXNpbmcgZ2dwbG90Lg0KDQotICAgUGxvdGx5IC0gYnJpbmdzIGludGVyYWN0aXZpdHkgdG8gZ2dwbG90cy4gV2Ugd2lsbCBzcGVuZCBhIHdlZWsgb24gcGxvdGx5KCkuDQoNCi0gICBwYXRjaHdvcmsgLSBhcnJhbmdlcyBtdWx0aXBsZSBSIHBsb3RzIG9uIHRoZSBzYW1lIGdyYXBoaWNzIHBhZ2UNCg0KLSAgIGdnbWFwIC0gaXMgYSBwb3dlcmZ1bCBwYWNrYWdlIGZvciB2aXN1YWxpemluZyBzcGF0aWFsIGRhdGEgYW5kIG1vZGVscy4gSXQgbGF5ZXJzIGRhdGEgb24gdG9wIG9mIHN0YXRpYyBtYXBzIGZyb20gcG9wdWxhciBvbmxpbmUgc291cmNlcy4gV2Ugd2lsbCB1c2UgdGhlc2UgcGFja2FnZXMgdG8gbWFrZSBtYXBzIGxhdGVyLg0KDQotICAgZ2dyZXBlbCAtIGdpdmVzIGdncGxvdDIgdXNlcnMgZ3JlYXRlciBjb250cm9sIG92ZXIgaG93IHRleHQgbGFiZWxzIGFwcGVhciBpbiB0aGVpciBjaGFydHMuDQoNCi0gICBnZ2NvcnJwbG90IC0gY29udHJvbHMgdGhlIGFwcGVhcmFuY2Ugb2YgdGhlIG1hdHJpeCwgZnJvbSBhbHRlcmluZyB0aGUgY29sb3IsIHNoYXBlLCBvciBzaXplIG9mIHRoZSBib3hlcyAoYXMgaW4gdGhlIGNpcmNsZS1tYXRyaXggYWJvdmUpLCB0byBhZGRpbmcgY29lZmZpY2llbnQgbGFiZWxzLCByZW9yZGVyaW5nIHRoZSBtYXRyaXggYWNjb3JkaW5nIHRvIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLCBhbmQgc28gb24uDQoNCi0gICBHR2FsbHkgLSBicmluZ3MgdG9nZXRoZXIgbWFueSB1c2VmdWwgYWRkaXRpb25hbCB2aXN1YWxpemF0aW9uIGZ1bmN0aW9uYWxpdHksIGFsbCBpbiBvbmUgcGFja2FnZS4NCg0KLSAgIGdnaXJhcGggLWlzIGFuIGh0bWx3aWRnZXQgdGhhdCBjYW4gYmUgZXh0ZW5kZWQgdG8gYW4gZXhpc3RpbmcgZ2dwbG90MiBzdWNoIGFzIGJhciBjaGFydCwgc2NhdHRlcnBsb3QsIGJveHBsb3QsIG1hcCwgZXRjLiwgYW5kIGRvZXMgdGhpbmdzIGxpa2UgZGlzcGxheWluZyBhIHRvb2x0aXAgb2YgeW91ciBjaG9pY2UuDQoNCiMgU2F2ZSBgZ2dwbG90YCBJbWFnZXMNCg0KQSBgZ2dwbG90YCBjYW4gYmUgc2F2ZWQgdG8gZGlmZmVyZW50IGZpbGUgZm9ybWF0cywgaW5jbHVkaW5nIFBERiwgU1ZHIHZlY3RvciBmaWxlcywgUE5HLCBUSUZGLCBKUEVHLCBldGMuDQoNCldlIGNhbiBlaXRoZXIgcHJpbnQgZGlyZWN0bHkgYSBgZ2dwbG90YCBpbnRvIGBQTkcvUERGYCBmaWxlcyBvciB1c2UgdGhlIGNvbnZlbmllbnQgZnVuY3Rpb24gYGdnc2F2ZSgpYCBmb3Igc2F2aW5nIGEgYGdncGxvdGAuDQoNClRoZSBkZWZhdWx0IG9mIGBnZ3NhdmUoKWAgaXMgdG8gZXhwb3J0IHRoZSBsYXN0IHBsb3QgdGhhdCB5b3UgZGlzcGxheWVkLCB1c2luZyB0aGUgc2l6ZSBvZiB0aGUgY3VycmVudCBncmFwaGljcyBkZXZpY2UuIEl0IGFsc28gZ3Vlc3NlcyB0aGUgdHlwZSBvZiBncmFwaGljcyBkZXZpY2UgZnJvbSB0aGUgZXh0ZW5zaW9uLg0KDQojIyBHZW5lcmFsIFN0ZXBzDQoNClRoZSBzdGFuZGFyZCBwcm9jZWR1cmUgdG8gc2F2ZSBhbnkgZ3JhcGhpY3MgZnJvbSBSIGlzIGFzIGZvbGxvd3M6DQoNCi0gICBPcGVuIGEgZ3JhcGhpYyBkZXZpY2UgdXNpbmcgb25lIG9mIHRoZSBmb2xsb3dpbmcgZnVuY3Rpb25zOg0KDQogICAgLSAgIGBwZGYo4oCcci1ncmFwaGljcy5wZGbigJ0pYCwNCiAgICAtICAgYHN2ZyjigJxyLWdyYXBoaWNzLnN2Z+KAnSlgLA0KICAgIC0gICBgcG5nKOKAnHItZ3JhcGhpY3MucG5n4oCdKWAsDQogICAgLSAgIGB0aWZmKOKAnHItZ3JhcGhpY3MudGlmZuKAnSlgLA0KICAgIC0gICBganBlZyjigJxyLWdyYXBoaWNzLmpwZ+KAnSlgLCBldGMuDQoNCi0gICBBZGRpdGlvbmFsIGFyZ3VtZW50cyBpbmRpY2F0aW5nIHRoZSB3aWR0aCBhbmQgdGhlIGhlaWdodCAoaW4gaW5jaGVzKSBvZiB0aGUgZ3JhcGhpY3MgcmVnaW9uIGNhbiBiZSBhbHNvIHNwZWNpZmllZCBpbiB0aGUgbWVudGlvbmVkIGZ1bmN0aW9uLg0KDQotICAgQ3JlYXRlIGFuZCBwcmludCBhIHBsb3QuIENsb3NlIHRoZSBncmFwaGljIGRldmljZSB1c2luZyB0aGUgZnVuY3Rpb24gYGRldi5vZmYoKWAuDQoNCiMjIFNhdmUgYGdncGxvdGAgaW50byBhIFBERiBGaWxlDQoNClRoZSBmb2xsb3dpbmcgY29kZSBpbGx1c3RyYXRlcyBob3cgdG8gc2F2ZSBhIGdncGxvdCBpbiBhIGZvbGRlciBpbiBQREYgZm9ybWF0Lg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KIyBzY2F0dGVyIHBsb3RzDQppcmlzLnNjYXR0ZXIgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFNlcGFsLldpZHRoKSkgKyANCiAgICAgICAgICAgZ2VvbV9wb2ludCgpDQojIyBib3gtcGxvdA0KaXJpcy5ib3hwbG90IDwtIGdncGxvdChpcmlzLCBhZXMoU3BlY2llcywgU2VwYWwuTGVuZ3RoKSkgKyANCiAgZ2VvbV9ib3hwbG90KCkNCiMgUHJpbnQgcGxvdHMgdG8gYSBQREYgZmlsZTogb25lIHBhZ2UgcGVyIFBERiBmaWxlDQpwZGYoInNhdmVQREZnZ3Bsb3QucGRmIikgICAjIFNhdmUgdGhlIFBERiBmaWxlIGluIGdncGxvdCBmb2xkZXIuDQpwcmludChpcmlzLnNjYXR0ZXIpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGDQpwcmludChpcmlzLmJveHBsb3QpICAgICAjIFBsb3QgMiAtLS0+IGluIHRoZSBzZWNvbmQgcGFnZSBvZiB0aGUgUERGDQpkZXYub2ZmKCkgDQpgYGANCg0KIyMgU2F2ZSBnZ3Bsb3Qgd2l0aCBgZ2dzYXZlKClgDQoNCkl0J3MgYWxzbyBwb3NzaWJsZSB0byBtYWtlIGEgZ2dwbG90IGFuZCBzYXZlIGl0IGZyb20gdGhlIHNjcmVlbiB1c2luZyB0aGUgZnVuY3Rpb24gYGdnc2F2ZSgpYC4NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCiMgMS4gQ3JlYXRlIGEgcGxvdDogZGlzcGxheWVkIG9uIHRoZSBzY3JlZW4gKGJ5IGRlZmF1bHQpDQpnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpICsgZ2VvbV9wb2ludCgpDQojIDIuMS4gU2F2ZSB0aGUgcGxvdCB0byBhIHBkZg0KZ2dzYXZlKCJtdGNhcm15cGxvdC5wZGYiKQ0KIyAyLjIgT1Igc2F2ZSBpdCB0byBwbmcgZmlsZQ0KZ2dzYXZlKCJtdGNhcm15cGxvdC5wbmciKQ0KYGBgDQoNCldlIGNhbiBhbHNvIHNhdmUgbXVsdGlwbGUgcGxvdHMgaW4gdGhlIHNhbXBsZSBmb3JtYXQgdG8gYSBzaW5nbGUgZmlsZS4gV2UgY2FuIHVzZSBgcGxvdF9ncmlkKClgIGluICoqe2Nvd3Bsb3R9KiogdG8gbWFrZSB0d28gZmlndXJlcyBvbiB0aGUgc2FtZSBncmFwaGljIHBhZ2UgYW5kIHRoZW4gdXNlIGBnZ3NhdmUoKWAgdG8gc2F2ZSBpdCB0byBhIHNpbmdsZSBmaWxlLg0KDQpgYGB7cn0NCiMgDQpwMSA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpICsgZ2VvbV9wb2ludCgpDQpwMiA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMod3QpKSArIGdlb21faGlzdG9ncmFtKCkNCmNvbWJpbmVkUGxvdCA8LSBwbG90X2dyaWQocDEsIHAyLCBsYWJlbHM9YygiQSIsICJCIiksIG5jb2wgPSAyLCBucm93ID0gMSkNCiMjDQpnZ3NhdmUoIkNvbWJpbmVkUGxvdC5wbmciLCBwbG90ID0gY29tYmluZWRQbG90KQ0KYGBgDQoNCiMgVGhlIFJvbGUgb2YgQ29sb3IgaW4gRWZmZWN0aXZlIFZpc3VhbGl6YXRpb24NCg0KU3Vic3RhbnRpYWwgcmVzZWFyY2ggc2hvd3MgdGhhdCBjb2xvciBwbGF5cyBhIHBpdm90YWwgcm9sZSBpbiBvdXIgdmlzdWFsIGV4cGVyaWVuY2VzLiBJbiB0aGlzIGNsYXNzLCB3ZSB1c2UgY29sb3JzIGZvciB0d28gbWFpbiBwdXJwb3NlczogZW5jb2RpbmcgYW5kIGhpZ2hsaWdodGluZyBpbmZvcm1hdGlvbi4NCg0KV2UgdXNlIGRpZmZlcmVudCBjb2xvcnMgdG8gZGVub3RlIGRpZmZlcmVudCB2YWx1ZXMgb2YgYSB2YXJpYWJsZS4gVGhlcmUgYXJlIG1hbnkgY29udGludW91cyBhbmQgZGlzY3JldGUgY29sb3IgcGFsZXR0ZXMgYXZhaWxhYmxlIGluIGRpZmZlcmVudCBSIGxpYnJhcmllcyB0aGF0IGNhbiBiZSB1c2VkIG9uIGRpZmZlcmVudCBvY2Nhc2lvbnMuIEhvd2V2ZXIsIHdlIG5lZWQgdG8gcGF5IHZlcnkgc3BlY2lhbCBhdHRlbnRpb24gdG8gdGhlIGNhc2VzIHdoZW4gY29sb3JzIGFyZSB1c2VkIGZvciBlbmNvZGluZyBiZWNhdXNlIHdlIHNlZSBjb2xvcnMgZGlmZmVyZW50bHkgLSBwZW9wbGUgd2l0aCB2aXNpb24gZGVmaWNpZW5jeSBhcmUgbGVzcyBzZW5zaXRpdmUgdG8gc29tZSBvZiB0aGUgY29sb3JzLiBUaGUgbmV4dCBmaWd1cmUgaWxsdXN0cmF0ZXMgdGhlIG1ham9yIHR5cGVzIG9mIGNvbG9yIGJsaW5kbmVzcy4NCg0KYGBge3IgZmlnLmFsaWduID0gJ2NlbnRlcicsIG91dC53aWR0aCA9ICc4MCUnfQ0KaW5jbHVkZV9ncmFwaGljcygiQ29sb3JCbGluZG5lc3NUeXBlcy5wbmciKQ0KYGBgDQoNCldoYXQgY29sb3JzIHNob3VsZCB5b3UgdXNlPyBUaGVyZSBhcmUgZGlmZmVyZW50IGNvbG9yLXBpY2tlciB0b29scyBhdmFpbGFibGUgZm9yIHVzIHRvIGNob29zZSBjb2xvcnMgd2hpbGUgbWFraW5nIHN1cmUgdGhhdCB0aGUgY29sb3IgcGFsZXR0ZSBpcyBhY2Nlc3NpYmxlLiBIZXJlIGlzIGEgc2V0IG9mIDggcGFpcnMgb2YgY29udHJhc3RpbmcgY29sb3JzIHRoYXQgbWFpbnRhaW4gdGhlaXIgY29udHJhc3QgZm9yIHBlb3BsZSB3aG8gYXJlIGNvbG9yYmxpbmQuDQoNCmBgYHtyIGZpZy5hbGlnbiA9ICdjZW50ZXInLCBvdXQud2lkdGggPSAnODAlJ30NCmluY2x1ZGVfZ3JhcGhpY3MoInItY29sb3ItcGFsZXR0ZXMucG5nIikNCmBgYA0KDQpGb3IgZXhhbXBsZSwgdGhlIGZvbGxvd2luZyB0aHJlZSBzYW1wbGUgcGFsZXR0ZXMgYXJlIGNvbG9yYmxpbmQtZnJpZW5kbHkuDQoNCi0gICAqKklCTSBEZXNpZ24gTGlicmFyeSoqDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9MX0NCnBhcihtZnJvdz1jKDEsNSksIG9tYT1jKDAsMCwwLDApLCBtYXIgPSBjKDEsMC41LDEsMC41KSkNCnBsb3QoTlVMTCwgdHlwZT0ibiIsIHhsaW09YygtMSwxKSwgeWxpbT1jKC0xLDEpLCBheGVzID0gRkFMU0UsIHhsYWIgPSAiIiwgeWxhYiA9ICIiKQ0KcmVjdCh4bGVmdCA9IC0xLCB5Ym90dG9tID0gLTAuNSwgeHJpZ2h0ID0xLCB5dG9wID0gMC41LCBsdHkgPSAxLCBjb2wgPSAiIzY0OEZGRiIpDQp0ZXh0KDAsMCwgIiM2NDhGRkYiKQ0KIyMNCnBsb3QoTlVMTCwgdHlwZT0ibiIsIHhsaW09YygtMSwxKSwgeWxpbT1jKC0xLDEpLCBheGVzID0gRkFMU0UsIHhsYWIgPSAiIiwgeWxhYiA9ICIiKQ0KcmVjdCh4bGVmdCA9IC0xLCB5Ym90dG9tID0gLTAuNSwgeHJpZ2h0ID0xLCB5dG9wID0gMC41LCBsdHkgPSAxLCBjb2wgPSAiIzc4NUVGMCIpDQp0ZXh0KDAsMCwgIiM3ODVFRjAiKQ0KIyMNCnBsb3QoTlVMTCwgdHlwZT0ibiIsIHhsaW09YygtMSwxKSwgeWxpbT1jKC0xLDEpLCBheGVzID0gRkFMU0UsIHhsYWIgPSAiIiwgeWxhYiA9ICIiKQ0KcmVjdCh4bGVmdCA9IC0xLCB5Ym90dG9tID0gLTAuNSwgeHJpZ2h0ID0xLCB5dG9wID0gMC41LCBsdHkgPSAxLCBjb2wgPSAiI0RDMjY3RiIpDQp0ZXh0KDAsMCwgIiNEQzI2N0YiKQ0KIyMNCnBsb3QoTlVMTCwgdHlwZT0ibiIsIHhsaW09YygtMSwxKSwgeWxpbT1jKC0xLDEpLCBheGVzID0gRkFMU0UsIHhsYWIgPSAiIiwgeWxhYiA9ICIiKQ0KcmVjdCh4bGVmdCA9IC0xLCB5Ym90dG9tID0gLTAuNSwgeHJpZ2h0ID0xLCB5dG9wID0gMC41LCBsdHkgPSAxLCBjb2wgPSAiI0ZFNjEwMCIpDQp0ZXh0KDAsMCwgIiNGRTYxMDAiKQ0KIyMNCnBsb3QoTlVMTCwgdHlwZT0ibiIsIHhsaW09YygtMSwxKSwgeWxpbT1jKC0xLDEpLCBheGVzID0gRkFMU0UsIHhsYWIgPSAiIiwgeWxhYiA9ICIiKQ0KcmVjdCh4bGVmdCA9IC0xLCB5Ym90dG9tID0gLTAuNSwgeHJpZ2h0ID0xLCB5dG9wID0gMC41LCBsdHkgPSAxLCBjb2wgPSAiI0ZGQjAwMCIpDQp0ZXh0KDAsMCwgIiNGRkIwMDAiKQ0KYGBgDQoNCi0gICAqKldvbmcncyBQYWxldHRlKioNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD0xfQ0KcGFyKG1mcm93PWMoMSw4KSwgb21hPWMoMCwwLDAsMCksIG1hciA9IGMoMSwwLjUsMSwwLjUpKQ0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjMDAwMDAwIikNCnRleHQoMCwwLCAiIzAwMDAwMCIsIGNvbCA9ICJ3aGl0ZSIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjRTY5RjAwIikNCnRleHQoMCwwLCAiI0U2OUYwMCIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjNTZCNEU5IikNCnRleHQoMCwwLCAiIzU2QjRFOSIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjMDA5RTczIikNCnRleHQoMCwwLCAiIzAwOUU3MyIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjRjBFNDQyIikNCnRleHQoMCwwLCAiI0YwRTQ0MiIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjMDA3MkIyIikNCnRleHQoMCwwLCAiIzAwNzJCMiIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjRDU1RTAwIikNCnRleHQoMCwwLCAiI0Q1NUUwMCIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjQ0M3OUE3IikNCnRleHQoMCwwLCAiI0NDNzlBNyIpDQpgYGANCg0KLSAgICoqUGFsJ3MgUGFsbGV0ZSoqDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9MX0NCnBhcihtZnJvdz1jKDEsOCksIG9tYT1jKDAsMCwwLDApLCBtYXIgPSBjKDEsMC41LDEsMC41KSkNCg0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjMzMyMjg4IikNCnRleHQoMCwwLCAiIzMzMjI4OCIsIGNvbCA9ICJ3aGl0ZSIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjMTE3NzMzIikNCnRleHQoMCwwLCAiIzExNzczMyIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjNDRBQTk5IikNCnRleHQoMCwwLCAiIzQ0QUE5OSIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjODhDQ0VFIikNCnRleHQoMCwwLCAiIzg4Q0NFRSIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjRERDQzc3IikNCnRleHQoMCwwLCAiI0REQ0M3NyIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjQ0M2Njc3IikNCnRleHQoMCwwLCAiI0NDNjY3NyIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjQUE0NDk5IikNCnRleHQoMCwwLCAiI0FBNDQ5OSIpDQojIw0KcGxvdChOVUxMLCB0eXBlPSJuIiwgeGxpbT1jKC0xLDEpLCB5bGltPWMoLTEsMSksIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpDQpyZWN0KHhsZWZ0ID0gLTEsIHlib3R0b20gPSAtMC41LCB4cmlnaHQgPTEsIHl0b3AgPSAwLjUsIGx0eSA9IDEsIGNvbCA9ICIjODgyMjU1IikNCnRleHQoMCwwLCAiIzg4MjI1NSIsIGNvbCA9ICJ3aGl0ZSIpDQpgYGANCg0KV2UgY2FuIGZpbmQgbW9yZSBSIGNvbG9yIHBhbGV0dGVzIGZyb20gPGh0dHBzOi8vci1jaGFydHMuY29tL2NvbG9yLXBhbGV0dGVzLz4uDQoNCg0KQXMgYW4gIGV4YW1wbGUsIHdlIHVzZSB0aGUgYWJvdmUgY29sb3IgYmxpbmQgZnJpZW5kbHkgY29sb3Igc2NoZW1lIGFuZCBkcmF3IHZhcmlvdXMgZGVuc2l0eSBjdXJ2ZXMuDQoNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD00fQ0KaXJpczAgPSBpcmlzDQpUeXBlID0gYyhwYXN0ZShpcmlzJFNwZWNpZXMsIi5TZXBhbC5MZW5ndGgiLCBzZXAgPSAiIikscGFzdGUoaXJpcyRTcGVjaWVzLCIuU2VwYWwuV2lkdGgiLCBzZXAgPSAiIikpDQpNZWFzdXJlID0gYyhpcmlzJFNlcGFsLkxlbmd0aCAsaXJpcyRTZXBhbC5XaWR0aCkNCmlyaXNOZXcgPSBkYXRhLmZyYW1lKFR5cGUgPSBUeXBlLCBNZWFzdXJlID0gTWVhc3VyZSkNCmNvbHMxID0gYygiIzMzMjI4OCIsIiMxMTc3MzMiLCIjNDRBQTk5IiwiIzg4Q0NFRSIsIiNERENDNzciLCIjQ0M2Njc3IikNCmNvbHMzID0gYygiI0FBNDQ5OSIsIiM4ODIyNTUiKQ0KcCA9IGdncGxvdCgpICsgDQogICAgICBnZW9tX2RlbnNpdHkoZGF0YSA9IGlyaXNOZXcsIGFlcyh4ID0gTWVhc3VyZSwgY29sb3IgPSBUeXBlKSwgbHdkID0gMSkgKyANCiAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xzMSkgKw0KICAgICAgZ2d0aXRsZSgiTXVsdGlwbGUgRGVuc2l0eSBDdXJ2ZXMiKSArDQogICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSwgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAibmF2eSIpKQ0KcA0KYGBgDQoNCg0KU29tZSBwZW9wbGUgbGlrZSBmaWxsZWQgZGVuc2l0eSBjdXJ2ZXMuIGBhbHBoYWAgaXMgYSBmdW5jdGlvbiBpbiB0aGUgbGlicmFyeSBvZiBgZ2dwbG90MmAuDQoNCg0KYGBge3J9DQpwMSA9IGdncGxvdChkYXRhID0gaXJpc05ldywgYWVzKHggPSBNZWFzdXJlLCBjb2xvciA9IFR5cGUsIGZpbGwgPSBUeXBlKSkgKyANCiAgICAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMjUsIGx3ZCA9IDEuNSkgKw0KICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sczEpICsgDQogICAgICBnZ3RpdGxlKCJNdWx0aXBsZSBGaWxsZWQgRGVuc2l0eSBDdXJ2ZXMiKSArDQogICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSwgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAicHVycGxlIikpDQoNCnAxDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==