Question 3

Consider the Gini index, classification error, and entropy in a simple classification setting with two classes. Create a single plot that displays each of these quantities as a function of ˆpm1. The x-axis should display ˆpm1, ranging from 0 to 1, and the y-axis should display the value of the Gini index, classification error, and entropy.** hint: in a setting with two classes, ˆpm1 = 1 - ˆpm2. You could make this plot by hand, but it will be much easier in R.

p=seq(0,1,0.0001)
#Gini
G=2*p*(1-p)
#Classification Error
E=1-pmax(p,1-p)
#Entropy
D=-(p*log(p) + (1-p)*log(1-p))

plot(p,D, col="red",ylab="")
lines(p,E,col='green')
lines(p,G,col='blue')
legend(0.3,0.15,c("Entropy", "Missclassification","Gini"),lty=c(1,1,1),lwd=c(2.5,2.5,2.5),col=c('red','green','blue'))

Question 8

In the lab, a classification tree was applied to the Carseats data set after converting Sales into a qualitative response variable. Now we will seek to predict Sales using regression trees and related approaches, treating the response as a quantitative variable.

a.) Split the data

library(ISLR2)
library(tree)

data(Carseats)

set.seed(1)
train <- sample(1:nrow(Carseats), nrow(Carseats)/2)

carseats.train <- Carseats[train, ]
carseats.test <- Carseats[-train, ]

b.) Regression Tree

Fit a Regression Tree to the training set. Plot the tree, and interpret the results. What test MSE do you obtain?

tree.carseats <- tree(Sales ~ ., data = carseats.train)
summary(tree.carseats)

Regression tree:
tree(formula = Sales ~ ., data = carseats.train)
Variables actually used in tree construction:
[1] "ShelveLoc"   "Price"       "Age"         "Advertising"
[5] "CompPrice"   "US"         
Number of terminal nodes:  18 
Residual mean deviance:  2.167 = 394.3 / 182 
Distribution of residuals:
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-3.88200 -0.88200 -0.08712  0.00000  0.89590  4.09900 
plot(tree.carseats)
text(tree.carseats, pretty = 0, cex = .4)


pred <- predict(tree.carseats, newdata = carseats.test)
tree.test.mse <- mean((pred - carseats.test$Sales)^2)

cat("MSE:", tree.test.mse, "\n")
MSE: 4.922039 

The tree first splits on ShelveLoc and Price, indicating the two as the strongest predictors for Sales.

c.) Cross Validation

Use cross-validation in order to determine the optimal level of tree complexity. Does pruning the tree improve the test MSE?

cv.carseats <- cv.tree(tree.carseats)
plot(cv.carseats$size, cv.carseats$dev,
     type = "b",
     xlab = "Tree Size",
     ylab = "CV Deviance")


best.size <- cv.carseats$size[which.min(cv.carseats$dev)]
cat("Best Size:", best.size, "\n") 
Best Size: 5 
prune.carseats <- prune.tree(tree.carseats, best = best.size)
plot(prune.carseats)
text(prune.carseats, pretty = 0, cex = .6)


yhat.prune <- predict(prune.carseats, newdata = carseats.test)
prune.mse <- mean((yhat.prune - carseats.test$Sales)^2)
cat("Prune MSE:", prune.mse, "\n")
Prune MSE: 5.186482 

The error rates for the pruned and unpruned trees are very similar, indicating that pruning the tree would not make a significant difference for this data set.

d.) Bagging Approach

Use the bagging approach in order to analyze this data. What test MSE do you obtain? use the importance() function to determine which variables are most important.

library(randomForest)

set.seed(1)
p <- ncol(carseats.train) - 1

bag.carseats <-  randomForest(Sales ~ ., 
                              data = carseats.train, 
                              mtry = p, importance = TRUE)
bag.carseats

Call:
 randomForest(formula = Sales ~ ., data = carseats.train, mtry = p,      importance = TRUE) 
               Type of random forest: regression
                     Number of trees: 500
No. of variables tried at each split: 10

          Mean of squared residuals: 2.889221
                    % Var explained: 63.26
yhat.bag <- predict(bag.carseats, newdata = carseats.test)
bag.test.MSE <- mean((yhat.bag - carseats.test$Sales)^2)
cat("Bagging MSE:", bag.test.MSE, "\n")
Bagging MSE: 2.605253 
importance(bag.carseats)
               %IncMSE IncNodePurity
CompPrice   24.8888481    170.182937
Income       4.7121131     91.264880
Advertising 12.7692401     97.164338
Population  -1.8074075     58.244596
Price       56.3326252    502.903407
ShelveLoc   48.8886689    380.032715
Age         17.7275460    157.846774
Education    0.5962186     44.598731
Urban        0.1728373      9.822082
US           4.2172102     18.073863
varImpPlot(bag.carseats)

e.) Random Forest

Use random forests to analyze this data. What test MSE do you obtain? Use the importance() function to determine which variables are most important. Describe the effect of m, the number of variables considered at each split, on the error rate obtained.

set.seed(1)
mtry.values <- c(2,3,4,5,6,7,p)
rf.test.mse <- rep(NA, length(mtry.values))

for (i in seq_along(mtry.values)) {
  rf.fit <- randomForest(Sales ~ .,
                         data = carseats.train,
                         mtry = mtry.values[i], 
                         importance = TRUE)
  yhat.rf <- predict(rf.fit, 
                     newdata = carseats.test)
  rf.test.mse[i] <- mean((yhat.rf - carseats.test$Sales)^2)
}

data.frame(mtry = mtry.values, test.MSE = rf.test.mse)
plot(mtry.values,
     rf.test.mse,
     type = "b",
     pch = 19,
     xlab = "mtry",
     ylab = "Test MSE",
     main = "Random Forest Test MSE v. # of split variables tested")


best.mtry <- mtry.values[which.min(rf.test.mse)]
rf.best <- randomForest(Sales ~., 
                        data = carseats.test,
                        mtry = best.mtry,
                        importance = TRUE)

importance(rf.best)
              %IncMSE IncNodePurity
CompPrice   17.368496    121.061683
Income       8.099290     77.099105
Advertising 21.295981    146.013186
Population   1.387677     60.365860
Price       53.825774    423.426286
ShelveLoc   65.551343    587.325928
Age         13.650662    106.605549
Education    2.561897     37.091145
Urban        1.161303      9.504955
US           1.318976      7.126472
varImpPlot(rf.best)

From the plot of MSE vs Number of split variables tests, one can gather that the Test error decreases as the number of variables in each split increases. It is noted that the most significant decrease occurred when m increased from 2 to 6, where after the MSE begins to plateau, with the best MSE (2.608) being at m = 10. From the rf.best plots, we conclude that ShelveLoc and Price are the most significant predictor variables, having both the highest increasing MSE and Node Purity. The removal of either variable would substantially decrease prediction accuracy.

