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:
- remove rows with missing salary by using
na.omit;
- keep the original variables;
- convert factor predictors to dummy variables with
model.matrix when a numeric matrix is needed.
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
- Variables such as
AtBat, Hits,
HmRun, Runs, RBI, and
Walks describe recent batting performance.
- Variables beginning with
C are career totals.
PutOuts, Assists, and Errors
describe fielding.
League, Division, and
NewLeague are categorical indicators.
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:
nsplit: number of internal splits in the tree;
CP: rpart’s scaled version of the pruning
penalty \(\alpha\);
rel_error: training error relative to the root-only
tree;
xerror: cross-validation error relative to the
root-only tree;
xstd: standard error of the cross-validation
error.
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.
- Bagging: bootstrap trees with
mtry = p, so each split
may consider all predictors.
- Random forest: bootstrap trees with
mtry < p, so
each split considers only a random subset of predictors.
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