Example #1: ANCOVA with an experimental design and pretest as covariate
Load in some helpful packages
library(tidyverse)
library(haven)
Load in the dataset
hap_ancova <- read_dta("hap-ancova1.dta")
Explore your data
glimpse(hap_ancova)
Rows: 82
Columns: 4
$ id <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, …
$ hap_pre <dbl> 40, 51, 54, 58, 52, 44, 58, 41, 42, 66, 47, 66, 47, 54,…
$ hap_post <dbl> 49, 56, 51, 52, 68, 53, 47, 49, 47, 58, 36, 59, 49, 54,…
$ treat <dbl+lbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
Clean your data
hap_ancova.clean <- hap_ancova %>%
mutate(.,
treat.fac = as_factor(treat))
Check out descriptive statistics for the treatment factor and continuous variables
The summary package in base R is a quick and easy way to do this!
summary(hap_ancova.clean)
id hap_pre hap_post treat
Min. : 1.00 Min. :28.0 Min. :30.00 Min. :1.0
1st Qu.:21.25 1st Qu.:44.0 1st Qu.:47.00 1st Qu.:1.0
Median :41.50 Median :51.5 Median :51.00 Median :1.5
Mean :41.50 Mean :51.3 Mean :53.46 Mean :1.5
3rd Qu.:61.75 3rd Qu.:58.0 3rd Qu.:59.00 3rd Qu.:2.0
Max. :82.00 Max. :81.0 Max. :77.00 Max. :2.0
treat.fac
Control group :41
Optimism therapy group:41
Correlate pre and post test results- how similar are they?
Hopefully, somewhat similar! If not, there may not be systematic change over time. The cor.test function in base R works well for simple correlations.
cor.test(hap_ancova.clean$hap_pre, hap_ancova.clean$hap_post)
Pearson's product-moment correlation
data: hap_ancova.clean$hap_pre and hap_ancova.clean$hap_post
t = 6.3349, df = 80, p-value = 1.302e-08
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.4127435 0.7063892
sample estimates:
cor
0.5779818
Run a basic ANCOVA
Here, we have hap_post as the outcome, treat as the factor, and hap_pre as the covariate. Since we cleaned our data at the beginning, R recognizes that treat is a factor and hap_pre is continuous so it will run as an ANCOVA and not a two-way ANOVA.
ancova1<-aov(hap_post~ treat.fac + hap_pre,data=hap_ancova.clean)
summary(ancova1)
Df Sum Sq Mean Sq F value Pr(>F)
treat.fac 1 226 225.6 3.653 0.0596 .
hap_pre 1 2700 2700.4 43.730 4.07e-09 ***
Residuals 79 4878 61.8
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
If you had more than 2 groups, you could do post-hoc comparisons:
TukeyHSD(ancova1)
non-factors ignored: hap_pre'which' specified some non-factors which will be dropped
Tukey multiple comparisons of means
95% family-wise confidence level
Fit: aov(formula = hap_post ~ treat.fac + hap_pre, data = hap_ancova.clean)
$treat.fac
diff lwr upr
Optimism therapy group-Control group 3.317073 -0.1375479 6.771694
p adj
Optimism therapy group-Control group 0.0596061
Get measures of effect size:
library(effectsize)
eta_squared(ancova1)
treat.fac hap_pre
0.02890181 0.34601162
omega_squared(ancova1)
Parameter | Omega2 (partial) | 90% CI
-------------------------------------------
treat.fac | 0.03 | [0.00, 0.12]
hap_pre | 0.34 | [0.21, 0.46]
What if we didn’t have the pretest score?
Now, see the difference without the covariate- just an ANOVA for treat:
anova1<-aov(hap_post~ treat.fac, data=hap_ancova.clean)
summary(anova1)
Df Sum Sq Mean Sq F value Pr(>F)
treat.fac 1 226 225.56 2.381 0.127
Residuals 80 7579 94.74
And, of course- how to run this as a multiple regression!
regression1 <- lm(hap_post ~ treat.fac + hap_pre, data = hap_ancova.clean)
summary(regression1)
Call:
lm(formula = hap_post ~ treat.fac + hap_pre, data = hap_ancova.clean)
Residuals:
Min 1Q Median 3Q Max
-20.7017 -3.7916 -0.0503 4.2741 18.3137
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 20.5479 4.8834 4.208 6.76e-05 ***
treat.facOptimism therapy group 3.9496 1.7382 2.272 0.0258 *
hap_pre 0.6031 0.0912 6.613 4.07e-09 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 7.858 on 79 degrees of freedom
Multiple R-squared: 0.3749, Adjusted R-squared: 0.3591
F-statistic: 23.69 on 2 and 79 DF, p-value: 8.702e-09
Example #2: ANCOVA with experimental design and related covariates (not pretest)
It might be better for external validity to use related measures as covariates, instead of a pretest. ## Load in the dataset:
hap_ancova2 <- read_dta("hap-ancova2.dta")
Explore your data:
glimpse(hap_ancova2)
Rows: 96
Columns: 6
$ id <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,…
$ hap <dbl> 34, 44, 58, 58, 50, 51, 48, 64, 36, 54, 42, 55, 41, 40…
$ treat <dbl+lbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
$ satfam <dbl+lbl> 5, 6, 6, 5, 3, 4, 5, 5, 6, 4, 3, 5, 5, 2, 4, 3, 4,…
$ health <dbl+lbl> 2, 5, 5, 4, 5, 3, 5, 4, 4, 3, 3, 3, 3, 4, 3, 3, 4,…
$ socfriend <dbl+lbl> 6, 4, 3, 2, 5, 5, 5, 7, 5, 4, 4, 4, 5, 6, 4, 5, 5,…
Clean your data:
hap_ancova2.clean <- hap_ancova2 %>%
mutate(.,
treat.fac = as_factor(treat))
Explore your data
summary(hap_ancova2.clean)
id hap treat satfam
Min. : 1.00 Min. :28.00 Min. :1.0 Min. :1.000
1st Qu.:24.75 1st Qu.:42.00 1st Qu.:1.0 1st Qu.:3.000
Median :48.50 Median :50.50 Median :1.5 Median :4.000
Mean :48.50 Mean :50.11 Mean :1.5 Mean :3.854
3rd Qu.:72.25 3rd Qu.:58.00 3rd Qu.:2.0 3rd Qu.:5.000
Max. :96.00 Max. :81.00 Max. :2.0 Max. :7.000
health socfriend treat.fac
Min. :2.000 Min. :2.000 Control group :48
1st Qu.:3.000 1st Qu.:4.000 Optimism therapy group:48
Median :4.000 Median :5.000
Mean :3.708 Mean :4.594
3rd Qu.:4.000 3rd Qu.:5.000
Max. :7.000 Max. :7.000
How much variance does our set of covariates explain?
regression2 <- lm(hap ~ satfam + health + socfriend, data = hap_ancova2.clean)
summary(regression2)
Call:
lm(formula = hap ~ satfam + health + socfriend, data = hap_ancova2.clean)
Residuals:
Min 1Q Median 3Q Max
-20.9955 -6.1508 0.0402 6.5118 19.5972
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 22.1176 5.0588 4.372 3.23e-05 ***
satfam 2.5023 0.6515 3.841 0.000225 ***
health 3.0764 0.8946 3.439 0.000880 ***
socfriend 1.5117 0.7884 1.918 0.058272 .
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 8.888 on 92 degrees of freedom
Multiple R-squared: 0.2878, Adjusted R-squared: 0.2646
F-statistic: 12.39 on 3 and 92 DF, p-value: 7.035e-07
Now, plug them into the ANCOVA model:
ancova2 <- aov(hap ~ treat.fac + satfam + health + socfriend, data = hap_ancova2.clean)
summary(ancova2)
Df Sum Sq Mean Sq F value Pr(>F)
treat.fac 1 298 297.5 3.960 0.0496 *
satfam 1 1504 1503.7 20.014 2.22e-05 ***
health 1 1280 1279.5 17.030 8.14e-05 ***
socfriend 1 286 285.6 3.801 0.0543 .
Residuals 91 6837 75.1
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Regression model with the variable treat.fac
regression3 <- lm(hap ~ treat.fac + satfam + health + socfriend, data = hap_ancova2.clean)
summary(regression3)
Call:
lm(formula = hap ~ treat.fac + satfam + health + socfriend, data = hap_ancova2.clean)
Residuals:
Min 1Q Median 3Q Max
-18.9098 -6.6984 0.6385 5.9155 17.7141
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 19.1636 5.0862 3.768 0.000292 ***
treat.facOptimism therapy group 4.3025 1.7993 2.391 0.018849 *
satfam 2.3761 0.6376 3.727 0.000337 ***
health 3.4395 0.8856 3.884 0.000195 ***
socfriend 1.4992 0.7689 1.950 0.054288 .
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 8.668 on 91 degrees of freedom
Multiple R-squared: 0.3299, Adjusted R-squared: 0.3005
F-statistic: 11.2 on 4 and 91 DF, p-value: 1.964e-07
Example #3: Repeated Measures ANOVA
Reshaping data from wide to long
Hint for this week’s content review!
sleep <- read_dta("sleep_wide.dta")
glimpse(sleep)
Rows: 100
Columns: 4
$ id <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17…
$ sleep1 <dbl> 303, 331, 374, 374, 349, 353, 343, 390, 307, 360, 327, 36…
$ sleep2 <dbl> 349, 380, 400, 385, 442, 365, 370, 399, 410, 346, 371, 34…
$ sleep3 <dbl> 382, 350, 345, 356, 366, 372, 354, 352, 375, 403, 336, 40…
summary(sleep)
id sleep1 sleep2 sleep3
Min. : 1.00 Min. :276.0 Min. :308.0 Min. :297.0
1st Qu.: 25.75 1st Qu.:326.5 1st Qu.:348.0 1st Qu.:348.8
Median : 50.50 Median :349.0 Median :368.0 Median :366.5
Mean : 50.50 Mean :348.2 Mean :369.3 Mean :366.7
3rd Qu.: 75.25 3rd Qu.:369.2 3rd Qu.:389.0 3rd Qu.:382.2
Max. :100.00 Max. :434.0 Max. :466.0 Max. :437.0
Using pivot_longer to reshape from wide to long
sleep.long <- sleep %>%
pivot_longer(.,
cols = starts_with("sleep"),
names_to = "month",
values_to = "sleep",
names_prefix = "sleep",
)
glimpse(sleep.long)
Rows: 300
Columns: 3
$ id <dbl> 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7…
$ month <chr> "1", "2", "3", "1", "2", "3", "1", "2", "3", "1", "2", "3"…
$ sleep <dbl> 303, 349, 382, 331, 380, 350, 374, 400, 345, 374, 385, 356…
Reshape back to wide using pivot_wider
sleep.wide <- sleep.long %>%
pivot_wider(.,
id_cols = c("id"),
names_from = month,
values_from = sleep,
names_prefix = "sleep",
)
glimpse(sleep.wide)
Rows: 100
Columns: 4
$ id <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17…
$ sleep1 <dbl> 303, 331, 374, 374, 349, 353, 343, 390, 307, 360, 327, 36…
$ sleep2 <dbl> 349, 380, 400, 385, 442, 365, 370, 399, 410, 346, 371, 34…
$ sleep3 <dbl> 382, 350, 345, 356, 366, 372, 354, 352, 375, 403, 336, 40…
Density Plot for Month 1
kdensity1 <- ggplot(sleep, aes(x=sleep1)) +
geom_density() +
stat_function(fun = dnorm, n = 100, args = list(mean = 350, sd = 30), color = "red", linetype = "dashed") +
labs(title="Distribution of Sleep in Month 1",x="Sleep (minutes per night)", y = "Density", caption = "N = 100 participants. The red line indicates a normal distribution. The black lines represents the observed distribution.")
kdensity1

