In this file, we will import our already semi-cleaned up stable
isotope dataset and explore it using some basic summary statistics,
visualisation and tests.
Import the data
Your data should already have been tidied up, probably in Excel, so
as to remove any of the additional lines at the start that often come
back from analytical laboratories. As with all data files, your first
line (row) should be the names of each of the columns. When naming
columns, it is important to not use any unusual, non-standard
characters, as these can confuse the functions that are parsing (reading
and processing) the datafile: if you need to separate words in a column
header you should use “_” or “.”, e.g. “delta_C” or “delta.C” but not
“delta/C”.
Thereafter, unless otherwise stated (as is the case when supplying
source data for siar for example, which we will come to
later in the course) each row should represent a single, unique
observation (sample) with corresponding name of the sample, \(\delta^{13}\text{C}\), \(\delta^{15}\text{N}\) values etc…
You then have a choice of how to save so that R can more easily
import it. I favour comma separated files (*.csv) whereby
each cell is delimited (separated) by a comma “,”. Depending on where in
the world you are from, you may need to be very careful about this, as
some regions often us the comma to indicate thousands in numers, so
1000 might entered or appear as 1,000 in
Excel. Similarly, some regions use the comma to indicate a decimal
place, so 10.34 might be 10,34. This can
seriously mess up the importation process. Sometimes you have to change
the region settings on your computer in order to change this
automatic behaviour in Excel, or you can do a “find and replace” in the
*.csv file if you open it in a basic text editor after
saving it. Alternatively again, you might chose to use a different
delimiter, such as “;” to separate the cells in your dataset, and if you
do so, you can tell R this is the case by exploring the help files
associated with ?read.table.
The function read.csv() is a shortcut to importing
*.csv files and in my experience is the most stable
function to use. When calling it, I specify the name of the file (and
location if it is not in the current working directory) and that the
first row contain the header names (header = TRUE). I also
like to not let the function automatically interpret strings of letters
in cells as being special factor type data which is R’s way
of encoding categorical variables. My view is that if we want a column
to be a factor, we should do it in the code ourselves, as we may not
want all our character data to be factors, and in any case, most R
functions will convert them to factors internally if when presented with
them.
# import the data.
# If you get an error about "No such file or directory" then you
# need to take care about where R is currently working, and where your
# data file is in relation to this.
mydata <- read.csv("Practical01.csv",
header = TRUE, stringsAsFactors = FALSE)
# verify that our data looks correct by printing the first1 10 lines
# to screen
head(mydata)
# if the data is not a massive dataset, you might like to look
# at it all
# mydata
What are my data?
One of the first things you should do with an imported dataset, is to
take some time to understand how it is encoded, and how R is going to
treat each variable. The function str does this by telling
us what type of encoding each of the columns in the data are.
In this case, mydata is a data.frame
object, and inside it, Taxon and ID are
character vectors, while the rest are numeric.
str(mydata)
'data.frame': 60 obs. of 5 variables:
$ Taxon: chr "POM" "POM" "POM" "POM" ...
$ ID : chr "AF_POM01" "AF_POM02" "AF_POM03" "AF_POM04" ...
$ d13C : num -20.5 -19.9 -19.4 -20.3 -19.1 ...
$ d15N : num 13.6 12.5 14.1 13.1 12.5 ...
$ C.N : num 9.5 8 9.1 8.1 7.3 7.7 9.3 9 9.2 10 ...
The next obvious thing we might want to know is how big are these
numbers, and how many observations we have etc… Accessing columns within
a data.frame object is achieved using the $ notation, so
mydata$Taxon is the vector of characters with the taxon
identifier for each observation. N.B. in a previous existence I
advocated the use of the function attach() but no longer: it can get you
into all manner of mischief that is best avoided.
# prove to ourselves that the $ notation works.
mydata$Taxon
[1] "POM" "POM" "POM" "POM" "POM" "Benthic.algae" "Benthic.algae" "Benthic.algae" "Benthic.algae" "Benthic.algae" "Fissurella" "Fissurella"
[13] "Fissurella" "Fissurella" "Fissurella" "Fissurella" "Fissurella" "Fissurella" "Fissurella" "Fissurella" "Perumytilus" "Perumytilus" "Perumytilus" "Perumytilus"
[25] "Perumytilus" "Perumytilus" "Perumytilus" "Perumytilus" "Perumytilus" "Perumytilus" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla"
[37] "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla"
[49] "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla" "Cabrilla"
# a basic summary of each column in the data.frame
summary(mydata)
Taxon ID d13C d15N C.N
Length:60 Length:60 Min. :-23.10 Min. :11.84 Min. : 3.100
Class :character Class :character 1st Qu.:-18.95 1st Qu.:15.62 1st Qu.: 3.575
Mode :character Mode :character Median :-17.47 Median :17.92 Median : 4.050
Mean :-17.25 Mean :17.80 Mean : 5.275
3rd Qu.:-15.57 3rd Qu.:20.58 3rd Qu.: 6.125
Max. : -6.20 Max. :23.38 Max. :12.500
# how many observations do we have per Taxon might also be of use
table(mydata$Taxon)
Benthic.algae Cabrilla Fissurella Perumytilus POM
5 30 10 10 5
It is not so easy to get summary statistics on each Taxonomic group
within our dataset using the basic R functions. There are many packages
out there to help, but Hadley Wickham’s suite of packages are impossible
to beat in my opinion, and we will be using his ggplot2
package for pretty graphs later on so lets stick with the “tidyverse”
set of functions.
# Install tidyverse if required
# install.packages('tidyverse')
# load the library
library(tidyverse)
# generate summary statistics by group
mydata %>%
group_by(Taxon) %>%
summarise(count = n(),
mC = mean(d13C),
sdC = sd(d13C),
mN = mean(d15N),
sdN = sd(d15N),
mCN = mean(C.N),
sdCN = sd(C.N))
NA
NA
Basic Visualisation
Visualising the data is often the next step. You can generate
histograms of the data very easily.
# set up a multi-panel plot, 1 row, 3 columns
par(mfrow = c(1,3))
hist(mydata$d13C)
hist(mydata$d15N)
hist(mydata$C.N)

