ZZSC5855 Project

Introduction

This document will go through how we can assess the profitability and sustainability of harvesting abalone via various statistical techniques.

Models and Methods

Q1

# stratified train val test 
#standardize on train 
# why or why not PCA on train
  # why we do PCA , why it reduces multicolinarity
#methods - SVM, Multiclass logistic regression (no need for multivariate as 1 response )
  # crossvalidation talk about
#predict - logistic and SVM 
#non PCA predict - logistics - SVM
#hyper para tune with VAL , cross val 
#analysis of results 
  # interpretation of model coefficients 
  # note that standardized data has diff inter...
  # precision recall what they mean for this scenario , which is more important in sustainability perspective.
# talk about normality how it affects interpretation but not prediction 
# why you didnt do certain things - manova , LDA 
# talk about why you picked these prediction models
#Residual analysis 
#conclusion 

Q2

# train test val
  # note - predictors are different from q1 
# PCA here or not (we need interpretation? or we dont?)
# multivariate regression model mlm() for shucked and visceral
# obtain coefficients 
# create function for price index 
# input is new x vector and prices 
# using beta coefficients to form equation for price 
# for prediction interval see james answer of ed and week3 2831 properties 
# analysis of results  
# make graph of price index at various values 
# interpretation 

Final Conclusion

# what do the models tell us # what answer the prompt # future investigations

Data Prepossessing

In this section we shall process the data to be more effective for modelling.

abalone <- read.csv("/Users/dakshmukhra/Desktop/uni/masters/hex5_ZZSC5855_2024/abalone.csv")
abalone <-as.data.frame(abalone)
abalone$Sex <- as.factor(abalone$Sex)

Null and zero value detection is done below to detect any nonsensical values

colSums(is.na(abalone))
##            Sex         Length       Diameter         Height   Whole.weight 
##              0              0              0              0              0 
## Shucked.weight Viscera.weight   Shell.weight          Rings 
##              0              0              0              0
colSums(abalone=="")
##            Sex         Length       Diameter         Height   Whole.weight 
##              0              0              0              0              0 
## Shucked.weight Viscera.weight   Shell.weight          Rings 
##              0              0              0              0
colSums(abalone==0)
##            Sex         Length       Diameter         Height   Whole.weight 
##              0              0              0              2              0 
## Shucked.weight Viscera.weight   Shell.weight          Rings 
##              0              0              0              0

We can see that height has two zero values, we shall remove these. By inspecting the csv given and sorting by height we can see that their are two extreme outliers as well. Removing these outliers will ensure our distributions are more friendly.

abalone <- abalone[abalone$Height>0, ]
abalone = abalone[!(abalone$Height == 226),]
abalone = abalone[!(abalone$Height == 103),]

EDA

Our data is now ready for some exploration, we shall examine distributions and employ various statistical tests to gain a better understanding of the data.

par(mfrow = c(3, 3))


hist(abalone$Length,
     xlab = abalone$Length,
     main = "Histogram of length" ,
     breaks = sqrt(length(abalone$Length)) # set number of bins
)

hist(abalone$Height,
     xlab = abalone$Height,
     main = "Histogram of Height" ,
     breaks = sqrt(length(abalone$Height)) # set number of bins
)

hist(abalone$Diameter,
     xlab = abalone$Diameter,
     main = "Histogram of Diameter" ,
     breaks = sqrt(length(abalone$Diameter)) # set number of bins
)

hist(abalone$Whole.weight,
     xlab = abalone$Whole.weight,
     main = "Histogram of Whole.weight" ,
     breaks = sqrt(length(abalone$Whole.weight)) # set number of bins
)

hist(abalone$Shucked.weight,
     xlab = abalone$Shucked.weight,
     main = "Histogram of Shucked.weight" ,
     breaks = sqrt(length(abalone$Shucked.weight)) # set number of bins
)

hist(abalone$Viscera.weight,
     xlab = abalone$Viscera.weight,
     main = "Histogram of Viscera.weight" ,
     breaks = sqrt(length(abalone$Viscera.weight)) # set number of bins
)

hist(abalone$Shell.weight,
     xlab = abalone$Shell.weight,
     main = "Histogram of Shell.weight" ,
     breaks = sqrt(length(abalone$Shell.weight)) # set number of bins
)

barplot(table(abalone$Sex),
        ylab = "Frequency",
        xlab = "Sex")

We can see from the above histograms the distributions of Length and Diameter are left skewed whilst Height is neutral mimicking a normal distribution. The variables of Shucked weight and Viscera weight are highly right skewed. The distribution of sex among the classes of infant female and male seem to be relatively even, hence class imbalance will not be an issue in modelling.

Lets look further into our numeric features for question 1 i.e. Height, Length, Diameter

numeric_cols <- abalone[c("Height","Length","Diameter")]
ggpairs(numeric_cols) 

as we can see from the plot above the pairwise correlations indicate that the variables are highly correlated with one another. This further emphasised by the scatter plots between the various pairs. Highly correlated variables can be a problem for the interpretation for many linear models however prediction strength is unchanged. From this plot we can deduce that the three variables are non normal marginally and jointly.

mvn(numeric_cols, mvnTest="mardia", multivariatePlot="qq")

## $multivariateNormality
##              Test        Statistic               p value Result
## 1 Mardia Skewness 1218.27867529684 1.64308044649984e-255     NO
## 2 Mardia Kurtosis 115.207675661647                     0     NO
## 3             MVN             <NA>                  <NA>     NO
## 
## $univariateNormality
##               Test  Variable Statistic   p value Normality
## 1 Anderson-Darling  Height     10.3260  <0.001      NO    
## 2 Anderson-Darling  Length     36.7518  <0.001      NO    
## 3 Anderson-Darling Diameter    36.5694  <0.001      NO    
## 
## $Descriptives
##             n      Mean   Std.Dev Median Min Max 25th 75th       Skew
## Height   4173  27.85119  7.675505     28   2  50   23   33 -0.2491366
## Length   4173 104.80757 24.012124    109  15 163   90  123 -0.6410833
## Diameter 4173  81.58303 19.842093     85  11 130   70   96 -0.6103689
##             Kurtosis
## Height   -0.18448134
## Length    0.06529644
## Diameter -0.04515963

We can see from the above statistical tests (Mardia and Anderson Darling) that normality is rejected jointly and marginally. The Chi-Square plot also reveal the same conclusion with the data point veering off the diagonal. However this conclusion of non normality needs to be taken with a grain of salt. As mention within the ZZSC5855 notes

“as the sample size increases, the Central Limit Theorem tells us that many statistics, including sample means and (much more slowly) sample variances and covariances, approach normality—and multivariate statistics generally approach multivariate normality. This means that regardless of the underlying distribution, the statistical procedures depending on the normality assumption become valid even as the chances that a statistical hypothesis test will detect non-normality there is approaches 1”

By exampling the histograms shown earlier height and a transformed Length and Diameter (shown later on) will be adequate to assume multivariate normality even if the p-value is small for these statistical tests.

numeric_cols.T <- numeric_cols %>% mutate(
  `Height` = (`Height`), # leave height as is
  `Length` = Length^2, 
  `Diameter` = Diameter^2)
keeps <- c("Height","Length","Diameter")
numeric_cols.T = numeric_cols.T[keeps]

abalone.T <- numeric_cols.T
abalone.T$Sex <- abalone$Sex

ggpairs(abalone.T)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

as we can see using the square() transformation on Length and Diameter moved its distribution plots closer to normal.

Models and Methods

Q1

Data manitpulation

Lets split our data into a training and testing set and scale it. The reason we shall scale the data set is that many models such as support vector machine require that its variables be on the same scale for it work effectively. Note the scaling is first done on the train set and using the mean and standard deviation from the train set we scale the test. This so that data leakage does not occur.

Also note that Principal Component analysis was explored as a correlation reduction technique however prediction results were largely unaffected and interpretation of coefficients was lost. Hence PCA has been omitted in the following data manipulation. Please see appendix for code if required.

# join transformed data frame 
q1data <- abalone.T[c("Sex","Height","Length","Diameter")]

# Split into train and test 
# createDataPartition automatically stratify samples to ensure classes are balnced in split 
set.seed(42)
train.index <- createDataPartition(q1data$Sex, p = .7, list = FALSE)
train <- q1data[ train.index,]
test  <- q1data[-train.index,]

##### NOTE SCALE AFTER SPLIT TO AVOID DATA LEAKAGE 
# Scale train 
train.scaled <- scale(train[c("Height","Length","Diameter")])
# apply the same mean and std of scaled train to test to avoid data leakage 
test.scaled <- scale(test[c("Height","Length","Diameter")], center=attr(train.scaled, "scaled:center"), scale=attr(train.scaled, "scaled:scale"))

train.scaled <- as.data.frame(train.scaled)
test.scaled <- as.data.frame(test.scaled)

train.scaled$Sex <- train$Sex
test.scaled$Sex  <- test$Sex

train.scaled$Sex <- relevel(factor(train.scaled$Sex), ref = "F")
test.scaled$Sex <- relevel(factor(test.scaled$Sex), ref = "F")

Modelling

Now that we have adequately split our data we are ready to model the relationship between Sex and variables Length Height and Diameter. The models of multinomial logistic regression and support vector machine (svm) were chosen for this task, as we need a way to predict multiple classes for a single dependent variable (Sex). Logistic regression in particular was chosen for its simple interpretation of its coefficients and svm is robust in adjusting to non linear relationships in the data with an appropriate choice of Kernel.

The 4 relationships we require to be modeled are we shall model each one outputting a score for both training and test data: - predicting the sex of the abalone in general; - predicting specifically Infants as opposed to others (to avoid harvesting them); - predicting specifically Females as opposed to others (when profitability is prioritised); - predicting specifically Males as opposed to others (when sustainability is prioritised).

Predicting the sex of the abalone in general

Logistic Regression

