1 Introduction

This report presents a comprehensive analysis comparing four types of supervised classification models to predict diabetes status based on patient health metrics. The practical hypothesis is that by comparing these models, we can identify the most accurate and reliable algorithm for identifying patients at risk of diabetes.

This project implements and evaluates the following algorithms:

  1. Logistic Regression (serving as a baseline)
  2. Perceptron (a single-layer neural network)
  3. Decision Tree
  4. BAGGING (Bootstrap Aggregation)

The report is structured as follows: * Part 1: Data loading, exploratory data analysis (EDA), and preprocessing. * Part 2: Development of all four candidate model families, including model-specific visualizations. * Part 3: A final comparison of all developed models using ROC-AUC analysis to determine the best-performing model.


2 Part 1: Data Preparation and EDA

The first step in any modeling process is to load, understand, and clean the data. This section covers our data preparation and preprocessing, which will be used for all subsequent models.

2.1 1.1 Data Loading and Initial Inspection

# Set directory and load the dataset
setwd("/Users/jeffery/Library/Mobile Documents/com~apple~CloudDocs/Documents/Documents - jMacP/WCUPA/Classes/Fall 2025/STA551/Project 2/Data")
diabetes.data <- read_csv("diabetes_prediction_dataset.csv")

After loading, we inspect its dimensions (dim()) and data types (str()) to understand the dataset’s structure and variables.

# Display the dimensions (rows, columns) of the data
dim(diabetes.data)
## [1] 100000      9

summary() provides an overview of each variable.

# Summary of each variable
summary(diabetes.data)
##     gender               age         hypertension     heart_disease    
##  Length:100000      Min.   : 0.08   Min.   :0.00000   Min.   :0.00000  
##  Class :character   1st Qu.:24.00   1st Qu.:0.00000   1st Qu.:0.00000  
##  Mode  :character   Median :43.00   Median :0.00000   Median :0.00000  
##                     Mean   :41.89   Mean   :0.07485   Mean   :0.03942  
##                     3rd Qu.:60.00   3rd Qu.:0.00000   3rd Qu.:0.00000  
##                     Max.   :80.00   Max.   :1.00000   Max.   :1.00000  
##  smoking_history         bmi         HbA1c_level    blood_glucose_level
##  Length:100000      Min.   :10.01   Min.   :3.500   Min.   : 80.0      
##  Class :character   1st Qu.:23.63   1st Qu.:4.800   1st Qu.:100.0      
##  Mode  :character   Median :27.32   Median :5.800   Median :140.0      
##                     Mean   :27.32   Mean   :5.528   Mean   :138.1      
##                     3rd Qu.:29.58   3rd Qu.:6.200   3rd Qu.:159.0      
##                     Max.   :95.69   Max.   :9.000   Max.   :300.0      
##     diabetes    
##  Min.   :0.000  
##  1st Qu.:0.000  
##  Median :0.000  
##  Mean   :0.085  
##  3rd Qu.:0.000  
##  Max.   :1.000

2.2 1.2 Data Preprocessing

The next step is to clean and prepare the data for modeling. This involves handling missing values and ensuring all variables are in the correct format.

2.2.1 Missing Value Handling

We use colSums(is.na()) to count the number of NA values in each column.

# Check for missing (NA) values in each column
colSums(is.na(diabetes.data))
##              gender                 age        hypertension       heart_disease 
##                   0                   0                   0                   0 
##     smoking_history                 bmi         HbA1c_level blood_glucose_level 
##                   0                   0                   0                   0 
##            diabetes 
##                   0

Observation: The output shows 0 missing values for all columns, so no imputation is needed.

2.2.2 Feature Encoding

The dataset contains several character-based variables (gender, smoking_history) that need to be converted to factors for R’s modeling functions (like glm()) to interpret them correctly as categorical predictors.

The target variable, diabetes, is also converted to a factor with clear ‘Yes’/‘No’ labels for better interpretability in our results.

During our initial summary(), we noted that the ‘Other’ category in gender has very few observations (only 18). To prevent model instability, we will remove these observations. We then use droplevels() to remove ‘Other’ from the factor levels.

# Filter out 'Other' gender category
diabetes.data.clean <- diabetes.data %>%
  filter(gender != "Other")

# Convert character columns and target to factors
diabetes.data.clean <- diabetes.data.clean %>%
  mutate(
    diabetes = factor(diabetes,
                      levels = c(0, 1),
                      labels = c("No", "Yes")),
    gender = as.factor(gender),
    smoking_history = as.factor(smoking_history)
  )

# Remove 'Other' level from the factor
diabetes.data.clean$gender <- droplevels(diabetes.data.clean$gender)

# Check the structure to confirm all changes have been applied
str(diabetes.data.clean)
## tibble [99,982 × 9] (S3: tbl_df/tbl/data.frame)
##  $ gender             : Factor w/ 2 levels "Female","Male": 1 1 2 1 2 1 1 1 2 1 ...
##  $ age                : num [1:99982] 80 54 28 36 76 20 44 79 42 32 ...
##  $ hypertension       : num [1:99982] 0 0 0 0 1 0 0 0 0 0 ...
##  $ heart_disease      : num [1:99982] 1 0 0 0 1 0 0 0 0 0 ...
##  $ smoking_history    : Factor w/ 6 levels "current","ever",..: 4 5 4 1 1 4 4 5 4 4 ...
##  $ bmi                : num [1:99982] 25.2 27.3 27.3 23.4 20.1 ...
##  $ HbA1c_level        : num [1:99982] 6.6 6.6 5.7 5 4.8 6.6 6.5 5.7 4.8 5 ...
##  $ blood_glucose_level: num [1:99982] 140 80 158 155 155 85 200 85 145 100 ...
##  $ diabetes           : Factor w/ 2 levels "No","Yes": 1 1 1 1 1 1 2 1 1 1 ...

2.3 1.3 Exploratory Data Visualization

Now that the data is clean, we can perform visual EDA to understand the distributions and relationships between variables.

2.3.1 Pair-wise Scatter Plot

We first create a pair-wise scatter plot of the numerical variables to check for correlations.

# Select numeric variables from our cleaned data
numeric_vars_df <- diabetes.data.clean[, c("age", "bmi", "HbA1c_level", "blood_glucose_level")]

# Create the pairs plot
pairs(numeric_vars_df, cex = 0.3, col = "navy", 
      main = "Pair-wise Scatter Plot of Numerical Variables")
Pair-wise Scatter Plot of Numerical Variables

Pair-wise Scatter Plot of Numerical Variables

Observation: The plot shows a strong positive correlation between blood_glucose_level and HbA1c_level. The variables age and bmi are continuously and widely distributed.

2.3.2 Key Variable Distributions

Next, we plot the distributions of several key predictors and the outcome variable.

# Set up a 2x2 plotting grid
par(mfrow = c(2, 2))

# 1. Age
hist(diabetes.data.clean$age, xlab = "Age", col = "steelblue", main = "Age Distribution")

# 2. Gender
barplot(table(diabetes.data.clean$gender), col = "steelblue", main = "Distribution of Gender")

# 3. BMI
hist(diabetes.data.clean$bmi, xlab = "BMI", col = "steelblue", main = "BMI Distribution")

# 4. Smoking History
barplot(table(diabetes.data.clean$smoking_history), col = "steelblue", main = "Smoking History")
Distributions of Key Variables

Distributions of Key Variables

# Reset plotting grid
par(mfrow = c(1, 1))

Observation: The dataset is diverse in age and bmi. Most participants are ‘Female’, and a large portion have ‘no info’ or ‘never’ for smoking history.

2.3.3 Glucose Distribution by Diabetes Status

Finally, we show a density plot to compare the distribution of blood_glucose_level for those with and without diabetes.

# Get glucose for diabetic and non-diabetic groups
diab.glucose <- diabetes.data.clean$blood_glucose_level[diabetes.data.clean$diabetes == "Yes"]
no.diab.glucose <- diabetes.data.clean$blood_glucose_level[diabetes.data.clean$diabetes == "No"]

# Plot the densities
plot(density(no.diab.glucose), col = "darkgreen", 
     main = "Distribution of Glucose Levels", xlab = "Glucose", xlim = range(diabetes.data.clean$blood_glucose_level))
lines(density(diab.glucose), col = "darkred", lwd = 2)
legend("topright", c("Diabetes Group", "Diabetes Free"),
       col = c("darkred", "darkgreen"), lwd = c(2, 1), bty = "n")
Distribution of Glucose Levels by Diabetes Status

Distribution of Glucose Levels by Diabetes Status

Observation: As expected, the distributions are very different. The “Diabetes Free” group is tightly clustered around lower glucose levels, while the “Diabetes Group” has a much higher mean and wider spread. This confirms it will be a strong predictor.


3 Part 2: Model Development

Now we develop our candidate models. This involves building multiple glm models, one neuralnet model, one rpart (decision tree) model, and one bagging model on the full dataset to compare their performance.

3.1 2.1 Logistic Regression Models (Baseline)

We will build three logistic regression models to serve as our baseline and for comparison.

3.1.1 Logistic (Sigmoid) Curve

The logistic regression model works by transforming a linear combination of inputs (\(z\)) through the logistic function (also called a sigmoid function) to produce a probability between 0 and 1. We can visualize this function.

z <- seq(-6, 6, length = 100)
pi_z <- 1 / (1 + exp(-z))
plot(z, pi_z, type = "l", lwd = 2, xlab = "z (Linear Combination)", ylab = expression(pi(z)),
     main = "Logistic Curve", col = "blue")
text(-3, 0.8, expression(pi(z) == frac(1, 1 + exp(-z))), col = "blue")
The Logistic (Sigmoid) Function

The Logistic (Sigmoid) Function

3.1.2 Model Building

We build three candidate models: 1. reducedModel: A simple model with only the variables we hypothesize are most clinically significant. 2. fullModel: A complex model that includes all available predictors. 3. forwards: An optimized model found using forward selection, starting from the reducedModel and adding predictors from the fullModel based on AIC.

# Define a reduced model with variables we assume are significant
reducedModel <- glm(diabetes ~ age + bmi + blood_glucose_level + HbA1c_level,
                    family = binomial(link = logit),
                    data = diabetes.data.clean)

# Define the full model with all variables
fullModel <- glm(diabetes ~ .,
                 family = binomial(link = logit),
                 data = diabetes.data.clean)

# Use forward selection to find the best model between reduced and full
forwards <- step(reducedModel,
                 scope = list(lower = formula(reducedModel), upper = formula(fullModel)),
                 direction = "forward",
                 trace = FALSE)

# Display the summary of the final, forward-selected model
summary(forwards)
## 
## Call:
## glm(formula = diabetes ~ age + bmi + blood_glucose_level + HbA1c_level + 
##     hypertension + smoking_history + heart_disease + gender, 
##     family = binomial(link = logit), data = diabetes.data.clean)
## 
## Coefficients:
##                              Estimate Std. Error z value Pr(>|z|)    
## (Intercept)                -2.708e+01  2.929e-01 -92.456  < 2e-16 ***
## age                         4.620e-02  1.126e-03  41.040  < 2e-16 ***
## bmi                         8.895e-02  2.555e-03  34.819  < 2e-16 ***
## blood_glucose_level         3.336e-02  4.821e-04  69.207  < 2e-16 ***
## HbA1c_level                 2.340e+00  3.578e-02  65.414  < 2e-16 ***
## hypertension                7.413e-01  4.710e-02  15.737  < 2e-16 ***
## smoking_historyever        -5.097e-02  9.248e-02  -0.551  0.58154    
## smoking_historyformer      -1.084e-01  7.009e-02  -1.546  0.12203    
## smoking_historynever       -1.566e-01  6.057e-02  -2.586  0.00971 ** 
## smoking_historyNo Info     -7.304e-01  6.651e-02 -10.981  < 2e-16 ***
## smoking_historynot current -2.114e-01  8.332e-02  -2.538  0.01115 *  
## heart_disease               7.346e-01  6.072e-02  12.099  < 2e-16 ***
## genderMale                  2.724e-01  3.613e-02   7.540 4.69e-14 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 58160  on 99981  degrees of freedom
## Residual deviance: 22627  on 99969  degrees of freedom
## AIC: 22653
## 
## Number of Fisher Scoring iterations: 8

