R Markdown

This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

plot(cars)

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

RStudio is able to simulate the final formatting live, by switching from “Source” to “Visual” in the task bar above.

Linear Regression

Description of data set

The file module1-video_reading.csv contains the following data:

  • participant: unique id for each participant

  • score_reading: number of points the participant scored in a reading test

  • hours_video: average number of hours the participant spends watching video stream (TV, movies, ..) each day.

Tasks

We first have to install all packages that we need for the following tasks

# only run (by uncommenting) if not already installed (comment out again after installation): 
#install.packages('tidyverse', dependencies = T)
#install.packages('mlr3verse', dependencies = T)
  1. Read the data file module1-video_reading.csv into R and assign it to a variable called “dat”.
dat <- read.csv("module1-video_reading.csv")
head(dat)
  1. Draw a scatter plot of “hours_video” and “score_reading”.
library(ggplot2)

scatter_plot <- ggplot(dat, aes(x=hours_video, y=score_reading)) + 
  geom_point() 
scatter_plot

  1. Estimate a linear regression model for “score_reading” as target (dependent variable) and “hours_video” as feature (independent/explanatory variable) using the lm() function.
mdl <- lm(score_reading ~ hours_video, data = dat)
summary(mdl)

Call:
lm(formula = score_reading ~ hours_video, data = dat)

Residuals:
    Min      1Q  Median      3Q     Max 
-18.853  -6.666   0.039   6.046  52.424 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  47.6583     1.6863  28.262   <2e-16 ***
hours_video  -0.1258     0.5089  -0.247    0.805    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 9.387 on 98 degrees of freedom
Multiple R-squared:  0.0006233, Adjusted R-squared:  -0.009574 
F-statistic: 0.06112 on 1 and 98 DF,  p-value: 0.8053
  1. Redo task 3 using the mlr3verse package. Does your final model output (applying the summary() function on the fitted model object) differ from the one in task 3, which was estimated using base R functionality?
library(mlr3verse)
tsk = as_task_regr(score_reading ~ hours_video, data = dat)
mdl = lrn("regr.lm")
mdl$train(tsk)
summary(mdl$model)

Call:
stats::lm(formula = task$formula(), data = task$data())

Residuals:
    Min      1Q  Median      3Q     Max 
-18.853  -6.666   0.039   6.046  52.424 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  47.6583     1.6863  28.262   <2e-16 ***
hours_video  -0.1258     0.5089  -0.247    0.805    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 9.387 on 98 degrees of freedom
Multiple R-squared:  0.0006233, Adjusted R-squared:  -0.009574 
F-statistic: 0.06112 on 1 and 98 DF,  p-value: 0.8053
  1. Add the regression line from task 4 to the plot from task 2.
scatter_plot <- scatter_plot + 
  geom_abline(aes(intercept = mdl$model$coefficients[['(Intercept)']], slope = mdl$model$coefficients[['hours_video']]), col = 'blue')
scatter_plot


# Alternative solution:
#scatter_plot <- ggplot(dat, aes(x=hours_video, y=score_reading)) + 
#  geom_point() +
#  geom_smooth(method='lm')
#scatter_plot
  1. Identify and exclude the outlier, and redo tasks 4 and 5 (i.e., estimate the model again and add the new line to the scatter plot).
dat2 <- dat[dat$participant != 70, ]

# mlr3verse:
task2 = as_task_regr(score_reading ~ hours_video, data = dat2)
mdl2 = lrn("regr.lm")
mdl2$train(task2)
summary(mdl2$model)

Call:
stats::lm(formula = task$formula(), data = task$data())

Residuals:
     Min       1Q   Median       3Q      Max 
-15.7277  -6.3708   0.6861   5.4776  13.3032 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  49.8564     1.3909  35.844  < 2e-16 ***
hours_video  -1.1383     0.4324  -2.632  0.00987 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 7.562 on 97 degrees of freedom
Multiple R-squared:  0.06667,   Adjusted R-squared:  0.05705 
F-statistic: 6.929 on 1 and 97 DF,  p-value: 0.009867
# base R:
#mdl2 <- lm(score_reading ~ hours_video, data = dat2)
#summary(mdl2)

# plotting:
scatter_plot <- scatter_plot + 
  geom_abline(aes(intercept = mdl2$model$coefficients[['(Intercept)']], slope = mdl2$model$coefficients[['hours_video']]), col = 'red')
scatter_plot

  1. What would be the reading score (“score_reading”) of a participant with a video consumption (“hours_video”) equivalent to the 95th percentile as predicted by the model? (Hint: you can use the predict_newdata() method on the fitted model object, which behaves similar to the predict() function in base R)
