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==