3.2 2.2 Perceptron (Single-Layer Neural Network)

The perceptron is a single-layer neural network. When it uses a logistic (sigmoid) activation function, it is mathematically equivalent to logistic regression.

3.2.1 Data Prep for neuralnet

Neural networks are sensitive to the scale of input data and require all inputs to be numeric. We must first scale our numeric features (using min-max normalization) and create a design matrix (dummify) for the neuralnet function.

# Create a copy of the data for neural network preprocessing
neuralData <- diabetes.data.clean

# Identify numeric variables for scaling
numeric.vars <- c("age", "bmi", "HbA1c_level", "blood_glucose_level")

# Loop through numeric variables, scale them using min-max normalization
for (col in numeric.vars) {
  min.val <- min(neuralData[[col]])
  max.val <- max(neuralData[[col]])
  
  # The min-max formula
  neuralData[[col]] <- (neuralData[[col]] - min.val) / (max.val - min.val)
}

Next, we use model.matrix() to automatically create dummy variables for all our factors and build a formula string.

# Create the design matrix, which automatically dummifies factor variables 
neuralData.matrix <- model.matrix(~ ., data = neuralData)
neuralData.nn <- as.data.frame(neuralData.matrix)

# Clean the column names to make them valid R variables
valid.names <- make.names(colnames(neuralData.nn))
colnames(neuralData.nn) <- valid.names

# Add the numeric response variable (0/1) for neuralnet
neuralData.nn$diabetes_num <- ifelse(neuralData$diabetes == "Yes", 1, 0)

# Get all column names from the new data frame
columnNames <- colnames(neuralData.nn)

# Create the list of predictors by removing the (Intercept) and response variable
columnList <- paste(columnNames[-c(1, length(columnNames))], collapse = "+")

# Create the final formula string
modelFormula <- as.formula(paste("diabetes_num ~", columnList))

# Print the formula to check
print(modelFormula)
## diabetes_num ~ genderMale + age + hypertension + heart_disease + 
##     smoking_historyever + smoking_historyformer + smoking_historynever + 
##     smoking_historyNo.Info + smoking_historynot.current + bmi + 
##     HbA1c_level + blood_glucose_level + diabetesYes

3.2.2 Build and Plot Perceptron Model

With the scaled and dummified data, we train the perceptron. * We set hidden = 1 for a single-layer network. * We use act.fct = "logistic" (the sigmoid function). * We set linear.output = FALSE for classification.

# Train the perceptron
set.seed(123)
perceptron.model <- neuralnet(modelFormula,
                              data = neuralData.nn,
                              hidden = 1, 
                              act.fct = "logistic",
                              linear.output = FALSE) 

We can now plot the resulting network diagram.

# Create a visual representation of the perceptron model
plot(perceptron.model, rep = "best")
Perceptron Network Diagram

Perceptron Network Diagram

Observation: The plot displays a two-layer neural network. The input variables (e.g., genderMale, age, bmi) are on the left. Each input is connected to a single hidden node (the first circle), with the connecting lines showing their respective weights. This hidden node also receives a bias input (from the top-left “1” with weight -9.25315).

The output of this hidden node (shown as 14.75531) is then fed into a final output node. This output node receives its own bias input (from the top-right “1” with weight -7.842) and produces the final diabetes_num prediction.

3.3 2.3 Decision Tree Model

We will use the rpart library to build our decision tree. This algorithm generates a set of rules that are easy to interpret.

# Build the decision tree model
# We use method = "class" for a classification tree
tree_model <- rpart(
  formula = diabetes ~ .,
  data = diabetes.data.clean,
  method = "class",
  # We use the default control parameters
  control = rpart.control(minsplit = 20, cp = 0.01)
)

# Print the model summary
print(tree_model)
## n= 99982 
## 
## node), split, n, loss, yval, (yprob)
##       * denotes terminal node
## 
## 1) root 99982 8500 No (0.91498470 0.08501530)  
##   2) HbA1c_level< 6.7 96087 4605 No (0.95207468 0.04792532)  
##     4) blood_glucose_level< 210 94295 2813 No (0.97016809 0.02983191) *
##     5) blood_glucose_level>=210 1792    0 Yes (0.00000000 1.00000000) *
##   3) HbA1c_level>=6.7 3895    0 Yes (0.00000000 1.00000000) *

3.3.1 Visualize the Tree

We use rpart.plot to create a clean visual of the tree structure.

# Plot the tree
rpart.plot(tree_model, main = "Decision Tree for Diabetes Prediction")
Decision Tree for Diabetes Prediction

Decision Tree for Diabetes Prediction

Observation: The tree first splits the data based on an HbA1c_level threshold. If a patient’s level is high, the model immediately predicts ‘Yes’ for diabetes. For patients with a lower HbA1c level, the model asks a second question about their blood_glucose_level. This shows the hierarchy of predictor importance.

3.4 2.4 BAGGING (Bootstrap Aggregation) Algorithm

BAGGING (Bootstrap Aggregation) is an ensemble method that builds multiple decision trees on different bootstrap samples of the data and aggregates their predictions (using “majority voting”). This process is used to decrease the variance in the prediction model.

We will use the bagging() function from the ipred library.

# Build the BAGGING ensemble model
set.seed(123) # For reproducibility
bagging_model <- bagging(
  formula = as.factor(diabetes) ~ .,
  data = diabetes.data.clean,
  nbagg = 150,      # number of trees
  coob = TRUE,      
  parms = list(loss = matrix(c(0, 10, 1, 0),
                           ncol = 2, 
                           byrow = TRUE), 
               split = "gini"), 
  control = rpart.control(minsplit = 10, 
                          cp = 0.02)     
)

# Print the model summary
print(bagging_model)
## 
## Bagging classification trees with 150 bootstrap replications 
## 
## Call: bagging.data.frame(formula = as.factor(diabetes) ~ ., data = diabetes.data.clean, 
##     nbagg = 150, coob = TRUE, parms = list(loss = matrix(c(0, 
##         10, 1, 0), ncol = 2, byrow = TRUE), split = "gini"), 
##     control = rpart.control(minsplit = 10, cp = 0.02))
## 
## Out-of-bag estimate of misclassification error:  0.0281

3.4.1 Bootstrap AUC Distribution

To visualize the effect of bagging, we can run a smaller bootstrap loop (B=100) to build 100 logistic regression models and plot the histogram of their AUCs.

# We will use B=100 for speed.
btAUC.vec <- c()
B <- 100
sample.size <- dim(diabetes.data.clean)[1]
set.seed(456) # New seed for this loop

for (k in 1:B) {
  # 1. Get bootstrap sample
  boot.id <- sample(1:sample.size, sample.size, replace = TRUE)
  boot.sample <- diabetes.data.clean[boot.id, ]
  
  # 2. Fit model on bootstrap sample
  boot.logistic <- glm(diabetes ~ ., family = binomial, data = boot.sample)
  
  # 3. Get predictions 
  pred.prob <- predict.glm(boot.logistic, newdata = boot.sample, type = "response")
  
  # 4. Calculate and store AUC
  category <- boot.sample$diabetes == "Yes"
  ROCobj <- roc(category, pred.prob, quiet = TRUE)
  btAUC.vec[k] <- auc(ROCobj)
}

# Plot the histogram of the 100 bootstrap AUCs
hist(btAUC.vec, xlab = "Bootstrap AUC", main = "Bootstrap Sampling Distribution of AUCs \n (B=100)")
Bootstrap Sampling Distribution of AUCs (B=100)

Bootstrap Sampling Distribution of AUCs (B=100)

Observation: This histogram shows the sampling distribution of the AUC. We can see a clear central tendency, and the spread of this distribution gives us an idea of the model’s stability. We could use this to form a 95% confidence interval.


4 Part 3: Final Model Comparison and Selection

Now that all candidate models have been built, we will perform a comprehensive comparison to select the best one. As per the project instructions and feedback, we will use ROC-AUC analysis to compare the global performance of all models.

We will generate predictions from all models and plot their ROC curves on a single graph for a direct, synthesized comparison.

# 1. Get predictions (as probabilities) for all models
predReduced <- predict(reducedModel, newdata = diabetes.data.clean, type = "response")
predFull <- predict(fullModel, newdata = diabetes.data.clean, type = "response")
predForwards <- predict(forwards, newdata = diabetes.data.clean, type = "response")

# Perceptron model predictions (uses the scaled neuralData.nn)
predNN.raw <- predict(perceptron.model, newdata = neuralData.nn)
predNN <- as.vector(predNN.raw) 

# Decision Tree model predictions
tree_probs <- predict(tree_model, newdata = diabetes.data.clean, type = "prob")[, "Yes"]

# BAGGING model predictions (requires type = "prob")
bagging_probs <- predict(bagging_model, newdata = diabetes.data.clean, type = "prob")
bagging_probs_yes <- bagging_probs[, "Yes"] # Select the "Yes" probability

# 2. Define the true category 
category <- diabetes.data.clean$diabetes == "Yes"

# 3. Create ROC objects for all models
ROCobj.reduced <- roc(category, predReduced, quiet = TRUE)
ROCobj.full <- roc(category, predFull, quiet = TRUE)
ROCobj.forwards <- roc(category, predForwards, quiet = TRUE)
ROCobj.NN <- roc(category, predNN, quiet = TRUE)
ROCobj.tree <- roc(category, tree_probs, quiet = TRUE)
ROCobj.bagging <- roc(category, bagging_probs_yes, quiet = TRUE)

# 4. Get AUC values
auc_reduced <- auc(ROCobj.reduced)
auc_full <- auc(ROCobj.full)
auc_forwards <- auc(ROCobj.forwards)
auc_nn <- auc(ROCobj.NN)
auc_tree <- auc(ROCobj.tree)
auc_bagging <- auc(ROCobj.bagging)

# 5. Plot all ROC curves on one graph for comparison
plot(ROCobj.bagging, col = "red", main = "Comprehensive Model Comparison (ROC)", lwd = 2)
plot(ROCobj.full, col = "blue", add = TRUE, lty = 2, lwd = 2)
plot(ROCobj.forwards, col = "darkgreen", add = TRUE, lty = 3, lwd = 2)
plot(ROCobj.NN, col = "purple", add = TRUE, lty = 4, lwd = 2)
plot(ROCobj.tree, col = "orange", add = TRUE, lty = 5, lwd = 2)
plot(ROCobj.reduced, col = "grey", add = TRUE, lty = 6, lwd = 2)