We might also be interested visualising the correlations among
variables in our dataset. The pairs function does this very
nicely, and should be something you use regularly at the start of
analyses since you get the histograms, and the pair-wise scatter plots
and correlations all in one figure. The associated help function has
lots of examples and explains more about where the following code comes
from ?pairs.
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# These functions are copied from the help file for pairs()
## put histograms on the diagonal
panel.hist <- function(x, ...)
{
usr <- par("usr"); on.exit(par(usr))
par(usr = c(usr[1:2], 0, 1.5) )
h <- hist(x, plot = FALSE)
breaks <- h$breaks; nB <- length(breaks)
y <- h$counts; y <- y/max(y)
rect(breaks[-nB], 0, breaks[-1], y, col = "cyan", ...)
}
panel.cor <- function(x, y, digits = 2, prefix = "", cex.cor, ...)
{
usr <- par("usr"); on.exit(par(usr))
par(usr = c(0, 1, 0, 1))
r <- cor(x, y)
txt <- format(c(r, 0.123456789), digits = digits)[1]
txt <- paste0(prefix, txt)
if(missing(cex.cor)) cex.cor <- 0.8/strwidth(txt)
# text(0.5, 0.5, txt, cex = cex.cor * r)
text(0.5, 0.5, txt, cex = 3)
}
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# A basic pairs() plot of the numeric data
# These two lines of code are identical, but use the
# dplyr functions slightly differently
# pairs(select(mydata, d13C, d15N, C.N))
# pairs(mydata %>% select(d13C, d15N, C.N))
# this select function comes from the package dplyr and allows us to
# select a subset of columns to plot.
pairs(select(mydata, d13C, d15N, C.N),
diag.panel = panel.hist,
upper.panel = panel.smooth,
lower.panel = panel.cor)

NA
NA
Boxplots are a quick way to visualise the variation among taxa. Note
the use of the ~ which means d13C ~ Taxon
reads as, do something, in this case draw a boxplot, of
d13C as a function of Taxon. This
formula approach then allows us to tell it to find these
variables in mydata and then finally we specify a main
label for each graph for this summary so we can keep track of what we
are looking at.
# first Carbon
boxplot(d13C ~ Taxon, data = mydata, main = "d13C")

# second Nitrogen
boxplot(d15N ~ Taxon, data = mydata, main = "d15N")

# third the C/N ratio
boxplot(C.N ~ Taxon, data = mydata, main = "C/N ratio")

QQ-plots are also a great way to visualise how normal your data are.
If they are perfectly normally distributed then they will sit on the
diagonal line.
# another 1x3 panel plot
par(mfrow = c(1,3))
qqnorm(mydata$d13C)
qqline(mydata$d13C)
qqnorm(mydata$d15N)
qqline(mydata$d15N)
qqnorm(mydata$C.N)
qqline(mydata$C.N)

One of the d13C values for the Benthic Algae appears to be very high
in relation to the others, both on the boxplots and the qq-plots. If you
are satisfied this is an error in the sample process which is entirely
possible, then you may choose to remove it. But, I would caution against
taking a strict approach based on statistical tests designed to find
outliers, especially when your sample size within a group is low (n = 4
in this case).
If you wanted, you could remove this data point, which we will do in
this case.
subset_mydata <- mydata %>% filter(d13C < max(d13C))
# check our boxplots again
boxplot(d13C ~ Taxon, data = subset_mydata, main = "d13C")

