Data Science Stream

Topic 9B: Machine Learning I


Example R code solutions for the Data Science Computer Lab 9 are presented below.


1 Preparations

1.1 Load Required Packages

# Specify required packages
ml_packages <- c("caret", "magrittr", "rpart.plot")
# Install missing packages
install.packages(setdiff(ml_packages, rownames(installed.packages())))
# Load all packages
lapply(ml_packages, library, character.only = TRUE)

2 Predicting Penguin Species

2.1 Penguin Data

No answer required.

2.2

library(palmerpenguins)

ml_penguins <- na.omit(penguins[, -8]) # note we ignore the year variable here

2.3 Aim

Since we have multiple feature variables, and a known outcome variable, we are dealing with a supervised learning multi-class classification problem.

2.4 Data Visualisation

featurePlot(x = ml_penguins[, -1], y = ml_penguins$species, 
            plot = "pairs", auto.key = list(columns = 3))

2.4.1

We observe in several of the scatter plots that the observations for the Adelie and Chinstrap penguins overlap - this could potentially mean that it will be difficult to distinguish between them when classifying penguins.

3 Pre-Processing the Penguin data

3.1 Dummy Variables

# Load a package to help with the restructure of the data
library(tibble) 

# Use the dummayVars function to create a full set of dummy variables for the ml_penguins data
dummy_penguins <- dummyVars(species ~ ., data = ml_penguins)

# Use the predict function to update our ml_penguins feature variables with 
# both island and sex dummy variables
ml_penguins_updated <- as_tibble(predict(dummy_penguins, newdata = ml_penguins))

# Prepend the outcome variable to our updated data set, otherwise it will be lost
ml_penguins_updated <- cbind(species = ml_penguins$species, ml_penguins_updated)

3.2

head(ml_penguins_updated)
##   species island.Biscoe island.Dream island.Torgersen bill_length_mm
## 1  Adelie             0            0                1           39.1
## 2  Adelie             0            0                1           39.5
## 3  Adelie             0            0                1           40.3
## 4  Adelie             0            0                1           36.7
## 5  Adelie             0            0                1           39.3
## 6  Adelie             0            0                1           38.9
##   bill_depth_mm flipper_length_mm body_mass_g sex.female sex.male
## 1          18.7               181        3750          0        1
## 2          17.4               186        3800          1        0
## 3          18.0               195        3250          1        0
## 4          19.3               193        3450          1        0
## 5          20.6               190        3650          0        1
## 6          17.8               181        3625          1        0

3.3 Highly Influential Samples

nearZeroVar(ml_penguins_updated, saveMetrics = F)
## integer(0)
nearZeroVar(ml_penguins_updated, saveMetrics = T)
##                   freqRatio percentUnique zeroVar   nzv
## species            1.226891     0.9009009   FALSE FALSE
## island.Biscoe      1.042945     0.6006006   FALSE FALSE
## island.Dream       1.707317     0.6006006   FALSE FALSE
## island.Torgersen   6.085106     0.6006006   FALSE FALSE
## bill_length_mm     1.166667    48.9489489   FALSE FALSE
## bill_depth_mm      1.200000    23.7237237   FALSE FALSE
## flipper_length_mm  1.235294    16.2162162   FALSE FALSE
## body_mass_g        1.200000    27.9279279   FALSE FALSE
## sex.female         1.018182     0.6006006   FALSE FALSE
## sex.male           1.018182     0.6006006   FALSE FALSE

3.3.1

nearZeroVar(ml_penguins_updated, saveMetrics = T, freqCut = 2, uniqueCut = 5)
##                   freqRatio percentUnique zeroVar   nzv
## species            1.226891     0.9009009   FALSE FALSE
## island.Biscoe      1.042945     0.6006006   FALSE FALSE
## island.Dream       1.707317     0.6006006   FALSE FALSE
## island.Torgersen   6.085106     0.6006006   FALSE  TRUE
## bill_length_mm     1.166667    48.9489489   FALSE FALSE
## bill_depth_mm      1.200000    23.7237237   FALSE FALSE
## flipper_length_mm  1.235294    16.2162162   FALSE FALSE
## body_mass_g        1.200000    27.9279279   FALSE FALSE
## sex.female         1.018182     0.6006006   FALSE FALSE
## sex.male           1.018182     0.6006006   FALSE FALSE

