Team Information

This report was prepared for ChemsRUs by Evan Hernandez, hernae5 for the team “We Don’t Zinc So” My team members are: Jessica Coco, Tamia Brefo, Morgan Ford, and Jeffrey Chai cocoj2, brefot, fordm2, chaij

Our team used http://codalab.idea.rpi.edu/competitions/37

Introduction

Chems-R-Us has created an entry to the challenge at https://competitions.codalab.org/competitions/22892 based on logistic regression (LR). Their entry is in the file ‘FinalProjChemsRUs.Rmd’ Based on the information in the leaderboard under bennek, their entry is not performing feature selection well. The approaches tried by Chems-R-Us were LR with feature selection based on the coefficients of logistic regression with p-values used to determine importance.

The purpose of this report is to investigate alternative approaches to the that may help achieve high AUC scores on the testing set while correctly identifying the relevant features as measured by balanced accuracy.

Methods Used

The first method that I used for classification was randomForest. RandomForest works by creating a tree of nodes for selection and trying different variables at each level to find the correlation to the data. The second method I used was SVM (Support Vector Machines), which generates vectors about the points in the dataset to separate them into two classes. The first feature selection method that I used was Recursive Feature Elimination and the second feature selection I used was variable importance using the caret method ‘rpart’.

Data Description:

  1. The dataset had 168 attributes 2a. The test data was divided independently 2b. The training and validation data were made from a 90:10 split of the feature, class, and result data provided
  2. The testing set had 400 data points per feature, the validation set had 105 points per feature, and the training set had 905 data points per feature
  3. The scaling method used was the preProcess function with the scale method

Results Using All Features

The accuracy for the training models were across the board better than the validation models. The reason for this is that the training data had nine times the information that the validation data, giving the AI more time to learn and better classify the results based on the information given. The difference was not stark, and was only about 4-8% at most. Logistically, this means that the return was most likely logarithmic for data and accuracy and therefore there is optimization potential for runtime, data used, accuracy, etc. for both models.

Results Using Feature Selection

The two feature selection methods that I used were Recursive Feature Elimination and rpart Variable Importance. Recursive feature selection used a multi step process with k-folds in the data to repeat and evaluate what the most determining variables were. A similar process is used for rpart Variable Importance, and that selected the top 5 features only to be used. Recursie feature elimination for both classification methods performed better than all features for the training and validation sets, but did not perform as well on the challenge site or test data. Rpart Variable Importance performed the worst in the training, validation, and test sets for both classifiers, leading me to believe that rpart Variable Importance is not very viable for data that needs extreme accuracy, however the rpart ran significantly faster than the recursive feature elimination.

Results Comparison

knitr::opts_chunk$set(cache = T)

# Set the correct default repository
r = getOption("repos")
r["CRAN"] = "http://cran.rstudio.com"
options(repos = r)


# These will install required packages if they are not already installed
if (!require("ggplot2")) {
   install.packages("ggplot2", dependencies = TRUE)
   library(ggplot2)
}
if (!require("knitr")) {
   install.packages("knitr", dependencies = TRUE)
   library(knitr)
}
if (!require("xtable")) {
   install.packages("xtable", dependencies = TRUE)
   library(xtable)
}
if (!require("pander")) {
   install.packages("pander", dependencies = TRUE)
   library(pander)
}

if (!require("devtools")) {
  install.packages("devtools",dependencies = TRUE ) 
  library(devtools)
}

if (!require("usethis")) {
  install.packages("usethis" ) 
  library(usethis)
}

if (!require("e1071")) {
 install.packages("e1071" ) 
  library(e1071)
}

if (!require("pROC")){
  install.packages("pROC")
   library(pROC)
} 

if (!require("dplyr")) {
   install.packages("dplyr", dependencies = TRUE)
   library(dplyr)
}

if (!require("tidyverse")) {
   install.packages("tidyverse", dependencies = TRUE)
   library(tidyverse)
}

if (!require("caret")) {
   install.packages("caret", dependencies = TRUE)
   library(caret)
}
if (!require('randomForest')){
   install.packages('randomForest')
   library(randomForest)
}
if (!require('InformationValue')){
   install.packages('InformationValue')
   library(InformationValue)
}
if (!require('DALEX')){
   install.packages('DALEX')
   library(DALEX)
}
if (!require('mlbench')){
   install.packages('mlbench')
   library(mlbench)
}
if (!require('kernlab')){
   install.packages('kernlab')
   library(kernlab)
}
if (!require('e1071')){
   install.packages('e1071')
   library(e1071)
}
knitr::opts_chunk$set(echo = TRUE)

The best method for prediction overall was SVM, and the best feature selection methods were all features for random forest and recursive feature elimination for SVM. The strength of random forest approaches is to be able to quickly parse through many variables to draw conclusions. Because of this, it makes sense why the feature elimination across the board decreased random forest effectiveness, as there were less nodes to work from and therefore less learning that the forest could do. Therefore, unless you are splitting data in a smaller size, i.e. 50% of data is significant, I’d advise against using feature selections for random forest. Support Vectors work differently, as they use a point based method and therefore outlying points will have an impact on the accuracy of the model. Recursive Feature Elimination, the proven better of the two methods, showed a 5% increase in AUC score and a 2% increase in balanced accuracy over the standard all variables approach. Contrarily, the numbers dropped drastically with rpart variable importance. Overall however, I would recommend using randomforest with all features as my go to method. SVM produced very accurate results, but was very inconsistent. In the training and testing data, SVM proved much less effective returning roughly 50% and 70% AUC scores for testing and training data respectively, while randomforest remained relatively the same throughout with all features (+- 2%).

Additional Analysis

val.results <- matrix(c('rF-All',168,.81,.93,'rF-rfe',20,.62,.88,'rf-rpart',5,.61,.61,'SVM-All',168,.87,.93,'SVM-rfe',20,.92,.95,'SVM-rpart',5,.74,.85),ncol=4,byrow=TRUE)
colnames(val.results) <- c("Classification & Feature","Dimension","AUC Score",'Balanced Accuracy')
rownames(val.results) <- c('1','2','3','4','5','6')
val.results <- as.table(val.results)
val.results
  Classification & Feature Dimension AUC Score Balanced Accuracy
1 rF-All                   168       0.81      0.93             
2 rF-rfe                   20        0.62      0.88             
3 rf-rpart                 5         0.61      0.61             
4 SVM-All                  168       0.87      0.93             
5 SVM-rfe                  20        0.92      0.95             
6 SVM-rpart                5         0.74      0.85             

For my additional analysis, I provided a table with false positive and false negative rates for each method. Overall, randomforest had better true positive accuracy, and svm had better true negative acccuracy. Also, rf-rpart had extreme difficulty correctly classifying negative data, with only a 34% true positive rate. Therefore, if one variable is more significant than the other, i.e. you would rather have false positives than false negatives, then it is viable to select one method over the other.

Challenge Prediction

My challenge ID is hernae5 with an AUC score of .82 for prediction and balanced accuracy .50 for feature selection. I used random forest as my final selection method, as it is the most consistent method and returned the higest results in the challenge of all that I produced.

For my challenge entries, SVM consistently only returned 50% AUC score, while randomforest was 70-80%. All feature selection methods were fairly pedestrian, only returning about 50-53% for each score. Therefore, all randomforest methods were more effective than SVM, and of the choices randomforest with all features was clearly the best method posting a score of 82 compared to 77 for both with feaure selections.

Conclusion

In conclusion, SVM can be a very accurate tool for classification modeling, but I prefer randomforest due to its overall consistency. SVM worked best with recursive feature selection, and randomforest with all features selected. Overall, using rpart variable importance only served as a hindrance to any of the classification methods, making it a non-viable feature selection method for the task at hand.

Project Background

The European REACH regulation requires information on ready biodegradation, which is a screening test to assess the biodegradability of chemicals. At the same time REACH encourages the use of alternatives to animal testing.

Our contract is to build a classification model to predict ready biodegradation of chemicals and to predict the classification of 400 newly-developed molecules. This will help Chems-R-Us to design new materials with desired biodegradation properties more quickly and for lower cost.

Chems-R-Us Challenge Objective

The Chems-R-Us Challenge consists of two problems:

  • Binary classification: Each data row is labeled -1 or 1. We must train a predictive model on the train dataset to be able to find (as best we can) the labels of the test dataset.
  • Feature selection: Scattered among the 168 features there are fake features. These are randomly generated variables which don’t help predicting the class. The goal of this problem is to classify features between fake (0) and real (1).

Chems-R-Us Training & Testing Data

  • Experimental values of 1055 chemicals were collected.
  • The training dataset consists of these 1055 chemicals; whether they were readily biodegradable (1= yes , -1 = no); and 168 molecular descriptors.
  • Molecules and Molecular descriptors are proprietary. No details are provided except cryptic names in column headers
  • A testing set of 400 molecules with unknown biodegradability is provided

Chems-R-Us Challenge Files (Detail)

  • TRAINING DATA is divided into four files:

    • chems_train.data.csv: Training data matrix with no response labels (1018 samples x 168 feature values)
    • chems_feat.name.csv: Name of the 168 attributes (168 x 1 features names).
    • chems_train.solution.csv: Training target values (1018 lines x 1 column)
  • EXTERNAL TEST DATA is one file

    • chems_test.data.csv: Test data matrix (437 samples x 168 features values)

Reading the Data

# Prepare biodegradability data 
#get feature names 
featurenames <- read.csv("~/MATP-4400/data/chems_feat.name.csv",
                         header=FALSE, 
                         colClasses = "character")
featurenames
# get training data and rename with feature names
cdata.df <-read.csv("~/MATP-4400/data/chems_train.data.csv",
                    header=FALSE)
colnames(cdata.df) <- featurenames$V1
cdata.df
# get external testing data and rename with feature names
tdata.df <-read.csv("~/MATP-4400/data/chems_test.data.csv",
                    header=FALSE) 

colnames(tdata.df) <- featurenames$V1

class <- read.csv("~/MATP-4400/data/chems_train.solution.csv",
                  header=FALSE, 
                  colClasses = "factor") 
class <- class$V1

Preparing the Data: Create Training and Validation datasets

We split the data into 90% train and 10% validation datasets.

#ss will be the number of data points in the training set
n <- nrow(cdata.df)

ss <- ceiling(n*0.90)
# Set random seed for reproducibility
set.seed(200)
train.perm <- sample(1:n,ss)

#Split training and validation data
train <- cdata.df %>% slice(train.perm) 
validation <- cdata.df %>% slice(-train.perm) 

Next, we create a scaler to normalize the data and prevent outliers having significant impact on the results.

# Initialize the scaler on the training data
scaler <- preProcess(train, method = "scale") 
scaler <- preProcess(validation, method = "scale")
These variables have zero variances: X108, X131
scaler <- preProcess(tdata.df, method = "scale")


test <- predict(scaler, tdata.df)
# Normalize training data
# Split the output classes

classtrain <- class[train.perm]
classval <-class[-train.perm]

Fitting Data

First, we create dataframes combining the variables and the class results.

# Fit model to classify all the variables
train.df <- cbind(train,classtrain)
val.df <- cbind(validation, classval)

Then we write our methods for feature selection, both for the training and validation sets.

#Create training for rpart variable importance
set.seed(100)
rPartMod <- train(classtrain ~ ., data=train.df, method="rpart")
rpartImp <- varImp(rPartMod)

#Select most important variables, and select those from dataframe to use
variable.select <- rpartImp$importance %>%
   dplyr::select(Overall) > 0
variable.fs <- train %>%
   select_if(variable.select)
train.df.rpart <- cbind(variable.fs, classtrain)
set.seed(100)
rPartMod.val <- train(classval ~ ., data=val.df, method="rpart")
rpartImp.val <- varImp(rPartMod.val)
variable.select.val <- rpartImp.val$importance %>%
   dplyr::select(Overall) > 0
variable.fs.val <- validation %>%
   select_if(variable.select.val)
val.df.rpart <- cbind(variable.fs.val, classval)
set.seed(7)
library(mlbench)
library(caret)
# define the control using a random forest selection function
control <- rfeControl(functions=rfFuncs, method="cv", number=10)
# run the RFE algorithm
results <- rfe(train.df[1:168], train.df$classtrain, sizes=20, rfeControl=control)

sig.rfe <- predictors(results)
sig.rfe <- as.factor(sig.rfe)

train.df.rfe <- train.df %>% select(sig.rfe)
Note: Using an external vector in selections is ambiguous.
ℹ Use `all_of(sig.rfe)` instead of `sig.rfe` to silence this message.
ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.
train.df.rfe <- cbind(train.df.rfe, classtrain)
set.seed(7)
library(mlbench)
library(caret)
# define the control using a random forest selection function
control.val <- rfeControl(functions=rfFuncs, method="cv", number=10)
# run the RFE algorithm
results.val <- rfe(val.df[1:168], val.df$classval, sizes=20, rfeControl=control.val)

sig.rfe.val <- predictors(results.val)
sig.rfe.val <- as.factor(sig.rfe.val)

val.df.rfe <- val.df %>% select(sig.rfe.val)
Note: Using an external vector in selections is ambiguous.
ℹ Use `all_of(sig.rfe.val)` instead of `sig.rfe.val` to silence this message.
ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.
val.df.rfe <- cbind(val.df.rfe, classval)

Then, we plug in our training and validation dataframes to the training algorithms for both classifications and all feature combinations. We return values for balanced accuracy, and a confusion matrix of data.


rf.rfe <- randomForest(
   classtrain~.,
   data = train.df.rfe,
)

confmat.rf.rfe <- rf.rfe$confusion
confmat.rf.rfe <- subset(confmat.rf.rfe, select = -c(class.error) )
# True Positive Rate or Sensitivity
Sensitivity.rf.rfe <- sensitivity_from_confmat(confmat.rf.rfe)# True Negative Rate or Specificity
Specificity.rf.rfe <- specificity_from_confmat(confmat.rf.rfe)
BalancedAccuracy.rf.rfe <- (Sensitivity.rf.rfe+Specificity.rf.rfe)/2

rf.rfe.val <- randomForest(
   classval~.,
   data = val.df.rfe,
)

confmat.rf.rfe.val <- rf.rfe.val$confusion
confmat.rf.rfe.val <- subset(confmat.rf.rfe.val, select = -c(class.error) )
# True Positive Rate or Sensitivity
Sensitivity.rf.rfe.val <- sensitivity_from_confmat(confmat.rf.rfe.val)# True Negative Rate or Specificity
Specificity.rf.rfe.val <- specificity_from_confmat(confmat.rf.rfe.val)
BalancedAccuracy.rf.rfe.val <- (Sensitivity.rf.rfe.val+Specificity.rf.rfe.val)/2
BalancedAccuracy.rf.rfe.val
[1] 0.886
#random forest using rpart variable imporance
rf.rpart <- randomForest(
   classtrain~.,
   data = train.df.rpart,
)

