Let’s get to coding!
We will start with an example where we take mpg
data and make a scatterplot.
How do we get from this:
The data
head(mpg)
to this:
The plot
Let’s plot!
Load the ggplot2 library:
library(ggplot2)
ggplot
is the base layer.
ggplot()
This is a good place to for us to define our plot’s default data.
ggplot(data = mpg)
There isn’t much being plotted yet… Let’s tell our plot about our mapping, where our x
will be our cty
and y
will be our hwy
.
ggplot(
data = mpg,
mapping = aes(x = cty, y = hwy)
)
Our plot now has enough information to set some automatic axis limits based on the data.
ggplot(
data = mpg,
mapping = aes(x = cty, y = hwy)
) +
geom_point()
We can map the number of cylinders as a set of discrete values to color.
ggplot(
data = mpg,
mapping = aes(x = cty, y = hwy)
) +
geom_point(
mapping = aes(color = factor(cyl))
)
At this point, we have the base of the plot. We can save this to a variable, mpg_base_plot
, and make additional adjustments.
mpg_base_plot <- ggplot(
data = mpg,
mapping = aes(x = cty, y = hwy)
) +
geom_point(
mapping = aes(color = factor(cyl))
)
mpg_base_plot
To add the reference line, we add a layer like so:
mpg_base_plot +
geom_abline(
slope = 1,
intercept = 0,
color = 'black',
linetype = 'dotted'
)
Note that this geom layer can be added on top of a blank plot. All geom_
prefixed functions return an environment
object that holds onto the information we call it with.
ggplot() +
geom_abline(
slope = 1,
intercept = 0,
color = 'black',
linetype = 'dotted'
)
Let’s continue. Our plot looks a bit funny – the grid for the x and y are not 1 to 1. Let’s use a coord_
function to adjust this:
mpg_base_plot +
geom_abline(
slope = 1,
intercept = 0,
color = 'black',
linetype = 'dotted'
) +
coord_fixed(ratio = 1)
We can clean up the plot like so:
mpg_plot <- mpg_base_plot +
geom_abline(
slope = 1,
intercept = 0,
color = 'black',
linetype = 'dotted'
) +
coord_fixed(ratio = 1) +
scale_color_manual(
name = '# of Cyl',
values = c('skyblue','royalblue', 'blue', 'navy')
) +
ggtitle('Miles per gallon') +
theme_light()
mpg_plot
We can further manipulate the plot using things like facet_
.
mpg_plot +
facet_grid(.~class)
Let’s experiment
We can get the base plots from the ggplot cheatsheets on our computer by sourcing this R script.
source('https://goo.gl/3EQSXt')
The magic behind the +
We can discover how the +
is helping us construct these plots by running:
methods('+')
[1] +.Date +.gg* +.POSIXt
see '?methods' for accessing help and source code
From the result, we can see that the +
function is overloaded for objects that match the gg
namespace. If we look at the code for this function, we learn more about how it works.
Object-oriented
We can explore more of how ggplot
works underneath by looking at:
base_plot <- ggplot()
names(base_plot)
[1] "data" "layers" "scales" "mapping" "theme" "coordinates" "facet" "plot_env"
[9] "labels"
It’s an object that can be mutated. Other functions output objects as well.
mpg_mapping <- aes(x = cty, y = hwy)
mpg_mapping
* x -> cty
* y -> hwy
Let’s mutate the base_plot
:
base_plot$data <- mpg
base_plot$mapping <- mpg_mapping
base_plot$layers <- c(base_plot$layers, geom_point())
base_plot
Extensibility
You can also create custom stat_
s, geom_
s, and theme_
s as needed. This vignette explains the object-oriented patterns for ggplot2, and how to extend the library for your needs.
There’s actually a lot we can learn from the source code, such as all possible aes
parameters.
The makings of a pie
Grammar-based graphics lends flexibility
Back to Leland Wilkinson’s point about making a pie – that a pie chart is really a stacked bar chart that has been transformed with polar coordinates where the y is mapped to the angle, a.k.a. theta.
We start with a bar chart:
diamonds_by_cut_base <- ggplot(data = diamonds, mapping = aes(x = cut, fill = cut))
diamonds_by_cut_base +
geom_bar()
Here, we have a stacked bar chart of diamonds grouped by cut.
diamonds_by_cut_stacked <- diamonds_by_cut_base +
geom_bar(width = 1, aes(x=factor("")))
diamonds_by_cut_stacked
Taking the bar chart, we simply “add” a coord_polar
to it.
diamonds_by_cut_stacked +
coord_polar() +
scale_x_discrete("")
By default, coord_polar
maps the theta
from x
and the radius
from y
.
bullseye_coords <- coord_polar()
bullseye_coords$theta
[1] "x"
bullseye_coords$r
[1] "y"
We need to a coord_polar
where the theta
maps from y
:
pie_coords <- coord_polar(theta = "y")
pie_coords$theta
[1] "y"
Now, we can transform the stacked bar chart to a pie chart:
diamonds_by_cut_stacked +
pie_coords
?coord_polar
A layered grammar is powerful
The full grammar consists of:
- The base plot –
ggplot
– with default data
and mapping
s
- Any number of layers –
geom_
or stat_
– each with
data
mapping
- plot’s
mapping
s by default
- defined by
aes
thetics
geom_
- Sometimes defined by default by
stat_
if defined
stat_
- Identity by default
- Sometimes defined by default by
geom_
if defined
position
- Optional, any adjustments necessary to geom positioning
Additionally, the grammar includes control over the whole plot’s
scale
s
- to define how data values map
aes
thetic values
coordinate
s
facet
s
LS0tCnRpdGxlOiAiR3JhbW1hciBvZiBHcmFwaGljcyBpbiBSIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBsb2FkX2dncGxvdCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpgYGAKCiMgTGV0J3MgZ2V0IHRvIGNvZGluZyEKCldlIHdpbGwgc3RhcnQgd2l0aCBhbiBleGFtcGxlIHdoZXJlIHdlIHRha2UgYG1wZ2AgZGF0YSBhbmQgbWFrZSBhIHNjYXR0ZXJwbG90LgoKSG93IGRvIHdlIGdldCBmcm9tIHRoaXM6CgojIyBUaGUgZGF0YQoKYGBge3Igdmlld19kYXRhfQpoZWFkKG1wZykKYGBgCgp0byB0aGlzOgoKIyMgVGhlIHBsb3QKCmBgYHtyIHByZXZpZXdfcGxvdCwgZWNobyA9IEZ9Cm1wZ19wbG90IDwtIGdncGxvdCgKICAgIGRhdGEgPSBtcGcsCiAgICBtYXBwaW5nID0gYWVzKHggPSBjdHksIHkgPSBod3kpCiAgKSArCiAgZ2VvbV9wb2ludCgKICAgIG1hcHBpbmcgPSBhZXMoY29sb3IgPSBmYWN0b3IoY3lsKSkKICApICsKICBnZW9tX2FibGluZSgKICAgIHNsb3BlID0gMSwKICAgIGludGVyY2VwdCA9IDAsCiAgICBjb2xvciA9ICdibGFjaycsCiAgICBsaW5ldHlwZSA9ICdkb3R0ZWQnCiAgKSArCiAgY29vcmRfZml4ZWQoCiAgICByYXRpbyA9IDEKICApICsKICBzY2FsZV9jb2xvcl9tYW51YWwoCiAgICBuYW1lID0gJyMgb2YgQ3lsJywKICAgIHZhbHVlcyA9IGMoJ3NreWJsdWUnLCdyb3lhbGJsdWUnLCAnYmx1ZScsICduYXZ5JykKICApICsKICBnZ3RpdGxlKCdNaWxlcyBwZXIgZ2FsbG9uJykgKwogIHRoZW1lX2xpZ2h0KCkKCm1wZ19wbG90CmBgYAoKIyBMZXQncyBwbG90IQoKTG9hZCB0aGUgZ2dwbG90MiBsaWJyYXJ5OgoKYGBge3IgbG9hZF9saWJyYXJ5X25vX2V2YWwsIGV2YWwgPSBGfQpsaWJyYXJ5KGdncGxvdDIpCmBgYAoKYGdncGxvdGAgaXMgdGhlIGJhc2UgbGF5ZXIuCgpgYGB7ciBzdGVwMDBfYmFzZX0KZ2dwbG90KCkKYGBgCgpUaGlzIGlzIGEgZ29vZCBwbGFjZSB0byBmb3IgdXMgdG8gZGVmaW5lIG91ciBwbG90J3MgZGVmYXVsdCBkYXRhLgoKYGBge3Igc3RlcDAxX2RhdGF9CmdncGxvdChkYXRhID0gbXBnKQpgYGAKClRoZXJlIGlzbid0IG11Y2ggYmVpbmcgcGxvdHRlZCB5ZXQuLi4gTGV0J3MgdGVsbCBvdXIgcGxvdCBhYm91dCBvdXIgbWFwcGluZywgd2hlcmUgb3VyIGB4YCB3aWxsIGJlIG91ciBgY3R5YCBhbmQgYHlgIHdpbGwgYmUgb3VyIGBod3lgLgoKYGBge3Igc3RlcDAyX21hcHBpbmd9CmdncGxvdCgKICAgIGRhdGEgPSBtcGcsCiAgICBtYXBwaW5nID0gYWVzKHggPSBjdHksIHkgPSBod3kpCiAgKQpgYGAKCk91ciBwbG90IG5vdyBoYXMgZW5vdWdoIGluZm9ybWF0aW9uIHRvIHNldCBzb21lIGF1dG9tYXRpYyBheGlzIGxpbWl0cyBiYXNlZCBvbiB0aGUgZGF0YS4KCmBgYHtyIHN0ZXAwM19wb2ludHN9CmdncGxvdCgKICAgIGRhdGEgPSBtcGcsCiAgICBtYXBwaW5nID0gYWVzKHggPSBjdHksIHkgPSBod3kpCiAgKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKV2UgY2FuIG1hcCB0aGUgbnVtYmVyIG9mIGN5bGluZGVycyBhcyBhIHNldCBvZiBkaXNjcmV0ZSB2YWx1ZXMgdG8gY29sb3IuCgpgYGB7ciBzdGVwMDRfY29sb3JzfQpnZ3Bsb3QoCiAgICBkYXRhID0gbXBnLAogICAgbWFwcGluZyA9IGFlcyh4ID0gY3R5LCB5ID0gaHd5KQogICkgKwogIGdlb21fcG9pbnQoCiAgICBtYXBwaW5nID0gYWVzKGNvbG9yID0gZmFjdG9yKGN5bCkpCiAgKQpgYGAKCkF0IHRoaXMgcG9pbnQsIHdlIGhhdmUgdGhlIGJhc2Ugb2YgdGhlIHBsb3QuICBXZSBjYW4gc2F2ZSB0aGlzIHRvIGEgdmFyaWFibGUsIGBtcGdfYmFzZV9wbG90YCwgYW5kIG1ha2UgYWRkaXRpb25hbCBhZGp1c3RtZW50cy4KCmBgYHtyIHN0ZXAwNV9hc3NpZ259Cm1wZ19iYXNlX3Bsb3QgPC0gZ2dwbG90KAogICAgZGF0YSA9IG1wZywKICAgIG1hcHBpbmcgPSBhZXMoeCA9IGN0eSwgeSA9IGh3eSkKICApICsKICBnZW9tX3BvaW50KAogICAgbWFwcGluZyA9IGFlcyhjb2xvciA9IGZhY3RvcihjeWwpKQogICkKbXBnX2Jhc2VfcGxvdApgYGAKCgpUbyBhZGQgdGhlIHJlZmVyZW5jZSBsaW5lLCB3ZSBhZGQgYSBsYXllciBsaWtlIHNvOgoKYGBge3Igc3RlcDA2X2xpbmV9Cm1wZ19iYXNlX3Bsb3QgKwogIGdlb21fYWJsaW5lKAogICAgc2xvcGUgPSAxLAogICAgaW50ZXJjZXB0ID0gMCwKICAgIGNvbG9yID0gJ2JsYWNrJywKICAgIGxpbmV0eXBlID0gJ2RvdHRlZCcKICApCmBgYAoKTm90ZSB0aGF0IHRoaXMgZ2VvbSBsYXllciBjYW4gYmUgYWRkZWQgb24gdG9wIG9mIGEgYmxhbmsgcGxvdC4gIEFsbCBgZ2VvbV9gIHByZWZpeGVkIGZ1bmN0aW9ucyByZXR1cm4gYW4gYGVudmlyb25tZW50YCBvYmplY3QgdGhhdCBob2xkcyBvbnRvIHRoZSBpbmZvcm1hdGlvbiB3ZSBjYWxsIGl0IHdpdGguCgpgYGB7ciBsaW5lX2xheWVyX2V4YW1wbGV9CmdncGxvdCgpICsKICBnZW9tX2FibGluZSgKICAgIHNsb3BlID0gMSwKICAgIGludGVyY2VwdCA9IDAsCiAgICBjb2xvciA9ICdibGFjaycsCiAgICBsaW5ldHlwZSA9ICdkb3R0ZWQnCiAgKQpgYGAKCkxldCdzIGNvbnRpbnVlLiAgT3VyIHBsb3QgbG9va3MgYSBiaXQgZnVubnkgLS0gdGhlIGdyaWQgZm9yIHRoZSB4IGFuZCB5IGFyZSBub3QgMSB0byAxLiAgTGV0J3MgdXNlIGEgYGNvb3JkX2AgZnVuY3Rpb24gdG8gYWRqdXN0IHRoaXM6CgpgYGB7ciBzdGVwMDdfZml4ZWR9Cm1wZ19iYXNlX3Bsb3QgKwogIGdlb21fYWJsaW5lKAogICAgc2xvcGUgPSAxLAogICAgaW50ZXJjZXB0ID0gMCwKICAgIGNvbG9yID0gJ2JsYWNrJywKICAgIGxpbmV0eXBlID0gJ2RvdHRlZCcKICApICsKICBjb29yZF9maXhlZChyYXRpbyA9IDEpCmBgYAoKV2UgY2FuIGNsZWFuIHVwIHRoZSBwbG90IGxpa2Ugc286CgpgYGB7ciBzdGVwMDhfY2xlYW51cH0KbXBnX3Bsb3QgPC0gbXBnX2Jhc2VfcGxvdCArCiAgZ2VvbV9hYmxpbmUoCiAgICBzbG9wZSA9IDEsCiAgICBpbnRlcmNlcHQgPSAwLAogICAgY29sb3IgPSAnYmxhY2snLAogICAgbGluZXR5cGUgPSAnZG90dGVkJwogICkgKwogIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCgKICAgIG5hbWUgPSAnIyBvZiBDeWwnLAogICAgdmFsdWVzID0gYygnc2t5Ymx1ZScsJ3JveWFsYmx1ZScsICdibHVlJywgJ25hdnknKQogICkgKwogIGdndGl0bGUoJ01pbGVzIHBlciBnYWxsb24nKSArCiAgdGhlbWVfbGlnaHQoKQptcGdfcGxvdApgYGAKCldlIGNhbiBmdXJ0aGVyIG1hbmlwdWxhdGUgdGhlIHBsb3QgdXNpbmcgdGhpbmdzIGxpa2UgYGZhY2V0X2AuCgpgYGB7ciBmYWNldHNfZXhhbXBsZX0KbXBnX3Bsb3QgKwogIGZhY2V0X2dyaWQoLn5jbGFzcykKYGBgCgojIExldCdzIGV4cGVyaW1lbnQKCldlIGNhbiBnZXQgdGhlIGJhc2UgcGxvdHMgZnJvbSB0aGUgZ2dwbG90IGNoZWF0c2hlZXRzIG9uIG91ciBjb21wdXRlciBieSBzb3VyY2luZyBbdGhpcyBSIHNjcmlwdF0oaHR0cHM6Ly9ob3VzdG9udXNlcnMuZ2l0aHViLmlvL2ludHJvLXRvLWdncGxvdDIvZXhhbXBsZXNfZnJvbV9jaGVhdHNoZWV0KS4KCmBgYHtyIGV2YWwgPSBGfQpzb3VyY2UoJ2h0dHBzOi8vZ29vLmdsLzNFUVNYdCcpCmBgYAoKCiMgVGhlIG1hZ2ljIGJlaGluZCB0aGUgYCtgCgpXZSBjYW4gZGlzY292ZXIgaG93IHRoZSBgK2AgaXMgaGVscGluZyB1cyBjb25zdHJ1Y3QgdGhlc2UgcGxvdHMgYnkgcnVubmluZzoKCmBgYHtyIHdoYXRfdGhlX3BsdXN9Cm1ldGhvZHMoJysnKQpgYGAKCkZyb20gdGhlIHJlc3VsdCwgd2UgY2FuIHNlZSB0aGF0IHRoZSBgK2AgZnVuY3Rpb24gaXMgb3ZlcmxvYWRlZCBmb3Igb2JqZWN0cyB0aGF0IG1hdGNoIHRoZSBgZ2dgIG5hbWVzcGFjZS4gIElmIHdlIGxvb2sgW2F0IHRoZSBjb2RlXShodHRwczovL2dpdGh1Yi5jb20vdGlkeXZlcnNlL2dncGxvdDIvYmxvYi9tYXN0ZXIvUi9wbG90LWNvbnN0cnVjdGlvbi5yI0wzOSkgZm9yIHRoaXMgZnVuY3Rpb24sIHdlIGxlYXJuIG1vcmUgYWJvdXQgaG93IGl0IHdvcmtzLgoKIyMgT2JqZWN0LW9yaWVudGVkCgpXZSBjYW4gZXhwbG9yZSBtb3JlIG9mIGhvdyBgZ2dwbG90YCB3b3JrcyB1bmRlcm5lYXRoIGJ5IGxvb2tpbmcgYXQ6CgpgYGB7cn0KYmFzZV9wbG90IDwtIGdncGxvdCgpCm5hbWVzKGJhc2VfcGxvdCkKYGBgCgpJdCdzIGFuIG9iamVjdCB0aGF0IGNhbiBiZSBtdXRhdGVkLiAgT3RoZXIgZnVuY3Rpb25zIG91dHB1dCBvYmplY3RzIGFzIHdlbGwuCgpgYGB7cn0KbXBnX21hcHBpbmcgPC0gYWVzKHggPSBjdHksIHkgPSBod3kpCm1wZ19tYXBwaW5nCmBgYAoKTGV0J3MgbXV0YXRlIHRoZSBgYmFzZV9wbG90YDoKCmBgYHtyfQpiYXNlX3Bsb3QkZGF0YSA8LSBtcGcKYmFzZV9wbG90JG1hcHBpbmcgPC0gbXBnX21hcHBpbmcKYmFzZV9wbG90JGxheWVycyA8LSBjKGJhc2VfcGxvdCRsYXllcnMsIGdlb21fcG9pbnQoKSkKYmFzZV9wbG90CmBgYAoKCiMjIEV4dGVuc2liaWxpdHkKCllvdSBjYW4gYWxzbyBjcmVhdGUgY3VzdG9tIGBzdGF0X2BzLCBgZ2VvbV9gcywgYW5kIGB0aGVtZV9gcyBhcyBuZWVkZWQuICBUaGlzIFt2aWduZXR0ZV0oaHR0cDovL2RvY3MuZ2dwbG90Mi5vcmcvY3VycmVudC92aWduZXR0ZXMvZXh0ZW5kaW5nLWdncGxvdDIuaHRtbCkgZXhwbGFpbnMgdGhlIG9iamVjdC1vcmllbnRlZCBwYXR0ZXJucyBmb3IgZ2dwbG90MiwgYW5kIGhvdyB0byBleHRlbmQgdGhlIGxpYnJhcnkgZm9yIHlvdXIgbmVlZHMuCgpUaGVyZSdzIGFjdHVhbGx5IGEgbG90IHdlIGNhbiBsZWFybiBmcm9tIFt0aGUgc291cmNlIGNvZGVdKGh0dHBzOi8vZ2l0aHViLmNvbS90aWR5dmVyc2UvZ2dwbG90Mi90cmVlL21hc3Rlci9SKSwgc3VjaCBhcyBhbGwgcG9zc2libGUgW2BhZXNgIHBhcmFtZXRlcnNdKGh0dHBzOi8vZ2l0aHViLmNvbS90aWR5dmVyc2UvZ2dwbG90Mi9ibG9iL21hc3Rlci9SL2Flcy5yI0w0LUw4KS4KCiMgVGhlIG1ha2luZ3Mgb2YgYSBwaWUKCiMjIyBHcmFtbWFyLWJhc2VkIGdyYXBoaWNzIGxlbmRzIGZsZXhpYmlsaXR5CgpCYWNrIHRvIExlbGFuZCBXaWxraW5zb24ncyBwb2ludCBhYm91dCBtYWtpbmcgYSBwaWUgLS0gdGhhdCBhIHBpZSBjaGFydCBpcyByZWFsbHkgYSBzdGFja2VkIGJhciBjaGFydCB0aGF0IGhhcyBiZWVuIHRyYW5zZm9ybWVkIHdpdGggcG9sYXIgY29vcmRpbmF0ZXMgd2hlcmUgdGhlIHkgaXMgbWFwcGVkIHRvIHRoZSBhbmdsZSwgYS5rLmEuIHRoZXRhLgoKIyMjIFdlIHN0YXJ0IHdpdGggYSBiYXIgY2hhcnQ6CgpgYGB7ciBwaWUwMF9iYXJ9CmRpYW1vbmRzX2J5X2N1dF9iYXNlIDwtIGdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IGN1dCwgZmlsbCA9IGN1dCkpCmRpYW1vbmRzX2J5X2N1dF9iYXNlICsKICBnZW9tX2JhcigpCmBgYAoKSGVyZSwgd2UgaGF2ZSBhIHN0YWNrZWQgYmFyIGNoYXJ0IG9mIGRpYW1vbmRzIGdyb3VwZWQgYnkgY3V0LgoKYGBge3IgcGllMDFfc3RhY2tlZH0KZGlhbW9uZHNfYnlfY3V0X3N0YWNrZWQgPC0gZGlhbW9uZHNfYnlfY3V0X2Jhc2UgKwogIGdlb21fYmFyKHdpZHRoID0gMSwgYWVzKHg9ZmFjdG9yKCIiKSkpCgpkaWFtb25kc19ieV9jdXRfc3RhY2tlZApgYGAKClRha2luZyB0aGUgYmFyIGNoYXJ0LCB3ZSBzaW1wbHkgImFkZCIgYSBgY29vcmRfcG9sYXJgIHRvIGl0LgoKYGBge3IgcGllMDJfYnVsbHNleWV9CmRpYW1vbmRzX2J5X2N1dF9zdGFja2VkICsKICBjb29yZF9wb2xhcigpICArCiAgc2NhbGVfeF9kaXNjcmV0ZSgiIikKYGBgCgpCeSBkZWZhdWx0LCBgY29vcmRfcG9sYXJgIG1hcHMgdGhlIGB0aGV0YWAgZnJvbSBgeGAgYW5kIHRoZSBgcmFkaXVzYCBmcm9tIGB5YC4KCmBgYHtyIHdoYXRfaXNfYV9jb29yZF9hbnl3YXl9CmJ1bGxzZXllX2Nvb3JkcyA8LSBjb29yZF9wb2xhcigpCmJ1bGxzZXllX2Nvb3JkcyR0aGV0YQpidWxsc2V5ZV9jb29yZHMkcgpgYGAKCldlIG5lZWQgdG8gYSBgY29vcmRfcG9sYXJgIHdoZXJlIHRoZSBgdGhldGFgIG1hcHMgZnJvbSBgeWA6CgpgYGB7ciBwaWVfY29vcmRzfQpwaWVfY29vcmRzIDwtIGNvb3JkX3BvbGFyKHRoZXRhID0gInkiKQpwaWVfY29vcmRzJHRoZXRhCmBgYAoKTm93LCB3ZSBjYW4gdHJhbnNmb3JtIHRoZSBzdGFja2VkIGJhciBjaGFydCB0byBhIHBpZSBjaGFydDoKCmBgYHtyIHBpZTAzX3BpZX0KZGlhbW9uZHNfYnlfY3V0X3N0YWNrZWQgKwogIHBpZV9jb29yZHMKYGBgCgpgYGB7cn0KP2Nvb3JkX3BvbGFyCmBgYAoKCiMgQSBsYXllcmVkIGdyYW1tYXIgaXMgcG93ZXJmdWwKClRoZSBmdWxsIGdyYW1tYXIgY29uc2lzdHMgb2Y6CgogICogVGhlIGJhc2UgcGxvdCAtLSBgZ2dwbG90YCAtLSB3aXRoIGRlZmF1bHQgYGRhdGFgIGFuZCBgbWFwcGluZ2BzCiAgKiBBbnkgbnVtYmVyIG9mIGxheWVycyAtLSBgZ2VvbV9gIG9yIGBzdGF0X2AgLS0gZWFjaCB3aXRoCiAgICAqIGBkYXRhYAogICAgICAgICogcGxvdCdzIGBkYXRhYCBieSBkZWZhdWx0CiAgICAqIGBtYXBwaW5nYAogICAgICAgICogcGxvdCdzIGBtYXBwaW5nYHMgYnkgZGVmYXVsdAogICAgICAgICogZGVmaW5lZCBieSBgYWVzYHRoZXRpY3MKICAgICogYGdlb21fYAogICAgICAgICogU29tZXRpbWVzIGRlZmluZWQgYnkgZGVmYXVsdCBieSBgc3RhdF9gIGlmIGRlZmluZWQKICAgICogYHN0YXRfYAogICAgICAgICogSWRlbnRpdHkgYnkgZGVmYXVsdAogICAgICAgICogU29tZXRpbWVzIGRlZmluZWQgYnkgZGVmYXVsdCBieSBgZ2VvbV9gIGlmIGRlZmluZWQKICAgICogYHBvc2l0aW9uYAogICAgICAgICogT3B0aW9uYWwsIGFueSBhZGp1c3RtZW50cyBuZWNlc3NhcnkgdG8gZ2VvbSBwb3NpdGlvbmluZwoKQWRkaXRpb25hbGx5LCB0aGUgZ3JhbW1hciBpbmNsdWRlcyBjb250cm9sIG92ZXIgdGhlIHdob2xlIHBsb3QncwoKICAqIGBzY2FsZWBzCiAgICAqIHRvIGRlZmluZSBob3cgZGF0YSB2YWx1ZXMgbWFwIGBhZXNgdGhldGljIHZhbHVlcwogICogYGNvb3JkaW5hdGVgcwogICogYGZhY2V0YHMKICAgICogc3VicGxvdHMKCgojIFJlYWRpbmcgYW5kIFNvdXJjZXMKCltBIExheWVyZWQgR3JhbW1hciBvZiBHcmFwaGljc10oaHR0cDovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy9sYXllcmVkLWdyYW1tYXIucGRmKQoKW01hc3RlcmluZyB0aGUgR3JhbW1hcl0oaHR0cHM6Ly9ycHVicy5jb20vZmxvd2VydGVhci8yMjQ0MjQpCgpbUiBmb3IgRGF0YSBTY2llbmNlOiBWaXN1YWxpemluZyBEYXRhXShodHRwOi8vcjRkcy5oYWQuY28ubnovZGF0YS12aXN1YWxpc2F0aW9uLmh0bWwjaW50cm9kdWN0aW9uLTEpCgpbRXh0ZW5kaW5nIGdncGxvdDJdKGh0dHA6Ly9kb2NzLmdncGxvdDIub3JnL2N1cnJlbnQvdmlnbmV0dGVzL2V4dGVuZGluZy1nZ3Bsb3QyLmh0bWwpCgpbVGhlIFIgR3JhcGggR2FsbGVyeV0oaHR0cDovL3d3dy5yLWdyYXBoLWdhbGxlcnkuY29tL3BvcnRmb2xpby9nZ3Bsb3QyLXBhY2thZ2UvKQoKW0RhdGEgQ2FtcDogRGF0YSBWaXN1YWxpemF0aW9uIHdpdGggZ2dwbG90Ml0oaHR0cHM6Ly93d3cuZGF0YWNhbXAuY29tL2NvdXJzZXMvZGF0YS12aXN1YWxpemF0aW9uLXdpdGgtZ2dwbG90Mi0xKQoKW1N3aXJsOiBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzXShodHRwOi8vc3dpcmxzdGF0cy5jb20vc2NuL2VkYS5odG1sKQoKW2dncGxvdDIgQ2hlYXRzaGVldF0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTYvMTEvZ2dwbG90Mi1jaGVhdHNoZWV0LTIuMS5wZGYpCgpbVGVsbGluZyBzdG9yaWVzIHdpdGggZGF0YSB1c2luZyB0aGUgZ3JhbW1hciBvZiBncmFwaGljc10oaHR0cHM6Ly9jb2Rld29yZHMucmVjdXJzZS5jb20vaXNzdWVzL3NpeC90ZWxsaW5nLXN0b3JpZXMtd2l0aC1kYXRhLXVzaW5nLXRoZS1ncmFtbWFyLW9mLWdyYXBoaWNzKQoKW0ludHJvZHVjdGlvbiB0byBSIEdyYXBoaWNzIHdpdGggZ2dwbG90Ml0oaHR0cDovL3R1dG9yaWFscy5pcS5oYXJ2YXJkLmVkdS9SL1JncmFwaGljcy9SZ3JhcGhpY3MuaHRtbCkKCltnZ3Bsb3QyIHNvdXJjZSBjb2RlXShodHRwczovL2dpdGh1Yi5jb20vdGlkeXZlcnNlL2dncGxvdDIvdHJlZS9tYXN0ZXIvUikKCltnZ3Bsb3QyOiBFbGVnYW50IEdyYXBoaWNzIGZvciBEYXRhIEFuYWx5c2lzXShodHRwOi8vZ2dwbG90Mi5vcmcvYm9vay8pCgpbVGhlIEdyYW1tYXIgb2YgR3JhcGhpY3NdKGh0dHBzOi8vd3d3LmFtYXpvbi5jb20vR3JhbW1hci1HcmFwaGljcy1TdGF0aXN0aWNzLUNvbXB1dGluZy9kcC8wMzg3MjQ1NDQ4KQoKW1NlbWlvbG9neSBvZiBHcmFwaGljc10oaHR0cHM6Ly93d3cuYW1hem9uLmNvbS9TZW1pb2xvZ3ktR3JhcGhpY3MtRGlhZ3JhbXMtTmV0d29ya3MtTWFwcy9kcC8xNTg5NDgyNjExKQo=