1 Background

In agriculture, it is important to have a high yield of crops. For lettuce plants, plants with many leaves are known to be preferred by the consumers.

One way to increase the number of leaves (or better, total leaf weight) is by using a fertilizer. Recently, there has been a tendency to rely more on natural fertilizers, such as compost. Near Ghent, the institute of research for agriculture and fishery is testing new, natural fertilization methods. One of these new fertilizers is called biochar. Biochar is a residual product from pyrolysis, a process in which biomass is burned under specific conditions (such as high pressure) in order to produce energy. Biochar is similar to charcoal, but has some very useful properties, such as retaining water in the soil. It also has a positive influence on the soil microbiome.

2 The lettuce dataset

The researchers hypothesize that biochar, compost and a combination of both biochar and compost can have a different influence on the growth of lettuce plants. To this end, they grew up lettuce plants in a greenhouse. The pots were filled with one of four soil types;

  1. Soil only (control)
  2. Soil supplemented with biochar (refoak)
  3. Soil supplemented with compost (compost)
  4. Soil supplemented with both biochar and compost (cobc)

The dataset freshweight_lettuce.txt contains the freshweight (in grams) for 28 lettuce plants (7 per condition). The researchers want to use an ANOVA test to assess if the treatments have a different effect on the growth of lettuce plants. If so, they will use a post-hoc test (Tuckey test) to discover which specific treatments have an effect.

Load the required libraries

library(tidyverse)
theme_set(theme_bw()) # Change ggplot2 default theme

library(multcomp)

3 Data import

lettuce <- read_csv("https://raw.githubusercontent.com/statOmics/PSLSData/main/freshweight_lettuce.txt")
# Take a glimpse at the data
glimpse(lettuce)
## Rows: 28
## Columns: 3
## $ id          <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1…
## $ treatment   <chr> "control", "control", "control", "control", "con…
## $ freshweight <dbl> 38, 34, 41, 43, 43, 29, 38, 59, 64, 57, 56, 50, …

First, we will set the treatment variable to a factor

lettuce <- lettuce %>%
  mutate(
    ## Convert treatment to factor
    treatment = as.factor(treatment),
    ## Relevels such that "control" is the reference
    treatment = relevel(treatment, ref = "control")
  )

4 Data exploration

## Count the number of observations per treatment
lettuce %>%
  count(treatment)

Now let’s make a boxplot displaying the freshweight of each treatment condition:

boxplot <- lettuce %>%
  ggplot(aes(x = treatment, y = freshweight, fill = treatment)) +
  scale_fill_brewer(palette = "RdGy") +
  geom_boxplot(outlier.shape = NA) +
  geom_jitter(width = 0.2) +
  ggtitle("Boxplot of the freshweigth for each treatment condition") +
  ylab("freshweight (gram)") +
  stat_summary(
    fun = mean, geom = "point",
    shape = 5, size = 3, color = "black"
  )
boxplot

Note that there are no clear outliers in the data. We can see that the mean freshweight is very comparable between the control and refoak treatments and between the compost and cobc treatments. We can also see that the mean freshweight is much higher in the cobc and control treatments than in the control and refoak treatments. But is this observed difference significant?

5 ANOVA

To study if the observed difference between the average freshweight values of the different treatment groups are significant, we may perform an ANOVA.

5.1 Formulate null and alternative hypotheses

The null hypothesis of ANOVA states that: \(H0\): The mean freshweigth is equal between the different treatment groups.

The alternative hypothesis of ANOVA states that: \(HA\): The mean freshweigth for at least one treatment group is different from the mean freshweight in at least one other treatment group.

5.2 Checking the assumptions for ANOVA

Before we may proceed with the analysis, we must make sure that all assumptions for ANOVA are met. ANOVA has three assumptions:

  1. The observations are independent of each other (in all groups)
  2. The data (freshweigth) must be normally distributed (in all groups)
  3. The variability within all groups is similar

5.2.1 Assumption of independence

The first assumption is met; we started of with 28 lettuce plants and we randomly submitted them to one of four treatment conditions. There is no reason to believe that the plants display systematic differences between treatment groups, other than the actual treatment.

5.2.2 Assumption of normality

For the second assumption, we must check normality in each group.

ggplot(lettuce, aes(sample = freshweight)) +
  geom_qq() +
  geom_qq_line(aes(col = treatment), show.legend = FALSE) +
  scale_color_brewer(palette = "Dark2") +
  facet_wrap(vars(treatment))

According to these plots, the normality assumption is met for each group. However, we need to be careful with relying on these plots, as they are only based on 7 observations per group. Indeed, checking the assumptions of normality (and equal variances) on such small datasets can be tricky. For instance, when sampling 7 datapoints from data that has an underlying distribution that is not normally distributed, the small sample may be normally distributed by chance. Equivalently, when sampling 7 datapoints from a normal distribution, the small sample may appear not normally distributed. We can show this as follows.

Simulate from uniform distribution:

set.seed(5)

## sample 7 observations from a uniform distribution, repeat 6 times
df <- data.frame(x = runif(7 * 6), rep = rep(1:6, each = 7))
ggplot(df, aes(sample = x)) +
  geom_qq() +
  geom_qq_line(col = "darkorchid") +
  facet_wrap(vars(rep)) +
  ggtitle("QQ plots for uniformly distributed data")