Density Plot for Month 2
kdensity2 <- ggplot(sleep, aes(x=sleep2)) +
geom_density() +
stat_function(fun = dnorm, n = 100, args = list(mean = 370, sd = 30), color = "red", linetype = "dashed") +
labs(title="Distribution of Sleep in Month 2",x="Sleep (minutes per night)", y = "Density", caption = "N = 100 participants. The red line indicates a normal distribution. The black lines represents the observed distribution.")
kdensity2

Density Plot for Month 3
kdensity3 <- ggplot(sleep, aes(x=sleep2)) +
geom_density() +
stat_function(fun = dnorm, n = 100, args = list(mean = 370, sd = 30), color = "red", linetype = "dashed") +
labs(title="Distribution of Sleep in Month 3",x="Sleep (minutes per night)", y = "Density", caption = "N = 100 participants. The red line indicates a normal distribution. The black lines represents the observed distribution.")
kdensity3

Summary Statistics of Sleep by Month
library(rstatix)
library(ggpubr)
sleep.long %>%
group_by(month) %>%
get_summary_stats(sleep, type = "mean_sd")
Code for basic repeated measures ANOVA
Note:id is participant id in this case.
sleep.aov <- anova_test(data = sleep.long, dv = sleep, wid = id, within = month)
summary(sleep.aov)
Length Class Mode
ANOVA 7 data.frame list
Mauchly's Test for Sphericity 4 data.frame list
Sphericity Corrections 9 data.frame list
Here is the basic ANOVA table:
get_anova_table(sleep.aov)
ANOVA Table (type III tests)
Effect DFn DFd F p p<.05 ges
1 month 2 198 17.938 6.92e-08 * 0.09
Here are the corrected p-values with the sphericity adjustments:
sleep.aov$`Sphericity Corrections`
Post-hoc comparisons by month, using Tukey’s method:
pairwise_t_test(
formula = sleep ~ month, paired = TRUE,
p.adjust.method = "bonferroni",
data = sleep.long
)
LS0tCnRpdGxlOiAiTVZTIE1vZHVsZSA0IEhhbmRvdXQ6IEFkdmFuY2VkIEFOT1ZBIgphdXRob3I6ICJEci4gQnJvZGEiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgRXhhbXBsZSAjMTogQU5DT1ZBIHdpdGggYW4gZXhwZXJpbWVudGFsIGRlc2lnbiBhbmQgcHJldGVzdCBhcyBjb3ZhcmlhdGUKIyMgTG9hZCBpbiBzb21lIGhlbHBmdWwgcGFja2FnZXMKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGhhdmVuKQpgYGAKCiMjIExvYWQgaW4gdGhlIGRhdGFzZXQKYGBge3J9CmhhcF9hbmNvdmEgPC0gcmVhZF9kdGEoImhhcC1hbmNvdmExLmR0YSIpCmBgYAoKIyMgRXhwbG9yZSB5b3VyIGRhdGEKYGBge3J9CmdsaW1wc2UoaGFwX2FuY292YSkKYGBgCiMjIENsZWFuIHlvdXIgZGF0YQpgYGB7cn0KaGFwX2FuY292YS5jbGVhbiA8LSBoYXBfYW5jb3ZhICU+JQptdXRhdGUoLiwKICB0cmVhdC5mYWMgPSBhc19mYWN0b3IodHJlYXQpKQpgYGAKCiMjIENoZWNrIG91dCBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGZvciB0aGUgdHJlYXRtZW50IGZhY3RvciBhbmQgY29udGludW91cyB2YXJpYWJsZXMgClRoZSBgc3VtbWFyeWAgcGFja2FnZSBpbiBiYXNlIGBSYCBpcyBhIHF1aWNrIGFuZCBlYXN5IHdheSB0byBkbyB0aGlzISAKYGBge3J9CnN1bW1hcnkoaGFwX2FuY292YS5jbGVhbikKYGBgCgojIyBDb3JyZWxhdGUgcHJlIGFuZCBwb3N0IHRlc3QgcmVzdWx0cy0gaG93IHNpbWlsYXIgYXJlIHRoZXk/IApIb3BlZnVsbHksIHNvbWV3aGF0IHNpbWlsYXIhIElmIG5vdCwgdGhlcmUgbWF5IG5vdCBiZSBzeXN0ZW1hdGljIGNoYW5nZSBvdmVyIHRpbWUuIFRoZSBgY29yLnRlc3RgIGZ1bmN0aW9uIGluIGJhc2UgYFJgIHdvcmtzIHdlbGwgZm9yIHNpbXBsZSBjb3JyZWxhdGlvbnMuCmBgYHtyfQpjb3IudGVzdChoYXBfYW5jb3ZhLmNsZWFuJGhhcF9wcmUsIGhhcF9hbmNvdmEuY2xlYW4kaGFwX3Bvc3QpCmBgYAoKIyMgUnVuIGEgYmFzaWMgQU5DT1ZBIApIZXJlLCB3ZSBoYXZlIGBoYXBfcG9zdGAgYXMgdGhlIG91dGNvbWUsIGB0cmVhdGAgYXMgdGhlIGZhY3RvciwgYW5kIGBoYXBfcHJlYCBhcyB0aGUgY292YXJpYXRlLiBTaW5jZSB3ZSBjbGVhbmVkIG91ciBkYXRhIGF0IHRoZSBiZWdpbm5pbmcsIGBSYCByZWNvZ25pemVzIHRoYXQgYHRyZWF0YCBpcyBhIGZhY3RvciBhbmQgYGhhcF9wcmVgIGlzIGNvbnRpbnVvdXMgc28gaXQgd2lsbCBydW4gYXMgYW4gQU5DT1ZBIGFuZCBub3QgYSB0d28td2F5IEFOT1ZBLgpgYGB7cn0KYW5jb3ZhMTwtYW92KGhhcF9wb3N0fiB0cmVhdC5mYWMgKyBoYXBfcHJlLGRhdGE9aGFwX2FuY292YS5jbGVhbikKc3VtbWFyeShhbmNvdmExKQpgYGAKCiMjIElmIHlvdSBoYWQgbW9yZSB0aGFuIDIgZ3JvdXBzLCB5b3UgY291bGQgZG8gcG9zdC1ob2MgY29tcGFyaXNvbnM6CmBgYHtyfQpUdWtleUhTRChhbmNvdmExKQpgYGAKCiMjIEdldCBtZWFzdXJlcyBvZiBlZmZlY3Qgc2l6ZToKYGBge3J9CmxpYnJhcnkoZWZmZWN0c2l6ZSkKZXRhX3NxdWFyZWQoYW5jb3ZhMSkKb21lZ2Ffc3F1YXJlZChhbmNvdmExKQpgYGAKCiMjIFdoYXQgaWYgd2UgZGlkbid0IGhhdmUgdGhlIHByZXRlc3Qgc2NvcmU/Ck5vdywgc2VlIHRoZSBkaWZmZXJlbmNlIHdpdGhvdXQgdGhlIGNvdmFyaWF0ZS0ganVzdCBhbiBBTk9WQSBmb3IgYHRyZWF0YDoKYGBge3J9CmFub3ZhMTwtYW92KGhhcF9wb3N0fiB0cmVhdC5mYWMsIGRhdGE9aGFwX2FuY292YS5jbGVhbikKc3VtbWFyeShhbm92YTEpCmBgYAoKIyMgQW5kLCBvZiBjb3Vyc2UtIGhvdyB0byBydW4gdGhpcyBhcyBhIG11bHRpcGxlIHJlZ3Jlc3Npb24hCmBgYHtyfQpyZWdyZXNzaW9uMSA8LSBsbShoYXBfcG9zdCB+IHRyZWF0LmZhYyArIGhhcF9wcmUsIGRhdGEgPSBoYXBfYW5jb3ZhLmNsZWFuKQpzdW1tYXJ5KHJlZ3Jlc3Npb24xKQpgYGAKCiMgRXhhbXBsZSAjMjogQU5DT1ZBIHdpdGggZXhwZXJpbWVudGFsIGRlc2lnbiBhbmQgcmVsYXRlZCBjb3ZhcmlhdGVzIChub3QgcHJldGVzdCkKSXQgbWlnaHQgYmUgYmV0dGVyIGZvciBleHRlcm5hbCB2YWxpZGl0eSB0byB1c2UgcmVsYXRlZCBtZWFzdXJlcyBhcyBjb3ZhcmlhdGVzLCBpbnN0ZWFkIG9mIGEgcHJldGVzdC4KIyMgTG9hZCBpbiB0aGUgZGF0YXNldDoKYGBge3J9CmhhcF9hbmNvdmEyIDwtIHJlYWRfZHRhKCJoYXAtYW5jb3ZhMi5kdGEiKQpgYGAKCiMjIEV4cGxvcmUgeW91ciBkYXRhOgpgYGB7cn0KZ2xpbXBzZShoYXBfYW5jb3ZhMikKYGBgCgojIyBDbGVhbiB5b3VyIGRhdGE6CmBgYHtyfQpoYXBfYW5jb3ZhMi5jbGVhbiA8LSBoYXBfYW5jb3ZhMiAlPiUKICBtdXRhdGUoLiwKICAgICAgICAgdHJlYXQuZmFjID0gYXNfZmFjdG9yKHRyZWF0KSkKYGBgCgojIyBFeHBsb3JlIHlvdXIgZGF0YQpgYGB7cn0Kc3VtbWFyeShoYXBfYW5jb3ZhMi5jbGVhbikKYGBgCgojIyBIb3cgbXVjaCB2YXJpYW5jZSBkb2VzIG91ciBzZXQgb2YgY292YXJpYXRlcyBleHBsYWluPwpgYGB7cn0KcmVncmVzc2lvbjIgPC0gbG0oaGFwIH4gc2F0ZmFtICsgaGVhbHRoICsgc29jZnJpZW5kLCBkYXRhID0gaGFwX2FuY292YTIuY2xlYW4pCnN1bW1hcnkocmVncmVzc2lvbjIpCmBgYAoKIyMgTm93LCBwbHVnIHRoZW0gaW50byB0aGUgQU5DT1ZBIG1vZGVsOgpgYGB7cn0KYW5jb3ZhMiA8LSBhb3YoaGFwIH4gdHJlYXQuZmFjICsgc2F0ZmFtICsgaGVhbHRoICsgc29jZnJpZW5kLCBkYXRhID0gaGFwX2FuY292YTIuY2xlYW4pCnN1bW1hcnkoYW5jb3ZhMikKYGBgCgojIyBSZWdyZXNzaW9uIG1vZGVsIHdpdGggdGhlIHZhcmlhYmxlIGB0cmVhdC5mYWNgCmBgYHtyfQpyZWdyZXNzaW9uMyA8LSBsbShoYXAgfiB0cmVhdC5mYWMgKyBzYXRmYW0gKyBoZWFsdGggKyBzb2NmcmllbmQsIGRhdGEgPSBoYXBfYW5jb3ZhMi5jbGVhbikKc3VtbWFyeShyZWdyZXNzaW9uMykKYGBgCgojIEV4YW1wbGUgIzM6IFJlcGVhdGVkIE1lYXN1cmVzIEFOT1ZBCiMjIFJlc2hhcGluZyBkYXRhIGZyb20gd2lkZSB0byBsb25nIApIaW50IGZvciB0aGlzIHdlZWsncyBjb250ZW50IHJldmlldyEKYGBge3J9CnNsZWVwIDwtIHJlYWRfZHRhKCJzbGVlcF93aWRlLmR0YSIpCmBgYAoKYGBge3J9CmdsaW1wc2Uoc2xlZXApCmBgYAoKYGBge3J9CnN1bW1hcnkoc2xlZXApCmBgYAoKIyMgVXNpbmcgYHBpdm90X2xvbmdlcmAgdG8gcmVzaGFwZSBmcm9tICp3aWRlKiB0byAqbG9uZyoKYGBge3J9CnNsZWVwLmxvbmcgPC0gc2xlZXAgJT4lCnBpdm90X2xvbmdlciguLAogICAgICAgICAgICAgIGNvbHMgPSBzdGFydHNfd2l0aCgic2xlZXAiKSwKICAgICAgICAgICAgICBuYW1lc190byA9ICJtb250aCIsCiAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInNsZWVwIiwKICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAic2xlZXAiLAogICAgICAgICAgICAgICkKZ2xpbXBzZShzbGVlcC5sb25nKQpgYGAKCiMjIFJlc2hhcGUgYmFjayB0byB3aWRlIHVzaW5nIGBwaXZvdF93aWRlcmAKYGBge3J9CnNsZWVwLndpZGUgPC0gc2xlZXAubG9uZyAlPiUKICBwaXZvdF93aWRlciguLAogICAgICAgICAgICAgIGlkX2NvbHMgPSBjKCJpZCIpLAogICAgICAgICAgICAgIG5hbWVzX2Zyb20gPSBtb250aCwKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IHNsZWVwLAogICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJzbGVlcCIsCiAgICAgICAgICAgICAgKQoKZ2xpbXBzZShzbGVlcC53aWRlKQpgYGAKCiMjIERlbnNpdHkgUGxvdCBmb3IgTW9udGggMQpgYGB7cn0Ka2RlbnNpdHkxIDwtIGdncGxvdChzbGVlcCwgYWVzKHg9c2xlZXAxKSkgKyAKICBnZW9tX2RlbnNpdHkoKSArIAogIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIG4gPSAxMDAsIGFyZ3MgPSBsaXN0KG1lYW4gPSAzNTAsIHNkID0gMzApLCBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgbGFicyh0aXRsZT0iRGlzdHJpYnV0aW9uIG9mIFNsZWVwIGluIE1vbnRoIDEiLHg9IlNsZWVwIChtaW51dGVzIHBlciBuaWdodCkiLCB5ID0gIkRlbnNpdHkiLCBjYXB0aW9uID0gIk4gPSAxMDAgcGFydGljaXBhbnRzLiBUaGUgcmVkIGxpbmUgaW5kaWNhdGVzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gVGhlIGJsYWNrIGxpbmVzIHJlcHJlc2VudHMgdGhlIG9ic2VydmVkIGRpc3RyaWJ1dGlvbi4iKQprZGVuc2l0eTEKYGBgCiMjIERlbnNpdHkgUGxvdCBmb3IgTW9udGggMgpgYGB7cn0Ka2RlbnNpdHkyIDwtIGdncGxvdChzbGVlcCwgYWVzKHg9c2xlZXAyKSkgKyAKICBnZW9tX2RlbnNpdHkoKSArIAogIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIG4gPSAxMDAsIGFyZ3MgPSBsaXN0KG1lYW4gPSAzNzAsIHNkID0gMzApLCBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgbGFicyh0aXRsZT0iRGlzdHJpYnV0aW9uIG9mIFNsZWVwIGluIE1vbnRoIDIiLHg9IlNsZWVwIChtaW51dGVzIHBlciBuaWdodCkiLCB5ID0gIkRlbnNpdHkiLCBjYXB0aW9uID0gIk4gPSAxMDAgcGFydGljaXBhbnRzLiBUaGUgcmVkIGxpbmUgaW5kaWNhdGVzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gVGhlIGJsYWNrIGxpbmVzIHJlcHJlc2VudHMgdGhlIG9ic2VydmVkIGRpc3RyaWJ1dGlvbi4iKQprZGVuc2l0eTIKYGBgCgojIyBEZW5zaXR5IFBsb3QgZm9yIE1vbnRoIDMKYGBge3J9CmtkZW5zaXR5MyA8LSBnZ3Bsb3Qoc2xlZXAsIGFlcyh4PXNsZWVwMikpICsgCiAgZ2VvbV9kZW5zaXR5KCkgKyAKICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBuID0gMTAwLCBhcmdzID0gbGlzdChtZWFuID0gMzcwLCBzZCA9IDMwKSwgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGxhYnModGl0bGU9IkRpc3RyaWJ1dGlvbiBvZiBTbGVlcCBpbiBNb250aCAzIix4PSJTbGVlcCAobWludXRlcyBwZXIgbmlnaHQpIiwgeSA9ICJEZW5zaXR5IiwgY2FwdGlvbiA9ICJOID0gMTAwIHBhcnRpY2lwYW50cy4gVGhlIHJlZCBsaW5lIGluZGljYXRlcyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFRoZSBibGFjayBsaW5lcyByZXByZXNlbnRzIHRoZSBvYnNlcnZlZCBkaXN0cmlidXRpb24uIikKa2RlbnNpdHkzCmBgYAoKIyMgVXNpbmcgZGF0YSBpbiAqbG9uZyogZm9ybSwgd2UgY2FuIGZhY2V0IGJ5IG1vbnRoIHRvIGluY2x1ZGUgYWxsIHRocmVlIGdyYXBocyBpbiBvbmUgdGFibGUKYGBge3J9CmFsbF9zbGVlcDwtZ2dwbG90KHNsZWVwLmxvbmcsIGFlcyh4PXNsZWVwKSkrCiAgZ2VvbV9kZW5zaXR5KCkrZmFjZXRfd3JhcChtb250aCB+IC4sIG5jb2wgPSAxKQphbGxfc2xlZXAKYGBgCgojIyBTdW1tYXJ5IFN0YXRpc3RpY3Mgb2YgU2xlZXAgYnkgTW9udGgKYGBge3J9CmxpYnJhcnkocnN0YXRpeCkKbGlicmFyeShnZ3B1YnIpCnNsZWVwLmxvbmcgJT4lCiAgZ3JvdXBfYnkobW9udGgpICU+JQogIGdldF9zdW1tYXJ5X3N0YXRzKHNsZWVwLCB0eXBlID0gIm1lYW5fc2QiKQpgYGAKCiMjIENvZGUgZm9yIGJhc2ljIHJlcGVhdGVkIG1lYXN1cmVzIEFOT1ZBIApOb3RlOmBpZGAgaXMgcGFydGljaXBhbnQgaWQgaW4gdGhpcyBjYXNlLgpgYGB7cn0Kc2xlZXAuYW92IDwtIGFub3ZhX3Rlc3QoZGF0YSA9IHNsZWVwLmxvbmcsIGR2ID0gc2xlZXAsIHdpZCA9IGlkLCB3aXRoaW4gPSBtb250aCkKc3VtbWFyeShzbGVlcC5hb3YpCmBgYApIZXJlIGlzIHRoZSBiYXNpYyBBTk9WQSB0YWJsZToKYGBge3J9CmdldF9hbm92YV90YWJsZShzbGVlcC5hb3YpCmBgYApIZXJlIGFyZSB0aGUgY29ycmVjdGVkIHAtdmFsdWVzIHdpdGggdGhlIHNwaGVyaWNpdHkgYWRqdXN0bWVudHM6CmBgYHtyfQpzbGVlcC5hb3YkYFNwaGVyaWNpdHkgQ29ycmVjdGlvbnNgCmBgYAoKIyMgUG9zdC1ob2MgY29tcGFyaXNvbnMgYnkgbW9udGgsIHVzaW5nIFR1a2V5J3MgbWV0aG9kOgpgYGB7cn0KcGFpcndpc2VfdF90ZXN0KAogICAgZm9ybXVsYSA9IHNsZWVwIH4gbW9udGgsIHBhaXJlZCA9IFRSVUUsCiAgICBwLmFkanVzdC5tZXRob2QgPSAiYm9uZmVycm9uaSIsCiAgICBkYXRhID0gc2xlZXAubG9uZwogICAgKQpgYGAKCiMgQ2hlY2sgZm9yIHNwaGVyaWNpdHkgYW5kIG11bHRpdmFyaWF0ZSBub3JtYWxpdHkKIyMjICBTdGFydCB3aXRoIHVuaXZhcmlhdGUgbm9ybWFsaXR5LCBwbG90cyBhbmQgc3RhdGlzdGljcwpgYGB7cn0KbGlicmFyeShNVk4pCnNsZWVwX3VuaXZhcmlhdGUgPC0gbXZuKGRhdGEgPSBzbGVlcC53aWRlLCBtdm5UZXN0ID0gTlVMTCwgdW5pdmFyaWF0ZVBsb3QgPSAicXFwbG90IikKCnNsZWVwX3VuaXZhcmlhdGUyIDwtIG12bihkYXRhID0gc2xlZXAud2lkZSwgbXZuVGVzdCA9IE5VTEwsIHVuaXZhcmlhdGVUZXN0ID0gIlNXIiwgZGVzYyA9IFRSVUUpCnNsZWVwX3VuaXZhcmlhdGUyJHVuaXZhcmlhdGVOb3JtYWxpdHkKYGBgCiMjIyBUZXN0cyBmb3IgTVZOCiMjIyMgRG9vcm5pay1IYW5zZW4gVGVzdCAKVGhlIGxhc3QgY29sdW1uIGluZGljYXRlcyB3aGV0aGVyIGRhdGFzZXQgZm9sbG93cyBhIG11bHRpdmFyaWF0ZSBub3JtYWxpdHkgb3Igbm90IChpLmUsIFlFUyBvciBOTykgYXQgc2lnbmlmaWNhbmNlIGxldmVsIDAuMDUuCmBgYHtyfQpzbGVlcF9tdm4gPC0gbXZuKGRhdGEgPSBzbGVlcC53aWRlLCBtdm5UZXN0ID0gImRoIikKc2xlZXBfbXZuJG11bHRpdmFyaWF0ZU5vcm1hbGl0eQpgYGAKCiMjIyMgTWFyZGlhJ3MgTVZOIFRlc3QgClRoaXMgZnVuY3Rpb24gcGVyZm9ybXMgbXVsdGl2YXJpYXRlIHNrZXduZXNzIGFuZCBrdXJ0b3NpcyB0ZXN0cyBhdCB0aGUgc2FtZSB0aW1lIGFuZCBjb21iaW5lcyB0ZXN0IHJlc3VsdHMgZm9yIG11bHRpdmFyaWF0ZSBub3JtYWxpdHkuIElmIGJvdGggdGVzdHMgaW5kaWNhdGUgbXVsdGl2YXJpYXRlIG5vcm1hbGl0eSwgdGhlbiBkYXRhIGZvbGxvd3MgYSBtdWx0aXZhcmlhdGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiBhdCB0aGUgMC4wNSBzaWduaWZpY2FuY2UgbGV2ZWwuCmBgYHtyfQpzbGVlcF9tdm4yIDwtIG12bihkYXRhID0gc2xlZXAud2lkZSwgbXZuVGVzdCA9ICJtYXJkaWEiKQpzbGVlcF9tdm4yJG11bHRpdmFyaWF0ZU5vcm1hbGl0eQpgYGAKIyMgQ2hlY2sgdGhlIHNwaGVyaWNpdHkgYXNzdW1wdGlvbgpgYGB7cn0Kc2xlZXAuYW92JGBNYXVjaGx5J3MgVGVzdCBmb3IgU3BoZXJpY2l0eWAKYGBgCg==