suppressPackageStartupMessages(library("tidyverse"))
package 㤼㸱tidyverse㤼㸲 was built under R version 3.6.3
suppressPackageStartupMessages(library("modelr"))
package 㤼㸱modelr㤼㸲 was built under R version 3.6.3
1. What happens if you repeat the analysis of sim2
using a model without an intercept. What happens to the model equation? What happens to the predictions?
To run a model without an intercept, add - 1
or + 0
to the right-hand-side of the formula:
mod2a <- lm(y ~ x - 1, data = sim2)
mod2 <- lm(y ~ x, data = sim2)
The predictions are exactly the same in the models with and without an intercept:
grid <- sim2 %>%
data_grid(x) %>%
spread_predictions(mod2, mod2a)
grid
2. Use model_matrix()
to explore the equations generated for the models I fit to sim3
and sim4
. Why is *
a good shorthand for interaction?
For x1 * x2
when x2
is a categorical variable produces indicator variables x2b
, x2c
, x2d
and variables x1:x2b
, x1:x2c
, and x1:x2d
which are the products of x1
and x2*
variables:
x3 <- model_matrix(y ~ x1 * x2, data = sim3)
x3
We can confirm that the variables x1:x2b is the product of x1
and x2b
,
all(x3[["x1:x2b"]] == (x3[["x1"]] * x3[["x2b"]]))
[1] TRUE
and similarly for x1:x2c
and x2c
, and x1:x2d
and x2d
:
all(x3[["x1:x2c"]] == (x3[["x1"]] * x3[["x2c"]]))
[1] TRUE
all(x3[["x1:x2d"]] == (x3[["x1"]] * x3[["x2d"]]))
[1] TRUE
For x1 * x2
where both x1
and x2
are continuous variables, model_matrix()
creates variables x1
, x2
, and x1:x2
:
x4 <- model_matrix(y ~ x1 * x2, data = sim4)
x4
Confirm that x1:x2
is the product of the x1
and x2
,
all(x4[["x1"]] * x4[["x2"]] == x4[["x1:x2"]])
[1] TRUE
The asterisk *
is good shorthand for an interaction since an interaction between x1
and x2
includes terms for x1
, x2
, and the product of x1
and x2
.
3. Using the basic principles, convert the formulas in the following two models into functions. (Hint: start by converting the categorical variable into 0-1 variables.)
mod1 <- lm(y ~ x1 + x2, data = sim3)
mod2 <- lm(y ~ x1 * x2, data = sim3)
The problem is to convert the formulas in the models into functions. I will assume that the function is only handling the conversion of the right hand side of the formula into a model matrix. The functions will take one argument, a data frame with x1
and x2
columns, and it will return a data frame. In other words, the functions will be special cases of the model_matrix()
function.
Consider the right hand side of the first formula, ~ x1 + x2
. In the sim3 data frame, the column x1
is an integer, and the variable x2
is a factor with four levels.
levels(sim3$x2)
[1] "a" "b" "c" "d"
Since x1
is numeric it is unchanged. Since x2
is a factor it is replaced with columns of indicator variables for all but one of its levels. I will first consider the special case in which x2
only takes the levels of x2
in sim3. In this case, “a” is considered the reference level and omitted, and new columns are made for “b”, “c”, and “d”.
model_matrix_mod1 <- function(.data) {
mutate(.data,
x2b = as.numeric(x2 == "b"),
x2c = as.numeric(x2 == "c"),
x2d = as.numeric(x2 == "d"),
`(Intercept)` = 1
) %>%
select(`(Intercept)`, x1, x2b, x2c, x2d)
}
model_matrix_mod1(sim3)
A more general function for ~ x1 + x2
would not hard-code the specific levels in x2
.
model_matrix_mod1b <- function(.data) {
# the levels of x2
lvls <- levels(.data$x2)
# drop the first level
# this assumes that there are at least two levels
lvls <- lvls[2:length(lvls)]
# create an indicator variable for each level of x2
for (lvl in lvls) {
# new column name x2 + level name
varname <- str_c("x2", lvl)
# add indicator variable for lvl
.data[[varname]] <- as.numeric(.data$x2 == lvl)
}
# generate the list of variables to keep
x2_variables <- str_c("x2", lvls)
# Add an intercept
.data[["(Intercept)"]] <- 1
# keep x1 and x2 indicator variables
select(.data, `(Intercept)`, x1, one_of(x2_variables))
}
model_matrix_mod1b(sim3)
Consider the right hand side of the first formula, ~ x1 * x2
. The output data frame will consist of x1
, columns with indicator variables for each level (except the reference level) of x2
, and columns with the x2
indicator variables multiplied by x1
.
As with the previous formula, first I’ll write a function that hard-codes the levels of x2
.
model_matrix_mod2 <- function(.data) {
mutate(.data,
`(Intercept)` = 1,
x2b = as.numeric(x2 == "b"),
x2c = as.numeric(x2 == "c"),
x2d = as.numeric(x2 == "d"),
`x1:x2b` = x1 * x2b,
`x1:x2c` = x1 * x2c,
`x1:x2d` = x1 * x2d
) %>%
select(`(Intercept)`, x1, x2b, x2c, x2d, `x1:x2b`, `x1:x2c`, `x1:x2d`)
}
model_matrix_mod2(sim3)
For a more general function which will handle arbitrary levels in x2
, I will extend the model_matrix_mod1b()
function that I wrote earlier.
model_matrix_mod2b <- function(.data) {
# get dataset with x1 and x2 indicator variables
out <- model_matrix_mod1b(.data)
# get names of the x2 indicator columns
x2cols <- str_subset(colnames(out), "^x2")
# create interactions between x1 and the x2 indicator columns
for (varname in x2cols) {
# name of the interaction variable
newvar <- str_c("x1:", varname)
out[[newvar]] <- out$x1 * out[[varname]]
}
out
}
model_matrix_mod2b(sim3)
These functions could be further generalized to allow for x1
and x2
to be either numeric or factors. However, generalizing much more than that and we will soon start reimplementing all of the matrix_model()
function.
4. For sim4
, which of mod1
and mod2
is better? I think mod2
does a slightly better job at removing patterns, but it’s pretty subtle. Can you come up with a plot to support my claim?
Estimate models mod1
and mod2
on sim4
,
mod1 <- lm(y ~ x1 + x2, data = sim4)
mod2 <- lm(y ~ x1 * x2, data = sim4)
and add the residuals from these models to the sim4
data,
sim4_mods <- gather_residuals(sim4, mod1, mod2)
Frequency plots of both the residuals,
ggplot(sim4_mods, aes(x = resid, colour = model)) +
geom_freqpoly(binwidth = 0.5) +
geom_rug()

