Loading Packages & Data

Load tidyverse to get access to ggplot2 and dplyr. Load ggpmisc to add regression equation. Load broom to convert model outputs to dataframes. Load gridExtra and ggExtra for additional ggplot features such as marginal distributions on scatterplot.

ipak <- function(pkg){
  new.pkg <- pkg[!(pkg %in% installed.packages()[, "Package"])]
  if (length(new.pkg)) 
    install.packages(new.pkg, dependencies = TRUE)
  sapply(pkg, require, character.only = TRUE)
}
packages <- c("tidyverse", "ggpmisc", "broom", "gridExtra", "ggExtra")
ipak(packages)


Load in the data for the exercise.
NOTE: You will need to change this path on your machine

ex8_data <- read.csv("/Users/Julian/GDrive/Misc/Classes/InterStats/Ex8_LabData.csv") %>% tbl_df()


Make light theme default (white backgronud) for ggplot

old <- theme_set(theme_light(base_size = 12))

Scatterplot

Create a scatterplot with CESD scores at Time 1 (CESD1) and Time 2 (CESD2). Begin the plot with ggplot(dataframe, aes(X variable, Y variable)). Then add the scatterplot by adding the geom_point() layer.

ggplot(ex8_data, aes(CESD1, CESD2)) + 
  geom_point()


Add a LOESS line (i.e., smooth / running average) by adding the geom_smooth() layer.

ggplot(ex8_data, aes(CESD1, CESD2)) + 
  geom_point() + 
  geom_smooth()

Correlation

Compute the correlation between CESD1 and CESD2 using the cor(Variable 1, Variable 2) function.

cor(ex8_data$CESD1, ex8_data$CESD2)
[1] 0.4845713


Test the correlation between CESD1 and CESD2 using the cor.test(Variable 1, Variable 2) function.

cor.test(ex8_data$CESD1, ex8_data$CESD2)

    Pearson's product-moment correlation

data:  ex8_data$CESD1 and ex8_data$CESD2
t = 7.5954, df = 188, p-value = 0.0000000000014
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.3675733 0.5864698
sample estimates:
      cor 
0.4845713 

BONUS: Boostrapped Confidence Interval

This code is more complicated, but here’s the general sequence of events: set the random number generator to today’s date; specify the number of simulations (boostrap resamplings) we are conducting (in this case 1,000); boostrap our data; test the correlation on each replication; rename the variable name to “pearson_r”; arrange these estimates from smallest to largest; isolate the 2.5% and 97.5% percentiles; round these estimates to four decimals; then add a column to specify the lower and upper bound.

set.seed(10312016)
nSims <- 1000
ex8_data %>% 
  bootstrap(nSims) %>% 
  do(tidy(cor(.$CESD1, .$CESD2))) %>% 
  ungroup() %>% select(pearson_r = x) %>% 
  arrange(pearson_r) %>% 
  slice( c(nSims*.025, nSims*.975)) %>% 
  mutate_all(funs(round(.,4))) %>% 
  add_column(bound = c("lower", "upper"))

Regression

Now we will conduct a linear regression of CESD time 1 scores predicting CESD at time 2. Here we use the lm() function to create a linear model. The first argument takes a formula in model notation (Y variable ~ X variables; outcomes ~ predictors). The second argument takes a dataframe. We assign this function to a model object called “m1”. We then use summary() to retrieve key details about the linear model we just estimated.

NOTE: You may notice that the significance codes (e.g., ***) have a strange output in HTML files, but they will render more cleanly in your RStudio window.

m1 <- lm(CESD2 ~ CESD1, data=ex8_data)
summary(m1)

Call:
lm(formula = CESD2 ~ CESD1, data = ex8_data)

Residuals:
    Min      1Q  Median      3Q     Max 
-21.931  -7.460  -1.998   6.900  40.830 

Coefficients:
            Estimate Std. Error t value        Pr(>|t|)    
