1 Key idea

  • Another fast approach to fit the GWAS-LMM.
  • Builds on the developments in BOLT-LMM
  1. Project known covariates \(\mathbf{X}_c\) out
  2. Take BOLT-LMM-inf idea to use LOCO-residuals under the null model.
  3. Exploit link between mixed models and ridge regression for fast approximation of LOCO-BLUP under \(H_0\).
  4. Model LOCO residuals using candidate SNP.

\[ \mathbf{y}_\text{residual-LOCO, H_0} = \tilde{\mathbf{x}}_\text{test} \boldsymbol{\beta}_\text{test} + \epsilon \]

  1. Use a score test similar to BOLT-LMM-inf

\[ T = \frac{(\tilde{\mathbf{x}}^T_\text{test}\mathbf{y}_\text{residual-LOCO, H_0})^2}{\hat\sigma^2_\epsilon\tilde{\mathbf{x}}^T\tilde{\mathbf{x}}} \]

  1. Estimate \(\hat{\sigma}^2_\epsilon\) under the null model:

\[ \hat\sigma^2_\epsilon = \frac{\vert\vert\mathbf{y}_\text{residual-LOCO, H_0}\vert\vert^2_2}{N-C} \]

  • In contrast to BOLT-LMM-inf no calibration factor used in denominator
  • they “found in applications that the results obtained using this simple form match up closely to those using a calibration factor”

Key contribution efficiently obtain \(\mathbf{y}_\text{residual-LOCO, H_0}\)

  • Check method section paper
  • As usual in publications: to understand the methods, it is all about the supplement

2 Intermezzo ridge regression

  • Penalized regression useful in a high dimensional context when the number of covariates are larger than the number of samples (e.g. M >> N)
  • When covariates are highly correlated

The ridge parameter estimator is defined as the parameter \(\mathbf\beta\) that minimises the penalised least squares loss function

\[ \text{SSE}_\text{pen}=\Vert\mathbf{Y} - \mathbf{X\beta}\Vert_2^2 + \lambda \Vert \boldsymbol{\beta} \Vert_2^2 \]

  • \(\Vert \boldsymbol{\beta} \Vert_2^2=\sum_{j=1}^p \beta_j^2\) is the \(L_2\) penalty term

  • \(\lambda>0\) is the penalty parameter (to be chosen by the user).

Note, that that is equivalent to minimizing \[ \Vert\mathbf{Y} - \mathbf{X\beta}\Vert_2^2 \text{ subject to } \Vert \boldsymbol{\beta}\Vert^2_2\leq s \]

Note, that \(s\) has a one-to-one correspondence with \(\lambda\)

2.1 Graphical interpretation

  • Ridge estimator can switch sign as compared to OLS.

2.2 Estimator

The loss function to be minimised is \[ L_\text{ridge}(\mathbf{Y},\boldsymbol{\beta},\lambda) = \text{SSE}_\text{pen}=\Vert\mathbf{Y} - \mathbf{X\beta}\Vert_2^2 + \lambda \Vert \boldsymbol{\beta} \Vert_2^2. \]

First we re-express the loss function in matrix notation: \[ L_\text{ridge}(\mathbf{Y},\boldsymbol{\beta},\lambda) = (\mathbf{Y}-\mathbf{X\beta})^T(\mathbf{Y}-\mathbf{X\beta}) + \lambda \boldsymbol{\beta}^T\boldsymbol{\beta}. \]

Solving \(L_\text{ridge}(\mathbf{Y},\boldsymbol{\beta},\lambda)=0\) gives

[ \[\begin{array}{rcl} \frac{\partial}{\partial \boldsymbol{\beta}}L_\text{ridge}(\mathbf{Y},\boldsymbol{\beta},\lambda) &=& 0 \\\\ -2\mathbf{X}^T(\mathbf{Y}-\mathbf{X\beta})+2\lambda\boldsymbol{\beta} &=& 0\\\\ \hat{\boldsymbol{\beta}} &=& (\mathbf{X^TX}+\lambda \mathbf{I})^{-1} \mathbf{X^T Y} \end{array}\]

]

It can be shown that \((\mathbf{X^TX}+\lambda \mathbf{I})\) is always of rank \(p\) if \(\lambda>0\).

Hence, \((\mathbf{X^TX}+\lambda \mathbf{I})\) is invertible and \(\hat{\boldsymbol{\beta}}\) exists even if \(p>>>n\).

2.4 Properties ridge

  • The Ridge estimator is biased! The \(\boldsymbol{\beta}\) are shrunken to zero! \[\begin{eqnarray} \text{E}[\hat{\boldsymbol{\beta}}] &=& (\mathbf{X^TX}+\lambda \mathbf{I})^{-1} \mathbf{X}^T \text{E}[\mathbf{Y}]\\\\ &=& (\mathbf{X}^T\mathbf{X}+\lambda \mathbf{I})^{-1} \mathbf{X}^T \mathbf{X}\boldsymbol{\beta}\\ \end{eqnarray}\]

  • Note, that the shrinkage is larger in the direction of the smaller eigenvalues.

\[\begin{eqnarray} \text{E}[\hat{\boldsymbol{\beta}}]&=&\mathbf{V} (\boldsymbol{\Delta}^2+\lambda \mathbf{I})^{-1} \mathbf{V}^T \mathbf{V} \boldsymbol{\Delta}^2 \mathbf{V}^T\boldsymbol{\beta}\\\\ &=&\mathbf{V} (\boldsymbol{\Delta}^2+\lambda \mathbf{I})^{-1} \boldsymbol{\Delta}^2 \mathbf{V}^T\boldsymbol{\beta}\\\\ &=& \mathbf{V} \left[\begin{array}{ccc} \frac{\delta_1^2}{\delta_1^2+\lambda}&\ldots&0 \\ &\vdots&\\ 0&\ldots&\frac{\delta_r^2}{\delta_r^2+\lambda} \end{array}\right] \mathbf{V}^T\boldsymbol{\beta} \end{eqnarray}\]

  • Ridge regression can lead to parameters that switch sign if penality increases