################## LOGISTIC -- TRAIN IS SCALED
# we can see that the Train data is not sperable linearly 
colors <- c("#999999", "#E69F00", "#56B4E9")
colors <- colors[as.numeric(train.scaled$Sex)]
s3d <- scatterplot3d(train.scaled[c("Length","Diameter","Height")], pch = 16, color = colors , box=FALSE)
legend("right", legend = levels(train.scaled$Sex),
       col =  c("#999999", "#E69F00", "#56B4E9"), pch = 16)

As we can see form the above scatter plot the data is not linearly separable for the classes. Lets test this.

######## "Acc 0.522245037645448"
log.scaled.m <- multinom(Sex~Length + Diameter + Height, data = train.scaled)
## # weights:  15 (8 variable)
## initial  value 3210.145107 
## iter  10 value 2653.212832
## final  value 2643.727856 
## converged
summary(log.scaled.m)
## Call:
## multinom(formula = Sex ~ Length + Diameter + Height, data = train.scaled)
## 
## Coefficients:
##   (Intercept)    Length   Diameter     Height
## I  -0.2961434 0.9273012 -1.8720556 -0.9096382
## M   0.2356347 0.1949341 -0.1766856 -0.2217037
## 
## Std. Errors:
##   (Intercept)    Length  Diameter     Height
## I  0.06173442 0.3380388 0.3523215 0.13488018
## M  0.05064883 0.2248627 0.2308985 0.09937892
## 
## Residual Deviance: 5287.456 
## AIC: 5303.456
#Predicting the values for train dataset
train.predicted_vals <- predict(log.scaled.m, newdata = train.scaled[c("Length","Diameter","Height")], "class")

# Building classification table
paste('Acc', accuracy(train.scaled$Sex, train.predicted_vals))
## [1] "Acc 0.522245037645448"
cf.train <- caret::confusionMatrix(data=train.predicted_vals, reference=train.scaled$Sex)
cf.train
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   F   I   M
##          F 170  11 173
##          I 152 683 223
##          M 593 244 673
## 
## Overall Statistics
##                                           
##                Accuracy : 0.5222          
##                  95% CI : (0.5039, 0.5405)
##     No Information Rate : 0.3658          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.2726          
##                                           
##  Mcnemar's Test P-Value : < 2.2e-16       
## 
## Statistics by Class:
## 
##                      Class: F Class: I Class: M
## Sensitivity           0.18579   0.7281   0.6296
## Specificity           0.90832   0.8110   0.5483
## Pos Pred Value        0.48023   0.6456   0.4457
## Neg Pred Value        0.70989   0.8632   0.7195
## Prevalence            0.31314   0.3210   0.3658
## Detection Rate        0.05818   0.2337   0.2303
## Detection Prevalence  0.12115   0.3621   0.5168
## Balanced Accuracy     0.54706   0.7696   0.5889

The training set accuracy is “Acc 0.522245037645448”.We can see form the confusion matrix that the model inst able to correctly classify Female data points as much as Infants and Male. Lets examine the test set and predicted using are trained model.

test.predicted_vals <- predict(log.scaled.m, newdata = test.scaled[c("Length","Diameter","Height")], "class")
# Building classification table
paste('Acc', accuracy(test.scaled$Sex, test.predicted_vals))
## [1] "Acc 0.535571542765787"
cf.test <- caret::confusionMatrix(data=test.predicted_vals, reference=test.scaled$Sex)
cf.test
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   F   I   M
##          F  87   2  79
##          I  61 307 103
##          M 243  93 276
## 
## Overall Statistics
##                                           
##                Accuracy : 0.5356          
##                  95% CI : (0.5075, 0.5635)
##     No Information Rate : 0.3661          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.2941          
##                                           
##  Mcnemar's Test P-Value : < 2.2e-16       
## 
## Statistics by Class:
## 
##                      Class: F Class: I Class: M
## Sensitivity           0.22251   0.7637   0.6026
## Specificity           0.90581   0.8068   0.5763
## Pos Pred Value        0.51786   0.6518   0.4510
## Neg Pred Value        0.71930   0.8782   0.7152
## Prevalence            0.31255   0.3213   0.3661
## Detection Rate        0.06954   0.2454   0.2206
## Detection Prevalence  0.13429   0.3765   0.4892
## Balanced Accuracy     0.56416   0.7853   0.5895

The test set is “Acc 0.535571542765787” our model has generalised as much as it can predicting on new data. The results of the ocnfusion matrix for the test set are similar to the train set. Lets check out SVM.

Support Vector Machine

summary(tuned.svm <- tune.svm(Sex~.,data=train.scaled, kernel="radial", gamma = 10^(-1:1), cost = 10^(-1:1)))
## 
## Parameter tuning of 'svm':
## 
## - sampling method: 10-fold cross validation 
## 
## - best parameters:
##  gamma cost
##      1    1
## 
## - best performance: 0.4787835 
## 
## - Detailed performance results:
##   gamma cost     error dispersion
## 1   0.1  0.1 0.4811807 0.02273328
## 2   1.0  0.1 0.4822081 0.02322048
## 3  10.0  0.1 0.4856188 0.02141920
## 4   0.1  1.0 0.4842618 0.02078784
## 5   1.0  1.0 0.4787835 0.02437308
## 6  10.0  1.0 0.4921210 0.02328719
## 7   0.1 10.0 0.4880254 0.02313988
## 8   1.0 10.0 0.4811866 0.02302472
## 9  10.0 10.0 0.5013523 0.02568625
tuned.svm$best.model
## 
## Call:
## best.svm(x = Sex ~ ., data = train.scaled, gamma = 10^(-1:1), cost = 10^(-1:1), 
##     kernel = "radial")
## 
## 
## Parameters:
##    SVM-Type:  C-classification 
##  SVM-Kernel:  radial 
##        cost:  1 
## 
## Number of Support Vectors:  2479
train.predicted_vals <- predict(tuned.svm$best.model, newdata=train.scaled[c("Length","Diameter","Height")], decision.values=TRUE)
# Building classification table
paste('Acc', accuracy(train.scaled$Sex, train.predicted_vals))
## [1] "Acc 0.538329911019849"
cf.train <- caret::confusionMatrix(data=train.predicted_vals, reference=train.scaled$Sex)
cf.train
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   F   I   M
##          F 157  14 106
##          I 165 699 246
##          M 593 225 717
## 
## Overall Statistics
##                                           
##                Accuracy : 0.5383          
##                  95% CI : (0.5201, 0.5565)
##     No Information Rate : 0.3658          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.2964          
##                                           
##  Mcnemar's Test P-Value : < 2.2e-16       
## 
## Statistics by Class:
## 
##                      Class: F Class: I Class: M
## Sensitivity           0.17158   0.7452   0.6707
## Specificity           0.94021   0.7928   0.5586
## Pos Pred Value        0.56679   0.6297   0.4671
## Neg Pred Value        0.71342   0.8681   0.7462
## Prevalence            0.31314   0.3210   0.3658
## Detection Rate        0.05373   0.2392   0.2454
## Detection Prevalence  0.09480   0.3799   0.5253
## Balanced Accuracy     0.55590   0.7690   0.6146

For the SVM we tune across selection of hyper parameters and find the best selection and then using the best model we predict with the training set. The results are extremely similar to logistic train with a “Acc 0.538329911019849” and similar confusion matrix. lets see the test set.

test.predicted_vals <- predict(tuned.svm$best.model, newdata=test.scaled[c("Length","Diameter","Height")], decision.values=TRUE)
# Building classification table
paste('Acc', accuracy(test.scaled$Sex, test.predicted_vals))
## [1] "Acc 0.512390087929656"
cf.test <- caret::confusionMatrix(data=test.predicted_vals, reference=test.scaled$Sex)
cf.test
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   F   I   M
##          F  50   5  66
##          I  68 310 111
##          M 273  87 281
## 
## Overall Statistics
##                                           
##                Accuracy : 0.5124          
##                  95% CI : (0.4843, 0.5404)
##     No Information Rate : 0.3661          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.2573          
##                                           
##  Mcnemar's Test P-Value : < 2.2e-16       
## 
## Statistics by Class:
## 
##                      Class: F Class: I Class: M
## Sensitivity           0.12788   0.7711   0.6135
## Specificity           0.91744   0.7892   0.5460
## Pos Pred Value        0.41322   0.6339   0.4384
## Neg Pred Value        0.69823   0.8793   0.7098
## Prevalence            0.31255   0.3213   0.3661
## Detection Rate        0.03997   0.2478   0.2246
## Detection Prevalence  0.09672   0.3909   0.5124
## Balanced Accuracy     0.52266   0.7802   0.5798

Note an “Acc 0.512390087929656” score and similar in ability to classify Female data points as logistics regression even tho a non linear keneral method “radial” was specified.

Conclusion for predicting the sex of the abalone in general

Both Models of Logistic regression had mediocre performance on unseen data, as such logistic regression should be used with its accuracy score of 0.53 and highly interpretable coefficients.

Predicting specifically Infants

As we shift to a binary classification problem class imbalance may become an issue as such more robust metrics such as F1-score will be reported. The same models above will be trained and tested.

First we need to get our data.

################## INFANT DATA PREP 
inf.train.scaled <- train.scaled
levels(inf.train.scaled $Sex) =c('Not Infant', 'I', 'Not Infant')

inf.test.scaled <- test.scaled
levels(inf.test.scaled $Sex) =c('Not Infant', 'I', 'Not Infant')

Lets see the distribution for infants.

################## INFANT LOGISTIC TRAIN  
colors <- c("#E69F00", "#56B4E9")
colors <- colors[as.numeric(inf.train.scaled$Sex)]
s3d <- scatterplot3d(inf.train.scaled[c("Length","Diameter","Height")], pch = 16, color = colors , box=FALSE)
legend("right", legend = levels(inf.train.scaled$Sex),
       col =  c("#E69F00", "#56B4E9"), pch = 16)

compared we can see that the data is much more linearly sperable.

