Data source

This notebook begins analysis with already cleaned GC/MS data. I’ve already calculated relative peak areas (RPA) subtracted field blanks, and replaced any zeroes or NAs with 1/peak area of internal standard.

Load packages

library(tidyverse)
── Attaching packages ────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 3.0.0     ✔ purrr   0.2.5
✔ tibble  1.4.2     ✔ dplyr   0.7.6
✔ tidyr   0.8.1     ✔ stringr 1.3.1
✔ readr   1.1.1     ✔ forcats 0.3.0
── Conflicts ───────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
✖ purrr::map()    masks mclust::map()
library(here)
here() starts at /Users/scottericr/Documents/Tufts/Research Projects/BACE Tea/Data for drought by herbivory in tea
library(gglabeller)
library(cowplot)

Attaching package: ‘cowplot’

The following object is masked from ‘package:ggplot2’:

    ggsave
library(ggrepel)
library(ropls)
library(HDoutliers)

Create some additional custom functions

`%!in%` <- Negate(`%in%`)
#detect if a column has at least one non-zero value.  Probably other ways to do this.
not_empty <- function(x) !is.double(x) || is.double(x) && sum(x) != 0
#function to select columns that are NOT double, i.e. integer, factor, and character
not_double <- function(x) !is.double(x)

Read in data

gc <- read_rds(here("cleaned_data", "GCMS.rds"))
gc_zeroes <- read_rds(here("cleaned_data", "GCMS_zeroes.rds"))

Create a “wide” version of the data frame for use in multivarite analyses

gc_wide <- gc %>%
  select(sample, Treatment, Spray, Plot, Plant, Compound, RPA) %>% 
  spread(key = Compound, value = RPA)

Scatter Plots

I’ll use a combination of univariate scatter plots and preliminary PCA to detect outliers. ## Prep Data

Unlabeled Plots

p1 <- ggplot(plotdata[[1]], aes(x = shortname, y = RPA, color = Spray)) + horiz_scatter() + theme_bw()
p2 <- ggplot(plotdata[[2]], aes(x = shortname, y = RPA, color = Spray)) + horiz_scatter() + theme_bw()
p3 <- ggplot(plotdata[[3]], aes(x = shortname, y = RPA, color = Spray)) + horiz_scatter() + theme_bw()
p4 <- ggplot(plotdata[[4]], aes(x = shortname, y = RPA, color = Spray)) + horiz_scatter() + theme_bw()

Interactive labeling (not run)

A recent update to gglabeller broke this code I think. I think I need to explicitly drop unused levels from sample for this to work.

p1.labeled <- gglabeller(p1, aes(label = plotdata[[1]]$sample))
p1 <- p1.labeled$plot

p2.labeled <- gglabeller(p2, aes(label = plotdata[[2]]$sample))
p2 <- p2.labeled$plot

p3.labeled <- gglabeller(p3, aes(label = plotdata[[3]]$sample))
p3 <- p3.labeled$plot

p4.labeled <- gglabeller(p4, aes(label = plotdata[[4]]$sample))
p4 <- p4.labeled$plot

Labeled Plots

Produced by running the chunk above interactively and copying the plot data from, e.g., p1.labeled$code