Even though the data originates from a uniform distribution, several samples meet the normality requirements by chance.

Sample from normal distribution:

set.seed(5)
## sample 7 observations from a normal distribution, repeat 6 times
df <- data.frame(x = rnorm(7 * 6), rep = rep(1:6, each = 7))
ggplot(df, aes(sample = x)) +
  geom_qq() +
  geom_qq_line(col = "dodgerblue") +
  facet_wrap(vars(rep)) +
  ggtitle("QQ plots for normally distributed data")

Even though the data originates from a normal distribution, some deviations can be observed due to random chance.

Alternatively, we may generate a qq-plot of the residuals of a linear model. These residuals should be normally distributed if the data for each treatment condition is normally distributed.

fit <- lm(freshweight ~ treatment, lettuce)
plot(fit, which = 2, col = fit$model$treatment)
legend("bottomright", levels(fit$model$treatment), text.col = 1:4)

The Q-Q plot of the residuals shows a slight skew to the left. Care should be taken when interpreting the results for this dataset. Due to the sample size, a better alternative might be a non-parametric approach.

5.2.3 Assumption of equal variances

We can check the assumption of equal variance with a boxplot:

boxplot

As a measure of variability, we may take the height of each box. This is the interval between the 25% and 75% quantile. Here we can see that this interval, as well as the length of the whiskers, is approximately equal for most groups. However, the variability of cobc does seem to be quite a bit larger than the variability in the refoak group.

With this little observations per group, it is very difficult to reliably assess the assumptions of normality and equal variances. In this tutorial, we will assume that all assumptions are met. In a later tutorial, “Kruskal_lettuce_plants.Rmd”, we will see would happen if we decide to reject these assumptions.

5.3 Modeling

fit <- lm(freshweight ~ treatment, data = lettuce)
fit_anova <- anova(fit)
fit_anova

The p-value of the ANOVA analysis is extremely significant (p-value = 8.308e-09), so we reject the null hypothesis that the mean freshweigth is equal for the different treatment groups. We can say that the mean freshweigth is significantly different between at least two treatment groups on the 5% significance level.

Based on this analysis, we do not yet know between which particular groups there is a significant difference. To assess this, we will perfrom the Tuckey post-hoc analysis.

5.4 Post-hoc analysis

We will perform a post-hoc analysis to look at the difference in fresweigth between each pairwise comparison of treatment groups. Importantly, with this strategy, the p-values will be automatically adjusted for multiple testing.

The null hypothesis for each pairwise test is that \(H0\) there is no difference in the average freshweight values between both groups.

The alternative hypothesis for each pairwise test states that \(HA\) there is indeed a difference in the average freshweight values between both groups.

mcp <- glht(fit, linfct = mcp(treatment = "Tukey"))
mcp_summary <- summary(mcp)
mcp_summary
## 
##   Simultaneous Tests for General Linear Hypotheses
## 
## Multiple Comparisons of Means: Tukey Contrasts
## 
## 
## Fit: lm(formula = freshweight ~ treatment, data = lettuce)
## 
## Linear Hypotheses:
##                        Estimate Std. Error t value Pr(>|t|)    
## cobc - control == 0      16.143      2.766   5.837   <0.001 ***
## compost - control == 0   20.857      2.766   7.541   <0.001 ***
## refoak - control == 0    -1.857      2.766  -0.671    0.907    
## compost - cobc == 0       4.714      2.766   1.704    0.343    
## refoak - cobc == 0      -18.000      2.766  -6.508   <0.001 ***
## refoak - compost == 0   -22.714      2.766  -8.213   <0.001 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## (Adjusted p values reported -- single-step method)
# We will also calculate the confidence interval on the mean differences.
mcp_confint <- confint(mcp)
mcp_confint
## 
##   Simultaneous Confidence Intervals
## 
## Multiple Comparisons of Means: Tukey Contrasts
## 
## 
## Fit: lm(formula = freshweight ~ treatment, data = lettuce)
## 
## Quantile = 2.7597
## 95% family-wise confidence level
##  
## 
## Linear Hypotheses:
##                        Estimate lwr      upr     
## cobc - control == 0     16.1429   8.5101  23.7757
## compost - control == 0  20.8571  13.2243  28.4899
## refoak - control == 0   -1.8571  -9.4899   5.7757
## compost - cobc == 0      4.7143  -2.9185  12.3471
## refoak - cobc == 0     -18.0000 -25.6328 -10.3672
## refoak - compost == 0  -22.7143 -30.3471 -15.0815

5.5 Conclusion

We have found an extremely significant dependence (p-value = 8.308^{-9}), between the mean freshweigth and the treatment condition on the global 5% significance level.

The mean freshweight of plants grown with compost is extremely significantly higher as compared to in control plants (Tuckey test, adjusted p-value < 0.0001, 20.9g higher, 95% CI: [13.2; 28.5]) and as compared to refoak plants (Tuckey test, adjusted p-value < 0.0001, 22.7g higher, 95% CI: [15.1; 30.3]).

The mean freshweight of plants grown with cobc is extremely significantly higher as compared to in control plants (Tuckey test, adjusted p-value < 0.0001, 16.1g higher, 95% CI: [8.5; 23.8]) and as compared to refoak plants (Tuckey test, adjusted p-value < 0.0001, 18.0g higher, 95% CI: [10.4; 25.6]).

