1 Introduction

This report details the development of two types of supervised classification models to predict diabetes status: logistic regression and a single-layer neural network (perceptron). The project is divided into three parts:

  1. Part 1: Data loading, exploratory data analysis (EDA) and preprocessing.
  2. Part 2: Development of three logistic regression models and one perceptron model.
  3. Part 3: Comparison of all four 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 prepartion and feature engineering.

2.1 Data Loading and Initial Inspection

setwd("/Users/jeffery/Library/Mobile Documents/com~apple~CloudDocs/Documents/Documents - jMacP/WCUPA/Classes/Fall 2025/STA551/Project 2/Data")
# Load the CSV file
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 the variables.

# Display the dimensions (rows, columns) of the data
dim(diabetes.data)
[1] 100000      9
# Structure of the data including variable names and types
str(diabetes.data)
'data.frame':   100000 obs. of  9 variables:
 $ gender             : chr  "Female" "Female" "Male" "Female" ...
 $ age                : num  80 54 28 36 76 20 44 79 42 32 ...
 $ hypertension       : int  0 0 0 0 1 0 0 0 0 0 ...
 $ heart_disease      : int  1 0 0 0 1 0 0 0 0 0 ...
 $ smoking_history    : chr  "never" "No Info" "never" "current" ...
 $ bmi                : num  25.2 27.3 27.3 23.4 20.1 ...
 $ HbA1c_level        : num  6.6 6.6 5.7 5 4.8 6.6 6.5 5.7 4.8 5 ...
 $ blood_glucose_level: int  140 80 158 155 155 85 200 85 145 100 ...
 $ diabetes           : int  0 0 0 0 0 0 1 0 0 0 ...

summary() providing 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 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

The first step for missing values, 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 adjustment 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.

# Convert categorical string variables to factors
diabetes.data$gender <- as.factor(diabetes.data$gender)
diabetes.data$smoking_history <- as.factor(diabetes.data$smoking_history)

# Convert the target variable 'diabetes' to a factor
diabetes.data$diabetes <- factor(diabetes.data$diabetes,
                                 levels = c(0, 1),
                                 labels = c("No", "Yes"))

# The 'Other' value in 'gender' has few observations so we remove it for model stability.
diabetes.data <- diabetes.data[diabetes.data$gender != "Other", ]

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

# Check the structure to confirm all changes have been applied
str(diabetes.data)
'data.frame':   99982 obs. of  9 variables:
 $ gender             : Factor w/ 2 levels "Female","Male": 1 1 2 1 2 1 1 1 2 1 ...
 $ age                : num  80 54 28 36 76 20 44 79 42 32 ...
 $ hypertension       : int  0 0 0 0 1 0 0 0 0 0 ...
 $ heart_disease      : int  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  25.2 27.3 27.3 23.4 20.1 ...
 $ HbA1c_level        : num  6.6 6.6 5.7 5 4.8 6.6 6.5 5.7 4.8 5 ...
 $ blood_glucose_level: int  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 ...

3 Part 2: Model Development

Now we develop and compare models. This involves building multiple glm models and one neuralnet model on the full dataset to compare their performance.

3.1 Logistic Regression (Baseline)

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

  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)

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

# Use forward selection to find the best model between reduced and full
# trace = FALSE hides the step-by-step output of the selection process
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)

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 Perceptron (Single-Layer Neural Network)

Neural networks are sensitive to the scale of input data and require all inputs to be numeric. Before building our perceptron, we must preprocess the data, which involves two key steps: manually scaling numeric features and creating a design matrix (dummifying) for the neuralnet function.

3.2.1 Data Prep for neuralnet

We will use min-max normalization to scale all numeric predictors to a range of [0, 1]. This ensures that variables with large magnitudes (like blood_glucose_level) do not disproportionately influence the model’s weights compared to variables with small magnitudes.

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

# 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, the neuralnet package requires a formula and a data frame that does not contain factors. We use model.matrix() to automatically create dummy variables for all our factors (like genderFemale, smoking_historynever, etc.).

This creates a new data frame of only numeric values. We must also clean the column names (using make.names()) to remove spaces or special characters (e.g., “No Info” becomes “No.Info”) and then dynamically build a formula string that includes all these new dummy predictors.

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

# Clean the column names to make them valid R variables
# This fixes errors from factor levels with spaces like "No Info"
valid.names <- make.names(colnames(neuralData.nn))
colnames(neuralData.nn) <- valid.names

# Add the numeric response variable (0/1) for neuralnet
# The neuralnet function requires a numeric target
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 first (Intercept)
# and the last (our new response variable 'diabetes_num')
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 Perceptron Model

With the scaled and dummified data prepared, we can now train the perceptron.

  • We set hidden = 1 to create a single-layer network (a true perceptron).
  • We use act.fct = "logistic" (the sigmoid function) because this is a binary classification problem, which mirrors our logistic regression.
  • We set linear.output = FALSE to ensure the activation function is applied to the output, giving us a probability between 0 and 1.
# Train the perceptron
set.seed(123)
perceptron.model <- neuralnet(modelFormula,
                              data = neuralData.nn,
                              hidden = 1, 
                              act.fct = "logistic",
                              linear.output = FALSE) 

4 Part 3: Model Comparison

To determine the best model we will compare their predictive performance on the entire dataset. The primary metric for comparison will be the Area Under the Curve (AUC) from the Receiver Operating Characteristic (ROC) curve. An AUC of 1.0 represents a perfect classifier, while an AUC of 0.5 represents a model with no discriminatory power (same as random guessing).

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

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

# Neural network model predictions
predNN.raw <- predict(perceptron.model, newdata = neuralData.nn)
predNN <- as.vector(predNN.raw) # Ensure it's a vector for pROC

# 2. Create ROC objects for all models
category <- diabetes.data$diabetes == "Yes"
ROCobj.reduced <- roc(category, predReduced)
ROCobj.full <- roc(category, predFull)
ROCobj.forwards <- roc(category, predForwards)
ROCobj.NN <- roc(category, predNN)