#logistic regression
log.scaled.m <- glm(Sex~ Length + Diameter + Height, data=inf.train.scaled, family = 'binomial')
summary(log.scaled.m)
## 
## Call:
## glm(formula = Sex ~ Length + Diameter + Height, family = "binomial", 
##     data = inf.train.scaled)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -1.11665    0.05453 -20.478  < 2e-16 ***
## Length       0.82011    0.31397   2.612    0.009 ** 
## Diameter    -1.77512    0.32805  -5.411 6.26e-08 ***
## Height      -0.78242    0.12210  -6.408 1.47e-10 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 3667.9  on 2921  degrees of freedom
## Residual deviance: 2563.6  on 2918  degrees of freedom
## AIC: 2571.6
## 
## Number of Fisher Scoring iterations: 5
#Predicting the values for train dataset
train.predicted_vals <- predict(log.scaled.m, newdata = inf.train.scaled[c("Length","Diameter","Height")])
train.predict_class <- ifelse(train.predicted_vals>0.5,  'I', 'Not Infant')

#Accuracy 0.782340862422998
paste('Acc', accuracy(inf.train.scaled$Sex, train.predict_class))
## [1] "Acc 0.783367556468172"
#F1 score 0.8542621
f1_val <- MLmetrics::F1_Score(inf.train.scaled$Sex, train.predict_class)
f1_val
## [1] 0.8544493
# Building classification table
table(inf.train.scaled$Sex, train.predict_class)
##             train.predict_class
##                 I Not Infant
##   Not Infant  126       1858
##   I           431        507

The initial train results are providing us with relative good accuracy of “Acc 0.783367556468172” and F1 score of 0.8544493. From the confusion matrix we can see that although non infants are classified at a higher rate infants are incorrectly classified with more frequency (low precision)

Lets examine the test set.

#Predicting the values for train dataset
test.predicted_vals <- predict(log.scaled.m, newdata = inf.test.scaled[c("Length","Diameter","Height")])
test.predict_class <- ifelse(test.predicted_vals>0.5,  'I', 'Not Infant')

paste('Acc', accuracy(inf.test.scaled$Sex, test.predict_class))
## [1] "Acc 0.778577138289368"
f1_val <- MLmetrics::F1_Score(inf.test.scaled$Sex, test.predict_class)
f1_val
## [1] 0.8521089
# Building classification table
table(inf.test.scaled$Sex, test.predict_class)
##             test.predict_class
##                I Not Infant
##   Not Infant  51        798
##   I          176        226

The results are comparable to train, we should note they are slightly lower as we are predicting on unseen data. Lets examine the SVM.

# train svm
tuned.svm <- tune.svm(Sex~.,data=inf.train.scaled, kernel="radial", gamma = 10^(-1:1), cost = 10^(-1:1))
tuned.svm$best.model
## 
## Call:
## best.svm(x = Sex ~ ., data = inf.train.scaled, gamma = 10^(-1:1), 
##     cost = 10^(-1:1), kernel = "radial")
## 
## 
## Parameters:
##    SVM-Type:  C-classification 
##  SVM-Kernel:  radial 
##        cost:  1 
## 
## Number of Support Vectors:  1335
train.predicted_vals <- predict(tuned.svm$best.model, newdata=inf.train.scaled[c("Length","Diameter","Height")], decision.values=TRUE)

# Metrics 
cf.train <- caret::confusionMatrix(data=train.predicted_vals, reference=inf.train.scaled$Sex ,mode = "everything")
cf.train
## Confusion Matrix and Statistics
## 
##             Reference
## Prediction   Not Infant    I
##   Not Infant       1785  400
##   I                 199  538
##                                           
##                Accuracy : 0.795           
##                  95% CI : (0.7799, 0.8095)
##     No Information Rate : 0.679           
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.5016          
##                                           
##  Mcnemar's Test P-Value : 3.039e-16       
##                                           
##             Sensitivity : 0.8997          
##             Specificity : 0.5736          
##          Pos Pred Value : 0.8169          
##          Neg Pred Value : 0.7300          
##               Precision : 0.8169          
##                  Recall : 0.8997          
##                      F1 : 0.8563          
##              Prevalence : 0.6790          
##          Detection Rate : 0.6109          
##    Detection Prevalence : 0.7478          
##       Balanced Accuracy : 0.7366          
##                                           
##        'Positive' Class : Not Infant      
## 

The scores in terms of F1 score and accuracy is similar to the train logistic scores however from the confusion matrix th SVM is able to better classify infants. lets see the test set.

test.predicted_vals <- predict(tuned.svm$best.model, newdata=inf.test.scaled[c("Length","Diameter","Height")], decision.values=TRUE)

# Metrics
cf.test <- caret::confusionMatrix(data=test.predicted_vals, reference=inf.test.scaled$Sex, ,mode = "everything")
cf.test
## Confusion Matrix and Statistics
## 
##             Reference
## Prediction   Not Infant   I
##   Not Infant        762 177
##   I                  87 225
##                                           
##                Accuracy : 0.789           
##                  95% CI : (0.7653, 0.8113)
##     No Information Rate : 0.6787          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.4859          
##                                           
##  Mcnemar's Test P-Value : 4.312e-08       
##                                           
##             Sensitivity : 0.8975          
##             Specificity : 0.5597          
##          Pos Pred Value : 0.8115          
##          Neg Pred Value : 0.7212          
##               Precision : 0.8115          
##                  Recall : 0.8975          
##                      F1 : 0.8523          
##              Prevalence : 0.6787          
##          Detection Rate : 0.6091          
##    Detection Prevalence : 0.7506          
##       Balanced Accuracy : 0.7286          
##                                           
##        'Positive' Class : Not Infant      
## 

Again SVM is much better at classifying Infants.

Conclusion for predicting specifically Infants

Both models performed better then the first scenario due to more ‘separable’ data set. Support vector machine should be chosen as the go to model to predict which abalone is an infant to avoid harvesting them.

Predicting specifically Females as opposed to others

Female abalone detection can be highly profitable , as such lets examine how reliably we can predict them