We do not find enough evidence to claim a difference in mean freshweight between refoak and control plants or between cobc and compost plants.

We may conclude that supplementing soil with compost or with both compost and biochar will have a positive effect on the freshweigth of lettuce plants.

LS0tCnRpdGxlOiAiRXhlcmNpc2UgNy4xOiBBTk9WQSBvbiB0aGUgbGV0dHVjZSBkYXRhc2V0IC0gc29sdXRpb24iCmF1dGhvcjogIkxpZXZlbiBDbGVtZW50IGFuZCBKZXJvZW4gR2lsaXMiCmRhdGU6ICJzdGF0T21pY3MsIEdoZW50IFVuaXZlcnNpdHkgKGh0dHBzOi8vc3RhdG9taWNzLmdpdGh1Yi5pbykiCi0tLQoKIyBCYWNrZ3JvdW5kCgpJbiBhZ3JpY3VsdHVyZSwgaXQgaXMgaW1wb3J0YW50IHRvIGhhdmUgYSBoaWdoIHlpZWxkIG9mIGNyb3BzLiBGb3IgbGV0dHVjZQpwbGFudHMsIHBsYW50cyB3aXRoIG1hbnkgbGVhdmVzIGFyZSBrbm93biB0byBiZSBwcmVmZXJyZWQgYnkgdGhlIGNvbnN1bWVycy4KCk9uZSB3YXkgdG8gaW5jcmVhc2UgdGhlIG51bWJlciBvZiBsZWF2ZXMgKG9yIGJldHRlciwgdG90YWwKbGVhZiB3ZWlnaHQpIGlzIGJ5IHVzaW5nIGEgZmVydGlsaXplci4gUmVjZW50bHksIHRoZXJlIGhhcyBiZWVuCmEgdGVuZGVuY3kgdG8gcmVseSBtb3JlIG9uIG5hdHVyYWwgZmVydGlsaXplcnMsIHN1Y2ggYXMgY29tcG9zdC4KTmVhciBHaGVudCwgdGhlIGluc3RpdHV0ZSBvZiByZXNlYXJjaCBmb3IgYWdyaWN1bHR1cmUgYW5kIGZpc2hlcnkgaXMgdGVzdGluZwpuZXcsIG5hdHVyYWwgZmVydGlsaXphdGlvbiBtZXRob2RzLiBPbmUgb2YgdGhlc2UgbmV3IGZlcnRpbGl6ZXJzIGlzIGNhbGxlZApiaW9jaGFyLiBCaW9jaGFyIGlzIGEgcmVzaWR1YWwgcHJvZHVjdCBmcm9tIHB5cm9seXNpcywgYSBwcm9jZXNzIGluIHdoaWNoCmJpb21hc3MgaXMgYnVybmVkIHVuZGVyIHNwZWNpZmljIGNvbmRpdGlvbnMgKHN1Y2ggYXMgaGlnaCBwcmVzc3VyZSkgaW4gb3JkZXIKdG8gcHJvZHVjZSBlbmVyZ3kuIEJpb2NoYXIgaXMgc2ltaWxhciB0byBjaGFyY29hbCwgYnV0IGhhcyBzb21lIHZlcnkgdXNlZnVsCnByb3BlcnRpZXMsIHN1Y2ggYXMgcmV0YWluaW5nIHdhdGVyIGluIHRoZSBzb2lsLiBJdCBhbHNvIGhhcyBhIHBvc2l0aXZlCmluZmx1ZW5jZSBvbiB0aGUgc29pbCBtaWNyb2Jpb21lLgoKIyBUaGUgbGV0dHVjZSBkYXRhc2V0CgpUaGUgcmVzZWFyY2hlcnMgaHlwb3RoZXNpemUgdGhhdCBiaW9jaGFyLCBjb21wb3N0IGFuZAphIGNvbWJpbmF0aW9uIG9mIGJvdGggYmlvY2hhciBhbmQgY29tcG9zdCBjYW4gaGF2ZSBhIGRpZmZlcmVudCBpbmZsdWVuY2UKb24gdGhlIGdyb3d0aCBvZiBsZXR0dWNlIHBsYW50cy4gVG8gdGhpcyBlbmQsIHRoZXkgZ3JldyB1cApsZXR0dWNlIHBsYW50cyBpbiBhIGdyZWVuaG91c2UuIFRoZSBwb3RzIHdlcmUgZmlsbGVkIHdpdGgKb25lIG9mIGZvdXIgc29pbCB0eXBlczsKCjEuIFNvaWwgb25seSAoY29udHJvbCkKMi4gU29pbCBzdXBwbGVtZW50ZWQgd2l0aCBiaW9jaGFyIChyZWZvYWspCjMuIFNvaWwgc3VwcGxlbWVudGVkIHdpdGggY29tcG9zdCAoY29tcG9zdCkKNC4gU29pbCBzdXBwbGVtZW50ZWQgd2l0aCBib3RoIGJpb2NoYXIgYW5kIGNvbXBvc3QgKGNvYmMpCgpUaGUgZGF0YXNldCBgZnJlc2h3ZWlnaHRfbGV0dHVjZS50eHRgIGNvbnRhaW5zIHRoZSBmcmVzaHdlaWdodAooaW4gZ3JhbXMpIGZvciAyOCBsZXR0dWNlIHBsYW50cyAoNyBwZXIgY29uZGl0aW9uKS4gVGhlIHJlc2VhcmNoZXJzCndhbnQgdG8gdXNlIGFuIEFOT1ZBIHRlc3QgdG8gYXNzZXNzIGlmIHRoZSB0cmVhdG1lbnRzIGhhdmUgYSBkaWZmZXJlbnQgZWZmZWN0Cm9uIHRoZSBncm93dGggb2YgbGV0dHVjZSBwbGFudHMuIElmIHNvLCB0aGV5IHdpbGwgdXNlIGEgcG9zdC1ob2MgdGVzdAooVHVja2V5IHRlc3QpIHRvIGRpc2NvdmVyIHdoaWNoIHNwZWNpZmljIHRyZWF0bWVudHMgaGF2ZSBhbiBlZmZlY3QuCgpMb2FkIHRoZSByZXF1aXJlZCBsaWJyYXJpZXMKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQp0aGVtZV9zZXQodGhlbWVfYncoKSkgIyBDaGFuZ2UgZ2dwbG90MiBkZWZhdWx0IHRoZW1lCgpsaWJyYXJ5KG11bHRjb21wKQpgYGAKCiMgRGF0YSBpbXBvcnQKCmBgYHtyLG1lc3NhZ2U9RkFMU0V9CmxldHR1Y2UgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zdGF0T21pY3MvUFNMU0RhdGEvbWFpbi9mcmVzaHdlaWdodF9sZXR0dWNlLnR4dCIpCiMgVGFrZSBhIGdsaW1wc2UgYXQgdGhlIGRhdGEKZ2xpbXBzZShsZXR0dWNlKQpgYGAKCkZpcnN0LCB3ZSB3aWxsIHNldCB0aGUgYHRyZWF0bWVudGAgdmFyaWFibGUgdG8gYSBmYWN0b3IKCmBgYHtyfQpsZXR0dWNlIDwtIGxldHR1Y2UgJT4lCiAgbXV0YXRlKAogICAgIyMgQ29udmVydCB0cmVhdG1lbnQgdG8gZmFjdG9yCiAgICB0cmVhdG1lbnQgPSBhcy5mYWN0b3IodHJlYXRtZW50KSwKICAgICMjIFJlbGV2ZWxzIHN1Y2ggdGhhdCAiY29udHJvbCIgaXMgdGhlIHJlZmVyZW5jZQogICAgdHJlYXRtZW50ID0gcmVsZXZlbCh0cmVhdG1lbnQsIHJlZiA9ICJjb250cm9sIikKICApCmBgYAoKIyBEYXRhIGV4cGxvcmF0aW9uCgpgYGB7cn0KIyMgQ291bnQgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgcGVyIHRyZWF0bWVudApsZXR0dWNlICU+JQogIGNvdW50KHRyZWF0bWVudCkKYGBgCgpOb3cgbGV0J3MgbWFrZSBhIGJveHBsb3QgZGlzcGxheWluZyB0aGUgZnJlc2h3ZWlnaHQgb2YgZWFjaCB0cmVhdG1lbnQgY29uZGl0aW9uOgoKYGBge3J9CmJveHBsb3QgPC0gbGV0dHVjZSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB0cmVhdG1lbnQsIHkgPSBmcmVzaHdlaWdodCwgZmlsbCA9IHRyZWF0bWVudCkpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlJkR3kiKSArCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGUgPSBOQSkgKwogIGdlb21faml0dGVyKHdpZHRoID0gMC4yKSArCiAgZ2d0aXRsZSgiQm94cGxvdCBvZiB0aGUgZnJlc2h3ZWlndGggZm9yIGVhY2ggdHJlYXRtZW50IGNvbmRpdGlvbiIpICsKICB5bGFiKCJmcmVzaHdlaWdodCAoZ3JhbSkiKSArCiAgc3RhdF9zdW1tYXJ5KAogICAgZnVuID0gbWVhbiwgZ2VvbSA9ICJwb2ludCIsCiAgICBzaGFwZSA9IDUsIHNpemUgPSAzLCBjb2xvciA9ICJibGFjayIKICApCmJveHBsb3QKYGBgCgpOb3RlIHRoYXQgdGhlcmUgYXJlIG5vIGNsZWFyIG91dGxpZXJzIGluIHRoZSBkYXRhLgpXZSBjYW4gc2VlIHRoYXQgdGhlIG1lYW4gZnJlc2h3ZWlnaHQgaXMgdmVyeSBjb21wYXJhYmxlCmJldHdlZW4gdGhlIGNvbnRyb2wgYW5kIHJlZm9hayB0cmVhdG1lbnRzIGFuZCBiZXR3ZWVuIHRoZQpjb21wb3N0IGFuZCBjb2JjIHRyZWF0bWVudHMuIFdlIGNhbiBhbHNvIHNlZSB0aGF0IHRoZSBtZWFuCmZyZXNod2VpZ2h0IGlzIG11Y2ggaGlnaGVyIGluIHRoZSBjb2JjIGFuZCBjb250cm9sIHRyZWF0bWVudHMKdGhhbiBpbiB0aGUgY29udHJvbCBhbmQgcmVmb2FrIHRyZWF0bWVudHMuIEJ1dCBpcyB0aGlzCm9ic2VydmVkIGRpZmZlcmVuY2Ugc2lnbmlmaWNhbnQ/CgojIEFOT1ZBCgpUbyBzdHVkeSBpZiB0aGUgb2JzZXJ2ZWQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZQphdmVyYWdlIGZyZXNod2VpZ2h0IHZhbHVlcyBvZiB0aGUgZGlmZmVyZW50IHRyZWF0bWVudCBncm91cHMKYXJlIHNpZ25pZmljYW50LCB3ZSBtYXkgcGVyZm9ybSBhbiBBTk9WQS4KCiMjIEZvcm11bGF0ZSBudWxsIGFuZCBhbHRlcm5hdGl2ZSBoeXBvdGhlc2VzCgpUaGUgbnVsbCBoeXBvdGhlc2lzIG9mIEFOT1ZBIHN0YXRlcyB0aGF0OgokSDAkOiBUaGUgbWVhbiBmcmVzaHdlaWd0aCBpcyBlcXVhbCBiZXR3ZWVuIHRoZSBkaWZmZXJlbnQgdHJlYXRtZW50IGdyb3Vwcy4KClRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIG9mIEFOT1ZBIHN0YXRlcyB0aGF0OgokSEEkOiBUaGUgbWVhbiBmcmVzaHdlaWd0aCBmb3IgYXQgbGVhc3Qgb25lIHRyZWF0bWVudCBncm91cCBpcyBkaWZmZXJlbnQKZnJvbSB0aGUgbWVhbiBmcmVzaHdlaWdodCBpbiBhdCBsZWFzdCBvbmUgb3RoZXIgdHJlYXRtZW50IGdyb3VwLgoKIyMgQ2hlY2tpbmcgdGhlIGFzc3VtcHRpb25zIGZvciBBTk9WQQoKQmVmb3JlIHdlIG1heSBwcm9jZWVkIHdpdGggdGhlIGFuYWx5c2lzLCB3ZSBtdXN0IG1ha2Ugc3VyZSB0aGF0IGFsbAphc3N1bXB0aW9ucyBmb3IgQU5PVkEgYXJlIG1ldC4gQU5PVkEgaGFzIHRocmVlIGFzc3VtcHRpb25zOgoKMS4gVGhlIG9ic2VydmF0aW9ucyBhcmUgaW5kZXBlbmRlbnQgb2YgZWFjaCBvdGhlciAoaW4gYWxsIGdyb3VwcykKMi4gVGhlIGRhdGEgKGZyZXNod2VpZ3RoKSBtdXN0IGJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIChpbiBhbGwgZ3JvdXBzKQozLiBUaGUgdmFyaWFiaWxpdHkgd2l0aGluIGFsbCBncm91cHMgaXMgc2ltaWxhcgoKIyMjIEFzc3VtcHRpb24gb2YgaW5kZXBlbmRlbmNlCgpUaGUgZmlyc3QgYXNzdW1wdGlvbiBpcyBtZXQ7IHdlIHN0YXJ0ZWQgb2Ygd2l0aCAyOCBsZXR0dWNlIHBsYW50cyBhbmQKd2UgcmFuZG9tbHkgc3VibWl0dGVkIHRoZW0gdG8gb25lIG9mIGZvdXIgdHJlYXRtZW50IGNvbmRpdGlvbnMuIFRoZXJlCmlzIG5vIHJlYXNvbiB0byBiZWxpZXZlIHRoYXQgdGhlIHBsYW50cyBkaXNwbGF5IHN5c3RlbWF0aWMgZGlmZmVyZW5jZXMKYmV0d2VlbiB0cmVhdG1lbnQgZ3JvdXBzLCBvdGhlciB0aGFuIHRoZSBhY3R1YWwgdHJlYXRtZW50LgoKIyMjIEFzc3VtcHRpb24gb2Ygbm9ybWFsaXR5CgpGb3IgdGhlIHNlY29uZCBhc3N1bXB0aW9uLCB3ZSBtdXN0IGNoZWNrIG5vcm1hbGl0eSBpbiBlYWNoIGdyb3VwLgoKYGBge3J9CmdncGxvdChsZXR0dWNlLCBhZXMoc2FtcGxlID0gZnJlc2h3ZWlnaHQpKSArCiAgZ2VvbV9xcSgpICsKICBnZW9tX3FxX2xpbmUoYWVzKGNvbCA9IHRyZWF0bWVudCksIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpICsKICBmYWNldF93cmFwKHZhcnModHJlYXRtZW50KSkKYGBgCgpBY2NvcmRpbmcgdG8gdGhlc2UgcGxvdHMsIHRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbiBpcyBtZXQKZm9yIGVhY2ggZ3JvdXAuIEhvd2V2ZXIsIHdlIG5lZWQgdG8gYmUgY2FyZWZ1bCB3aXRoIHJlbHlpbmcgb24KdGhlc2UgcGxvdHMsIGFzIHRoZXkgYXJlIG9ubHkgYmFzZWQgb24gNyBvYnNlcnZhdGlvbnMgcGVyIGdyb3VwLgpJbmRlZWQsIGNoZWNraW5nIHRoZSBhc3N1bXB0aW9ucyBvZiBub3JtYWxpdHkgKGFuZCBlcXVhbCB2YXJpYW5jZXMpCm9uIHN1Y2ggc21hbGwgZGF0YXNldHMgY2FuIGJlIHRyaWNreS4gRm9yIGluc3RhbmNlLCB3aGVuCnNhbXBsaW5nIDcgZGF0YXBvaW50cyBmcm9tIGRhdGEgdGhhdCBoYXMgYW4gdW5kZXJseWluZyBkaXN0cmlidXRpb24KdGhhdCBpcyBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIHRoZSBzbWFsbCBzYW1wbGUgbWF5IGJlIG5vcm1hbGx5CmRpc3RyaWJ1dGVkIGJ5IGNoYW5jZS4gRXF1aXZhbGVudGx5LCB3aGVuIHNhbXBsaW5nIDcgZGF0YXBvaW50cwpmcm9tIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgdGhlIHNtYWxsIHNhbXBsZSBtYXkgYXBwZWFyIG5vdCBub3JtYWxseQpkaXN0cmlidXRlZC4gV2UgY2FuIHNob3cgdGhpcyBhcyBmb2xsb3dzLgoKU2ltdWxhdGUgZnJvbSB1bmlmb3JtIGRpc3RyaWJ1dGlvbjoKCmBgYHtyfQpzZXQuc2VlZCg1KQoKIyMgc2FtcGxlIDcgb2JzZXJ2YXRpb25zIGZyb20gYSB1bmlmb3JtIGRpc3RyaWJ1dGlvbiwgcmVwZWF0IDYgdGltZXMKZGYgPC0gZGF0YS5mcmFtZSh4ID0gcnVuaWYoNyAqIDYpLCByZXAgPSByZXAoMTo2LCBlYWNoID0gNykpCmdncGxvdChkZiwgYWVzKHNhbXBsZSA9IHgpKSArCiAgZ2VvbV9xcSgpICsKICBnZW9tX3FxX2xpbmUoY29sID0gImRhcmtvcmNoaWQiKSArCiAgZmFjZXRfd3JhcCh2YXJzKHJlcCkpICsKICBnZ3RpdGxlKCJRUSBwbG90cyBmb3IgdW5pZm9ybWx5IGRpc3RyaWJ1dGVkIGRhdGEiKQpgYGAKCkV2ZW4gdGhvdWdoIHRoZSBkYXRhIG9yaWdpbmF0ZXMgZnJvbSBhIHVuaWZvcm0gZGlzdHJpYnV0aW9uLCBzZXZlcmFsCnNhbXBsZXMgbWVldCB0aGUgbm9ybWFsaXR5IHJlcXVpcmVtZW50cyBieSBjaGFuY2UuCgpTYW1wbGUgZnJvbSBub3JtYWwgZGlzdHJpYnV0aW9uOgoKYGBge3J9CnNldC5zZWVkKDUpCiMjIHNhbXBsZSA3IG9ic2VydmF0aW9ucyBmcm9tIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgcmVwZWF0IDYgdGltZXMKZGYgPC0gZGF0YS5mcmFtZSh4ID0gcm5vcm0oNyAqIDYpLCByZXAgPSByZXAoMTo2LCBlYWNoID0gNykpCmdncGxvdChkZiwgYWVzKHNhbXBsZSA9IHgpKSArCiAgZ2VvbV9xcSgpICsKICBnZW9tX3FxX2xpbmUoY29sID0gImRvZGdlcmJsdWUiKSArCiAgZmFjZXRfd3JhcCh2YXJzKHJlcCkpICsKICBnZ3RpdGxlKCJRUSBwbG90cyBmb3Igbm9ybWFsbHkgZGlzdHJpYnV0ZWQgZGF0YSIpCmBgYAoKRXZlbiB0aG91Z2ggdGhlIGRhdGEgb3JpZ2luYXRlcyBmcm9tIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgc29tZSBkZXZpYXRpb25zCmNhbiBiZSBvYnNlcnZlZCBkdWUgdG8gcmFuZG9tIGNoYW5jZS4KCkFsdGVybmF0aXZlbHksIHdlIG1heSBnZW5lcmF0ZSBhIHFxLXBsb3Qgb2YgdGhlIHJlc2lkdWFscwpvZiBhIGxpbmVhciBtb2RlbC4gVGhlc2UgcmVzaWR1YWxzIHNob3VsZCBiZSBub3JtYWxseSBkaXN0cmlidXRlZAppZiB0aGUgZGF0YSBmb3IgZWFjaCB0cmVhdG1lbnQgY29uZGl0aW9uIGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLgoKYGBge3J9CmZpdCA8LSBsbShmcmVzaHdlaWdodCB+IHRyZWF0bWVudCwgbGV0dHVjZSkKcGxvdChmaXQsIHdoaWNoID0gMiwgY29sID0gZml0JG1vZGVsJHRyZWF0bWVudCkKbGVnZW5kKCJib3R0b21yaWdodCIsIGxldmVscyhmaXQkbW9kZWwkdHJlYXRtZW50KSwgdGV4dC5jb2wgPSAxOjQpCmBgYAoKVGhlIFEtUSBwbG90IG9mIHRoZSByZXNpZHVhbHMgc2hvd3MgYSBzbGlnaHQgc2tldyB0byB0aGUgbGVmdC4gQ2FyZSBzaG91bGQgYmUKdGFrZW4gd2hlbiBpbnRlcnByZXRpbmcgdGhlIHJlc3VsdHMgZm9yIHRoaXMgZGF0YXNldC4gRHVlIHRvIHRoZSBzYW1wbGUgc2l6ZSwgYQpiZXR0ZXIgYWx0ZXJuYXRpdmUgbWlnaHQgYmUgYSBub24tcGFyYW1ldHJpYyBhcHByb2FjaC4KCgojIyMgQXNzdW1wdGlvbiBvZiBlcXVhbCB2YXJpYW5jZXMKCldlIGNhbiBjaGVjayB0aGUgYXNzdW1wdGlvbiBvZiBlcXVhbCB2YXJpYW5jZSB3aXRoIGEgYm94cGxvdDoKCmBgYHtyfQpib3hwbG90CmBgYAoKQXMgYSBtZWFzdXJlIG9mIHZhcmlhYmlsaXR5LCB3ZSBtYXkgdGFrZSB0aGUgaGVpZ2h0Cm9mIGVhY2ggYm94LiBUaGlzIGlzIHRoZSBpbnRlcnZhbCBiZXR3ZWVuCnRoZSAyNSUgYW5kIDc1JSBxdWFudGlsZS4gSGVyZSB3ZSBjYW4gc2VlIHRoYXQgdGhpcwppbnRlcnZhbCwgYXMgd2VsbCBhcyB0aGUgbGVuZ3RoIG9mIHRoZSB3aGlza2VycywgaXMKYXBwcm94aW1hdGVseSBlcXVhbCBmb3IgbW9zdCBncm91cHMuIEhvd2V2ZXIsIHRoZQp2YXJpYWJpbGl0eSBvZiBjb2JjIGRvZXMgc2VlbSB0byBiZSBxdWl0ZSBhIGJpdCBsYXJnZXIKdGhhbiB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIHJlZm9hayBncm91cC4KCldpdGggdGhpcyBsaXR0bGUgb2JzZXJ2YXRpb25zIHBlciBncm91cCwgaXQgaXMgdmVyeQpkaWZmaWN1bHQgdG8gcmVsaWFibHkgYXNzZXNzIHRoZSBhc3N1bXB0aW9ucyBvZiBub3JtYWxpdHkgYW5kIGVxdWFsIHZhcmlhbmNlcy4KSW4gdGhpcyB0dXRvcmlhbCwgd2Ugd2lsbCBhc3N1bWUgdGhhdCBhbGwgYXNzdW1wdGlvbnMgYXJlIG1ldC4KKipJbiBhIGxhdGVyIHR1dG9yaWFsLCAiS3J1c2thbF9sZXR0dWNlX3BsYW50cy5SbWQiLCoqCioqd2Ugd2lsbCBzZWUgd291bGQgaGFwcGVuIGlmIHdlIGRlY2lkZSB0byByZWplY3QgdGhlc2UgYXNzdW1wdGlvbnMuKioKCiMjIE1vZGVsaW5nCgpgYGB7cn0KZml0IDwtIGxtKGZyZXNod2VpZ2h0IH4gdHJlYXRtZW50LCBkYXRhID0gbGV0dHVjZSkKZml0X2Fub3ZhIDwtIGFub3ZhKGZpdCkKZml0X2Fub3ZhCmBgYAoKVGhlIHAtdmFsdWUgb2YgdGhlIEFOT1ZBIGFuYWx5c2lzIGlzIGV4dHJlbWVseSBzaWduaWZpY2FudAoocC12YWx1ZSA9IGByIGZvcm1hdChmaXRfYW5vdmEkIlByKD5GKSJbMV0sZGlnaXRzPTQpYCksCnNvIHdlIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIHRoYXQgdGhlIG1lYW4KZnJlc2h3ZWlndGggaXMgZXF1YWwgZm9yIHRoZSBkaWZmZXJlbnQgdHJlYXRtZW50IGdyb3Vwcy4KV2UgY2FuIHNheSB0aGF0IHRoZSBtZWFuIGZyZXNod2VpZ3RoIGlzIHNpZ25pZmljYW50bHkgZGlmZmVyZW50CmJldHdlZW4gYXQgbGVhc3QgdHdvIHRyZWF0bWVudCBncm91cHMgb24gdGhlIDUlIHNpZ25pZmljYW5jZSBsZXZlbC4KCkJhc2VkIG9uIHRoaXMgYW5hbHlzaXMsIHdlIGRvIG5vdCB5ZXQga25vdyBiZXR3ZWVuIHdoaWNoIHBhcnRpY3VsYXIKZ3JvdXBzIHRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZS4gVG8gYXNzZXNzIHRoaXMsIHdlIHdpbGwKcGVyZnJvbSB0aGUgVHVja2V5IHBvc3QtaG9jIGFuYWx5c2lzLgoKIyMgUG9zdC1ob2MgYW5hbHlzaXMKCldlIHdpbGwgcGVyZm9ybSBhIHBvc3QtaG9jIGFuYWx5c2lzIHRvIGxvb2sgYXQgdGhlIGRpZmZlcmVuY2UKaW4gZnJlc3dlaWd0aCBiZXR3ZWVuIGVhY2ggcGFpcndpc2UgY29tcGFyaXNvbiBvZiB0cmVhdG1lbnQgZ3JvdXBzLgpJbXBvcnRhbnRseSwgd2l0aCB0aGlzIHN0cmF0ZWd5LCB0aGUgcC12YWx1ZXMgd2lsbCBiZSBhdXRvbWF0aWNhbGx5CmFkanVzdGVkIGZvciBtdWx0aXBsZSB0ZXN0aW5nLgoKVGhlIG51bGwgaHlwb3RoZXNpcyBmb3IgZWFjaCBwYWlyd2lzZSB0ZXN0IGlzIHRoYXQKJEgwJCB0aGVyZSBpcyBubyBkaWZmZXJlbmNlIGluIHRoZSBhdmVyYWdlIGZyZXNod2VpZ2h0IHZhbHVlcyBiZXR3ZWVuCmJvdGggZ3JvdXBzLgoKVGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMgZm9yIGVhY2ggcGFpcndpc2UgdGVzdCBzdGF0ZXMgdGhhdAokSEEkIHRoZXJlIGlzIGluZGVlZCBhIGRpZmZlcmVuY2UgaW4gdGhlIGF2ZXJhZ2UgZnJlc2h3ZWlnaHQgdmFsdWVzCmJldHdlZW4gYm90aCBncm91cHMuCgpgYGB7cn0KbWNwIDwtIGdsaHQoZml0LCBsaW5mY3QgPSBtY3AodHJlYXRtZW50ID0gIlR1a2V5IikpCm1jcF9zdW1tYXJ5IDwtIHN1bW1hcnkobWNwKQptY3Bfc3VtbWFyeQoKIyBXZSB3aWxsIGFsc28gY2FsY3VsYXRlIHRoZSBjb25maWRlbmNlIGludGVydmFsIG9uIHRoZSBtZWFuIGRpZmZlcmVuY2VzLgptY3BfY29uZmludCA8LSBjb25maW50KG1jcCkKbWNwX2NvbmZpbnQKYGBgCgojIyBDb25jbHVzaW9uCgpXZSBoYXZlIGZvdW5kIGFuIGV4dHJlbWVseSBzaWduaWZpY2FudCBkZXBlbmRlbmNlCihwLXZhbHVlID0gYHIgcm91bmQoZml0X2Fub3ZhJCJQcig+RikiWzFdLGRpZ2l0cz0xMilgKSwKYmV0d2VlbiB0aGUgbWVhbiBmcmVzaHdlaWd0aCBhbmQgdGhlIHRyZWF0bWVudCBjb25kaXRpb24Kb24gdGhlIGdsb2JhbCA1JSBzaWduaWZpY2FuY2UgbGV2ZWwuCgpUaGUgbWVhbiBmcmVzaHdlaWdodCBvZiBwbGFudHMgZ3Jvd24gd2l0aCBjb21wb3N0IGlzIGV4dHJlbWVseQpzaWduaWZpY2FudGx5IGhpZ2hlciBhcyBjb21wYXJlZCB0byBpbiBjb250cm9sIHBsYW50cyAoVHVja2V5IHRlc3QsCmFkanVzdGVkIHAtdmFsdWUgPCAwLjAwMDEsIDIwLjlnIGhpZ2hlciwgOTUlIENJOiBbMTMuMjsgMjguNV0pCmFuZCBhcyBjb21wYXJlZCB0byByZWZvYWsgcGxhbnRzIChUdWNrZXkgdGVzdCwgYWRqdXN0ZWQKcC12YWx1ZSA8IDAuMDAwMSwgMjIuN2cgaGlnaGVyLCA5NSUgQ0k6IFsxNS4xOyAzMC4zXSkuCgpUaGUgbWVhbiBmcmVzaHdlaWdodCBvZiBwbGFudHMgZ3Jvd24gd2l0aCBjb2JjIGlzIGV4dHJlbWVseQpzaWduaWZpY2FudGx5IGhpZ2hlciBhcyBjb21wYXJlZCB0byBpbiBjb250cm9sIHBsYW50cyAoVHVja2V5IHRlc3QsCmFkanVzdGVkIHAtdmFsdWUgPCAwLjAwMDEsIDE2LjFnIGhpZ2hlciwgOTUlIENJOiBbOC41OyAyMy44XSkKYW5kIGFzIGNvbXBhcmVkIHRvIHJlZm9hayBwbGFudHMgKFR1Y2tleSB0ZXN0LCBhZGp1c3RlZApwLXZhbHVlIDwgMC4wMDAxLCAxOC4wZyBoaWdoZXIsIDk1JSBDSTogWzEwLjQ7IDI1LjZdKS4KCldlIGRvIG5vdCBmaW5kIGVub3VnaCBldmlkZW5jZSB0byBjbGFpbSBhIGRpZmZlcmVuY2UgaW4gbWVhbgpmcmVzaHdlaWdodCBiZXR3ZWVuIHJlZm9hayBhbmQgY29udHJvbCBwbGFudHMgb3IgYmV0d2VlbiBjb2JjCmFuZCBjb21wb3N0IHBsYW50cy4KCldlIG1heSBjb25jbHVkZSB0aGF0IHN1cHBsZW1lbnRpbmcgc29pbCB3aXRoIGNvbXBvc3Qgb3Igd2l0aApib3RoIGNvbXBvc3QgYW5kIGJpb2NoYXIgd2lsbCBoYXZlIGEgcG9zaXRpdmUgZWZmZWN0IG9uIHRoZQpmcmVzaHdlaWd0aCBvZiBsZXR0dWNlIHBsYW50cy4K