4. Plotting in R
Plot basics
- As we have heard, R has extensive graphical capabilities
- …but we need to start simple
- We will describe base graphics in R: the plots available with any standard R installation
- other more advanced alternatives are, e.g.,
lattice
, ggplot2
- See our intermediate R course for fancy graphics
- Plotting in R is a vast topic:
- We cannot cover everything
- You can tinker with plots to your hearts content
- Best to learn from examples; e.g. The R Graph Gallery
- You need to think about how best to visualise your data
- R cannot prevent you from creating a plotting disaster:
Making a Scatter Plot
- If given a single vector as an argument, the function
plot()
will make a scatter plot with the values of the vector on the y axis, and indices in the x axis
- e.g. it puts a point at:
- x = 1, y = 70.8
- x = 2, y = 67.9 etc…
- We are going to be using the patients data frame, read using the following command
patients <- read.delim("patient-info.txt")
Remember that $
can be used to access a particular column. The result is a vector, which is the most-basic type of data used in plotting
patients$Weight
- R tries to guess the most appropriate way to visualise the data, according to the type and dimensions of the object(s) provided
plot(patients$Weight)
- Axis limits, labels, titles are inferred from the data
- We can modify these as we wish, by specifying arguments
- We can give two arguments to
plot()
:
- In order to visualise the relationship between two variables
- It will put the values from the first argument in the x axis, and values from the second argument on the y axis
patients$Age
plot(patients$Age, patients$Weight)
Making a barplot
- Other types of visualisation are available:
- These are often just special cases of using the
plot()
function
- One such function is
barplot()
barplot(patients$Age)
- It is more usual to display count data in a barplot
- e.g. the counts of a particular categorical variable
barplot(summary(patients$Sex))
Plotting a distribution: Histogram
- A histogram is a popular way of visualising a distribution of continuous data:
- You can change the width of bins
- The y-axis can be either frequency of density
hist(patients$Weight)
Plotting a distribution: Boxplot
- The boxplot is commonly used in statistics to visualise a distribution:
boxplot(patients$Weight)
boxplot(patients$Weight ~ patients$Sex)
- Other alternatives to consider:
example(dotchart)
example(stripchart)
example(vioplot) # From vioplot library
example(beeswarm) # From beeswarm library
Exercise: Exercise 4a
- In the course folder you will find the file
ozone.csv
:
- Read these data into R using
read.csv
or read.delim
as described in the previous section
- you will need to choose which is appropriate for the file type
- What data types are present? Try to think of ways to create the following plots from the data
- Scatter plot two variables. e.g. Solar Radiation against Ozone
- A histogram. e.g. Wind Speed
- Boxplot of a continuous variable against a categorical variable. e.g. Ozone level per month
### Your Answer Here ###
Simple customisations
plot()
comes with a large collection of arguments that can be set when we call the function:
- Recall that, unless specified, arguments have a default value
- We can choose to draw lines on the plot rather than points
- The rest of the plot remains the same
plot(patients$Weight, type = "l")
- We can also have both lines and points:
plot(patients$Weight, type = "b")
- Add an informative title to the plot using the
main
argument:
plot(patients$Age, patients$Weight,
main = "Relationship between Weight and Age")
plot(patients$Age, patients$Weight, xlab = "Age")
plot(patients$Age, patients$Weight, ylab = "Weight")
- We can specifiy multiple arguments at once:
- here
ylim
and xlim
are used to specify axis limits
plot(patients$Age,patients$Weight,
ylab="Weight",
xlab="Age",
main="Relationship between Weight and Age",
xlim=c(10,70),
ylim=c(60,80))
Defining a colour
Changing the col
argument to plot()
changes the colour that the points are plotted in:
plot(patients$Age, patients$Weight, col = "red")
Plotting characters
- R can use a variety of plotting characters
- Each of which has a numeric code
plot(patients$Age, patients$Weight, pch = 16)
- Or you can specify a character:
plot(patients$Age, patients$Weight, pch = "X")
Size of points
Character expansion: Make the size of points 3 times larger than the default
plot(patients$Age, patients$Weight, cex = 3)
or 20% of the original size
plot(patients$Age, patients$Weight, cex = 0.2)
Colours and characters as vectors
- Previously we have used a vector of length 1 as our value of colour and character
- We can use a vector of any length:
- the values will get recycled (re-used) so that each point gets assigned a value
- We can use a pre-defined colour palette (see later)
plot(patients$Age, patients$Weight,
col = c("red","blue"))
Other plots use the same arguments
- Other plotting functions use the same arguments as
plot()
- technical explanation: the arguments are ‘inherited’
boxplot(patients$Weight~patients$Sex,
xlab = "Sex",
ylab = "Weight",
main = "Relationship between Weight and Gender",
col = c("blue","yellow"))
Exercise: exercise4b
- Can you re-create the following plots? Hint:
- See the
breaks
and freq
arguments to hist (?hist
) to create 20 bins and display density rather than frequency
- For third plot, see the rainbow function (
?rainbow
)
- Don’t worry too much about getting the colours exactly correct
- The
las
argument changes the label orientation. See ?par
.
- look at the arguments to
boxplot
to see how to change the names printed under each box
### Your Answer Here ###
More on colours
- The
rainbow()
function is used to create a vector of colours for the boxplot; in other words a palette:
- Red, Orange, Yellow, Green, Blue, Indigo, Violet, etc.
- Other palette functions available:
heat.colors(), terrain.colors(), topo.colors(), cm.colors()
- Red, Orange, Yellow, Green, Blue, Indigo, Violet….etc
- More aesthetically-pleasing palettes are provided by the
RColorBrewer
package:
- can also check for palettes that are accepted for those with colour-blindness
- You may need to install
RColorBrewer
with the following line of code
- remember, you only need to do this once
install.packages("RColorBrewer")
library(RColorBrewer)
display.brewer.all()
display.brewer.all(colorblindFriendly = TRUE)
weather <- read.csv("ozone.csv")
boxplot(weather$Temp ~ weather$Month,col=brewer.pal(5,"Set1"))
LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFNvbHZpbmcgQmlvbG9naWNhbCBQcm9ibGVtcyBVc2luZyBSIC0gRGF5IDEiCmF1dGhvcjogTWFyayBEdW5uaW5nLCBTdXJhaiBNZW5vbiBhbmQgQWlvcmEgWmFiYWxhLiBPcmlnaW5hbCBtYXRlcmlhbCBieSBSb2JlcnQgU3Rvam5pxIcsCiAgTGF1cmVudCBHYXR0bywgUm9iIEZveSwgSm9obiBEYXZleSwgRMOhdmlkIE1vbG7DoXIgYW5kIElhbiBSb2JlcnRzCmRhdGU6ICdgciBmb3JtYXQoU3lzLnRpbWUoKSwgIkxhc3QgbW9kaWZpZWQ6ICVkICViICVZIilgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCgoKIyA0LiBQbG90dGluZyBpbiBSCgojI1Bsb3QgYmFzaWNzCgotIEFzIHdlIGhhdmUgaGVhcmQsIFIgaGFzIGV4dGVuc2l2ZSBncmFwaGljYWwgY2FwYWJpbGl0aWVzCi0gLi4uYnV0IHdlIG5lZWQgdG8gc3RhcnQgc2ltcGxlCi0gV2Ugd2lsbCBkZXNjcmliZSAqYmFzZSogZ3JhcGhpY3MgaW4gUjogdGhlIHBsb3RzIGF2YWlsYWJsZSB3aXRoIGFueSBzdGFuZGFyZCBSIGluc3RhbGxhdGlvbgogICAgKyBvdGhlciBtb3JlIGFkdmFuY2VkIGFsdGVybmF0aXZlcyBhcmUsIGUuZy4sIGBsYXR0aWNlYCwgYGdncGxvdDJgCiAgICArIFNlZSBvdXIgW2ludGVybWVkaWF0ZSBSIGNvdXJzZV0oaHR0cDovL2Jpb2luZm9ybWF0aWNzLWNvcmUtc2hhcmVkLXRyYWluaW5nLmdpdGh1Yi5pby9yLWludGVybWVkaWF0ZS8pIGZvciBmYW5jeSBncmFwaGljcwotIFBsb3R0aW5nIGluIFIgaXMgYSAqdmFzdCogdG9waWM6CiAgICArIFdlIGNhbm5vdCBjb3ZlciBldmVyeXRoaW5nCiAgICArIFlvdSBjYW4gdGlua2VyIHdpdGggcGxvdHMgdG8geW91ciBoZWFydHMgY29udGVudAogICAgKyBCZXN0IHRvIGxlYXJuIGZyb20gZXhhbXBsZXM7IGUuZy4gW1RoZSBSIEdyYXBoIEdhbGxlcnldKGh0dHA6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS8pCi0gKioqWW91IG5lZWQgdG8gdGhpbmsgYWJvdXQgaG93IGJlc3QgdG8gdmlzdWFsaXNlIHlvdXIgZGF0YSoqKiAKICAgICsgaHR0cDovL3d3dy5iaW9pbmZvcm1hdGljcy5iYWJyYWhhbS5hYy51ay90cmFpbmluZy5odG1sI2ZpZ3VyZWRlc2lnbgotIFIgY2Fubm90IHByZXZlbnQgeW91IGZyb20gY3JlYXRpbmcgYSBwbG90dGluZyBkaXNhc3RlcjogCiAgICArIGh0dHA6Ly93d3cuYnVzaW5lc3NpbnNpZGVyLmNvbS90aGUtMjctd29yc3QtY2hhcnRzLW9mLWFsbC10aW1lLTIwMTMtNj9vcD0xJklSPVQKICAgIAojI01ha2luZyBhIFNjYXR0ZXIgUGxvdAoKLSBJZiBnaXZlbiBhIHNpbmdsZSB2ZWN0b3IgYXMgYW4gYXJndW1lbnQsIHRoZSBmdW5jdGlvbiAqKmBwbG90KClgKiogd2lsbCBtYWtlIGEgc2NhdHRlciBwbG90IHdpdGggdGhlICp2YWx1ZXMqIG9mIHRoZSB2ZWN0b3Igb24gdGhlICp5KiBheGlzLCBhbmQgKmluZGljZXMqIGluIHRoZSAqeCogYXhpcwogICAgKyBlLmcuIGl0IHB1dHMgYSBwb2ludCBhdDoKICAgICAgICArIHggPSAxLCB5ID0gNzAuOAogICAgICAgICsgeCA9IDIsIHkgPSA2Ny45IGV0Yy4uLgotIFdlIGFyZSBnb2luZyB0byBiZSB1c2luZyB0aGUgcGF0aWVudHMgZGF0YSBmcmFtZSwgcmVhZCB1c2luZyB0aGUgZm9sbG93aW5nIGNvbW1hbmQKCmBgYHtyfQpwYXRpZW50cyA8LSByZWFkLmRlbGltKCJwYXRpZW50LWluZm8udHh0IikKYGBgCgpSZW1lbWJlciB0aGF0IGAkYCBjYW4gYmUgdXNlZCB0byBhY2Nlc3MgYSBwYXJ0aWN1bGFyIGNvbHVtbi4gVGhlIHJlc3VsdCBpcyBhIHZlY3Rvciwgd2hpY2ggaXMgdGhlIG1vc3QtYmFzaWMgdHlwZSBvZiBkYXRhIHVzZWQgaW4gcGxvdHRpbmcKICAgICAgICAKYGBge3J9CnBhdGllbnRzJFdlaWdodApgYGAKCgotIFIgdHJpZXMgdG8gZ3Vlc3MgdGhlIG1vc3QgYXBwcm9wcmlhdGUgd2F5IHRvIHZpc3VhbGlzZSB0aGUgZGF0YSwgYWNjb3JkaW5nIHRvIHRoZSB0eXBlIGFuZCBkaW1lbnNpb25zIG9mIHRoZSBvYmplY3QocykgcHJvdmlkZWQKCgpgYGB7cn0KcGxvdChwYXRpZW50cyRXZWlnaHQpCmBgYAoKLSBBeGlzIGxpbWl0cywgbGFiZWxzLCB0aXRsZXMgYXJlIGluZmVycmVkIGZyb20gdGhlIGRhdGEKICAgICsgV2UgY2FuIG1vZGlmeSB0aGVzZSBhcyB3ZSB3aXNoLCBieSBzcGVjaWZ5aW5nICoqKmFyZ3VtZW50cyoqKgoKLSBXZSBjYW4gZ2l2ZSB0d28gYXJndW1lbnRzIHRvIGBwbG90KClgOgogICAgKyBJbiBvcmRlciB0byB2aXN1YWxpc2UgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byB2YXJpYWJsZXMKICAgICsgSXQgd2lsbCBwdXQgdGhlIHZhbHVlcyBmcm9tIHRoZSAqZmlyc3QqIGFyZ3VtZW50IGluIHRoZSAqeCogYXhpcywgYW5kIHZhbHVlcyBmcm9tIHRoZSAqc2Vjb25kKiBhcmd1bWVudCBvbiB0aGUgKnkqIGF4aXMKCmBgYHtyfQpwYXRpZW50cyRBZ2UKcGxvdChwYXRpZW50cyRBZ2UsIHBhdGllbnRzJFdlaWdodCkKYGBgCgojI01ha2luZyBhIGJhcnBsb3QKCi0gT3RoZXIgdHlwZXMgb2YgdmlzdWFsaXNhdGlvbiBhcmUgYXZhaWxhYmxlOgogICAgKyBUaGVzZSBhcmUgb2Z0ZW4ganVzdCBzcGVjaWFsIGNhc2VzIG9mIHVzaW5nIHRoZSBgcGxvdCgpYCBmdW5jdGlvbgogICAgKyBPbmUgc3VjaCBmdW5jdGlvbiBpcyBgYmFycGxvdCgpYAogICAgCiAgICAKYGBge3J9CmJhcnBsb3QocGF0aWVudHMkQWdlKQpgYGAKCgotIEl0IGlzIG1vcmUgdXN1YWwgdG8gZGlzcGxheSBjb3VudCBkYXRhIGluIGEgYmFycGxvdAogICAgKyBlLmcuIHRoZSBjb3VudHMgb2YgYSBwYXJ0aWN1bGFyICoqKmNhdGVnb3JpY2FsKioqIHZhcmlhYmxlCgpgYGB7cn0KYmFycGxvdChzdW1tYXJ5KHBhdGllbnRzJFNleCkpCmBgYAoKIyNQbG90dGluZyBhIGRpc3RyaWJ1dGlvbjogSGlzdG9ncmFtCgotIEEgaGlzdG9ncmFtIGlzIGEgcG9wdWxhciB3YXkgb2YgdmlzdWFsaXNpbmcgYSBkaXN0cmlidXRpb24gb2YgKioqY29udGludW91cyoqKiBkYXRhOgogICAgKyBZb3UgY2FuIGNoYW5nZSB0aGUgd2lkdGggb2YgYmlucwogICAgKyBUaGUgeS1heGlzIGNhbiBiZSBlaXRoZXIgZnJlcXVlbmN5IG9mIGRlbnNpdHkKCmBgYHtyfQpoaXN0KHBhdGllbnRzJFdlaWdodCkKYGBgCgoKCiMjUGxvdHRpbmcgYSBkaXN0cmlidXRpb246IEJveHBsb3QKCi0gVGhlIGJveHBsb3QgaXMgY29tbW9ubHkgdXNlZCBpbiBzdGF0aXN0aWNzIHRvIHZpc3VhbGlzZSBhIGRpc3RyaWJ1dGlvbjoKYGBge3J9CmJveHBsb3QocGF0aWVudHMkV2VpZ2h0KQpgYGAKCi0gVGhlIGJsYWNrIHNvbGlkIGxpbmUgaXMgdGhlICoqKm1lZGlhbioqKgotIFRoZSB0b3AgYW5kIGJvdHRvbSBvZiB0aGUgYm94IGFyZSB0aGUgNzV0aCBhbmQgMjV0aCBwZXJjZW50aWxlcwogICAgICsgSGVuY2UsIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIHRoZXNlIGlzIGEgcmVmbGVjdGlvbiBvZiB0aGUgKnNwcmVhZCogb2YgdGhlIGRhdGE7IHRoZSBJbnRlci1RdWFydGlsZSBSYW5nZSAoKioqSVFSKioqKQotIFdoaXNrZXJzIGFyZSBkcmF3biBhdCAxLjUgeCBJUVIgYW5kIC0xLjUgeCBJUVIKCgotIFNvbWV0aW1lcyB3ZSB3YW50IHRvIGNvbXBhcmUgZGlzdHJpYnV0aW9ucyBiZXR3ZWVuIGRpZmZlcmVudCBjYXRlZ29yaWVzIGluIG91ciBkYXRhCi0gRm9yIHRoaXMgd2UgbmVlZCB0byB1c2UgdGhlICcqZm9ybXVsYSonIHN5bnRheAogICAgKyBGb3Igbm93LCBgeSB+IHhgIG1lYW5zIHB1dCBjb250aW51b3VzIHZhcmlhYmxlIGB5YCBvbiB0aGUgKnkqIGF4aXMgYW5kIGNhdGVnb3JpY2FsIGB4YCBvbiB0aGUgeCBheGlzCmBgYHtyfQpib3hwbG90KHBhdGllbnRzJFdlaWdodCB+IHBhdGllbnRzJFNleCkKCmBgYAoKLSBPdGhlciBhbHRlcm5hdGl2ZXMgdG8gY29uc2lkZXI6CiAgICAtIGBleGFtcGxlKGRvdGNoYXJ0KWAKICAgIC0gYGV4YW1wbGUoc3RyaXBjaGFydClgCiAgICAtIGBleGFtcGxlKHZpb3Bsb3QpICAjIEZyb20gdmlvcGxvdCBsaWJyYXJ5YAogICAgLSBgZXhhbXBsZShiZWVzd2FybSkgIyBGcm9tIGJlZXN3YXJtIGxpYnJhcnlgCgojIyBFeGVyY2lzZTogRXhlcmNpc2UgNGEKCi0gSW4gdGhlIGNvdXJzZSBmb2xkZXIgeW91IHdpbGwgZmluZCB0aGUgZmlsZSBgb3pvbmUuY3N2YDoKICAgICsgRGF0YSBkZXNjcmliaW5nIHdlYXRoZXIgY29uZGl0aW9ucyBpbiBOZXcgWW9yayBDaXR5IGluIDE5NzMsIG9idGFpbmVkIGZyb20gdGhlIFtzdXBwbGVtZW50YXJ5IGRhdGFdKGh0dHA6Ly9mYWN1bHR5Lndhc2hpbmd0b24uZWR1L2hlYWdlcnR5L0Jvb2tzL0Jpb3N0YXRpc3RpY3MvaW5kZXgtY2hhcHRlci5odG1sKSB0byAqQmlvc3RhdGlzdGljczogQSBNZXRob2RvbG9neSBmb3IgdGhlIEhlYWx0aCBTY2llbmNlcyoKICAgICsgRnVsbCBkZXNjcmlwdGlvbiBoZXJlOiBodHRwOi8vZmFjdWx0eS53YXNoaW5ndG9uLmVkdS9oZWFnZXJ0eS9Cb29rcy9CaW9zdGF0aXN0aWNzL0RBVEEvb3pvbmVkb2MudHh0CjEuIFJlYWQgdGhlc2UgZGF0YSBpbnRvIFIgdXNpbmcgYHJlYWQuY3N2YCBvciBgcmVhZC5kZWxpbWAgYXMgZGVzY3JpYmVkIGluIHRoZSBwcmV2aW91cyBzZWN0aW9uCiAgICArIHlvdSB3aWxsIG5lZWQgdG8gY2hvb3NlIHdoaWNoIGlzIGFwcHJvcHJpYXRlIGZvciB0aGUgZmlsZSB0eXBlCjIuIFdoYXQgZGF0YSB0eXBlcyBhcmUgcHJlc2VudD8gVHJ5IHRvIHRoaW5rIG9mIHdheXMgdG8gY3JlYXRlIHRoZSBmb2xsb3dpbmcgcGxvdHMgZnJvbSB0aGUgZGF0YQogICAgKyBTY2F0dGVyIHBsb3QgdHdvIHZhcmlhYmxlcy4gZS5nLiBTb2xhciBSYWRpYXRpb24gYWdhaW5zdCBPem9uZQogICAgKyBBIGhpc3RvZ3JhbS4gZS5nLiBXaW5kIFNwZWVkCiAgICArIEJveHBsb3Qgb2YgYSBjb250aW51b3VzIHZhcmlhYmxlIGFnYWluc3QgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gZS5nLiBPem9uZSBsZXZlbCBwZXIgbW9udGgKICAgIAohW10oaW1hZ2VzL2V4ZXJjaXNlNGEucG5nKQoKCgpgYGB7cn0KIyMjIFlvdXIgQW5zd2VyIEhlcmUgIyMjCgoKCmBgYAoKCiMjIFNpbXBsZSBjdXN0b21pc2F0aW9ucwoKLSBgcGxvdCgpYCBjb21lcyB3aXRoIGEgbGFyZ2UgY29sbGVjdGlvbiBvZiBhcmd1bWVudHMgdGhhdCBjYW4gYmUgc2V0IHdoZW4gd2UgY2FsbCB0aGUgZnVuY3Rpb246CiAgICArIFNlZSBgP3Bsb3RgIGFuZCBgP3BhcmAKLSBSZWNhbGwgdGhhdCwgdW5sZXNzIHNwZWNpZmllZCwgYXJndW1lbnRzIGhhdmUgYSBkZWZhdWx0IHZhbHVlCi0gV2UgY2FuIGNob29zZSB0byBkcmF3IGxpbmVzIG9uIHRoZSBwbG90IHJhdGhlciB0aGFuIHBvaW50cwogICAgKyBUaGUgcmVzdCBvZiB0aGUgcGxvdCByZW1haW5zIHRoZSBzYW1lCgpgYGB7cn0KcGxvdChwYXRpZW50cyRXZWlnaHQsIHR5cGUgPSAibCIpCmBgYAoKCi0gV2UgY2FuIGFsc28gaGF2ZSBib3RoIGxpbmVzIGFuZCBwb2ludHM6CgpgYGB7cn0KcGxvdChwYXRpZW50cyRXZWlnaHQsIHR5cGUgPSAiYiIpCmBgYAoKCi0gQWRkIGFuIGluZm9ybWF0aXZlIHRpdGxlIHRvIHRoZSBwbG90IHVzaW5nIHRoZSBgbWFpbmAgYXJndW1lbnQ6CgpgYGB7cn0KcGxvdChwYXRpZW50cyRBZ2UsIHBhdGllbnRzJFdlaWdodCwKICAgICBtYWluID0gIlJlbGF0aW9uc2hpcCBiZXR3ZWVuIFdlaWdodCBhbmQgQWdlIikKYGBgCgoKCi0gQWRkaW5nIHRoZSB4LWF4aXMgbGFiZWw6CmBgYHtyfQpwbG90KHBhdGllbnRzJEFnZSwgcGF0aWVudHMkV2VpZ2h0LCB4bGFiID0gIkFnZSIpCmBgYAoKLSBBZGRpbmcgdGhlIHktYXhpcyBsYWJlbDoKCmBgYHtyfQpwbG90KHBhdGllbnRzJEFnZSwgcGF0aWVudHMkV2VpZ2h0LCB5bGFiID0gIldlaWdodCIpCmBgYAoKLSBXZSBjYW4gc3BlY2lmaXkgbXVsdGlwbGUgYXJndW1lbnRzIGF0IG9uY2U6CiAgICArIGhlcmUgYHlsaW1gIGFuZCBgeGxpbWAgYXJlIHVzZWQgdG8gc3BlY2lmeSBheGlzIGxpbWl0cwpgYGB7cn0KcGxvdChwYXRpZW50cyRBZ2UscGF0aWVudHMkV2VpZ2h0LAogICAgIHlsYWI9IldlaWdodCIsCiAgICAgeGxhYj0iQWdlIiwKICAgICBtYWluPSJSZWxhdGlvbnNoaXAgYmV0d2VlbiBXZWlnaHQgYW5kIEFnZSIsCiAgICAgeGxpbT1jKDEwLDcwKSwKICAgICB5bGltPWMoNjAsODApKQoKYGBgCgojI0RlZmluaW5nIGEgY29sb3VyCgotIFIgY2FuIHJlY29nbmlzZSB2YXJpb3VzIHN0cmluZ3MsIHN1Y2ggYXMgYCJyZWQiYCwgYCJvcmFuZ2UiYCxgImdyZWVuImAsYCJibHVlImAsYCJ5ZWxsb3ciYC4uLgotIE9yIG1vcmUgZXhvdGljIG9uZXMgbGlrZSBgYHIgc2FtcGxlKGNvbG91cnMoKSw4KWBgLi4uCiAgICAgICArIFNlZSBgY29sb3VycygpYAotIFNlZSBodHRwOi8vd3d3LnN0YXQuY29sdW1iaWEuZWR1L350emhlbmcvZmlsZXMvUmNvbG9yLnBkZgotIENhbiBhbHNvIHVzZSAqKlIqKmVkICoqRyoqcmVlbiAqKkIqKmx1ZSBhbmQgaGV4YWRlY2ltYWwgdmFsdWVzOgoKICAgICAgICsgYHJnYigwLjcsIDAuNywgMC43KWAg4oaSIEEgbGlnaHQgZ3JleSBpbiBSR0IgZm9ybWF0YAogICAgICAgKyBgIiNCM0IzQjMiYCDihpIgIFRoZSBzYW1lIGxpZ2h0IGdyZXkgaW4gaGV4YWRlY2ltYWwKICAgICAgICsgYCIjMDAwMEZGODgiYOKGkiBBIHNlbWktdHJhbnNwYXJlbnQgYmx1ZSwgaW4gaGV4YWRlY2ltYWwKICAgICAgICAgICsgVGhlIGhleGFkZWNpbWFsIHN5c3RlbSBpcyB0aGUgbmF0aXZlIGNvbG91ciBzeXN0ZW0gZm9yIHNjcmVlbiB2aXN1YWxpc2F0aW9uIChlLmcuIHdlYnMpLiBJdCBpbmRpY2F0ZXMgdGhlIGludGVuc2l0eSBvZiBSZWQsIEdyZWVuIGFuZCBCbHVlIGJ5IHVzaW5nIHR3byBkaWdpdHMgZm9yIGVhY2ggY29sb3VyLCBpbiBhIHNjYWxlIGZyb20gMC05IGFuZCBBLUYgKDAgbWVhbmluZyBubyBpbnRlbnNpdHkgYW5kIEYgbWVhbmluZyBtb3N0IGludGVuc2UpCgoKQ2hhbmdpbmcgdGhlIGBjb2xgIGFyZ3VtZW50IHRvIGBwbG90KClgIGNoYW5nZXMgdGhlIGNvbG91ciB0aGF0IHRoZSBwb2ludHMgYXJlIHBsb3R0ZWQgaW46CgpgYGB7cn0KcGxvdChwYXRpZW50cyRBZ2UsIHBhdGllbnRzJFdlaWdodCwgY29sID0gInJlZCIpCmBgYAoKCiMjUGxvdHRpbmcgY2hhcmFjdGVycwoKLSBSIGNhbiB1c2UgYSB2YXJpZXR5IG9mICoqcCoqbG90dGluZyAqKmNoKiphcmFjdGVycwotIEVhY2ggb2Ygd2hpY2ggaGFzIGEgbnVtZXJpYyAqY29kZSogCgohW10oaW1hZ2VzL3BjaC5wbmcpCgpgYGB7cn0KcGxvdChwYXRpZW50cyRBZ2UsIHBhdGllbnRzJFdlaWdodCwgcGNoID0gMTYpCmBgYAoKCgotIE9yIHlvdSBjYW4gc3BlY2lmeSBhIGNoYXJhY3RlcjoKCmBgYHtyfQpwbG90KHBhdGllbnRzJEFnZSwgcGF0aWVudHMkV2VpZ2h0LCBwY2ggPSAiWCIpCmBgYAoKCiMjU2l6ZSBvZiBwb2ludHMKCioqQyoqaGFyYWN0ZXIgKipleCoqcGFuc2lvbjogTWFrZSB0aGUgc2l6ZSBvZiBwb2ludHMgMyB0aW1lcyBsYXJnZXIgdGhhbiB0aGUgZGVmYXVsdAoKYGBge3J9CnBsb3QocGF0aWVudHMkQWdlLCBwYXRpZW50cyRXZWlnaHQsIGNleCA9IDMpCmBgYAoKb3IgMjAlIG9mIHRoZSBvcmlnaW5hbCBzaXplCgpgYGB7cn0KcGxvdChwYXRpZW50cyRBZ2UsIHBhdGllbnRzJFdlaWdodCwgY2V4ID0gMC4yKQpgYGAKCiMjQ29sb3VycyBhbmQgY2hhcmFjdGVycyBhcyB2ZWN0b3JzCgotIFByZXZpb3VzbHkgd2UgaGF2ZSB1c2VkIGEgKnZlY3Rvciogb2YgbGVuZ3RoIDEgYXMgb3VyIHZhbHVlIG9mIGNvbG91ciBhbmQgY2hhcmFjdGVyCi0gV2UgY2FuIHVzZSBhIHZlY3RvciBvZiBhbnkgbGVuZ3RoOgogICAgKyB0aGUgdmFsdWVzIHdpbGwgZ2V0ICpyZWN5Y2xlZCogKHJlLXVzZWQpIHNvIHRoYXQgZWFjaCBwb2ludCBnZXRzIGFzc2lnbmVkIGEgdmFsdWUKLSBXZSBjYW4gdXNlIGEgcHJlLWRlZmluZWQgKioqY29sb3VyIHBhbGV0dGUqKiogKHNlZSBsYXRlcikKCmBgYHtyfQpwbG90KHBhdGllbnRzJEFnZSwgcGF0aWVudHMkV2VpZ2h0LCAKICAgICBjb2wgPSBjKCJyZWQiLCJibHVlIikpCmBgYAoKIyNPdGhlciBwbG90cyB1c2UgdGhlIHNhbWUgYXJndW1lbnRzCgotIE90aGVyIHBsb3R0aW5nIGZ1bmN0aW9ucyB1c2UgdGhlIHNhbWUgYXJndW1lbnRzIGFzIGBwbG90KClgCiAgICArIHRlY2huaWNhbCBleHBsYW5hdGlvbjogdGhlIGFyZ3VtZW50cyBhcmUgKidpbmhlcml0ZWQnKgoKYGBge3J9CmJveHBsb3QocGF0aWVudHMkV2VpZ2h0fnBhdGllbnRzJFNleCwKICAgICAgICB4bGFiID0gIlNleCIsCiAgICAgICAgeWxhYiA9ICJXZWlnaHQiLAogICAgICAgIG1haW4gPSAiUmVsYXRpb25zaGlwIGJldHdlZW4gV2VpZ2h0IGFuZCBHZW5kZXIiLAogICAgICAgIGNvbCAgPSBjKCJibHVlIiwieWVsbG93IikpCmBgYAoKCiMjRXhlcmNpc2U6IGV4ZXJjaXNlNGIKCi0gQ2FuIHlvdSByZS1jcmVhdGUgdGhlIGZvbGxvd2luZyBwbG90cz8gSGludDoKICAgICsgU2VlIHRoZSBgYnJlYWtzYCBhbmQgYGZyZXFgIGFyZ3VtZW50cyB0byBoaXN0IChgP2hpc3RgKSB0byBjcmVhdGUgMjAgYmlucyBhbmQgZGlzcGxheSBkZW5zaXR5IHJhdGhlciB0aGFuIGZyZXF1ZW5jeQogICAgKyBGb3IgdGhpcmQgcGxvdCwgc2VlIHRoZSByYWluYm93IGZ1bmN0aW9uIChgP3JhaW5ib3dgKQogICAgKyBEb24ndCB3b3JyeSB0b28gbXVjaCBhYm91dCBnZXR0aW5nIHRoZSBjb2xvdXJzIGV4YWN0bHkgY29ycmVjdAogICAgKyBUaGUgYGxhc2AgYXJndW1lbnQgY2hhbmdlcyB0aGUgbGFiZWwgb3JpZW50YXRpb24uIFNlZSBgP3BhcmAuCiAgICArIGxvb2sgYXQgdGhlIGFyZ3VtZW50cyB0byBgYm94cGxvdGAgdG8gc2VlIGhvdyB0byBjaGFuZ2UgdGhlIG5hbWVzIHByaW50ZWQgdW5kZXIgZWFjaCBib3gKICAgIAohW10oaW1hZ2VzL2V4ZXJjaXNlNGIucG5nKQoKYGBge3J9CiMjIyBZb3VyIEFuc3dlciBIZXJlICMjIwoKCmBgYAoKIyMgTW9yZSBvbiBjb2xvdXJzCgotIFRoZSAqKmByYWluYm93KClgKiogZnVuY3Rpb24gaXMgdXNlZCB0byBjcmVhdGUgYSB2ZWN0b3Igb2YgY29sb3VycyBmb3IgdGhlIGJveHBsb3Q7IGluIG90aGVyIHdvcmRzIGEgKioqcGFsZXR0ZSoqKjoKICAgICsgUmVkLCBPcmFuZ2UsIFllbGxvdywgR3JlZW4sIEJsdWUsIEluZGlnbywgVmlvbGV0LCBldGMuCiAgICArIE90aGVyIHBhbGV0dGUgZnVuY3Rpb25zIGF2YWlsYWJsZTogYGhlYXQuY29sb3JzKCksIHRlcnJhaW4uY29sb3JzKCksIHRvcG8uY29sb3JzKCksIGNtLmNvbG9ycygpYAogICAgKyBSZWQsIE9yYW5nZSwgWWVsbG93LCBHcmVlbiwgQmx1ZSwgSW5kaWdvLCBWaW9sZXQuLi4uZXRjCgogICAgCgoKLSBNb3JlIGFlc3RoZXRpY2FsbHktcGxlYXNpbmcgcGFsZXR0ZXMgYXJlIHByb3ZpZGVkIGJ5IHRoZSAqKmBSQ29sb3JCcmV3ZXJgKiogcGFja2FnZToKICAgICsgY2FuIGFsc28gY2hlY2sgZm9yIHBhbGV0dGVzIHRoYXQgYXJlIGFjY2VwdGVkIGZvciB0aG9zZSB3aXRoIGNvbG91ci1ibGluZG5lc3MKLSAgWW91IG1heSBuZWVkIHRvICppbnN0YWxsKiBgUkNvbG9yQnJld2VyYCB3aXRoIHRoZSBmb2xsb3dpbmcgbGluZSBvZiBjb2RlCiAgICArIHJlbWVtYmVyLCB5b3Ugb25seSBuZWVkIHRvIGRvIHRoaXMgb25jZSAKICAgIApgYGB7ciBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJSQ29sb3JCcmV3ZXIiKQpgYGAKCgpgYGB7cn0KbGlicmFyeShSQ29sb3JCcmV3ZXIpCmRpc3BsYXkuYnJld2VyLmFsbCgpCmRpc3BsYXkuYnJld2VyLmFsbChjb2xvcmJsaW5kRnJpZW5kbHkgPSBUUlVFKQpgYGAKCmBgYHtyfQp3ZWF0aGVyIDwtIHJlYWQuY3N2KCJvem9uZS5jc3YiKQpib3hwbG90KHdlYXRoZXIkVGVtcCB+IHdlYXRoZXIkTW9udGgsY29sPWJyZXdlci5wYWwoNSwiU2V0MSIpKQpgYGAKCgojRW5kIG9mIERheSAxCgojIyBUbyBjb21lIHRvbW9ycm93Li4uCi0gTW9yZSBjdXN0b21pc2F0aW9uIG9mIHBsb3RzCi0gU3RhdGlzdGljcwotIEZ1cnRoZXIgbWFuaXB1bGF0aW9uIG9mIGRhdGEKLSBSZXBvcnQgd3JpdGluZwo=