f.) BART

Now analyze the data using BART, and report your results

library(BART)
x <- Carseats[, -which(names(Carseats) == "Sales")]
Error in .rs.exprMutatesPackageLibrary(part) : 
  argument "part" is missing, with no default
y <- Carseats$Sales

x <- model.matrix(Sales ~. -1, data = Carseats) |> as.data.frame()

xtrain <- x[train, ]
ytrain <- y[train]
xtest <- x[-train, ]
ytest <- y[-train]

set.seed(1)
bartfit <- gbart(xtrain,
                 ytrain, 
                 x.test = xtest)
*****Calling gbart: type=1
*****Data:
data:n,p,np: 200, 12, 200
y1,yn: 2.781850, 1.091850
x1,x[n*p]: 107.000000, 1.000000
xp1,xp[np*p]: 111.000000, 1.000000
*****Number of Trees: 200
*****Number of Cut Points: 63 ... 1
*****burn,nd,thin: 100,1000,1
*****Prior:beta,alpha,tau,nu,lambda,offset: 2,0.95,0.273474,3,0.23074,7.57815
*****sigma: 1.088371
*****w (weights): 1.000000 ... 1.000000
*****Dirichlet:sparse,theta,omega,a,b,rho,augment: 0,0,1,0.5,1,12,0
*****printevery: 100

MCMC
done 0 (out of 1100)
done 100 (out of 1100)
done 200 (out of 1100)
done 300 (out of 1100)
done 400 (out of 1100)
done 500 (out of 1100)
done 600 (out of 1100)
done 700 (out of 1100)
done 800 (out of 1100)
done 900 (out of 1100)
done 1000 (out of 1100)
time: 3s
trcnt,tecnt: 1000,1000
#test error
yhat.bart <- bartfit$yhat.test.mean
bart.test.mse <- mean((ytest - yhat.bart)^2)

cat("BART MSE:", bart.test.mse, "\n")
BART MSE: 1.432639 
#check # of times each variable appeared
ord <- order(bartfit$varcount.mean,
             decreasing = TRUE)
bartfit$varcount.mean[ord]
          Price   ShelveLocGood           USYes       CompPrice 
         27.531          21.300          20.896          20.731 
      Education ShelveLocMedium    ShelveLocBad          Income 
         20.059          19.842          18.918          18.831 
            Age      Population     Advertising        UrbanYes 
         18.465          17.913          17.501          17.403 

BART outperformed the other tree-based methods tested, since its approach uses a Bayesian framework in fitting many small trees while regularizing the model. This leads to a reduction in the models tendency to fit noise on the training data, therefore reducing the chance of overfitting. Hence, BART produced the lowest MSE when compared to the rest of the tree-based methods tried.

Question 9

This Problem involves the OJ data set which is part of the ISLR2 package

data("OJ")

a.) Random training set

Create a training set containing a random sample of 800 observations, and a test containing the remaining observations.

set.seed(1)
train.oj <- sample(1:nrow(OJ), 800)

oj.train <- OJ[train.oj, ]
oj.test <- OJ[-train.oj, ]

b.) Tree

Fit a tree to the training data, with Purchase as the response and the other variables as predictors. Use the summary() function to produce summary statistics about the tree, and describe the results obtained. What is the training error rate? how many terminal nodes does the tree have?

tree.oj <- tree(Purchase ~ .,
                data = oj.train)
summary(tree.oj)

Classification tree:
tree(formula = Purchase ~ ., data = oj.train)
Variables actually used in tree construction:
[1] "LoyalCH"       "PriceDiff"     "SpecialCH"     "ListPriceDiff"
[5] "PctDiscMM"    
Number of terminal nodes:  9 
Residual mean deviance:  0.7432 = 587.8 / 791 
Misclassification error rate: 0.1588 = 127 / 800 

The variables used for the tree include: “LoyalCH”, “PriceDiff”, “SpecialCH”, “ListPriceDiff”, and “PctDiscMM”. The tree has a total of 9 terminal nodes. and an MSE of 15%.

c.) Terminal Nodes Interpretation

Type in the name of the tree object in order to get a detailed text output. Pick one of the terminal nodes, and interpret the information displayed.

tree.oj
node), split, n, deviance, yval, (yprob)
      * denotes terminal node

 1) root 800 1073.00 CH ( 0.60625 0.39375 )  
   2) LoyalCH < 0.5036 365  441.60 MM ( 0.29315 0.70685 )  
     4) LoyalCH < 0.280875 177  140.50 MM ( 0.13559 0.86441 )  
       8) LoyalCH < 0.0356415 59   10.14 MM ( 0.01695 0.98305 ) *
       9) LoyalCH > 0.0356415 118  116.40 MM ( 0.19492 0.80508 ) *
     5) LoyalCH > 0.280875 188  258.00 MM ( 0.44149 0.55851 )  
      10) PriceDiff < 0.05 79   84.79 MM ( 0.22785 0.77215 )  
        20) SpecialCH < 0.5 64   51.98 MM ( 0.14062 0.85938 ) *
        21) SpecialCH > 0.5 15   20.19 CH ( 0.60000 0.40000 ) *
      11) PriceDiff > 0.05 109  147.00 CH ( 0.59633 0.40367 ) *
   3) LoyalCH > 0.5036 435  337.90 CH ( 0.86897 0.13103 )  
     6) LoyalCH < 0.764572 174  201.00 CH ( 0.73563 0.26437 )  
      12) ListPriceDiff < 0.235 72   99.81 MM ( 0.50000 0.50000 )  
        24) PctDiscMM < 0.196196 55   73.14 CH ( 0.61818 0.38182 ) *
        25) PctDiscMM > 0.196196 17   12.32 MM ( 0.11765 0.88235 ) *
      13) ListPriceDiff > 0.235 102   65.43 CH ( 0.90196 0.09804 ) *
     7) LoyalCH > 0.764572 261   91.20 CH ( 0.95785 0.04215 ) *

Looking at terminal node 7, we note that there are 261 observations, where customers with LoyalCH great than .764 are classified as CH. Here the class probabilities for purchasing CH are 95.8% while there is a 4.2% probability of purchasing MM, with a low deviance of 91.20.

d.) Plot of Tree

Create a plot of the tree, and interpret the results.

plot(tree.oj)
text(tree.oj, pretty = 0, cex = .6)

The tree suggests that LoyalCH (customer brand loyalty for CH) has the most significance as a predictor variable. This indicates that customer loyalty is the primary factor in influencing brand choice, followed by PriceDiff, ListPriceDiff, SpecialCH, and PctDiscMM in the case of loyalty not being the singular determining factor.

e.) Confusion Matrix and Test Error

Predict the response on the test data, and produce a confusion matrix comparing the test labels to the predicted test labels. What is the test error rate?

