library(tidyverse)

Regression and supervised classification address the problem of predicting an output \(y\in\mathcal Y\) by inputs \(x\in\mathbb R^p\). We have to find a machine: \[m:\mathbb R^p\to\mathcal Y\] with the data \((X_1,Y_1),\dots,(X_n,Y_n)\).

Data are often collected in a data frame df of the form

\(Y\) \(X_1\) \(X_2\) \(\dots\) \(X_p\)
\(y_1\) \(x_{1,1}\) \(x_{1,2}\) \(\dots\) \(x_{1,p}\)
\(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\)
\(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\)
\(y_n\) \(x_{n,1}\) \(x_{n,2}\) \(\dots\) \(x_{n,p}\)

We adjust a model on R always in the same way: we have to indicate

  • the method (or the algorithm)
  • the variable to explain
  • the explanatory variables
  • the options of the method

For instance, command

method(Y~X1+X3,data=df,…)

computes the model method to explain \(Y\) by \(X_1\) and \(X_3\) with the data in df (dots are for eventual options of the method). Here are some examples of methods:

R function algorithm Package Problem
lm linear model Reg
glm logistic model Class
lda linear discriminant analysis MASS Class
svm Support Vector Machine e1071 Class
knn.reg nearest neighbor FNN Reg
knn nearest neighbor class Class
rpart tree rpart Reg and Class
glmnet ridge and lasso glmnet Reg and Class
gbm boosting gbm Reg and Class
randomForest random Forest randomForest Reg and Class

Remark: for glmnet, it is not possible to use a formula Y~.. We have to use a matrix for the \(X\) and a vector for \(Y\). Function model.matrix is very interesting to compute the matrix of \(X\).

Since a lot of algorithms are available to address problems of regression and supervised classication, it is important to define performance criteria. Criteria are generally unknown and need to be estimated on a validation set (or by cross validation). We generaly have to used the predict function to make the estimation. This function is a generic function: it means that we can use predict for tree, random forest, logistic regression… However prediction algorithms are not the same for all models. To obtain the help on predict for

  • logistic model, you have to enter help(predict.glm)
  • penalized logistic models you have to enter help(predict.glmnet)
  • trees, you have to enter help(predict.rpart)
  • random forest, you have to enter help(predict.randomForest)

In the sequel, we assume that \(\mathcal Y=\mathbb R\) and we consider the regression model \[Y=m(X)+\varepsilon.\] The performance of a given estimate \(\widehat{m}\) of \(m\) will be measured by its mean square error: \[E[(Y-\widehat m(X))^2].\]

Exercise 1 (linear model, lm and predict)

We consider the linear regression model \[Y=\beta_0+\beta_1X_1+\dots+\beta_pX_p+\varepsilon\] where \(X_1,\dots,X_p\) are independent variables with distribution \(\mathcal N(0,1)\) and \(\varepsilon\) has distribution \(\mathcal N(0,0.5^2)\) (\(X=(X_1,\dots,X_p)\) and \(\varepsilon\) are independent).

We consider \(p=5\), \(\beta_0=0\) and \(\beta_1=\dots=\beta_5=1\).

  1. Generate \(n=1000\) observations \((x_1,y_1),\dots,(x_n,y_n)\) according to this model (use rnorm and runif). Put these observations in a data.frame df of dimension \(1000\times 6\).
n <- 1000
p <- 5
set.seed(1234)
X.mat <- matrix(rnorm(n*p),ncol=p)
eps <- rnorm(n,mean = 0,sd=0.5)
df <- data.frame(X.mat,eps)
df <- df %>% mutate(Y=X1+X2+X3+X4+X5+eps) %>% select(-eps)
  1. Fit a linear model (lm function) on df and print the estimator of \(\beta_0,\dots,\beta_5\).

  2. Generate a test dataset (df.test) \((x_{n+1},y_{n+1}),\dots,(x_{n+m},y_{n+m})\) with \(m=500\) according to the same model.

  3. For each individuals in df.test dataset, calculate the predictions \(\widehat y_i\) by the model fitted in question 2 (use predict function with the option newdata).

  4. Create a new dataframe which contains both predictions \(\widehat y_i\) and observations \(y_i\) of the test data set.

  5. With summarize verb, calculate the estimated mean square error of the linear model: \[\frac{1}{m}\sum_{i\in test}(\widehat y_i-y_i)^2.\]

