1 Linear regression

Let’s say that we have \(n=10\) observations for a predictor (explanatory) variable \(x\) and a dependent variable \(y\):

set.seed(123)
(x <- round(rnorm(10, mean = 0, sd = 1), digits = 2))
##  [1] -0.56 -0.23  1.56  0.07  0.13  1.72  0.46 -1.27 -0.69 -0.45
(y <- 2 + 3 * x + rnorm(10, mean = 0, sd = 1))
##  [1]  1.5440818  1.6698138  7.0807715  2.3206827  1.8341589  8.9469131
##  [7]  3.8778505 -3.7766172  0.6313559  0.1772086
ggplot(data.frame(x = x, y = y), 
       aes(x = x, y = y)) + 
    geom_point(size = 4) + theme_minimal()

Linear regression of \(y\) on \(x\) would imply using the model \[y_i = \beta_0 + \beta_1\cdot x_i + \varepsilon_i\] for \(i=1, ..., n\), and where \(\beta_0\) (intercept) and \(\beta_1\) (slope) are parameters that are unknown and will be estimated from the data. In other words, \[y_1 = \beta_0 + \beta_1\cdot x_1 + \varepsilon_1\\y_2 = \beta_0 + \beta_1\cdot x_2 + \varepsilon_2\\...\\y_n = \beta_0 + \beta_1\cdot x_n + \varepsilon_n\]

We can write this in matrix form as follows: \[\begin{pmatrix}y_1\\y_2\\...\\y_n\end{pmatrix} = \begin{pmatrix}1 & x_1\\1 & x_2\\... & ...\\1 & x_n\end{pmatrix}\begin{pmatrix}\beta_0\\\beta_1\end{pmatrix} + \begin{pmatrix}\varepsilon_1\\\varepsilon_2\\...\\\varepsilon_n\end{pmatrix}\]

The first matrix to the right of the equal sign is the design matrix.

In R, we would express this model in formula notation as y ~ x. This will by default include an intercept (unless we explicitly tell it not to), and since x is a continuous variable, it will include a single coefficient for it (what we have referred to as \(\beta_1\) above). The design matrix for a given model can be created using the model.matrix() function in R (note that this does not require knowledge about the response variable):

model.matrix(~ x)
##    (Intercept)     x
## 1            1 -0.56
## 2            1 -0.23
## 3            1  1.56
## 4            1  0.07
## 5            1  0.13
## 6            1  1.72
## 7            1  0.46
## 8            1 -1.27
## 9            1 -0.69
## 10           1 -0.45
## attr(,"assign")
## [1] 0 1

We can also fit a linear model in R, which will provide estimates for the coefficients \(\beta_0\) and \(\beta_1\):

summary(lm(y ~ x))
## 
## Call:
## lm(formula = y ~ x)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.33130 -0.64381 -0.02429  0.49406  1.41357 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   2.1622     0.2850   7.587 6.38e-05 ***
## x             3.6279     0.3131  11.587 2.80e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.8982 on 8 degrees of freedom
## Multiple R-squared:  0.9438, Adjusted R-squared:  0.9367 
## F-statistic: 134.3 on 1 and 8 DF,  p-value: 2.799e-06

Note that the coefficients are not displayed as \(\beta_0\) and \(\beta_1\), but rather as (Intercept) and x (to be interpreted as ‘the coefficient for the x column in the design matrix’).

For a given value of \(x\), the fitted/predicted value of \(y\) can now be obtained as \[\hat{y}_i = \hat{\beta}_0 + \hat{\beta}_1\cdot x_i\] or in matrix form \[\begin{pmatrix}\hat{y}_1\\\hat{y}_2\\...\\\hat{y}_n\end{pmatrix} = \begin{pmatrix}1 & x_1\\1 & x_2\\... & ...\\1 & x_n\end{pmatrix}\begin{pmatrix}\hat{\beta}_0\\\hat{\beta}_1\end{pmatrix}\] In other words, the fitted values are obtained by matrix multiplication of the design matrix and the estimated coefficients.

ggplot(data.frame(x = x, y = y), 
       aes(x = x, y = y)) + 
    geom_point(size = 4) + theme_minimal() + 
    geom_smooth(method = "lm", se = FALSE)
## `geom_smooth()` using formula = 'y ~ x'

The ExploreModelMatrix Bioconductor package gives us a way to explore the design, either interactively or programmatically:

library(ExploreModelMatrix)
library(cowplot)

if (interactive()) {
    ExploreModelMatrix(sampleData = data.frame(y = y, x = x), 
                       designFormula = ~ x)
}

vd <- VisualizeDesign(sampleData = data.frame(y = y, x = x), 
                      designFormula = ~ x)
cowplot::plot_grid(plotlist = vd$plotlist)

The displayed plot indicates that the fitted value for a given observation is obtained by taking the intercept + the value of \(x\) times the coefficient for x.

2 Categorical predictors

Let’s now assume that we have a categorical predictor treat:

