To experiment with the concepts of ANOVA, we will work with a dataset on the freshweight of lettuce plants.

1 The lettuce dataset

In agriculture, it is important to have a high yield of crops. For lettuce plants, this means having as many leaves as possible per plant. This is known to preferred by the consumers.

One way to increase the number of leaves (or better, total leaf weigth) is by using a fertilizer. Recently, there is 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.

The researcher want to find out if biochar, compost and a combination of both biochar and compost have an 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 find out whether or not there is an effect of one or more of the treatments on the growth of lettuce plants. If so, they will use a post-hoc test (Tuckey test) to find which specific treatments have an effect.

Load the required libraries

library(tidyverse)
library(car)

2 Data import

lettuce <- read_csv("https://raw.githubusercontent.com/GTPB/PSLS20/master/data/freshweight_lettuce.txt")
#Take a glimpse at the data
glimpse(lettuce)
## Observations: 28
## Variables: 3
## $ id          <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17…
## $ treatment   <chr> "control", "control", "control", "control", "control", "c…
## $ freshweight <dbl> 38, 34, 41, 43, 43, 29, 38, 59, 64, 57, 56, 50, 64, 62, 3…

First, we will set the treatment variable to a factor

## treatment to factor
lettuce <- lettuce %>%
  mutate(treatment = as.factor(treatment))

3 Data exploration

## Count the number of observations per treatment
lettuce %>%
  count(treatment)
## # A tibble: 4 x 2
##   treatment     n
##   <fct>     <int>
## 1 cobc          7
## 2 compost       7
## 3 control       7
## 4 refoak        7

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

lettuce %>%
  ggplot(aes(x=treatment,y=freshweight,fill=treatment)) + 
  scale_fill_brewer(palette="RdGy") +
  theme_bw() +
  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.y=mean, geom="point", shape=5, size=3, color="black", fill="black")

Note that there are no clear outliers in the data. We can see that the mean fresweight 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?

3.1 Checking the assumptions for ANOVA

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

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.

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

3.1.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.

Question: What would happen if all of the control and refoak plants were grown in 1 greenhouse and the other two conditions were grown in another greenhouse?

3.1.2 Assumption of normality

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

## get qqplots for each individual treatment group
par(mfrow = c(2,2))
for(i in levels(lettuce$treatment)){
  qqPlot(subset(lettuce,treatment == i)$freshweight, main = i, ylab = "")
}

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)
par(mfrow = c(3,2),mar=c(2, 4, 2, 0.2))
for (i in 1:6) {
  x <- runif(7) ## sample 7 observations from a uniform distribution
  qqPlot(x,ylab = "")
}

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

Sample from normal distribution:

set.seed(5)
par(mfrow = c(3,2),mar=c(2, 4, 2, 0.2))
for (i in 1:6) {
  x <- rnorm(7) ## sample 7 sobservations from a normal distribution
  qqPlot(x,ylab = "")
}

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

As an alternative, 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 data seems to be approximately normally distributed, with a slight left skew.

3.1.3 Assumption of equal variances

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

lettuce %>%
  ggplot(aes(x=treatment,y=freshweight,fill=treatment)) + 
  scale_fill_brewer(palette="RdGy") +
  theme_bw() +
  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.y=mean, geom="point", shape=5, size=3, color="black", fill="black")

As a measure of variability, we may take the height of each boxplot’s 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 the assumptions.

4 Data analysis

4.1 ANOVA

fit <- lm(freshweight~treatment, data=lettuce)
fit_anova <- anova(fit)
fit_anova
## Analysis of Variance Table
## 
## Response: freshweight
##           Df  Sum Sq Mean Sq F value    Pr(>F)    
## treatment  3 2732.14  910.71  34.015 8.308e-09 ***
## Residuals 24  642.57   26.77                      
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

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.

4.2 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.

library(multcomp, quietly = TRUE)
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|)    
## compost - cobc == 0       4.714      2.766   1.704    0.343    
## control - cobc == 0     -16.143      2.766  -5.837   <0.001 ***
## refoak - cobc == 0      -18.000      2.766  -6.508   <0.001 ***
## control - compost == 0  -20.857      2.766  -7.541   <0.001 ***
## refoak - compost == 0   -22.714      2.766  -8.213   <0.001 ***
## refoak - control == 0    -1.857      2.766  -0.671    0.907    
## ---
## 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     
## compost - cobc == 0      4.7143  -2.9185  12.3471
## control - cobc == 0    -16.1429 -23.7757  -8.5101
## refoak - cobc == 0     -18.0000 -25.6328 -10.3672
## control - compost == 0 -20.8571 -28.4899 -13.2243
## refoak - compost == 0  -22.7143 -30.3471 -15.0815
## refoak - control == 0   -1.8571  -9.4899   5.7757

