1 Introduction

This report presents a comprehensive analysis of the bank loan default dataset. This project consists of three parts:

  1. Part I: Exploratory Data Analysis (EDA) and feature engineering.
  2. Part II: Classical statistical modeling focused on association analysis for Credit_score (Linear Regression) and Default (Logistic Regression).
  3. Part III: Predictive modeling using cross-validation to select the best predictive models and evaluating them on train test data.

The overall goal is to understand the factors related to loan defaults and credit scores and to build reliable models for prediction.


2 Data Preparation

2.1 Feature Engineering

We begin by loading the raw data and applying the feature engineering functions. This function scales numerical predictors and creates dummy variables for categorical predictors.

# Load the raw dataset
setwd("/Users/jeffery/Library/Mobile Documents/com~apple~CloudDocs/Documents/Documents - jMacP/WCUPA/Classes/Fall 2025/STA551/Homework/Data")
loan_data <- read.csv("BankLoanDefaultDataset.csv")

# Utilizing the feature engineering function from Part II instead of Part I
create_analytical_features <- function(raw_data) {
  # Step 1: Clean data
  data_cleaned <- raw_data
  data_cleaned$Checking_amount[data_cleaned$Checking_amount < 0] <- 0
  
  # Step 2: Convert to factors
  data_cleaned$Gender <- as.factor(data_cleaned$Gender)
  data_cleaned$Marital_status <- as.factor(data_cleaned$Marital_status)
  data_cleaned$Emp_status <- as.factor(data_cleaned$Emp_status)
  
  # Step 3: Scale numerical predictors
  vars_to_scale <- c("Checking_amount", "Term", "Credit_score",
                     "Saving_amount", "Emp_duration", "Age", "No_of_credit_acc")
  scaled_data <- scale(data_cleaned[, vars_to_scale])
  colnames(scaled_data) <- paste0(vars_to_scale, "_scaled")
  
  # Step 4: Create dummy variables
  dummy_vars <- model.matrix(~ Gender + Marital_status + Emp_status - 1, data = data_cleaned)
  
  # Step 5: Combine all parts, retaining original Credit_score and Amount for response
  binary_vars <- data_cleaned[, c("Car_loan", "Personal_loan", "Home_loan", "Education_loan", "Default")]
  final_data <- cbind(Amount = data_cleaned$Amount, 
                      Credit_score = data_cleaned$Credit_score, # Keep original Credit_score
                      scaled_data, 
                      dummy_vars, 
                      binary_vars)
  
  return(as.data.frame(final_data))
}

# Create the final dataset
regression_data <- create_analytical_features(loan_data)
cat("Dimensions of the final processed dataset:\n")
Dimensions of the final processed dataset:
dim(regression_data)
[1] 1000   18

3 Part I: Exploratory Data Analysis (Summary)

In Part I, we performed an EDA. Key findings included:

  • The dataset was clean with no missing values.
  • Financial variables like Amount and Saving_amount were right-skewed.
  • The Default variable consisted of more non-defaulters (0) than defaulters (1).
  • A visualization showed that customers who defaulted tended to have lower credit scores, which provided a strong basis for our modeling.
# Recreating a  plot from Part I
boxplot(Credit_score ~ Default, data = regression_data,
        main = "Credit Score by Default",
        xlab = "Default (0=No, 1=Yes)", ylab = "Credit Score",
        col = c("darkgreen", "darkred"))
Credit Score by Default Status

Credit Score by Default Status


4 Part II: Statistical Regression Modeling

This part of the project focuses on classical regression modeling for association analysis. We aim to understand the relationships between predictors and our two response variables: Credit_score and Default.

4.1 Linear Regression Analysis

4.1.1 Statement of the Question

The primary goal of this linear regression analysis is association analysis. We answer the question: What customer and loan characteristics are associated with a customer’s Credit score?

Understanding these associations can help identify factors linked to better financial health.

4.1.2 Model Building and Selection Process

4.1.2.1 Initial Model and Diagnostics

We begin by fitting a full model using the original Credit_score as the response. We exclude Amount, Default, and the scaled version of Credit_score from the predictors. We then assess the model’s assumptions using diagnostic plots.

# Build the initial model using the original Credit_score
ini.model <- lm(Credit_score ~ . - Amount - Default - Credit_score_scaled, data = regression_data)

# Assess the assumptions of the initial linear model
par(mfrow = c(2, 2))
plot(ini.model)
Diagnostic Plots for the Initial Linear Model

Diagnostic Plots for the Initial Linear Model

par(mfrow = c(1, 1))

Observations:

The diagnostic plots show a slight curve in the Residuals vs Fitted plot, suggesting potential non-linearity. To address this we will use a Box-Cox plot to investigate if a power transformation of the response variable could improve the model’s fit.

4.1.2.2 Transformation

# Use Box-Cox to find a potential transformation for the response variable
boxcox(ini.model, lambda = seq(-2, 2, 0.1))
Box-Cox Plot for Power Transformation

Box-Cox Plot for Power Transformation

The Box-Cox plot indicates that a lambda value near 0 may be optimal, suggesting a log transformation could be beneficial. We will create a new model with log(Credit_score) as the response.

4.1.2.3 Model Comparison

We now have three candidate models to compare:

  1. ini.model: The full model on the original Credit_score.
  2. transform.model: The full model on log(Credit_score).
  3. final.model: A simplified version of the transformed model selected via step().
# Build a transformed model using log(Credit_score)
transform.model <- lm(log(Credit_score) ~ . - Amount - Default - Credit_score_scaled, data = regression_data)

# Use backward elimination to find final model from the transformed model
final.model <- step(transform.model, direction = "backward", trace = 0)

# Compare the R-squared of all three candidate models
r.ini.model <- summary(ini.model)$r.squared
r.transfd.model <- summary(transform.model)$r.squared
r.final.model <- summary(final.model)$r.squared

# Create a data frame for comparison and display it as a table
Rsquare <- data.frame(
  ini.model = r.ini.model,
  transfd.model = r.transfd.model,
  final.model = r.final.model
)

kable(Rsquare, caption = "Coefficients of Determination (R-squared) for the Three Candidate Models")
Coefficients of Determination (R-squared) for the Three Candidate Models
ini.model transfd.model final.model
0.1425433 0.1491919 0.1456441

4.1.3 Interpretation of the Final Model

The comparison table shows that all three models have very similar R-squared values. The transformed model and the final simplified model offer a slightly better fit than the initial model. Given that the final.model uses less variables while maintaining the highest R-squared we select it as our final working model.

# Display the summary of the final selected model
final_lm_summary <- summary(final.model)
pander(final_lm_summary$coefficients, caption = "Summary of Final Linear Regression Model Coefficients")
Summary of Final Linear Regression Model Coefficients
  Estimate Std. Error t value Pr(>|t|)
(Intercept) 6.63 0.003393 1954 0
Checking_amount_scaled 0.008597 0.003408 2.522 0.01182
Term_scaled -0.01069 0.003346 -3.196 0.001438
Saving_amount_scaled 0.00971 0.00345 2.814 0.004984
Age_scaled 0.02693 0.003594 7.495 1.466e-13
Education_loan -0.0186 0.01039 -1.791 0.07362

The model’s Adjusted R-squared value is .1456, which means that about 14.6% of the variability in the log of Credit_score is explained by the predictors.

Key Findings:

  • Checking_amount_scaled: The coefficient is positive and significant. This indicates that applicants with higher checkings tend to have a higher credit score.
  • Saving_amount_scaled: This is also positively associated with credit score. Having higher savings is linked to a higher score.
  • Term_scaled: Longer loan durations is negatively associated with a higher credit score, suggesting longer term loans are taken by people with lower credit scores.
  • Age_scaled As borrower’s age increases, their credit score increases.

4.2 Logistic Regression Analysis

4.2.1 Statement of the Question

The purpose of this analysis is primarily association. We want to answer: What are the key characteristics of a loan applicant that influence their likelihood of defaulting?

The dataset contains the Default variable and numerous potential predictors.

4.2.2 Model Building

We will start with a “full model” that includes all predictors. For the variable selection process, we will also have a “optimized model” containing only predictors we believe may be important. The step() function will be used for backward selection to find the final model, using AIC as the selection criterion.

# define the full model
full_logistic_model <- glm(Default ~ . - Amount - Credit_score, data = regression_data, family = "binomial")

# define the reduced model with practically important variables
optimized_logistic_model <- glm(Default ~ Credit_score_scaled + Checking_amount_scaled, data = regression_data, family = "binomial")

# using step() for backward selection to find the final model
# the scope is defined by the optimized and full models
final_logistic_model <- step(full_logistic_model,
                             scope = list(lower = formula(optimized_logistic_model), upper = formula(full_logistic_model)),
                             direction = "backward",
                             trace = 0)

4.2.3 Interpretation of the Final Model

The step() function has identified the best model based on AIC.

# Get the summary and display it using pander for clean formatting
final_summary <- summary(final_logistic_model)
pander(final_summary$coefficients, caption = "Summary of Final Logistic Regression Model Coefficients")
Summary of Final Logistic Regression Model Coefficients
  Estimate Std. Error z value Pr(>|z|)
(Intercept) -2.632 0.3837 -6.86 6.895e-12
Checking_amount_scaled -1.709 0.225 -7.595 3.077e-14
Term_scaled 0.5333 0.1677 3.18 0.001474
Credit_score_scaled -0.8611 0.1614 -5.334 9.593e-08
Saving_amount_scaled -1.58 0.2073 -7.623 2.487e-14
Age_scaled -2.643 0.2607 -10.14 3.725e-24
Emp_statusunemployed 0.6117 0.3375 1.813 0.06991
Personal_loan -0.8951 0.3335 -2.684 0.007272
Home_loan -2.79 0.7782 -3.585 0.0003368
Education_loan 1.42 0.5542 2.563 0.01038

Key Findings:

The final model highlights several factors significantly associated with defaulting:

  • Credit_score_scaled: The coefficient is -0.86. This is a strong negative association. A higher credit score makes a default much less likely.
  • Term_scaled: The positive coefficient (0.53) implies that longer loan terms are associated with higher log-odds of default.
  • Checking_amount_scaled: A larger amount in the checking account is associated with lower odds of default (-1.7).
  • Age_scaled: Younger borrowers are more likely to default than older borrowers (-2.6).

5 Part III: Predictive Modeling and Cross-Validation

Now we shift from association to prediction. We will use a 75%/25% split for training and testing. We will then perform 5-fold cross-validation on the training data to select the model with the best predictive performance, which will then be evaluated on the 25% test set.

5.1 Data Splitting (75% Train, 25% Test)

We first shuffle the data and split it into a 75% training set (for CV) and a 25% testing set (for final evaluation).

set.seed(123) # for reproducibility

# Get total sample size
n <- nrow(regression_data)
obs.ID <- 1:n