confmat.rf.rpart <- rf.rpart$confusion
confmat.rf.rpart <- subset(confmat.rf.rpart, select = -c(class.error) )
# True Positive Rate or Sensitivity
Sensitivity.rf.rpart <- sensitivity_from_confmat(confmat.rf.rpart)# True Negative Rate or Specificity
Specificity.rf.rpart <- specificity_from_confmat(confmat.rf.rpart)
BalancedAccuracy.rf.rpart <- (Sensitivity.rf.rpart+Specificity.rf.rpart)/2
#random forest using rpart variable imporance
rf.rpart.val <- randomForest(
   classval~.,
   data = val.df.rpart,
)

confmat.rf.rpart.val <- rf.rpart.val$confusion
confmat.rf.rpart.val <- subset(confmat.rf.rpart.val, select = -c(class.error) )
# True Positive Rate or Sensitivity
Sensitivity.rf.rpart.val <- sensitivity_from_confmat(confmat.rf.rpart.val)# True Negative Rate or Specificity
Specificity.rf.rpart.val <- specificity_from_confmat(confmat.rf.rpart.val)
BalancedAccuracy.rf.rpart.val <- (Sensitivity.rf.rpart.val+Specificity.rf.rpart.val)/2
control <- trainControl(method="cv", number=10)
metric <- "Accuracy"
set.seed(7)
fit.svm <- train(classtrain~., data=train.df, method="svmRadial", metric=metric, trControl=control)
pred.svm <- predict(fit.svm, train.df)