and the absolute values of the residuals,
ggplot(sim4_mods, aes(x = abs(resid), colour = model)) +
geom_freqpoly(binwidth = 0.5) +
geom_rug()

does not show much difference in the residuals between the models. However, mod2
appears to have fewer residuals in the tails of the distribution between 2.5 and 5 (although the most extreme residuals are from mod2
.
This is confirmed by checking the standard deviation of the residuals of these models,
sim4_mods %>%
group_by(model) %>%
summarise(resid = sd(resid))
The standard deviation of the residuals of mod2
is smaller than that of mod1
.
LS0tDQp0aXRsZTogIkZvcm11bGFzIGFuZCBtb2RlbCBmYW1pbGllcyINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQotLS0NCg0KYGBge3J9DQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSgidGlkeXZlcnNlIikpDQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSgibW9kZWxyIikpDQpgYGANCg0KIyMjIDEuIFdoYXQgaGFwcGVucyBpZiB5b3UgcmVwZWF0IHRoZSBhbmFseXNpcyBvZiBgc2ltMmAgdXNpbmcgYSBtb2RlbCB3aXRob3V0IGFuIGludGVyY2VwdC4gV2hhdCBoYXBwZW5zIHRvIHRoZSBtb2RlbCBlcXVhdGlvbj8gV2hhdCBoYXBwZW5zIHRvIHRoZSBwcmVkaWN0aW9ucz8NCg0KVG8gcnVuIGEgbW9kZWwgd2l0aG91dCBhbiBpbnRlcmNlcHQsIGFkZCBgLSAxYCBvciBgKyAwYCB0byB0aGUgcmlnaHQtaGFuZC1zaWRlIG9mIHRoZSBmb3JtdWxhOg0KDQpgYGB7cn0NCm1vZDJhIDwtIGxtKHkgfiB4IC0gMSwgZGF0YSA9IHNpbTIpDQptb2QyIDwtIGxtKHkgfiB4LCBkYXRhID0gc2ltMikNCmBgYA0KDQpUaGUgcHJlZGljdGlvbnMgYXJlIGV4YWN0bHkgdGhlIHNhbWUgaW4gdGhlIG1vZGVscyB3aXRoIGFuZCB3aXRob3V0IGFuIGludGVyY2VwdDoNCg0KYGBge3J9DQpncmlkIDwtIHNpbTIgJT4lDQogIGRhdGFfZ3JpZCh4KSAlPiUNCiAgc3ByZWFkX3ByZWRpY3Rpb25zKG1vZDIsIG1vZDJhKQ0KZ3JpZA0KYGBgDQoNCiMjIyAyLiBVc2UgYG1vZGVsX21hdHJpeCgpYCB0byBleHBsb3JlIHRoZSBlcXVhdGlvbnMgZ2VuZXJhdGVkIGZvciB0aGUgbW9kZWxzIEkgZml0IHRvIGBzaW0zYCBhbmQgYHNpbTRgLiBXaHkgaXMgYCpgIGEgZ29vZCBzaG9ydGhhbmQgZm9yIGludGVyYWN0aW9uPw0KDQpGb3IgYHgxICogeDJgIHdoZW4gYHgyYCBpcyBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIHByb2R1Y2VzIGluZGljYXRvciB2YXJpYWJsZXMgYHgyYmAsIGB4MmNgLCBgeDJkYCBhbmQgdmFyaWFibGVzIGB4MTp4MmJgLCBgeDE6eDJjYCwgYW5kIGB4MTp4MmRgIHdoaWNoIGFyZSB0aGUgcHJvZHVjdHMgb2YgYHgxYCBhbmQgYHgyKmAgdmFyaWFibGVzOg0KDQpgYGB7cn0NCngzIDwtIG1vZGVsX21hdHJpeCh5IH4geDEgKiB4MiwgZGF0YSA9IHNpbTMpDQp4Mw0KYGBgDQoNCldlIGNhbiBjb25maXJtIHRoYXQgdGhlIHZhcmlhYmxlcyB4MTp4MmIgaXMgdGhlIHByb2R1Y3Qgb2YgYHgxYCBhbmQgYHgyYmAsDQoNCmBgYHtyfQ0KYWxsKHgzW1sieDE6eDJiIl1dID09ICh4M1tbIngxIl1dICogeDNbWyJ4MmIiXV0pKQ0KYGBgDQoNCmFuZCBzaW1pbGFybHkgZm9yIGB4MTp4MmNgIGFuZCBgeDJjYCwgYW5kIGB4MTp4MmRgIGFuZCBgeDJkYDoNCg0KYGBge3J9DQphbGwoeDNbWyJ4MTp4MmMiXV0gPT0gKHgzW1sieDEiXV0gKiB4M1tbIngyYyJdXSkpDQphbGwoeDNbWyJ4MTp4MmQiXV0gPT0gKHgzW1sieDEiXV0gKiB4M1tbIngyZCJdXSkpDQpgYGANCg0KRm9yIGB4MSAqIHgyYCB3aGVyZSBib3RoIGB4MWAgYW5kIGB4MmAgYXJlIGNvbnRpbnVvdXMgdmFyaWFibGVzLCBgbW9kZWxfbWF0cml4KClgIGNyZWF0ZXMgdmFyaWFibGVzIGB4MWAsIGB4MmAsIGFuZCBgeDE6eDJgOg0KDQpgYGB7cn0NCng0IDwtIG1vZGVsX21hdHJpeCh5IH4geDEgKiB4MiwgZGF0YSA9IHNpbTQpDQp4NA0KYGBgDQoNCkNvbmZpcm0gdGhhdCBgeDE6eDJgIGlzIHRoZSBwcm9kdWN0IG9mIHRoZSBgeDFgIGFuZCBgeDJgLA0KDQpgYGB7cn0NCmFsbCh4NFtbIngxIl1dICogeDRbWyJ4MiJdXSA9PSB4NFtbIngxOngyIl1dKQ0KYGBgDQoNClRoZSBhc3RlcmlzayBgKmAgaXMgZ29vZCBzaG9ydGhhbmQgZm9yIGFuIGludGVyYWN0aW9uIHNpbmNlIGFuIGludGVyYWN0aW9uIGJldHdlZW4gYHgxYCBhbmQgYHgyYCBpbmNsdWRlcyB0ZXJtcyBmb3IgYHgxYCwgYHgyYCwgYW5kIHRoZSBwcm9kdWN0IG9mIGB4MWAgYW5kIGB4MmAuDQoNCiMjIyAzLiBVc2luZyB0aGUgYmFzaWMgcHJpbmNpcGxlcywgY29udmVydCB0aGUgZm9ybXVsYXMgaW4gdGhlIGZvbGxvd2luZyB0d28gbW9kZWxzIGludG8gZnVuY3Rpb25zLiAoSGludDogc3RhcnQgYnkgY29udmVydGluZyB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGUgaW50byAwLTEgdmFyaWFibGVzLikNCg0KYGBge3J9DQptb2QxIDwtIGxtKHkgfiB4MSArIHgyLCBkYXRhID0gc2ltMykNCm1vZDIgPC0gbG0oeSB+IHgxICogeDIsIGRhdGEgPSBzaW0zKQ0KYGBgDQoNClRoZSBwcm9ibGVtIGlzIHRvIGNvbnZlcnQgdGhlIGZvcm11bGFzIGluIHRoZSBtb2RlbHMgaW50byBmdW5jdGlvbnMuIEkgd2lsbCBhc3N1bWUgdGhhdCB0aGUgZnVuY3Rpb24gaXMgb25seSBoYW5kbGluZyB0aGUgY29udmVyc2lvbiBvZiB0aGUgcmlnaHQgaGFuZCBzaWRlIG9mIHRoZSBmb3JtdWxhIGludG8gYSBtb2RlbCBtYXRyaXguIFRoZSBmdW5jdGlvbnMgd2lsbCB0YWtlIG9uZSBhcmd1bWVudCwgYSBkYXRhIGZyYW1lIHdpdGggYHgxYCBhbmQgYHgyYCBjb2x1bW5zLCBhbmQgaXQgd2lsbCByZXR1cm4gYSBkYXRhIGZyYW1lLiBJbiBvdGhlciB3b3JkcywgdGhlIGZ1bmN0aW9ucyB3aWxsIGJlIHNwZWNpYWwgY2FzZXMgb2YgdGhlIGBtb2RlbF9tYXRyaXgoKWAgZnVuY3Rpb24uDQoNCkNvbnNpZGVyIHRoZSByaWdodCBoYW5kIHNpZGUgb2YgdGhlIGZpcnN0IGZvcm11bGEsIGB+IHgxICsgeDJgLiBJbiB0aGUgc2ltMyBkYXRhIGZyYW1lLCB0aGUgY29sdW1uIGB4MWAgaXMgYW4gaW50ZWdlciwgYW5kIHRoZSB2YXJpYWJsZSBgeDJgIGlzIGEgZmFjdG9yIHdpdGggZm91ciBsZXZlbHMuDQoNCmBgYHtyfQ0KbGV2ZWxzKHNpbTMkeDIpDQpgYGANCg0KU2luY2UgYHgxYCBpcyBudW1lcmljIGl0IGlzIHVuY2hhbmdlZC4gU2luY2UgYHgyYCBpcyBhIGZhY3RvciBpdCBpcyByZXBsYWNlZCB3aXRoIGNvbHVtbnMgb2YgaW5kaWNhdG9yIHZhcmlhYmxlcyBmb3IgYWxsIGJ1dCBvbmUgb2YgaXRzIGxldmVscy4gSSB3aWxsIGZpcnN0IGNvbnNpZGVyIHRoZSBzcGVjaWFsIGNhc2UgaW4gd2hpY2ggYHgyYCBvbmx5IHRha2VzIHRoZSBsZXZlbHMgb2YgYHgyYCBpbiBzaW0zLiBJbiB0aGlzIGNhc2UsIOKAnGHigJ0gaXMgY29uc2lkZXJlZCB0aGUgcmVmZXJlbmNlIGxldmVsIGFuZCBvbWl0dGVkLCBhbmQgbmV3IGNvbHVtbnMgYXJlIG1hZGUgZm9yIOKAnGLigJ0sIOKAnGPigJ0sIGFuZCDigJxk4oCdLg0KDQpgYGB7cn0NCm1vZGVsX21hdHJpeF9tb2QxIDwtIGZ1bmN0aW9uKC5kYXRhKSB7DQogIG11dGF0ZSguZGF0YSwNCiAgICB4MmIgPSBhcy5udW1lcmljKHgyID09ICJiIiksDQogICAgeDJjID0gYXMubnVtZXJpYyh4MiA9PSAiYyIpLA0KICAgIHgyZCA9IGFzLm51bWVyaWMoeDIgPT0gImQiKSwNCiAgICBgKEludGVyY2VwdClgID0gMQ0KICApICU+JQ0KICAgIHNlbGVjdChgKEludGVyY2VwdClgLCB4MSwgeDJiLCB4MmMsIHgyZCkNCn0NCm1vZGVsX21hdHJpeF9tb2QxKHNpbTMpDQpgYGANCg0KQSBtb3JlIGdlbmVyYWwgZnVuY3Rpb24gZm9yIGB+IHgxICsgeDJgIHdvdWxkIG5vdCBoYXJkLWNvZGUgdGhlIHNwZWNpZmljIGxldmVscyBpbiBgeDJgLg0KDQpgYGB7cn0NCm1vZGVsX21hdHJpeF9tb2QxYiA8LSBmdW5jdGlvbiguZGF0YSkgew0KICAjIHRoZSBsZXZlbHMgb2YgeDINCiAgbHZscyA8LSBsZXZlbHMoLmRhdGEkeDIpDQogICMgZHJvcCB0aGUgZmlyc3QgbGV2ZWwNCiAgIyB0aGlzIGFzc3VtZXMgdGhhdCB0aGVyZSBhcmUgYXQgbGVhc3QgdHdvIGxldmVscw0KICBsdmxzIDwtIGx2bHNbMjpsZW5ndGgobHZscyldDQogICMgY3JlYXRlIGFuIGluZGljYXRvciB2YXJpYWJsZSBmb3IgZWFjaCBsZXZlbCBvZiB4Mg0KICBmb3IgKGx2bCBpbiBsdmxzKSB7DQogICAgIyBuZXcgY29sdW1uIG5hbWUgeDIgKyBsZXZlbCBuYW1lDQogICAgdmFybmFtZSA8LSBzdHJfYygieDIiLCBsdmwpDQogICAgIyBhZGQgaW5kaWNhdG9yIHZhcmlhYmxlIGZvciBsdmwNCiAgICAuZGF0YVtbdmFybmFtZV1dIDwtIGFzLm51bWVyaWMoLmRhdGEkeDIgPT0gbHZsKQ0KICB9DQogICMgZ2VuZXJhdGUgdGhlIGxpc3Qgb2YgdmFyaWFibGVzIHRvIGtlZXANCiAgeDJfdmFyaWFibGVzIDwtIHN0cl9jKCJ4MiIsIGx2bHMpDQogICMgQWRkIGFuIGludGVyY2VwdA0KICAuZGF0YVtbIihJbnRlcmNlcHQpIl1dIDwtIDENCiAgIyBrZWVwIHgxIGFuZCB4MiBpbmRpY2F0b3IgdmFyaWFibGVzDQogIHNlbGVjdCguZGF0YSwgYChJbnRlcmNlcHQpYCwgeDEsIG9uZV9vZih4Ml92YXJpYWJsZXMpKQ0KfQ0KbW9kZWxfbWF0cml4X21vZDFiKHNpbTMpDQpgYGANCg0KQ29uc2lkZXIgdGhlIHJpZ2h0IGhhbmQgc2lkZSBvZiB0aGUgZmlyc3QgZm9ybXVsYSwgYH4geDEgKiB4MmAuIFRoZSBvdXRwdXQgZGF0YSBmcmFtZSB3aWxsIGNvbnNpc3Qgb2YgYHgxYCwgY29sdW1ucyB3aXRoIGluZGljYXRvciB2YXJpYWJsZXMgZm9yIGVhY2ggbGV2ZWwgKGV4Y2VwdCB0aGUgcmVmZXJlbmNlIGxldmVsKSBvZiBgeDJgLCBhbmQgY29sdW1ucyB3aXRoIHRoZSBgeDJgIGluZGljYXRvciB2YXJpYWJsZXMgbXVsdGlwbGllZCBieSBgeDFgLg0KDQpBcyB3aXRoIHRoZSBwcmV2aW91cyBmb3JtdWxhLCBmaXJzdCBJ4oCZbGwgd3JpdGUgYSBmdW5jdGlvbiB0aGF0IGhhcmQtY29kZXMgdGhlIGxldmVscyBvZiBgeDJgLg0KDQpgYGB7cn0NCm1vZGVsX21hdHJpeF9tb2QyIDwtIGZ1bmN0aW9uKC5kYXRhKSB7DQogIG11dGF0ZSguZGF0YSwNCiAgICBgKEludGVyY2VwdClgID0gMSwNCiAgICB4MmIgPSBhcy5udW1lcmljKHgyID09ICJiIiksDQogICAgeDJjID0gYXMubnVtZXJpYyh4MiA9PSAiYyIpLA0KICAgIHgyZCA9IGFzLm51bWVyaWMoeDIgPT0gImQiKSwNCiAgICBgeDE6eDJiYCA9IHgxICogeDJiLA0KICAgIGB4MTp4MmNgID0geDEgKiB4MmMsDQogICAgYHgxOngyZGAgPSB4MSAqIHgyZA0KICApICU+JQ0KICAgIHNlbGVjdChgKEludGVyY2VwdClgLCB4MSwgeDJiLCB4MmMsIHgyZCwgYHgxOngyYmAsIGB4MTp4MmNgLCBgeDE6eDJkYCkNCn0NCm1vZGVsX21hdHJpeF9tb2QyKHNpbTMpDQpgYGANCg0KRm9yIGEgbW9yZSBnZW5lcmFsIGZ1bmN0aW9uIHdoaWNoIHdpbGwgaGFuZGxlIGFyYml0cmFyeSBsZXZlbHMgaW4gYHgyYCwgSSB3aWxsIGV4dGVuZCB0aGUgYG1vZGVsX21hdHJpeF9tb2QxYigpYCBmdW5jdGlvbiB0aGF0IEkgd3JvdGUgZWFybGllci4NCg0KYGBge3J9DQptb2RlbF9tYXRyaXhfbW9kMmIgPC0gZnVuY3Rpb24oLmRhdGEpIHsNCiAgIyBnZXQgZGF0YXNldCB3aXRoIHgxIGFuZCB4MiBpbmRpY2F0b3IgdmFyaWFibGVzDQogIG91dCA8LSBtb2RlbF9tYXRyaXhfbW9kMWIoLmRhdGEpDQogICMgZ2V0IG5hbWVzIG9mIHRoZSB4MiBpbmRpY2F0b3IgY29sdW1ucw0KICB4MmNvbHMgPC0gc3RyX3N1YnNldChjb2xuYW1lcyhvdXQpLCAiXngyIikNCiAgIyBjcmVhdGUgaW50ZXJhY3Rpb25zIGJldHdlZW4geDEgYW5kIHRoZSB4MiBpbmRpY2F0b3IgY29sdW1ucw0KICBmb3IgKHZhcm5hbWUgaW4geDJjb2xzKSB7DQogICAgIyBuYW1lIG9mIHRoZSBpbnRlcmFjdGlvbiB2YXJpYWJsZQ0KICAgIG5ld3ZhciA8LSBzdHJfYygieDE6IiwgdmFybmFtZSkNCiAgICBvdXRbW25ld3Zhcl1dIDwtIG91dCR4MSAqIG91dFtbdmFybmFtZV1dDQogIH0NCiAgb3V0DQp9DQptb2RlbF9tYXRyaXhfbW9kMmIoc2ltMykNCmBgYA0KDQpUaGVzZSBmdW5jdGlvbnMgY291bGQgYmUgZnVydGhlciBnZW5lcmFsaXplZCB0byBhbGxvdyBmb3IgYHgxYCBhbmQgYHgyYCB0byBiZSBlaXRoZXIgbnVtZXJpYyBvciBmYWN0b3JzLiBIb3dldmVyLCBnZW5lcmFsaXppbmcgbXVjaCBtb3JlIHRoYW4gdGhhdCBhbmQgd2Ugd2lsbCBzb29uIHN0YXJ0IHJlaW1wbGVtZW50aW5nIGFsbCBvZiB0aGUgYG1hdHJpeF9tb2RlbCgpYCBmdW5jdGlvbi4NCg0KIyMjIDQuIEZvciBgc2ltNGAsIHdoaWNoIG9mIGBtb2QxYCBhbmQgYG1vZDJgIGlzIGJldHRlcj8gSSB0aGluayBgbW9kMmAgZG9lcyBhIHNsaWdodGx5IGJldHRlciBqb2IgYXQgcmVtb3ZpbmcgcGF0dGVybnMsIGJ1dCBpdOKAmXMgcHJldHR5IHN1YnRsZS4gQ2FuIHlvdSBjb21lIHVwIHdpdGggYSBwbG90IHRvIHN1cHBvcnQgbXkgY2xhaW0/DQoNCkVzdGltYXRlIG1vZGVscyBgbW9kMWAgYW5kIGBtb2QyYCBvbiBgc2ltNGAsDQoNCmBgYHtyfQ0KbW9kMSA8LSBsbSh5IH4geDEgKyB4MiwgZGF0YSA9IHNpbTQpDQptb2QyIDwtIGxtKHkgfiB4MSAqIHgyLCBkYXRhID0gc2ltNCkNCmBgYA0KDQphbmQgYWRkIHRoZSByZXNpZHVhbHMgZnJvbSB0aGVzZSBtb2RlbHMgdG8gdGhlIGBzaW00YCBkYXRhLA0KDQpgYGB7cn0NCnNpbTRfbW9kcyA8LSBnYXRoZXJfcmVzaWR1YWxzKHNpbTQsIG1vZDEsIG1vZDIpDQpgYGANCg0KRnJlcXVlbmN5IHBsb3RzIG9mIGJvdGggdGhlIHJlc2lkdWFscywNCg0KYGBge3J9DQpnZ3Bsb3Qoc2ltNF9tb2RzLCBhZXMoeCA9IHJlc2lkLCBjb2xvdXIgPSBtb2RlbCkpICsNCiAgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDAuNSkgKw0KICBnZW9tX3J1ZygpDQpgYGANCg0KYW5kIHRoZSBhYnNvbHV0ZSB2YWx1ZXMgb2YgdGhlIHJlc2lkdWFscywNCg0KYGBge3J9DQpnZ3Bsb3Qoc2ltNF9tb2RzLCBhZXMoeCA9IGFicyhyZXNpZCksIGNvbG91ciA9IG1vZGVsKSkgKw0KICBnZW9tX2ZyZXFwb2x5KGJpbndpZHRoID0gMC41KSArDQogIGdlb21fcnVnKCkNCmBgYA0KDQpkb2VzIG5vdCBzaG93IG11Y2ggZGlmZmVyZW5jZSBpbiB0aGUgcmVzaWR1YWxzIGJldHdlZW4gdGhlIG1vZGVscy4gSG93ZXZlciwgYG1vZDJgIGFwcGVhcnMgdG8gaGF2ZSBmZXdlciByZXNpZHVhbHMgaW4gdGhlIHRhaWxzIG9mIHRoZSBkaXN0cmlidXRpb24gYmV0d2VlbiAyLjUgYW5kIDUgKGFsdGhvdWdoIHRoZSBtb3N0IGV4dHJlbWUgcmVzaWR1YWxzIGFyZSBmcm9tIGBtb2QyYC4NCg0KVGhpcyBpcyBjb25maXJtZWQgYnkgY2hlY2tpbmcgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgcmVzaWR1YWxzIG9mIHRoZXNlIG1vZGVscywNCg0KYGBge3J9DQpzaW00X21vZHMgJT4lDQogIGdyb3VwX2J5KG1vZGVsKSAlPiUNCiAgc3VtbWFyaXNlKHJlc2lkID0gc2QocmVzaWQpKQ0KYGBgDQoNClRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIHJlc2lkdWFscyBvZiBgbW9kMmAgaXMgc21hbGxlciB0aGFuIHRoYXQgb2YgYG1vZDFgLg==