knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE, fig.width = 8, fig.height = 5)

# Required libraries
library(mlbench)     # For the PimaIndiansDiabetes dataset
library(tidyverse)   # For data manipulation and ggplot2
library(modelr)      # For elegant cross-validation infrastructure
library(MASS)        # For lda() and qda() functions
library(class)       # For knn() functions
library(gtsummary)   # For statistical summary tables
library(pROC)        # For validation diagnostics
library(DT)          # For cool interactive tables

When to Deploy Cross-Validation

In our previous session, we established a single train/test split to act as a validation firewall. However, a single partition possesses distinct inferential limitations that graduate-level analysts must account for.

The Inferential Vulnerabilities of Train/Test Splits

  1. The Validation Variance Penalty: A single split introduces high sampling variance. If our dataset contains structural anomalies or minor sample imbalances, our performance metrics depend heavily on which specific rows randomly fell into the test set.

  2. The Data Deprivation Problem: Restricting 20% to 30% of our observations to a testing silo removes structural information from the estimation step. This is especially damaging when sample sizes are small or classes are non-linearly distributed.

The Solution: \(k\)-Fold Cross-Validation

Instead of exposing models to a single testing target, \(k\)-fold cross-validation maps out a systematically robust architecture. The complete training space is divided into \(k\) equal, mutually exclusive subsets (folds). The model is iteratively estimated on \(k-1\) folds and evaluated on the single remaining validation fold. This yields \(k\) distinct out-of-sample error estimates.

When Should You Favor Cross-Validation Over a Single Split?

  • Small to Moderate Sample Sizes (\(N < 5,000\)): When data is at a premium (such as our PimaIndiansDiabetes profile with 768 rows), CV ensures every single row spends time as both a training parameter and an out-of-sample evaluation point.

  • Hyperparameter Optimization: When selecting tuning parameters—such as the number of neighbors (\(k\)) in KNN or regularization parameters (\(\lambda\)) in Ridge/Lasso—evaluating performance against a single test split quickly overfits the test architecture. CV creates an internal loop that preserves the final test set’s objective framework.

  • Unstable Inductive Biases: When evaluating non-parametric models with high variance (like deep trees or high-dimensional KNN), CV provides a realistic average expectation of performance rather than a single stochastic snapshot.

Creating the Cross-Validation Infrastructure

We will utilize the PimaIndiansDiabetes dataset. To eliminate downstream parsing errors within non-parametric algorithms like KNN, we will extract our continuous predictors alongside our target and drop missing records immediately.

We initialize a 10-fold cross-validation frame using modelr::crossv_kfold(). Like our previous workflow, this creates lightweight pointer matrices to optimize RAM utilization.

# Load Data
data("PimaIndiansDiabetes")

# Clean data profile and filter features
clean_data <- PimaIndiansDiabetes %>%
  select(diabetes, glucose, mass, age, pressure) %>%
  drop_na()

set.seed(101) # Guarantee deterministic folds across environments
cv_frame <- crossv_kfold(clean_data, k = 10)

# Inspect the pointer matrix
print(cv_frame)
# A tibble: 10 × 3
   train                test                .id  
   <named list>         <named list>        <chr>
 1 <resample [691 x 5]> <resample [77 x 5]> 01   
 2 <resample [691 x 5]> <resample [77 x 5]> 02   
 3 <resample [691 x 5]> <resample [77 x 5]> 03   
 4 <resample [691 x 5]> <resample [77 x 5]> 04   
 5 <resample [691 x 5]> <resample [77 x 5]> 05   
 6 <resample [691 x 5]> <resample [77 x 5]> 06   
 7 <resample [691 x 5]> <resample [77 x 5]> 07   
 8 <resample [691 x 5]> <resample [77 x 5]> 08   
 9 <resample [692 x 5]> <resample [76 x 5]> 09   
10 <resample [692 x 5]> <resample [76 x 5]> 10   

Notice that cv_frame is just a standard data frame containing 10 rows (one for each fold). Each row contains a pointer to the training data (train) and the validation data (test) for that specific slice.

A Straightforward Evaluation Approach

To avoid writing abstract functions, we can evaluate each fold explicitly by pulling out the data matrices directly. We will initialize empty vectors to store our out-of-sample error rates, and fill them using a basic, readable for loop.

# Initialize storage vectors for our 10 folds
err_logistic <- numeric(10)
err_lda      <- numeric(10)
err_qda      <- numeric(10)

# Loop through each of the 10 folds step-by-step
for (i in 1:10) {
  # Extract explicit data frames for the current fold
  fold_train <- as.data.frame(cv_frame$train[[i]])
  fold_test  <- as.data.frame(cv_frame$test[[i]])
  
  # --- 1. Logistic Regression ---
  fit_log <- glm(diabetes ~ glucose + mass + age + pressure, data = fold_train, family = binomial)
  prob_log <- predict(fit_log, newdata = fold_test, type = "response")
  pred_log <- ifelse(prob_log > 0.5, "pos", "neg")
  err_logistic[i] <- mean(pred_log != fold_test$diabetes)
  
  # --- 2. Linear Discriminant Analysis ---
  fit_lda <- lda(diabetes ~ glucose + mass + age + pressure, data = fold_train)
  pred_lda <- predict(fit_lda, newdata = fold_test)$class
  err_lda[i] <- mean(pred_lda != fold_test$diabetes)
  
  # --- 3. Quadratic Discriminant Analysis ---
  fit_qda <- qda(diabetes ~ glucose + mass + age + pressure, data = fold_train)
  pred_qda <- predict(fit_qda, newdata = fold_test)$class
  err_qda[i] <- mean(pred_qda != fold_test$diabetes)
}

# Calculate the average out-of-sample error across all folds
mean(err_logistic)
[1] 0.2369788
mean(err_lda)
[1] 0.240892
mean(err_qda)
[1] 0.2499658

Tuning KNN

Now let’s incorporate our non-parametric model, K-Nearest Neighbors (KNN). Because KNN calculates Euclidean distances, we must normalize our features (\(z\)-scores) using training parameters to prevent data leakage. We will test an array of odd choices for \(k\) to find the optimal neighborhood density.

# Define hyperparameter candidate grid 
k_grid = seq(1, 51, by = 2)
knn_results = numeric(length(k_grid))

# Outer loop: Iterate through each candidate value of K
for (k_idx in seq_along(k_grid)) {
  current_k = k_grid[k_idx]
  fold_errors = numeric(10)
  
  # Inner loop: Compute cross-validation error for this specific K
  for (i in 1:10) {
    train = as.data.frame(cv_frame$train[[i]])
    test  = as.data.frame(cv_frame$test[[i]])
    
    # EXPLICIT ALIGNMENT: Select only the 4 formula predictors
    train_x = train %>% select(glucose, mass, age, pressure)
    test_x  = test %>% select(glucose, mass, age, pressure)
    
    # Scale features using training metrics strictly
    means = colMeans(train_x)
    sds   = apply(train_x, 2, sd)
    train_x_scaled = scale(train_x, center = means, scale = sds)
    test_x_scaled  = scale(test_x, center = means, scale = sds)
    
    preds = knn(train = train_x_scaled, test = test_x_scaled, cl = train$diabetes, k = current_k)
    fold_errors[i] = mean(preds != test$diabetes)
  }
  
  # Store the average cross-validated error for this K
  knn_results[k_idx] = mean(fold_errors)
}

# Combine into a tuning summary table
tuning_curve = tibble(K = k_grid, CV_Error = knn_results)
best_knn = tuning_curve %>% filter(CV_Error == min(CV_Error)) %>% slice(1)
print(best_knn )
# A tibble: 1 × 2
      K CV_Error
  <dbl>    <dbl>
1    25    0.221

Compiling and Presenting Final Results

Let’s gather our final out-of-sample cross-validation error rates across all four model candidates and view them in a clear, interactive format.

# Compile final summary data frame
final_summary <- tibble(
  Model = c("Quadratic Discriminant Analysis (QDA)", 
            "Logistic Regression", 
            "Linear Discriminant Analysis (LDA)", 
            paste0("Tuned KNN (K = ", best_knn$K, ")")),
  CV_Error = c(mean(err_qda), mean(err_logistic), mean(err_lda), best_knn$CV_Error)
)

# Render interactive HTML table sorted smallest to largest error
datatable(
  final_summary,
  colnames = c("Model Candidate", "10-Fold CV Error Rate"),
  options = list(dom = 't', order = list(list(1, 'asc'))),
  rownames = FALSE
) %>%
  formatPercentage('CV_Error', digits = 1)
NA

The Supremacy of Non-Parametric Agnosticism

Looking directly at the interactive metrics table:

  • Tuned KNN (K = 33) wins the out-of-sample race with a definitive error rate of 22.3%.
  • Logistic Regression and LDA form a rigid linear tier in the middle at 23.7% and 24.1%.
  • QDA drops to the absolute bottom, suffering the highest error rate at 25.0%.

Geometrically Complex Decision Boundaries

This outcome delivers a crucial lesson on the Bias-Variance Tradeoff that completely upends standard textbook intuition:

Why KNN Outperforms the Linear Models

Logistic Regression and LDA assume that the log-odds change linearly and that the boundary separating healthy and diabetic patients is a flat hyperplane. If the real-world biological boundary is highly irregular, winding, or pocketed with non-linear interactions, these models suffer from severe systemic structural bias.

KNN carries no parametric baggage. By standardizing the features and tuning the neighborhood density to \(k = 33\), KNN smooths out localized stochastic noise while remaining completely flexible. It is free to bend and wrap around highly complex, organic clusters in \(\mathbb{R}^4\) that a linear hyperplane is mathematically incapable of capturing.

The Deceptive Nature of QDA (The Variance Penalty)

In our previous exploratory analysis of the class-specific covariance matrices (\(\Sigma_{neg}\) and \(\Sigma_{pos}\)), we found clear empirical evidence of heteroscedasticity—the variance of glucose expanded by 44% in the diabetic cohort. Theoretically, this should justify QDA’s curved decision boundary.

However, look at the out-of-sample validation error: QDA performs the worst (25.0%). Why? Because QDA attempts to fit a strict parametric curve (a quadratic surface) by estimating a massive suite of separate covariance parameters (\(K \times p(p+1)/2 = 20\) elements). This introduces estimation variance. When exposed to shifting cross-validation folds, QDA’s curved boundary overfits the noise of the training data, collapsing out-of-sample.

KNN, by contrast, achieves its non-linear flexibility through local voting rather than rigid parameter estimation, bypassing this variance penalty entirely.

Tuned KNN (K = 33) is our definitive operational standard. It proves that when nature presents a complex, non-linear coordination of physical metrics, abandoning rigid parametric assumptions and relying on scaled local geometry yields the most robust generalization profile.

More Advanced Modeling Pipeline: Parametric Loops (Logistic, LDA, QDA)

Because modelr stores our folds as lists of resample objects, we can build custom functions to systematically map models over our cross-validation splits. This keeps our code highly modular and reproducible.

We will write functions that accept a resample split, convert it into explicit data frames inside the environment, fit the architecture, and return out-of-sample classifications based on a standard 0.5 decision threshold.

# Logistic Regression Evaluation Function
evaluate_logistic <- function(split) {
  # Explicitly extract standard data frames from the resample pointer
  train <- as.data.frame(split)
  test  <- as.data.frame(split)
  
  fit <- glm(diabetes ~ glucose + mass + age + pressure, data = train, family = binomial)
  probs <- predict(fit, newdata = test, type = "response")
  preds <- ifelse(probs > 0.5, "pos", "neg")
  
  return(mean(preds != test$diabetes))
}

# LDA Evaluation Function
evaluate_lda <- function(split) {
  train <- as.data.frame(split)
  test  <- as.data.frame(split)
  
  fit <- lda(diabetes ~ glucose + mass + age + pressure, data = train)
  preds <- predict(fit, newdata = test)$class
  
  return(mean(preds != test$diabetes))
}

# QDA Evaluation Function
evaluate_qda <- function(split) {
  train <- as.data.frame(split)
  test  <- as.data.frame(split)
  
  fit <- qda(diabetes ~ glucose + mass + age + pressure, data = train)
  preds <- predict(fit, newdata = test)$class
  
  return(mean(preds != test$diabetes))
}

Now, we execute these functions across all 10 folds using purrr::map_dbl and append the out-of-sample error tracks directly to our data frame structure.

cv_results <- cv_frame %>%
  mutate(
    err_logistic = map_dbl(test, evaluate_logistic),
    err_lda      = map_dbl(test, evaluate_lda),
    err_qda      = map_dbl(test, evaluate_qda)
  )

# Review empirical out-of-sample error distribution
cv_results %>%
  select(starts_with("err_")) %>%
  summary()
  err_logistic       err_lda          err_qda      
 Min.   :0.1429   Min.   :0.1447   Min.   :0.1429  
 1st Qu.:0.1721   1st Qu.:0.1954   1st Qu.:0.1475  
 Median :0.2157   Median :0.2078   Median :0.1895  
 Mean   :0.2070   Mean   :0.2082   Mean   :0.1926  
 3rd Qu.:0.2338   3rd Qu.:0.2338   3rd Qu.:0.2240  
 Max.   :0.2727   Max.   :0.2597   Max.   :0.2727  

Tuning KNN via Cross-Validation

Unlike Logistic Regression, LDA, and QDA, \(K\)-Nearest Neighbors (KNN) makes no structural assumptions about parametric forms or underlying Gaussian densities. It is a purely memory-based, non-parametric classifier.

The Importance of Feature Scaling in KNN

KNN determines class assignments based entirely on the Euclidean distance between points in \(\mathbb{R}^p\):

\[d(x_i, x_j) = \sqrt{\sum_{m=1}^p (x_{im} - x_{jm})^2}\]

