1 Linear mixed model for GWAS

1.1 Specification

\[ \tag{1} \mathbf{Y} = \mathbf{x}_\text{test}\beta_\text{test} + \mathbf{X}_c\boldsymbol{\beta}_c + \mathbf{Z}_{GRM}\mathbf{u} +\boldsymbol{\epsilon} \] with

  • \(\mathbf{Y}\) an \(N\times1\) vector of the phenotype
  • \(\mathbf{x}_\text{test}\) an \(N\times1\) vector with the genotype for the candidate SNP
  • \(\beta_\text{test}\) the association of candidate SNP and the phenotype
  • \(\mathbf{X}_c\) an \(N\times p\) matrix with the covariate pattern for \(C\) known covariates (vector of ones (intercept), age, gender, batch,…)
  • \(\boldsymbol{\beta}_c\) the \(p\times 1\) vector of parameters modeling the association of the p covariates and the phenotype.
  • \(\mathbf{Z}\) an \(N\times M\) genetic relationship matrix (GRM) with all normalised genotypes
  • \(\mathbf{u}\) an \(M\times 1\) vector with i.i.d. random effect for each SNP \(\mathbf{u}\sim \text{MVN}(0,\mathbf{I}\sigma_u^2)\)
  • \(\boldsymbol{\epsilon}\) an \(N\times 1\) vector with environmental residuals that are assumed to be independent of \(\mathbf{u}\) and i.i.d. \(\boldsymbol{\epsilon}\sim \text{MVN}(0,\mathbf{I}\sigma_\epsilon^2)\)

Random effects are used to model the correlation structure in the data. They imply a certain covariance structure of \(\mathbf{y}\)

1.2 Covariance structure

Covariance structure of \(\mathbf{y}\) implied by GWAS mixed model:

\[ \begin{array}{ccl} \text{var}\left[\mathbf{Y}\right] &=& \text{var}\left[\mathbf{x}_\text{test}\beta_\text{test} + \mathbf{X}_c\boldsymbol{\beta}_c + \mathbf{Z}_\text{GRM}\mathbf{u} +\boldsymbol{\epsilon}\right]\\\\ &\updownarrow& \mathbf{u} \perp \boldsymbol{\epsilon}\\\\ &=& \text{var}[\mathbf{Z}_\text{GRM}\mathbf{u}] + \text{var}[\boldsymbol{\epsilon}]\\\\ &=&\mathbf{Z}_\text{GRM}\text{var}[\mathbf{u}]\mathbf{Z}_\text{GRM}^T + \mathbf{I} \sigma^2\\\\ &=&\mathbf{Z}_\text{GRM}\mathbf{I}\sigma^2_u\mathbf{Z}_\text{GRM}^T + \mathbf{I} \sigma^2_\epsilon \\\\ &=&\mathbf{Z}_\text{GRM}\mathbf{Z}_\text{GRM}^T \sigma^2_u+ \mathbf{I} \sigma^2_\epsilon \end{array} \]

Note that the model is often also written in another way:

\[ \tag{1} \mathbf{Y} = \mathbf{x}_\text{test}\beta_\text{test} + \mathbf{X}_c\boldsymbol{\beta}_c + \mathbf{g} +\boldsymbol{\epsilon} \]

  • with \(\mathbf{g} \sim \text{MVN}(\mathbf{0},\mathbf{K}\sigma^2_g)\)

  • \(\mathbf{K}\) the \(N \times N\) empirical kinship matrix

\[ \mathbf{K} = \frac{\mathbf{Z}_\text{GRM}\mathbf{Z}^T_\text{GRM}}{M} \]

  • \(\sigma_g^2\) the polygenic variance \(\sigma_g^2=M\sigma_u^2\)

1.3 Main advantages of LMM method

  1. Better control of false positive associations by correcting for population or relatedness structure
  2. An increase in power:
  • Through the correction for this structure.
  • by conditioning on associated loci other than the candidate locus.

1.4 Pitfalls of LMM

  1. Computational complexity:
  • \(M > 500.000\), \(N > 70000\)
  • Building the GRM (\(M \times M\) matrix)
  • Estimating the mean and variance components for each of the \(M\) candidate SNP!
  • Association statistics for each variant (for each SNP!)
  1. Loss in power when the candidate marker is included in the GRM

  2. Using a small subset of markers in the GRM can compromise correction for stratification

2 Bolt LMM

What does projecting out covariates \(\mathbf{X}_c\) mean?

2.1 Intermezzo Projections

Linear regression and least squares are projections.

2.1.1 Toy-Data

Consider the following toy-dataset with 3 observation (X,Y):

library(tidyverse)
data <- data.frame(x=1:3,y=c(1,2,2))
data

2.1.2 Scalar model form

  • Consider a vector of predictors \(\mathbf{x}=(x_1,\ldots,x_p)\) and
  • a real-valued response \(Y\)
  • then the linear regression model can be written as \[ y_i=\beta_0+\sum\limits_{j=1}^p x_{ij}\beta_j + \epsilon \]

Scalar model for the toy dataset

\[ y_i=\beta_0+\beta_1x + \epsilon_i \]

If we write the model for each observation:

\[ \begin{array} {lcl} 1 &=& \beta_0+\beta_1 1 + \epsilon_1 \\ 2 &=& \beta_0 + \beta_1 2 + \epsilon_2 \\ 2 &=& \beta_0+\beta_1 3+ \epsilon_3 \\ \end{array} \]

2.1.3 Vector/Matrix form

  • \(n\) observations \((\mathbf{x}_1,y_1) \ldots (\mathbf{x}_n,y_n)\) with \(\mathbf{x}_1^T=[\begin{array}{cccc} 1& x_1& \ldots& x_p\end{array}]\)
  • Regression in matrix notation \[\mathbf{Y}=\mathbf{X\beta} + \mathbf{\epsilon}\] with \(\mathbf{Y}=\left[\begin{array}{c}y_1\\ \vdots\\y_n\end{array}\right]\), \(\mathbf{X}=\left[\begin{array}{cccc} 1&x_{11}&\ldots&x_{1p}\\ \vdots&\vdots&&\vdots\\ 1&x_{n1}&\ldots&x_{np} \end{array}\right]\) or \(\mathbf{X}=\left[\begin{array}{c} \mathbf{x}_1^T\\\vdots\\\mathbf{x}_n^T\end{array}\right]\), \(\boldsymbol{\beta}=\left[\begin{array}{c}\beta_0\\ \vdots\\ \beta_p\end{array}\right]\) and \(\mathbf{\epsilon}=\left[\begin{array}{c} \epsilon_1 \\ \vdots \\ \epsilon_n\end{array}\right]\)

Application to toy dataset:

\[ \mathbf{Y} = \mathbf{X}\boldsymbol{\beta}+\boldsymbol{\epsilon} \]

with

\[ \mathbf{Y}=\left[ \begin{array}{c} 1\\ 2\\ 2\\ \end{array}\right], \quad \mathbf{X}= \left[ \begin{array}{cc} 1&1\\ 1&2\\ 1&3\\ \end{array} \right], \quad \boldsymbol{\beta} = \left[ \begin{array}{c} \beta_0\\ \beta_1\\ \end{array} \right] \quad \text{and} \quad \boldsymbol{\epsilon}= \left[ \begin{array}{c} \epsilon_1\\ \epsilon_2\\ \epsilon_3 \end{array} \right] \]

2.1.4 Interpretation

From the linear regression we get \[ E[Y \mid \mathbf{x}] = \mathbf{x}^T\boldsymbol{\beta} . \]

  • Hence, the \(\beta\) parameters relate the predictor \(\mathbf{x}\) to the mean outcome.
  • If we know the covariate pattern \(\mathbf{x}\) we can use the model to predict \(Y\).

For a model with a single predictor, e.g. Y is BMI and \(x_1\) is bloodpressure (BP) we obtain

\[ E[Y \mid x_1] = \beta_0 + \beta_1 x_1 \] and \[ \begin{array}{rccl} \beta_1 &=& E[Y\mid x_1=b+1] &- E[Y\mid x_1=b] \\ &=& \beta_0 + \beta_1 (b+1)& - \beta_0 - \beta_1 b \end{array} \]

  • The parameter \(\beta_1\) has an interpretation as the average difference in BMI between subjects that differ with 1mmHG in BP.

  • The parameter \(\beta_1\) does not say much about individual outcomes. The residual variance \(\sigma^2\) determines how much individual outcomes vary about the mean outcome.

For a model with multiple predictors: BP (\(x_1\)), sex (\(x_2\)), age (\(x_3\)) we obtain

\[ E[Y \mid x_1, x_2, x_3] = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \beta_3 x_3 \] and \[ \begin{array}{rcl} \beta_1 &=& E[Y\mid x_1=b+1, x_2=s, x_3 = a] - E[Y\mid x_1=b, x_2=s, x_3 = a]\\\\ &=& \beta_0 + \beta_1 (b+1) + \beta_3 s + \beta_3 a - \beta_0 - \beta_1 b - \beta_3 s - \beta_3 a. \end{array} \]

  • The parameter \(\beta_1\) has an interpretation as the average difference in BMI between subjects of the same sex and the same age that differ 1 mmHg in BP.
  • or average difference in BMI between subjects that differ 1 mmHg in BP while controlling for age and gender.

The \(\boldsymbol{\beta}\) parameters are used to measure association, but a \(\boldsymbol{\beta} \neq \mathbf{0}\) does not necessarily mean that the model will give good predictions.

\[ \hat Y = \mathbf{x}^T \hat \beta \] Model fit and predictions based on the toy dataset

2.1.5 Estimation of \(\boldsymbol{\beta}\)

2.1.5.1 Ordinary Least Squares (OLS)

  • Minimize the sum of squares of the errors \[\begin{eqnarray*} \text{SSE}(\boldsymbol{\beta})&=&\sum\limits_{i=1}^n e^2_i\\\\ &=&\sum\limits_{i=1}^n \left(y_i-\beta_0-\sum\limits_{j=1}^p x_{ij}\beta_j\right)^2 \end{eqnarray*}\]

  • or in matrix notation \[\begin{eqnarray*} \text{SSE}(\boldsymbol{\beta})&=&\mathbf{e}^T\mathbf{e}\\\\ &=& \left[\begin{array}{ccc} e_1 &\ldots& e_n \end{array}\right]\left[\begin{array}{c}e_1\\\vdots\\e_n\end{array}\right]\\ &=& e_1^2 + e_2^2 + \ldots + e_n^2\\\\ &=&(\mathbf{Y}-\mathbf{X\beta})^T(\mathbf{Y}-\mathbf{X\beta})\\\\ &=&\Vert \mathbf{Y}-\mathbf{X\beta}\Vert^2_2\\ \end{eqnarray*}\] with the \(L_2\)-norm of a \(p\)-dim. vector \(v\) \(\Vert \mathbf{v} \Vert_2=\sqrt{v_1^2+\ldots+v_p^2}\)
    \(\rightarrow\) \(\hat{\boldsymbol{\beta}}=\text{argmin}_\beta \Vert \mathbf{Y}-\mathbf{X\beta}\Vert^2_2\)


2.1.5.2 Minimize SSE

\[ \begin{array}{ccc} \frac{\partial}{\partial \boldsymbol{\beta}} \text{SSE}&=&\mathbf{0}\\\\ \frac{\partial}{\partial \boldsymbol{\beta}}(\mathbf{Y}-\mathbf{X\beta})^T(\mathbf{Y}-\mathbf{X}\boldsymbol{\beta})&=&\mathbf{0}\\\\ -2\mathbf{X}^T(\mathbf{Y}-\mathbf{X}\boldsymbol{\beta})&=&\mathbf{0}\\\\ -2\mathbf{X}^T\mathbf{Y} + 2\mathbf{X}^T\mathbf{X\beta}&=&0\\\\ \mathbf{X}^T\mathbf{X\beta}&=&\mathbf{X}^T\mathbf{Y}\\\\ \hat{\boldsymbol{\beta}}&=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{Y} \end{array} \]

It can be shown that the estimator is unbiased:

