5

We have seen that we can fit an SVM with a non-linear kernel in order to perform classification using a non-linear decision boundary. We will now see that we can also obtain a non-linear decision boundary by performing logistic regression using non-linear transformations of the features.

(a) Generate a data set with n = 500 and p = 2, such that the observations belong to two classes with a quadratic decision boundary between them.

x1 = runif(500) - 0.5
x2 = runif(500) - 0.5
y = 1*(x1^2 - x2^2 > 0)

(b) Plot the observations, colored according to their class labels. Your plot should display X1 on the x-axis, and X2 on the y-axis.

library(tidyverse)

df = data.frame(x1,x2,y)


plot(x1[y ==0], x2[y == 0], col = 'red', xlab = 'x1',ylab = 'x2')
points(x1[y==1],x2[y==1],col = 'blue')

(c) Fit a logistic regression model to the data, using X1 and X2 as predictors.

(d) Apply this model to the training data in order to obtain a predicted class label for each training observation. Plot the observations, colored according to the predicted class labels. The decision boundary should be linear.

(e) Now fit a logistic regression model to the data using non-linear functions of X1 and X2 as predictors (e.g. X21 , X1 ×X2, log(X2),and so forth).

(f) Apply this model to the training data in order to obtain a predicted class label for each training observation. Plot the observations, colored according to the predicted class labels. The decision boundary should be obviously non-linear. If it is not, then repeat (a)-(e) until you come up with an example in which the predicted class labels are obviously non-linear.

glm.fit2 = glm(y ~ x1 * x2, data = df, family = "binomial")
df$class = predict(glm.fit2, type = 'response')
df$pred.class = ifelse(df$class > .5, 1, 0)
df$pred.class = as.factor(df$pred.class)
df|>
ggplot(mapping = aes(x1,x2))+geom_point(aes(colour = pred.class))+labs(title = "Logistic Regression with a Non Linear Decision Boundary", subtitle = "Y ~ X1 * X2")+theme_minimal()

(g) Fit a support vector classifier to the data with X1 and X2 as predictors. Obtain a class prediction for each training observation. Plot the observations, colored according to the predicted class labels.

library(caret)
set.seed(12)
control.cv= trainControl(method = "cv", number = 10)
svm = train(as.factor(y) ~., data = df, method = "svmLinear", trControl = control.cv)

svm
Support Vector Machines with Linear Kernel 

500 samples
  6 predictor
  2 classes: '0', '1' 

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 450, 450, 450, 450, 450, 451, ... 
Resampling results:

  Accuracy   Kappa    
  0.9621961  0.9243392

Tuning parameter 'C' was held constant at a value of 1
df$svm_pred = predict(svm, newdata = df)
df|>
ggplot(aes(x = x1, y = x2, color = svm_pred))+geom_point()+theme_minimal()+labs(title = "Support Vector Machine", subtitle = "Color by predicted Class")

(h) Fit a SVM using a non-linear kernel to the data. Obtain a class prediction for each training observation. Plot the observations, colored according to the predicted class labels.

set.seed(12)
nonlin.svm = train(y ~.,data =df, method = "svmRadial", trControl = control.cv)

nonlin.svm
Support Vector Machines with Radial Basis Function Kernel 

500 samples
  6 predictor
  2 classes: '0', '1' 

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 450, 450, 450, 450, 450, 451, ... 
Resampling results across tuning parameters:

  C     Accuracy   Kappa    
  0.25  0.9621961  0.9243392
  0.50  0.9621961  0.9243392
  1.00  0.9621961  0.9243392

Tuning parameter 'sigma' was held constant at a value of 0.3029571
Accuracy was used to select the optimal model using the
 largest value.
The final values used for the model were sigma = 0.3029571 and C
 = 0.25.
df$svm_pred_nonlin = predict(nonlin.svm,newdata = df)

df|>
  ggplot(aes(x1,x2, color = svm_pred_nonlin))+geom_point()+labs(title = "Suppor Vector Machine With Radial Kernel", subtitle = "Colored by Predicted Class")+theme_minimal()

(i) Comment on your results caret() is hypertunning the SVM with a Radial kernel thus it is appears to look like the linear kernel.

7

In this problem, you will use support vector approaches in order to predict whether a given car gets high or low gas mileage based on the Auto data set.

library(ISLR2)
library(e1071)
data(Auto)

(a) Create a binary variable that takes on a 1 for cars with gas mileage above the median, and a 0 for cars with gas mileage below the median.

Auto$mpg = ifelse( Auto$mpg >median(Auto$mpg),1,0)
Error in median.default(Auto$mpg) : need numeric data

(b) Fit a support vector classifier to the data with various values of cost,in order to predict whether a car gets high or low gas mileage. Report the cross-validation errors associated with different values of this parameter. Comment on your results. Note you will need to fit the classifier without the gas mileage variable to produce sensible results.


cost.grid = expand.grid(C = c(0.01,.1,1,10,100))
mpg.svm = train(mpg~. -name, data = Auto,method = "svmLinear", trControl = control.cv, tuneGrid = cost.grid)


mpg.svm
Support Vector Machines with Linear Kernel 

392 samples
  8 predictor
  2 classes: '0', '1' 

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 352, 354, 353, 354, 354, 352, ... 
Resampling results across tuning parameters:

  C      Accuracy   Kappa    
  1e-02  0.9107928  0.8215789
  1e-01  0.9056613  0.8113158
  1e+00  0.9132928  0.8265789
  1e+01  0.9080331  0.8160256
  1e+02  0.9080331  0.8160256

Accuracy was used to select the optimal model using the
 largest value.
The final value used for the model was C = 1.

The Best Cost parameter is 1e+00 and an accuracy of .913.

(c) Now repeat (b), this time using SVMs with radial and polynomial basis kernels, with different values of gamma and degree and cost. Comment on your results.