Exercise 2 (variable selection)

We still consider the model \[Y=\beta_0+\beta_1X_1+\dots+\beta_pX_p+\varepsilon\] where \(X_1,\dots,X_p\) are independent variables with distribution \(\mathcal N(0,1)\) and \(\varepsilon\) has distribution \(\mathcal N(0,0.5^2)\) (\(X=(X_1,\dots,X_p)\) and \(\varepsilon\) are independent).

But here, we assume that \(p=105\) and that

  • \(\beta_0=0\), \(\beta_1=\dots=\beta_5=1\)
  • \(\beta_6=\dots=\beta_{105}=0\). So here, only variables \(X_1,\dots,X_5\) are relevant to explain \(Y\).
  1. Generate \(n=1000\) observations \((x_1,y_1),\dots,(x_n,y_n)\) according to this model. Put these observations in a data.frame df with dimension \(1000\times 106\).

  2. Fit a linear model (lm function) with df and print the estimator of \(\beta_0,\dots,\beta_{105}\).

  3. We propose to make a variable selection procedure with a backward selection approach using BIC criterion (this approach will be described in details in other lectures). You just have to use the step function with the direction=“backward” and k=log(1000) options. Write the selected model. We call it mod.step.

  4. Generate a test dataset (df.test) \((x_{n+1},y_{n+1}),\dots,(x_{n+m},y_{n+m})\) with \(m=500\) according to the same model. Compute the estimated mean square error \[\frac{1}{m}\sum_{i\in test}(\widehat y_i-y_i)^2\] for the two models.

We observe that the selected model has smaller MSE than the full model. We conclude that this model is better for this criterion. We will see that it is due to the variance of least square estimates: when there are noisy variables in a model, variance of least square estimate increases. These estimates are thus less accurate. To circumvent this problem, we have to select variables (as we do here with the step function) or to use regularized methods such as Lasso and Ridge (ISL lecture).

In the previous exercise we have estimated the MSE of a model by validation hold out. The dataset has been splitted into two parts:

  • a train set (size 1000) to fit the model
  • a test set (size 500) to estimate the mean square error.

To be efficient, this approach requires a large number of data. When this is not the case, we can use \(K\)-fold cross validation. Such approaches consists in repeating the hold out procedures on many blocks of the data. More precisely:

  • the dataset is first divided into \(K\) blocks \(B_1,\dots,B_K\)
  • For each block \(B_j\),

    • fit the model on the whole dataset without \(B_j\). We note \(m_j\) the obtained model.
    • predict individuals in \(B_j\) by \(m_j\)
  • We thus obtained a prediction \(\widehat y_i\) for each individual of the data set and we estimate the mean square error by \[\frac{1}{n}\sum_{i=1}^n(\widehat y_i-y_i)^2.\]

Exercise 3 (cross validation for ozone data set)

We consider the ozone.txt dataset available here. The problem is to explain the daily maximum one-hour-average ozone reading in column maxO3.

  1. Import the dataset with read.table

  2. Fit a linear model on the whole dataset.

  3. Split the data into \(10\) folds with the createFolds function of the caret package (observe that the output of this function is a list).

  4. Estimate the mean square error of the linear model by cross validation. (Use a loop).

  5. Now, we want to explain maxO3 by the other variables with a tree. Fit a tree on the whole dataset with the rpart function of the rpart package.

  6. Visualize the tree with

    • the rpart.plot function of the rpart.plot package
    • the visTree function of the visNetwork package
  7. Estimate the mean square error of the tree by 10-fold cross validation and make a comparison with the linear model.

Remark

In the ILS lecture, we will see that package caret allow to estimate MSE or (RMSE) directly