\[ \begin{array}{ccc} E[\hat{\boldsymbol{\beta}}]&=&E[(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{Y}]\\ \\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T E[\mathbf{Y}]\\\\ &=& (\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{X}\boldsymbol{\beta}\\\\ &=& \boldsymbol{\beta} \end{array} \]


2.1.6 Projection

There is also another picture to regression:

  • Instead of plotting each observation \(i= 1 \ldots n\) as a data-point in \(\mathbb{R}^p\) with dimensions \(1 \ldots p\) for every variable/feature that is recorded for each observation

  • We can also plot \(\mathbf{Y}\), \(\hat{\mathbf{Y}}\) and each column of \(\mathbf{X}\): \(\mathbf{X}_j\) with \(j=1 \ldots p\) as a vector in \(\mathbb{R}^n\) with dimensions \(1 \ldots n\) for every observation.

  • In this representation linear regression can be interpreted as a projection of the vector \(\mathbf{Y}\) onto the subspace of \(\mathbb{R}^n\) that is spanned by the vectors for the predictors \(\mathbf{X}_1 \ldots \mathbf{X}_p\).

  • The space \(\mathbf{X}_1 \ldots \mathbf{X}_p\) is also referred to as the column space of \(\mathbf{X}\), the space that consists of all linear combinations of the vectors of the predictors or columns \(\mathbf{X}_1 \ldots \mathbf{X}_p\).

2.1.6.1 Intermezzo: Projection of vector on X and Y axis

\[ \mathbf{e}=\left[\begin{array}{c} e_1\\e_2\end{array}\right], \mathbf{u}_1 = \left[\begin{array}{c} 1\\0\end{array}\right], \mathbf{u}_2 = \left[\begin{array}{c} 0\\1\end{array}\right] \]

  1. Projection of vector e on x-axis

\[\begin{eqnarray*} \mathbf{u}_1^T \mathbf{e} &=& \Vert \mathbf{u}_1\Vert_2 \Vert \mathbf{e}_1\Vert_2 \cos <\mathbf{u}_1,\mathbf{e}_1>\\ &=&\left[\begin{array}{cc} 1&0\end{array}\right] \left[\begin{array}{c} e_1\\e_2\end{array}\right]\\ &=& 1\times e_1 + 0 \times e_2 \\ &=& e_1\\ \end{eqnarray*}\]

  1. Projection of vector e on y-axis

\[\begin{eqnarray*} \mathbf{u}_2^T \mathbf{e} &=& \left[\begin{array}{cc} 0&1\end{array}\right] \left[\begin{array}{c} e_1\\e_2\end{array}\right]\\ &=& 0\times e_1 + 1 \times e_2 \\ &=& e_2 \end{eqnarray*}\]

  1. Projection of vector e on itself

\[\begin{eqnarray*} \mathbf{e}^T \mathbf{e} &=&\left[\begin{array}{cc} e_1&e_2\end{array}\right] \left[\begin{array}{c} e_1\\e_2\end{array}\right]\\ &=&e_1^2+e_2^2\\ &=&\Vert e \Vert^2_2 \rightarrow \text{ Pythagorean theorem} \end{eqnarray*}\]


2.1.6.2 Interpretation of least squares as a projection

Fitted values:

\[ \begin{array}{lcl} \hat{\mathbf{Y}} &=& \mathbf{X}\hat{\boldsymbol{\beta}}\\ &=& \mathbf{X} (\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{Y}\\ &=& \mathbf{HY} \end{array} \] with \(\mathbf{H}\) the projection matrix also referred to as the hat matrix.

X <- model.matrix(~x,data)
X
##   (Intercept) x
## 1           1 1
## 2           1 2
## 3           1 3
## attr(,"assign")
## [1] 0 1
XtX <- t(X)%*%X
XtX
##             (Intercept)  x
## (Intercept)           3  6
## x                     6 14
XtXinv <- solve(t(X)%*%X)
XtXinv
##             (Intercept)    x
## (Intercept)    2.333333 -1.0
## x             -1.000000  0.5
H <- X %*% XtXinv %*% t(X)
H
##            1         2          3
## 1  0.8333333 0.3333333 -0.1666667
## 2  0.3333333 0.3333333  0.3333333
## 3 -0.1666667 0.3333333  0.8333333
Y <- data$y
Yhat <- H%*%Y
Yhat
##       [,1]
## 1 1.166667
## 2 1.666667
## 3 2.166667
  • We can also interpret the fit as the projection of the \(n\times 1\) vector \(\mathbf{Y}\) on the column space of the matrix \(\mathbf{X}\).

  • So each column in \(\mathbf{X}\) is also an \(n\times 1\) vector.

  • For the toy example n=3 and p=2. The other picture to linear regression is to consider \(X_0\), \(X_1\) and \(Y\) as vectors in the space of the data \(\mathbb{R}^n\), here \(\mathbb{R}^3\) because we have three data points. So the column space of X is a plane in the three dimensional space.

\[ \hat{\mathbf{Y}} = \mathbf{X} (\mathbf{X}^T\mathbf{X})^{-1} \mathbf{X}^T \mathbf{Y} \]

  1. Plane spanned by column space: The other picture to linear regression is to consider \(X_0\), \(X_1\) and \(Y\) as vectors in the space of the data \(\mathbb{R}^n\), here \(\mathbb{R}^3\) because we have three data points.
originRn <- data.frame(X1=0,X2=0,X3=0)
data$x0 <- 1
dataRn <- data.frame(t(data))

library(plotly)

p1 <- plot_ly(
    originRn,
    x = ~ X1,
    y = ~ X2,
    z= ~ X3, name="origin") %>%
  add_markers(type="scatter3d") %>%
  layout(
    scene = list(
      aspectmode="cube",
      xaxis = list(range=c(-4,4)), yaxis = list(range=c(-4,4)), zaxis = list(range=c(-4,4))
      )
    )
p1 <- p1 %>%
  add_trace(
    x = c(0,1),
    y = c(0,0),
    z = c(0,0),
    mode = "lines",
    line = list(width = 5, color = "grey"),
    type="scatter3d",
    name = "obs1") %>%
  add_trace(
    x = c(0,0),
    y = c(0,1),
    z = c(0,0),
    mode = "lines",
    line = list(width = 5, color = "grey"),
    type="scatter3d",
    name = "obs2") %>%
  add_trace(
    x = c(0,0),
    y = c(0,0),
    z = c(0,1),
    mode = "lines",
    line = list(width = 5, color = "grey"),
    type="scatter3d",
    name = "obs3") %>%
  add_trace(
    x = c(0,1),
    y = c(0,1),
    z = c(0,1),
    mode = "lines",
    line = list(width = 5, color = "black"),
    type="scatter3d",
    name = "X1") %>%
    add_trace(
    x = c(0,1),
    y = c(0,2),
    z = c(0,3),
    mode = "lines",
    line = list(width = 5, color = "black"),
    type="scatter3d",
    name = "X2")
p1
  1. Vector of Y:

Actual values of \(\mathbf{Y}\):

data$y
## [1] 1 2 2

\[ \mathbf{Y}=\left[\begin{array}{c} 1 \\ 2 \\ 2 \end{array}\right] \]

p2 <- p1 %>%
  add_trace(
    x = c(0,Y[1]),
    y = c(0,Y[2]),
    z = c(0,Y[3]),
    mode = "lines",
    line = list(width = 5, color = "red"),
    type="scatter3d",
    name = "Y")
p2
  1. Projection of Y onto column space

Actual values of fitted values \(\mathbf{\hat{Y}}\):

data$yhat
## [1] 1.166667 1.666667 2.166667

\[ \mathbf{Y}=\left[\begin{array}{c} 1.1666667 \\ 1.6666667 \\ 2.1666667 \end{array}\right] \]

p2 <- p2 %>%
  add_trace(
    x = c(0,Yhat[1]),
    y = c(0,Yhat[2]),
    z = c(0,Yhat[3]),
    mode = "lines",
    line = list(width = 5, color = "orange"),
    type="scatter3d",
    name="Yhat") %>%
    add_trace(
    x = c(Y[1],Yhat[1]),
    y = c(Y[2],Yhat[2]),
    z = c(Y[3],Yhat[3]),
    mode = "lines",
    line = list(width = 5, color = "red", dash="dash"),
    type="scatter3d",
    name="Y -> Yhat"
    )
p2

\(\mathbf{Y}\) is projected in the column space of \(\mathbf{X}\)! spanned by the columns.

2.1.6.3 How does this projection works?

\[ \begin{array}{lcl} \hat{\mathbf{Y}} &=& \mathbf{X} (\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{Y}\\ &=& \mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1/2}(\mathbf{X}^T\mathbf{X})^{-1/2}\mathbf{X}^T\mathbf{Y}\\ &=& \mathbf{S}\mathbf{S}^T\mathbf{Y} \end{array} \]

  • \(\mathbf{S}\) is a new orthonormal basis in \(\mathbb{R}^2\), a subspace of \(\mathbb{R}^3\)

  • The space spanned by \(\mathbf{S}\) and \(\mathbf{X}\) is the column space of \(\mathbf{X}\), e.g. it contains all possible linear combinantions of \(\mathbf{X}\). \(\mathbf{S}^t\mathbf{Y}\) is the projection of \(\mathbf{Y}\) on this new orthonormal basis

svdX <- svd(X) # SVD X = UDV^T
XtXinvSqrt <- svdX$v %*%diag(1/svdX$d)%*%t(svdX$v)
S <- X %*% XtXinvSqrt
  • \(\mathbf{S}\) orthonormal basis
S
##         [,1]        [,2]
## 1  0.9116067 -0.04802616
## 2  0.3881706  0.42738380
## 3 -0.1352655  0.90279376
t(S)%*%S
##              [,1]         [,2]
## [1,] 1.000000e+00 2.380172e-16
## [2,] 2.380172e-16 1.000000e+00
  • \(\mathbf{SS}^T\) equals projection matrix
S%*%t(S)
##            1         2          3
## 1  0.8333333 0.3333333 -0.1666667
## 2  0.3333333 0.3333333  0.3333333
## 3 -0.1666667 0.3333333  0.8333333
H
##            1         2          3
## 1  0.8333333 0.3333333 -0.1666667
## 2  0.3333333 0.3333333  0.3333333
## 3 -0.1666667 0.3333333  0.8333333
p3 <- p1 %>%
  add_trace(
    x = c(0,S[1,1]),
    y = c(0,S[2,1]),
    z = c(0,S[3,1]),
    mode = "lines",
    line = list(width = 5, color = "blue"),
    type="scatter3d",
    name = "S1") %>%
  add_trace(
    x = c(0,S[1,2]),
    y = c(0,S[2,2]),
    z = c(0,S[3,2]),
    mode = "lines",
    line = list(width = 5, color = "blue"),
    type="scatter3d",
    name = "S2")

p3
  • \(\mathbf{S}^T\mathbf{Y}\) is the projection of \(\mathbf{Y}\) in the space spanned by \(\mathbf{S}\).
  • Indeed \(\mathbf{S}_1^T\mathbf{Y}\)
p4 <- p3 %>%
  add_trace(
    x = c(0,Y[1]),
    y = c(0,Y[2]),
    z = c(0,Y[3]),
    mode = "lines",
    line = list(width = 5, color = "red"),
    type="scatter3d",
    name = "Y") %>%
  add_trace(
    x = c(0,S[1,1]*(S[,1]%*%Y)),
    y = c(0,S[2,1]*(S[,1]%*%Y)),
    z = c(0,S[3,1]*(S[,1]%*%Y)),
    mode = "lines",
    line = list(width = 5, color = "red",dash="dash"),
    type="scatter3d",
    name="Y -> S1") %>% add_trace(
    x = c(Y[1],S[1,1]*(S[,1]%*%Y)),
    y = c(Y[2],S[2,1]*(S[,1]%*%Y)),
    z = c(Y[3],S[3,1]*(S[,1]%*%Y)),
    mode = "lines",
    line = list(width = 5, color = "red", dash="dash"),
    type="scatter3d",
    name="Y -> S1")
p4
  • and \(\mathbf{S}_2^T\mathbf{Y}\)
p5 <- p4 %>%
  add_trace(
    x = c(0,S[1,2]*(S[,2]%*%Y)),
    y = c(0,S[2,2]*(S[,2]%*%Y)),
    z = c(0,S[3,2]*(S[,2]%*%Y)),
    mode = "lines",
    line = list(width = 5, color = "red",dash="dash"),
    type="scatter3d",
    name="Y -> S2") %>% add_trace(
    x = c(Y[1],S[1,2]*(S[,2]%*%Y)),
    y = c(Y[2],S[2,2]*(S[,2]%*%Y)),
    z = c(Y[3],S[3,2]*(S[,2]%*%Y)),
    mode = "lines",
    line = list(width = 5, color = "red", dash="dash"),
    type="scatter3d",
    name="Y -> S2")
p5
  • Yhat is the resulting vector that lies in the plane spanned by \(\mathbf{S}_1\) and \(\mathbf{S}_2\) and thus also in the column space of \(\mathbf{X}\).
p6 <- p5 %>%
  add_trace(
    x = c(0,Yhat[1]),
    y = c(0,Yhat[2]),
    z = c(0,Yhat[3]),
    mode = "lines",
    line = list(width = 5, color = "orange"),
    type="scatter3d",
    name = "Yhat") %>%
  add_trace(
    x = c(Y[1],Yhat[1]),
    y = c(Y[2],Yhat[2]),
    z = c(Y[3],Yhat[3]),
    mode = "lines",
    line = list(width = 5, color = "maroon2"),
    type="scatter3d",
    name = "e") %>%
  add_trace(
    x = c(S[1,1]*(S[,1]%*%Y),Yhat[1]),
    y = c(S[2,1]*(S[,1]%*%Y),Yhat[2]),
    z = c(S[3,1]*(S[,1]%*%Y),Yhat[3]),
    mode = "lines",
    line = list(width = 5, color = "orange", dash="dash"),
    type="scatter3d",
    name = "Y -> S")  %>%
  add_trace(
    x = c(S[1,2]*(S[,2]%*%Y),Yhat[1]),
    y = c(S[2,2]*(S[,2]%*%Y),Yhat[2]),
    z = c(S[3,2]*(S[,2]%*%Y),Yhat[3]),
    mode = "lines",
    line = list(width = 5, color = "orange", dash="dash"),
    type="scatter3d",
    name = "Y -> S")
p6

2.1.7 Error

Note, that it is also clear from the equation in the derivation of the least squares solution that the residual is orthogonal on the column space:

\[ -2 \mathbf{X}^T(\mathbf{Y}-\mathbf{X}\boldsymbol{\beta}) = 0 \]

2.1.8 Curse of dimensionality?

  • Imagine what happens when p approaches n \(p=n\) or becomes much larger than p >> n!!!

  • Suppose that we add a predictor \(\mathbf{X}_2 = [2,0,1]^T\)?

\[ \mathbf{Y}=\left[ \begin{array}{c} 1\\ 2\\ 2\\ \end{array}\right], \quad \mathbf{X}= \left[ \begin{array}{ccc} 1&1&2\\ 1&2&0\\ 1&3&1\\ \end{array} \right], \quad \boldsymbol{\beta} = \left[ \begin{array}{c} \beta_0\\ \beta_1\\ \beta_2 \end{array} \right] \quad \text{and} \quad \boldsymbol{\epsilon}= \left[ \begin{array}{c} \epsilon_1\\ \epsilon_2\\ \epsilon_3 \end{array} \right] \]

data$x2 <- c(2,0,1)
fit <- lm(y~x+x2,data)
# predict values on regular xy grid
x1pred <- seq(-1, 4, length.out = 10)
x2pred <- seq(-1, 4, length.out = 10)
xy <- expand.grid(x = x1pred,
x2 = x2pred)
ypred <- matrix (nrow = 30, ncol = 30,
data = predict(fit, newdata = data.frame(xy)))

library(plot3D)


# fitted points for droplines to surface
th=20
ph=5
scatter3D(data$x,
  data$x2,
  Y,
  pch = 16,
  col="darkblue",
  cex = 1,
  theta = th,
  ticktype = "detailed",
  xlab = "x1",
  ylab = "x2",
  zlab = "y",
  colvar=FALSE,
  bty = "g",
  xlim=c(-1,3),
  ylim=c(-1,3),
  zlim=c(-2,4))

z.pred3D <- outer(
  x1pred,
  x2pred,
  function(x1,x2)
  {
    fit$coef[1] + fit$coef[2]*x1+fit$coef[2]*x2
  })

x.pred3D <- outer(
  x1pred,
  x2pred,
  function(x,y) x)

y.pred3D <- outer(
  x1pred,
  x2pred,
  function(x,y) y)

scatter3D(data$x,
  data$x2,
  data$y,
  pch = 16,
  col="darkblue",
  cex = 1,
  theta = th,
  ticktype = "detailed",
  xlab = "x1",
  ylab = "x2",
  zlab = "y",
  colvar=FALSE,
  bty = "g",
  xlim=c(-1,4),
  ylim=c(-1,4),
  zlim=c(-2,4))

surf3D(
  x.pred3D,
  y.pred3D,
  z.pred3D,
  col="blue",
  facets=NA,
  add=TRUE)

Note, that the linear regression is now a plane.

However, we obtain a perfect fit and all the data points are falling in the plane! 🫦

This is obvious if we look at the column space of X!

X <- cbind(X,c(2,0,1))
svdX <- svd(X) # SVD X = UDV^T
XtXinvSqrt <- svdX$v %*%diag(1/svdX$d)%*%t(svdX$v)
S <- X %*% XtXinvSqrt

p7 <- p1 %>%
  add_trace(
    x = c(0,2),
    y = c(0,0),
    z = c(0,1),
    mode = "lines",
    line = list(width = 5, color = "darkgreen"),
    type="scatter3d",
    name = "X3")
p7
p8 <- p7 %>%
  add_trace(
    x = c(0,S[1,1]),
    y = c(0,S[2,1]),
    z = c(0,S[3,1]),
    mode = "lines",
    line = list(width = 5, color = "blue"),
    type="scatter3d",
    name = "S1") %>%
  add_trace(
    x = c(0,S[1,2]),
    y = c(0,S[2,2]),
    z = c(0,S[3,2]),
    mode = "lines",
    line = list(width = 5, color = "blue"),
    type="scatter3d",
    name = "S2") %>%
  add_trace(
    x = c(0,S[1,3]),
    y = c(0,S[2,3]),
    z = c(0,S[3,3]),
    mode = "lines",
    line = list(width = 5, color = "blue"),
    type="scatter3d",
    name = "S3")

p8
  • The column space now spans the entire \(\mathbb{R}^3\)!

  • With the intercept and the two predictors we can thus fit every dataset that only has 3 observations for the predictors and the response.

  • So the model can no longer be used to generalise the patterns seen in the data towards the population (new observations).

  • Problem of overfitting!!!

  • If \(p >> n\) then the problem gets even worse! Then there is even no longer a unique solution to the least squares problem…

  • Indeed, then we have more vectors/dimensions/columns in X than datapoints!

2.1.9 Variance Estimator?

\[ \begin{array}{ccl} \hat{\boldsymbol{\Sigma}}_{\hat{\boldsymbol{\beta}}} &=&\text{var}\left[(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{Y}\right]\\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\text{var}\left[\mathbf{Y}\right]\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1} \text{(*)}\\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T(\mathbf{I}\sigma^2)\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1} \\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{I}\quad\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1}\sigma^2\\\\ %\hat{\boldmath{\Sigma}}_{\hat{\boldsymbol{\beta}}}&=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T \text{var}\left[\mathbf{Y}\right](\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1}\sigma^2\\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\sigma^2 \end{array} \]

(*) Under assumption that all observations \(\mathbf{Y}\) are independent and identically distributed.

The fact that \(\hat{\boldsymbol{\beta}}\) is unbiased and has a variance of \((\mathbf{X}^T\mathbf{X})^{-1}\sigma^2\) will be important when assessing association!

2.1.10 Projecting covariates out

We can get rid of covariates \(\mathbf{X}_c\) by projecting them out, i.e. 

\[ \mathbf{P}_c = (\mathbf{I} - \mathbf{X}_x(\mathbf{X}_x^T\mathbf{X}_x)^{-1}\mathbf{X}_x^T) \]

So one obtains

\[ \begin{array}{ccccccccc} \mathbf{P}_c \mathbf{y} &=& \mathbf{P}_c \mathbf{x}_\text{test}\beta_\text{test}& + & \mathbf{P}_c \mathbf{X}_c\boldsymbol{\beta}_c &+ &\mathbf{P}_c \mathbf{Zu} &+&\mathbf{P}_c \boldsymbol{\epsilon} \\ \tilde{\mathbf{y}} &=& \tilde{\mathbf{x}}_\text{test}\beta_\text{test} &+&\mathbf{0}&+ &\tilde{\mathbf{Z}}\mathbf{u} &+&\tilde{\boldsymbol{\epsilon}} \end{array} \]

  • \(\mathbf{P}_x\) projects vectors to an (N − C)-dimensional subspace orthogonal on the known covariates.
  • Keep in mind that these \(N\) vectors all belong to the (N − C)-dimensional subspace!
    \(\rightarrow\) Important when estimating \(\tilde{\mathbf{y}}\) and \(\tilde{\mathbf{Z}}\)

2.2 Bolt-LMM uses a score test to test for the candidate SNP

If all model assumptions hold we can derive a \(\chi^2\) test under the null hypothesis \(H_0: \boldsymbol{\beta}=\mathbf{0}\):

\[ t=\hat{\beta}\hat{\boldsymbol{\Sigma}}_{\hat{\beta}}^{-1}\hat{\beta} \] which follows an asymptotic \(\chi^2\) distribution under \(H_0\).

In our case we only test for one model parameter, i.e. that of the candidate SNP so the test reduces to

\[ t_{\chi^2}=\frac{\hat{\beta}_\text{test}^2}{\text{SE}_{\hat{\beta}_\text{test}}^2} \] which follows an asymptotic \(\chi^2_1\) distribution with one degree of freedom under \(H_0\).

We need the estimator for \(\hat{\beta}\) and \(\hat{\boldsymbol{\Sigma}}\).

2.2.1 Intermezzo fitting linear mixed models (LMM)

In the general formulation of LMM also allow random effects to be correlated.

\[ \left\{ \begin{array}{ccc} Y &=& \mathbf{X}\beta + \mathbf{Zu} + \boldsymbol{\epsilon}\\ \mathbf{u} &\sim&\text{MVN}(\mathbf{0},\mathbf{G})\\ \boldsymbol{\epsilon} &\sim&\text{MVN}(\mathbf{0},\mathbf{I}\sigma^2_\epsilon) \end{array} \right. \]

This expression implies three different model representations:

  1. the conditional model (conditional on the random effects)

\[ f(\mathbf{Y}\vert \mathbf{X, u})\sim \text{MVN}(\mathbf{X}\boldsymbol{\beta}+\mathbf{Zu},\mathbf{I}\sigma^2_\epsilon) \]

  1. the joint model of \(\mathbf{Y}\) and \(\mathbf{u}\) \[ \begin{array}{ccl} f(\mathbf{Y, u}\vert \mathbf{X}) &\sim& f(\mathbf{Y}\vert \mathbf{X, u}) f(\mathbf{u}) \\\\ &\sim&\text{MVN}(\mathbf{X}\boldsymbol{\beta}+\mathbf{Zu},\mathbf{I}\sigma^2_\epsilon)\times\text{MVN}(\mathbf{0},\mathbf{G}) \end{array} \]
  2. Marginal model of \(\mathbf{Y}\)

\[ \begin{array}{rcl} f(\mathbf{Y}\vert \mathbf{X}) &\sim& \int f(\mathbf{Y, u}\vert \mathbf{X}) d\mathbf{u} \\\\ &\sim& \text{MVN}(\mathbf{X}\boldsymbol{\beta},\mathbf{V})\\\\ \mathbf{V} &=& \mathbf{ZG}\mathbf{Z}^T + \mathbf{I}\sigma^2_\epsilon \end{array} \] So estimating the mixed model implies estimating

  • Model parameters of fixed effects \(\boldsymbol{\beta}\)
  • Variance components \(\mathbf{G}\) and \(\sigma^2_\epsilon\)

\(\rightarrow\) We can do that by using maximum likelihood.

  • Choosing the data parameters as such so that the probability to observe the data under the marginal model becomes maximal

\[ L(\boldsymbol{\beta}, \mathbf{G},\sigma^2_\epsilon \vert \mathbf{X, Y}) = \frac{1}{(2\pi)^{N/2}|\mathbf{V}|^{1/2}} \exp\left(-\frac{1}{2} (\mathbf{Y} - \mathbf{X}\boldsymbol{\beta})^T \mathbf{V}^{-1} (\mathbf{Y} - \mathbf{X}\boldsymbol{\beta})\right) \] This is nasty to maximise \(\rightarrow\) maximise the log-likelihood

\[ l(\boldsymbol{\beta}, \mathbf{G},\sigma^2_\epsilon \vert \mathbf{X, Y}) = -\frac{N}{2} \log(2\pi) - \frac{1}{2} \log(|\mathbf{V}|) - \frac{1}{2} (\mathbf{Y} - \mathbf{X}\boldsymbol{\beta})^T \mathbf{V}^{-1} (\mathbf{Y} - \mathbf{X}\boldsymbol{\beta}) \]

\[ \begin{array}{rcl} \frac{\partial}{\partial \beta} l(\boldsymbol{\beta}, \mathbf{G},\sigma^2_\epsilon \vert \mathbf{X, Y}) &=& 0 \\\\ \frac{1}{2} \mathbf{X}^T \mathbf{V}^{-1} (\mathbf{Y} - \mathbf{X}\boldsymbol{\beta}) &=& 0\\\\ \mathbf{X}^T \mathbf{V}^{-1} \mathbf{X}\boldsymbol{\beta} &=&\mathbf{X}^T \mathbf{V}^{-1} \mathbf{Y}\\\\ \hat{\boldsymbol{\beta}} &=& (\mathbf{X}^T \mathbf{V}^{-1} \mathbf{X})^{-1} \mathbf{X}^T \mathbf{V}^{-1} \mathbf{Y} \end{array} \] The variance-covariance matrix for this estimator becomes:

\[ \begin{array}{rcl} \text{var}\left[\hat{\boldsymbol{\beta}}\right] &=& \text{var}\left[(\mathbf{X}^T \mathbf{V}^{-1} \mathbf{X})^{-1} \mathbf{X}^T \mathbf{V}^{-1} \mathbf{Y}\right]\\\\ \boldsymbol{\Sigma}_\hat{\boldsymbol{\beta}} &=&(\mathbf{X}^T \mathbf{V}^{-1} \mathbf{X})^{-1} \mathbf{X}^T \mathbf{V}^{-1} \text{var}\left[\mathbf{Y}\right] \mathbf{V}^{-1} \mathbf{X} (\mathbf{X}^T \mathbf{V}^{-1} \mathbf{X})^{-1} \\\\ \boldsymbol{\Sigma}_\hat{\boldsymbol{\beta}} &=&(\mathbf{X}^T \mathbf{V}^{-1} \mathbf{X})^{-1} \mathbf{X}^T \mathbf{V}^{-1} \mathbf{V} \mathbf{V}^{-1} \mathbf{X} (\mathbf{X}^T \mathbf{V}^{-1} \mathbf{X})^{-1} \\\\ \boldsymbol{\Sigma}_\hat{\boldsymbol{\beta}} &=&(\mathbf{X}^T \mathbf{V}^{-1} \mathbf{X})^{-1} \end{array} \] So we only need to estimate the variance components \(\mathbf{G}\) and \(\sigma_\epsilon^2\).

  • This can be done by the profile likelihood, i.e. replacing \(\boldsymbol{\beta}\) by the ML estimator \((\mathbf{X}^T \mathbf{V}^{-1} \mathbf{X})^{-1} \mathbf{X}^T \mathbf{V}^{-1} \mathbf{Y}\).
  • We then maximise the profile likelihood to \(\mathbf{G}\) and \(\sigma_\epsilon^2\)
  • Note that the ML for the variance components are biased in small sample because it does not account for the loss in degrees of freedom due to the estimation of the fixed effect model parameters
  • Therefore, ML is often replaced by restricted maximum likelihood (REML) which adds a penalty term to the ML to correct for the loss in df.

Once estimators for the variance components \(\hat{\mathbf{G}}\) and \(\hat{\sigma}_\epsilon^2\) are obtained we will plug them in \(\mathbf{V}\), \(\hat{\mathbf{\beta}}\) and \(\boldsymbol{\Sigma}_\hat{\boldsymbol{\beta}}\)

\[ \begin{array}{ccl} \hat{\mathbf{V}} &=& \mathbf{Z}\hat{\mathbf{G}}^{-1}\mathbf{Z}^T + \mathbf{I}\hat\sigma^2_\epsilon\\\\ \hat{\boldsymbol{\beta}} &=& (\mathbf{X}^T \hat{\mathbf{V}}^{-1} \mathbf{X})^{-1} \mathbf{X}^T \hat{\mathbf{V}}^{-1} \mathbf{Y}\\\\ \hat{\boldsymbol{\Sigma}}_\hat{\boldsymbol{\beta}} &=&(\mathbf{X}^T \hat{\mathbf{V}}^{-1} \mathbf{X})^{-1} \end{array} \]

Note, that

  • for our GWAS LMM \(\mathbf{G} = \mathbf{I} \sigma^2_u\) and thus reduces to estimating \(\sigma^2_u\)

  • The optimisation for the variance components is computationally demanding

  • \(\hat{\boldsymbol{\beta}}\) and \(\boldsymbol{\Sigma}_\hat{\boldsymbol{\beta}}\) involve the computationally challenging calculation of

    • \(\hat{\mathbf{V}}^{-1}\) an \(N \times N\) with \(N > 70.000\) subjects
    • \(\mathbf{Z}_{GRM}^T\mathbf{Z}\) an \(M \times M\) with \(M > 500.000\) high quality SNPs

2.2.2 Adopting result to BOLT-LMM

Note, that in BOLT-LMM they profiled out all fixed effect except for the candidate SNP.
so only one fixed effect parameter \(\hat{\beta}_\text{test}\)

\[ \begin{array}{ccl} \hat{\beta}_\text{test} &=& (\tilde{\mathbf{x}}^t_\text{test} \hat{\mathbf{V}}^{-1}\tilde{\mathbf{x}}_\text{test})^{-1}\tilde{\mathbf{x}}^t_\text{test} \hat{\mathbf{V}}^{-1}\tilde{\mathbf{y}} \\\\ &=& \frac{(\tilde{\mathbf{x}}^t_\text{test} \hat{\mathbf{V}}^{-1}\tilde{\mathbf{y}})}{(\tilde{\mathbf{x}}^t_\text{test} \hat{\mathbf{V}}^{-1}\tilde{\mathbf{x}}_\text{test})} \end{array} \] Next, we need the \(\text{SE}_{\hat{\beta}_\text{test}}\)

\[ \text{SE}^2_{\hat{\beta}_\text{test}} = (\tilde{\mathbf{x}}^t_\text{test}\hat{\mathbf{V}}^{-1}\tilde{\mathbf{x}}^t_\text{test})^{-1} \] We plug \(\hat{\beta}_\text{test}^2\) and \(\text{SE}_{\hat{\beta}_\text{test}}^2\) in the \(\chi^2\)-test:

\[ \begin{array}{ccl} t_{\chi^2}&=&\frac{\hat{\beta}_\text{test}^2}{\text{SE}_{\hat{\beta}_\text{test}}^2} \\\\ &=& \frac{(\tilde{\mathbf{x}}^t_\text{test} \hat{\mathbf{V}}^{-1}\tilde{\mathbf{y}})^2}{(\tilde{\mathbf{x}}^t_\text{test} \hat{\mathbf{V}}^{-1}\tilde{\mathbf{x}}_\text{test})} \end{array} \] which follows a \(\chi^2_1\) with one degree of freedom.

We can construct a score test by replacing the variance components \(\sigma_\epsilon^2\) and \(\sigma_u^2\) by their estimates under the model implied by \(H_0: \beta_\text{test} = 0\):

\[ \tilde{\mathbf{y}} \sim \text{MVN}(\mathbf{0},\mathbf{V}) \]

2.2.3 LOCO score test

Within a Leave One Chromosome Out (LOCO) scheme, the test statistic becomes

\[ \tag{4} t_\text{LOCO} = \frac{(\tilde{\mathbf{x}}^t_\text{test} \hat{V}^{-1}_{\text{LOCO},H_0}\tilde{\mathbf{y}})^2}{\tilde{\mathbf{x}}^t_\text{test} \hat{V}^{-1}_{\text{LOCO},H_0}\tilde{\mathbf{x}}_\text{test}} \]

\(\rightarrow\) effect of candidate SNP not by diluted by proximal contamination.
\(\rightarrow\) variance components under \(H_0\)
\(\rightarrow\) \(\hat{V}^{-1}_{\text{LOCO}, H_0}\) is the same for all candidate SNPs from the same chromosome.

2.3 BOLT-LMM-inf

“The BOLT-LMM-inf infinitesimal mixed-model statistic is slightly different”

2.3.1 Denominator LOCO statistic

Under the assumption that many loci with small effects control the trait, the denominator of the LOCO score test

\[ \tilde{\mathbf{x}}^t_\text{test} {V}^{-1}_\text{LOCO}\tilde{\mathbf{x}}^t_\text{test} \]

is very similar between all candidate SNPs

\(\rightarrow\) can be replaced by a calibration factor that only has to be estimated once!

The BOLT-LMM-inf statistics thus becomes:

\[t_\text{BOLT-LMM-inf} = \frac{(\tilde{\mathbf{x}}^t_\text{test} \hat{V}^{-1}_{\text{LOCO},H_0}\tilde{\mathbf{y}})^2}{c_\text{inf}}\]

which follows a \(\chi^2_1\) under \(H_0\).

  • Calibration factor estimated with 30 random SNPs without effect.

  • Important to keep in mind:

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

2.4 Generalisation of BOLT-LMM-inf

“We now generalize BOLT-LMM-inf by obsering that the vector \(\mathbf{V}^{-1}_{\text{LOCO},H_0} \mathbf{y}\) appearing in equation (8) is a scalar multiple of the residual phenotype \(\sigma^2_\epsilon \mathbf{V}^{-1}_{\text{LOCO},H_0} \mathbf{y}\) from the best linear unbiased prediction (BLUP)”

2.4.1 What is the best linear unbiased predictor BLUP?

  • The random effects can be predicted using the BLUP
  • An estimator for the fixed effects and for the prediction of the random effects can be derived from the joint-likelihood of \(\mathbf{Y}\) and \(\mathbf{u}\), i.e. 

Maximising the log of the joint density \(f(\mathbf{Y},\mathbf{u}\vert \mathbf{X})\) to \(\mathbf{u}\) and \(\boldsymbol{\beta}\):

\[ \text{argmax}_{\mathbf{u},\boldsymbol{\beta}} \left[ -\frac{N}{2}\log( \sigma^2_\epsilon)-\frac{1}{2\sigma^2_\epsilon}\left(\mathbf{Y}-\mathbf{X}\boldsymbol{\beta}-\mathbf{Zu}\right)^T\left(\mathbf{Y}-\mathbf{X}\boldsymbol{\beta}-\mathbf{Zu}\right)-1/2\log(\vert\mathbf{G}\vert)-\frac{1}{2}\mathbf{u}^T\mathbf{G}^{-1}\mathbf{u}\right] \]

Maximisation to \(\boldsymbol{\theta}=\left[\begin{array}{c} \boldsymbol{\beta}\\\mathbf{u}\end{array}\right]\) only involves

\[ -\frac{1}{2\sigma^2_\epsilon}\left(\mathbf{Y}-\mathbf{X}\boldsymbol{\beta}-\mathbf{Zu}\right)^T\left(\mathbf{Y}-\mathbf{X}\boldsymbol{\beta}-\mathbf{Zu}\right)-\frac{1}{2}\mathbf{u}^T\mathbf{G}^{-1}\mathbf{u} \] which can be rewritten as a kind of loss function that we will minimise

\[ L^{MM}=(\mathbf{Y}-\mathbf{C}\boldsymbol{\theta})^T(\mathbf{Y}-\mathbf{C}\boldsymbol{\theta})+{\sigma^2_\epsilon}\boldsymbol{\theta}^T\mathbf{H}\boldsymbol{\theta} \]

with \(\mathbf{C}=[\mathbf{X}\text{ } \mathbf{Z}]\) and \(\mathbf{H}=\left[\begin{array}{cc} \mathbf{0}&\mathbf{0}\\ \mathbf{0}&\mathbf{G}^{-1} \end{array} \right]\)

We can find the estimator for \(\boldsymbol{\theta}\) by taken the first derivative of \(L^{MM}\) to \(\boldsymbol{\theta}\) and setting it to zero, \[ \begin{array}{rcl} \frac{\partial }{\partial \boldsymbol{\theta}} L^{MM} &=& 0\\\\ -2 \mathbf{C}^T(\mathbf{Y}-\mathbf{C}\boldsymbol{\theta})+2 {\sigma^2_\epsilon} H\boldsymbol{\theta}&=&0\\\\ -\mathbf{C}^T\mathbf{Y}+\mathbf{C}^T\mathbf{C}\boldsymbol{\theta}+ {\sigma^2_\epsilon} H\boldsymbol{\theta}&=&0\\\\ (\mathbf{C}^T\mathbf{C} + \sigma^2_\epsilon H)\boldsymbol{\theta} &=&\mathbf{C}^T\mathbf{Y}\\\\ \hat{\boldsymbol{\theta}} &=& (\mathbf{C}^T\mathbf{C} + \sigma^2_\epsilon H)^{-1}\mathbf{C}^T\mathbf{Y} \end{array} \]

For our GWAS - LMM this reduces to

\[ \left[\begin{array}{c} \hat{\boldsymbol{\beta}}\\\hat{\mathbf{u}}\end{array}\right] = \left( \left[ \begin{array}{cc} \mathbf{X}^T\mathbf{X}& \mathbf{X}^T\mathbf{Z}\\ \mathbf{Z}^T\mathbf{X}& \mathbf{Z}^T\mathbf{Z} \end{array} \right] + \frac{\sigma^2_\epsilon}{\sigma^2_u} \left[ \begin{array}{cc} \mathbf{0}& \mathbf{0}\\ \mathbf{0}& \mathbf{I} \end{array} \right] \right)^{-1} \left[ \begin{array}{c} \mathbf{X}^T\\ \mathbf{Z}^T \end{array} \right]\mathbf{Y} \]

It can also be shown that \(\hat{\mathbf{u}}\) for the conventional LMM equals

\[ \hat{\mathbf{u}}= \hat{\mathbf{G}}\mathbf{Z}^T\hat{\mathbf{V}}^{− 1} (\mathbf{Y} − \mathbf{X} \hat{\boldsymbol{\beta}}) \]

and for our GWAS-LMM

\[ \hat{\mathbf{u}}= \hat{\sigma}^2_u\mathbf{Z}^T\hat{\mathbf{V}}^{− 1} (\mathbf{Y} − \mathbf{X} \hat{\boldsymbol{\beta}}) \]

2.4.2 Adopting result to the Nominator LOCO statistic of the BOLT-LMM-inf statistic

Moreover, under \(H_0: \beta_\text{test}=0\) the BLUP for the LOCO SNPs becomes

\[ \hat{\mathbf{u}}_{\text{LOCO}, H_0} = \hat\sigma^2_{u,\text{LOCO},H_0}\mathbf{Z}^T\hat{\mathbf{V}}^{− 1}_{\text{LOCO}, H_0} \tilde{\mathbf{y}} \] \(\rightarrow\) BLUP residuals become

\[ \begin{array}{ccl} \mathbf{y}_\text{residual-LOCO, H_0} &=& \tilde{\mathbf{y}} - \mathbf{Z}\hat{\mathbf{u}}_{\text{LOCO}, H_0}\\ \\ &=& \hat{\mathbf{V}}_{\text{LOCO}, H_0}\hat{\mathbf{V}}_{\text{LOCO}, H_0}^{-1}\tilde{\mathbf{y}}-\mathbf{Z}\mathbf{Z}^T\hat\sigma^2_{u,\text{LOCO},H_0} \hat{\mathbf{V}}_{\text{LOCO}, H_0}^{-1}\tilde{\mathbf{y}} \\\\ &=& (\hat{\mathbf{V}}_{\text{LOCO}, H_0}-\mathbf{Z}\mathbf{Z}^T\hat\sigma^2_{u,\text{LOCO},H_0}) \hat{\mathbf{V}}_{\text{LOCO}, H_0}^{-1}\tilde{\mathbf{y}} \\\\ &=& (\mathbf{Z}^T\mathbf{Z}\hat{\sigma}^2_{u\text{LOCO}, H_0}+\mathbf{I}\hat{\sigma}^2\epsilon-\mathbf{Z}\mathbf{Z}^T\hat\sigma^2_{u,\text{LOCO},H_0}) \hat{\mathbf{V}}_{\text{LOCO}, H_0}^{-1}\tilde{\mathbf{y}} \\\\ &=& \hat\sigma^2_{\epsilon,LOCO,H_0}\hat{\mathbf{V}}_{\text{LOCO}, H_0}^{-1}\tilde{\mathbf{y}} \end{array} \] \(\rightarrow\) nominator of LOCO score test upon a factor equal to:

\[ (\tilde{\mathbf{x}}^t_\text{test} \mathbf{y}_\text{residual-LOCO, H_0})^2 \]

BOLT-LMM-inf statistic thus becomes:

\[t_\text{BOLT-LMM-inf} = \frac{(\tilde{\mathbf{x}}^t_\text{test} \mathbf{y}_\text{residual-LOCO, H_0})^2}{c}\] with c the adjusted calibration factor.

2.5 BOLT-LMM Gaussian mixture-model association test

To generalise the estimation to non-infinitesimal genetic architextures they replace the Gaussian prior on the SNP effects sizes \(u_m\) by a Gaussian mixture prior :

\[u_m \sim p N(0,\sigma^2_{u,1}) + (1-p) N(0,\sigma^2_{u,2})\]

with a small probability \(p << 1\) and \(\sigma^2_{u,1} >> \sigma^2_{u,2}\).

  • “This mixture more flexibly models the heavier-tailed distributions of the genetic effects of typical (non-infinitesimal) phenotypes.”

  • “The first component in the mixture is a ‘slab’ that models the existence of a small number of loci with relatively large effects and the second component is a ‘spike’ that models the assumption that most SNPs have an effect of nearly—but not exactly—zero on the phenotype.”

  • “(Note, however, that all SNPs are assigned the same mixture prior; that is, SNPs are not individually allocated to one or the other component.)”

  • “It is important that the spike component have nonzero variance so as to capture genome-wide effects on phenotype such as ancestry or relatedness; then, when testing SNPs for association, these genome-wide effects are conditioned out from residual phenotypes, protecting against confounding.”

  • “Under this generalized model, posterior means no longer correspond to BLUP, but we can still approximately fit the Bayesian model (once per left-out chromosome) and obtain residuals”

  • “Plugging these residuals into equation (11) gives the BOLT-LMM Gaussian mixture-model association test statistic.”

LS0tCnRpdGxlOiAiVW5kZXIgdGhlIGhvb2Qgb2YgQk9MVC1MTU0iCmF1dGhvcjogIkxpZXZlbiBDbGVtZW50IgpkYXRlOiAic3RhdE9taWNzLCBHaGVudCBVbml2ZXJzaXR5IChodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8pIgpvdXRwdXQ6CiAgICBib29rZG93bjo6aHRtbF9kb2N1bWVudDI6CiAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICAgIHRoZW1lOiBmbGF0bHkKICAgICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgICB0b2M6IHRydWUKICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgICBjb2RlX2ZvbGRpbmc6IHNob3cKLS0tCgpgYGB7ciBsaWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBsb3RseSkKYGBgCgotIFtCT0xULUxNTSBwYXBlcl0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNDM0MjI5Ny9wZGYvbmlobXM2NTAyODQucGRmKQotIFtTdXBwbGVtZW50YXJ5IEluZm9ybWF0aW9uXShodHRwczovL3N0YXRpYy1jb250ZW50LnNwcmluZ2VyLmNvbS9lc20vYXJ0JTNBMTAuMTAzOCUyRm5nLjMxOTAvTWVkaWFPYmplY3RzLzQxNTg4XzIwMTVfQkZuZzMxOTBfTU9FU001X0VTTS5wZGYpCgoKCiMgTGluZWFyIG1peGVkIG1vZGVsIGZvciBHV0FTCgojIyBTcGVjaWZpY2F0aW9uCgokJApcdGFnezF9ClxtYXRoYmZ7WX0gPSBcbWF0aGJme3h9X1x0ZXh0e3Rlc3R9XGJldGFfXHRleHR7dGVzdH0gKyBcbWF0aGJme1h9X2NcYm9sZHN5bWJvbHtcYmV0YX1fYyArIFxtYXRoYmZ7Wn1fe0dSTX1cbWF0aGJme3V9ICtcYm9sZHN5bWJvbHtcZXBzaWxvbn0gCiQkCndpdGggCgotICRcbWF0aGJme1l9JCBhbiAkTlx0aW1lczEkIHZlY3RvciBvZiB0aGUgcGhlbm90eXBlCi0gJFxtYXRoYmZ7eH1fXHRleHR7dGVzdH0kIGFuICROXHRpbWVzMSQgdmVjdG9yIHdpdGggdGhlIGdlbm90eXBlIGZvciB0aGUgY2FuZGlkYXRlIFNOUAotICRcYmV0YV9cdGV4dHt0ZXN0fSQgdGhlIGFzc29jaWF0aW9uIG9mIGNhbmRpZGF0ZSBTTlAgYW5kIHRoZSBwaGVub3R5cGUKLSAkXG1hdGhiZntYfV9jJCBhbiAkTlx0aW1lcyBwJCBtYXRyaXggd2l0aCB0aGUgY292YXJpYXRlIHBhdHRlcm4gZm9yICRDJCBrbm93biBjb3ZhcmlhdGVzICh2ZWN0b3Igb2Ygb25lcyAoaW50ZXJjZXB0KSwgYWdlLCBnZW5kZXIsIGJhdGNoLC4uLikgCi0gJFxib2xkc3ltYm9se1xiZXRhfV9jJCB0aGUgJHBcdGltZXMgMSQgdmVjdG9yIG9mIHBhcmFtZXRlcnMgbW9kZWxpbmcgdGhlIGFzc29jaWF0aW9uIG9mIHRoZSBwIGNvdmFyaWF0ZXMgYW5kIHRoZSBwaGVub3R5cGUuIAotICRcbWF0aGJme1p9JCBhbiAkTlx0aW1lcyBNJCBnZW5ldGljIHJlbGF0aW9uc2hpcCBtYXRyaXggKEdSTSkgd2l0aCBhbGwgbm9ybWFsaXNlZCBnZW5vdHlwZXMKLSAkXG1hdGhiZnt1fSQgYW4gJE1cdGltZXMgMSQgdmVjdG9yIHdpdGggaS5pLmQuIHJhbmRvbSBlZmZlY3QgZm9yIGVhY2ggU05QICRcbWF0aGJme3V9XHNpbSBcdGV4dHtNVk59KDAsXG1hdGhiZntJfVxzaWdtYV91XjIpJAotICRcYm9sZHN5bWJvbHtcZXBzaWxvbn0kIGFuICROXHRpbWVzIDEkIHZlY3RvciB3aXRoIGVudmlyb25tZW50YWwgcmVzaWR1YWxzIHRoYXQgYXJlIGFzc3VtZWQgdG8gYmUgaW5kZXBlbmRlbnQgb2YgJFxtYXRoYmZ7dX0kIGFuZCBpLmkuZC4gICRcYm9sZHN5bWJvbHtcZXBzaWxvbn1cc2ltIFx0ZXh0e01WTn0oMCxcbWF0aGJme0l9XHNpZ21hX1xlcHNpbG9uXjIpJAoKUmFuZG9tIGVmZmVjdHMgYXJlIHVzZWQgdG8gbW9kZWwgdGhlIGNvcnJlbGF0aW9uIHN0cnVjdHVyZSBpbiB0aGUgZGF0YS4gVGhleSBpbXBseSBhIGNlcnRhaW4gY292YXJpYW5jZSBzdHJ1Y3R1cmUgb2YgJFxtYXRoYmZ7eX0kCgojIyBDb3ZhcmlhbmNlIHN0cnVjdHVyZQoKQ292YXJpYW5jZSBzdHJ1Y3R1cmUgb2YgJFxtYXRoYmZ7eX0kIGltcGxpZWQgYnkgR1dBUyBtaXhlZCBtb2RlbDogCgokJApcYmVnaW57YXJyYXl9e2NjbH0KXHRleHR7dmFyfVxsZWZ0W1xtYXRoYmZ7WX1ccmlnaHRdICY9JiBcdGV4dHt2YXJ9XGxlZnRbXG1hdGhiZnt4fV9cdGV4dHt0ZXN0fVxiZXRhX1x0ZXh0e3Rlc3R9ICsgXG1hdGhiZntYfV9jXGJvbGRzeW1ib2x7XGJldGF9X2MgKyBcbWF0aGJme1p9X1x0ZXh0e0dSTX1cbWF0aGJme3V9ICtcYm9sZHN5bWJvbHtcZXBzaWxvbn1ccmlnaHRdXFxcXAomXHVwZG93bmFycm93JiBcbWF0aGJme3V9IFxwZXJwIFxib2xkc3ltYm9se1xlcHNpbG9ufVxcXFwKJj0mIFx0ZXh0e3Zhcn1bXG1hdGhiZntafV9cdGV4dHtHUk19XG1hdGhiZnt1fV0gKyBcdGV4dHt2YXJ9W1xib2xkc3ltYm9se1xlcHNpbG9ufV1cXFxcCiY9JlxtYXRoYmZ7Wn1fXHRleHR7R1JNfVx0ZXh0e3Zhcn1bXG1hdGhiZnt1fV1cbWF0aGJme1p9X1x0ZXh0e0dSTX1eVCArIFxtYXRoYmZ7SX0gXHNpZ21hXjJcXFxcCiY9JlxtYXRoYmZ7Wn1fXHRleHR7R1JNfVxtYXRoYmZ7SX1cc2lnbWFeMl91XG1hdGhiZntafV9cdGV4dHtHUk19XlQgKyBcbWF0aGJme0l9IFxzaWdtYV4yX1xlcHNpbG9uIFxcXFwKJj0mXG1hdGhiZntafV9cdGV4dHtHUk19XG1hdGhiZntafV9cdGV4dHtHUk19XlQgXHNpZ21hXjJfdSsgXG1hdGhiZntJfSBcc2lnbWFeMl9cZXBzaWxvbgpcZW5ke2FycmF5fQokJAoKTm90ZSB0aGF0IHRoZSBtb2RlbCBpcyBvZnRlbiBhbHNvIHdyaXR0ZW4gaW4gYW5vdGhlciB3YXk6CgokJApcdGFnezF9ClxtYXRoYmZ7WX0gPSBcbWF0aGJme3h9X1x0ZXh0e3Rlc3R9XGJldGFfXHRleHR7dGVzdH0gKyBcbWF0aGJme1h9X2NcYm9sZHN5bWJvbHtcYmV0YX1fYyArIFxtYXRoYmZ7Z30gK1xib2xkc3ltYm9se1xlcHNpbG9ufSAKJCQKCi0gd2l0aCAkXG1hdGhiZntnfSBcc2ltIFx0ZXh0e01WTn0oXG1hdGhiZnswfSxcbWF0aGJme0t9XHNpZ21hXjJfZykkIAoKCi0gJFxtYXRoYmZ7S30kIHRoZSAkTiBcdGltZXMgTiQgZW1waXJpY2FsIGtpbnNoaXAgbWF0cml4IAoKJCQKXG1hdGhiZntLfSA9IFxmcmFje1xtYXRoYmZ7Wn1fXHRleHR7R1JNfVxtYXRoYmZ7Wn1eVF9cdGV4dHtHUk19fXtNfSAKJCQgCgotICRcc2lnbWFfZ14yJCB0aGUgcG9seWdlbmljIHZhcmlhbmNlICRcc2lnbWFfZ14yPU1cc2lnbWFfdV4yJAoKIyMgTWFpbiBhZHZhbnRhZ2VzIG9mIExNTSBtZXRob2QKCjEuIEJldHRlciBjb250cm9sIG9mIGZhbHNlIHBvc2l0aXZlIGFzc29jaWF0aW9ucyBieSBjb3JyZWN0aW5nIGZvciBwb3B1bGF0aW9uIG9yIHJlbGF0ZWRuZXNzIHN0cnVjdHVyZSAKMi4gQW4gaW5jcmVhc2UgaW4gcG93ZXI6IAotIFRocm91Z2ggdGhlIGNvcnJlY3Rpb24gZm9yIHRoaXMgc3RydWN0dXJlLiAKLSBieSBjb25kaXRpb25pbmcgb24gYXNzb2NpYXRlZCBsb2NpIG90aGVyIHRoYW4gdGhlIGNhbmRpZGF0ZSBsb2N1cy4gCgoKIyMgUGl0ZmFsbHMgb2YgTE1NCgoxLiBDb21wdXRhdGlvbmFsIGNvbXBsZXhpdHk6IAoKICAtICRNID4gNTAwLjAwMCQsICROID4gNzAwMDAkCiAgLSBCdWlsZGluZyB0aGUgR1JNICgkTSBcdGltZXMgTSQgbWF0cml4KQogIC0gRXN0aW1hdGluZyB0aGUgbWVhbiBhbmQgdmFyaWFuY2UgY29tcG9uZW50cyAgZm9yIGVhY2ggb2YgdGhlICRNJCBjYW5kaWRhdGUgU05QISAKICAtIEFzc29jaWF0aW9uIHN0YXRpc3RpY3MgZm9yIGVhY2ggdmFyaWFudCAoZm9yIGVhY2ggU05QISkKCjIuIExvc3MgaW4gcG93ZXIgd2hlbiB0aGUgY2FuZGlkYXRlIG1hcmtlciBpcyBpbmNsdWRlZCBpbiB0aGUgR1JNCgozLiBVc2luZyBhIHNtYWxsIHN1YnNldCBvZiBtYXJrZXJzIGluIHRoZSBHUk0gY2FuIGNvbXByb21pc2UgY29ycmVjdGlvbiBmb3Igc3RyYXRpZmljYXRpb24KCiMgQm9sdCBMTU0gCgpXaGF0IGRvZXMgcHJvamVjdGluZyBvdXQgY292YXJpYXRlcyAkXG1hdGhiZntYfV9jJCBtZWFuPyAKCiMjIEludGVybWV6em8gUHJvamVjdGlvbnMgCgpMaW5lYXIgcmVncmVzc2lvbiBhbmQgbGVhc3Qgc3F1YXJlcyBhcmUgcHJvamVjdGlvbnMuIAoKIyMjIFRveS1EYXRhCgpDb25zaWRlciB0aGUgZm9sbG93aW5nIHRveS1kYXRhc2V0IHdpdGggMyBvYnNlcnZhdGlvbiAoWCxZKToKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKZGF0YSA8LSBkYXRhLmZyYW1lKHg9MTozLHk9YygxLDIsMikpCmRhdGEKYGBgCgojIyMgU2NhbGFyIG1vZGVsIGZvcm0KCi0gQ29uc2lkZXIgYSB2ZWN0b3Igb2YgcHJlZGljdG9ycyAkXG1hdGhiZnt4fT0oeF8xLFxsZG90cyx4X3ApJCBhbmQKLSBhIHJlYWwtdmFsdWVkIHJlc3BvbnNlICRZJAotIHRoZW4gdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGNhbiBiZSB3cml0dGVuIGFzClxbCnlfaT1cYmV0YV8wK1xzdW1cbGltaXRzX3tqPTF9XnAgeF97aWp9XGJldGFfaiArIFxlcHNpbG9uClxdCgoKU2NhbGFyIG1vZGVsIGZvciB0aGUgdG95IGRhdGFzZXQKCiQkCnlfaT1cYmV0YV8wK1xiZXRhXzF4ICsgXGVwc2lsb25faQokJAoKSWYgd2Ugd3JpdGUgdGhlIG1vZGVsIGZvciBlYWNoIG9ic2VydmF0aW9uOgoKJCQKXGJlZ2lue2FycmF5fSB7bGNsfQoxICY9JiBcYmV0YV8wK1xiZXRhXzEgMSAgKyBcZXBzaWxvbl8xIFxcCjIgJj0mIFxiZXRhXzAgKyBcYmV0YV8xIDIgKyBcZXBzaWxvbl8yIFxcCjIgJj0mIFxiZXRhXzArXGJldGFfMSAzKyBcZXBzaWxvbl8zIFxcClxlbmR7YXJyYXl9CiQkCgoKIyMjIFZlY3Rvci9NYXRyaXggZm9ybQoKLSAkbiQgb2JzZXJ2YXRpb25zICQoXG1hdGhiZnt4fV8xLHlfMSkgXGxkb3RzIChcbWF0aGJme3h9X24seV9uKSQgd2l0aCAkXG1hdGhiZnt4fV8xXlQ9W1xiZWdpbnthcnJheX17Y2NjY30gMSYgeF8xJiBcbGRvdHMmIHhfcFxlbmR7YXJyYXl9XSQKLSBSZWdyZXNzaW9uIGluIG1hdHJpeCBub3RhdGlvbgpcW1xtYXRoYmZ7WX09XG1hdGhiZntYXGJldGF9ICsgXG1hdGhiZntcZXBzaWxvbn1cXQp3aXRoICRcbWF0aGJme1l9PVxsZWZ0W1xiZWdpbnthcnJheX17Y315XzFcXCBcdmRvdHNcXHlfblxlbmR7YXJyYXl9XHJpZ2h0XSQsCiRcbWF0aGJme1h9PVxsZWZ0W1xiZWdpbnthcnJheX17Y2NjY30gMSZ4X3sxMX0mXGxkb3RzJnhfezFwfVxcClx2ZG90cyZcdmRvdHMmJlx2ZG90c1xcCjEmeF97bjF9JlxsZG90cyZ4X3tucH0KXGVuZHthcnJheX1ccmlnaHRdJCBvciAkXG1hdGhiZntYfT1cbGVmdFtcYmVnaW57YXJyYXl9e2N9IFxtYXRoYmZ7eH1fMV5UXFxcdmRvdHNcXFxtYXRoYmZ7eH1fbl5UXGVuZHthcnJheX1ccmlnaHRdJCwKJFxib2xkc3ltYm9se1xiZXRhfT1cbGVmdFtcYmVnaW57YXJyYXl9e2N9XGJldGFfMFxcIFx2ZG90c1xcIFxiZXRhX3BcZW5ke2FycmF5fVxyaWdodF0kIGFuZAokXG1hdGhiZntcZXBzaWxvbn09XGxlZnRbXGJlZ2lue2FycmF5fXtjfSBcZXBzaWxvbl8xIFxcIFx2ZG90cyBcXCBcZXBzaWxvbl9uXGVuZHthcnJheX1ccmlnaHRdJAoKCkFwcGxpY2F0aW9uIHRvIHRveSBkYXRhc2V0OiAKCiQkClxtYXRoYmZ7WX0gPSBcbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9K1xib2xkc3ltYm9se1xlcHNpbG9ufQokJAoKd2l0aAoKJCQKXG1hdGhiZntZfT1cbGVmdFsKXGJlZ2lue2FycmF5fXtjfQoxXFwKMlxcCjJcXApcZW5ke2FycmF5fVxyaWdodF0sClxxdWFkClxtYXRoYmZ7WH09IFxsZWZ0WwpcYmVnaW57YXJyYXl9e2NjfQoxJjFcXAoxJjJcXAoxJjNcXApcZW5ke2FycmF5fQpccmlnaHRdLApccXVhZCBcYm9sZHN5bWJvbHtcYmV0YX0gPSBcbGVmdFsKXGJlZ2lue2FycmF5fXtjfQpcYmV0YV8wXFwKXGJldGFfMVxcClxlbmR7YXJyYXl9ClxyaWdodF0KXHF1YWQKXHRleHR7YW5kfQpccXVhZApcYm9sZHN5bWJvbHtcZXBzaWxvbn09ClxsZWZ0WwpcYmVnaW57YXJyYXl9e2N9ClxlcHNpbG9uXzFcXApcZXBzaWxvbl8yXFwKXGVwc2lsb25fMwpcZW5ke2FycmF5fQpccmlnaHRdCiQkCgojIyMgSW50ZXJwcmV0YXRpb24KCkZyb20gdGhlIGxpbmVhciByZWdyZXNzaW9uIHdlIGdldApcWwogIEVbWSBcbWlkIFxtYXRoYmZ7eH1dID0gXG1hdGhiZnt4fV5UXGJvbGRzeW1ib2x7XGJldGF9IC4KXF0KCi0gSGVuY2UsIHRoZSAkXGJldGEkIHBhcmFtZXRlcnMgcmVsYXRlIHRoZSBwcmVkaWN0b3IgJFxtYXRoYmZ7eH0kIHRvIHRoZSBtZWFuIG91dGNvbWUuCi0gSWYgd2Uga25vdyB0aGUgY292YXJpYXRlIHBhdHRlcm4gJFxtYXRoYmZ7eH0kIHdlIGNhbiB1c2UgdGhlIG1vZGVsIHRvCnByZWRpY3QgJFkkLgoKRm9yIGEgbW9kZWwgd2l0aCBhIHNpbmdsZSBwcmVkaWN0b3IsIGUuZy4gWSBpcyBCTUkgYW5kICR4XzEkIGlzIGJsb29kcHJlc3N1cmUgKEJQKSB3ZSBvYnRhaW4KClxbCkVbWSBcbWlkIHhfMV0gPSBcYmV0YV8wICsgXGJldGFfMSB4XzEKXF0KYW5kClxbClxiZWdpbnthcnJheX17cmNjbH0KIFxiZXRhXzEgJj0mIEVbWVxtaWQgeF8xPWIrMV0gJi0gRVtZXG1pZCB4XzE9Yl0gXFwKICY9JiBcYmV0YV8wICsgXGJldGFfMSAoYisxKSYgLSBcYmV0YV8wIC0gXGJldGFfMSBiCiBcZW5ke2FycmF5fQpcXQoKLSBUaGUgcGFyYW1ldGVyICAkXGJldGFfMSQgaGFzIGFuIGludGVycHJldGF0aW9uIGFzIHRoZSBhdmVyYWdlIGRpZmZlcmVuY2UgaW4gQk1JIGJldHdlZW4gc3ViamVjdHMgdGhhdCBkaWZmZXIgd2l0aCAxbW1IRyBpbiBCUC4KCi0gVGhlIHBhcmFtZXRlciAgJFxiZXRhXzEkICBkb2VzIG5vdCBzYXkgbXVjaCBhYm91dCBpbmRpdmlkdWFsIG91dGNvbWVzLiBUaGUgcmVzaWR1YWwgdmFyaWFuY2UgJFxzaWdtYV4yJCBkZXRlcm1pbmVzIGhvdyBtdWNoIGluZGl2aWR1YWwgb3V0Y29tZXMgdmFyeSBhYm91dCB0aGUgbWVhbiBvdXRjb21lLgoKCkZvciBhIG1vZGVsIHdpdGggbXVsdGlwbGUgcHJlZGljdG9yczogQlAgKCR4XzEkKSwgc2V4ICgkeF8yJCksIGFnZSAoJHhfMyQpIHdlIG9idGFpbgoKXFsKRVtZIFxtaWQgeF8xLCB4XzIsIHhfM10gPSBcYmV0YV8wICsgXGJldGFfMSB4XzEgKyBcYmV0YV8yIHhfMiArIFxiZXRhXzMgeF8zIApcXQphbmQKXFsKXGJlZ2lue2FycmF5fXtyY2x9CiBcYmV0YV8xICY9JiBFW1lcbWlkIHhfMT1iKzEsIHhfMj1zLCB4XzMgPSBhXSAtIEVbWVxtaWQgeF8xPWIsIHhfMj1zLCB4XzMgPSBhXVxcXFwgCiAmPSYgXGJldGFfMCArIFxiZXRhXzEgKGIrMSkgKyBcYmV0YV8zIHMgKyBcYmV0YV8zIGEgIC0gXGJldGFfMCAtIFxiZXRhXzEgYiAtIFxiZXRhXzMgcyAtIFxiZXRhXzMgYS4KXGVuZHthcnJheX0KXF0KCi0gVGhlIHBhcmFtZXRlciAgJFxiZXRhXzEkIGhhcyBhbiBpbnRlcnByZXRhdGlvbiBhcyB0aGUgYXZlcmFnZSBkaWZmZXJlbmNlIGluIEJNSSBiZXR3ZWVuIHN1YmplY3RzIG9mIHRoZSBzYW1lIHNleCBhbmQgdGhlIHNhbWUgYWdlIHRoYXQgZGlmZmVyIDEgbW1IZyBpbiBCUC4KLSBvciBhdmVyYWdlIGRpZmZlcmVuY2UgaW4gQk1JIGJldHdlZW4gc3ViamVjdHMgdGhhdCBkaWZmZXIgMSBtbUhnIGluIEJQIHdoaWxlIGNvbnRyb2xsaW5nIGZvciBhZ2UgYW5kIGdlbmRlci4KCgoKVGhlICRcYm9sZHN5bWJvbHtcYmV0YX0kIHBhcmFtZXRlcnMgYXJlIHVzZWQgdG8gbWVhc3VyZSBhc3NvY2lhdGlvbiwgYnV0IGEgJFxib2xkc3ltYm9se1xiZXRhfSBcbmVxIFxtYXRoYmZ7MH0kIGRvZXMgbm90IG5lY2Vzc2FyaWx5IG1lYW4gdGhhdCB0aGUgbW9kZWwgd2lsbCBnaXZlIGdvb2QgcHJlZGljdGlvbnMuCgokJApcaGF0IFkgPSBcbWF0aGJme3h9XlQgXGhhdCBcYmV0YQokJApNb2RlbCBmaXQgYW5kIHByZWRpY3Rpb25zIGJhc2VkIG9uIHRoZSB0b3kgZGF0YXNldAoKYGBge3IgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQpsbTEgPC0gbG0oeX54LGRhdGEpCmRhdGEkeWhhdCA8LSBsbTEkZml0dGVkCgpkYXRhICU+JQogIGdncGxvdChhZXMoeCx5KSkgKwogIGdlb21fcG9pbnQoKSArCiAgeWxpbSgwLDQpICsKICB4bGltKDAsNCkgKwogIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0gInJlZCIsIGZ1bGxyYW5nZSA9IFRSVUUpICsKICBnZW9tX3BvaW50KGFlcyh4PXgsIHkgPXloYXQpLCBwY2ggPSAyLCBzaXplID0gMywgY29sb3IgPSAicmVkIikgKwogIGdlb21fc2VnbWVudChkYXRhID0gZGF0YSwgYWVzKHggPSB4LCB4ZW5kID0geCwgeSA9IHksIHllbmQgPSB5aGF0KSwgbHR5ID0gMiApCmBgYAoKIyMjIEVzdGltYXRpb24gb2YgJFxib2xkc3ltYm9se1xiZXRhfSQKIyMjIyBPcmRpbmFyeSBMZWFzdCBTcXVhcmVzIChPTFMpCgotIE1pbmltaXplIHRoZSBzdW0gb2Ygc3F1YXJlcyBvZiB0aGUgZXJyb3JzClxiZWdpbntlcW5hcnJheSp9Clx0ZXh0e1NTRX0oXGJvbGRzeW1ib2x7XGJldGF9KSY9JlxzdW1cbGltaXRzX3tpPTF9Xm4gZV4yX2lcXFxcCiY9JlxzdW1cbGltaXRzX3tpPTF9Xm4gXGxlZnQoeV9pLVxiZXRhXzAtXHN1bVxsaW1pdHNfe2o9MX1ecCB4X3tpan1cYmV0YV9qXHJpZ2h0KV4yClxlbmR7ZXFuYXJyYXkqfQoKLSBvciBpbiBtYXRyaXggbm90YXRpb24KXGJlZ2lue2VxbmFycmF5Kn0KXHRleHR7U1NFfShcYm9sZHN5bWJvbHtcYmV0YX0pJj0mXG1hdGhiZntlfV5UXG1hdGhiZntlfVxcXFwKJj0mIFxsZWZ0W1xiZWdpbnthcnJheX17Y2NjfSBlXzEgJlxsZG90cyYgZV9uIFxlbmR7YXJyYXl9XHJpZ2h0XVxsZWZ0W1xiZWdpbnthcnJheX17Y31lXzFcXFx2ZG90c1xcZV9uXGVuZHthcnJheX1ccmlnaHRdXFwKJj0mIGVfMV4yICsgZV8yXjIgKyBcbGRvdHMgKyBlX25eMlxcXFwKJj0mKFxtYXRoYmZ7WX0tXG1hdGhiZntYXGJldGF9KV5UKFxtYXRoYmZ7WX0tXG1hdGhiZntYXGJldGF9KVxcXFwKJj0mXFZlcnQgXG1hdGhiZntZfS1cbWF0aGJme1hcYmV0YX1cVmVydF4yXzJcXApcZW5ke2VxbmFycmF5Kn0Kd2l0aCB0aGUgJExfMiQtbm9ybSBvZiBhICRwJC1kaW0uIHZlY3RvciAkdiQgJFxWZXJ0IFxtYXRoYmZ7dn0gXFZlcnRfMj1cc3FydHt2XzFeMitcbGRvdHMrdl9wXjJ9JCAgICAKJFxyaWdodGFycm93JCAkXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19PVx0ZXh0e2FyZ21pbn1fXGJldGEgXFZlcnQgXG1hdGhiZntZfS1cbWF0aGJme1hcYmV0YX1cVmVydF4yXzIkCgoKLS0tCgojIyMjIE1pbmltaXplIFNTRQpcWwpcYmVnaW57YXJyYXl9e2NjY30KXGZyYWN7XHBhcnRpYWx9e1xwYXJ0aWFsIFxib2xkc3ltYm9se1xiZXRhfX0gXHRleHR7U1NFfSY9JlxtYXRoYmZ7MH1cXFxcClxmcmFje1xwYXJ0aWFsfXtccGFydGlhbCBcYm9sZHN5bWJvbHtcYmV0YX19KFxtYXRoYmZ7WX0tXG1hdGhiZntYXGJldGF9KV5UKFxtYXRoYmZ7WX0tXG1hdGhiZntYfVxib2xkc3ltYm9se1xiZXRhfSkmPSZcbWF0aGJmezB9XFxcXAotMlxtYXRoYmZ7WH1eVChcbWF0aGJme1l9LVxtYXRoYmZ7WH1cYm9sZHN5bWJvbHtcYmV0YX0pJj0mXG1hdGhiZnswfVxcXFwKLTJcbWF0aGJme1h9XlRcbWF0aGJme1l9ICsgMlxtYXRoYmZ7WH1eVFxtYXRoYmZ7WFxiZXRhfSY9JjBcXFxcClxtYXRoYmZ7WH1eVFxtYXRoYmZ7WFxiZXRhfSY9JlxtYXRoYmZ7WH1eVFxtYXRoYmZ7WX1cXFxcClxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fSY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntZfQpcZW5ke2FycmF5fQpcXQoKSXQgY2FuIGJlIHNob3duIHRoYXQgdGhlIGVzdGltYXRvciBpcyB1bmJpYXNlZDoKCiQkClxiZWdpbnthcnJheX17Y2NjfQpFW1xoYXR7XGJvbGRzeW1ib2x7XGJldGF9fV0mPSZFWyhcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntZfV1cXCBcXAomPSYoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVCBFW1xtYXRoYmZ7WX1dXFxcXAomPSYgKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cbWF0aGJme1h9XlRcbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9XFxcXAomPSYgXGJvbGRzeW1ib2x7XGJldGF9ClxlbmR7YXJyYXl9CiQkCgoKLS0tCgoKIyMjIFByb2plY3Rpb24KClRoZXJlIGlzIGFsc28gYW5vdGhlciBwaWN0dXJlIHRvIHJlZ3Jlc3Npb246CgotIEluc3RlYWQgb2YgcGxvdHRpbmcgZWFjaCBvYnNlcnZhdGlvbiAkaT0gMSBcbGRvdHMgbiQgYXMgYSBkYXRhLXBvaW50IGluICRcbWF0aGJie1J9XnAkIHdpdGggZGltZW5zaW9ucyAkMSBcbGRvdHMgcCQgZm9yIGV2ZXJ5IHZhcmlhYmxlL2ZlYXR1cmUgdGhhdCBpcyByZWNvcmRlZCBmb3IgZWFjaCBvYnNlcnZhdGlvbgoKLSBXZSBjYW4gYWxzbyBwbG90ICRcbWF0aGJme1l9JCwgJFxoYXR7XG1hdGhiZntZfX0kIGFuZCBlYWNoIGNvbHVtbiBvZiAkXG1hdGhiZntYfSQ6ICRcbWF0aGJme1h9X2okIHdpdGggJGo9MSBcbGRvdHMgcCQgYXMgYSB2ZWN0b3IgaW4gJFxtYXRoYmJ7Un1ebiQgd2l0aCBkaW1lbnNpb25zICQxIFxsZG90cyBuJCBmb3IgZXZlcnkgb2JzZXJ2YXRpb24uCgotIEluIHRoaXMgcmVwcmVzZW50YXRpb24gbGluZWFyIHJlZ3Jlc3Npb24gY2FuIGJlIGludGVycHJldGVkIGFzIGEgcHJvamVjdGlvbiBvZiB0aGUgdmVjdG9yICRcbWF0aGJme1l9JCBvbnRvIHRoZSBzdWJzcGFjZSBvZiAkXG1hdGhiYntSfV5uJCB0aGF0IGlzIHNwYW5uZWQgYnkgdGhlIHZlY3RvcnMgZm9yIHRoZSBwcmVkaWN0b3JzICRcbWF0aGJme1h9XzEgXGxkb3RzIFxtYXRoYmZ7WH1fcCQuCgotIFRoZSBzcGFjZSAkXG1hdGhiZntYfV8xIFxsZG90cyBcbWF0aGJme1h9X3AkIGlzIGFsc28gcmVmZXJyZWQgdG8gYXMgdGhlIGNvbHVtbiBzcGFjZSBvZiAkXG1hdGhiZntYfSQsIHRoZSBzcGFjZSB0aGF0IGNvbnNpc3RzIG9mIGFsbCBsaW5lYXIgY29tYmluYXRpb25zIG9mIHRoZSB2ZWN0b3JzIG9mIHRoZSBwcmVkaWN0b3JzIG9yIGNvbHVtbnMgJFxtYXRoYmZ7WH1fMSBcbGRvdHMgXG1hdGhiZntYfV9wJC4KCgojIyMjIEludGVybWV6em86IFByb2plY3Rpb24gb2YgdmVjdG9yIG9uIFggIGFuZCBZIGF4aXMKCiQkClxtYXRoYmZ7ZX09XGxlZnRbXGJlZ2lue2FycmF5fXtjfSBlXzFcXGVfMlxlbmR7YXJyYXl9XHJpZ2h0XSwgXG1hdGhiZnt1fV8xID0gIFxsZWZ0W1xiZWdpbnthcnJheX17Y30gMVxcMFxlbmR7YXJyYXl9XHJpZ2h0XSwgXG1hdGhiZnt1fV8yID0gIFxsZWZ0W1xiZWdpbnthcnJheX17Y30gMFxcMVxlbmR7YXJyYXl9XHJpZ2h0XQokJAoKCmBgYHtyIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxlY2hvPUZBTFNFfQpwbG90ZGF0YSA8LSBkYXRhLmZyYW1lKGUxPTMsZTI9MikKcGxvdGRhdGEgJT4lIGdncGxvdChhZXMoeD1lMSx5PWUyKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMywgeSA9IDIsIHhlbmQgPSAzLCB5ZW5kID0gMCksY29sb3I9Im9yYW5nZSIsbGluZXR5cGU9MixzaXplPTIpICsKZ2VvbV9zZWdtZW50KGFlcyh4ID0gMywgeSA9IDIsIHhlbmQgPSAwLCB5ZW5kID0gMiksY29sb3I9Im9yYW5nZSIsbGluZXR5cGU9MixzaXplPTIpICsKIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gMywgeWVuZCA9IDIpLAogICAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC41LCAiY20iKSksY29sb3I9InJlZCIsc2l6ZT0yKSArCiBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IDMsIHllbmQgPSAwKSwKICAgICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuNSwgImNtIikpLGNvbG9yPSJvcmFuZ2UiLHNpemU9MikgKwogZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSAwLCB5ZW5kID0gMiksCiAgICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjUsICJjbSIpKSxjb2xvcj0ib3JhbmdlIixzaXplPTIpICArCiBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IDAsIHllbmQgPSAxKSwKICAgICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuNSwgImNtIikpLHNpemU9MikgKwpnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IDEsIHllbmQgPSAwKSwKICAgICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuNSwgImNtIikpLHNpemU9MikgKwogZ2VvbV90ZXh0KGFlcyh4PTMuMDUseT0yLjIsbGFiZWw9ImU9KGUxLGUyKSIpLGNvbG9yPSJyZWQiLHNpemUgPSA3LGhqdXN0PSJsZWZ0IikgKwogZ2VvbV90ZXh0KGFlcyh4PTMuMDUseT0wLGxhYmVsPSIoZTEsMCkiKSxjb2xvcj0ib3JhbmdlIixzaXplID0gNyxoanVzdD0ibGVmdCIsdmp1c3Q9InRvcCIpICsKIGdlb21fdGV4dChhZXMoeD0wLHk9Mi4yLGxhYmVsPSIoMCxlMikiKSxjb2xvcj0ib3JhbmdlIixzaXplID0gNyxoanVzdD0ibGVmdCIpICsKIGdlb21fdGV4dChhZXMoeD0wLjA1LHk9MS4yLGxhYmVsPSJ1Mj0oMCwxKSIpLHNpemUgPSA3LGhqdXN0PSJsZWZ0IikgKwogICBnZW9tX3RleHQoYWVzKHg9MS4wNSx5PTAuMixsYWJlbD0idTE9KDEsMCkiKSxzaXplID0gNyxoanVzdD0ibGVmdCIpICArCiAgIGNvb3JkX2ZpeGVkKCkgKwogIHhsaW0oLS41LDQpICsKICB5bGltKC0uNSwyLjUpCmBgYAoKCgoxLiBQcm9qZWN0aW9uIG9mIHZlY3RvciBlIG9uIHgtYXhpcwoKXGJlZ2lue2VxbmFycmF5Kn0KXG1hdGhiZnt1fV8xXlQgXG1hdGhiZntlfSAmPSYgXFZlcnQgXG1hdGhiZnt1fV8xXFZlcnRfMiAgXFZlcnQgXG1hdGhiZntlfV8xXFZlcnRfMiBcY29zIDxcbWF0aGJme3V9XzEsXG1hdGhiZntlfV8xPlxcCiY9JlxsZWZ0W1xiZWdpbnthcnJheX17Y2N9IDEmMFxlbmR7YXJyYXl9XHJpZ2h0XSBcbGVmdFtcYmVnaW57YXJyYXl9e2N9IGVfMVxcZV8yXGVuZHthcnJheX1ccmlnaHRdXFwgJj0mIDFcdGltZXMgZV8xICsgMCBcdGltZXMgZV8yIFxcCiY9JiBlXzFcXApcZW5ke2VxbmFycmF5Kn0KCjIuIFByb2plY3Rpb24gb2YgdmVjdG9yIGUgb24geS1heGlzCgpcYmVnaW57ZXFuYXJyYXkqfQpcbWF0aGJme3V9XzJeVCBcbWF0aGJme2V9ICY9JiBcbGVmdFtcYmVnaW57YXJyYXl9e2NjfSAwJjFcZW5ke2FycmF5fVxyaWdodF0gXGxlZnRbXGJlZ2lue2FycmF5fXtjfSBlXzFcXGVfMlxlbmR7YXJyYXl9XHJpZ2h0XVxcICY9JiAwXHRpbWVzIGVfMSArIDEgXHRpbWVzIGVfMiBcXAomPSYgZV8yClxlbmR7ZXFuYXJyYXkqfQoKMy4gUHJvamVjdGlvbiBvZiB2ZWN0b3IgZSBvbiBpdHNlbGYKCgpcYmVnaW57ZXFuYXJyYXkqfQpcbWF0aGJme2V9XlQgXG1hdGhiZntlfSAmPSZcbGVmdFtcYmVnaW57YXJyYXl9e2NjfSBlXzEmZV8yXGVuZHthcnJheX1ccmlnaHRdIFxsZWZ0W1xiZWdpbnthcnJheX17Y30gZV8xXFxlXzJcZW5ke2FycmF5fVxyaWdodF1cXAomPSZlXzFeMitlXzJeMlxcCiY9JlxWZXJ0IGUgXFZlcnReMl8yIFxyaWdodGFycm93IFx0ZXh0eyBQeXRoYWdvcmVhbiB0aGVvcmVtfQpcZW5ke2VxbmFycmF5Kn0KCi0tLQoKIyMjIyBJbnRlcnByZXRhdGlvbiBvZiBsZWFzdCBzcXVhcmVzIGFzIGEgcHJvamVjdGlvbgoKRml0dGVkIHZhbHVlczoKCiQkClxiZWdpbnthcnJheX17bGNsfQpcaGF0e1xtYXRoYmZ7WX19ICY9JiBcbWF0aGJme1h9XGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19XFwKJj0mIFxtYXRoYmZ7WH0gKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cbWF0aGJme1h9XlRcbWF0aGJme1l9XFwKJj0mIFxtYXRoYmZ7SFl9ClxlbmR7YXJyYXl9CiQkCndpdGggJFxtYXRoYmZ7SH0kIHRoZSBwcm9qZWN0aW9uIG1hdHJpeCBhbHNvIHJlZmVycmVkIHRvIGFzIHRoZSBoYXQgbWF0cml4LgoKCmBgYHtyfQpYIDwtIG1vZGVsLm1hdHJpeCh+eCxkYXRhKQpYCmBgYAoKYGBge3J9Clh0WCA8LSB0KFgpJSolWApYdFgKYGBgCgpgYGB7cn0KWHRYaW52IDwtIHNvbHZlKHQoWCklKiVYKQpYdFhpbnYKYGBgCgpgYGB7cn0KSCA8LSBYICUqJSBYdFhpbnYgJSolIHQoWCkKSApgYGAKCgpgYGB7cn0KWSA8LSBkYXRhJHkKWWhhdCA8LSBIJSolWQpZaGF0CmBgYAoKCi0gV2UgY2FuIGFsc28gaW50ZXJwcmV0IHRoZSBmaXQgYXMgdGhlIHByb2plY3Rpb24gb2YgdGhlICRuXHRpbWVzIDEkIHZlY3RvciAkXG1hdGhiZntZfSQgb24gdGhlIGNvbHVtbiBzcGFjZSBvZiB0aGUgbWF0cml4ICRcbWF0aGJme1h9JC4KCi0gU28gZWFjaCBjb2x1bW4gaW4gJFxtYXRoYmZ7WH0kIGlzIGFsc28gYW4gJG5cdGltZXMgMSQgdmVjdG9yLgoKLSBGb3IgdGhlIHRveSBleGFtcGxlIG49MyBhbmQgcD0yLgogIFRoZSBvdGhlciBwaWN0dXJlIHRvIGxpbmVhciByZWdyZXNzaW9uIGlzIHRvIGNvbnNpZGVyICRYXzAkLCAkWF8xJCBhbmQgJFkkIGFzIHZlY3RvcnMgaW4gdGhlIHNwYWNlIG9mIHRoZSBkYXRhICRcbWF0aGJie1J9Xm4kLCBoZXJlICRcbWF0aGJie1J9XjMkIGJlY2F1c2Ugd2UgaGF2ZSB0aHJlZSBkYXRhIHBvaW50cy4KU28gdGhlIGNvbHVtbiBzcGFjZSBvZiBYIGlzIGEgcGxhbmUgaW4gdGhlIHRocmVlIGRpbWVuc2lvbmFsIHNwYWNlLgoKXFsKXGhhdHtcbWF0aGJme1l9fSA9IFxtYXRoYmZ7WH0gKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX0gXG1hdGhiZntYfV5UIFxtYXRoYmZ7WX0KXF0KCjEuIFBsYW5lIHNwYW5uZWQgYnkgY29sdW1uIHNwYWNlOgpUaGUgb3RoZXIgcGljdHVyZSB0byBsaW5lYXIgcmVncmVzc2lvbiBpcyB0byBjb25zaWRlciAkWF8wJCwgJFhfMSQgYW5kICRZJCBhcyB2ZWN0b3JzIGluIHRoZSBzcGFjZSBvZiB0aGUgZGF0YSAkXG1hdGhiYntSfV5uJCwgaGVyZSAkXG1hdGhiYntSfV4zJCBiZWNhdXNlIHdlIGhhdmUgdGhyZWUgZGF0YSBwb2ludHMuCgoKYGBge3J9Cm9yaWdpblJuIDwtIGRhdGEuZnJhbWUoWDE9MCxYMj0wLFgzPTApCmRhdGEkeDAgPC0gMQpkYXRhUm4gPC0gZGF0YS5mcmFtZSh0KGRhdGEpKQoKbGlicmFyeShwbG90bHkpCgpwMSA8LSBwbG90X2x5KAogICAgb3JpZ2luUm4sCiAgICB4ID0gfiBYMSwKICAgIHkgPSB+IFgyLAogICAgej0gfiBYMywgbmFtZT0ib3JpZ2luIikgJT4lCiAgYWRkX21hcmtlcnModHlwZT0ic2NhdHRlcjNkIikgJT4lCiAgbGF5b3V0KAogICAgc2NlbmUgPSBsaXN0KAogICAgICBhc3BlY3Rtb2RlPSJjdWJlIiwKICAgICAgeGF4aXMgPSBsaXN0KHJhbmdlPWMoLTQsNCkpLCB5YXhpcyA9IGxpc3QocmFuZ2U9YygtNCw0KSksIHpheGlzID0gbGlzdChyYW5nZT1jKC00LDQpKQogICAgICApCiAgICApCnAxIDwtIHAxICU+JQogIGFkZF90cmFjZSgKICAgIHggPSBjKDAsMSksCiAgICB5ID0gYygwLDApLAogICAgeiA9IGMoMCwwKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJncmV5IiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZSA9ICJvYnMxIikgJT4lCiAgYWRkX3RyYWNlKAogICAgeCA9IGMoMCwwKSwKICAgIHkgPSBjKDAsMSksCiAgICB6ID0gYygwLDApLAogICAgbW9kZSA9ICJsaW5lcyIsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUsIGNvbG9yID0gImdyZXkiKSwKICAgIHR5cGU9InNjYXR0ZXIzZCIsCiAgICBuYW1lID0gIm9iczIiKSAlPiUKICBhZGRfdHJhY2UoCiAgICB4ID0gYygwLDApLAogICAgeSA9IGMoMCwwKSwKICAgIHogPSBjKDAsMSksCiAgICBtb2RlID0gImxpbmVzIiwKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSwgY29sb3IgPSAiZ3JleSIpLAogICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgIG5hbWUgPSAib2JzMyIpICU+JQogIGFkZF90cmFjZSgKICAgIHggPSBjKDAsMSksCiAgICB5ID0gYygwLDEpLAogICAgeiA9IGMoMCwxKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJibGFjayIpLAogICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgIG5hbWUgPSAiWDEiKSAlPiUKICAgIGFkZF90cmFjZSgKICAgIHggPSBjKDAsMSksCiAgICB5ID0gYygwLDIpLAogICAgeiA9IGMoMCwzKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJibGFjayIpLAogICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgIG5hbWUgPSAiWDIiKQpwMQpgYGAKCgoyLiBWZWN0b3Igb2YgWToKCkFjdHVhbCB2YWx1ZXMgb2YgJFxtYXRoYmZ7WX0kOgoKYGBge3J9CmRhdGEkeQpgYGAKClxbClxtYXRoYmZ7WX09XGxlZnRbXGJlZ2lue2FycmF5fXtjfQpgciBkYXRhJHlbMV1gIFxcCmByIGRhdGEkeVsyXWAgXFwKYHIgZGF0YSR5WzNdYApcZW5ke2FycmF5fVxyaWdodF0KXF0KCmBgYHtyfQpwMiA8LSBwMSAlPiUKICBhZGRfdHJhY2UoCiAgICB4ID0gYygwLFlbMV0pLAogICAgeSA9IGMoMCxZWzJdKSwKICAgIHogPSBjKDAsWVszXSksCiAgICBtb2RlID0gImxpbmVzIiwKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSwgY29sb3IgPSAicmVkIiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZSA9ICJZIikKcDIKYGBgCgozLiBQcm9qZWN0aW9uIG9mIFkgb250byBjb2x1bW4gc3BhY2UKCkFjdHVhbCB2YWx1ZXMgb2YgZml0dGVkIHZhbHVlcyAkXG1hdGhiZntcaGF0e1l9fSQ6CgpgYGB7cn0KZGF0YSR5aGF0CmBgYAoKXFsKXG1hdGhiZntZfT1cbGVmdFtcYmVnaW57YXJyYXl9e2N9CmByIGRhdGEkeWhhdFsxXWAgXFwKYHIgZGF0YSR5aGF0WzJdYCBcXApgciBkYXRhJHloYXRbM11gClxlbmR7YXJyYXl9XHJpZ2h0XQpcXQoKYGBge3J9CnAyIDwtIHAyICU+JQogIGFkZF90cmFjZSgKICAgIHggPSBjKDAsWWhhdFsxXSksCiAgICB5ID0gYygwLFloYXRbMl0pLAogICAgeiA9IGMoMCxZaGF0WzNdKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJvcmFuZ2UiKSwKICAgIHR5cGU9InNjYXR0ZXIzZCIsCiAgICBuYW1lPSJZaGF0IikgJT4lCiAgICBhZGRfdHJhY2UoCiAgICB4ID0gYyhZWzFdLFloYXRbMV0pLAogICAgeSA9IGMoWVsyXSxZaGF0WzJdKSwKICAgIHogPSBjKFlbM10sWWhhdFszXSksCiAgICBtb2RlID0gImxpbmVzIiwKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSwgY29sb3IgPSAicmVkIiwgZGFzaD0iZGFzaCIpLAogICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgIG5hbWU9IlkgLT4gWWhhdCIKICAgICkKcDIKYGBgCgokXG1hdGhiZntZfSQgaXMgcHJvamVjdGVkIGluIHRoZSBjb2x1bW4gc3BhY2Ugb2YgJFxtYXRoYmZ7WH0kISBzcGFubmVkIGJ5IHRoZSBjb2x1bW5zLgoKIyMjIyBIb3cgZG9lcyB0aGlzIHByb2plY3Rpb24gd29ya3M/CgokJApcYmVnaW57YXJyYXl9e2xjbH0KXGhhdHtcbWF0aGJme1l9fSAmPSYgXG1hdGhiZntYfSAoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFxtYXRoYmZ7WX1cXAomPSYgXG1hdGhiZntYfShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTEvMn0oXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xLzJ9XG1hdGhiZntYfV5UXG1hdGhiZntZfVxcCiY9JiBcbWF0aGJme1N9XG1hdGhiZntTfV5UXG1hdGhiZntZfQpcZW5ke2FycmF5fQokJAoKCi0gJFxtYXRoYmZ7U30kIGlzIGEgbmV3IG9ydGhvbm9ybWFsIGJhc2lzIGluICRcbWF0aGJie1J9XjIkLCBhIHN1YnNwYWNlIG9mICRcbWF0aGJie1J9XjMkCgotIFRoZSBzcGFjZSBzcGFubmVkIGJ5ICRcbWF0aGJme1N9JCBhbmQgJFxtYXRoYmZ7WH0kIGlzIHRoZSBjb2x1bW4gc3BhY2Ugb2YgJFxtYXRoYmZ7WH0kLCBlLmcuIGl0IGNvbnRhaW5zIGFsbCBwb3NzaWJsZSBsaW5lYXIgY29tYmluYW50aW9ucyBvZiAkXG1hdGhiZntYfSQuCiRcbWF0aGJme1N9XnRcbWF0aGJme1l9JCBpcyB0aGUgcHJvamVjdGlvbiBvZiAkXG1hdGhiZntZfSQgb24gdGhpcyBuZXcgb3J0aG9ub3JtYWwgYmFzaXMKCmBgYHtyfQpzdmRYIDwtIHN2ZChYKSAjIFNWRCBYID0gVURWXlQKWHRYaW52U3FydCA8LSBzdmRYJHYgJSolZGlhZygxL3N2ZFgkZCklKiV0KHN2ZFgkdikKUyA8LSBYICUqJSBYdFhpbnZTcXJ0CmBgYAoKCi0gJFxtYXRoYmZ7U30kIG9ydGhvbm9ybWFsIGJhc2lzCgpgYGB7cn0KUwp0KFMpJSolUwpgYGAKCi0gJFxtYXRoYmZ7U1N9XlQkIGVxdWFscyBwcm9qZWN0aW9uIG1hdHJpeAoKYGBge3J9ClMlKiV0KFMpCkgKYGBgCgoKYGBge3J9CnAzIDwtIHAxICU+JQogIGFkZF90cmFjZSgKICAgIHggPSBjKDAsU1sxLDFdKSwKICAgIHkgPSBjKDAsU1syLDFdKSwKICAgIHogPSBjKDAsU1szLDFdKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJibHVlIiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZSA9ICJTMSIpICU+JQogIGFkZF90cmFjZSgKICAgIHggPSBjKDAsU1sxLDJdKSwKICAgIHkgPSBjKDAsU1syLDJdKSwKICAgIHogPSBjKDAsU1szLDJdKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJibHVlIiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZSA9ICJTMiIpCgpwMwpgYGAKCgotICRcbWF0aGJme1N9XlRcbWF0aGJme1l9JCBpcyB0aGUgcHJvamVjdGlvbiBvZiAkXG1hdGhiZntZfSQgaW4gdGhlIHNwYWNlIHNwYW5uZWQgYnkgJFxtYXRoYmZ7U30kLgotIEluZGVlZCAkXG1hdGhiZntTfV8xXlRcbWF0aGJme1l9JAoKYGBge3J9CnA0IDwtIHAzICU+JQogIGFkZF90cmFjZSgKICAgIHggPSBjKDAsWVsxXSksCiAgICB5ID0gYygwLFlbMl0pLAogICAgeiA9IGMoMCxZWzNdKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJyZWQiKSwKICAgIHR5cGU9InNjYXR0ZXIzZCIsCiAgICBuYW1lID0gIlkiKSAlPiUKICBhZGRfdHJhY2UoCiAgICB4ID0gYygwLFNbMSwxXSooU1ssMV0lKiVZKSksCiAgICB5ID0gYygwLFNbMiwxXSooU1ssMV0lKiVZKSksCiAgICB6ID0gYygwLFNbMywxXSooU1ssMV0lKiVZKSksCiAgICBtb2RlID0gImxpbmVzIiwKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSwgY29sb3IgPSAicmVkIixkYXNoPSJkYXNoIiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZT0iWSAtPiBTMSIpICU+JSBhZGRfdHJhY2UoCiAgICB4ID0gYyhZWzFdLFNbMSwxXSooU1ssMV0lKiVZKSksCiAgICB5ID0gYyhZWzJdLFNbMiwxXSooU1ssMV0lKiVZKSksCiAgICB6ID0gYyhZWzNdLFNbMywxXSooU1ssMV0lKiVZKSksCiAgICBtb2RlID0gImxpbmVzIiwKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSwgY29sb3IgPSAicmVkIiwgZGFzaD0iZGFzaCIpLAogICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgIG5hbWU9IlkgLT4gUzEiKQpwNApgYGAKCi0gYW5kICRcbWF0aGJme1N9XzJeVFxtYXRoYmZ7WX0kCmBgYHtyfQpwNSA8LSBwNCAlPiUKICBhZGRfdHJhY2UoCiAgICB4ID0gYygwLFNbMSwyXSooU1ssMl0lKiVZKSksCiAgICB5ID0gYygwLFNbMiwyXSooU1ssMl0lKiVZKSksCiAgICB6ID0gYygwLFNbMywyXSooU1ssMl0lKiVZKSksCiAgICBtb2RlID0gImxpbmVzIiwKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSwgY29sb3IgPSAicmVkIixkYXNoPSJkYXNoIiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZT0iWSAtPiBTMiIpICU+JSBhZGRfdHJhY2UoCiAgICB4ID0gYyhZWzFdLFNbMSwyXSooU1ssMl0lKiVZKSksCiAgICB5ID0gYyhZWzJdLFNbMiwyXSooU1ssMl0lKiVZKSksCiAgICB6ID0gYyhZWzNdLFNbMywyXSooU1ssMl0lKiVZKSksCiAgICBtb2RlID0gImxpbmVzIiwKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSwgY29sb3IgPSAicmVkIiwgZGFzaD0iZGFzaCIpLAogICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgIG5hbWU9IlkgLT4gUzIiKQpwNQpgYGAKCi0gWWhhdCBpcyB0aGUgcmVzdWx0aW5nIHZlY3RvciB0aGF0IGxpZXMgaW4gdGhlIHBsYW5lIHNwYW5uZWQgYnkgJFxtYXRoYmZ7U31fMSQgYW5kICRcbWF0aGJme1N9XzIkIGFuZCB0aHVzIGFsc28gaW4gdGhlIGNvbHVtbiBzcGFjZSBvZiAkXG1hdGhiZntYfSQuCgpgYGB7cn0KcDYgPC0gcDUgJT4lCiAgYWRkX3RyYWNlKAogICAgeCA9IGMoMCxZaGF0WzFdKSwKICAgIHkgPSBjKDAsWWhhdFsyXSksCiAgICB6ID0gYygwLFloYXRbM10pLAogICAgbW9kZSA9ICJsaW5lcyIsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUsIGNvbG9yID0gIm9yYW5nZSIpLAogICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgIG5hbWUgPSAiWWhhdCIpICU+JQogIGFkZF90cmFjZSgKICAgIHggPSBjKFlbMV0sWWhhdFsxXSksCiAgICB5ID0gYyhZWzJdLFloYXRbMl0pLAogICAgeiA9IGMoWVszXSxZaGF0WzNdKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJtYXJvb24yIiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZSA9ICJlIikgJT4lCiAgYWRkX3RyYWNlKAogICAgeCA9IGMoU1sxLDFdKihTWywxXSUqJVkpLFloYXRbMV0pLAogICAgeSA9IGMoU1syLDFdKihTWywxXSUqJVkpLFloYXRbMl0pLAogICAgeiA9IGMoU1szLDFdKihTWywxXSUqJVkpLFloYXRbM10pLAogICAgbW9kZSA9ICJsaW5lcyIsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUsIGNvbG9yID0gIm9yYW5nZSIsIGRhc2g9ImRhc2giKSwKICAgIHR5cGU9InNjYXR0ZXIzZCIsCiAgICBuYW1lID0gIlkgLT4gUyIpICAlPiUKICBhZGRfdHJhY2UoCiAgICB4ID0gYyhTWzEsMl0qKFNbLDJdJSolWSksWWhhdFsxXSksCiAgICB5ID0gYyhTWzIsMl0qKFNbLDJdJSolWSksWWhhdFsyXSksCiAgICB6ID0gYyhTWzMsMl0qKFNbLDJdJSolWSksWWhhdFszXSksCiAgICBtb2RlID0gImxpbmVzIiwKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSwgY29sb3IgPSAib3JhbmdlIiwgZGFzaD0iZGFzaCIpLAogICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgIG5hbWUgPSAiWSAtPiBTIikKcDYKYGBgCgojIyMgRXJyb3IKCk5vdGUsIHRoYXQgaXQgaXMgYWxzbyBjbGVhciBmcm9tIHRoZSBlcXVhdGlvbiBpbiB0aGUgZGVyaXZhdGlvbiBvZiB0aGUgbGVhc3Qgc3F1YXJlcyBzb2x1dGlvbiB0aGF0IHRoZSByZXNpZHVhbCBpcyBvcnRob2dvbmFsIG9uIHRoZSBjb2x1bW4gc3BhY2U6CgpcWwogLTIgXG1hdGhiZntYfV5UKFxtYXRoYmZ7WX0tXG1hdGhiZntYfVxib2xkc3ltYm9se1xiZXRhfSkgPSAwClxdCgoKIyMjIEN1cnNlIG9mIGRpbWVuc2lvbmFsaXR5PwoKLSBJbWFnaW5lIHdoYXQgaGFwcGVucyB3aGVuIHAgYXBwcm9hY2hlcyBuICRwPW4kIG9yIGJlY29tZXMgbXVjaCBsYXJnZXIgdGhhbiBwID4+IG4hISEKCi0gU3VwcG9zZSB0aGF0IHdlIGFkZCBhIHByZWRpY3RvciAkXG1hdGhiZntYfV8yID0gWzIsMCwxXV5UJD8KCiQkClxtYXRoYmZ7WX09XGxlZnRbClxiZWdpbnthcnJheX17Y30KMVxcCjJcXAoyXFwKXGVuZHthcnJheX1ccmlnaHRdLApccXVhZApcbWF0aGJme1h9PSBcbGVmdFsKXGJlZ2lue2FycmF5fXtjY2N9CjEmMSYyXFwKMSYyJjBcXAoxJjMmMVxcClxlbmR7YXJyYXl9ClxyaWdodF0sClxxdWFkIFxib2xkc3ltYm9se1xiZXRhfSA9IFxsZWZ0WwpcYmVnaW57YXJyYXl9e2N9ClxiZXRhXzBcXApcYmV0YV8xXFwKXGJldGFfMgpcZW5ke2FycmF5fQpccmlnaHRdClxxdWFkClx0ZXh0e2FuZH0KXHF1YWQKXGJvbGRzeW1ib2x7XGVwc2lsb259PQpcbGVmdFsKXGJlZ2lue2FycmF5fXtjfQpcZXBzaWxvbl8xXFwKXGVwc2lsb25fMlxcClxlcHNpbG9uXzMKXGVuZHthcnJheX0KXHJpZ2h0XQokJAoKCmBgYHtyfQpkYXRhJHgyIDwtIGMoMiwwLDEpCmZpdCA8LSBsbSh5fngreDIsZGF0YSkKIyBwcmVkaWN0IHZhbHVlcyBvbiByZWd1bGFyIHh5IGdyaWQKeDFwcmVkIDwtIHNlcSgtMSwgNCwgbGVuZ3RoLm91dCA9IDEwKQp4MnByZWQgPC0gc2VxKC0xLCA0LCBsZW5ndGgub3V0ID0gMTApCnh5IDwtIGV4cGFuZC5ncmlkKHggPSB4MXByZWQsCngyID0geDJwcmVkKQp5cHJlZCA8LSBtYXRyaXggKG5yb3cgPSAzMCwgbmNvbCA9IDMwLApkYXRhID0gcHJlZGljdChmaXQsIG5ld2RhdGEgPSBkYXRhLmZyYW1lKHh5KSkpCgpsaWJyYXJ5KHBsb3QzRCkKCgojIGZpdHRlZCBwb2ludHMgZm9yIGRyb3BsaW5lcyB0byBzdXJmYWNlCnRoPTIwCnBoPTUKc2NhdHRlcjNEKGRhdGEkeCwKICBkYXRhJHgyLAogIFksCiAgcGNoID0gMTYsCiAgY29sPSJkYXJrYmx1ZSIsCiAgY2V4ID0gMSwKICB0aGV0YSA9IHRoLAogIHRpY2t0eXBlID0gImRldGFpbGVkIiwKICB4bGFiID0gIngxIiwKICB5bGFiID0gIngyIiwKICB6bGFiID0gInkiLAogIGNvbHZhcj1GQUxTRSwKICBidHkgPSAiZyIsCiAgeGxpbT1jKC0xLDMpLAogIHlsaW09YygtMSwzKSwKICB6bGltPWMoLTIsNCkpCgoKei5wcmVkM0QgPC0gb3V0ZXIoCiAgeDFwcmVkLAogIHgycHJlZCwKICBmdW5jdGlvbih4MSx4MikKICB7CiAgICBmaXQkY29lZlsxXSArIGZpdCRjb2VmWzJdKngxK2ZpdCRjb2VmWzJdKngyCiAgfSkKCngucHJlZDNEIDwtIG91dGVyKAogIHgxcHJlZCwKICB4MnByZWQsCiAgZnVuY3Rpb24oeCx5KSB4KQoKeS5wcmVkM0QgPC0gb3V0ZXIoCiAgeDFwcmVkLAogIHgycHJlZCwKICBmdW5jdGlvbih4LHkpIHkpCgpzY2F0dGVyM0QoZGF0YSR4LAogIGRhdGEkeDIsCiAgZGF0YSR5LAogIHBjaCA9IDE2LAogIGNvbD0iZGFya2JsdWUiLAogIGNleCA9IDEsCiAgdGhldGEgPSB0aCwKICB0aWNrdHlwZSA9ICJkZXRhaWxlZCIsCiAgeGxhYiA9ICJ4MSIsCiAgeWxhYiA9ICJ4MiIsCiAgemxhYiA9ICJ5IiwKICBjb2x2YXI9RkFMU0UsCiAgYnR5ID0gImciLAogIHhsaW09YygtMSw0KSwKICB5bGltPWMoLTEsNCksCiAgemxpbT1jKC0yLDQpKQoKc3VyZjNEKAogIHgucHJlZDNELAogIHkucHJlZDNELAogIHoucHJlZDNELAogIGNvbD0iYmx1ZSIsCiAgZmFjZXRzPU5BLAogIGFkZD1UUlVFKQpgYGAKCk5vdGUsIHRoYXQgdGhlIGxpbmVhciByZWdyZXNzaW9uIGlzIG5vdyBhIHBsYW5lLgoKSG93ZXZlciwgd2Ugb2J0YWluIGEgcGVyZmVjdCBmaXQgYW5kIGFsbCB0aGUgZGF0YSBwb2ludHMgYXJlIGZhbGxpbmcgaW4gdGhlIHBsYW5lISBgciBzZXQuc2VlZCg0KTsgZW1vamk6OmVtb2ppKCJmZWFyIilgCgpUaGlzIGlzIG9idmlvdXMgaWYgd2UgbG9vayBhdCB0aGUgY29sdW1uIHNwYWNlIG9mIFghCgpgYGB7cn0KWCA8LSBjYmluZChYLGMoMiwwLDEpKQpzdmRYIDwtIHN2ZChYKSAjIFNWRCBYID0gVURWXlQKWHRYaW52U3FydCA8LSBzdmRYJHYgJSolZGlhZygxL3N2ZFgkZCklKiV0KHN2ZFgkdikKUyA8LSBYICUqJSBYdFhpbnZTcXJ0CgpwNyA8LSBwMSAlPiUKICBhZGRfdHJhY2UoCiAgICB4ID0gYygwLDIpLAogICAgeSA9IGMoMCwwKSwKICAgIHogPSBjKDAsMSksCiAgICBtb2RlID0gImxpbmVzIiwKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSwgY29sb3IgPSAiZGFya2dyZWVuIiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZSA9ICJYMyIpCnA3CmBgYAoKYGBge3J9CnA4IDwtIHA3ICU+JQogIGFkZF90cmFjZSgKICAgIHggPSBjKDAsU1sxLDFdKSwKICAgIHkgPSBjKDAsU1syLDFdKSwKICAgIHogPSBjKDAsU1szLDFdKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJibHVlIiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZSA9ICJTMSIpICU+JQogIGFkZF90cmFjZSgKICAgIHggPSBjKDAsU1sxLDJdKSwKICAgIHkgPSBjKDAsU1syLDJdKSwKICAgIHogPSBjKDAsU1szLDJdKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJibHVlIiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZSA9ICJTMiIpICU+JQogIGFkZF90cmFjZSgKICAgIHggPSBjKDAsU1sxLDNdKSwKICAgIHkgPSBjKDAsU1syLDNdKSwKICAgIHogPSBjKDAsU1szLDNdKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJibHVlIiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZSA9ICJTMyIpCgpwOApgYGAKCi0gVGhlIGNvbHVtbiBzcGFjZSBub3cgc3BhbnMgdGhlIGVudGlyZSAgJFxtYXRoYmJ7Un1eMyQhCgotIFdpdGggdGhlIGludGVyY2VwdCBhbmQgdGhlIHR3byBwcmVkaWN0b3JzIHdlIGNhbiB0aHVzIGZpdCBldmVyeSBkYXRhc2V0IHRoYXQgb25seSBoYXMgMyBvYnNlcnZhdGlvbnMgZm9yIHRoZSBwcmVkaWN0b3JzIGFuZCB0aGUgcmVzcG9uc2UuCgotIFNvIHRoZSBtb2RlbCBjYW4gbm8gbG9uZ2VyIGJlIHVzZWQgdG8gZ2VuZXJhbGlzZSB0aGUgcGF0dGVybnMgc2VlbiBpbiB0aGUgZGF0YSB0b3dhcmRzIHRoZSBwb3B1bGF0aW9uIChuZXcgb2JzZXJ2YXRpb25zKS4KCi0gUHJvYmxlbSBvZiBvdmVyZml0dGluZyEhIQoKLSBJZiAkcCA+PiBuJCB0aGVuIHRoZSBwcm9ibGVtIGdldHMgZXZlbiB3b3JzZSEgVGhlbiB0aGVyZSBpcyBldmVuIG5vIGxvbmdlciBhIHVuaXF1ZSBzb2x1dGlvbiB0byB0aGUgbGVhc3Qgc3F1YXJlcyBwcm9ibGVtLi4uCgotIEluZGVlZCwgdGhlbiB3ZSBoYXZlIG1vcmUgdmVjdG9ycy9kaW1lbnNpb25zL2NvbHVtbnMgaW4gWCAgdGhhbiBkYXRhcG9pbnRzIQoKCiMjIyBWYXJpYW5jZSBFc3RpbWF0b3I/ClxbClxiZWdpbnthcnJheX17Y2NsfQpcaGF0e1xib2xkc3ltYm9se1xTaWdtYX19X3tcaGF0e1xib2xkc3ltYm9se1xiZXRhfX19CiY9Jlx0ZXh0e3Zhcn1cbGVmdFsoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFxtYXRoYmZ7WX1ccmlnaHRdXFxcXAomPSYoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFx0ZXh0e3Zhcn1cbGVmdFtcbWF0aGJme1l9XHJpZ2h0XVxtYXRoYmZ7WH0oXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfSBcdGV4dHsoKil9XFxcXAomPSYoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVChcbWF0aGJme0l9XHNpZ21hXjIpXG1hdGhiZntYfShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9ClxcXFwKJj0mKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cbWF0aGJme1h9XlRcbWF0aGJme0l9XHF1YWRcbWF0aGJme1h9KFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cc2lnbWFeMlxcXFwKJVxoYXR7XGJvbGRtYXRoe1xTaWdtYX19X3tcaGF0e1xib2xkc3ltYm9se1xiZXRhfX19Jj0mKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cbWF0aGJme1h9XlQgXHRleHR7dmFyfVxsZWZ0W1xtYXRoYmZ7WX1ccmlnaHRdKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cbWF0aGJme1h9XFwKJj0mKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cbWF0aGJme1h9XlRcbWF0aGJme1h9KFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cc2lnbWFeMlxcXFwKJj0mKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cc2lnbWFeMgpcZW5ke2FycmF5fQpcXQoKKCopIFVuZGVyIGFzc3VtcHRpb24gdGhhdCBhbGwgb2JzZXJ2YXRpb25zICRcbWF0aGJme1l9JCBhcmUgaW5kZXBlbmRlbnQgYW5kIGlkZW50aWNhbGx5IGRpc3RyaWJ1dGVkLgoKVGhlIGZhY3QgdGhhdCAkXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19JCBpcyB1bmJpYXNlZCBhbmQgaGFzIGEgdmFyaWFuY2Ugb2YgJChcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XHNpZ21hXjIkIHdpbGwgYmUgaW1wb3J0YW50IHdoZW4gYXNzZXNzaW5nIGFzc29jaWF0aW9uIQoKIyMjIFByb2plY3RpbmcgY292YXJpYXRlcyBvdXQgCgpXZSBjYW4gZ2V0IHJpZCBvZiBjb3ZhcmlhdGVzICRcbWF0aGJme1h9X2MkIGJ5IHByb2plY3RpbmcgdGhlbSBvdXQsIGkuZS4gCgokJApcbWF0aGJme1B9X2MgPSAoXG1hdGhiZntJfSAtIFxtYXRoYmZ7WH1feChcbWF0aGJme1h9X3heVFxtYXRoYmZ7WH1feCleey0xfVxtYXRoYmZ7WH1feF5UKQokJAoKU28gb25lIG9idGFpbnMgCgokJApcYmVnaW57YXJyYXl9e2NjY2NjY2NjY30KXG1hdGhiZntQfV9jIFxtYXRoYmZ7eX0gJj0mIFxtYXRoYmZ7UH1fYyBcbWF0aGJme3h9X1x0ZXh0e3Rlc3R9XGJldGFfXHRleHR7dGVzdH0mICsgJiBcbWF0aGJme1B9X2MgXG1hdGhiZntYfV9jXGJvbGRzeW1ib2x7XGJldGF9X2MgJisgJlxtYXRoYmZ7UH1fYyBcbWF0aGJme1p1fSAmKyZcbWF0aGJme1B9X2MgXGJvbGRzeW1ib2x7XGVwc2lsb259IFxcClx0aWxkZXtcbWF0aGJme3l9fSAmPSYgXHRpbGRle1xtYXRoYmZ7eH19X1x0ZXh0e3Rlc3R9XGJldGFfXHRleHR7dGVzdH0gJismXG1hdGhiZnswfSYrICZcdGlsZGV7XG1hdGhiZntafX1cbWF0aGJme3V9ICYrJlx0aWxkZXtcYm9sZHN5bWJvbHtcZXBzaWxvbn19ClxlbmR7YXJyYXl9CiQkCgotICRcbWF0aGJme1B9X3gkIHByb2plY3RzIHZlY3RvcnMgdG8gYW4gKE4g4oiSIEMpLWRpbWVuc2lvbmFsIHN1YnNwYWNlIG9ydGhvZ29uYWwgb24gdGhlIGtub3duIGNvdmFyaWF0ZXMuIAotIEtlZXAgaW4gbWluZCB0aGF0IHRoZXNlICROJCB2ZWN0b3JzIGFsbCBiZWxvbmcgdG8gdGhlIChOIOKIkiBDKS1kaW1lbnNpb25hbCBzdWJzcGFjZSEgICAKJFxyaWdodGFycm93JCBJbXBvcnRhbnQgd2hlbiBlc3RpbWF0aW5nICRcdGlsZGV7XG1hdGhiZnt5fX0kIGFuZCAgJFx0aWxkZXtcbWF0aGJme1p9fSQgCgojIyBCb2x0LUxNTSB1c2VzIGEgc2NvcmUgdGVzdCB0byB0ZXN0IGZvciB0aGUgY2FuZGlkYXRlIFNOUAoKSWYgYWxsIG1vZGVsIGFzc3VtcHRpb25zIGhvbGQgd2UgY2FuIGRlcml2ZSBhICRcY2hpXjIkIHRlc3QgdW5kZXIgdGhlIG51bGwgaHlwb3RoZXNpcyAkSF8wOiBcYm9sZHN5bWJvbHtcYmV0YX09XG1hdGhiZnswfSQ6IAoKJCQKdD1caGF0e1xiZXRhfVxoYXR7XGJvbGRzeW1ib2x7XFNpZ21hfX1fe1xoYXR7XGJldGF9fV57LTF9XGhhdHtcYmV0YX0KJCQKd2hpY2ggZm9sbG93cyBhbiBhc3ltcHRvdGljICRcY2hpXjIkIGRpc3RyaWJ1dGlvbiB1bmRlciAkSF8wJC4gCgpJbiBvdXIgY2FzZSB3ZSBvbmx5IHRlc3QgZm9yIG9uZSBtb2RlbCBwYXJhbWV0ZXIsIGkuZS4gdGhhdCBvZiB0aGUgY2FuZGlkYXRlIFNOUCBzbyB0aGUgdGVzdCByZWR1Y2VzIHRvIAoKJCQKdF97XGNoaV4yfT1cZnJhY3tcaGF0e1xiZXRhfV9cdGV4dHt0ZXN0fV4yfXtcdGV4dHtTRX1fe1xoYXR7XGJldGF9X1x0ZXh0e3Rlc3R9fV4yfSAKJCQKd2hpY2ggZm9sbG93cyBhbiBhc3ltcHRvdGljICRcY2hpXjJfMSQgZGlzdHJpYnV0aW9uIHdpdGggb25lIGRlZ3JlZSBvZiBmcmVlZG9tIHVuZGVyICRIXzAkLiAKCldlIG5lZWQgdGhlIGVzdGltYXRvciBmb3IgJFxoYXR7XGJldGF9JCBhbmQgJFxoYXR7XGJvbGRzeW1ib2x7XFNpZ21hfX0kLgoKIyMjIEludGVybWV6em8gZml0dGluZyBsaW5lYXIgbWl4ZWQgbW9kZWxzIChMTU0pCgpJbiB0aGUgZ2VuZXJhbCBmb3JtdWxhdGlvbiBvZiBMTU0gYWxzbyBhbGxvdyByYW5kb20gZWZmZWN0cyB0byBiZSBjb3JyZWxhdGVkLiAKCiQkClxsZWZ0XHsKXGJlZ2lue2FycmF5fXtjY2N9ClkgJj0mIFxtYXRoYmZ7WH1cYmV0YSArIFxtYXRoYmZ7WnV9ICsgXGJvbGRzeW1ib2x7XGVwc2lsb259XFwgClxtYXRoYmZ7dX0gJlxzaW0mXHRleHR7TVZOfShcbWF0aGJmezB9LFxtYXRoYmZ7R30pXFwKXGJvbGRzeW1ib2x7XGVwc2lsb259ICAmXHNpbSZcdGV4dHtNVk59KFxtYXRoYmZ7MH0sXG1hdGhiZntJfVxzaWdtYV4yX1xlcHNpbG9uKQpcZW5ke2FycmF5fQpccmlnaHQuCiQkCiAKVGhpcyBleHByZXNzaW9uIGltcGxpZXMgdGhyZWUgZGlmZmVyZW50IG1vZGVsIHJlcHJlc2VudGF0aW9uczogCgoxLiB0aGUgY29uZGl0aW9uYWwgbW9kZWwgKGNvbmRpdGlvbmFsIG9uIHRoZSByYW5kb20gZWZmZWN0cykKCiQkCmYoXG1hdGhiZntZfVx2ZXJ0IFxtYXRoYmZ7WCwgdX0pXHNpbSBcdGV4dHtNVk59KFxtYXRoYmZ7WH1cYm9sZHN5bWJvbHtcYmV0YX0rXG1hdGhiZntadX0sXG1hdGhiZntJfVxzaWdtYV4yX1xlcHNpbG9uKQokJAoKMi4gdGhlIGpvaW50IG1vZGVsIG9mICRcbWF0aGJme1l9JCBhbmQgJFxtYXRoYmZ7dX0kCiQkClxiZWdpbnthcnJheX17Y2NsfQpmKFxtYXRoYmZ7WSwgdX1cdmVydCBcbWF0aGJme1h9KSAmXHNpbSYgZihcbWF0aGJme1l9XHZlcnQgXG1hdGhiZntYLCB1fSkgZihcbWF0aGJme3V9KSBcXFxcCiZcc2ltJlx0ZXh0e01WTn0oXG1hdGhiZntYfVxib2xkc3ltYm9se1xiZXRhfStcbWF0aGJme1p1fSxcbWF0aGJme0l9XHNpZ21hXjJfXGVwc2lsb24pXHRpbWVzXHRleHR7TVZOfShcbWF0aGJmezB9LFxtYXRoYmZ7R30pClxlbmR7YXJyYXl9CiQkCjMuIE1hcmdpbmFsIG1vZGVsIG9mICRcbWF0aGJme1l9JAoKJCQKXGJlZ2lue2FycmF5fXtyY2x9CmYoXG1hdGhiZntZfVx2ZXJ0IFxtYXRoYmZ7WH0pICZcc2ltJiBcaW50IGYoXG1hdGhiZntZLCB1fVx2ZXJ0IFxtYXRoYmZ7WH0pIGRcbWF0aGJme3V9IFxcXFwKJlxzaW0mIFx0ZXh0e01WTn0oXG1hdGhiZntYfVxib2xkc3ltYm9se1xiZXRhfSxcbWF0aGJme1Z9KVxcXFwKXG1hdGhiZntWfSAmPSYgXG1hdGhiZntaR31cbWF0aGJme1p9XlQgKyBcbWF0aGJme0l9XHNpZ21hXjJfXGVwc2lsb24KXGVuZHthcnJheX0KJCQKU28gZXN0aW1hdGluZyB0aGUgbWl4ZWQgbW9kZWwgaW1wbGllcyBlc3RpbWF0aW5nCgotIE1vZGVsIHBhcmFtZXRlcnMgb2YgZml4ZWQgZWZmZWN0cyAkXGJvbGRzeW1ib2x7XGJldGF9JAotIFZhcmlhbmNlIGNvbXBvbmVudHMgJFxtYXRoYmZ7R30kIGFuZCAkXHNpZ21hXjJfXGVwc2lsb24kCgokXHJpZ2h0YXJyb3ckIFdlIGNhbiBkbyB0aGF0IGJ5IHVzaW5nIG1heGltdW0gbGlrZWxpaG9vZC4gCgotIENob29zaW5nIHRoZSBkYXRhIHBhcmFtZXRlcnMgYXMgc3VjaCBzbyB0aGF0IHRoZSBwcm9iYWJpbGl0eSB0byBvYnNlcnZlIHRoZSBkYXRhIHVuZGVyIHRoZSBtYXJnaW5hbCBtb2RlbCBiZWNvbWVzIG1heGltYWwgCgokJApMKFxib2xkc3ltYm9se1xiZXRhfSwgXG1hdGhiZntHfSxcc2lnbWFeMl9cZXBzaWxvbiBcdmVydCBcbWF0aGJme1gsIFl9KSA9IFxmcmFjezF9eygyXHBpKV57Ti8yfXxcbWF0aGJme1Z9fF57MS8yfX0gXGV4cFxsZWZ0KC1cZnJhY3sxfXsyfSAoXG1hdGhiZntZfSAtIFxtYXRoYmZ7WH1cYm9sZHN5bWJvbHtcYmV0YX0pXlQgXG1hdGhiZntWfV57LTF9IChcbWF0aGJme1l9IC0gXG1hdGhiZntYfVxib2xkc3ltYm9se1xiZXRhfSlccmlnaHQpCiQkClRoaXMgaXMgbmFzdHkgdG8gbWF4aW1pc2UgJFxyaWdodGFycm93JCBtYXhpbWlzZSB0aGUgbG9nLWxpa2VsaWhvb2QKCiQkCmwoXGJvbGRzeW1ib2x7XGJldGF9LCBcbWF0aGJme0d9LFxzaWdtYV4yX1xlcHNpbG9uIFx2ZXJ0IFxtYXRoYmZ7WCwgWX0pID0gLVxmcmFje059ezJ9IFxsb2coMlxwaSkgLSBcZnJhY3sxfXsyfSBcbG9nKHxcbWF0aGJme1Z9fCkgLSBcZnJhY3sxfXsyfSAgKFxtYXRoYmZ7WX0gLSBcbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9KV5UIFxtYXRoYmZ7Vn1eey0xfSAoXG1hdGhiZntZfSAtIFxtYXRoYmZ7WH1cYm9sZHN5bWJvbHtcYmV0YX0pCiQkCgokJApcYmVnaW57YXJyYXl9e3JjbH0KXGZyYWN7XHBhcnRpYWx9e1xwYXJ0aWFsIFxiZXRhfSBsKFxib2xkc3ltYm9se1xiZXRhfSwgXG1hdGhiZntHfSxcc2lnbWFeMl9cZXBzaWxvbiBcdmVydCBcbWF0aGJme1gsIFl9KSAmPSYgMCBcXFxcClxmcmFjezF9ezJ9IFxtYXRoYmZ7WH1eVCBcbWF0aGJme1Z9XnstMX0gKFxtYXRoYmZ7WX0gLSBcbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9KSAmPSYgMFxcXFwKXG1hdGhiZntYfV5UIFxtYXRoYmZ7Vn1eey0xfSBcbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9ICY9JlxtYXRoYmZ7WH1eVCBcbWF0aGJme1Z9XnstMX0gXG1hdGhiZntZfVxcXFwKXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19ICY9JiAoXG1hdGhiZntYfV5UIFxtYXRoYmZ7Vn1eey0xfSBcbWF0aGJme1h9KV57LTF9IFxtYXRoYmZ7WH1eVCBcbWF0aGJme1Z9XnstMX0gXG1hdGhiZntZfQpcZW5ke2FycmF5fQokJApUaGUgdmFyaWFuY2UtY292YXJpYW5jZSBtYXRyaXggZm9yIHRoaXMgZXN0aW1hdG9yICBiZWNvbWVzOiAKCiQkClxiZWdpbnthcnJheX17cmNsfQpcdGV4dHt2YXJ9XGxlZnRbXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19XHJpZ2h0XSAmPSYgXHRleHR7dmFyfVxsZWZ0WyhcbWF0aGJme1h9XlQgXG1hdGhiZntWfV57LTF9IFxtYXRoYmZ7WH0pXnstMX0gXG1hdGhiZntYfV5UIFxtYXRoYmZ7Vn1eey0xfSBcbWF0aGJme1l9XHJpZ2h0XVxcXFwKXGJvbGRzeW1ib2x7XFNpZ21hfV9caGF0e1xib2xkc3ltYm9se1xiZXRhfX0gJj0mKFxtYXRoYmZ7WH1eVCBcbWF0aGJme1Z9XnstMX0gXG1hdGhiZntYfSleey0xfSBcbWF0aGJme1h9XlQgXG1hdGhiZntWfV57LTF9IFx0ZXh0e3Zhcn1cbGVmdFtcbWF0aGJme1l9XHJpZ2h0XSBcbWF0aGJme1Z9XnstMX0gXG1hdGhiZntYfSAoXG1hdGhiZntYfV5UIFxtYXRoYmZ7Vn1eey0xfSBcbWF0aGJme1h9KV57LTF9IFxcXFwKXGJvbGRzeW1ib2x7XFNpZ21hfV9caGF0e1xib2xkc3ltYm9se1xiZXRhfX0gJj0mKFxtYXRoYmZ7WH1eVCBcbWF0aGJme1Z9XnstMX0gXG1hdGhiZntYfSleey0xfSBcbWF0aGJme1h9XlQgXG1hdGhiZntWfV57LTF9IFxtYXRoYmZ7Vn0gXG1hdGhiZntWfV57LTF9IFxtYXRoYmZ7WH0gKFxtYXRoYmZ7WH1eVCBcbWF0aGJme1Z9XnstMX0gXG1hdGhiZntYfSleey0xfSBcXFxcClxib2xkc3ltYm9se1xTaWdtYX1fXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19ICY9JihcbWF0aGJme1h9XlQgXG1hdGhiZntWfV57LTF9IFxtYXRoYmZ7WH0pXnstMX0KXGVuZHthcnJheX0KJCQKU28gd2Ugb25seSBuZWVkIHRvIGVzdGltYXRlIHRoZSB2YXJpYW5jZSBjb21wb25lbnRzICRcbWF0aGJme0d9JCBhbmQgJFxzaWdtYV9cZXBzaWxvbl4yJC4gCgotIFRoaXMgY2FuIGJlIGRvbmUgYnkgdGhlIHByb2ZpbGUgbGlrZWxpaG9vZCwgaS5lLiByZXBsYWNpbmcgJFxib2xkc3ltYm9se1xiZXRhfSQgYnkgdGhlIE1MIGVzdGltYXRvciAkKFxtYXRoYmZ7WH1eVCBcbWF0aGJme1Z9XnstMX0gXG1hdGhiZntYfSleey0xfSBcbWF0aGJme1h9XlQgXG1hdGhiZntWfV57LTF9IFxtYXRoYmZ7WX0kLiAKLSBXZSAgdGhlbiBtYXhpbWlzZSB0aGUgcHJvZmlsZSBsaWtlbGlob29kIHRvICRcbWF0aGJme0d9JCBhbmQgJFxzaWdtYV9cZXBzaWxvbl4yJCAKLSBOb3RlIHRoYXQgdGhlIE1MIGZvciB0aGUgdmFyaWFuY2UgY29tcG9uZW50cyBhcmUgYmlhc2VkIGluIHNtYWxsIHNhbXBsZSBiZWNhdXNlIGl0IGRvZXMgbm90IGFjY291bnQgZm9yIHRoZSBsb3NzIGluIGRlZ3JlZXMgb2YgZnJlZWRvbSBkdWUgdG8gdGhlIGVzdGltYXRpb24gb2YgdGhlIGZpeGVkIGVmZmVjdCBtb2RlbCBwYXJhbWV0ZXJzCi0gVGhlcmVmb3JlLCBNTCBpcyBvZnRlbiByZXBsYWNlZCBieSByZXN0cmljdGVkIG1heGltdW0gbGlrZWxpaG9vZCAoUkVNTCkgd2hpY2ggYWRkcyBhIHBlbmFsdHkgdGVybSB0byB0aGUgTUwgdG8gY29ycmVjdCBmb3IgdGhlIGxvc3MgaW4gZGYuCgpPbmNlIGVzdGltYXRvcnMgZm9yIHRoZSB2YXJpYW5jZSBjb21wb25lbnRzICRcaGF0e1xtYXRoYmZ7R319JCBhbmQgJFxoYXR7XHNpZ21hfV9cZXBzaWxvbl4yJCBhcmUgb2J0YWluZWQgd2Ugd2lsbCBwbHVnIHRoZW0gaW4gJFxtYXRoYmZ7Vn0kLCAkXGhhdHtcbWF0aGJme1xiZXRhfX0kIGFuZCAkXGJvbGRzeW1ib2x7XFNpZ21hfV9caGF0e1xib2xkc3ltYm9se1xiZXRhfX0kCgoKCgokJApcYmVnaW57YXJyYXl9e2NjbH0KXGhhdHtcbWF0aGJme1Z9fSAmPSYgXG1hdGhiZntafVxoYXR7XG1hdGhiZntHfX1eey0xfVxtYXRoYmZ7Wn1eVCArIFxtYXRoYmZ7SX1caGF0XHNpZ21hXjJfXGVwc2lsb25cXFxcClxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fSAmPSYgKFxtYXRoYmZ7WH1eVCBcaGF0e1xtYXRoYmZ7Vn19XnstMX0gXG1hdGhiZntYfSleey0xfSBcbWF0aGJme1h9XlQgXGhhdHtcbWF0aGJme1Z9fV57LTF9IFxtYXRoYmZ7WX1cXFxcClxoYXR7XGJvbGRzeW1ib2x7XFNpZ21hfX1fXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19ICY9JihcbWF0aGJme1h9XlQgXGhhdHtcbWF0aGJme1Z9fV57LTF9IFxtYXRoYmZ7WH0pXnstMX0KXGVuZHthcnJheX0KJCQKCk5vdGUsIHRoYXQgCgotIGZvciBvdXIgR1dBUyBMTU0gJFxtYXRoYmZ7R30gPSBcbWF0aGJme0l9IFxzaWdtYV4yX3UkIGFuZCB0aHVzIHJlZHVjZXMgdG8gZXN0aW1hdGluZyAkXHNpZ21hXjJfdSQKLSBUaGUgb3B0aW1pc2F0aW9uIGZvciB0aGUgdmFyaWFuY2UgY29tcG9uZW50cyBpcyBjb21wdXRhdGlvbmFsbHkgZGVtYW5kaW5nIAotICRcaGF0e1xib2xkc3ltYm9se1xiZXRhfX0kIGFuZCAgJFxib2xkc3ltYm9se1xTaWdtYX1fXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19JCBpbnZvbHZlIHRoZSBjb21wdXRhdGlvbmFsbHkgY2hhbGxlbmdpbmcgY2FsY3VsYXRpb24gb2YgCiAgCiAgLSAkXGhhdHtcbWF0aGJme1Z9fV57LTF9JCBhbiAkTiBcdGltZXMgTiQgIHdpdGggJE4gPiA3MC4wMDAkIHN1YmplY3RzCiAgLSAkXG1hdGhiZntafV97R1JNfV5UXG1hdGhiZntafSQgYW4gJE0gXHRpbWVzIE0kIHdpdGggJE0gPiA1MDAuMDAwJCBoaWdoIHF1YWxpdHkgU05QcwogICAgCiMjIyBBZG9wdGluZyByZXN1bHQgdG8gQk9MVC1MTU0KCk5vdGUsIHRoYXQgaW4gQk9MVC1MTU0gdGhleSBwcm9maWxlZCBvdXQgYWxsIGZpeGVkIGVmZmVjdCBleGNlcHQgZm9yIHRoZSBjYW5kaWRhdGUgU05QLiAgICAKXHJpZ2h0YXJyb3cgc28gb25seSBvbmUgZml4ZWQgZWZmZWN0IHBhcmFtZXRlciAkXGhhdHtcYmV0YX1fXHRleHR7dGVzdH0kCgokJApcYmVnaW57YXJyYXl9e2NjbH0KXGhhdHtcYmV0YX1fXHRleHR7dGVzdH0gJj0mIChcdGlsZGV7XG1hdGhiZnt4fX1edF9cdGV4dHt0ZXN0fSBcaGF0e1xtYXRoYmZ7Vn19XnstMX1cdGlsZGV7XG1hdGhiZnt4fX1fXHRleHR7dGVzdH0pXnstMX1cdGlsZGV7XG1hdGhiZnt4fX1edF9cdGV4dHt0ZXN0fSBcaGF0e1xtYXRoYmZ7Vn19XnstMX1cdGlsZGV7XG1hdGhiZnt5fX0gXFxcXAomPSYgXGZyYWN7KFx0aWxkZXtcbWF0aGJme3h9fV50X1x0ZXh0e3Rlc3R9IFxoYXR7XG1hdGhiZntWfX1eey0xfVx0aWxkZXtcbWF0aGJme3l9fSl9eyhcdGlsZGV7XG1hdGhiZnt4fX1edF9cdGV4dHt0ZXN0fSBcaGF0e1xtYXRoYmZ7Vn19XnstMX1cdGlsZGV7XG1hdGhiZnt4fX1fXHRleHR7dGVzdH0pfQpcZW5ke2FycmF5fQokJApOZXh0LCB3ZSBuZWVkIHRoZSAkXHRleHR7U0V9X3tcaGF0e1xiZXRhfV9cdGV4dHt0ZXN0fX0kCgokJApcdGV4dHtTRX1eMl97XGhhdHtcYmV0YX1fXHRleHR7dGVzdH19ID0gKFx0aWxkZXtcbWF0aGJme3h9fV50X1x0ZXh0e3Rlc3R9XGhhdHtcbWF0aGJme1Z9fV57LTF9XHRpbGRle1xtYXRoYmZ7eH19XnRfXHRleHR7dGVzdH0pXnstMX0KJCQKV2UgcGx1ZyAkXGhhdHtcYmV0YX1fXHRleHR7dGVzdH1eMiQgYW5kICRcdGV4dHtTRX1fe1xoYXR7XGJldGF9X1x0ZXh0e3Rlc3R9fV4yJCBpbiB0aGUgJFxjaGleMiQtdGVzdDoKCiQkIApcYmVnaW57YXJyYXl9e2NjbH0KdF97XGNoaV4yfSY9JlxmcmFje1xoYXR7XGJldGF9X1x0ZXh0e3Rlc3R9XjJ9e1x0ZXh0e1NFfV97XGhhdHtcYmV0YX1fXHRleHR7dGVzdH19XjJ9IFxcXFwKJj0mIFxmcmFjeyhcdGlsZGV7XG1hdGhiZnt4fX1edF9cdGV4dHt0ZXN0fSBcaGF0e1xtYXRoYmZ7Vn19XnstMX1cdGlsZGV7XG1hdGhiZnt5fX0pXjJ9eyhcdGlsZGV7XG1hdGhiZnt4fX1edF9cdGV4dHt0ZXN0fSBcaGF0e1xtYXRoYmZ7Vn19XnstMX1cdGlsZGV7XG1hdGhiZnt4fX1fXHRleHR7dGVzdH0pfQpcZW5ke2FycmF5fQokJCAKd2hpY2ggZm9sbG93cyBhICRcY2hpXjJfMSQgd2l0aCBvbmUgZGVncmVlIG9mIGZyZWVkb20uIAoKV2UgY2FuIGNvbnN0cnVjdCBhIHNjb3JlIHRlc3QgIGJ5IHJlcGxhY2luZyB0aGUgdmFyaWFuY2UgY29tcG9uZW50cyAkXHNpZ21hX1xlcHNpbG9uXjIkIGFuZCAkXHNpZ21hX3VeMiQgYnkgdGhlaXIgZXN0aW1hdGVzIHVuZGVyIHRoZSBtb2RlbCBpbXBsaWVkIGJ5ICRIXzA6IFxiZXRhX1x0ZXh0e3Rlc3R9ID0gMCQ6IAoKJCQgClx0aWxkZXtcbWF0aGJme3l9fSBcc2ltIFx0ZXh0e01WTn0oXG1hdGhiZnswfSxcbWF0aGJme1Z9KQokJAoKIyMjIExPQ08gc2NvcmUgdGVzdAoKV2l0aGluIGEgTGVhdmUgT25lIENocm9tb3NvbWUgT3V0IChMT0NPKSBzY2hlbWUsIHRoZSB0ZXN0IHN0YXRpc3RpYyBiZWNvbWVzCgokJCAKXHRhZ3s0fQp0X1x0ZXh0e0xPQ099ID0gXGZyYWN7KFx0aWxkZXtcbWF0aGJme3h9fV50X1x0ZXh0e3Rlc3R9IFxoYXR7Vn1eey0xfV97XHRleHR7TE9DT30sSF8wfVx0aWxkZXtcbWF0aGJme3l9fSleMn17XHRpbGRle1xtYXRoYmZ7eH19XnRfXHRleHR7dGVzdH0gXGhhdHtWfV57LTF9X3tcdGV4dHtMT0NPfSxIXzB9XHRpbGRle1xtYXRoYmZ7eH19X1x0ZXh0e3Rlc3R9fQokJAoKJFxyaWdodGFycm93JCBlZmZlY3Qgb2YgY2FuZGlkYXRlIFNOUCBub3QgYnkgZGlsdXRlZCBieSBwcm94aW1hbCBjb250YW1pbmF0aW9uLiAgICAKJFxyaWdodGFycm93JCB2YXJpYW5jZSBjb21wb25lbnRzIHVuZGVyICRIXzAkICAgICAKJFxyaWdodGFycm93JCAkXGhhdHtWfV57LTF9X3tcdGV4dHtMT0NPfSwgSF8wfSQgIGlzIHRoZSBzYW1lIGZvciBhbGwgY2FuZGlkYXRlIFNOUHMgZnJvbSB0aGUgc2FtZSBjaHJvbW9zb21lLiAKCiMjIEJPTFQtTE1NLWluZgoKIlRoZSBCT0xULUxNTS1pbmYgaW5maW5pdGVzaW1hbCBtaXhlZC1tb2RlbCBzdGF0aXN0aWMgaXMgc2xpZ2h0bHkgZGlmZmVyZW50IgoKIyMjIERlbm9taW5hdG9yIExPQ08gc3RhdGlzdGljIAoKVW5kZXIgdGhlIGFzc3VtcHRpb24gdGhhdCBtYW55IGxvY2kgd2l0aCBzbWFsbCBlZmZlY3RzIGNvbnRyb2wgdGhlIHRyYWl0LCB0aGUgZGVub21pbmF0b3Igb2YgdGhlIExPQ08gc2NvcmUgdGVzdCAgCgokJApcdGlsZGV7XG1hdGhiZnt4fX1edF9cdGV4dHt0ZXN0fSB7Vn1eey0xfV9cdGV4dHtMT0NPfVx0aWxkZXtcbWF0aGJme3h9fV50X1x0ZXh0e3Rlc3R9CiQkICAKCmlzICB2ZXJ5IHNpbWlsYXIgYmV0d2VlbiBhbGwgY2FuZGlkYXRlIFNOUHMgCgokXHJpZ2h0YXJyb3ckIGNhbiBiZSByZXBsYWNlZCBieSBhIGNhbGlicmF0aW9uIGZhY3RvciB0aGF0IG9ubHkgaGFzIHRvIGJlIGVzdGltYXRlZCBvbmNlISAKCgpUaGUgQk9MVC1MTU0taW5mIHN0YXRpc3RpY3MgdGh1cyBiZWNvbWVzOiAKCiQkdF9cdGV4dHtCT0xULUxNTS1pbmZ9ID0gXGZyYWN7KFx0aWxkZXtcbWF0aGJme3h9fV50X1x0ZXh0e3Rlc3R9IFxoYXR7Vn1eey0xfV97XHRleHR7TE9DT30sSF8wfVx0aWxkZXtcbWF0aGJme3l9fSleMn17Y19cdGV4dHtpbmZ9fSQkIAoKd2hpY2ggZm9sbG93cyBhICRcY2hpXjJfMSQgdW5kZXIgJEhfMCQuCgotIENhbGlicmF0aW9uIGZhY3RvciBlc3RpbWF0ZWQgd2l0aCAzMCByYW5kb20gU05QcyB3aXRob3V0IGVmZmVjdC4KCi0gSW1wb3J0YW50IHRvIGtlZXAgaW4gbWluZDogICAKCiQkIGNfXHRleHR7aW5mfSBcbGVmdHJpZ2h0YXJyb3cgXHRpbGRle1xtYXRoYmZ7eH19XlRcaGF0e1xtYXRoYmZ7Vn19XnstMX1fe1x0ZXh0e0xPQ099LEhfMH1cdGlsZGV7XG1hdGhiZnt4fX0kJAoKIyMgR2VuZXJhbGlzYXRpb24gb2YgQk9MVC1MTU0taW5mCgoiV2Ugbm93IGdlbmVyYWxpemUgQk9MVC1MTU0taW5mIGJ5IG9ic2VyaW5nIHRoYXQgdGhlIHZlY3RvciAkXG1hdGhiZntWfV57LTF9X3tcdGV4dHtMT0NPfSxIXzB9IFxtYXRoYmZ7eX0kIGFwcGVhcmluZyBpbiBlcXVhdGlvbiAoOCkgaXMgYSBzY2FsYXIgbXVsdGlwbGUgb2YgdGhlIHJlc2lkdWFsIHBoZW5vdHlwZSAkXHNpZ21hXjJfXGVwc2lsb24gXG1hdGhiZntWfV57LTF9X3tcdGV4dHtMT0NPfSxIXzB9IFxtYXRoYmZ7eX0kIGZyb20gdGhlIGJlc3QgbGluZWFyIHVuYmlhc2VkIHByZWRpY3Rpb24gKEJMVVApIgoKIyMjIFdoYXQgaXMgdGhlIGJlc3QgbGluZWFyIHVuYmlhc2VkIHByZWRpY3RvciBCTFVQPwoKCi0gVGhlIHJhbmRvbSBlZmZlY3RzIGNhbiBiZSBwcmVkaWN0ZWQgdXNpbmcgdGhlIEJMVVAKLSBBbiBlc3RpbWF0b3IgZm9yIHRoZSBmaXhlZCBlZmZlY3RzIGFuZCBmb3IgdGhlIHByZWRpY3Rpb24gb2YgdGhlIHJhbmRvbSBlZmZlY3RzIGNhbiBiZSBkZXJpdmVkIGZyb20gdGhlIGpvaW50LWxpa2VsaWhvb2Qgb2YgJFxtYXRoYmZ7WX0kIGFuZCAkXG1hdGhiZnt1fSQsIGkuZS4gCgoKTWF4aW1pc2luZyB0aGUgbG9nIG9mIHRoZSBqb2ludCBkZW5zaXR5ICRmKFxtYXRoYmZ7WX0sXG1hdGhiZnt1fVx2ZXJ0IFxtYXRoYmZ7WH0pJCB0byAkXG1hdGhiZnt1fSQgYW5kICRcYm9sZHN5bWJvbHtcYmV0YX0kOiAKCiQkClx0ZXh0e2FyZ21heH1fe1xtYXRoYmZ7dX0sXGJvbGRzeW1ib2x7XGJldGF9fSBcbGVmdFsKIC1cZnJhY3tOfXsyfVxsb2coIFxzaWdtYV4yX1xlcHNpbG9uKS1cZnJhY3sxfXsyXHNpZ21hXjJfXGVwc2lsb259XGxlZnQoXG1hdGhiZntZfS1cbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9LVxtYXRoYmZ7WnV9XHJpZ2h0KV5UXGxlZnQoXG1hdGhiZntZfS1cbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9LVxtYXRoYmZ7WnV9XHJpZ2h0KS0xLzJcbG9nKFx2ZXJ0XG1hdGhiZntHfVx2ZXJ0KS1cZnJhY3sxfXsyfVxtYXRoYmZ7dX1eVFxtYXRoYmZ7R31eey0xfVxtYXRoYmZ7dX1ccmlnaHRdCiQkCgpNYXhpbWlzYXRpb24gdG8gJFxib2xkc3ltYm9se1x0aGV0YX09XGxlZnRbXGJlZ2lue2FycmF5fXtjfSBcYm9sZHN5bWJvbHtcYmV0YX1cXFxtYXRoYmZ7dX1cZW5ke2FycmF5fVxyaWdodF0kIG9ubHkgaW52b2x2ZXMKCiQkCi1cZnJhY3sxfXsyXHNpZ21hXjJfXGVwc2lsb259XGxlZnQoXG1hdGhiZntZfS1cbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9LVxtYXRoYmZ7WnV9XHJpZ2h0KV5UXGxlZnQoXG1hdGhiZntZfS1cbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9LVxtYXRoYmZ7WnV9XHJpZ2h0KS1cZnJhY3sxfXsyfVxtYXRoYmZ7dX1eVFxtYXRoYmZ7R31eey0xfVxtYXRoYmZ7dX0KJCQKd2hpY2ggY2FuIGJlIHJld3JpdHRlbiBhcyBhIGtpbmQgb2YgbG9zcyBmdW5jdGlvbiB0aGF0IHdlIHdpbGwgbWluaW1pc2UKCiQkCkxee01NfT0oXG1hdGhiZntZfS1cbWF0aGJme0N9XGJvbGRzeW1ib2x7XHRoZXRhfSleVChcbWF0aGJme1l9LVxtYXRoYmZ7Q31cYm9sZHN5bWJvbHtcdGhldGF9KSt7XHNpZ21hXjJfXGVwc2lsb259XGJvbGRzeW1ib2x7XHRoZXRhfV5UXG1hdGhiZntIfVxib2xkc3ltYm9se1x0aGV0YX0KJCQKCndpdGggJFxtYXRoYmZ7Q309W1xtYXRoYmZ7WH1cdGV4dHsgfSBcbWF0aGJme1p9XSQgYW5kICRcbWF0aGJme0h9PVxsZWZ0W1xiZWdpbnthcnJheX17Y2N9ClxtYXRoYmZ7MH0mXG1hdGhiZnswfVxcClxtYXRoYmZ7MH0mXG1hdGhiZntHfV57LTF9ClxlbmR7YXJyYXl9ClxyaWdodF0kCgpXZSBjYW4gZmluZCB0aGUgZXN0aW1hdG9yIGZvciAkXGJvbGRzeW1ib2x7XHRoZXRhfSQgYnkgdGFrZW4gdGhlIGZpcnN0IGRlcml2YXRpdmUgb2YgJExee01NfSQgdG8gJFxib2xkc3ltYm9se1x0aGV0YX0kIGFuZCBzZXR0aW5nIGl0IHRvIHplcm8sIAokJApcYmVnaW57YXJyYXl9e3JjbH0KXGZyYWN7XHBhcnRpYWwgfXtccGFydGlhbCBcYm9sZHN5bWJvbHtcdGhldGF9fSBMXntNTX0gJj0mIDBcXFxcCi0yIFxtYXRoYmZ7Q31eVChcbWF0aGJme1l9LVxtYXRoYmZ7Q31cYm9sZHN5bWJvbHtcdGhldGF9KSsyIHtcc2lnbWFeMl9cZXBzaWxvbn0gSFxib2xkc3ltYm9se1x0aGV0YX0mPSYwXFxcXAotXG1hdGhiZntDfV5UXG1hdGhiZntZfStcbWF0aGJme0N9XlRcbWF0aGJme0N9XGJvbGRzeW1ib2x7XHRoZXRhfSsge1xzaWdtYV4yX1xlcHNpbG9ufSBIXGJvbGRzeW1ib2x7XHRoZXRhfSY9JjBcXFxcCiAoXG1hdGhiZntDfV5UXG1hdGhiZntDfSArIFxzaWdtYV4yX1xlcHNpbG9uIEgpXGJvbGRzeW1ib2x7XHRoZXRhfSAmPSZcbWF0aGJme0N9XlRcbWF0aGJme1l9XFxcXAogXGhhdHtcYm9sZHN5bWJvbHtcdGhldGF9fSAmPSYgIChcbWF0aGJme0N9XlRcbWF0aGJme0N9ICsgXHNpZ21hXjJfXGVwc2lsb24gSCleey0xfVxtYXRoYmZ7Q31eVFxtYXRoYmZ7WX0KXGVuZHthcnJheX0KJCQKCkZvciBvdXIgR1dBUyAtIExNTSB0aGlzIHJlZHVjZXMgdG8gCgokJApcbGVmdFtcYmVnaW57YXJyYXl9e2N9IFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fVxcXGhhdHtcbWF0aGJme3V9fVxlbmR7YXJyYXl9XHJpZ2h0XSA9ICAKXGxlZnQoClxsZWZ0WwpcYmVnaW57YXJyYXl9e2NjfQpcbWF0aGJme1h9XlRcbWF0aGJme1h9JiBcbWF0aGJme1h9XlRcbWF0aGJme1p9XFwKXG1hdGhiZntafV5UXG1hdGhiZntYfSYgXG1hdGhiZntafV5UXG1hdGhiZntafQpcZW5ke2FycmF5fQpccmlnaHRdICsgClxmcmFje1xzaWdtYV4yX1xlcHNpbG9ufXtcc2lnbWFeMl91fSAKXGxlZnRbClxiZWdpbnthcnJheX17Y2N9ClxtYXRoYmZ7MH0mIFxtYXRoYmZ7MH1cXApcbWF0aGJmezB9JiBcbWF0aGJme0l9ClxlbmR7YXJyYXl9ClxyaWdodF0KXHJpZ2h0KV57LTF9ClxsZWZ0WwpcYmVnaW57YXJyYXl9e2N9ClxtYXRoYmZ7WH1eVFxcClxtYXRoYmZ7Wn1eVApcZW5ke2FycmF5fQpccmlnaHRdXG1hdGhiZntZfQokJAoKSXQgY2FuIGFsc28gYmUgc2hvd24gdGhhdCAkXGhhdHtcbWF0aGJme3V9fSQgZm9yIHRoZSBjb252ZW50aW9uYWwgTE1NIGVxdWFscyAKCiQkClxoYXR7XG1hdGhiZnt1fX09IFxoYXR7XG1hdGhiZntHfX1cbWF0aGJme1p9XlRcaGF0e1xtYXRoYmZ7Vn19XnviiJIgMX0gKFxtYXRoYmZ7WX0g4oiSIFxtYXRoYmZ7WH0gXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19KQokJCAKCmFuZCBmb3Igb3VyIEdXQVMtTE1NCgokJApcaGF0e1xtYXRoYmZ7dX19PSBcaGF0e1xzaWdtYX1eMl91XG1hdGhiZntafV5UXGhhdHtcbWF0aGJme1Z9fV574oiSIDF9IChcbWF0aGJme1l9IOKIkiBcbWF0aGJme1h9IFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fSkKJCQKCiMjIyBBZG9wdGluZyByZXN1bHQgdG8gdGhlIE5vbWluYXRvciBMT0NPIHN0YXRpc3RpYyBvZiB0aGUgQk9MVC1MTU0taW5mIHN0YXRpc3RpYwoKTW9yZW92ZXIsIHVuZGVyICRIXzA6IFxiZXRhX1x0ZXh0e3Rlc3R9PTAkIHRoZSBCTFVQIGZvciB0aGUgTE9DTyBTTlBzIGJlY29tZXMgIAoKJCQKXGhhdHtcbWF0aGJme3V9fV97XHRleHR7TE9DT30sIEhfMH0gPSBcaGF0XHNpZ21hXjJfe3UsXHRleHR7TE9DT30sSF8wfVxtYXRoYmZ7Wn1eVFxoYXR7XG1hdGhiZntWfX1ee+KIkiAxfV97XHRleHR7TE9DT30sIEhfMH0gXHRpbGRle1xtYXRoYmZ7eX19IAokJAokXHJpZ2h0YXJyb3ckIEJMVVAgcmVzaWR1YWxzIGJlY29tZSAKCiQkClxiZWdpbnthcnJheX17Y2NsfQpcbWF0aGJme3l9X1x0ZXh0e3Jlc2lkdWFsLUxPQ08sIEhfMH0gJj0mIFx0aWxkZXtcbWF0aGJme3l9fSAtIFxtYXRoYmZ7Wn1caGF0e1xtYXRoYmZ7dX19X3tcdGV4dHtMT0NPfSwgSF8wfVxcIFxcCiY9JiBcaGF0e1xtYXRoYmZ7Vn19X3tcdGV4dHtMT0NPfSwgSF8wfVxoYXR7XG1hdGhiZntWfX1fe1x0ZXh0e0xPQ099LCBIXzB9XnstMX1cdGlsZGV7XG1hdGhiZnt5fX0tXG1hdGhiZntafVxtYXRoYmZ7Wn1eVFxoYXRcc2lnbWFeMl97dSxcdGV4dHtMT0NPfSxIXzB9IFxoYXR7XG1hdGhiZntWfX1fe1x0ZXh0e0xPQ099LCBIXzB9XnstMX1cdGlsZGV7XG1hdGhiZnt5fX0gXFxcXAomPSYgKFxoYXR7XG1hdGhiZntWfX1fe1x0ZXh0e0xPQ099LCBIXzB9LVxtYXRoYmZ7Wn1cbWF0aGJme1p9XlRcaGF0XHNpZ21hXjJfe3UsXHRleHR7TE9DT30sSF8wfSkgXGhhdHtcbWF0aGJme1Z9fV97XHRleHR7TE9DT30sIEhfMH1eey0xfVx0aWxkZXtcbWF0aGJme3l9fSBcXFxcCiY9JiAoXG1hdGhiZntafV5UXG1hdGhiZntafVxoYXR7XHNpZ21hfV4yX3t1XHRleHR7TE9DT30sIEhfMH0rXG1hdGhiZntJfVxoYXR7XHNpZ21hfV4yXGVwc2lsb24tXG1hdGhiZntafVxtYXRoYmZ7Wn1eVFxoYXRcc2lnbWFeMl97dSxcdGV4dHtMT0NPfSxIXzB9KSBcaGF0e1xtYXRoYmZ7Vn19X3tcdGV4dHtMT0NPfSwgSF8wfV57LTF9XHRpbGRle1xtYXRoYmZ7eX19IFxcXFwKJj0mIFxoYXRcc2lnbWFeMl97XGVwc2lsb24sTE9DTyxIXzB9XGhhdHtcbWF0aGJme1Z9fV97XHRleHR7TE9DT30sIEhfMH1eey0xfVx0aWxkZXtcbWF0aGJme3l9fQpcZW5ke2FycmF5fQokJAokXHJpZ2h0YXJyb3ckIG5vbWluYXRvciBvZiBMT0NPIHNjb3JlIHRlc3QgdXBvbiBhIGZhY3RvciBlcXVhbCB0bzogCgokJAooXHRpbGRle1xtYXRoYmZ7eH19XnRfXHRleHR7dGVzdH0gXG1hdGhiZnt5fV9cdGV4dHtyZXNpZHVhbC1MT0NPLCBIXzB9KV4yCiQkCgpCT0xULUxNTS1pbmYgc3RhdGlzdGljIHRodXMgYmVjb21lczogCgokJHRfXHRleHR7Qk9MVC1MTU0taW5mfSA9IFxmcmFjeyhcdGlsZGV7XG1hdGhiZnt4fX1edF9cdGV4dHt0ZXN0fSBcbWF0aGJme3l9X1x0ZXh0e3Jlc2lkdWFsLUxPQ08sIEhfMH0pXjJ9e2N9JCQgCndpdGggYyB0aGUgYWRqdXN0ZWQgY2FsaWJyYXRpb24gZmFjdG9yLgoKIyMgQk9MVC1MTU0gR2F1c3NpYW4gbWl4dHVyZS1tb2RlbCBhc3NvY2lhdGlvbiB0ZXN0IAoKVG8gZ2VuZXJhbGlzZSB0aGUgZXN0aW1hdGlvbiB0byBub24taW5maW5pdGVzaW1hbCBnZW5ldGljIGFyY2hpdGV4dHVyZXMgdGhleSByZXBsYWNlIHRoZSBHYXVzc2lhbiBwcmlvciBvbiB0aGUgU05QIGVmZmVjdHMgc2l6ZXMgJHVfbSQgYnkgYSAKR2F1c3NpYW4gbWl4dHVyZSBwcmlvciA6IAoKJCR1X20gXHNpbSBwIE4oMCxcc2lnbWFeMl97dSwxfSkgKyAoMS1wKSBOKDAsXHNpZ21hXjJfe3UsMn0pJCQKCndpdGggYSBzbWFsbCBwcm9iYWJpbGl0eSAkcCA8PCAxJCBhbmQgJFxzaWdtYV4yX3t1LDF9ID4+IFxzaWdtYV4yX3t1LDJ9JC4KCi0gIlRoaXMgbWl4dHVyZSBtb3JlIGZsZXhpYmx5IG1vZGVscyB0aGUgaGVhdmllci10YWlsZWQgZGlzdHJpYnV0aW9ucyBvZiB0aGUgZ2VuZXRpYyBlZmZlY3RzIG9mIHR5cGljYWwgKG5vbi1pbmZpbml0ZXNpbWFsKSBwaGVub3R5cGVzLiIKCi0gIlRoZSBmaXJzdCBjb21wb25lbnQgaW4gdGhlIG1peHR1cmUgaXMgYSAnc2xhYicgdGhhdCBtb2RlbHMgdGhlIGV4aXN0ZW5jZSBvZiBhIHNtYWxsIG51bWJlciBvZiBsb2NpIHdpdGggcmVsYXRpdmVseSBsYXJnZSBlZmZlY3RzIGFuZCB0aGUgc2Vjb25kIGNvbXBvbmVudCBpcyBhIOKAmHNwaWtl4oCZIHRoYXQgbW9kZWxzIHRoZSBhc3N1bXB0aW9uIHRoYXQgbW9zdCBTTlBzIGhhdmUgYW4gZWZmZWN0IG9mIG5lYXJseeKAlGJ1dCBub3QgZXhhY3RseeKAlHplcm8gb24gdGhlIHBoZW5vdHlwZS4iCgotICIoTm90ZSwgaG93ZXZlciwgdGhhdCBhbGwgU05QcyBhcmUgYXNzaWduZWQgdGhlIHNhbWUgbWl4dHVyZSBwcmlvcjsgdGhhdCBpcywgU05QcyBhcmUgbm90IGluZGl2aWR1YWxseSBhbGxvY2F0ZWQgdG8gb25lIG9yIHRoZSBvdGhlciBjb21wb25lbnQuKSIKCi0gIkl0IGlzIGltcG9ydGFudCB0aGF0IHRoZSBzcGlrZSBjb21wb25lbnQgaGF2ZSBub256ZXJvIHZhcmlhbmNlIHNvIGFzIHRvIGNhcHR1cmUgZ2Vub21lLXdpZGUgZWZmZWN0cyBvbiBwaGVub3R5cGUgc3VjaCBhcyBhbmNlc3RyeSBvciByZWxhdGVkbmVzczsgdGhlbiwgd2hlbiB0ZXN0aW5nIFNOUHMgZm9yIGFzc29jaWF0aW9uLCB0aGVzZSBnZW5vbWUtd2lkZSBlZmZlY3RzIGFyZSBjb25kaXRpb25lZCBvdXQgZnJvbSByZXNpZHVhbCBwaGVub3R5cGVzLCBwcm90ZWN0aW5nIGFnYWluc3QgY29uZm91bmRpbmcuIgoKLSAiVW5kZXIgdGhpcyBnZW5lcmFsaXplZCBtb2RlbCwgcG9zdGVyaW9yIG1lYW5zIG5vIGxvbmdlciBjb3JyZXNwb25kIHRvIEJMVVAsIGJ1dCB3ZSBjYW4gc3RpbGwgYXBwcm94aW1hdGVseSBmaXQgdGhlIEJheWVzaWFuIG1vZGVsIChvbmNlIHBlciBsZWZ0LW91dCBjaHJvbW9zb21lKSBhbmQgb2J0YWluIHJlc2lkdWFscyIKCi0gIlBsdWdnaW5nIHRoZXNlIHJlc2lkdWFscyBpbnRvIGVxdWF0aW9uICgxMSkgZ2l2ZXMgdGhlIEJPTFQtTE1NIEdhdXNzaWFuIG1peHR1cmUtbW9kZWwgYXNzb2NpYXRpb24gdGVzdCBzdGF0aXN0aWMuIg==