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)
- We can include multiple factors
boxplot(patients$Weight ~ patients$Smokes + 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, ylab = \Weight\)
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 = 0.2)
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"))
We can use factors to determine which points to color
plot(patients$Age, patients$Weight,col = patients$Sex)
palette(c("firebrick1","dodgerblue"))
plot(patients$Age, patients$Weight,col = patients$Sex)
Other plots use the same arguments
- Other plotting functions use the same arguments as
plot()
- technical explanation: the arguments are ‘inherited’
### Your Answer Here ###
We can change color, and size according to data
plot(patients$Age, patients$Weight, col = patients$Sex, cex=patients$Age/10,pch=18)
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"))
LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFNvbHZpbmcgQmlvbG9naWNhbCBQcm9ibGVtcyBVc2luZyBSIC0gV2VlayAyIgpkYXRlOiAnYHIgZm9ybWF0KFN5cy50aW1lKCksICJMYXN0IG1vZGlmaWVkOiAlZCAlYiAlWSIpYCcKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKLS0tCipQZXRlck1hYyBEYXRhIFNjaWVuY2UncyBtb2RpZmllZCB2ZXJzaW9uIG9mIG1hdGVyaWFsIGJ5IHRoZSBVbml2ZXJzaXR5IG9mIENhbWJyaWRnZSAoTWFyayBEdW5uaW5nLCBTdXJhaiBNZW5vbiBhbmQgQWlvcmEgWmFiYWxhLiBPcmlnaW5hbCBtYXRlcmlhbCBieSBSb2JlcnQgU3Rvam5pxIcsCiAgTGF1cmVudCBHYXR0bywgUm9iIEZveSwgSm9obiBEYXZleSwgRMOhdmlkIE1vbG7DoXIgYW5kIElhbiBSb2JlcnRzKSoKCgojIDQuIFBsb3R0aW5nIGluIFIKCiMjUGxvdCBiYXNpY3MKCi0gQXMgd2UgaGF2ZSBoZWFyZCwgUiBoYXMgZXh0ZW5zaXZlIGdyYXBoaWNhbCBjYXBhYmlsaXRpZXMKLSAuLi5idXQgd2UgbmVlZCB0byBzdGFydCBzaW1wbGUKLSBXZSB3aWxsIGRlc2NyaWJlICpiYXNlKiBncmFwaGljcyBpbiBSOiB0aGUgcGxvdHMgYXZhaWxhYmxlIHdpdGggYW55IHN0YW5kYXJkIFIgaW5zdGFsbGF0aW9uCiAgICArIG90aGVyIG1vcmUgYWR2YW5jZWQgYWx0ZXJuYXRpdmVzIGFyZSwgZS5nLiwgYGxhdHRpY2VgLCBgZ2dwbG90MmAKICAgICsgU2VlIG91ciBbaW50ZXJtZWRpYXRlIFIgY291cnNlXShodHRwOi8vYmlvaW5mb3JtYXRpY3MtY29yZS1zaGFyZWQtdHJhaW5pbmcuZ2l0aHViLmlvL3ItaW50ZXJtZWRpYXRlLykgZm9yIGZhbmN5IGdyYXBoaWNzCi0gUGxvdHRpbmcgaW4gUiBpcyBhICp2YXN0KiB0b3BpYzoKICAgICsgV2UgY2Fubm90IGNvdmVyIGV2ZXJ5dGhpbmcKICAgICsgWW91IGNhbiB0aW5rZXIgd2l0aCBwbG90cyB0byB5b3VyIGhlYXJ0cyBjb250ZW50CiAgICArIEJlc3QgdG8gbGVhcm4gZnJvbSBleGFtcGxlczsgZS5nLiBbVGhlIFIgR3JhcGggR2FsbGVyeV0oaHR0cDovL3d3dy5yLWdyYXBoLWdhbGxlcnkuY29tLykKLSAqKipZb3UgbmVlZCB0byB0aGluayBhYm91dCBob3cgYmVzdCB0byB2aXN1YWxpc2UgeW91ciBkYXRhKioqIAogICAgKyBodHRwOi8vd3d3LmJpb2luZm9ybWF0aWNzLmJhYnJhaGFtLmFjLnVrL3RyYWluaW5nLmh0bWwjZmlndXJlZGVzaWduCi0gUiBjYW5ub3QgcHJldmVudCB5b3UgZnJvbSBjcmVhdGluZyBhIHBsb3R0aW5nIGRpc2FzdGVyOiAKICAgICsgaHR0cDovL3d3dy5idXNpbmVzc2luc2lkZXIuY29tL3RoZS0yNy13b3JzdC1jaGFydHMtb2YtYWxsLXRpbWUtMjAxMy02P29wPTEmSVI9VAogICAgCiMjTWFraW5nIGEgU2NhdHRlciBQbG90CgotIElmIGdpdmVuIGEgc2luZ2xlIHZlY3RvciBhcyBhbiBhcmd1bWVudCwgdGhlIGZ1bmN0aW9uICoqYHBsb3QoKWAqKiB3aWxsIG1ha2UgYSBzY2F0dGVyIHBsb3Qgd2l0aCB0aGUgKnZhbHVlcyogb2YgdGhlIHZlY3RvciBvbiB0aGUgKnkqIGF4aXMsIGFuZCAqaW5kaWNlcyogaW4gdGhlICp4KiBheGlzCiAgICArIGUuZy4gaXQgcHV0cyBhIHBvaW50IGF0OgogICAgICAgICsgeCA9IDEsIHkgPSA3MC44CiAgICAgICAgKyB4ID0gMiwgeSA9IDY3LjkgZXRjLi4uCi0gV2UgYXJlIGdvaW5nIHRvIGJlIHVzaW5nIHRoZSBwYXRpZW50cyBkYXRhIGZyYW1lLCByZWFkIHVzaW5nIHRoZSBmb2xsb3dpbmcgY29tbWFuZAoKYGBge3J9CnBhdGllbnRzIDwtIHJlYWQuZGVsaW0oInBhdGllbnQtaW5mby50eHQiKQpgYGAKClJlbWVtYmVyIHRoYXQgYCRgIGNhbiBiZSB1c2VkIHRvIGFjY2VzcyBhIHBhcnRpY3VsYXIgY29sdW1uLiBUaGUgcmVzdWx0IGlzIGEgdmVjdG9yLCB3aGljaCBpcyB0aGUgbW9zdC1iYXNpYyB0eXBlIG9mIGRhdGEgdXNlZCBpbiBwbG90dGluZwogICAgICAgIApgYGB7cn0KcGF0aWVudHMkV2VpZ2h0CmBgYAoKCi0gUiB0cmllcyB0byBndWVzcyB0aGUgbW9zdCBhcHByb3ByaWF0ZSB3YXkgdG8gdmlzdWFsaXNlIHRoZSBkYXRhLCBhY2NvcmRpbmcgdG8gdGhlIHR5cGUgYW5kIGRpbWVuc2lvbnMgb2YgdGhlIG9iamVjdChzKSBwcm92aWRlZAoKCmBgYHtyfQpwbG90KHBhdGllbnRzJFdlaWdodCkKYGBgCgotIEF4aXMgbGltaXRzLCBsYWJlbHMsIHRpdGxlcyBhcmUgaW5mZXJyZWQgZnJvbSB0aGUgZGF0YQogICAgKyBXZSBjYW4gbW9kaWZ5IHRoZXNlIGFzIHdlIHdpc2gsIGJ5IHNwZWNpZnlpbmcgKioqYXJndW1lbnRzKioqCgotIFdlIGNhbiBnaXZlIHR3byBhcmd1bWVudHMgdG8gYHBsb3QoKWA6CiAgICArIEluIG9yZGVyIHRvIHZpc3VhbGlzZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdHdvIHZhcmlhYmxlcwogICAgKyBJdCB3aWxsIHB1dCB0aGUgdmFsdWVzIGZyb20gdGhlICpmaXJzdCogYXJndW1lbnQgaW4gdGhlICp4KiBheGlzLCBhbmQgdmFsdWVzIGZyb20gdGhlICpzZWNvbmQqIGFyZ3VtZW50IG9uIHRoZSAqeSogYXhpcwoKYGBge3J9CnBhdGllbnRzJEFnZQpwbG90KHBhdGllbnRzJEFnZSwgcGF0aWVudHMkV2VpZ2h0KQpgYGAKCiMjTWFraW5nIGEgYmFycGxvdAoKLSBPdGhlciB0eXBlcyBvZiB2aXN1YWxpc2F0aW9uIGFyZSBhdmFpbGFibGU6CiAgICArIFRoZXNlIGFyZSBvZnRlbiBqdXN0IHNwZWNpYWwgY2FzZXMgb2YgdXNpbmcgdGhlIGBwbG90KClgIGZ1bmN0aW9uCiAgICArIE9uZSBzdWNoIGZ1bmN0aW9uIGlzIGBiYXJwbG90KClgCiAgICAKICAgIApgYGB7cn0KYmFycGxvdChwYXRpZW50cyRBZ2UpCmBgYAoKCi0gSXQgaXMgbW9yZSB1c3VhbCB0byBkaXNwbGF5IGNvdW50IGRhdGEgaW4gYSBiYXJwbG90CiAgICArIGUuZy4gdGhlIGNvdW50cyBvZiBhIHBhcnRpY3VsYXIgKioqY2F0ZWdvcmljYWwqKiogdmFyaWFibGUKCmBgYHtyfQpiYXJwbG90KHN1bW1hcnkocGF0aWVudHMkU2V4KSkKYGBgCgojI1Bsb3R0aW5nIGEgZGlzdHJpYnV0aW9uOiBIaXN0b2dyYW0KCi0gQSBoaXN0b2dyYW0gaXMgYSBwb3B1bGFyIHdheSBvZiB2aXN1YWxpc2luZyBhIGRpc3RyaWJ1dGlvbiBvZiAqKipjb250aW51b3VzKioqIGRhdGE6CiAgICArIFlvdSBjYW4gY2hhbmdlIHRoZSB3aWR0aCBvZiBiaW5zCiAgICArIFRoZSB5LWF4aXMgY2FuIGJlIGVpdGhlciBmcmVxdWVuY3kgb2YgZGVuc2l0eQoKYGBge3J9Cmhpc3QocGF0aWVudHMkV2VpZ2h0KQpgYGAKCgoKIyNQbG90dGluZyBhIGRpc3RyaWJ1dGlvbjogQm94cGxvdAoKLSBUaGUgYm94cGxvdCBpcyBjb21tb25seSB1c2VkIGluIHN0YXRpc3RpY3MgdG8gdmlzdWFsaXNlIGEgZGlzdHJpYnV0aW9uOgpgYGB7cn0KYm94cGxvdChwYXRpZW50cyRXZWlnaHQpCmBgYAoKLSBUaGUgYmxhY2sgc29saWQgbGluZSBpcyB0aGUgKioqbWVkaWFuKioqCi0gVGhlIHRvcCBhbmQgYm90dG9tIG9mIHRoZSBib3ggYXJlIHRoZSA3NXRoIGFuZCAyNXRoIHBlcmNlbnRpbGVzCiAgICAgKyBIZW5jZSwgdGhlIGRpc3RhbmNlIGJldHdlZW4gdGhlc2UgaXMgYSByZWZsZWN0aW9uIG9mIHRoZSAqc3ByZWFkKiBvZiB0aGUgZGF0YTsgdGhlIEludGVyLVF1YXJ0aWxlIFJhbmdlICgqKipJUVIqKiopCi0gV2hpc2tlcnMgYXJlIGRyYXduIGF0IDEuNSB4IElRUiBhbmQgLTEuNSB4IElRUgoKCi0gU29tZXRpbWVzIHdlIHdhbnQgdG8gY29tcGFyZSBkaXN0cmlidXRpb25zIGJldHdlZW4gZGlmZmVyZW50IGNhdGVnb3JpZXMgaW4gb3VyIGRhdGEKLSBGb3IgdGhpcyB3ZSBuZWVkIHRvIHVzZSB0aGUgJypmb3JtdWxhKicgc3ludGF4CiAgICArIEZvciBub3csIGB5IH4geGAgbWVhbnMgcHV0IGNvbnRpbnVvdXMgdmFyaWFibGUgYHlgIG9uIHRoZSAqeSogYXhpcyBhbmQgY2F0ZWdvcmljYWwgYHhgIG9uIHRoZSB4IGF4aXMKYGBge3J9CmJveHBsb3QocGF0aWVudHMkV2VpZ2h0IH4gcGF0aWVudHMkU2V4KQoKYGBgCgotIFdlIGNhbiBpbmNsdWRlIG11bHRpcGxlIGZhY3RvcnMKCmBgYHtyfQpib3hwbG90KHBhdGllbnRzJFdlaWdodCB+IHBhdGllbnRzJFNtb2tlcyArIHBhdGllbnRzJFNleCkKYGBgCgotIE90aGVyIGFsdGVybmF0aXZlcyB0byBjb25zaWRlcjoKICAgIC0gYGV4YW1wbGUoZG90Y2hhcnQpYAogICAgLSBgZXhhbXBsZShzdHJpcGNoYXJ0KWAKICAgIC0gYGV4YW1wbGUodmlvcGxvdCkgICMgRnJvbSB2aW9wbG90IGxpYnJhcnlgCiAgICAtIGBleGFtcGxlKGJlZXN3YXJtKSAjIEZyb20gYmVlc3dhcm0gbGlicmFyeWAKCiMjIEV4ZXJjaXNlOiBFeGVyY2lzZSA0YQoKLSBJbiB0aGUgY291cnNlIGZvbGRlciB5b3Ugd2lsbCBmaW5kIHRoZSBmaWxlIGBvem9uZS5jc3ZgOgogICAgKyBEYXRhIGRlc2NyaWJpbmcgd2VhdGhlciBjb25kaXRpb25zIGluIE5ldyBZb3JrIENpdHkgaW4gMTk3Mywgb2J0YWluZWQgZnJvbSB0aGUgW3N1cHBsZW1lbnRhcnkgZGF0YV0oaHR0cDovL2ZhY3VsdHkud2FzaGluZ3Rvbi5lZHUvaGVhZ2VydHkvQm9va3MvQmlvc3RhdGlzdGljcy9pbmRleC1jaGFwdGVyLmh0bWwpIHRvICpCaW9zdGF0aXN0aWNzOiBBIE1ldGhvZG9sb2d5IGZvciB0aGUgSGVhbHRoIFNjaWVuY2VzKgogICAgKyBGdWxsIGRlc2NyaXB0aW9uIGhlcmU6IGh0dHA6Ly9mYWN1bHR5Lndhc2hpbmd0b24uZWR1L2hlYWdlcnR5L0Jvb2tzL0Jpb3N0YXRpc3RpY3MvREFUQS9vem9uZWRvYy50eHQKMS4gUmVhZCB0aGVzZSBkYXRhIGludG8gUiB1c2luZyBgcmVhZC5jc3ZgIG9yIGByZWFkLmRlbGltYCBhcyBkZXNjcmliZWQgaW4gdGhlIHByZXZpb3VzIHNlY3Rpb24KICAgICsgeW91IHdpbGwgbmVlZCB0byBjaG9vc2Ugd2hpY2ggaXMgYXBwcm9wcmlhdGUgZm9yIHRoZSBmaWxlIHR5cGUKMi4gV2hhdCBkYXRhIHR5cGVzIGFyZSBwcmVzZW50PyBUcnkgdG8gdGhpbmsgb2Ygd2F5cyB0byBjcmVhdGUgdGhlIGZvbGxvd2luZyBwbG90cyBmcm9tIHRoZSBkYXRhCiAgICArIFNjYXR0ZXIgcGxvdCB0d28gdmFyaWFibGVzLiBlLmcuIFNvbGFyIFJhZGlhdGlvbiBhZ2FpbnN0IE96b25lCiAgICArIEEgaGlzdG9ncmFtLiBlLmcuIFdpbmQgU3BlZWQKICAgICsgQm94cGxvdCBvZiBhIGNvbnRpbnVvdXMgdmFyaWFibGUgYWdhaW5zdCBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlLiBlLmcuIE96b25lIGxldmVsIHBlciBtb250aAogICAgCiFbXShpbWFnZXMvZXhlcmNpc2U0YS5wbmcpCgoKCmBgYHtyfQojIyMgWW91ciBBbnN3ZXIgSGVyZSAjIyMKCgoKYGBgCgoKIyMgU2ltcGxlIGN1c3RvbWlzYXRpb25zCgotIGBwbG90KClgIGNvbWVzIHdpdGggYSBsYXJnZSBjb2xsZWN0aW9uIG9mIGFyZ3VtZW50cyB0aGF0IGNhbiBiZSBzZXQgd2hlbiB3ZSBjYWxsIHRoZSBmdW5jdGlvbjoKICAgICsgU2VlIGA/cGxvdGAgYW5kIGA/cGFyYAotIFJlY2FsbCB0aGF0LCB1bmxlc3Mgc3BlY2lmaWVkLCBhcmd1bWVudHMgaGF2ZSBhIGRlZmF1bHQgdmFsdWUKLSBXZSBjYW4gY2hvb3NlIHRvIGRyYXcgbGluZXMgb24gdGhlIHBsb3QgcmF0aGVyIHRoYW4gcG9pbnRzCiAgICArIFRoZSByZXN0IG9mIHRoZSBwbG90IHJlbWFpbnMgdGhlIHNhbWUKCmBgYHtyfQpwbG90KHBhdGllbnRzJFdlaWdodCwgdHlwZSA9ICJsIikKYGBgCgoKLSBXZSBjYW4gYWxzbyBoYXZlIGJvdGggbGluZXMgYW5kIHBvaW50czoKCmBgYHtyfQpwbG90KHBhdGllbnRzJFdlaWdodCwgdHlwZSA9ICJiIikKYGBgCgoKLSBBZGQgYW4gaW5mb3JtYXRpdmUgdGl0bGUgdG8gdGhlIHBsb3QgdXNpbmcgdGhlIGBtYWluYCBhcmd1bWVudDoKCmBgYHtyfQpwbG90KHBhdGllbnRzJEFnZSwgcGF0aWVudHMkV2VpZ2h0LAogICAgIG1haW4gPSAiUmVsYXRpb25zaGlwIGJldHdlZW4gV2VpZ2h0IGFuZCBBZ2UiKQpgYGAKCgoKLSBBZGRpbmcgdGhlIHgtYXhpcyBsYWJlbDoKYGBge3J9CnBsb3QocGF0aWVudHMkQWdlLCBwYXRpZW50cyRXZWlnaHQsIHhsYWIgPSAiQWdlIikKYGBgCgotIEFkZGluZyB0aGUgeS1heGlzIGxhYmVsOgoKYGBge3J9CnBsb3QocGF0aWVudHMkQWdlLCBwYXRpZW50cyRXZWlnaHQsIHlsYWIgPSAiV2VpZ2h0IikKYGBgCgotIFdlIGNhbiBzcGVjaWZpeSBtdWx0aXBsZSBhcmd1bWVudHMgYXQgb25jZToKICAgICsgaGVyZSBgeWxpbWAgYW5kIGB4bGltYCBhcmUgdXNlZCB0byBzcGVjaWZ5IGF4aXMgbGltaXRzCmBgYHtyfQpwbG90KHBhdGllbnRzJEFnZSxwYXRpZW50cyRXZWlnaHQsCiAgICAgeWxhYj0iV2VpZ2h0IiwKICAgICB4bGFiPSJBZ2UiLAogICAgIG1haW49IlJlbGF0aW9uc2hpcCBiZXR3ZWVuIFdlaWdodCBhbmQgQWdlIiwKICAgICB4bGltPWMoMTAsNzApLAogICAgIHlsaW09Yyg2MCw4MCkpCgpgYGAKCiMjRGVmaW5pbmcgYSBjb2xvdXIKCi0gUiBjYW4gcmVjb2duaXNlIHZhcmlvdXMgc3RyaW5ncywgc3VjaCBhcyBgInJlZCJgLCBgIm9yYW5nZSJgLGAiZ3JlZW4iYCxgImJsdWUiYCxgInllbGxvdyJgLi4uCi0gT3IgbW9yZSBleG90aWMgb25lcyBsaWtlIGBgciBzYW1wbGUoY29sb3VycygpLDgpYGAuLi4KICAgICAgICsgU2VlIGBjb2xvdXJzKClgCi0gU2VlIGh0dHA6Ly93d3cuc3RhdC5jb2x1bWJpYS5lZHUvfnR6aGVuZy9maWxlcy9SY29sb3IucGRmCi0gQ2FuIGFsc28gdXNlICoqUioqZWQgKipHKipyZWVuICoqQioqbHVlIGFuZCBoZXhhZGVjaW1hbCB2YWx1ZXM6CgogICAgICAgKyBgcmdiKDAuNywgMC43LCAwLjcpYCDihpIgQSBsaWdodCBncmV5IGluIFJHQiBmb3JtYXRgCiAgICAgICArIGAiI0IzQjNCMyJgIOKGkiAgVGhlIHNhbWUgbGlnaHQgZ3JleSBpbiBoZXhhZGVjaW1hbAogICAgICAgKyBgIiMwMDAwRkY4OCJg4oaSIEEgc2VtaS10cmFuc3BhcmVudCBibHVlLCBpbiBoZXhhZGVjaW1hbAogICAgICAgICAgKyBUaGUgaGV4YWRlY2ltYWwgc3lzdGVtIGlzIHRoZSBuYXRpdmUgY29sb3VyIHN5c3RlbSBmb3Igc2NyZWVuIHZpc3VhbGlzYXRpb24gKGUuZy4gd2VicykuIEl0IGluZGljYXRlcyB0aGUgaW50ZW5zaXR5IG9mIFJlZCwgR3JlZW4gYW5kIEJsdWUgYnkgdXNpbmcgdHdvIGRpZ2l0cyBmb3IgZWFjaCBjb2xvdXIsIGluIGEgc2NhbGUgZnJvbSAwLTkgYW5kIEEtRiAoMCBtZWFuaW5nIG5vIGludGVuc2l0eSBhbmQgRiBtZWFuaW5nIG1vc3QgaW50ZW5zZSkKCgpDaGFuZ2luZyB0aGUgYGNvbGAgYXJndW1lbnQgdG8gYHBsb3QoKWAgY2hhbmdlcyB0aGUgY29sb3VyIHRoYXQgdGhlIHBvaW50cyBhcmUgcGxvdHRlZCBpbjoKCmBgYHtyfQpwbG90KHBhdGllbnRzJEFnZSwgcGF0aWVudHMkV2VpZ2h0LCBjb2wgPSAicmVkIikKYGBgCgoKIyNQbG90dGluZyBjaGFyYWN0ZXJzCgotIFIgY2FuIHVzZSBhIHZhcmlldHkgb2YgKipwKipsb3R0aW5nICoqY2gqKmFyYWN0ZXJzCi0gRWFjaCBvZiB3aGljaCBoYXMgYSBudW1lcmljICpjb2RlKiAKCiFbXShpbWFnZXMvcGNoLnBuZykKCmBgYHtyfQpwbG90KHBhdGllbnRzJEFnZSwgcGF0aWVudHMkV2VpZ2h0LCBwY2ggPSAxNikKYGBgCgoKCi0gT3IgeW91IGNhbiBzcGVjaWZ5IGEgY2hhcmFjdGVyOgoKYGBge3J9CnBsb3QocGF0aWVudHMkQWdlLCBwYXRpZW50cyRXZWlnaHQsIHBjaCA9ICJYIikKYGBgCgoKIyNTaXplIG9mIHBvaW50cwoKKipDKipoYXJhY3RlciAqKmV4KipwYW5zaW9uOiBNYWtlIHRoZSBzaXplIG9mIHBvaW50cyAzIHRpbWVzIGxhcmdlciB0aGFuIHRoZSBkZWZhdWx0CgpgYGB7cn0KcGxvdChwYXRpZW50cyRBZ2UsIHBhdGllbnRzJFdlaWdodCwgY2V4ID0gMykKYGBgCgpvciAyMCUgb2YgdGhlIG9yaWdpbmFsIHNpemUKCmBgYHtyfQpwbG90KHBhdGllbnRzJEFnZSwgcGF0aWVudHMkV2VpZ2h0LCBjZXggPSAwLjIpCmBgYAoKIyNDb2xvdXJzIGFuZCBjaGFyYWN0ZXJzIGFzIHZlY3RvcnMKCi0gUHJldmlvdXNseSB3ZSBoYXZlIHVzZWQgYSAqdmVjdG9yKiBvZiBsZW5ndGggMSBhcyBvdXIgdmFsdWUgb2YgY29sb3VyIGFuZCBjaGFyYWN0ZXIKLSBXZSBjYW4gdXNlIGEgdmVjdG9yIG9mIGFueSBsZW5ndGg6CiAgICArIHRoZSB2YWx1ZXMgd2lsbCBnZXQgKnJlY3ljbGVkKiAocmUtdXNlZCkgc28gdGhhdCBlYWNoIHBvaW50IGdldHMgYXNzaWduZWQgYSB2YWx1ZQotIFdlIGNhbiB1c2UgYSBwcmUtZGVmaW5lZCAqKipjb2xvdXIgcGFsZXR0ZSoqKiAoc2VlIGxhdGVyKQoKYGBge3J9CnBsb3QocGF0aWVudHMkQWdlLCBwYXRpZW50cyRXZWlnaHQsIAogICAgIGNvbCA9IGMoInJlZCIsImJsdWUiKSkKYGBgCgpXZSBjYW4gdXNlIGZhY3RvcnMgdG8gZGV0ZXJtaW5lIHdoaWNoIHBvaW50cyB0byBjb2xvcgoKYGBge3J9CgpwbG90KHBhdGllbnRzJEFnZSwgcGF0aWVudHMkV2VpZ2h0LGNvbCA9IHBhdGllbnRzJFNleCkKCnBhbGV0dGUoYygiZmlyZWJyaWNrMSIsImRvZGdlcmJsdWUiKSkKcGxvdChwYXRpZW50cyRBZ2UsIHBhdGllbnRzJFdlaWdodCxjb2wgPSBwYXRpZW50cyRTZXgpCgpgYGAKCgojI090aGVyIHBsb3RzIHVzZSB0aGUgc2FtZSBhcmd1bWVudHMKCi0gT3RoZXIgcGxvdHRpbmcgZnVuY3Rpb25zIHVzZSB0aGUgc2FtZSBhcmd1bWVudHMgYXMgYHBsb3QoKWAKICAgICsgdGVjaG5pY2FsIGV4cGxhbmF0aW9uOiB0aGUgYXJndW1lbnRzIGFyZSAqJ2luaGVyaXRlZCcqCgpgYGB7cn0KYm94cGxvdChwYXRpZW50cyRXZWlnaHR+cGF0aWVudHMkU2V4LAogICAgICAgIHhsYWIgPSAiU2V4IiwKICAgICAgICB5bGFiID0gIldlaWdodCIsCiAgICAgICAgbWFpbiA9ICJSZWxhdGlvbnNoaXAgYmV0d2VlbiBXZWlnaHQgYW5kIEdlbmRlciIsCiAgICAgICAgY29sICA9IGMoImJsdWUiLCJ5ZWxsb3ciKSkKYGBgCgojIyBXZSBjYW4gY2hhbmdlIGNvbG9yLCBhbmQgc2l6ZSBhY2NvcmRpbmcgdG8gZGF0YQpgYGB7cn0KcGxvdChwYXRpZW50cyRBZ2UsIHBhdGllbnRzJFdlaWdodCwgY29sID0gcGF0aWVudHMkU2V4LCBjZXg9cGF0aWVudHMkQWdlLzEwLHBjaD0xOCkKYGBgCgoKIyNFeGVyY2lzZTogZXhlcmNpc2U0YgoKLSBDYW4geW91IHJlLWNyZWF0ZSB0aGUgZm9sbG93aW5nIHBsb3RzPyBIaW50OgogICAgKyBTZWUgdGhlIGBicmVha3NgIGFuZCBgZnJlcWAgYXJndW1lbnRzIHRvIGhpc3QgKGA/aGlzdGApIHRvIGNyZWF0ZSAyMCBiaW5zIGFuZCBkaXNwbGF5IGRlbnNpdHkgcmF0aGVyIHRoYW4gZnJlcXVlbmN5CiAgICArIEZvciB0aGlyZCBwbG90LCBzZWUgdGhlIHJhaW5ib3cgZnVuY3Rpb24gKGA/cmFpbmJvd2ApCiAgICArIERvbid0IHdvcnJ5IHRvbyBtdWNoIGFib3V0IGdldHRpbmcgdGhlIGNvbG91cnMgZXhhY3RseSBjb3JyZWN0CiAgICArIFRoZSBgbGFzYCBhcmd1bWVudCBjaGFuZ2VzIHRoZSBsYWJlbCBvcmllbnRhdGlvbi4gU2VlIGA/cGFyYC4KICAgICsgbG9vayBhdCB0aGUgYXJndW1lbnRzIHRvIGBib3hwbG90YCB0byBzZWUgaG93IHRvIGNoYW5nZSB0aGUgbmFtZXMgcHJpbnRlZCB1bmRlciBlYWNoIGJveAogICAgCiFbXShpbWFnZXMvZXhlcmNpc2U0Yi5wbmcpCgpgYGB7cn0KIyMjIFlvdXIgQW5zd2VyIEhlcmUgIyMjCgoKYGBgCgojIyBNb3JlIG9uIGNvbG91cnMKCi0gVGhlICoqYHJhaW5ib3coKWAqKiBmdW5jdGlvbiBpcyB1c2VkIHRvIGNyZWF0ZSBhIHZlY3RvciBvZiBjb2xvdXJzIGZvciB0aGUgYm94cGxvdDsgaW4gb3RoZXIgd29yZHMgYSAqKipwYWxldHRlKioqOgogICAgKyBSZWQsIE9yYW5nZSwgWWVsbG93LCBHcmVlbiwgQmx1ZSwgSW5kaWdvLCBWaW9sZXQsIGV0Yy4KICAgICsgT3RoZXIgcGFsZXR0ZSBmdW5jdGlvbnMgYXZhaWxhYmxlOiBgaGVhdC5jb2xvcnMoKSwgdGVycmFpbi5jb2xvcnMoKSwgdG9wby5jb2xvcnMoKSwgY20uY29sb3JzKClgCiAgICArIFJlZCwgT3JhbmdlLCBZZWxsb3csIEdyZWVuLCBCbHVlLCBJbmRpZ28sIFZpb2xldC4uLi5ldGMKCiAgICAKCgotIE1vcmUgYWVzdGhldGljYWxseS1wbGVhc2luZyBwYWxldHRlcyBhcmUgcHJvdmlkZWQgYnkgdGhlICoqYFJDb2xvckJyZXdlcmAqKiBwYWNrYWdlOgogICAgKyBjYW4gYWxzbyBjaGVjayBmb3IgcGFsZXR0ZXMgdGhhdCBhcmUgYWNjZXB0ZWQgZm9yIHRob3NlIHdpdGggY29sb3VyLWJsaW5kbmVzcwotICBZb3UgbWF5IG5lZWQgdG8gKmluc3RhbGwqIGBSQ29sb3JCcmV3ZXJgIHdpdGggdGhlIGZvbGxvd2luZyBsaW5lIG9mIGNvZGUKICAgICsgcmVtZW1iZXIsIHlvdSBvbmx5IG5lZWQgdG8gZG8gdGhpcyBvbmNlIAogICAgCmBgYHtyIGV2YWw9RkFMU0V9Cmluc3RhbGwucGFja2FnZXMoIlJDb2xvckJyZXdlciIpCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKZGlzcGxheS5icmV3ZXIuYWxsKCkKZGlzcGxheS5icmV3ZXIuYWxsKGNvbG9yYmxpbmRGcmllbmRseSA9IFRSVUUpCmBgYAoKYGBge3J9CndlYXRoZXIgPC0gcmVhZC5jc3YoIm96b25lLmNzdiIpCmJveHBsb3Qod2VhdGhlciRUZW1wIH4gd2VhdGhlciRNb250aCxjb2w9YnJld2VyLnBhbCg1LCJTZXQxIikpCmBgYAo=