1 Introduction

1.1 Prediction with High Dimensional Predictors

General setting:

  • Aim: build a prediction model that gives a prediction of an outcome for a given set of predictors.

  • We use \(X\) to refer to the predictors and \(Y\) to refer to the outcome.

  • A training data set is available, say \((\mathbf{X},\mathbf{Y})\). It contains \(n\) observations on outcomes and on \(p\) predictors.

  • Using the training data, a prediction model is build, say \(\hat{m}(\mathbf{X})\). This typically involves model building (feature selection) and parameter estimation.

  • During the model building, potential models need to be evaluated in terms of their prediction quality.

1.2 Example: Toxicogenomics in early drug development

1.2.1 Background

  • Effect of compound on gene expression.

  • Insight in action and toxicity of drug in early phase

  • Determine activity with bio-assay: e.g. binding affinity of compound to cell wall receptor (target, IC50).

  • Early phase: 20 to 50 compounds

  • Based on in vitro results one aims to get insight in how to build better compound (higher on-target activity less toxicity.

  • Small variations in molecular structure lead to variations in BA and gene expression.

  • Aim: Build model to predict bio-activity based on gene expression in liver cell line.

1.2.2 Data

  • 30 chemical compounds have been screened for toxicity

  • Bioassay data on toxicity screening

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

toxData <- read_csv(
  "https://raw.githubusercontent.com/statOmics/HDA2020/data/toxDataCentered.csv",
  col_types = cols()
)
svdX <- svd(toxData[,-1])

Data is already centered:

toxData %>%
  colMeans %>%
  range
#> [1] -2.405483e-17  5.921189e-17
 toxData %>%
  names %>%
  head
#> [1] "BA" "X1" "X2" "X3" "X4" "X5"
  • First column contains data on Bioassay.
  • The higher the score on Bioassay the more toxic the compound
  • Other columns contain data on gene expression X1, … , X4000

1.2.3 Data exploration

toxData %>%
  ggplot(aes(x="",y=BA)) +
  geom_boxplot(outlier.shape=NA) +
  geom_point(position="jitter")

svdX <- toxData[,-1] %>%
  svd

k <- 2
Vk <- svdX$v[,1:k]
Uk <- svdX$u[,1:k]
Dk <- diag(svdX$d[1:k])
Zk <- Uk%*%Dk
colnames(Zk) <- paste0("Z",1:k)
colnames(Vk) <- paste0("V",1:k)

Zk %>%
  as.data.frame %>%
  mutate(BA = toxData %>% pull(BA)) %>%
  ggplot(aes(x= Z1, y = Z2, color = BA)) +
  geom_point(size = 3) +
  scale_colour_gradient2(low = "blue",mid="white",high="red") +
  geom_point(size = 3, pch = 21, color = "black")

  • Scores on the first two principal components (or MDS plot).
  • Each point corresponds to a compound.
  • Color code refers to the toxicity score (higher score more toxic).
  • Clear separation between compounds according to toxicity.

  • Next logic step in a PCA is to interpret the principal components.
  • We thus have to assess the loadings.
  • We can add a vector for each gene to get a biplot, but this would require plotting 4000 vectors, which would render the plot unreadable.

Alternative graph to look at the many loadings of the first two PCs.

grid.arrange(
  Vk %>%
    as.data.frame %>%
    mutate(geneID = 1:nrow(Vk)) %>%
    ggplot(aes(x = geneID, y = V1)) +
    geom_point(pch=21) +
    geom_hline(yintercept = c(-2,0,2)*sd(Vk[,1]), col = "red") ,
  Vk %>%
    as.data.frame %>%
    mutate(geneID = 1:nrow(Vk)) %>%
    ggplot(aes(x = geneID, y = V2)) +
    geom_point(pch=21) +
    geom_hline(yintercept = c(-2,0,2)*sd(Vk[,2]), col = "red"),
  ncol=2)

  • It is almost impossible to interpret the PCs because there are 4000 genes contributing to each PC.

  • In an attempt to find the most important genes (in the sense that they drive the interpretation of the PCs), the plots show horizontal reference lines: the average of the loadings, and the average ± twice the standard deviation of the loadings. In between the lines we expects about 95% of the loadings (if they were normally distributed).

  • The points outside the band come from the genes that have rather large loadings (in absolute value) and hence are important for the interpretation of the PCs.

  • Note, that particularly for the first PC, only a few genes show a markedly large loadings that are negative. This means that an upregulation of these genes will lead to low scores on PC1.

  • These genes will very likely play an important role in the toxicity mechanism.

  • Indeed, low scores on PC1 are in the direction of more toxicity.

  • In the next chapter we will introduce a method to obtain sparse PCs.

1.2.4 Prediction model

m1 <- lm(BA ~ -1 + ., toxData)

m1 %>%
  coef %>%
  head(40)
#>          X1          X2          X3          X4          X5          X6 
#>  -7.4569940   0.3571348  11.2492315  10.8354021 -13.7433891   5.6833874 
#>          X7          X8          X9         X10         X11         X12 
#>  65.5387777   4.3404555   7.9103924  37.0296057 -54.8368698 -55.5547845 
#>         X13         X14         X15         X16         X17         X18 
#>   5.7924667  23.1428002  -6.9610365 -28.5250571 -22.5509025 -97.9623731 
#>         X19         X20         X21         X22         X23         X24 
#> -30.4171782 -32.6991673 -14.2808834 -16.1431266 -22.7498681  73.1635178 
#>         X25         X26         X27         X28         X29         X30 
#>  -5.7065827  37.4745379 -20.1999102  14.9906821  99.6080955          NA 
#>         X31         X32         X33         X34         X35         X36 
#>          NA          NA          NA          NA          NA          NA 
#>         X37         X38         X39         X40 
#>          NA          NA          NA          NA
m1 %>%
  coef %>%
  is.na %>%
  sum
#> [1] 3971
summary(m1)$r.squared
#> [1] 1

Problem??

1.3 Brain example

  • Courtesy to Solomon Kurz. Statistical rethinking with brms, ggplot2, and the tidyverse version 1.2.0.

https://bookdown.org/content/3890/ https://github.com/ASKurz/Statistical_Rethinking_with_brms_ggplot2_and_the_tidyverse

  • Data with brain size and body size for seven species
brain <-
tibble(species = c("afarensis", "africanus", "habilis", "boisei", "rudolfensis", "ergaster", "sapiens"),
       brain   = c(438, 452, 612, 521, 752, 871, 1350),
       mass    = c(37.0, 35.5, 34.5, 41.5, 55.5, 61.0, 53.5))

1.3.1 Data exploration

brain
p <- brain %>%
  ggplot(aes(x =  mass, y = brain, label = species)) +
  geom_point()

p + geom_text(nudge_y = 40)

1.3.2 Models

Six models range in complexity from the simple univariate model

\[\begin{align*} \text{brain}_i & \sim \operatorname{Normal} (\mu_i, \sigma) \\ \mu_i & = \beta_0 + \beta_1 \text{mass}_i, \end{align*}\]

to the dizzying sixth-degree polynomial model

\[\begin{align*} \text{brain}_i & \sim \operatorname{Normal} (\mu_i, \sigma) \\ \mu_i & = \beta_0 + \beta_1 \text{mass}_i + \beta_2 \text{mass}_i^2 + \beta_3 \text{mass}_i^3 + \beta_4 \text{mass}_i^4 + \beta_5 \text{mass}_i^5 + \beta_6 \text{mass}_i^6. \end{align*}\]

formulas <- sapply(1:6, function(i)
  return(
     paste0("I(mass^",1:i,")") %>% paste(collapse=" + ")
    )
)

formulas <- sapply(
  paste0("brain ~ ", formulas),
  as.formula)

models <- lapply(formulas, lm , data = brain)
data.frame(
  formula=formulas %>%
    as.character,
  r2 = sapply(
    models,
    function(mod) summary(mod)$r.squared)
  )  %>%
  ggplot(
    aes(x = r2,
      y = formula,
      label = r2 %>%
        round(2) %>%
        as.character)
  ) +
  geom_text()

We plot the fit for each model individually and them arrange them together in one plot.

plots <- lapply(1:6, function(i)
{
  p +
  geom_smooth(method = "lm", formula = y ~ poly(x,i)) +
  ggtitle(
    paste0(
      "r2 = ",
      round(summary(models[[i]])$r.squared*100,1),
      "%")
    )
})

do.call("grid.arrange",c(plots, ncol = 3))

  • We clearly see that increasing the model complexity always produces a fit with a smaller SSE.

  • The problem of overfitting is very obvious. The more complex polynomial models will not generalise well for prediction!

  • We even have a model that fits the data perfectly, but that will make very absurd preditions!

  • Too few parameters hurts, too. Fit the underfit intercept-only model.

m0 <- lm(brain ~ 1, brain)
summary(m0)
#> 
#> Call:
#> lm(formula = brain ~ 1, data = brain)
#> 
#> Residuals:
#>     Min      1Q  Median      3Q     Max 
#> -275.71 -227.21 -101.71   97.79  636.29 
#> 
#> Coefficients:
#>             Estimate Std. Error t value Pr(>|t|)   
#> (Intercept)    713.7      121.8    5.86  0.00109 **
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 322.2 on 6 degrees of freedom
p +
  stat_smooth(method = "lm", formula = y ~ 1) +
  ggtitle(
    paste0(
      "r2 = ",
      round(summary(m0)$r.squared*100,1),
      "%")
    )

The underfit model did not learn anything about the relation between mass and brain. It would also do a very poor job for predicting new data.

1.4 Overview

We will make a distinction between continuous and discrete outcomes. In this course we focus on

  • Linear regression models for continous outcomes

    • Penalised regression: Lasso and ridge
    • Principal component regression (PCR)
  • Logistic regression models for binary outcomes

    • Penalised regression: Lasso and ridge

For all types of model, we will discuss feature selection methods.

2 Linear Regression for High Dimensional Data

Consider linear regression model (for double centered data) \[ Y_i = \beta_1X_{i1} + \beta_2 X_{i2} + \cdots + \beta_pX_{ip} + \epsilon_i , \] with \(\text{E}\left[\epsilon \mid \mathbf{X}\right]=0\) and \(\text{var}\left[\epsilon \mid \mathbf{X}\right]=\sigma^2\).

In matrix notation the model becomes \[ \mathbf{Y} = \mathbf{X}\mathbf\beta + \mathbf\epsilon. \] The least squares estimator of \(\mathbf\beta\) is given by \[ \hat{\mathbf\beta} = (\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{Y} , \] and the variance of \(\hat{\mathbf\beta}\) equals \[ \text{var}\left[\hat{\mathbf\beta}\right] = (\mathbf{X}^T\mathbf{X})^{-1}\sigma^2. \] \(\longrightarrow\) the \(p \times p\) matrix \((\mathbf{X}^T\mathbf{X})^{-1}\) is crucial

Note, that

  • with double centered data it is meant that both the responses are centered (mean of \(\mathbf{Y}\) is zero) and that all predictors are centered (columns of \(\mathbf{X}\) have zero mean). With double centered data the intercept in a linear regression model is always exactly equal to zero and hence the intercept must not be included in the model.

  • we do not assume that the residuals are normally distributed. For prediction purposes this is often not required (normality is particularly important for statistical inference in small samples).

2.1 Linear Regression for multivariate data vs High Dimensional Data

  • \(\mathbf{X^TX}\) and \((\mathbf{X^TX})^{-1}\) are \(p \times p\) matrices

  • \(\mathbf{X^TX}\) can only be inverted if it has rank \(p\)

  • Rank of a matrix of form \(\mathbf{X^TX}\), with \(\mathbf{X}\) and \(n\times p\) matrix, can never be larger than \(\min(n,p)\).

  • in most regression problems \(n>p\) and rank of \((\mathbf{X^TX})\) equals \(p\)

  • in high dimensional regression problems \(p >>> n\) and rank of \((\mathbf{X^TX})\) equals \(n<p\)

  • in the toxicogenomics example \(n=30<p=4000\) and \(\text{rank}(\mathbf{X^TX})\leq n=30\). \(\longrightarrow\) \((\mathbf{X^TX})^{-1}\) does not exist, and neither does \(\hat{\boldsymbol{\beta}}\).

2.2 Can SVD help?

  • Since the columns of \(\mathbf{X}\) are centered, \(\mathbf{X^TX} \propto \text{var}\left[\mathbf{X}\right]\).

  • if \(\text{rank}(\mathbf{X^TX})=n=30\), the PCA will give 30 components, each being a linear combination of \(p=4000\) variables. These 30 PCs contain all information present in the original \(\mathbf{X}\) data.

  • if \(\text{rank}(\mathbf{X})=n=30\), the SVD of \(\mathbf{X}\) is given by \[ \mathbf{X} = \sum_{i=1}^n \delta_i \mathbf{u}_i \mathbf{v}_i^T = \mathbf{U} \boldsymbol{\Delta} \mathbf{V}^T = \mathbf{ZV}^T, \] with \(\mathbf{Z}\) the \(n\times n\) matrix with the scores on the \(n\) PCs.

  • Still problematic because if we use all PCs \(n=p\).

3 Principal Component Regression

A principal component regression (PCR) consists of

  1. transforming \(p=4000\) dimensional \(X\)-variable to the \(n=30\) dimensional \(Z\)-variable (PC scores). The \(n\) PCs are mutually uncorrelated.

  2. using the \(n\) PC-variables as regressors in a linear regression model

  3. performing feature selection to select the most important regressors (PC).

Feature selection is key, because we don’t want to have as many regressors as there are observations in the data. This would result in zero residual degrees of freedom. (see later)


To keep the exposition general so that we allow for a feature selection to have taken place, I use the notation \(\mathbf{U}_S\) to denote a matrix with left-singular column vectors \(\mathbf{u}_i\), with \(i \in {\cal{S}}\) (\({\cal{S}}\) an index set referring to the PCs to be included in the regression model).

For example, suppose that a feature selection method has resulted in the selection of PCs 1, 3 and 12 for inclusion in the prediction model, then \({\cal{S}}=\{1,3,12\}\) and \[ \mathbf{U}_S = \begin{pmatrix} \mathbf{u}_1 & \mathbf{u}_3 & \mathbf{u}_{12} \end{pmatrix}. \]


3.0.1 Example model based on first 4 PCs

k <- 30
Uk <- svdX$u[,1:k]
Dk <- diag(svdX$d[1:k])
Zk <- Uk%*%Dk
Y <- toxData %>%
  pull(BA)

m4 <- lm(Y~Zk[,1:4])
summary(m4)
#> 
#> Call:
#> lm(formula = Y ~ Zk[, 1:4])
#> 
#> Residuals:
#>     Min      1Q  Median      3Q     Max 
#> -2.1438 -0.7033 -0.1222  0.7255  2.2997 
#> 
#> Coefficients:
#>               Estimate Std. Error t value Pr(>|t|)    
#> (Intercept)  7.961e-16  2.081e-01   0.000   1.0000    
#> Zk[, 1:4]1  -5.275e-01  7.725e-02  -6.828 3.72e-07 ***
#> Zk[, 1:4]2  -1.231e-02  8.262e-02  -0.149   0.8828    
#> Zk[, 1:4]3  -1.759e-01  8.384e-02  -2.098   0.0461 *  
#> Zk[, 1:4]4  -3.491e-02  8.396e-02  -0.416   0.6811    
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 1.14 on 25 degrees of freedom
#> Multiple R-squared:  0.672,  Adjusted R-squared:  0.6195 
#> F-statistic:  12.8 on 4 and 25 DF,  p-value: 8.352e-06

Note:

  • the intercept is estimated as zero. (Why?) The model could have been fitted as
m4 <- lm(Y~-1+Zk[,1:4])
  • the PC-predictors are uncorrelated (by construction)

  • first PC-predictors are not necessarily the most important predictors

  • \(p\)-values are not very meaningful when prediction is the objective

Methods for feature selection will be discussed later.

4 Ridge Regression

4.1 Penalty

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

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

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

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

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

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

4.2 Graphical interpretation

4.3 Solution

The solution is given by \[ \hat{\boldsymbol{\beta}} = (\mathbf{X^TX}+\lambda \mathbf{I})^{-1} \mathbf{X^T Y}. \] It can be shown that \((\mathbf{X^TX}+\lambda \mathbf{I})\) is always of rank \(p\) if \(\lambda>0\).

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

We also find \[ \text{var}\left[\hat{\mathbf\beta}\right] = (\mathbf{X^TX}+\lambda \mathbf{I})^{-1} \mathbf{X}^T\mathbf{X} (\mathbf{X^TX}+\lambda \mathbf{I})^{-1}\sigma^2 \]

However, it can be shown that improved intervals that also account for the bias can be constructed by using:

\[ \text{var}\left[\hat{\mathbf\beta}\right] = (\mathbf{X^TX}+\lambda \mathbf{I})^{-1} \sigma^2. \]

4.3.1 Proof

The criterion to be minimised is \[ \text{SSE}_\text{pen}=\Vert\mathbf{Y} - \mathbf{X\beta}\Vert_2^2 + \lambda \Vert \boldsymbol{\beta} \Vert_2^2. \] First we re-express SSE in matrix notation: \[ \text{SSE}_\text{pen} = (\mathbf{Y}-\mathbf{X\beta})^T(\mathbf{Y}-\mathbf{X\beta}) + \lambda \boldsymbol{\beta}^T\boldsymbol{\beta}. \] The partial derivative w.r.t. \(\boldsymbol{\beta}\) is \[ \frac{\partial}{\partial \boldsymbol{\beta}}\text{SSE}_\text{pen} = -2\mathbf{X}^T(\mathbf{Y}-\mathbf{X\beta})+2\lambda\boldsymbol{\beta}. \] Solving \(\frac{\partial}{\partial \boldsymbol{\beta}}\text{SSE}_\text{pen}=0\) gives \[ \hat{\boldsymbol{\beta}} = (\mathbf{X^TX}+\lambda \mathbf{I})^{-1} \mathbf{X^T Y}. \] (assumption: \((\mathbf{X^TX}+\lambda \mathbf{I})\) is of rank \(p\). This is always true if \(\lambda>0\))

4.5 Properties

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

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

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

  • the variance of the prediction \(\hat{{Y}}(\mathbf{x})=\mathbf{x}^T\hat\beta\), \[ \text{var}\left[\hat{{Y}}(\mathbf{x})\mid \mathbf{x}\right] = \mathbf{x}^T(\mathbf{X^TX}+\lambda \mathbf{I})^{-1}\mathbf{x} \] is smaller than with the least-squares estimator.

  • through the bias-variance trade-off it is hoped that better predictions in terms of expected conditional test error can be obtained, for an appropriate choice of \(\lambda\).

Recall the expression of the expected conditional test error \[\begin{eqnarray} Err(\mathbf{x}) &=& \text{E}\left[(\hat{Y} - Y^*)^2\mid \mathbf{x}\right]\\ &=& \text{var}\left[\hat{Y}\mid \mathbf{x}\right] + \text{bias}^2(\mathbf{x})+ \text{var}\left[Y^*\mid \mathbf{x}\right] \end{eqnarray}\] where

  • \(\hat{Y}=\hat{Y}(\mathbf{x})=\mathbf{x}^T\hat{\boldsymbol{\beta}}\) is the prediction at \(\mathbf{x}\)
  • \(Y^*\) is an outcome at predictor \(\mathbf{x}\)
  • \(\mu(\mathbf{x}) = \text{E}\left[\hat{Y}\mid \mathbf{x}\right] \text{ and } \mu^*(x)=\text{E}\left[Y^*\mid \mathbf{x}\right]\)
  • \(\text{bias}(\mathbf{x})=\mu(\mathbf{x})-\mu^*(\mathbf{x})\)
  • \(\text{var}\left[Y^*\mid \mathbf{x}\right]\) the irreducible error that does not depend on the model. It simply originates from observations that randomly fluctuate around the true mean \(\mu^*(x)\).

4.6 Toxicogenomics example

library(glmnet)
mRidge <- glmnet(
  x = toxData[,-1] %>%
    as.matrix,
  y = toxData %>%
    pull(BA),
  alpha = 0) # ridge: alpha = 0

plot(mRidge, xvar="lambda")

The R function uses to refer to the penalty parameter. In this course we use \(\lambda\), because \(\lambda\) is often used as eigenvalues.

The graph shows that with increasing penalty parameter, the parameter estimates are shrunken towards zero. The estimates will only reach zero for \(\lambda \rightarrow \infty\). The stronger the shrinkage, the larger the bias (towards zero) and the smaller the variance of the parameter estimators (and hence also smaller variance of the predictions).

Another (informal) viewpoint is the following. By shrinking the estimates towards zero, the estimates loose some of their ``degrees of freedom’’ so that the parameters become estimable with only \(n<p\) data points. Even with a very small \(\lambda>0\), the parameters regain their estimability. However, note that the variance of the estimator is given by \[ \text{var}\left[\hat{\mathbf\beta}\right] = (\mathbf{X^TX}+\lambda \mathbf{I})^{-1} \sigma^2 = \mathbf{V}(\boldsymbol{\Delta}^2+\lambda\mathbf{I})^{-1}\mathbf{V}^T\sigma^2. \] Hence, a small \(\lambda\) will result in large variances of the parameter estimators. The larger \(\lambda\), the smaller the variances become. In the limit, as \(\lambda\rightarrow\infty\), the estimates are converged to zero and show no variability any longer.

5 Lasso Regression

  • The Lasso is another example of penalised regression.

  • The lasso estimator of \(\boldsymbol{\beta}\) is the solution to minimising the penalised SSE \[ \text{SSE}_\text{pen} = \sum_{i=1}^n (Y_i - \mathbf{x}_i^T\boldsymbol{\beta})^2 + \lambda \sum_{j=1}^p \vert \beta_j\vert. \]

or, equivalently, minimising

\[ \text{SSE} = \Vert \mathbf{Y} - \mathbf{X\beta}\Vert_2^2 \text{ subject to } \Vert \mathbf\beta\Vert_1 \leq c \] with

  • \(\Vert \mathbf\beta\Vert_1 = \sum\limits_{j=1}^p \vert \beta_j \vert\)

  • Despite strong similarity between ridge and lasso regression (\(L_2\) versus \(L_1\) norm in penalty term), there is no analytical solution of the lasso parameter estimate of \(\mathbf\beta\).

  • Fortunately, computational efficient algorithms have been implemented in statistical software

  • The Lasso estimator of \(\boldsymbol{\beta}\) is biased and generally has a smaller variance then the least-squares estimator.

  • Hence, the bias-variance trade-off may here also help in finding better predictions with biased estimators.

  • In contrast to ridge regression, however, the lasso estimator can give at most \(\min(p,n)\) non-zero \(\beta\)-estimates.

  • Hence, at first sight the lasso is not directly appropriate for high-dimensional settings.

  • An important advantage of the lasso is that choosing an appropriate value for \(\lambda\) is a kind a model building or feature selection procedure (see further).

5.1 Graphical interpretation of Lasso vs ridge

Note that the lasso is a constrained regression problem with

\[ \Vert \mathbf{Y} - \mathbf{X\beta}\Vert_2^2 \text{ subject to } \Vert \mathbf\beta\Vert_1 \leq c \] and ridge \[ \Vert \mathbf{Y} - \mathbf{X\beta}\Vert_2^2 \text{ subject to } \Vert \mathbf\beta\Vert^2_2 \leq c \]

Note, that

  • parameters for the lasso can never switch sign, they are set at zero! Selection!
  • ridge regression can lead to parameters that switch sign.

5.2 Toxicogenomics example

mLasso <- glmnet(
  x = toxData[,-1] %>%
    as.matrix,
  y = toxData %>%
    pull(BA),
alpha = 1)
plot(mLasso, xvar = "lambda")

  • The graph with the paths of the parameter estimates nicely illustrates the typical behaviour of the lasso estimates as a function of \(\lambda\): when \(\lambda\) increases the estimates are shrunken towards zero.

  • When an estimate hits zero, it remains exactly equal to zero when \(\gamma\) further increases. A parameter estimate equal to zero, say \(\hat\beta_j=0\), implies that the corresponding predictor \(x_j\) is no longer included in the model (i.e. \(\beta_jx_j=0\)).

  • The model fit is known as a sparse model fit (many zeroes). Hence, choosing a appropriate value for \(\gamma\) is like choosing the important predictors in the model (feature selection).

6 Splines and the connection to ridge regression.

6.1 Lidar dataset

  • LIDAR (light detection and ranging) uses the reflection of laser-emitted light to detect chemical compounds in the atmosphere.

  • The LIDAR technique has proven to be an efficient tool for monitoring the distribution of several atmospheric pollutants of importance; see Sigrist (1994).

  • The range is the distance traveled before the light is reflected back to its source.

  • The logratio is the logarithm of the ratio of received light from two laser sources.

    • One source had a frequency equal to the resonance frequency of the compound of interest, which was mercury in this study.

    • The other source had a frequency off this resonance frequency.

    • The concentration of mercury can be derived from a regression model of the logratio in function of the range for each range x.

library("SemiPar")
data(lidar)
pLidar <- lidar %>%
  ggplot(aes(x = range, y = logratio)) +
  geom_point() +
  xlab("range (m)")

pLidar +
  geom_smooth()

  • The data is non-linear
  • Linear regression will not work!
  • The data shows a smooth relation between the logratio and the range

6.2 Basis expansion

\[y_i=f(x_i)+\epsilon_i,\] with \[f(x)=\sum\limits_{k=1}^K \theta_k b_k(x)\]

  • Select set of basis functions \(b_k(x)\)

  • Select number of basis functions \(K\)

  • Examples

    • Polynomial model: \(x^k\)
    • Orthogonal series: Fourier, Legendre polynomials, Wavelets
    • Polynomial splines: \(1, x, (x-t_m)_+\) with \(m=1, \ldots, K-2\) knots \(t_m\)

6.2.1 Trunctated line basis

\[y_i=f(x_i)+\epsilon_i,\]

  • One of the most simple basis expansions
  • \(f(x_i)=\beta_0+\beta_1x_i+\sum\limits_{m=1}^{K-2}\theta_m(x_i-t_m)_+\) with \((.)_+\) the operator that takes the positive part.
  • Note, that better basis expansions exist, which are orthogonal, computational more stable and/or continuous derivative beyond first order
  • We will use this basis for didactical purposes
  • We can use OLS to fit y w.r.t. the basis.
knots <- seq(400,700,12.5)

basis <- sapply(knots,
  function(k,y) (y-k)*(y>k),
  y= lidar %>% pull(range)
  )

basisExp <- cbind(1, range = lidar %>% pull(range), basis)

splineFitLs <- lm(logratio ~ -1 + basisExp, lidar)

pBasis <- basisExp[,-1] %>%
  data.frame %>%
  gather("basis","values",-1) %>%
  ggplot(aes(x = range, y = values, color = basis)) +
  geom_line() +
  theme(legend.position="none") +
  ylab("basis")

grid.arrange(
  pLidar +
    geom_line(aes(x = lidar$range, y = splineFitLs$fitted), lwd = 2),
  pBasis,
  ncol=1)

  • Note, that the model is overfitting!
  • The fit is very wiggly and is tuned too much to the data.
  • The fit has a large variance and low bias.
  • It will therefore not generalise well to predict the logratio of future observations.

6.2.1.1 Solution for overfitting?

  • We could perform model selection on the basis to select the important basis functions to model the signal. But, this will have the undesired property that the fit will no longer be smooth.

  • We can also adopt a ridge penalty!

  • However, we do not want to penalise the intercept and the linear term.

  • Ridge criterion

\[\Vert\mathbf{Y}-\mathbf{X\beta}\Vert^2+\lambda\boldsymbol{\beta}^T\mathbf{D}\boldsymbol{\beta} \]

With \(\mathbf{D}\) with dimensions (K,K): \(\mathbf{D}=\left[\begin{array}{cc}\mathbf{0}_{2\times2}& \mathbf{0}_{2\times K-2}\\ \mathbf{0}_{K-2\times2}&\mathbf{I}_{K-2\times K-2}\end{array}\right]\)

  • Here we will set the penalty at 900.
D <- diag(ncol(basisExp))
D[1:2,1:2] <- 0
lambda <- 900
betaRidge <- solve(t(basisExp)%*%basisExp+(lambda*D))%*%t(basisExp)%*%lidar$logratio
grid.arrange(
  pLidar +
    geom_line(aes(x = lidar$range, y = c(basisExp %*% betaRidge)), lwd = 2),
  pBasis,
  ncol=1)

How do we choose \(\lambda\)?


7 Evaluation of Prediction Models

Predictions are calculated with the fitted model \[ \hat{Y}(\mathbf{x}) = \hat{m}(\mathbf{x})=\mathbf{x}^T\hat{\beta} \] when focussing on prediction, we want the prediction error to be as small as possible.

The prediction error for a prediction at covariate pattern \(\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 covariate pattern \(\mathbf{x}\)

Prediction is typically used to predict an outcome before it is observed.

  • Hence, the outcome \(Y^*\) is not observed yet, and
  • the prediction error cannot be computed.

  • Recall that the prediction model \(\hat{Y}(\mathbf{x})\) is estimated by using data in the training data set \((\mathbf{X},\mathbf{Y})\), and

  • that the outcome \(Y^*\) is an outcome at \(\mathbf{x}\) which is assumed to be independent of the training data.

  • Goal is to use prediction model for predicting a future observation (\(Y^*\)), i.e. an observation that still has to be realised/observed (otherwise prediction seems rather useless).

  • Hence, \(Y^*\) can never be part of the training data set.


Here we provide definitions and we show how the prediction performance of a prediction model can be evaluated from data.

Let \({\cal{T}}=(\mathbf{Y},\mathbf{X})\) denote the training data, from which the prediction model \(\hat{Y}(\cdot)\) is build. This building process typically involves feature selection and parameter estimation.

We will use a more general notation for the prediction model: \(\hat{m}(\mathbf{x})=\hat{Y}(\mathbf{x})\).


7.1 Test or Generalisation Error

The test or generalisation error for prediction model \(\hat{m}(\cdot)\) is given by \[ \text{Err}_{\cal{T}} = \text{E}_{Y^*,X^*}\left[(\hat{m}(\mathbf{X}^*) - Y^*)^2\mid {\cal{T}}\right] \] where \((Y^*,X^*)\) is independent of the training data.


  • Note that the test error is conditional on the training data \({\cal{T}}\).
  • Hence, the test error evaluates the performance of the single model build from the observed training data.
  • This is the ultimate target of the model assessment, because it is exactly this prediction model that will be used in practice and applied to future predictors \(\mathbf{X}^*\) to predict \(Y^*\).
  • The test error is defined as an average over all such future observations \((Y^*,\mathbf{X}^*)\).

7.2 Conditional test error

Sometimes the conditional test error is used:

The conditional test error in \(\mathbf{x}\) for prediction model \(\hat{m}(\mathbf{x})\) is given by \[ \text{Err}_{\cal{T}}(\mathbf{x}) = \text{E}_{Y^*}\left[(\hat{m}(\mathbf{x}) - Y^*)^2\mid {\cal{T}}, \mathbf{x}\right] \] where \(Y^*\) is an outcome at predictor \(\mathbf{x}\), independent of the training data.

Hence, \[ \text{Err}_{\cal{T}} = \text{E}_{X^*}\left[\text{Err}_{\cal{T}}(\mathbf{X}^*)\right]. \]

A closely related error is the insample error.


7.3 Insample Error

The insample error for prediction model \(\hat{m}(\mathbf{x})\) is given by \[ \text{Err}_{\text{in} \cal{T}} = \frac{1}{n}\sum_{i=1}^n \text{Err}_{\cal{T}}(\mathbf{x}_i), \]

i.e. the insample error is the sample average of the conditional test errors evaluated in the \(n\) training dataset predictors \(\mathbf{x}_i\).

Since \(\text{Err}_{\cal{T}}\) is an average over all \(\mathbf{X}\), even over those predictors not observed in the training dataset, it is sometimes referred to as the outsample error.


7.4 Estimation of the insample error

We start with introducing the training error rate, which is closely related to the MSE in linear models.

7.4.1 Training error

The training error is given by \[ \overline{\text{err}} = \frac{1}{n}\sum_{i=1}^n (Y_i - \hat{m}(\mathbf{x}_i))^2 , \] where the \((Y_i,\mathbf{x}_i)\) from the training dataset which is also used for the calculation of \(\hat{m}\).

  • The training error is an overly optimistic estimate of the test error \(\text{Err}_{\cal{T}}\).

  • The training error will never increases when the model becomes more complex. \(\longrightarrow\) cannot be used directly as a model selection criterion.

Indeed, model parameters are often estimated by minimising the training error (cfr. SSE).

  • Hence the fitted model adapts to the training data, and
  • training error will be an overly optimistic estimate of the test error \(\text{Err}_{\cal{T}}\).

It can be shown that the training error is related to the insample test error via

\[ \text{E}_\mathbf{Y} \left[\text{Err}_{\text{in}{\cal{T}}}\right] = \text{E}_\mathbf{Y}\left[\overline{\text{err}}\right] + \frac{2}{n}\sum_{i=1}^n \text{cov}_\mathbf{Y}\left[\hat{m}(\mathbf{x}_i),Y_i\right], \]

Note, that for linear models \[ \hat{m}(\mathbf{x}_i) = \mathbf{X}\hat{\boldsymbol{\beta}}= \mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{Y} = \mathbf{HY} \] with

  • \(\mathbf{H}\) the hat matrix and
  • all \(Y_i\) are assumed to be independently distributed \(N(\mathbf{X}\boldsymbol{\beta},\sigma^2)\)

Hence, for linear models with independent observations

\[\begin{eqnarray} \text{cov}_\mathbf{Y}\left[\hat{m}(\mathbf{x}_i),Y_i)\right] &=& \text{cov}_\mathbf{Y}\left[\mathbf{H}_{i}^T\mathbf{Y},Y_i)\right]\\ &=& \text{cov}_\mathbf{Y}\left[h_{ii} Y_i,Y_i\right]\\ &=& h_{ii} \text{cov}_\mathbf{Y}\left[Y_i,Y_i\right]\\ &=& h_{ii} \sigma^2\\ \end{eqnarray}\]

And we can thus estimate the insample error by Mallow’s \(C_p\)

\[\begin{eqnarray} C_p &=& \overline{\text{err}} + \frac{2\sigma^2}{n}\text{tr}(\mathbf{H})\\ &=& \overline{\text{err}} + \frac{2\sigma^2p}{n} \end{eqnarray}\]

with \(p\) the number of predictors.

  • Mallow’s \(C_p\) is often used for model selection.
  • Note, that we can also consider it as a kind of penalized least squares:

\[ n \times C_p = \Vert \mathbf{Y} - \mathbf{X}\boldsymbol{\beta}\Vert_2^2 + 2\sigma^2 \Vert \boldsymbol{\beta} \Vert_0 \] with \(L_0\) norm \(\Vert \boldsymbol{\beta} \Vert_0 = \sum_{j=1}^p \beta_p^0 = p\).


7.5 Expected test error

The test or generalisation error was defined conditionally on the training data. By averaging over the distribution of training datasets, the expected test error arises.

\[\begin{eqnarray*} \text{E}_{\cal{T}}\left[\text{Err}_{{\cal{T}}}\right] &=& \text{E}_{\cal{T}}\left[\text{E}_{Y^*,X^*}\left[(\hat{m}(\mathbf{X}^*) - Y^*)^2\mid {\cal{T}}\right]\right] \\ &=& \text{E}_{Y^*,X^*,{\cal{T}}}\left[(\hat{m}(\mathbf{X}^*) - Y^*)^2\right]. \end{eqnarray*}\]

  • The expected test error may not be of direct interest when the goal is to assess the prediction performance of a single prediction model \(\hat{m}(\cdot)\).

  • The expected test error averages the test errors of all models that can be build from all training datasets, and hence this may be less relevant when the interest is in evaluating one particular model that resulted from a single observed training dataset.

  • Also note that building a prediction model involves both parameter estimation and feature selection.

  • Hence the expected test error also evaluates the feature selection procedure (on average).

  • If the expected test error is small, it is an indication that the model building process gives good predictions for future observations \((Y^*,\mathbf{X}^*)\) on average.

7.5.1 Estimating the Expected test error

The expected test error may be estimated by cross validation (CV).

7.5.1.1 Leave one out cross validation (LOOCV)}

