4.6.1 The Stock Market Data
### Call the Library ISLR and data file smarket.
library(ISLR)
### List the variable names
names(Smarket)
[1] "Year" "Lag1" "Lag2" "Lag3"
[5] "Lag4" "Lag5" "Volume" "Today"
[9] "Direction"
## Dimensions and summary stats for continuous variables
dim(Smarket)
[1] 1250 9
summary(Smarket)
Year Lag1
Min. :2001 Min. :-4.922000
1st Qu.:2002 1st Qu.:-0.639500
Median :2003 Median : 0.039000
Mean :2003 Mean : 0.003834
3rd Qu.:2004 3rd Qu.: 0.596750
Max. :2005 Max. : 5.733000
Lag2 Lag3
Min. :-4.922000 Min. :-4.922000
1st Qu.:-0.639500 1st Qu.:-0.640000
Median : 0.039000 Median : 0.038500
Mean : 0.003919 Mean : 0.001716
3rd Qu.: 0.596750 3rd Qu.: 0.596750
Max. : 5.733000 Max. : 5.733000
Lag4 Lag5
Min. :-4.922000 Min. :-4.92200
1st Qu.:-0.640000 1st Qu.:-0.64000
Median : 0.038500 Median : 0.03850
Mean : 0.001636 Mean : 0.00561
3rd Qu.: 0.596750 3rd Qu.: 0.59700
Max. : 5.733000 Max. : 5.73300
Volume Today Direction
Min. :0.3561 Min. :-4.922000 Down:602
1st Qu.:1.2574 1st Qu.:-0.639500 Up :648
Median :1.4229 Median : 0.038500
Mean :1.4783 Mean : 0.003138
3rd Qu.:1.6417 3rd Qu.: 0.596750
Max. :3.1525 Max. : 5.733000

### Scatterplot Matrix
pairs(Smarket)
#cor(Smarket) #### Code has error.
cor(Smarket[,-9])
Year Lag1 Lag2
Year 1.00000000 0.029699649 0.030596422
Lag1 0.02969965 1.000000000 -0.026294328
Lag2 0.03059642 -0.026294328 1.000000000
Lag3 0.03319458 -0.010803402 -0.025896670
Lag4 0.03568872 -0.002985911 -0.010853533
Lag5 0.02978799 -0.005674606 -0.003557949
Volume 0.53900647 0.040909908 -0.043383215
Today 0.03009523 -0.026155045 -0.010250033
Lag3 Lag4 Lag5
Year 0.033194581 0.035688718 0.029787995
Lag1 -0.010803402 -0.002985911 -0.005674606
Lag2 -0.025896670 -0.010853533 -0.003557949
Lag3 1.000000000 -0.024051036 -0.018808338
Lag4 -0.024051036 1.000000000 -0.027083641
Lag5 -0.018808338 -0.027083641 1.000000000
Volume -0.041823686 -0.048414246 -0.022002315
Today -0.002447647 -0.006899527 -0.034860083
Volume Today
Year 0.53900647 0.030095229
Lag1 0.04090991 -0.026155045
Lag2 -0.04338321 -0.010250033
Lag3 -0.04182369 -0.002447647
Lag4 -0.04841425 -0.006899527
Lag5 -0.02200231 -0.034860083
Volume 1.00000000 0.014591823
Today 0.01459182 1.000000000
###Attach the data file smarket and plot a graph of volume vs. Index.
attach(Smarket)
plot(Volume)

