Part 1
First lets import the data:
ads.train <- read.csv("internetads_train.csv", header=TRUE)
ads.test <- read.csv("internetads_test.csv", header=TRUE)
I tried creating a summary of the data. But there are just too many columns to visualize.
#summary(ads.train)
First lets transform all the “?” into NA’s. I coerced it when it wasn’t a numerical character.
ads.train$height = as.numeric(as.character(ads.train$height))
NAs introduced by coercion
ads.train$width = as.numeric(as.character(ads.train$width))
NAs introduced by coercion
ads.train$aratio = as.numeric(as.character(ads.train$aratio))
NAs introduced by coercion
ads.train$local = as.numeric(as.character(ads.train$local))
NAs introduced by coercion
ads.test$height = as.numeric(as.character(ads.test$height))
NAs introduced by coercion
ads.test$width = as.numeric(as.character(ads.test$width))
NAs introduced by coercion
ads.test$aratio = as.numeric(as.character(ads.test$aratio))
NAs introduced by coercion
ads.test$local = as.numeric(as.character(ads.test$local))
NAs introduced by coercion
There are many missing values, so removing those rows would cause a loss of a large amount of data. Also removing the columns which have missing data would result a loss of important information regarding the height and width of the images. The best solution I have found is to impute the missing data values using KNN. I am using an imputing function from the library DMwR to do it.
#install.packages("DMwR")
library(DMwR)
package <U+393C><U+3E31>DMwR<U+393C><U+3E32> was built under R version 3.3.3Loading required package: lattice
Loading required package: grid
ads.train.imp <- knnImputation(ads.train, k = 10, scale = F, meth = "weighAvg",distData = NULL)
ads.test.imp <- knnImputation(ads.test, k = 10, scale = F, meth = "weighAvg",distData = NULL)
Now we do not have any missing values. For many other algorithms it might be a good idea to standardize the data. But for trees this does not make sense, because the algorithms looks at how best to separate the data. Thus if one columns values are much bigger than the others, it doesn’t matter.
Now there are no more missing values in the data. So we are ready to run the tree algorithms on it.
Part 2
** Build a classification tree using all possible predictors in the training set with a maximum tree depth of 3. **
library(rpart)
Sys.setlocale("LC_ALL", "C")
[1] "C"
tree.3 <- rpart(class ~., data=ads.train.imp, cp=0.001, maxdepth = 3)
** Plot the classification tree and explain, in words, what the decision rules are for classification. **
par(mar=c(0,4.1,0,2.1))
plot(tree.3)
text(tree.3, cex=.75)

The rules go as follows:
If the image is wider than 224, the height is smaller than 94, but its height is over 58, then it is an ad.
If the image is wider than 224, and the height is bigger than 93, then it is not an ad.
If the image is not wider then 224, its destination URL contains the pattern com, and the ratio between its width and height is less than 4.585, then it probably is an ad.
If the image is not wider then 224, its destination URL contains the pattern com, and the ratio is more than 4.585, then its probably not an ad.
If the image is not wider then 224, and its destination URL does not contain the pattern com, and its URL contains the pattern for ads, then it is probably also an ad.
If the image is not wider then 224, and its destination URL does not contain the pattern com, and its URL does not contain the pattern for ads, then it is probably also not an ad.
** Then show a confusion matrix and compute the misclassification error rate on the test set. **
pred.3 <- predict(tree.3, ads.test.imp)
ads.test.imp$class2 <- factor(ifelse(ads.test.imp$class == "ad.", "yes", "no"))
pred.3.final <- ifelse(pred.3[,2] >= 0.5, "no", "yes")
mean(pred.3.final != ads.test.imp$class2)
[1] 0.06161746
table(ads.test.imp$class2, pred.3.final)
pred.3.final
no yes
no 649 12
yes 36 82
The error rate of a tree with depth 3, is 6,16%. The result does not seem great, but with a tree with more layers, we might get better results. Looking at the confusion matrix, it looks like the most common error was that for some of the images that were ads, it was classifying them as they were not.
** Now build a classification tree using all possible predictors in the training set with a maximum tree depth of 5. **
tree.5 <- rpart(class ~., data=ads.train.imp, cp=0.001, maxdepth = 5)
** Plot this new tree **
par(mar=c(0,4.1,0,2.1))
plot(tree.5)
text(tree.5, cex=.75)

