Steve Su, Pierson Wodarz
University of Illinois - Urbana, Champaign; MCS

Objective

Evaluate and select two models which can best predict the sales price of a given house from the data set. The two models should meet RMSE scores of 0.125 for the first 5 training/test splits and 0.135 for the remaining 5 training/test splits. Use the R programming language to perform this study. The two chosen models were:

  1. Decision tree - Gradient boosting model (GBM),
  2. Regression model with ElasticNet penalty.

Data

The dataset has 2930 rows (i.e., houses) and 83 columns, named Ames_data.csv originally prepared by De Cock 2011.

  • The first column is “PID”, the Parcel identification number;
  • The last column is the response variable, Sale_Price;
  • The remaining 81 columns are explanatory variables describing (almost) every aspect of residential homes.

Test IDs

Use project1_testIDs.dat to generate 10 sets of training/test splits from Ames_data.csv. Each column contains the 879 row-numbers of a test data. Use the R code below as a guide to perform the splits.

data <- read.csv("Ames_data.csv")
testIDs <- read.table("project1_testIDs.dat")
j <- 2
train <- data[-testIDs[,j], ]
test <- data[testIDs[,j], ]
test.y <- test[, c(1, 83)]
test <- test[, -83]
write.csv(train,"train.csv",row.names=FALSE)
write.csv(test, "test.csv",row.names=FALSE)
write.csv(test.y,"test_y.csv",row.names=FALSE)

Data Preprocessing

Decision Tree - Gradient Boosting Model (GBM)

Train data preprocessing:

Data preprocessing was negligible for the boosting tree model. The only preprocessing for training was to transform character fields to factor.

Test data preprocessing:

For the test data for the boosting tree model, we only needed to ensure that the character fields were transformed to factors with the same levels/values as those in the train dataset. Therefore, the character fields were transformed to factors using the levels from the train dataset.

Regression Model with ElasticNet Penalty

Train data preprocessing:

Remove variables: Some predictors were removed because they were not beneficial to the model. There are several reasons this could be the case, such as variables with a high frequency in only one category, or variables which don’t have a clear relationship to price at all. For example, the character variable of street would result in many distinct categories, and a disparity in streets between train and test would result in many NAs when factorizing, making the variable meaningless. Additionally, this information may be captured by other neighborhood categories.

One Hot Encoding: For better results, we perform one-hot encoding using dummy variables trained on the character/categorical fields in the test data. Factor/character variables where converted to k-1 dummy variables where k corresponds to the levels of the categorical variable. Those dummy variables are then used to create additional one hot encoding columns, with the values representing whether a particular property had the categorical value for that category. We then removed the factorized categorical columns as the data is contained within the one hot encoding columns.

Replace NA: We found that NANs existed within the “yr_garage_built” variable. We replaced NANs with 0, as our regression model cannot process NAN and replacing NAN with 0 allows the model to process these properties without significantly negatively impacting performance.

Winsorization: Certain numerical values usually only have a linear influence on price within a specific range, above which the value of the house is not as significantly impacted by a corresponding increase in the value of the predictor. As a result, we windsorize the data to the 95th percentile for several of these numerical fields.

Test data preprocessing:

We performed the same preprocessing for our test data as we did for our training data: removing variables, one hot encoding, replacing NA values, and winsorization. The only difference was in the preprocessing of the character/categorical values. Similar to the test data preprocessing for GBM, we needed to ensure that the character fields were transformed into one hot encodings with encodings equivalent to those from the train data set. In this case, we encode into dummy variables using the predict function where the model is created by the dummyVars function from the caret package and the data for this model is the train data. This ensures that the results are encodings from the train dataset or NA (for values in the test dataset which weren’t present in the train dataset). For the NA values, they were simply set to 0. This is appropriate for one-hot encoding as a value that is outside the existing range will be encoded into 0 within the range.

Model Technical Details

Decision Tree - GBM

Model description: We used the gbm package in r to fit a gradient boosting model. This model uses a group of decision trees, each of which is a weak or shallow decision tree model. By improving the decision trees successively, the final model has powerful predictive performance. Code details can be found in the PerformanceValidation.Rmd file or view here for html version.

Tuning parameters: The tuning parameters were set statically for all train and test splits/subsets and do not change between the splits/subsets. Instead, the parameters were tuned on the overall dataset, and reusing the same tuning parameters produced good performance on any observed split/subset of the data. We tuned the parameters manually, using the tuning parameters from https://uc-r.github.io/gbm_regression as a starting point.

distribution = gaussian: A gaussian distribution is used for regression as is the case here. This uses the squared error.

n.trees = 500: This parameter specifies the number of trees to fit. 500 trees produced the best performance across the various train/test splits when manually tuning parameters using steps of ~50 trees.

shrinkage = 0.1: This parameter controls the learning rate. A shrinkage of .1 produced optimal results.

interaction.depth = 5: This parameter specifies the maximum depth of each tree and controls the number of interactions between parameters. Above 5 or below 5 reduced the overall performance. This is because below 5 was too shallow but above 5 was too deep.

bag.fraction = 1: This parameter controls the fraction of the training set observations selected for the next tree. In this case, we found that using the entirety of the training set for the next tree produced positive results.

cv.folds = 5: 5 fold cross validation was a good balance between training time and performance, with performance not significantly improving with additional cross validation (e.g. 10 fold).

Regression Model - ElasticNet

Model description:
To perform the elasticnet model we used R’s glmnet library. The cv.glmnet() class provides a minimum lambda value which we used as a hyperparameter for our prediction. This minimum lambda value is determined through cross validation of the training data which is automatically performed by R. Note, interactions and polynomial terms were not introduced into this model which could be helpful. Code details can be found in the PerformanceValidation.Rmd file or view here for html version.

