R Demo
Data Prep
We’ll use the PimaIndiansDiabetes2
dataset from the mlbench
package.
# Install necessary packages (if not already installed)
# install.packages("mlbench")
# install.packages("caret") # For cross-validation and convenient model training
# install.packages("MASS") # For LDA and QDA
# Load packages
library(mlbench) # For PimaIndiansDiabetes2 dataset
library(caret) # For cross-validation and model training utilities
Loading required package: ggplot2
Loading required package: lattice
Registered S3 method overwritten by 'data.table':
method from
print.data.table
library(MASS) # For lda() and qda() functions
# Load the dataset
data(PimaIndiansDiabetes2)
my_data <- PimaIndiansDiabetes2
# Explore the data
head(my_data)
summary(my_data)
pregnant glucose pressure
Min. : 0.000 Min. : 44.0 Min. : 24.00
1st Qu.: 1.000 1st Qu.: 99.0 1st Qu.: 64.00
Median : 3.000 Median :117.0 Median : 72.00
Mean : 3.845 Mean :121.7 Mean : 72.41
3rd Qu.: 6.000 3rd Qu.:141.0 3rd Qu.: 80.00
Max. :17.000 Max. :199.0 Max. :122.00
NA's :5 NA's :35
triceps insulin mass
Min. : 7.00 Min. : 14.00 Min. :18.20
1st Qu.:22.00 1st Qu.: 76.25 1st Qu.:27.50
Median :29.00 Median :125.00 Median :32.30
Mean :29.15 Mean :155.55 Mean :32.46
3rd Qu.:36.00 3rd Qu.:190.00 3rd Qu.:36.60
Max. :99.00 Max. :846.00 Max. :67.10
NA's :227 NA's :374 NA's :11
pedigree age diabetes
Min. :0.0780 Min. :21.00 neg:500
1st Qu.:0.2437 1st Qu.:24.00 pos:268
Median :0.3725 Median :29.00
Mean :0.4719 Mean :33.24
3rd Qu.:0.6262 3rd Qu.:41.00
Max. :2.4200 Max. :81.00
str(my_data)
'data.frame': 768 obs. of 9 variables:
$ pregnant: num 6 1 8 1 0 5 3 10 2 8 ...
$ glucose : num 148 85 183 89 137 116 78 115 197 125 ...
$ pressure: num 72 66 64 66 40 74 50 NA 70 96 ...
$ triceps : num 35 29 NA 23 35 NA 32 NA 45 NA ...
$ insulin : num NA NA NA 94 168 NA 88 NA 543 NA ...
$ mass : num 33.6 26.6 23.3 28.1 43.1 25.6 31 35.3 30.5 NA ...
$ pedigree: num 0.627 0.351 0.672 0.167 2.288 ...
$ age : num 50 31 32 21 33 30 26 29 53 54 ...
$ diabetes: Factor w/ 2 levels "neg","pos": 2 1 2 1 2 1 2 1 2 2 ...
# Check for missing values
colSums(is.na(my_data))
pregnant glucose pressure triceps insulin mass pedigree
0 5 35 227 374 11 0
age diabetes
0 0
# Handle missing values: For simplicity in this demo, we'll remove rows with NA.
# In a real-world scenario, imputation would be a better approach for missing data.
my_data <- na.omit(my_data)
colSums(is.na(my_data)) # Verify no more NAs
pregnant glucose pressure triceps insulin mass pedigree
0 0 0 0 0 0 0
age diabetes
0 0
# Convert 'diabetes' to a factor with desired levels if not already
# The 'outcome' variable needs to be a factor for classification models in R.
# Let's ensure the levels are meaningful.
my_data$diabetes <- factor(my_data$diabetes, levels = c("neg", "pos"), labels = c("No", "Yes"))
# Ensure other variables are numeric where appropriate
# (PimaIndiansDiabetes2 is generally clean, but good practice to check)
- What did we just do with
na.omit()
? What are the
implications?
- Why is it important for our outcome variable (
diabetes
)
to be a factor?
- What are some other strategies for handling missing data besides
na.omit()
? (Imputation, etc.)
Model Fitting and Evaluation with Cross-Validation
Now that our data is ready, let’s fit our three models and evaluate
them using k-fold cross-validation. We’ll use the caret package, which
simplifies the cross-validation process significantly.
Setting up Cross-Validations Control
# Set a random seed for reproducibility
set.seed(123)
# Define cross-validation control
# We'll use 10-fold cross-validation
# 'repeats = 3' means the entire 10-fold CV process is repeated 3 times.
# This gives an even more stable estimate of performance.
train_control <- trainControl(
method = "repeatedcv",
number = 10, # k = 10 folds
repeats = 3, # Repeat the 10-fold CV 3 times
classProbs = TRUE, # Needed for ROC curve later if desired
summaryFunction = twoClassSummary # For classification, provides AUC, sensitivity, specificity
)
- Why
set.seed()
?
- What’s the difference between
number
and
repeats
in trainControl
?
- What other method options are available in
trainControl
? (e.g., boot
,
loocv
)
Logistic Regression (glm)
# Fit Logistic Regression model using caret's train function
# The 'method = "glm"' specifies a generalized linear model (logistic regression in this case)
# The 'family = "binomial"' is implicitly handled by caret for factor outcomes.
# 'metric = "ROC"' tells caret to optimize/report based on Area Under the ROC Curve.
# This is a good metric for imbalanced classes.
cat("\n--- Fitting Logistic Regression Model ---\n")
--- Fitting Logistic Regression Model ---
model_glm <- train(
diabetes ~ ., # Predict 'diabetes' using all other variables
data = my_data, # Our pre-processed dataset
method = "glm", # Specify Logistic Regression
family = "binomial", # For binary classification
trControl = train_control, # Our defined CV control
metric = "ROC" # Evaluate using ROC AUC
)
# Print the model results
print(model_glm)
Generalized Linear Model
392 samples
8 predictor
2 classes: 'No', 'Yes'
No pre-processing
Resampling: Cross-Validated (10 fold, repeated 3 times)
Summary of sample sizes: 352, 353, 353, 353, 353, 353, ...
Resampling results:
ROC Sens Spec
0.8505187 0.8877968 0.5538462
# Access individual folds' performance (optional, for deeper insight)
# model_glm$resample
Linear Discriminant Analysis (LDA)
# Fit LDA model using caret's train function
# 'method = "lda"' for Linear Discriminant Analysis
cat("\n--- Fitting LDA Model ---\n")
--- Fitting LDA Model ---
model_lda <- train(
diabetes ~ .,
data = my_data,
method = "lda",
trControl = train_control,
metric = "ROC"
)
# Print the model results
print(model_lda)
Linear Discriminant Analysis
392 samples
8 predictor
2 classes: 'No', 'Yes'
No pre-processing
Resampling: Cross-Validated (10 fold, repeated 3 times)
Summary of sample sizes: 352, 353, 353, 352, 353, 353, ...
Resampling results:
ROC Sens Spec
0.8397984 0.8841406 0.5692308
Quadratic Discriminant (QDA)
# Fit QDA model using caret's train function
# 'method = "qda"' for Quadratic Discriminant Analysis
cat("\n--- Fitting QDA Model ---\n")
--- Fitting QDA Model ---
model_qda <- train(
diabetes ~ .,
data = my_data,
method = "qda",
trControl = train_control,
metric = "ROC"
)
# Print the model results
print(model_qda)
Quadratic Discriminant Analysis
392 samples
8 predictor
2 classes: 'No', 'Yes'
No pre-processing
Resampling: Cross-Validated (10 fold, repeated 3 times)
Summary of sample sizes: 353, 353, 353, 352, 353, 353, ...
Resampling results:
ROC Sens Spec
0.8265505 0.8643875 0.6153846
Comparing Models
# Compare the models using resamples
# This provides a statistical comparison of performance metrics across models.
resample_results <- resamples(list(
Logistic = model_glm,
LDA = model_lda,
QDA = model_qda
))
# Summarize the comparison
summary(resample_results)
Call:
summary.resamples(object = resample_results)
Models: Logistic, LDA, QDA
Number of resamples: 30
ROC
Min. 1st Qu. Median Mean 3rd Qu.
Logistic 0.7485207 0.7995562 0.8431953 0.8505187 0.9075444
LDA 0.7236467 0.7914201 0.8284024 0.8397984 0.9016272
QDA 0.6210826 0.7877219 0.8317993 0.8265505 0.8823964
Max. NA's
Logistic 0.9792899 0
LDA 0.9467456 0
QDA 0.9556213 0
Sens
Min. 1st Qu. Median Mean 3rd Qu.
Logistic 0.7307692 0.8475783 0.8888889 0.8877968 0.9230769
LDA 0.7407407 0.8461538 0.9038462 0.8841406 0.9230769
QDA 0.7037037 0.8461538 0.8490028 0.8643875 0.9145299
Max. NA's
Logistic 1 0
LDA 1 0
QDA 1 0
Spec
Min. 1st Qu. Median Mean 3rd Qu.
Logistic 0.2307692 0.4615385 0.5384615 0.5538462 0.6153846
LDA 0.3076923 0.4615385 0.5384615 0.5692308 0.6730769
QDA 0.3846154 0.4807692 0.6153846 0.6153846 0.6923077
Max. NA's
Logistic 0.8461538 0
LDA 0.8461538 0
QDA 0.9230769 0
# Visualize the comparison (e.g., box plots of ROC, Sensitivity, Specificity)
scales <- list(x = list(relation = "free"), y = list(relation = "free"))
bwplot(resample_results, scales = scales)

# You can also use a dotplot for a different visualization
dotplot(resample_results, scales = scales)

- Looking at the output of
print(model_glm)
, what do the
‘ROC’, ‘Sens’, and ‘Spec’ values represent? (Area Under the ROC Curve,
Sensitivity/Recall, Specificity).
- How do the ROC values compare across the three models? Which one
performs best on average according to this metric?
- What does the
bwplot
or dotplot
tell us
about the variability of the model’s performance across the folds?
- Based on these results, which model would you recommend for this
dataset and why? (Encourage discussion on trade-offs, interpretability,
and assumptions).
- If a model has a much higher ROC but lower Sens compared to another,
what might that imply about its performance on different types of errors
(false positives vs. false negatives)?
Predictions and Interpretation
caret
handles much of the complexity, it’s important to
understand how to make predictions with your trained models and
interpret their coefficients.
Making Predictions
# Let's use the best performing model (or pick one for demonstration)
# For example, let's use the Logistic Regression model for prediction.
# We'll predict on the original training data for simplicity,
# but in a real scenario, you'd use a completely unseen test set.
# Predict class labels
predictions_glm <- predict(model_glm, newdata = my_data)
head(predictions_glm)
[1] No Yes No Yes Yes Yes
Levels: No Yes
# Predict class probabilities
probabilities_glm <- predict(model_glm, newdata = my_data, type = "prob")
head(probabilities_glm)
# Create a confusion matrix for the logistic regression model on training data
# This is NOT cross-validated performance, but good for understanding.
confusionMatrix(predictions_glm, my_data$diabetes)
Confusion Matrix and Statistics
Reference
Prediction No Yes
No 233 56
Yes 29 74
Accuracy : 0.7832
95% CI : (0.739, 0.823)
No Information Rate : 0.6684
P-Value [Acc > NIR] : 3.836e-07
Kappa : 0.4839
Mcnemar's Test P-Value : 0.004801
Sensitivity : 0.8893
Specificity : 0.5692
Pos Pred Value : 0.8062
Neg Pred Value : 0.7184
Prevalence : 0.6684
Detection Rate : 0.5944
Detection Prevalence : 0.7372
Balanced Accuracy : 0.7293
'Positive' Class : No
Interpreting Coefficients (Logistic Regression)
# For Logistic Regression, we can inspect the coefficients
# Note: For LDA/QDA, interpretation is more about discriminant functions,
# not direct coefficient interpretation like GLM.
# Access the fitted GLM model object from the caret train object
glm_object <- model_glm$finalModel
summary(glm_object)
Call:
NULL
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -1.004e+01 1.218e+00 -8.246 < 2e-16 ***
pregnant 8.216e-02 5.543e-02 1.482 0.13825
glucose 3.827e-02 5.768e-03 6.635 3.24e-11 ***
pressure -1.420e-03 1.183e-02 -0.120 0.90446
triceps 1.122e-02 1.708e-02 0.657 0.51128
insulin -8.253e-04 1.306e-03 -0.632 0.52757
mass 7.054e-02 2.734e-02 2.580 0.00989 **
pedigree 1.141e+00 4.274e-01 2.669 0.00760 **
age 3.395e-02 1.838e-02 1.847 0.06474 .
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 498.10 on 391 degrees of freedom
Residual deviance: 344.02 on 383 degrees of freedom
AIC: 362.02
Number of Fisher Scoring iterations: 5
# Interpret coefficients (e.g., odds ratios)
# For a coefficient 'b', the odds ratio is exp(b)
# Example: For 'glucose', if the coefficient is 'x', then for every unit increase in glucose,
# the odds of having diabetes are multiplied by exp(x), assuming other predictors are constant.
exp(coef(glm_object))
(Intercept) pregnant glucose pressure
4.358754e-05 1.085629e+00 1.039011e+00 9.985807e-01
triceps insulin mass pedigree
1.011285e+00 9.991750e-01 1.073085e+00 3.129611e+00
age
1.034535e+00
- What’s the difference between
type = "raw"
(default
predict output) and type = "prob"
?
- Why is interpreting coefficients more straightforward for Logistic
Regression than for LDA/QDA?
- What does an odds ratio less than 1 mean? Greater than 1?
Recap and Advanced Topics
- Logistic Regression: Linear decision boundary, predicts
probabilities.
- LDA: Linear decision boundary, assumes equal covariance
matrices.
- QDA: Quadratic decision boundary, assumes unequal covariance
matrices.
- Cross-validation: Essential for reliable model performance
estimation.
Further Exploration
- Hyperparameter Tuning: For more complex models
(like Random Forests, SVMs),
caret
can also tune
hyperparameters automatically during cross-validation. Explore tuneGrid
and tuneLength arguments in train()
.
- Other Metrics: Investigate other evaluation metrics
like Precision, Recall, F1-score, especially for imbalanced
datasets.
- Imbalanced Data: The
PimaIndiansDiabetes2
dataset has some class imbalance.
Research techniques for handling imbalanced data (e.g., SMOTE,
undersampling/oversampling) and how they integrate with caret.
- Feature Engineering/Selection: How would feature
engineering or feature selection impact these models?
- Visualization: Create ROC curves for each model to
visualize their trade-offs between sensitivity and specificity.
Appendix
ROC Curve Plot (requires pROC
package or manual
calculation)
# install.packages("pROC")
library(pROC)
# Get predictions from one of the models (e.g., Logistic Regression)
# Note: This is on the training data, for illustration. For true ROC, use a held-out test set.
prob_glm <- predict(model_glm, newdata = my_data, type = "prob")
# Create ROC object
roc_glm <- roc(response = my_data$diabetes, predictor = prob_glm$Yes, levels = c("No", "Yes"))
Setting direction: controls < cases
# Plot ROC curve
plot(roc_glm, main = "ROC Curve for Logistic Regression", col = "blue", lwd = 2)