5 Conclusion

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

The mean fresweight 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 fresweight 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.

Question: can we extrapolate these results to another vegetable for instance, spinache?

LS0tCnRpdGxlOiAiVHV0b3JpYWwgNy4xOiBBTk9WQSBpbiB0aGUgbGV0dHVjZSBkYXRhc2V0IiAgIApvdXRwdXQ6CiAgICBodG1sX2RvY3VtZW50OgogICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlICAgIAogICAgICB0aGVtZTogY29zbW8KICAgICAgdG9jOiB0cnVlCiAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQotLS0KClRvIGV4cGVyaW1lbnQgd2l0aCB0aGUgY29uY2VwdHMgb2YgQU5PVkEsIHdlIHdpbGwgd29yayAKd2l0aCBhIGRhdGFzZXQgb24gdGhlIGZyZXNod2VpZ2h0IG9mIGxldHR1Y2UgcGxhbnRzLgoKIyBUaGUgbGV0dHVjZSBkYXRhc2V0CgpJbiBhZ3JpY3VsdHVyZSwgaXQgaXMgaW1wb3J0YW50IHRvIGhhdmUgYSBoaWdoIHlpZWxkCm9mIGNyb3BzLiBGb3IgbGV0dHVjZSBwbGFudHMsIHRoaXMgbWVhbnMgaGF2aW5nIGFzIG1hbnkKbGVhdmVzIGFzIHBvc3NpYmxlIHBlciBwbGFudC4gVGhpcyBpcyBrbm93biB0byBwcmVmZXJyZWQKYnkgdGhlIGNvbnN1bWVycy4KCk9uZSB3YXkgdG8gaW5jcmVhc2UgdGhlIG51bWJlciBvZiBsZWF2ZXMgKG9yIGJldHRlciwgdG90YWwKbGVhZiB3ZWlndGgpIGlzIGJ5IHVzaW5nIGEgZmVydGlsaXplci4gUmVjZW50bHksIHRoZXJlIGlzIAphIHRlbmRlbmN5IHRvIHJlbHkgbW9yZSBvbiBuYXR1cmFsIGZlcnRpbGl6ZXJzLCBzdWNoIGFzIGNvbXBvc3QuCk5lYXIgR2hlbnQsIHRoZSBpbnN0aXR1dGUgb2YgcmVzZWFyY2ggZm9yIGFncmljdWx0dXJlIGFuZCBmaXNoZXJ5CmlzIHRlc3RpbmcgbmV3LCBuYXR1cmFsIGZlcnRpbGl6YXRpb24gbWV0aG9kcy4gT25lIG9mIHRoZXNlIG5ldwpmZXJ0aWxpemVycyBpcyBjYWxsZWQgYmlvY2hhci4gQmlvY2hhciBpcyBhIHJlc2lkdWFsIHByb2R1Y3QKZnJvbSBweXJvbHlzaXMsIGEgcHJvY2VzcyBpbiB3aGljaCBiaW9tYXNzIGlzIGJ1cm5lZCB1bmRlcgpzcGVjaWZpYyBjb25kaXRpb25zIChzdWNoIGFzIGhpZ2ggcHJlc3N1cmUpIGluIG9yZGVyIHRvIHByb2R1Y2UKZW5lcmd5LiBCaW9jaGFyIGlzIHNpbWlsYXIgdG8gY2hhcmNvYWwsIGJ1dCBoYXMgc29tZSB2ZXJ5IAp1c2VmdWwgcHJvcGVydGllcywgc3VjaCBhcyByZXRhaW5pbmcgd2F0ZXIgaW4gdGhlIHNvaWwuIEl0IGFsc28KaGFzIGEgcG9zaXRpdmUgaW5mbHVlbmNlIG9uIHRoZSBzb2lsIG1pY3JvYmlvbWUuCgpUaGUgcmVzZWFyY2hlciB3YW50IHRvIGZpbmQgb3V0IGlmIGJpb2NoYXIsIGNvbXBvc3QgYW5kCmEgY29tYmluYXRpb24gb2YgYm90aCBiaW9jaGFyIGFuZCBjb21wb3N0IGhhdmUgYW4gaW5mbHVlbmNlCm9uIHRoZSBncm93dGggb2YgbGV0dHVjZSBwbGFudHMuIFRvIHRoaXMgZW5kLCB0aGV5IGdyZXcgdXAKbGV0dHVjZSBwbGFudHMgaW4gYSBncmVlbmhvdXNlLiBUaGUgcG90cyB3ZXJlIGZpbGxlZCB3aXRoCm9uZSBvZiBmb3VyIHNvaWwgdHlwZXM7CgoxLiBTb2lsIG9ubHkgKGNvbnRyb2wpCjIuIFNvaWwgc3VwcGxlbWVudGVkIHdpdGggYmlvY2hhciAocmVmb2FrKQozLiBTb2lsIHN1cHBsZW1lbnRlZCB3aXRoIGNvbXBvc3QgKGNvbXBvc3QpCjQuIFNvaWwgc3VwcGxlbWVudGVkIHdpdGggYm90aCBiaW9jaGFyIGFuZCBjb21wb3N0IChjb2JjKQoKVGhlIGRhdGFzZXQgYGZyZXNod2VpZ2h0X2xldHR1Y2UudHh0YCBjb250YWlucyB0aGUgZnJlc2h3ZWlnaHQKKGluIGdyYW1zKSBmb3IgMjggbGV0dHVjZSBwbGFudHMgKDcgcGVyIGNvbmRpdGlvbikuIFRoZSByZXNlYXJjaGVycwp3YW50IHRvIHVzZSBhbiBBTk9WQSB0ZXN0IHRvIGZpbmQgb3V0IHdoZXRoZXIgb3Igbm90IHRoZXJlIGlzIGFuCmVmZmVjdCBvZiBvbmUgb3IgbW9yZSBvZiB0aGUgdHJlYXRtZW50cyBvbiB0aGUgZ3Jvd3RoIG9mIGxldHR1Y2UgCnBsYW50cy4gSWYgc28sIHRoZXkgd2lsbCB1c2UgYSBwb3N0LWhvYyB0ZXN0IChUdWNrZXkgdGVzdCkgdG8gZmluZAp3aGljaCBzcGVjaWZpYyB0cmVhdG1lbnRzIGhhdmUgYW4gZWZmZWN0LgoKTG9hZCB0aGUgcmVxdWlyZWQgbGlicmFyaWVzCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShjYXIpCmBgYAoKIyBEYXRhIGltcG9ydAoKYGBge3IsbWVzc2FnZT1GQUxTRX0KbGV0dHVjZSA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0dUUEIvUFNMUzIwL21hc3Rlci9kYXRhL2ZyZXNod2VpZ2h0X2xldHR1Y2UudHh0IikKI1Rha2UgYSBnbGltcHNlIGF0IHRoZSBkYXRhCmdsaW1wc2UobGV0dHVjZSkKYGBgCgpGaXJzdCwgd2Ugd2lsbCBzZXQgdGhlIGB0cmVhdG1lbnRgIHZhcmlhYmxlIHRvIGEgZmFjdG9yCgpgYGB7cn0KIyMgdHJlYXRtZW50IHRvIGZhY3RvcgpsZXR0dWNlIDwtIGxldHR1Y2UgJT4lCiAgbXV0YXRlKHRyZWF0bWVudCA9IGFzLmZhY3Rvcih0cmVhdG1lbnQpKQpgYGAKCiMgRGF0YSBleHBsb3JhdGlvbgoKYGBge3J9CiMjIENvdW50IHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIHBlciB0cmVhdG1lbnQKbGV0dHVjZSAlPiUKICBjb3VudCh0cmVhdG1lbnQpCmBgYAoKTm93IGxldCdzIG1ha2UgYSBib3hwbG90IGRpc3BsYXlpbmcgdGhlIGZyZXNod2VpZ2h0Cm9mIGVhY2ggdHJlYXRtZW50IGNvbmRpdGlvbjoKCmBgYHtyfQpsZXR0dWNlICU+JQogIGdncGxvdChhZXMoeD10cmVhdG1lbnQseT1mcmVzaHdlaWdodCxmaWxsPXRyZWF0bWVudCkpICsgCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iUmRHeSIpICsKICB0aGVtZV9idygpICsKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZT1OQSkgKyAKICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMikgKwogIGdndGl0bGUoIkJveHBsb3Qgb2YgdGhlIGZyZXNod2VpZ3RoIGZvciBlYWNoIHRyZWF0bWVudCBjb25kaXRpb24iKSArCiAgeWxhYigiZnJlc2h3ZWlnaHQgKGdyYW0pIikgKyAKICBzdGF0X3N1bW1hcnkoZnVuLnk9bWVhbiwgZ2VvbT0icG9pbnQiLCBzaGFwZT01LCBzaXplPTMsIGNvbG9yPSJibGFjayIsIGZpbGw9ImJsYWNrIikKYGBgCgpOb3RlIHRoYXQgdGhlcmUgYXJlIG5vIGNsZWFyIG91dGxpZXJzIGluIHRoZSBkYXRhLgpXZSBjYW4gc2VlIHRoYXQgdGhlIG1lYW4gZnJlc3dlaWdodCBpcyB2ZXJ5IGNvbXBhcmFibGUKYmV0d2VlbiB0aGUgY29udHJvbCBhbmQgcmVmb2FrIHRyZWF0bWVudHMgYW5kIGJldHdlZW4gdGhlCmNvbXBvc3QgYW5kIGNvYmMgdHJlYXRtZW50cy4gV2UgY2FuIGFsc28gc2VlIHRoYXQgdGhlIG1lYW4KZnJlc2h3ZWlnaHQgaXMgbXVjaCBoaWdoZXIgaW4gdGhlIGNvYmMgYW5kIGNvbnRyb2wgdHJlYXRtZW50cwp0aGFuIGluIHRoZSBjb250cm9sIGFuZCByZWZvYWsgdHJlYXRtZW50cy4gQnV0IGlzIHRoaXMKb2JzZXJ2ZWQgZGlmZmVyZW5jZSBzaWduaWZpY2FudD8KCiMjIENoZWNraW5nIHRoZSBhc3N1bXB0aW9ucyBmb3IgQU5PVkEKClRvIHN0dWR5IHdoZXRoZXIgb3Igbm90IHRoZSBvYnNlcnZlZCBkaWZmZXJlbmNlIGJldHdlZW4gdGhlCmF2ZXJhZ2UgZnJlc2h3ZWlnaHQgdmFsdWVzIG9mIHRoZSBkaWZmZXJlbnQgdHJlYXRtZW50IGdyb3VwcwphcmUgc2lnbmlmaWNhbnQsIHdlIG1heSBwZXJmb3JtIGFuIEFOT1ZBLgoKVGhlIG51bGwgaHlwb3RoZXNpcyBvZiBBTk9WQSBzdGF0ZXMgdGhhdDoKJEgwJDogVGhlIG1lYW4gZnJlc2h3ZWlndGggaXMgZXF1YWwgYmV0d2VlbiB0aGUgZGlmZmVyZW50IHRyZWF0bWVudCBncm91cHMuCgpUaGUgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyBvZiBBTk9WQSBzdGF0ZXMgdGhhdDoKJEhBJDogVGhlIG1lYW4gZnJlc2h3ZWlndGggZm9yIGF0IGxlYXN0IG9uZSB0cmVhdG1lbnQgZ3JvdXAgaXMgZGlmZmVyZW50IApmcm9tIHRoZSBtZWFuIGZyZXNod2VpZ2h0IGluIGF0IGxlYXN0IG9uZSBvdGhlciB0cmVhdG1lbnQgZ3JvdXAuCgpCZWZvcmUgd2UgbWF5IHByb2NlZWQgd2l0aCB0aGUgYW5hbHlzaXMsIHdlIG11c3QgbWFrZSBzdXJlIHRoYXQgYWxsCmFzc3VtcHRpb25zIGZvciBBTk9WQSBhcmUgbWV0LiBBTk9WQSBoYXMgdGhyZWUgYXNzdW1wdGlvbnM6CgoxLiBUaGUgb2JzZXJ2YXRpb25zIGFyZSBpbmRlcGVuZGVudCBvZiBlYWNoIG90aGVyIChpbiBhbGwgZ3JvdXBzKQoyLiBUaGUgZGF0YSAoZnJlc2h3ZWlndGgpIG11c3QgYmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgKGluIGFsbCBncm91cHMpCjMuIFRoZSB2YXJpYWJpbGl0eSB3aXRoaW4gYWxsIGdyb3VwcyBpcyBzaW1pbGFyCgojIyMgQXNzdW1wdGlvbiBvZiBpbmRlcGVuZGVuY2UKClRoZSBmaXJzdCBhc3N1bXB0aW9uIGlzIG1ldDsgd2Ugc3RhcnRlZCBvZiB3aXRoIDI4IGxldHR1Y2UgcGxhbnRzIGFuZAp3ZSByYW5kb21seSBzdWJtaXR0ZWQgdGhlbSB0byBvbmUgb2YgZm91ciB0cmVhdG1lbnQgY29uZGl0aW9ucy4gVGhlcmUKaXMgbm8gcmVhc29uIHRvIGJlbGlldmUgdGhhdCB0aGUgcGxhbnRzIGRpc3BsYXkgc3lzdGVtYXRpYyBkaWZmZXJlbmNlcwpiZXR3ZWVuIHRyZWF0bWVudCBncm91cHMsIG90aGVyIHRoYW4gdGhlIGFjdHVhbCB0cmVhdG1lbnQuCgoqKlF1ZXN0aW9uOiBXaGF0IHdvdWxkIGhhcHBlbiBpZiBhbGwgb2YgdGhlIGNvbnRyb2wgYW5kIHJlZm9hayoqCioqcGxhbnRzIHdlcmUgZ3Jvd24gaW4gMSBncmVlbmhvdXNlIGFuZCB0aGUgb3RoZXIgdHdvIGNvbmRpdGlvbnMqKgoqKndlcmUgZ3Jvd24gaW4gYW5vdGhlciBncmVlbmhvdXNlPyoqCgojIyMgQXNzdW1wdGlvbiBvZiBub3JtYWxpdHkKCkZvciB0aGUgc2Vjb25kIGFzc3VtcHRpb24sIHdlIG11c3QgY2hlY2sgbm9ybWFsaXR5IGluIGVhY2ggZ3JvdXAuCgpgYGB7cn0KIyMgZ2V0IHFxcGxvdHMgZm9yIGVhY2ggaW5kaXZpZHVhbCB0cmVhdG1lbnQgZ3JvdXAKcGFyKG1mcm93ID0gYygyLDIpKQpmb3IoaSBpbiBsZXZlbHMobGV0dHVjZSR0cmVhdG1lbnQpKXsKICBxcVBsb3Qoc3Vic2V0KGxldHR1Y2UsdHJlYXRtZW50ID09IGkpJGZyZXNod2VpZ2h0LCBtYWluID0gaSwgeWxhYiA9ICIiKQp9CmBgYAoKQWNjb3JkaW5nIHRvIHRoZXNlIHBsb3RzLCB0aGUgbm9ybWFsaXR5IGFzc3VtcHRpb24gaXMgbWV0CmZvciBlYWNoIGdyb3VwLiBIb3dldmVyLCB3ZSBuZWVkIHRvIGJlIGNhcmVmdWwgd2l0aCByZWx5aW5nIG9uIAp0aGVzZSBwbG90cywgYXMgdGhleSBhcmUgb25seSBiYXNlZCBvbiA3IG9ic2VydmF0aW9ucyBwZXIgZ3JvdXAuCkluZGVlZCwgY2hlY2tpbmcgdGhlIGFzc3VtcHRpb25zIG9mIG5vcm1hbGl0eSAoYW5kIGVxdWFsIHZhcmlhbmNlcykKb24gc3VjaCBzbWFsbCBkYXRhc2V0cyBjYW4gYmUgdHJpY2t5LiBGb3IgaW5zdGFuY2UsIHdoZW4Kc2FtcGxpbmcgNyBkYXRhcG9pbnRzIGZyb20gZGF0YSB0aGF0IGhhcyBhbiB1bmRlcmx5aW5nIGRpc3RyaWJ1dGlvbiAKdGhhdCBpcyBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIHRoZSBzbWFsbCBzYW1wbGUgbWF5IGJlIG5vcm1hbGx5CmRpc3RyaWJ1dGVkIGJ5IGNoYW5jZS4gRXF1aXZhbGVudGx5LCB3aGVuIHNhbXBsaW5nIDcgZGF0YXBvaW50cwpmcm9tIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgdGhlIHNtYWxsIHNhbXBsZSBtYXkgYXBwZWFyIG5vdCBub3JtYWxseQpkaXN0cmlidXRlZC4gV2UgY2FuIHNob3cgdGhpcyBhcyBmb2xsb3dzLgoKU2ltdWxhdGUgZnJvbSB1bmlmb3JtIGRpc3RyaWJ1dGlvbjoKCmBgYHtyfQpzZXQuc2VlZCg1KQpwYXIobWZyb3cgPSBjKDMsMiksbWFyPWMoMiwgNCwgMiwgMC4yKSkKZm9yIChpIGluIDE6NikgewogIHggPC0gcnVuaWYoNykgIyMgc2FtcGxlIDcgb2JzZXJ2YXRpb25zIGZyb20gYSB1bmlmb3JtIGRpc3RyaWJ1dGlvbgogIHFxUGxvdCh4LHlsYWIgPSAiIikKfQpgYGAKCkV2ZW4gdGhvdWdoIHRoZSBkYXRhIG9yaWdpbmF0ZXMgZnJvbSBhIHVuaWZvcm0gZGlzdHJpYnV0aW9uLCBzZXZlcmFsCnNhbXBsZXMgbWVldCB0aGUgbm9ybWFsaXR5IHJlcXVpcmVtZW50cyBieSBjaGFuY2UuCgpTYW1wbGUgZnJvbSBub3JtYWwgZGlzdHJpYnV0aW9uOgoKYGBge3J9CnNldC5zZWVkKDUpCnBhcihtZnJvdyA9IGMoMywyKSxtYXI9YygyLCA0LCAyLCAwLjIpKQpmb3IgKGkgaW4gMTo2KSB7CiAgeCA8LSBybm9ybSg3KSAjIyBzYW1wbGUgNyBzb2JzZXJ2YXRpb25zIGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uCiAgcXFQbG90KHgseWxhYiA9ICIiKQp9CmBgYAoKRXZlbiB0aG91Z2ggdGhlIGRhdGEgb3JpZ2luYXRlcyBmcm9tIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgc2V2ZXJhbApzYW1wbGVzIGRvIG5vdCBtZWV0IHRoZSBub3JtYWxpdHkgcmVxdWlyZW1lbnRzIGJ5IGNoYW5jZS4KCkFzIGFuIGFsdGVybmF0aXZlLCB3ZSBtYXkgZ2VuZXJhdGUgYSBxcS1wbG90IG9mIHRoZSByZXNpZHVhbHMKb2YgYSBsaW5lYXIgbW9kZWwuIFRoZXNlIHJlc2lkdWFscyBzaG91bGQgYmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQKaWYgdGhlIGRhdGEgZm9yIGVhY2ggdHJlYXRtZW50IGNvbmRpdGlvbiBpcyBub3JtYWxseSBkaXN0cmlidXRlZC4KCmBgYHtyfQpmaXQgPC0gbG0oZnJlc2h3ZWlnaHR+dHJlYXRtZW50LCBsZXR0dWNlKQpwbG90KGZpdCwgd2hpY2ggPSAyLGNvbCA9IGZpdCRtb2RlbCR0cmVhdG1lbnQpCmxlZ2VuZCgnYm90dG9tcmlnaHQnLCBsZXZlbHMoZml0JG1vZGVsJHRyZWF0bWVudCksIHRleHQuY29sID0gMTo0KQpgYGAKClRoZSBkYXRhIHNlZW1zIHRvIGJlIGFwcHJveGltYXRlbHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsCndpdGggYSBzbGlnaHQgbGVmdCBza2V3LgoKIyMjIEFzc3VtcHRpb24gb2YgZXF1YWwgdmFyaWFuY2VzCgpXZSBjYW4gY2hlY2sgdGhlIGFzc3VtcHRpb24gb2YgZXF1YWwgdmFyaWFuY2Ugd2l0aCBhIGJveHBsb3Q6CgpgYGB7cn0KbGV0dHVjZSAlPiUKICBnZ3Bsb3QoYWVzKHg9dHJlYXRtZW50LHk9ZnJlc2h3ZWlnaHQsZmlsbD10cmVhdG1lbnQpKSArIAogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IlJkR3kiKSArCiAgdGhlbWVfYncoKSArCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGU9TkEpICsgCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjIpICsKICBnZ3RpdGxlKCJCb3hwbG90IG9mIHRoZSBmcmVzaHdlaWd0aCBmb3IgZWFjaCB0cmVhdG1lbnQgY29uZGl0aW9uIikgKwogIHlsYWIoImZyZXNod2VpZ2h0IChncmFtKSIpICsgCiAgc3RhdF9zdW1tYXJ5KGZ1bi55PW1lYW4sIGdlb209InBvaW50Iiwgc2hhcGU9NSwgc2l6ZT0zLCBjb2xvcj0iYmxhY2siLCBmaWxsPSJibGFjayIpCmBgYAoKQXMgYSBtZWFzdXJlIG9mIHZhcmlhYmlsaXR5LCB3ZSBtYXkgdGFrZSB0aGUgaGVpZ2h0Cm9mIGVhY2ggYm94cGxvdCdzIGJveC4gVGhpcyBpcyB0aGUgaW50ZXJ2YWwgYmV0d2Vlbgp0aGUgMjUlIGFuZCA3NSUgcXVhbnRpbGUuIEhlcmUgd2UgY2FuIHNlZSB0aGF0IHRoaXMKaW50ZXJ2YWwsIGFzIHdlbGwgYXMgdGhlIGxlbmd0aCBvZiB0aGUgd2hpc2tlcnMsIGlzCmFwcHJveGltYXRlbHkgZXF1YWwgZm9yIG1vc3QgZ3JvdXBzLiBIb3dldmVyLCB0aGUKdmFyaWFiaWxpdHkgb2YgY29iYyBkb2VzIHNlZW0gdG8gYmUgcXVpdGUgYSBiaXQgbGFyZ2VyIAp0aGFuIHRoZSB2YXJpYWJpbGl0eSBpbiB0aGUgcmVmb2FrIGdyb3VwLiAKCldpdGggdGhpcyBsaXR0bGUgb2JzZXJ2YXRpb25zIHBlciBncm91cCwgaXQgaXMgdmVyeQpkaWZmaWN1bHQgdG8gcmVsaWFibHkgYXNzZXNzIHRoZSBhc3N1bXB0aW9ucyBvZiBub3JtYWxpdHkgYW5kIGVxdWFsIHZhcmlhbmNlcy4gCkluIHRoaXMgdHV0b3JpYWwsIHdlIHdpbGwgYXNzdW1lIHRoYXQgYWxsIGFzc3VtcHRpb25zIGFyZSBtZXQuIAoqKkluIGEgbGF0ZXIgdHV0b3JpYWwsICJLcnVza2FsX2xldHR1Y2VfcGxhbnRzLlJtZCIsKioKKip3ZSB3aWxsIHNlZSB3b3VsZCBoYXBwZW4gaWYgd2UgZGVjaWRlIHRvIHJlamVjdCB0aGUgYXNzdW1wdGlvbnMuKioKCiMgRGF0YSBhbmFseXNpcwoKIyMgQU5PVkEKCmBgYHtyfQpmaXQgPC0gbG0oZnJlc2h3ZWlnaHR+dHJlYXRtZW50LCBkYXRhPWxldHR1Y2UpCmZpdF9hbm92YSA8LSBhbm92YShmaXQpCmZpdF9hbm92YQpgYGAKClRoZSBwLXZhbHVlIG9mIHRoZSBBTk9WQSBhbmFseXNpcyBpcyBleHRyZW1lbHkgc2lnbmlmaWNhbnQKKHAtdmFsdWUgPSBgciBmb3JtYXQoZml0X2Fub3ZhJCJQcig+RikiWzFdLGRpZ2l0cz00KWApLCAKc28gd2UgcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGUgbWVhbiAKZnJlc2h3ZWlndGggaXMgZXF1YWwgZm9yIHRoZSBkaWZmZXJlbnQgdHJlYXRtZW50IGdyb3Vwcy4KV2UgY2FuIHNheSB0aGF0IHRoZSBtZWFuIGZyZXNod2VpZ3RoIGlzIHNpZ25pZmljYW50bHkgZGlmZmVyZW50CmJldHdlZW4gYXQgbGVhc3QgdHdvIHRyZWF0bWVudCBncm91cHMgb24gdGhlIDUlIHNpZ25pZmljYW5jZSBsZXZlbC4KCkJhc2VkIG9uIHRoaXMgYW5hbHlzaXMsIHdlIGRvIG5vdCB5ZXQga25vdyBiZXR3ZWVuIHdoaWNoIHBhcnRpY3VsYXIKZ3JvdXBzIHRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZS4gVG8gYXNzZXNzIHRoaXMsIHdlIHdpbGwKcGVyZnJvbSB0aGUgVHVja2V5IHBvc3QtaG9jIGFuYWx5c2lzLgoKIyMgUG9zdC1ob2MgYW5hbHlzaXMKCldlIHdpbGwgcGVyZm9ybSBhIHBvc3QtaG9jIGFuYWx5c2lzIHRvIGxvb2sgYXQgdGhlIGRpZmZlcmVuY2UgCmluIGZyZXN3ZWlndGggYmV0d2VlbiBlYWNoIHBhaXJ3aXNlIGNvbXBhcmlzb24gb2YgdHJlYXRtZW50IGdyb3Vwcy4KSW1wb3J0YW50bHksIHdpdGggdGhpcyBzdHJhdGVneSwgdGhlIHAtdmFsdWVzIHdpbGwgYmUgYXV0b21hdGljYWxseQphZGp1c3RlZCBmb3IgbXVsdGlwbGUgdGVzdGluZy4KClRoZSBudWxsIGh5cG90aGVzaXMgZm9yIGVhY2ggcGFpcndpc2UgdGVzdCBpcyB0aGF0IAokSDAkIHRoZXJlIGlzIG5vIGRpZmZlcmVuY2UgaW4gdGhlIGF2ZXJhZ2UgZnJlc2h3ZWlnaHQgdmFsdWVzIGJldHdlZW4gCmJvdGggZ3JvdXBzLgoKVGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMgZm9yIGVhY2ggcGFpcndpc2UgdGVzdCBzdGF0ZXMgdGhhdCAKJEhBJCB0aGVyZSBpcyBpbmRlZWQgYSBkaWZmZXJlbmNlIGluIHRoZSBhdmVyYWdlIGZyZXNod2VpZ2h0IHZhbHVlcyAKYmV0d2VlbiBib3RoIGdyb3Vwcy4KCmBgYHtyLG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkobXVsdGNvbXAsIHF1aWV0bHkgPSBUUlVFKQptY3AgPC0gZ2xodChmaXQsIGxpbmZjdCA9IG1jcCh0cmVhdG1lbnQgPSAiVHVrZXkiKSkKbWNwX3N1bW1hcnkgPC0gc3VtbWFyeShtY3ApCm1jcF9zdW1tYXJ5CgojV2Ugd2lsbCBhbHNvIGNhbGN1bGF0ZSB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbCBvbiB0aGUgbWVhbiBkaWZmZXJlbmNlcy4KbWNwX2NvbmZpbnQgPC0gY29uZmludChtY3ApCm1jcF9jb25maW50CmBgYAoKIyBDb25jbHVzaW9uCgpXZSBoYXZlIGZvdW5kIGFuIGV4dHJlbWVseSBzaWduaWZpY2FudCBkZXBlbmRlbmNlIChwLXZhbHVlID0gYHIgcm91bmQoZml0X2Fub3ZhJCJQcig+RikiWzFdLGRpZ2l0cz0xMilgKSwgCmJldHdlZW4gdGhlIG1lYW4gZnJlc2h3ZWlndGggYW5kIHRoZSB0cmVhdG1lbnQgY29uZGl0aW9uCm9uIHRoZSBnbG9iYWwgNSUgc2lnbmlmaWNhbmNlIGxldmVsLgoKVGhlIG1lYW4gZnJlc3dlaWdodCBvZiBwbGFudHMgZ3Jvd24gd2l0aCBjb21wb3N0IGlzIGV4dHJlbWVseQpzaWduaWZpY2FudGx5IGhpZ2hlciBhcyBjb21wYXJlZCB0byBpbiBjb250cm9sIHBsYW50cyAoVHVja2V5IHRlc3QsCmFkanVzdGVkIHAtdmFsdWUgPCAwLjAwMDEsIDIwLjlnIGhpZ2hlciwgOTUlIENJOiBbMTMuMjsgMjguNV0pCmFuZCBhcyBjb21wYXJlZCB0byByZWZvYWsgcGxhbnRzIChUdWNrZXkgdGVzdCwgYWRqdXN0ZWQgCnAtdmFsdWUgPCAwLjAwMDEsIDIyLjdnIGhpZ2hlciwgOTUlIENJOiBbMTUuMTsgMzAuM10pLgoKVGhlIG1lYW4gZnJlc3dlaWdodCBvZiBwbGFudHMgZ3Jvd24gd2l0aCBjb2JjIGlzIGV4dHJlbWVseQpzaWduaWZpY2FudGx5IGhpZ2hlciBhcyBjb21wYXJlZCB0byBpbiBjb250cm9sIHBsYW50cyAoVHVja2V5IHRlc3QsCmFkanVzdGVkIHAtdmFsdWUgPCAwLjAwMDEsIDE2LjFnIGhpZ2hlciwgOTUlIENJOiBbOC41OyAyMy44XSkKYW5kIGFzIGNvbXBhcmVkIHRvIHJlZm9hayBwbGFudHMgKFR1Y2tleSB0ZXN0LCBhZGp1c3RlZCAKcC12YWx1ZSA8IDAuMDAwMSwgMTguMGcgaGlnaGVyLCA5NSUgQ0k6IFsxMC40OyAyNS42XSkuCgpXZSBkbyBub3QgZmluZCBlbm91Z2ggZXZpZGVuY2UgdG8gY2xhaW0gYSBkaWZmZXJlbmNlIGluIG1lYW4KZnJlc2h3ZWlnaHQgYmV0d2VlbiByZWZvYWsgYW5kIGNvbnRyb2wgcGxhbnRzIG9yIGJldHdlZW4gY29iYwphbmQgY29tcG9zdCBwbGFudHMuCgpXZSBtYXkgY29uY2x1ZGUgdGhhdCBzdXBwbGVtZW50aW5nIHNvaWwgd2l0aCBjb21wb3N0IG9yIHdpdGgKYm90aCBjb21wb3N0IGFuZCBiaW9jaGFyIHdpbGwgaGF2ZSBhIHBvc2l0aXZlIGVmZmVjdCBvbiB0aGUgCmZyZXNod2VpZ3RoIG9mIGxldHR1Y2UgcGxhbnRzLgoKKipRdWVzdGlvbjogY2FuIHdlIGV4dHJhcG9sYXRlIHRoZXNlIHJlc3VsdHMgdG8gYW5vdGhlciB2ZWdldGFibGUqKgoqKmZvciBpbnN0YW5jZSwgc3BpbmFjaGU/KioKCgoKCgoKCgoKCgoKCgoKCgo=