# Shuffle IDs
shuffled.id <- sample(obs.ID, n, replace = FALSE) # randomizing the observation IDs
shuffled_data <- regression_data[shuffled.id, ] # randomizing the data set

# Define training set size (75%)
n.train <- round(0.75 * n)

# Create training and testing sets
train.data <- shuffled_data[1:n.train, ]
test.data <- shuffled_data[(n.train + 1):n, ]

cat("Training data dimensions:", dim(train.data), "\n")
Training data dimensions: 750 18 
cat("Testing data dimensions:", dim(test.data), "\n")
Testing data dimensions: 250 18 

5.2 Prediction Linear Regression (Credit_score)

We will use 5-fold cross-validation to compare two candidate models based on their Mean Square Error (MSE). We must calculate MSE on the original Credit_score scale, so we will use exp() on our predictions from the log(Credit_score) models.

  • M1 (Full Model): The full model from Part II (transform.model).
  • M2 (Step-Selected Model): The final, simpler model from Part II (final.model).
# Set number of folds
k <- 5
# Define size of 5-fold splitting, -1 to guarantee 5*n.fold <= n.train
n.fold <- round(n.train / k) - 1 

# Initialize vectors to store MSE for each fold
MSE.M1 <- rep(0, k)
MSE.M2 <- rep(0, k)

# Define the model formulas from Part II
formula.M1.linear <- log(Credit_score) ~ . - Amount - Default - Credit_score_scaled
formula.M2.linear <- summary(final.model)$call$formula # Use formula from step() in Part II

for (i in 1:k) {
  # Get validation fold IDs
  valid.id <- ((i - 1) * n.fold + 1):(i * n.fold)
  
  # Define cross-training and cross-validation sets
  cross.train <- train.data[-valid.id, ]
  cross.valid <- train.data[valid.id,]
  
  # Fit candidate models
  lm1 <- lm(formula.M1.linear, data = cross.train)
  lm2 <- lm(formula.M2.linear, data = cross.train)
  
  # Predict on the validation fold
  predM1_log <- predict(lm1, newdata = cross.valid)
  predM2_log <- predict(lm2, newdata = cross.valid)
  
  # Convert predictions back to original scale (exp) and get true values
  predM1_orig <- exp(predM1_log)
  predM2_orig <- exp(predM2_log)
  true_values <- cross.valid$Credit_score
  
  # Calculate MSE on the original scale
  MSE.M1[i] <- mean((predM1_orig - true_values)^2)
  MSE.M2[i] <- mean((predM2_orig - true_values)^2)
  
  cat(paste("Fold", i, "- M1 MSE:", round(MSE.M1[i]), "| M2 MSE:", round(MSE.M2[i]), "\n"))
}
Fold 1 - M1 MSE: 5850 | M2 MSE: 5492 
Fold 2 - M1 MSE: 5257 | M2 MSE: 5046 
Fold 3 - M1 MSE: 7143 | M2 MSE: 6991 
Fold 4 - M1 MSE: 4538 | M2 MSE: 4488 
Fold 5 - M1 MSE: 4947 | M2 MSE: 4850 
# Calculate average MSE across all folds
avg.MSE.M1 <- mean(MSE.M1)
avg.MSE.M2 <- mean(MSE.M2)

cat("M1 (Full Model) Average MSE:", round(avg.MSE.M1), "\n")
M1 (Full Model) Average MSE: 5547 
cat("M2 (Step Model) Average MSE:", round(avg.MSE.M2), "\n")
M2 (Step Model) Average MSE: 5373 
# Plot the MSE results
plot(1:k, MSE.M1, type = "l", col = "navy", lwd = 2,
     xlab = "Iterations 1 to 5", ylab = "MSE", 
     ylim = c(min(c(MSE.M1, MSE.M2)) - 5000, max(c(MSE.M1, MSE.M2)) + 5000),
     xlim = c(0, 6),
     main = "Performance Evaluation via 5-fold Cross Validation",
     cex.main = 0.8)
points(1:k, MSE.M1, pch = 19, col = "navy")
lines(1:k, MSE.M2, type = "l", col = "darkred", lwd = 2)
points(1:k, MSE.M2, pch = 19, col = "darkred")

legend("topright", c(paste("M1 Avg. MSE:", round(avg.MSE.M1)), 
                     paste("M2 Avg. MSE:", round(avg.MSE.M2))),
       col = c("navy", "darkred"), lwd = 2, pch = 19, bty = "n", cex = 0.8)
Linear Model 5-Fold CV Performance (MSE)

Linear Model 5-Fold CV Performance (MSE)

CV Result: The model with the lower average MSE is selected as the best predictive model. Based on the output we choose M2 (Step-Selected Model).

5.3 Prediction Logistic Regression (Default)

We repeat the 5-fold CV process to compare two logistic models. The primary metric will be the Area Under the Curve (AUC) from the ROC analysis.

  • M1 (Full Model): The full logistic model from Part II (full_logistic_model).
  • M2 (Step-Selected Model): The final, simpler logistic model from Part II (final_logistic_model).
# Initialize vectors to store AUC for each fold
AUC.M1 <- rep(0, k)
AUC.M2 <- rep(0, k)

# Use the same folds as the linear CV
# k, n.fold are already defined

# Define the model formulas from Part II
formula.M1.logistic <- Default ~ . - Amount - Credit_score
formula.M2.logistic <- summary(final_logistic_model)$call$formula # Use formula from step()

for (i in 1:k) {
  # Get validation fold IDs
  valid.id <- ((i - 1) * n.fold + 1):(i * n.fold)
  
  # Define cross-training and cross-validation sets
  cross.train <- train.data[-valid.id, ]
  cross.valid <- train.data[valid.id,]
  
  # Fit candidate models
  glm1 <- glm(formula.M1.logistic, data = cross.train, family = "binomial")
  glm2 <- glm(formula.M2.logistic, data = cross.train, family = "binomial")
  
  # Predict probabilities on the validation fold
  predM1_prob <- predict(glm1, newdata = cross.valid, type = "response")
  predM2_prob <- predict(glm2, newdata = cross.valid, type = "response")
  
  # Calculate AUC
  # Using quiet = TRUE to prevent printing messages during the loop
  AUC.M1[i] <- auc(roc(cross.valid$Default, predM1_prob, quiet = TRUE))
  AUC.M2[i] <- auc(roc(cross.valid$Default, predM2_prob, quiet = TRUE))
  
  cat(paste("Fold", i, "- M1 AUC:", round(AUC.M1[i], 4), "| M2 AUC:", round(AUC.M2[i], 4), "\n"))
}
Fold 1 - M1 AUC: 0.9704 | M2 AUC: 0.9691 
Fold 2 - M1 AUC: 0.9783 | M2 AUC: 0.9814 
Fold 3 - M1 AUC: 0.986 | M2 AUC: 0.9847 
Fold 4 - M1 AUC: 0.9829 | M2 AUC: 0.9839 
Fold 5 - M1 AUC: 0.9795 | M2 AUC: 0.9791 
# Calculate average AUC across all folds
avg.AUC.M1 <- mean(AUC.M1)
avg.AUC.M2 <- mean(AUC.M2)

cat("M1 (Full Model) Average AUC:", round(avg.AUC.M1, 4), "\n")
M1 (Full Model) Average AUC: 0.9794 
cat("M2 (Step Model) Average AUC:", round(avg.AUC.M2, 4), "\n")
M2 (Step Model) Average AUC: 0.9796 
# Plot the AUC results
plot(1:k, AUC.M1, type = "l", col = "navy", lwd = 2,
     xlab = "Iterations 1 to 5", ylab = "AUC", 
     ylim = c(min(c(AUC.M1, AUC.M2)) - 0.05, max(c(AUC.M1, AUC.M2)) + 0.05),
     xlim = c(0, 6),
     main = "Performance Evaluation via 5-fold Cross Validation",
     cex.main = 0.8)
points(1:k, AUC.M1, pch = 19, col = "navy")
lines(1:k, AUC.M2, type = "l", col = "darkred", lwd = 2)
points(1:k, AUC.M2, pch = 19, col = "darkred")

legend("bottomright", c(paste("M1 Avg. AUC:", round(avg.AUC.M1, 4)), 
                        paste("M2 Avg. AUC:", round(avg.AUC.M2, 4))),
       col = c("navy", "darkred"), lwd = 2, pch = 19, bty = "n", cex = 0.8)
Logistic Model 5-Fold CV Performance (AUC)

Logistic Model 5-Fold CV Performance (AUC)

CV Result: The model with the higher average AUC is selected. Based on the output, we choose the M2 (Step-Selected Model) as it performs a bit better than the full model while being simpler.

5.4 Final Model Testing

We will now train our chosen models (M2 for both) on the entire 75% training set and report their final performance on the 25% testing set.

5.4.1 Final Linear Model (M2) Performance

# 1. Re-train the winning model (M2) on the entire training set
final.lm.test <- lm(formula.M2.linear, data = train.data)

# 2. Predict on the 25% test set
pred.lm.test_log <- predict(final.lm.test, newdata = test.data)

# 3. Convert predictions to original scale
pred.lm.test_orig <- exp(pred.lm.test_log)
true_values_test <- test.data$Credit_score

# 4. Calculate and report the final MSE on the test data
final.MSE <- mean((pred.lm.test_orig - true_values_test)^2)

cat("Final MSE on 25% Test Set:", round(final.MSE), "\n")
Final MSE on 25% Test Set: 5106 

5.4.2 Final Logistic Model (M2) Performance

# 1. Re-train the winning model (M2) on the entire training set
final.glm.test <- glm(formula.M2.logistic, data = train.data, family = "binomial")

# 2. Predict probabilities on the 25% test set
pred.glm.test_prob <- predict(final.glm.test, newdata = test.data, type = "response")

# 3. Calculate and report the final AUC and plot the ROC curve
final.roc.obj <- roc(test.data$Default, pred.glm.test_prob)
final.AUC <- auc(final.roc.obj)

cat("Final AUC on 25% Test Set:", round(final.AUC, 4), "\n")
Final AUC on 25% Test Set: 0.9888 
# Plot the final ROC curve
plot(final.roc.obj, main = "Final Model ROC Curve (Test Data)", 
     col = "darkblue", lwd = 2, print.auc = TRUE)
Final ROC Curve on 25% Test Data

Final ROC Curve on 25% Test Data


6 Conclusion

This project has analyzed the bank loan dataset from three perspectives:

  • Part I (EDA) revealed key relationships between Credit_score and Default.
  • Part II (Statistical Modeling) identified significant associative factors for both log(Credit_score) and Default using classical regression. The selection of these models was justified by a combination of checks (like Box-Cox transformations) and statistical criteria (R-squared and AIC-based backward selection).
  • Part III (Predictive Modeling) used 5-fold cross-validation to select the best predictive models, shifting the focus to performance metrics like MSE and AUC. For both the linear (Credit_score) and logistic (Default) problems, the simpler, step-selected models (M2) from Part II were confirmed to be the better choice for prediction.

