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==