(Intercept)  2.56758    1.64667   1.559           0.121    
CESD1        0.45031    0.05929   7.595 0.0000000000014 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 10.27 on 188 degrees of freedom
Multiple R-squared:  0.2348,    Adjusted R-squared:  0.2307 
F-statistic: 57.69 on 1 and 188 DF,  p-value: 0.0000000000014


Plug in estimates to specify equation for line of best fit. Copy and paste the values we obtained above. The estimate of (Intercept) corresponds to a, whereas the estimate of CESD1 corresponds to b. \[ \hat{Y} = 2.57 + 0.45x \]


Examine confidence interval of the estimates using the confint(model object) function.

confint(m1)
                 2.5 %    97.5 %
(Intercept) -0.6807409 5.8158967
CESD1        0.3333569 0.5672637


Manually add linear regression fit onto scatterplot. Start by retrieving the estimate of the intercept (i.e., the first coefficient in the model) using the coef(model object) function and specifying the first element [1]. Next retrieve the estimate of the slope using the same function but instead indexing the second element [2]. Now we add a layer to create a manual line where we specify our slope and intercept using geom_abline(intercept = intercept estimate, slope = slope estimate). Let’s also color it blue so we can distinguish it from the black data points.

m1_intercept <- coef(m1)[1]
m1_slope <- coef(m1)[2]
ggplot(ex8_data, aes(CESD1, CESD2)) + 
  geom_point() + 
  geom_abline(intercept = m1_intercept, slope = m1_slope, color="blue")


Automatically add linear regression fit (w/ 95% CI) onto scatterplot using geom_smooth() but this time we specify that the method argument will be a linear model (lm), rather than a LOESS line (default).

ggplot(ex8_data, aes(CESD1, CESD2)) + 
  geom_point() + 
  geom_smooth(method=lm)


Remember to add title and axes to your plots using xlab(), ylab(), and ggtitle().

ggplot(ex8_data, aes(CESD1, CESD2)) + 
  geom_point() + 
  geom_smooth(method=lm) + 
  xlab("CESD at Time 1") + 
  ylab("CESD at Time 2") + 
  ggtitle("CESD at Time 2 as a function of CESD at Time 1")

BONUS: Adding Equation

We can also superimpose the regression line using the stat_poly_eq() function.

ggplot(ex8_data, aes(CESD1, CESD2)) + 
  geom_point() + 
  geom_smooth(method=lm) +
  xlab("CESD at Time 1") + 
  ylab("CESD at Time 2") + 
  ggtitle("CESD at Time 2 as a function of CESD at Time 1") +
  stat_poly_eq(aes(label =  paste(..eq.label.., ..adj.rr.label.., sep = "~~~~")),
               formula = y~x, parse = TRUE, label.x.npc = .5, color="blue")

BONUS: Marginal Histograms

We can also add histograms to convey marginal distributions

p1 <- ggplot(ex8_data, aes(CESD1, CESD2)) + 
  geom_point() + 
  geom_smooth(method=lm) +
  xlab("CESD at Time 1") + 
  ylab("CESD at Time 2") + 
  stat_poly_eq(aes(label =  paste(..eq.label.., ..adj.rr.label.., sep = "~~~~")),
               formula = y~x, parse = TRUE, label.x.npc = .5, color="blue") + 
  
  # Remove gridlines 
  theme_bw(base_size = 16) +
  theme(axis.text.x     = element_text(size = 12),
        axis.title.y    = element_text(vjust = +1.5),
        panel.grid.major  = element_blank(),
        panel.grid.minor  = element_blank(),
        legend.background = element_blank(),
        legend.key = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank(),
        axis.line  = element_line(colour = "black"))
ggMarginal(
  p = p1,
  type = 'histogram',
  bins = 20,
  margins = 'both',
  size = 2,
  col = 'black',
  fill="grey"
)

