Import and check your data
Type the following line into your script file. Execute the command as
you did in the previous module by placing the cursor anywhere on the
line and typing ctrl-Enter (Windows) or cmd-Enter (Mac).
# Save an imported data frame into a named variable
gapminder_data <- read.csv("data/gapminder_data_2007.csv")
N.B. stringsAsFactors=
: A common argument for
read.csv
was stringsAsFactors=
, which could be
TRUE
or FALSE
. The default for R (v4.0+) is
stringsAsFactors=FALSE
as R will automatically treat
character data as categorical for the purposes of visualisation. Many
statistical methods or older versions of R, however, still require
conversion to factors. See the appendix below for more information on
creating factors.
When imported into R, the data from the csv file are translated into
an R object called a data frame. Data frames are simply
tables, organised into rows and columns. The columns have names taken
from the first row of the csv file, and each subsequent row of the csv
file becomes a row in the data frame.
We store the data frame in a named variable so that we can refer to
it later (i.e., perform analyses on it). We use the assignment operator,
as we did in our previous module.
After storing our data frame into a variable, you should always check
that the data have been imported correctly. Data entry errors can cause
R to make the wrong assumptions about your data. If you have a column of
numbers that contains even one accidental alphabetic character (typos do
happen) R will consider the whole column to be strings. Later, R will
give the wrong results when you perform statistical analyses on these
data (or it will refuse to perform them at all).
Use the following commands to inspect your imported data:
# Write the first few lines of a data frame to the console with function head
head(gapminder_data)
#> country continent year lifeExp pop gdpPercap
#> 1 Afghanistan Asia 1952 28.801 8425333 779.4453
#> 2 Afghanistan Asia 1957 30.332 9240934 820.8530
#> 3 Afghanistan Asia 1962 31.997 10267083 853.1007
#> 4 Afghanistan Asia 1967 34.020 11537966 836.1971
#> 5 Afghanistan Asia 1972 36.088 13079460 739.9811
#> 6 Afghanistan Asia 1977 38.438 14880372 786.1134
# Write the last few lines of a data frame to the console with function tail
tail(gapminder_data)
#> country continent year lifeExp pop gdpPercap
#> 1699 Zimbabwe Africa 1982 60.363 7636524 788.8550
#> 1700 Zimbabwe Africa 1987 62.351 9216418 706.1573
#> 1701 Zimbabwe Africa 1992 60.377 10704340 693.4208
#> 1702 Zimbabwe Africa 1997 46.809 11404948 792.4500
#> 1703 Zimbabwe Africa 2002 39.989 11926563 672.0386
#> 1704 Zimbabwe Africa 2007 43.487 12311143 469.7093
# Display the number of lines, the column names, and the data type of each
# column, with function str (short for 'structure')
str(gapminder_data)
#> 'data.frame': 1704 obs. of 6 variables:
#> $ country : chr "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
#> $ continent: chr "Asia" "Asia" "Asia" "Asia" ...
#> $ year : int 1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 ...
#> $ lifeExp : num 28.8 30.3 32 34 36.1 ...
#> $ pop : int 8425333 9240934 10267083 11537966 13079460 14880372 12881816 13867957 16317921 22227415 ...
#> $ gdpPercap: num 779 821 853 836 740 ...
Each column in a data frame is associated with a data type
chr, int, or num.
These indicate what kind of data R identified in the input file. Columns
that are chr contain strings (characters), columns that
are int contain integers (whole numbers) and columns
that are num contain numbers with a decimal part. Always check that
these properties of the imported columns are correct for your data set.
If they are not, you must locate and correct any errors in your csv
file.
You can see the same information about the structure of a data frame
in the Environment panel of RStudio (upper-right of screen; Environment
tab). When you successfully import a csv file into a variable with
read.csv
, the resulting data frame appears in the
Environment pane. Click the blue arrow beside the object to display the
details of its structure.
Creating graphs in R
There are a variety of complex analyses that we can perform on a data
frame using R’s built-in statistical functions and those available in
additional packages and libraries. We will explore many of these
techniques in later modules. However, an effective first step in getting
to know a data set is to generate plots and graphs to represent visually
the patterns in the data.
Simple plots - the histogram
A histogram shows the frequency distribution of a
data set. That is, it shows counts of the different values of the
dependent variable (or ranges of values, for continuous variables). We
generate this graph with function hist
. The graph will be
displayed in the Plots tab of RStudio’s lower-right pane.
# Histogram of life expectancy values from gapminder
hist(gapminder_data$lifeExp)
We see that the distribution of life expectancy is approximately
bell-shaped, with many scores between 65 and 85, and a small number of
extreme values greater than 80 or less than 30.
Boxplots
The boxplot allows us to compare distribution information between
groups. For example, we can compare life expectancy for the different
continents.
The R function boxplot
accepts two arguments.
The first argument is the formula. This is a
complex, yet very common, argument format for R statistical functions.
The formula describes a linear model for a data set with the general
structure: dependent or predicted variable ~ independent
variables or predictors, using columns names from the data
frame. The ~ (tilde) is read as “depends on” or “is predicted by”. For
our example, we are interested in the way that life expectancy is
dependent on the continent, so we specify our formula as lifeExp
~ continent. We will see more complex examples of the formula
argument later in the semester.
The second argument to boxplot is the data frame.
boxplot(lifeExp ~ continent, gapminder_data)
Boxplots efficiently illustrate both the central tendency and the
variability of a data set. Each grey box extends from the first quartile
to the third quartile of its input values. The dark line across the box
is at the median. The two thin lines outside the qrey box show the
values of the minimum and maximum scores, excluding extreme outliers. If
extreme outliers are present, they are shown as small circles. This
figure clearly illustrates that, in the gapminder data, life expectancy
– both central tendency and variablity – is not the same for all
continents.
Plotting with ggplot2
The hist
and boxplot
functions are part of
Base R. They are useful, but for more elaborate, publication-quality
graphs, we can use the third-party library ggplot
contained in package ggplot2. The ggplot library is a
very popular data visualisation tool based on an elaborate symbolic
system called the ‘Grammar of Graphics’.
The syntax of ggplot is complex, and we will concentrate on the
foundations in this module. For additional detail, see the Data
Visualisation chapter in the R for Data Science online text, at https://r4ds.had.co.nz/data-visualisation.html.
Semantics of ggplot
You can think of a ggplot graph as being built as a sequence of
layers. On the bottom is the base of the graph, then the axes and the
data are layered on, then titles and notations and other features. A
ggplot command reflects this layered structure.
Building a graph
To use the ggplot library, we must install the ggplot2 package (once
on a computer) and invoke the library command (for every RStudio
session).
# Once on any computer
install.packages(ggplot2)
# Once for any RStudio session
library(ggplot2)
Every graph represents a data frame. The base part of any ggplot
command is a call to function ggplot()
passing in the data
frame, assigned to function argument data
.
# The ggplot base layer
ggplot(data = gapminder_data)
If you run this command from the RStudio console or an R script, the
grey square shown above appears in the Plots pane. This indicates that
ggplot is ready to draw a figure – this is the bottom layer of a ggplot
graph.
To add x and y axes to the graph, we need to define the relationship
between informational elements in the data set (the variables we want to
plot) and visual elements in our graph (the axes). In ggplot this
relationship is a mapping. To initialise a mapping, we
identify a particular element of the graph (e.g. the x-axis) and assign
a particular element of the data (e.g. a column in the data frame) to
it. This assignment is called an aesthetic in the
Grammar of Graphics, and in ggplot we use function aes()
to
specify aesthetics.
Imagine that we wish to make a graph showing the relationship between
per capita GDP and life expectancy (two columns in gapminder_data). We
map the first variable to the x axis (argument x) of our graph and the
second to the y axis (argument y). This will add a new layer. We add
this new information to the ggplot() base call as shown below. Note that
we don’t need to use the $
operator here, as all column
names in a ggplot command apply to the supplied data frame.
ggplot(data = gapminder_data, mapping = aes(x = gdpPercap, y = lifeExp ))
We have added a new layer to our graph with axes and grid lines. Note
that the axes’ tic values are correctly formatted for the associated
data and the data frame column names are used as the axis labels (we
will see how to improve those labels later).
To add points to our graph, we specify a geometry
(another term from the Grammar of Graphics). There are many, many
available geometries in ggplot, corresponding to all the different sorts
of graphs – scatterplots, bar plots, pie charts, line graphs, etc. –
that you might wish to make. For our current graph, we wish to place a
point at the intersection of per capita GDP (our x axis) and life
expectancy (our y axis) for each row in the input data frame. To add
this geometry to ggplot append geom_point()
to your current
ggplot command using the +
operator. It is conventional to
place each chunk of the ggplot command on its own line in the code.
# Add points (a 'geometry') to the graph
ggplot(data = gapminder_data, mapping = aes(x = gdpPercap, y = lifeExp )) +
geom_point()
This type of graph (usually called a scatterplot)
illustrates the relationship between two dependent variables. Even from
this very simple figure we can see that there is a general tendency for
higher per capita GDP to be associated with higher life expectancy in
the gapminder data.
Like most functions, geom_point
can accept arguments
that modify its behaviour. The argument colour
determines the colour of the points to be drawn, and can be assigned any
of R’s built-in colour names (call function colours()
to
list all possible values) or a hexidecimal RGB code (see for example, https://r-charts.com/colors/).
ggplot(data = gapminder_data, mapping = aes(x = gdpPercap, y = lifeExp )) +
geom_point(colour = 'tomato')
This livens up our plot, but it doesn’t acutally add any new
information. It is better technique to use colour to represent another
of our data variables. We might, for example, wish to use a different
colour for each continent, to see how the relationship between GDP and
life expectancy differs between continents. This requires defining a
mapping between a visual feature (colour) and an element of the data set
(column continent), so we initialise the mapping
property
with function aes
, in our call to
geom_point
.
ggplot(data = gapminder_data, mapping = aes(x = gdpPercap, y = lifeExp )) +
geom_point(mapping = aes(colour = continent))
This graph illustrates clearly that, in the gapminder data, life
expectancy and per capita GDP vary substantially between continents.
You should carefully compare the two preceding graphs. In the first,
we simply set the colour argument of
functiongeom_point
. In the second, we set the
mapping argument of geom_point
using
function aes
. In the former graph, all points are the same
colour. In the latter graph, the colour of each point depends on its
continent value. That is, we have mapped colour to
continent.
Choosing geometries
It is essential to select the correct type of graph (the correct
geometry in ggplot) for the data pattern you wish to illustrate.
Assume, for example, that you wish to show the change in life
expectancy across years, for the country of Denmark. First, we must
select out only the rows for Denmark from our data frame. (We will
consider selection in detail in next week’s module. For now, just note
that between the square brackets we provide row and column criteria for
selection, and an empty value for column means
all.)
We will then pass the selected data to ggplot as before, specifying
the mapping of the data to the x and y axes.
The graph will be illustrating a trend (change in a variable across
time). Trend graphs are usually drawn with a continuous line between the
plotted points. In ggplot, this is geometry geom_line
.
The complete code is:
# Select all rows where the country is equal to Denmark. Select all columns.
denmark_data <- gapminder_data[gapminder_data$country == "Denmark", ]
ggplot(data = denmark_data, mapping = aes(x = year, y = lifeExp)) +
geom_line()
We can use ggplot to produce a histogram for life expectancy (as we
did in Base R above) with geom_histogram
. For histograms we
only need to map the x axis, as the y axis represents, by default,
frequency. We can enhance the plot’s appearance by initialising
geom_histogram
arguments colour
which sets the
border around the bars on the graph, and fill
which sets
the interior of the bars on the graph.
ggplot(data = gapminder_data, mapping = aes(x = lifeExp)) +
geom_histogram(colour = "white", fill = "darkgreen")
Similarly, we can reproduce the boxplot above with
geom_boxplot
. In Base R we used a formula
to identify the dependent and independent variables for the boxplot.
With ggplot, we use a mapping to assign the DV to the x axis and the IV
to the y axis.
ggplot(data = gapminder_data, mapping = aes(x = continent, y = lifeExp)) +
geom_boxplot()
Exercise:
What would you predict to be the effect of swapping the values of x
and y in the call to aes
above? Test your prediction.
Refining the appearance of a plot
After we have built the foundation of our plot with data and
geometry, we can add further layers to modify other visual features. For
example, we can use function labs
to set the axis, legend,
and main titles of our plots. Consider the following enhancements to our
figure illustrating the relationship between GDP and life expectancy by
continent:
# NB: Multiple function arguments (as in labs below) can
# be placed on separate lines to improve readability
ggplot(data = gapminder_data, mapping = aes(x = gdpPercap, y = lifeExp )) +
geom_point(mapping = aes(colour = continent)) +
labs(x = "GDP Per Capita",
y = "Life Expectancy",
title = "Gap Minder Data 1952 to 2007",
colour = "Continent")
The code for ggplot formatting can get extremely complex, and the
full functionality is beyond the scope of this module. In addition,
there are many, many more geometries available, each with appropriate
arguments and mapping options.
The formal documentation for ggplot can be found at https://ggplot2.tidyverse.org/index.html. If you prefer
tutorials and galleries, there are many available online. Two good
places to start are http://www.cookbook-r.com/Graphs/ and https://www.r-graph-gallery.com/.
Saving ggplots
You can save figures made with ggplot to image files, which can then
be used in documents generated in MS Word or other text editors. We
first save the output of our ggplot command into a named variable (to R
a ggplot is a data object just like a number or a string). We then use
function ggsave
to export out plot as an image file. You
specify the image format by supplying an outfile name with the
corresponding file suffix (e.g. .jpg or .png). By default, the file is
saved into the working folder (in our case, the folder containing our
csv and script files).
# Save a ggplot to a variable. The syntax of the gpplot command is unaffected
gdp_lifeExp_plot <- ggplot(data = gapminder_data, mapping = aes(x = gdpPercap, y = lifeExp )) +
geom_point(mapping = aes(colour = continent)) +
xlab("GDP Per Capita") +
ylab("Life Expectancy") +
ggtitle("Gap Minder Data 1952 to 2007")
# Export the variable as an image file. Provide the file name and the ggplot object
ggsave(filename = "gdp_lifeExp_plot.png", gdp_lifeExp_plot)
#> Saving 7 x 5 in image
LS0tCnRpdGxlOiAiVmlzdWFsaXNpbmciCmRhdGU6ICJTZW1lc3RlciAyLCAyMDIzIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCgpsaWJyYXJ5KGtuaXRyKQoKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGNvbW1lbnQgPSAiIz4iLAogIGZpZy5wYXRoID0gImZpZ3VyZXMvMDIvIiwgIyB1c2Ugb25seSBmb3Igc2luZ2xlIFJtZCBmaWxlcwogIGNvbGxhcHNlID0gVFJVRSwKICBlY2hvID0gVFJVRQopCmBgYAoKPiAjIyMjIERhdGEgRm9jdXMKPgo+IE1vZHVsZXMgYW5kIEhhbmRvdXRzIDItLTcgY292ZXIgdG9waWNzIHRoYXQgYXJlIHBhcnQgb2YgdGhlIGRhdGEgYW5hbHlzaXMgam91cm5leSBhbmQgYXJlIGFsbCBpbnRlcnJlbGF0ZWQuIEVhY2ggbW9kdWxlIHdpbGwgaW50cm9kdWNlIG5ldyBjb250ZW50IGFuZCBleHBhbmQgb24gdGhlIG1hdGVyaWFsIGNvdmVyZWQgaW4gcHJldmlvdXMgbW9kdWxlcy4gRm9yIGV4YW1wbGUsIGluIE1vZHVsZSAyIHdlIGludHJvZHVjZSB0aGUgYmFzaWNzIG9mIHRoZSBSIHBsb3R0aW5nIHN5c3RlbXMuIFN1YnNlcXVlbnQgaGFuZG91dHMgaW5jbHVkZSBtb3JlIGFkdmFuY2VkIHBsb3R0aW5nIG9wdGlvbnMgYW5kIHRlY2huaXF1ZXMuCgpcCgpcCgo+ICMjIyMgQXNzb2NpYXRlZCBNYXRlcmlhbAo+Cj4gWm9vbSBOb3RlczogW1pvb20gbm90ZXMgMDIgLSBWaXN1YWxpc2luZyBEYXRhXSh6b29tX25vdGVzXzAyX3Zpc3VhbGlzZS5odG1sKQo+IAo+IFJlYWRpbmdzOgo+Cj4gLSBbUiBmb3IgRGF0YSBTY2llbmNlIC0gQ2hhcHRlciAxMl0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei90aWR5LWRhdGEuaHRtbCkKPiAtIFtSIGZvciBEYXRhIFNjaWVuY2UgLSBDaGFwdGVyIDExXShodHRwczovL3I0ZHMuaGFkLmNvLm56L2RhdGEtaW1wb3J0Lmh0bWwpCj4gLSBbUiBmb3IgRGF0YSBTY2llbmNlIC0gQ2hhcHRlciAzXShodHRwczovL3I0ZHMuaGFkLmNvLm56L2RhdGEtdmlzdWFsaXNhdGlvbi5odG1sKQoKCgojIyBUYWJ1bGFyIERhdGEKCkluIHRoaXMgbW9kdWxlIHdlIGJlZ2luIHRvIHdvcmsgd2l0aCBjb21wbGV0ZSB0YWJsZXMgb2YgZGF0YSwgYW5kIGxlYXJuIGhvdyB0byBtYWtlIGluZm9ybWF0aXZlIGdyYXBocy4gCgpXZSB3aWxsIHN0YXJ0IGJ5IGdldHRpbmcgb3VyIHNjaWVudGlmaWMgcmVzZWFyY2ggZGF0YSBpbnRvIFJTdHVkaW8uIENvbW1vbmx5LCB3ZSBmaXJzdCBlbnRlciBvdXIgZGF0YSBpbnRvIEV4Y2VsIGZvciBjbGVhbmluZyBhbmQgb3JnYW5pc2luZywgYW5kIHNhdmUgaXQgb3V0IGFzIGEgY3N2IChjb21tYSBzZXBhcmF0ZWQgdmFsdWVzKSBmaWxlLiBXZSB0aGVuIGltcG9ydCB0aGUgZmlsZSBpbnRvIFIgZm9yIGRhdGEgYW5hbHlzaXMuIFdlIHdpbGwgcHJvdmlkZSB0aGUgY3N2IGZpbGUgdG8gYmUgdXNlZCB3aXRoIHRoaXMgbW9kdWxlLgoKV2hlbiB5b3UgaGF2ZSBjb21wbGV0ZWQgdGhpcyBtb2R1bGUsIHlvdSBzaG91bGQgYmUgcmVhZHkgdG8gcHJvZHVjZSB0aGUgZ3JhcGhzIGFuZCBmaWd1cmVzIHlvdSB3aWxsIG5lZWQgZm9yIHlvdXIgaW4tY291cnNlIHJlc2VhcmNoIHByb2plY3RzLiBJZiB5b3UgcnVuIGludG8gYW55IHByb2JsZW1zIG9yIGhhdmUgYW55IHF1ZXN0aW9ucywgZW1haWwgdXMsIG9yIGRyb3AgdXMgYSBtZXNzYWdlIGluIHRoZSBSNFNTUCBjbGFzcyB0ZWFtIG9uIE1TIFRlYW1zLgoKXAoKIyMgUHJlcGFyYXRpb24gLSBEbyB0aGlzIHZlcnkgY2FyZWZ1bGx5CgpEb2N1bWVudCBvcmdhbmlzYXRpb24gaXMgdml0YWxseSBpbXBvcnRhbnQuIFdlIHN1Z2dlc3QgdGhhdCB5b3Uga2VlcCBhIHNlcGFyYXRlIGZvbGRlciBmb3IgZWFjaCBSNFNTUCBtb2R1bGUuIFRvIGRvIHRoaXMsIHlvdSBjYW4gc2V0IHVwIGEgZm9ybWFsIFJTdHVkaW8gcHJvamVjdCAoc2VlIGZvciBleGFtcGxlIGh0dHBzOi8vc3VwcG9ydC5yc3R1ZGlvLmNvbS9oYy9lbi11cy9hcnRpY2xlcy8yMDA1MjYyMDctVXNpbmctUHJvamVjdHMpLiBCdXQgaWYgeW91IGRvbid0IGZlZWwgcXVpdGUgcmVhZHkgZm9yIHRoYXQsIHlvdSBjYW4ganVzdCBjb2xsZWN0IHlvdXIgc2NyaXB0IGZpbGUgYW5kIGRhdGEgZmlsZXMgdG9nZXRoZXIgaW4gYSBmb2xkZXIuIFByb2NlZWQgYXMgZm9sbG93czoKCgoxLiBMYXVuY2ggUiBTdHVkaW8KCjIuIEZvbGxvd2luZyB0aGUgcHJvY2VkdXJlIGZyb20gKipVc2luZyBhIFNjcmlwdCBGaWxlKiogaW4gTW9kdWxlIDEgLSBJbnRybyB0byBSIGFuZCBSU3R1ZGlvLCBjcmVhdGUgYSBzY3JpcHQgZmlsZSBhbmQgc2F2ZSBpdCB0byB5b3VyIGRlc2t0b3Agb3Igb3RoZXIgbG9jYXRpb24gd2hlcmUgeW91IHdpbGwgYmUgYWJsZSB0byBmaW5kIGl0LgoKMy4gUXVpdCBSIFN0dWRpbwoKNC4gTG9jYXRlIHRoZSBzY3JpcHQgZmlsZSB5b3UganVzdCBtYWRlIChpdCB3aWxsIGhhdmUgYSAuUiBmaWxlIHN1ZmZpeCkuIAoKNS4gQ3JlYXRlIGEgbmV3IGZvbGRlciAobmFtZSBpdCBzb21ldGhpbmcgc2Vuc2libGUpIGFuZCBwbGFjZSB0aGUgc2NyaXB0IGZpbGUgaW4gaXQuCgo2LiBUaGUgc2FtcGxlIGNzdiBkYXRhIGZpbGUgdXNlZCBpbiB0aGlzIG1vZHVsZSBpcyBjYWxsZWQgKipnYXBtaW5kZXJfZGF0YV8yMDA3LmNzdioqLiBUaGlzIGZpbGUgY29udGFpbnMgbGlmZSBleHBlY3RhbmN5LCBwb3B1bGF0aW9uLCBhbmQgR0RQIHZhbHVlcyBmb3IgMTQyIGRpZmZlcmVudCBjb3VudHJpZXMuIFRoZSBkYXRhIGZvciBlYWNoIGNvdW50cnkgd2VyZSBtZWFzdXJlZCAxMiB0aW1lcyBiZXR3ZWVuIDE5NTIgYW5kIDIwMDcuIFRoZXNlIGRhdGEgYXJlIHNvdXJjZWQgZnJvbSB0aGUgR2FwTWluZGVyIGZvdW5kYXRpb24gKGh0dHBzOi8vd3d3LmdhcG1pbmRlci5vcmcvKS4KCj4gRG93bmxvYWRpbmcgdGhlIEdhcE1pbmRlciAyMDA3IGRhdGE6Cj4gCj4gT25lIG9wdGlvbiBmb3IgZG93bmxvYWRpbmcgdGhlIGRhdGEgaXMgdG8gdXNlIGBkb3dubG9hZC5maWxlKClgIGluIFIuIFlvdSBjYW4gdXNlIHRoaXMgY29tbWFuZCB0byBkb3dubG9hZCB0aGUgZmlsZSBpbnRvIHlvdXIgYGRhdGEvYCBkaXJlY3Rvcnk6IAo+IAo+IGBgYHtyLCBldmFsID0gRkFMU0V9Cj4gZG93bmxvYWQuZmlsZSh1cmwgPSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3J0aXMtdHJhaW5pbmcvMjAyMy1zMi1yNHNzcC9tYWluL2RvY3MvZGF0YS9nYXBtaW5kZXJfZGF0YV8yMDA3LmNzdiIsIAo+ICBkZXN0ZmlsZSA9ICJkYXRhL2dhcG1pbmRlcl9kYXRhXzIwMDcuY3N2IikKPiBgYGAKPiBfUmVtZW1iZXIgdGhhdCBSIGlzIGNhc2Ugc2Vuc2l0aXZlIHNvIGlmIHlvdSBkb24ndCBoYXZlIGEgZGlyZWN0b3J5IGV4YWN0bHkgY2FsbGVkIGBkYXRhL2AgbW9kaWZ5IHRoZSBjb21tYW5kIHRvIG1hdGNoIHlvdXIgZGlyZWN0b3J5Ll8KPgo+IFRoZSBzZWNvbmQgb3B0aW9uIGlzIGluIGEgd2ViIGJyb3dzZXIgZ28gdG8gaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3J0aXMtdHJhaW5pbmcvMjAyMy1zMi1yNHNzcC9tYWluL2RvY3MvZGF0YS9nYXBtaW5kZXJfZGF0YV8yMDA3LmNzdiBhbmQgdGhlbiBzYXZlIHRoZSBwYWdlIHVzaW5nIGBTYXZlIEFzYCBhbmQgZ2l2ZSBpdCB0aGUgbmFtZSAiZ2FwbWluZGVyX2RhdGFfMjAwNy5jc3YiLiBTYXZlIGl0IHRvIHlvdXIgZGF0YSBsb2NhdGlvbi4KCjwhLS0KUmV0cmlldmUgdGhlIHNhbXBsZSBjc3YgZGF0YSBmaWxlIGZyb20gdGhlIGNvdXJzZSBbR29vZ2xlIERyaXZlIHNoYXJlZCBmb2xkZXJdKGh0dHBzOi8vZHJpdmUuZ29vZ2xlLmNvbS9kcml2ZS9mb2xkZXJzLzF0dGYxczgtdmtKTk9sSGRwaGZpMnpGeU1xNmdHRXZDeT91c3A9c2hhcmluZykuIFBsYWNlIHRoZSBjc3YgZmlsZSBpbnRvIHRoZSBmb2xkZXIgd2l0aCB5b3VyIHNjcmlwdCBmaWxlLgotLT4KCgo3LiBPcGVuIHRoZSBmb2xkZXIgYW5kIGRvdWJsZS1jbGljayAqKm9uIHlvdXIgc2NyaXB0IGZpbGUqKiB0byBvcGVuIGl0IGluIFJTdHVkaW8uCgpJZiB5b3Ugc2V0IHRoaW5ncyB1cCB0aGlzIHdheSwgUlN0dWRpbyB3aWxsIGJlIGFibGUgdG8gZmluZCB5b3VyIGRhdGEgZmlsZSBmb3IgdGhlIG5leHQgc3RlcCAtLSBpbXBvcnRpbmcgZGF0YS4KClwKCiMjIEltcG9ydGluZyBhIGRhdGEgZmlsZQoKVGhlIGZpcnN0IGZldyBsaW5lcyBvZiBvdXIgZGF0YSBmaWxlICoqZ2FwbWluZGVyX2RhdGFfMjAwNy5jc3YqKiBhcyBpdCBhcHBlYXJzIHdoZW4gb3BlbmVkIGluIEV4Y2VsLCBhcmUgc2hvd24gYmVsb3c6CgpgYGB7ciBmaWcwMSwgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGggPSAiNTAlIiwgZmlnLmNhcCA9ICJHYXBNaW5kZXIgRGF0YSIsIGZpZy5wb3MgPSAiSCIsIGVjaG8gPSBGQUxTRX0KaW5jbHVkZV9ncmFwaGljcygiaW1hZ2VzLzAyLWdhcG1pbmRlcl9kYXRhXzIwMDcucG5nIikKYGBgCgoKCldlIHdpbGwgaW1wb3J0IHRoaXMgZmlsZSBpbnRvIFIgZm9yIHNvbWUgcHJlbGltaW5hcnkgZGF0YSBhbmFseXNpcy4KClRvIGltcG9ydCBhIGNzdiBmaWxlIGludG8gUiB3ZSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBgcmVhZC5jc3ZgLiBMaWtlIHRoZSBSIGZ1bmN0aW9ucyB3ZSB1c2VkIGluIE1vZHVsZSAxLCBgcmVhZC5jc3ZgIGFjY2VwdHMgYW4gYXJndW1lbnQgYmV0d2VlbiBpdHMgcm91bmQgYnJhY2tldHMuIFRoZSBhcmd1bWVudCBpcyB0aGUgbmFtZSBvZiB0aGUgaW5wdXQgZmlsZS4gQmVjYXVzZSB0aGUgbmFtZSBvZiB0aGUgZmlsZSBpcyBhIHN0cmluZywgd2Ugc3Vycm91bmQgaXQgd2l0aCBkb3VibGUgcXVvdGVzLiBUaGUgY29tcGxldGUgY29tbWFuZCBpcyBzaG93biBiZWxvdy4KClwKCiMjIEltcG9ydCBhbmQgY2hlY2sgeW91ciBkYXRhCgpUeXBlIHRoZSBmb2xsb3dpbmcgbGluZSBpbnRvIHlvdXIgc2NyaXB0IGZpbGUuIEV4ZWN1dGUgdGhlIGNvbW1hbmQgYXMgeW91IGRpZCBpbiB0aGUgcHJldmlvdXMgbW9kdWxlIGJ5IHBsYWNpbmcgdGhlIGN1cnNvciBhbnl3aGVyZSBvbiB0aGUgbGluZSBhbmQgdHlwaW5nIGN0cmwtRW50ZXIgKFdpbmRvd3MpIG9yIGNtZC1FbnRlciAoTWFjKS4KCmBgYHtyIHJlYWQuY3N2fQojIFNhdmUgYW4gaW1wb3J0ZWQgZGF0YSBmcmFtZSBpbnRvIGEgbmFtZWQgdmFyaWFibGUKZ2FwbWluZGVyX2RhdGEgPC0gcmVhZC5jc3YoImRhdGEvZ2FwbWluZGVyX2RhdGFfMjAwNy5jc3YiKQpgYGAKCj4gTi5CLiBgc3RyaW5nc0FzRmFjdG9ycz1gOiBBIGNvbW1vbiBhcmd1bWVudCBmb3IgYHJlYWQuY3N2YCB3YXMgYHN0cmluZ3NBc0ZhY3RvcnM9YCwgd2hpY2ggY291bGQgYmUgYFRSVUVgIG9yIGBGQUxTRWAuIFRoZSBkZWZhdWx0IGZvciBSICh2NC4wKykgaXMgYHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0VgIGFzIFIgd2lsbCBhdXRvbWF0aWNhbGx5IHRyZWF0IGNoYXJhY3RlciBkYXRhIGFzIGNhdGVnb3JpY2FsIGZvciB0aGUgcHVycG9zZXMgb2YgdmlzdWFsaXNhdGlvbi4gTWFueSBzdGF0aXN0aWNhbCBtZXRob2RzIG9yIG9sZGVyIHZlcnNpb25zIG9mIFIsIGhvd2V2ZXIsIHN0aWxsIHJlcXVpcmUgY29udmVyc2lvbiB0byBmYWN0b3JzLiBTZWUgdGhlIGFwcGVuZGl4IGJlbG93IGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIGNyZWF0aW5nIGZhY3RvcnMuCgpXaGVuIGltcG9ydGVkIGludG8gUiwgdGhlIGRhdGEgZnJvbSB0aGUgY3N2IGZpbGUgYXJlIHRyYW5zbGF0ZWQgaW50byBhbiBSIG9iamVjdCBjYWxsZWQgYSAqKmRhdGEgZnJhbWUqKi4gRGF0YSBmcmFtZXMgYXJlIHNpbXBseSB0YWJsZXMsIG9yZ2FuaXNlZCBpbnRvIHJvd3MgYW5kIGNvbHVtbnMuIFRoZSBjb2x1bW5zIGhhdmUgbmFtZXMgdGFrZW4gZnJvbSB0aGUgZmlyc3Qgcm93IG9mIHRoZSBjc3YgZmlsZSwgYW5kIGVhY2ggc3Vic2VxdWVudCByb3cgb2YgdGhlIGNzdiBmaWxlIGJlY29tZXMgYSByb3cgaW4gdGhlIGRhdGEgZnJhbWUuIAoKV2Ugc3RvcmUgdGhlIGRhdGEgZnJhbWUgaW4gYSBuYW1lZCB2YXJpYWJsZSBzbyB0aGF0IHdlIGNhbiByZWZlciB0byBpdCBsYXRlciAoaS5lLiwgcGVyZm9ybSBhbmFseXNlcyBvbiBpdCkuIFdlIHVzZSB0aGUgYXNzaWdubWVudCBvcGVyYXRvciwgYXMgd2UgZGlkIGluIG91ciBwcmV2aW91cyBtb2R1bGUuIAoKQWZ0ZXIgc3RvcmluZyBvdXIgZGF0YSBmcmFtZSBpbnRvIGEgdmFyaWFibGUsIHlvdSBzaG91bGQgYWx3YXlzIGNoZWNrIHRoYXQgdGhlIGRhdGEgaGF2ZSBiZWVuIGltcG9ydGVkIGNvcnJlY3RseS4gRGF0YSBlbnRyeSBlcnJvcnMgY2FuIGNhdXNlIFIgdG8gbWFrZSB0aGUgd3JvbmcgYXNzdW1wdGlvbnMgYWJvdXQgeW91ciBkYXRhLiBJZiB5b3UgaGF2ZSBhIGNvbHVtbiBvZiBudW1iZXJzIHRoYXQgY29udGFpbnMgZXZlbiBvbmUgYWNjaWRlbnRhbCBhbHBoYWJldGljIGNoYXJhY3RlciAodHlwb3MgZG8gaGFwcGVuKSBSIHdpbGwgY29uc2lkZXIgdGhlIHdob2xlIGNvbHVtbiB0byBiZSBzdHJpbmdzLiBMYXRlciwgUiB3aWxsIGdpdmUgdGhlIHdyb25nIHJlc3VsdHMgd2hlbiB5b3UgcGVyZm9ybSBzdGF0aXN0aWNhbCBhbmFseXNlcyBvbiB0aGVzZSBkYXRhIChvciBpdCB3aWxsIHJlZnVzZSB0byBwZXJmb3JtIHRoZW0gYXQgYWxsKS4gCgpVc2UgdGhlIGZvbGxvd2luZyBjb21tYW5kcyB0byBpbnNwZWN0IHlvdXIgaW1wb3J0ZWQgZGF0YToKCmBgYHtyIGNoZWNrIDF9CiMgV3JpdGUgdGhlIGZpcnN0IGZldyBsaW5lcyBvZiBhIGRhdGEgZnJhbWUgdG8gdGhlIGNvbnNvbGUgd2l0aCBmdW5jdGlvbiBoZWFkCmhlYWQoZ2FwbWluZGVyX2RhdGEpCgojIFdyaXRlIHRoZSBsYXN0IGZldyBsaW5lcyBvZiBhIGRhdGEgZnJhbWUgdG8gdGhlIGNvbnNvbGUgd2l0aCBmdW5jdGlvbiB0YWlsCnRhaWwoZ2FwbWluZGVyX2RhdGEpCmBgYAoKYGBge3IgY2hlY2sgMn0KIyBEaXNwbGF5IHRoZSBudW1iZXIgb2YgbGluZXMsIHRoZSBjb2x1bW4gbmFtZXMsIGFuZCB0aGUgZGF0YSB0eXBlIG9mIGVhY2ggCiMgY29sdW1uLCB3aXRoIGZ1bmN0aW9uIHN0ciAoc2hvcnQgZm9yICdzdHJ1Y3R1cmUnKQpzdHIoZ2FwbWluZGVyX2RhdGEpCmBgYAoKRWFjaCBjb2x1bW4gaW4gYSBkYXRhIGZyYW1lIGlzIGFzc29jaWF0ZWQgd2l0aCBhIGRhdGEgdHlwZSAqKmNocioqLCAqKmludCoqLCBvciAqKm51bSoqLiBUaGVzZSBpbmRpY2F0ZSB3aGF0IGtpbmQgb2YgZGF0YSBSIGlkZW50aWZpZWQgaW4gdGhlIGlucHV0IGZpbGUuIENvbHVtbnMgdGhhdCBhcmUgKipjaHIqKiBjb250YWluIHN0cmluZ3MgKGNoYXJhY3RlcnMpLCBjb2x1bW5zIHRoYXQgYXJlICoqaW50KiogY29udGFpbiBpbnRlZ2VycyAod2hvbGUgbnVtYmVycykgYW5kIGNvbHVtbnMgdGhhdCBhcmUgbnVtIGNvbnRhaW4gbnVtYmVycyB3aXRoIGEgZGVjaW1hbCBwYXJ0LiBBbHdheXMgY2hlY2sgdGhhdCB0aGVzZSBwcm9wZXJ0aWVzIG9mIHRoZSBpbXBvcnRlZCBjb2x1bW5zIGFyZSBjb3JyZWN0IGZvciB5b3VyIGRhdGEgc2V0LiBJZiB0aGV5IGFyZSBub3QsIHlvdSBtdXN0IGxvY2F0ZSBhbmQgY29ycmVjdCBhbnkgZXJyb3JzIGluIHlvdXIgY3N2IGZpbGUuCgpZb3UgY2FuIHNlZSB0aGUgc2FtZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc3RydWN0dXJlIG9mIGEgZGF0YSBmcmFtZSBpbiB0aGUgRW52aXJvbm1lbnQgcGFuZWwgb2YgUlN0dWRpbyAodXBwZXItcmlnaHQgb2Ygc2NyZWVuOyBFbnZpcm9ubWVudCB0YWIpLiBXaGVuIHlvdSBzdWNjZXNzZnVsbHkgaW1wb3J0IGEgY3N2IGZpbGUgaW50byBhIHZhcmlhYmxlIHdpdGggYHJlYWQuY3N2YCwgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIGFwcGVhcnMgaW4gdGhlIEVudmlyb25tZW50IHBhbmUuIENsaWNrIHRoZSBibHVlIGFycm93IGJlc2lkZSB0aGUgb2JqZWN0IHRvIGRpc3BsYXkgdGhlIGRldGFpbHMgb2YgaXRzIHN0cnVjdHVyZS4KCmBgYHtyIGZpZzAyLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aCA9ICI1MCUiLCBmaWcuY2FwID0gIkRhdGEgRnJhbWUgaW4gRW52aXJvbm1lbnQgUGFuZSIsIGZpZy5wb3MgPSAiSCIsIGVjaG8gPSBGQUxTRX0KaW5jbHVkZV9ncmFwaGljcygiaW1hZ2VzLzAyLWRmX2luX2Vudl9wYW5lLnBuZyIpCmBgYAoKXAoKCiMjIFNlbGVjdGluZyBhbmQgdXNpbmcgY29sdW1ucyBvZiBkYXRhCgpJbiBib3RoIG9mIHRoZSBhYm92ZSBkaXNwbGF5cywgZWFjaCBjb2x1bW4gbmFtZSBpcyBwcmVmYWNlZCB3aXRoIGAkYC4gV2UgdXNlIHRoZSBgJGAgb3BlcmF0b3IgdG8gc2VsZWN0IGluZGl2aWR1YWwgY29sdW1ucyBvZiBkYXRhIGZyb20gYSBkYXRhIGZyYW1lLiBGb3IgZXhhbXBsZSwgdG8gc2VsZWN0IGp1c3QgdGhlIGxpZmUgZXhwZWN0YW5jeSB2YWx1ZXMgZnJvbSBvdXIgZGF0YSwgd2Ugc2F5IGBnYXBtaW5kZXJfZGF0YSRsaWZlRXhwYC4gKEJlIHN1cmUgdG8gdHlwZSB0aGUgY29sdW1uIG5hbWUgZXhhY3RseSBhcyBpdCBhcHBlYXJzIGluIHRoZSBvdXRwdXQgb2YgYHN0cmAgYW5kIGluIHRoZSBFbnZpcm9ubWVudCBwYW5lLikgV2UgY2FuIHVzZSB0aGlzIGV4cHJlc3Npb24gZGlyZWN0bHkgYXMgdGhlIGFyZ3VtZW50IHRvIGEgZnVuY3Rpb24sIG9yIHdlIGNhbiBzdG9yZSB0aGUgc2VsZWN0ZWQgY29sdW1uIGluIGEgdmFyaWFibGUgKHdoaWNoIHdpbGwgYmUgYSB2ZWN0b3IpIGZvciBsYXRlciBwcm9jZXNzaW5nLiAKCkZvciBleGFtcGxlOgoKYGBgIHtyIHNpbmdsZSBjb2x1bW4gZXhhbXBsZXN9CiMgUHJpbnQgdGhlIGZpcnN0IGZldyB2YWx1ZXMgb2YgY29sdW1uIHllYXIKaGVhZChnYXBtaW5kZXJfZGF0YSR5ZWFyKQoKIyBTdG9yZSBjb2x1bW4gY29udGluZW50IGluIGEgbmV3IHZhcmlhYmxlCmNvbnRpbmVudHMgPC0gZ2FwbWluZGVyX2RhdGEkY29udGluZW50CmBgYAoKXAoKIyMgQ3JlYXRpbmcgZ3JhcGhzIGluIFIKClRoZXJlIGFyZSBhIHZhcmlldHkgb2YgY29tcGxleCBhbmFseXNlcyB0aGF0IHdlIGNhbiBwZXJmb3JtIG9uIGEgZGF0YSBmcmFtZSB1c2luZyBSJ3MgYnVpbHQtaW4gc3RhdGlzdGljYWwgZnVuY3Rpb25zIGFuZCB0aG9zZSBhdmFpbGFibGUgaW4gYWRkaXRpb25hbCBwYWNrYWdlcyBhbmQgbGlicmFyaWVzLiBXZSB3aWxsIGV4cGxvcmUgbWFueSBvZiB0aGVzZSB0ZWNobmlxdWVzIGluIGxhdGVyIG1vZHVsZXMuIEhvd2V2ZXIsIGFuIGVmZmVjdGl2ZSBmaXJzdCBzdGVwIGluIGdldHRpbmcgdG8ga25vdyBhIGRhdGEgc2V0IGlzIHRvIGdlbmVyYXRlIHBsb3RzIGFuZCBncmFwaHMgdG8gcmVwcmVzZW50IHZpc3VhbGx5IHRoZSBwYXR0ZXJucyBpbiB0aGUgZGF0YS4KClwKCiMjIyBTaW1wbGUgcGxvdHMgLSB0aGUgaGlzdG9ncmFtCgpBIGhpc3RvZ3JhbSBzaG93cyB0aGUgKipmcmVxdWVuY3kgZGlzdHJpYnV0aW9uKiogb2YgYSBkYXRhIHNldC4gVGhhdCBpcywgaXQgc2hvd3MgY291bnRzIG9mIHRoZSAgZGlmZmVyZW50IHZhbHVlcyBvZiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIChvciByYW5nZXMgb2YgdmFsdWVzLCBmb3IgY29udGludW91cyB2YXJpYWJsZXMpLiBXZSBnZW5lcmF0ZSB0aGlzIGdyYXBoIHdpdGggZnVuY3Rpb24gYGhpc3RgLiBUaGUgZ3JhcGggd2lsbCBiZSBkaXNwbGF5ZWQgaW4gdGhlIFBsb3RzIHRhYiBvZiBSU3R1ZGlvJ3MgbG93ZXItcmlnaHQgcGFuZS4KCmBgYHtyIGhpc3R9CiMgSGlzdG9ncmFtIG9mIGxpZmUgZXhwZWN0YW5jeSB2YWx1ZXMgZnJvbSBnYXBtaW5kZXIKaGlzdChnYXBtaW5kZXJfZGF0YSRsaWZlRXhwKQpgYGAKClwKCldlIHNlZSB0aGF0IHRoZSBkaXN0cmlidXRpb24gb2YgbGlmZSBleHBlY3RhbmN5IGlzIGFwcHJveGltYXRlbHkgYmVsbC1zaGFwZWQsIHdpdGggbWFueSBzY29yZXMgYmV0d2VlbiA2NSBhbmQgODUsIGFuZCBhIHNtYWxsIG51bWJlciBvZiBleHRyZW1lIHZhbHVlcyBncmVhdGVyIHRoYW4gODAgb3IgbGVzcyB0aGFuIDMwLiAKClwKCiMjIyBCb3hwbG90cwpUaGUgYm94cGxvdCBhbGxvd3MgdXMgdG8gY29tcGFyZSBkaXN0cmlidXRpb24gaW5mb3JtYXRpb24gYmV0d2VlbiBncm91cHMuIEZvciBleGFtcGxlLCB3ZSBjYW4gY29tcGFyZSBsaWZlIGV4cGVjdGFuY3kgZm9yIHRoZSBkaWZmZXJlbnQgY29udGluZW50cy4gCgpUaGUgUiBmdW5jdGlvbiBgYm94cGxvdGAgYWNjZXB0cyB0d28gYXJndW1lbnRzLiAKClRoZSBmaXJzdCBhcmd1bWVudCBpcyB0aGUgKipmb3JtdWxhKiouIFRoaXMgaXMgYSBjb21wbGV4LCB5ZXQgdmVyeSBjb21tb24sIGFyZ3VtZW50IGZvcm1hdCBmb3IgUiBzdGF0aXN0aWNhbCBmdW5jdGlvbnMuIFRoZSBmb3JtdWxhIGRlc2NyaWJlcyBhIGxpbmVhciBtb2RlbCBmb3IgYSBkYXRhIHNldCB3aXRoIHRoZSBnZW5lcmFsIHN0cnVjdHVyZTogKipkZXBlbmRlbnQgb3IgcHJlZGljdGVkIHZhcmlhYmxlIH4gaW5kZXBlbmRlbnQgdmFyaWFibGVzIG9yIHByZWRpY3RvcnMqKiwgdXNpbmcgY29sdW1ucyBuYW1lcyBmcm9tIHRoZSBkYXRhIGZyYW1lLiBUaGUgfiAodGlsZGUpIGlzIHJlYWQgYXMgImRlcGVuZHMgb24iIG9yICJpcyBwcmVkaWN0ZWQgYnkiLiBGb3Igb3VyIGV4YW1wbGUsIHdlIGFyZSBpbnRlcmVzdGVkIGluIHRoZSB3YXkgdGhhdCBsaWZlIGV4cGVjdGFuY3kgaXMgZGVwZW5kZW50IG9uIHRoZSBjb250aW5lbnQsIHNvIHdlIHNwZWNpZnkgb3VyIGZvcm11bGEgYXMgKipsaWZlRXhwIH4gY29udGluZW50KiouICBXZSB3aWxsIHNlZSBtb3JlIGNvbXBsZXggZXhhbXBsZXMgb2YgdGhlIGZvcm11bGEgYXJndW1lbnQgbGF0ZXIgaW4gdGhlIHNlbWVzdGVyLgoKVGhlIHNlY29uZCBhcmd1bWVudCB0byBib3hwbG90IGlzIHRoZSBkYXRhIGZyYW1lLgoKYGBge3IgYm94cGxvdDAxfQpib3hwbG90KGxpZmVFeHAgfiBjb250aW5lbnQsIGdhcG1pbmRlcl9kYXRhKQpgYGAKClwKCkJveHBsb3RzIGVmZmljaWVudGx5IGlsbHVzdHJhdGUgYm90aCB0aGUgY2VudHJhbCB0ZW5kZW5jeSBhbmQgdGhlIHZhcmlhYmlsaXR5IG9mIGEgZGF0YSBzZXQuIEVhY2ggZ3JleSBib3ggZXh0ZW5kcyBmcm9tIHRoZSBmaXJzdCBxdWFydGlsZSB0byB0aGUgdGhpcmQgcXVhcnRpbGUgb2YgaXRzIGlucHV0IHZhbHVlcy4gVGhlIGRhcmsgbGluZSBhY3Jvc3MgdGhlIGJveCBpcyBhdCB0aGUgbWVkaWFuLiBUaGUgdHdvIHRoaW4gbGluZXMgb3V0c2lkZSB0aGUgcXJleSBib3ggc2hvdyB0aGUgdmFsdWVzIG9mIHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIHNjb3JlcywgZXhjbHVkaW5nIGV4dHJlbWUgb3V0bGllcnMuIElmIGV4dHJlbWUgb3V0bGllcnMgYXJlIHByZXNlbnQsIHRoZXkgYXJlIHNob3duIGFzIHNtYWxsIGNpcmNsZXMuIFRoaXMgZmlndXJlIGNsZWFybHkgaWxsdXN0cmF0ZXMgdGhhdCwgaW4gdGhlIGdhcG1pbmRlciBkYXRhLCBsaWZlIGV4cGVjdGFuY3kgLS0gYm90aCBjZW50cmFsIHRlbmRlbmN5IGFuZCB2YXJpYWJsaXR5IC0tIGlzIG5vdCB0aGUgc2FtZSBmb3IgYWxsIGNvbnRpbmVudHMuCgoKXAoKIyMgUGxvdHRpbmcgd2l0aCBnZ3Bsb3QyCgpUaGUgYGhpc3RgIGFuZCBgYm94cGxvdGAgZnVuY3Rpb25zIGFyZSBwYXJ0IG9mIEJhc2UgUi4gVGhleSBhcmUgdXNlZnVsLCBidXQgZm9yIG1vcmUgZWxhYm9yYXRlLCBwdWJsaWNhdGlvbi1xdWFsaXR5IGdyYXBocywgd2UgY2FuIHVzZSB0aGUgdGhpcmQtcGFydHkgbGlicmFyeSAqKmdncGxvdCoqIGNvbnRhaW5lZCBpbiBwYWNrYWdlICoqZ2dwbG90MioqLiBUaGUgZ2dwbG90IGxpYnJhcnkgaXMgYSB2ZXJ5IHBvcHVsYXIgZGF0YSB2aXN1YWxpc2F0aW9uIHRvb2wgYmFzZWQgb24gYW4gZWxhYm9yYXRlIHN5bWJvbGljIHN5c3RlbSBjYWxsZWQgdGhlICdHcmFtbWFyIG9mIEdyYXBoaWNzJy4gCgpUaGUgc3ludGF4IG9mIGdncGxvdCBpcyBjb21wbGV4LCBhbmQgd2Ugd2lsbCBjb25jZW50cmF0ZSBvbiB0aGUgZm91bmRhdGlvbnMgaW4gdGhpcyBtb2R1bGUuIEZvciBhZGRpdGlvbmFsIGRldGFpbCwgc2VlIHRoZSBEYXRhIFZpc3VhbGlzYXRpb24gY2hhcHRlciBpbiB0aGUgUiBmb3IgRGF0YSBTY2llbmNlIG9ubGluZSB0ZXh0LCBhdCAgaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9kYXRhLXZpc3VhbGlzYXRpb24uaHRtbC4KClwKCiMjIyBTZW1hbnRpY3Mgb2YgZ2dwbG90CllvdSBjYW4gdGhpbmsgb2YgYSBnZ3Bsb3QgZ3JhcGggYXMgYmVpbmcgYnVpbHQgYXMgYSBzZXF1ZW5jZSBvZiBsYXllcnMuIE9uIHRoZSBib3R0b20gaXMgdGhlIGJhc2Ugb2YgdGhlIGdyYXBoLCB0aGVuIHRoZSBheGVzIGFuZCB0aGUgZGF0YSBhcmUgbGF5ZXJlZCBvbiwgdGhlbiB0aXRsZXMgYW5kIG5vdGF0aW9ucyBhbmQgb3RoZXIgZmVhdHVyZXMuIEEgZ2dwbG90IGNvbW1hbmQgcmVmbGVjdHMgdGhpcyBsYXllcmVkIHN0cnVjdHVyZS4KClwKCiMjIyBCdWlsZGluZyBhIGdyYXBoCgpUbyB1c2UgdGhlIGdncGxvdCBsaWJyYXJ5LCB3ZSBtdXN0IGluc3RhbGwgdGhlIGdncGxvdDIgcGFja2FnZSAob25jZSBvbiBhIGNvbXB1dGVyKSBhbmQgaW52b2tlIHRoZSBsaWJyYXJ5IGNvbW1hbmQgKGZvciBldmVyeSBSU3R1ZGlvIHNlc3Npb24pLgoKYGBge3IgaW5zdGFsbCBhbmQgbG9hZCwgZXZhbCA9IEZBTFNFfQojIE9uY2Ugb24gYW55IGNvbXB1dGVyCmluc3RhbGwucGFja2FnZXMoZ2dwbG90MikKCiMgT25jZSBmb3IgYW55IFJTdHVkaW8gc2Vzc2lvbgpsaWJyYXJ5KGdncGxvdDIpCmBgYAoKRXZlcnkgZ3JhcGggcmVwcmVzZW50cyBhIGRhdGEgZnJhbWUuIFRoZSBiYXNlIHBhcnQgb2YgYW55IGdncGxvdCBjb21tYW5kIGlzIGEgY2FsbCB0byBmdW5jdGlvbiBgZ2dwbG90KClgIHBhc3NpbmcgaW4gdGhlIGRhdGEgZnJhbWUsIGFzc2lnbmVkIHRvIGZ1bmN0aW9uIGFyZ3VtZW50IGBkYXRhYC4KCmBgYHtyIGdncGxvdF9iYXNlfQojIFRoZSBnZ3Bsb3QgYmFzZSBsYXllcgpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlcl9kYXRhKQpgYGAKClwKCklmIHlvdSBydW4gdGhpcyBjb21tYW5kIGZyb20gdGhlIFJTdHVkaW8gY29uc29sZSBvciBhbiBSIHNjcmlwdCwgdGhlIGdyZXkgc3F1YXJlIHNob3duIGFib3ZlIGFwcGVhcnMgaW4gdGhlIFBsb3RzIHBhbmUuIFRoaXMgaW5kaWNhdGVzIHRoYXQgZ2dwbG90IGlzIHJlYWR5IHRvIGRyYXcgYSBmaWd1cmUgLS0gdGhpcyBpcyB0aGUgIGJvdHRvbSBsYXllciBvZiBhIGdncGxvdCBncmFwaC4KClRvIGFkZCB4IGFuZCB5IGF4ZXMgdG8gdGhlIGdyYXBoLCB3ZSBuZWVkIHRvIGRlZmluZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gaW5mb3JtYXRpb25hbCBlbGVtZW50cyBpbiB0aGUgZGF0YSBzZXQgKHRoZSB2YXJpYWJsZXMgd2Ugd2FudCB0byBwbG90KSBhbmQgdmlzdWFsIGVsZW1lbnRzIGluIG91ciBncmFwaCAodGhlIGF4ZXMpLiBJbiBnZ3Bsb3QgdGhpcyByZWxhdGlvbnNoaXAgaXMgYSAqKm1hcHBpbmcqKi4gVG8gaW5pdGlhbGlzZSBhIG1hcHBpbmcsIHdlIGlkZW50aWZ5IGEgcGFydGljdWxhciBlbGVtZW50IG9mIHRoZSBncmFwaCAoZS5nLiB0aGUgeC1heGlzKSBhbmQgYXNzaWduIGEgcGFydGljdWxhciBlbGVtZW50IG9mIHRoZSBkYXRhIChlLmcuIGEgY29sdW1uIGluIHRoZSBkYXRhIGZyYW1lKSB0byBpdC4gVGhpcyBhc3NpZ25tZW50IGlzIGNhbGxlZCBhbiAqKmFlc3RoZXRpYyoqIGluIHRoZSBHcmFtbWFyIG9mIEdyYXBoaWNzLCBhbmQgaW4gZ2dwbG90IHdlIHVzZSBmdW5jdGlvbiBgYWVzKClgIHRvIHNwZWNpZnkgYWVzdGhldGljcy4gCgpJbWFnaW5lIHRoYXQgd2Ugd2lzaCB0byBtYWtlIGEgZ3JhcGggc2hvd2luZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gcGVyIGNhcGl0YSBHRFAgYW5kIGxpZmUgZXhwZWN0YW5jeSAodHdvIGNvbHVtbnMgaW4gZ2FwbWluZGVyX2RhdGEpLiBXZSBtYXAgdGhlIGZpcnN0IHZhcmlhYmxlIHRvIHRoZSB4IGF4aXMgKGFyZ3VtZW50IHgpIG9mIG91ciBncmFwaCBhbmQgdGhlIHNlY29uZCB0byB0aGUgeSBheGlzIChhcmd1bWVudCB5KS4gVGhpcyB3aWxsIGFkZCBhIG5ldyBsYXllci4gV2UgYWRkIHRoaXMgbmV3IGluZm9ybWF0aW9uIHRvIHRoZSBnZ3Bsb3QoKSBiYXNlIGNhbGwgYXMgc2hvd24gYmVsb3cuIE5vdGUgdGhhdCB3ZSBkb24ndCBuZWVkIHRvIHVzZSB0aGUgYCRgIG9wZXJhdG9yIGhlcmUsIGFzIGFsbCBjb2x1bW4gbmFtZXMgaW4gYSBnZ3Bsb3QgY29tbWFuZCBhcHBseSB0byB0aGUgc3VwcGxpZWQgZGF0YSBmcmFtZS4KCmBgYHtyIG1hcHBpbmdfeF9hbmRfeX0KZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXJfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCApKQoKCmBgYAoKXAoKV2UgaGF2ZSBhZGRlZCBhIG5ldyBsYXllciB0byBvdXIgZ3JhcGggd2l0aCBheGVzIGFuZCBncmlkIGxpbmVzLiBOb3RlIHRoYXQgdGhlIGF4ZXMnIHRpYyB2YWx1ZXMgYXJlIGNvcnJlY3RseSBmb3JtYXR0ZWQgZm9yIHRoZSBhc3NvY2lhdGVkIGRhdGEgYW5kIHRoZSBkYXRhIGZyYW1lIGNvbHVtbiBuYW1lcyBhcmUgdXNlZCBhcyB0aGUgYXhpcyBsYWJlbHMgKHdlIHdpbGwgc2VlIGhvdyB0byBpbXByb3ZlIHRob3NlIGxhYmVscyBsYXRlcikuCgpUbyBhZGQgcG9pbnRzIHRvIG91ciBncmFwaCwgd2Ugc3BlY2lmeSBhICoqZ2VvbWV0cnkqKiAoYW5vdGhlciB0ZXJtIGZyb20gdGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MpLiBUaGVyZSBhcmUgbWFueSwgbWFueSBhdmFpbGFibGUgZ2VvbWV0cmllcyBpbiBnZ3Bsb3QsIGNvcnJlc3BvbmRpbmcgdG8gYWxsIHRoZSBkaWZmZXJlbnQgc29ydHMgb2YgZ3JhcGhzIC0tIHNjYXR0ZXJwbG90cywgYmFyIHBsb3RzLCBwaWUgY2hhcnRzLCBsaW5lIGdyYXBocywgZXRjLiAtLSB0aGF0IHlvdSBtaWdodCB3aXNoIHRvIG1ha2UuIEZvciBvdXIgY3VycmVudCBncmFwaCwgd2Ugd2lzaCB0byBwbGFjZSBhIHBvaW50IGF0IHRoZSBpbnRlcnNlY3Rpb24gb2YgcGVyIGNhcGl0YSBHRFAgKG91ciB4IGF4aXMpIGFuZCBsaWZlIGV4cGVjdGFuY3kgKG91ciB5IGF4aXMpIGZvciBlYWNoIHJvdyBpbiB0aGUgaW5wdXQgZGF0YSBmcmFtZS4gVG8gYWRkIHRoaXMgZ2VvbWV0cnkgdG8gZ2dwbG90IGFwcGVuZCBgZ2VvbV9wb2ludCgpYCB0byB5b3VyIGN1cnJlbnQgZ2dwbG90IGNvbW1hbmQgdXNpbmcgdGhlIGArYCBvcGVyYXRvci4gSXQgaXMgY29udmVudGlvbmFsIHRvIHBsYWNlIGVhY2ggY2h1bmsgb2YgdGhlIGdncGxvdCBjb21tYW5kIG9uIGl0cyBvd24gbGluZSBpbiB0aGUgY29kZS4KCmBgYHtyIGdlb21fcG9pbnR9CiMgQWRkIHBvaW50cyAoYSAnZ2VvbWV0cnknKSB0byB0aGUgZ3JhcGgKZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXJfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCApKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKXAoKVGhpcyB0eXBlIG9mIGdyYXBoICh1c3VhbGx5IGNhbGxlZCBhICoqc2NhdHRlcnBsb3QqKikgaWxsdXN0cmF0ZXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byBkZXBlbmRlbnQgdmFyaWFibGVzLiBFdmVuIGZyb20gdGhpcyB2ZXJ5IHNpbXBsZSBmaWd1cmUgd2UgY2FuIHNlZSB0aGF0IHRoZXJlIGlzIGEgZ2VuZXJhbCB0ZW5kZW5jeSBmb3IgaGlnaGVyIHBlciBjYXBpdGEgR0RQIHRvIGJlIGFzc29jaWF0ZWQgd2l0aCBoaWdoZXIgbGlmZSBleHBlY3RhbmN5IGluIHRoZSBnYXBtaW5kZXIgZGF0YS4KCkxpa2UgbW9zdCBmdW5jdGlvbnMsIGBnZW9tX3BvaW50YCBjYW4gYWNjZXB0IGFyZ3VtZW50cyB0aGF0IG1vZGlmeSBpdHMgYmVoYXZpb3VyLiBUaGUgYXJndW1lbnQgKipjb2xvdXIqKiBkZXRlcm1pbmVzIHRoZSBjb2xvdXIgb2YgdGhlIHBvaW50cyB0byBiZSBkcmF3biwgYW5kIGNhbiBiZSBhc3NpZ25lZCBhbnkgb2YgUidzIGJ1aWx0LWluIGNvbG91ciBuYW1lcyAoY2FsbCBmdW5jdGlvbiBgY29sb3VycygpYCB0byBsaXN0IGFsbCBwb3NzaWJsZSB2YWx1ZXMpIG9yIGEgaGV4aWRlY2ltYWwgUkdCIGNvZGUgKHNlZSBmb3IgZXhhbXBsZSwgaHR0cHM6Ly9yLWNoYXJ0cy5jb20vY29sb3JzLykuCgpgYGAge3IgY29sb3VyfQpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlcl9kYXRhLCBtYXBwaW5nID0gYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwICkpICsKICBnZW9tX3BvaW50KGNvbG91ciA9ICd0b21hdG8nKQpgYGAKClwKClRoaXMgbGl2ZW5zIHVwIG91ciBwbG90LCBidXQgaXQgZG9lc24ndCBhY3V0YWxseSBhZGQgYW55IG5ldyBpbmZvcm1hdGlvbi4gSXQgaXMgYmV0dGVyIHRlY2huaXF1ZSB0byB1c2UgY29sb3VyIHRvIHJlcHJlc2VudCBhbm90aGVyIG9mIG91ciBkYXRhIHZhcmlhYmxlcy4gV2UgbWlnaHQsIGZvciBleGFtcGxlLCB3aXNoIHRvIHVzZSBhIGRpZmZlcmVudCBjb2xvdXIgZm9yIGVhY2ggY29udGluZW50LCB0byBzZWUgaG93IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBHRFAgYW5kIGxpZmUgZXhwZWN0YW5jeSBkaWZmZXJzIGJldHdlZW4gY29udGluZW50cy4gVGhpcyByZXF1aXJlcyBkZWZpbmluZyBhIG1hcHBpbmcgYmV0d2VlbiBhIHZpc3VhbCBmZWF0dXJlIChjb2xvdXIpIGFuZCBhbiBlbGVtZW50IG9mIHRoZSBkYXRhIHNldCAoY29sdW1uIGNvbnRpbmVudCksIHNvIHdlIGluaXRpYWxpc2UgdGhlIGBtYXBwaW5nYCBwcm9wZXJ0eSB3aXRoIGZ1bmN0aW9uIGBhZXNgLCBpbiBvdXIgY2FsbCB0byBgZ2VvbV9wb2ludGAuCgpgYGB7ciBtYXBwaW5nX2NvbG91cn0KZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXJfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCApKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKGNvbG91ciA9IGNvbnRpbmVudCkpCmBgYAoKXAoKVGhpcyBncmFwaCBpbGx1c3RyYXRlcyBjbGVhcmx5IHRoYXQsIGluIHRoZSBnYXBtaW5kZXIgZGF0YSwgbGlmZSBleHBlY3RhbmN5IGFuZCBwZXIgY2FwaXRhIEdEUCB2YXJ5IHN1YnN0YW50aWFsbHkgYmV0d2VlbiBjb250aW5lbnRzLgoKWW91IHNob3VsZCBjYXJlZnVsbHkgY29tcGFyZSB0aGUgdHdvIHByZWNlZGluZyBncmFwaHMuIEluIHRoZSBmaXJzdCwgd2Ugc2ltcGx5IHNldCB0aGUgKipjb2xvdXIqKiBhcmd1bWVudCBvZiBmdW5jdGlvbmBnZW9tX3BvaW50YC4gSW4gdGhlIHNlY29uZCwgd2Ugc2V0IHRoZSAqKm1hcHBpbmcqKiBhcmd1bWVudCBvZiBgZ2VvbV9wb2ludGAgdXNpbmcgZnVuY3Rpb24gYGFlc2AuIEluIHRoZSBmb3JtZXIgZ3JhcGgsIGFsbCBwb2ludHMgYXJlIHRoZSBzYW1lIGNvbG91ci4gSW4gdGhlIGxhdHRlciBncmFwaCwgdGhlIGNvbG91ciBvZiBlYWNoIHBvaW50IGRlcGVuZHMgb24gaXRzIGNvbnRpbmVudCB2YWx1ZS4gVGhhdCBpcywgd2UgaGF2ZSAqKm1hcHBlZCoqIGNvbG91ciB0byBjb250aW5lbnQuIAoKXAoKIyMjIENob29zaW5nIGdlb21ldHJpZXMKCkl0IGlzIGVzc2VudGlhbCB0byBzZWxlY3QgdGhlIGNvcnJlY3QgdHlwZSBvZiBncmFwaCAodGhlIGNvcnJlY3QgZ2VvbWV0cnkgaW4gZ2dwbG90KSBmb3IgdGhlIGRhdGEgcGF0dGVybiB5b3Ugd2lzaCB0byBpbGx1c3RyYXRlLgoKQXNzdW1lLCBmb3IgZXhhbXBsZSwgdGhhdCB5b3Ugd2lzaCB0byBzaG93IHRoZSBjaGFuZ2UgaW4gbGlmZSBleHBlY3RhbmN5IGFjcm9zcyB5ZWFycywgZm9yIHRoZSBjb3VudHJ5IG9mIERlbm1hcmsuIEZpcnN0LCB3ZSBtdXN0IHNlbGVjdCBvdXQgb25seSB0aGUgcm93cyBmb3IgRGVubWFyayBmcm9tIG91ciBkYXRhIGZyYW1lLiAoV2Ugd2lsbCBjb25zaWRlciBzZWxlY3Rpb24gaW4gZGV0YWlsIGluIG5leHQgd2VlaydzIG1vZHVsZS4gRm9yIG5vdywganVzdCBub3RlIHRoYXQgYmV0d2VlbiB0aGUgc3F1YXJlIGJyYWNrZXRzIHdlIHByb3ZpZGUgcm93IGFuZCBjb2x1bW4gY3JpdGVyaWEgZm9yIHNlbGVjdGlvbiwgYW5kIGFuIGVtcHR5IHZhbHVlIGZvciBjb2x1bW4gbWVhbnMgKiphbGwqKi4pIAoKV2Ugd2lsbCB0aGVuIHBhc3MgdGhlIHNlbGVjdGVkIGRhdGEgdG8gZ2dwbG90IGFzIGJlZm9yZSwgc3BlY2lmeWluZyB0aGUgbWFwcGluZyBvZiB0aGUgZGF0YSB0byB0aGUgeCBhbmQgeSBheGVzLgoKVGhlIGdyYXBoIHdpbGwgYmUgaWxsdXN0cmF0aW5nIGEgdHJlbmQgKGNoYW5nZSBpbiBhIHZhcmlhYmxlIGFjcm9zcyB0aW1lKS4gVHJlbmQgZ3JhcGhzIGFyZSB1c3VhbGx5IGRyYXduIHdpdGggYSBjb250aW51b3VzIGxpbmUgYmV0d2VlbiB0aGUgcGxvdHRlZCBwb2ludHMuIEluIGdncGxvdCwgdGhpcyBpcyBnZW9tZXRyeSBgZ2VvbV9saW5lYC4KClRoZSBjb21wbGV0ZSBjb2RlIGlzOgoKYGBge3IgbGluZV9ncmFwaH0KIyBTZWxlY3QgYWxsIHJvd3Mgd2hlcmUgdGhlIGNvdW50cnkgaXMgZXF1YWwgdG8gRGVubWFyay4gU2VsZWN0IGFsbCBjb2x1bW5zLgpkZW5tYXJrX2RhdGEgPC0gZ2FwbWluZGVyX2RhdGFbZ2FwbWluZGVyX2RhdGEkY291bnRyeSA9PSAiRGVubWFyayIsIF0KCmdncGxvdChkYXRhID0gZGVubWFya19kYXRhLCBtYXBwaW5nID0gYWVzKHggPSB5ZWFyLCB5ID0gbGlmZUV4cCkpICsKICBnZW9tX2xpbmUoKQpgYGAKClwKCldlIGNhbiB1c2UgZ2dwbG90IHRvIHByb2R1Y2UgYSBoaXN0b2dyYW0gZm9yIGxpZmUgZXhwZWN0YW5jeSAoYXMgd2UgZGlkIGluIEJhc2UgUiBhYm92ZSkgd2l0aCBgZ2VvbV9oaXN0b2dyYW1gLiBGb3IgaGlzdG9ncmFtcyB3ZSBvbmx5IG5lZWQgdG8gbWFwIHRoZSB4IGF4aXMsIGFzIHRoZSB5IGF4aXMgcmVwcmVzZW50cywgYnkgZGVmYXVsdCwgZnJlcXVlbmN5LiBXZSBjYW4gZW5oYW5jZSB0aGUgcGxvdCdzIGFwcGVhcmFuY2UgYnkgaW5pdGlhbGlzaW5nIGBnZW9tX2hpc3RvZ3JhbWAgYXJndW1lbnRzIGBjb2xvdXJgIHdoaWNoIHNldHMgdGhlIGJvcmRlciBhcm91bmQgdGhlIGJhcnMgb24gdGhlIGdyYXBoLCBhbmQgYGZpbGxgIHdoaWNoIHNldHMgdGhlIGludGVyaW9yIG9mIHRoZSBiYXJzIG9uIHRoZSBncmFwaC4KCmBgYHtyIGhpc3RvZ3JhbSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlcl9kYXRhLCBtYXBwaW5nID0gYWVzKHggPSBsaWZlRXhwKSkgKwogIGdlb21faGlzdG9ncmFtKGNvbG91ciA9ICJ3aGl0ZSIsIGZpbGwgPSAiZGFya2dyZWVuIikKYGBgCgpcCgpTaW1pbGFybHksIHdlIGNhbiByZXByb2R1Y2UgdGhlIGJveHBsb3QgYWJvdmUgd2l0aCBgZ2VvbV9ib3hwbG90YC4gSW4gQmFzZSBSIHdlIHVzZWQgYSAqKmZvcm11bGEqKiB0byBpZGVudGlmeSB0aGUgZGVwZW5kZW50IGFuZCBpbmRlcGVuZGVudCB2YXJpYWJsZXMgZm9yIHRoZSBib3hwbG90LiBXaXRoIGdncGxvdCwgd2UgdXNlIGEgbWFwcGluZyB0byBhc3NpZ24gdGhlIERWIHRvIHRoZSB4IGF4aXMgYW5kIHRoZSBJViB0byB0aGUgeSBheGlzLgoKYGBge3IgYm94cGxvdH0KZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXJfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gY29udGluZW50LCB5ID0gbGlmZUV4cCkpICsKICBnZW9tX2JveHBsb3QoKQoKCmBgYAoKXAoKIyMjIEV4ZXJjaXNlOgpXaGF0IHdvdWxkIHlvdSBwcmVkaWN0IHRvIGJlIHRoZSBlZmZlY3Qgb2Ygc3dhcHBpbmcgdGhlIHZhbHVlcyBvZiB4IGFuZCB5IGluIHRoZSBjYWxsIHRvIGBhZXNgIGFib3ZlPyBUZXN0IHlvdXIgcHJlZGljdGlvbi4KClwKCiMjIyBSZWZpbmluZyB0aGUgYXBwZWFyYW5jZSBvZiBhIHBsb3QKQWZ0ZXIgd2UgaGF2ZSBidWlsdCB0aGUgZm91bmRhdGlvbiBvZiBvdXIgcGxvdCB3aXRoIGRhdGEgYW5kIGdlb21ldHJ5LCB3ZSBjYW4gYWRkIGZ1cnRoZXIgbGF5ZXJzIHRvIG1vZGlmeSBvdGhlciB2aXN1YWwgZmVhdHVyZXMuIEZvciBleGFtcGxlLCB3ZSBjYW4gdXNlIGZ1bmN0aW9uIGBsYWJzYCB0byBzZXQgdGhlIGF4aXMsIGxlZ2VuZCwgYW5kIG1haW4gdGl0bGVzIG9mIG91ciBwbG90cy4gQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBlbmhhbmNlbWVudHMgdG8gb3VyIGZpZ3VyZSBpbGx1c3RyYXRpbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIEdEUCBhbmQgbGlmZSBleHBlY3RhbmN5IGJ5IGNvbnRpbmVudDoKCmBgYCB7ciBsYWJlbHN9CiMgTkI6IE11bHRpcGxlIGZ1bmN0aW9uIGFyZ3VtZW50cyAoYXMgaW4gbGFicyBiZWxvdykgY2FuCiMgYmUgcGxhY2VkIG9uIHNlcGFyYXRlIGxpbmVzIHRvIGltcHJvdmUgcmVhZGFiaWxpdHkKCmdncGxvdChkYXRhID0gZ2FwbWluZGVyX2RhdGEsIG1hcHBpbmcgPSBhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHAgKSkgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyhjb2xvdXIgPSBjb250aW5lbnQpKSArCiAgbGFicyh4ID0gIkdEUCBQZXIgQ2FwaXRhIiwgCiAgICAgICB5ID0gIkxpZmUgRXhwZWN0YW5jeSIsIAogICAgICAgdGl0bGUgPSAiR2FwIE1pbmRlciBEYXRhIDE5NTIgdG8gMjAwNyIsIAogICAgICAgY29sb3VyID0gIkNvbnRpbmVudCIpCmBgYAoKXAoKClRoZSBjb2RlIGZvciBnZ3Bsb3QgZm9ybWF0dGluZyBjYW4gZ2V0IGV4dHJlbWVseSBjb21wbGV4LCBhbmQgdGhlIGZ1bGwgZnVuY3Rpb25hbGl0eSBpcyBiZXlvbmQgdGhlIHNjb3BlIG9mIHRoaXMgbW9kdWxlLiBJbiBhZGRpdGlvbiwgdGhlcmUgYXJlIG1hbnksIG1hbnkgbW9yZSBnZW9tZXRyaWVzIGF2YWlsYWJsZSwgZWFjaCB3aXRoIGFwcHJvcHJpYXRlIGFyZ3VtZW50cyBhbmQgbWFwcGluZyBvcHRpb25zLgoKVGhlIGZvcm1hbCBkb2N1bWVudGF0aW9uIGZvciBnZ3Bsb3QgY2FuIGJlIGZvdW5kIGF0IGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL2luZGV4Lmh0bWwuIElmIHlvdSBwcmVmZXIgdHV0b3JpYWxzIGFuZCBnYWxsZXJpZXMsIHRoZXJlIGFyZSBtYW55IGF2YWlsYWJsZSBvbmxpbmUuIFR3byBnb29kIHBsYWNlcyB0byBzdGFydCBhcmUgaHR0cDovL3d3dy5jb29rYm9vay1yLmNvbS9HcmFwaHMvIGFuZCBodHRwczovL3d3dy5yLWdyYXBoLWdhbGxlcnkuY29tLy4KClwKCiMjIyBTYXZpbmcgZ2dwbG90cwpZb3UgY2FuIHNhdmUgZmlndXJlcyBtYWRlIHdpdGggZ2dwbG90IHRvIGltYWdlIGZpbGVzLCB3aGljaCBjYW4gdGhlbiBiZSB1c2VkIGluIGRvY3VtZW50cyBnZW5lcmF0ZWQgaW4gTVMgV29yZCBvciBvdGhlciB0ZXh0IGVkaXRvcnMuIFdlIGZpcnN0IHNhdmUgdGhlIG91dHB1dCBvZiBvdXIgZ2dwbG90IGNvbW1hbmQgaW50byBhIG5hbWVkIHZhcmlhYmxlICh0byBSIGEgZ2dwbG90IGlzIGEgZGF0YSBvYmplY3QganVzdCBsaWtlIGEgbnVtYmVyIG9yIGEgc3RyaW5nKS4gV2UgdGhlbiB1c2UgZnVuY3Rpb24gYGdnc2F2ZWAgdG8gZXhwb3J0IG91dCBwbG90IGFzIGFuIGltYWdlIGZpbGUuIFlvdSBzcGVjaWZ5IHRoZSBpbWFnZSBmb3JtYXQgYnkgc3VwcGx5aW5nIGFuIG91dGZpbGUgbmFtZSB3aXRoIHRoZSBjb3JyZXNwb25kaW5nIGZpbGUgc3VmZml4IChlLmcuIC5qcGcgb3IgLnBuZykuIEJ5IGRlZmF1bHQsIHRoZSBmaWxlIGlzIHNhdmVkIGludG8gdGhlIHdvcmtpbmcgZm9sZGVyIChpbiBvdXIgY2FzZSwgdGhlIGZvbGRlciBjb250YWluaW5nIG91ciBjc3YgYW5kIHNjcmlwdCBmaWxlcykuCgpgYGB7ciBnZ3NhdmV9CiMgU2F2ZSBhIGdncGxvdCB0byBhIHZhcmlhYmxlLiBUaGUgc3ludGF4IG9mIHRoZSBncHBsb3QgY29tbWFuZCBpcyB1bmFmZmVjdGVkCmdkcF9saWZlRXhwX3Bsb3QgPC0gZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXJfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCApKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKGNvbG91ciA9IGNvbnRpbmVudCkpICsKICB4bGFiKCJHRFAgUGVyIENhcGl0YSIpICsKICB5bGFiKCJMaWZlIEV4cGVjdGFuY3kiKSArCiAgZ2d0aXRsZSgiR2FwIE1pbmRlciBEYXRhIDE5NTIgdG8gMjAwNyIpCgojIEV4cG9ydCB0aGUgdmFyaWFibGUgYXMgYW4gaW1hZ2UgZmlsZS4gUHJvdmlkZSB0aGUgZmlsZSBuYW1lIGFuZCB0aGUgZ2dwbG90IG9iamVjdApnZ3NhdmUoZmlsZW5hbWUgPSAiZ2RwX2xpZmVFeHBfcGxvdC5wbmciLCBnZHBfbGlmZUV4cF9wbG90KQpgYGAKClwKClwKCiMjIENvbmNsdXNpb24KClRoaXMgZG9jdW1lbnQgaGFzIHByZXNlbnRlZCBhIHNpbXBsZSBpbnRyb2R1Y3Rpb24gdG8gd29ya2luZyB3aXRoIGNvbXBsZXRlIHRhYmxlcyBvZiBkYXRhIGluIFIuIFdlIGxlYXJuZWQgaG93IHRvIGltcG9ydCBhIGNzdiBmaWxlIGludG8gYSBkYXRhIGZyYW1lLCBhbmQgaG93IHRvIHVzZSBCYXNlIFIgb3IgbGlicmFyeSBgZ2dwbG90YCB0byBnZW5lcmF0ZSBncmFwaHMgdG8gaWxsdXN0cmF0ZSBpbXBvcnRhbnQgcGF0dGVybnMgaW4gb3VyIGRhdGEuCgpcCgojIyMgV2hhdCdzIE5leHQKCkVuc3VyZSB5b3UgaGF2ZSB0aGUgYHRpZHl2ZXJzZWAgaW5zdGFsbGVkIGZvciB0aGUgbmV4dCBtb2R1bGUuIFRoZSB0aWR5dmVyc2UgaXMgYSBjb2xsZWN0aW9uIG9mIHBhY2thZ2VzIChhIG1ldGFwYWNrYWdlKSB0aGF0IHByb3ZpZGUgYSBzdWNjaW5jdCBzeW50YXggZm9yIHBlcmZvcm1pbmcgZGF0YSBtYW5pcHVsYXRpb24gYW5kIGJhc2ljIGFuYWx5c2lzIGluIFIuICBSdW5uaW5nIGBpbnN0YWxsLnBhY2thZ2VzKHRpZHl2ZXJzZSlgIGluc3RhbGxzIGFsbCB0aGUgaW5kaXZpZHVhbCBwYWNrYWdlcyB0byB5b3VyIG1hY2hpbmUuCgpUaGUgdGlkeXZlcnNlIGNvbnNpc3RzIG9mIGBnZ3Bsb3QyYCAocGxvdHRpbmcpLCBgZHBseXJgIChkYXRhIG1hbmlwdWxhdGlvbiksIGB0aWR5cmAgKGRhdGEgdGlkeWluZyksIGByZWFkcmAgKGRhdGEgaW1wb3J0aW5nKSwgYHB1cnJyYCAoZnVuY3Rpb25hbCBwcm9ncmFtbWluZyksIGB0aWJibGVgIChhIHNwZWNpYWwgdHlwZSBvZiBkYXRhIGZyYW1lKSwgYHN0cmluZ3JgIChjb21tb24gdGFza3MgZm9yIHN0cmluZyBtYW5pcHVsYXRpb25zKSwgYW5kICBgZm9yY2F0c2AgKGRlYWxpbmcgd2l0aCBmYWN0b3JzKQogCgpgYGB7ciBpbnN0YWxsIHRpZHl2ZXJzZSwgZXZhbCA9IEZBTFNFfQojIERvd25sb2FkIGFuZCBpbnN0YWxsIHRoZSBwYWNrYWdlcyBvZiB0aWR5dmVyc2UKaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKYGBgCgoKYGBge3J9CiMgbG9hZCB0aGUgdGlkeXZlcnNlIHBhY2thZ2VzIGZvciB0aGUgY3VycmVudCBzZXNzaW9uCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCgojIyBBcHBlbmRpeCAtIEZhY3RvcnMKCklmIHlvdSBhcmUgZGVhbGluZyB3aXRoIGNhdGVnb3JpY2FsIGRhdGEsIFIgY2FsbHMgdGhlc2UgX0ZhY3RvcnNfLiBQcmlvciB0byBSIHZlcnNpb24gNC4wIHRoZSBkZWZhdWx0IGJlaGF2aW91ciBvZiBgcmVhZC5jc3ZgIHdhcyB0byBoYXZlIHRoZSBwYXJhbWV0ZXIgYHN0cmluZ3NBc0ZhY3RvcnM9VFJVRWAgYXMgdGhlIGRlZmF1bHQgd2hpY2ggd291bGQgcmVhZCBhbGwgY2hhcmFjdGVyIGRhdGEgaW4gYXV0b21hdGljYWxseSBhcyBhIF9mYWN0b3JfLiBGcm9tIHZlcnNpb24gNC4wIG9ud2FyZHMgdGhlIGRlZmF1bHQgaXMgYHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0VgLgoKVG8gY29udmVydCBhIGNvbHVtbiB0byBhIGZhY3RvciB5b3UgY2FuIHVzZSBgYXMuZmFjdG9yYC4gWW91IGNhbiB1c2UgYGxldmVsc2Agb24gYSBfZmFjdG9yXyB0byBzZWUgdGhlIGNhdGVnb3JpZXMuIEl0IGlzIGJlc3QgdG8gd2FpdCB1bnRpbCBhZnRlciBhbnkgZGF0YSB0aWR5aW5nIGlzIHBlcmZvcm1lZCBiZWZvcmUgY29udmVydGluZyB0byBmYWN0b3JzLiBGb3IgaW5zdGFuY2UgaWYgd2UgcmVwZWF0ZWQgdGhlIGNvZGUgZm9yIHB1bGxpbmcgb3V0IHRoZSBjb250aW5lbnRzIGNvbHVtbiBmcm9tIHRoZSBnYXBtaW5kZXIgZGF0YXNldDoKCmBgYHtyfQojIFN0b3JlIGNvbHVtbiBjb250aW5lbnQgaW4gYSBuZXcgdmFyaWFibGUgYWZ0ZXIgY29udmVydGluZyB0byBhIGZhY3Rvcgpjb250aW5lbnRzX2ZhY3RvciA8LSBhcy5mYWN0b3IoZ2FwbWluZGVyX2RhdGEkY29udGluZW50KQoKIyBQYXNzIHRoZSB2YXJpYWJsZSB0byBmdW5jdGlvbiBsZXZlbHMgd2hpY2ggcmV0dXJucyB0aGUgdmFsdWVzCiMgb2YgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZQpsZXZlbHMoY29udGluZW50c19mYWN0b3IpCmBgYA==