tree.pred <- predict(tree.oj, 
                     newdata = oj.test,
                     type = "class")
conf.mat <- table(Predicted = tree.pred, 
                  Actual = oj.test$Purchase)
conf.mat
         Actual
Predicted  CH  MM
       CH 160  38
       MM   8  64
#test error
test.error <- 1 - sum(diag(conf.mat)) / sum(conf.mat)
cat("Test Error:", test.error*100, "%\n")
Test Error: 17.03704 %

f.) Cross Validation Tree

Apply the cv.tree() function to the training set in order to determine the optimal tree size.

set.seed(1)

cv.oj <- cv.tree(tree.oj, 
            FUN = prune.misclass)
cv.oj
$size
[1] 9 8 7 4 2 1

$dev
[1] 145 145 146 146 167 315

$k
[1]       -Inf   0.000000   3.000000   4.333333  10.500000 151.000000

$method
[1] "misclass"

attr(,"class")
[1] "prune"         "tree.sequence"

g.) Plot Tree Size v Error Rate

Produce a plot with tree size on th ex-axis and cross-validated classification error rate on the y-axis.

plot(cv.oj$size, 
     cv.oj$dev, 
     type = "b", 
     pch = 19,
     xlab = "Tree Size",
     ylab = "CV Error")

h.) Best Tree Size

Which tree size corresponds to the lowest cross-validated classification error rate?

best.size.oj <- cv.oj$size[which.min(cv.oj$dev)]
cat("Best Size:", best.size.oj, "\n")
Best Size: 9 

i.) Pruned Tree for Optimal Tree Size

Produce a pruned tree corresponding to the optimal tree size obtained using cross-validation. If cross-validation does not lead to selection of a pruned tree, then create a pruned tree with five terminal nodes.

size.to.use <- if(best.size.oj == max(cv.oj$size)) 5 else best.size.oj

prune.oj <- prune.misclass(tree.oj, best = size.to.use)

plot(prune.oj)
text(prune.oj, 
     pretty = 0,
     cex = .8)

j.) Pruned v Unpruned Tree Error Rates

Compare the training error rates between the pruned an unpruned trees. Which is higher?

pred.unpruned.train <- predict(tree.oj,
                               newdata = oj.train,
                               type = "class")
train.error.unpruned <- mean(pred.unpruned.train != oj.train$Purchase)

pred.pruned.train <- predict(prune.oj,
                             newdata = oj.train,
                             type = "class")
train.error.pruned <- mean(pred.pruned.train != oj.train$Purchase)

cat("Unpruned:",train.error.unpruned*100, "%\n", "Pruned:", train.error.pruned*100, "%\n")
Unpruned: 15.875 %
 Pruned: 16.25 %

k.) Comparining the Test Error Rates

Compare the test error rates between the pruned and unpruned trees. Which is higher?

Unpruned: 15.875 %
 Pruned: 16.25 %

When compared the test error results are close, with the unpruned test error rate being lower at 15.88% and the pruned test error rate being higher at 16.25%. This suggests that pruning the tree did not improve the prediction on the test set. However since the difference in the test error is so small, both models perform well, with pruning not providing any substatial difference to the test data.