3.3.2

There do not appear to be any feature variables which need to be removed due to their freqCut and uniqueCut values. While the variables for the different islands and sexes have low percentUnique values, and in the case of the island.Torgersen variable a high freqRatio value, this is not cause for concern as these are dummy variables.

3.3.3

base_cor <-  cor(ml_penguins_updated[, 5:8])
extreme_cor <- sum(abs(base_cor[upper.tri(base_cor)]) > .999)
extreme_cor
## [1] 0

The result of 0 here tells us that no feature variables have extremely high correlation with other feature variables.

3.3.4

base_cor 
##                   bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
## bill_length_mm         1.0000000    -0.2286256         0.6530956   0.5894511
## bill_depth_mm         -0.2286256     1.0000000        -0.5777917  -0.4720157
## flipper_length_mm      0.6530956    -0.5777917         1.0000000   0.8729789
## body_mass_g            0.5894511    -0.4720157         0.8729789   1.0000000

The largest negative correlation value is -0.5777917, between flipper_length_mm and bill_depth_mm. The largest positive correlation value is 0.8729789, between flipper_length_mm and body_mass_g.

The correlation value of could potentially be problematic.

3.3.5

No answer required.

3.3.6

ml_penguins_filtered <- ml_penguins_updated[, - 7] # flipper_length_mm has been removed

4 Training and Validation Data

set.seed(1650)
train_index <- createDataPartition(ml_penguins_filtered$species,
                                   p = .8, # here p designates the split - 80/20
                                   list = FALSE, times = 1) 

4.1

penguin_train <- ml_penguins_filtered[train_index, ]
penguin_validate <- ml_penguins_filtered[-train_index, ]

5 Fitting a Decision Tree Machine Learning Model

5.1 Decision Tree

set.seed(1650) 
penguin_decision_tree <- train(species ~ .,
                               data = penguin_train,
                               method = "rpart")
penguin_decision_tree
## CART 
## 
## 268 samples
##   8 predictor
##   3 classes: 'Adelie', 'Chinstrap', 'Gentoo' 
## 
## No pre-processing
## Resampling: Bootstrapped (25 reps) 
## Summary of sample sizes: 268, 268, 268, 268, 268, 268, ... 
## Resampling results across tuning parameters:
## 
##   cp          Accuracy   Kappa    
##   0.01324503  0.9436108  0.9117821
##   0.35761589  0.8089728  0.6885822
##   0.56291391  0.5649392  0.2597508
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was cp = 0.01324503.

5.1.1

No answer required.

5.1.2

rpart.plot(penguin_decision_tree$finalModel)

6 Validating Results

6.1

# Load magrittr package for piping
library(magrittr)

# count number of observations in validation data
validation_numbers <- nrow(penguin_validate)

# Use the fitted model to predict quality values given the validation data
predict_penguin_decision_tree <- predict(penguin_decision_tree, 
                                          newdata =penguin_validate)
# When run, the code below gives us the percentage of correct predictions
dec_tree_accuracy <- sum(predict_penguin_decision_tree == 
                         penguin_validate$species) / validation_numbers * 100

dec_tree_accuracy %>% round(2) 
## [1] 92.31

6.2

The decision tree ML model we have trained appears to do an excellent job of predicting the penguin species, for both the training and validation data sets (94.36% accuracy and 92.31% accuracy respectively).


Great work, that’s everything for today. Don’t worry if you did not complete everything in the designated lab time, there is a lot to learn!


References

Thulin, M. 2021. Modern Statistics with R: From Wrangling and Exploring Data to Inference and Predictive Modelling.


These notes have been prepared by Rupert Kuveke. Please note that some of the content in these notes has been developed from content in Thulin (2021). The copyright for the material in these notes resides with the authors named above, with the Department of Mathematical and Physical Sciences and with La Trobe University. Copyright in this work is vested in La Trobe University including all La Trobe University branding and naming. Unless otherwise stated, material within this work is licensed under a Creative Commons Attribution-Non Commercial-Non Derivatives License BY-NC-ND.

LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiA5QiBTb2x1dGlvbnMiDQpvdXRwdXQ6DQogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjogDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KYmlibGlvZ3JhcGh5OiBTVE0xMDAxX0RTX0NMX3JlZmVyZW5jZXMuYmliIA0KbGluay1jaXRhdGlvbnM6IHllcw0KLS0tDQoNCjxzdHlsZT4NCiNUT0Mgew0KICBiYWNrZ3JvdW5kOiB1cmwoImh0dHBzOi8vd3d3LmxhdHJvYmUuZWR1LmF1L19tZWRpYS9sYS10cm9iZS1hcGkvdjUvaW1nL2xvZ28uc3ZnIik7DQogIGJhY2tncm91bmQtc2l6ZTogY29udGFpbjsNCiAgcGFkZGluZy10b3A6IDgwcHggIWltcG9ydGFudDsNCiAgYmFja2dyb3VuZC1yZXBlYXQ6IG5vLXJlcGVhdDsNCn0NCjwvc3R5bGU+DQoNCiMjIyBEYXRhIFNjaWVuY2UgU3RyZWFtIHstfQ0KDQojIyMgVG9waWMgOUI6IE1hY2hpbmUgTGVhcm5pbmcgSSB7LX0NCg0KPGJyPg0KDQpFeGFtcGxlIFIgY29kZSBzb2x1dGlvbnMgZm9yIHRoZSBbRGF0YSBTY2llbmNlIENvbXB1dGVyIExhYiA5XShodHRwczovL3JwdWJzLmNvbS9MVFVfU1RNMTAwMS9EU01DTDkpIGFyZSBwcmVzZW50ZWQgYmVsb3cuDQoNCjxicj4NCg0KIyBQcmVwYXJhdGlvbnMgeyNwcmVwfQ0KDQojIyBMb2FkIFJlcXVpcmVkIFBhY2thZ2VzIHsjbG9hZH0NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBpbmNsdWRlID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KIyBTcGVjaWZ5IHJlcXVpcmVkIHBhY2thZ2VzDQptbF9wYWNrYWdlcyA8LSBjKCJjYXJldCIsICJtYWdyaXR0ciIsICJycGFydC5wbG90IikNCiMgSW5zdGFsbCBtaXNzaW5nIHBhY2thZ2VzDQppbnN0YWxsLnBhY2thZ2VzKHNldGRpZmYobWxfcGFja2FnZXMsIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKSkpDQojIExvYWQgYWxsIHBhY2thZ2VzDQpsYXBwbHkobWxfcGFja2FnZXMsIGxpYnJhcnksIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBGLCBpbmNsdWRlID0gRiwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KIyBTcGVjaWZ5IHJlcXVpcmVkIHBhY2thZ2VzDQptbF9wYWNrYWdlcyA8LSBjKCJjYXJldCIsICJtYWdyaXR0ciIsICJycGFydC5wbG90IikNCiMgSW5zdGFsbCBtaXNzaW5nIHBhY2thZ2VzDQppbnN0YWxsLnBhY2thZ2VzKHNldGRpZmYobWxfcGFja2FnZXMsIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKSkpDQojIExvYWQgYWxsIHBhY2thZ2VzDQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGFwcGx5KG1sX3BhY2thZ2VzLCBsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKQ0KYGBgDQoNCiMgUHJlZGljdGluZyBQZW5ndWluIFNwZWNpZXMNCg0KIyMgUGVuZ3VpbiBEYXRhIHsjcG5lZ2lufQ0KDQpObyBhbnN3ZXIgcmVxdWlyZWQuDQoNCiMjDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQoNCm1sX3Blbmd1aW5zIDwtIG5hLm9taXQocGVuZ3VpbnNbLCAtOF0pICMgbm90ZSB3ZSBpZ25vcmUgdGhlIHllYXIgdmFyaWFibGUgaGVyZQ0KYGBgDQoNCiMjIEFpbQ0KDQpTaW5jZSB3ZSBoYXZlIG11bHRpcGxlIGZlYXR1cmUgdmFyaWFibGVzLCBhbmQgYSBrbm93biBvdXRjb21lIHZhcmlhYmxlLCB3ZSBhcmUgZGVhbGluZyB3aXRoIGEgc3VwZXJ2aXNlZCBsZWFybmluZyBtdWx0aS1jbGFzcyBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtLg0KDQojIyBEYXRhIFZpc3VhbGlzYXRpb24gIHsjcHJvYmxlbX0NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCBmaWcuZGltID0gYygxMiwgMTIpLCBmaWcuYWxpZ249J2NlbnRlcid9DQpmZWF0dXJlUGxvdCh4ID0gbWxfcGVuZ3VpbnNbLCAtMV0sIHkgPSBtbF9wZW5ndWlucyRzcGVjaWVzLCANCiAgICAgICAgICAgIHBsb3QgPSAicGFpcnMiLCBhdXRvLmtleSA9IGxpc3QoY29sdW1ucyA9IDMpKQ0KYGBgDQoNCiMjIw0KDQpXZSBvYnNlcnZlIGluIHNldmVyYWwgb2YgdGhlIHNjYXR0ZXIgcGxvdHMgdGhhdCB0aGUgb2JzZXJ2YXRpb25zIGZvciB0aGUgQWRlbGllIGFuZCBDaGluc3RyYXAgcGVuZ3VpbnMgb3ZlcmxhcCAtIHRoaXMgY291bGQgcG90ZW50aWFsbHkgbWVhbiB0aGF0IGl0IHdpbGwgYmUgZGlmZmljdWx0IHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gdGhlbSB3aGVuIGNsYXNzaWZ5aW5nIHBlbmd1aW5zLg0KDQojIFByZS1Qcm9jZXNzaW5nIHRoZSBQZW5ndWluIGRhdGEgeyNwcmVwcm99DQoNCiMjIER1bW15IFZhcmlhYmxlcyB7I2R1bW15fQ0KDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCiMgTG9hZCBhIHBhY2thZ2UgdG8gaGVscCB3aXRoIHRoZSByZXN0cnVjdHVyZSBvZiB0aGUgZGF0YQ0KbGlicmFyeSh0aWJibGUpIA0KDQojIFVzZSB0aGUgZHVtbWF5VmFycyBmdW5jdGlvbiB0byBjcmVhdGUgYSBmdWxsIHNldCBvZiBkdW1teSB2YXJpYWJsZXMgZm9yIHRoZSBtbF9wZW5ndWlucyBkYXRhDQpkdW1teV9wZW5ndWlucyA8LSBkdW1teVZhcnMoc3BlY2llcyB+IC4sIGRhdGEgPSBtbF9wZW5ndWlucykNCg0KIyBVc2UgdGhlIHByZWRpY3QgZnVuY3Rpb24gdG8gdXBkYXRlIG91ciBtbF9wZW5ndWlucyBmZWF0dXJlIHZhcmlhYmxlcyB3aXRoIA0KIyBib3RoIGlzbGFuZCBhbmQgc2V4IGR1bW15IHZhcmlhYmxlcw0KbWxfcGVuZ3VpbnNfdXBkYXRlZCA8LSBhc190aWJibGUocHJlZGljdChkdW1teV9wZW5ndWlucywgbmV3ZGF0YSA9IG1sX3Blbmd1aW5zKSkNCg0KIyBQcmVwZW5kIHRoZSBvdXRjb21lIHZhcmlhYmxlIHRvIG91ciB1cGRhdGVkIGRhdGEgc2V0LCBvdGhlcndpc2UgaXQgd2lsbCBiZSBsb3N0DQptbF9wZW5ndWluc191cGRhdGVkIDwtIGNiaW5kKHNwZWNpZXMgPSBtbF9wZW5ndWlucyRzcGVjaWVzLCBtbF9wZW5ndWluc191cGRhdGVkKQ0KYGBgDQoNCiMjDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCmhlYWQobWxfcGVuZ3VpbnNfdXBkYXRlZCkNCmBgYA0KDQojIyBIaWdobHkgSW5mbHVlbnRpYWwgU2FtcGxlcyB7I2V4Y2Vzc2l2ZWluZmx1ZW5jZX0NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KbmVhclplcm9WYXIobWxfcGVuZ3VpbnNfdXBkYXRlZCwgc2F2ZU1ldHJpY3MgPSBGKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCm5lYXJaZXJvVmFyKG1sX3Blbmd1aW5zX3VwZGF0ZWQsIHNhdmVNZXRyaWNzID0gVCkNCmBgYA0KDQojIyMgeyNjdXRvZmZ9DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCm5lYXJaZXJvVmFyKG1sX3Blbmd1aW5zX3VwZGF0ZWQsIHNhdmVNZXRyaWNzID0gVCwgZnJlcUN1dCA9IDIsIHVuaXF1ZUN1dCA9IDUpDQpgYGANCg0KIyMjDQoNClRoZXJlIGRvIG5vdCBhcHBlYXIgdG8gYmUgYW55IGZlYXR1cmUgdmFyaWFibGVzIHdoaWNoIG5lZWQgdG8gYmUgcmVtb3ZlZCBkdWUgdG8gdGhlaXIgYGZyZXFDdXRgIGFuZCBgdW5pcXVlQ3V0YCB2YWx1ZXMuIFdoaWxlIHRoZSB2YXJpYWJsZXMgZm9yIHRoZSBkaWZmZXJlbnQgaXNsYW5kcyBhbmQgc2V4ZXMgaGF2ZSBsb3cgYHBlcmNlbnRVbmlxdWVgIHZhbHVlcywgYW5kIGluIHRoZSBjYXNlIG9mIHRoZSBgaXNsYW5kLlRvcmdlcnNlbmAgdmFyaWFibGUgYSBoaWdoIGBmcmVxUmF0aW9gIHZhbHVlLA0KdGhpcyBpcyBub3QgY2F1c2UgZm9yIGNvbmNlcm4gYXMgdGhlc2UgYXJlIGR1bW15IHZhcmlhYmxlcy4NCg0KIyMjDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCmJhc2VfY29yIDwtICBjb3IobWxfcGVuZ3VpbnNfdXBkYXRlZFssIDU6OF0pDQpleHRyZW1lX2NvciA8LSBzdW0oYWJzKGJhc2VfY29yW3VwcGVyLnRyaShiYXNlX2NvcildKSA+IC45OTkpDQpleHRyZW1lX2Nvcg0KYGBgDQoNClRoZSByZXN1bHQgb2YgMCBoZXJlIHRlbGxzIHVzIHRoYXQgbm8gZmVhdHVyZSB2YXJpYWJsZXMgaGF2ZSBleHRyZW1lbHkgaGlnaCBjb3JyZWxhdGlvbiB3aXRoIG90aGVyIGZlYXR1cmUgdmFyaWFibGVzLg0KDQojIyMgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCmJhc2VfY29yIA0KYGBgDQoNClRoZSBsYXJnZXN0IG5lZ2F0aXZlIGNvcnJlbGF0aW9uIHZhbHVlIGlzIC0wLjU3Nzc5MTcsIGJldHdlZW4gYGZsaXBwZXJfbGVuZ3RoX21tYCBhbmQgYGJpbGxfZGVwdGhfbW1gLg0KVGhlIGxhcmdlc3QgcG9zaXRpdmUgY29ycmVsYXRpb24gdmFsdWUgaXMgMC44NzI5Nzg5LCBiZXR3ZWVuIGBmbGlwcGVyX2xlbmd0aF9tbWAgYW5kIGBib2R5X21hc3NfZ2AuDQoNClRoZSBjb3JyZWxhdGlvbiB2YWx1ZSBvZiBjb3VsZCBwb3RlbnRpYWxseSBiZSBwcm9ibGVtYXRpYy4NCg0KDQojIyMgeyNjb3JsaW1pdH0NCg0KTm8gYW5zd2VyIHJlcXVpcmVkLg0KDQojIyMNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEZ9DQptbF9wZW5ndWluc19maWx0ZXJlZCA8LSBtbF9wZW5ndWluc191cGRhdGVkWywgLSA3XSAjIGZsaXBwZXJfbGVuZ3RoX21tIGhhcyBiZWVuIHJlbW92ZWQNCmBgYA0KDQojIFRyYWluaW5nIGFuZCBWYWxpZGF0aW9uIERhdGEgeyN0cmFpbn0NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0Kc2V0LnNlZWQoMTY1MCkNCnRyYWluX2luZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24obWxfcGVuZ3VpbnNfZmlsdGVyZWQkc3BlY2llcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcCA9IC44LCAjIGhlcmUgcCBkZXNpZ25hdGVzIHRoZSBzcGxpdCAtIDgwLzIwDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QgPSBGQUxTRSwgdGltZXMgPSAxKSANCmBgYA0KDQojIw0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpwZW5ndWluX3RyYWluIDwtIG1sX3Blbmd1aW5zX2ZpbHRlcmVkW3RyYWluX2luZGV4LCBdDQpwZW5ndWluX3ZhbGlkYXRlIDwtIG1sX3Blbmd1aW5zX2ZpbHRlcmVkWy10cmFpbl9pbmRleCwgXQ0KYGBgDQoNCiMgRml0dGluZyBhIERlY2lzaW9uIFRyZWUgTWFjaGluZSBMZWFybmluZyBNb2RlbCB7I2ZpdH0NCg0KIyMgRGVjaXNpb24gVHJlZSB7I2RlY3RyZWV9DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgY2FjaGUgPSBUfQ0Kc2V0LnNlZWQoMTY1MCkgDQpwZW5ndWluX2RlY2lzaW9uX3RyZWUgPC0gdHJhaW4oc3BlY2llcyB+IC4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHBlbmd1aW5fdHJhaW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJwYXJ0IikNCnBlbmd1aW5fZGVjaXNpb25fdHJlZQ0KYGBgDQoNCiMjIw0KDQpObyBhbnN3ZXIgcmVxdWlyZWQuDQoNCiMjIw0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEYsIGZpZy5kaW0gPSBjKDgsOCksIGZpZy5hbGlnbj0nY2VudGVyJ30NCnJwYXJ0LnBsb3QocGVuZ3Vpbl9kZWNpc2lvbl90cmVlJGZpbmFsTW9kZWwpDQpgYGANCg0KIyBWYWxpZGF0aW5nIFJlc3VsdHMgeyN2YWx9DQoNCiMjDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCiMgTG9hZCBtYWdyaXR0ciBwYWNrYWdlIGZvciBwaXBpbmcNCmxpYnJhcnkobWFncml0dHIpDQoNCiMgY291bnQgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiB2YWxpZGF0aW9uIGRhdGENCnZhbGlkYXRpb25fbnVtYmVycyA8LSBucm93KHBlbmd1aW5fdmFsaWRhdGUpDQoNCiMgVXNlIHRoZSBmaXR0ZWQgbW9kZWwgdG8gcHJlZGljdCBxdWFsaXR5IHZhbHVlcyBnaXZlbiB0aGUgdmFsaWRhdGlvbiBkYXRhDQpwcmVkaWN0X3Blbmd1aW5fZGVjaXNpb25fdHJlZSA8LSBwcmVkaWN0KHBlbmd1aW5fZGVjaXNpb25fdHJlZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID1wZW5ndWluX3ZhbGlkYXRlKQ0KIyBXaGVuIHJ1biwgdGhlIGNvZGUgYmVsb3cgZ2l2ZXMgdXMgdGhlIHBlcmNlbnRhZ2Ugb2YgY29ycmVjdCBwcmVkaWN0aW9ucw0KZGVjX3RyZWVfYWNjdXJhY3kgPC0gc3VtKHByZWRpY3RfcGVuZ3Vpbl9kZWNpc2lvbl90cmVlID09IA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBlbmd1aW5fdmFsaWRhdGUkc3BlY2llcykgLyB2YWxpZGF0aW9uX251bWJlcnMgKiAxMDANCg0KZGVjX3RyZWVfYWNjdXJhY3kgJT4lIHJvdW5kKDIpIA0KYGBgDQoNCiMjDQoNClRoZSBkZWNpc2lvbiB0cmVlIE1MIG1vZGVsIHdlIGhhdmUgdHJhaW5lZCBhcHBlYXJzIHRvIGRvIGFuIGV4Y2VsbGVudCBqb2Igb2YgcHJlZGljdGluZyB0aGUgcGVuZ3VpbiBgc3BlY2llc2AsIGZvciBib3RoIHRoZSB0cmFpbmluZyBhbmQgdmFsaWRhdGlvbiBkYXRhIHNldHMgKGByIHJvdW5kKDEwMCptYXgocGVuZ3Vpbl9kZWNpc2lvbl90cmVlJHJlc3VsdHMkQWNjdXJhY3kpLCAyKWAlIGFjY3VyYWN5IGFuZCBgciBkZWNfdHJlZV9hY2N1cmFjeSAlPiUgcm91bmQoMilgJSBhY2N1cmFjeSByZXNwZWN0aXZlbHkpLg0KDQo8YnI+DQoNCiMjIyMgR3JlYXQgd29yaywgdGhhdCdzIGV2ZXJ5dGhpbmcgZm9yIHRvZGF5LiBEb24ndCB3b3JyeSBpZiB5b3UgZGlkIG5vdCBjb21wbGV0ZSBldmVyeXRoaW5nIGluIHRoZSBkZXNpZ25hdGVkIGxhYiB0aW1lLCB0aGVyZSBpcyBhIGxvdCB0byBsZWFybiEgIyMjIyB7LX0NCg0KPGJyPg0KDQojIFJlZmVyZW5jZXMgey0gI1JlZn0NCjxkaXYgaWQ9InJlZnMiPjwvZGl2Pg0KDQo8YnI+DQoNCjxmb250IGNvbG9yID0gImdyZXkiPg0KVGhlc2Ugbm90ZXMgaGF2ZSBiZWVuIHByZXBhcmVkIGJ5IFJ1cGVydCBLdXZla2UuIFBsZWFzZSBub3RlIHRoYXQgc29tZSBvZiB0aGUgY29udGVudCBpbiB0aGVzZSBub3RlcyBoYXMgYmVlbiBkZXZlbG9wZWQgZnJvbSBjb250ZW50IGluIEBNb2RTdGF0LiBUaGUgY29weXJpZ2h0IGZvciB0aGUgbWF0ZXJpYWwgaW4gdGhlc2Ugbm90ZXMgcmVzaWRlcyB3aXRoIHRoZSBhdXRob3JzIG5hbWVkIGFib3ZlLCB3aXRoIHRoZSBEZXBhcnRtZW50IG9mIE1hdGhlbWF0aWNhbCBhbmQgUGh5c2ljYWwgU2NpZW5jZXMgYW5kIHdpdGggTGEgVHJvYmUgVW5pdmVyc2l0eS4gQ29weXJpZ2h0IGluIHRoaXMgd29yayBpcyB2ZXN0ZWQgaW4gTGEgVHJvYmUgVW5pdmVyc2l0eSBpbmNsdWRpbmcgYWxsIExhIFRyb2JlIFVuaXZlcnNpdHkgYnJhbmRpbmcgYW5kIG5hbWluZy4gVW5sZXNzIG90aGVyd2lzZSBzdGF0ZWQsIG1hdGVyaWFsIHdpdGhpbiB0aGlzIHdvcmsgaXMgbGljZW5zZWQgdW5kZXIgYSBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLU5vbiBDb21tZXJjaWFsLU5vbiBEZXJpdmF0aXZlcyBMaWNlbnNlIA0KPGEgaHJlZiA9ICJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktbmMtbmQvNC4wL0NDIiB0YXJnZXQ9Il9ibGFuayI+IEJZLU5DLU5ELiA8L2E+DQo8L2ZvbnQ+