LS0tCnRpdGxlOiAiRmlyc3QgTG9vayBBdCBTSUEgRGF0YSIKYXV0aG9yOiAiQW5kcmV3IEwgSmFja3NvbiAmIENocmlzIEhhcnJvZCIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNikKYGBgCgoKSW4gdGhpcyBmaWxlLCB3ZSB3aWxsIGltcG9ydCBvdXIgYWxyZWFkeSBzZW1pLWNsZWFuZWQgdXAgc3RhYmxlIGlzb3RvcGUgZGF0YXNldCBhbmQgZXhwbG9yZSBpdCB1c2luZyBzb21lIGJhc2ljIHN1bW1hcnkgc3RhdGlzdGljcywgdmlzdWFsaXNhdGlvbiBhbmQgdGVzdHMuCgojIyBJbXBvcnQgdGhlIGRhdGEKCllvdXIgZGF0YSBzaG91bGQgYWxyZWFkeSBoYXZlIGJlZW4gdGlkaWVkIHVwLCBwcm9iYWJseSBpbiBFeGNlbCwgc28gYXMgdG8gcmVtb3ZlIGFueSBvZiB0aGUgYWRkaXRpb25hbCBsaW5lcyBhdCB0aGUgc3RhcnQgdGhhdCBvZnRlbiBjb21lIGJhY2sgZnJvbSBhbmFseXRpY2FsIGxhYm9yYXRvcmllcy4gQXMgd2l0aCBhbGwgZGF0YSBmaWxlcywgeW91ciBmaXJzdCBsaW5lIChyb3cpIHNob3VsZCBiZSB0aGUgbmFtZXMgb2YgZWFjaCBvZiB0aGUgY29sdW1ucy4gV2hlbiBuYW1pbmcgY29sdW1ucywgaXQgaXMgaW1wb3J0YW50IHRvIG5vdCB1c2UgYW55IHVudXN1YWwsIG5vbi1zdGFuZGFyZCBjaGFyYWN0ZXJzLCBhcyB0aGVzZSBjYW4gY29uZnVzZSB0aGUgZnVuY3Rpb25zIHRoYXQgYXJlIHBhcnNpbmcgKHJlYWRpbmcgYW5kIHByb2Nlc3NpbmcpIHRoZSBkYXRhZmlsZTogaWYgeW91IG5lZWQgdG8gc2VwYXJhdGUgd29yZHMgaW4gYSBjb2x1bW4gaGVhZGVyIHlvdSBzaG91bGQgdXNlICJfIiBvciAiLiIsIGUuZy4gImRlbHRhX0MiIG9yICJkZWx0YS5DIiBidXQgbm90ICJkZWx0YS9DIi4KClRoZXJlYWZ0ZXIsIHVubGVzcyBvdGhlcndpc2Ugc3RhdGVkIChhcyBpcyB0aGUgY2FzZSB3aGVuIHN1cHBseWluZyBzb3VyY2UgZGF0YSBmb3IgYHNpYXJgIGZvciBleGFtcGxlLCB3aGljaCB3ZSB3aWxsIGNvbWUgdG8gbGF0ZXIgaW4gdGhlIGNvdXJzZSkgZWFjaCByb3cgc2hvdWxkIHJlcHJlc2VudCBhIHNpbmdsZSwgdW5pcXVlIG9ic2VydmF0aW9uIChzYW1wbGUpIHdpdGggY29ycmVzcG9uZGluZyBuYW1lIG9mIHRoZSBzYW1wbGUsICRcZGVsdGFeezEzfVx0ZXh0e0N9JCwgJFxkZWx0YV57MTV9XHRleHR7Tn0kIHZhbHVlcyBldGMuLi4KCllvdSB0aGVuIGhhdmUgYSBjaG9pY2Ugb2YgaG93IHRvIHNhdmUgc28gdGhhdCBSIGNhbiBtb3JlIGVhc2lseSBpbXBvcnQgaXQuIEkgZmF2b3VyIGNvbW1hIHNlcGFyYXRlZCBmaWxlcyAoYCouY3N2YCkgd2hlcmVieSBlYWNoIGNlbGwgaXMgZGVsaW1pdGVkIChzZXBhcmF0ZWQpIGJ5IGEgY29tbWEgIiwiLiBEZXBlbmRpbmcgb24gd2hlcmUgaW4gdGhlIHdvcmxkIHlvdSBhcmUgZnJvbSwgeW91IG1heSBuZWVkIHRvIGJlIHZlcnkgY2FyZWZ1bCBhYm91dCB0aGlzLCBhcyBzb21lIHJlZ2lvbnMgb2Z0ZW4gdXMgdGhlIGNvbW1hIHRvIGluZGljYXRlIHRob3VzYW5kcyBpbiBudW1lcnMsIHNvIGAxMDAwYCBtaWdodCBlbnRlcmVkIG9yIGFwcGVhciBhcyBgMSwwMDBgIGluIEV4Y2VsLiBTaW1pbGFybHksIHNvbWUgcmVnaW9ucyB1c2UgdGhlIGNvbW1hIHRvIGluZGljYXRlIGEgZGVjaW1hbCBwbGFjZSwgc28gYDEwLjM0YCBtaWdodCBiZSBgMTAsMzRgLiBUaGlzIGNhbiBzZXJpb3VzbHkgbWVzcyB1cCB0aGUgaW1wb3J0YXRpb24gcHJvY2Vzcy4gU29tZXRpbWVzIHlvdSBoYXZlIHRvIGNoYW5nZSB0aGUgKnJlZ2lvbiogc2V0dGluZ3Mgb24geW91ciBjb21wdXRlciBpbiBvcmRlciB0byBjaGFuZ2UgdGhpcyBhdXRvbWF0aWMgYmVoYXZpb3VyIGluIEV4Y2VsLCBvciB5b3UgY2FuIGRvIGEgImZpbmQgYW5kIHJlcGxhY2UiIGluIHRoZSBgKi5jc3ZgIGZpbGUgaWYgeW91IG9wZW4gaXQgaW4gYSBiYXNpYyB0ZXh0IGVkaXRvciBhZnRlciBzYXZpbmcgaXQuIEFsdGVybmF0aXZlbHkgYWdhaW4sIHlvdSBtaWdodCBjaG9zZSB0byB1c2UgYSBkaWZmZXJlbnQgZGVsaW1pdGVyLCBzdWNoIGFzICI7IiB0byBzZXBhcmF0ZSB0aGUgY2VsbHMgaW4geW91ciBkYXRhc2V0LCBhbmQgaWYgeW91IGRvIHNvLCB5b3UgY2FuIHRlbGwgUiB0aGlzIGlzIHRoZSBjYXNlIGJ5IGV4cGxvcmluZyB0aGUgaGVscCBmaWxlcyBhc3NvY2lhdGVkIHdpdGggYD9yZWFkLnRhYmxlYC4KClRoZSBmdW5jdGlvbiBgcmVhZC5jc3YoKWAgaXMgYSBzaG9ydGN1dCB0byBpbXBvcnRpbmcgYCouY3N2YCBmaWxlcyBhbmQgaW4gbXkgZXhwZXJpZW5jZSBpcyB0aGUgbW9zdCBzdGFibGUgZnVuY3Rpb24gdG8gdXNlLiBXaGVuIGNhbGxpbmcgaXQsIEkgc3BlY2lmeSB0aGUgbmFtZSBvZiB0aGUgZmlsZSAoYW5kIGxvY2F0aW9uIGlmIGl0IGlzIG5vdCBpbiB0aGUgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeSkgYW5kIHRoYXQgdGhlIGZpcnN0IHJvdyBjb250YWluIHRoZSBoZWFkZXIgbmFtZXMgKGBoZWFkZXIgPSBUUlVFYCkuIEkgYWxzbyBsaWtlIHRvIG5vdCBsZXQgdGhlIGZ1bmN0aW9uIGF1dG9tYXRpY2FsbHkgaW50ZXJwcmV0IHN0cmluZ3Mgb2YgbGV0dGVycyBpbiBjZWxscyBhcyBiZWluZyBzcGVjaWFsIGBmYWN0b3JgIHR5cGUgZGF0YSB3aGljaCBpcyBSJ3Mgd2F5IG9mIGVuY29kaW5nIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4gTXkgdmlldyBpcyB0aGF0IGlmIHdlIHdhbnQgYSBjb2x1bW4gdG8gYmUgYSBmYWN0b3IsIHdlIHNob3VsZCBkbyBpdCBpbiB0aGUgY29kZSBvdXJzZWx2ZXMsIGFzIHdlIG1heSBub3Qgd2FudCBhbGwgb3VyIGNoYXJhY3RlciBkYXRhIHRvIGJlIGZhY3RvcnMsIGFuZCBpbiBhbnkgY2FzZSwgbW9zdCBSIGZ1bmN0aW9ucyB3aWxsIGNvbnZlcnQgdGhlbSB0byBmYWN0b3JzIGludGVybmFsbHkgaWYgd2hlbiBwcmVzZW50ZWQgd2l0aCB0aGVtLgoKCmBgYHtyIGltcG9ydC1kYXRhfQojIGltcG9ydCB0aGUgZGF0YS4gCiMgSWYgeW91IGdldCBhbiBlcnJvciBhYm91dCAiTm8gc3VjaCBmaWxlIG9yIGRpcmVjdG9yeSIgdGhlbiB5b3UgCiMgbmVlZCB0byB0YWtlIGNhcmUgYWJvdXQgd2hlcmUgUiBpcyBjdXJyZW50bHkgd29ya2luZywgYW5kIHdoZXJlIHlvdXIgCiMgZGF0YSBmaWxlIGlzIGluIHJlbGF0aW9uIHRvIHRoaXMuCm15ZGF0YSA8LSByZWFkLmNzdigiUHJhY3RpY2FsMDEuY3N2IiwgCiAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgojIHZlcmlmeSB0aGF0IG91ciBkYXRhIGxvb2tzIGNvcnJlY3QgYnkgcHJpbnRpbmcgdGhlIGZpcnN0MSAxMCBsaW5lcwojIHRvIHNjcmVlbgpoZWFkKG15ZGF0YSkKCiMgaWYgdGhlIGRhdGEgaXMgbm90IGEgbWFzc2l2ZSBkYXRhc2V0LCB5b3UgbWlnaHQgbGlrZSB0byBsb29rICAKIyBhdCBpdCBhbGwKIyBteWRhdGEKYGBgCgojIyBXaGF0IGFyZSBteSBkYXRhPwoKT25lIG9mIHRoZSBmaXJzdCB0aGluZ3MgeW91IHNob3VsZCBkbyB3aXRoIGFuIGltcG9ydGVkIGRhdGFzZXQsIGlzIHRvIHRha2Ugc29tZSB0aW1lIHRvIHVuZGVyc3RhbmQgaG93IGl0IGlzIGVuY29kZWQsIGFuZCBob3cgUiBpcyBnb2luZyB0byB0cmVhdCBlYWNoIHZhcmlhYmxlLiBUaGUgZnVuY3Rpb24gYHN0cmAgZG9lcyB0aGlzIGJ5IHRlbGxpbmcgdXMgd2hhdCB0eXBlIG9mIGVuY29kaW5nIGVhY2ggb2YgdGhlIGNvbHVtbnMgaW4gdGhlIGRhdGEgYXJlLgoKSW4gdGhpcyBjYXNlLCBgbXlkYXRhYCBpcyBhIGBkYXRhLmZyYW1lYCBvYmplY3QsIGFuZCBpbnNpZGUgaXQsIGBUYXhvbmAgYW5kIGBJRGAgYXJlIGNoYXJhY3RlciB2ZWN0b3JzLCB3aGlsZSB0aGUgcmVzdCBhcmUgYG51bWVyaWNgLgoKYGBge3Igd2hhdC1hcmUtZGF0YX0Kc3RyKG15ZGF0YSkKYGBgCgpUaGUgbmV4dCBvYnZpb3VzIHRoaW5nIHdlIG1pZ2h0IHdhbnQgdG8ga25vdyBpcyBob3cgYmlnIGFyZSB0aGVzZSBudW1iZXJzLCBhbmQgaG93IG1hbnkgb2JzZXJ2YXRpb25zIHdlIGhhdmUgZXRjLi4uIEFjY2Vzc2luZyBjb2x1bW5zIHdpdGhpbiBhIGRhdGEuZnJhbWUgb2JqZWN0IGlzIGFjaGlldmVkIHVzaW5nIHRoZSBgJGAgbm90YXRpb24sIHNvIGBteWRhdGEkVGF4b25gIGlzIHRoZSB2ZWN0b3Igb2YgY2hhcmFjdGVycyB3aXRoIHRoZSB0YXhvbiBpZGVudGlmaWVyIGZvciBlYWNoIG9ic2VydmF0aW9uLiAqTi5CLiBpbiBhIHByZXZpb3VzIGV4aXN0ZW5jZSBJIGFkdm9jYXRlZCB0aGUgdXNlIG9mIHRoZSBmdW5jdGlvbiBhdHRhY2goKSBidXQgbm8gbG9uZ2VyOiBpdCBjYW4gZ2V0IHlvdSBpbnRvIGFsbCBtYW5uZXIgb2YgbWlzY2hpZWYgdGhhdCBpcyBiZXN0IGF2b2lkZWQuKgoKYGBge3Igc3VtbWFyeS1kYXRhfQoKIyBwcm92ZSB0byBvdXJzZWx2ZXMgdGhhdCB0aGUgJCBub3RhdGlvbiB3b3Jrcy4KbXlkYXRhJFRheG9uCgojIGEgYmFzaWMgc3VtbWFyeSBvZiBlYWNoIGNvbHVtbiBpbiB0aGUgZGF0YS5mcmFtZQpzdW1tYXJ5KG15ZGF0YSkKCiMgaG93IG1hbnkgb2JzZXJ2YXRpb25zIGRvIHdlIGhhdmUgcGVyIFRheG9uIG1pZ2h0IGFsc28gYmUgb2YgdXNlCnRhYmxlKG15ZGF0YSRUYXhvbikKYGBgCgpJdCBpcyBub3Qgc28gZWFzeSB0byBnZXQgc3VtbWFyeSBzdGF0aXN0aWNzIG9uIGVhY2ggVGF4b25vbWljIGdyb3VwIHdpdGhpbiBvdXIgZGF0YXNldCB1c2luZyB0aGUgYmFzaWMgUiBmdW5jdGlvbnMuIFRoZXJlIGFyZSBtYW55IHBhY2thZ2VzIG91dCB0aGVyZSB0byBoZWxwLCBidXQgSGFkbGV5IFdpY2toYW0ncyBzdWl0ZSBvZiBwYWNrYWdlcyBhcmUgaW1wb3NzaWJsZSB0byBiZWF0IGluIG15IG9waW5pb24sIGFuZCB3ZSB3aWxsIGJlIHVzaW5nIGhpcyBgZ2dwbG90MmAgcGFja2FnZSBmb3IgcHJldHR5IGdyYXBocyBsYXRlciBvbiBzbyBsZXRzIHN0aWNrIHdpdGggdGhlICJ0aWR5dmVyc2UiIHNldCBvZiBmdW5jdGlvbnMuCgpgYGB7ciBzdW1tYXJpc2UtZGF0YS1ieS1ncm91cH0KIyBJbnN0YWxsIHRpZHl2ZXJzZSBpZiByZXF1aXJlZAojIGluc3RhbGwucGFja2FnZXMoJ3RpZHl2ZXJzZScpCgojIGxvYWQgdGhlIGxpYnJhcnkKbGlicmFyeSh0aWR5dmVyc2UpCgojIGdlbmVyYXRlIHN1bW1hcnkgc3RhdGlzdGljcyBieSBncm91cApteWRhdGEgfD4gCiAgZ3JvdXBfYnkoVGF4b24pIHw+CiAgc3VtbWFyaXNlKGNvdW50ID0gbigpLAogICAgICAgICAgICBtQyA9IG1lYW4oZDEzQyksIAogICAgICAgICAgICBzZEMgPSBzZChkMTNDKSwgCiAgICAgICAgICAgIG1OID0gbWVhbihkMTVOKSwgCiAgICAgICAgICAgIHNkTiA9IHNkKGQxNU4pLAogICAgICAgICAgICBtQ04gPSBtZWFuKEMuTiksCiAgICAgICAgICAgIHNkQ04gPSBzZChDLk4pKQoKCmBgYAoKIyMgQmFzaWMgVmlzdWFsaXNhdGlvbgoKVmlzdWFsaXNpbmcgdGhlIGRhdGEgaXMgb2Z0ZW4gdGhlIG5leHQgc3RlcC4gWW91IGNhbiBnZW5lcmF0ZSBoaXN0b2dyYW1zIG9mIHRoZSBkYXRhIHZlcnkgZWFzaWx5LgoKYGBge3IgaGlzdG99CiMgc2V0IHVwIGEgbXVsdGktcGFuZWwgcGxvdCwgMSByb3csIDMgY29sdW1ucwpwYXIobWZyb3cgPSBjKDEsMykpIApoaXN0KG15ZGF0YSRkMTNDKQpoaXN0KG15ZGF0YSRkMTVOKQpoaXN0KG15ZGF0YSRDLk4pCmBgYAoKV2UgbWlnaHQgYWxzbyBiZSBpbnRlcmVzdGVkIHZpc3VhbGlzaW5nIHRoZSBjb3JyZWxhdGlvbnMgYW1vbmcgdmFyaWFibGVzIGluIG91ciBkYXRhc2V0LiBUaGUgYHBhaXJzYCBmdW5jdGlvbiBkb2VzIHRoaXMgdmVyeSBuaWNlbHksIGFuZCBzaG91bGQgYmUgc29tZXRoaW5nIHlvdSB1c2UgcmVndWxhcmx5IGF0IHRoZSBzdGFydCBvZiBhbmFseXNlcyBzaW5jZSB5b3UgZ2V0IHRoZSBoaXN0b2dyYW1zLCBhbmQgdGhlIHBhaXItd2lzZSBzY2F0dGVyIHBsb3RzIGFuZCBjb3JyZWxhdGlvbnMgYWxsIGluIG9uZSBmaWd1cmUuIFRoZSBhc3NvY2lhdGVkIGhlbHAgZnVuY3Rpb24gaGFzIGxvdHMgb2YgZXhhbXBsZXMgYW5kIGV4cGxhaW5zIG1vcmUgYWJvdXQgd2hlcmUgdGhlIGZvbGxvd2luZyBjb2RlIGNvbWVzIGZyb20gYD9wYWlyc2AuCgpgYGB7ciBwYWlycy12aXN9CiMgPSA9ID0gPSA9ID0gPSA9ID0gPSA9ID0gPSA9ID0gPSA9ID0gPSA9ID0gPSA9ID0gPSA9ID0gPSA9ID0KIyBUaGVzZSBmdW5jdGlvbnMgYXJlIGNvcGllZCBmcm9tIHRoZSBoZWxwIGZpbGUgZm9yIHBhaXJzKCkKIyMgcHV0IGhpc3RvZ3JhbXMgb24gdGhlIGRpYWdvbmFsCnBhbmVsLmhpc3QgPC0gZnVuY3Rpb24oeCwgLi4uKQp7CiAgICB1c3IgPC0gcGFyKCJ1c3IiKTsgb24uZXhpdChwYXIodXNyKSkKICAgIHBhcih1c3IgPSBjKHVzclsxOjJdLCAwLCAxLjUpICkKICAgIGggPC0gaGlzdCh4LCBwbG90ID0gRkFMU0UpCiAgICBicmVha3MgPC0gaCRicmVha3M7IG5CIDwtIGxlbmd0aChicmVha3MpCiAgICB5IDwtIGgkY291bnRzOyB5IDwtIHkvbWF4KHkpCiAgICByZWN0KGJyZWFrc1stbkJdLCAwLCBicmVha3NbLTFdLCB5LCBjb2wgPSAiY3lhbiIsIC4uLikKfQoKcGFuZWwuY29yIDwtIGZ1bmN0aW9uKHgsIHksIGRpZ2l0cyA9IDIsIHByZWZpeCA9ICIiLCBjZXguY29yLCAuLi4pCnsKICAgIHVzciA8LSBwYXIoInVzciIpOyBvbi5leGl0KHBhcih1c3IpKQogICAgcGFyKHVzciA9IGMoMCwgMSwgMCwgMSkpCiAgICByIDwtIGNvcih4LCB5KQogICAgdHh0IDwtIGZvcm1hdChjKHIsIDAuMTIzNDU2Nzg5KSwgZGlnaXRzID0gZGlnaXRzKVsxXQogICAgdHh0IDwtIHBhc3RlMChwcmVmaXgsIHR4dCkKICAgIGlmKG1pc3NpbmcoY2V4LmNvcikpIGNleC5jb3IgPC0gMC44L3N0cndpZHRoKHR4dCkKICAgICMgdGV4dCgwLjUsIDAuNSwgdHh0LCBjZXggPSBjZXguY29yICogcikKICAgIHRleHQoMC41LCAwLjUsIHR4dCwgY2V4ID0gMykKfQoKIyA9ID0gPSA9ID0gPSA9ID0gPSA9ID0gPSA9ID0gPSA9ID0gPSA9ID0gPSA9ID0gPSA9ID0gPSA9ID0gPQoKIyBBIGJhc2ljIHBhaXJzKCkgcGxvdCBvZiB0aGUgbnVtZXJpYyBkYXRhCiMgVGhlc2UgdHdvIGxpbmVzIG9mIGNvZGUgYXJlIGlkZW50aWNhbCwgYnV0IHVzZSB0aGUgCiMgZHBseXIgZnVuY3Rpb25zIHNsaWdodGx5IGRpZmZlcmVudGx5CiMgcGFpcnMoc2VsZWN0KG15ZGF0YSwgZDEzQywgZDE1TiwgQy5OKSkKIyBwYWlycyhteWRhdGEgJT4lIHNlbGVjdChkMTNDLCBkMTVOLCBDLk4pKQoKCgojIHRoaXMgc2VsZWN0IGZ1bmN0aW9uIGNvbWVzIGZyb20gdGhlIHBhY2thZ2UgZHBseXIgYW5kIGFsbG93cyB1cyB0byAKIyBzZWxlY3QgYSBzdWJzZXQgb2YgY29sdW1ucyB0byBwbG90LgpwYWlycyhzZWxlY3QobXlkYXRhLCBkMTNDLCBkMTVOLCBDLk4pLCAKICAgICAgZGlhZy5wYW5lbCA9IHBhbmVsLmhpc3QsCiAgICAgIHVwcGVyLnBhbmVsID0gcGFuZWwuc21vb3RoLAogICAgICBsb3dlci5wYW5lbCA9IHBhbmVsLmNvcikKCgpgYGAKCkJveHBsb3RzIGFyZSBhIHF1aWNrIHdheSB0byB2aXN1YWxpc2UgdGhlIHZhcmlhdGlvbiBhbW9uZyB0YXhhLiBOb3RlIHRoZSB1c2Ugb2YgdGhlIGB+YCB3aGljaCBtZWFucyBgZDEzQyB+IFRheG9uYCByZWFkcyBhcywgZG8gc29tZXRoaW5nLCBpbiB0aGlzIGNhc2UgZHJhdyBhIGJveHBsb3QsIG9mIGBkMTNDYCBhcyBhIGZ1bmN0aW9uIG9mIGBUYXhvbmAuIFRoaXMgYGZvcm11bGFgIGFwcHJvYWNoIHRoZW4gYWxsb3dzIHVzIHRvIHRlbGwgaXQgdG8gZmluZCB0aGVzZSB2YXJpYWJsZXMgaW4gYG15ZGF0YWAgYW5kIHRoZW4gZmluYWxseSB3ZSBzcGVjaWZ5IGEgbWFpbiBsYWJlbCBmb3IgZWFjaCBncmFwaCBmb3IgdGhpcyBzdW1tYXJ5IHNvIHdlIGNhbiBrZWVwIHRyYWNrIG9mIHdoYXQgd2UgYXJlIGxvb2tpbmcgYXQuCgpgYGB7ciBib3hwbG90cy1ieS1ncm91cH0KCiMgZmlyc3QgQ2FyYm9uCmJveHBsb3QoZDEzQyB+IFRheG9uLCBkYXRhID0gbXlkYXRhLCBtYWluID0gImQxM0MiKQoKIyBzZWNvbmQgTml0cm9nZW4KYm94cGxvdChkMTVOIH4gVGF4b24sIGRhdGEgPSBteWRhdGEsIG1haW4gPSAiZDE1TiIpCgojIHRoaXJkIHRoZSBDL04gcmF0aW8KYm94cGxvdChDLk4gfiBUYXhvbiwgZGF0YSA9IG15ZGF0YSwgbWFpbiA9ICJDL04gcmF0aW8iKQoKYGBgCgpRUS1wbG90cyBhcmUgYWxzbyBhIGdyZWF0IHdheSB0byB2aXN1YWxpc2UgaG93IG5vcm1hbCB5b3VyIGRhdGEgYXJlLiBJZiB0aGV5IGFyZSBwZXJmZWN0bHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgdGhlbiB0aGV5IHdpbGwgc2l0IG9uIHRoZSBkaWFnb25hbCBsaW5lLgoKYGBge3IgcXEtcGxvdHN9CiMgYW5vdGhlciAxeDMgcGFuZWwgcGxvdApwYXIobWZyb3cgPSBjKDEsMykpCgpxcW5vcm0obXlkYXRhJGQxM0MpCnFxbGluZShteWRhdGEkZDEzQykKCnFxbm9ybShteWRhdGEkZDE1TikKcXFsaW5lKG15ZGF0YSRkMTVOKQoKcXFub3JtKG15ZGF0YSRDLk4pCnFxbGluZShteWRhdGEkQy5OKQoKYGBgCgoKT25lIG9mIHRoZSBkMTNDIHZhbHVlcyBmb3IgdGhlIEJlbnRoaWMgQWxnYWUgYXBwZWFycyB0byBiZSB2ZXJ5IGhpZ2ggaW4gcmVsYXRpb24gdG8gdGhlIG90aGVycywgYm90aCBvbiB0aGUgYm94cGxvdHMgYW5kIHRoZSBxcS1wbG90cy4gSWYgeW91IGFyZSBzYXRpc2ZpZWQgdGhpcyBpcyBhbiBlcnJvciBpbiB0aGUgc2FtcGxlIHByb2Nlc3Mgd2hpY2ggaXMgZW50aXJlbHkgcG9zc2libGUsIHRoZW4geW91IG1heSBjaG9vc2UgdG8gcmVtb3ZlIGl0LiBCdXQsIEkgd291bGQgY2F1dGlvbiBhZ2FpbnN0IHRha2luZyBhIHN0cmljdCBhcHByb2FjaCBiYXNlZCBvbiBzdGF0aXN0aWNhbCB0ZXN0cyBkZXNpZ25lZCB0byBmaW5kIG91dGxpZXJzLCBlc3BlY2lhbGx5IHdoZW4geW91ciBzYW1wbGUgc2l6ZSB3aXRoaW4gYSBncm91cCBpcyBsb3cgKG4gPSBgciB0YWJsZShteWRhdGEkVGF4b24pWyJCZW50aGljLmFsZ2FlIl1gIGluIHRoaXMgY2FzZSkuCgpJZiB5b3Ugd2FudGVkLCB5b3UgY291bGQgcmVtb3ZlIHRoaXMgZGF0YSBwb2ludCwgd2hpY2ggd2Ugd2lsbCBkbyBpbiB0aGlzIGNhc2UuCgpgYGB7cn0Kc3Vic2V0X215ZGF0YSA8LSBteWRhdGEgJT4lIGZpbHRlcihkMTNDIDwgbWF4KGQxM0MpKQoKIyBjaGVjayBvdXIgYm94cGxvdHMgYWdhaW4KYm94cGxvdChkMTNDIH4gVGF4b24sIGRhdGEgPSBzdWJzZXRfbXlkYXRhLCBtYWluID0gImQxM0MiKQpgYGAKCgoKCgoKCgo=