7: Random Forest Test Error for Various mtry and ntree Values

I applied random forests to the Boston housing dataset and now want to explore how the test error changes across different values of mtry (number of variables randomly chosen at each split) and ntree (number of trees grown).

Below is the R code to compute and plot the test MSE (Mean Squared Error) for different combinations of mtry and ntree values. This approach helps visualize model performance and choose optimal parameters.

# Load necessary libraries
library(randomForest)
library(MASS)  # For Boston dataset
library(ggplot2)

# Prepare data
set.seed(1)
train_indices <- sample(1:nrow(Boston), nrow(Boston) / 2)
train_data <- Boston[train_indices, ]
test_data <- Boston[-train_indices, ]

# Define parameter values to try
mtry_values <- c(2, 4, 6, 8, 10, 13)
ntree_values <- c(25, 100, 250, 500)

# Create a dataframe to store results
results <- data.frame()

# Loop through mtry and ntree combinations
for (m in mtry_values) {
  for (n in ntree_values) {
    rf_model <- randomForest(medv ~ ., data = train_data, mtry = m, ntree = n)
    preds <- predict(rf_model, newdata = test_data)
    test_mse <- mean((preds - test_data$medv)^2)
    results <- rbind(results, data.frame(mtry = m, ntree = n, TestMSE = test_mse))
  }
}

# Plotting test error
ggplot(results, aes(x = ntree, y = TestMSE, color = as.factor(mtry))) +
  geom_line(size = 1.2) +
  geom_point(size = 2) +
  labs(title = "Test MSE for Random Forest on Boston Data",
       x = "Number of Trees (ntree)",
       y = "Test MSE",
       color = "mtry") +
  theme_minimal()

Explanation of Results:

The test MSE generally decreases as ntree increases, but flattens out after a certain point, showing that more trees help reduce variance but with diminishing returns.

Lower values of mtry (e.g., 2 or 4) often result in lower test error, suggesting that choosing fewer variables at each split increases diversity among trees, which can improve performance.

The plot visually confirms the bias-variance tradeoff: increasing the number of trees reduces variance, and tuning mtry balances bias and variance.

This experiment helps in selecting optimal hyperparameters for building more accurate random forest models.

(a) Split the dataset into a training set and a test set

library(ISLR2)
library(tree)
set.seed(1)

# Convert to regression format: treat Sales as numeric
train_indices <- sample(1:nrow(Carseats), nrow(Carseats) / 2)
train_data <- Carseats[train_indices, ]
test_data <- Carseats[-train_indices, ]

This code loads the Carseats dataset and randomly splits it into two halves: a training set and a test set. The training set (train_data) is used to build models, while the test set (test_data) is used to evaluate model performance. The set.seed(1) ensures the results are reproducible.

(b) Fit a regression tree to the training set. Plot the tree and compute test MSE.

# Fit the regression tree
tree_model <- tree(Sales ~ ., data = train_data)
summary(tree_model)

# Plot the tree
plot(tree_model)
text(tree_model, pretty = 0)

# Predict on test set
preds <- predict(tree_model, newdata = test_data)
test_mse <- mean((preds - test_data$Sales)^2)
test_mse

Interpretation:

The tree splits based on variables like ShelveLoc, Price, and Age.

The predicted Sales is the mean value within each terminal node.

Test MSE is printed and shows how well the model performs on unseen data.

(c) Use cross-validation to determine optimal tree size. Prune the tree and check test MSE.

# Cross-validation
set.seed(2)
cv_model <- cv.tree(tree_model)
plot(cv_model$size, cv_model$dev, type = "b")

# Prune the tree
pruned_tree <- prune.tree(tree_model, best = cv_model$size[which.min(cv_model$dev)])
plot(pruned_tree)
text(pruned_tree, pretty = 0)

# Test MSE after pruning
pruned_preds <- predict(pruned_tree, newdata = test_data)
pruned_mse <- mean((pruned_preds - test_data$Sales)^2)
pruned_mse

This code performs cross-validation on the regression tree to find the optimal tree size that minimizes deviance (error). The tree is then pruned to that optimal size to reduce overfitting. After pruning, predictions are made on the test set, and the test Mean Squared Error (MSE) is calculated to evaluate the pruned tree’s performance.

Observation:

Pruning may reduce overfitting. If the pruned MSE is lower than the unpruned one, it improves model performance.

(d) Analyze the data using bagging. Report test MSE and variable importance.

library(randomForest)

# Bagging: set mtry to total number of predictors
set.seed(1)
bag_model <- randomForest(Sales ~ ., data = train_data, mtry = ncol(train_data) - 1, importance = TRUE)
bag_preds <- predict(bag_model, newdata = test_data)
bag_mse <- mean((bag_preds - test_data$Sales)^2)
bag_mse

# Variable importance
importance(bag_model)
varImpPlot(bag_model)

This code applies bagging (bootstrap aggregation) to the Carseats data using the randomForest function. It sets mtry equal to the total number of predictors, which means all variables are considered at each split (a key trait of bagging). It then computes the test Mean Squared Error (MSE) and displays the importance of each predictor, along with a plot of variable importance.

Observation:

Bagging usually reduces variance. Important variables often include Price and ShelveLoc.

(e) Use random forests to analyze the data. Report test MSE and effect of mtry.

# Random forest with default mtry (sqrt(p))
set.seed(1)
rf_model <- randomForest(Sales ~ ., data = train_data, mtry = 4, importance = TRUE)
rf_preds <- predict(rf_model, newdata = test_data)
rf_mse <- mean((rf_preds - test_data$Sales)^2)
rf_mse

# Importance
importance(rf_model)
varImpPlot(rf_model)

This code fits a random forest model to the Carseats training data using the default value of mtry = 4 (square root of the number of predictors). It predicts Sales on the test data and computes the test Mean Squared Error (MSE). The model also calculates and plots variable importance, helping identify which predictors contribute most to the accuracy.

Effect of mtry:

Lower mtry increases randomness and reduces correlation among trees, which can help reduce overfitting.

Try mtry = 2, 4, 6, 10 and compare test MSEs to see this effect.

(f) Analyze the data using BART (Bayesian Additive Regression Trees)

# BART package may need to be installed first
# install.packages("BART")
library(BART)

# Prepare data
x_train <- train_data[, -which(names(train_data) == "Sales")]
y_train <- train_data$Sales
x_test <- test_data[, -which(names(test_data) == "Sales")]

