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.
It is always useful to think about the message you want to convey and the appropriate plot before writing any R code. Resources like this should help.
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.
As with dplyr
, we won’t have time to cover all details of ggplot2
. This is however a useful cheatsheet that can be printed as a reference.
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. In the case of the gapminder
data we might have to persuade ggplot2
that the year
column is a factor
rather than numerical data.
ggplot(gapminder, aes(x = as.factor(year), y=gdpPercap)) + geom_boxplot()
ggplot(gapminder, aes(x = gdpPercap)) + geom_histogram()
Counts with a barplot
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 stat="identity"
argument is set within geom_bar
. Note that the axis labels can be modified, as we will see later on.
gapminder2002 <- filter(gapminder, year==2002,continent=="Americas")
ggplot(gapminder2002, aes(x=country,y=gdpPercap)) + geom_bar(stat="identity")
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 = as.factor(year), y=gdpPercap)) + geom_boxplot() + geom_point()
ggplot(gapminder, aes(x = as.factor(year), 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 increase in GDP over time.
- 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
).
Solutions
ggplot(gapminder, aes(x = as.factor(year), y = gdpPercap)) + geom_violin()
filter(gapminder, country=="United Kingdom") %>%
ggplot(aes(x = year, y= lifeExp)) + geom_point()
## We can to keep factor as a numeric value (i.e. not convert to a factor) to make the plot
filter(gapminder, country=="United Kingdom") %>%
ggplot(aes(x = year, y= lifeExp)) + geom_line()
filter(gapminder, country=="United Kingdom") %>%
ggplot(aes(x = year, y= lifeExp)) + geom_smooth()
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.
It is also up to the user how to interpret the data. Consider the following plot and what message it might be conveying.
However, when considering the source of the plot your interpretation might change.
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")
However, a powerful feature of ggplot2
is that colours are treated as aesthetics of the plot. In other words we can use 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()
Question: Can you explain why the colour scheme used on the following two plots are different
ggplot(gapminder, aes(x = gdpPercap, y=lifeExp,col=year)) + geom_point()
ggplot(gapminder, aes(x = gdpPercap, y=lifeExp,col=as.factor(year))) + geom_point()
Shape and size of points can also be mapped from the data. However, it is easy to get carried away.
ggplot(gapminder, aes(x = gdpPercap, y=lifeExp,shape=continent,size=pop,col=as.factor(year))) + geom_point()
Scales and their legends have so far been handled using ggplot2 defaults. ggplot2 offers functionality to have finer control over scales and legends using the scale methods.
Scale methods are divided into functions by combinations of
scale_
aesthetic_type
Try typing in scale_
then tab to autocomplete. This will provide some examples of the scale functions available in ggplot2
.
Although different scale functions accept some variety in their arguments, common arguments to scale functions include -
name - The axis or legend title
limits - Minimum and maximum of the scale
breaks - Label/tick positions along an axis
labels - Label names at each break
values - the set of aesthetic values to map data values
We can choose specific colour palettes, such as those provided by the RColorBrewer
package. This package provides palettes for different types of scale (sequential, diverging, qualitative).
library(RColorBrewer)
display.brewer.all()
When experimenting with colour palettes and labels, it is useful to save the plot as an object
p <- ggplot(gapminder, aes(x = gdpPercap, y=lifeExp,col=continent)) + geom_point()
p + scale_color_brewer(palette = "Set2")
Or we can even specify our own colours; such as The University of Sheffield branding colours
my_pal <- c(rgb(0,159,218,maxColorValue = 255),
rgb(31,20,93,maxColorValue = 255),
rgb(249,227,0,maxColorValue = 255),
rgb(0,155,72,maxColorValue = 255),
rgb(190,214,0,maxColorValue = 255))
p + scale_color_manual(values=my_pal)
Various labels can be modified using the labs
function.
p + labs(x="Wealth",y="Life Expectancy",title="Relationship between Wealth and Life Expectancy")
We can also modify the x- and y- limits of the plot so that any outliers are not shown. ggplot2
will give a warning that some points are excluded.
p + xlim(0,60000)
Saving is supported by the ggsave
function.
ggsave(p, file="my_ggplot.png")
Saving 7 x 7 in image
Most aspects of the plot can be modified from the background colour to the grid sizes and font. Several pre-defined “themes” exist and we can modify the appearance of the whole plot using a theme_..
function.
p + theme_bw()
More themes are supported by the ggthemes
package. You can make your plots look like the Economist, Wall Street Journal or Excel (but please don’t do this!)
Facets
One very useful feature of ggplot is faceting. This allows you to produce plots subset by variables in your data. In the scatter plot above, it was quite difficult to see if the relationship between gdp and life expectancy was the same for each continent. To overcome this, we would like a see a separate plot for each continent.
To facet our data into multiple plots we can use the facet_wrap
or facet_grid
function and specify the variable we split by.
p + facet_wrap(~continent)
The facet_grid
function will create a grid-like plot with one variable on the x-axis and another on the y-axis.
p + facet_grid(continent~year)
The previous plot was a bit messy as it contained all combinations of year and continent. Let’s suppose we want our analysis to be a bit more focussed and disregard countries in Oceania (as there are only 2 in our dataset) and years between 1997 and 2002. We should know how to restrict the rows from the gapminder
dataset using the filter
function. Instead of filtering the data, creating a new data frame and construcing the data frame from these new data we can use the%>%
operator to create the data frame on the fly and pass directly to ggplot
. Thus we don’t have to save a new data frame or alter the original data.
filter(gapminder, continent!="Oceania", year %in% c(1997,2002,2007)) %>%
ggplot(aes(x = gdpPercap, y=lifeExp,col=continent)) + geom_point() + facet_grid(continent~year)
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 poins 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 labelled. 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)
LS0tCnRpdGxlOiAiUiBDcmFzaCBDb3Vyc2UiCmF1dGhvcjogIk1hcmsgRHVubmluZyIKZGF0ZTogJ2ByIGZvcm1hdChTeXMudGltZSgpLCAiTGFzdCBtb2RpZmllZDogJWQgJWIgJVkiKWAnCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgojIEludHJvZHVjdGlvbiB0byBSIC0gUGFydCBJSQoKIyMgUmVjYXAKCldlIHNob3VsZCBoYXZlIGxvYWRlZCB0aGUgYHRpZHl2ZXJzZWAgbGlicmFyeSBhbmQgaW1wb3J0ZWQgYW4gZXhhbXBsZSBkYXRhc2V0IGludG8gUgoKYGBge3IgbWVzc2FnZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmdhcG1pbmRlciA8LSByZWFkX2NzdigicmF3X2RhdGEvZ2FwbWluZGVyLmNzdiIpCmBgYAoKCiMjIE1hbmlwdWxhdGluZyBkYXRhCgpXZSBhcmUgZ29pbmcgdG8gdXNlIGZ1bmN0aW9ucyBmcm9tIHRoZSAqKmBkcGx5cmAqKiBwYWNrYWdlICh3aGljaCBpcyBhdXRvbWF0aWNhbGx5IGxvYWRlZCBieSBsb2FkaW5nIHRoZSBgdGlkeXZlcnNlYCkgdG8gKiptYW5pcHVsYXRlIHRoZSBkYXRhIGZyYW1lKiogd2UgaGF2ZSBqdXN0IGNyZWF0ZWQuIEl0IGlzIHBlcmZlY3RseSBwb3NzaWJsZSB0byB3b3JrIHdpdGggZGF0YSBmcmFtZXMgdXNpbmcgdGhlIGZ1bmN0aW9ucyBwcm92aWRlZCBhcyBwYXJ0IG9mICIqYmFzZSBSKiIuIEhvd2V2ZXIsIG1hbnkgZmluZCBpdCBlYXN5IHRvIHJlYWQgYW5kIHdyaXRlIGNvZGUgdXNpbmcgYGRwbHlyYC4KClRoZXJlIGFyZSAqKm1hbnkgbW9yZSBmdW5jdGlvbnMgYXZhaWxhYmxlIGluIGBkcGx5cmAqKiB0aGFuIHdlIHdpbGwgY292ZXIgdG9kYXkuIEFuIG92ZXJ2aWV3IG9mIGFsbCBmdW5jdGlvbnMgaXMgZ2l2ZW4gaW4gdGhlIGZvbGxvd2luZyBbY2hlYXRzaGVldF0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTUvMDIvZGF0YS13cmFuZ2xpbmctY2hlYXRzaGVldC5wZGYpCgojIyMgYHNlbGVjdGBpbmcgY29sdW1ucwoKCldlIGNhbiAqKmFjY2VzcyB0aGUgY29sdW1ucyoqIG9mIGEgZGF0YSBmcmFtZSB1c2luZyB0aGUgYHNlbGVjdGAgZnVuY3Rpb24uIAoKIyMjIyBieSBuYW1lCgpGaXJzdGx5LCB3ZSBjYW4gc2VsZWN0IGNvbHVtbiBieSBuYW1lLCBieSBhZGRpbmcgYmFyZSBjb2x1bW4gbmFtZXMgYWZ0ZXIgdGhlIG5hbWUgb2YgdGhlIGRhdGEgZnJhbWUsIHNlcGFyYXRlZCBieSBhIGAsYCAuIAoKYGBge3J9CnNlbGVjdChnYXBtaW5kZXIsIGNvdW50cnksIGNvbnRpbmVudCkKYGBgCgpXZSBjYW4gYWxzbyByZW1vdmUgY29sdW1ucyBmcm9tIGJ5IHB1dHRpbmcgYSBtaW51cyAoYC1gKSBpbiBmcm9udCBvZiB0aGUgY29sdW1uIG5hbWUuCgpgYGB7cn0Kc2VsZWN0KGdhcG1pbmRlciwgLWNvdW50cnkpCmBgYAoKIyMjIyByYW5nZSBvZiBjb2x1bW5zCgpBIHJhbmdlIG9mIGNvbHVtbnMgY2FuIGJlIHNlbGVjdGVkIGJ5IHRoZSBgOmAgb3BlcmF0b3IuCgpgYGB7cn0Kc2VsZWN0KGdhcG1pbmRlciwgbGlmZUV4cDpnZHBQZXJjYXApCmBgYAoKIyMjIyBoZWxwZXIgZnVuY3Rpb25zIAoKVGhlcmUgYXJlIGEgbnVtYmVyIG9mIGhlbHBlciBmdW5jdGlvbnMgY2FuIGJlIGVtcGxveWVkIGlmIHdlIGFyZSB1bnN1cmUgYWJvdXQgdGhlIGV4YWN0IG5hbWUgb2YgdGhlIGNvbHVtbi4KCmBgYHtyfQpzZWxlY3QoZ2FwbWluZGVyLCBzdGFydHNfd2l0aCgibGlmZSIpKQpzZWxlY3QoZ2FwbWluZGVyLCBjb250YWlucygicG9wIikpCnNlbGVjdChnYXBtaW5kZXIsIG9uZV9vZigicG9wIiwgImNvdW50cnkiKSkKYGBgCgojIyMgUmVzdHJpY3Rpbmcgcm93cyB3aXRoIGZpbHRlcgoKU28gZmFyIHdlIGhhdmUgYmVlbiByZXR1cm5pbmcgYWxsIHRoZSByb3dzIGluIHRoZSBvdXRwdXQuIFdlIGNhbiB1c2Ugd2hhdCB3ZSBjYWxsIGEgKipsb2dpY2FsIHRlc3QqKiB0byAqKmZpbHRlciB0aGUgcm93cyoqIGluIGEgZGF0YSBmcmFtZS4gVGhpcyBsb2dpY2FsIHRlc3Qgd2lsbCBiZSBhcHBsaWVkIHRvIGVhY2ggcm93IGFuZCBnaXZlIGVpdGhlciBhIGBUUlVFYCBvciBgRkFMU0VgIHJlc3VsdC4gV2hlbiBmaWx0ZXJpbmcsICoqb25seSByb3dzIHdpdGggYSBgVFJVRWAgcmVzdWx0IGdldCByZXR1cm5lZCoqLgoKRm9yIGV4YW1wbGUgd2UgZmlsdGVyIGZvciByb3dzIHdoZXJlIHRoZSAqKmBsaWZlRXhwYCB2YXJpYWJsZSBpcyBsZXNzIHRoYW4gNDAqKi4gCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgbGlmZUV4cCA8IDQwKQpgYGAKCkludGVybmFsbHksIFIgY3JlYXRlcyBhICp2ZWN0b3IqIG9mIGBUUlVFYCBvciBgRkFMU0VgOyBvbmUgZm9yIGVhY2ggcm93IGluIHRoZSBkYXRhIGZyYW1lLiBUaGlzIGlzIHRoZW4gdXNlZCB0byBkZWNpZGUgd2hpY2ggcm93cyB0byBkaXNwbGF5LgoKVGVzdGluZyBmb3IgZXF1YWxpdHkgY2FuIGJlIGRvbmUgdXNpbmcgYD09YC4gVGhpcyB3aWxsIG9ubHkgZ2l2ZSBgVFJVRWAgZm9yIGVudHJpZXMgdGhhdCBhcmUgKmV4YWN0bHkqIHRoZSBzYW1lIGFzIHRoZSB0ZXN0IHN0cmluZy4gCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgY291bnRyeSA9PSAiWmFtYmlhIikKCmBgYAoKTi5CLiBGb3IgcGFydGlhbCBtYXRjaGVzLCB0aGUgYGdyZXBsYCBmdW5jdGlvbiBhbmQgLyBvciAqcmVndWxhciBleHByZXNzaW9ucyogKGlmIHlvdSBrbm93IHRoZW0pIGNhbiBiZSB1c2VkLgoKYGBge3J9CmZpbHRlcihnYXBtaW5kZXIsIGdyZXBsKCJsYW5kIiwgY291bnRyeSkpCmBgYAoKV2UgY2FuIGFsc28gdGVzdCBpZiByb3dzIGFyZSAqbm90KiBlcXVhbCB0byBhIHZhbHVlIHVzaW5nICBgIT1gIAoKYGBge3J9CmZpbHRlcihnYXBtaW5kZXIsIGNvbnRpbmVudCAhPSAiRXVyb3BlIikKCmBgYAoKIyMjIyB0ZXN0aW5nIG1vcmUgdGhhbiBvbmUgY29uZGl0aW9uCgpUaGVyZSBhcmUgYSBjb3VwbGUgb2Ygd2F5cyBvZiB0ZXN0aW5nIGZvciBtb3JlIHRoYW4gb25lIHBhdHRlcm4uIFRoZSBmaXJzdCB1c2VzIGFuICpvciogYHxgIHN0YXRlbWVudC4gaS5lLiB0ZXN0aW5nIGlmIHRoZSB2YWx1ZSBvZiBgY291bnRyeWAgaXMgYFphbWJpYWAgKm9yKiB0aGUgdmFsdWUgaXMgYFppbWJhYndlYC4gUmVtZW1iZXIgdG8gdXNlIGRvdWJsZSBgPWAgc2lnbiB0byB0ZXN0IGZvciBzdHJpbmcgZXF1YWxpdHk7IGA9PWAuCgoKYGBge3J9CmZpbHRlcihnYXBtaW5kZXIsIGNvdW50cnkgPT0gIlphbWJpYSIgfCBjb3VudHJ5ID09ICJaaW1iYWJ3ZSIpCmBgYAoKClRoZSBgJWluJWAgZnVuY3Rpb24gaXMgYSBjb252ZW5pZW50IGZ1bmN0aW9uIGZvciB0ZXN0aW5nIHdoaWNoIGl0ZW1zIGluIGEgdmVjdG9yIGNvcnJlc3BvbmQgdG8gYSBkZWZpbmVkIHNldCBvZiB2YWx1ZXMuCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgY291bnRyeSAlaW4lIGMoIlphbWJpYSIsICJaaW1iYWJ3ZSIpKQpgYGAKCgpXZSBjYW4gcmVxdWlyZSB0aGF0IGJvdGggdGVzdHMgYXJlIGBUUlVFYCwgIGUuZy4gd2hpY2ggeWVhcnMgaW4gWmFtYmlhIGhhZCBhIGxpZmUgZXhwZWN0YW5jeSBsZXNzIHRoYW4gNDAsIGJ5OgoKLSB1c2luZyBhbiAqYW5kKiBgJmAgb3BlcmF0aW9uLgoKYGBge3J9CmZpbHRlcihnYXBtaW5kZXIsIGNvdW50cnkgPT0gIlphbWJpYSIgJiBsaWZlRXhwIDwgNDApCgpgYGAKT3IganVzdCBieSBzZXBhcmF0aW5nIGNvbmRpdGlvbmFsIHN0YXRlbW5ldHMgYnkgYSBgLGAKCmBgYHtyfQpmaWx0ZXIoZ2FwbWluZGVyLCBjb3VudHJ5ID09ICJaYW1iaWEiLCBsaWZlRXhwIDwgNDApCmBgYAoKCioqKioqKgoqKioqKioKKioqKioqCgojIyMjIEV4ZXJjaXNlCgotIENyZWF0ZSBhIHN1YnNldCBvZiB0aGUgZGF0YSB3aGVyZSB0aGUgcG9wdWxhdGlvbiBsZXNzIHRoYW4gYSBtaWxsaW9uIGluIHRoZSB5ZWFyIDIwMDIKLSBDcmVhdGUgYSBzdWJzZXQgb2YgdGhlIGRhdGEgd2hlcmUgdGhlIGxpZmUgZXhwZWN0YW5jeSBpcyBncmVhdGVyIHRoYW4gNzUgaW4gdGhlIHllYXJzIHByaW9yIHRvIDE5ODcKLSAoRVhUUkEpCi0gRGF0YSBmb3IgY291bnRyaWVzIHdob3NlIG5hbWUgYmVnaW5zIHdpdGggdGhlIGxldHRlciBgWmAuIFlvdSBtaWdodCB3YW50IHRvIGludmVzdGlnYXRlIHRoZSB1c2FnZSBvZiB0aGUgYHN1YnN0cmAgZnVuY3Rpb24uCgoKKioqKioqCioqKioqKgoqKioqKioKCiMjIyBtYW5pcHVsYXRpbmcgY29sdW1uIHZhbHVlcwoKQXMgd2VsbCBhcyBzZWxlY3RpbmcgZXhpc3RpbmcgY29sdW1ucyBpbiB0aGUgZGF0YSBmcmFtZSwgbmV3IGNvbHVtbnMgY2FuIGJlIGNyZWF0ZWQgYW5kIGV4aXN0aW5nIG9uZXMgbWFuaXB1bGF0ZWQgdXNpbmcgdGhlIGBtdXRhdGVgIGZ1bmN0aW9uLiBUeXBpY2FsbHkgYSBmdW5jdGlvbiBvciBtYXRoZW1hdGljYWwgZXhwcmVzc2lvbiB0byBkYXRhIGluIGV4aXN0aW5nIGNvbHVtbnMgYnkgcm93IGFuZCB0aGUgcmVzdWx0IGVpdGhlciBzdG9yZWQgaW4gYSBuZXcgY29sdW1uIG9yIHJlYXNzaWduZWQgdG8gYW4gZXhpc3Rpbmcgb25lLiBJbiBvdGhlciB3b3JkcywgdGhlIG51bWJlciBvZiB2YWx1ZXMgcmV0dXJuZWQgYnkgdGhlIGZ1bmN0aW9uIG11c3QgYmUgdGhlIHNhbWUgYXMgdGhlIG51bWJlciBvZiBpbnB1dCB2YWx1ZXMuIE11bHRpcGxlIG11dGF0aW9ucyBjYW4gYmUgcGVyZm9ybWVkIGluIG9uZSBjYWxsLgoKSGVyZSwgd2UgY3JlYXRlIGEgbmV3IGNvbHVtbiBvZiBwb3B1bGF0aW9uIGluIG1pbGxpb25zIChgUG9wSW5NaWxsaW9uc2ApIGFuZCByb3VuZCBgbGlmZUV4cGAgdG8gdGhlIG5lYXJlc3QgaW50ZWdlci4KCmBgYHtyfQptdXRhdGUoZ2FwbWluZGVyLCBQb3BJbk1pbGxpb25zID0gcG9wIC8gMWU2LAogICAgICAgbGlmZUV4cCA9IHJvdW5kKGxpZmVFeHApKQoKYGBgCgpJZiB3ZSB3YW50IHRvIHJlbmFtZSBleGlzdGluZyBjb2x1bW5zLCBhbmQgbm90IGNyZWF0ZSBhbnkgZXh0cmEgY29sdW1ucywgd2UgY2FuIHVzZSB0aGUgYHJlbmFtZWAgZnVuY3Rpb24uCgpgYGB7cn0KcmVuYW1lKGdhcG1pbmRlciwgR0RQPWdkcFBlcmNhcCkKYGBgCgoKIyMjIE9yZGVyaW5nIGFuZCBzb3J0aW5nCgpUaGUgd2hvbGUgZGF0YSBmcmFtZSBjYW4gYmUgcmUtb3JkZXJlZCBhY2NvcmRpbmcgdG8gdGhlIHZhbHVlcyBpbiBvbmUgY29sdW1uIHVzaW5nIHRoZSBgYXJyYW5nZWAgZnVuY3Rpb24uIFNvIHRvIG9yZGVyIHRoZSB0YWJsZSBhY2NvcmRpbmcgdG8gcG9wdWxhdGlvbiBzaXplOi0KCmBgYHtyfQphcnJhbmdlKGdhcG1pbmRlciwgcG9wKQpgYGAKCgpUaGUgZGVmYXVsdCBpcyBgc21hbGxlc3QgLS0+IGxhcmdlc3RgIGJ5IHdlIGNhbiBjaGFuZ2UgdGhpcyB1c2luZyB0aGUgYGRlc2NgIGZ1bmN0aW9uCgpgYGB7cn0KYXJyYW5nZShnYXBtaW5kZXIsIGRlc2MocG9wKSkKYGBgCgpgYXJyYW5nZWAgYWxzbyB3b3JrcyBvbiBjaGFyYWN0ZXIgdmVjdG9ycywgYXJyYW5nZSB0aGVtIGFscGhhLW51bWVyaWNhbGx5LgoKYGBge3J9CmFycmFuZ2UoZ2FwbWluZGVyLCBkZXNjKGNvdW50cnkpKQpgYGAKCldlIGNhbiBldmVuIG9yZGVyIGJ5IG1vcmUgdGhhbiBvbmUgY29uZGl0aW9uCgpgYGB7cn0KYXJyYW5nZShnYXBtaW5kZXIsIHllYXIsIHBvcCkKYGBgCgoKCgojIyMgc2F2aW5nIGRhdGEgZnJhbWVzCgpBIGZpbmFsIHBvaW50IG9uIGRhdGEgZnJhbWVzIGlzIHRoYXQgd2UgY2FuICoqd3JpdGUgdGhlbSB0byBkaXNrIG9uY2Ugd2UgaGF2ZSBkb25lIG91ciBkYXRhIHByb2Nlc3NpbmcqKi4gCgpMZXQncyBjcmVhdGUgYSBmb2xkZXIgaW4gd2hpY2ggdG8gc3RvcmUgc3VjaCBwcm9jZXNzZWQsIGFuYWx5c2lzIHJlYWR5IGRhdGEKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpkaXIuY3JlYXRlKCJkYXRhIikKYGBgCgoKYGBge3J9CmJ5V2VhbHRoIDwtIGFycmFuZ2UoZ2FwbWluZGVyLCBkZXNjKGdkcFBlcmNhcCkpCmhlYWQoYnlXZWFsdGgpCndyaXRlX2NzdihieVdlYWx0aCwgcGF0aCA9ICgiZGF0YS9ieV93ZWFsdGguY3N2IikpCmBgYAoKV2Ugd2lsbCBub3cgdHJ5IGFuIGV4ZXJjaXNlIHRoYXQgaW52b2x2ZXMgdXNpbmcgc2V2ZXJhbCBzdGVwcyBvZiB0aGVzZSBvcGVyYXRpb25zCgoqKioqKioKKioqKioqCioqKioqKgoKIyMjIyBFeGVyY2lzZQoKLSBGaWx0ZXIgdGhlIGRhdGEgdG8gaW5jbHVkZSBqdXN0IG9ic2VydmF0aW9ucyBmcm9tIHRoZSB5ZWFyIDIwMDIKLSBSZS1hcnJhbmdlIHRoZSB0YWJsZSBzbyB0aGF0IHRoZSBjb3VudHJpZXMgZnJvbSBlYWNoIGNvbnRpbmVudCBhcmUgb3JkZXJlZCBhY2NvcmRpbmcgdG8gZGVjcmVhc2luZyB3ZWFsdGguIGkuZS4gdGhlIHdlYWx0aGllc3QgY291bnRyaWVzIGZpcnN0Ci0gUmVtb3ZlIHRoZSB5ZWFyIGNvbHVtbiBmcm9tIHRoZSByZXN1bHRpbmcgZGF0YSBmcmFtZQotIFdyaXRlIHRoZSBkYXRhIGZyYW1lIG91dCB0byBhIGZpbGUgaW4gYGRhdGEvYCBmb2xkZXIKCmBgYHtyIGVjaG89RkFMU0V9CmZpbHRlcihnYXBtaW5kZXIsIHllYXI9PTIwMDIpICU+JSAKICBhcnJhbmdlKGNvbnRpbmVudCwgZGVzYyhnZHBQZXJjYXApKSAlPiUgCiAgc2VsZWN0KC15ZWFyKQoKCmBgYAoKCioqKioqKgoqKioqKioKKioqKioqCgoKIyMjICJQaXBpbmciCgpXZSB3aWxsICoqb2Z0ZW4gbmVlZCB0byBwZXJmb3JtIGFuIGFuYWx5c2lzLCBvciBjbGVhbiBhIGRhdGFzZXQsIHVzaW5nIHNldmVyYWwgYGRwbHlyYCBmdW5jdGlvbnMgaW4gc2VxdWVuY2UqKi4gZS5nLiBmaWx0ZXJpbmcsIG11dGF0aW5nLCB0aGVuIHNlbGVjdGluZyBjb2x1bW5zIG9mIGludGVyZXN0IChwb3NzaWJseSBmb2xsb3dlZCBieSBwbG90dGluZyAtIHNlZSBsYXRlcikuCgpJZiB3ZSB3YW50ZWQgdG8gZmlsdGVyIG91ciByZXN1bHRzIHRvIGp1c3QgRXVyb3BlIGFuZCB0aGVuIGFsc28gcmVtb3ZlIHRoZSBub3cgc29tZXdoYXQgdW5uZWNlc3NhcnkgYGNvbnRpbmVudGAgY29sdW1uLgoKVGhlIGZvbGxvd2luZyBpcyBwZXJmZWN0bHkgdmFsaWQgUiBjb2RlLCBidXQgaW52aXRlcyB0aGUgdXNlciB0byBtYWtlIG1pc3Rha2VzIHdoZW4gd3JpdGluZyBpdC4gV2UgYWxzbyBoYXZlIHRvIGNyZWF0ZSBtdWx0aXBsZSBjb3BpZXMgb2YgdGhlIHNhbWUgZGF0YSBmcmFtZS4KCmBgYHtyfQp0bXAgPC0gZmlsdGVyKGdhcG1pbmRlciwgY29udGluZW50ID09ICJFdXJvcGUiKQp0bXAyIDwtIHNlbGVjdCh0bXAsIC1jb250aW5lbnQpCnRtcDIKYGBgCgpUaG9zZSBmYW1pbGlhciB3aXRoIFVuaXggbWF5IHJlY2FsbCB0aGF0IGNvbW1hbmRzIGNhbiBiZSBqb2luZWQgd2l0aCBhIHBpcGU7IGB8YAoKSW4gUiwgYGRwbHlyYCBjb21tYW5kcyB0byBiZSBsaW5rZWQgdG9nZXRoZXIgYW5kIGZvcm0gYSB3b3JrZmxvdy4gVGhlIHN5bWJvbCBgJT4lYCBpcyBwcm9ub3VuY2VkICoqdGhlbioqLiBXaXRoIGEgYCU+JSBgIHRoZSBpbnB1dCB0byBhIGZ1bmN0aW9uIGlzIGFzc3VtZWQgdG8gYmUgdGhlIG91dHB1dCBvZiB0aGUgcHJldmlvdXMgbGluZS4gQWxsIHRoZSBgZHBseXJgIGZ1bmN0aW9ucyB0aGF0IHdlIGhhdmUgc2VlbiBzbyBmYXIgdGFrZSBhIGRhdGEgZnJhbWUgYXMgYW4gaW5wdXQgYW5kIHJldHVybiBhbiBhbHRlcmVkIGRhdGEgZnJhbWUgYXMgYW4gb3V0cHV0LCBzbyBhcmUgYW1lYW5hYmxlIHRvIHRoaXMgdHlwZSBvZiBwcm9ncmFtbWluZy4KClRoZSBleGFtcGxlIHdlIGdhdmUgb2YgZmlsdGVyaW5nIGp1c3QgdGhlIEV1cm9wZWFuIGNvdW50cmllcyBhbmQgcmVtb3ZpbmcgdGhlIGBjb250aW5lbnRgIGNvbHVtbiBiZWNvbWVzOi0KCipub3RpY2UgdGhhdCBpbiB0aGUgYHNlbGVjdGAgc3RhdGVtZW50IHdlIGRvbid0IG5lZWQgdG8gc3BlY2lmeSB0aGUgbmFtZSBvZiB0aGUgZGF0YSBmcmFtZSoKCmBgYHtyfQpmaWx0ZXIoZ2FwbWluZGVyLCBjb250aW5lbnQ9PSJFdXJvcGUiKSAlPiUgCiAgc2VsZWN0KC1jb250aW5lbnQpCgpgYGAKCldlIGNhbiBqb2luIGFzIG1hbnkgYGRwbHlyYCBmdW5jdGlvbnMgYXMgd2UgcmVxdWlyZSBmb3IgdGhlIGFuYWx5c2lzLgoKYGBge3J9CmZpbHRlcihnYXBtaW5kZXIsIGNvbnRpbmVudD09IkV1cm9wZSIpICU+JSAKICBzZWxlY3QoLWNvbnRpbmVudCkgJT4lIAogIG11dGF0ZShsaWZlRXhwID0gcm91bmQobGlmZUV4cCkpICU+JSAKICBhcnJhbmdlKHllYXIsIGxpZmVFeHApICU+JSAKICBzZWxlY3QoY291bnRyeSwgeWVhcjpsaWZlRXhwKSAlPiUgCiAgd3JpdGVfY3N2KHBhdGggPSAiZGF0YS9ldXJvcGVfYnlfbGlmZUV4cC5jc3YiKQoKYGBgCgoKIyBQbG90dGluZwoKVGhlIFIgbGFuZ3VhZ2UgaGFzIGV4dGVuc2l2ZSBncmFwaGljYWwgY2FwYWJpbGl0aWVzLgoKR3JhcGhpY3MgaW4gUiBtYXkgYmUgY3JlYXRlZCBieSBtYW55IGRpZmZlcmVudCBtZXRob2RzIGluY2x1ZGluZyBiYXNlIGdyYXBoaWNzIGFuZCBtb3JlIGFkdmFuY2VkIHBsb3R0aW5nIHBhY2thZ2VzIHN1Y2ggYXMgbGF0dGljZS4KClRoZSBgZ2dwbG90MmAgcGFja2FnZSB3YXMgY3JlYXRlZCBieSBIYWRsZXkgV2lja2hhbSBhbmQgcHJvdmlkZXMgYSBpbnR1aXRpdmUgcGxvdHRpbmcgc3lzdGVtIHRvIHJhcGlkbHkgZ2VuZXJhdGUgcHVibGljYXRpb24gcXVhbGl0eSBncmFwaGljcy4KCmBnZ3Bsb3QyYCBidWlsZHMgb24gdGhlIGNvbmNlcHQgb2YgdGhlIOKAnEdyYW1tYXIgb2YgR3JhcGhpY3PigJ0gKFdpbGtpbnNvbiAyMDA1LCBCZXJ0aW4gMTk4Mykgd2hpY2ggZGVzY3JpYmVzIGEgY29uc2lzdGVudCBzeW50YXggZm9yIHRoZSBjb25zdHJ1Y3Rpb24gb2YgYSB3aWRlIHJhbmdlIG9mIGNvbXBsZXggZ3JhcGhpY3MgYnkgYSBjb25jaXNlIGRlc2NyaXB0aW9uIG9mIHRoZWlyIGNvbXBvbmVudHMuCgojIyBXaHkgdXNlIGdncGxvdDI/CgpUaGUgc3RydWN0dXJlZCBzeW50YXggYW5kIGhpZ2ggbGV2ZWwgb2YgYWJzdHJhY3Rpb24gdXNlZCBieSBnZ3Bsb3QyIHNob3VsZCBhbGxvdyBmb3IgdGhlIHVzZXIgdG8gY29uY2VudHJhdGUgb24gdGhlIHZpc3VhbGlzYXRpb25zIGluc3RlYWQgb2YgY3JlYXRpbmcgdGhlIHVuZGVybHlpbmcgY29kZS4KCk9uIHRvcCBvZiB0aGlzIGNlbnRyYWwgcGhpbG9zb3BoeSBnZ3Bsb3QyIGhhczoKCi0gSW5jcmVhc2VkIGZsZXhpYmlsaXR5IG92ZXIgbWFueSBwbG90dGluZyBzeXN0ZW1zLgotIEFuIGFkdmFuY2VkIHRoZW1lIHN5c3RlbSBmb3IgcHJvZmVzc2lvbmFsL3B1YmxpY2F0aW9uIGxldmVsIGdyYXBoaWNzLgotIExhcmdlIGRldmVsb3BlciBiYXNlIOKAkyBNYW55IGxpYnJhcmllcyBleHRlbmRpbmcgaXRzIGZsZXhpYmlsaXR5LgotIExhcmdlIHVzZXIgYmFzZSDigJMgR3JlYXQgZG9jdW1lbnRhdGlvbiBhbmQgYWN0aXZlIG1haWxpbmcgbGlzdC4KCgpJdCBpcyBhbHdheXMgdXNlZnVsIHRvIHRoaW5rIGFib3V0IHRoZSBtZXNzYWdlIHlvdSB3YW50IHRvIGNvbnZleSBhbmQgdGhlIGFwcHJvcHJpYXRlIHBsb3QgYmVmb3JlIHdyaXRpbmcgYW55IFIgY29kZS4gUmVzb3VyY2VzIGxpa2UgW3RoaXNdKGh0dHBzOi8vd3d3LmRhdGEtdG8tdml6LmNvbS8pIHNob3VsZCBoZWxwLgoKV2l0aCBzb21lIHByYWN0aWNlLCBgZ2dwbG90MmAgbWFrZXMgaXQgZWFzaWVyIHRvIGdvIGZyb20gdGhlIGZpZ3VyZSB5b3UgYXJlIGltYWdpbmluZyBpbiBvdXIgaGVhZCAob3Igb24gcGFwZXIpIHRvIGEgcHVibGljYXRpb24tcmVhZHkgaW1hZ2UgaW4gUi4KCkFzIHdpdGggYGRwbHlyYCwgd2Ugd29uJ3QgaGF2ZSB0aW1lIHRvIGNvdmVyIGFsbCBkZXRhaWxzIG9mIGBnZ3Bsb3QyYC4gVGhpcyBpcyBob3dldmVyIGEgdXNlZnVsIFtjaGVhdHNoZWV0XShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNS8wMy9nZ3Bsb3QyLWNoZWF0c2hlZXQucGRmKSB0aGF0IGNhbiBiZSBwcmludGVkIGFzIGEgcmVmZXJlbmNlLgoKIyMgQmFzaWMgcGxvdCB0eXBlcwoKQSBwbG90IGluIGBnZ3Bsb3QyYCBpcyBjcmVhdGVkIHdpdGggdGhlIGZvbGxvd2luZyB0eXBlIG9mIGNvbW1hbmQKCmBgYApnZ3Bsb3QoZGF0YSA9IDxEQVRBPiwgbWFwcGluZyA9IGFlcyg8TUFQUElOR1M+KSkgKyAgPEdFT01fRlVOQ1RJT04+KCkKYGBgCgpTbyB3ZSBuZWVkIHRvIHNwZWNpZnkKCi0gVGhlIGRhdGEgdG8gYmUgdXNlZCBpbiBncmFwaAotIE1hcHBpbmdzIG9mIGRhdGEgdG8gdGhlIGdyYXBoICgqYWVzdGhldGljKiBtYXBwaW5nKQotIFdoYXQgdHlwZSBvZiBncmFwaCB3ZSB3YW50IHRvIHVzZSAoVGhlICpnZW9tKiB0byB1c2UpLgoKTGV0cyBzYXkgdGhhdCB3ZSB3YW50IHRvIGV4cGxvcmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIEdEUCBhbmQgTGlmZSBFeHBlY3RhbmN5LiBXZSBtaWdodCBzdGFydCB3aXRoIHRoZSBoeXBvdGhlc2lzIHRoYXQgcmljaGVyIGNvdW50cmllcyBoYXZlIGhpZ2hlciBsaWZlIGV4cGVjdGFuY3kuIEEgc2Vuc2libGUgY2hvaWNlIG9mIHBsb3Qgd291bGQgYmUgYSAqc2NhdHRlciBwbG90KiB3aXRoIGdkcCBvbiB0aGUgeC1heGlzIGFuZCBsaWZlIGV4cGVjdGFuY3kgb24gdGhlIHktYXhpcy4KClRoZSBmaXJzdCBzdGFnZSBpcyB0byBzcGVjaWZ5IG91ciBkYXRhc2V0CgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlcikKYGBgCgpGb3IgdGhlIGFlc3RoZXRpY3MsIGFzIGEgYmFyZSBtaW5pbXVtIHdlIHdpbGwgbWFwIHRoZSBgZ2RwUGVyY2FwYCBhbmQgYGxpZmVFeHBgIHRvIHRoZSB4LSBhbmQgeS1heGlzIG9mIHRoZSBwbG90LiBTb21lIHByb2dyZXNzIGlzIG1hZGU7IHdlIGF0IGxlYXN0IGdldCBheGVzCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXIsYWVzKHg9Z2RwUGVyY2FwLCB5PWxpZmVFeHApKQpgYGAKClRoYXQgY3JlYXRlZCB0aGUgYXhlcywgYnV0IHdlIHN0aWxsIG5lZWQgdG8gZGVmaW5lIGhvdyB0byBkaXNwbGF5IG91ciBwb2ludHMgb24gdGhlIHBsb3QuIEFzIHdlIGhhdmUgY29udGludW91cyBkYXRhIGZvciBib3RoIHRoZSB4LSBhbmQgeS1heGlzLCBgZ2VvbV9wb2ludGAgaXMgYSBnb29kIGNob2ljZS4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlcixhZXMoeD1nZHBQZXJjYXAsIHk9bGlmZUV4cCkpICsgZ2VvbV9wb2ludCgpCmBgYAoKCgoKVGhlICpnZW9tKiB3ZSB1c2Ugd2lsbCBkZXBlbmQgb24gd2hhdCBraW5kIG9mIGRhdGEgd2UgaGF2ZSAoY29udGludW91cywgY2F0ZWdvcmljYWwgZXRjKQoKLSBgZ2VvbV9wb2ludCgpYCAtIFNjYXR0ZXIgcGxvdHMKLSBgZ2VvbV9saW5lKClgIC0gTGluZSBwbG90cwotIGBnZW9tX3Ntb290aCgpYCAtIEZpdHRlZCBsaW5lIHBsb3RzCi0gYGdlb21fYmFyKClgIC0gQmFyIHBsb3RzCi0gYGdlb21fYm94cGxvdCgpYCAtIEJveHBsb3RzCi0gYGdlb21faml0dGVyKClgIC0gSml0dGVyIHRvIHBsb3RzCi0gYGdlb21faGlzdG9ncmFtKClgIC0gSGlzdG9ncmFtIHBsb3RzCi0gYGdlb21fZGVuc2l0eSgpYCAtIERlbnNpdHkgcGxvdHMKLSBgZ2VvbV90ZXh0KClgIC0gVGV4dCB0byBwbG90cwotIGBnZW9tX2Vycm9yYmFyKClgIC0gRXJyb3JiYXJzIHRvIHBsb3RzCi0gYGdlb21fdmlvbGluKClgIC0gVmlvbGluIHBsb3RzCi0gYGdlb21fdGlsZSgpYCAtIGZvciAiaGVhdG1hcCItbGlrZSBwbG90cwoKCkJveHBsb3RzIGFyZSBjb21tb25seSB1c2VkIHRvIHZpc3VhbGlzZSB0aGUgZGlzdHJpYnV0aW9ucyBvZiBjb250aW51b3VzIGRhdGEuIFdlIGhhdmUgdG8gdXNlIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgb24gdGhlIHgtYXhpcy4gSW4gdGhlIGNhc2Ugb2YgdGhlIGBnYXBtaW5kZXJgIGRhdGEgd2UgbWlnaHQgaGF2ZSB0byBwZXJzdWFkZSBgZ2dwbG90MmAgdGhhdCB0aGUgYHllYXJgIGNvbHVtbiBpcyBhIGBmYWN0b3JgIHJhdGhlciB0aGFuIG51bWVyaWNhbCBkYXRhLgoKYGBge3J9CmdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gYXMuZmFjdG9yKHllYXIpLCB5PWdkcFBlcmNhcCkpICsgZ2VvbV9ib3hwbG90KCkKYGBgCgoKYGBge3J9CmdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gZ2RwUGVyY2FwKSkgKyBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKQ291bnRzIHdpdGggYSBiYXJwbG90CgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHg9Y29udGluZW50KSkgKyBnZW9tX2JhcigpCmBgYAoKVGhlIGhlaWdodCBvZiB0aGUgYmFycyBjYW4gYWxzbyBiZSBtYXBwZWQgZGlyZWN0bHkgdG8gbnVtZXJpYyB2YXJpYWJsZXMgaW4gdGhlIGRhdGEgZnJhbWUgaWYgdGhlIGBzdGF0PSJpZGVudGl0eSJgIGFyZ3VtZW50IGlzIHNldCB3aXRoaW4gYGdlb21fYmFyYC4gTm90ZSB0aGF0IHRoZSBheGlzIGxhYmVscyBjYW4gYmUgbW9kaWZpZWQsIGFzIHdlIHdpbGwgc2VlIGxhdGVyIG9uLgoKYGBge3J9CmdhcG1pbmRlcjIwMDIgPC0gZmlsdGVyKGdhcG1pbmRlciwgeWVhcj09MjAwMixjb250aW5lbnQ9PSJBbWVyaWNhcyIpCmdncGxvdChnYXBtaW5kZXIyMDAyLCBhZXMoeD1jb3VudHJ5LHk9Z2RwUGVyY2FwKSkgKyBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpCmBgYAoKV2hlcmUgYXBwcm9wcmlhdGUsIHdlIGNhbiBhZGQgbXVsdGlwbGUgbGF5ZXJzIG9mIGBnZW9tYHMgdG8gdGhlIHBsb3QuIEZvciBpbnN0YW5jZSwgYSBjcml0aWNpc20gb2YgdGhlIGJveHBsb3QgaXMgdGhhdCBpdCBkb2VzIG5vdCBzaG93IGFsbCB0aGUgZGF0YS4gV2UgY2FuIHJlY3RpZnkgdGhpcyBieSBvdmVybGF5aW5nIHRoZSBpbmRpdmlkdWFsIHBvaW50cy4KCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGFzLmZhY3Rvcih5ZWFyKSwgeT1nZHBQZXJjYXApKSArIGdlb21fYm94cGxvdCgpICsgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3J9CmdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gYXMuZmFjdG9yKHllYXIpLCB5PWdkcFBlcmNhcCkpICsgZ2VvbV9ib3hwbG90KCkgKyBnZW9tX2ppdHRlcih3aWR0aD0wLjEpCmBgYAoKCioqKioqKgoqKioqKioKKioqKioqCgojIyMgRXhlcmNpc2VzCgoKLSBUaGUgdmlvbGluIHBsb3QgaXMgYSBwb3B1bGFyIGFsdGVybmF0aXZlIHRvIHRoZSBib3hwbG90LiBDcmVhdGUgYSB2aW9saW4gcGxvdCB3aXRoIGBnZW9tX3Zpb2xpbmAgdG8gdmlzdWFsaXNlIHRoZSBpbmNyZWFzZSBpbiBHRFAgb3ZlciB0aW1lLgotIENyZWF0ZSBhIHN1YnNldCBvZiB0aGUgYGdhcG1pbmRlcmAgZGF0YSBmcmFtZSBjb250YWluaW5nIGp1c3QgdGhlIHJvd3MgZm9yIHlvdXIgY291bnRyeSBvZiBiaXJ0aAotIEhhcyB0aGVyZSBiZWVuIGFuIGluY3JlYXNlIGluIGxpZmUgZXhwZWN0YW5jeSBvdmVyIHRpbWU/CiAgICArIHZpc3VhbGlzZSB0aGUgdHJlbmQgdXNpbmcgYSBzY2F0dGVyIHBsb3QgKGBnZW9tX3BvaW50YCksIGxpbmUgZ3JhcGggKGBnZW9tX2xpbmVgKSBvciBzbW9vdGhlZCBsaW5lIChgZ2VvbV9zbW9vdGhgKS4KCgoqKioqKioKKioqKioqCioqKioqKgoKCiMjIyAqKlNvbHV0aW9ucyoqCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBhcy5mYWN0b3IoeWVhciksIHkgPSBnZHBQZXJjYXApKSArIGdlb21fdmlvbGluKCkKCmBgYAoKYGBge3J9CmZpbHRlcihnYXBtaW5kZXIsIGNvdW50cnk9PSJVbml0ZWQgS2luZ2RvbSIpICU+JSAKICAgICAgZ2dwbG90KGFlcyh4ID0geWVhciwgeT0gbGlmZUV4cCkpICsgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3J9CiMjIFdlIGNhbiB0byBrZWVwIGZhY3RvciBhcyBhIG51bWVyaWMgdmFsdWUgKGkuZS4gbm90IGNvbnZlcnQgdG8gYSBmYWN0b3IpIHRvIG1ha2UgdGhlIHBsb3QKCmZpbHRlcihnYXBtaW5kZXIsIGNvdW50cnk9PSJVbml0ZWQgS2luZ2RvbSIpICU+JSAKICAgICAgZ2dwbG90KGFlcyh4ID0geWVhciwgeT0gbGlmZUV4cCkpICsgZ2VvbV9saW5lKCkKYGBgCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgY291bnRyeT09IlVuaXRlZCBLaW5nZG9tIikgJT4lIAogICAgICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5PSBsaWZlRXhwKSkgKyBnZW9tX3Ntb290aCgpCmBgYAoKFQpBcyB3ZSBoYXZlIHNlZW4gYWxyZWFkeSwgYGdncGxvdGAgb2ZmZXJzIGFuIGludGVyZmFjZSB0byBjcmVhdGUgbWFueSBwb3B1bGFyIHBsb3QgdHlwZXMuIEl0IGlzIHVwIHRvIHRoZSB1c2VyIHRvIGRlY2lkZSB3aGF0IHRoZSBiZXN0IHdheSB0byB2aXN1YWxpc2UgdGhlIGRhdGEuCgpJdCBpcyBhbHNvIHVwIHRvIHRoZSB1c2VyIGhvdyB0byBpbnRlcnByZXQgdGhlIGRhdGEuIENvbnNpZGVyIHRoZSBmb2xsb3dpbmcgcGxvdCBhbmQgd2hhdCBtZXNzYWdlIGl0IG1pZ2h0IGJlIGNvbnZleWluZy4gCgohW10oaW1hZ2VzL2dvb2RfY29ycmVsYXRpb24ucG5nKQoKSG93ZXZlciwgd2hlbiBjb25zaWRlcmluZyBbdGhlIHNvdXJjZSBvZiB0aGUgcGxvdF0oaHR0cDovL3d3dy50eWxlcnZpZ2VuLmNvbS9zcHVyaW91cy1jb3JyZWxhdGlvbnMpIHlvdXIgaW50ZXJwcmV0YXRpb24gbWlnaHQgY2hhbmdlLgoKIyMgQ3VzdG9taXNpbmcgdGhlIHBsb3QgYXBwZWFyYW5jZQoKT3VyIHBsb3RzIGFyZSBhIGJpdCBkcmVhcnkgYXQgdGhlIG1vbWVudCwgYnV0IG9uZSB3YXkgdG8gYWRkIGNvbG91ciBpcyB0byBhZGQgYSBgY29sYCBhcmd1bWVudCB0byB0aGUgYGdlb21fcG9pbnRgIGZ1bmN0aW9uLiBUaGUgdmFsdWUgY2FuIGJlIGFueSBvZiB0aGUgcHJlLWRlZmluZWQgY29sb3VyIG5hbWVzIGluIFIuIFRoZXNlIGFyZSBkaXNwbGF5ZWQgaW4gdGhpcyBbaGFuZHkgb25saW5lIHJlZmVyZW5jZV0oaHR0cDovL3d3dy5zdGF0LmNvbHVtYmlhLmVkdS9+dHpoZW5nL2ZpbGVzL1Jjb2xvci5wZGYpLiAqUiplZCwgKkcqcmVlbiwgKkIqbHVlIG9mICpIZXgqIHZhbHVlcyBjYW4gYWxzbyBiZSBnaXZlbi4KCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgeT1saWZlRXhwKSkgKyBnZW9tX3BvaW50KGNvbD0icmVkIikKYGBgCgpIb3dldmVyLCBhIHBvd2VyZnVsIGZlYXR1cmUgb2YgYGdncGxvdDJgIGlzIHRoYXQgY29sb3VycyBhcmUgdHJlYXRlZCBhcyBhZXN0aGV0aWNzIG9mIHRoZSBwbG90LiBJbiBvdGhlciB3b3JkcyB3ZSBjYW4gdXNlIGNvbHVtbiBpbiBvdXIgZGF0YXNldC4KCkxldCdzIHNheSB0aGF0IHdlIHdhbnQgcG9pbnRzIG9uIG91ciBwbG90IHRvIGJlIGNvbG91cmVkIGFjY29yZGluZyB0byBjb250aW5lbnQuIFdlIGFkZCBhbiBleHRyYSBhcmd1bWVudCB0byB0aGUgZGVmaW5pdGlvbiBvZiBhZXN0aGV0aWNzIHRvIGRlZmluZSB0aGUgbWFwcGluZy4gYGdncGxvdDJgIHdpbGwgZXZlbiBkZWNpZGUgb24gY29sb3VycyBhbmQgY3JlYXRlIGEgbGVnZW5kIGZvciB1cy4KCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgeT1saWZlRXhwLGNvbD1jb250aW5lbnQpKSArIGdlb21fcG9pbnQoKQpgYGAKCgoKPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtd2FybmluZyI+CgoqKlF1ZXN0aW9uOiBDYW4geW91IGV4cGxhaW4gd2h5IHRoZSBjb2xvdXIgc2NoZW1lIHVzZWQgb24gdGhlIGZvbGxvd2luZyB0d28gcGxvdHMgYXJlIGRpZmZlcmVudCoqCgo8L2Rpdj4KCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgeT1saWZlRXhwLGNvbD15ZWFyKSkgKyBnZW9tX3BvaW50KCkKZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBnZHBQZXJjYXAsIHk9bGlmZUV4cCxjb2w9YXMuZmFjdG9yKHllYXIpKSkgKyBnZW9tX3BvaW50KCkKYGBgCgpTaGFwZSBhbmQgc2l6ZSBvZiBwb2ludHMgY2FuIGFsc28gYmUgbWFwcGVkIGZyb20gdGhlIGRhdGEuIEhvd2V2ZXIsIGl0IGlzIGVhc3kgdG8gZ2V0IGNhcnJpZWQgYXdheS4KCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgeT1saWZlRXhwLHNoYXBlPWNvbnRpbmVudCxzaXplPXBvcCxjb2w9YXMuZmFjdG9yKHllYXIpKSkgKyBnZW9tX3BvaW50KCkKYGBgCgpTY2FsZXMgYW5kIHRoZWlyIGxlZ2VuZHMgaGF2ZSBzbyBmYXIgYmVlbiBoYW5kbGVkIHVzaW5nIGdncGxvdDIgZGVmYXVsdHMuIGdncGxvdDIgb2ZmZXJzIGZ1bmN0aW9uYWxpdHkgdG8gaGF2ZSBmaW5lciBjb250cm9sIG92ZXIgc2NhbGVzIGFuZCBsZWdlbmRzIHVzaW5nIHRoZSBzY2FsZSBtZXRob2RzLgoKU2NhbGUgbWV0aG9kcyBhcmUgZGl2aWRlZCBpbnRvIGZ1bmN0aW9ucyBieSBjb21iaW5hdGlvbnMgb2YKCi0gdGhlIGFlc3RoZXRpY3MgdGhleSBjb250cm9sLgoKLSB0aGUgdHlwZSBvZiBkYXRhIG1hcHBlZCB0byBzY2FsZS4KCmBzY2FsZV9gKmFlc3RoZXRpYypfKnR5cGUqCgpUcnkgdHlwaW5nIGluIGBzY2FsZV9gIHRoZW4gdGFiIHRvIGF1dG9jb21wbGV0ZS4gVGhpcyB3aWxsIHByb3ZpZGUgc29tZSBleGFtcGxlcyBvZiB0aGUgc2NhbGUgZnVuY3Rpb25zIGF2YWlsYWJsZSBpbiBgZ2dwbG90MmAuCgpBbHRob3VnaCBkaWZmZXJlbnQgc2NhbGUgZnVuY3Rpb25zIGFjY2VwdCBzb21lIHZhcmlldHkgaW4gdGhlaXIgYXJndW1lbnRzLCBjb21tb24gYXJndW1lbnRzIHRvIHNjYWxlIGZ1bmN0aW9ucyBpbmNsdWRlIC0KCi0gbmFtZSAtIFRoZSBheGlzIG9yIGxlZ2VuZCB0aXRsZQoKLSBsaW1pdHMgLSBNaW5pbXVtIGFuZCBtYXhpbXVtIG9mIHRoZSBzY2FsZQoKLSBicmVha3MgLSBMYWJlbC90aWNrIHBvc2l0aW9ucyBhbG9uZyBhbiBheGlzCgotIGxhYmVscyAtIExhYmVsIG5hbWVzIGF0IGVhY2ggYnJlYWsKCi0gdmFsdWVzIC0gdGhlIHNldCBvZiBhZXN0aGV0aWMgdmFsdWVzIHRvIG1hcCBkYXRhIHZhbHVlcwoKV2UgY2FuIGNob29zZSBzcGVjaWZpYyBjb2xvdXIgcGFsZXR0ZXMsIHN1Y2ggYXMgdGhvc2UgcHJvdmlkZWQgYnkgdGhlIGBSQ29sb3JCcmV3ZXJgIHBhY2thZ2UuIFRoaXMgcGFja2FnZSBwcm92aWRlcyBwYWxldHRlcyBmb3IgZGlmZmVyZW50IHR5cGVzIG9mIHNjYWxlIChzZXF1ZW50aWFsLCBkaXZlcmdpbmcsIHF1YWxpdGF0aXZlKS4KCmBgYHtyfQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKZGlzcGxheS5icmV3ZXIuYWxsKCkKYGBgCgpXaGVuIGV4cGVyaW1lbnRpbmcgd2l0aCBjb2xvdXIgcGFsZXR0ZXMgYW5kIGxhYmVscywgaXQgaXMgdXNlZnVsIHRvIHNhdmUgdGhlIHBsb3QgYXMgYW4gb2JqZWN0CmBgYHtyfQpwIDwtIGdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gZ2RwUGVyY2FwLCB5PWxpZmVFeHAsY29sPWNvbnRpbmVudCkpICsgZ2VvbV9wb2ludCgpCmBgYAoKCmBgYHtyfQpwICsgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpCmBgYAoKT3Igd2UgY2FuIGV2ZW4gc3BlY2lmeSBvdXIgb3duIGNvbG91cnM7IHN1Y2ggYXMgVGhlIFVuaXZlcnNpdHkgb2YgU2hlZmZpZWxkIGJyYW5kaW5nIGNvbG91cnMKCmBgYHtyfQpteV9wYWwgPC0gYyhyZ2IoMCwxNTksMjE4LG1heENvbG9yVmFsdWUgPSAyNTUpLAogICAgICAgICAgICByZ2IoMzEsMjAsOTMsbWF4Q29sb3JWYWx1ZSA9IDI1NSksCiAgICAgICAgICAgIHJnYigyNDksMjI3LDAsbWF4Q29sb3JWYWx1ZSA9IDI1NSksCiAgICAgICAgICAgIHJnYigwLDE1NSw3MixtYXhDb2xvclZhbHVlID0gMjU1KSwKICAgICAgICAgICAgcmdiKDE5MCwyMTQsMCxtYXhDb2xvclZhbHVlID0gMjU1KSkKcCArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9bXlfcGFsKQoKYGBgCgoKClZhcmlvdXMgbGFiZWxzIGNhbiBiZSBtb2RpZmllZCB1c2luZyB0aGUgYGxhYnNgIGZ1bmN0aW9uLgoKYGBge3J9CnAgKyBsYWJzKHg9IldlYWx0aCIseT0iTGlmZSBFeHBlY3RhbmN5Iix0aXRsZT0iUmVsYXRpb25zaGlwIGJldHdlZW4gV2VhbHRoIGFuZCBMaWZlIEV4cGVjdGFuY3kiKQpgYGAKCldlIGNhbiBhbHNvIG1vZGlmeSB0aGUgeC0gYW5kIHktIGxpbWl0cyBvZiB0aGUgcGxvdCBzbyB0aGF0IGFueSBvdXRsaWVycyBhcmUgbm90IHNob3duLiBgZ2dwbG90MmAgd2lsbCBnaXZlIGEgd2FybmluZyB0aGF0IHNvbWUgcG9pbnRzIGFyZSBleGNsdWRlZC4KCmBgYHtyfQpwICsgeGxpbSgwLDYwMDAwKQpgYGAKClNhdmluZyBpcyBzdXBwb3J0ZWQgYnkgdGhlIGBnZ3NhdmVgIGZ1bmN0aW9uLgoKYGBge3J9CgpnZ3NhdmUocCwgZmlsZT0ibXlfZ2dwbG90LnBuZyIpCmBgYAoKTW9zdCBhc3BlY3RzIG9mIHRoZSBwbG90IGNhbiBiZSBtb2RpZmllZCBmcm9tIHRoZSBiYWNrZ3JvdW5kIGNvbG91ciB0byB0aGUgZ3JpZCBzaXplcyBhbmQgZm9udC4gU2V2ZXJhbCBwcmUtZGVmaW5lZCAidGhlbWVzIiBleGlzdCBhbmQgd2UgY2FuIG1vZGlmeSB0aGUgYXBwZWFyYW5jZSBvZiB0aGUgd2hvbGUgcGxvdCB1c2luZyBhIGB0aGVtZV8uLmAgZnVuY3Rpb24uCgpgYGB7cn0KcCArIHRoZW1lX2J3KCkKYGBgCgpNb3JlIHRoZW1lcyBhcmUgc3VwcG9ydGVkIGJ5IHRoZSBgZ2d0aGVtZXNgIHBhY2thZ2UuIFlvdSBjYW4gbWFrZSB5b3VyIHBsb3RzIGxvb2sgbGlrZSB0aGUgRWNvbm9taXN0LCBXYWxsIFN0cmVldCBKb3VybmFsIG9yIEV4Y2VsICgqKmJ1dCBwbGVhc2UgZG9uJ3QgZG8gdGhpcyEqKikKCiMjIEZhY2V0cwoKT25lIHZlcnkgdXNlZnVsIGZlYXR1cmUgb2YgZ2dwbG90IGlzIGZhY2V0aW5nLiBUaGlzIGFsbG93cyB5b3UgdG8gcHJvZHVjZSBwbG90cyBzdWJzZXQgYnkgdmFyaWFibGVzIGluIHlvdXIgZGF0YS4gSW4gdGhlIHNjYXR0ZXIgcGxvdCBhYm92ZSwgaXQgd2FzIHF1aXRlIGRpZmZpY3VsdCB0byBzZWUgaWYgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGdkcCBhbmQgbGlmZSBleHBlY3RhbmN5IHdhcyB0aGUgc2FtZSBmb3IgZWFjaCBjb250aW5lbnQuIFRvIG92ZXJjb21lIHRoaXMsIHdlIHdvdWxkIGxpa2UgYSBzZWUgYSBzZXBhcmF0ZSBwbG90IGZvciBlYWNoIGNvbnRpbmVudC4KClRvIGZhY2V0IG91ciBkYXRhIGludG8gbXVsdGlwbGUgcGxvdHMgd2UgY2FuIHVzZSB0aGUgYGZhY2V0X3dyYXBgIG9yIGBmYWNldF9ncmlkYCBmdW5jdGlvbiBhbmQgc3BlY2lmeSB0aGUgdmFyaWFibGUgd2Ugc3BsaXQgYnkuIAoKYGBge3J9CnAgKyBmYWNldF93cmFwKH5jb250aW5lbnQpCgpgYGAKClRoZSBgZmFjZXRfZ3JpZGAgZnVuY3Rpb24gd2lsbCBjcmVhdGUgYSBncmlkLWxpa2UgcGxvdCB3aXRoIG9uZSB2YXJpYWJsZSBvbiB0aGUgeC1heGlzIGFuZCBhbm90aGVyIG9uIHRoZSB5LWF4aXMuCgpgYGB7ciBmaWcud2lkdGg9MTJ9CnAgKyBmYWNldF9ncmlkKGNvbnRpbmVudH55ZWFyKQpgYGAKCgpUaGUgcHJldmlvdXMgcGxvdCB3YXMgYSBiaXQgbWVzc3kgYXMgaXQgY29udGFpbmVkIGFsbCBjb21iaW5hdGlvbnMgb2YgeWVhciBhbmQgY29udGluZW50LiBMZXQncyBzdXBwb3NlIHdlIHdhbnQgb3VyIGFuYWx5c2lzIHRvIGJlIGEgYml0IG1vcmUgZm9jdXNzZWQgYW5kIGRpc3JlZ2FyZCBjb3VudHJpZXMgaW4gT2NlYW5pYSAoYXMgdGhlcmUgYXJlIG9ubHkgMiBpbiBvdXIgZGF0YXNldCkgYW5kIHllYXJzIGJldHdlZW4gMTk5NyBhbmQgMjAwMi4gV2Ugc2hvdWxkIGtub3cgaG93IHRvIHJlc3RyaWN0IHRoZSByb3dzIGZyb20gdGhlIGBnYXBtaW5kZXJgIGRhdGFzZXQgdXNpbmcgdGhlIGBmaWx0ZXJgIGZ1bmN0aW9uLiBJbnN0ZWFkIG9mIGZpbHRlcmluZyB0aGUgZGF0YSwgY3JlYXRpbmcgYSBuZXcgZGF0YSBmcmFtZSBhbmQgY29uc3RydWNpbmcgdGhlIGRhdGEgZnJhbWUgZnJvbSB0aGVzZSBuZXcgZGF0YSB3ZSBjYW4gdXNlIHRoZWAgJT4lYCBvcGVyYXRvciB0byBjcmVhdGUgdGhlIGRhdGEgZnJhbWUgb24gdGhlIGZseSBhbmQgcGFzcyBkaXJlY3RseSB0byBgZ2dwbG90YC4gVGh1cyB3ZSBkb24ndCBoYXZlIHRvIHNhdmUgYSBuZXcgZGF0YSBmcmFtZSBvciBhbHRlciB0aGUgb3JpZ2luYWwgZGF0YS4KCgpgYGB7ciBmaWcud2lkdGg9MTJ9CmZpbHRlcihnYXBtaW5kZXIsIGNvbnRpbmVudCE9Ik9jZWFuaWEiLCB5ZWFyICVpbiUgYygxOTk3LDIwMDIsMjAwNykpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBnZHBQZXJjYXAsIHk9bGlmZUV4cCxjb2w9Y29udGluZW50KSkgKyBnZW9tX3BvaW50KCkgKyBmYWNldF9ncmlkKGNvbnRpbmVudH55ZWFyKQpgYGAKCiMjIEFkZGluZyB0ZXh0IHRvIGEgcGxvdAoKQW5ub3RhdGlvbnMgY2FuIGJlIGFkZGVkIHRvIGEgcGxvdCB1c2luZyB0aGUgZmxleGlibGUgYGFubm90YXRlYCBmdW5jdGlvbiBkb2N1bWVudGVkIFtoZXJlXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvYW5ub3RhdGUuaHRtbCkuIFRoaXMgcHJlc3VtZXMgdGhhdCB5b3Uga25vdyB0aGUgY29vcmRpbmF0ZXMgdGhhdCB5b3Ugd2FudCB0byBhZGQgdGhlIGFubm90YXRpb25zIGF0LgoKYGBge3J9CnA8LSBnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHAsY29sPWNvbnRpbmVudCkpICsgZ2VvbV9wb2ludCgpCnAgKyBhbm5vdGF0ZSgidGV4dCIsIHggPSA5MDAwMCx5PTYwLCBsYWJlbD0iU29tZSB0ZXh0IikKCgpgYGAKCkhpZ2hsaWdodGluZyBwYXJ0aWN1bGFyIHBvaW5zIG9mIGludGVyZXN0IHVzaW5nIGEgcmVjdGFuZ2xlLgoKYGBge3J9CnAgKyBhbm5vdGF0ZSgicmVjdCIsIHhtaW49MjUwMDAsIHhtYXg9MTIwMDAwLHltaW49NTAseW1heD03NSxhbHBoYT0wLjIpCmBgYAoKCldlIGNhbiBhbHNvIG1hcCBkaXJlY3RseSBmcm9tIGEgY29sdW1uIGluIG91ciBkYXRhc2V0IHRvIHRoZSBgbGFiZWxgIGFlc3RoZXRpYy4gSG93ZXZlciwgdGhpcyB3aWxsIGxhYmVsIGFsbCB0aGUgcG9pbnRzIHdoaWNoIGlzIHJhdGhlciBjbHV0dGVyZWQgaW4gb3VyIGNhc2UKCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHAsY29sPWNvbnRpbmVudCxsYWJlbD1jb3VudHJ5KSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3RleHQoKQpgYGAKCkluc3RlYWQsIHdlIGNvdWxkIHVzZSBhIGRpZmZlcmVudCBkYXRhc2V0IHdoZW4gd2UgY3JlYXRlIHRoZSB0ZXh0IGxhYmVscyB3aXRoIGBnZW9tX3RleHRgLiBIZXJlIHdlIGZpbHRlciB0aGUgYGdhcG1pbmRlcmAgZGF0YXNldCB0byBvbmx5IGNvdW50cmllcyB3aXRoIGBnZHBQZXJjYXBgIGdyZWF0ZXIgdGhhbiBgNTcwMDBgIGFuZCBvbmx5IHRoZXNlIHBvaW50cyBnZXQgbGFiZWxsZWQuIFdlIGNhbiBhbHNvIHNldCB0aGUgdGV4dCBjb2xvdXJzIHRvIGEgcGFydGljdWxhciB2YWx1ZSByYXRoZXIgdGhhbiB1c2luZyB0aGUgb3JpZ2luYWwgY29sb3VyIG1hcHBpbmdzIGZvciB0aGUgcGxvdCAoYmFzZWQgb24gY29udGluZW50KS4KCmBgYHtyfQpwICsgZ2VvbV90ZXh0KGRhdGEgPSBmaWx0ZXIoZ2FwbWluZGVyLCBnZHBQZXJjYXAgPiA1NzAwMCksIAogICAgICAgICAgICAgIGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCxsYWJlbD1jb3VudHJ5KSxjb2w9ImJsYWNrIikKYGBgCgpgYGB7cn0KcCArIGdlb21fdGV4dChkYXRhID0gZmlsdGVyKGdhcG1pbmRlciwgZ2RwUGVyY2FwID4gMjUwMDAsIGxpZmVFeHAgPCA3NSksIAogICAgICAgICAgICAgIGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCxsYWJlbD1jb3VudHJ5KSxjb2w9ImJsYWNrIixzaXplPTMpICsgYW5ub3RhdGUoInJlY3QiLCB4bWluPTI1MDAwLCB4bWF4PTEyMDAwMCx5bWluPTUwLHltYXg9NzUsYWxwaGE9MC4yKQpgYGAKCgojIyBDb21tZW50IGFib3V0IHRoZSBheGlzIHNjYWxlCgpUaGUgcGxvdCBvZiBgZ2RwUGVyY2FwYCB2cyBgbGlmZUV4cGAgb24gdGhlIG9yaWdpbmFsIHNjYWxlIHNlZW1zIHRvIGJlIGluZmx1ZW5jZWQgYnkgdGhlIG91dGxpZXIgb2JzZXJ2YXRpb25zICh3aGljaCB3ZSBub3cga25vdyBhcmUgb2JzZXJ2YXRpb25zIGZyb20gYEt1d2FpdGApLiBJbiBzdWNoIHNpdHVhdGlvbnMgaXQgbWF5IGJlIHBvc3NpYmxlIHRvIHRyYW5zZm9ybSB0aGUgc2NhbGUgb2Ygb25lIGF4aXMgZm9yIHZpc3VhbGlzYXRpb24gcHVycG9zZXMuIE9uZSBzdWNoIHRyYW5zZm9ybWF0aW9uIGlzIGBsb2cxMGAsIHdoaWNoIHdlIGNhbiBhcHBseSB3aXRoIHRoZSBgc2NhbGVfeF9sb2cxMGAgZnVuY3Rpb24uIE90aGVycyBpbmNsdWRlIGBzY2FsZV94X2xvZzJgLCBgc2NhbGVfeF9zcXJ0YCBhbmQgZXF1aXZhbGVudHMgZm9yIHRoZSB5IGF4aXMuCgpgYGB7cn0KcCArIHNjYWxlX3hfbG9nMTAoKQpgYGAKCkJ5IHNwbGl0dGluZyB0aGUgcGxvdCBieSBjb250aW5lbnRzIHdlIHNlZSBtb3JlIGNsZWFybHkgd2hpY2ggY29udGluZW50cyBoYXZlIGEgbW9yZSBsaW5lYXIgcmVsYXRpb25zaGlwLiBBdCB0aGUgbW9tZW50IHRoaXMgaXMgdXNlZnVsIGZvciB2aXN1YWxpc2F0aW9uIHB1cnBvc2VzLCBpZiB3ZSB3YW50ZWQgdG8gb2J0YWluIHN1bW1hcmllcyBmcm9tIHRoZSBkYXRhIHdlIHdvdWxkIG5lZWQgdGhlIHRlY2huaXF1ZXMgaW4gdGhlIG5leHQgc2VjdGlvbi4KCmBgYHtyfQpwICsgc2NhbGVfeF9sb2cxMCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsY29sPSJibGFjayIpICsgZmFjZXRfd3JhcCh+Y29udGluZW50KQpgYGAKCioqKioqKgoqKioqKioKKioqKioqCgojIyMgRXhlcmNpc2UKCi0gSW4gYSBwcmV2aW91cyBleGVyY2lzZSB3ZSBmaWx0ZXJlZCB0aGUgZ2FwbWluZGVyIGRhdGEgdG8gYSBwYXJ0aWN1bGFyIGNvdW50cnkgb2YgaW50ZXJlc3QsIGFuZCB0aGVuIHBsb3R0ZWQgdGhlIHRyZW5kIGluIGxpZmUgZXhwZWN0YW5jeSBvdmVyIHRpbWUKLSBSZXBlYXQgdGhpcyBwbG90LCBidXQgc2VsZWN0aW5nIHRocmVlIGNvdW50cmllcyBvZiBpbnRlcmVzdCBhbmQgdXNpbmcgcGlwaW5nIGAlPiVgIHRvIGF2b2lkIGNyZWF0aW5nIGFuIGludGVybWVkaWF0ZSBkYXRhIGZyYW1lCi0gVXNlIGEgKmZhY2V0KiB0byBzcGxpdCBpbnRvIHNlcGFyYXRlIHBsb3RzIAotIFNlZSBiZWxvdyBmb3IgYW4gZXhhbXBsZQoKIVtdKGltYWdlcy9nZ3Bsb3RfZXhhbXBsZS5wbmcpCgoqKioqKioKKioqKioqCioqKioqKgoKCiMjIyAqKlNvbHV0aW9uKioKCmBgYHtyfQpmaWx0ZXIoZ2FwbWluZGVyLCBjb3VudHJ5ICVpbiUgYygiRnJhbmNlIiwiU3BhaW4iLCJVbml0ZWQgS2luZ2RvbSIpKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IGxpZmVFeHAsY29sPWNvdW50cnkpKSArIGdlb21fbGluZSgpICsgZmFjZXRfd3JhcCh+Y291bnRyeSkgKyBsYWJzKHg9IlllYXIiLCB5ID0gIkxpZmUgRXhwZWN0YW5jeSIpCmBgYAoKCgoK
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.
Exercise
%>%
to avoid creating an intermediate data frameSolution