PeterMac Data Science’s modified version of material by the University of Cambridge (Mark Dunning, Suraj Menon and Aiora Zabala. Original material by Robert Stojnić, Laurent Gatto, Rob Foy, John Davey, Dávid Molnár and Ian Roberts)
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
- R tries to guess the most appropriate way to visualise the data, according to the type and dimensions of the object(s) provided
- 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()
- 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
Plotting a distribution: Boxplot
- The boxplot is commonly used in statistics to visualise a distribution:
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
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’
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
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=