The final models were tested yielding a Final MSE of 5106 for predicting Credit_score and a Final AUC of 0.9888 for predicting Default. This demonstrates that our final step-selected models are not only valid (as shown in Part II) but also are reliable for prediction (as confirmed in Part III).

LS0tCnRpdGxlOiAnUHJvamVjdCBPbmU6IENvbXByZWhlbnNpdmUgQW5hbHlzaXMgLSBFREEsIFJlZ3Jlc3Npb24sIGFuZCBQcmVkaWN0aXZlIE1vZGVsaW5nJwphdXRob3I6ICdKZWZmIERlbHZhJwpkYXRlOiAiT2N0b2JlciAxNSwgMjAyNSIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX3dpZHRoOiA4CiAgICBmaWdfaGVpZ2h0OiA1CiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdG9jX2NvbGxhcHNlZDogeWVzCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgc21vb3RoX3Njcm9sbDogeWVzCiAgICB0aGVtZTogbHVtZW4KICAgIGhpZ2hsaWdodDogdGFuZ28KLS0tCgpgYGB7Y3NzLCBlY2hvID0gRkFMU0V9CmgxLnRpdGxlIHsKICBmb250LXNpemU6IDI0cHg7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgY29sb3I6IERhcmtSZWQ7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9Cmg0LmF1dGhvciwgaDQuZGF0ZSB7CiAgZm9udC1zaXplOiAxOHB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogIGNvbG9yOiBEYXJrQmx1ZTsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KaDEgewogICAgZm9udC1zaXplOiAyMHB4OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBkYXJrcmVkOwogICAgdGV4dC1hbGlnbjogY2VudGVyOwp9CmgyIHsKICAgIGZvbnQtc2l6ZTogMThweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KaDMgewogICAgZm9udC1zaXplOiAxNnB4OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQouaGVhZGVyLXNlY3Rpb24tbnVtYmVyOjphZnRlciB7CiAgY29udGVudDogIi4iOwp9CmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmlmICghcmVxdWlyZSgia25pdHIiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpCiAgIGxpYnJhcnkoa25pdHIpCn0KaWYgKCFyZXF1aXJlKCJwYW5kZXIiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJwYW5kZXIiKQogICBsaWJyYXJ5KHBhbmRlcikKfQppZiAoIXJlcXVpcmUoIk1BU1MiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJNQVNTIikKICAgbGlicmFyeShNQVNTKQp9CmlmICghcmVxdWlyZSgicFJPQyIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInBST0MiKQogICBsaWJyYXJ5KHBST0MpCn0KCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BCiAgICAgICAgICAgICAgICAgICAgICApCmBgYAoKIyBJbnRyb2R1Y3Rpb24KClRoaXMgcmVwb3J0IHByZXNlbnRzIGEgY29tcHJlaGVuc2l2ZSBhbmFseXNpcyBvZiB0aGUgYmFuayBsb2FuIGRlZmF1bHQgZGF0YXNldC4gVGhpcyBwcm9qZWN0IGNvbnNpc3RzIG9mIHRocmVlIHBhcnRzOgoKMS4gICoqUGFydCBJOioqIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgKEVEQSkgYW5kIGZlYXR1cmUgZW5naW5lZXJpbmcuCjIuICAqKlBhcnQgSUk6KiogQ2xhc3NpY2FsIHN0YXRpc3RpY2FsIG1vZGVsaW5nIGZvY3VzZWQgb24gKiphc3NvY2lhdGlvbiBhbmFseXNpcyoqIGZvciBgQ3JlZGl0X3Njb3JlYCAoTGluZWFyIFJlZ3Jlc3Npb24pIGFuZCBgRGVmYXVsdGAgKExvZ2lzdGljIFJlZ3Jlc3Npb24pLgozLiAgKipQYXJ0IElJSToqKiAqKlByZWRpY3RpdmUgbW9kZWxpbmcqKiB1c2luZyBjcm9zcy12YWxpZGF0aW9uIHRvIHNlbGVjdCB0aGUgYmVzdCBwcmVkaWN0aXZlIG1vZGVscyBhbmQgZXZhbHVhdGluZyB0aGVtIG9uIHRyYWluIHRlc3QgZGF0YS4KClRoZSBvdmVyYWxsIGdvYWwgaXMgdG8gdW5kZXJzdGFuZCB0aGUgZmFjdG9ycyByZWxhdGVkIHRvIGxvYW4gZGVmYXVsdHMgYW5kIGNyZWRpdCBzY29yZXMgYW5kIHRvIGJ1aWxkIHJlbGlhYmxlIG1vZGVscyBmb3IgcHJlZGljdGlvbi4KCi0tLS0tCgojIERhdGEgUHJlcGFyYXRpb24KCiMjIEZlYXR1cmUgRW5naW5lZXJpbmcKCldlIGJlZ2luIGJ5IGxvYWRpbmcgdGhlIHJhdyBkYXRhIGFuZCBhcHBseWluZyB0aGUgZmVhdHVyZSBlbmdpbmVlcmluZyBmdW5jdGlvbnMuIFRoaXMgZnVuY3Rpb24gc2NhbGVzIG51bWVyaWNhbCBwcmVkaWN0b3JzIGFuZCBjcmVhdGVzIGR1bW15IHZhcmlhYmxlcyBmb3IgY2F0ZWdvcmljYWwgcHJlZGljdG9ycy4KCmBgYHtyIHByZXBhcmUtZGF0YX0KIyBMb2FkIHRoZSByYXcgZGF0YXNldApzZXR3ZCgiL1VzZXJzL2plZmZlcnkvTGlicmFyeS9Nb2JpbGUgRG9jdW1lbnRzL2NvbX5hcHBsZX5DbG91ZERvY3MvRG9jdW1lbnRzL0RvY3VtZW50cyAtIGpNYWNQL1dDVVBBL0NsYXNzZXMvRmFsbCAyMDI1L1NUQTU1MS9Ib21ld29yay9EYXRhIikKbG9hbl9kYXRhIDwtIHJlYWQuY3N2KCJCYW5rTG9hbkRlZmF1bHREYXRhc2V0LmNzdiIpCgojIFV0aWxpemluZyB0aGUgZmVhdHVyZSBlbmdpbmVlcmluZyBmdW5jdGlvbiBmcm9tIFBhcnQgSUkgaW5zdGVhZCBvZiBQYXJ0IEkKY3JlYXRlX2FuYWx5dGljYWxfZmVhdHVyZXMgPC0gZnVuY3Rpb24ocmF3X2RhdGEpIHsKICAjIFN0ZXAgMTogQ2xlYW4gZGF0YQogIGRhdGFfY2xlYW5lZCA8LSByYXdfZGF0YQogIGRhdGFfY2xlYW5lZCRDaGVja2luZ19hbW91bnRbZGF0YV9jbGVhbmVkJENoZWNraW5nX2Ftb3VudCA8IDBdIDwtIDAKICAKICAjIFN0ZXAgMjogQ29udmVydCB0byBmYWN0b3JzCiAgZGF0YV9jbGVhbmVkJEdlbmRlciA8LSBhcy5mYWN0b3IoZGF0YV9jbGVhbmVkJEdlbmRlcikKICBkYXRhX2NsZWFuZWQkTWFyaXRhbF9zdGF0dXMgPC0gYXMuZmFjdG9yKGRhdGFfY2xlYW5lZCRNYXJpdGFsX3N0YXR1cykKICBkYXRhX2NsZWFuZWQkRW1wX3N0YXR1cyA8LSBhcy5mYWN0b3IoZGF0YV9jbGVhbmVkJEVtcF9zdGF0dXMpCiAgCiAgIyBTdGVwIDM6IFNjYWxlIG51bWVyaWNhbCBwcmVkaWN0b3JzCiAgdmFyc190b19zY2FsZSA8LSBjKCJDaGVja2luZ19hbW91bnQiLCAiVGVybSIsICJDcmVkaXRfc2NvcmUiLAogICAgICAgICAgICAgICAgICAgICAiU2F2aW5nX2Ftb3VudCIsICJFbXBfZHVyYXRpb24iLCAiQWdlIiwgIk5vX29mX2NyZWRpdF9hY2MiKQogIHNjYWxlZF9kYXRhIDwtIHNjYWxlKGRhdGFfY2xlYW5lZFssIHZhcnNfdG9fc2NhbGVdKQogIGNvbG5hbWVzKHNjYWxlZF9kYXRhKSA8LSBwYXN0ZTAodmFyc190b19zY2FsZSwgIl9zY2FsZWQiKQogIAogICMgU3RlcCA0OiBDcmVhdGUgZHVtbXkgdmFyaWFibGVzCiAgZHVtbXlfdmFycyA8LSBtb2RlbC5tYXRyaXgofiBHZW5kZXIgKyBNYXJpdGFsX3N0YXR1cyArIEVtcF9zdGF0dXMgLSAxLCBkYXRhID0gZGF0YV9jbGVhbmVkKQogIAogICMgU3RlcCA1OiBDb21iaW5lIGFsbCBwYXJ0cywgcmV0YWluaW5nIG9yaWdpbmFsIENyZWRpdF9zY29yZSBhbmQgQW1vdW50IGZvciByZXNwb25zZQogIGJpbmFyeV92YXJzIDwtIGRhdGFfY2xlYW5lZFssIGMoIkNhcl9sb2FuIiwgIlBlcnNvbmFsX2xvYW4iLCAiSG9tZV9sb2FuIiwgIkVkdWNhdGlvbl9sb2FuIiwgIkRlZmF1bHQiKV0KICBmaW5hbF9kYXRhIDwtIGNiaW5kKEFtb3VudCA9IGRhdGFfY2xlYW5lZCRBbW91bnQsIAogICAgICAgICAgICAgICAgICAgICAgQ3JlZGl0X3Njb3JlID0gZGF0YV9jbGVhbmVkJENyZWRpdF9zY29yZSwgIyBLZWVwIG9yaWdpbmFsIENyZWRpdF9zY29yZQogICAgICAgICAgICAgICAgICAgICAgc2NhbGVkX2RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgZHVtbXlfdmFycywgCiAgICAgICAgICAgICAgICAgICAgICBiaW5hcnlfdmFycykKICAKICByZXR1cm4oYXMuZGF0YS5mcmFtZShmaW5hbF9kYXRhKSkKfQoKIyBDcmVhdGUgdGhlIGZpbmFsIGRhdGFzZXQKcmVncmVzc2lvbl9kYXRhIDwtIGNyZWF0ZV9hbmFseXRpY2FsX2ZlYXR1cmVzKGxvYW5fZGF0YSkKY2F0KCJEaW1lbnNpb25zIG9mIHRoZSBmaW5hbCBwcm9jZXNzZWQgZGF0YXNldDpcbiIpCmRpbShyZWdyZXNzaW9uX2RhdGEpCmBgYAoKLS0tLS0KCiMgUGFydCBJOiBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIChTdW1tYXJ5KQoKSW4gUGFydCBJLCB3ZSBwZXJmb3JtZWQgYW4gRURBLiBLZXkgZmluZGluZ3MgaW5jbHVkZWQ6CgogICogVGhlIGRhdGFzZXQgd2FzIGNsZWFuIHdpdGggbm8gbWlzc2luZyB2YWx1ZXMuCiAgKiBGaW5hbmNpYWwgdmFyaWFibGVzIGxpa2UgYEFtb3VudGAgYW5kIGBTYXZpbmdfYW1vdW50YCB3ZXJlIHJpZ2h0LXNrZXdlZC4KICAqIFRoZSBgRGVmYXVsdGAgdmFyaWFibGUgY29uc2lzdGVkIG9mIG1vcmUgbm9uLWRlZmF1bHRlcnMgKDApIHRoYW4gZGVmYXVsdGVycyAoMSkuCiAgKiBBIHZpc3VhbGl6YXRpb24gc2hvd2VkIHRoYXQgY3VzdG9tZXJzIHdobyBkZWZhdWx0ZWQgdGVuZGVkIHRvIGhhdmUgbG93ZXIgY3JlZGl0IHNjb3Jlcywgd2hpY2ggcHJvdmlkZWQgYSBzdHJvbmcgYmFzaXMgZm9yIG91ciBtb2RlbGluZy4KCmBgYHtyIHBsb3QtZGVmYXVsdC1zdW1tYXJ5LCBmaWcuY2FwPSJDcmVkaXQgU2NvcmUgYnkgRGVmYXVsdCBTdGF0dXMifQojIFJlY3JlYXRpbmcgYSAgcGxvdCBmcm9tIFBhcnQgSQpib3hwbG90KENyZWRpdF9zY29yZSB+IERlZmF1bHQsIGRhdGEgPSByZWdyZXNzaW9uX2RhdGEsCiAgICAgICAgbWFpbiA9ICJDcmVkaXQgU2NvcmUgYnkgRGVmYXVsdCIsCiAgICAgICAgeGxhYiA9ICJEZWZhdWx0ICgwPU5vLCAxPVllcykiLCB5bGFiID0gIkNyZWRpdCBTY29yZSIsCiAgICAgICAgY29sID0gYygiZGFya2dyZWVuIiwgImRhcmtyZWQiKSkKYGBgCgotLS0tLQoKIyBQYXJ0IElJOiBTdGF0aXN0aWNhbCBSZWdyZXNzaW9uIE1vZGVsaW5nCgpUaGlzIHBhcnQgb2YgdGhlIHByb2plY3QgZm9jdXNlcyBvbiBjbGFzc2ljYWwgcmVncmVzc2lvbiBtb2RlbGluZyBmb3IgKiphc3NvY2lhdGlvbiBhbmFseXNpcyoqLiBXZSBhaW0gdG8gdW5kZXJzdGFuZCB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHByZWRpY3RvcnMgYW5kIG91ciB0d28gcmVzcG9uc2UgdmFyaWFibGVzOiBgQ3JlZGl0X3Njb3JlYCBhbmQgYERlZmF1bHRgLgoKIyMgTGluZWFyIFJlZ3Jlc3Npb24gQW5hbHlzaXMKCiMjIyBTdGF0ZW1lbnQgb2YgdGhlIFF1ZXN0aW9uCgpUaGUgcHJpbWFyeSBnb2FsIG9mIHRoaXMgbGluZWFyIHJlZ3Jlc3Npb24gYW5hbHlzaXMgaXMgKiphc3NvY2lhdGlvbiBhbmFseXNpcyoqLiBXZSBhbnN3ZXIgdGhlIHF1ZXN0aW9uOiAqKldoYXQgY3VzdG9tZXIgYW5kIGxvYW4gY2hhcmFjdGVyaXN0aWNzIGFyZSBhc3NvY2lhdGVkIHdpdGggYSBjdXN0b21lcidzIGBDcmVkaXQgc2NvcmVgPyoqCgpVbmRlcnN0YW5kaW5nIHRoZXNlIGFzc29jaWF0aW9ucyBjYW4gaGVscCBpZGVudGlmeSBmYWN0b3JzIGxpbmtlZCB0byBiZXR0ZXIgZmluYW5jaWFsIGhlYWx0aC4KCiMjIyBNb2RlbCBCdWlsZGluZyBhbmQgU2VsZWN0aW9uIFByb2Nlc3MKCiMjIyMgSW5pdGlhbCBNb2RlbCBhbmQgRGlhZ25vc3RpY3MKCldlIGJlZ2luIGJ5IGZpdHRpbmcgYSBmdWxsIG1vZGVsIHVzaW5nIHRoZSBvcmlnaW5hbCBgQ3JlZGl0X3Njb3JlYCBhcyB0aGUgcmVzcG9uc2UuIFdlIGV4Y2x1ZGUgYEFtb3VudGAsIGBEZWZhdWx0YCwgYW5kIHRoZSBzY2FsZWQgdmVyc2lvbiBvZiBgQ3JlZGl0X3Njb3JlYCBmcm9tIHRoZSBwcmVkaWN0b3JzLiBXZSB0aGVuIGFzc2VzcyB0aGUgbW9kZWwncyBhc3N1bXB0aW9ucyB1c2luZyBkaWFnbm9zdGljIHBsb3RzLgoKYGBge3IgaW5pdGlhbC1sbSwgZmlnLmNhcD0iRGlhZ25vc3RpYyBQbG90cyBmb3IgdGhlIEluaXRpYWwgTGluZWFyIE1vZGVsIn0KIyBCdWlsZCB0aGUgaW5pdGlhbCBtb2RlbCB1c2luZyB0aGUgb3JpZ2luYWwgQ3JlZGl0X3Njb3JlCmluaS5tb2RlbCA8LSBsbShDcmVkaXRfc2NvcmUgfiAuIC0gQW1vdW50IC0gRGVmYXVsdCAtIENyZWRpdF9zY29yZV9zY2FsZWQsIGRhdGEgPSByZWdyZXNzaW9uX2RhdGEpCgojIEFzc2VzcyB0aGUgYXNzdW1wdGlvbnMgb2YgdGhlIGluaXRpYWwgbGluZWFyIG1vZGVsCnBhcihtZnJvdyA9IGMoMiwgMikpCnBsb3QoaW5pLm1vZGVsKQpwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCioqT2JzZXJ2YXRpb25zOioqCgpUaGUgZGlhZ25vc3RpYyBwbG90cyBzaG93IGEgc2xpZ2h0IGN1cnZlIGluIHRoZSAqKlJlc2lkdWFscyB2cyBGaXR0ZWQqKiBwbG90LCBzdWdnZXN0aW5nIHBvdGVudGlhbCBub24tbGluZWFyaXR5LiBUbyBhZGRyZXNzIHRoaXMgd2Ugd2lsbCB1c2UgYSBCb3gtQ294IHBsb3QgdG8gaW52ZXN0aWdhdGUgaWYgYSBwb3dlciB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgcmVzcG9uc2UgdmFyaWFibGUgY291bGQgaW1wcm92ZSB0aGUgbW9kZWwncyBmaXQuCgojIyMjIFRyYW5zZm9ybWF0aW9uCgpgYGB7ciBib3gtY294LXRyYW5zZm9ybWF0aW9uLCBmaWcuY2FwPSJCb3gtQ294IFBsb3QgZm9yIFBvd2VyIFRyYW5zZm9ybWF0aW9uIn0KIyBVc2UgQm94LUNveCB0byBmaW5kIGEgcG90ZW50aWFsIHRyYW5zZm9ybWF0aW9uIGZvciB0aGUgcmVzcG9uc2UgdmFyaWFibGUKYm94Y294KGluaS5tb2RlbCwgbGFtYmRhID0gc2VxKC0yLCAyLCAwLjEpKQpgYGAKClRoZSBCb3gtQ294IHBsb3QgaW5kaWNhdGVzIHRoYXQgYSBsYW1iZGEgdmFsdWUgbmVhciAwIG1heSBiZSBvcHRpbWFsLCBzdWdnZXN0aW5nIGEgKipsb2cgdHJhbnNmb3JtYXRpb24qKiBjb3VsZCBiZSBiZW5lZmljaWFsLiBXZSB3aWxsIGNyZWF0ZSBhIG5ldyBtb2RlbCB3aXRoIGBsb2coQ3JlZGl0X3Njb3JlKWAgYXMgdGhlIHJlc3BvbnNlLgoKIyMjIyBNb2RlbCBDb21wYXJpc29uCgpXZSBub3cgaGF2ZSB0aHJlZSBjYW5kaWRhdGUgbW9kZWxzIHRvIGNvbXBhcmU6CgoxLiAgYGluaS5tb2RlbGA6IFRoZSBmdWxsIG1vZGVsIG9uIHRoZSBvcmlnaW5hbCBgQ3JlZGl0X3Njb3JlYC4KMi4gIGB0cmFuc2Zvcm0ubW9kZWxgOiBUaGUgZnVsbCBtb2RlbCBvbiBgbG9nKENyZWRpdF9zY29yZSlgLgozLiAgYGZpbmFsLm1vZGVsYDogQSBzaW1wbGlmaWVkIHZlcnNpb24gb2YgdGhlIHRyYW5zZm9ybWVkIG1vZGVsIHNlbGVjdGVkIHZpYSBgc3RlcCgpYC4KCmBgYHtyIGNvbXBhcmlzb24tY3JlZGl0LXNjb3JlfQojIEJ1aWxkIGEgdHJhbnNmb3JtZWQgbW9kZWwgdXNpbmcgbG9nKENyZWRpdF9zY29yZSkKdHJhbnNmb3JtLm1vZGVsIDwtIGxtKGxvZyhDcmVkaXRfc2NvcmUpIH4gLiAtIEFtb3VudCAtIERlZmF1bHQgLSBDcmVkaXRfc2NvcmVfc2NhbGVkLCBkYXRhID0gcmVncmVzc2lvbl9kYXRhKQoKIyBVc2UgYmFja3dhcmQgZWxpbWluYXRpb24gdG8gZmluZCBmaW5hbCBtb2RlbCBmcm9tIHRoZSB0cmFuc2Zvcm1lZCBtb2RlbApmaW5hbC5tb2RlbCA8LSBzdGVwKHRyYW5zZm9ybS5tb2RlbCwgZGlyZWN0aW9uID0gImJhY2t3YXJkIiwgdHJhY2UgPSAwKQoKIyBDb21wYXJlIHRoZSBSLXNxdWFyZWQgb2YgYWxsIHRocmVlIGNhbmRpZGF0ZSBtb2RlbHMKci5pbmkubW9kZWwgPC0gc3VtbWFyeShpbmkubW9kZWwpJHIuc3F1YXJlZApyLnRyYW5zZmQubW9kZWwgPC0gc3VtbWFyeSh0cmFuc2Zvcm0ubW9kZWwpJHIuc3F1YXJlZApyLmZpbmFsLm1vZGVsIDwtIHN1bW1hcnkoZmluYWwubW9kZWwpJHIuc3F1YXJlZAoKIyBDcmVhdGUgYSBkYXRhIGZyYW1lIGZvciBjb21wYXJpc29uIGFuZCBkaXNwbGF5IGl0IGFzIGEgdGFibGUKUnNxdWFyZSA8LSBkYXRhLmZyYW1lKAogIGluaS5tb2RlbCA9IHIuaW5pLm1vZGVsLAogIHRyYW5zZmQubW9kZWwgPSByLnRyYW5zZmQubW9kZWwsCiAgZmluYWwubW9kZWwgPSByLmZpbmFsLm1vZGVsCikKCmthYmxlKFJzcXVhcmUsIGNhcHRpb24gPSAiQ29lZmZpY2llbnRzIG9mIERldGVybWluYXRpb24gKFItc3F1YXJlZCkgZm9yIHRoZSBUaHJlZSBDYW5kaWRhdGUgTW9kZWxzIikKYGBgCgojIyMgSW50ZXJwcmV0YXRpb24gb2YgdGhlIEZpbmFsIE1vZGVsCgpUaGUgY29tcGFyaXNvbiB0YWJsZSBzaG93cyB0aGF0IGFsbCB0aHJlZSBtb2RlbHMgaGF2ZSB2ZXJ5IHNpbWlsYXIgUi1zcXVhcmVkIHZhbHVlcy4gVGhlIHRyYW5zZm9ybWVkIG1vZGVsIGFuZCB0aGUgZmluYWwgc2ltcGxpZmllZCBtb2RlbCBvZmZlciBhIHNsaWdodGx5IGJldHRlciBmaXQgdGhhbiB0aGUgaW5pdGlhbCBtb2RlbC4gR2l2ZW4gdGhhdCB0aGUgYGZpbmFsLm1vZGVsYCB1c2VzIGxlc3MgdmFyaWFibGVzIHdoaWxlIG1haW50YWluaW5nIHRoZSBoaWdoZXN0IFItc3F1YXJlZCB3ZSBzZWxlY3QgaXQgYXMgb3VyIGZpbmFsIHdvcmtpbmcgbW9kZWwuCgpgYGB7ciBmaW5hbC1saW5lYXItc3VtbWFyeSwgcmVzdWx0cz0nYXNpcyd9CiMgRGlzcGxheSB0aGUgc3VtbWFyeSBvZiB0aGUgZmluYWwgc2VsZWN0ZWQgbW9kZWwKZmluYWxfbG1fc3VtbWFyeSA8LSBzdW1tYXJ5KGZpbmFsLm1vZGVsKQpwYW5kZXIoZmluYWxfbG1fc3VtbWFyeSRjb2VmZmljaWVudHMsIGNhcHRpb24gPSAiU3VtbWFyeSBvZiBGaW5hbCBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbCBDb2VmZmljaWVudHMiKQpgYGAKClRoZSBtb2RlbCdzIEFkanVzdGVkIFItc3F1YXJlZCB2YWx1ZSBpcyAqKi4xNDU2KiosIHdoaWNoIG1lYW5zIHRoYXQgYWJvdXQgKioxNC42JSoqIG9mIHRoZSB2YXJpYWJpbGl0eSBpbiB0aGUgKipsb2cgb2YgYENyZWRpdF9zY29yZWAqKiBpcyBleHBsYWluZWQgYnkgdGhlIHByZWRpY3RvcnMuCgoqKktleSBGaW5kaW5nczoqKgoKICAqICoqQ2hlY2tpbmdcX2Ftb3VudFxfc2NhbGVkOioqIFRoZSBjb2VmZmljaWVudCBpcyBwb3NpdGl2ZSBhbmQgc2lnbmlmaWNhbnQuIFRoaXMgaW5kaWNhdGVzIHRoYXQgYXBwbGljYW50cyB3aXRoIGhpZ2hlciBjaGVja2luZ3MgdGVuZCB0byBoYXZlIGEgaGlnaGVyIGNyZWRpdCBzY29yZS4KICAqICoqU2F2aW5nXF9hbW91bnRcX3NjYWxlZDoqKiBUaGlzIGlzIGFsc28gcG9zaXRpdmVseSBhc3NvY2lhdGVkIHdpdGggY3JlZGl0IHNjb3JlLiBIYXZpbmcgaGlnaGVyIHNhdmluZ3MgaXMgbGlua2VkIHRvIGEgaGlnaGVyIHNjb3JlLgogICogKipUZXJtXF9zY2FsZWQ6KiogTG9uZ2VyIGxvYW4gZHVyYXRpb25zIGlzIG5lZ2F0aXZlbHkgYXNzb2NpYXRlZCB3aXRoIGEgaGlnaGVyIGNyZWRpdCBzY29yZSwgc3VnZ2VzdGluZyBsb25nZXIgdGVybSBsb2FucyBhcmUgdGFrZW4gYnkgcGVvcGxlIHdpdGggbG93ZXIgY3JlZGl0IHNjb3Jlcy4KICAqICoqQWdlXF9zY2FsZWQqKiBBcyBib3Jyb3dlcidzIGFnZSBpbmNyZWFzZXMsIHRoZWlyIGNyZWRpdCBzY29yZSBpbmNyZWFzZXMuCgojIyBMb2dpc3RpYyBSZWdyZXNzaW9uIEFuYWx5c2lzCgojIyMgU3RhdGVtZW50IG9mIHRoZSBRdWVzdGlvbgoKVGhlIHB1cnBvc2Ugb2YgdGhpcyBhbmFseXNpcyBpcyBwcmltYXJpbHkgKiphc3NvY2lhdGlvbioqLiBXZSB3YW50IHRvIGFuc3dlcjogKipXaGF0IGFyZSB0aGUga2V5IGNoYXJhY3RlcmlzdGljcyBvZiBhIGxvYW4gYXBwbGljYW50IHRoYXQgaW5mbHVlbmNlIHRoZWlyIGxpa2VsaWhvb2Qgb2YgZGVmYXVsdGluZz8qKgoKVGhlIGRhdGFzZXQgY29udGFpbnMgdGhlIGBEZWZhdWx0YCB2YXJpYWJsZSBhbmQgbnVtZXJvdXMgcG90ZW50aWFsIHByZWRpY3RvcnMuCgojIyMgTW9kZWwgQnVpbGRpbmcKCldlIHdpbGwgc3RhcnQgd2l0aCBhICJmdWxsIG1vZGVsIiB0aGF0IGluY2x1ZGVzIGFsbCBwcmVkaWN0b3JzLiBGb3IgdGhlIHZhcmlhYmxlIHNlbGVjdGlvbiBwcm9jZXNzLCB3ZSB3aWxsIGFsc28gaGF2ZSBhICJvcHRpbWl6ZWQgbW9kZWwiIGNvbnRhaW5pbmcgb25seSBwcmVkaWN0b3JzIHdlIGJlbGlldmUgbWF5IGJlIGltcG9ydGFudC4gVGhlIGBzdGVwKClgIGZ1bmN0aW9uIHdpbGwgYmUgdXNlZCBmb3IgYmFja3dhcmQgc2VsZWN0aW9uIHRvIGZpbmQgdGhlIGZpbmFsIG1vZGVsLCB1c2luZyBBSUMgYXMgdGhlIHNlbGVjdGlvbiBjcml0ZXJpb24uCgpgYGB7ciBsb2dpc3RpYy1tb2RlbC1idWlsZGluZ30KIyBkZWZpbmUgdGhlIGZ1bGwgbW9kZWwKZnVsbF9sb2dpc3RpY19tb2RlbCA8LSBnbG0oRGVmYXVsdCB+IC4gLSBBbW91bnQgLSBDcmVkaXRfc2NvcmUsIGRhdGEgPSByZWdyZXNzaW9uX2RhdGEsIGZhbWlseSA9ICJiaW5vbWlhbCIpCgojIGRlZmluZSB0aGUgcmVkdWNlZCBtb2RlbCB3aXRoIHByYWN0aWNhbGx5IGltcG9ydGFudCB2YXJpYWJsZXMKb3B0aW1pemVkX2xvZ2lzdGljX21vZGVsIDwtIGdsbShEZWZhdWx0IH4gQ3JlZGl0X3Njb3JlX3NjYWxlZCArIENoZWNraW5nX2Ftb3VudF9zY2FsZWQsIGRhdGEgPSByZWdyZXNzaW9uX2RhdGEsIGZhbWlseSA9ICJiaW5vbWlhbCIpCgojIHVzaW5nIHN0ZXAoKSBmb3IgYmFja3dhcmQgc2VsZWN0aW9uIHRvIGZpbmQgdGhlIGZpbmFsIG1vZGVsCiMgdGhlIHNjb3BlIGlzIGRlZmluZWQgYnkgdGhlIG9wdGltaXplZCBhbmQgZnVsbCBtb2RlbHMKZmluYWxfbG9naXN0aWNfbW9kZWwgPC0gc3RlcChmdWxsX2xvZ2lzdGljX21vZGVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjb3BlID0gbGlzdChsb3dlciA9IGZvcm11bGEob3B0aW1pemVkX2xvZ2lzdGljX21vZGVsKSwgdXBwZXIgPSBmb3JtdWxhKGZ1bGxfbG9naXN0aWNfbW9kZWwpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAiYmFja3dhcmQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWNlID0gMCkKYGBgCgojIyMgSW50ZXJwcmV0YXRpb24gb2YgdGhlIEZpbmFsIE1vZGVsCgpUaGUgYHN0ZXAoKWAgZnVuY3Rpb24gaGFzIGlkZW50aWZpZWQgdGhlIGJlc3QgbW9kZWwgYmFzZWQgb24gQUlDLgoKYGBge3IgZmluYWwtbG9naXN0aWMtc3VtbWFyeSwgcmVzdWx0cz0nYXNpcyd9CiMgR2V0IHRoZSBzdW1tYXJ5IGFuZCBkaXNwbGF5IGl0IHVzaW5nIHBhbmRlciBmb3IgY2xlYW4gZm9ybWF0dGluZwpmaW5hbF9zdW1tYXJ5IDwtIHN1bW1hcnkoZmluYWxfbG9naXN0aWNfbW9kZWwpCnBhbmRlcihmaW5hbF9zdW1tYXJ5JGNvZWZmaWNpZW50cywgY2FwdGlvbiA9ICJTdW1tYXJ5IG9mIEZpbmFsIExvZ2lzdGljIFJlZ3Jlc3Npb24gTW9kZWwgQ29lZmZpY2llbnRzIikKYGBgCgoqKktleSBGaW5kaW5nczoqKgoKVGhlIGZpbmFsIG1vZGVsIGhpZ2hsaWdodHMgc2V2ZXJhbCBmYWN0b3JzIHNpZ25pZmljYW50bHkgYXNzb2NpYXRlZCB3aXRoIGRlZmF1bHRpbmc6CgogICogKipDcmVkaXRcX3Njb3JlXF9zY2FsZWQ6KiogVGhlIGNvZWZmaWNpZW50IGlzICoqLTAuODYqKi4gVGhpcyBpcyBhIHN0cm9uZyBuZWdhdGl2ZSBhc3NvY2lhdGlvbi4gQSBoaWdoZXIgY3JlZGl0IHNjb3JlIG1ha2VzIGEgZGVmYXVsdCBtdWNoIGxlc3MgbGlrZWx5LgogICogKipUZXJtXF9zY2FsZWQ6KiogVGhlIHBvc2l0aXZlIGNvZWZmaWNpZW50ICgqKjAuNTMqKikgaW1wbGllcyB0aGF0IGxvbmdlciBsb2FuIHRlcm1zIGFyZSBhc3NvY2lhdGVkIHdpdGggaGlnaGVyIGxvZy1vZGRzIG9mIGRlZmF1bHQuCiAgKiAqKkNoZWNraW5nXF9hbW91bnRcX3NjYWxlZDoqKiBBIGxhcmdlciBhbW91bnQgaW4gdGhlIGNoZWNraW5nIGFjY291bnQgaXMgYXNzb2NpYXRlZCB3aXRoIGxvd2VyIG9kZHMgb2YgZGVmYXVsdCAoKiotMS43KiopLgogICogKipBZ2VcX3NjYWxlZDoqKiBZb3VuZ2VyIGJvcnJvd2VycyBhcmUgbW9yZSBsaWtlbHkgdG8gZGVmYXVsdCB0aGFuIG9sZGVyIGJvcnJvd2VycyAoKiotMi42KiopLgoKLS0tLS0KCiMgUGFydCBJSUk6IFByZWRpY3RpdmUgTW9kZWxpbmcgYW5kIENyb3NzLVZhbGlkYXRpb24KCk5vdyB3ZSBzaGlmdCBmcm9tIGFzc29jaWF0aW9uIHRvICoqcHJlZGljdGlvbioqLiBXZSB3aWxsIHVzZSBhIDc1JS8yNSUgc3BsaXQgZm9yIHRyYWluaW5nIGFuZCB0ZXN0aW5nLiBXZSB3aWxsIHRoZW4gcGVyZm9ybSA1LWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiBvbiB0aGUgdHJhaW5pbmcgZGF0YSB0byBzZWxlY3QgdGhlIG1vZGVsIHdpdGggdGhlIGJlc3QgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSwgd2hpY2ggd2lsbCB0aGVuIGJlIGV2YWx1YXRlZCBvbiB0aGUgMjUlIHRlc3Qgc2V0LgoKIyMgRGF0YSBTcGxpdHRpbmcgKDc1JSBUcmFpbiwgMjUlIFRlc3QpCgpXZSBmaXJzdCBzaHVmZmxlIHRoZSBkYXRhIGFuZCBzcGxpdCBpdCBpbnRvIGEgNzUlIHRyYWluaW5nIHNldCAoZm9yIENWKSBhbmQgYSAyNSUgdGVzdGluZyBzZXQgKGZvciBmaW5hbCBldmFsdWF0aW9uKS4KCmBgYHtyIGRhdGEtc3BsaXR0aW5nfQpzZXQuc2VlZCgxMjMpICMgZm9yIHJlcHJvZHVjaWJpbGl0eQoKIyBHZXQgdG90YWwgc2FtcGxlIHNpemUKbiA8LSBucm93KHJlZ3Jlc3Npb25fZGF0YSkKb2JzLklEIDwtIDE6bgoKIyBTaHVmZmxlIElEcwpzaHVmZmxlZC5pZCA8LSBzYW1wbGUob2JzLklELCBuLCByZXBsYWNlID0gRkFMU0UpICMgcmFuZG9taXppbmcgdGhlIG9ic2VydmF0aW9uIElEcwpzaHVmZmxlZF9kYXRhIDwtIHJlZ3Jlc3Npb25fZGF0YVtzaHVmZmxlZC5pZCwgXSAjIHJhbmRvbWl6aW5nIHRoZSBkYXRhIHNldAoKIyBEZWZpbmUgdHJhaW5pbmcgc2V0IHNpemUgKDc1JSkKbi50cmFpbiA8LSByb3VuZCgwLjc1ICogbikKCiMgQ3JlYXRlIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMKdHJhaW4uZGF0YSA8LSBzaHVmZmxlZF9kYXRhWzE6bi50cmFpbiwgXQp0ZXN0LmRhdGEgPC0gc2h1ZmZsZWRfZGF0YVsobi50cmFpbiArIDEpOm4sIF0KCmNhdCgiVHJhaW5pbmcgZGF0YSBkaW1lbnNpb25zOiIsIGRpbSh0cmFpbi5kYXRhKSwgIlxuIikKY2F0KCJUZXN0aW5nIGRhdGEgZGltZW5zaW9uczoiLCBkaW0odGVzdC5kYXRhKSwgIlxuIikKYGBgCgojIyBQcmVkaWN0aW9uIExpbmVhciBSZWdyZXNzaW9uIChgQ3JlZGl0X3Njb3JlYCkKCldlIHdpbGwgdXNlIDUtZm9sZCBjcm9zcy12YWxpZGF0aW9uIHRvIGNvbXBhcmUgdHdvIGNhbmRpZGF0ZSBtb2RlbHMgYmFzZWQgb24gdGhlaXIgKipNZWFuIFNxdWFyZSBFcnJvciAoTVNFKSoqLiBXZSBtdXN0IGNhbGN1bGF0ZSBNU0Ugb24gdGhlICpvcmlnaW5hbCogYENyZWRpdF9zY29yZWAgc2NhbGUsIHNvIHdlIHdpbGwgdXNlIGBleHAoKWAgb24gb3VyIHByZWRpY3Rpb25zIGZyb20gdGhlIGBsb2coQ3JlZGl0X3Njb3JlKWAgbW9kZWxzLgoKICAqICoqTTEgKEZ1bGwgTW9kZWwpOioqIFRoZSBmdWxsIG1vZGVsIGZyb20gUGFydCBJSSAoYHRyYW5zZm9ybS5tb2RlbGApLgogICogKipNMiAoU3RlcC1TZWxlY3RlZCBNb2RlbCk6KiogVGhlIGZpbmFsLCBzaW1wbGVyIG1vZGVsIGZyb20gUGFydCBJSSAoYGZpbmFsLm1vZGVsYCkuCgpgYGB7ciBsaW5lYXItY3YtbG9vcH0KIyBTZXQgbnVtYmVyIG9mIGZvbGRzCmsgPC0gNQojIERlZmluZSBzaXplIG9mIDUtZm9sZCBzcGxpdHRpbmcsIC0xIHRvIGd1YXJhbnRlZSA1Km4uZm9sZCA8PSBuLnRyYWluCm4uZm9sZCA8LSByb3VuZChuLnRyYWluIC8gaykgLSAxIAoKIyBJbml0aWFsaXplIHZlY3RvcnMgdG8gc3RvcmUgTVNFIGZvciBlYWNoIGZvbGQKTVNFLk0xIDwtIHJlcCgwLCBrKQpNU0UuTTIgPC0gcmVwKDAsIGspCgojIERlZmluZSB0aGUgbW9kZWwgZm9ybXVsYXMgZnJvbSBQYXJ0IElJCmZvcm11bGEuTTEubGluZWFyIDwtIGxvZyhDcmVkaXRfc2NvcmUpIH4gLiAtIEFtb3VudCAtIERlZmF1bHQgLSBDcmVkaXRfc2NvcmVfc2NhbGVkCmZvcm11bGEuTTIubGluZWFyIDwtIHN1bW1hcnkoZmluYWwubW9kZWwpJGNhbGwkZm9ybXVsYSAjIFVzZSBmb3JtdWxhIGZyb20gc3RlcCgpIGluIFBhcnQgSUkKCmZvciAoaSBpbiAxOmspIHsKICAjIEdldCB2YWxpZGF0aW9uIGZvbGQgSURzCiAgdmFsaWQuaWQgPC0gKChpIC0gMSkgKiBuLmZvbGQgKyAxKTooaSAqIG4uZm9sZCkKICAKICAjIERlZmluZSBjcm9zcy10cmFpbmluZyBhbmQgY3Jvc3MtdmFsaWRhdGlvbiBzZXRzCiAgY3Jvc3MudHJhaW4gPC0gdHJhaW4uZGF0YVstdmFsaWQuaWQsIF0KICBjcm9zcy52YWxpZCA8LSB0cmFpbi5kYXRhW3ZhbGlkLmlkLF0KICAKICAjIEZpdCBjYW5kaWRhdGUgbW9kZWxzCiAgbG0xIDwtIGxtKGZvcm11bGEuTTEubGluZWFyLCBkYXRhID0gY3Jvc3MudHJhaW4pCiAgbG0yIDwtIGxtKGZvcm11bGEuTTIubGluZWFyLCBkYXRhID0gY3Jvc3MudHJhaW4pCiAgCiAgIyBQcmVkaWN0IG9uIHRoZSB2YWxpZGF0aW9uIGZvbGQKICBwcmVkTTFfbG9nIDwtIHByZWRpY3QobG0xLCBuZXdkYXRhID0gY3Jvc3MudmFsaWQpCiAgcHJlZE0yX2xvZyA8LSBwcmVkaWN0KGxtMiwgbmV3ZGF0YSA9IGNyb3NzLnZhbGlkKQogIAogICMgQ29udmVydCBwcmVkaWN0aW9ucyBiYWNrIHRvIG9yaWdpbmFsIHNjYWxlIChleHApIGFuZCBnZXQgdHJ1ZSB2YWx1ZXMKICBwcmVkTTFfb3JpZyA8LSBleHAocHJlZE0xX2xvZykKICBwcmVkTTJfb3JpZyA8LSBleHAocHJlZE0yX2xvZykKICB0cnVlX3ZhbHVlcyA8LSBjcm9zcy52YWxpZCRDcmVkaXRfc2NvcmUKICAKICAjIENhbGN1bGF0ZSBNU0Ugb24gdGhlIG9yaWdpbmFsIHNjYWxlCiAgTVNFLk0xW2ldIDwtIG1lYW4oKHByZWRNMV9vcmlnIC0gdHJ1ZV92YWx1ZXMpXjIpCiAgTVNFLk0yW2ldIDwtIG1lYW4oKHByZWRNMl9vcmlnIC0gdHJ1ZV92YWx1ZXMpXjIpCiAgCiAgY2F0KHBhc3RlKCJGb2xkIiwgaSwgIi0gTTEgTVNFOiIsIHJvdW5kKE1TRS5NMVtpXSksICJ8IE0yIE1TRToiLCByb3VuZChNU0UuTTJbaV0pLCAiXG4iKSkKfQoKIyBDYWxjdWxhdGUgYXZlcmFnZSBNU0UgYWNyb3NzIGFsbCBmb2xkcwphdmcuTVNFLk0xIDwtIG1lYW4oTVNFLk0xKQphdmcuTVNFLk0yIDwtIG1lYW4oTVNFLk0yKQoKY2F0KCJNMSAoRnVsbCBNb2RlbCkgQXZlcmFnZSBNU0U6Iiwgcm91bmQoYXZnLk1TRS5NMSksICJcbiIpCmNhdCgiTTIgKFN0ZXAgTW9kZWwpIEF2ZXJhZ2UgTVNFOiIsIHJvdW5kKGF2Zy5NU0UuTTIpLCAiXG4iKQpgYGAKCmBgYHtyIHBsb3QtbGluZWFyLXJlc3VsdHMsIGZpZy5jYXA9IkxpbmVhciBNb2RlbCA1LUZvbGQgQ1YgUGVyZm9ybWFuY2UgKE1TRSkifQojIFBsb3QgdGhlIE1TRSByZXN1bHRzCnBsb3QoMTprLCBNU0UuTTEsIHR5cGUgPSAibCIsIGNvbCA9ICJuYXZ5IiwgbHdkID0gMiwKICAgICB4bGFiID0gIkl0ZXJhdGlvbnMgMSB0byA1IiwgeWxhYiA9ICJNU0UiLCAKICAgICB5bGltID0gYyhtaW4oYyhNU0UuTTEsIE1TRS5NMikpIC0gNTAwMCwgbWF4KGMoTVNFLk0xLCBNU0UuTTIpKSArIDUwMDApLAogICAgIHhsaW0gPSBjKDAsIDYpLAogICAgIG1haW4gPSAiUGVyZm9ybWFuY2UgRXZhbHVhdGlvbiB2aWEgNS1mb2xkIENyb3NzIFZhbGlkYXRpb24iLAogICAgIGNleC5tYWluID0gMC44KQpwb2ludHMoMTprLCBNU0UuTTEsIHBjaCA9IDE5LCBjb2wgPSAibmF2eSIpCmxpbmVzKDE6aywgTVNFLk0yLCB0eXBlID0gImwiLCBjb2wgPSAiZGFya3JlZCIsIGx3ZCA9IDIpCnBvaW50cygxOmssIE1TRS5NMiwgcGNoID0gMTksIGNvbCA9ICJkYXJrcmVkIikKCmxlZ2VuZCgidG9wcmlnaHQiLCBjKHBhc3RlKCJNMSBBdmcuIE1TRToiLCByb3VuZChhdmcuTVNFLk0xKSksIAogICAgICAgICAgICAgICAgICAgICBwYXN0ZSgiTTIgQXZnLiBNU0U6Iiwgcm91bmQoYXZnLk1TRS5NMikpKSwKICAgICAgIGNvbCA9IGMoIm5hdnkiLCAiZGFya3JlZCIpLCBsd2QgPSAyLCBwY2ggPSAxOSwgYnR5ID0gIm4iLCBjZXggPSAwLjgpCmBgYAoKKipDViBSZXN1bHQ6KiogVGhlIG1vZGVsIHdpdGggdGhlIGxvd2VyIGF2ZXJhZ2UgTVNFIGlzIHNlbGVjdGVkIGFzIHRoZSBiZXN0IHByZWRpY3RpdmUgbW9kZWwuIEJhc2VkIG9uIHRoZSBvdXRwdXQgd2UgY2hvb3NlICoqTTIgKFN0ZXAtU2VsZWN0ZWQgTW9kZWwpKiouCgojIyBQcmVkaWN0aW9uIExvZ2lzdGljIFJlZ3Jlc3Npb24gKGBEZWZhdWx0YCkKCldlIHJlcGVhdCB0aGUgNS1mb2xkIENWIHByb2Nlc3MgdG8gY29tcGFyZSB0d28gbG9naXN0aWMgbW9kZWxzLiBUaGUgcHJpbWFyeSBtZXRyaWMgd2lsbCBiZSB0aGUgKipBcmVhIFVuZGVyIHRoZSBDdXJ2ZSAoQVVDKSoqIGZyb20gdGhlIFJPQyBhbmFseXNpcy4KCiAgKiAqKk0xIChGdWxsIE1vZGVsKToqKiBUaGUgZnVsbCBsb2dpc3RpYyBtb2RlbCBmcm9tIFBhcnQgSUkgKGBmdWxsX2xvZ2lzdGljX21vZGVsYCkuCiAgKiAqKk0yIChTdGVwLVNlbGVjdGVkIE1vZGVsKToqKiBUaGUgZmluYWwsIHNpbXBsZXIgbG9naXN0aWMgbW9kZWwgZnJvbSBQYXJ0IElJIChgZmluYWxfbG9naXN0aWNfbW9kZWxgKS4KCgpgYGB7ciBsb2dpc3RpYy1jdi1sb29wfQojIEluaXRpYWxpemUgdmVjdG9ycyB0byBzdG9yZSBBVUMgZm9yIGVhY2ggZm9sZApBVUMuTTEgPC0gcmVwKDAsIGspCkFVQy5NMiA8LSByZXAoMCwgaykKCiMgVXNlIHRoZSBzYW1lIGZvbGRzIGFzIHRoZSBsaW5lYXIgQ1YKIyBrLCBuLmZvbGQgYXJlIGFscmVhZHkgZGVmaW5lZAoKIyBEZWZpbmUgdGhlIG1vZGVsIGZvcm11bGFzIGZyb20gUGFydCBJSQpmb3JtdWxhLk0xLmxvZ2lzdGljIDwtIERlZmF1bHQgfiAuIC0gQW1vdW50IC0gQ3JlZGl0X3Njb3JlCmZvcm11bGEuTTIubG9naXN0aWMgPC0gc3VtbWFyeShmaW5hbF9sb2dpc3RpY19tb2RlbCkkY2FsbCRmb3JtdWxhICMgVXNlIGZvcm11bGEgZnJvbSBzdGVwKCkKCmZvciAoaSBpbiAxOmspIHsKICAjIEdldCB2YWxpZGF0aW9uIGZvbGQgSURzCiAgdmFsaWQuaWQgPC0gKChpIC0gMSkgKiBuLmZvbGQgKyAxKTooaSAqIG4uZm9sZCkKICAKICAjIERlZmluZSBjcm9zcy10cmFpbmluZyBhbmQgY3Jvc3MtdmFsaWRhdGlvbiBzZXRzCiAgY3Jvc3MudHJhaW4gPC0gdHJhaW4uZGF0YVstdmFsaWQuaWQsIF0KICBjcm9zcy52YWxpZCA8LSB0cmFpbi5kYXRhW3ZhbGlkLmlkLF0KICAKICAjIEZpdCBjYW5kaWRhdGUgbW9kZWxzCiAgZ2xtMSA8LSBnbG0oZm9ybXVsYS5NMS5sb2dpc3RpYywgZGF0YSA9IGNyb3NzLnRyYWluLCBmYW1pbHkgPSAiYmlub21pYWwiKQogIGdsbTIgPC0gZ2xtKGZvcm11bGEuTTIubG9naXN0aWMsIGRhdGEgPSBjcm9zcy50cmFpbiwgZmFtaWx5ID0gImJpbm9taWFsIikKICAKICAjIFByZWRpY3QgcHJvYmFiaWxpdGllcyBvbiB0aGUgdmFsaWRhdGlvbiBmb2xkCiAgcHJlZE0xX3Byb2IgPC0gcHJlZGljdChnbG0xLCBuZXdkYXRhID0gY3Jvc3MudmFsaWQsIHR5cGUgPSAicmVzcG9uc2UiKQogIHByZWRNMl9wcm9iIDwtIHByZWRpY3QoZ2xtMiwgbmV3ZGF0YSA9IGNyb3NzLnZhbGlkLCB0eXBlID0gInJlc3BvbnNlIikKICAKICAjIENhbGN1bGF0ZSBBVUMKICAjIFVzaW5nIHF1aWV0ID0gVFJVRSB0byBwcmV2ZW50IHByaW50aW5nIG1lc3NhZ2VzIGR1cmluZyB0aGUgbG9vcAogIEFVQy5NMVtpXSA8LSBhdWMocm9jKGNyb3NzLnZhbGlkJERlZmF1bHQsIHByZWRNMV9wcm9iLCBxdWlldCA9IFRSVUUpKQogIEFVQy5NMltpXSA8LSBhdWMocm9jKGNyb3NzLnZhbGlkJERlZmF1bHQsIHByZWRNMl9wcm9iLCBxdWlldCA9IFRSVUUpKQogIAogIGNhdChwYXN0ZSgiRm9sZCIsIGksICItIE0xIEFVQzoiLCByb3VuZChBVUMuTTFbaV0sIDQpLCAifCBNMiBBVUM6Iiwgcm91bmQoQVVDLk0yW2ldLCA0KSwgIlxuIikpCn0KCiMgQ2FsY3VsYXRlIGF2ZXJhZ2UgQVVDIGFjcm9zcyBhbGwgZm9sZHMKYXZnLkFVQy5NMSA8LSBtZWFuKEFVQy5NMSkKYXZnLkFVQy5NMiA8LSBtZWFuKEFVQy5NMikKCmNhdCgiTTEgKEZ1bGwgTW9kZWwpIEF2ZXJhZ2UgQVVDOiIsIHJvdW5kKGF2Zy5BVUMuTTEsIDQpLCAiXG4iKQpjYXQoIk0yIChTdGVwIE1vZGVsKSBBdmVyYWdlIEFVQzoiLCByb3VuZChhdmcuQVVDLk0yLCA0KSwgIlxuIikKYGBgCgpgYGB7ciBwbG90LWxvZ2lzdGljLWN2LXJlc3VsdHMsIGZpZy5jYXA9IkxvZ2lzdGljIE1vZGVsIDUtRm9sZCBDViBQZXJmb3JtYW5jZSAoQVVDKSJ9CiMgUGxvdCB0aGUgQVVDIHJlc3VsdHMKcGxvdCgxOmssIEFVQy5NMSwgdHlwZSA9ICJsIiwgY29sID0gIm5hdnkiLCBsd2QgPSAyLAogICAgIHhsYWIgPSAiSXRlcmF0aW9ucyAxIHRvIDUiLCB5bGFiID0gIkFVQyIsIAogICAgIHlsaW0gPSBjKG1pbihjKEFVQy5NMSwgQVVDLk0yKSkgLSAwLjA1LCBtYXgoYyhBVUMuTTEsIEFVQy5NMikpICsgMC4wNSksCiAgICAgeGxpbSA9IGMoMCwgNiksCiAgICAgbWFpbiA9ICJQZXJmb3JtYW5jZSBFdmFsdWF0aW9uIHZpYSA1LWZvbGQgQ3Jvc3MgVmFsaWRhdGlvbiIsCiAgICAgY2V4Lm1haW4gPSAwLjgpCnBvaW50cygxOmssIEFVQy5NMSwgcGNoID0gMTksIGNvbCA9ICJuYXZ5IikKbGluZXMoMTprLCBBVUMuTTIsIHR5cGUgPSAibCIsIGNvbCA9ICJkYXJrcmVkIiwgbHdkID0gMikKcG9pbnRzKDE6aywgQVVDLk0yLCBwY2ggPSAxOSwgY29sID0gImRhcmtyZWQiKQoKbGVnZW5kKCJib3R0b21yaWdodCIsIGMocGFzdGUoIk0xIEF2Zy4gQVVDOiIsIHJvdW5kKGF2Zy5BVUMuTTEsIDQpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCJNMiBBdmcuIEFVQzoiLCByb3VuZChhdmcuQVVDLk0yLCA0KSkpLAogICAgICAgY29sID0gYygibmF2eSIsICJkYXJrcmVkIiksIGx3ZCA9IDIsIHBjaCA9IDE5LCBidHkgPSAibiIsIGNleCA9IDAuOCkKYGBgCgoqKkNWIFJlc3VsdDoqKiBUaGUgbW9kZWwgd2l0aCB0aGUgaGlnaGVyIGF2ZXJhZ2UgQVVDIGlzIHNlbGVjdGVkLiBCYXNlZCBvbiB0aGUgb3V0cHV0LCB3ZSBjaG9vc2UgdGhlICoqTTIgKFN0ZXAtU2VsZWN0ZWQgTW9kZWwpKiogYXMgaXQgcGVyZm9ybXMgYSBiaXQgYmV0dGVyIHRoYW4gdGhlIGZ1bGwgbW9kZWwgd2hpbGUgYmVpbmcgc2ltcGxlci4KCiMjIEZpbmFsIE1vZGVsIFRlc3RpbmcKCldlIHdpbGwgbm93IHRyYWluIG91ciBjaG9zZW4gbW9kZWxzIChNMiBmb3IgYm90aCkgb24gdGhlICoqZW50aXJlIDc1JSB0cmFpbmluZyBzZXQqKiBhbmQgcmVwb3J0IHRoZWlyIGZpbmFsIHBlcmZvcm1hbmNlIG9uIHRoZSAqKjI1JSB0ZXN0aW5nIHNldCoqLgoKIyMjIEZpbmFsIExpbmVhciBNb2RlbCAoTTIpIFBlcmZvcm1hbmNlCgpgYGB7ciBmaW5hbC1saW5lYXItdGVzdH0KIyAxLiBSZS10cmFpbiB0aGUgd2lubmluZyBtb2RlbCAoTTIpIG9uIHRoZSBlbnRpcmUgdHJhaW5pbmcgc2V0CmZpbmFsLmxtLnRlc3QgPC0gbG0oZm9ybXVsYS5NMi5saW5lYXIsIGRhdGEgPSB0cmFpbi5kYXRhKQoKIyAyLiBQcmVkaWN0IG9uIHRoZSAyNSUgdGVzdCBzZXQKcHJlZC5sbS50ZXN0X2xvZyA8LSBwcmVkaWN0KGZpbmFsLmxtLnRlc3QsIG5ld2RhdGEgPSB0ZXN0LmRhdGEpCgojIDMuIENvbnZlcnQgcHJlZGljdGlvbnMgdG8gb3JpZ2luYWwgc2NhbGUKcHJlZC5sbS50ZXN0X29yaWcgPC0gZXhwKHByZWQubG0udGVzdF9sb2cpCnRydWVfdmFsdWVzX3Rlc3QgPC0gdGVzdC5kYXRhJENyZWRpdF9zY29yZQoKIyA0LiBDYWxjdWxhdGUgYW5kIHJlcG9ydCB0aGUgZmluYWwgTVNFIG9uIHRoZSB0ZXN0IGRhdGEKZmluYWwuTVNFIDwtIG1lYW4oKHByZWQubG0udGVzdF9vcmlnIC0gdHJ1ZV92YWx1ZXNfdGVzdCleMikKCmNhdCgiRmluYWwgTVNFIG9uIDI1JSBUZXN0IFNldDoiLCByb3VuZChmaW5hbC5NU0UpLCAiXG4iKQpgYGAKCiMjIyBGaW5hbCBMb2dpc3RpYyBNb2RlbCAoTTIpIFBlcmZvcm1hbmNlCgpgYGB7ciBmaW5hbC1sb2dpc3RpYy10ZXN0LCBmaWcuY2FwPSJGaW5hbCBST0MgQ3VydmUgb24gMjUlIFRlc3QgRGF0YSJ9CiMgMS4gUmUtdHJhaW4gdGhlIHdpbm5pbmcgbW9kZWwgKE0yKSBvbiB0aGUgZW50aXJlIHRyYWluaW5nIHNldApmaW5hbC5nbG0udGVzdCA8LSBnbG0oZm9ybXVsYS5NMi5sb2dpc3RpYywgZGF0YSA9IHRyYWluLmRhdGEsIGZhbWlseSA9ICJiaW5vbWlhbCIpCgojIDIuIFByZWRpY3QgcHJvYmFiaWxpdGllcyBvbiB0aGUgMjUlIHRlc3Qgc2V0CnByZWQuZ2xtLnRlc3RfcHJvYiA8LSBwcmVkaWN0KGZpbmFsLmdsbS50ZXN0LCBuZXdkYXRhID0gdGVzdC5kYXRhLCB0eXBlID0gInJlc3BvbnNlIikKCiMgMy4gQ2FsY3VsYXRlIGFuZCByZXBvcnQgdGhlIGZpbmFsIEFVQyBhbmQgcGxvdCB0aGUgUk9DIGN1cnZlCmZpbmFsLnJvYy5vYmogPC0gcm9jKHRlc3QuZGF0YSREZWZhdWx0LCBwcmVkLmdsbS50ZXN0X3Byb2IpCmZpbmFsLkFVQyA8LSBhdWMoZmluYWwucm9jLm9iaikKCmNhdCgiRmluYWwgQVVDIG9uIDI1JSBUZXN0IFNldDoiLCByb3VuZChmaW5hbC5BVUMsIDQpLCAiXG4iKQoKIyBQbG90IHRoZSBmaW5hbCBST0MgY3VydmUKcGxvdChmaW5hbC5yb2Mub2JqLCBtYWluID0gIkZpbmFsIE1vZGVsIFJPQyBDdXJ2ZSAoVGVzdCBEYXRhKSIsIAogICAgIGNvbCA9ICJkYXJrYmx1ZSIsIGx3ZCA9IDIsIHByaW50LmF1YyA9IFRSVUUpCmBgYAoKLS0tLS0KCiMgQ29uY2x1c2lvbgoKVGhpcyBwcm9qZWN0IGhhcyBhbmFseXplZCB0aGUgYmFuayBsb2FuIGRhdGFzZXQgZnJvbSB0aHJlZSBwZXJzcGVjdGl2ZXM6CgogICogKipQYXJ0IEkgKEVEQSkqKiByZXZlYWxlZCBrZXkgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGBDcmVkaXRfc2NvcmVgIGFuZCBgRGVmYXVsdGAuCiAgKiAqKlBhcnQgSUkgKFN0YXRpc3RpY2FsIE1vZGVsaW5nKSoqIGlkZW50aWZpZWQgc2lnbmlmaWNhbnQgKmFzc29jaWF0aXZlKiBmYWN0b3JzIGZvciBib3RoIGBsb2coQ3JlZGl0X3Njb3JlKWAgYW5kIGBEZWZhdWx0YCB1c2luZyBjbGFzc2ljYWwgcmVncmVzc2lvbi4gVGhlIHNlbGVjdGlvbiBvZiB0aGVzZSBtb2RlbHMgd2FzIGp1c3RpZmllZCBieSBhIGNvbWJpbmF0aW9uIG9mIGNoZWNrcyAobGlrZSBCb3gtQ294IHRyYW5zZm9ybWF0aW9ucykgYW5kIHN0YXRpc3RpY2FsIGNyaXRlcmlhIChSLXNxdWFyZWQgYW5kIEFJQy1iYXNlZCBiYWNrd2FyZCBzZWxlY3Rpb24pLgogICogKipQYXJ0IElJSSAoUHJlZGljdGl2ZSBNb2RlbGluZykqKiB1c2VkIDUtZm9sZCBjcm9zcy12YWxpZGF0aW9uIHRvIHNlbGVjdCB0aGUgYmVzdCBwcmVkaWN0aXZlIG1vZGVscywgc2hpZnRpbmcgdGhlIGZvY3VzIHRvIHBlcmZvcm1hbmNlIG1ldHJpY3MgbGlrZSBNU0UgYW5kIEFVQy4gRm9yIGJvdGggdGhlIGxpbmVhciAoYENyZWRpdF9zY29yZWApIGFuZCBsb2dpc3RpYyAoYERlZmF1bHRgKSBwcm9ibGVtcywgdGhlIHNpbXBsZXIsIHN0ZXAtc2VsZWN0ZWQgbW9kZWxzIChNMikgZnJvbSBQYXJ0IElJIHdlcmUgY29uZmlybWVkIHRvIGJlIHRoZSBiZXR0ZXIgY2hvaWNlIGZvciBwcmVkaWN0aW9uLgoKVGhlIGZpbmFsIG1vZGVscyB3ZXJlIHRlc3RlZCB5aWVsZGluZyBhICoqRmluYWwgTVNFIG9mIGByIHJvdW5kKGZpbmFsLk1TRSlgKiogZm9yIHByZWRpY3RpbmcgYENyZWRpdF9zY29yZWAgYW5kIGEgKipGaW5hbCBBVUMgb2YgYHIgcm91bmQoZmluYWwuQVVDLCA0KWAqKiBmb3IgcHJlZGljdGluZyBgRGVmYXVsdGAuIFRoaXMgZGVtb25zdHJhdGVzIHRoYXQgb3VyIGZpbmFsIHN0ZXAtc2VsZWN0ZWQgbW9kZWxzIGFyZSBub3Qgb25seSB2YWxpZCAoYXMgc2hvd24gaW4gUGFydCBJSSkgYnV0IGFsc28gYXJlIHJlbGlhYmxlIGZvciBwcmVkaWN0aW9uIChhcyBjb25maXJtZWQgaW4gUGFydCBJSUkpLg==