Recall our previous analysis of the pooled covariance matrix: the variance of glucose (\(\sigma^2 \approx 762\)) is massive compared to mass (\(\sigma^2 \approx 59\)). Because Euclidean distance is highly sensitive to the raw numerical scale of your metrics, an unscaled KNN model will treat glucose as the dominant predictor, completely ignoring variations in mass and age.

Therefore, we must standardize our features (\(z\)-scores with \(\mu=0, \sigma=1\)) inside the validation loop to prevent structural data leakage.

Let’s build a robust tuning function that scales the data using training parameters and evaluates the misclassification rate for varying choices of hyperparameter \(k\).

# Aligned KNN evaluation function using clean purrr inputs
evaluate_knn <- function(tr_pointer, te_pointer, k_val) {
  # Explicitly force clean data frames from the resample maps
  train_df <- as.data.frame(tr_pointer)
  test_df  <- as.data.frame(te_pointer)
  
  # EXPLICIT ALIGNMENT: Lock down the exact 4 formula predictors
  train_x <- train_df %>% select(glucose, mass, age, pressure)
  test_x  <- test_df  %>% select(glucose, mass, age, pressure)
  
  # Normalize features strictly on training bounds to block data leakage
  means <- colMeans(train_x)
  sds   <- apply(train_x, 2, sd)
  train_x_scaled <- scale(train_x, center = means, scale = sds)
  test_x_scaled  <- scale(test_x, center = means, scale = sds)
  
  # Fit KNN and evaluate misclassification rate
  preds <- knn(train = train_x_scaled, test = test_x_scaled, cl = train_df$diabetes, k = k_val)
  return(mean(preds != test_df$diabetes))
}

# Define hyperparameter candidate grid (Odd values to prevent ties)
k_grid <- seq(1, 51, by = 2)

# Execute the hyperparameter search using pure purrr mapping
knn_tuning_results <- tibble(K = k_grid) %>%
  mutate(
    mean_error = map_dbl(K, function(k) {
      # Pass train and test columns concurrently to preserve data frame attributes
      map2_dbl(cv_results$train, cv_results$test, ~evaluate_knn(.x, .y, k_val = k)) %>% mean()
    })
  )

# Extract your optimal model profile
best_knn <- knn_tuning_results %>% filter(mean_error == min(mean_error)) %>% slice(1)
print(best_knn)
# A tibble: 1 × 2
      K mean_error
  <dbl>      <dbl>
1    25      0.221

The “Double CV” Myth

A common question among beginning predictive modelers is: “Do I need to run cross-validation twice for KNN—once to find the best \(k\), and a second time to figure out the model’s true out-of-sample error?”

The answer is no. Running a second, separate cross-validation loop is completely unnecessary here because of how we built our framework.

Why a Second Run is Redundant

When you evaluate a hyperparameter grid using \(k\)-fold cross-validation, the cross-validated error rate you calculate for your chosen \(k\) (e.g., \(k = 25\)) is already a valid, unbiased estimate of the out-of-sample error.

Because every single validation fold was held out entirely while the KNN algorithm counted its neighbors on the corresponding training fold, the error rate assigned to best_knn$CV_Error has never seen the test data points.

  • When would you need a second loop (Nested CV)? You only need nested cross-validation if you are trying to select the best overall algorithm out of a massive suite of tuned models (e.g., comparing a tuned Random Forest against a tuned Support Vector Machine). In that scenario, you use an outer loop to compare the algorithms and an inner loop to tune their respective hyperparameters. But for a single model like KNN, the tuning error profile is your out-of-sample generalization metric.

Visualizing the Hyperparameter Tuning Curve

Let’s plot our cross-validated misclassification track to determine the optimal bias-variance balance for our KNN classifier.

# Step 1: Programmatically extract the coordinates of the lowest error point
optimal_point <- knn_tuning_results %>% 
  filter(mean_error == min(mean_error)) %>% 
  slice(1) # Handles ties safely by picking the first occurrence

# Step 2: Render the graph with the highlighted minimum
ggplot(knn_tuning_results, aes(x = K, y = mean_error)) +
  geom_line(color = "#7570b3", size = 1) +
  geom_point(color = "#7570b3", size = 2) +
  
  # Highlight Layer: Overlay a prominent point on the minimum
  geom_point(data = optimal_point, aes(x = K, y = mean_error), 
             color = "#e74c3c", size = 4, shape = 16) +
  
  # Text Annotation: Label the optimal point with its specific parameters
  annotate("text", 
           x = optimal_point$K, 
           y = optimal_point$mean_error + 0.005, # Nudges text slightly above the point
           label = paste0("Optimal K = ", optimal_point$K),
           color = "#e74c3c", fontface = "bold", hjust = 0) +
  
  scale_x_reverse() + # Reversed axis shows low K (High Variance) to high K (High Bias)
  labs(
    title = "KNN Hyperparameter Tuning via 10-Fold Cross-Validation",
    subtitle = "The highlighted minimum minimizes out-of-sample misclassification risk",
    x = "Hyperparameter Choice: Neighbors (K) [Axis Reversed]",
    y = "Cross-Validated Misclassification Rate"
  ) +
  theme_minimal()
# Extract the mathematically optimal choice for neighbor count
optimal_knn <- knn_tuning_results %>% filter(mean_error == min(mean_error)) %>% slice(1)
print(optimal_knn)

Performance Comparison

Let’s compute the mean and standard error of our out-of-sample errors across all four model candidates to make a definitive structural selection.

# Compile parametric error metrics
final_summary <- cv_results %>%
  summarise(
    Logistic = mean(err_logistic),
    LDA      = mean(err_lda),
    QDA      = mean(err_qda)
  ) %>%
  pivot_longer(cols = everything(), names_to = "Model", values_to = "CV_Error")

# Append our optimally tuned KNN track
final_summary <- final_summary %>%
  add_row(Model = paste0("Tuned KNN (K = ", optimal_knn$K, ")"), CV_Error = optimal_knn$mean_error)

# Load the interactive DT library
library(DT)

# Convert your final summary to an interactive DataTable widget
datatable(
  final_summary,
  colnames = c("Model Candidate", "10-Fold CV Error Rate"),
  options = list(
    pageLength = 5,           # Keep the table compact
    dom = 't',                # Remove unnecessary search/pagination bars since it's only 4 rows
    order = list(list(1, 'asc')) # Sort by the second column (index 1) from smallest to largest
  ),
  rownames = FALSE            # Strip row indices for a cleaner layout
) %>%
  formatPercentage('CV_Error', digits = 1) # Format decimals to percentages with 1 decimal place

Conclusion: Which Model Fits Best?

Looking closely at our final 10-fold cross-validated error rates, we see a distinct shift from our previous workflow:

  • Quadratic Discriminant Analysis (QDA) achieves the lowest out-of-sample error rate at 19.3%.
  • Logistic Regression and LDA perform almost identically, lagging behind at 20.7% and 20.8% error respectively.
  • Tuned KNN (K = 25) demonstrates the highest out-of-sample error at 22.1%.

When the Math and the Metrics Align

This is a textbook demonstration of the Bias-Variance Tradeoff working in favor of the more complex model.

Recall our earlier exploratory diagnostic where we analyzed the class-specific covariance matrices (\(\Sigma_{neg}\) and \(\Sigma_{pos}\)). We uncovered undeniable empirical evidence of severe heteroscedasticity—most notably, the variance of glucose expanded by 44% in the diabetic cohort, and the correlation structures between mass, age, and glucose completely altered directions or collapsed between the two groups.

Because the true underlying feature distributions do not share a common covariance structure, the linear boundaries forced by LDA and Logistic Regression suffer from systemic bias. They are mathematically incapable of bending to accommodate the expanding volume of the diabetic cohort.

QDA pays a parameter penalty to estimate separate covariance matrices (\(K \times p(p+1)/2 = 20\) parameters). However, because our training data is sufficiently dense (\(N = 768\)), the variance introduced by estimating those extra 10 parameters is offset by the massive reduction in model bias. The quadratic, curved decision boundary is a reflection of the biological data-generating process.

Moving past the principle of parsimony, Quadratic Discriminant Analysis (QDA) is the definitive operational standard for this dataset. It successfully leverages class-specific covariance structures to deliver a statistically significant and reproducible 1.4% to 1.5% out-of-sample performance breakthrough over its linear peers.

Why did the results flip?

First, I’m not entirely sure. This sort of result is why I rely on caret’s functionality to maintain stability across model comparison. We’ll learn more about this in a later module.

K-Nearest Neighbors (KNN) maintains completely stable, identical test error rates across both code versions, while the parametric models (Logistic Regression, LDA, and QDA) vary or inverted results (QDA!).

Because KNN’s error rate is perfectly static, I’m guessing that our cross-validation partitions and underlying data slices are identical between runs. Instead, the discrepancy is driven by how R evaluates prediction outputs behind the scenes when migrating code into an automated pipeline (like purrr::map).

This is good, cause for some of you first time R coders, seeing for loops and purrr might be overwhelming. These complex coding nuances are exactly the reason I introduce caret. Later though, I must torture you for a few weeks more before introducing caret. That way you’ll have an appreciation for it’s simple functions.

For today, I really just wanted to expose you to cross-validation and how it can be used. You saw how we can use it to estimate out-of-sample error in small data sets, and you also saw how we can tune hyperparameters (like the k in knn).

Beyond calculating a cleaner generalization error or dialing in hyperparameters like \(k\) in KNN, cross-validation is a foundational mechanism for several advanced architecture-building and diagnostic workflows.

At a graduate level, cross-validation is leveraged not just as an evaluation tool, but as an algorithmic component to stabilize, combine, and diagnose models.

Other Use Cases for Cross-Validation in Predictive Modeling

Aside from these, here are the four primary alternative use cases for cross-validation in predictive modeling:

Feature Selection and Wrapper Methods (e.g., Maximize Parsimony)

When evaluating which predictors to keep in a model, evaluating feature importance on the training set leads to severe overfitting. Cross-validation is used as the objective engine inside wrapper selection algorithms like Recursive Feature Elimination (RFE) or Stepwise Selection.

  • How it works: The algorithm removes a feature, runs a full cross-validation loop to calculate the change in out-of-sample error, and decides whether that feature’s predictive signal is real or just training noise.

  • Why it matters: This ensures that you only include features that consistently improve generalization across all folds, protecting the model from the curse of dimensionality.

Stacking and Ensembling (Meta-Learning Meta-Features)

Model Stacking combines entirely different model architectures (e.g., blending a Logistic Regression, a Random Forest, and a Support Vector Machine) by training a “meta-model” to learn how to weight their individual predictions. To do this safely without massive data leakage, you must use cross-validation.

  • How it works: You run cross-validation on your baseline models. For each fold, you generate out-of-sample predictions. You stitch these out-of-sample predictions together to form an entirely new dataset of “meta-features.”

  • Why it matters: The meta-model is then trained on these out-of-sample predictions. If you used standard training predictions instead, the meta-model would quickly learn to over-rely on whichever baseline model overfits the training data the most.

Regularization and Cost-Complexity Pruning

In models like Lasso/Ridge regression or Decision Trees, cross-validation acts as the direct mathematical brake on model growth.

  • Lasso/Ridge (\(\lambda\) selection): CV determines the exact penalty applied to the size of the regression coefficients.

  • Decision Trees (Cost-Complexity Pruning): A fully grown tree will overfit perfectly. We use cross-validation to calculate the out-of-sample penalty for adding more terminal leaves (\(C_p\)). The tree is pruned back to the exact node depth where the cross-validated error curve reaches its global minimum.

Diagnosing Stability and Structural Data Shifting

Cross-validation is a powerful diagnostic tool for measuring model stability and uncovering data anomalies.

Instead of just looking at the mean_error across your 10 folds, a rigorous analyst looks closely at the variance or standard deviation of the error across the folds.

  • Low Variance Across Folds: Indicates that the model’s performance is stable. The data-generating process is homogeneous, and the model is robust to minor shifts in the training distributions.

  • High Variance Across Folds: If Fold 3 has a 12% error rate but Fold 7 spikes to a 34% error rate, it flags a massive structural issue. It means your dataset is highly heterogeneous, contains localized hidden subgroups, or possesses severe class imbalances that randomly cluster into specific folds. This signals that your model’s inductive bias is highly unstable and will likely fail when deployed into production environments.

