About
The best way to create a predictive model is to begin with a complete data set. Split the data into two seperate datasets: the training set and the testing set, with the training set greater than or equal to 50% of the data. We will use the training set to build a model best predictor of the data. Lastly, we will test the built model on the testing data set, and assess how good the model is in predicting the actual values.
The concept may seem overwhelming, but we already have almost all of the tools to perform this analysis.
Setup
Remember to always set your working directory to the source file location. Go to ‘Session’, scroll down to ‘Set Working Directory’, and click ‘To Source File Location’. Read carefully the below and follow the instructions to complete the tasks and answer any questions. Submit your work to RPubs as detailed in previous notes.
Note
For your assignment you may be using different data sets than what is included here. Always read carefully the instructions on Sakai. For clarity, tasks/questions to be completed/answered are highlighted in red color (visible in preview) and numbered according to their particular placement in the task section. Quite often you will need to add your own code chunk.
Execute all code chunks, preview, publish, and submit link on Sakai.
Task 1: Training Set & Model Building
The first half of this lab focuses on building a model using the training data. The data we will be using was obtained from Kaggle site[1] and is in reference to world university rankings [2] . The data looks at university world scoring based on different ranking criteria such as quality of education, quality of faculty, and rank for patents. Spend some time to visit the referenced website to get more acquinated with the data. The data obtained is divided inteo two sets: a training set and a testing set. We begin by reading the training data set ‘universityrank_training.csv’ file, and checking the header lines to make sure the data is read correctly.
traindata = read.csv(file="data/universityrank_training.csv", header=TRUE)
head(traindata)
Next, we extract the two columns of interest and call them properly so we can easily refer to them later in the code.
patent_train = traindata$patents
score_train = traindata$score
The first model we will build is a simple linear model. We will use the patents ranking variable to predict the university score. To better understand the data, the lower the patents ranking number the better it is. A value of 1 is a top rank for patents and represent the highest category in terms of number of patents owned by the particular academic institution. On the other hand the higher the calculated total score the better, as reflected by the world rank number. A value of 100 is a perfect score.
linear_train = lm(score_train ~ patent_train)
summary(linear_train)
Call:
lm(formula = score_train ~ patent_train)
Residuals:
Min 1Q Median 3Q Max
-9.876 -4.010 -1.118 1.512 45.471
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 54.6397362 0.3857798 141.63 <2e-16 ***
patent_train -0.0157558 0.0008281 -19.03 <2e-16 ***
---
Signif. codes: 0 *** 0.001 ** 0.01 * 0.05 . 0.1 1
Residual standard error: 7.535 on 1198 degrees of freedom
Multiple R-squared: 0.2321, Adjusted R-squared: 0.2314
F-statistic: 362 on 1 and 1198 DF, p-value: < 2.2e-16
plot(patent_train,score_train)
abline(linear_train, col="blue", lwd=2)

##### 1A) Fill-in the code chunk below to build a non-linear quadratic model. Follow the steps in lab07 to derive a quadratic model similar to what we did for costs versus servers.
# First define a new variable which is the squared value of patent_train (defined above)
patent_train2 = patent_train^2
# Next derive the quadratic regression model. You may want to call it quad_train.
quad_train = lm(score_train ~ patent_train + patent_train2)
# Publish the summary statistics
summary(quad_train)
Call:
lm(formula = score_train ~ patent_train + patent_train2)
Residuals:
Min 1Q Median 3Q Max
-13.555 -2.345 -0.582 1.302 40.843
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 5.966e+01 4.971e-01 120.02 <2e-16 ***
patent_train -6.249e-02 3.319e-03 -18.83 <2e-16 ***
patent_train2 5.972e-05 4.127e-06 14.47 <2e-16 ***
---
Signif. codes: 0 *** 0.001 ** 0.01 * 0.05 . 0.1 1
Residual standard error: 6.955 on 1197 degrees of freedom
Multiple R-squared: 0.3464, Adjusted R-squared: 0.3453
F-statistic: 317.2 on 2 and 1197 DF, p-value: < 2.2e-16
##### 1B) Based on the values of R-Squared and Adj R-Squared for both the linear and the quadratic select the best predictive model. Explain your argument. According to the Adj R-Squared, the quadratic model is the best predictive model because it has higher AdJ R-Squared value compare with the linear model.
Task 2: Testing Set & Model Evaluation
The second half of predictive modeling is about testing the model using a different data set called the testing data. Again we must first read the testing data set, and make sure the dataset is read propertly.
testdata = read.csv("data/universityrank_testing.csv", header=TRUE)
head(testdata)
We extract again the two columns of interest, in reference this time to the testing data set, and call them accordingly.
patent_test = testdata$patents
score_test = testdata$score
We are ready now to check if the derived models are actually good predictive models. First we calculate the predicted test data score using the linear model. Later we will consider the quadratic model derived earlier.
# Calculate the predicted test data score
score_predict1 = coef(linear_train)[1] + coef(linear_train)[2]*patent_test
For a visual representation we can plot the actual testing data, and overlay the predicted values
# Plot the actual values for patent and score as observed in the testing data set
plot(patent_test, score_test, main='Test Data -- Score vs Patent')
# Overlay the predicted values as calculated from the linear model and derived using the training model
par(new=TRUE, xaxt="n", yaxt="n", ann=FALSE)
# The red color is used to distinguish the predicted values which, because of the linear model, will fit exactly a line
plot(patent_test, score_predict1, col="red")