dat3 <- data.frame('hours_video' = quantile(dat2$hours_video, probs = 0.95))

# mlr3verse:
dat3$score_reading <- mdl2$predict_newdata(newdata = dat3)$response
dat3$score_reading
[1] 43.09493
# base R:
#dat3$score_reading <- predict(mdl2, newdata = dat3)
#dat3$score_reading
  1. Add the prediction from task 7 to the scatter plot from task 6.
scatter_plot <- scatter_plot +
  geom_vline(data = dat3, aes(xintercept = hours_video), lty = 2) +
  geom_point(data = dat3, color = 'red', size = 5, shape = 'cross')
scatter_plot

  1. Bonus: What is the effect of “hours_video” on “score_reading” in standardized units? How would you interpret this effect? (Hint: You can use a linear regression model to determine the correlation between “hours_video” and “score_reading”)
# z-standardize data to get standardized regression coefficients
dat2$score_reading_z <- scale(dat2$score_reading)
dat2$hours_video_z <- scale(dat2$hours_video)

# estimate binary regression model
## mlr3verse:
task_z = as_task_regr(score_reading_z ~ hours_video_z, data = dat2)
mdl_z = lrn("regr.lm")
mdl_z$train(task_z)
summary(mdl_z$model)

Call:
stats::lm(formula = task$formula(), data = task$data())

Residuals:
     Min       1Q   Median       3Q      Max 
-2.01967 -0.81810  0.08811  0.70341  1.70832 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)   
(Intercept)    4.566e-16  9.759e-02   0.000  1.00000   
hours_video_z -2.582e-01  9.809e-02  -2.632  0.00987 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.9711 on 97 degrees of freedom
Multiple R-squared:  0.06667,   Adjusted R-squared:  0.05705 
F-statistic: 6.929 on 1 and 97 DF,  p-value: 0.009867
## base R:
#mdl_z <- lm(score_reading_z ~ hours_video_z, data = dat2)
#summary(mdl_z)

# comparison to direct correlation testing
cor.test(dat2$hours_video, dat2$score_reading)

    Pearson's product-moment correlation

data:  dat2$hours_video and dat2$score_reading
t = -2.6323, df = 97, p-value = 0.009867
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 -0.4335232 -0.0640633
sample estimates:
       cor 