auc(roc_glm) # Area Under Curve
Area under the curve: 0.8624
# You could loop this for all models and plot on the same graph for comparison
Without caret
It’s certainly possible to perform cross-validation and fit these
models without caret
. While caret
simplifies
the process significantly, doing it manually provides a deeper
understanding of the underlying mechanics.
Here’s the same demo without using the caret
package.
This version focuses more on manual loops for cross-validation and
direct calls to glm
, lda
, and
qda
.
Manual K-Fold Cross-Validation Setup
# Set a random seed for reproducibility
set.seed(123)
# Define number of folds (k)
k_folds <- 10
num_repeats <- 3 # We'll repeat the entire CV process multiple times for more stable results
# Create empty lists to store results for each model and repeat
results_glm <- list()
results_lda <- list()
results_qda <- list()
# Store performance metrics (ROC, Sensitivity, Specificity) for each fold/repeat
metrics_glm <- data.frame(ROC = numeric(), Sensitivity = numeric(), Specificity = numeric())
metrics_lda <- data.frame(ROC = numeric(), Sensitivity = numeric(), Specificity = numeric())
metrics_qda <- data.frame(ROC = numeric(), Sensitivity = numeric(), Specificity = numeric())
# Get the number of observations
n_obs <- nrow(my_data)
# Outer loop for repeats
for (rep in 1:num_repeats) {
cat(paste0("\n--- Cross-Validation Repeat ", rep, " ---\n"))
# Randomly permute the data to ensure folds are diverse
shuffled_data <- my_data[sample(n_obs), ]
# Create folds
# This creates a vector indicating which fold each row belongs to
folds <- cut(seq(1, n_obs), breaks = k_folds, labels = FALSE)
# Inner loop for folds
for (i in 1:k_folds) {
cat(paste0(" Fold ", i, "...\n"))
# Define training and testing sets for this fold
test_indices <- which(folds == i)
train_data <- shuffled_data[-test_indices, ]
test_data <- shuffled_data[test_indices, ]
# --- Fit Logistic Regression ---
tryCatch({ # Use tryCatch to handle potential errors (e.g., perfect separation)
model_glm_fold <- glm(diabetes ~ ., data = train_data, family = "binomial")
pred_prob_glm <- predict(model_glm_fold, newdata = test_data, type = "response")
pred_class_glm <- factor(ifelse(pred_prob_glm > 0.5, "Yes", "No"), levels = c("No", "Yes"))
# Ensure test_data$diabetes has consistent levels
test_data$diabetes <- factor(test_data$diabetes, levels = c("No", "Yes"))
# Calculate confusion matrix for GLM
cm_glm <- table(Predicted = pred_class_glm, Actual = test_data$diabetes)
# Calculate metrics manually or using pROC for AUC
# Need to handle cases where classes might be missing in a fold
if (ncol(cm_glm) == 2 && nrow(cm_glm) == 2) {
TP_glm <- cm_glm[2,2]
TN_glm <- cm_glm[1,1]
FP_glm <- cm_glm[2,1]
FN_glm <- cm_glm[1,2]
sensitivity_glm <- TP_glm / (TP_glm + FN_glm)
specificity_glm <- TN_glm / (TN_glm + FP_glm)
# Calculate ROC AUC (requires at least two distinct predictions and two classes in test set)
if (length(unique(test_data$diabetes)) > 1 && length(unique(pred_prob_glm)) > 1) {
roc_obj_glm <- roc(response = test_data$diabetes, predictor = pred_prob_glm, levels = c("No", "Yes"), quiet = TRUE)
auc_glm <- auc(roc_obj_glm)
} else {
auc_glm <- NA # Cannot compute AUC if only one class or constant predictions
}
} else {
sensitivity_glm <- NA
specificity_glm <- NA
auc_glm <- NA
}
metrics_glm <- rbind(metrics_glm, data.frame(ROC = auc_glm, Sensitivity = sensitivity_glm, Specificity = specificity_glm))
}, error = function(e) {
cat(" GLM Error in fold ", i, ": ", e$message, "\n")
metrics_glm <- rbind(metrics_glm, data.frame(ROC = NA, Sensitivity = NA, Specificity = NA))
})
# --- Fit LDA ---
tryCatch({
# Ensure there are at least two distinct classes in the training data for LDA
if (length(unique(train_data$diabetes)) > 1) {
model_lda_fold <- lda(diabetes ~ ., data = train_data)
pred_lda <- predict(model_lda_fold, newdata = test_data)
pred_prob_lda_yes <- pred_lda$posterior[, "Yes"] # Probability of 'Yes' class
pred_class_lda <- pred_lda$class
cm_lda <- table(Predicted = pred_class_lda, Actual = test_data$diabetes)
if (ncol(cm_lda) == 2 && nrow(cm_lda) == 2) {
TP_lda <- cm_lda[2,2]
TN_lda <- cm_lda[1,1]
FP_lda <- cm_lda[2,1]
FN_lda <- cm_lda[1,2]
sensitivity_lda <- TP_lda / (TP_lda + FN_lda)
specificity_lda <- TN_lda / (TN_lda + FP_lda)
if (length(unique(test_data$diabetes)) > 1 && length(unique(pred_prob_lda_yes)) > 1) {
roc_obj_lda <- roc(response = test_data$diabetes, predictor = pred_prob_lda_yes, levels = c("No", "Yes"), quiet = TRUE)
auc_lda <- auc(roc_obj_lda)
} else {
auc_lda <- NA
}
} else {
sensitivity_lda <- NA
specificity_lda <- NA
auc_lda <- NA
}
} else {
sensitivity_lda <- NA
specificity_lda <- NA
auc_lda <- NA
cat(" LDA skipped in fold ", i, ": Only one class present in training data.\n")
}
metrics_lda <- rbind(metrics_lda, data.frame(ROC = auc_lda, Sensitivity = sensitivity_lda, Specificity = specificity_lda))
}, error = function(e) {
cat(" LDA Error in fold ", i, ": ", e$message, "\n")
metrics_lda <- rbind(metrics_lda, data.frame(ROC = NA, Sensitivity = NA, Specificity = NA))
})
# --- Fit QDA ---
tryCatch({
# QDA often requires more data per class, can fail if not enough observations
if (length(unique(train_data$diabetes)) > 1 &&
sum(train_data$diabetes == "No") > ncol(train_data) &&
sum(train_data$diabetes == "Yes") > ncol(train_data)) { # Rule of thumb for QDA
model_qda_fold <- qda(diabetes ~ ., data = train_data)
pred_qda <- predict(model_qda_fold, newdata = test_data)
pred_prob_qda_yes <- pred_qda$posterior[, "Yes"]
pred_class_qda <- pred_qda$class
cm_qda <- table(Predicted = pred_class_qda, Actual = test_data$diabetes)
if (ncol(cm_qda) == 2 && nrow(cm_qda) == 2) {
TP_qda <- cm_qda[2,2]
TN_qda <- cm_qda[1,1]
FP_qda <- cm_qda[2,1]
FN_qda <- cm_qda[1,2]
sensitivity_qda <- TP_qda / (TP_qda + FN_qda)
specificity_qda <- TN_qda / (TN_qda + FP_qda)
if (length(unique(test_data$diabetes)) > 1 && length(unique(pred_prob_qda_yes)) > 1) {
roc_obj_qda <- roc(response = test_data$diabetes, predictor = pred_prob_qda_yes, levels = c("No", "Yes"), quiet = TRUE)
auc_qda <- auc(roc_obj_qda)
} else {
auc_qda <- NA
}
} else {
sensitivity_qda <- NA
specificity_qda <- NA
auc_qda <- NA
}
} else {
sensitivity_qda <- NA
specificity_qda <- NA
auc_qda <- NA
cat(" QDA skipped in fold ", i, ": Not enough observations per class in training data.\n")
}
metrics_qda <- rbind(metrics_qda, data.frame(ROC = auc_qda, Sensitivity = sensitivity_qda, Specificity = specificity_qda))
}, error = function(e) {
cat(" QDA Error in fold ", i, ": ", e$message, "\n")
metrics_qda <- rbind(metrics_qda, data.frame(ROC = NA, Sensitivity = NA, Specificity = NA))
})
} # End of inner fold loop
} # End of outer repeat loop
--- Cross-Validation Repeat 1 ---
Fold 1...
Fold 2...
Fold 3...
Fold 4...
Fold 5...
Fold 6...
Fold 7...
Fold 8...
Fold 9...
Fold 10...
--- Cross-Validation Repeat 2 ---
Fold 1...
Fold 2...
Fold 3...
Fold 4...
Fold 5...
Fold 6...
Fold 7...
Fold 8...
Fold 9...
Fold 10...
--- Cross-Validation Repeat 3 ---
Fold 1...
Fold 2...
Fold 3...
Fold 4...
Fold 5...
Fold 6...
Fold 7...
Fold 8...
Fold 9...
Fold 10...
Summarizing and Comparing Results
After running all the folds and repeats, we’ll have a collection of
performance metrics. Let’s summarize them and compare our models.
# Summarize the cross-validation results
cat("\n--- Summary of Logistic Regression Performance ---\n")
--- Summary of Logistic Regression Performance ---
summary(metrics_glm)
ROC Sensitivity Specificity
Min. :0.6759 Min. :0.1429 Min. :0.7727
1st Qu.:0.7695 1st Qu.:0.5000 1st Qu.:0.8519
Median :0.8514 Median :0.5774 Median :0.8889
Mean :0.8379 Mean :0.5534 Mean :0.8877
3rd Qu.:0.8992 3rd Qu.:0.6774 3rd Qu.:0.9223
Max. :0.9423 Max. :0.8000 Max. :0.9655
cat("Average ROC: ", mean(metrics_glm$ROC, na.rm = TRUE), "\n")
Average ROC: 0.8378616
cat("Average Sensitivity: ", mean(metrics_glm$Sensitivity, na.rm = TRUE), "\n")
Average Sensitivity: 0.5534245
cat("Average Specificity: ", mean(metrics_glm$Specificity, na.rm = TRUE), "\n")
Average Specificity: 0.8877038
cat("\n--- Summary of LDA Performance ---\n")
--- Summary of LDA Performance ---
summary(metrics_lda)
ROC Sensitivity Specificity
Min. :0.6728 Min. :0.1429 Min. :0.7619
1st Qu.:0.7708 1st Qu.:0.4659 1st Qu.:0.8519
Median :0.8485 Median :0.5833 Median :0.8889
Mean :0.8374 Mean :0.5521 Mean :0.8831
3rd Qu.:0.8942 3rd Qu.:0.6618 3rd Qu.:0.9200
Max. :0.9444 Max. :0.8000 Max. :0.9643
cat("Average ROC: ", mean(metrics_lda$ROC, na.rm = TRUE), "\n")
Average ROC: 0.8374091
cat("Average Sensitivity: ", mean(metrics_lda$Sensitivity, na.rm = TRUE), "\n")
Average Sensitivity: 0.5520997
cat("Average Specificity: ", mean(metrics_lda$Specificity, na.rm = TRUE), "\n")
Average Specificity: 0.8831438
cat("\n--- Summary of QDA Performance ---\n")
--- Summary of QDA Performance ---
summary(metrics_qda)
ROC Sensitivity Specificity
Min. :0.6667 Min. :0.1250 Min. :0.7600
1st Qu.:0.7771 1st Qu.:0.5000 1st Qu.:0.8180
Median :0.8240 Median :0.6339 Median :0.8596
Mean :0.8235 Mean :0.5729 Mean :0.8543
3rd Qu.:0.8819 3rd Qu.:0.7044 3rd Qu.:0.8878
Max. :0.9517 Max. :0.8333 Max. :0.9655
cat("Average ROC: ", mean(metrics_qda$ROC, na.rm = TRUE), "\n")
Average ROC: 0.8234612
cat("Average Sensitivity: ", mean(metrics_qda$Sensitivity, na.rm = TRUE), "\n")
Average Sensitivity: 0.5729406
cat("Average Specificity: ", mean(metrics_qda$Specificity, na.rm = TRUE), "\n")
Average Specificity: 0.8543147
# Combine results for easier comparison and visualization
all_metrics <- rbind(
data.frame(Model = "Logistic", metrics_glm),
data.frame(Model = "LDA", metrics_lda),
data.frame(Model = "QDA", metrics_qda)
)
# Remove rows with NA values (from failed folds) for plotting
all_metrics_clean <- na.omit(all_metrics)
# --- Visualization (requires ggplot2, not loaded by default here, but good for discussion) ---
# install.packages("ggplot2")
library(ggplot2)
# Boxplot of ROC values
ggplot(all_metrics_clean, aes(x = Model, y = ROC, fill = Model)) +
geom_boxplot() +
labs(title = "Model Performance Comparison (ROC)",
y = "ROC AUC", x = "Model") +
theme_minimal()

# Boxplot of Sensitivity values
ggplot(all_metrics_clean, aes(x = Model, y = Sensitivity, fill = Model)) +
geom_boxplot() +
labs(title = "Model Performance Comparison (Sensitivity)",
y = "Sensitivity", x = "Model") +
theme_minimal()

# Boxplot of Specificity values
ggplot(all_metrics_clean, aes(x = Model, y = Specificity, fill = Model)) +
geom_boxplot() +
labs(title = "Model Performance Comparison (Specificity)",
y = "Specificity", x = "Model") +
theme_minimal()

Here’s a summary of the average performance metrics for each
model:
Logistic Regression |
0.8379 |
0.5534 |
0.8877 |
LDA |
0.8374 |
0.5521 |
0.8831 |
QDA |
0.8235 |
0.5729 |
0.8543 |
Overall Performance (ROC AUC):
- Logistic Regression (0.8379) and LDA (0.8374) have almost identical
average ROC AUCs. The difference is negligible.
- QDA (0.8235) has a slightly lower average ROC AUC compared to
Logistic Regression and LDA.
- Interpretation: Based purely on overall discriminative power across
all possible thresholds, Logistic Regression and LDA are performing
equally well and slightly better than QDA. An AUC above 0.8 is generally
considered good, indicating that the models have strong discriminatory
power.
Sensitivity (True Positive Rate):
- QDA (0.5729) has the highest average Sensitivity.
- Logistic Regression (0.5534) and LDA (0.5521) have very similar and
slightly lower average Sensitivities compared to QDA.
- Interpretation: QDA is slightly better at correctly identifying
actual positive cases (e.g., people with diabetes). This means it has
fewer false negatives.
Specificity (True Negative Rate):
- Logistic Regression (0.8877) has the highest average
Specificity.
- LDA (0.8831) is very close to Logistic Regression.
- QDA (0.8543) has a lower average Specificity compared to Logistic
Regression and LDA.
- Logistic Regression is best at correctly identifying actual negative
cases (e.g., people without diabetes). This means it has fewer false
positives.
Which one is the “best”?
The “best” model depends on the specific goals and costs
associated with False Positives and False Negatives in your
application.
- If overall discriminative power (considering all thresholds) is
paramount, and you don’t have a strong preference for minimizing one
type of error over another:
- Logistic Regression or LDA are the top contenders, as their average
ROC AUCs are virtually identical and the highest. There’s not enough
difference here to declare one definitively better than the other based
solely on this metric.
- If minimizing False Negatives (catching as many positive cases as
possible) is more critical (i.e., high Sensitivity is preferred):
- QDA might be considered slightly better due to its marginally higher
average Sensitivity (0.5729). However, keep in mind that this comes at
the cost of lower Specificity.
- If minimizing False Positives (correctly identifying negative cases
and avoiding false alarms) is more critical (i.e., high Specificity is
preferred):
- Logistic Regression (or very closely, LDA) is clearly the winner. It
has the highest average Specificity, meaning it’s less likely to
incorrectly classify a negative case as positive.
Based on these results:
- For overall balanced performance (highest ROC AUC) and strongest
control over False Positives (highest Specificity), Logistic Regression
is marginally the “best,” closely followed by LDA.
- If you prioritize slightly higher Sensitivity (catching more true
positives), QDA offers a small advantage, but at the cost of lower
Specificity and a slightly lower overall ROC.
Logistic Regression appears to be the most robust choice among these
three models for this dataset. The differences between Logistic
Regression and LDA are so small that they could be considered
effectively equivalent. You’d likely choose between them based on ease
of implementation, interpretability, or minor theoretical
preferences.
How to interpret ROC, Sensitivity, and Specificity
ROC, Sensitivity, and Specificity are three metrics for evaluating
the performance of classification models, especially in situations where
you care about different types of errors. To understand these, first
understand the four basic outcomes in a binary classification problem,
often represented in a confusion matrix:
Predicted Positive |
True Positive (TP) |
False Positive (FP) |
Predicted Negative |
False Negative (FN) |
True Negative (TN) |
- True Positive (TP): The model correctly predicted a
positive outcome (e.g., predicted “has diabetes,” and the person
actually has diabetes).
- False Positive (FP): The model incorrectly
predicted a positive outcome (e.g., predicted “has diabetes,” but the
person actually does not have diabetes). This is also known as a Type I
error or a “false alarm.”
- False Negative (FN): The model incorrectly
predicted a negative outcome (e.g., predicted “does not have diabetes,”
but the person actually has diabetes). This is also known as a Type II
error or a “miss.”
- True Negative (TN): The model correctly predicted a
negative outcome (e.g., predicted “does not have diabetes,” and the
person actually does not have diabetes).
Sensitivity (Recall, True Positive Rate - TPR)
Sensitivity measures the proportion of actual positive cases that
were correctly identified by the model. It answers the question: “Out of
all the people who truly have the condition, how many did our model
correctly find?”
\(Sensitivity =
\frac{TP}{TP+FN}\)
- High Sensitivity (closer to 1.0): Means the model
is very good at catching positive cases. It has a low rate of False
Negatives (missing actual positives).
- Low Sensitivity (closer to 0.0): Means the model
misses many actual positive cases, leading to a high rate of False
Negatives.
Sensitivity is crucial when the cost of a False Negative is
high.
- Medical Diagnosis (e.g., detecting a serious disease like
cancer): You want very high sensitivity because missing a
disease (false negative) can have severe consequences for the patient.
You’d rather have some false alarms (false positives) that can be
further investigated.
- Security Systems: You want to detect all intruders
(high sensitivity), even if it means some false alarms.
- Fraud Detection: You want to catch as many
fraudulent transactions as possible.
SNNOUT (Sensitive Negative = Rule OUT)
If a highly sensitive test comes back negative, you can confidently
rule out the disease/condition.
Specificity (True Negative Rate - TNR)
Specificity measures the proportion of actual negative cases that
were correctly identified by the model. It answers the question: “Out of
all the people who truly do NOT have the condition, how many did our
model correctly identify as not having it?”
\(Specificity =
\frac{TN}{TN+FP}\)
- High Specificity (closer to 1.0): Means the model
is very good at correctly identifying negative cases. It has a low rate
of False Positives (incorrectly classifying negatives as
positives).
- Low Specificity (closer to 0.0): Means the model
incorrectly flags many negative cases as positive, leading to a high
rate of False Positives.
Specificity is crucial when the cost of a False Positive is
high.
- Spam Detection: You want high specificity to avoid
marking legitimate emails as spam (false positive), which would be very
annoying to users. You might tolerate a few spam emails getting through
(false negatives).
- Legal/Criminal Justice: Incorrectly identifying an
innocent person as guilty (false positive) has high societal
costs.
- Expensive or Invasive Follow-up Tests: If a
positive test leads to a costly, painful, or risky follow-up procedure,
you want high specificity to minimize unnecessary procedures.
SPPIN (Specific Positive = Rule IN)
If a highly specific test comes back positive, you can confidently
rule in the disease/condition.
Reciever Operating Characteristic Curve (ROC)
The ROC curve is a graphical plot that illustrates the diagnostic
ability of a binary classifier system as its discrimination threshold is
varied. It plots the True Positive Rate (Sensitivity) on the
y-axis against the False Positive Rate (1 - Specificity) on the
x-axis for different possible thresholds.
Each point on the ROC curve represents a sensitivity/specificity
pair corresponding to a particular classification threshold.
- Top-left corner (0,1): This represents a perfect
classifier: 100% Sensitivity (no false negatives) and 100% Specificity
(no false positives). The closer your curve is to this corner, the
better the model’s overall performance.
- Diagonal line (from 0,0 to 1,1): This represents a random
classifier (equivalent to flipping a coin). A model whose ROC curve
falls along this diagonal is performing no better than chance.
- Curve below the diagonal: Indicates a model that performs
worse than random chance. If this happens, you can often just invert the
model’s predictions to get a better-than-chance model!
The AUC is a single scalar value that summarizes the overall
performance of the classifier across all possible thresholds. It ranges
from 0 to 1.
- AUC = 1: Perfect classification.
- AUC = 0.5: Performance is no better than random
guessing.
- AUC < 0.5: Performance is worse than random
guessing.
- *A higher AUC indicates a better model. It can be interpreted as the
probability that the model ranks a randomly chosen positive instance
higher than a randomly chosen negative instance.
# Install necessary packages (if not already installed)
# install.packages("mlbench")
# install.packages("MASS")
# install.packages("dplyr")
# install.packages("ggplot2") # For plotting
# install.packages("pROC") # For ROC curves
# Load packages
library(mlbench)
library(MASS)
library(dplyr)
library(ggplot2)
library(pROC) # Load pROC for ROC curves and AUC
# Load the dataset
data(PimaIndiansDiabetes2)
my_data <- PimaIndiansDiabetes2
# Data Preparation (using dplyr)
my_data <- my_data %>%
na.omit() %>%
mutate(diabetes = factor(diabetes, levels = c("neg", "pos"), labels = c("No", "Yes")))
# --- 1. Fit each model on the *entire* dataset ---
# This is a final model, not part of the cross-validation loop.
# It's used for plotting the "overall" ROC curve for the full dataset.
cat("\n--- Fitting Final Models on Full Dataset for ROC Curve ---\n")
--- Fitting Final Models on Full Dataset for ROC Curve ---
# Logistic Regression
final_model_glm <- glm(diabetes ~ ., data = my_data, family = "binomial")
# LDA
final_model_lda <- lda(diabetes ~ ., data = my_data)
# QDA
# Add a check to ensure QDA can be fitted (sufficient data per class relative to predictors)
# This is similar to the check in the CV loop, but applied to the full dataset.
# The `ncol(my_data) - 1` accounts for all predictors
if (sum(my_data$diabetes == "No") >= (ncol(my_data) - 1) &&
sum(my_data$diabetes == "Yes") >= (ncol(my_data) - 1)) {
final_model_qda <- qda(diabetes ~ ., data = my_data)
} else {
warning("QDA could not be fitted on the full dataset due to insufficient observations per class.")
final_model_qda <- NULL # Set to NULL if it cannot be fitted
}
# --- 2. Get predicted probabilities for the 'Yes' class from each model ---
# Logistic Regression probabilities
# type = "response" gives probabilities directly for glm
prob_glm <- predict(final_model_glm, newdata = my_data, type = "response")
# LDA probabilities
# predict() for lda/qda returns a list; $posterior contains probabilities
prob_lda <- predict(final_model_lda, newdata = my_data)$posterior[, "Yes"]
# QDA probabilities (check if QDA model was successfully fitted)
prob_qda <- if (!is.null(final_model_qda)) {
predict(final_model_qda, newdata = my_data)$posterior[, "Yes"]
} else {
NULL # If QDA wasn't fitted, no probabilities
}
# --- 3. Create ROC objects for each model ---
# For Logistic Regression
roc_obj_glm <- roc(response = my_data$diabetes, predictor = prob_glm,
levels = c("No", "Yes")) # Ensure levels are in order: negative first
Setting direction: controls < cases
# For LDA
roc_obj_lda <- roc(response = my_data$diabetes, predictor = prob_lda,
levels = c("No", "Yes"))
Setting direction: controls < cases
# For QDA (only if the model was fitted)
if (!is.null(prob_qda)) {
roc_obj_qda <- roc(response = my_data$diabetes, predictor = prob_qda,
levels = c("No", "Yes"))
} else {
roc_obj_qda <- NULL
}
Setting direction: controls < cases
# --- 4. Plot all three ROC curves on the same graph ---
# Start with the first ROC curve (e.g., Logistic Regression)
plot(roc_obj_glm,
col = "#3182bd", # Color for GLM
lwd = 2, # Line width
main = "ROC Curves for Classification Models",
sub = paste0("Logistic Regression AUC: ", round(auc(roc_obj_glm), 3),
"\nLDA AUC: ", round(auc(roc_obj_lda), 3),
if (!is.null(roc_obj_qda)) { paste0("\nQDA AUC: ", round(auc(roc_obj_qda), 3)) }
else { "" }
),
grid = TRUE # Add a grid for better readability
)
# Add LDA curve to the existing plot
plot(roc_obj_lda,
col = "#de2d26", # Color for LDA
lwd = 2,
add = TRUE # Add to the current plot
)
# Add QDA curve to the existing plot (if it was fitted)
if (!is.null(roc_obj_qda)) {
plot(roc_obj_qda,
col = "#31a354", # Color for QDA
lwd = 2,
add = TRUE
)
}
# Add a legend to distinguish the curves
legend("bottomright",
legend = c(paste0("Logistic Regression (AUC = ", round(auc(roc_obj_glm), 3), ")"),
paste0("LDA (AUC = ", round(auc(roc_obj_lda), 3), ")"),
if (!is.null(roc_obj_qda)) { paste0("QDA (AUC = ", round(auc(roc_obj_qda), 3), ")") }
else { "QDA (Not Fitted)" }
),
col = c("#3182bd", "#de2d26", "#31a354"),
lwd = 2,
cex = 0.8 # Font size for legend
)
# You can also add the diagonal line representing random chance
abline(a = 0, b = 1, lty = 2, col = "#bdbdbd") # Dashed gray line