A better way to qualify the goodness of a predictive model is to scatter plot the actual values against the predicted values. In a perfect predictive model the points will line up along the diagonal line. This is rarely the case, if ever!
#Plot predicted values from the linear model versus actual values form the test data
plot(score_test, score_predict1, xlab='Actual', ylab='Predict', main='Linear Model -- Predict vs Actual Test')

From the plot we can easily see that most of the predicted values versus actual are far from the diagoonal line. In many cases this is fine. Finally, to quantify the goodness of a model, we need to calculate the Root Mean Square Error (RMSE).
#Calculate RMSE for Linear Model
error1 = sum((score_predict1 - score_test)^2)/length(score_test)
rmse1 = sqrt(error1)
rmse1
[1] 5.95868
It is hard to judge the goodness of the number unless we compare to other possibilities. Of course a perfect scenario will have zero RMSE. We now need to repeat the above calculations for the non-linear quadratic model.
##### 2A) Fill in the code chunk below to calculate the predicted values for the non-linear quadratic model
# Calculate score_predict2 based on the quadratic model and the patent test data. You need to refer again to the coefficients of the quadratic model derived earlier and the actual patent values obtained from the testing data
score_predict2 = coef(quad_train)[1] + coef(quad_train)[2]*patent_test + coef(quad_train)[3]*patent_test^2
For a visual representation, similar to the linear model, we need to do the following.
##### 2B) Fill-in the code chunk below to plot the Score vs Patent for the test data, and overlay the predicted values as calculated in 2A. Label axes and title properly.
# Plot the actual values for patent and score as observed in the testing data set
plot(patent_test, score_test, main='Test Data -- Score vs Patent')
# Overlay the predicted values as calculated from the linear model and derived using the training model
par(new=TRUE, xaxt="n", yaxt="n", ann=FALSE)
# The green color is used to distinguish the predicted values which, because of the quadratic model, will in this case fit exactly a parabola
plot(patent_test, score_predict2, col="green")

NA
#### 2C) Plot the Predict vs Actual for the quadratic model. Label axes and tile properly.
#Plot the predicted values form the quadratic model versus the actual values from the test data
plot(score_test, score_predict2, xlab='Actual', ylab='Predict', main='Quadratic Model -- Predict vs Actual Test')