gglabeller_data <- p1$data
gglabeller_data$gglabeller_labels <- plotdata[[1]]$sample
gglabeller_data[c(1:483, 485:647, 649:659, 661:750, 752:804, 806, 808:816, 818:823, 826, 828:829, 831:1426, 1428:1536),'gglabeller_labels'] <- ''
p1 <- p1 + geom_text_repel(data = gglabeller_data,mapping = aes(label = gglabeller_labels), min.segment.length = unit(0.5, 'lines'),box.padding = unit(0.25, 'lines'),point.padding = unit(1e-06, 'lines'))
gglabeller_data <- p2$data
gglabeller_data$gglabeller_labels <- plotdata[[2]]$sample
gglabeller_data[c(1:133, 135, 137:364, 366:430, 432:478, 480:891, 893:1009, 1011:1161, 1163:1255, 1257:1296, 1298:1299, 1301:1417, 1419:1435, 1437:1457, 1459:1536),'gglabeller_labels'] <- ''
p2 <- p2 + geom_text_repel(data = gglabeller_data,mapping = aes(label = gglabeller_labels), min.segment.length = unit(0.5, 'lines'),box.padding = unit(0.25, 'lines'),point.padding = unit(1e-06, 'lines'))
gglabeller_data <- p3$data
gglabeller_data$gglabeller_labels <- plotdata[[3]]$sample
gglabeller_data[c(1:44, 46:288, 290:357, 360:473, 475:505, 507:761, 763:812, 814:821, 823:1287, 1289:1413, 1415:1424, 1426:1432, 1434:1536),'gglabeller_labels'] <- ''
p3 <- p3 + geom_text_repel(data = gglabeller_data,mapping = aes(label = gglabeller_labels), min.segment.length = unit(0.5, 'lines'),box.padding = unit(0.25, 'lines'),point.padding = unit(1e-06, 'lines'))
gglabeller_data <- p4$data
gglabeller_data$gglabeller_labels <- plotdata[[4]]$sample
gglabeller_data[c(1:41, 43:71, 73:354, 356:462, 464:494, 496:502, 504:823, 826:831, 833:1293, 1295:1417, 1419:1424, 1426:1536),'gglabeller_labels'] <- ''
p4<- p4 + geom_text_repel(data = gglabeller_data,mapping = aes(label = gglabeller_labels), min.segment.length = unit(0.5, 'lines'),box.padding = unit(0.25, 'lines'),point.padding = unit(1e-06, 'lines'))

Build plots

legend <- get_legend(p1)
grid1 <- plot_grid(p1 + theme(legend.position = "none"),
                   p2 + theme(legend.position = "none"),
                   ncol = 2, nrow = 1)
grid2 <- plot_grid(grid1, legend, rel_widths = c(3, 0.4))
grid3 <- plot_grid(p3 + theme(legend.position = "none"),
                   p4 + theme(legend.position = "none"),
                   ncol = 2, nrow = 1)
grid4 <- plot_grid(grid3, legend, rel_widths = c(3, 0.4))

Looks like e6 and maybe c10 and i4 are outliers. e6 especially is showing up often as extreme values and with extreme values that are very extreme.

The data are also not normally distributed.

Preliminary PCA

I also conduct a PCA with opls() which will give me a diagnostic plot that identifies outliers.

pca_pre <- opls(select_if(gc_wide, is.double), plotL = FALSE, scale = "standard")
PCA
48 samples x 128 variables
standard scaling of predictors
plot(pca_pre, parLabVc = gc_wide$sample)

Log Transform

after log transformation:

gc_log <- gc_wide %>% mutate_if(is.double, log)
pca_pre_log <- opls(select_if(gc_log, is.double), plotL = FALSE, scale = "standard")
PCA
48 samples x 128 variables
standard scaling of predictors
plot(pca_pre_log, parLabVc = gc_log$sample)

Before log transformation, e6 and i4 are identified as possible outliers. After log transformation, e6, c6 and c9 are identified as outliers.

Check with HDoutliers package

HDoutliers also identifies e6, but log transormation seems to fix it.

Remove outliers

e6 is for sure an outlier based on univariate plots. I’ll also remove c6 and c9 and use the log transformed data from here on out.

outliers <- c("e6", "c6", "c9")
gc_log.1 <- gc_log %>% 
  filter(sample %!in% outliers) %>% 
  select_if(not_empty) #and check for empty columns

gc_zeroes.1 <- gc_zeroes %>% 
  filter(sample %!in% outliers) %>% 
  select_if(not_empty)

Save output

Save data frames with outliers removed for analysis step.

gc_log.1
gc_zeroes.1