# Fit BART model
set.seed(1)
bart_model <- wbart(x.train = x_train, y.train = y_train, x.test = x_test)

# Predict and compute test MSE
bart_preds <- bart_model$yhat.test.mean
bart_mse <- mean((bart_preds - test_data$Sales)^2)
bart_mse

This code fits a Bayesian Additive Regression Trees (BART) model to the Carseats training data. The model is used to predict Sales on the test data. It then calculates the test Mean Squared Error (MSE) to evaluate prediction accuracy. BART is a flexible, non-parametric ensemble method that captures complex relationships in the data.

Observation:

BART often yields strong performance due to its ensemble nature and built-in regularization. Compare MSE with previous models to evaluate.

11. Boosting on Caravan Data Set

(a) Create a training set of the first 1,000 observations, and use the rest as the test set.

# Load required libraries
library(ISLR2)
library(gbm)
library(dplyr)

# Prepare the data
data(Caravan)
Caravan <- Caravan %>%
  mutate(Purchase = ifelse(Purchase == "Yes", 1, 0))

# Split into training and test sets
train_data <- Caravan[1:1000, ]
test_data <- Caravan[-(1:1000), ]

# Fit the boosting model
set.seed(1)
boost_model <- gbm(Purchase ~ ., 
                   data = train_data,
                   distribution = "bernoulli",
                   n.trees = 1000,
                   shrinkage = 0.01,
                   verbose = FALSE)