LS0tCnRpdGxlOiAiQWJhbG9uZSBTdXN0YWluYWJpbGl0eSBhbmQgUHJvZml0YWJpbGl0eSIKYXV0aG9yOiAiRGFrc2ggTXVraHJhIgpkYXRlOiAiMjAyNC0xMC0wMiIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OiAKICAgIGNvZGVfZG93bmxvYWQ6IFRydWUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpsaWJyYXJ5KE1MbWV0cmljcykKbGlicmFyeShNZXRyaWNzKSAKbGlicmFyeShkcGx5cikgCmxpYnJhcnkobm5ldCkKbGlicmFyeShjYXJldCkKbGlicmFyeShlMTA3MSkgCiNsaWJyYXJ5KGhlcGxvdHMpIApsaWJyYXJ5KE1BU1MpCmxpYnJhcnkocHN5Y2gpCmxpYnJhcnkocHBjb3IpCiNsaWJyYXJ5KGdnbSkKI2xpYnJhcnkoZ3JhcGgpCmxpYnJhcnkocHVycnIpCmxpYnJhcnkoaGVyZSkKbGlicmFyeShtdnRub3JtKQpsaWJyYXJ5KEdHYWxseSkKbGlicmFyeShNVk4pCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoSUNTTlApCmxpYnJhcnkoQ0NBKQpsaWJyYXJ5KENDUCkKbGlicmFyeSgic2NhdHRlcnBsb3QzZCIpIAoKYGBgCgojIFpaU0M1ODU1IFByb2plY3QKCgojIyBJbnRyb2R1Y3Rpb24gClRoaXMgZG9jdW1lbnQgd2lsbCBnbyB0aHJvdWdoIGhvdyB3ZSBjYW4gYXNzZXNzIHRoZSBwcm9maXRhYmlsaXR5IGFuZCBzdXN0YWluYWJpbGl0eSBvZiBoYXJ2ZXN0aW5nIGFiYWxvbmUgdmlhIHZhcmlvdXMgc3RhdGlzdGljYWwgdGVjaG5pcXVlcy4KCgoKIyMgTW9kZWxzIGFuZCBNZXRob2RzIAojIyMgUTEgIAogICAgIyBzdHJhdGlmaWVkIHRyYWluIHZhbCB0ZXN0IAogICAgI3N0YW5kYXJkaXplIG9uIHRyYWluIAogICAgIyB3aHkgb3Igd2h5IG5vdCBQQ0Egb24gdHJhaW4KICAgICAgIyB3aHkgd2UgZG8gUENBICwgd2h5IGl0IHJlZHVjZXMgbXVsdGljb2xpbmFyaXR5CiAgICAjbWV0aG9kcyAtIFNWTSwgTXVsdGljbGFzcyBsb2dpc3RpYyByZWdyZXNzaW9uIChubyBuZWVkIGZvciBtdWx0aXZhcmlhdGUgYXMgMSByZXNwb25zZSApCiAgICAgICMgY3Jvc3N2YWxpZGF0aW9uIHRhbGsgYWJvdXQKICAgICNwcmVkaWN0IC0gbG9naXN0aWMgYW5kIFNWTSAKICAgICNub24gUENBIHByZWRpY3QgLSBsb2dpc3RpY3MgLSBTVk0KICAgICNoeXBlciBwYXJhIHR1bmUgd2l0aCBWQUwgLCBjcm9zcyB2YWwgCiAgICAjYW5hbHlzaXMgb2YgcmVzdWx0cyAKICAgICAgIyBpbnRlcnByZXRhdGlvbiBvZiBtb2RlbCBjb2VmZmljaWVudHMgCiAgICAgICMgbm90ZSB0aGF0IHN0YW5kYXJkaXplZCBkYXRhIGhhcyBkaWZmIGludGVyLi4uCiAgICAgICMgcHJlY2lzaW9uIHJlY2FsbCB3aGF0IHRoZXkgbWVhbiBmb3IgdGhpcyBzY2VuYXJpbyAsIHdoaWNoIGlzIG1vcmUgaW1wb3J0YW50IGluIHN1c3RhaW5hYmlsaXR5IHBlcnNwZWN0aXZlLgogICAgIyB0YWxrIGFib3V0IG5vcm1hbGl0eSBob3cgaXQgYWZmZWN0cyBpbnRlcnByZXRhdGlvbiBidXQgbm90IHByZWRpY3Rpb24gCiAgICAjIHdoeSB5b3UgZGlkbnQgZG8gY2VydGFpbiB0aGluZ3MgLSBtYW5vdmEgLCBMREEgCiAgICAjIHRhbGsgYWJvdXQgd2h5IHlvdSBwaWNrZWQgdGhlc2UgcHJlZGljdGlvbiBtb2RlbHMKICAgICNSZXNpZHVhbCBhbmFseXNpcyAKICAgICNjb25jbHVzaW9uIAoKIyMjIFEyICAKICAgICMgdHJhaW4gdGVzdCB2YWwKICAgICAgIyBub3RlIC0gcHJlZGljdG9ycyBhcmUgZGlmZmVyZW50IGZyb20gcTEgCiAgICAjIFBDQSBoZXJlIG9yIG5vdCAod2UgbmVlZCBpbnRlcnByZXRhdGlvbj8gb3Igd2UgZG9udD8pCiAgICAjIG11bHRpdmFyaWF0ZSByZWdyZXNzaW9uIG1vZGVsIG1sbSgpIGZvciBzaHVja2VkIGFuZCB2aXNjZXJhbAogICAgIyBvYnRhaW4gY29lZmZpY2llbnRzIAogICAgIyBjcmVhdGUgZnVuY3Rpb24gZm9yIHByaWNlIGluZGV4IAogICAgIyBpbnB1dCBpcyBuZXcgeCB2ZWN0b3IgYW5kIHByaWNlcyAKICAgICMgdXNpbmcgYmV0YSBjb2VmZmljaWVudHMgdG8gZm9ybSBlcXVhdGlvbiBmb3IgcHJpY2UgCiAgICAjIGZvciBwcmVkaWN0aW9uIGludGVydmFsIHNlZSBqYW1lcyBhbnN3ZXIgb2YgZWQgYW5kIHdlZWszIDI4MzEgcHJvcGVydGllcyAKICAgICMgYW5hbHlzaXMgb2YgcmVzdWx0cyAgCiAgICAjIG1ha2UgZ3JhcGggb2YgcHJpY2UgaW5kZXggYXQgdmFyaW91cyB2YWx1ZXMgCiAgICAjIGludGVycHJldGF0aW9uIAoKIyMgRmluYWwgQ29uY2x1c2lvbgogICMgd2hhdCBkbyB0aGUgbW9kZWxzIHRlbGwgdXMgCiAgIyB3aGF0IGFuc3dlciB0aGUgcHJvbXB0IAogICMgZnV0dXJlIGludmVzdGlnYXRpb25zIAogIAogIAogIAoKIyMgRGF0YSBQcmVwb3NzZXNzaW5nIApJbiB0aGlzIHNlY3Rpb24gd2Ugc2hhbGwgcHJvY2VzcyB0aGUgZGF0YSB0byBiZSBtb3JlIGVmZmVjdGl2ZSBmb3IgbW9kZWxsaW5nLgoKYGBge3J9CmFiYWxvbmUgPC0gcmVhZC5jc3YoIi9Vc2Vycy9kYWtzaG11a2hyYS9EZXNrdG9wL3VuaS9tYXN0ZXJzL2hleDVfWlpTQzU4NTVfMjAyNC9hYmFsb25lLmNzdiIpCmFiYWxvbmUgPC1hcy5kYXRhLmZyYW1lKGFiYWxvbmUpCmFiYWxvbmUkU2V4IDwtIGFzLmZhY3RvcihhYmFsb25lJFNleCkKYGBgCgpOdWxsIGFuZCB6ZXJvIHZhbHVlIGRldGVjdGlvbiBpcyBkb25lIGJlbG93IHRvIGRldGVjdCBhbnkgbm9uc2Vuc2ljYWwgdmFsdWVzIApgYGB7cn0KY29sU3Vtcyhpcy5uYShhYmFsb25lKSkKY29sU3VtcyhhYmFsb25lPT0iIikKY29sU3VtcyhhYmFsb25lPT0wKQpgYGAKV2UgY2FuIHNlZSB0aGF0IGhlaWdodCBoYXMgdHdvIHplcm8gdmFsdWVzLCB3ZSBzaGFsbCByZW1vdmUgdGhlc2UuIEJ5IGluc3BlY3RpbmcgdGhlIGNzdiBnaXZlbiBhbmQgc29ydGluZyBieSBoZWlnaHQgd2UgY2FuIHNlZSB0aGF0IHRoZWlyIGFyZSB0d28gZXh0cmVtZSBvdXRsaWVycyBhcyB3ZWxsLiBSZW1vdmluZyB0aGVzZSBvdXRsaWVycyB3aWxsIGVuc3VyZSBvdXIgZGlzdHJpYnV0aW9ucyBhcmUgbW9yZSBmcmllbmRseS4KCmBgYHtyfQphYmFsb25lIDwtIGFiYWxvbmVbYWJhbG9uZSRIZWlnaHQ+MCwgXQphYmFsb25lID0gYWJhbG9uZVshKGFiYWxvbmUkSGVpZ2h0ID09IDIyNiksXQphYmFsb25lID0gYWJhbG9uZVshKGFiYWxvbmUkSGVpZ2h0ID09IDEwMyksXQpgYGAKCiMjIEVEQSAKT3VyIGRhdGEgaXMgbm93IHJlYWR5IGZvciBzb21lIGV4cGxvcmF0aW9uLCB3ZSBzaGFsbCBleGFtaW5lIGRpc3RyaWJ1dGlvbnMgYW5kIGVtcGxveSB2YXJpb3VzIHN0YXRpc3RpY2FsIHRlc3RzIHRvIGdhaW4gYSBiZXR0ZXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgZGF0YS4gCgpgYGB7cn0KcGFyKG1mcm93ID0gYygzLCAzKSkKCgpoaXN0KGFiYWxvbmUkTGVuZ3RoLAogICAgIHhsYWIgPSBhYmFsb25lJExlbmd0aCwKICAgICBtYWluID0gIkhpc3RvZ3JhbSBvZiBsZW5ndGgiICwKICAgICBicmVha3MgPSBzcXJ0KGxlbmd0aChhYmFsb25lJExlbmd0aCkpICMgc2V0IG51bWJlciBvZiBiaW5zCikKCmhpc3QoYWJhbG9uZSRIZWlnaHQsCiAgICAgeGxhYiA9IGFiYWxvbmUkSGVpZ2h0LAogICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIEhlaWdodCIgLAogICAgIGJyZWFrcyA9IHNxcnQobGVuZ3RoKGFiYWxvbmUkSGVpZ2h0KSkgIyBzZXQgbnVtYmVyIG9mIGJpbnMKKQoKaGlzdChhYmFsb25lJERpYW1ldGVyLAogICAgIHhsYWIgPSBhYmFsb25lJERpYW1ldGVyLAogICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIERpYW1ldGVyIiAsCiAgICAgYnJlYWtzID0gc3FydChsZW5ndGgoYWJhbG9uZSREaWFtZXRlcikpICMgc2V0IG51bWJlciBvZiBiaW5zCikKCmhpc3QoYWJhbG9uZSRXaG9sZS53ZWlnaHQsCiAgICAgeGxhYiA9IGFiYWxvbmUkV2hvbGUud2VpZ2h0LAogICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIFdob2xlLndlaWdodCIgLAogICAgIGJyZWFrcyA9IHNxcnQobGVuZ3RoKGFiYWxvbmUkV2hvbGUud2VpZ2h0KSkgIyBzZXQgbnVtYmVyIG9mIGJpbnMKKQoKaGlzdChhYmFsb25lJFNodWNrZWQud2VpZ2h0LAogICAgIHhsYWIgPSBhYmFsb25lJFNodWNrZWQud2VpZ2h0LAogICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIFNodWNrZWQud2VpZ2h0IiAsCiAgICAgYnJlYWtzID0gc3FydChsZW5ndGgoYWJhbG9uZSRTaHVja2VkLndlaWdodCkpICMgc2V0IG51bWJlciBvZiBiaW5zCikKCmhpc3QoYWJhbG9uZSRWaXNjZXJhLndlaWdodCwKICAgICB4bGFiID0gYWJhbG9uZSRWaXNjZXJhLndlaWdodCwKICAgICBtYWluID0gIkhpc3RvZ3JhbSBvZiBWaXNjZXJhLndlaWdodCIgLAogICAgIGJyZWFrcyA9IHNxcnQobGVuZ3RoKGFiYWxvbmUkVmlzY2VyYS53ZWlnaHQpKSAjIHNldCBudW1iZXIgb2YgYmlucwopCgpoaXN0KGFiYWxvbmUkU2hlbGwud2VpZ2h0LAogICAgIHhsYWIgPSBhYmFsb25lJFNoZWxsLndlaWdodCwKICAgICBtYWluID0gIkhpc3RvZ3JhbSBvZiBTaGVsbC53ZWlnaHQiICwKICAgICBicmVha3MgPSBzcXJ0KGxlbmd0aChhYmFsb25lJFNoZWxsLndlaWdodCkpICMgc2V0IG51bWJlciBvZiBiaW5zCikKCmJhcnBsb3QodGFibGUoYWJhbG9uZSRTZXgpLAogICAgICAgIHlsYWIgPSAiRnJlcXVlbmN5IiwKICAgICAgICB4bGFiID0gIlNleCIpCmBgYApXZSBjYW4gc2VlIGZyb20gdGhlIGFib3ZlIGhpc3RvZ3JhbXMgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgTGVuZ3RoIGFuZCBEaWFtZXRlciBhcmUgbGVmdCBza2V3ZWQgd2hpbHN0IEhlaWdodCBpcyBuZXV0cmFsIG1pbWlja2luZyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIApUaGUgdmFyaWFibGVzIG9mIFNodWNrZWQgd2VpZ2h0IGFuZCBWaXNjZXJhIHdlaWdodCBhcmUgaGlnaGx5IHJpZ2h0IHNrZXdlZC4gVGhlIGRpc3RyaWJ1dGlvbiBvZiBzZXggYW1vbmcgdGhlIGNsYXNzZXMgb2YgaW5mYW50IGZlbWFsZSBhbmQgbWFsZSBzZWVtIHRvIGJlIHJlbGF0aXZlbHkgZXZlbiwgaGVuY2UgY2xhc3MgaW1iYWxhbmNlIHdpbGwgbm90IGJlIGFuIGlzc3VlIGluIG1vZGVsbGluZy4gCgpMZXRzIGxvb2sgZnVydGhlciBpbnRvIG91ciBudW1lcmljIGZlYXR1cmVzIGZvciBxdWVzdGlvbiAxIGkuZS4gSGVpZ2h0LCBMZW5ndGgsIERpYW1ldGVyIAoKYGBge3J9Cm51bWVyaWNfY29scyA8LSBhYmFsb25lW2MoIkhlaWdodCIsIkxlbmd0aCIsIkRpYW1ldGVyIildCmdncGFpcnMobnVtZXJpY19jb2xzKSAKYGBgCmFzIHdlIGNhbiBzZWUgZnJvbSB0aGUgcGxvdCBhYm92ZSB0aGUgcGFpcndpc2UgY29ycmVsYXRpb25zIGluZGljYXRlIHRoYXQgdGhlIHZhcmlhYmxlcyBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQgd2l0aCBvbmUgYW5vdGhlci4gVGhpcyBmdXJ0aGVyIGVtcGhhc2lzZWQgYnkgdGhlIHNjYXR0ZXIgcGxvdHMgYmV0d2VlbiB0aGUgdmFyaW91cyBwYWlycy4gSGlnaGx5IGNvcnJlbGF0ZWQgdmFyaWFibGVzIGNhbiBiZSBhIHByb2JsZW0gZm9yIHRoZSBpbnRlcnByZXRhdGlvbiBmb3IgbWFueSBsaW5lYXIgbW9kZWxzIGhvd2V2ZXIgcHJlZGljdGlvbiBzdHJlbmd0aCBpcyB1bmNoYW5nZWQuIEZyb20gdGhpcyBwbG90IHdlIGNhbiBkZWR1Y2UgdGhhdCB0aGUgdGhyZWUgdmFyaWFibGVzIGFyZSBub24gbm9ybWFsIG1hcmdpbmFsbHkgYW5kIGpvaW50bHkuCgpgYGB7cn0KbXZuKG51bWVyaWNfY29scywgbXZuVGVzdD0ibWFyZGlhIiwgbXVsdGl2YXJpYXRlUGxvdD0icXEiKQpgYGAKV2UgY2FuIHNlZSBmcm9tIHRoZSBhYm92ZSBzdGF0aXN0aWNhbCB0ZXN0cyAoTWFyZGlhIGFuZCBBbmRlcnNvbiBEYXJsaW5nKSB0aGF0IG5vcm1hbGl0eSBpcyByZWplY3RlZCBqb2ludGx5IGFuZCBtYXJnaW5hbGx5LiBUaGUgQ2hpLVNxdWFyZSBwbG90IGFsc28gcmV2ZWFsIHRoZSBzYW1lIGNvbmNsdXNpb24gd2l0aCB0aGUgZGF0YSBwb2ludCB2ZWVyaW5nIG9mZiB0aGUgZGlhZ29uYWwuIEhvd2V2ZXIgdGhpcyBjb25jbHVzaW9uIG9mIG5vbiBub3JtYWxpdHkgbmVlZHMgdG8gYmUgdGFrZW4gd2l0aCBhIGdyYWluIG9mIHNhbHQuIEFzIG1lbnRpb24gd2l0aGluIHRoZSBaWlNDNTg1NSBub3RlcyAKCiJhcyB0aGUgc2FtcGxlIHNpemUgaW5jcmVhc2VzLCB0aGUgQ2VudHJhbCBMaW1pdCBUaGVvcmVtIHRlbGxzIHVzIHRoYXQgbWFueSBzdGF0aXN0aWNzLCBpbmNsdWRpbmcgc2FtcGxlIG1lYW5zIGFuZCAobXVjaCBtb3JlIHNsb3dseSkgc2FtcGxlIHZhcmlhbmNlcyBhbmQgY292YXJpYW5jZXMsIGFwcHJvYWNoIG5vcm1hbGl0eeKAlGFuZCBtdWx0aXZhcmlhdGUgc3RhdGlzdGljcyBnZW5lcmFsbHkgYXBwcm9hY2ggbXVsdGl2YXJpYXRlIG5vcm1hbGl0eS4gVGhpcyBtZWFucyB0aGF0IHJlZ2FyZGxlc3Mgb2YgdGhlIHVuZGVybHlpbmcgZGlzdHJpYnV0aW9uLCB0aGUgc3RhdGlzdGljYWwgcHJvY2VkdXJlcyBkZXBlbmRpbmcgb24gdGhlIG5vcm1hbGl0eSBhc3N1bXB0aW9uIGJlY29tZSB2YWxpZCBldmVuIGFzIHRoZSBjaGFuY2VzIHRoYXQgYSBzdGF0aXN0aWNhbCBoeXBvdGhlc2lzIHRlc3Qgd2lsbCBkZXRlY3Qgbm9uLW5vcm1hbGl0eSB0aGVyZSBpcyBhcHByb2FjaGVzIDEiIAoKQnkgZXhhbXBsaW5nIHRoZSBoaXN0b2dyYW1zIHNob3duIGVhcmxpZXIgaGVpZ2h0IGFuZCBhIHRyYW5zZm9ybWVkIExlbmd0aCBhbmQgRGlhbWV0ZXIgKHNob3duIGxhdGVyIG9uKSB3aWxsIGJlIGFkZXF1YXRlIHRvIGFzc3VtZSBtdWx0aXZhcmlhdGUgbm9ybWFsaXR5IGV2ZW4gaWYgdGhlIHAtdmFsdWUgaXMgc21hbGwgZm9yIHRoZXNlIHN0YXRpc3RpY2FsIHRlc3RzLiAKCmBgYHtyfQpudW1lcmljX2NvbHMuVCA8LSBudW1lcmljX2NvbHMgJT4lIG11dGF0ZSgKICBgSGVpZ2h0YCA9IChgSGVpZ2h0YCksICMgbGVhdmUgaGVpZ2h0IGFzIGlzCiAgYExlbmd0aGAgPSBMZW5ndGheMiwgCiAgYERpYW1ldGVyYCA9IERpYW1ldGVyXjIpCmtlZXBzIDwtIGMoIkhlaWdodCIsIkxlbmd0aCIsIkRpYW1ldGVyIikKbnVtZXJpY19jb2xzLlQgPSBudW1lcmljX2NvbHMuVFtrZWVwc10KCmFiYWxvbmUuVCA8LSBudW1lcmljX2NvbHMuVAphYmFsb25lLlQkU2V4IDwtIGFiYWxvbmUkU2V4CgpnZ3BhaXJzKGFiYWxvbmUuVCkKYGBgCgphcyB3ZSBjYW4gc2VlIHVzaW5nIHRoZSBzcXVhcmUoKSB0cmFuc2Zvcm1hdGlvbiBvbiBMZW5ndGggYW5kIERpYW1ldGVyIG1vdmVkIGl0cyBkaXN0cmlidXRpb24gcGxvdHMgY2xvc2VyIHRvIG5vcm1hbC4gCgoKIyMgTW9kZWxzIGFuZCBNZXRob2RzIAojIyMgUTEgIAoKIyMjIyBEYXRhIG1hbml0cHVsYXRpb24gCkxldHMgc3BsaXQgb3VyIGRhdGEgaW50byBhIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldCBhbmQgc2NhbGUgaXQuIFRoZSByZWFzb24gd2Ugc2hhbGwgc2NhbGUgdGhlIGRhdGEgc2V0IGlzIHRoYXQgbWFueSBtb2RlbHMgc3VjaCBhcyBzdXBwb3J0IHZlY3RvciBtYWNoaW5lIHJlcXVpcmUgdGhhdCBpdHMgdmFyaWFibGVzIGJlIG9uIHRoZSBzYW1lIHNjYWxlIGZvciBpdCB3b3JrIGVmZmVjdGl2ZWx5LiBOb3RlIHRoZSBzY2FsaW5nIGlzIGZpcnN0IGRvbmUgb24gdGhlIHRyYWluIHNldCBhbmQgdXNpbmcgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBmcm9tIHRoZSB0cmFpbiBzZXQgd2Ugc2NhbGUgdGhlIHRlc3QuIFRoaXMgc28gdGhhdCBkYXRhIGxlYWthZ2UgZG9lcyBub3Qgb2NjdXIuIAoKQWxzbyBub3RlIHRoYXQgUHJpbmNpcGFsIENvbXBvbmVudCBhbmFseXNpcyB3YXMgZXhwbG9yZWQgYXMgYSBjb3JyZWxhdGlvbiByZWR1Y3Rpb24gdGVjaG5pcXVlIGhvd2V2ZXIgcHJlZGljdGlvbiByZXN1bHRzIHdlcmUgbGFyZ2VseSB1bmFmZmVjdGVkIGFuZCBpbnRlcnByZXRhdGlvbiBvZiBjb2VmZmljaWVudHMgd2FzIGxvc3QuIEhlbmNlIFBDQSBoYXMgYmVlbiBvbWl0dGVkIGluIHRoZSBmb2xsb3dpbmcgZGF0YSBtYW5pcHVsYXRpb24uIFBsZWFzZSBzZWUgYXBwZW5kaXggZm9yIGNvZGUgaWYgcmVxdWlyZWQuIAoKYGBge3J9CiMgam9pbiB0cmFuc2Zvcm1lZCBkYXRhIGZyYW1lIApxMWRhdGEgPC0gYWJhbG9uZS5UW2MoIlNleCIsIkhlaWdodCIsIkxlbmd0aCIsIkRpYW1ldGVyIildCgojIFNwbGl0IGludG8gdHJhaW4gYW5kIHRlc3QgCiMgY3JlYXRlRGF0YVBhcnRpdGlvbiBhdXRvbWF0aWNhbGx5IHN0cmF0aWZ5IHNhbXBsZXMgdG8gZW5zdXJlIGNsYXNzZXMgYXJlIGJhbG5jZWQgaW4gc3BsaXQgCnNldC5zZWVkKDQyKQp0cmFpbi5pbmRleCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHExZGF0YSRTZXgsIHAgPSAuNywgbGlzdCA9IEZBTFNFKQp0cmFpbiA8LSBxMWRhdGFbIHRyYWluLmluZGV4LF0KdGVzdCAgPC0gcTFkYXRhWy10cmFpbi5pbmRleCxdCgojIyMjIyBOT1RFIFNDQUxFIEFGVEVSIFNQTElUIFRPIEFWT0lEIERBVEEgTEVBS0FHRSAKIyBTY2FsZSB0cmFpbiAKdHJhaW4uc2NhbGVkIDwtIHNjYWxlKHRyYWluW2MoIkhlaWdodCIsIkxlbmd0aCIsIkRpYW1ldGVyIildKQojIGFwcGx5IHRoZSBzYW1lIG1lYW4gYW5kIHN0ZCBvZiBzY2FsZWQgdHJhaW4gdG8gdGVzdCB0byBhdm9pZCBkYXRhIGxlYWthZ2UgCnRlc3Quc2NhbGVkIDwtIHNjYWxlKHRlc3RbYygiSGVpZ2h0IiwiTGVuZ3RoIiwiRGlhbWV0ZXIiKV0sIGNlbnRlcj1hdHRyKHRyYWluLnNjYWxlZCwgInNjYWxlZDpjZW50ZXIiKSwgc2NhbGU9YXR0cih0cmFpbi5zY2FsZWQsICJzY2FsZWQ6c2NhbGUiKSkKCnRyYWluLnNjYWxlZCA8LSBhcy5kYXRhLmZyYW1lKHRyYWluLnNjYWxlZCkKdGVzdC5zY2FsZWQgPC0gYXMuZGF0YS5mcmFtZSh0ZXN0LnNjYWxlZCkKCnRyYWluLnNjYWxlZCRTZXggPC0gdHJhaW4kU2V4CnRlc3Quc2NhbGVkJFNleCAgPC0gdGVzdCRTZXgKCnRyYWluLnNjYWxlZCRTZXggPC0gcmVsZXZlbChmYWN0b3IodHJhaW4uc2NhbGVkJFNleCksIHJlZiA9ICJGIikKdGVzdC5zY2FsZWQkU2V4IDwtIHJlbGV2ZWwoZmFjdG9yKHRlc3Quc2NhbGVkJFNleCksIHJlZiA9ICJGIikKYGBgCgojIyMjIE1vZGVsbGluZyAKCk5vdyB0aGF0IHdlIGhhdmUgYWRlcXVhdGVseSBzcGxpdCBvdXIgZGF0YSB3ZSBhcmUgcmVhZHkgdG8gbW9kZWwgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIFNleCBhbmQgdmFyaWFibGVzIExlbmd0aCBIZWlnaHQgYW5kIERpYW1ldGVyLiBUaGUgbW9kZWxzIG9mIG11bHRpbm9taWFsIGxvZ2lzdGljIHJlZ3Jlc3Npb24gYW5kIHN1cHBvcnQgdmVjdG9yIG1hY2hpbmUgKHN2bSkgd2VyZSBjaG9zZW4gZm9yIHRoaXMgdGFzaywgYXMgd2UgbmVlZCBhIHdheSB0byBwcmVkaWN0IG11bHRpcGxlIGNsYXNzZXMgZm9yIGEgc2luZ2xlIGRlcGVuZGVudCB2YXJpYWJsZSAoU2V4KS4gTG9naXN0aWMgcmVncmVzc2lvbiBpbiBwYXJ0aWN1bGFyIHdhcyBjaG9zZW4gZm9yIGl0cyBzaW1wbGUgaW50ZXJwcmV0YXRpb24gb2YgaXRzIGNvZWZmaWNpZW50cyBhbmQgc3ZtIGlzIHJvYnVzdCBpbiBhZGp1c3RpbmcgdG8gbm9uIGxpbmVhciByZWxhdGlvbnNoaXBzIGluIHRoZSBkYXRhIHdpdGggYW4gYXBwcm9wcmlhdGUgY2hvaWNlIG9mIEtlcm5lbC4gCgpUaGUgNCByZWxhdGlvbnNoaXBzIHdlIHJlcXVpcmUgdG8gYmUgbW9kZWxlZCBhcmUgd2Ugc2hhbGwgbW9kZWwgZWFjaCBvbmUgb3V0cHV0dGluZyBhIHNjb3JlIGZvciBib3RoIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGE6IAotIHByZWRpY3RpbmcgdGhlIHNleCBvZiB0aGUgYWJhbG9uZSBpbiBnZW5lcmFsOwotIHByZWRpY3Rpbmcgc3BlY2lmaWNhbGx5IEluZmFudHMgYXMgb3Bwb3NlZCB0byBvdGhlcnMgKHRvIGF2b2lkIGhhcnZlc3RpbmcgdGhlbSk7Ci0gcHJlZGljdGluZyBzcGVjaWZpY2FsbHkgRmVtYWxlcyBhcyBvcHBvc2VkIHRvIG90aGVycyAod2hlbiBwcm9maXRhYmlsaXR5IGlzIHByaW9yaXRpc2VkKTsKLSBwcmVkaWN0aW5nIHNwZWNpZmljYWxseSBNYWxlcyBhcyBvcHBvc2VkIHRvIG90aGVycyAod2hlbiBzdXN0YWluYWJpbGl0eSBpcyBwcmlvcml0aXNlZCkuCgoKIyMjIyBQcmVkaWN0aW5nIHRoZSBzZXggb2YgdGhlIGFiYWxvbmUgaW4gZ2VuZXJhbAoKIyMjIyBMb2dpc3RpYyBSZWdyZXNzaW9uIAoKYGBge3J9CiMjIyMjIyMjIyMjIyMjIyMjIyBMT0dJU1RJQyAtLSBUUkFJTiBJUyBTQ0FMRUQKIyB3ZSBjYW4gc2VlIHRoYXQgdGhlIFRyYWluIGRhdGEgaXMgbm90IHNwZXJhYmxlIGxpbmVhcmx5IApjb2xvcnMgPC0gYygiIzk5OTk5OSIsICIjRTY5RjAwIiwgIiM1NkI0RTkiKQpjb2xvcnMgPC0gY29sb3JzW2FzLm51bWVyaWModHJhaW4uc2NhbGVkJFNleCldCnMzZCA8LSBzY2F0dGVycGxvdDNkKHRyYWluLnNjYWxlZFtjKCJMZW5ndGgiLCJEaWFtZXRlciIsIkhlaWdodCIpXSwgcGNoID0gMTYsIGNvbG9yID0gY29sb3JzICwgYm94PUZBTFNFKQpsZWdlbmQoInJpZ2h0IiwgbGVnZW5kID0gbGV2ZWxzKHRyYWluLnNjYWxlZCRTZXgpLAogICAgICAgY29sID0gIGMoIiM5OTk5OTkiLCAiI0U2OUYwMCIsICIjNTZCNEU5IiksIHBjaCA9IDE2KQpgYGAKCkFzIHdlIGNhbiBzZWUgZm9ybSB0aGUgYWJvdmUgc2NhdHRlciBwbG90IHRoZSBkYXRhIGlzIG5vdCBsaW5lYXJseSBzZXBhcmFibGUgZm9yIHRoZSBjbGFzc2VzLiBMZXRzIHRlc3QgdGhpcy4KCmBgYHtyfQojIyMjIyMjIyAiQWNjIDAuNTIyMjQ1MDM3NjQ1NDQ4Igpsb2cuc2NhbGVkLm0gPC0gbXVsdGlub20oU2V4fkxlbmd0aCArIERpYW1ldGVyICsgSGVpZ2h0LCBkYXRhID0gdHJhaW4uc2NhbGVkKQpzdW1tYXJ5KGxvZy5zY2FsZWQubSkKCiNQcmVkaWN0aW5nIHRoZSB2YWx1ZXMgZm9yIHRyYWluIGRhdGFzZXQKdHJhaW4ucHJlZGljdGVkX3ZhbHMgPC0gcHJlZGljdChsb2cuc2NhbGVkLm0sIG5ld2RhdGEgPSB0cmFpbi5zY2FsZWRbYygiTGVuZ3RoIiwiRGlhbWV0ZXIiLCJIZWlnaHQiKV0sICJjbGFzcyIpCgojIEJ1aWxkaW5nIGNsYXNzaWZpY2F0aW9uIHRhYmxlCnBhc3RlKCdBY2MnLCBhY2N1cmFjeSh0cmFpbi5zY2FsZWQkU2V4LCB0cmFpbi5wcmVkaWN0ZWRfdmFscykpCmNmLnRyYWluIDwtIGNhcmV0Ojpjb25mdXNpb25NYXRyaXgoZGF0YT10cmFpbi5wcmVkaWN0ZWRfdmFscywgcmVmZXJlbmNlPXRyYWluLnNjYWxlZCRTZXgpCmNmLnRyYWluCmBgYApUaGUgdHJhaW5pbmcgc2V0IGFjY3VyYWN5IGlzICJBY2MgMC41MjIyNDUwMzc2NDU0NDgiLldlIGNhbiBzZWUgZm9ybSB0aGUgY29uZnVzaW9uIG1hdHJpeCB0aGF0IHRoZSBtb2RlbCBpbnN0IGFibGUgdG8gY29ycmVjdGx5IGNsYXNzaWZ5IEZlbWFsZSBkYXRhIHBvaW50cyBhcyBtdWNoIGFzIEluZmFudHMgYW5kIE1hbGUuIExldHMgZXhhbWluZSB0aGUgdGVzdCBzZXQgYW5kIHByZWRpY3RlZCB1c2luZyBhcmUgdHJhaW5lZCBtb2RlbC4gIAoKYGBge3J9CnRlc3QucHJlZGljdGVkX3ZhbHMgPC0gcHJlZGljdChsb2cuc2NhbGVkLm0sIG5ld2RhdGEgPSB0ZXN0LnNjYWxlZFtjKCJMZW5ndGgiLCJEaWFtZXRlciIsIkhlaWdodCIpXSwgImNsYXNzIikKIyBCdWlsZGluZyBjbGFzc2lmaWNhdGlvbiB0YWJsZQpwYXN0ZSgnQWNjJywgYWNjdXJhY3kodGVzdC5zY2FsZWQkU2V4LCB0ZXN0LnByZWRpY3RlZF92YWxzKSkKY2YudGVzdCA8LSBjYXJldDo6Y29uZnVzaW9uTWF0cml4KGRhdGE9dGVzdC5wcmVkaWN0ZWRfdmFscywgcmVmZXJlbmNlPXRlc3Quc2NhbGVkJFNleCkKY2YudGVzdApgYGAKClRoZSB0ZXN0IHNldCBpcyAiQWNjIDAuNTM1NTcxNTQyNzY1Nzg3IiBvdXIgbW9kZWwgaGFzIGdlbmVyYWxpc2VkIGFzIG11Y2ggYXMgaXQgY2FuIHByZWRpY3Rpbmcgb24gbmV3IGRhdGEuIFRoZSByZXN1bHRzIG9mIHRoZSBvY25mdXNpb24gbWF0cml4IGZvciB0aGUgdGVzdCBzZXQgYXJlIHNpbWlsYXIgdG8gdGhlIHRyYWluIHNldC4gTGV0cyBjaGVjayBvdXQgU1ZNLiAKCiMjIyMgU3VwcG9ydCBWZWN0b3IgTWFjaGluZQoKYGBge3J9CnN1bW1hcnkodHVuZWQuc3ZtIDwtIHR1bmUuc3ZtKFNleH4uLGRhdGE9dHJhaW4uc2NhbGVkLCBrZXJuZWw9InJhZGlhbCIsIGdhbW1hID0gMTBeKC0xOjEpLCBjb3N0ID0gMTBeKC0xOjEpKSkKdHVuZWQuc3ZtJGJlc3QubW9kZWwKCnRyYWluLnByZWRpY3RlZF92YWxzIDwtIHByZWRpY3QodHVuZWQuc3ZtJGJlc3QubW9kZWwsIG5ld2RhdGE9dHJhaW4uc2NhbGVkW2MoIkxlbmd0aCIsIkRpYW1ldGVyIiwiSGVpZ2h0IildLCBkZWNpc2lvbi52YWx1ZXM9VFJVRSkKIyBCdWlsZGluZyBjbGFzc2lmaWNhdGlvbiB0YWJsZQpwYXN0ZSgnQWNjJywgYWNjdXJhY3kodHJhaW4uc2NhbGVkJFNleCwgdHJhaW4ucHJlZGljdGVkX3ZhbHMpKQpjZi50cmFpbiA8LSBjYXJldDo6Y29uZnVzaW9uTWF0cml4KGRhdGE9dHJhaW4ucHJlZGljdGVkX3ZhbHMsIHJlZmVyZW5jZT10cmFpbi5zY2FsZWQkU2V4KQpjZi50cmFpbgoKYGBgCgpGb3IgdGhlIFNWTSB3ZSB0dW5lIGFjcm9zcyBzZWxlY3Rpb24gb2YgaHlwZXIgcGFyYW1ldGVycyBhbmQgZmluZCB0aGUgYmVzdCBzZWxlY3Rpb24gYW5kIHRoZW4gdXNpbmcgdGhlIGJlc3QgbW9kZWwgd2UgcHJlZGljdCB3aXRoIHRoZSB0cmFpbmluZyBzZXQuIFRoZSByZXN1bHRzIGFyZSBleHRyZW1lbHkgc2ltaWxhciB0byBsb2dpc3RpYyB0cmFpbiB3aXRoIGEgIkFjYyAwLjUzODMyOTkxMTAxOTg0OSIgYW5kIHNpbWlsYXIgY29uZnVzaW9uIG1hdHJpeC4gbGV0cyBzZWUgdGhlIHRlc3Qgc2V0LiAKCmBgYHtyfQp0ZXN0LnByZWRpY3RlZF92YWxzIDwtIHByZWRpY3QodHVuZWQuc3ZtJGJlc3QubW9kZWwsIG5ld2RhdGE9dGVzdC5zY2FsZWRbYygiTGVuZ3RoIiwiRGlhbWV0ZXIiLCJIZWlnaHQiKV0sIGRlY2lzaW9uLnZhbHVlcz1UUlVFKQojIEJ1aWxkaW5nIGNsYXNzaWZpY2F0aW9uIHRhYmxlCnBhc3RlKCdBY2MnLCBhY2N1cmFjeSh0ZXN0LnNjYWxlZCRTZXgsIHRlc3QucHJlZGljdGVkX3ZhbHMpKQpjZi50ZXN0IDwtIGNhcmV0Ojpjb25mdXNpb25NYXRyaXgoZGF0YT10ZXN0LnByZWRpY3RlZF92YWxzLCByZWZlcmVuY2U9dGVzdC5zY2FsZWQkU2V4KQpjZi50ZXN0CmBgYAoKTm90ZSBhbiAiQWNjIDAuNTEyMzkwMDg3OTI5NjU2IiBzY29yZSBhbmQgc2ltaWxhciBpbiBhYmlsaXR5IHRvIGNsYXNzaWZ5IEZlbWFsZSBkYXRhIHBvaW50cyBhcyBsb2dpc3RpY3MgcmVncmVzc2lvbiBldmVuIHRobyBhIG5vbiBsaW5lYXIga2VuZXJhbCBtZXRob2QgInJhZGlhbCIgd2FzIHNwZWNpZmllZC4gCgoKIyMjIyBDb25jbHVzaW9uIGZvciBwcmVkaWN0aW5nIHRoZSBzZXggb2YgdGhlIGFiYWxvbmUgaW4gZ2VuZXJhbAoKQm90aCBNb2RlbHMgb2YgTG9naXN0aWMgcmVncmVzc2lvbiBoYWQgbWVkaW9jcmUgcGVyZm9ybWFuY2Ugb24gdW5zZWVuIGRhdGEsIGFzIHN1Y2ggbG9naXN0aWMgcmVncmVzc2lvbiBzaG91bGQgYmUgdXNlZCB3aXRoIGl0cyBhY2N1cmFjeSBzY29yZSBvZiAwLjUzIGFuZCBoaWdobHkgaW50ZXJwcmV0YWJsZSBjb2VmZmljaWVudHMuIAoKCiMjIyMgUHJlZGljdGluZyBzcGVjaWZpY2FsbHkgSW5mYW50cwpBcyB3ZSBzaGlmdCB0byBhIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtIGNsYXNzIGltYmFsYW5jZSBtYXkgYmVjb21lIGFuIGlzc3VlIGFzIHN1Y2ggbW9yZSByb2J1c3QgbWV0cmljcyBzdWNoIGFzIEYxLXNjb3JlIHdpbGwgYmUgcmVwb3J0ZWQuIFRoZSBzYW1lIG1vZGVscyBhYm92ZSB3aWxsIGJlIHRyYWluZWQgYW5kIHRlc3RlZC4gCgpGaXJzdCB3ZSBuZWVkIHRvIGdldCBvdXIgZGF0YS4gCmBgYHtyfQojIyMjIyMjIyMjIyMjIyMjIyMgSU5GQU5UIERBVEEgUFJFUCAKaW5mLnRyYWluLnNjYWxlZCA8LSB0cmFpbi5zY2FsZWQKbGV2ZWxzKGluZi50cmFpbi5zY2FsZWQgJFNleCkgPWMoJ05vdCBJbmZhbnQnLCAnSScsICdOb3QgSW5mYW50JykKCmluZi50ZXN0LnNjYWxlZCA8LSB0ZXN0LnNjYWxlZApsZXZlbHMoaW5mLnRlc3Quc2NhbGVkICRTZXgpID1jKCdOb3QgSW5mYW50JywgJ0knLCAnTm90IEluZmFudCcpCgpgYGAKCkxldHMgc2VlIHRoZSBkaXN0cmlidXRpb24gZm9yIGluZmFudHMuIAoKYGBge3J9CiMjIyMjIyMjIyMjIyMjIyMjIyBJTkZBTlQgTE9HSVNUSUMgVFJBSU4gIApjb2xvcnMgPC0gYygiI0U2OUYwMCIsICIjNTZCNEU5IikKY29sb3JzIDwtIGNvbG9yc1thcy5udW1lcmljKGluZi50cmFpbi5zY2FsZWQkU2V4KV0KczNkIDwtIHNjYXR0ZXJwbG90M2QoaW5mLnRyYWluLnNjYWxlZFtjKCJMZW5ndGgiLCJEaWFtZXRlciIsIkhlaWdodCIpXSwgcGNoID0gMTYsIGNvbG9yID0gY29sb3JzICwgYm94PUZBTFNFKQpsZWdlbmQoInJpZ2h0IiwgbGVnZW5kID0gbGV2ZWxzKGluZi50cmFpbi5zY2FsZWQkU2V4KSwKICAgICAgIGNvbCA9ICBjKCIjRTY5RjAwIiwgIiM1NkI0RTkiKSwgcGNoID0gMTYpCmBgYApjb21wYXJlZCB3ZSBjYW4gc2VlIHRoYXQgdGhlIGRhdGEgaXMgbXVjaCBtb3JlIGxpbmVhcmx5IHNwZXJhYmxlLiAKCgpgYGB7cn0KI2xvZ2lzdGljIHJlZ3Jlc3Npb24KbG9nLnNjYWxlZC5tIDwtIGdsbShTZXh+IExlbmd0aCArIERpYW1ldGVyICsgSGVpZ2h0LCBkYXRhPWluZi50cmFpbi5zY2FsZWQsIGZhbWlseSA9ICdiaW5vbWlhbCcpCnN1bW1hcnkobG9nLnNjYWxlZC5tKQoKCiNQcmVkaWN0aW5nIHRoZSB2YWx1ZXMgZm9yIHRyYWluIGRhdGFzZXQKdHJhaW4ucHJlZGljdGVkX3ZhbHMgPC0gcHJlZGljdChsb2cuc2NhbGVkLm0sIG5ld2RhdGEgPSBpbmYudHJhaW4uc2NhbGVkW2MoIkxlbmd0aCIsIkRpYW1ldGVyIiwiSGVpZ2h0IildKQp0cmFpbi5wcmVkaWN0X2NsYXNzIDwtIGlmZWxzZSh0cmFpbi5wcmVkaWN0ZWRfdmFscz4wLjUsICAnSScsICdOb3QgSW5mYW50JykKCiNBY2N1cmFjeSAwLjc4MjM0MDg2MjQyMjk5OApwYXN0ZSgnQWNjJywgYWNjdXJhY3koaW5mLnRyYWluLnNjYWxlZCRTZXgsIHRyYWluLnByZWRpY3RfY2xhc3MpKQoKI0YxIHNjb3JlIDAuODU0MjYyMQpmMV92YWwgPC0gTUxtZXRyaWNzOjpGMV9TY29yZShpbmYudHJhaW4uc2NhbGVkJFNleCwgdHJhaW4ucHJlZGljdF9jbGFzcykKZjFfdmFsCgojIEJ1aWxkaW5nIGNsYXNzaWZpY2F0aW9uIHRhYmxlCnRhYmxlKGluZi50cmFpbi5zY2FsZWQkU2V4LCB0cmFpbi5wcmVkaWN0X2NsYXNzKQpgYGAKVGhlIGluaXRpYWwgdHJhaW4gcmVzdWx0cyBhcmUgcHJvdmlkaW5nIHVzIHdpdGggcmVsYXRpdmUgZ29vZCBhY2N1cmFjeSBvZiAiQWNjIDAuNzgzMzY3NTU2NDY4MTcyIiBhbmQgRjEgc2NvcmUgb2YgMC44NTQ0NDkzLiAKRnJvbSB0aGUgY29uZnVzaW9uIG1hdHJpeCB3ZSBjYW4gc2VlIHRoYXQgYWx0aG91Z2ggbm9uIGluZmFudHMgYXJlIGNsYXNzaWZpZWQgYXQgYSBoaWdoZXIgcmF0ZSBpbmZhbnRzIGFyZSBpbmNvcnJlY3RseSBjbGFzc2lmaWVkIHdpdGggbW9yZSBmcmVxdWVuY3kgKGxvdyBwcmVjaXNpb24pCgpMZXRzIGV4YW1pbmUgdGhlIHRlc3Qgc2V0LgoKYGBge3J9CiNQcmVkaWN0aW5nIHRoZSB2YWx1ZXMgZm9yIHRyYWluIGRhdGFzZXQKdGVzdC5wcmVkaWN0ZWRfdmFscyA8LSBwcmVkaWN0KGxvZy5zY2FsZWQubSwgbmV3ZGF0YSA9IGluZi50ZXN0LnNjYWxlZFtjKCJMZW5ndGgiLCJEaWFtZXRlciIsIkhlaWdodCIpXSkKdGVzdC5wcmVkaWN0X2NsYXNzIDwtIGlmZWxzZSh0ZXN0LnByZWRpY3RlZF92YWxzPjAuNSwgICdJJywgJ05vdCBJbmZhbnQnKQoKcGFzdGUoJ0FjYycsIGFjY3VyYWN5KGluZi50ZXN0LnNjYWxlZCRTZXgsIHRlc3QucHJlZGljdF9jbGFzcykpCgpmMV92YWwgPC0gTUxtZXRyaWNzOjpGMV9TY29yZShpbmYudGVzdC5zY2FsZWQkU2V4LCB0ZXN0LnByZWRpY3RfY2xhc3MpCmYxX3ZhbAoKIyBCdWlsZGluZyBjbGFzc2lmaWNhdGlvbiB0YWJsZQp0YWJsZShpbmYudGVzdC5zY2FsZWQkU2V4LCB0ZXN0LnByZWRpY3RfY2xhc3MpCmBgYApUaGUgcmVzdWx0cyBhcmUgY29tcGFyYWJsZSB0byB0cmFpbiwgd2Ugc2hvdWxkIG5vdGUgdGhleSBhcmUgc2xpZ2h0bHkgbG93ZXIgYXMgd2UgYXJlIHByZWRpY3Rpbmcgb24gdW5zZWVuIGRhdGEuIExldHMgZXhhbWluZSB0aGUgU1ZNLiAKCmBgYHtyfQojIHRyYWluIHN2bQp0dW5lZC5zdm0gPC0gdHVuZS5zdm0oU2V4fi4sZGF0YT1pbmYudHJhaW4uc2NhbGVkLCBrZXJuZWw9InJhZGlhbCIsIGdhbW1hID0gMTBeKC0xOjEpLCBjb3N0ID0gMTBeKC0xOjEpKQp0dW5lZC5zdm0kYmVzdC5tb2RlbAoKdHJhaW4ucHJlZGljdGVkX3ZhbHMgPC0gcHJlZGljdCh0dW5lZC5zdm0kYmVzdC5tb2RlbCwgbmV3ZGF0YT1pbmYudHJhaW4uc2NhbGVkW2MoIkxlbmd0aCIsIkRpYW1ldGVyIiwiSGVpZ2h0IildLCBkZWNpc2lvbi52YWx1ZXM9VFJVRSkKCiMgTWV0cmljcyAKY2YudHJhaW4gPC0gY2FyZXQ6OmNvbmZ1c2lvbk1hdHJpeChkYXRhPXRyYWluLnByZWRpY3RlZF92YWxzLCByZWZlcmVuY2U9aW5mLnRyYWluLnNjYWxlZCRTZXggLG1vZGUgPSAiZXZlcnl0aGluZyIpCmNmLnRyYWluCgpgYGAKClRoZSBzY29yZXMgaW4gdGVybXMgb2YgRjEgc2NvcmUgYW5kIGFjY3VyYWN5IGlzIHNpbWlsYXIgdG8gdGhlIHRyYWluIGxvZ2lzdGljIHNjb3JlcyBob3dldmVyIGZyb20gdGhlIGNvbmZ1c2lvbiBtYXRyaXggdGggU1ZNIGlzIGFibGUgdG8gYmV0dGVyIGNsYXNzaWZ5IGluZmFudHMuIGxldHMgc2VlIHRoZSB0ZXN0IHNldC4gCgpgYGB7cn0KdGVzdC5wcmVkaWN0ZWRfdmFscyA8LSBwcmVkaWN0KHR1bmVkLnN2bSRiZXN0Lm1vZGVsLCBuZXdkYXRhPWluZi50ZXN0LnNjYWxlZFtjKCJMZW5ndGgiLCJEaWFtZXRlciIsIkhlaWdodCIpXSwgZGVjaXNpb24udmFsdWVzPVRSVUUpCgojIE1ldHJpY3MKY2YudGVzdCA8LSBjYXJldDo6Y29uZnVzaW9uTWF0cml4KGRhdGE9dGVzdC5wcmVkaWN0ZWRfdmFscywgcmVmZXJlbmNlPWluZi50ZXN0LnNjYWxlZCRTZXgsICxtb2RlID0gImV2ZXJ5dGhpbmciKQpjZi50ZXN0CgpgYGAKQWdhaW4gU1ZNIGlzIG11Y2ggYmV0dGVyIGF0IGNsYXNzaWZ5aW5nIEluZmFudHMuIAoKIyMjIyBDb25jbHVzaW9uIGZvciBwcmVkaWN0aW5nIHNwZWNpZmljYWxseSBJbmZhbnRzCkJvdGggbW9kZWxzIHBlcmZvcm1lZCBiZXR0ZXIgdGhlbiB0aGUgZmlyc3Qgc2NlbmFyaW8gZHVlIHRvIG1vcmUgJ3NlcGFyYWJsZScgZGF0YSBzZXQuIFN1cHBvcnQgdmVjdG9yIG1hY2hpbmUgc2hvdWxkIGJlIGNob3NlbiBhcyB0aGUgZ28gdG8gbW9kZWwgdG8gcHJlZGljdCB3aGljaCBhYmFsb25lIGlzIGFuIGluZmFudCB0byBhdm9pZCBoYXJ2ZXN0aW5nIHRoZW0uCgojIyMjIFByZWRpY3Rpbmcgc3BlY2lmaWNhbGx5IEZlbWFsZXMgYXMgb3Bwb3NlZCB0byBvdGhlcnMKCkZlbWFsZSBhYmFsb25lIGRldGVjdGlvbiBjYW4gYmUgaGlnaGx5IHByb2ZpdGFibGUgLCBhcyBzdWNoIGxldHMgZXhhbWluZSBob3cgcmVsaWFibHkgd2UgY2FuIHByZWRpY3QgdGhlbQoK