** How different are the two trees, structurally, and how do their test errors compare? **
pred.5 <- predict(tree.5, ads.test.imp)
#ads.test.imp$class2 <- factor(ifelse(ads.test.imp$class == "ad.", "yes", "no"))
pred.5.final <- ifelse(pred.5[,2] >= 0.5, "no", "yes")
mean(pred.5.final != ads.test.imp$class2)
[1] 0.05519897
table(ads.test.imp$class2, pred.5.final)
pred.5.final
no yes
no 650 11
yes 32 86
The complexity of the tree has increases, as there are more levels to the tree. The first 3 layers are very similar, with everyting that existed in the previous tree with depth 3, still existing in this tree. But in the new tree there is more detail, especially if the width is over 224, and the height is lower than 93. It also has many rules when the width is under 224, and the destination URL has the com pattern.
Suprisingly though, their test errors do not make a big difference. The new error is 5,52%, which is not a huge decrease from a tree with 3 layers.
The most common type of error continues to be classifying some of the images as not advertisement, when they actually are.
Part 3
** Use bagging with 50 trees to build an ensemble classifier for these images using the training set. **
#install.packages("randomForest")
library(randomForest)
package 'randomForest' was built under R version 3.3.3randomForest 4.6-12
Type rfNews() to see new features/changes/bug fixes.
set.seed(222)
baf <- randomForest(class ~ ., data=ads.train.imp,ntree=50, importance=TRUE)
** What is the out-of-bag error rate? What test error do you obtain using this new classifier? In your own words, explain what the difference is between the two. **
baf
Call:
randomForest(formula = class ~ ., data = ads.train.imp, ntree = 50, importance = TRUE)
Type of random forest: classification
Number of trees: 50
No. of variables tried at each split: 39
OOB estimate of error rate: 2.6%
Confusion matrix:
ad. nonad. class.error
ad. 286 55 0.161290323
nonad. 10 2149 0.004631774
bag.pred <- predict(baf, ads.test.imp)
mean(bag.pred != ads.test.imp$class)
[1] 0.03080873
The out of the bag error rate is 2,6%. The error on the test data is 3,1%.
The out of the bag error rate, is the error it got total, from the bags of random samples (samples of the training data). The error on the test data, was tested on data that was not included on training the tree.
The error rate is much lower than when just using a tree of depth 5.
** Display a variable importance (or feature importance) plot associated with the bagged trees. Just include the top 10 most important variables in this plot. In a couple of sentences, interpret this plot. **
varImpPlot(baf, n.var = 10)

