Instructions

Consider the the Cervical Cancer (Risk Factors) data set (available from UCI repository) and try to accurately classify Dx.Cancer.

You must compare different approaches and parameters of a) single decision tree, b) random forest.

Evaluation of derived models should follow a correct methodology, comparing different estimates of generalization error (i.e. holdout, cross-validation, bootstrap, …)

Submit a report (in PDF, generated from R) with the code and the resulting analysis.

The Dataset

The dataset comprises of 36 variables, being 24 categorical and 12 continuous variables. The dataset was collected at ‘Hospital Universitario de Caracas’ in Caracas, Venezuela. The dataset comprises demographic information, habits, and historic medical records of 858 patients. Several patients decided not to answer some of the questions because of privacy concerns (missing values), mainly use of contraceptives, IUD and concerning the STDs.

The Exercise

The objective of this exercise is to attemtp to classify the variable Dx:Cancer using some available algorithms and compare them.

There are several algorithms for that we may use:

Here is a “brief” example of assumptions that we may take in account for each method:

All the Annoying Assumptions, from Dip Ranjan Chatterjee

Regression or Classification?

First, we need to explain what is Regression and what is Classification, since this often causes confusion:

  • Both are under the same umbrella of supervised machine learning.
  • Both utilize a known dataset (training dataset) to make predictions.
  • The main difference is the output: for regression is numerical (or continuous) while for classification is categorical (or discrete).

In this exercise, we have a classification problem, that will attempt to classify for true (has cancer) or false (doesn’t have cancer), having no other class involved.

So, the current exercise will use the following classification methods:

  1. Decision tree
  2. Random forest

Decision Tree

First, let’s create a partition with 80% of the data:

Than, let’s create a model with Gini impurity index and Information Gain:

Both algorithms find that dx_hpv is a strong predictor for dx_cancer.

This seems a little odd (not impossible), let’s take a look in the dataset:

Using the nearZeroVar from caret we see that there are several variables that contains near-zero variance predictors (i.e.: variables that take an unique value across samples).

We don’t want to simply remove these variables. So we will try another approach: subsampling.

Subsampling

Subsampling tries to balance a class-unbalanced dataset. We don’t want to artificially balance the test set, so we will take the training set and, before model fitting, and sample the data. There are two issues with this approach according to caret package help pages:

The author also suggests as an alternative, to include the subsampling inside of the usual resampling procedure (cross-validation, bootstrap, …), in exchange of computing time.

There we will try two algorithms, down-sampling and up-sampling. The latter is know to be more optimistic, but since we have so few cases, it seems the more interesting for us.

Random Forest and evaluations

First we will compare both models using the down-sampled training data:


Call:
summary.resamples(object = results)

Models: rpart, rf 
Number of resamples: 150 

ROC 
           Min. 1st Qu. Median      Mean 3rd Qu. Max. NA's
rpart 0.6666667       1      1 0.9655556       1    1    0
rf    1.0000000       1      1 1.0000000       1    1    0

Sens 
           Min. 1st Qu. Median      Mean 3rd Qu. Max. NA's
rpart 0.3333333       1      1 0.9311111       1    1    0
rf    0.3333333       1      1 0.9311111       1    1    0

Spec 
      Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
rpart    1       1      1    1       1    1    0
rf       1       1      1    1       1    1    0
Setting levels: control = 1, case = 2
Setting direction: controls < cases

Setting levels: control = 1, case = 2
Setting direction: controls < cases


Call:
summary.resamples(object = results)

Models: rpart, rf 
Number of resamples: 150 

ROC 
           Min.   1st Qu.    Median      Mean   3rd Qu. Max. NA's
rpart 0.9948207 0.9990811 0.9996658 0.9988108 0.9999095    1    0
rf    1.0000000 1.0000000 1.0000000 1.0000000 1.0000000    1    0

Sens 
      Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
rpart    1       1      1    1       1    1    0
rf       1       1      1    1       1    1    0

Spec 
           Min.   1st Qu.    Median      Mean   3rd Qu. Max. NA's
rpart 0.9701493 0.9850746 0.9925373 0.9896020 0.9925373    1    0
rf    0.9850746 1.0000000 1.0000000 0.9979602 1.0000000    1    0
Setting levels: control = 1, case = 2
Setting direction: controls < cases

Setting levels: control = 1, case = 2
Setting direction: controls < cases

Validation

Now it is time to validate the models using the test set we created from the original dataset:

Confusion Matrix and Statistics

          Reference
Prediction yes  no
       yes   3   4
       no    0 164
                                          
               Accuracy : 0.9766          
                 95% CI : (0.9412, 0.9936)
    No Information Rate : 0.9825          
    P-Value [Acc > NIR] : 0.8168          
                                          
                  Kappa : 0.5899          
                                          
 Mcnemar's Test P-Value : 0.1336          
                                          
            Sensitivity : 1.00000         
            Specificity : 0.97619         
         Pos Pred Value : 0.42857         
         Neg Pred Value : 1.00000         
             Prevalence : 0.01754         
         Detection Rate : 0.01754         
   Detection Prevalence : 0.04094         
      Balanced Accuracy : 0.98810         
                                          
       'Positive' Class : yes             
                                          
Confusion Matrix and Statistics

          Reference
Prediction yes  no
       yes   3   4
       no    0 164
                                          
               Accuracy : 0.9766          
                 95% CI : (0.9412, 0.9936)
    No Information Rate : 0.9825          
    P-Value [Acc > NIR] : 0.8168          
                                          
                  Kappa : 0.5899          
                                          
 Mcnemar's Test P-Value : 0.1336          
                                          
            Sensitivity : 1.00000         
            Specificity : 0.97619         
         Pos Pred Value : 0.42857         
         Neg Pred Value : 1.00000         
             Prevalence : 0.01754         
         Detection Rate : 0.01754         
   Detection Prevalence : 0.04094         
      Balanced Accuracy : 0.98810         
                                          
       'Positive' Class : yes             
                                          
Confusion Matrix and Statistics

          Reference
