Import the data as before. Note that we get some warnings from both that “objects are masked from” various packages. This is because the packages we have just loaded have functions of the same name as those that have already been loaded (usually ones from the base R packages). This warning is telling us that, in the case of filter, when we simply call filter(), we will be using the last loaded one, i.e. from dplyr, rather than the one from stats. If you want to force R to use a particular function from a particular package you can write the long form, dplyr::filter() or stats::filter(). In many ways, it is good practice to always use this format, but in reality we are all lazy.


#install.packages("tidyverse")

library(tidyverse)

# 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)

# As per the summary statistics file, I am going to remove our outlier
# note the use of dplyr::filter() to avoid loading the entire 
# dplyr pacakge for just one function
mydata <- dplyr::filter(mydata, d13C < max(d13C))

# 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

The graphics that are part of “base R” are not very pretty (or at least some people think so - I quite like them to be honest). Another one of Hadley Wickham’s very popular packages is ggplot2 which by default makes some very blog-friendly graphics. And of course you can change the theme (template) on them to get something more suitable for publication (and fashion seems to be changing here too).

In ggplot2, figures are created in layer, by first creating a basic layer of axes according to an “aesthetic” which is then used as a framework to add points and lines and other embellishments. If you push the layers into an object using the <- assignment as I have done here, then you will need to print() your figure to get it to render.


first.plot <- ggplot(data = mydata, 
                     mapping = aes(x = d13C, 
                                   y = d15N)) + 
  geom_point(aes(color = Taxon, shape = Taxon), size = 5) +
  ylab(expression(paste(delta^{15}, "N (\u2030)"))) +
  xlab(expression(paste(delta^{13}, "C (\u2030)"))) + 
  theme(text = element_text(size=20))

# And print our plot to screen
print(first.plot)

If you want to use the more normal plotting format for journals without gridlines, and without the light-grey background, you add theme_classic() to your plot. This is a shortcut to some default settings of the myriad options available in theme() which we used above to increase the text size in our plot.


classic.first.plot <- first.plot + theme_classic() + 
  theme(text = element_text(size=35)) + 
  coord_equal() + 
  theme(axis.ticks.length = unit(-0.2, "cm")) +
  scale_colour_viridis_d(end = 0.9)

# and print to screen
print(classic.first.plot)


# options to add to point the axis tick marks inwards
# theme(axis.ticks.length = unit(-0.1, "cm"))

Errorbar biplots

Adding the means and error bars in both directions to create the classic isotope biplot requires adding these as additional layers. We could re-write all the lines of code for the original plot above, or we can simply create a new ggplot object based on the original, and then add the layers we want, and print. We need some summary data for each of the groups to plot the intervals: we use dplyr::summarise as in the previous script today except this time we store the output into a new object which I call sbg. When we add the layers for the means and the errorbars, we need to tell the layers to use a new aesthetic mapping using the new x and y data: mapping = aes(x = mC, y = mN, ...).


# Summarise By Group (sbg)
sbg <- mydata %>% 
  group_by(Taxon) %>% 
  summarise(count = n(),
            mC = mean(d13C), 
            sdC = sd(d13C), 
            mN = mean(d15N), 
            sdN = sd(d15N) )

# make a copy of the first.plot object
# second.plot <- first.plot

# add the layers using the summary data in sbg
second.plot <- first.plot + 
  geom_errorbar(data = sbg, 
                mapping = aes(x = mC, y = mN,
                              ymin = mN - 1.96*sdN, 
                              ymax = mN + 1.96*sdN), 
                width = 0) +
  geom_errorbarh(data = sbg, 
                 mapping = aes(x = mC, y = mN,
                               xmin = mC - 1.96*sdC,
                               xmax = mC + 1.96*sdC),
                 height = 0) + 
  geom_point(data = sbg, aes(x = mC, 
                             y = mN,
                             fill = Taxon), 
             color = "black", shape = 22, size = 5,
             alpha = 0.7, show.legend = FALSE) + 
  coord_equal()