legend("bottomright", 
       legend = c(paste("BAGGING (AUC:", round(auc_bagging, 4), ")"),
                  paste("GLM Full (AUC:", round(auc_full, 4), ")"),
                  paste("GLM Forwards (AUC:", round(auc_forwards, 4), ")"),
                  paste("Perceptron (AUC:", round(auc_nn, 4), ")"),
                  paste("Decision Tree (AUC:", round(auc_tree, 4), ")"),
                  paste("GLM Reduced (AUC:", round(auc_reduced, 4), ")")),
       col = c("red", "blue", "darkgreen", "purple", "orange", "grey"),
       lwd = 2,
       lty = c(1, 2, 3, 4, 5, 6))
Comprehensive Model Comparison (ROC)

Comprehensive Model Comparison (ROC)

Analysis: The ROC plot compares the performance of six models. The Perceptron model (purple) shows perfect performance with an AUC of 1, its curve following the top-left edge of the plot.

The three GLM models also perform exceptionally well and are clustered together: GLM Full (AUC 0.9619), GLM Forwards (AUC 0.9619), and GLM Reduced (AUC 0.9586).

The BAGGING and Decision Tree models are the weakest, both achieving an identical and significantly lower AUC of 0.8345.


5 Part 4: Conclusion

  • Part 1 (Data Prep): The data was successfully loaded, inspected, and preprocessed. No missing values were found, and categorical variables were correctly encoded. Visual EDA confirmed that key predictors like blood_glucose_level are highly correlated with diabetes.

  • Part 2 (Model Development): We developed all four candidate models as specified by the project:

    • Logistic Regression: Three models (reduced, full, forwards) were built as a baseline.
    • Perceptron (neuralnet): A single-layer neural network was built and visualized.
    • Decision Tree: A single classification tree was built and plotted.
    • BAGGING: An ensemble model was built using ipred, and its bootstrap-based AUC distribution was visualized.
  • Part 3 (Final Model Comparison): We evaluated all models by calculating their ROC-AUC on the entire dataset and plotting them on a single, comprehensive graph. The final AUC scores were:

Model AUC
BAGGING (Ensemble) 0.8345
Full GLM 0.9619
Forwards GLM 0.9619
Perceptron (NN) 1
Decision Tree 0.8345
Reduced GLM 0.9586

Based on this analysis, the Perceptron (NN) model is the best-performing model, achieving a perfect AUC of 1. The Full Logistic Model and Forwards-Selected Logistic Model also proved to be very strong and highly competitive.

Interestingly, the BAGGING model (AUC 0.8345) performed identically to the single Decision Tree (AUC 0.8345). While theory suggests bagging should reduce variance and improve accuracy, that was not the case here, as it provided no performance benefit over the single tree.