mpg.poly
Support Vector Machines with Polynomial Kernel 

392 samples
  8 predictor
  2 classes: '0', '1' 

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 353, 353, 354, 353, 353, 352, ... 
Resampling results across tuning parameters:

  degree  scale  C     Accuracy   Kappa    
  1       0.001  0.25  0.6679858  0.3451886
  1       0.001  0.50  0.8013866  0.6030865
  1       0.001  1.00  0.8699798  0.7397750
  1       0.010  0.25  0.8906309  0.7810256
  1       0.010  0.50  0.9032591  0.8064520
  1       0.010  1.00  0.9108941  0.8216622
  1       0.100  0.25  0.9083300  0.8165374
  1       0.100  0.50  0.9083300  0.8164163
  1       0.100  1.00  0.9056984  0.8111532
  2       0.001  0.25  0.8013866  0.6030865
  2       0.001  0.50  0.8699798  0.7397750
  2       0.001  1.00  0.8830027  0.7657072
  2       0.010  0.25  0.9032591  0.8064520
  2       0.010  0.50  0.9108941  0.8216622
  2       0.010  1.00  0.9083300  0.8165374
  2       0.100  0.25  0.9108941  0.8215411
  2       0.100  0.50  0.9081950  0.8161531
  2       0.100  1.00  0.9107591  0.8213320
  3       0.001  0.25  0.8674798  0.7347750
  3       0.001  0.50  0.8778745  0.7555645
  3       0.001  1.00  0.8931950  0.7862048
  3       0.010  0.25  0.9134582  0.8268403
  3       0.010  0.50  0.9108941  0.8216622
  3       0.010  1.00  0.9057659  0.8113858
  3       0.100  0.25  0.9030668  0.8059034
  3       0.100  0.50  0.9159514  0.8317065
  3       0.100  1.00  0.9210796  0.8420096

Accuracy was used to select the optimal model using the
 largest value.
The final values used for the model were degree = 3, scale =
 0.1 and C = 1.

The best Cost parameter is . 5 with an accuracy of .91. While the best cost parameter for the Polynomial kernel is The final values used for the model were degree = 3, scale = 0.1 and C = 1 and with an accuracy of .921,

(d) Make some plots to back up your assertions in (b) and (c)

plot(mpg.svm)

The highest accuracy is around .91 with a cost of .1.

plot(mpg.rad)

Accuracy is around .91 with a Cost parameter of .5.

plot(mpg.poly)

The best C = 1, Scale = .1, and Degree = 3, which follows what we was previously stated.

8

8. This problem involves the OJ data set which is part of the ISLR2 package.

(a) Create a training set containing a random sample of 800 observations, and a test set containing the remaining observations.

data(OJ)

oj.index = sample(1:nrow(OJ), 800)
oj.train = OJ[oj.index,]
oj.test = OJ[-oj.index,]

(b) Fit a support vector classifier to the training data using cost = 0.01, with Purchase as the response and the other variables as predictors. Use the summary() function to produce summary statistics, and describe the results obtained.

oj.svm
Support Vector Machines with Linear Kernel 

800 samples
 17 predictor
  2 classes: 'CH', 'MM' 

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 720, 720, 721, 720, 720, 719, ... 
Resampling results:

  Accuracy   Kappa    
  0.8337758  0.6440631

Tuning parameter 'C' was held constant at a value of 0.01

The accuracy of the this Support Machine Classifier with a Linear Kernel is .83 containing 17 variables.

(c) What are the training and test error rates?

confusionMatrix(oj.svm.pred,oj.test$Purchase)
Confusion Matrix and Statistics

          Reference
Prediction  CH  MM
        CH 143  32
        MM  19  76
                                         
               Accuracy : 0.8111         
                 95% CI : (0.7592, 0.856)
    No Information Rate : 0.6            
    P-Value [Acc > NIR] : 8.311e-14      
                                         
                  Kappa : 0.5984         
                                         
 Mcnemar's Test P-Value : 0.09289        
                                         
            Sensitivity : 0.8827         
            Specificity : 0.7037         
         Pos Pred Value : 0.8171         
         Neg Pred Value : 0.8000         
             Prevalence : 0.6000         
         Detection Rate : 0.5296         
   Detection Prevalence : 0.6481         
      Balanced Accuracy : 0.7932         
                                         
       'Positive' Class : CH             
                                         

The test error is .81 while the training error is .83, which could suggest some overfitting within the svmLinear model.

(d) Use the tune() function to select an optimal cost. Consider values in the range 0.01 to 10.

oj.svm = train(Purchase ~., data = oj.train, method = "svmLinear", trControl = control.cv, tuneGrid = cost.grid)

oj.svm
Support Vector Machines with Linear Kernel 

800 samples
 17 predictor
  2 classes: 'CH', 'MM' 

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 720, 720, 720, 721, 720, 720, ... 
Resampling results across tuning parameters:

  C      Accuracy   Kappa    
  1e-02  0.8312299  0.6370552
  1e-01  0.8337457  0.6463782
  1e+00  0.8387619  0.6561486
  1e+01  0.8412148  0.6605159
  1e+02  0.8411990  0.6604185

Accuracy was used to select the optimal model using the
 largest value.
The final value used for the model was C = 10.

(e) Compute the training and test error rates using this new value for cost.

oj.svm.pred = predict(oj.svm,newdata = oj.test)

confusionMatrix(oj.svm.pred,oj.test$Purchase)
Confusion Matrix and Statistics

          Reference