Above we have the variable importance plot. On the left we have the Mean decrease accuracy. The destination URL click pattern, if removed would cause the most errors when classifying, followed by the width, and the destination url paterrn http www. The mean decrease gini looks at the purity that is caused by the splits. Thas is how well that variable splits the data. the most important is the width, followed by the destination URL pattern com, and the destination URL click pattern.
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojUGFydCAxDQoNCkZpcnN0IGxldHMgaW1wb3J0IHRoZSBkYXRhOg0KDQpgYGB7cn0NCmFkcy50cmFpbiA8LSByZWFkLmNzdigiaW50ZXJuZXRhZHNfdHJhaW4uY3N2IiwgaGVhZGVyPVRSVUUpDQphZHMudGVzdCA8LSByZWFkLmNzdigiaW50ZXJuZXRhZHNfdGVzdC5jc3YiLCBoZWFkZXI9VFJVRSkNCg0KYGBgDQoNCkkgdHJpZWQgY3JlYXRpbmcgYSBzdW1tYXJ5IG9mIHRoZSBkYXRhLiBCdXQgdGhlcmUgYXJlIGp1c3QgdG9vIG1hbnkgY29sdW1ucyB0byB2aXN1YWxpemUuDQoNCmBgYHtyfQ0KI3N1bW1hcnkoYWRzLnRyYWluKQ0KYGBgDQoNCkZpcnN0IGxldHMgdHJhbnNmb3JtIGFsbCB0aGUgIj8iIGludG8gTkEncy4gSSBjb2VyY2VkIGl0IHdoZW4gaXQgd2Fzbid0IGEgbnVtZXJpY2FsIGNoYXJhY3Rlci4NCg0KYGBge3J9DQphZHMudHJhaW4kaGVpZ2h0ID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoYWRzLnRyYWluJGhlaWdodCkpDQphZHMudHJhaW4kd2lkdGggPSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihhZHMudHJhaW4kd2lkdGgpKQ0KYWRzLnRyYWluJGFyYXRpbyA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGFkcy50cmFpbiRhcmF0aW8pKQ0KYWRzLnRyYWluJGxvY2FsID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoYWRzLnRyYWluJGxvY2FsKSkNCmFkcy50ZXN0JGhlaWdodCA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGFkcy50ZXN0JGhlaWdodCkpDQphZHMudGVzdCR3aWR0aCA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGFkcy50ZXN0JHdpZHRoKSkNCmFkcy50ZXN0JGFyYXRpbyA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGFkcy50ZXN0JGFyYXRpbykpDQphZHMudGVzdCRsb2NhbCA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGFkcy50ZXN0JGxvY2FsKSkNCg0KYGBgDQoNClRoZXJlIGFyZSBtYW55IG1pc3NpbmcgdmFsdWVzLCBzbyByZW1vdmluZyB0aG9zZSByb3dzIHdvdWxkIGNhdXNlIGEgbG9zcyBvZiBhIGxhcmdlIGFtb3VudCBvZiBkYXRhLiBBbHNvIHJlbW92aW5nIHRoZSBjb2x1bW5zIHdoaWNoIGhhdmUgbWlzc2luZyBkYXRhIHdvdWxkIHJlc3VsdCBhIGxvc3Mgb2YgaW1wb3J0YW50IGluZm9ybWF0aW9uIHJlZ2FyZGluZyB0aGUgaGVpZ2h0IGFuZCB3aWR0aCBvZiB0aGUgaW1hZ2VzLiBUaGUgYmVzdCBzb2x1dGlvbiBJIGhhdmUgZm91bmQgaXMgdG8gaW1wdXRlIHRoZSBtaXNzaW5nIGRhdGEgdmFsdWVzIHVzaW5nIEtOTi4gSSBhbSB1c2luZyBhbiBpbXB1dGluZyBmdW5jdGlvbiBmcm9tIHRoZSBsaWJyYXJ5IERNd1IgdG8gZG8gaXQuDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoIkRNd1IiKQ0KbGlicmFyeShETXdSKQ0KYGBgDQoNCmBgYHtyfQ0KYWRzLnRyYWluLmltcCA8LSBrbm5JbXB1dGF0aW9uKGFkcy50cmFpbiwgayA9IDEwLCBzY2FsZSA9IEYsIG1ldGggPSAid2VpZ2hBdmciLGRpc3REYXRhID0gTlVMTCkNCmFkcy50ZXN0LmltcCA8LSBrbm5JbXB1dGF0aW9uKGFkcy50ZXN0LCBrID0gMTAsIHNjYWxlID0gRiwgbWV0aCA9ICJ3ZWlnaEF2ZyIsZGlzdERhdGEgPSBOVUxMKQ0KYGBgDQoNCk5vdyB3ZSBkbyBub3QgaGF2ZSBhbnkgbWlzc2luZyB2YWx1ZXMuIEZvciBtYW55IG90aGVyIGFsZ29yaXRobXMgaXQgbWlnaHQgYmUgYSBnb29kIGlkZWEgdG8gc3RhbmRhcmRpemUgdGhlIGRhdGEuIEJ1dCBmb3IgdHJlZXMgdGhpcyBkb2VzIG5vdCBtYWtlIHNlbnNlLCBiZWNhdXNlIHRoZSBhbGdvcml0aG1zIGxvb2tzIGF0IGhvdyBiZXN0IHRvIHNlcGFyYXRlIHRoZSBkYXRhLiBUaHVzIGlmIG9uZSBjb2x1bW5zIHZhbHVlcyBhcmUgbXVjaCBiaWdnZXIgdGhhbiB0aGUgb3RoZXJzLCBpdCBkb2Vzbid0IG1hdHRlci4NCg0KTm93IHRoZXJlIGFyZSBubyBtb3JlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBkYXRhLiBTbyB3ZSBhcmUgcmVhZHkgdG8gcnVuIHRoZSB0cmVlIGFsZ29yaXRobXMgb24gaXQuDQoNCiNQYXJ0IDINCg0KKiogQnVpbGQgYSBjbGFzc2lmaWNhdGlvbiB0cmVlIHVzaW5nIGFsbCBwb3NzaWJsZSBwcmVkaWN0b3JzIGluIHRoZSB0cmFpbmluZyBzZXQgd2l0aCBhIG1heGltdW0gdHJlZSBkZXB0aCBvZiAzLiAqKg0KDQoNCmBgYHtyfQ0KbGlicmFyeShycGFydCkNClN5cy5zZXRsb2NhbGUoIkxDX0FMTCIsICJDIikNCnRyZWUuMyA8LSBycGFydChjbGFzcyB+LiwgZGF0YT1hZHMudHJhaW4uaW1wLCBjcD0wLjAwMSwgbWF4ZGVwdGggPSAzKQ0KYGBgDQoNCioqIFBsb3QgdGhlIGNsYXNzaWZpY2F0aW9uIHRyZWUgYW5kIGV4cGxhaW4sIGluIHdvcmRzLCB3aGF0IHRoZSBkZWNpc2lvbiBydWxlcyBhcmUgZm9yIGNsYXNzaWZpY2F0aW9uLiAqKg0KDQpgYGB7cn0NCnBhcihtYXI9YygwLDQuMSwwLDIuMSkpDQpwbG90KHRyZWUuMykNCnRleHQodHJlZS4zLCBjZXg9Ljc1KQ0KYGBgDQoNClRoZSBydWxlcyBnbyBhcyBmb2xsb3dzOg0KDQoqIElmIHRoZSBpbWFnZSBpcyB3aWRlciB0aGFuIDIyNCwgdGhlIGhlaWdodCBpcyBzbWFsbGVyIHRoYW4gOTQsIGJ1dCBpdHMgaGVpZ2h0IGlzIG92ZXIgNTgsIHRoZW4gaXQgaXMgYW4gYWQuDQoNCiogSWYgdGhlIGltYWdlIGlzIHdpZGVyIHRoYW4gMjI0LCBhbmQgdGhlIGhlaWdodCBpcyBiaWdnZXIgdGhhbiA5MywgdGhlbiBpdCBpcyBub3QgYW4gYWQuDQoNCiogSWYgdGhlIGltYWdlIGlzIG5vdCB3aWRlciB0aGVuIDIyNCwgaXRzIGRlc3RpbmF0aW9uIFVSTCBjb250YWlucyB0aGUgcGF0dGVybiBjb20sIGFuZCB0aGUgcmF0aW8gYmV0d2VlbiBpdHMgd2lkdGggYW5kIGhlaWdodCBpcyBsZXNzIHRoYW4gNC41ODUsIHRoZW4gaXQgcHJvYmFibHkgaXMgYW4gYWQuIA0KDQoqIElmIHRoZSBpbWFnZSBpcyBub3Qgd2lkZXIgdGhlbiAyMjQsIGl0cyBkZXN0aW5hdGlvbiBVUkwgY29udGFpbnMgdGhlIHBhdHRlcm4gY29tLCBhbmQgdGhlIHJhdGlvIGlzIG1vcmUgdGhhbiA0LjU4NSwgdGhlbiBpdHMgcHJvYmFibHkgbm90IGFuIGFkLg0KDQoqIElmIHRoZSBpbWFnZSBpcyBub3Qgd2lkZXIgdGhlbiAyMjQsIGFuZCBpdHMgZGVzdGluYXRpb24gVVJMIGRvZXMgbm90IGNvbnRhaW4gdGhlIHBhdHRlcm4gY29tLCBhbmQgaXRzIFVSTCBjb250YWlucyB0aGUgcGF0dGVybiBmb3IgYWRzLCB0aGVuIGl0IGlzIHByb2JhYmx5IGFsc28gYW4gYWQuIA0KDQoqIElmIHRoZSBpbWFnZSBpcyBub3Qgd2lkZXIgdGhlbiAyMjQsIGFuZCBpdHMgZGVzdGluYXRpb24gVVJMIGRvZXMgbm90IGNvbnRhaW4gdGhlIHBhdHRlcm4gY29tLCBhbmQgaXRzIFVSTCBkb2VzIG5vdCBjb250YWluIHRoZSBwYXR0ZXJuIGZvciBhZHMsIHRoZW4gaXQgaXMgcHJvYmFibHkgYWxzbyBub3QgYW4gYWQuDQoNCg0KKiogVGhlbiBzaG93IGEgY29uZnVzaW9uIG1hdHJpeCBhbmQgY29tcHV0ZSB0aGUgbWlzY2xhc3NpZmljYXRpb24gZXJyb3IgcmF0ZSBvbiB0aGUgdGVzdCBzZXQuICoqDQoNCmBgYHtyfQ0KcHJlZC4zIDwtIHByZWRpY3QodHJlZS4zLCBhZHMudGVzdC5pbXApDQphZHMudGVzdC5pbXAkY2xhc3MyIDwtIGZhY3RvcihpZmVsc2UoYWRzLnRlc3QuaW1wJGNsYXNzID09ICJhZC4iLCAieWVzIiwgIm5vIikpDQpwcmVkLjMuZmluYWwgPC0gaWZlbHNlKHByZWQuM1ssMl0gPj0gMC41LCAibm8iLCAieWVzIikNCm1lYW4ocHJlZC4zLmZpbmFsICE9IGFkcy50ZXN0LmltcCRjbGFzczIpDQp0YWJsZShhZHMudGVzdC5pbXAkY2xhc3MyLCBwcmVkLjMuZmluYWwpDQpgYGANCg0KVGhlIGVycm9yIHJhdGUgb2YgYSB0cmVlIHdpdGggZGVwdGggMywgaXMgNiwxNiUuIFRoZSByZXN1bHQgZG9lcyBub3Qgc2VlbSBncmVhdCwgYnV0IHdpdGggYSB0cmVlIHdpdGggbW9yZSBsYXllcnMsIHdlIG1pZ2h0IGdldCBiZXR0ZXIgcmVzdWx0cy4gTG9va2luZyBhdCB0aGUgY29uZnVzaW9uIG1hdHJpeCwgaXQgbG9va3MgbGlrZSB0aGUgbW9zdCBjb21tb24gZXJyb3Igd2FzIHRoYXQgZm9yIHNvbWUgb2YgdGhlIGltYWdlcyB0aGF0IHdlcmUgYWRzLCBpdCB3YXMgY2xhc3NpZnlpbmcgdGhlbSBhcyB0aGV5IHdlcmUgbm90LiANCg0KKiogTm93IGJ1aWxkIGEgY2xhc3NpZmljYXRpb24gdHJlZSB1c2luZyBhbGwgcG9zc2libGUgcHJlZGljdG9ycyBpbiB0aGUgdHJhaW5pbmcgc2V0IHdpdGggYSBtYXhpbXVtIHRyZWUgZGVwdGggb2YgNS4gKioNCg0KYGBge3J9DQp0cmVlLjUgPC0gcnBhcnQoY2xhc3Mgfi4sIGRhdGE9YWRzLnRyYWluLmltcCwgY3A9MC4wMDEsIG1heGRlcHRoID0gNSkNCmBgYA0KDQoqKiBQbG90IHRoaXMgbmV3IHRyZWUgKioNCg0KYGBge3J9DQpwYXIobWFyPWMoMCw0LjEsMCwyLjEpKQ0KcGxvdCh0cmVlLjUpDQp0ZXh0KHRyZWUuNSwgY2V4PS43NSkNCmBgYA0KDQoNCioqIEhvdyBkaWZmZXJlbnQgYXJlIHRoZSB0d28gdHJlZXMsIHN0cnVjdHVyYWxseSwgYW5kIGhvdyBkbyB0aGVpciB0ZXN0IGVycm9ycyBjb21wYXJlPyAqKg0KDQpgYGB7cn0NCnByZWQuNSA8LSBwcmVkaWN0KHRyZWUuNSwgYWRzLnRlc3QuaW1wKQ0KI2Fkcy50ZXN0LmltcCRjbGFzczIgPC0gZmFjdG9yKGlmZWxzZShhZHMudGVzdC5pbXAkY2xhc3MgPT0gImFkLiIsICJ5ZXMiLCAibm8iKSkNCnByZWQuNS5maW5hbCA8LSBpZmVsc2UocHJlZC41WywyXSA+PSAwLjUsICJubyIsICJ5ZXMiKQ0KbWVhbihwcmVkLjUuZmluYWwgIT0gYWRzLnRlc3QuaW1wJGNsYXNzMikNCnRhYmxlKGFkcy50ZXN0LmltcCRjbGFzczIsIHByZWQuNS5maW5hbCkNCmBgYA0KDQpUaGUgY29tcGxleGl0eSBvZiB0aGUgdHJlZSBoYXMgaW5jcmVhc2VzLCBhcyB0aGVyZSBhcmUgbW9yZSBsZXZlbHMgdG8gdGhlIHRyZWUuIFRoZSBmaXJzdCAzIGxheWVycyBhcmUgdmVyeSBzaW1pbGFyLCB3aXRoIGV2ZXJ5dGluZyB0aGF0IGV4aXN0ZWQgaW4gdGhlIHByZXZpb3VzIHRyZWUgd2l0aCBkZXB0aCAzLCBzdGlsbCBleGlzdGluZyBpbiB0aGlzIHRyZWUuIEJ1dCBpbiB0aGUgbmV3IHRyZWUgdGhlcmUgaXMgbW9yZSBkZXRhaWwsIGVzcGVjaWFsbHkgaWYgdGhlIHdpZHRoIGlzIG92ZXIgMjI0LCBhbmQgdGhlIGhlaWdodCBpcyAgbG93ZXIgdGhhbiA5My4gSXQgYWxzbyBoYXMgbWFueSBydWxlcyB3aGVuIHRoZSB3aWR0aCBpcyB1bmRlciAyMjQsIGFuZCB0aGUgZGVzdGluYXRpb24gVVJMIGhhcyB0aGUgY29tIHBhdHRlcm4uDQoNClN1cHJpc2luZ2x5IHRob3VnaCwgdGhlaXIgdGVzdCBlcnJvcnMgZG8gbm90IG1ha2UgYSBiaWcgZGlmZmVyZW5jZS4gVGhlIG5ldyBlcnJvciBpcyA1LDUyJSwgd2hpY2ggaXMgbm90IGEgaHVnZSBkZWNyZWFzZSBmcm9tIGEgdHJlZSB3aXRoIDMgbGF5ZXJzLg0KDQpUaGUgbW9zdCBjb21tb24gdHlwZSBvZiBlcnJvciBjb250aW51ZXMgdG8gYmUgY2xhc3NpZnlpbmcgc29tZSBvZiB0aGUgaW1hZ2VzIGFzIG5vdCBhZHZlcnRpc2VtZW50LCB3aGVuIHRoZXkgYWN0dWFsbHkgYXJlLg0KDQojIFBhcnQgMw0KDQoqKiBVc2UgYmFnZ2luZyB3aXRoIDUwIHRyZWVzIHRvIGJ1aWxkIGFuIGVuc2VtYmxlIGNsYXNzaWZpZXIgZm9yIHRoZXNlIGltYWdlcyB1c2luZyB0aGUgdHJhaW5pbmcgc2V0LiAqKg0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21Gb3Jlc3QiKQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQoNCnNldC5zZWVkKDIyMikNCmJhZiA8LSByYW5kb21Gb3Jlc3QoY2xhc3MgfiAuLCBkYXRhPWFkcy50cmFpbi5pbXAsbnRyZWU9NTAsIGltcG9ydGFuY2U9VFJVRSkNCmBgYA0KDQogKiogV2hhdCBpcyB0aGUgb3V0LW9mLWJhZyBlcnJvciByYXRlPyBXaGF0IHRlc3QgZXJyb3IgZG8geW91IG9idGFpbiB1c2luZyB0aGlzIG5ldyBjbGFzc2lmaWVyPyBJbiB5b3VyIG93biB3b3JkcywgZXhwbGFpbiB3aGF0IHRoZSBkaWZmZXJlbmNlIGlzIGJldHdlZW4gdGhlIHR3by4gKioNCg0KYGBge3J9DQpiYWYNCmJhZy5wcmVkIDwtIHByZWRpY3QoYmFmLCBhZHMudGVzdC5pbXApDQptZWFuKGJhZy5wcmVkICE9IGFkcy50ZXN0LmltcCRjbGFzcykNCmBgYA0KDQpUaGUgb3V0IG9mIHRoZSBiYWcgZXJyb3IgcmF0ZSBpcyAyLDYlLg0KVGhlIGVycm9yIG9uIHRoZSB0ZXN0IGRhdGEgaXMgMywxJS4NCg0KVGhlIG91dCBvZiB0aGUgYmFnIGVycm9yIHJhdGUsIGlzIHRoZSBlcnJvciBpdCBnb3QgdG90YWwsIGZyb20gdGhlIGJhZ3Mgb2YgcmFuZG9tIHNhbXBsZXMgKHNhbXBsZXMgb2YgdGhlIHRyYWluaW5nIGRhdGEpLiBUaGUgZXJyb3Igb24gdGhlIHRlc3QgZGF0YSwgd2FzIHRlc3RlZCBvbiBkYXRhIHRoYXQgd2FzIG5vdCBpbmNsdWRlZCBvbiB0cmFpbmluZyB0aGUgdHJlZS4NCg0KVGhlIGVycm9yIHJhdGUgaXMgbXVjaCBsb3dlciB0aGFuIHdoZW4ganVzdCB1c2luZyBhIHRyZWUgb2YgZGVwdGggNS4NCg0KKiogRGlzcGxheSBhIHZhcmlhYmxlIGltcG9ydGFuY2UgKG9yIGZlYXR1cmUgaW1wb3J0YW5jZSkgcGxvdCBhc3NvY2lhdGVkIHdpdGggdGhlIGJhZ2dlZCB0cmVlcy4gSnVzdCBpbmNsdWRlIHRoZSB0b3AgMTAgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzIGluIHRoaXMgcGxvdC4gSW4gYSBjb3VwbGUgb2Ygc2VudGVuY2VzLCBpbnRlcnByZXQgdGhpcyBwbG90LiAqKg0KDQpgYGB7cn0NCnZhckltcFBsb3QoYmFmLCBuLnZhciA9IDEwKQ0KYGBgDQoNCkFib3ZlIHdlIGhhdmUgdGhlIHZhcmlhYmxlIGltcG9ydGFuY2UgcGxvdC4gT24gdGhlIGxlZnQgd2UgaGF2ZSB0aGUgTWVhbiBkZWNyZWFzZSBhY2N1cmFjeS4gVGhlIGRlc3RpbmF0aW9uIFVSTCBjbGljayBwYXR0ZXJuLCBpZiByZW1vdmVkIHdvdWxkIGNhdXNlIHRoZSBtb3N0IGVycm9ycyB3aGVuIGNsYXNzaWZ5aW5nLCBmb2xsb3dlZCBieSB0aGUgd2lkdGgsIGFuZCB0aGUgZGVzdGluYXRpb24gdXJsIHBhdGVycm4gaHR0cCB3d3cuIFRoZSBtZWFuIGRlY3JlYXNlIGdpbmkgbG9va3MgYXQgdGhlIHB1cml0eSB0aGF0IGlzIGNhdXNlZCBieSB0aGUgc3BsaXRzLiBUaGFzIGlzIGhvdyB3ZWxsIHRoYXQgdmFyaWFibGUgc3BsaXRzIHRoZSBkYXRhLiB0aGUgbW9zdCBpbXBvcnRhbnQgaXMgdGhlIHdpZHRoLCBmb2xsb3dlZCBieSB0aGUgZGVzdGluYXRpb24gVVJMIHBhdHRlcm4gY29tLCBhbmQgdGhlIGRlc3RpbmF0aW9uIFVSTCBjbGljayBwYXR0ZXJuLg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==