Example #1: ANCOVA with an experimental design and pretest as covariate
Load in some helpful packages
library(tidyverse)
library(haven)
Load in the dataset
write_ancova <- read_dta("Writing_Tech.dta")
Explore your data
glimpse(write_ancova)
Rows: 30
Columns: 3
$ Treatment <dbl+lbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3,...
$ Cog_Engage <dbl> 3, 2, 5, 2, 2, 2, 7, 2, 4, 7, 5, 3, 4, 4, 7, 5, 4, 9, 2, ...
$ Pre_Engage <dbl> 4, 1, 5, 1, 2, 2, 7, 4, 5, 5, 3, 1, 2, 2, 6, 4, 2, 1, 3, ...
Clean your data
write_ancova.clean <- write_ancova %>%
mutate(.,
treat.fac = as_factor(Treatment),
cog.fac = as_factor(Cog_Engage),
pre.fac = as_factor(Pre_Engage))
Check out descriptive statistics for the treatment factor and continuous variables
summary(write_ancova.clean)
Treatment Cog_Engage Pre_Engage treat.fac cog.fac
Min. :1.000 Min. :2.000 Min. :0.000 Paper/ Pencil: 9 4 :8
1st Qu.:1.000 1st Qu.:3.000 1st Qu.:1.000 Laptop : 8 2 :7
Median :2.000 Median :4.000 Median :2.500 Tablet :13 5 :4
Mean :2.133 Mean :4.367 Mean :2.733 3 :3
3rd Qu.:3.000 3rd Qu.:5.750 3rd Qu.:4.000 6 :3
Max. :3.000 Max. :9.000 Max. :7.000 7 :3
(Other):2
pre.fac
1 :6
2 :6
3 :5
4 :4
5 :4
0 :3
(Other):2
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(write_ancova.clean$Cog_Engage, write_ancova.clean$Pre_Engage)
Pearson's product-moment correlation
data: write_ancova.clean$Cog_Engage and write_ancova.clean$Pre_Engage
t = 1.345, df = 28, p-value = 0.1894
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
-0.1250150 0.5571688
sample estimates:
cor
0.2463496
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(Cog_Engage ~ treat.fac + Pre_Engage, data = write_ancova.clean)
summary(ancova1)
Df Sum Sq Mean Sq F value Pr(>F)
treat.fac 2 16.84 8.422 2.770 0.0812 .
Pre_Engage 1 15.08 15.076 4.959 0.0348 *
Residuals 26 79.05 3.040
---
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: Pre_EngageError in rep.int(n, length(means)) : unimplemented type 'NULL' in 'rep3'
Get measures of effect size:
library(effectsize)
package 㤼㸱effectsize㤼㸲 was built under R version 4.0.3
eta_squared(ancova1)
Parameter | Eta2 (partial) | 90% CI
------------------------------------------
treat.fac | 0.18 | [0.00, 0.37]
Pre_Engage | 0.16 | [0.01, 0.37]
omega_squared(ancova1)
Parameter | Omega2 (partial) | 90% CI
--------------------------------------------
treat.fac | 0.11 | [0.00, 0.28]
Pre_Engage | 0.12 | [0.00, 0.32]
What if we didn’t have the pretest score?
Now, see the difference without the covariate- just an ANOVA for treat:
anova1<-aov(Cog_Engage ~ treat.fac, write_ancova.clean)
summary(anova1)
Df Sum Sq Mean Sq F value Pr(>F)
treat.fac 2 16.84 8.422 2.416 0.108
Residuals 27 94.12 3.486
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)
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)
summary(sleep)
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)
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)
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=sleep3)) +
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)
Here is the basic ANOVA table:
get_anova_table(sleep.aov)
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
)
LS0tDQp0aXRsZTogIk1WUyBNb2R1bGUgNDogQWR2YW5jZWQgQU5PVkEiDQphdXRob3I6ICJKYWtlIFJleW5vbGRzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyBFeGFtcGxlICMxOiBBTkNPVkEgd2l0aCBhbiBleHBlcmltZW50YWwgZGVzaWduIGFuZCBwcmV0ZXN0IGFzIGNvdmFyaWF0ZQ0KIyMgTG9hZCBpbiBzb21lIGhlbHBmdWwgcGFja2FnZXMNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGhhdmVuKQ0KYGBgDQoNCiMjIExvYWQgaW4gdGhlIGRhdGFzZXQNCmBgYHtyfQ0Kd3JpdGVfYW5jb3ZhIDwtIHJlYWRfZHRhKCJXcml0aW5nX1RlY2guZHRhIikNCmBgYA0KDQojIyBFeHBsb3JlIHlvdXIgZGF0YQ0KYGBge3J9DQpnbGltcHNlKHdyaXRlX2FuY292YSkNCmBgYA0KIyMgQ2xlYW4geW91ciBkYXRhDQpgYGB7cn0NCndyaXRlX2FuY292YS5jbGVhbiA8LSB3cml0ZV9hbmNvdmEgJT4lDQptdXRhdGUoLiwNCiAgdHJlYXQuZmFjID0gYXNfZmFjdG9yKFRyZWF0bWVudCksDQogIGNvZy5mYWMgPSBhc19mYWN0b3IoQ29nX0VuZ2FnZSksDQogIHByZS5mYWMgPSBhc19mYWN0b3IoUHJlX0VuZ2FnZSkpDQpgYGANCg0KIyMgQ2hlY2sgb3V0IGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgZm9yIHRoZSB0cmVhdG1lbnQgZmFjdG9yIGFuZCBjb250aW51b3VzIHZhcmlhYmxlcyANCmBgYHtyfQ0Kc3VtbWFyeSh3cml0ZV9hbmNvdmEuY2xlYW4pDQpgYGANCg0KIyMgQ29ycmVsYXRlIHByZSBhbmQgcG9zdCB0ZXN0IHJlc3VsdHMtIGhvdyBzaW1pbGFyIGFyZSB0aGV5PyANCkhvcGVmdWxseSwgc29tZXdoYXQgc2ltaWxhciEgSWYgbm90LCB0aGVyZSBtYXkgbm90IGJlIHN5c3RlbWF0aWMgY2hhbmdlIG92ZXIgdGltZS4gVGhlIGBjb3IudGVzdGAgZnVuY3Rpb24gaW4gYmFzZSBgUmAgd29ya3Mgd2VsbCBmb3Igc2ltcGxlIGNvcnJlbGF0aW9ucy4NCmBgYHtyfQ0KY29yLnRlc3Qod3JpdGVfYW5jb3ZhLmNsZWFuJENvZ19FbmdhZ2UsIHdyaXRlX2FuY292YS5jbGVhbiRQcmVfRW5nYWdlKQ0KYGBgDQoNCiMjIFJ1biBhIGJhc2ljIEFOQ09WQSANCkhlcmUsIHdlIGhhdmUgYGhhcF9wb3N0YCBhcyB0aGUgb3V0Y29tZSwgYHRyZWF0YCBhcyB0aGUgZmFjdG9yLCBhbmQgYGhhcF9wcmVgIGFzIHRoZSBjb3ZhcmlhdGUuIFNpbmNlIHdlIGNsZWFuZWQgb3VyIGRhdGEgYXQgdGhlIGJlZ2lubmluZywgYFJgIHJlY29nbml6ZXMgdGhhdCBgdHJlYXRgIGlzIGEgZmFjdG9yIGFuZCBgaGFwX3ByZWAgaXMgY29udGludW91cyBzbyBpdCB3aWxsIHJ1biBhcyBhbiBBTkNPVkEgYW5kIG5vdCBhIHR3by13YXkgQU5PVkEuDQpgYGB7cn0NCmFuY292YTE8LWFvdihDb2dfRW5nYWdlIH4gdHJlYXQuZmFjICsgUHJlX0VuZ2FnZSwgZGF0YSA9IHdyaXRlX2FuY292YS5jbGVhbikNCnN1bW1hcnkoYW5jb3ZhMSkNCmBgYA0KDQojIyBJZiB5b3UgaGFkIG1vcmUgdGhhbiAyIGdyb3VwcywgeW91IGNvdWxkIGRvIHBvc3QtaG9jIGNvbXBhcmlzb25zOg0KYGBge3J9DQpUdWtleUhTRChhbmNvdmExKQ0KYGBgDQoNCiMjIEdldCBtZWFzdXJlcyBvZiBlZmZlY3Qgc2l6ZToNCmBgYHtyfQ0KbGlicmFyeShlZmZlY3RzaXplKQ0KZXRhX3NxdWFyZWQoYW5jb3ZhMSkNCm9tZWdhX3NxdWFyZWQoYW5jb3ZhMSkNCmBgYA0KDQojIyBXaGF0IGlmIHdlIGRpZG4ndCBoYXZlIHRoZSBwcmV0ZXN0IHNjb3JlPw0KTm93LCBzZWUgdGhlIGRpZmZlcmVuY2Ugd2l0aG91dCB0aGUgY292YXJpYXRlLSBqdXN0IGFuIEFOT1ZBIGZvciBgdHJlYXRgOg0KYGBge3J9DQphbm92YTE8LWFvdihDb2dfRW5nYWdlIH4gdHJlYXQuZmFjLCB3cml0ZV9hbmNvdmEuY2xlYW4pDQpzdW1tYXJ5KGFub3ZhMSkNCmBgYA0KDQojIyBBbmQsIG9mIGNvdXJzZS0gaG93IHRvIHJ1biB0aGlzIGFzIGEgbXVsdGlwbGUgcmVncmVzc2lvbiENCmBgYHtyfQ0KcmVncmVzc2lvbjEgPC0gbG0oaGFwX3Bvc3QgfiB0cmVhdC5mYWMgKyBoYXBfcHJlLCBkYXRhID0gaGFwX2FuY292YS5jbGVhbikNCnN1bW1hcnkocmVncmVzc2lvbjEpDQpgYGANCg0KIyBFeGFtcGxlICMyOiBBTkNPVkEgd2l0aCBleHBlcmltZW50YWwgZGVzaWduIGFuZCByZWxhdGVkIGNvdmFyaWF0ZXMgKG5vdCBwcmV0ZXN0KQ0KSXQgbWlnaHQgYmUgYmV0dGVyIGZvciBleHRlcm5hbCB2YWxpZGl0eSB0byB1c2UgcmVsYXRlZCBtZWFzdXJlcyBhcyBjb3ZhcmlhdGVzLCBpbnN0ZWFkIG9mIGEgcHJldGVzdC4NCiMjIExvYWQgaW4gdGhlIGRhdGFzZXQ6DQpgYGB7cn0NCmhhcF9hbmNvdmEyIDwtIHJlYWRfZHRhKCJoYXAtYW5jb3ZhMi5kdGEiKQ0KYGBgDQoNCiMjIEV4cGxvcmUgeW91ciBkYXRhOg0KYGBge3J9DQpnbGltcHNlKGhhcF9hbmNvdmEyKQ0KYGBgDQoNCiMjIENsZWFuIHlvdXIgZGF0YToNCmBgYHtyfQ0KaGFwX2FuY292YTIuY2xlYW4gPC0gaGFwX2FuY292YTIgJT4lDQogIG11dGF0ZSguLA0KICAgICAgICAgdHJlYXQuZmFjID0gYXNfZmFjdG9yKHRyZWF0KSkNCmBgYA0KDQojIyBFeHBsb3JlIHlvdXIgZGF0YQ0KYGBge3J9DQpzdW1tYXJ5KGhhcF9hbmNvdmEyLmNsZWFuKQ0KYGBgDQoNCiMjIEhvdyBtdWNoIHZhcmlhbmNlIGRvZXMgb3VyIHNldCBvZiBjb3ZhcmlhdGVzIGV4cGxhaW4/DQpgYGB7cn0NCnJlZ3Jlc3Npb24yIDwtIGxtKGhhcCB+IHNhdGZhbSArIGhlYWx0aCArIHNvY2ZyaWVuZCwgZGF0YSA9IGhhcF9hbmNvdmEyLmNsZWFuKQ0Kc3VtbWFyeShyZWdyZXNzaW9uMikNCmBgYA0KDQojIyBOb3csIHBsdWcgdGhlbSBpbnRvIHRoZSBBTkNPVkEgbW9kZWw6DQpgYGB7cn0NCmFuY292YTIgPC0gYW92KGhhcCB+IHRyZWF0LmZhYyArIHNhdGZhbSArIGhlYWx0aCArIHNvY2ZyaWVuZCwgZGF0YSA9IGhhcF9hbmNvdmEyLmNsZWFuKQ0Kc3VtbWFyeShhbmNvdmEyKQ0KYGBgDQoNCiMjIFJlZ3Jlc3Npb24gbW9kZWwgd2l0aCB0aGUgdmFyaWFibGUgYHRyZWF0LmZhY2ANCmBgYHtyfQ0KcmVncmVzc2lvbjMgPC0gbG0oaGFwIH4gdHJlYXQuZmFjICsgc2F0ZmFtICsgaGVhbHRoICsgc29jZnJpZW5kLCBkYXRhID0gaGFwX2FuY292YTIuY2xlYW4pDQpzdW1tYXJ5KHJlZ3Jlc3Npb24zKQ0KYGBgDQoNCiMgRXhhbXBsZSAjMzogUmVwZWF0ZWQgTWVhc3VyZXMgQU5PVkENCiMjIFJlc2hhcGluZyBkYXRhIGZyb20gd2lkZSB0byBsb25nIA0KSGludCBmb3IgdGhpcyB3ZWVrJ3MgY29udGVudCByZXZpZXchDQpgYGB7cn0NCnNsZWVwIDwtIHJlYWRfZHRhKCJzbGVlcF93aWRlLmR0YSIpDQpgYGANCg0KYGBge3J9DQpnbGltcHNlKHNsZWVwKQ0KYGBgDQoNCmBgYHtyfQ0Kc3VtbWFyeShzbGVlcCkNCmBgYA0KDQojIyBVc2luZyBgcGl2b3RfbG9uZ2VyYCB0byByZXNoYXBlIGZyb20gKndpZGUqIHRvICpsb25nKg0KYGBge3J9DQpzbGVlcC5sb25nIDwtIHNsZWVwICU+JQ0KcGl2b3RfbG9uZ2VyKC4sDQogICAgICAgICAgICAgIGNvbHMgPSBzdGFydHNfd2l0aCgic2xlZXAiKSwNCiAgICAgICAgICAgICAgbmFtZXNfdG8gPSAibW9udGgiLA0KICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAic2xlZXAiLA0KICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAic2xlZXAiLA0KICAgICAgICAgICAgICApDQpnbGltcHNlKHNsZWVwLmxvbmcpDQpgYGANCg0KIyMgUmVzaGFwZSBiYWNrIHRvIHdpZGUgdXNpbmcgYHBpdm90X3dpZGVyYA0KYGBge3J9DQpzbGVlcC53aWRlIDwtIHNsZWVwLmxvbmcgJT4lDQogIHBpdm90X3dpZGVyKC4sDQogICAgICAgICAgICAgIGlkX2NvbHMgPSBjKCJpZCIpLA0KICAgICAgICAgICAgICBuYW1lc19mcm9tID0gbW9udGgsDQogICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gc2xlZXAsDQogICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJzbGVlcCIsDQogICAgICAgICAgICAgICkNCg0KZ2xpbXBzZShzbGVlcC53aWRlKQ0KYGBgDQoNCiMjIERlbnNpdHkgUGxvdCBmb3IgTW9udGggMQ0KYGBge3J9DQprZGVuc2l0eTEgPC0gZ2dwbG90KHNsZWVwLCBhZXMoeD1zbGVlcDEpKSArIA0KICBnZW9tX2RlbnNpdHkoKSArIA0KICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBuID0gMTAwLCBhcmdzID0gbGlzdChtZWFuID0gMzUwLCBzZCA9IDMwKSwgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBsYWJzKHRpdGxlPSJEaXN0cmlidXRpb24gb2YgU2xlZXAgaW4gTW9udGggMSIseD0iU2xlZXAgKG1pbnV0ZXMgcGVyIG5pZ2h0KSIsIHkgPSAiRGVuc2l0eSIsIGNhcHRpb24gPSAiTiA9IDEwMCBwYXJ0aWNpcGFudHMuIFRoZSByZWQgbGluZSBpbmRpY2F0ZXMgYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBUaGUgYmxhY2sgbGluZXMgcmVwcmVzZW50cyB0aGUgb2JzZXJ2ZWQgZGlzdHJpYnV0aW9uLiIpDQprZGVuc2l0eTENCmBgYA0KIyMgRGVuc2l0eSBQbG90IGZvciBNb250aCAyDQpgYGB7cn0NCmtkZW5zaXR5MiA8LSBnZ3Bsb3Qoc2xlZXAsIGFlcyh4PXNsZWVwMikpICsgDQogIGdlb21fZGVuc2l0eSgpICsgDQogIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIG4gPSAxMDAsIGFyZ3MgPSBsaXN0KG1lYW4gPSAzNzAsIHNkID0gMzApLCBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGxhYnModGl0bGU9IkRpc3RyaWJ1dGlvbiBvZiBTbGVlcCBpbiBNb250aCAyIix4PSJTbGVlcCAobWludXRlcyBwZXIgbmlnaHQpIiwgeSA9ICJEZW5zaXR5IiwgY2FwdGlvbiA9ICJOID0gMTAwIHBhcnRpY2lwYW50cy4gVGhlIHJlZCBsaW5lIGluZGljYXRlcyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFRoZSBibGFjayBsaW5lcyByZXByZXNlbnRzIHRoZSBvYnNlcnZlZCBkaXN0cmlidXRpb24uIikNCmtkZW5zaXR5Mg0KYGBgDQoNCiMjIERlbnNpdHkgUGxvdCBmb3IgTW9udGggMw0KYGBge3J9DQprZGVuc2l0eTMgPC0gZ2dwbG90KHNsZWVwLCBhZXMoeD1zbGVlcDMpKSArIA0KICBnZW9tX2RlbnNpdHkoKSArIA0KICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBuID0gMTAwLCBhcmdzID0gbGlzdChtZWFuID0gMzcwLCBzZCA9IDMwKSwgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBsYWJzKHRpdGxlPSJEaXN0cmlidXRpb24gb2YgU2xlZXAgaW4gTW9udGggMyIseD0iU2xlZXAgKG1pbnV0ZXMgcGVyIG5pZ2h0KSIsIHkgPSAiRGVuc2l0eSIsIGNhcHRpb24gPSAiTiA9IDEwMCBwYXJ0aWNpcGFudHMuIFRoZSByZWQgbGluZSBpbmRpY2F0ZXMgYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBUaGUgYmxhY2sgbGluZXMgcmVwcmVzZW50cyB0aGUgb2JzZXJ2ZWQgZGlzdHJpYnV0aW9uLiIpDQprZGVuc2l0eTMNCmBgYA0KDQojIyBVc2luZyBkYXRhIGluICpsb25nKiBmb3JtLCB3ZSBjYW4gZmFjZXQgYnkgbW9udGggdG8gaW5jbHVkZSBhbGwgdGhyZWUgZ3JhcGhzIGluIG9uZSB0YWJsZQ0KYGBge3J9DQphbGxfc2xlZXA8LWdncGxvdChzbGVlcC5sb25nLCBhZXMoeD1zbGVlcCkpKw0KICBnZW9tX2RlbnNpdHkoKStmYWNldF93cmFwKG1vbnRoIH4gLiwgbmNvbCA9IDEpDQphbGxfc2xlZXANCmBgYA0KDQojIyBTdW1tYXJ5IFN0YXRpc3RpY3Mgb2YgU2xlZXAgYnkgTW9udGgNCmBgYHtyfQ0KbGlicmFyeShyc3RhdGl4KQ0KbGlicmFyeShnZ3B1YnIpDQpzbGVlcC5sb25nICU+JQ0KICBncm91cF9ieShtb250aCkgJT4lDQogIGdldF9zdW1tYXJ5X3N0YXRzKHNsZWVwLCB0eXBlID0gIm1lYW5fc2QiKQ0KYGBgDQoNCiMjIENvZGUgZm9yIGJhc2ljIHJlcGVhdGVkIG1lYXN1cmVzIEFOT1ZBIA0KTm90ZTpgaWRgIGlzIHBhcnRpY2lwYW50IGlkIGluIHRoaXMgY2FzZS4NCmBgYHtyfQ0Kc2xlZXAuYW92IDwtIGFub3ZhX3Rlc3QoZGF0YSA9IHNsZWVwLmxvbmcsIGR2ID0gc2xlZXAsIHdpZCA9IGlkLCB3aXRoaW4gPSBtb250aCkNCnN1bW1hcnkoc2xlZXAuYW92KQ0KYGBgDQpIZXJlIGlzIHRoZSBiYXNpYyBBTk9WQSB0YWJsZToNCmBgYHtyfQ0KZ2V0X2Fub3ZhX3RhYmxlKHNsZWVwLmFvdikNCmBgYA0KSGVyZSBhcmUgdGhlIGNvcnJlY3RlZCBwLXZhbHVlcyB3aXRoIHRoZSBzcGhlcmljaXR5IGFkanVzdG1lbnRzOg0KYGBge3J9DQpzbGVlcC5hb3YkYFNwaGVyaWNpdHkgQ29ycmVjdGlvbnNgDQpgYGANCg0KIyMgUG9zdC1ob2MgY29tcGFyaXNvbnMgYnkgbW9udGgsIHVzaW5nIFR1a2V5J3MgbWV0aG9kOg0KYGBge3J9DQpwYWlyd2lzZV90X3Rlc3QoDQogICAgZm9ybXVsYSA9IHNsZWVwIH4gbW9udGgsIHBhaXJlZCA9IFRSVUUsDQogICAgcC5hZGp1c3QubWV0aG9kID0gImJvbmZlcnJvbmkiLA0KICAgIGRhdGEgPSBzbGVlcC5sb25nDQogICAgKQ0KYGBgDQoNCiMgQ2hlY2sgZm9yIHNwaGVyaWNpdHkgYW5kIG11bHRpdmFyaWF0ZSBub3JtYWxpdHkNCiMjIyAgU3RhcnQgd2l0aCB1bml2YXJpYXRlIG5vcm1hbGl0eSwgcGxvdHMgYW5kIHN0YXRpc3RpY3MNCmBgYHtyfQ0KbGlicmFyeShNVk4pDQpzbGVlcF91bml2YXJpYXRlIDwtIG12bihkYXRhID0gc2xlZXAud2lkZSwgbXZuVGVzdCA9IE5VTEwsIHVuaXZhcmlhdGVQbG90ID0gInFxcGxvdCIpDQoNCnNsZWVwX3VuaXZhcmlhdGUyIDwtIG12bihkYXRhID0gc2xlZXAud2lkZSwgbXZuVGVzdCA9IE5VTEwsIHVuaXZhcmlhdGVUZXN0ID0gIlNXIiwgZGVzYyA9IFRSVUUpDQpzbGVlcF91bml2YXJpYXRlMiR1bml2YXJpYXRlTm9ybWFsaXR5DQpgYGANCiMjIyBUZXN0cyBmb3IgTVZODQojIyMjIERvb3JuaWstSGFuc2VuIFRlc3QgDQpUaGUgbGFzdCBjb2x1bW4gaW5kaWNhdGVzIHdoZXRoZXIgZGF0YXNldCBmb2xsb3dzIGEgbXVsdGl2YXJpYXRlIG5vcm1hbGl0eSBvciBub3QgKGkuZSwgWUVTIG9yIE5PKSBhdCBzaWduaWZpY2FuY2UgbGV2ZWwgMC4wNS4NCmBgYHtyfQ0Kc2xlZXBfbXZuIDwtIG12bihkYXRhID0gc2xlZXAud2lkZSwgbXZuVGVzdCA9ICJkaCIpDQpzbGVlcF9tdm4kbXVsdGl2YXJpYXRlTm9ybWFsaXR5DQpgYGANCg0KIyMjIyBNYXJkaWEncyBNVk4gVGVzdCANClRoaXMgZnVuY3Rpb24gcGVyZm9ybXMgbXVsdGl2YXJpYXRlIHNrZXduZXNzIGFuZCBrdXJ0b3NpcyB0ZXN0cyBhdCB0aGUgc2FtZSB0aW1lIGFuZCBjb21iaW5lcyB0ZXN0IHJlc3VsdHMgZm9yIG11bHRpdmFyaWF0ZSBub3JtYWxpdHkuIElmIGJvdGggdGVzdHMgaW5kaWNhdGUgbXVsdGl2YXJpYXRlIG5vcm1hbGl0eSwgdGhlbiBkYXRhIGZvbGxvd3MgYSBtdWx0aXZhcmlhdGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiBhdCB0aGUgMC4wNSBzaWduaWZpY2FuY2UgbGV2ZWwuDQpgYGB7cn0NCnNsZWVwX212bjIgPC0gbXZuKGRhdGEgPSBzbGVlcC53aWRlLCBtdm5UZXN0ID0gIm1hcmRpYSIpDQpzbGVlcF9tdm4yJG11bHRpdmFyaWF0ZU5vcm1hbGl0eQ0KYGBgDQojIyBDaGVjayB0aGUgc3BoZXJpY2l0eSBhc3N1bXB0aW9uDQpgYGB7cn0NCnNsZWVwLmFvdiRgTWF1Y2hseSdzIFRlc3QgZm9yIFNwaGVyaWNpdHlgDQpgYGANCg==