Prediction  CH  MM
        CH 142  30
        MM  20  78
                                          
               Accuracy : 0.8148          
                 95% CI : (0.7633, 0.8593)
    No Information Rate : 0.6             
    P-Value [Acc > NIR] : 2.854e-14       
                                          
                  Kappa : 0.6082          
                                          
 Mcnemar's Test P-Value : 0.2031          
                                          
            Sensitivity : 0.8765          
            Specificity : 0.7222          
         Pos Pred Value : 0.8256          
         Neg Pred Value : 0.7959          
             Prevalence : 0.6000          
         Detection Rate : 0.5259          
   Detection Prevalence : 0.6370          
      Balanced Accuracy : 0.7994          
                                          
       'Positive' Class : CH              
                                          

The test error is .81 with the Cost parameter set from .01 to 10

(f) Repeat parts (b) through (e) using a support vector machine with a radial kernel. Use the default value for gamma.

oj.svm.rad
Support Vector Machines with Radial Basis Function Kernel 

800 samples
 17 predictor
  2 classes: 'CH', 'MM' 

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 720, 720, 720, 720, 720, 721, ... 
Resampling results across tuning parameters:

  C     Accuracy   Kappa    
  0.25  0.8226033  0.6160577
  0.50  0.8238070  0.6182171
  1.00  0.8250725  0.6210410

Tuning parameter 'sigma' was held constant at a value of 0.05870129
Accuracy was used to select the optimal model using the
 largest value.
The final values used for the model were sigma = 0.05870129 and
 C = 1.

The best accuracy is .825.

oj.svm.rad.pred = predict(oj.svm.rad,newdata = oj.test)

confusionMatrix(oj.svm.rad.pred,oj.test$Purchase)
Confusion Matrix and Statistics

          Reference
Prediction  CH  MM
        CH 148  31
        MM  14  77
                                          
               Accuracy : 0.8333          
                 95% CI : (0.7834, 0.8758)
    No Information Rate : 0.6             
    P-Value [Acc > NIR] : < 2e-16         
                                          
                  Kappa : 0.6434          
                                          
 Mcnemar's Test P-Value : 0.01707         
                                          
            Sensitivity : 0.9136          
            Specificity : 0.7130          
         Pos Pred Value : 0.8268          
         Neg Pred Value : 0.8462          
             Prevalence : 0.6000          
         Detection Rate : 0.5481          
   Detection Prevalence : 0.6630          
      Balanced Accuracy : 0.8133          
                                          
       'Positive' Class : CH              
                                          

Test error is .833

confusionMatrix(oj.svm.rad.pred,oj.test$Purchase)
Confusion Matrix and Statistics

          Reference
Prediction  CH  MM
        CH 162 108
        MM   0   0
                                          
               Accuracy : 0.6             
                 95% CI : (0.5389, 0.6589)
    No Information Rate : 0.6             
    P-Value [Acc > NIR] : 0.5264          
                                          
                  Kappa : 0               
                                          
 Mcnemar's Test P-Value : <2e-16          
                                          
            Sensitivity : 1.0             
            Specificity : 0.0             
         Pos Pred Value : 0.6             
         Neg Pred Value : NaN             
             Prevalence : 0.6             
         Detection Rate : 0.6             
   Detection Prevalence : 1.0             
      Balanced Accuracy : 0.5             
                                          
       'Positive' Class : CH              
                                          

The accuracy dropped down to .6 on the test set.

(g) Repeat parts (b) through (e) using a support vector machine with a polynomial kernel. Set degree = 2.

oj.svm.poly
Support Vector Machines with Polynomial Kernel 

800 samples
 17 predictor
  2 classes: 'CH', 'MM' 

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 720, 720, 720, 720, 720, 720, ... 
Resampling results across tuning parameters:

  degree  scale  C     Accuracy   Kappa     
  1       0.001  0.25  0.6137537  0.00000000
  1       0.001  0.50  0.6137537  0.00000000
  1       0.001  1.00  0.6862248  0.23152111
  1       0.001  2.00  0.8150250  0.59234779
  1       0.010  0.25  0.8212600  0.61138927
  1       0.010  0.50  0.8362912  0.64999247
  1       0.010  1.00  0.8325566  0.64085386
  1       0.010  2.00  0.8325412  0.64192112
  1       0.100  0.25  0.8350258  0.64680442
  1       0.100  0.50  0.8387912  0.65551745
  1       0.100  1.00  0.8387758  0.65503882
  1       0.100  2.00  0.8400570  0.65911582
  1       1.000  0.25  0.8388070  0.65590840
  1       1.000  0.50  0.8400725  0.65816588
  1       1.000  1.00  0.8400879  0.65825974
  1       1.000  2.00  0.8413225  0.66062428
  2       0.001  0.25  0.6137537  0.00000000
  2       0.001  0.50  0.6874748  0.23511741
  2       0.001  1.00  0.8150250  0.59234779
  2       0.001  2.00  0.8312908  0.63879437
  2       0.010  0.25  0.8362754  0.64832893
  2       0.010  0.50  0.8387600  0.65415544
  2       0.010  1.00  0.8400258  0.65767211
  2       0.010  2.00  0.8438229  0.66715525
  2       0.100  0.25  0.8325879  0.63972766
  2       0.100  0.50  0.8238221  0.62019796
  2       0.100  1.00  0.8151029  0.60139473
  2       0.100  2.00  0.8188217  0.60926107
  2       1.000  0.25  0.8063526  0.58327905
  2       1.000  0.50  0.8075867  0.58672156
  2       1.000  1.00  0.8088526  0.58858804
  2       1.000  2.00  0.8088529  0.58823031
  3       0.001  0.25  0.6400049  0.08356011
  3       0.001  0.50  0.7862584  0.51150175
  3       0.001  1.00  0.8300100  0.63268277
  3       0.001  2.00  0.8338066  0.64365701
  3       0.010  0.25  0.8362754  0.64849317
  3       0.010  0.50  0.8375408  0.65273253
  3       0.010  1.00  0.8425570  0.66396035
  3       0.010  2.00  0.8438537  0.66554860
  3       0.100  0.25  0.8263375  0.62528448
  3       0.100  0.50  0.8238846  0.62064853
  3       0.100  1.00  0.8226192  0.61875712
  3       0.100  2.00  0.8213217  0.61532808
  3       1.000  0.25  0.7901180  0.54909107
  3       1.000  0.50  0.8013996  0.57404855
  3       1.000  1.00  0.7901797  0.55225155
  3       1.000  2.00  0.7901489  0.55239281