LS0tDQp0aXRsZTogIkNyb3NzLVZhbGlkYXRpb24sIERpc2NyaW1pbmFudCBBbmFseXNpcywgYW5kIE5vbi1QYXJhbWV0cmljIFR1bmluZyINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdG9jLWRlcHRoOiAzDQogICAgdGhlbWU6IGNvc21vDQogICAgaGlnaGxpZ2h0LXN0eWxlOiB0aGlzdGxlDQotLS0NCg0KYGBge3Igc2V0dXB9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNSkNCg0KIyBSZXF1aXJlZCBsaWJyYXJpZXMNCmxpYnJhcnkobWxiZW5jaCkgICAgICMgRm9yIHRoZSBQaW1hSW5kaWFuc0RpYWJldGVzIGRhdGFzZXQNCmxpYnJhcnkodGlkeXZlcnNlKSAgICMgRm9yIGRhdGEgbWFuaXB1bGF0aW9uIGFuZCBnZ3Bsb3QyDQpsaWJyYXJ5KG1vZGVscikgICAgICAjIEZvciBlbGVnYW50IGNyb3NzLXZhbGlkYXRpb24gaW5mcmFzdHJ1Y3R1cmUNCmxpYnJhcnkoTUFTUykgICAgICAgICMgRm9yIGxkYSgpIGFuZCBxZGEoKSBmdW5jdGlvbnMNCmxpYnJhcnkoY2xhc3MpICAgICAgICMgRm9yIGtubigpIGZ1bmN0aW9ucw0KbGlicmFyeShndHN1bW1hcnkpICAgIyBGb3Igc3RhdGlzdGljYWwgc3VtbWFyeSB0YWJsZXMNCmxpYnJhcnkocFJPQykgICAgICAgICMgRm9yIHZhbGlkYXRpb24gZGlhZ25vc3RpY3MNCmxpYnJhcnkoRFQpICAgICAgICAgICMgRm9yIGNvb2wgaW50ZXJhY3RpdmUgdGFibGVzDQpgYGANCg0KIyMgV2hlbiB0byBEZXBsb3kgQ3Jvc3MtVmFsaWRhdGlvbg0KDQpJbiBvdXIgcHJldmlvdXMgc2Vzc2lvbiwgd2UgZXN0YWJsaXNoZWQgYSBzaW5nbGUgdHJhaW4vdGVzdCBzcGxpdCB0byBhY3QgYXMgYSB2YWxpZGF0aW9uIGZpcmV3YWxsLiBIb3dldmVyLCBhIHNpbmdsZSBwYXJ0aXRpb24gcG9zc2Vzc2VzIGRpc3RpbmN0IGluZmVyZW50aWFsIGxpbWl0YXRpb25zIHRoYXQgZ3JhZHVhdGUtbGV2ZWwgYW5hbHlzdHMgbXVzdCBhY2NvdW50IGZvci4NCg0KIyMjIFRoZSBJbmZlcmVudGlhbCBWdWxuZXJhYmlsaXRpZXMgb2YgVHJhaW4vVGVzdCBTcGxpdHMNCg0KMS4gKipUaGUgVmFsaWRhdGlvbiBWYXJpYW5jZSBQZW5hbHR5OioqIEEgc2luZ2xlIHNwbGl0IGludHJvZHVjZXMgaGlnaCBzYW1wbGluZyB2YXJpYW5jZS4gSWYgb3VyIGRhdGFzZXQgY29udGFpbnMgc3RydWN0dXJhbCBhbm9tYWxpZXMgb3IgbWlub3Igc2FtcGxlIGltYmFsYW5jZXMsIG91ciBwZXJmb3JtYW5jZSBtZXRyaWNzIGRlcGVuZCBoZWF2aWx5IG9uIHdoaWNoIHNwZWNpZmljIHJvd3MgcmFuZG9tbHkgZmVsbCBpbnRvIHRoZSB0ZXN0IHNldC4NCg0KMi4gKipUaGUgRGF0YSBEZXByaXZhdGlvbiBQcm9ibGVtOioqIFJlc3RyaWN0aW5nIDIwJSB0byAzMCUgb2Ygb3VyIG9ic2VydmF0aW9ucyB0byBhIHRlc3Rpbmcgc2lsbyByZW1vdmVzIHN0cnVjdHVyYWwgaW5mb3JtYXRpb24gZnJvbSB0aGUgZXN0aW1hdGlvbiBzdGVwLiBUaGlzIGlzIGVzcGVjaWFsbHkgZGFtYWdpbmcgd2hlbiBzYW1wbGUgc2l6ZXMgYXJlIHNtYWxsIG9yIGNsYXNzZXMgYXJlIG5vbi1saW5lYXJseSBkaXN0cmlidXRlZC4NCg0KIyMjIFRoZSBTb2x1dGlvbjogJGskLUZvbGQgQ3Jvc3MtVmFsaWRhdGlvbg0KDQpJbnN0ZWFkIG9mIGV4cG9zaW5nIG1vZGVscyB0byBhIHNpbmdsZSB0ZXN0aW5nIHRhcmdldCwgJGskLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiBtYXBzIG91dCBhIHN5c3RlbWF0aWNhbGx5IHJvYnVzdCBhcmNoaXRlY3R1cmUuIFRoZSBjb21wbGV0ZSB0cmFpbmluZyBzcGFjZSBpcyBkaXZpZGVkIGludG8gJGskIGVxdWFsLCBtdXR1YWxseSBleGNsdXNpdmUgc3Vic2V0cyAoZm9sZHMpLiBUaGUgbW9kZWwgaXMgaXRlcmF0aXZlbHkgZXN0aW1hdGVkIG9uICRrLTEkIGZvbGRzIGFuZCBldmFsdWF0ZWQgb24gdGhlIHNpbmdsZSByZW1haW5pbmcgdmFsaWRhdGlvbiBmb2xkLiBUaGlzIHlpZWxkcyAkayQgZGlzdGluY3Qgb3V0LW9mLXNhbXBsZSBlcnJvciBlc3RpbWF0ZXMuDQoNCiMjIyBXaGVuIFNob3VsZCBZb3UgRmF2b3IgQ3Jvc3MtVmFsaWRhdGlvbiBPdmVyIGEgU2luZ2xlIFNwbGl0Pw0KDQoqICoqU21hbGwgdG8gTW9kZXJhdGUgU2FtcGxlIFNpemVzICgkTiA8IDUsMDAwJCk6KiogV2hlbiBkYXRhIGlzIGF0IGEgcHJlbWl1bSAoc3VjaCBhcyBvdXIgYFBpbWFJbmRpYW5zRGlhYmV0ZXNgIHByb2ZpbGUgd2l0aCA3Njggcm93cyksIENWIGVuc3VyZXMgZXZlcnkgc2luZ2xlIHJvdyBzcGVuZHMgdGltZSBhcyBib3RoIGEgdHJhaW5pbmcgcGFyYW1ldGVyIGFuZCBhbiBvdXQtb2Ytc2FtcGxlIGV2YWx1YXRpb24gcG9pbnQuDQoNCiogKipIeXBlcnBhcmFtZXRlciBPcHRpbWl6YXRpb246KiogV2hlbiBzZWxlY3RpbmcgdHVuaW5nIHBhcmFtZXRlcnPigJRzdWNoIGFzIHRoZSBudW1iZXIgb2YgbmVpZ2hib3JzICgkayQpIGluIEtOTiBvciByZWd1bGFyaXphdGlvbiBwYXJhbWV0ZXJzICgkXGxhbWJkYSQpIGluIFJpZGdlL0xhc3Nv4oCUZXZhbHVhdGluZyBwZXJmb3JtYW5jZSBhZ2FpbnN0IGEgc2luZ2xlIHRlc3Qgc3BsaXQgcXVpY2tseSBvdmVyZml0cyB0aGUgdGVzdCBhcmNoaXRlY3R1cmUuIENWIGNyZWF0ZXMgYW4gaW50ZXJuYWwgbG9vcCB0aGF0IHByZXNlcnZlcyB0aGUgZmluYWwgdGVzdCBzZXQncyBvYmplY3RpdmUgZnJhbWV3b3JrLg0KDQoqICoqVW5zdGFibGUgSW5kdWN0aXZlIEJpYXNlczoqKiBXaGVuIGV2YWx1YXRpbmcgbm9uLXBhcmFtZXRyaWMgbW9kZWxzIHdpdGggaGlnaCB2YXJpYW5jZSAobGlrZSBkZWVwIHRyZWVzIG9yIGhpZ2gtZGltZW5zaW9uYWwgS05OKSwgQ1YgcHJvdmlkZXMgYSByZWFsaXN0aWMgYXZlcmFnZSBleHBlY3RhdGlvbiBvZiBwZXJmb3JtYW5jZSByYXRoZXIgdGhhbiBhIHNpbmdsZSBzdG9jaGFzdGljIHNuYXBzaG90Lg0KDQojIyBDcmVhdGluZyB0aGUgQ3Jvc3MtVmFsaWRhdGlvbiBJbmZyYXN0cnVjdHVyZQ0KDQpXZSB3aWxsIHV0aWxpemUgdGhlIGBQaW1hSW5kaWFuc0RpYWJldGVzYCBkYXRhc2V0LiBUbyBlbGltaW5hdGUgZG93bnN0cmVhbSBwYXJzaW5nIGVycm9ycyB3aXRoaW4gbm9uLXBhcmFtZXRyaWMgYWxnb3JpdGhtcyBsaWtlIEtOTiwgd2Ugd2lsbCBleHRyYWN0IG91ciBjb250aW51b3VzIHByZWRpY3RvcnMgYWxvbmdzaWRlIG91ciB0YXJnZXQgYW5kIGRyb3AgbWlzc2luZyByZWNvcmRzIGltbWVkaWF0ZWx5Lg0KDQpXZSBpbml0aWFsaXplIGEgMTAtZm9sZCBjcm9zcy12YWxpZGF0aW9uIGZyYW1lIHVzaW5nIGBtb2RlbHI6OmNyb3Nzdl9rZm9sZCgpYC4gTGlrZSBvdXIgcHJldmlvdXMgd29ya2Zsb3csIHRoaXMgY3JlYXRlcyBsaWdodHdlaWdodCBwb2ludGVyIG1hdHJpY2VzIHRvIG9wdGltaXplIFJBTSB1dGlsaXphdGlvbi4NCg0KYGBge3IgY3Ytc2V0dXB9DQojIExvYWQgRGF0YQ0KZGF0YSgiUGltYUluZGlhbnNEaWFiZXRlcyIpDQoNCiMgQ2xlYW4gZGF0YSBwcm9maWxlIGFuZCBmaWx0ZXIgZmVhdHVyZXMNCmNsZWFuX2RhdGEgPC0gUGltYUluZGlhbnNEaWFiZXRlcyAlPiUNCiAgc2VsZWN0KGRpYWJldGVzLCBnbHVjb3NlLCBtYXNzLCBhZ2UsIHByZXNzdXJlKSAlPiUNCiAgZHJvcF9uYSgpDQoNCnNldC5zZWVkKDEwMSkgIyBHdWFyYW50ZWUgZGV0ZXJtaW5pc3RpYyBmb2xkcyBhY3Jvc3MgZW52aXJvbm1lbnRzDQpjdl9mcmFtZSA8LSBjcm9zc3Zfa2ZvbGQoY2xlYW5fZGF0YSwgayA9IDEwKQ0KDQojIEluc3BlY3QgdGhlIHBvaW50ZXIgbWF0cml4DQpwcmludChjdl9mcmFtZSkNCmBgYA0KTm90aWNlIHRoYXQgYGN2X2ZyYW1lYCBpcyBqdXN0IGEgc3RhbmRhcmQgZGF0YSBmcmFtZSBjb250YWluaW5nIDEwIHJvd3MgKG9uZSBmb3IgZWFjaCBmb2xkKS4gRWFjaCByb3cgY29udGFpbnMgYSBwb2ludGVyIHRvIHRoZSB0cmFpbmluZyBkYXRhICh0cmFpbikgYW5kIHRoZSB2YWxpZGF0aW9uIGRhdGEgKHRlc3QpIGZvciB0aGF0IHNwZWNpZmljIHNsaWNlLg0KDQojIyBBIFN0cmFpZ2h0Zm9yd2FyZCBFdmFsdWF0aW9uIEFwcHJvYWNoDQoNClRvIGF2b2lkIHdyaXRpbmcgYWJzdHJhY3QgZnVuY3Rpb25zLCB3ZSBjYW4gZXZhbHVhdGUgZWFjaCBmb2xkIGV4cGxpY2l0bHkgYnkgcHVsbGluZyBvdXQgdGhlIGRhdGEgbWF0cmljZXMgZGlyZWN0bHkuIFdlIHdpbGwgaW5pdGlhbGl6ZSBlbXB0eSB2ZWN0b3JzIHRvIHN0b3JlIG91ciBvdXQtb2Ytc2FtcGxlIGVycm9yIHJhdGVzLCBhbmQgZmlsbCB0aGVtIHVzaW5nIGEgYmFzaWMsIHJlYWRhYmxlIGBmb3JgIGxvb3AuDQoNCmBgYHtyfQ0KIyBJbml0aWFsaXplIHN0b3JhZ2UgdmVjdG9ycyBmb3Igb3VyIDEwIGZvbGRzDQplcnJfbG9naXN0aWMgPC0gbnVtZXJpYygxMCkNCmVycl9sZGEgICAgICA8LSBudW1lcmljKDEwKQ0KZXJyX3FkYSAgICAgIDwtIG51bWVyaWMoMTApDQoNCiMgTG9vcCB0aHJvdWdoIGVhY2ggb2YgdGhlIDEwIGZvbGRzIHN0ZXAtYnktc3RlcA0KZm9yIChpIGluIDE6MTApIHsNCiAgIyBFeHRyYWN0IGV4cGxpY2l0IGRhdGEgZnJhbWVzIGZvciB0aGUgY3VycmVudCBmb2xkDQogIGZvbGRfdHJhaW4gPC0gYXMuZGF0YS5mcmFtZShjdl9mcmFtZSR0cmFpbltbaV1dKQ0KICBmb2xkX3Rlc3QgIDwtIGFzLmRhdGEuZnJhbWUoY3ZfZnJhbWUkdGVzdFtbaV1dKQ0KICANCiAgIyAtLS0gMS4gTG9naXN0aWMgUmVncmVzc2lvbiAtLS0NCiAgZml0X2xvZyA8LSBnbG0oZGlhYmV0ZXMgfiBnbHVjb3NlICsgbWFzcyArIGFnZSArIHByZXNzdXJlLCBkYXRhID0gZm9sZF90cmFpbiwgZmFtaWx5ID0gYmlub21pYWwpDQogIHByb2JfbG9nIDwtIHByZWRpY3QoZml0X2xvZywgbmV3ZGF0YSA9IGZvbGRfdGVzdCwgdHlwZSA9ICJyZXNwb25zZSIpDQogIHByZWRfbG9nIDwtIGlmZWxzZShwcm9iX2xvZyA+IDAuNSwgInBvcyIsICJuZWciKQ0KICBlcnJfbG9naXN0aWNbaV0gPC0gbWVhbihwcmVkX2xvZyAhPSBmb2xkX3Rlc3QkZGlhYmV0ZXMpDQogIA0KICAjIC0tLSAyLiBMaW5lYXIgRGlzY3JpbWluYW50IEFuYWx5c2lzIC0tLQ0KICBmaXRfbGRhIDwtIGxkYShkaWFiZXRlcyB+IGdsdWNvc2UgKyBtYXNzICsgYWdlICsgcHJlc3N1cmUsIGRhdGEgPSBmb2xkX3RyYWluKQ0KICBwcmVkX2xkYSA8LSBwcmVkaWN0KGZpdF9sZGEsIG5ld2RhdGEgPSBmb2xkX3Rlc3QpJGNsYXNzDQogIGVycl9sZGFbaV0gPC0gbWVhbihwcmVkX2xkYSAhPSBmb2xkX3Rlc3QkZGlhYmV0ZXMpDQogIA0KICAjIC0tLSAzLiBRdWFkcmF0aWMgRGlzY3JpbWluYW50IEFuYWx5c2lzIC0tLQ0KICBmaXRfcWRhIDwtIHFkYShkaWFiZXRlcyB+IGdsdWNvc2UgKyBtYXNzICsgYWdlICsgcHJlc3N1cmUsIGRhdGEgPSBmb2xkX3RyYWluKQ0KICBwcmVkX3FkYSA8LSBwcmVkaWN0KGZpdF9xZGEsIG5ld2RhdGEgPSBmb2xkX3Rlc3QpJGNsYXNzDQogIGVycl9xZGFbaV0gPC0gbWVhbihwcmVkX3FkYSAhPSBmb2xkX3Rlc3QkZGlhYmV0ZXMpDQp9DQoNCiMgQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIG91dC1vZi1zYW1wbGUgZXJyb3IgYWNyb3NzIGFsbCBmb2xkcw0KbWVhbihlcnJfbG9naXN0aWMpDQptZWFuKGVycl9sZGEpDQptZWFuKGVycl9xZGEpDQpgYGANCiMjIFR1bmluZyBLTk4NCg0KTm93IGxldCdzIGluY29ycG9yYXRlIG91ciBub24tcGFyYW1ldHJpYyBtb2RlbCwgSy1OZWFyZXN0IE5laWdoYm9ycyAoS05OKS4gQmVjYXVzZSBLTk4gY2FsY3VsYXRlcyBFdWNsaWRlYW4gZGlzdGFuY2VzLCB3ZSBtdXN0IG5vcm1hbGl6ZSBvdXIgZmVhdHVyZXMgKCR6JC1zY29yZXMpIHVzaW5nIHRyYWluaW5nIHBhcmFtZXRlcnMgdG8gcHJldmVudCBkYXRhIGxlYWthZ2UuIFdlIHdpbGwgdGVzdCBhbiBhcnJheSBvZiBvZGQgY2hvaWNlcyBmb3IgJGskIHRvIGZpbmQgdGhlIG9wdGltYWwgbmVpZ2hib3Job29kIGRlbnNpdHkuDQoNCmBgYHtyfQ0KIyBEZWZpbmUgaHlwZXJwYXJhbWV0ZXIgY2FuZGlkYXRlIGdyaWQgDQprX2dyaWQgPSBzZXEoMSwgNTEsIGJ5ID0gMikNCmtubl9yZXN1bHRzID0gbnVtZXJpYyhsZW5ndGgoa19ncmlkKSkNCg0KIyBPdXRlciBsb29wOiBJdGVyYXRlIHRocm91Z2ggZWFjaCBjYW5kaWRhdGUgdmFsdWUgb2YgSw0KZm9yIChrX2lkeCBpbiBzZXFfYWxvbmcoa19ncmlkKSkgew0KICBjdXJyZW50X2sgPSBrX2dyaWRba19pZHhdDQogIGZvbGRfZXJyb3JzID0gbnVtZXJpYygxMCkNCiAgDQogICMgSW5uZXIgbG9vcDogQ29tcHV0ZSBjcm9zcy12YWxpZGF0aW9uIGVycm9yIGZvciB0aGlzIHNwZWNpZmljIEsNCiAgZm9yIChpIGluIDE6MTApIHsNCiAgICB0cmFpbiA9IGFzLmRhdGEuZnJhbWUoY3ZfZnJhbWUkdHJhaW5bW2ldXSkNCiAgICB0ZXN0ICA9IGFzLmRhdGEuZnJhbWUoY3ZfZnJhbWUkdGVzdFtbaV1dKQ0KICAgIA0KICAgICMgRVhQTElDSVQgQUxJR05NRU5UOiBTZWxlY3Qgb25seSB0aGUgNCBmb3JtdWxhIHByZWRpY3RvcnMNCiAgICB0cmFpbl94ID0gdHJhaW4gJT4lIHNlbGVjdChnbHVjb3NlLCBtYXNzLCBhZ2UsIHByZXNzdXJlKQ0KICAgIHRlc3RfeCAgPSB0ZXN0ICU+JSBzZWxlY3QoZ2x1Y29zZSwgbWFzcywgYWdlLCBwcmVzc3VyZSkNCiAgICANCiAgICAjIFNjYWxlIGZlYXR1cmVzIHVzaW5nIHRyYWluaW5nIG1ldHJpY3Mgc3RyaWN0bHkNCiAgICBtZWFucyA9IGNvbE1lYW5zKHRyYWluX3gpDQogICAgc2RzICAgPSBhcHBseSh0cmFpbl94LCAyLCBzZCkNCiAgICB0cmFpbl94X3NjYWxlZCA9IHNjYWxlKHRyYWluX3gsIGNlbnRlciA9IG1lYW5zLCBzY2FsZSA9IHNkcykNCiAgICB0ZXN0X3hfc2NhbGVkICA9IHNjYWxlKHRlc3RfeCwgY2VudGVyID0gbWVhbnMsIHNjYWxlID0gc2RzKQ0KICAgIA0KICAgIHByZWRzID0ga25uKHRyYWluID0gdHJhaW5feF9zY2FsZWQsIHRlc3QgPSB0ZXN0X3hfc2NhbGVkLCBjbCA9IHRyYWluJGRpYWJldGVzLCBrID0gY3VycmVudF9rKQ0KICAgIGZvbGRfZXJyb3JzW2ldID0gbWVhbihwcmVkcyAhPSB0ZXN0JGRpYWJldGVzKQ0KICB9DQogIA0KICAjIFN0b3JlIHRoZSBhdmVyYWdlIGNyb3NzLXZhbGlkYXRlZCBlcnJvciBmb3IgdGhpcyBLDQogIGtubl9yZXN1bHRzW2tfaWR4XSA9IG1lYW4oZm9sZF9lcnJvcnMpDQp9DQoNCiMgQ29tYmluZSBpbnRvIGEgdHVuaW5nIHN1bW1hcnkgdGFibGUNCnR1bmluZ19jdXJ2ZSA9IHRpYmJsZShLID0ga19ncmlkLCBDVl9FcnJvciA9IGtubl9yZXN1bHRzKQ0KYmVzdF9rbm4gPSB0dW5pbmdfY3VydmUgJT4lIGZpbHRlcihDVl9FcnJvciA9PSBtaW4oQ1ZfRXJyb3IpKSAlPiUgc2xpY2UoMSkNCnByaW50KGJlc3Rfa25uICkNCmBgYA0KIyMgQ29tcGlsaW5nIGFuZCBQcmVzZW50aW5nIEZpbmFsIFJlc3VsdHMNCg0KTGV0J3MgZ2F0aGVyIG91ciBmaW5hbCBvdXQtb2Ytc2FtcGxlIGNyb3NzLXZhbGlkYXRpb24gZXJyb3IgcmF0ZXMgYWNyb3NzIGFsbCBmb3VyIG1vZGVsIGNhbmRpZGF0ZXMgYW5kIHZpZXcgdGhlbSBpbiBhIGNsZWFyLCBpbnRlcmFjdGl2ZSBmb3JtYXQuDQoNCmBgYHtyfQ0KIyBDb21waWxlIGZpbmFsIHN1bW1hcnkgZGF0YSBmcmFtZQ0KZmluYWxfc3VtbWFyeSA8LSB0aWJibGUoDQogIE1vZGVsID0gYygiUXVhZHJhdGljIERpc2NyaW1pbmFudCBBbmFseXNpcyAoUURBKSIsIA0KICAgICAgICAgICAgIkxvZ2lzdGljIFJlZ3Jlc3Npb24iLCANCiAgICAgICAgICAgICJMaW5lYXIgRGlzY3JpbWluYW50IEFuYWx5c2lzIChMREEpIiwgDQogICAgICAgICAgICBwYXN0ZTAoIlR1bmVkIEtOTiAoSyA9ICIsIGJlc3Rfa25uJEssICIpIikpLA0KICBDVl9FcnJvciA9IGMobWVhbihlcnJfcWRhKSwgbWVhbihlcnJfbG9naXN0aWMpLCBtZWFuKGVycl9sZGEpLCBiZXN0X2tubiRDVl9FcnJvcikNCikNCg0KIyBSZW5kZXIgaW50ZXJhY3RpdmUgSFRNTCB0YWJsZSBzb3J0ZWQgc21hbGxlc3QgdG8gbGFyZ2VzdCBlcnJvcg0KZGF0YXRhYmxlKA0KICBmaW5hbF9zdW1tYXJ5LA0KICBjb2xuYW1lcyA9IGMoIk1vZGVsIENhbmRpZGF0ZSIsICIxMC1Gb2xkIENWIEVycm9yIFJhdGUiKSwNCiAgb3B0aW9ucyA9IGxpc3QoZG9tID0gJ3QnLCBvcmRlciA9IGxpc3QobGlzdCgxLCAnYXNjJykpKSwNCiAgcm93bmFtZXMgPSBGQUxTRQ0KKSAlPiUNCiAgZm9ybWF0UGVyY2VudGFnZSgnQ1ZfRXJyb3InLCBkaWdpdHMgPSAxKQ0KDQpgYGANCg0KIyMjIFRoZSBTdXByZW1hY3kgb2YgTm9uLVBhcmFtZXRyaWMgQWdub3N0aWNpc20NCg0KTG9va2luZyBkaXJlY3RseSBhdCB0aGUgaW50ZXJhY3RpdmUgbWV0cmljcyB0YWJsZToNCg0KKiAqKlR1bmVkIEtOTiAoSyA9IDMzKSoqIHdpbnMgdGhlIG91dC1vZi1zYW1wbGUgcmFjZSB3aXRoIGEgZGVmaW5pdGl2ZSBlcnJvciByYXRlIG9mICoqMjIuMyUqKi4NCiogKipMb2dpc3RpYyBSZWdyZXNzaW9uKiogYW5kICoqTERBKiogZm9ybSBhIHJpZ2lkIGxpbmVhciB0aWVyIGluIHRoZSBtaWRkbGUgYXQgKioyMy43JSoqIGFuZCAqKjI0LjElKiouDQoqICoqUURBKiogZHJvcHMgdG8gdGhlIGFic29sdXRlIGJvdHRvbSwgc3VmZmVyaW5nIHRoZSBoaWdoZXN0IGVycm9yIHJhdGUgYXQgKioyNS4wJSoqLg0KDQojIyMgR2VvbWV0cmljYWxseSBDb21wbGV4IERlY2lzaW9uIEJvdW5kYXJpZXMNCg0KVGhpcyBvdXRjb21lIGRlbGl2ZXJzIGEgY3J1Y2lhbCBsZXNzb24gb24gdGhlICoqQmlhcy1WYXJpYW5jZSBUcmFkZW9mZioqIHRoYXQgY29tcGxldGVseSB1cGVuZHMgc3RhbmRhcmQgdGV4dGJvb2sgaW50dWl0aW9uOg0KDQojIyMjIFdoeSBLTk4gT3V0cGVyZm9ybXMgdGhlIExpbmVhciBNb2RlbHMNCg0KTG9naXN0aWMgUmVncmVzc2lvbiBhbmQgTERBIGFzc3VtZSB0aGF0IHRoZSBsb2ctb2RkcyBjaGFuZ2UgbGluZWFybHkgYW5kIHRoYXQgdGhlIGJvdW5kYXJ5IHNlcGFyYXRpbmcgaGVhbHRoeSBhbmQgZGlhYmV0aWMgcGF0aWVudHMgaXMgYSBmbGF0IGh5cGVycGxhbmUuIElmIHRoZSByZWFsLXdvcmxkIGJpb2xvZ2ljYWwgYm91bmRhcnkgaXMgaGlnaGx5IGlycmVndWxhciwgd2luZGluZywgb3IgcG9ja2V0ZWQgd2l0aCBub24tbGluZWFyIGludGVyYWN0aW9ucywgdGhlc2UgbW9kZWxzIHN1ZmZlciBmcm9tIHNldmVyZSAqKnN5c3RlbWljIHN0cnVjdHVyYWwgYmlhcyoqLg0KDQpLTk4gY2FycmllcyBubyBwYXJhbWV0cmljIGJhZ2dhZ2UuIEJ5IHN0YW5kYXJkaXppbmcgdGhlIGZlYXR1cmVzIGFuZCB0dW5pbmcgdGhlIG5laWdoYm9yaG9vZCBkZW5zaXR5IHRvICRrID0gMzMkLCBLTk4gc21vb3RocyBvdXQgbG9jYWxpemVkIHN0b2NoYXN0aWMgbm9pc2Ugd2hpbGUgcmVtYWluaW5nIGNvbXBsZXRlbHkgZmxleGlibGUuIEl0IGlzIGZyZWUgdG8gYmVuZCBhbmQgd3JhcCBhcm91bmQgaGlnaGx5IGNvbXBsZXgsIG9yZ2FuaWMgY2x1c3RlcnMgaW4gJFxtYXRoYmJ7Un1eNCQgdGhhdCBhIGxpbmVhciBoeXBlcnBsYW5lIGlzIG1hdGhlbWF0aWNhbGx5IGluY2FwYWJsZSBvZiBjYXB0dXJpbmcuDQoNCiMjIyMgVGhlIERlY2VwdGl2ZSBOYXR1cmUgb2YgUURBIChUaGUgVmFyaWFuY2UgUGVuYWx0eSkNCg0KSW4gb3VyIHByZXZpb3VzIGV4cGxvcmF0b3J5IGFuYWx5c2lzIG9mIHRoZSBjbGFzcy1zcGVjaWZpYyBjb3ZhcmlhbmNlIG1hdHJpY2VzICgkXFNpZ21hX3tuZWd9JCBhbmQgJFxTaWdtYV97cG9zfSQpLCB3ZSBmb3VuZCBjbGVhciBlbXBpcmljYWwgZXZpZGVuY2Ugb2YgaGV0ZXJvc2NlZGFzdGljaXR54oCUdGhlIHZhcmlhbmNlIG9mIGBnbHVjb3NlYCBleHBhbmRlZCBieSA0NCUgaW4gdGhlIGRpYWJldGljIGNvaG9ydC4gVGhlb3JldGljYWxseSwgdGhpcyAqc2hvdWxkKiBqdXN0aWZ5IFFEQSdzIGN1cnZlZCBkZWNpc2lvbiBib3VuZGFyeS4NCg0KSG93ZXZlciwgbG9vayBhdCB0aGUgb3V0LW9mLXNhbXBsZSB2YWxpZGF0aW9uIGVycm9yOiBRREEgcGVyZm9ybXMgdGhlIHdvcnN0ICgqKjI1LjAlKiopLiBXaHk/IEJlY2F1c2UgUURBIGF0dGVtcHRzIHRvIGZpdCBhIHN0cmljdCBwYXJhbWV0cmljIGN1cnZlIChhIHF1YWRyYXRpYyBzdXJmYWNlKSBieSBlc3RpbWF0aW5nIGEgbWFzc2l2ZSBzdWl0ZSBvZiBzZXBhcmF0ZSBjb3ZhcmlhbmNlIHBhcmFtZXRlcnMgKCRLIFx0aW1lcyBwKHArMSkvMiA9IDIwJCBlbGVtZW50cykuIFRoaXMgaW50cm9kdWNlcyAqKmVzdGltYXRpb24gdmFyaWFuY2UqKi4gV2hlbiBleHBvc2VkIHRvIHNoaWZ0aW5nIGNyb3NzLXZhbGlkYXRpb24gZm9sZHMsIFFEQSdzIGN1cnZlZCBib3VuZGFyeSBvdmVyZml0cyB0aGUgbm9pc2Ugb2YgdGhlIHRyYWluaW5nIGRhdGEsIGNvbGxhcHNpbmcgb3V0LW9mLXNhbXBsZS4gDQoNCktOTiwgYnkgY29udHJhc3QsIGFjaGlldmVzIGl0cyBub24tbGluZWFyIGZsZXhpYmlsaXR5IHRocm91Z2ggbG9jYWwgdm90aW5nIHJhdGhlciB0aGFuIHJpZ2lkIHBhcmFtZXRlciBlc3RpbWF0aW9uLCBieXBhc3NpbmcgdGhpcyB2YXJpYW5jZSBwZW5hbHR5IGVudGlyZWx5Lg0KDQoqKlR1bmVkIEtOTiAoSyA9IDMzKSoqIGlzIG91ciBkZWZpbml0aXZlIG9wZXJhdGlvbmFsIHN0YW5kYXJkLiBJdCBwcm92ZXMgdGhhdCB3aGVuIG5hdHVyZSBwcmVzZW50cyBhIGNvbXBsZXgsIG5vbi1saW5lYXIgY29vcmRpbmF0aW9uIG9mIHBoeXNpY2FsIG1ldHJpY3MsIGFiYW5kb25pbmcgcmlnaWQgcGFyYW1ldHJpYyBhc3N1bXB0aW9ucyBhbmQgcmVseWluZyBvbiBzY2FsZWQgbG9jYWwgZ2VvbWV0cnkgeWllbGRzIHRoZSBtb3N0IHJvYnVzdCBnZW5lcmFsaXphdGlvbiBwcm9maWxlLg0KDQojIyBNb3JlIEFkdmFuY2VkIE1vZGVsaW5nIFBpcGVsaW5lOiBQYXJhbWV0cmljIExvb3BzIChMb2dpc3RpYywgTERBLCBRREEpDQoNCkJlY2F1c2UgYG1vZGVscmAgc3RvcmVzIG91ciBmb2xkcyBhcyBsaXN0cyBvZiBgcmVzYW1wbGVgIG9iamVjdHMsIHdlIGNhbiBidWlsZCBjdXN0b20gZnVuY3Rpb25zIHRvIHN5c3RlbWF0aWNhbGx5IG1hcCBtb2RlbHMgb3ZlciBvdXIgY3Jvc3MtdmFsaWRhdGlvbiBzcGxpdHMuIFRoaXMga2VlcHMgb3VyIGNvZGUgaGlnaGx5IG1vZHVsYXIgYW5kIHJlcHJvZHVjaWJsZS4NCg0KV2Ugd2lsbCB3cml0ZSBmdW5jdGlvbnMgdGhhdCBhY2NlcHQgYSByZXNhbXBsZSBzcGxpdCwgY29udmVydCBpdCBpbnRvIGV4cGxpY2l0IGRhdGEgZnJhbWVzIGluc2lkZSB0aGUgZW52aXJvbm1lbnQsIGZpdCB0aGUgYXJjaGl0ZWN0dXJlLCBhbmQgcmV0dXJuIG91dC1vZi1zYW1wbGUgY2xhc3NpZmljYXRpb25zIGJhc2VkIG9uIGEgc3RhbmRhcmQgMC41IGRlY2lzaW9uIHRocmVzaG9sZC4NCg0KYGBge3IgcGFyYW1ldHJpYy1jdi1mdW5jdGlvbnN9DQojIExvZ2lzdGljIFJlZ3Jlc3Npb24gRXZhbHVhdGlvbiBGdW5jdGlvbg0KZXZhbHVhdGVfbG9naXN0aWMgPC0gZnVuY3Rpb24oc3BsaXQpIHsNCiAgIyBFeHBsaWNpdGx5IGV4dHJhY3Qgc3RhbmRhcmQgZGF0YSBmcmFtZXMgZnJvbSB0aGUgcmVzYW1wbGUgcG9pbnRlcg0KICB0cmFpbiA8LSBhcy5kYXRhLmZyYW1lKHNwbGl0KQ0KICB0ZXN0ICA8LSBhcy5kYXRhLmZyYW1lKHNwbGl0KQ0KICANCiAgZml0IDwtIGdsbShkaWFiZXRlcyB+IGdsdWNvc2UgKyBtYXNzICsgYWdlICsgcHJlc3N1cmUsIGRhdGEgPSB0cmFpbiwgZmFtaWx5ID0gYmlub21pYWwpDQogIHByb2JzIDwtIHByZWRpY3QoZml0LCBuZXdkYXRhID0gdGVzdCwgdHlwZSA9ICJyZXNwb25zZSIpDQogIHByZWRzIDwtIGlmZWxzZShwcm9icyA+IDAuNSwgInBvcyIsICJuZWciKQ0KICANCiAgcmV0dXJuKG1lYW4ocHJlZHMgIT0gdGVzdCRkaWFiZXRlcykpDQp9DQoNCiMgTERBIEV2YWx1YXRpb24gRnVuY3Rpb24NCmV2YWx1YXRlX2xkYSA8LSBmdW5jdGlvbihzcGxpdCkgew0KICB0cmFpbiA8LSBhcy5kYXRhLmZyYW1lKHNwbGl0KQ0KICB0ZXN0ICA8LSBhcy5kYXRhLmZyYW1lKHNwbGl0KQ0KICANCiAgZml0IDwtIGxkYShkaWFiZXRlcyB+IGdsdWNvc2UgKyBtYXNzICsgYWdlICsgcHJlc3N1cmUsIGRhdGEgPSB0cmFpbikNCiAgcHJlZHMgPC0gcHJlZGljdChmaXQsIG5ld2RhdGEgPSB0ZXN0KSRjbGFzcw0KICANCiAgcmV0dXJuKG1lYW4ocHJlZHMgIT0gdGVzdCRkaWFiZXRlcykpDQp9DQoNCiMgUURBIEV2YWx1YXRpb24gRnVuY3Rpb24NCmV2YWx1YXRlX3FkYSA8LSBmdW5jdGlvbihzcGxpdCkgew0KICB0cmFpbiA8LSBhcy5kYXRhLmZyYW1lKHNwbGl0KQ0KICB0ZXN0ICA8LSBhcy5kYXRhLmZyYW1lKHNwbGl0KQ0KICANCiAgZml0IDwtIHFkYShkaWFiZXRlcyB+IGdsdWNvc2UgKyBtYXNzICsgYWdlICsgcHJlc3N1cmUsIGRhdGEgPSB0cmFpbikNCiAgcHJlZHMgPC0gcHJlZGljdChmaXQsIG5ld2RhdGEgPSB0ZXN0KSRjbGFzcw0KICANCiAgcmV0dXJuKG1lYW4ocHJlZHMgIT0gdGVzdCRkaWFiZXRlcykpDQp9DQoNCmBgYA0KDQpOb3csIHdlIGV4ZWN1dGUgdGhlc2UgZnVuY3Rpb25zIGFjcm9zcyBhbGwgMTAgZm9sZHMgdXNpbmcgYHB1cnJyOjptYXBfZGJsYCBhbmQgYXBwZW5kIHRoZSBvdXQtb2Ytc2FtcGxlIGVycm9yIHRyYWNrcyBkaXJlY3RseSB0byBvdXIgZGF0YSBmcmFtZSBzdHJ1Y3R1cmUuDQoNCmBgYHtyIGV4ZWN1dGUtcGFyYW1ldHJpYy1jdn0NCmN2X3Jlc3VsdHMgPC0gY3ZfZnJhbWUgJT4lDQogIG11dGF0ZSgNCiAgICBlcnJfbG9naXN0aWMgPSBtYXBfZGJsKHRlc3QsIGV2YWx1YXRlX2xvZ2lzdGljKSwNCiAgICBlcnJfbGRhICAgICAgPSBtYXBfZGJsKHRlc3QsIGV2YWx1YXRlX2xkYSksDQogICAgZXJyX3FkYSAgICAgID0gbWFwX2RibCh0ZXN0LCBldmFsdWF0ZV9xZGEpDQogICkNCg0KIyBSZXZpZXcgZW1waXJpY2FsIG91dC1vZi1zYW1wbGUgZXJyb3IgZGlzdHJpYnV0aW9uDQpjdl9yZXN1bHRzICU+JQ0KICBzZWxlY3Qoc3RhcnRzX3dpdGgoImVycl8iKSkgJT4lDQogIHN1bW1hcnkoKQ0KDQpgYGANCg0KIyMgVHVuaW5nIEtOTiB2aWEgQ3Jvc3MtVmFsaWRhdGlvbg0KDQpVbmxpa2UgTG9naXN0aWMgUmVncmVzc2lvbiwgTERBLCBhbmQgUURBLCAqKiRLJC1OZWFyZXN0IE5laWdoYm9ycyAoS05OKSoqIG1ha2VzIG5vIHN0cnVjdHVyYWwgYXNzdW1wdGlvbnMgYWJvdXQgcGFyYW1ldHJpYyBmb3JtcyBvciB1bmRlcmx5aW5nIEdhdXNzaWFuIGRlbnNpdGllcy4gSXQgaXMgYSBwdXJlbHkgbWVtb3J5LWJhc2VkLCBub24tcGFyYW1ldHJpYyBjbGFzc2lmaWVyLg0KDQojIyMgVGhlIEltcG9ydGFuY2Ugb2YgRmVhdHVyZSBTY2FsaW5nIGluIEtOTg0KDQpLTk4gZGV0ZXJtaW5lcyBjbGFzcyBhc3NpZ25tZW50cyBiYXNlZCBlbnRpcmVseSBvbiB0aGUgRXVjbGlkZWFuIGRpc3RhbmNlIGJldHdlZW4gcG9pbnRzIGluICRcbWF0aGJie1J9XnAkOg0KDQokJGQoeF9pLCB4X2opID0gXHNxcnR7XHN1bV97bT0xfV5wICh4X3tpbX0gLSB4X3tqbX0pXjJ9JCQNCiANClJlY2FsbCBvdXIgcHJldmlvdXMgYW5hbHlzaXMgb2YgdGhlIHBvb2xlZCBjb3ZhcmlhbmNlIG1hdHJpeDogdGhlIHZhcmlhbmNlIG9mIGBnbHVjb3NlYCAoJFxzaWdtYV4yIFxhcHByb3ggNzYyJCkgaXMgbWFzc2l2ZSBjb21wYXJlZCB0byBgbWFzc2AgKCRcc2lnbWFeMiBcYXBwcm94IDU5JCkuIEJlY2F1c2UgRXVjbGlkZWFuIGRpc3RhbmNlIGlzIGhpZ2hseSBzZW5zaXRpdmUgdG8gdGhlIHJhdyBudW1lcmljYWwgc2NhbGUgb2YgeW91ciBtZXRyaWNzLCBhbiB1bnNjYWxlZCBLTk4gbW9kZWwgd2lsbCB0cmVhdCBgZ2x1Y29zZWAgYXMgdGhlIGRvbWluYW50IHByZWRpY3RvciwgY29tcGxldGVseSBpZ25vcmluZyB2YXJpYXRpb25zIGluIGBtYXNzYCBhbmQgYGFnZWAuDQoNClRoZXJlZm9yZSwgd2UgbXVzdCBzdGFuZGFyZGl6ZSBvdXIgZmVhdHVyZXMgKCR6JC1zY29yZXMgd2l0aCAkXG11PTAsIFxzaWdtYT0xJCkgKmluc2lkZSogdGhlIHZhbGlkYXRpb24gbG9vcCB0byBwcmV2ZW50IHN0cnVjdHVyYWwgZGF0YSBsZWFrYWdlLg0KDQpMZXQncyBidWlsZCBhIHJvYnVzdCB0dW5pbmcgZnVuY3Rpb24gdGhhdCBzY2FsZXMgdGhlIGRhdGEgdXNpbmcgdHJhaW5pbmcgcGFyYW1ldGVycyBhbmQgZXZhbHVhdGVzIHRoZSBtaXNjbGFzc2lmaWNhdGlvbiByYXRlIGZvciB2YXJ5aW5nIGNob2ljZXMgb2YgaHlwZXJwYXJhbWV0ZXIgJGskLg0KDQpgYGB7ciBrbm4tY3YtdHVuaW5nfQ0KIyBBbGlnbmVkIEtOTiBldmFsdWF0aW9uIGZ1bmN0aW9uIHVzaW5nIGNsZWFuIHB1cnJyIGlucHV0cw0KZXZhbHVhdGVfa25uIDwtIGZ1bmN0aW9uKHRyX3BvaW50ZXIsIHRlX3BvaW50ZXIsIGtfdmFsKSB7DQogICMgRXhwbGljaXRseSBmb3JjZSBjbGVhbiBkYXRhIGZyYW1lcyBmcm9tIHRoZSByZXNhbXBsZSBtYXBzDQogIHRyYWluX2RmIDwtIGFzLmRhdGEuZnJhbWUodHJfcG9pbnRlcikNCiAgdGVzdF9kZiAgPC0gYXMuZGF0YS5mcmFtZSh0ZV9wb2ludGVyKQ0KICANCiAgIyBFWFBMSUNJVCBBTElHTk1FTlQ6IExvY2sgZG93biB0aGUgZXhhY3QgNCBmb3JtdWxhIHByZWRpY3RvcnMNCiAgdHJhaW5feCA8LSB0cmFpbl9kZiAlPiUgc2VsZWN0KGdsdWNvc2UsIG1hc3MsIGFnZSwgcHJlc3N1cmUpDQogIHRlc3RfeCAgPC0gdGVzdF9kZiAgJT4lIHNlbGVjdChnbHVjb3NlLCBtYXNzLCBhZ2UsIHByZXNzdXJlKQ0KICANCiAgIyBOb3JtYWxpemUgZmVhdHVyZXMgc3RyaWN0bHkgb24gdHJhaW5pbmcgYm91bmRzIHRvIGJsb2NrIGRhdGEgbGVha2FnZQ0KICBtZWFucyA8LSBjb2xNZWFucyh0cmFpbl94KQ0KICBzZHMgICA8LSBhcHBseSh0cmFpbl94LCAyLCBzZCkNCiAgdHJhaW5feF9zY2FsZWQgPC0gc2NhbGUodHJhaW5feCwgY2VudGVyID0gbWVhbnMsIHNjYWxlID0gc2RzKQ0KICB0ZXN0X3hfc2NhbGVkICA8LSBzY2FsZSh0ZXN0X3gsIGNlbnRlciA9IG1lYW5zLCBzY2FsZSA9IHNkcykNCiAgDQogICMgRml0IEtOTiBhbmQgZXZhbHVhdGUgbWlzY2xhc3NpZmljYXRpb24gcmF0ZQ0KICBwcmVkcyA8LSBrbm4odHJhaW4gPSB0cmFpbl94X3NjYWxlZCwgdGVzdCA9IHRlc3RfeF9zY2FsZWQsIGNsID0gdHJhaW5fZGYkZGlhYmV0ZXMsIGsgPSBrX3ZhbCkNCiAgcmV0dXJuKG1lYW4ocHJlZHMgIT0gdGVzdF9kZiRkaWFiZXRlcykpDQp9DQoNCiMgRGVmaW5lIGh5cGVycGFyYW1ldGVyIGNhbmRpZGF0ZSBncmlkIChPZGQgdmFsdWVzIHRvIHByZXZlbnQgdGllcykNCmtfZ3JpZCA8LSBzZXEoMSwgNTEsIGJ5ID0gMikNCg0KIyBFeGVjdXRlIHRoZSBoeXBlcnBhcmFtZXRlciBzZWFyY2ggdXNpbmcgcHVyZSBwdXJyciBtYXBwaW5nDQprbm5fdHVuaW5nX3Jlc3VsdHMgPC0gdGliYmxlKEsgPSBrX2dyaWQpICU+JQ0KICBtdXRhdGUoDQogICAgbWVhbl9lcnJvciA9IG1hcF9kYmwoSywgZnVuY3Rpb24oaykgew0KICAgICAgIyBQYXNzIHRyYWluIGFuZCB0ZXN0IGNvbHVtbnMgY29uY3VycmVudGx5IHRvIHByZXNlcnZlIGRhdGEgZnJhbWUgYXR0cmlidXRlcw0KICAgICAgbWFwMl9kYmwoY3ZfcmVzdWx0cyR0cmFpbiwgY3ZfcmVzdWx0cyR0ZXN0LCB+ZXZhbHVhdGVfa25uKC54LCAueSwga192YWwgPSBrKSkgJT4lIG1lYW4oKQ0KICAgIH0pDQogICkNCg0KIyBFeHRyYWN0IHlvdXIgb3B0aW1hbCBtb2RlbCBwcm9maWxlDQpiZXN0X2tubiA8LSBrbm5fdHVuaW5nX3Jlc3VsdHMgJT4lIGZpbHRlcihtZWFuX2Vycm9yID09IG1pbihtZWFuX2Vycm9yKSkgJT4lIHNsaWNlKDEpDQpwcmludChiZXN0X2tubikNCg0KYGBgDQojIyMgVGhlICJEb3VibGUgQ1YiIE15dGgNCg0KQSBjb21tb24gcXVlc3Rpb24gYW1vbmcgYmVnaW5uaW5nIHByZWRpY3RpdmUgbW9kZWxlcnMgaXM6ICoqIkRvIEkgbmVlZCB0byBydW4gY3Jvc3MtdmFsaWRhdGlvbiB0d2ljZSBmb3IgS05O4oCUb25jZSB0byBmaW5kIHRoZSBiZXN0ICRrJCwgYW5kIGEgc2Vjb25kIHRpbWUgdG8gZmlndXJlIG91dCB0aGUgbW9kZWwncyB0cnVlIG91dC1vZi1zYW1wbGUgZXJyb3I/IioqDQoNClRoZSBhbnN3ZXIgaXMgKipubyoqLiBSdW5uaW5nIGEgc2Vjb25kLCBzZXBhcmF0ZSBjcm9zcy12YWxpZGF0aW9uIGxvb3AgaXMgY29tcGxldGVseSB1bm5lY2Vzc2FyeSBoZXJlIGJlY2F1c2Ugb2YgaG93IHdlIGJ1aWx0IG91ciBmcmFtZXdvcmsuDQoNCiMjIyMgV2h5IGEgU2Vjb25kIFJ1biBpcyBSZWR1bmRhbnQNCg0KV2hlbiB5b3UgZXZhbHVhdGUgYSBoeXBlcnBhcmFtZXRlciBncmlkIHVzaW5nICRrJC1mb2xkIGNyb3NzLXZhbGlkYXRpb24sIHRoZSBjcm9zcy12YWxpZGF0ZWQgZXJyb3IgcmF0ZSB5b3UgY2FsY3VsYXRlIGZvciB5b3VyIGNob3NlbiAkayQgKGUuZy4sICRrID0gMjUkKSAqKmlzIGFscmVhZHkgYSB2YWxpZCwgdW5iaWFzZWQgZXN0aW1hdGUgb2YgdGhlIG91dC1vZi1zYW1wbGUgZXJyb3IuKioNCg0KQmVjYXVzZSBldmVyeSBzaW5nbGUgdmFsaWRhdGlvbiBmb2xkIHdhcyBoZWxkIG91dCBlbnRpcmVseSB3aGlsZSB0aGUgS05OIGFsZ29yaXRobSBjb3VudGVkIGl0cyBuZWlnaGJvcnMgb24gdGhlIGNvcnJlc3BvbmRpbmcgdHJhaW5pbmcgZm9sZCwgdGhlIGVycm9yIHJhdGUgYXNzaWduZWQgdG8gYGJlc3Rfa25uJENWX0Vycm9yYCBoYXMgbmV2ZXIgc2VlbiB0aGUgdGVzdCBkYXRhIHBvaW50cy4NCg0KKiAqKldoZW4gd291bGQgeW91IG5lZWQgYSBzZWNvbmQgbG9vcCAoTmVzdGVkIENWKT8qKiBZb3Ugb25seSBuZWVkIG5lc3RlZCBjcm9zcy12YWxpZGF0aW9uIGlmIHlvdSBhcmUgdHJ5aW5nIHRvIHNlbGVjdCB0aGUgYmVzdCBvdmVyYWxsICphbGdvcml0aG0qIG91dCBvZiBhIG1hc3NpdmUgc3VpdGUgb2YgdHVuZWQgbW9kZWxzIChlLmcuLCBjb21wYXJpbmcgYSB0dW5lZCBSYW5kb20gRm9yZXN0IGFnYWluc3QgYSB0dW5lZCBTdXBwb3J0IFZlY3RvciBNYWNoaW5lKS4gSW4gdGhhdCBzY2VuYXJpbywgeW91IHVzZSBhbiAqb3V0ZXIgbG9vcCogdG8gY29tcGFyZSB0aGUgYWxnb3JpdGhtcyBhbmQgYW4gKmlubmVyIGxvb3AqIHRvIHR1bmUgdGhlaXIgcmVzcGVjdGl2ZSBoeXBlcnBhcmFtZXRlcnMuIEJ1dCBmb3IgYSBzaW5nbGUgbW9kZWwgbGlrZSBLTk4sIHRoZSB0dW5pbmcgZXJyb3IgcHJvZmlsZSAqaXMqIHlvdXIgb3V0LW9mLXNhbXBsZSBnZW5lcmFsaXphdGlvbiBtZXRyaWMuDQoNCiMjIyBWaXN1YWxpemluZyB0aGUgSHlwZXJwYXJhbWV0ZXIgVHVuaW5nIEN1cnZlDQoNCkxldCdzIHBsb3Qgb3VyIGNyb3NzLXZhbGlkYXRlZCBtaXNjbGFzc2lmaWNhdGlvbiB0cmFjayB0byBkZXRlcm1pbmUgdGhlIG9wdGltYWwgYmlhcy12YXJpYW5jZSBiYWxhbmNlIGZvciBvdXIgS05OIGNsYXNzaWZpZXIuDQoNCmBgYHtyIHBsb3Qta25uLXR1bmluZ30NCiMgU3RlcCAxOiBQcm9ncmFtbWF0aWNhbGx5IGV4dHJhY3QgdGhlIGNvb3JkaW5hdGVzIG9mIHRoZSBsb3dlc3QgZXJyb3IgcG9pbnQNCm9wdGltYWxfcG9pbnQgPC0ga25uX3R1bmluZ19yZXN1bHRzICU+JSANCiAgZmlsdGVyKG1lYW5fZXJyb3IgPT0gbWluKG1lYW5fZXJyb3IpKSAlPiUgDQogIHNsaWNlKDEpICMgSGFuZGxlcyB0aWVzIHNhZmVseSBieSBwaWNraW5nIHRoZSBmaXJzdCBvY2N1cnJlbmNlDQoNCiMgU3RlcCAyOiBSZW5kZXIgdGhlIGdyYXBoIHdpdGggdGhlIGhpZ2hsaWdodGVkIG1pbmltdW0NCmdncGxvdChrbm5fdHVuaW5nX3Jlc3VsdHMsIGFlcyh4ID0gSywgeSA9IG1lYW5fZXJyb3IpKSArDQogIGdlb21fbGluZShjb2xvciA9ICIjNzU3MGIzIiwgc2l6ZSA9IDEpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICIjNzU3MGIzIiwgc2l6ZSA9IDIpICsNCiAgDQogICMgSGlnaGxpZ2h0IExheWVyOiBPdmVybGF5IGEgcHJvbWluZW50IHBvaW50IG9uIHRoZSBtaW5pbXVtDQogIGdlb21fcG9pbnQoZGF0YSA9IG9wdGltYWxfcG9pbnQsIGFlcyh4ID0gSywgeSA9IG1lYW5fZXJyb3IpLCANCiAgICAgICAgICAgICBjb2xvciA9ICIjZTc0YzNjIiwgc2l6ZSA9IDQsIHNoYXBlID0gMTYpICsNCiAgDQogICMgVGV4dCBBbm5vdGF0aW9uOiBMYWJlbCB0aGUgb3B0aW1hbCBwb2ludCB3aXRoIGl0cyBzcGVjaWZpYyBwYXJhbWV0ZXJzDQogIGFubm90YXRlKCJ0ZXh0IiwgDQogICAgICAgICAgIHggPSBvcHRpbWFsX3BvaW50JEssIA0KICAgICAgICAgICB5ID0gb3B0aW1hbF9wb2ludCRtZWFuX2Vycm9yICsgMC4wMDUsICMgTnVkZ2VzIHRleHQgc2xpZ2h0bHkgYWJvdmUgdGhlIHBvaW50DQogICAgICAgICAgIGxhYmVsID0gcGFzdGUwKCJPcHRpbWFsIEsgPSAiLCBvcHRpbWFsX3BvaW50JEspLA0KICAgICAgICAgICBjb2xvciA9ICIjZTc0YzNjIiwgZm9udGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMCkgKw0KICANCiAgc2NhbGVfeF9yZXZlcnNlKCkgKyAjIFJldmVyc2VkIGF4aXMgc2hvd3MgbG93IEsgKEhpZ2ggVmFyaWFuY2UpIHRvIGhpZ2ggSyAoSGlnaCBCaWFzKQ0KICBsYWJzKA0KICAgIHRpdGxlID0gIktOTiBIeXBlcnBhcmFtZXRlciBUdW5pbmcgdmlhIDEwLUZvbGQgQ3Jvc3MtVmFsaWRhdGlvbiIsDQogICAgc3VidGl0bGUgPSAiVGhlIGhpZ2hsaWdodGVkIG1pbmltdW0gbWluaW1pemVzIG91dC1vZi1zYW1wbGUgbWlzY2xhc3NpZmljYXRpb24gcmlzayIsDQogICAgeCA9ICJIeXBlcnBhcmFtZXRlciBDaG9pY2U6IE5laWdoYm9ycyAoSykgW0F4aXMgUmV2ZXJzZWRdIiwNCiAgICB5ID0gIkNyb3NzLVZhbGlkYXRlZCBNaXNjbGFzc2lmaWNhdGlvbiBSYXRlIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQpgYGB7ciBpZGVudGlmeS1iZXN0LWt9DQojIEV4dHJhY3QgdGhlIG1hdGhlbWF0aWNhbGx5IG9wdGltYWwgY2hvaWNlIGZvciBuZWlnaGJvciBjb3VudA0Kb3B0aW1hbF9rbm4gPC0ga25uX3R1bmluZ19yZXN1bHRzICU+JSBmaWx0ZXIobWVhbl9lcnJvciA9PSBtaW4obWVhbl9lcnJvcikpICU+JSBzbGljZSgxKQ0KcHJpbnQob3B0aW1hbF9rbm4pDQoNCmBgYA0KDQojIyBQZXJmb3JtYW5jZSBDb21wYXJpc29uDQoNCkxldCdzIGNvbXB1dGUgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGVycm9yIG9mIG91ciBvdXQtb2Ytc2FtcGxlIGVycm9ycyBhY3Jvc3MgYWxsIGZvdXIgbW9kZWwgY2FuZGlkYXRlcyB0byBtYWtlIGEgZGVmaW5pdGl2ZSBzdHJ1Y3R1cmFsIHNlbGVjdGlvbi4NCg0KYGBge3IgY29tcGxldGUtc3ludGhlc2lzfQ0KIyBDb21waWxlIHBhcmFtZXRyaWMgZXJyb3IgbWV0cmljcw0KZmluYWxfc3VtbWFyeSA8LSBjdl9yZXN1bHRzICU+JQ0KICBzdW1tYXJpc2UoDQogICAgTG9naXN0aWMgPSBtZWFuKGVycl9sb2dpc3RpYyksDQogICAgTERBICAgICAgPSBtZWFuKGVycl9sZGEpLA0KICAgIFFEQSAgICAgID0gbWVhbihlcnJfcWRhKQ0KICApICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSAiTW9kZWwiLCB2YWx1ZXNfdG8gPSAiQ1ZfRXJyb3IiKQ0KDQojIEFwcGVuZCBvdXIgb3B0aW1hbGx5IHR1bmVkIEtOTiB0cmFjaw0KZmluYWxfc3VtbWFyeSA8LSBmaW5hbF9zdW1tYXJ5ICU+JQ0KICBhZGRfcm93KE1vZGVsID0gcGFzdGUwKCJUdW5lZCBLTk4gKEsgPSAiLCBvcHRpbWFsX2tubiRLLCAiKSIpLCBDVl9FcnJvciA9IG9wdGltYWxfa25uJG1lYW5fZXJyb3IpDQoNCiMgTG9hZCB0aGUgaW50ZXJhY3RpdmUgRFQgbGlicmFyeQ0KbGlicmFyeShEVCkNCg0KIyBDb252ZXJ0IHlvdXIgZmluYWwgc3VtbWFyeSB0byBhbiBpbnRlcmFjdGl2ZSBEYXRhVGFibGUgd2lkZ2V0DQpkYXRhdGFibGUoDQogIGZpbmFsX3N1bW1hcnksDQogIGNvbG5hbWVzID0gYygiTW9kZWwgQ2FuZGlkYXRlIiwgIjEwLUZvbGQgQ1YgRXJyb3IgUmF0ZSIpLA0KICBvcHRpb25zID0gbGlzdCgNCiAgICBwYWdlTGVuZ3RoID0gNSwgICAgICAgICAgICMgS2VlcCB0aGUgdGFibGUgY29tcGFjdA0KICAgIGRvbSA9ICd0JywgICAgICAgICAgICAgICAgIyBSZW1vdmUgdW5uZWNlc3Nhcnkgc2VhcmNoL3BhZ2luYXRpb24gYmFycyBzaW5jZSBpdCdzIG9ubHkgNCByb3dzDQogICAgb3JkZXIgPSBsaXN0KGxpc3QoMSwgJ2FzYycpKSAjIFNvcnQgYnkgdGhlIHNlY29uZCBjb2x1bW4gKGluZGV4IDEpIGZyb20gc21hbGxlc3QgdG8gbGFyZ2VzdA0KICApLA0KICByb3duYW1lcyA9IEZBTFNFICAgICAgICAgICAgIyBTdHJpcCByb3cgaW5kaWNlcyBmb3IgYSBjbGVhbmVyIGxheW91dA0KKSAlPiUNCiAgZm9ybWF0UGVyY2VudGFnZSgnQ1ZfRXJyb3InLCBkaWdpdHMgPSAxKSAjIEZvcm1hdCBkZWNpbWFscyB0byBwZXJjZW50YWdlcyB3aXRoIDEgZGVjaW1hbCBwbGFjZQ0KDQpgYGANCg0KIyMgQ29uY2x1c2lvbjogV2hpY2ggTW9kZWwgRml0cyBCZXN0Pw0KDQpMb29raW5nIGNsb3NlbHkgYXQgb3VyIGZpbmFsIDEwLWZvbGQgY3Jvc3MtdmFsaWRhdGVkIGVycm9yIHJhdGVzLCB3ZSBzZWUgYSBkaXN0aW5jdCBzaGlmdCBmcm9tIG91ciBwcmV2aW91cyB3b3JrZmxvdzoNCg0KKiAqKlF1YWRyYXRpYyBEaXNjcmltaW5hbnQgQW5hbHlzaXMgKFFEQSkqKiBhY2hpZXZlcyB0aGUgbG93ZXN0IG91dC1vZi1zYW1wbGUgZXJyb3IgcmF0ZSBhdCAqKjE5LjMlKiouIA0KKiAqKkxvZ2lzdGljIFJlZ3Jlc3Npb24qKiBhbmQgKipMREEqKiBwZXJmb3JtIGFsbW9zdCBpZGVudGljYWxseSwgbGFnZ2luZyBiZWhpbmQgYXQgKioyMC43JSoqIGFuZCAqKjIwLjglKiogZXJyb3IgcmVzcGVjdGl2ZWx5Lg0KKiAqKlR1bmVkIEtOTiAoSyA9IDI1KSoqIGRlbW9uc3RyYXRlcyB0aGUgaGlnaGVzdCBvdXQtb2Ytc2FtcGxlIGVycm9yIGF0ICoqMjIuMSUqKi4NCg0KIyMjIFdoZW4gdGhlIE1hdGggYW5kIHRoZSBNZXRyaWNzIEFsaWduDQpUaGlzIGlzIGEgdGV4dGJvb2sgZGVtb25zdHJhdGlvbiBvZiB0aGUgKipCaWFzLVZhcmlhbmNlIFRyYWRlb2ZmKiogd29ya2luZyBpbiBmYXZvciBvZiB0aGUgbW9yZSBjb21wbGV4IG1vZGVsLiANCg0KUmVjYWxsIG91ciBlYXJsaWVyIGV4cGxvcmF0b3J5IGRpYWdub3N0aWMgd2hlcmUgd2UgYW5hbHl6ZWQgdGhlIGNsYXNzLXNwZWNpZmljIGNvdmFyaWFuY2UgbWF0cmljZXMgKCRcU2lnbWFfe25lZ30kIGFuZCAkXFNpZ21hX3twb3N9JCkuIFdlIHVuY292ZXJlZCB1bmRlbmlhYmxlIGVtcGlyaWNhbCBldmlkZW5jZSBvZiBzZXZlcmUgaGV0ZXJvc2NlZGFzdGljaXR54oCUbW9zdCBub3RhYmx5LCB0aGUgdmFyaWFuY2Ugb2YgYGdsdWNvc2VgIGV4cGFuZGVkIGJ5IDQ0JSBpbiB0aGUgZGlhYmV0aWMgY29ob3J0LCBhbmQgdGhlIGNvcnJlbGF0aW9uIHN0cnVjdHVyZXMgYmV0d2VlbiBgbWFzc2AsIGBhZ2VgLCBhbmQgYGdsdWNvc2VgIGNvbXBsZXRlbHkgYWx0ZXJlZCBkaXJlY3Rpb25zIG9yIGNvbGxhcHNlZCBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzLg0KDQpCZWNhdXNlIHRoZSB0cnVlIHVuZGVybHlpbmcgZmVhdHVyZSBkaXN0cmlidXRpb25zIGRvIG5vdCBzaGFyZSBhIGNvbW1vbiBjb3ZhcmlhbmNlIHN0cnVjdHVyZSwgdGhlIGxpbmVhciBib3VuZGFyaWVzIGZvcmNlZCBieSBMREEgYW5kIExvZ2lzdGljIFJlZ3Jlc3Npb24gc3VmZmVyIGZyb20gKipzeXN0ZW1pYyBiaWFzKiouIFRoZXkgYXJlIG1hdGhlbWF0aWNhbGx5IGluY2FwYWJsZSBvZiBiZW5kaW5nIHRvIGFjY29tbW9kYXRlIHRoZSBleHBhbmRpbmcgdm9sdW1lIG9mIHRoZSBkaWFiZXRpYyBjb2hvcnQuIA0KDQpRREEgcGF5cyBhIHBhcmFtZXRlciBwZW5hbHR5IHRvIGVzdGltYXRlIHNlcGFyYXRlIGNvdmFyaWFuY2UgbWF0cmljZXMgKCRLIFx0aW1lcyBwKHArMSkvMiA9IDIwJCBwYXJhbWV0ZXJzKS4gSG93ZXZlciwgYmVjYXVzZSBvdXIgdHJhaW5pbmcgZGF0YSBpcyBzdWZmaWNpZW50bHkgZGVuc2UgKCROID0gNzY4JCksIHRoZSB2YXJpYW5jZSBpbnRyb2R1Y2VkIGJ5IGVzdGltYXRpbmcgdGhvc2UgZXh0cmEgMTAgcGFyYW1ldGVycyBpcyBvZmZzZXQgYnkgdGhlIG1hc3NpdmUgcmVkdWN0aW9uIGluIG1vZGVsIGJpYXMuIFRoZSBxdWFkcmF0aWMsIGN1cnZlZCBkZWNpc2lvbiBib3VuZGFyeSBpcyBhIHJlZmxlY3Rpb24gb2YgdGhlIGJpb2xvZ2ljYWwgZGF0YS1nZW5lcmF0aW5nIHByb2Nlc3MuDQoNCk1vdmluZyBwYXN0IHRoZSBwcmluY2lwbGUgb2YgcGFyc2ltb255LCAqKlF1YWRyYXRpYyBEaXNjcmltaW5hbnQgQW5hbHlzaXMgKFFEQSkqKiBpcyB0aGUgZGVmaW5pdGl2ZSBvcGVyYXRpb25hbCBzdGFuZGFyZCBmb3IgdGhpcyBkYXRhc2V0LiBJdCBzdWNjZXNzZnVsbHkgbGV2ZXJhZ2VzIGNsYXNzLXNwZWNpZmljIGNvdmFyaWFuY2Ugc3RydWN0dXJlcyB0byBkZWxpdmVyIGEgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBhbmQgcmVwcm9kdWNpYmxlICoqMS40JSB0byAxLjUlIG91dC1vZi1zYW1wbGUgcGVyZm9ybWFuY2UgYnJlYWt0aHJvdWdoKiogb3ZlciBpdHMgbGluZWFyIHBlZXJzLg0KDQojIyBXaHkgZGlkIHRoZSByZXN1bHRzIGZsaXA/DQoNCkZpcnN0LCBJJ20gbm90IGVudGlyZWx5IHN1cmUuIFRoaXMgc29ydCBvZiByZXN1bHQgaXMgd2h5IEkgcmVseSBvbiBgY2FyZXRgJ3MgZnVuY3Rpb25hbGl0eSB0byBtYWludGFpbiBzdGFiaWxpdHkgYWNyb3NzIG1vZGVsIGNvbXBhcmlzb24uIFdlJ2xsIGxlYXJuIG1vcmUgYWJvdXQgdGhpcyBpbiBhIGxhdGVyIG1vZHVsZS4gDQoNCioqSy1OZWFyZXN0IE5laWdoYm9ycyAoS05OKSoqIG1haW50YWlucyBjb21wbGV0ZWx5IHN0YWJsZSwgaWRlbnRpY2FsIHRlc3QgZXJyb3IgcmF0ZXMgYWNyb3NzIGJvdGggY29kZSB2ZXJzaW9ucywgd2hpbGUgdGhlIHBhcmFtZXRyaWMgbW9kZWxzICgqKkxvZ2lzdGljIFJlZ3Jlc3Npb24sIExEQSwgYW5kIFFEQSoqKSB2YXJ5IG9yIGludmVydGVkIHJlc3VsdHMgKFFEQSEpLg0KDQpCZWNhdXNlIEtOTidzIGVycm9yIHJhdGUgaXMgcGVyZmVjdGx5IHN0YXRpYywgSSdtIGd1ZXNzaW5nIHRoYXQgb3VyIGNyb3NzLXZhbGlkYXRpb24gcGFydGl0aW9ucyBhbmQgdW5kZXJseWluZyBkYXRhIHNsaWNlcyBhcmUgaWRlbnRpY2FsIGJldHdlZW4gcnVucy4gSW5zdGVhZCwgdGhlIGRpc2NyZXBhbmN5IGlzIGRyaXZlbiBieSBob3cgUiBldmFsdWF0ZXMgcHJlZGljdGlvbiBvdXRwdXRzIGJlaGluZCB0aGUgc2NlbmVzIHdoZW4gbWlncmF0aW5nIGNvZGUgaW50byBhbiBhdXRvbWF0ZWQgcGlwZWxpbmUgKGxpa2UgYHB1cnJyOjptYXBgKS4NCg0KVGhpcyBpcyBnb29kLCBjYXVzZSBmb3Igc29tZSBvZiB5b3UgZmlyc3QgdGltZSBSIGNvZGVycywgc2VlaW5nIGBmb3JgIGxvb3BzIGFuZCBgcHVycnJgIG1pZ2h0IGJlIG92ZXJ3aGVsbWluZy4gVGhlc2UgY29tcGxleCBjb2RpbmcgbnVhbmNlcyBhcmUgZXhhY3RseSB0aGUgcmVhc29uIEkgaW50cm9kdWNlIGBjYXJldGAuIExhdGVyIHRob3VnaCwgSSBtdXN0IHRvcnR1cmUgeW91IGZvciBhIGZldyB3ZWVrcyBtb3JlIGJlZm9yZSBpbnRyb2R1Y2luZyBgY2FyZXRgLiBUaGF0IHdheSB5b3UnbGwgaGF2ZSBhbiBhcHByZWNpYXRpb24gZm9yIGl0J3Mgc2ltcGxlIGZ1bmN0aW9ucy4gDQoNCkZvciB0b2RheSwgSSByZWFsbHkganVzdCB3YW50ZWQgdG8gZXhwb3NlIHlvdSB0byBjcm9zcy12YWxpZGF0aW9uIGFuZCBob3cgaXQgY2FuIGJlIHVzZWQuIFlvdSBzYXcgaG93IHdlIGNhbiB1c2UgaXQgdG8gZXN0aW1hdGUgb3V0LW9mLXNhbXBsZSBlcnJvciBpbiBzbWFsbCBkYXRhIHNldHMsIGFuZCB5b3UgYWxzbyBzYXcgaG93IHdlIGNhbiB0dW5lIGh5cGVycGFyYW1ldGVycyAobGlrZSB0aGUgKmsqIGluIGtubikuDQoNCkJleW9uZCBjYWxjdWxhdGluZyBhIGNsZWFuZXIgZ2VuZXJhbGl6YXRpb24gZXJyb3Igb3IgZGlhbGluZyBpbiBoeXBlcnBhcmFtZXRlcnMgbGlrZSAkayQgaW4gS05OLCBjcm9zcy12YWxpZGF0aW9uIGlzIGEgZm91bmRhdGlvbmFsIG1lY2hhbmlzbSBmb3Igc2V2ZXJhbCBhZHZhbmNlZCBhcmNoaXRlY3R1cmUtYnVpbGRpbmcgYW5kIGRpYWdub3N0aWMgd29ya2Zsb3dzLg0KDQpBdCBhIGdyYWR1YXRlIGxldmVsLCBjcm9zcy12YWxpZGF0aW9uIGlzIGxldmVyYWdlZCBub3QganVzdCBhcyBhbiBldmFsdWF0aW9uIHRvb2wsIGJ1dCBhcyBhbiAqKmFsZ29yaXRobWljIGNvbXBvbmVudCoqIHRvIHN0YWJpbGl6ZSwgY29tYmluZSwgYW5kIGRpYWdub3NlIG1vZGVscy4NCg0KIyMgT3RoZXIgVXNlIENhc2VzIGZvciBDcm9zcy1WYWxpZGF0aW9uIGluIFByZWRpY3RpdmUgTW9kZWxpbmcgDQoNCkFzaWRlIGZyb20gdGhlc2UsIGhlcmUgYXJlIHRoZSBmb3VyIHByaW1hcnkgYWx0ZXJuYXRpdmUgdXNlIGNhc2VzIGZvciBjcm9zcy12YWxpZGF0aW9uIGluIHByZWRpY3RpdmUgbW9kZWxpbmc6DQoNCiMjIyBGZWF0dXJlIFNlbGVjdGlvbiBhbmQgV3JhcHBlciBNZXRob2RzIChlLmcuLCBNYXhpbWl6ZSBQYXJzaW1vbnkpDQoNCldoZW4gZXZhbHVhdGluZyB3aGljaCBwcmVkaWN0b3JzIHRvIGtlZXAgaW4gYSBtb2RlbCwgZXZhbHVhdGluZyBmZWF0dXJlIGltcG9ydGFuY2Ugb24gdGhlIHRyYWluaW5nIHNldCBsZWFkcyB0byBzZXZlcmUgb3ZlcmZpdHRpbmcuIENyb3NzLXZhbGlkYXRpb24gaXMgdXNlZCBhcyB0aGUgb2JqZWN0aXZlIGVuZ2luZSBpbnNpZGUgKip3cmFwcGVyIHNlbGVjdGlvbiBhbGdvcml0aG1zKiogbGlrZSBSZWN1cnNpdmUgRmVhdHVyZSBFbGltaW5hdGlvbiAoUkZFKSBvciBTdGVwd2lzZSBTZWxlY3Rpb24uDQoNCiogKipIb3cgaXQgd29ya3M6KiogVGhlIGFsZ29yaXRobSByZW1vdmVzIGEgZmVhdHVyZSwgcnVucyBhIGZ1bGwgY3Jvc3MtdmFsaWRhdGlvbiBsb29wIHRvIGNhbGN1bGF0ZSB0aGUgY2hhbmdlIGluIG91dC1vZi1zYW1wbGUgZXJyb3IsIGFuZCBkZWNpZGVzIHdoZXRoZXIgdGhhdCBmZWF0dXJlJ3MgcHJlZGljdGl2ZSBzaWduYWwgaXMgcmVhbCBvciBqdXN0IHRyYWluaW5nIG5vaXNlLg0KDQoqICoqV2h5IGl0IG1hdHRlcnM6KiogVGhpcyBlbnN1cmVzIHRoYXQgeW91IG9ubHkgaW5jbHVkZSBmZWF0dXJlcyB0aGF0IGNvbnNpc3RlbnRseSBpbXByb3ZlIGdlbmVyYWxpemF0aW9uIGFjcm9zcyBhbGwgZm9sZHMsIHByb3RlY3RpbmcgdGhlIG1vZGVsIGZyb20gdGhlIGN1cnNlIG9mIGRpbWVuc2lvbmFsaXR5Lg0KDQoNCiMjIyBTdGFja2luZyBhbmQgRW5zZW1ibGluZyAoTWV0YS1MZWFybmluZyBNZXRhLUZlYXR1cmVzKQ0KDQoqKk1vZGVsIFN0YWNraW5nKiogY29tYmluZXMgZW50aXJlbHkgZGlmZmVyZW50IG1vZGVsIGFyY2hpdGVjdHVyZXMgKGUuZy4sIGJsZW5kaW5nIGEgTG9naXN0aWMgUmVncmVzc2lvbiwgYSBSYW5kb20gRm9yZXN0LCBhbmQgYSBTdXBwb3J0IFZlY3RvciBNYWNoaW5lKSBieSB0cmFpbmluZyBhICJtZXRhLW1vZGVsIiB0byBsZWFybiBob3cgdG8gd2VpZ2h0IHRoZWlyIGluZGl2aWR1YWwgcHJlZGljdGlvbnMuIFRvIGRvIHRoaXMgc2FmZWx5IHdpdGhvdXQgbWFzc2l2ZSBkYXRhIGxlYWthZ2UsIHlvdSAqbXVzdCogdXNlIGNyb3NzLXZhbGlkYXRpb24uDQoNCiogKipIb3cgaXQgd29ya3M6KiogWW91IHJ1biBjcm9zcy12YWxpZGF0aW9uIG9uIHlvdXIgYmFzZWxpbmUgbW9kZWxzLiBGb3IgZWFjaCBmb2xkLCB5b3UgZ2VuZXJhdGUgb3V0LW9mLXNhbXBsZSBwcmVkaWN0aW9ucy4gWW91IHN0aXRjaCB0aGVzZSBvdXQtb2Ytc2FtcGxlIHByZWRpY3Rpb25zIHRvZ2V0aGVyIHRvIGZvcm0gYW4gZW50aXJlbHkgbmV3IGRhdGFzZXQgb2YgIm1ldGEtZmVhdHVyZXMuIg0KDQoqICoqV2h5IGl0IG1hdHRlcnM6KiogVGhlIG1ldGEtbW9kZWwgaXMgdGhlbiB0cmFpbmVkIG9uIHRoZXNlIG91dC1vZi1zYW1wbGUgcHJlZGljdGlvbnMuIElmIHlvdSB1c2VkIHN0YW5kYXJkIHRyYWluaW5nIHByZWRpY3Rpb25zIGluc3RlYWQsIHRoZSBtZXRhLW1vZGVsIHdvdWxkIHF1aWNrbHkgbGVhcm4gdG8gb3Zlci1yZWx5IG9uIHdoaWNoZXZlciBiYXNlbGluZSBtb2RlbCBvdmVyZml0cyB0aGUgdHJhaW5pbmcgZGF0YSB0aGUgbW9zdC4NCg0KDQojIyMgUmVndWxhcml6YXRpb24gYW5kIENvc3QtQ29tcGxleGl0eSBQcnVuaW5nDQoNCkluIG1vZGVscyBsaWtlIExhc3NvL1JpZGdlIHJlZ3Jlc3Npb24gb3IgRGVjaXNpb24gVHJlZXMsIGNyb3NzLXZhbGlkYXRpb24gYWN0cyBhcyB0aGUgZGlyZWN0IG1hdGhlbWF0aWNhbCBicmFrZSBvbiBtb2RlbCBncm93dGguDQoNCiogKipMYXNzby9SaWRnZSAoJFxsYW1iZGEkIHNlbGVjdGlvbik6KiogQ1YgZGV0ZXJtaW5lcyB0aGUgZXhhY3QgcGVuYWx0eSBhcHBsaWVkIHRvIHRoZSBzaXplIG9mIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cy4NCg0KKiAqKkRlY2lzaW9uIFRyZWVzIChDb3N0LUNvbXBsZXhpdHkgUHJ1bmluZyk6KiogQSBmdWxseSBncm93biB0cmVlIHdpbGwgb3ZlcmZpdCBwZXJmZWN0bHkuIFdlIHVzZSBjcm9zcy12YWxpZGF0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgb3V0LW9mLXNhbXBsZSBwZW5hbHR5IGZvciBhZGRpbmcgbW9yZSB0ZXJtaW5hbCBsZWF2ZXMgKCRDX3AkKS4gVGhlIHRyZWUgaXMgcHJ1bmVkIGJhY2sgdG8gdGhlIGV4YWN0IG5vZGUgZGVwdGggd2hlcmUgdGhlIGNyb3NzLXZhbGlkYXRlZCBlcnJvciBjdXJ2ZSByZWFjaGVzIGl0cyBnbG9iYWwgbWluaW11bS4NCg0KIyMjIERpYWdub3NpbmcgU3RhYmlsaXR5IGFuZCBTdHJ1Y3R1cmFsIERhdGEgU2hpZnRpbmcNCg0KQ3Jvc3MtdmFsaWRhdGlvbiBpcyBhIHBvd2VyZnVsIGRpYWdub3N0aWMgdG9vbCBmb3IgbWVhc3VyaW5nICoqbW9kZWwgc3RhYmlsaXR5KiogYW5kIHVuY292ZXJpbmcgZGF0YSBhbm9tYWxpZXMuDQoNCkluc3RlYWQgb2YganVzdCBsb29raW5nIGF0IHRoZSBgbWVhbl9lcnJvcmAgYWNyb3NzIHlvdXIgMTAgZm9sZHMsIGEgcmlnb3JvdXMgYW5hbHlzdCBsb29rcyBjbG9zZWx5IGF0IHRoZSAqKnZhcmlhbmNlIG9yIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgZXJyb3IgYWNyb3NzIHRoZSBmb2xkcyoqLg0KDQoqICoqTG93IFZhcmlhbmNlIEFjcm9zcyBGb2xkczoqKiBJbmRpY2F0ZXMgdGhhdCB0aGUgbW9kZWwncyBwZXJmb3JtYW5jZSBpcyBzdGFibGUuIFRoZSBkYXRhLWdlbmVyYXRpbmcgcHJvY2VzcyBpcyBob21vZ2VuZW91cywgYW5kIHRoZSBtb2RlbCBpcyByb2J1c3QgdG8gbWlub3Igc2hpZnRzIGluIHRoZSB0cmFpbmluZyBkaXN0cmlidXRpb25zLg0KDQoqICoqSGlnaCBWYXJpYW5jZSBBY3Jvc3MgRm9sZHM6KiogSWYgRm9sZCAzIGhhcyBhIDEyJSBlcnJvciByYXRlIGJ1dCBGb2xkIDcgc3Bpa2VzIHRvIGEgMzQlIGVycm9yIHJhdGUsIGl0IGZsYWdzIGEgbWFzc2l2ZSBzdHJ1Y3R1cmFsIGlzc3VlLiBJdCBtZWFucyB5b3VyIGRhdGFzZXQgaXMgaGlnaGx5IGhldGVyb2dlbmVvdXMsIGNvbnRhaW5zIGxvY2FsaXplZCBoaWRkZW4gc3ViZ3JvdXBzLCBvciBwb3NzZXNzZXMgc2V2ZXJlIGNsYXNzIGltYmFsYW5jZXMgdGhhdCByYW5kb21seSBjbHVzdGVyIGludG8gc3BlY2lmaWMgZm9sZHMuIFRoaXMgc2lnbmFscyB0aGF0IHlvdXIgbW9kZWwncyBpbmR1Y3RpdmUgYmlhcyBpcyBoaWdobHkgdW5zdGFibGUgYW5kIHdpbGwgbGlrZWx5IGZhaWwgd2hlbiBkZXBsb3llZCBpbnRvIHByb2R1Y3Rpb24gZW52aXJvbm1lbnRzLg0K