#### 2D) By looking at the scatterplots for the linear and quadratic models are you able to tell which model is better? Elaborate.
According to those two models, the quadratic models are the better one because it has more data in the lower left corner which means it closer to the diagonal line than liner model.
A better way is to quantify the goodness of the model by calculating again the RMSE.
#### 2E) Calculate the root mean square error (RMSE) for the quadratic model.
#Calculate RMSE for Quadratic Model
error2 = sum((score_predict2 - score_test)^2)/length(score_test)
rmse2 = sqrt(error2)
rmse2
[1] 5.685396
#### 2F) Based on the root mean square error (RMSE) which model is better? How do results reconcile with the results from Task 1? linear model’s RMSE is 5.956 Quadratic model’s RMSE is 5.685
Base one the RMSE, the quadratic model, is better. This result is related to the Task 2 that quadratic model has higher ADJ R-squared value than liner model.
source [1]: http://www.kaggle.com
source [2]: http://www.cwur.org
LS0tDQp0aXRsZTogIkJTQUQzNDMgRmFsbCAyMDE4IExhYiBXb3Jrc2hlZXQgMDkiDQphdXRob3I6ICJZSSBQQU4iDQpkYXRlOiAiMTEtMjQtMjAxOCINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBodG1sX2RvY3VtZW50OiBkZWZhdWx0DQpzdWJ0aXRsZTogTW9kZWwgQnVpbGRpbmcgJiBFdmFsdWF0aW9uIChic2FkLWxhYjA5KQ0KLS0tDQoNCiMjIyBBYm91dA0KDQpUaGUgYmVzdCB3YXkgdG8gY3JlYXRlIGEgcHJlZGljdGl2ZSBtb2RlbCBpcyB0byBiZWdpbiB3aXRoIGEgY29tcGxldGUgZGF0YSBzZXQuIFNwbGl0IHRoZSBkYXRhIGludG8gdHdvIHNlcGVyYXRlIGRhdGFzZXRzOiB0aGUgdHJhaW5pbmcgc2V0IGFuZCB0aGUgdGVzdGluZyBzZXQsIHdpdGggdGhlIHRyYWluaW5nIHNldCBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gNTAlIG9mIHRoZSBkYXRhLiBXZSB3aWxsIHVzZSB0aGUgdHJhaW5pbmcgc2V0IHRvIGJ1aWxkIGEgbW9kZWwgYmVzdCBwcmVkaWN0b3Igb2YgdGhlIGRhdGEuIExhc3RseSwgd2Ugd2lsbCB0ZXN0IHRoZSBidWlsdCBtb2RlbCBvbiB0aGUgdGVzdGluZyBkYXRhIHNldCwgYW5kIGFzc2VzcyBob3cgZ29vZCB0aGUgbW9kZWwgaXMgaW4gcHJlZGljdGluZyB0aGUgYWN0dWFsIHZhbHVlcy4NCg0KVGhlIGNvbmNlcHQgbWF5IHNlZW0gb3ZlcndoZWxtaW5nLCBidXQgd2UgYWxyZWFkeSBoYXZlIGFsbW9zdCBhbGwgb2YgdGhlIHRvb2xzIHRvIHBlcmZvcm0gdGhpcyBhbmFseXNpcy4gDQoNCiMjIyBTZXR1cA0KDQpSZW1lbWJlciB0byBhbHdheXMgc2V0IHlvdXIgd29ya2luZyBkaXJlY3RvcnkgdG8gdGhlIHNvdXJjZSBmaWxlIGxvY2F0aW9uLiBHbyB0byAnU2Vzc2lvbicsIHNjcm9sbCBkb3duIHRvICdTZXQgV29ya2luZyBEaXJlY3RvcnknLCBhbmQgY2xpY2sgJ1RvIFNvdXJjZSBGaWxlIExvY2F0aW9uJy4gUmVhZCBjYXJlZnVsbHkgdGhlIGJlbG93IGFuZCBmb2xsb3cgdGhlIGluc3RydWN0aW9ucyB0byBjb21wbGV0ZSB0aGUgdGFza3MgYW5kIGFuc3dlciBhbnkgcXVlc3Rpb25zLiAgU3VibWl0IHlvdXIgd29yayB0byBSUHVicyBhcyBkZXRhaWxlZCBpbiBwcmV2aW91cyBub3Rlcy4gDQoNCiMjIyBOb3RlDQoNCkZvciB5b3VyIGFzc2lnbm1lbnQgeW91IG1heSBiZSB1c2luZyBkaWZmZXJlbnQgZGF0YSBzZXRzIHRoYW4gd2hhdCBpcyBpbmNsdWRlZCBoZXJlLiBBbHdheXMgcmVhZCBjYXJlZnVsbHkgdGhlIGluc3RydWN0aW9ucyBvbiBTYWthaS4gIEZvciBjbGFyaXR5LCB0YXNrcy9xdWVzdGlvbnMgdG8gYmUgY29tcGxldGVkL2Fuc3dlcmVkIGFyZSBoaWdobGlnaHRlZCBpbiByZWQgY29sb3IgKHZpc2libGUgaW4gcHJldmlldykgYW5kIG51bWJlcmVkIGFjY29yZGluZyB0byB0aGVpciBwYXJ0aWN1bGFyIHBsYWNlbWVudCBpbiB0aGUgdGFzayBzZWN0aW9uLiAgUXVpdGUgb2Z0ZW4geW91IHdpbGwgbmVlZCB0byBhZGQgeW91ciBvd24gY29kZSBjaHVuay4NCg0KRXhlY3V0ZSBhbGwgY29kZSBjaHVua3MsIHByZXZpZXcsIHB1Ymxpc2gsIGFuZCBzdWJtaXQgbGluayBvbiBTYWthaS4NCg0KLS0tLS0tLS0tLS0tLS0NCg0KIyMjIFRhc2sgMTogVHJhaW5pbmcgU2V0ICYgTW9kZWwgQnVpbGRpbmcNCg0KVGhlIGZpcnN0IGhhbGYgb2YgdGhpcyBsYWIgZm9jdXNlcyBvbiBidWlsZGluZyBhIG1vZGVsIHVzaW5nIHRoZSB0cmFpbmluZyBkYXRhLiBUaGUgZGF0YSB3ZSB3aWxsIGJlIHVzaW5nIHdhcyBvYnRhaW5lZCBmcm9tIEthZ2dsZSBzaXRlWzFdIGFuZCBpcyBpbiByZWZlcmVuY2UgdG8gd29ybGQgdW5pdmVyc2l0eSByYW5raW5ncyBbMl0gLiBUaGUgZGF0YSBsb29rcyBhdCB1bml2ZXJzaXR5IHdvcmxkIHNjb3JpbmcgYmFzZWQgb24gZGlmZmVyZW50IHJhbmtpbmcgY3JpdGVyaWEgc3VjaCBhcyBxdWFsaXR5IG9mIGVkdWNhdGlvbiwgcXVhbGl0eSBvZiBmYWN1bHR5LCBhbmQgcmFuayBmb3IgcGF0ZW50cy4gU3BlbmQgc29tZSB0aW1lIHRvIHZpc2l0IHRoZSByZWZlcmVuY2VkIHdlYnNpdGUgdG8gZ2V0IG1vcmUgYWNxdWluYXRlZCB3aXRoIHRoZSBkYXRhLiAgVGhlIGRhdGEgb2J0YWluZWQgaXMgZGl2aWRlZCBpbnRlbyB0d28gc2V0czogYSB0cmFpbmluZyBzZXQgYW5kIGEgdGVzdGluZyBzZXQuIFdlIGJlZ2luIGJ5IHJlYWRpbmcgdGhlIHRyYWluaW5nIGRhdGEgc2V0ICd1bml2ZXJzaXR5cmFua190cmFpbmluZy5jc3YnIGZpbGUsIGFuZCBjaGVja2luZyB0aGUgaGVhZGVyIGxpbmVzIHRvIG1ha2Ugc3VyZSB0aGUgZGF0YSBpcyByZWFkIGNvcnJlY3RseS4gDQoNCmBgYHtyfQ0KdHJhaW5kYXRhID0gcmVhZC5jc3YoZmlsZT0iZGF0YS91bml2ZXJzaXR5cmFua190cmFpbmluZy5jc3YiLCBoZWFkZXI9VFJVRSkNCmhlYWQodHJhaW5kYXRhKQ0KYGBgDQoNCk5leHQsIHdlIGV4dHJhY3QgdGhlIHR3byBjb2x1bW5zIG9mIGludGVyZXN0IGFuZCBjYWxsIHRoZW0gcHJvcGVybHkgc28gd2UgY2FuIGVhc2lseSByZWZlciB0byB0aGVtIGxhdGVyIGluIHRoZSBjb2RlLiANCg0KYGBge3J9DQpwYXRlbnRfdHJhaW4gPSB0cmFpbmRhdGEkcGF0ZW50cw0Kc2NvcmVfdHJhaW4gPSB0cmFpbmRhdGEkc2NvcmUNCmBgYA0KDQpUaGUgZmlyc3QgbW9kZWwgd2Ugd2lsbCBidWlsZCBpcyBhIHNpbXBsZSBsaW5lYXIgbW9kZWwuIFdlIHdpbGwgdXNlIHRoZSBwYXRlbnRzIHJhbmtpbmcgdmFyaWFibGUgdG8gcHJlZGljdCB0aGUgdW5pdmVyc2l0eSBzY29yZS4gIFRvIGJldHRlciB1bmRlcnN0YW5kIHRoZSBkYXRhLCB0aGUgbG93ZXIgdGhlIHBhdGVudHMgcmFua2luZyBudW1iZXIgdGhlIGJldHRlciBpdCBpcy4gIEEgdmFsdWUgb2YgMSBpcyBhIHRvcCByYW5rIGZvciBwYXRlbnRzIGFuZCByZXByZXNlbnQgdGhlIGhpZ2hlc3QgY2F0ZWdvcnkgaW4gdGVybXMgb2YgbnVtYmVyIG9mIHBhdGVudHMgb3duZWQgYnkgdGhlIHBhcnRpY3VsYXIgYWNhZGVtaWMgaW5zdGl0dXRpb24uIE9uIHRoZSBvdGhlciBoYW5kIHRoZSBoaWdoZXIgdGhlIGNhbGN1bGF0ZWQgdG90YWwgc2NvcmUgdGhlIGJldHRlciwgYXMgcmVmbGVjdGVkICBieSB0aGUgd29ybGQgcmFuayBudW1iZXIuIEEgIHZhbHVlIG9mIDEwMCBpcyBhIHBlcmZlY3Qgc2NvcmUuICANCg0KYGBge3J9DQpsaW5lYXJfdHJhaW4gPSBsbShzY29yZV90cmFpbiB+IHBhdGVudF90cmFpbikNCnN1bW1hcnkobGluZWFyX3RyYWluKQ0KDQpwbG90KHBhdGVudF90cmFpbixzY29yZV90cmFpbikNCmFibGluZShsaW5lYXJfdHJhaW4sIGNvbD0iYmx1ZSIsIGx3ZD0yKQ0KYGBgDQoNCjxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPg0KIyMjIyMgMUEpIEZpbGwtaW4gdGhlIGNvZGUgY2h1bmsgYmVsb3cgdG8gYnVpbGQgYSBub24tbGluZWFyIHF1YWRyYXRpYyBtb2RlbC4gRm9sbG93IHRoZSBzdGVwcyBpbiBsYWIwNyB0byBkZXJpdmUgYSBxdWFkcmF0aWMgbW9kZWwgc2ltaWxhciB0byB3aGF0IHdlIGRpZCBmb3IgY29zdHMgdmVyc3VzIHNlcnZlcnMuDQo8L3NwYW4+DQoNCmBgYHtyfQ0KIyBGaXJzdCBkZWZpbmUgYSBuZXcgdmFyaWFibGUgd2hpY2ggaXMgdGhlIHNxdWFyZWQgdmFsdWUgb2YgcGF0ZW50X3RyYWluIChkZWZpbmVkIGFib3ZlKQ0KcGF0ZW50X3RyYWluMiA9IHBhdGVudF90cmFpbl4yDQoNCiMgTmV4dCBkZXJpdmUgdGhlIHF1YWRyYXRpYyByZWdyZXNzaW9uIG1vZGVsLiAgWW91IG1heSB3YW50IHRvIGNhbGwgaXQgcXVhZF90cmFpbi4NCnF1YWRfdHJhaW4gPSBsbShzY29yZV90cmFpbiB+IHBhdGVudF90cmFpbiArIHBhdGVudF90cmFpbjIpDQoNCiMgUHVibGlzaCB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzDQpzdW1tYXJ5KHF1YWRfdHJhaW4pDQpgYGANCg0KPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+DQojIyMjIyAxQikgQmFzZWQgb24gdGhlIHZhbHVlcyBvZiBSLVNxdWFyZWQgYW5kIEFkaiBSLVNxdWFyZWQgZm9yIGJvdGggdGhlIGxpbmVhciBhbmQgdGhlIHF1YWRyYXRpYyBzZWxlY3QgdGhlIGJlc3QgcHJlZGljdGl2ZSBtb2RlbC4gRXhwbGFpbiB5b3VyIGFyZ3VtZW50Lg0KPC9zcGFuPg0KQWNjb3JkaW5nIHRvIHRoZSBBZGogUi1TcXVhcmVkLCB0aGUgcXVhZHJhdGljIG1vZGVsIGlzIHRoZSBiZXN0IHByZWRpY3RpdmUgbW9kZWwgYmVjYXVzZSBpdCBoYXMgaGlnaGVyIEFkSiBSLVNxdWFyZWQgdmFsdWUgY29tcGFyZSB3aXRoIHRoZSBsaW5lYXIgbW9kZWwuDQoNCg0KLS0tLS0tLS0tLQ0KDQojIyMgVGFzayAyOiBUZXN0aW5nIFNldCAmIE1vZGVsIEV2YWx1YXRpb24NCg0KVGhlIHNlY29uZCBoYWxmIG9mIHByZWRpY3RpdmUgbW9kZWxpbmcgaXMgYWJvdXQgdGVzdGluZyB0aGUgbW9kZWwgdXNpbmcgYSBkaWZmZXJlbnQgZGF0YSBzZXQgY2FsbGVkIHRoZSB0ZXN0aW5nIGRhdGEuIEFnYWluIHdlIG11c3QgZmlyc3QgcmVhZCB0aGUgdGVzdGluZyBkYXRhIHNldCwgYW5kIG1ha2Ugc3VyZSB0aGUgZGF0YXNldCBpcyByZWFkIHByb3BlcnRseS4NCg0KYGBge3J9DQp0ZXN0ZGF0YSA9IHJlYWQuY3N2KCJkYXRhL3VuaXZlcnNpdHlyYW5rX3Rlc3RpbmcuY3N2IiwgaGVhZGVyPVRSVUUpDQpoZWFkKHRlc3RkYXRhKQ0KYGBgDQoNCldlIGV4dHJhY3QgYWdhaW4gdGhlIHR3byBjb2x1bW5zIG9mIGludGVyZXN0LCBpbiByZWZlcmVuY2UgdGhpcyB0aW1lIHRvIHRoZSB0ZXN0aW5nIGRhdGEgc2V0LCBhbmQgY2FsbCB0aGVtIGFjY29yZGluZ2x5Lg0KDQpgYGB7cn0NCnBhdGVudF90ZXN0ID0gdGVzdGRhdGEkcGF0ZW50cw0Kc2NvcmVfdGVzdCA9IHRlc3RkYXRhJHNjb3JlDQpgYGANCg0KV2UgYXJlIHJlYWR5IG5vdyB0byBjaGVjayBpZiB0aGUgZGVyaXZlZCBtb2RlbHMgYXJlIGFjdHVhbGx5IGdvb2QgcHJlZGljdGl2ZSBtb2RlbHMuIEZpcnN0IHdlIGNhbGN1bGF0ZSB0aGUgcHJlZGljdGVkIHRlc3QgZGF0YSBzY29yZSB1c2luZyB0aGUgbGluZWFyIG1vZGVsLiBMYXRlciB3ZSB3aWxsIGNvbnNpZGVyIHRoZSBxdWFkcmF0aWMgbW9kZWwgZGVyaXZlZCBlYXJsaWVyLg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSBwcmVkaWN0ZWQgdGVzdCBkYXRhIHNjb3JlIA0Kc2NvcmVfcHJlZGljdDEgPSBjb2VmKGxpbmVhcl90cmFpbilbMV0gKyBjb2VmKGxpbmVhcl90cmFpbilbMl0qcGF0ZW50X3Rlc3QNCmBgYA0KDQpGb3IgYSB2aXN1YWwgcmVwcmVzZW50YXRpb24gd2UgY2FuIHBsb3QgdGhlIGFjdHVhbCB0ZXN0aW5nIGRhdGEsIGFuZCBvdmVybGF5IHRoZSBwcmVkaWN0ZWQgdmFsdWVzDQoNCmBgYHtyfQ0KIyBQbG90IHRoZSBhY3R1YWwgdmFsdWVzIGZvciBwYXRlbnQgYW5kIHNjb3JlIGFzIG9ic2VydmVkIGluIHRoZSB0ZXN0aW5nIGRhdGEgc2V0DQpwbG90KHBhdGVudF90ZXN0LCBzY29yZV90ZXN0LCBtYWluPSdUZXN0IERhdGEgLS0gU2NvcmUgdnMgUGF0ZW50JykNCiMgT3ZlcmxheSB0aGUgcHJlZGljdGVkIHZhbHVlcyBhcyBjYWxjdWxhdGVkIGZyb20gdGhlIGxpbmVhciBtb2RlbCBhbmQgZGVyaXZlZCB1c2luZyB0aGUgdHJhaW5pbmcgbW9kZWwNCnBhcihuZXc9VFJVRSwgeGF4dD0ibiIsIHlheHQ9Im4iLCBhbm49RkFMU0UpDQojIFRoZSByZWQgY29sb3IgaXMgdXNlZCB0byBkaXN0aW5ndWlzaCB0aGUgcHJlZGljdGVkIHZhbHVlcyB3aGljaCwgYmVjYXVzZSBvZiB0aGUgbGluZWFyIG1vZGVsLCB3aWxsIGZpdCBleGFjdGx5IGEgbGluZQ0KcGxvdChwYXRlbnRfdGVzdCwgc2NvcmVfcHJlZGljdDEsIGNvbD0icmVkIikNCmBgYA0KDQpBIGJldHRlciB3YXkgdG8gcXVhbGlmeSB0aGUgZ29vZG5lc3Mgb2YgYSBwcmVkaWN0aXZlIG1vZGVsIGlzIHRvIHNjYXR0ZXIgcGxvdCB0aGUgYWN0dWFsIHZhbHVlcyBhZ2FpbnN0IHRoZSBwcmVkaWN0ZWQgdmFsdWVzLiBJbiBhIHBlcmZlY3QgcHJlZGljdGl2ZSBtb2RlbCB0aGUgcG9pbnRzIHdpbGwgbGluZSB1cCBhbG9uZyB0aGUgZGlhZ29uYWwgbGluZS4gIFRoaXMgaXMgcmFyZWx5IHRoZSBjYXNlLCBpZiBldmVyIQ0KDQpgYGB7cn0NCiNQbG90IHByZWRpY3RlZCB2YWx1ZXMgZnJvbSB0aGUgbGluZWFyIG1vZGVsIHZlcnN1cyBhY3R1YWwgdmFsdWVzIGZvcm0gdGhlIHRlc3QgZGF0YQ0KcGxvdChzY29yZV90ZXN0LCBzY29yZV9wcmVkaWN0MSwgeGxhYj0nQWN0dWFsJywgeWxhYj0nUHJlZGljdCcsIG1haW49J0xpbmVhciBNb2RlbCAtLSBQcmVkaWN0IHZzIEFjdHVhbCBUZXN0JykNCmBgYA0KDQpGcm9tIHRoZSBwbG90IHdlIGNhbiBlYXNpbHkgc2VlIHRoYXQgbW9zdCBvZiB0aGUgcHJlZGljdGVkIHZhbHVlcyB2ZXJzdXMgYWN0dWFsIGFyZSBmYXIgZnJvbSB0aGUgZGlhZ29vbmFsIGxpbmUuIEluIG1hbnkgY2FzZXMgdGhpcyBpcyBmaW5lLiBGaW5hbGx5LCB0byBxdWFudGlmeSB0aGUgZ29vZG5lc3Mgb2YgYSAgbW9kZWwsIHdlIG5lZWQgdG8gY2FsY3VsYXRlIHRoZSBSb290IE1lYW4gU3F1YXJlIEVycm9yIChSTVNFKS4NCg0KYGBge3J9DQojQ2FsY3VsYXRlIFJNU0UgZm9yIExpbmVhciBNb2RlbA0KZXJyb3IxID0gc3VtKChzY29yZV9wcmVkaWN0MSAtIHNjb3JlX3Rlc3QpXjIpL2xlbmd0aChzY29yZV90ZXN0KQ0Kcm1zZTEgPSBzcXJ0KGVycm9yMSkNCnJtc2UxDQpgYGANCg0KDQpJdCBpcyBoYXJkIHRvIGp1ZGdlIHRoZSBnb29kbmVzcyBvZiB0aGUgbnVtYmVyIHVubGVzcyB3ZSBjb21wYXJlIHRvIG90aGVyIHBvc3NpYmlsaXRpZXMuIE9mIGNvdXJzZSBhIHBlcmZlY3Qgc2NlbmFyaW8gd2lsbCBoYXZlIHplcm8gUk1TRS4gV2Ugbm93IG5lZWQgdG8gcmVwZWF0IHRoZSBhYm92ZSBjYWxjdWxhdGlvbnMgZm9yIHRoZSBub24tbGluZWFyIHF1YWRyYXRpYyBtb2RlbC4NCg0KPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+DQojIyMjIyAyQSkgRmlsbCBpbiB0aGUgY29kZSBjaHVuayBiZWxvdyB0byBjYWxjdWxhdGUgdGhlIHByZWRpY3RlZCB2YWx1ZXMgZm9yIHRoZSBub24tbGluZWFyIHF1YWRyYXRpYyBtb2RlbA0KPC9zcGFuPg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHNjb3JlX3ByZWRpY3QyIGJhc2VkIG9uIHRoZSBxdWFkcmF0aWMgbW9kZWwgYW5kIHRoZSBwYXRlbnQgdGVzdCBkYXRhLiAgWW91IG5lZWQgdG8gcmVmZXIgYWdhaW4gdG8gdGhlIGNvZWZmaWNpZW50cyBvZiB0aGUgcXVhZHJhdGljIG1vZGVsIGRlcml2ZWQgZWFybGllciBhbmQgdGhlIGFjdHVhbCBwYXRlbnQgdmFsdWVzIG9idGFpbmVkIGZyb20gdGhlIHRlc3RpbmcgZGF0YQ0Kc2NvcmVfcHJlZGljdDIgPSBjb2VmKHF1YWRfdHJhaW4pWzFdICsgY29lZihxdWFkX3RyYWluKVsyXSpwYXRlbnRfdGVzdCArIGNvZWYocXVhZF90cmFpbilbM10qcGF0ZW50X3Rlc3ReMg0KDQpgYGANCg0KRm9yIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uLCBzaW1pbGFyIHRvIHRoZSBsaW5lYXIgbW9kZWwsIHdlIG5lZWQgdG8gZG8gdGhlIGZvbGxvd2luZy4NCg0KPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+DQojIyMjIyAyQikgRmlsbC1pbiB0aGUgY29kZSBjaHVuayBiZWxvdyB0byBwbG90IHRoZSBTY29yZSB2cyBQYXRlbnQgZm9yIHRoZSB0ZXN0IGRhdGEsIGFuZCBvdmVybGF5IHRoZSBwcmVkaWN0ZWQgdmFsdWVzIGFzIGNhbGN1bGF0ZWQgaW4gMkEuIExhYmVsIGF4ZXMgYW5kIHRpdGxlIHByb3Blcmx5Lg0KPC9zcGFuPg0KDQpgYGB7cn0NCiMgUGxvdCB0aGUgYWN0dWFsIHZhbHVlcyBmb3IgcGF0ZW50IGFuZCBzY29yZSBhcyBvYnNlcnZlZCBpbiB0aGUgdGVzdGluZyBkYXRhIHNldA0KcGxvdChwYXRlbnRfdGVzdCwgc2NvcmVfdGVzdCwgbWFpbj0nVGVzdCBEYXRhIC0tIFNjb3JlIHZzIFBhdGVudCcpDQoNCiMgT3ZlcmxheSB0aGUgcHJlZGljdGVkIHZhbHVlcyBhcyBjYWxjdWxhdGVkIGZyb20gdGhlIGxpbmVhciBtb2RlbCBhbmQgZGVyaXZlZCB1c2luZyB0aGUgdHJhaW5pbmcgbW9kZWwNCnBhcihuZXc9VFJVRSwgeGF4dD0ibiIsIHlheHQ9Im4iLCBhbm49RkFMU0UpDQoNCiMgVGhlIGdyZWVuIGNvbG9yIGlzIHVzZWQgdG8gZGlzdGluZ3Vpc2ggdGhlIHByZWRpY3RlZCB2YWx1ZXMgd2hpY2gsIGJlY2F1c2Ugb2YgdGhlIHF1YWRyYXRpYyBtb2RlbCwgd2lsbCBpbiB0aGlzIGNhc2UgZml0IGV4YWN0bHkgYSBwYXJhYm9sYQ0KcGxvdChwYXRlbnRfdGVzdCwgc2NvcmVfcHJlZGljdDIsIGNvbD0iZ3JlZW4iKQ0KIA0KYGBgDQoNCjxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPg0KIyMjIyAyQykgUGxvdCB0aGUgUHJlZGljdCB2cyBBY3R1YWwgZm9yIHRoZSBxdWFkcmF0aWMgbW9kZWwuICBMYWJlbCBheGVzIGFuZCB0aWxlIHByb3Blcmx5Lg0KPC9zcGFuPg0KDQpgYGB7cn0NCiNQbG90IHRoZSBwcmVkaWN0ZWQgdmFsdWVzIGZvcm0gdGhlIHF1YWRyYXRpYyBtb2RlbCB2ZXJzdXMgdGhlIGFjdHVhbCB2YWx1ZXMgZnJvbSB0aGUgdGVzdCBkYXRhDQpwbG90KHNjb3JlX3Rlc3QsIHNjb3JlX3ByZWRpY3QyLCB4bGFiPSdBY3R1YWwnLCB5bGFiPSdQcmVkaWN0JywgbWFpbj0nUXVhZHJhdGljIE1vZGVsIC0tIFByZWRpY3QgdnMgQWN0dWFsIFRlc3QnKQ0KYGBgDQoNCjxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPg0KIyMjIyAyRCkgQnkgbG9va2luZyBhdCB0aGUgc2NhdHRlcnBsb3RzIGZvciB0aGUgbGluZWFyIGFuZCBxdWFkcmF0aWMgbW9kZWxzIGFyZSB5b3UgYWJsZSB0byB0ZWxsIHdoaWNoIG1vZGVsIGlzIGJldHRlcj8gRWxhYm9yYXRlLg0KPC9zcGFuPg0KDQpBY2NvcmRpbmcgdG8gdGhvc2UgdHdvIG1vZGVscywgdGhlIHF1YWRyYXRpYyBtb2RlbHMgYXJlIHRoZSBiZXR0ZXIgb25lIGJlY2F1c2UgaXQgaGFzIG1vcmUgZGF0YSBpbiB0aGUgbG93ZXIgbGVmdCBjb3JuZXIgd2hpY2ggbWVhbnMgaXQgY2xvc2VyIHRvIHRoZSBkaWFnb25hbCBsaW5lIHRoYW4gbGluZXIgbW9kZWwuDQogDQoNCkEgYmV0dGVyIHdheSBpcyB0byBxdWFudGlmeSB0aGUgZ29vZG5lc3Mgb2YgdGhlIG1vZGVsIGJ5IGNhbGN1bGF0aW5nIGFnYWluIHRoZSBSTVNFLg0KDQo8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj4NCiMjIyMgMkUpIENhbGN1bGF0ZSB0aGUgcm9vdCBtZWFuIHNxdWFyZSBlcnJvciAoUk1TRSkgZm9yIHRoZSBxdWFkcmF0aWMgbW9kZWwuDQo8L3NwYW4+DQoNCmBgYHtyfQ0KI0NhbGN1bGF0ZSBSTVNFICBmb3IgUXVhZHJhdGljIE1vZGVsDQplcnJvcjIgPSBzdW0oKHNjb3JlX3ByZWRpY3QyIC0gc2NvcmVfdGVzdCleMikvbGVuZ3RoKHNjb3JlX3Rlc3QpDQpybXNlMiA9IHNxcnQoZXJyb3IyKQ0Kcm1zZTINCmBgYA0KDQo8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj4NCiMjIyMgMkYpIEJhc2VkIG9uIHRoZSByb290IG1lYW4gc3F1YXJlIGVycm9yIChSTVNFKSAgd2hpY2ggbW9kZWwgaXMgYmV0dGVyPyBIb3cgZG8gcmVzdWx0cyByZWNvbmNpbGUgd2l0aCB0aGUgcmVzdWx0cyBmcm9tIFRhc2sgMT8NCjwvc3Bhbj4NCmxpbmVhciBtb2RlbCdzIFJNU0UgaXMgNS45NTYNClF1YWRyYXRpYyBtb2RlbCdzIFJNU0UgaXMgNS42ODUNCg0KQmFzZSBvbmUgdGhlIFJNU0UsIHRoZSBxdWFkcmF0aWMgbW9kZWwsIGlzIGJldHRlci4gVGhpcyByZXN1bHQgaXMgcmVsYXRlZCB0byB0aGUgVGFzayAyIHRoYXQgcXVhZHJhdGljIG1vZGVsIGhhcyBoaWdoZXIgQURKIFItc3F1YXJlZCB2YWx1ZSB0aGFuIGxpbmVyIG1vZGVsLiANCg0Kc291cmNlIFsxXTogW2h0dHA6Ly93d3cua2FnZ2xlLmNvbV0oaHR0cDovL3d3dy5rYWdnbGUuY29tKQ0KDQpzb3VyY2UgWzJdOiBbaHR0cDovL3d3dy5jd3VyLm9yZ10oaHR0cDovL3d3dy5jd3VyLm9yZykgDQo=