Tuning parameters:
Depending on the split, the minimum lambda parameter was found to be between 0.010 and 0.014. The other hyperparameter required for our model was alpha. When alpha is zero it performs a ridge penalty. When alpha is one it performs a lasso penalty. We found that an alpha of 0.2 was enough to achieve the required performance.

Results

In general, for each split the GBM model has a lower RMSE than the ElasticNet model. We were able to achieve the set performance goals for each model.

Performance (log scale)

Test Split GBM RMSE Test Error ElasticNet RMSE Test Error
1 0.1163156 0.1226382
2 0.1160827 0.1179175
3 0.1088356 0.120597
4 0.1132515 0.1198059
5 0.1083723 0.1114046
6 0.1230553 0.1336597
7 0.1266367 0.1270781
8 0.1168903 0.1208161
9 0.1261995 0.1299768
10 0.1193281 0.123419

Running time

Running time = 3.348801 minutes

System specs:

  • Processor: Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz, 1190 Mhz, 4 Core(s), 8 Logical Processor(s)
  • RAM: 8.00 GB

Summary

  • Before finding a successful regression model using elasticnet, we ran both ridge and lasso penalty regression models. The ridge and lasso performed similarly and was just shy of hitting the performance benchmarks. In both models, the minimum lambda was a better choice than the one standard error lambda value. On average (10 splits) the minimum lambda showed between 5-7% improvement in RMSE value compared to the one standard error lambda for both the lasso and ridge models. With this information we also used the minimum lambda in our elasticnet model. Before giving up on the lasso model we also refit the model using lm() function with the non-zero beta coefficients of the one standard error lambda model. We saw an average improvement (10 splits) of 6% in RMSE reduction. In the minimun lambda lasso model we did not see any improvement with refit but rather a slight degredation in performance. In our final model, we used the elasticnet penalty in order to meet the performance benchmarks. An alpha level of 0.2 was used. An alpha level closer to zero behaves more closely to a ridge penalty.

  • GBM required significantly less preprocessing, but was more sensitive to tuning parameters. For example, ElasticNet acheived the performance requirements with an alpha = 0.2 or alpha = 0.5. However, doubling a parameter for GBM, such as n.trees, resulted in signficantly worse performance.

  • There may be the potential to improve ElasticNet performance by the inclusion of non-linear relationships or interactions between factors. However, the glmnet package does not have a trivial way to include non-linear terms or interactions between terms.

  • Winsorization had one of the largest performance gains for all preprocessing steps for the ElasticNet model. This validates the initial assumption that certain numerical values do not maintain a linear relationship in the extreme cases.

  • A more automated method of tuning parameters, specifically for GBM, could be explored to further increase performance.