Prediction yes  no
       yes   2   1
       no    1 167
                                          
               Accuracy : 0.9883          
                 95% CI : (0.9584, 0.9986)
    No Information Rate : 0.9825          
    P-Value [Acc > NIR] : 0.4212          
                                          
                  Kappa : 0.6607          
                                          
 Mcnemar's Test P-Value : 1.0000          
                                          
            Sensitivity : 0.66667         
            Specificity : 0.99405         
         Pos Pred Value : 0.66667         
         Neg Pred Value : 0.99405         
             Prevalence : 0.01754         
         Detection Rate : 0.01170         
   Detection Prevalence : 0.01754         
      Balanced Accuracy : 0.83036         
                                          
       'Positive' Class : yes             
                                          
Confusion Matrix and Statistics

          Reference
Prediction yes  no
       yes   3   4
       no    0 164
                                          
               Accuracy : 0.9766          
                 95% CI : (0.9412, 0.9936)
    No Information Rate : 0.9825          
    P-Value [Acc > NIR] : 0.8168          
                                          
                  Kappa : 0.5899          
                                          
 Mcnemar's Test P-Value : 0.1336          
                                          
            Sensitivity : 1.00000         
            Specificity : 0.97619         
         Pos Pred Value : 0.42857         
         Neg Pred Value : 1.00000         
             Prevalence : 0.01754         
         Detection Rate : 0.01754         
   Detection Prevalence : 0.04094         
      Balanced Accuracy : 0.98810         
                                          
       'Positive' Class : yes             
                                          

We see that all algorithms had similar performances in the validation step. Even augmenting the dataset isn’t enough to overcome the inherent problems of this dataset with such heavy class imbalances. I would dare to say that in the validation step, the best result was with down sampling instead of upsampling, since we had fewer false negatives (that is a big problem in diagnostic tests).