confmat.svm <- table(pred.svm, train.df$classtrain, dnn=c("Prediction", "Actual"))   
# True Positive Rate or Sensitivity
Sensitivity.svm <- sensitivity_from_confmat(confmat.svm)# True Negative Rate or Specificity
Specificity.svm <- specificity_from_confmat(confmat.svm)
BalancedAccuracy.svm <- (Sensitivity.svm+Specificity.svm)/2
control <- trainControl(method="cv", number=10)
metric <- "Accuracy"
set.seed(7)
fit.svm.val <- train(classval~., data=val.df, method="svmRadial", metric=metric, trControl=control)
Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.Variable(s) `' constant. Cannot scale data.
pred.svm.val <- predict(fit.svm, val.df)


confmat.svm.val <- table(pred.svm.val, val.df$classval, dnn=c("Prediction", "Actual"))   
# True Positive Rate or Sensitivity
Sensitivity.svm.val <- sensitivity_from_confmat(confmat.svm.val)# True Negative Rate or Specificity
Specificity.svm.val <- specificity_from_confmat(confmat.svm.val)
BalancedAccuracy.svm.val <- (Sensitivity.svm.val+Specificity.svm.val)/2
control <- trainControl(method="cv", number=10)
metric <- "Accuracy"
set.seed(7)
fit.svm.rfe <- train(classtrain~., data=train.df.rfe, method="svmRadial", metric=metric, trControl=control)
pred.svm.rfe <- predict(fit.svm.rfe, train.df.rfe)


confmat.svm.rfe <- table(pred.svm.rfe, train.df$classtrain, dnn=c("Prediction", "Actual"))  
Sensitivity.svm.rfe <- sensitivity_from_confmat(confmat.svm.rfe)# True Negative Rate or Specificity
Specificity.svm.rfe <- specificity_from_confmat(confmat.svm.rfe)
BalancedAccuracy.svm.rfe <- (Sensitivity.svm.rfe+Specificity.svm.rfe)/2
control <- trainControl(method="cv", number=10)
metric <- "Accuracy"
set.seed(7)
fit.svm.rfe.val <- train(classval~., data=val.df.rfe, method="svmRadial", metric=metric, trControl=control)
pred.svm.rfe.val <- predict(fit.svm.rfe.val, val.df.rfe)


confmat.svm.rfe.val <- table(pred.svm.rfe.val, val.df$classval, dnn=c("Prediction", "Actual"))  
Sensitivity.svm.rfe.val <- sensitivity_from_confmat(confmat.svm.rfe.val)# True Negative Rate or Specificity
Specificity.svm.rfe.val <- specificity_from_confmat(confmat.svm.rfe.val)
BalancedAccuracy.svm.rfe.val <- (Sensitivity.svm.rfe.val+Specificity.svm.rfe.val)/2
control <- trainControl(method="cv", number=10)
metric <- "Accuracy"
set.seed(7)
fit.svm.rpart <- train(classtrain~., data=train.df.rpart, method="svmRadial", metric=metric, trControl=control)
pred.svm.rpart <- predict(fit.svm.rpart, train.df.rpart)

confmat.svm.rpart <- table(pred.svm.rpart, train.df$classtrain, dnn=c("Prediction", "Actual"))
Sensitivity.svm.rpart <- sensitivity_from_confmat(confmat.svm.rpart)# True Negative Rate or Specificity
Specificity.svm.rpart <- specificity_from_confmat(confmat.svm.rpart)
BalancedAccuracy.svm.rpart <- (Sensitivity.svm.rpart+Specificity.svm.rpart)/2
control <- trainControl(method="cv", number=10)
metric <- "Accuracy"
set.seed(7)
fit.svm.rpart.val <- train(classval~., data=val.df.rpart, method="svmRadial", metric=metric, trControl=control)
pred.svm.rpart.val <- predict(fit.svm.rpart.val, val.df.rpart)

confmat.svm.rpart.val <- table(pred.svm.rpart.val, val.df$classval, dnn=c("Prediction", "Actual"))
Sensitivity.svm.rpart.val <- sensitivity_from_confmat(confmat.svm.rpart.val)# True Negative Rate or Specificity
Specificity.svm.rpart.val <- specificity_from_confmat(confmat.svm.rpart.val)
BalancedAccuracy.svm.rpart.val <- (Sensitivity.svm.rpart.val+Specificity.svm.rpart.val)/2

ROC Curves

roc.data <- data.frame("Class" = classval, 
                       "No Selection" = pred.svm.val,
                       "With Selection" = pred.svm.rfe.val)
roc.data$Class <- as.numeric(roc.data$Class)
roc.data$No.Selection <- as.numeric(roc.data$No.Selection)
roc.data$With.Selection <- as.numeric(roc.data$With.Selection)
roc.list <- roc(Class ~., 
                data = roc.data)
no.selection.auc <- round(auc(Class ~ No.Selection, data = roc.data), digits = 3)
with.selection.auc <- round(auc(Class ~ With.Selection, data = roc.data), digits = 3)
roc_plot <- ggroc(roc.list) + 
   ggtitle("ROC Curves (Validation Set)", subtitle = "SVM without Feature Selection versus SVM with RFE") + 
   scale_color_discrete(name = "Model", 
                        labels = c(paste("No Selection\nAUC :", no.selection.auc), paste("With Selection\nAUC :", with.selection.auc)))
roc_plot

roc.data <- data.frame("Class" = classval, 
                       "No Selection" = pred.svm.val,
                       "With Selection" = pred.svm.rpart.val)
roc.data$Class <- as.numeric(roc.data$Class)
roc.data$No.Selection <- as.numeric(roc.data$No.Selection)
roc.data$With.Selection <- as.numeric(roc.data$With.Selection)
roc.list <- roc(Class ~., 
                data = roc.data)
no.selection.auc <- round(auc(Class ~ No.Selection, data = roc.data), digits = 3)
with.selection.auc <- round(auc(Class ~ With.Selection, data = roc.data), digits = 3)
roc_plot <- ggroc(roc.list) + 
   ggtitle("ROC Curves (Validation Set)", subtitle = "SVM without Feature Selection versus SVM with RFE") + 
   scale_color_discrete(name = "Model", 
                        labels = c(paste("No Selection\nAUC :", no.selection.auc), paste("With Selection\nAUC :", with.selection.auc)))
roc_plot

pred.rf <- predict(rf.val, validation, type = 'response')
pred.rf.rfe <- predict(rf.rfe.val, validation, type = 'response')
pred.rf.rpart <- predict(rf.rpart.val, validation, type = 'response')
roc.data <- data.frame("Class" = classval, 
                       "No Selection" = pred.rf,
                       "With Selection" = pred.rf.rfe)
roc.data$Class <- as.numeric(roc.data$Class)
roc.data$No.Selection <- as.numeric(roc.data$No.Selection)
roc.data$With.Selection <- as.numeric(roc.data$With.Selection)
roc.list <- roc(Class ~., 
                data = roc.data)
no.selection.auc <- round(auc(Class ~ No.Selection, data = roc.data), digits = 3)
with.selection.auc <- round(auc(Class ~ With.Selection, data = roc.data), digits = 3)
roc_plot <- ggroc(roc.list) + 
   ggtitle("ROC Curves (Validation Set)", subtitle = "SVM without Feature Selection versus SVM with RFE") + 
   scale_color_discrete(name = "Model", 
                        labels = c(paste("No Selection\nAUC :", no.selection.auc), paste("With Selection\nAUC :", with.selection.auc)))
roc_plot

pred.rf <- predict(rf.val, validation, type = 'response')
pred.rf.rfe <- predict(rf.rfe.val, validation, type = 'response')
pred.rf.rpart <- predict(rf.rpart.val, validation, type = 'response')
roc.data <- data.frame("Class" = classval, 
                       "No Selection" = pred.rf,
                       "With Selection" = pred.rf.rpart)
roc.data$Class <- as.numeric(roc.data$Class)
roc.data$No.Selection <- as.numeric(roc.data$No.Selection)
roc.data$With.Selection <- as.numeric(roc.data$With.Selection)
roc.list <- roc(Class ~., 
                data = roc.data)
no.selection.auc <- round(auc(Class ~ No.Selection, data = roc.data), digits = 3)
with.selection.auc <- round(auc(Class ~ With.Selection, data = roc.data), digits = 3)
roc_plot <- ggroc(roc.list) + 
   ggtitle("ROC Curves (Validation Set)", subtitle = "SVM without Feature Selection versus SVM with RFE") + 
   scale_color_discrete(name = "Model", 
                        labels = c(paste("No Selection\nAUC :", no.selection.auc), paste("With Selection\nAUC :", with.selection.auc)))
roc_plot

Interpreting the ROC Results

The validation results show that randomForest with feature selection produces slightly worse generalizations (accuracy on the validation set) results than our original model using all the variables.

Alternatively, we see that when using SVM with RFE selecion, that the feature selected model will result in better generalizations on the data.

Competition Entry: Saving & Uploading Your Predictions

The following code creates a valid entry for the contest:

  • Predict the test data and put the ranking in ranking_lrtest
    • The ranking can be any number leading to a classification like log odds.
    • This means values greater than 0 mean class 1 and values less than 0 mean class -1.
    • The results will be ranked by AUC.
  • Then write the results the CSV file named classification.csv (You must use this filename)
  • NOTE: You may need to execute this code chunk (and the chunks above it) individually for write.table() to work.
ss.analysis <- matrix(c('rF-All',.03,.12,'rF-rfe',.05,.19,'rf-rpart',.12,.66,'SVM-All',.10,.04,'SVM-rfe',.06,.03,'SVM-rpart',.18,.11),ncol=3,byrow=TRUE)
colnames(ss.analysis) <- c("Classification & Feature", 'False Positive Rate', 'False Negative Rate')
rownames(ss.analysis) <- c('1','2','3','4','5','6')
ss.analysis <- as.table(ss.analysis)
ss.analysis
  Classification & Feature False Positive Rate False Negative Rate
1 rF-All                   0.03                0.12               
2 rF-rfe                   0.05                0.19               
3 rf-rpart                 0.12                0.66               
4 SVM-All                  0.1                 0.04               
5 SVM-rfe                  0.06                0.03               
6 SVM-rpart                0.18                0.11               

Storing Feature Selection Results

  • Store your prediction for the features.
    • This should be binary, where 1 means keep the feature and 0 means don’t keep feature.
    • The results will be ranked by balanced accuracy.
  • Then write the results into the CSV file named selection.csv (You must use this filename)
  • NOTE: You may need to execute this code chunk (and the chunks above it) individually for write.table() to work.
# Predict the test data (OUTPUTS LOG-ODDS)
ranking_rf <- predict(fit.svm, test)
ranking_rf <- as.numeric(ranking_rf)

# no need to convert to 0 and 1 since ranking needed for AUC.
write.table(ranking_rf,file = "classification.csv", row.names=F, col.names=F)

Zipping and Submitting Your Results to the Challenge

  • Zip your classification.csv and selection.csv files – we must use these exact names! – into a single archive to generate the file MyEntry.csv.zip to enter the contest.
  • The name of your zip file is not important, but should not include spaces or characters like ( etc. The following code creates a zip filename that will always be unique.
  • NOTE:
    • You may need to execute this code chunk (and the chunks above it) individually for system() to work.
    • This code creates a zip with a filename based on time that will always be unique. This will result in many zips accumulating in your working directory!
# Here is the mean prediction file for submission to the website 
# features should be a column vector of 0's and 1's. 
# 1 = keep feature, 0 = don't
features<-matrix(0,nrow=(ncol(train)),ncol=1)
# Set the ones we want to keep to 1
features[variable.select] <- 1
write.table(features,file = "selection.csv", row.names=F, col.names=F)
LS0tCnRpdGxlOiAnTUFUUC00NDAwIEZpbmFsIFByb2plY3QgTm90ZWJvb2sgKDIwMjEpJwpzdWJ0aXRsZTogJ1ByZWRpY3RpbmcgQmlvZGVncmFkYWJpbGl0eSBDaGFsbGVuZ2UnCmF1dGhvcjogIkV2YW4gSGVybmFuZGV6IgpkYXRlOiAiTWF5IDIwMjEiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IHVuaXRlZAogICAgdG9jOiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBoZWFkZXItaW5jbHVkZXM6IFx1c2VwYWNrYWdle2NvbG9yfQogICAgdG9jOiB5ZXMKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwotLS0KCmBgYHtyLCBpbmNsdWRlPUZBTFNFLCBzZXQuc2VlZCgyMCl9CmtuaXRyOjpvcHRzX2NodW5rJHNldChjYWNoZSA9IFQpCgojIFNldCB0aGUgY29ycmVjdCBkZWZhdWx0IHJlcG9zaXRvcnkKciA9IGdldE9wdGlvbigicmVwb3MiKQpyWyJDUkFOIl0gPSAiaHR0cDovL2NyYW4ucnN0dWRpby5jb20iCm9wdGlvbnMocmVwb3MgPSByKQoKCiMgVGhlc2Ugd2lsbCBpbnN0YWxsIHJlcXVpcmVkIHBhY2thZ2VzIGlmIHRoZXkgYXJlIG5vdCBhbHJlYWR5IGluc3RhbGxlZAppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICAgbGlicmFyeShnZ3Bsb3QyKQp9CmlmICghcmVxdWlyZSgia25pdHIiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiAgIGxpYnJhcnkoa25pdHIpCn0KaWYgKCFyZXF1aXJlKCJ4dGFibGUiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJ4dGFibGUiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQogICBsaWJyYXJ5KHh0YWJsZSkKfQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiAgIGxpYnJhcnkocGFuZGVyKQp9CgppZiAoIXJlcXVpcmUoImRldnRvb2xzIikpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIsZGVwZW5kZW5jaWVzID0gVFJVRSApIAogIGxpYnJhcnkoZGV2dG9vbHMpCn0KCmlmICghcmVxdWlyZSgidXNldGhpcyIpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygidXNldGhpcyIgKSAKICBsaWJyYXJ5KHVzZXRoaXMpCn0KCmlmICghcmVxdWlyZSgiZTEwNzEiKSkgewogaW5zdGFsbC5wYWNrYWdlcygiZTEwNzEiICkgCiAgbGlicmFyeShlMTA3MSkKfQoKaWYgKCFyZXF1aXJlKCJwUk9DIikpewogIGluc3RhbGwucGFja2FnZXMoInBST0MiKQogICBsaWJyYXJ5KHBST0MpCn0gCgppZiAoIXJlcXVpcmUoImRwbHlyIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQogICBsaWJyYXJ5KGRwbHlyKQp9CgppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiAgIGxpYnJhcnkodGlkeXZlcnNlKQp9CgppZiAoIXJlcXVpcmUoImNhcmV0IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQogICBsaWJyYXJ5KGNhcmV0KQp9CmlmICghcmVxdWlyZSgncmFuZG9tRm9yZXN0JykpewogICBpbnN0YWxsLnBhY2thZ2VzKCdyYW5kb21Gb3Jlc3QnKQogICBsaWJyYXJ5KHJhbmRvbUZvcmVzdCkKfQppZiAoIXJlcXVpcmUoJ0luZm9ybWF0aW9uVmFsdWUnKSl7CiAgIGluc3RhbGwucGFja2FnZXMoJ0luZm9ybWF0aW9uVmFsdWUnKQogICBsaWJyYXJ5KEluZm9ybWF0aW9uVmFsdWUpCn0KaWYgKCFyZXF1aXJlKCdEQUxFWCcpKXsKICAgaW5zdGFsbC5wYWNrYWdlcygnREFMRVgnKQogICBsaWJyYXJ5KERBTEVYKQp9CmlmICghcmVxdWlyZSgnbWxiZW5jaCcpKXsKICAgaW5zdGFsbC5wYWNrYWdlcygnbWxiZW5jaCcpCiAgIGxpYnJhcnkobWxiZW5jaCkKfQppZiAoIXJlcXVpcmUoJ2tlcm5sYWInKSl7CiAgIGluc3RhbGwucGFja2FnZXMoJ2tlcm5sYWInKQogICBsaWJyYXJ5KGtlcm5sYWIpCn0KaWYgKCFyZXF1aXJlKCdlMTA3MScpKXsKICAgaW5zdGFsbC5wYWNrYWdlcygnZTEwNzEnKQogICBsaWJyYXJ5KGUxMDcxKQp9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgoKIyBUZWFtIEluZm9ybWF0aW9uCgpUaGlzIHJlcG9ydCB3YXMgcHJlcGFyZWQgZm9yIENoZW1zUlVzIGJ5ICBFdmFuIEhlcm5hbmRleiwgaGVybmFlNQpmb3IgdGhlIHRlYW0gIldlIERvbid0IFppbmMgU28iCk15IHRlYW0gbWVtYmVycyBhcmU6IEplc3NpY2EgQ29jbywgVGFtaWEgQnJlZm8sIE1vcmdhbiBGb3JkLCBhbmQgSmVmZnJleSBDaGFpICBjb2NvajIsIGJyZWZvdCwgZm9yZG0yLCBjaGFpaiAKCk91ciB0ZWFtIHVzZWQgaHR0cDovL2NvZGFsYWIuaWRlYS5ycGkuZWR1L2NvbXBldGl0aW9ucy8zNwogICAKCiMgSW50cm9kdWN0aW9uCgpDaGVtcy1SLVVzIGhhcyBjcmVhdGVkIGFuIGVudHJ5IHRvIHRoZSBjaGFsbGVuZ2UgYXQgaHR0cHM6Ly9jb21wZXRpdGlvbnMuY29kYWxhYi5vcmcvY29tcGV0aXRpb25zLzIyODkyIGJhc2VkIG9uIGxvZ2lzdGljIHJlZ3Jlc3Npb24gKExSKS4gIFRoZWlyIGVudHJ5IGlzIGluIHRoZSBmaWxlICdGaW5hbFByb2pDaGVtc1JVcy5SbWQnIEJhc2VkIG9uIHRoZSBpbmZvcm1hdGlvbiBpbiB0aGUgbGVhZGVyYm9hcmQgdW5kZXIgYmVubmVrLCB0aGVpciBlbnRyeSBpcyBub3QgcGVyZm9ybWluZyBmZWF0dXJlIHNlbGVjdGlvbiB3ZWxsLiBUaGUgYXBwcm9hY2hlcyB0cmllZCBieSBDaGVtcy1SLVVzIHdlcmUgTFIgd2l0aCBmZWF0dXJlIHNlbGVjdGlvbiBiYXNlZCBvbiB0aGUgY29lZmZpY2llbnRzIG9mIGxvZ2lzdGljIHJlZ3Jlc3Npb24gd2l0aCBwLXZhbHVlcyB1c2VkIHRvIGRldGVybWluZSBpbXBvcnRhbmNlLiAgIAoKVGhlIHB1cnBvc2Ugb2YgdGhpcyByZXBvcnQgaXMgdG8gaW52ZXN0aWdhdGUgYWx0ZXJuYXRpdmUgYXBwcm9hY2hlcyB0byB0aGUgdGhhdCBtYXkgaGVscCBhY2hpZXZlIGhpZ2ggQVVDIHNjb3JlcyBvbiB0aGUgdGVzdGluZyBzZXQgd2hpbGUgY29ycmVjdGx5IGlkZW50aWZ5aW5nIHRoZSByZWxldmFudCBmZWF0dXJlcyBhcyBtZWFzdXJlZCBieSBiYWxhbmNlZCBhY2N1cmFjeS4gCgoKIyBNZXRob2RzIFVzZWQKClRoZSBmaXJzdCBtZXRob2QgdGhhdCBJIHVzZWQgZm9yIGNsYXNzaWZpY2F0aW9uIHdhcyByYW5kb21Gb3Jlc3QuIFJhbmRvbUZvcmVzdCB3b3JrcyBieSBjcmVhdGluZyBhIHRyZWUgb2Ygbm9kZXMgZm9yIHNlbGVjdGlvbiBhbmQgdHJ5aW5nIGRpZmZlcmVudCB2YXJpYWJsZXMgYXQgZWFjaCBsZXZlbCB0byBmaW5kIHRoZSBjb3JyZWxhdGlvbiB0byB0aGUgZGF0YS4gVGhlIHNlY29uZCBtZXRob2QgSSB1c2VkIHdhcyBTVk0gKFN1cHBvcnQgVmVjdG9yIE1hY2hpbmVzKSwgd2hpY2ggZ2VuZXJhdGVzIHZlY3RvcnMgYWJvdXQgdGhlIHBvaW50cyBpbiB0aGUgZGF0YXNldCB0byBzZXBhcmF0ZSB0aGVtIGludG8gdHdvIGNsYXNzZXMuIFRoZSBmaXJzdCBmZWF0dXJlIHNlbGVjdGlvbiBtZXRob2QgdGhhdCBJIHVzZWQgd2FzIFJlY3Vyc2l2ZSBGZWF0dXJlIEVsaW1pbmF0aW9uIGFuZCB0aGUgc2Vjb25kIGZlYXR1cmUgc2VsZWN0aW9uIEkgdXNlZCB3YXMgdmFyaWFibGUgaW1wb3J0YW5jZSB1c2luZyB0aGUgY2FyZXQgbWV0aG9kICdycGFydCcuCgoKIyBEYXRhIERlc2NyaXB0aW9uOgoKMS4gVGhlIGRhdGFzZXQgaGFkIDE2OCBhdHRyaWJ1dGVzCjJhLiBUaGUgdGVzdCBkYXRhIHdhcyBkaXZpZGVkIGluZGVwZW5kZW50bHkKMmIuIFRoZSB0cmFpbmluZyBhbmQgdmFsaWRhdGlvbiBkYXRhIHdlcmUgbWFkZSBmcm9tIGEgOTA6MTAgc3BsaXQgb2YgdGhlIGZlYXR1cmUsIGNsYXNzLCBhbmQgcmVzdWx0IGRhdGEgcHJvdmlkZWQKMy4gVGhlIHRlc3Rpbmcgc2V0IGhhZCA0MDAgZGF0YSBwb2ludHMgcGVyIGZlYXR1cmUsIHRoZSB2YWxpZGF0aW9uIHNldCBoYWQgMTA1IHBvaW50cyBwZXIgZmVhdHVyZSwgYW5kIHRoZSB0cmFpbmluZyBzZXQgaGFkIDkwNSBkYXRhIHBvaW50cyBwZXIgZmVhdHVyZQo0LiBUaGUgc2NhbGluZyBtZXRob2QgdXNlZCB3YXMgdGhlIHByZVByb2Nlc3MgZnVuY3Rpb24gd2l0aCB0aGUgc2NhbGUgbWV0aG9kCgoKIyBSZXN1bHRzIFVzaW5nICBBbGwgRmVhdHVyZXMKIApUaGUgYWNjdXJhY3kgZm9yIHRoZSB0cmFpbmluZyBtb2RlbHMgd2VyZSBhY3Jvc3MgdGhlIGJvYXJkIGJldHRlciB0aGFuIHRoZSB2YWxpZGF0aW9uIG1vZGVscy4gVGhlIHJlYXNvbiBmb3IgdGhpcyBpcyB0aGF0IHRoZSB0cmFpbmluZyBkYXRhIGhhZCBuaW5lIHRpbWVzIHRoZSBpbmZvcm1hdGlvbiB0aGF0IHRoZSB2YWxpZGF0aW9uIGRhdGEsIGdpdmluZyB0aGUgQUkgbW9yZSB0aW1lIHRvIGxlYXJuIGFuZCBiZXR0ZXIgY2xhc3NpZnkgdGhlIHJlc3VsdHMgYmFzZWQgb24gdGhlIGluZm9ybWF0aW9uIGdpdmVuLiBUaGUgZGlmZmVyZW5jZSB3YXMgbm90IHN0YXJrLCBhbmQgd2FzIG9ubHkgYWJvdXQgNC04JSBhdCBtb3N0LiBMb2dpc3RpY2FsbHksIHRoaXMgbWVhbnMgdGhhdCB0aGUgcmV0dXJuIHdhcyBtb3N0IGxpa2VseSBsb2dhcml0aG1pYyBmb3IgZGF0YSBhbmQgYWNjdXJhY3kgYW5kIHRoZXJlZm9yZSB0aGVyZSBpcyBvcHRpbWl6YXRpb24gcG90ZW50aWFsIGZvciBydW50aW1lLCBkYXRhIHVzZWQsIGFjY3VyYWN5LCBldGMuIGZvciBib3RoIG1vZGVscy4KCgojIFJlc3VsdHMgVXNpbmcgRmVhdHVyZSBTZWxlY3Rpb24KClRoZSB0d28gZmVhdHVyZSBzZWxlY3Rpb24gbWV0aG9kcyB0aGF0IEkgdXNlZCB3ZXJlIFJlY3Vyc2l2ZSBGZWF0dXJlIEVsaW1pbmF0aW9uIGFuZCBycGFydCBWYXJpYWJsZSBJbXBvcnRhbmNlLiBSZWN1cnNpdmUgZmVhdHVyZSBzZWxlY3Rpb24gdXNlZCBhIG11bHRpIHN0ZXAgcHJvY2VzcyB3aXRoIGstZm9sZHMgaW4gdGhlIGRhdGEgdG8gcmVwZWF0IGFuZCBldmFsdWF0ZSB3aGF0IHRoZSBtb3N0IGRldGVybWluaW5nIHZhcmlhYmxlcyB3ZXJlLiBBIHNpbWlsYXIgcHJvY2VzcyBpcyB1c2VkIGZvciBycGFydCBWYXJpYWJsZSBJbXBvcnRhbmNlLCBhbmQgdGhhdCBzZWxlY3RlZCB0aGUgdG9wIDUgZmVhdHVyZXMgb25seSB0byBiZSB1c2VkLiBSZWN1cnNpZSBmZWF0dXJlIGVsaW1pbmF0aW9uIGZvciBib3RoIGNsYXNzaWZpY2F0aW9uIG1ldGhvZHMgcGVyZm9ybWVkIGJldHRlciB0aGFuIGFsbCBmZWF0dXJlcyBmb3IgdGhlIHRyYWluaW5nIGFuZCB2YWxpZGF0aW9uIHNldHMsIGJ1dCBkaWQgbm90IHBlcmZvcm0gYXMgd2VsbCBvbiB0aGUgY2hhbGxlbmdlIHNpdGUgb3IgdGVzdCBkYXRhLiBScGFydCBWYXJpYWJsZSBJbXBvcnRhbmNlIHBlcmZvcm1lZCB0aGUgd29yc3QgaW4gdGhlIHRyYWluaW5nLCB2YWxpZGF0aW9uLCBhbmQgdGVzdCBzZXRzIGZvciBib3RoIGNsYXNzaWZpZXJzLCBsZWFkaW5nIG1lIHRvIGJlbGlldmUgdGhhdCBycGFydCBWYXJpYWJsZSBJbXBvcnRhbmNlIGlzIG5vdCB2ZXJ5IHZpYWJsZSBmb3IgZGF0YSB0aGF0IG5lZWRzIGV4dHJlbWUgYWNjdXJhY3ksIGhvd2V2ZXIgdGhlIHJwYXJ0IHJhbiBzaWduaWZpY2FudGx5IGZhc3RlciB0aGFuIHRoZSByZWN1cnNpdmUgZmVhdHVyZSBlbGltaW5hdGlvbi4gCgoKIyBSZXN1bHRzIENvbXBhcmlzb24KCmBgYHtyfQp2YWwucmVzdWx0cyA8LSBtYXRyaXgoYygnckYtQWxsJywxNjgsLjgxLC45MywnckYtcmZlJywyMCwuNjIsLjg4LCdyZi1ycGFydCcsNSwuNjEsLjYxLCdTVk0tQWxsJywxNjgsLjg3LC45MywnU1ZNLXJmZScsMjAsLjkyLC45NSwnU1ZNLXJwYXJ0Jyw1LC43NCwuODUpLG5jb2w9NCxieXJvdz1UUlVFKQpjb2xuYW1lcyh2YWwucmVzdWx0cykgPC0gYygiQ2xhc3NpZmljYXRpb24gJiBGZWF0dXJlIiwiRGltZW5zaW9uIiwiQVVDIFNjb3JlIiwnQmFsYW5jZWQgQWNjdXJhY3knKQpyb3duYW1lcyh2YWwucmVzdWx0cykgPC0gYygnMScsJzInLCczJywnNCcsJzUnLCc2JykKdmFsLnJlc3VsdHMgPC0gYXMudGFibGUodmFsLnJlc3VsdHMpCnZhbC5yZXN1bHRzCmBgYAoKVGhlIGJlc3QgbWV0aG9kIGZvciBwcmVkaWN0aW9uIG92ZXJhbGwgd2FzIFNWTSwgYW5kIHRoZSBiZXN0IGZlYXR1cmUgc2VsZWN0aW9uIG1ldGhvZHMgd2VyZSBhbGwgZmVhdHVyZXMgZm9yIHJhbmRvbSBmb3Jlc3QgYW5kIHJlY3Vyc2l2ZSBmZWF0dXJlIGVsaW1pbmF0aW9uIGZvciBTVk0uIFRoZSBzdHJlbmd0aCBvZiByYW5kb20gZm9yZXN0IGFwcHJvYWNoZXMgaXMgdG8gYmUgYWJsZSB0byBxdWlja2x5IHBhcnNlIHRocm91Z2ggbWFueSB2YXJpYWJsZXMgdG8gZHJhdyBjb25jbHVzaW9ucy4gQmVjYXVzZSBvZiB0aGlzLCBpdCBtYWtlcyBzZW5zZSB3aHkgdGhlIGZlYXR1cmUgZWxpbWluYXRpb24gYWNyb3NzIHRoZSBib2FyZCBkZWNyZWFzZWQgcmFuZG9tIGZvcmVzdCBlZmZlY3RpdmVuZXNzLCBhcyB0aGVyZSB3ZXJlIGxlc3Mgbm9kZXMgdG8gd29yayBmcm9tIGFuZCB0aGVyZWZvcmUgbGVzcyBsZWFybmluZyB0aGF0IHRoZSBmb3Jlc3QgY291bGQgZG8uIFRoZXJlZm9yZSwgdW5sZXNzIHlvdSBhcmUgc3BsaXR0aW5nIGRhdGEgaW4gYSBzbWFsbGVyIHNpemUsIGkuZS4gNTAlIG9mIGRhdGEgaXMgc2lnbmlmaWNhbnQsIEknZCBhZHZpc2UgYWdhaW5zdCB1c2luZyBmZWF0dXJlIHNlbGVjdGlvbnMgZm9yIHJhbmRvbSBmb3Jlc3QuIFN1cHBvcnQgVmVjdG9ycyB3b3JrIGRpZmZlcmVudGx5LCBhcyB0aGV5IHVzZSBhIHBvaW50IGJhc2VkIG1ldGhvZCBhbmQgdGhlcmVmb3JlIG91dGx5aW5nIHBvaW50cyB3aWxsIGhhdmUgYW4gaW1wYWN0IG9uIHRoZSBhY2N1cmFjeSBvZiB0aGUgbW9kZWwuIFJlY3Vyc2l2ZSBGZWF0dXJlIEVsaW1pbmF0aW9uLCB0aGUgcHJvdmVuIGJldHRlciBvZiB0aGUgdHdvIG1ldGhvZHMsIHNob3dlZCBhIDUlIGluY3JlYXNlIGluIEFVQyBzY29yZSBhbmQgYSAyJSBpbmNyZWFzZSBpbiBiYWxhbmNlZCBhY2N1cmFjeSBvdmVyIHRoZSBzdGFuZGFyZCBhbGwgdmFyaWFibGVzIGFwcHJvYWNoLiBDb250cmFyaWx5LCB0aGUgbnVtYmVycyBkcm9wcGVkIGRyYXN0aWNhbGx5IHdpdGggcnBhcnQgdmFyaWFibGUgaW1wb3J0YW5jZS4gT3ZlcmFsbCBob3dldmVyLCBJIHdvdWxkIHJlY29tbWVuZCB1c2luZyByYW5kb21mb3Jlc3Qgd2l0aCBhbGwgZmVhdHVyZXMgYXMgbXkgZ28gdG8gbWV0aG9kLiBTVk0gcHJvZHVjZWQgdmVyeSBhY2N1cmF0ZSByZXN1bHRzLCBidXQgd2FzIHZlcnkgaW5jb25zaXN0ZW50LiBJbiB0aGUgdHJhaW5pbmcgYW5kIHRlc3RpbmcgZGF0YSwgU1ZNIHByb3ZlZCBtdWNoIGxlc3MgZWZmZWN0aXZlIHJldHVybmluZyByb3VnaGx5IDUwJSBhbmQgNzAlIEFVQyBzY29yZXMgZm9yIHRlc3RpbmcgYW5kIHRyYWluaW5nIGRhdGEgcmVzcGVjdGl2ZWx5LCB3aGlsZSByYW5kb21mb3Jlc3QgcmVtYWluZWQgcmVsYXRpdmVseSB0aGUgc2FtZSB0aHJvdWdob3V0IHdpdGggYWxsIGZlYXR1cmVzICgrLSAyJSkuCgoKIyBBZGRpdGlvbmFsIEFuYWx5c2lzIApgYGB7cn0Kc3MuYW5hbHlzaXMgPC0gbWF0cml4KGMoJ3JGLUFsbCcsLjAzLC4xMiwnckYtcmZlJywuMDUsLjE5LCdyZi1ycGFydCcsLjEyLC42NiwnU1ZNLUFsbCcsLjEwLC4wNCwnU1ZNLXJmZScsLjA2LC4wMywnU1ZNLXJwYXJ0JywuMTgsLjExKSxuY29sPTMsYnlyb3c9VFJVRSkKY29sbmFtZXMoc3MuYW5hbHlzaXMpIDwtIGMoIkNsYXNzaWZpY2F0aW9uICYgRmVhdHVyZSIsICdGYWxzZSBQb3NpdGl2ZSBSYXRlJywgJ0ZhbHNlIE5lZ2F0aXZlIFJhdGUnKQpyb3duYW1lcyhzcy5hbmFseXNpcykgPC0gYygnMScsJzInLCczJywnNCcsJzUnLCc2JykKc3MuYW5hbHlzaXMgPC0gYXMudGFibGUoc3MuYW5hbHlzaXMpCnNzLmFuYWx5c2lzCmBgYAoKRm9yIG15IGFkZGl0aW9uYWwgYW5hbHlzaXMsIEkgcHJvdmlkZWQgYSB0YWJsZSB3aXRoIGZhbHNlIHBvc2l0aXZlIGFuZCBmYWxzZSBuZWdhdGl2ZSByYXRlcyBmb3IgZWFjaCBtZXRob2QuIE92ZXJhbGwsIHJhbmRvbWZvcmVzdCBoYWQgYmV0dGVyIHRydWUgcG9zaXRpdmUgYWNjdXJhY3ksIGFuZCBzdm0gaGFkIGJldHRlciB0cnVlIG5lZ2F0aXZlIGFjY2N1cmFjeS4gQWxzbywgcmYtcnBhcnQgaGFkIGV4dHJlbWUgZGlmZmljdWx0eSBjb3JyZWN0bHkgY2xhc3NpZnlpbmcgbmVnYXRpdmUgZGF0YSwgd2l0aCBvbmx5IGEgMzQlIHRydWUgcG9zaXRpdmUgcmF0ZS4gVGhlcmVmb3JlLCBpZiBvbmUgdmFyaWFibGUgaXMgbW9yZSBzaWduaWZpY2FudCB0aGFuIHRoZSBvdGhlciwgaS5lLiB5b3Ugd291bGQgcmF0aGVyIGhhdmUgZmFsc2UgcG9zaXRpdmVzIHRoYW4gZmFsc2UgbmVnYXRpdmVzLCB0aGVuIGl0IGlzIHZpYWJsZSB0byBzZWxlY3Qgb25lIG1ldGhvZCBvdmVyIHRoZSBvdGhlci4gCgoKIyBDaGFsbGVuZ2UgUHJlZGljdGlvbgoKTXkgY2hhbGxlbmdlIElEIGlzIGhlcm5hZTUgd2l0aCBhbiBBVUMgc2NvcmUgb2YgLjgyIGZvciBwcmVkaWN0aW9uIGFuZCBiYWxhbmNlZCBhY2N1cmFjeSAuNTAgZm9yIGZlYXR1cmUgc2VsZWN0aW9uLiBJIHVzZWQgcmFuZG9tIGZvcmVzdCBhcyBteSBmaW5hbCBzZWxlY3Rpb24gbWV0aG9kLCBhcyBpdCBpcyB0aGUgbW9zdCBjb25zaXN0ZW50IG1ldGhvZCBhbmQgcmV0dXJuZWQgdGhlIGhpZ2VzdCByZXN1bHRzIGluIHRoZSBjaGFsbGVuZ2Ugb2YgYWxsIHRoYXQgSSBwcm9kdWNlZC4gCgpGb3IgbXkgY2hhbGxlbmdlIGVudHJpZXMsIFNWTSBjb25zaXN0ZW50bHkgb25seSByZXR1cm5lZCA1MCUgQVVDIHNjb3JlLCB3aGlsZSByYW5kb21mb3Jlc3Qgd2FzIDcwLTgwJS4gQWxsIGZlYXR1cmUgc2VsZWN0aW9uIG1ldGhvZHMgd2VyZSBmYWlybHkgcGVkZXN0cmlhbiwgb25seSByZXR1cm5pbmcgYWJvdXQgNTAtNTMlIGZvciBlYWNoIHNjb3JlLiBUaGVyZWZvcmUsIGFsbCByYW5kb21mb3Jlc3QgbWV0aG9kcyB3ZXJlIG1vcmUgZWZmZWN0aXZlIHRoYW4gU1ZNLCBhbmQgb2YgdGhlIGNob2ljZXMgcmFuZG9tZm9yZXN0IHdpdGggYWxsIGZlYXR1cmVzIHdhcyBjbGVhcmx5IHRoZSBiZXN0IG1ldGhvZCBwb3N0aW5nIGEgc2NvcmUgb2YgODIgY29tcGFyZWQgdG8gNzcgZm9yIGJvdGggd2l0aCBmZWF1cmUgc2VsZWN0aW9ucy4gCgoKIyBDb25jbHVzaW9uCgpJbiBjb25jbHVzaW9uLCBTVk0gY2FuIGJlIGEgdmVyeSBhY2N1cmF0ZSB0b29sIGZvciBjbGFzc2lmaWNhdGlvbiBtb2RlbGluZywgYnV0IEkgcHJlZmVyIHJhbmRvbWZvcmVzdCBkdWUgdG8gaXRzIG92ZXJhbGwgY29uc2lzdGVuY3kuIFNWTSB3b3JrZWQgYmVzdCB3aXRoIHJlY3Vyc2l2ZSBmZWF0dXJlIHNlbGVjdGlvbiwgYW5kIHJhbmRvbWZvcmVzdCB3aXRoIGFsbCBmZWF0dXJlcyBzZWxlY3RlZC4gT3ZlcmFsbCwgdXNpbmcgcnBhcnQgdmFyaWFibGUgaW1wb3J0YW5jZSBvbmx5IHNlcnZlZCBhcyBhIGhpbmRyYW5jZSB0byBhbnkgb2YgdGhlIGNsYXNzaWZpY2F0aW9uIG1ldGhvZHMsIG1ha2luZyBpdCBhIG5vbi12aWFibGUgZmVhdHVyZSBzZWxlY3Rpb24gbWV0aG9kIGZvciB0aGUgdGFzayBhdCBoYW5kLiAKCgojIyBQcm9qZWN0IEJhY2tncm91bmQKClRoZSBFdXJvcGVhbiBSRUFDSCByZWd1bGF0aW9uIHJlcXVpcmVzIGluZm9ybWF0aW9uIG9uIHJlYWR5IGJpb2RlZ3JhZGF0aW9uLCB3aGljaCBpcyBhIHNjcmVlbmluZyB0ZXN0IHRvIGFzc2VzcyB0aGUgYmlvZGVncmFkYWJpbGl0eSBvZiBjaGVtaWNhbHMuIEF0IHRoZSBzYW1lIHRpbWUgUkVBQ0ggZW5jb3VyYWdlcyB0aGUgdXNlIG9mIGFsdGVybmF0aXZlcyB0byBhbmltYWwgdGVzdGluZy4gCgpPdXIgY29udHJhY3QgaXMgdG8gYnVpbGQgYSBjbGFzc2lmaWNhdGlvbiBtb2RlbCB0byBwcmVkaWN0IHJlYWR5IGJpb2RlZ3JhZGF0aW9uIG9mIGNoZW1pY2FscyBhbmQgdG8gcHJlZGljdCB0aGUgY2xhc3NpZmljYXRpb24gb2YgNDAwIG5ld2x5LWRldmVsb3BlZCBtb2xlY3VsZXMuIFRoaXMgd2lsbCBoZWxwIENoZW1zLVItVXMgdG8gZGVzaWduIG5ldyBtYXRlcmlhbHMgd2l0aCBkZXNpcmVkIGJpb2RlZ3JhZGF0aW9uIHByb3BlcnRpZXMgbW9yZSBxdWlja2x5IGFuZCBmb3IgbG93ZXIgY29zdC4KCgojIyBDaGVtcy1SLVVzIENoYWxsZW5nZSBPYmplY3RpdmUKClRoZSBDaGVtcy1SLVVzIENoYWxsZW5nZSBjb25zaXN0cyBvZiB0d28gcHJvYmxlbXM6CgogICAqIEJpbmFyeSBjbGFzc2lmaWNhdGlvbjogRWFjaCBkYXRhIHJvdyBpcyBsYWJlbGVkIC0xIG9yIDEuIFdlIG11c3QgdHJhaW4gYSBwcmVkaWN0aXZlIG1vZGVsIG9uIHRoZSB0cmFpbiBkYXRhc2V0IHRvIGJlIGFibGUgdG8gZmluZCAoYXMgYmVzdCB3ZSBjYW4pIHRoZSBsYWJlbHMgb2YgdGhlIHRlc3QgZGF0YXNldC4KICAgKiBGZWF0dXJlIHNlbGVjdGlvbjogU2NhdHRlcmVkIGFtb25nIHRoZSAxNjggZmVhdHVyZXMgdGhlcmUgYXJlIGZha2UgZmVhdHVyZXMuIFRoZXNlIGFyZSByYW5kb21seSBnZW5lcmF0ZWQgdmFyaWFibGVzIHdoaWNoIGRvbid0IGhlbHAgcHJlZGljdGluZyB0aGUgY2xhc3MuIFRoZSBnb2FsIG9mIHRoaXMgcHJvYmxlbSBpcyB0byBjbGFzc2lmeSBmZWF0dXJlcyBiZXR3ZWVuIGZha2UgKDApIGFuZCByZWFsICgxKS4KCgojIyBDaGVtcy1SLVVzIFRyYWluaW5nICYgVGVzdGluZyBEYXRhCgogICAqIEV4cGVyaW1lbnRhbCB2YWx1ZXMgb2YgMTA1NSBjaGVtaWNhbHMgd2VyZSBjb2xsZWN0ZWQuIAogICAqIFRoZSB0cmFpbmluZyBkYXRhc2V0IGNvbnNpc3RzIG9mIHRoZXNlIDEwNTUgY2hlbWljYWxzOyB3aGV0aGVyIHRoZXkgd2VyZSByZWFkaWx5IGJpb2RlZ3JhZGFibGUgKDE9IHllcyAsIC0xID0gbm8pOyBhbmQgMTY4IG1vbGVjdWxhciBkZXNjcmlwdG9ycy4gIAogICAqIE1vbGVjdWxlcyBhbmQgTW9sZWN1bGFyIGRlc2NyaXB0b3JzIGFyZSBwcm9wcmlldGFyeS4gIE5vIGRldGFpbHMgYXJlIHByb3ZpZGVkIGV4Y2VwdCBfY3J5cHRpYyBuYW1lc18gaW4gY29sdW1uIGhlYWRlcnMKICAgKiBBIHRlc3Rpbmcgc2V0IG9mIDQwMCBtb2xlY3VsZXMgd2l0aCB1bmtub3duIGJpb2RlZ3JhZGFiaWxpdHkgaXMgcHJvdmlkZWQKCgojIyBDaGVtcy1SLVVzIENoYWxsZW5nZSBGaWxlcyAoRGV0YWlsKQoKKiBUUkFJTklORyBEQVRBIGlzIGRpdmlkZWQgaW50byBmb3VyIGZpbGVzOiAKCiAgICogYGNoZW1zX3RyYWluLmRhdGEuY3N2YDogVHJhaW5pbmcgZGF0YSBtYXRyaXggd2l0aCBubyByZXNwb25zZSBsYWJlbHMgKDEwMTggc2FtcGxlcyB4IDE2OCBmZWF0dXJlIHZhbHVlcykKICAgKiBgY2hlbXNfZmVhdC5uYW1lLmNzdmA6IE5hbWUgb2YgdGhlIDE2OCBhdHRyaWJ1dGVzICgxNjggeCAxIGZlYXR1cmVzIG5hbWVzKS4KICAgKiBgY2hlbXNfdHJhaW4uc29sdXRpb24uY3N2YDogVHJhaW5pbmcgdGFyZ2V0IHZhbHVlcyAoMTAxOCBsaW5lcyB4IDEgY29sdW1uKQoKKiBFWFRFUk5BTCBURVNUIERBVEEgaXMgb25lIGZpbGUKCiAgICogYGNoZW1zX3Rlc3QuZGF0YS5jc3ZgOiBUZXN0IGRhdGEgbWF0cml4ICg0Mzcgc2FtcGxlcyB4IDE2OCBmZWF0dXJlcyB2YWx1ZXMpCgoKIyMjIFJlYWRpbmcgdGhlIERhdGEKCmBgYHtyIGRhdGEucmVhZH0KIyBQcmVwYXJlIGJpb2RlZ3JhZGFiaWxpdHkgZGF0YSAKI2dldCBmZWF0dXJlIG5hbWVzIApmZWF0dXJlbmFtZXMgPC0gcmVhZC5jc3YoIn4vTUFUUC00NDAwL2RhdGEvY2hlbXNfZmVhdC5uYW1lLmNzdiIsCiAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXI9RkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICAgY29sQ2xhc3NlcyA9ICJjaGFyYWN0ZXIiKQpmZWF0dXJlbmFtZXMKIyBnZXQgdHJhaW5pbmcgZGF0YSBhbmQgcmVuYW1lIHdpdGggZmVhdHVyZSBuYW1lcwpjZGF0YS5kZiA8LXJlYWQuY3N2KCJ+L01BVFAtNDQwMC9kYXRhL2NoZW1zX3RyYWluLmRhdGEuY3N2IiwKICAgICAgICAgICAgICAgICAgICBoZWFkZXI9RkFMU0UpCmNvbG5hbWVzKGNkYXRhLmRmKSA8LSBmZWF0dXJlbmFtZXMkVjEKY2RhdGEuZGYKIyBnZXQgZXh0ZXJuYWwgdGVzdGluZyBkYXRhIGFuZCByZW5hbWUgd2l0aCBmZWF0dXJlIG5hbWVzCnRkYXRhLmRmIDwtcmVhZC5jc3YoIn4vTUFUUC00NDAwL2RhdGEvY2hlbXNfdGVzdC5kYXRhLmNzdiIsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyPUZBTFNFKSAKCmNvbG5hbWVzKHRkYXRhLmRmKSA8LSBmZWF0dXJlbmFtZXMkVjEKCmNsYXNzIDwtIHJlYWQuY3N2KCJ+L01BVFAtNDQwMC9kYXRhL2NoZW1zX3RyYWluLnNvbHV0aW9uLmNzdiIsCiAgICAgICAgICAgICAgICAgIGhlYWRlcj1GQUxTRSwgCiAgICAgICAgICAgICAgICAgIGNvbENsYXNzZXMgPSAiZmFjdG9yIikgCmNsYXNzIDwtIGNsYXNzJFYxCmBgYAoKIyMjIFByZXBhcmluZyB0aGUgRGF0YTogQ3JlYXRlIFRyYWluaW5nIGFuZCBWYWxpZGF0aW9uIGRhdGFzZXRzCgpXZSBzcGxpdCB0aGUgZGF0YSBpbnRvICoqOTAlIHRyYWluKiogYW5kICoqMTAlIHZhbGlkYXRpb24qKiBkYXRhc2V0cy4KCmBgYHtyIGRhdGEuc3BsaXR9CiNzcyB3aWxsIGJlIHRoZSBudW1iZXIgb2YgZGF0YSBwb2ludHMgaW4gdGhlIHRyYWluaW5nIHNldApuIDwtIG5yb3coY2RhdGEuZGYpCgpzcyA8LSBjZWlsaW5nKG4qMC45MCkKIyBTZXQgcmFuZG9tIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCgyMDApCnRyYWluLnBlcm0gPC0gc2FtcGxlKDE6bixzcykKCiNTcGxpdCB0cmFpbmluZyBhbmQgdmFsaWRhdGlvbiBkYXRhCnRyYWluIDwtIGNkYXRhLmRmICU+JSBzbGljZSh0cmFpbi5wZXJtKSAKdmFsaWRhdGlvbiA8LSBjZGF0YS5kZiAlPiUgc2xpY2UoLXRyYWluLnBlcm0pIApgYGAKCk5leHQsIHdlIGNyZWF0ZSBhIHNjYWxlciB0byBub3JtYWxpemUgdGhlIGRhdGEgYW5kIHByZXZlbnQgb3V0bGllcnMgaGF2aW5nIHNpZ25pZmljYW50IGltcGFjdCBvbiB0aGUgcmVzdWx0cy4gCgpgYGB7ciBzY2FsZXJ9CiMgSW5pdGlhbGl6ZSB0aGUgc2NhbGVyIG9uIHRoZSB0cmFpbmluZyBkYXRhCnNjYWxlciA8LSBwcmVQcm9jZXNzKHRyYWluLCBtZXRob2QgPSAic2NhbGUiKSAKc2NhbGVyIDwtIHByZVByb2Nlc3ModmFsaWRhdGlvbiwgbWV0aG9kID0gInNjYWxlIikKc2NhbGVyIDwtIHByZVByb2Nlc3ModGRhdGEuZGYsIG1ldGhvZCA9ICJzY2FsZSIpCgoKdGVzdCA8LSBwcmVkaWN0KHNjYWxlciwgdGRhdGEuZGYpCiMgTm9ybWFsaXplIHRyYWluaW5nIGRhdGEKIyBTcGxpdCB0aGUgb3V0cHV0IGNsYXNzZXMKCmNsYXNzdHJhaW4gPC0gY2xhc3NbdHJhaW4ucGVybV0KY2xhc3N2YWwgPC1jbGFzc1stdHJhaW4ucGVybV0KCgpgYGAKCiMjIyBGaXR0aW5nIERhdGEKCkZpcnN0LCB3ZSBjcmVhdGUgZGF0YWZyYW1lcyBjb21iaW5pbmcgdGhlIHZhcmlhYmxlcyBhbmQgdGhlIGNsYXNzIHJlc3VsdHMuCgpgYGB7ciB0cmFpbi5kZi5pbml0LCB3YXJuaW5nPUZBTFNFfQojIEZpdCBtb2RlbCB0byBjbGFzc2lmeSBhbGwgdGhlIHZhcmlhYmxlcwp0cmFpbi5kZiA8LSBjYmluZCh0cmFpbixjbGFzc3RyYWluKQp2YWwuZGYgPC0gY2JpbmQodmFsaWRhdGlvbiwgY2xhc3N2YWwpCmBgYAoKVGhlbiB3ZSB3cml0ZSBvdXIgbWV0aG9kcyBmb3IgZmVhdHVyZSBzZWxlY3Rpb24sIGJvdGggZm9yIHRoZSB0cmFpbmluZyBhbmQgdmFsaWRhdGlvbiBzZXRzLiAKCmBgYHtyIHJwYXJ0LmZlYXR1cmVzZWxlY3R9CiNDcmVhdGUgdHJhaW5pbmcgZm9yIHJwYXJ0IHZhcmlhYmxlIGltcG9ydGFuY2UKc2V0LnNlZWQoMTAwKQpyUGFydE1vZCA8LSB0cmFpbihjbGFzc3RyYWluIH4gLiwgZGF0YT10cmFpbi5kZiwgbWV0aG9kPSJycGFydCIpCnJwYXJ0SW1wIDwtIHZhckltcChyUGFydE1vZCkKCiNTZWxlY3QgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzLCBhbmQgc2VsZWN0IHRob3NlIGZyb20gZGF0YWZyYW1lIHRvIHVzZQp2YXJpYWJsZS5zZWxlY3QgPC0gcnBhcnRJbXAkaW1wb3J0YW5jZSAlPiUKICAgZHBseXI6OnNlbGVjdChPdmVyYWxsKSA+IDAKdmFyaWFibGUuZnMgPC0gdHJhaW4gJT4lCiAgIHNlbGVjdF9pZih2YXJpYWJsZS5zZWxlY3QpCnRyYWluLmRmLnJwYXJ0IDwtIGNiaW5kKHZhcmlhYmxlLmZzLCBjbGFzc3RyYWluKQpgYGAKCmBgYHtyIHJwYXJ0LmZlYXR1cmVzZWxlY3QudmFsfQpzZXQuc2VlZCgxMDApCnJQYXJ0TW9kLnZhbCA8LSB0cmFpbihjbGFzc3ZhbCB+IC4sIGRhdGE9dmFsLmRmLCBtZXRob2Q9InJwYXJ0IikKcnBhcnRJbXAudmFsIDwtIHZhckltcChyUGFydE1vZC52YWwpCnZhcmlhYmxlLnNlbGVjdC52YWwgPC0gcnBhcnRJbXAudmFsJGltcG9ydGFuY2UgJT4lCiAgIGRwbHlyOjpzZWxlY3QoT3ZlcmFsbCkgPiAwCnZhcmlhYmxlLmZzLnZhbCA8LSB2YWxpZGF0aW9uICU+JQogICBzZWxlY3RfaWYodmFyaWFibGUuc2VsZWN0LnZhbCkKdmFsLmRmLnJwYXJ0IDwtIGNiaW5kKHZhcmlhYmxlLmZzLnZhbCwgY2xhc3N2YWwpCmBgYAoKYGBge3IgcmZlLmZlYXR1cmVzZWxlY3R9CnNldC5zZWVkKDcpCmxpYnJhcnkobWxiZW5jaCkKbGlicmFyeShjYXJldCkKIyBkZWZpbmUgdGhlIGNvbnRyb2wgdXNpbmcgYSByYW5kb20gZm9yZXN0IHNlbGVjdGlvbiBmdW5jdGlvbgpjb250cm9sIDwtIHJmZUNvbnRyb2woZnVuY3Rpb25zPXJmRnVuY3MsIG1ldGhvZD0iY3YiLCBudW1iZXI9MTApCiMgcnVuIHRoZSBSRkUgYWxnb3JpdGhtCnJlc3VsdHMgPC0gcmZlKHRyYWluLmRmWzE6MTY4XSwgdHJhaW4uZGYkY2xhc3N0cmFpbiwgc2l6ZXM9MjAsIHJmZUNvbnRyb2w9Y29udHJvbCkKCnNpZy5yZmUgPC0gcHJlZGljdG9ycyhyZXN1bHRzKQpzaWcucmZlIDwtIGFzLmZhY3RvcihzaWcucmZlKQoKdHJhaW4uZGYucmZlIDwtIHRyYWluLmRmICU+JSBzZWxlY3Qoc2lnLnJmZSkKdHJhaW4uZGYucmZlIDwtIGNiaW5kKHRyYWluLmRmLnJmZSwgY2xhc3N0cmFpbikKYGBgCgpgYGB7ciByZmUuZmVhdHVyZXNlbGVjdC52YWx9CnNldC5zZWVkKDcpCmxpYnJhcnkobWxiZW5jaCkKbGlicmFyeShjYXJldCkKIyBkZWZpbmUgdGhlIGNvbnRyb2wgdXNpbmcgYSByYW5kb20gZm9yZXN0IHNlbGVjdGlvbiBmdW5jdGlvbgpjb250cm9sLnZhbCA8LSByZmVDb250cm9sKGZ1bmN0aW9ucz1yZkZ1bmNzLCBtZXRob2Q9ImN2IiwgbnVtYmVyPTEwKQojIHJ1biB0aGUgUkZFIGFsZ29yaXRobQpyZXN1bHRzLnZhbCA8LSByZmUodmFsLmRmWzE6MTY4XSwgdmFsLmRmJGNsYXNzdmFsLCBzaXplcz0yMCwgcmZlQ29udHJvbD1jb250cm9sLnZhbCkKCnNpZy5yZmUudmFsIDwtIHByZWRpY3RvcnMocmVzdWx0cy52YWwpCnNpZy5yZmUudmFsIDwtIGFzLmZhY3RvcihzaWcucmZlLnZhbCkKCnZhbC5kZi5yZmUgPC0gdmFsLmRmICU+JSBzZWxlY3Qoc2lnLnJmZS52YWwpCnZhbC5kZi5yZmUgPC0gY2JpbmQodmFsLmRmLnJmZSwgY2xhc3N2YWwpCmBgYAoKVGhlbiwgd2UgcGx1ZyBpbiBvdXIgdHJhaW5pbmcgYW5kIHZhbGlkYXRpb24gZGF0YWZyYW1lcyB0byB0aGUgdHJhaW5pbmcgYWxnb3JpdGhtcyBmb3IgYm90aCBjbGFzc2lmaWNhdGlvbnMgYW5kIGFsbCBmZWF0dXJlIGNvbWJpbmF0aW9ucy4gV2UgcmV0dXJuIHZhbHVlcyBmb3IgYmFsYW5jZWQgYWNjdXJhY3ksIGFuZCBhIGNvbmZ1c2lvbiBtYXRyaXggb2YgZGF0YS4gCgpgYGB7ciByZi50cmFpbiwgZWNobz1GQUxTRX0KI0NyZWF0ZSBjbGFzc2lmaWNhdGlvbiBvZiBkYXRhIHZpYSByYW5kb21Gb3Jlc3Qgdy8gYWxsIGZlYXR1cmVzCnJmIDwtIHJhbmRvbUZvcmVzdCgKICAgY2xhc3N0cmFpbn4uLAogICBkYXRhID0gdHJhaW4uZGYsCikKCmNvbmZtYXQucmYgPC0gcmYkY29uZnVzaW9uCmNvbmZtYXQucmYgPC0gc3Vic2V0KGNvbmZtYXQucmYsIHNlbGVjdCA9IC1jKGNsYXNzLmVycm9yKSApCiMgVHJ1ZSBQb3NpdGl2ZSBSYXRlIG9yIFNlbnNpdGl2aXR5ClNlbnNpdGl2aXR5LnJmIDwtIHNlbnNpdGl2aXR5X2Zyb21fY29uZm1hdChjb25mbWF0LnJmKSMgVHJ1ZSBOZWdhdGl2ZSBSYXRlIG9yIFNwZWNpZmljaXR5ClNwZWNpZmljaXR5LnJmIDwtIHNwZWNpZmljaXR5X2Zyb21fY29uZm1hdChjb25mbWF0LnJmKQpCYWxhbmNlZEFjY3VyYWN5LnJmIDwtIChTZW5zaXRpdml0eS5yZitTcGVjaWZpY2l0eS5yZikvMgpgYGAKCmBgYHtyIHJmLnZhbCwgZWNobz1GQUxTRX0KI0NyZWF0ZSBjbGFzc2lmaWNhdGlvbiBvZiBkYXRhIHZpYSByYW5kb21Gb3Jlc3Qgdy8gYWxsIGZlYXR1cmVzCnJmLnZhbCA8LSByYW5kb21Gb3Jlc3QoCiAgIGNsYXNzdmFsfi4sCiAgIGRhdGEgPSB2YWwuZGYsCiAgIG50cmVlID0gMzAwCikKCmNvbmZtYXQucmYudmFsIDwtIHJmJGNvbmZ1c2lvbgpjb25mbWF0LnJmLnZhbCA8LSBzdWJzZXQoY29uZm1hdC5yZi52YWwsIHNlbGVjdCA9IC1jKGNsYXNzLmVycm9yKSApCiMgVHJ1ZSBQb3NpdGl2ZSBSYXRlIG9yIFNlbnNpdGl2aXR5ClNlbnNpdGl2aXR5LnJmLnZhbCA8LSBzZW5zaXRpdml0eV9mcm9tX2NvbmZtYXQoY29uZm1hdC5yZi52YWwpIyBUcnVlIE5lZ2F0aXZlIFJhdGUgb3IgU3BlY2lmaWNpdHkKU3BlY2lmaWNpdHkucmYudmFsIDwtIHNwZWNpZmljaXR5X2Zyb21fY29uZm1hdChjb25mbWF0LnJmLnZhbCkKQmFsYW5jZWRBY2N1cmFjeS5yZi52YWwgPC0gKFNlbnNpdGl2aXR5LnJmLnZhbCtTcGVjaWZpY2l0eS5yZi52YWwpLzIKYGBgCgpgYGB7ciByZi5yZmUudHJhaW59CgpyZi5yZmUgPC0gcmFuZG9tRm9yZXN0KAogICBjbGFzc3RyYWlufi4sCiAgIGRhdGEgPSB0cmFpbi5kZi5yZmUsCikKCmNvbmZtYXQucmYucmZlIDwtIHJmLnJmZSRjb25mdXNpb24KY29uZm1hdC5yZi5yZmUgPC0gc3Vic2V0KGNvbmZtYXQucmYucmZlLCBzZWxlY3QgPSAtYyhjbGFzcy5lcnJvcikgKQojIFRydWUgUG9zaXRpdmUgUmF0ZSBvciBTZW5zaXRpdml0eQpTZW5zaXRpdml0eS5yZi5yZmUgPC0gc2Vuc2l0aXZpdHlfZnJvbV9jb25mbWF0KGNvbmZtYXQucmYucmZlKSMgVHJ1ZSBOZWdhdGl2ZSBSYXRlIG9yIFNwZWNpZmljaXR5ClNwZWNpZmljaXR5LnJmLnJmZSA8LSBzcGVjaWZpY2l0eV9mcm9tX2NvbmZtYXQoY29uZm1hdC5yZi5yZmUpCkJhbGFuY2VkQWNjdXJhY3kucmYucmZlIDwtIChTZW5zaXRpdml0eS5yZi5yZmUrU3BlY2lmaWNpdHkucmYucmZlKS8yCmBgYAoKYGBge3IgcmYucmZlLnZhbH0KCnJmLnJmZS52YWwgPC0gcmFuZG9tRm9yZXN0KAogICBjbGFzc3ZhbH4uLAogICBkYXRhID0gdmFsLmRmLnJmZSwKKQoKY29uZm1hdC5yZi5yZmUudmFsIDwtIHJmLnJmZS52YWwkY29uZnVzaW9uCmNvbmZtYXQucmYucmZlLnZhbCA8LSBzdWJzZXQoY29uZm1hdC5yZi5yZmUudmFsLCBzZWxlY3QgPSAtYyhjbGFzcy5lcnJvcikgKQojIFRydWUgUG9zaXRpdmUgUmF0ZSBvciBTZW5zaXRpdml0eQpTZW5zaXRpdml0eS5yZi5yZmUudmFsIDwtIHNlbnNpdGl2aXR5X2Zyb21fY29uZm1hdChjb25mbWF0LnJmLnJmZS52YWwpIyBUcnVlIE5lZ2F0aXZlIFJhdGUgb3IgU3BlY2lmaWNpdHkKU3BlY2lmaWNpdHkucmYucmZlLnZhbCA8LSBzcGVjaWZpY2l0eV9mcm9tX2NvbmZtYXQoY29uZm1hdC5yZi5yZmUudmFsKQpCYWxhbmNlZEFjY3VyYWN5LnJmLnJmZS52YWwgPC0gKFNlbnNpdGl2aXR5LnJmLnJmZS52YWwrU3BlY2lmaWNpdHkucmYucmZlLnZhbCkvMgpCYWxhbmNlZEFjY3VyYWN5LnJmLnJmZS52YWwKYGBgCgpgYGB7ciByZi5ycGFydC50cmFpbn0KI3JhbmRvbSBmb3Jlc3QgdXNpbmcgcnBhcnQgdmFyaWFibGUgaW1wb3JhbmNlCnJmLnJwYXJ0IDwtIHJhbmRvbUZvcmVzdCgKICAgY2xhc3N0cmFpbn4uLAogICBkYXRhID0gdHJhaW4uZGYucnBhcnQsCikKCmNvbmZtYXQucmYucnBhcnQgPC0gcmYucnBhcnQkY29uZnVzaW9uCmNvbmZtYXQucmYucnBhcnQgPC0gc3Vic2V0KGNvbmZtYXQucmYucnBhcnQsIHNlbGVjdCA9IC1jKGNsYXNzLmVycm9yKSApCiMgVHJ1ZSBQb3NpdGl2ZSBSYXRlIG9yIFNlbnNpdGl2aXR5ClNlbnNpdGl2aXR5LnJmLnJwYXJ0IDwtIHNlbnNpdGl2aXR5X2Zyb21fY29uZm1hdChjb25mbWF0LnJmLnJwYXJ0KSMgVHJ1ZSBOZWdhdGl2ZSBSYXRlIG9yIFNwZWNpZmljaXR5ClNwZWNpZmljaXR5LnJmLnJwYXJ0IDwtIHNwZWNpZmljaXR5X2Zyb21fY29uZm1hdChjb25mbWF0LnJmLnJwYXJ0KQpCYWxhbmNlZEFjY3VyYWN5LnJmLnJwYXJ0IDwtIChTZW5zaXRpdml0eS5yZi5ycGFydCtTcGVjaWZpY2l0eS5yZi5ycGFydCkvMgpgYGAKCmBgYHtyIHJmLnJwYXJ0LnZhbH0KI3JhbmRvbSBmb3Jlc3QgdXNpbmcgcnBhcnQgdmFyaWFibGUgaW1wb3JhbmNlCnJmLnJwYXJ0LnZhbCA8LSByYW5kb21Gb3Jlc3QoCiAgIGNsYXNzdmFsfi4sCiAgIGRhdGEgPSB2YWwuZGYucnBhcnQsCikKCmNvbmZtYXQucmYucnBhcnQudmFsIDwtIHJmLnJwYXJ0LnZhbCRjb25mdXNpb24KY29uZm1hdC5yZi5ycGFydC52YWwgPC0gc3Vic2V0KGNvbmZtYXQucmYucnBhcnQudmFsLCBzZWxlY3QgPSAtYyhjbGFzcy5lcnJvcikgKQojIFRydWUgUG9zaXRpdmUgUmF0ZSBvciBTZW5zaXRpdml0eQpTZW5zaXRpdml0eS5yZi5ycGFydC52YWwgPC0gc2Vuc2l0aXZpdHlfZnJvbV9jb25mbWF0KGNvbmZtYXQucmYucnBhcnQudmFsKSMgVHJ1ZSBOZWdhdGl2ZSBSYXRlIG9yIFNwZWNpZmljaXR5ClNwZWNpZmljaXR5LnJmLnJwYXJ0LnZhbCA8LSBzcGVjaWZpY2l0eV9mcm9tX2NvbmZtYXQoY29uZm1hdC5yZi5ycGFydC52YWwpCkJhbGFuY2VkQWNjdXJhY3kucmYucnBhcnQudmFsIDwtIChTZW5zaXRpdml0eS5yZi5ycGFydC52YWwrU3BlY2lmaWNpdHkucmYucnBhcnQudmFsKS8yCmBgYAoKYGBge3Igc3ZtLnRyYWlufQpjb250cm9sIDwtIHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgbnVtYmVyPTEwKQptZXRyaWMgPC0gIkFjY3VyYWN5IgpzZXQuc2VlZCg3KQpmaXQuc3ZtIDwtIHRyYWluKGNsYXNzdHJhaW5+LiwgZGF0YT10cmFpbi5kZiwgbWV0aG9kPSJzdm1SYWRpYWwiLCBtZXRyaWM9bWV0cmljLCB0ckNvbnRyb2w9Y29udHJvbCkKcHJlZC5zdm0gPC0gcHJlZGljdChmaXQuc3ZtLCB0cmFpbi5kZikKCgpjb25mbWF0LnN2bSA8LSB0YWJsZShwcmVkLnN2bSwgdHJhaW4uZGYkY2xhc3N0cmFpbiwgZG5uPWMoIlByZWRpY3Rpb24iLCAiQWN0dWFsIikpICAgCiMgVHJ1ZSBQb3NpdGl2ZSBSYXRlIG9yIFNlbnNpdGl2aXR5ClNlbnNpdGl2aXR5LnN2bSA8LSBzZW5zaXRpdml0eV9mcm9tX2NvbmZtYXQoY29uZm1hdC5zdm0pIyBUcnVlIE5lZ2F0aXZlIFJhdGUgb3IgU3BlY2lmaWNpdHkKU3BlY2lmaWNpdHkuc3ZtIDwtIHNwZWNpZmljaXR5X2Zyb21fY29uZm1hdChjb25mbWF0LnN2bSkKQmFsYW5jZWRBY2N1cmFjeS5zdm0gPC0gKFNlbnNpdGl2aXR5LnN2bStTcGVjaWZpY2l0eS5zdm0pLzIKYGBgCgpgYGB7ciBzdm0udmFsfQpjb250cm9sIDwtIHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgbnVtYmVyPTEwKQptZXRyaWMgPC0gIkFjY3VyYWN5IgpzZXQuc2VlZCg3KQpmaXQuc3ZtLnZhbCA8LSB0cmFpbihjbGFzc3ZhbH4uLCBkYXRhPXZhbC5kZiwgbWV0aG9kPSJzdm1SYWRpYWwiLCBtZXRyaWM9bWV0cmljLCB0ckNvbnRyb2w9Y29udHJvbCkKcHJlZC5zdm0udmFsIDwtIHByZWRpY3QoZml0LnN2bSwgdmFsLmRmKQoKCmNvbmZtYXQuc3ZtLnZhbCA8LSB0YWJsZShwcmVkLnN2bS52YWwsIHZhbC5kZiRjbGFzc3ZhbCwgZG5uPWMoIlByZWRpY3Rpb24iLCAiQWN0dWFsIikpICAgCiMgVHJ1ZSBQb3NpdGl2ZSBSYXRlIG9yIFNlbnNpdGl2aXR5ClNlbnNpdGl2aXR5LnN2bS52YWwgPC0gc2Vuc2l0aXZpdHlfZnJvbV9jb25mbWF0KGNvbmZtYXQuc3ZtLnZhbCkjIFRydWUgTmVnYXRpdmUgUmF0ZSBvciBTcGVjaWZpY2l0eQpTcGVjaWZpY2l0eS5zdm0udmFsIDwtIHNwZWNpZmljaXR5X2Zyb21fY29uZm1hdChjb25mbWF0LnN2bS52YWwpCkJhbGFuY2VkQWNjdXJhY3kuc3ZtLnZhbCA8LSAoU2Vuc2l0aXZpdHkuc3ZtLnZhbCtTcGVjaWZpY2l0eS5zdm0udmFsKS8yCmBgYAoKYGBge3Igc3ZtLnJmZS50cmFpbn0KY29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kPSJjdiIsIG51bWJlcj0xMCkKbWV0cmljIDwtICJBY2N1cmFjeSIKc2V0LnNlZWQoNykKZml0LnN2bS5yZmUgPC0gdHJhaW4oY2xhc3N0cmFpbn4uLCBkYXRhPXRyYWluLmRmLnJmZSwgbWV0aG9kPSJzdm1SYWRpYWwiLCBtZXRyaWM9bWV0cmljLCB0ckNvbnRyb2w9Y29udHJvbCkKcHJlZC5zdm0ucmZlIDwtIHByZWRpY3QoZml0LnN2bS5yZmUsIHRyYWluLmRmLnJmZSkKCgpjb25mbWF0LnN2bS5yZmUgPC0gdGFibGUocHJlZC5zdm0ucmZlLCB0cmFpbi5kZiRjbGFzc3RyYWluLCBkbm49YygiUHJlZGljdGlvbiIsICJBY3R1YWwiKSkgIApTZW5zaXRpdml0eS5zdm0ucmZlIDwtIHNlbnNpdGl2aXR5X2Zyb21fY29uZm1hdChjb25mbWF0LnN2bS5yZmUpIyBUcnVlIE5lZ2F0aXZlIFJhdGUgb3IgU3BlY2lmaWNpdHkKU3BlY2lmaWNpdHkuc3ZtLnJmZSA8LSBzcGVjaWZpY2l0eV9mcm9tX2NvbmZtYXQoY29uZm1hdC5zdm0ucmZlKQpCYWxhbmNlZEFjY3VyYWN5LnN2bS5yZmUgPC0gKFNlbnNpdGl2aXR5LnN2bS5yZmUrU3BlY2lmaWNpdHkuc3ZtLnJmZSkvMgpgYGAKCmBgYHtyIHN2bS5yZmUudmFsfQpjb250cm9sIDwtIHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgbnVtYmVyPTEwKQptZXRyaWMgPC0gIkFjY3VyYWN5IgpzZXQuc2VlZCg3KQpmaXQuc3ZtLnJmZS52YWwgPC0gdHJhaW4oY2xhc3N2YWx+LiwgZGF0YT12YWwuZGYucmZlLCBtZXRob2Q9InN2bVJhZGlhbCIsIG1ldHJpYz1tZXRyaWMsIHRyQ29udHJvbD1jb250cm9sKQpwcmVkLnN2bS5yZmUudmFsIDwtIHByZWRpY3QoZml0LnN2bS5yZmUudmFsLCB2YWwuZGYucmZlKQoKCmNvbmZtYXQuc3ZtLnJmZS52YWwgPC0gdGFibGUocHJlZC5zdm0ucmZlLnZhbCwgdmFsLmRmJGNsYXNzdmFsLCBkbm49YygiUHJlZGljdGlvbiIsICJBY3R1YWwiKSkgIApTZW5zaXRpdml0eS5zdm0ucmZlLnZhbCA8LSBzZW5zaXRpdml0eV9mcm9tX2NvbmZtYXQoY29uZm1hdC5zdm0ucmZlLnZhbCkjIFRydWUgTmVnYXRpdmUgUmF0ZSBvciBTcGVjaWZpY2l0eQpTcGVjaWZpY2l0eS5zdm0ucmZlLnZhbCA8LSBzcGVjaWZpY2l0eV9mcm9tX2NvbmZtYXQoY29uZm1hdC5zdm0ucmZlLnZhbCkKQmFsYW5jZWRBY2N1cmFjeS5zdm0ucmZlLnZhbCA8LSAoU2Vuc2l0aXZpdHkuc3ZtLnJmZS52YWwrU3BlY2lmaWNpdHkuc3ZtLnJmZS52YWwpLzIKYGBgCgpgYGB7ciBzdm0ucnBhcnQudHJhaW59CmNvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZD0iY3YiLCBudW1iZXI9MTApCm1ldHJpYyA8LSAiQWNjdXJhY3kiCnNldC5zZWVkKDcpCmZpdC5zdm0ucnBhcnQgPC0gdHJhaW4oY2xhc3N0cmFpbn4uLCBkYXRhPXRyYWluLmRmLnJwYXJ0LCBtZXRob2Q9InN2bVJhZGlhbCIsIG1ldHJpYz1tZXRyaWMsIHRyQ29udHJvbD1jb250cm9sKQpwcmVkLnN2bS5ycGFydCA8LSBwcmVkaWN0KGZpdC5zdm0ucnBhcnQsIHRyYWluLmRmLnJwYXJ0KQoKY29uZm1hdC5zdm0ucnBhcnQgPC0gdGFibGUocHJlZC5zdm0ucnBhcnQsIHRyYWluLmRmJGNsYXNzdHJhaW4sIGRubj1jKCJQcmVkaWN0aW9uIiwgIkFjdHVhbCIpKQpTZW5zaXRpdml0eS5zdm0ucnBhcnQgPC0gc2Vuc2l0aXZpdHlfZnJvbV9jb25mbWF0KGNvbmZtYXQuc3ZtLnJwYXJ0KSMgVHJ1ZSBOZWdhdGl2ZSBSYXRlIG9yIFNwZWNpZmljaXR5ClNwZWNpZmljaXR5LnN2bS5ycGFydCA8LSBzcGVjaWZpY2l0eV9mcm9tX2NvbmZtYXQoY29uZm1hdC5zdm0ucnBhcnQpCkJhbGFuY2VkQWNjdXJhY3kuc3ZtLnJwYXJ0IDwtIChTZW5zaXRpdml0eS5zdm0ucnBhcnQrU3BlY2lmaWNpdHkuc3ZtLnJwYXJ0KS8yCmBgYAoKYGBge3Igc3ZtLnJwYXJ0LnZhbH0KY29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kPSJjdiIsIG51bWJlcj0xMCkKbWV0cmljIDwtICJBY2N1cmFjeSIKc2V0LnNlZWQoNykKZml0LnN2bS5ycGFydC52YWwgPC0gdHJhaW4oY2xhc3N2YWx+LiwgZGF0YT12YWwuZGYucnBhcnQsIG1ldGhvZD0ic3ZtUmFkaWFsIiwgbWV0cmljPW1ldHJpYywgdHJDb250cm9sPWNvbnRyb2wpCnByZWQuc3ZtLnJwYXJ0LnZhbCA8LSBwcmVkaWN0KGZpdC5zdm0ucnBhcnQudmFsLCB2YWwuZGYucnBhcnQpCgpjb25mbWF0LnN2bS5ycGFydC52YWwgPC0gdGFibGUocHJlZC5zdm0ucnBhcnQudmFsLCB2YWwuZGYkY2xhc3N2YWwsIGRubj1jKCJQcmVkaWN0aW9uIiwgIkFjdHVhbCIpKQpTZW5zaXRpdml0eS5zdm0ucnBhcnQudmFsIDwtIHNlbnNpdGl2aXR5X2Zyb21fY29uZm1hdChjb25mbWF0LnN2bS5ycGFydC52YWwpIyBUcnVlIE5lZ2F0aXZlIFJhdGUgb3IgU3BlY2lmaWNpdHkKU3BlY2lmaWNpdHkuc3ZtLnJwYXJ0LnZhbCA8LSBzcGVjaWZpY2l0eV9mcm9tX2NvbmZtYXQoY29uZm1hdC5zdm0ucnBhcnQudmFsKQpCYWxhbmNlZEFjY3VyYWN5LnN2bS5ycGFydC52YWwgPC0gKFNlbnNpdGl2aXR5LnN2bS5ycGFydC52YWwrU3BlY2lmaWNpdHkuc3ZtLnJwYXJ0LnZhbCkvMgpgYGAKCiMjIyBST0MgQ3VydmVzIAoKCmBgYHtyIHN2bS5yZmUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFfQpyb2MuZGF0YSA8LSBkYXRhLmZyYW1lKCJDbGFzcyIgPSBjbGFzc3ZhbCwgCiAgICAgICAgICAgICAgICAgICAgICAgIk5vIFNlbGVjdGlvbiIgPSBwcmVkLnN2bS52YWwsCiAgICAgICAgICAgICAgICAgICAgICAgIldpdGggU2VsZWN0aW9uIiA9IHByZWQuc3ZtLnJmZS52YWwpCnJvYy5kYXRhJENsYXNzIDwtIGFzLm51bWVyaWMocm9jLmRhdGEkQ2xhc3MpCnJvYy5kYXRhJE5vLlNlbGVjdGlvbiA8LSBhcy5udW1lcmljKHJvYy5kYXRhJE5vLlNlbGVjdGlvbikKcm9jLmRhdGEkV2l0aC5TZWxlY3Rpb24gPC0gYXMubnVtZXJpYyhyb2MuZGF0YSRXaXRoLlNlbGVjdGlvbikKcm9jLmxpc3QgPC0gcm9jKENsYXNzIH4uLCAKICAgICAgICAgICAgICAgIGRhdGEgPSByb2MuZGF0YSkKbm8uc2VsZWN0aW9uLmF1YyA8LSByb3VuZChhdWMoQ2xhc3MgfiBOby5TZWxlY3Rpb24sIGRhdGEgPSByb2MuZGF0YSksIGRpZ2l0cyA9IDMpCndpdGguc2VsZWN0aW9uLmF1YyA8LSByb3VuZChhdWMoQ2xhc3MgfiBXaXRoLlNlbGVjdGlvbiwgZGF0YSA9IHJvYy5kYXRhKSwgZGlnaXRzID0gMykKcm9jX3Bsb3QgPC0gZ2dyb2Mocm9jLmxpc3QpICsgCiAgIGdndGl0bGUoIlJPQyBDdXJ2ZXMgKFZhbGlkYXRpb24gU2V0KSIsIHN1YnRpdGxlID0gIlNWTSB3aXRob3V0IEZlYXR1cmUgU2VsZWN0aW9uIHZlcnN1cyBTVk0gd2l0aCBSRkUiKSArIAogICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShuYW1lID0gIk1vZGVsIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMocGFzdGUoIk5vIFNlbGVjdGlvblxuQVVDIDoiLCBuby5zZWxlY3Rpb24uYXVjKSwgcGFzdGUoIldpdGggU2VsZWN0aW9uXG5BVUMgOiIsIHdpdGguc2VsZWN0aW9uLmF1YykpKQpyb2NfcGxvdApgYGAKCmBgYHtyIHN2bS5ycGFydCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CnJvYy5kYXRhIDwtIGRhdGEuZnJhbWUoIkNsYXNzIiA9IGNsYXNzdmFsLCAKICAgICAgICAgICAgICAgICAgICAgICAiTm8gU2VsZWN0aW9uIiA9IHByZWQuc3ZtLnZhbCwKICAgICAgICAgICAgICAgICAgICAgICAiV2l0aCBTZWxlY3Rpb24iID0gcHJlZC5zdm0ucnBhcnQudmFsKQpyb2MuZGF0YSRDbGFzcyA8LSBhcy5udW1lcmljKHJvYy5kYXRhJENsYXNzKQpyb2MuZGF0YSROby5TZWxlY3Rpb24gPC0gYXMubnVtZXJpYyhyb2MuZGF0YSROby5TZWxlY3Rpb24pCnJvYy5kYXRhJFdpdGguU2VsZWN0aW9uIDwtIGFzLm51bWVyaWMocm9jLmRhdGEkV2l0aC5TZWxlY3Rpb24pCnJvYy5saXN0IDwtIHJvYyhDbGFzcyB+LiwgCiAgICAgICAgICAgICAgICBkYXRhID0gcm9jLmRhdGEpCm5vLnNlbGVjdGlvbi5hdWMgPC0gcm91bmQoYXVjKENsYXNzIH4gTm8uU2VsZWN0aW9uLCBkYXRhID0gcm9jLmRhdGEpLCBkaWdpdHMgPSAzKQp3aXRoLnNlbGVjdGlvbi5hdWMgPC0gcm91bmQoYXVjKENsYXNzIH4gV2l0aC5TZWxlY3Rpb24sIGRhdGEgPSByb2MuZGF0YSksIGRpZ2l0cyA9IDMpCnJvY19wbG90IDwtIGdncm9jKHJvYy5saXN0KSArIAogICBnZ3RpdGxlKCJST0MgQ3VydmVzIChWYWxpZGF0aW9uIFNldCkiLCBzdWJ0aXRsZSA9ICJTVk0gd2l0aG91dCBGZWF0dXJlIFNlbGVjdGlvbiB2ZXJzdXMgU1ZNIHdpdGggUkZFIikgKyAKICAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZSA9ICJNb2RlbCIsIAogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKHBhc3RlKCJObyBTZWxlY3Rpb25cbkFVQyA6Iiwgbm8uc2VsZWN0aW9uLmF1YyksIHBhc3RlKCJXaXRoIFNlbGVjdGlvblxuQVVDIDoiLCB3aXRoLnNlbGVjdGlvbi5hdWMpKSkKcm9jX3Bsb3QKYGBgCgpgYGB7ciByZi5yZmUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFfQpwcmVkLnJmIDwtIHByZWRpY3QocmYudmFsLCB2YWxpZGF0aW9uLCB0eXBlID0gJ3Jlc3BvbnNlJykKcHJlZC5yZi5yZmUgPC0gcHJlZGljdChyZi5yZmUudmFsLCB2YWxpZGF0aW9uLCB0eXBlID0gJ3Jlc3BvbnNlJykKcHJlZC5yZi5ycGFydCA8LSBwcmVkaWN0KHJmLnJwYXJ0LnZhbCwgdmFsaWRhdGlvbiwgdHlwZSA9ICdyZXNwb25zZScpCnJvYy5kYXRhIDwtIGRhdGEuZnJhbWUoIkNsYXNzIiA9IGNsYXNzdmFsLCAKICAgICAgICAgICAgICAgICAgICAgICAiTm8gU2VsZWN0aW9uIiA9IHByZWQucmYsCiAgICAgICAgICAgICAgICAgICAgICAgIldpdGggU2VsZWN0aW9uIiA9IHByZWQucmYucmZlKQpyb2MuZGF0YSRDbGFzcyA8LSBhcy5udW1lcmljKHJvYy5kYXRhJENsYXNzKQpyb2MuZGF0YSROby5TZWxlY3Rpb24gPC0gYXMubnVtZXJpYyhyb2MuZGF0YSROby5TZWxlY3Rpb24pCnJvYy5kYXRhJFdpdGguU2VsZWN0aW9uIDwtIGFzLm51bWVyaWMocm9jLmRhdGEkV2l0aC5TZWxlY3Rpb24pCnJvYy5saXN0IDwtIHJvYyhDbGFzcyB+LiwgCiAgICAgICAgICAgICAgICBkYXRhID0gcm9jLmRhdGEpCm5vLnNlbGVjdGlvbi5hdWMgPC0gcm91bmQoYXVjKENsYXNzIH4gTm8uU2VsZWN0aW9uLCBkYXRhID0gcm9jLmRhdGEpLCBkaWdpdHMgPSAzKQp3aXRoLnNlbGVjdGlvbi5hdWMgPC0gcm91bmQoYXVjKENsYXNzIH4gV2l0aC5TZWxlY3Rpb24sIGRhdGEgPSByb2MuZGF0YSksIGRpZ2l0cyA9IDMpCnJvY19wbG90IDwtIGdncm9jKHJvYy5saXN0KSArIAogICBnZ3RpdGxlKCJST0MgQ3VydmVzIChWYWxpZGF0aW9uIFNldCkiLCBzdWJ0aXRsZSA9ICJTVk0gd2l0aG91dCBGZWF0dXJlIFNlbGVjdGlvbiB2ZXJzdXMgU1ZNIHdpdGggUkZFIikgKyAKICAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZSA9ICJNb2RlbCIsIAogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKHBhc3RlKCJObyBTZWxlY3Rpb25cbkFVQyA6Iiwgbm8uc2VsZWN0aW9uLmF1YyksIHBhc3RlKCJXaXRoIFNlbGVjdGlvblxuQVVDIDoiLCB3aXRoLnNlbGVjdGlvbi5hdWMpKSkKcm9jX3Bsb3QKYGBgCgpgYGB7ciByZi5ycGFydCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CnByZWQucmYgPC0gcHJlZGljdChyZi52YWwsIHZhbGlkYXRpb24sIHR5cGUgPSAncmVzcG9uc2UnKQpwcmVkLnJmLnJmZSA8LSBwcmVkaWN0KHJmLnJmZS52YWwsIHZhbGlkYXRpb24sIHR5cGUgPSAncmVzcG9uc2UnKQpwcmVkLnJmLnJwYXJ0IDwtIHByZWRpY3QocmYucnBhcnQudmFsLCB2YWxpZGF0aW9uLCB0eXBlID0gJ3Jlc3BvbnNlJykKcm9jLmRhdGEgPC0gZGF0YS5mcmFtZSgiQ2xhc3MiID0gY2xhc3N2YWwsIAogICAgICAgICAgICAgICAgICAgICAgICJObyBTZWxlY3Rpb24iID0gcHJlZC5yZiwKICAgICAgICAgICAgICAgICAgICAgICAiV2l0aCBTZWxlY3Rpb24iID0gcHJlZC5yZi5ycGFydCkKcm9jLmRhdGEkQ2xhc3MgPC0gYXMubnVtZXJpYyhyb2MuZGF0YSRDbGFzcykKcm9jLmRhdGEkTm8uU2VsZWN0aW9uIDwtIGFzLm51bWVyaWMocm9jLmRhdGEkTm8uU2VsZWN0aW9uKQpyb2MuZGF0YSRXaXRoLlNlbGVjdGlvbiA8LSBhcy5udW1lcmljKHJvYy5kYXRhJFdpdGguU2VsZWN0aW9uKQpyb2MubGlzdCA8LSByb2MoQ2xhc3Mgfi4sIAogICAgICAgICAgICAgICAgZGF0YSA9IHJvYy5kYXRhKQpuby5zZWxlY3Rpb24uYXVjIDwtIHJvdW5kKGF1YyhDbGFzcyB+IE5vLlNlbGVjdGlvbiwgZGF0YSA9IHJvYy5kYXRhKSwgZGlnaXRzID0gMykKd2l0aC5zZWxlY3Rpb24uYXVjIDwtIHJvdW5kKGF1YyhDbGFzcyB+IFdpdGguU2VsZWN0aW9uLCBkYXRhID0gcm9jLmRhdGEpLCBkaWdpdHMgPSAzKQpyb2NfcGxvdCA8LSBnZ3JvYyhyb2MubGlzdCkgKyAKICAgZ2d0aXRsZSgiUk9DIEN1cnZlcyAoVmFsaWRhdGlvbiBTZXQpIiwgc3VidGl0bGUgPSAiU1ZNIHdpdGhvdXQgRmVhdHVyZSBTZWxlY3Rpb24gdmVyc3VzIFNWTSB3aXRoIFJGRSIpICsgCiAgIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiTW9kZWwiLCAKICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYyhwYXN0ZSgiTm8gU2VsZWN0aW9uXG5BVUMgOiIsIG5vLnNlbGVjdGlvbi5hdWMpLCBwYXN0ZSgiV2l0aCBTZWxlY3Rpb25cbkFVQyA6Iiwgd2l0aC5zZWxlY3Rpb24uYXVjKSkpCnJvY19wbG90CmBgYAojIyMgSW50ZXJwcmV0aW5nIHRoZSBST0MgUmVzdWx0cwoKVGhlIHZhbGlkYXRpb24gcmVzdWx0cyBzaG93IHRoYXQgcmFuZG9tRm9yZXN0IHdpdGggZmVhdHVyZSBzZWxlY3Rpb24gcHJvZHVjZXMgc2xpZ2h0bHkgd29yc2UgZ2VuZXJhbGl6YXRpb25zIChhY2N1cmFjeSBvbiB0aGUgdmFsaWRhdGlvbiBzZXQpIHJlc3VsdHMgdGhhbiBvdXIgb3JpZ2luYWwgbW9kZWwgdXNpbmcgYWxsIHRoZSB2YXJpYWJsZXMuCgpBbHRlcm5hdGl2ZWx5LCB3ZSBzZWUgdGhhdCB3aGVuIHVzaW5nIFNWTSB3aXRoIFJGRSBzZWxlY2lvbiwgdGhhdCB0aGUgZmVhdHVyZSBzZWxlY3RlZCBtb2RlbCB3aWxsIHJlc3VsdCBpbiBiZXR0ZXIgZ2VuZXJhbGl6YXRpb25zIG9uIHRoZSBkYXRhLgoKIyMgQ29tcGV0aXRpb24gRW50cnk6IFNhdmluZyAmIFVwbG9hZGluZyBZb3VyIFByZWRpY3Rpb25zCgpUaGUgZm9sbG93aW5nIGNvZGUgY3JlYXRlcyBhIHZhbGlkIGVudHJ5IGZvciB0aGUgY29udGVzdDogCgogICAqIFByZWRpY3QgdGhlIHRlc3QgZGF0YSBhbmQgcHV0IHRoZSByYW5raW5nIGluIGByYW5raW5nX2xydGVzdGAKICAgICAgKiBUaGUgcmFua2luZyBjYW4gYmUgYW55IG51bWJlciAqbGVhZGluZyB0byBhIGNsYXNzaWZpY2F0aW9uIGxpa2UgbG9nIG9kZHMqLgogICAgICAqIFRoaXMgbWVhbnMgKip2YWx1ZXMgZ3JlYXRlciB0aGFuIDAgbWVhbiBjbGFzcyAxKiogYW5kICoqdmFsdWVzIGxlc3MgdGhhbiAwIG1lYW4gY2xhc3MgLTEqKi4gCiAgICAgICogVGhlIHJlc3VsdHMgd2lsbCBiZSAqKnJhbmtlZCBieSBBVUMqKi4KICAgKiBUaGVuIHdyaXRlIHRoZSByZXN1bHRzIHRoZSBDU1YgZmlsZSBuYW1lZCBgY2xhc3NpZmljYXRpb24uY3N2YCAoWW91IF9tdXN0XyB1c2UgdGhpcyBmaWxlbmFtZSkKICAgKiBOT1RFOiBZb3UgbWF5IG5lZWQgdG8gZXhlY3V0ZSB0aGlzIGNvZGUgY2h1bmsgKGFuZCB0aGUgY2h1bmtzIGFib3ZlIGl0KSBpbmRpdmlkdWFsbHkgZm9yIGB3cml0ZS50YWJsZSgpYCB0byB3b3JrLgoKCmBgYHtyIGNhY2hlPUZBTFNFfQojIFByZWRpY3QgdGhlIHRlc3QgZGF0YSAoT1VUUFVUUyBMT0ctT0REUykKcmFua2luZ19yZiA8LSBwcmVkaWN0KGZpdC5zdm0sIHRlc3QpCnJhbmtpbmdfcmYgPC0gYXMubnVtZXJpYyhyYW5raW5nX3JmKQoKIyBubyBuZWVkIHRvIGNvbnZlcnQgdG8gMCBhbmQgMSBzaW5jZSByYW5raW5nIG5lZWRlZCBmb3IgQVVDLgp3cml0ZS50YWJsZShyYW5raW5nX3JmLGZpbGUgPSAiY2xhc3NpZmljYXRpb24uY3N2Iiwgcm93Lm5hbWVzPUYsIGNvbC5uYW1lcz1GKQpgYGAKCiMjIFN0b3JpbmcgRmVhdHVyZSBTZWxlY3Rpb24gUmVzdWx0cwoKICAgKiBTdG9yZSB5b3VyIHByZWRpY3Rpb24gZm9yIHRoZSBmZWF0dXJlcy4KICAgICAgKiBUaGlzIHNob3VsZCBiZSAqKmJpbmFyeSoqLCB3aGVyZSAqKjEgbWVhbnMga2VlcCB0aGUgZmVhdHVyZSoqIGFuZCAqKjAgbWVhbnMgZG9uJ3Qga2VlcCBmZWF0dXJlKiouIAogICAgICAqIFRoZSByZXN1bHRzIHdpbGwgYmUgcmFua2VkIGJ5ICoqYmFsYW5jZWQgYWNjdXJhY3kqKi4KICAgKiBUaGVuIHdyaXRlIHRoZSByZXN1bHRzIGludG8gdGhlIENTViBmaWxlIG5hbWVkIGBzZWxlY3Rpb24uY3N2YCAoWW91IF9tdXN0XyB1c2UgdGhpcyBmaWxlbmFtZSkKICAgKiBOT1RFOiBZb3UgbWF5IG5lZWQgdG8gZXhlY3V0ZSB0aGlzIGNvZGUgY2h1bmsgKGFuZCB0aGUgY2h1bmtzIGFib3ZlIGl0KSBpbmRpdmlkdWFsbHkgZm9yIGB3cml0ZS50YWJsZSgpYCB0byB3b3JrLgoKCmBgYHtyIGNhY2hlPUZBTFNFfQojIEhlcmUgaXMgdGhlIG1lYW4gcHJlZGljdGlvbiBmaWxlIGZvciBzdWJtaXNzaW9uIHRvIHRoZSB3ZWJzaXRlIAojIGZlYXR1cmVzIHNob3VsZCBiZSBhIGNvbHVtbiB2ZWN0b3Igb2YgMCdzIGFuZCAxJ3MuIAojIDEgPSBrZWVwIGZlYXR1cmUsIDAgPSBkb24ndApmZWF0dXJlczwtbWF0cml4KDAsbnJvdz0obmNvbCh0cmFpbikpLG5jb2w9MSkKIyBTZXQgdGhlIG9uZXMgd2Ugd2FudCB0byBrZWVwIHRvIDEKZmVhdHVyZXNbdmFyaWFibGUuc2VsZWN0XSA8LSAxCndyaXRlLnRhYmxlKGZlYXR1cmVzLGZpbGUgPSAic2VsZWN0aW9uLmNzdiIsIHJvdy5uYW1lcz1GLCBjb2wubmFtZXM9RikKYGBgCgojIyBaaXBwaW5nIGFuZCBTdWJtaXR0aW5nIFlvdXIgUmVzdWx0cyB0byB0aGUgQ2hhbGxlbmdlCgogICAqIFppcCB5b3VyIGBjbGFzc2lmaWNhdGlvbi5jc3ZgIGFuZCBgc2VsZWN0aW9uLmNzdmAgZmlsZXMgLS0gd2UgbXVzdCB1c2UgdGhlc2UgZXhhY3QgbmFtZXMhIC0tIGludG8gYSBzaW5nbGUgYXJjaGl2ZSB0byBnZW5lcmF0ZSB0aGUgZmlsZSBgTXlFbnRyeS5jc3YuemlwYCB0byBlbnRlciB0aGUgY29udGVzdC4KICAgKiBUaGUgbmFtZSBvZiB5b3VyIHppcCBmaWxlIGlzIG5vdCBpbXBvcnRhbnQsIGJ1dCAqKnNob3VsZCBub3QgaW5jbHVkZSBzcGFjZXMgb3IgY2hhcmFjdGVycyBsaWtlIGAoYCBldGMqKi4gVGhlIGZvbGxvd2luZyBjb2RlIGNyZWF0ZXMgYSB6aXAgZmlsZW5hbWUgdGhhdCB3aWxsIGFsd2F5cyBiZSB1bmlxdWUuIAogICAqIE5PVEU6CiAgICAgICogWW91IG1heSBuZWVkIHRvIGV4ZWN1dGUgdGhpcyBjb2RlIGNodW5rIChhbmQgdGhlIGNodW5rcyBhYm92ZSBpdCkgaW5kaXZpZHVhbGx5IGZvciBgc3lzdGVtKClgIHRvIHdvcmsuCiAgICAgICogVGhpcyBjb2RlIGNyZWF0ZXMgYSB6aXAgd2l0aCBhIGZpbGVuYW1lIGJhc2VkIG9uIHRpbWUgdGhhdCB3aWxsIGFsd2F5cyBiZSB1bmlxdWUuIFRoaXMgd2lsbCByZXN1bHQgaW4gbWFueSB6aXBzIGFjY3VtdWxhdGluZyBpbiB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5IQoKCmBgYHtyIGNhY2hlPUZBTFNFfQojIGdldCB0aW1lCnRpbWUgPC0gIGZvcm1hdChTeXMudGltZSgpLCAiJUglTSVTIikKCnRpbWUgIyB2ZXJpZnkgYSBuZXcgdmFsdWUgZ2VuZXJhdGVkCgojVGhpcyBhdXRvbWF0aWNhbGx5IGdlbmVyYXRlcyBhIGNvbXByZXNzZWQgKHppcCkgZmlsZSAKc3lzdGVtKHBhc3RlMCgiemlwIC11IE15RW50cnktIiwgdGltZSwgIi5jc3YuemlwIGNsYXNzaWZpY2F0aW9uLmNzdiIpKQpzeXN0ZW0ocGFzdGUwKCJ6aXAgLXUgTXlFbnRyeS0iLCB0aW1lLCAiLmNzdi56aXAgc2VsZWN0aW9uLmNzdiIpKQoKcGFzdGUwKCJUaGUgbmFtZSBvZiB5b3VyIGVudHJ5IGZpbGU6IE15RW50cnktIiwgdGltZSwgIi5jc3YuemlwIikKYGBgCgogICAKICAKCg==