LS0tDQp0aXRsZTogIlByZWRpY3QgdGhlIEhvdXNpbmcgUHJpY2VzIGluIEFtZXMsIElvd2EgLSBEZWNpc2lvbiBUcmVlIChHQk0pIHZzIExpbmVhciBSZWdyZXNzaW9uIE1vZGVsIChFbGFzdGljIE5ldCkiDQpkYXRlOiAiRmFsbCAyMDIxIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19mbG9hdDogVFJVRQ0KLS0tDQoNClN0ZXZlIFN1LCBQaWVyc29uIFdvZGFyeiAgIA0KVW5pdmVyc2l0eSBvZiBJbGxpbm9pcyAtIFVyYmFuYSwgQ2hhbXBhaWduOyBNQ1MgICANCg0KIyBPYmplY3RpdmUNCkV2YWx1YXRlIGFuZCBzZWxlY3QgdHdvIG1vZGVscyB3aGljaCBjYW4gYmVzdCBwcmVkaWN0IHRoZSBzYWxlcyBwcmljZSBvZiBhIGdpdmVuIGhvdXNlIGZyb20gdGhlIGRhdGEgc2V0LiBUaGUgdHdvIG1vZGVscyBzaG91bGQgbWVldCBSTVNFIHNjb3JlcyBvZiAwLjEyNSBmb3IgdGhlIGZpcnN0IDUgdHJhaW5pbmcvdGVzdCBzcGxpdHMgYW5kDQowLjEzNSBmb3IgdGhlIHJlbWFpbmluZyA1IHRyYWluaW5nL3Rlc3Qgc3BsaXRzLiAgVXNlIHRoZSBSIHByb2dyYW1taW5nIGxhbmd1YWdlIHRvIHBlcmZvcm0gdGhpcyBzdHVkeS4gIFRoZSB0d28gY2hvc2VuIG1vZGVscyB3ZXJlOiAgDQoNCjEuIERlY2lzaW9uIHRyZWUgLSBHcmFkaWVudCBib29zdGluZyBtb2RlbCAoR0JNKSwgIA0KMi4gUmVncmVzc2lvbiBtb2RlbCB3aXRoIEVsYXN0aWNOZXQgcGVuYWx0eS4gIA0KDQoNCiMgRGF0YSAgDQpUaGUgZGF0YXNldCBoYXMgMjkzMCByb3dzIChpLmUuLCBob3VzZXMpIGFuZCA4MyBjb2x1bW5zLCBuYW1lZCBgQW1lc19kYXRhLmNzdmAgb3JpZ2luYWxseSBwcmVwYXJlZCBieSBbRGUgQ29jayAyMDExXShodHRwOi8vanNlLmFtc3RhdC5vcmcvdjE5bjMvZGVjb2NrLnBkZikuICANCg0KLSBUaGUgZmlyc3QgY29sdW1uIGlzIOKAnFBJROKAnSwgdGhlIFBhcmNlbCBpZGVudGlmaWNhdGlvbiBudW1iZXI7DQotIFRoZSBsYXN0IGNvbHVtbiBpcyB0aGUgcmVzcG9uc2UgdmFyaWFibGUsIFNhbGVfUHJpY2U7DQotIFRoZSByZW1haW5pbmcgODEgY29sdW1ucyBhcmUgZXhwbGFuYXRvcnkgdmFyaWFibGVzIGRlc2NyaWJpbmcgKGFsbW9zdCkgZXZlcnkgYXNwZWN0IG9mIHJlc2lkZW50aWFsIGhvbWVzLiAgDQoNCiMjIFRlc3QgSURzDQpVc2UgYHByb2plY3QxX3Rlc3RJRHMuZGF0YCB0byBnZW5lcmF0ZSAxMCBzZXRzIG9mIHRyYWluaW5nL3Rlc3Qgc3BsaXRzIGZyb20gQW1lc19kYXRhLmNzdi4gRWFjaCBjb2x1bW4gY29udGFpbnMgdGhlIDg3OSByb3ctbnVtYmVycyBvZiBhIHRlc3QgZGF0YS4gIFVzZSB0aGUgUiBjb2RlIGJlbG93IGFzIGEgZ3VpZGUgdG8gcGVyZm9ybSB0aGUgc3BsaXRzLg0KDQpgYGB7cixldmFsPUZBTFNFfQ0KZGF0YSA8LSByZWFkLmNzdigiQW1lc19kYXRhLmNzdiIpDQp0ZXN0SURzIDwtIHJlYWQudGFibGUoInByb2plY3QxX3Rlc3RJRHMuZGF0IikNCmogPC0gMg0KdHJhaW4gPC0gZGF0YVstdGVzdElEc1ssal0sIF0NCnRlc3QgPC0gZGF0YVt0ZXN0SURzWyxqXSwgXQ0KdGVzdC55IDwtIHRlc3RbLCBjKDEsIDgzKV0NCnRlc3QgPC0gdGVzdFssIC04M10NCndyaXRlLmNzdih0cmFpbiwidHJhaW4uY3N2Iixyb3cubmFtZXM9RkFMU0UpDQp3cml0ZS5jc3YodGVzdCwgInRlc3QuY3N2Iixyb3cubmFtZXM9RkFMU0UpDQp3cml0ZS5jc3YodGVzdC55LCJ0ZXN0X3kuY3N2Iixyb3cubmFtZXM9RkFMU0UpDQoNCmBgYA0KDQoNCiMgRGF0YSBQcmVwcm9jZXNzaW5nIA0KIyMgRGVjaXNpb24gVHJlZSAtIEdyYWRpZW50IEJvb3N0aW5nIE1vZGVsIChHQk0pDQoqKlRyYWluIGRhdGEgcHJlcHJvY2Vzc2luZzoqKg0KDQpEYXRhIHByZXByb2Nlc3Npbmcgd2FzIG5lZ2xpZ2libGUgZm9yIHRoZSBib29zdGluZyB0cmVlIG1vZGVsLiBUaGUgb25seSBwcmVwcm9jZXNzaW5nIGZvciB0cmFpbmluZyB3YXMgdG8gdHJhbnNmb3JtIGNoYXJhY3RlciBmaWVsZHMgdG8gZmFjdG9yLg0KDQoqKlRlc3QgZGF0YSBwcmVwcm9jZXNzaW5nOiAqKg0KDQpGb3IgdGhlIHRlc3QgZGF0YSBmb3IgdGhlIGJvb3N0aW5nIHRyZWUgbW9kZWwsIHdlIG9ubHkgbmVlZGVkIHRvIGVuc3VyZSB0aGF0IHRoZSBjaGFyYWN0ZXIgZmllbGRzIHdlcmUgdHJhbnNmb3JtZWQgdG8gZmFjdG9ycyB3aXRoIHRoZSBzYW1lIGxldmVscy92YWx1ZXMgYXMgdGhvc2UgaW4gdGhlIHRyYWluIGRhdGFzZXQuIFRoZXJlZm9yZSwgdGhlIGNoYXJhY3RlciBmaWVsZHMgd2VyZSB0cmFuc2Zvcm1lZCB0byBmYWN0b3JzIHVzaW5nIHRoZSBsZXZlbHMgZnJvbSB0aGUgdHJhaW4gZGF0YXNldC4NCg0KIyMgUmVncmVzc2lvbiBNb2RlbCB3aXRoIEVsYXN0aWNOZXQgUGVuYWx0eQ0KKipUcmFpbiBkYXRhIHByZXByb2Nlc3Npbmc6KioNCg0KUmVtb3ZlIHZhcmlhYmxlczogU29tZSBwcmVkaWN0b3JzIHdlcmUgcmVtb3ZlZCBiZWNhdXNlIHRoZXkgd2VyZSBub3QgYmVuZWZpY2lhbCB0byB0aGUgbW9kZWwuIFRoZXJlIGFyZSBzZXZlcmFsIHJlYXNvbnMgdGhpcyBjb3VsZCBiZSB0aGUgY2FzZSwgc3VjaCBhcyB2YXJpYWJsZXMgd2l0aCBhIGhpZ2ggZnJlcXVlbmN5IGluIG9ubHkgb25lIGNhdGVnb3J5LCBvciB2YXJpYWJsZXMgd2hpY2ggZG9uJ3QgaGF2ZSBhIGNsZWFyIHJlbGF0aW9uc2hpcCB0byBwcmljZSBhdCBhbGwuIEZvciBleGFtcGxlLCB0aGUgY2hhcmFjdGVyIHZhcmlhYmxlIG9mIHN0cmVldCB3b3VsZCByZXN1bHQgaW4gbWFueSBkaXN0aW5jdCBjYXRlZ29yaWVzLCBhbmQgYSBkaXNwYXJpdHkgaW4gc3RyZWV0cyBiZXR3ZWVuIHRyYWluIGFuZCB0ZXN0IHdvdWxkIHJlc3VsdCBpbiBtYW55IE5BcyB3aGVuIGZhY3Rvcml6aW5nLCBtYWtpbmcgdGhlIHZhcmlhYmxlIG1lYW5pbmdsZXNzLiBBZGRpdGlvbmFsbHksIHRoaXMgaW5mb3JtYXRpb24gbWF5IGJlIGNhcHR1cmVkIGJ5IG90aGVyIG5laWdoYm9yaG9vZCBjYXRlZ29yaWVzLiANCg0KT25lIEhvdCBFbmNvZGluZzogRm9yIGJldHRlciByZXN1bHRzLCB3ZSBwZXJmb3JtIG9uZS1ob3QgZW5jb2RpbmcgdXNpbmcgZHVtbXkgdmFyaWFibGVzIHRyYWluZWQgb24gdGhlIGNoYXJhY3Rlci9jYXRlZ29yaWNhbCBmaWVsZHMgaW4gdGhlIHRlc3QgZGF0YS4gRmFjdG9yL2NoYXJhY3RlciB2YXJpYWJsZXMgd2hlcmUgY29udmVydGVkIHRvIGstMSBkdW1teSB2YXJpYWJsZXMgd2hlcmUgayBjb3JyZXNwb25kcyB0byB0aGUgbGV2ZWxzIG9mIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gVGhvc2UgZHVtbXkgdmFyaWFibGVzIGFyZSB0aGVuIHVzZWQgdG8gY3JlYXRlIGFkZGl0aW9uYWwgb25lIGhvdCBlbmNvZGluZyBjb2x1bW5zLCB3aXRoIHRoZSB2YWx1ZXMgcmVwcmVzZW50aW5nIHdoZXRoZXIgYSBwYXJ0aWN1bGFyIHByb3BlcnR5IGhhZCB0aGUgY2F0ZWdvcmljYWwgdmFsdWUgZm9yIHRoYXQgY2F0ZWdvcnkuIFdlIHRoZW4gcmVtb3ZlZCB0aGUgZmFjdG9yaXplZCBjYXRlZ29yaWNhbCBjb2x1bW5zIGFzIHRoZSBkYXRhIGlzIGNvbnRhaW5lZCB3aXRoaW4gdGhlIG9uZSBob3QgZW5jb2RpbmcgY29sdW1ucy4gDQoNClJlcGxhY2UgTkE6IFdlIGZvdW5kIHRoYXQgTkFOcyBleGlzdGVkIHdpdGhpbiB0aGUgInlyX2dhcmFnZV9idWlsdCIgdmFyaWFibGUuIFdlIHJlcGxhY2VkIE5BTnMgd2l0aCAwLCBhcyBvdXIgcmVncmVzc2lvbiBtb2RlbCBjYW5ub3QgcHJvY2VzcyBOQU4gYW5kIHJlcGxhY2luZyBOQU4gd2l0aCAwIGFsbG93cyB0aGUgbW9kZWwgdG8gcHJvY2VzcyB0aGVzZSBwcm9wZXJ0aWVzIHdpdGhvdXQgc2lnbmlmaWNhbnRseSBuZWdhdGl2ZWx5IGltcGFjdGluZyBwZXJmb3JtYW5jZS4gDQoNCldpbnNvcml6YXRpb246IENlcnRhaW4gbnVtZXJpY2FsIHZhbHVlcyB1c3VhbGx5IG9ubHkgaGF2ZSBhIGxpbmVhciBpbmZsdWVuY2Ugb24gcHJpY2Ugd2l0aGluIGEgc3BlY2lmaWMgcmFuZ2UsIGFib3ZlIHdoaWNoIHRoZSB2YWx1ZSBvZiB0aGUgaG91c2UgaXMgbm90IGFzIHNpZ25pZmljYW50bHkgaW1wYWN0ZWQgYnkgYSBjb3JyZXNwb25kaW5nIGluY3JlYXNlIGluIHRoZSB2YWx1ZSBvZiB0aGUgcHJlZGljdG9yLiBBcyBhIHJlc3VsdCwgd2Ugd2luZHNvcml6ZSB0aGUgZGF0YSB0byB0aGUgOTV0aCBwZXJjZW50aWxlIGZvciBzZXZlcmFsIG9mIHRoZXNlIG51bWVyaWNhbCBmaWVsZHMuIA0KDQoqKlRlc3QgZGF0YSBwcmVwcm9jZXNzaW5nOioqDQoNCldlIHBlcmZvcm1lZCB0aGUgc2FtZSBwcmVwcm9jZXNzaW5nIGZvciBvdXIgdGVzdCBkYXRhIGFzIHdlIGRpZCBmb3Igb3VyIHRyYWluaW5nIGRhdGE6IHJlbW92aW5nIHZhcmlhYmxlcywgb25lIGhvdCBlbmNvZGluZywgcmVwbGFjaW5nIE5BIHZhbHVlcywgYW5kIHdpbnNvcml6YXRpb24uIFRoZSBvbmx5IGRpZmZlcmVuY2Ugd2FzIGluIHRoZSBwcmVwcm9jZXNzaW5nIG9mIHRoZSBjaGFyYWN0ZXIvY2F0ZWdvcmljYWwgdmFsdWVzLiBTaW1pbGFyIHRvIHRoZSB0ZXN0IGRhdGEgcHJlcHJvY2Vzc2luZyBmb3IgR0JNLCB3ZSBuZWVkZWQgdG8gZW5zdXJlIHRoYXQgdGhlIGNoYXJhY3RlciBmaWVsZHMgd2VyZSB0cmFuc2Zvcm1lZCBpbnRvIG9uZSBob3QgZW5jb2RpbmdzIHdpdGggZW5jb2RpbmdzIGVxdWl2YWxlbnQgdG8gdGhvc2UgZnJvbSB0aGUgdHJhaW4gZGF0YSBzZXQuIEluIHRoaXMgY2FzZSwgd2UgZW5jb2RlIGludG8gZHVtbXkgdmFyaWFibGVzIHVzaW5nIHRoZSBwcmVkaWN0IGZ1bmN0aW9uIHdoZXJlIHRoZSBtb2RlbCBpcyBjcmVhdGVkIGJ5IHRoZSBgZHVtbXlWYXJzYCBmdW5jdGlvbiBmcm9tIHRoZSBgY2FyZXRgIHBhY2thZ2UgYW5kIHRoZSBkYXRhIGZvciB0aGlzIG1vZGVsIGlzIHRoZSB0cmFpbiBkYXRhLiBUaGlzIGVuc3VyZXMgdGhhdCB0aGUgcmVzdWx0cyBhcmUgZW5jb2RpbmdzIGZyb20gdGhlIHRyYWluIGRhdGFzZXQgb3IgTkEgKGZvciB2YWx1ZXMgaW4gdGhlIHRlc3QgZGF0YXNldCB3aGljaCB3ZXJlbid0IHByZXNlbnQgaW4gdGhlIHRyYWluIGRhdGFzZXQpLiBGb3IgdGhlIE5BIHZhbHVlcywgdGhleSB3ZXJlIHNpbXBseSBzZXQgdG8gMC4gVGhpcyBpcyBhcHByb3ByaWF0ZSBmb3Igb25lLWhvdCBlbmNvZGluZyBhcyBhIHZhbHVlIHRoYXQgaXMgb3V0c2lkZSB0aGUgZXhpc3RpbmcgcmFuZ2Ugd2lsbCBiZSBlbmNvZGVkIGludG8gMCB3aXRoaW4gdGhlIHJhbmdlLiAgDQoNCiMgTW9kZWwgVGVjaG5pY2FsIERldGFpbHMNCiMjIERlY2lzaW9uIFRyZWUgLSBHQk0NCioqTW9kZWwgZGVzY3JpcHRpb246KiogV2UgdXNlZCB0aGUgYGdibWAgcGFja2FnZSBpbiByIHRvIGZpdCBhIGdyYWRpZW50IGJvb3N0aW5nIG1vZGVsLiBUaGlzIG1vZGVsIHVzZXMgYSBncm91cCBvZiBkZWNpc2lvbiB0cmVlcywgZWFjaCBvZiB3aGljaCBpcyBhIHdlYWsgb3Igc2hhbGxvdyBkZWNpc2lvbiB0cmVlIG1vZGVsLiBCeSBpbXByb3ZpbmcgdGhlIGRlY2lzaW9uIHRyZWVzIHN1Y2Nlc3NpdmVseSwgdGhlIGZpbmFsIG1vZGVsIGhhcyBwb3dlcmZ1bCBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlLiAgQ29kZSBkZXRhaWxzIGNhbiBiZSBmb3VuZCBpbiB0aGUgYFBlcmZvcm1hbmNlVmFsaWRhdGlvbi5SbWRgIGZpbGUgb3IgdmlldyBbaGVyZV0oaHR0cHM6Ly9zdGV2ZTMwMy5naXRodWIuaW8vc3RhdDU0MnByb2oxLXJlZ3Jlc3Npb25UcmVlcy9QZXJmb3JtYW5jZVZhbGlkYXRpb24ubmIuaHRtbCkgZm9yIGh0bWwgdmVyc2lvbi4NCg0KKipUdW5pbmcgcGFyYW1ldGVyczoqKiBUaGUgdHVuaW5nIHBhcmFtZXRlcnMgd2VyZSBzZXQgc3RhdGljYWxseSBmb3IgYWxsIHRyYWluIGFuZCB0ZXN0IHNwbGl0cy9zdWJzZXRzIGFuZCBkbyBub3QgY2hhbmdlIGJldHdlZW4gdGhlIHNwbGl0cy9zdWJzZXRzLiBJbnN0ZWFkLCB0aGUgcGFyYW1ldGVycyB3ZXJlIHR1bmVkIG9uIHRoZSBvdmVyYWxsIGRhdGFzZXQsIGFuZCByZXVzaW5nIHRoZSBzYW1lIHR1bmluZyBwYXJhbWV0ZXJzIHByb2R1Y2VkIGdvb2QgcGVyZm9ybWFuY2Ugb24gYW55IG9ic2VydmVkIHNwbGl0L3N1YnNldCBvZiB0aGUgZGF0YS4gV2UgdHVuZWQgdGhlIHBhcmFtZXRlcnMgbWFudWFsbHksIHVzaW5nIHRoZSB0dW5pbmcgcGFyYW1ldGVycyBmcm9tIGh0dHBzOi8vdWMtci5naXRodWIuaW8vZ2JtX3JlZ3Jlc3Npb24gYXMgYSBzdGFydGluZyBwb2ludC4gDQoNCmRpc3RyaWJ1dGlvbiA9IGdhdXNzaWFuOiBBIGdhdXNzaWFuIGRpc3RyaWJ1dGlvbiBpcyB1c2VkIGZvciByZWdyZXNzaW9uIGFzIGlzIHRoZSBjYXNlIGhlcmUuIFRoaXMgdXNlcyB0aGUgc3F1YXJlZCBlcnJvci4gDQoNCm4udHJlZXMgPSA1MDA6IFRoaXMgcGFyYW1ldGVyIHNwZWNpZmllcyB0aGUgbnVtYmVyIG9mIHRyZWVzIHRvIGZpdC4gNTAwIHRyZWVzIHByb2R1Y2VkIHRoZSBiZXN0IHBlcmZvcm1hbmNlIGFjcm9zcyB0aGUgdmFyaW91cyB0cmFpbi90ZXN0IHNwbGl0cyB3aGVuIG1hbnVhbGx5IHR1bmluZyBwYXJhbWV0ZXJzIHVzaW5nIHN0ZXBzIG9mIH41MCB0cmVlcy4gDQoNCnNocmlua2FnZSA9IDAuMTogVGhpcyBwYXJhbWV0ZXIgY29udHJvbHMgdGhlIGxlYXJuaW5nIHJhdGUuIEEgc2hyaW5rYWdlIG9mIC4xIHByb2R1Y2VkIG9wdGltYWwgcmVzdWx0cy4gDQoNCmludGVyYWN0aW9uLmRlcHRoID0gNTogVGhpcyBwYXJhbWV0ZXIgc3BlY2lmaWVzIHRoZSBtYXhpbXVtIGRlcHRoIG9mIGVhY2ggdHJlZSBhbmQgY29udHJvbHMgdGhlIG51bWJlciBvZiBpbnRlcmFjdGlvbnMgYmV0d2VlbiBwYXJhbWV0ZXJzLiBBYm92ZSA1IG9yIGJlbG93IDUgcmVkdWNlZCB0aGUgb3ZlcmFsbCBwZXJmb3JtYW5jZS4gVGhpcyBpcyBiZWNhdXNlIGJlbG93IDUgd2FzIHRvbyBzaGFsbG93IGJ1dCBhYm92ZSA1IHdhcyB0b28gZGVlcC4gDQoNCmJhZy5mcmFjdGlvbiA9IDE6IFRoaXMgcGFyYW1ldGVyIGNvbnRyb2xzIHRoZSBmcmFjdGlvbiBvZiB0aGUgdHJhaW5pbmcgc2V0IG9ic2VydmF0aW9ucyBzZWxlY3RlZCBmb3IgdGhlIG5leHQgdHJlZS4gSW4gdGhpcyBjYXNlLCB3ZSBmb3VuZCB0aGF0IHVzaW5nIHRoZSBlbnRpcmV0eSBvZiB0aGUgdHJhaW5pbmcgc2V0IGZvciB0aGUgbmV4dCB0cmVlIHByb2R1Y2VkIHBvc2l0aXZlIHJlc3VsdHMuIA0KDQpjdi5mb2xkcyA9IDU6IDUgZm9sZCBjcm9zcyB2YWxpZGF0aW9uIHdhcyBhIGdvb2QgYmFsYW5jZSBiZXR3ZWVuIHRyYWluaW5nIHRpbWUgYW5kIHBlcmZvcm1hbmNlLCB3aXRoIHBlcmZvcm1hbmNlIG5vdCBzaWduaWZpY2FudGx5IGltcHJvdmluZyB3aXRoIGFkZGl0aW9uYWwgY3Jvc3MgdmFsaWRhdGlvbiAoZS5nLiAxMCBmb2xkKS4NCg0KIyMgUmVncmVzc2lvbiBNb2RlbCAtIEVsYXN0aWNOZXQNCioqTW9kZWwgZGVzY3JpcHRpb246KiogIA0KVG8gcGVyZm9ybSB0aGUgZWxhc3RpY25ldCBtb2RlbCB3ZSB1c2VkIFIncyBnbG1uZXQgbGlicmFyeS4gIFRoZSBgY3YuZ2xtbmV0KClgIGNsYXNzIHByb3ZpZGVzIGEgbWluaW11bSBsYW1iZGEgdmFsdWUgd2hpY2ggd2UgdXNlZCBhcyBhIGh5cGVycGFyYW1ldGVyIGZvciBvdXIgcHJlZGljdGlvbi4gIFRoaXMgbWluaW11bSBsYW1iZGEgdmFsdWUgaXMgZGV0ZXJtaW5lZCB0aHJvdWdoIGNyb3NzIHZhbGlkYXRpb24gb2YgdGhlIHRyYWluaW5nIGRhdGEgd2hpY2ggaXMgYXV0b21hdGljYWxseSBwZXJmb3JtZWQgYnkgUi4gIE5vdGUsIGludGVyYWN0aW9ucyBhbmQgcG9seW5vbWlhbCB0ZXJtcyB3ZXJlIG5vdCBpbnRyb2R1Y2VkIGludG8gdGhpcyBtb2RlbCB3aGljaCBjb3VsZCBiZSBoZWxwZnVsLiAgQ29kZSBkZXRhaWxzIGNhbiBiZSBmb3VuZCBpbiB0aGUgYFBlcmZvcm1hbmNlVmFsaWRhdGlvbi5SbWRgIGZpbGUgb3IgdmlldyBbaGVyZV0oaHR0cHM6Ly9zdGV2ZTMwMy5naXRodWIuaW8vc3RhdDU0MnByb2oxLXJlZ3Jlc3Npb25UcmVlcy9QZXJmb3JtYW5jZVZhbGlkYXRpb24ubmIuaHRtbCkgZm9yIGh0bWwgdmVyc2lvbi4gIA0KDQoqKlR1bmluZyBwYXJhbWV0ZXJzOioqICANCkRlcGVuZGluZyBvbiB0aGUgc3BsaXQsIHRoZSBtaW5pbXVtIGxhbWJkYSBwYXJhbWV0ZXIgd2FzIGZvdW5kIHRvIGJlIGJldHdlZW4gMC4wMTAgYW5kIDAuMDE0Lg0KVGhlIG90aGVyIGh5cGVycGFyYW1ldGVyIHJlcXVpcmVkIGZvciBvdXIgbW9kZWwgd2FzIGFscGhhLiAgV2hlbiBhbHBoYSBpcyB6ZXJvIGl0IHBlcmZvcm1zIGEgcmlkZ2UgcGVuYWx0eS4gIFdoZW4gYWxwaGEgaXMgb25lIGl0IHBlcmZvcm1zIGEgbGFzc28gcGVuYWx0eS4gIFdlIGZvdW5kIHRoYXQgYW4gYWxwaGEgb2YgMC4yIHdhcyBlbm91Z2ggdG8gYWNoaWV2ZSB0aGUgcmVxdWlyZWQgcGVyZm9ybWFuY2UuICANCg0KDQojIFJlc3VsdHMNCkluIGdlbmVyYWwsIGZvciBlYWNoIHNwbGl0IHRoZSBHQk0gbW9kZWwgaGFzIGEgbG93ZXIgUk1TRSB0aGFuIHRoZSBFbGFzdGljTmV0IG1vZGVsLiAgV2Ugd2VyZSBhYmxlIHRvIGFjaGlldmUgdGhlIHNldCBwZXJmb3JtYW5jZSBnb2FscyBmb3IgZWFjaCBtb2RlbC4NCg0KIyMgUGVyZm9ybWFuY2UgKGxvZyBzY2FsZSkNCg0KfCBUZXN0IFNwbGl0IHwgR0JNIFJNU0UgVGVzdCBFcnJvciB8IEVsYXN0aWNOZXQgUk1TRSBUZXN0IEVycm9yIHwNCnwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IDEgICAgICAgICAgfCAwLjExNjMxNTYgICAgICAgICAgIHwgMC4xMjI2MzgyICAgICAgICAgICAgICAgICAgfA0KfCAyICAgICAgICAgIHwgMC4xMTYwODI3ICAgICAgICAgICB8IDAuMTE3OTE3NSAgICAgICAgICAgICAgICAgIHwNCnwgMyAgICAgICAgICB8IDAuMTA4ODM1NiAgICAgICAgICAgfCAwLjEyMDU5NyAgICAgICAgICAgICAgICAgICB8DQp8IDQgICAgICAgICAgfCAwLjExMzI1MTUgICAgICAgICAgIHwgMC4xMTk4MDU5ICAgICAgICAgICAgICAgICAgfA0KfCA1ICAgICAgICAgIHwgMC4xMDgzNzIzICAgICAgICAgICB8IDAuMTExNDA0NiAgICAgICAgICAgICAgICAgIHwNCnwgNiAgICAgICAgICB8IDAuMTIzMDU1MyAgICAgICAgICAgfCAwLjEzMzY1OTcgICAgICAgICAgICAgICAgICB8DQp8IDcgICAgICAgICAgfCAwLjEyNjYzNjcgICAgICAgICAgIHwgMC4xMjcwNzgxICAgICAgICAgICAgICAgICAgfA0KfCA4ICAgICAgICAgIHwgMC4xMTY4OTAzICAgICAgICAgICB8IDAuMTIwODE2MSAgICAgICAgICAgICAgICAgIHwNCnwgOSAgICAgICAgICB8IDAuMTI2MTk5NSAgICAgICAgICAgfCAwLjEyOTk3NjggICAgICAgICAgICAgICAgICB8DQp8IDEwICAgICAgICAgfCAwLjExOTMyODEgICAgICAgICAgIHwgMC4xMjM0MTkgICAgICAgICAgICAgICAgICAgfA0KDQojIyBSdW5uaW5nIHRpbWUNClJ1bm5pbmcgdGltZSA9IDMuMzQ4ODAxIG1pbnV0ZXMNCg0KU3lzdGVtIHNwZWNzOiANCg0KICAqIFByb2Nlc3NvcjoJSW50ZWwoUikgQ29yZShUTSkgaTUtMTAzNUcxIENQVSBAIDEuMDBHSHosIDExOTAgTWh6LCA0IENvcmUocyksIDggTG9naWNhbCBQcm9jZXNzb3IocykNCiAgKiBSQU06IDguMDAgR0INCg0KIyBTdW1tYXJ5DQoqICBCZWZvcmUgZmluZGluZyBhIHN1Y2Nlc3NmdWwgcmVncmVzc2lvbiBtb2RlbCB1c2luZyBlbGFzdGljbmV0LCB3ZSByYW4gYm90aCByaWRnZSBhbmQgbGFzc28gcGVuYWx0eSByZWdyZXNzaW9uIG1vZGVscy4gIFRoZSByaWRnZSBhbmQgbGFzc28gcGVyZm9ybWVkIHNpbWlsYXJseSBhbmQgd2FzIGp1c3Qgc2h5IG9mIGhpdHRpbmcgdGhlIHBlcmZvcm1hbmNlIGJlbmNobWFya3MuICBJbiBib3RoIG1vZGVscywgdGhlIG1pbmltdW0gbGFtYmRhIHdhcyBhIGJldHRlciBjaG9pY2UgdGhhbiB0aGUgb25lIHN0YW5kYXJkIGVycm9yIGxhbWJkYSB2YWx1ZS4gIE9uIGF2ZXJhZ2UgKDEwIHNwbGl0cykgdGhlIG1pbmltdW0gbGFtYmRhIHNob3dlZCBiZXR3ZWVuIDUtNyUgaW1wcm92ZW1lbnQgaW4gUk1TRSB2YWx1ZSBjb21wYXJlZCB0byB0aGUgb25lIHN0YW5kYXJkIGVycm9yIGxhbWJkYSBmb3IgYm90aCB0aGUgbGFzc28gYW5kIHJpZGdlIG1vZGVscy4gIFdpdGggdGhpcyBpbmZvcm1hdGlvbiB3ZSBhbHNvIHVzZWQgdGhlIG1pbmltdW0gbGFtYmRhIGluIG91ciBlbGFzdGljbmV0IG1vZGVsLiAgQmVmb3JlIGdpdmluZyB1cCBvbiB0aGUgbGFzc28gbW9kZWwgd2UgYWxzbyByZWZpdCB0aGUgbW9kZWwgdXNpbmcgbG0oKSBmdW5jdGlvbiB3aXRoIHRoZSBub24temVybyBiZXRhIGNvZWZmaWNpZW50cyBvZiB0aGUgb25lIHN0YW5kYXJkIGVycm9yIGxhbWJkYSBtb2RlbC4gIFdlIHNhdyBhbiBhdmVyYWdlIGltcHJvdmVtZW50ICgxMCBzcGxpdHMpIG9mIDYlIGluIFJNU0UgcmVkdWN0aW9uLiAgSW4gdGhlIG1pbmltdW4gbGFtYmRhIGxhc3NvIG1vZGVsIHdlIGRpZCBub3Qgc2VlIGFueSBpbXByb3ZlbWVudCB3aXRoIHJlZml0IGJ1dCByYXRoZXIgYSBzbGlnaHQgZGVncmVkYXRpb24gaW4gcGVyZm9ybWFuY2UuICBJbiBvdXIgZmluYWwgbW9kZWwsIHdlIHVzZWQgdGhlIGVsYXN0aWNuZXQgcGVuYWx0eSBpbiBvcmRlciB0byBtZWV0IHRoZSBwZXJmb3JtYW5jZSBiZW5jaG1hcmtzLiAgQW4gYWxwaGEgbGV2ZWwgb2YgMC4yIHdhcyB1c2VkLiAgQW4gYWxwaGEgbGV2ZWwgY2xvc2VyIHRvIHplcm8gYmVoYXZlcyBtb3JlIGNsb3NlbHkgdG8gYSByaWRnZSBwZW5hbHR5LiAgICANCg0KKiBHQk0gcmVxdWlyZWQgc2lnbmlmaWNhbnRseSBsZXNzIHByZXByb2Nlc3NpbmcsIGJ1dCB3YXMgbW9yZSBzZW5zaXRpdmUgdG8gdHVuaW5nIHBhcmFtZXRlcnMuIEZvciBleGFtcGxlLCBFbGFzdGljTmV0IGFjaGVpdmVkIHRoZSBwZXJmb3JtYW5jZSByZXF1aXJlbWVudHMgd2l0aCBhbiBhbHBoYSA9IDAuMiBvciBhbHBoYSA9IDAuNS4gSG93ZXZlciwgZG91YmxpbmcgYSBwYXJhbWV0ZXIgZm9yIEdCTSwgc3VjaCBhcyBuLnRyZWVzLCByZXN1bHRlZCBpbiBzaWduZmljYW50bHkgd29yc2UgcGVyZm9ybWFuY2UuIA0KDQoqIFRoZXJlIG1heSBiZSB0aGUgcG90ZW50aWFsIHRvIGltcHJvdmUgRWxhc3RpY05ldCBwZXJmb3JtYW5jZSBieSB0aGUgaW5jbHVzaW9uIG9mIG5vbi1saW5lYXIgcmVsYXRpb25zaGlwcyBvciBpbnRlcmFjdGlvbnMgYmV0d2VlbiBmYWN0b3JzLiBIb3dldmVyLCB0aGUgYGdsbW5ldGAgcGFja2FnZSBkb2VzIG5vdCBoYXZlIGEgdHJpdmlhbCB3YXkgdG8gaW5jbHVkZSBub24tbGluZWFyIHRlcm1zIG9yIGludGVyYWN0aW9ucyBiZXR3ZWVuIHRlcm1zLiANCg0KKiBXaW5zb3JpemF0aW9uIGhhZCBvbmUgb2YgdGhlIGxhcmdlc3QgcGVyZm9ybWFuY2UgZ2FpbnMgZm9yIGFsbCBwcmVwcm9jZXNzaW5nIHN0ZXBzIGZvciB0aGUgRWxhc3RpY05ldCBtb2RlbC4gVGhpcyB2YWxpZGF0ZXMgdGhlIGluaXRpYWwgYXNzdW1wdGlvbiB0aGF0IGNlcnRhaW4gbnVtZXJpY2FsIHZhbHVlcyBkbyBub3QgbWFpbnRhaW4gYSBsaW5lYXIgcmVsYXRpb25zaGlwIGluIHRoZSBleHRyZW1lIGNhc2VzLiANCg0KKiBBIG1vcmUgYXV0b21hdGVkIG1ldGhvZCBvZiB0dW5pbmcgcGFyYW1ldGVycywgc3BlY2lmaWNhbGx5IGZvciBHQk0sIGNvdWxkIGJlIGV4cGxvcmVkIHRvIGZ1cnRoZXIgaW5jcmVhc2UgcGVyZm9ybWFuY2UuIA0KDQojIFJlc291cmNlcw0KaHR0cHM6Ly91Yy1yLmdpdGh1Yi5pby9nYm1fcmVncmVzc2lvbiAgDQpodHRwczovL2dkY29kZXIuY29tL3doZW4td2h5LXRvLXVzZS1sb2ctdHJhbnNmb3JtYXRpb24taW4tcmVncmVzc2lvbi8gIA0KW0RlIENvY2sgMjAxMV0oaHR0cDovL2pzZS5hbXN0YXQub3JnL3YxOW4zL2RlY29jay5wZGYpICANCg0KDQoNCg0KDQoNCg==