Problem 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. For instance, you can do this as follows:

x1=runif (500) -0.5
x2=runif (500) -0.5
y=1*(x1^2-x2^2 > 0)
str(df)
'data.frame':   500 obs. of  3 variables:
 $ x1: num  -0.212 0.288 -0.091 0.383 0.44 ...
 $ x2: num  -0.146 -0.134 -0.213 -0.42 -0.135 ...
 $ y : num  1 1 0 0 1 1 0 1 0 0 ...

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

library(ggplot2)
ggplot(df,aes(x1, x2, color = y)) +
  geom_point(alpha = 1)

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

set.seed(123) 
model_lr <- glm(y ~.,family='binomial',data=df)
summary(model_lr)

Call:
glm(formula = y ~ ., family = "binomial", data = df)

Deviance Residuals: 
   Min      1Q  Median      3Q     Max  
-1.227  -1.200   1.133   1.157   1.188  

Coefficients:
            Estimate Std. Error z value Pr(>|z|)
(Intercept)  0.04792    0.08949   0.535    0.592
x1          -0.03999    0.31516  -0.127    0.899
x2           0.11509    0.30829   0.373    0.709

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 692.86  on 499  degrees of freedom
Residual deviance: 692.71  on 497  degrees of freedom
AIC: 698.71

Number of Fisher Scoring iterations: 3