Accuracy was used to select the optimal model using the
 largest value.
The final values used for the model were degree = 3, scale =
 0.01 and C = 2.

degree = 2 scale = 0.010 cost =2.00 Accuracy= 0.8438229

(h) Overall, which approach seems to give the best results on this data? The best approach is the Radial kernel with an accuracy of .833 on the test data set. The final values used for the model were sigma = 0.05870129 and C = 1.

LS0tDQp0aXRsZTogIlN1cHBvcnQgVmVjdG9yIE1hY2hpbmUiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19mbG9hdDogVHJ1ZQ0KLS0tDQoNCiMgNQ0KDQoqKldlIGhhdmUgc2VlbiB0aGF0IHdlIGNhbiBmaXQgYW4gU1ZNIHdpdGggYSBub24tbGluZWFyIGtlcm5lbCBpbiBvcmRlciB0byBwZXJmb3JtIGNsYXNzaWZpY2F0aW9uIHVzaW5nIGEgbm9uLWxpbmVhciBkZWNpc2lvbiBib3VuZGFyeS4gV2Ugd2lsbCBub3cgc2VlIHRoYXQgd2UgY2FuIGFsc28gb2J0YWluIGEgbm9uLWxpbmVhciBkZWNpc2lvbiBib3VuZGFyeSBieSBwZXJmb3JtaW5nIGxvZ2lzdGljIHJlZ3Jlc3Npb24gdXNpbmcgbm9uLWxpbmVhciB0cmFuc2Zvcm1hdGlvbnMgb2YgdGhlIGZlYXR1cmVzLioqDQoNCioqKGEpIEdlbmVyYXRlIGEgZGF0YSBzZXQgd2l0aCBuID0gNTAwIGFuZCBwID0gMiwgc3VjaCB0aGF0IHRoZSBvYnNlcnZhdGlvbnMgYmVsb25nIHRvIHR3byBjbGFzc2VzIHdpdGggYSBxdWFkcmF0aWMgZGVjaXNpb24gYm91bmRhcnkgYmV0d2VlbiB0aGVtLioqDQpgYGB7cn0NCngxID0gcnVuaWYoNTAwKSAtIDAuNQ0KeDIgPSBydW5pZig1MDApIC0gMC41DQp5ID0gMSooeDFeMiAtIHgyXjIgPiAwKQ0KYGBgDQoNCg0KKiooYikgUGxvdCB0aGUgb2JzZXJ2YXRpb25zLCBjb2xvcmVkIGFjY29yZGluZyB0byB0aGVpciBjbGFzcyBsYWJlbHMuIFlvdXIgcGxvdCBzaG91bGQgZGlzcGxheSBYMSBvbiB0aGUgeC1heGlzLCBhbmQgWDIgb24gdGhlIHktYXhpcy4qKg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0KZGYgPSBkYXRhLmZyYW1lKHgxLHgyLHkpDQoNCg0KcGxvdCh4MVt5ID09MF0sIHgyW3kgPT0gMF0sIGNvbCA9ICdyZWQnLCB4bGFiID0gJ3gxJyx5bGFiID0gJ3gyJykNCnBvaW50cyh4MVt5PT0xXSx4Mlt5PT0xXSxjb2wgPSAnYmx1ZScpDQpgYGANCioqKGMpIEZpdCBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgdG8gdGhlIGRhdGEsIHVzaW5nIFgxIGFuZCBYMiBhcyBwcmVkaWN0b3JzLioqDQoNCg0KKiooZCkgQXBwbHkgdGhpcyBtb2RlbCB0byB0aGUgdHJhaW5pbmcgZGF0YSBpbiBvcmRlciB0byBvYnRhaW4gYSBwcmVkaWN0ZWQgY2xhc3MgbGFiZWwgZm9yIGVhY2ggdHJhaW5pbmcgb2JzZXJ2YXRpb24uIFBsb3QgdGhlIG9ic2VydmF0aW9ucywgY29sb3JlZCBhY2NvcmRpbmcgdG8gdGhlIHByZWRpY3RlZCBjbGFzcyBsYWJlbHMuIFRoZSBkZWNpc2lvbiBib3VuZGFyeSBzaG91bGQgYmUgbGluZWFyLioqDQpgYGB7cn0NCmdsbS5maXQgPSBnbG0oeSB+LiwgZGF0YSA9IGRmLCBmYW1pbHkgPSAiYmlub21pYWwiKQ0KZGYkY2xhc3M9IHByZWRpY3QoZ2xtLmZpdCwgdHlwZSA9ICJyZXNwb25zZSIpDQpkZiRwcmVkLmNsYXNzID0gaWZlbHNlKGRmJGNsYXNzID4gLjUsIDEsIDApDQoNCmRmfD4NCmdncGxvdChhZXMoeD0geDEsIHk9eDIpKSsNCiAgZ2VvbV9wb2ludChkYXRhID0gZGYsIG1hcHBpbmcgPSBhZXMoY29sb3VyID0gcHJlZC5jbGFzcykpKw0KICBsYWJzKHRpdGxlID0gIkxvZ2lzdGljIFJlZ3Jlc3Npb24gTW9kZWwiLCBzdWJ0aXRsZSA9ICJZIH4gWDEgKyBYMiIpKw0KICB0aGVtZV9taW5pbWFsKCkNCiAgDQpgYGANCioqKGUpIE5vdyBmaXQgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHRvIHRoZSBkYXRhIHVzaW5nIG5vbi1saW5lYXIgZnVuY3Rpb25zIG9mIFgxIGFuZCBYMiBhcyBwcmVkaWN0b3JzIChlLmcuIFgyMSAsIFgxIMOXWDIsIGxvZyhYMiksYW5kIHNvIGZvcnRoKS4qKg0KDQoNCioqKGYpIEFwcGx5IHRoaXMgbW9kZWwgdG8gdGhlIHRyYWluaW5nIGRhdGEgaW4gb3JkZXIgdG8gb2J0YWluIGEgcHJlZGljdGVkIGNsYXNzIGxhYmVsIGZvciBlYWNoIHRyYWluaW5nIG9ic2VydmF0aW9uLiBQbG90IHRoZSBvYnNlcnZhdGlvbnMsIGNvbG9yZWQgYWNjb3JkaW5nIHRvIHRoZSBwcmVkaWN0ZWQgY2xhc3MgbGFiZWxzLiBUaGUgZGVjaXNpb24gYm91bmRhcnkgc2hvdWxkIGJlIG9idmlvdXNseSBub24tbGluZWFyLiBJZiBpdCBpcyBub3QsIHRoZW4gcmVwZWF0IChhKS0oZSkgdW50aWwgeW91IGNvbWUgdXAgd2l0aCBhbiBleGFtcGxlIGluIHdoaWNoIHRoZSBwcmVkaWN0ZWQgY2xhc3MgbGFiZWxzIGFyZSBvYnZpb3VzbHkgbm9uLWxpbmVhci4qKg0KDQpgYGB7cn0NCmdsbS5maXQyID0gZ2xtKHkgfiB4MSAqIHgyLCBkYXRhID0gZGYsIGZhbWlseSA9ICJiaW5vbWlhbCIpDQpkZiRjbGFzcyA9IHByZWRpY3QoZ2xtLmZpdDIsIHR5cGUgPSAncmVzcG9uc2UnKQ0KZGYkcHJlZC5jbGFzcyA9IGlmZWxzZShkZiRjbGFzcyA+IC41LCAxLCAwKQ0KZGYkcHJlZC5jbGFzcyA9IGFzLmZhY3RvcihkZiRwcmVkLmNsYXNzKQ0KZGZ8Pg0KZ2dwbG90KG1hcHBpbmcgPSBhZXMoeDEseDIpKStnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBwcmVkLmNsYXNzKSkrbGFicyh0aXRsZSA9ICJMb2dpc3RpYyBSZWdyZXNzaW9uIHdpdGggYSBOb24gTGluZWFyIERlY2lzaW9uIEJvdW5kYXJ5Iiwgc3VidGl0bGUgPSAiWSB+IFgxICogWDIiKSt0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoqKihnKSBGaXQgYSBzdXBwb3J0IHZlY3RvciBjbGFzc2lmaWVyIHRvIHRoZSBkYXRhIHdpdGggWDEgYW5kIFgyIGFzIHByZWRpY3RvcnMuIE9idGFpbiBhIGNsYXNzIHByZWRpY3Rpb24gZm9yIGVhY2ggdHJhaW5pbmcgb2JzZXJ2YXRpb24uIFBsb3QgdGhlIG9ic2VydmF0aW9ucywgY29sb3JlZCBhY2NvcmRpbmcgdG8gdGhlIHByZWRpY3RlZCBjbGFzcyBsYWJlbHMuKioNCg0KYGBge3J9DQpsaWJyYXJ5KGNhcmV0KQ0Kc2V0LnNlZWQoMTIpDQpjb250cm9sLmN2PSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gMTApDQpzdm0gPSB0cmFpbihhcy5mYWN0b3IoeSkgfi4sIGRhdGEgPSBkZiwgbWV0aG9kID0gInN2bUxpbmVhciIsIHRyQ29udHJvbCA9IGNvbnRyb2wuY3YpDQoNCnN2bQ0KDQpkZiRzdm1fcHJlZCA9IHByZWRpY3Qoc3ZtLCBuZXdkYXRhID0gZGYpDQoNCg0KYGBgDQoNCmBgYHtyfQ0KZGZ8Pg0KZ2dwbG90KGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3IgPSBzdm1fcHJlZCkpK2dlb21fcG9pbnQoKSt0aGVtZV9taW5pbWFsKCkrbGFicyh0aXRsZSA9ICJTdXBwb3J0IFZlY3RvciBNYWNoaW5lIiwgc3VidGl0bGUgPSAiQ29sb3IgYnkgcHJlZGljdGVkIENsYXNzIikNCmBgYA0KKiooaCkgRml0IGEgU1ZNIHVzaW5nIGEgbm9uLWxpbmVhciBrZXJuZWwgdG8gdGhlIGRhdGEuIE9idGFpbiBhIGNsYXNzIHByZWRpY3Rpb24gZm9yIGVhY2ggdHJhaW5pbmcgb2JzZXJ2YXRpb24uIFBsb3QgdGhlIG9ic2VydmF0aW9ucywgY29sb3JlZCBhY2NvcmRpbmcgdG8gdGhlIHByZWRpY3RlZCBjbGFzcyBsYWJlbHMuKioNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIpDQpub25saW4uc3ZtID0gdHJhaW4oeSB+LixkYXRhID1kZiwgbWV0aG9kID0gInN2bVJhZGlhbCIsIHRyQ29udHJvbCA9IGNvbnRyb2wuY3YpDQoNCm5vbmxpbi5zdm0NCg0KZGYkc3ZtX3ByZWRfbm9ubGluID0gcHJlZGljdChub25saW4uc3ZtLG5ld2RhdGEgPSBkZikNCmBgYA0KDQpgYGB7cn0NCg0KZGZ8Pg0KICBnZ3Bsb3QoYWVzKHgxLHgyLCBjb2xvciA9IHN2bV9wcmVkX25vbmxpbikpK2dlb21fcG9pbnQoKStsYWJzKHRpdGxlID0gIlN1cHBvciBWZWN0b3IgTWFjaGluZSBXaXRoIFJhZGlhbCBLZXJuZWwiLCBzdWJ0aXRsZSA9ICJDb2xvcmVkIGJ5IFByZWRpY3RlZCBDbGFzcyIpK3RoZW1lX21pbmltYWwoKQ0KYGBgDQoqKihpKSBDb21tZW50IG9uIHlvdXIgcmVzdWx0cyoqIGNhcmV0KCkgaXMgaHlwZXJ0dW5uaW5nIHRoZSBTVk0gd2l0aCBhIFJhZGlhbCBrZXJuZWwgdGh1cyBpdCBpcyBhcHBlYXJzIHRvIGxvb2sgbGlrZSB0aGUgbGluZWFyIGtlcm5lbC4NCg0KDQojIDcgDQoNCioqSW4gdGhpcyBwcm9ibGVtLCB5b3Ugd2lsbCB1c2Ugc3VwcG9ydCB2ZWN0b3IgYXBwcm9hY2hlcyBpbiBvcmRlciB0byBwcmVkaWN0IHdoZXRoZXIgYSBnaXZlbiBjYXIgZ2V0cyBoaWdoIG9yIGxvdyBnYXMgbWlsZWFnZSBiYXNlZCBvbiB0aGUgQXV0byBkYXRhIHNldC4qKg0KDQpgYGB7cn0NCmxpYnJhcnkoSVNMUjIpDQpsaWJyYXJ5KGUxMDcxKQ0KZGF0YShBdXRvKQ0KYGBgDQoNCioqKGEpIENyZWF0ZSBhIGJpbmFyeSB2YXJpYWJsZSB0aGF0IHRha2VzIG9uIGEgMSBmb3IgY2FycyB3aXRoIGdhcyBtaWxlYWdlIGFib3ZlIHRoZSBtZWRpYW4sIGFuZCBhIDAgZm9yIGNhcnMgd2l0aCBnYXMgbWlsZWFnZSBiZWxvdyB0aGUgbWVkaWFuLioqDQpgYGB7cn0NCkF1dG8kbXBnID0gaWZlbHNlKCBBdXRvJG1wZyA+bWVkaWFuKEF1dG8kbXBnKSwxLDApDQpBdXRvJG1wZyA9IGFzLmZhY3RvcihBdXRvJG1wZykNCmBgYA0KDQoqKihiKSBGaXQgYSBzdXBwb3J0IHZlY3RvciBjbGFzc2lmaWVyIHRvIHRoZSBkYXRhIHdpdGggdmFyaW91cyB2YWx1ZXMgb2YgY29zdCxpbiBvcmRlciB0byBwcmVkaWN0IHdoZXRoZXIgYSBjYXIgZ2V0cyBoaWdoIG9yIGxvdyBnYXMgbWlsZWFnZS4gUmVwb3J0IHRoZSBjcm9zcy12YWxpZGF0aW9uIGVycm9ycyBhc3NvY2lhdGVkIHdpdGggZGlmZmVyZW50IHZhbHVlcyBvZiB0aGlzIHBhcmFtZXRlci4gQ29tbWVudCBvbiB5b3VyIHJlc3VsdHMuIE5vdGUgeW91IHdpbGwgbmVlZCB0byBmaXQgdGhlIGNsYXNzaWZpZXIgd2l0aG91dCB0aGUgZ2FzIG1pbGVhZ2UgdmFyaWFibGUgdG8gcHJvZHVjZSBzZW5zaWJsZSByZXN1bHRzLioqDQoNCmBgYHtyfQ0KDQpjb3N0LmdyaWQgPSBleHBhbmQuZ3JpZChDID0gYygwLjAxLC4xLDEsMTAsMTAwKSwgc2lnbWEgPSBOQSkNCm1wZy5zdm0gPSB0cmFpbihtcGd+LiAtbmFtZSwgZGF0YSA9IEF1dG8sbWV0aG9kID0gInN2bUxpbmVhciIsIHRyQ29udHJvbCA9IGNvbnRyb2wuY3YsIHR1bmVHcmlkID0gY29zdC5ncmlkKQ0KDQoNCm1wZy5zdm0NCmBgYA0KDQpUaGUgQmVzdCBDb3N0IHBhcmFtZXRlciBpcyAxZSswMCBhbmQgYW4gYWNjdXJhY3kgb2YgLjkxMy4NCg0KKiooYykgTm93IHJlcGVhdCAoYiksIHRoaXMgdGltZSB1c2luZyBTVk1zIHdpdGggcmFkaWFsIGFuZCBwb2x5bm9taWFsIGJhc2lzIGtlcm5lbHMsIHdpdGggZGlmZmVyZW50IHZhbHVlcyBvZiBnYW1tYSBhbmQgZGVncmVlIGFuZCBjb3N0LiBDb21tZW50IG9uIHlvdXIgcmVzdWx0cy4qKg0KYGBge3J9DQptcGcucmFkID0gdHJhaW4obXBnfi4gLW5hbWUsZGF0YSA9IEF1dG8sIHRyQ29udHJvbCA9IGNvbnRyb2wuY3YsIG1ldGhvZCA9ICJzdm1SYWRpYWwiKQ0KDQptcGcucmFkDQoNCg0KDQptcGcucG9seSA9IHRyYWluKG1wZ34uIC0gbmFtZSwgZGF0YSA9IEF1dG8sIHRyQ29udHJvbCA9IGNvbnRyb2wuY3YsIG1ldGhvZCA9InN2bVBvbHkiKQ0KbXBnLnBvbHkNCmBgYA0KVGhlIGJlc3QgQ29zdCBwYXJhbWV0ZXIgaXMgLiA1IHdpdGggYW4gYWNjdXJhY3kgb2YgLjkxLiBXaGlsZSB0aGUgYmVzdCBjb3N0IHBhcmFtZXRlciBmb3IgdGhlIFBvbHlub21pYWwga2VybmVsIGlzIFRoZSBmaW5hbCB2YWx1ZXMgdXNlZCBmb3IgdGhlIG1vZGVsIHdlcmUgZGVncmVlID0gMywgc2NhbGUgPSAwLjEgYW5kIEMgPSAxIGFuZCB3aXRoIGFuIGFjY3VyYWN5IG9mIC45MjEsDQoNCg0KKiooZCkgTWFrZSBzb21lIHBsb3RzIHRvIGJhY2sgdXAgeW91ciBhc3NlcnRpb25zIGluIChiKSBhbmQgKGMpKioNCg0KYGBge3J9DQpwbG90KG1wZy5zdm0pDQpgYGANClRoZSBoaWdoZXN0IGFjY3VyYWN5IGlzIGFyb3VuZCAuOTEgd2l0aCBhIGNvc3Qgb2YgLjEuIA0KYGBge3J9DQpwbG90KG1wZy5yYWQpDQpgYGANCkFjY3VyYWN5IGlzIGFyb3VuZCAuOTEgd2l0aCBhIENvc3QgcGFyYW1ldGVyIG9mIC41Lg0KYGBge3J9DQpwbG90KG1wZy5wb2x5KQ0KYGBgDQpUaGUgYmVzdCBDID0gMSwgU2NhbGUgPSAuMSwgYW5kIERlZ3JlZSA9IDMsIHdoaWNoIGZvbGxvd3Mgd2hhdCB3ZSB3YXMgcHJldmlvdXNseSBzdGF0ZWQuDQoNCg0KIyA4IA0KDQoqKjguIFRoaXMgcHJvYmxlbSBpbnZvbHZlcyB0aGUgT0ogZGF0YSBzZXQgd2hpY2ggaXMgcGFydCBvZiB0aGUgSVNMUjIgcGFja2FnZS4qKg0KDQoNCioqKGEpIENyZWF0ZSBhIHRyYWluaW5nIHNldCBjb250YWluaW5nIGEgcmFuZG9tIHNhbXBsZSBvZiA4MDAgb2JzZXJ2YXRpb25zLCBhbmQgYSB0ZXN0IHNldCBjb250YWluaW5nIHRoZSByZW1haW5pbmcgb2JzZXJ2YXRpb25zLioqDQpgYGB7cn0NCmRhdGEoT0opDQoNCm9qLmluZGV4ID0gc2FtcGxlKDE6bnJvdyhPSiksIDgwMCkNCm9qLnRyYWluID0gT0pbb2ouaW5kZXgsXQ0Kb2oudGVzdCA9IE9KWy1vai5pbmRleCxdDQpgYGANCg0KDQoqKihiKSBGaXQgYSBzdXBwb3J0IHZlY3RvciBjbGFzc2lmaWVyIHRvIHRoZSB0cmFpbmluZyBkYXRhIHVzaW5nIGNvc3QgPSAwLjAxLCB3aXRoIFB1cmNoYXNlIGFzIHRoZSByZXNwb25zZSBhbmQgdGhlIG90aGVyIHZhcmlhYmxlcyBhcyBwcmVkaWN0b3JzLiBVc2UgdGhlIHN1bW1hcnkoKSBmdW5jdGlvbiB0byBwcm9kdWNlIHN1bW1hcnkgc3RhdGlzdGljcywgYW5kIGRlc2NyaWJlIHRoZSByZXN1bHRzIG9idGFpbmVkLioqDQpgYGB7cn0NCm9qLnN2bSA9IHRyYWluKFB1cmNoYXNlIH4uICwgZGF0YSA9IG9qLnRyYWluLCBtZXRob2QgPSAic3ZtTGluZWFyIiwgdHJDb250cm9sID0gY29udHJvbC5jdiwgdHVuZUdyaWQgPSBleHBhbmQuZ3JpZChDID0gLjAxKSkNCg0Kb2ouc3ZtDQpgYGANClRoZSBhY2N1cmFjeSBvZiB0aGUgdGhpcyBTdXBwb3J0IE1hY2hpbmUgQ2xhc3NpZmllciB3aXRoIGEgTGluZWFyIEtlcm5lbCBpcyAuODMgY29udGFpbmluZyAxNyB2YXJpYWJsZXMuDQoNCg0KKiooYykgV2hhdCBhcmUgdGhlIHRyYWluaW5nIGFuZCB0ZXN0IGVycm9yIHJhdGVzPyoqDQoNCmBgYHtyfQ0Kb2ouc3ZtLnByZWQgPSBwcmVkaWN0KG9qLnN2bSxuZXdkYXRhID0gb2oudGVzdCkNCg0KY29uZnVzaW9uTWF0cml4KG9qLnN2bS5wcmVkLG9qLnRlc3QkUHVyY2hhc2UpDQpgYGANClRoZSB0ZXN0IGVycm9yIGlzIC44MSB3aGlsZSB0aGUgdHJhaW5pbmcgZXJyb3IgaXMgLjgzLCB3aGljaCBjb3VsZCBzdWdnZXN0IHNvbWUgb3ZlcmZpdHRpbmcgd2l0aGluIHRoZSBzdm1MaW5lYXIgbW9kZWwuDQoNCioqKGQpIFVzZSB0aGUgdHVuZSgpIGZ1bmN0aW9uIHRvIHNlbGVjdCBhbiBvcHRpbWFsIGNvc3QuIENvbnNpZGVyIHZhbHVlcyBpbiB0aGUgcmFuZ2UgMC4wMSB0byAxMC4qKg0KDQpgYGB7cn0NCm9qLnN2bSA9IHRyYWluKFB1cmNoYXNlIH4uLCBkYXRhID0gb2oudHJhaW4sIG1ldGhvZCA9ICJzdm1MaW5lYXIiLCB0ckNvbnRyb2wgPSBjb250cm9sLmN2LCB0dW5lR3JpZCA9IGNvc3QuZ3JpZCkNCg0Kb2ouc3ZtDQpgYGANCg0KKiooZSkgQ29tcHV0ZSB0aGUgdHJhaW5pbmcgYW5kIHRlc3QgZXJyb3IgcmF0ZXMgdXNpbmcgdGhpcyBuZXcgdmFsdWUgZm9yIGNvc3QuKioNCg0KYGBge3J9DQpvai5zdm0ucHJlZCA9IHByZWRpY3Qob2ouc3ZtLG5ld2RhdGEgPSBvai50ZXN0KQ0KDQpjb25mdXNpb25NYXRyaXgob2ouc3ZtLnByZWQsb2oudGVzdCRQdXJjaGFzZSkNCmBgYA0KVGhlIHRlc3QgZXJyb3IgaXMgLjgxIHdpdGggdGhlIENvc3QgcGFyYW1ldGVyIHNldCBmcm9tIC4wMSB0byAxMA0KDQoqKihmKSBSZXBlYXQgcGFydHMgKGIpIHRocm91Z2ggKGUpIHVzaW5nIGEgc3VwcG9ydCB2ZWN0b3IgbWFjaGluZSB3aXRoIGEgcmFkaWFsIGtlcm5lbC4gVXNlIHRoZSBkZWZhdWx0IHZhbHVlIGZvciBnYW1tYS4qKg0KYGBge3J9DQpvai5zdm0ucmFkID0gdHJhaW4oUHVyY2hhc2V+LixkYXRhID0gb2oudHJhaW4sIG1ldGhvZD0ic3ZtUmFkaWFsIiwgdHJDb250cm9sID0gY29udHJvbC5jdikNCg0Kb2ouc3ZtLnJhZA0KYGBgDQpUaGUgYmVzdCBhY2N1cmFjeSBpcyAuODI1Lg0KDQoNCmBgYHtyfQ0Kb2ouc3ZtLnJhZC5wcmVkID0gcHJlZGljdChvai5zdm0ucmFkLG5ld2RhdGEgPSBvai50ZXN0KQ0KDQpjb25mdXNpb25NYXRyaXgob2ouc3ZtLnJhZC5wcmVkLG9qLnRlc3QkUHVyY2hhc2UpDQpgYGANClRlc3QgZXJyb3IgaXMgLjgzMw0KDQoNCg0KYGBge3J9DQpvai5zdm0ucmFkID0gc3ZtKFB1cmNoYXNlfi4sZGF0YSA9IG9qLnRyYWluLGtlcm5lbCA9ICJyYWRpYWwiLCBjb3N0ID0gYyguMDEsLjEsMSwxMCksIGdhbW1hID0gYyguNSwxLDIsMyw0KSkNCnN1bW1hcnkob2ouc3ZtLnJhZCkNCg0KDQpvai5zdm0ucmFkLnByZWQgPSBwcmVkaWN0KG9qLnN2bS5yYWQsbmV3ZGF0YSA9IG9qLnRlc3QpDQoNCmNvbmZ1c2lvbk1hdHJpeChvai5zdm0ucmFkLnByZWQsb2oudGVzdCRQdXJjaGFzZSkNCmBgYA0KVGhlIGFjY3VyYWN5IGRyb3BwZWQgZG93biB0byAuNiBvbiB0aGUgdGVzdCBzZXQuDQoNCg0KKiooZykgUmVwZWF0IHBhcnRzIChiKSB0aHJvdWdoIChlKSB1c2luZyBhIHN1cHBvcnQgdmVjdG9yIG1hY2hpbmUgd2l0aCBhIHBvbHlub21pYWwga2VybmVsLiBTZXQgZGVncmVlID0gMi4qKg0KDQpgYGB7cn0NCm9qLnN2bS5wb2x5ID0gdHJhaW4oUHVyY2hhc2Ugfi4sIGRhdGEgPSBvai50cmFpbiwgbWV0aG9kID0gInN2bVBvbHkiLCB0ckNvbnRyb2wgPSBjb250cm9sLmN2LHR1bmVMZW5ndGggPSA0KQ0KDQpvai5zdm0ucG9seQ0KDQoNCm9qLnN2bS5wb2x5LnByZWQgPSBwcmVkaWN0KG9qLnN2bS5wb2x5LCBuZXdkYXRhID0gb2oudGVzdCkNCg0KY29uZnVzaW9uTWF0cml4KG9qLnN2bS5wb2x5LnByZWQsb2oudGVzdCRQdXJjaGFzZSkNCmBgYA0KZGVncmVlID0gMiAgICAgIHNjYWxlID0gMC4wMTAgICBjb3N0ID0yLjAwIEFjY3VyYWN5PSAwLjg0MzgyMjkgIA0KDQoNCioqKGgpIE92ZXJhbGwsIHdoaWNoIGFwcHJvYWNoIHNlZW1zIHRvIGdpdmUgdGhlIGJlc3QgcmVzdWx0cyBvbiB0aGlzIGRhdGE/KioNClRoZSBiZXN0IGFwcHJvYWNoIGlzIHRoZSBSYWRpYWwga2VybmVsIHdpdGggYW4gYWNjdXJhY3kgb2YgLjgzMyBvbiB0aGUgdGVzdCBkYXRhIHNldC4gVGhlIGZpbmFsIHZhbHVlcyB1c2VkIGZvciB0aGUgbW9kZWwgd2VyZSBzaWdtYSA9IDAuMDU4NzAxMjkgYW5kDQogQyA9IDEuDQoNCg0KDQoNCg0K