LS0tDQp0aXRsZTogIlRyZWUtQmFzZWQgTWV0aG9kcyINCmF1dGhvcjogIkFzaGxleSBUb3JyZXMiDQpkYXRlOiAiMjAyNi0wNy0xNSINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdG9jLWRlcHRoOiAzDQogICAgdGhlbWU6IGNvc21vDQotLS0NCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICB3YXJuaW5nID0gRkFMU0UsDQogIG1lc3NhZ2UgPSBGQUxTRSwNCiAgcmVzdWx0cyA9ICdob2xkJywNCiAgZmlnLnNob3cgPSAnaG9sZCcNCikNCmBgYA0KDQoNCiMjIFF1ZXN0aW9uIDMNCg0KQ29uc2lkZXIgdGhlIEdpbmkgaW5kZXgsIGNsYXNzaWZpY2F0aW9uIGVycm9yLCBhbmQgZW50cm9weSBpbiBhIHNpbXBsZSBjbGFzc2lmaWNhdGlvbiBzZXR0aW5nIHdpdGggdHdvIGNsYXNzZXMuIENyZWF0ZSBhIHNpbmdsZSBwbG90IHRoYXQgZGlzcGxheXMgZWFjaCBvZiB0aGVzZSBxdWFudGl0aWVzIGFzIGEgZnVuY3Rpb24gb2Ygy4ZwbTEuIFRoZSB4LWF4aXMgc2hvdWxkIGRpc3BsYXkgy4ZwbTEsIHJhbmdpbmcgZnJvbSAwIHRvIDEsIGFuZCB0aGUgeS1heGlzIHNob3VsZCBkaXNwbGF5IHRoZSB2YWx1ZSBvZiB0aGUgR2luaSBpbmRleCwgY2xhc3NpZmljYXRpb24gZXJyb3IsIGFuZCBlbnRyb3B5LioqIA0KaGludDogaW4gYSBzZXR0aW5nIHdpdGggdHdvIGNsYXNzZXMsIMuGcG0xID0gMSAtIMuGcG0yLiBZb3UgY291bGQgbWFrZSB0aGlzIHBsb3QgYnkgaGFuZCwgYnV0IGl0IHdpbGwgYmUgbXVjaCBlYXNpZXIgaW4gUi4NCg0KYGBge3J9DQpwPXNlcSgwLDEsMC4wMDAxKQ0KI0dpbmkNCkc9MipwKigxLXApDQojQ2xhc3NpZmljYXRpb24gRXJyb3INCkU9MS1wbWF4KHAsMS1wKQ0KI0VudHJvcHkNCkQ9LShwKmxvZyhwKSArICgxLXApKmxvZygxLXApKQ0KDQpwbG90KHAsRCwgY29sPSJyZWQiLHlsYWI9IiIpDQpsaW5lcyhwLEUsY29sPSdncmVlbicpDQpsaW5lcyhwLEcsY29sPSdibHVlJykNCmxlZ2VuZCgwLjMsMC4xNSxjKCJFbnRyb3B5IiwgIk1pc3NjbGFzc2lmaWNhdGlvbiIsIkdpbmkiKSxsdHk9YygxLDEsMSksbHdkPWMoMi41LDIuNSwyLjUpLGNvbD1jKCdyZWQnLCdncmVlbicsJ2JsdWUnKSkNCmBgYA0KDQoNCiMjIFF1ZXN0aW9uIDgNCg0KSW4gdGhlIGxhYiwgYSBjbGFzc2lmaWNhdGlvbiB0cmVlIHdhcyBhcHBsaWVkIHRvIHRoZSBDYXJzZWF0cyBkYXRhIHNldCBhZnRlciBjb252ZXJ0aW5nIFNhbGVzIGludG8gYSBxdWFsaXRhdGl2ZSByZXNwb25zZSB2YXJpYWJsZS4gTm93IHdlIHdpbGwgc2VlayB0byBwcmVkaWN0IFNhbGVzIHVzaW5nIHJlZ3Jlc3Npb24gdHJlZXMgYW5kIHJlbGF0ZWQgYXBwcm9hY2hlcywgdHJlYXRpbmcgdGhlIHJlc3BvbnNlIGFzIGEgcXVhbnRpdGF0aXZlIHZhcmlhYmxlLiANCg0KDQojIyMgYS4pIFNwbGl0IHRoZSBkYXRhIA0KDQpgYGB7cn0NCmxpYnJhcnkoSVNMUjIpDQpsaWJyYXJ5KHRyZWUpDQoNCmRhdGEoQ2Fyc2VhdHMpDQoNCnNldC5zZWVkKDEpDQp0cmFpbiA8LSBzYW1wbGUoMTpucm93KENhcnNlYXRzKSwgbnJvdyhDYXJzZWF0cykvMikNCg0KY2Fyc2VhdHMudHJhaW4gPC0gQ2Fyc2VhdHNbdHJhaW4sIF0NCmNhcnNlYXRzLnRlc3QgPC0gQ2Fyc2VhdHNbLXRyYWluLCBdDQpgYGANCg0KDQoNCiMjIyBiLikgUmVncmVzc2lvbiBUcmVlIA0KDQpGaXQgYSBSZWdyZXNzaW9uIFRyZWUgdG8gdGhlIHRyYWluaW5nIHNldC4gUGxvdCB0aGUgdHJlZSwgYW5kIGludGVycHJldCB0aGUgcmVzdWx0cy4gV2hhdCB0ZXN0IE1TRSBkbyB5b3Ugb2J0YWluPw0KDQoNCmBgYHtyfQ0KdHJlZS5jYXJzZWF0cyA8LSB0cmVlKFNhbGVzIH4gLiwgZGF0YSA9IGNhcnNlYXRzLnRyYWluKQ0Kc3VtbWFyeSh0cmVlLmNhcnNlYXRzKQ0KDQpwbG90KHRyZWUuY2Fyc2VhdHMpDQp0ZXh0KHRyZWUuY2Fyc2VhdHMsIHByZXR0eSA9IDAsIGNleCA9IC40KQ0KDQpwcmVkIDwtIHByZWRpY3QodHJlZS5jYXJzZWF0cywgbmV3ZGF0YSA9IGNhcnNlYXRzLnRlc3QpDQp0cmVlLnRlc3QubXNlIDwtIG1lYW4oKHByZWQgLSBjYXJzZWF0cy50ZXN0JFNhbGVzKV4yKQ0KDQpjYXQoIk1TRToiLCB0cmVlLnRlc3QubXNlLCAiXG4iKQ0KYGBgDQpUaGUgdHJlZSBmaXJzdCBzcGxpdHMgb24gU2hlbHZlTG9jIGFuZCBQcmljZSwgaW5kaWNhdGluZyB0aGUgdHdvIGFzIHRoZSBzdHJvbmdlc3QgcHJlZGljdG9ycyBmb3IgU2FsZXMuIA0KDQoNCiMjIyBjLikgQ3Jvc3MgVmFsaWRhdGlvbg0KDQpVc2UgY3Jvc3MtdmFsaWRhdGlvbiBpbiBvcmRlciB0byBkZXRlcm1pbmUgdGhlIG9wdGltYWwgbGV2ZWwgb2YgdHJlZSBjb21wbGV4aXR5LiBEb2VzIHBydW5pbmcgdGhlIHRyZWUgaW1wcm92ZSB0aGUgdGVzdCBNU0U/DQoNCg0KYGBge3J9DQpjdi5jYXJzZWF0cyA8LSBjdi50cmVlKHRyZWUuY2Fyc2VhdHMpDQpwbG90KGN2LmNhcnNlYXRzJHNpemUsIGN2LmNhcnNlYXRzJGRldiwNCiAgICAgdHlwZSA9ICJiIiwNCiAgICAgeGxhYiA9ICJUcmVlIFNpemUiLA0KICAgICB5bGFiID0gIkNWIERldmlhbmNlIikNCg0KYmVzdC5zaXplIDwtIGN2LmNhcnNlYXRzJHNpemVbd2hpY2gubWluKGN2LmNhcnNlYXRzJGRldildDQpjYXQoIkJlc3QgU2l6ZToiLCBiZXN0LnNpemUsICJcbiIpIA0KDQpwcnVuZS5jYXJzZWF0cyA8LSBwcnVuZS50cmVlKHRyZWUuY2Fyc2VhdHMsIGJlc3QgPSBiZXN0LnNpemUpDQpwbG90KHBydW5lLmNhcnNlYXRzKQ0KdGV4dChwcnVuZS5jYXJzZWF0cywgcHJldHR5ID0gMCwgY2V4ID0gLjYpDQoNCnloYXQucHJ1bmUgPC0gcHJlZGljdChwcnVuZS5jYXJzZWF0cywgbmV3ZGF0YSA9IGNhcnNlYXRzLnRlc3QpDQpwcnVuZS5tc2UgPC0gbWVhbigoeWhhdC5wcnVuZSAtIGNhcnNlYXRzLnRlc3QkU2FsZXMpXjIpDQpjYXQoIlBydW5lIE1TRToiLCBwcnVuZS5tc2UsICJcbiIpDQoNCmBgYA0KDQpUaGUgZXJyb3IgcmF0ZXMgZm9yIHRoZSBwcnVuZWQgYW5kIHVucHJ1bmVkIHRyZWVzIGFyZSB2ZXJ5IHNpbWlsYXIsIGluZGljYXRpbmcgdGhhdCBwcnVuaW5nIHRoZSB0cmVlIHdvdWxkIG5vdCBtYWtlIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBmb3IgdGhpcyBkYXRhIHNldC4gDQoNCiMjIyBkLikgQmFnZ2luZyBBcHByb2FjaA0KDQpVc2UgdGhlIGJhZ2dpbmcgYXBwcm9hY2ggaW4gb3JkZXIgdG8gYW5hbHl6ZSB0aGlzIGRhdGEuIFdoYXQgdGVzdCBNU0UgZG8geW91IG9idGFpbj8gdXNlIHRoZSBpbXBvcnRhbmNlKCkgZnVuY3Rpb24gdG8gZGV0ZXJtaW5lIHdoaWNoIHZhcmlhYmxlcyBhcmUgbW9zdCBpbXBvcnRhbnQuDQoNCmBgYHtyfQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQoNCnNldC5zZWVkKDEpDQpwIDwtIG5jb2woY2Fyc2VhdHMudHJhaW4pIC0gMQ0KDQpiYWcuY2Fyc2VhdHMgPC0gIHJhbmRvbUZvcmVzdChTYWxlcyB+IC4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGNhcnNlYXRzLnRyYWluLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG10cnkgPSBwLCBpbXBvcnRhbmNlID0gVFJVRSkNCmJhZy5jYXJzZWF0cw0KDQp5aGF0LmJhZyA8LSBwcmVkaWN0KGJhZy5jYXJzZWF0cywgbmV3ZGF0YSA9IGNhcnNlYXRzLnRlc3QpDQpiYWcudGVzdC5NU0UgPC0gbWVhbigoeWhhdC5iYWcgLSBjYXJzZWF0cy50ZXN0JFNhbGVzKV4yKQ0KY2F0KCJCYWdnaW5nIE1TRToiLCBiYWcudGVzdC5NU0UsICJcbiIpDQoNCmltcG9ydGFuY2UoYmFnLmNhcnNlYXRzKQ0KdmFySW1wUGxvdChiYWcuY2Fyc2VhdHMpDQpgYGANCg0KDQoNCiMjIyBlLikgUmFuZG9tIEZvcmVzdCANCg0KVXNlIHJhbmRvbSBmb3Jlc3RzIHRvIGFuYWx5emUgdGhpcyBkYXRhLiBXaGF0IHRlc3QgTVNFIGRvIHlvdSBvYnRhaW4/IFVzZSB0aGUgaW1wb3J0YW5jZSgpIGZ1bmN0aW9uIHRvIGRldGVybWluZSB3aGljaCB2YXJpYWJsZXMgYXJlIG1vc3QgaW1wb3J0YW50LiBEZXNjcmliZSB0aGUgZWZmZWN0IG9mIG0sIHRoZSBudW1iZXIgb2YgdmFyaWFibGVzIGNvbnNpZGVyZWQgYXQgZWFjaCBzcGxpdCwgb24gdGhlIGVycm9yIHJhdGUgb2J0YWluZWQuDQoNCg0KYGBge3J9DQpzZXQuc2VlZCgxKQ0KbXRyeS52YWx1ZXMgPC0gYygyLDMsNCw1LDYsNyxwKQ0KcmYudGVzdC5tc2UgPC0gcmVwKE5BLCBsZW5ndGgobXRyeS52YWx1ZXMpKQ0KDQpmb3IgKGkgaW4gc2VxX2Fsb25nKG10cnkudmFsdWVzKSkgew0KICByZi5maXQgPC0gcmFuZG9tRm9yZXN0KFNhbGVzIH4gLiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gY2Fyc2VhdHMudHJhaW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgbXRyeSA9IG10cnkudmFsdWVzW2ldLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBpbXBvcnRhbmNlID0gVFJVRSkNCiAgeWhhdC5yZiA8LSBwcmVkaWN0KHJmLmZpdCwgDQogICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gY2Fyc2VhdHMudGVzdCkNCiAgcmYudGVzdC5tc2VbaV0gPC0gbWVhbigoeWhhdC5yZiAtIGNhcnNlYXRzLnRlc3QkU2FsZXMpXjIpDQp9DQoNCmRhdGEuZnJhbWUobXRyeSA9IG10cnkudmFsdWVzLCB0ZXN0Lk1TRSA9IHJmLnRlc3QubXNlKQ0KcGxvdChtdHJ5LnZhbHVlcywNCiAgICAgcmYudGVzdC5tc2UsDQogICAgIHR5cGUgPSAiYiIsDQogICAgIHBjaCA9IDE5LA0KICAgICB4bGFiID0gIm10cnkiLA0KICAgICB5bGFiID0gIlRlc3QgTVNFIiwNCiAgICAgbWFpbiA9ICJSYW5kb20gRm9yZXN0IFRlc3QgTVNFIHYuICMgb2Ygc3BsaXQgdmFyaWFibGVzIHRlc3RlZCIpDQoNCmJlc3QubXRyeSA8LSBtdHJ5LnZhbHVlc1t3aGljaC5taW4ocmYudGVzdC5tc2UpXQ0KcmYuYmVzdCA8LSByYW5kb21Gb3Jlc3QoU2FsZXMgfi4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGNhcnNlYXRzLnRlc3QsDQogICAgICAgICAgICAgICAgICAgICAgICBtdHJ5ID0gYmVzdC5tdHJ5LA0KICAgICAgICAgICAgICAgICAgICAgICAgaW1wb3J0YW5jZSA9IFRSVUUpDQoNCmltcG9ydGFuY2UocmYuYmVzdCkNCnZhckltcFBsb3QocmYuYmVzdCkNCg0KYGBgDQoNCkZyb20gdGhlIHBsb3Qgb2YgTVNFIHZzIE51bWJlciBvZiBzcGxpdCB2YXJpYWJsZXMgdGVzdHMsIG9uZSBjYW4gZ2F0aGVyIHRoYXQgdGhlIFRlc3QgZXJyb3IgZGVjcmVhc2VzIGFzIHRoZSBudW1iZXIgb2YgdmFyaWFibGVzIGluIGVhY2ggc3BsaXQgaW5jcmVhc2VzLiBJdCBpcyBub3RlZCB0aGF0IHRoZSBtb3N0IHNpZ25pZmljYW50IGRlY3JlYXNlIG9jY3VycmVkIHdoZW4gbSBpbmNyZWFzZWQgZnJvbSAyIHRvIDYsIHdoZXJlIGFmdGVyIHRoZSBNU0UgYmVnaW5zIHRvIHBsYXRlYXUsIHdpdGggdGhlIGJlc3QgTVNFICgyLjYwOCkgYmVpbmcgYXQgbSA9IDEwLiBGcm9tIHRoZSByZi5iZXN0IHBsb3RzLCB3ZSBjb25jbHVkZSB0aGF0IFNoZWx2ZUxvYyBhbmQgUHJpY2UgYXJlIHRoZSBtb3N0IHNpZ25pZmljYW50IHByZWRpY3RvciB2YXJpYWJsZXMsIGhhdmluZyBib3RoIHRoZSBoaWdoZXN0IGluY3JlYXNpbmcgTVNFIGFuZCBOb2RlIFB1cml0eS4gVGhlIHJlbW92YWwgb2YgZWl0aGVyIHZhcmlhYmxlIHdvdWxkIHN1YnN0YW50aWFsbHkgZGVjcmVhc2UgcHJlZGljdGlvbiBhY2N1cmFjeS4gDQoNCiMjIyBmLikgQkFSVCANCg0KTm93IGFuYWx5emUgdGhlIGRhdGEgdXNpbmcgQkFSVCwgYW5kIHJlcG9ydCB5b3VyIHJlc3VsdHMNCg0KDQpgYGB7cn0NCmxpYnJhcnkoQkFSVCkNCnggPC0gQ2Fyc2VhdHNbLCAtd2hpY2gobmFtZXMoQ2Fyc2VhdHMpID09ICJTYWxlcyIpXQ0KeSA8LSBDYXJzZWF0cyRTYWxlcw0KDQp4IDwtIG1vZGVsLm1hdHJpeChTYWxlcyB+LiAtMSwgZGF0YSA9IENhcnNlYXRzKSB8PiBhcy5kYXRhLmZyYW1lKCkNCg0KeHRyYWluIDwtIHhbdHJhaW4sIF0NCnl0cmFpbiA8LSB5W3RyYWluXQ0KeHRlc3QgPC0geFstdHJhaW4sIF0NCnl0ZXN0IDwtIHlbLXRyYWluXQ0KDQpzZXQuc2VlZCgxKQ0KYmFydGZpdCA8LSBnYmFydCh4dHJhaW4sDQogICAgICAgICAgICAgICAgIHl0cmFpbiwgDQogICAgICAgICAgICAgICAgIHgudGVzdCA9IHh0ZXN0KQ0KDQojdGVzdCBlcnJvcg0KeWhhdC5iYXJ0IDwtIGJhcnRmaXQkeWhhdC50ZXN0Lm1lYW4NCmJhcnQudGVzdC5tc2UgPC0gbWVhbigoeXRlc3QgLSB5aGF0LmJhcnQpXjIpDQoNCmNhdCgiQkFSVCBNU0U6IiwgYmFydC50ZXN0Lm1zZSwgIlxuIikNCg0KI2NoZWNrICMgb2YgdGltZXMgZWFjaCB2YXJpYWJsZSBhcHBlYXJlZA0Kb3JkIDwtIG9yZGVyKGJhcnRmaXQkdmFyY291bnQubWVhbiwNCiAgICAgICAgICAgICBkZWNyZWFzaW5nID0gVFJVRSkNCmJhcnRmaXQkdmFyY291bnQubWVhbltvcmRdDQoNCmBgYA0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoRFQpDQptc2UuZGYgPC0gZGF0YS5mcmFtZSgNCiAgTW9kZWwgPSBjKCJTaW5nbGUgVHJlZSIsICJQcnVuZWQgVHJlZSIsICJCYWdnaW5nIiwgIlJhbmRvbSBGb3Jlc3QiLCAiQkFSVCIpLA0KICBNU0UgPSBjKHRyZWUudGVzdC5tc2UsDQogICAgICAgICAgcHJ1bmUubXNlLA0KICAgICAgICAgIGJhZy50ZXN0Lk1TRSwNCiAgICAgICAgICBtaW4ocmYudGVzdC5tc2UpLA0KICAgICAgICAgIGJhcnQudGVzdC5tc2UpDQopDQoNCmdncGxvdChtc2UuZGYsIGFlcyh4ID0gcmVvcmRlcihNb2RlbCwgTVNFKSwgeSA9IE1TRSkpICsNCiAgZ2VvbV9jb2woKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGxhYnModGl0bGUgPSAiQ29tcGFyaXNvbiBvZiBUZXN0IE1TRSBieSBNb2RlbCIsDQogICAgICAgeCA9ICJNb2RlbCIsDQogICAgICAgeSA9ICJUZXN0IE1TRSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmRhdGF0YWJsZSgNCiAgbXNlLmRmLA0KICBjYXB0aW9uID0gIkNvbXBhcmlzb24gb2YgVGVzdCBFcnJvcnMiLA0KICBvcHRpb25zID0gbGlzdCgNCiAgICBwYWdlTGVuZ3RoID0gNSwNCiAgICBkb20gPSAidCIsDQogICAgb3JkZXJpbmcgPSBUUlVFDQogICkNCikNCmBgYA0KDQpCQVJUIG91dHBlcmZvcm1lZCB0aGUgb3RoZXIgdHJlZS1iYXNlZCBtZXRob2RzIHRlc3RlZCwgc2luY2UgaXRzIGFwcHJvYWNoIHVzZXMgYSBCYXllc2lhbiBmcmFtZXdvcmsgaW4gZml0dGluZyBtYW55IHNtYWxsIHRyZWVzIHdoaWxlIHJlZ3VsYXJpemluZyB0aGUgbW9kZWwuIFRoaXMgbGVhZHMgdG8gYSByZWR1Y3Rpb24gaW4gdGhlIG1vZGVscyB0ZW5kZW5jeSB0byBmaXQgbm9pc2Ugb24gdGhlIHRyYWluaW5nIGRhdGEsIHRoZXJlZm9yZSByZWR1Y2luZyB0aGUgY2hhbmNlIG9mIG92ZXJmaXR0aW5nLiBIZW5jZSwgQkFSVCBwcm9kdWNlZCB0aGUgbG93ZXN0IE1TRSB3aGVuIGNvbXBhcmVkIHRvIHRoZSByZXN0IG9mIHRoZSB0cmVlLWJhc2VkIG1ldGhvZHMgdHJpZWQuDQoNCiMjIFF1ZXN0aW9uIDkNCg0KVGhpcyBQcm9ibGVtIGludm9sdmVzIHRoZSBPSiBkYXRhIHNldCB3aGljaCBpcyBwYXJ0IG9mIHRoZSBJU0xSMiBwYWNrYWdlDQoNCmBgYHtyfQ0KZGF0YSgiT0oiKQ0KYGBgDQoNCiMjIyBhLikgUmFuZG9tIHRyYWluaW5nIHNldA0KDQpDcmVhdGUgYSB0cmFpbmluZyBzZXQgY29udGFpbmluZyBhIHJhbmRvbSBzYW1wbGUgb2YgODAwIG9ic2VydmF0aW9ucywgYW5kIGEgdGVzdCBjb250YWluaW5nIHRoZSByZW1haW5pbmcgb2JzZXJ2YXRpb25zLg0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQp0cmFpbi5vaiA8LSBzYW1wbGUoMTpucm93KE9KKSwgODAwKQ0KDQpvai50cmFpbiA8LSBPSlt0cmFpbi5vaiwgXQ0Kb2oudGVzdCA8LSBPSlstdHJhaW4ub2osIF0NCg0KYGBgDQoNCg0KIyMjIGIuKSBUcmVlDQoNCkZpdCBhIHRyZWUgdG8gdGhlIHRyYWluaW5nIGRhdGEsIHdpdGggUHVyY2hhc2UgYXMgdGhlIHJlc3BvbnNlIGFuZCB0aGUgb3RoZXIgdmFyaWFibGVzIGFzIHByZWRpY3RvcnMuIFVzZSB0aGUgc3VtbWFyeSgpIGZ1bmN0aW9uIHRvIHByb2R1Y2Ugc3VtbWFyeSBzdGF0aXN0aWNzIGFib3V0IHRoZSB0cmVlLCBhbmQgZGVzY3JpYmUgdGhlIHJlc3VsdHMgb2J0YWluZWQuIFdoYXQgaXMgdGhlIHRyYWluaW5nIGVycm9yIHJhdGU/IGhvdyBtYW55IHRlcm1pbmFsIG5vZGVzIGRvZXMgdGhlIHRyZWUgaGF2ZT8NCg0KDQpgYGB7cn0NCnRyZWUub2ogPC0gdHJlZShQdXJjaGFzZSB+IC4sDQogICAgICAgICAgICAgICAgZGF0YSA9IG9qLnRyYWluKQ0Kc3VtbWFyeSh0cmVlLm9qKQ0KYGBgDQoNClRoZSB2YXJpYWJsZXMgdXNlZCBmb3IgdGhlIHRyZWUgaW5jbHVkZTogIkxveWFsQ0giLCAiUHJpY2VEaWZmIiwgIlNwZWNpYWxDSCIsICJMaXN0UHJpY2VEaWZmIiwgYW5kICJQY3REaXNjTU0iLiBUaGUgdHJlZSBoYXMgYSB0b3RhbCBvZiA5IHRlcm1pbmFsIG5vZGVzLiBhbmQgYW4gTVNFIG9mIDE1JS4NCg0KIyMjIGMuKSBUZXJtaW5hbCBOb2RlcyBJbnRlcnByZXRhdGlvbg0KDQoNClR5cGUgaW4gdGhlIG5hbWUgb2YgdGhlIHRyZWUgb2JqZWN0IGluIG9yZGVyIHRvIGdldCBhIGRldGFpbGVkIHRleHQgb3V0cHV0LiBQaWNrIG9uZSBvZiB0aGUgdGVybWluYWwgbm9kZXMsIGFuZCBpbnRlcnByZXQgdGhlIGluZm9ybWF0aW9uIGRpc3BsYXllZC4NCg0KYGBge3J9DQp0cmVlLm9qDQpgYGANCkxvb2tpbmcgYXQgdGVybWluYWwgbm9kZSA3LCB3ZSBub3RlIHRoYXQgdGhlcmUgYXJlIDI2MSBvYnNlcnZhdGlvbnMsIHdoZXJlIGN1c3RvbWVycyB3aXRoIExveWFsQ0ggZ3JlYXQgdGhhbiAuNzY0IGFyZSBjbGFzc2lmaWVkIGFzIENILiBIZXJlIHRoZSBjbGFzcyBwcm9iYWJpbGl0aWVzIGZvciBwdXJjaGFzaW5nIENIIGFyZSA5NS44JSB3aGlsZSB0aGVyZSBpcyBhIDQuMiUgcHJvYmFiaWxpdHkgb2YgcHVyY2hhc2luZyBNTSwgd2l0aCBhIGxvdyBkZXZpYW5jZSBvZiA5MS4yMC4gDQoNCiMjIyBkLikgUGxvdCBvZiBUcmVlDQoNCkNyZWF0ZSBhIHBsb3Qgb2YgdGhlIHRyZWUsIGFuZCBpbnRlcnByZXQgdGhlIHJlc3VsdHMuDQoNCmBgYHtyfQ0KcGxvdCh0cmVlLm9qKQ0KdGV4dCh0cmVlLm9qLCBwcmV0dHkgPSAwLCBjZXggPSAuNikNCmBgYA0KDQpUaGUgdHJlZSBzdWdnZXN0cyB0aGF0IExveWFsQ0ggKGN1c3RvbWVyIGJyYW5kIGxveWFsdHkgZm9yIENIKSBoYXMgdGhlIG1vc3Qgc2lnbmlmaWNhbmNlIGFzIGEgcHJlZGljdG9yIHZhcmlhYmxlLiBUaGlzIGluZGljYXRlcyB0aGF0IGN1c3RvbWVyIGxveWFsdHkgaXMgdGhlIHByaW1hcnkgZmFjdG9yIGluIGluZmx1ZW5jaW5nIGJyYW5kIGNob2ljZSwgZm9sbG93ZWQgYnkgUHJpY2VEaWZmLCBMaXN0UHJpY2VEaWZmLCBTcGVjaWFsQ0gsIGFuZCBQY3REaXNjTU0gaW4gdGhlIGNhc2Ugb2YgbG95YWx0eSBub3QgYmVpbmcgdGhlIHNpbmd1bGFyIGRldGVybWluaW5nIGZhY3Rvci4gDQoNCiMjIyBlLikgQ29uZnVzaW9uIE1hdHJpeCBhbmQgVGVzdCBFcnJvcg0KDQoNClByZWRpY3QgdGhlIHJlc3BvbnNlIG9uIHRoZSB0ZXN0IGRhdGEsIGFuZCBwcm9kdWNlIGEgY29uZnVzaW9uIG1hdHJpeCBjb21wYXJpbmcgdGhlIHRlc3QgbGFiZWxzIHRvIHRoZSBwcmVkaWN0ZWQgdGVzdCBsYWJlbHMuIFdoYXQgaXMgdGhlIHRlc3QgZXJyb3IgcmF0ZT8NCg0KYGBge3J9DQp0cmVlLnByZWQgPC0gcHJlZGljdCh0cmVlLm9qLCANCiAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBvai50ZXN0LA0KICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJjbGFzcyIpDQpjb25mLm1hdCA8LSB0YWJsZShQcmVkaWN0ZWQgPSB0cmVlLnByZWQsIA0KICAgICAgICAgICAgICAgICAgQWN0dWFsID0gb2oudGVzdCRQdXJjaGFzZSkNCmNvbmYubWF0DQojdGVzdCBlcnJvcg0KdGVzdC5lcnJvciA8LSAxIC0gc3VtKGRpYWcoY29uZi5tYXQpKSAvIHN1bShjb25mLm1hdCkNCmNhdCgiVGVzdCBFcnJvcjoiLCB0ZXN0LmVycm9yKjEwMCwgIiVcbiIpDQpgYGANCg0KDQojIyMgZi4pIENyb3NzIFZhbGlkYXRpb24gVHJlZQ0KDQpBcHBseSB0aGUgY3YudHJlZSgpIGZ1bmN0aW9uIHRvIHRoZSB0cmFpbmluZyBzZXQgaW4gb3JkZXIgdG8gZGV0ZXJtaW5lIHRoZSBvcHRpbWFsIHRyZWUgc2l6ZS4NCg0KYGBge3J9DQpzZXQuc2VlZCgxKQ0KDQpjdi5vaiA8LSBjdi50cmVlKHRyZWUub2osIA0KICAgICAgICAgICAgRlVOID0gcHJ1bmUubWlzY2xhc3MpDQpjdi5vag0KYGBgDQoNCg0KIyMjIGcuKSBQbG90IFRyZWUgU2l6ZSB2IEVycm9yIFJhdGUNCg0KUHJvZHVjZSBhIHBsb3Qgd2l0aCB0cmVlIHNpemUgb24gdGggZXgtYXhpcyBhbmQgY3Jvc3MtdmFsaWRhdGVkIGNsYXNzaWZpY2F0aW9uIGVycm9yIHJhdGUgb24gdGhlIHktYXhpcy4NCg0KYGBge3J9DQpwbG90KGN2Lm9qJHNpemUsIA0KICAgICBjdi5vaiRkZXYsIA0KICAgICB0eXBlID0gImIiLCANCiAgICAgcGNoID0gMTksDQogICAgIHhsYWIgPSAiVHJlZSBTaXplIiwNCiAgICAgeWxhYiA9ICJDViBFcnJvciIpDQpgYGANCg0KDQojIyMgaC4pIEJlc3QgVHJlZSBTaXplDQoNCldoaWNoIHRyZWUgc2l6ZSBjb3JyZXNwb25kcyB0byB0aGUgbG93ZXN0IGNyb3NzLXZhbGlkYXRlZCBjbGFzc2lmaWNhdGlvbiBlcnJvciByYXRlPw0KDQpgYGB7cn0NCmJlc3Quc2l6ZS5vaiA8LSBjdi5vaiRzaXplW3doaWNoLm1pbihjdi5vaiRkZXYpXQ0KY2F0KCJCZXN0IFNpemU6IiwgYmVzdC5zaXplLm9qLCAiXG4iKQ0KYGBgDQoNCg0KIyMjIGkuKSBQcnVuZWQgVHJlZSBmb3IgT3B0aW1hbCBUcmVlIFNpemUNCg0KUHJvZHVjZSBhIHBydW5lZCB0cmVlIGNvcnJlc3BvbmRpbmcgdG8gdGhlIG9wdGltYWwgdHJlZSBzaXplIG9idGFpbmVkIHVzaW5nIGNyb3NzLXZhbGlkYXRpb24uIElmIGNyb3NzLXZhbGlkYXRpb24gZG9lcyBub3QgbGVhZCB0byBzZWxlY3Rpb24gb2YgYSBwcnVuZWQgdHJlZSwgdGhlbiBjcmVhdGUgYSBwcnVuZWQgdHJlZSB3aXRoIGZpdmUgdGVybWluYWwgbm9kZXMuDQoNCmBgYHtyfQ0Kc2l6ZS50by51c2UgPC0gaWYoYmVzdC5zaXplLm9qID09IG1heChjdi5vaiRzaXplKSkgNSBlbHNlIGJlc3Quc2l6ZS5vag0KDQpwcnVuZS5vaiA8LSBwcnVuZS5taXNjbGFzcyh0cmVlLm9qLCBiZXN0ID0gc2l6ZS50by51c2UpDQoNCnBsb3QocHJ1bmUub2opDQp0ZXh0KHBydW5lLm9qLCANCiAgICAgcHJldHR5ID0gMCwNCiAgICAgY2V4ID0gLjgpDQpgYGANCg0KDQojIyMgai4pIFBydW5lZCB2IFVucHJ1bmVkIFRyZWUgRXJyb3IgUmF0ZXMNCg0KQ29tcGFyZSB0aGUgdHJhaW5pbmcgZXJyb3IgcmF0ZXMgYmV0d2VlbiB0aGUgcHJ1bmVkIGFuIHVucHJ1bmVkIHRyZWVzLiBXaGljaCBpcyBoaWdoZXI/DQoNCmBgYHtyfQ0KcHJlZC51bnBydW5lZC50cmFpbiA8LSBwcmVkaWN0KHRyZWUub2osDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IG9qLnRyYWluLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiY2xhc3MiKQ0KdHJhaW4uZXJyb3IudW5wcnVuZWQgPC0gbWVhbihwcmVkLnVucHJ1bmVkLnRyYWluICE9IG9qLnRyYWluJFB1cmNoYXNlKQ0KDQpwcmVkLnBydW5lZC50cmFpbiA8LSBwcmVkaWN0KHBydW5lLm9qLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gb2oudHJhaW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiY2xhc3MiKQ0KdHJhaW4uZXJyb3IucHJ1bmVkIDwtIG1lYW4ocHJlZC5wcnVuZWQudHJhaW4gIT0gb2oudHJhaW4kUHVyY2hhc2UpDQoNCmNhdCgiVW5wcnVuZWQ6Iix0cmFpbi5lcnJvci51bnBydW5lZCoxMDAsICIlXG4iLCAiUHJ1bmVkOiIsIHRyYWluLmVycm9yLnBydW5lZCoxMDAsICIlXG4iKQ0KDQpgYGANCg0KDQojIyMgay4pIENvbXBhcmluaW5nIHRoZSBUZXN0IEVycm9yIFJhdGVzDQoNCkNvbXBhcmUgdGhlIHRlc3QgZXJyb3IgcmF0ZXMgYmV0d2VlbiB0aGUgcHJ1bmVkIGFuZCB1bnBydW5lZCB0cmVlcy4gV2hpY2ggaXMgaGlnaGVyPw0KDQpgYGB7ciwgZWNobyA9IEZBTFNFfQ0KY2F0KCJVbnBydW5lZDoiLHRyYWluLmVycm9yLnVucHJ1bmVkKjEwMCwgIiVcbiIsICJQcnVuZWQ6IiwgdHJhaW4uZXJyb3IucHJ1bmVkKjEwMCwgIiVcbiIpDQpgYGANCldoZW4gY29tcGFyZWQgdGhlIHRlc3QgZXJyb3IgcmVzdWx0cyBhcmUgY2xvc2UsIHdpdGggdGhlIHVucHJ1bmVkIHRlc3QgZXJyb3IgcmF0ZSBiZWluZyBsb3dlciBhdCAxNS44OCUgYW5kIHRoZSBwcnVuZWQgdGVzdCBlcnJvciByYXRlIGJlaW5nIGhpZ2hlciBhdCAxNi4yNSUuIFRoaXMgc3VnZ2VzdHMgdGhhdCBwcnVuaW5nIHRoZSB0cmVlIGRpZCBub3QgaW1wcm92ZSB0aGUgcHJlZGljdGlvbiBvbiB0aGUgdGVzdCBzZXQuIEhvd2V2ZXIgc2luY2UgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIHRlc3QgZXJyb3IgaXMgc28gc21hbGwsIGJvdGggbW9kZWxzIHBlcmZvcm0gd2VsbCwgd2l0aCBwcnVuaW5nIG5vdCBwcm92aWRpbmcgYW55IHN1YnN0YXRpYWwgZGlmZmVyZW5jZSB0byB0aGUgdGVzdCBkYXRhLiA=