4.6.2 Logistic Regression
### Fitting a GLM Model.
glm.fit=glm(Direction~Lag1+Lag2+Lag3+Lag4+Lag5+Volume,data=Smarket,family=binomial)
### Summary of the Model that was fitted above, Quantiles of the deviance, coefficients and the exact estimates.
summary(glm.fit)
Call:
glm(formula = Direction ~ Lag1 + Lag2 + Lag3 + Lag4 + Lag5 +
Volume, family = binomial, data = Smarket)
Deviance Residuals:
Min 1Q Median 3Q Max
-1.446 -1.203 1.065 1.145 1.326
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -0.126000 0.240736 -0.523 0.601
Lag1 -0.073074 0.050167 -1.457 0.145
Lag2 -0.042301 0.050086 -0.845 0.398
Lag3 0.011085 0.049939 0.222 0.824
Lag4 0.009359 0.049974 0.187 0.851
Lag5 0.010313 0.049511 0.208 0.835
Volume 0.135441 0.158360 0.855 0.392
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 1731.2 on 1249 degrees of freedom
Residual deviance: 1727.6 on 1243 degrees of freedom
AIC: 1741.6
Number of Fisher Scoring iterations: 3
coef(glm.fit)
(Intercept) Lag1 Lag2 Lag3
-0.126000257 -0.073073746 -0.042301344 0.011085108
Lag4 Lag5 Volume
0.009358938 0.010313068 0.135440659
### Just the coefficients from the GLM fit
summary(glm.fit)$coef
Estimate Std. Error z value
(Intercept) -0.126000257 0.24073574 -0.5233966
Lag1 -0.073073746 0.05016739 -1.4565986
Lag2 -0.042301344 0.05008605 -0.8445733
Lag3 0.011085108 0.04993854 0.2219750
Lag4 0.009358938 0.04997413 0.1872757
Lag5 0.010313068 0.04951146 0.2082966
Volume 0.135440659 0.15835970 0.8552723
Pr(>|z|)
(Intercept) 0.6006983
Lag1 0.1452272
Lag2 0.3983491
Lag3 0.8243333
Lag4 0.8514445
Lag5 0.8349974
Volume 0.3924004
### Only The 4th column in Coefficients, prob using z test.
summary(glm.fit)$coef[,4]
(Intercept) Lag1 Lag2 Lag3
0.6006983 0.1452272 0.3983491 0.8243333
Lag4 Lag5 Volume
0.8514445 0.8349974 0.3924004
### Generating probabilities using predict function and Type=response.
glm.probs=predict(glm.fit,type="response")
### Probabilities 1 through 10 are printed and the direction of prediction is up=1.
glm.probs[1:10]
1 2 3 4 5
0.5070841 0.4814679 0.4811388 0.5152224 0.5107812
6 7 8 9 10
0.5069565 0.4926509 0.5092292 0.5176135 0.4888378
contrasts(Direction)
Up
Down 0
Up 1
### Using the same predict function on all 1250 observations, we are creating the direction of the market as Up if the prob exceeds 0.5 and downotherwise. The table of predicted vs. direction is printed too.
glm.pred=rep("Down",1250)
glm.pred[glm.probs>.5]="Up"
table(glm.pred,Direction)
Direction
glm.pred Down Up
Down 145 141
Up 457 507
### Following divides the total true prediction by total to get the portion of correct predictions in this table.
mean(glm.pred==Direction)
[1] 0.5216
(507+145)/1250
[1] 0.5216
Dividing the Obs prior to 2005 as training data and the rest as test data
### The vector train pertains to all observationd prior to year 2005.
train=(Year<2005)
Smarket.2005=Smarket[!train,]
dim(Smarket.2005) ## Contains 252 obs and 9 variables
[1] 252 9
Direction.2005=Direction[!train]
###GLM Logistic regression for the training dataset using all 6 predictors.
glm.fit=glm(Direction~Lag1+Lag2+Lag3+Lag4+Lag5+Volume,data=Smarket,family=binomial,subset=train)
glm.probs=predict(glm.fit,Smarket.2005,type="response")
###direction calculation and mean direction of training data
glm.pred=rep("Down",252)
glm.pred[glm.probs>.5]="Up"
table(glm.pred,Direction.2005)
Direction.2005
glm.pred Down Up
Down 77 97
Up 34 44
mean(glm.pred==Direction.2005)
[1] 0.4801587
mean(glm.pred!=Direction.2005)
[1] 0.5198413
### GLM Logistic regression model using only lag1 & lag2 as predictors and the training dataset, generating proabilities, direction and mean direction
glm.fit=glm(Direction~Lag1+Lag2,data=Smarket,family=binomial,subset=train)
glm.probs=predict(glm.fit,Smarket.2005,type="response")
glm.pred=rep("Down",252)
glm.pred[glm.probs>.5]="Up"
### Table of actual vs. predicted directions
table(glm.pred,Direction.2005)
Direction.2005
glm.pred Down Up
Down 35 35
Up 76 106
### Dividing correct predictions by total to get the correct portion predicted
mean(glm.pred==Direction.2005)
[1] 0.5595238
106/(106+76)
[1] 0.5824176
### Another Log Reg GLM model and predicted probabilities using specific lag1 and lag2 values.
predict(glm.fit,newdata=data.frame(Lag1=c(1.2,1.5),Lag2=c(1.1,-0.8)),type="response")
1 2
0.4791462 0.4960939
4.6.3 Linear Discriminant Analysis
### Calling the library named MASS.
library(MASS)
### Fit a Linear Discriminant Analysis model to get Baye's Prior Probabilities, group average for predictors and the coefficients.
lda.fit=lda(Direction~Lag1+Lag2,data=Smarket,subset=train)
lda.fit
plot(lda.fit)
### Predictions using Linear Discriminant Analysis(LDA)
lda.pred=predict(lda.fit, Smarket.2005)
names(lda.pred)
[1] "class" "posterior" "x"
### Yields class=var for LDA's predictions, Posterior=Var for Posterior Prob & value x
### Assign the predicted class to the LDA Class and generating a table of this predicted class vs. the direction.
lda.class=lda.pred$class
table(lda.class,Direction.2005)
Direction.2005
lda.class Down Up
Down 35 35
Up 76 106
### Generating the Test-Error Rate
mean(lda.class==Direction.2005)
[1] 0.5595238
###Splitting the predicted probabilities by those above 50% and below 50% and getting their occurences
sum(lda.pred$posterior[,1]>=.5)
[1] 70
sum(lda.pred$posterior[,1]<.5)
[1] 182
### Printing the Predicted posteriors of 20 obserations and their class
lda.pred$posterior[1:20,1]
999 1000 1001 1002 1003
0.4901792 0.4792185 0.4668185 0.4740011 0.4927877
1004 1005 1006 1007 1008
0.4938562 0.4951016 0.4872861 0.4907013 0.4844026
1009 1010 1011 1012 1013
0.4906963 0.5119988 0.4895152 0.4706761 0.4744593
1014 1015 1016 1017 1018
0.4799583 0.4935775 0.5030894 0.4978806 0.4886331
lda.class[1:20]
[1] Up Up Up Up Up Up Up Up Up
[10] Up Up Down Up Up Up Up Up Down
[19] Up Up
Levels: Down Up
### If classification of posterior is set at 90%
sum(lda.pred$posterior[,1]>.9)
[1] 0
### there are no observation with 90% prob.
4.6.4 Quadratic Discriminant Analysis(QDA)
### Fitting a QDA model for data=smarket.
qda.fit=qda(Direction~Lag1+Lag2,data=Smarket,subset=train)
qda.fit
Call:
qda(Direction ~ Lag1 + Lag2, data = Smarket, subset = train)
Prior probabilities of groups:
Down Up
0.491984 0.508016
Group means:
Lag1 Lag2
Down 0.04279022 0.03389409
Up -0.03954635 -0.03132544
### Creating the table of class created by QDA model(QDA.class) vs. the direction for year 2005
qda.class=predict(qda.fit,Smarket.2005)$class
table(qda.class,Direction.2005)
Direction.2005
qda.class Down Up
Down 30 20
Up 81 121
### The mean value of QDA class for 2005.
mean(qda.class==Direction.2005)
[1] 0.5992063
4.6.5 K-Nearest Neighbors(KNN)
###Calling Library class for KNN using knn() function.
###Setting the training and test data sets
library(class)
train.X=cbind(Lag1,Lag2)[train,]
test.X=cbind(Lag1,Lag2)[!train,]
train.Direction=Direction[train]
set.seed(1)
### with k=1, we try knn
knn.pred=knn(train.X,test.X,train.Direction,k=1)
table(knn.pred,Direction.2005)
Direction.2005
knn.pred Down Up
Down 43 58
Up 68 83
### calculated error rate =50%
(83+43)/252
[1] 0.5
### Now, with k=3 rerun knn, derive table and the mean eror rate as 53.6%
knn.pred=knn(train.X,test.X,train.Direction,k=3)
table(knn.pred,Direction.2005)
Direction.2005
knn.pred Down Up
Down 48 54
Up 63 87
mean(knn.pred==Direction.2005)
[1] 0.5357143
4.6.6 An Application to Caravan Insurance Data
### Next using Caravan data
dim(Caravan)
[1] 5822 86
attach(Caravan)
summary(Purchase)
No Yes
5474 348
348/5822
[1] 0.05977327
### Standardizing the data for all but outcome=86th variable
standardized.X=scale(Caravan[,-86])
var(Caravan[,1])
[1] 165.0378
var(Caravan[,2])
[1] 0.1647078
var(standardized.X[,1])
[1] 1
var(standardized.X[,2])
[1] 1
### knn models are again built for k=1 & k=3
set.seed(1)
knn.pred=knn(train.X,test.X,train.Y,k=1)
mean(test.Y!=knn.pred)
[1] 0.118
mean(test.Y!="No")
[1] 0.059
table(knn.pred,test.Y)
test.Y
knn.pred No Yes
No 873 50
Yes 68 9
9/(68+9)
[1] 0.1168831
### After standardizing, k=1 yields 11.69% purchase rate and k=3 yields 19.23%
knn.pred=knn(train.X,test.X,train.Y,k=3)
table(knn.pred,test.Y)
test.Y
knn.pred No Yes
No 920 54
Yes 21 5
5/26
[1] 0.1923077
### When k=5 the purchase rate is at 26.67%
knn.pred=knn(train.X,test.X,train.Y,k=5)
table(knn.pred,test.Y)
test.Y
knn.pred No Yes
No 930 55
Yes 11 4
4/15
[1] 0.2666667
###Fitting a logistic regression model
glm.fit=glm(Purchase~.,data=Caravan,family=binomial,subset=-test)
glm.fit: fitted probabilities numerically 0 or 1 occurred
glm.probs=predict(glm.fit,Caravan[test,],type="response")
### Setting prob>50% as purchase=Y there are no correct purchase predictions.
glm.pred=rep("No",1000)
glm.pred[glm.probs>.5]="Yes"
table(glm.pred,test.Y)
test.Y
glm.pred No Yes
No 934 59
Yes 7 0
### But setting prob>.25 as Purchase=Y we get 11 correct predictions and a 33.3% Purchase rate.
glm.pred=rep("No",1000)
glm.pred[glm.probs>.25]="Yes"
table(glm.pred,test.Y)
test.Y
glm.pred No Yes
No 919 48
Yes 22 11
11/(22+11)
[1] 0.3333333
LS0tDQp0aXRsZTogIiMjIyBSIE5vdGVib29rIGZvciBMYWItNCAwNjkwLUFkdi4gVG9waWNzIGluIEJpb3N0YXQgRmFsbCAyMDE4Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQpOYW1lOiBKYXlhc2hyZWUgU2FtcGF0aA0KRGF0ZTogT2N0b2JlciAyLCAyMDE4LiBEdWUgYnkgT2N0b2JlciAzLCAyMDE4Lg0KLS0tDQojIyM0LjYuMSBUaGUgU3RvY2sgTWFya2V0IERhdGENCmBgYHtyfQ0KIyMjIENhbGwgdGhlIExpYnJhcnkgSVNMUiBhbmQgZGF0YSBmaWxlIHNtYXJrZXQuDQpsaWJyYXJ5KElTTFIpDQojIyMgTGlzdCB0aGUgdmFyaWFibGUgbmFtZXMgDQpuYW1lcyhTbWFya2V0KQ0KIyMgRGltZW5zaW9ucyBhbmQgc3VtbWFyeSBzdGF0cyBmb3IgY29udGludW91cyB2YXJpYWJsZXMgIA0KZGltKFNtYXJrZXQpDQpzdW1tYXJ5KFNtYXJrZXQpDQpgYGANCg0KYGBge3J9DQojIyMgU2NhdHRlcnBsb3QgTWF0cml4DQpwYWlycyhTbWFya2V0KQ0KI2NvcihTbWFya2V0KSAjIyMjIENvZGUgaGFzIGVycm9yLg0KY29yKFNtYXJrZXRbLC05XSkNCiMjI0F0dGFjaCB0aGUgZGF0YSBmaWxlIHNtYXJrZXQgYW5kIHBsb3QgYSBncmFwaCBvZiB2b2x1bWUgdnMuIEluZGV4Lg0KYXR0YWNoKFNtYXJrZXQpDQpwbG90KFZvbHVtZSkNCmBgYA0KIyMgNC42LjIgTG9naXN0aWMgUmVncmVzc2lvbg0KYGBge3J9DQojIyMgRml0dGluZyBhIEdMTSBsb2dpc3RpYyBSZWdyZXNzaW9uIG1vZGVsIC4NCmdsbS5maXQ9Z2xtKERpcmVjdGlvbn5MYWcxK0xhZzIrTGFnMytMYWc0K0xhZzUrVm9sdW1lLGRhdGE9U21hcmtldCxmYW1pbHk9Ymlub21pYWwpDQojIyMgU3VtbWFyeSBvZiB0aGUgTW9kZWwgdGhhdCB3YXMgZml0dGVkIGFib3ZlLCBRdWFudGlsZXMgb2YgdGhlIGRldmlhbmNlIFJlc2lkdWFscywgY29lZmZpY2llbnRzLCBleGFjdCBlc3RpbWF0ZXMgbnVsbCBkZXZpYW5jZSBmcm9tIGludGVyY2VwdCBvbmx5IG1vZGVsIGFuZCB0aGUgcmVzaWR1YWwgZGV2aWFuY2UgZnJvbSB0aGUgZnVsbCBtb2RlbC4NCnN1bW1hcnkoZ2xtLmZpdCkNCmNvZWYoZ2xtLmZpdCkNCmBgYA0KDQpgYGB7cn0NCiMjIyBKdXN0IHRoZSBjb2VmZmljaWVudHMgZnJvbSB0aGUgR0xNIGZpdA0Kc3VtbWFyeShnbG0uZml0KSRjb2VmDQojIyMgT25seSBUaGUgNHRoIGNvbHVtbiBpbiBDb2VmZmljaWVudHMsIHByb2IgdXNpbmcgeiB0ZXN0Lg0Kc3VtbWFyeShnbG0uZml0KSRjb2VmWyw0XQ0KYGBgDQoNCmBgYHtyfQ0KIyMjIEdlbmVyYXRpbmcgcHJvYmFiaWxpdGllcyB1c2luZyBwcmVkaWN0IGZ1bmN0aW9uIGFuZCAgVHlwZT1yZXNwb25zZS4gDQpnbG0ucHJvYnM9cHJlZGljdChnbG0uZml0LHR5cGU9InJlc3BvbnNlIikNCiMjIyBQcm9iYWJpbGl0aWVzIDEgdGhyb3VnaCAxMCBhcmUgcHJpbnRlZCBhbmQgdGhlIGRpcmVjdGlvbiBvZiBwcmVkaWN0aW9uIGlzIHVwPTEuDQpnbG0ucHJvYnNbMToxMF0NCmNvbnRyYXN0cyhEaXJlY3Rpb24pDQpgYGANCg0KYGBge3J9DQojIyMgVXNpbmcgdGhlIHNhbWUgcHJlZGljdCBmdW5jdGlvbiBvbiBhbGwgMTI1MCBvYnNlcnZhdGlvbnMsIHdlIGFyZSBjcmVhdGluZyB0aGUgZGlyZWN0aW9uIG9mIHRoZSBtYXJrZXQgYXMgVXAgaWYgdGhlIHByb2IgZXhjZWVkcyAwLjUgYW5kIGRvd25vdGhlcndpc2UuIFRoZSB0YWJsZSBvZiBwcmVkaWN0ZWQgdnMuIGRpcmVjdGlvbiBpcyBwcmludGVkIHRvby4NCmdsbS5wcmVkPXJlcCgiRG93biIsMTI1MCkNCmdsbS5wcmVkW2dsbS5wcm9icz4uNV09IlVwIg0KdGFibGUoZ2xtLnByZWQsRGlyZWN0aW9uKQ0KIyMjIEZvbGxvd2luZyBkaXZpZGVzIHRoZSB0b3RhbCB0cnVlIHByZWRpY3Rpb24gYnkgdG90YWwgdG8gZ2V0IHRoZSBwb3J0aW9uIG9mIGNvcnJlY3QgcHJlZGljdGlvbnMgaW4gdGhpcyB0YWJsZS4NCm1lYW4oZ2xtLnByZWQ9PURpcmVjdGlvbikNCig1MDcrMTQ1KS8xMjUwDQpgYGANCiMjIERpdmlkaW5nIHRoZSBPYnMgcHJpb3IgdG8gMjAwNSBhcyB0cmFpbmluZyBkYXRhIGFuZCB0aGUgcmVzdCBhcyB0ZXN0IGRhdGENCmBgYHtyfQ0KIyMjIFRoZSB2ZWN0b3IgdHJhaW4gcGVydGFpbnMgdG8gYWxsIG9ic2VydmF0aW9uZCBwcmlvciB0byB5ZWFyIDIwMDUuDQp0cmFpbj0oWWVhcjwyMDA1KQ0KU21hcmtldC4yMDA1PVNtYXJrZXRbIXRyYWluLF0NCmRpbShTbWFya2V0LjIwMDUpICMjIENvbnRhaW5zIDI1MiBvYnMgYW5kIDkgdmFyaWFibGVzDQpEaXJlY3Rpb24uMjAwNT1EaXJlY3Rpb25bIXRyYWluXQ0KIyMjR0xNIExvZ2lzdGljIHJlZ3Jlc3Npb24gZm9yIHRoZSB0cmFpbmluZyBkYXRhc2V0IHVzaW5nIGFsbCA2IHByZWRpY3RvcnMuDQpnbG0uZml0PWdsbShEaXJlY3Rpb25+TGFnMStMYWcyK0xhZzMrTGFnNCtMYWc1K1ZvbHVtZSxkYXRhPVNtYXJrZXQsZmFtaWx5PWJpbm9taWFsLHN1YnNldD10cmFpbikNCmdsbS5wcm9icz1wcmVkaWN0KGdsbS5maXQsU21hcmtldC4yMDA1LHR5cGU9InJlc3BvbnNlIikNCiMjI2RpcmVjdGlvbiBjYWxjdWxhdGlvbiBhbmQgbWVhbiBkaXJlY3Rpb24gb2YgdHJhaW5pbmcgZGF0YQ0KZ2xtLnByZWQ9cmVwKCJEb3duIiwyNTIpDQpnbG0ucHJlZFtnbG0ucHJvYnM+LjVdPSJVcCINCnRhYmxlKGdsbS5wcmVkLERpcmVjdGlvbi4yMDA1KQ0KbWVhbihnbG0ucHJlZD09RGlyZWN0aW9uLjIwMDUpDQptZWFuKGdsbS5wcmVkIT1EaXJlY3Rpb24uMjAwNSkNCmBgYA0KDQpgYGB7cn0NCiMjIyBHTE0gTG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB1c2luZyBvbmx5IGxhZzEgJiBsYWcyIGFzIHByZWRpY3RvcnMgYW5kIHRoZSB0cmFpbmluZyBkYXRhc2V0LCBnZW5lcmF0aW5nIHByb2FiaWxpdGllcywgZGlyZWN0aW9uIGFuZCBtZWFuIGRpcmVjdGlvbg0KZ2xtLmZpdD1nbG0oRGlyZWN0aW9ufkxhZzErTGFnMixkYXRhPVNtYXJrZXQsZmFtaWx5PWJpbm9taWFsLHN1YnNldD10cmFpbikNCmdsbS5wcm9icz1wcmVkaWN0KGdsbS5maXQsU21hcmtldC4yMDA1LHR5cGU9InJlc3BvbnNlIikNCmdsbS5wcmVkPXJlcCgiRG93biIsMjUyKQ0KZ2xtLnByZWRbZ2xtLnByb2JzPi41XT0iVXAiDQojIyMgVGFibGUgb2YgYWN0dWFsIHZzLiBwcmVkaWN0ZWQgZGlyZWN0aW9ucyANCnRhYmxlKGdsbS5wcmVkLERpcmVjdGlvbi4yMDA1KQ0KIyMjIERpdmlkaW5nIGNvcnJlY3QgcHJlZGljdGlvbnMgYnkgdG90YWwgdG8gZ2V0IHRoZSBjb3JyZWN0IHBvcnRpb24gcHJlZGljdGVkDQptZWFuKGdsbS5wcmVkPT1EaXJlY3Rpb24uMjAwNSkNCjEwNi8oMTA2Kzc2KQ0KYGBgDQoNCmBgYHtyfQ0KIyMjIEFub3RoZXIgTG9nIFJlZyBHTE0gbW9kZWwgYW5kIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIHVzaW5nIHNwZWNpZmljIGxhZzEgYW5kIGxhZzIgdmFsdWVzLg0KcHJlZGljdChnbG0uZml0LG5ld2RhdGE9ZGF0YS5mcmFtZShMYWcxPWMoMS4yLDEuNSksTGFnMj1jKDEuMSwtMC44KSksdHlwZT0icmVzcG9uc2UiKQ0KYGBgDQojIyA0LjYuMyBMaW5lYXIgRGlzY3JpbWluYW50IEFuYWx5c2lzDQpgYGB7cn0NCiMjIyBDYWxsaW5nIHRoZSBsaWJyYXJ5IG5hbWVkIE1BU1MuDQpsaWJyYXJ5KE1BU1MpDQojIyMgRml0IGEgTGluZWFyIERpc2NyaW1pbmFudCBBbmFseXNpcyBtb2RlbCB0byBnZXQgQmF5ZSdzIFByaW9yIFByb2JhYmlsaXRpZXMsIGdyb3VwIGF2ZXJhZ2UgZm9yIHByZWRpY3RvcnMgYW5kIHRoZSBjb2VmZmljaWVudHMuDQpsZGEuZml0PWxkYShEaXJlY3Rpb25+TGFnMStMYWcyLGRhdGE9U21hcmtldCxzdWJzZXQ9dHJhaW4pDQpsZGEuZml0DQpwbG90KGxkYS5maXQpDQpgYGANCg0KYGBge3J9DQojIyMgUHJlZGljdGlvbnMgdXNpbmcgTGluZWFyIERpc2NyaW1pbmFudCBBbmFseXNpcyhMREEpDQpsZGEucHJlZD1wcmVkaWN0KGxkYS5maXQsIFNtYXJrZXQuMjAwNSkNCm5hbWVzKGxkYS5wcmVkKQ0KIyMjIFlpZWxkcyBjbGFzcz12YXIgZm9yIExEQSdzIHByZWRpY3Rpb25zLCBQb3N0ZXJpb3I9VmFyIGZvciBQb3N0ZXJpb3IgUHJvYiAmIHZhciB4IGZvciB0aGUgZGlzY3JpbWluYW50cywgYXMgZm9sbG93czsNCmBgYA0KDQpgYGB7cn0NCiMjIyBBc3NpZ24gdGhlIHByZWRpY3RlZCBjbGFzcyB0byB0aGUgTERBIENsYXNzIGFuZCBnZW5lcmF0aW5nIGEgdGFibGUgb2YgdGhpcyBwcmVkaWN0ZWQgY2xhc3MgdnMuIHRoZSBkaXJlY3Rpb24uDQpsZGEuY2xhc3M9bGRhLnByZWQkY2xhc3MNCnRhYmxlKGxkYS5jbGFzcyxEaXJlY3Rpb24uMjAwNSkNCiMjIyBHZW5lcmF0aW5nIHRoZSBUZXN0LUVycm9yIFJhdGUNCm1lYW4obGRhLmNsYXNzPT1EaXJlY3Rpb24uMjAwNSkNCmBgYA0KDQpgYGB7cn0NCiMjI1NwbGl0dGluZyB0aGUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgYnkgdGhvc2UgYWJvdmUgNTAlIGFuZCBiZWxvdyA1MCUgYW5kIGdldHRpbmcgdGhlaXIgb2NjdXJlbmNlcw0Kc3VtKGxkYS5wcmVkJHBvc3RlcmlvclssMV0+PS41KQ0Kc3VtKGxkYS5wcmVkJHBvc3RlcmlvclssMV08LjUpDQpgYGANCg0KYGBge3J9DQojIyMgUHJpbnRpbmcgdGhlIFByZWRpY3RlZCBwb3N0ZXJpb3JzIG9mIDIwIG9ic2VyYXRpb25zIGFuZCB0aGVpciBjbGFzcw0KbGRhLnByZWQkcG9zdGVyaW9yWzE6MjAsMV0NCmxkYS5jbGFzc1sxOjIwXQ0KIyMjIElmIGNsYXNzaWZpY2F0aW9uIG9mIHBvc3RlcmlvciBpcyBzZXQgYXQgOTAlIA0Kc3VtKGxkYS5wcmVkJHBvc3RlcmlvclssMV0+LjkpDQojIyMgdGhlcmUgYXJlIG5vIG9ic2VydmF0aW9ucyBpbiAyMDA1IHdpdGggOTAlIHByb2IuDQpgYGANCiMjIyA0LjYuNCBRdWFkcmF0aWMgRGlzY3JpbWluYW50IEFuYWx5c2lzKFFEQSkNCmBgYHtyfQ0KIyMjIEZpdHRpbmcgYSBRREEgbW9kZWwgZm9yIGRhdGE9c21hcmtldC4NCnFkYS5maXQ9cWRhKERpcmVjdGlvbn5MYWcxK0xhZzIsZGF0YT1TbWFya2V0LHN1YnNldD10cmFpbikNCnFkYS5maXQNCmBgYA0KDQpgYGB7cn0NCiMjIyBDcmVhdGluZyB0aGUgdGFibGUgb2YgY2xhc3MgY3JlYXRlZCBieSBRREEgbW9kZWwoUURBLmNsYXNzKSB2cy4gdGhlIGRpcmVjdGlvbiBmb3IgeWVhciAyMDA1IA0KcWRhLmNsYXNzPXByZWRpY3QocWRhLmZpdCxTbWFya2V0LjIwMDUpJGNsYXNzDQp0YWJsZShxZGEuY2xhc3MsRGlyZWN0aW9uLjIwMDUpDQojIyMgVGhlIG1lYW4gdmFsdWUgb2YgUURBIGNsYXNzIGZvciAyMDA1Lg0KbWVhbihxZGEuY2xhc3M9PURpcmVjdGlvbi4yMDA1KQ0KYGBgDQojIyMgNC42LjUgSy1OZWFyZXN0IE5laWdoYm9ycyhLTk4pDQpgYGB7cn0NCiMjI0NhbGxpbmcgTGlicmFyeSBjbGFzcyBmb3IgS05OIHVzaW5nIGtubigpIGZ1bmN0aW9uLg0KIyMjU2V0dGluZyB0aGUgdHJhaW5pbmcgYW5kIHRlc3QgZGF0YSBzZXRzDQpsaWJyYXJ5KGNsYXNzKQ0KdHJhaW4uWD1jYmluZChMYWcxLExhZzIpW3RyYWluLF0NCnRlc3QuWD1jYmluZChMYWcxLExhZzIpWyF0cmFpbixdDQp0cmFpbi5EaXJlY3Rpb249RGlyZWN0aW9uW3RyYWluXQ0Kc2V0LnNlZWQoMSkNCiMjIyB3aXRoIGs9MSwgd2UgdHJ5IGtubg0Ka25uLnByZWQ9a25uKHRyYWluLlgsdGVzdC5YLHRyYWluLkRpcmVjdGlvbixrPTEpDQp0YWJsZShrbm4ucHJlZCxEaXJlY3Rpb24uMjAwNSkNCiMjIyBjYWxjdWxhdGVkIGVycm9yIHJhdGUgPTUwJQ0KKDgzKzQzKS8yNTINCiMjIyBOb3csIHdpdGggaz0zIHJlcnVuIGtubiwgZGVyaXZlIHRhYmxlIGFuZCB0aGUgbWVhbiBlcm9yIHJhdGUgYXMgNTMuNiUNCmtubi5wcmVkPWtubih0cmFpbi5YLHRlc3QuWCx0cmFpbi5EaXJlY3Rpb24saz0zKQ0KdGFibGUoa25uLnByZWQsRGlyZWN0aW9uLjIwMDUpDQptZWFuKGtubi5wcmVkPT1EaXJlY3Rpb24uMjAwNSkNCmBgYA0KIyMjIDQuNi42IEFuIEFwcGxpY2F0aW9uIHRvIENhcmF2YW4gSW5zdXJhbmNlIERhdGENCmBgYHtyfQ0KIyMjIE5leHQgd2UgdXNlIENhcmF2YW4gZGF0YSB3aXRoIDU4MjIgb2JzICYgODYgdmFyaWFibGVzLiBPdXRjb21lPVB1cmNoYXNlIFkvTi4gWWVzPTAuMDU5NyA9IDYlLg0KZGltKENhcmF2YW4pDQphdHRhY2goQ2FyYXZhbikNCnN1bW1hcnkoUHVyY2hhc2UpDQozNDgvNTgyMg0KYGBgDQoNCmBgYHtyfQ0KIyMjIFN0YW5kYXJkaXppbmcgdGhlIGRhdGEgZm9yIGFsbCBidXQgb3V0Y29tZT04NnRoIHZhcmlhYmxlDQpzdGFuZGFyZGl6ZWQuWD1zY2FsZShDYXJhdmFuWywtODZdKQ0KdmFyKENhcmF2YW5bLDFdKQ0KdmFyKENhcmF2YW5bLDJdKQ0KdmFyKHN0YW5kYXJkaXplZC5YWywxXSkNCnZhcihzdGFuZGFyZGl6ZWQuWFssMl0pDQoNCmBgYA0KDQpgYGB7cn0NCiMjIyBUZXN0IGRhdGEgaXMgdGhlIGZpcnJzdCAxMDAwIG9icyBhbmQgcmVtYWluaW50IGFyZSB0cmFpbiBkYXRhIHdpdGhvdXQgdGhlIHZhcmlhYmxlIHB1cmNoYXNlDQp0ZXN0PTE6MTAwMA0KdHJhaW4uWD1zdGFuZGFyZGl6ZWQuWFstdGVzdCxdDQp0ZXN0Llg9c3RhbmRhcmRpemVkLlhbdGVzdCxdDQp0cmFpbi5ZPVB1cmNoYXNlWy10ZXN0XQ0KdGVzdC5ZPVB1cmNoYXNlW3Rlc3RdDQpgYGANCg0KYGBge3J9DQojIyMga25uIG1vZGVscyBhcmUgYWdhaW4gYnVpbHQgZm9yIGs9MSAmIGs9Mw0Kc2V0LnNlZWQoMSkNCmtubi5wcmVkPWtubih0cmFpbi5YLHRlc3QuWCx0cmFpbi5ZLGs9MSkNCm1lYW4odGVzdC5ZIT1rbm4ucHJlZCkNCm1lYW4odGVzdC5ZIT0iTm8iKQ0KdGFibGUoa25uLnByZWQsdGVzdC5ZKQ0KOS8oNjgrOSkNCiMjIyBBZnRlciBzdGFuZGFyZGl6aW5nLCBrPTEgeWllbGRzIDExLjY5JSBwdXJjaGFzZSByYXRlIGFuZCBrPTMgeWllbGRzIDE5LjIzJQ0Ka25uLnByZWQ9a25uKHRyYWluLlgsdGVzdC5YLHRyYWluLlksaz0zKQ0KdGFibGUoa25uLnByZWQsdGVzdC5ZKQ0KNS8yNg0KYGBgDQoNCmBgYHtyfQ0KIyMjIFdoZW4gaz01IHRoZSBwdXJjaGFzZSByYXRlIGlzIGF0IDI2LjY3JQ0Ka25uLnByZWQ9a25uKHRyYWluLlgsdGVzdC5YLHRyYWluLlksaz01KQ0KdGFibGUoa25uLnByZWQsdGVzdC5ZKQ0KNC8xNQ0KYGBgDQoNCmBgYHtyfQ0KIyMjRml0dGluZyBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwNCmdsbS5maXQ9Z2xtKFB1cmNoYXNlfi4sZGF0YT1DYXJhdmFuLGZhbWlseT1iaW5vbWlhbCxzdWJzZXQ9LXRlc3QpDQpnbG0ucHJvYnM9cHJlZGljdChnbG0uZml0LENhcmF2YW5bdGVzdCxdLHR5cGU9InJlc3BvbnNlIikNCiMjIyBTZXR0aW5nIHByb2I+NTAlIGFzIHB1cmNoYXNlPVkgdGhlcmUgYXJlIG5vIGNvcnJlY3QgcHVyY2hhc2UgcHJlZGljdGlvbnMuDQpnbG0ucHJlZD1yZXAoIk5vIiwxMDAwKQ0KZ2xtLnByZWRbZ2xtLnByb2JzPi41XT0iWWVzIg0KdGFibGUoZ2xtLnByZWQsdGVzdC5ZKQ0KIyMjIEJ1dCBzZXR0aW5nIHByb2I+LjI1IGFzIFB1cmNoYXNlPVkgd2UgZ2V0IDExIGNvcnJlY3QgcHJlZGljdGlvbnMgYW5kIGEgMzMuMyUgUHVyY2hhc2UgcmF0ZS4NCmdsbS5wcmVkPXJlcCgiTm8iLDEwMDApDQpnbG0ucHJlZFtnbG0ucHJvYnM+LjI1XT0iWWVzIg0KdGFibGUoZ2xtLnByZWQsdGVzdC5ZKQ0KMTEvKDIyKzExKQ0KDQpgYGANCg0KDQoNCg==