Packages and data

knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE)

library(ggplot2)
library(glmnet)
library(randomForest)
library(rpart)

We use the Hitters data from ISLR2, the same dataset used in the Lecture 10 R demo. The response is Salary, measured in thousands of dollars.

The preprocessing is similar to Lecture 10:

For the main tree and forest methods, we do not need to add all two-way interactions manually. Trees and forests can learn interactions through recursive splitting. Later, we also include ridge and Lasso with the Lecture 10 interaction-expanded design as a comparison.

data("Hitters", package = "ISLR2")
hitters <- na.omit(Hitters)

hitters

Train/test split

set.seed(32950)
n <- nrow(hitters)
train_id <- sample(seq_len(n), size = floor(0.8 * n))
test_id <- setdiff(seq_len(n), train_id)

train <- hitters[train_id, ]
test <- hitters[test_id, ]

length(train_id)
[1] 210
length(test_id)
[1] 53

For regularized linear models, we use a numeric design matrix. The factor variables are converted to dummy variables by model.matrix. The tree methods below use formula interfaces, so they can handle the factor variables directly.

X <- model.matrix(Salary ~ ., data = hitters)[, -1]
y <- hitters$Salary

X_train <- X[train_id, , drop = FALSE]
X_test <- X[test_id, , drop = FALSE]
y_train <- y[train_id]
y_test <- y[test_id]

dim(X_train)
[1] 210  19

Linear baselines for comparison

Before fitting trees, compare against linear methods (OLS, ridge and Lasso) on the same predictors.

lm_fit <- lm(Salary ~ ., data = train)
pred_lm <- predict(lm_fit, newdata = test)
# OLS fit test error
mse_lm <- mean((y_test - pred_lm)^2)

# Ridge fit
set.seed(32950)
cv_ridge <- cv.glmnet(X_train, y_train, alpha = 0, standardize = TRUE)

# Lasso fit
set.seed(32950)
cv_lasso <- cv.glmnet(X_train, y_train, alpha = 1, standardize = TRUE)

pred_ridge <- predict(cv_ridge, newx = X_test, s = "lambda.min")
pred_lasso <- predict(cv_lasso, newx = X_test, s = "lambda.min")

mse_ridge <- mean((y_test - pred_ridge)^2)
mse_lasso <- mean((y_test - pred_lasso)^2)

data.frame(
  Method = c("Linear regression", "Ridge", "Lasso"),
  Test_MSE = c(mse_lm, mse_ridge, mse_lasso)
)

For comparison with the Lecture 10 demo, we also fit ridge and Lasso after adding two-way interactions among the quantitative predictors.

reg_formula_int <- Salary ~ League + Division + NewLeague +
  (AtBat + Hits + HmRun + Runs + RBI + Walks + Years +
   CAtBat + CHits + CHmRun + CRuns + CRBI + CWalks +
   PutOuts + Assists + Errors)^2

X_int <- model.matrix(reg_formula_int, data = hitters)[, -1]

X_int_train <- X_int[train_id, , drop = FALSE]
X_int_test <- X_int[test_id, , drop = FALSE]

dim(X_int_train)
[1] 210 139
set.seed(32950)
cv_ridge_int <- cv.glmnet(X_int_train, y_train, alpha = 0, standardize = TRUE)

set.seed(32950)
cv_lasso_int <- cv.glmnet(X_int_train, y_train, alpha = 1, standardize = TRUE)

pred_ridge_int <- predict(cv_ridge_int, newx = X_int_test, s = "lambda.min")
pred_lasso_int <- predict(cv_lasso_int, newx = X_int_test, s = "lambda.min")

mse_ridge_int <- mean((y_test - pred_ridge_int)^2)
mse_lasso_int <- mean((y_test - pred_lasso_int)^2)

data.frame(
  Method = c("Ridge with two-way interactions", "Lasso with two-way interactions"),
  Test_MSE = c(mse_ridge_int, mse_lasso_int)
)

One regression tree

We first fit one large regression tree and then use cost-complexity pruning, as in Lecture 12. The CV-selected tree is used for prediction. Separately, we make a smaller tree for visualization so that the splits are readable in class.

set.seed(32950)
tree_big <- rpart(
  Salary ~ .,
  data = train,
  method = "anova",
  control = rpart.control(cp = 0, minsplit = 10, xval = 10) # minsplit: the minimum number of observations in a node for splitting; xval: number of CVs
)

In Lecture 12, cost-complexity pruning chooses a subtree by minimizing

\[ \sum_{m=1}^{|T|} \sum_{i:x_i \in R_m} (y_i - \bar y_m)^2 + \alpha |T|. \]

The rpart output uses the same idea, but it calls the tuning parameter CP instead of \(\alpha\). The CP values are scaled relative to the root-node training error, so they are not on the raw RSS scale. This scaling changes the units, not the logic: larger CP means a stronger penalty and a smaller tree.

The pruning table is the computed cost-complexity path. Each row corresponds to one candidate subtree:

So rpart is not using a different pruning idea. It is implementing the weakest-link cost-complexity pruning path, then using cross-validation (xval = 10) to estimate prediction error for each subtree.

Using CV error for pruning the tree

cp_table <- as.data.frame(tree_big$cptable)
cp_table$nsplit <- as.integer(cp_table$nsplit)
names(cp_table) <- c("alpha_scaled_CP", "splits", "train_relative_error",
                     "cv_relative_error", "cv_standard_error")
cp_table
plotcp(tree_big)

In this example, the CV error does not rise much as the number of splits increases. This does not mean that larger trees are always better. It means that, for this particular training split and 10-fold CV run, CV does not find strong evidence that the smaller trees predict better than some larger trees. The xstd column is useful here: if several subtrees have CV errors within roughly one standard error of the minimum, their predictive performance is not clearly distinguishable.

In practice, a common alternative is the one-standard-error rule: choose the smallest tree whose CV error is within one standard error of the minimum. That gives a simpler tree, but it adds one more selection rule. To keep this demo focused, we use the minimum-CV tree for test MSE and use a separate small tree only for visualization.

The prediction tree uses the minimum CV error over the full cost-complexity path.

best_cp_cv <- tree_big$cptable[which.min(tree_big$cptable[, "xerror"]), "CP"]
tree_cv <- prune(tree_big, cp = best_cp_cv)

tree_cv_summary <- data.frame(
  alpha_scaled_CP = best_cp_cv,
  Splits = sum(tree_cv$frame$var != "<leaf>"),
  Leaves = sum(tree_cv$frame$var == "<leaf>"),
  CV_Relative_Error = min(tree_big$cptable[, "xerror"]),
  Test_MSE = NA_real_
)


pred_tree_cv <- predict(tree_cv, newdata = test)
mse_tree_cv <- mean((y_test - pred_tree_cv)^2)

tree_cv_summary$Test_MSE <- mse_tree_cv
tree_cv_summary

The single pruned tree has noticeably larger test MSE than the linear baselines here. This is not a mistake: a single tree is easy to interpret, but its piecewise-constant predictions can be unstable and relatively crude. The ensemble methods below are designed to improve this weakness by averaging many trees.

Visualization of a smaller tree

For visualization only, choose the best CV tree among cost-complexity subtrees with at most five splits. This is easier to read, but it is not the tree used for the test MSE comparison.

cptable_display <- tree_big$cptable[tree_big$cptable[, "nsplit"] <= 5, , drop = FALSE]
best_cp_display <- cptable_display[which.min(cptable_display[, "xerror"]), "CP"]
tree_display <- prune(tree_big, cp = best_cp_display)

tree_display_summary <- data.frame(
  alpha_scaled_CP = best_cp_display,
  Splits = sum(tree_display$frame$var != "<leaf>"),
  Leaves = sum(tree_display$frame$var == "<leaf>"),
  CV_Relative_Error = min(cptable_display[, "xerror"])
)

pred_tree_display <- predict(tree_display, newdata = test)
mse_tree_display <- mean((y_test - pred_tree_display)^2)

tree_display_summary$Test_MSE <- mse_tree_display
tree_display_summary
NA