LS0tCnRpdGxlOiAnUHJvamVjdCBUd286IFN1cGVydmlzZWQgQ2xhc3NpZmljYXRpb24gZm9yIERpYWJldGVzIFByZWRpY3Rpb24gLSBBIENvbXBhcmlzb24gb2YgTG9naXN0aWMsIFBlcmNlcHRyb24sIFRyZWUsIGFuZCBCYWdnaW5nIE1vZGVscycKYXV0aG9yOiAnSmVmZiBEZWx2YScKZGF0ZTogIk5vdmVtYmVyIDgsIDIwMjUiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ193aWR0aDogOAogICAgZmlnX2hlaWdodDogNQogICAgZmlnX2NhcHRpb246IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRvY19jb2xsYXBzZWQ6IHllcwogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIHNtb290aF9zY3JvbGw6IHllcwogICAgdGhlbWU6IGx1bWVuCiAgICBoaWdobGlnaHQ6IHRhbmdvCi0tLQoKYGBge2NzcywgZWNobyA9IEZBTFNFfQpoMS50aXRsZSB7CiAgZm9udC1zaXplOiAyNHB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQpoNC5hdXRob3IsIGg0LmRhdGUgewogIGZvbnQtc2l6ZTogMThweDsKICBmb250LXdlaWdodDogYm9sZDsKICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICBjb2xvcjogRGFya0JsdWU7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CmgxIHsKICAgIGZvbnQtc2l6ZTogMjBweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogZGFya3JlZDsKICAgIHRleHQtYWxpZ246IGNlbnRlcjsKfQpoMiB7CiAgICBmb250LXNpemU6IDE4cHg7CiAgICBmb250LXdlaWdodDogYm9sZDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9CmgzIHsKICAgIGZvbnQtc2l6ZTogMTZweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KLmhlYWRlci1zZWN0aW9uLW51bWJlcjo6YWZ0ZXIgewogIGNvbnRlbnQ6ICIuIjsKfQpgYGAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFKQoKIyBMb2FkIGxpYnJhcmllcyBmb3IgdGhlIHJlcG9ydApsaWJyYXJ5KGtuaXRyKSAgICAgICAjIEZvciBrbml0dGluZyB0aGUgZG9jdW1lbnQKbGlicmFyeShwYW5kZXIpICAgICAgIyBGb3IgZm9ybWF0dGluZyB0YWJsZXMKbGlicmFyeShNQVNTKSAgICAgICAgIyBGb3Igc3RlcCgpIGZ1bmN0aW9uCmxpYnJhcnkocmVhZHIpICAgICAgICMgRm9yIGxvYWRpbmcgQ1NWIGRhdGEKbGlicmFyeShkcGx5cikgICAgICAgIyBGb3IgZGF0YSBtYW5pcHVsYXRpb24KbGlicmFyeShycGFydCkgICAgICAgIyBGb3IgZGVjaXNpb24gdHJlZXMKbGlicmFyeShycGFydC5wbG90KSAgIyBGb3IgcGxvdHRpbmcgdHJlZXMKbGlicmFyeShwUk9DKSAgICAgICAgIyBGb3IgUk9DIGFuYWx5c2lzCmxpYnJhcnkobmV1cmFsbmV0KSAgICMgRm9yIHBlcmNlcHRyb24gbW9kZWxzCmxpYnJhcnkoaXByZWQpICAgICAgICMgRm9yIGJhZ2dpbmcgYWxnb3JpdGhtCmBgYAoKIyBJbnRyb2R1Y3Rpb24KClRoaXMgcmVwb3J0IHByZXNlbnRzIGEgY29tcHJlaGVuc2l2ZSBhbmFseXNpcyBjb21wYXJpbmcgZm91ciB0eXBlcyBvZiBzdXBlcnZpc2VkIGNsYXNzaWZpY2F0aW9uIG1vZGVscyB0byBwcmVkaWN0IGRpYWJldGVzIHN0YXR1cyBiYXNlZCBvbiBwYXRpZW50IGhlYWx0aCBtZXRyaWNzLiBUaGUgcHJhY3RpY2FsIGh5cG90aGVzaXMgaXMgdGhhdCBieSBjb21wYXJpbmcgdGhlc2UgbW9kZWxzLCB3ZSBjYW4gaWRlbnRpZnkgdGhlIG1vc3QgYWNjdXJhdGUgYW5kIHJlbGlhYmxlIGFsZ29yaXRobSBmb3IgaWRlbnRpZnlpbmcgcGF0aWVudHMgYXQgcmlzayBvZiBkaWFiZXRlcy4KClRoaXMgcHJvamVjdCBpbXBsZW1lbnRzIGFuZCBldmFsdWF0ZXMgdGhlIGZvbGxvd2luZyBhbGdvcml0aG1zOgoKMS4gICoqTG9naXN0aWMgUmVncmVzc2lvbioqIChzZXJ2aW5nIGFzIGEgYmFzZWxpbmUpCjIuICAqKlBlcmNlcHRyb24qKiAoYSBzaW5nbGUtbGF5ZXIgbmV1cmFsIG5ldHdvcmspCjMuICAqKkRlY2lzaW9uIFRyZWUqKgo0LiAgKipCQUdHSU5HKiogKEJvb3RzdHJhcCBBZ2dyZWdhdGlvbikKClRoZSByZXBvcnQgaXMgc3RydWN0dXJlZCBhcyBmb2xsb3dzOgoqICoqUGFydCAxOioqIERhdGEgbG9hZGluZywgZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcyAoRURBKSwgYW5kIHByZXByb2Nlc3NpbmcuCiogKipQYXJ0IDI6KiogRGV2ZWxvcG1lbnQgb2YgYWxsIGZvdXIgY2FuZGlkYXRlIG1vZGVsIGZhbWlsaWVzLCBpbmNsdWRpbmcgbW9kZWwtc3BlY2lmaWMgdmlzdWFsaXphdGlvbnMuCiogKipQYXJ0IDM6KiogQSBmaW5hbCBjb21wYXJpc29uIG9mIGFsbCBkZXZlbG9wZWQgbW9kZWxzIHVzaW5nIFJPQy1BVUMgYW5hbHlzaXMgdG8gZGV0ZXJtaW5lIHRoZSBiZXN0LXBlcmZvcm1pbmcgbW9kZWwuCgotLS0tLQoKIyBQYXJ0IDE6IERhdGEgUHJlcGFyYXRpb24gYW5kIEVEQQoKVGhlIGZpcnN0IHN0ZXAgaW4gYW55IG1vZGVsaW5nIHByb2Nlc3MgaXMgdG8gbG9hZCwgdW5kZXJzdGFuZCwgYW5kIGNsZWFuIHRoZSBkYXRhLiBUaGlzIHNlY3Rpb24gY292ZXJzIG91ciBkYXRhIHByZXBhcmF0aW9uIGFuZCBwcmVwcm9jZXNzaW5nLCB3aGljaCB3aWxsIGJlIHVzZWQgZm9yIGFsbCBzdWJzZXF1ZW50IG1vZGVscy4KCiMjIDEuMSBEYXRhIExvYWRpbmcgYW5kIEluaXRpYWwgSW5zcGVjdGlvbgoKYGBge3IgbG9hZC1kYXRhfQojIFNldCBkaXJlY3RvcnkgYW5kIGxvYWQgdGhlIGRhdGFzZXQKc2V0d2QoIi9Vc2Vycy9qZWZmZXJ5L0xpYnJhcnkvTW9iaWxlIERvY3VtZW50cy9jb21+YXBwbGV+Q2xvdWREb2NzL0RvY3VtZW50cy9Eb2N1bWVudHMgLSBqTWFjUC9XQ1VQQS9DbGFzc2VzL0ZhbGwgMjAyNS9TVEE1NTEvUHJvamVjdCAyL0RhdGEiKQpkaWFiZXRlcy5kYXRhIDwtIHJlYWRfY3N2KCJkaWFiZXRlc19wcmVkaWN0aW9uX2RhdGFzZXQuY3N2IikKYGBgCgpBZnRlciBsb2FkaW5nLCB3ZSBpbnNwZWN0IGl0cyBkaW1lbnNpb25zIChgZGltKClgKSBhbmQgZGF0YSB0eXBlcyAoYHN0cigpYCkgdG8gdW5kZXJzdGFuZCB0aGUgZGF0YXNldCdzIHN0cnVjdHVyZSBhbmQgdmFyaWFibGVzLgoKYGBge3IgcmV2aWV3fQojIERpc3BsYXkgdGhlIGRpbWVuc2lvbnMgKHJvd3MsIGNvbHVtbnMpIG9mIHRoZSBkYXRhCmRpbShkaWFiZXRlcy5kYXRhKQpgYGAKCmBzdW1tYXJ5KClgIHByb3ZpZGVzIGFuIG92ZXJ2aWV3IG9mIGVhY2ggdmFyaWFibGUuCgpgYGB7ciBzdW1tYXJ5fQojIFN1bW1hcnkgb2YgZWFjaCB2YXJpYWJsZQpzdW1tYXJ5KGRpYWJldGVzLmRhdGEpCmBgYAoKIyMgMS4yIERhdGEgUHJlcHJvY2Vzc2luZwoKVGhlIG5leHQgc3RlcCBpcyB0byBjbGVhbiBhbmQgcHJlcGFyZSB0aGUgZGF0YSBmb3IgbW9kZWxpbmcuIFRoaXMgaW52b2x2ZXMgaGFuZGxpbmcgbWlzc2luZyB2YWx1ZXMgYW5kIGVuc3VyaW5nIGFsbCB2YXJpYWJsZXMgYXJlIGluIHRoZSBjb3JyZWN0IGZvcm1hdC4KCiMjIyBNaXNzaW5nIFZhbHVlIEhhbmRsaW5nCgpXZSB1c2UgYGNvbFN1bXMoaXMubmEoKSlgIHRvIGNvdW50IHRoZSBudW1iZXIgb2YgYE5BYCB2YWx1ZXMgaW4gZWFjaCBjb2x1bW4uCgpgYGB7ciBtaXNzaW5nLXZhbHVlc30KIyBDaGVjayBmb3IgbWlzc2luZyAoTkEpIHZhbHVlcyBpbiBlYWNoIGNvbHVtbgpjb2xTdW1zKGlzLm5hKGRpYWJldGVzLmRhdGEpKQpgYGAKCioqT2JzZXJ2YXRpb24qKjogVGhlIG91dHB1dCBzaG93cyAwIG1pc3NpbmcgdmFsdWVzIGZvciBhbGwgY29sdW1ucywgc28gbm8gaW1wdXRhdGlvbiBpcyBuZWVkZWQuCgojIyMgRmVhdHVyZSBFbmNvZGluZwoKVGhlIGRhdGFzZXQgY29udGFpbnMgc2V2ZXJhbCBjaGFyYWN0ZXItYmFzZWQgdmFyaWFibGVzIChgZ2VuZGVyYCwgYHNtb2tpbmdfaGlzdG9yeWApIHRoYXQgbmVlZCB0byBiZSBjb252ZXJ0ZWQgdG8gZmFjdG9ycyBmb3IgUidzIG1vZGVsaW5nIGZ1bmN0aW9ucyAobGlrZSBgZ2xtKClgKSB0byBpbnRlcnByZXQgdGhlbSBjb3JyZWN0bHkgYXMgY2F0ZWdvcmljYWwgcHJlZGljdG9ycy4KClRoZSB0YXJnZXQgdmFyaWFibGUsIGBkaWFiZXRlc2AsIGlzIGFsc28gY29udmVydGVkIHRvIGEgZmFjdG9yIHdpdGggY2xlYXIgJ1llcycvJ05vJyBsYWJlbHMgZm9yIGJldHRlciBpbnRlcnByZXRhYmlsaXR5IGluIG91ciByZXN1bHRzLgoKRHVyaW5nIG91ciBpbml0aWFsIGBzdW1tYXJ5KClgLCB3ZSBub3RlZCB0aGF0IHRoZSAnT3RoZXInIGNhdGVnb3J5IGluIGBnZW5kZXJgIGhhcyB2ZXJ5IGZldyBvYnNlcnZhdGlvbnMgKG9ubHkgMTgpLiBUbyBwcmV2ZW50IG1vZGVsIGluc3RhYmlsaXR5LCB3ZSB3aWxsIHJlbW92ZSB0aGVzZSBvYnNlcnZhdGlvbnMuIFdlIHRoZW4gdXNlIGBkcm9wbGV2ZWxzKClgIHRvIHJlbW92ZSAnT3RoZXInIGZyb20gdGhlIGZhY3RvciBsZXZlbHMuCgpgYGB7ciBmYWN0b3ItY29udmVyc2lvbn0KIyBGaWx0ZXIgb3V0ICdPdGhlcicgZ2VuZGVyIGNhdGVnb3J5CmRpYWJldGVzLmRhdGEuY2xlYW4gPC0gZGlhYmV0ZXMuZGF0YSAlPiUKICBmaWx0ZXIoZ2VuZGVyICE9ICJPdGhlciIpCgojIENvbnZlcnQgY2hhcmFjdGVyIGNvbHVtbnMgYW5kIHRhcmdldCB0byBmYWN0b3JzCmRpYWJldGVzLmRhdGEuY2xlYW4gPC0gZGlhYmV0ZXMuZGF0YS5jbGVhbiAlPiUKICBtdXRhdGUoCiAgICBkaWFiZXRlcyA9IGZhY3RvcihkaWFiZXRlcywKICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoMCwgMSksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJObyIsICJZZXMiKSksCiAgICBnZW5kZXIgPSBhcy5mYWN0b3IoZ2VuZGVyKSwKICAgIHNtb2tpbmdfaGlzdG9yeSA9IGFzLmZhY3RvcihzbW9raW5nX2hpc3RvcnkpCiAgKQoKIyBSZW1vdmUgJ090aGVyJyBsZXZlbCBmcm9tIHRoZSBmYWN0b3IKZGlhYmV0ZXMuZGF0YS5jbGVhbiRnZW5kZXIgPC0gZHJvcGxldmVscyhkaWFiZXRlcy5kYXRhLmNsZWFuJGdlbmRlcikKCiMgQ2hlY2sgdGhlIHN0cnVjdHVyZSB0byBjb25maXJtIGFsbCBjaGFuZ2VzIGhhdmUgYmVlbiBhcHBsaWVkCnN0cihkaWFiZXRlcy5kYXRhLmNsZWFuKQpgYGAKCiMjIDEuMyBFeHBsb3JhdG9yeSBEYXRhIFZpc3VhbGl6YXRpb24KCk5vdyB0aGF0IHRoZSBkYXRhIGlzIGNsZWFuLCB3ZSBjYW4gcGVyZm9ybSB2aXN1YWwgRURBIHRvIHVuZGVyc3RhbmQgdGhlIGRpc3RyaWJ1dGlvbnMgYW5kIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB2YXJpYWJsZXMuCgojIyMgUGFpci13aXNlIFNjYXR0ZXIgUGxvdApXZSBmaXJzdCBjcmVhdGUgYSBwYWlyLXdpc2Ugc2NhdHRlciBwbG90IG9mIHRoZSBudW1lcmljYWwgdmFyaWFibGVzIHRvIGNoZWNrIGZvciBjb3JyZWxhdGlvbnMuCgpgYGB7ciBwbG90LXBhaXJzLCBmaWcuY2FwPSJQYWlyLXdpc2UgU2NhdHRlciBQbG90IG9mIE51bWVyaWNhbCBWYXJpYWJsZXMifQojIFNlbGVjdCBudW1lcmljIHZhcmlhYmxlcyBmcm9tIG91ciBjbGVhbmVkIGRhdGEKbnVtZXJpY192YXJzX2RmIDwtIGRpYWJldGVzLmRhdGEuY2xlYW5bLCBjKCJhZ2UiLCAiYm1pIiwgIkhiQTFjX2xldmVsIiwgImJsb29kX2dsdWNvc2VfbGV2ZWwiKV0KCiMgQ3JlYXRlIHRoZSBwYWlycyBwbG90CnBhaXJzKG51bWVyaWNfdmFyc19kZiwgY2V4ID0gMC4zLCBjb2wgPSAibmF2eSIsIAogICAgICBtYWluID0gIlBhaXItd2lzZSBTY2F0dGVyIFBsb3Qgb2YgTnVtZXJpY2FsIFZhcmlhYmxlcyIpCmBgYAoqKk9ic2VydmF0aW9uOioqIFRoZSBwbG90IHNob3dzIGEgc3Ryb25nIHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gYGJsb29kX2dsdWNvc2VfbGV2ZWxgIGFuZCBgSGJBMWNfbGV2ZWxgLiBUaGUgdmFyaWFibGVzIGBhZ2VgIGFuZCBgYm1pYCBhcmUgY29udGludW91c2x5IGFuZCB3aWRlbHkgZGlzdHJpYnV0ZWQuCgojIyMgS2V5IFZhcmlhYmxlIERpc3RyaWJ1dGlvbnMKTmV4dCwgd2UgcGxvdCB0aGUgZGlzdHJpYnV0aW9ucyBvZiBzZXZlcmFsIGtleSBwcmVkaWN0b3JzIGFuZCB0aGUgb3V0Y29tZSB2YXJpYWJsZS4KCmBgYHtyIHBsb3QtZGlzdHJpYnV0aW9ucy1ncmlkLCBmaWcuY2FwPSJEaXN0cmlidXRpb25zIG9mIEtleSBWYXJpYWJsZXMifQojIFNldCB1cCBhIDJ4MiBwbG90dGluZyBncmlkCnBhcihtZnJvdyA9IGMoMiwgMikpCgojIDEuIEFnZQpoaXN0KGRpYWJldGVzLmRhdGEuY2xlYW4kYWdlLCB4bGFiID0gIkFnZSIsIGNvbCA9ICJzdGVlbGJsdWUiLCBtYWluID0gIkFnZSBEaXN0cmlidXRpb24iKQoKIyAyLiBHZW5kZXIKYmFycGxvdCh0YWJsZShkaWFiZXRlcy5kYXRhLmNsZWFuJGdlbmRlciksIGNvbCA9ICJzdGVlbGJsdWUiLCBtYWluID0gIkRpc3RyaWJ1dGlvbiBvZiBHZW5kZXIiKQoKIyAzLiBCTUkKaGlzdChkaWFiZXRlcy5kYXRhLmNsZWFuJGJtaSwgeGxhYiA9ICJCTUkiLCBjb2wgPSAic3RlZWxibHVlIiwgbWFpbiA9ICJCTUkgRGlzdHJpYnV0aW9uIikKCiMgNC4gU21va2luZyBIaXN0b3J5CmJhcnBsb3QodGFibGUoZGlhYmV0ZXMuZGF0YS5jbGVhbiRzbW9raW5nX2hpc3RvcnkpLCBjb2wgPSAic3RlZWxibHVlIiwgbWFpbiA9ICJTbW9raW5nIEhpc3RvcnkiKQoKIyBSZXNldCBwbG90dGluZyBncmlkCnBhcihtZnJvdyA9IGMoMSwgMSkpCmBgYAoqKk9ic2VydmF0aW9uOioqIFRoZSBkYXRhc2V0IGlzIGRpdmVyc2UgaW4gYGFnZWAgYW5kIGBibWlgLiBNb3N0IHBhcnRpY2lwYW50cyBhcmUgJ0ZlbWFsZScsIGFuZCBhIGxhcmdlIHBvcnRpb24gaGF2ZSAnbm8gaW5mbycgb3IgJ25ldmVyJyBmb3Igc21va2luZyBoaXN0b3J5LgoKIyMjIEdsdWNvc2UgRGlzdHJpYnV0aW9uIGJ5IERpYWJldGVzIFN0YXR1cwpGaW5hbGx5LCB3ZSBzaG93IGEgZGVuc2l0eSBwbG90IHRvIGNvbXBhcmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBgYmxvb2RfZ2x1Y29zZV9sZXZlbGAgZm9yIHRob3NlIHdpdGggYW5kIHdpdGhvdXQgZGlhYmV0ZXMuCgpgYGB7ciBwbG90LWdsdWNvc2UtZGVuc2l0eSwgZmlnLmNhcD0iRGlzdHJpYnV0aW9uIG9mIEdsdWNvc2UgTGV2ZWxzIGJ5IERpYWJldGVzIFN0YXR1cyJ9CiMgR2V0IGdsdWNvc2UgZm9yIGRpYWJldGljIGFuZCBub24tZGlhYmV0aWMgZ3JvdXBzCmRpYWIuZ2x1Y29zZSA8LSBkaWFiZXRlcy5kYXRhLmNsZWFuJGJsb29kX2dsdWNvc2VfbGV2ZWxbZGlhYmV0ZXMuZGF0YS5jbGVhbiRkaWFiZXRlcyA9PSAiWWVzIl0Kbm8uZGlhYi5nbHVjb3NlIDwtIGRpYWJldGVzLmRhdGEuY2xlYW4kYmxvb2RfZ2x1Y29zZV9sZXZlbFtkaWFiZXRlcy5kYXRhLmNsZWFuJGRpYWJldGVzID09ICJObyJdCgojIFBsb3QgdGhlIGRlbnNpdGllcwpwbG90KGRlbnNpdHkobm8uZGlhYi5nbHVjb3NlKSwgY29sID0gImRhcmtncmVlbiIsIAogICAgIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIEdsdWNvc2UgTGV2ZWxzIiwgeGxhYiA9ICJHbHVjb3NlIiwgeGxpbSA9IHJhbmdlKGRpYWJldGVzLmRhdGEuY2xlYW4kYmxvb2RfZ2x1Y29zZV9sZXZlbCkpCmxpbmVzKGRlbnNpdHkoZGlhYi5nbHVjb3NlKSwgY29sID0gImRhcmtyZWQiLCBsd2QgPSAyKQpsZWdlbmQoInRvcHJpZ2h0IiwgYygiRGlhYmV0ZXMgR3JvdXAiLCAiRGlhYmV0ZXMgRnJlZSIpLAogICAgICAgY29sID0gYygiZGFya3JlZCIsICJkYXJrZ3JlZW4iKSwgbHdkID0gYygyLCAxKSwgYnR5ID0gIm4iKQpgYGAKKipPYnNlcnZhdGlvbjoqKiBBcyBleHBlY3RlZCwgdGhlIGRpc3RyaWJ1dGlvbnMgYXJlIHZlcnkgZGlmZmVyZW50LiBUaGUgIkRpYWJldGVzIEZyZWUiIGdyb3VwIGlzIHRpZ2h0bHkgY2x1c3RlcmVkIGFyb3VuZCBsb3dlciBnbHVjb3NlIGxldmVscywgd2hpbGUgdGhlICJEaWFiZXRlcyBHcm91cCIgaGFzIGEgbXVjaCBoaWdoZXIgbWVhbiBhbmQgd2lkZXIgc3ByZWFkLiBUaGlzIGNvbmZpcm1zIGl0IHdpbGwgYmUgYSBzdHJvbmcgcHJlZGljdG9yLgoKLS0tLS0KCiMgUGFydCAyOiBNb2RlbCBEZXZlbG9wbWVudAoKTm93IHdlIGRldmVsb3Agb3VyIGNhbmRpZGF0ZSBtb2RlbHMuIFRoaXMgaW52b2x2ZXMgYnVpbGRpbmcgbXVsdGlwbGUgYGdsbWAgbW9kZWxzLCBvbmUgYG5ldXJhbG5ldGAgbW9kZWwsIG9uZSBgcnBhcnRgIChkZWNpc2lvbiB0cmVlKSBtb2RlbCwgYW5kIG9uZSBgYmFnZ2luZ2AgbW9kZWwgb24gdGhlICoqZnVsbCBkYXRhc2V0KiogdG8gY29tcGFyZSB0aGVpciBwZXJmb3JtYW5jZS4KCiMjIDIuMSBMb2dpc3RpYyBSZWdyZXNzaW9uIE1vZGVscyAoQmFzZWxpbmUpCgpXZSB3aWxsIGJ1aWxkIHRocmVlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzIHRvIHNlcnZlIGFzIG91ciBiYXNlbGluZSBhbmQgZm9yIGNvbXBhcmlzb24uCgojIyMgTG9naXN0aWMgKFNpZ21vaWQpIEN1cnZlClRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHdvcmtzIGJ5IHRyYW5zZm9ybWluZyBhIGxpbmVhciBjb21iaW5hdGlvbiBvZiBpbnB1dHMgKCR6JCkgdGhyb3VnaCB0aGUgKipsb2dpc3RpYyBmdW5jdGlvbioqIChhbHNvIGNhbGxlZCBhICoqc2lnbW9pZCBmdW5jdGlvbioqKSB0byBwcm9kdWNlIGEgcHJvYmFiaWxpdHkgYmV0d2VlbiAwIGFuZCAxLiBXZSBjYW4gdmlzdWFsaXplIHRoaXMgZnVuY3Rpb24uCgpgYGB7ciBwbG90LWxvZ2lzdGljLWN1cnZlLCBmaWcuY2FwPSJUaGUgTG9naXN0aWMgKFNpZ21vaWQpIEZ1bmN0aW9uIn0KeiA8LSBzZXEoLTYsIDYsIGxlbmd0aCA9IDEwMCkKcGlfeiA8LSAxIC8gKDEgKyBleHAoLXopKQpwbG90KHosIHBpX3osIHR5cGUgPSAibCIsIGx3ZCA9IDIsIHhsYWIgPSAieiAoTGluZWFyIENvbWJpbmF0aW9uKSIsIHlsYWIgPSBleHByZXNzaW9uKHBpKHopKSwKICAgICBtYWluID0gIkxvZ2lzdGljIEN1cnZlIiwgY29sID0gImJsdWUiKQp0ZXh0KC0zLCAwLjgsIGV4cHJlc3Npb24ocGkoeikgPT0gZnJhYygxLCAxICsgZXhwKC16KSkpLCBjb2wgPSAiYmx1ZSIpCmBgYAoKIyMjIE1vZGVsIEJ1aWxkaW5nCldlIGJ1aWxkIHRocmVlIGNhbmRpZGF0ZSBtb2RlbHM6CjEuICAqKmByZWR1Y2VkTW9kZWxgKio6IEEgc2ltcGxlIG1vZGVsIHdpdGggb25seSB0aGUgdmFyaWFibGVzIHdlIGh5cG90aGVzaXplIGFyZSBtb3N0IGNsaW5pY2FsbHkgc2lnbmlmaWNhbnQuCjIuICAqKmBmdWxsTW9kZWxgKio6IEEgY29tcGxleCBtb2RlbCB0aGF0IGluY2x1ZGVzIGFsbCBhdmFpbGFibGUgcHJlZGljdG9ycy4KMy4gICoqYGZvcndhcmRzYCoqOiBBbiBvcHRpbWl6ZWQgbW9kZWwgZm91bmQgdXNpbmcgZm9yd2FyZCBzZWxlY3Rpb24sIHN0YXJ0aW5nIGZyb20gdGhlIGByZWR1Y2VkTW9kZWxgIGFuZCBhZGRpbmcgcHJlZGljdG9ycyBmcm9tIHRoZSBgZnVsbE1vZGVsYCBiYXNlZCBvbiBBSUMuCgpgYGB7ciBtb2RlbHN9CiMgRGVmaW5lIGEgcmVkdWNlZCBtb2RlbCB3aXRoIHZhcmlhYmxlcyB3ZSBhc3N1bWUgYXJlIHNpZ25pZmljYW50CnJlZHVjZWRNb2RlbCA8LSBnbG0oZGlhYmV0ZXMgfiBhZ2UgKyBibWkgKyBibG9vZF9nbHVjb3NlX2xldmVsICsgSGJBMWNfbGV2ZWwsCiAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwobGluayA9IGxvZ2l0KSwKICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGlhYmV0ZXMuZGF0YS5jbGVhbikKCiMgRGVmaW5lIHRoZSBmdWxsIG1vZGVsIHdpdGggYWxsIHZhcmlhYmxlcwpmdWxsTW9kZWwgPC0gZ2xtKGRpYWJldGVzIH4gLiwKICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gbG9naXQpLAogICAgICAgICAgICAgICAgIGRhdGEgPSBkaWFiZXRlcy5kYXRhLmNsZWFuKQoKIyBVc2UgZm9yd2FyZCBzZWxlY3Rpb24gdG8gZmluZCB0aGUgYmVzdCBtb2RlbCBiZXR3ZWVuIHJlZHVjZWQgYW5kIGZ1bGwKZm9yd2FyZHMgPC0gc3RlcChyZWR1Y2VkTW9kZWwsCiAgICAgICAgICAgICAgICAgc2NvcGUgPSBsaXN0KGxvd2VyID0gZm9ybXVsYShyZWR1Y2VkTW9kZWwpLCB1cHBlciA9IGZvcm11bGEoZnVsbE1vZGVsKSksCiAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gImZvcndhcmQiLAogICAgICAgICAgICAgICAgIHRyYWNlID0gRkFMU0UpCgojIERpc3BsYXkgdGhlIHN1bW1hcnkgb2YgdGhlIGZpbmFsLCBmb3J3YXJkLXNlbGVjdGVkIG1vZGVsCnN1bW1hcnkoZm9yd2FyZHMpCmBgYAoKIyMgMi4yIFBlcmNlcHRyb24gKFNpbmdsZS1MYXllciBOZXVyYWwgTmV0d29yaykKClRoZSBwZXJjZXB0cm9uIGlzIGEgc2luZ2xlLWxheWVyIG5ldXJhbCBuZXR3b3JrLiBXaGVuIGl0IHVzZXMgYSBsb2dpc3RpYyAoc2lnbW9pZCkgYWN0aXZhdGlvbiBmdW5jdGlvbiwgaXQgaXMgbWF0aGVtYXRpY2FsbHkgZXF1aXZhbGVudCB0byBsb2dpc3RpYyByZWdyZXNzaW9uLgoKIyMjIERhdGEgUHJlcCBmb3IgYG5ldXJhbG5ldGAKTmV1cmFsIG5ldHdvcmtzIGFyZSBzZW5zaXRpdmUgdG8gdGhlIHNjYWxlIG9mIGlucHV0IGRhdGEgYW5kIHJlcXVpcmUgYWxsIGlucHV0cyB0byBiZSBudW1lcmljLiBXZSBtdXN0IGZpcnN0IHNjYWxlIG91ciBudW1lcmljIGZlYXR1cmVzICh1c2luZyBtaW4tbWF4IG5vcm1hbGl6YXRpb24pIGFuZCBjcmVhdGUgYSBkZXNpZ24gbWF0cml4IChkdW1taWZ5KSBmb3IgdGhlIGBuZXVyYWxuZXRgIGZ1bmN0aW9uLgoKYGBge3IgbWFudWFsLXNjYWxpbmd9CiMgQ3JlYXRlIGEgY29weSBvZiB0aGUgZGF0YSBmb3IgbmV1cmFsIG5ldHdvcmsgcHJlcHJvY2Vzc2luZwpuZXVyYWxEYXRhIDwtIGRpYWJldGVzLmRhdGEuY2xlYW4KCiMgSWRlbnRpZnkgbnVtZXJpYyB2YXJpYWJsZXMgZm9yIHNjYWxpbmcKbnVtZXJpYy52YXJzIDwtIGMoImFnZSIsICJibWkiLCAiSGJBMWNfbGV2ZWwiLCAiYmxvb2RfZ2x1Y29zZV9sZXZlbCIpCgojIExvb3AgdGhyb3VnaCBudW1lcmljIHZhcmlhYmxlcywgc2NhbGUgdGhlbSB1c2luZyBtaW4tbWF4IG5vcm1hbGl6YXRpb24KZm9yIChjb2wgaW4gbnVtZXJpYy52YXJzKSB7CiAgbWluLnZhbCA8LSBtaW4obmV1cmFsRGF0YVtbY29sXV0pCiAgbWF4LnZhbCA8LSBtYXgobmV1cmFsRGF0YVtbY29sXV0pCiAgCiAgIyBUaGUgbWluLW1heCBmb3JtdWxhCiAgbmV1cmFsRGF0YVtbY29sXV0gPC0gKG5ldXJhbERhdGFbW2NvbF1dIC0gbWluLnZhbCkgLyAobWF4LnZhbCAtIG1pbi52YWwpCn0KYGBgCgpOZXh0LCB3ZSB1c2UgYG1vZGVsLm1hdHJpeCgpYCB0byBhdXRvbWF0aWNhbGx5IGNyZWF0ZSBkdW1teSB2YXJpYWJsZXMgZm9yIGFsbCBvdXIgZmFjdG9ycyBhbmQgYnVpbGQgYSBmb3JtdWxhIHN0cmluZy4KCmBgYHtyIG5uLWZvcm11bGF9CiMgQ3JlYXRlIHRoZSBkZXNpZ24gbWF0cml4LCB3aGljaCBhdXRvbWF0aWNhbGx5IGR1bW1pZmllcyBmYWN0b3IgdmFyaWFibGVzIApuZXVyYWxEYXRhLm1hdHJpeCA8LSBtb2RlbC5tYXRyaXgofiAuLCBkYXRhID0gbmV1cmFsRGF0YSkKbmV1cmFsRGF0YS5ubiA8LSBhcy5kYXRhLmZyYW1lKG5ldXJhbERhdGEubWF0cml4KQoKIyBDbGVhbiB0aGUgY29sdW1uIG5hbWVzIHRvIG1ha2UgdGhlbSB2YWxpZCBSIHZhcmlhYmxlcwp2YWxpZC5uYW1lcyA8LSBtYWtlLm5hbWVzKGNvbG5hbWVzKG5ldXJhbERhdGEubm4pKQpjb2xuYW1lcyhuZXVyYWxEYXRhLm5uKSA8LSB2YWxpZC5uYW1lcwoKIyBBZGQgdGhlIG51bWVyaWMgcmVzcG9uc2UgdmFyaWFibGUgKDAvMSkgZm9yIG5ldXJhbG5ldApuZXVyYWxEYXRhLm5uJGRpYWJldGVzX251bSA8LSBpZmVsc2UobmV1cmFsRGF0YSRkaWFiZXRlcyA9PSAiWWVzIiwgMSwgMCkKCiMgR2V0IGFsbCBjb2x1bW4gbmFtZXMgZnJvbSB0aGUgbmV3IGRhdGEgZnJhbWUKY29sdW1uTmFtZXMgPC0gY29sbmFtZXMobmV1cmFsRGF0YS5ubikKCiMgQ3JlYXRlIHRoZSBsaXN0IG9mIHByZWRpY3RvcnMgYnkgcmVtb3ZpbmcgdGhlIChJbnRlcmNlcHQpIGFuZCByZXNwb25zZSB2YXJpYWJsZQpjb2x1bW5MaXN0IDwtIHBhc3RlKGNvbHVtbk5hbWVzWy1jKDEsIGxlbmd0aChjb2x1bW5OYW1lcykpXSwgY29sbGFwc2UgPSAiKyIpCgojIENyZWF0ZSB0aGUgZmluYWwgZm9ybXVsYSBzdHJpbmcKbW9kZWxGb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUoImRpYWJldGVzX251bSB+IiwgY29sdW1uTGlzdCkpCgojIFByaW50IHRoZSBmb3JtdWxhIHRvIGNoZWNrCnByaW50KG1vZGVsRm9ybXVsYSkKYGBgCgojIyMgQnVpbGQgYW5kIFBsb3QgUGVyY2VwdHJvbiBNb2RlbApXaXRoIHRoZSBzY2FsZWQgYW5kIGR1bW1pZmllZCBkYXRhLCB3ZSB0cmFpbiB0aGUgcGVyY2VwdHJvbi4KKiBXZSBzZXQgYGhpZGRlbiA9IDFgIGZvciBhIHNpbmdsZS1sYXllciBuZXR3b3JrLgoqIFdlIHVzZSBgYWN0LmZjdCA9ICJsb2dpc3RpYyJgICh0aGUgc2lnbW9pZCBmdW5jdGlvbikuCiogV2Ugc2V0IGBsaW5lYXIub3V0cHV0ID0gRkFMU0VgIGZvciBjbGFzc2lmaWNhdGlvbi4KCmBgYHtyIG5uLXRyYWlufQojIFRyYWluIHRoZSBwZXJjZXB0cm9uCnNldC5zZWVkKDEyMykKcGVyY2VwdHJvbi5tb2RlbCA8LSBuZXVyYWxuZXQobW9kZWxGb3JtdWxhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gbmV1cmFsRGF0YS5ubiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGlkZGVuID0gMSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjdC5mY3QgPSAibG9naXN0aWMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5lYXIub3V0cHV0ID0gRkFMU0UpIApgYGAKCldlIGNhbiBub3cgcGxvdCB0aGUgcmVzdWx0aW5nIG5ldHdvcmsgZGlhZ3JhbS4KCmBgYHtyIHBsb3Qtbm4sIGZpZy5jYXA9IlBlcmNlcHRyb24gTmV0d29yayBEaWFncmFtIn0KIyBDcmVhdGUgYSB2aXN1YWwgcmVwcmVzZW50YXRpb24gb2YgdGhlIHBlcmNlcHRyb24gbW9kZWwKcGxvdChwZXJjZXB0cm9uLm1vZGVsLCByZXAgPSAiYmVzdCIpCmBgYAoqKk9ic2VydmF0aW9uOioqIFRoZSBwbG90IGRpc3BsYXlzIGEgdHdvLWxheWVyIG5ldXJhbCBuZXR3b3JrLiBUaGUgaW5wdXQgdmFyaWFibGVzIChlLmcuLCBgZ2VuZGVyTWFsZWAsIGBhZ2VgLCBgYm1pYCkgYXJlIG9uIHRoZSBsZWZ0LiBFYWNoIGlucHV0IGlzIGNvbm5lY3RlZCB0byBhIHNpbmdsZSBoaWRkZW4gbm9kZSAodGhlIGZpcnN0IGNpcmNsZSksIHdpdGggdGhlIGNvbm5lY3RpbmcgbGluZXMgc2hvd2luZyB0aGVpciByZXNwZWN0aXZlIHdlaWdodHMuIFRoaXMgaGlkZGVuIG5vZGUgYWxzbyByZWNlaXZlcyBhIGJpYXMgaW5wdXQgKGZyb20gdGhlIHRvcC1sZWZ0ICIxIiB3aXRoIHdlaWdodCAtOS4yNTMxNSkuCgpUaGUgb3V0cHV0IG9mIHRoaXMgaGlkZGVuIG5vZGUgKHNob3duIGFzIDE0Ljc1NTMxKSBpcyB0aGVuIGZlZCBpbnRvIGEgZmluYWwgb3V0cHV0IG5vZGUuIFRoaXMgb3V0cHV0IG5vZGUgcmVjZWl2ZXMgaXRzIG93biBiaWFzIGlucHV0IChmcm9tIHRoZSB0b3AtcmlnaHQgIjEiIHdpdGggd2VpZ2h0IC03Ljg0MikgYW5kIHByb2R1Y2VzIHRoZSBmaW5hbCBgZGlhYmV0ZXNfbnVtYCBwcmVkaWN0aW9uLgoKIyMgMi4zIERlY2lzaW9uIFRyZWUgTW9kZWwKCldlIHdpbGwgdXNlIHRoZSBgcnBhcnRgIGxpYnJhcnkgdG8gYnVpbGQgb3VyIGRlY2lzaW9uIHRyZWUuIFRoaXMgYWxnb3JpdGhtIGdlbmVyYXRlcyBhIHNldCBvZiBydWxlcyB0aGF0IGFyZSBlYXN5IHRvIGludGVycHJldC4KCmBgYHtyIGJ1aWxkLXRyZWV9CiMgQnVpbGQgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwKIyBXZSB1c2UgbWV0aG9kID0gImNsYXNzIiBmb3IgYSBjbGFzc2lmaWNhdGlvbiB0cmVlCnRyZWVfbW9kZWwgPC0gcnBhcnQoCiAgZm9ybXVsYSA9IGRpYWJldGVzIH4gLiwKICBkYXRhID0gZGlhYmV0ZXMuZGF0YS5jbGVhbiwKICBtZXRob2QgPSAiY2xhc3MiLAogICMgV2UgdXNlIHRoZSBkZWZhdWx0IGNvbnRyb2wgcGFyYW1ldGVycwogIGNvbnRyb2wgPSBycGFydC5jb250cm9sKG1pbnNwbGl0ID0gMjAsIGNwID0gMC4wMSkKKQoKIyBQcmludCB0aGUgbW9kZWwgc3VtbWFyeQpwcmludCh0cmVlX21vZGVsKQpgYGAKCiMjIyBWaXN1YWxpemUgdGhlIFRyZWUKV2UgdXNlIGBycGFydC5wbG90YCB0byBjcmVhdGUgYSBjbGVhbiB2aXN1YWwgb2YgdGhlIHRyZWUgc3RydWN0dXJlLgoKYGBge3IgcGxvdC10cmVlLCBmaWcuY2FwPSJEZWNpc2lvbiBUcmVlIGZvciBEaWFiZXRlcyBQcmVkaWN0aW9uIn0KIyBQbG90IHRoZSB0cmVlCnJwYXJ0LnBsb3QodHJlZV9tb2RlbCwgbWFpbiA9ICJEZWNpc2lvbiBUcmVlIGZvciBEaWFiZXRlcyBQcmVkaWN0aW9uIikKYGBgCgoqKk9ic2VydmF0aW9uOioqIFRoZSB0cmVlIGZpcnN0IHNwbGl0cyB0aGUgZGF0YSBiYXNlZCBvbiBhbiBgSGJBMWNfbGV2ZWxgIHRocmVzaG9sZC4gSWYgYSBwYXRpZW50J3MgbGV2ZWwgaXMgaGlnaCwgdGhlIG1vZGVsIGltbWVkaWF0ZWx5IHByZWRpY3RzICdZZXMnIGZvciBkaWFiZXRlcy4gRm9yIHBhdGllbnRzIHdpdGggYSBsb3dlciBgSGJBMWNgIGxldmVsLCB0aGUgbW9kZWwgYXNrcyBhIHNlY29uZCBxdWVzdGlvbiBhYm91dCB0aGVpciBgYmxvb2RfZ2x1Y29zZV9sZXZlbGAuIFRoaXMgc2hvd3MgdGhlIGhpZXJhcmNoeSBvZiBwcmVkaWN0b3IgaW1wb3J0YW5jZS4KCiMjIDIuNCBCQUdHSU5HIChCb290c3RyYXAgQWdncmVnYXRpb24pIEFsZ29yaXRobQoKQkFHR0lORyAoQm9vdHN0cmFwIEFnZ3JlZ2F0aW9uKSBpcyBhbiBlbnNlbWJsZSBtZXRob2QgdGhhdCBidWlsZHMgbXVsdGlwbGUgZGVjaXNpb24gdHJlZXMgb24gZGlmZmVyZW50IGJvb3RzdHJhcCBzYW1wbGVzIG9mIHRoZSBkYXRhIGFuZCBhZ2dyZWdhdGVzIHRoZWlyIHByZWRpY3Rpb25zICh1c2luZyAibWFqb3JpdHkgdm90aW5nIikuIFRoaXMgcHJvY2VzcyBpcyB1c2VkIHRvIGRlY3JlYXNlIHRoZSB2YXJpYW5jZSBpbiB0aGUgcHJlZGljdGlvbiBtb2RlbC4KCldlIHdpbGwgdXNlIHRoZSBgYmFnZ2luZygpYCBmdW5jdGlvbiBmcm9tIHRoZSBgaXByZWRgIGxpYnJhcnkuCgpgYGB7ciBidWlsZC1iYWdnaW5nfQojIEJ1aWxkIHRoZSBCQUdHSU5HIGVuc2VtYmxlIG1vZGVsCnNldC5zZWVkKDEyMykgIyBGb3IgcmVwcm9kdWNpYmlsaXR5CmJhZ2dpbmdfbW9kZWwgPC0gYmFnZ2luZygKICBmb3JtdWxhID0gYXMuZmFjdG9yKGRpYWJldGVzKSB+IC4sCiAgZGF0YSA9IGRpYWJldGVzLmRhdGEuY2xlYW4sCiAgbmJhZ2cgPSAxNTAsICAgICAgIyBudW1iZXIgb2YgdHJlZXMKICBjb29iID0gVFJVRSwgICAgICAKICBwYXJtcyA9IGxpc3QobG9zcyA9IG1hdHJpeChjKDAsIDEwLCAxLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBieXJvdyA9IFRSVUUpLCAKICAgICAgICAgICAgICAgc3BsaXQgPSAiZ2luaSIpLCAKICBjb250cm9sID0gcnBhcnQuY29udHJvbChtaW5zcGxpdCA9IDEwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBjcCA9IDAuMDIpICAgICAKKQoKIyBQcmludCB0aGUgbW9kZWwgc3VtbWFyeQpwcmludChiYWdnaW5nX21vZGVsKQpgYGAKCiMjIyBCb290c3RyYXAgQVVDIERpc3RyaWJ1dGlvbgpUbyB2aXN1YWxpemUgdGhlIGVmZmVjdCBvZiBiYWdnaW5nLCB3ZSBjYW4gcnVuIGEgc21hbGxlciBib290c3RyYXAgbG9vcCAoQj0xMDApIHRvIGJ1aWxkIDEwMCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyBhbmQgcGxvdCB0aGUgaGlzdG9ncmFtIG9mIHRoZWlyIEFVQ3MuCgpgYGB7ciBiYWdnaW5nLWF1Yy1oaXN0LCBmaWcuY2FwPSJCb290c3RyYXAgU2FtcGxpbmcgRGlzdHJpYnV0aW9uIG9mIEFVQ3MgKEI9MTAwKSJ9CgojIFdlIHdpbGwgdXNlIEI9MTAwIGZvciBzcGVlZC4KYnRBVUMudmVjIDwtIGMoKQpCIDwtIDEwMApzYW1wbGUuc2l6ZSA8LSBkaW0oZGlhYmV0ZXMuZGF0YS5jbGVhbilbMV0Kc2V0LnNlZWQoNDU2KSAjIE5ldyBzZWVkIGZvciB0aGlzIGxvb3AKCmZvciAoayBpbiAxOkIpIHsKICAjIDEuIEdldCBib290c3RyYXAgc2FtcGxlCiAgYm9vdC5pZCA8LSBzYW1wbGUoMTpzYW1wbGUuc2l6ZSwgc2FtcGxlLnNpemUsIHJlcGxhY2UgPSBUUlVFKQogIGJvb3Quc2FtcGxlIDwtIGRpYWJldGVzLmRhdGEuY2xlYW5bYm9vdC5pZCwgXQogIAogICMgMi4gRml0IG1vZGVsIG9uIGJvb3RzdHJhcCBzYW1wbGUKICBib290LmxvZ2lzdGljIDwtIGdsbShkaWFiZXRlcyB+IC4sIGZhbWlseSA9IGJpbm9taWFsLCBkYXRhID0gYm9vdC5zYW1wbGUpCiAgCiAgIyAzLiBHZXQgcHJlZGljdGlvbnMgCiAgcHJlZC5wcm9iIDwtIHByZWRpY3QuZ2xtKGJvb3QubG9naXN0aWMsIG5ld2RhdGEgPSBib290LnNhbXBsZSwgdHlwZSA9ICJyZXNwb25zZSIpCiAgCiAgIyA0LiBDYWxjdWxhdGUgYW5kIHN0b3JlIEFVQwogIGNhdGVnb3J5IDwtIGJvb3Quc2FtcGxlJGRpYWJldGVzID09ICJZZXMiCiAgUk9Db2JqIDwtIHJvYyhjYXRlZ29yeSwgcHJlZC5wcm9iLCBxdWlldCA9IFRSVUUpCiAgYnRBVUMudmVjW2tdIDwtIGF1YyhST0NvYmopCn0KCiMgUGxvdCB0aGUgaGlzdG9ncmFtIG9mIHRoZSAxMDAgYm9vdHN0cmFwIEFVQ3MKaGlzdChidEFVQy52ZWMsIHhsYWIgPSAiQm9vdHN0cmFwIEFVQyIsIG1haW4gPSAiQm9vdHN0cmFwIFNhbXBsaW5nIERpc3RyaWJ1dGlvbiBvZiBBVUNzIFxuIChCPTEwMCkiKQpgYGAKKipPYnNlcnZhdGlvbjoqKiBUaGlzIGhpc3RvZ3JhbSBzaG93cyB0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIG9mIHRoZSBBVUMuIFdlIGNhbiBzZWUgYSBjbGVhciBjZW50cmFsIHRlbmRlbmN5LCBhbmQgdGhlIHNwcmVhZCBvZiB0aGlzIGRpc3RyaWJ1dGlvbiBnaXZlcyB1cyBhbiBpZGVhIG9mIHRoZSBtb2RlbCdzIHN0YWJpbGl0eS4gV2UgY291bGQgdXNlIHRoaXMgdG8gZm9ybSBhIDk1JSBjb25maWRlbmNlIGludGVydmFsLgoKLS0tLS0KCiMgUGFydCAzOiBGaW5hbCBNb2RlbCBDb21wYXJpc29uIGFuZCBTZWxlY3Rpb24KCk5vdyB0aGF0IGFsbCBjYW5kaWRhdGUgbW9kZWxzIGhhdmUgYmVlbiBidWlsdCwgd2Ugd2lsbCBwZXJmb3JtIGEgY29tcHJlaGVuc2l2ZSBjb21wYXJpc29uIHRvIHNlbGVjdCB0aGUgYmVzdCBvbmUuIEFzIHBlciB0aGUgcHJvamVjdCBpbnN0cnVjdGlvbnMgYW5kIGZlZWRiYWNrLCB3ZSB3aWxsIHVzZSAqKlJPQy1BVUMgYW5hbHlzaXMqKiB0byBjb21wYXJlIHRoZSBnbG9iYWwgcGVyZm9ybWFuY2Ugb2YgYWxsIG1vZGVscy4KCldlIHdpbGwgZ2VuZXJhdGUgcHJlZGljdGlvbnMgZnJvbSBhbGwgbW9kZWxzIGFuZCBwbG90IHRoZWlyIFJPQyBjdXJ2ZXMgb24gYSBzaW5nbGUgZ3JhcGggZm9yIGEgZGlyZWN0LCBzeW50aGVzaXplZCBjb21wYXJpc29uLgoKYGBge3IgZmluYWwtcm9jLWNvbXBhcmlzb24sIGZpZy5jYXA9IkNvbXByZWhlbnNpdmUgTW9kZWwgQ29tcGFyaXNvbiAoUk9DKSJ9CiMgMS4gR2V0IHByZWRpY3Rpb25zIChhcyBwcm9iYWJpbGl0aWVzKSBmb3IgYWxsIG1vZGVscwpwcmVkUmVkdWNlZCA8LSBwcmVkaWN0KHJlZHVjZWRNb2RlbCwgbmV3ZGF0YSA9IGRpYWJldGVzLmRhdGEuY2xlYW4sIHR5cGUgPSAicmVzcG9uc2UiKQpwcmVkRnVsbCA8LSBwcmVkaWN0KGZ1bGxNb2RlbCwgbmV3ZGF0YSA9IGRpYWJldGVzLmRhdGEuY2xlYW4sIHR5cGUgPSAicmVzcG9uc2UiKQpwcmVkRm9yd2FyZHMgPC0gcHJlZGljdChmb3J3YXJkcywgbmV3ZGF0YSA9IGRpYWJldGVzLmRhdGEuY2xlYW4sIHR5cGUgPSAicmVzcG9uc2UiKQoKIyBQZXJjZXB0cm9uIG1vZGVsIHByZWRpY3Rpb25zICh1c2VzIHRoZSBzY2FsZWQgbmV1cmFsRGF0YS5ubikKcHJlZE5OLnJhdyA8LSBwcmVkaWN0KHBlcmNlcHRyb24ubW9kZWwsIG5ld2RhdGEgPSBuZXVyYWxEYXRhLm5uKQpwcmVkTk4gPC0gYXMudmVjdG9yKHByZWROTi5yYXcpIAoKIyBEZWNpc2lvbiBUcmVlIG1vZGVsIHByZWRpY3Rpb25zCnRyZWVfcHJvYnMgPC0gcHJlZGljdCh0cmVlX21vZGVsLCBuZXdkYXRhID0gZGlhYmV0ZXMuZGF0YS5jbGVhbiwgdHlwZSA9ICJwcm9iIilbLCAiWWVzIl0KCiMgQkFHR0lORyBtb2RlbCBwcmVkaWN0aW9ucyAocmVxdWlyZXMgdHlwZSA9ICJwcm9iIikKYmFnZ2luZ19wcm9icyA8LSBwcmVkaWN0KGJhZ2dpbmdfbW9kZWwsIG5ld2RhdGEgPSBkaWFiZXRlcy5kYXRhLmNsZWFuLCB0eXBlID0gInByb2IiKQpiYWdnaW5nX3Byb2JzX3llcyA8LSBiYWdnaW5nX3Byb2JzWywgIlllcyJdICMgU2VsZWN0IHRoZSAiWWVzIiBwcm9iYWJpbGl0eQoKIyAyLiBEZWZpbmUgdGhlIHRydWUgY2F0ZWdvcnkgCmNhdGVnb3J5IDwtIGRpYWJldGVzLmRhdGEuY2xlYW4kZGlhYmV0ZXMgPT0gIlllcyIKCiMgMy4gQ3JlYXRlIFJPQyBvYmplY3RzIGZvciBhbGwgbW9kZWxzClJPQ29iai5yZWR1Y2VkIDwtIHJvYyhjYXRlZ29yeSwgcHJlZFJlZHVjZWQsIHF1aWV0ID0gVFJVRSkKUk9Db2JqLmZ1bGwgPC0gcm9jKGNhdGVnb3J5LCBwcmVkRnVsbCwgcXVpZXQgPSBUUlVFKQpST0NvYmouZm9yd2FyZHMgPC0gcm9jKGNhdGVnb3J5LCBwcmVkRm9yd2FyZHMsIHF1aWV0ID0gVFJVRSkKUk9Db2JqLk5OIDwtIHJvYyhjYXRlZ29yeSwgcHJlZE5OLCBxdWlldCA9IFRSVUUpClJPQ29iai50cmVlIDwtIHJvYyhjYXRlZ29yeSwgdHJlZV9wcm9icywgcXVpZXQgPSBUUlVFKQpST0NvYmouYmFnZ2luZyA8LSByb2MoY2F0ZWdvcnksIGJhZ2dpbmdfcHJvYnNfeWVzLCBxdWlldCA9IFRSVUUpCgojIDQuIEdldCBBVUMgdmFsdWVzCmF1Y19yZWR1Y2VkIDwtIGF1YyhST0NvYmoucmVkdWNlZCkKYXVjX2Z1bGwgPC0gYXVjKFJPQ29iai5mdWxsKQphdWNfZm9yd2FyZHMgPC0gYXVjKFJPQ29iai5mb3J3YXJkcykKYXVjX25uIDwtIGF1YyhST0NvYmouTk4pCmF1Y190cmVlIDwtIGF1YyhST0NvYmoudHJlZSkKYXVjX2JhZ2dpbmcgPC0gYXVjKFJPQ29iai5iYWdnaW5nKQoKIyA1LiBQbG90IGFsbCBST0MgY3VydmVzIG9uIG9uZSBncmFwaCBmb3IgY29tcGFyaXNvbgpwbG90KFJPQ29iai5iYWdnaW5nLCBjb2wgPSAicmVkIiwgbWFpbiA9ICJDb21wcmVoZW5zaXZlIE1vZGVsIENvbXBhcmlzb24gKFJPQykiLCBsd2QgPSAyKQpwbG90KFJPQ29iai5mdWxsLCBjb2wgPSAiYmx1ZSIsIGFkZCA9IFRSVUUsIGx0eSA9IDIsIGx3ZCA9IDIpCnBsb3QoUk9Db2JqLmZvcndhcmRzLCBjb2wgPSAiZGFya2dyZWVuIiwgYWRkID0gVFJVRSwgbHR5ID0gMywgbHdkID0gMikKcGxvdChST0NvYmouTk4sIGNvbCA9ICJwdXJwbGUiLCBhZGQgPSBUUlVFLCBsdHkgPSA0LCBsd2QgPSAyKQpwbG90KFJPQ29iai50cmVlLCBjb2wgPSAib3JhbmdlIiwgYWRkID0gVFJVRSwgbHR5ID0gNSwgbHdkID0gMikKcGxvdChST0NvYmoucmVkdWNlZCwgY29sID0gImdyZXkiLCBhZGQgPSBUUlVFLCBsdHkgPSA2LCBsd2QgPSAyKQoKbGVnZW5kKCJib3R0b21yaWdodCIsIAogICAgICAgbGVnZW5kID0gYyhwYXN0ZSgiQkFHR0lORyAoQVVDOiIsIHJvdW5kKGF1Y19iYWdnaW5nLCA0KSwgIikiKSwKICAgICAgICAgICAgICAgICAgcGFzdGUoIkdMTSBGdWxsIChBVUM6Iiwgcm91bmQoYXVjX2Z1bGwsIDQpLCAiKSIpLAogICAgICAgICAgICAgICAgICBwYXN0ZSgiR0xNIEZvcndhcmRzIChBVUM6Iiwgcm91bmQoYXVjX2ZvcndhcmRzLCA0KSwgIikiKSwKICAgICAgICAgICAgICAgICAgcGFzdGUoIlBlcmNlcHRyb24gKEFVQzoiLCByb3VuZChhdWNfbm4sIDQpLCAiKSIpLAogICAgICAgICAgICAgICAgICBwYXN0ZSgiRGVjaXNpb24gVHJlZSAoQVVDOiIsIHJvdW5kKGF1Y190cmVlLCA0KSwgIikiKSwKICAgICAgICAgICAgICAgICAgcGFzdGUoIkdMTSBSZWR1Y2VkIChBVUM6Iiwgcm91bmQoYXVjX3JlZHVjZWQsIDQpLCAiKSIpKSwKICAgICAgIGNvbCA9IGMoInJlZCIsICJibHVlIiwgImRhcmtncmVlbiIsICJwdXJwbGUiLCAib3JhbmdlIiwgImdyZXkiKSwKICAgICAgIGx3ZCA9IDIsCiAgICAgICBsdHkgPSBjKDEsIDIsIDMsIDQsIDUsIDYpKQpgYGAKCioqQW5hbHlzaXM6KiogVGhlIFJPQyBwbG90IGNvbXBhcmVzIHRoZSBwZXJmb3JtYW5jZSBvZiBzaXggbW9kZWxzLiBUaGUgKipQZXJjZXB0cm9uKiogbW9kZWwgKHB1cnBsZSkgc2hvd3MgcGVyZmVjdCBwZXJmb3JtYW5jZSB3aXRoIGFuIEFVQyBvZiAxLCBpdHMgY3VydmUgZm9sbG93aW5nIHRoZSB0b3AtbGVmdCBlZGdlIG9mIHRoZSBwbG90LgoKVGhlIHRocmVlIEdMTSBtb2RlbHMgYWxzbyBwZXJmb3JtIGV4Y2VwdGlvbmFsbHkgd2VsbCBhbmQgYXJlIGNsdXN0ZXJlZCB0b2dldGhlcjogKipHTE0gRnVsbCoqIChBVUMgMC45NjE5KSwgKipHTE0gRm9yd2FyZHMqKiAoQVVDIDAuOTYxOSksIGFuZCAqKkdMTSBSZWR1Y2VkKiogKEFVQyAwLjk1ODYpLgoKVGhlICoqQkFHR0lORyoqIGFuZCAqKkRlY2lzaW9uIFRyZWUqKiBtb2RlbHMgYXJlIHRoZSB3ZWFrZXN0LCBib3RoIGFjaGlldmluZyBhbiBpZGVudGljYWwgYW5kIHNpZ25pZmljYW50bHkgbG93ZXIgQVVDIG9mIDAuODM0NS4KCi0tLS0tCgojIFBhcnQgNDogQ29uY2x1c2lvbgoKKiAqKlBhcnQgMSAoRGF0YSBQcmVwKSoqOiBUaGUgZGF0YSB3YXMgc3VjY2Vzc2Z1bGx5IGxvYWRlZCwgaW5zcGVjdGVkLCBhbmQgcHJlcHJvY2Vzc2VkLiBObyBtaXNzaW5nIHZhbHVlcyB3ZXJlIGZvdW5kLCBhbmQgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHdlcmUgY29ycmVjdGx5IGVuY29kZWQuIFZpc3VhbCBFREEgY29uZmlybWVkIHRoYXQga2V5IHByZWRpY3RvcnMgbGlrZSBgYmxvb2RfZ2x1Y29zZV9sZXZlbGAgYXJlIGhpZ2hseSBjb3JyZWxhdGVkIHdpdGggYGRpYWJldGVzYC4KCiogKipQYXJ0IDIgKE1vZGVsIERldmVsb3BtZW50KSoqOiBXZSBkZXZlbG9wZWQgYWxsIGZvdXIgY2FuZGlkYXRlIG1vZGVscyBhcyBzcGVjaWZpZWQgYnkgdGhlIHByb2plY3Q6CiAgICAqICoqTG9naXN0aWMgUmVncmVzc2lvbioqOiBUaHJlZSBtb2RlbHMgKGByZWR1Y2VkYCwgYGZ1bGxgLCBgZm9yd2FyZHNgKSB3ZXJlIGJ1aWx0IGFzIGEgYmFzZWxpbmUuCiAgICAqICoqUGVyY2VwdHJvbiAoYG5ldXJhbG5ldGApKio6IEEgc2luZ2xlLWxheWVyIG5ldXJhbCBuZXR3b3JrIHdhcyBidWlsdCBhbmQgdmlzdWFsaXplZC4KICAgICogKipEZWNpc2lvbiBUcmVlKio6IEEgc2luZ2xlIGNsYXNzaWZpY2F0aW9uIHRyZWUgd2FzIGJ1aWx0IGFuZCBwbG90dGVkLgogICAgKiAqKkJBR0dJTkcqKjogQW4gZW5zZW1ibGUgbW9kZWwgd2FzIGJ1aWx0IHVzaW5nIGBpcHJlZGAsIGFuZCBpdHMgYm9vdHN0cmFwLWJhc2VkIEFVQyBkaXN0cmlidXRpb24gd2FzIHZpc3VhbGl6ZWQuCgoqICoqUGFydCAzIChGaW5hbCBNb2RlbCBDb21wYXJpc29uKSoqOiBXZSBldmFsdWF0ZWQgYWxsIG1vZGVscyBieSBjYWxjdWxhdGluZyB0aGVpciBST0MtQVVDIG9uIHRoZSAqZW50aXJlIGRhdGFzZXQqIGFuZCBwbG90dGluZyB0aGVtIG9uIGEgc2luZ2xlLCBjb21wcmVoZW5zaXZlIGdyYXBoLiBUaGUgZmluYWwgQVVDIHNjb3JlcyB3ZXJlOgoKfCBNb2RlbCB8IEFVQyB8CnwgOi0tLSB8IDotLS0gfAp8ICoqQkFHR0lORyAoRW5zZW1ibGUpKiogfCBgciByb3VuZChhdWNfYmFnZ2luZywgNClgIHwKfCAqKkZ1bGwgR0xNKiogfCBgciByb3VuZChhdWNfZnVsbCwgNClgIHwKfCAqKkZvcndhcmRzIEdMTSoqIHwgYHIgcm91bmQoYXVjX2ZvcndhcmRzLCA0KWAgfAp8ICoqUGVyY2VwdHJvbiAoTk4pKiogfCBgciByb3VuZChhdWNfbm4sIDQpYCB8CnwgKipEZWNpc2lvbiBUcmVlKiogfCBgciByb3VuZChhdWNfdHJlZSwgNClgIHwKfCAqKlJlZHVjZWQgR0xNKiogfCBgciByb3VuZChhdWNfcmVkdWNlZCwgNClgIHwKCkJhc2VkIG9uIHRoaXMgYW5hbHlzaXMsIHRoZSAqKlBlcmNlcHRyb24gKE5OKSBtb2RlbCoqIGlzIHRoZSBiZXN0LXBlcmZvcm1pbmcgbW9kZWwsIGFjaGlldmluZyBhIHBlcmZlY3QgQVVDIG9mIDEuIFRoZSAqKkZ1bGwgTG9naXN0aWMgTW9kZWwqKiBhbmQgKipGb3J3YXJkcy1TZWxlY3RlZCBMb2dpc3RpYyBNb2RlbCoqIGFsc28gcHJvdmVkIHRvIGJlIHZlcnkgc3Ryb25nIGFuZCBoaWdobHkgY29tcGV0aXRpdmUuCgpJbnRlcmVzdGluZ2x5LCB0aGUgKipCQUdHSU5HKiogbW9kZWwgKEFVQyAwLjgzNDUpIHBlcmZvcm1lZCBpZGVudGljYWxseSB0byB0aGUgc2luZ2xlICoqRGVjaXNpb24gVHJlZSoqIChBVUMgMC44MzQ1KS4gV2hpbGUgdGhlb3J5IHN1Z2dlc3RzIGJhZ2dpbmcgc2hvdWxkIHJlZHVjZSB2YXJpYW5jZSBhbmQgaW1wcm92ZSBhY2N1cmFjeSwgdGhhdCB3YXMgbm90IHRoZSBjYXNlIGhlcmUsIGFzIGl0IHByb3ZpZGVkIG5vIHBlcmZvcm1hbmNlIGJlbmVmaXQgb3ZlciB0aGUgc2luZ2xlIHRyZWUu