(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.

attach(df)
preds_lr <- predict(model_lr,df, type = "response")

plot(x1,x2, col = ifelse(preds_lr>=0.5,'red','blue'), 
     pch = 19,main = "Predicted obs. with linear decision boundary")

The above plot with the predicted observations from logistic regression has got a linear decision boundary and we can see that.

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

set.seed(123) 
model_lr2 <- glm(y ~ I(x1*x2)+poly(x2,2)+poly(x1,3),family='binomial',data=df)
summary(model_lr2)

(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.

preds_lr2 <- predict(model_lr2, df, type = "response")
plot(x1,x2, col = ifelse(preds_lr2>=0.5,'#2A363B','#FF847C'), 
     pch = 19,main = "Predicted obs. with linear decision boundary")

(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.

e107
Error: object 'e107' not found
plot(x1,x2, col = ifelse(svm.preds!=0,'#8482b3','#82b2b3'), 
     pch = 19,main = "Predicted obs. with svm")

(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(123)
svmfit2=svm(y~., data=df, kernel ="radial", gamma=1, cost=1)
plot(x1,x2, col = ifelse(svm.preds2!=0,'#7b0749','#e7b5d2'), 
     pch = 19,main = "Predicted obs. with non-linear kernel")

(i) Comment on your results.

library(caret)
preds_lr <- as.factor(ifelse(preds_lr >= 0.5,1,0))
confusionMatrix(preds_lr,df$y)
Confusion Matrix and Statistics

          Reference
Prediction   0   1
         0  40   9
         1 204 247
                                          
               Accuracy : 0.574           
                 95% CI : (0.5293, 0.6178)
    No Information Rate : 0.512           
    P-Value [Acc > NIR] : 0.003128        
                                          
                  Kappa : 0.1312          
                                          
 Mcnemar's Test P-Value : < 2.2e-16       
                                          
            Sensitivity : 0.1639          
            Specificity : 0.9648          
         Pos Pred Value : 0.8163          
         Neg Pred Value : 0.5477          
             Prevalence : 0.4880          
         Detection Rate : 0.0800          
   Detection Prevalence : 0.0980          
      Balanced Accuracy : 0.5644          
                                          
       'Positive' Class : 0               
                                          
preds_lr2 <- as.factor(ifelse(preds_lr2 >= 0.5,1,0))
confusionMatrix(preds_lr2,df$y)
confusionMatrix(svm.preds,df$y)
confusionMatrix(svm.preds2, df$y)

From the confusion matrices of all the models built above we can see that the accuarcies of the Support vector machine models are higher. Also logistic Regression with non-linear functions of the predictors shows an accuracy of 100%.

Problem 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.
(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.

library(ISLR)
data <- Auto
str(data)
'data.frame':   392 obs. of  9 variables:
 $ mpg         : num  18 15 18 16 17 15 14 14 14 15 ...
 $ cylinders   : num  8 8 8 8 8 8 8 8 8 8 ...
 $ displacement: num  307 350 318 304 302 429 454 440 455 390 ...
 $ horsepower  : num  130 165 150 150 140 198 220 215 225 190 ...
 $ weight      : num  3504 3693 3436 3433 3449 ...
 $ acceleration: num  12 11.5 11 12 10.5 10 9 8.5 10 8.5 ...
 $ year        : num  70 70 70 70 70 70 70 70 70 70 ...
 $ origin      : num  1 1 1 1 1 1 1 1 1 1 ...
 $ name        : Factor w/ 304 levels "amc ambassador brougham",..: 49 36 231 14 161 141 54 223 241 2 ...
summary(data)   # 22.75
      mpg          cylinders      displacement     horsepower        weight    
 Min.   : 9.00   Min.   :3.000   Min.   : 68.0   Min.   : 46.0   Min.   :1613  
 1st Qu.:17.00   1st Qu.:4.000   1st Qu.:105.0   1st Qu.: 75.0   1st Qu.:2225  
 Median :22.75   Median :4.000   Median :151.0   Median : 93.5   Median :2804  
 Mean   :23.45   Mean   :5.472   Mean   :194.4   Mean   :104.5   Mean   :2978  
 3rd Qu.:29.00   3rd Qu.:8.000   3rd Qu.:275.8   3rd Qu.:126.0   3rd Qu.:3615  
 Max.   :46.60   Max.   :8.000   Max.   :455.0   Max.   :230.0   Max.   :5140  
                                                                               
  acceleration        year           origin                      name    
 Min.   : 8.00   Min.   :70.00   Min.   :1.000   amc matador       :  5  
 1st Qu.:13.78   1st Qu.:73.00   1st Qu.:1.000   ford pinto        :  5  
 Median :15.50   Median :76.00   Median :1.000   toyota corolla    :  5  
 Mean   :15.54   Mean   :75.98   Mean   :1.577   amc gremlin       :  4  
 3rd Qu.:17.02   3rd Qu.:79.00   3rd Qu.:2.000   amc hornet        :  4  
 Max.   :24.80   Max.   :82.00   Max.   :3.000   chevrolet chevette:  4  
                                                 (Other)           :365  

From the output the median value of the mpg column is 22.75
Now we will create a binary variable which takes value for cars with gas mileage above the median and a 0 for cars with gas mileage below the median

data$class <- as.factor(ifelse(data$mpg > 22.75,1,0))

(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.

set.seed(123)
tune.out=tune(svm, class~., data=data, kernel = 'linear',ranges=list(cost=c(0.1,1,10,100,1000),
                                                   gamma=c(0.5,1,2,3,4)))
summary(tune.out)

Parameter tuning of ‘svm’:

- sampling method: 10-fold cross validation 

- best parameters:

- best performance: 0.01025641 

- Detailed performance results:
NA

The best cost parameter we can use from the 10 fold cross validations is 1 with a 0.5 gamma value. the minimum error recorded was 0.07392109. The cross validation errors for each combination of gamma and cost values are shown in the output above. We used the tune() to perform cross validation here.

(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.

set.seed(123)
tune.out.radial=tune(svm, class~., data=data, kernel ="radial",
              ranges=list(cost=c(0.1,1,10,100,1000),
                          gamma=c(0.5,1,2,3,4)))
summary(tune.out.radial)

Parameter tuning of ‘svm’:

- sampling method: 10-fold cross validation 

- best parameters:

- best performance: 0.04576923 

- Detailed performance results:
NA
set.seed(123)
tune.out.polynomial=tune(svm, class~., data=data, kernel ="polynomial",
              ranges=list(cost=c(0.1,1,10,100,1000),
                          gamma=c(0.5,1,2,3,4)))
summary(tune.out.polynomial)

Parameter tuning of ‘svm’:

- sampling method: 10-fold cross validation 

- best parameters:

- best performance: 0.03570513 

- Detailed performance results:
tune.out.polynomial$best.model

Call:
best.tune(method = svm, train.x = class ~ ., data = data, ranges = list(cost = c(0.1, 
    1, 10, 100, 1000), gamma = c(0.5, 1, 2, 3, 4)), kernel = "polynomial")


Parameters:
   SVM-Type:  C-classification 
 SVM-Kernel:  polynomial 
       cost:  1 
     degree:  3 
     coef.0:  0 

Number of Support Vectors:  83

SVM with radial kernel:
When I performed the SVM classification with radial kernel the best parameters was cost = 1 and gamma = 0.5. And the lowest error recorded was 0.04837794.
SVM with polynomial kernel:
When I performed the SVM classification with polynomial kernel the best parameters was cost = 0.1 and gamma = 0.5. And the lowest error recorded was 0.0961884.

So for the linear and the radial kernels the cost and the gamma values chosen are 1 and 0.5 with errors 0.07392109 and 0.04837794. While the polynomial kernel error was 0.0961884. which is bigger than the linear and radial kernel classification. The best model among the three is the model with the minimum error which is radial model with an error of 0.04837794

(d) Make some plots to back up your assertions in (b) and (c). Hint: In the lab, we used the plot() function for svm objects only in cases with p = 2. When p > 2, you can use the plot() function to create plots displaying pairs of variables at a time. Essentially, instead of typing plot(svmfit , dat) where svmfit contains your fitted model and dat is a data frame containing your data, you can type plot(svmfit , dat , x1∼x4)

# creation of a function to create plots with all the variables one by one
plotpairs = function(svmfit){
  for(name in names(data)[!(names(data) %in% c("mpg","class","name"))]){
    plot(svmfit,data,as.formula(paste("mpg",name,sep = "~")))
  }
}
# models with their kerenels and best parameters
set.seed(123)
svm.linear = svm(class~., data=data, kernal="linear", cost=1,gamma = 0.5)
svm.radial = svm(class~., data=data, kernal="radial", cost=1, gamma=0.5)
svm.polynomial = svm(class~., data=data, kernal="polynomial", cost=0.1, degree=3, 
                     gamma = 0.5)
# plotting to back up my assertions
plotpairs(svm.linear)

plotpairs(svm.radial)

NA

plotpairs(svm.polynomial)

Problem 8

This problem involves the OJ data set which is part of the ISLR package.
(a) Create a training set containing a random sample of 800 observations, and a test set containing the remaining observations.

oj <- OJ
str(oj)
'data.frame':   1070 obs. of  18 variables:
 $ Purchase      : Factor w/ 2 levels "CH","MM": 1 1 1 2 1 1 1 1 1 1 ...
 $ WeekofPurchase: num  237 239 245 227 228 230 232 234 235 238 ...
 $ StoreID       : num  1 1 1 1 7 7 7 7 7 7 ...
 $ PriceCH       : num  1.75 1.75 1.86 1.69 1.69 1.69 1.69 1.75 1.75 1.75 ...
 $ PriceMM       : num  1.99 1.99 2.09 1.69 1.69 1.99 1.99 1.99 1.99 1.99 ...
 $ DiscCH        : num  0 0 0.17 0 0 0 0 0 0 0 ...
 $ DiscMM        : num  0 0.3 0 0 0 0 0.4 0.4 0.4 0.4 ...
 $ SpecialCH     : num  0 0 0 0 0 0 1 1 0 0 ...
 $ SpecialMM     : num  0 1 0 0 0 1 1 0 0 0 ...
 $ LoyalCH       : num  0.5 0.6 0.68 0.4 0.957 ...
 $ SalePriceMM   : num  1.99 1.69 2.09 1.69 1.69 1.99 1.59 1.59 1.59 1.59 ...
 $ SalePriceCH   : num  1.75 1.75 1.69 1.69 1.69 1.69 1.69 1.75 1.75 1.75 ...
 $ PriceDiff     : num  0.24 -0.06 0.4 0 0 0.3 -0.1 -0.16 -0.16 -0.16 ...
 $ Store7        : Factor w/ 2 levels "No","Yes": 1 1 1 1 2 2 2 2 2 2 ...
 $ PctDiscMM     : num  0 0.151 0 0 0 ...
 $ PctDiscCH     : num  0 0 0.0914 0 0 ...
 $ ListPriceDiff : num  0.24 0.24 0.23 0 0 0.3 0.3 0.24 0.24 0.24 ...
 $ STORE         : num  1 1 1 1 0 0 0 0 0 0 ...
set.seed(123)
intrain <- createDataPartition(oj$Purchase, p = 0.746, list = FALSE)
train <- oj[intrain,]
test <- oj[-intrain,]

(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.

set.seed(123)
svmfit=svm(Purchase~., kernel = "linear",data=train,cost=0.01)
summary(svmfit)

Call:
svm(formula = Purchase ~ ., data = train, kernel = "linear", cost = 0.01)


Parameters:
   SVM-Type:  C-classification 
 SVM-Kernel:  linear 
       cost:  0.01 

Number of Support Vectors:  431

 ( 216 215 )


Number of Classes:  2 

Levels: 
 CH MM

The svm classifier used the kernel linear one and got 627 support vectors. For this model fit I used all the explanatory variables with Purchase as the target variable.

# predicting on the test data
pred=predict(svmfit,newdata =test)
confusionMatrix(pred,as.factor(test$Purchase))
Confusion Matrix and Statistics

          Reference
Prediction  CH  MM
        CH 136  25
        MM  29  80
                                         
               Accuracy : 0.8            
                 95% CI : (0.7472, 0.846)
    No Information Rate : 0.6111         
    P-Value [Acc > NIR] : 2.106e-11      
                                         
                  Kappa : 0.5821         
                                         
 Mcnemar's Test P-Value : 0.6831         
                                         
            Sensitivity : 0.8242         
            Specificity : 0.7619         
         Pos Pred Value : 0.8447         
         Neg Pred Value : 0.7339         
             Prevalence : 0.6111         
         Detection Rate : 0.5037         
   Detection Prevalence : 0.5963         
      Balanced Accuracy : 0.7931         
                                         
       'Positive' Class : CH             
                                         
# Predicting on the train data
pred.train=predict(svmfit,newdata =train)
confusionMatrix(pred.train,as.factor(train$Purchase))
Confusion Matrix and Statistics

          Reference
Prediction  CH  MM
        CH 433  75
        MM  55 237
                                          
               Accuracy : 0.8375          
                 95% CI : (0.8101, 0.8624)
    No Information Rate : 0.61            
    P-Value [Acc > NIR] : < 2e-16         
                                          
                  Kappa : 0.6545          
                                          
 Mcnemar's Test P-Value : 0.09563         
                                          
            Sensitivity : 0.8873          
            Specificity : 0.7596          
         Pos Pred Value : 0.8524          
         Neg Pred Value : 0.8116          
             Prevalence : 0.6100          
         Detection Rate : 0.5413          
   Detection Prevalence : 0.6350          
      Balanced Accuracy : 0.8235          
                                          
       'Positive' Class : CH              
                                          

From the confusion matrices we got the testing and training error rates are 100-80 = 20 and 100-83.75 = 16.25 respectively. The svm preformed well balancing both the sensitivity and specificity scores.

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

set.seed(123)
new_tune=tune(svm, Purchase~., data=train, kernel ="linear",
              ranges=list(cost=c(0.01,0.05,0.1,0.5,1,1.5,10)))
summary(new_tune)

Parameter tuning of ‘svm’:

- sampling method: 10-fold cross validation 

- best parameters:

- best performance: 0.16625 

- Detailed performance results:
new_tune$best.model

Call:
best.tune(method = svm, train.x = Purchase ~ ., data = train, ranges = list(cost = c(0.01, 
    0.05, 0.1, 0.5, 1, 1.5, 10)), kernel = "linear")


Parameters:
   SVM-Type:  C-classification 
 SVM-Kernel:  linear 
       cost:  0.05 

Number of Support Vectors:  356

The lowest error was 0.16625 with a best cost parameter value 0.05

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

set.seed(123)
svmfit.new=svm(Purchase~., kernel = "linear",data=train,cost=0.05)
summary(svmfit.new)

Call:
svm(formula = Purchase ~ ., data = train, kernel = "linear", cost = 0.05)


Parameters:
   SVM-Type:  C-classification 
 SVM-Kernel:  linear 
       cost:  0.05 

Number of Support Vectors:  356

 ( 179 177 )


Number of Classes:  2 

Levels: 
 CH MM
# predicting on the test data
pred.new=predict(svmfit.new,newdata =test)
confusionMatrix(pred.new,as.factor(test$Purchase))
Confusion Matrix and Statistics

          Reference
Prediction  CH  MM
        CH 136  21
        MM  29  84
                                          
               Accuracy : 0.8148          
                 95% CI : (0.7633, 0.8593)
    No Information Rate : 0.6111          
    P-Value [Acc > NIR] : 4.049e-13       
                                          
                  Kappa : 0.6157          
                                          
 Mcnemar's Test P-Value : 0.3222          
                                          
            Sensitivity : 0.8242          
            Specificity : 0.8000          
         Pos Pred Value : 0.8662          
         Neg Pred Value : 0.7434          
             Prevalence : 0.6111          
         Detection Rate : 0.5037          
   Detection Prevalence : 0.5815          
      Balanced Accuracy : 0.8121          
                                          
       'Positive' Class : CH              
                                          
# Predicting on the train data
pred.new2=predict(svmfit.new,newdata =train)
confusionMatrix(pred.new2,as.factor(train$Purchase))
Confusion Matrix and Statistics

          Reference
Prediction  CH  MM
        CH 433  75
        MM  55 237
                                          
               Accuracy : 0.8375          
                 95% CI : (0.8101, 0.8624)
    No Information Rate : 0.61            
    P-Value [Acc > NIR] : < 2e-16         
                                          
                  Kappa : 0.6545          
                                          
 Mcnemar's Test P-Value : 0.09563         
                                          
            Sensitivity : 0.8873          
            Specificity : 0.7596          
         Pos Pred Value : 0.8524          
         Neg Pred Value : 0.8116          
             Prevalence : 0.6100          
         Detection Rate : 0.5413          
   Detection Prevalence : 0.6350          
      Balanced Accuracy : 0.8235          
                                          
       'Positive' Class : CH              
                                          

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

confusionMatrix(pred.train,as.factor(train$Purchase))
Confusion Matrix and Statistics

          Reference
Prediction  CH  MM
        CH 488 312
        MM   0   0
                                         
               Accuracy : 0.61           
                 95% CI : (0.5752, 0.644)
    No Information Rate : 0.61           
    P-Value [Acc > NIR] : 0.5155         
                                         
                  Kappa : 0              
                                         
 Mcnemar's Test P-Value : <2e-16         
                                         
            Sensitivity : 1.00           
            Specificity : 0.00           
         Pos Pred Value : 0.61           
         Neg Pred Value :  NaN           
             Prevalence : 0.61           
         Detection Rate : 0.61           
   Detection Prevalence : 1.00           
      Balanced Accuracy : 0.50           
                                         
       'Positive' Class : CH             
                                         

Tuning the model

tune.radial$best.model

Call:
best.tune(method = svm, train.x = Purchase ~ ., data = train, ranges = list(cost = c(0.01, 
    0.05, 0.1, 0.5, 1, 1.5, 10)), kernel = "radial")


Parameters:
   SVM-Type:  C-classification 
 SVM-Kernel:  radial 
       cost:  1 

Number of Support Vectors:  365
confusionMatrix(pred.radialtrain,as.factor(train$Purchase))
Confusion Matrix and Statistics

          Reference
Prediction  CH  MM
        CH 446  76
        MM  42 236
                                         
               Accuracy : 0.8525         
                 95% CI : (0.826, 0.8764)
    No Information Rate : 0.61           
    P-Value [Acc > NIR] : < 2.2e-16      
                                         
                  Kappa : 0.6838         
                                         
 Mcnemar's Test P-Value : 0.002382       
                                         
            Sensitivity : 0.9139         
            Specificity : 0.7564         
         Pos Pred Value : 0.8544         
         Neg Pred Value : 0.8489         
             Prevalence : 0.6100         
         Detection Rate : 0.5575         
   Detection Prevalence : 0.6525         
      Balanced Accuracy : 0.8352         
                                         
       'Positive' Class : CH             
                                         

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

confusionMatrix(pred.train,as.factor(train$Purchase))
Confusion Matrix and Statistics

          Reference
Prediction  CH  MM
        CH 488 312
        MM   0   0
                                         
               Accuracy : 0.61           
                 95% CI : (0.5752, 0.644)
    No Information Rate : 0.61           
    P-Value [Acc > NIR] : 0.5155         
                                         
                  Kappa : 0              
                                         
 Mcnemar's Test P-Value : <2e-16         
                                         
            Sensitivity : 1.00           
            Specificity : 0.00           
         Pos Pred Value : 0.61           
         Neg Pred Value :  NaN           
             Prevalence : 0.61           
         Detection Rate : 0.61           
   Detection Prevalence : 1.00           
      Balanced Accuracy : 0.50           
                                         
       'Positive' Class : CH             
                                         

Tuning the model

tune.poly$best.model

Call:
best.tune(method = svm, train.x = Purchase ~ ., data = train, ranges = list(cost = c(0.01, 
    0.05, 0.1, 0.5, 1, 1.5, 10)), kernel = "polynomial")


Parameters:
   SVM-Type:  C-classification 
 SVM-Kernel:  polynomial 
       cost:  1.5 
     degree:  3 
     coef.0:  0 

Number of Support Vectors:  391
set.seed(123)
svmfit.polyrefit=svm(Purchase~., kernel = "polynomial",data=train,cost=1.5, degree = 3)
summary(svmfit.polyrefit)

Call:
svm(formula = Purchase ~ ., data = train, kernel = "polynomial", cost = 1.5, 
    degree = 3)


Parameters:
   SVM-Type:  C-classification 
 SVM-Kernel:  polynomial 
       cost:  1.5 
     degree:  3 
     coef.0:  0 

Number of Support Vectors:  391

 ( 196 195 )


Number of Classes:  2 

Levels: 
 CH MM
# predicting on the test data
pred.polytest=predict(svmfit.polyrefit,newdata =test)
confusionMatrix(pred.polytest,as.factor(test$Purchase))
Confusion Matrix and Statistics

          Reference
Prediction  CH  MM
        CH 140  30
        MM  25  75
                                          
               Accuracy : 0.7963          
                 95% CI : (0.7433, 0.8427)
    No Information Rate : 0.6111          
    P-Value [Acc > NIR] : 5.34e-11        
                                          
                  Kappa : 0.5677          
                                          
 Mcnemar's Test P-Value : 0.5896          
                                          
            Sensitivity : 0.8485          
            Specificity : 0.7143          
         Pos Pred Value : 0.8235          
         Neg Pred Value : 0.7500          
             Prevalence : 0.6111          
         Detection Rate : 0.5185          
   Detection Prevalence : 0.6296          
      Balanced Accuracy : 0.7814          
                                          
       'Positive' Class : CH              
                                          
# Predicting on the train data
pred.polytrain=predict(svmfit.polyrefit,newdata =train)
confusionMatrix(pred.polytrain,as.factor(train$Purchase))
Confusion Matrix and Statistics

          Reference
Prediction  CH  MM
        CH 458  96
        MM  30 216
                                          
               Accuracy : 0.8425          
                 95% CI : (0.8154, 0.8671)
    No Information Rate : 0.61            
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.6559          
                                          
 Mcnemar's Test P-Value : 7.011e-09       
                                          
            Sensitivity : 0.9385          
            Specificity : 0.6923          
         Pos Pred Value : 0.8267          
         Neg Pred Value : 0.8780          
             Prevalence : 0.6100          
         Detection Rate : 0.5725          
   Detection Prevalence : 0.6925          
      Balanced Accuracy : 0.8154          
                                          
       'Positive' Class : CH              
                                          

(h) Overall, which approach seems to give the best results on this data?

I am creating a dataframe of test and train accuracies with the used models below. I subtracted the accuracy from 100% to get the error since total of accuracy + error = 100%. We can also find the classification error rate by just counting the total number of false positives and false negatives from the table in the confusion matrix and divide it by the total observations.

We can see that the svm classifier with a radial kernel has got the minimum error among all the kernels. I used the coast =1 after tuning and finding out the optimal parameter.

LS0tDQp0aXRsZTogIkFzc2lnbm1lbnQgOCINCmF1dGhvcjogIk1vdW5pY2EgTWlyaXlhbGEiDQpkYXRlOiAiNC8yNS8yMDIxIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICBkZl9wcmludDogcGFnZWQNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSxtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgY2FjaGUgPSBUUlVFKQ0KYGBgICANCiAgDQojIyMgUHJvYmxlbSA1ICANCioqV2UgaGF2ZSBzZWVuIHRoYXQgd2UgY2FuIGZpdCBhbiBTVk0gd2l0aCBhIG5vbi1saW5lYXIga2VybmVsIGluIG9yZGVyIHRvIHBlcmZvcm0gY2xhc3NpZmljYXRpb24gdXNpbmcgYSBub24tbGluZWFyIGRlY2lzaW9uIGJvdW5kYXJ5LiBXZSB3aWxsIG5vdyBzZWUgdGhhdCB3ZSBjYW4gYWxzbyBvYnRhaW4gYSBub24tbGluZWFyIGRlY2lzaW9uIGJvdW5kYXJ5IGJ5IHBlcmZvcm1pbmcgbG9naXN0aWMgcmVncmVzc2lvbiB1c2luZyBub24tbGluZWFyIHRyYW5zZm9ybWF0aW9ucyBvZiB0aGUgZmVhdHVyZXMuKiogIA0KKiooYSkgR2VuZXJhdGUgYSBkYXRhIHNldCB3aXRoIG4gPSA1MDAgYW5kIHAgPSAyLCBzdWNoIHRoYXQgdGhlIG9ic2VydmF0aW9ucyBiZWxvbmcgdG8gdHdvIGNsYXNzZXMgd2l0aCBhIHF1YWRyYXRpYyBkZWNpc2lvbiBib3VuZGFyeSBiZXR3ZWVuIHRoZW0uIEZvciBpbnN0YW5jZSwgeW91IGNhbiBkbyB0aGlzIGFzIGZvbGxvd3M6KiogIA0KYGBge3J9DQpsaWJyYXJ5KGdsbW5ldCkNCnNldC5zZWVkKDEyMykNCngxPXJ1bmlmICg1MDApIC0wLjUNCngyPXJ1bmlmICg1MDApIC0wLjUNCnk9MSooeDFeMi14Ml4yID4gMCkNCmRmIDwtIGRhdGEuZnJhbWUoeDEseDIseSkNCmBgYA0KICANCiAgDQpgYGB7cn0NCnN0cihkZikNCmRmJHkgPC0gYXMuZmFjdG9yKGRmJHkpDQpgYGANCg0KKiooYikgUGxvdCB0aGUgb2JzZXJ2YXRpb25zLCBjb2xvcmVkIGFjY29yZGluZyB0byB0aGVpciBjbGFzcyBsYWJlbHMuIFlvdXIgcGxvdCBzaG91bGQgZGlzcGxheSBYMSBvbiB0aGUgeC1heGlzLCBhbmQgWDIgb24gdGhlIHlheGlzLioqICANCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KZ2dwbG90KGRmLGFlcyh4MSwgeDIsIGNvbG9yID0geSkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDEpDQoNCmBgYA0KICANCioqKGMpIEZpdCBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgdG8gdGhlIGRhdGEsIHVzaW5nIFgxIGFuZCBYMiBhcyBwcmVkaWN0b3JzLioqICANCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKSANCm1vZGVsX2xyIDwtIGdsbSh5IH4uLGZhbWlseT0nYmlub21pYWwnLGRhdGE9ZGYpDQpzdW1tYXJ5KG1vZGVsX2xyKQ0KYGBgDQogIA0KKiooZCkgQXBwbHkgdGhpcyBtb2RlbCB0byB0aGUgdHJhaW5pbmcgZGF0YSBpbiBvcmRlciB0byBvYnRhaW4gYSBwcmVkaWN0ZWQgY2xhc3MgbGFiZWwgZm9yIGVhY2ggdHJhaW5pbmcgb2JzZXJ2YXRpb24uIFBsb3QgdGhlIG9ic2VydmF0aW9ucywgY29sb3JlZCBhY2NvcmRpbmcgdG8gdGhlIHByZWRpY3RlZCBjbGFzcyBsYWJlbHMuIFRoZSBkZWNpc2lvbiBib3VuZGFyeSBzaG91bGQgYmUgbGluZWFyLioqICANCmBgYHtyfQ0KYXR0YWNoKGRmKQ0KcHJlZHNfbHIgPC0gcHJlZGljdChtb2RlbF9scixkZiwgdHlwZSA9ICJyZXNwb25zZSIpDQoNCnBsb3QoeDEseDIsIGNvbCA9IGlmZWxzZShwcmVkc19scj49MC41LCdyZWQnLCdibHVlJyksIA0KICAgICBwY2ggPSAxOSxtYWluID0gIlByZWRpY3RlZCBvYnMuIHdpdGggbGluZWFyIGRlY2lzaW9uIGJvdW5kYXJ5IikNCg0KYGBgICANCiAgDQpUaGUgYWJvdmUgcGxvdCB3aXRoIHRoZSBwcmVkaWN0ZWQgb2JzZXJ2YXRpb25zIGZyb20gbG9naXN0aWMgcmVncmVzc2lvbiBoYXMgZ290IGEgbGluZWFyIGRlY2lzaW9uIGJvdW5kYXJ5IGFuZCB3ZSBjYW4gc2VlIHRoYXQuICANCiAgDQoqKihlKSBOb3cgZml0IGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB0byB0aGUgZGF0YSB1c2luZyBub24tbGluZWFyIGZ1bmN0aW9ucyBvZiBYMSBhbmQgWDIgYXMgcHJlZGljdG9ycyAoZS5nLiBYMiAxICwgWDHDl1gyLCBsb2coWDIpLCBhbmQgc28gZm9ydGgpLioqICANCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKSANCm1vZGVsX2xyMiA8LSBnbG0oeSB+IEkoeDEqeDIpK3BvbHkoeDIsMikrcG9seSh4MSwzKSxmYW1pbHk9J2Jpbm9taWFsJyxkYXRhPWRmKQ0Kc3VtbWFyeShtb2RlbF9scjIpDQpgYGANCiAgDQoqKihmKSBBcHBseSB0aGlzIG1vZGVsIHRvIHRoZSB0cmFpbmluZyBkYXRhIGluIG9yZGVyIHRvIG9idGFpbiBhIHByZWRpY3RlZCBjbGFzcyBsYWJlbCBmb3IgZWFjaCB0cmFpbmluZyBvYnNlcnZhdGlvbi4gUGxvdCB0aGUgb2JzZXJ2YXRpb25zLCBjb2xvcmVkIGFjY29yZGluZyB0byB0aGUgcHJlZGljdGVkIGNsYXNzIGxhYmVscy4gVGhlIGRlY2lzaW9uIGJvdW5kYXJ5IHNob3VsZCBiZSBvYnZpb3VzbHkgbm9uLWxpbmVhci4gSWYgaXQgaXMgbm90LCB0aGVuIHJlcGVhdCAoYSktKGUpIHVudGlsIHlvdSBjb21lIHVwIHdpdGggYW4gZXhhbXBsZSBpbiB3aGljaCB0aGUgcHJlZGljdGVkIGNsYXNzIGxhYmVscyBhcmUgb2J2aW91c2x5IG5vbi1saW5lYXIuKiogIA0KYGBge3J9DQpwcmVkc19scjIgPC0gcHJlZGljdChtb2RlbF9scjIsIGRmLCB0eXBlID0gInJlc3BvbnNlIikNCmBgYCAgDQogIA0KYGBge3J9DQpwbG90KHgxLHgyLCBjb2wgPSBpZmVsc2UocHJlZHNfbHIyPj0wLjUsJyMyQTM2M0InLCcjRkY4NDdDJyksIA0KICAgICBwY2ggPSAxOSxtYWluID0gIlByZWRpY3RlZCBvYnMuIHdpdGggbGluZWFyIGRlY2lzaW9uIGJvdW5kYXJ5IikNCmBgYA0KICANCioqKGcpIEZpdCBhIHN1cHBvcnQgdmVjdG9yIGNsYXNzaWZpZXIgdG8gdGhlIGRhdGEgd2l0aCBYMSBhbmQgWDIgYXMgcHJlZGljdG9ycy4gT2J0YWluIGEgY2xhc3MgcHJlZGljdGlvbiBmb3IgZWFjaCB0cmFpbmluZyBvYnNlcnZhdGlvbi4gUGxvdCB0aGUgb2JzZXJ2YXRpb25zLCBjb2xvcmVkIGFjY29yZGluZyB0byB0aGUgcHJlZGljdGVkIGNsYXNzIGxhYmVscy4qKiAgDQpgYGB7cn0NCmxpYnJhcnkoZTEwNzEpDQpzZXQuc2VlZCgxMjMpDQpzdm1maXQgPSBzdm0oeX4uLCBkYXRhPWRmLGNvc3QgPTEwLCBzY2FsZT1GQUxTRSkNCg0Kc3ZtLnByZWRzIDwtIHByZWRpY3Qoc3ZtZml0LG5ld2RhdGE9ZGYsdHlwZT0ncmVzcG9uc2UnKQ0KDQpgYGANCiAgDQpgYGB7cn0NCnBsb3QoeDEseDIsIGNvbCA9IGlmZWxzZShzdm0ucHJlZHMhPTAsJyM4NDgyYjMnLCcjODJiMmIzJyksIA0KICAgICBwY2ggPSAxOSxtYWluID0gIlByZWRpY3RlZCBvYnMuIHdpdGggc3ZtIikNCmBgYA0KICANCioqKGgpIEZpdCBhIFNWTSB1c2luZyBhIG5vbi1saW5lYXIga2VybmVsIHRvIHRoZSBkYXRhLiBPYnRhaW4gYSBjbGFzcyBwcmVkaWN0aW9uIGZvciBlYWNoIHRyYWluaW5nIG9ic2VydmF0aW9uLiBQbG90IHRoZSBvYnNlcnZhdGlvbnMsIGNvbG9yZWQgYWNjb3JkaW5nIHRvIHRoZSBwcmVkaWN0ZWQgY2xhc3MgbGFiZWxzLioqICANCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0Kc3ZtZml0MiA9IHN2bSh5fi4sIGRhdGE9ZGYsIGtlcm5lbCA9InJhZGlhbCIsIGdhbW1hPTEsIGNvc3Q9MSkNCg0Kc3ZtLnByZWRzMiA8LSBwcmVkaWN0KHN2bWZpdDIsbmV3ZGF0YT1kZix0eXBlPSdyZXNwb25zZScpDQpgYGANCiAgDQpgYGB7cn0NCnBsb3QoeDEseDIsIGNvbCA9IGlmZWxzZShzdm0ucHJlZHMyIT0wLCcjN2IwNzQ5JywnI2U3YjVkMicpLCANCiAgICAgcGNoID0gMTksbWFpbiA9ICJQcmVkaWN0ZWQgb2JzLiB3aXRoIG5vbi1saW5lYXIga2VybmVsIikNCmBgYA0KICANCioqKGkpIENvbW1lbnQgb24geW91ciByZXN1bHRzLioqICANCmBgYHtyfQ0KbGlicmFyeShjYXJldCkNCnByZWRzX2xyIDwtIGFzLmZhY3RvcihpZmVsc2UocHJlZHNfbHIgPj0gMC41LDEsMCkpDQpjb25mdXNpb25NYXRyaXgocHJlZHNfbHIsZGYkeSkNCmBgYA0KICANCmBgYHtyfQ0KcHJlZHNfbHIyIDwtIGFzLmZhY3RvcihpZmVsc2UocHJlZHNfbHIyID49IDAuNSwxLDApKQ0KY29uZnVzaW9uTWF0cml4KHByZWRzX2xyMixkZiR5KQ0KYGBgDQogIA0KYGBge3J9DQpjb25mdXNpb25NYXRyaXgoc3ZtLnByZWRzLGRmJHkpDQpgYGANCiAgDQpgYGB7cn0NCmNvbmZ1c2lvbk1hdHJpeChzdm0ucHJlZHMyLCBkZiR5KQ0KYGBgDQogIA0KRnJvbSB0aGUgY29uZnVzaW9uIG1hdHJpY2VzIG9mIGFsbCB0aGUgbW9kZWxzIGJ1aWx0IGFib3ZlIHdlIGNhbiBzZWUgdGhhdCB0aGUgYWNjdWFyY2llcyBvZiB0aGUgU3VwcG9ydCB2ZWN0b3IgbWFjaGluZSBtb2RlbHMgYXJlIGhpZ2hlci4gQWxzbyBsb2dpc3RpYyBSZWdyZXNzaW9uIHdpdGggbm9uLWxpbmVhciBmdW5jdGlvbnMgb2YgdGhlIHByZWRpY3RvcnMgc2hvd3MgYW4gYWNjdXJhY3kgb2YgMTAwJS4gIA0KICANCiMjIyBQcm9ibGVtIDcgDQoqKkluIHRoaXMgcHJvYmxlbSwgeW91IHdpbGwgdXNlIHN1cHBvcnQgdmVjdG9yIGFwcHJvYWNoZXMgaW4gb3JkZXIgdG8gcHJlZGljdCB3aGV0aGVyIGEgZ2l2ZW4gY2FyIGdldHMgaGlnaCBvciBsb3cgZ2FzIG1pbGVhZ2UgYmFzZWQgb24gdGhlIGBBdXRvYCBkYXRhIHNldC4qKiAgDQoqKihhKSBDcmVhdGUgYSBiaW5hcnkgdmFyaWFibGUgdGhhdCB0YWtlcyBvbiBhIDEgZm9yIGNhcnMgd2l0aCBnYXMgbWlsZWFnZSBhYm92ZSB0aGUgbWVkaWFuLCBhbmQgYSAwIGZvciBjYXJzIHdpdGggZ2FzIG1pbGVhZ2UgYmVsb3cgdGhlIG1lZGlhbi4qKiAgDQpgYGB7cn0NCmxpYnJhcnkoSVNMUikNCmRhdGEgPC0gQXV0bw0Kc3RyKGRhdGEpDQpzdW1tYXJ5KGRhdGEpICAgIyAyMi43NQ0KYGBgDQogIA0KRnJvbSB0aGUgb3V0cHV0IHRoZSBtZWRpYW4gdmFsdWUgb2YgdGhlIG1wZyBjb2x1bW4gaXMgYDIyLjc1YCAgDQpOb3cgd2Ugd2lsbCBjcmVhdGUgYSBiaW5hcnkgdmFyaWFibGUgd2hpY2ggdGFrZXMgdmFsdWUgZm9yIGNhcnMgd2l0aCBnYXMgbWlsZWFnZSBhYm92ZSB0aGUgbWVkaWFuIGFuZCBhIDAgZm9yIGNhcnMgd2l0aCBnYXMgbWlsZWFnZSBiZWxvdyB0aGUgbWVkaWFuICANCmBgYHtyfQ0KZGF0YSRjbGFzcyA8LSBhcy5mYWN0b3IoaWZlbHNlKGRhdGEkbXBnID4gMjIuNzUsMSwwKSkNCg0KYGBgDQogIA0KKiooYikgRml0IGEgc3VwcG9ydCB2ZWN0b3IgY2xhc3NpZmllciB0byB0aGUgZGF0YSB3aXRoIHZhcmlvdXMgdmFsdWVzIG9mIGNvc3QsIGluIG9yZGVyIHRvIHByZWRpY3Qgd2hldGhlciBhIGNhciBnZXRzIGhpZ2ggb3IgbG93IGdhcyBtaWxlYWdlLiBSZXBvcnQgdGhlIGNyb3NzLXZhbGlkYXRpb24gZXJyb3JzIGFzc29jaWF0ZWQgd2l0aCBkaWZmZXJlbnQgdmFsdWVzIG9mIHRoaXMgcGFyYW1ldGVyLiBDb21tZW50IG9uIHlvdXIgcmVzdWx0cy4qKiAgDQpgYGB7cn0NCnNldC5zZWVkKDEyMykNCnR1bmUub3V0PXR1bmUoc3ZtLCBjbGFzc34uLCBkYXRhPWRhdGEsIGtlcm5lbCA9ICdsaW5lYXInLHJhbmdlcz1saXN0KGNvc3Q9YygwLjEsMSwxMCwxMDAsMTAwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnYW1tYT1jKDAuNSwxLDIsMyw0KSkpDQpzdW1tYXJ5KHR1bmUub3V0KQ0KYGBgDQogIA0KVGhlIGJlc3QgY29zdCBwYXJhbWV0ZXIgd2UgY2FuIHVzZSBmcm9tIHRoZSAxMCBmb2xkIGNyb3NzIHZhbGlkYXRpb25zIGlzIDEgd2l0aCBhIDAuNSBnYW1tYSB2YWx1ZS4gdGhlIG1pbmltdW0gZXJyb3IgcmVjb3JkZWQgd2FzIGAwLjA3MzkyMTA5CWAuIFRoZSBjcm9zcyB2YWxpZGF0aW9uIGVycm9ycyBmb3IgZWFjaCBjb21iaW5hdGlvbiBvZiBnYW1tYSBhbmQgY29zdCB2YWx1ZXMgYXJlIHNob3duIGluIHRoZSBvdXRwdXQgYWJvdmUuIFdlIHVzZWQgdGhlIHR1bmUoKSB0byBwZXJmb3JtIGNyb3NzIHZhbGlkYXRpb24gaGVyZS4gIA0KICAgDQoqKihjKSBOb3cgcmVwZWF0IChiKSwgdGhpcyB0aW1lIHVzaW5nIFNWTXMgd2l0aCByYWRpYWwgYW5kIHBvbHlub21pYWwgYmFzaXMga2VybmVscywgd2l0aCBkaWZmZXJlbnQgdmFsdWVzIG9mIGdhbW1hIGFuZCBkZWdyZWUgYW5kIGNvc3QuIENvbW1lbnQgb24geW91ciByZXN1bHRzLioqICANCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KdHVuZS5vdXQucmFkaWFsPXR1bmUoc3ZtLCBjbGFzc34uLCBkYXRhPWRhdGEsIGtlcm5lbCA9InJhZGlhbCIsDQogICAgICAgICAgICAgIHJhbmdlcz1saXN0KGNvc3Q9YygwLjEsMSwxMCwxMDAsMTAwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGdhbW1hPWMoMC41LDEsMiwzLDQpKSkNCnN1bW1hcnkodHVuZS5vdXQucmFkaWFsKQ0KYGBgDQogIA0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQp0dW5lLm91dC5wb2x5bm9taWFsPXR1bmUoc3ZtLCBjbGFzc34uLCBkYXRhPWRhdGEsIGtlcm5lbCA9InBvbHlub21pYWwiLA0KICAgICAgICAgICAgICByYW5nZXM9bGlzdChjb3N0PWMoMC4xLDEsMTAsMTAwLDEwMDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBnYW1tYT1jKDAuNSwxLDIsMyw0KSkpDQpzdW1tYXJ5KHR1bmUub3V0LnBvbHlub21pYWwpDQp0dW5lLm91dC5wb2x5bm9taWFsJGJlc3QubW9kZWwNCmBgYA0KICANCioqU1ZNIHdpdGggcmFkaWFsIGtlcm5lbDoqKiAgDQpXaGVuIEkgcGVyZm9ybWVkIHRoZSBTVk0gY2xhc3NpZmljYXRpb24gd2l0aCByYWRpYWwga2VybmVsIHRoZSBiZXN0IHBhcmFtZXRlcnMgd2FzIGNvc3QgPSAxIGFuZCBnYW1tYSA9IDAuNS4gQW5kIHRoZSBsb3dlc3QgZXJyb3IgcmVjb3JkZWQgd2FzIGAwLjA0ODM3Nzk0YC4gIA0KKipTVk0gd2l0aCBwb2x5bm9taWFsIGtlcm5lbDoqKiAgDQpXaGVuIEkgcGVyZm9ybWVkIHRoZSBTVk0gY2xhc3NpZmljYXRpb24gd2l0aCBwb2x5bm9taWFsIGtlcm5lbCB0aGUgYmVzdCBwYXJhbWV0ZXJzIHdhcyBjb3N0ID0gMC4xCSBhbmQgZ2FtbWEgPSAwLjUuIEFuZCB0aGUgbG93ZXN0IGVycm9yIHJlY29yZGVkIHdhcyBgMC4wOTYxODg0YC4gIA0KDQpTbyBmb3IgdGhlIGxpbmVhciBhbmQgdGhlIHJhZGlhbCBrZXJuZWxzIHRoZSBjb3N0IGFuZCB0aGUgZ2FtbWEgdmFsdWVzIGNob3NlbiBhcmUgMSBhbmQgMC41IHdpdGggZXJyb3JzIDAuMDczOTIxMDkgYW5kIDAuMDQ4Mzc3OTQuIFdoaWxlIHRoZSBwb2x5bm9taWFsIGtlcm5lbCBlcnJvciB3YXMgMC4wOTYxODg0LiB3aGljaCBpcyBiaWdnZXIgdGhhbiB0aGUgbGluZWFyIGFuZCByYWRpYWwga2VybmVsIGNsYXNzaWZpY2F0aW9uLiBUaGUgYmVzdCBtb2RlbCBhbW9uZyB0aGUgdGhyZWUgaXMgdGhlIG1vZGVsIHdpdGggdGhlIG1pbmltdW0gZXJyb3Igd2hpY2ggaXMgcmFkaWFsIG1vZGVsIHdpdGggYW4gZXJyb3Igb2YgYDAuMDQ4Mzc3OTRgICANCiAgDQoqKihkKSBNYWtlIHNvbWUgcGxvdHMgdG8gYmFjayB1cCB5b3VyIGFzc2VydGlvbnMgaW4gKGIpIGFuZCAoYykuIEhpbnQ6IEluIHRoZSBsYWIsIHdlIHVzZWQgdGhlIHBsb3QoKSBmdW5jdGlvbiBmb3Igc3ZtIG9iamVjdHMgb25seSBpbiBjYXNlcyB3aXRoIHAgPSAyLiBXaGVuIHAgPiAyLCB5b3UgY2FuIHVzZSB0aGUgcGxvdCgpIGZ1bmN0aW9uIHRvIGNyZWF0ZSBwbG90cyBkaXNwbGF5aW5nIHBhaXJzIG9mIHZhcmlhYmxlcyBhdCBhIHRpbWUuIEVzc2VudGlhbGx5LCBpbnN0ZWFkIG9mIHR5cGluZyBwbG90KHN2bWZpdCAsIGRhdCkgd2hlcmUgc3ZtZml0IGNvbnRhaW5zIHlvdXIgZml0dGVkIG1vZGVsIGFuZCBkYXQgaXMgYSBkYXRhIGZyYW1lIGNvbnRhaW5pbmcgeW91ciBkYXRhLCB5b3UgY2FuIHR5cGUgcGxvdChzdm1maXQgLCBkYXQgLCB4MeKIvHg0KSoqICANCmBgYHtyfQ0KIyBjcmVhdGlvbiBvZiBhIGZ1bmN0aW9uIHRvIGNyZWF0ZSBwbG90cyB3aXRoIGFsbCB0aGUgdmFyaWFibGVzIG9uZSBieSBvbmUNCnBsb3RwYWlycyA9IGZ1bmN0aW9uKHN2bWZpdCl7DQogIGZvcihpIGluIG5hbWVzKGRhdGEpWyEobmFtZXMoZGF0YSkgJWluJSBjKCJtcGciLCJjbGFzcyIsIm5hbWUiKSldKXsNCiAgICBwbG90KHN2bWZpdCxkYXRhLGFzLmZvcm11bGEocGFzdGUoIm1wZyIsaSxzZXAgPSAifiIpKSkNCiAgfQ0KfQ0KYGBgDQogIA0KYGBge3J9DQojIG1vZGVscyB3aXRoIHRoZWlyIGtlcmVuZWxzIGFuZCBiZXN0IHBhcmFtZXRlcnMNCnNldC5zZWVkKDEyMykNCnN2bS5saW5lYXIgPSBzdm0oY2xhc3N+LiwgZGF0YT1kYXRhLCBrZXJuYWw9ImxpbmVhciIsIGNvc3Q9MSxnYW1tYSA9IDAuNSkNCnN2bS5yYWRpYWwgPSBzdm0oY2xhc3N+LiwgZGF0YT1kYXRhLCBrZXJuYWw9InJhZGlhbCIsIGNvc3Q9MSwgZ2FtbWE9MC41KQ0Kc3ZtLnBvbHlub21pYWwgPSBzdm0oY2xhc3N+LiwgZGF0YT1kYXRhLCBrZXJuYWw9InBvbHlub21pYWwiLCBjb3N0PTAuMSwgZGVncmVlPTMsIA0KICAgICAgICAgICAgICAgICAgICAgZ2FtbWEgPSAwLjUpDQpgYGANCiAgDQpgYGB7cn0NCiMgcGxvdHRpbmcgdG8gYmFjayB1cCBteSBhc3NlcnRpb25zDQpwbG90cGFpcnMoc3ZtLmxpbmVhcikNCmBgYCAgDQogIA0KYGBge3J9DQpwbG90cGFpcnMoc3ZtLnJhZGlhbCkNCg0KYGBgDQogIA0KYGBge3J9DQpwbG90cGFpcnMoc3ZtLnBvbHlub21pYWwpDQpgYGANCiAgDQojIyMgUHJvYmxlbSA4ICANCioqVGhpcyBwcm9ibGVtIGludm9sdmVzIHRoZSBPSiBkYXRhIHNldCB3aGljaCBpcyBwYXJ0IG9mIHRoZSBJU0xSIHBhY2thZ2UuKiogIA0KKiooYSkgQ3JlYXRlIGEgdHJhaW5pbmcgc2V0IGNvbnRhaW5pbmcgYSByYW5kb20gc2FtcGxlIG9mIDgwMCBvYnNlcnZhdGlvbnMsIGFuZCBhIHRlc3Qgc2V0IGNvbnRhaW5pbmcgdGhlIHJlbWFpbmluZyBvYnNlcnZhdGlvbnMuKiogIA0KYGBge3J9DQpvaiA8LSBPSg0Kc3RyKG9qKQ0KYGBgICANCiAgDQpgYGB7cn0NCnNldC5zZWVkKDEyMykNCmludHJhaW4gPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihvaiRQdXJjaGFzZSwgcCA9IDAuNzQ2LCBsaXN0ID0gRkFMU0UpDQp0cmFpbiA8LSBvaltpbnRyYWluLF0NCnRlc3QgPC0gb2pbLWludHJhaW4sXQ0KYGBgDQogIA0KKiooYikgRml0IGEgc3VwcG9ydCB2ZWN0b3IgY2xhc3NpZmllciB0byB0aGUgdHJhaW5pbmcgZGF0YSB1c2luZyBjb3N0PTAuMDEsIHdpdGggUHVyY2hhc2UgYXMgdGhlIHJlc3BvbnNlIGFuZCB0aGUgb3RoZXIgdmFyaWFibGVzIGFzIHByZWRpY3RvcnMuIFVzZSB0aGUgc3VtbWFyeSgpIGZ1bmN0aW9uIHRvIHByb2R1Y2Ugc3VtbWFyeSBzdGF0aXN0aWNzLCBhbmQgZGVzY3JpYmUgdGhlIHJlc3VsdHMgb2J0YWluZWQuKiogIA0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQpzdm1maXQ9c3ZtKFB1cmNoYXNlfi4sIGtlcm5lbCA9ICJsaW5lYXIiLGRhdGE9dHJhaW4sY29zdD0wLjAxKQ0Kc3VtbWFyeShzdm1maXQpDQoNCmBgYA0KICANClRoZSBzdm0gY2xhc3NpZmllciB1c2VkIHRoZSAga2VybmVsIGxpbmVhciBvbmUgYW5kIGdvdCA2Mjcgc3VwcG9ydCB2ZWN0b3JzLiBGb3IgdGhpcyBtb2RlbCBmaXQgSSB1c2VkIGFsbCB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzIHdpdGggUHVyY2hhc2UgYXMgdGhlIHRhcmdldCB2YXJpYWJsZS4gIA0KYGBge3J9DQojIHByZWRpY3Rpbmcgb24gdGhlIHRlc3QgZGF0YQ0KcHJlZD1wcmVkaWN0KHN2bWZpdCxuZXdkYXRhID10ZXN0KQ0KY29uZnVzaW9uTWF0cml4KHByZWQsYXMuZmFjdG9yKHRlc3QkUHVyY2hhc2UpKQ0KDQpgYGANCiAgDQpgYGB7cn0NCiMgUHJlZGljdGluZyBvbiB0aGUgdHJhaW4gZGF0YQ0KcHJlZC50cmFpbj1wcmVkaWN0KHN2bWZpdCxuZXdkYXRhID10cmFpbikNCmNvbmZ1c2lvbk1hdHJpeChwcmVkLnRyYWluLGFzLmZhY3Rvcih0cmFpbiRQdXJjaGFzZSkpDQoNCmBgYA0KICANCkZyb20gdGhlIGNvbmZ1c2lvbiBtYXRyaWNlcyB3ZSBnb3QgdGhlIHRlc3RpbmcgYW5kIHRyYWluaW5nIGVycm9yIHJhdGVzIGFyZSBgMTAwLTgwID0gMjBgIGFuZCBgMTAwLTgzLjc1ID0gMTYuMjVgIHJlc3BlY3RpdmVseS4gVGhlIHN2bSBwcmVmb3JtZWQgd2VsbCBiYWxhbmNpbmcgYm90aCB0aGUgc2Vuc2l0aXZpdHkgYW5kIHNwZWNpZmljaXR5IHNjb3Jlcy4gIA0KICANCioqKGQpIFVzZSB0aGUgdHVuZSgpIGZ1bmN0aW9uIHRvIHNlbGVjdCBhbiBvcHRpbWFsIGNvc3QuIENvbnNpZGVyIHZhbHVlcyBpbiB0aGUgcmFuZ2UgMC4wMSB0byAxMC4qKiAgDQpgYGB7cn0NCnNldC5zZWVkKDEyMykNCm5ld190dW5lPXR1bmUoc3ZtLCBQdXJjaGFzZX4uLCBkYXRhPXRyYWluLCBrZXJuZWwgPSJsaW5lYXIiLA0KICAgICAgICAgICAgICByYW5nZXM9bGlzdChjb3N0PWMoMC4wMSwwLjA1LDAuMSwwLjUsMSwxLjUsMTApKSkNCnN1bW1hcnkobmV3X3R1bmUpDQpuZXdfdHVuZSRiZXN0Lm1vZGVsDQpgYGAgIA0KICANClRoZSBsb3dlc3QgZXJyb3Igd2FzIDAuMTY2MjUJd2l0aCBhIGJlc3QgY29zdCBwYXJhbWV0ZXIgdmFsdWUgMC4wNSAgDQogIA0KKiooZSkgQ29tcHV0ZSB0aGUgdHJhaW5pbmcgYW5kIHRlc3QgZXJyb3IgcmF0ZXMgdXNpbmcgdGhpcyBuZXcgdmFsdWUgZm9yIGNvc3QuKiogIA0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQpzdm1maXQubmV3PXN2bShQdXJjaGFzZX4uLCBrZXJuZWwgPSAibGluZWFyIixkYXRhPXRyYWluLGNvc3Q9MC4wNSkNCnN1bW1hcnkoc3ZtZml0Lm5ldykNCmBgYA0KICANCmBgYHtyfQ0KIyBwcmVkaWN0aW5nIG9uIHRoZSB0ZXN0IGRhdGENCnByZWQubmV3PXByZWRpY3Qoc3ZtZml0Lm5ldyxuZXdkYXRhID10ZXN0KQ0KY29uZnVzaW9uTWF0cml4KHByZWQubmV3LGFzLmZhY3Rvcih0ZXN0JFB1cmNoYXNlKSkNCmBgYA0KICANCmBgYHtyfQ0KIyBQcmVkaWN0aW5nIG9uIHRoZSB0cmFpbiBkYXRhDQpwcmVkLm5ldzI9cHJlZGljdChzdm1maXQubmV3LG5ld2RhdGEgPXRyYWluKQ0KY29uZnVzaW9uTWF0cml4KHByZWQubmV3Mixhcy5mYWN0b3IodHJhaW4kUHVyY2hhc2UpKQ0KYGBgDQogIA0KKiooZikgUmVwZWF0IHBhcnRzIChiKSB0aHJvdWdoIChlKSB1c2luZyBhIHN1cHBvcnQgdmVjdG9yIG1hY2hpbmUgd2l0aCBhIHJhZGlhbCBrZXJuZWwuIFVzZSB0aGUgZGVmYXVsdCB2YWx1ZSBmb3IgZ2FtbWEuKiogIA0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQpzdm1maXQucmFkaWFsZml0PXN2bShQdXJjaGFzZX4uLCBrZXJuZWwgPSAicmFkaWFsIixkYXRhPXRyYWluLGNvc3Q9MC4wMSkNCnN1bW1hcnkoc3ZtZml0LnJhZGlhbGZpdCkNCg0KIyBwcmVkaWN0aW5nIG9uIHRoZSB0ZXN0IGRhdGENCnByZWQ9cHJlZGljdChzdm1maXQucmFkaWFsZml0LG5ld2RhdGEgPXRlc3QpDQpjb25mdXNpb25NYXRyaXgocHJlZCxhcy5mYWN0b3IodGVzdCRQdXJjaGFzZSkpDQoNCiMgUHJlZGljdGluZyBvbiB0aGUgdHJhaW4gZGF0YQ0KcHJlZC50cmFpbj1wcmVkaWN0KHN2bWZpdC5yYWRpYWxmaXQsbmV3ZGF0YSA9dHJhaW4pDQpjb25mdXNpb25NYXRyaXgocHJlZC50cmFpbixhcy5mYWN0b3IodHJhaW4kUHVyY2hhc2UpKQ0KYGBgDQogIA0KKipUdW5pbmcgdGhlIG1vZGVsKiogIA0KICANCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KdHVuZS5yYWRpYWw9dHVuZShzdm0sIFB1cmNoYXNlfi4sIGRhdGE9dHJhaW4sIGtlcm5lbCA9InJhZGlhbCIsDQogICAgICAgICAgICAgIHJhbmdlcz1saXN0KGNvc3Q9YygwLjAxLDAuMDUsMC4xLDAuNSwxLDEuNSwxMCkpKQ0Kc3VtbWFyeSh0dW5lLnJhZGlhbCkNCnR1bmUucmFkaWFsJGJlc3QubW9kZWwNCg0KYGBgDQogIA0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQpzdm1maXQucmFkaWFscmVmaXQ9c3ZtKFB1cmNoYXNlfi4sIGtlcm5lbCA9ICJyYWRpYWwiLGRhdGE9dHJhaW4sY29zdD0xKQ0Kc3VtbWFyeShzdm1maXQucmFkaWFscmVmaXQpDQoNCiMgcHJlZGljdGluZyBvbiB0aGUgdGVzdCBkYXRhDQpwcmVkLnJhZGlhbHRlc3Q9cHJlZGljdChzdm1maXQucmFkaWFscmVmaXQsbmV3ZGF0YSA9dGVzdCkNCmNvbmZ1c2lvbk1hdHJpeChwcmVkLnJhZGlhbHRlc3QsYXMuZmFjdG9yKHRlc3QkUHVyY2hhc2UpKQ0KDQojIFByZWRpY3Rpbmcgb24gdGhlIHRyYWluIGRhdGENCnByZWQucmFkaWFsdHJhaW49cHJlZGljdChzdm1maXQucmFkaWFscmVmaXQsbmV3ZGF0YSA9dHJhaW4pDQpjb25mdXNpb25NYXRyaXgocHJlZC5yYWRpYWx0cmFpbixhcy5mYWN0b3IodHJhaW4kUHVyY2hhc2UpKQ0KYGBgDQogIA0KKiooZykgUmVwZWF0IHBhcnRzIChiKSB0aHJvdWdoIChlKSB1c2luZyBhIHN1cHBvcnQgdmVjdG9yIG1hY2hpbmUgd2l0aCBhIHBvbHlub21pYWwga2VybmVsLiBTZXQgZGVncmVlPTIqKiAgDQpgYGB7cn0NCnNldC5zZWVkKDEyMykNCnN2bWZpdC5wb2x5Zml0PXN2bShQdXJjaGFzZX4uLCBrZXJuZWwgPSAicG9seW5vbWlhbCIsZGF0YT10cmFpbixjb3N0PTAuMDEsIGRlZ3JlZSA9IDIpDQpzdW1tYXJ5KHN2bWZpdC5wb2x5Zml0KQ0KDQojIHByZWRpY3Rpbmcgb24gdGhlIHRlc3QgZGF0YQ0KcHJlZD1wcmVkaWN0KHN2bWZpdC5wb2x5Zml0LG5ld2RhdGEgPXRlc3QpDQpjb25mdXNpb25NYXRyaXgocHJlZCxhcy5mYWN0b3IodGVzdCRQdXJjaGFzZSkpDQoNCiMgUHJlZGljdGluZyBvbiB0aGUgdHJhaW4gZGF0YQ0KcHJlZC50cmFpbj1wcmVkaWN0KHN2bWZpdC5wb2x5Zml0LG5ld2RhdGEgPXRyYWluKQ0KY29uZnVzaW9uTWF0cml4KHByZWQudHJhaW4sYXMuZmFjdG9yKHRyYWluJFB1cmNoYXNlKSkNCmBgYA0KICANCioqVHVuaW5nIHRoZSBtb2RlbCoqICANCiAgDQpgYGB7cn0NCnNldC5zZWVkKDEyMykNCnR1bmUucG9seT10dW5lKHN2bSwgUHVyY2hhc2V+LiwgZGF0YT10cmFpbiwga2VybmVsID0icG9seW5vbWlhbCIsDQogICAgICAgICAgICAgIHJhbmdlcz1saXN0KGNvc3Q9YygwLjAxLDAuMDUsMC4xLDAuNSwxLDEuNSwxMCkpKQ0Kc3VtbWFyeSh0dW5lLnBvbHkpDQp0dW5lLnBvbHkkYmVzdC5tb2RlbA0KDQpgYGANCg0KICANCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0Kc3ZtZml0LnBvbHlyZWZpdD1zdm0oUHVyY2hhc2V+Liwga2VybmVsID0gInBvbHlub21pYWwiLGRhdGE9dHJhaW4sY29zdD0xLjUsIGRlZ3JlZSA9IDMpDQpzdW1tYXJ5KHN2bWZpdC5wb2x5cmVmaXQpDQoNCiMgcHJlZGljdGluZyBvbiB0aGUgdGVzdCBkYXRhDQpwcmVkLnBvbHl0ZXN0PXByZWRpY3Qoc3ZtZml0LnBvbHlyZWZpdCxuZXdkYXRhID10ZXN0KQ0KY29uZnVzaW9uTWF0cml4KHByZWQucG9seXRlc3QsYXMuZmFjdG9yKHRlc3QkUHVyY2hhc2UpKQ0KDQojIFByZWRpY3Rpbmcgb24gdGhlIHRyYWluIGRhdGENCnByZWQucG9seXRyYWluPXByZWRpY3Qoc3ZtZml0LnBvbHlyZWZpdCxuZXdkYXRhID10cmFpbikNCmNvbmZ1c2lvbk1hdHJpeChwcmVkLnBvbHl0cmFpbixhcy5mYWN0b3IodHJhaW4kUHVyY2hhc2UpKQ0KYGBgDQogIA0KKiooaCkgT3ZlcmFsbCwgd2hpY2ggYXBwcm9hY2ggc2VlbXMgdG8gZ2l2ZSB0aGUgYmVzdCByZXN1bHRzIG9uIHRoaXMgZGF0YT8qKg0KICANCkkgYW0gY3JlYXRpbmcgYSBkYXRhZnJhbWUgb2YgdGVzdCBhbmQgdHJhaW4gYWNjdXJhY2llcyB3aXRoIHRoZSB1c2VkIG1vZGVscyBiZWxvdy4gSSBzdWJ0cmFjdGVkIHRoZSBhY2N1cmFjeSBmcm9tIDEwMCUgdG8gZ2V0IHRoZSBlcnJvciBzaW5jZSB0b3RhbCBvZiBhY2N1cmFjeSArIGVycm9yID0gMTAwJS4gV2UgY2FuIGFsc28gZmluZCB0aGUgY2xhc3NpZmljYXRpb24gZXJyb3IgcmF0ZSBieSBqdXN0IGNvdW50aW5nIHRoZSB0b3RhbCBudW1iZXIgb2YgZmFsc2UgcG9zaXRpdmVzIGFuZCBmYWxzZSBuZWdhdGl2ZXMgZnJvbSB0aGUgdGFibGUgaW4gdGhlIGNvbmZ1c2lvbiBtYXRyaXggYW5kIGRpdmlkZSBpdCBieSB0aGUgdG90YWwgb2JzZXJ2YXRpb25zLiAgICANCmBgYHtyfQ0KbW9kZWwua2VybmVsIDwtIGMoImxpbmVhciIsInJhZGlhbCIsICJwb2x5bm9taWFsIikNCnRlc3RfZXJyb3IgPC0gIGMoMTguNTIsIDE4LjE1LCAyMC4zNykNCnRyYWluX2Vycm9yIDwtIGMoMTYuMjUsIDE0Ljc1LCAxNS43NSkNCnRhYmxlX2FjY3VyYWN5IDwtIGRhdGEuZnJhbWUobW9kZWwua2VybmVsLHRyYWluX2Vycm9yLHRlc3RfZXJyb3IpDQoNCnRhYmxlX2FjY3VyYWN5DQpgYGANCg0KICANCldlIGNhbiBzZWUgdGhhdCB0aGUgc3ZtIGNsYXNzaWZpZXIgd2l0aCBhIHJhZGlhbCBrZXJuZWwgaGFzIGdvdCB0aGUgbWluaW11bSBlcnJvciBhbW9uZyBhbGwgdGhlIGtlcm5lbHMuIEkgdXNlZCB0aGUgY29hc3QgPTEgYWZ0ZXIgdHVuaW5nIGFuZCBmaW5kaW5nIG91dCB0aGUgb3B0aW1hbCBwYXJhbWV0ZXIuIA0KDQoNCg0KICANCg0KDQoNCg0K