LS0tCnRpdGxlOiAiSEVBRFMgLSBISURBIC0gTEVBUk46IEFzc2lnbm1lbnQgMSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGZpZ19oZWlnaHQ6IDgKICAgIGZpZ193aWR0aDogMTAKICAgIGhpZ2hsaWdodDogcHlnbWVudHMKICAgIHRoZW1lOiB1bml0ZWQKICAgIHRvYzogeWVzCmF1dGhvcjogRnJhbmNpc2NvIEJpc2Nob2ZmCi0tLQoKIyMgSW5zdHJ1Y3Rpb25zCgpDb25zaWRlciB0aGUgdGhlIENlcnZpY2FsIENhbmNlciAoUmlzayBGYWN0b3JzKSBkYXRhIHNldCAoYXZhaWxhYmxlIGZyb20gVUNJIHJlcG9zaXRvcnkpIGFuZCB0cnkgdG8gYWNjdXJhdGVseSBjbGFzc2lmeSBEeC5DYW5jZXIuCgpZb3UgbXVzdCBjb21wYXJlIGRpZmZlcmVudCBhcHByb2FjaGVzIGFuZCBwYXJhbWV0ZXJzIG9mIGEpIHNpbmdsZSBkZWNpc2lvbiB0cmVlLCBiKSByYW5kb20gZm9yZXN0LgoKRXZhbHVhdGlvbiBvZiBkZXJpdmVkIG1vZGVscyBzaG91bGQgZm9sbG93IGEgY29ycmVjdCBtZXRob2RvbG9neSwgY29tcGFyaW5nIGRpZmZlcmVudCBlc3RpbWF0ZXMgb2YgZ2VuZXJhbGl6YXRpb24gZXJyb3IgKGkuZS4gaG9sZG91dCwgY3Jvc3MtdmFsaWRhdGlvbiwgYm9vdHN0cmFwLCAuLi4pCgpTdWJtaXQgYSByZXBvcnQgKGluIFBERiwgZ2VuZXJhdGVkIGZyb20gUikgd2l0aCB0aGUgY29kZSBhbmQgdGhlIHJlc3VsdGluZyBhbmFseXNpcy4gCgpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZyA9IFRSVUUpCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoZXhwc3MpCmxpYnJhcnkocnBhcnQpCmxpYnJhcnkocnBhcnQucGxvdCkgIyBiZXR0ZXIgcGxvdHMKbGlicmFyeShwUk9DKQpkYXRhc2V0IDwtIHJlYWRfY3N2KCJyaXNrX2ZhY3RvcnNfY2VydmljYWxfY2FuY2VyLmNzdiIsIG5hID0gIj8iKQojIGNvZXJjZSBpdCB0byBkYXRhLmZyYW1lLCBmb3Igc2ltcGxpY2l0eQpkYXRhc2V0IDwtIGRhdGEuZnJhbWUoZGF0YXNldCkKY29sdW1ucyA8LSBjKAogICJhZ2UiLAogICJwYXJ0bmVycyIsCiAgInNleGFyY2EiLAogICJwcmVnbmFuY2llcyIsCiAgInNtb2tlcyIsCiAgInNtb2tlc195ZWFycyIsCiAgInNtb2tlcnNfcGFja195ZWFyIiwKICAib2hjIiwKICAib2hjX3llYXJzIiwKICAiaXVkIiwKICAiaXVkX3llYXJzIiwKICAic3RkIiwKICAic3RkX251bSIsCiAgInN0ZF9jb25keWxvbWEiLAogICJzdGRfY2VydmljYWwiLAogICJzdGRfdmFnaW5hbCIsCiAgInN0ZF92dWx2b19wZXJpdCIsCiAgInN0ZF9zeXBoaWxpcyIsCiAgInN0ZF9waWQiLAogICJzdGRfaGVycGVzIiwKICAic3RkX21vbGx1c2N1bSIsCiAgInN0ZF9haWRzIiwKICAic3RkX2hpdiIsCiAgInN0ZF9oZXBiIiwKICAic3RkX2hwdiIsCiAgInN0ZF9udW1fb2ZfZGlhZyIsCiAgInN0ZF90aW1lX3NpbmNlX2ZpcnN0IiwKICAic3RkX3RpbWVfc2luY2VfbGFzdCIsCiAgImR4X2NhbmNlciIsCiAgImR4X2NpbiIsCiAgImR4X2hwdiIsCiAgImR4IiwKICAiaGluc2VsbWFubiIsCiAgInNjaGlsbGVyIiwKICAiY2l0b2xvZ3kiLAogICJiaW9wc3kiCikKbmFtZXMoZGF0YXNldCkgPC0gY29sdW1ucwojIHVnbHkgd2F5IHRvIG1vdmUgdGhlIGRlcGVuZGFudCB2YXJpYWJsZSB0byB0aGUgZW5kOgpkYXRhc2V0IDwtIGNiaW5kKGRhdGFzZXRbLCAtd2hpY2goY29sdW1ucyA9PSAiZHhfY2FuY2VyIildLAogICAgICAgICAgICAgICAgIGR4X2NhbmNlciA9IGRhdGFzZXQkZHhfY2FuY2VyKQoKZGF0YXNldCRzbW9rZXMgPC0gZmFjdG9yKGRhdGFzZXQkc21va2VzLAogIGxldmVscyA9IGMoMSwgMCksCiAgbGFiZWxzID0gYygieWVzIiwgIm5vIikKKQpkYXRhc2V0JG9oYyA8LSBmYWN0b3IoZGF0YXNldCRvaGMsCiAgbGV2ZWxzID0gYygxLCAwKSwKICBsYWJlbHMgPSBjKCJ5ZXMiLCAibm8iKQopCmRhdGFzZXQkaXVkIDwtIGZhY3RvcihkYXRhc2V0JGl1ZCwKICBsZXZlbHMgPSBjKDEsIDApLAogIGxhYmVscyA9IGMoInllcyIsICJubyIpCikKZGF0YXNldCRzdGQgPC0gZmFjdG9yKGRhdGFzZXQkc3RkLAogIGxldmVscyA9IGMoMSwgMCksCiAgbGFiZWxzID0gYygieWVzIiwgIm5vIikKKQpkYXRhc2V0JHN0ZF9jb25keWxvbWEgPC0gZmFjdG9yKGRhdGFzZXQkc3RkX2NvbmR5bG9tYSwKICBsZXZlbHMgPSBjKDEsIDApLAogIGxhYmVscyA9IGMoInllcyIsICJubyIpCikKZGF0YXNldCRzdGRfY2VydmljYWwgPC0gZmFjdG9yKGRhdGFzZXQkc3RkX2NlcnZpY2FsLAogIGxldmVscyA9IGMoMSwgMCksCiAgbGFiZWxzID0gYygieWVzIiwgIm5vIikKKQpkYXRhc2V0JHN0ZF92YWdpbmFsIDwtIGZhY3RvcihkYXRhc2V0JHN0ZF92YWdpbmFsLAogIGxldmVscyA9IGMoMSwgMCksCiAgbGFiZWxzID0gYygieWVzIiwgIm5vIikKKQpkYXRhc2V0JHN0ZF92dWx2b19wZXJpdCA8LSBmYWN0b3IoZGF0YXNldCRzdGRfdnVsdm9fcGVyaXQsCiAgbGV2ZWxzID0gYygxLCAwKSwKICBsYWJlbHMgPSBjKCJ5ZXMiLCAibm8iKQopCmRhdGFzZXQkc3RkX3N5cGhpbGlzIDwtIGZhY3RvcihkYXRhc2V0JHN0ZF9zeXBoaWxpcywKICBsZXZlbHMgPSBjKDEsIDApLAogIGxhYmVscyA9IGMoInllcyIsICJubyIpCikKZGF0YXNldCRzdGRfcGlkIDwtIGZhY3RvcihkYXRhc2V0JHN0ZF9waWQsCiAgbGV2ZWxzID0gYygxLCAwKSwKICBsYWJlbHMgPSBjKCJ5ZXMiLCAibm8iKQopCmRhdGFzZXQkc3RkX2hlcnBlcyA8LSBmYWN0b3IoZGF0YXNldCRzdGRfaGVycGVzLAogIGxldmVscyA9IGMoMSwgMCksCiAgbGFiZWxzID0gYygieWVzIiwgIm5vIikKKQpkYXRhc2V0JHN0ZF9tb2xsdXNjdW0gPC0gZmFjdG9yKGRhdGFzZXQkc3RkX21vbGx1c2N1bSwKICBsZXZlbHMgPSBjKDEsIDApLAogIGxhYmVscyA9IGMoInllcyIsICJubyIpCikKZGF0YXNldCRzdGRfYWlkcyA8LSBmYWN0b3IoZGF0YXNldCRzdGRfYWlkcywKICBsZXZlbHMgPSBjKDEsIDApLAogIGxhYmVscyA9IGMoInllcyIsICJubyIpCikKZGF0YXNldCRzdGRfaGl2IDwtIGZhY3RvcihkYXRhc2V0JHN0ZF9oaXYsCiAgbGV2ZWxzID0gYygxLCAwKSwKICBsYWJlbHMgPSBjKCJ5ZXMiLCAibm8iKQopCmRhdGFzZXQkc3RkX2hlcGIgPC0gZmFjdG9yKGRhdGFzZXQkc3RkX2hlcGIsCiAgbGV2ZWxzID0gYygxLCAwKSwKICBsYWJlbHMgPSBjKCJ5ZXMiLCAibm8iKQopCmRhdGFzZXQkc3RkX2hwdiA8LSBmYWN0b3IoZGF0YXNldCRzdGRfaHB2LAogIGxldmVscyA9IGMoMSwgMCksCiAgbGFiZWxzID0gYygieWVzIiwgIm5vIikKKQpkYXRhc2V0JGR4X2NhbmNlciA8LSBmYWN0b3IoZGF0YXNldCRkeF9jYW5jZXIsCiAgbGV2ZWxzID0gYygxLCAwKSwKICBsYWJlbHMgPSBjKCJ5ZXMiLCAibm8iKQopCmRhdGFzZXQkZHhfY2luIDwtIGZhY3RvcihkYXRhc2V0JGR4X2NpbiwKICBsZXZlbHMgPSBjKDEsIDApLAogIGxhYmVscyA9IGMoInllcyIsICJubyIpCikKZGF0YXNldCRkeF9ocHYgPC0gZmFjdG9yKGRhdGFzZXQkZHhfaHB2LAogIGxldmVscyA9IGMoMSwgMCksCiAgbGFiZWxzID0gYygieWVzIiwgIm5vIikKKQpkYXRhc2V0JGR4IDwtIGZhY3RvcihkYXRhc2V0JGR4LAogIGxldmVscyA9IGMoMSwgMCksCiAgbGFiZWxzID0gYygieWVzIiwgIm5vIikKKQpkYXRhc2V0JGhpbnNlbG1hbm4gPC0gZmFjdG9yKGRhdGFzZXQkaGluc2VsbWFubiwKICBsZXZlbHMgPSBjKDEsIDApLAogIGxhYmVscyA9IGMoInllcyIsICJubyIpCikKZGF0YXNldCRzY2hpbGxlciA8LSBmYWN0b3IoZGF0YXNldCRzY2hpbGxlciwKICBsZXZlbHMgPSBjKDEsIDApLAogIGxhYmVscyA9IGMoInllcyIsICJubyIpCikKZGF0YXNldCRjaXRvbG9neSA8LSBmYWN0b3IoZGF0YXNldCRjaXRvbG9neSwKICBsZXZlbHMgPSBjKDEsIDApLAogIGxhYmVscyA9IGMoInllcyIsICJubyIpCikKZGF0YXNldCRiaW9wc3kgPC0gZmFjdG9yKGRhdGFzZXQkYmlvcHN5LAogIGxldmVscyA9IGMoMSwgMCksCiAgbGFiZWxzID0gYygieWVzIiwgIm5vIikKKQpgYGAKCiMjIFRoZSBEYXRhc2V0CgpUaGUgZGF0YXNldCBjb21wcmlzZXMgb2YgMzYgdmFyaWFibGVzLCBiZWluZyAyNCBjYXRlZ29yaWNhbCBhbmQgMTIgY29udGludW91cyB2YXJpYWJsZXMuIFRoZSBkYXRhc2V0IHdhcyBjb2xsZWN0ZWQgYXQgJ0hvc3BpdGFsIFVuaXZlcnNpdGFyaW8gZGUgQ2FyYWNhcycgaW4gQ2FyYWNhcywgVmVuZXp1ZWxhLiBUaGUgZGF0YXNldCBjb21wcmlzZXMgZGVtb2dyYXBoaWMgaW5mb3JtYXRpb24sIGhhYml0cywgYW5kIGhpc3RvcmljIG1lZGljYWwgcmVjb3JkcyBvZiA4NTggcGF0aWVudHMuIFNldmVyYWwgcGF0aWVudHMgZGVjaWRlZCBub3QgdG8gYW5zd2VyIHNvbWUgb2YgdGhlIHF1ZXN0aW9ucyBiZWNhdXNlIG9mIHByaXZhY3kgY29uY2VybnMgKG1pc3NpbmcgdmFsdWVzKSwgbWFpbmx5IHVzZSBvZiBjb250cmFjZXB0aXZlcywgSVVEIGFuZCBjb25jZXJuaW5nIHRoZSBTVERzLgoKIyMgVGhlIEV4ZXJjaXNlCgpUaGUgb2JqZWN0aXZlIG9mIHRoaXMgZXhlcmNpc2UgaXMgdG8gYXR0ZW10cCB0byBjbGFzc2lmeSB0aGUgdmFyaWFibGUgRHg6Q2FuY2VyIHVzaW5nIHNvbWUgYXZhaWxhYmxlIGFsZ29yaXRobXMgYW5kIGNvbXBhcmUgdGhlbS4KClRoZXJlIGFyZSBzZXZlcmFsIGFsZ29yaXRobXMgZm9yIHRoYXQgd2UgbWF5IHVzZToKCi0gTGluZWFyIHJlZ3Jlc3Npb24sIExvZ2lzdGljIFJlZ3Jlc3Npb24gYW5kIFN1cHBvcnQgVmVjdG9yIE1hY2hpbmVzCi0gUGVyY2VwdHJvbnMgYW5kIEFydGlmaWNpYWwgTmV1cmFsIE5ldHdvcmtzCi0gTmFpdmUgQmF5ZXMgQ2xhc3NpZmllcgotIERlY2lzaW9uIFRyZWVzCi0gay1uZWFyZXN0IG5laWdoYm9yCgpIZXJlIGlzIGEgImJyaWVmIiBleGFtcGxlIG9mIGFzc3VtcHRpb25zIHRoYXQgd2UgbWF5IHRha2UgaW4gYWNjb3VudCBmb3IgZWFjaCBtZXRob2Q6CgohW10oaHR0cHM6Ly9taXJvLm1lZGl1bS5jb20vbWF4LzE3MjgvMSprTzJ4RTN0UUZ4Zzl4cDBaU0tXT0lRLnBuZyk8ZGl2IGFsaWduID0gImNlbnRlciI+QWxsIHRoZSBBbm5veWluZyBBc3N1bXB0aW9ucywgZnJvbSBbRGlwIFJhbmphbiBDaGF0dGVyamVlCl0oaHR0cHM6Ly90b3dhcmRzZGF0YXNjaWVuY2UuY29tL2FsbC10aGUtYW5ub3lpbmctYXNzdW1wdGlvbnMtMzFiNTVkZjI0NmMzKTwvZGl2PgoKIyMjIFJlZ3Jlc3Npb24gb3IgQ2xhc3NpZmljYXRpb24/CkZpcnN0LCB3ZSBuZWVkIHRvIGV4cGxhaW4gd2hhdCBpcyBSZWdyZXNzaW9uIGFuZCB3aGF0IGlzIENsYXNzaWZpY2F0aW9uLCBzaW5jZSB0aGlzIG9mdGVuIGNhdXNlcyBjb25mdXNpb246CgotIEJvdGggYXJlIHVuZGVyIHRoZSBzYW1lIHVtYnJlbGxhIG9mIHN1cGVydmlzZWQgbWFjaGluZSBsZWFybmluZy4KLSBCb3RoIHV0aWxpemUgYSBrbm93biBkYXRhc2V0ICh0cmFpbmluZyBkYXRhc2V0KSB0byBtYWtlIHByZWRpY3Rpb25zLgotIFRoZSBtYWluIGRpZmZlcmVuY2UgaXMgdGhlIG91dHB1dDogZm9yIHJlZ3Jlc3Npb24gaXMgbnVtZXJpY2FsIChvciBjb250aW51b3VzKSB3aGlsZSBmb3IgY2xhc3NpZmljYXRpb24gaXMgY2F0ZWdvcmljYWwgKG9yIGRpc2NyZXRlKS4KCkluIHRoaXMgZXhlcmNpc2UsIHdlIGhhdmUgYSBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtLCB0aGF0IHdpbGwgYXR0ZW1wdCB0byBjbGFzc2lmeSBmb3IgKipgdHJ1ZWAqKiAoaGFzIGNhbmNlcikgb3IgKipgZmFsc2VgKiogKGRvZXNuJ3QgaGF2ZSBjYW5jZXIpLCBoYXZpbmcgbm8gb3RoZXIgY2xhc3MgaW52b2x2ZWQuCgpTbywgdGhlIGN1cnJlbnQgZXhlcmNpc2Ugd2lsbCB1c2UgdGhlIGZvbGxvd2luZyBjbGFzc2lmaWNhdGlvbiBtZXRob2RzOgoKYSkgRGVjaXNpb24gdHJlZQpiKSBSYW5kb20gZm9yZXN0CgojIyBEZWNpc2lvbiBUcmVlCgpGaXJzdCwgbGV0J3MgY3JlYXRlIGEgcGFydGl0aW9uIHdpdGggODAlIG9mIHRoZSBkYXRhOgoKYGBge3IgcHJlcGFyZV9kYXRhfQpzZXQuc2VlZCgyMDIwKQojIENyZWF0ZSB0aGUgaW5kZXhlcyBvZiB0aGUgODAlIHBhcnRpdGlvbgpwYXJ0X2lkeHMgPC0KICBjcmVhdGVEYXRhUGFydGl0aW9uKGRhdGFzZXQkZHhfY2FuY2VyLCBwID0gMC44LCBsaXN0ID0gRkFMU0UpCmRhdGEudHJhaW4gPC0gZGF0YXNldFtwYXJ0X2lkeHMsIF0KZGF0YS50ZXN0IDwtIGRhdGFzZXRbLXBhcnRfaWR4cywgXQpgYGAKClRoYW4sIGxldCdzIGNyZWF0ZSBhIG1vZGVsIHdpdGggR2luaSBpbXB1cml0eSBpbmRleCBhbmQgSW5mb3JtYXRpb24gR2FpbjoKCmBgYHtyIHNpbmdsZV90cmVlLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD0xMH0KIyBMZWFybiB0cmVlIHdpdGggR2luaSBpbXB1cml0eQp0cmVlLmdpbmkgPC0gcnBhcnQoCiAgZHhfY2FuY2VyIH4gLiwKICBkYXRhID0gZGF0YS50cmFpbiwKICBwYXJtcyA9IGxpc3Qoc3BsaXQgPSAiZ2luaSIpLAogIG1ldGhvZCA9ICJjbGFzcyIKKQojIExlYW4gdHJlZSB3aXRoIGluZm9ybWF0aW9uIGdhaW4KdHJlZS5pbmZvcm1hdGlvbiA8LSBycGFydCgKICBkeF9jYW5jZXIgfiAuLAogIGRhdGEgPSBkYXRhLnRyYWluLAogIHBhcm1zID0gbGlzdChzcGxpdCA9ICJpbmZvcm1hdGlvbiIpLAogIG1ldGhvZCA9ICJjbGFzcyIKKQpwYXIobWZyb3cgPSBjKDEsIDIpKQpycGFydC5wbG90KHRyZWUuZ2luaSwgbWFpbiA9ICJHaW5pIEluZGV4IiwgdHlwZSA9IDUpCnJwYXJ0LnBsb3QodHJlZS5pbmZvcm1hdGlvbiwgbWFpbiA9ICJJbmZvcm1hdGlvbiBHYWluIiwgdHlwZSA9IDUpCmBgYAoKQm90aCBhbGdvcml0aG1zIGZpbmQgdGhhdCAqKmR4X2hwdioqIGlzIGEgc3Ryb25nIHByZWRpY3RvciBmb3IgKipkeF9jYW5jZXIqKi4KClRoaXMgc2VlbXMgYSBsaXR0bGUgb2RkIChub3QgaW1wb3NzaWJsZSksIGxldCdzIHRha2UgYSBsb29rIGluIHRoZSBkYXRhc2V0OgoKYGBge3IgY2hlY2tfZGF0YSwgZWNobz1UUlVFfQpuZWFyIDwtIG5lYXJaZXJvVmFyKGRhdGFzZXQsIHNhdmVNZXRyaWNzID0gVFJVRSkKbmVhcltuZWFyWywgInplcm9WYXIiXSArIG5lYXJbLCAibnp2Il0gPiAwLCBdCmBgYAoKVXNpbmcgdGhlIGBuZWFyWmVyb1ZhcmAgZnJvbSBgY2FyZXRgIHdlIHNlZSB0aGF0IHRoZXJlIGFyZSBzZXZlcmFsIHZhcmlhYmxlcyB0aGF0IGNvbnRhaW5zIG5lYXItemVybyB2YXJpYW5jZSBwcmVkaWN0b3JzIChpLmUuOiB2YXJpYWJsZXMgdGhhdCB0YWtlIGFuIHVuaXF1ZSB2YWx1ZSBhY3Jvc3Mgc2FtcGxlcykuCgpXZSBkb24ndCB3YW50IHRvIHNpbXBseSByZW1vdmUgdGhlc2UgdmFyaWFibGVzLiBTbyB3ZSB3aWxsIHRyeSBhbm90aGVyIGFwcHJvYWNoOiBzdWJzYW1wbGluZy4KCiMjIFN1YnNhbXBsaW5nCgpTdWJzYW1wbGluZyB0cmllcyB0byBiYWxhbmNlIGEgY2xhc3MtdW5iYWxhbmNlZCBkYXRhc2V0LiBXZSBkb24ndCB3YW50IHRvIGFydGlmaWNpYWxseSBiYWxhbmNlIHRoZSB0ZXN0IHNldCwgc28gd2Ugd2lsbCB0YWtlIHRoZSB0cmFpbmluZyBzZXQgYW5kLCBiZWZvcmUgbW9kZWwgZml0dGluZywgYW5kIHNhbXBsZSB0aGUgZGF0YS4gVGhlcmUgYXJlIHR3byBpc3N1ZXMgd2l0aCB0aGlzIGFwcHJvYWNoIGFjY29yZGluZyB0byBgY2FyZXRgIHBhY2thZ2UgaGVscCBwYWdlczoKCi0gRmlyc3RseSwgZHVyaW5nIG1vZGVsIHR1bmluZyB0aGUgaG9sZG91dCBzYW1wbGVzIGdlbmVyYXRlZCBkdXJpbmcgcmVzYW1wbGluZyBhcmUgYWxzbyBnbGFuY2VkIGFuZCBtYXkgbm90IHJlZmxlY3QgdGhlIGNsYXNzIGltYmFsYW5jZSB0aGF0IGZ1dHVyZSBwcmVkaWN0aW9ucyB3b3VsZCBlbmNvdW50ZXIuIFRoaXMgaXMgbGlrZWx5IHRvIGxlYWQgdG8gb3Zlcmx5IG9wdGltaXN0aWMgZXN0aW1hdGVzIG9mIHBlcmZvcm1hbmNlLgoKLSBTZWNvbmRseSwgdGhlIHN1YnNhbXBsaW5nIHByb2Nlc3Mgd2lsbCBwcm9iYWJseSBpbmR1Y2UgbW9yZSBtb2RlbCB1bmNlcnRhaW50eS4gV291bGQgdGhlIG1vZGVsIHJlc3VsdHMgZGlmZmVyIHVuZGVyIGEgZGlmZmVyZW50IHN1YnNhbXBsZT8gQXMgYWJvdmUsIHRoZSByZXNhbXBsaW5nIHN0YXRpc3RpY3MgYXJlIG1vcmUgbGlrZWx5IHRvIG1ha2UgdGhlIG1vZGVsIGFwcGVhciBtb3JlIGVmZmVjdGl2ZSB0aGFuIGl0IGFjdHVhbGx5IGlzLgoKVGhlIGF1dGhvciBhbHNvIHN1Z2dlc3RzIGFzIGFuIGFsdGVybmF0aXZlLCB0byBpbmNsdWRlIHRoZSBzdWJzYW1wbGluZyBpbnNpZGUgb2YgdGhlIHVzdWFsIHJlc2FtcGxpbmcgcHJvY2VkdXJlIChjcm9zcy12YWxpZGF0aW9uLCBib290c3RyYXAsIC4uLiksIGluIGV4Y2hhbmdlIG9mIGNvbXB1dGluZyB0aW1lLgoKVGhlcmUgd2Ugd2lsbCB0cnkgdHdvIGFsZ29yaXRobXMsIF9kb3duLXNhbXBsaW5nXyBhbmQgX3VwLXNhbXBsaW5nXy4gVGhlIGxhdHRlciBpcyBrbm93IHRvIGJlIG1vcmUgb3B0aW1pc3RpYywgYnV0IHNpbmNlIHdlIGhhdmUgc28gZmV3IGNhc2VzLCBpdCBzZWVtcyB0aGUgbW9yZSBpbnRlcmVzdGluZyBmb3IgdXMuCgojIyMgRG93biBTYW1wbGluZwoKRmlyc3QsIGNyZWF0ZSB0aGUgZG93bi1zYW1wbGVkIHRyYWluaW5nIHNldC4KCmBgYHtyIGRvd25fc2FtcGxlfQpzZXQuc2VlZCgyMDIwKQpkb3duX3RyYWluIDwtIGRvd25TYW1wbGUoCiAgeCA9IGRhdGEudHJhaW5bLCAtbmNvbChkYXRhLnRyYWluKV0sCiAgeSA9IGRhdGEudHJhaW4kZHhfY2FuY2VyLAogIHluYW1lID0gImR4X2NhbmNlciIKKQpgYGAKCk5vdyBsZXQncyBtb2RlbCBhZ2FpbjoKCmBgYHtyIGRvd25fdHJlZSwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTB9CiMgTGVhcm4gdHJlZSB3aXRoIEdpbmkgaW1wdXJpdHkKdHJlZS5naW5pIDwtIHJwYXJ0KAogIGR4X2NhbmNlciB+IC4sCiAgZGF0YSA9IGRvd25fdHJhaW4sCiAgcGFybXMgPSBsaXN0KHNwbGl0ID0gImdpbmkiKSwKICBtZXRob2QgPSAiY2xhc3MiCikKIyBMZWFuIHRyZWUgd2l0aCBpbmZvcm1hdGlvbiBnYWluCnRyZWUuaW5mb3JtYXRpb24gPC0gcnBhcnQoCiAgZHhfY2FuY2VyIH4gLiwKICBkYXRhID0gZG93bl90cmFpbiwKICBwYXJtcyA9IGxpc3Qoc3BsaXQgPSAiaW5mb3JtYXRpb24iKSwKICBtZXRob2QgPSAiY2xhc3MiCikKcGFyKG1mcm93ID0gYygxLCAyKSkKcnBhcnQucGxvdCh0cmVlLmdpbmksIG1haW4gPSAiR2luaSBJbmRleCIsIHR5cGUgPSA1KQpycGFydC5wbG90KHRyZWUuaW5mb3JtYXRpb24sIG1haW4gPSAiSW5mb3JtYXRpb24gR2FpbiIsIHR5cGUgPSA1KQpgYGAKCldlIHN0aWxsIGhhdmUgdGhlIHNhbWUgYXMgYmVmb3JlOiBhIHN0cm9uZyBwcmVkaWN0aW9uIGJhc2VkIG9uICoqZHhfaHB2KiouCgojIyMgVXAgU2FtcGxpbmcKCkZpcnN0LCBjcmVhdGUgdGhlIHVwLXNhbXBsZWQgdHJhaW5pbmcgc2V0LgoKYGBge3IgdXBfc2FtcGxlfQpzZXQuc2VlZCgyMDIwKQp1cF90cmFpbiA8LSB1cFNhbXBsZSgKICB4ID0gZGF0YS50cmFpblssIC1uY29sKGRhdGEudHJhaW4pXSwKICB5ID0gZGF0YS50cmFpbiRkeF9jYW5jZXIsCiAgeW5hbWUgPSAiZHhfY2FuY2VyIgopCmBgYAoKTm93IGxldCdzIG1vZGVsIGFnYWluOgoKYGBge3IgdXBfdHJlZSwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTB9CiMgTGVhcm4gdHJlZSB3aXRoIEdpbmkgaW1wdXJpdHkKdHJlZS5naW5pIDwtIHJwYXJ0KAogIGR4X2NhbmNlciB+IC4sCiAgZGF0YSA9IHVwX3RyYWluLAogIHBhcm1zID0gbGlzdChzcGxpdCA9ICJnaW5pIiksCiAgbWV0aG9kID0gImNsYXNzIgopCiMgTGVhbiB0cmVlIHdpdGggaW5mb3JtYXRpb24gZ2Fpbgp0cmVlLmluZm9ybWF0aW9uIDwtIHJwYXJ0KAogIGR4X2NhbmNlciB+IC4sCiAgZGF0YSA9IHVwX3RyYWluLAogIHBhcm1zID0gbGlzdChzcGxpdCA9ICJpbmZvcm1hdGlvbiIpLAogIG1ldGhvZCA9ICJjbGFzcyIKKQpwYXIobWZyb3cgPSBjKDEsIDIpKQpycGFydC5wbG90KHRyZWUuZ2luaSwgbWFpbiA9ICJHaW5pIEluZGV4IiwgdHlwZSA9IDUpCnJwYXJ0LnBsb3QodHJlZS5pbmZvcm1hdGlvbiwgbWFpbiA9ICJJbmZvcm1hdGlvbiBHYWluIiwgdHlwZSA9IDUpCmBgYAoKTm93IHdlIGhhdmUgc29tZXRoaW5nIGludGVyZXN0aW5nLiBJdCBzZWVtcyB0aGF0ICoqZHgqKiBiZWNhbWUgYWxzbyBpbXBvcnRhbnQgZm9yIG91ciBkZWNpc2lvbiB0cmVlLgoKSW4gdGhlIG5leHQgc3RlcCB3ZSB3aWxsIHRoZW4gZXZhbHVhdGUgdGhlIERlY2lzaW9uIFRyZWUgbW9kZWwgYW5kIHRoZSBSYW5kb20gRm9yZXN0IG1vZGVsLgoKIyMgUmFuZG9tIEZvcmVzdCBhbmQgZXZhbHVhdGlvbnMKCkZpcnN0IHdlIHdpbGwgY29tcGFyZSBib3RoIG1vZGVscyB1c2luZyB0aGUgZG93bi1zYW1wbGVkIHRyYWluaW5nIGRhdGE6CgpgYGB7ciBkb3duX2V2YWx1YXRpb24sIHdhcm5pbmc9RkFMU0V9Cm1ldHJpYyA8LSAiUk9DIgpjb250cm9sIDwtIHRyYWluQ29udHJvbCgKICBtZXRob2QgPSAiTEdPQ1YiLCBwID0gMC44LCBudW1iZXIgPSAxNTAsCiAgc3VtbWFyeUZ1bmN0aW9uID0gdHdvQ2xhc3NTdW1tYXJ5LAogIGNsYXNzUHJvYnMgPSBULAogIHNhdmVQcmVkaWN0aW9ucyA9IFQKKQoKc2V0LnNlZWQoNykKZml0LnJwYXJ0LmRvd24gPC0gdHJhaW4oCiAgZHhfY2FuY2VyIH4gLiwKICBkYXRhID0gZG93bl90cmFpbiwKICBtZXRob2QgPSAicnBhcnQiLAogIG1ldHJpYyA9IG1ldHJpYywKICB0ckNvbnRyb2wgPSBjb250cm9sLAogIG5hLmFjdGlvbiA9IG5hLnJwYXJ0CikKIyBSYW5kb20gRm9yZXN0CnNldC5zZWVkKDcpCmZpdC5yZi5kb3duIDwtIHRyYWluKAogIGR4X2NhbmNlciB+IC4sCiAgZGF0YSA9IGRvd25fdHJhaW4sCiAgbWV0aG9kID0gInJmIiwKICBtZXRyaWMgPSBtZXRyaWMsCiAgdHJDb250cm9sID0gY29udHJvbCwKICBuYS5hY3Rpb24gPSByYW5kb21Gb3Jlc3Q6Om5hLnJvdWdoZml4CikKCiMgU3VtbWFyaXplIGFjY3VyYWN5IG9mIG1vZGVscwpyZXN1bHRzIDwtIHJlc2FtcGxlcyhsaXN0KHJwYXJ0ID0gZml0LnJwYXJ0LmRvd24sIHJmID0gZml0LnJmLmRvd24pKQpzdW1tYXJ5KHJlc3VsdHMpCnBsb3Qucm9jKGFzLm51bWVyaWMoZml0LnJwYXJ0LmRvd24kcHJlZCRvYnMpLCBhcy5udW1lcmljKGZpdC5ycGFydC5kb3duJHByZWQkcHJlZCksIG1haW4gPSBmaXQucnBhcnQuZG93biRtZXRob2QsIHByaW50LmF1YyA9IFQpCnBsb3Qucm9jKGFzLm51bWVyaWMoZml0LnJmLmRvd24kcHJlZCRvYnMpLCBhcy5udW1lcmljKGZpdC5yZi5kb3duJHByZWQkcHJlZCksIG1haW4gPSBmaXQucmYuZG93biRtZXRob2QsIHByaW50LmF1YyA9IFQpCiMgQ29tcGFyZSBhY2N1cmFjeSBvZiBtb2RlbHMKZG90cGxvdChyZXN1bHRzKQpgYGAKCmBgYHtyIHVwX2V2YWx1YXRpb24sIHdhcm5pbmc9RkFMU0V9Cm1ldHJpYyA8LSAiUk9DIgpjb250cm9sIDwtIHRyYWluQ29udHJvbCgKICBtZXRob2QgPSAiTEdPQ1YiLCBwID0gMC44LCBudW1iZXIgPSAxNTAsCiAgc3VtbWFyeUZ1bmN0aW9uID0gdHdvQ2xhc3NTdW1tYXJ5LAogIGNsYXNzUHJvYnMgPSBULAogIHNhdmVQcmVkaWN0aW9ucyA9IFQKKQoKc2V0LnNlZWQoNykKZml0LnJwYXJ0LnVwIDwtIHRyYWluKAogIGR4X2NhbmNlciB+IC4sCiAgZGF0YSA9IHVwX3RyYWluLAogIG1ldGhvZCA9ICJycGFydCIsCiAgbWV0cmljID0gbWV0cmljLAogIHRyQ29udHJvbCA9IGNvbnRyb2wsCiAgbmEuYWN0aW9uID0gbmEucnBhcnQKKQojIFJhbmRvbSBGb3Jlc3QKc2V0LnNlZWQoNykKZml0LnJmLnVwIDwtIHRyYWluKAogIGR4X2NhbmNlciB+IC4sCiAgZGF0YSA9IHVwX3RyYWluLAogIG1ldGhvZCA9ICJyZiIsCiAgbWV0cmljID0gbWV0cmljLAogIHRyQ29udHJvbCA9IGNvbnRyb2wsCiAgbmEuYWN0aW9uID0gcmFuZG9tRm9yZXN0OjpuYS5yb3VnaGZpeAopCgojIFN1bW1hcml6ZSBhY2N1cmFjeSBvZiBtb2RlbHMKcmVzdWx0cyA8LSByZXNhbXBsZXMobGlzdChycGFydCA9IGZpdC5ycGFydC51cCwgcmYgPSBmaXQucmYudXApKQpzdW1tYXJ5KHJlc3VsdHMpCnBsb3Qucm9jKGFzLm51bWVyaWMoZml0LnJwYXJ0LnVwJHByZWQkb2JzKSwgYXMubnVtZXJpYyhmaXQucnBhcnQudXAkcHJlZCRwcmVkKSwgbWFpbiA9IGZpdC5ycGFydC51cCRtZXRob2QsIHByaW50LmF1YyA9IFQpCnBsb3Qucm9jKGFzLm51bWVyaWMoZml0LnJmLnVwJHByZWQkb2JzKSwgYXMubnVtZXJpYyhmaXQucmYudXAkcHJlZCRwcmVkKSwgbWFpbiA9IGZpdC5yZi51cCRtZXRob2QsIHByaW50LmF1YyA9IFQpCiMgQ29tcGFyZSBhY2N1cmFjeSBvZiBtb2RlbHMKZG90cGxvdChyZXN1bHRzKQpgYGAKCiMjIFZhbGlkYXRpb24KCk5vdyBpdCBpcyB0aW1lIHRvIHZhbGlkYXRlIHRoZSBtb2RlbHMgdXNpbmcgdGhlIHRlc3Qgc2V0IHdlIGNyZWF0ZWQgZnJvbSB0aGUgb3JpZ2luYWwgZGF0YXNldDoKCmBgYHtyIHZhbGlkYXRpb259CiMgTWFrZSBwcmVkaWN0aW9ucwpwcmVkLnJwYXJ0LnVwIDwtIHByZWRpY3QoZml0LnJwYXJ0LnVwLCBkYXRhLnRlc3QsIG5hLmFjdGlvbiA9IG5hLnJwYXJ0KQpwcmVkLnJwYXJ0LmRvd24gPC0gcHJlZGljdChmaXQucnBhcnQudXAsIGRhdGEudGVzdCwgbmEuYWN0aW9uID0gbmEucnBhcnQpCnByZWQucmYudXAgPC0gcHJlZGljdChmaXQucmYudXAsIGRhdGEudGVzdCwgbmEuYWN0aW9uID0gcmFuZG9tRm9yZXN0OjpuYS5yb3VnaGZpeCkKcHJlZC5yZi5kb3duIDwtIHByZWRpY3QoZml0LnJmLmRvd24sIGRhdGEudGVzdCwgbmEuYWN0aW9uID0gcmFuZG9tRm9yZXN0OjpuYS5yb3VnaGZpeCkKCmNvbmZ1c2lvbk1hdHJpeChwcmVkLnJwYXJ0LnVwLCBkYXRhLnRlc3QkZHhfY2FuY2VyKQpjb25mdXNpb25NYXRyaXgocHJlZC5ycGFydC5kb3duLCBkYXRhLnRlc3QkZHhfY2FuY2VyKQpjb25mdXNpb25NYXRyaXgocHJlZC5yZi51cCwgZGF0YS50ZXN0JGR4X2NhbmNlcikKY29uZnVzaW9uTWF0cml4KHByZWQucmYuZG93biwgZGF0YS50ZXN0JGR4X2NhbmNlcikKYGBgCgpXZSBzZWUgdGhhdCBhbGwgYWxnb3JpdGhtcyBoYWQgc2ltaWxhciBwZXJmb3JtYW5jZXMgaW4gdGhlIHZhbGlkYXRpb24gc3RlcC4gRXZlbiBhdWdtZW50aW5nIHRoZSBkYXRhc2V0IGlzbid0IGVub3VnaCB0byBvdmVyY29tZSB0aGUgaW5oZXJlbnQgcHJvYmxlbXMgb2YgdGhpcyBkYXRhc2V0IHdpdGggc3VjaCBoZWF2eSBjbGFzcyBpbWJhbGFuY2VzLiBJIHdvdWxkIGRhcmUgdG8gc2F5IHRoYXQgaW4gdGhlIHZhbGlkYXRpb24gc3RlcCwgdGhlIGJlc3QgcmVzdWx0IHdhcyB3aXRoIGRvd24gc2FtcGxpbmcgaW5zdGVhZCBvZiB1cHNhbXBsaW5nLCBzaW5jZSB3ZSBoYWQgZmV3ZXIgZmFsc2UgbmVnYXRpdmVzICh0aGF0IGlzIGEgYmlnIHByb2JsZW0gaW4gZGlhZ25vc3RpYyB0ZXN0cykuCg==