1 Introduction

  • We live in a big data era
  • Massive Datasets on our location, surfing behavior, consumer data, social media, …
  • Life Sciences: advent of high throughput technologies has enabled us to measure brain activity (brain images) as well as the expression of thousands of genes, proteins, … for each subject or even single cell.
  • Industry: process control with many sensors that follow process in real time…
  • Data drive journalism

Challenge: We have to learn from high dimensional data!


1.1 What are high dimensional data?

  • We typically observe multiple variables/features (p) for each subject/experimental unit \(i=1,\ldots,n\) i.e. \[ \mathbf{x}_i^T=[x_{i1},\ldots,x_{ip}]\]

  • Multivariate statistics have a long history, but were designed for the \(n>>>p\) case,

  • Nowadays many high throughput technologies generate multivariate data with many variables (large \(p\)) as compared to the number of independent replicates or samples (sample size \(n\)), resulting in {high-dimensional data}, which is characterised by \[ p >>> n. \]

  • New statistical methods for dealing with the \(p >>> n\) case have been developed in the last 20 years. Some of them are adaptations of multivariate methods.


Issues with high-dimensional data:

  • Computational problems: large matrices, numerical accuracy of computers become an issue

  • Classical asymptotic theory does not hold for \(p\rightarrow\infty\) as \(n \rightarrow \infty\)

  • Model (or feature) selection requires specialised methods to deal with the enormous number of possible models. (e.g. in linear regression with p potential predictors: \(2^p\) possible models)

  • Models that can potentially depend on large-p predictors are vulnerable to huge overfitting.

  • In searching for associations between an outcome and large p potential exploratory variables, we are at risk to make many false discoveries

  • The Curse of Dimensionality may cause a prediction model to become useless. (\(n\) data points become sparse in large-\(p\) dimensional space)


2 Important tasks in high dimensional data analysis?

2.1 Example: Kang et al. (2018)’s droplet-based scRNA-seq data of PBMCs cells from 8 lupus patients measured before and after 6h-treatment with INF-\(\beta\) (16 samples in total).

library(tidyverse)

## Packages to load and visualize the single-cell data
library(ExperimentHub)
library(scater)
## Load Kang single-cell data from ExperimentHub
eh <- ExperimentHub()
sce <- eh[["EH2259"]]

sce
#> class: SingleCellExperiment 
#> dim: 35635 29065 
#> metadata(0):
#> assays(1): counts
#> rownames(35635): MIR1302-10 FAM138A ... MT-ND6 MT-CYB
#> rowData names(2): ENSEMBL SYMBOL
#> colnames(29065): AAACATACAATGCC-1 AAACATACATTTCC-1 ... TTTGCATGGTTTGG-1
#>   TTTGCATGTCTTAC-1
#> colData names(5): ind stim cluster cell multiplets
#> reducedDimNames(1): TSNE
#> mainExpName: NULL
#> altExpNames(0):
  • Data on gene expression of 35635 genes of 29065 cells.

2.2 Data exploration and dimensionality reduction

  • Visualisation is a first essential step to learn from data
counts(sce)[18990:19000,9:20]
#> 11 x 12 sparse Matrix of class "dgCMatrix"
#>                                                    
#> RP11-467L20.10   . .   . . .   .  .  .  . .   .   .
#> MYRF             . .   . . .   .  .  .  . .   .   .
#> TMEM258          . .   3 . .   .  1  1  . .   .   .
#> FEN1             . .   . . .   .  .  .  . .   .   .
#> FADS2            . .   . . .   .  .  .  . .   .   .
#> FADS1            . .   . . .   .  .  .  . .   .   .
#> FADS3            . .   . . .   .  .  .  . .   .   .
#> RAB3IL1          . .   . . .   .  .  .  . .   .   .
#> BEST1            1 .   . . .   .  1  .  . .   1   1
#> FTH1           163 8 271 4 5 174 38 58 31 3 663 612
#> AP003733.1       . .   . . .   .  .  .  . .   .   .
  • It is impossible to learn about the structure in the data by staring at the data matrix.
  • We should be able to explore the data in a low dimensional projection
plotReducedDim(sce, dimred="TSNE", colour_by="cell")

plotReducedDim(sce, dimred="TSNE", colour_by="stim")

  • Note, that we see huge effect of treatment. If I see this I am always on my guard!
  • We contacted the authors and learned that all control cells were sequenced in a first run and all stimulated cells were on a second sequencing run. So the large effect might be an effect of batch!

2.3 Prediction

plotReducedDim(sce, dimred="TSNE", colour_by="cell")

  • In single cell analysis it is key to identify cell types in scRNA-seq data sets before in-depth investigations of their functional and pathological roles.

  • Use models that were build based on reference data sets to predict cell types in new data sets based on their gene expression pattern

  • Problem: we have 35635 genes at our disposal to build this prediction model!

  • Other examples

    • Prediction of risk on mortality is used on a daily basis in intensive care units to prioritise patient care.

    • Facebook: predict the identity of the faces of people that are on an new image that is uploaded.

    • Netflix: Suggest movies that you would like

2.4 Large scale hypothesis testing

plotReducedDim(sce, dimred="TSNE", colour_by="cell", shape_by="stim")

  • Which genes are differentially expressed between control and stimulated treatment (assess in each cell type)
  • For which genes we see that the differential expression according to treatment is changing according to the cell type (interaction)
  • We have to model the gene expression for each gene

\[ \left \{ \begin{array}{lcl} y_{ig} &\sim& NB(\mu_{ig}, \phi_{g})\\ E[y_{ig}] &=& \mu_{ig}\\ \log(\mu_{ig}) &=& \eta_{ig}\\ \eta_{ig} &=& \beta_{0,g} + \sum\limits_{c=2}^C \beta_{c,g} X_{ic} + \beta_{s,g} X_{is} + \sum\limits_{c=2}^C \beta_{c:s,g} X_{ic} X_{is} + \sum\limits_{e=2}^E \beta_{e,g} X_{ie} + \alpha_{ig} \end{array} \right. \]

With

  • Cell type \(c=2\ldots C\) and \(X_{ic}\) is a dummy variable that \(X_{ic}=1\) if cell \(i\) is of cell type c and \(X_{ic}=0\) otherwise. Note that cell type \(c=1\) is the reference cell type

  • Indicator \(X_{is}\) indicates if cell \(i\) was stimulated \(X_{is}=1\) or not \(X_{is}=0\). So the control treatment is the reference treatment.

  • Experimental unit \(e=2\ldots E\) and \(X_{ie}\) a dummy variable that \(X_{ie}=1\) if cell \(i\) originates of experimental unit (subject) \(e\) and \(X_{ie}=0\) otherwise. Note that cell type \(e=1\) is the reference experimental unit.

  • \(\alpha_{ig}\) a cell specific and gene specific normalisation factor

Suppose we want to test if the effect of the stimulus (average difference in expression between stimulated and non stimulated cells) is different in cell type c than in the reference cell type 1?

  • \(H_0: \beta_{c:s,g} = 0\)

  • \(H_1: \beta_{c:s,g} \neq 0\)

  • We have to assess this for 35635 genes!

  • If we assess each test at the 5% level we can expect 0.05 * 35635 = 1782 false positives.

\(\rightarrow\) massive multiple testing problem!

Note, that we cannot differentiate between batch and treatment because of the flaw in the experimental design!

Other examples

  • Find regions (voxels) in the brain (on brain image) that are associated with a certain condition/treatment
  • Evaluation of trading rules

3 Linear regression

  • Linear regression is a very important statistical tool to study the association between variables and to build prediction models.

3.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

3.2 Model

3.2.1 Scalar 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=f(\mathbf{x}) +\epsilon=\beta_0+\sum\limits_{j=1}^p x_j\beta_j + \epsilon \] with i.i.d. \(\epsilon\sim N(0,\sigma^2)\)
  • We will often work on mean centered variables: \(Y_c = Y-\bar{Y}\) and \(X_c = X - \bar{X}\), then the intercept is dropped from the model: \[ Y_c=f(\mathbf{x}_c) +\epsilon=\sum\limits_{j=1}^p x_{cj}\beta_j + \epsilon \]

3.2.2 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} \]

3.2.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]\)

Note, that upon centering of \(\mathbf{X}\) and \(\mathbf{Y}\) the 1 is dropped from each \(\mathbf{x}_i\) and thus the column of 1 is dropped in \(\mathbf{X}_c\)

3.2.4 Matrix form for toy dataset

We can also write this in matrix form

\[ \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] \]


3.3 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 we obtain

\[ E[Y \mid x] = \beta_0 + \beta_1 x \] and \[ \beta_1 = E[Y\mid x+1] - E[Y\mid x] = \beta_0 + \beta_1 (x+1) - \beta_0 - \beta_1 x. \]

In this course we will typically center Y and X and then we get the following

\[ E[Y_c \mid x_c] = \beta_1 x_c \] and \[ \beta_1 = E[Y_c\mid x_c+1] - E[Y_c\mid x_c] . \]

Note, that the estimator for \(\beta_1\) will be exactly same when estimated based on the models with/or without centering.

  • The parameter \(\beta_1\) has an interpretation as the average difference in the outcome between subjects that differ with one unit for the predictor.

  • 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.

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

\[ \hat Y = \mathbf{x}^T \hat \beta \] - In Chapter 3 will we will discuss the prediction problem for high dimensional data

Model fit and predictions based on the toy dataset

lm1 <- lm(y~x,data)
data$yhat <- lm1$fitted

data %>%
  ggplot(aes(x,y)) +
  geom_point() +
  ylim(0,4) +
  xlim(0,4) +
  stat_smooth(method = "lm", color = "red", fullrange = TRUE) +
  geom_point(aes(x=x, y =yhat), pch = 2, size = 3, color = "red") +
  geom_segment(data = data, aes(x = x, xend = x, y = y, yend = yhat), lty = 2 )


  • In a Chapter 6 we will discuss the problem of large scale hypothesis testing: testing many hypotheses in a single study (ten to hundred thousands of hypotheses).

    • A statistical test is constructed to control the type I error rate at the significance level \(\alpha\) to assess the null hypothesis that there is no association between a predictor and the outcome vs the alternative hypothesis that there is an association between a predictor and the outcome.

    \[ H_0: \beta_1 = 0 \text{ vs }H_1:\beta_1 \neq 0. \]

    • However, when many hypotheses are to be tested in a single study, the probability to find false associations is no longer controlled if p-values are compared to the significance level \(\alpha\).

    • We will later introduce the concept of false discovery rates to overcome the problem.

3.4 Least Squares (LS)

  • Minimize the residual sum of squares \[\begin{eqnarray*} RSS(\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*} RSS(\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\)


3.4.1 Minimize RSS

\[ \begin{array}{ccc} \frac{\partial RSS}{\partial \boldsymbol{\beta}}&=&\mathbf{0}\\\\ \frac{(\mathbf{Y}-\mathbf{X\beta})^T(\mathbf{Y}-\mathbf{X}\boldsymbol{\beta})}{\partial \boldsymbol{\beta}}&=&\mathbf{0}\\\\ -2\mathbf{X}^T(\mathbf{Y}-\mathbf{X}\boldsymbol{\beta})&=&\mathbf{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:

\[ E[\hat{\boldsymbol{\beta}}]=\boldsymbol{\beta} \]


3.4.2 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\).

3.4.2.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 error 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 error 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 error 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*}\]


3.4.2.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.

3.4.2.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{U}\mathbf{U}^T\mathbf{Y} \end{array} \]

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

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

eigenXtX <- eigen(XtX)
XtXinvSqrt <- eigenXtX$vectors %*%diag(1/eigenXtX$values^.5)%*%t(eigenXtX$vectors)
U <- X %*% XtXinvSqrt
  • \(\mathbf{U}\) orthonormal basis
U
#>         [,1]        [,2]
#> 1  0.9116067 -0.04802616
#> 2  0.3881706  0.42738380
#> 3 -0.1352655  0.90279376
t(U)%*%U
#>              [,1]         [,2]
#> [1,] 1.000000e+00 2.915205e-16
#> [2,] 2.915205e-16 1.000000e+00
  • \(\mathbf{UU}^T\) equals projection matrix
U%*%t(U)
#>            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,U[1,1]),
    y = c(0,U[2,1]),
    z = c(0,U[3,1]),
    mode = "lines",
    line = list(width = 5, color = "blue"),
    type="scatter3d",
    name = "U1") %>%
  add_trace(
    x = c(0,U[1,2]),
    y = c(0,U[2,2]),
    z = c(0,U[3,2]),
    mode = "lines",
    line = list(width = 5, color = "blue"),
    type="scatter3d",
    name = "U2")

p3
  • \(\mathbf{U}^T\mathbf{Y}\) is the projection of \(\mathbf{Y}\) in the space spanned by \(\mathbf{U}\).
  • Indeed \(\mathbf{U}_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,U[1,1]*(U[,1]%*%Y)),
    y = c(0,U[2,1]*(U[,1]%*%Y)),
    z = c(0,U[3,1]*(U[,1]%*%Y)),
    mode = "lines",
    line = list(width = 5, color = "red",dash="dash"),
    type="scatter3d",
    name="Y -> U1") %>% add_trace(
    x = c(Y[1],U[1,1]*(U[,1]%*%Y)),
    y = c(Y[2],U[2,1]*(U[,1]%*%Y)),
    z = c(Y[3],U[3,1]*(U[,1]%*%Y)),
    mode = "lines",
    line = list(width = 5, color = "red", dash="dash"),
    type="scatter3d",
    name="Y -> U1")
p4
  • and \(\mathbf{U}_2^T\mathbf{Y}\)
p5 <- p4 %>%
  add_trace(
    x = c(0,U[1,2]*(U[,2]%*%Y)),
    y = c(0,U[2,2]*(U[,2]%*%Y)),
    z = c(0,U[3,2]*(U[,2]%*%Y)),
    mode = "lines",
    line = list(width = 5, color = "red",dash="dash"),
    type="scatter3d",
    name="Y -> U2") %>% add_trace(
    x = c(Y[1],U[1,2]*(U[,2]%*%Y)),
    y = c(Y[2],U[2,2]*(U[,2]%*%Y)),
    z = c(Y[3],U[3,2]*(U[,2]%*%Y)),
    mode = "lines",
    line = list(width = 5, color = "red", dash="dash"),
    type="scatter3d",
    name="Y -> U2")
p5
  • Yhat is the resulting vector that lies in the plane spanned by \(\mathbf{U}_1\) and \(\mathbf{U}_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(U[1,1]*(U[,1]%*%Y),Yhat[1]),
    y = c(U[2,1]*(U[,1]%*%Y),Yhat[2]),
    z = c(U[3,1]*(U[,1]%*%Y),Yhat[3]),
    mode = "lines",
    line = list(width = 5, color = "orange", dash="dash"),
    type="scatter3d",
    name = "Y -> U")  %>%
  add_trace(
    x = c(U[1,2]*(U[,2]%*%Y),Yhat[1]),
    y = c(U[2,2]*(U[,2]%*%Y),Yhat[2]),
    z = c(U[3,2]*(U[,2]%*%Y),Yhat[3]),
    mode = "lines",
    line = list(width = 5, color = "orange", dash="dash"),
    type="scatter3d",
    name = "Y -> U")
p6