-0.2582096 
LS0tDQp0aXRsZTogIk1vZHVsZSAxOiBUdXRvcmlhbDogUmVncmVzc2lvbiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KIyBSIE1hcmtkb3duDQoNClRoaXMgaXMgYW4gW1IgTWFya2Rvd25dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pIE5vdGVib29rLiBXaGVuIHlvdSBleGVjdXRlIGNvZGUgd2l0aGluIHRoZSBub3RlYm9vaywgdGhlIHJlc3VsdHMgYXBwZWFyIGJlbmVhdGggdGhlIGNvZGUuDQoNClRyeSBleGVjdXRpbmcgdGhpcyBjaHVuayBieSBjbGlja2luZyB0aGUgKlJ1biogYnV0dG9uIHdpdGhpbiB0aGUgY2h1bmsgb3IgYnkgcGxhY2luZyB5b3VyIGN1cnNvciBpbnNpZGUgaXQgYW5kIHByZXNzaW5nICpDdHJsK1NoaWZ0K0VudGVyKi4NCg0KYGBge3J9DQpwbG90KGNhcnMpDQpgYGANCg0KQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLg0KDQpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkN0cmwrU2hpZnQrSyogdG8gcHJldmlldyB0aGUgSFRNTCBmaWxlKS4NCg0KVGhlIHByZXZpZXcgc2hvd3MgeW91IGEgcmVuZGVyZWQgSFRNTCBjb3B5IG9mIHRoZSBjb250ZW50cyBvZiB0aGUgZWRpdG9yLiBDb25zZXF1ZW50bHksIHVubGlrZSAqS25pdCosICpQcmV2aWV3KiBkb2VzIG5vdCBydW4gYW55IFIgY29kZSBjaHVua3MuIEluc3RlYWQsIHRoZSBvdXRwdXQgb2YgdGhlIGNodW5rIHdoZW4gaXQgd2FzIGxhc3QgcnVuIGluIHRoZSBlZGl0b3IgaXMgZGlzcGxheWVkLg0KDQpSU3R1ZGlvIGlzIGFibGUgdG8gKipzaW11bGF0ZSB0aGUgZmluYWwgZm9ybWF0dGluZyBsaXZlKiosIGJ5IHN3aXRjaGluZyBmcm9tICJTb3VyY2UiIHRvICJWaXN1YWwiIGluIHRoZSB0YXNrIGJhciBhYm92ZS4NCg0KIyBMaW5lYXIgUmVncmVzc2lvbg0KDQojIyBEZXNjcmlwdGlvbiBvZiBkYXRhIHNldA0KDQpUaGUgZmlsZSBtb2R1bGUxLXZpZGVvX3JlYWRpbmcuY3N2IGNvbnRhaW5zIHRoZSBmb2xsb3dpbmcgZGF0YToNCg0KLSAgIHBhcnRpY2lwYW50OiB1bmlxdWUgaWQgZm9yIGVhY2ggcGFydGljaXBhbnQNCg0KLSAgIHNjb3JlX3JlYWRpbmc6IG51bWJlciBvZiBwb2ludHMgdGhlIHBhcnRpY2lwYW50IHNjb3JlZCBpbiBhIHJlYWRpbmcgdGVzdA0KDQotICAgaG91cnNfdmlkZW86IGF2ZXJhZ2UgbnVtYmVyIG9mIGhvdXJzIHRoZSBwYXJ0aWNpcGFudCBzcGVuZHMgd2F0Y2hpbmcgdmlkZW8gc3RyZWFtIChUViwgbW92aWVzLCAuLikgZWFjaCBkYXkuDQoNCiMjIFRhc2tzDQoNCldlIGZpcnN0IGhhdmUgdG8gaW5zdGFsbCBhbGwgcGFja2FnZXMgdGhhdCB3ZSBuZWVkIGZvciB0aGUgZm9sbG93aW5nIHRhc2tzDQoNCmBgYHtyfQ0KIyBvbmx5IHJ1biAoYnkgdW5jb21tZW50aW5nKSBpZiBub3QgYWxyZWFkeSBpbnN0YWxsZWQgKGNvbW1lbnQgb3V0IGFnYWluIGFmdGVyIGluc3RhbGxhdGlvbik6IA0KI2luc3RhbGwucGFja2FnZXMoJ3RpZHl2ZXJzZScsIGRlcGVuZGVuY2llcyA9IFQpDQojaW5zdGFsbC5wYWNrYWdlcygnbWxyM3ZlcnNlJywgZGVwZW5kZW5jaWVzID0gVCkNCmBgYA0KDQoxLiAgUmVhZCB0aGUgZGF0YSBmaWxlIG1vZHVsZTEtdmlkZW9fcmVhZGluZy5jc3YgaW50byBSIGFuZCBhc3NpZ24gaXQgdG8gYSB2YXJpYWJsZSBjYWxsZWQgImRhdCIuDQoNCmBgYHtyfQ0KZGF0IDwtIHJlYWQuY3N2KCJtb2R1bGUxLXZpZGVvX3JlYWRpbmcuY3N2IikNCmhlYWQoZGF0KQ0KYGBgDQoNCjIuICBEcmF3IGEgc2NhdHRlciBwbG90IG9mICJob3Vyc192aWRlbyIgYW5kICJzY29yZV9yZWFkaW5nIi4NCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCnNjYXR0ZXJfcGxvdCA8LSBnZ3Bsb3QoZGF0LCBhZXMoeD1ob3Vyc192aWRlbywgeT1zY29yZV9yZWFkaW5nKSkgKyANCiAgZ2VvbV9wb2ludCgpIA0Kc2NhdHRlcl9wbG90DQpgYGANCg0KMy4gIEVzdGltYXRlIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgZm9yICJzY29yZV9yZWFkaW5nIiBhcyB0YXJnZXQgKGRlcGVuZGVudCB2YXJpYWJsZSkgYW5kICJob3Vyc192aWRlbyIgYXMgZmVhdHVyZSAoaW5kZXBlbmRlbnQvZXhwbGFuYXRvcnkgdmFyaWFibGUpIHVzaW5nIHRoZSBgbG0oKWAgZnVuY3Rpb24uDQoNCmBgYHtyfQ0KbWRsIDwtIGxtKHNjb3JlX3JlYWRpbmcgfiBob3Vyc192aWRlbywgZGF0YSA9IGRhdCkNCnN1bW1hcnkobWRsKQ0KYGBgDQoNCjQuICBSZWRvIHRhc2sgMyB1c2luZyB0aGUgYG1scjN2ZXJzZWAgcGFja2FnZS4gRG9lcyB5b3VyIGZpbmFsIG1vZGVsIG91dHB1dCAoYXBwbHlpbmcgdGhlIGBzdW1tYXJ5KClgIGZ1bmN0aW9uIG9uIHRoZSBmaXR0ZWQgbW9kZWwgb2JqZWN0KSBkaWZmZXIgZnJvbSB0aGUgb25lIGluIHRhc2sgMywgd2hpY2ggd2FzIGVzdGltYXRlZCB1c2luZyBgYmFzZWAgUiBmdW5jdGlvbmFsaXR5Pw0KDQpgYGB7cn0NCmxpYnJhcnkobWxyM3ZlcnNlKQ0KdHNrID0gYXNfdGFza19yZWdyKHNjb3JlX3JlYWRpbmcgfiBob3Vyc192aWRlbywgZGF0YSA9IGRhdCkNCm1kbCA9IGxybigicmVnci5sbSIpDQptZGwkdHJhaW4odHNrKQ0Kc3VtbWFyeShtZGwkbW9kZWwpDQpgYGANCg0KNS4gIEFkZCB0aGUgcmVncmVzc2lvbiBsaW5lIGZyb20gdGFzayA0IHRvIHRoZSBwbG90IGZyb20gdGFzayAyLg0KDQpgYGB7cn0NCnNjYXR0ZXJfcGxvdCA8LSBzY2F0dGVyX3Bsb3QgKyANCiAgZ2VvbV9hYmxpbmUoYWVzKGludGVyY2VwdCA9IG1kbCRtb2RlbCRjb2VmZmljaWVudHNbWycoSW50ZXJjZXB0KSddXSwgc2xvcGUgPSBtZGwkbW9kZWwkY29lZmZpY2llbnRzW1snaG91cnNfdmlkZW8nXV0pLCBjb2wgPSAnYmx1ZScpDQpzY2F0dGVyX3Bsb3QNCg0KIyBBbHRlcm5hdGl2ZSBzb2x1dGlvbjoNCiNzY2F0dGVyX3Bsb3QgPC0gZ2dwbG90KGRhdCwgYWVzKHg9aG91cnNfdmlkZW8sIHk9c2NvcmVfcmVhZGluZykpICsgDQojICBnZW9tX3BvaW50KCkgKw0KIyAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScpDQojc2NhdHRlcl9wbG90DQpgYGANCg0KNi4gIElkZW50aWZ5IGFuZCBleGNsdWRlIHRoZSBvdXRsaWVyLCBhbmQgcmVkbyB0YXNrcyA0IGFuZCA1IChpLmUuLCBlc3RpbWF0ZSB0aGUgbW9kZWwgYWdhaW4gYW5kIGFkZCB0aGUgbmV3IGxpbmUgdG8gdGhlIHNjYXR0ZXIgcGxvdCkuDQoNCmBgYHtyfQ0KZGF0MiA8LSBkYXRbZGF0JHBhcnRpY2lwYW50ICE9IDcwLCBdDQoNCiMgbWxyM3ZlcnNlOg0KdGFzazIgPSBhc190YXNrX3JlZ3Ioc2NvcmVfcmVhZGluZyB+IGhvdXJzX3ZpZGVvLCBkYXRhID0gZGF0MikNCm1kbDIgPSBscm4oInJlZ3IubG0iKQ0KbWRsMiR0cmFpbih0YXNrMikNCnN1bW1hcnkobWRsMiRtb2RlbCkNCg0KIyBiYXNlIFI6DQojbWRsMiA8LSBsbShzY29yZV9yZWFkaW5nIH4gaG91cnNfdmlkZW8sIGRhdGEgPSBkYXQyKQ0KI3N1bW1hcnkobWRsMikNCg0KIyBwbG90dGluZzoNCnNjYXR0ZXJfcGxvdCA8LSBzY2F0dGVyX3Bsb3QgKyANCiAgZ2VvbV9hYmxpbmUoYWVzKGludGVyY2VwdCA9IG1kbDIkbW9kZWwkY29lZmZpY2llbnRzW1snKEludGVyY2VwdCknXV0sIHNsb3BlID0gbWRsMiRtb2RlbCRjb2VmZmljaWVudHNbWydob3Vyc192aWRlbyddXSksIGNvbCA9ICdyZWQnKQ0Kc2NhdHRlcl9wbG90DQpgYGANCg0KNy4gIFdoYXQgd291bGQgYmUgdGhlIHJlYWRpbmcgc2NvcmUgKCJzY29yZV9yZWFkaW5nIikgb2YgYSBwYXJ0aWNpcGFudCB3aXRoIGEgdmlkZW8gY29uc3VtcHRpb24gKCJob3Vyc192aWRlbyIpIGVxdWl2YWxlbnQgdG8gdGhlIDk1dGggcGVyY2VudGlsZSBhcyBwcmVkaWN0ZWQgYnkgdGhlIG1vZGVsPyAoSGludDogeW91IGNhbiB1c2UgdGhlIGBwcmVkaWN0X25ld2RhdGEoKWAgbWV0aG9kIG9uIHRoZSBmaXR0ZWQgbW9kZWwgb2JqZWN0LCB3aGljaCBiZWhhdmVzIHNpbWlsYXIgdG8gdGhlIGBwcmVkaWN0KClgIGZ1bmN0aW9uIGluIGBiYXNlYCBSKQ0KDQpgYGB7cn0NCmRhdDMgPC0gZGF0YS5mcmFtZSgnaG91cnNfdmlkZW8nID0gcXVhbnRpbGUoZGF0MiRob3Vyc192aWRlbywgcHJvYnMgPSAwLjk1KSkNCg0KIyBtbHIzdmVyc2U6DQpkYXQzJHNjb3JlX3JlYWRpbmcgPC0gbWRsMiRwcmVkaWN0X25ld2RhdGEobmV3ZGF0YSA9IGRhdDMpJHJlc3BvbnNlDQpkYXQzJHNjb3JlX3JlYWRpbmcNCg0KIyBiYXNlIFI6DQojZGF0MyRzY29yZV9yZWFkaW5nIDwtIHByZWRpY3QobWRsMiwgbmV3ZGF0YSA9IGRhdDMpDQojZGF0MyRzY29yZV9yZWFkaW5nDQpgYGANCg0KOC4gIEFkZCB0aGUgcHJlZGljdGlvbiBmcm9tIHRhc2sgNyB0byB0aGUgc2NhdHRlciBwbG90IGZyb20gdGFzayA2Lg0KDQpgYGB7cn0NCnNjYXR0ZXJfcGxvdCA8LSBzY2F0dGVyX3Bsb3QgKw0KICBnZW9tX3ZsaW5lKGRhdGEgPSBkYXQzLCBhZXMoeGludGVyY2VwdCA9IGhvdXJzX3ZpZGVvKSwgbHR5ID0gMikgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBkYXQzLCBjb2xvciA9ICdyZWQnLCBzaXplID0gNSwgc2hhcGUgPSAnY3Jvc3MnKQ0Kc2NhdHRlcl9wbG90DQpgYGANCg0KOS4gIEJvbnVzOiBXaGF0IGlzIHRoZSBlZmZlY3Qgb2YgImhvdXJzX3ZpZGVvIiBvbiAic2NvcmVfcmVhZGluZyIgaW4gc3RhbmRhcmRpemVkIHVuaXRzPyBIb3cgd291bGQgeW91IGludGVycHJldCB0aGlzIGVmZmVjdD8gKEhpbnQ6IFlvdSBjYW4gdXNlIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgdG8gZGV0ZXJtaW5lIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuICJob3Vyc192aWRlbyIgYW5kICJzY29yZV9yZWFkaW5nIikNCg0KYGBge3J9DQojIHotc3RhbmRhcmRpemUgZGF0YSB0byBnZXQgc3RhbmRhcmRpemVkIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzDQpkYXQyJHNjb3JlX3JlYWRpbmdfeiA8LSBzY2FsZShkYXQyJHNjb3JlX3JlYWRpbmcpDQpkYXQyJGhvdXJzX3ZpZGVvX3ogPC0gc2NhbGUoZGF0MiRob3Vyc192aWRlbykNCg0KIyBlc3RpbWF0ZSBiaW5hcnkgcmVncmVzc2lvbiBtb2RlbA0KIyMgbWxyM3ZlcnNlOg0KdGFza196ID0gYXNfdGFza19yZWdyKHNjb3JlX3JlYWRpbmdfeiB+IGhvdXJzX3ZpZGVvX3osIGRhdGEgPSBkYXQyKQ0KbWRsX3ogPSBscm4oInJlZ3IubG0iKQ0KbWRsX3okdHJhaW4odGFza196KQ0Kc3VtbWFyeShtZGxfeiRtb2RlbCkNCg0KIyMgYmFzZSBSOg0KI21kbF96IDwtIGxtKHNjb3JlX3JlYWRpbmdfeiB+IGhvdXJzX3ZpZGVvX3osIGRhdGEgPSBkYXQyKQ0KI3N1bW1hcnkobWRsX3opDQoNCiMgY29tcGFyaXNvbiB0byBkaXJlY3QgY29ycmVsYXRpb24gdGVzdGluZw0KY29yLnRlc3QoZGF0MiRob3Vyc192aWRlbywgZGF0MiRzY29yZV9yZWFkaW5nKQ0KYGBgDQo=