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.
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;
- Soil only (control)
- Soil supplemented with biochar (refoak)
- Soil supplemented with compost (compost)
- 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)
library(car)
Data import
lettuce <- read_csv("https://raw.githubusercontent.com/statOmics/PSLS21/data/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, 15, 16, 17,…
## $ treatment <chr> "control", "control", "control", "control", "control", "co…
## $ freshweight <dbl> 38, 34, 41, 43, 43, 29, 38, 59, 64, 57, 56, 50, 64, 62, 38…
First, we will set the treatment
variable to a factor
## treatment to factor
lettuce <- lettuce %>%
mutate(treatment = as.factor(treatment))
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:
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=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 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?
ANOVA
To study if the observed difference between the average freshweight values of the different treatment groups are significant, we may perform an ANOVA.
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:
- The observations are independent of each other (in all groups)
- The data (freshweigth) must be normally distributed (in all groups)
- The variability within all groups is similar
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.
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.
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 data seems to be approximately normally distributed, with a slight left skew.
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=mean, geom="point", shape=5, size=3, color="black", fill="black")
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.
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.
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
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 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.
LS0tCnRpdGxlOiAiRXhlcmNpc2UgNy4xOiBBTk9WQSBvbiB0aGUgbGV0dHVjZSBkYXRhc2V0IC0gc29sdXRpb24iICAgCmF1dGhvcjogIkxpZXZlbiBDbGVtZW50IGFuZCBKZXJvZW4gR2lsaXMiCmRhdGU6ICJzdGF0T21pY3MsIEdoZW50IFVuaXZlcnNpdHkgKGh0dHBzOi8vc3RhdG9taWNzLmdpdGh1Yi5pbykiICAKb3V0cHV0OgogICAgaHRtbF9kb2N1bWVudDoKICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZSAgICAKICAgICAgdGhlbWU6IGNvc21vCiAgICAgIHRvYzogdHJ1ZQogICAgICB0b2NfZmxvYXQ6IHRydWUKICAgICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKLS0tCgojIEJhY2tncm91bmQKCkluIGFncmljdWx0dXJlLCBpdCBpcyBpbXBvcnRhbnQgdG8gaGF2ZSBhIGhpZ2ggeWllbGQgb2YgY3JvcHMuIEZvciBsZXR0dWNlIApwbGFudHMsIHBsYW50cyB3aXRoIG1hbnkgbGVhdmVzIGFyZSBrbm93biB0byBiZSBwcmVmZXJyZWQgYnkgdGhlIGNvbnN1bWVycy4KCk9uZSB3YXkgdG8gaW5jcmVhc2UgdGhlIG51bWJlciBvZiBsZWF2ZXMgKG9yIGJldHRlciwgdG90YWwKbGVhZiB3ZWlnaHQpIGlzIGJ5IHVzaW5nIGEgZmVydGlsaXplci4gUmVjZW50bHksIHRoZXJlIGhhcyBiZWVuIAphIHRlbmRlbmN5IHRvIHJlbHkgbW9yZSBvbiBuYXR1cmFsIGZlcnRpbGl6ZXJzLCBzdWNoIGFzIGNvbXBvc3QuCk5lYXIgR2hlbnQsIHRoZSBpbnN0aXR1dGUgb2YgcmVzZWFyY2ggZm9yIGFncmljdWx0dXJlIGFuZCBmaXNoZXJ5IGlzIHRlc3RpbmcgCm5ldywgbmF0dXJhbCBmZXJ0aWxpemF0aW9uIG1ldGhvZHMuIE9uZSBvZiB0aGVzZSBuZXcgZmVydGlsaXplcnMgaXMgY2FsbGVkIApiaW9jaGFyLiBCaW9jaGFyIGlzIGEgcmVzaWR1YWwgcHJvZHVjdCBmcm9tIHB5cm9seXNpcywgYSBwcm9jZXNzIGluIHdoaWNoIApiaW9tYXNzIGlzIGJ1cm5lZCB1bmRlciBzcGVjaWZpYyBjb25kaXRpb25zIChzdWNoIGFzIGhpZ2ggcHJlc3N1cmUpIGluIG9yZGVyIAp0byBwcm9kdWNlIGVuZXJneS4gQmlvY2hhciBpcyBzaW1pbGFyIHRvIGNoYXJjb2FsLCBidXQgaGFzIHNvbWUgdmVyeSB1c2VmdWwgCnByb3BlcnRpZXMsIHN1Y2ggYXMgcmV0YWluaW5nIHdhdGVyIGluIHRoZSBzb2lsLiBJdCBhbHNvIGhhcyBhIHBvc2l0aXZlIAppbmZsdWVuY2Ugb24gdGhlIHNvaWwgbWljcm9iaW9tZS4KCiMgVGhlIGxldHR1Y2UgZGF0YXNldAoKVGhlIHJlc2VhcmNoZXJzIGh5cG90aGVzaXplIHRoYXQgYmlvY2hhciwgY29tcG9zdCBhbmQKYSBjb21iaW5hdGlvbiBvZiBib3RoIGJpb2NoYXIgYW5kIGNvbXBvc3QgY2FuIGhhdmUgYSBkaWZmZXJlbnQgaW5mbHVlbmNlCm9uIHRoZSBncm93dGggb2YgbGV0dHVjZSBwbGFudHMuIFRvIHRoaXMgZW5kLCB0aGV5IGdyZXcgdXAKbGV0dHVjZSBwbGFudHMgaW4gYSBncmVlbmhvdXNlLiBUaGUgcG90cyB3ZXJlIGZpbGxlZCB3aXRoCm9uZSBvZiBmb3VyIHNvaWwgdHlwZXM7CgoxLiBTb2lsIG9ubHkgKGNvbnRyb2wpCjIuIFNvaWwgc3VwcGxlbWVudGVkIHdpdGggYmlvY2hhciAocmVmb2FrKQozLiBTb2lsIHN1cHBsZW1lbnRlZCB3aXRoIGNvbXBvc3QgKGNvbXBvc3QpCjQuIFNvaWwgc3VwcGxlbWVudGVkIHdpdGggYm90aCBiaW9jaGFyIGFuZCBjb21wb3N0IChjb2JjKQoKVGhlIGRhdGFzZXQgYGZyZXNod2VpZ2h0X2xldHR1Y2UudHh0YCBjb250YWlucyB0aGUgZnJlc2h3ZWlnaHQKKGluIGdyYW1zKSBmb3IgMjggbGV0dHVjZSBwbGFudHMgKDcgcGVyIGNvbmRpdGlvbikuIFRoZSByZXNlYXJjaGVycwp3YW50IHRvIHVzZSBhbiBBTk9WQSB0ZXN0IHRvIGFzc2VzcyBpZiB0aGUgdHJlYXRtZW50cyBoYXZlIGEgZGlmZmVyZW50IGVmZmVjdCAKb24gdGhlIGdyb3d0aCBvZiBsZXR0dWNlIHBsYW50cy4gSWYgc28sIHRoZXkgd2lsbCB1c2UgYSBwb3N0LWhvYyB0ZXN0IAooVHVja2V5IHRlc3QpIHRvIGRpc2NvdmVyIHdoaWNoIHNwZWNpZmljIHRyZWF0bWVudHMgaGF2ZSBhbiBlZmZlY3QuCgpMb2FkIHRoZSByZXF1aXJlZCBsaWJyYXJpZXMKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGNhcikKYGBgCgojIERhdGEgaW1wb3J0CgpgYGB7cixtZXNzYWdlPUZBTFNFfQpsZXR0dWNlIDwtIHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc3RhdE9taWNzL1BTTFMyMS9kYXRhL2ZyZXNod2VpZ2h0X2xldHR1Y2UudHh0IikKI1Rha2UgYSBnbGltcHNlIGF0IHRoZSBkYXRhCmdsaW1wc2UobGV0dHVjZSkKYGBgCgpGaXJzdCwgd2Ugd2lsbCBzZXQgdGhlIGB0cmVhdG1lbnRgIHZhcmlhYmxlIHRvIGEgZmFjdG9yCgpgYGB7cn0KIyMgdHJlYXRtZW50IHRvIGZhY3RvcgpsZXR0dWNlIDwtIGxldHR1Y2UgJT4lCiAgbXV0YXRlKHRyZWF0bWVudCA9IGFzLmZhY3Rvcih0cmVhdG1lbnQpKQpgYGAKCiMgRGF0YSBleHBsb3JhdGlvbgoKYGBge3J9CiMjIENvdW50IHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIHBlciB0cmVhdG1lbnQKbGV0dHVjZSAlPiUKICBjb3VudCh0cmVhdG1lbnQpCmBgYAoKTm93IGxldCdzIG1ha2UgYSBib3hwbG90IGRpc3BsYXlpbmcgdGhlIGZyZXNod2VpZ2h0IG9mIGVhY2ggdHJlYXRtZW50IGNvbmRpdGlvbjoKCmBgYHtyfQpsZXR0dWNlICU+JQogIGdncGxvdChhZXMoeD10cmVhdG1lbnQseT1mcmVzaHdlaWdodCxmaWxsPXRyZWF0bWVudCkpICsgCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJSZEd5IikgKwogICAgdGhlbWVfYncoKSArCiAgICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZT1OQSkgKyAKICAgIGdlb21faml0dGVyKHdpZHRoID0gMC4yKSArCiAgICBnZ3RpdGxlKCJCb3hwbG90IG9mIHRoZSBmcmVzaHdlaWd0aCBmb3IgZWFjaCB0cmVhdG1lbnQgY29uZGl0aW9uIikgKwogICAgeWxhYigiZnJlc2h3ZWlnaHQgKGdyYW0pIikgKyAKICAgIHN0YXRfc3VtbWFyeShmdW49bWVhbiwgZ2VvbT0icG9pbnQiLCBzaGFwZT01LCBzaXplPTMsIGNvbG9yPSJibGFjayIsIGZpbGw9ImJsYWNrIikKYGBgCgpOb3RlIHRoYXQgdGhlcmUgYXJlIG5vIGNsZWFyIG91dGxpZXJzIGluIHRoZSBkYXRhLgpXZSBjYW4gc2VlIHRoYXQgdGhlIG1lYW4gZnJlc2h3ZWlnaHQgaXMgdmVyeSBjb21wYXJhYmxlCmJldHdlZW4gdGhlIGNvbnRyb2wgYW5kIHJlZm9hayB0cmVhdG1lbnRzIGFuZCBiZXR3ZWVuIHRoZQpjb21wb3N0IGFuZCBjb2JjIHRyZWF0bWVudHMuIFdlIGNhbiBhbHNvIHNlZSB0aGF0IHRoZSBtZWFuCmZyZXNod2VpZ2h0IGlzIG11Y2ggaGlnaGVyIGluIHRoZSBjb2JjIGFuZCBjb250cm9sIHRyZWF0bWVudHMKdGhhbiBpbiB0aGUgY29udHJvbCBhbmQgcmVmb2FrIHRyZWF0bWVudHMuIEJ1dCBpcyB0aGlzCm9ic2VydmVkIGRpZmZlcmVuY2Ugc2lnbmlmaWNhbnQ/CgojIEFOT1ZBCgpUbyBzdHVkeSBpZiB0aGUgb2JzZXJ2ZWQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZQphdmVyYWdlIGZyZXNod2VpZ2h0IHZhbHVlcyBvZiB0aGUgZGlmZmVyZW50IHRyZWF0bWVudCBncm91cHMKYXJlIHNpZ25pZmljYW50LCB3ZSBtYXkgcGVyZm9ybSBhbiBBTk9WQS4KCiMjIEZvcm11bGF0ZSBudWxsIGFuZCBhbHRlcm5hdGl2ZSBoeXBvdGhlc2VzCgpUaGUgbnVsbCBoeXBvdGhlc2lzIG9mIEFOT1ZBIHN0YXRlcyB0aGF0OgokSDAkOiBUaGUgbWVhbiBmcmVzaHdlaWd0aCBpcyBlcXVhbCBiZXR3ZWVuIHRoZSBkaWZmZXJlbnQgdHJlYXRtZW50IGdyb3Vwcy4KClRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIG9mIEFOT1ZBIHN0YXRlcyB0aGF0OgokSEEkOiBUaGUgbWVhbiBmcmVzaHdlaWd0aCBmb3IgYXQgbGVhc3Qgb25lIHRyZWF0bWVudCBncm91cCBpcyBkaWZmZXJlbnQgCmZyb20gdGhlIG1lYW4gZnJlc2h3ZWlnaHQgaW4gYXQgbGVhc3Qgb25lIG90aGVyIHRyZWF0bWVudCBncm91cC4KCiMjIENoZWNraW5nIHRoZSBhc3N1bXB0aW9ucyBmb3IgQU5PVkEKCkJlZm9yZSB3ZSBtYXkgcHJvY2VlZCB3aXRoIHRoZSBhbmFseXNpcywgd2UgbXVzdCBtYWtlIHN1cmUgdGhhdCBhbGwKYXNzdW1wdGlvbnMgZm9yIEFOT1ZBIGFyZSBtZXQuIEFOT1ZBIGhhcyB0aHJlZSBhc3N1bXB0aW9uczoKCjEuIFRoZSBvYnNlcnZhdGlvbnMgYXJlIGluZGVwZW5kZW50IG9mIGVhY2ggb3RoZXIgKGluIGFsbCBncm91cHMpCjIuIFRoZSBkYXRhIChmcmVzaHdlaWd0aCkgbXVzdCBiZSBub3JtYWxseSBkaXN0cmlidXRlZCAoaW4gYWxsIGdyb3VwcykKMy4gVGhlIHZhcmlhYmlsaXR5IHdpdGhpbiBhbGwgZ3JvdXBzIGlzIHNpbWlsYXIKCiMjIyBBc3N1bXB0aW9uIG9mIGluZGVwZW5kZW5jZQoKVGhlIGZpcnN0IGFzc3VtcHRpb24gaXMgbWV0OyB3ZSBzdGFydGVkIG9mIHdpdGggMjggbGV0dHVjZSBwbGFudHMgYW5kCndlIHJhbmRvbWx5IHN1Ym1pdHRlZCB0aGVtIHRvIG9uZSBvZiBmb3VyIHRyZWF0bWVudCBjb25kaXRpb25zLiBUaGVyZQppcyBubyByZWFzb24gdG8gYmVsaWV2ZSB0aGF0IHRoZSBwbGFudHMgZGlzcGxheSBzeXN0ZW1hdGljIGRpZmZlcmVuY2VzCmJldHdlZW4gdHJlYXRtZW50IGdyb3Vwcywgb3RoZXIgdGhhbiB0aGUgYWN0dWFsIHRyZWF0bWVudC4KCiMjIyBBc3N1bXB0aW9uIG9mIG5vcm1hbGl0eQoKRm9yIHRoZSBzZWNvbmQgYXNzdW1wdGlvbiwgd2UgbXVzdCBjaGVjayBub3JtYWxpdHkgaW4gZWFjaCBncm91cC4KCmBgYHtyfQojIyBnZXQgcXFwbG90cyBmb3IgZWFjaCBpbmRpdmlkdWFsIHRyZWF0bWVudCBncm91cApwYXIobWZyb3cgPSBjKDIsMikpCmZvcihpIGluIGxldmVscyhsZXR0dWNlJHRyZWF0bWVudCkpewogIHFxUGxvdChzdWJzZXQobGV0dHVjZSx0cmVhdG1lbnQgPT0gaSkkZnJlc2h3ZWlnaHQsIG1haW4gPSBpLCB5bGFiID0gIiIpCn0KYGBgCgpBY2NvcmRpbmcgdG8gdGhlc2UgcGxvdHMsIHRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbiBpcyBtZXQKZm9yIGVhY2ggZ3JvdXAuIEhvd2V2ZXIsIHdlIG5lZWQgdG8gYmUgY2FyZWZ1bCB3aXRoIHJlbHlpbmcgb24gCnRoZXNlIHBsb3RzLCBhcyB0aGV5IGFyZSBvbmx5IGJhc2VkIG9uIDcgb2JzZXJ2YXRpb25zIHBlciBncm91cC4KSW5kZWVkLCBjaGVja2luZyB0aGUgYXNzdW1wdGlvbnMgb2Ygbm9ybWFsaXR5IChhbmQgZXF1YWwgdmFyaWFuY2VzKQpvbiBzdWNoIHNtYWxsIGRhdGFzZXRzIGNhbiBiZSB0cmlja3kuIEZvciBpbnN0YW5jZSwgd2hlbgpzYW1wbGluZyA3IGRhdGFwb2ludHMgZnJvbSBkYXRhIHRoYXQgaGFzIGFuIHVuZGVybHlpbmcgZGlzdHJpYnV0aW9uIAp0aGF0IGlzIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZCwgdGhlIHNtYWxsIHNhbXBsZSBtYXkgYmUgbm9ybWFsbHkKZGlzdHJpYnV0ZWQgYnkgY2hhbmNlLiBFcXVpdmFsZW50bHksIHdoZW4gc2FtcGxpbmcgNyBkYXRhcG9pbnRzCmZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uLCB0aGUgc21hbGwgc2FtcGxlIG1heSBhcHBlYXIgbm90IG5vcm1hbGx5CmRpc3RyaWJ1dGVkLiBXZSBjYW4gc2hvdyB0aGlzIGFzIGZvbGxvd3MuCgpTaW11bGF0ZSBmcm9tIHVuaWZvcm0gZGlzdHJpYnV0aW9uOgoKYGBge3J9CnNldC5zZWVkKDUpCnBhcihtZnJvdyA9IGMoMywyKSxtYXI9YygyLCA0LCAyLCAwLjIpKQpmb3IgKGkgaW4gMTo2KSB7CiAgeCA8LSBydW5pZig3KSAjIyBzYW1wbGUgNyBvYnNlcnZhdGlvbnMgZnJvbSBhIHVuaWZvcm0gZGlzdHJpYnV0aW9uCiAgcXFQbG90KHgseWxhYiA9ICIiKQp9CmBgYAoKRXZlbiB0aG91Z2ggdGhlIGRhdGEgb3JpZ2luYXRlcyBmcm9tIGEgdW5pZm9ybSBkaXN0cmlidXRpb24sIHNldmVyYWwKc2FtcGxlcyBtZWV0IHRoZSBub3JtYWxpdHkgcmVxdWlyZW1lbnRzIGJ5IGNoYW5jZS4KClNhbXBsZSBmcm9tIG5vcm1hbCBkaXN0cmlidXRpb246CgpgYGB7cn0Kc2V0LnNlZWQoNSkKcGFyKG1mcm93ID0gYygzLDIpLG1hcj1jKDIsIDQsIDIsIDAuMikpCmZvciAoaSBpbiAxOjYpIHsKICB4IDwtIHJub3JtKDcpICMjIHNhbXBsZSA3IHNvYnNlcnZhdGlvbnMgZnJvbSBhIG5vcm1hbCBkaXN0cmlidXRpb24KICBxcVBsb3QoeCx5bGFiID0gIiIpCn0KYGBgCgpFdmVuIHRob3VnaCB0aGUgZGF0YSBvcmlnaW5hdGVzIGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uLCBzZXZlcmFsCnNhbXBsZXMgZG8gbm90IG1lZXQgdGhlIG5vcm1hbGl0eSByZXF1aXJlbWVudHMgYnkgY2hhbmNlLgoKQWx0ZXJuYXRpdmVseSwgd2UgbWF5IGdlbmVyYXRlIGEgcXEtcGxvdCBvZiB0aGUgcmVzaWR1YWxzCm9mIGEgbGluZWFyIG1vZGVsLiBUaGVzZSByZXNpZHVhbHMgc2hvdWxkIGJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkCmlmIHRoZSBkYXRhIGZvciBlYWNoIHRyZWF0bWVudCBjb25kaXRpb24gaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuCgpgYGB7cn0KZml0IDwtIGxtKGZyZXNod2VpZ2h0fnRyZWF0bWVudCwgbGV0dHVjZSkKcGxvdChmaXQsIHdoaWNoID0gMixjb2wgPSBmaXQkbW9kZWwkdHJlYXRtZW50KQpsZWdlbmQoJ2JvdHRvbXJpZ2h0JywgbGV2ZWxzKGZpdCRtb2RlbCR0cmVhdG1lbnQpLCB0ZXh0LmNvbCA9IDE6NCkKYGBgCgpUaGUgZGF0YSBzZWVtcyB0byBiZSBhcHByb3hpbWF0ZWx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLAp3aXRoIGEgc2xpZ2h0IGxlZnQgc2tldy4KCiMjIyBBc3N1bXB0aW9uIG9mIGVxdWFsIHZhcmlhbmNlcwoKV2UgY2FuIGNoZWNrIHRoZSBhc3N1bXB0aW9uIG9mIGVxdWFsIHZhcmlhbmNlIHdpdGggYSBib3hwbG90OgoKYGBge3J9CmxldHR1Y2UgJT4lCiAgZ2dwbG90KGFlcyh4PXRyZWF0bWVudCx5PWZyZXNod2VpZ2h0LGZpbGw9dHJlYXRtZW50KSkgKyAKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJSZEd5IikgKwogIHRoZW1lX2J3KCkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlPU5BKSArIAogIGdlb21faml0dGVyKHdpZHRoID0gMC4yKSArCiAgZ2d0aXRsZSgiQm94cGxvdCBvZiB0aGUgZnJlc2h3ZWlndGggZm9yIGVhY2ggdHJlYXRtZW50IGNvbmRpdGlvbiIpICsKICB5bGFiKCJmcmVzaHdlaWdodCAoZ3JhbSkiKSArIAogIHN0YXRfc3VtbWFyeShmdW49bWVhbiwgZ2VvbT0icG9pbnQiLCBzaGFwZT01LCBzaXplPTMsIGNvbG9yPSJibGFjayIsIGZpbGw9ImJsYWNrIikKYGBgCgpBcyBhIG1lYXN1cmUgb2YgdmFyaWFiaWxpdHksIHdlIG1heSB0YWtlIHRoZSBoZWlnaHQKb2YgZWFjaCBib3guIFRoaXMgaXMgdGhlIGludGVydmFsIGJldHdlZW4KdGhlIDI1JSBhbmQgNzUlIHF1YW50aWxlLiBIZXJlIHdlIGNhbiBzZWUgdGhhdCB0aGlzCmludGVydmFsLCBhcyB3ZWxsIGFzIHRoZSBsZW5ndGggb2YgdGhlIHdoaXNrZXJzLCBpcwphcHByb3hpbWF0ZWx5IGVxdWFsIGZvciBtb3N0IGdyb3Vwcy4gSG93ZXZlciwgdGhlCnZhcmlhYmlsaXR5IG9mIGNvYmMgZG9lcyBzZWVtIHRvIGJlIHF1aXRlIGEgYml0IGxhcmdlciAKdGhhbiB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIHJlZm9hayBncm91cC4gCgpXaXRoIHRoaXMgbGl0dGxlIG9ic2VydmF0aW9ucyBwZXIgZ3JvdXAsIGl0IGlzIHZlcnkKZGlmZmljdWx0IHRvIHJlbGlhYmx5IGFzc2VzcyB0aGUgYXNzdW1wdGlvbnMgb2Ygbm9ybWFsaXR5IGFuZCBlcXVhbCB2YXJpYW5jZXMuIApJbiB0aGlzIHR1dG9yaWFsLCB3ZSB3aWxsIGFzc3VtZSB0aGF0IGFsbCBhc3N1bXB0aW9ucyBhcmUgbWV0LiAKKipJbiBhIGxhdGVyIHR1dG9yaWFsLCAiS3J1c2thbF9sZXR0dWNlX3BsYW50cy5SbWQiLCoqCioqd2Ugd2lsbCBzZWUgd291bGQgaGFwcGVuIGlmIHdlIGRlY2lkZSB0byByZWplY3QgdGhlc2UgYXNzdW1wdGlvbnMuKioKCiMjIE1vZGVsaW5nCgpgYGB7cn0KZml0IDwtIGxtKGZyZXNod2VpZ2h0fnRyZWF0bWVudCwgZGF0YT1sZXR0dWNlKQpmaXRfYW5vdmEgPC0gYW5vdmEoZml0KQpmaXRfYW5vdmEKYGBgCgpUaGUgcC12YWx1ZSBvZiB0aGUgQU5PVkEgYW5hbHlzaXMgaXMgZXh0cmVtZWx5IHNpZ25pZmljYW50CihwLXZhbHVlID0gYHIgZm9ybWF0KGZpdF9hbm92YSQiUHIoPkYpIlsxXSxkaWdpdHM9NClgKSwgCnNvIHdlIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIHRoYXQgdGhlIG1lYW4gCmZyZXNod2VpZ3RoIGlzIGVxdWFsIGZvciB0aGUgZGlmZmVyZW50IHRyZWF0bWVudCBncm91cHMuCldlIGNhbiBzYXkgdGhhdCB0aGUgbWVhbiBmcmVzaHdlaWd0aCBpcyBzaWduaWZpY2FudGx5IGRpZmZlcmVudApiZXR3ZWVuIGF0IGxlYXN0IHR3byB0cmVhdG1lbnQgZ3JvdXBzIG9uIHRoZSA1JSBzaWduaWZpY2FuY2UgbGV2ZWwuCgpCYXNlZCBvbiB0aGlzIGFuYWx5c2lzLCB3ZSBkbyBub3QgeWV0IGtub3cgYmV0d2VlbiB3aGljaCBwYXJ0aWN1bGFyCmdyb3VwcyB0aGVyZSBpcyBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UuIFRvIGFzc2VzcyB0aGlzLCB3ZSB3aWxsCnBlcmZyb20gdGhlIFR1Y2tleSBwb3N0LWhvYyBhbmFseXNpcy4KCiMjIFBvc3QtaG9jIGFuYWx5c2lzCgpXZSB3aWxsIHBlcmZvcm0gYSBwb3N0LWhvYyBhbmFseXNpcyB0byBsb29rIGF0IHRoZSBkaWZmZXJlbmNlIAppbiBmcmVzd2VpZ3RoIGJldHdlZW4gZWFjaCBwYWlyd2lzZSBjb21wYXJpc29uIG9mIHRyZWF0bWVudCBncm91cHMuCkltcG9ydGFudGx5LCB3aXRoIHRoaXMgc3RyYXRlZ3ksIHRoZSBwLXZhbHVlcyB3aWxsIGJlIGF1dG9tYXRpY2FsbHkKYWRqdXN0ZWQgZm9yIG11bHRpcGxlIHRlc3RpbmcuCgpUaGUgbnVsbCBoeXBvdGhlc2lzIGZvciBlYWNoIHBhaXJ3aXNlIHRlc3QgaXMgdGhhdCAKJEgwJCB0aGVyZSBpcyBubyBkaWZmZXJlbmNlIGluIHRoZSBhdmVyYWdlIGZyZXNod2VpZ2h0IHZhbHVlcyBiZXR3ZWVuIApib3RoIGdyb3Vwcy4KClRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIGZvciBlYWNoIHBhaXJ3aXNlIHRlc3Qgc3RhdGVzIHRoYXQgCiRIQSQgdGhlcmUgaXMgaW5kZWVkIGEgZGlmZmVyZW5jZSBpbiB0aGUgYXZlcmFnZSBmcmVzaHdlaWdodCB2YWx1ZXMgCmJldHdlZW4gYm90aCBncm91cHMuCgpgYGB7cixtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KG11bHRjb21wLCBxdWlldGx5ID0gVFJVRSkKbWNwIDwtIGdsaHQoZml0LCBsaW5mY3QgPSBtY3AodHJlYXRtZW50ID0gIlR1a2V5IikpCm1jcF9zdW1tYXJ5IDwtIHN1bW1hcnkobWNwKQptY3Bfc3VtbWFyeQoKI1dlIHdpbGwgYWxzbyBjYWxjdWxhdGUgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgb24gdGhlIG1lYW4gZGlmZmVyZW5jZXMuCm1jcF9jb25maW50IDwtIGNvbmZpbnQobWNwKQptY3BfY29uZmludApgYGAKCiMjIENvbmNsdXNpb24KCldlIGhhdmUgZm91bmQgYW4gZXh0cmVtZWx5IHNpZ25pZmljYW50IGRlcGVuZGVuY2UgCihwLXZhbHVlID0gYHIgcm91bmQoZml0X2Fub3ZhJCJQcig+RikiWzFdLGRpZ2l0cz0xMilgKSwgCmJldHdlZW4gdGhlIG1lYW4gZnJlc2h3ZWlndGggYW5kIHRoZSB0cmVhdG1lbnQgY29uZGl0aW9uCm9uIHRoZSBnbG9iYWwgNSUgc2lnbmlmaWNhbmNlIGxldmVsLgoKVGhlIG1lYW4gZnJlc2h3ZWlnaHQgb2YgcGxhbnRzIGdyb3duIHdpdGggY29tcG9zdCBpcyBleHRyZW1lbHkKc2lnbmlmaWNhbnRseSBoaWdoZXIgYXMgY29tcGFyZWQgdG8gaW4gY29udHJvbCBwbGFudHMgKFR1Y2tleSB0ZXN0LAphZGp1c3RlZCBwLXZhbHVlIDwgMC4wMDAxLCAyMC45ZyBoaWdoZXIsIDk1JSBDSTogWzEzLjI7IDI4LjVdKQphbmQgYXMgY29tcGFyZWQgdG8gcmVmb2FrIHBsYW50cyAoVHVja2V5IHRlc3QsIGFkanVzdGVkIApwLXZhbHVlIDwgMC4wMDAxLCAyMi43ZyBoaWdoZXIsIDk1JSBDSTogWzE1LjE7IDMwLjNdKS4KClRoZSBtZWFuIGZyZXNod2VpZ2h0IG9mIHBsYW50cyBncm93biB3aXRoIGNvYmMgaXMgZXh0cmVtZWx5CnNpZ25pZmljYW50bHkgaGlnaGVyIGFzIGNvbXBhcmVkIHRvIGluIGNvbnRyb2wgcGxhbnRzIChUdWNrZXkgdGVzdCwKYWRqdXN0ZWQgcC12YWx1ZSA8IDAuMDAwMSwgMTYuMWcgaGlnaGVyLCA5NSUgQ0k6IFs4LjU7IDIzLjhdKQphbmQgYXMgY29tcGFyZWQgdG8gcmVmb2FrIHBsYW50cyAoVHVja2V5IHRlc3QsIGFkanVzdGVkIApwLXZhbHVlIDwgMC4wMDAxLCAxOC4wZyBoaWdoZXIsIDk1JSBDSTogWzEwLjQ7IDI1LjZdKS4KCldlIGRvIG5vdCBmaW5kIGVub3VnaCBldmlkZW5jZSB0byBjbGFpbSBhIGRpZmZlcmVuY2UgaW4gbWVhbgpmcmVzaHdlaWdodCBiZXR3ZWVuIHJlZm9hayBhbmQgY29udHJvbCBwbGFudHMgb3IgYmV0d2VlbiBjb2JjCmFuZCBjb21wb3N0IHBsYW50cy4KCldlIG1heSBjb25jbHVkZSB0aGF0IHN1cHBsZW1lbnRpbmcgc29pbCB3aXRoIGNvbXBvc3Qgb3Igd2l0aApib3RoIGNvbXBvc3QgYW5kIGJpb2NoYXIgd2lsbCBoYXZlIGEgcG9zaXRpdmUgZWZmZWN0IG9uIHRoZSAKZnJlc2h3ZWlndGggb2YgbGV0dHVjZSBwbGFudHMuCgoKCgo=