Part A - Kaggle (20%)
Do at least one submission on Kaggle competition beyond the first sample_submission you already did. This means you should have at least 2 entries shown on the Leaderboard and your RMSE Score must be < 107.28565. If you already did this, then you are done with this part of the homework.
(Eventually you will need to have a total of at least 5 submissions by May 3, Sunday, 11:59 pm.)
Part B - Multiple Regression & Logistic Regression Practice (80%)
Do your work on this R Notebook file showing all the outputs and answers along with the code. (If you have trouble working with an R notebook file, you may want to review this video (the same video I mentioned before).
Enter your answers on the Blackboard quizzes provided. Submitting the R notebook is optional.
1. More Catalog Marketing
This is a practice of using different models for subsets of the data and computing rmse’s.It is related to what we did in the previous homework and in class.
First, run below to read in the 2 data sets into respective data frames as before.
train <- read.csv("CatalogTrain.csv")
test <- read.csv("CatalogTest.csv")
Remember in class, the model we used was:
model <- lm(log(Amount.Spent) ~ Close + Salary + Children + Previous.Customer +
Previous.Spent + Catalogs, data = train)
This gave us RMSE of train data = 606.31 and RMSE of test data = 626.67.
In train, create a new variable logAmt that is equal to the log of the Amount.Spent. So we can work with the new variable logAmt instead of log(Amount.Spent).
Use pairs.panels function from psych package to create a scatterplot matrix of these variables:
Salary, Children, Previous.Spent, Catalogs, logAmt.
Look at the scatterplot between Previous.Spent and logAmt. Notice there are many zero values for Previous.Spent. If Previous.Customer = No, this means the customer did not make any purchases the previous year, so Previous.Spent value must be 0. Verify that the customers for which Previous.Customer = No have all 0 values for Previous.Spent.
For customers who are not previous customers, it makes no sense to use Previous.Spent as a predictor. So we will try building separate regression models for the customers who are previous customers and those who are not. Create 2 data frames from train as follows:
trainYes = subset of train for which Previous.Customer == Yes
trainNo = subset of train for which Previous.Customer == No
Now build separate scatterplot matrices for trainYes and trainNo data frames. For trainNo, Previous.Spent will all be 0, so we don’t want to include that variable.
For trainNo, the variables should be Salary, Children, Catalogs, logAmt.
For trainYes, the variables should be Salary, Children, Previous.Spent, Catalogs, logAmt.
For trainYes, try running a linear regression model with y = logAmt and X = Close, Salary, Children, Previous.Spent and Catalogs. Let’s call this model1. Are all the x variables statistically significant?
Now, test for multicollinearity with vif function from the car package. Which two variable(s) have vif value higher than 5?
Let’s try removing one of these two variables. Run 2 regressions:
model2 with x = Close, Salary, Children, Catalogs and
model3 with x = Close, Children, Previous.Spent and Catalogs.
Compare the RMSE and Adj R-squared of the two regression models. Based on these, which one should you keep?
Now, let’s decide on a regression model for trainNo data frame. These customers were not previous customers, so we will not include the Previous.Spent variable. For trainNo, run a regression model with y = logAmt and x = Close, Salary, Children, Catalogs. Call this model model4.
There will be one variable that is not significant. Remove this variable and run regression again. Call this model5.
Run the predict function with model2 on trainYes and model5 on trainNo. Call the results predTrainYes and predTrainNo, respectively. What are the rmse’s from these (remember this function comes from Metrics package)? Don’t forget - since the predictions are for logAmt, use exp to get the predicted Amount.Spent.
So far, we obtained the RMSE’s separately from running different models on trainYes and trainNo, the subsets of train data. Run the following code to combine them together to find the overall RMSE.
# Add predicted Amount.Spent column to the two data frames.
trainYes1 <- cbind(trainYes, pred.Amount = exp(predTrainYes))
trainNo1 <- cbind(trainNo, pred.Amount = exp(predTrainNo))
# Combine the two data frames to make one data frame then get the rmse from it.
trainCombined <- rbind(trainYes1, trainNo1)
rmse(trainCombined$Amount.Spent, trainCombined$pred.Amount)
Now let’s use model2 and model5 on the appropriate rows of the test data. Build 2 smaller data frames from the test data as you did with train data in (c). They should be named testNo and testYes.
Repeat the previous two parts (j) and (k) on these data frames testYes and testNo to find the 2 separate RMSE’s then the overall RMSE.
Remember the RMSE of the previous model were 606.31 (train) and 626.67 (test). Compare these to the (overall) RMSE now from the train (from (k)) and the test data (from (m)). Are the new RMSE’s better or worse for the train data? What about for the test data?
2. Lending Club
In the lending industry, investors provide loans to borrowers in exchange for the promise of repayment with interest. If the borrower repays the loan, then the lender profits from the interest. However, if the borrower is unable to repay the loan (default), then the lender loses money. Therefore, lenders face the problem of predicting the risk of a borrower being unable to repay a loan.
LendingClub.csv contains data from lendingclub.com, a website that connects borrowers and investors over the Internet. This dataset represents a sample of loans that were funded through the lendingclub.com platform in 2007 - 2015. The definition of the columns are in the file LendingClub_DataDescription.csv.
Here, the dependent variable is loan_status.
Based on this data, we would like to build a model that predicts which borrowers will pay back the full loan, that is loan_status = 1. We use logistic regression that computes for each loan the probability that it will be paid in full.
Read the data into a data frame lending and run str(). How many observations are in this data set?
How many loans were paid in full? What proportion is that?
Create a training data and the test data as follows. Load the package caTools, set the seed to 20. Use sample.split to randomly allocate 80 % of the rows to data frame, train, and the remaining 20% to data frame, test. Recall using sample.split function with lending$loan_status will ensure that the distribution of loan_status is similar between the training set and the test set. (Use the set of commands we went over in class.) What proportion of loans were paid full in train? What about in test?
Let’s work with the train data first. (Build a logistic regression with the following as the predictors: log(annual_inc), delinq_2yrs, dti, int_rate, loan_amnt. Here, log is the natural log of the annual income. We are using the log because annual income is highly right-skewed. [If you are annoyed with all the scientific notations like -1.507e-05 in the output, you can suppress the scientific notation by running this command before asking for the output: options(scipen = 999). Now all the outputs from that point on will not have scientific notation. If you want to undo this later during the session, use command: options(scipen = 0).]
According to the regression coefficients, increasing which variables will increase the probability of the loan being paid in full? Does this make sense?
Write down the expression for logit, z. (This kind format: z = 2.567 + 0.435 x - 1.345 y)
Suppose you have the following data about borrower A. Annual income is $73,500, debt-to-income ratio is 13.0, there was one delinquency in the past two years, the loan amount he wants is $20,000 with the interest rate 8%. What is the probability borrower A will pay back the loan in full? (Compute by plugging in variable values in the regression to get z then plugging this z value into the expression 1/(1+exp(-z)). What are the odds of him paying back in full? Would you lend to this borrower?
Now compute the probability directly with the predict function. You should get the same probability you computed above.
For all the observations in the train data set, use predict function to compute the probabilities of paying back in full. Name the result predTrain. Since you are using the train data that the model came from, you don’t need to specify newdata in the predict function. Verify this is a vector 9,249 probability values.
Now for all the observations in the test data set, use predict function to compute the probabilities of paying back in full. Name the result predTest. Display the first 10 values.
LS0tDQp0aXRsZTogIkhvbWV3b3JrIDkgZHVlIDQvMjcvMjAiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBQYXJ0IEEgLSBLYWdnbGUgKDIwJSkNCg0KKipEbyBhdCBsZWFzdCBvbmUgc3VibWlzc2lvbiBvbiBLYWdnbGUgY29tcGV0aXRpb24gYmV5b25kIHRoZSBmaXJzdCBzYW1wbGVfc3VibWlzc2lvbiB5b3UgYWxyZWFkeSBkaWQuKiogICBUaGlzIG1lYW5zIHlvdSBzaG91bGQgaGF2ZSBhdCBsZWFzdCAyIGVudHJpZXMgc2hvd24gb24gdGhlIExlYWRlcmJvYXJkIGFuZCB5b3VyIFJNU0UgU2NvcmUgbXVzdCBiZSA8IDEwNy4yODU2NS4gSWYgeW91IGFscmVhZHkgZGlkIHRoaXMsIHRoZW4geW91IGFyZSBkb25lIHdpdGggdGhpcyBwYXJ0IG9mIHRoZSBob21ld29yay4gIA0KKEV2ZW50dWFsbHkgeW91IHdpbGwgbmVlZCB0byBoYXZlIGEgdG90YWwgb2YgYXQgbGVhc3QgNSBzdWJtaXNzaW9ucyBieSBNYXkgMywgU3VuZGF5LCAxMTo1OSBwbS4pDQoNCiMjIFBhcnQgQiAtIE11bHRpcGxlIFJlZ3Jlc3Npb24gJiBMb2dpc3RpYyBSZWdyZXNzaW9uIFByYWN0aWNlICg4MCUpDQoNCkRvIHlvdXIgd29yayBvbiB0aGlzIFIgTm90ZWJvb2sgZmlsZSBzaG93aW5nIGFsbCB0aGUgb3V0cHV0cyBhbmQgYW5zd2VycyBhbG9uZyB3aXRoIHRoZSBjb2RlLiAgKElmIHlvdSBoYXZlIHRyb3VibGUgd29ya2luZyB3aXRoIGFuIFIgbm90ZWJvb2sgZmlsZSwgeW91IG1heSB3YW50IHRvIHJldmlldyB0aGlzIHZpZGVvICh0aGUgc2FtZSBbdmlkZW9dKGh0dHBzOi8veW91dHUuYmUvMGREMlU3cnJHYUEpIEkgbWVudGlvbmVkIGJlZm9yZSkuICANCioqRW50ZXIgeW91ciBhbnN3ZXJzIG9uIHRoZSBCbGFja2JvYXJkIHF1aXp6ZXMgcHJvdmlkZWQuKiogU3VibWl0dGluZyB0aGUgUiBub3RlYm9vayBpcyBvcHRpb25hbC4NCg0KIyMjIDEuIE1vcmUgQ2F0YWxvZyBNYXJrZXRpbmcNClRoaXMgaXMgYSBwcmFjdGljZSBvZiB1c2luZyBkaWZmZXJlbnQgbW9kZWxzIGZvciBzdWJzZXRzIG9mIHRoZSBkYXRhIGFuZCBjb21wdXRpbmcgcm1zZSdzLkl0IGlzIHJlbGF0ZWQgdG8gd2hhdCB3ZSBkaWQgaW4gdGhlIHByZXZpb3VzIGhvbWV3b3JrIGFuZCBpbiBjbGFzcy4gICAgDQpGaXJzdCwgcnVuIGJlbG93IHRvIHJlYWQgaW4gdGhlIDIgZGF0YSBzZXRzIGludG8gcmVzcGVjdGl2ZSBkYXRhIGZyYW1lcyBhcyBiZWZvcmUuDQoNCmBgYHtyfQ0KdHJhaW4gPC0gcmVhZC5jc3YoIkNhdGFsb2dUcmFpbi5jc3YiKSAgICANCnRlc3QgPC0gcmVhZC5jc3YoIkNhdGFsb2dUZXN0LmNzdiIpDQpgYGANCg0KUmVtZW1iZXIgaW4gY2xhc3MsIHRoZSBtb2RlbCB3ZSB1c2VkIHdhczogDQoNCmBgYHtyfQ0KbW9kZWwgPC0gbG0obG9nKEFtb3VudC5TcGVudCkgfiBDbG9zZSArIFNhbGFyeSArIENoaWxkcmVuICsgUHJldmlvdXMuQ3VzdG9tZXIgKyANCiAgICAgICAgICAgICAgUHJldmlvdXMuU3BlbnQgKyBDYXRhbG9ncywgZGF0YSA9IHRyYWluKQ0KYGBgDQoNClRoaXMgZ2F2ZSB1cyBSTVNFIG9mIHRyYWluIGRhdGEgPSAqKjYwNi4zMSoqIGFuZCBSTVNFIG9mIHRlc3QgZGF0YSA9ICoqNjI2LjY3KiouIA0KICAgICAgICAgICAgICANCihhKSBJbiAqKnRyYWluKiosIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSAqKmxvZ0FtdCoqIHRoYXQgaXMgZXF1YWwgdG8gdGhlIGxvZyBvZiB0aGUgQW1vdW50LlNwZW50LiBTbyB3ZSBjYW4gd29yayB3aXRoIHRoZSBuZXcgdmFyaWFibGUgbG9nQW10IGluc3RlYWQgb2YgbG9nKEFtb3VudC5TcGVudCkuICANClVzZSAqcGFpcnMucGFuZWxzKiBmdW5jdGlvbiBmcm9tIHBzeWNoIHBhY2thZ2UgdG8gY3JlYXRlIGEgc2NhdHRlcnBsb3QgbWF0cml4IG9mIHRoZXNlIHZhcmlhYmxlczogICANClNhbGFyeSwgQ2hpbGRyZW4sIFByZXZpb3VzLlNwZW50LCBDYXRhbG9ncywgbG9nQW10LiAgDQoNCihiKSBMb29rIGF0IHRoZSBzY2F0dGVycGxvdCBiZXR3ZWVuICoqUHJldmlvdXMuU3BlbnQqKiBhbmQgKipsb2dBbXQqKi4gTm90aWNlIHRoZXJlIGFyZSBtYW55IHplcm8gdmFsdWVzIGZvciBQcmV2aW91cy5TcGVudC4gIElmIFByZXZpb3VzLkN1c3RvbWVyID0gTm8sIHRoaXMgbWVhbnMgdGhlIGN1c3RvbWVyIGRpZCBub3QgbWFrZSBhbnkgcHVyY2hhc2VzIHRoZSBwcmV2aW91cyB5ZWFyLCBzbyBQcmV2aW91cy5TcGVudCB2YWx1ZSBtdXN0IGJlIDAuIFZlcmlmeSB0aGF0IHRoZSBjdXN0b21lcnMgZm9yIHdoaWNoIFByZXZpb3VzLkN1c3RvbWVyID0gTm8gaGF2ZSBhbGwgMCB2YWx1ZXMgZm9yIFByZXZpb3VzLlNwZW50LiANCg0KKGMpIEZvciBjdXN0b21lcnMgd2hvIGFyZSBub3QgcHJldmlvdXMgY3VzdG9tZXJzLCBpdCBtYWtlcyBubyBzZW5zZSB0byB1c2UgUHJldmlvdXMuU3BlbnQgYXMgYSBwcmVkaWN0b3IuICBTbyB3ZSB3aWxsIHRyeSBidWlsZGluZyBzZXBhcmF0ZSByZWdyZXNzaW9uIG1vZGVscyBmb3IgdGhlIGN1c3RvbWVycyB3aG8gYXJlIHByZXZpb3VzIGN1c3RvbWVycyBhbmQgdGhvc2Ugd2hvIGFyZSBub3QuICBDcmVhdGUgMiBkYXRhIGZyYW1lcyBmcm9tIHRyYWluIGFzIGZvbGxvd3M6ICAgDQp0cmFpblllcyA9IHN1YnNldCBvZiB0cmFpbiBmb3Igd2hpY2ggUHJldmlvdXMuQ3VzdG9tZXIgPT0gWWVzICANCnRyYWluTm8gPSBzdWJzZXQgb2YgdHJhaW4gZm9yIHdoaWNoIFByZXZpb3VzLkN1c3RvbWVyID09IE5vDQoNCihkKSBOb3cgYnVpbGQgc2VwYXJhdGUgc2NhdHRlcnBsb3QgbWF0cmljZXMgZm9yIHRyYWluWWVzIGFuZCB0cmFpbk5vIGRhdGEgZnJhbWVzLiBGb3IgdHJhaW5ObywgUHJldmlvdXMuU3BlbnQgd2lsbCBhbGwgYmUgMCwgc28gd2UgZG9uJ3Qgd2FudCB0byBpbmNsdWRlIHRoYXQgdmFyaWFibGUuICANCkZvciB0cmFpbk5vLCB0aGUgdmFyaWFibGVzIHNob3VsZCBiZSBTYWxhcnksIENoaWxkcmVuLCBDYXRhbG9ncywgbG9nQW10LiAgDQpGb3IgdHJhaW5ZZXMsIHRoZSB2YXJpYWJsZXMgc2hvdWxkIGJlIFNhbGFyeSwgQ2hpbGRyZW4sIFByZXZpb3VzLlNwZW50LCBDYXRhbG9ncywgbG9nQW10Lg0KDQooZSkgRm9yIHRyYWluWWVzLCB0cnkgcnVubmluZyBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHdpdGggeSA9IGxvZ0FtdCBhbmQgWCA9IENsb3NlLCBTYWxhcnksIENoaWxkcmVuLCBQcmV2aW91cy5TcGVudCBhbmQgQ2F0YWxvZ3MuIExldCdzIGNhbGwgdGhpcyBtb2RlbDEuIEFyZSBhbGwgdGhlIHggdmFyaWFibGVzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQ/DQoNCihmKSBOb3csIHRlc3QgZm9yIG11bHRpY29sbGluZWFyaXR5IHdpdGggdmlmIGZ1bmN0aW9uIGZyb20gdGhlIGNhciBwYWNrYWdlLg0KV2hpY2ggdHdvIHZhcmlhYmxlKHMpIGhhdmUgdmlmIHZhbHVlIGhpZ2hlciB0aGFuIDU/DQoNCihnKSBMZXQncyB0cnkgcmVtb3Zpbmcgb25lIG9mIHRoZXNlIHR3byB2YXJpYWJsZXMuIFJ1biAyIHJlZ3Jlc3Npb25zOiAgIA0KKiptb2RlbDIqKiB3aXRoIHggPSBDbG9zZSwgU2FsYXJ5LCBDaGlsZHJlbiwgQ2F0YWxvZ3MgYW5kICAgDQoqKm1vZGVsMyoqIHdpdGggeCA9IENsb3NlLCBDaGlsZHJlbiwgUHJldmlvdXMuU3BlbnQgYW5kIENhdGFsb2dzLiAgICANCkNvbXBhcmUgdGhlIFJNU0UgYW5kIEFkaiBSLXNxdWFyZWQgb2YgdGhlIHR3byByZWdyZXNzaW9uIG1vZGVscy4NCkJhc2VkIG9uIHRoZXNlLCB3aGljaCBvbmUgc2hvdWxkIHlvdSBrZWVwPw0KDQooaCkgTm93LCBsZXQncyBkZWNpZGUgb24gYSByZWdyZXNzaW9uIG1vZGVsIGZvciAqKnRyYWluTm8qKiBkYXRhIGZyYW1lLiBUaGVzZSBjdXN0b21lcnMgd2VyZSBub3QgcHJldmlvdXMgY3VzdG9tZXJzLCBzbyB3ZSB3aWxsIG5vdCBpbmNsdWRlIHRoZSBQcmV2aW91cy5TcGVudCB2YXJpYWJsZS4gIEZvciB0cmFpbk5vLCBydW4gYSByZWdyZXNzaW9uIG1vZGVsIHdpdGggeSA9IGxvZ0FtdCBhbmQgeCA9IENsb3NlLCBTYWxhcnksIENoaWxkcmVuLCBDYXRhbG9ncy4gQ2FsbCB0aGlzIG1vZGVsICoqbW9kZWw0KiouDQoNCihpKSBUaGVyZSB3aWxsIGJlIG9uZSB2YXJpYWJsZSB0aGF0IGlzIG5vdCBzaWduaWZpY2FudC4gUmVtb3ZlIHRoaXMgdmFyaWFibGUgYW5kIHJ1biByZWdyZXNzaW9uIGFnYWluLiAgQ2FsbCB0aGlzICoqbW9kZWw1KiouDQoNCihqKSBSdW4gdGhlIHByZWRpY3QgZnVuY3Rpb24gd2l0aCBtb2RlbDIgb24gdHJhaW5ZZXMgYW5kIG1vZGVsNSBvbiB0cmFpbk5vLiBDYWxsIHRoZSByZXN1bHRzICoqcHJlZFRyYWluWWVzKiogYW5kICoqcHJlZFRyYWluTm8qKiwgcmVzcGVjdGl2ZWx5LiAgV2hhdCBhcmUgdGhlIHJtc2UncyBmcm9tIHRoZXNlIChyZW1lbWJlciB0aGlzIGZ1bmN0aW9uIGNvbWVzIGZyb20gTWV0cmljcyBwYWNrYWdlKT8gIERvbid0IGZvcmdldCAtIHNpbmNlIHRoZSBwcmVkaWN0aW9ucyBhcmUgZm9yIGxvZ0FtdCwgdXNlIGV4cCB0byBnZXQgdGhlIHByZWRpY3RlZCBBbW91bnQuU3BlbnQuIA0KDQooaykgU28gZmFyLCB3ZSBvYnRhaW5lZCB0aGUgUk1TRSdzIHNlcGFyYXRlbHkgZnJvbSBydW5uaW5nIGRpZmZlcmVudCBtb2RlbHMgb24gdHJhaW5ZZXMgYW5kIHRyYWluTm8sIHRoZSBzdWJzZXRzIG9mIHRyYWluIGRhdGEuIFJ1biB0aGUgZm9sbG93aW5nIGNvZGUgdG8gY29tYmluZSB0aGVtIHRvZ2V0aGVyIHRvIGZpbmQgdGhlIG92ZXJhbGwgUk1TRS4gDQoNCmBgYHtyfQ0KIyBBZGQgcHJlZGljdGVkIEFtb3VudC5TcGVudCBjb2x1bW4gdG8gdGhlIHR3byBkYXRhIGZyYW1lcy4NCnRyYWluWWVzMSA8LSBjYmluZCh0cmFpblllcywgcHJlZC5BbW91bnQgPSBleHAocHJlZFRyYWluWWVzKSkgIA0KdHJhaW5ObzEgPC0gY2JpbmQodHJhaW5ObywgcHJlZC5BbW91bnQgPSBleHAocHJlZFRyYWluTm8pKQ0KDQojIENvbWJpbmUgdGhlIHR3byBkYXRhIGZyYW1lcyB0byBtYWtlIG9uZSBkYXRhIGZyYW1lIHRoZW4gZ2V0IHRoZSBybXNlIGZyb20gaXQuDQp0cmFpbkNvbWJpbmVkIDwtIHJiaW5kKHRyYWluWWVzMSwgdHJhaW5ObzEpDQpybXNlKHRyYWluQ29tYmluZWQkQW1vdW50LlNwZW50LCB0cmFpbkNvbWJpbmVkJHByZWQuQW1vdW50KQ0KYGBgDQoNCihsKSBOb3cgbGV0J3MgdXNlIG1vZGVsMiBhbmQgbW9kZWw1IG9uIHRoZSBhcHByb3ByaWF0ZSByb3dzIG9mIHRoZSB0ZXN0IGRhdGEuIEJ1aWxkIDIgc21hbGxlciBkYXRhIGZyYW1lcyBmcm9tIHRoZSB0ZXN0IGRhdGEgYXMgeW91IGRpZCB3aXRoIHRyYWluIGRhdGEgaW4gKGMpLiBUaGV5IHNob3VsZCBiZSBuYW1lZCAqKnRlc3RObyoqIGFuZCAqKnRlc3RZZXMqKi4NCg0KKG0pIFJlcGVhdCB0aGUgcHJldmlvdXMgdHdvIHBhcnRzIChqKSBhbmQgKGspIG9uIHRoZXNlIGRhdGEgZnJhbWVzIHRlc3RZZXMgYW5kIHRlc3RObyB0byBmaW5kIHRoZSAyIHNlcGFyYXRlIFJNU0UncyB0aGVuIHRoZSBvdmVyYWxsIFJNU0UuDQoNCihuKSBSZW1lbWJlciB0aGUgUk1TRSBvZiB0aGUgcHJldmlvdXMgbW9kZWwgd2VyZSA2MDYuMzEgKHRyYWluKSBhbmQgNjI2LjY3ICh0ZXN0KS4gQ29tcGFyZSB0aGVzZSB0byB0aGUgKG92ZXJhbGwpIFJNU0Ugbm93IGZyb20gdGhlIHRyYWluIChmcm9tIChrKSkgYW5kIHRoZSB0ZXN0IGRhdGEgKGZyb20gKG0pKS4gQXJlIHRoZSBuZXcgUk1TRSdzIGJldHRlciBvciB3b3JzZSBmb3IgdGhlIHRyYWluIGRhdGE/IFdoYXQgYWJvdXQgZm9yIHRoZSB0ZXN0IGRhdGE/DQoNCg0KIyMjIDIuCUxlbmRpbmcgQ2x1Yg0KSW4gdGhlIGxlbmRpbmcgaW5kdXN0cnksIGludmVzdG9ycyBwcm92aWRlIGxvYW5zIHRvIGJvcnJvd2VycyBpbiBleGNoYW5nZSBmb3IgdGhlIHByb21pc2Ugb2YgcmVwYXltZW50IHdpdGggaW50ZXJlc3QuIElmIHRoZSBib3Jyb3dlciByZXBheXMgdGhlIGxvYW4sIHRoZW4gdGhlIGxlbmRlciBwcm9maXRzIGZyb20gdGhlIGludGVyZXN0LiBIb3dldmVyLCBpZiB0aGUgYm9ycm93ZXIgaXMgdW5hYmxlIHRvIHJlcGF5IHRoZSBsb2FuIChkZWZhdWx0KSwgdGhlbiB0aGUgbGVuZGVyIGxvc2VzIG1vbmV5LiBUaGVyZWZvcmUsIGxlbmRlcnMgZmFjZSB0aGUgcHJvYmxlbSBvZiBwcmVkaWN0aW5nIHRoZSByaXNrIG9mIGEgYm9ycm93ZXIgYmVpbmcgdW5hYmxlIHRvIHJlcGF5IGEgbG9hbi4gIA0KKipMZW5kaW5nQ2x1Yi5jc3YqKiBjb250YWlucyBkYXRhIGZyb20gW2xlbmRpbmdjbHViLmNvbV0oaHR0cHM6Ly93d3cubGVuZGluZ2NsdWIuY29tLyksIGEgd2Vic2l0ZSB0aGF0IGNvbm5lY3RzIGJvcnJvd2VycyBhbmQgaW52ZXN0b3JzIG92ZXIgdGhlIEludGVybmV0LiBUaGlzIGRhdGFzZXQgcmVwcmVzZW50cyBhIHNhbXBsZSBvZiBsb2FucyB0aGF0IHdlcmUgZnVuZGVkIHRocm91Z2ggdGhlIGxlbmRpbmdjbHViLmNvbSBwbGF0Zm9ybSBpbiAyMDA3IC0gMjAxNS4gVGhlIGRlZmluaXRpb24gb2YgdGhlIGNvbHVtbnMgYXJlIGluIHRoZSBmaWxlICoqTGVuZGluZ0NsdWJfRGF0YURlc2NyaXB0aW9uLmNzdi4qKiAgIA0KDQpIZXJlLCB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGlzICoqbG9hbl9zdGF0dXMqKi4gICANCkJhc2VkIG9uIHRoaXMgZGF0YSwgd2Ugd291bGQgbGlrZSB0byBidWlsZCBhIG1vZGVsIHRoYXQgcHJlZGljdHMgd2hpY2ggYm9ycm93ZXJzIHdpbGwgcGF5IGJhY2sgdGhlIGZ1bGwgbG9hbiwgdGhhdCBpcyBsb2FuX3N0YXR1cyA9IDEuIFdlIHVzZSBsb2dpc3RpYyByZWdyZXNzaW9uIHRoYXQgY29tcHV0ZXMgZm9yIGVhY2ggbG9hbiB0aGUgcHJvYmFiaWxpdHkgdGhhdCBpdCB3aWxsIGJlIHBhaWQgaW4gZnVsbC4gDQoNCihhKQlSZWFkIHRoZSBkYXRhIGludG8gYSBkYXRhIGZyYW1lICoqbGVuZGluZyoqIGFuZCBydW4gc3RyKCkuICBIb3cgbWFueSBvYnNlcnZhdGlvbnMgYXJlIGluIHRoaXMgZGF0YSBzZXQ/DQoNCihiKSBIb3cgbWFueSBsb2FucyB3ZXJlIHBhaWQgaW4gZnVsbD8gV2hhdCBwcm9wb3J0aW9uIGlzIHRoYXQ/DQoNCihjKQlDcmVhdGUgYSB0cmFpbmluZyBkYXRhIGFuZCB0aGUgdGVzdCBkYXRhIGFzIGZvbGxvd3MuIExvYWQgdGhlIHBhY2thZ2UgY2FUb29scywgc2V0IHRoZSBzZWVkIHRvIDIwLiAgVXNlIHNhbXBsZS5zcGxpdCB0byByYW5kb21seSBhbGxvY2F0ZSA4MCAlIG9mIHRoZSByb3dzIHRvIGRhdGEgZnJhbWUsICoqdHJhaW4qKiwgYW5kIHRoZSByZW1haW5pbmcgMjAlIHRvIGRhdGEgZnJhbWUsICoqdGVzdCoqLiAgUmVjYWxsIHVzaW5nIHNhbXBsZS5zcGxpdCBmdW5jdGlvbiB3aXRoIGxlbmRpbmckbG9hbl9zdGF0dXMgd2lsbCBlbnN1cmUgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGxvYW5fc3RhdHVzIGlzIHNpbWlsYXIgYmV0d2VlbiB0aGUgdHJhaW5pbmcgc2V0IGFuZCB0aGUgdGVzdCBzZXQuICAoVXNlIHRoZSBzZXQgb2YgY29tbWFuZHMgd2Ugd2VudCBvdmVyIGluIGNsYXNzLikgICBXaGF0IHByb3BvcnRpb24gb2YgbG9hbnMgd2VyZSBwYWlkIGZ1bGwgaW4gdHJhaW4/IFdoYXQgYWJvdXQgaW4gdGVzdD8NCg0KKGQpCUxldCdzIHdvcmsgd2l0aCB0aGUgKip0cmFpbioqIGRhdGEgZmlyc3QuICAoQnVpbGQgYSBsb2dpc3RpYyByZWdyZXNzaW9uIHdpdGggdGhlIGZvbGxvd2luZyBhcyB0aGUgcHJlZGljdG9yczoNCmxvZyhhbm51YWxfaW5jKSwgZGVsaW5xXzJ5cnMsIGR0aSwgaW50X3JhdGUsIGxvYW5fYW1udC4gIEhlcmUsIGxvZyBpcyB0aGUgbmF0dXJhbCBsb2cgb2YgdGhlIGFubnVhbCBpbmNvbWUuICBXZSBhcmUgdXNpbmcgdGhlIGxvZyBiZWNhdXNlIGFubnVhbCBpbmNvbWUgaXMgaGlnaGx5IHJpZ2h0LXNrZXdlZC4gW0lmIHlvdSBhcmUgYW5ub3llZCB3aXRoIGFsbCB0aGUgc2NpZW50aWZpYyBub3RhdGlvbnMgbGlrZSAtMS41MDdlLTA1IGluIHRoZSBvdXRwdXQsIHlvdSBjYW4gc3VwcHJlc3MgdGhlIHNjaWVudGlmaWMgbm90YXRpb24gYnkgcnVubmluZyB0aGlzIGNvbW1hbmQgYmVmb3JlIGFza2luZyBmb3IgdGhlIG91dHB1dDogIG9wdGlvbnMoc2NpcGVuID0gOTk5KS4gIE5vdyBhbGwgdGhlIG91dHB1dHMgZnJvbSB0aGF0IHBvaW50IG9uIHdpbGwgbm90IGhhdmUgc2NpZW50aWZpYyBub3RhdGlvbi4gIElmIHlvdSB3YW50IHRvIHVuZG8gdGhpcyBsYXRlciBkdXJpbmcgdGhlIHNlc3Npb24sIHVzZSBjb21tYW5kOiAgb3B0aW9ucyhzY2lwZW4gPSAwKS5dDQoNCihlKQlBY2NvcmRpbmcgdG8gdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzLCBpbmNyZWFzaW5nIHdoaWNoIHZhcmlhYmxlcyB3aWxsIGluY3JlYXNlIHRoZSBwcm9iYWJpbGl0eSBvZiB0aGUgbG9hbiBiZWluZyBwYWlkIGluIGZ1bGw/ICBEb2VzIHRoaXMgbWFrZSBzZW5zZT8NCg0KKGYpCVdyaXRlIGRvd24gdGhlIGV4cHJlc3Npb24gZm9yIGxvZ2l0LCB6LiAoVGhpcyBraW5kIGZvcm1hdDogIHogPSAyLjU2NyArIDAuNDM1IHggLSAxLjM0NSB5KSANCg0KKGcpCVN1cHBvc2UgeW91IGhhdmUgdGhlIGZvbGxvd2luZyBkYXRhIGFib3V0IGJvcnJvd2VyIEEuIEFubnVhbCBpbmNvbWUgaXMgJDczLDUwMCwgZGVidC10by1pbmNvbWUgcmF0aW8gaXMgMTMuMCwgdGhlcmUgd2FzIG9uZSBkZWxpbnF1ZW5jeSBpbiB0aGUgcGFzdCB0d28geWVhcnMsIHRoZSBsb2FuIGFtb3VudCBoZSB3YW50cyBpcyAkMjAsMDAwIHdpdGggdGhlIGludGVyZXN0IHJhdGUgOCUuICBXaGF0IGlzIHRoZSBwcm9iYWJpbGl0eSBib3Jyb3dlciBBIHdpbGwgcGF5IGJhY2sgdGhlIGxvYW4gaW4gZnVsbD8gIChDb21wdXRlIGJ5IHBsdWdnaW5nIGluIHZhcmlhYmxlIHZhbHVlcyBpbiB0aGUgcmVncmVzc2lvbiB0byBnZXQgeiB0aGVuIHBsdWdnaW5nIHRoaXMgeiB2YWx1ZSBpbnRvIHRoZSBleHByZXNzaW9uIDEvKDErZXhwKC16KSkuICBXaGF0IGFyZSB0aGUgb2RkcyBvZiBoaW0gcGF5aW5nIGJhY2sgaW4gZnVsbD8gV291bGQgeW91IGxlbmQgdG8gdGhpcyBib3Jyb3dlcj8gICANCg0KKGgpCU5vdyBjb21wdXRlIHRoZSBwcm9iYWJpbGl0eSBkaXJlY3RseSB3aXRoIHRoZSBwcmVkaWN0IGZ1bmN0aW9uLiBZb3Ugc2hvdWxkIGdldCB0aGUgc2FtZSBwcm9iYWJpbGl0eSB5b3UgY29tcHV0ZWQgYWJvdmUuDQoNCihpKQlGb3IgYWxsIHRoZSBvYnNlcnZhdGlvbnMgaW4gdGhlIHRyYWluIGRhdGEgc2V0LCB1c2UgKipwcmVkaWN0KiogZnVuY3Rpb24gdG8gY29tcHV0ZSB0aGUgcHJvYmFiaWxpdGllcyBvZiBwYXlpbmcgYmFjayBpbiBmdWxsLiBOYW1lIHRoZSByZXN1bHQgKipwcmVkVHJhaW4qKi4gU2luY2UgeW91IGFyZSB1c2luZyB0aGUgdHJhaW4gZGF0YSB0aGF0IHRoZSBtb2RlbCBjYW1lIGZyb20sIHlvdSBkb24ndCBuZWVkIHRvIHNwZWNpZnkgbmV3ZGF0YSBpbiB0aGUgcHJlZGljdCBmdW5jdGlvbi4gIFZlcmlmeSB0aGlzIGlzIGEgdmVjdG9yIDksMjQ5IHByb2JhYmlsaXR5IHZhbHVlcy4NCg0KKGopIE5vdyBmb3IgYWxsIHRoZSBvYnNlcnZhdGlvbnMgaW4gdGhlICoqdGVzdCoqIGRhdGEgc2V0LCB1c2UgKipwcmVkaWN0KiogZnVuY3Rpb24gdG8gY29tcHV0ZSB0aGUgcHJvYmFiaWxpdGllcyBvZiBwYXlpbmcgYmFjayBpbiBmdWxsLiBOYW1lIHRoZSByZXN1bHQgcHJlZFRlc3QuIERpc3BsYXkgdGhlIGZpcnN0IDEwIHZhbHVlcy4=