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
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.
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:
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)
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.
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