The LOOCV estimator of the expected test error (or expected outsample error) is given by \[ \text{CV} = \frac{1}{n} \sum_{i=1}^n \left(Y_i - \hat{m}^{-i}(\mathbf{x}_i)\right)^2 , \] where

  • the \((Y_i,\mathbf{x}_i)\) form the training dataset
  • \(\hat{m}^{-i}\) is the fitted model based on all training data, except observation \(i\)
  • \(\hat{m}^{-i}(\mathbf{x}_i)\) is the prediction at \(\mathbf{x}_i\), which is the observation left out the training data before building model \(m\).

Some rationale as to why LOOCV offers a good estimator of the outsample error:

  • the prediction error \(Y^*-\hat{m}(\mathbf{x})\) is mimicked by not using one of the training outcomes \(Y_i\) for the estimation of the model so that this \(Y_i\) plays the role of \(Y^*\), and, consequently, the fitted model \(\hat{m}^{-i}\) is independent of \(Y_i\)

  • the sum in \(CV\) is over all \(\mathbf{x}_i\) in the training dataset, but each term \(\mathbf{x}_i\) was left out once for the calculation of \(\hat{m}^{-i}\). Hence, \(\hat{m}^{-i}(\mathbf{x}_i)\) mimics an outsample prediction.

  • the sum in CV is over \(n\) different training datasets (each one with a different observation removed), and hence CV is an estimator of the expected test error.

  • For linear models the LOOCV can be readily obtained from the fitted model: i.e.

\[\text{CV} = \frac{1}{n}\sum\limits_{i=1}^n \frac{e_i^2}{(1-h_{ii})^2}\]

with \(e_i\) the residuals from the model that is fitted based on all training data.


An alternative to LOOCV is the \(k\)-fold cross validation procedure. It also gives an estimate of the expected outsample error.

7.5.1.2 \(k\)-fold cross validation

  • Randomly divide the training dataset into \(k\) approximately equal subsets . Let \(S_j\) denote the index set of the \(j\)th subset (referred to as a fold). Let \(n_j\) denote the number of observations in fold \(j\).

  • The \(k\)-fold cross validation estimator of the expected outsample error is given by \[ \text{CV}_k = \frac{1}{k}\sum_{j=1}^k \frac{1}{n_j} \sum_{i\in S_j} \left(Y_i - \hat{m}^{-S_j}(\mathbf{x}_i)\right)^2 \] where \(\hat{m}^{-S_j}\) is the model fitted using all training data, except observations in fold \(j\) (i.e. observations \(i \in S_j\)).


The cross validation estimators of the expected outsample error are nearly unbiased. One argument that helps to understand where the bias comes from is the fact that e.g. in de LOOCV estimator the model is fit on only \(n-1\) observations, whereas we are aiming at estimating the outsample error of a model fit on all \(n\) training observations. Fortunately, the bias is often small and is in practice hardly a concern.

\(k\)-fold CV is computationally more complex.

Since CV and CV\(_k\) are estimators, they also show sampling variability. Standard errors of the CV or CV\(_k\) can be computed. We don’t show the details, but in the example this is illustrated.

7.5.2 Bias Variance trade-off

For the expected conditional test error in \(\mathbf{x}\), it holds that \[\begin{eqnarray*} \text{E}_{\cal{T}}\left[\text{Err}_{\cal{T}}(\mathbf{x})\right] &=& \text{E}_{Y^*,{\cal{T}}}\left[(\hat{m}(\mathbf{x})-Y^*)^2 \mid \mathbf{x}\right] \\ &=& \text{var}_{\mathbf{Y}}\left[\hat{Y}(\mathbf{x})\mid \mathbf{x}\right] +(\mu(\mathbf{x})-\mu^*(\mathbf{x}))^2+\text{var}_{Y^*}\left[Y^*\mid \mathbf{x}\right] \end{eqnarray*}\] where \(\mu(\mathbf{x}) = \text{E}_{\mathbf{Y}}\left[\hat{Y}(\mathbf{x})\mid \mathbf{x}\right] \text{ and } \mu^*(\mathbf{x})=\text{E}_{Y^*}\left[Y^*\mid \mathbf{x}\right]\).

  • bias: \(\text{bias}(\mathbf{x})=\mu(\mathbf{x})-\mu^*(\mathbf{x})\)

  • \(\text{var}_{Y^*}\left[Y^*\mid \mathbf{x}\right]\) does not depend on the model, and is referred to as the irreducible variance.


The importance of the bias-variance trade-off can be seen from a model selection perspective. When we agree that a good model is a model that has a small expected conditional test error at some point \(\mathbf{x}\), then the bias-variance trade-off shows us that a model may be biased as long as it has a small variance to compensate for the bias. It often happens that a biased model has a substantial smaller variance. When these two are combined, a small expected test error may occur.

Also note that the model \(m\) which forms the basis of the prediction model \(\hat{m}(\mathbf{x})\) does NOT need to satisfy \(m(\mathbf{x})=\mu(\mathbf{x})\) or \(m(\mathbf{x})=\mu^*(\mathbf{x})\). The model \(m\) is known by the data-analyst (its the basis of the prediction model), whereas \(\mu(\mathbf{x})\) and \(\mu^*(\mathbf{x})\) are generally unknown to the data-analyst. We only hope that \(m\) serves well as a prediction model.


7.5.3 In practice

We use cross validation to estimate the lambda penalty for penalised regression:

  • Ridge Regression
  • Lasso
  • Build models, e.g. select the number of PCs for PCA regression
  • Splines

7.5.4 Toxicogenomics example

7.5.4.1 Lasso

set.seed(15)
library(glmnet)
mCvLasso <- cv.glmnet(
  x = toxData[,-1] %>%
    as.matrix,
  y = toxData %>%
    pull(BA),
  alpha = 1)  # lasso alpha=1

plot(mCvLasso)

Default CV procedure in is \(k=10\)-fold CV.

The Graphs shows

  • 10-fold CV estimates of the extra-sample error as a function of the lasso penalty parameter \(\lambda\).
  • estimate plus and minus once the estimated standard error of the CV estimate (grey bars)
  • On top the number of non-zero regression parameter estimates are shown.

Two vertical reference lines are added to the graph. They correspond to

  • the \(\log(\lambda)\) that gives the smallest CV estimate of the extra-sample error, and
  • the largest \(\log(\lambda)\) that gives a CV estimate of the extra-sample error that is within one standard error from the smallest error estimate.
  • The latter choice of \(\lambda\) has no firm theoretical basis, except that it somehow accounts for the imprecision of the error estimate. One could loosely say that this \(\gamma\) corresponds to the smallest model (i.e. least number of predictors) that gives an error that is within margin of error of the error of the best model.

mLassoOpt <- glmnet(
  x = toxData[,-1] %>%
    as.matrix,
  y = toxData %>%
    pull(BA),
    alpha = 1,
    lambda = mCvLasso$lambda.min)

summary(coef(mLassoOpt))

With the optimal \(\lambda\) (smallest error estimate) the output shows the 9 non-zero estimated regression coefficients (sparse solution).


mLasso1se <- glmnet(
  x = toxData[,-1] %>%
    as.matrix,
    y= toxData %>%
      pull(BA),
    alpha = 1,
    lambda = mCvLasso$lambda.1se)

mLasso1se %>%
  coef %>%
  summary

This shows the solution for the largest \(\lambda\) within one standard error of the optimal model. Now only 3 non-zero estimates result.


7.5.4.2 Ridge

mCvRidge <- cv.glmnet(
  x = toxData[,-1] %>%
    as.matrix,
    y = toxData %>%
      pull(BA),
      alpha = 0)  # ridge alpha=0

plot(mCvRidge)

  • Ridge does not seem to have optimal solution.
  • 10-fold CV is also larger than for lasso.

7.5.4.3 PCA regression

set.seed(1264)
library(DAAG)

tox <- data.frame(
  Y = toxData %>%
    pull(BA),
  PC = Zk)

PC.seq <- 1:25
Err <- numeric(25)

mCvPca <- cv.lm(
  Y~PC.1,
  data = tox,
  m = 5,
  printit = FALSE)

Err[1]<-attr(mCvPca,"ms")

for(i in 2:25) {
  mCvPca <- cv.lm(
    as.formula(
      paste("Y ~ PC.1 + ",
        paste("PC.", 2:i, collapse = "+", sep=""),
        sep=""
      )
    ),
    data = tox,
    m = 5,
    printit = FALSE)
  Err[i]<-attr(mCvPca,"ms")
}
  • Here we illustrate principal component regression.

  • The most important PCs are selected in a forward model selection procedure.

  • Within the model selection procedure the models are evaluated with 5-fold CV estimates of the outsample error.

  • It is important to realise that a forward model selection procedure will not necessarily result in the best prediction model, particularly because the order of the PCs is generally not related to the importance of the PCs for predicting the outcome.

  • A supervised PC would be better.

pPCreg <- data.frame(PC.seq, Err) %>%
  ggplot(aes(x = PC.seq, y = Err)) +
  geom_line() +
  geom_point() +
  geom_hline(
    yintercept = c(
      mCvLasso$cvm[mCvLasso$lambda==mCvLasso$lambda.min],
      mCvLasso$cvm[mCvLasso$lambda==mCvLasso$lambda.1se]),
    col = "red") +
  xlim(1,26)

grid.arrange(
  pPCreg,
  pPCreg + ylim(0,5),
  ncol=2)

  • The graph shows the CV estimate of the outsample error as a function of the number of sparse PCs included in the model.

  • A very small error is obtained with the model with only the first PC. The best model with 3 PCs.

  • The two vertical reference lines correspond to the error estimates obtained with lasso (optimal \(\lambda\) and largest \(\lambda\) within one standard error).

  • Thus although there was a priori no guarantee that the first PCs are the most predictive, it seems to be the case here (we were lucky!).

  • Moreover, the first PC resulted in a small outsample error.

  • Note that the graph does not indicate the variability of the error estimates (no error bars).

  • Also note that the graph clearly illustrates the effect of overfitting: including too many PCs causes a large outsample error.

7.5.5 Lidar Example: splines

  • We use the mgcv package to fit the spline model to the lidar data.
  • A better basis is used than the truncated spline basis
  • Thin plate splines are also linear smoothers, i.e. \(\hat{Y} = \hat{m}(\mathbf{X}) = \mathbf{SY}\)
  • So their variance can be easily calculated.
  • The ridge/smoothness penalty is chosen by generalized cross validation.
library(mgcv)
gamfit <- gam(logratio ~ s(range), data = lidar)
gamfit$sp
#>    s(range) 
#> 0.006114634
pLidar +
  geom_line(aes(x = lidar$range, y = gamfit$fitted), lwd = 2)

7.6 More general error definitions

So far we only looked at continuous outcomes \(Y\) and errors defined in terms of the squared loss \((\hat{m}(\mathbf{x})-Y^*)^2\).

More generally, a loss function measures an discrepancy between the prediction \(\hat{m}(\mathbf{x})\) and an independent outcome \(Y^*\) that corresponds to \(\mathbf{x}\).

Some examples for continuous \(Y\): \[\begin{eqnarray*} L(Y^*,\hat{m}(\mathbf{x})) &=& (\hat{m}(\mathbf{x})-Y^*)^2 \;\;\text{(squared error)} \\ L(Y^*,\hat{m}(\mathbf{x})) &=& \vert\hat{m}(\mathbf{x})-Y^*\vert \;\;\text{(absolute error)} \\ L(Y^*,\hat{m}(\mathbf{x})) &=& 2 \int_{\cal{Y}} f_y(y) \log\frac{f_y(y)}{f_{\hat{m}}(y)} dy \;\;\text{(deviance)}. \end{eqnarray*}\]

In the expression of the deviance

  • \(f_y\) denotes the density function of a distribution with mean set to \(y\) (cfr. perfect fit), and
  • \(f_{\hat{m}}\) is the density function of the same distribution but with mean set to the predicted outcome \(\hat{m}(\mathbf{x})\).

With a given loss function, the errors are defined as follows: - Test or generalisation or outsample error \[ \text{Err}_{\cal{T}} = \text{E}_{Y^*,X^*}\left[L(Y^*,\hat{m}(\mathbf{X}^*))\right] \]

  • Training error \[ \overline{\text{err}} = \frac{1}{n}\sum_{i=1}^n L(Y_i,\hat{m}(\mathbf{x}_i)) \]

  • \(\ldots\)


When an exponential family distribution is assumed for the outcome distribution, and when the deviance loss is used, the insample error can be estimated by means of the AIC and BIC.

7.6.1 Akaike’s Information Criterion (AIC)

The AIC for a model \(m\) is given by \[ \text{AIC} = -2 \ln \hat{L}(m) +2p \] where \(\hat{L}(m)\) is the maximised likelihood for model \(m\).

When assuming normally distributed error terms and homoscedasticity, the AIC becomes \[ \text{AIC} = n\ln \text{SSE}(m) +2p = n\ln(n\overline{\text{err}}(m)) + 2p \] with \(\text{SSE}(m)\) the residual sum of squares of model \(m\).

In linear models with normal error terms, Mallow’s \(C_p\) criterion (statistic) is a linearised version of AIC and it is an unbiased estimator of the in-sample error.


7.6.2 Bayesian Information Criterion (BIC)}

The BIC for a model \(m\) is given by \[ \text{BIC} = -2 \ln \hat{L}(m) +p\ln(n) \] where \(\hat{L}(m)\) is the maximised likelihood for model \(m\).

When assuming normally distributed error terms and homoscedasticity, the BIC becomes \[ \text{BIC} = n\ln \text{SSE}(m) +p\ln(n) = n\ln(n\overline{\text{err}}(m)) + p\ln(n) \] with \(\text{SSE}(m)\) the residual sum of squares of model \(m\).

When large datasets are used, the BIC will favour smaller models than the AIC.


7.7 Training and test sets

Sometimes, when a large (training) dataset is available, one may decide the split the dataset randomly in a

  • training dataset: data are used for model fitting and for model building or feature selection (this may require e.g. cross validation)

  • test dataset: this data are used to evaluate the final model (result of model building). An unbiased estimate of the outsample error (i.e. test or generalisation error) based on this test data is \[ \frac{1}{m} \sum_{i=1}^m \left(\hat{m}(\mathbf{x}_i)-Y_i\right)^2, \] where

    • \((Y_1,\mathbf{x}_1), \ldots, (Y_m,\mathbf{x}_m)\) denote the \(m\) observations in the test dataset

    • \(\hat{m}\) is estimated from using the training data (this may also be the result from model building, using only the training data).


Note that the training dataset is used for model building or feature selection. This also requires the evaluation of models. For these evaluations the methods from the previous slides can be used (e.g. cross validation, \(k\)-fold CV, Mallow’s \(C_p\)). The test dataset is only used for the evaluation of the final model (estimated and build from using only the training data). The estimate of the outsample error based on the test dataset is the best possible estimate in the sense that it is unbiased. The observations used for this estimation are independent of the observations in the training data. However, if the number of data points in the test dataset (\(m\)) is small, the estimate of the outsample error may show large variance and hence is not reliable.

8 Logistic Regression Analysis for High Dimensional Data

8.1 Breast Cancer Example

  • Schmidt et al., 2008, Cancer Research, 68, 5405-5413

  • Gene expression patterns in \(n=200\) breast tumors were investigated (\(p=22283\) genes)

  • After surgery the tumors were graded by a pathologist (stage 1,2,3)

  • Here the objective is to predict stage 3 from the gene expression data (prediction of binary outcome)

  • If the prediction model works well, it can be used to predict the stage from a biopsy sample.

8.2 Data

#BiocManager::install("genefu")
#BiocManager::install("breastCancerMAINZ")

library(genefu)
library(breastCancerMAINZ)
data(mainz)

X <- t(exprs(mainz)) # gene expressions
n <- nrow(X)
H <- diag(n)-1/n*matrix(1,ncol=n,nrow=n)
X <- H%*%X
Y <- ifelse(pData(mainz)$grade==3,1,0)
table(Y)
#> Y
#>   0   1 
#> 165  35
svdX <- svd(X)
k <- 2
Zk <- svdX$u[,1:k] %*% diag(svdX$d[1:k])
colnames(Zk) <- paste0("Z",1:k)

Zk %>%
  as.data.frame %>%
  mutate(grade = Y %>% as.factor) %>%
  ggplot(aes(x= Z1, y = Z2, color = grade)) +
  geom_point(size = 3)


8.3 Logistic regression models

Binary outcomes are often analysed with logistic regression models.

Let \(Y\) denote the binary (1/0, case/control, positive/negative) outcome, and \(\mathbf{x}\) the \(p\)-dimensional predictor.

Logistic regression assumes \[ Y \mid \mathbf{x} \sim \text{Bernoulli}(\pi(\mathbf{x})) \] with \(\pi(\mathbf{x}) = \text{P}\left[Y=1\mid \mathbf{x}\right]\) and \[ \ln \frac{\pi(\mathbf{x})}{1-\pi(\mathbf{x})}=\beta_0 + \boldsymbol{\beta}^T\mathbf{x}. \]

The parameters are typically estimated by maximising the log-likelihood, which is denoted by \(l(\mathbf{ \beta})\), i.e. \[ \hat{\boldsymbol{\beta}} = \text{ArgMax}_\beta l(\boldsymbol{\beta}). \]

  • Maximum likelihood is only applicable when \(n>p\).

  • When \(p>n\) penalised maximum likelihood methods are applicable.


8.4 Penalized maximum likelihood

Penalised estimation methods (e.g. lasso and ridge) can als be applied to maximum likelihood, resulting in the penalised maximum likelihood estimate.

Lasso: \[ \hat{\boldsymbol{\beta}} = \text{ArgMax}_\beta l(\boldsymbol{\beta}) -\lambda \Vert \boldsymbol{\beta}\Vert_1. \]

Ridge: \[ \hat{\boldsymbol{\beta}} = \text{ArgMax}_\beta l(\boldsymbol{\beta}) -\lambda \Vert \boldsymbol{\beta}\Vert_2^2. \]

Once the parameters are estimated, the model may be used to compute \[ \hat{\pi}(\mathbf{x}) = \hat{\text{P}}\left[Y=1\mid \mathbf{x}\right]. \] With these estimated probabilities the prediction rule becomes \[\begin{eqnarray*} \hat{\pi}(\mathbf{x}) &\leq c& \text{predict } Y=0 \\ \hat{\pi}(\mathbf{x}) &>c & \text{predict } Y=1 \end{eqnarray*}\] with \(0<c<1\) a threshold that either is fixed (e.g. \(c=1/2\)), depends on prior probabilities, or is empirically determined by optimising e.g. the Area Under the ROC Curve (AUC) or by finding a good compromise between sensitivity and specificity.

Note that logistic regression directly models the Posterior probability that an observation belongs to class \(Y=1\), given the predictor \(\mathbf{x}\).

8.5 Model evaluation

Common model evaluation criteria for binary prediction models are:

  • sensitivity = true positive rate (TPR)

  • specificity = true negative rate (TNR)

  • misclassification error

  • area under the ROC curve (AUC)

These criteria can again be estimated via cross validation or via splitting of the data into training and test/validation data.

8.5.1 Sensitivity of a model \(\pi\) with threshold \(c\)

Sensitivity is the probability to correctly predict a positive outcome: \[ \text{sens}(\pi,c)=\text{P}_{X^*}\left[\hat\pi(\mathbf{X}^*)>c \mid Y^*=1 \mid {\cal{T}}\right]. \]

It is also known as the true positive rate (TPR).

8.5.2 Specificity of a model \(\pi\) with threshold \(c\)

Specificity is the probability to correctly predict a negative outcome: \[ \text{spec}(\pi,c)=\text{P}_{X^*}\left[\hat\pi(\mathbf{X}^*)\leq c \mid Y^*=0 \mid {\cal{T}}\right]. \]

It is also known as the true negative rate (TNR).


8.5.3 Misclassification error of a model \(\pi\) with threshold \(c\)

The misclassification error is the probability to incorrectly predict an outcome: \[\begin{eqnarray*} \text{mce}(\pi,c) &=&\text{P}_{X^*,Y^*}\left[\hat\pi(\mathbf{X})\leq c \text{ and } Y^*=1 \mid {\cal{T}}\right] \\ & & + \text{P}_{X^*,Y^*}\left[\hat\pi(\mathbf{X})> c \text{ and } Y^*=0 \mid {\cal{T}}\right]. \end{eqnarray*}\]

Note that in the definitions of sensitivity, specificity and the misclassification error, the probabilities refer to the distribution of the \((\mathbf{X}^*,Y^*)\), which is independent of the training data, conditional on the training data. This is in line with the test or generalisation error. The misclassification error is actually the test error when a 0/1 loss function is used. Just as before, the sensitivity, specificity and the misclassification error can also be averaged over the distribution of the training data set, which is in line with the expected test error which has been discussed earlier.


8.5.4 ROC curve of a model \(\pi\)

The Receiver Operating Characteristic (ROC) curve for model \(\pi\) is given by the function

\[ \text{ROC}: [0,1] \rightarrow [0,1]\times [0,1]: c \mapsto (1-\text{spec}(\pi,c), \text{sens}(\pi,c)). \]

For when \(c\) moves from 1 to 0, the ROC function defines a curve in the plane \([0,1]\times [0,1]\), moving from \((0,0)\) for \(c=1\) to \((1,1)\) for \(c=0\).

The horizontal axis of the ROC curve shows 1-specificity. This is also known as the False Positive Rate (FPR).


8.5.5 Area under the curve (AUC) of a model \(\pi\)

The area under the curve (AUC) for model \(\pi\) is area under the ROC curve and is given by \[ \int_0^1 \text{ROC}(c) dc. \]

Some notes about the AUC:

  • AUC=0.5 results when the ROC curve is the diagonal. This corresponds to flipping a coin, i.e. a complete random prediction.

  • AUC=1 results from the perfect ROC curve, which is the ROC curve through the points \((0,0)\), \((0,1)\) and \((1,1)\). This ROC curve includes a threshold \(c\) such that sensitivity and specificity are equal to one.

8.6 Breast cancer example

8.6.1 Data

library(glmnet)

#BiocManager::install("genefu")
#BiocManager::install("breastCancerMAINZ")

library(genefu)
library(breastCancerMAINZ)
data(mainz)

X <- t(exprs(mainz)) # gene expressions
n <- nrow(X)
H <- diag(n)-1/n*matrix(1,ncol=n,nrow=n)
X <- H%*%X
Y <- ifelse(pData(mainz)$grade==3,1,0)
table(Y)
#> Y
#>   0   1 
#> 165  35

From the table of the outcomes in Y we read that

  • 35 tumors were graded as stage 3 and
  • 165 tumors were graded as stage 1 or 2.

In this the stage 3 tumors are referred to as cases or postives and the stage 1 and 2 tumors as controls or negatives.


8.6.2 Training and test dataset

The use of the lasso logistic regression for the prediction of stage 3 breast cancer is illustrated here by

  • randomly splitting the dataset into a training dataset (\(80\%\) of data = 160 tumors) and a test dataset (40 tumors)

  • using the training data to select a good \(\lambda\) value in the lasso logistic regression model (through 10-fold CV)

  • evaluating the final model by means of the test dataset (ROC Curve, AUC).

## Used to provide same results as in previous R version
RNGkind(sample.kind = "Rounding")
set.seed(6977326)
####

n <- nrow(X)
nTrain <- round(0.8*n)
nTrain
#> [1] 160
indTrain <- sample(1:n,nTrain)
XTrain <- X[indTrain,]
YTrain <- Y[indTrain]
XTest <- X[-indTrain,]
YTest <- Y[-indTrain]
table(YTest)
#> YTest
#>  0  1 
#> 32  8

Note that the randomly selected test data has 20% cases of stage 3 tumors. This is a bit higher than the 17.5% in the complete data.

One could also perform the random splitting among the positives and the negatives separately (stratified splitting).

8.6.3 Model fitting based on training data

mLasso <- glmnet(
  x = XTrain,
  y = YTrain,
  alpha = 1,
  family="binomial")  # lasso: alpha = 1

plot(mLasso, xvar = "lambda", xlim = c(-6,-1.5))


mCvLasso <- cv.glmnet(
  x = XTrain,
  y = YTrain,
  alpha = 1,
  type.measure = "class",
    family = "binomial")  # lasso alpha = 1

plot(mCvLasso)

mCvLasso
#> 
#> Call:  cv.glmnet(x = XTrain, y = YTrain, type.measure = "class", alpha = 1,      family = "binomial") 
#> 
#> Measure: Misclassification Error 
#> 
#>     Lambda Index Measure      SE Nonzero
#> min 0.1044    14  0.1437 0.03366      18
#> 1se 0.1911     1  0.1688 0.03492       0

The total misclassification error is used here to select a good value for \(\lambda\).

# BiocManager::install("plotROC")
library(plotROC)

dfLassoOpt <- data.frame(
  pi = predict(mCvLasso,
    newx = XTest,
    s = mCvLasso$lambda.min,
    type = "response") %>% c(.),
  known.truth = YTest)

roc <-
  dfLassoOpt  %>%
  ggplot(aes(d = known.truth, m = pi)) +
  geom_roc(n.cuts = 0) +
  xlab("1-specificity (FPR)") +
  ylab("sensitivity (TPR)")

roc

calc_auc(roc)
  • The ROC curve is shown for the model based on \(\lambda\) with the smallest misclassification error. The model has an AUC of 0.83.

  • Based on this ROC curve an appropriate threshold \(c\) can be chosen. For example, from the ROC curve we see that it is possible to attain a specificity and a sensitivity of 75%.

  • The sensitivities and specificities in the ROC curve are unbiased (independent test dataset) for the prediction model build from the training data. The estimates of sensitivity and specificity, however, are based on only 40 observations.


mLambdaOpt <- glmnet(x = XTrain,
  y = YTrain,
  alpha = 1,
  lambda = mCvLasso$lambda.min,
  family="binomial")

qplot(
  summary(coef(mLambdaOpt))[-1,1],
  summary(coef(mLambdaOpt))[-1,3]) +
  xlab("gene ID") +
  ylab("beta-hat") +
  geom_hline(yintercept = 0, color = "red")

  • The model with the optimal \(\lambda\) has only 19 non-zero parameter estimates.
  • Thus only 19 genes are involved in the prediction model.
  • These 19 parameter estimates are plotting in the graph. A listing of the model output would show the names of the genes.

dfLasso1se <- data.frame(
  pi = predict(mCvLasso,
    newx = XTest,
    s = mCvLasso$lambda.1se,
    type = "response") %>% c(.),
  known.truth = YTest)

roc <-
  rbind(
    dfLassoOpt %>%
      mutate(method = "min"),
    dfLasso1se %>%
      mutate(method = "1se")
  ) %>%
  ggplot(aes(d = known.truth, m = pi, color = method)) +
  geom_roc(n.cuts = 0) +
  xlab("1-specificity (FPR)") +
  ylab("sensitivity (TPR)")

roc

calc_auc(roc)
  • When using the \(\lambda\) of the optimal model up to 1 standard deviation, a diagonal ROC curve is obtained and hence AUC is \(0.5\).

  • This prediction model is thus equivalent to flipping a coin for making the prediction.

  • The reason is that with this choice of \(\lambda\) (strong penalisation) almost all predictors are removed from the model.

  • Therefore, do never blindly choose for the ``optimal’’ \(\lambda\) as defined here, but assess the performance of the model first.

mLambda1se <- glmnet(x = XTrain,
  y = YTrain,
  alpha = 1,
  lambda = mCvLasso$lambda.1se,
  family="binomial")

mLambda1se %>%
  coef %>%
  summary

8.7 The Elastic Net

The lasso and ridge regression have positive and negative properties.

  • Lasso

    • positive: sparse solution

    • negative: at most \(\min(n,p)\) predictors can be selected

    • negative: tend to select one predictor among a group of highly correlated predictors

  • Ridge

    • negative: no sparse solution
    • positive: more than \(\min(n,p)\) predictors can be selected

A compromise between lasso and ridge: the elastic net: \[ \hat{\boldsymbol{\beta}} = \text{ArgMax}_\beta l(\boldsymbol{\beta}) -\gamma_1 \Vert \boldsymbol\beta\Vert_1 -\gamma_2 \Vert \boldsymbol\beta\Vert_2^2. \]

The elastic gives a sparse solution with potentially more than \(\min(n,p)\) predictors.


The glmnet R function uses the following parameterisation, \[ \hat{\boldsymbol{\beta}} = \text{ArgMax}_\beta l(\boldsymbol{\beta}) -\lambda\alpha \Vert \boldsymbol\beta\Vert_1 -\lambda(1-\alpha) \Vert \boldsymbol\beta\Vert_2^2. \]

  • \(\alpha\) parameter gives weight to \(L_1\) penalty term (hence \(\alpha=1\) gives the lasso, and \(\alpha=0\) gives ridge).

  • a \(\lambda\) parameter to give weight to the penalisation

  • Note that the combination of \(\lambda\) and \(\alpha\) gives the same flexibility as the combination of the parameters \(\lambda_1\) and \(\lambda_2\).


8.7.1 Breast cancer example

mElastic <- glmnet(
  x = XTrain,
  y = YTrain,
  alpha = 0.5,
  family="binomial")  # elastic net

plot(mElastic, xvar = "lambda",xlim=c(-5.5,-1))

mCvElastic <- cv.glmnet(x = XTrain,
  y = YTrain,
  alpha = 0.5,
  family = "binomial",
    type.measure = "class")  # elastic net

plot(mCvElastic)

mCvElastic
#> 
#> Call:  cv.glmnet(x = XTrain, y = YTrain, type.measure = "class", alpha = 0.5,      family = "binomial") 
#> 
#> Measure: Misclassification Error 
#> 
#>      Lambda Index Measure      SE Nonzero
#> min 0.01859    66  0.1313 0.02708     148
#> 1se 0.21876    13  0.1562 0.03391      26
dfElast <- data.frame(
  pi = predict(mElastic,
    newx = XTest,
    s = mCvElastic$lambda.min,
    type = "response") %>% c(.),
  known.truth = YTest)

roc <- rbind(
  dfLassoOpt %>% mutate(method = "lasso"),
  dfElast %>% mutate(method = "elast. net")) %>%
  ggplot(aes(d = known.truth, m = pi, color = method)) +
  geom_roc(n.cuts = 0) +
  xlab("1-specificity (FPR)") +
  ylab("sensitivity (TPR)")

roc

calc_auc(roc)
  • More parameters are used than for the lasso, but the performance does not improve.
mElasticOpt <- glmnet(x = XTrain,
  y = YTrain,
  alpha = 0.5,
  lambda = mCvElastic$lambda.min,
  family="binomial")