Warning: Ignoring unknown aesthetics: x
print(second.plot)

NA

Ellipses instead of errorbars

The ggplot2 function stat_ellipse allows us to easily add ellipses, of varying level which corresponds to the prediction interval. This function defaults to using the t-distribution so we will override this and specify the normal distribution as is more fitting with the SIBER approach.

# use our ellipse function to generate the ellipses for plotting

# decide how big an ellipse you want to draw
p.ell <- 0.95

# create our plot based on first.plot above
# adding the stat_ellipse() geometry. We 
# specify thee ellipse to be plotted using 
# the polygon geom, with fill and edge colour
# defined by Taxon as a grouping variable, 
# using the normal distribution and with 
# a quite high level of transparency.
ellipse.plot <- first.plot + 
  stat_ellipse(aes(group = Taxon, 
                   fill = Taxon, 
                   color = Taxon), 
               alpha = 0.25, 
               level = p.ell,
               type = "norm",
               geom = "polygon")

print(ellipse.plot)


ggsave(plot = ellipse.plot, filename = "../../images/biplot.jpeg")
Saving 9 x 6 in image

Trouble shooting

If the permil symbol ‰ is not showing correctly (and instead is printing as \(\text{\u2030}\) in your plot it is likely because your computer is not set up to use UTF-8 format character encoding. This is not a problem with your setup of R or Rstudio, but is deeper in your computer. It is fixed by changing the region or locale settings on your computer. You can access these in the system preferences area of your computer’s operating system. To check if your computer is set up to identify and interpret UTF-8 encoding, you can type sessionInfo() in the R console. You should see something like this: en_IE.UTF-8/en_IE.UTF-8/en_IE.UTF-8/C/en_IE.UTF-8/en_IE.UTF-8 under the heading “locale”. On my machine, this indicates that it is use english (en), for Ireland (IE) and the UTF-8 encoding format. If the UTF-8 format is missing from your sessionInfo() then you could try changing your operating system to a locale similar to your own region’s location and see if the UTF-8 format is supported there.

LS0tCnRpdGxlOiAiU0lBIHBsb3RzIHVzaW5nIGdncGxvdDIiCmF1dGhvcjogIkFuZHJldyBMIEphY2tzb24gJiBDaHJpcyBIYXJyb2QiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDYpCmBgYAoKSW1wb3J0IHRoZSBkYXRhIGFzIGJlZm9yZS4gTm90ZSB0aGF0IHdlIGdldCBzb21lIHdhcm5pbmdzIGZyb20gYm90aCB0aGF0ICJvYmplY3RzIGFyZSBtYXNrZWQgZnJvbSIgdmFyaW91cyBwYWNrYWdlcy4gVGhpcyBpcyBiZWNhdXNlIHRoZSBwYWNrYWdlcyB3ZSBoYXZlIGp1c3QgbG9hZGVkIGhhdmUgZnVuY3Rpb25zIG9mIHRoZSBzYW1lIG5hbWUgYXMgdGhvc2UgdGhhdCBoYXZlIGFscmVhZHkgYmVlbiBsb2FkZWQgKHVzdWFsbHkgb25lcyBmcm9tIHRoZSBiYXNlIFIgcGFja2FnZXMpLiBUaGlzIHdhcm5pbmcgaXMgdGVsbGluZyB1cyB0aGF0LCBpbiB0aGUgY2FzZSBvZiBgZmlsdGVyYCwgd2hlbiB3ZSBzaW1wbHkgY2FsbCBgZmlsdGVyKClgLCB3ZSB3aWxsIGJlIHVzaW5nIHRoZSBsYXN0IGxvYWRlZCBvbmUsIGkuZS4gZnJvbSBgZHBseXJgLCByYXRoZXIgdGhhbiB0aGUgb25lIGZyb20gYHN0YXRzYC4gSWYgeW91IHdhbnQgdG8gZm9yY2UgUiB0byB1c2UgYSBwYXJ0aWN1bGFyIGZ1bmN0aW9uIGZyb20gYSBwYXJ0aWN1bGFyIHBhY2thZ2UgeW91IGNhbiB3cml0ZSB0aGUgbG9uZyBmb3JtLCBgZHBseXI6OmZpbHRlcigpYCBvciBgc3RhdHM6OmZpbHRlcigpYC4gSW4gbWFueSB3YXlzLCBpdCBpcyBnb29kIHByYWN0aWNlIHRvIGFsd2F5cyB1c2UgdGhpcyBmb3JtYXQsIGJ1dCBpbiByZWFsaXR5IHdlIGFyZSBhbGwgbGF6eS4KCmBgYHtyIGltcG9ydC1kYXRhfQoKI2luc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCgpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMgaW1wb3J0IHRoZSBkYXRhLiAKIyBJZiB5b3UgZ2V0IGFuIGVycm9yIGFib3V0ICJObyBzdWNoIGZpbGUgb3IgZGlyZWN0b3J5IiB0aGVuIHlvdSAKIyBuZWVkIHRvIHRha2UgY2FyZSBhYm91dCB3aGVyZSBSIGlzIGN1cnJlbnRseSB3b3JraW5nLCBhbmQgd2hlcmUgeW91ciAKIyBkYXRhIGZpbGUgaXMgaW4gcmVsYXRpb24gdG8gdGhpcy4KbXlkYXRhIDwtIHJlYWQuY3N2KCJQcmFjdGljYWwwMS5jc3YiLCAKICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKIyBBcyBwZXIgdGhlIHN1bW1hcnkgc3RhdGlzdGljcyBmaWxlLCBJIGFtIGdvaW5nIHRvIHJlbW92ZSBvdXIgb3V0bGllcgojIG5vdGUgdGhlIHVzZSBvZiBkcGx5cjo6ZmlsdGVyKCkgdG8gYXZvaWQgbG9hZGluZyB0aGUgZW50aXJlIAojIGRwbHlyIHBhY2FrZ2UgZm9yIGp1c3Qgb25lIGZ1bmN0aW9uCm15ZGF0YSA8LSBkcGx5cjo6ZmlsdGVyKG15ZGF0YSwgZDEzQyA8IG1heChkMTNDKSkKCiMgdmVyaWZ5IHRoYXQgb3VyIGRhdGEgbG9va3MgY29ycmVjdCBieSBwcmludGluZyB0aGUgZmlyc3QxIDEwIGxpbmVzCiMgdG8gc2NyZWVuCmhlYWQobXlkYXRhKQoKIyBpZiB0aGUgZGF0YSBpcyBub3QgYSBtYXNzaXZlIGRhdGFzZXQsIHlvdSBtaWdodCBsaWtlIHRvIGxvb2sgIAojIGF0IGl0IGFsbApteWRhdGEKYGBgCgpUaGUgZ3JhcGhpY3MgdGhhdCBhcmUgcGFydCBvZiAiYmFzZSBSIiBhcmUgbm90IHZlcnkgcHJldHR5IChvciBhdCBsZWFzdCBzb21lIHBlb3BsZSB0aGluayBzbyAtIEkgcXVpdGUgbGlrZSB0aGVtIHRvIGJlIGhvbmVzdCkuIEFub3RoZXIgb25lIG9mIEhhZGxleSBXaWNraGFtJ3MgdmVyeSBwb3B1bGFyIHBhY2thZ2VzIGlzIGBnZ3Bsb3QyYCB3aGljaCBieSBkZWZhdWx0IG1ha2VzIHNvbWUgdmVyeSBibG9nLWZyaWVuZGx5IGdyYXBoaWNzLiBBbmQgb2YgY291cnNlIHlvdSBjYW4gY2hhbmdlIHRoZSB0aGVtZSAodGVtcGxhdGUpIG9uIHRoZW0gdG8gZ2V0IHNvbWV0aGluZyBtb3JlIHN1aXRhYmxlIGZvciBwdWJsaWNhdGlvbiAoYW5kIGZhc2hpb24gc2VlbXMgdG8gYmUgY2hhbmdpbmcgaGVyZSB0b28pLgoKSW4gZ2dwbG90MiwgZmlndXJlcyBhcmUgY3JlYXRlZCBpbiBsYXllciwgYnkgZmlyc3QgY3JlYXRpbmcgYSBiYXNpYyBsYXllciBvZiBheGVzIGFjY29yZGluZyB0byBhbiAiYWVzdGhldGljIiB3aGljaCBpcyB0aGVuIHVzZWQgYXMgYSBmcmFtZXdvcmsgdG8gYWRkIHBvaW50cyBhbmQgbGluZXMgYW5kIG90aGVyIGVtYmVsbGlzaG1lbnRzLiBJZiB5b3UgcHVzaCB0aGUgbGF5ZXJzIGludG8gYW4gb2JqZWN0IHVzaW5nIHRoZSBgPC1gIGFzc2lnbm1lbnQgYXMgSSBoYXZlIGRvbmUgaGVyZSwgdGhlbiB5b3Ugd2lsbCBuZWVkIHRvIGBwcmludCgpYCB5b3VyIGZpZ3VyZSB0byBnZXQgaXQgdG8gcmVuZGVyLgoKYGBge3IgZmlyc3QtZ2d9CgpmaXJzdC5wbG90IDwtIGdncGxvdChkYXRhID0gbXlkYXRhLCAKICAgICAgICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gZDEzQywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGQxNU4pKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gVGF4b24sIHNoYXBlID0gVGF4b24pLCBzaXplID0gNSkgKwogIHlsYWIoZXhwcmVzc2lvbihwYXN0ZShkZWx0YV57MTV9LCAiTiAoXHUyMDMwKSIpKSkgKwogIHhsYWIoZXhwcmVzc2lvbihwYXN0ZShkZWx0YV57MTN9LCAiQyAoXHUyMDMwKSIpKSkgKyAKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApKQoKIyBBbmQgcHJpbnQgb3VyIHBsb3QgdG8gc2NyZWVuCnByaW50KGZpcnN0LnBsb3QpCgpgYGAKCklmIHlvdSB3YW50IHRvIHVzZSB0aGUgbW9yZSBub3JtYWwgcGxvdHRpbmcgZm9ybWF0IGZvciBqb3VybmFscyB3aXRob3V0IGdyaWRsaW5lcywgYW5kIHdpdGhvdXQgdGhlIGxpZ2h0LWdyZXkgYmFja2dyb3VuZCwgeW91IGFkZCBgdGhlbWVfY2xhc3NpYygpYCB0byB5b3VyIHBsb3QuIFRoaXMgaXMgYSBzaG9ydGN1dCB0byBzb21lIGRlZmF1bHQgc2V0dGluZ3Mgb2YgdGhlIG15cmlhZCBvcHRpb25zIGF2YWlsYWJsZSBpbiBgdGhlbWUoKWAgd2hpY2ggd2UgdXNlZCBhYm92ZSB0byBpbmNyZWFzZSB0aGUgdGV4dCBzaXplIGluIG91ciBwbG90LgoKYGBge3IgY2xhc3NpYy10aGVtZX0KCmNsYXNzaWMuZmlyc3QucGxvdCA8LSBmaXJzdC5wbG90ICsgdGhlbWVfY2xhc3NpYygpICsgCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTM1KSkgKyAKICBjb29yZF9lcXVhbCgpICsgCiAgdGhlbWUoYXhpcy50aWNrcy5sZW5ndGggPSB1bml0KC0wLjIsICJjbSIpKSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChlbmQgPSAwLjkpCgojIGFuZCBwcmludCB0byBzY3JlZW4KcHJpbnQoY2xhc3NpYy5maXJzdC5wbG90KQoKIyBvcHRpb25zIHRvIGFkZCB0byBwb2ludCB0aGUgYXhpcyB0aWNrIG1hcmtzIGlud2FyZHMKIyB0aGVtZShheGlzLnRpY2tzLmxlbmd0aCA9IHVuaXQoLTAuMSwgImNtIikpCmBgYAoKCiMgRXJyb3JiYXIgYmlwbG90cwoKQWRkaW5nIHRoZSBtZWFucyBhbmQgZXJyb3IgYmFycyBpbiBib3RoIGRpcmVjdGlvbnMgdG8gY3JlYXRlIHRoZSBjbGFzc2ljIGlzb3RvcGUgYmlwbG90IHJlcXVpcmVzIGFkZGluZyB0aGVzZSBhcyBhZGRpdGlvbmFsIGxheWVycy4gV2UgY291bGQgcmUtd3JpdGUgYWxsIHRoZSBsaW5lcyBvZiBjb2RlIGZvciB0aGUgb3JpZ2luYWwgcGxvdCBhYm92ZSwgb3Igd2UgY2FuIHNpbXBseSBjcmVhdGUgYSBuZXcgZ2dwbG90IG9iamVjdCBiYXNlZCBvbiB0aGUgb3JpZ2luYWwsIGFuZCB0aGVuIGFkZCB0aGUgbGF5ZXJzIHdlIHdhbnQsIGFuZCBwcmludC4gV2UgbmVlZCBzb21lIHN1bW1hcnkgZGF0YSBmb3IgZWFjaCBvZiB0aGUgZ3JvdXBzIHRvIHBsb3QgdGhlIGludGVydmFsczogd2UgdXNlIGBkcGx5cjo6c3VtbWFyaXNlYCBhcyBpbiB0aGUgcHJldmlvdXMgc2NyaXB0IHRvZGF5IGV4Y2VwdCB0aGlzIHRpbWUgd2Ugc3RvcmUgdGhlIG91dHB1dCBpbnRvIGEgbmV3IG9iamVjdCB3aGljaCBJIGNhbGwgYHNiZ2AuIFdoZW4gd2UgYWRkIHRoZSBsYXllcnMgZm9yIHRoZSBtZWFucyBhbmQgdGhlIGVycm9yYmFycywgd2UgbmVlZCB0byB0ZWxsIHRoZSBsYXllcnMgdG8gdXNlIGEgbmV3IGFlc3RoZXRpYyBtYXBwaW5nIHVzaW5nIHRoZSBuZXcgeCBhbmQgeSBkYXRhOiBgbWFwcGluZyA9IGFlcyh4ID0gbUMsIHkgPSBtTiwgLi4uKWAuCgpgYGB7ciBjbGFzc2ljLWJpcGxvdH0KCiMgU3VtbWFyaXNlIEJ5IEdyb3VwIChzYmcpCnNiZyA8LSBteWRhdGEgJT4lIAogIGdyb3VwX2J5KFRheG9uKSAlPiUgCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpLAogICAgICAgICAgICBtQyA9IG1lYW4oZDEzQyksIAogICAgICAgICAgICBzZEMgPSBzZChkMTNDKSwgCiAgICAgICAgICAgIG1OID0gbWVhbihkMTVOKSwgCiAgICAgICAgICAgIHNkTiA9IHNkKGQxNU4pICkKCiMgbWFrZSBhIGNvcHkgb2YgdGhlIGZpcnN0LnBsb3Qgb2JqZWN0CiMgc2Vjb25kLnBsb3QgPC0gZmlyc3QucGxvdAoKIyBhZGQgdGhlIGxheWVycyB1c2luZyB0aGUgc3VtbWFyeSBkYXRhIGluIHNiZwpzZWNvbmQucGxvdCA8LSBmaXJzdC5wbG90ICsgCiAgZ2VvbV9lcnJvcmJhcihkYXRhID0gc2JnLCAKICAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IG1DLCB5ID0gbU4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHltaW4gPSBtTiAtIDEuOTYqc2ROLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeW1heCA9IG1OICsgMS45NipzZE4pLCAKICAgICAgICAgICAgICAgIHdpZHRoID0gMCkgKwogIGdlb21fZXJyb3JiYXJoKGRhdGEgPSBzYmcsIAogICAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IG1DLCB5ID0gbU4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4bWluID0gbUMgLSAxLjk2KnNkQywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhtYXggPSBtQyArIDEuOTYqc2RDKSwKICAgICAgICAgICAgICAgICBoZWlnaHQgPSAwKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IHNiZywgYWVzKHggPSBtQywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IG1OLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBUYXhvbiksIAogICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaGFwZSA9IDIyLCBzaXplID0gNSwKICAgICAgICAgICAgIGFscGhhID0gMC43LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIGNvb3JkX2VxdWFsKCkKCgpwcmludChzZWNvbmQucGxvdCkKICAKYGBgCgojIyBFbGxpcHNlcyBpbnN0ZWFkIG9mIGVycm9yYmFycwoKVGhlIGdncGxvdDIgZnVuY3Rpb24gYHN0YXRfZWxsaXBzZWAgYWxsb3dzIHVzIHRvIGVhc2lseSBhZGQgZWxsaXBzZXMsIG9mIHZhcnlpbmcgKipsZXZlbCoqIHdoaWNoIGNvcnJlc3BvbmRzIHRvIHRoZSBwcmVkaWN0aW9uIGludGVydmFsLiBUaGlzIGZ1bmN0aW9uIGRlZmF1bHRzIHRvIHVzaW5nIHRoZSB0LWRpc3RyaWJ1dGlvbiBzbyB3ZSB3aWxsIG92ZXJyaWRlIHRoaXMgYW5kIHNwZWNpZnkgdGhlIG5vcm1hbCBkaXN0cmlidXRpb24gYXMgaXMgbW9yZSBmaXR0aW5nIHdpdGggdGhlIFNJQkVSIGFwcHJvYWNoLiAgCgpgYGB7ciBuaWNlLWVsbGlwc2VzfQojIHVzZSBvdXIgZWxsaXBzZSBmdW5jdGlvbiB0byBnZW5lcmF0ZSB0aGUgZWxsaXBzZXMgZm9yIHBsb3R0aW5nCgojIGRlY2lkZSBob3cgYmlnIGFuIGVsbGlwc2UgeW91IHdhbnQgdG8gZHJhdwpwLmVsbCA8LSAwLjk1CgojIGNyZWF0ZSBvdXIgcGxvdCBiYXNlZCBvbiBmaXJzdC5wbG90IGFib3ZlCiMgYWRkaW5nIHRoZSBzdGF0X2VsbGlwc2UoKSBnZW9tZXRyeS4gV2UgCiMgc3BlY2lmeSB0aGVlIGVsbGlwc2UgdG8gYmUgcGxvdHRlZCB1c2luZyAKIyB0aGUgcG9seWdvbiBnZW9tLCB3aXRoIGZpbGwgYW5kIGVkZ2UgY29sb3VyCiMgZGVmaW5lZCBieSBUYXhvbiBhcyBhIGdyb3VwaW5nIHZhcmlhYmxlLCAKIyB1c2luZyB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiBhbmQgd2l0aCAKIyBhIHF1aXRlIGhpZ2ggbGV2ZWwgb2YgdHJhbnNwYXJlbmN5LgplbGxpcHNlLnBsb3QgPC0gZmlyc3QucGxvdCArIAogIHN0YXRfZWxsaXBzZShhZXMoZ3JvdXAgPSBUYXhvbiwgCiAgICAgICAgICAgICAgICAgICBmaWxsID0gVGF4b24sIAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBUYXhvbiksIAogICAgICAgICAgICAgICBhbHBoYSA9IDAuMjUsIAogICAgICAgICAgICAgICBsZXZlbCA9IHAuZWxsLAogICAgICAgICAgICAgICB0eXBlID0gIm5vcm0iLAogICAgICAgICAgICAgICBnZW9tID0gInBvbHlnb24iKQoKcHJpbnQoZWxsaXBzZS5wbG90KQoKZ2dzYXZlKHBsb3QgPSBlbGxpcHNlLnBsb3QsIGZpbGVuYW1lID0gIi4uLy4uL2ltYWdlcy9iaXBsb3QuanBlZyIpCgpgYGAKCgojIyBUcm91YmxlIHNob290aW5nCgpJZiB0aGUgcGVybWlsIHN5bWJvbCBgciAiXHUyMDMwImAgaXMgbm90IHNob3dpbmcgY29ycmVjdGx5IChhbmQgaW5zdGVhZCBpcyBwcmludGluZyBhcyAkXHRleHR7XHUyMDMwfSQgaW4geW91ciBwbG90IGl0IGlzIGxpa2VseSBiZWNhdXNlIHlvdXIgY29tcHV0ZXIgaXMgbm90IHNldCB1cCB0byB1c2UgVVRGLTggZm9ybWF0IGNoYXJhY3RlciBlbmNvZGluZy4gVGhpcyBpcyBub3QgYSBwcm9ibGVtIHdpdGggeW91ciBzZXR1cCBvZiBSIG9yIFJzdHVkaW8sIGJ1dCBpcyBkZWVwZXIgaW4geW91ciBjb21wdXRlci4gSXQgaXMgZml4ZWQgYnkgY2hhbmdpbmcgdGhlIHJlZ2lvbiBvciBsb2NhbGUgc2V0dGluZ3Mgb24geW91ciBjb21wdXRlci4gWW91IGNhbiBhY2Nlc3MgdGhlc2UgaW4gdGhlIHN5c3RlbSBwcmVmZXJlbmNlcyBhcmVhIG9mIHlvdXIgY29tcHV0ZXIncyBvcGVyYXRpbmcgc3lzdGVtLiBUbyBjaGVjayBpZiB5b3VyIGNvbXB1dGVyIGlzIHNldCB1cCB0byBpZGVudGlmeSBhbmQgaW50ZXJwcmV0IFVURi04IGVuY29kaW5nLCB5b3UgY2FuIHR5cGUgYHNlc3Npb25JbmZvKClgIGluIHRoZSBSIGNvbnNvbGUuIFlvdSBzaG91bGQgc2VlIHNvbWV0aGluZyBsaWtlIHRoaXM6IGBlbl9JRS5VVEYtOC9lbl9JRS5VVEYtOC9lbl9JRS5VVEYtOC9DL2VuX0lFLlVURi04L2VuX0lFLlVURi04YCB1bmRlciB0aGUgaGVhZGluZyAibG9jYWxlIi4gT24gbXkgbWFjaGluZSwgdGhpcyBpbmRpY2F0ZXMgdGhhdCBpdCBpcyB1c2UgZW5nbGlzaCAoZW4pLCBmb3IgSXJlbGFuZCAoSUUpIGFuZCB0aGUgVVRGLTggZW5jb2RpbmcgZm9ybWF0LiBJZiB0aGUgVVRGLTggZm9ybWF0IGlzIG1pc3NpbmcgZnJvbSB5b3VyIGBzZXNzaW9uSW5mbygpYCB0aGVuIHlvdSBjb3VsZCB0cnkgY2hhbmdpbmcgeW91ciBvcGVyYXRpbmcgc3lzdGVtIHRvIGEgbG9jYWxlIHNpbWlsYXIgdG8geW91ciBvd24gcmVnaW9uJ3MgbG9jYXRpb24gYW5kIHNlZSBpZiB0aGUgVVRGLTggZm9ybWF0IGlzIHN1cHBvcnRlZCB0aGVyZS4KCgoKCg==