Getting Started

First we will import the needed packages. We will use the tidyverse set of packages for code readability and simplicity, along with glmnet for LASSO and Elastic Net.

library(tidyverse)
library('tidymodels')
Registered S3 method overwritten by 'tune':
  method                   from   
  required_pkgs.model_spec parsnip
-- Attaching packages --------------------------------------------------------------------------------------------------------------------- tidymodels 0.1.3 --
v broom        0.7.8      v rsample      0.1.0 
v dials        0.0.9      v tune         0.1.6 
v infer        1.0.0      v workflows    0.2.3 
v modeldata    0.1.1      v workflowsets 0.1.0 
v parsnip      0.1.7      v yardstick    0.0.8 
v recipes      0.1.16     
Warning: package ‘infer’ was built under R version 4.1.1
-- Conflicts ------------------------------------------------------------------------------------------------------------------------ tidymodels_conflicts() --
x scales::discard()        masks purrr::discard()
x dplyr::filter()          masks stats::filter()
x recipes::fixed()         masks stringr::fixed()
x dplyr::lag()             masks stats::lag()
x caret::lift()            masks purrr::lift()
x yardstick::precision()   masks caret::precision()
x yardstick::recall()      masks caret::recall()
x yardstick::sensitivity() masks caret::sensitivity()
x yardstick::spec()        masks readr::spec()
x yardstick::specificity() masks caret::specificity()
x recipes::step()          masks stats::step()
* Use tidymodels_prefer() to resolve common conflicts.

Next, we need to import the dataset for the sessions exercises. We will process it just like we did for Session 1

df = read_csv('../../Data/S1_data.csv.gz')

-- Column specification ---------------------------------------------------------------------------------------------------------------------------------------
cols(
  .default = col_double(),
  Filing = col_character(),
  `date filed_x` = col_character(),
  FYE_x = col_character(),
  restate_filing = col_character(),
  Form = col_character(),
  Date = col_character(),
  loc = col_character()
)
i Use `spec()` for the full column specifications.
train <- df %>% filter(year < 2004)
test  <- df %>% filter(year == 2004)
print(c(nrow(df), nrow(train), nrow(test)))
[1] 14301 11478  2823
BD_eq <- as.formula(paste("sdvol1 ~ ", paste(paste0("Topic_",1:30,"_n_oI"), collapse=" + "), collapse=""))
BCE_eq <- as.formula(paste("Restate_Int_chr ~ logtotasset + rsst_acc + chg_recv + chg_inv +
  soft_assets + pct_chg_cashsales + chg_roa + issuance +
  oplease_dum + book_mkt + lag_sdvol + merger + bigNaudit +
  midNaudit + cffin + exfin + restruct + bullets + headerlen +
  newlines + alltags + processedsize + sentlen_u + wordlen_s +
  paralen_s + repetitious_p + sentlen_s + typetoken +
  clindex + fog + active_p + passive_p + lm_negative_p +
  lm_positive_p + allcaps + exclamationpoints + questionmarks + ",
  paste(paste0("Topic_",1:30,"_n_oI"), collapse=" + "), collapse=""))

SVM

In computer science and many other discplines, a simple type of classifier used in training models is SVM. This is particularly common for algorithms that are being used as supervised methods for predicting some variable in a model.

SVM: Support Vector Machine for Classification

This is the most common approach for these algorithms. The algorithm will allow us to approach a classification problem, and it can either report the most likely class for each observation, or, like with logistic regression, it can report the probability of belonging to a given class.

We will use the implementation from kernlab, though we will call it from caret.

This implementation is rather slow, and as such we are going to only use 3-fold CV in the example code below. You can change the number parameter to increase the number of folds.

recipe_svm <-
  recipe(BCE_eq, data = train)  %>%
  step_zv(all_predictors()) %>% # remove any zero variance predictors
  step_center(all_predictors()) %>%  # Center all prediction variables
  step_scale(all_predictors()) %>%  # Scale all prediction variables
  step_intercept() %>% # Add an intercept to the model
  step_num2factor(all_outcomes(), ordered = T, levels=c("0","1"),
                  transform = function(x) x + 1)  # Convert DV to factor
model_svm <-
  svm_linear(cost = tune()) %>%
  set_mode("classification") %>%
  set_engine("LiblineaR")

workflow_svm <- workflow() %>%
  add_model(model_svm) %>%
  add_recipe(recipe_svm)

folds_svm <- vfold_cv(train, v=10)  # from rsample
metrics_svm = metric_set(roc_auc)  # from yardstick
grid_svm <- expand_grid(cost = exp(seq(-10,0, length.out=10)))

svm_fit_tuned <- tune_grid(workflow_svm,
                             grid = grid_svm,
                             resamples = folds_svm,
                             metrics=metrics_svm)