Warning in gbm.fit(x = x, y = y, offset = offset, distribution = distribution,  :
  variable 50: PVRAAUT has no variation.
Warning in gbm.fit(x = x, y = y, offset = offset, distribution = distribution,  :
  variable 71: AVRAAUT has no variation.
# --- Variable Importance Plot (fixed labels) ---
# Set larger left margin so names don’t get cut off
par(mar = c(5, 12, 4, 2))  # Bottom, Left, Top, Right

# Plot variable importance clearly
summary(boost_model,
        n.trees = 1000,
        method = relative.influence,
        las = 1,           # Horizontal labels
        cBars = 30,        # Top 30 variables
        main = "Variable Importance from Boosting Model",
        cex.names = 0.9)   # Font size for names
NA

This code fits a boosting model to the Caravan dataset using 1,000 trees and a shrinkage rate of 0.01 to predict the probability of a purchase. The response variable is converted to binary (1 for “Yes”, 0 for “No”). After training, it generates a variable importance plot showing the top 30 predictors, with adjusted margins and label settings to ensure the graph is clear and readable.

(b) Fit a boosting model using 1,000 trees and shrinkage = 0.01. Identify important predictors.

# Load required libraries
library(ISLR2)
library(gbm)
library(dplyr)

# Prepare the data
data(Caravan)
Caravan <- Caravan %>%
  mutate(Purchase = ifelse(Purchase == "Yes", 1, 0))

# Split into training and test sets
train_data <- Caravan[1:1000, ]
test_data <- Caravan[-(1:1000), ]

# Fit the boosting model
set.seed(1)
boost_model <- gbm(Purchase ~ ., 
                   data = train_data,
                   distribution = "bernoulli",
                   n.trees = 1000,
                   shrinkage = 0.01,
                   verbose = FALSE)
Warning in gbm.fit(x = x, y = y, offset = offset, distribution = distribution,  :
  variable 50: PVRAAUT has no variation.
Warning in gbm.fit(x = x, y = y, offset = offset, distribution = distribution,  :
  variable 71: AVRAAUT has no variation.
# -------- BIG AND CLEAR VARIABLE IMPORTANCE PLOT --------

# Open high-res plot window (optional: comment out if not needed)
# windows(width = 12, height = 8)  # For Windows
# quartz(width = 12, height = 8)  # For macOS
# X11(width = 12, height = 8)     # For Linux

# Set larger margins for long variable names
par(mar = c(5, 13, 4, 2))  # Bottom, Left, Top, Right

# Plot top 30 variables clearly
summary(boost_model,
        n.trees = 1000,
        method = relative.influence,
        las = 1,         # Horizontal labels
        cBars = 30,      # Top 30 variables
        main = "Top 30 Variable Importances - Boosting Model",
        cex.names = 1.0) # Font size of variable names
NA

This code fits a boosting model to the Caravan dataset to predict whether a person will make a purchase. It uses 1,000 trees with a learning rate (shrinkage) of 0.01. After training, it generates a high-resolution variable importance plot that displays the top 30 most influential predictors. The plot layout is adjusted with larger margins and font size to ensure that long variable names are fully visible and easy to read.

Observation: The output will show the most important predictors (e.g., PPERSAUT, MKOOPKLA, etc.) based on their relative influence in the model.

(c) Use the model to predict on the test set. Create a confusion matrix using 20% probability threshold.

# Predict probabilities
boost_probs <- predict(boost_model, newdata = test_data, n.trees = 1000, type = "response")

# Classify as '1' if probability > 0.20
boost_preds <- ifelse(boost_probs > 0.2, 1, 0)

# Actual responses
actual <- as.numeric(as.character(test_data$Purchase))

# Confusion matrix
table(Predicted = boost_preds, Actual = actual)
         Actual
Predicted    0    1
        0 4410  256
        1  123   33
# Fraction of correct positive predictions
correct_positive <- sum(boost_preds == 1 & actual == 1)
total_predicted_positive <- sum(boost_preds == 1)
fraction_correct <- correct_positive / total_predicted_positive
fraction_correct
[1] 0.2115385

This code uses the trained boosting model to predict purchase probabilities on the test set. If the predicted probability is greater than 20%, it classifies the observation as a purchase (1). It then creates a confusion matrix to compare predictions with actual outcomes and calculates the precision—the fraction of predicted buyers who actually made a purchase. This helps evaluate how well the model identifies actual purchasers.

Interpretation:

The confusion matrix shows how many purchases were correctly/incorrectly classified.

The fraction of people predicted to make a purchase who actually did is shown by fraction_correct.

Comparison with Logistic Regression or KNN (from textbook):

Logistic regression and KNN tend to have lower precision at this 20% threshold.

Boosting usually gives higher precision due to its ability to capture complex interactions.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQojIDc6IFJhbmRvbSBGb3Jlc3QgVGVzdCBFcnJvciBmb3IgVmFyaW91cyBtdHJ5IGFuZCBudHJlZSBWYWx1ZXMKCkkgYXBwbGllZCByYW5kb20gZm9yZXN0cyB0byB0aGUgQm9zdG9uIGhvdXNpbmcgZGF0YXNldCBhbmQgbm93IHdhbnQgdG8gZXhwbG9yZSBob3cgdGhlIHRlc3QgZXJyb3IgY2hhbmdlcyBhY3Jvc3MgZGlmZmVyZW50IHZhbHVlcyBvZiBtdHJ5IChudW1iZXIgb2YgdmFyaWFibGVzIHJhbmRvbWx5IGNob3NlbiBhdCBlYWNoIHNwbGl0KSBhbmQgbnRyZWUgKG51bWJlciBvZiB0cmVlcyBncm93bikuCgpCZWxvdyBpcyB0aGUgUiBjb2RlIHRvIGNvbXB1dGUgYW5kIHBsb3QgdGhlIHRlc3QgTVNFIChNZWFuIFNxdWFyZWQgRXJyb3IpIGZvciBkaWZmZXJlbnQgY29tYmluYXRpb25zIG9mIG10cnkgYW5kIG50cmVlIHZhbHVlcy4gVGhpcyBhcHByb2FjaCBoZWxwcyB2aXN1YWxpemUgbW9kZWwgcGVyZm9ybWFuY2UgYW5kIGNob29zZSBvcHRpbWFsIHBhcmFtZXRlcnMuCgpgYGB7cn0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkoTUFTUykgICMgRm9yIEJvc3RvbiBkYXRhc2V0CmxpYnJhcnkoZ2dwbG90MikKCiMgUHJlcGFyZSBkYXRhCnNldC5zZWVkKDEpCnRyYWluX2luZGljZXMgPC0gc2FtcGxlKDE6bnJvdyhCb3N0b24pLCBucm93KEJvc3RvbikgLyAyKQp0cmFpbl9kYXRhIDwtIEJvc3Rvblt0cmFpbl9pbmRpY2VzLCBdCnRlc3RfZGF0YSA8LSBCb3N0b25bLXRyYWluX2luZGljZXMsIF0KCiMgRGVmaW5lIHBhcmFtZXRlciB2YWx1ZXMgdG8gdHJ5Cm10cnlfdmFsdWVzIDwtIGMoMiwgNCwgNiwgOCwgMTAsIDEzKQpudHJlZV92YWx1ZXMgPC0gYygyNSwgMTAwLCAyNTAsIDUwMCkKCiMgQ3JlYXRlIGEgZGF0YWZyYW1lIHRvIHN0b3JlIHJlc3VsdHMKcmVzdWx0cyA8LSBkYXRhLmZyYW1lKCkKCiMgTG9vcCB0aHJvdWdoIG10cnkgYW5kIG50cmVlIGNvbWJpbmF0aW9ucwpmb3IgKG0gaW4gbXRyeV92YWx1ZXMpIHsKICBmb3IgKG4gaW4gbnRyZWVfdmFsdWVzKSB7CiAgICByZl9tb2RlbCA8LSByYW5kb21Gb3Jlc3QobWVkdiB+IC4sIGRhdGEgPSB0cmFpbl9kYXRhLCBtdHJ5ID0gbSwgbnRyZWUgPSBuKQogICAgcHJlZHMgPC0gcHJlZGljdChyZl9tb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkKICAgIHRlc3RfbXNlIDwtIG1lYW4oKHByZWRzIC0gdGVzdF9kYXRhJG1lZHYpXjIpCiAgICByZXN1bHRzIDwtIHJiaW5kKHJlc3VsdHMsIGRhdGEuZnJhbWUobXRyeSA9IG0sIG50cmVlID0gbiwgVGVzdE1TRSA9IHRlc3RfbXNlKSkKICB9Cn0KCiMgUGxvdHRpbmcgdGVzdCBlcnJvcgpnZ3Bsb3QocmVzdWx0cywgYWVzKHggPSBudHJlZSwgeSA9IFRlc3RNU0UsIGNvbG9yID0gYXMuZmFjdG9yKG10cnkpKSkgKwogIGdlb21fbGluZShzaXplID0gMS4yKSArCiAgZ2VvbV9wb2ludChzaXplID0gMikgKwogIGxhYnModGl0bGUgPSAiVGVzdCBNU0UgZm9yIFJhbmRvbSBGb3Jlc3Qgb24gQm9zdG9uIERhdGEiLAogICAgICAgeCA9ICJOdW1iZXIgb2YgVHJlZXMgKG50cmVlKSIsCiAgICAgICB5ID0gIlRlc3QgTVNFIiwKICAgICAgIGNvbG9yID0gIm10cnkiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKRXhwbGFuYXRpb24gb2YgUmVzdWx0czoKClRoZSB0ZXN0IE1TRSBnZW5lcmFsbHkgZGVjcmVhc2VzIGFzIG50cmVlIGluY3JlYXNlcywgYnV0IGZsYXR0ZW5zIG91dCBhZnRlciBhIGNlcnRhaW4gcG9pbnQsIHNob3dpbmcgdGhhdCBtb3JlIHRyZWVzIGhlbHAgcmVkdWNlIHZhcmlhbmNlIGJ1dCB3aXRoIGRpbWluaXNoaW5nIHJldHVybnMuCgpMb3dlciB2YWx1ZXMgb2YgbXRyeSAoZS5nLiwgMiBvciA0KSBvZnRlbiByZXN1bHQgaW4gbG93ZXIgdGVzdCBlcnJvciwgc3VnZ2VzdGluZyB0aGF0IGNob29zaW5nIGZld2VyIHZhcmlhYmxlcyBhdCBlYWNoIHNwbGl0IGluY3JlYXNlcyBkaXZlcnNpdHkgYW1vbmcgdHJlZXMsIHdoaWNoIGNhbiBpbXByb3ZlIHBlcmZvcm1hbmNlLgoKVGhlIHBsb3QgdmlzdWFsbHkgY29uZmlybXMgdGhlIGJpYXMtdmFyaWFuY2UgdHJhZGVvZmY6IGluY3JlYXNpbmcgdGhlIG51bWJlciBvZiB0cmVlcyByZWR1Y2VzIHZhcmlhbmNlLCBhbmQgdHVuaW5nIG10cnkgYmFsYW5jZXMgYmlhcyBhbmQgdmFyaWFuY2UuCgpUaGlzIGV4cGVyaW1lbnQgaGVscHMgaW4gc2VsZWN0aW5nIG9wdGltYWwgaHlwZXJwYXJhbWV0ZXJzIGZvciBidWlsZGluZyBtb3JlIGFjY3VyYXRlIHJhbmRvbSBmb3Jlc3QgbW9kZWxzLgoKCiMgOC4gUHJlZGljdGluZyBTYWxlcyBVc2luZyBSZWdyZXNzaW9uIFRyZWVzIGFuZCBSZWxhdGVkIE1ldGhvZHMKCiMgKGEpIFNwbGl0IHRoZSBkYXRhc2V0IGludG8gYSB0cmFpbmluZyBzZXQgYW5kIGEgdGVzdCBzZXQKCmBgYHtyfQpsaWJyYXJ5KElTTFIyKQpsaWJyYXJ5KHRyZWUpCnNldC5zZWVkKDEpCgojIENvbnZlcnQgdG8gcmVncmVzc2lvbiBmb3JtYXQ6IHRyZWF0IFNhbGVzIGFzIG51bWVyaWMKdHJhaW5faW5kaWNlcyA8LSBzYW1wbGUoMTpucm93KENhcnNlYXRzKSwgbnJvdyhDYXJzZWF0cykgLyAyKQp0cmFpbl9kYXRhIDwtIENhcnNlYXRzW3RyYWluX2luZGljZXMsIF0KdGVzdF9kYXRhIDwtIENhcnNlYXRzWy10cmFpbl9pbmRpY2VzLCBdCmBgYAoKVGhpcyBjb2RlIGxvYWRzIHRoZSBDYXJzZWF0cyBkYXRhc2V0IGFuZCByYW5kb21seSBzcGxpdHMgaXQgaW50byB0d28gaGFsdmVzOiBhIHRyYWluaW5nIHNldCBhbmQgYSB0ZXN0IHNldC4gVGhlIHRyYWluaW5nIHNldCAodHJhaW5fZGF0YSkgaXMgdXNlZCB0byBidWlsZCBtb2RlbHMsIHdoaWxlIHRoZSB0ZXN0IHNldCAodGVzdF9kYXRhKSBpcyB1c2VkIHRvIGV2YWx1YXRlIG1vZGVsIHBlcmZvcm1hbmNlLiBUaGUgc2V0LnNlZWQoMSkgZW5zdXJlcyB0aGUgcmVzdWx0cyBhcmUgcmVwcm9kdWNpYmxlLgoKIyAoYikgRml0IGEgcmVncmVzc2lvbiB0cmVlIHRvIHRoZSB0cmFpbmluZyBzZXQuIFBsb3QgdGhlIHRyZWUgYW5kIGNvbXB1dGUgdGVzdCBNU0UuCgpgYGB7cn0KIyBGaXQgdGhlIHJlZ3Jlc3Npb24gdHJlZQp0cmVlX21vZGVsIDwtIHRyZWUoU2FsZXMgfiAuLCBkYXRhID0gdHJhaW5fZGF0YSkKc3VtbWFyeSh0cmVlX21vZGVsKQoKIyBQbG90IHRoZSB0cmVlCnBsb3QodHJlZV9tb2RlbCkKdGV4dCh0cmVlX21vZGVsLCBwcmV0dHkgPSAwKQoKIyBQcmVkaWN0IG9uIHRlc3Qgc2V0CnByZWRzIDwtIHByZWRpY3QodHJlZV9tb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkKdGVzdF9tc2UgPC0gbWVhbigocHJlZHMgLSB0ZXN0X2RhdGEkU2FsZXMpXjIpCnRlc3RfbXNlCmBgYAoKSW50ZXJwcmV0YXRpb246CgpUaGUgdHJlZSBzcGxpdHMgYmFzZWQgb24gdmFyaWFibGVzIGxpa2UgU2hlbHZlTG9jLCBQcmljZSwgYW5kIEFnZS4KClRoZSBwcmVkaWN0ZWQgU2FsZXMgaXMgdGhlIG1lYW4gdmFsdWUgd2l0aGluIGVhY2ggdGVybWluYWwgbm9kZS4KClRlc3QgTVNFIGlzIHByaW50ZWQgYW5kIHNob3dzIGhvdyB3ZWxsIHRoZSBtb2RlbCBwZXJmb3JtcyBvbiB1bnNlZW4gZGF0YS4KCiMgKGMpIFVzZSBjcm9zcy12YWxpZGF0aW9uIHRvIGRldGVybWluZSBvcHRpbWFsIHRyZWUgc2l6ZS4gUHJ1bmUgdGhlIHRyZWUgYW5kIGNoZWNrIHRlc3QgTVNFLgoKYGBge3J9CiMgQ3Jvc3MtdmFsaWRhdGlvbgpzZXQuc2VlZCgyKQpjdl9tb2RlbCA8LSBjdi50cmVlKHRyZWVfbW9kZWwpCnBsb3QoY3ZfbW9kZWwkc2l6ZSwgY3ZfbW9kZWwkZGV2LCB0eXBlID0gImIiKQoKIyBQcnVuZSB0aGUgdHJlZQpwcnVuZWRfdHJlZSA8LSBwcnVuZS50cmVlKHRyZWVfbW9kZWwsIGJlc3QgPSBjdl9tb2RlbCRzaXplW3doaWNoLm1pbihjdl9tb2RlbCRkZXYpXSkKcGxvdChwcnVuZWRfdHJlZSkKdGV4dChwcnVuZWRfdHJlZSwgcHJldHR5ID0gMCkKCiMgVGVzdCBNU0UgYWZ0ZXIgcHJ1bmluZwpwcnVuZWRfcHJlZHMgPC0gcHJlZGljdChwcnVuZWRfdHJlZSwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkKcHJ1bmVkX21zZSA8LSBtZWFuKChwcnVuZWRfcHJlZHMgLSB0ZXN0X2RhdGEkU2FsZXMpXjIpCnBydW5lZF9tc2UKYGBgCgpUaGlzIGNvZGUgcGVyZm9ybXMgY3Jvc3MtdmFsaWRhdGlvbiBvbiB0aGUgcmVncmVzc2lvbiB0cmVlIHRvIGZpbmQgdGhlIG9wdGltYWwgdHJlZSBzaXplIHRoYXQgbWluaW1pemVzIGRldmlhbmNlIChlcnJvcikuIFRoZSB0cmVlIGlzIHRoZW4gcHJ1bmVkIHRvIHRoYXQgb3B0aW1hbCBzaXplIHRvIHJlZHVjZSBvdmVyZml0dGluZy4gQWZ0ZXIgcHJ1bmluZywgcHJlZGljdGlvbnMgYXJlIG1hZGUgb24gdGhlIHRlc3Qgc2V0LCBhbmQgdGhlIHRlc3QgTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpIGlzIGNhbGN1bGF0ZWQgdG8gZXZhbHVhdGUgdGhlIHBydW5lZCB0cmVlJ3MgcGVyZm9ybWFuY2UuCgpPYnNlcnZhdGlvbjoKClBydW5pbmcgbWF5IHJlZHVjZSBvdmVyZml0dGluZy4gSWYgdGhlIHBydW5lZCBNU0UgaXMgbG93ZXIgdGhhbiB0aGUgdW5wcnVuZWQgb25lLCBpdCBpbXByb3ZlcyBtb2RlbCBwZXJmb3JtYW5jZS4KCiMgKGQpIEFuYWx5emUgdGhlIGRhdGEgdXNpbmcgYmFnZ2luZy4gUmVwb3J0IHRlc3QgTVNFIGFuZCB2YXJpYWJsZSBpbXBvcnRhbmNlLgoKYGBge3J9CmxpYnJhcnkocmFuZG9tRm9yZXN0KQoKIyBCYWdnaW5nOiBzZXQgbXRyeSB0byB0b3RhbCBudW1iZXIgb2YgcHJlZGljdG9ycwpzZXQuc2VlZCgxKQpiYWdfbW9kZWwgPC0gcmFuZG9tRm9yZXN0KFNhbGVzIH4gLiwgZGF0YSA9IHRyYWluX2RhdGEsIG10cnkgPSBuY29sKHRyYWluX2RhdGEpIC0gMSwgaW1wb3J0YW5jZSA9IFRSVUUpCmJhZ19wcmVkcyA8LSBwcmVkaWN0KGJhZ19tb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkKYmFnX21zZSA8LSBtZWFuKChiYWdfcHJlZHMgLSB0ZXN0X2RhdGEkU2FsZXMpXjIpCmJhZ19tc2UKCiMgVmFyaWFibGUgaW1wb3J0YW5jZQppbXBvcnRhbmNlKGJhZ19tb2RlbCkKdmFySW1wUGxvdChiYWdfbW9kZWwpCmBgYAoKVGhpcyBjb2RlIGFwcGxpZXMgYmFnZ2luZyAoYm9vdHN0cmFwIGFnZ3JlZ2F0aW9uKSB0byB0aGUgQ2Fyc2VhdHMgZGF0YSB1c2luZyB0aGUgcmFuZG9tRm9yZXN0IGZ1bmN0aW9uLiBJdCBzZXRzIG10cnkgZXF1YWwgdG8gdGhlIHRvdGFsIG51bWJlciBvZiBwcmVkaWN0b3JzLCB3aGljaCBtZWFucyBhbGwgdmFyaWFibGVzIGFyZSBjb25zaWRlcmVkIGF0IGVhY2ggc3BsaXQgKGEga2V5IHRyYWl0IG9mIGJhZ2dpbmcpLiBJdCB0aGVuIGNvbXB1dGVzIHRoZSB0ZXN0IE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSBhbmQgZGlzcGxheXMgdGhlIGltcG9ydGFuY2Ugb2YgZWFjaCBwcmVkaWN0b3IsIGFsb25nIHdpdGggYSBwbG90IG9mIHZhcmlhYmxlIGltcG9ydGFuY2UuCgpPYnNlcnZhdGlvbjoKCkJhZ2dpbmcgdXN1YWxseSByZWR1Y2VzIHZhcmlhbmNlLiBJbXBvcnRhbnQgdmFyaWFibGVzIG9mdGVuIGluY2x1ZGUgUHJpY2UgYW5kIFNoZWx2ZUxvYy4KCiMgKGUpIFVzZSByYW5kb20gZm9yZXN0cyB0byBhbmFseXplIHRoZSBkYXRhLiBSZXBvcnQgdGVzdCBNU0UgYW5kIGVmZmVjdCBvZiBtdHJ5LgoKYGBge3J9CiMgUmFuZG9tIGZvcmVzdCB3aXRoIGRlZmF1bHQgbXRyeSAoc3FydChwKSkKc2V0LnNlZWQoMSkKcmZfbW9kZWwgPC0gcmFuZG9tRm9yZXN0KFNhbGVzIH4gLiwgZGF0YSA9IHRyYWluX2RhdGEsIG10cnkgPSA0LCBpbXBvcnRhbmNlID0gVFJVRSkKcmZfcHJlZHMgPC0gcHJlZGljdChyZl9tb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkKcmZfbXNlIDwtIG1lYW4oKHJmX3ByZWRzIC0gdGVzdF9kYXRhJFNhbGVzKV4yKQpyZl9tc2UKCiMgSW1wb3J0YW5jZQppbXBvcnRhbmNlKHJmX21vZGVsKQp2YXJJbXBQbG90KHJmX21vZGVsKQpgYGAKClRoaXMgY29kZSBmaXRzIGEgcmFuZG9tIGZvcmVzdCBtb2RlbCB0byB0aGUgQ2Fyc2VhdHMgdHJhaW5pbmcgZGF0YSB1c2luZyB0aGUgZGVmYXVsdCB2YWx1ZSBvZiBtdHJ5ID0gNCAoc3F1YXJlIHJvb3Qgb2YgdGhlIG51bWJlciBvZiBwcmVkaWN0b3JzKS4gSXQgcHJlZGljdHMgU2FsZXMgb24gdGhlIHRlc3QgZGF0YSBhbmQgY29tcHV0ZXMgdGhlIHRlc3QgTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpLiBUaGUgbW9kZWwgYWxzbyBjYWxjdWxhdGVzIGFuZCBwbG90cyB2YXJpYWJsZSBpbXBvcnRhbmNlLCBoZWxwaW5nIGlkZW50aWZ5IHdoaWNoIHByZWRpY3RvcnMgY29udHJpYnV0ZSBtb3N0IHRvIHRoZSBhY2N1cmFjeS4KCgpFZmZlY3Qgb2YgbXRyeToKCkxvd2VyIG10cnkgaW5jcmVhc2VzIHJhbmRvbW5lc3MgYW5kIHJlZHVjZXMgY29ycmVsYXRpb24gYW1vbmcgdHJlZXMsIHdoaWNoIGNhbiBoZWxwIHJlZHVjZSBvdmVyZml0dGluZy4KClRyeSBtdHJ5ID0gMiwgNCwgNiwgMTAgYW5kIGNvbXBhcmUgdGVzdCBNU0VzIHRvIHNlZSB0aGlzIGVmZmVjdC4KCgojIChmKSBBbmFseXplIHRoZSBkYXRhIHVzaW5nIEJBUlQgKEJheWVzaWFuIEFkZGl0aXZlIFJlZ3Jlc3Npb24gVHJlZXMpCgpgYGB7cn0KIyBCQVJUIHBhY2thZ2UgbWF5IG5lZWQgdG8gYmUgaW5zdGFsbGVkIGZpcnN0CiMgaW5zdGFsbC5wYWNrYWdlcygiQkFSVCIpCmxpYnJhcnkoQkFSVCkKCiMgUHJlcGFyZSBkYXRhCnhfdHJhaW4gPC0gdHJhaW5fZGF0YVssIC13aGljaChuYW1lcyh0cmFpbl9kYXRhKSA9PSAiU2FsZXMiKV0KeV90cmFpbiA8LSB0cmFpbl9kYXRhJFNhbGVzCnhfdGVzdCA8LSB0ZXN0X2RhdGFbLCAtd2hpY2gobmFtZXModGVzdF9kYXRhKSA9PSAiU2FsZXMiKV0KCiMgRml0IEJBUlQgbW9kZWwKc2V0LnNlZWQoMSkKYmFydF9tb2RlbCA8LSB3YmFydCh4LnRyYWluID0geF90cmFpbiwgeS50cmFpbiA9IHlfdHJhaW4sIHgudGVzdCA9IHhfdGVzdCkKCiMgUHJlZGljdCBhbmQgY29tcHV0ZSB0ZXN0IE1TRQpiYXJ0X3ByZWRzIDwtIGJhcnRfbW9kZWwkeWhhdC50ZXN0Lm1lYW4KYmFydF9tc2UgPC0gbWVhbigoYmFydF9wcmVkcyAtIHRlc3RfZGF0YSRTYWxlcyleMikKYmFydF9tc2UKYGBgCgpUaGlzIGNvZGUgZml0cyBhIEJheWVzaWFuIEFkZGl0aXZlIFJlZ3Jlc3Npb24gVHJlZXMgKEJBUlQpIG1vZGVsIHRvIHRoZSBDYXJzZWF0cyB0cmFpbmluZyBkYXRhLiBUaGUgbW9kZWwgaXMgdXNlZCB0byBwcmVkaWN0IFNhbGVzIG9uIHRoZSB0ZXN0IGRhdGEuIEl0IHRoZW4gY2FsY3VsYXRlcyB0aGUgdGVzdCBNZWFuIFNxdWFyZWQgRXJyb3IgKE1TRSkgdG8gZXZhbHVhdGUgcHJlZGljdGlvbiBhY2N1cmFjeS4gQkFSVCBpcyBhIGZsZXhpYmxlLCBub24tcGFyYW1ldHJpYyBlbnNlbWJsZSBtZXRob2QgdGhhdCBjYXB0dXJlcyBjb21wbGV4IHJlbGF0aW9uc2hpcHMgaW4gdGhlIGRhdGEuCgpPYnNlcnZhdGlvbjoKCkJBUlQgb2Z0ZW4geWllbGRzIHN0cm9uZyBwZXJmb3JtYW5jZSBkdWUgdG8gaXRzIGVuc2VtYmxlIG5hdHVyZSBhbmQgYnVpbHQtaW4gcmVndWxhcml6YXRpb24uIENvbXBhcmUgTVNFIHdpdGggcHJldmlvdXMgbW9kZWxzIHRvIGV2YWx1YXRlLgoKIyAxMS4gQm9vc3Rpbmcgb24gQ2FyYXZhbiBEYXRhIFNldAoKIyAoYSkgQ3JlYXRlIGEgdHJhaW5pbmcgc2V0IG9mIHRoZSBmaXJzdCAxLDAwMCBvYnNlcnZhdGlvbnMsIGFuZCB1c2UgdGhlIHJlc3QgYXMgdGhlIHRlc3Qgc2V0LgoKYGBge3J9CiMgTG9hZCByZXF1aXJlZCBsaWJyYXJpZXMKbGlicmFyeShJU0xSMikKbGlicmFyeShnYm0pCmxpYnJhcnkoZHBseXIpCgojIFByZXBhcmUgdGhlIGRhdGEKZGF0YShDYXJhdmFuKQpDYXJhdmFuIDwtIENhcmF2YW4gJT4lCiAgbXV0YXRlKFB1cmNoYXNlID0gaWZlbHNlKFB1cmNoYXNlID09ICJZZXMiLCAxLCAwKSkKCiMgU3BsaXQgaW50byB0cmFpbmluZyBhbmQgdGVzdCBzZXRzCnRyYWluX2RhdGEgPC0gQ2FyYXZhblsxOjEwMDAsIF0KdGVzdF9kYXRhIDwtIENhcmF2YW5bLSgxOjEwMDApLCBdCgojIEZpdCB0aGUgYm9vc3RpbmcgbW9kZWwKc2V0LnNlZWQoMSkKYm9vc3RfbW9kZWwgPC0gZ2JtKFB1cmNoYXNlIH4gLiwgCiAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5fZGF0YSwKICAgICAgICAgICAgICAgICAgIGRpc3RyaWJ1dGlvbiA9ICJiZXJub3VsbGkiLAogICAgICAgICAgICAgICAgICAgbi50cmVlcyA9IDEwMDAsCiAgICAgICAgICAgICAgICAgICBzaHJpbmthZ2UgPSAwLjAxLAogICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQoKIyAtLS0gVmFyaWFibGUgSW1wb3J0YW5jZSBQbG90IChmaXhlZCBsYWJlbHMpIC0tLQojIFNldCBsYXJnZXIgbGVmdCBtYXJnaW4gc28gbmFtZXMgZG9u4oCZdCBnZXQgY3V0IG9mZgpwYXIobWFyID0gYyg1LCAxMiwgNCwgMikpICAjIEJvdHRvbSwgTGVmdCwgVG9wLCBSaWdodAoKIyBQbG90IHZhcmlhYmxlIGltcG9ydGFuY2UgY2xlYXJseQpzdW1tYXJ5KGJvb3N0X21vZGVsLAogICAgICAgIG4udHJlZXMgPSAxMDAwLAogICAgICAgIG1ldGhvZCA9IHJlbGF0aXZlLmluZmx1ZW5jZSwKICAgICAgICBsYXMgPSAxLCAgICAgICAgICAgIyBIb3Jpem9udGFsIGxhYmVscwogICAgICAgIGNCYXJzID0gMzAsICAgICAgICAjIFRvcCAzMCB2YXJpYWJsZXMKICAgICAgICBtYWluID0gIlZhcmlhYmxlIEltcG9ydGFuY2UgZnJvbSBCb29zdGluZyBNb2RlbCIsCiAgICAgICAgY2V4Lm5hbWVzID0gMC45KSAgICMgRm9udCBzaXplIGZvciBuYW1lcwpgYGAKClRoaXMgY29kZSBmaXRzIGEgYm9vc3RpbmcgbW9kZWwgdG8gdGhlIENhcmF2YW4gZGF0YXNldCB1c2luZyAxLDAwMCB0cmVlcyBhbmQgYSBzaHJpbmthZ2UgcmF0ZSBvZiAwLjAxIHRvIHByZWRpY3QgdGhlIHByb2JhYmlsaXR5IG9mIGEgcHVyY2hhc2UuIFRoZSByZXNwb25zZSB2YXJpYWJsZSBpcyBjb252ZXJ0ZWQgdG8gYmluYXJ5ICgxIGZvciAiWWVzIiwgMCBmb3IgIk5vIikuIEFmdGVyIHRyYWluaW5nLCBpdCBnZW5lcmF0ZXMgYSB2YXJpYWJsZSBpbXBvcnRhbmNlIHBsb3Qgc2hvd2luZyB0aGUgdG9wIDMwIHByZWRpY3RvcnMsIHdpdGggYWRqdXN0ZWQgbWFyZ2lucyBhbmQgbGFiZWwgc2V0dGluZ3MgdG8gZW5zdXJlIHRoZSBncmFwaCBpcyBjbGVhciBhbmQgcmVhZGFibGUuCgojIChiKSBGaXQgYSBib29zdGluZyBtb2RlbCB1c2luZyAxLDAwMCB0cmVlcyBhbmQgc2hyaW5rYWdlID0gMC4wMS4gSWRlbnRpZnkgaW1wb3J0YW50IHByZWRpY3RvcnMuCgpgYGB7cn0KIyBMb2FkIHJlcXVpcmVkIGxpYnJhcmllcwpsaWJyYXJ5KElTTFIyKQpsaWJyYXJ5KGdibSkKbGlicmFyeShkcGx5cikKCiMgUHJlcGFyZSB0aGUgZGF0YQpkYXRhKENhcmF2YW4pCkNhcmF2YW4gPC0gQ2FyYXZhbiAlPiUKICBtdXRhdGUoUHVyY2hhc2UgPSBpZmVsc2UoUHVyY2hhc2UgPT0gIlllcyIsIDEsIDApKQoKIyBTcGxpdCBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0IHNldHMKdHJhaW5fZGF0YSA8LSBDYXJhdmFuWzE6MTAwMCwgXQp0ZXN0X2RhdGEgPC0gQ2FyYXZhblstKDE6MTAwMCksIF0KCiMgRml0IHRoZSBib29zdGluZyBtb2RlbApzZXQuc2VlZCgxKQpib29zdF9tb2RlbCA8LSBnYm0oUHVyY2hhc2UgfiAuLCAKICAgICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbl9kYXRhLAogICAgICAgICAgICAgICAgICAgZGlzdHJpYnV0aW9uID0gImJlcm5vdWxsaSIsCiAgICAgICAgICAgICAgICAgICBuLnRyZWVzID0gMTAwMCwKICAgICAgICAgICAgICAgICAgIHNocmlua2FnZSA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpCgojIC0tLS0tLS0tIEJJRyBBTkQgQ0xFQVIgVkFSSUFCTEUgSU1QT1JUQU5DRSBQTE9UIC0tLS0tLS0tCgojIE9wZW4gaGlnaC1yZXMgcGxvdCB3aW5kb3cgKG9wdGlvbmFsOiBjb21tZW50IG91dCBpZiBub3QgbmVlZGVkKQojIHdpbmRvd3Mod2lkdGggPSAxMiwgaGVpZ2h0ID0gOCkgICMgRm9yIFdpbmRvd3MKIyBxdWFydHood2lkdGggPSAxMiwgaGVpZ2h0ID0gOCkgICMgRm9yIG1hY09TCiMgWDExKHdpZHRoID0gMTIsIGhlaWdodCA9IDgpICAgICAjIEZvciBMaW51eAoKIyBTZXQgbGFyZ2VyIG1hcmdpbnMgZm9yIGxvbmcgdmFyaWFibGUgbmFtZXMKcGFyKG1hciA9IGMoNSwgMTMsIDQsIDIpKSAgIyBCb3R0b20sIExlZnQsIFRvcCwgUmlnaHQKCiMgUGxvdCB0b3AgMzAgdmFyaWFibGVzIGNsZWFybHkKc3VtbWFyeShib29zdF9tb2RlbCwKICAgICAgICBuLnRyZWVzID0gMTAwMCwKICAgICAgICBtZXRob2QgPSByZWxhdGl2ZS5pbmZsdWVuY2UsCiAgICAgICAgbGFzID0gMSwgICAgICAgICAjIEhvcml6b250YWwgbGFiZWxzCiAgICAgICAgY0JhcnMgPSAzMCwgICAgICAjIFRvcCAzMCB2YXJpYWJsZXMKICAgICAgICBtYWluID0gIlRvcCAzMCBWYXJpYWJsZSBJbXBvcnRhbmNlcyAtIEJvb3N0aW5nIE1vZGVsIiwKICAgICAgICBjZXgubmFtZXMgPSAxLjApICMgRm9udCBzaXplIG9mIHZhcmlhYmxlIG5hbWVzCmBgYAoKVGhpcyBjb2RlIGZpdHMgYSBib29zdGluZyBtb2RlbCB0byB0aGUgQ2FyYXZhbiBkYXRhc2V0IHRvIHByZWRpY3Qgd2hldGhlciBhIHBlcnNvbiB3aWxsIG1ha2UgYSBwdXJjaGFzZS4gSXQgdXNlcyAxLDAwMCB0cmVlcyB3aXRoIGEgbGVhcm5pbmcgcmF0ZSAoc2hyaW5rYWdlKSBvZiAwLjAxLiBBZnRlciB0cmFpbmluZywgaXQgZ2VuZXJhdGVzIGEgaGlnaC1yZXNvbHV0aW9uIHZhcmlhYmxlIGltcG9ydGFuY2UgcGxvdCB0aGF0IGRpc3BsYXlzIHRoZSB0b3AgMzAgbW9zdCBpbmZsdWVudGlhbCBwcmVkaWN0b3JzLiBUaGUgcGxvdCBsYXlvdXQgaXMgYWRqdXN0ZWQgd2l0aCBsYXJnZXIgbWFyZ2lucyBhbmQgZm9udCBzaXplIHRvIGVuc3VyZSB0aGF0IGxvbmcgdmFyaWFibGUgbmFtZXMgYXJlIGZ1bGx5IHZpc2libGUgYW5kIGVhc3kgdG8gcmVhZC4KCk9ic2VydmF0aW9uOgpUaGUgb3V0cHV0IHdpbGwgc2hvdyB0aGUgbW9zdCBpbXBvcnRhbnQgcHJlZGljdG9ycyAoZS5nLiwgUFBFUlNBVVQsIE1LT09QS0xBLCBldGMuKSBiYXNlZCBvbiB0aGVpciByZWxhdGl2ZSBpbmZsdWVuY2UgaW4gdGhlIG1vZGVsLgoKIyAoYykgVXNlIHRoZSBtb2RlbCB0byBwcmVkaWN0IG9uIHRoZSB0ZXN0IHNldC4gQ3JlYXRlIGEgY29uZnVzaW9uIG1hdHJpeCB1c2luZyAyMCUgcHJvYmFiaWxpdHkgdGhyZXNob2xkLgoKYGBge3J9CiMgUHJlZGljdCBwcm9iYWJpbGl0aWVzCmJvb3N0X3Byb2JzIDwtIHByZWRpY3QoYm9vc3RfbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2RhdGEsIG4udHJlZXMgPSAxMDAwLCB0eXBlID0gInJlc3BvbnNlIikKCiMgQ2xhc3NpZnkgYXMgJzEnIGlmIHByb2JhYmlsaXR5ID4gMC4yMApib29zdF9wcmVkcyA8LSBpZmVsc2UoYm9vc3RfcHJvYnMgPiAwLjIsIDEsIDApCgojIEFjdHVhbCByZXNwb25zZXMKYWN0dWFsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHRlc3RfZGF0YSRQdXJjaGFzZSkpCgojIENvbmZ1c2lvbiBtYXRyaXgKdGFibGUoUHJlZGljdGVkID0gYm9vc3RfcHJlZHMsIEFjdHVhbCA9IGFjdHVhbCkKCiMgRnJhY3Rpb24gb2YgY29ycmVjdCBwb3NpdGl2ZSBwcmVkaWN0aW9ucwpjb3JyZWN0X3Bvc2l0aXZlIDwtIHN1bShib29zdF9wcmVkcyA9PSAxICYgYWN0dWFsID09IDEpCnRvdGFsX3ByZWRpY3RlZF9wb3NpdGl2ZSA8LSBzdW0oYm9vc3RfcHJlZHMgPT0gMSkKZnJhY3Rpb25fY29ycmVjdCA8LSBjb3JyZWN0X3Bvc2l0aXZlIC8gdG90YWxfcHJlZGljdGVkX3Bvc2l0aXZlCmZyYWN0aW9uX2NvcnJlY3QKYGBgCgoKVGhpcyBjb2RlIHVzZXMgdGhlIHRyYWluZWQgYm9vc3RpbmcgbW9kZWwgdG8gcHJlZGljdCBwdXJjaGFzZSBwcm9iYWJpbGl0aWVzIG9uIHRoZSB0ZXN0IHNldC4gSWYgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0eSBpcyBncmVhdGVyIHRoYW4gMjAlLCBpdCBjbGFzc2lmaWVzIHRoZSBvYnNlcnZhdGlvbiBhcyBhIHB1cmNoYXNlICgxKS4gSXQgdGhlbiBjcmVhdGVzIGEgY29uZnVzaW9uIG1hdHJpeCB0byBjb21wYXJlIHByZWRpY3Rpb25zIHdpdGggYWN0dWFsIG91dGNvbWVzIGFuZCBjYWxjdWxhdGVzIHRoZSBwcmVjaXNpb27igJR0aGUgZnJhY3Rpb24gb2YgcHJlZGljdGVkIGJ1eWVycyB3aG8gYWN0dWFsbHkgbWFkZSBhIHB1cmNoYXNlLiBUaGlzIGhlbHBzIGV2YWx1YXRlIGhvdyB3ZWxsIHRoZSBtb2RlbCBpZGVudGlmaWVzIGFjdHVhbCBwdXJjaGFzZXJzLgoKCkludGVycHJldGF0aW9uOgoKVGhlIGNvbmZ1c2lvbiBtYXRyaXggc2hvd3MgaG93IG1hbnkgcHVyY2hhc2VzIHdlcmUgY29ycmVjdGx5L2luY29ycmVjdGx5IGNsYXNzaWZpZWQuCgpUaGUgZnJhY3Rpb24gb2YgcGVvcGxlIHByZWRpY3RlZCB0byBtYWtlIGEgcHVyY2hhc2Ugd2hvIGFjdHVhbGx5IGRpZCBpcyBzaG93biBieSBmcmFjdGlvbl9jb3JyZWN0LgoKQ29tcGFyaXNvbiB3aXRoIExvZ2lzdGljIFJlZ3Jlc3Npb24gb3IgS05OIChmcm9tIHRleHRib29rKToKCkxvZ2lzdGljIHJlZ3Jlc3Npb24gYW5kIEtOTiB0ZW5kIHRvIGhhdmUgbG93ZXIgcHJlY2lzaW9uIGF0IHRoaXMgMjAlIHRocmVzaG9sZC4KCkJvb3N0aW5nIHVzdWFsbHkgZ2l2ZXMgaGlnaGVyIHByZWNpc2lvbiBkdWUgdG8gaXRzIGFiaWxpdHkgdG8gY2FwdHVyZSBjb21wbGV4IGludGVyYWN0aW9ucy4KCg==