ctrl <- trainControl(method="cv",number=10)
train(maxO3~.,data=ozone,method="lm",trControl=ctrl)
LS0tCnRpdGxlOiAiUmVncmVzc2lvbiBtb2RlbHMgd2l0aCBSIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgoKUmVncmVzc2lvbiBhbmQgc3VwZXJ2aXNlZCBjbGFzc2lmaWNhdGlvbiBhZGRyZXNzIHRoZSBwcm9ibGVtIG9mIHByZWRpY3RpbmcgYW4gb3V0cHV0ICR5XGluXG1hdGhjYWwgWSQgYnkgaW5wdXRzICR4XGluXG1hdGhiYiBSXnAkLiBXZSBoYXZlIHRvIGZpbmQgYSBtYWNoaW5lOgokJG06XG1hdGhiYiBSXnBcdG9cbWF0aGNhbCBZJCQKd2l0aCB0aGUgZGF0YSAkKFhfMSxZXzEpLFxkb3RzLChYX24sWV9uKSQuCgpEYXRhIGFyZSBvZnRlbiBjb2xsZWN0ZWQgaW4gYSAqKmRhdGEgZnJhbWUqKiAqKmRmKiogb2YgdGhlIGZvcm0KCiRZJHwkWF8xJHwkWF8yJHwkXGRvdHMkfCRYX3AkCi0tLS18LS18LS0tLS18LS0tLS0tLXwtLS0tLS0tLS0tCiR5XzEkfCR4X3sxLDF9JHwkeF97MSwyfSR8JFxkb3RzJHwkeF97MSxwfSQKJFx2ZG90cyR8JFx2ZG90cyR8JFx2ZG90cyR8JFx2ZG90cyR8JFx2ZG90cyQKJFx2ZG90cyR8JFx2ZG90cyR8JFx2ZG90cyR8JFx2ZG90cyR8JFx2ZG90cyQKJHlfbiR8JHhfe24sMX0kfCR4X3tuLDJ9JHwkXGRvdHMkfCR4X3tuLHB9JAoKV2UgYWRqdXN0IGEgbW9kZWwgb24gKipSKiogYWx3YXlzIGluIHRoZSBzYW1lIHdheTogd2UgaGF2ZSB0byBpbmRpY2F0ZQoKKiB0aGUgbWV0aG9kIChvciB0aGUgYWxnb3JpdGhtKQoqIHRoZSB2YXJpYWJsZSB0byBleHBsYWluCiogdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcwoqIHRoZSBvcHRpb25zIG9mIHRoZSBtZXRob2QKCkZvciBpbnN0YW5jZSwgY29tbWFuZAoKPGNlbnRlcj5tZXRob2QoWX5YMStYMyxkYXRhPWRmLC4uLik8L2NlbnRlcj4KCgpjb21wdXRlcyB0aGUgbW9kZWwgKiptZXRob2QqKiB0byBleHBsYWluICRZJCBieSAkWF8xJCBhbmQgJFhfMyQgd2l0aCB0aGUgZGF0YSBpbiAqKmRmKiogKGRvdHMgYXJlIGZvciBldmVudHVhbCBvcHRpb25zIG9mIHRoZSBtZXRob2QpLiBIZXJlIGFyZSBzb21lIGV4YW1wbGVzIG9mIG1ldGhvZHM6CgpSIGZ1bmN0aW9uIHwgYWxnb3JpdGhtIHwgUGFja2FnZSB8IFByb2JsZW0KLS0tLS0tLS0tLS18LS0tLS0tLS0tLS18LS0tLS0tLS0tfC0tLS0tLS0tCioqbG0qKiB8IGxpbmVhciBtb2RlbCB8IHwgUmVnIAoqKmdsbSoqfCBsb2dpc3RpYyBtb2RlbCAgfCB8Q2xhc3MKKipsZGEqKnwgbGluZWFyIGRpc2NyaW1pbmFudCBhbmFseXNpcyB8TUFTUyB8Q2xhc3MKKipzdm0qKnwgU3VwcG9ydCBWZWN0b3IgTWFjaGluZSB8IGUxMDcxIHwgQ2xhc3MKKiprbm4ucmVnKip8bmVhcmVzdCBuZWlnaGJvcnxGTk58IFJlZwoqKmtubioqIHwgbmVhcmVzdCBuZWlnaGJvcnxjbGFzc3xDbGFzcwoqKnJwYXJ0Kip8IHRyZWUgfHJwYXJ0fCBSZWcgYW5kIENsYXNzCioqZ2xtbmV0Kip8cmlkZ2UgYW5kIGxhc3NvfGdsbW5ldHxSZWcgYW5kIENsYXNzCioqZ2JtKip8Ym9vc3Rpbmd8Z2JtfFJlZyBhbmQgQ2xhc3MKKipyYW5kb21Gb3Jlc3QqKnxyYW5kb20gRm9yZXN0IHwgcmFuZG9tRm9yZXN0IHwgUmVnIGFuZCBDbGFzcwoKKipSZW1hcmsqKjogZm9yICpnbG1uZXQqLCBpdCBpcyBub3QgcG9zc2libGUgdG8gdXNlIGEgZm9ybXVsYSAqKll+LioqLiBXZSBoYXZlIHRvIHVzZSBhIG1hdHJpeCBmb3IgdGhlICRYJCBhbmQgYSB2ZWN0b3IgZm9yICRZJC4gRnVuY3Rpb24gKiptb2RlbC5tYXRyaXgqKiBpcyB2ZXJ5IGludGVyZXN0aW5nIHRvIGNvbXB1dGUgdGhlIG1hdHJpeCBvZiAkWCQuCgpTaW5jZSBhIGxvdCBvZiBhbGdvcml0aG1zIGFyZSBhdmFpbGFibGUgdG8gYWRkcmVzcyAgcHJvYmxlbXMgb2YgcmVncmVzc2lvbiBhbmQgc3VwZXJ2aXNlZCBjbGFzc2ljYXRpb24sIGl0IGlzIGltcG9ydGFudCB0byBkZWZpbmUgcGVyZm9ybWFuY2UgY3JpdGVyaWEuIENyaXRlcmlhIGFyZSBnZW5lcmFsbHkgdW5rbm93biBhbmQgbmVlZCB0byBiZSBlc3RpbWF0ZWQgb24gYSB2YWxpZGF0aW9uIHNldCAob3IgYnkgY3Jvc3MgdmFsaWRhdGlvbikuIFdlIGdlbmVyYWx5IGhhdmUgdG8gdXNlZCB0aGUgKipwcmVkaWN0KiogZnVuY3Rpb24gdG8gbWFrZSB0aGUgZXN0aW1hdGlvbi4gVGhpcyBmdW5jdGlvbiBpcyBhIGdlbmVyaWMgZnVuY3Rpb246IGl0IG1lYW5zIHRoYXQgd2UgY2FuIHVzZSAqKnByZWRpY3QqKiBmb3IgdHJlZSwgcmFuZG9tIGZvcmVzdCwgbG9naXN0aWMgcmVncmVzc2lvbi4uLiBIb3dldmVyIHByZWRpY3Rpb24gYWxnb3JpdGhtcyBhcmUgbm90IHRoZSBzYW1lIGZvciBhbGwgbW9kZWxzLiBUbyBvYnRhaW4gdGhlIGhlbHAgb24gKipwcmVkaWN0KiogZm9yIAoKKiBsb2dpc3RpYyBtb2RlbCwgeW91IGhhdmUgdG8gZW50ZXIgKipoZWxwKHByZWRpY3QuZ2xtKSoqCiogcGVuYWxpemVkIGxvZ2lzdGljIG1vZGVscyB5b3UgaGF2ZSB0byBlbnRlciAqKmhlbHAocHJlZGljdC5nbG1uZXQpKioKKiB0cmVlcywgeW91IGhhdmUgdG8gZW50ZXIgKipoZWxwKHByZWRpY3QucnBhcnQpKioKKiByYW5kb20gZm9yZXN0LCB5b3UgaGF2ZSB0byBlbnRlciAqKmhlbHAocHJlZGljdC5yYW5kb21Gb3Jlc3QpKioKKiAuLi4KCgpJbiB0aGUgc2VxdWVsLCB3ZSBhc3N1bWUgdGhhdCAkXG1hdGhjYWwgWT1cbWF0aGJiIFIkIGFuZCB3ZSBjb25zaWRlciB0aGUgcmVncmVzc2lvbiBtb2RlbAokJFk9bShYKStcdmFyZXBzaWxvbi4kJApUaGUgcGVyZm9ybWFuY2Ugb2YgYSBnaXZlbiBlc3RpbWF0ZSAkXHdpZGVoYXR7bX0kIG9mICRtJCB3aWxsIGJlIG1lYXN1cmVkIGJ5IGl0cyBtZWFuIHNxdWFyZSBlcnJvcjoKJCRFWyhZLVx3aWRlaGF0IG0oWCkpXjJdLiQkCgojIyBFeGVyY2lzZSAxIChsaW5lYXIgbW9kZWwsIGxtIGFuZCBwcmVkaWN0KQoKV2UgY29uc2lkZXIgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsCiQkWT1cYmV0YV8wK1xiZXRhXzFYXzErXGRvdHMrXGJldGFfcFhfcCtcdmFyZXBzaWxvbiQkCndoZXJlICRYXzEsXGRvdHMsWF9wJCBhcmUgaW5kZXBlbmRlbnQgdmFyaWFibGVzIHdpdGggZGlzdHJpYnV0aW9uICRcbWF0aGNhbCBOKDAsMSkkIGFuZCAkXHZhcmVwc2lsb24kIGhhcyBkaXN0cmlidXRpb24gJFxtYXRoY2FsIE4oMCwwLjVeMikkICgkWD0oWF8xLFxkb3RzLFhfcCkkIGFuZCAkXHZhcmVwc2lsb24kIGFyZSBpbmRlcGVuZGVudCkuCgpXZSBjb25zaWRlciAkcD01JCwgJFxiZXRhXzA9MCQgYW5kICRcYmV0YV8xPVxkb3RzPVxiZXRhXzU9MSQuCgoxLiBHZW5lcmF0ZSAkbj0xMDAwJCBvYnNlcnZhdGlvbnMgJCh4XzEseV8xKSxcZG90cywoeF9uLHlfbikkIGFjY29yZGluZyB0byB0aGlzIG1vZGVsICh1c2UgKipybm9ybSoqIGFuZCAqKnJ1bmlmKiopLiBQdXQgdGhlc2Ugb2JzZXJ2YXRpb25zIGluIGEgKipkYXRhLmZyYW1lIGRmKiogb2YgZGltZW5zaW9uICQxMDAwXHRpbWVzIDYkLgoKYGBge3J9Cm4gPC0gMTAwMApwIDwtIDUKc2V0LnNlZWQoMTIzNCkKWC5tYXQgPC0gbWF0cml4KHJub3JtKG4qcCksbmNvbD1wKQplcHMgPC0gcm5vcm0obixtZWFuID0gMCxzZD0wLjUpCmRmIDwtIGRhdGEuZnJhbWUoWC5tYXQsZXBzKQpkZiA8LSBkZiAlPiUgbXV0YXRlKFk9WDErWDIrWDMrWDQrWDUrZXBzKSAlPiUgc2VsZWN0KC1lcHMpCmBgYAoKMi4gRml0IGEgbGluZWFyIG1vZGVsICgqKmxtKiogZnVuY3Rpb24pIG9uICpkZiogYW5kIHByaW50IHRoZSBlc3RpbWF0b3Igb2YgJFxiZXRhXzAsXGRvdHMsXGJldGFfNSQuCgozLiBHZW5lcmF0ZSBhIHRlc3QgZGF0YXNldCAoKipkZi50ZXN0KiopICQoeF97bisxfSx5X3tuKzF9KSxcZG90cywoeF97bittfSx5X3tuK219KSQgd2l0aCAkbT01MDAkIGFjY29yZGluZyB0byB0aGUgc2FtZSBtb2RlbC4KCjQuIEZvciBlYWNoIGluZGl2aWR1YWxzIGluICoqZGYudGVzdCoqIGRhdGFzZXQsIGNhbGN1bGF0ZSB0aGUgcHJlZGljdGlvbnMgJFx3aWRlaGF0IHlfaSQgYnkgdGhlIG1vZGVsIGZpdHRlZCBpbiBxdWVzdGlvbiAyICh1c2UgKipwcmVkaWN0KiogZnVuY3Rpb24gd2l0aCB0aGUgb3B0aW9uICpuZXdkYXRhKikuCgo1LiBDcmVhdGUgYSBuZXcgZGF0YWZyYW1lIHdoaWNoIGNvbnRhaW5zIGJvdGggcHJlZGljdGlvbnMgJFx3aWRlaGF0IHlfaSQgYW5kIG9ic2VydmF0aW9ucyAkeV9pJCBvZiB0aGUgdGVzdCBkYXRhIHNldC4KCjYuIFdpdGggKipzdW1tYXJpemUqKiB2ZXJiLCBjYWxjdWxhdGUgdGhlIGVzdGltYXRlZCBtZWFuIHNxdWFyZSBlcnJvciBvZiB0aGUgbGluZWFyIG1vZGVsOgokJFxmcmFjezF9e219XHN1bV97aVxpbiB0ZXN0fShcd2lkZWhhdCB5X2kteV9pKV4yLiQkCgoKIyMgRXhlcmNpc2UgMiAodmFyaWFibGUgc2VsZWN0aW9uKQoKV2Ugc3RpbGwgY29uc2lkZXIgdGhlIG1vZGVsCiQkWT1cYmV0YV8wK1xiZXRhXzFYXzErXGRvdHMrXGJldGFfcFhfcCtcdmFyZXBzaWxvbiQkCndoZXJlICRYXzEsXGRvdHMsWF9wJCBhcmUgaW5kZXBlbmRlbnQgdmFyaWFibGVzIHdpdGggZGlzdHJpYnV0aW9uICRcbWF0aGNhbCBOKDAsMSkkIGFuZCAkXHZhcmVwc2lsb24kIGhhcyBkaXN0cmlidXRpb24gJFxtYXRoY2FsIE4oMCwwLjVeMikkICgkWD0oWF8xLFxkb3RzLFhfcCkkIGFuZCAkXHZhcmVwc2lsb24kIGFyZSBpbmRlcGVuZGVudCkuCgpCdXQgaGVyZSwgd2UgYXNzdW1lIHRoYXQgJHA9MTA1JCBhbmQgdGhhdAoKKiAkXGJldGFfMD0wJCwgJFxiZXRhXzE9XGRvdHM9XGJldGFfNT0xJAoqICRcYmV0YV82PVxkb3RzPVxiZXRhX3sxMDV9PTAkLgpTbyBoZXJlLCBvbmx5IHZhcmlhYmxlcyAkWF8xLFxkb3RzLFhfNSQgYXJlIHJlbGV2YW50IHRvIGV4cGxhaW4gJFkkLgoKMS4gR2VuZXJhdGUgJG49MTAwMCQgb2JzZXJ2YXRpb25zICQoeF8xLHlfMSksXGRvdHMsKHhfbix5X24pJCBhY2NvcmRpbmcgdG8gdGhpcyBtb2RlbC4gUHV0IHRoZXNlIG9ic2VydmF0aW9ucyBpbiBhICoqZGF0YS5mcmFtZSBkZioqIHdpdGggZGltZW5zaW9uICQxMDAwXHRpbWVzIDEwNiQuCgoyLiBGaXQgYSBsaW5lYXIgbW9kZWwgKCoqbG0qKiBmdW5jdGlvbikgd2l0aCAqZGYqIGFuZCBwcmludCB0aGUgZXN0aW1hdG9yIG9mICRcYmV0YV8wLFxkb3RzLFxiZXRhX3sxMDV9JC4KCjMuIFdlIHByb3Bvc2UgdG8gbWFrZSBhIHZhcmlhYmxlIHNlbGVjdGlvbiBwcm9jZWR1cmUgd2l0aCBhIGJhY2t3YXJkIHNlbGVjdGlvbiBhcHByb2FjaCB1c2luZyAqKkJJQyoqIGNyaXRlcmlvbiAodGhpcyBhcHByb2FjaCB3aWxsIGJlIGRlc2NyaWJlZCBpbiBkZXRhaWxzIGluIG90aGVyIGxlY3R1cmVzKS4gWW91IGp1c3QgaGF2ZSB0byB1c2UgdGhlICoqc3RlcCoqIGZ1bmN0aW9uIHdpdGggdGhlICoqZGlyZWN0aW9uPSJiYWNrd2FyZCIqKiBhbmQgKiprPWxvZygxMDAwKSoqIG9wdGlvbnMuIFdyaXRlIHRoZSBzZWxlY3RlZCBtb2RlbC4gV2UgY2FsbCBpdCAqKm1vZC5zdGVwKiouCgo0LiBHZW5lcmF0ZSBhIHRlc3QgZGF0YXNldCAoKipkZi50ZXN0KiopICQoeF97bisxfSx5X3tuKzF9KSxcZG90cywoeF97bittfSx5X3tuK219KSQgd2l0aCAkbT01MDAkIGFjY29yZGluZyB0byB0aGUgc2FtZSBtb2RlbC4gQ29tcHV0ZSB0aGUgZXN0aW1hdGVkIG1lYW4gc3F1YXJlIGVycm9yCiQkXGZyYWN7MX17bX1cc3VtX3tpXGluIHRlc3R9KFx3aWRlaGF0IHlfaS15X2kpXjIkJApmb3IgdGhlIHR3byBtb2RlbHMuCgpXZSBvYnNlcnZlIHRoYXQgdGhlIHNlbGVjdGVkIG1vZGVsIGhhcyBzbWFsbGVyIE1TRSB0aGFuIHRoZSBmdWxsIG1vZGVsLiBXZSBjb25jbHVkZSB0aGF0IHRoaXMgbW9kZWwgaXMgYmV0dGVyIGZvciB0aGlzIGNyaXRlcmlvbi4gV2Ugd2lsbCBzZWUgdGhhdCBpdCBpcyBkdWUgdG8gdGhlIHZhcmlhbmNlIG9mIGxlYXN0IHNxdWFyZSBlc3RpbWF0ZXM6IHdoZW4gdGhlcmUgYXJlIG5vaXN5IHZhcmlhYmxlcyBpbiBhIG1vZGVsLCB2YXJpYW5jZSBvZiBsZWFzdCBzcXVhcmUgZXN0aW1hdGUgaW5jcmVhc2VzLiBUaGVzZSBlc3RpbWF0ZXMgYXJlIHRodXMgbGVzcyBhY2N1cmF0ZS4gVG8gY2lyY3VtdmVudCB0aGlzIHByb2JsZW0sIHdlIGhhdmUgdG8gc2VsZWN0IHZhcmlhYmxlcyAoYXMgd2UgZG8gaGVyZSB3aXRoIHRoZSAqKnN0ZXAqKiBmdW5jdGlvbikgb3IgdG8gdXNlIHJlZ3VsYXJpemVkIG1ldGhvZHMgc3VjaCBhcyBMYXNzbyBhbmQgUmlkZ2UgKElTTCBsZWN0dXJlKS4KCgpJbiB0aGUgcHJldmlvdXMgZXhlcmNpc2Ugd2UgaGF2ZSBlc3RpbWF0ZWQgdGhlIE1TRSBvZiBhIG1vZGVsIGJ5IHZhbGlkYXRpb24gaG9sZCBvdXQuIFRoZSBkYXRhc2V0IGhhcyBiZWVuIHNwbGl0dGVkIGludG8gdHdvIHBhcnRzOgoKKiBhIHRyYWluIHNldCAoc2l6ZSAxMDAwKSB0byBmaXQgdGhlIG1vZGVsCiogYSB0ZXN0IHNldCAoc2l6ZSA1MDApIHRvIGVzdGltYXRlIHRoZSBtZWFuIHNxdWFyZSBlcnJvci4KClRvIGJlIGVmZmljaWVudCwgdGhpcyBhcHByb2FjaCByZXF1aXJlcyBhIGxhcmdlIG51bWJlciBvZiBkYXRhLiBXaGVuIHRoaXMgaXMgbm90IHRoZSBjYXNlLCB3ZSBjYW4gdXNlICRLJC1mb2xkIGNyb3NzIHZhbGlkYXRpb24uIFN1Y2ggYXBwcm9hY2hlcyBjb25zaXN0cyBpbiByZXBlYXRpbmcgdGhlIGhvbGQgb3V0IHByb2NlZHVyZXMgb24gbWFueSBibG9ja3Mgb2YgdGhlIGRhdGEuIE1vcmUgcHJlY2lzZWx5OgoKKiB0aGUgZGF0YXNldCBpcyBmaXJzdCBkaXZpZGVkIGludG8gJEskIGJsb2NrcyAkQl8xLFxkb3RzLEJfSyQKKiBGb3IgZWFjaCBibG9jayAkQl9qJCwKCiAgICAqIGZpdCB0aGUgbW9kZWwgb24gdGhlIHdob2xlIGRhdGFzZXQgd2l0aG91dCAkQl9qJC4gV2Ugbm90ZSAkbV9qJCB0aGUgb2J0YWluZWQgbW9kZWwuCiAgICAqIHByZWRpY3QgaW5kaXZpZHVhbHMgaW4gJEJfaiQgYnkgJG1faiQKKiBXZSB0aHVzIG9idGFpbmVkIGEgcHJlZGljdGlvbiAkXHdpZGVoYXQgeV9pJCBmb3IgZWFjaCBpbmRpdmlkdWFsIG9mIHRoZSBkYXRhIHNldCBhbmQgd2UgZXN0aW1hdGUgdGhlIG1lYW4gc3F1YXJlIGVycm9yIGJ5CiQkXGZyYWN7MX17bn1cc3VtX3tpPTF9Xm4oXHdpZGVoYXQgeV9pLXlfaSleMi4kJAoKIyMgRXhlcmNpc2UgMyAoY3Jvc3MgdmFsaWRhdGlvbiBmb3Igb3pvbmUgZGF0YSBzZXQpCgpXZSBjb25zaWRlciB0aGUgKipvem9uZS50eHQqKiBkYXRhc2V0IGF2YWlsYWJsZSBbaGVyZV0oaHR0cHM6Ly9yLXN0YXQtc2MtZG9ubmVlcy5naXRodWIuaW8vbGlzdGVfZG9uLmh0bWwpLiBUaGUgcHJvYmxlbSBpcyB0byBleHBsYWluIHRoZSBkYWlseSBtYXhpbXVtIG9uZS1ob3VyLWF2ZXJhZ2Ugb3pvbmUgcmVhZGluZyBpbiBjb2x1bW4gKiptYXhPMyoqLgoKICAxLiBJbXBvcnQgdGhlIGRhdGFzZXQgd2l0aCAqKnJlYWQudGFibGUqKgoKICAyLiBGaXQgYSBsaW5lYXIgbW9kZWwgb24gdGhlIHdob2xlIGRhdGFzZXQuCgogIDMuIFNwbGl0IHRoZSBkYXRhIGludG8gJDEwJCBmb2xkcyB3aXRoIHRoZSAqKmNyZWF0ZUZvbGRzKiogZnVuY3Rpb24gb2YgdGhlICoqY2FyZXQqKiBwYWNrYWdlIChvYnNlcnZlIHRoYXQgdGhlIG91dHB1dCBvZiB0aGlzIGZ1bmN0aW9uIGlzIGEgbGlzdCkuCgogIDQuIEVzdGltYXRlIHRoZSBtZWFuIHNxdWFyZSBlcnJvciBvZiB0aGUgbGluZWFyIG1vZGVsIGJ5IGNyb3NzIHZhbGlkYXRpb24uIChVc2UgYSBsb29wKS4KCgogIDUuIE5vdywgd2Ugd2FudCB0byBleHBsYWluICoqbWF4TzMqKiBieSB0aGUgb3RoZXIgdmFyaWFibGVzIHdpdGggYSB0cmVlLiBGaXQgYSB0cmVlIG9uIHRoZSB3aG9sZSBkYXRhc2V0IHdpdGggdGhlICoqcnBhcnQqKiBmdW5jdGlvbiBvZiB0aGUgKipycGFydCoqIHBhY2thZ2UuIAoKICA2LiBWaXN1YWxpemUgdGhlIHRyZWUgd2l0aCAKCiAgICAgICogdGhlICoqcnBhcnQucGxvdCoqIGZ1bmN0aW9uIG9mIHRoZSAqKnJwYXJ0LnBsb3QqKiBwYWNrYWdlCiAgICAgICogdGhlICoqdmlzVHJlZSoqIGZ1bmN0aW9uIG9mIHRoZSAqKnZpc05ldHdvcmsqKiBwYWNrYWdlCgoKICA3LiBFc3RpbWF0ZSB0aGUgbWVhbiBzcXVhcmUgZXJyb3Igb2YgdGhlIHRyZWUgYnkgMTAtZm9sZCBjcm9zcyB2YWxpZGF0aW9uIGFuZCBtYWtlIGEgY29tcGFyaXNvbiB3aXRoIHRoZSBsaW5lYXIgbW9kZWwuCgoKIyMjIFJlbWFyawoKSW4gdGhlIElMUyBsZWN0dXJlLCB3ZSB3aWxsIHNlZSB0aGF0IHBhY2thZ2UgKipjYXJldCoqIGFsbG93IHRvIGVzdGltYXRlIE1TRSBvciAoUk1TRSkgZGlyZWN0bHkKYGBge3J9CmN0cmwgPC0gdHJhaW5Db250cm9sKG1ldGhvZD0iY3YiLG51bWJlcj0xMCkKdHJhaW4obWF4TzN+LixkYXRhPW96b25lLG1ldGhvZD0ibG0iLHRyQ29udHJvbD1jdHJsKQpgYGAKCg==