3.4.3 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 \]

3.4.4 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))
XtX <- t(X)%*%X
eigenXtX <- eigen(XtX)
XtXinvSqrt <- eigenXtX$vectors %*%diag(1/eigenXtX$values^.5)%*%t(eigenXtX$vectors)
U <- 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,U[1,1]),
    y = c(0,U[2,1]),
    z = c(0,U[3,1]),
    mode = "lines",
    line = list(width = 5, color = "blue"),
    type="scatter3d",
    name = "U1") %>%
  add_trace(
    x = c(0,U[1,2]),
    y = c(0,U[2,2]),
    z = c(0,U[3,2]),
    mode = "lines",
    line = list(width = 5, color = "blue"),
    type="scatter3d",
    name = "U2") %>%
  add_trace(
    x = c(0,U[1,3]),
    y = c(0,U[2,3]),
    z = c(0,U[3,3]),
    mode = "lines",
    line = list(width = 5, color = "blue"),
    type="scatter3d",
    name = "U3")

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!

3.5 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!


3.6 Prediction error

Least squares estimators are unbiased and consistent, but these properties are not very important for prediction models.

A prediction model is considered good if it can predict well outcomes.

The prediction error for a prediction at predictor \(\mathbf{x}\) is given by

\[ \hat{Y}(\mathbf{x}) - Y^*, \]

where

  • \(\hat{Y}(\mathbf{x})=\mathbf{x}^T\hat{\boldsymbol{\beta}}\) is the prediction at \(\mathbf{x}\)

  • \(Y^*\) is an outcome at predictor \(\mathbf{x}\)

Since prediction is typically used to predict an outcome before it is observed, the outcome \(Y^*\) is not observed yet. Hence, the prediction error cannot be computed.


The problem of unobservable prediction errors is partly solved by the expected conditional test error (sometimes referred to as the mean squared error, MSE) \[ \text{Err}(\mathbf{x})=E[(\hat{Y}(\mathbf{x}) - Y^*)^2]. \]

With (suppressing the dependence on \(\mathbf{x}\)) \[ \mu = E[\hat{Y}] \text{ and } \mu^*=E[Y^*] \] the error can be expressed as \[\begin{eqnarray*} \text{Err} &=&E\left\{\left[(\hat{Y} -\mu)-(Y^*-\mu^*)-(\mu^*-\mu)\right]^2\right\} \\ &=& E[(\hat{Y} -\mu)^2]+E[(Y^*-\mu^*)^2]+E[(\mu^*-\mu)^2] \\ &=& \text{var}[\hat{Y}] +\text{var}[Y^*] +\text{bias}^2 \end{eqnarray*}\]

The term \(\text{var}[Y^*]\) (irreducible error) does not depend on the model and may therefore be ignored when \(\text{Err}\) is used for comparing prediction models.


In this introductory chapter we only aim to give a rough discussion on prediction errors. Later definitions will be refined and the notation will be more accurate. Also a more detailed discussion on the bias-variance trade-off will follow. For the moment it is sufficient to vaguely know that:

  • the expected conditional test error is introduced to circumvent the problem that the prediction error cannot be observed. In later chapters we will look at estimators of the expected error.

  • the expected conditional test error is in some literature also known as the mean squared error (MSE), but we do not adopt this terminology because MSE is also commonly used to refer to SSE divided by the residual degrees of freedom in a linear regression model.

  • The identity \(\text{Err}=\text{var}[\hat{Y}] +\text{var}[Y] +\text{bias}^2\) is known as the bias-variance trade-off. It shows that a good prediction model (i.e. a model resulting in a small Err), can be obtained by a model that shows a small bias as long as this bias is compensated with a large reduction of the variance or the predictions. A more detailed discussion will follow in later chapters.

  • For prediction models with a large number of predictors we will therefore introduce penalized regression. This will induce some bias in the estimation, but will allow us to reduce the variance considerably.

Session info