write_rds(gc_log.1, here("cleaned_data", "GCMS_wide_loged.rds"))
write_rds(gc_zeroes.1, here("cleaned_data", "GCMS_tidy_zeroes.rds"))
LS0tCnRpdGxlOiAiR0NNUyBFREEsIG91dGxpZXIgcmVtb3ZhbCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQojIERhdGEgc291cmNlClRoaXMgbm90ZWJvb2sgYmVnaW5zIGFuYWx5c2lzIHdpdGggYWxyZWFkeSBjbGVhbmVkIEdDL01TIGRhdGEuICBJJ3ZlIGFscmVhZHkgY2FsY3VsYXRlZCByZWxhdGl2ZSBwZWFrIGFyZWFzIChSUEEpIHN1YnRyYWN0ZWQgZmllbGQgYmxhbmtzLCBhbmQgcmVwbGFjZWQgYW55IHplcm9lcyBvciBOQXMgd2l0aCAxL3BlYWsgYXJlYSBvZiBpbnRlcm5hbCBzdGFuZGFyZC4KCiMgTG9hZCBwYWNrYWdlcwpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoaGVyZSkKbGlicmFyeShnZ2xhYmVsbGVyKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShyb3BscykKbGlicmFyeShIRG91dGxpZXJzKQpgYGAKIyBDcmVhdGUgc29tZSBhZGRpdGlvbmFsIGN1c3RvbSBmdW5jdGlvbnMKYGBge3J9CmAlIWluJWAgPC0gTmVnYXRlKGAlaW4lYCkKI2RldGVjdCBpZiBhIGNvbHVtbiBoYXMgYXQgbGVhc3Qgb25lIG5vbi16ZXJvIHZhbHVlLiAgUHJvYmFibHkgb3RoZXIgd2F5cyB0byBkbyB0aGlzLgpub3RfZW1wdHkgPC0gZnVuY3Rpb24oeCkgIWlzLmRvdWJsZSh4KSB8fCBpcy5kb3VibGUoeCkgJiYgc3VtKHgpICE9IDAKCiNmdW5jdGlvbiB0byBzZWxlY3QgY29sdW1ucyB0aGF0IGFyZSBOT1QgZG91YmxlLCBpLmUuIGludGVnZXIsIGZhY3RvciwgYW5kIGNoYXJhY3Rlcgpub3RfZG91YmxlIDwtIGZ1bmN0aW9uKHgpICFpcy5kb3VibGUoeCkKYGBgCgojIFJlYWQgaW4gZGF0YQpgYGB7cn0KZ2MgPC0gcmVhZF9yZHMoaGVyZSgiY2xlYW5lZF9kYXRhIiwgIkdDTVMucmRzIikpCmdjX3plcm9lcyA8LSByZWFkX3JkcyhoZXJlKCJjbGVhbmVkX2RhdGEiLCAiR0NNU196ZXJvZXMucmRzIikpCmBgYApDcmVhdGUgYSAid2lkZSIgdmVyc2lvbiBvZiB0aGUgZGF0YSBmcmFtZSBmb3IgdXNlIGluIG11bHRpdmFyaXRlIGFuYWx5c2VzCmBgYHtyfQpnY193aWRlIDwtIGdjICU+JQogIHNlbGVjdChzYW1wbGUsIFRyZWF0bWVudCwgU3ByYXksIFBsb3QsIFBsYW50LCBDb21wb3VuZCwgUlBBKSAlPiUgCiAgc3ByZWFkKGtleSA9IENvbXBvdW5kLCB2YWx1ZSA9IFJQQSkKYGBgCgojIFNjYXR0ZXIgUGxvdHMKSSdsbCB1c2UgYSBjb21iaW5hdGlvbiBvZiB1bml2YXJpYXRlIHNjYXR0ZXIgcGxvdHMgYW5kIHByZWxpbWluYXJ5IFBDQSB0byBkZXRlY3Qgb3V0bGllcnMuCiMjIFByZXAgRGF0YQpgYGB7ciBlY2hvPUZBTFNFfQojY3JlYXRlIGFicmV2aWF0ZWQgbmFtZS4gc3RyX3RydW5jKCkgZG9lc24ndCB3b3JrIGluc2lkZSBtdXRhdGUoKSBkdWUgdG8gYSBidWcgaW4gdGhlIHZlcnNpb24gb2Ygc3RyaW5nciBJJ20gdXNpbmcKZ2MucCA8LSBnYyAlPiUKICBhZGRfY29sdW1uKHNob3J0bmFtZSA9IG1hcF9jaHIoZ2MkQ29tcG91bmQsIH5zdHJfdHJ1bmMoLiwgMTMpKSkKCiNjcmVhdGUgZ3JvdXBpbmcgdmFyaWFibGUgZm9yIGZhY2V0aW5nIGJ5IGNodW5rcyBvZiBjb21wb3VuZHMKcGllY2VzID0gNAoKZ2MucCA8LSBnYy5wICU+JSAKICBtdXRhdGUoR3JvdXAgPSBDb21wb3VuZCAlPiUgIyBjaGFyYWN0ZXIKICAgICAgICAgICBhcy5mYWN0b3IoKSAlPiUgICAgIyBjb252ZXJ0IHRvIGZhY3RvcgogICAgICAgICAgIGFzLm51bWVyaWMoKSAlPiUgICAjIGNvbnZlcnQgdG8gbnVtZXJpYyAoZHVtbXkgdmFyaWFibGUpCiAgICAgICAgICAgY3V0KHBpZWNlcykgJT4lICAgICMgc2xpY2UgaXQgdXAKICAgICAgICAgICBhcy5pbnRlZ2VyKCkpICAgICAgIyBjb252ZXJ0IHNsaWNlcyB0byBudW1iZXJzCgpwbG90ZGF0YSA8LSBzcGxpdChnYy5wLCBnYy5wJEdyb3VwKQoKI2NyZWF0ZSBwbG90dGluZyBmdW5jdGlvbgpob3Jpel9zY2F0dGVyIDwtIGZ1bmN0aW9uKCl7CiAgbGlzdCgKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjI1KSwKICAgIGNvb3JkX2ZsaXAoKSwKICAgIGxhYnMoeCA9ICJDb21wb3VuZCBOYW1lIiwgeSA9ICJSUEEiKSwKICAgIHNjYWxlX2NvbG9yX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gNikKICApCn0KYGBgCgojIyBVbmxhYmVsZWQgUGxvdHMKYGBge3J9CnAxIDwtIGdncGxvdChwbG90ZGF0YVtbMV1dLCBhZXMoeCA9IHNob3J0bmFtZSwgeSA9IFJQQSwgY29sb3IgPSBTcHJheSkpICsgaG9yaXpfc2NhdHRlcigpICsgdGhlbWVfYncoKQpwMiA8LSBnZ3Bsb3QocGxvdGRhdGFbWzJdXSwgYWVzKHggPSBzaG9ydG5hbWUsIHkgPSBSUEEsIGNvbG9yID0gU3ByYXkpKSArIGhvcml6X3NjYXR0ZXIoKSArIHRoZW1lX2J3KCkKcDMgPC0gZ2dwbG90KHBsb3RkYXRhW1szXV0sIGFlcyh4ID0gc2hvcnRuYW1lLCB5ID0gUlBBLCBjb2xvciA9IFNwcmF5KSkgKyBob3Jpel9zY2F0dGVyKCkgKyB0aGVtZV9idygpCnA0IDwtIGdncGxvdChwbG90ZGF0YVtbNF1dLCBhZXMoeCA9IHNob3J0bmFtZSwgeSA9IFJQQSwgY29sb3IgPSBTcHJheSkpICsgaG9yaXpfc2NhdHRlcigpICsgdGhlbWVfYncoKQpgYGAKIyMgSW50ZXJhY3RpdmUgbGFiZWxpbmcgKG5vdCBydW4pCkEgcmVjZW50IHVwZGF0ZSB0byBnZ2xhYmVsbGVyIGJyb2tlIHRoaXMgY29kZSBJIHRoaW5rLiAgSSB0aGluayBJIG5lZWQgdG8gZXhwbGljaXRseSBkcm9wIHVudXNlZCBsZXZlbHMgZnJvbSBgc2FtcGxlYCBmb3IgdGhpcyB0byB3b3JrLgpgYGB7ciBldmFsPUZBTFNFfQpwMS5sYWJlbGVkIDwtIGdnbGFiZWxsZXIocDEsIGFlcyhsYWJlbCA9IHBsb3RkYXRhW1sxXV0kc2FtcGxlKSkKcDEgPC0gcDEubGFiZWxlZCRwbG90CgpwMi5sYWJlbGVkIDwtIGdnbGFiZWxsZXIocDIsIGFlcyhsYWJlbCA9IHBsb3RkYXRhW1syXV0kc2FtcGxlKSkKcDIgPC0gcDIubGFiZWxlZCRwbG90CgpwMy5sYWJlbGVkIDwtIGdnbGFiZWxsZXIocDMsIGFlcyhsYWJlbCA9IHBsb3RkYXRhW1szXV0kc2FtcGxlKSkKcDMgPC0gcDMubGFiZWxlZCRwbG90CgpwNC5sYWJlbGVkIDwtIGdnbGFiZWxsZXIocDQsIGFlcyhsYWJlbCA9IHBsb3RkYXRhW1s0XV0kc2FtcGxlKSkKcDQgPC0gcDQubGFiZWxlZCRwbG90CmBgYAojIyBMYWJlbGVkIFBsb3RzClByb2R1Y2VkIGJ5IHJ1bm5pbmcgdGhlIGNodW5rIGFib3ZlIGludGVyYWN0aXZlbHkgYW5kIGNvcHlpbmcgdGhlIHBsb3QgZGF0YSBmcm9tLCBlLmcuLCBgcDEubGFiZWxlZCRjb2RlYApgYGB7cn0KZ2dsYWJlbGxlcl9kYXRhIDwtIHAxJGRhdGEKZ2dsYWJlbGxlcl9kYXRhJGdnbGFiZWxsZXJfbGFiZWxzIDwtIHBsb3RkYXRhW1sxXV0kc2FtcGxlCmdnbGFiZWxsZXJfZGF0YVtjKDE6NDgzLCA0ODU6NjQ3LCA2NDk6NjU5LCA2NjE6NzUwLCA3NTI6ODA0LCA4MDYsIDgwODo4MTYsIDgxODo4MjMsIDgyNiwgODI4OjgyOSwgODMxOjE0MjYsIDE0Mjg6MTUzNiksJ2dnbGFiZWxsZXJfbGFiZWxzJ10gPC0gJycKcDEgPC0gcDEgKyBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IGdnbGFiZWxsZXJfZGF0YSxtYXBwaW5nID0gYWVzKGxhYmVsID0gZ2dsYWJlbGxlcl9sYWJlbHMpLCBtaW4uc2VnbWVudC5sZW5ndGggPSB1bml0KDAuNSwgJ2xpbmVzJyksYm94LnBhZGRpbmcgPSB1bml0KDAuMjUsICdsaW5lcycpLHBvaW50LnBhZGRpbmcgPSB1bml0KDFlLTA2LCAnbGluZXMnKSkKCmdnbGFiZWxsZXJfZGF0YSA8LSBwMiRkYXRhCmdnbGFiZWxsZXJfZGF0YSRnZ2xhYmVsbGVyX2xhYmVscyA8LSBwbG90ZGF0YVtbMl1dJHNhbXBsZQpnZ2xhYmVsbGVyX2RhdGFbYygxOjEzMywgMTM1LCAxMzc6MzY0LCAzNjY6NDMwLCA0MzI6NDc4LCA0ODA6ODkxLCA4OTM6MTAwOSwgMTAxMToxMTYxLCAxMTYzOjEyNTUsIDEyNTc6MTI5NiwgMTI5ODoxMjk5LCAxMzAxOjE0MTcsIDE0MTk6MTQzNSwgMTQzNzoxNDU3LCAxNDU5OjE1MzYpLCdnZ2xhYmVsbGVyX2xhYmVscyddIDwtICcnCnAyIDwtIHAyICsgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBnZ2xhYmVsbGVyX2RhdGEsbWFwcGluZyA9IGFlcyhsYWJlbCA9IGdnbGFiZWxsZXJfbGFiZWxzKSwgbWluLnNlZ21lbnQubGVuZ3RoID0gdW5pdCgwLjUsICdsaW5lcycpLGJveC5wYWRkaW5nID0gdW5pdCgwLjI1LCAnbGluZXMnKSxwb2ludC5wYWRkaW5nID0gdW5pdCgxZS0wNiwgJ2xpbmVzJykpCgpnZ2xhYmVsbGVyX2RhdGEgPC0gcDMkZGF0YQpnZ2xhYmVsbGVyX2RhdGEkZ2dsYWJlbGxlcl9sYWJlbHMgPC0gcGxvdGRhdGFbWzNdXSRzYW1wbGUKZ2dsYWJlbGxlcl9kYXRhW2MoMTo0NCwgNDY6Mjg4LCAyOTA6MzU3LCAzNjA6NDczLCA0NzU6NTA1LCA1MDc6NzYxLCA3NjM6ODEyLCA4MTQ6ODIxLCA4MjM6MTI4NywgMTI4OToxNDEzLCAxNDE1OjE0MjQsIDE0MjY6MTQzMiwgMTQzNDoxNTM2KSwnZ2dsYWJlbGxlcl9sYWJlbHMnXSA8LSAnJwpwMyA8LSBwMyArIGdlb21fdGV4dF9yZXBlbChkYXRhID0gZ2dsYWJlbGxlcl9kYXRhLG1hcHBpbmcgPSBhZXMobGFiZWwgPSBnZ2xhYmVsbGVyX2xhYmVscyksIG1pbi5zZWdtZW50Lmxlbmd0aCA9IHVuaXQoMC41LCAnbGluZXMnKSxib3gucGFkZGluZyA9IHVuaXQoMC4yNSwgJ2xpbmVzJykscG9pbnQucGFkZGluZyA9IHVuaXQoMWUtMDYsICdsaW5lcycpKQoKZ2dsYWJlbGxlcl9kYXRhIDwtIHA0JGRhdGEKZ2dsYWJlbGxlcl9kYXRhJGdnbGFiZWxsZXJfbGFiZWxzIDwtIHBsb3RkYXRhW1s0XV0kc2FtcGxlCmdnbGFiZWxsZXJfZGF0YVtjKDE6NDEsIDQzOjcxLCA3MzozNTQsIDM1Njo0NjIsIDQ2NDo0OTQsIDQ5Njo1MDIsIDUwNDo4MjMsIDgyNjo4MzEsIDgzMzoxMjkzLCAxMjk1OjE0MTcsIDE0MTk6MTQyNCwgMTQyNjoxNTM2KSwnZ2dsYWJlbGxlcl9sYWJlbHMnXSA8LSAnJwpwNDwtIHA0ICsgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBnZ2xhYmVsbGVyX2RhdGEsbWFwcGluZyA9IGFlcyhsYWJlbCA9IGdnbGFiZWxsZXJfbGFiZWxzKSwgbWluLnNlZ21lbnQubGVuZ3RoID0gdW5pdCgwLjUsICdsaW5lcycpLGJveC5wYWRkaW5nID0gdW5pdCgwLjI1LCAnbGluZXMnKSxwb2ludC5wYWRkaW5nID0gdW5pdCgxZS0wNiwgJ2xpbmVzJykpCmBgYAoKIyMgQnVpbGQgcGxvdHMKYGBge3J9CmxlZ2VuZCA8LSBnZXRfbGVnZW5kKHAxKQpncmlkMSA8LSBwbG90X2dyaWQocDEgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLAogICAgICAgICAgICAgICAgICAgcDIgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLAogICAgICAgICAgICAgICAgICAgbmNvbCA9IDIsIG5yb3cgPSAxKQpncmlkMiA8LSBwbG90X2dyaWQoZ3JpZDEsIGxlZ2VuZCwgcmVsX3dpZHRocyA9IGMoMywgMC40KSkKCmdyaWQzIDwtIHBsb3RfZ3JpZChwMyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksCiAgICAgICAgICAgICAgICAgICBwNCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksCiAgICAgICAgICAgICAgICAgICBuY29sID0gMiwgbnJvdyA9IDEpCmdyaWQ0IDwtIHBsb3RfZ3JpZChncmlkMywgbGVnZW5kLCByZWxfd2lkdGhzID0gYygzLCAwLjQpKQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9CmdyaWQyCmdyaWQ0CmBgYApMb29rcyBsaWtlIGU2IGFuZCBtYXliZSBjMTAgYW5kIGk0IGFyZSBvdXRsaWVycy4gZTYgZXNwZWNpYWxseSBpcyBzaG93aW5nIHVwIG9mdGVuIGFzIGV4dHJlbWUgdmFsdWVzIGFuZCB3aXRoIGV4dHJlbWUgdmFsdWVzIHRoYXQgYXJlIHZlcnkgZXh0cmVtZS4KClRoZSBkYXRhIGFyZSBhbHNvIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZC4KCiMgUHJlbGltaW5hcnkgUENBCkkgYWxzbyBjb25kdWN0IGEgUENBIHdpdGggYG9wbHMoKWAgd2hpY2ggd2lsbCBnaXZlIG1lIGEgZGlhZ25vc3RpYyBwbG90IHRoYXQgaWRlbnRpZmllcyBvdXRsaWVycy4KCmBgYHtyfQpwY2FfcHJlIDwtIG9wbHMoc2VsZWN0X2lmKGdjX3dpZGUsIGlzLmRvdWJsZSksIHBsb3RMID0gRkFMU0UsIHNjYWxlID0gInN0YW5kYXJkIikKcGxvdChwY2FfcHJlLCBwYXJMYWJWYyA9IGdjX3dpZGUkc2FtcGxlKQpgYGAKIyMgTG9nIFRyYW5zZm9ybQphZnRlciBsb2cgdHJhbnNmb3JtYXRpb246CmBgYHtyfQpnY19sb2cgPC0gZ2Nfd2lkZSAlPiUgbXV0YXRlX2lmKGlzLmRvdWJsZSwgbG9nKQoKcGNhX3ByZV9sb2cgPC0gb3BscyhzZWxlY3RfaWYoZ2NfbG9nLCBpcy5kb3VibGUpLCBwbG90TCA9IEZBTFNFLCBzY2FsZSA9ICJzdGFuZGFyZCIpCnBsb3QocGNhX3ByZV9sb2csIHBhckxhYlZjID0gZ2NfbG9nJHNhbXBsZSkKYGBgCgpCZWZvcmUgbG9nIHRyYW5zZm9ybWF0aW9uLCBlNiBhbmQgaTQgYXJlIGlkZW50aWZpZWQgYXMgcG9zc2libGUgb3V0bGllcnMuICBBZnRlciBsb2cgdHJhbnNmb3JtYXRpb24sIGU2LCBjNiBhbmQgYzkgYXJlIGlkZW50aWZpZWQgYXMgb3V0bGllcnMuCgojIyBDaGVjayB3aXRoIEhEb3V0bGllcnMgcGFja2FnZQpgYGB7cn0Kb3V0bGllcnMgPC0gSERvdXRsaWVycyhzZWxlY3RfaWYoZ2Nfd2lkZSwgaXMuZG91YmxlKSwgdHJhbnNmb3JtID0gRkFMU0UpCmdjX3dpZGVbb3V0bGllcnMsIF0KSERvdXRsaWVycyhzZWxlY3RfaWYoZ2NfbG9nLCBpcy5kb3VibGUpLCB0cmFuc2Zvcm0gPSBGQUxTRSkKYGBgCkhEb3V0bGllcnMgYWxzbyBpZGVudGlmaWVzIGU2LCBidXQgbG9nIHRyYW5zb3JtYXRpb24gc2VlbXMgdG8gZml4IGl0LgoKIyBSZW1vdmUgb3V0bGllcnMgCmU2IGlzIGZvciBzdXJlIGFuIG91dGxpZXIgYmFzZWQgb24gdW5pdmFyaWF0ZSBwbG90cy4gIEknbGwgYWxzbyByZW1vdmUgYzYgYW5kIGM5IGFuZCB1c2UgdGhlIGxvZyB0cmFuc2Zvcm1lZCBkYXRhIGZyb20gaGVyZSBvbiBvdXQuCmBgYHtyfQpvdXRsaWVycyA8LSBjKCJlNiIsICJjNiIsICJjOSIpCmdjX2xvZy4xIDwtIGdjX2xvZyAlPiUgCiAgZmlsdGVyKHNhbXBsZSAlIWluJSBvdXRsaWVycykgJT4lIAogIHNlbGVjdF9pZihub3RfZW1wdHkpICNhbmQgY2hlY2sgZm9yIGVtcHR5IGNvbHVtbnMKCmdjX3plcm9lcy4xIDwtIGdjX3plcm9lcyAlPiUgCiAgZmlsdGVyKHNhbXBsZSAlIWluJSBvdXRsaWVycykgJT4lIAogIHNlbGVjdF9pZihub3RfZW1wdHkpCmBgYAoKCiMgU2F2ZSBvdXRwdXQKU2F2ZSBkYXRhIGZyYW1lcyB3aXRoIG91dGxpZXJzIHJlbW92ZWQgZm9yIGFuYWx5c2lzIHN0ZXAuCmBgYHtyfQpnY19sb2cuMQpnY196ZXJvZXMuMQoKd3JpdGVfcmRzKGdjX2xvZy4xLCBoZXJlKCJjbGVhbmVkX2RhdGEiLCAiR0NNU193aWRlX2xvZ2VkLnJkcyIpKQp3cml0ZV9yZHMoZ2NfemVyb2VzLjEsIGhlcmUoImNsZWFuZWRfZGF0YSIsICJHQ01TX3RpZHlfemVyb2VzLnJkcyIpKQpgYGAKCg==