You can see that the display tree here actually have smaller MSE on the test data than the CV selected tree. Actually, that is not suprising, as the CV error are very similar and a smaller tree is less likely to overfit.

The plot below is the small display tree. In each terminal node, the number shown is the predicted salary for observations that fall into that leaf.

rpart.plot::rpart.plot(
    tree_display,
    type = 2,
    extra = 101,
    fallen.leaves = TRUE,
    box.palette = "Blues",
    main = "Small display tree for salary"
)

The same tree can be read as a small set of terminal-node rules. Each row is one leaf:

  • N: number of training observations in that leaf;
  • Prediction: average training salary in that leaf;
  • Rule: conditions needed to reach that leaf.
leaf_nodes <- as.numeric(row.names(tree_display$frame[tree_display$frame$var == "<leaf>", ]))
tree_paths <- path.rpart(tree_display, nodes = leaf_nodes, print.it = FALSE)

tree_rules <- data.frame(
  Leaf = leaf_nodes,
  N = tree_display$frame[as.character(leaf_nodes), "n"],
  Prediction = round(tree_display$frame[as.character(leaf_nodes), "yval"], 1),
  Rule = sapply(tree_paths, function(z) paste(z[-1], collapse = " & ")),
  row.names = NULL
)

tree_rules

A single tree is indeed very interpretative, though its prediction strength is not ideal.

Bagging and random forest

We now use the randomForest package to perform bagging and random forest.

The package reports an out-of-bag (OOB) MSE after each number of trees. OOB error is computed from observations that were not included in a tree’s bootstrap sample.

Bagged trees

Bagging grows many large trees on bootstrap samples and averages their predictions. Here mtry = p, so each split can use any predictor.

set.seed(32950)
p_tree <- ncol(train) - 1

bag_fit <- randomForest(
  Salary ~ .,
  data = train,
  ntree = 500,
  mtry = p_tree,
  importance = TRUE
)

bag_fit

Call:
 randomForest(formula = Salary ~ ., data = train, ntree = 500,      mtry = p_tree, importance = TRUE) 
               Type of random forest: regression
                     Number of trees: 500
No. of variables tried at each split: 19

          Mean of squared residuals: 82670.99
                    % Var explained: 58.45
bag_oob_mse <- bag_fit$mse[bag_fit$ntree]
bag_oob_mse
[1] 82670.99

We can visualize how OOB MSE change with the number of trees.

 plot(bag_fit$mse, type = "l",
       xlab = "Number of trees",
       ylab = "OOB MSE",
       main = "Bagging: OOB error vs number of trees")

We see here that OOB MSE already stabilizes at \(B = 500\). Increasing the number of trees in bagging or random forests typically does not overfit. We typically start with a large \(B\) and increase \(B\) only if OOB MSE does not stabilize.

pred_bag <- predict(bag_fit, newdata = test)
mse_bag <- mean((y_test - pred_bag)^2)
mse_bag
[1] 71601.36

The prediction test error is now smaller than linear methods, indicating that considering interactions of predictors help. Notice that in the R demo of lecture 10, the best test data MSE we get is 60647 from Lasso with interaction terms, consistent with the results here.

Random forest

For regression, a common default is \(m_{\mathrm{try}} \approx p/3\). This keeps each tree strong while reducing correlation across trees.

set.seed(32950)
mtry_rf <- max(1, floor(p_tree / 3))
mtry_rf
[1] 6
set.seed(32950)
rf_fit <- randomForest(
  Salary ~ .,
  data = train,
  ntree = 500,
  mtry = mtry_rf,
  importance = TRUE
)

rf_fit

Call:
 randomForest(formula = Salary ~ ., data = train, ntree = 500,      mtry = mtry_rf, importance = TRUE) 
               Type of random forest: regression
                     Number of trees: 500
No. of variables tried at each split: 6

          Mean of squared residuals: 80668.42
                    % Var explained: 59.46
rf_oob_mse <- rf_fit$mse[rf_fit$ntree]
rf_oob_mse
[1] 80668.42
plot(rf_fit$mse, type = "l",
       xlab = "Number of trees",
       ylab = "OOB MSE",
       main = "Random forest: OOB error vs number of trees")

pred_rf <- predict(rf_fit, newdata = test)
mse_rf <- mean((y_test - pred_rf)^2)
mse_rf
[1] 68668.24

We further drop the OOB MSE and test data MSE using random forest.

Variable importance for the random forest

Permutation importance measures how much prediction error increases after breaking the relationship between one predictor and the response. The randomForest package computes this using OOB observations.

importance_df <- data.frame(
  Variable = row.names(importance(rf_fit, type = 1)),
  Increase_in_MSE = importance(rf_fit, type = 1)[, 1],
  row.names = NULL
)

importance_top <- head(importance_df[order(-importance_df$Increase_in_MSE), ], 10)
importance_top

Compare test-set performance across methods

results <- data.frame(
  Method = c("Linear regression", "Ridge", "Lasso",
             "Ridge with two-way interactions", "Lasso with two-way interactions",
             "CV-pruned tree", "Bagged trees", "Random forest"),
  Test_MSE = c(mse_lm, mse_ridge, mse_lasso,
               mse_ridge_int, mse_lasso_int,
               mse_tree_cv, mse_bag, mse_rf)
)

results[order(results$Test_MSE), ]
pred_df <- data.frame(
  Salary = rep(y_test, 8),
  Prediction = c(pred_lm, as.numeric(pred_ridge), as.numeric(pred_lasso),
                 as.numeric(pred_ridge_int), as.numeric(pred_lasso_int),
                 pred_tree_cv, pred_bag, pred_rf),
  Method = rep(c("Linear regression", "Ridge", "Lasso",
                 "Ridge with interactions", "Lasso with interactions",
                 "CV-pruned tree", "Bagged trees", "Random forest"),
               each = length(y_test))
)

ggplot(pred_df, aes(x = Salary, y = Prediction, color = Method)) +
  geom_point(alpha = 0.7) +
  geom_abline(slope = 1, intercept = 0, linetype = 2) +
  facet_wrap(~ Method) +
  labs(
    title = "Observed versus predicted salary on the test set",
    x = "Observed salary",
    y = "Predicted salary"
  ) +
  theme_minimal(base_size = 13) +
  theme(legend.position = "none")

In this test split, the two highest-salary observations are fitted better by the linear methods than by the tree-based methods. One reason is that these salaries are much larger than most other observations. Regression trees, bagging, and random forests make predictions by averaging training responses within leaves or across trees, so predictions for extreme observations are often pulled back toward more typical salaries. Linear methods can produce more extreme fitted values through linear combinations of predictors, especially when interaction terms are included. Because MSE squares errors, underpredicting a few very high-salary players can noticeably increase the test MSE for tree-based methods, which may partly explain why the interaction Lasso performs well here.

For many lower-salary observations, the scatter plot suggests that the tree-based methods predict better than the linear methods. This is consistent with the averaging nature of trees: in the denser lower-salary range, local averaging can stabilize predictions and reduce large deviations. Thus, the same averaging behavior can help tree-based methods for typical observations while hurting them for extreme high-salary observations.

Takeaway

  • The same Hitters preprocessing from Lecture 10 works here: remove missing values and encode factors when a numeric matrix is needed.
  • Linear methods give useful baselines.
  • A small pruned tree is interpretable but can have high prediction variance.
  • Bagging averages many bootstrap trees and usually improves test error.
  • Random forests add random predictor selection at each split, reducing correlation among trees.
  • Variable importance summarizes a forest, but it is predictive rather than causal.