# --- Optional: Print individual AUCs again for easy reference ---
cat("\n--- AUC Values for Final Models ---\n")
--- AUC Values for Final Models ---
cat("Logistic Regression AUC: ", auc(roc_obj_glm), "\n")
Logistic Regression AUC: 0.8623605
cat("LDA AUC: ", auc(roc_obj_lda), "\n")
LDA AUC: 0.8605109
if (!is.null(roc_obj_qda)) {
cat("QDA AUC: ", auc(roc_obj_qda), "\n")
} else {
cat("QDA AUC: Not applicable (model not fitted)\n")
}
QDA AUC: 0.8652965
Why it’s important:
- Unlike Accuracy, Sensitivity, and Specificity (which depend on a
chosen threshold), the ROC curve and AUC provide a view of the model’s
performance across all possible thresholds. This is especially
useful for models that output probabilities (like logistic regression),
as you can choose the optimal threshold later based on your specific
needs.
- AUC is generally a more reliable metric than accuracy when dealing
with imbalanced
datasets (where one class is much more prevalent than the other). A
classifier can achieve high accuracy by simply predicting the majority
class all the time, but its AUC would reveal its poor discriminatory
power.
- AUC is excellent for comparing different models because it
summarizes their overall discriminative power in a single number. The
model with a higher AUC is generally preferred.
Trade-offs between Sensitivity and Specificity (and how ROC shows
it)
There’s often a trade-off between sensitivity and specificity.
- If you set a very low threshold for classifying an outcome as
“positive” (e.g., if any probability above 0.1 is considered positive),
you’ll likely increase your sensitivity (catch more true positives) but
also increase your false positive rate (decrease specificity).
- Conversely, if you set a very high threshold (e.g., only
probabilities above 0.9 are positive), you’ll increase your specificity
(reduce false positives) but likely decrease your sensitivity (miss more
true positives).
LS0tDQp0aXRsZTogIkxvZ2lzdGljIFJlZ3Jlc3Npb24sIExEQSwgYW5kIFFEQSB3aXRoIENyb3NzLVZhbGlkYXRpb24iDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KLS0tDQoNClRoaXMgZGVtbyBjb3ZlcnMgc29tZSBmdW5kYW1lbnRhbCBjbGFzc2lmaWNhdGlvbiB0ZWNobmlxdWVzOiBbTG9naXN0aWMgUmVncmVzc2lvbl0oaHR0cHM6Ly9zdGF0cy5vYXJjLnVjbGEuZWR1L3IvZGFlL2xvZ2l0LXJlZ3Jlc3Npb24vKSwgW0xpbmVhciBEaXNjcmltaW5hbnQgQW5hbHlzaXMgKExEQSldKGh0dHBzOi8vd3d3LnN0aGRhLmNvbS9lbmdsaXNoL2FydGljbGVzLzM2LWNsYXNzaWZpY2F0aW9uLW1ldGhvZHMtZXNzZW50aWFscy8xNDYtZGlzY3JpbWluYW50LWFuYWx5c2lzLWVzc2VudGlhbHMtaW4tci8pLCBhbmQgW1F1YWRyYXRpYyBEaXNjcmltaW5hbnQgQW5hbHlzaXMgKFFEQSldKGh0dHBzOi8vd3d3LnN0aGRhLmNvbS9lbmdsaXNoL2FydGljbGVzLzM2LWNsYXNzaWZpY2F0aW9uLW1ldGhvZHMtZXNzZW50aWFscy8xNDYtZGlzY3JpbWluYW50LWFuYWx5c2lzLWVzc2VudGlhbHMtaW4tci8pLiBXZSdsbCBhbHNvIGZvY3VzIG9uIGEgY3J1Y2lhbCBhc3BlY3Qgb2YgbW9kZWwgYnVpbGRpbmc6IHJvYnVzdCBldmFsdWF0aW9uIHVzaW5nIFtjcm9zcy12YWxpZGF0aW9uXShodHRwczovL3d3dy5nZWVrc2ZvcmdlZWtzLm9yZy9yLWxhbmd1YWdlL2Nyb3NzLXZhbGlkYXRpb24taW4tci1wcm9ncmFtbWluZy8pLiANCg0KIyMgTGVhcm5pbmcgT2JqZWN0aXZlczogIA0KIC0gVW5kZXJzdGFuZCB0aGUgdGhlb3JldGljYWwgdW5kZXJwaW5uaW5ncyBvZiBMb2dpc3RpYyBSZWdyZXNzaW9uLCBMaW5lYXIgRGlzY3JpbWluYW50IEFuYWx5c2lzIChMREEpLCBhbmQgUXVhZHJhdGljIERpc2NyaW1pbmFudCBBbmFseXNpcyAoUURBKS4gIA0KIC0gSW1wbGVtZW50IHRoZXNlIG1vZGVscyBpbiBgUmAuICANCiAtIEFwcGx5ICprKi1mb2xkIGNyb3NzLXZhbGlkYXRpb24gZm9yIHJvYnVzdCBtb2RlbCBldmFsdWF0aW9uLiAgDQogLSBDb21wYXJlIHRoZSBwZXJmb3JtYW5jZSBvZiBkaWZmZXJlbnQgY2xhc3NpZmljYXRpb24gbW9kZWxzLiAgDQoNCiMjIFdoYXQgYXJlIENsYXNzaWZpY2F0aW9uIE1vZGVscz8NCg0KIC0gX19Hb2FsOl9fIFByZWRpY3QgYSBjYXRlZ29yaWNhbCBvdXRjb21lIHZhcmlhYmxlIChlLmcuLCBzcGFtL25vdCBzcGFtLCBkaXNlYXNlL25vIGRpc2Vhc2UsIGN1c3RvbWVyIGNodXJuL25vIGNodXJuKS4gIA0KIC0gX19TdXBlcnZpc2VkIExlYXJuaW5nOl9fIFdlIGhhdmUgbGFiZWxlZCBkYXRhIChmZWF0dXJlcyBhbmQga25vd24gb3V0Y29tZXMpLiAgDQoNCiMjIEJyaWVmIFRoZW9yZXRpY2FsIE92ZXJ2aWV3IA0KDQpfXzEuIExvZ2lzdGljIFJlZ3Jlc3Npb246X18gICANCg0KIC0gV2hhdCdzIHRoZSBjb3JlIGlkZWE/IChQcmVkaWN0cyB0aGUgcHJvYmFiaWxpdHkgb2YgYmVsb25naW5nIHRvIGEgY2xhc3MgdXNpbmcgYSBsb2dpc3RpYyBmdW5jdGlvbikuICANCiAtIFdoYXQgdHlwZSBvZiBkZWNpc2lvbiBib3VuZGFyeSBkb2VzIGl0IGNyZWF0ZT8gKExpbmVhcikuICANCiAtIEFzc3VtcHRpb25zPyAoTm8gc3Ryb25nIGRpc3RyaWJ1dGlvbmFsIGFzc3VtcHRpb25zIG9uIHByZWRpY3RvcnMsIGJ1dCBsaW5lYXJpdHkgaW4gdGhlIGxvZy1vZGRzIGlzIGFzc3VtZWQpLiAgDQoNCl9fMi4gTGluZWFyIERpc2NyaW1pbmFudCBBbmFseXNpcyAoTERBKTpfXw0KDQogLSBXaGF0J3MgdGhlIGNvcmUgaWRlYT8gKEFzc3VtZXMgcHJlZGljdG9ycyB3aXRoaW4gZWFjaCBjbGFzcyBmb2xsb3cgYSBtdWx0aXZhcmlhdGUgR2F1c3NpYW4gZGlzdHJpYnV0aW9uIHdpdGggZXF1YWwgY292YXJpYW5jZSBtYXRyaWNlcykuICANCiAtIEhvdyBkb2VzIGl0IGNsYXNzaWZ5PyAoRmluZHMgbGluZWFyIGNvbWJpbmF0aW9ucyBvZiBwcmVkaWN0b3JzIHRoYXQgYmVzdCBzZXBhcmF0ZSB0aGUgY2xhc3NlcykuICANCiAtIERlY2lzaW9uIGJvdW5kYXJ5PyAoTGluZWFyKS4gIA0KIC0gV2hlbiBtaWdodCBpdCBiZSBwcmVmZXJyZWQgb3ZlciBsb2dpc3RpYyByZWdyZXNzaW9uPyAoV2hlbiBjbGFzc2VzIGFyZSB3ZWxsLXNlcGFyYXRlZCwgb3Igd2hlbiB0aGUgbm9ybWFsaXR5IGFzc3VtcHRpb24gaG9sZHMpLiAgDQoNCl9fMy4gUXVhZHJhdGljIERpc2NyaW1pbmFudCBBbmFseXNpcyAoUURBKTpfXyAgDQoNCiAtIEhvdyBkb2VzIGl0IGRpZmZlciBmcm9tIExEQT8gKEFzc3VtZXMgcHJlZGljdG9ycyB3aXRoaW4gZWFjaCBjbGFzcyBmb2xsb3cgYSBtdWx0aXZhcmlhdGUgR2F1c3NpYW4gZGlzdHJpYnV0aW9uIGJ1dCB3aXRoIHVuZXF1YWwgY292YXJpYW5jZSBtYXRyaWNlcykuICANCiAtIERlY2lzaW9uIGJvdW5kYXJ5PyAoUXVhZHJhdGljKS4gIA0KIC0gV2hlbiBtaWdodCBpdCBiZSBwcmVmZXJyZWQgb3ZlciBMREE/IChXaGVuIHRoZSBhc3N1bXB0aW9uIG9mIGVxdWFsIGNvdmFyaWFuY2UgbWF0cmljZXMgaXMgdmlvbGF0ZWQsIGFuZCB0aGUgdHJ1ZSBkZWNpc2lvbiBib3VuZGFyeSBpcyBub24tbGluZWFyKS4gIA0KDQojIyBXaHkgQ3Jvc3MtVmFsaWRhdGlvbj8NCiAtIFdoeSBkb24ndCB3ZSBqdXN0IHNwbGl0IG91ciBkYXRhIG9uY2UgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzPyIgKERpc2N1c3MgbGltaXRhdGlvbnM6IHNlbnNpdGl2aXR5IHRvIHNwbGl0LCBsZXNzIHJvYnVzdCBwZXJmb3JtYW5jZSBlc3RpbWF0ZSkuICANCiAtIF9fQ3Jvc3MtdmFsaWRhdGlvbjpfXyBBIHRlY2huaXF1ZSB0byBnZXQgYSBtb3JlIHJlbGlhYmxlIGVzdGltYXRlIG9mIGEgbW9kZWwncyBwZXJmb3JtYW5jZSBvbiB1bnNlZW4gZGF0YSBieSByZXBlYXRlZGx5IHNwbGl0dGluZyB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB2YWxpZGF0aW9uIHNldHMuICANCiAtIF9fKmsqLUZvbGQgQ3Jvc3MtVmFsaWRhdGlvbjpfXyBFeHBsYWluIHRoZSBwcm9jZXNzIOKAkyBkaXZpZGUgZGF0YSBpbnRvICprKiBmb2xkcywgdHJhaW4gb24gKmsqLTEgZm9sZHMsIHZhbGlkYXRlIG9uIHRoZSByZW1haW5pbmcgZm9sZCwgcmVwZWF0ICprKiB0aW1lcy4gIA0KIC0gX19CZW5lZml0czpfXyBNb3JlIHJvYnVzdCBwZXJmb3JtYW5jZSBlc3RpbWF0ZSwgdXNlcyBhbGwgZGF0YSBmb3IgYm90aCB0cmFpbmluZyBhbmQgdmFsaWRhdGlvbi4gICANCg0KIyBSIERlbW8NCg0KIyMgRGF0YSBQcmVwICANCldlJ2xsIHVzZSB0aGUgW1BpbWFJbmRpYW5zRGlhYmV0ZXMyXShodHRwczovL3JkcnIuaW8vY3Jhbi9tbGJlbmNoL21hbi9QaW1hSW5kaWFuc0RpYWJldGVzLmh0bWwpIGRhdGFzZXQgZnJvbSB0aGUgYG1sYmVuY2hgIHBhY2thZ2UuDQoNCmBgYHtyfQ0KIyBJbnN0YWxsIG5lY2Vzc2FyeSBwYWNrYWdlcyAoaWYgbm90IGFscmVhZHkgaW5zdGFsbGVkKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJtbGJlbmNoIikNCiMgaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKSAjIEZvciBjcm9zcy12YWxpZGF0aW9uIGFuZCBjb252ZW5pZW50IG1vZGVsIHRyYWluaW5nDQojIGluc3RhbGwucGFja2FnZXMoIk1BU1MiKSAgIyBGb3IgTERBIGFuZCBRREENCg0KIyBMb2FkIHBhY2thZ2VzDQpsaWJyYXJ5KG1sYmVuY2gpICAgIyBGb3IgUGltYUluZGlhbnNEaWFiZXRlczIgZGF0YXNldA0KbGlicmFyeShjYXJldCkgICAgICMgRm9yIGNyb3NzLXZhbGlkYXRpb24gYW5kIG1vZGVsIHRyYWluaW5nIHV0aWxpdGllcw0KbGlicmFyeShNQVNTKSAgICAgICMgRm9yIGxkYSgpIGFuZCBxZGEoKSBmdW5jdGlvbnMNCg0KIyBMb2FkIHRoZSBkYXRhc2V0DQpkYXRhKFBpbWFJbmRpYW5zRGlhYmV0ZXMyKQ0KbXlfZGF0YSA8LSBQaW1hSW5kaWFuc0RpYWJldGVzMg0KDQojIEV4cGxvcmUgdGhlIGRhdGENCmhlYWQobXlfZGF0YSkNCnN1bW1hcnkobXlfZGF0YSkNCnN0cihteV9kYXRhKQ0KDQojIENoZWNrIGZvciBtaXNzaW5nIHZhbHVlcw0KY29sU3Vtcyhpcy5uYShteV9kYXRhKSkNCg0KIyBIYW5kbGUgbWlzc2luZyB2YWx1ZXM6IEZvciBzaW1wbGljaXR5IGluIHRoaXMgZGVtbywgd2UnbGwgcmVtb3ZlIHJvd3Mgd2l0aCBOQS4NCiMgSW4gYSByZWFsLXdvcmxkIHNjZW5hcmlvLCBpbXB1dGF0aW9uIHdvdWxkIGJlIGEgYmV0dGVyIGFwcHJvYWNoIGZvciBtaXNzaW5nIGRhdGEuDQpteV9kYXRhIDwtIG5hLm9taXQobXlfZGF0YSkNCmNvbFN1bXMoaXMubmEobXlfZGF0YSkpICMgVmVyaWZ5IG5vIG1vcmUgTkFzDQoNCiMgQ29udmVydCAnZGlhYmV0ZXMnIHRvIGEgZmFjdG9yIHdpdGggZGVzaXJlZCBsZXZlbHMgaWYgbm90IGFscmVhZHkNCiMgVGhlICdvdXRjb21lJyB2YXJpYWJsZSBuZWVkcyB0byBiZSBhIGZhY3RvciBmb3IgY2xhc3NpZmljYXRpb24gbW9kZWxzIGluIFIuDQojIExldCdzIGVuc3VyZSB0aGUgbGV2ZWxzIGFyZSBtZWFuaW5nZnVsLg0KbXlfZGF0YSRkaWFiZXRlcyA8LSBmYWN0b3IobXlfZGF0YSRkaWFiZXRlcywgbGV2ZWxzID0gYygibmVnIiwgInBvcyIpLCBsYWJlbHMgPSBjKCJObyIsICJZZXMiKSkNCg0KIyBFbnN1cmUgb3RoZXIgdmFyaWFibGVzIGFyZSBudW1lcmljIHdoZXJlIGFwcHJvcHJpYXRlDQojIChQaW1hSW5kaWFuc0RpYWJldGVzMiBpcyBnZW5lcmFsbHkgY2xlYW4sIGJ1dCBnb29kIHByYWN0aWNlIHRvIGNoZWNrKQ0KYGBgDQoNCiAtIFdoYXQgZGlkIHdlIGp1c3QgZG8gd2l0aCBgbmEub21pdCgpYD8gV2hhdCBhcmUgdGhlIGltcGxpY2F0aW9ucz8gIA0KIC0gV2h5IGlzIGl0IGltcG9ydGFudCBmb3Igb3VyIG91dGNvbWUgdmFyaWFibGUgKGBkaWFiZXRlc2ApIHRvIGJlIGEgZmFjdG9yPyAgDQogLSBXaGF0IGFyZSBzb21lIG90aGVyIHN0cmF0ZWdpZXMgZm9yIGhhbmRsaW5nIG1pc3NpbmcgZGF0YSBiZXNpZGVzIGBuYS5vbWl0KClgPyAoSW1wdXRhdGlvbiwgZXRjLikgIA0KDQojIyBNb2RlbCBGaXR0aW5nIGFuZCBFdmFsdWF0aW9uIHdpdGggQ3Jvc3MtVmFsaWRhdGlvbiANCk5vdyB0aGF0IG91ciBkYXRhIGlzIHJlYWR5LCBsZXQncyBmaXQgb3VyIHRocmVlIG1vZGVscyBhbmQgZXZhbHVhdGUgdGhlbSB1c2luZyBrLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbi4gV2UnbGwgdXNlIHRoZSBjYXJldCBwYWNrYWdlLCB3aGljaCBzaW1wbGlmaWVzIHRoZSBjcm9zcy12YWxpZGF0aW9uIHByb2Nlc3Mgc2lnbmlmaWNhbnRseS4NCg0KIyMjIFNldHRpbmcgdXAgQ3Jvc3MtVmFsaWRhdGlvbnMgQ29udHJvbCAgDQpgYGB7cn0NCiMgU2V0IGEgcmFuZG9tIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQ0Kc2V0LnNlZWQoMTIzKQ0KDQojIERlZmluZSBjcm9zcy12YWxpZGF0aW9uIGNvbnRyb2wNCiMgV2UnbGwgdXNlIDEwLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbg0KIyAncmVwZWF0cyA9IDMnIG1lYW5zIHRoZSBlbnRpcmUgMTAtZm9sZCBDViBwcm9jZXNzIGlzIHJlcGVhdGVkIDMgdGltZXMuDQojIFRoaXMgZ2l2ZXMgYW4gZXZlbiBtb3JlIHN0YWJsZSBlc3RpbWF0ZSBvZiBwZXJmb3JtYW5jZS4NCnRyYWluX2NvbnRyb2wgPC0gdHJhaW5Db250cm9sKA0KICBtZXRob2QgPSAicmVwZWF0ZWRjdiIsDQogIG51bWJlciA9IDEwLCAgICAgICAjIGsgPSAxMCBmb2xkcw0KICByZXBlYXRzID0gMywgICAgICAgIyBSZXBlYXQgdGhlIDEwLWZvbGQgQ1YgMyB0aW1lcw0KICBjbGFzc1Byb2JzID0gVFJVRSwgIyBOZWVkZWQgZm9yIFJPQyBjdXJ2ZSBsYXRlciBpZiBkZXNpcmVkDQogIHN1bW1hcnlGdW5jdGlvbiA9IHR3b0NsYXNzU3VtbWFyeSAjIEZvciBjbGFzc2lmaWNhdGlvbiwgcHJvdmlkZXMgQVVDLCBzZW5zaXRpdml0eSwgc3BlY2lmaWNpdHkNCikNCmBgYA0KDQogLSBXaHkgYHNldC5zZWVkKClgPyAgDQogLSBXaGF0J3MgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBgbnVtYmVyYCBhbmQgYHJlcGVhdHNgIGluIGB0cmFpbkNvbnRyb2xgPyAgDQogLSBXaGF0IG90aGVyIG1ldGhvZCBvcHRpb25zIGFyZSBhdmFpbGFibGUgaW4gYHRyYWluQ29udHJvbGA/IChlLmcuLCBgYm9vdGAsIGBsb29jdmApICAgIA0KIA0KIyMjIExvZ2lzdGljIFJlZ3Jlc3Npb24gKGdsbSkNCmBgYHtyfQ0KIyBGaXQgTG9naXN0aWMgUmVncmVzc2lvbiBtb2RlbCB1c2luZyBjYXJldCdzIHRyYWluIGZ1bmN0aW9uDQojIFRoZSAnbWV0aG9kID0gImdsbSInIHNwZWNpZmllcyBhIGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbCAobG9naXN0aWMgcmVncmVzc2lvbiBpbiB0aGlzIGNhc2UpDQojIFRoZSAnZmFtaWx5ID0gImJpbm9taWFsIicgaXMgaW1wbGljaXRseSBoYW5kbGVkIGJ5IGNhcmV0IGZvciBmYWN0b3Igb3V0Y29tZXMuDQojICdtZXRyaWMgPSAiUk9DIicgdGVsbHMgY2FyZXQgdG8gb3B0aW1pemUvcmVwb3J0IGJhc2VkIG9uIEFyZWEgVW5kZXIgdGhlIFJPQyBDdXJ2ZS4NCiMgVGhpcyBpcyBhIGdvb2QgbWV0cmljIGZvciBpbWJhbGFuY2VkIGNsYXNzZXMuDQoNCmNhdCgiXG4tLS0gRml0dGluZyBMb2dpc3RpYyBSZWdyZXNzaW9uIE1vZGVsIC0tLVxuIikNCm1vZGVsX2dsbSA8LSB0cmFpbigNCiAgZGlhYmV0ZXMgfiAuLCAgICAgICAgICMgUHJlZGljdCAnZGlhYmV0ZXMnIHVzaW5nIGFsbCBvdGhlciB2YXJpYWJsZXMNCiAgZGF0YSA9IG15X2RhdGEsICAgICAgICMgT3VyIHByZS1wcm9jZXNzZWQgZGF0YXNldA0KICBtZXRob2QgPSAiZ2xtIiwgICAgICAgIyBTcGVjaWZ5IExvZ2lzdGljIFJlZ3Jlc3Npb24NCiAgZmFtaWx5ID0gImJpbm9taWFsIiwgICMgRm9yIGJpbmFyeSBjbGFzc2lmaWNhdGlvbg0KICB0ckNvbnRyb2wgPSB0cmFpbl9jb250cm9sLCAjIE91ciBkZWZpbmVkIENWIGNvbnRyb2wNCiAgbWV0cmljID0gIlJPQyIgICAgICAgICMgRXZhbHVhdGUgdXNpbmcgUk9DIEFVQw0KKQ0KDQojIFByaW50IHRoZSBtb2RlbCByZXN1bHRzDQpwcmludChtb2RlbF9nbG0pDQoNCiMgQWNjZXNzIGluZGl2aWR1YWwgZm9sZHMnIHBlcmZvcm1hbmNlIChvcHRpb25hbCwgZm9yIGRlZXBlciBpbnNpZ2h0KQ0KIyBtb2RlbF9nbG0kcmVzYW1wbGUNCmBgYA0KIyMjIExpbmVhciBEaXNjcmltaW5hbnQgQW5hbHlzaXMgKExEQSkNCmBgYHtyfQ0KIyBGaXQgTERBIG1vZGVsIHVzaW5nIGNhcmV0J3MgdHJhaW4gZnVuY3Rpb24NCiMgJ21ldGhvZCA9ICJsZGEiJyBmb3IgTGluZWFyIERpc2NyaW1pbmFudCBBbmFseXNpcw0KDQpjYXQoIlxuLS0tIEZpdHRpbmcgTERBIE1vZGVsIC0tLVxuIikNCm1vZGVsX2xkYSA8LSB0cmFpbigNCiAgZGlhYmV0ZXMgfiAuLA0KICBkYXRhID0gbXlfZGF0YSwNCiAgbWV0aG9kID0gImxkYSIsDQogIHRyQ29udHJvbCA9IHRyYWluX2NvbnRyb2wsDQogIG1ldHJpYyA9ICJST0MiDQopDQoNCiMgUHJpbnQgdGhlIG1vZGVsIHJlc3VsdHMNCnByaW50KG1vZGVsX2xkYSkNCmBgYA0KIyMjIFF1YWRyYXRpYyBEaXNjcmltaW5hbnQgKFFEQSkNCmBgYHtyfQ0KIyBGaXQgUURBIG1vZGVsIHVzaW5nIGNhcmV0J3MgdHJhaW4gZnVuY3Rpb24NCiMgJ21ldGhvZCA9ICJxZGEiJyBmb3IgUXVhZHJhdGljIERpc2NyaW1pbmFudCBBbmFseXNpcw0KDQpjYXQoIlxuLS0tIEZpdHRpbmcgUURBIE1vZGVsIC0tLVxuIikNCm1vZGVsX3FkYSA8LSB0cmFpbigNCiAgZGlhYmV0ZXMgfiAuLA0KICBkYXRhID0gbXlfZGF0YSwNCiAgbWV0aG9kID0gInFkYSIsDQogIHRyQ29udHJvbCA9IHRyYWluX2NvbnRyb2wsDQogIG1ldHJpYyA9ICJST0MiDQopDQoNCiMgUHJpbnQgdGhlIG1vZGVsIHJlc3VsdHMNCnByaW50KG1vZGVsX3FkYSkNCmBgYA0KDQojIyMgQ29tcGFyaW5nIE1vZGVscw0KYGBge3J9DQojIENvbXBhcmUgdGhlIG1vZGVscyB1c2luZyByZXNhbXBsZXMNCiMgVGhpcyBwcm92aWRlcyBhIHN0YXRpc3RpY2FsIGNvbXBhcmlzb24gb2YgcGVyZm9ybWFuY2UgbWV0cmljcyBhY3Jvc3MgbW9kZWxzLg0KcmVzYW1wbGVfcmVzdWx0cyA8LSByZXNhbXBsZXMobGlzdCgNCiAgTG9naXN0aWMgPSBtb2RlbF9nbG0sDQogIExEQSA9IG1vZGVsX2xkYSwNCiAgUURBID0gbW9kZWxfcWRhDQopKQ0KDQojIFN1bW1hcml6ZSB0aGUgY29tcGFyaXNvbg0Kc3VtbWFyeShyZXNhbXBsZV9yZXN1bHRzKQ0KDQojIFZpc3VhbGl6ZSB0aGUgY29tcGFyaXNvbiAoZS5nLiwgYm94IHBsb3RzIG9mIFJPQywgU2Vuc2l0aXZpdHksIFNwZWNpZmljaXR5KQ0Kc2NhbGVzIDwtIGxpc3QoeCA9IGxpc3QocmVsYXRpb24gPSAiZnJlZSIpLCB5ID0gbGlzdChyZWxhdGlvbiA9ICJmcmVlIikpDQpid3Bsb3QocmVzYW1wbGVfcmVzdWx0cywgc2NhbGVzID0gc2NhbGVzKQ0KDQojIFlvdSBjYW4gYWxzbyB1c2UgYSBkb3RwbG90IGZvciBhIGRpZmZlcmVudCB2aXN1YWxpemF0aW9uDQpkb3RwbG90KHJlc2FtcGxlX3Jlc3VsdHMsIHNjYWxlcyA9IHNjYWxlcykNCmBgYA0KDQogLSBMb29raW5nIGF0IHRoZSBvdXRwdXQgb2YgYHByaW50KG1vZGVsX2dsbSlgLCB3aGF0IGRvIHRoZSAnUk9DJywgJ1NlbnMnLCBhbmQgJ1NwZWMnIHZhbHVlcyByZXByZXNlbnQ/IChBcmVhIFVuZGVyIHRoZSBST0MgQ3VydmUsIFNlbnNpdGl2aXR5L1JlY2FsbCwgU3BlY2lmaWNpdHkpLiAgDQogLSBIb3cgZG8gdGhlIFJPQyB2YWx1ZXMgY29tcGFyZSBhY3Jvc3MgdGhlIHRocmVlIG1vZGVscz8gV2hpY2ggb25lIHBlcmZvcm1zIGJlc3Qgb24gYXZlcmFnZSBhY2NvcmRpbmcgdG8gdGhpcyBtZXRyaWM/ICANCiAtIFdoYXQgZG9lcyB0aGUgYGJ3cGxvdGAgb3IgYGRvdHBsb3RgIHRlbGwgdXMgYWJvdXQgdGhlIHZhcmlhYmlsaXR5IG9mIHRoZSBtb2RlbCdzIHBlcmZvcm1hbmNlIGFjcm9zcyB0aGUgZm9sZHM/ICANCiAtIEJhc2VkIG9uIHRoZXNlIHJlc3VsdHMsIHdoaWNoIG1vZGVsIHdvdWxkIHlvdSByZWNvbW1lbmQgZm9yIHRoaXMgZGF0YXNldCBhbmQgd2h5PyAoRW5jb3VyYWdlIGRpc2N1c3Npb24gb24gdHJhZGUtb2ZmcywgaW50ZXJwcmV0YWJpbGl0eSwgYW5kIGFzc3VtcHRpb25zKS4gIA0KIC0gSWYgYSBtb2RlbCBoYXMgYSBtdWNoIGhpZ2hlciBST0MgYnV0IGxvd2VyIFNlbnMgY29tcGFyZWQgdG8gYW5vdGhlciwgd2hhdCBtaWdodCB0aGF0IGltcGx5IGFib3V0IGl0cyBwZXJmb3JtYW5jZSBvbiBkaWZmZXJlbnQgdHlwZXMgb2YgZXJyb3JzIChmYWxzZSBwb3NpdGl2ZXMgdnMuIGZhbHNlIG5lZ2F0aXZlcyk/ICANCg0KIyMgUHJlZGljdGlvbnMgYW5kIEludGVycHJldGF0aW9uICANCmBjYXJldGAgaGFuZGxlcyBtdWNoIG9mIHRoZSBjb21wbGV4aXR5LCBpdCdzIGltcG9ydGFudCB0byB1bmRlcnN0YW5kIGhvdyB0byBtYWtlIHByZWRpY3Rpb25zIHdpdGggeW91ciB0cmFpbmVkIG1vZGVscyBhbmQgaW50ZXJwcmV0IHRoZWlyIGNvZWZmaWNpZW50cy4gIA0KDQojIyMgTWFraW5nIFByZWRpY3Rpb25zDQpgYGB7cn0NCiMgTGV0J3MgdXNlIHRoZSBiZXN0IHBlcmZvcm1pbmcgbW9kZWwgKG9yIHBpY2sgb25lIGZvciBkZW1vbnN0cmF0aW9uKQ0KIyBGb3IgZXhhbXBsZSwgbGV0J3MgdXNlIHRoZSBMb2dpc3RpYyBSZWdyZXNzaW9uIG1vZGVsIGZvciBwcmVkaWN0aW9uLg0KIyBXZSdsbCBwcmVkaWN0IG9uIHRoZSBvcmlnaW5hbCB0cmFpbmluZyBkYXRhIGZvciBzaW1wbGljaXR5LA0KIyBidXQgaW4gYSByZWFsIHNjZW5hcmlvLCB5b3UnZCB1c2UgYSBjb21wbGV0ZWx5IHVuc2VlbiB0ZXN0IHNldC4NCg0KIyBQcmVkaWN0IGNsYXNzIGxhYmVscw0KcHJlZGljdGlvbnNfZ2xtIDwtIHByZWRpY3QobW9kZWxfZ2xtLCBuZXdkYXRhID0gbXlfZGF0YSkNCmhlYWQocHJlZGljdGlvbnNfZ2xtKQ0KDQojIFByZWRpY3QgY2xhc3MgcHJvYmFiaWxpdGllcw0KcHJvYmFiaWxpdGllc19nbG0gPC0gcHJlZGljdChtb2RlbF9nbG0sIG5ld2RhdGEgPSBteV9kYXRhLCB0eXBlID0gInByb2IiKQ0KaGVhZChwcm9iYWJpbGl0aWVzX2dsbSkNCg0KIyBDcmVhdGUgYSBjb25mdXNpb24gbWF0cml4IGZvciB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBvbiB0cmFpbmluZyBkYXRhDQojIFRoaXMgaXMgTk9UIGNyb3NzLXZhbGlkYXRlZCBwZXJmb3JtYW5jZSwgYnV0IGdvb2QgZm9yIHVuZGVyc3RhbmRpbmcuDQpjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnNfZ2xtLCBteV9kYXRhJGRpYWJldGVzKQ0KYGBgDQoNCiMjIyBJbnRlcnByZXRpbmcgQ29lZmZpY2llbnRzIChMb2dpc3RpYyBSZWdyZXNzaW9uKQ0KYGBge3J9DQojIEZvciBMb2dpc3RpYyBSZWdyZXNzaW9uLCB3ZSBjYW4gaW5zcGVjdCB0aGUgY29lZmZpY2llbnRzDQojIE5vdGU6IEZvciBMREEvUURBLCBpbnRlcnByZXRhdGlvbiBpcyBtb3JlIGFib3V0IGRpc2NyaW1pbmFudCBmdW5jdGlvbnMsDQojIG5vdCBkaXJlY3QgY29lZmZpY2llbnQgaW50ZXJwcmV0YXRpb24gbGlrZSBHTE0uDQoNCiMgQWNjZXNzIHRoZSBmaXR0ZWQgR0xNIG1vZGVsIG9iamVjdCBmcm9tIHRoZSBjYXJldCB0cmFpbiBvYmplY3QNCmdsbV9vYmplY3QgPC0gbW9kZWxfZ2xtJGZpbmFsTW9kZWwNCnN1bW1hcnkoZ2xtX29iamVjdCkNCg0KIyBJbnRlcnByZXQgY29lZmZpY2llbnRzIChlLmcuLCBvZGRzIHJhdGlvcykNCiMgRm9yIGEgY29lZmZpY2llbnQgJ2InLCB0aGUgb2RkcyByYXRpbyBpcyBleHAoYikNCiMgRXhhbXBsZTogRm9yICdnbHVjb3NlJywgaWYgdGhlIGNvZWZmaWNpZW50IGlzICd4JywgdGhlbiBmb3IgZXZlcnkgdW5pdCBpbmNyZWFzZSBpbiBnbHVjb3NlLA0KIyB0aGUgb2RkcyBvZiBoYXZpbmcgZGlhYmV0ZXMgYXJlIG11bHRpcGxpZWQgYnkgZXhwKHgpLCBhc3N1bWluZyBvdGhlciBwcmVkaWN0b3JzIGFyZSBjb25zdGFudC4NCmV4cChjb2VmKGdsbV9vYmplY3QpKQ0KYGBgDQoNCiAtIFdoYXQncyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGB0eXBlID0gInJhdyJgIChkZWZhdWx0IHByZWRpY3Qgb3V0cHV0KSBhbmQgYHR5cGUgPSAicHJvYiJgPyAgIA0KIC0gV2h5IGlzIGludGVycHJldGluZyBjb2VmZmljaWVudHMgbW9yZSBzdHJhaWdodGZvcndhcmQgZm9yIExvZ2lzdGljIFJlZ3Jlc3Npb24gdGhhbiBmb3IgTERBL1FEQT8gIA0KIC0gV2hhdCBkb2VzIGFuIG9kZHMgcmF0aW8gbGVzcyB0aGFuIDEgbWVhbj8gR3JlYXRlciB0aGFuIDE/ICANCiANCiMjIFJlY2FwIGFuZCBBZHZhbmNlZCBUb3BpY3MgIA0KIC0gTG9naXN0aWMgUmVncmVzc2lvbjogTGluZWFyIGRlY2lzaW9uIGJvdW5kYXJ5LCBwcmVkaWN0cyBwcm9iYWJpbGl0aWVzLiAgDQogLSBMREE6IExpbmVhciBkZWNpc2lvbiBib3VuZGFyeSwgYXNzdW1lcyBlcXVhbCBjb3ZhcmlhbmNlIG1hdHJpY2VzLiAgDQogLSBRREE6IFF1YWRyYXRpYyBkZWNpc2lvbiBib3VuZGFyeSwgYXNzdW1lcyB1bmVxdWFsIGNvdmFyaWFuY2UgbWF0cmljZXMuICANCiAtIENyb3NzLXZhbGlkYXRpb246IEVzc2VudGlhbCBmb3IgcmVsaWFibGUgbW9kZWwgcGVyZm9ybWFuY2UgZXN0aW1hdGlvbi4gIA0KDQojIyMgRnVydGhlciBFeHBsb3JhdGlvbiANCiAtIF9fSHlwZXJwYXJhbWV0ZXIgVHVuaW5nOl9fIEZvciBtb3JlIGNvbXBsZXggbW9kZWxzIChsaWtlIFJhbmRvbSBGb3Jlc3RzLCBTVk1zKSwgYGNhcmV0YCBjYW4gYWxzbyB0dW5lIGh5cGVycGFyYW1ldGVycyBhdXRvbWF0aWNhbGx5IGR1cmluZyBjcm9zcy12YWxpZGF0aW9uLiBFeHBsb3JlIHR1bmVHcmlkIGFuZCB0dW5lTGVuZ3RoIGFyZ3VtZW50cyBpbiBgdHJhaW4oKWAuIA0KIC0gX19PdGhlciBNZXRyaWNzOl9fIEludmVzdGlnYXRlIG90aGVyIGV2YWx1YXRpb24gbWV0cmljcyBsaWtlIFByZWNpc2lvbiwgUmVjYWxsLCBGMS1zY29yZSwgZXNwZWNpYWxseSBmb3IgaW1iYWxhbmNlZCBkYXRhc2V0cy4gIA0KIC0gX19JbWJhbGFuY2VkIERhdGE6X18gVGhlIGBQaW1hSW5kaWFuc0RpYWJldGVzMmAgZGF0YXNldCBoYXMgc29tZSBjbGFzcyBpbWJhbGFuY2UuIFJlc2VhcmNoIHRlY2huaXF1ZXMgZm9yIGhhbmRsaW5nIGltYmFsYW5jZWQgZGF0YSAoZS5nLiwgU01PVEUsIHVuZGVyc2FtcGxpbmcvb3ZlcnNhbXBsaW5nKSBhbmQgaG93IHRoZXkgaW50ZWdyYXRlIHdpdGggY2FyZXQuICANCiAtIF9fRmVhdHVyZSBFbmdpbmVlcmluZy9TZWxlY3Rpb246X18gSG93IHdvdWxkIGZlYXR1cmUgZW5naW5lZXJpbmcgb3IgZmVhdHVyZSBzZWxlY3Rpb24gaW1wYWN0IHRoZXNlIG1vZGVscz8gIA0KIC0gX19WaXN1YWxpemF0aW9uOl9fIENyZWF0ZSBST0MgY3VydmVzIGZvciBlYWNoIG1vZGVsIHRvIHZpc3VhbGl6ZSB0aGVpciB0cmFkZS1vZmZzIGJldHdlZW4gc2Vuc2l0aXZpdHkgYW5kIHNwZWNpZmljaXR5LiAgDQogDQojIEFwcGVuZGl4IA0KDQojIyBST0MgQ3VydmUgUGxvdCAocmVxdWlyZXMgYHBST0NgIHBhY2thZ2Ugb3IgbWFudWFsIGNhbGN1bGF0aW9uKQ0KDQpgYGB7cn0NCiMgaW5zdGFsbC5wYWNrYWdlcygicFJPQyIpDQpsaWJyYXJ5KHBST0MpDQoNCiMgR2V0IHByZWRpY3Rpb25zIGZyb20gb25lIG9mIHRoZSBtb2RlbHMgKGUuZy4sIExvZ2lzdGljIFJlZ3Jlc3Npb24pDQojIE5vdGU6IFRoaXMgaXMgb24gdGhlIHRyYWluaW5nIGRhdGEsIGZvciBpbGx1c3RyYXRpb24uIEZvciB0cnVlIFJPQywgdXNlIGEgaGVsZC1vdXQgdGVzdCBzZXQuDQpwcm9iX2dsbSA8LSBwcmVkaWN0KG1vZGVsX2dsbSwgbmV3ZGF0YSA9IG15X2RhdGEsIHR5cGUgPSAicHJvYiIpDQoNCiMgQ3JlYXRlIFJPQyBvYmplY3QNCnJvY19nbG0gPC0gcm9jKHJlc3BvbnNlID0gbXlfZGF0YSRkaWFiZXRlcywgcHJlZGljdG9yID0gcHJvYl9nbG0kWWVzLCBsZXZlbHMgPSBjKCJObyIsICJZZXMiKSkNCg0KIyBQbG90IFJPQyBjdXJ2ZQ0KcGxvdChyb2NfZ2xtLCBtYWluID0gIlJPQyBDdXJ2ZSBmb3IgTG9naXN0aWMgUmVncmVzc2lvbiIsIGNvbCA9ICJibHVlIiwgbHdkID0gMikNCmF1Yyhyb2NfZ2xtKSAjIEFyZWEgVW5kZXIgQ3VydmUNCg0KIyBZb3UgY291bGQgbG9vcCB0aGlzIGZvciBhbGwgbW9kZWxzIGFuZCBwbG90IG9uIHRoZSBzYW1lIGdyYXBoIGZvciBjb21wYXJpc29uDQpgYGANCg0KDQojIyBXaXRob3V0IGBjYXJldGAgIA0KSXQncyBjZXJ0YWlubHkgcG9zc2libGUgdG8gcGVyZm9ybSBjcm9zcy12YWxpZGF0aW9uIGFuZCBmaXQgdGhlc2UgbW9kZWxzIHdpdGhvdXQgYGNhcmV0YC4gV2hpbGUgYGNhcmV0YCBzaW1wbGlmaWVzIHRoZSBwcm9jZXNzIHNpZ25pZmljYW50bHksIGRvaW5nIGl0IG1hbnVhbGx5IHByb3ZpZGVzIGEgZGVlcGVyIHVuZGVyc3RhbmRpbmcgb2YgdGhlIHVuZGVybHlpbmcgbWVjaGFuaWNzLg0KDQpIZXJlJ3MgdGhlIHNhbWUgZGVtbyB3aXRob3V0IHVzaW5nIHRoZSBgY2FyZXRgIHBhY2thZ2UuIFRoaXMgdmVyc2lvbiBmb2N1c2VzIG1vcmUgb24gbWFudWFsIGxvb3BzIGZvciBjcm9zcy12YWxpZGF0aW9uIGFuZCBkaXJlY3QgY2FsbHMgdG8gYGdsbWAsIGBsZGFgLCBhbmQgYHFkYWAuDQoNCiMjIE1hbnVhbCBLLUZvbGQgQ3Jvc3MtVmFsaWRhdGlvbiBTZXR1cA0KYGBge3J9DQojIFNldCBhIHJhbmRvbSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCnNldC5zZWVkKDEyMykNCg0KIyBEZWZpbmUgbnVtYmVyIG9mIGZvbGRzIChrKQ0Ka19mb2xkcyA8LSAxMA0KbnVtX3JlcGVhdHMgPC0gMyAjIFdlJ2xsIHJlcGVhdCB0aGUgZW50aXJlIENWIHByb2Nlc3MgbXVsdGlwbGUgdGltZXMgZm9yIG1vcmUgc3RhYmxlIHJlc3VsdHMNCg0KIyBDcmVhdGUgZW1wdHkgbGlzdHMgdG8gc3RvcmUgcmVzdWx0cyBmb3IgZWFjaCBtb2RlbCBhbmQgcmVwZWF0DQpyZXN1bHRzX2dsbSA8LSBsaXN0KCkNCnJlc3VsdHNfbGRhIDwtIGxpc3QoKQ0KcmVzdWx0c19xZGEgPC0gbGlzdCgpDQoNCiMgU3RvcmUgcGVyZm9ybWFuY2UgbWV0cmljcyAoUk9DLCBTZW5zaXRpdml0eSwgU3BlY2lmaWNpdHkpIGZvciBlYWNoIGZvbGQvcmVwZWF0DQptZXRyaWNzX2dsbSA8LSBkYXRhLmZyYW1lKFJPQyA9IG51bWVyaWMoKSwgU2Vuc2l0aXZpdHkgPSBudW1lcmljKCksIFNwZWNpZmljaXR5ID0gbnVtZXJpYygpKQ0KbWV0cmljc19sZGEgPC0gZGF0YS5mcmFtZShST0MgPSBudW1lcmljKCksIFNlbnNpdGl2aXR5ID0gbnVtZXJpYygpLCBTcGVjaWZpY2l0eSA9IG51bWVyaWMoKSkNCm1ldHJpY3NfcWRhIDwtIGRhdGEuZnJhbWUoUk9DID0gbnVtZXJpYygpLCBTZW5zaXRpdml0eSA9IG51bWVyaWMoKSwgU3BlY2lmaWNpdHkgPSBudW1lcmljKCkpDQoNCiMgR2V0IHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zDQpuX29icyA8LSBucm93KG15X2RhdGEpDQoNCiMgT3V0ZXIgbG9vcCBmb3IgcmVwZWF0cw0KZm9yIChyZXAgaW4gMTpudW1fcmVwZWF0cykgew0KICBjYXQocGFzdGUwKCJcbi0tLSBDcm9zcy1WYWxpZGF0aW9uIFJlcGVhdCAiLCByZXAsICIgLS0tXG4iKSkNCg0KICAjIFJhbmRvbWx5IHBlcm11dGUgdGhlIGRhdGEgdG8gZW5zdXJlIGZvbGRzIGFyZSBkaXZlcnNlDQogIHNodWZmbGVkX2RhdGEgPC0gbXlfZGF0YVtzYW1wbGUobl9vYnMpLCBdDQoNCiAgIyBDcmVhdGUgZm9sZHMNCiAgIyBUaGlzIGNyZWF0ZXMgYSB2ZWN0b3IgaW5kaWNhdGluZyB3aGljaCBmb2xkIGVhY2ggcm93IGJlbG9uZ3MgdG8NCiAgZm9sZHMgPC0gY3V0KHNlcSgxLCBuX29icyksIGJyZWFrcyA9IGtfZm9sZHMsIGxhYmVscyA9IEZBTFNFKQ0KDQogICMgSW5uZXIgbG9vcCBmb3IgZm9sZHMNCiAgZm9yIChpIGluIDE6a19mb2xkcykgew0KICAgIGNhdChwYXN0ZTAoIiAgRm9sZCAiLCBpLCAiLi4uXG4iKSkNCg0KICAgICMgRGVmaW5lIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMgZm9yIHRoaXMgZm9sZA0KICAgIHRlc3RfaW5kaWNlcyA8LSB3aGljaChmb2xkcyA9PSBpKQ0KICAgIHRyYWluX2RhdGEgPC0gc2h1ZmZsZWRfZGF0YVstdGVzdF9pbmRpY2VzLCBdDQogICAgdGVzdF9kYXRhIDwtIHNodWZmbGVkX2RhdGFbdGVzdF9pbmRpY2VzLCBdDQoNCiAgICAjIC0tLSBGaXQgTG9naXN0aWMgUmVncmVzc2lvbiAtLS0NCiAgICB0cnlDYXRjaCh7ICMgVXNlIHRyeUNhdGNoIHRvIGhhbmRsZSBwb3RlbnRpYWwgZXJyb3JzIChlLmcuLCBwZXJmZWN0IHNlcGFyYXRpb24pDQogICAgICBtb2RlbF9nbG1fZm9sZCA8LSBnbG0oZGlhYmV0ZXMgfiAuLCBkYXRhID0gdHJhaW5fZGF0YSwgZmFtaWx5ID0gImJpbm9taWFsIikNCiAgICAgIHByZWRfcHJvYl9nbG0gPC0gcHJlZGljdChtb2RlbF9nbG1fZm9sZCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSwgdHlwZSA9ICJyZXNwb25zZSIpDQogICAgICBwcmVkX2NsYXNzX2dsbSA8LSBmYWN0b3IoaWZlbHNlKHByZWRfcHJvYl9nbG0gPiAwLjUsICJZZXMiLCAiTm8iKSwgbGV2ZWxzID0gYygiTm8iLCAiWWVzIikpDQoNCiAgICAgICMgRW5zdXJlIHRlc3RfZGF0YSRkaWFiZXRlcyBoYXMgY29uc2lzdGVudCBsZXZlbHMNCiAgICAgIHRlc3RfZGF0YSRkaWFiZXRlcyA8LSBmYWN0b3IodGVzdF9kYXRhJGRpYWJldGVzLCBsZXZlbHMgPSBjKCJObyIsICJZZXMiKSkNCg0KICAgICAgIyBDYWxjdWxhdGUgY29uZnVzaW9uIG1hdHJpeCBmb3IgR0xNDQogICAgICBjbV9nbG0gPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZF9jbGFzc19nbG0sIEFjdHVhbCA9IHRlc3RfZGF0YSRkaWFiZXRlcykNCiAgICAgICMgQ2FsY3VsYXRlIG1ldHJpY3MgbWFudWFsbHkgb3IgdXNpbmcgcFJPQyBmb3IgQVVDDQogICAgICAjIE5lZWQgdG8gaGFuZGxlIGNhc2VzIHdoZXJlIGNsYXNzZXMgbWlnaHQgYmUgbWlzc2luZyBpbiBhIGZvbGQNCiAgICAgIGlmIChuY29sKGNtX2dsbSkgPT0gMiAmJiBucm93KGNtX2dsbSkgPT0gMikgew0KICAgICAgICAgIFRQX2dsbSA8LSBjbV9nbG1bMiwyXQ0KICAgICAgICAgIFROX2dsbSA8LSBjbV9nbG1bMSwxXQ0KICAgICAgICAgIEZQX2dsbSA8LSBjbV9nbG1bMiwxXQ0KICAgICAgICAgIEZOX2dsbSA8LSBjbV9nbG1bMSwyXQ0KDQogICAgICAgICAgc2Vuc2l0aXZpdHlfZ2xtIDwtIFRQX2dsbSAvIChUUF9nbG0gKyBGTl9nbG0pDQogICAgICAgICAgc3BlY2lmaWNpdHlfZ2xtIDwtIFROX2dsbSAvIChUTl9nbG0gKyBGUF9nbG0pDQoNCiAgICAgICAgICAjIENhbGN1bGF0ZSBST0MgQVVDIChyZXF1aXJlcyBhdCBsZWFzdCB0d28gZGlzdGluY3QgcHJlZGljdGlvbnMgYW5kIHR3byBjbGFzc2VzIGluIHRlc3Qgc2V0KQ0KICAgICAgICAgIGlmIChsZW5ndGgodW5pcXVlKHRlc3RfZGF0YSRkaWFiZXRlcykpID4gMSAmJiBsZW5ndGgodW5pcXVlKHByZWRfcHJvYl9nbG0pKSA+IDEpIHsNCiAgICAgICAgICAgICAgcm9jX29ial9nbG0gPC0gcm9jKHJlc3BvbnNlID0gdGVzdF9kYXRhJGRpYWJldGVzLCBwcmVkaWN0b3IgPSBwcmVkX3Byb2JfZ2xtLCBsZXZlbHMgPSBjKCJObyIsICJZZXMiKSwgcXVpZXQgPSBUUlVFKQ0KICAgICAgICAgICAgICBhdWNfZ2xtIDwtIGF1Yyhyb2Nfb2JqX2dsbSkNCiAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgICBhdWNfZ2xtIDwtIE5BICMgQ2Fubm90IGNvbXB1dGUgQVVDIGlmIG9ubHkgb25lIGNsYXNzIG9yIGNvbnN0YW50IHByZWRpY3Rpb25zDQogICAgICAgICAgfQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgICBzZW5zaXRpdml0eV9nbG0gPC0gTkENCiAgICAgICAgICBzcGVjaWZpY2l0eV9nbG0gPC0gTkENCiAgICAgICAgICBhdWNfZ2xtIDwtIE5BDQogICAgICB9DQoNCiAgICAgIG1ldHJpY3NfZ2xtIDwtIHJiaW5kKG1ldHJpY3NfZ2xtLCBkYXRhLmZyYW1lKFJPQyA9IGF1Y19nbG0sIFNlbnNpdGl2aXR5ID0gc2Vuc2l0aXZpdHlfZ2xtLCBTcGVjaWZpY2l0eSA9IHNwZWNpZmljaXR5X2dsbSkpDQoNCiAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICAgIGNhdCgiICBHTE0gRXJyb3IgaW4gZm9sZCAiLCBpLCAiOiAiLCBlJG1lc3NhZ2UsICJcbiIpDQogICAgICBtZXRyaWNzX2dsbSA8LSByYmluZChtZXRyaWNzX2dsbSwgZGF0YS5mcmFtZShST0MgPSBOQSwgU2Vuc2l0aXZpdHkgPSBOQSwgU3BlY2lmaWNpdHkgPSBOQSkpDQogICAgfSkNCg0KDQogICAgIyAtLS0gRml0IExEQSAtLS0NCiAgICB0cnlDYXRjaCh7DQogICAgICAjIEVuc3VyZSB0aGVyZSBhcmUgYXQgbGVhc3QgdHdvIGRpc3RpbmN0IGNsYXNzZXMgaW4gdGhlIHRyYWluaW5nIGRhdGEgZm9yIExEQQ0KICAgICAgaWYgKGxlbmd0aCh1bmlxdWUodHJhaW5fZGF0YSRkaWFiZXRlcykpID4gMSkgew0KICAgICAgICBtb2RlbF9sZGFfZm9sZCA8LSBsZGEoZGlhYmV0ZXMgfiAuLCBkYXRhID0gdHJhaW5fZGF0YSkNCiAgICAgICAgcHJlZF9sZGEgPC0gcHJlZGljdChtb2RlbF9sZGFfZm9sZCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkNCiAgICAgICAgcHJlZF9wcm9iX2xkYV95ZXMgPC0gcHJlZF9sZGEkcG9zdGVyaW9yWywgIlllcyJdICMgUHJvYmFiaWxpdHkgb2YgJ1llcycgY2xhc3MNCiAgICAgICAgcHJlZF9jbGFzc19sZGEgPC0gcHJlZF9sZGEkY2xhc3MNCg0KICAgICAgICBjbV9sZGEgPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZF9jbGFzc19sZGEsIEFjdHVhbCA9IHRlc3RfZGF0YSRkaWFiZXRlcykNCiAgICAgICAgaWYgKG5jb2woY21fbGRhKSA9PSAyICYmIG5yb3coY21fbGRhKSA9PSAyKSB7DQogICAgICAgICAgICBUUF9sZGEgPC0gY21fbGRhWzIsMl0NCiAgICAgICAgICAgIFROX2xkYSA8LSBjbV9sZGFbMSwxXQ0KICAgICAgICAgICAgRlBfbGRhIDwtIGNtX2xkYVsyLDFdDQogICAgICAgICAgICBGTl9sZGEgPC0gY21fbGRhWzEsMl0NCg0KICAgICAgICAgICAgc2Vuc2l0aXZpdHlfbGRhIDwtIFRQX2xkYSAvIChUUF9sZGEgKyBGTl9sZGEpDQogICAgICAgICAgICBzcGVjaWZpY2l0eV9sZGEgPC0gVE5fbGRhIC8gKFROX2xkYSArIEZQX2xkYSkNCg0KICAgICAgICAgICAgaWYgKGxlbmd0aCh1bmlxdWUodGVzdF9kYXRhJGRpYWJldGVzKSkgPiAxICYmIGxlbmd0aCh1bmlxdWUocHJlZF9wcm9iX2xkYV95ZXMpKSA+IDEpIHsNCiAgICAgICAgICAgICAgICByb2Nfb2JqX2xkYSA8LSByb2MocmVzcG9uc2UgPSB0ZXN0X2RhdGEkZGlhYmV0ZXMsIHByZWRpY3RvciA9IHByZWRfcHJvYl9sZGFfeWVzLCBsZXZlbHMgPSBjKCJObyIsICJZZXMiKSwgcXVpZXQgPSBUUlVFKQ0KICAgICAgICAgICAgICAgIGF1Y19sZGEgPC0gYXVjKHJvY19vYmpfbGRhKQ0KICAgICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgICAgICBhdWNfbGRhIDwtIE5BDQogICAgICAgICAgICB9DQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgICBzZW5zaXRpdml0eV9sZGEgPC0gTkENCiAgICAgICAgICAgIHNwZWNpZmljaXR5X2xkYSA8LSBOQQ0KICAgICAgICAgICAgYXVjX2xkYSA8LSBOQQ0KICAgICAgICB9DQogICAgICB9IGVsc2Ugew0KICAgICAgICAgIHNlbnNpdGl2aXR5X2xkYSA8LSBOQQ0KICAgICAgICAgIHNwZWNpZmljaXR5X2xkYSA8LSBOQQ0KICAgICAgICAgIGF1Y19sZGEgPC0gTkENCiAgICAgICAgICBjYXQoIiAgTERBIHNraXBwZWQgaW4gZm9sZCAiLCBpLCAiOiBPbmx5IG9uZSBjbGFzcyBwcmVzZW50IGluIHRyYWluaW5nIGRhdGEuXG4iKQ0KICAgICAgfQ0KICAgICAgbWV0cmljc19sZGEgPC0gcmJpbmQobWV0cmljc19sZGEsIGRhdGEuZnJhbWUoUk9DID0gYXVjX2xkYSwgU2Vuc2l0aXZpdHkgPSBzZW5zaXRpdml0eV9sZGEsIFNwZWNpZmljaXR5ID0gc3BlY2lmaWNpdHlfbGRhKSkNCg0KICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICAgICAgY2F0KCIgIExEQSBFcnJvciBpbiBmb2xkICIsIGksICI6ICIsIGUkbWVzc2FnZSwgIlxuIikNCiAgICAgIG1ldHJpY3NfbGRhIDwtIHJiaW5kKG1ldHJpY3NfbGRhLCBkYXRhLmZyYW1lKFJPQyA9IE5BLCBTZW5zaXRpdml0eSA9IE5BLCBTcGVjaWZpY2l0eSA9IE5BKSkNCiAgICB9KQ0KDQoNCiAgICAjIC0tLSBGaXQgUURBIC0tLQ0KICAgIHRyeUNhdGNoKHsNCiAgICAgICMgUURBIG9mdGVuIHJlcXVpcmVzIG1vcmUgZGF0YSBwZXIgY2xhc3MsIGNhbiBmYWlsIGlmIG5vdCBlbm91Z2ggb2JzZXJ2YXRpb25zDQogICAgICBpZiAobGVuZ3RoKHVuaXF1ZSh0cmFpbl9kYXRhJGRpYWJldGVzKSkgPiAxICYmDQogICAgICAgICAgc3VtKHRyYWluX2RhdGEkZGlhYmV0ZXMgPT0gIk5vIikgPiBuY29sKHRyYWluX2RhdGEpICYmDQogICAgICAgICAgc3VtKHRyYWluX2RhdGEkZGlhYmV0ZXMgPT0gIlllcyIpID4gbmNvbCh0cmFpbl9kYXRhKSkgeyAjIFJ1bGUgb2YgdGh1bWIgZm9yIFFEQQ0KICAgICAgICBtb2RlbF9xZGFfZm9sZCA8LSBxZGEoZGlhYmV0ZXMgfiAuLCBkYXRhID0gdHJhaW5fZGF0YSkNCiAgICAgICAgcHJlZF9xZGEgPC0gcHJlZGljdChtb2RlbF9xZGFfZm9sZCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkNCiAgICAgICAgcHJlZF9wcm9iX3FkYV95ZXMgPC0gcHJlZF9xZGEkcG9zdGVyaW9yWywgIlllcyJdDQogICAgICAgIHByZWRfY2xhc3NfcWRhIDwtIHByZWRfcWRhJGNsYXNzDQoNCiAgICAgICAgY21fcWRhIDwtIHRhYmxlKFByZWRpY3RlZCA9IHByZWRfY2xhc3NfcWRhLCBBY3R1YWwgPSB0ZXN0X2RhdGEkZGlhYmV0ZXMpDQogICAgICAgIGlmIChuY29sKGNtX3FkYSkgPT0gMiAmJiBucm93KGNtX3FkYSkgPT0gMikgew0KICAgICAgICAgICAgVFBfcWRhIDwtIGNtX3FkYVsyLDJdDQogICAgICAgICAgICBUTl9xZGEgPC0gY21fcWRhWzEsMV0NCiAgICAgICAgICAgIEZQX3FkYSA8LSBjbV9xZGFbMiwxXQ0KICAgICAgICAgICAgRk5fcWRhIDwtIGNtX3FkYVsxLDJdDQoNCiAgICAgICAgICAgIHNlbnNpdGl2aXR5X3FkYSA8LSBUUF9xZGEgLyAoVFBfcWRhICsgRk5fcWRhKQ0KICAgICAgICAgICAgc3BlY2lmaWNpdHlfcWRhIDwtIFROX3FkYSAvIChUTl9xZGEgKyBGUF9xZGEpDQoNCiAgICAgICAgICAgIGlmIChsZW5ndGgodW5pcXVlKHRlc3RfZGF0YSRkaWFiZXRlcykpID4gMSAmJiBsZW5ndGgodW5pcXVlKHByZWRfcHJvYl9xZGFfeWVzKSkgPiAxKSB7DQogICAgICAgICAgICAgICAgcm9jX29ial9xZGEgPC0gcm9jKHJlc3BvbnNlID0gdGVzdF9kYXRhJGRpYWJldGVzLCBwcmVkaWN0b3IgPSBwcmVkX3Byb2JfcWRhX3llcywgbGV2ZWxzID0gYygiTm8iLCAiWWVzIiksIHF1aWV0ID0gVFJVRSkNCiAgICAgICAgICAgICAgICBhdWNfcWRhIDwtIGF1Yyhyb2Nfb2JqX3FkYSkNCiAgICAgICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgICAgICAgYXVjX3FkYSA8LSBOQQ0KICAgICAgICAgICAgfQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgc2Vuc2l0aXZpdHlfcWRhIDwtIE5BDQogICAgICAgICAgICBzcGVjaWZpY2l0eV9xZGEgPC0gTkENCiAgICAgICAgICAgIGF1Y19xZGEgPC0gTkENCiAgICAgICAgfQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgICBzZW5zaXRpdml0eV9xZGEgPC0gTkENCiAgICAgICAgICBzcGVjaWZpY2l0eV9xZGEgPC0gTkENCiAgICAgICAgICBhdWNfcWRhIDwtIE5BDQogICAgICAgICAgY2F0KCIgIFFEQSBza2lwcGVkIGluIGZvbGQgIiwgaSwgIjogTm90IGVub3VnaCBvYnNlcnZhdGlvbnMgcGVyIGNsYXNzIGluIHRyYWluaW5nIGRhdGEuXG4iKQ0KICAgICAgfQ0KICAgICAgbWV0cmljc19xZGEgPC0gcmJpbmQobWV0cmljc19xZGEsIGRhdGEuZnJhbWUoUk9DID0gYXVjX3FkYSwgU2Vuc2l0aXZpdHkgPSBzZW5zaXRpdml0eV9xZGEsIFNwZWNpZmljaXR5ID0gc3BlY2lmaWNpdHlfcWRhKSkNCg0KICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICAgICAgY2F0KCIgIFFEQSBFcnJvciBpbiBmb2xkICIsIGksICI6ICIsIGUkbWVzc2FnZSwgIlxuIikNCiAgICAgIG1ldHJpY3NfcWRhIDwtIHJiaW5kKG1ldHJpY3NfcWRhLCBkYXRhLmZyYW1lKFJPQyA9IE5BLCBTZW5zaXRpdml0eSA9IE5BLCBTcGVjaWZpY2l0eSA9IE5BKSkNCiAgICB9KQ0KICB9ICMgRW5kIG9mIGlubmVyIGZvbGQgbG9vcA0KfSAjIEVuZCBvZiBvdXRlciByZXBlYXQgbG9vcA0KYGBgDQoNCiMjIFN1bW1hcml6aW5nIGFuZCBDb21wYXJpbmcgUmVzdWx0cyAgDQpBZnRlciBydW5uaW5nIGFsbCB0aGUgZm9sZHMgYW5kIHJlcGVhdHMsIHdlJ2xsIGhhdmUgYSBjb2xsZWN0aW9uIG9mIHBlcmZvcm1hbmNlIG1ldHJpY3MuIExldCdzIHN1bW1hcml6ZSB0aGVtIGFuZCBjb21wYXJlIG91ciBtb2RlbHMuDQoNCmBgYHtyfQ0KIyBTdW1tYXJpemUgdGhlIGNyb3NzLXZhbGlkYXRpb24gcmVzdWx0cw0KY2F0KCJcbi0tLSBTdW1tYXJ5IG9mIExvZ2lzdGljIFJlZ3Jlc3Npb24gUGVyZm9ybWFuY2UgLS0tXG4iKQ0Kc3VtbWFyeShtZXRyaWNzX2dsbSkNCmNhdCgiQXZlcmFnZSBST0M6ICIsIG1lYW4obWV0cmljc19nbG0kUk9DLCBuYS5ybSA9IFRSVUUpLCAiXG4iKQ0KY2F0KCJBdmVyYWdlIFNlbnNpdGl2aXR5OiAiLCBtZWFuKG1ldHJpY3NfZ2xtJFNlbnNpdGl2aXR5LCBuYS5ybSA9IFRSVUUpLCAiXG4iKQ0KY2F0KCJBdmVyYWdlIFNwZWNpZmljaXR5OiAiLCBtZWFuKG1ldHJpY3NfZ2xtJFNwZWNpZmljaXR5LCBuYS5ybSA9IFRSVUUpLCAiXG4iKQ0KDQpjYXQoIlxuLS0tIFN1bW1hcnkgb2YgTERBIFBlcmZvcm1hbmNlIC0tLVxuIikNCnN1bW1hcnkobWV0cmljc19sZGEpDQpjYXQoIkF2ZXJhZ2UgUk9DOiAiLCBtZWFuKG1ldHJpY3NfbGRhJFJPQywgbmEucm0gPSBUUlVFKSwgIlxuIikNCmNhdCgiQXZlcmFnZSBTZW5zaXRpdml0eTogIiwgbWVhbihtZXRyaWNzX2xkYSRTZW5zaXRpdml0eSwgbmEucm0gPSBUUlVFKSwgIlxuIikNCmNhdCgiQXZlcmFnZSBTcGVjaWZpY2l0eTogIiwgbWVhbihtZXRyaWNzX2xkYSRTcGVjaWZpY2l0eSwgbmEucm0gPSBUUlVFKSwgIlxuIikNCg0KY2F0KCJcbi0tLSBTdW1tYXJ5IG9mIFFEQSBQZXJmb3JtYW5jZSAtLS1cbiIpDQpzdW1tYXJ5KG1ldHJpY3NfcWRhKQ0KY2F0KCJBdmVyYWdlIFJPQzogIiwgbWVhbihtZXRyaWNzX3FkYSRST0MsIG5hLnJtID0gVFJVRSksICJcbiIpDQpjYXQoIkF2ZXJhZ2UgU2Vuc2l0aXZpdHk6ICIsIG1lYW4obWV0cmljc19xZGEkU2Vuc2l0aXZpdHksIG5hLnJtID0gVFJVRSksICJcbiIpDQpjYXQoIkF2ZXJhZ2UgU3BlY2lmaWNpdHk6ICIsIG1lYW4obWV0cmljc19xZGEkU3BlY2lmaWNpdHksIG5hLnJtID0gVFJVRSksICJcbiIpDQoNCiMgQ29tYmluZSByZXN1bHRzIGZvciBlYXNpZXIgY29tcGFyaXNvbiBhbmQgdmlzdWFsaXphdGlvbg0KYWxsX21ldHJpY3MgPC0gcmJpbmQoDQogIGRhdGEuZnJhbWUoTW9kZWwgPSAiTG9naXN0aWMiLCBtZXRyaWNzX2dsbSksDQogIGRhdGEuZnJhbWUoTW9kZWwgPSAiTERBIiwgbWV0cmljc19sZGEpLA0KICBkYXRhLmZyYW1lKE1vZGVsID0gIlFEQSIsIG1ldHJpY3NfcWRhKQ0KKQ0KDQojIFJlbW92ZSByb3dzIHdpdGggTkEgdmFsdWVzIChmcm9tIGZhaWxlZCBmb2xkcykgZm9yIHBsb3R0aW5nDQphbGxfbWV0cmljc19jbGVhbiA8LSBuYS5vbWl0KGFsbF9tZXRyaWNzKQ0KDQojIC0tLSBWaXN1YWxpemF0aW9uIChyZXF1aXJlcyBnZ3Bsb3QyLCBub3QgbG9hZGVkIGJ5IGRlZmF1bHQgaGVyZSwgYnV0IGdvb2QgZm9yIGRpc2N1c3Npb24pIC0tLQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikNCmxpYnJhcnkoZ2dwbG90MikNCg0KIyBCb3hwbG90IG9mIFJPQyB2YWx1ZXMNCmdncGxvdChhbGxfbWV0cmljc19jbGVhbiwgYWVzKHggPSBNb2RlbCwgeSA9IFJPQywgZmlsbCA9IE1vZGVsKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGxhYnModGl0bGUgPSAiTW9kZWwgUGVyZm9ybWFuY2UgQ29tcGFyaXNvbiAoUk9DKSIsDQogICAgICAgeSA9ICJST0MgQVVDIiwgeCA9ICJNb2RlbCIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCiMgQm94cGxvdCBvZiBTZW5zaXRpdml0eSB2YWx1ZXMNCmdncGxvdChhbGxfbWV0cmljc19jbGVhbiwgYWVzKHggPSBNb2RlbCwgeSA9IFNlbnNpdGl2aXR5LCBmaWxsID0gTW9kZWwpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgbGFicyh0aXRsZSA9ICJNb2RlbCBQZXJmb3JtYW5jZSBDb21wYXJpc29uIChTZW5zaXRpdml0eSkiLA0KICAgICAgIHkgPSAiU2Vuc2l0aXZpdHkiLCB4ID0gIk1vZGVsIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KIyBCb3hwbG90IG9mIFNwZWNpZmljaXR5IHZhbHVlcw0KZ2dwbG90KGFsbF9tZXRyaWNzX2NsZWFuLCBhZXMoeCA9IE1vZGVsLCB5ID0gU3BlY2lmaWNpdHksIGZpbGwgPSBNb2RlbCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBsYWJzKHRpdGxlID0gIk1vZGVsIFBlcmZvcm1hbmNlIENvbXBhcmlzb24gKFNwZWNpZmljaXR5KSIsDQogICAgICAgeSA9ICJTcGVjaWZpY2l0eSIsIHggPSAiTW9kZWwiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQpIZXJlJ3MgYSBzdW1tYXJ5IG9mIHRoZSBhdmVyYWdlIHBlcmZvcm1hbmNlIG1ldHJpY3MgZm9yIGVhY2ggbW9kZWw6DQoNCnwgKipNb2RlbCoqICAgICAgICAgICAgICAgfCAqKkF2ZXJhZ2UgUk9DKiogfCAqKkF2ZXJhZ2UgU2Vuc2l0aXZpdHkqKiB8ICoqQXZlcmFnZSBTcGVjaWZpY2l0eSoqIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS06fDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tOnwNCnwgKipMb2dpc3RpYyBSZWdyZXNzaW9uKiogfCAqKl8wLjgzNzlfKiogICAgfCAwLjU1MzQgICAgICAgICAgICAgICAgICB8ICoqXzAuODg3N18qKiAgICAgICAgICAgIHwNCnwgKipMREEqKiAgICAgICAgICAgICAgICAgfCAwLjgzNzQgICAgICAgICAgfCAwLjU1MjEgICAgICAgICAgICAgICAgICB8IDAuODgzMSAgICAgICAgICAgICAgICAgIHwNCnwgKipRREEqKiAgICAgICAgICAgICAgICAgfCAwLjgyMzUgICAgICAgICAgfCAqKl8wLjU3MjlfKiogICAgICAgICAgICB8IDAuODU0MyAgICAgICAgICAgICAgICAgIHwNCg0KX19PdmVyYWxsIFBlcmZvcm1hbmNlIChST0MgQVVDKTpfXyAgDQoNCiAtIExvZ2lzdGljIFJlZ3Jlc3Npb24gKDAuODM3OSkgYW5kIExEQSAoMC44Mzc0KSBoYXZlIGFsbW9zdCBpZGVudGljYWwgYXZlcmFnZSBST0MgQVVDcy4gVGhlIGRpZmZlcmVuY2UgaXMgbmVnbGlnaWJsZS4NCiAtIFFEQSAoMC44MjM1KSBoYXMgYSBzbGlnaHRseSBsb3dlciBhdmVyYWdlIFJPQyBBVUMgY29tcGFyZWQgdG8gTG9naXN0aWMgUmVncmVzc2lvbiBhbmQgTERBLiAgDQogLSBJbnRlcnByZXRhdGlvbjogQmFzZWQgcHVyZWx5IG9uIG92ZXJhbGwgZGlzY3JpbWluYXRpdmUgcG93ZXIgYWNyb3NzIGFsbCBwb3NzaWJsZSB0aHJlc2hvbGRzLCBMb2dpc3RpYyBSZWdyZXNzaW9uIGFuZCBMREEgYXJlIHBlcmZvcm1pbmcgZXF1YWxseSB3ZWxsIGFuZCBzbGlnaHRseSBiZXR0ZXIgdGhhbiBRREEuIEFuIEFVQyBhYm92ZSAwLjggaXMgZ2VuZXJhbGx5IGNvbnNpZGVyZWQgZ29vZCwgaW5kaWNhdGluZyB0aGF0IHRoZSBtb2RlbHMgaGF2ZSBzdHJvbmcgZGlzY3JpbWluYXRvcnkgcG93ZXIuICANCg0KX19TZW5zaXRpdml0eSAoVHJ1ZSBQb3NpdGl2ZSBSYXRlKTpfXyAgDQoNCiAtIFFEQSAoMC41NzI5KSBoYXMgdGhlIGhpZ2hlc3QgYXZlcmFnZSBTZW5zaXRpdml0eS4gIA0KIC0gTG9naXN0aWMgUmVncmVzc2lvbiAoMC41NTM0KSBhbmQgTERBICgwLjU1MjEpIGhhdmUgdmVyeSBzaW1pbGFyIGFuZCBzbGlnaHRseSBsb3dlciBhdmVyYWdlIFNlbnNpdGl2aXRpZXMgY29tcGFyZWQgdG8gUURBLiAgDQogLSBJbnRlcnByZXRhdGlvbjogUURBIGlzIHNsaWdodGx5IGJldHRlciBhdCBjb3JyZWN0bHkgaWRlbnRpZnlpbmcgYWN0dWFsIHBvc2l0aXZlIGNhc2VzIChlLmcuLCBwZW9wbGUgd2l0aCBkaWFiZXRlcykuIFRoaXMgbWVhbnMgaXQgaGFzIGZld2VyIGZhbHNlIG5lZ2F0aXZlcy4gIA0KIA0KX19TcGVjaWZpY2l0eSAoVHJ1ZSBOZWdhdGl2ZSBSYXRlKTpfXyAgDQoNCiAtIExvZ2lzdGljIFJlZ3Jlc3Npb24gKDAuODg3NykgaGFzIHRoZSBoaWdoZXN0IGF2ZXJhZ2UgU3BlY2lmaWNpdHkuDQogLSBMREEgKDAuODgzMSkgaXMgdmVyeSBjbG9zZSB0byBMb2dpc3RpYyBSZWdyZXNzaW9uLg0KIC0gUURBICgwLjg1NDMpIGhhcyBhIGxvd2VyIGF2ZXJhZ2UgU3BlY2lmaWNpdHkgY29tcGFyZWQgdG8gTG9naXN0aWMgUmVncmVzc2lvbiBhbmQgTERBLg0KIC0gTG9naXN0aWMgUmVncmVzc2lvbiBpcyBiZXN0IGF0IGNvcnJlY3RseSBpZGVudGlmeWluZyBhY3R1YWwgbmVnYXRpdmUgY2FzZXMgKGUuZy4sIHBlb3BsZSB3aXRob3V0IGRpYWJldGVzKS4gVGhpcyBtZWFucyBpdCBoYXMgZmV3ZXIgZmFsc2UgcG9zaXRpdmVzLg0KDQpfX1doaWNoIG9uZSBpcyB0aGUgImJlc3QiP19fICANCg0KVGhlICJiZXN0IiBtb2RlbCBkZXBlbmRzIG9uIHRoZSAqc3BlY2lmaWMgZ29hbHMgYW5kIGNvc3RzKiBhc3NvY2lhdGVkIHdpdGggRmFsc2UgUG9zaXRpdmVzIGFuZCBGYWxzZSBOZWdhdGl2ZXMgaW4geW91ciBhcHBsaWNhdGlvbi4gIA0KDQogLSBJZiBvdmVyYWxsIGRpc2NyaW1pbmF0aXZlIHBvd2VyIChjb25zaWRlcmluZyBhbGwgdGhyZXNob2xkcykgaXMgcGFyYW1vdW50LCBhbmQgeW91IGRvbid0IGhhdmUgYSBzdHJvbmcgcHJlZmVyZW5jZSBmb3IgbWluaW1pemluZyBvbmUgdHlwZSBvZiBlcnJvciBvdmVyIGFub3RoZXI6ICANCiAgIC0gTG9naXN0aWMgUmVncmVzc2lvbiBvciBMREEgYXJlIHRoZSB0b3AgY29udGVuZGVycywgYXMgdGhlaXIgYXZlcmFnZSBST0MgQVVDcyBhcmUgdmlydHVhbGx5IGlkZW50aWNhbCBhbmQgdGhlIGhpZ2hlc3QuIFRoZXJlJ3Mgbm90IGVub3VnaCBkaWZmZXJlbmNlIGhlcmUgdG8gZGVjbGFyZSBvbmUgZGVmaW5pdGl2ZWx5IGJldHRlciB0aGFuIHRoZSBvdGhlciBiYXNlZCBzb2xlbHkgb24gdGhpcyBtZXRyaWMuICANCiAtIElmIG1pbmltaXppbmcgRmFsc2UgTmVnYXRpdmVzIChjYXRjaGluZyBhcyBtYW55IHBvc2l0aXZlIGNhc2VzIGFzIHBvc3NpYmxlKSBpcyBtb3JlIGNyaXRpY2FsIChpLmUuLCBoaWdoIFNlbnNpdGl2aXR5IGlzIHByZWZlcnJlZCk6ICANCiAgIC0gUURBIG1pZ2h0IGJlIGNvbnNpZGVyZWQgc2xpZ2h0bHkgYmV0dGVyIGR1ZSB0byBpdHMgbWFyZ2luYWxseSBoaWdoZXIgYXZlcmFnZSBTZW5zaXRpdml0eSAoMC41NzI5KS4gSG93ZXZlciwga2VlcCBpbiBtaW5kIHRoYXQgdGhpcyBjb21lcyBhdCB0aGUgY29zdCBvZiBsb3dlciBTcGVjaWZpY2l0eS4gIA0KIC0gSWYgbWluaW1pemluZyBGYWxzZSBQb3NpdGl2ZXMgKGNvcnJlY3RseSBpZGVudGlmeWluZyBuZWdhdGl2ZSBjYXNlcyBhbmQgYXZvaWRpbmcgZmFsc2UgYWxhcm1zKSBpcyBtb3JlIGNyaXRpY2FsIChpLmUuLCBoaWdoIFNwZWNpZmljaXR5IGlzIHByZWZlcnJlZCk6ICANCiAgIC0gTG9naXN0aWMgUmVncmVzc2lvbiAob3IgdmVyeSBjbG9zZWx5LCBMREEpIGlzIGNsZWFybHkgdGhlIHdpbm5lci4gSXQgaGFzIHRoZSBoaWdoZXN0IGF2ZXJhZ2UgU3BlY2lmaWNpdHksIG1lYW5pbmcgaXQncyBsZXNzIGxpa2VseSB0byBpbmNvcnJlY3RseSBjbGFzc2lmeSBhIG5lZ2F0aXZlIGNhc2UgYXMgcG9zaXRpdmUuICAgDQoNCl9fQmFzZWQgb24gdGhlc2UgcmVzdWx0czpfXyAgDQoNCiAtIEZvciBvdmVyYWxsIGJhbGFuY2VkIHBlcmZvcm1hbmNlIChoaWdoZXN0IFJPQyBBVUMpIGFuZCBzdHJvbmdlc3QgY29udHJvbCBvdmVyIEZhbHNlIFBvc2l0aXZlcyAoaGlnaGVzdCBTcGVjaWZpY2l0eSksIExvZ2lzdGljIFJlZ3Jlc3Npb24gaXMgbWFyZ2luYWxseSB0aGUgImJlc3QsIiBjbG9zZWx5IGZvbGxvd2VkIGJ5IExEQS4gIA0KIC0gSWYgeW91IHByaW9yaXRpemUgc2xpZ2h0bHkgaGlnaGVyIFNlbnNpdGl2aXR5IChjYXRjaGluZyBtb3JlIHRydWUgcG9zaXRpdmVzKSwgUURBIG9mZmVycyBhIHNtYWxsIGFkdmFudGFnZSwgYnV0IGF0IHRoZSBjb3N0IG9mIGxvd2VyIFNwZWNpZmljaXR5IGFuZCBhIHNsaWdodGx5IGxvd2VyIG92ZXJhbGwgUk9DLiAgDQoNCkxvZ2lzdGljIFJlZ3Jlc3Npb24gYXBwZWFycyB0byBiZSB0aGUgbW9zdCByb2J1c3QgY2hvaWNlIGFtb25nIHRoZXNlIHRocmVlIG1vZGVscyBmb3IgdGhpcyBkYXRhc2V0LiBUaGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBMb2dpc3RpYyBSZWdyZXNzaW9uIGFuZCBMREEgYXJlIHNvIHNtYWxsIHRoYXQgdGhleSBjb3VsZCBiZSBjb25zaWRlcmVkIGVmZmVjdGl2ZWx5IGVxdWl2YWxlbnQuIFlvdSdkIGxpa2VseSBjaG9vc2UgYmV0d2VlbiB0aGVtIGJhc2VkIG9uIGVhc2Ugb2YgaW1wbGVtZW50YXRpb24sIGludGVycHJldGFiaWxpdHksIG9yIG1pbm9yIHRoZW9yZXRpY2FsIHByZWZlcmVuY2VzLg0KDQojIyBIb3cgdG8gaW50ZXJwcmV0IFJPQywgU2Vuc2l0aXZpdHksIGFuZCBTcGVjaWZpY2l0eQ0KDQpST0MsIFNlbnNpdGl2aXR5LCBhbmQgU3BlY2lmaWNpdHkgYXJlIHRocmVlIG1ldHJpY3MgZm9yIGV2YWx1YXRpbmcgdGhlIHBlcmZvcm1hbmNlIG9mIGNsYXNzaWZpY2F0aW9uIG1vZGVscywgZXNwZWNpYWxseSBpbiBzaXR1YXRpb25zIHdoZXJlIHlvdSBjYXJlIGFib3V0IGRpZmZlcmVudCB0eXBlcyBvZiBlcnJvcnMuIFRvIHVuZGVyc3RhbmQgdGhlc2UsIGZpcnN0IHVuZGVyc3RhbmQgdGhlIGZvdXIgYmFzaWMgb3V0Y29tZXMgaW4gYSBiaW5hcnkgY2xhc3NpZmljYXRpb24gcHJvYmxlbSwgb2Z0ZW4gcmVwcmVzZW50ZWQgaW4gYSBjb25mdXNpb24gbWF0cml4OiAgDQoNCnwgICAgICAgICAgICAgICAgICAgICAgICB8ICoqQWN0dWFsIFBvc2l0aXZlKiogfCAqKkFjdHVhbCBOZWdhdGl2ZSoqIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLTp8DQp8ICoqUHJlZGljdGVkIFBvc2l0aXZlKiogfCBUcnVlIFBvc2l0aXZlIChUUCkgIHwgRmFsc2UgUG9zaXRpdmUgKEZQKSB8DQp8ICoqUHJlZGljdGVkIE5lZ2F0aXZlKiogfCBGYWxzZSBOZWdhdGl2ZSAoRk4pIHwgVHJ1ZSBOZWdhdGl2ZSAoVE4pICB8ICANCg0KDQogLSBfX1RydWUgUG9zaXRpdmUgKFRQKTpfXyBUaGUgbW9kZWwgY29ycmVjdGx5IHByZWRpY3RlZCBhIHBvc2l0aXZlIG91dGNvbWUgKGUuZy4sIHByZWRpY3RlZCAiaGFzIGRpYWJldGVzLCIgYW5kIHRoZSBwZXJzb24gYWN0dWFsbHkgaGFzIGRpYWJldGVzKS4gIA0KIC0gX19GYWxzZSBQb3NpdGl2ZSAoRlApOl9fIFRoZSBtb2RlbCBpbmNvcnJlY3RseSBwcmVkaWN0ZWQgYSBwb3NpdGl2ZSBvdXRjb21lIChlLmcuLCBwcmVkaWN0ZWQgImhhcyBkaWFiZXRlcywiIGJ1dCB0aGUgcGVyc29uIGFjdHVhbGx5IGRvZXMgbm90IGhhdmUgZGlhYmV0ZXMpLiBUaGlzIGlzIGFsc28ga25vd24gYXMgYSBUeXBlIEkgZXJyb3Igb3IgYSAiZmFsc2UgYWxhcm0uIiAgDQogLSBfX0ZhbHNlIE5lZ2F0aXZlIChGTik6X18gVGhlIG1vZGVsIGluY29ycmVjdGx5IHByZWRpY3RlZCBhIG5lZ2F0aXZlIG91dGNvbWUgKGUuZy4sIHByZWRpY3RlZCAiZG9lcyBub3QgaGF2ZSBkaWFiZXRlcywiIGJ1dCB0aGUgcGVyc29uIGFjdHVhbGx5IGhhcyBkaWFiZXRlcykuIFRoaXMgaXMgYWxzbyBrbm93biBhcyBhIFR5cGUgSUkgZXJyb3Igb3IgYSAibWlzcy4iICANCiAtIF9fVHJ1ZSBOZWdhdGl2ZSAoVE4pOl9fIFRoZSBtb2RlbCBjb3JyZWN0bHkgcHJlZGljdGVkIGEgbmVnYXRpdmUgb3V0Y29tZSAoZS5nLiwgcHJlZGljdGVkICJkb2VzIG5vdCBoYXZlIGRpYWJldGVzLCIgYW5kIHRoZSBwZXJzb24gYWN0dWFsbHkgZG9lcyBub3QgaGF2ZSBkaWFiZXRlcykuICANCiANCiMjIyBTZW5zaXRpdml0eSAoUmVjYWxsLCBUcnVlIFBvc2l0aXZlIFJhdGUgLSBUUFIpICANClNlbnNpdGl2aXR5IG1lYXN1cmVzIHRoZSBwcm9wb3J0aW9uIG9mIGFjdHVhbCBwb3NpdGl2ZSBjYXNlcyB0aGF0IHdlcmUgY29ycmVjdGx5IGlkZW50aWZpZWQgYnkgdGhlIG1vZGVsLiBJdCBhbnN3ZXJzIHRoZSBxdWVzdGlvbjogIk91dCBvZiBhbGwgdGhlIHBlb3BsZSB3aG8gdHJ1bHkgaGF2ZSB0aGUgY29uZGl0aW9uLCBob3cgbWFueSBkaWQgb3VyIG1vZGVsIGNvcnJlY3RseSBmaW5kPyIgIA0KDQokU2Vuc2l0aXZpdHkgPSBcZnJhY3tUUH17VFArRk59JA0KDQogLSBfX0hpZ2ggU2Vuc2l0aXZpdHkgKGNsb3NlciB0byAxLjApOl9fIE1lYW5zIHRoZSBtb2RlbCBpcyB2ZXJ5IGdvb2QgYXQgY2F0Y2hpbmcgcG9zaXRpdmUgY2FzZXMuIEl0IGhhcyBhIGxvdyByYXRlIG9mIEZhbHNlIE5lZ2F0aXZlcyAobWlzc2luZyBhY3R1YWwgcG9zaXRpdmVzKS4gIA0KIC0gX19Mb3cgU2Vuc2l0aXZpdHkgKGNsb3NlciB0byAwLjApOl9fIE1lYW5zIHRoZSBtb2RlbCBtaXNzZXMgbWFueSBhY3R1YWwgcG9zaXRpdmUgY2FzZXMsIGxlYWRpbmcgdG8gYSBoaWdoIHJhdGUgb2YgRmFsc2UgTmVnYXRpdmVzLiAgDQogDQpfX1NlbnNpdGl2aXR5IGlzIGNydWNpYWwgd2hlbiB0aGUgY29zdCBvZiBhIEZhbHNlIE5lZ2F0aXZlIGlzIGhpZ2guX18gIA0KDQogLSBfX01lZGljYWwgRGlhZ25vc2lzIChlLmcuLCBkZXRlY3RpbmcgYSBzZXJpb3VzIGRpc2Vhc2UgbGlrZSBjYW5jZXIpOl9fIFlvdSB3YW50IHZlcnkgaGlnaCBzZW5zaXRpdml0eSBiZWNhdXNlIG1pc3NpbmcgYSBkaXNlYXNlIChmYWxzZSBuZWdhdGl2ZSkgY2FuIGhhdmUgc2V2ZXJlIGNvbnNlcXVlbmNlcyBmb3IgdGhlIHBhdGllbnQuIFlvdSdkIHJhdGhlciBoYXZlIHNvbWUgZmFsc2UgYWxhcm1zIChmYWxzZSBwb3NpdGl2ZXMpIHRoYXQgY2FuIGJlIGZ1cnRoZXIgaW52ZXN0aWdhdGVkLiAgDQogLSBfX1NlY3VyaXR5IFN5c3RlbXM6X18gWW91IHdhbnQgdG8gZGV0ZWN0IGFsbCBpbnRydWRlcnMgKGhpZ2ggc2Vuc2l0aXZpdHkpLCBldmVuIGlmIGl0IG1lYW5zIHNvbWUgZmFsc2UgYWxhcm1zLiAgDQogLSBfX0ZyYXVkIERldGVjdGlvbjpfXyBZb3Ugd2FudCB0byBjYXRjaCBhcyBtYW55IGZyYXVkdWxlbnQgdHJhbnNhY3Rpb25zIGFzIHBvc3NpYmxlLiAgDQoNCiMjIyMgU05OT1VUIChTZW5zaXRpdmUgTmVnYXRpdmUgPSBSdWxlIE9VVCkgIA0KSWYgYSBoaWdobHkgc2Vuc2l0aXZlIHRlc3QgY29tZXMgYmFjayBuZWdhdGl2ZSwgeW91IGNhbiBjb25maWRlbnRseSBydWxlIG91dCB0aGUgZGlzZWFzZS9jb25kaXRpb24uDQoNCiMjIyBTcGVjaWZpY2l0eSAoVHJ1ZSBOZWdhdGl2ZSBSYXRlIC0gVE5SKSAgDQpTcGVjaWZpY2l0eSBtZWFzdXJlcyB0aGUgcHJvcG9ydGlvbiBvZiBhY3R1YWwgbmVnYXRpdmUgY2FzZXMgdGhhdCB3ZXJlIGNvcnJlY3RseSBpZGVudGlmaWVkIGJ5IHRoZSBtb2RlbC4gSXQgYW5zd2VycyB0aGUgcXVlc3Rpb246ICJPdXQgb2YgYWxsIHRoZSBwZW9wbGUgd2hvIHRydWx5IGRvIE5PVCBoYXZlIHRoZSBjb25kaXRpb24sIGhvdyBtYW55IGRpZCBvdXIgbW9kZWwgY29ycmVjdGx5IGlkZW50aWZ5IGFzIG5vdCBoYXZpbmcgaXQ/IiAgDQoNCiRTcGVjaWZpY2l0eSA9IFxmcmFje1ROfXtUTitGUH0kDQoNCiAtIF9fSGlnaCBTcGVjaWZpY2l0eSAoY2xvc2VyIHRvIDEuMCk6X18gTWVhbnMgdGhlIG1vZGVsIGlzIHZlcnkgZ29vZCBhdCBjb3JyZWN0bHkgaWRlbnRpZnlpbmcgbmVnYXRpdmUgY2FzZXMuIEl0IGhhcyBhIGxvdyByYXRlIG9mIEZhbHNlIFBvc2l0aXZlcyAoaW5jb3JyZWN0bHkgY2xhc3NpZnlpbmcgbmVnYXRpdmVzIGFzIHBvc2l0aXZlcykuICANCiAtIF9fTG93IFNwZWNpZmljaXR5IChjbG9zZXIgdG8gMC4wKTpfXyBNZWFucyB0aGUgbW9kZWwgaW5jb3JyZWN0bHkgZmxhZ3MgbWFueSBuZWdhdGl2ZSBjYXNlcyBhcyBwb3NpdGl2ZSwgbGVhZGluZyB0byBhIGhpZ2ggcmF0ZSBvZiBGYWxzZSBQb3NpdGl2ZXMuICANCiANCl9fU3BlY2lmaWNpdHkgaXMgY3J1Y2lhbCB3aGVuIHRoZSBjb3N0IG9mIGEgRmFsc2UgUG9zaXRpdmUgaXMgaGlnaC5fXyAgDQoNCiAtIF9fU3BhbSBEZXRlY3Rpb246X18gWW91IHdhbnQgaGlnaCBzcGVjaWZpY2l0eSB0byBhdm9pZCBtYXJraW5nIGxlZ2l0aW1hdGUgZW1haWxzIGFzIHNwYW0gKGZhbHNlIHBvc2l0aXZlKSwgd2hpY2ggd291bGQgYmUgdmVyeSBhbm5veWluZyB0byB1c2Vycy4gWW91IG1pZ2h0IHRvbGVyYXRlIGEgZmV3IHNwYW0gZW1haWxzIGdldHRpbmcgdGhyb3VnaCAoZmFsc2UgbmVnYXRpdmVzKS4gIA0KIC0gX19MZWdhbC9DcmltaW5hbCBKdXN0aWNlOl9fIEluY29ycmVjdGx5IGlkZW50aWZ5aW5nIGFuIGlubm9jZW50IHBlcnNvbiBhcyBndWlsdHkgKGZhbHNlIHBvc2l0aXZlKSBoYXMgaGlnaCBzb2NpZXRhbCBjb3N0cy4gIA0KIC0gX19FeHBlbnNpdmUgb3IgSW52YXNpdmUgRm9sbG93LXVwIFRlc3RzOl9fIElmIGEgcG9zaXRpdmUgdGVzdCBsZWFkcyB0byBhIGNvc3RseSwgcGFpbmZ1bCwgb3Igcmlza3kgZm9sbG93LXVwIHByb2NlZHVyZSwgeW91IHdhbnQgaGlnaCBzcGVjaWZpY2l0eSB0byBtaW5pbWl6ZSB1bm5lY2Vzc2FyeSBwcm9jZWR1cmVzLiAgDQoNCiMjIyMgU1BQSU4gKFNwZWNpZmljIFBvc2l0aXZlID0gUnVsZSBJTikgIA0KSWYgYSBoaWdobHkgc3BlY2lmaWMgdGVzdCBjb21lcyBiYWNrIHBvc2l0aXZlLCB5b3UgY2FuIGNvbmZpZGVudGx5IHJ1bGUgaW4gdGhlIGRpc2Vhc2UvY29uZGl0aW9uLg0KDQojIyMgUmVjaWV2ZXIgT3BlcmF0aW5nIENoYXJhY3RlcmlzdGljIEN1cnZlIChST0MpICANClRoZSBST0MgY3VydmUgaXMgYSBncmFwaGljYWwgcGxvdCB0aGF0IGlsbHVzdHJhdGVzIHRoZSBkaWFnbm9zdGljIGFiaWxpdHkgb2YgYSBiaW5hcnkgY2xhc3NpZmllciBzeXN0ZW0gYXMgaXRzIGRpc2NyaW1pbmF0aW9uIHRocmVzaG9sZCBpcyB2YXJpZWQuIEl0IHBsb3RzIHRoZSAqVHJ1ZSBQb3NpdGl2ZSBSYXRlIChTZW5zaXRpdml0eSkqIG9uIHRoZSB5LWF4aXMgYWdhaW5zdCB0aGUgRmFsc2UgUG9zaXRpdmUgUmF0ZSAqKDEgLSBTcGVjaWZpY2l0eSkqIG9uIHRoZSB4LWF4aXMgZm9yIGRpZmZlcmVudCBwb3NzaWJsZSB0aHJlc2hvbGRzLg0KDQogLSBFYWNoIHBvaW50IG9uIHRoZSBST0MgY3VydmUgcmVwcmVzZW50cyBhIHNlbnNpdGl2aXR5L3NwZWNpZmljaXR5IHBhaXIgY29ycmVzcG9uZGluZyB0byBhIHBhcnRpY3VsYXIgY2xhc3NpZmljYXRpb24gdGhyZXNob2xkLiAgDQogDQogICAtICpUb3AtbGVmdCBjb3JuZXIgKDAsMSk6KiBUaGlzIHJlcHJlc2VudHMgYSBwZXJmZWN0IGNsYXNzaWZpZXI6IDEwMCUgU2Vuc2l0aXZpdHkgKG5vIGZhbHNlIG5lZ2F0aXZlcykgYW5kIDEwMCUgU3BlY2lmaWNpdHkgKG5vIGZhbHNlIHBvc2l0aXZlcykuIFRoZSBjbG9zZXIgeW91ciBjdXJ2ZSBpcyB0byB0aGlzIGNvcm5lciwgdGhlIGJldHRlciB0aGUgbW9kZWwncyBvdmVyYWxsIHBlcmZvcm1hbmNlLiAgDQogICAtICpEaWFnb25hbCBsaW5lIChmcm9tIDAsMCB0byAxLDEpOiogVGhpcyByZXByZXNlbnRzIGEgcmFuZG9tIGNsYXNzaWZpZXIgKGVxdWl2YWxlbnQgdG8gZmxpcHBpbmcgYSBjb2luKS4gQSBtb2RlbCB3aG9zZSBST0MgY3VydmUgZmFsbHMgYWxvbmcgdGhpcyBkaWFnb25hbCBpcyBwZXJmb3JtaW5nIG5vIGJldHRlciB0aGFuIGNoYW5jZS4gIA0KICAgLSAqQ3VydmUgYmVsb3cgdGhlIGRpYWdvbmFsOiogSW5kaWNhdGVzIGEgbW9kZWwgdGhhdCBwZXJmb3JtcyB3b3JzZSB0aGFuIHJhbmRvbSBjaGFuY2UuIElmIHRoaXMgaGFwcGVucywgeW91IGNhbiBvZnRlbiBqdXN0IGludmVydCB0aGUgbW9kZWwncyBwcmVkaWN0aW9ucyB0byBnZXQgYSBiZXR0ZXItdGhhbi1jaGFuY2UgbW9kZWwhICANCiAgIA0KIC0gVGhlIEFVQyBpcyBhIHNpbmdsZSBzY2FsYXIgdmFsdWUgdGhhdCBzdW1tYXJpemVzIHRoZSBvdmVyYWxsIHBlcmZvcm1hbmNlIG9mIHRoZSBjbGFzc2lmaWVyIGFjcm9zcyBhbGwgcG9zc2libGUgdGhyZXNob2xkcy4gSXQgcmFuZ2VzIGZyb20gMCB0byAxLiAgDQogDQogICAtICpBVUMgPSAxOiogUGVyZmVjdCBjbGFzc2lmaWNhdGlvbi4gIA0KICAgLSAqQVVDID0gMC41OiogUGVyZm9ybWFuY2UgaXMgbm8gYmV0dGVyIHRoYW4gcmFuZG9tIGd1ZXNzaW5nLiAgDQogICAtICpBVUMgPCAwLjU6KiBQZXJmb3JtYW5jZSBpcyB3b3JzZSB0aGFuIHJhbmRvbSBndWVzc2luZy4gIA0KICAgLSAqQSBoaWdoZXIgQVVDIGluZGljYXRlcyBhIGJldHRlciBtb2RlbC4gSXQgY2FuIGJlIGludGVycHJldGVkIGFzIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBtb2RlbCByYW5rcyBhIHJhbmRvbWx5IGNob3NlbiBwb3NpdGl2ZSBpbnN0YW5jZSBoaWdoZXIgdGhhbiBhIHJhbmRvbWx5IGNob3NlbiBuZWdhdGl2ZSBpbnN0YW5jZS4NCg0KYGBge3J9DQojIEluc3RhbGwgbmVjZXNzYXJ5IHBhY2thZ2VzIChpZiBub3QgYWxyZWFkeSBpbnN0YWxsZWQpDQojIGluc3RhbGwucGFja2FnZXMoIm1sYmVuY2giKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJNQVNTIikNCiMgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikgIyBGb3IgcGxvdHRpbmcNCiMgaW5zdGFsbC5wYWNrYWdlcygicFJPQyIpICAgICMgRm9yIFJPQyBjdXJ2ZXMNCg0KIyBMb2FkIHBhY2thZ2VzDQpsaWJyYXJ5KG1sYmVuY2gpDQpsaWJyYXJ5KE1BU1MpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShwUk9DKSAjIExvYWQgcFJPQyBmb3IgUk9DIGN1cnZlcyBhbmQgQVVDDQoNCiMgTG9hZCB0aGUgZGF0YXNldA0KZGF0YShQaW1hSW5kaWFuc0RpYWJldGVzMikNCm15X2RhdGEgPC0gUGltYUluZGlhbnNEaWFiZXRlczINCg0KIyBEYXRhIFByZXBhcmF0aW9uICh1c2luZyBkcGx5cikNCm15X2RhdGEgPC0gbXlfZGF0YSAlPiUNCiAgbmEub21pdCgpICU+JQ0KICBtdXRhdGUoZGlhYmV0ZXMgPSBmYWN0b3IoZGlhYmV0ZXMsIGxldmVscyA9IGMoIm5lZyIsICJwb3MiKSwgbGFiZWxzID0gYygiTm8iLCAiWWVzIikpKQ0KDQojIC0tLSAxLiBGaXQgZWFjaCBtb2RlbCBvbiB0aGUgKmVudGlyZSogZGF0YXNldCAtLS0NCiMgVGhpcyBpcyBhIGZpbmFsIG1vZGVsLCBub3QgcGFydCBvZiB0aGUgY3Jvc3MtdmFsaWRhdGlvbiBsb29wLg0KIyBJdCdzIHVzZWQgZm9yIHBsb3R0aW5nIHRoZSAib3ZlcmFsbCIgUk9DIGN1cnZlIGZvciB0aGUgZnVsbCBkYXRhc2V0Lg0KDQpjYXQoIlxuLS0tIEZpdHRpbmcgRmluYWwgTW9kZWxzIG9uIEZ1bGwgRGF0YXNldCBmb3IgUk9DIEN1cnZlIC0tLVxuIikNCg0KIyBMb2dpc3RpYyBSZWdyZXNzaW9uDQpmaW5hbF9tb2RlbF9nbG0gPC0gZ2xtKGRpYWJldGVzIH4gLiwgZGF0YSA9IG15X2RhdGEsIGZhbWlseSA9ICJiaW5vbWlhbCIpDQoNCiMgTERBDQpmaW5hbF9tb2RlbF9sZGEgPC0gbGRhKGRpYWJldGVzIH4gLiwgZGF0YSA9IG15X2RhdGEpDQoNCiMgUURBDQojIEFkZCBhIGNoZWNrIHRvIGVuc3VyZSBRREEgY2FuIGJlIGZpdHRlZCAoc3VmZmljaWVudCBkYXRhIHBlciBjbGFzcyByZWxhdGl2ZSB0byBwcmVkaWN0b3JzKQ0KIyBUaGlzIGlzIHNpbWlsYXIgdG8gdGhlIGNoZWNrIGluIHRoZSBDViBsb29wLCBidXQgYXBwbGllZCB0byB0aGUgZnVsbCBkYXRhc2V0Lg0KIyBUaGUgYG5jb2wobXlfZGF0YSkgLSAxYCBhY2NvdW50cyBmb3IgYWxsIHByZWRpY3RvcnMNCmlmIChzdW0obXlfZGF0YSRkaWFiZXRlcyA9PSAiTm8iKSA+PSAobmNvbChteV9kYXRhKSAtIDEpICYmDQogICAgc3VtKG15X2RhdGEkZGlhYmV0ZXMgPT0gIlllcyIpID49IChuY29sKG15X2RhdGEpIC0gMSkpIHsNCiAgZmluYWxfbW9kZWxfcWRhIDwtIHFkYShkaWFiZXRlcyB+IC4sIGRhdGEgPSBteV9kYXRhKQ0KfSBlbHNlIHsNCiAgd2FybmluZygiUURBIGNvdWxkIG5vdCBiZSBmaXR0ZWQgb24gdGhlIGZ1bGwgZGF0YXNldCBkdWUgdG8gaW5zdWZmaWNpZW50IG9ic2VydmF0aW9ucyBwZXIgY2xhc3MuIikNCiAgZmluYWxfbW9kZWxfcWRhIDwtIE5VTEwgIyBTZXQgdG8gTlVMTCBpZiBpdCBjYW5ub3QgYmUgZml0dGVkDQp9DQoNCg0KIyAtLS0gMi4gR2V0IHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGZvciB0aGUgJ1llcycgY2xhc3MgZnJvbSBlYWNoIG1vZGVsIC0tLQ0KDQojIExvZ2lzdGljIFJlZ3Jlc3Npb24gcHJvYmFiaWxpdGllcw0KIyB0eXBlID0gInJlc3BvbnNlIiBnaXZlcyBwcm9iYWJpbGl0aWVzIGRpcmVjdGx5IGZvciBnbG0NCnByb2JfZ2xtIDwtIHByZWRpY3QoZmluYWxfbW9kZWxfZ2xtLCBuZXdkYXRhID0gbXlfZGF0YSwgdHlwZSA9ICJyZXNwb25zZSIpDQoNCiMgTERBIHByb2JhYmlsaXRpZXMNCiMgcHJlZGljdCgpIGZvciBsZGEvcWRhIHJldHVybnMgYSBsaXN0OyAkcG9zdGVyaW9yIGNvbnRhaW5zIHByb2JhYmlsaXRpZXMNCnByb2JfbGRhIDwtIHByZWRpY3QoZmluYWxfbW9kZWxfbGRhLCBuZXdkYXRhID0gbXlfZGF0YSkkcG9zdGVyaW9yWywgIlllcyJdDQoNCiMgUURBIHByb2JhYmlsaXRpZXMgKGNoZWNrIGlmIFFEQSBtb2RlbCB3YXMgc3VjY2Vzc2Z1bGx5IGZpdHRlZCkNCnByb2JfcWRhIDwtIGlmICghaXMubnVsbChmaW5hbF9tb2RlbF9xZGEpKSB7DQogIHByZWRpY3QoZmluYWxfbW9kZWxfcWRhLCBuZXdkYXRhID0gbXlfZGF0YSkkcG9zdGVyaW9yWywgIlllcyJdDQp9IGVsc2Ugew0KICBOVUxMICMgSWYgUURBIHdhc24ndCBmaXR0ZWQsIG5vIHByb2JhYmlsaXRpZXMNCn0NCg0KIyAtLS0gMy4gQ3JlYXRlIFJPQyBvYmplY3RzIGZvciBlYWNoIG1vZGVsIC0tLQ0KDQojIEZvciBMb2dpc3RpYyBSZWdyZXNzaW9uDQpyb2Nfb2JqX2dsbSA8LSByb2MocmVzcG9uc2UgPSBteV9kYXRhJGRpYWJldGVzLCBwcmVkaWN0b3IgPSBwcm9iX2dsbSwNCiAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJObyIsICJZZXMiKSkgIyBFbnN1cmUgbGV2ZWxzIGFyZSBpbiBvcmRlcjogbmVnYXRpdmUgZmlyc3QNCg0KIyBGb3IgTERBDQpyb2Nfb2JqX2xkYSA8LSByb2MocmVzcG9uc2UgPSBteV9kYXRhJGRpYWJldGVzLCBwcmVkaWN0b3IgPSBwcm9iX2xkYSwNCiAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJObyIsICJZZXMiKSkNCg0KIyBGb3IgUURBIChvbmx5IGlmIHRoZSBtb2RlbCB3YXMgZml0dGVkKQ0KaWYgKCFpcy5udWxsKHByb2JfcWRhKSkgew0KICByb2Nfb2JqX3FkYSA8LSByb2MocmVzcG9uc2UgPSBteV9kYXRhJGRpYWJldGVzLCBwcmVkaWN0b3IgPSBwcm9iX3FkYSwNCiAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk5vIiwgIlllcyIpKQ0KfSBlbHNlIHsNCiAgcm9jX29ial9xZGEgPC0gTlVMTA0KfQ0KDQoNCiMgLS0tIDQuIFBsb3QgYWxsIHRocmVlIFJPQyBjdXJ2ZXMgb24gdGhlIHNhbWUgZ3JhcGggLS0tDQoNCiMgU3RhcnQgd2l0aCB0aGUgZmlyc3QgUk9DIGN1cnZlIChlLmcuLCBMb2dpc3RpYyBSZWdyZXNzaW9uKQ0KcGxvdChyb2Nfb2JqX2dsbSwNCiAgICAgY29sID0gIiMzMTgyYmQiLCAjIENvbG9yIGZvciBHTE0NCiAgICAgbHdkID0gMiwgICAgICAjIExpbmUgd2lkdGgNCiAgICAgbWFpbiA9ICJST0MgQ3VydmVzIGZvciBDbGFzc2lmaWNhdGlvbiBNb2RlbHMiLA0KICAgICBzdWIgPSBwYXN0ZTAoIkxvZ2lzdGljIFJlZ3Jlc3Npb24gQVVDOiAiLCByb3VuZChhdWMocm9jX29ial9nbG0pLCAzKSwNCiAgICAgICAgICAgICAgICAgICJcbkxEQSBBVUM6ICIsIHJvdW5kKGF1Yyhyb2Nfb2JqX2xkYSksIDMpLA0KICAgICAgICAgICAgICAgICAgaWYgKCFpcy5udWxsKHJvY19vYmpfcWRhKSkgeyBwYXN0ZTAoIlxuUURBIEFVQzogIiwgcm91bmQoYXVjKHJvY19vYmpfcWRhKSwgMykpIH0NCiAgICAgICAgICAgICAgICAgIGVsc2UgeyAiIiB9DQogICAgICAgICAgICAgICAgICApLA0KICAgICBncmlkID0gVFJVRSAjIEFkZCBhIGdyaWQgZm9yIGJldHRlciByZWFkYWJpbGl0eQ0KICAgICApDQoNCiMgQWRkIExEQSBjdXJ2ZSB0byB0aGUgZXhpc3RpbmcgcGxvdA0KcGxvdChyb2Nfb2JqX2xkYSwNCiAgICAgY29sID0gIiNkZTJkMjYiLCAjIENvbG9yIGZvciBMREENCiAgICAgbHdkID0gMiwNCiAgICAgYWRkID0gVFJVRSAjIEFkZCB0byB0aGUgY3VycmVudCBwbG90DQogICAgICkNCg0KIyBBZGQgUURBIGN1cnZlIHRvIHRoZSBleGlzdGluZyBwbG90IChpZiBpdCB3YXMgZml0dGVkKQ0KaWYgKCFpcy5udWxsKHJvY19vYmpfcWRhKSkgew0KICBwbG90KHJvY19vYmpfcWRhLA0KICAgICAgIGNvbCA9ICIjMzFhMzU0IiwgIyBDb2xvciBmb3IgUURBDQogICAgICAgbHdkID0gMiwNCiAgICAgICBhZGQgPSBUUlVFDQogICAgICAgKQ0KfQ0KDQojIEFkZCBhIGxlZ2VuZCB0byBkaXN0aW5ndWlzaCB0aGUgY3VydmVzDQpsZWdlbmQoImJvdHRvbXJpZ2h0IiwNCiAgICAgICBsZWdlbmQgPSBjKHBhc3RlMCgiTG9naXN0aWMgUmVncmVzc2lvbiAoQVVDID0gIiwgcm91bmQoYXVjKHJvY19vYmpfZ2xtKSwgMyksICIpIiksDQogICAgICAgICAgICAgICAgICBwYXN0ZTAoIkxEQSAoQVVDID0gIiwgcm91bmQoYXVjKHJvY19vYmpfbGRhKSwgMyksICIpIiksDQogICAgICAgICAgICAgICAgICBpZiAoIWlzLm51bGwocm9jX29ial9xZGEpKSB7IHBhc3RlMCgiUURBIChBVUMgPSAiLCByb3VuZChhdWMocm9jX29ial9xZGEpLCAzKSwgIikiKSB9DQogICAgICAgICAgICAgICAgICBlbHNlIHsgIlFEQSAoTm90IEZpdHRlZCkiIH0NCiAgICAgICAgICAgICAgICAgICksDQogICAgICAgY29sID0gYygiIzMxODJiZCIsICIjZGUyZDI2IiwgIiMzMWEzNTQiKSwNCiAgICAgICBsd2QgPSAyLA0KICAgICAgIGNleCA9IDAuOCAjIEZvbnQgc2l6ZSBmb3IgbGVnZW5kDQogICAgICAgKQ0KDQojIFlvdSBjYW4gYWxzbyBhZGQgdGhlIGRpYWdvbmFsIGxpbmUgcmVwcmVzZW50aW5nIHJhbmRvbSBjaGFuY2UNCmFibGluZShhID0gMCwgYiA9IDEsIGx0eSA9IDIsIGNvbCA9ICIjYmRiZGJkIikgIyBEYXNoZWQgZ3JheSBsaW5lDQoNCiMgLS0tIE9wdGlvbmFsOiBQcmludCBpbmRpdmlkdWFsIEFVQ3MgYWdhaW4gZm9yIGVhc3kgcmVmZXJlbmNlIC0tLQ0KY2F0KCJcbi0tLSBBVUMgVmFsdWVzIGZvciBGaW5hbCBNb2RlbHMgLS0tXG4iKQ0KY2F0KCJMb2dpc3RpYyBSZWdyZXNzaW9uIEFVQzogIiwgYXVjKHJvY19vYmpfZ2xtKSwgIlxuIikNCmNhdCgiTERBIEFVQzogIiwgYXVjKHJvY19vYmpfbGRhKSwgIlxuIikNCmlmICghaXMubnVsbChyb2Nfb2JqX3FkYSkpIHsNCiAgY2F0KCJRREEgQVVDOiAiLCBhdWMocm9jX29ial9xZGEpLCAiXG4iKQ0KfSBlbHNlIHsNCiAgY2F0KCJRREEgQVVDOiBOb3QgYXBwbGljYWJsZSAobW9kZWwgbm90IGZpdHRlZClcbiIpDQp9DQpgYGANCg0KX19XaHkgaXQncyBpbXBvcnRhbnQ6X18gIA0KDQogLSBVbmxpa2UgQWNjdXJhY3ksIFNlbnNpdGl2aXR5LCBhbmQgU3BlY2lmaWNpdHkgKHdoaWNoIGRlcGVuZCBvbiBhIGNob3NlbiB0aHJlc2hvbGQpLCB0aGUgUk9DIGN1cnZlIGFuZCBBVUMgcHJvdmlkZSBhIHZpZXcgb2YgdGhlIG1vZGVsJ3MgcGVyZm9ybWFuY2UgYWNyb3NzICphbGwgcG9zc2libGUgdGhyZXNob2xkcy4qIFRoaXMgaXMgZXNwZWNpYWxseSB1c2VmdWwgZm9yIG1vZGVscyB0aGF0IG91dHB1dCBwcm9iYWJpbGl0aWVzIChsaWtlIGxvZ2lzdGljIHJlZ3Jlc3Npb24pLCBhcyB5b3UgY2FuIGNob29zZSB0aGUgb3B0aW1hbCB0aHJlc2hvbGQgbGF0ZXIgYmFzZWQgb24geW91ciBzcGVjaWZpYyBuZWVkcy4gIA0KIC0gQVVDIGlzIGdlbmVyYWxseSBhIG1vcmUgcmVsaWFibGUgbWV0cmljIHRoYW4gYWNjdXJhY3kgd2hlbiBkZWFsaW5nIHdpdGggW2ltYmFsYW5jZWQgZGF0YXNldHNdKGh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL21hY2hpbmUtbGVhcm5pbmcvY3Jhc2gtY291cnNlL292ZXJmaXR0aW5nL2ltYmFsYW5jZWQtZGF0YXNldHMpICh3aGVyZSBvbmUgY2xhc3MgaXMgbXVjaCBtb3JlIHByZXZhbGVudCB0aGFuIHRoZSBvdGhlcikuIEEgY2xhc3NpZmllciBjYW4gYWNoaWV2ZSBoaWdoIGFjY3VyYWN5IGJ5IHNpbXBseSBwcmVkaWN0aW5nIHRoZSBtYWpvcml0eSBjbGFzcyBhbGwgdGhlIHRpbWUsIGJ1dCBpdHMgQVVDIHdvdWxkIHJldmVhbCBpdHMgcG9vciBkaXNjcmltaW5hdG9yeSBwb3dlci4gIA0KIC0gQVVDIGlzIGV4Y2VsbGVudCBmb3IgY29tcGFyaW5nIGRpZmZlcmVudCBtb2RlbHMgYmVjYXVzZSBpdCBzdW1tYXJpemVzIHRoZWlyIG92ZXJhbGwgZGlzY3JpbWluYXRpdmUgcG93ZXIgaW4gYSBzaW5nbGUgbnVtYmVyLiBUaGUgbW9kZWwgd2l0aCBhIGhpZ2hlciBBVUMgaXMgZ2VuZXJhbGx5IHByZWZlcnJlZC4NCg0KIyMjIyBUcmFkZS1vZmZzIGJldHdlZW4gU2Vuc2l0aXZpdHkgYW5kIFNwZWNpZmljaXR5IChhbmQgaG93IFJPQyBzaG93cyBpdCkgIA0KDQpUaGVyZSdzIG9mdGVuIGEgdHJhZGUtb2ZmIGJldHdlZW4gc2Vuc2l0aXZpdHkgYW5kIHNwZWNpZmljaXR5LiAgDQoNCiAtIElmIHlvdSBzZXQgYSB2ZXJ5IGxvdyB0aHJlc2hvbGQgZm9yIGNsYXNzaWZ5aW5nIGFuIG91dGNvbWUgYXMgInBvc2l0aXZlIiAoZS5nLiwgaWYgYW55IHByb2JhYmlsaXR5IGFib3ZlIDAuMSBpcyBjb25zaWRlcmVkIHBvc2l0aXZlKSwgeW91J2xsIGxpa2VseSBpbmNyZWFzZSB5b3VyIHNlbnNpdGl2aXR5IChjYXRjaCBtb3JlIHRydWUgcG9zaXRpdmVzKSBidXQgYWxzbyBpbmNyZWFzZSB5b3VyIGZhbHNlIHBvc2l0aXZlIHJhdGUgKGRlY3JlYXNlIHNwZWNpZmljaXR5KS4gIA0KIC0gQ29udmVyc2VseSwgaWYgeW91IHNldCBhIHZlcnkgaGlnaCB0aHJlc2hvbGQgKGUuZy4sIG9ubHkgcHJvYmFiaWxpdGllcyBhYm92ZSAwLjkgYXJlIHBvc2l0aXZlKSwgeW91J2xsIGluY3JlYXNlIHlvdXIgc3BlY2lmaWNpdHkgKHJlZHVjZSBmYWxzZSBwb3NpdGl2ZXMpIGJ1dCBsaWtlbHkgZGVjcmVhc2UgeW91ciBzZW5zaXRpdml0eSAobWlzcyBtb3JlIHRydWUgcG9zaXRpdmVzKS4gIA==