Data Focus
Modules and Handouts 2–7 cover topics that are part of the data
analysis journey and are all interrelated. Each module will introduce
new content and expand on the material covered in previous modules. For
example, in Module 2 we introduce the basics of the R plotting systems.
Subsequent handouts include more advanced plotting options and
techniques.
Associated Material
Zoom Notes: Zoom notes 02 -
Visualising Data
Readings:
Tabular Data
In this module we begin to work with complete tables of data, and
learn how to make informative graphs.
We will start by getting our scientific research data into RStudio.
Commonly, we first enter our data into Excel for cleaning and
organising, and save it out as a csv (comma separated values) file. We
then import the file into R for data analysis. We will provide the csv
file to be used with this module.
When you have completed this module, you should be ready to produce
the graphs and figures you will need for your in-course research
projects. If you run into any problems or have any questions, email us,
or drop us a message in the R4SSP class team on MS Teams.
Preparation - Do this very carefully
Document organisation is vitally important. We suggest that you keep
a separate folder for each R4SSP module. To do this, you can set up a
formal RStudio project (see for example https://support.rstudio.com/hc/en-us/articles/200526207-Using-Projects).
But if you don’t feel quite ready for that, you can just collect your
script file and data files together in a folder. Proceed as follows:
Launch R Studio
Following the procedure from Using a Script File
in Module 1 - Intro to R and RStudio, create a script file and save it
to your desktop or other location where you will be able to find
it.
Quit R Studio
Locate the script file you just made (it will have a .R file
suffix).
Create a new folder (name it something sensible) and place the
script file in it.
The sample csv data file used in this module is called
gapminder_data_2007.csv. This file contains life
expectancy, population, and GDP values for 142 different countries. The
data for each country were measured 12 times between 1952 and 2007.
These data are sourced from the GapMinder foundation (https://www.gapminder.org/).
Downloading the GapMinder 2007 data:
One option for downloading the data is to use
download.file()
in R. You can use this command to download
the file into your data/
directory:
download.file(url = "https://raw.githubusercontent.com/rtis-training/2023-s2-r4ssp/main/docs/data/gapminder_data_2007.csv",
destfile = "data/gapminder_data_2007.csv")
Remember that R is case sensitive so if you don’t have a
directory exactly called data/
modify the command to match
your directory.
The second option is in a web browser go to https://raw.githubusercontent.com/rtis-training/2023-s2-r4ssp/main/docs/data/gapminder_data_2007.csv
and then save the page using Save As
and give it the name
“gapminder_data_2007.csv”. Save it to your data location.
- Open the folder and double-click on your script
file to open it in RStudio.
If you set things up this way, RStudio will be able to find your data
file for the next step – importing data.
Importing a data file
The first few lines of our data file
gapminder_data_2007.csv as it appears when opened in
Excel, are shown below:
We will import this file into R for some preliminary data
analysis.
To import a csv file into R we can use the function
read.csv
. Like the R functions we used in Module 1,
read.csv
accepts an argument between its round brackets.
The argument is the name of the input file. Because the name of the file
is a string, we surround it with double quotes. The complete command is
shown below.
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).
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:
head(gapminder_data)
tail(gapminder_data)
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.
Selecting and using columns of data
In both of the above displays, each column name is prefaced with
$
. We use the $
operator to select individual
columns of data from a data frame. For example, to select just the life
expectancy values from our data, we say
gapminder_data$lifeExp
. (Be sure to type the column name
exactly as it appears in the output of str
and in the
Environment pane.) We can use this expression directly as the argument
to a function, or we can store the selected column in a variable (which
will be a vector) for later processing.
For example:
head(gapminder_data$year)
continents <- gapminder_data$continent
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.
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).
install.packages(ggplot2)
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
.
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.
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:
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:
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).
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")
ggsave(filename = "gdp_lifeExp_plot.png", gdp_lifeExp_plot)
Conclusion
This document has presented a simple introduction to working with
complete tables of data in R. We learned how to import a csv file into a
data frame, and how to use Base R or library ggplot
to
generate graphs to illustrate important patterns in our data.
What’s Next
Ensure you have the tidyverse
installed for the next
module. The tidyverse is a collection of packages (a metapackage) that
provide a succinct syntax for performing data manipulation and basic
analysis in R. Running install.packages(tidyverse)
installs
all the individual packages to your machine.
The tidyverse consists of ggplot2
(plotting),
dplyr
(data manipulation), tidyr
(data
tidying), readr
(data importing), purrr
(functional programming), tibble
(a special type of data
frame), stringr
(common tasks for string manipulations),
and forcats
(dealing with factors)
install.packages("tidyverse")
Appendix - Factors
If you are dealing with categorical data, R calls these
Factors. Prior to R version 4.0 the default behaviour of
read.csv
was to have the parameter
stringsAsFactors=TRUE
as the default which would read all
character data in automatically as a factor. From version 4.0
onwards the default is stringsAsFactors=FALSE
.
To convert a column to a factor you can use as.factor
.
You can use levels
on a factor to see the
categories. It is best to wait until after any data tidying is performed
before converting to factors. For instance if we repeated the code for
pulling out the continents column from the gapminder dataset:
continents_factor <- as.factor(gapminder_data$continent)
levels(continents_factor)
LS0tCnRpdGxlOiAiVmlzdWFsaXNpbmciCmRhdGU6ICJTZW1lc3RlciAyLCAyMDIzIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCgpsaWJyYXJ5KGtuaXRyKQoKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGNvbW1lbnQgPSAiIz4iLAogIGZpZy5wYXRoID0gImZpZ3VyZXMvMDIvIiwgIyB1c2Ugb25seSBmb3Igc2luZ2xlIFJtZCBmaWxlcwogIGNvbGxhcHNlID0gVFJVRSwKICBlY2hvID0gVFJVRQopCmBgYAoKPiAjIyMjIERhdGEgRm9jdXMKPgo+IE1vZHVsZXMgYW5kIEhhbmRvdXRzIDItLTcgY292ZXIgdG9waWNzIHRoYXQgYXJlIHBhcnQgb2YgdGhlIGRhdGEgYW5hbHlzaXMgam91cm5leSBhbmQgYXJlIGFsbCBpbnRlcnJlbGF0ZWQuIEVhY2ggbW9kdWxlIHdpbGwgaW50cm9kdWNlIG5ldyBjb250ZW50IGFuZCBleHBhbmQgb24gdGhlIG1hdGVyaWFsIGNvdmVyZWQgaW4gcHJldmlvdXMgbW9kdWxlcy4gRm9yIGV4YW1wbGUsIGluIE1vZHVsZSAyIHdlIGludHJvZHVjZSB0aGUgYmFzaWNzIG9mIHRoZSBSIHBsb3R0aW5nIHN5c3RlbXMuIFN1YnNlcXVlbnQgaGFuZG91dHMgaW5jbHVkZSBtb3JlIGFkdmFuY2VkIHBsb3R0aW5nIG9wdGlvbnMgYW5kIHRlY2huaXF1ZXMuCgpcCgpcCgo+ICMjIyMgQXNzb2NpYXRlZCBNYXRlcmlhbAo+Cj4gWm9vbSBOb3RlczogW1pvb20gbm90ZXMgMDIgLSBWaXN1YWxpc2luZyBEYXRhXSh6b29tX25vdGVzXzAyX3Zpc3VhbGlzZS5odG1sKQo+IAo+IFJlYWRpbmdzOgo+Cj4gLSBbUiBmb3IgRGF0YSBTY2llbmNlIC0gQ2hhcHRlciAxMl0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei90aWR5LWRhdGEuaHRtbCkKPiAtIFtSIGZvciBEYXRhIFNjaWVuY2UgLSBDaGFwdGVyIDExXShodHRwczovL3I0ZHMuaGFkLmNvLm56L2RhdGEtaW1wb3J0Lmh0bWwpCj4gLSBbUiBmb3IgRGF0YSBTY2llbmNlIC0gQ2hhcHRlciAzXShodHRwczovL3I0ZHMuaGFkLmNvLm56L2RhdGEtdmlzdWFsaXNhdGlvbi5odG1sKQoKCgojIyBUYWJ1bGFyIERhdGEKCkluIHRoaXMgbW9kdWxlIHdlIGJlZ2luIHRvIHdvcmsgd2l0aCBjb21wbGV0ZSB0YWJsZXMgb2YgZGF0YSwgYW5kIGxlYXJuIGhvdyB0byBtYWtlIGluZm9ybWF0aXZlIGdyYXBocy4gCgpXZSB3aWxsIHN0YXJ0IGJ5IGdldHRpbmcgb3VyIHNjaWVudGlmaWMgcmVzZWFyY2ggZGF0YSBpbnRvIFJTdHVkaW8uIENvbW1vbmx5LCB3ZSBmaXJzdCBlbnRlciBvdXIgZGF0YSBpbnRvIEV4Y2VsIGZvciBjbGVhbmluZyBhbmQgb3JnYW5pc2luZywgYW5kIHNhdmUgaXQgb3V0IGFzIGEgY3N2IChjb21tYSBzZXBhcmF0ZWQgdmFsdWVzKSBmaWxlLiBXZSB0aGVuIGltcG9ydCB0aGUgZmlsZSBpbnRvIFIgZm9yIGRhdGEgYW5hbHlzaXMuIFdlIHdpbGwgcHJvdmlkZSB0aGUgY3N2IGZpbGUgdG8gYmUgdXNlZCB3aXRoIHRoaXMgbW9kdWxlLgoKV2hlbiB5b3UgaGF2ZSBjb21wbGV0ZWQgdGhpcyBtb2R1bGUsIHlvdSBzaG91bGQgYmUgcmVhZHkgdG8gcHJvZHVjZSB0aGUgZ3JhcGhzIGFuZCBmaWd1cmVzIHlvdSB3aWxsIG5lZWQgZm9yIHlvdXIgaW4tY291cnNlIHJlc2VhcmNoIHByb2plY3RzLiBJZiB5b3UgcnVuIGludG8gYW55IHByb2JsZW1zIG9yIGhhdmUgYW55IHF1ZXN0aW9ucywgZW1haWwgdXMsIG9yIGRyb3AgdXMgYSBtZXNzYWdlIGluIHRoZSBSNFNTUCBjbGFzcyB0ZWFtIG9uIE1TIFRlYW1zLgoKXAoKIyMgUHJlcGFyYXRpb24gLSBEbyB0aGlzIHZlcnkgY2FyZWZ1bGx5CgpEb2N1bWVudCBvcmdhbmlzYXRpb24gaXMgdml0YWxseSBpbXBvcnRhbnQuIFdlIHN1Z2dlc3QgdGhhdCB5b3Uga2VlcCBhIHNlcGFyYXRlIGZvbGRlciBmb3IgZWFjaCBSNFNTUCBtb2R1bGUuIFRvIGRvIHRoaXMsIHlvdSBjYW4gc2V0IHVwIGEgZm9ybWFsIFJTdHVkaW8gcHJvamVjdCAoc2VlIGZvciBleGFtcGxlIGh0dHBzOi8vc3VwcG9ydC5yc3R1ZGlvLmNvbS9oYy9lbi11cy9hcnRpY2xlcy8yMDA1MjYyMDctVXNpbmctUHJvamVjdHMpLiBCdXQgaWYgeW91IGRvbid0IGZlZWwgcXVpdGUgcmVhZHkgZm9yIHRoYXQsIHlvdSBjYW4ganVzdCBjb2xsZWN0IHlvdXIgc2NyaXB0IGZpbGUgYW5kIGRhdGEgZmlsZXMgdG9nZXRoZXIgaW4gYSBmb2xkZXIuIFByb2NlZWQgYXMgZm9sbG93czoKCgoxLiBMYXVuY2ggUiBTdHVkaW8KCjIuIEZvbGxvd2luZyB0aGUgcHJvY2VkdXJlIGZyb20gKipVc2luZyBhIFNjcmlwdCBGaWxlKiogaW4gTW9kdWxlIDEgLSBJbnRybyB0byBSIGFuZCBSU3R1ZGlvLCBjcmVhdGUgYSBzY3JpcHQgZmlsZSBhbmQgc2F2ZSBpdCB0byB5b3VyIGRlc2t0b3Agb3Igb3RoZXIgbG9jYXRpb24gd2hlcmUgeW91IHdpbGwgYmUgYWJsZSB0byBmaW5kIGl0LgoKMy4gUXVpdCBSIFN0dWRpbwoKNC4gTG9jYXRlIHRoZSBzY3JpcHQgZmlsZSB5b3UganVzdCBtYWRlIChpdCB3aWxsIGhhdmUgYSAuUiBmaWxlIHN1ZmZpeCkuIAoKNS4gQ3JlYXRlIGEgbmV3IGZvbGRlciAobmFtZSBpdCBzb21ldGhpbmcgc2Vuc2libGUpIGFuZCBwbGFjZSB0aGUgc2NyaXB0IGZpbGUgaW4gaXQuCgo2LiBUaGUgc2FtcGxlIGNzdiBkYXRhIGZpbGUgdXNlZCBpbiB0aGlzIG1vZHVsZSBpcyBjYWxsZWQgKipnYXBtaW5kZXJfZGF0YV8yMDA3LmNzdioqLiBUaGlzIGZpbGUgY29udGFpbnMgbGlmZSBleHBlY3RhbmN5LCBwb3B1bGF0aW9uLCBhbmQgR0RQIHZhbHVlcyBmb3IgMTQyIGRpZmZlcmVudCBjb3VudHJpZXMuIFRoZSBkYXRhIGZvciBlYWNoIGNvdW50cnkgd2VyZSBtZWFzdXJlZCAxMiB0aW1lcyBiZXR3ZWVuIDE5NTIgYW5kIDIwMDcuIFRoZXNlIGRhdGEgYXJlIHNvdXJjZWQgZnJvbSB0aGUgR2FwTWluZGVyIGZvdW5kYXRpb24gKGh0dHBzOi8vd3d3LmdhcG1pbmRlci5vcmcvKS4KCj4gRG93bmxvYWRpbmcgdGhlIEdhcE1pbmRlciAyMDA3IGRhdGE6Cj4gCj4gT25lIG9wdGlvbiBmb3IgZG93bmxvYWRpbmcgdGhlIGRhdGEgaXMgdG8gdXNlIGBkb3dubG9hZC5maWxlKClgIGluIFIuIFlvdSBjYW4gdXNlIHRoaXMgY29tbWFuZCB0byBkb3dubG9hZCB0aGUgZmlsZSBpbnRvIHlvdXIgYGRhdGEvYCBkaXJlY3Rvcnk6IAo+IAo+IGBgYHtyLCBldmFsID0gRkFMU0V9Cj4gZG93bmxvYWQuZmlsZSh1cmwgPSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3J0aXMtdHJhaW5pbmcvMjAyMy1zMi1yNHNzcC9tYWluL2RvY3MvZGF0YS9nYXBtaW5kZXJfZGF0YV8yMDA3LmNzdiIsIAo+ICBkZXN0ZmlsZSA9ICJkYXRhL2dhcG1pbmRlcl9kYXRhXzIwMDcuY3N2IikKPiBgYGAKPiBfUmVtZW1iZXIgdGhhdCBSIGlzIGNhc2Ugc2Vuc2l0aXZlIHNvIGlmIHlvdSBkb24ndCBoYXZlIGEgZGlyZWN0b3J5IGV4YWN0bHkgY2FsbGVkIGBkYXRhL2AgbW9kaWZ5IHRoZSBjb21tYW5kIHRvIG1hdGNoIHlvdXIgZGlyZWN0b3J5Ll8KPgo+IFRoZSBzZWNvbmQgb3B0aW9uIGlzIGluIGEgd2ViIGJyb3dzZXIgZ28gdG8gaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3J0aXMtdHJhaW5pbmcvMjAyMy1zMi1yNHNzcC9tYWluL2RvY3MvZGF0YS9nYXBtaW5kZXJfZGF0YV8yMDA3LmNzdiBhbmQgdGhlbiBzYXZlIHRoZSBwYWdlIHVzaW5nIGBTYXZlIEFzYCBhbmQgZ2l2ZSBpdCB0aGUgbmFtZSAiZ2FwbWluZGVyX2RhdGFfMjAwNy5jc3YiLiBTYXZlIGl0IHRvIHlvdXIgZGF0YSBsb2NhdGlvbi4KCjwhLS0KUmV0cmlldmUgdGhlIHNhbXBsZSBjc3YgZGF0YSBmaWxlIGZyb20gdGhlIGNvdXJzZSBbR29vZ2xlIERyaXZlIHNoYXJlZCBmb2xkZXJdKGh0dHBzOi8vZHJpdmUuZ29vZ2xlLmNvbS9kcml2ZS9mb2xkZXJzLzF0dGYxczgtdmtKTk9sSGRwaGZpMnpGeU1xNmdHRXZDeT91c3A9c2hhcmluZykuIFBsYWNlIHRoZSBjc3YgZmlsZSBpbnRvIHRoZSBmb2xkZXIgd2l0aCB5b3VyIHNjcmlwdCBmaWxlLgotLT4KCgo3LiBPcGVuIHRoZSBmb2xkZXIgYW5kIGRvdWJsZS1jbGljayAqKm9uIHlvdXIgc2NyaXB0IGZpbGUqKiB0byBvcGVuIGl0IGluIFJTdHVkaW8uCgpJZiB5b3Ugc2V0IHRoaW5ncyB1cCB0aGlzIHdheSwgUlN0dWRpbyB3aWxsIGJlIGFibGUgdG8gZmluZCB5b3VyIGRhdGEgZmlsZSBmb3IgdGhlIG5leHQgc3RlcCAtLSBpbXBvcnRpbmcgZGF0YS4KClwKCiMjIEltcG9ydGluZyBhIGRhdGEgZmlsZQoKVGhlIGZpcnN0IGZldyBsaW5lcyBvZiBvdXIgZGF0YSBmaWxlICoqZ2FwbWluZGVyX2RhdGFfMjAwNy5jc3YqKiBhcyBpdCBhcHBlYXJzIHdoZW4gb3BlbmVkIGluIEV4Y2VsLCBhcmUgc2hvd24gYmVsb3c6CgpgYGB7ciBmaWcwMSwgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGggPSAiNTAlIiwgZmlnLmNhcCA9ICJHYXBNaW5kZXIgRGF0YSIsIGZpZy5wb3MgPSAiSCIsIGVjaG8gPSBGQUxTRX0KaW5jbHVkZV9ncmFwaGljcygiaW1hZ2VzLzAyLWdhcG1pbmRlcl9kYXRhXzIwMDcucG5nIikKYGBgCgoKCldlIHdpbGwgaW1wb3J0IHRoaXMgZmlsZSBpbnRvIFIgZm9yIHNvbWUgcHJlbGltaW5hcnkgZGF0YSBhbmFseXNpcy4KClRvIGltcG9ydCBhIGNzdiBmaWxlIGludG8gUiB3ZSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBgcmVhZC5jc3ZgLiBMaWtlIHRoZSBSIGZ1bmN0aW9ucyB3ZSB1c2VkIGluIE1vZHVsZSAxLCBgcmVhZC5jc3ZgIGFjY2VwdHMgYW4gYXJndW1lbnQgYmV0d2VlbiBpdHMgcm91bmQgYnJhY2tldHMuIFRoZSBhcmd1bWVudCBpcyB0aGUgbmFtZSBvZiB0aGUgaW5wdXQgZmlsZS4gQmVjYXVzZSB0aGUgbmFtZSBvZiB0aGUgZmlsZSBpcyBhIHN0cmluZywgd2Ugc3Vycm91bmQgaXQgd2l0aCBkb3VibGUgcXVvdGVzLiBUaGUgY29tcGxldGUgY29tbWFuZCBpcyBzaG93biBiZWxvdy4KClwKCiMjIEltcG9ydCBhbmQgY2hlY2sgeW91ciBkYXRhCgpUeXBlIHRoZSBmb2xsb3dpbmcgbGluZSBpbnRvIHlvdXIgc2NyaXB0IGZpbGUuIEV4ZWN1dGUgdGhlIGNvbW1hbmQgYXMgeW91IGRpZCBpbiB0aGUgcHJldmlvdXMgbW9kdWxlIGJ5IHBsYWNpbmcgdGhlIGN1cnNvciBhbnl3aGVyZSBvbiB0aGUgbGluZSBhbmQgdHlwaW5nIGN0cmwtRW50ZXIgKFdpbmRvd3MpIG9yIGNtZC1FbnRlciAoTWFjKS4KCmBgYHtyIHJlYWQuY3N2fQojIFNhdmUgYW4gaW1wb3J0ZWQgZGF0YSBmcmFtZSBpbnRvIGEgbmFtZWQgdmFyaWFibGUKZ2FwbWluZGVyX2RhdGEgPC0gcmVhZC5jc3YoImRhdGEvZ2FwbWluZGVyX2RhdGFfMjAwNy5jc3YiKQpgYGAKCj4gTi5CLiBgc3RyaW5nc0FzRmFjdG9ycz1gOiBBIGNvbW1vbiBhcmd1bWVudCBmb3IgYHJlYWQuY3N2YCB3YXMgYHN0cmluZ3NBc0ZhY3RvcnM9YCwgd2hpY2ggY291bGQgYmUgYFRSVUVgIG9yIGBGQUxTRWAuIFRoZSBkZWZhdWx0IGZvciBSICh2NC4wKykgaXMgYHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0VgIGFzIFIgd2lsbCBhdXRvbWF0aWNhbGx5IHRyZWF0IGNoYXJhY3RlciBkYXRhIGFzIGNhdGVnb3JpY2FsIGZvciB0aGUgcHVycG9zZXMgb2YgdmlzdWFsaXNhdGlvbi4gTWFueSBzdGF0aXN0aWNhbCBtZXRob2RzIG9yIG9sZGVyIHZlcnNpb25zIG9mIFIsIGhvd2V2ZXIsIHN0aWxsIHJlcXVpcmUgY29udmVyc2lvbiB0byBmYWN0b3JzLiBTZWUgdGhlIGFwcGVuZGl4IGJlbG93IGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIGNyZWF0aW5nIGZhY3RvcnMuCgpXaGVuIGltcG9ydGVkIGludG8gUiwgdGhlIGRhdGEgZnJvbSB0aGUgY3N2IGZpbGUgYXJlIHRyYW5zbGF0ZWQgaW50byBhbiBSIG9iamVjdCBjYWxsZWQgYSAqKmRhdGEgZnJhbWUqKi4gRGF0YSBmcmFtZXMgYXJlIHNpbXBseSB0YWJsZXMsIG9yZ2FuaXNlZCBpbnRvIHJvd3MgYW5kIGNvbHVtbnMuIFRoZSBjb2x1bW5zIGhhdmUgbmFtZXMgdGFrZW4gZnJvbSB0aGUgZmlyc3Qgcm93IG9mIHRoZSBjc3YgZmlsZSwgYW5kIGVhY2ggc3Vic2VxdWVudCByb3cgb2YgdGhlIGNzdiBmaWxlIGJlY29tZXMgYSByb3cgaW4gdGhlIGRhdGEgZnJhbWUuIAoKV2Ugc3RvcmUgdGhlIGRhdGEgZnJhbWUgaW4gYSBuYW1lZCB2YXJpYWJsZSBzbyB0aGF0IHdlIGNhbiByZWZlciB0byBpdCBsYXRlciAoaS5lLiwgcGVyZm9ybSBhbmFseXNlcyBvbiBpdCkuIFdlIHVzZSB0aGUgYXNzaWdubWVudCBvcGVyYXRvciwgYXMgd2UgZGlkIGluIG91ciBwcmV2aW91cyBtb2R1bGUuIAoKQWZ0ZXIgc3RvcmluZyBvdXIgZGF0YSBmcmFtZSBpbnRvIGEgdmFyaWFibGUsIHlvdSBzaG91bGQgYWx3YXlzIGNoZWNrIHRoYXQgdGhlIGRhdGEgaGF2ZSBiZWVuIGltcG9ydGVkIGNvcnJlY3RseS4gRGF0YSBlbnRyeSBlcnJvcnMgY2FuIGNhdXNlIFIgdG8gbWFrZSB0aGUgd3JvbmcgYXNzdW1wdGlvbnMgYWJvdXQgeW91ciBkYXRhLiBJZiB5b3UgaGF2ZSBhIGNvbHVtbiBvZiBudW1iZXJzIHRoYXQgY29udGFpbnMgZXZlbiBvbmUgYWNjaWRlbnRhbCBhbHBoYWJldGljIGNoYXJhY3RlciAodHlwb3MgZG8gaGFwcGVuKSBSIHdpbGwgY29uc2lkZXIgdGhlIHdob2xlIGNvbHVtbiB0byBiZSBzdHJpbmdzLiBMYXRlciwgUiB3aWxsIGdpdmUgdGhlIHdyb25nIHJlc3VsdHMgd2hlbiB5b3UgcGVyZm9ybSBzdGF0aXN0aWNhbCBhbmFseXNlcyBvbiB0aGVzZSBkYXRhIChvciBpdCB3aWxsIHJlZnVzZSB0byBwZXJmb3JtIHRoZW0gYXQgYWxsKS4gCgpVc2UgdGhlIGZvbGxvd2luZyBjb21tYW5kcyB0byBpbnNwZWN0IHlvdXIgaW1wb3J0ZWQgZGF0YToKCmBgYHtyIGNoZWNrIDF9CiMgV3JpdGUgdGhlIGZpcnN0IGZldyBsaW5lcyBvZiBhIGRhdGEgZnJhbWUgdG8gdGhlIGNvbnNvbGUgd2l0aCBmdW5jdGlvbiBoZWFkCmhlYWQoZ2FwbWluZGVyX2RhdGEpCgojIFdyaXRlIHRoZSBsYXN0IGZldyBsaW5lcyBvZiBhIGRhdGEgZnJhbWUgdG8gdGhlIGNvbnNvbGUgd2l0aCBmdW5jdGlvbiB0YWlsCnRhaWwoZ2FwbWluZGVyX2RhdGEpCmBgYAoKYGBge3IgY2hlY2sgMn0KIyBEaXNwbGF5IHRoZSBudW1iZXIgb2YgbGluZXMsIHRoZSBjb2x1bW4gbmFtZXMsIGFuZCB0aGUgZGF0YSB0eXBlIG9mIGVhY2ggCiMgY29sdW1uLCB3aXRoIGZ1bmN0aW9uIHN0ciAoc2hvcnQgZm9yICdzdHJ1Y3R1cmUnKQpzdHIoZ2FwbWluZGVyX2RhdGEpCmBgYAoKRWFjaCBjb2x1bW4gaW4gYSBkYXRhIGZyYW1lIGlzIGFzc29jaWF0ZWQgd2l0aCBhIGRhdGEgdHlwZSAqKmNocioqLCAqKmludCoqLCBvciAqKm51bSoqLiBUaGVzZSBpbmRpY2F0ZSB3aGF0IGtpbmQgb2YgZGF0YSBSIGlkZW50aWZpZWQgaW4gdGhlIGlucHV0IGZpbGUuIENvbHVtbnMgdGhhdCBhcmUgKipjaHIqKiBjb250YWluIHN0cmluZ3MgKGNoYXJhY3RlcnMpLCBjb2x1bW5zIHRoYXQgYXJlICoqaW50KiogY29udGFpbiBpbnRlZ2VycyAod2hvbGUgbnVtYmVycykgYW5kIGNvbHVtbnMgdGhhdCBhcmUgbnVtIGNvbnRhaW4gbnVtYmVycyB3aXRoIGEgZGVjaW1hbCBwYXJ0LiBBbHdheXMgY2hlY2sgdGhhdCB0aGVzZSBwcm9wZXJ0aWVzIG9mIHRoZSBpbXBvcnRlZCBjb2x1bW5zIGFyZSBjb3JyZWN0IGZvciB5b3VyIGRhdGEgc2V0LiBJZiB0aGV5IGFyZSBub3QsIHlvdSBtdXN0IGxvY2F0ZSBhbmQgY29ycmVjdCBhbnkgZXJyb3JzIGluIHlvdXIgY3N2IGZpbGUuCgpZb3UgY2FuIHNlZSB0aGUgc2FtZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc3RydWN0dXJlIG9mIGEgZGF0YSBmcmFtZSBpbiB0aGUgRW52aXJvbm1lbnQgcGFuZWwgb2YgUlN0dWRpbyAodXBwZXItcmlnaHQgb2Ygc2NyZWVuOyBFbnZpcm9ubWVudCB0YWIpLiBXaGVuIHlvdSBzdWNjZXNzZnVsbHkgaW1wb3J0IGEgY3N2IGZpbGUgaW50byBhIHZhcmlhYmxlIHdpdGggYHJlYWQuY3N2YCwgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIGFwcGVhcnMgaW4gdGhlIEVudmlyb25tZW50IHBhbmUuIENsaWNrIHRoZSBibHVlIGFycm93IGJlc2lkZSB0aGUgb2JqZWN0IHRvIGRpc3BsYXkgdGhlIGRldGFpbHMgb2YgaXRzIHN0cnVjdHVyZS4KCmBgYHtyIGZpZzAyLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aCA9ICI1MCUiLCBmaWcuY2FwID0gIkRhdGEgRnJhbWUgaW4gRW52aXJvbm1lbnQgUGFuZSIsIGZpZy5wb3MgPSAiSCIsIGVjaG8gPSBGQUxTRX0KaW5jbHVkZV9ncmFwaGljcygiaW1hZ2VzLzAyLWRmX2luX2Vudl9wYW5lLnBuZyIpCmBgYAoKXAoKCiMjIFNlbGVjdGluZyBhbmQgdXNpbmcgY29sdW1ucyBvZiBkYXRhCgpJbiBib3RoIG9mIHRoZSBhYm92ZSBkaXNwbGF5cywgZWFjaCBjb2x1bW4gbmFtZSBpcyBwcmVmYWNlZCB3aXRoIGAkYC4gV2UgdXNlIHRoZSBgJGAgb3BlcmF0b3IgdG8gc2VsZWN0IGluZGl2aWR1YWwgY29sdW1ucyBvZiBkYXRhIGZyb20gYSBkYXRhIGZyYW1lLiBGb3IgZXhhbXBsZSwgdG8gc2VsZWN0IGp1c3QgdGhlIGxpZmUgZXhwZWN0YW5jeSB2YWx1ZXMgZnJvbSBvdXIgZGF0YSwgd2Ugc2F5IGBnYXBtaW5kZXJfZGF0YSRsaWZlRXhwYC4gKEJlIHN1cmUgdG8gdHlwZSB0aGUgY29sdW1uIG5hbWUgZXhhY3RseSBhcyBpdCBhcHBlYXJzIGluIHRoZSBvdXRwdXQgb2YgYHN0cmAgYW5kIGluIHRoZSBFbnZpcm9ubWVudCBwYW5lLikgV2UgY2FuIHVzZSB0aGlzIGV4cHJlc3Npb24gZGlyZWN0bHkgYXMgdGhlIGFyZ3VtZW50IHRvIGEgZnVuY3Rpb24sIG9yIHdlIGNhbiBzdG9yZSB0aGUgc2VsZWN0ZWQgY29sdW1uIGluIGEgdmFyaWFibGUgKHdoaWNoIHdpbGwgYmUgYSB2ZWN0b3IpIGZvciBsYXRlciBwcm9jZXNzaW5nLiAKCkZvciBleGFtcGxlOgoKYGBgIHtyIHNpbmdsZSBjb2x1bW4gZXhhbXBsZXN9CiMgUHJpbnQgdGhlIGZpcnN0IGZldyB2YWx1ZXMgb2YgY29sdW1uIHllYXIKaGVhZChnYXBtaW5kZXJfZGF0YSR5ZWFyKQoKIyBTdG9yZSBjb2x1bW4gY29udGluZW50IGluIGEgbmV3IHZhcmlhYmxlCmNvbnRpbmVudHMgPC0gZ2FwbWluZGVyX2RhdGEkY29udGluZW50CmBgYAoKXAoKIyMgQ3JlYXRpbmcgZ3JhcGhzIGluIFIKClRoZXJlIGFyZSBhIHZhcmlldHkgb2YgY29tcGxleCBhbmFseXNlcyB0aGF0IHdlIGNhbiBwZXJmb3JtIG9uIGEgZGF0YSBmcmFtZSB1c2luZyBSJ3MgYnVpbHQtaW4gc3RhdGlzdGljYWwgZnVuY3Rpb25zIGFuZCB0aG9zZSBhdmFpbGFibGUgaW4gYWRkaXRpb25hbCBwYWNrYWdlcyBhbmQgbGlicmFyaWVzLiBXZSB3aWxsIGV4cGxvcmUgbWFueSBvZiB0aGVzZSB0ZWNobmlxdWVzIGluIGxhdGVyIG1vZHVsZXMuIEhvd2V2ZXIsIGFuIGVmZmVjdGl2ZSBmaXJzdCBzdGVwIGluIGdldHRpbmcgdG8ga25vdyBhIGRhdGEgc2V0IGlzIHRvIGdlbmVyYXRlIHBsb3RzIGFuZCBncmFwaHMgdG8gcmVwcmVzZW50IHZpc3VhbGx5IHRoZSBwYXR0ZXJucyBpbiB0aGUgZGF0YS4KClwKCiMjIyBTaW1wbGUgcGxvdHMgLSB0aGUgaGlzdG9ncmFtCgpBIGhpc3RvZ3JhbSBzaG93cyB0aGUgKipmcmVxdWVuY3kgZGlzdHJpYnV0aW9uKiogb2YgYSBkYXRhIHNldC4gVGhhdCBpcywgaXQgc2hvd3MgY291bnRzIG9mIHRoZSAgZGlmZmVyZW50IHZhbHVlcyBvZiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIChvciByYW5nZXMgb2YgdmFsdWVzLCBmb3IgY29udGludW91cyB2YXJpYWJsZXMpLiBXZSBnZW5lcmF0ZSB0aGlzIGdyYXBoIHdpdGggZnVuY3Rpb24gYGhpc3RgLiBUaGUgZ3JhcGggd2lsbCBiZSBkaXNwbGF5ZWQgaW4gdGhlIFBsb3RzIHRhYiBvZiBSU3R1ZGlvJ3MgbG93ZXItcmlnaHQgcGFuZS4KCmBgYHtyIGhpc3R9CiMgSGlzdG9ncmFtIG9mIGxpZmUgZXhwZWN0YW5jeSB2YWx1ZXMgZnJvbSBnYXBtaW5kZXIKaGlzdChnYXBtaW5kZXJfZGF0YSRsaWZlRXhwKQpgYGAKClwKCldlIHNlZSB0aGF0IHRoZSBkaXN0cmlidXRpb24gb2YgbGlmZSBleHBlY3RhbmN5IGlzIGFwcHJveGltYXRlbHkgYmVsbC1zaGFwZWQsIHdpdGggbWFueSBzY29yZXMgYmV0d2VlbiA2NSBhbmQgODUsIGFuZCBhIHNtYWxsIG51bWJlciBvZiBleHRyZW1lIHZhbHVlcyBncmVhdGVyIHRoYW4gODAgb3IgbGVzcyB0aGFuIDMwLiAKClwKCiMjIyBCb3hwbG90cwpUaGUgYm94cGxvdCBhbGxvd3MgdXMgdG8gY29tcGFyZSBkaXN0cmlidXRpb24gaW5mb3JtYXRpb24gYmV0d2VlbiBncm91cHMuIEZvciBleGFtcGxlLCB3ZSBjYW4gY29tcGFyZSBsaWZlIGV4cGVjdGFuY3kgZm9yIHRoZSBkaWZmZXJlbnQgY29udGluZW50cy4gCgpUaGUgUiBmdW5jdGlvbiBgYm94cGxvdGAgYWNjZXB0cyB0d28gYXJndW1lbnRzLiAKClRoZSBmaXJzdCBhcmd1bWVudCBpcyB0aGUgKipmb3JtdWxhKiouIFRoaXMgaXMgYSBjb21wbGV4LCB5ZXQgdmVyeSBjb21tb24sIGFyZ3VtZW50IGZvcm1hdCBmb3IgUiBzdGF0aXN0aWNhbCBmdW5jdGlvbnMuIFRoZSBmb3JtdWxhIGRlc2NyaWJlcyBhIGxpbmVhciBtb2RlbCBmb3IgYSBkYXRhIHNldCB3aXRoIHRoZSBnZW5lcmFsIHN0cnVjdHVyZTogKipkZXBlbmRlbnQgb3IgcHJlZGljdGVkIHZhcmlhYmxlIH4gaW5kZXBlbmRlbnQgdmFyaWFibGVzIG9yIHByZWRpY3RvcnMqKiwgdXNpbmcgY29sdW1ucyBuYW1lcyBmcm9tIHRoZSBkYXRhIGZyYW1lLiBUaGUgfiAodGlsZGUpIGlzIHJlYWQgYXMgImRlcGVuZHMgb24iIG9yICJpcyBwcmVkaWN0ZWQgYnkiLiBGb3Igb3VyIGV4YW1wbGUsIHdlIGFyZSBpbnRlcmVzdGVkIGluIHRoZSB3YXkgdGhhdCBsaWZlIGV4cGVjdGFuY3kgaXMgZGVwZW5kZW50IG9uIHRoZSBjb250aW5lbnQsIHNvIHdlIHNwZWNpZnkgb3VyIGZvcm11bGEgYXMgKipsaWZlRXhwIH4gY29udGluZW50KiouICBXZSB3aWxsIHNlZSBtb3JlIGNvbXBsZXggZXhhbXBsZXMgb2YgdGhlIGZvcm11bGEgYXJndW1lbnQgbGF0ZXIgaW4gdGhlIHNlbWVzdGVyLgoKVGhlIHNlY29uZCBhcmd1bWVudCB0byBib3hwbG90IGlzIHRoZSBkYXRhIGZyYW1lLgoKYGBge3IgYm94cGxvdDAxfQpib3hwbG90KGxpZmVFeHAgfiBjb250aW5lbnQsIGdhcG1pbmRlcl9kYXRhKQpgYGAKClwKCkJveHBsb3RzIGVmZmljaWVudGx5IGlsbHVzdHJhdGUgYm90aCB0aGUgY2VudHJhbCB0ZW5kZW5jeSBhbmQgdGhlIHZhcmlhYmlsaXR5IG9mIGEgZGF0YSBzZXQuIEVhY2ggZ3JleSBib3ggZXh0ZW5kcyBmcm9tIHRoZSBmaXJzdCBxdWFydGlsZSB0byB0aGUgdGhpcmQgcXVhcnRpbGUgb2YgaXRzIGlucHV0IHZhbHVlcy4gVGhlIGRhcmsgbGluZSBhY3Jvc3MgdGhlIGJveCBpcyBhdCB0aGUgbWVkaWFuLiBUaGUgdHdvIHRoaW4gbGluZXMgb3V0c2lkZSB0aGUgcXJleSBib3ggc2hvdyB0aGUgdmFsdWVzIG9mIHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIHNjb3JlcywgZXhjbHVkaW5nIGV4dHJlbWUgb3V0bGllcnMuIElmIGV4dHJlbWUgb3V0bGllcnMgYXJlIHByZXNlbnQsIHRoZXkgYXJlIHNob3duIGFzIHNtYWxsIGNpcmNsZXMuIFRoaXMgZmlndXJlIGNsZWFybHkgaWxsdXN0cmF0ZXMgdGhhdCwgaW4gdGhlIGdhcG1pbmRlciBkYXRhLCBsaWZlIGV4cGVjdGFuY3kgLS0gYm90aCBjZW50cmFsIHRlbmRlbmN5IGFuZCB2YXJpYWJsaXR5IC0tIGlzIG5vdCB0aGUgc2FtZSBmb3IgYWxsIGNvbnRpbmVudHMuCgoKXAoKIyMgUGxvdHRpbmcgd2l0aCBnZ3Bsb3QyCgpUaGUgYGhpc3RgIGFuZCBgYm94cGxvdGAgZnVuY3Rpb25zIGFyZSBwYXJ0IG9mIEJhc2UgUi4gVGhleSBhcmUgdXNlZnVsLCBidXQgZm9yIG1vcmUgZWxhYm9yYXRlLCBwdWJsaWNhdGlvbi1xdWFsaXR5IGdyYXBocywgd2UgY2FuIHVzZSB0aGUgdGhpcmQtcGFydHkgbGlicmFyeSAqKmdncGxvdCoqIGNvbnRhaW5lZCBpbiBwYWNrYWdlICoqZ2dwbG90MioqLiBUaGUgZ2dwbG90IGxpYnJhcnkgaXMgYSB2ZXJ5IHBvcHVsYXIgZGF0YSB2aXN1YWxpc2F0aW9uIHRvb2wgYmFzZWQgb24gYW4gZWxhYm9yYXRlIHN5bWJvbGljIHN5c3RlbSBjYWxsZWQgdGhlICdHcmFtbWFyIG9mIEdyYXBoaWNzJy4gCgpUaGUgc3ludGF4IG9mIGdncGxvdCBpcyBjb21wbGV4LCBhbmQgd2Ugd2lsbCBjb25jZW50cmF0ZSBvbiB0aGUgZm91bmRhdGlvbnMgaW4gdGhpcyBtb2R1bGUuIEZvciBhZGRpdGlvbmFsIGRldGFpbCwgc2VlIHRoZSBEYXRhIFZpc3VhbGlzYXRpb24gY2hhcHRlciBpbiB0aGUgUiBmb3IgRGF0YSBTY2llbmNlIG9ubGluZSB0ZXh0LCBhdCAgaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9kYXRhLXZpc3VhbGlzYXRpb24uaHRtbC4KClwKCiMjIyBTZW1hbnRpY3Mgb2YgZ2dwbG90CllvdSBjYW4gdGhpbmsgb2YgYSBnZ3Bsb3QgZ3JhcGggYXMgYmVpbmcgYnVpbHQgYXMgYSBzZXF1ZW5jZSBvZiBsYXllcnMuIE9uIHRoZSBib3R0b20gaXMgdGhlIGJhc2Ugb2YgdGhlIGdyYXBoLCB0aGVuIHRoZSBheGVzIGFuZCB0aGUgZGF0YSBhcmUgbGF5ZXJlZCBvbiwgdGhlbiB0aXRsZXMgYW5kIG5vdGF0aW9ucyBhbmQgb3RoZXIgZmVhdHVyZXMuIEEgZ2dwbG90IGNvbW1hbmQgcmVmbGVjdHMgdGhpcyBsYXllcmVkIHN0cnVjdHVyZS4KClwKCiMjIyBCdWlsZGluZyBhIGdyYXBoCgpUbyB1c2UgdGhlIGdncGxvdCBsaWJyYXJ5LCB3ZSBtdXN0IGluc3RhbGwgdGhlIGdncGxvdDIgcGFja2FnZSAob25jZSBvbiBhIGNvbXB1dGVyKSBhbmQgaW52b2tlIHRoZSBsaWJyYXJ5IGNvbW1hbmQgKGZvciBldmVyeSBSU3R1ZGlvIHNlc3Npb24pLgoKYGBge3IgaW5zdGFsbCBhbmQgbG9hZCwgZXZhbCA9IEZBTFNFfQojIE9uY2Ugb24gYW55IGNvbXB1dGVyCmluc3RhbGwucGFja2FnZXMoZ2dwbG90MikKCiMgT25jZSBmb3IgYW55IFJTdHVkaW8gc2Vzc2lvbgpsaWJyYXJ5KGdncGxvdDIpCmBgYAoKRXZlcnkgZ3JhcGggcmVwcmVzZW50cyBhIGRhdGEgZnJhbWUuIFRoZSBiYXNlIHBhcnQgb2YgYW55IGdncGxvdCBjb21tYW5kIGlzIGEgY2FsbCB0byBmdW5jdGlvbiBgZ2dwbG90KClgIHBhc3NpbmcgaW4gdGhlIGRhdGEgZnJhbWUsIGFzc2lnbmVkIHRvIGZ1bmN0aW9uIGFyZ3VtZW50IGBkYXRhYC4KCmBgYHtyIGdncGxvdF9iYXNlfQojIFRoZSBnZ3Bsb3QgYmFzZSBsYXllcgpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlcl9kYXRhKQpgYGAKClwKCklmIHlvdSBydW4gdGhpcyBjb21tYW5kIGZyb20gdGhlIFJTdHVkaW8gY29uc29sZSBvciBhbiBSIHNjcmlwdCwgdGhlIGdyZXkgc3F1YXJlIHNob3duIGFib3ZlIGFwcGVhcnMgaW4gdGhlIFBsb3RzIHBhbmUuIFRoaXMgaW5kaWNhdGVzIHRoYXQgZ2dwbG90IGlzIHJlYWR5IHRvIGRyYXcgYSBmaWd1cmUgLS0gdGhpcyBpcyB0aGUgIGJvdHRvbSBsYXllciBvZiBhIGdncGxvdCBncmFwaC4KClRvIGFkZCB4IGFuZCB5IGF4ZXMgdG8gdGhlIGdyYXBoLCB3ZSBuZWVkIHRvIGRlZmluZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gaW5mb3JtYXRpb25hbCBlbGVtZW50cyBpbiB0aGUgZGF0YSBzZXQgKHRoZSB2YXJpYWJsZXMgd2Ugd2FudCB0byBwbG90KSBhbmQgdmlzdWFsIGVsZW1lbnRzIGluIG91ciBncmFwaCAodGhlIGF4ZXMpLiBJbiBnZ3Bsb3QgdGhpcyByZWxhdGlvbnNoaXAgaXMgYSAqKm1hcHBpbmcqKi4gVG8gaW5pdGlhbGlzZSBhIG1hcHBpbmcsIHdlIGlkZW50aWZ5IGEgcGFydGljdWxhciBlbGVtZW50IG9mIHRoZSBncmFwaCAoZS5nLiB0aGUgeC1heGlzKSBhbmQgYXNzaWduIGEgcGFydGljdWxhciBlbGVtZW50IG9mIHRoZSBkYXRhIChlLmcuIGEgY29sdW1uIGluIHRoZSBkYXRhIGZyYW1lKSB0byBpdC4gVGhpcyBhc3NpZ25tZW50IGlzIGNhbGxlZCBhbiAqKmFlc3RoZXRpYyoqIGluIHRoZSBHcmFtbWFyIG9mIEdyYXBoaWNzLCBhbmQgaW4gZ2dwbG90IHdlIHVzZSBmdW5jdGlvbiBgYWVzKClgIHRvIHNwZWNpZnkgYWVzdGhldGljcy4gCgpJbWFnaW5lIHRoYXQgd2Ugd2lzaCB0byBtYWtlIGEgZ3JhcGggc2hvd2luZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gcGVyIGNhcGl0YSBHRFAgYW5kIGxpZmUgZXhwZWN0YW5jeSAodHdvIGNvbHVtbnMgaW4gZ2FwbWluZGVyX2RhdGEpLiBXZSBtYXAgdGhlIGZpcnN0IHZhcmlhYmxlIHRvIHRoZSB4IGF4aXMgKGFyZ3VtZW50IHgpIG9mIG91ciBncmFwaCBhbmQgdGhlIHNlY29uZCB0byB0aGUgeSBheGlzIChhcmd1bWVudCB5KS4gVGhpcyB3aWxsIGFkZCBhIG5ldyBsYXllci4gV2UgYWRkIHRoaXMgbmV3IGluZm9ybWF0aW9uIHRvIHRoZSBnZ3Bsb3QoKSBiYXNlIGNhbGwgYXMgc2hvd24gYmVsb3cuIE5vdGUgdGhhdCB3ZSBkb24ndCBuZWVkIHRvIHVzZSB0aGUgYCRgIG9wZXJhdG9yIGhlcmUsIGFzIGFsbCBjb2x1bW4gbmFtZXMgaW4gYSBnZ3Bsb3QgY29tbWFuZCBhcHBseSB0byB0aGUgc3VwcGxpZWQgZGF0YSBmcmFtZS4KCmBgYHtyIG1hcHBpbmdfeF9hbmRfeX0KZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXJfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCApKQoKCmBgYAoKXAoKV2UgaGF2ZSBhZGRlZCBhIG5ldyBsYXllciB0byBvdXIgZ3JhcGggd2l0aCBheGVzIGFuZCBncmlkIGxpbmVzLiBOb3RlIHRoYXQgdGhlIGF4ZXMnIHRpYyB2YWx1ZXMgYXJlIGNvcnJlY3RseSBmb3JtYXR0ZWQgZm9yIHRoZSBhc3NvY2lhdGVkIGRhdGEgYW5kIHRoZSBkYXRhIGZyYW1lIGNvbHVtbiBuYW1lcyBhcmUgdXNlZCBhcyB0aGUgYXhpcyBsYWJlbHMgKHdlIHdpbGwgc2VlIGhvdyB0byBpbXByb3ZlIHRob3NlIGxhYmVscyBsYXRlcikuCgpUbyBhZGQgcG9pbnRzIHRvIG91ciBncmFwaCwgd2Ugc3BlY2lmeSBhICoqZ2VvbWV0cnkqKiAoYW5vdGhlciB0ZXJtIGZyb20gdGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MpLiBUaGVyZSBhcmUgbWFueSwgbWFueSBhdmFpbGFibGUgZ2VvbWV0cmllcyBpbiBnZ3Bsb3QsIGNvcnJlc3BvbmRpbmcgdG8gYWxsIHRoZSBkaWZmZXJlbnQgc29ydHMgb2YgZ3JhcGhzIC0tIHNjYXR0ZXJwbG90cywgYmFyIHBsb3RzLCBwaWUgY2hhcnRzLCBsaW5lIGdyYXBocywgZXRjLiAtLSB0aGF0IHlvdSBtaWdodCB3aXNoIHRvIG1ha2UuIEZvciBvdXIgY3VycmVudCBncmFwaCwgd2Ugd2lzaCB0byBwbGFjZSBhIHBvaW50IGF0IHRoZSBpbnRlcnNlY3Rpb24gb2YgcGVyIGNhcGl0YSBHRFAgKG91ciB4IGF4aXMpIGFuZCBsaWZlIGV4cGVjdGFuY3kgKG91ciB5IGF4aXMpIGZvciBlYWNoIHJvdyBpbiB0aGUgaW5wdXQgZGF0YSBmcmFtZS4gVG8gYWRkIHRoaXMgZ2VvbWV0cnkgdG8gZ2dwbG90IGFwcGVuZCBgZ2VvbV9wb2ludCgpYCB0byB5b3VyIGN1cnJlbnQgZ2dwbG90IGNvbW1hbmQgdXNpbmcgdGhlIGArYCBvcGVyYXRvci4gSXQgaXMgY29udmVudGlvbmFsIHRvIHBsYWNlIGVhY2ggY2h1bmsgb2YgdGhlIGdncGxvdCBjb21tYW5kIG9uIGl0cyBvd24gbGluZSBpbiB0aGUgY29kZS4KCmBgYHtyIGdlb21fcG9pbnR9CiMgQWRkIHBvaW50cyAoYSAnZ2VvbWV0cnknKSB0byB0aGUgZ3JhcGgKZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXJfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCApKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKXAoKVGhpcyB0eXBlIG9mIGdyYXBoICh1c3VhbGx5IGNhbGxlZCBhICoqc2NhdHRlcnBsb3QqKikgaWxsdXN0cmF0ZXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byBkZXBlbmRlbnQgdmFyaWFibGVzLiBFdmVuIGZyb20gdGhpcyB2ZXJ5IHNpbXBsZSBmaWd1cmUgd2UgY2FuIHNlZSB0aGF0IHRoZXJlIGlzIGEgZ2VuZXJhbCB0ZW5kZW5jeSBmb3IgaGlnaGVyIHBlciBjYXBpdGEgR0RQIHRvIGJlIGFzc29jaWF0ZWQgd2l0aCBoaWdoZXIgbGlmZSBleHBlY3RhbmN5IGluIHRoZSBnYXBtaW5kZXIgZGF0YS4KCkxpa2UgbW9zdCBmdW5jdGlvbnMsIGBnZW9tX3BvaW50YCBjYW4gYWNjZXB0IGFyZ3VtZW50cyB0aGF0IG1vZGlmeSBpdHMgYmVoYXZpb3VyLiBUaGUgYXJndW1lbnQgKipjb2xvdXIqKiBkZXRlcm1pbmVzIHRoZSBjb2xvdXIgb2YgdGhlIHBvaW50cyB0byBiZSBkcmF3biwgYW5kIGNhbiBiZSBhc3NpZ25lZCBhbnkgb2YgUidzIGJ1aWx0LWluIGNvbG91ciBuYW1lcyAoY2FsbCBmdW5jdGlvbiBgY29sb3VycygpYCB0byBsaXN0IGFsbCBwb3NzaWJsZSB2YWx1ZXMpIG9yIGEgaGV4aWRlY2ltYWwgUkdCIGNvZGUgKHNlZSBmb3IgZXhhbXBsZSwgaHR0cHM6Ly9yLWNoYXJ0cy5jb20vY29sb3JzLykuCgpgYGAge3IgY29sb3VyfQpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlcl9kYXRhLCBtYXBwaW5nID0gYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwICkpICsKICBnZW9tX3BvaW50KGNvbG91ciA9ICd0b21hdG8nKQpgYGAKClwKClRoaXMgbGl2ZW5zIHVwIG91ciBwbG90LCBidXQgaXQgZG9lc24ndCBhY3V0YWxseSBhZGQgYW55IG5ldyBpbmZvcm1hdGlvbi4gSXQgaXMgYmV0dGVyIHRlY2huaXF1ZSB0byB1c2UgY29sb3VyIHRvIHJlcHJlc2VudCBhbm90aGVyIG9mIG91ciBkYXRhIHZhcmlhYmxlcy4gV2UgbWlnaHQsIGZvciBleGFtcGxlLCB3aXNoIHRvIHVzZSBhIGRpZmZlcmVudCBjb2xvdXIgZm9yIGVhY2ggY29udGluZW50LCB0byBzZWUgaG93IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBHRFAgYW5kIGxpZmUgZXhwZWN0YW5jeSBkaWZmZXJzIGJldHdlZW4gY29udGluZW50cy4gVGhpcyByZXF1aXJlcyBkZWZpbmluZyBhIG1hcHBpbmcgYmV0d2VlbiBhIHZpc3VhbCBmZWF0dXJlIChjb2xvdXIpIGFuZCBhbiBlbGVtZW50IG9mIHRoZSBkYXRhIHNldCAoY29sdW1uIGNvbnRpbmVudCksIHNvIHdlIGluaXRpYWxpc2UgdGhlIGBtYXBwaW5nYCBwcm9wZXJ0eSB3aXRoIGZ1bmN0aW9uIGBhZXNgLCBpbiBvdXIgY2FsbCB0byBgZ2VvbV9wb2ludGAuCgpgYGB7ciBtYXBwaW5nX2NvbG91cn0KZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXJfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCApKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKGNvbG91ciA9IGNvbnRpbmVudCkpCmBgYAoKXAoKVGhpcyBncmFwaCBpbGx1c3RyYXRlcyBjbGVhcmx5IHRoYXQsIGluIHRoZSBnYXBtaW5kZXIgZGF0YSwgbGlmZSBleHBlY3RhbmN5IGFuZCBwZXIgY2FwaXRhIEdEUCB2YXJ5IHN1YnN0YW50aWFsbHkgYmV0d2VlbiBjb250aW5lbnRzLgoKWW91IHNob3VsZCBjYXJlZnVsbHkgY29tcGFyZSB0aGUgdHdvIHByZWNlZGluZyBncmFwaHMuIEluIHRoZSBmaXJzdCwgd2Ugc2ltcGx5IHNldCB0aGUgKipjb2xvdXIqKiBhcmd1bWVudCBvZiBmdW5jdGlvbmBnZW9tX3BvaW50YC4gSW4gdGhlIHNlY29uZCwgd2Ugc2V0IHRoZSAqKm1hcHBpbmcqKiBhcmd1bWVudCBvZiBgZ2VvbV9wb2ludGAgdXNpbmcgZnVuY3Rpb24gYGFlc2AuIEluIHRoZSBmb3JtZXIgZ3JhcGgsIGFsbCBwb2ludHMgYXJlIHRoZSBzYW1lIGNvbG91ci4gSW4gdGhlIGxhdHRlciBncmFwaCwgdGhlIGNvbG91ciBvZiBlYWNoIHBvaW50IGRlcGVuZHMgb24gaXRzIGNvbnRpbmVudCB2YWx1ZS4gVGhhdCBpcywgd2UgaGF2ZSAqKm1hcHBlZCoqIGNvbG91ciB0byBjb250aW5lbnQuIAoKXAoKIyMjIENob29zaW5nIGdlb21ldHJpZXMKCkl0IGlzIGVzc2VudGlhbCB0byBzZWxlY3QgdGhlIGNvcnJlY3QgdHlwZSBvZiBncmFwaCAodGhlIGNvcnJlY3QgZ2VvbWV0cnkgaW4gZ2dwbG90KSBmb3IgdGhlIGRhdGEgcGF0dGVybiB5b3Ugd2lzaCB0byBpbGx1c3RyYXRlLgoKQXNzdW1lLCBmb3IgZXhhbXBsZSwgdGhhdCB5b3Ugd2lzaCB0byBzaG93IHRoZSBjaGFuZ2UgaW4gbGlmZSBleHBlY3RhbmN5IGFjcm9zcyB5ZWFycywgZm9yIHRoZSBjb3VudHJ5IG9mIERlbm1hcmsuIEZpcnN0LCB3ZSBtdXN0IHNlbGVjdCBvdXQgb25seSB0aGUgcm93cyBmb3IgRGVubWFyayBmcm9tIG91ciBkYXRhIGZyYW1lLiAoV2Ugd2lsbCBjb25zaWRlciBzZWxlY3Rpb24gaW4gZGV0YWlsIGluIG5leHQgd2VlaydzIG1vZHVsZS4gRm9yIG5vdywganVzdCBub3RlIHRoYXQgYmV0d2VlbiB0aGUgc3F1YXJlIGJyYWNrZXRzIHdlIHByb3ZpZGUgcm93IGFuZCBjb2x1bW4gY3JpdGVyaWEgZm9yIHNlbGVjdGlvbiwgYW5kIGFuIGVtcHR5IHZhbHVlIGZvciBjb2x1bW4gbWVhbnMgKiphbGwqKi4pIAoKV2Ugd2lsbCB0aGVuIHBhc3MgdGhlIHNlbGVjdGVkIGRhdGEgdG8gZ2dwbG90IGFzIGJlZm9yZSwgc3BlY2lmeWluZyB0aGUgbWFwcGluZyBvZiB0aGUgZGF0YSB0byB0aGUgeCBhbmQgeSBheGVzLgoKVGhlIGdyYXBoIHdpbGwgYmUgaWxsdXN0cmF0aW5nIGEgdHJlbmQgKGNoYW5nZSBpbiBhIHZhcmlhYmxlIGFjcm9zcyB0aW1lKS4gVHJlbmQgZ3JhcGhzIGFyZSB1c3VhbGx5IGRyYXduIHdpdGggYSBjb250aW51b3VzIGxpbmUgYmV0d2VlbiB0aGUgcGxvdHRlZCBwb2ludHMuIEluIGdncGxvdCwgdGhpcyBpcyBnZW9tZXRyeSBgZ2VvbV9saW5lYC4KClRoZSBjb21wbGV0ZSBjb2RlIGlzOgoKYGBge3IgbGluZV9ncmFwaH0KIyBTZWxlY3QgYWxsIHJvd3Mgd2hlcmUgdGhlIGNvdW50cnkgaXMgZXF1YWwgdG8gRGVubWFyay4gU2VsZWN0IGFsbCBjb2x1bW5zLgpkZW5tYXJrX2RhdGEgPC0gZ2FwbWluZGVyX2RhdGFbZ2FwbWluZGVyX2RhdGEkY291bnRyeSA9PSAiRGVubWFyayIsIF0KCmdncGxvdChkYXRhID0gZGVubWFya19kYXRhLCBtYXBwaW5nID0gYWVzKHggPSB5ZWFyLCB5ID0gbGlmZUV4cCkpICsKICBnZW9tX2xpbmUoKQpgYGAKClwKCldlIGNhbiB1c2UgZ2dwbG90IHRvIHByb2R1Y2UgYSBoaXN0b2dyYW0gZm9yIGxpZmUgZXhwZWN0YW5jeSAoYXMgd2UgZGlkIGluIEJhc2UgUiBhYm92ZSkgd2l0aCBgZ2VvbV9oaXN0b2dyYW1gLiBGb3IgaGlzdG9ncmFtcyB3ZSBvbmx5IG5lZWQgdG8gbWFwIHRoZSB4IGF4aXMsIGFzIHRoZSB5IGF4aXMgcmVwcmVzZW50cywgYnkgZGVmYXVsdCwgZnJlcXVlbmN5LiBXZSBjYW4gZW5oYW5jZSB0aGUgcGxvdCdzIGFwcGVhcmFuY2UgYnkgaW5pdGlhbGlzaW5nIGBnZW9tX2hpc3RvZ3JhbWAgYXJndW1lbnRzIGBjb2xvdXJgIHdoaWNoIHNldHMgdGhlIGJvcmRlciBhcm91bmQgdGhlIGJhcnMgb24gdGhlIGdyYXBoLCBhbmQgYGZpbGxgIHdoaWNoIHNldHMgdGhlIGludGVyaW9yIG9mIHRoZSBiYXJzIG9uIHRoZSBncmFwaC4KCmBgYHtyIGhpc3RvZ3JhbSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlcl9kYXRhLCBtYXBwaW5nID0gYWVzKHggPSBsaWZlRXhwKSkgKwogIGdlb21faGlzdG9ncmFtKGNvbG91ciA9ICJ3aGl0ZSIsIGZpbGwgPSAiZGFya2dyZWVuIikKYGBgCgpcCgpTaW1pbGFybHksIHdlIGNhbiByZXByb2R1Y2UgdGhlIGJveHBsb3QgYWJvdmUgd2l0aCBgZ2VvbV9ib3hwbG90YC4gSW4gQmFzZSBSIHdlIHVzZWQgYSAqKmZvcm11bGEqKiB0byBpZGVudGlmeSB0aGUgZGVwZW5kZW50IGFuZCBpbmRlcGVuZGVudCB2YXJpYWJsZXMgZm9yIHRoZSBib3hwbG90LiBXaXRoIGdncGxvdCwgd2UgdXNlIGEgbWFwcGluZyB0byBhc3NpZ24gdGhlIERWIHRvIHRoZSB4IGF4aXMgYW5kIHRoZSBJViB0byB0aGUgeSBheGlzLgoKYGBge3IgYm94cGxvdH0KZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXJfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gY29udGluZW50LCB5ID0gbGlmZUV4cCkpICsKICBnZW9tX2JveHBsb3QoKQoKCmBgYAoKXAoKIyMjIEV4ZXJjaXNlOgpXaGF0IHdvdWxkIHlvdSBwcmVkaWN0IHRvIGJlIHRoZSBlZmZlY3Qgb2Ygc3dhcHBpbmcgdGhlIHZhbHVlcyBvZiB4IGFuZCB5IGluIHRoZSBjYWxsIHRvIGBhZXNgIGFib3ZlPyBUZXN0IHlvdXIgcHJlZGljdGlvbi4KClwKCiMjIyBSZWZpbmluZyB0aGUgYXBwZWFyYW5jZSBvZiBhIHBsb3QKQWZ0ZXIgd2UgaGF2ZSBidWlsdCB0aGUgZm91bmRhdGlvbiBvZiBvdXIgcGxvdCB3aXRoIGRhdGEgYW5kIGdlb21ldHJ5LCB3ZSBjYW4gYWRkIGZ1cnRoZXIgbGF5ZXJzIHRvIG1vZGlmeSBvdGhlciB2aXN1YWwgZmVhdHVyZXMuIEZvciBleGFtcGxlLCB3ZSBjYW4gdXNlIGZ1bmN0aW9uIGBsYWJzYCB0byBzZXQgdGhlIGF4aXMsIGxlZ2VuZCwgYW5kIG1haW4gdGl0bGVzIG9mIG91ciBwbG90cy4gQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBlbmhhbmNlbWVudHMgdG8gb3VyIGZpZ3VyZSBpbGx1c3RyYXRpbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIEdEUCBhbmQgbGlmZSBleHBlY3RhbmN5IGJ5IGNvbnRpbmVudDoKCmBgYCB7ciBsYWJlbHN9CiMgTkI6IE11bHRpcGxlIGZ1bmN0aW9uIGFyZ3VtZW50cyAoYXMgaW4gbGFicyBiZWxvdykgY2FuCiMgYmUgcGxhY2VkIG9uIHNlcGFyYXRlIGxpbmVzIHRvIGltcHJvdmUgcmVhZGFiaWxpdHkKCmdncGxvdChkYXRhID0gZ2FwbWluZGVyX2RhdGEsIG1hcHBpbmcgPSBhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHAgKSkgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyhjb2xvdXIgPSBjb250aW5lbnQpKSArCiAgbGFicyh4ID0gIkdEUCBQZXIgQ2FwaXRhIiwgCiAgICAgICB5ID0gIkxpZmUgRXhwZWN0YW5jeSIsIAogICAgICAgdGl0bGUgPSAiR2FwIE1pbmRlciBEYXRhIDE5NTIgdG8gMjAwNyIsIAogICAgICAgY29sb3VyID0gIkNvbnRpbmVudCIpCmBgYAoKXAoKClRoZSBjb2RlIGZvciBnZ3Bsb3QgZm9ybWF0dGluZyBjYW4gZ2V0IGV4dHJlbWVseSBjb21wbGV4LCBhbmQgdGhlIGZ1bGwgZnVuY3Rpb25hbGl0eSBpcyBiZXlvbmQgdGhlIHNjb3BlIG9mIHRoaXMgbW9kdWxlLiBJbiBhZGRpdGlvbiwgdGhlcmUgYXJlIG1hbnksIG1hbnkgbW9yZSBnZW9tZXRyaWVzIGF2YWlsYWJsZSwgZWFjaCB3aXRoIGFwcHJvcHJpYXRlIGFyZ3VtZW50cyBhbmQgbWFwcGluZyBvcHRpb25zLgoKVGhlIGZvcm1hbCBkb2N1bWVudGF0aW9uIGZvciBnZ3Bsb3QgY2FuIGJlIGZvdW5kIGF0IGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL2luZGV4Lmh0bWwuIElmIHlvdSBwcmVmZXIgdHV0b3JpYWxzIGFuZCBnYWxsZXJpZXMsIHRoZXJlIGFyZSBtYW55IGF2YWlsYWJsZSBvbmxpbmUuIFR3byBnb29kIHBsYWNlcyB0byBzdGFydCBhcmUgaHR0cDovL3d3dy5jb29rYm9vay1yLmNvbS9HcmFwaHMvIGFuZCBodHRwczovL3d3dy5yLWdyYXBoLWdhbGxlcnkuY29tLy4KClwKCiMjIyBTYXZpbmcgZ2dwbG90cwpZb3UgY2FuIHNhdmUgZmlndXJlcyBtYWRlIHdpdGggZ2dwbG90IHRvIGltYWdlIGZpbGVzLCB3aGljaCBjYW4gdGhlbiBiZSB1c2VkIGluIGRvY3VtZW50cyBnZW5lcmF0ZWQgaW4gTVMgV29yZCBvciBvdGhlciB0ZXh0IGVkaXRvcnMuIFdlIGZpcnN0IHNhdmUgdGhlIG91dHB1dCBvZiBvdXIgZ2dwbG90IGNvbW1hbmQgaW50byBhIG5hbWVkIHZhcmlhYmxlICh0byBSIGEgZ2dwbG90IGlzIGEgZGF0YSBvYmplY3QganVzdCBsaWtlIGEgbnVtYmVyIG9yIGEgc3RyaW5nKS4gV2UgdGhlbiB1c2UgZnVuY3Rpb24gYGdnc2F2ZWAgdG8gZXhwb3J0IG91dCBwbG90IGFzIGFuIGltYWdlIGZpbGUuIFlvdSBzcGVjaWZ5IHRoZSBpbWFnZSBmb3JtYXQgYnkgc3VwcGx5aW5nIGFuIG91dGZpbGUgbmFtZSB3aXRoIHRoZSBjb3JyZXNwb25kaW5nIGZpbGUgc3VmZml4IChlLmcuIC5qcGcgb3IgLnBuZykuIEJ5IGRlZmF1bHQsIHRoZSBmaWxlIGlzIHNhdmVkIGludG8gdGhlIHdvcmtpbmcgZm9sZGVyIChpbiBvdXIgY2FzZSwgdGhlIGZvbGRlciBjb250YWluaW5nIG91ciBjc3YgYW5kIHNjcmlwdCBmaWxlcykuCgpgYGB7ciBnZ3NhdmV9CiMgU2F2ZSBhIGdncGxvdCB0byBhIHZhcmlhYmxlLiBUaGUgc3ludGF4IG9mIHRoZSBncHBsb3QgY29tbWFuZCBpcyB1bmFmZmVjdGVkCmdkcF9saWZlRXhwX3Bsb3QgPC0gZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXJfZGF0YSwgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCApKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKGNvbG91ciA9IGNvbnRpbmVudCkpICsKICB4bGFiKCJHRFAgUGVyIENhcGl0YSIpICsKICB5bGFiKCJMaWZlIEV4cGVjdGFuY3kiKSArCiAgZ2d0aXRsZSgiR2FwIE1pbmRlciBEYXRhIDE5NTIgdG8gMjAwNyIpCgojIEV4cG9ydCB0aGUgdmFyaWFibGUgYXMgYW4gaW1hZ2UgZmlsZS4gUHJvdmlkZSB0aGUgZmlsZSBuYW1lIGFuZCB0aGUgZ2dwbG90IG9iamVjdApnZ3NhdmUoZmlsZW5hbWUgPSAiZ2RwX2xpZmVFeHBfcGxvdC5wbmciLCBnZHBfbGlmZUV4cF9wbG90KQpgYGAKClwKClwKCiMjIENvbmNsdXNpb24KClRoaXMgZG9jdW1lbnQgaGFzIHByZXNlbnRlZCBhIHNpbXBsZSBpbnRyb2R1Y3Rpb24gdG8gd29ya2luZyB3aXRoIGNvbXBsZXRlIHRhYmxlcyBvZiBkYXRhIGluIFIuIFdlIGxlYXJuZWQgaG93IHRvIGltcG9ydCBhIGNzdiBmaWxlIGludG8gYSBkYXRhIGZyYW1lLCBhbmQgaG93IHRvIHVzZSBCYXNlIFIgb3IgbGlicmFyeSBgZ2dwbG90YCB0byBnZW5lcmF0ZSBncmFwaHMgdG8gaWxsdXN0cmF0ZSBpbXBvcnRhbnQgcGF0dGVybnMgaW4gb3VyIGRhdGEuCgpcCgojIyMgV2hhdCdzIE5leHQKCkVuc3VyZSB5b3UgaGF2ZSB0aGUgYHRpZHl2ZXJzZWAgaW5zdGFsbGVkIGZvciB0aGUgbmV4dCBtb2R1bGUuIFRoZSB0aWR5dmVyc2UgaXMgYSBjb2xsZWN0aW9uIG9mIHBhY2thZ2VzIChhIG1ldGFwYWNrYWdlKSB0aGF0IHByb3ZpZGUgYSBzdWNjaW5jdCBzeW50YXggZm9yIHBlcmZvcm1pbmcgZGF0YSBtYW5pcHVsYXRpb24gYW5kIGJhc2ljIGFuYWx5c2lzIGluIFIuICBSdW5uaW5nIGBpbnN0YWxsLnBhY2thZ2VzKHRpZHl2ZXJzZSlgIGluc3RhbGxzIGFsbCB0aGUgaW5kaXZpZHVhbCBwYWNrYWdlcyB0byB5b3VyIG1hY2hpbmUuCgpUaGUgdGlkeXZlcnNlIGNvbnNpc3RzIG9mIGBnZ3Bsb3QyYCAocGxvdHRpbmcpLCBgZHBseXJgIChkYXRhIG1hbmlwdWxhdGlvbiksIGB0aWR5cmAgKGRhdGEgdGlkeWluZyksIGByZWFkcmAgKGRhdGEgaW1wb3J0aW5nKSwgYHB1cnJyYCAoZnVuY3Rpb25hbCBwcm9ncmFtbWluZyksIGB0aWJibGVgIChhIHNwZWNpYWwgdHlwZSBvZiBkYXRhIGZyYW1lKSwgYHN0cmluZ3JgIChjb21tb24gdGFza3MgZm9yIHN0cmluZyBtYW5pcHVsYXRpb25zKSwgYW5kICBgZm9yY2F0c2AgKGRlYWxpbmcgd2l0aCBmYWN0b3JzKQogCgpgYGB7ciBpbnN0YWxsIHRpZHl2ZXJzZSwgZXZhbCA9IEZBTFNFfQojIERvd25sb2FkIGFuZCBpbnN0YWxsIHRoZSBwYWNrYWdlcyBvZiB0aWR5dmVyc2UKaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKYGBgCgoKYGBge3J9CiMgbG9hZCB0aGUgdGlkeXZlcnNlIHBhY2thZ2VzIGZvciB0aGUgY3VycmVudCBzZXNzaW9uCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCgojIyBBcHBlbmRpeCAtIEZhY3RvcnMKCklmIHlvdSBhcmUgZGVhbGluZyB3aXRoIGNhdGVnb3JpY2FsIGRhdGEsIFIgY2FsbHMgdGhlc2UgX0ZhY3RvcnNfLiBQcmlvciB0byBSIHZlcnNpb24gNC4wIHRoZSBkZWZhdWx0IGJlaGF2aW91ciBvZiBgcmVhZC5jc3ZgIHdhcyB0byBoYXZlIHRoZSBwYXJhbWV0ZXIgYHN0cmluZ3NBc0ZhY3RvcnM9VFJVRWAgYXMgdGhlIGRlZmF1bHQgd2hpY2ggd291bGQgcmVhZCBhbGwgY2hhcmFjdGVyIGRhdGEgaW4gYXV0b21hdGljYWxseSBhcyBhIF9mYWN0b3JfLiBGcm9tIHZlcnNpb24gNC4wIG9ud2FyZHMgdGhlIGRlZmF1bHQgaXMgYHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0VgLgoKVG8gY29udmVydCBhIGNvbHVtbiB0byBhIGZhY3RvciB5b3UgY2FuIHVzZSBgYXMuZmFjdG9yYC4gWW91IGNhbiB1c2UgYGxldmVsc2Agb24gYSBfZmFjdG9yXyB0byBzZWUgdGhlIGNhdGVnb3JpZXMuIEl0IGlzIGJlc3QgdG8gd2FpdCB1bnRpbCBhZnRlciBhbnkgZGF0YSB0aWR5aW5nIGlzIHBlcmZvcm1lZCBiZWZvcmUgY29udmVydGluZyB0byBmYWN0b3JzLiBGb3IgaW5zdGFuY2UgaWYgd2UgcmVwZWF0ZWQgdGhlIGNvZGUgZm9yIHB1bGxpbmcgb3V0IHRoZSBjb250aW5lbnRzIGNvbHVtbiBmcm9tIHRoZSBnYXBtaW5kZXIgZGF0YXNldDoKCmBgYHtyfQojIFN0b3JlIGNvbHVtbiBjb250aW5lbnQgaW4gYSBuZXcgdmFyaWFibGUgYWZ0ZXIgY29udmVydGluZyB0byBhIGZhY3Rvcgpjb250aW5lbnRzX2ZhY3RvciA8LSBhcy5mYWN0b3IoZ2FwbWluZGVyX2RhdGEkY29udGluZW50KQoKIyBQYXNzIHRoZSB2YXJpYWJsZSB0byBmdW5jdGlvbiBsZXZlbHMgd2hpY2ggcmV0dXJucyB0aGUgdmFsdWVzCiMgb2YgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZQpsZXZlbHMoY29udGluZW50c19mYWN0b3IpCmBgYA==