Introduction to R - Part II
If you have previously-attended this course and are reviewing the
notes, please be aware of some re-organisation of the materials in this
section. Some of the later examples have been moved to Part 3.
We should have loaded the readr
library and imported an
example dataset into R
library(readr)
gapminder <- read_csv("raw_data/gapminder.csv")
Manipulating Columns
We are going to use functions from the
dplyr
package to manipulate the
data frame we have just created. It is perfectly possible to
work with data frames using the functions provided as part of “base
R”. However, many find it easy to read and write code using
dplyr
.
There are many more functions available in
dplyr
than we will cover today. An overview of all
functions is given in a cheatsheet.
Before using any of these functions, we need to load the
library:-
library(dplyr)
select
ing columns
We can access the columns of a data frame using the
select
function.
by name
Firstly, we can select column by name, by adding bare column names
(i.e. not requiring quote marks around the name) after the name of the
data frame, separated by a ,
.
select(gapminder, country, continent)
As we have to type the column names manually (no auto-complete!), we
have to make sure we type the name exactly as it appears in the data. If
select
sees a name that doesn’t exist in the data frame it
should give an informative message
Error: Can't subset columns that don't exist.
We can also omit columns from the ouput by putting a minus
(-
) in front of the column name. Note that this is not the
same as removing the column from the data permanently.
select(gapminder, -country)
range of columns
A range of columns can be selected by the :
operator.
select(gapminder, lifeExp:gdpPercap)
helper functions
There are a number of helper functions can be employed if we are
unsure about the exact name of the column.
select(gapminder, starts_with("co"))
select(gapminder, contains("life"))
# selecting the last and penultimate columns
select(gapminder, last_col(1),last_col())
It is also possible to use the column number in the selection.
select(gapminder, 4:6)
Question: Why might using the number of the column
sometimes be problematic? Consider what might happen if you wrote code
to select the last column of data from a file using the column number.
What would happen if the number of columns in the file was not
constant?
The select
function can be used with just a single
column name - in a similar manner to the $
operation we saw
last time. However, select
always returns a data
frame whereas $
gives a vector. Compare the output of
the following code chunks
select(gapminder, pop)
gapminder$pop
The consequence of this is that you cannot use functions such as
mean
in combination with select
pops <- select(gapminder, pop)
mean(pops)
In the next session we will see how to calculate summary statistics
on particular columns in our data. For now, a useful function is
pull
that will return the correct type of data required for
a function such as mean
.
pops <- pull(gapminder,pop)
mean(pops)
[1] 29601212
Restricting rows with filter
So far we have been returning all the rows in the output. We can use
what we call a logical test to filter the
rows in a data frame. This logical test will be applied to each
row and give either a TRUE
or FALSE
result.
When filtering, only rows with a TRUE
result get
returned.
For example we filter for rows where the lifeExp
variable is less than 40.
filter(gapminder, lifeExp < 40)
Internally, R creates a vector of TRUE
or
FALSE
; one for each row in the data frame. This is then
used to decide which rows to display.
Testing for equality can be done using ==
. This will
only give TRUE
for entries that are exactly the
same as the test string.
filter(gapminder, country == "Zambia")
N.B. For partial matches, the grepl
function and / or
regular expressions (if you know them) can be used.
filter(gapminder, grepl("land", country))
We can also test if rows are not equal to a value using
!=
filter(gapminder, continent != "Europe")
testing more than one condition
There are a couple of ways of testing for more than one pattern. The
first uses an or |
statement. i.e. testing if the
value of country
is Zambia
or the
value is Zimbabwe
. Remember to use double =
sign to test for string equality; ==
.
filter(gapminder, country == "Zambia" | country == "Zimbabwe")
The %in%
function is a convenient function for testing
which items in a vector correspond to a defined set of values.
filter(gapminder, country %in% c("Zambia", "Zimbabwe"))
We can require that both tests are TRUE
, e.g. which
years in Zambia had a life expectancy less than 40, by separating
conditional statements by a ,
. This performs an
AND test so only rows that meet both conditions are
returned.
filter(gapminder, country == "Zambia", lifeExp < 40)
Exercise
- Create a subset of the data where the population less than a million
in the year 2002
- Create a subset of the data where the life expectancy is greater
than 75 in the years prior to 1987
- Create a subset of the European data where the life expectancy is
between 75 and 80 in the years 2002 or 2007.
- If you are finished with these, try to explore alternative ways of
performing the same filtering
Manipulating the values in a column / creating new columns
As well as selecting existing columns in the data frame, new columns
can be created and existing ones manipulated using the
mutate
function. Typically a function or mathematical
expression is applied to data in existing columns by row, and the result
either stored in a new column or reassigned to an existing one. In other
words, the number of values returned by the function must be the same as
the number of input values. Multiple mutations can be performed in one
line of code.
Here, we create a new column of population in millions
(PopInMillions
) and round lifeExp
to the
nearest integer.
mutate(gapminder, PopInMillions = pop / 1e6,
lifeExp = round(lifeExp))
Note that we haven’t actually changed our gapminder
data
frame. If we wanted to make the new columns permanent, we would have to
create a new variable.
If we want to rename existing columns, and not create any extra
columns, we can use the rename
function.
rename(gapminder, GDP=gdpPercap)
Ordering / sorting
The whole data frame can be re-ordered according to the values in one
column using the arrange
function. So to order the table
according to population size:-
arrange(gapminder, pop)
The default is smallest --> largest
but we can change
this using the desc
function
arrange(gapminder, desc(pop))
arrange
also works on character vectors, arrange them
alpha-numerically.
arrange(gapminder, desc(country))
We can even order by more than one condition
arrange(gapminder, year, pop)
arrange(gapminder, year, continent, pop)
Saving data frames
A final point on data frames is that we can write them to disk once
we have done our data processing.
Let’s create a folder in which to store such processed, analysis
ready data
dir.create("out_data",showWarnings = FALSE)
## showWarnings will stop a message from appearing if the directory already exists
byWealth <- arrange(gapminder, desc(gdpPercap))
# check the output before writing
head(byWealth)
write_csv(byWealth, file = "out_data/by_wealth.csv")
We will now try an exercise that involves using several steps of
these operations
Exercise
- Filter the data to include just observations from the year 2002
- Re-arrange the table so that the countries from each continent are
ordered according to decreasing wealth. i.e. the wealthiest countries
first
- Select all the columns apart from year
- Write the data frame out to a file in
out_data/
folder
“Piping”
As have have just seen, we will often need to perform an analysis, or
clean a dataset, using several dplyr
functions in sequence.
e.g. filtering, mutating, then selecting columns of interest (possibly
followed by plotting - see shortly).
As a small example; if we wanted to filter our results to just Europe
the continent
column becomes redundant so we might as well
remove it.
The following is perfectly valid R code, but invites the user to make
mistakes and copy-and-paste erros when writing it. We also have to
create multiple copies of the same data frame.
tmp <- filter(gapminder, continent == "Europe")
tmp2 <- select(tmp, -continent)
tmp2
(Those familiar with Unix may recall that commands can be joined with
a pipe; |
)
In R, dplyr
commands to be linked together and form a
workflow. The symbol %>%
is pronounced
then. With a %>%
the input to a
function is assumed to be the output of the previous line. All the
dplyr
functions that we have seen so far take a data frame
as an input and return an altered data frame as an output, so are
amenable to this type of programming.
The example we gave of filtering just the European countries and
removing the continent
column becomes:-
filter(gapminder, continent=="Europe") %>%
select(-continent)
Exercise
- Re-write your solution to the previous exercise, but using the
%>%
symbol
Plotting
The R language has extensive graphical capabilities.
Graphics in R may be created by many different methods including base
graphics and more advanced plotting packages such as lattice.
The ggplot2
package was created by Hadley Wickham and
provides a intuitive plotting system to rapidly generate publication
quality graphics.
ggplot2
builds on the concept of the “Grammar of
Graphics” (Wilkinson 2005, Bertin 1983) which describes a consistent
syntax for the construction of a wide range of complex graphics by a
concise description of their components.
Why use ggplot2?
The structured syntax and high level of abstraction used by ggplot2
should allow for the user to concentrate on the visualisations instead
of creating the underlying code.
On top of this central philosophy ggplot2 has:
- Increased flexibility over many plotting systems.
- An advanced theme system for professional/publication level
graphics.
- Large developer base – Many libraries extending its
flexibility.
- Large user base – Great documentation and active mailing list.
With some practice, ggplot2
makes it easier to go from
the figure you are imagining in our head (or on paper) to a
publication-ready image in R.
Basic plot types
A plot in ggplot2
is created with the following type of
command
ggplot(data = <DATA>, mapping = aes(<MAPPINGS>)) + <GEOM_FUNCTION>()
So we need to specify
- The data to be used in graph
- Mappings of data to the graph (aesthetic mapping)
- What type of graph we want to use (The geom to use).
Lets say that we want to explore the relationship between GDP and
Life Expectancy. We might start with the hypothesis that richer
countries have higher life expectancy. A sensible choice of plot would
be a scatter plot with gdp on the x-axis and life expectancy on
the y-axis.
The first stage is to specify our dataset
library(ggplot2)
ggplot(data = gapminder)
For the aesthetics, as a bare minimum we will map the
gdpPercap
and lifeExp
to the x- and y-axis of
the plot. Some progress is made; we at least get axes
ggplot(data = gapminder,aes(x=gdpPercap, y=lifeExp))
That created the axes, but we still need to define how to display our
points on the plot. As we have continuous data for both the x- and
y-axis, geom_point
is a good choice.
ggplot(data = gapminder,aes(x=gdpPercap, y=lifeExp)) + geom_point()
The geom we use will depend on what kind of data we have
(continuous, categorical etc)
geom_point()
- Scatter plots
geom_line()
- Line plots
geom_smooth()
- Fitted line plots
geom_bar()
- Bar plots
geom_boxplot()
- Boxplots
geom_jitter()
- Jitter to plots
geom_histogram()
- Histogram plots
geom_density()
- Density plots
geom_text()
- Text to plots
geom_errorbar()
- Errorbars to plots
geom_violin()
- Violin plots
geom_tile()
- for “heatmap”-like plots
Boxplots are commonly used to visualise the distributions of
continuous data. We have to use a categorical variable on the x-axis
such as continent
or country
(not advisable in
this case as there are too many different values).
The order of the boxes along the x-axis is dictated by the order of
categories in the factor; with the default for names being alphabetical
order.
ggplot(gapminder, aes(x = continent, y=gdpPercap)) + geom_boxplot()
ggplot(gapminder, aes(x = gdpPercap)) + geom_histogram()
Producing a barplot of counts only requires an x
variable. The counts will be generated by R.
ggplot(gapminder, aes(x=continent)) + geom_bar()
The height of the bars can also be mapped directly to numeric
variables in the data frame if the geom_col
function is
used instead.
In the below plot the axis labels will be messy and difficult to
read. This is something that can be customised with some of the
ggplot2
options we will explore later.
gapminder2002 <- filter(gapminder, year==2002,continent=="Americas")
ggplot(gapminder2002, aes(x=country,y=gdpPercap)) + geom_col()
Where appropriate, we can add multiple layers of geom
s
to the plot. For instance, a criticism of the boxplot is that it does
not show all the data. We can rectify this by overlaying the individual
points.
ggplot(gapminder, aes(x = continent, y=gdpPercap)) + geom_boxplot() + geom_point()
ggplot(gapminder, aes(x = continent, y=gdpPercap)) + geom_boxplot() + geom_jitter(width=0.1)
Exercises
- The violin plot is a popular alternative to the boxplot. Create a
violin plot with
geom_violin
to visualise the differences
in GDP between different continents.
- Create a subset of the
gapminder
data frame containing
just the rows for your country of birth
- Has there been an increase in life expectancy over time?
- visualise the trend using a scatter plot (
geom_point
),
line graph (geom_line
) or smoothed line
(geom_smooth
).
- What happens when you modify the
geom_boxplot
example
to compare the gdp distributions for different years?
- Look at the message
ggplot2
prints above the plot and
try to modify the code to give a separate boxplot for each year
As we have seen already, ggplot
offers an interface to
create many popular plot types. It is up to the user to decide what the
best way to visualise the data.
Customising the plot appearance
Our plots are a bit dreary at the moment, but one way to add colour
is to add a col
argument to the geom_point
function. The value can be any of the pre-defined colour names in R.
These are displayed in this handy
online reference. Red, Green, Blue of
Hex values can also be given.
ggplot(gapminder, aes(x = gdpPercap, y=lifeExp)) + geom_point(col="red")
# Use the Hex codes from Farrow and Ball: https://convertingcolors.com/list/farrow-ball.html
# (cook's blue)
ggplot(gapminder, aes(x = gdpPercap, y=lifeExp)) + geom_point(col="#6A90B4")
However, a powerful feature of ggplot2
is that colours
are treated as aesthetics of the plot. In other words we can use a
column in our dataset.
Let’s say that we want points on our plot to be coloured according to
continent. We add an extra argument to the definition of aesthetics to
define the mapping. ggplot2
will even decide on colours and
create a legend for us.
ggplot(gapminder, aes(x = gdpPercap, y=lifeExp,col=continent)) + geom_point()
It will even choose a continuous or discrete colour scale based on
the data type. We have already seen that ggplot2
is treat
our year
column as numerical data; which is probably not
very useful for visualisation.
ggplot(gapminder, aes(x = gdpPercap, y=lifeExp,col=year)) + geom_point()
We can force ggplot2
to treat year
as
categorical data by using as.factor
when creating the
aesthetics.
ggplot(gapminder, aes(x = gdpPercap, y=lifeExp,col=as.factor(year))) + geom_point()
When used in the construction of a boxplot, the col
argument will change the colour of the lines. To change the colour of
the boxes we have to use fill
.
ggplot(gapminder, aes(x = continent, y=gdpPercap,fill=continent)) + geom_boxplot()
Exercises before the next session
- Using the
filter
function, find all countries that
start with the letter Z
- Hint: You can find the first letter of each country using the
substr
function. The mutate
function can then
be used to add a new column to the data.
- Use
geom_tile
to create a heatmap visualising life
expectancy over time for European countries. You will need to work out
what aes
thetics to specify for a geom_tile
plot
An example plot is shown on the compiled notes.
Appendix
Adding text to a plot
Annotations can be added to a plot using the flexible
annotate
function documented here.
This presumes that you know the coordinates that you want to add the
annotations at.
p<- ggplot(gapminder, aes(x = gdpPercap, y = lifeExp,col=continent)) + geom_point()
p + annotate("text", x = 90000,y=60, label="Some text")
Highlighting particular points of interest using a rectangle.
p + annotate("rect", xmin=25000, xmax=120000,ymin=50,ymax=75,alpha=0.2)
We can also map directly from a column in our dataset to the
label
aesthetic. However, this will label all the points
which is rather cluttered in our case
ggplot(gapminder, aes(x = gdpPercap, y = lifeExp,col=continent,label=country)) + geom_point() + geom_text()
Instead, we could use a different dataset when we create the text
labels with geom_text
. Here we filter the
gapminder
dataset to only countries with
gdpPercap
greater than 57000
and only these
points get labeled. We can also set the text colours to a particular
value rather than using the original colour mappings for the plot (based
on continent).
p + geom_text(data = filter(gapminder, gdpPercap > 57000),
aes(x = gdpPercap, y = lifeExp,label=country),col="black")
p + geom_text(data = filter(gapminder, gdpPercap > 25000, lifeExp < 75),
aes(x = gdpPercap, y = lifeExp,label=country),col="black",size=3) + annotate("rect", xmin=25000, xmax=120000,ymin=50,ymax=75,alpha=0.2)
LS0tCnRpdGxlOiAiUiBDcmFzaCBDb3Vyc2UiCmF1dGhvcjogIk1hcmsgRHVubmluZyIKZGF0ZTogJ2ByIGZvcm1hdChTeXMudGltZSgpLCAiTGFzdCBtb2RpZmllZDogJWQgJWIgJVkiKWAnCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNzczogc3R5bGVzaGVldHMvc3R5bGVzLmNzcwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCiMgSW50cm9kdWN0aW9uIHRvIFIgLSBQYXJ0IElJCgo8aW1nIHNyYz0iaW1hZ2VzL2xvZ28tc20ucG5nIiBzdHlsZT0icG9zaXRpb246YWJzb2x1dGU7dG9wOjQwcHg7cmlnaHQ6MTBweDsiIHdpZHRoPSIyMDAiIC8+Cgo8ZGl2IGNsYXNzPSJ3YXJuaW5nIj4KSWYgeW91IGhhdmUgcHJldmlvdXNseS1hdHRlbmRlZCB0aGlzIGNvdXJzZSBhbmQgYXJlIHJldmlld2luZyB0aGUgbm90ZXMsIHBsZWFzZSBiZSBhd2FyZSBvZiBzb21lIHJlLW9yZ2FuaXNhdGlvbiBvZiB0aGUgbWF0ZXJpYWxzIGluIHRoaXMgc2VjdGlvbi4gU29tZSBvZiB0aGUgbGF0ZXIgZXhhbXBsZXMgaGF2ZSBiZWVuIG1vdmVkIHRvIFBhcnQgMy4KPC9kaXY+CgoKV2Ugc2hvdWxkIGhhdmUgbG9hZGVkIHRoZSBgcmVhZHJgIGxpYnJhcnkgYW5kIGltcG9ydGVkIGFuIGV4YW1wbGUgZGF0YXNldCBpbnRvIFIKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkocmVhZHIpCmdhcG1pbmRlciA8LSByZWFkX2NzdigicmF3X2RhdGEvZ2FwbWluZGVyLmNzdiIpCmBgYAoKCiMjIE1hbmlwdWxhdGluZyBDb2x1bW5zCgpXZSBhcmUgZ29pbmcgdG8gdXNlIGZ1bmN0aW9ucyBmcm9tIHRoZSAqKmBkcGx5cmAqKiBwYWNrYWdlIHRvICoqbWFuaXB1bGF0ZSB0aGUgZGF0YSBmcmFtZSoqIHdlIGhhdmUganVzdCBjcmVhdGVkLiBJdCBpcyBwZXJmZWN0bHkgcG9zc2libGUgdG8gd29yayB3aXRoIGRhdGEgZnJhbWVzIHVzaW5nIHRoZSBmdW5jdGlvbnMgcHJvdmlkZWQgYXMgcGFydCBvZiAiKmJhc2UgUioiLiBIb3dldmVyLCBtYW55IGZpbmQgaXQgZWFzeSB0byByZWFkIGFuZCB3cml0ZSBjb2RlIHVzaW5nIGBkcGx5cmAuCgpUaGVyZSBhcmUgKiptYW55IG1vcmUgZnVuY3Rpb25zIGF2YWlsYWJsZSBpbiBgZHBseXJgKiogdGhhbiB3ZSB3aWxsIGNvdmVyIHRvZGF5LiBBbiBvdmVydmlldyBvZiBhbGwgZnVuY3Rpb25zIGlzIGdpdmVuIGluIGEgY2hlYXRzaGVldC4KCjxkaXYgY2xhc3M9ImluZm9ybWF0aW9uIj4KCi0gW2RwbHlyIGNoZWF0c2hlZXRdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE1LzAyL2RhdGEtd3JhbmdsaW5nLWNoZWF0c2hlZXQucGRmKS4gVGhlIGNoZWF0c2hlZXQgaXMgYWxzbyBhdmFpbGFibGUgdGhyb3VnaCB0aGUgUlN0dWRpbyBIZWxwIG1lbnUuCgo8L2Rpdj4KCkJlZm9yZSB1c2luZyBhbnkgb2YgdGhlc2UgZnVuY3Rpb25zLCB3ZSBuZWVkIHRvIGxvYWQgdGhlIGxpYnJhcnk6LSAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZHBseXIpCmBgYAoKCiMjIyBgc2VsZWN0YGluZyBjb2x1bW5zCgoKV2UgY2FuICoqYWNjZXNzIHRoZSBjb2x1bW5zKiogb2YgYSBkYXRhIGZyYW1lIHVzaW5nIHRoZSBgc2VsZWN0YCBmdW5jdGlvbi4gCgojIyMjIGJ5IG5hbWUKCkZpcnN0bHksIHdlIGNhbiBzZWxlY3QgY29sdW1uIGJ5IG5hbWUsIGJ5IGFkZGluZyBiYXJlIGNvbHVtbiBuYW1lcyAoaS5lLiBub3QgcmVxdWlyaW5nIHF1b3RlIG1hcmtzIGFyb3VuZCB0aGUgbmFtZSkgYWZ0ZXIgdGhlIG5hbWUgb2YgdGhlIGRhdGEgZnJhbWUsIHNlcGFyYXRlZCBieSBhIGAsYCAuIAoKYGBge3J9CnNlbGVjdChnYXBtaW5kZXIsIGNvdW50cnksIGNvbnRpbmVudCkKYGBgCgo8ZGl2IGNsYXNzPSJ3YXJuaW5nIj4KQXMgd2UgaGF2ZSB0byB0eXBlIHRoZSBjb2x1bW4gbmFtZXMgbWFudWFsbHkgKG5vIGF1dG8tY29tcGxldGUhKSwgd2UgaGF2ZSB0byBtYWtlIHN1cmUgd2UgdHlwZSB0aGUgbmFtZSBleGFjdGx5IGFzIGl0IGFwcGVhcnMgaW4gdGhlIGRhdGEuIElmIGBzZWxlY3RgIHNlZXMgYSBuYW1lIHRoYXQgZG9lc24ndCBleGlzdCBpbiB0aGUgZGF0YSBmcmFtZSBpdCBzaG91bGQgZ2l2ZSBhbiBpbmZvcm1hdGl2ZSBtZXNzYWdlCmBFcnJvcjogQ2FuJ3Qgc3Vic2V0IGNvbHVtbnMgdGhhdCBkb24ndCBleGlzdC5gCjwvZGl2PgoKV2UgY2FuIGFsc28gb21pdCBjb2x1bW5zIGZyb20gdGhlIG91cHV0IGJ5IHB1dHRpbmcgYSBtaW51cyAoYC1gKSBpbiBmcm9udCBvZiB0aGUgY29sdW1uIG5hbWUuIE5vdGUgdGhhdCB0aGlzIGlzIG5vdCB0aGUgc2FtZSBhcyByZW1vdmluZyB0aGUgY29sdW1uIGZyb20gdGhlIGRhdGEgcGVybWFuZW50bHkuCgpgYGB7cn0Kc2VsZWN0KGdhcG1pbmRlciwgLWNvdW50cnkpCmBgYAoKIyMjIyByYW5nZSBvZiBjb2x1bW5zCgpBIHJhbmdlIG9mIGNvbHVtbnMgY2FuIGJlIHNlbGVjdGVkIGJ5IHRoZSBgOmAgb3BlcmF0b3IuCgpgYGB7cn0Kc2VsZWN0KGdhcG1pbmRlciwgbGlmZUV4cDpnZHBQZXJjYXApCmBgYAoKIyMjIyBoZWxwZXIgZnVuY3Rpb25zIAoKVGhlcmUgYXJlIGEgbnVtYmVyIG9mIGhlbHBlciBmdW5jdGlvbnMgY2FuIGJlIGVtcGxveWVkIGlmIHdlIGFyZSB1bnN1cmUgYWJvdXQgdGhlIGV4YWN0IG5hbWUgb2YgdGhlIGNvbHVtbi4KCmBgYHtyfQpzZWxlY3QoZ2FwbWluZGVyLCBzdGFydHNfd2l0aCgiY28iKSkKc2VsZWN0KGdhcG1pbmRlciwgY29udGFpbnMoImxpZmUiKSkKIyBzZWxlY3RpbmcgdGhlIGxhc3QgYW5kIHBlbnVsdGltYXRlIGNvbHVtbnMKc2VsZWN0KGdhcG1pbmRlciwgbGFzdF9jb2woMSksbGFzdF9jb2woKSkKYGBgCgpJdCBpcyBhbHNvIHBvc3NpYmxlIHRvIHVzZSB0aGUgY29sdW1uIG51bWJlciBpbiB0aGUgc2VsZWN0aW9uLgoKYGBge3J9CnNlbGVjdChnYXBtaW5kZXIsIDQ6NikKYGBgCgo8ZGl2IGNsYXNzPSJleGVyY2lzZSI+CioqUXVlc3Rpb24qKjogV2h5IG1pZ2h0IHVzaW5nIHRoZSBudW1iZXIgb2YgdGhlIGNvbHVtbiBzb21ldGltZXMgYmUgcHJvYmxlbWF0aWM/IENvbnNpZGVyIHdoYXQgbWlnaHQgaGFwcGVuIGlmIHlvdSB3cm90ZSBjb2RlIHRvIHNlbGVjdCB0aGUgbGFzdCBjb2x1bW4gb2YgZGF0YSBmcm9tIGEgZmlsZSB1c2luZyB0aGUgY29sdW1uIG51bWJlci4gV2hhdCB3b3VsZCBoYXBwZW4gaWYgdGhlIG51bWJlciBvZiBjb2x1bW5zIGluIHRoZSBmaWxlIHdhcyBub3QgY29uc3RhbnQ/CjwvZGl2PgoKVGhlIGBzZWxlY3RgIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHdpdGgganVzdCBhIHNpbmdsZSBjb2x1bW4gbmFtZSAtIGluIGEgc2ltaWxhciBtYW5uZXIgdG8gdGhlIGAkYCBvcGVyYXRpb24gd2Ugc2F3IGxhc3QgdGltZS4gSG93ZXZlciwgYHNlbGVjdGAgYWx3YXlzIHJldHVybnMgYSAqZGF0YSBmcmFtZSogd2hlcmVhcyBgJGAgZ2l2ZXMgYSB2ZWN0b3IuIENvbXBhcmUgdGhlIG91dHB1dCBvZiB0aGUgZm9sbG93aW5nIGNvZGUgY2h1bmtzCgpgYGB7cn0Kc2VsZWN0KGdhcG1pbmRlciwgcG9wKQpgYGAKCmBgYHtyIGV2YWw9RkFMU0V9CmdhcG1pbmRlciRwb3AKYGBgCgpUaGUgY29uc2VxdWVuY2Ugb2YgdGhpcyBpcyB0aGF0IHlvdSBjYW5ub3QgdXNlIGZ1bmN0aW9ucyBzdWNoIGFzIGBtZWFuYCBpbiBjb21iaW5hdGlvbiB3aXRoIGBzZWxlY3RgCgpgYGB7ciBldmFsPUZBTFNFfQpwb3BzIDwtIHNlbGVjdChnYXBtaW5kZXIsIHBvcCkKbWVhbihwb3BzKQpgYGAKCkluIHRoZSBuZXh0IHNlc3Npb24gd2Ugd2lsbCBzZWUgaG93IHRvIGNhbGN1bGF0ZSBzdW1tYXJ5IHN0YXRpc3RpY3Mgb24gcGFydGljdWxhciBjb2x1bW5zIGluIG91ciBkYXRhLiBGb3Igbm93LCBhIHVzZWZ1bCBmdW5jdGlvbiBpcyBgcHVsbGAgdGhhdCB3aWxsIHJldHVybiB0aGUgY29ycmVjdCB0eXBlIG9mIGRhdGEgcmVxdWlyZWQgZm9yIGEgZnVuY3Rpb24gc3VjaCBhcyBgbWVhbmAuCgpgYGB7cn0KcG9wcyA8LSBwdWxsKGdhcG1pbmRlcixwb3ApCm1lYW4ocG9wcykKYGBgCgoKCiMjIFJlc3RyaWN0aW5nIHJvd3Mgd2l0aCBmaWx0ZXIKClNvIGZhciB3ZSBoYXZlIGJlZW4gcmV0dXJuaW5nIGFsbCB0aGUgcm93cyBpbiB0aGUgb3V0cHV0LiBXZSBjYW4gdXNlIHdoYXQgd2UgY2FsbCBhICoqbG9naWNhbCB0ZXN0KiogdG8gKipmaWx0ZXIgdGhlIHJvd3MqKiBpbiBhIGRhdGEgZnJhbWUuIFRoaXMgbG9naWNhbCB0ZXN0IHdpbGwgYmUgYXBwbGllZCB0byBlYWNoIHJvdyBhbmQgZ2l2ZSBlaXRoZXIgYSBgVFJVRWAgb3IgYEZBTFNFYCByZXN1bHQuIFdoZW4gZmlsdGVyaW5nLCAqKm9ubHkgcm93cyB3aXRoIGEgYFRSVUVgIHJlc3VsdCBnZXQgcmV0dXJuZWQqKi4KCkZvciBleGFtcGxlIHdlIGZpbHRlciBmb3Igcm93cyB3aGVyZSB0aGUgYGxpZmVFeHBgIHZhcmlhYmxlIGlzIGxlc3MgdGhhbiA0MC4gCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgbGlmZUV4cCA8IDQwKQpgYGAKCkludGVybmFsbHksIFIgY3JlYXRlcyBhICp2ZWN0b3IqIG9mIGBUUlVFYCBvciBgRkFMU0VgOyBvbmUgZm9yIGVhY2ggcm93IGluIHRoZSBkYXRhIGZyYW1lLiBUaGlzIGlzIHRoZW4gdXNlZCB0byBkZWNpZGUgd2hpY2ggcm93cyB0byBkaXNwbGF5LgoKVGVzdGluZyBmb3IgZXF1YWxpdHkgY2FuIGJlIGRvbmUgdXNpbmcgYD09YC4gVGhpcyB3aWxsIG9ubHkgZ2l2ZSBgVFJVRWAgZm9yIGVudHJpZXMgdGhhdCBhcmUgKmV4YWN0bHkqIHRoZSBzYW1lIGFzIHRoZSB0ZXN0IHN0cmluZy4gCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgY291bnRyeSA9PSAiWmFtYmlhIikKYGBgCgpOLkIuIEZvciBwYXJ0aWFsIG1hdGNoZXMsIHRoZSBgZ3JlcGxgIGZ1bmN0aW9uIGFuZCAvIG9yICpyZWd1bGFyIGV4cHJlc3Npb25zKiAoaWYgeW91IGtub3cgdGhlbSkgY2FuIGJlIHVzZWQuCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgZ3JlcGwoImxhbmQiLCBjb3VudHJ5KSkKYGBgCgpXZSBjYW4gYWxzbyB0ZXN0IGlmIHJvd3MgYXJlICpub3QqIGVxdWFsIHRvIGEgdmFsdWUgdXNpbmcgIGAhPWAgCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgY29udGluZW50ICE9ICJFdXJvcGUiKQpgYGAKCiMjIyB0ZXN0aW5nIG1vcmUgdGhhbiBvbmUgY29uZGl0aW9uCgpUaGVyZSBhcmUgYSBjb3VwbGUgb2Ygd2F5cyBvZiB0ZXN0aW5nIGZvciBtb3JlIHRoYW4gb25lIHBhdHRlcm4uIFRoZSBmaXJzdCB1c2VzIGFuICpvciogYHxgIHN0YXRlbWVudC4gaS5lLiB0ZXN0aW5nIGlmIHRoZSB2YWx1ZSBvZiBgY291bnRyeWAgaXMgYFphbWJpYWAgKm9yKiB0aGUgdmFsdWUgaXMgYFppbWJhYndlYC4gUmVtZW1iZXIgdG8gdXNlIGRvdWJsZSBgPWAgc2lnbiB0byB0ZXN0IGZvciBzdHJpbmcgZXF1YWxpdHk7IGA9PWAuCgoKYGBge3J9CmZpbHRlcihnYXBtaW5kZXIsIGNvdW50cnkgPT0gIlphbWJpYSIgfCBjb3VudHJ5ID09ICJaaW1iYWJ3ZSIpCmBgYAoKClRoZSBgJWluJWAgZnVuY3Rpb24gaXMgYSBjb252ZW5pZW50IGZ1bmN0aW9uIGZvciB0ZXN0aW5nIHdoaWNoIGl0ZW1zIGluIGEgdmVjdG9yIGNvcnJlc3BvbmQgdG8gYSBkZWZpbmVkIHNldCBvZiB2YWx1ZXMuCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgY291bnRyeSAlaW4lIGMoIlphbWJpYSIsICJaaW1iYWJ3ZSIpKQpgYGAKCgpXZSBjYW4gcmVxdWlyZSB0aGF0IGJvdGggdGVzdHMgYXJlIGBUUlVFYCwgIGUuZy4gd2hpY2ggeWVhcnMgaW4gWmFtYmlhIGhhZCBhIGxpZmUgZXhwZWN0YW5jeSBsZXNzIHRoYW4gNDAsIGJ5IHNlcGFyYXRpbmcgY29uZGl0aW9uYWwgc3RhdGVtZW50cyBieSBhIGAsYC4gVGhpcyBwZXJmb3JtcyBhbiAqQU5EKiB0ZXN0IHNvIG9ubHkgcm93cyB0aGF0IG1lZXQgYm90aCBjb25kaXRpb25zIGFyZSByZXR1cm5lZC4KCmBgYHtyfQpmaWx0ZXIoZ2FwbWluZGVyLCBjb3VudHJ5ID09ICJaYW1iaWEiLCBsaWZlRXhwIDwgNDApCmBgYAoKPGRpdiBjbGFzcz0iaW5mb3JtYXRpb24iPgpZb3UgbWF5IGhhdmUgbm90aWNlZCB0aGF0IGBmaWx0ZXJgIHdpbGwgYWx3YXlzIG91dHB1dCB0aGUgc2FtZSBudW1iZXIgb2YgY29sdW1ucyBhcyB0aGUgaW5wdXQgZGF0YSBmcmFtZS4gYGZpbHRlcmAgbmV2ZXIgY2hhbmdlcyB0aGUgY29sdW1ucyB0aGF0IGFyZSBkaXNwbGF5ZWQuIFRoZXJlIGFyZSB3YXlzIG9mIHVzaW5nIGBmaWx0ZXJgIGluIGNvbmp1bmN0aW9uIHdpdGggYHNlbGVjdGAgYXMgd2Ugd2lsbCBzZWUgbGF0ZXIuCjwvZGl2PgoKKioqKioqCioqKioqKgoqKioqKioKCiMjIEV4ZXJjaXNlCjxkaXYgY2xhc3M9ImV4ZXJjaXNlIj4KLSBDcmVhdGUgYSBzdWJzZXQgb2YgdGhlIGRhdGEgd2hlcmUgdGhlIHBvcHVsYXRpb24gbGVzcyB0aGFuIGEgbWlsbGlvbiBpbiB0aGUgeWVhciAyMDAyCi0gQ3JlYXRlIGEgc3Vic2V0IG9mIHRoZSBkYXRhIHdoZXJlIHRoZSBsaWZlIGV4cGVjdGFuY3kgaXMgZ3JlYXRlciB0aGFuIDc1IGluIHRoZSB5ZWFycyBwcmlvciB0byAxOTg3Ci0gQ3JlYXRlIGEgc3Vic2V0IG9mIHRoZSBFdXJvcGVhbiBkYXRhIHdoZXJlIHRoZSBsaWZlIGV4cGVjdGFuY3kgaXMgYmV0d2VlbiA3NSBhbmQgODAgaW4gdGhlIHllYXJzIDIwMDIgb3IgMjAwNy4KLSBJZiB5b3UgYXJlIGZpbmlzaGVkIHdpdGggdGhlc2UsIHRyeSB0byBleHBsb3JlIGFsdGVybmF0aXZlIHdheXMgb2YgcGVyZm9ybWluZyB0aGUgc2FtZSBmaWx0ZXJpbmcKPC9kaXY+CioqKioqKgoqKioqKioKKioqKioqCgoKIyMgTWFuaXB1bGF0aW5nIHRoZSB2YWx1ZXMgaW4gYSBjb2x1bW4gLyBjcmVhdGluZyBuZXcgY29sdW1ucwoKQXMgd2VsbCBhcyBzZWxlY3RpbmcgZXhpc3RpbmcgY29sdW1ucyBpbiB0aGUgZGF0YSBmcmFtZSwgbmV3IGNvbHVtbnMgY2FuIGJlIGNyZWF0ZWQgYW5kIGV4aXN0aW5nIG9uZXMgbWFuaXB1bGF0ZWQgdXNpbmcgdGhlIGBtdXRhdGVgIGZ1bmN0aW9uLiBUeXBpY2FsbHkgYSBmdW5jdGlvbiBvciBtYXRoZW1hdGljYWwgZXhwcmVzc2lvbiBpcyBhcHBsaWVkIHRvIGRhdGEgaW4gZXhpc3RpbmcgY29sdW1ucyBieSByb3csIGFuZCB0aGUgcmVzdWx0IGVpdGhlciBzdG9yZWQgaW4gYSBuZXcgY29sdW1uIG9yIHJlYXNzaWduZWQgdG8gYW4gZXhpc3Rpbmcgb25lLiBJbiBvdGhlciB3b3JkcywgdGhlIG51bWJlciBvZiB2YWx1ZXMgcmV0dXJuZWQgYnkgdGhlIGZ1bmN0aW9uIG11c3QgYmUgdGhlIHNhbWUgYXMgdGhlIG51bWJlciBvZiBpbnB1dCB2YWx1ZXMuIE11bHRpcGxlIG11dGF0aW9ucyBjYW4gYmUgcGVyZm9ybWVkIGluIG9uZSBsaW5lIG9mIGNvZGUuCgpIZXJlLCB3ZSBjcmVhdGUgYSBuZXcgY29sdW1uIG9mIHBvcHVsYXRpb24gaW4gbWlsbGlvbnMgKGBQb3BJbk1pbGxpb25zYCkgYW5kIHJvdW5kIGBsaWZlRXhwYCB0byB0aGUgbmVhcmVzdCBpbnRlZ2VyLgoKYGBge3J9Cm11dGF0ZShnYXBtaW5kZXIsIFBvcEluTWlsbGlvbnMgPSBwb3AgLyAxZTYsCiAgICAgICBsaWZlRXhwID0gcm91bmQobGlmZUV4cCkpCmBgYApOb3RlIHRoYXQgd2UgaGF2ZW4ndCBhY3R1YWxseSBjaGFuZ2VkIG91ciBgZ2FwbWluZGVyYCBkYXRhIGZyYW1lLiBJZiB3ZSB3YW50ZWQgdG8gbWFrZSB0aGUgbmV3IGNvbHVtbnMgcGVybWFuZW50LCB3ZSB3b3VsZCBoYXZlIHRvIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZS4KCklmIHdlIHdhbnQgdG8gcmVuYW1lIGV4aXN0aW5nIGNvbHVtbnMsIGFuZCBub3QgY3JlYXRlIGFueSBleHRyYSBjb2x1bW5zLCB3ZSBjYW4gdXNlIHRoZSBgcmVuYW1lYCBmdW5jdGlvbi4KCmBgYHtyfQpyZW5hbWUoZ2FwbWluZGVyLCBHRFA9Z2RwUGVyY2FwKQpgYGAKCgojIyBPcmRlcmluZyAvIHNvcnRpbmcKClRoZSB3aG9sZSBkYXRhIGZyYW1lIGNhbiBiZSByZS1vcmRlcmVkIGFjY29yZGluZyB0byB0aGUgdmFsdWVzIGluIG9uZSBjb2x1bW4gdXNpbmcgdGhlIGBhcnJhbmdlYCBmdW5jdGlvbi4gU28gdG8gb3JkZXIgdGhlIHRhYmxlIGFjY29yZGluZyB0byBwb3B1bGF0aW9uIHNpemU6LQoKYGBge3J9CmFycmFuZ2UoZ2FwbWluZGVyLCBwb3ApCmBgYAoKClRoZSBkZWZhdWx0IGlzIGBzbWFsbGVzdCAtLT4gbGFyZ2VzdGAgYnV0IHdlIGNhbiBjaGFuZ2UgdGhpcyB1c2luZyB0aGUgYGRlc2NgIGZ1bmN0aW9uCgpgYGB7cn0KYXJyYW5nZShnYXBtaW5kZXIsIGRlc2MocG9wKSkKYGBgCgpgYXJyYW5nZWAgYWxzbyB3b3JrcyBvbiBjaGFyYWN0ZXIgdmVjdG9ycywgYXJyYW5nZSB0aGVtIGFscGhhLW51bWVyaWNhbGx5LgoKYGBge3J9CmFycmFuZ2UoZ2FwbWluZGVyLCBkZXNjKGNvdW50cnkpKQpgYGAKCldlIGNhbiBldmVuIG9yZGVyIGJ5IG1vcmUgdGhhbiBvbmUgY29uZGl0aW9uCgpgYGB7cn0KYXJyYW5nZShnYXBtaW5kZXIsIHllYXIsIHBvcCkKYGBgCgoKYGBge3J9CmFycmFuZ2UoZ2FwbWluZGVyLCB5ZWFyLCBjb250aW5lbnQsIHBvcCkKYGBgCgojIyBTYXZpbmcgZGF0YSBmcmFtZXMKCkEgZmluYWwgcG9pbnQgb24gZGF0YSBmcmFtZXMgaXMgdGhhdCB3ZSBjYW4gd3JpdGUgdGhlbSB0byBkaXNrIG9uY2Ugd2UgaGF2ZSBkb25lIG91ciBkYXRhIHByb2Nlc3NpbmcuIAoKTGV0J3MgY3JlYXRlIGEgZm9sZGVyIGluIHdoaWNoIHRvIHN0b3JlIHN1Y2ggcHJvY2Vzc2VkLCBhbmFseXNpcyByZWFkeSBkYXRhCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZGlyLmNyZWF0ZSgib3V0X2RhdGEiLHNob3dXYXJuaW5ncyA9IEZBTFNFKQojIyBzaG93V2FybmluZ3Mgd2lsbCBzdG9wIGEgbWVzc2FnZSBmcm9tIGFwcGVhcmluZyBpZiB0aGUgZGlyZWN0b3J5IGFscmVhZHkgZXhpc3RzCmBgYAoKCmBgYHtyfQpieVdlYWx0aCA8LSBhcnJhbmdlKGdhcG1pbmRlciwgZGVzYyhnZHBQZXJjYXApKQojIGNoZWNrIHRoZSBvdXRwdXQgYmVmb3JlIHdyaXRpbmcKaGVhZChieVdlYWx0aCkKd3JpdGVfY3N2KGJ5V2VhbHRoLCBmaWxlID0gIm91dF9kYXRhL2J5X3dlYWx0aC5jc3YiKQpgYGAKCldlIHdpbGwgbm93IHRyeSBhbiBleGVyY2lzZSB0aGF0IGludm9sdmVzIHVzaW5nIHNldmVyYWwgc3RlcHMgb2YgdGhlc2Ugb3BlcmF0aW9ucwoKKioqKioqCioqKioqKgoqKioqKioKCiMjIEV4ZXJjaXNlCjxkaXYgY2xhc3M9ImV4ZXJjaXNlIj4KLSBGaWx0ZXIgdGhlIGRhdGEgdG8gaW5jbHVkZSBqdXN0IG9ic2VydmF0aW9ucyBmcm9tIHRoZSB5ZWFyIDIwMDIKLSBSZS1hcnJhbmdlIHRoZSB0YWJsZSBzbyB0aGF0IHRoZSBjb3VudHJpZXMgZnJvbSBlYWNoIGNvbnRpbmVudCBhcmUgb3JkZXJlZCBhY2NvcmRpbmcgdG8gZGVjcmVhc2luZyB3ZWFsdGguIGkuZS4gdGhlIHdlYWx0aGllc3QgY291bnRyaWVzIGZpcnN0Ci0gU2VsZWN0IGFsbCB0aGUgY29sdW1ucyBhcGFydCBmcm9tIHllYXIgCi0gV3JpdGUgdGhlIGRhdGEgZnJhbWUgb3V0IHRvIGEgZmlsZSBpbiBgb3V0X2RhdGEvYCBmb2xkZXIKPC9kaXY+CmBgYHtyIGVjaG89RkFMU0V9CgoKCmBgYAoKCioqKioqKgoqKioqKioKKioqKioqCgoKIyMgIlBpcGluZyIKCkFzIGhhdmUgaGF2ZSBqdXN0IHNlZW4sIHdlIHdpbGwgb2Z0ZW4gbmVlZCB0byBwZXJmb3JtIGFuIGFuYWx5c2lzLCBvciBjbGVhbiBhIGRhdGFzZXQsIHVzaW5nIHNldmVyYWwgYGRwbHlyYCBmdW5jdGlvbnMgaW4gc2VxdWVuY2UuIGUuZy4gZmlsdGVyaW5nLCBtdXRhdGluZywgdGhlbiBzZWxlY3RpbmcgY29sdW1ucyBvZiBpbnRlcmVzdCAocG9zc2libHkgZm9sbG93ZWQgYnkgcGxvdHRpbmcgLSBzZWUgc2hvcnRseSkuCgpBcyBhIHNtYWxsIGV4YW1wbGU7IGlmIHdlIHdhbnRlZCB0byBmaWx0ZXIgb3VyIHJlc3VsdHMgdG8ganVzdCBFdXJvcGUgdGhlIGBjb250aW5lbnRgIGNvbHVtbiBiZWNvbWVzIHJlZHVuZGFudCBzbyB3ZSBtaWdodCBhcyB3ZWxsIHJlbW92ZSBpdC4KClRoZSBmb2xsb3dpbmcgaXMgcGVyZmVjdGx5IHZhbGlkIFIgY29kZSwgYnV0IGludml0ZXMgdGhlIHVzZXIgdG8gbWFrZSBtaXN0YWtlcyBhbmQgY29weS1hbmQtcGFzdGUgZXJyb3Mgd2hlbiB3cml0aW5nIGl0LiBXZSBhbHNvIGhhdmUgdG8gY3JlYXRlIG11bHRpcGxlIGNvcGllcyBvZiB0aGUgc2FtZSBkYXRhIGZyYW1lLgoKYGBge3J9CnRtcCA8LSBmaWx0ZXIoZ2FwbWluZGVyLCBjb250aW5lbnQgPT0gIkV1cm9wZSIpCnRtcDIgPC0gc2VsZWN0KHRtcCwgLWNvbnRpbmVudCkKdG1wMgpgYGAKCihUaG9zZSBmYW1pbGlhciB3aXRoIFVuaXggbWF5IHJlY2FsbCB0aGF0IGNvbW1hbmRzIGNhbiBiZSBqb2luZWQgd2l0aCBhIHBpcGU7IGB8YCkKCkluIFIsIGBkcGx5cmAgY29tbWFuZHMgdG8gYmUgbGlua2VkIHRvZ2V0aGVyIGFuZCBmb3JtIGEgd29ya2Zsb3cuIFRoZSBzeW1ib2wgYCU+JWAgaXMgcHJvbm91bmNlZCAqKnRoZW4qKi4gV2l0aCBhIGAlPiUgYCB0aGUgaW5wdXQgdG8gYSBmdW5jdGlvbiBpcyBhc3N1bWVkIHRvIGJlIHRoZSBvdXRwdXQgb2YgdGhlIHByZXZpb3VzIGxpbmUuIEFsbCB0aGUgYGRwbHlyYCBmdW5jdGlvbnMgdGhhdCB3ZSBoYXZlIHNlZW4gc28gZmFyIHRha2UgYSBkYXRhIGZyYW1lIGFzIGFuIGlucHV0IGFuZCByZXR1cm4gYW4gYWx0ZXJlZCBkYXRhIGZyYW1lIGFzIGFuIG91dHB1dCwgc28gYXJlIGFtZW5hYmxlIHRvIHRoaXMgdHlwZSBvZiBwcm9ncmFtbWluZy4KClRoZSBleGFtcGxlIHdlIGdhdmUgb2YgZmlsdGVyaW5nIGp1c3QgdGhlIEV1cm9wZWFuIGNvdW50cmllcyBhbmQgcmVtb3ZpbmcgdGhlIGBjb250aW5lbnRgIGNvbHVtbiBiZWNvbWVzOi0KCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgY29udGluZW50PT0iRXVyb3BlIikgJT4lIAogIHNlbGVjdCgtY29udGluZW50KQpgYGAKCgoqKioqKioKKioqKioqCioqKioqKgoKIyMjIyBFeGVyY2lzZQoKPGRpdiBjbGFzcz0iZXhlcmNpc2UiPgotIFJlLXdyaXRlIHlvdXIgc29sdXRpb24gdG8gdGhlIHByZXZpb3VzIGV4ZXJjaXNlLCBidXQgdXNpbmcgdGhlIGAgJT4lIGAgc3ltYm9sCgo8L2Rpdj4KCmBgYHtyfQoKCmBgYAoKCioqKioqKgoqKioqKioKKioqKioqCgojIFBsb3R0aW5nCgpUaGUgUiBsYW5ndWFnZSBoYXMgZXh0ZW5zaXZlIGdyYXBoaWNhbCBjYXBhYmlsaXRpZXMuCgpHcmFwaGljcyBpbiBSIG1heSBiZSBjcmVhdGVkIGJ5IG1hbnkgZGlmZmVyZW50IG1ldGhvZHMgaW5jbHVkaW5nIGJhc2UgZ3JhcGhpY3MgYW5kIG1vcmUgYWR2YW5jZWQgcGxvdHRpbmcgcGFja2FnZXMgc3VjaCBhcyBsYXR0aWNlLgoKVGhlIGBnZ3Bsb3QyYCBwYWNrYWdlIHdhcyBjcmVhdGVkIGJ5IEhhZGxleSBXaWNraGFtIGFuZCBwcm92aWRlcyBhIGludHVpdGl2ZSBwbG90dGluZyBzeXN0ZW0gdG8gcmFwaWRseSBnZW5lcmF0ZSBwdWJsaWNhdGlvbiBxdWFsaXR5IGdyYXBoaWNzLgoKYGdncGxvdDJgIGJ1aWxkcyBvbiB0aGUgY29uY2VwdCBvZiB0aGUg4oCcR3JhbW1hciBvZiBHcmFwaGljc+KAnSAoV2lsa2luc29uIDIwMDUsIEJlcnRpbiAxOTgzKSB3aGljaCBkZXNjcmliZXMgYSBjb25zaXN0ZW50IHN5bnRheCBmb3IgdGhlIGNvbnN0cnVjdGlvbiBvZiBhIHdpZGUgcmFuZ2Ugb2YgY29tcGxleCBncmFwaGljcyBieSBhIGNvbmNpc2UgZGVzY3JpcHRpb24gb2YgdGhlaXIgY29tcG9uZW50cy4KCiMjIFdoeSB1c2UgZ2dwbG90Mj8KClRoZSBzdHJ1Y3R1cmVkIHN5bnRheCBhbmQgaGlnaCBsZXZlbCBvZiBhYnN0cmFjdGlvbiB1c2VkIGJ5IGdncGxvdDIgc2hvdWxkIGFsbG93IGZvciB0aGUgdXNlciB0byBjb25jZW50cmF0ZSBvbiB0aGUgdmlzdWFsaXNhdGlvbnMgaW5zdGVhZCBvZiBjcmVhdGluZyB0aGUgdW5kZXJseWluZyBjb2RlLgoKT24gdG9wIG9mIHRoaXMgY2VudHJhbCBwaGlsb3NvcGh5IGdncGxvdDIgaGFzOgoKLSBJbmNyZWFzZWQgZmxleGliaWxpdHkgb3ZlciBtYW55IHBsb3R0aW5nIHN5c3RlbXMuCi0gQW4gYWR2YW5jZWQgdGhlbWUgc3lzdGVtIGZvciBwcm9mZXNzaW9uYWwvcHVibGljYXRpb24gbGV2ZWwgZ3JhcGhpY3MuCi0gTGFyZ2UgZGV2ZWxvcGVyIGJhc2Ug4oCTIE1hbnkgbGlicmFyaWVzIGV4dGVuZGluZyBpdHMgZmxleGliaWxpdHkuCi0gTGFyZ2UgdXNlciBiYXNlIOKAkyBHcmVhdCBkb2N1bWVudGF0aW9uIGFuZCBhY3RpdmUgbWFpbGluZyBsaXN0LgoKPGRpdiBjbGFzcz0iaW5mb3JtYXRpb24iPgoKSXQgaXMgYWx3YXlzIHVzZWZ1bCB0byB0aGluayBhYm91dCB0aGUgbWVzc2FnZSB5b3Ugd2FudCB0byBjb252ZXkgYW5kIHRoZSBhcHByb3ByaWF0ZSBwbG90IGJlZm9yZSB3cml0aW5nIGFueSBSIGNvZGUuIFJlc291cmNlcyBsaWtlIFtkYXRhLXRvLXZpei5jb21dKGh0dHBzOi8vd3d3LmRhdGEtdG8tdml6LmNvbS8pIHNob3VsZCBoZWxwLgo8L2Rpdj4KCldpdGggc29tZSBwcmFjdGljZSwgYGdncGxvdDJgIG1ha2VzIGl0IGVhc2llciB0byBnbyBmcm9tIHRoZSBmaWd1cmUgeW91IGFyZSBpbWFnaW5pbmcgaW4gb3VyIGhlYWQgKG9yIG9uIHBhcGVyKSB0byBhIHB1YmxpY2F0aW9uLXJlYWR5IGltYWdlIGluIFIuCgo8ZGl2IGNsYXNzPSJpbmZvcm1hdGlvbiI+CkFzIHdpdGggYGRwbHlyYCwgd2Ugd29uJ3QgaGF2ZSB0aW1lIHRvIGNvdmVyIGFsbCBkZXRhaWxzIG9mIGBnZ3Bsb3QyYC4gVGhpcyBpcyBob3dldmVyIGEgdXNlZnVsIFtjaGVhdHNoZWV0XShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcnN0dWRpby9jaGVhdHNoZWV0cy9tYWluL2RhdGEtdmlzdWFsaXphdGlvbi5wZGYpIHRoYXQgY2FuIGJlIHByaW50ZWQgYXMgYSByZWZlcmVuY2UuIFRoZSBjaGVhdHNoZWV0IGlzIGFsc28gYXZhaWxhYmxlIHRocm91Z2ggdGhlIFJTdHVkaW8gSGVscCBtZW51Lgo8L2Rpdj4KCiMjIEJhc2ljIHBsb3QgdHlwZXMKCkEgcGxvdCBpbiBgZ2dwbG90MmAgaXMgY3JlYXRlZCB3aXRoIHRoZSBmb2xsb3dpbmcgdHlwZSBvZiBjb21tYW5kCgpgYGAKZ2dwbG90KGRhdGEgPSA8REFUQT4sIG1hcHBpbmcgPSBhZXMoPE1BUFBJTkdTPikpICsgIDxHRU9NX0ZVTkNUSU9OPigpCmBgYAoKU28gd2UgbmVlZCB0byBzcGVjaWZ5CgotIFRoZSBkYXRhIHRvIGJlIHVzZWQgaW4gZ3JhcGgKLSBNYXBwaW5ncyBvZiBkYXRhIHRvIHRoZSBncmFwaCAoKmFlc3RoZXRpYyogbWFwcGluZykKLSBXaGF0IHR5cGUgb2YgZ3JhcGggd2Ugd2FudCB0byB1c2UgKFRoZSAqZ2VvbSogdG8gdXNlKS4KCkxldHMgc2F5IHRoYXQgd2Ugd2FudCB0byBleHBsb3JlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBHRFAgYW5kIExpZmUgRXhwZWN0YW5jeS4gV2UgbWlnaHQgc3RhcnQgd2l0aCB0aGUgaHlwb3RoZXNpcyB0aGF0IHJpY2hlciBjb3VudHJpZXMgaGF2ZSBoaWdoZXIgbGlmZSBleHBlY3RhbmN5LiBBIHNlbnNpYmxlIGNob2ljZSBvZiBwbG90IHdvdWxkIGJlIGEgKnNjYXR0ZXIgcGxvdCogd2l0aCBnZHAgb24gdGhlIHgtYXhpcyBhbmQgbGlmZSBleHBlY3RhbmN5IG9uIHRoZSB5LWF4aXMuCgpUaGUgZmlyc3Qgc3RhZ2UgaXMgdG8gc3BlY2lmeSBvdXIgZGF0YXNldAoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXIpCmBgYAoKRm9yIHRoZSBhZXN0aGV0aWNzLCBhcyBhIGJhcmUgbWluaW11bSB3ZSB3aWxsIG1hcCB0aGUgYGdkcFBlcmNhcGAgYW5kIGBsaWZlRXhwYCB0byB0aGUgeC0gYW5kIHktYXhpcyBvZiB0aGUgcGxvdC4gU29tZSBwcm9ncmVzcyBpcyBtYWRlOyB3ZSBhdCBsZWFzdCBnZXQgYXhlcwoKYGBge3J9CmdncGxvdChkYXRhID0gZ2FwbWluZGVyLGFlcyh4PWdkcFBlcmNhcCwgeT1saWZlRXhwKSkKYGBgCgpUaGF0IGNyZWF0ZWQgdGhlIGF4ZXMsIGJ1dCB3ZSBzdGlsbCBuZWVkIHRvIGRlZmluZSBob3cgdG8gZGlzcGxheSBvdXIgcG9pbnRzIG9uIHRoZSBwbG90LiBBcyB3ZSBoYXZlIGNvbnRpbnVvdXMgZGF0YSBmb3IgYm90aCB0aGUgeC0gYW5kIHktYXhpcywgYGdlb21fcG9pbnRgIGlzIGEgZ29vZCBjaG9pY2UuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXIsYWVzKHg9Z2RwUGVyY2FwLCB5PWxpZmVFeHApKSArIGdlb21fcG9pbnQoKQpgYGAKCgoKClRoZSAqZ2VvbSogd2UgdXNlIHdpbGwgZGVwZW5kIG9uIHdoYXQga2luZCBvZiBkYXRhIHdlIGhhdmUgKGNvbnRpbnVvdXMsIGNhdGVnb3JpY2FsIGV0YykKCi0gYGdlb21fcG9pbnQoKWAgLSBTY2F0dGVyIHBsb3RzCi0gYGdlb21fbGluZSgpYCAtIExpbmUgcGxvdHMKLSBgZ2VvbV9zbW9vdGgoKWAgLSBGaXR0ZWQgbGluZSBwbG90cwotIGBnZW9tX2JhcigpYCAtIEJhciBwbG90cwotIGBnZW9tX2JveHBsb3QoKWAgLSBCb3hwbG90cwotIGBnZW9tX2ppdHRlcigpYCAtIEppdHRlciB0byBwbG90cwotIGBnZW9tX2hpc3RvZ3JhbSgpYCAtIEhpc3RvZ3JhbSBwbG90cwotIGBnZW9tX2RlbnNpdHkoKWAgLSBEZW5zaXR5IHBsb3RzCi0gYGdlb21fdGV4dCgpYCAtIFRleHQgdG8gcGxvdHMKLSBgZ2VvbV9lcnJvcmJhcigpYCAtIEVycm9yYmFycyB0byBwbG90cwotIGBnZW9tX3Zpb2xpbigpYCAtIFZpb2xpbiBwbG90cwotIGBnZW9tX3RpbGUoKWAgLSBmb3IgImhlYXRtYXAiLWxpa2UgcGxvdHMKCgpCb3hwbG90cyBhcmUgY29tbW9ubHkgdXNlZCB0byB2aXN1YWxpc2UgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgY29udGludW91cyBkYXRhLiBXZSBoYXZlIHRvIHVzZSBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIG9uIHRoZSB4LWF4aXMgc3VjaCBhcyBgY29udGluZW50YCBvciBgY291bnRyeWAgKG5vdCBhZHZpc2FibGUgaW4gdGhpcyBjYXNlIGFzIHRoZXJlIGFyZSB0b28gbWFueSBkaWZmZXJlbnQgdmFsdWVzKS4gCgpUaGUgb3JkZXIgb2YgdGhlIGJveGVzIGFsb25nIHRoZSB4LWF4aXMgaXMgZGljdGF0ZWQgYnkgdGhlIG9yZGVyIG9mIGNhdGVnb3JpZXMgaW4gdGhlIGZhY3Rvcjsgd2l0aCB0aGUgZGVmYXVsdCBmb3IgbmFtZXMgYmVpbmcgYWxwaGFiZXRpY2FsIG9yZGVyLgoKYGBge3J9CmdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gY29udGluZW50LCB5PWdkcFBlcmNhcCkpICsgZ2VvbV9ib3hwbG90KCkKYGBgCgoKYGBge3J9CmdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gZ2RwUGVyY2FwKSkgKyBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKUHJvZHVjaW5nIGEgYmFycGxvdCBvZiBjb3VudHMgb25seSByZXF1aXJlcyBhbiBgeGAgdmFyaWFibGUuIFRoZSBjb3VudHMgd2lsbCBiZSBnZW5lcmF0ZWQgYnkgUi4gCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHg9Y29udGluZW50KSkgKyBnZW9tX2JhcigpCmBgYAoKVGhlIGhlaWdodCBvZiB0aGUgYmFycyBjYW4gYWxzbyBiZSBtYXBwZWQgZGlyZWN0bHkgdG8gbnVtZXJpYyB2YXJpYWJsZXMgaW4gdGhlIGRhdGEgZnJhbWUgaWYgdGhlIGBnZW9tX2NvbGAgZnVuY3Rpb24gaXMgdXNlZCBpbnN0ZWFkLiAKCkluIHRoZSBiZWxvdyBwbG90IHRoZSBheGlzIGxhYmVscyB3aWxsIGJlIG1lc3N5IGFuZCBkaWZmaWN1bHQgdG8gcmVhZC4gVGhpcyBpcyBzb21ldGhpbmcgdGhhdCBjYW4gYmUgY3VzdG9taXNlZCB3aXRoIHNvbWUgb2YgdGhlIGBnZ3Bsb3QyYCBvcHRpb25zIHdlIHdpbGwgZXhwbG9yZSBsYXRlci4KCmBgYHtyfQpnYXBtaW5kZXIyMDAyIDwtIGZpbHRlcihnYXBtaW5kZXIsIHllYXI9PTIwMDIsY29udGluZW50PT0iQW1lcmljYXMiKQpnZ3Bsb3QoZ2FwbWluZGVyMjAwMiwgYWVzKHg9Y291bnRyeSx5PWdkcFBlcmNhcCkpICsgZ2VvbV9jb2woKQpgYGAKCldoZXJlIGFwcHJvcHJpYXRlLCB3ZSBjYW4gYWRkIG11bHRpcGxlIGxheWVycyBvZiBgZ2VvbWBzIHRvIHRoZSBwbG90LiBGb3IgaW5zdGFuY2UsIGEgY3JpdGljaXNtIG9mIHRoZSBib3hwbG90IGlzIHRoYXQgaXQgZG9lcyBub3Qgc2hvdyBhbGwgdGhlIGRhdGEuIFdlIGNhbiByZWN0aWZ5IHRoaXMgYnkgb3ZlcmxheWluZyB0aGUgaW5kaXZpZHVhbCBwb2ludHMuCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBjb250aW5lbnQsIHk9Z2RwUGVyY2FwKSkgKyBnZW9tX2JveHBsb3QoKSArIGdlb21fcG9pbnQoKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGNvbnRpbmVudCwgeT1nZHBQZXJjYXApKSArIGdlb21fYm94cGxvdCgpICsgZ2VvbV9qaXR0ZXIod2lkdGg9MC4xKQpgYGAKCgoqKioqKioKKioqKioqCioqKioqKgoKIyMjIEV4ZXJjaXNlcwoKPGRpdiBjbGFzcz0iZXhlcmNpc2UiPgotIFRoZSB2aW9saW4gcGxvdCBpcyBhIHBvcHVsYXIgYWx0ZXJuYXRpdmUgdG8gdGhlIGJveHBsb3QuIENyZWF0ZSBhIHZpb2xpbiBwbG90IHdpdGggYGdlb21fdmlvbGluYCB0byB2aXN1YWxpc2UgdGhlIGRpZmZlcmVuY2VzIGluIEdEUCBiZXR3ZWVuIGRpZmZlcmVudCBjb250aW5lbnRzLgotIENyZWF0ZSBhIHN1YnNldCBvZiB0aGUgYGdhcG1pbmRlcmAgZGF0YSBmcmFtZSBjb250YWluaW5nIGp1c3QgdGhlIHJvd3MgZm9yIHlvdXIgY291bnRyeSBvZiBiaXJ0aAotIEhhcyB0aGVyZSBiZWVuIGFuIGluY3JlYXNlIGluIGxpZmUgZXhwZWN0YW5jeSBvdmVyIHRpbWU/CiAgICArIHZpc3VhbGlzZSB0aGUgdHJlbmQgdXNpbmcgYSBzY2F0dGVyIHBsb3QgKGBnZW9tX3BvaW50YCksIGxpbmUgZ3JhcGggKGBnZW9tX2xpbmVgKSBvciBzbW9vdGhlZCBsaW5lIChgZ2VvbV9zbW9vdGhgKS4KLSBXaGF0IGhhcHBlbnMgd2hlbiB5b3UgbW9kaWZ5IHRoZSBgZ2VvbV9ib3hwbG90YCBleGFtcGxlIHRvIGNvbXBhcmUgdGhlIGdkcCBkaXN0cmlidXRpb25zIGZvciBkaWZmZXJlbnQgeWVhcnM/CiAgICArIExvb2sgYXQgdGhlIG1lc3NhZ2UgYGdncGxvdDJgIHByaW50cyBhYm92ZSB0aGUgcGxvdCBhbmQgdHJ5IHRvIG1vZGlmeSB0aGUgY29kZSB0byBnaXZlIGEgc2VwYXJhdGUgYm94cGxvdCBmb3IgZWFjaCB5ZWFyCjwvZGl2PgoKKioqKioqCioqKioqKgoqKioqKioKCgpgYGB7cn0KCmBgYAoKCgoKCkFzIHdlIGhhdmUgc2VlbiBhbHJlYWR5LCBgZ2dwbG90YCBvZmZlcnMgYW4gaW50ZXJmYWNlIHRvIGNyZWF0ZSBtYW55IHBvcHVsYXIgcGxvdCB0eXBlcy4gSXQgaXMgdXAgdG8gdGhlIHVzZXIgdG8gZGVjaWRlIHdoYXQgdGhlIGJlc3Qgd2F5IHRvIHZpc3VhbGlzZSB0aGUgZGF0YS4KCgoKCiMjIEN1c3RvbWlzaW5nIHRoZSBwbG90IGFwcGVhcmFuY2UKCk91ciBwbG90cyBhcmUgYSBiaXQgZHJlYXJ5IGF0IHRoZSBtb21lbnQsIGJ1dCBvbmUgd2F5IHRvIGFkZCBjb2xvdXIgaXMgdG8gYWRkIGEgYGNvbGAgYXJndW1lbnQgdG8gdGhlIGBnZW9tX3BvaW50YCBmdW5jdGlvbi4gVGhlIHZhbHVlIGNhbiBiZSBhbnkgb2YgdGhlIHByZS1kZWZpbmVkIGNvbG91ciBuYW1lcyBpbiBSLiBUaGVzZSBhcmUgZGlzcGxheWVkIGluIHRoaXMgW2hhbmR5IG9ubGluZSByZWZlcmVuY2VdKGh0dHA6Ly93d3cuc3RhdC5jb2x1bWJpYS5lZHUvfnR6aGVuZy9maWxlcy9SY29sb3IucGRmKS4gKlIqZWQsICpHKnJlZW4sICpCKmx1ZSBvZiAqSGV4KiB2YWx1ZXMgY2FuIGFsc28gYmUgZ2l2ZW4uCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBnZHBQZXJjYXAsIHk9bGlmZUV4cCkpICsgZ2VvbV9wb2ludChjb2w9InJlZCIpCmBgYAoKYGBge3J9CiMgVXNlIHRoZSBIZXggY29kZXMgZnJvbSBGYXJyb3cgYW5kIEJhbGw6IGh0dHBzOi8vY29udmVydGluZ2NvbG9ycy5jb20vbGlzdC9mYXJyb3ctYmFsbC5odG1sCiMgKGNvb2sncyBibHVlKQoKZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBnZHBQZXJjYXAsIHk9bGlmZUV4cCkpICsgZ2VvbV9wb2ludChjb2w9IiM2QTkwQjQiKQpgYGAKCgpIb3dldmVyLCBhIHBvd2VyZnVsIGZlYXR1cmUgb2YgYGdncGxvdDJgIGlzIHRoYXQgY29sb3VycyBhcmUgdHJlYXRlZCBhcyBhZXN0aGV0aWNzIG9mIHRoZSBwbG90LiBJbiBvdGhlciB3b3JkcyB3ZSBjYW4gdXNlIGEgY29sdW1uIGluIG91ciBkYXRhc2V0LgoKTGV0J3Mgc2F5IHRoYXQgd2Ugd2FudCBwb2ludHMgb24gb3VyIHBsb3QgdG8gYmUgY29sb3VyZWQgYWNjb3JkaW5nIHRvIGNvbnRpbmVudC4gV2UgYWRkIGFuIGV4dHJhIGFyZ3VtZW50IHRvIHRoZSBkZWZpbml0aW9uIG9mIGFlc3RoZXRpY3MgdG8gZGVmaW5lIHRoZSBtYXBwaW5nLiBgZ2dwbG90MmAgd2lsbCBldmVuIGRlY2lkZSBvbiBjb2xvdXJzIGFuZCBjcmVhdGUgYSBsZWdlbmQgZm9yIHVzLgoKYGBge3J9CmdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gZ2RwUGVyY2FwLCB5PWxpZmVFeHAsY29sPWNvbnRpbmVudCkpICsgZ2VvbV9wb2ludCgpCmBgYAoKSXQgd2lsbCBldmVuIGNob29zZSBhIGNvbnRpbnVvdXMgb3IgZGlzY3JldGUgY29sb3VyIHNjYWxlIGJhc2VkIG9uIHRoZSBkYXRhIHR5cGUuIFdlIGhhdmUgYWxyZWFkeSBzZWVuIHRoYXQgYGdncGxvdDJgIGlzIHRyZWF0IG91ciBgeWVhcmAgY29sdW1uIGFzIG51bWVyaWNhbCBkYXRhOyB3aGljaCBpcyBwcm9iYWJseSBub3QgdmVyeSB1c2VmdWwgZm9yIHZpc3VhbGlzYXRpb24uCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBnZHBQZXJjYXAsIHk9bGlmZUV4cCxjb2w9eWVhcikpICsgZ2VvbV9wb2ludCgpCmBgYAoKV2UgY2FuIGZvcmNlIGBnZ3Bsb3QyYCB0byB0cmVhdCBgeWVhcmAgYXMgY2F0ZWdvcmljYWwgZGF0YSBieSB1c2luZyBgYXMuZmFjdG9yYCB3aGVuIGNyZWF0aW5nIHRoZSBhZXN0aGV0aWNzLgoKYGBge3J9CmdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gZ2RwUGVyY2FwLCB5PWxpZmVFeHAsY29sPWFzLmZhY3Rvcih5ZWFyKSkpICsgZ2VvbV9wb2ludCgpCmBgYAoKV2hlbiB1c2VkIGluIHRoZSBjb25zdHJ1Y3Rpb24gb2YgYSBib3hwbG90LCB0aGUgYGNvbGAgYXJndW1lbnQgd2lsbCBjaGFuZ2UgdGhlIGNvbG91ciBvZiB0aGUgbGluZXMuIFRvIGNoYW5nZSB0aGUgY29sb3VyIG9mIHRoZSBib3hlcyB3ZSBoYXZlIHRvIHVzZSBgZmlsbGAuCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBjb250aW5lbnQsIHk9Z2RwUGVyY2FwLGZpbGw9Y29udGluZW50KSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCgoqKioqKioKKioqKioqCioqKioqKgoKIyMjIEV4ZXJjaXNlcyBiZWZvcmUgdGhlIG5leHQgc2Vzc2lvbgo8ZGl2IGNsYXNzPSJleGVyY2lzZSI+Ci0gVXNpbmcgdGhlIGBmaWx0ZXJgIGZ1bmN0aW9uLCBmaW5kIGFsbCBjb3VudHJpZXMgdGhhdCBzdGFydCB3aXRoIHRoZSBsZXR0ZXIgWgogICsgSGludDogWW91IGNhbiBmaW5kIHRoZSBmaXJzdCBsZXR0ZXIgb2YgZWFjaCBjb3VudHJ5IHVzaW5nIHRoZSBgc3Vic3RyYCBmdW5jdGlvbi4gVGhlIGBtdXRhdGVgIGZ1bmN0aW9uIGNhbiB0aGVuIGJlIHVzZWQgdG8gYWRkIGEgbmV3IGNvbHVtbiB0byB0aGUgZGF0YS4KLSBVc2UgYGdlb21fdGlsZWAgdG8gY3JlYXRlIGEgaGVhdG1hcCB2aXN1YWxpc2luZyBsaWZlIGV4cGVjdGFuY3kgb3ZlciB0aW1lIGZvciBFdXJvcGVhbiBjb3VudHJpZXMuIFlvdSB3aWxsIG5lZWQgdG8gd29yayBvdXQgd2hhdCBgYWVzYHRoZXRpY3MgdG8gc3BlY2lmeSBmb3IgYSBgZ2VvbV90aWxlYCBwbG90CjwvZGl2PgoKKioqKioqCioqKioqKgoqKioqKioKCkFuIGV4YW1wbGUgcGxvdCBpcyBzaG93biBvbiB0aGUgY29tcGlsZWQgbm90ZXMuCgohW10oaW1hZ2VzL3RpbGVfZXhhbXBsZS5wbmcpCgoKIyMgQXBwZW5kaXgKCiMjIyBBZGRpbmcgdGV4dCB0byBhIHBsb3QKCkFubm90YXRpb25zIGNhbiBiZSBhZGRlZCB0byBhIHBsb3QgdXNpbmcgdGhlIGZsZXhpYmxlIGBhbm5vdGF0ZWAgZnVuY3Rpb24gZG9jdW1lbnRlZCBbaGVyZV0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2Fubm90YXRlLmh0bWwpLiBUaGlzIHByZXN1bWVzIHRoYXQgeW91IGtub3cgdGhlIGNvb3JkaW5hdGVzIHRoYXQgeW91IHdhbnQgdG8gYWRkIHRoZSBhbm5vdGF0aW9ucyBhdC4KCmBgYHtyfQpwPC0gZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwLGNvbD1jb250aW5lbnQpKSArIGdlb21fcG9pbnQoKQpwICsgYW5ub3RhdGUoInRleHQiLCB4ID0gOTAwMDAseT02MCwgbGFiZWw9IlNvbWUgdGV4dCIpCmBgYAoKSGlnaGxpZ2h0aW5nIHBhcnRpY3VsYXIgcG9pbnRzIG9mIGludGVyZXN0IHVzaW5nIGEgcmVjdGFuZ2xlLgoKYGBge3J9CnAgKyBhbm5vdGF0ZSgicmVjdCIsIHhtaW49MjUwMDAsIHhtYXg9MTIwMDAwLHltaW49NTAseW1heD03NSxhbHBoYT0wLjIpCmBgYAoKCldlIGNhbiBhbHNvIG1hcCBkaXJlY3RseSBmcm9tIGEgY29sdW1uIGluIG91ciBkYXRhc2V0IHRvIHRoZSBgbGFiZWxgIGFlc3RoZXRpYy4gSG93ZXZlciwgdGhpcyB3aWxsIGxhYmVsIGFsbCB0aGUgcG9pbnRzIHdoaWNoIGlzIHJhdGhlciBjbHV0dGVyZWQgaW4gb3VyIGNhc2UKCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHAsY29sPWNvbnRpbmVudCxsYWJlbD1jb3VudHJ5KSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3RleHQoKQpgYGAKCkluc3RlYWQsIHdlIGNvdWxkIHVzZSBhIGRpZmZlcmVudCBkYXRhc2V0IHdoZW4gd2UgY3JlYXRlIHRoZSB0ZXh0IGxhYmVscyB3aXRoIGBnZW9tX3RleHRgLiBIZXJlIHdlIGZpbHRlciB0aGUgYGdhcG1pbmRlcmAgZGF0YXNldCB0byBvbmx5IGNvdW50cmllcyB3aXRoIGBnZHBQZXJjYXBgIGdyZWF0ZXIgdGhhbiBgNTcwMDBgIGFuZCBvbmx5IHRoZXNlIHBvaW50cyBnZXQgbGFiZWxlZC4gV2UgY2FuIGFsc28gc2V0IHRoZSB0ZXh0IGNvbG91cnMgdG8gYSBwYXJ0aWN1bGFyIHZhbHVlIHJhdGhlciB0aGFuIHVzaW5nIHRoZSBvcmlnaW5hbCBjb2xvdXIgbWFwcGluZ3MgZm9yIHRoZSBwbG90IChiYXNlZCBvbiBjb250aW5lbnQpLgoKYGBge3J9CnAgKyBnZW9tX3RleHQoZGF0YSA9IGZpbHRlcihnYXBtaW5kZXIsIGdkcFBlcmNhcCA+IDU3MDAwKSwgCiAgICAgICAgICAgICAgYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwLGxhYmVsPWNvdW50cnkpLGNvbD0iYmxhY2siKQpgYGAKCmBgYHtyfQpwICsgZ2VvbV90ZXh0KGRhdGEgPSBmaWx0ZXIoZ2FwbWluZGVyLCBnZHBQZXJjYXAgPiAyNTAwMCwgbGlmZUV4cCA8IDc1KSwgCiAgICAgICAgICAgICAgYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwLGxhYmVsPWNvdW50cnkpLGNvbD0iYmxhY2siLHNpemU9MykgKyBhbm5vdGF0ZSgicmVjdCIsIHhtaW49MjUwMDAsIHhtYXg9MTIwMDAwLHltaW49NTAseW1heD03NSxhbHBoYT0wLjIpCmBgYAoKCiMjIyBDb21tZW50IGFib3V0IHRoZSBheGlzIHNjYWxlCgpUaGUgcGxvdCBvZiBgZ2RwUGVyY2FwYCB2cyBgbGlmZUV4cGAgb24gdGhlIG9yaWdpbmFsIHNjYWxlIHNlZW1zIHRvIGJlIGluZmx1ZW5jZWQgYnkgdGhlIG91dGxpZXIgb2JzZXJ2YXRpb25zICh3aGljaCB3ZSBub3cga25vdyBhcmUgb2JzZXJ2YXRpb25zIGZyb20gYEt1d2FpdGApLiBJbiBzdWNoIHNpdHVhdGlvbnMgaXQgbWF5IGJlIHBvc3NpYmxlIHRvIHRyYW5zZm9ybSB0aGUgc2NhbGUgb2Ygb25lIGF4aXMgZm9yIHZpc3VhbGlzYXRpb24gcHVycG9zZXMuIE9uZSBzdWNoIHRyYW5zZm9ybWF0aW9uIGlzIGBsb2cxMGAsIHdoaWNoIHdlIGNhbiBhcHBseSB3aXRoIHRoZSBgc2NhbGVfeF9sb2cxMGAgZnVuY3Rpb24uIE90aGVycyBpbmNsdWRlIGBzY2FsZV94X2xvZzJgLCBgc2NhbGVfeF9zcXJ0YCBhbmQgZXF1aXZhbGVudHMgZm9yIHRoZSB5IGF4aXMuCgpgYGB7cn0KcCArIHNjYWxlX3hfbG9nMTAoKQpgYGAKCkJ5IHNwbGl0dGluZyB0aGUgcGxvdCBieSBjb250aW5lbnRzIHdlIHNlZSBtb3JlIGNsZWFybHkgd2hpY2ggY29udGluZW50cyBoYXZlIGEgbW9yZSBsaW5lYXIgcmVsYXRpb25zaGlwLiBBdCB0aGUgbW9tZW50IHRoaXMgaXMgdXNlZnVsIGZvciB2aXN1YWxpc2F0aW9uIHB1cnBvc2VzLCBpZiB3ZSB3YW50ZWQgdG8gb2J0YWluIHN1bW1hcmllcyBmcm9tIHRoZSBkYXRhIHdlIHdvdWxkIG5lZWQgdGhlIHRlY2huaXF1ZXMgaW4gdGhlIG5leHQgc2VjdGlvbi4KCmBgYHtyfQpwICsgc2NhbGVfeF9sb2cxMCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsY29sPSJibGFjayIpICsgZmFjZXRfd3JhcCh+Y29udGluZW50KQpgYGAKCg==
Comment about the axis scale
The plot of
gdpPercap
vslifeExp
on the original scale seems to be influenced by the outlier observations (which we now know are observations fromKuwait
). In such situations it may be possible to transform the scale of one axis for visualisation purposes. One such transformation islog10
, which we can apply with thescale_x_log10
function. Others includescale_x_log2
,scale_x_sqrt
and equivalents for the y axis.By splitting the plot by continents we see more clearly which continents have a more linear relationship. At the moment this is useful for visualisation purposes, if we wanted to obtain summaries from the data we would need the techniques in the next section.