LS0tCnRpdGxlOiAiTGVjdHVyZSAxMyBEZW1vOiBUcmVlcywgQmFnZ2luZywgYW5kIFJhbmRvbSBGb3Jlc3RzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIFBhY2thZ2VzIGFuZCBkYXRhCgpgYGB7ciBzZXR1cH0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSkKCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnbG1uZXQpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KHJwYXJ0KQpgYGAKCgoKV2UgdXNlIHRoZSBgSGl0dGVyc2AgZGF0YSBmcm9tIGBJU0xSMmAsIHRoZSBzYW1lIGRhdGFzZXQgdXNlZCBpbiB0aGUgTGVjdHVyZSAxMCBSIGRlbW8uClRoZSByZXNwb25zZSBpcyBgU2FsYXJ5YCwgbWVhc3VyZWQgaW4gdGhvdXNhbmRzIG9mIGRvbGxhcnMuCgpUaGUgcHJlcHJvY2Vzc2luZyBpcyBzaW1pbGFyIHRvIExlY3R1cmUgMTA6CgotIHJlbW92ZSByb3dzIHdpdGggbWlzc2luZyBzYWxhcnkgYnkgdXNpbmcgYG5hLm9taXRgOwotIGtlZXAgdGhlIG9yaWdpbmFsIHZhcmlhYmxlczsKLSBjb252ZXJ0IGZhY3RvciBwcmVkaWN0b3JzIHRvIGR1bW15IHZhcmlhYmxlcyB3aXRoIGBtb2RlbC5tYXRyaXhgIHdoZW4gYSBudW1lcmljIG1hdHJpeCBpcyBuZWVkZWQuCgpGb3IgdGhlIG1haW4gdHJlZSBhbmQgZm9yZXN0IG1ldGhvZHMsIHdlIGRvIG5vdCBuZWVkIHRvIGFkZCBhbGwgdHdvLXdheSBpbnRlcmFjdGlvbnMgbWFudWFsbHkuIFRyZWVzIGFuZCBmb3Jlc3RzIGNhbiBsZWFybiBpbnRlcmFjdGlvbnMgdGhyb3VnaCByZWN1cnNpdmUgc3BsaXR0aW5nLiBMYXRlciwgd2UgYWxzbyBpbmNsdWRlIHJpZGdlIGFuZCBMYXNzbyB3aXRoIHRoZSBMZWN0dXJlIDEwIGludGVyYWN0aW9uLWV4cGFuZGVkIGRlc2lnbiBhcyBhIGNvbXBhcmlzb24uCgpgYGB7cn0KZGF0YSgiSGl0dGVycyIsIHBhY2thZ2UgPSAiSVNMUjIiKQpoaXR0ZXJzIDwtIG5hLm9taXQoSGl0dGVycykKCmhpdHRlcnMKYGBgCgotIFZhcmlhYmxlcyBzdWNoIGFzIGBBdEJhdGAsIGBIaXRzYCwgYEhtUnVuYCwgYFJ1bnNgLCBgUkJJYCwgYW5kIGBXYWxrc2AgZGVzY3JpYmUgcmVjZW50IGJhdHRpbmcgcGVyZm9ybWFuY2UuCi0gVmFyaWFibGVzIGJlZ2lubmluZyB3aXRoIGBDYCBhcmUgY2FyZWVyIHRvdGFscy4KLSBgUHV0T3V0c2AsIGBBc3Npc3RzYCwgYW5kIGBFcnJvcnNgIGRlc2NyaWJlIGZpZWxkaW5nLgotIGBMZWFndWVgLCBgRGl2aXNpb25gLCBhbmQgYE5ld0xlYWd1ZWAgYXJlIGNhdGVnb3JpY2FsIGluZGljYXRvcnMuCgojIyBUcmFpbi90ZXN0IHNwbGl0CgpgYGB7cn0Kc2V0LnNlZWQoMzI5NTApCm4gPC0gbnJvdyhoaXR0ZXJzKQp0cmFpbl9pZCA8LSBzYW1wbGUoc2VxX2xlbihuKSwgc2l6ZSA9IGZsb29yKDAuOCAqIG4pKQp0ZXN0X2lkIDwtIHNldGRpZmYoc2VxX2xlbihuKSwgdHJhaW5faWQpCgp0cmFpbiA8LSBoaXR0ZXJzW3RyYWluX2lkLCBdCnRlc3QgPC0gaGl0dGVyc1t0ZXN0X2lkLCBdCgpsZW5ndGgodHJhaW5faWQpCmxlbmd0aCh0ZXN0X2lkKQpgYGAKCkZvciByZWd1bGFyaXplZCBsaW5lYXIgbW9kZWxzLCB3ZSB1c2UgYSBudW1lcmljIGRlc2lnbiBtYXRyaXguIFRoZSBmYWN0b3IgdmFyaWFibGVzIGFyZSBjb252ZXJ0ZWQgdG8gZHVtbXkgdmFyaWFibGVzIGJ5IGBtb2RlbC5tYXRyaXhgLgpUaGUgdHJlZSBtZXRob2RzIGJlbG93IHVzZSBmb3JtdWxhIGludGVyZmFjZXMsIHNvIHRoZXkgY2FuIGhhbmRsZSB0aGUgZmFjdG9yIHZhcmlhYmxlcyBkaXJlY3RseS4KCmBgYHtyfQpYIDwtIG1vZGVsLm1hdHJpeChTYWxhcnkgfiAuLCBkYXRhID0gaGl0dGVycylbLCAtMV0KeSA8LSBoaXR0ZXJzJFNhbGFyeQoKWF90cmFpbiA8LSBYW3RyYWluX2lkLCAsIGRyb3AgPSBGQUxTRV0KWF90ZXN0IDwtIFhbdGVzdF9pZCwgLCBkcm9wID0gRkFMU0VdCnlfdHJhaW4gPC0geVt0cmFpbl9pZF0KeV90ZXN0IDwtIHlbdGVzdF9pZF0KCmRpbShYX3RyYWluKQpgYGAKCiMjIExpbmVhciBiYXNlbGluZXMgZm9yIGNvbXBhcmlzb24KCkJlZm9yZSBmaXR0aW5nIHRyZWVzLCBjb21wYXJlIGFnYWluc3QgbGluZWFyIG1ldGhvZHMgKE9MUywgcmlkZ2UgYW5kIExhc3NvKSBvbiB0aGUgc2FtZSBwcmVkaWN0b3JzLgoKYGBge3J9CmxtX2ZpdCA8LSBsbShTYWxhcnkgfiAuLCBkYXRhID0gdHJhaW4pCnByZWRfbG0gPC0gcHJlZGljdChsbV9maXQsIG5ld2RhdGEgPSB0ZXN0KQojIE9MUyBmaXQgdGVzdCBlcnJvcgptc2VfbG0gPC0gbWVhbigoeV90ZXN0IC0gcHJlZF9sbSleMikKCiMgUmlkZ2UgZml0CnNldC5zZWVkKDMyOTUwKQpjdl9yaWRnZSA8LSBjdi5nbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAwLCBzdGFuZGFyZGl6ZSA9IFRSVUUpCgojIExhc3NvIGZpdApzZXQuc2VlZCgzMjk1MCkKY3ZfbGFzc28gPC0gY3YuZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMSwgc3RhbmRhcmRpemUgPSBUUlVFKQoKcHJlZF9yaWRnZSA8LSBwcmVkaWN0KGN2X3JpZGdlLCBuZXd4ID0gWF90ZXN0LCBzID0gImxhbWJkYS5taW4iKQpwcmVkX2xhc3NvIDwtIHByZWRpY3QoY3ZfbGFzc28sIG5ld3ggPSBYX3Rlc3QsIHMgPSAibGFtYmRhLm1pbiIpCgptc2VfcmlkZ2UgPC0gbWVhbigoeV90ZXN0IC0gcHJlZF9yaWRnZSleMikKbXNlX2xhc3NvIDwtIG1lYW4oKHlfdGVzdCAtIHByZWRfbGFzc28pXjIpCgpkYXRhLmZyYW1lKAogIE1ldGhvZCA9IGMoIkxpbmVhciByZWdyZXNzaW9uIiwgIlJpZGdlIiwgIkxhc3NvIiksCiAgVGVzdF9NU0UgPSBjKG1zZV9sbSwgbXNlX3JpZGdlLCBtc2VfbGFzc28pCikKYGBgCgpGb3IgY29tcGFyaXNvbiB3aXRoIHRoZSBMZWN0dXJlIDEwIGRlbW8sIHdlIGFsc28gZml0IHJpZGdlIGFuZCBMYXNzbyBhZnRlciBhZGRpbmcgdHdvLXdheSBpbnRlcmFjdGlvbnMgYW1vbmcgdGhlIHF1YW50aXRhdGl2ZSBwcmVkaWN0b3JzLgoKYGBge3J9CnJlZ19mb3JtdWxhX2ludCA8LSBTYWxhcnkgfiBMZWFndWUgKyBEaXZpc2lvbiArIE5ld0xlYWd1ZSArCiAgKEF0QmF0ICsgSGl0cyArIEhtUnVuICsgUnVucyArIFJCSSArIFdhbGtzICsgWWVhcnMgKwogICBDQXRCYXQgKyBDSGl0cyArIENIbVJ1biArIENSdW5zICsgQ1JCSSArIENXYWxrcyArCiAgIFB1dE91dHMgKyBBc3Npc3RzICsgRXJyb3JzKV4yCgpYX2ludCA8LSBtb2RlbC5tYXRyaXgocmVnX2Zvcm11bGFfaW50LCBkYXRhID0gaGl0dGVycylbLCAtMV0KClhfaW50X3RyYWluIDwtIFhfaW50W3RyYWluX2lkLCAsIGRyb3AgPSBGQUxTRV0KWF9pbnRfdGVzdCA8LSBYX2ludFt0ZXN0X2lkLCAsIGRyb3AgPSBGQUxTRV0KCmRpbShYX2ludF90cmFpbikKYGBgCgpgYGB7cn0Kc2V0LnNlZWQoMzI5NTApCmN2X3JpZGdlX2ludCA8LSBjdi5nbG1uZXQoWF9pbnRfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMCwgc3RhbmRhcmRpemUgPSBUUlVFKQoKc2V0LnNlZWQoMzI5NTApCmN2X2xhc3NvX2ludCA8LSBjdi5nbG1uZXQoWF9pbnRfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMSwgc3RhbmRhcmRpemUgPSBUUlVFKQoKcHJlZF9yaWRnZV9pbnQgPC0gcHJlZGljdChjdl9yaWRnZV9pbnQsIG5ld3ggPSBYX2ludF90ZXN0LCBzID0gImxhbWJkYS5taW4iKQpwcmVkX2xhc3NvX2ludCA8LSBwcmVkaWN0KGN2X2xhc3NvX2ludCwgbmV3eCA9IFhfaW50X3Rlc3QsIHMgPSAibGFtYmRhLm1pbiIpCgptc2VfcmlkZ2VfaW50IDwtIG1lYW4oKHlfdGVzdCAtIHByZWRfcmlkZ2VfaW50KV4yKQptc2VfbGFzc29faW50IDwtIG1lYW4oKHlfdGVzdCAtIHByZWRfbGFzc29faW50KV4yKQoKZGF0YS5mcmFtZSgKICBNZXRob2QgPSBjKCJSaWRnZSB3aXRoIHR3by13YXkgaW50ZXJhY3Rpb25zIiwgIkxhc3NvIHdpdGggdHdvLXdheSBpbnRlcmFjdGlvbnMiKSwKICBUZXN0X01TRSA9IGMobXNlX3JpZGdlX2ludCwgbXNlX2xhc3NvX2ludCkKKQpgYGAKCiMgT25lIHJlZ3Jlc3Npb24gdHJlZQoKV2UgZmlyc3QgZml0IG9uZSBsYXJnZSByZWdyZXNzaW9uIHRyZWUgYW5kIHRoZW4gdXNlIGNvc3QtY29tcGxleGl0eSBwcnVuaW5nLCBhcyBpbiBMZWN0dXJlIDEyLiBUaGUgQ1Ytc2VsZWN0ZWQgdHJlZSBpcyB1c2VkIGZvciBwcmVkaWN0aW9uLiBTZXBhcmF0ZWx5LCB3ZSBtYWtlIGEgc21hbGxlciB0cmVlIGZvciB2aXN1YWxpemF0aW9uIHNvIHRoYXQgdGhlIHNwbGl0cyBhcmUgcmVhZGFibGUgaW4gY2xhc3MuCgpgYGB7cn0Kc2V0LnNlZWQoMzI5NTApCnRyZWVfYmlnIDwtIHJwYXJ0KAogIFNhbGFyeSB+IC4sCiAgZGF0YSA9IHRyYWluLAogIG1ldGhvZCA9ICJhbm92YSIsCiAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2woY3AgPSAwLCBtaW5zcGxpdCA9IDEwLCB4dmFsID0gMTApICMgbWluc3BsaXQ6IHRoZSBtaW5pbXVtIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gYSBub2RlIGZvciBzcGxpdHRpbmc7IHh2YWw6IG51bWJlciBvZiBDVnMKKQpgYGAKCkluIExlY3R1cmUgMTIsIGNvc3QtY29tcGxleGl0eSBwcnVuaW5nIGNob29zZXMgYSBzdWJ0cmVlIGJ5IG1pbmltaXppbmcKClxbClxzdW1fe209MX1ee3xUfH0gXHN1bV97aTp4X2kgXGluIFJfbX0gKHlfaSAtIFxiYXIgeV9tKV4yICsgXGFscGhhIHxUfC4KXF0KClRoZSBgcnBhcnRgIG91dHB1dCB1c2VzIHRoZSBzYW1lIGlkZWEsIGJ1dCBpdCBjYWxscyB0aGUgdHVuaW5nIHBhcmFtZXRlciBgQ1BgIGluc3RlYWQgb2YgXChcYWxwaGFcKS4KVGhlIGBDUGAgdmFsdWVzIGFyZSBzY2FsZWQgcmVsYXRpdmUgdG8gdGhlIHJvb3Qtbm9kZSB0cmFpbmluZyBlcnJvciwgc28gdGhleSBhcmUgbm90IG9uIHRoZSByYXcgUlNTIHNjYWxlLgpUaGlzIHNjYWxpbmcgY2hhbmdlcyB0aGUgdW5pdHMsIG5vdCB0aGUgbG9naWM6IGxhcmdlciBgQ1BgIG1lYW5zIGEgc3Ryb25nZXIgcGVuYWx0eSBhbmQgYSBzbWFsbGVyIHRyZWUuCgpUaGUgcHJ1bmluZyB0YWJsZSBpcyB0aGUgY29tcHV0ZWQgY29zdC1jb21wbGV4aXR5IHBhdGguCkVhY2ggcm93IGNvcnJlc3BvbmRzIHRvIG9uZSBjYW5kaWRhdGUgc3VidHJlZToKCi0gYG5zcGxpdGA6IG51bWJlciBvZiBpbnRlcm5hbCBzcGxpdHMgaW4gdGhlIHRyZWU7Ci0gYENQYDogYHJwYXJ0YCdzIHNjYWxlZCB2ZXJzaW9uIG9mIHRoZSBwcnVuaW5nIHBlbmFsdHkgXChcYWxwaGFcKTsKLSBgcmVsX2Vycm9yYDogdHJhaW5pbmcgZXJyb3IgcmVsYXRpdmUgdG8gdGhlIHJvb3Qtb25seSB0cmVlOwotIGB4ZXJyb3JgOiBjcm9zcy12YWxpZGF0aW9uIGVycm9yIHJlbGF0aXZlIHRvIHRoZSByb290LW9ubHkgdHJlZTsKLSBgeHN0ZGA6IHN0YW5kYXJkIGVycm9yIG9mIHRoZSBjcm9zcy12YWxpZGF0aW9uIGVycm9yLgoKU28gYHJwYXJ0YCBpcyBub3QgdXNpbmcgYSBkaWZmZXJlbnQgcHJ1bmluZyBpZGVhLgpJdCBpcyBpbXBsZW1lbnRpbmcgdGhlIHdlYWtlc3QtbGluayBjb3N0LWNvbXBsZXhpdHkgcHJ1bmluZyBwYXRoLCB0aGVuIHVzaW5nIGNyb3NzLXZhbGlkYXRpb24gKGB4dmFsID0gMTBgKSB0byBlc3RpbWF0ZSBwcmVkaWN0aW9uIGVycm9yIGZvciBlYWNoIHN1YnRyZWUuCgojIyBVc2luZyBDViBlcnJvciBmb3IgcHJ1bmluZyB0aGUgdHJlZQoKYGBge3J9CmNwX3RhYmxlIDwtIGFzLmRhdGEuZnJhbWUodHJlZV9iaWckY3B0YWJsZSkKY3BfdGFibGUkbnNwbGl0IDwtIGFzLmludGVnZXIoY3BfdGFibGUkbnNwbGl0KQpuYW1lcyhjcF90YWJsZSkgPC0gYygiYWxwaGFfc2NhbGVkX0NQIiwgInNwbGl0cyIsICJ0cmFpbl9yZWxhdGl2ZV9lcnJvciIsCiAgICAgICAgICAgICAgICAgICAgICJjdl9yZWxhdGl2ZV9lcnJvciIsICJjdl9zdGFuZGFyZF9lcnJvciIpCmNwX3RhYmxlCmBgYAoKYGBge3J9CnBsb3RjcCh0cmVlX2JpZykKYGBgCgpJbiB0aGlzIGV4YW1wbGUsIHRoZSBDViBlcnJvciBkb2VzIG5vdCByaXNlIG11Y2ggYXMgdGhlIG51bWJlciBvZiBzcGxpdHMgaW5jcmVhc2VzLgpUaGlzIGRvZXMgbm90IG1lYW4gdGhhdCBsYXJnZXIgdHJlZXMgYXJlIGFsd2F5cyBiZXR0ZXIuCkl0IG1lYW5zIHRoYXQsIGZvciB0aGlzIHBhcnRpY3VsYXIgdHJhaW5pbmcgc3BsaXQgYW5kIDEwLWZvbGQgQ1YgcnVuLCBDViBkb2VzIG5vdCBmaW5kIHN0cm9uZyBldmlkZW5jZSB0aGF0IHRoZSBzbWFsbGVyIHRyZWVzIHByZWRpY3QgYmV0dGVyIHRoYW4gc29tZSBsYXJnZXIgdHJlZXMuClRoZSBgeHN0ZGAgY29sdW1uIGlzIHVzZWZ1bCBoZXJlOiBpZiBzZXZlcmFsIHN1YnRyZWVzIGhhdmUgQ1YgZXJyb3JzIHdpdGhpbiByb3VnaGx5IG9uZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgbWluaW11bSwgdGhlaXIgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSBpcyBub3QgY2xlYXJseSBkaXN0aW5ndWlzaGFibGUuCgpJbiBwcmFjdGljZSwgYSBjb21tb24gYWx0ZXJuYXRpdmUgaXMgdGhlICoqb25lLXN0YW5kYXJkLWVycm9yIHJ1bGUqKjogY2hvb3NlIHRoZSBzbWFsbGVzdCB0cmVlIHdob3NlIENWIGVycm9yIGlzIHdpdGhpbiBvbmUgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIG1pbmltdW0uClRoYXQgZ2l2ZXMgYSBzaW1wbGVyIHRyZWUsIGJ1dCBpdCBhZGRzIG9uZSBtb3JlIHNlbGVjdGlvbiBydWxlLgpUbyBrZWVwIHRoaXMgZGVtbyBmb2N1c2VkLCB3ZSB1c2UgdGhlIG1pbmltdW0tQ1YgdHJlZSBmb3IgdGVzdCBNU0UgYW5kIHVzZSBhIHNlcGFyYXRlIHNtYWxsIHRyZWUgb25seSBmb3IgdmlzdWFsaXphdGlvbi4KClRoZSBwcmVkaWN0aW9uIHRyZWUgdXNlcyB0aGUgbWluaW11bSBDViBlcnJvciBvdmVyIHRoZSBmdWxsIGNvc3QtY29tcGxleGl0eSBwYXRoLgoKYGBge3J9CmJlc3RfY3BfY3YgPC0gdHJlZV9iaWckY3B0YWJsZVt3aGljaC5taW4odHJlZV9iaWckY3B0YWJsZVssICJ4ZXJyb3IiXSksICJDUCJdCnRyZWVfY3YgPC0gcHJ1bmUodHJlZV9iaWcsIGNwID0gYmVzdF9jcF9jdikKCnRyZWVfY3Zfc3VtbWFyeSA8LSBkYXRhLmZyYW1lKAogIGFscGhhX3NjYWxlZF9DUCA9IGJlc3RfY3BfY3YsCiAgU3BsaXRzID0gc3VtKHRyZWVfY3YkZnJhbWUkdmFyICE9ICI8bGVhZj4iKSwKICBMZWF2ZXMgPSBzdW0odHJlZV9jdiRmcmFtZSR2YXIgPT0gIjxsZWFmPiIpLAogIENWX1JlbGF0aXZlX0Vycm9yID0gbWluKHRyZWVfYmlnJGNwdGFibGVbLCAieGVycm9yIl0pLAogIFRlc3RfTVNFID0gTkFfcmVhbF8KKQoKCnByZWRfdHJlZV9jdiA8LSBwcmVkaWN0KHRyZWVfY3YsIG5ld2RhdGEgPSB0ZXN0KQptc2VfdHJlZV9jdiA8LSBtZWFuKCh5X3Rlc3QgLSBwcmVkX3RyZWVfY3YpXjIpCgp0cmVlX2N2X3N1bW1hcnkkVGVzdF9NU0UgPC0gbXNlX3RyZWVfY3YKdHJlZV9jdl9zdW1tYXJ5CmBgYAogVGhlIHNpbmdsZSBwcnVuZWQgdHJlZSBoYXMgbm90aWNlYWJseSBsYXJnZXIgdGVzdCBNU0UgdGhhbiB0aGUgbGluZWFyIGJhc2VsaW5lcyBoZXJlLiBUaGlzIGlzIG5vdCBhIG1pc3Rha2U6IGEgc2luZ2xlIHRyZWUgaXMgZWFzeSB0byBpbnRlcnByZXQsIGJ1dCBpdHMgcGllY2V3aXNlLWNvbnN0YW50IHByZWRpY3Rpb25zIGNhbiBiZSB1bnN0YWJsZSBhbmQgcmVsYXRpdmVseSBjcnVkZS4gVGhlIGVuc2VtYmxlIG1ldGhvZHMgYmVsb3cgYXJlIGRlc2lnbmVkIHRvIGltcHJvdmUgdGhpcyB3ZWFrbmVzcyBieSBhdmVyYWdpbmcgbWFueSB0cmVlcy4KCiMjIFZpc3VhbGl6YXRpb24gb2YgYSBzbWFsbGVyIHRyZWUKRm9yIHZpc3VhbGl6YXRpb24gb25seSwgY2hvb3NlIHRoZSBiZXN0IENWIHRyZWUgYW1vbmcgY29zdC1jb21wbGV4aXR5IHN1YnRyZWVzIHdpdGggYXQgbW9zdCBmaXZlIHNwbGl0cy4KVGhpcyBpcyBlYXNpZXIgdG8gcmVhZCwgYnV0IGl0IGlzIG5vdCB0aGUgdHJlZSB1c2VkIGZvciB0aGUgdGVzdCBNU0UgY29tcGFyaXNvbi4KCmBgYHtyfQpjcHRhYmxlX2Rpc3BsYXkgPC0gdHJlZV9iaWckY3B0YWJsZVt0cmVlX2JpZyRjcHRhYmxlWywgIm5zcGxpdCJdIDw9IDUsICwgZHJvcCA9IEZBTFNFXQpiZXN0X2NwX2Rpc3BsYXkgPC0gY3B0YWJsZV9kaXNwbGF5W3doaWNoLm1pbihjcHRhYmxlX2Rpc3BsYXlbLCAieGVycm9yIl0pLCAiQ1AiXQp0cmVlX2Rpc3BsYXkgPC0gcHJ1bmUodHJlZV9iaWcsIGNwID0gYmVzdF9jcF9kaXNwbGF5KQoKdHJlZV9kaXNwbGF5X3N1bW1hcnkgPC0gZGF0YS5mcmFtZSgKICBhbHBoYV9zY2FsZWRfQ1AgPSBiZXN0X2NwX2Rpc3BsYXksCiAgU3BsaXRzID0gc3VtKHRyZWVfZGlzcGxheSRmcmFtZSR2YXIgIT0gIjxsZWFmPiIpLAogIExlYXZlcyA9IHN1bSh0cmVlX2Rpc3BsYXkkZnJhbWUkdmFyID09ICI8bGVhZj4iKSwKICBDVl9SZWxhdGl2ZV9FcnJvciA9IG1pbihjcHRhYmxlX2Rpc3BsYXlbLCAieGVycm9yIl0pCikKCnByZWRfdHJlZV9kaXNwbGF5IDwtIHByZWRpY3QodHJlZV9kaXNwbGF5LCBuZXdkYXRhID0gdGVzdCkKbXNlX3RyZWVfZGlzcGxheSA8LSBtZWFuKCh5X3Rlc3QgLSBwcmVkX3RyZWVfZGlzcGxheSleMikKCnRyZWVfZGlzcGxheV9zdW1tYXJ5JFRlc3RfTVNFIDwtIG1zZV90cmVlX2Rpc3BsYXkKdHJlZV9kaXNwbGF5X3N1bW1hcnkKCmBgYApZb3UgY2FuIHNlZSB0aGF0IHRoZSBkaXNwbGF5IHRyZWUgaGVyZSBhY3R1YWxseSBoYXZlIHNtYWxsZXIgTVNFIG9uIHRoZSB0ZXN0IGRhdGEgdGhhbiB0aGUgQ1Ygc2VsZWN0ZWQgdHJlZS4gQWN0dWFsbHksIHRoYXQgaXMgbm90IHN1cHJpc2luZywgYXMgdGhlIENWIGVycm9yIGFyZSB2ZXJ5IHNpbWlsYXIgYW5kIGEgc21hbGxlciB0cmVlIGlzIGxlc3MgbGlrZWx5IHRvIG92ZXJmaXQuCgoKVGhlIHBsb3QgYmVsb3cgaXMgdGhlIHNtYWxsIGRpc3BsYXkgdHJlZS4KSW4gZWFjaCB0ZXJtaW5hbCBub2RlLCB0aGUgbnVtYmVyIHNob3duIGlzIHRoZSBwcmVkaWN0ZWQgc2FsYXJ5IGZvciBvYnNlcnZhdGlvbnMgdGhhdCBmYWxsIGludG8gdGhhdCBsZWFmLgoKYGBge3J9CnJwYXJ0LnBsb3Q6OnJwYXJ0LnBsb3QoCiAgICB0cmVlX2Rpc3BsYXksCiAgICB0eXBlID0gMiwKICAgIGV4dHJhID0gMTAxLAogICAgZmFsbGVuLmxlYXZlcyA9IFRSVUUsCiAgICBib3gucGFsZXR0ZSA9ICJCbHVlcyIsCiAgICBtYWluID0gIlNtYWxsIGRpc3BsYXkgdHJlZSBmb3Igc2FsYXJ5IgopCgpgYGAKClRoZSBzYW1lIHRyZWUgY2FuIGJlIHJlYWQgYXMgYSBzbWFsbCBzZXQgb2YgdGVybWluYWwtbm9kZSBydWxlcy4KRWFjaCByb3cgaXMgb25lIGxlYWY6CgotIGBOYDogbnVtYmVyIG9mIHRyYWluaW5nIG9ic2VydmF0aW9ucyBpbiB0aGF0IGxlYWY7Ci0gYFByZWRpY3Rpb25gOiBhdmVyYWdlIHRyYWluaW5nIHNhbGFyeSBpbiB0aGF0IGxlYWY7Ci0gYFJ1bGVgOiBjb25kaXRpb25zIG5lZWRlZCB0byByZWFjaCB0aGF0IGxlYWYuCgpgYGB7cn0KbGVhZl9ub2RlcyA8LSBhcy5udW1lcmljKHJvdy5uYW1lcyh0cmVlX2Rpc3BsYXkkZnJhbWVbdHJlZV9kaXNwbGF5JGZyYW1lJHZhciA9PSAiPGxlYWY+IiwgXSkpCnRyZWVfcGF0aHMgPC0gcGF0aC5ycGFydCh0cmVlX2Rpc3BsYXksIG5vZGVzID0gbGVhZl9ub2RlcywgcHJpbnQuaXQgPSBGQUxTRSkKCnRyZWVfcnVsZXMgPC0gZGF0YS5mcmFtZSgKICBMZWFmID0gbGVhZl9ub2RlcywKICBOID0gdHJlZV9kaXNwbGF5JGZyYW1lW2FzLmNoYXJhY3RlcihsZWFmX25vZGVzKSwgIm4iXSwKICBQcmVkaWN0aW9uID0gcm91bmQodHJlZV9kaXNwbGF5JGZyYW1lW2FzLmNoYXJhY3RlcihsZWFmX25vZGVzKSwgInl2YWwiXSwgMSksCiAgUnVsZSA9IHNhcHBseSh0cmVlX3BhdGhzLCBmdW5jdGlvbih6KSBwYXN0ZSh6Wy0xXSwgY29sbGFwc2UgPSAiICYgIikpLAogIHJvdy5uYW1lcyA9IE5VTEwKKQoKdHJlZV9ydWxlcwpgYGAKCkEgc2luZ2xlIHRyZWUgaXMgaW5kZWVkIHZlcnkgaW50ZXJwcmV0YXRpdmUsIHRob3VnaCBpdHMgcHJlZGljdGlvbiBzdHJlbmd0aCBpcyBub3QgaWRlYWwuCgojIEJhZ2dpbmcgYW5kIHJhbmRvbSBmb3Jlc3QKCldlIG5vdyB1c2UgdGhlIGByYW5kb21Gb3Jlc3RgIHBhY2thZ2UgdG8gcGVyZm9ybSBiYWdnaW5nIGFuZCByYW5kb20gZm9yZXN0LgoKLSBCYWdnaW5nOiBib290c3RyYXAgdHJlZXMgd2l0aCBgbXRyeSA9IHBgLCBzbyBlYWNoIHNwbGl0IG1heSBjb25zaWRlciBhbGwgcHJlZGljdG9ycy4KLSBSYW5kb20gZm9yZXN0OiBib290c3RyYXAgdHJlZXMgd2l0aCBgbXRyeSA8IHBgLCBzbyBlYWNoIHNwbGl0IGNvbnNpZGVycyBvbmx5IGEgcmFuZG9tIHN1YnNldCBvZiBwcmVkaWN0b3JzLgoKVGhlIHBhY2thZ2UgcmVwb3J0cyBhbiBvdXQtb2YtYmFnIChPT0IpIE1TRSBhZnRlciBlYWNoIG51bWJlciBvZiB0cmVlcy4KT09CIGVycm9yIGlzIGNvbXB1dGVkIGZyb20gb2JzZXJ2YXRpb25zIHRoYXQgd2VyZSBub3QgaW5jbHVkZWQgaW4gYSB0cmVlJ3MgYm9vdHN0cmFwIHNhbXBsZS4KCiMjIEJhZ2dlZCB0cmVlcwoKQmFnZ2luZyBncm93cyBtYW55IGxhcmdlIHRyZWVzIG9uIGJvb3RzdHJhcCBzYW1wbGVzIGFuZCBhdmVyYWdlcyB0aGVpciBwcmVkaWN0aW9ucy4gSGVyZSBgbXRyeSA9IHBgLCBzbyBlYWNoIHNwbGl0IGNhbiB1c2UgYW55IHByZWRpY3Rvci4KCmBgYHtyfQpzZXQuc2VlZCgzMjk1MCkKcF90cmVlIDwtIG5jb2wodHJhaW4pIC0gMQoKYmFnX2ZpdCA8LSByYW5kb21Gb3Jlc3QoCiAgU2FsYXJ5IH4gLiwKICBkYXRhID0gdHJhaW4sCiAgbnRyZWUgPSA1MDAsCiAgbXRyeSA9IHBfdHJlZSwKICBpbXBvcnRhbmNlID0gVFJVRQopCgpiYWdfZml0CmBgYAoKYGBge3J9CmJhZ19vb2JfbXNlIDwtIGJhZ19maXQkbXNlW2JhZ19maXQkbnRyZWVdCmJhZ19vb2JfbXNlCmBgYAoKV2UgY2FuIHZpc3VhbGl6ZSBob3cgT09CIE1TRSBjaGFuZ2Ugd2l0aCB0aGUgbnVtYmVyIG9mIHRyZWVzLgoKYGBge3J9CiBwbG90KGJhZ19maXQkbXNlLCB0eXBlID0gImwiLAogICAgICAgeGxhYiA9ICJOdW1iZXIgb2YgdHJlZXMiLAogICAgICAgeWxhYiA9ICJPT0IgTVNFIiwKICAgICAgIG1haW4gPSAiQmFnZ2luZzogT09CIGVycm9yIHZzIG51bWJlciBvZiB0cmVlcyIpCgpgYGAKV2Ugc2VlIGhlcmUgdGhhdCBPT0IgTVNFIGFscmVhZHkgc3RhYmlsaXplcyBhdCAkQiA9IDUwMCQuIEluY3JlYXNpbmcgdGhlIG51bWJlciBvZiB0cmVlcyBpbiBiYWdnaW5nIG9yIHJhbmRvbSBmb3Jlc3RzIHR5cGljYWxseSBkb2VzIG5vdCBvdmVyZml0LiBXZSB0eXBpY2FsbHkgc3RhcnQgd2l0aCBhIGxhcmdlICRCJCBhbmQgaW5jcmVhc2UgJEIkIG9ubHkgaWYgT09CIE1TRSBkb2VzIG5vdCBzdGFiaWxpemUuIAoKCmBgYHtyfQpwcmVkX2JhZyA8LSBwcmVkaWN0KGJhZ19maXQsIG5ld2RhdGEgPSB0ZXN0KQptc2VfYmFnIDwtIG1lYW4oKHlfdGVzdCAtIHByZWRfYmFnKV4yKQptc2VfYmFnCmBgYAoKVGhlIHByZWRpY3Rpb24gdGVzdCBlcnJvciBpcyBub3cgc21hbGxlciB0aGFuIGxpbmVhciBtZXRob2RzLCBpbmRpY2F0aW5nIHRoYXQgY29uc2lkZXJpbmcgaW50ZXJhY3Rpb25zIG9mIHByZWRpY3RvcnMgaGVscC4gTm90aWNlIHRoYXQgaW4gdGhlIFIgZGVtbyBvZiBsZWN0dXJlIDEwLCB0aGUgYmVzdCB0ZXN0IGRhdGEgTVNFIHdlIGdldCBpcyA2MDY0NyBmcm9tIExhc3NvIHdpdGggaW50ZXJhY3Rpb24gdGVybXMsIGNvbnNpc3RlbnQgd2l0aCB0aGUgcmVzdWx0cyBoZXJlLiAKCiMjIFJhbmRvbSBmb3Jlc3QKCkZvciByZWdyZXNzaW9uLCBhIGNvbW1vbiBkZWZhdWx0IGlzIFwobV97XG1hdGhybXt0cnl9fSBcYXBwcm94IHAvM1wpLiBUaGlzIGtlZXBzIGVhY2ggdHJlZSBzdHJvbmcgd2hpbGUgcmVkdWNpbmcgY29ycmVsYXRpb24gYWNyb3NzIHRyZWVzLgoKYGBge3J9CnNldC5zZWVkKDMyOTUwKQptdHJ5X3JmIDwtIG1heCgxLCBmbG9vcihwX3RyZWUgLyAzKSkKbXRyeV9yZgpgYGAKCmBgYHtyfQpzZXQuc2VlZCgzMjk1MCkKcmZfZml0IDwtIHJhbmRvbUZvcmVzdCgKICBTYWxhcnkgfiAuLAogIGRhdGEgPSB0cmFpbiwKICBudHJlZSA9IDUwMCwKICBtdHJ5ID0gbXRyeV9yZiwKICBpbXBvcnRhbmNlID0gVFJVRQopCgpyZl9maXQKYGBgCgpgYGB7cn0KcmZfb29iX21zZSA8LSByZl9maXQkbXNlW3JmX2ZpdCRudHJlZV0KcmZfb29iX21zZQpgYGAKCmBgYHtyfQpwbG90KHJmX2ZpdCRtc2UsIHR5cGUgPSAibCIsCiAgICAgICB4bGFiID0gIk51bWJlciBvZiB0cmVlcyIsCiAgICAgICB5bGFiID0gIk9PQiBNU0UiLAogICAgICAgbWFpbiA9ICJSYW5kb20gZm9yZXN0OiBPT0IgZXJyb3IgdnMgbnVtYmVyIG9mIHRyZWVzIikKCmBgYAoKYGBge3J9CnByZWRfcmYgPC0gcHJlZGljdChyZl9maXQsIG5ld2RhdGEgPSB0ZXN0KQptc2VfcmYgPC0gbWVhbigoeV90ZXN0IC0gcHJlZF9yZileMikKbXNlX3JmCmBgYAoKV2UgZnVydGhlciBkcm9wIHRoZSBPT0IgTVNFIGFuZCB0ZXN0IGRhdGEgTVNFIHVzaW5nIHJhbmRvbSBmb3Jlc3QuCgojIyBWYXJpYWJsZSBpbXBvcnRhbmNlIGZvciB0aGUgcmFuZG9tIGZvcmVzdAoKUGVybXV0YXRpb24gaW1wb3J0YW5jZSBtZWFzdXJlcyBob3cgbXVjaCBwcmVkaWN0aW9uIGVycm9yIGluY3JlYXNlcyBhZnRlciBicmVha2luZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gb25lIHByZWRpY3RvciBhbmQgdGhlIHJlc3BvbnNlLgpUaGUgYHJhbmRvbUZvcmVzdGAgcGFja2FnZSBjb21wdXRlcyB0aGlzIHVzaW5nIE9PQiBvYnNlcnZhdGlvbnMuCgpgYGB7cn0KaW1wb3J0YW5jZV9kZiA8LSBkYXRhLmZyYW1lKAogIFZhcmlhYmxlID0gcm93Lm5hbWVzKGltcG9ydGFuY2UocmZfZml0LCB0eXBlID0gMSkpLAogIEluY3JlYXNlX2luX01TRSA9IGltcG9ydGFuY2UocmZfZml0LCB0eXBlID0gMSlbLCAxXSwKICByb3cubmFtZXMgPSBOVUxMCikKCmltcG9ydGFuY2VfdG9wIDwtIGhlYWQoaW1wb3J0YW5jZV9kZltvcmRlcigtaW1wb3J0YW5jZV9kZiRJbmNyZWFzZV9pbl9NU0UpLCBdLCAxMCkKaW1wb3J0YW5jZV90b3AKYGBgCgojIENvbXBhcmUgdGVzdC1zZXQgcGVyZm9ybWFuY2UgYWNyb3NzIG1ldGhvZHMKCmBgYHtyfQpyZXN1bHRzIDwtIGRhdGEuZnJhbWUoCiAgTWV0aG9kID0gYygiTGluZWFyIHJlZ3Jlc3Npb24iLCAiUmlkZ2UiLCAiTGFzc28iLAogICAgICAgICAgICAgIlJpZGdlIHdpdGggdHdvLXdheSBpbnRlcmFjdGlvbnMiLCAiTGFzc28gd2l0aCB0d28td2F5IGludGVyYWN0aW9ucyIsCiAgICAgICAgICAgICAiQ1YtcHJ1bmVkIHRyZWUiLCAiQmFnZ2VkIHRyZWVzIiwgIlJhbmRvbSBmb3Jlc3QiKSwKICBUZXN0X01TRSA9IGMobXNlX2xtLCBtc2VfcmlkZ2UsIG1zZV9sYXNzbywKICAgICAgICAgICAgICAgbXNlX3JpZGdlX2ludCwgbXNlX2xhc3NvX2ludCwKICAgICAgICAgICAgICAgbXNlX3RyZWVfY3YsIG1zZV9iYWcsIG1zZV9yZikKKQoKcmVzdWx0c1tvcmRlcihyZXN1bHRzJFRlc3RfTVNFKSwgXQpgYGAKCmBgYHtyfQpwcmVkX2RmIDwtIGRhdGEuZnJhbWUoCiAgU2FsYXJ5ID0gcmVwKHlfdGVzdCwgOCksCiAgUHJlZGljdGlvbiA9IGMocHJlZF9sbSwgYXMubnVtZXJpYyhwcmVkX3JpZGdlKSwgYXMubnVtZXJpYyhwcmVkX2xhc3NvKSwKICAgICAgICAgICAgICAgICBhcy5udW1lcmljKHByZWRfcmlkZ2VfaW50KSwgYXMubnVtZXJpYyhwcmVkX2xhc3NvX2ludCksCiAgICAgICAgICAgICAgICAgcHJlZF90cmVlX2N2LCBwcmVkX2JhZywgcHJlZF9yZiksCiAgTWV0aG9kID0gcmVwKGMoIkxpbmVhciByZWdyZXNzaW9uIiwgIlJpZGdlIiwgIkxhc3NvIiwKICAgICAgICAgICAgICAgICAiUmlkZ2Ugd2l0aCBpbnRlcmFjdGlvbnMiLCAiTGFzc28gd2l0aCBpbnRlcmFjdGlvbnMiLAogICAgICAgICAgICAgICAgICJDVi1wcnVuZWQgdHJlZSIsICJCYWdnZWQgdHJlZXMiLCAiUmFuZG9tIGZvcmVzdCIpLAogICAgICAgICAgICAgICBlYWNoID0gbGVuZ3RoKHlfdGVzdCkpCikKCmdncGxvdChwcmVkX2RmLCBhZXMoeCA9IFNhbGFyeSwgeSA9IFByZWRpY3Rpb24sIGNvbG9yID0gTWV0aG9kKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjcpICsKICBnZW9tX2FibGluZShzbG9wZSA9IDEsIGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gMikgKwogIGZhY2V0X3dyYXAofiBNZXRob2QpICsKICBsYWJzKAogICAgdGl0bGUgPSAiT2JzZXJ2ZWQgdmVyc3VzIHByZWRpY3RlZCBzYWxhcnkgb24gdGhlIHRlc3Qgc2V0IiwKICAgIHggPSAiT2JzZXJ2ZWQgc2FsYXJ5IiwKICAgIHkgPSAiUHJlZGljdGVkIHNhbGFyeSIKICApICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEzKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiBJbiB0aGlzIHRlc3Qgc3BsaXQsIHRoZSB0d28gaGlnaGVzdC1zYWxhcnkgb2JzZXJ2YXRpb25zIGFyZSBmaXR0ZWQgYmV0dGVyIGJ5IHRoZSBsaW5lYXIgbWV0aG9kcyB0aGFuIGJ5IHRoZSB0cmVlLWJhc2VkIG1ldGhvZHMuIE9uZSByZWFzb24gaXMgdGhhdCB0aGVzZSBzYWxhcmllcyBhcmUgbXVjaCBsYXJnZXIKICB0aGFuIG1vc3Qgb3RoZXIgb2JzZXJ2YXRpb25zLiBSZWdyZXNzaW9uIHRyZWVzLCBiYWdnaW5nLCBhbmQgcmFuZG9tIGZvcmVzdHMgbWFrZSBwcmVkaWN0aW9ucyBieSBhdmVyYWdpbmcgdHJhaW5pbmcgcmVzcG9uc2VzIHdpdGhpbiBsZWF2ZXMgb3IgYWNyb3NzIHRyZWVzLCBzbyBwcmVkaWN0aW9ucyBmb3IKICBleHRyZW1lIG9ic2VydmF0aW9ucyBhcmUgb2Z0ZW4gcHVsbGVkIGJhY2sgdG93YXJkIG1vcmUgdHlwaWNhbCBzYWxhcmllcy4gTGluZWFyIG1ldGhvZHMgY2FuIHByb2R1Y2UgbW9yZSBleHRyZW1lIGZpdHRlZCB2YWx1ZXMgdGhyb3VnaCBsaW5lYXIgY29tYmluYXRpb25zIG9mIHByZWRpY3RvcnMsCiAgZXNwZWNpYWxseSB3aGVuIGludGVyYWN0aW9uIHRlcm1zIGFyZSBpbmNsdWRlZC4gQmVjYXVzZSBNU0Ugc3F1YXJlcyBlcnJvcnMsIHVuZGVycHJlZGljdGluZyBhIGZldyB2ZXJ5IGhpZ2gtc2FsYXJ5IHBsYXllcnMgY2FuIG5vdGljZWFibHkgaW5jcmVhc2UgdGhlIHRlc3QgTVNFIGZvciB0cmVlLWJhc2VkCiAgbWV0aG9kcywgd2hpY2ggbWF5IHBhcnRseSBleHBsYWluIHdoeSB0aGUgaW50ZXJhY3Rpb24gTGFzc28gcGVyZm9ybXMgd2VsbCBoZXJlLgoKICBGb3IgbWFueSBsb3dlci1zYWxhcnkgb2JzZXJ2YXRpb25zLCB0aGUgc2NhdHRlciBwbG90IHN1Z2dlc3RzIHRoYXQgdGhlIHRyZWUtYmFzZWQgbWV0aG9kcyBwcmVkaWN0IGJldHRlciB0aGFuIHRoZSBsaW5lYXIgbWV0aG9kcy4gVGhpcyBpcyBjb25zaXN0ZW50IHdpdGggdGhlIGF2ZXJhZ2luZyBuYXR1cmUgb2YKICB0cmVlczogaW4gdGhlIGRlbnNlciBsb3dlci1zYWxhcnkgcmFuZ2UsIGxvY2FsIGF2ZXJhZ2luZyBjYW4gc3RhYmlsaXplIHByZWRpY3Rpb25zIGFuZCByZWR1Y2UgbGFyZ2UgZGV2aWF0aW9ucy4gVGh1cywgdGhlIHNhbWUgYXZlcmFnaW5nIGJlaGF2aW9yIGNhbiBoZWxwIHRyZWUtYmFzZWQgbWV0aG9kcyBmb3IKICB0eXBpY2FsIG9ic2VydmF0aW9ucyB3aGlsZSBodXJ0aW5nIHRoZW0gZm9yIGV4dHJlbWUgaGlnaC1zYWxhcnkgb2JzZXJ2YXRpb25zLgoKCgoKIyMgVGFrZWF3YXkKCi0gVGhlIHNhbWUgYEhpdHRlcnNgIHByZXByb2Nlc3NpbmcgZnJvbSBMZWN0dXJlIDEwIHdvcmtzIGhlcmU6IHJlbW92ZSBtaXNzaW5nIHZhbHVlcyBhbmQgZW5jb2RlIGZhY3RvcnMgd2hlbiBhIG51bWVyaWMgbWF0cml4IGlzIG5lZWRlZC4KLSBMaW5lYXIgbWV0aG9kcyBnaXZlIHVzZWZ1bCBiYXNlbGluZXMuCi0gQSBzbWFsbCBwcnVuZWQgdHJlZSBpcyBpbnRlcnByZXRhYmxlIGJ1dCBjYW4gaGF2ZSBoaWdoIHByZWRpY3Rpb24gdmFyaWFuY2UuCi0gQmFnZ2luZyBhdmVyYWdlcyBtYW55IGJvb3RzdHJhcCB0cmVlcyBhbmQgdXN1YWxseSBpbXByb3ZlcyB0ZXN0IGVycm9yLgotIFJhbmRvbSBmb3Jlc3RzIGFkZCByYW5kb20gcHJlZGljdG9yIHNlbGVjdGlvbiBhdCBlYWNoIHNwbGl0LCByZWR1Y2luZyBjb3JyZWxhdGlvbiBhbW9uZyB0cmVlcy4KLSBWYXJpYWJsZSBpbXBvcnRhbmNlIHN1bW1hcml6ZXMgYSBmb3Jlc3QsIGJ1dCBpdCBpcyBwcmVkaWN0aXZlIHJhdGhlciB0aGFuIGNhdXNhbC4K