x Fold01: preprocessor 1/1, model 1/10 (predictions): Error: No prob prediction method available for this model.
* Value for `type` should be one of: 'class', 'raw'
x Fold01: preprocessor 1/1, model 2/10 (predictions): Error: No prob prediction method available for this model.
* Value for `type` should be one of: 'class', 'raw'
x Fold01: preprocessor 1/1, model 3/10 (predictions): Error: No prob prediction method available for this model.
* Value for `type` should be one of: 'class', 'raw'
x Fold01: preprocessor 1/1, model 4/10 (predictions): Error: No prob prediction method available for this model.
* Value for `type` should be one of: 'class', 'raw'
x Fold01: preprocessor 1/1, model 5/10 (predictions): Error: No prob prediction method available for this model.
* Value for `type` should be one of: 'class', 'raw'
x Fold01: preprocessor 1/1, model 6/10 (predictions): Error: No prob prediction method available for this model.
* Value for `type` should be one of: 'class', 'raw'
x Fold01: preprocessor 1/1, model 7/10 (predictions): Error: No prob prediction method available for this model.
* Value for `type` should be one of: 'class', 'raw'
x Fold01: preprocessor 1/1, model 8/10 (predictions): Error: No prob prediction method available for this model.
* Value for `type` should be one of: 'class', 'raw'
x Fold01: preprocessor 1/1, model 9/10 (predictions): Error: No prob prediction method available for this model.
* Value for `type` should be one of: 'class', 'raw'

To see which model was best, we can use the show_best() function.

svm_final <- workflow_svm %>%
  finalize_workflow(
  select_best(svm_fit_tuned, "roc_auc")
) %>%
  fit(train)
 Setting default kernel parameters  