2.5 Toxicogenomics example

  • N = 30 chemical compounds have been screened for toxicity

  • Bioassay data on toxicity screening

  • Gene expressions in a liver cell line are profiled for each compound (M = 4000 genes)

  • Predict Bioassay score in function of gene expression.

toxData <- read_csv(
  "https://raw.githubusercontent.com/statOmics/HDA2020/data/toxDataCentered.csv",
  col_types = cols()
)
dim(toxData)
## [1]   30 4001
lmFit <- lm (BA ~. , toxData)
lmFit %>%  
  coef %>% 
  head(40)
##   (Intercept)            X1            X2            X3            X4 
## -2.059917e-17 -7.456994e+00  3.571348e-01  1.124923e+01  1.083540e+01 
##            X5            X6            X7            X8            X9 
## -1.374339e+01  5.683387e+00  6.553878e+01  4.340456e+00  7.910392e+00 
##           X10           X11           X12           X13           X14 
##  3.702961e+01 -5.483687e+01 -5.555478e+01  5.792467e+00  2.314280e+01 
##           X15           X16           X17           X18           X19 
## -6.961036e+00 -2.852506e+01 -2.255090e+01 -9.796237e+01 -3.041718e+01 
##           X20           X21           X22           X23           X24 
## -3.269917e+01 -1.428088e+01 -1.614313e+01 -2.274987e+01  7.316352e+01 
##           X25           X26           X27           X28           X29 
## -5.706583e+00  3.747454e+01 -2.019991e+01  1.499068e+01  9.960810e+01 
##           X30           X31           X32           X33           X34 
##            NA            NA            NA            NA            NA 
##           X35           X36           X37           X38           X39 
##            NA            NA            NA            NA            NA
lmFit %>% 
  coef %>% 
  is.na %>% 
  sum
## [1] 3971
lmFit %>% 
  coef %>% 
  is.na %>% 
  `!` %>% 
  sum 
## [1] 30
library(glmnet)
## Loading required package: Matrix
## 
## Attaching package: 'Matrix'
## The following objects are masked from 'package:tidyr':
## 
##     expand, pack, unpack
## Loaded glmnet 4.1-8
mRidge <- glmnet(
  x = toxData[,-1] %>%
    as.matrix,
  y = toxData %>%
    pull(BA),
  alpha = 0) # ridge: alpha = 0

plot(mRidge, xvar="lambda")

2.6 No penalisation of some parameters

Note, that we typically do not penalise the intercept. We can do this by introducing matrix

\[ \mathbf{D} = \left[\begin{array}{ccc}0& 0 \\ 0&\mathbf{I}\end{array}\right] \] And let \[ \mathbf{C} = \left[ \begin{array}{cc} 1 & \mathbf{1}\\\mathbf{1}&\mathbf{X}_{1\ldots p}\end{array}\right] \text{ and } \boldsymbol{\theta} =\left[\begin{array}{c}\beta_0\\ \boldsymbol{\beta}_{1\ldots p}\end{array}\right] \]

with \(\mathbf{X}_{1\ldots p}\) the matrix of the predictors for which the slope terms \(\boldsymbol {\beta}_{1\ldots p}\) are estimated.

The loss function then becomes

\[ L_\text{ridge}(\mathbf{Y},\boldsymbol{\beta},\lambda) = (\mathbf{Y}-\mathbf{C\theta})^T(\mathbf{Y}-\mathbf{C\theta}) + \lambda \boldsymbol{\beta}^T\mathbf{D}\boldsymbol{\theta}. \]

and the ridge estimator then becomes

\[ \hat{\boldsymbol{\beta}} = (\mathbf{C^TC}+\lambda \mathbf{D})^{-1} \mathbf{C^T Y} \]

3 Remarks

  • Use of R different penalties \(\lambda_1 \ldots \lambda_R\) as a proxy to allow for different random effect variances
  • Construct R predictions for phenotype with different penalties per block
  • Combine the predictors in a stacked predictor
  • I feel that this also allows deviations of the infinitesimal model
  • Danger that it is not well calibrated: indeed nominator

\[ \hat\sigma^2_\epsilon\tilde{\mathbf{x}}^T\tilde{\mathbf{x}} \leftrightarrow \tilde{\mathbf{x}}^T\hat{\mathbf{V}}^{-1}_{\text{LOCO},H_0}\tilde{\mathbf{x}} \]