set.seed(123)
(treat <- gl(n = 2, k = 5, labels = c("ctrl", "trt")))
##  [1] ctrl ctrl ctrl ctrl ctrl trt  trt  trt  trt  trt 
## Levels: ctrl trt
y <- rnorm(n = 10, mean = rep(c(1, 3), each = 5))
ggplot(data.frame(treat = treat, y = y), 
       aes(x = treat, y = y)) + 
    geom_point(size = 4) + theme_minimal()

Of course we can’t multiply a coefficient with a character, so instead we recode the predictor using so called dummy variables. A dummy variable is 1 for all observations that belong to a certain category, and 0 otherwise. Let’s make a design matrix for this example:

model.matrix(~ treat)
##    (Intercept) treattrt
## 1            1        0
## 2            1        0
## 3            1        0
## 4            1        0
## 5            1        0
## 6            1        1
## 7            1        1
## 8            1        1
## 9            1        1
## 10           1        1
## attr(,"assign")
## [1] 0 1
## attr(,"contrasts")
## attr(,"contrasts")$treat
## [1] "contr.treatment"

Note that we still have the intercept, and the treattrt column is 1 for all samples in treat group trt, and zero for all samples not in group trt. Also note that we don’t have a column which is 1 only for group ctrl - this would be overparametrizing the model, since that column would be identical to the (Intercept) column minus the treattrt column.

Fitting a linear model again allows us to estimate the coefficients:

summary(lm(y ~ treat))
## 
## Call:
## lm(formula = y ~ treat)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1.2207 -0.5878 -0.2622  0.3629  1.7594 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)  
## (Intercept)   1.1936     0.4485   2.661   0.0287 *
## treattrt      1.7621     0.6343   2.778   0.0240 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 1.003 on 8 degrees of freedom
## Multiple R-squared:  0.491,  Adjusted R-squared:  0.4274 
## F-statistic: 7.718 on 1 and 8 DF,  p-value: 0.02399

So how do we interpret this? Recall from above that the fitted/predicted value of \(y\) can be obtained by multiplying the design matrix by the estimated coefficients: \[\begin{pmatrix}\hat{y}_1\\\hat{y}_2\\...\\\hat{y}_n\end{pmatrix} = \begin{pmatrix}1 & 0\\1 & 0\\... & ...\\1 & 1\end{pmatrix}\begin{pmatrix}\widehat{(Intercept)}\\\widehat{treattrt}\end{pmatrix}\] So similarly to what we did for the linear regression, the fitted values for all samples in group ctrl (the first five), will be ‘1 times the intercept + 0 times the coefficient for treattrt’, i.e., just the intercept. The fitted value for all samples in group trt is ‘1 times the intercept + 1 times the coefficient for treattrt’, i.e., intercept + coefficient for treattrt. Thus, the coefficient for treattrt in this case directly represents the difference between the fitted values for the two groups, and the intercept represents the fitted value for the samples in group ctrl. We can again explore the design interactively using ExploreModelMatrix:

if (interactive()) {
    ExploreModelMatrix(sampleData = data.frame(y = y, treat = treat), 
                       designFormula = ~ treat)
}

vd <- VisualizeDesign(sampleData = data.frame(y = y, treat = treat), 
                      designFormula = ~ treat)
cowplot::plot_grid(plotlist = vd$plotlist)

There are other ways of parametrizing this model - if we want the coefficients to directly give us the fitted values for each group, we can exclude the intercept:

model.matrix(~ 0 + treat)
##    treatctrl treattrt
## 1          1        0
## 2          1        0
## 3          1        0
## 4          1        0
## 5          1        0
## 6          0        1
## 7          0        1
## 8          0        1
## 9          0        1
## 10         0        1
## attr(,"assign")
## [1] 1 1
## attr(,"contrasts")
## attr(,"contrasts")$treat
## [1] "contr.treatment"
summary(lm(y ~ 0 + treat))
## 
## Call:
## lm(formula = y ~ 0 + treat)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1.2207 -0.5878 -0.2622  0.3629  1.7594 
## 
## Coefficients:
##           Estimate Std. Error t value Pr(>|t|)    
## treatctrl   1.1936     0.4485   2.661 0.028748 *  
## treattrt    2.9557     0.4485   6.590 0.000171 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 1.003 on 8 degrees of freedom
## Multiple R-squared:  0.8633, Adjusted R-squared:  0.8291 
## F-statistic: 25.26 on 2 and 8 DF,  p-value: 0.0003494
if (interactive()) {
    ExploreModelMatrix(sampleData = data.frame(y = y, treat = treat), 
                       designFormula = ~ 0 + treat)
}

vd <- VisualizeDesign(sampleData = data.frame(y = y, treat = treat), 
                      designFormula = ~ 0 + treat)
cowplot::plot_grid(plotlist = vd$plotlist)

Both models are equally ‘valid’ - the main difference appears when we want to interpret the coefficients or perform statistical tests. For example, to perform a test comparing groups ctrl and trt with the first approach, we would directly test whether the coefficient treattrt is different from zero. In the second case, we would have to test whether the difference between the treattrt and treatctrl coefficients is different from zero (such linear combinations of coefficients are usually referred to as contrasts).