Warning in .local(x, ...) : Variable(s) `' constant. Cannot scale data.
maximum number of iterations reached 0.001801685 0.001785087

Next we can output a prediction just like we did for linear regression, and then use that prediction to obtain our ROC AUC and ROC curve.

print(paste0('ROC AUC: ', auc@y.values[[1]]))
[1] "ROC AUC: 0.695682643896551"
print(paste0('ROC AUC: ', auc_out@y.values[[1]]))
[1] "ROC AUC: 0.554117586010075"

In this case, we see that out-of-sample performance is much lower.

We can also plot out an ROC curve by calculating the True Positive rate and False Positive Rate. We can use ROCR with ggplot to get a nice visualization of this.

Using XGBoost

Note: The general workflow using parsnip with xgboost is the same as with svm. The model name is boost_tree() in this case, however, and of course the parameter list is longer (reference).

For simplicity, the below workflow is only to create a single XGBoost model using the package itself, rather than the tidymodels interface. As such, we will use the juice() function to extract the data in the format needed for XGBoost.

rec <- recipe(BCE_eq, data = train) %>%
  step_zv(all_predictors()) %>%  # Drop any variables with zero variance
  step_center(all_predictors()) %>%  # Center all prediction variables
  step_scale(all_predictors()) %>%  # Scale all prediction variables
  step_intercept()  # Add an intercept to the model

# Juice our data
prepped <- rec %>% prep(training=train)
train_x <- juice(prepped, all_predictors(), composition = "dgCMatrix")
train_y <- juice(prepped, all_outcomes(), composition = "matrix")
test_prepped <- rec %>% prep(training=test)
test_x <- juice(test_prepped, all_predictors(), composition = "dgCMatrix")
test_y <- juice(test_prepped, all_outcomes(), composition = "matrix")

# Cross validation
set.seed(482342)  #for reproducibility
library(xgboost)

Attaching package: ‘xgboost’

The following object is masked from ‘package:dplyr’:

    slice
# model setup
params <- list(max_depth=10,
               eta=0.2,
               gamma=10,
               min_child_weight = 5,
               objective =
                 "binary:logistic")

# run the model
xgbCV <- xgb.cv(params=params,
                data=train_x,
                label=train_y,
                nrounds=100,
                eval_metric="auc",
                nfold=10,
                stratified=TRUE)
[1] train-auc:0.500000+0.000000 test-auc:0.500000+0.000000 
[2] train-auc:0.500000+0.000000 test-auc:0.500000+0.000000 
[3] train-auc:0.500000+0.000000 test-auc:0.500000+0.000000 
[4] train-auc:0.500000+0.000000 test-auc:0.500000+0.000000 
[5] train-auc:0.500000+0.000000 test-auc:0.500000+0.000000 
[6] train-auc:0.500000+0.000000 test-auc:0.500000+0.000000 
[7] train-auc:0.500000+0.000000 test-auc:0.500000+0.000000 
[8] train-auc:0.512896+0.038689 test-auc:0.496177+0.011467 
[9] train-auc:0.560039+0.060273 test-auc:0.516633+0.044405 
[10]    train-auc:0.596276+0.048602 test-auc:0.562167+0.076553 
[11]    train-auc:0.618009+0.011237 test-auc:0.601066+0.067260 
[12]    train-auc:0.643567+0.019062 test-auc:0.608147+0.060943 
[13]    train-auc:0.646723+0.021138 test-auc:0.613355+0.065557 
[14]    train-auc:0.669839+0.014928 test-auc:0.633844+0.070390 
[15]    train-auc:0.683102+0.018207 test-auc:0.637254+0.069544 
[16]    train-auc:0.708800+0.025621 test-auc:0.639027+0.082525 
[17]    train-auc:0.726156+0.018551 test-auc:0.644384+0.083992 
[18]    train-auc:0.749755+0.031893 test-auc:0.653366+0.090486 
[19]    train-auc:0.767492+0.033702 test-auc:0.661573+0.085642 
[20]    train-auc:0.774949+0.030019 test-auc:0.664315+0.088128 
[21]    train-auc:0.788037+0.021650 test-auc:0.663974+0.092046 
[22]    train-auc:0.801918+0.023933 test-auc:0.665885+0.098105 
[23]    train-auc:0.813597+0.022498 test-auc:0.673366+0.102972 
[24]    train-auc:0.826128+0.017047 test-auc:0.680494+0.105595 
[25]    train-auc:0.835805+0.018088 test-auc:0.674278+0.103718 
[26]    train-auc:0.843025+0.018110 test-auc:0.683315+0.073458 
[27]    train-auc:0.851160+0.016997 test-auc:0.681916+0.076628 
[28]    train-auc:0.855128+0.018839 test-auc:0.679028+0.064542 
[29]    train-auc:0.861913+0.021880 test-auc:0.678984+0.070448 
[30]    train-auc:0.866917+0.021857 test-auc:0.674863+0.077988 
[31]    train-auc:0.876251+0.019530 test-auc:0.679354+0.067319 
[32]    train-auc:0.881639+0.019588 test-auc:0.680695+0.065898 
[33]    train-auc:0.885488+0.018508 test-auc:0.683177+0.061797 
[34]    train-auc:0.888280+0.019101 test-auc:0.682871+0.059875 
[35]    train-auc:0.892049+0.020314 test-auc:0.680254+0.061654 
[36]    train-auc:0.894742+0.022124 test-auc:0.680570+0.061335 
[37]    train-auc:0.898266+0.021981 test-auc:0.682069+0.065854 
[38]    train-auc:0.898684+0.021750 test-auc:0.682828+0.065218 
[39]    train-auc:0.901598+0.021755 test-auc:0.680384+0.069263 
[40]    train-auc:0.902870+0.022647 test-auc:0.679353+0.067703 
[41]    train-auc:0.905980+0.024111 test-auc:0.680171+0.070536 
[42]    train-auc:0.908842+0.025752 test-auc:0.677667+0.074860 
[43]    train-auc:0.910068+0.026359 test-auc:0.681808+0.070568 
[44]    train-auc:0.913474+0.028733 test-auc:0.681250+0.066556 
[45]    train-auc:0.914379+0.029492 test-auc:0.678519+0.070892 
[46]    train-auc:0.914952+0.030008 test-auc:0.679641+0.069695 
[47]    train-auc:0.915599+0.030600 test-auc:0.680817+0.069873 
[48]    train-auc:0.915834+0.030842 test-auc:0.681183+0.069732 
[49]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[50]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[51]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[52]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[53]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[54]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[55]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[56]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[57]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[58]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[59]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[60]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[61]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[62]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[63]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[64]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[65]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[66]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[67]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[68]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[69]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[70]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[71]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[72]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[73]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[74]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[75]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[76]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[77]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[78]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[79]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[80]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[81]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[82]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[83]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[84]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[85]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[86]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[87]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[88]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[89]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[90]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[91]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[92]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[93]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[94]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[95]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[96]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[97]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[98]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[99]    train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 
[100]   train-auc:0.916752+0.031916 test-auc:0.682152+0.069441 

Next, we re-run the optimal model.

numTrees <- min(
 which(
  xgbCV$evaluation_log$test_auc_mean == 
  max(xgbCV$evaluation_log$test_auc_mean)
 )
)

fit4 <- xgboost(params=params,
                data = train_x,
                label = train_y,
                nrounds = numTrees,
                eval_metric="auc")
[1] train-auc:0.500000 
[2] train-auc:0.500000 
[3] train-auc:0.500000 
[4] train-auc:0.500000 
[5] train-auc:0.500000 
[6] train-auc:0.500000 
[7] train-auc:0.500000 
[8] train-auc:0.500000 
[9] train-auc:0.617282 
[10]    train-auc:0.617282 
[11]    train-auc:0.623066 
[12]    train-auc:0.657007 
[13]    train-auc:0.657007 
[14]    train-auc:0.658862 
[15]    train-auc:0.663206 
[16]    train-auc:0.731354 
[17]    train-auc:0.731541 
[18]    train-auc:0.739063 
[19]    train-auc:0.754038 
[20]    train-auc:0.760430 
[21]    train-auc:0.771918 
[22]    train-auc:0.807310 
[23]    train-auc:0.809272 
[24]    train-auc:0.829148 
[25]    train-auc:0.837454 
[26]    train-auc:0.855574 

We can output an importance plot easily.

xgb.train.data = xgb.DMatrix(train_x, label = train_y, missing = NA)
col_names = attr(xgb.train.data, ".Dimnames")[[2]]
imp = xgb.importance(col_names, fit4)
# Variable importance
xgb.plot.importance(imp)

We can also calculate out-of-sample AUC.

pred.xgb <- predict(fit4, test_x, type="response")
ROCpred.xgb <- prediction(as.numeric(pred.xgb), as.numeric(test_y))
ROCperf.xgb <- performance(ROCpred.xgb, 'tpr','fpr')
#plot(ROCperf.xgb)
df_ROC.xgb <- data.frame(FalsePositive=c(ROCperf.xgb@x.values[[1]]),
                 TruePositive=c(ROCperf.xgb@y.values[[1]]))

ggplot() +
  geom_line(data=df_ROC.xgb, aes(x=FalsePositive, y=TruePositive, color="Out of Sample")) + 
  geom_abline(slope=1)


auc.xgb <- performance(ROCpred.xgb, measure = "auc")
auc <- auc.xgb@y.values[[1]]
names(auc) <- c("XGBoost AUC")
auc
XGBoost AUC 
  0.6326631 
LS0tDQp0aXRsZTogIlNlc3Npb24gMjogQ2xhc3NpZmljYXRpb24iDQphdXRob3I6ICJEci4gUmljaGFyZCBNLiBDcm93bGV5Ig0KZGF0ZTogIiINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMgR2V0dGluZyBTdGFydGVkDQoNCkZpcnN0IHdlIHdpbGwgaW1wb3J0IHRoZSBuZWVkZWQgcGFja2FnZXMuICBXZSB3aWxsIHVzZSB0aGUgYHRpZHl2ZXJzZWAgc2V0IG9mIHBhY2thZ2VzIGZvciBjb2RlIHJlYWRhYmlsaXR5IGFuZCBzaW1wbGljaXR5LCBhbG9uZyB3aXRoIGBnbG1uZXRgIGZvciBMQVNTTyBhbmQgRWxhc3RpYyBOZXQuDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHRpZHltb2RlbHMpDQpsaWJyYXJ5KFJPQ1IpDQpgYGANCg0KTmV4dCwgd2UgbmVlZCB0byBpbXBvcnQgdGhlIGRhdGFzZXQgZm9yIHRoZSBzZXNzaW9ucyBleGVyY2lzZXMuICBXZSB3aWxsIHByb2Nlc3MgaXQganVzdCBsaWtlIHdlIGRpZCBmb3IgU2Vzc2lvbiAxDQoNCmBgYHtyfQ0KZGYgPSByZWFkX2NzdignLi4vLi4vRGF0YS9TMV9kYXRhLmNzdi5neicpDQp0cmFpbiA8LSBkZiAlPiUgZmlsdGVyKHllYXIgPCAyMDA0KQ0KdGVzdCAgPC0gZGYgJT4lIGZpbHRlcih5ZWFyID09IDIwMDQpDQpwcmludChjKG5yb3coZGYpLCBucm93KHRyYWluKSwgbnJvdyh0ZXN0KSkpDQpCRF9lcSA8LSBhcy5mb3JtdWxhKHBhc3RlKCJzZHZvbDEgfiAiLCBwYXN0ZShwYXN0ZTAoIlRvcGljXyIsMTozMCwiX25fb0kiKSwgY29sbGFwc2U9IiArICIpLCBjb2xsYXBzZT0iIikpDQpCQ0VfZXEgPC0gYXMuZm9ybXVsYShwYXN0ZSgiUmVzdGF0ZV9JbnQgfiBsb2d0b3Rhc3NldCArIHJzc3RfYWNjICsgY2hnX3JlY3YgKyBjaGdfaW52ICsNCiAgc29mdF9hc3NldHMgKyBwY3RfY2hnX2Nhc2hzYWxlcyArIGNoZ19yb2EgKyBpc3N1YW5jZSArDQogIG9wbGVhc2VfZHVtICsgYm9va19ta3QgKyBsYWdfc2R2b2wgKyBtZXJnZXIgKyBiaWdOYXVkaXQgKw0KICBtaWROYXVkaXQgKyBjZmZpbiArIGV4ZmluICsgcmVzdHJ1Y3QgKyBidWxsZXRzICsgaGVhZGVybGVuICsNCiAgbmV3bGluZXMgKyBhbGx0YWdzICsgcHJvY2Vzc2Vkc2l6ZSArIHNlbnRsZW5fdSArIHdvcmRsZW5fcyArDQogIHBhcmFsZW5fcyArIHJlcGV0aXRpb3VzX3AgKyBzZW50bGVuX3MgKyB0eXBldG9rZW4gKw0KICBjbGluZGV4ICsgZm9nICsgYWN0aXZlX3AgKyBwYXNzaXZlX3AgKyBsbV9uZWdhdGl2ZV9wICsNCiAgbG1fcG9zaXRpdmVfcCArIGFsbGNhcHMgKyBleGNsYW1hdGlvbnBvaW50cyArIHF1ZXN0aW9ubWFya3MgKyAiLA0KICBwYXN0ZShwYXN0ZTAoIlRvcGljXyIsMTozMCwiX25fb0kiKSwgY29sbGFwc2U9IiArICIpLCBjb2xsYXBzZT0iIikpDQpgYGANCg0KIyMgU1ZNDQoNCkluIGNvbXB1dGVyIHNjaWVuY2UgYW5kIG1hbnkgb3RoZXIgZGlzY3BsaW5lcywgYSBzaW1wbGUgdHlwZSBvZiBjbGFzc2lmaWVyIHVzZWQgaW4gdHJhaW5pbmcgbW9kZWxzIGlzIFNWTS4gIFRoaXMgaXMgcGFydGljdWxhcmx5IGNvbW1vbiBmb3IgYWxnb3JpdGhtcyB0aGF0IGFyZSBiZWluZyB1c2VkIGFzIHN1cGVydmlzZWQgbWV0aG9kcyBmb3IgcHJlZGljdGluZyBzb21lIHZhcmlhYmxlIGluIGEgbW9kZWwuDQoNCiMjIyBTVk06IFN1cHBvcnQgVmVjdG9yIE1hY2hpbmUgZm9yIENsYXNzaWZpY2F0aW9uDQoNClRoaXMgaXMgdGhlIG1vc3QgY29tbW9uIGFwcHJvYWNoIGZvciB0aGVzZSBhbGdvcml0aG1zLiAgVGhlIGFsZ29yaXRobSB3aWxsIGFsbG93IHVzIHRvIGFwcHJvYWNoIGEgY2xhc3NpZmljYXRpb24gcHJvYmxlbSwgYW5kIGl0IGNhbiBlaXRoZXIgcmVwb3J0IHRoZSBtb3N0IGxpa2VseSBjbGFzcyBmb3IgZWFjaCBvYnNlcnZhdGlvbiwgb3IsIGxpa2Ugd2l0aCBsb2dpc3RpYyByZWdyZXNzaW9uLCBpdCBjYW4gcmVwb3J0IHRoZSBwcm9iYWJpbGl0eSBvZiBiZWxvbmdpbmcgdG8gYSBnaXZlbiBjbGFzcy4NCg0KV2Ugd2lsbCB1c2UgdGhlIGltcGxlbWVudGF0aW9uIGZyb20gW2tlcm5sYWJdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9rZXJubGFiL2luZGV4Lmh0bWwpLCB0aG91Z2ggd2Ugd2lsbCBjYWxsIGl0IGZyb20gW2NhcmV0XShodHRwczovL3RvcGVwby5naXRodWIuaW8vY2FyZXQvKS4NCg0KVGhpcyBpbXBsZW1lbnRhdGlvbiBpcyByYXRoZXIgc2xvdywgYW5kIGFzIHN1Y2ggd2UgYXJlIGdvaW5nIHRvIG9ubHkgdXNlIDMtZm9sZCBDViBpbiB0aGUgZXhhbXBsZSBjb2RlIGJlbG93LiAgWW91IGNhbiBjaGFuZ2UgdGhlIGBudW1iZXJgIHBhcmFtZXRlciB0byBpbmNyZWFzZSB0aGUgbnVtYmVyIG9mIGZvbGRzLg0KDQpgYGB7cn0NCiMgRm9yIHBhcmFsbGVsIHByb2Nlc3NpbmcNCmRvUGFyYWxsZWw6OnJlZ2lzdGVyRG9QYXJhbGxlbCgpDQoNCnJlY2lwZV9zdm0gPC0NCiAgcmVjaXBlKEJDRV9lcSwgZGF0YSA9IHRyYWluKSAgJT4lDQogIHN0ZXBfenYoYWxsX3ByZWRpY3RvcnMoKSkgJT4lICMgcmVtb3ZlIGFueSB6ZXJvIHZhcmlhbmNlIHByZWRpY3RvcnMNCiAgc3RlcF9jZW50ZXIoYWxsX3ByZWRpY3RvcnMoKSkgJT4lICAjIENlbnRlciBhbGwgcHJlZGljdGlvbiB2YXJpYWJsZXMNCiAgc3RlcF9zY2FsZShhbGxfcHJlZGljdG9ycygpKSAlPiUgICMgU2NhbGUgYWxsIHByZWRpY3Rpb24gdmFyaWFibGVzDQogIHN0ZXBfaW50ZXJjZXB0KCkgJT4lICMgQWRkIGFuIGludGVyY2VwdCB0byB0aGUgbW9kZWwNCiAgc3RlcF9udW0yZmFjdG9yKGFsbF9vdXRjb21lcygpLCBvcmRlcmVkID0gVCwgbGV2ZWxzPWMoIjAiLCIxIiksDQogICAgICAgICAgICAgICAgICB0cmFuc2Zvcm0gPSBmdW5jdGlvbih4KSB4ICsgMSwgc2tpcCA9IFRSVUUpICAjIENvbnZlcnQgRFYgdG8gZmFjdG9yDQptb2RlbF9zdm0gPC0NCiAgc3ZtX2xpbmVhcihjb3N0ID0gdHVuZSgpKSAlPiUNCiAgc2V0X21vZGUoImNsYXNzaWZpY2F0aW9uIikgJT4lDQogIHNldF9lbmdpbmUoImtlcm5sYWIiKQ0KDQp3b3JrZmxvd19zdm0gPC0gd29ya2Zsb3coKSAlPiUNCiAgYWRkX21vZGVsKG1vZGVsX3N2bSkgJT4lDQogIGFkZF9yZWNpcGUocmVjaXBlX3N2bSkNCg0KZm9sZHNfc3ZtIDwtIHZmb2xkX2N2KHRyYWluLCB2PTEwKSAgIyBmcm9tIHJzYW1wbGUNCm1ldHJpY3Nfc3ZtID0gbWV0cmljX3NldChyb2NfYXVjKSAgIyBmcm9tIHlhcmRzdGljaw0KZ3JpZF9zdm0gPC0gZXhwYW5kX2dyaWQoY29zdCA9IGV4cChzZXEoLTEwLDAsIGxlbmd0aC5vdXQ9MTApKSkNCg0Kc3ZtX2ZpdF90dW5lZCA8LSB0dW5lX2dyaWQod29ya2Zsb3dfc3ZtLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JpZCA9IGdyaWRfc3ZtLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzYW1wbGVzID0gZm9sZHNfc3ZtLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0cmljcz1tZXRyaWNzX3N2bSkNCmBgYA0KDQpUbyBzZWUgd2hpY2ggbW9kZWwgd2FzIGJlc3QsIHdlIGNhbiB1c2UgdGhlIGBzaG93X2Jlc3QoKWAgZnVuY3Rpb24uDQoNCmBgYHtyfQ0Kc2hvd19iZXN0KHN2bV9maXRfdHVuZWQsIG1ldHJpYyA9ICJyb2NfYXVjIikNCmBgYA0KDQpgYGB7cn0NCnN2bV9maW5hbCA8LSB3b3JrZmxvd19zdm0gJT4lDQogIGZpbmFsaXplX3dvcmtmbG93KA0KICBzZWxlY3RfYmVzdChzdm1fZml0X3R1bmVkLCAicm9jX2F1YyIpDQopICU+JQ0KICBmaXQodHJhaW4pDQpgYGANCg0KDQoNCg0KTmV4dCB3ZSBjYW4gb3V0cHV0IGEgcHJlZGljdGlvbiBqdXN0IGxpa2Ugd2UgZGlkIGZvciBsaW5lYXIgcmVncmVzc2lvbiwgYW5kIHRoZW4gdXNlIHRoYXQgcHJlZGljdGlvbiB0byBvYnRhaW4gb3VyIFJPQyBBVUMgYW5kIFJPQyBjdXJ2ZS4NCg0KYGBge3J9DQojIExvZ2l0LCBpbi1zYW1wbGUNCllfaGF0X3RyYWluIDwtIHByZWRpY3Qoc3ZtX2ZpbmFsLCB0cmFpbiwgdHlwZT0icHJvYiIpDQpST0NwcmVkIDwtIHByZWRpY3Rpb24oWV9oYXRfdHJhaW4kLnByZWRfMSwgdHJhaW4kUmVzdGF0ZV9JbnQpDQojUk9DcHJlZF9vdXQgPC0gcHJlZGljdGlvbihhcy5udW1lcmljKHByZWRbZGYkVGVzdD09MSAmICFpcy5uYShwcmVkKV0pLCBhcy5udW1lcmljKGRmW2RmJFRlc3Q9PTEgJiAhaXMubmEocHJlZCksXSRBQUVSKSkNCg0KYXVjIDwtIHBlcmZvcm1hbmNlKFJPQ3ByZWQsIG1lYXN1cmUgPSAiYXVjIikNCg0KcHJpbnQocGFzdGUwKCdST0MgQVVDOiAnLCBhdWNAeS52YWx1ZXNbWzFdXSkpDQpgYGANCg0KYGBge3J9DQojIExvZ2l0LCBvdXQtb2Ytc2FtcGxlDQpZX2hhdF90ZXN0IDwtIHByZWRpY3Qoc3ZtX2ZpbmFsLCB0ZXN0LCB0eXBlPSJwcm9iIikNClJPQ3ByZWRfb3V0IDwtIHByZWRpY3Rpb24oWV9oYXRfdGVzdCQucHJlZF8xLCB0ZXN0JFJlc3RhdGVfSW50KQ0KDQphdWNfb3V0IDwtIHBlcmZvcm1hbmNlKFJPQ3ByZWRfb3V0LCBtZWFzdXJlID0gImF1YyIpDQoNCnByaW50KHBhc3RlMCgnUk9DIEFVQzogJywgYXVjX291dEB5LnZhbHVlc1tbMV1dKSkNCmBgYA0KDQpJbiB0aGlzIGNhc2UsIHdlIHNlZSB0aGF0IG91dC1vZi1zYW1wbGUgcGVyZm9ybWFuY2UgaXMgbXVjaCBsb3dlci4NCg0KV2UgY2FuIGFsc28gcGxvdCBvdXQgYW4gUk9DIGN1cnZlIGJ5IGNhbGN1bGF0aW5nIHRoZSBUcnVlIFBvc2l0aXZlIHJhdGUgYW5kIEZhbHNlIFBvc2l0aXZlIFJhdGUuICBXZSBjYW4gdXNlIGBST0NSYCB3aXRoIGBnZ3Bsb3RgIHRvIGdldCBhIG5pY2UgdmlzdWFsaXphdGlvbiBvZiB0aGlzLg0KDQpgYGB7cn0NClJPQ3BlcmYgPC0gcGVyZm9ybWFuY2UoUk9DcHJlZCwgJ3RwcicsJ2ZwcicpDQpST0NwZXJmX291dCA8LSBwZXJmb3JtYW5jZShST0NwcmVkX291dCwgJ3RwcicsJ2ZwcicpDQoNCmRmX3JvY19pbiA8LSBkYXRhLmZyYW1lKEZhbHNlUG9zaXRpdmU9YyhST0NwZXJmQHgudmFsdWVzW1sxXV0pLA0KICAgICAgICAgICAgICAgICBUcnVlUG9zaXRpdmU9YyhST0NwZXJmQHkudmFsdWVzW1sxXV0pKQ0KZGZfcm9jX291dCA8LSBkYXRhLmZyYW1lKEZhbHNlUG9zaXRpdmU9YyhST0NwZXJmX291dEB4LnZhbHVlc1tbMV1dKSwNCiAgICAgICAgICAgICAgICAgVHJ1ZVBvc2l0aXZlPWMoUk9DcGVyZl9vdXRAeS52YWx1ZXNbWzFdXSkpDQoNCmdncGxvdCgpICsNCiAgZ2VvbV9saW5lKGRhdGE9ZGZfcm9jX2luLCBhZXMoeD1GYWxzZVBvc2l0aXZlLCB5PVRydWVQb3NpdGl2ZSwgY29sb3I9IkluIFNhbXBsZSIpKSArDQogIGdlb21fbGluZShkYXRhPWRmX3JvY19vdXQsIGFlcyh4PUZhbHNlUG9zaXRpdmUsIHk9VHJ1ZVBvc2l0aXZlLCBjb2xvcj0iT3V0IG9mIFNhbXBsZSIpKSArIA0KICBnZW9tX2FibGluZShzbG9wZT0xKQ0KYGBgDQoNCiMgVXNpbmcgWEdCb29zdA0KDQpOb3RlOiBUaGUgZ2VuZXJhbCB3b3JrZmxvdyB1c2luZyBwYXJzbmlwIHdpdGggeGdib29zdCBpcyB0aGUgc2FtZSBhcyB3aXRoIHN2bS4gIFRoZSBtb2RlbCBuYW1lIGlzIGBib29zdF90cmVlKClgIGluIHRoaXMgY2FzZSwgaG93ZXZlciwgYW5kIG9mIGNvdXJzZSB0aGUgcGFyYW1ldGVyIGxpc3QgaXMgbG9uZ2VyIChbcmVmZXJlbmNlXShodHRwczovL3BhcnNuaXAudGlkeW1vZGVscy5vcmcvcmVmZXJlbmNlL2Jvb3N0X3RyZWUuaHRtbCkpLg0KDQpGb3Igc2ltcGxpY2l0eSwgdGhlIGJlbG93IHdvcmtmbG93IGlzIG9ubHkgdG8gY3JlYXRlIGEgc2luZ2xlIFhHQm9vc3QgbW9kZWwgdXNpbmcgdGhlIHBhY2thZ2UgaXRzZWxmLCByYXRoZXIgdGhhbiB0aGUgYHRpZHltb2RlbHNgIGludGVyZmFjZS4gIEFzIHN1Y2gsIHdlIHdpbGwgdXNlIHRoZSBganVpY2UoKWAgZnVuY3Rpb24gdG8gZXh0cmFjdCB0aGUgZGF0YSBpbiB0aGUgZm9ybWF0IG5lZWRlZCBmb3IgWEdCb29zdC4NCg0KYGBge3J9DQpyZWMgPC0gcmVjaXBlKEJDRV9lcSwgZGF0YSA9IHRyYWluKSAlPiUNCiAgc3RlcF96dihhbGxfcHJlZGljdG9ycygpKSAlPiUgICMgRHJvcCBhbnkgdmFyaWFibGVzIHdpdGggemVybyB2YXJpYW5jZQ0KICBzdGVwX2NlbnRlcihhbGxfcHJlZGljdG9ycygpKSAlPiUgICMgQ2VudGVyIGFsbCBwcmVkaWN0aW9uIHZhcmlhYmxlcw0KICBzdGVwX3NjYWxlKGFsbF9wcmVkaWN0b3JzKCkpICU+JSAgIyBTY2FsZSBhbGwgcHJlZGljdGlvbiB2YXJpYWJsZXMNCiAgc3RlcF9pbnRlcmNlcHQoKSAgIyBBZGQgYW4gaW50ZXJjZXB0IHRvIHRoZSBtb2RlbA0KDQojIEp1aWNlIG91ciBkYXRhDQpwcmVwcGVkIDwtIHJlYyAlPiUgcHJlcCh0cmFpbmluZz10cmFpbikNCnRyYWluX3ggPC0ganVpY2UocHJlcHBlZCwgYWxsX3ByZWRpY3RvcnMoKSwgY29tcG9zaXRpb24gPSAiZGdDTWF0cml4IikNCnRyYWluX3kgPC0ganVpY2UocHJlcHBlZCwgYWxsX291dGNvbWVzKCksIGNvbXBvc2l0aW9uID0gIm1hdHJpeCIpDQp0ZXN0X3ByZXBwZWQgPC0gcmVjICU+JSBwcmVwKHRyYWluaW5nPXRlc3QpDQp0ZXN0X3ggPC0ganVpY2UodGVzdF9wcmVwcGVkLCBhbGxfcHJlZGljdG9ycygpLCBjb21wb3NpdGlvbiA9ICJkZ0NNYXRyaXgiKQ0KdGVzdF95IDwtIGp1aWNlKHRlc3RfcHJlcHBlZCwgYWxsX291dGNvbWVzKCksIGNvbXBvc2l0aW9uID0gIm1hdHJpeCIpDQoNCiMgQ3Jvc3MgdmFsaWRhdGlvbg0Kc2V0LnNlZWQoNDgyMzQyKSAgI2ZvciByZXByb2R1Y2liaWxpdHkNCmxpYnJhcnkoeGdib29zdCkNCg0KIyBtb2RlbCBzZXR1cA0KcGFyYW1zIDwtIGxpc3QobWF4X2RlcHRoPTEwLA0KICAgICAgICAgICAgICAgZXRhPTAuMiwNCiAgICAgICAgICAgICAgIGdhbW1hPTEwLA0KICAgICAgICAgICAgICAgbWluX2NoaWxkX3dlaWdodCA9IDUsDQogICAgICAgICAgICAgICBvYmplY3RpdmUgPQ0KICAgICAgICAgICAgICAgICAiYmluYXJ5OmxvZ2lzdGljIikNCg0KIyBydW4gdGhlIG1vZGVsDQp4Z2JDViA8LSB4Z2IuY3YocGFyYW1zPXBhcmFtcywNCiAgICAgICAgICAgICAgICBkYXRhPXRyYWluX3gsDQogICAgICAgICAgICAgICAgbGFiZWw9dHJhaW5feSwNCiAgICAgICAgICAgICAgICBucm91bmRzPTEwMCwNCiAgICAgICAgICAgICAgICBldmFsX21ldHJpYz0iYXVjIiwNCiAgICAgICAgICAgICAgICBuZm9sZD0xMCwNCiAgICAgICAgICAgICAgICBzdHJhdGlmaWVkPVRSVUUpDQpgYGANCg0KTmV4dCwgd2UgcmUtcnVuIHRoZSBvcHRpbWFsIG1vZGVsLg0KDQpgYGB7cn0NCm51bVRyZWVzIDwtIG1pbigNCiB3aGljaCgNCiAgeGdiQ1YkZXZhbHVhdGlvbl9sb2ckdGVzdF9hdWNfbWVhbiA9PSANCiAgbWF4KHhnYkNWJGV2YWx1YXRpb25fbG9nJHRlc3RfYXVjX21lYW4pDQogKQ0KKQ0KDQpmaXQ0IDwtIHhnYm9vc3QocGFyYW1zPXBhcmFtcywNCiAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5feCwNCiAgICAgICAgICAgICAgICBsYWJlbCA9IHRyYWluX3ksDQogICAgICAgICAgICAgICAgbnJvdW5kcyA9IG51bVRyZWVzLA0KICAgICAgICAgICAgICAgIGV2YWxfbWV0cmljPSJhdWMiKQ0KYGBgDQoNCldlIGNhbiBvdXRwdXQgYW4gaW1wb3J0YW5jZSBwbG90IGVhc2lseS4NCg0KYGBge3J9DQp4Z2IudHJhaW4uZGF0YSA9IHhnYi5ETWF0cml4KHRyYWluX3gsIGxhYmVsID0gdHJhaW5feSwgbWlzc2luZyA9IE5BKQ0KY29sX25hbWVzID0gYXR0cih4Z2IudHJhaW4uZGF0YSwgIi5EaW1uYW1lcyIpW1syXV0NCmltcCA9IHhnYi5pbXBvcnRhbmNlKGNvbF9uYW1lcywgZml0NCkNCiMgVmFyaWFibGUgaW1wb3J0YW5jZQ0KeGdiLnBsb3QuaW1wb3J0YW5jZShpbXApDQpgYGANCg0KV2UgY2FuIGFsc28gY2FsY3VsYXRlIG91dC1vZi1zYW1wbGUgQVVDLg0KDQpgYGB7cn0NCnByZWQueGdiIDwtIHByZWRpY3QoZml0NCwgdGVzdF94LCB0eXBlPSJyZXNwb25zZSIpDQpST0NwcmVkLnhnYiA8LSBwcmVkaWN0aW9uKGFzLm51bWVyaWMocHJlZC54Z2IpLCBhcy5udW1lcmljKHRlc3RfeSkpDQpST0NwZXJmLnhnYiA8LSBwZXJmb3JtYW5jZShST0NwcmVkLnhnYiwgJ3RwcicsJ2ZwcicpDQojcGxvdChST0NwZXJmLnhnYikNCmRmX1JPQy54Z2IgPC0gZGF0YS5mcmFtZShGYWxzZVBvc2l0aXZlPWMoUk9DcGVyZi54Z2JAeC52YWx1ZXNbWzFdXSksDQogICAgICAgICAgICAgICAgIFRydWVQb3NpdGl2ZT1jKFJPQ3BlcmYueGdiQHkudmFsdWVzW1sxXV0pKQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fbGluZShkYXRhPWRmX1JPQy54Z2IsIGFlcyh4PUZhbHNlUG9zaXRpdmUsIHk9VHJ1ZVBvc2l0aXZlLCBjb2xvcj0iT3V0IG9mIFNhbXBsZSIpKSArIA0KICBnZW9tX2FibGluZShzbG9wZT0xKQ0KDQphdWMueGdiIDwtIHBlcmZvcm1hbmNlKFJPQ3ByZWQueGdiLCBtZWFzdXJlID0gImF1YyIpDQphdWMgPC0gYXVjLnhnYkB5LnZhbHVlc1tbMV1dDQpuYW1lcyhhdWMpIDwtIGMoIlhHQm9vc3QgQVVDIikNCmF1Yw0KYGBgDQoNCg0KDQoNCg0KDQoNCg0K