# 3. Get AUC values from each ROC object
reducedAUC <- ROCobj.reduced$auc
fullAUC <- ROCobj.full$auc
forwardsAUC <- ROCobj.forwards$auc
NNAUC <- ROCobj.NN$auc

# 4. Plot all ROC curves on one graph for comparison
colors <- c("#8B4500", "#00008B", "#8B008B", "#055d03")

plot(ROCobj.reduced, col = colors[1], lwd = 2, main = "ROC Curves of Candidate Models (Full Dataset)")
lines(ROCobj.full, col = colors[2], lwd = 2, lty = 2)
lines(ROCobj.forwards, col = colors[3], lwd = 1)
lines(ROCobj.NN, col = colors[4], lwd = 1)

# Add legend
legend("bottomright", c("reduced", "full", "forwards", "NN"),
       col = colors, lwd = c(2, 2, 1, 1), lty = c(1, 2, 1, 1), bty = "n")

# AUC text annotations for clarity
text(0.4, 0.4, paste("AUC.reduced =", round(reducedAUC, 4)), col = colors[1], adj = 0)
text(0.4, 0.35, paste("AUC.full =", round(fullAUC, 4)), col = colors[2], adj = 0)
text(0.4, 0.3, paste("AUC.forwards =", round(forwardsAUC, 4)), col = colors[3], adj = 0)
text(0.4, 0.25, paste("AUC.NN =", round(NNAUC, 4)), col = colors[4], adj = 0)

5 Conclusion

  • Part 1 (Data Prep): The data was loaded, reviewed and prepped. No missing values were found. The gender variable was cleaned by removing the ‘Other’ category.
  • Part 2 (Model Development): We developed four models:
    • Logistic Regression: We built a reducedModel, a fullModel, and a forwards selection model using glm() and step().
    • Perceptron (neuralnet): We created a separate, scaled and dummified dataset using model.matrix. We built a neuralnet model with one hidden node (hidden = 1) and a logistic activation function.
  • Part 3 (Model Comparison): We evaluated all four models by calculating their ROC-AUC on the entire dataset. The final AUC scores were:
    • Reduced GLM AUC: 0.9586
    • Full GLM AUC: 0.9619
    • Forwards GLM AUC: 0.9619
    • Perceptron (NN) AUC: 1

Based on this analysis, the Full Logistic Model and the Forwards-Selected Logistic Model have almost identical and superior predictive results (AUC 0.962) compared to the simpler reduced model. This suggests that the additional variables in the full/forwards models (like smoking_history and gender) provide valuable predictive information.