3 What if we have two explanatory variables?

Multiple explanatory variables, assumed to have additive effects on the response, can be acccommodated as well:

(gt <- rep(c("wt", "mut"), 5))
##  [1] "wt"  "mut" "wt"  "mut" "wt"  "mut" "wt"  "mut" "wt"  "mut"
model.matrix(~ gt + treat)
##    (Intercept) gtwt treattrt
## 1            1    1        0
## 2            1    0        0
## 3            1    1        0
## 4            1    0        0
## 5            1    1        0
## 6            1    0        1
## 7            1    1        1
## 8            1    0        1
## 9            1    1        1
## 10           1    0        1
## attr(,"assign")
## [1] 0 1 2
## attr(,"contrasts")
## attr(,"contrasts")$gt
## [1] "contr.treatment"
## 
## attr(,"contrasts")$treat
## [1] "contr.treatment"

Note how the intercept absorbs the reference level of each of the predictors.

if (interactive()) {
    ExploreModelMatrix(sampleData = data.frame(y = y, gt = gt, treat = treat), 
                       designFormula = ~ gt + treat)
}

vd <- VisualizeDesign(sampleData = data.frame(y = y, gt = gt, treat = treat), 
                      designFormula = ~ gt + treat)
cowplot::plot_grid(plotlist = vd$plotlist)

Finally, if we don’t want to assume that the effects of the two predictors are additive, we can use an interaction term (effectively allowing the effect of one of the predictors to depend on the value of another):

model.matrix(~ gt * treat)    ## equivalent to ~ gt + treat + gt:treat
##    (Intercept) gtwt treattrt gtwt:treattrt
## 1            1    1        0             0
## 2            1    0        0             0
## 3            1    1        0             0
## 4            1    0        0             0
## 5            1    1        0             0
## 6            1    0        1             0
## 7            1    1        1             1
## 8            1    0        1             0
## 9            1    1        1             1
## 10           1    0        1             0
## attr(,"assign")
## [1] 0 1 2 3
## attr(,"contrasts")
## attr(,"contrasts")$gt
## [1] "contr.treatment"
## 
## attr(,"contrasts")$treat
## [1] "contr.treatment"
if (interactive()) {
    ExploreModelMatrix(sampleData = data.frame(y = y, gt = gt, treat = treat), 
                       designFormula = ~ gt * treat)
}

vd <- VisualizeDesign(sampleData = data.frame(y = y, gt = gt, treat = treat), 
                      designFormula = ~ gt * treat)
cowplot::plot_grid(plotlist = vd$plotlist)

4 Exercises

  • Take a look at the ExploreModelMatrix vignette and explore some of the designs in there. The ExploreModelMatrix app also provides example designs, which you can load via the ‘Use example design’ dropdown menu. Some background information and a description of the app is found in the accompanying paper.
  • Law et al (2020) provides an excellent guide to creating design matrices in R.