Session info
#> [1] "2024-10-07 12:41:46 CEST"
#> ─ Session info ───────────────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.4.0 RC (2024-04-16 r86468)
#>  os       macOS Big Sur 11.6
#>  system   aarch64, darwin20
#>  ui       X11
#>  language (EN)
#>  collate  en_US.UTF-8
#>  ctype    en_US.UTF-8
#>  tz       Europe/Brussels
#>  date     2024-10-07
#>  pandoc   3.1.1 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/ (via rmarkdown)
#> 
#> ─ Packages ───────────────────────────────────────────────────────────────────
#>  package              * version    date (UTC) lib source
#>  abind                  1.4-8      2024-09-12 [1] CRAN (R 4.4.1)
#>  AnnotationDbi          1.66.0     2024-05-01 [1] Bioconductor 3.19 (R 4.4.0)
#>  AnnotationHub        * 3.12.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  assertthat             0.2.1      2019-03-21 [1] CRAN (R 4.4.0)
#>  beachmat               2.20.0     2024-05-06 [1] Bioconductor 3.19 (R 4.4.0)
#>  beeswarm               0.4.0      2021-06-01 [1] CRAN (R 4.4.0)
#>  Biobase              * 2.64.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  BiocFileCache        * 2.12.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  BiocGenerics         * 0.50.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  BiocManager            1.30.25    2024-08-28 [1] CRAN (R 4.4.1)
#>  BiocNeighbors          1.22.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  BiocParallel           1.38.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  BiocSingular           1.20.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  BiocVersion            3.19.1     2024-04-22 [1] Bioconductor 3.19 (R 4.4.0)
#>  Biostrings             2.72.1     2024-06-02 [1] Bioconductor 3.19 (R 4.4.0)
#>  bit                    4.5.0      2024-09-20 [1] CRAN (R 4.4.1)
#>  bit64                  4.5.2      2024-09-22 [1] CRAN (R 4.4.1)
#>  blob                   1.2.4      2023-03-17 [1] CRAN (R 4.4.0)
#>  bookdown               0.40       2024-07-02 [1] CRAN (R 4.4.0)
#>  bslib                  0.8.0      2024-07-29 [1] CRAN (R 4.4.0)
#>  cachem                 1.1.0      2024-05-16 [1] CRAN (R 4.4.0)
#>  cli                    3.6.3      2024-06-21 [1] CRAN (R 4.4.0)
#>  codetools              0.2-20     2024-03-31 [1] CRAN (R 4.4.0)
#>  colorspace             2.1-1      2024-07-26 [1] CRAN (R 4.4.0)
#>  cowplot                1.1.3      2024-01-22 [1] CRAN (R 4.4.0)
#>  crayon                 1.5.3      2024-06-20 [1] CRAN (R 4.4.0)
#>  crosstalk              1.2.1      2023-11-23 [1] CRAN (R 4.4.0)
#>  curl                   5.2.3      2024-09-20 [1] CRAN (R 4.4.1)
#>  data.table             1.16.0     2024-08-27 [1] CRAN (R 4.4.1)
#>  DBI                    1.2.3      2024-06-02 [1] CRAN (R 4.4.0)
#>  dbplyr               * 2.5.0      2024-03-19 [1] CRAN (R 4.4.0)
#>  DelayedArray           0.30.1     2024-05-07 [1] Bioconductor 3.19 (R 4.4.0)
#>  DelayedMatrixStats     1.26.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  digest                 0.6.37     2024-08-19 [1] CRAN (R 4.4.1)
#>  dplyr                * 1.1.4      2023-11-17 [1] CRAN (R 4.4.0)
#>  emo                    0.0.0.9000 2024-10-02 [1] Github (hadley/emo@3f03b11)
#>  evaluate               1.0.0      2024-09-17 [1] CRAN (R 4.4.1)
#>  ExperimentHub        * 2.12.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  fansi                  1.0.6      2023-12-08 [1] CRAN (R 4.4.0)
#>  farver                 2.1.2      2024-05-13 [1] CRAN (R 4.4.0)
#>  fastmap                1.2.0      2024-05-15 [1] CRAN (R 4.4.0)
#>  filelock               1.0.3      2023-12-11 [1] CRAN (R 4.4.0)
#>  forcats              * 1.0.0      2023-01-29 [1] CRAN (R 4.4.0)
#>  generics               0.1.3      2022-07-05 [1] CRAN (R 4.4.0)
#>  GenomeInfoDb         * 1.40.1     2024-06-16 [1] Bioconductor 3.19 (R 4.4.0)
#>  GenomeInfoDbData       1.2.12     2024-04-24 [1] Bioconductor
#>  GenomicRanges        * 1.56.1     2024-06-16 [1] Bioconductor 3.19 (R 4.4.0)
#>  ggbeeswarm             0.7.2      2023-04-29 [1] CRAN (R 4.4.0)
#>  ggplot2              * 3.5.1      2024-04-23 [1] CRAN (R 4.4.0)
#>  ggrepel                0.9.6      2024-09-07 [1] CRAN (R 4.4.1)
#>  glue                   1.8.0      2024-09-30 [1] CRAN (R 4.4.1)
#>  gridExtra              2.3        2017-09-09 [1] CRAN (R 4.4.0)
#>  gtable                 0.3.5      2024-04-22 [1] CRAN (R 4.4.0)
#>  highr                  0.11       2024-05-26 [1] CRAN (R 4.4.0)
#>  hms                    1.1.3      2023-03-21 [1] CRAN (R 4.4.0)
#>  htmltools              0.5.8.1    2024-04-04 [1] CRAN (R 4.4.0)
#>  htmlwidgets            1.6.4      2023-12-06 [1] CRAN (R 4.4.0)
#>  httr                   1.4.7      2023-08-15 [1] CRAN (R 4.4.0)
#>  IRanges              * 2.38.1     2024-07-03 [1] Bioconductor 3.19 (R 4.4.1)
#>  irlba                  2.3.5.1    2022-10-03 [1] CRAN (R 4.4.0)
#>  jquerylib              0.1.4      2021-04-26 [1] CRAN (R 4.4.0)
#>  jsonlite               1.8.9      2024-09-20 [1] CRAN (R 4.4.1)
#>  KEGGREST               1.44.1     2024-06-19 [1] Bioconductor 3.19 (R 4.4.0)
#>  knitr                  1.48       2024-07-07 [1] CRAN (R 4.4.0)
#>  labeling               0.4.3      2023-08-29 [1] CRAN (R 4.4.0)
#>  lattice                0.22-6     2024-03-20 [1] CRAN (R 4.4.0)
#>  lazyeval               0.2.2      2019-03-15 [1] CRAN (R 4.4.0)
#>  lifecycle              1.0.4      2023-11-07 [1] CRAN (R 4.4.0)
#>  lubridate            * 1.9.3      2023-09-27 [1] CRAN (R 4.4.0)
#>  magrittr               2.0.3      2022-03-30 [1] CRAN (R 4.4.0)
#>  Matrix                 1.7-0      2024-03-22 [1] CRAN (R 4.4.0)
#>  MatrixGenerics       * 1.16.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  matrixStats          * 1.4.1      2024-09-08 [1] CRAN (R 4.4.1)
#>  memoise                2.0.1      2021-11-26 [1] CRAN (R 4.4.0)
#>  mgcv                   1.9-1      2023-12-21 [1] CRAN (R 4.4.0)
#>  mime                   0.12       2021-09-28 [1] CRAN (R 4.4.0)
#>  misc3d                 0.9-1      2021-10-07 [1] CRAN (R 4.4.0)
#>  munsell                0.5.1      2024-04-01 [1] CRAN (R 4.4.0)
#>  muscData             * 1.18.0     2024-05-02 [1] Bioconductor 3.19 (R 4.4.0)
#>  nlme                   3.1-166    2024-08-14 [1] CRAN (R 4.4.0)
#>  pillar                 1.9.0      2023-03-22 [1] CRAN (R 4.4.0)
#>  pkgconfig              2.0.3      2019-09-22 [1] CRAN (R 4.4.0)
#>  plot3D               * 1.4.1      2024-02-06 [1] CRAN (R 4.4.0)
#>  plotly               * 4.10.4     2024-01-13 [1] CRAN (R 4.4.0)
#>  png                    0.1-8      2022-11-29 [1] CRAN (R 4.4.0)
#>  purrr                * 1.0.2      2023-08-10 [1] CRAN (R 4.4.0)
#>  R6                     2.5.1      2021-08-19 [1] CRAN (R 4.4.0)
#>  rappdirs               0.3.3      2021-01-31 [1] CRAN (R 4.4.0)
#>  Rcpp                   1.0.13     2024-07-17 [1] CRAN (R 4.4.0)
#>  readr                * 2.1.5      2024-01-10 [1] CRAN (R 4.4.0)
#>  rlang                  1.1.4      2024-06-04 [1] CRAN (R 4.4.0)
#>  rmarkdown              2.28       2024-08-17 [1] CRAN (R 4.4.0)
#>  RSQLite                2.3.7      2024-05-27 [1] CRAN (R 4.4.0)
#>  rstudioapi             0.16.0     2024-03-24 [1] CRAN (R 4.4.0)
#>  rsvd                   1.0.5      2021-04-16 [1] CRAN (R 4.4.0)
#>  S4Arrays               1.4.1      2024-05-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  S4Vectors            * 0.42.1     2024-07-03 [1] Bioconductor 3.19 (R 4.4.1)
#>  sass                   0.4.9      2024-03-15 [1] CRAN (R 4.4.0)
#>  ScaledMatrix           1.12.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  scales                 1.3.0      2023-11-28 [1] CRAN (R 4.4.0)
#>  scater               * 1.32.1     2024-07-21 [1] Bioconductor 3.19 (R 4.4.0)
#>  scuttle              * 1.14.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  sessioninfo            1.2.2      2021-12-06 [1] CRAN (R 4.4.0)
#>  SingleCellExperiment * 1.26.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  SparseArray            1.4.8      2024-05-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  sparseMatrixStats      1.16.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  stringi                1.8.4      2024-05-06 [1] CRAN (R 4.4.0)
#>  stringr              * 1.5.1      2023-11-14 [1] CRAN (R 4.4.0)
#>  SummarizedExperiment * 1.34.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  tibble               * 3.2.1      2023-03-20 [1] CRAN (R 4.4.0)
#>  tidyr                * 1.3.1      2024-01-24 [1] CRAN (R 4.4.0)
#>  tidyselect             1.2.1      2024-03-11 [1] CRAN (R 4.4.0)
#>  tidyverse            * 2.0.0      2023-02-22 [1] CRAN (R 4.4.0)
#>  timechange             0.3.0      2024-01-18 [1] CRAN (R 4.4.0)
#>  tzdb                   0.4.0      2023-05-12 [1] CRAN (R 4.4.0)
#>  UCSC.utils             1.0.0      2024-05-06 [1] Bioconductor 3.19 (R 4.4.0)
#>  utf8                   1.2.4      2023-10-22 [1] CRAN (R 4.4.0)
#>  vctrs                  0.6.5      2023-12-01 [1] CRAN (R 4.4.0)
#>  vipor                  0.4.7      2023-12-18 [1] CRAN (R 4.4.0)
#>  viridis                0.6.5      2024-01-29 [1] CRAN (R 4.4.0)
#>  viridisLite            0.4.2      2023-05-02 [1] CRAN (R 4.4.0)
#>  withr                  3.0.1      2024-07-31 [1] CRAN (R 4.4.0)
#>  xfun                   0.47       2024-08-17 [1] CRAN (R 4.4.0)
#>  XVector                0.44.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  yaml                   2.3.10     2024-07-26 [1] CRAN (R 4.4.0)
#>  zlibbioc               1.50.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#> 
#>  [1] /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library
#> 
#> ──────────────────────────────────────────────────────────────────────────────
LS0tCnRpdGxlOiAiMS4gSW50cm9kdWN0aW9uIHRvIEhpZ2ggRGltZW5zaW9uYWwgRGF0YSBBbmFseXNpcyIKYXV0aG9yOiAiTGlldmVuIENsZW1lbnQiCmRhdGU6ICJzdGF0T21pY3MsIEdoZW50IFVuaXZlcnNpdHkgKGh0dHBzOi8vc3RhdG9taWNzLmdpdGh1Yi5pbykiCgpvdXRwdXQ6CiAgYm9va2Rvd246OnBkZl9kb2N1bWVudDI6CiAgICB0b2M6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4CmFsd2F5c19hbGxvd19odG1sOiB0cnVlCi0tLQoKYGBge3IsIGNoaWxkPSJfc2V0dXAuUm1kIn0KYGBgCgojIEludHJvZHVjdGlvbgoKYGBge3Igc2V0dXAyLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQobWVzc2FnZSA9IEZBTFNFKQpgYGAKCi0gV2UgbGl2ZSBpbiBhIGJpZyBkYXRhIGVyYQotIE1hc3NpdmUgRGF0YXNldHMgb24gb3VyIGxvY2F0aW9uLCBzdXJmaW5nIGJlaGF2aW9yLCBjb25zdW1lciBkYXRhLCBzb2NpYWwgbWVkaWEsIC4uLgotIExpZmUgU2NpZW5jZXM6IGFkdmVudCBvZiBoaWdoIHRocm91Z2hwdXQgdGVjaG5vbG9naWVzIGhhcyBlbmFibGVkIHVzIHRvIG1lYXN1cmUgYnJhaW4gYWN0aXZpdHkgKGJyYWluIGltYWdlcykgYXMgd2VsbCBhcyB0aGUgZXhwcmVzc2lvbiBvZiB0aG91c2FuZHMgb2YgZ2VuZXMsIHByb3RlaW5zLCAuLi4gZm9yIGVhY2ggc3ViamVjdCBvciBldmVuIHNpbmdsZSBjZWxsLgotIEluZHVzdHJ5OiBwcm9jZXNzIGNvbnRyb2wgd2l0aCBtYW55IHNlbnNvcnMgdGhhdCBmb2xsb3cgcHJvY2VzcyBpbiByZWFsIHRpbWUuLi4KLSBEYXRhIGRyaXZlIGpvdXJuYWxpc20KLSAuLi4KCkNoYWxsZW5nZTogV2UgaGF2ZSB0byBsZWFybiBmcm9tIGhpZ2ggZGltZW5zaW9uYWwgZGF0YSEKCi0tLQoKIyMgV2hhdCBhcmUgaGlnaCBkaW1lbnNpb25hbCBkYXRhPwoKLSBXZSB0eXBpY2FsbHkgb2JzZXJ2ZSBtdWx0aXBsZSB2YXJpYWJsZXMvZmVhdHVyZXMgKHApIGZvciBlYWNoIHN1YmplY3QvZXhwZXJpbWVudGFsIHVuaXQgJGk9MSxcbGRvdHMsbiQgaS5lLgpcWyBcbWF0aGJme3h9X2leVD1beF97aTF9LFxsZG90cyx4X3tpcH1dXF0KCi0gTXVsdGl2YXJpYXRlIHN0YXRpc3RpY3MgaGF2ZSBhIGxvbmcgaGlzdG9yeSwgYnV0IHdlcmUgZGVzaWduZWQgZm9yIHRoZSAkbj4+PnAkIGNhc2UsCgotIE5vd2FkYXlzIG1hbnkgaGlnaCB0aHJvdWdocHV0IHRlY2hub2xvZ2llcyBnZW5lcmF0ZSBtdWx0aXZhcmlhdGUgZGF0YSB3aXRoIG1hbnkgdmFyaWFibGVzIChsYXJnZSAkcCQpIGFzIGNvbXBhcmVkIHRvIHRoZSBudW1iZXIgb2YgaW5kZXBlbmRlbnQgcmVwbGljYXRlcyBvciBzYW1wbGVzIChzYW1wbGUgc2l6ZSAkbiQpLCByZXN1bHRpbmcgaW4gKip7aGlnaC1kaW1lbnNpb25hbCBkYXRhfSoqLCB3aGljaCBpcyBjaGFyYWN0ZXJpc2VkIGJ5CiAgXFsKICAgICBwID4+PiBuLgogIFxdCgotICBOZXcgc3RhdGlzdGljYWwgbWV0aG9kcyBmb3IgZGVhbGluZyB3aXRoIHRoZSAkcCA+Pj4gbiQgY2FzZSBoYXZlIGJlZW4gZGV2ZWxvcGVkIGluIHRoZSBsYXN0IDIwIHllYXJzLiAgU29tZSBvZiB0aGVtIGFyZSBhZGFwdGF0aW9ucyBvZiBtdWx0aXZhcmlhdGUgbWV0aG9kcy4KCi0tLQoKSXNzdWVzIHdpdGggaGlnaC1kaW1lbnNpb25hbCBkYXRhOgoKLSAqQ29tcHV0YXRpb25hbCBwcm9ibGVtcyo6IGxhcmdlIG1hdHJpY2VzLCBudW1lcmljYWwgYWNjdXJhY3kgb2YgY29tcHV0ZXJzIGJlY29tZSBhbiBpc3N1ZQotICpDbGFzc2ljYWwgYXN5bXB0b3RpYyB0aGVvcnkgZG9lcyBub3QgaG9sZCogZm9yICRwXHJpZ2h0YXJyb3dcaW5mdHkkIGFzICRuIFxyaWdodGFycm93IFxpbmZ0eSQKLSAqTW9kZWwgKG9yIGZlYXR1cmUpIHNlbGVjdGlvbiogcmVxdWlyZXMgc3BlY2lhbGlzZWQgbWV0aG9kcyB0byBkZWFsIHdpdGggdGhlIGVub3Jtb3VzIG51bWJlciBvZiBwb3NzaWJsZSBtb2RlbHMuIChlLmcuIGluIGxpbmVhciByZWdyZXNzaW9uIHdpdGggcCBwb3RlbnRpYWwgcHJlZGljdG9yczogJDJecCQgcG9zc2libGUgbW9kZWxzKQotIE1vZGVscyB0aGF0IGNhbiBwb3RlbnRpYWxseSBkZXBlbmQgb24gbGFyZ2UtcCBwcmVkaWN0b3JzIGFyZSB2dWxuZXJhYmxlIHRvICpodWdlIG92ZXJmaXR0aW5nKi4KLSBJbiBzZWFyY2hpbmcgZm9yIGFzc29jaWF0aW9ucyBiZXR3ZWVuIGFuIG91dGNvbWUgYW5kIGxhcmdlIHAgcG90ZW50aWFsIGV4cGxvcmF0b3J5IHZhcmlhYmxlcywgd2UgYXJlIGF0IHJpc2sgdG8gbWFrZSAqbWFueSBmYWxzZSBkaXNjb3ZlcmllcyoKCi0gVGhlICpDdXJzZSBvZiBEaW1lbnNpb25hbGl0eSogbWF5IGNhdXNlIGEgcHJlZGljdGlvbiBtb2RlbCB0byBiZWNvbWUgdXNlbGVzcy4gKCRuJCBkYXRhIHBvaW50cyBiZWNvbWUgc3BhcnNlIGluIGxhcmdlLSRwJCBkaW1lbnNpb25hbCBzcGFjZSkKCi0tLQoKIyBJbXBvcnRhbnQgdGFza3MgaW4gaGlnaCBkaW1lbnNpb25hbCBkYXRhIGFuYWx5c2lzPwoKIyMgRXhhbXBsZTogICBLYW5nIGV0IGFsLiAoMjAxOCnigJlzIGRyb3BsZXQtYmFzZWQgc2NSTkEtc2VxIGRhdGEgb2YgUEJNQ3MgY2VsbHMgZnJvbSA4IGx1cHVzIHBhdGllbnRzIG1lYXN1cmVkIGJlZm9yZSBhbmQgYWZ0ZXIgNmgtdHJlYXRtZW50IHdpdGggSU5GLSRcYmV0YSQgKDE2IHNhbXBsZXMgaW4gdG90YWwpLgoKYGBge3IgbGlicmFyaWVzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMjIFBhY2thZ2VzIHRvIGxvYWQgYW5kIHZpc3VhbGl6ZSB0aGUgc2luZ2xlLWNlbGwgZGF0YQpsaWJyYXJ5KEV4cGVyaW1lbnRIdWIpCmxpYnJhcnkoc2NhdGVyKQpgYGAKCmBgYHtyfQojIyBMb2FkIEthbmcgc2luZ2xlLWNlbGwgZGF0YSBmcm9tIEV4cGVyaW1lbnRIdWIKZWggPC0gRXhwZXJpbWVudEh1YigpCnNjZSA8LSBlaFtbIkVIMjI1OSJdXQoKc2NlCmBgYAoKLSBEYXRhIG9uIGdlbmUgZXhwcmVzc2lvbiBvZiBgciBucm93KHNjZSlgIGdlbmVzIG9mIGByIG5jb2woc2NlKWAgY2VsbHMuCgoKIyMgRGF0YSBleHBsb3JhdGlvbiBhbmQgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uCgotIFZpc3VhbGlzYXRpb24gaXMgYSBmaXJzdCBlc3NlbnRpYWwgc3RlcCB0byBsZWFybiBmcm9tIGRhdGEKCgpgYGB7cn0KY291bnRzKHNjZSlbMTg5OTA6MTkwMDAsOToyMF0KYGBgCgotIEl0IGlzIGltcG9zc2libGUgdG8gbGVhcm4gYWJvdXQgdGhlIHN0cnVjdHVyZSBpbiB0aGUgZGF0YSBieSBzdGFyaW5nIGF0IHRoZSBkYXRhIG1hdHJpeC4KLSBXZSBzaG91bGQgYmUgYWJsZSB0byBleHBsb3JlIHRoZSBkYXRhIGluIGEgbG93IGRpbWVuc2lvbmFsIHByb2plY3Rpb24KCmBgYHtyfQpwbG90UmVkdWNlZERpbShzY2UsIGRpbXJlZD0iVFNORSIsIGNvbG91cl9ieT0iY2VsbCIpCnBsb3RSZWR1Y2VkRGltKHNjZSwgZGltcmVkPSJUU05FIiwgY29sb3VyX2J5PSJzdGltIikKYGBgCgotIE5vdGUsIHRoYXQgd2Ugc2VlIGh1Z2UgZWZmZWN0IG9mIHRyZWF0bWVudC4gSWYgSSBzZWUgdGhpcyBJIGFtIGFsd2F5cyBvbiBteSBndWFyZCEKLSBXZSBjb250YWN0ZWQgdGhlIGF1dGhvcnMgYW5kIGxlYXJuZWQgdGhhdCBhbGwgY29udHJvbCBjZWxscyB3ZXJlIHNlcXVlbmNlZCBpbiBhIGZpcnN0IHJ1biBhbmQgYWxsIHN0aW11bGF0ZWQgY2VsbHMgd2VyZSBvbiBhIHNlY29uZCBzZXF1ZW5jaW5nIHJ1bi4gU28gdGhlIGxhcmdlIGVmZmVjdCBtaWdodCBiZSBhbiBlZmZlY3Qgb2YgYmF0Y2ghCgojIyBQcmVkaWN0aW9uCgpgYGB7cn0KcGxvdFJlZHVjZWREaW0oc2NlLCBkaW1yZWQ9IlRTTkUiLCBjb2xvdXJfYnk9ImNlbGwiKQpgYGAKCi0gSW4gc2luZ2xlIGNlbGwgYW5hbHlzaXMgaXQgaXMga2V5IHRvIGlkZW50aWZ5IGNlbGwgdHlwZXMgaW4gc2NSTkEtc2VxIGRhdGEgc2V0cyBiZWZvcmUgaW4tZGVwdGggaW52ZXN0aWdhdGlvbnMgb2YgdGhlaXIgZnVuY3Rpb25hbCBhbmQgcGF0aG9sb2dpY2FsIHJvbGVzLgoKLSBVc2UgbW9kZWxzIHRoYXQgd2VyZSBidWlsZCBiYXNlZCBvbiByZWZlcmVuY2UgZGF0YSBzZXRzIHRvIHByZWRpY3QgY2VsbCB0eXBlcyBpbiBuZXcgZGF0YSBzZXRzIGJhc2VkIG9uIHRoZWlyIGdlbmUgZXhwcmVzc2lvbiBwYXR0ZXJuCgotIFByb2JsZW06IHdlIGhhdmUgYHIgbnJvdyhzY2UpYCBnZW5lcyBhdCBvdXIgZGlzcG9zYWwgdG8gYnVpbGQgdGhpcyBwcmVkaWN0aW9uIG1vZGVsIQoKLSBPdGhlciBleGFtcGxlcwoKICAtIFByZWRpY3Rpb24gb2YgcmlzayBvbiBtb3J0YWxpdHkgaXMgdXNlZCBvbiBhIGRhaWx5IGJhc2lzIGluIGludGVuc2l2ZSBjYXJlIHVuaXRzIHRvIHByaW9yaXRpc2UgcGF0aWVudCBjYXJlLgoKICAtIEZhY2Vib29rOiBwcmVkaWN0IHRoZSBpZGVudGl0eSBvZiB0aGUgZmFjZXMgb2YgcGVvcGxlIHRoYXQgYXJlIG9uIGFuIG5ldyBpbWFnZSB0aGF0IGlzIHVwbG9hZGVkLgoKICAtIE5ldGZsaXg6IFN1Z2dlc3QgbW92aWVzIHRoYXQgeW91IHdvdWxkIGxpa2UKCiMjIExhcmdlIHNjYWxlIGh5cG90aGVzaXMgdGVzdGluZwoKYGBge3J9CnBsb3RSZWR1Y2VkRGltKHNjZSwgZGltcmVkPSJUU05FIiwgY29sb3VyX2J5PSJjZWxsIiwgc2hhcGVfYnk9InN0aW0iKQpgYGAKCi0gV2hpY2ggZ2VuZXMgYXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBiZXR3ZWVuIGNvbnRyb2wgYW5kIHN0aW11bGF0ZWQgdHJlYXRtZW50ICAoYXNzZXNzIGluIGVhY2ggY2VsbCB0eXBlKQotIEZvciB3aGljaCBnZW5lcyB3ZSBzZWUgdGhhdCB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYWNjb3JkaW5nIHRvIHRyZWF0bWVudCBpcyBjaGFuZ2luZyBhY2NvcmRpbmcgdG8gdGhlIGNlbGwgdHlwZSAoaW50ZXJhY3Rpb24pCi0gV2UgaGF2ZSB0byBtb2RlbCB0aGUgZ2VuZSBleHByZXNzaW9uIGZvciBlYWNoIGdlbmUKCiQkClxsZWZ0IFx7ClxiZWdpbnthcnJheX17bGNsfQp5X3tpZ30gJlxzaW0mIE5CKFxtdV97aWd9LCBccGhpX3tnfSlcXApFW3lfe2lnfV0gJj0mIFxtdV97aWd9XFwKXGxvZyhcbXVfe2lnfSkgJj0mIFxldGFfe2lnfVxcClxldGFfe2lnfSAmPSYgXGJldGFfezAsZ30gKyBcc3VtXGxpbWl0c197Yz0yfV5DIFxiZXRhX3tjLGd9IFhfe2ljfSArIFxiZXRhX3tzLGd9IFhfe2lzfSArIFxzdW1cbGltaXRzX3tjPTJ9XkMgXGJldGFfe2M6cyxnfSBYX3tpY30gWF97aXN9ICArIFxzdW1cbGltaXRzX3tlPTJ9XkUgXGJldGFfe2UsZ30gWF97aWV9ICsgXGFscGhhX3tpZ30KXGVuZHthcnJheX0gXHJpZ2h0LgokJAoKV2l0aAoKLSBDZWxsIHR5cGUgJGM9MlxsZG90cyBDJCBhbmQgJFhfe2ljfSQgaXMgYSBkdW1teSB2YXJpYWJsZSB0aGF0ICRYX3tpY309MSQgaWYgY2VsbCAkaSQgaXMgb2YgY2VsbCB0eXBlIGMgYW5kICRYX3tpY309MCQgb3RoZXJ3aXNlLiBOb3RlIHRoYXQgY2VsbCB0eXBlICRjPTEkIGlzIHRoZSByZWZlcmVuY2UgY2VsbCB0eXBlCgotIEluZGljYXRvciAkWF97aXN9JCBpbmRpY2F0ZXMgaWYgY2VsbCAkaSQgd2FzIHN0aW11bGF0ZWQgJFhfe2lzfT0xJCBvciBub3QgJFhfe2lzfT0wJC4gU28gdGhlIGNvbnRyb2wgdHJlYXRtZW50IGlzIHRoZSByZWZlcmVuY2UgdHJlYXRtZW50LgoKLSBFeHBlcmltZW50YWwgdW5pdCAkZT0yXGxkb3RzIEUkIGFuZCAkWF97aWV9JCBhIGR1bW15IHZhcmlhYmxlIHRoYXQgJFhfe2llfT0xJCBpZiBjZWxsICRpJCBvcmlnaW5hdGVzIG9mIGV4cGVyaW1lbnRhbCB1bml0IChzdWJqZWN0KSAkZSQgYW5kICRYX3tpZX09MCQgb3RoZXJ3aXNlLiBOb3RlIHRoYXQgY2VsbCB0eXBlICRlPTEkIGlzIHRoZSByZWZlcmVuY2UgZXhwZXJpbWVudGFsIHVuaXQuIAoKLSAkXGFscGhhX3tpZ30kIGEgY2VsbCBzcGVjaWZpYyBhbmQgZ2VuZSBzcGVjaWZpYyBub3JtYWxpc2F0aW9uIGZhY3RvcgoKClN1cHBvc2Ugd2Ugd2FudCB0byB0ZXN0IGlmIHRoZSBlZmZlY3Qgb2YgdGhlIHN0aW11bHVzIChhdmVyYWdlIGRpZmZlcmVuY2UgaW4gZXhwcmVzc2lvbiBiZXR3ZWVuIHN0aW11bGF0ZWQgYW5kIG5vbiBzdGltdWxhdGVkIGNlbGxzKSBpcyBkaWZmZXJlbnQgaW4gY2VsbCB0eXBlIGMgdGhhbiBpbiB0aGUgcmVmZXJlbmNlIGNlbGwgdHlwZSAxPwoKLSAkSF8wOiBcYmV0YV97YzpzLGd9ID0gMCQKLSAkSF8xOiBcYmV0YV97YzpzLGd9IFxuZXEgMCQKCgotIFdlIGhhdmUgdG8gYXNzZXNzIHRoaXMgZm9yIGByIG5yb3coc2NlKWAgZ2VuZXMhCi0gSWYgd2UgYXNzZXNzIGVhY2ggdGVzdCBhdCB0aGUgNSUgbGV2ZWwgd2UgY2FuIGV4cGVjdCAwLjA1ICogYHIgbnJvdyhzY2UpYCA9IGByIHJvdW5kKDAuMDUqbnJvdyhzY2UpLDApYCBmYWxzZSBwb3NpdGl2ZXMuCgokXHJpZ2h0YXJyb3ckIG1hc3NpdmUgbXVsdGlwbGUgdGVzdGluZyBwcm9ibGVtIQoKCk5vdGUsIHRoYXQgd2UgY2Fubm90IGRpZmZlcmVudGlhdGUgYmV0d2VlbiBiYXRjaCBhbmQgdHJlYXRtZW50IGJlY2F1c2Ugb2YgdGhlIGZsYXcgaW4gdGhlIGV4cGVyaW1lbnRhbCBkZXNpZ24hCgpPdGhlciBleGFtcGxlcwoKLSBGaW5kIHJlZ2lvbnMgKHZveGVscykgaW4gdGhlIGJyYWluIChvbiBicmFpbiBpbWFnZSkgdGhhdCBhcmUgYXNzb2NpYXRlZCB3aXRoIGEgY2VydGFpbiBjb25kaXRpb24vdHJlYXRtZW50Ci0gRXZhbHVhdGlvbiBvZiB0cmFkaW5nIHJ1bGVzCgoKCi0tLQoKCiMgTGluZWFyIHJlZ3Jlc3Npb24KCi0gTGluZWFyIHJlZ3Jlc3Npb24gaXMgYSB2ZXJ5IGltcG9ydGFudCBzdGF0aXN0aWNhbCB0b29sIHRvIHN0dWR5IHRoZSBhc3NvY2lhdGlvbiBiZXR3ZWVuIHZhcmlhYmxlcyBhbmQgdG8gYnVpbGQgcHJlZGljdGlvbiBtb2RlbHMuCgojIyBUb3ktRGF0YQoKQ29uc2lkZXIgdGhlIGZvbGxvd2luZyB0b3ktZGF0YXNldCB3aXRoIDMgb2JzZXJ2YXRpb24gKFgsWSk6CgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmRhdGEgPC0gZGF0YS5mcmFtZSh4PTE6Myx5PWMoMSwyLDIpKQpkYXRhCmBgYAoKIyMgTW9kZWwKCiMjIyBTY2FsYXIgZm9ybQoKLSBDb25zaWRlciBhIHZlY3RvciBvZiBwcmVkaWN0b3JzICRcbWF0aGJme3h9PSh4XzEsXGxkb3RzLHhfcCkkIGFuZAotIGEgcmVhbC12YWx1ZWQgcmVzcG9uc2UgJFkkCi0gdGhlbiB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgY2FuIGJlIHdyaXR0ZW4gYXMKXFsKWT1mKFxtYXRoYmZ7eH0pICtcZXBzaWxvbj1cYmV0YV8wK1xzdW1cbGltaXRzX3tqPTF9XnAgeF9qXGJldGFfaiArIFxlcHNpbG9uClxdCndpdGggaS5pLmQuICRcZXBzaWxvblxzaW0gTigwLFxzaWdtYV4yKSQKLSBXZSB3aWxsIG9mdGVuIHdvcmsgb24gbWVhbiBjZW50ZXJlZCB2YXJpYWJsZXM6ICRZX2MgPSBZLVxiYXJ7WX0kIGFuZCAkWF9jID0gWCAtIFxiYXJ7WH0kLCB0aGVuIHRoZSBpbnRlcmNlcHQgaXMgZHJvcHBlZCBmcm9tIHRoZSBtb2RlbDoKXFsKWV9jPWYoXG1hdGhiZnt4fV9jKSArXGVwc2lsb249XHN1bVxsaW1pdHNfe2o9MX1ecCB4X3tjan1cYmV0YV9qICsgXGVwc2lsb24KXF0KCiMjIyBTY2FsYXIgbW9kZWwgZm9yIHRoZSB0b3kgZGF0YXNldAoKJCQKeV9pPVxiZXRhXzArXGJldGFfMXggKyBcZXBzaWxvbl9pCiQkCgpJZiB3ZSB3cml0ZSB0aGUgbW9kZWwgZm9yIGVhY2ggb2JzZXJ2YXRpb246CgokJApcYmVnaW57YXJyYXl9IHtsY2x9CjEgJj0mIFxiZXRhXzArXGJldGFfMSAxICArIFxlcHNpbG9uXzEgXFwKMiAmPSYgXGJldGFfMCArIFxiZXRhXzEgMiArIFxlcHNpbG9uXzIgXFwKMiAmPSYgXGJldGFfMCtcYmV0YV8xIDMrIFxlcHNpbG9uXzMgXFwKXGVuZHthcnJheX0KJCQKCgojIyMgVmVjdG9yL01hdHJpeCBmb3JtCgotICRuJCBvYnNlcnZhdGlvbnMgJChcbWF0aGJme3h9XzEseV8xKSBcbGRvdHMgKFxtYXRoYmZ7eH1fbix5X24pJCB3aXRoICRcbWF0aGJme3h9XzFeVD1bXGJlZ2lue2FycmF5fXtjY2NjfSAxJiB4XzEmIFxsZG90cyYgeF9wXGVuZHthcnJheX1dJAotIFJlZ3Jlc3Npb24gaW4gbWF0cml4IG5vdGF0aW9uClxbXG1hdGhiZntZfT1cbWF0aGJme1hcYmV0YX0gKyBcbWF0aGJme1xlcHNpbG9ufVxdCndpdGggJFxtYXRoYmZ7WX09XGxlZnRbXGJlZ2lue2FycmF5fXtjfXlfMVxcIFx2ZG90c1xceV9uXGVuZHthcnJheX1ccmlnaHRdJCwKJFxtYXRoYmZ7WH09XGxlZnRbXGJlZ2lue2FycmF5fXtjY2NjfSAxJnhfezExfSZcbGRvdHMmeF97MXB9XFwKXHZkb3RzJlx2ZG90cyYmXHZkb3RzXFwKMSZ4X3tuMX0mXGxkb3RzJnhfe25wfQpcZW5ke2FycmF5fVxyaWdodF0kIG9yICRcbWF0aGJme1h9PVxsZWZ0W1xiZWdpbnthcnJheX17Y30gXG1hdGhiZnt4fV8xXlRcXFx2ZG90c1xcXG1hdGhiZnt4fV9uXlRcZW5ke2FycmF5fVxyaWdodF0kLAokXGJvbGRzeW1ib2x7XGJldGF9PVxsZWZ0W1xiZWdpbnthcnJheX17Y31cYmV0YV8wXFwgXHZkb3RzXFwgXGJldGFfcFxlbmR7YXJyYXl9XHJpZ2h0XSQgYW5kCiRcbWF0aGJme1xlcHNpbG9ufT1cbGVmdFtcYmVnaW57YXJyYXl9e2N9IFxlcHNpbG9uXzEgXFwgXHZkb3RzIFxcIFxlcHNpbG9uX25cZW5ke2FycmF5fVxyaWdodF0kCgpOb3RlLCB0aGF0IHVwb24gY2VudGVyaW5nIG9mICRcbWF0aGJme1h9JCBhbmQgJFxtYXRoYmZ7WX0kIHRoZSAxIGlzIGRyb3BwZWQgZnJvbSBlYWNoICRcbWF0aGJme3h9X2kkIGFuZCB0aHVzIHRoZSBjb2x1bW4gb2YgMSBpcyBkcm9wcGVkIGluICRcbWF0aGJme1h9X2MkCgojIyMgTWF0cml4IGZvcm0gZm9yIHRveSBkYXRhc2V0CgpXZSBjYW4gYWxzbyB3cml0ZSB0aGlzIGluIG1hdHJpeCBmb3JtCgokJApcbWF0aGJme1l9ID0gXG1hdGhiZntYfVxib2xkc3ltYm9se1xiZXRhfStcYm9sZHN5bWJvbHtcZXBzaWxvbn0KJCQKCndpdGgKCiQkClxtYXRoYmZ7WX09XGxlZnRbClxiZWdpbnthcnJheX17Y30KMVxcCjJcXAoyXFwKXGVuZHthcnJheX1ccmlnaHRdLApccXVhZApcbWF0aGJme1h9PSBcbGVmdFsKXGJlZ2lue2FycmF5fXtjY30KMSYxXFwKMSYyXFwKMSYzXFwKXGVuZHthcnJheX0KXHJpZ2h0XSwKXHF1YWQgXGJvbGRzeW1ib2x7XGJldGF9ID0gXGxlZnRbClxiZWdpbnthcnJheX17Y30KXGJldGFfMFxcClxiZXRhXzFcXApcZW5ke2FycmF5fQpccmlnaHRdClxxdWFkClx0ZXh0e2FuZH0KXHF1YWQKXGJvbGRzeW1ib2x7XGVwc2lsb259PQpcbGVmdFsKXGJlZ2lue2FycmF5fXtjfQpcZXBzaWxvbl8xXFwKXGVwc2lsb25fMlxcClxlcHNpbG9uXzMKXGVuZHthcnJheX0KXHJpZ2h0XQokJAoKCgoKCi0tLQoKIyMgSW50ZXJwcmV0YXRpb24KCkZyb20gdGhlIGxpbmVhciByZWdyZXNzaW9uIHdlIGdldApcWwogIEVbWSBcbWlkIFxtYXRoYmZ7eH1dID0gXG1hdGhiZnt4fV5UXGJvbGRzeW1ib2x7XGJldGF9IC4KXF0KCi0gSGVuY2UsIHRoZSAkXGJldGEkIHBhcmFtZXRlcnMgcmVsYXRlIHRoZSBwcmVkaWN0b3IgJFxtYXRoYmZ7eH0kIHRvIHRoZSBtZWFuIG91dGNvbWUuCi0gSWYgd2Uga25vdyB0aGUgY292YXJpYXRlIHBhdHRlcm4gJFxtYXRoYmZ7eH0kIHdlIGNhbiB1c2UgdGhlIG1vZGVsIHRvCnByZWRpY3QgJFkkLgoKRm9yIGEgbW9kZWwgd2l0aCBhIHNpbmdsZSBwcmVkaWN0b3Igd2Ugb2J0YWluCgpcWwpFW1kgXG1pZCB4XSA9IFxiZXRhXzAgKyBcYmV0YV8xIHgKXF0KYW5kClxbCiBcYmV0YV8xID0gRVtZXG1pZCB4KzFdIC0gRVtZXG1pZCB4XSA9IFxiZXRhXzAgKyBcYmV0YV8xICh4KzEpIC0gXGJldGFfMCAtIFxiZXRhXzEgeC4KXF0KCkluIHRoaXMgY291cnNlIHdlIHdpbGwgdHlwaWNhbGx5IGNlbnRlciBZIGFuZCBYIGFuZCB0aGVuIHdlIGdldCB0aGUgZm9sbG93aW5nCgpcWwogIEVbWV9jIFxtaWQgeF9jXSA9IFxiZXRhXzEgeF9jClxdCmFuZApcWwogXGJldGFfMSA9IEVbWV9jXG1pZCB4X2MrMV0gLSBFW1lfY1xtaWQgeF9jXSAuClxdCgpOb3RlLCB0aGF0IHRoZSBlc3RpbWF0b3IgZm9yICRcYmV0YV8xJCB3aWxsIGJlIGV4YWN0bHkgc2FtZSB3aGVuIGVzdGltYXRlZCBiYXNlZCBvbiB0aGUgbW9kZWxzIHdpdGgvb3Igd2l0aG91dCBjZW50ZXJpbmcuCgotIFRoZSBwYXJhbWV0ZXIgICRcYmV0YV8xJCBoYXMgYW4gaW50ZXJwcmV0YXRpb24gYXMgdGhlIGF2ZXJhZ2UgZGlmZmVyZW5jZSBpbiB0aGUgb3V0Y29tZSBiZXR3ZWVuIHN1YmplY3RzIHRoYXQgZGlmZmVyIHdpdGggb25lIHVuaXQgZm9yIHRoZSBwcmVkaWN0b3IuCgotIFRoZSBwYXJhbWV0ZXIgICRcYmV0YV8xJCAgZG9lcyBub3Qgc2F5IG11Y2ggYWJvdXQgaW5kaXZpZHVhbCBvdXRjb21lcy4gVGhlIHJlc2lkdWFsIHZhcmlhbmNlICRcc2lnbWFeMiQgZGV0ZXJtaW5lcyBob3cgbXVjaCBpbmRpdmlkdWFsIG91dGNvbWVzIHZhcnkgYWJvdXQgdGhlIG1lYW4gb3V0Y29tZS4KClRoZSAkXGJldGEkIHBhcmFtZXRlcnMgYXJlIHVzZWQgdG8gbWVhc3VyZSBhc3NvY2lhdGlvbiwgYnV0IGEgJFxiZXRhIFxuZXEgMCQgZG9lcyBub3QgbmVjZXNzYXJpbHkgbWVhbiB0aGF0IHRoZSBtb2RlbCB3aWxsIGdpdmUgZ29vZCBwcmVkaWN0aW9ucy4KCiQkClxoYXQgWSA9IFxtYXRoYmZ7eH1eVCBcaGF0IFxiZXRhCiQkCi0gSW4gQ2hhcHRlciAzIHdpbGwgd2Ugd2lsbCBkaXNjdXNzIHRoZSBwcmVkaWN0aW9uIHByb2JsZW0gZm9yIGhpZ2ggZGltZW5zaW9uYWwgZGF0YQoKTW9kZWwgZml0IGFuZCBwcmVkaWN0aW9ucyBiYXNlZCBvbiB0aGUgdG95IGRhdGFzZXQKCmBgYHtyfQpsbTEgPC0gbG0oeX54LGRhdGEpCmRhdGEkeWhhdCA8LSBsbTEkZml0dGVkCgpkYXRhICU+JQogIGdncGxvdChhZXMoeCx5KSkgKwogIGdlb21fcG9pbnQoKSArCiAgeWxpbSgwLDQpICsKICB4bGltKDAsNCkgKwogIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0gInJlZCIsIGZ1bGxyYW5nZSA9IFRSVUUpICsKICBnZW9tX3BvaW50KGFlcyh4PXgsIHkgPXloYXQpLCBwY2ggPSAyLCBzaXplID0gMywgY29sb3IgPSAicmVkIikgKwogIGdlb21fc2VnbWVudChkYXRhID0gZGF0YSwgYWVzKHggPSB4LCB4ZW5kID0geCwgeSA9IHksIHllbmQgPSB5aGF0KSwgbHR5ID0gMiApCmBgYAoKCi0tLQoKCi0gSW4gYSBDaGFwdGVyIDYgd2Ugd2lsbCBkaXNjdXNzIHRoZSBwcm9ibGVtIG9mIGxhcmdlIHNjYWxlIGh5cG90aGVzaXMgdGVzdGluZzogdGVzdGluZyBtYW55IGh5cG90aGVzZXMgaW4gYSBzaW5nbGUgc3R1ZHkgKHRlbiB0byBodW5kcmVkIHRob3VzYW5kcyBvZiBoeXBvdGhlc2VzKS4KCiAgICAtIEEgc3RhdGlzdGljYWwgdGVzdCBpcyBjb25zdHJ1Y3RlZCB0byBjb250cm9sIHRoZSB0eXBlIEkgZXJyb3IgcmF0ZSBhdCB0aGUgc2lnbmlmaWNhbmNlIGxldmVsICRcYWxwaGEkIHRvIGFzc2VzcyB0aGUgbnVsbCBoeXBvdGhlc2lzIHRoYXQgdGhlcmUgaXMgbm8gYXNzb2NpYXRpb24gYmV0d2VlbiBhIHByZWRpY3RvciBhbmQgdGhlIG91dGNvbWUgdnMgdGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMgdGhhdCB0aGVyZSBpcyBhbiBhc3NvY2lhdGlvbiBiZXR3ZWVuIGEgcHJlZGljdG9yIGFuZCB0aGUgb3V0Y29tZS4KCiAgICAkJAogICAgSF8wOiBcYmV0YV8xID0gMCBcdGV4dHsgdnMgfUhfMTpcYmV0YV8xIFxuZXEgMC4KICAgICQkCgogICAgLSBIb3dldmVyLCB3aGVuIG1hbnkgaHlwb3RoZXNlcyBhcmUgdG8gYmUgdGVzdGVkIGluIGEgc2luZ2xlIHN0dWR5LCB0aGUgcHJvYmFiaWxpdHkgdG8gZmluZCBmYWxzZSBhc3NvY2lhdGlvbnMgaXMgbm8gbG9uZ2VyIGNvbnRyb2xsZWQgaWYgcC12YWx1ZXMgYXJlIGNvbXBhcmVkIHRvIHRoZSBzaWduaWZpY2FuY2UgbGV2ZWwgJFxhbHBoYSQuCgogICAgLSBXZSB3aWxsIGxhdGVyIGludHJvZHVjZSB0aGUgY29uY2VwdCBvZiBmYWxzZSBkaXNjb3ZlcnkgcmF0ZXMgdG8gb3ZlcmNvbWUgdGhlIHByb2JsZW0uCgojIyBMZWFzdCBTcXVhcmVzIChMUykKCi0gTWluaW1pemUgdGhlIHJlc2lkdWFsIHN1bSBvZiBzcXVhcmVzClxiZWdpbntlcW5hcnJheSp9ClJTUyhcYm9sZHN5bWJvbHtcYmV0YX0pJj0mXHN1bVxsaW1pdHNfe2k9MX1ebiBlXjJfaVxcXFwKJj0mXHN1bVxsaW1pdHNfe2k9MX1ebiBcbGVmdCh5X2ktXGJldGFfMC1cc3VtXGxpbWl0c197aj0xfV5wIHhfe2lqfVxiZXRhX2pccmlnaHQpXjIKXGVuZHtlcW5hcnJheSp9CgotIG9yIGluIG1hdHJpeCBub3RhdGlvbgpcYmVnaW57ZXFuYXJyYXkqfQpSU1MoXGJvbGRzeW1ib2x7XGJldGF9KSY9JlxtYXRoYmZ7ZX1eVFxtYXRoYmZ7ZX1cXFxcCiY9JiBcbGVmdFtcYmVnaW57YXJyYXl9e2NjY30gZV8xICZcbGRvdHMmIGVfbiBcZW5ke2FycmF5fVxyaWdodF1cbGVmdFtcYmVnaW57YXJyYXl9e2N9ZV8xXFxcdmRvdHNcXGVfblxlbmR7YXJyYXl9XHJpZ2h0XVxcCiY9JiBlXzFeMiArIGVfMl4yICsgXGxkb3RzICsgZV9uXjJcXFxcCiY9JihcbWF0aGJme1l9LVxtYXRoYmZ7WFxiZXRhfSleVChcbWF0aGJme1l9LVxtYXRoYmZ7WFxiZXRhfSlcXFxcCiY9JlxWZXJ0IFxtYXRoYmZ7WX0tXG1hdGhiZntYXGJldGF9XFZlcnReMl8yXFwKXGVuZHtlcW5hcnJheSp9CndpdGggdGhlICRMXzIkLW5vcm0gb2YgYSAkcCQtZGltLiB2ZWN0b3IgJHYkICRcVmVydCBcbWF0aGJme3Z9IFxWZXJ0XzI9XHNxcnR7dl8xXjIrXGxkb3RzK3ZfcF4yfSQKJFxyaWdodGFycm93JCAkXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19PVx0ZXh0e2FyZ21pbn1fXGJldGEgXFZlcnQgXG1hdGhiZntZfS1cbWF0aGJme1hcYmV0YX1cVmVydF4yXzIkCgoKLS0tCgojIyMgTWluaW1pemUgUlNTClxbClxiZWdpbnthcnJheX17Y2NjfQpcZnJhY3tccGFydGlhbCBSU1N9e1xwYXJ0aWFsIFxib2xkc3ltYm9se1xiZXRhfX0mPSZcbWF0aGJmezB9XFxcXApcZnJhY3soXG1hdGhiZntZfS1cbWF0aGJme1hcYmV0YX0pXlQoXG1hdGhiZntZfS1cbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9KX17XHBhcnRpYWwgXGJvbGRzeW1ib2x7XGJldGF9fSY9JlxtYXRoYmZ7MH1cXFxcCi0yXG1hdGhiZntYfV5UKFxtYXRoYmZ7WX0tXG1hdGhiZntYfVxib2xkc3ltYm9se1xiZXRhfSkmPSZcbWF0aGJmezB9XFxcXApcbWF0aGJme1h9XlRcbWF0aGJme1hcYmV0YX0mPSZcbWF0aGJme1h9XlRcbWF0aGJme1l9XFxcXApcaGF0e1xib2xkc3ltYm9se1xiZXRhfX0mPSYoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFxtYXRoYmZ7WX0KXGVuZHthcnJheX0KXF0KCkl0IGNhbiBiZSBzaG93biB0aGF0IHRoZSBlc3RpbWF0b3IgaXMgdW5iaWFzZWQ6CgokJApFW1xoYXR7XGJvbGRzeW1ib2x7XGJldGF9fV09XGJvbGRzeW1ib2x7XGJldGF9CiQkCgoKLS0tCgoKIyMjIFByb2plY3Rpb24KClRoZXJlIGlzIGFsc28gYW5vdGhlciBwaWN0dXJlIHRvIHJlZ3Jlc3Npb246CgotIEluc3RlYWQgb2YgcGxvdHRpbmcgZWFjaCBvYnNlcnZhdGlvbiAkaT0gMSBcbGRvdHMgbiQgYXMgYSBkYXRhLXBvaW50IGluICRcbWF0aGJie1J9XnAkIHdpdGggZGltZW5zaW9ucyAkMSBcbGRvdHMgcCQgZm9yIGV2ZXJ5IHZhcmlhYmxlL2ZlYXR1cmUgdGhhdCBpcyByZWNvcmRlZCBmb3IgZWFjaCBvYnNlcnZhdGlvbgoKLSBXZSBjYW4gYWxzbyBwbG90ICRcbWF0aGJme1l9JCwgJFxoYXR7XG1hdGhiZntZfX0kIGFuZCBlYWNoIGNvbHVtbiBvZiAkXG1hdGhiZntYfSQ6ICRcbWF0aGJme1h9X2okIHdpdGggJGo9MSBcbGRvdHMgcCQgYXMgYSB2ZWN0b3IgaW4gJFxtYXRoYmJ7Un1ebiQgd2l0aCBkaW1lbnNpb25zICQxIFxsZG90cyBuJCBmb3IgZXZlcnkgb2JzZXJ2YXRpb24uCgotIEluIHRoaXMgcmVwcmVzZW50YXRpb24gbGluZWFyIHJlZ3Jlc3Npb24gY2FuIGJlIGludGVycHJldGVkIGFzIGEgcHJvamVjdGlvbiBvZiB0aGUgdmVjdG9yICRcbWF0aGJme1l9JCBvbnRvIHRoZSBzdWJzcGFjZSBvZiAkXG1hdGhiYntSfV5uJCB0aGF0IGlzIHNwYW5uZWQgYnkgdGhlIHZlY3RvcnMgZm9yIHRoZSBwcmVkaWN0b3JzICRcbWF0aGJme1h9XzEgXGxkb3RzIFxtYXRoYmZ7WH1fcCQuCgotIFRoZSBzcGFjZSAkXG1hdGhiZntYfV8xIFxsZG90cyBcbWF0aGJme1h9X3AkIGlzIGFsc28gcmVmZXJyZWQgdG8gYXMgdGhlIGNvbHVtbiBzcGFjZSBvZiAkXG1hdGhiZntYfSQsIHRoZSBzcGFjZSB0aGF0IGNvbnNpc3RzIG9mIGFsbCBsaW5lYXIgY29tYmluYXRpb25zIG9mIHRoZSB2ZWN0b3JzIG9mIHRoZSBwcmVkaWN0b3JzIG9yIGNvbHVtbnMgJFxtYXRoYmZ7WH1fMSBcbGRvdHMgXG1hdGhiZntYfV9wJC4KCgojIyMjIEludGVybWV6em86IFByb2plY3Rpb24gb2YgdmVjdG9yIG9uIFggIGFuZCBZIGF4aXMKCiQkClxtYXRoYmZ7ZX09XGxlZnRbXGJlZ2lue2FycmF5fXtjfSBlXzFcXGVfMlxlbmR7YXJyYXl9XHJpZ2h0XSwgXG1hdGhiZnt1fV8xID0gIFxsZWZ0W1xiZWdpbnthcnJheX17Y30gMVxcMFxlbmR7YXJyYXl9XHJpZ2h0XSwgXG1hdGhiZnt1fV8yID0gIFxsZWZ0W1xiZWdpbnthcnJheX17Y30gMFxcMVxlbmR7YXJyYXl9XHJpZ2h0XQokJAoKCmBgYHtyIGVjaG89RkFMU0V9CnBsb3RkYXRhIDwtIGRhdGEuZnJhbWUoZTE9MyxlMj0yKQpwbG90ZGF0YSAlPiUgZ2dwbG90KGFlcyh4PWUxLHk9ZTIpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAzLCB5ID0gMiwgeGVuZCA9IDMsIHllbmQgPSAwKSxjb2xvcj0ib3JhbmdlIixsaW5ldHlwZT0yLHNpemU9MikgKwpnZW9tX3NlZ21lbnQoYWVzKHggPSAzLCB5ID0gMiwgeGVuZCA9IDAsIHllbmQgPSAyKSxjb2xvcj0ib3JhbmdlIixsaW5ldHlwZT0yLHNpemU9MikgKwogZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSAzLCB5ZW5kID0gMiksCiAgICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjUsICJjbSIpKSxjb2xvcj0icmVkIixzaXplPTIpICsKIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gMywgeWVuZCA9IDApLAogICAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC41LCAiY20iKSksY29sb3I9Im9yYW5nZSIsc2l6ZT0yKSArCiBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IDAsIHllbmQgPSAyKSwKICAgICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuNSwgImNtIikpLGNvbG9yPSJvcmFuZ2UiLHNpemU9MikgICsKIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gMCwgeWVuZCA9IDEpLAogICAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC41LCAiY20iKSksc2l6ZT0yKSArCmdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gMSwgeWVuZCA9IDApLAogICAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC41LCAiY20iKSksc2l6ZT0yKSArCiBnZW9tX3RleHQoYWVzKHg9My4wNSx5PTIuMixsYWJlbD0iZT0oZTEsZTIpIiksY29sb3I9InJlZCIsc2l6ZSA9IDcsaGp1c3Q9ImxlZnQiKSArCiBnZW9tX3RleHQoYWVzKHg9My4wNSx5PTAsbGFiZWw9IihlMSwwKSIpLGNvbG9yPSJvcmFuZ2UiLHNpemUgPSA3LGhqdXN0PSJsZWZ0Iix2anVzdD0idG9wIikgKwogZ2VvbV90ZXh0KGFlcyh4PTAseT0yLjIsbGFiZWw9IigwLGUyKSIpLGNvbG9yPSJvcmFuZ2UiLHNpemUgPSA3LGhqdXN0PSJsZWZ0IikgKwogZ2VvbV90ZXh0KGFlcyh4PTAuMDUseT0xLjIsbGFiZWw9InUyPSgwLDEpIiksc2l6ZSA9IDcsaGp1c3Q9ImxlZnQiKSArCiAgIGdlb21fdGV4dChhZXMoeD0xLjA1LHk9MC4yLGxhYmVsPSJ1MT0oMSwwKSIpLHNpemUgPSA3LGhqdXN0PSJsZWZ0IikgICsKICAgY29vcmRfZml4ZWQoKSArCiAgeGxpbSgtLjUsNCkgKwogIHlsaW0oLS41LDIuNSkKYGBgCgoKCjEuIFByb2plY3Rpb24gb2YgZXJyb3Igb24geC1heGlzCgpcYmVnaW57ZXFuYXJyYXkqfQpcbWF0aGJme3V9XzFeVCBcbWF0aGJme2V9ICY9JiBcVmVydCBcbWF0aGJme3V9XzFcVmVydF8yICBcVmVydCBcbWF0aGJme2V9XzFcVmVydF8yIFxjb3MgPFxtYXRoYmZ7dX1fMSxcbWF0aGJme2V9XzE+XFwKJj0mXGxlZnRbXGJlZ2lue2FycmF5fXtjY30gMSYwXGVuZHthcnJheX1ccmlnaHRdIFxsZWZ0W1xiZWdpbnthcnJheX17Y30gZV8xXFxlXzJcZW5ke2FycmF5fVxyaWdodF1cXCAmPSYgMVx0aW1lcyBlXzEgKyAwIFx0aW1lcyBlXzIgXFwKJj0mIGVfMVxcClxlbmR7ZXFuYXJyYXkqfQoKMi4gUHJvamVjdGlvbiBvZiBlcnJvciBvbiB5LWF4aXMKClxiZWdpbntlcW5hcnJheSp9ClxtYXRoYmZ7dX1fMl5UIFxtYXRoYmZ7ZX0gJj0mIFxsZWZ0W1xiZWdpbnthcnJheX17Y2N9IDAmMVxlbmR7YXJyYXl9XHJpZ2h0XSBcbGVmdFtcYmVnaW57YXJyYXl9e2N9IGVfMVxcZV8yXGVuZHthcnJheX1ccmlnaHRdXFwgJj0mIDBcdGltZXMgZV8xICsgMSBcdGltZXMgZV8yIFxcCiY9JiBlXzIKXGVuZHtlcW5hcnJheSp9CgozLiBQcm9qZWN0aW9uIG9mIGVycm9yIG9uIGl0c2VsZgoKClxiZWdpbntlcW5hcnJheSp9ClxtYXRoYmZ7ZX1eVCBcbWF0aGJme2V9ICY9JlxsZWZ0W1xiZWdpbnthcnJheX17Y2N9IGVfMSZlXzJcZW5ke2FycmF5fVxyaWdodF0gXGxlZnRbXGJlZ2lue2FycmF5fXtjfSBlXzFcXGVfMlxlbmR7YXJyYXl9XHJpZ2h0XVxcCiY9JmVfMV4yK2VfMl4yXFwKJj0mXFZlcnQgZSBcVmVydF4yXzIgXHJpZ2h0YXJyb3cgXHRleHR7IFB5dGhhZ29yZWFuIHRoZW9yZW19ClxlbmR7ZXFuYXJyYXkqfQoKLS0tCgojIyMjIEludGVycHJldGF0aW9uIG9mIGxlYXN0IHNxdWFyZXMgYXMgYSBwcm9qZWN0aW9uCgpGaXR0ZWQgdmFsdWVzOgoKJCQKXGJlZ2lue2FycmF5fXtsY2x9ClxoYXR7XG1hdGhiZntZfX0gJj0mIFxtYXRoYmZ7WH1caGF0e1xib2xkc3ltYm9se1xiZXRhfX1cXAomPSYgXG1hdGhiZntYfSAoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFxtYXRoYmZ7WX1cXAomPSYgXG1hdGhiZntIWX0KXGVuZHthcnJheX0KJCQKd2l0aCAkXG1hdGhiZntIfSQgdGhlIHByb2plY3Rpb24gbWF0cml4IGFsc28gcmVmZXJyZWQgdG8gYXMgdGhlIGhhdCBtYXRyaXguCgoKYGBge3J9ClggPC0gbW9kZWwubWF0cml4KH54LGRhdGEpClgKYGBgCgpgYGB7cn0KWHRYIDwtIHQoWCklKiVYClh0WApgYGAKCmBgYHtyfQpYdFhpbnYgPC0gc29sdmUodChYKSUqJVgpClh0WGludgpgYGAKCmBgYHtyfQpIIDwtIFggJSolIFh0WGludiAlKiUgdChYKQpICmBgYAoKCmBgYHtyfQpZIDwtIGRhdGEkeQpZaGF0IDwtIEglKiVZClloYXQKYGBgCgoKLSBXZSBjYW4gYWxzbyBpbnRlcnByZXQgdGhlIGZpdCBhcyB0aGUgcHJvamVjdGlvbiBvZiB0aGUgJG5cdGltZXMgMSQgdmVjdG9yICRcbWF0aGJme1l9JCBvbiB0aGUgY29sdW1uIHNwYWNlIG9mIHRoZSBtYXRyaXggJFxtYXRoYmZ7WH0kLgoKLSBTbyBlYWNoIGNvbHVtbiBpbiAkXG1hdGhiZntYfSQgaXMgYWxzbyBhbiAkblx0aW1lcyAxJCB2ZWN0b3IuCgotIEZvciB0aGUgdG95IGV4YW1wbGUgbj0zIGFuZCBwPTIuCiAgVGhlIG90aGVyIHBpY3R1cmUgdG8gbGluZWFyIHJlZ3Jlc3Npb24gaXMgdG8gY29uc2lkZXIgJFhfMCQsICRYXzEkIGFuZCAkWSQgYXMgdmVjdG9ycyBpbiB0aGUgc3BhY2Ugb2YgdGhlIGRhdGEgJFxtYXRoYmJ7Un1ebiQsIGhlcmUgJFxtYXRoYmJ7Un1eMyQgYmVjYXVzZSB3ZSBoYXZlIHRocmVlIGRhdGEgcG9pbnRzLgpTbyB0aGUgY29sdW1uIHNwYWNlIG9mIFggaXMgYSBwbGFuZSBpbiB0aGUgdGhyZWUgZGltZW5zaW9uYWwgc3BhY2UuCgpcWwpcaGF0e1xtYXRoYmZ7WX19ID0gXG1hdGhiZntYfSAoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfSBcbWF0aGJme1h9XlQgXG1hdGhiZntZfQpcXQoKMS4gUGxhbmUgc3Bhbm5lZCBieSBjb2x1bW4gc3BhY2U6ClRoZSBvdGhlciBwaWN0dXJlIHRvIGxpbmVhciByZWdyZXNzaW9uIGlzIHRvIGNvbnNpZGVyICRYXzAkLCAkWF8xJCBhbmQgJFkkIGFzIHZlY3RvcnMgaW4gdGhlIHNwYWNlIG9mIHRoZSBkYXRhICRcbWF0aGJie1J9Xm4kLCBoZXJlICRcbWF0aGJie1J9XjMkIGJlY2F1c2Ugd2UgaGF2ZSB0aHJlZSBkYXRhIHBvaW50cy4KCgpgYGB7cn0Kb3JpZ2luUm4gPC0gZGF0YS5mcmFtZShYMT0wLFgyPTAsWDM9MCkKZGF0YSR4MCA8LSAxCmRhdGFSbiA8LSBkYXRhLmZyYW1lKHQoZGF0YSkpCgpsaWJyYXJ5KHBsb3RseSkKCnAxIDwtIHBsb3RfbHkoCiAgICBvcmlnaW5SbiwKICAgIHggPSB+IFgxLAogICAgeSA9IH4gWDIsCiAgICB6PSB+IFgzLCBuYW1lPSJvcmlnaW4iKSAlPiUKICBhZGRfbWFya2Vycyh0eXBlPSJzY2F0dGVyM2QiKSAlPiUKICBsYXlvdXQoCiAgICBzY2VuZSA9IGxpc3QoCiAgICAgIGFzcGVjdG1vZGU9ImN1YmUiLAogICAgICB4YXhpcyA9IGxpc3QocmFuZ2U9YygtNCw0KSksIHlheGlzID0gbGlzdChyYW5nZT1jKC00LDQpKSwgemF4aXMgPSBsaXN0KHJhbmdlPWMoLTQsNCkpCiAgICAgICkKICAgICkKcDEgPC0gcDEgJT4lCiAgYWRkX3RyYWNlKAogICAgeCA9IGMoMCwxKSwKICAgIHkgPSBjKDAsMCksCiAgICB6ID0gYygwLDApLAogICAgbW9kZSA9ICJsaW5lcyIsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUsIGNvbG9yID0gImdyZXkiKSwKICAgIHR5cGU9InNjYXR0ZXIzZCIsCiAgICBuYW1lID0gIm9iczEiKSAlPiUKICBhZGRfdHJhY2UoCiAgICB4ID0gYygwLDApLAogICAgeSA9IGMoMCwxKSwKICAgIHogPSBjKDAsMCksCiAgICBtb2RlID0gImxpbmVzIiwKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSwgY29sb3IgPSAiZ3JleSIpLAogICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgIG5hbWUgPSAib2JzMiIpICU+JQogIGFkZF90cmFjZSgKICAgIHggPSBjKDAsMCksCiAgICB5ID0gYygwLDApLAogICAgeiA9IGMoMCwxKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJncmV5IiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZSA9ICJvYnMzIikgJT4lCiAgYWRkX3RyYWNlKAogICAgeCA9IGMoMCwxKSwKICAgIHkgPSBjKDAsMSksCiAgICB6ID0gYygwLDEpLAogICAgbW9kZSA9ICJsaW5lcyIsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUsIGNvbG9yID0gImJsYWNrIiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZSA9ICJYMSIpICU+JQogICAgYWRkX3RyYWNlKAogICAgeCA9IGMoMCwxKSwKICAgIHkgPSBjKDAsMiksCiAgICB6ID0gYygwLDMpLAogICAgbW9kZSA9ICJsaW5lcyIsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUsIGNvbG9yID0gImJsYWNrIiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZSA9ICJYMiIpCnAxCmBgYAoKCjIuIFZlY3RvciBvZiBZOgoKQWN0dWFsIHZhbHVlcyBvZiAkXG1hdGhiZntZfSQ6CgpgYGB7cn0KZGF0YSR5CmBgYAoKXFsKXG1hdGhiZntZfT1cbGVmdFtcYmVnaW57YXJyYXl9e2N9CmByIGRhdGEkeVsxXWAgXFwKYHIgZGF0YSR5WzJdYCBcXApgciBkYXRhJHlbM11gClxlbmR7YXJyYXl9XHJpZ2h0XQpcXQoKYGBge3J9CnAyIDwtIHAxICU+JQogIGFkZF90cmFjZSgKICAgIHggPSBjKDAsWVsxXSksCiAgICB5ID0gYygwLFlbMl0pLAogICAgeiA9IGMoMCxZWzNdKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJyZWQiKSwKICAgIHR5cGU9InNjYXR0ZXIzZCIsCiAgICBuYW1lID0gIlkiKQpwMgpgYGAKCjMuIFByb2plY3Rpb24gb2YgWSBvbnRvIGNvbHVtbiBzcGFjZQoKQWN0dWFsIHZhbHVlcyBvZiBmaXR0ZWQgdmFsdWVzICRcbWF0aGJme1xoYXR7WX19JDoKCmBgYHtyfQpkYXRhJHloYXQKYGBgCgpcWwpcbWF0aGJme1l9PVxsZWZ0W1xiZWdpbnthcnJheX17Y30KYHIgZGF0YSR5aGF0WzFdYCBcXApgciBkYXRhJHloYXRbMl1gIFxcCmByIGRhdGEkeWhhdFszXWAKXGVuZHthcnJheX1ccmlnaHRdClxdCgpgYGB7cn0KcDIgPC0gcDIgJT4lCiAgYWRkX3RyYWNlKAogICAgeCA9IGMoMCxZaGF0WzFdKSwKICAgIHkgPSBjKDAsWWhhdFsyXSksCiAgICB6ID0gYygwLFloYXRbM10pLAogICAgbW9kZSA9ICJsaW5lcyIsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUsIGNvbG9yID0gIm9yYW5nZSIpLAogICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgIG5hbWU9IlloYXQiKSAlPiUKICAgIGFkZF90cmFjZSgKICAgIHggPSBjKFlbMV0sWWhhdFsxXSksCiAgICB5ID0gYyhZWzJdLFloYXRbMl0pLAogICAgeiA9IGMoWVszXSxZaGF0WzNdKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJyZWQiLCBkYXNoPSJkYXNoIiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZT0iWSAtPiBZaGF0IgogICAgKQpwMgpgYGAKCiRcbWF0aGJme1l9JCBpcyBwcm9qZWN0ZWQgaW4gdGhlIGNvbHVtbiBzcGFjZSBvZiAkXG1hdGhiZntYfSQhIHNwYW5uZWQgYnkgdGhlIGNvbHVtbnMuCgojIyMjIEhvdyBkb2VzIHRoaXMgcHJvamVjdGlvbiB3b3Jrcz8KCiQkClxiZWdpbnthcnJheX17bGNsfQpcaGF0e1xtYXRoYmZ7WX19ICY9JiBcbWF0aGJme1h9IChcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntZfVxcCiY9JiBcbWF0aGJme1h9KFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMS8yfShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTEvMn1cbWF0aGJme1h9XlRcbWF0aGJme1l9XFwKJj0mIFxtYXRoYmZ7VX1cbWF0aGJme1V9XlRcbWF0aGJme1l9ClxlbmR7YXJyYXl9CiQkCgoKLSAkXG1hdGhiZntVfSQgaXMgYSBuZXcgb3J0aG9ub3JtYWwgYmFzaXMgaW4gJFxtYXRoYmJ7Un1eMiQsIGEgc3Vic3BhY2Ugb2YgJFxtYXRoYmJ7Un1eMyQKCi0gVGhlIHNwYWNlIHNwYW5uZWQgYnkgVSBhbmQgWCBpcyB0aGUgY29sdW1uIHNwYWNlIG9mIFgsIGUuZy4gaXQgY29udGFpbnMgYWxsIHBvc3NpYmxlIGxpbmVhciBjb21iaW5hbnRpb25zIG9mIFguCiRcbWF0aGJme1V9XnRcbWF0aGJme1l9JCBpcyB0aGUgcHJvamVjdGlvbiBvZiBZIG9uIHRoaXMgbmV3IG9ydGhvbm9ybWFsIGJhc2lzCgpgYGB7cn0KZWlnZW5YdFggPC0gZWlnZW4oWHRYKQpYdFhpbnZTcXJ0IDwtIGVpZ2VuWHRYJHZlY3RvcnMgJSolZGlhZygxL2VpZ2VuWHRYJHZhbHVlc14uNSklKiV0KGVpZ2VuWHRYJHZlY3RvcnMpClUgPC0gWCAlKiUgWHRYaW52U3FydApgYGAKCgotICRcbWF0aGJme1V9JCBvcnRob25vcm1hbCBiYXNpcwoKYGBge3J9ClUKdChVKSUqJVUKYGBgCgotICRcbWF0aGJme1VVfV5UJCBlcXVhbHMgcHJvamVjdGlvbiBtYXRyaXgKCmBgYHtyfQpVJSoldChVKQpICmBgYAoKCmBgYHtyfQpwMyA8LSBwMSAlPiUKICBhZGRfdHJhY2UoCiAgICB4ID0gYygwLFVbMSwxXSksCiAgICB5ID0gYygwLFVbMiwxXSksCiAgICB6ID0gYygwLFVbMywxXSksCiAgICBtb2RlID0gImxpbmVzIiwKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSwgY29sb3IgPSAiYmx1ZSIpLAogICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgIG5hbWUgPSAiVTEiKSAlPiUKICBhZGRfdHJhY2UoCiAgICB4ID0gYygwLFVbMSwyXSksCiAgICB5ID0gYygwLFVbMiwyXSksCiAgICB6ID0gYygwLFVbMywyXSksCiAgICBtb2RlID0gImxpbmVzIiwKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSwgY29sb3IgPSAiYmx1ZSIpLAogICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgIG5hbWUgPSAiVTIiKQoKcDMKYGBgCgoKLSAkXG1hdGhiZntVfV5UXG1hdGhiZntZfSQgaXMgdGhlIHByb2plY3Rpb24gb2YgJFxtYXRoYmZ7WX0kIGluIHRoZSBzcGFjZSBzcGFubmVkIGJ5ICRcbWF0aGJme1V9JC4KLSBJbmRlZWQgJFxtYXRoYmZ7VX1fMV5UXG1hdGhiZntZfSQKCmBgYHtyfQpwNCA8LSBwMyAlPiUKICBhZGRfdHJhY2UoCiAgICB4ID0gYygwLFlbMV0pLAogICAgeSA9IGMoMCxZWzJdKSwKICAgIHogPSBjKDAsWVszXSksCiAgICBtb2RlID0gImxpbmVzIiwKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSwgY29sb3IgPSAicmVkIiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZSA9ICJZIikgJT4lCiAgYWRkX3RyYWNlKAogICAgeCA9IGMoMCxVWzEsMV0qKFVbLDFdJSolWSkpLAogICAgeSA9IGMoMCxVWzIsMV0qKFVbLDFdJSolWSkpLAogICAgeiA9IGMoMCxVWzMsMV0qKFVbLDFdJSolWSkpLAogICAgbW9kZSA9ICJsaW5lcyIsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUsIGNvbG9yID0gInJlZCIsZGFzaD0iZGFzaCIpLAogICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgIG5hbWU9IlkgLT4gVTEiKSAlPiUgYWRkX3RyYWNlKAogICAgeCA9IGMoWVsxXSxVWzEsMV0qKFVbLDFdJSolWSkpLAogICAgeSA9IGMoWVsyXSxVWzIsMV0qKFVbLDFdJSolWSkpLAogICAgeiA9IGMoWVszXSxVWzMsMV0qKFVbLDFdJSolWSkpLAogICAgbW9kZSA9ICJsaW5lcyIsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUsIGNvbG9yID0gInJlZCIsIGRhc2g9ImRhc2giKSwKICAgIHR5cGU9InNjYXR0ZXIzZCIsCiAgICBuYW1lPSJZIC0+IFUxIikKcDQKYGBgCgotIGFuZCAkXG1hdGhiZntVfV8yXlRcbWF0aGJme1l9JApgYGB7cn0KcDUgPC0gcDQgJT4lCiAgYWRkX3RyYWNlKAogICAgeCA9IGMoMCxVWzEsMl0qKFVbLDJdJSolWSkpLAogICAgeSA9IGMoMCxVWzIsMl0qKFVbLDJdJSolWSkpLAogICAgeiA9IGMoMCxVWzMsMl0qKFVbLDJdJSolWSkpLAogICAgbW9kZSA9ICJsaW5lcyIsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUsIGNvbG9yID0gInJlZCIsZGFzaD0iZGFzaCIpLAogICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgIG5hbWU9IlkgLT4gVTIiKSAlPiUgYWRkX3RyYWNlKAogICAgeCA9IGMoWVsxXSxVWzEsMl0qKFVbLDJdJSolWSkpLAogICAgeSA9IGMoWVsyXSxVWzIsMl0qKFVbLDJdJSolWSkpLAogICAgeiA9IGMoWVszXSxVWzMsMl0qKFVbLDJdJSolWSkpLAogICAgbW9kZSA9ICJsaW5lcyIsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUsIGNvbG9yID0gInJlZCIsIGRhc2g9ImRhc2giKSwKICAgIHR5cGU9InNjYXR0ZXIzZCIsCiAgICBuYW1lPSJZIC0+IFUyIikKcDUKYGBgCgotIFloYXQgaXMgdGhlIHJlc3VsdGluZyB2ZWN0b3IgdGhhdCBsaWVzIGluIHRoZSBwbGFuZSBzcGFubmVkIGJ5ICRcbWF0aGJme1V9XzEkIGFuZCAkXG1hdGhiZntVfV8yJCBhbmQgdGh1cyBhbHNvIGluIHRoZSBjb2x1bW4gc3BhY2Ugb2YgJFxtYXRoYmZ7WH0kLgoKYGBge3J9CnA2IDwtIHA1ICU+JQogIGFkZF90cmFjZSgKICAgIHggPSBjKDAsWWhhdFsxXSksCiAgICB5ID0gYygwLFloYXRbMl0pLAogICAgeiA9IGMoMCxZaGF0WzNdKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJvcmFuZ2UiKSwKICAgIHR5cGU9InNjYXR0ZXIzZCIsCiAgICBuYW1lID0gIlloYXQiKSAlPiUKICBhZGRfdHJhY2UoCiAgICB4ID0gYyhZWzFdLFloYXRbMV0pLAogICAgeSA9IGMoWVsyXSxZaGF0WzJdKSwKICAgIHogPSBjKFlbM10sWWhhdFszXSksCiAgICBtb2RlID0gImxpbmVzIiwKICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gNSwgY29sb3IgPSAibWFyb29uMiIpLAogICAgdHlwZT0ic2NhdHRlcjNkIiwKICAgIG5hbWUgPSAiZSIpICU+JQogIGFkZF90cmFjZSgKICAgIHggPSBjKFVbMSwxXSooVVssMV0lKiVZKSxZaGF0WzFdKSwKICAgIHkgPSBjKFVbMiwxXSooVVssMV0lKiVZKSxZaGF0WzJdKSwKICAgIHogPSBjKFVbMywxXSooVVssMV0lKiVZKSxZaGF0WzNdKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJvcmFuZ2UiLCBkYXNoPSJkYXNoIiksCiAgICB0eXBlPSJzY2F0dGVyM2QiLAogICAgbmFtZSA9ICJZIC0+IFUiKSAgJT4lCiAgYWRkX3RyYWNlKAogICAgeCA9IGMoVVsxLDJdKihVWywyXSUqJVkpLFloYXRbMV0pLAogICAgeSA9IGMoVVsyLDJdKihVWywyXSUqJVkpLFloYXRbMl0pLAogICAgeiA9IGMoVVszLDJdKihVWywyXSUqJVkpLFloYXRbM10pLAogICAgbW9kZSA9ICJsaW5lcyIsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUsIGNvbG9yID0gIm9yYW5nZSIsIGRhc2g9ImRhc2giKSwKICAgIHR5cGU9InNjYXR0ZXIzZCIsCiAgICBuYW1lID0gIlkgLT4gVSIpCnA2CmBgYAoKIyMjIEVycm9yCgpOb3RlLCB0aGF0IGl0IGlzIGFsc28gY2xlYXIgZnJvbSB0aGUgZXF1YXRpb24gaW4gdGhlIGRlcml2YXRpb24gb2YgdGhlIGxlYXN0IHNxdWFyZXMgc29sdXRpb24gdGhhdCB0aGUgcmVzaWR1YWwgaXMgb3J0aG9nb25hbCBvbiB0aGUgY29sdW1uIHNwYWNlOgoKXFsKIC0yIFxtYXRoYmZ7WH1eVChcbWF0aGJme1l9LVxtYXRoYmZ7WH1cYm9sZHN5bWJvbHtcYmV0YX0pID0gMApcXQoKCiMjIyBDdXJzZSBvZiBkaW1lbnNpb25hbGl0eT8KCi0gSW1hZ2luZSB3aGF0IGhhcHBlbnMgd2hlbiBwIGFwcHJvYWNoZXMgbiAkcD1uJCBvciBiZWNvbWVzIG11Y2ggbGFyZ2VyIHRoYW4gcCA+PiBuISEhCgotIFN1cHBvc2UgdGhhdCB3ZSBhZGQgYSBwcmVkaWN0b3IgJFxtYXRoYmZ7WH1fMiA9IFsyLDAsMV1eVCQ/CgokJApcbWF0aGJme1l9PVxsZWZ0WwpcYmVnaW57YXJyYXl9e2N9CjFcXAoyXFwKMlxcClxlbmR7YXJyYXl9XHJpZ2h0XSwKXHF1YWQKXG1hdGhiZntYfT0gXGxlZnRbClxiZWdpbnthcnJheX17Y2NjfQoxJjEmMlxcCjEmMiYwXFwKMSYzJjFcXApcZW5ke2FycmF5fQpccmlnaHRdLApccXVhZCBcYm9sZHN5bWJvbHtcYmV0YX0gPSBcbGVmdFsKXGJlZ2lue2FycmF5fXtjfQpcYmV0YV8wXFwKXGJldGFfMVxcClxiZXRhXzIKXGVuZHthcnJheX0KXHJpZ2h0XQpccXVhZApcdGV4dHthbmR9ClxxdWFkClxib2xkc3ltYm9se1xlcHNpbG9ufT0KXGxlZnRbClxiZWdpbnthcnJheX17Y30KXGVwc2lsb25fMVxcClxlcHNpbG9uXzJcXApcZXBzaWxvbl8zClxlbmR7YXJyYXl9ClxyaWdodF0KJCQKCgpgYGB7cn0KZGF0YSR4MiA8LSBjKDIsMCwxKQpmaXQgPC0gbG0oeX54K3gyLGRhdGEpCiMgcHJlZGljdCB2YWx1ZXMgb24gcmVndWxhciB4eSBncmlkCngxcHJlZCA8LSBzZXEoLTEsIDQsIGxlbmd0aC5vdXQgPSAxMCkKeDJwcmVkIDwtIHNlcSgtMSwgNCwgbGVuZ3RoLm91dCA9IDEwKQp4eSA8LSBleHBhbmQuZ3JpZCh4ID0geDFwcmVkLAp4MiA9IHgycHJlZCkKeXByZWQgPC0gbWF0cml4IChucm93ID0gMzAsIG5jb2wgPSAzMCwKZGF0YSA9IHByZWRpY3QoZml0LCBuZXdkYXRhID0gZGF0YS5mcmFtZSh4eSkpKQoKbGlicmFyeShwbG90M0QpCgoKIyBmaXR0ZWQgcG9pbnRzIGZvciBkcm9wbGluZXMgdG8gc3VyZmFjZQp0aD0yMApwaD01CnNjYXR0ZXIzRChkYXRhJHgsCiAgZGF0YSR4MiwKICBZLAogIHBjaCA9IDE2LAogIGNvbD0iZGFya2JsdWUiLAogIGNleCA9IDEsCiAgdGhldGEgPSB0aCwKICB0aWNrdHlwZSA9ICJkZXRhaWxlZCIsCiAgeGxhYiA9ICJ4MSIsCiAgeWxhYiA9ICJ4MiIsCiAgemxhYiA9ICJ5IiwKICBjb2x2YXI9RkFMU0UsCiAgYnR5ID0gImciLAogIHhsaW09YygtMSwzKSwKICB5bGltPWMoLTEsMyksCiAgemxpbT1jKC0yLDQpKQoKCnoucHJlZDNEIDwtIG91dGVyKAogIHgxcHJlZCwKICB4MnByZWQsCiAgZnVuY3Rpb24oeDEseDIpCiAgewogICAgZml0JGNvZWZbMV0gKyBmaXQkY29lZlsyXSp4MStmaXQkY29lZlsyXSp4MgogIH0pCgp4LnByZWQzRCA8LSBvdXRlcigKICB4MXByZWQsCiAgeDJwcmVkLAogIGZ1bmN0aW9uKHgseSkgeCkKCnkucHJlZDNEIDwtIG91dGVyKAogIHgxcHJlZCwKICB4MnByZWQsCiAgZnVuY3Rpb24oeCx5KSB5KQoKc2NhdHRlcjNEKGRhdGEkeCwKICBkYXRhJHgyLAogIGRhdGEkeSwKICBwY2ggPSAxNiwKICBjb2w9ImRhcmtibHVlIiwKICBjZXggPSAxLAogIHRoZXRhID0gdGgsCiAgdGlja3R5cGUgPSAiZGV0YWlsZWQiLAogIHhsYWIgPSAieDEiLAogIHlsYWIgPSAieDIiLAogIHpsYWIgPSAieSIsCiAgY29sdmFyPUZBTFNFLAogIGJ0eSA9ICJnIiwKICB4bGltPWMoLTEsNCksCiAgeWxpbT1jKC0xLDQpLAogIHpsaW09YygtMiw0KSkKCnN1cmYzRCgKICB4LnByZWQzRCwKICB5LnByZWQzRCwKICB6LnByZWQzRCwKICBjb2w9ImJsdWUiLAogIGZhY2V0cz1OQSwKICBhZGQ9VFJVRSkKYGBgCgpOb3RlLCB0aGF0IHRoZSBsaW5lYXIgcmVncmVzc2lvbiBpcyBub3cgYSBwbGFuZS4KCkhvd2V2ZXIsIHdlIG9idGFpbiBhIHBlcmZlY3QgZml0IGFuZCBhbGwgdGhlIGRhdGEgcG9pbnRzIGFyZSBmYWxsaW5nIGluIHRoZSBwbGFuZSEgYHIgc2V0LnNlZWQoNCk7ZW1vOjpqaSgiZmVhciIpYAoKVGhpcyBpcyBvYnZpb3VzIGlmIHdlIGxvb2sgYXQgdGhlIGNvbHVtbiBzcGFjZSBvZiBYIQoKYGBge3J9ClggPC0gY2JpbmQoWCxjKDIsMCwxKSkKWHRYIDwtIHQoWCklKiVYCmVpZ2VuWHRYIDwtIGVpZ2VuKFh0WCkKWHRYaW52U3FydCA8LSBlaWdlblh0WCR2ZWN0b3JzICUqJWRpYWcoMS9laWdlblh0WCR2YWx1ZXNeLjUpJSoldChlaWdlblh0WCR2ZWN0b3JzKQpVIDwtIFggJSolIFh0WGludlNxcnQKCnA3IDwtIHAxICU+JQogIGFkZF90cmFjZSgKICAgIHggPSBjKDAsMiksCiAgICB5ID0gYygwLDApLAogICAgeiA9IGMoMCwxKSwKICAgIG1vZGUgPSAibGluZXMiLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1LCBjb2xvciA9ICJkYXJrZ3JlZW4iKSwKICAgIHR5cGU9InNjYXR0ZXIzZCIsCiAgICBuYW1lID0gIlgzIikKcDcKYGBgCgpgYGB7cn0KcDggPC0gcDcgJT4lCiAgYWRkX3RyYWNlKAogICAgeCA9IGMoMCxVWzEsMV0pLAogICAgeSA9IGMoMCxVWzIsMV0pLAogICAgeiA9IGMoMCxVWzMsMV0pLAogICAgbW9kZSA9ICJsaW5lcyIsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUsIGNvbG9yID0gImJsdWUiKSwKICAgIHR5cGU9InNjYXR0ZXIzZCIsCiAgICBuYW1lID0gIlUxIikgJT4lCiAgYWRkX3RyYWNlKAogICAgeCA9IGMoMCxVWzEsMl0pLAogICAgeSA9IGMoMCxVWzIsMl0pLAogICAgeiA9IGMoMCxVWzMsMl0pLAogICAgbW9kZSA9ICJsaW5lcyIsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUsIGNvbG9yID0gImJsdWUiKSwKICAgIHR5cGU9InNjYXR0ZXIzZCIsCiAgICBuYW1lID0gIlUyIikgJT4lCiAgYWRkX3RyYWNlKAogICAgeCA9IGMoMCxVWzEsM10pLAogICAgeSA9IGMoMCxVWzIsM10pLAogICAgeiA9IGMoMCxVWzMsM10pLAogICAgbW9kZSA9ICJsaW5lcyIsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUsIGNvbG9yID0gImJsdWUiKSwKICAgIHR5cGU9InNjYXR0ZXIzZCIsCiAgICBuYW1lID0gIlUzIikKCnA4CmBgYAoKLSBUaGUgY29sdW1uIHNwYWNlIG5vdyBzcGFucyB0aGUgZW50aXJlICAkXG1hdGhiYntSfV4zJCEKCi0gV2l0aCB0aGUgaW50ZXJjZXB0IGFuZCB0aGUgdHdvIHByZWRpY3RvcnMgd2UgY2FuIHRodXMgZml0IGV2ZXJ5IGRhdGFzZXQgdGhhdCBvbmx5IGhhcyAzIG9ic2VydmF0aW9ucyBmb3IgdGhlIHByZWRpY3RvcnMgYW5kIHRoZSByZXNwb25zZS4KCi0gU28gdGhlIG1vZGVsIGNhbiBubyBsb25nZXIgYmUgdXNlZCB0byBnZW5lcmFsaXNlIHRoZSBwYXR0ZXJucyBzZWVuIGluIHRoZSBkYXRhIHRvd2FyZHMgdGhlIHBvcHVsYXRpb24gKG5ldyBvYnNlcnZhdGlvbnMpLgoKLSBQcm9ibGVtIG9mIG92ZXJmaXR0aW5nISEhCgotIElmICRwID4+IG4kIHRoZW4gdGhlIHByb2JsZW0gZ2V0cyBldmVuIHdvcnNlISBUaGVuIHRoZXJlIGlzIGV2ZW4gbm8gbG9uZ2VyIGEgdW5pcXVlIHNvbHV0aW9uIHRvIHRoZSBsZWFzdCBzcXVhcmVzIHByb2JsZW0uLi4KCi0gSW5kZWVkLCB0aGVuIHdlIGhhdmUgbW9yZSB2ZWN0b3JzL2RpbWVuc2lvbnMvY29sdW1ucyBpbiBYICB0aGFuIGRhdGFwb2ludHMhCgoKIyMgVmFyaWFuY2UgRXN0aW1hdG9yPwpcWwpcYmVnaW57YXJyYXl9e2NjbH0KXGhhdHtcYm9sZHN5bWJvbHtcU2lnbWF9fV97XGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19fQomPSZcdGV4dHt2YXJ9XGxlZnRbKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cbWF0aGJme1h9XlRcbWF0aGJme1l9XHJpZ2h0XVxcXFwKJj0mKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cbWF0aGJme1h9XlRcdGV4dHt2YXJ9XGxlZnRbXG1hdGhiZntZfVxyaWdodF1cbWF0aGJme1h9KFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX0gXHRleHR7KCopfVxcXFwKJj0mKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cbWF0aGJme1h9XlQoXG1hdGhiZntJfVxzaWdtYV4yKVxtYXRoYmZ7WH0oXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfQpcXFxcCiY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntJfVxxdWFkXG1hdGhiZntYfShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XHNpZ21hXjJcXFxcCiVcaGF0e1xib2xkbWF0aHtcU2lnbWF9fV97XGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19fSY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UIFx0ZXh0e3Zhcn1cbGVmdFtcbWF0aGJme1l9XHJpZ2h0XShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfVxcCiY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntYfShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XHNpZ21hXjJcXFxcCiY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XHNpZ21hXjIKXGVuZHthcnJheX0KXF0KCigqKSBVbmRlciBhc3N1bXB0aW9uIHRoYXQgYWxsIG9ic2VydmF0aW9ucyAkXG1hdGhiZntZfSQgYXJlIGluZGVwZW5kZW50IGFuZCBpZGVudGljYWxseSBkaXN0cmlidXRlZC4KClRoZSBmYWN0IHRoYXQgJFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fSQgaXMgdW5iaWFzZWQgYW5kIGhhcyBhIHZhcmlhbmNlIG9mICQoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxzaWdtYV4yJCB3aWxsIGJlIGltcG9ydGFudCB3aGVuIGFzc2Vzc2luZyBhc3NvY2lhdGlvbiEKCi0tLQoKIyMgUHJlZGljdGlvbiBlcnJvcgoKTGVhc3Qgc3F1YXJlcyBlc3RpbWF0b3JzIGFyZSB1bmJpYXNlZCBhbmQgY29uc2lzdGVudCwgYnV0IHRoZXNlIHByb3BlcnRpZXMgYXJlIG5vdCB2ZXJ5IGltcG9ydGFudCBmb3IgcHJlZGljdGlvbiBtb2RlbHMuCgpBIHByZWRpY3Rpb24gbW9kZWwgaXMgY29uc2lkZXJlZCBnb29kIGlmIGl0IGNhbiBwcmVkaWN0IHdlbGwgb3V0Y29tZXMuCgpUaGUgKipwcmVkaWN0aW9uIGVycm9yKiogZm9yIGEgcHJlZGljdGlvbiBhdCBwcmVkaWN0b3IgJFxtYXRoYmZ7eH0kIGlzIGdpdmVuIGJ5CgokJAogICBcaGF0e1l9KFxtYXRoYmZ7eH0pIC0gWV4qLAokJAoKd2hlcmUKCi0gICRcaGF0e1l9KFxtYXRoYmZ7eH0pPVxtYXRoYmZ7eH1eVFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fSQgaXMgdGhlIHByZWRpY3Rpb24gYXQgJFxtYXRoYmZ7eH0kCgotICAkWV4qJCBpcyBhbiBvdXRjb21lIGF0IHByZWRpY3RvciAkXG1hdGhiZnt4fSQKCiBTaW5jZSBwcmVkaWN0aW9uIGlzIHR5cGljYWxseSB1c2VkIHRvIHByZWRpY3QgYW4gb3V0Y29tZSBiZWZvcmUgaXQgaXMgb2JzZXJ2ZWQsIHRoZSBvdXRjb21lICRZXiokIGlzIG5vdCBvYnNlcnZlZCB5ZXQuIEhlbmNlLCB0aGUgcHJlZGljdGlvbiBlcnJvciBjYW5ub3QgYmUgY29tcHV0ZWQuCgogLS0tCgogVGhlIHByb2JsZW0gb2YgdW5vYnNlcnZhYmxlIHByZWRpY3Rpb24gZXJyb3JzIGlzIHBhcnRseSBzb2x2ZWQgYnkgdGhlICoqZXhwZWN0ZWQgY29uZGl0aW9uYWwgdGVzdCBlcnJvcioqIChzb21ldGltZXMgcmVmZXJyZWQgdG8gYXMgdGhlIG1lYW4gc3F1YXJlZCBlcnJvciwgTVNFKQpcWwogIFx0ZXh0e0Vycn0oXG1hdGhiZnt4fSk9RVsoXGhhdHtZfShcbWF0aGJme3h9KSAtIFleKileMl0uClxdCgpXaXRoIChzdXBwcmVzc2luZyB0aGUgZGVwZW5kZW5jZSBvbiAkXG1hdGhiZnt4fSQpClxbCiAgXG11ID0gRVtcaGF0e1l9XSBcdGV4dHsgYW5kIH0gXG11Xio9RVtZXipdClxdCnRoZSBlcnJvciBjYW4gYmUgZXhwcmVzc2VkIGFzClxiZWdpbntlcW5hcnJheSp9CiAgXHRleHR7RXJyfQogICAgJj0mRVxsZWZ0XHtcbGVmdFsoXGhhdHtZfSAtXG11KS0oWV4qLVxtdV4qKS0oXG11XiotXG11KVxyaWdodF1eMlxyaWdodFx9IFxcCiAgICAmPSYgRVsoXGhhdHtZfSAtXG11KV4yXStFWyhZXiotXG11XiopXjJdK0VbKFxtdV4qLVxtdSleMl0gXFwKICAgICY9JiBcdGV4dHt2YXJ9W1xoYXR7WX1dICtcdGV4dHt2YXJ9W1leKl0gK1x0ZXh0e2JpYXN9XjIKXGVuZHtlcW5hcnJheSp9CgogVGhlIHRlcm0gJFx0ZXh0e3Zhcn1bWV4qXSQgKGlycmVkdWNpYmxlIGVycm9yKSBkb2VzIG5vdCBkZXBlbmQgb24gdGhlIG1vZGVsIGFuZCBtYXkgdGhlcmVmb3JlIGJlIGlnbm9yZWQgd2hlbiAkXHRleHR7RXJyfSQgaXMgdXNlZCBmb3IgY29tcGFyaW5nIHByZWRpY3Rpb24gbW9kZWxzLgoKIC0tLQoKCkluIHRoaXMgaW50cm9kdWN0b3J5IGNoYXB0ZXIgd2Ugb25seSBhaW0gdG8gZ2l2ZSBhIHJvdWdoIGRpc2N1c3Npb24gb24gcHJlZGljdGlvbiBlcnJvcnMuIExhdGVyIGRlZmluaXRpb25zIHdpbGwgYmUgcmVmaW5lZCBhbmQgdGhlIG5vdGF0aW9uIHdpbGwgYmUgbW9yZSBhY2N1cmF0ZS4gQWxzbyBhIG1vcmUgZGV0YWlsZWQgZGlzY3Vzc2lvbiBvbiB0aGUgYmlhcy12YXJpYW5jZSB0cmFkZS1vZmYgd2lsbCBmb2xsb3cuIEZvciB0aGUgbW9tZW50IGl0IGlzIHN1ZmZpY2llbnQgdG8gdmFndWVseSBrbm93IHRoYXQ6CgotIHRoZSBleHBlY3RlZCBjb25kaXRpb25hbCB0ZXN0IGVycm9yIGlzIGludHJvZHVjZWQgdG8gY2lyY3VtdmVudCB0aGUgcHJvYmxlbSB0aGF0IHRoZSBwcmVkaWN0aW9uIGVycm9yIGNhbm5vdCBiZSBvYnNlcnZlZC4gSW4gbGF0ZXIgY2hhcHRlcnMgd2Ugd2lsbCBsb29rIGF0IGVzdGltYXRvcnMgb2YgdGhlIGV4cGVjdGVkIGVycm9yLgoKLSB0aGUgZXhwZWN0ZWQgY29uZGl0aW9uYWwgdGVzdCBlcnJvciAgaXMgaW4gc29tZSBsaXRlcmF0dXJlIGFsc28ga25vd24gYXMgdGhlICptZWFuIHNxdWFyZWQgZXJyb3IqIChNU0UpLCBidXQgd2UgZG8gbm90IGFkb3B0IHRoaXMgdGVybWlub2xvZ3kgYmVjYXVzZSBNU0UgaXMgYWxzbyBjb21tb25seSB1c2VkIHRvIHJlZmVyIHRvIFNTRSBkaXZpZGVkIGJ5IHRoZSByZXNpZHVhbCBkZWdyZWVzIG9mIGZyZWVkb20gaW4gYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbC4KCi0gVGhlIGlkZW50aXR5ICRcdGV4dHtFcnJ9PVx0ZXh0e3Zhcn1bXGhhdHtZfV0gK1x0ZXh0e3Zhcn1bWV0gK1x0ZXh0e2JpYXN9XjIkIGlzIGtub3duIGFzIHRoZSBiaWFzLXZhcmlhbmNlIHRyYWRlLW9mZi4gSXQgc2hvd3MgdGhhdCBhIGdvb2QgcHJlZGljdGlvbiBtb2RlbCAoaS5lLiBhIG1vZGVsIHJlc3VsdGluZyBpbiBhIHNtYWxsIEVyciksIGNhbiBiZSBvYnRhaW5lZCBieSBhIG1vZGVsIHRoYXQgc2hvd3MgYSBzbWFsbCBiaWFzIGFzIGxvbmcgYXMgdGhpcyBiaWFzIGlzIGNvbXBlbnNhdGVkIHdpdGggYSBsYXJnZSByZWR1Y3Rpb24gb2YgdGhlIHZhcmlhbmNlIG9yIHRoZSBwcmVkaWN0aW9ucy4gQSBtb3JlIGRldGFpbGVkIGRpc2N1c3Npb24gd2lsbCBmb2xsb3cgaW4gbGF0ZXIgY2hhcHRlcnMuCgotIEZvciBwcmVkaWN0aW9uIG1vZGVscyB3aXRoIGEgbGFyZ2UgbnVtYmVyIG9mIHByZWRpY3RvcnMgd2Ugd2lsbCB0aGVyZWZvcmUgaW50cm9kdWNlIHBlbmFsaXplZCByZWdyZXNzaW9uLiBUaGlzIHdpbGwgaW5kdWNlIHNvbWUgYmlhcyBpbiB0aGUgZXN0aW1hdGlvbiwgYnV0IHdpbGwgYWxsb3cgdXMgdG8gcmVkdWNlIHRoZSB2YXJpYW5jZSBjb25zaWRlcmFibHkuCgpgYGB7ciwgY2hpbGQ9Il9zZXNzaW9uLWluZm8uUm1kIn0KYGBgCg==