LS0tCnRpdGxlOiAnUHJvamVjdCBUd286IFN1cGVydmlzZWQgQ2xhc3NpZmljYXRpb24nCmF1dGhvcjogJ0plZmYgRGVsdmEnCmRhdGU6ICJPY3RvYmVyIDI5LCAyMDI1IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgICBmaWdfd2lkdGg6IDgKICAgIGZpZ19oZWlnaHQ6IDUKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMKICAgIHRoZW1lOiBsdW1lbgogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCmBgYHtjc3MsIGVjaG8gPSBGQUxTRX0KaDEudGl0bGUgewogIGZvbnQtc2l6ZTogMjRweDsKICBmb250LXdlaWdodDogYm9sZDsKICBjb2xvcjogRGFya1JlZDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KaDQuYXV0aG9yLCBoNC5kYXRlIHsKICBmb250LXNpemU6IDE4cHg7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgY29sb3I6IERhcmtCbHVlOwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQpoMSB7CiAgICBmb250LXNpemU6IDIwcHg7CiAgICBmb250LXdlaWdodDogYm9sZDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KaDIgewogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQpoMyB7CiAgICBmb250LXNpemU6IDE2cHg7CiAgICBmb250LXdlaWdodDogYm9sZDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9Ci5oZWFkZXItc2VjdGlvbi1udW1iZXI6OmFmdGVyIHsKICBjb250ZW50OiAiLiI7Cn0KYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikKICAgbGlicmFyeShrbml0cikKfQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIpCiAgIGxpYnJhcnkocGFuZGVyKQp9CmlmICghcmVxdWlyZSgiTUFTUyIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoIk1BU1MiKQogICBsaWJyYXJ5KE1BU1MpCn0KaWYgKCFyZXF1aXJlKCJwUk9DIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygicFJPQyIpCiAgIGxpYnJhcnkocFJPQykKfQppZiAoIXJlcXVpcmUoIm5ldXJhbG5ldCIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoIm5ldXJhbG5ldCIpCiAgIGxpYnJhcnkobmV1cmFsbmV0KQp9Cgprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgcmVzdWx0cyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgY29tbWVudCA9IE5BICkKYGBgCgojIEludHJvZHVjdGlvbgoKVGhpcyByZXBvcnQgZGV0YWlscyB0aGUgZGV2ZWxvcG1lbnQgb2YgdHdvIHR5cGVzIG9mIHN1cGVydmlzZWQgY2xhc3NpZmljYXRpb24gbW9kZWxzIHRvIHByZWRpY3QgZGlhYmV0ZXMgc3RhdHVzOiBsb2dpc3RpYyByZWdyZXNzaW9uIGFuZCBhIHNpbmdsZS1sYXllciBuZXVyYWwgbmV0d29yayAocGVyY2VwdHJvbikuIFRoZSBwcm9qZWN0IGlzIGRpdmlkZWQgaW50byB0aHJlZSBwYXJ0czoKCjEuICAqKlBhcnQgMToqKiBEYXRhIGxvYWRpbmcsIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMgKEVEQSkgYW5kIHByZXByb2Nlc3NpbmcuCjIuICAqKlBhcnQgMjoqKiBEZXZlbG9wbWVudCBvZiB0aHJlZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyBhbmQgb25lIHBlcmNlcHRyb24gbW9kZWwuCjMuICAqKlBhcnQgMzoqKiBDb21wYXJpc29uIG9mIGFsbCBmb3VyIG1vZGVscyB1c2luZyBST0MtQVVDIGFuYWx5c2lzIHRvIGRldGVybWluZSB0aGUgYmVzdC1wZXJmb3JtaW5nIG1vZGVsLgoKLS0tLS0KCiMgUGFydCAxOiBEYXRhIFByZXBhcmF0aW9uIGFuZCBFREEKClRoZSBmaXJzdCBzdGVwIGluIGFueSBtb2RlbGluZyBwcm9jZXNzIGlzIHRvIGxvYWQsIHVuZGVyc3RhbmQgYW5kIGNsZWFuIHRoZSBkYXRhLiBUaGlzIHNlY3Rpb24gY292ZXJzIG91ciBkYXRhIHByZXBhcnRpb24gYW5kIGZlYXR1cmUgZW5naW5lZXJpbmcuCgojIyBEYXRhIExvYWRpbmcgYW5kIEluaXRpYWwgSW5zcGVjdGlvbgoKYGBge3IgbG9hZC1kYXRhfQpzZXR3ZCgiL1VzZXJzL2plZmZlcnkvTGlicmFyeS9Nb2JpbGUgRG9jdW1lbnRzL2NvbX5hcHBsZX5DbG91ZERvY3MvRG9jdW1lbnRzL0RvY3VtZW50cyAtIGpNYWNQL1dDVVBBL0NsYXNzZXMvRmFsbCAyMDI1L1NUQTU1MS9Qcm9qZWN0IDIvRGF0YSIpCiMgTG9hZCB0aGUgQ1NWIGZpbGUKZGlhYmV0ZXMuZGF0YSA8LSByZWFkLmNzdigiZGlhYmV0ZXNfcHJlZGljdGlvbl9kYXRhc2V0LmNzdiIpCmBgYAoKQWZ0ZXIgbG9hZGluZyB3ZSBpbnNwZWN0IGl0cyBkaW1lbnNpb25zIChgZGltKClgKSBhbmQgZGF0YSB0eXBlcyAoYHN0cigpYCkgdG8gdW5kZXJzdGFuZCB0aGUgZGF0YXNldCdzIHN0cnVjdHVyZSBhbmQgdGhlIHZhcmlhYmxlcy4KCmBgYHtyIGluaXRpYWwtcmV2aWV3fQojIERpc3BsYXkgdGhlIGRpbWVuc2lvbnMgKHJvd3MsIGNvbHVtbnMpIG9mIHRoZSBkYXRhCmRpbShkaWFiZXRlcy5kYXRhKQoKIyBTdHJ1Y3R1cmUgb2YgdGhlIGRhdGEgaW5jbHVkaW5nIHZhcmlhYmxlIG5hbWVzIGFuZCB0eXBlcwpzdHIoZGlhYmV0ZXMuZGF0YSkKYGBgCgpgc3VtbWFyeSgpYCBwcm92aWRpbmcgYW4gb3ZlcnZpZXcgb2YgZWFjaCB2YXJpYWJsZS4KCmBgYHtyIHN1bW1hcnl9CiMgU3VtbWFyeSBvZiBlYWNoIHZhcmlhYmxlCnN1bW1hcnkoZGlhYmV0ZXMuZGF0YSkKYGBgCgojIyBEYXRhIFByZXByb2Nlc3NpbmcKClRoZSBuZXh0IHN0ZXAgaXMgdG8gY2xlYW4gYW5kIHByZXBhcmUgdGhlIGRhdGEgZm9yIG1vZGVsaW5nLiBUaGlzIGludm9sdmVzIGhhbmRsaW5nIG1pc3NpbmcgdmFsdWVzIGFuZCBlbnN1cmluZyBhbGwgdmFyaWFibGVzIGFyZSBpbiB0aGUgY29ycmVjdCBmb3JtYXQuCgojIyMgTWlzc2luZyBWYWx1ZSBIYW5kbGluZwoKVGhlIGZpcnN0IHN0ZXAgZm9yIG1pc3NpbmcgdmFsdWVzLCB3ZSB1c2UgYGNvbFN1bXMoaXMubmEoKSlgIHRvIGNvdW50IHRoZSBudW1iZXIgb2YgYE5BYCB2YWx1ZXMgaW4gZWFjaCBjb2x1bW4uCgpgYGB7ciBtaXNzaW5nLXZhbHVlc30KIyBDaGVjayBmb3IgbWlzc2luZyAoTkEpIHZhbHVlcyBpbiBlYWNoIGNvbHVtbgpjb2xTdW1zKGlzLm5hKGRpYWJldGVzLmRhdGEpKQoKYGBgCgoqKk9ic2VydmF0aW9uKio6IFRoZSBvdXRwdXQgc2hvd3MgMCBtaXNzaW5nIHZhbHVlcyBmb3IgYWxsIGNvbHVtbnMsIHNvIG5vIGFkanVzdG1lbnQgaXMgbmVlZGVkLgoKIyMjIEZlYXR1cmUgRW5jb2RpbmcKClRoZSBkYXRhc2V0IGNvbnRhaW5zIHNldmVyYWwgY2hhcmFjdGVyLWJhc2VkIHZhcmlhYmxlcyAoYGdlbmRlcmAsIGBzbW9raW5nX2hpc3RvcnlgKSB0aGF0IG5lZWQgdG8gYmUgY29udmVydGVkIHRvIGZhY3RvcnMgZm9yIFIncyBtb2RlbGluZyBmdW5jdGlvbnMgKGxpa2UgYGdsbSgpYCkgdG8gaW50ZXJwcmV0IHRoZW0gY29ycmVjdGx5IGFzIGNhdGVnb3JpY2FsIHByZWRpY3RvcnMuCgpUaGUgdGFyZ2V0IHZhcmlhYmxlLCBgZGlhYmV0ZXNgLCBpcyBhbHNvIGNvbnZlcnRlZCB0byBhIGZhY3RvciB3aXRoIGNsZWFyICdZZXMnLydObycgbGFiZWxzIGZvciBiZXR0ZXIgaW50ZXJwcmV0YWJpbGl0eSBpbiBvdXIgcmVzdWx0cy4KCkR1cmluZyBvdXIgaW5pdGlhbCBgc3VtbWFyeSgpYCwgd2Ugbm90ZWQgdGhhdCB0aGUgJ090aGVyJyBjYXRlZ29yeSBpbiBgZ2VuZGVyYCBoYXMgdmVyeSBmZXcgb2JzZXJ2YXRpb25zIChvbmx5IDE4KS4gVG8gcHJldmVudCBtb2RlbCBpbnN0YWJpbGl0eSwgd2Ugd2lsbCByZW1vdmUgdGhlc2Ugb2JzZXJ2YXRpb25zLiBXZSB0aGVuIHVzZSBgZHJvcGxldmVscygpYCB0byByZW1vdmUgJ090aGVyJyBmcm9tIHRoZSBmYWN0b3IgbGV2ZWxzLgoKYGBge3IgZmFjdG9yLWNvbnZlcnNpb259CiMgQ29udmVydCBjYXRlZ29yaWNhbCBzdHJpbmcgdmFyaWFibGVzIHRvIGZhY3RvcnMKZGlhYmV0ZXMuZGF0YSRnZW5kZXIgPC0gYXMuZmFjdG9yKGRpYWJldGVzLmRhdGEkZ2VuZGVyKQpkaWFiZXRlcy5kYXRhJHNtb2tpbmdfaGlzdG9yeSA8LSBhcy5mYWN0b3IoZGlhYmV0ZXMuZGF0YSRzbW9raW5nX2hpc3RvcnkpCgojIENvbnZlcnQgdGhlIHRhcmdldCB2YXJpYWJsZSAnZGlhYmV0ZXMnIHRvIGEgZmFjdG9yCmRpYWJldGVzLmRhdGEkZGlhYmV0ZXMgPC0gZmFjdG9yKGRpYWJldGVzLmRhdGEkZGlhYmV0ZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoMCwgMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIk5vIiwgIlllcyIpKQoKIyBUaGUgJ090aGVyJyB2YWx1ZSBpbiAnZ2VuZGVyJyBoYXMgZmV3IG9ic2VydmF0aW9ucyBzbyB3ZSByZW1vdmUgaXQgZm9yIG1vZGVsIHN0YWJpbGl0eS4KZGlhYmV0ZXMuZGF0YSA8LSBkaWFiZXRlcy5kYXRhW2RpYWJldGVzLmRhdGEkZ2VuZGVyICE9ICJPdGhlciIsIF0KCiMgUmVtb3ZlICdPdGhlcicgbGV2ZWwgZnJvbSB0aGUgZmFjdG9yCmRpYWJldGVzLmRhdGEkZ2VuZGVyIDwtIGRyb3BsZXZlbHMoZGlhYmV0ZXMuZGF0YSRnZW5kZXIpCgojIENoZWNrIHRoZSBzdHJ1Y3R1cmUgdG8gY29uZmlybSBhbGwgY2hhbmdlcyBoYXZlIGJlZW4gYXBwbGllZApzdHIoZGlhYmV0ZXMuZGF0YSkKYGBgCgotLS0tLQoKIyBQYXJ0IDI6IE1vZGVsIERldmVsb3BtZW50CgpOb3cgd2UgZGV2ZWxvcCBhbmQgY29tcGFyZSBtb2RlbHMuIFRoaXMgaW52b2x2ZXMgYnVpbGRpbmcgbXVsdGlwbGUgYGdsbWAgbW9kZWxzIGFuZCBvbmUgYG5ldXJhbG5ldGAgbW9kZWwgb24gdGhlICoqZnVsbCBkYXRhc2V0KiogdG8gY29tcGFyZSB0aGVpciBwZXJmb3JtYW5jZS4KCiMjIExvZ2lzdGljIFJlZ3Jlc3Npb24gKEJhc2VsaW5lKQoKV2Ugd2lsbCBidWlsZCB0aHJlZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyB0byBzZXJ2ZSBhcyBvdXIgYmFzZWxpbmUgYW5kIGZvciBjb21wYXJpc29uLgoKMS4gICoqYHJlZHVjZWRNb2RlbGAqKjogQSBzaW1wbGUgbW9kZWwgd2l0aCBvbmx5IHRoZSB2YXJpYWJsZXMgd2UgaHlwb3RoZXNpemUgYXJlIG1vc3QgY2xpbmljYWxseSBzaWduaWZpY2FudC4KMi4gICoqYGZ1bGxNb2RlbGAqKjogQSBjb21wbGV4IG1vZGVsIHRoYXQgaW5jbHVkZXMgYWxsIGF2YWlsYWJsZSBwcmVkaWN0b3JzLgozLiAgKipgZm9yd2FyZHNgKio6IEFuIG9wdGltaXplZCBtb2RlbCBmb3VuZCB1c2luZyBmb3J3YXJkIHNlbGVjdGlvbiwgc3RhcnRpbmcgZnJvbSB0aGUgYHJlZHVjZWRNb2RlbGAgYW5kIGFkZGluZyBwcmVkaWN0b3JzIGZyb20gdGhlIGBmdWxsTW9kZWxgIGJhc2VkIG9uIEFJQy4KCmBgYHtyIG1vZGVsc30KIyBEZWZpbmUgYSByZWR1Y2VkIG1vZGVsIHdpdGggdmFyaWFibGVzIHdlIGFzc3VtZSBhcmUgc2lnbmlmaWNhbnQKcmVkdWNlZE1vZGVsIDwtIGdsbShkaWFiZXRlcyB+IGFnZSArIGJtaSArIGJsb29kX2dsdWNvc2VfbGV2ZWwgKyBIYkExY19sZXZlbCwKICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gbG9naXQpLAogICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkaWFiZXRlcy5kYXRhKQoKIyBEZWZpbmUgdGhlIGZ1bGwgbW9kZWwgd2l0aCBhbGwgdmFyaWFibGVzCmZ1bGxNb2RlbCA8LSBnbG0oZGlhYmV0ZXMgfiAuLAogICAgICAgICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSBsb2dpdCksCiAgICAgICAgICAgICAgICAgZGF0YSA9IGRpYWJldGVzLmRhdGEpCgojIFVzZSBmb3J3YXJkIHNlbGVjdGlvbiB0byBmaW5kIHRoZSBiZXN0IG1vZGVsIGJldHdlZW4gcmVkdWNlZCBhbmQgZnVsbAojIHRyYWNlID0gRkFMU0UgaGlkZXMgdGhlIHN0ZXAtYnktc3RlcCBvdXRwdXQgb2YgdGhlIHNlbGVjdGlvbiBwcm9jZXNzCmZvcndhcmRzIDwtIHN0ZXAocmVkdWNlZE1vZGVsLAogICAgICAgICAgICAgICAgIHNjb3BlID0gbGlzdChsb3dlciA9IGZvcm11bGEocmVkdWNlZE1vZGVsKSwgdXBwZXIgPSBmb3JtdWxhKGZ1bGxNb2RlbCkpLAogICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9ICJmb3J3YXJkIiwKICAgICAgICAgICAgICAgICB0cmFjZSA9IEZBTFNFKQoKIyBEaXNwbGF5IHRoZSBzdW1tYXJ5IG9mIHRoZSBmaW5hbCwgZm9yd2FyZC1zZWxlY3RlZCBtb2RlbApzdW1tYXJ5KGZvcndhcmRzKQpgYGAKCiMjIFBlcmNlcHRyb24gKFNpbmdsZS1MYXllciBOZXVyYWwgTmV0d29yaykKCk5ldXJhbCBuZXR3b3JrcyBhcmUgc2Vuc2l0aXZlIHRvIHRoZSBzY2FsZSBvZiBpbnB1dCBkYXRhIGFuZCByZXF1aXJlIGFsbCBpbnB1dHMgdG8gYmUgbnVtZXJpYy4gQmVmb3JlIGJ1aWxkaW5nIG91ciBwZXJjZXB0cm9uLCB3ZSBtdXN0IHByZXByb2Nlc3MgdGhlIGRhdGEsIHdoaWNoIGludm9sdmVzIHR3byBrZXkgc3RlcHM6IG1hbnVhbGx5IHNjYWxpbmcgbnVtZXJpYyBmZWF0dXJlcyBhbmQgY3JlYXRpbmcgYSBkZXNpZ24gbWF0cml4IChkdW1taWZ5aW5nKSBmb3IgdGhlIGBuZXVyYWxuZXRgIGZ1bmN0aW9uLgoKIyMjIERhdGEgUHJlcCBmb3IgYG5ldXJhbG5ldGAKCldlIHdpbGwgdXNlICoqbWluLW1heCBub3JtYWxpemF0aW9uKiogdG8gc2NhbGUgYWxsIG51bWVyaWMgcHJlZGljdG9ycyB0byBhIHJhbmdlIG9mIFswLCAxXS4gVGhpcyBlbnN1cmVzIHRoYXQgdmFyaWFibGVzIHdpdGggbGFyZ2UgbWFnbml0dWRlcyAobGlrZSBgYmxvb2RfZ2x1Y29zZV9sZXZlbGApIGRvIG5vdCBkaXNwcm9wb3J0aW9uYXRlbHkgaW5mbHVlbmNlIHRoZSBtb2RlbCdzIHdlaWdodHMgY29tcGFyZWQgdG8gdmFyaWFibGVzIHdpdGggc21hbGwgbWFnbml0dWRlcy4KCmBgYHtyIG1hbnVhbC1zY2FsaW5nfQojIENyZWF0ZSBhIGNvcHkgb2YgdGhlIGRhdGEgZm9yIG5ldXJhbCBuZXR3b3JrIHByZXByb2Nlc3NpbmcKbmV1cmFsRGF0YSA8LSBkaWFiZXRlcy5kYXRhCgojIElkZW50aWZ5IG51bWVyaWMgdmFyaWFibGVzIGZvciBzY2FsaW5nCm51bWVyaWMudmFycyA8LSBjKCJhZ2UiLCAiYm1pIiwgIkhiQTFjX2xldmVsIiwgImJsb29kX2dsdWNvc2VfbGV2ZWwiKQoKIyBMb29wIHRocm91Z2ggbnVtZXJpYyB2YXJpYWJsZXMsIHNjYWxlIHRoZW0gdXNpbmcgbWluLW1heCBub3JtYWxpemF0aW9uCmZvciAoY29sIGluIG51bWVyaWMudmFycykgewogIG1pbi52YWwgPC0gbWluKG5ldXJhbERhdGFbW2NvbF1dKQogIG1heC52YWwgPC0gbWF4KG5ldXJhbERhdGFbW2NvbF1dKQogIAogICMgVGhlIG1pbi1tYXggZm9ybXVsYQogIG5ldXJhbERhdGFbW2NvbF1dIDwtIChuZXVyYWxEYXRhW1tjb2xdXSAtIG1pbi52YWwpIC8gKG1heC52YWwgLSBtaW4udmFsKQp9CmBgYAoKTmV4dCwgdGhlIGBuZXVyYWxuZXRgIHBhY2thZ2UgcmVxdWlyZXMgYSBmb3JtdWxhIGFuZCBhIGRhdGEgZnJhbWUgdGhhdCBkb2VzICpub3QqIGNvbnRhaW4gZmFjdG9ycy4gV2UgdXNlIGBtb2RlbC5tYXRyaXgoKWAgdG8gYXV0b21hdGljYWxseSBjcmVhdGUgZHVtbXkgdmFyaWFibGVzIGZvciBhbGwgb3VyIGZhY3RvcnMgKGxpa2UgYGdlbmRlckZlbWFsZWAsIGBzbW9raW5nX2hpc3RvcnluZXZlcmAsIGV0Yy4pLgoKVGhpcyBjcmVhdGVzIGEgbmV3IGRhdGEgZnJhbWUgb2Ygb25seSBudW1lcmljIHZhbHVlcy4gV2UgbXVzdCBhbHNvIGNsZWFuIHRoZSBjb2x1bW4gbmFtZXMgKHVzaW5nIGBtYWtlLm5hbWVzKClgKSB0byByZW1vdmUgc3BhY2VzIG9yIHNwZWNpYWwgY2hhcmFjdGVycyAoZS5nLiwgIk5vIEluZm8iIGJlY29tZXMgIk5vLkluZm8iKSBhbmQgdGhlbiBkeW5hbWljYWxseSBidWlsZCBhIGZvcm11bGEgc3RyaW5nIHRoYXQgaW5jbHVkZXMgYWxsIHRoZXNlIG5ldyBkdW1teSBwcmVkaWN0b3JzLgoKYGBge3Igbm4tZm9ybXVsYX0KIyBDcmVhdGUgdGhlIGRlc2lnbiBtYXRyaXgsIHdoaWNoIGF1dG9tYXRpY2FsbHkgZHVtbWlmaWVzIGZhY3RvciB2YXJpYWJsZXMKIyBUaGUgJ34gLicgZm9ybXVsYSBpbmNsdWRlcyBhbGwgdmFyaWFibGVzCm5ldXJhbERhdGEubWF0cml4IDwtIG1vZGVsLm1hdHJpeCh+IC4sIGRhdGEgPSBuZXVyYWxEYXRhKQpuZXVyYWxEYXRhLm5uIDwtIGFzLmRhdGEuZnJhbWUobmV1cmFsRGF0YS5tYXRyaXgpCgojIENsZWFuIHRoZSBjb2x1bW4gbmFtZXMgdG8gbWFrZSB0aGVtIHZhbGlkIFIgdmFyaWFibGVzCiMgVGhpcyBmaXhlcyBlcnJvcnMgZnJvbSBmYWN0b3IgbGV2ZWxzIHdpdGggc3BhY2VzIGxpa2UgIk5vIEluZm8iCnZhbGlkLm5hbWVzIDwtIG1ha2UubmFtZXMoY29sbmFtZXMobmV1cmFsRGF0YS5ubikpCmNvbG5hbWVzKG5ldXJhbERhdGEubm4pIDwtIHZhbGlkLm5hbWVzCgojIEFkZCB0aGUgbnVtZXJpYyByZXNwb25zZSB2YXJpYWJsZSAoMC8xKSBmb3IgbmV1cmFsbmV0CiMgVGhlIG5ldXJhbG5ldCBmdW5jdGlvbiByZXF1aXJlcyBhIG51bWVyaWMgdGFyZ2V0Cm5ldXJhbERhdGEubm4kZGlhYmV0ZXNfbnVtIDwtIGlmZWxzZShuZXVyYWxEYXRhJGRpYWJldGVzID09ICJZZXMiLCAxLCAwKQoKIyBHZXQgYWxsIGNvbHVtbiBuYW1lcyBmcm9tIHRoZSBuZXcgZGF0YSBmcmFtZQpjb2x1bW5OYW1lcyA8LSBjb2xuYW1lcyhuZXVyYWxEYXRhLm5uKQoKIyBDcmVhdGUgdGhlIGxpc3Qgb2YgcHJlZGljdG9ycyBieSByZW1vdmluZyB0aGUgZmlyc3QgKEludGVyY2VwdCkKIyBhbmQgdGhlIGxhc3QgKG91ciBuZXcgcmVzcG9uc2UgdmFyaWFibGUgJ2RpYWJldGVzX251bScpCmNvbHVtbkxpc3QgPC0gcGFzdGUoY29sdW1uTmFtZXNbLWMoMSwgbGVuZ3RoKGNvbHVtbk5hbWVzKSldLCBjb2xsYXBzZSA9ICIrIikKCiMgQ3JlYXRlIHRoZSBmaW5hbCBmb3JtdWxhIHN0cmluZwptb2RlbEZvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZSgiZGlhYmV0ZXNfbnVtIH4iLCBjb2x1bW5MaXN0KSkKCiMgUHJpbnQgdGhlIGZvcm11bGEgdG8gY2hlY2sKcHJpbnQobW9kZWxGb3JtdWxhKQpgYGAKCiMjIyBCdWlsZCBQZXJjZXB0cm9uIE1vZGVsCgpXaXRoIHRoZSBzY2FsZWQgYW5kIGR1bW1pZmllZCBkYXRhIHByZXBhcmVkLCB3ZSBjYW4gbm93IHRyYWluIHRoZSBwZXJjZXB0cm9uLgoKICAqIFdlIHNldCBgaGlkZGVuID0gMWAgdG8gY3JlYXRlIGEgc2luZ2xlLWxheWVyIG5ldHdvcmsgKGEgdHJ1ZSBwZXJjZXB0cm9uKS4KICAqIFdlIHVzZSBgYWN0LmZjdCA9ICJsb2dpc3RpYyJgICh0aGUgc2lnbW9pZCBmdW5jdGlvbikgYmVjYXVzZSB0aGlzIGlzIGEgYmluYXJ5IGNsYXNzaWZpY2F0aW9uIHByb2JsZW0sIHdoaWNoIG1pcnJvcnMgb3VyIGxvZ2lzdGljIHJlZ3Jlc3Npb24uCiAgKiBXZSBzZXQgYGxpbmVhci5vdXRwdXQgPSBGQUxTRWAgdG8gZW5zdXJlIHRoZSBhY3RpdmF0aW9uIGZ1bmN0aW9uIGlzIGFwcGxpZWQgdG8gdGhlIG91dHB1dCwgZ2l2aW5nIHVzIGEgcHJvYmFiaWxpdHkgYmV0d2VlbiAwIGFuZCAxLgoKYGBge3Igbm4tdHJhaW59CiMgVHJhaW4gdGhlIHBlcmNlcHRyb24Kc2V0LnNlZWQoMTIzKQpwZXJjZXB0cm9uLm1vZGVsIDwtIG5ldXJhbG5ldChtb2RlbEZvcm11bGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBuZXVyYWxEYXRhLm5uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoaWRkZW4gPSAxLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWN0LmZjdCA9ICJsb2dpc3RpYyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVhci5vdXRwdXQgPSBGQUxTRSkgCmBgYAoKIyBQYXJ0IDM6IE1vZGVsIENvbXBhcmlzb24KClRvIGRldGVybWluZSB0aGUgYmVzdCBtb2RlbCB3ZSB3aWxsIGNvbXBhcmUgdGhlaXIgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSBvbiB0aGUgKiplbnRpcmUgZGF0YXNldCoqLiBUaGUgcHJpbWFyeSBtZXRyaWMgZm9yIGNvbXBhcmlzb24gd2lsbCBiZSB0aGUgKipBcmVhIFVuZGVyIHRoZSBDdXJ2ZSAoQVVDKSoqIGZyb20gdGhlIFJlY2VpdmVyIE9wZXJhdGluZyBDaGFyYWN0ZXJpc3RpYyAoUk9DKSBjdXJ2ZS4gQW4gQVVDIG9mIDEuMCByZXByZXNlbnRzIGEgcGVyZmVjdCBjbGFzc2lmaWVyLCB3aGlsZSBhbiBBVUMgb2YgMC41IHJlcHJlc2VudHMgYSBtb2RlbCB3aXRoIG5vIGRpc2NyaW1pbmF0b3J5IHBvd2VyIChzYW1lIGFzIHJhbmRvbSBndWVzc2luZykuCgpXZSB3aWxsIGdlbmVyYXRlIHByZWRpY3Rpb25zIGZyb20gYWxsIGZvdXIgbW9kZWxzIGFuZCBwbG90IHRoZWlyIFJPQyBjdXJ2ZXMgb24gYSBzaW5nbGUgZ3JhcGggZm9yIGRpcmVjdCBjb21wYXJpc29uLgoKYGBge3IgZmluYWwtcm9jLWNvbXBhcmlzb259CiMgMS4gR2V0IHByZWRpY3Rpb25zIChhcyBwcm9iYWJpbGl0aWVzKSBmb3IgYWxsIG1vZGVscwpwcmVkUmVkdWNlZCA8LSBwcmVkaWN0KHJlZHVjZWRNb2RlbCwgbmV3ZGF0YSA9IGRpYWJldGVzLmRhdGEsIHR5cGUgPSAicmVzcG9uc2UiKQpwcmVkRnVsbCA8LSBwcmVkaWN0KGZ1bGxNb2RlbCwgbmV3ZGF0YSA9IGRpYWJldGVzLmRhdGEsIHR5cGUgPSAicmVzcG9uc2UiKQpwcmVkRm9yd2FyZHMgPC0gcHJlZGljdChmb3J3YXJkcywgbmV3ZGF0YSA9IGRpYWJldGVzLmRhdGEsIHR5cGUgPSAicmVzcG9uc2UiKQoKIyBOZXVyYWwgbmV0d29yayBtb2RlbCBwcmVkaWN0aW9ucwpwcmVkTk4ucmF3IDwtIHByZWRpY3QocGVyY2VwdHJvbi5tb2RlbCwgbmV3ZGF0YSA9IG5ldXJhbERhdGEubm4pCnByZWROTiA8LSBhcy52ZWN0b3IocHJlZE5OLnJhdykgIyBFbnN1cmUgaXQncyBhIHZlY3RvciBmb3IgcFJPQwoKIyAyLiBDcmVhdGUgUk9DIG9iamVjdHMgZm9yIGFsbCBtb2RlbHMKY2F0ZWdvcnkgPC0gZGlhYmV0ZXMuZGF0YSRkaWFiZXRlcyA9PSAiWWVzIgpST0NvYmoucmVkdWNlZCA8LSByb2MoY2F0ZWdvcnksIHByZWRSZWR1Y2VkKQpST0NvYmouZnVsbCA8LSByb2MoY2F0ZWdvcnksIHByZWRGdWxsKQpST0NvYmouZm9yd2FyZHMgPC0gcm9jKGNhdGVnb3J5LCBwcmVkRm9yd2FyZHMpClJPQ29iai5OTiA8LSByb2MoY2F0ZWdvcnksIHByZWROTikKCiMgMy4gR2V0IEFVQyB2YWx1ZXMgZnJvbSBlYWNoIFJPQyBvYmplY3QKcmVkdWNlZEFVQyA8LSBST0NvYmoucmVkdWNlZCRhdWMKZnVsbEFVQyA8LSBST0NvYmouZnVsbCRhdWMKZm9yd2FyZHNBVUMgPC0gUk9Db2JqLmZvcndhcmRzJGF1YwpOTkFVQyA8LSBST0NvYmouTk4kYXVjCgojIDQuIFBsb3QgYWxsIFJPQyBjdXJ2ZXMgb24gb25lIGdyYXBoIGZvciBjb21wYXJpc29uCmNvbG9ycyA8LSBjKCIjOEI0NTAwIiwgIiMwMDAwOEIiLCAiIzhCMDA4QiIsICIjMDU1ZDAzIikKCnBsb3QoUk9Db2JqLnJlZHVjZWQsIGNvbCA9IGNvbG9yc1sxXSwgbHdkID0gMiwgbWFpbiA9ICJST0MgQ3VydmVzIG9mIENhbmRpZGF0ZSBNb2RlbHMgKEZ1bGwgRGF0YXNldCkiKQpsaW5lcyhST0NvYmouZnVsbCwgY29sID0gY29sb3JzWzJdLCBsd2QgPSAyLCBsdHkgPSAyKQpsaW5lcyhST0NvYmouZm9yd2FyZHMsIGNvbCA9IGNvbG9yc1szXSwgbHdkID0gMSkKbGluZXMoUk9Db2JqLk5OLCBjb2wgPSBjb2xvcnNbNF0sIGx3ZCA9IDEpCgojIEFkZCBsZWdlbmQKbGVnZW5kKCJib3R0b21yaWdodCIsIGMoInJlZHVjZWQiLCAiZnVsbCIsICJmb3J3YXJkcyIsICJOTiIpLAogICAgICAgY29sID0gY29sb3JzLCBsd2QgPSBjKDIsIDIsIDEsIDEpLCBsdHkgPSBjKDEsIDIsIDEsIDEpLCBidHkgPSAibiIpCgojIEFVQyB0ZXh0IGFubm90YXRpb25zIGZvciBjbGFyaXR5CnRleHQoMC40LCAwLjQsIHBhc3RlKCJBVUMucmVkdWNlZCA9Iiwgcm91bmQocmVkdWNlZEFVQywgNCkpLCBjb2wgPSBjb2xvcnNbMV0sIGFkaiA9IDApCnRleHQoMC40LCAwLjM1LCBwYXN0ZSgiQVVDLmZ1bGwgPSIsIHJvdW5kKGZ1bGxBVUMsIDQpKSwgY29sID0gY29sb3JzWzJdLCBhZGogPSAwKQp0ZXh0KDAuNCwgMC4zLCBwYXN0ZSgiQVVDLmZvcndhcmRzID0iLCByb3VuZChmb3J3YXJkc0FVQywgNCkpLCBjb2wgPSBjb2xvcnNbM10sIGFkaiA9IDApCnRleHQoMC40LCAwLjI1LCBwYXN0ZSgiQVVDLk5OID0iLCByb3VuZChOTkFVQywgNCkpLCBjb2wgPSBjb2xvcnNbNF0sIGFkaiA9IDApCgpgYGAKCiMgQ29uY2x1c2lvbgoKICAqICoqUGFydCAxIChEYXRhIFByZXApKio6IFRoZSBkYXRhIHdhcyBsb2FkZWQsIHJldmlld2VkIGFuZCBwcmVwcGVkLiBObyBtaXNzaW5nIHZhbHVlcyB3ZXJlIGZvdW5kLiBUaGUgYGdlbmRlcmAgdmFyaWFibGUgd2FzIGNsZWFuZWQgYnkgcmVtb3ZpbmcgdGhlICdPdGhlcicgY2F0ZWdvcnkuCiAgKiAqKlBhcnQgMiAoTW9kZWwgRGV2ZWxvcG1lbnQpKio6IFdlIGRldmVsb3BlZCBmb3VyIG1vZGVsczoKICAgICAgKiAqKkxvZ2lzdGljIFJlZ3Jlc3Npb24qKjogV2UgYnVpbHQgYSBgcmVkdWNlZE1vZGVsYCwgYSBgZnVsbE1vZGVsYCwgYW5kIGEgYGZvcndhcmRzYCBzZWxlY3Rpb24gbW9kZWwgdXNpbmcgYGdsbSgpYCBhbmQgYHN0ZXAoKWAuCiAgICAgICogKipQZXJjZXB0cm9uIChgbmV1cmFsbmV0YCkqKjogV2UgY3JlYXRlZCBhIHNlcGFyYXRlLCBzY2FsZWQgYW5kIGR1bW1pZmllZCBkYXRhc2V0IHVzaW5nIGBtb2RlbC5tYXRyaXhgLiBXZSBidWlsdCBhIGBuZXVyYWxuZXRgIG1vZGVsIHdpdGggb25lIGhpZGRlbiBub2RlIChgaGlkZGVuID0gMWApIGFuZCBhIGxvZ2lzdGljIGFjdGl2YXRpb24gZnVuY3Rpb24uCiAgKiAqKlBhcnQgMyAoTW9kZWwgQ29tcGFyaXNvbikqKjogV2UgZXZhbHVhdGVkIGFsbCBmb3VyIG1vZGVscyBieSBjYWxjdWxhdGluZyB0aGVpciBST0MtQVVDIG9uIHRoZSAqZW50aXJlIGRhdGFzZXQqLiBUaGUgZmluYWwgQVVDIHNjb3JlcyB3ZXJlOgogICAgICAqICoqUmVkdWNlZCBHTE0gQVVDKio6IGByIHJvdW5kKHJlZHVjZWRBVUMsIDQpYAogICAgICAqICoqRnVsbCBHTE0gQVVDKio6IGByIHJvdW5kKGZ1bGxBVUMsIDQpYAogICAgICAqICoqRm9yd2FyZHMgR0xNIEFVQyoqOiBgciByb3VuZChmb3J3YXJkc0FVQywgNClgCiAgICAgICogKipQZXJjZXB0cm9uIChOTikgQVVDKio6IGByIHJvdW5kKE5OQVVDLCA0KWAKCkJhc2VkIG9uIHRoaXMgYW5hbHlzaXMsIHRoZSAqKkZ1bGwgTG9naXN0aWMgTW9kZWwqKiBhbmQgdGhlICoqRm9yd2FyZHMtU2VsZWN0ZWQgTG9naXN0aWMgTW9kZWwqKiBoYXZlIGFsbW9zdCBpZGVudGljYWwgYW5kIHN1cGVyaW9yIHByZWRpY3RpdmUgcmVzdWx0cyAoQVVDIDAuOTYyKSBjb21wYXJlZCB0byB0aGUgc2ltcGxlciByZWR1Y2VkIG1vZGVsLiBUaGlzIHN1Z2dlc3RzIHRoYXQgdGhlIGFkZGl0aW9uYWwgdmFyaWFibGVzIGluIHRoZSBmdWxsL2ZvcndhcmRzIG1vZGVscyAobGlrZSBgc21va2luZ19oaXN0b3J5YCBhbmQgYGdlbmRlcmApIHByb3ZpZGUgdmFsdWFibGUgcHJlZGljdGl2ZSBpbmZvcm1hdGlvbi4=