LS0tCnRpdGxlOiA+CiAgRGVzaWduIG1hdHJpY2VzCnN1YnRpdGxlOiA+CiAgU2NpZW50aWZpYyBJbnF1aXJ5IDIwMjQKYXV0aG9yOgotIG5hbWU6IDxhIGhyZWY9Imh0dHBzOi8vY3NvbmVzb24uZ2l0aHViLmlvIj5DaGFybG90dGUgU29uZXNvbiAoY2hhcmxvdHRlLnNvbmVzb25AZm1pLmNoKTwvYT4KZGF0ZTogIjIwMjQtMDMtMDgiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRoZW1lOiBjb3NtbwogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKICBtYXJrZG93bjogCiAgICB3cmFwOiBzZW50ZW5jZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmxpYnJhcnkoZ2dwbG90MikKYGBgCgojIExpbmVhciByZWdyZXNzaW9uCgpMZXQncyBzYXkgdGhhdCB3ZSBoYXZlICRuPTEwJCBvYnNlcnZhdGlvbnMgZm9yIGEgcHJlZGljdG9yIChleHBsYW5hdG9yeSkgdmFyaWFibGUgJHgkIGFuZCBhIGRlcGVuZGVudCB2YXJpYWJsZSAkeSQ6CgpgYGB7cn0Kc2V0LnNlZWQoMTIzKQooeCA8LSByb3VuZChybm9ybSgxMCwgbWVhbiA9IDAsIHNkID0gMSksIGRpZ2l0cyA9IDIpKQooeSA8LSAyICsgMyAqIHggKyBybm9ybSgxMCwgbWVhbiA9IDAsIHNkID0gMSkpCmdncGxvdChkYXRhLmZyYW1lKHggPSB4LCB5ID0geSksIAogICAgICAgYWVzKHggPSB4LCB5ID0geSkpICsgCiAgICBnZW9tX3BvaW50KHNpemUgPSA0KSArIHRoZW1lX21pbmltYWwoKQpgYGAKCkxpbmVhciByZWdyZXNzaW9uIG9mICR5JCBvbiAkeCQgd291bGQgaW1wbHkgdXNpbmcgdGhlIG1vZGVsICQkeV9pID0gXGJldGFfMCArIFxiZXRhXzFcY2RvdCB4X2kgKyBcdmFyZXBzaWxvbl9pJCQgZm9yICRpPTEsIC4uLiwgbiQsIGFuZCB3aGVyZSAkXGJldGFfMCQgKGludGVyY2VwdCkgYW5kICRcYmV0YV8xJCAoc2xvcGUpIGFyZSBwYXJhbWV0ZXJzIHRoYXQgYXJlIHVua25vd24gYW5kIHdpbGwgYmUgZXN0aW1hdGVkIGZyb20gdGhlIGRhdGEuIEluIG90aGVyIHdvcmRzLCAkJHlfMSA9IFxiZXRhXzAgKyBcYmV0YV8xXGNkb3QgeF8xICsgXHZhcmVwc2lsb25fMVxceV8yID0gXGJldGFfMCArIFxiZXRhXzFcY2RvdCB4XzIgKyBcdmFyZXBzaWxvbl8yXFwuLi5cXHlfbiA9IFxiZXRhXzAgKyBcYmV0YV8xXGNkb3QgeF9uICsgXHZhcmVwc2lsb25fbiQkCgpXZSBjYW4gd3JpdGUgdGhpcyBpbiBtYXRyaXggZm9ybSBhcyBmb2xsb3dzOiAkJFxiZWdpbntwbWF0cml4fXlfMVxceV8yXFwuLi5cXHlfblxlbmR7cG1hdHJpeH0gPSBcYmVnaW57cG1hdHJpeH0xICYgeF8xXFwxICYgeF8yXFwuLi4gJiAuLi5cXDEgJiB4X25cZW5ke3BtYXRyaXh9XGJlZ2lue3BtYXRyaXh9XGJldGFfMFxcXGJldGFfMVxlbmR7cG1hdHJpeH0gKyBcYmVnaW57cG1hdHJpeH1cdmFyZXBzaWxvbl8xXFxcdmFyZXBzaWxvbl8yXFwuLi5cXFx2YXJlcHNpbG9uX25cZW5ke3BtYXRyaXh9JCQKClRoZSBmaXJzdCBtYXRyaXggdG8gdGhlIHJpZ2h0IG9mIHRoZSBlcXVhbCBzaWduIGlzIHRoZSBfZGVzaWduIG1hdHJpeF8uIAoKSW4gUiwgd2Ugd291bGQgZXhwcmVzcyB0aGlzIG1vZGVsIGluIGZvcm11bGEgbm90YXRpb24gYXMgYHkgfiB4YC4gVGhpcyB3aWxsIGJ5IGRlZmF1bHQgaW5jbHVkZSBhbiBpbnRlcmNlcHQgKHVubGVzcyB3ZSBleHBsaWNpdGx5IHRlbGwgaXQgbm90IHRvKSwgYW5kIHNpbmNlIGB4YCBpcyBhIGNvbnRpbnVvdXMgdmFyaWFibGUsIGl0IHdpbGwgaW5jbHVkZSBhIHNpbmdsZSBjb2VmZmljaWVudCBmb3IgaXQgKHdoYXQgd2UgaGF2ZSByZWZlcnJlZCB0byBhcyAkXGJldGFfMSQgYWJvdmUpLiBUaGUgZGVzaWduIG1hdHJpeCBmb3IgYSBnaXZlbiBtb2RlbCBjYW4gYmUgY3JlYXRlZCB1c2luZyB0aGUgYG1vZGVsLm1hdHJpeCgpYCBmdW5jdGlvbiBpbiBSIChub3RlIHRoYXQgdGhpcyBkb2VzIG5vdCByZXF1aXJlIGtub3dsZWRnZSBhYm91dCB0aGUgcmVzcG9uc2UgdmFyaWFibGUpOgoKYGBge3J9Cm1vZGVsLm1hdHJpeCh+IHgpCmBgYAoKV2UgY2FuIGFsc28gZml0IGEgbGluZWFyIG1vZGVsIGluIFIsIHdoaWNoIHdpbGwgcHJvdmlkZSBlc3RpbWF0ZXMgZm9yIHRoZSBjb2VmZmljaWVudHMgJFxiZXRhXzAkIGFuZCAkXGJldGFfMSQ6CgpgYGB7cn0Kc3VtbWFyeShsbSh5IH4geCkpCmBgYAoKTm90ZSB0aGF0IHRoZSBjb2VmZmljaWVudHMgYXJlIG5vdCBkaXNwbGF5ZWQgYXMgJFxiZXRhXzAkIGFuZCAkXGJldGFfMSQsIGJ1dCByYXRoZXIgYXMgYChJbnRlcmNlcHQpYCBhbmQgYHhgICh0byBiZSBpbnRlcnByZXRlZCBhcyAndGhlIGNvZWZmaWNpZW50IGZvciB0aGUgYHhgIGNvbHVtbiBpbiB0aGUgZGVzaWduIG1hdHJpeCcpLgoKRm9yIGEgZ2l2ZW4gdmFsdWUgb2YgJHgkLCB0aGUgZml0dGVkL3ByZWRpY3RlZCB2YWx1ZSBvZiAkeSQgY2FuIG5vdyBiZSBvYnRhaW5lZCBhcyAkJFxoYXR7eX1faSA9IFxoYXR7XGJldGF9XzAgKyBcaGF0e1xiZXRhfV8xXGNkb3QgeF9pJCQgb3IgaW4gbWF0cml4IGZvcm0KJCRcYmVnaW57cG1hdHJpeH1caGF0e3l9XzFcXFxoYXR7eX1fMlxcLi4uXFxcaGF0e3l9X25cZW5ke3BtYXRyaXh9ID0gXGJlZ2lue3BtYXRyaXh9MSAmIHhfMVxcMSAmIHhfMlxcLi4uICYgLi4uXFwxICYgeF9uXGVuZHtwbWF0cml4fVxiZWdpbntwbWF0cml4fVxoYXR7XGJldGF9XzBcXFxoYXR7XGJldGF9XzFcZW5ke3BtYXRyaXh9JCQKSW4gb3RoZXIgd29yZHMsIHRoZSBmaXR0ZWQgdmFsdWVzIGFyZSBvYnRhaW5lZCBieSBtYXRyaXggbXVsdGlwbGljYXRpb24gb2YgdGhlIGRlc2lnbiBtYXRyaXggYW5kIHRoZSBlc3RpbWF0ZWQgY29lZmZpY2llbnRzLiAKCmBgYHtyfQpnZ3Bsb3QoZGF0YS5mcmFtZSh4ID0geCwgeSA9IHkpLCAKICAgICAgIGFlcyh4ID0geCwgeSA9IHkpKSArIAogICAgZ2VvbV9wb2ludChzaXplID0gNCkgKyB0aGVtZV9taW5pbWFsKCkgKyAKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpCmBgYAoKVGhlIGBFeHBsb3JlTW9kZWxNYXRyaXhgIEJpb2NvbmR1Y3RvciBwYWNrYWdlIGdpdmVzIHVzIGEgd2F5IHRvIGV4cGxvcmUgdGhlIGRlc2lnbiwgZWl0aGVyIGludGVyYWN0aXZlbHkgb3IgcHJvZ3JhbW1hdGljYWxseTogCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShFeHBsb3JlTW9kZWxNYXRyaXgpCmxpYnJhcnkoY293cGxvdCkKCmlmIChpbnRlcmFjdGl2ZSgpKSB7CiAgICBFeHBsb3JlTW9kZWxNYXRyaXgoc2FtcGxlRGF0YSA9IGRhdGEuZnJhbWUoeSA9IHksIHggPSB4KSwgCiAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduRm9ybXVsYSA9IH4geCkKfQoKdmQgPC0gVmlzdWFsaXplRGVzaWduKHNhbXBsZURhdGEgPSBkYXRhLmZyYW1lKHkgPSB5LCB4ID0geCksIAogICAgICAgICAgICAgICAgICAgICAgZGVzaWduRm9ybXVsYSA9IH4geCkKY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gdmQkcGxvdGxpc3QpCmBgYAoKVGhlIGRpc3BsYXllZCBwbG90IGluZGljYXRlcyB0aGF0IHRoZSBmaXR0ZWQgdmFsdWUgZm9yIGEgZ2l2ZW4gb2JzZXJ2YXRpb24gaXMgb2J0YWluZWQgYnkgdGFraW5nIHRoZSBpbnRlcmNlcHQgKyB0aGUgdmFsdWUgb2YgJHgkIHRpbWVzIHRoZSBjb2VmZmljaWVudCBmb3IgYHhgLiAKCiMgQ2F0ZWdvcmljYWwgcHJlZGljdG9ycwoKTGV0J3Mgbm93IGFzc3VtZSB0aGF0IHdlIGhhdmUgYSBjYXRlZ29yaWNhbCBwcmVkaWN0b3IgYHRyZWF0YDoKCmBgYHtyfQpzZXQuc2VlZCgxMjMpCih0cmVhdCA8LSBnbChuID0gMiwgayA9IDUsIGxhYmVscyA9IGMoImN0cmwiLCAidHJ0IikpKQp5IDwtIHJub3JtKG4gPSAxMCwgbWVhbiA9IHJlcChjKDEsIDMpLCBlYWNoID0gNSkpCmdncGxvdChkYXRhLmZyYW1lKHRyZWF0ID0gdHJlYXQsIHkgPSB5KSwgCiAgICAgICBhZXMoeCA9IHRyZWF0LCB5ID0geSkpICsgCiAgICBnZW9tX3BvaW50KHNpemUgPSA0KSArIHRoZW1lX21pbmltYWwoKQpgYGAKCk9mIGNvdXJzZSB3ZSBjYW4ndCBtdWx0aXBseSBhIGNvZWZmaWNpZW50IHdpdGggYSBjaGFyYWN0ZXIsIHNvIGluc3RlYWQgd2UgcmVjb2RlIHRoZSBwcmVkaWN0b3IgdXNpbmcgc28gY2FsbGVkIGR1bW15IHZhcmlhYmxlcy4gQSBkdW1teSB2YXJpYWJsZSBpcyAxIGZvciBhbGwgb2JzZXJ2YXRpb25zIHRoYXQgYmVsb25nIHRvIGEgY2VydGFpbiBjYXRlZ29yeSwgYW5kIDAgb3RoZXJ3aXNlLiBMZXQncyBtYWtlIGEgZGVzaWduIG1hdHJpeCBmb3IgdGhpcyBleGFtcGxlOiAKCmBgYHtyfQptb2RlbC5tYXRyaXgofiB0cmVhdCkKYGBgCgpOb3RlIHRoYXQgd2Ugc3RpbGwgaGF2ZSB0aGUgaW50ZXJjZXB0LCBhbmQgdGhlIGB0cmVhdHRydGAgY29sdW1uIGlzIDEgZm9yIGFsbCBzYW1wbGVzIGluIHRyZWF0IGdyb3VwIGB0cnRgLCBhbmQgemVybyBmb3IgYWxsIHNhbXBsZXMgbm90IGluIGdyb3VwIGB0cnRgLiBBbHNvIG5vdGUgdGhhdCB3ZSBkb24ndCBoYXZlIGEgY29sdW1uIHdoaWNoIGlzIDEgb25seSBmb3IgZ3JvdXAgYGN0cmxgIC0gdGhpcyB3b3VsZCBiZSBvdmVycGFyYW1ldHJpemluZyB0aGUgbW9kZWwsIHNpbmNlIHRoYXQgY29sdW1uIHdvdWxkIGJlIGlkZW50aWNhbCB0byB0aGUgYChJbnRlcmNlcHQpYCBjb2x1bW4gbWludXMgdGhlIGB0cmVhdHRydGAgY29sdW1uLiAKCkZpdHRpbmcgYSBsaW5lYXIgbW9kZWwgYWdhaW4gYWxsb3dzIHVzIHRvIGVzdGltYXRlIHRoZSBjb2VmZmljaWVudHM6IAoKYGBge3J9CnN1bW1hcnkobG0oeSB+IHRyZWF0KSkKYGBgCgpTbyBob3cgZG8gd2UgaW50ZXJwcmV0IHRoaXM/IFJlY2FsbCBmcm9tIGFib3ZlIHRoYXQgdGhlIGZpdHRlZC9wcmVkaWN0ZWQgdmFsdWUgb2YgJHkkIGNhbiBiZSBvYnRhaW5lZCBieSBtdWx0aXBseWluZyB0aGUgZGVzaWduIG1hdHJpeCBieSB0aGUgZXN0aW1hdGVkIGNvZWZmaWNpZW50czogCiQkXGJlZ2lue3BtYXRyaXh9XGhhdHt5fV8xXFxcaGF0e3l9XzJcXC4uLlxcXGhhdHt5fV9uXGVuZHtwbWF0cml4fSA9IFxiZWdpbntwbWF0cml4fTEgJiAwXFwxICYgMFxcLi4uICYgLi4uXFwxICYgMVxlbmR7cG1hdHJpeH1cYmVnaW57cG1hdHJpeH1cd2lkZWhhdHsoSW50ZXJjZXB0KX1cXFx3aWRlaGF0e3RyZWF0dHJ0fVxlbmR7cG1hdHJpeH0kJApTbyBzaW1pbGFybHkgdG8gd2hhdCB3ZSBkaWQgZm9yIHRoZSBsaW5lYXIgcmVncmVzc2lvbiwgdGhlIGZpdHRlZCB2YWx1ZXMgZm9yIGFsbCBzYW1wbGVzIGluIGdyb3VwIGBjdHJsYCAodGhlIGZpcnN0IGZpdmUpLCB3aWxsIGJlICcxIHRpbWVzIHRoZSBpbnRlcmNlcHQgKyAwIHRpbWVzIHRoZSBjb2VmZmljaWVudCBmb3IgYHRyZWF0dHJ0YCcsIGkuZS4sIGp1c3QgdGhlIGludGVyY2VwdC4gVGhlIGZpdHRlZCB2YWx1ZSBmb3IgYWxsIHNhbXBsZXMgaW4gZ3JvdXAgYHRydGAgaXMgJzEgdGltZXMgdGhlIGludGVyY2VwdCArIDEgdGltZXMgdGhlIGNvZWZmaWNpZW50IGZvciBgdHJlYXR0cnRgJywgaS5lLiwgaW50ZXJjZXB0ICsgY29lZmZpY2llbnQgZm9yIGB0cmVhdHRydGAuIFRodXMsIHRoZSBjb2VmZmljaWVudCBmb3IgYHRyZWF0dHJ0YCBpbiB0aGlzIGNhc2UgZGlyZWN0bHkgcmVwcmVzZW50cyB0aGUgX2RpZmZlcmVuY2VfIGJldHdlZW4gdGhlIGZpdHRlZCB2YWx1ZXMgZm9yIHRoZSB0d28gZ3JvdXBzLCBhbmQgdGhlIGludGVyY2VwdCByZXByZXNlbnRzIHRoZSBmaXR0ZWQgdmFsdWUgZm9yIHRoZSBzYW1wbGVzIGluIGdyb3VwIGBjdHJsYC4gV2UgY2FuIGFnYWluIGV4cGxvcmUgdGhlIGRlc2lnbiBpbnRlcmFjdGl2ZWx5IHVzaW5nIGBFeHBsb3JlTW9kZWxNYXRyaXhgOgoKYGBge3J9CmlmIChpbnRlcmFjdGl2ZSgpKSB7CiAgICBFeHBsb3JlTW9kZWxNYXRyaXgoc2FtcGxlRGF0YSA9IGRhdGEuZnJhbWUoeSA9IHksIHRyZWF0ID0gdHJlYXQpLCAKICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ25Gb3JtdWxhID0gfiB0cmVhdCkKfQoKdmQgPC0gVmlzdWFsaXplRGVzaWduKHNhbXBsZURhdGEgPSBkYXRhLmZyYW1lKHkgPSB5LCB0cmVhdCA9IHRyZWF0KSwgCiAgICAgICAgICAgICAgICAgICAgICBkZXNpZ25Gb3JtdWxhID0gfiB0cmVhdCkKY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gdmQkcGxvdGxpc3QpCmBgYAoKVGhlcmUgYXJlIG90aGVyIHdheXMgb2YgcGFyYW1ldHJpemluZyB0aGlzIG1vZGVsIC0gaWYgd2Ugd2FudCB0aGUgY29lZmZpY2llbnRzIHRvIGRpcmVjdGx5IGdpdmUgdXMgdGhlIGZpdHRlZCB2YWx1ZXMgZm9yIGVhY2ggZ3JvdXAsIHdlIGNhbiBleGNsdWRlIHRoZSBpbnRlcmNlcHQ6IAoKYGBge3J9Cm1vZGVsLm1hdHJpeCh+IDAgKyB0cmVhdCkKYGBgCgpgYGB7cn0Kc3VtbWFyeShsbSh5IH4gMCArIHRyZWF0KSkKYGBgCgpgYGB7cn0KaWYgKGludGVyYWN0aXZlKCkpIHsKICAgIEV4cGxvcmVNb2RlbE1hdHJpeChzYW1wbGVEYXRhID0gZGF0YS5mcmFtZSh5ID0geSwgdHJlYXQgPSB0cmVhdCksIAogICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbkZvcm11bGEgPSB+IDAgKyB0cmVhdCkKfQoKdmQgPC0gVmlzdWFsaXplRGVzaWduKHNhbXBsZURhdGEgPSBkYXRhLmZyYW1lKHkgPSB5LCB0cmVhdCA9IHRyZWF0KSwgCiAgICAgICAgICAgICAgICAgICAgICBkZXNpZ25Gb3JtdWxhID0gfiAwICsgdHJlYXQpCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbG90bGlzdCA9IHZkJHBsb3RsaXN0KQpgYGAKCkJvdGggbW9kZWxzIGFyZSBlcXVhbGx5ICd2YWxpZCcgLSB0aGUgbWFpbiBkaWZmZXJlbmNlIGFwcGVhcnMgd2hlbiB3ZSB3YW50IHRvIGludGVycHJldCB0aGUgY29lZmZpY2llbnRzIG9yIHBlcmZvcm0gc3RhdGlzdGljYWwgdGVzdHMuIEZvciBleGFtcGxlLCB0byBwZXJmb3JtIGEgdGVzdCBjb21wYXJpbmcgZ3JvdXBzIGBjdHJsYCBhbmQgYHRydGAgd2l0aCB0aGUgZmlyc3QgYXBwcm9hY2gsIHdlIHdvdWxkIGRpcmVjdGx5IHRlc3Qgd2hldGhlciB0aGUgY29lZmZpY2llbnQgYHRyZWF0dHJ0YCBpcyBkaWZmZXJlbnQgZnJvbSB6ZXJvLiBJbiB0aGUgc2Vjb25kIGNhc2UsIHdlIHdvdWxkIGhhdmUgdG8gdGVzdCB3aGV0aGVyIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGB0cmVhdHRydGAgYW5kIGB0cmVhdGN0cmxgIGNvZWZmaWNpZW50cyBpcyBkaWZmZXJlbnQgZnJvbSB6ZXJvIChzdWNoIGxpbmVhciBjb21iaW5hdGlvbnMgb2YgY29lZmZpY2llbnRzIGFyZSB1c3VhbGx5IHJlZmVycmVkIHRvIGFzIGNvbnRyYXN0cykuIAoKIyBXaGF0IGlmIHdlIGhhdmUgdHdvIGV4cGxhbmF0b3J5IHZhcmlhYmxlcz8KCk11bHRpcGxlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcywgYXNzdW1lZCB0byBoYXZlIGFkZGl0aXZlIGVmZmVjdHMgb24gdGhlIHJlc3BvbnNlLCBjYW4gYmUgYWNjY29tbW9kYXRlZCBhcyB3ZWxsOiAKCmBgYHtyfQooZ3QgPC0gcmVwKGMoInd0IiwgIm11dCIpLCA1KSkKbW9kZWwubWF0cml4KH4gZ3QgKyB0cmVhdCkKYGBgCgpOb3RlIGhvdyB0aGUgaW50ZXJjZXB0IGFic29yYnMgdGhlIHJlZmVyZW5jZSBsZXZlbCBvZiBlYWNoIG9mIHRoZSBwcmVkaWN0b3JzLiAKCmBgYHtyfQppZiAoaW50ZXJhY3RpdmUoKSkgewogICAgRXhwbG9yZU1vZGVsTWF0cml4KHNhbXBsZURhdGEgPSBkYXRhLmZyYW1lKHkgPSB5LCBndCA9IGd0LCB0cmVhdCA9IHRyZWF0KSwgCiAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduRm9ybXVsYSA9IH4gZ3QgKyB0cmVhdCkKfQoKdmQgPC0gVmlzdWFsaXplRGVzaWduKHNhbXBsZURhdGEgPSBkYXRhLmZyYW1lKHkgPSB5LCBndCA9IGd0LCB0cmVhdCA9IHRyZWF0KSwgCiAgICAgICAgICAgICAgICAgICAgICBkZXNpZ25Gb3JtdWxhID0gfiBndCArIHRyZWF0KQpjb3dwbG90OjpwbG90X2dyaWQocGxvdGxpc3QgPSB2ZCRwbG90bGlzdCkKYGBgCgpGaW5hbGx5LCBpZiB3ZSBkb24ndCB3YW50IHRvIGFzc3VtZSB0aGF0IHRoZSBlZmZlY3RzIG9mIHRoZSB0d28gcHJlZGljdG9ycyBhcmUgYWRkaXRpdmUsIHdlIGNhbiB1c2UgYW4gaW50ZXJhY3Rpb24gdGVybSAoZWZmZWN0aXZlbHkgYWxsb3dpbmcgdGhlIGVmZmVjdCBvZiBvbmUgb2YgdGhlIHByZWRpY3RvcnMgdG8gZGVwZW5kIG9uIHRoZSB2YWx1ZSBvZiBhbm90aGVyKToKCmBgYHtyfQptb2RlbC5tYXRyaXgofiBndCAqIHRyZWF0KSAgICAjIyBlcXVpdmFsZW50IHRvIH4gZ3QgKyB0cmVhdCArIGd0OnRyZWF0CmBgYAoKYGBge3J9CmlmIChpbnRlcmFjdGl2ZSgpKSB7CiAgICBFeHBsb3JlTW9kZWxNYXRyaXgoc2FtcGxlRGF0YSA9IGRhdGEuZnJhbWUoeSA9IHksIGd0ID0gZ3QsIHRyZWF0ID0gdHJlYXQpLCAKICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ25Gb3JtdWxhID0gfiBndCAqIHRyZWF0KQp9Cgp2ZCA8LSBWaXN1YWxpemVEZXNpZ24oc2FtcGxlRGF0YSA9IGRhdGEuZnJhbWUoeSA9IHksIGd0ID0gZ3QsIHRyZWF0ID0gdHJlYXQpLCAKICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbkZvcm11bGEgPSB+IGd0ICogdHJlYXQpCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbG90bGlzdCA9IHZkJHBsb3RsaXN0KQpgYGAKCiMgRXhlcmNpc2VzCgoqIFRha2UgYSBsb29rIGF0IHRoZSBFeHBsb3JlTW9kZWxNYXRyaXggW3ZpZ25ldHRlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9FeHBsb3JlTW9kZWxNYXRyaXgvaW5zdC9kb2MvRXhwbG9yZU1vZGVsTWF0cml4Lmh0bWwpIGFuZCBleHBsb3JlIHNvbWUgb2YgdGhlIGRlc2lnbnMgaW4gdGhlcmUuIFRoZSBFeHBsb3JlTW9kZWxNYXRyaXggYXBwIGFsc28gcHJvdmlkZXMgZXhhbXBsZSBkZXNpZ25zLCB3aGljaCB5b3UgY2FuIGxvYWQgdmlhIHRoZSAnVXNlIGV4YW1wbGUgZGVzaWduJyBkcm9wZG93biBtZW51LiBTb21lIGJhY2tncm91bmQgaW5mb3JtYXRpb24gYW5kIGEgZGVzY3JpcHRpb24gb2YgdGhlIGFwcCBpcyBmb3VuZCBpbiB0aGUgYWNjb21wYW55aW5nIFtwYXBlcl0oaHR0cHM6Ly9mMTAwMHJlc2VhcmNoLmNvbS9hcnRpY2xlcy85LTUxMi92MikuIAoqIFtMYXcgZXQgYWwgKDIwMjApXShodHRwczovL2YxMDAwcmVzZWFyY2guY29tL2FydGljbGVzLzktMTQ0NCkgcHJvdmlkZXMgYW4gZXhjZWxsZW50IGd1aWRlIHRvIGNyZWF0aW5nIGRlc2lnbiBtYXRyaWNlcyBpbiBSLiAKCgoK