LS0tCnRpdGxlOiAiVW5kZXIgdGhlIGhvb2Qgb2YgUkVHRU5JRSIKYXV0aG9yOiAiTGlldmVuIENsZW1lbnQiCmRhdGU6ICJzdGF0T21pY3MsIEdoZW50IFVuaXZlcnNpdHkgKGh0dHBzOi8vc3RhdG9taWNzLmdpdGh1Yi5pbykiCm91dHB1dDoKICAgIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoKICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgICBkZl9wcmludDogcGFnZWQKICAgICAgdGhlbWU6IGZsYXRseQogICAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICAgIHRvYzogdHJ1ZQogICAgICB0b2NfZmxvYXQ6IHRydWUKICAgICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICAgIGNvZGVfZm9sZGluZzogc2hvdwotLS0KCiMgS2V5IGlkZWEKCi0gQW5vdGhlciBmYXN0IGFwcHJvYWNoIHRvIGZpdCB0aGUgR1dBUy1MTU0uIAotIEJ1aWxkcyBvbiB0aGUgZGV2ZWxvcG1lbnRzIGluIEJPTFQtTE1NCgoxLiBQcm9qZWN0IGtub3duIGNvdmFyaWF0ZXMgJFxtYXRoYmZ7WH1fYyQgb3V0IAoyLiBUYWtlIEJPTFQtTE1NLWluZiBpZGVhIHRvIHVzZSBMT0NPLXJlc2lkdWFscyB1bmRlciB0aGUgbnVsbCBtb2RlbC4gCjMuIEV4cGxvaXQgbGluayBiZXR3ZWVuIG1peGVkIG1vZGVscyBhbmQgcmlkZ2UgcmVncmVzc2lvbiBmb3IgZmFzdCBhcHByb3hpbWF0aW9uIG9mIExPQ08tQkxVUCB1bmRlciAkSF8wJC4gCjQuIE1vZGVsIExPQ08gcmVzaWR1YWxzIHVzaW5nIGNhbmRpZGF0ZSBTTlAuIAoKJCQKXG1hdGhiZnt5fV9cdGV4dHtyZXNpZHVhbC1MT0NPLCBIXzB9ID0gXHRpbGRle1xtYXRoYmZ7eH19X1x0ZXh0e3Rlc3R9IFxib2xkc3ltYm9se1xiZXRhfV9cdGV4dHt0ZXN0fSArIFxlcHNpbG9uCiQkCgo1LiBVc2UgYSBzY29yZSB0ZXN0IHNpbWlsYXIgdG8gQk9MVC1MTU0taW5mIAoKJCQKVCA9IFxmcmFjeyhcdGlsZGV7XG1hdGhiZnt4fX1eVF9cdGV4dHt0ZXN0fVxtYXRoYmZ7eX1fXHRleHR7cmVzaWR1YWwtTE9DTywgSF8wfSleMn17XGhhdFxzaWdtYV4yX1xlcHNpbG9uXHRpbGRle1xtYXRoYmZ7eH19XlRcdGlsZGV7XG1hdGhiZnt4fX19CiQkCgo2LiBFc3RpbWF0ZSAkXGhhdHtcc2lnbWF9XjJfXGVwc2lsb24kIHVuZGVyIHRoZSBudWxsIG1vZGVsOiAKCiQkClxoYXRcc2lnbWFeMl9cZXBzaWxvbiA9IFxmcmFje1x2ZXJ0XHZlcnRcbWF0aGJme3l9X1x0ZXh0e3Jlc2lkdWFsLUxPQ08sIEhfMH1cdmVydFx2ZXJ0XjJfMn17Ti1DfQokJCAKCi0gSW4gY29udHJhc3QgdG8gQk9MVC1MTU0taW5mIG5vIGNhbGlicmF0aW9uIGZhY3RvciB1c2VkIGluIGRlbm9taW5hdG9yCi0gdGhleSAiZm91bmQgaW4gYXBwbGljYXRpb25zIHRoYXQgdGhlIHJlc3VsdHMgb2J0YWluZWQgdXNpbmcgdGhpcyBzaW1wbGUgZm9ybSBtYXRjaCB1cCBjbG9zZWx5IHRvIHRob3NlIHVzaW5nIGEgY2FsaWJyYXRpb24gZmFjdG9yIgoKKipLZXkgY29udHJpYnV0aW9uIGVmZmljaWVudGx5IG9idGFpbiAkXG1hdGhiZnt5fV9cdGV4dHtyZXNpZHVhbC1MT0NPLCBIXzB9JCoqCgotIENoZWNrIG1ldGhvZCBzZWN0aW9uIFtwYXBlcl0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE1ODgtMDIxLTAwODcwLTcucGRmKQotIEFzIHVzdWFsIGluIHB1YmxpY2F0aW9uczogdG8gdW5kZXJzdGFuZCB0aGUgbWV0aG9kcywgaXQgaXMgYWxsIGFib3V0IHRoZSBbc3VwcGxlbWVudF0oaHR0cHM6Ly9zdGF0aWMtY29udGVudC5zcHJpbmdlci5jb20vZXNtL2FydCUzQTEwLjEwMzglMkZzNDE1ODgtMDIxLTAwODcwLTcvTWVkaWFPYmplY3RzLzQxNTg4XzIwMjFfODcwX01PRVNNMV9FU00ucGRmKQoKCiMgSW50ZXJtZXp6byByaWRnZSByZWdyZXNzaW9uCgotIFBlbmFsaXplZCByZWdyZXNzaW9uIHVzZWZ1bCBpbiBhIGhpZ2ggZGltZW5zaW9uYWwgY29udGV4dCB3aGVuIHRoZSBudW1iZXIgb2YgY292YXJpYXRlcyBhcmUgbGFyZ2VyIHRoYW4gdGhlIG51bWJlciBvZiBzYW1wbGVzIChlLmcuIE0gPj4gTikKLSBXaGVuIGNvdmFyaWF0ZXMgYXJlIGhpZ2hseSBjb3JyZWxhdGVkCgpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPSBGQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ3JpZEV4dHJhKQpgYGAKCgogVGhlIHJpZGdlIHBhcmFtZXRlciBlc3RpbWF0b3IgaXMgZGVmaW5lZCBhcyB0aGUgcGFyYW1ldGVyICRcbWF0aGJmXGJldGEkIHRoYXQgbWluaW1pc2VzIHRoZSAqKnBlbmFsaXNlZCBsZWFzdCBzcXVhcmVzIGxvc3MgZnVuY3Rpb24qKgoKIFxbCiBcdGV4dHtTU0V9X1x0ZXh0e3Blbn09XFZlcnRcbWF0aGJme1l9IC0gXG1hdGhiZntYXGJldGF9XFZlcnRfMl4yICsgXGxhbWJkYSBcVmVydCBcYm9sZHN5bWJvbHtcYmV0YX0gXFZlcnRfMl4yClxdCgotICRcVmVydCBcYm9sZHN5bWJvbHtcYmV0YX0gXFZlcnRfMl4yPVxzdW1fe2o9MX1ecCBcYmV0YV9qXjIkIGlzIHRoZSAqKiRMXzIkIHBlbmFsdHkgdGVybSoqCgotICRcbGFtYmRhPjAkIGlzIHRoZSBwZW5hbHR5IHBhcmFtZXRlciAodG8gYmUgY2hvc2VuIGJ5IHRoZSB1c2VyKS4KCk5vdGUsIHRoYXQgdGhhdCBpcyBlcXVpdmFsZW50IHRvIG1pbmltaXppbmcKXFsKXFZlcnRcbWF0aGJme1l9IC0gXG1hdGhiZntYXGJldGF9XFZlcnRfMl4yIFx0ZXh0eyBzdWJqZWN0IHRvIH0gXFZlcnQgXGJvbGRzeW1ib2x7XGJldGF9XFZlcnReMl8yXGxlcSBzClxdCgpOb3RlLCB0aGF0ICRzJCBoYXMgYSBvbmUtdG8tb25lIGNvcnJlc3BvbmRlbmNlIHdpdGggJFxsYW1iZGEkCgojIyBHcmFwaGljYWwgaW50ZXJwcmV0YXRpb24KCmBgYHtyIGVjaG8gPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkoZ2dmb3JjZSkKbGlicmFyeShsYXRleDJleHApCmxpYnJhcnkoZ3JpZEV4dHJhKQoKcDEgPC0gZ2dwbG90KCkgKwogIGdlb21fZWxsaXBzZShhZXMoeDAgPSA0LCB5MCA9IDExLCBhID0gMTAsIGIgPSAzLCBhbmdsZSA9IHBpIC8gNCkpICsKICBnZW9tX2VsbGlwc2UoYWVzKHgwID0gNCwgeTAgPSAxMSwgYSA9IDUsIGIgPSAxLjUsIGFuZ2xlID0gcGkgLyA0KSkgKwogIHhsaW0oLTEyLjUsIDEyLjUpICsKICB5bGltKC01LCAyMCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSA0LCB5ID0gMTEpKSArCiAgYW5ub3RhdGUoInRleHQiLCBsYWJlbCA9IFRlWCgiJChcXGhhdHtcXGJldGF9XzFee29sc30sIFxcaGF0e1xcYmV0YX1fMl57b2xzfSkkIiksIHggPSAtNSwgeSA9IDE1LCBzaXplID0gNiwgcGFyc2UgPSBUUlVFKSArCiAgeGxhYihUZVgoIiRcXGJldGFfMSQiKSkgKwogIHlsYWIoVGVYKCIkXFxiZXRhXzIkIikpICsKICBnZW9tX3NlZ21lbnQoCiAgICBhZXMoeCA9IC01LCB5ID0gMTIuNSwgeGVuZCA9IDMuNywgeWVuZCA9IDExLjMpLAogICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMjUsICJjbSIpKQogICAgKSArCiAgY29vcmRfZml4ZWQoKQoKcFJpZGdlIDwtIHAxICsKICBnZW9tX2NpcmNsZShhZXMoeDAgPSAwLCB5MCA9IDAsIHIgPSAzLjkpICwgY29sb3IgPSAicmVkIikgKwogIGdlb21fcG9pbnQoYWVzKHggPSAtMS4xLCB5ID0gMy43NSksIGNvbG9yID0gInJlZCIpICsKICBhbm5vdGF0ZSgidGV4dCIsIGxhYmVsID0gVGVYKCIkKFxcaGF0e1xcYmV0YX1fMV57cmlkZ2V9LCBcXGhhdHtcXGJldGF9XzJee3JpZGdlfSkkIiksIHggPSAtOC4xLCB5ID0gNC40NSwgc2l6ZSA9IDYsIHBhcnNlID0gVFJVRSwgY29sb3IgPSAicmVkIikgKwogIGdndGl0bGUoIlJpZGdlIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGNvbG9yID0gImdyZXkiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgY29sb3IgPSAiZ3JleSIpICsKICB0aGVtZV9taW5pbWFsKCkKCnBSaWRnZQpgYGAKCi0gUmlkZ2UgZXN0aW1hdG9yIGNhbiBzd2l0Y2ggc2lnbiBhcyBjb21wYXJlZCB0byBPTFMuIAoKIyMgRXN0aW1hdG9yCgpUaGUgbG9zcyBmdW5jdGlvbiB0byBiZSBtaW5pbWlzZWQgaXMKICBcWwogICBMX1x0ZXh0e3JpZGdlfShcbWF0aGJme1l9LFxib2xkc3ltYm9se1xiZXRhfSxcbGFtYmRhKSA9IFx0ZXh0e1NTRX1fXHRleHR7cGVufT1cVmVydFxtYXRoYmZ7WX0gLSBcbWF0aGJme1hcYmV0YX1cVmVydF8yXjIgKyBcbGFtYmRhIFxWZXJ0IFxib2xkc3ltYm9se1xiZXRhfSBcVmVydF8yXjIuCiBcXQogCiBGaXJzdCB3ZSByZS1leHByZXNzIHRoZSBsb3NzIGZ1bmN0aW9uIGluIG1hdHJpeCBub3RhdGlvbjoKIFxbCiAgIExfXHRleHR7cmlkZ2V9KFxtYXRoYmZ7WX0sXGJvbGRzeW1ib2x7XGJldGF9LFxsYW1iZGEpID0gKFxtYXRoYmZ7WX0tXG1hdGhiZntYXGJldGF9KV5UKFxtYXRoYmZ7WX0tXG1hdGhiZntYXGJldGF9KSArIFxsYW1iZGEgXGJvbGRzeW1ib2x7XGJldGF9XlRcYm9sZHN5bWJvbHtcYmV0YX0uCiBcXQogCiBTb2x2aW5nICRMX1x0ZXh0e3JpZGdlfShcbWF0aGJme1l9LFxib2xkc3ltYm9se1xiZXRhfSxcbGFtYmRhKT0wJCBnaXZlcwogCiBcWwogXGJlZ2lue2FycmF5fXtyY2x9CiAgIFxmcmFje1xwYXJ0aWFsfXtccGFydGlhbCBcYm9sZHN5bWJvbHtcYmV0YX19TF9cdGV4dHtyaWRnZX0oXG1hdGhiZntZfSxcYm9sZHN5bWJvbHtcYmV0YX0sXGxhbWJkYSkgIAogICAmPSYgMCBcXFxcCi0yXG1hdGhiZntYfV5UKFxtYXRoYmZ7WX0tXG1hdGhiZntYXGJldGF9KSsyXGxhbWJkYVxib2xkc3ltYm9se1xiZXRhfSAmPSYgMFxcXFwKXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19ICY9JiAoXG1hdGhiZntYXlRYfStcbGFtYmRhIFxtYXRoYmZ7SX0pXnstMX0gXG1hdGhiZntYXlQgWX0KCiAgIFxlbmR7YXJyYXl9ClxdCgpJdCBjYW4gYmUgc2hvd24gdGhhdCAkKFxtYXRoYmZ7WF5UWH0rXGxhbWJkYSBcbWF0aGJme0l9KSQgaXMgYWx3YXlzIG9mIHJhbmsgJHAkIGlmICRcbGFtYmRhPjAkLgoKSGVuY2UsICQoXG1hdGhiZntYXlRYfStcbGFtYmRhIFxtYXRoYmZ7SX0pJCBpcyBpbnZlcnRpYmxlIGFuZCAkXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19JCBleGlzdHMgZXZlbiBpZiAkcD4+Pm4kLgoKIyMgTGluayB3aXRoIFNWRAoKV3JpdGUgdGhlIFNWRCBvZiAkXG1hdGhiZntYfSQgKCRwPk4kKSBhcwpcWwogICBcbWF0aGJme1h9ID0gXHN1bV97aT0xfV5OIFxkZWx0YV9pIFxtYXRoYmZ7dX1faSBcbWF0aGJme3Z9X2leVCA9IFxzdW1fe2k9MX1ecCBcZGVsdGFfaSBcbWF0aGJme3V9X2kgXG1hdGhiZnt2fV9pXlQgID0gXG1hdGhiZntVfVxib2xkc3ltYm9se1xEZWx0YX0gXG1hdGhiZntWfV5UICwKXF0Kd2l0aAoKLSAkXGRlbHRhX3tuKzF9PVxkZWx0YV97bisyfT0gXGNkb3RzID0gXGRlbHRhX3A9MCQKCi0gJFxib2xkc3ltYm9se1xEZWx0YX0kIGEgJHBcdGltZXMgcCQgZGlhZ29uYWwgbWF0cml4IG9mIHRoZSAkXGRlbHRhXzEsXGxkb3RzLCBcZGVsdGFfcCQKCi0gICRcbWF0aGJme1V9JCBhbiAkblx0aW1lcyBwJCBtYXRyaXggYW5kICRcbWF0aGJme1Z9JCBhICRwIFx0aW1lcyBwJCBtYXRyaXguIE5vdGUgdGhhdCBvbmx5IHRoZSBmaXJzdCAkbiQgY29sdW1ucyBvZiAkXG1hdGhiZntVfSQgYW5kICRcbWF0aGJme1Z9JCBhcmUgaW5mb3JtYXRpdmUuCgpXaXRoIHRoZSBTVkQgb2YgJFxtYXRoYmZ7WH0kIHdlIHdyaXRlCiBcWwogICBcbWF0aGJme1h9XlRcbWF0aGJme1h9ID0gXG1hdGhiZntWfVxib2xkc3ltYm9se1xEZWx0YQogICAgIH1eMlxtYXRoYmZ7Vn1eVC4KIFxdCiBUaGUgaW52ZXJzZSBvZiAkXG1hdGhiZntYfV5UXG1hdGhiZntYfSQgaXMgdGhlbiBnaXZlbiBieQogXFsKICAgKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX0gPSBcbWF0aGJme1Z9XGJvbGRzeW1ib2x7XERlbHRhfV57LTJ9XG1hdGhiZntWfV5ULgogXF0KIFNpbmNlICRcYm9sZHN5bWJvbHtcRGVsdGF9JCBoYXMgJFxkZWx0YV97bisxfT1cZGVsdGFfe24rMn09IFxjZG90cyA9IFxkZWx0YV9wPTAkLCBpdCBpcyBub3QgaW52ZXJ0aWJsZS4KCgpOb3RlIHRoYXQgaXQgY2FuIGJlIHNob3duIHRoYXQKXFsKICBcbWF0aGJme1heVFh9K1xsYW1iZGEgXG1hdGhiZntJfSA9IFxtYXRoYmZ7Vn0gKFxib2xkc3ltYm9se1xEZWx0YX1eMitcbGFtYmRhIFxtYXRoYmZ7SX0pIFxtYXRoYmZ7Vn1eVCAsClxdCmkuZS4gYWRkaW5nIGEgY29uc3RhbnQgdG8gdGhlIGRpYWdvbmFsIGVsZW1lbnRzIGRvZXMgbm90IGFmZmVjdCB0aGUgZWlnZW52ZWN0b3JzLCBhbmQgYWxsIGVpZ2VudmFsdWVzIGFyZSBpbmNyZWFzZWQgYnkgdGhpcyBjb25zdGFudC4gICAKJFxsb25ncmlnaHRhcnJvdyQgemVybyBlaWdlbnZhbHVlcyBiZWNvbWUgJFxsYW1iZGEkLgoKSGVuY2UsClxbCiAgKFxtYXRoYmZ7WF5UWH0rXGxhbWJkYSBcbWF0aGJme0l9KV57LTF9ID0gXG1hdGhiZntWfSAoXGJvbGRzeW1ib2x7XERlbHRhfV4yK1xsYW1iZGEgXG1hdGhiZntJfSleey0xfSBcbWF0aGJme1Z9XlQgLApcXQp3aGljaCBjYW4gYmUgY29tcHV0ZWQgZXZlbiB3aGVuIHNvbWUgZWlnZW52YWx1ZXMgaW4gJFxib2xkc3ltYm9se1xEZWx0YX1eMiQgYXJlIHplcm8uCgpOb3RlLCB0aGF0IGZvciBoaWdoIGRpbWVuc2lvbmFsIGRhdGEgKCRwPj4+TiQpIG1hbnkgZWlnZW52YWx1ZXMgYXJlIHplcm8gYmVjYXVzZSAkXG1hdGhiZntYXlRYfSQgaXMgYSAkcCBcdGltZXMgcCQgbWF0cml4IGFuZCBoYXMgcmFuayAkTiQuCgojIyBQcm9wZXJ0aWVzIHJpZGdlCgotIFRoZSBSaWRnZSBlc3RpbWF0b3IgaXMgYmlhc2VkISBUaGUgJFxib2xkc3ltYm9se1xiZXRhfSQgYXJlIHNocnVua2VuIHRvIHplcm8hClxiZWdpbntlcW5hcnJheX0KIFx0ZXh0e0V9W1xoYXR7XGJvbGRzeW1ib2x7XGJldGF9fV0gJj0mIChcbWF0aGJme1heVFh9K1xsYW1iZGEgXG1hdGhiZntJfSleey0xfSBcbWF0aGJme1h9XlQgXHRleHR7RX1bXG1hdGhiZntZfV1cXFxcCiY9JiAoXG1hdGhiZntYfV5UXG1hdGhiZntYfStcbGFtYmRhIFxtYXRoYmZ7SX0pXnstMX0gXG1hdGhiZntYfV5UIFxtYXRoYmZ7WH1cYm9sZHN5bWJvbHtcYmV0YX1cXApcZW5ke2VxbmFycmF5fQoKLSBOb3RlLCB0aGF0IHRoZSBzaHJpbmthZ2UgaXMgbGFyZ2VyIGluIHRoZSBkaXJlY3Rpb24gb2YgdGhlIHNtYWxsZXIgZWlnZW52YWx1ZXMuCgpcYmVnaW57ZXFuYXJyYXl9Clx0ZXh0e0V9W1xoYXR7XGJvbGRzeW1ib2x7XGJldGF9fV0mPSZcbWF0aGJme1Z9IChcYm9sZHN5bWJvbHtcRGVsdGF9XjIrXGxhbWJkYSBcbWF0aGJme0l9KV57LTF9IFxtYXRoYmZ7Vn1eVCBcbWF0aGJme1Z9IFxib2xkc3ltYm9se1xEZWx0YX1eMiBcbWF0aGJme1Z9XlRcYm9sZHN5bWJvbHtcYmV0YX1cXFxcCiY9JlxtYXRoYmZ7Vn0gKFxib2xkc3ltYm9se1xEZWx0YX1eMitcbGFtYmRhIFxtYXRoYmZ7SX0pXnstMX0gXGJvbGRzeW1ib2x7XERlbHRhfV4yIFxtYXRoYmZ7Vn1eVFxib2xkc3ltYm9se1xiZXRhfVxcXFwKJj0mIFxtYXRoYmZ7Vn0KXGxlZnRbXGJlZ2lue2FycmF5fXtjY2N9ClxmcmFje1xkZWx0YV8xXjJ9e1xkZWx0YV8xXjIrXGxhbWJkYX0mXGxkb3RzJjAgXFwKJlx2ZG90cyZcXAowJlxsZG90cyZcZnJhY3tcZGVsdGFfcl4yfXtcZGVsdGFfcl4yK1xsYW1iZGF9ClxlbmR7YXJyYXl9XHJpZ2h0XQpcbWF0aGJme1Z9XlRcYm9sZHN5bWJvbHtcYmV0YX0KXGVuZHtlcW5hcnJheX0KCi0gUmlkZ2UgcmVncmVzc2lvbiBjYW4gbGVhZCB0byBwYXJhbWV0ZXJzIHRoYXQgc3dpdGNoIHNpZ24gaWYgcGVuYWxpdHkgaW5jcmVhc2VzCgoKIyMgVG94aWNvZ2Vub21pY3MgZXhhbXBsZQoKLSBOID0gMzAgY2hlbWljYWwgY29tcG91bmRzIGhhdmUgYmVlbiBzY3JlZW5lZCBmb3IgdG94aWNpdHkKCi0gQmlvYXNzYXkgZGF0YSBvbiB0b3hpY2l0eSBzY3JlZW5pbmcKCi0gR2VuZSBleHByZXNzaW9ucyBpbiBhIGxpdmVyIGNlbGwgbGluZSBhcmUgcHJvZmlsZWQgZm9yIGVhY2ggY29tcG91bmQgKE0gPSA0MDAwIGdlbmVzKQoKLSBQcmVkaWN0IEJpb2Fzc2F5IHNjb3JlIGluIGZ1bmN0aW9uIG9mIGdlbmUgZXhwcmVzc2lvbi4KCmBgYHtyfQp0b3hEYXRhIDwtIHJlYWRfY3N2KAogICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc3RhdE9taWNzL0hEQTIwMjAvZGF0YS90b3hEYXRhQ2VudGVyZWQuY3N2IiwKICBjb2xfdHlwZXMgPSBjb2xzKCkKKQpkaW0odG94RGF0YSkKCmxtRml0IDwtIGxtIChCQSB+LiAsIHRveERhdGEpCmxtRml0ICU+JSAgCiAgY29lZiAlPiUgCiAgaGVhZCg0MCkKCmxtRml0ICU+JSAKICBjb2VmICU+JSAKICBpcy5uYSAlPiUgCiAgc3VtCgpsbUZpdCAlPiUgCiAgY29lZiAlPiUgCiAgaXMubmEgJT4lIAogIGAhYCAlPiUgCiAgc3VtIApgYGAKCmBgYHtyfQpsaWJyYXJ5KGdsbW5ldCkKbVJpZGdlIDwtIGdsbW5ldCgKICB4ID0gdG94RGF0YVssLTFdICU+JQogICAgYXMubWF0cml4LAogIHkgPSB0b3hEYXRhICU+JQogICAgcHVsbChCQSksCiAgYWxwaGEgPSAwKSAjIHJpZGdlOiBhbHBoYSA9IDAKCnBsb3QobVJpZGdlLCB4dmFyPSJsYW1iZGEiKQpgYGAKCiMjIE5vIHBlbmFsaXNhdGlvbiBvZiBzb21lIHBhcmFtZXRlcnMgCgogTm90ZSwgdGhhdCB3ZSB0eXBpY2FsbHkgZG8gbm90IHBlbmFsaXNlIHRoZSBpbnRlcmNlcHQuIFdlIGNhbiBkbyB0aGlzIGJ5IGludHJvZHVjaW5nIG1hdHJpeCAKIAogJCQKIFxtYXRoYmZ7RH0gPSBcbGVmdFtcYmVnaW57YXJyYXl9e2NjY30wJiAwIFxcIDAmXG1hdGhiZntJfVxlbmR7YXJyYXl9XHJpZ2h0XQogJCQKIEFuZCBsZXQgCiAkJAogXG1hdGhiZntDfSA9IFxsZWZ0WyBcYmVnaW57YXJyYXl9e2NjfSAxICYgXG1hdGhiZnsxfVxcXG1hdGhiZnsxfSZcbWF0aGJme1h9X3sxXGxkb3RzIHB9XGVuZHthcnJheX1ccmlnaHRdIFx0ZXh0eyBhbmQgfSBcYm9sZHN5bWJvbHtcdGhldGF9ID1cbGVmdFtcYmVnaW57YXJyYXl9e2N9XGJldGFfMFxcCiBcYm9sZHN5bWJvbHtcYmV0YX1fezFcbGRvdHMgcH1cZW5ke2FycmF5fVxyaWdodF0KICQkIAogCndpdGggJFxtYXRoYmZ7WH1fezFcbGRvdHMgcH0kIHRoZSBtYXRyaXggb2YgdGhlIHByZWRpY3RvcnMgZm9yIHdoaWNoIHRoZSBzbG9wZSB0ZXJtcyAkXGJvbGRzeW1ib2wge1xiZXRhfV97MVxsZG90cyBwfSQgYXJlIGVzdGltYXRlZC4gCiAKIFRoZSBsb3NzIGZ1bmN0aW9uIHRoZW4gYmVjb21lcwogCiAkJAogTF9cdGV4dHtyaWRnZX0oXG1hdGhiZntZfSxcYm9sZHN5bWJvbHtcYmV0YX0sXGxhbWJkYSkgPSAoXG1hdGhiZntZfS1cbWF0aGJme0NcdGhldGF9KV5UKFxtYXRoYmZ7WX0tXG1hdGhiZntDXHRoZXRhfSkgKyBcbGFtYmRhIFxib2xkc3ltYm9se1xiZXRhfV5UXG1hdGhiZntEfVxib2xkc3ltYm9se1x0aGV0YX0uCiAkJAogCmFuZCB0aGUgcmlkZ2UgZXN0aW1hdG9yIHRoZW4gYmVjb21lcwogCiAkJApcaGF0e1xib2xkc3ltYm9se1xiZXRhfX0gPSAoXG1hdGhiZntDXlRDfStcbGFtYmRhIFxtYXRoYmZ7RH0pXnstMX0gXG1hdGhiZntDXlQgWX0KJCQKCgojIyBMaW5rIExNTQoKQWRkIEdhdXNzaWFuIHByaW9yIG9uIHRoZSBtb2RlbCBwYXJhbWV0ZXJzL3NwZWNpZnkgdGhlIGZpeGVkIGVmZmVjdHMgYXMgcmFuZG9tIGVmZmVjdHMgCgokJApcYmVnaW57YXJyYXl9e2NjbH0KXG1hdGhiZntZfSAmPSYgXG1hdGhiZnsxfVxiZXRhXzAgKyBcbWF0aGJme1h9X3sxXGxkb3RzIHB9IFxib2xkc3ltYm9se1xiZXRhfV97MVxsZG90cyBwfSArIFxib2xkc3ltYm9se1xlcHNpbG9ufVxcClxib2xkc3ltYm9se1xiZXRhfV97MVxsZG90cyBwfSAmXHNpbSYgXHRleHR7TVZOfSgwLFxtYXRoYmZ7SX1cc2lnbWFeMl9cYmV0YSkgXFwKXGJvbGRzeW1ib2x7XGVwc2lsb259ICZcc2ltJiBcdGV4dHtNVk59KDAsXG1hdGhiZntJfVxzaWdtYV4yX1xlcHNpbG9uKSBcXApcZW5ke2FycmF5fQokJCAKCk5vdGUgdGhhdCB3ZSBrbm93IGZyb20gTE1NIHRoZW9yeSB0aGF0IHRoZSBCTFVQIGlzCgokJAogXGhhdHtcYm9sZHN5bWJvbHtcdGhldGF9fSA9ICAoXG1hdGhiZntDfV5UXG1hdGhiZntDfSArIFxzaWdtYV4yX1xlcHNpbG9uIEgpXnstMX1cbWF0aGJme0N9XlRcbWF0aGJme1l9CiQkCndpdGgKCiQkCiBcbWF0aGJme0h9PVxsZWZ0W1xiZWdpbnthcnJheX17Y2N9ClxtYXRoYmZ7MH0mXG1hdGhiZnswfVxcClxtYXRoYmZ7MH0mXG1hdGhiZntHfV57LTF9ClxlbmR7YXJyYXl9XHJpZ2h0XSA9IFxsZWZ0W1xiZWdpbnthcnJheX17Y2N9ClxtYXRoYmZ7MH0mXG1hdGhiZnswfVxcClxtYXRoYmZ7MH0mXHNpZ21hXnstMn1fe1xiZXRhfVxtYXRoYmZ7SX0KXGVuZHthcnJheX1ccmlnaHRdID0gXHNpZ21hXnstMn1fXGJldGEgXG1hdGhiZntEfQokJAoKU28gdGhlIEJMVVAgcmVkdWNlcyB0byAKCiQkCiBcaGF0e1xib2xkc3ltYm9se1x0aGV0YX19ID0gIChcbWF0aGJme0N9XlRcbWF0aGJme0N9ICsgXGZyYWN7XHNpZ21hXjJfXGVwc2lsb259e1xzaWdtYV4yX1xiZXRhfSBcbWF0aGJme0R9KV57LTF9XG1hdGhiZntDfV5UXG1hdGhiZntZfQokJAoKd2hpY2ggaXMgZXF1aXZhbGVudCB0byB0aGUgcmlkZ2Ugc29sdXRpb24hIAoKLSAkXGZyYWN7XHNpZ21hXjJfXGVwc2lsb259e1xzaWdtYV4yX1xiZXRhfSQgcGxheXMgdGhlIHJvbGUgb2YgIHJpZGdlIHBlbmFsdHkgJFxsYW1iZGEkIGluIHJpZGdlIHJlZ3Jlc3Npb24uIAoKLSB3ZSBjYW4gZXhwbG9pdCB0aGUgbWl4ZWQgbW9kZWwgbWFjaGluZXJ5IHRvIHBlcmZvcm0gcmlkZ2UgcmVncmVzc2lvbiBhbmQgdG8gZXN0aW1hdGUgdGhlIHBlbmFsdHkgcGFyYW1ldGVyIHVzaW5nIHZhcmlhbmNlIGNvbXBvbmVudHMuIAoKCiRccmlnaHRhcnJvdyQgUmVnZW5pZSBpcyBleHBsb2l0aW5nIHRoaXMgbGluayB0byBhdm9pZCBjb21wdXRhdGlvbmFsIGNvbXBsZXhpdHkgb2YgZml0dGluZyBMTU1zLiAgICAKJFxyaWdodGFycm93JCBSZXZpc2l0IHRvIFtzdXBwbGVtZW50XShodHRwczovL3N0YXRpYy1jb250ZW50LnNwcmluZ2VyLmNvbS9lc20vYXJ0JTNBMTAuMTAzOCUyRnM0MTU4OC0wMjEtMDA4NzAtNy9NZWRpYU9iamVjdHMvNDE1ODhfMjAyMV84NzBfTU9FU00xX0VTTS5wZGYpCgoKIyBSZW1hcmtzIAoKLSBVc2Ugb2YgUiBkaWZmZXJlbnQgcGVuYWx0aWVzICRcbGFtYmRhXzEgXGxkb3RzIFxsYW1iZGFfUiQgYXMgYSBwcm94eSB0byBhbGxvdyBmb3IgZGlmZmVyZW50IHJhbmRvbSBlZmZlY3QgdmFyaWFuY2VzCi0gQ29uc3RydWN0IFIgcHJlZGljdGlvbnMgZm9yIHBoZW5vdHlwZSB3aXRoIGRpZmZlcmVudCBwZW5hbHRpZXMgcGVyIGJsb2NrIAotIENvbWJpbmUgdGhlIHByZWRpY3RvcnMgaW4gYSBzdGFja2VkIHByZWRpY3RvciAKLSBJIGZlZWwgdGhhdCB0aGlzIGFsc28gYWxsb3dzIGRldmlhdGlvbnMgb2YgdGhlIGluZmluaXRlc2ltYWwgbW9kZWwKLSBEYW5nZXIgdGhhdCBpdCBpcyBub3Qgd2VsbCBjYWxpYnJhdGVkOiBpbmRlZWQgbm9taW5hdG9yCgokJApcaGF0XHNpZ21hXjJfXGVwc2lsb25cdGlsZGV7XG1hdGhiZnt4fX1eVFx0aWxkZXtcbWF0aGJme3h9fSBcbGVmdHJpZ2h0YXJyb3cgXHRpbGRle1xtYXRoYmZ7eH19XlRcaGF0e1xtYXRoYmZ7Vn19XnstMX1fe1x0ZXh0e0xPQ099LEhfMH1cdGlsZGV7XG1hdGhiZnt4fX0KJCQKCg==