qplot(
  summary(coef(mElasticOpt))[-1,1],
  summary(coef(mElasticOpt))[-1,3]) +
  xlab("gene ID") +
  ylab("beta-hat") +
  geom_hline(yintercept = 0, color = "red")

Acknowledgement

  • Olivier Thas for sharing his materials of Analysis of High Dimensional Data 2019-2020, which I used as the starting point for this chapter.

Session info

Session info
#> [1] "2024-10-18 13:21:56 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-18
#>  pandoc   3.1.1 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/ (via rmarkdown)
#> 
#> ─ Packages ───────────────────────────────────────────────────────────────────
#>  package           * version    date (UTC) lib source
#>  AIMS              * 1.36.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  AnnotationDbi       1.66.0     2024-05-01 [1] Bioconductor 3.19 (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)
#>  biomaRt           * 2.60.1     2024-06-26 [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)
#>  bootstrap           2019.6     2019-06-17 [1] CRAN (R 4.4.0)
#>  breastCancerMAINZ * 1.42.0     2024-05-02 [1] Bioconductor 3.19 (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)
#>  class               7.3-22     2023-05-03 [1] CRAN (R 4.4.0)
#>  cli                 3.6.3      2024-06-21 [1] CRAN (R 4.4.0)
#>  cluster             2.1.6      2023-12-01 [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)
#>  crayon              1.5.3      2024-06-20 [1] CRAN (R 4.4.0)
#>  curl                5.2.3      2024-09-20 [1] CRAN (R 4.4.1)
#>  DAAG              * 1.25.6     2024-05-26 [1] CRAN (R 4.4.0)
#>  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)
#>  deldir              2.0-4      2024-02-28 [1] CRAN (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)
#>  e1071             * 1.7-16     2024-09-16 [1] CRAN (R 4.4.1)
#>  evaluate            1.0.0      2024-09-17 [1] CRAN (R 4.4.1)
#>  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)
#>  foreach             1.5.2      2022-02-02 [1] CRAN (R 4.4.0)
#>  future              1.34.0     2024-07-29 [1] CRAN (R 4.4.0)
#>  future.apply        1.11.2     2024-03-28 [1] CRAN (R 4.4.0)
#>  genefu            * 2.36.0     2024-04-30 [1] Bioconductor 3.19 (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
#>  ggforce           * 0.4.2      2024-02-19 [1] CRAN (R 4.4.0)
#>  ggplot2           * 3.5.1      2024-04-23 [1] CRAN (R 4.4.0)
#>  glmnet            * 4.1-8      2023-08-22 [1] CRAN (R 4.4.0)
#>  globals             0.16.3     2024-03-08 [1] CRAN (R 4.4.0)
#>  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)
#>  httr                1.4.7      2023-08-15 [1] CRAN (R 4.4.0)
#>  httr2               1.0.5      2024-09-26 [1] CRAN (R 4.4.1)
#>  iC10              * 2.0.2      2024-07-19 [1] CRAN (R 4.4.0)
#>  iC10TrainingData    2.0.1      2024-07-16 [1] CRAN (R 4.4.0)
#>  impute              1.78.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  interp              1.1-6      2024-01-26 [1] CRAN (R 4.4.0)
#>  IRanges             2.38.1     2024-07-03 [1] Bioconductor 3.19 (R 4.4.1)
#>  iterators           1.0.14     2022-02-05 [1] CRAN (R 4.4.0)
#>  jpeg                0.1-10     2022-11-29 [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)
#>  KernSmooth          2.23-24    2024-05-17 [1] CRAN (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)
#>  latex2exp         * 0.9.6      2022-11-28 [1] CRAN (R 4.4.0)
#>  lattice             0.22-6     2024-03-20 [1] CRAN (R 4.4.0)
#>  latticeExtra        0.6-30     2022-07-04 [1] CRAN (R 4.4.0)
#>  lava                1.8.0      2024-03-05 [1] CRAN (R 4.4.0)
#>  lifecycle           1.0.4      2023-11-07 [1] CRAN (R 4.4.0)
#>  limma               3.60.5     2024-09-29 [1] Bioconductor 3.19 (R 4.4.1)
#>  listenv             0.9.1      2024-01-29 [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)
#>  MASS                7.3-61     2024-06-13 [1] CRAN (R 4.4.0)
#>  Matrix            * 1.7-0      2024-03-22 [1] CRAN (R 4.4.0)
#>  mclust              6.1.1      2024-04-29 [1] CRAN (R 4.4.0)
#>  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)
#>  munsell             0.5.1      2024-04-01 [1] CRAN (R 4.4.0)
#>  nlme              * 3.1-166    2024-08-14 [1] CRAN (R 4.4.0)
#>  pamr                1.57       2024-07-01 [1] CRAN (R 4.4.0)
#>  parallelly          1.38.0     2024-07-27 [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)
#>  plotROC           * 2.3.1      2023-10-06 [1] CRAN (R 4.4.0)
#>  plyr                1.8.9      2023-10-02 [1] CRAN (R 4.4.0)
#>  png                 0.1-8      2022-11-29 [1] CRAN (R 4.4.0)
#>  polyclip            1.10-7     2024-07-23 [1] CRAN (R 4.4.0)
#>  prettyunits         1.2.0      2023-09-24 [1] CRAN (R 4.4.0)
#>  prodlim           * 2024.06.25 2024-06-24 [1] CRAN (R 4.4.0)
#>  progress            1.2.3      2023-12-06 [1] CRAN (R 4.4.0)
#>  proxy               0.4-27     2022-06-09 [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)
#>  rbibutils           2.2.16     2023-10-25 [1] CRAN (R 4.4.0)
#>  RColorBrewer        1.1-3      2022-04-03 [1] CRAN (R 4.4.0)
#>  Rcpp                1.0.13     2024-07-17 [1] CRAN (R 4.4.0)
#>  Rdpack              2.6.1      2024-08-06 [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)
#>  rmeta               3.0        2018-03-20 [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)
#>  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)
#>  scales              1.3.0      2023-11-28 [1] CRAN (R 4.4.0)
#>  SemiPar           * 1.0-4.2    2018-04-16 [1] CRAN (R 4.4.0)
#>  sessioninfo         1.2.2      2021-12-06 [1] CRAN (R 4.4.0)
#>  shape               1.4.6.1    2024-02-23 [1] CRAN (R 4.4.0)
#>  statmod             1.5.0      2023-01-06 [1] CRAN (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)
#>  SuppDists           1.1-9.8    2024-09-03 [1] CRAN (R 4.4.1)
#>  survcomp          * 1.54.0     2024-04-30 [1] Bioconductor 3.19 (R 4.4.0)
#>  survival          * 3.7-0      2024-06-05 [1] CRAN (R 4.4.0)
#>  survivalROC         1.0.3.1    2022-12-05 [1] CRAN (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)
#>  tweenr              2.0.3      2024-02-26 [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)
#>  vroom               1.6.5      2023-12-05 [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)
#>  xml2                1.3.6      2023-12-04 [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
#> 
#> ──────────────────────────────────────────────────────────────────────────────
LS0tCnRpdGxlOiAiMy4gUHJlZGljdGlvbiB3aXRoIEhpZ2ggRGltZW5zaW9uYWwgUHJlZGljdG9ycyIKYXV0aG9yOiAiTGlldmVuIENsZW1lbnQiCmRhdGU6ICJzdGF0T21pY3MsIEdoZW50IFVuaXZlcnNpdHkgKGh0dHBzOi8vc3RhdG9taWNzLmdpdGh1Yi5pbykiCm91dHB1dDoKICBib29rZG93bjo6cGRmX2RvY3VtZW50MjoKICAgIHRvYzogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBsYXRleF9lbmdpbmU6IHhlbGF0ZXgKYWx3YXlzX2FsbG93X2h0bWw6IHRydWUKLS0tCgpgYGB7ciwgY2hpbGQ9Il9zZXR1cC5SbWQifQpgYGAKCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9IEZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShncmlkRXh0cmEpCmBgYAoKIyBJbnRyb2R1Y3Rpb24KCiMjIFByZWRpY3Rpb24gd2l0aCBIaWdoIERpbWVuc2lvbmFsIFByZWRpY3RvcnMKCkdlbmVyYWwgc2V0dGluZzoKCi0gICBBaW06IGJ1aWxkIGEgKipwcmVkaWN0aW9uIG1vZGVsKiogdGhhdCBnaXZlcyBhIHByZWRpY3Rpb24gb2YgYW4gb3V0Y29tZSBmb3IgYSBnaXZlbiBzZXQgb2YgcHJlZGljdG9ycy4KCi0gV2UgdXNlICRYJCB0byByZWZlciB0byB0aGUgcHJlZGljdG9ycyBhbmQgJFkkIHRvIHJlZmVyIHRvIHRoZSBvdXRjb21lLgoKCi0gQSAqKnRyYWluaW5nIGRhdGEgc2V0KiogaXMgYXZhaWxhYmxlLCBzYXkgJChcbWF0aGJme1h9LFxtYXRoYmZ7WX0pJC4gSXQgY29udGFpbnMgJG4kIG9ic2VydmF0aW9ucyBvbiBvdXRjb21lcyBhbmQgb24gJHAkIHByZWRpY3RvcnMuCgotIFVzaW5nIHRoZSB0cmFpbmluZyBkYXRhLCBhIHByZWRpY3Rpb24gbW9kZWwgaXMgYnVpbGQsIHNheSAkXGhhdHttfShcbWF0aGJme1h9KSQuIFRoaXMgdHlwaWNhbGx5IGludm9sdmVzICoqbW9kZWwgYnVpbGRpbmcgKGZlYXR1cmUgc2VsZWN0aW9uKSoqIGFuZCBwYXJhbWV0ZXIgZXN0aW1hdGlvbi4KCgotICAgRHVyaW5nIHRoZSBtb2RlbCBidWlsZGluZywgcG90ZW50aWFsICoqbW9kZWxzIG5lZWQgdG8gYmUgZXZhbHVhdGVkKiogaW4gdGVybXMgb2YgdGhlaXIgcHJlZGljdGlvbiBxdWFsaXR5LgoKIyMgRXhhbXBsZTogVG94aWNvZ2Vub21pY3MgaW4gZWFybHkgZHJ1ZyBkZXZlbG9wbWVudAoKIyMjIEJhY2tncm91bmQKCi0gRWZmZWN0IG9mIGNvbXBvdW5kIG9uIGdlbmUgZXhwcmVzc2lvbi4KCi0gSW5zaWdodCBpbiBhY3Rpb24gYW5kIHRveGljaXR5IG9mIGRydWcgaW4gZWFybHkgcGhhc2UKLSBEZXRlcm1pbmUgYWN0aXZpdHkgd2l0aCBiaW8tYXNzYXk6IGUuZy4gYmluZGluZyBhZmZpbml0eSBvZiBjb21wb3VuZCB0byBjZWxsIHdhbGwgcmVjZXB0b3IgKHRhcmdldCwgSUM1MCkuCi0gRWFybHkgcGhhc2U6ICAyMCB0byA1MCBjb21wb3VuZHMKLSBCYXNlZCBvbiBpbiB2aXRybyByZXN1bHRzIG9uZSBhaW1zIHRvIGdldCBpbnNpZ2h0IGluIGhvdyB0byBidWlsZCBiZXR0ZXIgY29tcG91bmQgKGhpZ2hlciBvbi10YXJnZXQgYWN0aXZpdHkgbGVzcyB0b3hpY2l0eS4KLSBTbWFsbCB2YXJpYXRpb25zIGluIG1vbGVjdWxhciBzdHJ1Y3R1cmUgbGVhZCB0byB2YXJpYXRpb25zIGluIEJBIGFuZCBnZW5lIGV4cHJlc3Npb24uCi0gQWltOiBCdWlsZCBtb2RlbCB0byBwcmVkaWN0IGJpby1hY3Rpdml0eSBiYXNlZCBvbiBnZW5lIGV4cHJlc3Npb24gaW4gbGl2ZXIgY2VsbCBsaW5lLgoKIyMjIERhdGEKCi0gMzAgY2hlbWljYWwgY29tcG91bmRzIGhhdmUgYmVlbiBzY3JlZW5lZCBmb3IgdG94aWNpdHkKCi0gQmlvYXNzYXkgZGF0YSBvbiB0b3hpY2l0eSBzY3JlZW5pbmcKCi0gR2VuZSBleHByZXNzaW9ucyBpbiBhIGxpdmVyIGNlbGwgbGluZSBhcmUgcHJvZmlsZWQgZm9yIGVhY2ggY29tcG91bmQgKDQwMDAgZ2VuZXMpCgoKYGBge3J9CnRveERhdGEgPC0gcmVhZF9jc3YoCiAgImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zdGF0T21pY3MvSERBMjAyMC9kYXRhL3RveERhdGFDZW50ZXJlZC5jc3YiLAogIGNvbF90eXBlcyA9IGNvbHMoKQopCnN2ZFggPC0gc3ZkKHRveERhdGFbLC0xXSkKYGBgCgpEYXRhIGlzIGFscmVhZHkgY2VudGVyZWQ6CgpgYGB7cn0KdG94RGF0YSAlPiUKICBjb2xNZWFucyAlPiUKICByYW5nZQpgYGAKCmBgYHtyfQogdG94RGF0YSAlPiUKICBuYW1lcyAlPiUKICBoZWFkCmBgYAoKLSBGaXJzdCBjb2x1bW4gY29udGFpbnMgZGF0YSBvbiBCaW9hc3NheS4KLSBUaGUgaGlnaGVyIHRoZSBzY29yZSBvbiBCaW9hc3NheSB0aGUgbW9yZSB0b3hpYyB0aGUgY29tcG91bmQKLSBPdGhlciBjb2x1bW5zIGNvbnRhaW4gZGF0YSBvbiBnZW5lIGV4cHJlc3Npb24gWDEsIC4uLiAsIFg0MDAwCgojIyMgRGF0YSBleHBsb3JhdGlvbgoKYGBge3J9CnRveERhdGEgJT4lCiAgZ2dwbG90KGFlcyh4PSIiLHk9QkEpKSArCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGU9TkEpICsKICBnZW9tX3BvaW50KHBvc2l0aW9uPSJqaXR0ZXIiKQpgYGAKCmBgYHtyfQpzdmRYIDwtIHRveERhdGFbLC0xXSAlPiUKICBzdmQKCmsgPC0gMgpWayA8LSBzdmRYJHZbLDE6a10KVWsgPC0gc3ZkWCR1WywxOmtdCkRrIDwtIGRpYWcoc3ZkWCRkWzE6a10pClprIDwtIFVrJSolRGsKY29sbmFtZXMoWmspIDwtIHBhc3RlMCgiWiIsMTprKQpjb2xuYW1lcyhWaykgPC0gcGFzdGUwKCJWIiwxOmspCgpaayAlPiUKICBhcy5kYXRhLmZyYW1lICU+JQogIG11dGF0ZShCQSA9IHRveERhdGEgJT4lIHB1bGwoQkEpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9IFoxLCB5ID0gWjIsIGNvbG9yID0gQkEpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIHNjYWxlX2NvbG91cl9ncmFkaWVudDIobG93ID0gImJsdWUiLG1pZD0id2hpdGUiLGhpZ2g9InJlZCIpICsKICBnZW9tX3BvaW50KHNpemUgPSAzLCBwY2ggPSAyMSwgY29sb3IgPSAiYmxhY2siKQpgYGAKCi0gU2NvcmVzIG9uIHRoZSBmaXJzdCB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMgKG9yIE1EUyBwbG90KS4KLSBFYWNoIHBvaW50IGNvcnJlc3BvbmRzIHRvIGEgY29tcG91bmQuCi0gQ29sb3IgY29kZSByZWZlcnMgdG8gdGhlIHRveGljaXR5IHNjb3JlIChoaWdoZXIgc2NvcmUgbW9yZSB0b3hpYykuCi0gQ2xlYXIgc2VwYXJhdGlvbiBiZXR3ZWVuIGNvbXBvdW5kcyBhY2NvcmRpbmcgdG8gdG94aWNpdHkuCgotLS0KCi0gTmV4dCBsb2dpYyBzdGVwIGluIGEgUENBIGlzIHRvIGludGVycHJldCB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMuCi0gV2UgdGh1cyBoYXZlIHRvIGFzc2VzcyB0aGUgbG9hZGluZ3MuCi0gV2UgY2FuIGFkZCBhIHZlY3RvciBmb3IgZWFjaCBnZW5lIHRvIGdldCBhIGJpcGxvdCwgYnV0IHRoaXMgd291bGQgcmVxdWlyZSBwbG90dGluZyA0MDAwIHZlY3RvcnMsIHdoaWNoIHdvdWxkIHJlbmRlciB0aGUgcGxvdCB1bnJlYWRhYmxlLgoKQWx0ZXJuYXRpdmUgZ3JhcGggdG8gbG9vayBhdCB0aGUgbWFueSBsb2FkaW5ncyBvZiB0aGUgZmlyc3QgdHdvIFBDcy4KCmBgYHtyfQpncmlkLmFycmFuZ2UoCiAgVmsgJT4lCiAgICBhcy5kYXRhLmZyYW1lICU+JQogICAgbXV0YXRlKGdlbmVJRCA9IDE6bnJvdyhWaykpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gZ2VuZUlELCB5ID0gVjEpKSArCiAgICBnZW9tX3BvaW50KHBjaD0yMSkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gYygtMiwwLDIpKnNkKFZrWywxXSksIGNvbCA9ICJyZWQiKSAsCiAgVmsgJT4lCiAgICBhcy5kYXRhLmZyYW1lICU+JQogICAgbXV0YXRlKGdlbmVJRCA9IDE6bnJvdyhWaykpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gZ2VuZUlELCB5ID0gVjIpKSArCiAgICBnZW9tX3BvaW50KHBjaD0yMSkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gYygtMiwwLDIpKnNkKFZrWywyXSksIGNvbCA9ICJyZWQiKSwKICBuY29sPTIpCmBgYAoKLSBJdCBpcyBhbG1vc3QgaW1wb3NzaWJsZSB0byBpbnRlcnByZXQgdGhlIFBDcyBiZWNhdXNlIHRoZXJlIGFyZSA0MDAwIGdlbmVzIGNvbnRyaWJ1dGluZyB0byBlYWNoIFBDLgoKLSBJbiBhbiBhdHRlbXB0IHRvIGZpbmQgdGhlIG1vc3QgaW1wb3J0YW50IGdlbmVzIChpbiB0aGUgc2Vuc2UgdGhhdCB0aGV5IGRyaXZlIHRoZSBpbnRlcnByZXRhdGlvbiBvZiB0aGUgUENzKSwgdGhlIHBsb3RzIHNob3cgaG9yaXpvbnRhbCByZWZlcmVuY2UgbGluZXM6IHRoZSBhdmVyYWdlIG9mIHRoZSBsb2FkaW5ncywgYW5kIHRoZSBhdmVyYWdlIMKxIHR3aWNlIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIGxvYWRpbmdzLiBJbiBiZXR3ZWVuIHRoZSBsaW5lcyB3ZSBleHBlY3RzIGFib3V0IDk1JSBvZiB0aGUgbG9hZGluZ3MgKGlmIHRoZXkgd2VyZSBub3JtYWxseSBkaXN0cmlidXRlZCkuCgotIFRoZSBwb2ludHMgb3V0c2lkZSB0aGUgYmFuZCBjb21lIGZyb20gdGhlIGdlbmVzIHRoYXQgaGF2ZSByYXRoZXIgbGFyZ2UgbG9hZGluZ3MgKGluIGFic29sdXRlIHZhbHVlKSBhbmQgaGVuY2UgYXJlIGltcG9ydGFudCBmb3IgdGhlIGludGVycHJldGF0aW9uIG9mIHRoZSBQQ3MuCgotIE5vdGUsIHRoYXQgcGFydGljdWxhcmx5IGZvciB0aGUgZmlyc3QgUEMsIG9ubHkgYSBmZXcgZ2VuZXMgc2hvdyBhIG1hcmtlZGx5IGxhcmdlIGxvYWRpbmdzIHRoYXQgYXJlIG5lZ2F0aXZlLiBUaGlzIG1lYW5zIHRoYXQgYW4gdXByZWd1bGF0aW9uIG9mIHRoZXNlIGdlbmVzIHdpbGwgbGVhZCB0byBsb3cgc2NvcmVzIG9uIFBDMS4KLSBUaGVzZSBnZW5lcyB3aWxsIHZlcnkgbGlrZWx5IHBsYXkgYW4gaW1wb3J0YW50IHJvbGUgaW4gdGhlIHRveGljaXR5IG1lY2hhbmlzbS4KLSBJbmRlZWQsIGxvdyBzY29yZXMgb24gUEMxIGFyZSBpbiB0aGUgZGlyZWN0aW9uIG9mIG1vcmUgdG94aWNpdHkuCi0gSW4gdGhlIG5leHQgY2hhcHRlciB3ZSB3aWxsIGludHJvZHVjZSBhIG1ldGhvZCB0byBvYnRhaW4gc3BhcnNlIFBDcy4KCiMjIyBQcmVkaWN0aW9uIG1vZGVsCgpgYGB7cn0KbTEgPC0gbG0oQkEgfiAtMSArIC4sIHRveERhdGEpCgptMSAlPiUKICBjb2VmICU+JQogIGhlYWQoNDApCgptMSAlPiUKICBjb2VmICU+JQogIGlzLm5hICU+JQogIHN1bQoKc3VtbWFyeShtMSkkci5zcXVhcmVkCmBgYAoKUHJvYmxlbT8/CgojIyBCcmFpbiBleGFtcGxlCgotIENvdXJ0ZXN5IHRvIFNvbG9tb24gS3Vyei4gU3RhdGlzdGljYWwgcmV0aGlua2luZyB3aXRoIGJybXMsIGdncGxvdDIsIGFuZCB0aGUgdGlkeXZlcnNlIHZlcnNpb24gMS4yLjAuCgpodHRwczovL2Jvb2tkb3duLm9yZy9jb250ZW50LzM4OTAvCmh0dHBzOi8vZ2l0aHViLmNvbS9BU0t1cnovU3RhdGlzdGljYWxfUmV0aGlua2luZ193aXRoX2JybXNfZ2dwbG90Ml9hbmRfdGhlX3RpZHl2ZXJzZQoKLSBEYXRhIHdpdGggYnJhaW4gc2l6ZSBhbmQgYm9keSBzaXplIGZvciBzZXZlbiBzcGVjaWVzCgpgYGB7cn0KYnJhaW4gPC0KdGliYmxlKHNwZWNpZXMgPSBjKCJhZmFyZW5zaXMiLCAiYWZyaWNhbnVzIiwgImhhYmlsaXMiLCAiYm9pc2VpIiwgInJ1ZG9sZmVuc2lzIiwgImVyZ2FzdGVyIiwgInNhcGllbnMiKSwKICAgICAgIGJyYWluICAgPSBjKDQzOCwgNDUyLCA2MTIsIDUyMSwgNzUyLCA4NzEsIDEzNTApLAogICAgICAgbWFzcyAgICA9IGMoMzcuMCwgMzUuNSwgMzQuNSwgNDEuNSwgNTUuNSwgNjEuMCwgNTMuNSkpCmBgYAoKIyMjIERhdGEgZXhwbG9yYXRpb24KCmBgYHtyfQpicmFpbgoKcCA8LSBicmFpbiAlPiUKICBnZ3Bsb3QoYWVzKHggPSAgbWFzcywgeSA9IGJyYWluLCBsYWJlbCA9IHNwZWNpZXMpKSArCiAgZ2VvbV9wb2ludCgpCgpwICsgZ2VvbV90ZXh0KG51ZGdlX3kgPSA0MCkKYGBgCgojIyMgTW9kZWxzCgpTaXggbW9kZWxzIHJhbmdlIGluIGNvbXBsZXhpdHkgZnJvbSB0aGUgc2ltcGxlIHVuaXZhcmlhdGUgbW9kZWwKClxiZWdpbnthbGlnbip9Clx0ZXh0e2JyYWlufV9pICYgXHNpbSBcb3BlcmF0b3JuYW1le05vcm1hbH0gKFxtdV9pLCBcc2lnbWEpIFxcClxtdV9pICYgPSBcYmV0YV8wICsgXGJldGFfMSBcdGV4dHttYXNzfV9pLApcZW5ke2FsaWduKn0KCnRvIHRoZSBkaXp6eWluZyBzaXh0aC1kZWdyZWUgcG9seW5vbWlhbCBtb2RlbAoKXGJlZ2lue2FsaWduKn0KXHRleHR7YnJhaW59X2kgJiBcc2ltIFxvcGVyYXRvcm5hbWV7Tm9ybWFsfSAoXG11X2ksIFxzaWdtYSkgXFwKXG11X2kgJiA9IFxiZXRhXzAgKyBcYmV0YV8xIFx0ZXh0e21hc3N9X2kgKyBcYmV0YV8yIFx0ZXh0e21hc3N9X2leMiArIFxiZXRhXzMgXHRleHR7bWFzc31faV4zICsgXGJldGFfNCBcdGV4dHttYXNzfV9pXjQgKyBcYmV0YV81IFx0ZXh0e21hc3N9X2leNSArIFxiZXRhXzYgXHRleHR7bWFzc31faV42LgpcZW5ke2FsaWduKn0KCmBgYHtyLCBtZXNzYWdlID0gRiwgd2FybmluZyA9IEZ9CmZvcm11bGFzIDwtIHNhcHBseSgxOjYsIGZ1bmN0aW9uKGkpCiAgcmV0dXJuKAogICAgIHBhc3RlMCgiSShtYXNzXiIsMTppLCIpIikgJT4lIHBhc3RlKGNvbGxhcHNlPSIgKyAiKQogICAgKQopCgpmb3JtdWxhcyA8LSBzYXBwbHkoCiAgcGFzdGUwKCJicmFpbiB+ICIsIGZvcm11bGFzKSwKICBhcy5mb3JtdWxhKQoKbW9kZWxzIDwtIGxhcHBseShmb3JtdWxhcywgbG0gLCBkYXRhID0gYnJhaW4pCmBgYAoKYGBge3J9CmRhdGEuZnJhbWUoCiAgZm9ybXVsYT1mb3JtdWxhcyAlPiUKICAgIGFzLmNoYXJhY3RlciwKICByMiA9IHNhcHBseSgKICAgIG1vZGVscywKICAgIGZ1bmN0aW9uKG1vZCkgc3VtbWFyeShtb2QpJHIuc3F1YXJlZCkKICApICAlPiUKICBnZ3Bsb3QoCiAgICBhZXMoeCA9IHIyLAogICAgICB5ID0gZm9ybXVsYSwKICAgICAgbGFiZWwgPSByMiAlPiUKICAgICAgICByb3VuZCgyKSAlPiUKICAgICAgICBhcy5jaGFyYWN0ZXIpCiAgKSArCiAgZ2VvbV90ZXh0KCkKYGBgCgpXZSBwbG90IHRoZSBmaXQgZm9yIGVhY2ggbW9kZWwgaW5kaXZpZHVhbGx5IGFuZCB0aGVtIGFycmFuZ2UgdGhlbSB0b2dldGhlciBpbiBvbmUgcGxvdC4KCmBgYHtyfQpwbG90cyA8LSBsYXBwbHkoMTo2LCBmdW5jdGlvbihpKQp7CiAgcCArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHkgfiBwb2x5KHgsaSkpICsKICBnZ3RpdGxlKAogICAgcGFzdGUwKAogICAgICAicjIgPSAiLAogICAgICByb3VuZChzdW1tYXJ5KG1vZGVsc1tbaV1dKSRyLnNxdWFyZWQqMTAwLDEpLAogICAgICAiJSIpCiAgICApCn0pCgpkby5jYWxsKCJncmlkLmFycmFuZ2UiLGMocGxvdHMsIG5jb2wgPSAzKSkKYGBgCgotIFdlIGNsZWFybHkgc2VlIHRoYXQgaW5jcmVhc2luZyB0aGUgbW9kZWwgY29tcGxleGl0eSBhbHdheXMgcHJvZHVjZXMgYSBmaXQgd2l0aCBhIHNtYWxsZXIgU1NFLgotIFRoZSBwcm9ibGVtIG9mIG92ZXJmaXR0aW5nIGlzIHZlcnkgb2J2aW91cy4gVGhlIG1vcmUgY29tcGxleCBwb2x5bm9taWFsIG1vZGVscyB3aWxsIG5vdCBnZW5lcmFsaXNlIHdlbGwgZm9yIHByZWRpY3Rpb24hCi0gV2UgZXZlbiBoYXZlIGEgbW9kZWwgdGhhdCBmaXRzIHRoZSBkYXRhIHBlcmZlY3RseSwgYnV0IHRoYXQgd2lsbCBtYWtlIHZlcnkgYWJzdXJkIHByZWRpdGlvbnMhCgotIFRvbyBmZXcgcGFyYW1ldGVycyBodXJ0cywgdG9vLiBGaXQgdGhlIHVuZGVyZml0IGludGVyY2VwdC1vbmx5IG1vZGVsLgoKYGBge3J9Cm0wIDwtIGxtKGJyYWluIH4gMSwgYnJhaW4pCnN1bW1hcnkobTApCgpwICsKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IDEpICsKICBnZ3RpdGxlKAogICAgcGFzdGUwKAogICAgICAicjIgPSAiLAogICAgICByb3VuZChzdW1tYXJ5KG0wKSRyLnNxdWFyZWQqMTAwLDEpLAogICAgICAiJSIpCiAgICApCmBgYAoKVGhlIHVuZGVyZml0IG1vZGVsIGRpZCBub3QgbGVhcm4gYW55dGhpbmcgYWJvdXQgdGhlIHJlbGF0aW9uIGJldHdlZW4gbWFzcyBhbmQgYnJhaW4uIEl0IHdvdWxkIGFsc28gZG8gYSB2ZXJ5IHBvb3Igam9iIGZvciBwcmVkaWN0aW5nIG5ldyBkYXRhLgoKIyMgT3ZlcnZpZXcKCldlIHdpbGwgbWFrZSBhIGRpc3RpbmN0aW9uIGJldHdlZW4gY29udGludW91cyBhbmQgZGlzY3JldGUgb3V0Y29tZXMuIEluIHRoaXMgY291cnNlIHdlIGZvY3VzIG9uCgotIExpbmVhciByZWdyZXNzaW9uIG1vZGVscyBmb3IgY29udGlub3VzIG91dGNvbWVzCgogIC0gUGVuYWxpc2VkIHJlZ3Jlc3Npb246IExhc3NvIGFuZCByaWRnZQogIC0gUHJpbmNpcGFsIGNvbXBvbmVudCByZWdyZXNzaW9uIChQQ1IpCgotIExvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzIGZvciBiaW5hcnkgb3V0Y29tZXMKCiAgLSBQZW5hbGlzZWQgcmVncmVzc2lvbjogTGFzc28gYW5kIHJpZGdlCgpGb3IgYWxsIHR5cGVzIG9mIG1vZGVsLCB3ZSB3aWxsIGRpc2N1c3MgZmVhdHVyZSBzZWxlY3Rpb24gbWV0aG9kcy4KCiMgTGluZWFyIFJlZ3Jlc3Npb24gZm9yIEhpZ2ggRGltZW5zaW9uYWwgRGF0YQoKQ29uc2lkZXIgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgKGZvciBkb3VibGUgY2VudGVyZWQgZGF0YSkKXFsKICBZX2kgPSBcYmV0YV8xWF97aTF9ICsgXGJldGFfMiBYX3tpMn0gKyBcY2RvdHMgKyBcYmV0YV9wWF97aXB9ICsgXGVwc2lsb25faSAsClxdCndpdGggJFx0ZXh0e0V9XGxlZnRbXGVwc2lsb24gXG1pZCBcbWF0aGJme1h9XHJpZ2h0XT0wJCBhbmQgJFx0ZXh0e3Zhcn1cbGVmdFtcZXBzaWxvbiBcbWlkIFxtYXRoYmZ7WH1ccmlnaHRdPVxzaWdtYV4yJC4KCkluIG1hdHJpeCBub3RhdGlvbiB0aGUgbW9kZWwgYmVjb21lcwpcWwogIFxtYXRoYmZ7WX0gPSBcbWF0aGJme1h9XG1hdGhiZlxiZXRhICsgXG1hdGhiZlxlcHNpbG9uLgpcXQpUaGUgbGVhc3Qgc3F1YXJlcyBlc3RpbWF0b3Igb2YgJFxtYXRoYmZcYmV0YSQgaXMgZ2l2ZW4gYnkKXFsKICBcaGF0e1xtYXRoYmZcYmV0YX0gPSAoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFxtYXRoYmZ7WX0gLApcXQphbmQgdGhlIHZhcmlhbmNlIG9mICRcaGF0e1xtYXRoYmZcYmV0YX0kIGVxdWFscwpcWwogIFx0ZXh0e3Zhcn1cbGVmdFtcaGF0e1xtYXRoYmZcYmV0YX1ccmlnaHRdID0gKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cc2lnbWFeMi4KXF0KJFxsb25ncmlnaHRhcnJvdyQgdGhlICRwIFx0aW1lcyBwJCBtYXRyaXggJChcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9JCBpcyBjcnVjaWFsCgpOb3RlLCB0aGF0CgotIHdpdGggZG91YmxlIGNlbnRlcmVkIGRhdGEgaXQgaXMgbWVhbnQgdGhhdCBib3RoIHRoZSByZXNwb25zZXMgYXJlIGNlbnRlcmVkIChtZWFuIG9mICRcbWF0aGJme1l9JCBpcyB6ZXJvKSBhbmQgdGhhdCBhbGwgcHJlZGljdG9ycyBhcmUgY2VudGVyZWQgKGNvbHVtbnMgb2YgJFxtYXRoYmZ7WH0kIGhhdmUgemVybyBtZWFuKS4gV2l0aCBkb3VibGUgY2VudGVyZWQgZGF0YSB0aGUgaW50ZXJjZXB0IGluIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgaXMgYWx3YXlzIGV4YWN0bHkgZXF1YWwgdG8gemVybyBhbmQgaGVuY2UgdGhlIGludGVyY2VwdCBtdXN0IG5vdCBiZSBpbmNsdWRlZCBpbiB0aGUgbW9kZWwuCgotIHdlIGRvIG5vdCBhc3N1bWUgdGhhdCB0aGUgcmVzaWR1YWxzIGFyZSBub3JtYWxseSBkaXN0cmlidXRlZC4gRm9yIHByZWRpY3Rpb24gcHVycG9zZXMgdGhpcyBpcyBvZnRlbiBub3QgcmVxdWlyZWQgKG5vcm1hbGl0eSBpcyBwYXJ0aWN1bGFybHkgaW1wb3J0YW50IGZvciBzdGF0aXN0aWNhbCBpbmZlcmVuY2UgaW4gc21hbGwgc2FtcGxlcykuCgojIyBMaW5lYXIgUmVncmVzc2lvbiBmb3IgbXVsdGl2YXJpYXRlIGRhdGEgdnMgSGlnaCBEaW1lbnNpb25hbCBEYXRhCgotICRcbWF0aGJme1heVFh9JCBhbmQgJChcbWF0aGJme1heVFh9KV57LTF9JCBhcmUgJHAgXHRpbWVzIHAkIG1hdHJpY2VzCgotICRcbWF0aGJme1heVFh9JCBjYW4gb25seSBiZSBpbnZlcnRlZCBpZiBpdCBoYXMgcmFuayAkcCQKCi0gUmFuayBvZiBhIG1hdHJpeCBvZiBmb3JtICRcbWF0aGJme1heVFh9JCwgd2l0aCAkXG1hdGhiZntYfSQgYW5kICRuXHRpbWVzIHAkIG1hdHJpeCwgY2FuIG5ldmVyIGJlIGxhcmdlciB0aGFuICRcbWluKG4scCkkLgoKLSBpbiBtb3N0IHJlZ3Jlc3Npb24gcHJvYmxlbXMgJG4+cCQgYW5kIHJhbmsgb2YgJChcbWF0aGJme1heVFh9KSQgZXF1YWxzICRwJAoKLSBpbiBoaWdoIGRpbWVuc2lvbmFsIHJlZ3Jlc3Npb24gcHJvYmxlbXMgJHAgPj4+IG4kIGFuZCByYW5rIG9mICQoXG1hdGhiZntYXlRYfSkkIGVxdWFscyAkbjxwJAoKLSBpbiB0aGUgdG94aWNvZ2Vub21pY3MgZXhhbXBsZSAkbj0zMDxwPTQwMDAkIGFuZCAkXHRleHR7cmFua30oXG1hdGhiZntYXlRYfSlcbGVxIG49MzAkLgogICRcbG9uZ3JpZ2h0YXJyb3ckICQoXG1hdGhiZntYXlRYfSleey0xfSQgZG9lcyBub3QgZXhpc3QsIGFuZCBuZWl0aGVyIGRvZXMgJFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fSQuCgojIyBDYW4gU1ZEIGhlbHA/CiAgLSBTaW5jZSB0aGUgY29sdW1ucyBvZiAkXG1hdGhiZntYfSQgYXJlIGNlbnRlcmVkLCAkXG1hdGhiZntYXlRYfSBccHJvcHRvIFx0ZXh0e3Zhcn1cbGVmdFtcbWF0aGJme1h9XHJpZ2h0XSQuCgogIC0gaWYgJFx0ZXh0e3Jhbmt9KFxtYXRoYmZ7WF5UWH0pPW49MzAkLCB0aGUgUENBIHdpbGwgZ2l2ZSAzMCBjb21wb25lbnRzLCBlYWNoIGJlaW5nIGEgbGluZWFyIGNvbWJpbmF0aW9uIG9mICRwPTQwMDAkIHZhcmlhYmxlcy4gVGhlc2UgMzAgUENzIGNvbnRhaW4gYWxsIGluZm9ybWF0aW9uIHByZXNlbnQgaW4gdGhlIG9yaWdpbmFsICRcbWF0aGJme1h9JCBkYXRhLgoKICAtIGlmICRcdGV4dHtyYW5rfShcbWF0aGJme1h9KT1uPTMwJCwgdGhlIFNWRCBvZiAkXG1hdGhiZntYfSQgaXMgZ2l2ZW4gYnkKICBcWwogICBcbWF0aGJme1h9ID0gXHN1bV97aT0xfV5uIFxkZWx0YV9pIFxtYXRoYmZ7dX1faSBcbWF0aGJme3Z9X2leVCA9IFxtYXRoYmZ7VX0gXGJvbGRzeW1ib2x7XERlbHRhfSBcbWF0aGJme1Z9XlQgPSBcbWF0aGJme1pWfV5ULAogIFxdCiAgd2l0aCAkXG1hdGhiZntafSQgdGhlICRuXHRpbWVzIG4kIG1hdHJpeCB3aXRoIHRoZSBzY29yZXMgb24gdGhlICRuJCBQQ3MuCgogIC0gU3RpbGwgcHJvYmxlbWF0aWMgYmVjYXVzZSBpZiB3ZSB1c2UgYWxsIFBDcyAkbj1wJC4KCgojIFByaW5jaXBhbCBDb21wb25lbnQgUmVncmVzc2lvbgoKQSBwcmluY2lwYWwgY29tcG9uZW50IHJlZ3Jlc3Npb24gKFBDUikgY29uc2lzdHMgb2YKCjEuIHRyYW5zZm9ybWluZyAkcD00MDAwJCBkaW1lbnNpb25hbCAkWCQtdmFyaWFibGUgdG8gdGhlICRuPTMwJCBkaW1lbnNpb25hbCAkWiQtdmFyaWFibGUgKFBDIHNjb3JlcykuIFRoZSAkbiQgUENzIGFyZSBtdXR1YWxseSB1bmNvcnJlbGF0ZWQuCgoyLiB1c2luZyB0aGUgJG4kIFBDLXZhcmlhYmxlcyBhcyByZWdyZXNzb3JzIGluIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwKCjMuIHBlcmZvcm1pbmcgZmVhdHVyZSBzZWxlY3Rpb24gdG8gc2VsZWN0IHRoZSBtb3N0IGltcG9ydGFudCByZWdyZXNzb3JzIChQQykuCgpGZWF0dXJlIHNlbGVjdGlvbiBpcyBrZXksIGJlY2F1c2Ugd2UgZG9uJ3Qgd2FudCB0byBoYXZlIGFzIG1hbnkgcmVncmVzc29ycyBhcyB0aGVyZSBhcmUgb2JzZXJ2YXRpb25zIGluIHRoZSBkYXRhLiBUaGlzIHdvdWxkIHJlc3VsdCBpbiB6ZXJvIHJlc2lkdWFsIGRlZ3JlZXMgb2YgZnJlZWRvbS4gKHNlZSBsYXRlcikKCi0tLQoKVG8ga2VlcCB0aGUgZXhwb3NpdGlvbiBnZW5lcmFsIHNvIHRoYXQgd2UgYWxsb3cgZm9yIGEgZmVhdHVyZSBzZWxlY3Rpb24gdG8gaGF2ZSB0YWtlbiBwbGFjZSwgSSB1c2UgdGhlIG5vdGF0aW9uICRcbWF0aGJme1V9X1MkIHRvIGRlbm90ZSBhIG1hdHJpeCB3aXRoIGxlZnQtc2luZ3VsYXIgY29sdW1uIHZlY3RvcnMgJFxtYXRoYmZ7dX1faSQsIHdpdGggJGkgXGluIHtcY2Fse1N9fSQgKCR7XGNhbHtTfX0kIGFuIGluZGV4IHNldCByZWZlcnJpbmcgdG8gdGhlIFBDcyB0byBiZSBpbmNsdWRlZCBpbiB0aGUgcmVncmVzc2lvbiBtb2RlbCkuCgpGb3IgZXhhbXBsZSwgc3VwcG9zZSB0aGF0IGEgZmVhdHVyZSBzZWxlY3Rpb24gbWV0aG9kIGhhcyByZXN1bHRlZCBpbiB0aGUgc2VsZWN0aW9uIG9mIFBDcyAxLCAzIGFuZCAxMiBmb3IgaW5jbHVzaW9uIGluIHRoZSBwcmVkaWN0aW9uIG1vZGVsLCB0aGVuICR7XGNhbHtTfX09XHsxLDMsMTJcfSQgYW5kClxbCiBcbWF0aGJme1V9X1MgPSBcYmVnaW57cG1hdHJpeH0KICBcbWF0aGJme3V9XzEgJiBcbWF0aGJme3V9XzMgJiBcbWF0aGJme3V9X3sxMn0KIFxlbmR7cG1hdHJpeH0uClxdCgotLS0KCiMjIyBFeGFtcGxlIG1vZGVsIGJhc2VkIG9uIGZpcnN0IDQgUENzCgpgYGB7cn0KayA8LSAzMApVayA8LSBzdmRYJHVbLDE6a10KRGsgPC0gZGlhZyhzdmRYJGRbMTprXSkKWmsgPC0gVWslKiVEawpZIDwtIHRveERhdGEgJT4lCiAgcHVsbChCQSkKCm00IDwtIGxtKFl+WmtbLDE6NF0pCnN1bW1hcnkobTQpCmBgYAoKTm90ZToKCi0gdGhlIGludGVyY2VwdCBpcyBlc3RpbWF0ZWQgYXMgemVyby4gKFdoeT8pIFRoZSBtb2RlbCBjb3VsZCBoYXZlIGJlZW4gZml0dGVkIGFzCgpgYGAKbTQgPC0gbG0oWX4tMStaa1ssMTo0XSkKYGBgCgotIHRoZSBQQy1wcmVkaWN0b3JzIGFyZSB1bmNvcnJlbGF0ZWQgKGJ5IGNvbnN0cnVjdGlvbikKCi0gZmlyc3QgUEMtcHJlZGljdG9ycyBhcmUgbm90IG5lY2Vzc2FyaWx5IHRoZSBtb3N0IGltcG9ydGFudCBwcmVkaWN0b3JzCgotICRwJC12YWx1ZXMgYXJlIG5vdCB2ZXJ5IG1lYW5pbmdmdWwgd2hlbiBwcmVkaWN0aW9uIGlzIHRoZSBvYmplY3RpdmUKCk1ldGhvZHMgZm9yIGZlYXR1cmUgc2VsZWN0aW9uIHdpbGwgYmUgZGlzY3Vzc2VkIGxhdGVyLgoKIyBSaWRnZSBSZWdyZXNzaW9uCgojIyBQZW5hbHR5CgogVGhlIHJpZGdlIHBhcmFtZXRlciBlc3RpbWF0b3IgaXMgZGVmaW5lZCBhcyB0aGUgcGFyYW1ldGVyICRcbWF0aGJmXGJldGEkIHRoYXQgbWluaW1pc2VzIHRoZSAqKnBlbmFsaXNlZCBsZWFzdCBzcXVhcmVzIGNyaXRlcmlvbioqCgogXFsKIFx0ZXh0e1NTRX1fXHRleHR7cGVufT1cVmVydFxtYXRoYmZ7WX0gLSBcbWF0aGJme1hcYmV0YX1cVmVydF8yXjIgKyBcbGFtYmRhIFxWZXJ0IFxib2xkc3ltYm9se1xiZXRhfSBcVmVydF8yXjIKXF0KCi0gJFxWZXJ0IFxib2xkc3ltYm9se1xiZXRhfSBcVmVydF8yXjI9XHN1bV97aj0xfV5wIFxiZXRhX2peMiQgaXMgdGhlICoqJExfMiQgcGVuYWx0eSB0ZXJtKioKCi0gJFxsYW1iZGE+MCQgaXMgdGhlIHBlbmFsdHkgcGFyYW1ldGVyICh0byBiZSBjaG9zZW4gYnkgdGhlIHVzZXIpLgoKTm90ZSwgdGhhdCB0aGF0IGlzIGVxdWl2YWxlbnQgdG8gbWluaW1pemluZwpcWwpcVmVydFxtYXRoYmZ7WX0gLSBcbWF0aGJme1hcYmV0YX1cVmVydF8yXjIgXHRleHR7IHN1YmplY3QgdG8gfSBcVmVydCBcYm9sZHN5bWJvbHtcYmV0YX1cVmVydF4yXzJcbGVxIHMKXF0KCk5vdGUsIHRoYXQgJHMkIGhhcyBhIG9uZS10by1vbmUgY29ycmVzcG9uZGVuY2Ugd2l0aCAkXGxhbWJkYSQKCiMjIEdyYXBoaWNhbCBpbnRlcnByZXRhdGlvbgoKYGBge3IgZWNobyA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KbGlicmFyeShnZ2ZvcmNlKQpsaWJyYXJ5KGxhdGV4MmV4cCkKbGlicmFyeShncmlkRXh0cmEpCgpwMSA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9lbGxpcHNlKGFlcyh4MCA9IDQsIHkwID0gMTEsIGEgPSAxMCwgYiA9IDMsIGFuZ2xlID0gcGkgLyA0KSkgKwogIGdlb21fZWxsaXBzZShhZXMoeDAgPSA0LCB5MCA9IDExLCBhID0gNSwgYiA9IDEuNSwgYW5nbGUgPSBwaSAvIDQpKSArCiAgeGxpbSgtMTIuNSwgMTIuNSkgKwogIHlsaW0oLTUsIDIwKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IDQsIHkgPSAxMSkpICsKICBhbm5vdGF0ZSgidGV4dCIsIGxhYmVsID0gVGVYKCIkKFxcaGF0e1xcYmV0YX1fMV57b2xzfSwgXFxoYXR7XFxiZXRhfV8yXntvbHN9KSQiKSwgeCA9IC01LCB5ID0gMTUsIHNpemUgPSA2LCBwYXJzZSA9IFRSVUUpICsKICB4bGFiKFRlWCgiJFxcYmV0YV8xJCIpKSArCiAgeWxhYihUZVgoIiRcXGJldGFfMiQiKSkgKwogIGdlb21fc2VnbWVudCgKICAgIGFlcyh4ID0gLTUsIHkgPSAxMi41LCB4ZW5kID0gMy43LCB5ZW5kID0gMTEuMyksCiAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yNSwgImNtIikpCiAgICApICsKICBjb29yZF9maXhlZCgpCgpwUmlkZ2UgPC0gcDEgKwogIGdlb21fY2lyY2xlKGFlcyh4MCA9IDAsIHkwID0gMCwgciA9IDMuOSkgLCBjb2xvciA9ICJyZWQiKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IC0xLjEsIHkgPSAzLjc1KSwgY29sb3IgPSAicmVkIikgKwogIGFubm90YXRlKCJ0ZXh0IiwgbGFiZWwgPSBUZVgoIiQoXFxoYXR7XFxiZXRhfV8xXntyaWRnZX0sIFxcaGF0e1xcYmV0YX1fMl57cmlkZ2V9KSQiKSwgeCA9IC04LjEsIHkgPSA0LjQ1LCBzaXplID0gNiwgcGFyc2UgPSBUUlVFLCBjb2xvciA9ICJyZWQiKSArCiAgZ2d0aXRsZSgiUmlkZ2UiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgY29sb3IgPSAiZ3JleSIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBjb2xvciA9ICJncmV5IikgKwogIHRoZW1lX21pbmltYWwoKQoKcFJpZGdlCmBgYAoKIyMgU29sdXRpb24KClRoZSBzb2x1dGlvbiBpcyBnaXZlbiBieQpcWwogIFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fSA9IChcbWF0aGJme1heVFh9K1xsYW1iZGEgXG1hdGhiZntJfSleey0xfSBcbWF0aGJme1heVCBZfS4KXF0KSXQgY2FuIGJlIHNob3duIHRoYXQgJChcbWF0aGJme1heVFh9K1xsYW1iZGEgXG1hdGhiZntJfSkkIGlzIGFsd2F5cyBvZiByYW5rICRwJCBpZiAkXGxhbWJkYT4wJC4KCkhlbmNlLCAkKFxtYXRoYmZ7WF5UWH0rXGxhbWJkYSBcbWF0aGJme0l9KSQgaXMgaW52ZXJ0aWJsZSBhbmQgJFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fSQgZXhpc3RzIGV2ZW4gaWYgJHA+Pj5uJC4KCldlIGFsc28gZmluZApcWwogIFx0ZXh0e3Zhcn1cbGVmdFtcaGF0e1xtYXRoYmZcYmV0YX1ccmlnaHRdID0gKFxtYXRoYmZ7WF5UWH0rXGxhbWJkYSBcbWF0aGJme0l9KV57LTF9IFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0gKFxtYXRoYmZ7WF5UWH0rXGxhbWJkYSBcbWF0aGJme0l9KV57LTF9XHNpZ21hXjIKXF0KCkhvd2V2ZXIsIGl0IGNhbiBiZSBzaG93biB0aGF0IGltcHJvdmVkIGludGVydmFscyB0aGF0IGFsc28gYWNjb3VudCBmb3IgdGhlIGJpYXMgY2FuIGJlIGNvbnN0cnVjdGVkIGJ5IHVzaW5nOgoKXFsKICBcdGV4dHt2YXJ9XGxlZnRbXGhhdHtcbWF0aGJmXGJldGF9XHJpZ2h0XSA9IChcbWF0aGJme1heVFh9K1xsYW1iZGEgXG1hdGhiZntJfSleey0xfSAgXHNpZ21hXjIuClxdCgojIyMgUHJvb2YKClRoZSBjcml0ZXJpb24gdG8gYmUgbWluaW1pc2VkIGlzCiAgXFsKICAgXHRleHR7U1NFfV9cdGV4dHtwZW59PVxWZXJ0XG1hdGhiZntZfSAtIFxtYXRoYmZ7WFxiZXRhfVxWZXJ0XzJeMiArIFxsYW1iZGEgXFZlcnQgXGJvbGRzeW1ib2x7XGJldGF9IFxWZXJ0XzJeMi4KIFxdCiBGaXJzdCB3ZSByZS1leHByZXNzIFNTRSBpbiBtYXRyaXggbm90YXRpb246CiBcWwogICBcdGV4dHtTU0V9X1x0ZXh0e3Blbn0gPSAoXG1hdGhiZntZfS1cbWF0aGJme1hcYmV0YX0pXlQoXG1hdGhiZntZfS1cbWF0aGJme1hcYmV0YX0pICsgXGxhbWJkYSBcYm9sZHN5bWJvbHtcYmV0YX1eVFxib2xkc3ltYm9se1xiZXRhfS4KIFxdCiBUaGUgcGFydGlhbCBkZXJpdmF0aXZlIHcuci50LiAkXGJvbGRzeW1ib2x7XGJldGF9JCBpcwogXFsKICAgXGZyYWN7XHBhcnRpYWx9e1xwYXJ0aWFsIFxib2xkc3ltYm9se1xiZXRhfX1cdGV4dHtTU0V9X1x0ZXh0e3Blbn0gPSAtMlxtYXRoYmZ7WH1eVChcbWF0aGJme1l9LVxtYXRoYmZ7WFxiZXRhfSkrMlxsYW1iZGFcYm9sZHN5bWJvbHtcYmV0YX0uCiBcXQogU29sdmluZyAkXGZyYWN7XHBhcnRpYWx9e1xwYXJ0aWFsIFxib2xkc3ltYm9se1xiZXRhfX1cdGV4dHtTU0V9X1x0ZXh0e3Blbn09MCQgZ2l2ZXMKIFxbCiAgIFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fSA9IChcbWF0aGJme1heVFh9K1xsYW1iZGEgXG1hdGhiZntJfSleey0xfSBcbWF0aGJme1heVCBZfS4KIFxdCiAoYXNzdW1wdGlvbjogJChcbWF0aGJme1heVFh9K1xsYW1iZGEgXG1hdGhiZntJfSkkIGlzIG9mIHJhbmsgJHAkLiBUaGlzIGlzIGFsd2F5cyB0cnVlIGlmICRcbGFtYmRhPjAkKQoKIyMgTGluayB3aXRoIFNWRAoKIyMjIFNWRCBhbmQgaW52ZXJzZQpXcml0ZSB0aGUgU1ZEIG9mICRcbWF0aGJme1h9JCAoJHA+biQpIGFzClxbCiAgIFxtYXRoYmZ7WH0gPSBcc3VtX3tpPTF9Xm4gXGRlbHRhX2kgXG1hdGhiZnt1fV9pIFxtYXRoYmZ7dn1faV5UID0gXHN1bV97aT0xfV5wIFxkZWx0YV9pIFxtYXRoYmZ7dX1faSBcbWF0aGJme3Z9X2leVCAgPSBcbWF0aGJme1V9XGJvbGRzeW1ib2x7XERlbHRhfSBcbWF0aGJme1Z9XlQgLApcXQp3aXRoCgotICRcZGVsdGFfe24rMX09XGRlbHRhX3tuKzJ9PSBcY2RvdHMgPSBcZGVsdGFfcD0wJAoKLSAkXGJvbGRzeW1ib2x7XERlbHRhfSQgYSAkcFx0aW1lcyBwJCBkaWFnb25hbCBtYXRyaXggb2YgdGhlICRcZGVsdGFfMSxcbGRvdHMsIFxkZWx0YV9wJAoKLSAgJFxtYXRoYmZ7VX0kIGFuICRuXHRpbWVzIHAkIG1hdHJpeCBhbmQgJFxtYXRoYmZ7Vn0kIGEgJHAgXHRpbWVzIHAkIG1hdHJpeC4gTm90ZSB0aGF0IG9ubHkgdGhlIGZpcnN0ICRuJCBjb2x1bW5zIG9mICRcbWF0aGJme1V9JCBhbmQgJFxtYXRoYmZ7Vn0kIGFyZSBpbmZvcm1hdGl2ZS4KCldpdGggdGhlIFNWRCBvZiAkXG1hdGhiZntYfSQgd2Ugd3JpdGUKIFxbCiAgIFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0gPSBcbWF0aGJme1Z9XGJvbGRzeW1ib2x7XERlbHRhCiAgICAgfV4yXG1hdGhiZntWfV5ULgogXF0KIFRoZSBpbnZlcnNlIG9mICRcbWF0aGJme1h9XlRcbWF0aGJme1h9JCBpcyB0aGVuIGdpdmVuIGJ5CiBcWwogICAoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfSA9IFxtYXRoYmZ7Vn1cYm9sZHN5bWJvbHtcRGVsdGF9XnstMn1cbWF0aGJme1Z9XlQuCiBcXQogU2luY2UgJFxib2xkc3ltYm9se1xEZWx0YX0kIGhhcyAkXGRlbHRhX3tuKzF9PVxkZWx0YV97bisyfT0gXGNkb3RzID0gXGRlbHRhX3A9MCQsIGl0IGlzIG5vdCBpbnZlcnRpYmxlLgoKIyMjIFNWRCBvZiBwZW5hbGlzZWQgbWF0cml4ICRcbWF0aGJme1heVFh9K1xsYW1iZGEgXG1hdGhiZntJfSQKCkl0IGNhbiBiZSBzaG93biB0aGF0ClxbCiAgXG1hdGhiZntYXlRYfStcbGFtYmRhIFxtYXRoYmZ7SX0gPSBcbWF0aGJme1Z9IChcYm9sZHN5bWJvbHtcRGVsdGF9XjIrXGxhbWJkYSBcbWF0aGJme0l9KSBcbWF0aGJme1Z9XlQgLApcXQppLmUuIGFkZGluZyBhIGNvbnN0YW50IHRvIHRoZSBkaWFnb25hbCBlbGVtZW50cyBkb2VzIG5vdCBhZmZlY3QgdGhlIGVpZ2VudmVjdG9ycywgYW5kIGFsbCBlaWdlbnZhbHVlcyBhcmUgaW5jcmVhc2VkIGJ5IHRoaXMgY29uc3RhbnQuCiRcbG9uZ3JpZ2h0YXJyb3ckIHplcm8gZWlnZW52YWx1ZXMgYmVjb21lICRcbGFtYmRhJC4KCkhlbmNlLApcWwogIChcbWF0aGJme1heVFh9K1xsYW1iZGEgXG1hdGhiZntJfSleey0xfSA9IFxtYXRoYmZ7Vn0gKFxib2xkc3ltYm9se1xEZWx0YX1eMitcbGFtYmRhIFxtYXRoYmZ7SX0pXnstMX0gXG1hdGhiZntWfV5UICwKXF0Kd2hpY2ggY2FuIGJlIGNvbXB1dGVkIGV2ZW4gd2hlbiBzb21lIGVpZ2VudmFsdWVzIGluICRcYm9sZHN5bWJvbHtcRGVsdGF9XjIkIGFyZSB6ZXJvLgoKTm90ZSwgdGhhdCBmb3IgaGlnaCBkaW1lbnNpb25hbCBkYXRhICgkcD4+Pm4kKSBtYW55IGVpZ2VudmFsdWVzIGFyZSB6ZXJvIGJlY2F1c2UgJFxtYXRoYmZ7WF5UWH0kIGlzIGEgJHAgXHRpbWVzIHAkIG1hdHJpeCBhbmQgaGFzIHJhbmsgJG4kLgoKVGhlIGlkZW50aXR5ICRcbWF0aGJme1heVFh9K1xsYW1iZGEgXG1hdGhiZntJfSA9IFxtYXRoYmZ7Vn0gKFxib2xkc3ltYm9se1xEZWx0YX1eMitcbGFtYmRhIFxtYXRoYmZ7SX0pIFxtYXRoYmZ7Vn1eVCQgaXMgZWFzaWx5IGNoZWNrZWQ6ClxbCiAgXG1hdGhiZntWfSAoXGJvbGRzeW1ib2x7XERlbHRhfV4yK1xsYW1iZGEgXG1hdGhiZntJfSkgXG1hdGhiZntWfV5UID0gXG1hdGhiZntWfVxib2xkc3ltYm9se1xEZWx0YX1eMlxtYXRoYmZ7Vn1eVCArIFxsYW1iZGEgXG1hdGhiZntWVn1eVCAgPSBcbWF0aGJme1Z9XGJvbGRzeW1ib2x7XERlbHRhfV4yXG1hdGhiZntWfV5UICsgXGxhbWJkYSBcbWF0aGJme0l9ID0gXG1hdGhiZntYXlRYfStcbGFtYmRhIFxtYXRoYmZ7SX0uClxdCgoKIyMgUHJvcGVydGllcwoKLSBUaGUgUmlkZ2UgZXN0aW1hdG9yIGlzIGJpYXNlZCEgVGhlICRcYm9sZHN5bWJvbHtcYmV0YX0kIGFyZSBzaHJ1bmtlbiB0byB6ZXJvIQpcYmVnaW57ZXFuYXJyYXl9CiBcdGV4dHtFfVtcaGF0e1xib2xkc3ltYm9se1xiZXRhfX1dICY9JiAoXG1hdGhiZntYXlRYfStcbGFtYmRhIFxtYXRoYmZ7SX0pXnstMX0gXG1hdGhiZntYfV5UIFx0ZXh0e0V9W1xtYXRoYmZ7WX1dXFwKJj0mIChcbWF0aGJme1h9XlRcbWF0aGJme1h9K1xsYW1iZGEgXG1hdGhiZntJfSleey0xfSBcbWF0aGJme1h9XlQgXG1hdGhiZntYfVxib2xkc3ltYm9se1xiZXRhfVxcClxlbmR7ZXFuYXJyYXl9CgotIE5vdGUsIHRoYXQgdGhlIHNocmlua2FnZSBpcyBsYXJnZXIgaW4gdGhlIGRpcmVjdGlvbiBvZiB0aGUgc21hbGxlciBlaWdlbnZhbHVlcy4KClxiZWdpbntlcW5hcnJheX0KXHRleHR7RX1bXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19XSY9JlxtYXRoYmZ7Vn0gKFxib2xkc3ltYm9se1xEZWx0YX1eMitcbGFtYmRhIFxtYXRoYmZ7SX0pXnstMX0gXG1hdGhiZntWfV5UIFxtYXRoYmZ7Vn0gXGJvbGRzeW1ib2x7XERlbHRhfV4yIFxtYXRoYmZ7Vn1eVFxib2xkc3ltYm9se1xiZXRhfVxcCiY9JlxtYXRoYmZ7Vn0gKFxib2xkc3ltYm9se1xEZWx0YX1eMitcbGFtYmRhIFxtYXRoYmZ7SX0pXnstMX0gXGJvbGRzeW1ib2x7XERlbHRhfV4yIFxtYXRoYmZ7Vn1eVFxib2xkc3ltYm9se1xiZXRhfVxcCiY9JiBcbWF0aGJme1Z9ClxsZWZ0W1xiZWdpbnthcnJheX17Y2NjfQpcZnJhY3tcZGVsdGFfMV4yfXtcZGVsdGFfMV4yK1xsYW1iZGF9JlxsZG90cyYwIFxcCiZcdmRvdHMmXFwKMCZcbGRvdHMmXGZyYWN7XGRlbHRhX3JeMn17XGRlbHRhX3JeMitcbGFtYmRhfQpcZW5ke2FycmF5fVxyaWdodF0KXG1hdGhiZntWfV5UXGJvbGRzeW1ib2x7XGJldGF9ClxlbmR7ZXFuYXJyYXl9CgotICB0aGUgdmFyaWFuY2Ugb2YgdGhlIHByZWRpY3Rpb24gJFxoYXR7e1l9fShcbWF0aGJme3h9KT1cbWF0aGJme3h9XlRcaGF0XGJldGEkLAogIFxbCiAgICBcdGV4dHt2YXJ9XGxlZnRbXGhhdHt7WX19KFxtYXRoYmZ7eH0pXG1pZCBcbWF0aGJme3h9XHJpZ2h0XSA9IFxtYXRoYmZ7eH1eVChcbWF0aGJme1heVFh9K1xsYW1iZGEgXG1hdGhiZntJfSleey0xfVxtYXRoYmZ7eH0KICBcXQogIGlzIHNtYWxsZXIgdGhhbiB3aXRoIHRoZSBsZWFzdC1zcXVhcmVzIGVzdGltYXRvci4KCi0gIHRocm91Z2ggdGhlIGJpYXMtdmFyaWFuY2UgdHJhZGUtb2ZmIGl0IGlzIGhvcGVkIHRoYXQgYmV0dGVyIHByZWRpY3Rpb25zIGluIHRlcm1zIG9mIGV4cGVjdGVkIGNvbmRpdGlvbmFsIHRlc3QgZXJyb3IgY2FuIGJlIG9idGFpbmVkLCBmb3IgYW4gYXBwcm9wcmlhdGUgY2hvaWNlIG9mICRcbGFtYmRhJC4KCgpSZWNhbGwgdGhlIGV4cHJlc3Npb24gb2YgdGhlIGV4cGVjdGVkIGNvbmRpdGlvbmFsIHRlc3QgZXJyb3IKXGJlZ2lue2VxbmFycmF5fQogIEVycihcbWF0aGJme3h9KSAmPSYgXHRleHR7RX1cbGVmdFsoXGhhdHtZfSAtIFleKileMlxtaWQgXG1hdGhiZnt4fVxyaWdodF1cXAogICY9JgogIFx0ZXh0e3Zhcn1cbGVmdFtcaGF0e1l9XG1pZCBcbWF0aGJme3h9XHJpZ2h0XSArIFx0ZXh0e2JpYXN9XjIoXG1hdGhiZnt4fSkrCiAgXHRleHR7dmFyfVxsZWZ0W1leKlxtaWQgXG1hdGhiZnt4fVxyaWdodF0KXGVuZHtlcW5hcnJheX0Kd2hlcmUKCi0gJFxoYXR7WX09XGhhdHtZfShcbWF0aGJme3h9KT1cbWF0aGJme3h9XlRcaGF0e1xib2xkc3ltYm9se1xiZXRhfX0kIGlzIHRoZSBwcmVkaWN0aW9uIGF0ICRcbWF0aGJme3h9JAotICRZXiokIGlzIGFuIG91dGNvbWUgYXQgcHJlZGljdG9yICRcbWF0aGJme3h9JAotICRcbXUoXG1hdGhiZnt4fSkgPSBcdGV4dHtFfVxsZWZ0W1xoYXR7WX1cbWlkIFxtYXRoYmZ7eH1ccmlnaHRdIFx0ZXh0eyBhbmQgfSBcbXVeKih4KT1cdGV4dHtFfVxsZWZ0W1leKlxtaWQgXG1hdGhiZnt4fVxyaWdodF0kCi0gJFx0ZXh0e2JpYXN9KFxtYXRoYmZ7eH0pPVxtdShcbWF0aGJme3h9KS1cbXVeKihcbWF0aGJme3h9KSQKLSAkXHRleHR7dmFyfVxsZWZ0W1leKlxtaWQgXG1hdGhiZnt4fVxyaWdodF0kIHRoZSBpcnJlZHVjaWJsZSBlcnJvciB0aGF0IGRvZXMgbm90IGRlcGVuZCBvbiB0aGUgbW9kZWwuIEl0IHNpbXBseSBvcmlnaW5hdGVzIGZyb20gb2JzZXJ2YXRpb25zIHRoYXQgcmFuZG9tbHkgZmx1Y3R1YXRlIGFyb3VuZCB0aGUgdHJ1ZSBtZWFuICRcbXVeKih4KSQuCgojIyBUb3hpY29nZW5vbWljcyBleGFtcGxlCgpgYGB7cn0KbGlicmFyeShnbG1uZXQpCm1SaWRnZSA8LSBnbG1uZXQoCiAgeCA9IHRveERhdGFbLC0xXSAlPiUKICAgIGFzLm1hdHJpeCwKICB5ID0gdG94RGF0YSAlPiUKICAgIHB1bGwoQkEpLAogIGFscGhhID0gMCkgIyByaWRnZTogYWxwaGEgPSAwCgpwbG90KG1SaWRnZSwgeHZhcj0ibGFtYmRhIikKYGBgCgoKVGhlIFIgZnVuY3Rpb24gXHRleHRzZntnbG1uZXR9IHVzZXMgXHRleHRzZntsYW1iZGF9IHRvIHJlZmVyIHRvIHRoZSBwZW5hbHR5IHBhcmFtZXRlci4gSW4gdGhpcyBjb3Vyc2Ugd2UgdXNlICRcbGFtYmRhJCwgYmVjYXVzZSAkXGxhbWJkYSQgaXMgb2Z0ZW4gdXNlZCBhcyBlaWdlbnZhbHVlcy4KClRoZSBncmFwaCBzaG93cyB0aGF0IHdpdGggaW5jcmVhc2luZyBwZW5hbHR5IHBhcmFtZXRlciwgdGhlIHBhcmFtZXRlciBlc3RpbWF0ZXMgYXJlIHNocnVua2VuIHRvd2FyZHMgemVyby4gVGhlIGVzdGltYXRlcyB3aWxsIG9ubHkgcmVhY2ggemVybyBmb3IgJFxsYW1iZGEgXHJpZ2h0YXJyb3cgXGluZnR5JC4gVGhlIHN0cm9uZ2VyIHRoZSBzaHJpbmthZ2UsIHRoZSBsYXJnZXIgdGhlIGJpYXMgKHRvd2FyZHMgemVybykgYW5kIHRoZSBzbWFsbGVyIHRoZSB2YXJpYW5jZSBvZiB0aGUgcGFyYW1ldGVyIGVzdGltYXRvcnMgKGFuZCBoZW5jZSBhbHNvIHNtYWxsZXIgdmFyaWFuY2Ugb2YgdGhlIHByZWRpY3Rpb25zKS4KCkFub3RoZXIgKGluZm9ybWFsKSB2aWV3cG9pbnQgaXMgdGhlIGZvbGxvd2luZy4gQnkgc2hyaW5raW5nIHRoZSBlc3RpbWF0ZXMgdG93YXJkcyB6ZXJvLCB0aGUgZXN0aW1hdGVzIGxvb3NlIHNvbWUgb2YgdGhlaXIgYGBkZWdyZWVzIG9mIGZyZWVkb20nJyBzbyB0aGF0IHRoZSBwYXJhbWV0ZXJzIGJlY29tZSBlc3RpbWFibGUgd2l0aCBvbmx5ICRuPHAkIGRhdGEgcG9pbnRzLiBFdmVuIHdpdGggYSB2ZXJ5IHNtYWxsICRcbGFtYmRhPjAkLCB0aGUgcGFyYW1ldGVycyByZWdhaW4gdGhlaXIgZXN0aW1hYmlsaXR5LiBIb3dldmVyLCBub3RlIHRoYXQgdGhlIHZhcmlhbmNlIG9mIHRoZSBlc3RpbWF0b3IgaXMgZ2l2ZW4gYnkKXFsKICBcdGV4dHt2YXJ9XGxlZnRbXGhhdHtcbWF0aGJmXGJldGF9XHJpZ2h0XSA9IChcbWF0aGJme1heVFh9K1xsYW1iZGEgXG1hdGhiZntJfSleey0xfSBcc2lnbWFeMiA9IFxtYXRoYmZ7Vn0oXGJvbGRzeW1ib2x7XERlbHRhfV4yK1xsYW1iZGFcbWF0aGJme0l9KV57LTF9XG1hdGhiZntWfV5UXHNpZ21hXjIuClxdCkhlbmNlLCBhIHNtYWxsICRcbGFtYmRhJCB3aWxsIHJlc3VsdCBpbiBsYXJnZSB2YXJpYW5jZXMgb2YgdGhlIHBhcmFtZXRlciBlc3RpbWF0b3JzLiBUaGUgbGFyZ2VyICRcbGFtYmRhJCwgdGhlIHNtYWxsZXIgdGhlIHZhcmlhbmNlcyBiZWNvbWUuIEluIHRoZSBsaW1pdCwgYXMgJFxsYW1iZGFccmlnaHRhcnJvd1xpbmZ0eSQsIHRoZSBlc3RpbWF0ZXMgYXJlIGNvbnZlcmdlZCB0byB6ZXJvIGFuZCBzaG93IG5vIHZhcmlhYmlsaXR5IGFueSBsb25nZXIuCgojIExhc3NvIFJlZ3Jlc3Npb24KCi0gVGhlIExhc3NvIGlzIGFub3RoZXIgZXhhbXBsZSBvZiBwZW5hbGlzZWQgcmVncmVzc2lvbi4KCi0gVGhlIGxhc3NvIGVzdGltYXRvciBvZiAkXGJvbGRzeW1ib2x7XGJldGF9JCBpcyB0aGUgc29sdXRpb24gdG8gbWluaW1pc2luZyB0aGUgcGVuYWxpc2VkIFNTRQpcWwogXHRleHR7U1NFfV9cdGV4dHtwZW59ID0gXHN1bV97aT0xfV5uIChZX2kgLSBcbWF0aGJme3h9X2leVFxib2xkc3ltYm9se1xiZXRhfSleMiArIFxsYW1iZGEgXHN1bV97aj0xfV5wIFx2ZXJ0IFxiZXRhX2pcdmVydC4KXF0KCgpvciwgZXF1aXZhbGVudGx5LCBtaW5pbWlzaW5nCgpcWwpcdGV4dHtTU0V9ICA9IFxWZXJ0IFxtYXRoYmZ7WX0gLSBcbWF0aGJme1hcYmV0YX1cVmVydF8yXjIgXHRleHR7IHN1YmplY3QgdG8gfSBcVmVydCBcbWF0aGJmXGJldGFcVmVydF8xIFxsZXEgYwpcXQp3aXRoCgotICRcVmVydCBcbWF0aGJmXGJldGFcVmVydF8xID0gXHN1bVxsaW1pdHNfe2o9MX1ecCBcdmVydCBcYmV0YV9qIFx2ZXJ0JAoKLSBEZXNwaXRlIHN0cm9uZyBzaW1pbGFyaXR5IGJldHdlZW4gcmlkZ2UgYW5kIGxhc3NvIHJlZ3Jlc3Npb24gKCRMXzIkIHZlcnN1cyAkTF8xJCBub3JtIGluIHBlbmFsdHkgdGVybSksIHRoZXJlIGlzIG5vIGFuYWx5dGljYWwgc29sdXRpb24gb2YgdGhlIGxhc3NvIHBhcmFtZXRlciBlc3RpbWF0ZSBvZiAkXG1hdGhiZlxiZXRhJC4KCi0gRm9ydHVuYXRlbHksIGNvbXB1dGF0aW9uYWwgZWZmaWNpZW50IGFsZ29yaXRobXMgaGF2ZSBiZWVuIGltcGxlbWVudGVkIGluIHN0YXRpc3RpY2FsIHNvZnR3YXJlCgotIFRoZSBMYXNzbyBlc3RpbWF0b3Igb2YgJFxib2xkc3ltYm9se1xiZXRhfSQgaXMgYmlhc2VkIGFuZCBnZW5lcmFsbHkgaGFzIGEgc21hbGxlciB2YXJpYW5jZSB0aGVuIHRoZSBsZWFzdC1zcXVhcmVzIGVzdGltYXRvci4KCi0gSGVuY2UsIHRoZSBiaWFzLXZhcmlhbmNlIHRyYWRlLW9mZiBtYXkgaGVyZSBhbHNvIGhlbHAgaW4gZmluZGluZyBiZXR0ZXIgcHJlZGljdGlvbnMgd2l0aCBiaWFzZWQgZXN0aW1hdG9ycy4KCi0gSW4gY29udHJhc3QgdG8gcmlkZ2UgcmVncmVzc2lvbiwgaG93ZXZlciwgdGhlIGxhc3NvIGVzdGltYXRvciBjYW4gZ2l2ZSBhdCBtb3N0ICRcbWluKHAsbikkIG5vbi16ZXJvICRcYmV0YSQtZXN0aW1hdGVzLgoKLSBIZW5jZSwgYXQgZmlyc3Qgc2lnaHQgdGhlIGxhc3NvIGlzIG5vdCBkaXJlY3RseSBhcHByb3ByaWF0ZSBmb3IgaGlnaC1kaW1lbnNpb25hbCBzZXR0aW5ncy4KCi0gQW4gaW1wb3J0YW50IGFkdmFudGFnZSBvZiB0aGUgbGFzc28gaXMgdGhhdCBjaG9vc2luZyBhbiBhcHByb3ByaWF0ZSB2YWx1ZSBmb3IgJFxsYW1iZGEkIGlzIGEga2luZCBhIG1vZGVsIGJ1aWxkaW5nIG9yIGZlYXR1cmUgc2VsZWN0aW9uIHByb2NlZHVyZSAoc2VlIGZ1cnRoZXIpLgoKIyMgR3JhcGhpY2FsIGludGVycHJldGF0aW9uIG9mIExhc3NvIHZzIHJpZGdlCgpOb3RlIHRoYXQgdGhlIGxhc3NvIGlzIGEgY29uc3RyYWluZWQgcmVncmVzc2lvbiBwcm9ibGVtIHdpdGgKClxbClxWZXJ0IFxtYXRoYmZ7WX0gLSBcbWF0aGJme1hcYmV0YX1cVmVydF8yXjIgXHRleHR7IHN1YmplY3QgdG8gfSBcVmVydCBcbWF0aGJmXGJldGFcVmVydF8xIFxsZXEgYwpcXQphbmQgcmlkZ2UKXFsKXFZlcnQgXG1hdGhiZntZfSAtIFxtYXRoYmZ7WFxiZXRhfVxWZXJ0XzJeMiBcdGV4dHsgc3ViamVjdCB0byB9IFxWZXJ0IFxtYXRoYmZcYmV0YVxWZXJ0XjJfMiBcbGVxIGMKXF0KCmBgYHtyIGVjaG8gPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CnBMYXNzbyA8LSBwMSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IDQuMiAsIHhlbmQgPSA0LjIsIHllbmQgPSAwKSwgY29sb3IgPSAicmVkIikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSA0LjIgLCB4ZW5kID0gLSA0LjIsIHllbmQgPSAwKSwgY29sb3IgPSAicmVkIikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDQuMiwgeSA9IDAgLCB4ZW5kID0gMCwgeWVuZCA9IC00LjIpLCBjb2xvciA9ICJyZWQiKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IC0gNC4yICwgeGVuZCA9IC0gNC4yLCB5ZW5kID0gMCksIGNvbG9yID0gInJlZCIpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gMCwgeSA9IDQuMiksIGNvbG9yID0gInJlZCIpICsKICBhbm5vdGF0ZSgidGV4dCIsIGxhYmVsID0gVGVYKCIkKFxcaGF0e1xcYmV0YX1fMV57bGFzc299LCBcXGhhdHtcXGJldGF9XzJee2xhc3NvfSkkIiksIHggPSA3LCB5ID0gNC4yLCBzaXplID0gNiwgcGFyc2UgPSBUUlVFLCBjb2xvciA9ICJyZWQiKSArCiAgZ2d0aXRsZSgiTGFzc28iKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgY29sb3IgPSAiZ3JleSIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBjb2xvciA9ICJncmV5IikgKwogIHRoZW1lX21pbmltYWwoKQoKZ3JpZC5hcnJhbmdlKHBMYXNzbywgcFJpZGdlLCBuY29sID0gMikKYGBgCgpOb3RlLCB0aGF0CgotIHBhcmFtZXRlcnMgZm9yIHRoZSBsYXNzbyBjYW4gbmV2ZXIgc3dpdGNoIHNpZ24sIHRoZXkgYXJlIHNldCBhdCB6ZXJvISBTZWxlY3Rpb24hCi0gcmlkZ2UgcmVncmVzc2lvbiBjYW4gbGVhZCB0byBwYXJhbWV0ZXJzIHRoYXQgc3dpdGNoIHNpZ24uCgojIyBUb3hpY29nZW5vbWljcyBleGFtcGxlCgpgYGB7cn0KbUxhc3NvIDwtIGdsbW5ldCgKICB4ID0gdG94RGF0YVssLTFdICU+JQogICAgYXMubWF0cml4LAogIHkgPSB0b3hEYXRhICU+JQogICAgcHVsbChCQSksCmFscGhhID0gMSkKcGxvdChtTGFzc28sIHh2YXIgPSAibGFtYmRhIikKYGBgCgotIFRoZSBncmFwaCB3aXRoIHRoZSBwYXRocyBvZiB0aGUgcGFyYW1ldGVyIGVzdGltYXRlcyBuaWNlbHkgaWxsdXN0cmF0ZXMgdGhlIHR5cGljYWwgYmVoYXZpb3VyIG9mIHRoZSBsYXNzbyBlc3RpbWF0ZXMgYXMgYSBmdW5jdGlvbiBvZiAkXGxhbWJkYSQ6IHdoZW4gJFxsYW1iZGEkIGluY3JlYXNlcyB0aGUgZXN0aW1hdGVzIGFyZSBzaHJ1bmtlbiB0b3dhcmRzIHplcm8uCgotIFdoZW4gYW4gZXN0aW1hdGUgaGl0cyB6ZXJvLCBpdCByZW1haW5zIGV4YWN0bHkgZXF1YWwgdG8gemVybyB3aGVuICRcZ2FtbWEkIGZ1cnRoZXIgaW5jcmVhc2VzLiBBIHBhcmFtZXRlciBlc3RpbWF0ZSBlcXVhbCB0byB6ZXJvLCBzYXkgJFxoYXRcYmV0YV9qPTAkLCBpbXBsaWVzIHRoYXQgdGhlIGNvcnJlc3BvbmRpbmcgcHJlZGljdG9yICR4X2okIGlzIG5vIGxvbmdlciBpbmNsdWRlZCBpbiB0aGUgbW9kZWwgKGkuZS4gJFxiZXRhX2p4X2o9MCQpLgoKLSBUaGUgbW9kZWwgZml0IGlzIGtub3duIGFzIGEgc3BhcnNlIG1vZGVsIGZpdCAobWFueSB6ZXJvZXMpLiBIZW5jZSwgY2hvb3NpbmcgYSBhcHByb3ByaWF0ZSB2YWx1ZSBmb3IgJFxnYW1tYSQgaXMgbGlrZSBjaG9vc2luZyB0aGUgaW1wb3J0YW50IHByZWRpY3RvcnMgaW4gdGhlIG1vZGVsIChmZWF0dXJlIHNlbGVjdGlvbikuCgoKIyBTcGxpbmVzIGFuZCB0aGUgY29ubmVjdGlvbiB0byByaWRnZSByZWdyZXNzaW9uLgoKIyMgTGlkYXIgZGF0YXNldAoKLSBMSURBUiAobGlnaHQgZGV0ZWN0aW9uIGFuZCByYW5naW5nKSB1c2VzIHRoZSByZWZsZWN0aW9uIG9mIGxhc2VyLWVtaXR0ZWQgbGlnaHQgdG8gZGV0ZWN0IGNoZW1pY2FsIGNvbXBvdW5kcyBpbiB0aGUgYXRtb3NwaGVyZS4KLSBUaGUgTElEQVIgdGVjaG5pcXVlIGhhcyBwcm92ZW4gdG8gYmUgYW4gZWZmaWNpZW50IHRvb2wgZm9yIG1vbml0b3JpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBzZXZlcmFsIGF0bW9zcGhlcmljIHBvbGx1dGFudHMgb2YgaW1wb3J0YW5jZTsgc2VlIFNpZ3Jpc3QgKDE5OTQpLgotIFRoZSByYW5nZSBpcyB0aGUgZGlzdGFuY2UgdHJhdmVsZWQgYmVmb3JlIHRoZSBsaWdodCBpcyByZWZsZWN0ZWQgYmFjayB0byBpdHMgc291cmNlLgotIFRoZSBsb2dyYXRpbyBpcyB0aGUgbG9nYXJpdGhtIG9mIHRoZSByYXRpbyBvZiByZWNlaXZlZCBsaWdodCBmcm9tIHR3byBsYXNlciBzb3VyY2VzLgoKICAtIE9uZSBzb3VyY2UgaGFkIGEgZnJlcXVlbmN5IGVxdWFsIHRvIHRoZSByZXNvbmFuY2UgZnJlcXVlbmN5IG9mIHRoZSBjb21wb3VuZCBvZiBpbnRlcmVzdCwgd2hpY2ggd2FzIG1lcmN1cnkgaW4gdGhpcyBzdHVkeS4KICAtIFRoZSBvdGhlciBzb3VyY2UgaGFkIGEgZnJlcXVlbmN5IG9mZiB0aGlzIHJlc29uYW5jZSBmcmVxdWVuY3kuCgogIC0gVGhlIGNvbmNlbnRyYXRpb24gb2YgbWVyY3VyeSBjYW4gYmUgZGVyaXZlZCBmcm9tIGEgcmVncmVzc2lvbiBtb2RlbCBvZiB0aGUgbG9ncmF0aW8gaW4gZnVuY3Rpb24gb2YgIHRoZSByYW5nZSBmb3IgZWFjaCByYW5nZSB4LgoKYGBge3J9CmxpYnJhcnkoIlNlbWlQYXIiKQpkYXRhKGxpZGFyKQpwTGlkYXIgPC0gbGlkYXIgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcmFuZ2UsIHkgPSBsb2dyYXRpbykpICsKICBnZW9tX3BvaW50KCkgKwogIHhsYWIoInJhbmdlIChtKSIpCgpwTGlkYXIgKwogIGdlb21fc21vb3RoKCkKYGBgCgotIFRoZSBkYXRhIGlzIG5vbi1saW5lYXIKLSBMaW5lYXIgcmVncmVzc2lvbiB3aWxsIG5vdCB3b3JrIQotIFRoZSBkYXRhIHNob3dzIGEgc21vb3RoIHJlbGF0aW9uIGJldHdlZW4gdGhlIGxvZ3JhdGlvIGFuZCB0aGUgcmFuZ2UKCiMjIEJhc2lzIGV4cGFuc2lvbgoKXFt5X2k9Zih4X2kpK1xlcHNpbG9uX2ksXF0Kd2l0aApcW2YoeCk9XHN1bVxsaW1pdHNfe2s9MX1eSyBcdGhldGFfayBiX2soeClcXQoKLSAgU2VsZWN0IHNldCBvZiBiYXNpcyBmdW5jdGlvbnMgJGJfayh4KSQKLSAgU2VsZWN0IG51bWJlciBvZiBiYXNpcyBmdW5jdGlvbnMgJEskCi0gIEV4YW1wbGVzCgogICAgLSAgUG9seW5vbWlhbCBtb2RlbDogJHheayQKICAgIC0gIE9ydGhvZ29uYWwgc2VyaWVzOiBGb3VyaWVyLCBMZWdlbmRyZSBwb2x5bm9taWFscywgV2F2ZWxldHMKICAgIC0gIFBvbHlub21pYWwgc3BsaW5lczogJDEsIHgsICh4LXRfbSlfKyQgd2l0aCAkbT0xLCBcbGRvdHMsIEstMiQga25vdHMgJHRfbSQKICAgIC0gIC4uLgoKIyMjIFRydW5jdGF0ZWQgbGluZSBiYXNpcwoKXFt5X2k9Zih4X2kpK1xlcHNpbG9uX2ksXF0KCi0gIE9uZSBvZiB0aGUgbW9zdCBzaW1wbGUgYmFzaXMgZXhwYW5zaW9ucwotICAkZih4X2kpPVxiZXRhXzArXGJldGFfMXhfaStcc3VtXGxpbWl0c197bT0xfV57Sy0yfVx0aGV0YV9tKHhfaS10X20pXyskIHdpdGggJCguKV8rJCB0aGUgb3BlcmF0b3IgdGhhdCB0YWtlcyB0aGUgcG9zaXRpdmUgcGFydC4KLSAgTm90ZSwgdGhhdCBiZXR0ZXIgYmFzaXMgZXhwYW5zaW9ucyBleGlzdCwgd2hpY2ggYXJlIG9ydGhvZ29uYWwsIGNvbXB1dGF0aW9uYWwgbW9yZSBzdGFibGUgYW5kL29yIGNvbnRpbnVvdXMgZGVyaXZhdGl2ZSBiZXlvbmQgZmlyc3Qgb3JkZXIKLSAgV2Ugd2lsbCB1c2UgdGhpcyBiYXNpcyBmb3IgZGlkYWN0aWNhbCBwdXJwb3NlcwotIFdlIGNhbiB1c2UgT0xTIHRvIGZpdCB5IHcuci50LiB0aGUgYmFzaXMuCgpgYGB7cn0Ka25vdHMgPC0gc2VxKDQwMCw3MDAsMTIuNSkKCmJhc2lzIDwtIHNhcHBseShrbm90cywKICBmdW5jdGlvbihrLHkpICh5LWspKih5PmspLAogIHk9IGxpZGFyICU+JSBwdWxsKHJhbmdlKQogICkKCmJhc2lzRXhwIDwtIGNiaW5kKDEsIHJhbmdlID0gbGlkYXIgJT4lIHB1bGwocmFuZ2UpLCBiYXNpcykKCnNwbGluZUZpdExzIDwtIGxtKGxvZ3JhdGlvIH4gLTEgKyBiYXNpc0V4cCwgbGlkYXIpCgpwQmFzaXMgPC0gYmFzaXNFeHBbLC0xXSAlPiUKICBkYXRhLmZyYW1lICU+JQogIGdhdGhlcigiYmFzaXMiLCJ2YWx1ZXMiLC0xKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSByYW5nZSwgeSA9IHZhbHVlcywgY29sb3IgPSBiYXNpcykpICsKICBnZW9tX2xpbmUoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKwogIHlsYWIoImJhc2lzIikKCmdyaWQuYXJyYW5nZSgKICBwTGlkYXIgKwogICAgZ2VvbV9saW5lKGFlcyh4ID0gbGlkYXIkcmFuZ2UsIHkgPSBzcGxpbmVGaXRMcyRmaXR0ZWQpLCBsd2QgPSAyKSwKICBwQmFzaXMsCiAgbmNvbD0xKQpgYGAKCi0gTm90ZSwgdGhhdCB0aGUgbW9kZWwgaXMgb3ZlcmZpdHRpbmchCi0gVGhlIGZpdCBpcyB2ZXJ5IHdpZ2dseSBhbmQgaXMgdHVuZWQgdG9vIG11Y2ggdG8gdGhlIGRhdGEuCi0gVGhlIGZpdCBoYXMgYSBsYXJnZSB2YXJpYW5jZSBhbmQgbG93IGJpYXMuCi0gSXQgd2lsbCB0aGVyZWZvcmUgbm90IGdlbmVyYWxpc2Ugd2VsbCB0byBwcmVkaWN0IHRoZSBsb2dyYXRpbyBvZiBmdXR1cmUgb2JzZXJ2YXRpb25zLgoKIyMjIyBTb2x1dGlvbiBmb3Igb3ZlcmZpdHRpbmc/CgotIFdlIGNvdWxkIHBlcmZvcm0gbW9kZWwgc2VsZWN0aW9uIG9uIHRoZSBiYXNpcyB0byBzZWxlY3QgdGhlIGltcG9ydGFudCBiYXNpcyBmdW5jdGlvbnMgdG8gbW9kZWwgdGhlIHNpZ25hbC4gQnV0LCB0aGlzIHdpbGwgaGF2ZSB0aGUgdW5kZXNpcmVkIHByb3BlcnR5IHRoYXQgdGhlIGZpdCB3aWxsIG5vIGxvbmdlciBiZSBzbW9vdGguCgotIFdlIGNhbiBhbHNvIGFkb3B0IGEgcmlkZ2UgcGVuYWx0eSEKLSBIb3dldmVyLCB3ZSBkbyBub3Qgd2FudCB0byBwZW5hbGlzZSB0aGUgaW50ZXJjZXB0IGFuZCB0aGUgbGluZWFyIHRlcm0uCi0gUmlkZ2UgY3JpdGVyaW9uCgpcW1xWZXJ0XG1hdGhiZntZfS1cbWF0aGJme1hcYmV0YX1cVmVydF4yK1xsYW1iZGFcYm9sZHN5bWJvbHtcYmV0YX1eVFxtYXRoYmZ7RH1cYm9sZHN5bWJvbHtcYmV0YX0KXF0KCldpdGggJFxtYXRoYmZ7RH0kIHdpdGggZGltZW5zaW9ucyAoSyxLKTogJFxtYXRoYmZ7RH09XGxlZnRbXGJlZ2lue2FycmF5fXtjY31cbWF0aGJmezB9X3syXHRpbWVzMn0mIFxtYXRoYmZ7MH1fezJcdGltZXMgSy0yfVxcClxtYXRoYmZ7MH1fe0stMlx0aW1lczJ9JlxtYXRoYmZ7SX1fe0stMlx0aW1lcyBLLTJ9XGVuZHthcnJheX1ccmlnaHRdJAoKLSBIZXJlIHdlIHdpbGwgc2V0IHRoZSBwZW5hbHR5IGF0IDkwMC4KCmBgYHtyfQpEIDwtIGRpYWcobmNvbChiYXNpc0V4cCkpCkRbMToyLDE6Ml0gPC0gMApsYW1iZGEgPC0gOTAwCmJldGFSaWRnZSA8LSBzb2x2ZSh0KGJhc2lzRXhwKSUqJWJhc2lzRXhwKyhsYW1iZGEqRCkpJSoldChiYXNpc0V4cCklKiVsaWRhciRsb2dyYXRpbwpncmlkLmFycmFuZ2UoCiAgcExpZGFyICsKICAgIGdlb21fbGluZShhZXMoeCA9IGxpZGFyJHJhbmdlLCB5ID0gYyhiYXNpc0V4cCAlKiUgYmV0YVJpZGdlKSksIGx3ZCA9IDIpLAogIHBCYXNpcywKICBuY29sPTEpCmBgYAoKSG93IGRvIHdlIGNob29zZSAkXGxhbWJkYSQ/CgotLS0KCiMgRXZhbHVhdGlvbiBvZiBQcmVkaWN0aW9uIE1vZGVscwoKClByZWRpY3Rpb25zIGFyZSBjYWxjdWxhdGVkIHdpdGggdGhlIGZpdHRlZCBtb2RlbAogXFsKICAgXGhhdHtZfShcbWF0aGJme3h9KSA9IFxoYXR7bX0oXG1hdGhiZnt4fSk9XG1hdGhiZnt4fV5UXGhhdHtcYmV0YX0KIFxdCiB3aGVuIGZvY3Vzc2luZyBvbiBwcmVkaWN0aW9uLCB3ZSB3YW50IHRoZSBwcmVkaWN0aW9uIGVycm9yIHRvIGJlIGFzIHNtYWxsIGFzIHBvc3NpYmxlLgoKVGhlICoqcHJlZGljdGlvbiBlcnJvcioqIGZvciBhIHByZWRpY3Rpb24gYXQgY292YXJpYXRlIHBhdHRlcm4gJFxtYXRoYmZ7eH0kIGlzIGdpdmVuIGJ5CiAgXFsKICAgICBcaGF0e1l9KFxtYXRoYmZ7eH0pIC0gWV4qLAogIFxdCndoZXJlCgotICRcaGF0e1l9KFxtYXRoYmZ7eH0pPVxtYXRoYmZ7eH1eVFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fSQgaXMgdGhlIHByZWRpY3Rpb24gYXQgJFxtYXRoYmZ7eH0kCgotICAkWV4qJCBpcyBhbiBvdXRjb21lIGF0IGNvdmFyaWF0ZSBwYXR0ZXJuICRcbWF0aGJme3h9JAoKUHJlZGljdGlvbiBpcyB0eXBpY2FsbHkgdXNlZCB0byBwcmVkaWN0IGFuIG91dGNvbWUgYmVmb3JlIGl0IGlzIG9ic2VydmVkLgoKLSBIZW5jZSwgdGhlIG91dGNvbWUgJFleKiQgaXMgbm90IG9ic2VydmVkIHlldCwgYW5kCi0gdGhlIHByZWRpY3Rpb24gZXJyb3IgY2Fubm90IGJlIGNvbXB1dGVkLgoKLS0tCgotIFJlY2FsbCB0aGF0IHRoZSBwcmVkaWN0aW9uIG1vZGVsICRcaGF0e1l9KFxtYXRoYmZ7eH0pJCBpcyBlc3RpbWF0ZWQgYnkgdXNpbmcgZGF0YSBpbiB0aGUgdHJhaW5pbmcgZGF0YSBzZXQgJChcbWF0aGJme1h9LFxtYXRoYmZ7WX0pJCwgYW5kCi0gdGhhdCB0aGUgb3V0Y29tZSAkWV4qJCBpcyBhbiBvdXRjb21lIGF0ICRcbWF0aGJme3h9JCB3aGljaCBpcyBhc3N1bWVkIHRvIGJlIGluZGVwZW5kZW50IG9mIHRoZSB0cmFpbmluZyBkYXRhLgoKLSBHb2FsIGlzIHRvIHVzZSBwcmVkaWN0aW9uIG1vZGVsIGZvciBwcmVkaWN0aW5nIGEgZnV0dXJlIG9ic2VydmF0aW9uICgkWV4qJCksIGkuZS4gYW4gb2JzZXJ2YXRpb24gdGhhdCBzdGlsbCBoYXMgdG8gYmUgcmVhbGlzZWQvb2JzZXJ2ZWQgKG90aGVyd2lzZSBwcmVkaWN0aW9uIHNlZW1zIHJhdGhlciB1c2VsZXNzKS4KCi0gSGVuY2UsICRZXiokIGNhbiBuZXZlciBiZSBwYXJ0IG9mIHRoZSB0cmFpbmluZyBkYXRhIHNldC4KCi0tLQoKSGVyZSB3ZSBwcm92aWRlIGRlZmluaXRpb25zIGFuZCB3ZSBzaG93IGhvdyB0aGUgcHJlZGljdGlvbiBwZXJmb3JtYW5jZSBvZiBhIHByZWRpY3Rpb24gbW9kZWwgY2FuIGJlIGV2YWx1YXRlZCBmcm9tIGRhdGEuCgpMZXQgJHtcY2Fse1R9fT0oXG1hdGhiZntZfSxcbWF0aGJme1h9KSQgZGVub3RlIHRoZSB0cmFpbmluZyBkYXRhLCBmcm9tIHdoaWNoIHRoZSBwcmVkaWN0aW9uIG1vZGVsICRcaGF0e1l9KFxjZG90KSQgaXMgYnVpbGQuIFRoaXMgYnVpbGRpbmcgcHJvY2VzcyB0eXBpY2FsbHkgaW52b2x2ZXMgZmVhdHVyZSBzZWxlY3Rpb24gYW5kIHBhcmFtZXRlciBlc3RpbWF0aW9uLgoKIFdlIHdpbGwgdXNlIGEgbW9yZSBnZW5lcmFsIG5vdGF0aW9uIGZvciB0aGUgcHJlZGljdGlvbiBtb2RlbDogJFxoYXR7bX0oXG1hdGhiZnt4fSk9XGhhdHtZfShcbWF0aGJme3h9KSQuCgotLS0KCiMjIFRlc3Qgb3IgR2VuZXJhbGlzYXRpb24gRXJyb3IKCiBUaGUgdGVzdCBvciBnZW5lcmFsaXNhdGlvbiBlcnJvciBmb3IgcHJlZGljdGlvbiBtb2RlbCAkXGhhdHttfShcY2RvdCkkIGlzIGdpdmVuIGJ5CiAgXFsKICAgIFx0ZXh0e0Vycn1fe1xjYWx7VH19ID0gXHRleHR7RX1fe1leKixYXip9XGxlZnRbKFxoYXR7bX0oXG1hdGhiZntYfV4qKSAtIFleKileMlxtaWQge1xjYWx7VH19XHJpZ2h0XQogIFxdCiAgd2hlcmUgJChZXiosWF4qKSQgaXMgaW5kZXBlbmRlbnQgb2YgdGhlIHRyYWluaW5nIGRhdGEuCgotLS0KCi0gTm90ZSB0aGF0IHRoZSB0ZXN0IGVycm9yIGlzIGNvbmRpdGlvbmFsIG9uIHRoZSB0cmFpbmluZyBkYXRhICR7XGNhbHtUfX0kLgotIEhlbmNlLCB0aGUgdGVzdCBlcnJvciBldmFsdWF0ZXMgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBzaW5nbGUgbW9kZWwgYnVpbGQgZnJvbSB0aGUgb2JzZXJ2ZWQgdHJhaW5pbmcgZGF0YS4KLSBUaGlzIGlzIHRoZSB1bHRpbWF0ZSB0YXJnZXQgb2YgdGhlIG1vZGVsIGFzc2Vzc21lbnQsIGJlY2F1c2UgaXQgaXMgZXhhY3RseSB0aGlzIHByZWRpY3Rpb24gbW9kZWwgdGhhdCB3aWxsIGJlIHVzZWQgaW4gcHJhY3RpY2UgYW5kIGFwcGxpZWQgdG8gZnV0dXJlIHByZWRpY3RvcnMgJFxtYXRoYmZ7WH1eKiQgdG8gcHJlZGljdCAkWV4qJC4KLSBUaGUgdGVzdCBlcnJvciBpcyBkZWZpbmVkIGFzIGFuIGF2ZXJhZ2Ugb3ZlciBhbGwgc3VjaCBmdXR1cmUgb2JzZXJ2YXRpb25zICQoWV4qLFxtYXRoYmZ7WH1eKikkLgoKLS0tCgojIyBDb25kaXRpb25hbCB0ZXN0IGVycm9yCgpTb21ldGltZXMgdGhlIGNvbmRpdGlvbmFsIHRlc3QgZXJyb3IgaXMgdXNlZDoKClRoZSBjb25kaXRpb25hbCB0ZXN0IGVycm9yIGluICRcbWF0aGJme3h9JCBmb3IgcHJlZGljdGlvbiBtb2RlbCAkXGhhdHttfShcbWF0aGJme3h9KSQgaXMgZ2l2ZW4gYnkKIFxbCiAgIFx0ZXh0e0Vycn1fe1xjYWx7VH19KFxtYXRoYmZ7eH0pID0gXHRleHR7RX1fe1leKn1cbGVmdFsoXGhhdHttfShcbWF0aGJme3h9KSAtIFleKileMlxtaWQge1xjYWx7VH19LCBcbWF0aGJme3h9XHJpZ2h0XQogXF0KIHdoZXJlICRZXiokIGlzIGFuIG91dGNvbWUgYXQgcHJlZGljdG9yICRcbWF0aGJme3h9JCwgaW5kZXBlbmRlbnQgb2YgdGhlIHRyYWluaW5nIGRhdGEuCgogSGVuY2UsCiBcWwogICBcdGV4dHtFcnJ9X3tcY2Fse1R9fSA9IFx0ZXh0e0V9X3tYXip9XGxlZnRbXHRleHR7RXJyfV97XGNhbHtUfX0oXG1hdGhiZntYfV4qKVxyaWdodF0uCiBcXQoKQSBjbG9zZWx5IHJlbGF0ZWQgZXJyb3IgaXMgdGhlICoqaW5zYW1wbGUgZXJyb3IqKi4KCi0tLQoKIyMgSW5zYW1wbGUgRXJyb3IKClRoZSBpbnNhbXBsZSBlcnJvciBmb3IgcHJlZGljdGlvbiBtb2RlbCAkXGhhdHttfShcbWF0aGJme3h9KSQgaXMgZ2l2ZW4gYnkKIFxbCiAgIFx0ZXh0e0Vycn1fe1x0ZXh0e2lufSBcY2Fse1R9fSA9IFxmcmFjezF9e259XHN1bV97aT0xfV5uIFx0ZXh0e0Vycn1fe1xjYWx7VH19KFxtYXRoYmZ7eH1faSksCiBcXQoKaS5lLiB0aGUgaW5zYW1wbGUgZXJyb3IgaXMgdGhlIHNhbXBsZSBhdmVyYWdlIG9mIHRoZSBjb25kaXRpb25hbCB0ZXN0IGVycm9ycyBldmFsdWF0ZWQgaW4gdGhlICRuJCB0cmFpbmluZyBkYXRhc2V0IHByZWRpY3RvcnMgJFxtYXRoYmZ7eH1faSQuCgpTaW5jZSAkXHRleHR7RXJyfV97XGNhbHtUfX0kIGlzIGFuIGF2ZXJhZ2Ugb3ZlciBhbGwgJFxtYXRoYmZ7WH0kLCBldmVuIG92ZXIgdGhvc2UgcHJlZGljdG9ycyBub3Qgb2JzZXJ2ZWQgaW4gdGhlIHRyYWluaW5nIGRhdGFzZXQsIGl0IGlzIHNvbWV0aW1lcyByZWZlcnJlZCB0byBhcyB0aGUgKipvdXRzYW1wbGUgZXJyb3IqKi4KCi0tLQoKIyMgRXN0aW1hdGlvbiBvZiB0aGUgaW5zYW1wbGUgZXJyb3IKCldlIHN0YXJ0IHdpdGggaW50cm9kdWNpbmcgdGhlIHRyYWluaW5nIGVycm9yIHJhdGUsIHdoaWNoIGlzIGNsb3NlbHkgcmVsYXRlZCB0byB0aGUgTVNFIGluIGxpbmVhciBtb2RlbHMuCgojIyMgVHJhaW5pbmcgZXJyb3IKCiBUaGUgdHJhaW5pbmcgZXJyb3IgaXMgZ2l2ZW4gYnkKIFxbCiAgIFxvdmVybGluZXtcdGV4dHtlcnJ9fSA9IFxmcmFjezF9e259XHN1bV97aT0xfV5uIChZX2kgLSBcaGF0e219KFxtYXRoYmZ7eH1faSkpXjIgLAogXF0KIHdoZXJlIHRoZSAkKFlfaSxcbWF0aGJme3h9X2kpJCBmcm9tIHRoZSB0cmFpbmluZyBkYXRhc2V0IHdoaWNoIGlzIGFsc28gdXNlZCBmb3IgdGhlIGNhbGN1bGF0aW9uIG9mICRcaGF0e219JC4KCi0gVGhlIHRyYWluaW5nIGVycm9yIGlzIGFuIG92ZXJseSBvcHRpbWlzdGljIGVzdGltYXRlIG9mIHRoZSB0ZXN0IGVycm9yICRcdGV4dHtFcnJ9X3tcY2Fse1R9fSQuCgotIFRoZSB0cmFpbmluZyBlcnJvciB3aWxsIG5ldmVyIGluY3JlYXNlcyB3aGVuIHRoZSBtb2RlbCBiZWNvbWVzIG1vcmUgY29tcGxleC4gJFxsb25ncmlnaHRhcnJvdyQgY2Fubm90IGJlIHVzZWQgZGlyZWN0bHkgYXMgYSBtb2RlbCBzZWxlY3Rpb24gY3JpdGVyaW9uLgoKSW5kZWVkLCBtb2RlbCBwYXJhbWV0ZXJzIGFyZSBvZnRlbiBlc3RpbWF0ZWQgYnkgbWluaW1pc2luZyB0aGUgdHJhaW5pbmcgZXJyb3IgKGNmci4gU1NFKS4KCi0gSGVuY2UgdGhlIGZpdHRlZCBtb2RlbCBhZGFwdHMgdG8gdGhlIHRyYWluaW5nIGRhdGEsIGFuZAotIHRyYWluaW5nIGVycm9yIHdpbGwgYmUgYW4gb3Zlcmx5IG9wdGltaXN0aWMgZXN0aW1hdGUgb2YgdGhlIHRlc3QgZXJyb3IgJFx0ZXh0e0Vycn1fe1xjYWx7VH19JC4KCi0tLQoKSXQgY2FuIGJlIHNob3duIHRoYXQgdGhlIHRyYWluaW5nIGVycm9yIGlzIHJlbGF0ZWQgdG8gdGhlIGluc2FtcGxlIHRlc3QgZXJyb3IgdmlhCgpcWwpcdGV4dHtFfV9cbWF0aGJme1l9ClxsZWZ0W1x0ZXh0e0Vycn1fe1x0ZXh0e2lufXtcY2Fse1R9fX1ccmlnaHRdID0gXHRleHR7RX1fXG1hdGhiZntZfVxsZWZ0W1xvdmVybGluZXtcdGV4dHtlcnJ9fVxyaWdodF0gKyBcZnJhY3syfXtufVxzdW1fe2k9MX1ebiBcdGV4dHtjb3Z9X1xtYXRoYmZ7WX1cbGVmdFtcaGF0e219KFxtYXRoYmZ7eH1faSksWV9pXHJpZ2h0XSwKXF0KCk5vdGUsIHRoYXQgZm9yIGxpbmVhciBtb2RlbHMKXFsgXGhhdHttfShcbWF0aGJme3h9X2kpID0gXG1hdGhiZntYfVxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fT0gXG1hdGhiZntYfShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntZfSA9IFxtYXRoYmZ7SFl9ClxdCndpdGgKCi0gJFxtYXRoYmZ7SH0kIHRoZSBoYXQgbWF0cml4IGFuZAotIGFsbCAkWV9pJCBhcmUgYXNzdW1lZCB0byBiZSBpbmRlcGVuZGVudGx5IGRpc3RyaWJ1dGVkICAkTihcbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9LFxzaWdtYV4yKSQKCkhlbmNlLCBmb3IgbGluZWFyIG1vZGVscyB3aXRoIGluZGVwZW5kZW50IG9ic2VydmF0aW9ucwoKXGJlZ2lue2VxbmFycmF5fQpcdGV4dHtjb3Z9X1xtYXRoYmZ7WX1cbGVmdFtcaGF0e219KFxtYXRoYmZ7eH1faSksWV9pKVxyaWdodF0gJj0mClx0ZXh0e2Nvdn1fXG1hdGhiZntZfVxsZWZ0W1xtYXRoYmZ7SH1fe2l9XlRcbWF0aGJme1l9LFlfaSlccmlnaHRdXFwKJj0mIFx0ZXh0e2Nvdn1fXG1hdGhiZntZfVxsZWZ0W2hfe2lpfSBZX2ksWV9pXHJpZ2h0XVxcCiY9JiBoX3tpaX0gXHRleHR7Y292fV9cbWF0aGJme1l9XGxlZnRbWV9pLFlfaVxyaWdodF1cXAomPSYgaF97aWl9IFxzaWdtYV4yXFwKXGVuZHtlcW5hcnJheX0KCkFuZCB3ZSBjYW4gdGh1cyBlc3RpbWF0ZSB0aGUgaW5zYW1wbGUgZXJyb3IgYnkgTWFsbG93J3MgJENfcCQKClxiZWdpbntlcW5hcnJheX0KQ19wICY9JiBcb3ZlcmxpbmV7XHRleHR7ZXJyfX0gKyBcZnJhY3syXHNpZ21hXjJ9e259XHRleHR7dHJ9KFxtYXRoYmZ7SH0pXFwKJj0mIFxvdmVybGluZXtcdGV4dHtlcnJ9fSArIFxmcmFjezJcc2lnbWFeMnB9e259ClxlbmR7ZXFuYXJyYXl9Cgp3aXRoICRwJCB0aGUgbnVtYmVyIG9mIHByZWRpY3RvcnMuCgotIE1hbGxvdydzICRDX3AkIGlzIG9mdGVuIHVzZWQgZm9yIG1vZGVsIHNlbGVjdGlvbi4KLSBOb3RlLCB0aGF0IHdlIGNhbiBhbHNvIGNvbnNpZGVyIGl0IGFzIGEga2luZCBvZiBwZW5hbGl6ZWQgbGVhc3Qgc3F1YXJlczoKClxbCm4gXHRpbWVzIENfcCA9IFxWZXJ0IFxtYXRoYmZ7WX0gLSBcbWF0aGJme1h9XGJvbGRzeW1ib2x7XGJldGF9XFZlcnRfMl4yICsgMlxzaWdtYV4yIFxWZXJ0IFxib2xkc3ltYm9se1xiZXRhfSBcVmVydF8wClxdCndpdGggJExfMCQgbm9ybSAkXFZlcnQgXGJvbGRzeW1ib2x7XGJldGF9IFxWZXJ0XzAgPSBcc3VtX3tqPTF9XnAgXGJldGFfcF4wID0gcCQuCgotLS0KCiMjIEV4cGVjdGVkIHRlc3QgZXJyb3IKClRoZSB0ZXN0IG9yIGdlbmVyYWxpc2F0aW9uIGVycm9yIHdhcyBkZWZpbmVkIGNvbmRpdGlvbmFsbHkgb24gdGhlIHRyYWluaW5nIGRhdGEuIEJ5IGF2ZXJhZ2luZyBvdmVyIHRoZSBkaXN0cmlidXRpb24gb2YgdHJhaW5pbmcgZGF0YXNldHMsIHRoZSBleHBlY3RlZCB0ZXN0IGVycm9yIGFyaXNlcy4KClxiZWdpbntlcW5hcnJheSp9CiAgIFx0ZXh0e0V9X3tcY2Fse1R9fVxsZWZ0W1x0ZXh0e0Vycn1fe3tcY2Fse1R9fX1ccmlnaHRdCiAgICAgJj0mIFx0ZXh0e0V9X3tcY2Fse1R9fVxsZWZ0W1x0ZXh0e0V9X3tZXiosWF4qfVxsZWZ0WyhcaGF0e219KFxtYXRoYmZ7WH1eKikgLSBZXiopXjJcbWlkIHtcY2Fse1R9fVxyaWdodF1ccmlnaHRdIFxcCiAgICAgJj0mIFx0ZXh0e0V9X3tZXiosWF4qLHtcY2Fse1R9fX1cbGVmdFsoXGhhdHttfShcbWF0aGJme1h9XiopIC0gWV4qKV4yXHJpZ2h0XS4KIFxlbmR7ZXFuYXJyYXkqfQoKIC0gVGhlIGV4cGVjdGVkIHRlc3QgZXJyb3IgbWF5IG5vdCBiZSBvZiBkaXJlY3QgaW50ZXJlc3Qgd2hlbiB0aGUgZ29hbCBpcyB0byBhc3Nlc3MgdGhlIHByZWRpY3Rpb24gcGVyZm9ybWFuY2Ugb2YgYSBzaW5nbGUgcHJlZGljdGlvbiBtb2RlbCAkXGhhdHttfShcY2RvdCkkLgoKIC0gVGhlIGV4cGVjdGVkIHRlc3QgZXJyb3IgYXZlcmFnZXMgdGhlIHRlc3QgZXJyb3JzIG9mIGFsbCBtb2RlbHMgdGhhdCBjYW4gYmUgYnVpbGQgZnJvbSBhbGwgdHJhaW5pbmcgZGF0YXNldHMsIGFuZCBoZW5jZSB0aGlzIG1heSBiZSBsZXNzIHJlbGV2YW50IHdoZW4gdGhlIGludGVyZXN0IGlzIGluIGV2YWx1YXRpbmcgb25lIHBhcnRpY3VsYXIgbW9kZWwgdGhhdCByZXN1bHRlZCBmcm9tIGEgc2luZ2xlIG9ic2VydmVkIHRyYWluaW5nIGRhdGFzZXQuCgogLSBBbHNvIG5vdGUgdGhhdCBidWlsZGluZyBhIHByZWRpY3Rpb24gbW9kZWwgaW52b2x2ZXMgYm90aCBwYXJhbWV0ZXIgZXN0aW1hdGlvbiBhbmQgZmVhdHVyZSBzZWxlY3Rpb24uCgogLSBIZW5jZSB0aGUgZXhwZWN0ZWQgdGVzdCBlcnJvciBhbHNvIGV2YWx1YXRlcyB0aGUgZmVhdHVyZSBzZWxlY3Rpb24gcHJvY2VkdXJlIChvbiBhdmVyYWdlKS4KCiAtIElmIHRoZSBleHBlY3RlZCB0ZXN0IGVycm9yIGlzIHNtYWxsLCBpdCBpcyBhbiBpbmRpY2F0aW9uIHRoYXQgdGhlIG1vZGVsIGJ1aWxkaW5nIHByb2Nlc3MgZ2l2ZXMgZ29vZCBwcmVkaWN0aW9ucyBmb3IgZnV0dXJlIG9ic2VydmF0aW9ucyAkKFleKixcbWF0aGJme1h9XiopJCBvbiBhdmVyYWdlLgoKIyMjIEVzdGltYXRpbmcgdGhlIEV4cGVjdGVkIHRlc3QgZXJyb3IKClRoZSBleHBlY3RlZCB0ZXN0IGVycm9yIG1heSBiZSBlc3RpbWF0ZWQgYnkgY3Jvc3MgdmFsaWRhdGlvbiAoQ1YpLgoKIyMjIyBMZWF2ZSBvbmUgb3V0IGNyb3NzIHZhbGlkYXRpb24gKExPT0NWKX0KClRoZSBMT09DViBlc3RpbWF0b3Igb2YgdGhlIGV4cGVjdGVkIHRlc3QgZXJyb3IgKG9yIGV4cGVjdGVkIG91dHNhbXBsZSBlcnJvcikgaXMgZ2l2ZW4gYnkKICBcWwogICAgIFx0ZXh0e0NWfSA9IFxmcmFjezF9e259IFxzdW1fe2k9MX1ebiBcbGVmdChZX2kgLSBcaGF0e219XnstaX0oXG1hdGhiZnt4fV9pKVxyaWdodCleMiAsCiAgXF0Kd2hlcmUKCi0gdGhlICQoWV9pLFxtYXRoYmZ7eH1faSkkIGZvcm0gdGhlIHRyYWluaW5nIGRhdGFzZXQKLSAgICRcaGF0e219XnstaX0kIGlzIHRoZSBmaXR0ZWQgbW9kZWwgYmFzZWQgb24gYWxsIHRyYWluaW5nIGRhdGEsIGV4Y2VwdCBvYnNlcnZhdGlvbiAkaSQKLSAgICRcaGF0e219XnstaX0oXG1hdGhiZnt4fV9pKSQgaXMgdGhlIHByZWRpY3Rpb24gYXQgJFxtYXRoYmZ7eH1faSQsIHdoaWNoIGlzIHRoZSBvYnNlcnZhdGlvbiBsZWZ0IG91dCB0aGUgdHJhaW5pbmcgZGF0YSBiZWZvcmUgYnVpbGRpbmcgbW9kZWwgJG0kLgoKU29tZSByYXRpb25hbGUgYXMgdG8gd2h5IExPT0NWIG9mZmVycyBhIGdvb2QgZXN0aW1hdG9yIG9mIHRoZSBvdXRzYW1wbGUgZXJyb3I6CgotIHRoZSBwcmVkaWN0aW9uIGVycm9yICRZXiotXGhhdHttfShcbWF0aGJme3h9KSQgaXMgbWltaWNrZWQgYnkgbm90IHVzaW5nIG9uZSBvZiB0aGUgdHJhaW5pbmcgb3V0Y29tZXMgJFlfaSQgZm9yIHRoZSBlc3RpbWF0aW9uIG9mIHRoZSBtb2RlbCBzbyB0aGF0IHRoaXMgJFlfaSQgcGxheXMgdGhlIHJvbGUgb2YgJFleKiQsIGFuZCwgY29uc2VxdWVudGx5LCB0aGUgZml0dGVkIG1vZGVsICRcaGF0e219XnstaX0kIGlzIGluZGVwZW5kZW50IG9mICRZX2kkCgogLSB0aGUgc3VtIGluICRDViQgaXMgb3ZlciBhbGwgJFxtYXRoYmZ7eH1faSQgaW4gdGhlIHRyYWluaW5nIGRhdGFzZXQsIGJ1dCBlYWNoIHRlcm0gJFxtYXRoYmZ7eH1faSQgd2FzIGxlZnQgb3V0IG9uY2UgZm9yIHRoZSBjYWxjdWxhdGlvbiBvZiAkXGhhdHttfV57LWl9JC4gSGVuY2UsICRcaGF0e219XnstaX0oXG1hdGhiZnt4fV9pKSQgbWltaWNzIGFuIG91dHNhbXBsZSBwcmVkaWN0aW9uLgoKIC0gdGhlIHN1bSBpbiBDViBpcyBvdmVyICRuJCBkaWZmZXJlbnQgdHJhaW5pbmcgZGF0YXNldHMgKGVhY2ggb25lIHdpdGggYSBkaWZmZXJlbnQgb2JzZXJ2YXRpb24gcmVtb3ZlZCksIGFuZCBoZW5jZSBDViBpcyBhbiBlc3RpbWF0b3Igb2YgdGhlICpleHBlY3RlZCogdGVzdCBlcnJvci4KCiAtIEZvciBsaW5lYXIgbW9kZWxzIHRoZSBMT09DViBjYW4gYmUgcmVhZGlseSBvYnRhaW5lZCBmcm9tIHRoZSBmaXR0ZWQgbW9kZWw6IGkuZS4KCiBcW1x0ZXh0e0NWfSA9IFxmcmFjezF9e259XHN1bVxsaW1pdHNfe2k9MX1ebiBcZnJhY3tlX2leMn17KDEtaF97aWl9KV4yfVxdCgogd2l0aCAkZV9pJCB0aGUgcmVzaWR1YWxzIGZyb20gdGhlIG1vZGVsIHRoYXQgaXMgZml0dGVkIGJhc2VkIG9uIGFsbCB0cmFpbmluZyBkYXRhLgoKLS0tCgpBbiBhbHRlcm5hdGl2ZSB0byBMT09DViBpcyB0aGUgJGskLWZvbGQgY3Jvc3MgdmFsaWRhdGlvbiBwcm9jZWR1cmUuIEl0IGFsc28gZ2l2ZXMgYW4gZXN0aW1hdGUgb2YgdGhlIGV4cGVjdGVkIG91dHNhbXBsZSBlcnJvci4KCiMjIyMgJGskLWZvbGQgY3Jvc3MgdmFsaWRhdGlvbgoKLSAgUmFuZG9tbHkgZGl2aWRlIHRoZSB0cmFpbmluZyBkYXRhc2V0IGludG8gJGskIGFwcHJveGltYXRlbHkgZXF1YWwgc3Vic2V0cyAuIExldCAkU19qJCBkZW5vdGUgdGhlIGluZGV4IHNldCBvZiB0aGUgJGokdGggc3Vic2V0IChyZWZlcnJlZCB0byBhcyBhICoqZm9sZCoqKS4gTGV0ICRuX2okIGRlbm90ZSB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBmb2xkICRqJC4KCi0gVGhlICRrJC1mb2xkIGNyb3NzIHZhbGlkYXRpb24gZXN0aW1hdG9yIG9mIHRoZSBleHBlY3RlZCBvdXRzYW1wbGUgZXJyb3IgaXMgZ2l2ZW4gYnkKIFxbCiAgICAgXHRleHR7Q1Z9X2sgPSBcZnJhY3sxfXtrfVxzdW1fe2o9MX1eayBcZnJhY3sxfXtuX2p9IFxzdW1fe2lcaW4gU19qfSBcbGVmdChZX2kgLSBcaGF0e219XnstU19qfShcbWF0aGJme3h9X2kpXHJpZ2h0KV4yCiBcXQogd2hlcmUgJFxoYXR7bX1eey1TX2p9JCBpcyB0aGUgbW9kZWwgZml0dGVkIHVzaW5nIGFsbCB0cmFpbmluZyBkYXRhLCBleGNlcHQgb2JzZXJ2YXRpb25zIGluIGZvbGQgJGokIChpLmUuIG9ic2VydmF0aW9ucyAkaSBcaW4gU19qJCkuCgotLS0KClRoZSBjcm9zcyB2YWxpZGF0aW9uIGVzdGltYXRvcnMgb2YgdGhlIGV4cGVjdGVkIG91dHNhbXBsZSBlcnJvciBhcmUgbmVhcmx5IHVuYmlhc2VkLiBPbmUgYXJndW1lbnQgdGhhdCBoZWxwcyB0byB1bmRlcnN0YW5kIHdoZXJlIHRoZSBiaWFzIGNvbWVzIGZyb20gaXMgdGhlIGZhY3QgdGhhdCBlLmcuIGluIGRlIExPT0NWIGVzdGltYXRvciB0aGUgbW9kZWwgaXMgZml0IG9uIG9ubHkgJG4tMSQgb2JzZXJ2YXRpb25zLCB3aGVyZWFzIHdlIGFyZSBhaW1pbmcgYXQgZXN0aW1hdGluZyB0aGUgb3V0c2FtcGxlIGVycm9yIG9mIGEgbW9kZWwgZml0IG9uIGFsbCAkbiQgdHJhaW5pbmcgb2JzZXJ2YXRpb25zLiBGb3J0dW5hdGVseSwgdGhlIGJpYXMgaXMgb2Z0ZW4gc21hbGwgYW5kIGlzIGluIHByYWN0aWNlIGhhcmRseSBhIGNvbmNlcm4uCgokayQtZm9sZCBDViBpcyBjb21wdXRhdGlvbmFsbHkgbW9yZSBjb21wbGV4LgoKU2luY2UgQ1YgYW5kIENWJF9rJCBhcmUgZXN0aW1hdG9ycywgdGhleSBhbHNvIHNob3cgc2FtcGxpbmcgdmFyaWFiaWxpdHkuIFN0YW5kYXJkIGVycm9ycyBvZiB0aGUgQ1Ygb3IgQ1YkX2skIGNhbiBiZSBjb21wdXRlZC4gV2UgZG9uJ3Qgc2hvdyB0aGUgZGV0YWlscywgYnV0IGluIHRoZSBleGFtcGxlIHRoaXMgaXMgaWxsdXN0cmF0ZWQuCgojIyMgQmlhcyBWYXJpYW5jZSB0cmFkZS1vZmYKCkZvciB0aGUgZXhwZWN0ZWQgY29uZGl0aW9uYWwgdGVzdCBlcnJvciBpbiAkXG1hdGhiZnt4fSQsIGl0IGhvbGRzIHRoYXQKXGJlZ2lue2VxbmFycmF5Kn0KICBcdGV4dHtFfV97XGNhbHtUfX1cbGVmdFtcdGV4dHtFcnJ9X3tcY2Fse1R9fShcbWF0aGJme3h9KVxyaWdodF0KICAgICY9JiBcdGV4dHtFfV97WV4qLHtcY2Fse1R9fX1cbGVmdFsoXGhhdHttfShcbWF0aGJme3h9KS1ZXiopXjIgXG1pZCBcbWF0aGJme3h9XHJpZ2h0XSBcXAogICAgJj0mICBcdGV4dHt2YXJ9X3tcbWF0aGJme1l9fVxsZWZ0W1xoYXR7WX0oXG1hdGhiZnt4fSlcbWlkIFxtYXRoYmZ7eH1ccmlnaHRdICsoXG11KFxtYXRoYmZ7eH0pLVxtdV4qKFxtYXRoYmZ7eH0pKV4yK1x0ZXh0e3Zhcn1fe1leKn1cbGVmdFtZXipcbWlkIFxtYXRoYmZ7eH1ccmlnaHRdClxlbmR7ZXFuYXJyYXkqfQp3aGVyZSAkXG11KFxtYXRoYmZ7eH0pID0gXHRleHR7RX1fe1xtYXRoYmZ7WX19XGxlZnRbXGhhdHtZfShcbWF0aGJme3h9KVxtaWQgXG1hdGhiZnt4fVxyaWdodF0gXHRleHR7IGFuZCB9IFxtdV4qKFxtYXRoYmZ7eH0pPVx0ZXh0e0V9X3tZXip9XGxlZnRbWV4qXG1pZCBcbWF0aGJme3h9XHJpZ2h0XSQuCgotICoqYmlhcyoqOiAkXHRleHR7Ymlhc30oXG1hdGhiZnt4fSk9XG11KFxtYXRoYmZ7eH0pLVxtdV4qKFxtYXRoYmZ7eH0pJAoKLSAkXHRleHR7dmFyfV97WV4qfVxsZWZ0W1leKlxtaWQgXG1hdGhiZnt4fVxyaWdodF0kIGRvZXMgbm90IGRlcGVuZCBvbiB0aGUgbW9kZWwsIGFuZCBpcyByZWZlcnJlZCB0byBhcyB0aGUgKippcnJlZHVjaWJsZSB2YXJpYW5jZSoqLgoKLS0tCgpUaGUgaW1wb3J0YW5jZSBvZiB0aGUgYmlhcy12YXJpYW5jZSB0cmFkZS1vZmYgY2FuIGJlIHNlZW4gZnJvbSBhIG1vZGVsIHNlbGVjdGlvbiBwZXJzcGVjdGl2ZS4gV2hlbiB3ZSBhZ3JlZSB0aGF0IGEgZ29vZCBtb2RlbCBpcyBhIG1vZGVsIHRoYXQgaGFzIGEgc21hbGwgZXhwZWN0ZWQgY29uZGl0aW9uYWwgdGVzdCBlcnJvciBhdCBzb21lIHBvaW50ICRcbWF0aGJme3h9JCwgdGhlbiB0aGUgYmlhcy12YXJpYW5jZSB0cmFkZS1vZmYgc2hvd3MgdXMgdGhhdCBhIG1vZGVsIG1heSBiZSBiaWFzZWQgYXMgbG9uZyBhcyBpdCBoYXMgYSBzbWFsbCB2YXJpYW5jZSB0byBjb21wZW5zYXRlIGZvciB0aGUgYmlhcy4gIEl0IG9mdGVuIGhhcHBlbnMgdGhhdCBhIGJpYXNlZCBtb2RlbCBoYXMgYSBzdWJzdGFudGlhbCBzbWFsbGVyIHZhcmlhbmNlLiBXaGVuIHRoZXNlIHR3byBhcmUgY29tYmluZWQsIGEgc21hbGwgZXhwZWN0ZWQgdGVzdCBlcnJvciBtYXkgb2NjdXIuCgpBbHNvIG5vdGUgdGhhdCB0aGUgbW9kZWwgJG0kIHdoaWNoIGZvcm1zIHRoZSBiYXNpcyBvZiB0aGUgcHJlZGljdGlvbiBtb2RlbCAkXGhhdHttfShcbWF0aGJme3h9KSQgZG9lcyBOT1QgbmVlZCB0byBzYXRpc2Z5ICRtKFxtYXRoYmZ7eH0pPVxtdShcbWF0aGJme3h9KSQgb3IgJG0oXG1hdGhiZnt4fSk9XG11XiooXG1hdGhiZnt4fSkkLiBUaGUgbW9kZWwgJG0kIGlzIGtub3duIGJ5IHRoZSBkYXRhLWFuYWx5c3QgKGl0cyB0aGUgYmFzaXMgb2YgdGhlIHByZWRpY3Rpb24gbW9kZWwpLCB3aGVyZWFzICRcbXUoXG1hdGhiZnt4fSkkIGFuZCAkXG11XiooXG1hdGhiZnt4fSkkIGFyZSBnZW5lcmFsbHkgdW5rbm93biB0byB0aGUgZGF0YS1hbmFseXN0LiBXZSBvbmx5IGhvcGUgdGhhdCAkbSQgc2VydmVzIHdlbGwgYXMgYSBwcmVkaWN0aW9uIG1vZGVsLgoKLS0tCgojIyMgSW4gcHJhY3RpY2UKCldlIHVzZSBjcm9zcyB2YWxpZGF0aW9uIHRvIGVzdGltYXRlIHRoZSBsYW1iZGEgcGVuYWx0eSBmb3IgcGVuYWxpc2VkIHJlZ3Jlc3Npb246CgotIFJpZGdlIFJlZ3Jlc3Npb24KLSBMYXNzbwotIEJ1aWxkIG1vZGVscywgZS5nLiBzZWxlY3QgdGhlIG51bWJlciBvZiBQQ3MgZm9yIFBDQSByZWdyZXNzaW9uCi0gU3BsaW5lcwoKIyMjIFRveGljb2dlbm9taWNzIGV4YW1wbGUKCiMjIyMgTGFzc28KCmBgYHtyfQpzZXQuc2VlZCgxNSkKbGlicmFyeShnbG1uZXQpCm1Ddkxhc3NvIDwtIGN2LmdsbW5ldCgKICB4ID0gdG94RGF0YVssLTFdICU+JQogICAgYXMubWF0cml4LAogIHkgPSB0b3hEYXRhICU+JQogICAgcHVsbChCQSksCiAgYWxwaGEgPSAxKSAgIyBsYXNzbyBhbHBoYT0xCgpwbG90KG1Ddkxhc3NvKQpgYGAKCkRlZmF1bHQgQ1YgcHJvY2VkdXJlIGluIFx0ZXh0c2Z7Y3YuZ2xtbmV0fSBpcyAkaz0xMCQtZm9sZCBDVi4KClRoZSBHcmFwaHMgc2hvd3MKCi0gMTAtZm9sZCBDViBlc3RpbWF0ZXMgb2YgdGhlIGV4dHJhLXNhbXBsZSBlcnJvciBhcyBhIGZ1bmN0aW9uIG9mIHRoZSBsYXNzbyBwZW5hbHR5IHBhcmFtZXRlciAkXGxhbWJkYSQuCi0gZXN0aW1hdGUgcGx1cyBhbmQgbWludXMgb25jZSB0aGUgZXN0aW1hdGVkIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBDViBlc3RpbWF0ZSAoZ3JleSBiYXJzKQotIE9uIHRvcCB0aGUgbnVtYmVyIG9mIG5vbi16ZXJvIHJlZ3Jlc3Npb24gcGFyYW1ldGVyIGVzdGltYXRlcyBhcmUgc2hvd24uCgpUd28gdmVydGljYWwgcmVmZXJlbmNlIGxpbmVzIGFyZSBhZGRlZCB0byB0aGUgZ3JhcGguIFRoZXkgY29ycmVzcG9uZCB0bwoKLSB0aGUgJFxsb2coXGxhbWJkYSkkIHRoYXQgZ2l2ZXMgdGhlIHNtYWxsZXN0IENWIGVzdGltYXRlIG9mIHRoZSBleHRyYS1zYW1wbGUgZXJyb3IsIGFuZAotIHRoZSBsYXJnZXN0ICRcbG9nKFxsYW1iZGEpJCB0aGF0IGdpdmVzIGEgQ1YgZXN0aW1hdGUgb2YgdGhlIGV4dHJhLXNhbXBsZSBlcnJvciB0aGF0IGlzIHdpdGhpbiBvbmUgc3RhbmRhcmQgZXJyb3IgZnJvbSB0aGUgc21hbGxlc3QgZXJyb3IgZXN0aW1hdGUuCi0gVGhlIGxhdHRlciBjaG9pY2Ugb2YgJFxsYW1iZGEkIGhhcyBubyBmaXJtIHRoZW9yZXRpY2FsIGJhc2lzLCBleGNlcHQgdGhhdCBpdCBzb21laG93IGFjY291bnRzIGZvciB0aGUgaW1wcmVjaXNpb24gb2YgdGhlIGVycm9yIGVzdGltYXRlLiBPbmUgY291bGQgbG9vc2VseSBzYXkgdGhhdCB0aGlzICRcZ2FtbWEkIGNvcnJlc3BvbmRzIHRvIHRoZSBzbWFsbGVzdCBtb2RlbCAoaS5lLiBsZWFzdCBudW1iZXIgb2YgcHJlZGljdG9ycykgdGhhdCBnaXZlcyBhbiBlcnJvciB0aGF0IGlzIHdpdGhpbiBtYXJnaW4gb2YgZXJyb3Igb2YgdGhlIGVycm9yIG9mIHRoZSBiZXN0IG1vZGVsLgoKLS0tCgpgYGB7cn0KbUxhc3NvT3B0IDwtIGdsbW5ldCgKICB4ID0gdG94RGF0YVssLTFdICU+JQogICAgYXMubWF0cml4LAogIHkgPSB0b3hEYXRhICU+JQogICAgcHVsbChCQSksCiAgICBhbHBoYSA9IDEsCiAgICBsYW1iZGEgPSBtQ3ZMYXNzbyRsYW1iZGEubWluKQoKc3VtbWFyeShjb2VmKG1MYXNzb09wdCkpCmBgYAoKCldpdGggdGhlIG9wdGltYWwgJFxsYW1iZGEkIChzbWFsbGVzdCBlcnJvciBlc3RpbWF0ZSkgdGhlIG91dHB1dCBzaG93cyB0aGUgYHIgIG5yb3coc3VtbWFyeShjb2VmKG1MYXNzb09wdCkpKWAgbm9uLXplcm8gZXN0aW1hdGVkIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIChzcGFyc2Ugc29sdXRpb24pLgoKLS0tCgpgYGB7cn0KbUxhc3NvMXNlIDwtIGdsbW5ldCgKICB4ID0gdG94RGF0YVssLTFdICU+JQogICAgYXMubWF0cml4LAogICAgeT0gdG94RGF0YSAlPiUKICAgICAgcHVsbChCQSksCiAgICBhbHBoYSA9IDEsCiAgICBsYW1iZGEgPSBtQ3ZMYXNzbyRsYW1iZGEuMXNlKQoKbUxhc3NvMXNlICU+JQogIGNvZWYgJT4lCiAgc3VtbWFyeQpgYGAKClRoaXMgc2hvd3MgdGhlIHNvbHV0aW9uIGZvciB0aGUgbGFyZ2VzdCAkXGxhbWJkYSQgd2l0aGluIG9uZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgb3B0aW1hbCBtb2RlbC4gTm93IG9ubHkgYHIgIG5yb3coc3VtbWFyeShjb2VmKG1MYXNzbzFzZSkpKWAgbm9uLXplcm8gZXN0aW1hdGVzIHJlc3VsdC4KCi0tLQoKIyMjIyBSaWRnZQoKYGBge3J9Cm1DdlJpZGdlIDwtIGN2LmdsbW5ldCgKICB4ID0gdG94RGF0YVssLTFdICU+JQogICAgYXMubWF0cml4LAogICAgeSA9IHRveERhdGEgJT4lCiAgICAgIHB1bGwoQkEpLAogICAgICBhbHBoYSA9IDApICAjIHJpZGdlIGFscGhhPTAKCnBsb3QobUN2UmlkZ2UpCmBgYAoKLSBSaWRnZSBkb2VzIG5vdCBzZWVtIHRvIGhhdmUgb3B0aW1hbCBzb2x1dGlvbi4KLSAxMC1mb2xkIENWIGlzIGFsc28gbGFyZ2VyIHRoYW4gZm9yIGxhc3NvLgoKLS0tCgojIyMjIFBDQSByZWdyZXNzaW9uCgpgYGB7ciBmaWcua2VlcCA9ICJub25lIiwgd2FybmluZyA9IEZBTFNFfQpzZXQuc2VlZCgxMjY0KQpsaWJyYXJ5KERBQUcpCgp0b3ggPC0gZGF0YS5mcmFtZSgKICBZID0gdG94RGF0YSAlPiUKICAgIHB1bGwoQkEpLAogIFBDID0gWmspCgpQQy5zZXEgPC0gMToyNQpFcnIgPC0gbnVtZXJpYygyNSkKCm1DdlBjYSA8LSBjdi5sbSgKICBZflBDLjEsCiAgZGF0YSA9IHRveCwKICBtID0gNSwKICBwcmludGl0ID0gRkFMU0UpCgpFcnJbMV08LWF0dHIobUN2UGNhLCJtcyIpCgpmb3IoaSBpbiAyOjI1KSB7CiAgbUN2UGNhIDwtIGN2LmxtKAogICAgYXMuZm9ybXVsYSgKICAgICAgcGFzdGUoIlkgfiBQQy4xICsgIiwKICAgICAgICBwYXN0ZSgiUEMuIiwgMjppLCBjb2xsYXBzZSA9ICIrIiwgc2VwPSIiKSwKICAgICAgICBzZXA9IiIKICAgICAgKQogICAgKSwKICAgIGRhdGEgPSB0b3gsCiAgICBtID0gNSwKICAgIHByaW50aXQgPSBGQUxTRSkKICBFcnJbaV08LWF0dHIobUN2UGNhLCJtcyIpCn0KYGBgCgotIEhlcmUgd2UgaWxsdXN0cmF0ZSBwcmluY2lwYWwgY29tcG9uZW50IHJlZ3Jlc3Npb24uCgotIFRoZSBtb3N0IGltcG9ydGFudCBQQ3MgYXJlIHNlbGVjdGVkIGluIGEgZm9yd2FyZCBtb2RlbCBzZWxlY3Rpb24gcHJvY2VkdXJlLgoKLSBXaXRoaW4gdGhlIG1vZGVsIHNlbGVjdGlvbiBwcm9jZWR1cmUgdGhlIG1vZGVscyBhcmUgZXZhbHVhdGVkIHdpdGggNS1mb2xkIENWIGVzdGltYXRlcyBvZiB0aGUgb3V0c2FtcGxlIGVycm9yLgoKLSBJdCBpcyBpbXBvcnRhbnQgdG8gcmVhbGlzZSB0aGF0IGEgZm9yd2FyZCBtb2RlbCBzZWxlY3Rpb24gcHJvY2VkdXJlIHdpbGwgbm90IG5lY2Vzc2FyaWx5IHJlc3VsdCBpbiB0aGUgYmVzdCBwcmVkaWN0aW9uIG1vZGVsLCBwYXJ0aWN1bGFybHkgYmVjYXVzZSB0aGUgb3JkZXIgb2YgdGhlIFBDcyBpcyBnZW5lcmFsbHkgbm90IHJlbGF0ZWQgdG8gdGhlIGltcG9ydGFuY2Ugb2YgdGhlIFBDcyBmb3IgcHJlZGljdGluZyB0aGUgb3V0Y29tZS4KCi0gQSBzdXBlcnZpc2VkIFBDIHdvdWxkIGJlIGJldHRlci4KCmBgYHtyfQpwUENyZWcgPC0gZGF0YS5mcmFtZShQQy5zZXEsIEVycikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gUEMuc2VxLCB5ID0gRXJyKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGxpbmUoCiAgICB5aW50ZXJjZXB0ID0gYygKICAgICAgbUN2TGFzc28kY3ZtW21Ddkxhc3NvJGxhbWJkYT09bUN2TGFzc28kbGFtYmRhLm1pbl0sCiAgICAgIG1Ddkxhc3NvJGN2bVttQ3ZMYXNzbyRsYW1iZGE9PW1Ddkxhc3NvJGxhbWJkYS4xc2VdKSwKICAgIGNvbCA9ICJyZWQiKSArCiAgeGxpbSgxLDI2KQoKZ3JpZC5hcnJhbmdlKAogIHBQQ3JlZywKICBwUENyZWcgKyB5bGltKDAsNSksCiAgbmNvbD0yKQpgYGAKCi0gVGhlIGdyYXBoIHNob3dzIHRoZSBDViBlc3RpbWF0ZSBvZiB0aGUgb3V0c2FtcGxlIGVycm9yIGFzIGEgZnVuY3Rpb24gb2YgdGhlIG51bWJlciBvZiBzcGFyc2UgUENzIGluY2x1ZGVkIGluIHRoZSBtb2RlbC4KCi0gQSB2ZXJ5IHNtYWxsIGVycm9yIGlzIG9idGFpbmVkIHdpdGggdGhlIG1vZGVsIHdpdGggb25seSB0aGUgZmlyc3QgUEMuIFRoZSBiZXN0IG1vZGVsIHdpdGggMyBQQ3MuCgotIFRoZSB0d28gdmVydGljYWwgcmVmZXJlbmNlIGxpbmVzIGNvcnJlc3BvbmQgdG8gdGhlIGVycm9yIGVzdGltYXRlcyBvYnRhaW5lZCB3aXRoIGxhc3NvIChvcHRpbWFsICRcbGFtYmRhJCBhbmQgbGFyZ2VzdCAkXGxhbWJkYSQgd2l0aGluIG9uZSBzdGFuZGFyZCBlcnJvcikuCgotIFRodXMgYWx0aG91Z2ggdGhlcmUgd2FzIGEgcHJpb3JpIG5vIGd1YXJhbnRlZSB0aGF0IHRoZSBmaXJzdCBQQ3MgYXJlIHRoZSBtb3N0IHByZWRpY3RpdmUsIGl0IHNlZW1zIHRvIGJlIHRoZSBjYXNlIGhlcmUgKHdlIHdlcmUgbHVja3khKS4KCi0gTW9yZW92ZXIsIHRoZSBmaXJzdCBQQyByZXN1bHRlZCBpbiBhIHNtYWxsIG91dHNhbXBsZSBlcnJvci4KCi0gTm90ZSB0aGF0IHRoZSBncmFwaCBkb2VzIG5vdCBpbmRpY2F0ZSB0aGUgdmFyaWFiaWxpdHkgb2YgdGhlIGVycm9yIGVzdGltYXRlcyAobm8gZXJyb3IgYmFycykuCgotIEFsc28gbm90ZSB0aGF0IHRoZSBncmFwaCBjbGVhcmx5IGlsbHVzdHJhdGVzIHRoZSBlZmZlY3Qgb2Ygb3ZlcmZpdHRpbmc6IGluY2x1ZGluZyB0b28gbWFueSBQQ3MgY2F1c2VzIGEgbGFyZ2Ugb3V0c2FtcGxlIGVycm9yLgoKIyMjIExpZGFyIEV4YW1wbGU6IHNwbGluZXMKCi0gV2UgdXNlIHRoZSBtZ2N2IHBhY2thZ2UgdG8gZml0IHRoZSBzcGxpbmUgbW9kZWwgdG8gdGhlIGxpZGFyIGRhdGEuCi0gQSBiZXR0ZXIgYmFzaXMgaXMgdXNlZCB0aGFuIHRoZSB0cnVuY2F0ZWQgc3BsaW5lIGJhc2lzCi0gVGhpbiBwbGF0ZSBzcGxpbmVzIGFyZSBhbHNvIGxpbmVhciBzbW9vdGhlcnMsIGkuZS4KJFxoYXR7WX0gPSBcaGF0e219KFxtYXRoYmZ7WH0pID0gXG1hdGhiZntTWX0kCi0gU28gdGhlaXIgdmFyaWFuY2UgY2FuIGJlIGVhc2lseSBjYWxjdWxhdGVkLgotIFRoZSByaWRnZS9zbW9vdGhuZXNzIHBlbmFsdHkgaXMgY2hvc2VuIGJ5IGdlbmVyYWxpemVkIGNyb3NzIHZhbGlkYXRpb24uCgpgYGB7cn0KbGlicmFyeShtZ2N2KQpnYW1maXQgPC0gZ2FtKGxvZ3JhdGlvIH4gcyhyYW5nZSksIGRhdGEgPSBsaWRhcikKZ2FtZml0JHNwCgpwTGlkYXIgKwogIGdlb21fbGluZShhZXMoeCA9IGxpZGFyJHJhbmdlLCB5ID0gZ2FtZml0JGZpdHRlZCksIGx3ZCA9IDIpCmBgYAoKIyMgTW9yZSBnZW5lcmFsIGVycm9yIGRlZmluaXRpb25zCgpTbyBmYXIgd2Ugb25seSBsb29rZWQgYXQgY29udGludW91cyBvdXRjb21lcyAkWSQgYW5kIGVycm9ycyBkZWZpbmVkIGluIHRlcm1zIG9mIHRoZSBzcXVhcmVkIGxvc3MgJChcaGF0e219KFxtYXRoYmZ7eH0pLVleKileMiQuCgpNb3JlIGdlbmVyYWxseSwgYSAqKmxvc3MgZnVuY3Rpb24qKiBtZWFzdXJlcyBhbiBkaXNjcmVwYW5jeSBiZXR3ZWVuIHRoZSBwcmVkaWN0aW9uICRcaGF0e219KFxtYXRoYmZ7eH0pJCBhbmQgYW4gaW5kZXBlbmRlbnQgb3V0Y29tZSAkWV4qJCB0aGF0IGNvcnJlc3BvbmRzIHRvICRcbWF0aGJme3h9JC4KCgpTb21lIGV4YW1wbGVzIGZvciBjb250aW51b3VzICRZJDoKXGJlZ2lue2VxbmFycmF5Kn0KICBMKFleKixcaGF0e219KFxtYXRoYmZ7eH0pKQogICAgJj0mIChcaGF0e219KFxtYXRoYmZ7eH0pLVleKileMiBcO1w7XHRleHR7KHNxdWFyZWQgZXJyb3IpfSBcXAogIEwoWV4qLFxoYXR7bX0oXG1hdGhiZnt4fSkpCiAgICAmPSYgXHZlcnRcaGF0e219KFxtYXRoYmZ7eH0pLVleKlx2ZXJ0IFw7XDtcdGV4dHsoYWJzb2x1dGUgZXJyb3IpfSBcXAogICBMKFleKixcaGF0e219KFxtYXRoYmZ7eH0pKQogICAgJj0mIDIgXGludF97XGNhbHtZfX0gZl95KHkpIFxsb2dcZnJhY3tmX3koeSl9e2Zfe1xoYXR7bX19KHkpfSBkeSBcO1w7XHRleHR7KGRldmlhbmNlKX0uClxlbmR7ZXFuYXJyYXkqfQoKCkluIHRoZSBleHByZXNzaW9uIG9mIHRoZSBkZXZpYW5jZQoKLSAkZl95JCBkZW5vdGVzIHRoZSBkZW5zaXR5IGZ1bmN0aW9uIG9mIGEgZGlzdHJpYnV0aW9uIHdpdGggbWVhbiBzZXQgdG8gJHkkIChjZnIuIHBlcmZlY3QgZml0KSwgYW5kCi0gJGZfe1xoYXR7bX19JCBpcyB0aGUgZGVuc2l0eSBmdW5jdGlvbiBvZiB0aGUgc2FtZSBkaXN0cmlidXRpb24gYnV0IHdpdGggbWVhbiBzZXQgdG8gdGhlIHByZWRpY3RlZCBvdXRjb21lICRcaGF0e219KFxtYXRoYmZ7eH0pJC4KCi0tLQoKV2l0aCBhIGdpdmVuIGxvc3MgZnVuY3Rpb24sIHRoZSBlcnJvcnMgYXJlIGRlZmluZWQgYXMgZm9sbG93czoKLSBUZXN0IG9yIGdlbmVyYWxpc2F0aW9uIG9yIG91dHNhbXBsZSBlcnJvcgogICAgXFsKICAgICAgXHRleHR7RXJyfV97XGNhbHtUfX0gPSBcdGV4dHtFfV97WV4qLFheKn1cbGVmdFtMKFleKixcaGF0e219KFxtYXRoYmZ7WH1eKikpXHJpZ2h0XQogICAgXF0KCi0gVHJhaW5pbmcgZXJyb3IKICBcWwogICAgXG92ZXJsaW5le1x0ZXh0e2Vycn19ID0gXGZyYWN7MX17bn1cc3VtX3tpPTF9Xm4gTChZX2ksXGhhdHttfShcbWF0aGJme3h9X2kpKQogIFxdCgotICRcbGRvdHMkCgotLS0KCldoZW4gYW4gZXhwb25lbnRpYWwgZmFtaWx5IGRpc3RyaWJ1dGlvbiBpcyBhc3N1bWVkIGZvciB0aGUgb3V0Y29tZSBkaXN0cmlidXRpb24sIGFuZCB3aGVuIHRoZSBkZXZpYW5jZSBsb3NzIGlzIHVzZWQsIHRoZSBpbnNhbXBsZSBlcnJvciBjYW4gYmUgZXN0aW1hdGVkIGJ5IG1lYW5zIG9mIHRoZSBBSUMgYW5kIEJJQy4KCiMjIyBBa2Fpa2UncyBJbmZvcm1hdGlvbiBDcml0ZXJpb24gKEFJQykKClRoZSBBSUMgZm9yIGEgbW9kZWwgJG0kIGlzIGdpdmVuIGJ5ClxbClx0ZXh0e0FJQ30gPSAtMiBcbG4gXGhhdHtMfShtKSArMnAKXF0Kd2hlcmUgJFxoYXR7TH0obSkkIGlzIHRoZSBtYXhpbWlzZWQgbGlrZWxpaG9vZCBmb3IgbW9kZWwgJG0kLgoKV2hlbiBhc3N1bWluZyBub3JtYWxseSBkaXN0cmlidXRlZCBlcnJvciB0ZXJtcyBhbmQgaG9tb3NjZWRhc3RpY2l0eSwgdGhlIEFJQyBiZWNvbWVzClxbClx0ZXh0e0FJQ30gPSBuXGxuIFx0ZXh0e1NTRX0obSkgKzJwID0gblxsbihuXG92ZXJsaW5le1x0ZXh0e2Vycn19KG0pKSArIDJwClxdCndpdGggJFx0ZXh0e1NTRX0obSkkIHRoZSByZXNpZHVhbCBzdW0gb2Ygc3F1YXJlcyBvZiBtb2RlbCAkbSQuCgpJbiBsaW5lYXIgbW9kZWxzIHdpdGggbm9ybWFsIGVycm9yIHRlcm1zLCBNYWxsb3cncyAkQ19wJCBjcml0ZXJpb24gKHN0YXRpc3RpYykgaXMgYSBsaW5lYXJpc2VkIHZlcnNpb24gb2YgQUlDIGFuZCBpdCBpcyBhbiB1bmJpYXNlZCBlc3RpbWF0b3Igb2YgdGhlIGluLXNhbXBsZSBlcnJvci4KCi0tLQoKIyMjIEJheWVzaWFuIEluZm9ybWF0aW9uIENyaXRlcmlvbiAoQklDKX0KClRoZSBCSUMgZm9yIGEgbW9kZWwgJG0kIGlzIGdpdmVuIGJ5ClxbClx0ZXh0e0JJQ30gPSAtMiBcbG4gXGhhdHtMfShtKSArcFxsbihuKQpcXQp3aGVyZSAkXGhhdHtMfShtKSQgaXMgdGhlIG1heGltaXNlZCBsaWtlbGlob29kIGZvciBtb2RlbCAkbSQuCgpXaGVuIGFzc3VtaW5nIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGVycm9yIHRlcm1zIGFuZCBob21vc2NlZGFzdGljaXR5LCB0aGUgQklDIGJlY29tZXMKXFsKXHRleHR7QklDfSA9IG5cbG4gXHRleHR7U1NFfShtKSArcFxsbihuKSA9IG5cbG4oblxvdmVybGluZXtcdGV4dHtlcnJ9fShtKSkgKyBwXGxuKG4pClxdCndpdGggJFx0ZXh0e1NTRX0obSkkIHRoZSByZXNpZHVhbCBzdW0gb2Ygc3F1YXJlcyBvZiBtb2RlbCAkbSQuCgpXaGVuIGxhcmdlIGRhdGFzZXRzIGFyZSB1c2VkLCB0aGUgQklDIHdpbGwgZmF2b3VyIHNtYWxsZXIgbW9kZWxzIHRoYW4gdGhlIEFJQy4KCi0tLQoKIyMgVHJhaW5pbmcgYW5kIHRlc3Qgc2V0cwoKU29tZXRpbWVzLCB3aGVuIGEgbGFyZ2UgKHRyYWluaW5nKSBkYXRhc2V0IGlzIGF2YWlsYWJsZSwgb25lIG1heSBkZWNpZGUgdGhlIHNwbGl0IHRoZSBkYXRhc2V0IHJhbmRvbWx5IGluIGEKCi0gKip0cmFpbmluZyBkYXRhc2V0Kio6CiAgIGRhdGEgYXJlIHVzZWQgZm9yIG1vZGVsIGZpdHRpbmcgYW5kIGZvciBtb2RlbCBidWlsZGluZyBvciBmZWF0dXJlIHNlbGVjdGlvbiAodGhpcyBtYXkgcmVxdWlyZSBlLmcuIGNyb3NzIHZhbGlkYXRpb24pCgotICoqdGVzdCBkYXRhc2V0Kio6CiAgIHRoaXMgZGF0YSBhcmUgdXNlZCB0byBldmFsdWF0ZSB0aGUgZmluYWwgbW9kZWwgKHJlc3VsdCBvZiBtb2RlbCBidWlsZGluZykuIEFuIHVuYmlhc2VkIGVzdGltYXRlIG9mIHRoZSBvdXRzYW1wbGUgZXJyb3IgKGkuZS4gdGVzdCBvciBnZW5lcmFsaXNhdGlvbiBlcnJvcikgYmFzZWQgb24gdGhpcyB0ZXN0IGRhdGEgaXMKICBcWwogICAgIFxmcmFjezF9e219IFxzdW1fe2k9MX1ebSBcbGVmdChcaGF0e219KFxtYXRoYmZ7eH1faSktWV9pXHJpZ2h0KV4yLAogIFxdCiAgd2hlcmUKICAgIC0gJChZXzEsXG1hdGhiZnt4fV8xKSwgXGxkb3RzLCAoWV9tLFxtYXRoYmZ7eH1fbSkkIGRlbm90ZSB0aGUgJG0kIG9ic2VydmF0aW9ucyBpbiB0aGUgdGVzdCBkYXRhc2V0CgogICAgLSAkXGhhdHttfSQgaXMgZXN0aW1hdGVkIGZyb20gdXNpbmcgdGhlIHRyYWluaW5nIGRhdGEgKHRoaXMgbWF5IGFsc28gYmUgdGhlIHJlc3VsdCBmcm9tIG1vZGVsIGJ1aWxkaW5nLCB1c2luZyBvbmx5IHRoZSB0cmFpbmluZyBkYXRhKS4KCi0tLQoKTm90ZSB0aGF0IHRoZSB0cmFpbmluZyBkYXRhc2V0IGlzIHVzZWQgZm9yIG1vZGVsIGJ1aWxkaW5nIG9yIGZlYXR1cmUgc2VsZWN0aW9uLiBUaGlzIGFsc28gcmVxdWlyZXMgdGhlIGV2YWx1YXRpb24gb2YgbW9kZWxzLiBGb3IgdGhlc2UgZXZhbHVhdGlvbnMgdGhlIG1ldGhvZHMgZnJvbSB0aGUgcHJldmlvdXMgc2xpZGVzIGNhbiBiZSB1c2VkIChlLmcuIGNyb3NzIHZhbGlkYXRpb24sICRrJC1mb2xkIENWLCBNYWxsb3cncyAkQ19wJCkuIFRoZSB0ZXN0IGRhdGFzZXQgaXMgb25seSB1c2VkIGZvciB0aGUgIGV2YWx1YXRpb24gb2YgdGhlIGZpbmFsIG1vZGVsIChlc3RpbWF0ZWQgYW5kIGJ1aWxkIGZyb20gdXNpbmcgb25seSB0aGUgdHJhaW5pbmcgZGF0YSkuIFRoZSBlc3RpbWF0ZSBvZiB0aGUgb3V0c2FtcGxlIGVycm9yIGJhc2VkIG9uIHRoZSB0ZXN0IGRhdGFzZXQgaXMgdGhlIGJlc3QgcG9zc2libGUgZXN0aW1hdGUgaW4gdGhlIHNlbnNlIHRoYXQgaXQgaXMgdW5iaWFzZWQuIFRoZSBvYnNlcnZhdGlvbnMgdXNlZCBmb3IgdGhpcyBlc3RpbWF0aW9uIGFyZSBpbmRlcGVuZGVudCBvZiB0aGUgb2JzZXJ2YXRpb25zIGluIHRoZSB0cmFpbmluZyBkYXRhLgpIb3dldmVyLCBpZiB0aGUgbnVtYmVyIG9mIGRhdGEgcG9pbnRzIGluIHRoZSB0ZXN0IGRhdGFzZXQgKCRtJCkgaXMgc21hbGwsIHRoZSBlc3RpbWF0ZSBvZiB0aGUgb3V0c2FtcGxlIGVycm9yIG1heSBzaG93IGxhcmdlIHZhcmlhbmNlIGFuZCBoZW5jZSBpcyBub3QgcmVsaWFibGUuCgojIExvZ2lzdGljIFJlZ3Jlc3Npb24gQW5hbHlzaXMgZm9yIEhpZ2ggRGltZW5zaW9uYWwgRGF0YQoKIyMgQnJlYXN0IENhbmNlciBFeGFtcGxlCgotIFNjaG1pZHQgKmV0IGFsLiosIDIwMDgsIENhbmNlciBSZXNlYXJjaCwgKio2OCoqLCA1NDA1LTU0MTMKCi0gR2VuZSBleHByZXNzaW9uIHBhdHRlcm5zIGluICRuPTIwMCQgYnJlYXN0IHR1bW9ycyB3ZXJlIGludmVzdGlnYXRlZCAoJHA9MjIyODMkIGdlbmVzKQoKLSBBZnRlciBzdXJnZXJ5IHRoZSB0dW1vcnMgd2VyZSBncmFkZWQgYnkgYSBwYXRob2xvZ2lzdCAoc3RhZ2UgMSwyLDMpCgotIEhlcmUgdGhlIG9iamVjdGl2ZSBpcyB0byBwcmVkaWN0IHN0YWdlIDMgZnJvbSB0aGUgZ2VuZSBleHByZXNzaW9uIGRhdGEgKHByZWRpY3Rpb24gb2YgYmluYXJ5IG91dGNvbWUpCgotIElmIHRoZSBwcmVkaWN0aW9uIG1vZGVsIHdvcmtzIHdlbGwsIGl0IGNhbiBiZSB1c2VkIHRvIHByZWRpY3QgdGhlIHN0YWdlIGZyb20gYSBiaW9wc3kgc2FtcGxlLgoKIyMgRGF0YQoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNCaW9jTWFuYWdlcjo6aW5zdGFsbCgiZ2VuZWZ1IikKI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJicmVhc3RDYW5jZXJNQUlOWiIpCgpsaWJyYXJ5KGdlbmVmdSkKbGlicmFyeShicmVhc3RDYW5jZXJNQUlOWikKZGF0YShtYWlueikKClggPC0gdChleHBycyhtYWlueikpICMgZ2VuZSBleHByZXNzaW9ucwpuIDwtIG5yb3coWCkKSCA8LSBkaWFnKG4pLTEvbiptYXRyaXgoMSxuY29sPW4sbnJvdz1uKQpYIDwtIEglKiVYClkgPC0gaWZlbHNlKHBEYXRhKG1haW56KSRncmFkZT09MywxLDApCnRhYmxlKFkpCgpzdmRYIDwtIHN2ZChYKQprIDwtIDIKWmsgPC0gc3ZkWCR1WywxOmtdICUqJSBkaWFnKHN2ZFgkZFsxOmtdKQpjb2xuYW1lcyhaaykgPC0gcGFzdGUwKCJaIiwxOmspCgpaayAlPiUKICBhcy5kYXRhLmZyYW1lICU+JQogIG11dGF0ZShncmFkZSA9IFkgJT4lIGFzLmZhY3RvcikgJT4lCiAgZ2dwbG90KGFlcyh4PSBaMSwgeSA9IFoyLCBjb2xvciA9IGdyYWRlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMpCmBgYAoKLS0tCgojIyBMb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscwoKQmluYXJ5IG91dGNvbWVzIGFyZSBvZnRlbiBhbmFseXNlZCB3aXRoICoqbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMqKi4KCkxldCAkWSQgZGVub3RlIHRoZSBiaW5hcnkgKDEvMCwgY2FzZS9jb250cm9sLCBwb3NpdGl2ZS9uZWdhdGl2ZSkgb3V0Y29tZSwgYW5kICRcbWF0aGJme3h9JCB0aGUgJHAkLWRpbWVuc2lvbmFsIHByZWRpY3Rvci4KCkxvZ2lzdGljIHJlZ3Jlc3Npb24gIGFzc3VtZXMKXFsKICAgWSBcbWlkIFxtYXRoYmZ7eH0gXHNpbSBcdGV4dHtCZXJub3VsbGl9KFxwaShcbWF0aGJme3h9KSkKXF0Kd2l0aCAkXHBpKFxtYXRoYmZ7eH0pID0gXHRleHR7UH1cbGVmdFtZPTFcbWlkIFxtYXRoYmZ7eH1ccmlnaHRdJCBhbmQKXFsKICAgXGxuIFxmcmFje1xwaShcbWF0aGJme3h9KX17MS1ccGkoXG1hdGhiZnt4fSl9PVxiZXRhXzAgKyBcYm9sZHN5bWJvbHtcYmV0YX1eVFxtYXRoYmZ7eH0uClxdCgpUaGUgcGFyYW1ldGVycyBhcmUgdHlwaWNhbGx5IGVzdGltYXRlZCBieSBtYXhpbWlzaW5nIHRoZSBsb2ctbGlrZWxpaG9vZCwgd2hpY2ggaXMgZGVub3RlZCBieSAkbChcbWF0aGJmewpcYmV0YX0pJCwgaS5lLgpcWwogICBcaGF0e1xib2xkc3ltYm9se1xiZXRhfX0gPSBcdGV4dHtBcmdNYXh9X1xiZXRhIGwoXGJvbGRzeW1ib2x7XGJldGF9KS4KXF0KCi0gTWF4aW11bSBsaWtlbGlob29kIGlzIG9ubHkgYXBwbGljYWJsZSB3aGVuICRuPnAkLgoKLSBXaGVuICRwPm4kIHBlbmFsaXNlZCBtYXhpbXVtIGxpa2VsaWhvb2QgbWV0aG9kcyBhcmUgYXBwbGljYWJsZS4KCi0tLQoKIyMgUGVuYWxpemVkIG1heGltdW0gbGlrZWxpaG9vZAoKUGVuYWxpc2VkIGVzdGltYXRpb24gbWV0aG9kcyAoZS5nLiBsYXNzbyBhbmQgcmlkZ2UpIGNhbiBhbHMgYmUgYXBwbGllZCB0byBtYXhpbXVtIGxpa2VsaWhvb2QsIHJlc3VsdGluZyBpbiB0aGUgKipwZW5hbGlzZWQgbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRlKiouCgpMYXNzbzoKXFsKICBcaGF0e1xib2xkc3ltYm9se1xiZXRhfX0gPSBcdGV4dHtBcmdNYXh9X1xiZXRhIGwoXGJvbGRzeW1ib2x7XGJldGF9KSAtXGxhbWJkYSBcVmVydCBcYm9sZHN5bWJvbHtcYmV0YX1cVmVydF8xLgpcXQoKUmlkZ2U6ClxbCiAgXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19ID0gXHRleHR7QXJnTWF4fV9cYmV0YSBsKFxib2xkc3ltYm9se1xiZXRhfSkgLVxsYW1iZGEgXFZlcnQgXGJvbGRzeW1ib2x7XGJldGF9XFZlcnRfMl4yLgpcXQoKT25jZSB0aGUgcGFyYW1ldGVycyBhcmUgZXN0aW1hdGVkLCB0aGUgbW9kZWwgbWF5IGJlIHVzZWQgdG8gY29tcHV0ZQpcWwogIFxoYXR7XHBpfShcbWF0aGJme3h9KSA9IFxoYXR7XHRleHR7UH19XGxlZnRbWT0xXG1pZCBcbWF0aGJme3h9XHJpZ2h0XS4KXF0KV2l0aCB0aGVzZSBlc3RpbWF0ZWQgcHJvYmFiaWxpdGllcyB0aGUgcHJlZGljdGlvbiBydWxlIGJlY29tZXMKXGJlZ2lue2VxbmFycmF5Kn0KICBcaGF0e1xwaX0oXG1hdGhiZnt4fSkgJlxsZXEgYyYgXHRleHR7cHJlZGljdCB9IFk9MCBcXAogIFxoYXR7XHBpfShcbWF0aGJme3h9KSAmPmMgJiBcdGV4dHtwcmVkaWN0IH0gWT0xClxlbmR7ZXFuYXJyYXkqfQp3aXRoICQwPGM8MSQgYSB0aHJlc2hvbGQgdGhhdCBlaXRoZXIgaXMgZml4ZWQgKGUuZy4gJGM9MS8yJCksIGRlcGVuZHMgb24gcHJpb3IgcHJvYmFiaWxpdGllcywgb3IgaXMgZW1waXJpY2FsbHkgZGV0ZXJtaW5lZCBieSBvcHRpbWlzaW5nIGUuZy4gdGhlIEFyZWEgVW5kZXIgdGhlIFJPQyBDdXJ2ZSAoQVVDKSBvciBieSBmaW5kaW5nIGEgZ29vZCBjb21wcm9taXNlIGJldHdlZW4gc2Vuc2l0aXZpdHkgYW5kIHNwZWNpZmljaXR5LgoKTm90ZSB0aGF0IGxvZ2lzdGljIHJlZ3Jlc3Npb24gZGlyZWN0bHkgbW9kZWxzIHRoZSAqKlBvc3RlcmlvciBwcm9iYWJpbGl0eSoqIHRoYXQgYW4gb2JzZXJ2YXRpb24gYmVsb25ncyB0byBjbGFzcyAkWT0xJCwgZ2l2ZW4gdGhlIHByZWRpY3RvciAkXG1hdGhiZnt4fSQuCgojIyBNb2RlbCBldmFsdWF0aW9uCgpDb21tb24gbW9kZWwgZXZhbHVhdGlvbiBjcml0ZXJpYSBmb3IgYmluYXJ5IHByZWRpY3Rpb24gbW9kZWxzIGFyZToKCi0gc2Vuc2l0aXZpdHkgPSB0cnVlIHBvc2l0aXZlIHJhdGUgKFRQUikKCi0gc3BlY2lmaWNpdHkgPSB0cnVlIG5lZ2F0aXZlIHJhdGUgKFROUikKCi0gbWlzY2xhc3NpZmljYXRpb24gZXJyb3IKCi0gYXJlYSB1bmRlciB0aGUgUk9DIGN1cnZlIChBVUMpCgpUaGVzZSBjcml0ZXJpYSBjYW4gYWdhaW4gYmUgZXN0aW1hdGVkIHZpYSBjcm9zcyB2YWxpZGF0aW9uIG9yIHZpYSBzcGxpdHRpbmcgb2YgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdC92YWxpZGF0aW9uIGRhdGEuCgojIyMgU2Vuc2l0aXZpdHkgb2YgYSBtb2RlbCAkXHBpJCB3aXRoIHRocmVzaG9sZCAkYyQKClNlbnNpdGl2aXR5IGlzIHRoZSBwcm9iYWJpbGl0eSB0byBjb3JyZWN0bHkgcHJlZGljdCBhIHBvc2l0aXZlIG91dGNvbWU6ClxbClx0ZXh0e3NlbnN9KFxwaSxjKT1cdGV4dHtQfV97WF4qfVxsZWZ0W1xoYXRccGkoXG1hdGhiZntYfV4qKT5jIFxtaWQgWV4qPTEgXG1pZCB7XGNhbHtUfX1ccmlnaHRdLgpcXQoKSXQgaXMgYWxzbyBrbm93biBhcyB0aGUgdHJ1ZSBwb3NpdGl2ZSByYXRlIChUUFIpLgoKIyMjIFNwZWNpZmljaXR5IG9mIGEgbW9kZWwgJFxwaSQgd2l0aCB0aHJlc2hvbGQgJGMkCgpTcGVjaWZpY2l0eSBpcyB0aGUgcHJvYmFiaWxpdHkgdG8gY29ycmVjdGx5IHByZWRpY3QgYSBuZWdhdGl2ZSBvdXRjb21lOgpcWwpcdGV4dHtzcGVjfShccGksYyk9XHRleHR7UH1fe1heKn1cbGVmdFtcaGF0XHBpKFxtYXRoYmZ7WH1eKilcbGVxIGMgXG1pZCBZXio9MCBcbWlkIHtcY2Fse1R9fVxyaWdodF0uClxdCgpJdCBpcyBhbHNvIGtub3duIGFzIHRoZSB0cnVlIG5lZ2F0aXZlIHJhdGUgKFROUikuCgotLS0KCiMjIyBNaXNjbGFzc2lmaWNhdGlvbiBlcnJvciBvZiBhIG1vZGVsICRccGkkIHdpdGggdGhyZXNob2xkICRjJAoKVGhlIG1pc2NsYXNzaWZpY2F0aW9uIGVycm9yIGlzIHRoZSBwcm9iYWJpbGl0eSB0byBpbmNvcnJlY3RseSBwcmVkaWN0IGFuIG91dGNvbWU6ClxiZWdpbntlcW5hcnJheSp9Clx0ZXh0e21jZX0oXHBpLGMpICY9Jlx0ZXh0e1B9X3tYXiosWV4qfVxsZWZ0W1xoYXRccGkoXG1hdGhiZntYfSlcbGVxIGMgXHRleHR7IGFuZCB9IFleKj0xIFxtaWQge1xjYWx7VH19XHJpZ2h0XSBcXAomICAmICsgXHRleHR7UH1fe1heKixZXip9XGxlZnRbXGhhdFxwaShcbWF0aGJme1h9KT4gYyBcdGV4dHsgYW5kIH0gWV4qPTAgXG1pZCB7XGNhbHtUfX1ccmlnaHRdLgpcZW5ke2VxbmFycmF5Kn0KCk5vdGUgdGhhdCBpbiB0aGUgZGVmaW5pdGlvbnMgb2Ygc2Vuc2l0aXZpdHksIHNwZWNpZmljaXR5IGFuZCB0aGUgbWlzY2xhc3NpZmljYXRpb24gZXJyb3IsIHRoZSBwcm9iYWJpbGl0aWVzIHJlZmVyIHRvIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlICAkKFxtYXRoYmZ7WH1eKixZXiopJCwgd2hpY2ggaXMgaW5kZXBlbmRlbnQgb2YgdGhlIHRyYWluaW5nIGRhdGEsIGNvbmRpdGlvbmFsIG9uIHRoZSB0cmFpbmluZyBkYXRhLiBUaGlzIGlzIGluIGxpbmUgd2l0aCB0aGUgdGVzdCBvciBnZW5lcmFsaXNhdGlvbiBlcnJvci4gVGhlIG1pc2NsYXNzaWZpY2F0aW9uIGVycm9yIGlzIGFjdHVhbGx5IHRoZSB0ZXN0IGVycm9yIHdoZW4gYSAwLzEgbG9zcyBmdW5jdGlvbiBpcyB1c2VkLiBKdXN0IGFzIGJlZm9yZSwgdGhlIHNlbnNpdGl2aXR5LCBzcGVjaWZpY2l0eSBhbmQgdGhlIG1pc2NsYXNzaWZpY2F0aW9uIGVycm9yIGNhbiBhbHNvIGJlIGF2ZXJhZ2VkIG92ZXIgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdHJhaW5pbmcgZGF0YSBzZXQsIHdoaWNoIGlzIGluIGxpbmUgd2l0aCB0aGUgZXhwZWN0ZWQgdGVzdCBlcnJvciB3aGljaCBoYXMgYmVlbiBkaXNjdXNzZWQgZWFybGllci4KCi0tLQoKIyMjIFJPQyBjdXJ2ZSBvZiBhIG1vZGVsICRccGkkCgpUaGUgUmVjZWl2ZXIgT3BlcmF0aW5nIENoYXJhY3RlcmlzdGljIChST0MpIGN1cnZlIGZvciBtb2RlbCAkXHBpJCBpcyBnaXZlbiBieSB0aGUgZnVuY3Rpb24KClxbClx0ZXh0e1JPQ306IFswLDFdIFxyaWdodGFycm93IFswLDFdXHRpbWVzIFswLDFdOiBjIFxtYXBzdG8gKDEtXHRleHR7c3BlY30oXHBpLGMpLCBcdGV4dHtzZW5zfShccGksYykpLgpcXQoKRm9yIHdoZW4gJGMkIG1vdmVzIGZyb20gMSB0byAwLCB0aGUgUk9DIGZ1bmN0aW9uIGRlZmluZXMgYSBjdXJ2ZSBpbiB0aGUgcGxhbmUgJFswLDFdXHRpbWVzIFswLDFdJCwgbW92aW5nIGZyb20gJCgwLDApJCBmb3IgJGM9MSQgdG8gJCgxLDEpJCBmb3IgJGM9MCQuCgpUaGUgaG9yaXpvbnRhbCBheGlzIG9mIHRoZSBST0MgY3VydmUgc2hvd3MgMS1zcGVjaWZpY2l0eS4gVGhpcyBpcyBhbHNvIGtub3duIGFzIHRoZSBGYWxzZSBQb3NpdGl2ZSBSYXRlIChGUFIpLgoKLS0tCgojIyMgQXJlYSB1bmRlciB0aGUgY3VydmUgKEFVQykgb2YgYSBtb2RlbCAkXHBpJAoKVGhlIGFyZWEgdW5kZXIgdGhlIGN1cnZlIChBVUMpIGZvciBtb2RlbCAkXHBpJCBpcyBhcmVhIHVuZGVyIHRoZSBST0MgY3VydmUgYW5kIGlzIGdpdmVuIGJ5ClxbClxpbnRfMF4xIFx0ZXh0e1JPQ30oYykgZGMuClxdCgpTb21lIG5vdGVzIGFib3V0IHRoZSBBVUM6CgotIEFVQz0wLjUgcmVzdWx0cyB3aGVuIHRoZSBST0MgY3VydmUgaXMgdGhlIGRpYWdvbmFsLiBUaGlzIGNvcnJlc3BvbmRzIHRvIGZsaXBwaW5nIGEgY29pbiwgaS5lLiBhIGNvbXBsZXRlIHJhbmRvbSBwcmVkaWN0aW9uLgoKLSBBVUM9MSByZXN1bHRzIGZyb20gdGhlIHBlcmZlY3QgUk9DIGN1cnZlLCB3aGljaCBpcyB0aGUgUk9DIGN1cnZlIHRocm91Z2ggdGhlIHBvaW50cyAkKDAsMCkkLCAkKDAsMSkkIGFuZCAkKDEsMSkkLiBUaGlzIFJPQyBjdXJ2ZSBpbmNsdWRlcyBhIHRocmVzaG9sZCAkYyQgc3VjaCB0aGF0IHNlbnNpdGl2aXR5IGFuZCBzcGVjaWZpY2l0eSBhcmUgZXF1YWwgdG8gb25lLgoKIyMgQnJlYXN0IGNhbmNlciBleGFtcGxlCgojIyMgRGF0YQoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZ2xtbmV0KQoKI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJnZW5lZnUiKQojQmlvY01hbmFnZXI6Omluc3RhbGwoImJyZWFzdENhbmNlck1BSU5aIikKCmxpYnJhcnkoZ2VuZWZ1KQpsaWJyYXJ5KGJyZWFzdENhbmNlck1BSU5aKQpkYXRhKG1haW56KQoKWCA8LSB0KGV4cHJzKG1haW56KSkgIyBnZW5lIGV4cHJlc3Npb25zCm4gPC0gbnJvdyhYKQpIIDwtIGRpYWcobiktMS9uKm1hdHJpeCgxLG5jb2w9bixucm93PW4pClggPC0gSCUqJVgKWSA8LSBpZmVsc2UocERhdGEobWFpbnopJGdyYWRlPT0zLDEsMCkKdGFibGUoWSkKYGBgCgotLS0KCkZyb20gdGhlIHRhYmxlIG9mIHRoZSBvdXRjb21lcyBpbiBZIHdlIHJlYWQgdGhhdAoKLSBgciBzdW0oWT09MSlgIHR1bW9ycyB3ZXJlIGdyYWRlZCBhcyBzdGFnZSAzIGFuZAotIGByIHN1bShZPT0wKWAgdHVtb3JzIHdlcmUgZ3JhZGVkIGFzIHN0YWdlIDEgb3IgMi4KCkluIHRoaXMgdGhlIHN0YWdlIDMgdHVtb3JzIGFyZSByZWZlcnJlZCB0byBhcyBjYXNlcyBvciBwb3N0aXZlcyBhbmQgdGhlIHN0YWdlIDEgYW5kIDIgdHVtb3JzIGFzIGNvbnRyb2xzIG9yIG5lZ2F0aXZlcy4KCi0tLQoKIyMjIFRyYWluaW5nIGFuZCB0ZXN0IGRhdGFzZXQKClRoZSB1c2Ugb2YgdGhlIGxhc3NvIGxvZ2lzdGljIHJlZ3Jlc3Npb24gZm9yIHRoZSBwcmVkaWN0aW9uIG9mIHN0YWdlIDMgYnJlYXN0IGNhbmNlciBpcyBpbGx1c3RyYXRlZCBoZXJlIGJ5CgotIHJhbmRvbWx5IHNwbGl0dGluZyB0aGUgZGF0YXNldCBpbnRvIGEgdHJhaW5pbmcgZGF0YXNldCAoJDgwXCUkIG9mIGRhdGEgPSAxNjAgdHVtb3JzKSBhbmQgYSB0ZXN0IGRhdGFzZXQgKDQwIHR1bW9ycykKCi0gdXNpbmcgdGhlIHRyYWluaW5nIGRhdGEgdG8gc2VsZWN0IGEgZ29vZCAkXGxhbWJkYSQgdmFsdWUgaW4gdGhlIGxhc3NvIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgKHRocm91Z2ggMTAtZm9sZCBDVikKCi0gZXZhbHVhdGluZyB0aGUgZmluYWwgbW9kZWwgYnkgbWVhbnMgb2YgdGhlIHRlc3QgZGF0YXNldCAoUk9DIEN1cnZlLCBBVUMpLgoKCmBgYHtyfQoKIyMgVXNlZCB0byBwcm92aWRlIHNhbWUgcmVzdWx0cyBhcyBpbiBwcmV2aW91cyBSIHZlcnNpb24KUk5Ha2luZChzYW1wbGUua2luZCA9ICJSb3VuZGluZyIpCnNldC5zZWVkKDY5NzczMjYpCiMjIyMKCm4gPC0gbnJvdyhYKQpuVHJhaW4gPC0gcm91bmQoMC44Km4pCm5UcmFpbgoKaW5kVHJhaW4gPC0gc2FtcGxlKDE6bixuVHJhaW4pClhUcmFpbiA8LSBYW2luZFRyYWluLF0KWVRyYWluIDwtIFlbaW5kVHJhaW5dClhUZXN0IDwtIFhbLWluZFRyYWluLF0KWVRlc3QgPC0gWVstaW5kVHJhaW5dCnRhYmxlKFlUZXN0KQpgYGAKCk5vdGUgdGhhdCB0aGUgcmFuZG9tbHkgc2VsZWN0ZWQgdGVzdCBkYXRhIGhhcyBgciBtZWFuKFlUZXN0PT0xKSoxMDBgJSBjYXNlcyBvZiBzdGFnZSAzIHR1bW9ycy4KVGhpcyBpcyBhIGJpdCBoaWdoZXIgdGhhbiB0aGUgYHIgbWVhbihZPT0xKSoxMDBgJSAgaW4gdGhlIGNvbXBsZXRlIGRhdGEuCgpPbmUgY291bGQgYWxzbyBwZXJmb3JtIHRoZSByYW5kb20gc3BsaXR0aW5nIGFtb25nIHRoZSBwb3NpdGl2ZXMgYW5kIHRoZSBuZWdhdGl2ZXMgc2VwYXJhdGVseSAoc3RyYXRpZmllZCBzcGxpdHRpbmcpLgoKIyMjIE1vZGVsIGZpdHRpbmcgYmFzZWQgb24gdHJhaW5pbmcgZGF0YQoKYGBge3J9Cm1MYXNzbyA8LSBnbG1uZXQoCiAgeCA9IFhUcmFpbiwKICB5ID0gWVRyYWluLAogIGFscGhhID0gMSwKICBmYW1pbHk9ImJpbm9taWFsIikgICMgbGFzc286IGFscGhhID0gMQoKcGxvdChtTGFzc28sIHh2YXIgPSAibGFtYmRhIiwgeGxpbSA9IGMoLTYsLTEuNSkpCmBgYAoKLS0tCgpgYGB7cn0KbUN2TGFzc28gPC0gY3YuZ2xtbmV0KAogIHggPSBYVHJhaW4sCiAgeSA9IFlUcmFpbiwKICBhbHBoYSA9IDEsCiAgdHlwZS5tZWFzdXJlID0gImNsYXNzIiwKCWZhbWlseSA9ICJiaW5vbWlhbCIpICAjIGxhc3NvIGFscGhhID0gMQoKcGxvdChtQ3ZMYXNzbykKbUN2TGFzc28KYGBgCgpUaGUgdG90YWwgbWlzY2xhc3NpZmljYXRpb24gZXJyb3IgaXMgdXNlZCBoZXJlIHRvIHNlbGVjdCBhIGdvb2QgdmFsdWUgZm9yICRcbGFtYmRhJC4KCmBgYHtyfQojIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJwbG90Uk9DIikKbGlicmFyeShwbG90Uk9DKQoKZGZMYXNzb09wdCA8LSBkYXRhLmZyYW1lKAogIHBpID0gcHJlZGljdChtQ3ZMYXNzbywKICAgIG5ld3ggPSBYVGVzdCwKICAgIHMgPSBtQ3ZMYXNzbyRsYW1iZGEubWluLAogICAgdHlwZSA9ICJyZXNwb25zZSIpICU+JSBjKC4pLAogIGtub3duLnRydXRoID0gWVRlc3QpCgpyb2MgPC0KICBkZkxhc3NvT3B0ICAlPiUKICBnZ3Bsb3QoYWVzKGQgPSBrbm93bi50cnV0aCwgbSA9IHBpKSkgKwogIGdlb21fcm9jKG4uY3V0cyA9IDApICsKICB4bGFiKCIxLXNwZWNpZmljaXR5IChGUFIpIikgKwogIHlsYWIoInNlbnNpdGl2aXR5IChUUFIpIikKCnJvYwoKY2FsY19hdWMocm9jKQpgYGAKCi0gVGhlIFJPQyBjdXJ2ZSBpcyBzaG93biBmb3IgdGhlIG1vZGVsIGJhc2VkIG9uICRcbGFtYmRhJCB3aXRoIHRoZSBzbWFsbGVzdCBtaXNjbGFzc2lmaWNhdGlvbiBlcnJvci4gVGhlIG1vZGVsIGhhcyBhbiBBVUMgb2YgYHIgY2FsY19hdWMocm9jKSAlPiUgcHVsbChBVUMpICU+JSByb3VuZCgyKWAuCgotIEJhc2VkIG9uIHRoaXMgUk9DIGN1cnZlIGFuIGFwcHJvcHJpYXRlIHRocmVzaG9sZCAkYyQgY2FuIGJlIGNob3Nlbi4gRm9yIGV4YW1wbGUsIGZyb20gdGhlIFJPQyBjdXJ2ZSB3ZSBzZWUgdGhhdCBpdCBpcyBwb3NzaWJsZSB0byBhdHRhaW4gYSBzcGVjaWZpY2l0eSBhbmQgYSBzZW5zaXRpdml0eSBvZiA3NVwlLgoKLSBUaGUgc2Vuc2l0aXZpdGllcyBhbmQgc3BlY2lmaWNpdGllcyBpbiB0aGUgUk9DIGN1cnZlIGFyZSB1bmJpYXNlZCAoaW5kZXBlbmRlbnQgdGVzdCBkYXRhc2V0KSBmb3IgdGhlIHByZWRpY3Rpb24gbW9kZWwgYnVpbGQgZnJvbSB0aGUgdHJhaW5pbmcgZGF0YS4gVGhlIGVzdGltYXRlcyBvZiBzZW5zaXRpdml0eSBhbmQgc3BlY2lmaWNpdHksIGhvd2V2ZXIsIGFyZSBiYXNlZCBvbiBvbmx5IDQwIG9ic2VydmF0aW9ucy4KCi0tLQoKYGBge3J9Cm1MYW1iZGFPcHQgPC0gZ2xtbmV0KHggPSBYVHJhaW4sCiAgeSA9IFlUcmFpbiwKICBhbHBoYSA9IDEsCiAgbGFtYmRhID0gbUN2TGFzc28kbGFtYmRhLm1pbiwKICBmYW1pbHk9ImJpbm9taWFsIikKCnFwbG90KAogIHN1bW1hcnkoY29lZihtTGFtYmRhT3B0KSlbLTEsMV0sCiAgc3VtbWFyeShjb2VmKG1MYW1iZGFPcHQpKVstMSwzXSkgKwogIHhsYWIoImdlbmUgSUQiKSArCiAgeWxhYigiYmV0YS1oYXQiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgY29sb3IgPSAicmVkIikKYGBgCgotIFRoZSBtb2RlbCB3aXRoIHRoZSBvcHRpbWFsICRcbGFtYmRhJCBoYXMgb25seSBgciBtTGFtYmRhT3B0ICU+JSBjb2VmICU+JSBzdW1tYXJ5ICU+JSBucm93YCBub24temVybyBwYXJhbWV0ZXIgZXN0aW1hdGVzLgotIFRodXMgb25seSBgciBtTGFtYmRhT3B0ICU+JSBjb2VmICU+JSBzdW1tYXJ5ICU+JSBucm93YCBnZW5lcyBhcmUgaW52b2x2ZWQgaW4gdGhlIHByZWRpY3Rpb24gbW9kZWwuCi0gVGhlc2UgYHIgbUxhbWJkYU9wdCAlPiUgY29lZiAlPiUgc3VtbWFyeSAlPiUgbnJvd2AgcGFyYW1ldGVyIGVzdGltYXRlcyBhcmUgcGxvdHRpbmcgaW4gdGhlIGdyYXBoLgpBIGxpc3Rpbmcgb2YgdGhlIG1vZGVsIG91dHB1dCB3b3VsZCBzaG93IHRoZSBuYW1lcyBvZiB0aGUgZ2VuZXMuCgotLS0KCmBgYHtyfQoKZGZMYXNzbzFzZSA8LSBkYXRhLmZyYW1lKAogIHBpID0gcHJlZGljdChtQ3ZMYXNzbywKICAgIG5ld3ggPSBYVGVzdCwKICAgIHMgPSBtQ3ZMYXNzbyRsYW1iZGEuMXNlLAogICAgdHlwZSA9ICJyZXNwb25zZSIpICU+JSBjKC4pLAogIGtub3duLnRydXRoID0gWVRlc3QpCgpyb2MgPC0KICByYmluZCgKICAgIGRmTGFzc29PcHQgJT4lCiAgICAgIG11dGF0ZShtZXRob2QgPSAibWluIiksCiAgICBkZkxhc3NvMXNlICU+JQogICAgICBtdXRhdGUobWV0aG9kID0gIjFzZSIpCiAgKSAlPiUKICBnZ3Bsb3QoYWVzKGQgPSBrbm93bi50cnV0aCwgbSA9IHBpLCBjb2xvciA9IG1ldGhvZCkpICsKICBnZW9tX3JvYyhuLmN1dHMgPSAwKSArCiAgeGxhYigiMS1zcGVjaWZpY2l0eSAoRlBSKSIpICsKICB5bGFiKCJzZW5zaXRpdml0eSAoVFBSKSIpCgpyb2MKCmNhbGNfYXVjKHJvYykKYGBgCgotIFdoZW4gdXNpbmcgdGhlICRcbGFtYmRhJCBvZiB0aGUgb3B0aW1hbCBtb2RlbCB1cCB0byAxIHN0YW5kYXJkIGRldmlhdGlvbiwgYSBkaWFnb25hbCBST0MgY3VydmUgaXMgb2J0YWluZWQgYW5kIGhlbmNlIEFVQyBpcyAkMC41JC4KCi0gVGhpcyBwcmVkaWN0aW9uIG1vZGVsIGlzIHRodXMgZXF1aXZhbGVudCB0byBmbGlwcGluZyBhIGNvaW4gZm9yIG1ha2luZyB0aGUgcHJlZGljdGlvbi4KCi0gVGhlIHJlYXNvbiBpcyB0aGF0IHdpdGggdGhpcyBjaG9pY2Ugb2YgJFxsYW1iZGEkIChzdHJvbmcgcGVuYWxpc2F0aW9uKSBhbG1vc3QgYWxsIHByZWRpY3RvcnMgYXJlIHJlbW92ZWQgZnJvbSB0aGUgbW9kZWwuCgotIFRoZXJlZm9yZSwgZG8gbmV2ZXIgYmxpbmRseSBjaG9vc2UgZm9yIHRoZSBgYG9wdGltYWwnJyAkXGxhbWJkYSQgYXMgZGVmaW5lZCBoZXJlLCBidXQgYXNzZXNzIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgbW9kZWwgZmlyc3QuCgpgYGB7cn0KbUxhbWJkYTFzZSA8LSBnbG1uZXQoeCA9IFhUcmFpbiwKICB5ID0gWVRyYWluLAogIGFscGhhID0gMSwKICBsYW1iZGEgPSBtQ3ZMYXNzbyRsYW1iZGEuMXNlLAogIGZhbWlseT0iYmlub21pYWwiKQoKbUxhbWJkYTFzZSAlPiUKICBjb2VmICU+JQogIHN1bW1hcnkKYGBgCgotLS0KCiMjIFRoZSBFbGFzdGljIE5ldAoKVGhlIGxhc3NvIGFuZCByaWRnZSByZWdyZXNzaW9uIGhhdmUgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHByb3BlcnRpZXMuCgotIExhc3NvCgogICAtIHBvc2l0aXZlOiBzcGFyc2Ugc29sdXRpb24KCiAgIC0gbmVnYXRpdmU6IGF0IG1vc3QgJFxtaW4obixwKSQgcHJlZGljdG9ycyBjYW4gYmUgc2VsZWN0ZWQKCiAgIC0gbmVnYXRpdmU6IHRlbmQgdG8gc2VsZWN0IG9uZSBwcmVkaWN0b3IgYW1vbmcgYSBncm91cCBvZiBoaWdobHkgY29ycmVsYXRlZCBwcmVkaWN0b3JzCgoKLSBSaWRnZQoKICAgIC0gbmVnYXRpdmU6IG5vIHNwYXJzZSBzb2x1dGlvbgogICAgLSBwb3NpdGl2ZTogbW9yZSB0aGFuICRcbWluKG4scCkkIHByZWRpY3RvcnMgY2FuIGJlIHNlbGVjdGVkCgpBIGNvbXByb21pc2UgYmV0d2VlbiBsYXNzbyBhbmQgcmlkZ2U6IHRoZSAqKmVsYXN0aWMgbmV0Kio6ClxbCiAgXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19ID0gXHRleHR7QXJnTWF4fV9cYmV0YSBsKFxib2xkc3ltYm9se1xiZXRhfSkgLVxnYW1tYV8xIFxWZXJ0IFxib2xkc3ltYm9sXGJldGFcVmVydF8xIC1cZ2FtbWFfMiBcVmVydCBcYm9sZHN5bWJvbFxiZXRhXFZlcnRfMl4yLgpcXQoKVGhlIGVsYXN0aWMgZ2l2ZXMgYSBzcGFyc2Ugc29sdXRpb24gd2l0aCBwb3RlbnRpYWxseSBtb3JlIHRoYW4gJFxtaW4obixwKSQgcHJlZGljdG9ycy4KCi0tLQoKVGhlIGBnbG1uZXRgIFIgZnVuY3Rpb24gdXNlcyB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcmlzYXRpb24sClxbCiAgXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19ID0gXHRleHR7QXJnTWF4fV9cYmV0YSBsKFxib2xkc3ltYm9se1xiZXRhfSkgLVxsYW1iZGFcYWxwaGEgXFZlcnQgXGJvbGRzeW1ib2xcYmV0YVxWZXJ0XzEgLVxsYW1iZGEoMS1cYWxwaGEpIFxWZXJ0IFxib2xkc3ltYm9sXGJldGFcVmVydF8yXjIuClxdCgotICRcYWxwaGEkIHBhcmFtZXRlciBnaXZlcyB3ZWlnaHQgdG8gJExfMSQgcGVuYWx0eSB0ZXJtIChoZW5jZSAkXGFscGhhPTEkIGdpdmVzIHRoZSBsYXNzbywgYW5kICRcYWxwaGE9MCQgZ2l2ZXMgcmlkZ2UpLgoKLSBhICRcbGFtYmRhJCBwYXJhbWV0ZXIgdG8gZ2l2ZSB3ZWlnaHQgdG8gdGhlIHBlbmFsaXNhdGlvbgoKLSBOb3RlIHRoYXQgdGhlIGNvbWJpbmF0aW9uIG9mICRcbGFtYmRhJCBhbmQgJFxhbHBoYSQgZ2l2ZXMgdGhlIHNhbWUgZmxleGliaWxpdHkgYXMgdGhlIGNvbWJpbmF0aW9uIG9mIHRoZSBwYXJhbWV0ZXJzICRcbGFtYmRhXzEkIGFuZCAkXGxhbWJkYV8yJC4KCi0tLQoKIyMjIEJyZWFzdCBjYW5jZXIgZXhhbXBsZQoKYGBge3J9Cm1FbGFzdGljIDwtIGdsbW5ldCgKICB4ID0gWFRyYWluLAogIHkgPSBZVHJhaW4sCiAgYWxwaGEgPSAwLjUsCiAgZmFtaWx5PSJiaW5vbWlhbCIpICAjIGVsYXN0aWMgbmV0CgpwbG90KG1FbGFzdGljLCB4dmFyID0gImxhbWJkYSIseGxpbT1jKC01LjUsLTEpKQpgYGAKCmBgYHtyfQptQ3ZFbGFzdGljIDwtIGN2LmdsbW5ldCh4ID0gWFRyYWluLAogIHkgPSBZVHJhaW4sCiAgYWxwaGEgPSAwLjUsCiAgZmFtaWx5ID0gImJpbm9taWFsIiwKCXR5cGUubWVhc3VyZSA9ICJjbGFzcyIpICAjIGVsYXN0aWMgbmV0CgpwbG90KG1DdkVsYXN0aWMpCm1DdkVsYXN0aWMKYGBgCgpgYGB7cn0KZGZFbGFzdCA8LSBkYXRhLmZyYW1lKAogIHBpID0gcHJlZGljdChtRWxhc3RpYywKICAgIG5ld3ggPSBYVGVzdCwKICAgIHMgPSBtQ3ZFbGFzdGljJGxhbWJkYS5taW4sCiAgICB0eXBlID0gInJlc3BvbnNlIikgJT4lIGMoLiksCiAga25vd24udHJ1dGggPSBZVGVzdCkKCnJvYyA8LSByYmluZCgKICBkZkxhc3NvT3B0ICU+JSBtdXRhdGUobWV0aG9kID0gImxhc3NvIiksCiAgZGZFbGFzdCAlPiUgbXV0YXRlKG1ldGhvZCA9ICJlbGFzdC4gbmV0IikpICU+JQogIGdncGxvdChhZXMoZCA9IGtub3duLnRydXRoLCBtID0gcGksIGNvbG9yID0gbWV0aG9kKSkgKwogIGdlb21fcm9jKG4uY3V0cyA9IDApICsKICB4bGFiKCIxLXNwZWNpZmljaXR5IChGUFIpIikgKwogIHlsYWIoInNlbnNpdGl2aXR5IChUUFIpIikKCnJvYwoKY2FsY19hdWMocm9jKQpgYGAKCi0gTW9yZSBwYXJhbWV0ZXJzIGFyZSB1c2VkIHRoYW4gZm9yIHRoZSBsYXNzbywgYnV0IHRoZSBwZXJmb3JtYW5jZSBkb2VzIG5vdCBpbXByb3ZlLgoKYGBge3J9Cm1FbGFzdGljT3B0IDwtIGdsbW5ldCh4ID0gWFRyYWluLAogIHkgPSBZVHJhaW4sCiAgYWxwaGEgPSAwLjUsCiAgbGFtYmRhID0gbUN2RWxhc3RpYyRsYW1iZGEubWluLAogIGZhbWlseT0iYmlub21pYWwiKQoKcXBsb3QoCiAgc3VtbWFyeShjb2VmKG1FbGFzdGljT3B0KSlbLTEsMV0sCiAgc3VtbWFyeShjb2VmKG1FbGFzdGljT3B0KSlbLTEsM10pICsKICB4bGFiKCJnZW5lIElEIikgKwogIHlsYWIoImJldGEtaGF0IikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGNvbG9yID0gInJlZCIpCmBgYAoKIyBBY2tub3dsZWRnZW1lbnQgey19CgotIE9saXZpZXIgVGhhcyBmb3Igc2hhcmluZyBoaXMgbWF0ZXJpYWxzIG9mIEFuYWx5c2lzIG9mIEhpZ2ggRGltZW5zaW9uYWwgRGF0YSAyMDE5LTIwMjAsIHdoaWNoIEkgdXNlZCBhcyB0aGUgc3RhcnRpbmcgcG9pbnQgZm9yIHRoaXMgY2hhcHRlci4KCmBgYHtyLCBjaGlsZD0iX3Nlc3Npb24taW5mby5SbWQifQpgYGAK