LS0tDQp0aXRsZTogIkxhYjA5VyAtIENvcnJlbGF0aW9uICYgUmVncmVzc2lvbiINCmF1dGhvcjogIkp1bGlhbiBXaWxscyINCmRhdGU6ICJOb3ZlbWJlciAybmQsIDIwMTYiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgdG9jOiB5ZXMNCi0tLQ0KDQojIyBMb2FkaW5nIFBhY2thZ2VzICYgRGF0YQ0KDQpMb2FkICoqdGlkeXZlcnNlKiogdG8gZ2V0IGFjY2VzcyB0byBnZ3Bsb3QyIGFuZCBkcGx5ci4gTG9hZCAqKmdncG1pc2MqKiB0byBhZGQgcmVncmVzc2lvbiBlcXVhdGlvbi4gTG9hZCAqKmJyb29tKiogdG8gY29udmVydCBtb2RlbCBvdXRwdXRzIHRvIGRhdGFmcmFtZXMuIExvYWQgKipncmlkRXh0cmEqKiBhbmQgKipnZ0V4dHJhKiogZm9yIGFkZGl0aW9uYWwgZ2dwbG90IGZlYXR1cmVzIHN1Y2ggYXMgbWFyZ2luYWwgZGlzdHJpYnV0aW9ucyBvbiBzY2F0dGVycGxvdC4NCmBgYHtyLCByZXN1bHRzPSdoaWRlJywgbWVzc2FnZT1GQUxTRX0NCmlwYWsgPC0gZnVuY3Rpb24ocGtnKXsNCiAgbmV3LnBrZyA8LSBwa2dbIShwa2cgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKVssICJQYWNrYWdlIl0pXQ0KICBpZiAobGVuZ3RoKG5ldy5wa2cpKSANCiAgICBpbnN0YWxsLnBhY2thZ2VzKG5ldy5wa2csIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogIHNhcHBseShwa2csIHJlcXVpcmUsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCn0NCg0KcGFja2FnZXMgPC0gYygidGlkeXZlcnNlIiwgImdncG1pc2MiLCAiYnJvb20iLCAiZ3JpZEV4dHJhIiwgImdnRXh0cmEiKQ0KaXBhayhwYWNrYWdlcykNCmBgYA0KDQo8YnIgLz4NCkxvYWQgaW4gdGhlIGRhdGEgZm9yIHRoZSBleGVyY2lzZS4gIA0KKipOT1RFOiBZb3Ugd2lsbCBuZWVkIHRvIGNoYW5nZSB0aGlzIHBhdGggb24geW91ciBtYWNoaW5lKioNCmBgYHtyLCBlY2hvPVRSVUV9DQpleDhfZGF0YSA8LSByZWFkLmNzdigiL1VzZXJzL0p1bGlhbi9HRHJpdmUvTWlzYy9DbGFzc2VzL0ludGVyU3RhdHMvRXg4X0xhYkRhdGEuY3N2IikgJT4lIHRibF9kZigpDQpgYGANCg0KPGJyIC8+DQpNYWtlIGxpZ2h0IHRoZW1lIGRlZmF1bHQgKHdoaXRlIGJhY2tncm9udWQpIGZvciBnZ3Bsb3QNCmBgYHtyfQ0Kb2xkIDwtIHRoZW1lX3NldCh0aGVtZV9saWdodChiYXNlX3NpemUgPSAxMikpDQpgYGANCg0KIyMgU2NhdHRlcnBsb3QNCg0KQ3JlYXRlIGEgc2NhdHRlcnBsb3Qgd2l0aCBDRVNEIHNjb3JlcyBhdCBUaW1lIDEgKENFU0QxKSBhbmQgVGltZSAyIChDRVNEMikuIEJlZ2luIHRoZSBwbG90IHdpdGggKipnZ3Bsb3QoKmRhdGFmcmFtZSosIGFlcyhYIHZhcmlhYmxlLCBZIHZhcmlhYmxlKSkqKi4gVGhlbiBhZGQgdGhlIHNjYXR0ZXJwbG90IGJ5IGFkZGluZyB0aGUgKipnZW9tX3BvaW50KCkqKiBsYXllci4gDQpgYGB7cn0NCmdncGxvdChleDhfZGF0YSwgYWVzKENFU0QxLCBDRVNEMikpICsgDQogIGdlb21fcG9pbnQoKQ0KDQpgYGANCg0KPGJyIC8+DQpBZGQgYSBMT0VTUyBsaW5lIChpLmUuLCBzbW9vdGggLyBydW5uaW5nIGF2ZXJhZ2UpIGJ5IGFkZGluZyB0aGUgKipnZW9tX3Ntb290aCgpKiogbGF5ZXIuIA0KYGBge3J9DQpnZ3Bsb3QoZXg4X2RhdGEsIGFlcyhDRVNEMSwgQ0VTRDIpKSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9zbW9vdGgoKQ0KDQpgYGANCg0KIyMgQ29ycmVsYXRpb24NCg0KQ29tcHV0ZSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBDRVNEMSBhbmQgQ0VTRDIgdXNpbmcgdGhlICoqY29yKCoqKlZhcmlhYmxlIDEqLCAqVmFyaWFibGUgMioqKikqKiBmdW5jdGlvbi4gDQpgYGB7cn0NCmNvcihleDhfZGF0YSRDRVNEMSwgZXg4X2RhdGEkQ0VTRDIpDQoNCmBgYA0KDQo8YnIgLz4NClRlc3QgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gQ0VTRDEgYW5kIENFU0QyIHVzaW5nIHRoZSAqKmNvci50ZXN0KCpWYXJpYWJsZSAxKiwgKlZhcmlhYmxlIDIqKSoqIGZ1bmN0aW9uLiANCmBgYHtyfQ0KY29yLnRlc3QoZXg4X2RhdGEkQ0VTRDEsIGV4OF9kYXRhJENFU0QyKQ0KDQpgYGANCg0KDQojIyMgQk9OVVM6IEJvb3N0cmFwcGVkIENvbmZpZGVuY2UgSW50ZXJ2YWwNCg0KVGhpcyBjb2RlIGlzIG1vcmUgY29tcGxpY2F0ZWQsIGJ1dCBoZXJlJ3MgdGhlIGdlbmVyYWwgc2VxdWVuY2Ugb2YgZXZlbnRzOiBzZXQgdGhlIHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9yIHRvIHRvZGF5J3MgZGF0ZTsgc3BlY2lmeSB0aGUgbnVtYmVyIG9mIHNpbXVsYXRpb25zIChib29zdHJhcCByZXNhbXBsaW5ncykgd2UgYXJlIGNvbmR1Y3RpbmcgKGluIHRoaXMgY2FzZSAxLDAwMCk7IGJvb3N0cmFwIG91ciBkYXRhOyB0ZXN0IHRoZSBjb3JyZWxhdGlvbiBvbiBlYWNoIHJlcGxpY2F0aW9uOyByZW5hbWUgdGhlIHZhcmlhYmxlIG5hbWUgdG8gInBlYXJzb25fciI7IGFycmFuZ2UgdGhlc2UgZXN0aW1hdGVzIGZyb20gc21hbGxlc3QgdG8gbGFyZ2VzdDsgaXNvbGF0ZSB0aGUgMi41JSBhbmQgOTcuNSUgcGVyY2VudGlsZXM7IHJvdW5kIHRoZXNlIGVzdGltYXRlcyB0byBmb3VyIGRlY2ltYWxzOyB0aGVuIGFkZCBhIGNvbHVtbiB0byBzcGVjaWZ5IHRoZSBsb3dlciBhbmQgdXBwZXIgYm91bmQuIA0KYGBge3J9DQpzZXQuc2VlZCgxMDMxMjAxNikNCm5TaW1zIDwtIDEwMDANCmV4OF9kYXRhICU+JSANCiAgYm9vdHN0cmFwKG5TaW1zKSAlPiUgDQogIGRvKHRpZHkoY29yKC4kQ0VTRDEsIC4kQ0VTRDIpKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIHNlbGVjdChwZWFyc29uX3IgPSB4KSAlPiUgDQogIGFycmFuZ2UocGVhcnNvbl9yKSAlPiUgDQogIHNsaWNlKCBjKG5TaW1zKi4wMjUsIG5TaW1zKi45NzUpKSAlPiUgDQogIG11dGF0ZV9hbGwoZnVucyhyb3VuZCguLDQpKSkgJT4lIA0KICBhZGRfY29sdW1uKGJvdW5kID0gYygibG93ZXIiLCAidXBwZXIiKSkNCg0KYGBgDQoNCiMjIFJlZ3Jlc3Npb24NCg0KTm93IHdlIHdpbGwgY29uZHVjdCBhIGxpbmVhciByZWdyZXNzaW9uIG9mIENFU0QgdGltZSAxIHNjb3JlcyBwcmVkaWN0aW5nIENFU0QgYXQgdGltZSAyLiBIZXJlIHdlIHVzZSB0aGUgKipsbSgpKiogZnVuY3Rpb24gdG8gY3JlYXRlIGEgbGluZWFyIG1vZGVsLiBUaGUgZmlyc3QgYXJndW1lbnQgdGFrZXMgYSAqZm9ybXVsYSogaW4gbW9kZWwgbm90YXRpb24gKFkgdmFyaWFibGUgfiBYIHZhcmlhYmxlczsgb3V0Y29tZXMgfiBwcmVkaWN0b3JzKS4gVGhlIHNlY29uZCBhcmd1bWVudCB0YWtlcyBhICpkYXRhZnJhbWUqLiBXZSBhc3NpZ24gdGhpcyBmdW5jdGlvbiB0byBhICptb2RlbCBvYmplY3QqIGNhbGxlZCAibTEiLiBXZSB0aGVuIHVzZSAqKnN1bW1hcnkoKSoqIHRvIHJldHJpZXZlIGtleSBkZXRhaWxzIGFib3V0IHRoZSBsaW5lYXIgbW9kZWwgd2UganVzdCBlc3RpbWF0ZWQuICANCiAgDQpOT1RFOiBZb3UgbWF5IG5vdGljZSB0aGF0IHRoZSBzaWduaWZpY2FuY2UgY29kZXMgKGUuZy4sICoqKikgaGF2ZSBhIHN0cmFuZ2Ugb3V0cHV0IGluIEhUTUwgZmlsZXMsIGJ1dCB0aGV5IHdpbGwgcmVuZGVyIG1vcmUgY2xlYW5seSBpbiB5b3VyIFJTdHVkaW8gd2luZG93Lg0KYGBge3J9DQptMSA8LSBsbShDRVNEMiB+IENFU0QxLCBkYXRhPWV4OF9kYXRhKQ0Kc3VtbWFyeShtMSkNCmBgYA0KDQo8YnIgLz4NClBsdWcgaW4gZXN0aW1hdGVzIHRvIHNwZWNpZnkgZXF1YXRpb24gZm9yIGxpbmUgb2YgYmVzdCBmaXQuIENvcHkgYW5kIHBhc3RlIHRoZSB2YWx1ZXMgd2Ugb2J0YWluZWQgYWJvdmUuIFRoZSBlc3RpbWF0ZSBvZiAqKEludGVyY2VwdCkqIGNvcnJlc3BvbmRzIHRvICphKiwgd2hlcmVhcyB0aGUgZXN0aW1hdGUgb2YgKkNFU0QxKiBjb3JyZXNwb25kcyB0byAqYiouIA0KJCQNClxoYXR7WX0gPSAyLjU3ICsgMC40NXgNCiQkDQoNCg0KPGJyIC8+DQpFeGFtaW5lIGNvbmZpZGVuY2UgaW50ZXJ2YWwgb2YgdGhlIGVzdGltYXRlcyB1c2luZyB0aGUgKipjb25maW50KCptb2RlbCBvYmplY3QqKSoqIGZ1bmN0aW9uLiANCmBgYHtyfQ0KY29uZmludChtMSkNCmBgYCANCg0KPGJyIC8+DQpNYW51YWxseSBhZGQgbGluZWFyIHJlZ3Jlc3Npb24gZml0IG9udG8gc2NhdHRlcnBsb3QuIFN0YXJ0IGJ5IHJldHJpZXZpbmcgdGhlIGVzdGltYXRlIG9mIHRoZSBpbnRlcmNlcHQgKGkuZS4sIHRoZSBmaXJzdCBjb2VmZmljaWVudCBpbiB0aGUgbW9kZWwpIHVzaW5nIHRoZSAqKmNvZWYoKm1vZGVsIG9iamVjdCopKiogZnVuY3Rpb24gYW5kIHNwZWNpZnlpbmcgdGhlIGZpcnN0IGVsZW1lbnQgKipbMV0qKi4gTmV4dCByZXRyaWV2ZSB0aGUgZXN0aW1hdGUgb2YgdGhlIHNsb3BlIHVzaW5nIHRoZSBzYW1lIGZ1bmN0aW9uIGJ1dCBpbnN0ZWFkIGluZGV4aW5nIHRoZSBzZWNvbmQgZWxlbWVudCAqKlsyXSoqLiBOb3cgd2UgYWRkIGEgbGF5ZXIgdG8gY3JlYXRlIGEgbWFudWFsIGxpbmUgd2hlcmUgd2Ugc3BlY2lmeSBvdXIgc2xvcGUgYW5kIGludGVyY2VwdCB1c2luZyAqKmdlb21fYWJsaW5lKGludGVyY2VwdCA9ICppbnRlcmNlcHQgZXN0aW1hdGUqLCBzbG9wZSA9ICpzbG9wZSBlc3RpbWF0ZSopKiouIExldCdzIGFsc28gY29sb3IgaXQgKmJsdWUqIHNvIHdlIGNhbiBkaXN0aW5ndWlzaCBpdCBmcm9tIHRoZSBibGFjayBkYXRhIHBvaW50cy4gDQpgYGB7cn0NCm0xX2ludGVyY2VwdCA8LSBjb2VmKG0xKVsxXQ0KbTFfc2xvcGUgPC0gY29lZihtMSlbMl0NCg0KZ2dwbG90KGV4OF9kYXRhLCBhZXMoQ0VTRDEsIENFU0QyKSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IG0xX2ludGVyY2VwdCwgc2xvcGUgPSBtMV9zbG9wZSwgY29sb3I9ImJsdWUiKQ0KDQpgYGANCg0KPGJyIC8+DQpBdXRvbWF0aWNhbGx5IGFkZCBsaW5lYXIgcmVncmVzc2lvbiBmaXQgKHcvIDk1JSBDSSkgb250byBzY2F0dGVycGxvdCB1c2luZyAqKmdlb21fc21vb3RoKCkqKiBidXQgdGhpcyB0aW1lIHdlIHNwZWNpZnkgdGhhdCB0aGUgKm1ldGhvZCogYXJndW1lbnQgd2lsbCBiZSBhIGxpbmVhciBtb2RlbCAoKmxtKiksIHJhdGhlciB0aGFuIGEgTE9FU1MgbGluZSAoZGVmYXVsdCkuIA0KYGBge3J9DQpnZ3Bsb3QoZXg4X2RhdGEsIGFlcyhDRVNEMSwgQ0VTRDIpKSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtKQ0KDQpgYGANCg0KPGJyIC8+DQpSZW1lbWJlciB0byBhZGQgdGl0bGUgYW5kIGF4ZXMgdG8geW91ciBwbG90cyB1c2luZyAqKnhsYWIoKSoqLCAqKnlsYWIoKSoqLCBhbmQgKipnZ3RpdGxlKCkqKi4gDQpgYGB7cn0NCg0KZ2dwbG90KGV4OF9kYXRhLCBhZXMoQ0VTRDEsIENFU0QyKSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZD1sbSkgKyANCiAgeGxhYigiQ0VTRCBhdCBUaW1lIDEiKSArIA0KICB5bGFiKCJDRVNEIGF0IFRpbWUgMiIpICsgDQogIGdndGl0bGUoIkNFU0QgYXQgVGltZSAyIGFzIGEgZnVuY3Rpb24gb2YgQ0VTRCBhdCBUaW1lIDEiKQ0KDQpgYGANCg0KDQojIyMgQk9OVVM6IEFkZGluZyBFcXVhdGlvbg0KDQpXZSBjYW4gYWxzbyBzdXBlcmltcG9zZSB0aGUgcmVncmVzc2lvbiBsaW5lIHVzaW5nIHRoZSAqKnN0YXRfcG9seV9lcSgpKiogZnVuY3Rpb24uDQpgYGB7cn0NCg0KZ2dwbG90KGV4OF9kYXRhLCBhZXMoQ0VTRDEsIENFU0QyKSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZD1sbSkgKw0KICB4bGFiKCJDRVNEIGF0IFRpbWUgMSIpICsgDQogIHlsYWIoIkNFU0QgYXQgVGltZSAyIikgKyANCiAgZ2d0aXRsZSgiQ0VTRCBhdCBUaW1lIDIgYXMgYSBmdW5jdGlvbiBvZiBDRVNEIGF0IFRpbWUgMSIpICsNCiAgc3RhdF9wb2x5X2VxKGFlcyhsYWJlbCA9ICBwYXN0ZSguLmVxLmxhYmVsLi4sIC4uYWRqLnJyLmxhYmVsLi4sIHNlcCA9ICJ+fn5+IikpLA0KICAgICAgICAgICAgICAgZm9ybXVsYSA9IHl+eCwgcGFyc2UgPSBUUlVFLCBsYWJlbC54Lm5wYyA9IC41LCBjb2xvcj0iYmx1ZSIpDQpgYGANCg0KIyMjIEJPTlVTOiBNYXJnaW5hbCBIaXN0b2dyYW1zDQoNCldlIGNhbiBhbHNvIGFkZCBoaXN0b2dyYW1zIHRvIGNvbnZleSBtYXJnaW5hbCBkaXN0cmlidXRpb25zDQpgYGB7cn0NCnAxIDwtIGdncGxvdChleDhfZGF0YSwgYWVzKENFU0QxLCBDRVNEMikpICsgDQogIGdlb21fcG9pbnQoKSArIA0KICBnZW9tX3Ntb290aChtZXRob2Q9bG0pICsNCiAgeGxhYigiQ0VTRCBhdCBUaW1lIDEiKSArIA0KICB5bGFiKCJDRVNEIGF0IFRpbWUgMiIpICsgDQogIHN0YXRfcG9seV9lcShhZXMobGFiZWwgPSAgcGFzdGUoLi5lcS5sYWJlbC4uLCAuLmFkai5yci5sYWJlbC4uLCBzZXAgPSAifn5+fiIpKSwNCiAgICAgICAgICAgICAgIGZvcm11bGEgPSB5fngsIHBhcnNlID0gVFJVRSwgbGFiZWwueC5ucGMgPSAuNSwgY29sb3I9ImJsdWUiKSArIA0KICANCiAgIyBSZW1vdmUgZ3JpZGxpbmVzIA0KICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikgKw0KICB0aGVtZShheGlzLnRleHQueCAgICAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICAgICAgYXhpcy50aXRsZS55ICAgID0gZWxlbWVudF90ZXh0KHZqdXN0ID0gKzEuNSksDQogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yICA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy5saW5lICA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siKSkNCg0KZ2dNYXJnaW5hbCgNCiAgcCA9IHAxLA0KICB0eXBlID0gJ2hpc3RvZ3JhbScsDQogIGJpbnMgPSAyMCwNCiAgbWFyZ2lucyA9ICdib3RoJywNCiAgc2l6ZSA9IDIsDQogIGNvbCA9ICdibGFjaycsDQogIGZpbGw9ImdyZXkiDQopDQpgYGANCg0K