Overview

Class imbalance in supervised machine learning refers to the scenario where the distribution of classes in a dataset is significantly skewed, meaning one class (or a few classes) has far more examples than others. This imbalance poses a challenge because most machine learning algorithms are designed to optimize overall accuracy, often leading to poor performance on the minority class. This can lead to significant bias in prediction.

For example, consider a binary classification problem for detecting fraudulent transactions. If 98% of transactions are legitimate and only 2% are fraudulent, a naive model might predict all transactions as legitimate to achieve 98% accuracy. However, this approach fails to identify fraudulent cases, which are critical in this context.

The issue arises because algorithms like decision trees, neural networks, and support vector machines may focus on the majority class, neglecting the minority class. Metrics such as accuracy become misleading, as high accuracy can be achieved by ignoring minority cases altogether. Alternative evaluation metrics such as True Positives and True Negatives or the F1-Score can highlight potential class imbalance.

Several strategies have been developed to ameliorate class imbalance:

  1. Data-Level Approaches: These include resampling methods such as oversampling the minority class (e.g., SMOTE) or undersampling the majority class to balance the dataset.

  2. Algorithm-Level Modifications: Some algorithms can be adjusted to handle imbalances by incorporating class weights or cost-sensitive learning.

  3. Evaluation Metrics: Using metrics like precision, recall, F1-score, and area under the ROC curve (AUC-ROC) provides a clearer picture of a model’s performance on imbalanced datasets.

Understanding and addressing class imbalance is critical for building robust and meaningful machine learning models, particularly in applications like fraud detection, medical diagnosis, and anomaly detection, where the minority class often represents the cases of greatest interest but generally having the least representation in the data.

Data-Level Approaches

Data-level methods to manage class imbalance generally focus on resampling. Resampling methods are techniques used to address class imbalance in datasets by modifying the distribution of the minority and majority classes. These methods operate at the data level, aiming to create a balanced dataset that allows machine learning models to learn effectively from both classes. Common resampling methods include oversampling, undersampling, and hybrid approaches.

Oversampling

Oversampling is done by increasing the size of the minority class by replicating existing samples or generating synthetic samples. There are several common methods for oversampling minority classes:

  1. Random Oversampling: Randomly duplicates instances of the minority class until the class distribution is balanced.

  2. Synthetic Minority Over-sampling Technique (SMOTE): Generates new synthetic instances for the minority class by interpolating between existing samples and their nearest neighbors.

  3. Random Over-Sampling Examples (ROSE): A resampling method designed to address class imbalance by generating synthetic samples for the minority class. Unlike basic oversampling, which duplicates existing samples, or SMOTE, which interpolates between existing data points, ROSE uses a kernel density estimation approach to generate synthetic data points. This method creates a smoother, more realistic representation of the minority class distribution.

Oversampling is generally used when the dataset is small, as it avoids losing information from the majority class. SMOTE is a particularly useful technique when the minority class has sufficient variability to synthesize meaningful new samples.

Undersampling

Undersampling reduces the size of the majority class by randomly removing samples to balance the class distribution. Similar to oversampling, there are various strategies:

  1. Random Undersampling: Randomly selects a subset of majority class samples equal in size to the minority class.
  2. Tomek Links: Removes majority class instances that are closest to minority class instances to clean class boundaries.

Undersampling is often used when the dataset is large, and the majority class contains redundant or noisy samples. It works best when there is no risk of losing valuable information about the majority class.

The code example below illustrates both undersampling strategies1. Undersampling in R can be implemented without relying on a package by manually selecting a random subset of the majority class to match the size of the minority class. This approach ensures a balanced dataset by reducing the majority class to the size of the minority class.

  1. Check Class Distribution: Determine the number of samples in the minority and majority classes.
  2. Subset the Majority Class: Randomly sample rows from the majority class to match the size of the minority class.
  3. Combine the Subsets: Merge the reduced majority class with the minority class to create a balanced dataset.

Let’s first create an imbalanced dataset for testing:

set.seed(123)
# Simulate a binary classification dataset
data <- data.frame(
  x1 = rnorm(100),
  x2 = rnorm(100),
  class = c(rep(0, 90), rep(1, 10))  # Imbalanced with 90 majority and 10 minority
)

# Check class distribution
table(data$class)
## 
##  0  1 
## 90 10

Now we can separate the classes:

# Separate majority and minority classes
majority <- subset(data, class == 0)
minority <- subset(data, class == 1)

Next, we extract a random sample from the majority class:

# Randomly sample the majority class to match the size of the minority class
undersampled_majority <- majority[sample(nrow(majority), nrow(minority)), ]

Finally we can combine the subsets:

# Combine the undersampled majority class with the minority class
balanced_data <- rbind(undersampled_majority, minority)

# Check the new class distribution
table(balanced_data$class)
## 
##  0  1 
## 10 10

In the above code,

  1. The sample() function is used to randomly select rows from the majority class.
  2. The size of the sample (nrow(minority)) ensures that the majority class is reduced to the size of the minority class.
  3. The rbind() function combines the reduced majority class with the minority class into a balanced dataset.

To visualize the effect of undersampling, you can plot the original and balanced datasets:

library(ggplot2)

# Plot original data
ggplot(data, aes(x = x1, y = x2, color = factor(class))) +
  geom_point() +
  ggtitle("Original Imbalanced Dataset")

# Plot balanced data
ggplot(balanced_data, aes(x = x1, y = x2, color = factor(class))) +
  geom_point() +
  ggtitle("Dataset After Undersampling")

Remember that undersampling implies potentially a:

  1. Loss of Information: Undersampling reduces the size of the dataset, potentially discarding useful information from the majority class.
  2. Risk of Overfitting: With a small dataset, the model might overfit to the reduced data.

Hybrid Methods

Hybrid methods combine oversampling and undersampling to achieve a balanced dataset with minimal redundancy and improved synthetic sample quality. They generally start by oversampling the minority class to a predefined level and then undersampling the majority class to reduce redundancy. Methods like SMOTE-Tomek Links or SMOTE-ENN combine synthetic oversampling with cleaning techniques.

Hybrid methods work best when the dataset contains noisy or overlapping class boundaries. It is most effective for highly imbalanced datasets where a simple oversampling or undersampling approach might fail.


Summary of Use Cases

  1. Random Oversampling: Use for small datasets or when synthetic generation might introduce noise.
  2. SMOTE: Use when synthetic sample quality is essential and the minority class is well-defined.
  3. Random Undersampling: Use for large datasets with redundant majority samples.
  4. Hybrid Methods: Use for noisy or complex class boundaries.

By applying these techniques judiciously, practitioners can mitigate the adverse effects of class imbalance and improve model performance on both the majority and minority classes.

Synthetic Oversampling Methods

Let’s take a closer look with additional details on the two most common oversampling methods: ROSE and SMOTE.

ROSE

ROSE (Random Over-Sampling Examples) is a resampling method designed to address class imbalance by generating synthetic samples for the minority class. Unlike basic oversampling, which duplicates existing samples, or SMOTE, which interpolates between existing data points, ROSE uses a kernel density estimation approach to generate synthetic data points. This method creates a smoother, more realistic representation of the minority class distribution.

ROSE generates synthetic examples for both the minority and majority classes by sampling from a smoothed approximation of the original data distribution. This is achieved through the following steps:

  1. Kernel Density Estimation (KDE): A kernel density function is applied to the data to estimate the probability density function of the feature space. This density is used to randomly sample new data points, ensuring that the synthetic points reflect the underlying data distribution.

  2. Balanced Sampling: Synthetic samples are added to the minority class to balance the dataset. Optionally, some points from the majority class can also be synthetically generated or removed to ensure better class boundaries.

  3. Noise Handling: ROSE incorporates a level of randomness, reducing the risk of creating exact duplicates or overly simplistic synthetic points, which helps improve generalization.

Advantages of ROSE

ROSE has some key advantages over other methods:

  1. Preserves Data Characteristics: Synthetic samples closely resemble the real distribution, avoiding artifacts introduced by simpler methods like random duplication.
  2. Reduces Overfitting: By generating new points rather than duplicating existing ones, ROSE mitigates the risk of overfitting to the minority class.
  3. Improves Class Boundaries: The method often generates points near decision boundaries, improving the model’s ability to distinguish between classes.

ROSE is particularly effective in situations where the minority class is highly underrepresented and the dataset contains complex decision boundaries or overlaps between classes, but it is computationally complex. However, often simpler methods like random oversampling or SMOTE are insufficient to capture the minority class’s diversity.

Example of Applying ROSE in R

The ROSE package in R provides an easy-to-use implementation of this method. Here’s how it can be applied:

Consider a binary classification problem with an imbalanced dataset. The code below applies random oversampling using functions from the ROSE package.

library(ROSE)
## Loaded ROSE 0.0-4
# Simulate imbalanced data
set.seed(123)
data <- data.frame(
  x1 = rnorm(100),
  x2 = rnorm(100),
  class = c(rep(0, 90), rep(1, 10))
)

# Check class distribution
table(data$class)
## 
##  0  1 
## 90 10
# Apply ROSE to generate balanced data
rose_data <- ROSE(class ~ ., data = data, seed = 1)$data

# Check the new class distribution
table(rose_data$class)
## 
##  0  1 
## 52 48

You can visualize how ROSE generates synthetic points using scatterplots:

library(ggplot2)

# Original data
ggplot(data, aes(x = x1, y = x2, color = factor(class))) +
  geom_point() +
  ggtitle("Original Data")

# ROSE data
ggplot(rose_data, aes(x = x1, y = x2, color = factor(class))) +
  geom_point() +
  ggtitle("Data After ROSE")

The ROSE package also provides functionality to evaluate the effectiveness of resampling using models. For example:

# Train a logistic regression model on the resampled data
model <- glm(class ~ ., family = binomial, data = rose_data)

# Predict and evaluate
pred <- predict(model, newdata = data, type = "response")
roc.curve(data$class, pred)

## Area under the curve (AUC): 0.641

While ROSE is highly effective, it has some limitations:

  1. Potential Noise Introduction: Excessive randomness in synthetic point generation can introduce noise if the data distribution is not well-captured by the kernel.
  2. Scalability: Computationally intensive for very large datasets.

Nevertheless, ROSE is a versatile strategy for handling class imbalance by generating synthetic examples that reflect the original data distribution. It is particularly suitable for datasets with complex relationships between features and classes, providing a robust alternative to traditional resampling methods.

SMOTE

SMOTE is a popular resampling technique used to address class imbalance in datasets by generating synthetic samples for the minority class. Unlike random oversampling, which duplicates existing minority class samples, SMOTE creates new data points by interpolating between existing minority class samples. This method helps reduce overfitting and enhances the minority class representation in the dataset.

SMOTE applies the following steps:

  1. Identify Nearest Neighbors: For each sample in the minority class, SMOTE identifies its nearest neighbors within the same class based on a distance metric (commonly Euclidean distance). This is essentially an application of the kNN algorithm. Note that this requires categorical features to be numerically encoded and numeric features to be normalized to a common scale.

  2. Generate Synthetic Samples: New samples are created by taking a weighted average of a minority class sample and one of its nearest neighbors. This interpolation ensures that synthetic samples lie along the line segments connecting existing samples.

  3. Repeat Until Balanced: This process is repeated until the desired class balance is achieved.

Advantages of SMOTE

SMOTE has some key advantages over other methods:

  1. Avoids Overfitting: By generating new synthetic samples rather than duplicating existing ones, SMOTE mitigates the risk of overfitting to the minority class.

  2. Enhances Generalization: SMOTE encourages models to learn a broader representation of the minority class, especially near class boundaries.

  3. Improves Decision Boundaries: By creating samples near the edges of the minority class, SMOTE helps the model distinguish better between the majority and minority classes.

SMOTE is particularly effective in cases where:

  1. The dataset has a significant class imbalance.
  2. The minority class is underrepresented, making it difficult for the model to learn its patterns.
  3. The minority class distribution is well-defined and not noisy.

Implementation of SMOTE in R

The DMwR and smotefamily packages in R provide implementations of SMOTE. Here’s a step-by-step explanation with examples using a simulated imbalanced binary classification dataset:

set.seed(123)
data <- data.frame(
  x1 = c(rnorm(90, mean = 0), rnorm(10, mean = 3)),
  x2 = c(rnorm(90, mean = 0), rnorm(10, mean = 3)),
  class = c(rep(0, 90), rep(1, 10))
)

# Check class distribution
table(data$class)
## 
##  0  1 
## 90 10

The dataset is imbalanced, with only 10 samples in the minority class. Now we can use the smote(() function from the performanceEstimation package to generate synthetic samples:

library(performanceEstimation)     # For SMOTE

set.seed(123)

# Simulate an imbalanced dataset
data <- data.frame(
  x1 = c(rnorm(90, mean = 0), rnorm(10, mean = 3)),
  x2 = c(rnorm(90, mean = 0), rnorm(10, mean = 3)),
  class = as.factor(c(rep(0, 90), rep(1, 10)))  # 90 majority (0), 10 minority (1)
)

# Check class distribution
table(data$class)
## 
##  0  1 
## 90 10
# Apply SMOTE
smote_data <- performanceEstimation::smote(
  form = class ~ .,       # Formula specifying the target and predictors
  data = data,            # Original dataset
  perc.over = 200,        # Percentage of new synthetic samples for the minority class
  perc.under = 150        # Percentage of majority samples to retain
)

# Check the new class distribution
table(smote_data$class)
## 
##      0      1 
## 300000   2010

Note the following outcomes:

  • perc.over = 200: Increases the minority class by 200% (adds twice as many synthetic samples).
  • perc.under = 150: Adjusts the majority class to balance the dataset.

There are other packages for R that also implement SMOTE, such as the smotefamily package explained with an example later in this section.

We can visualize the effects of SMOTE on the dataset by comparing the original data sets distribution of classes against the oversampled data set:

library(ggplot2)

# Original data
ggplot(data, aes(x = x1, y = x2, color = factor(class))) +
  geom_point() +
  ggtitle("Original Dataset")

# SMOTE data
ggplot(smote_data, aes(x = x1, y = x2, color = factor(class))) +
  geom_point() +
  ggtitle("Dataset After SMOTE")

The smote() function has two important parameters:

  1. perc.over: Specifies the percentage of new synthetic samples to generate for the minority class.
  2. perc.under: Defines the proportion of majority class samples to keep after oversampling.

The smotefamily package provides additional flexibility, including options for multi-class datasets:

library(smotefamily)

set.seed(123)

# Simulate an imbalanced dataset
data <- data.frame(
  x1 = c(rnorm(90, mean = 0), rnorm(10, mean = 3)),
  x2 = c(rnorm(90, mean = 0), rnorm(10, mean = 3)),
  class = as.factor(c(rep(0, 90), rep(1, 10)))  # 90 majority (0), 10 minority (1)
)

# Apply SMOTE
smote_result <- smotefamily::SMOTE(
  X = data[, c("x1", "x2")],  # Features
  target = data$class,        # Target variable
  K = 5,                      # Number of nearest neighbors
  dup_size = 2                # Number of synthetic samples per minority class instance
)

# Extract the resampled data as a dataframe
smote_data <- data.frame(x1 = smote_result$data$x1,
                         x2 = smote_result$data$x2,
                         class = as.numeric(smote_result$data$class))



# Check the new class distribution
table(smote_data$class)
## 
##  0  1 
## 90 30

In the above example, there are two key parameters for the SMOTE() function:

  1. K: Number of nearest neighbors to consider.
  2. dup_size: Number of synthetic samples to generate per minority class instance.

To assess the effectiveness of SMOTE, train a model before and after applying it and compare the model’s performance:

library(ROCR)

# Train a logistic regression model on the original data
model_original <- glm(class ~ ., data = data, family = binomial)
pred_original <- predict(model_original, type = "response")
roc_original <- ROCR::prediction(pred_original, data$class)
auc_original <- ROCR::performance(roc_original, "auc")@y.values

# Train a logistic regression model on SMOTE data
model_smote <- glm(class ~ ., data = smote_data, family = binomial)
pred_smote <- predict(model_smote, newdata = smote_data, type = "response")
roc_smote <- ROCR::prediction(pred_smote, smote_data$class)
auc_smote <- ROCR::performance(roc_smote, "auc")@y.values

# Compare AUC
print(paste("AUC Before SMOTE:", auc_original))
## [1] "AUC Before SMOTE: 1"
print(paste("AUC After SMOTE:", auc_smote))
## [1] "AUC After SMOTE: 1"

Limitations of SMOTE

There are some important limitations to SMOTE which implies judicious use:

  1. Synthetic Data Quality:
    • If the minority class has noisy or overlapping samples, SMOTE may amplify this noise.
  2. Scalability:
    • Computational cost increases with the number of features and samples.
  3. Boundary Overlap:
    • SMOTE may generate synthetic samples that overlap with the majority class, leading to less discriminative decision boundaries.

Despite its potential limitations, SMOTE is a powerful resampling technique for addressing class imbalance, particularly in datasets where the minority class has well-defined and representative samples. By generating synthetic examples, it improves model performance on the minority class, resulting in better generalization. However, careful parameter tuning and evaluation are essential to avoid introducing noise or overcomplicating class boundaries.

ROSE vs SMOTE

SMOTE (Synthetic Minority Over-sampling Technique) and ROSE (Random Over-Sampling Examples) are two key resampling techniques designed to address class imbalance in datasets by generating synthetic samples. However, they differ in methodology and suitability for specific scenarios. The table below summarizes those differences:

Feature SMOTE ROSE
Methodology Interpolates between existing minority class samples using nearest neighbors. Uses kernel density estimation to generate synthetic points based on the entire data distribution.
Synthetic Samples Generated along line segments between existing minority samples. Generated randomly across the feature space, with smoothing to approximate the data distribution.
Focus Enhances decision boundaries by creating samples near existing data points. Provides a balanced and smoothed representation of both classes, not limited to minority class.
Noise Handling Can amplify noise or overlap if the minority class has noisy samples. Less prone to overfitting but can introduce unrelated points if the data distribution is poorly estimated.
Scalability Computationally heavier for large datasets due to nearest-neighbor calculations. Relatively lighter but depends on kernel density estimation complexity.
Applicability Works well with structured, clean datasets and when the minority class is not too sparse. Effective for datasets with noisy or overlapping class boundaries.

To summarize, we generally want to use SMOTE for structured datasets requiring precise synthetic data, and prefer ROSE for datasets needing broader smoothing or when noise in the minority class is a concern. So, in short, use:

  1. SMOTE:
    • when you have a clean dataset with a well-defined minority class.
    • when the goal is to create synthetic samples that closely resemble the original minority class.
    • for tasks requiring stronger decision boundaries, such as fraud detection or medical diagnosis.
  2. ROSE:
    • when the dataset is noisy or has overlapping class boundaries.
    • when you want a broader smoothing of the feature space, especially if the minority class distribution is sparse.
    • for exploratory analysis or models sensitive to randomness in synthetic samples.

Algorithm Level Modifications

Algorithm-level modifications to address class imbalance involve adjusting the learning algorithm itself to account for the imbalance without altering the dataset. This is achieved by making the model sensitive to the importance of each class, typically by introducing class weights, cost-sensitive learning, or custom loss functions. These methods ensure that the minority class has a proportionally larger influence during training.

Example I: Using Class Weights in Logistic Regression

Many machine learning algorithms allow specifying class weights, which assign higher importance to the minority class during the training process. The code example below demonstrates this with a logistic regression model and assigning class weights.

# Simulate imbalanced data
set.seed(123)
data <- data.frame(
  x1 = c(rnorm(90, mean = 0), rnorm(10, mean = 3)),
  x2 = c(rnorm(90, mean = 0), rnorm(10, mean = 3)),
  class = c(rep(0, 90), rep(1, 10))
)

# Check class distribution
table(data$class)
## 
##  0  1 
## 90 10
# Assign weights: higher for minority class
weights <- ifelse(data$class == 1, 9, 1)  # Ratio 90:10, so minority gets 9x weight

# Train logistic regression with weights
model <- glm(class ~ x1 + x2, family = binomial, data = data, weights = weights)

# Model summary
summary(model)
## 
## Call:
## glm(formula = class ~ x1 + x2, family = binomial, data = data, 
##     weights = weights)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)
## (Intercept)  -119.91   46002.47  -0.003    0.998
## x1             42.90   17740.59   0.002    0.998
## x2             31.54   14212.17   0.002    0.998
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 2.4953e+02  on 99  degrees of freedom
## Residual deviance: 1.1412e-08  on 97  degrees of freedom
## AIC: 6
## 
## Number of Fisher Scoring iterations: 25
# Predict probabilities
predicted <- predict(model, type = "response")

# Evaluate performance
library(ROCR)
roc_curve <- prediction(predicted, data$class)
auc <- performance(roc_curve, "auc")@y.values[[1]]
print(paste("AUC with weights:", auc))
## [1] "AUC with weights: 1"

Note that in the code above, the weights parameter adjusts the influence of each class. In this case, minority class instances are assigned a weight of 9, amplifying their impact on the model. This approach works well when the dataset is highly imbalanced but representative of real-world proportions. The choice of weights is empirical and requires experimentation.


Example II: Cost-Sensitive Decision Trees

Cost-sensitive learning explicitly penalizes misclassifications of the minority class more heavily than those of the majority class. Decision tree algorithms, like rpart, allow specifying cost matrices to achieve this. The code below illustrates this for a decision tree.

library(rpart)

# Define cost matrix: higher penalty for minority misclassification
cost_matrix <- matrix(c(0, 10, 1, 0), nrow = 2, byrow = TRUE)

# Train a cost-sensitive decision tree
model <- rpart(
  class ~ x1 + x2,
  data = data,
  method = "class",
  parms = list(loss = cost_matrix)
)

# Print the tree structure
print(model)
## n= 100 
## 
## node), split, n, loss, yval, (yprob)
##       * denotes terminal node
## 
## 1) root 100 10 0 (0.90000000 0.10000000)  
##   2) x1< 2.270525 91  1 0 (0.98901099 0.01098901) *
##   3) x1>=2.270525 9  0 1 (0.00000000 1.00000000) *
# Predict and evaluate
predicted <- predict(model, type = "class")
confusion_matrix <- table(data$class, predicted)
print("Confusion Matrix:")
## [1] "Confusion Matrix:"
print(confusion_matrix)
##    predicted
##      0  1
##   0 90  0
##   1  1  9

Once again, the weights are chosen empirically and tuned through trial-and-error. The cost matrix penalizes misclassification of the minority class (10) more heavily than the majority class (1). This modification directly influences how the decision tree splits data, prioritizing minority class accuracy.

Example III: Custom Loss Function in Neural Networks

In deep learning, custom loss functions can be used to address class imbalance by assigning different penalties to errors based on class. The R example below uses a weighted loss in Keras. While not the only package for deep learning in R, the keras package in R provides an interface to the Keras deep learning library, which is built on top of TensorFlow. It is designed for creating and training deep learning models in a more accessible form Keras and TensorFlow are mostly used for tasks such as image recognition, natural language processing, and time-series forecasting. TensorFlow operates on a computation graph model, where operations are represented as nodes, and data flows between them along edges. The framework is optimized to handle large-scale numerical computations using this structure, particularly for neural networks. The training of deep neural networks with many hidden layers is computationally very expensive, so the code below may require significant time to run on typical systems.

library(keras)

# Prepare imbalanced data
x <- as.matrix(data[, c("x1", "x2")])
y <- as.numeric(data$class)

# Define a weighted binary cross-entropy loss
weighted_loss <- function(y_true, y_pred) {
  weights <- ifelse(y_true == 1, 9, 1)  # Higher weight for minority class
  keras::k_mean(weights * k_binary_crossentropy(y_true, y_pred), axis = -1)
}

# Build a simple neural network
model <- keras_model_sequential() %>%
  layer_dense(units = 16, activation = "relu", input_shape = ncol(x)) %>%
  layer_dense(units = 1, activation = "sigmoid")

model %>% compile(
  optimizer = "adam",
  loss = weighted_loss,
  metrics = c("accuracy")
)

# Train the model
model %>% fit(
  x,
  y,
  epochs = 50,
  batch_size = 10,
  verbose = 1
)

Here, the custom loss function applies higher penalties to errors on minority class samples. This approach is particularly effective for neural networks or other algorithms where class weights may not be directly supported. However, neural networks carry a significant computational penalty and are computationally very expensive and time-consuming to train.

Algorithm-Level Modifications are best used when when class imbalance is moderate, and the algorithm supports weight adjustments (e.g., logistic regression, SVMs, kNN) or when misclassification costs vary significantly between classes, especially in decision trees or ensemble models. In deep learning or advanced models that require fine-grained control over optimization custom loss functions are an option.

These techniques are effective when you want to keep the dataset intact (without resampling) and rely on the algorithm to balance the learning process. In practice, both methods, oversampling and algorithm modification, can be used together.

Summary

This lesson explored methods for addressing class imbalance in supervised machine learning, focusing on data-level resampling techniques and algorithm-level modifications. Resampling methods modify the dataset’s class distribution to balance the representation of the majority and minority classes. Two key techniques discussed were SMOTE (Synthetic Minority Over-sampling Technique) and ROSE (Random Over-Sampling Examples). SMOTE generates synthetic samples for the minority class by interpolating between existing instances and their nearest neighbors, making it particularly suitable for structured datasets with a well-defined minority class. In contrast, ROSE employs kernel density estimation to generate synthetic samples across the feature space, making it effective for handling noisy or overlapping class boundaries. While SMOTE is ideal for enhancing decision boundaries, ROSE offers versatility for noisy datasets.

Algorithm-level modifications address class imbalance by directly influencing the learning process. These include the use of class weights, cost-sensitive learning, and custom loss functions. Class weights, as shown in a weighted logistic regression example, assign higher importance to minority class samples, ensuring they have a greater impact during model training. Cost-sensitive learning, such as with cost-sensitive decision trees, penalizes misclassifications of the minority class more heavily, effectively guiding the algorithm to prioritize these cases. Custom loss functions, often applied in deep learning, allow precise control over the training process by introducing penalties tailored to the dataset’s needs, such as weighted binary cross-entropy. These algorithmic approaches are particularly useful when the dataset itself should remain unaltered, and the imbalance can be addressed through adjustments to the learning framework.

In summary, resampling methods like SMOTE and ROSE are best used when modifying the dataset is feasible, with SMOTE being preferred for well-defined minority classes and ROSE for noisy or sparse datasets. Algorithm-level modifications, including class weights, cost matrices, and custom loss functions, are preferable when the learning process requires more nuanced adjustments. Together, these strategies provide a robust toolkit for handling class imbalance, ensuring better performance and fair representation of minority classes in machine learning models.


Files & Resources

All Files for Lesson 3.224

References

None yet.

Errata

Let us know.


  1. In prior versions of R, the package unbalanced provided support for undersampling, but the package is no longer available.↩︎

LS0tCnRpdGxlOiAiTWFuYWdpbmcgQ2xhc3MgSW1iYWxhbmNlIgpwYXJhbXM6CiAgY2F0ZWdvcnk6IDMKICBzdGFja3M6IDAKICBudW1iZXI6IDIyNAogIHRpbWU6IDYwCiAgbGV2ZWw6IGludGVybWVkaWF0ZQogIHRhZ3M6IGNsYXNzIGltYmFsYW5jZSxzbW90ZSxvdmVyc2FtcGxpbmcKICBkZXNjcmlwdGlvbjogIkV4cGxhaW5zIGNvbW1vbiBtZXRob2RzIGZvciByZWR1Y2luZyBjbGFzcyBpbWJhbGFuY2UuIgpkYXRlOiAiPHNtYWxsPmByIFN5cy5EYXRlKClgPC9zbWFsbD4iCmF1dGhvcjogIjxzbWFsbD5NYXJ0aW4gU2NoZWRsYmF1ZXI8L3NtYWxsPiIKZW1haWw6ICJtLnNjaGVkbGJhdWVyQG5ldS5lZHUiCmFmZmlsaXRhdGlvbjogIk5vcnRoZWFzdGVybiBVbml2ZXJzaXR5IgpvdXRwdXQ6IAogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0aGVtZTogbHVtZW4KICAgIGhpZ2hsaWdodDogdGFuZ28KLS0tCgotLS0KdGl0bGU6ICI8c21hbGw+YHIgcGFyYW1zJGNhdGVnb3J5YC5gciBwYXJhbXMkbnVtYmVyYDwvc21hbGw+PGJyLz48c3BhbiBzdHlsZT0nY29sb3I6ICMyRTQwNTM7IGZvbnQtc2l6ZTogMC45ZW0nPmByIHJtYXJrZG93bjo6bWV0YWRhdGEkdGl0bGVgPC9zcGFuPiIKLS0tCgpgYGB7ciBjb2RlPXhmdW46OnJlYWRfdXRmOChwYXN0ZTAoaGVyZTo6aGVyZSgpLCcvUi9faW5zZXJ0MkRCLlInKSksIGluY2x1ZGUgPSBGQUxTRX0KYGBgCgojIyBPdmVydmlldwoKQ2xhc3MgaW1iYWxhbmNlIGluIHN1cGVydmlzZWQgbWFjaGluZSBsZWFybmluZyByZWZlcnMgdG8gdGhlIHNjZW5hcmlvIHdoZXJlIHRoZSBkaXN0cmlidXRpb24gb2YgY2xhc3NlcyBpbiBhIGRhdGFzZXQgaXMgc2lnbmlmaWNhbnRseSBza2V3ZWQsIG1lYW5pbmcgb25lIGNsYXNzIChvciBhIGZldyBjbGFzc2VzKSBoYXMgZmFyIG1vcmUgZXhhbXBsZXMgdGhhbiBvdGhlcnMuIFRoaXMgaW1iYWxhbmNlIHBvc2VzIGEgY2hhbGxlbmdlIGJlY2F1c2UgbW9zdCBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobXMgYXJlIGRlc2lnbmVkIHRvIG9wdGltaXplIG92ZXJhbGwgYWNjdXJhY3ksIG9mdGVuIGxlYWRpbmcgdG8gcG9vciBwZXJmb3JtYW5jZSBvbiB0aGUgbWlub3JpdHkgY2xhc3MuIFRoaXMgY2FuIGxlYWQgdG8gc2lnbmlmaWNhbnQgYmlhcyBpbiBwcmVkaWN0aW9uLgoKRm9yIGV4YW1wbGUsIGNvbnNpZGVyIGEgYmluYXJ5IGNsYXNzaWZpY2F0aW9uIHByb2JsZW0gZm9yIGRldGVjdGluZyBmcmF1ZHVsZW50IHRyYW5zYWN0aW9ucy4gSWYgOTglIG9mIHRyYW5zYWN0aW9ucyBhcmUgbGVnaXRpbWF0ZSBhbmQgb25seSAyJSBhcmUgZnJhdWR1bGVudCwgYSBuYWl2ZSBtb2RlbCBtaWdodCBwcmVkaWN0IGFsbCB0cmFuc2FjdGlvbnMgYXMgbGVnaXRpbWF0ZSB0byBhY2hpZXZlIDk4JSBhY2N1cmFjeS4gSG93ZXZlciwgdGhpcyBhcHByb2FjaCBmYWlscyB0byBpZGVudGlmeSBmcmF1ZHVsZW50IGNhc2VzLCB3aGljaCBhcmUgY3JpdGljYWwgaW4gdGhpcyBjb250ZXh0LgoKVGhlIGlzc3VlIGFyaXNlcyBiZWNhdXNlIGFsZ29yaXRobXMgbGlrZSBkZWNpc2lvbiB0cmVlcywgbmV1cmFsIG5ldHdvcmtzLCBhbmQgc3VwcG9ydCB2ZWN0b3IgbWFjaGluZXMgbWF5IGZvY3VzIG9uIHRoZSBtYWpvcml0eSBjbGFzcywgbmVnbGVjdGluZyB0aGUgbWlub3JpdHkgY2xhc3MuIE1ldHJpY3Mgc3VjaCBhcyBhY2N1cmFjeSBiZWNvbWUgbWlzbGVhZGluZywgYXMgaGlnaCBhY2N1cmFjeSBjYW4gYmUgYWNoaWV2ZWQgYnkgaWdub3JpbmcgbWlub3JpdHkgY2FzZXMgYWx0b2dldGhlci4gQWx0ZXJuYXRpdmUgZXZhbHVhdGlvbiBtZXRyaWNzIHN1Y2ggYXMgVHJ1ZSBQb3NpdGl2ZXMgYW5kIFRydWUgTmVnYXRpdmVzIG9yIHRoZSBGMS1TY29yZSBjYW4gaGlnaGxpZ2h0IHBvdGVudGlhbCBjbGFzcyBpbWJhbGFuY2UuCgpTZXZlcmFsIHN0cmF0ZWdpZXMgaGF2ZSBiZWVuIGRldmVsb3BlZCB0byBhbWVsaW9yYXRlIGNsYXNzIGltYmFsYW5jZToKCjEuICAqKkRhdGEtTGV2ZWwgQXBwcm9hY2hlcyoqOiBUaGVzZSBpbmNsdWRlIHJlc2FtcGxpbmcgbWV0aG9kcyBzdWNoIGFzIG92ZXJzYW1wbGluZyB0aGUgbWlub3JpdHkgY2xhc3MgKCplLmcuKiwgKlNNT1RFKikgb3IgdW5kZXJzYW1wbGluZyB0aGUgbWFqb3JpdHkgY2xhc3MgdG8gYmFsYW5jZSB0aGUgZGF0YXNldC4KCjIuICAqKkFsZ29yaXRobS1MZXZlbCBNb2RpZmljYXRpb25zKio6IFNvbWUgYWxnb3JpdGhtcyBjYW4gYmUgYWRqdXN0ZWQgdG8gaGFuZGxlIGltYmFsYW5jZXMgYnkgaW5jb3Jwb3JhdGluZyBjbGFzcyB3ZWlnaHRzIG9yIGNvc3Qtc2Vuc2l0aXZlIGxlYXJuaW5nLgoKMy4gICoqRXZhbHVhdGlvbiBNZXRyaWNzKio6IFVzaW5nIG1ldHJpY3MgbGlrZSBwcmVjaXNpb24sIHJlY2FsbCwgRjEtc2NvcmUsIGFuZCBhcmVhIHVuZGVyIHRoZSBST0MgY3VydmUgKEFVQy1ST0MpIHByb3ZpZGVzIGEgY2xlYXJlciBwaWN0dXJlIG9mIGEgbW9kZWwncyBwZXJmb3JtYW5jZSBvbiBpbWJhbGFuY2VkIGRhdGFzZXRzLgoKVW5kZXJzdGFuZGluZyBhbmQgYWRkcmVzc2luZyBjbGFzcyBpbWJhbGFuY2UgaXMgY3JpdGljYWwgZm9yIGJ1aWxkaW5nIHJvYnVzdCBhbmQgbWVhbmluZ2Z1bCBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscywgcGFydGljdWxhcmx5IGluIGFwcGxpY2F0aW9ucyBsaWtlIGZyYXVkIGRldGVjdGlvbiwgbWVkaWNhbCBkaWFnbm9zaXMsIGFuZCBhbm9tYWx5IGRldGVjdGlvbiwgd2hlcmUgdGhlIG1pbm9yaXR5IGNsYXNzIG9mdGVuIHJlcHJlc2VudHMgdGhlIGNhc2VzIG9mIGdyZWF0ZXN0IGludGVyZXN0IGJ1dCBnZW5lcmFsbHkgaGF2aW5nIHRoZSBsZWFzdCByZXByZXNlbnRhdGlvbiBpbiB0aGUgZGF0YS4KCiMjIERhdGEtTGV2ZWwgQXBwcm9hY2hlcwoKRGF0YS1sZXZlbCBtZXRob2RzIHRvIG1hbmFnZSBjbGFzcyBpbWJhbGFuY2UgZ2VuZXJhbGx5IGZvY3VzIG9uIHJlc2FtcGxpbmcuIFJlc2FtcGxpbmcgbWV0aG9kcyBhcmUgdGVjaG5pcXVlcyB1c2VkIHRvIGFkZHJlc3MgY2xhc3MgaW1iYWxhbmNlIGluIGRhdGFzZXRzIGJ5IG1vZGlmeWluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBtaW5vcml0eSBhbmQgbWFqb3JpdHkgY2xhc3Nlcy4gVGhlc2UgbWV0aG9kcyBvcGVyYXRlIGF0IHRoZSBkYXRhIGxldmVsLCBhaW1pbmcgdG8gY3JlYXRlIGEgYmFsYW5jZWQgZGF0YXNldCB0aGF0IGFsbG93cyBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscyB0byBsZWFybiBlZmZlY3RpdmVseSBmcm9tIGJvdGggY2xhc3Nlcy4gQ29tbW9uIHJlc2FtcGxpbmcgbWV0aG9kcyBpbmNsdWRlIG92ZXJzYW1wbGluZywgdW5kZXJzYW1wbGluZywgYW5kIGh5YnJpZCBhcHByb2FjaGVzLgoKIyMjIE92ZXJzYW1wbGluZwoKT3ZlcnNhbXBsaW5nIGlzIGRvbmUgYnkgaW5jcmVhc2luZyB0aGUgc2l6ZSBvZiB0aGUgbWlub3JpdHkgY2xhc3MgYnkgcmVwbGljYXRpbmcgZXhpc3Rpbmcgc2FtcGxlcyBvciBnZW5lcmF0aW5nIHN5bnRoZXRpYyBzYW1wbGVzLiBUaGVyZSBhcmUgc2V2ZXJhbCBjb21tb24gbWV0aG9kcyBmb3Igb3ZlcnNhbXBsaW5nIG1pbm9yaXR5IGNsYXNzZXM6CgoxLiAgKipSYW5kb20gT3ZlcnNhbXBsaW5nKio6IFJhbmRvbWx5IGR1cGxpY2F0ZXMgaW5zdGFuY2VzIG9mIHRoZSBtaW5vcml0eSBjbGFzcyB1bnRpbCB0aGUgY2xhc3MgZGlzdHJpYnV0aW9uIGlzIGJhbGFuY2VkLgoKMi4gICoqU3ludGhldGljIE1pbm9yaXR5IE92ZXItc2FtcGxpbmcgVGVjaG5pcXVlIChTTU9URSkqKjogR2VuZXJhdGVzIG5ldyBzeW50aGV0aWMgaW5zdGFuY2VzIGZvciB0aGUgbWlub3JpdHkgY2xhc3MgYnkgaW50ZXJwb2xhdGluZyBiZXR3ZWVuIGV4aXN0aW5nIHNhbXBsZXMgYW5kIHRoZWlyIG5lYXJlc3QgbmVpZ2hib3JzLgoKMy4gICoqUmFuZG9tIE92ZXItU2FtcGxpbmcgRXhhbXBsZXMgKFJPU0UpKio6IEEgcmVzYW1wbGluZyBtZXRob2QgZGVzaWduZWQgdG8gYWRkcmVzcyBjbGFzcyBpbWJhbGFuY2UgYnkgZ2VuZXJhdGluZyBzeW50aGV0aWMgc2FtcGxlcyBmb3IgdGhlIG1pbm9yaXR5IGNsYXNzLiBVbmxpa2UgYmFzaWMgb3ZlcnNhbXBsaW5nLCB3aGljaCBkdXBsaWNhdGVzIGV4aXN0aW5nIHNhbXBsZXMsIG9yIFNNT1RFLCB3aGljaCBpbnRlcnBvbGF0ZXMgYmV0d2VlbiBleGlzdGluZyBkYXRhIHBvaW50cywgUk9TRSB1c2VzIGEga2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbiBhcHByb2FjaCB0byBnZW5lcmF0ZSBzeW50aGV0aWMgZGF0YSBwb2ludHMuIFRoaXMgbWV0aG9kIGNyZWF0ZXMgYSBzbW9vdGhlciwgbW9yZSByZWFsaXN0aWMgcmVwcmVzZW50YXRpb24gb2YgdGhlIG1pbm9yaXR5IGNsYXNzIGRpc3RyaWJ1dGlvbi4KCk92ZXJzYW1wbGluZyBpcyBnZW5lcmFsbHkgdXNlZCB3aGVuIHRoZSBkYXRhc2V0IGlzIHNtYWxsLCBhcyBpdCBhdm9pZHMgbG9zaW5nIGluZm9ybWF0aW9uIGZyb20gdGhlIG1ham9yaXR5IGNsYXNzLiAqU01PVEUqIGlzIGEgcGFydGljdWxhcmx5IHVzZWZ1bCB0ZWNobmlxdWUgd2hlbiB0aGUgbWlub3JpdHkgY2xhc3MgaGFzIHN1ZmZpY2llbnQgdmFyaWFiaWxpdHkgdG8gc3ludGhlc2l6ZSBtZWFuaW5nZnVsIG5ldyBzYW1wbGVzLgoKIyMjIFVuZGVyc2FtcGxpbmcKClVuZGVyc2FtcGxpbmcgcmVkdWNlcyB0aGUgc2l6ZSBvZiB0aGUgbWFqb3JpdHkgY2xhc3MgYnkgcmFuZG9tbHkgcmVtb3Zpbmcgc2FtcGxlcyB0byBiYWxhbmNlIHRoZSBjbGFzcyBkaXN0cmlidXRpb24uIFNpbWlsYXIgdG8gb3ZlcnNhbXBsaW5nLCB0aGVyZSBhcmUgdmFyaW91cyBzdHJhdGVnaWVzOgoKMS4gICoqUmFuZG9tIFVuZGVyc2FtcGxpbmcqKjogUmFuZG9tbHkgc2VsZWN0cyBhIHN1YnNldCBvZiBtYWpvcml0eSBjbGFzcyBzYW1wbGVzIGVxdWFsIGluIHNpemUgdG8gdGhlIG1pbm9yaXR5IGNsYXNzLgoyLiAgKipUb21layBMaW5rcyoqOiBSZW1vdmVzIG1ham9yaXR5IGNsYXNzIGluc3RhbmNlcyB0aGF0IGFyZSBjbG9zZXN0IHRvIG1pbm9yaXR5IGNsYXNzIGluc3RhbmNlcyB0byBjbGVhbiBjbGFzcyBib3VuZGFyaWVzLgoKVW5kZXJzYW1wbGluZyBpcyBvZnRlbiB1c2VkIHdoZW4gdGhlIGRhdGFzZXQgaXMgbGFyZ2UsIGFuZCB0aGUgbWFqb3JpdHkgY2xhc3MgY29udGFpbnMgcmVkdW5kYW50IG9yIG5vaXN5IHNhbXBsZXMuIEl0IHdvcmtzIGJlc3Qgd2hlbiB0aGVyZSBpcyBubyByaXNrIG9mIGxvc2luZyB2YWx1YWJsZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgbWFqb3JpdHkgY2xhc3MuCgpUaGUgY29kZSBleGFtcGxlIGJlbG93IGlsbHVzdHJhdGVzIGJvdGggdW5kZXJzYW1wbGluZyBzdHJhdGVnaWVzW14xXS4gVW5kZXJzYW1wbGluZyBpbiBSIGNhbiBiZSBpbXBsZW1lbnRlZCB3aXRob3V0IHJlbHlpbmcgb24gYSBwYWNrYWdlIGJ5IG1hbnVhbGx5IHNlbGVjdGluZyBhIHJhbmRvbSBzdWJzZXQgb2YgdGhlIG1ham9yaXR5IGNsYXNzIHRvIG1hdGNoIHRoZSBzaXplIG9mIHRoZSBtaW5vcml0eSBjbGFzcy4gVGhpcyBhcHByb2FjaCBlbnN1cmVzIGEgYmFsYW5jZWQgZGF0YXNldCBieSByZWR1Y2luZyB0aGUgbWFqb3JpdHkgY2xhc3MgdG8gdGhlIHNpemUgb2YgdGhlIG1pbm9yaXR5IGNsYXNzLgoKW14xXTogSW4gcHJpb3IgdmVyc2lvbnMgb2YgUiwgdGhlIHBhY2thZ2UgKip1bmJhbGFuY2VkKiogcHJvdmlkZWQgc3VwcG9ydCBmb3IgdW5kZXJzYW1wbGluZywgYnV0IHRoZSBwYWNrYWdlIGlzIG5vIGxvbmdlciBhdmFpbGFibGUuCgoxLiAgKipDaGVjayBDbGFzcyBEaXN0cmlidXRpb24qKjogRGV0ZXJtaW5lIHRoZSBudW1iZXIgb2Ygc2FtcGxlcyBpbiB0aGUgbWlub3JpdHkgYW5kIG1ham9yaXR5IGNsYXNzZXMuCjIuICAqKlN1YnNldCB0aGUgTWFqb3JpdHkgQ2xhc3MqKjogUmFuZG9tbHkgc2FtcGxlIHJvd3MgZnJvbSB0aGUgbWFqb3JpdHkgY2xhc3MgdG8gbWF0Y2ggdGhlIHNpemUgb2YgdGhlIG1pbm9yaXR5IGNsYXNzLgozLiAgKipDb21iaW5lIHRoZSBTdWJzZXRzKio6IE1lcmdlIHRoZSByZWR1Y2VkIG1ham9yaXR5IGNsYXNzIHdpdGggdGhlIG1pbm9yaXR5IGNsYXNzIHRvIGNyZWF0ZSBhIGJhbGFuY2VkIGRhdGFzZXQuCgpMZXQncyBmaXJzdCBjcmVhdGUgYW4gaW1iYWxhbmNlZCBkYXRhc2V0IGZvciB0ZXN0aW5nOgoKYGBge3J9CnNldC5zZWVkKDEyMykKIyBTaW11bGF0ZSBhIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiBkYXRhc2V0CmRhdGEgPC0gZGF0YS5mcmFtZSgKICB4MSA9IHJub3JtKDEwMCksCiAgeDIgPSBybm9ybSgxMDApLAogIGNsYXNzID0gYyhyZXAoMCwgOTApLCByZXAoMSwgMTApKSAgIyBJbWJhbGFuY2VkIHdpdGggOTAgbWFqb3JpdHkgYW5kIDEwIG1pbm9yaXR5CikKCiMgQ2hlY2sgY2xhc3MgZGlzdHJpYnV0aW9uCnRhYmxlKGRhdGEkY2xhc3MpCmBgYAoKTm93IHdlIGNhbiBzZXBhcmF0ZSB0aGUgY2xhc3NlczoKCmBgYHtyfQojIFNlcGFyYXRlIG1ham9yaXR5IGFuZCBtaW5vcml0eSBjbGFzc2VzCm1ham9yaXR5IDwtIHN1YnNldChkYXRhLCBjbGFzcyA9PSAwKQptaW5vcml0eSA8LSBzdWJzZXQoZGF0YSwgY2xhc3MgPT0gMSkKYGBgCgpOZXh0LCB3ZSBleHRyYWN0IGEgcmFuZG9tIHNhbXBsZSBmcm9tIHRoZSBtYWpvcml0eSBjbGFzczoKCmBgYHtyfQojIFJhbmRvbWx5IHNhbXBsZSB0aGUgbWFqb3JpdHkgY2xhc3MgdG8gbWF0Y2ggdGhlIHNpemUgb2YgdGhlIG1pbm9yaXR5IGNsYXNzCnVuZGVyc2FtcGxlZF9tYWpvcml0eSA8LSBtYWpvcml0eVtzYW1wbGUobnJvdyhtYWpvcml0eSksIG5yb3cobWlub3JpdHkpKSwgXQpgYGAKCkZpbmFsbHkgd2UgY2FuIGNvbWJpbmUgdGhlIHN1YnNldHM6CgpgYGB7cn0KIyBDb21iaW5lIHRoZSB1bmRlcnNhbXBsZWQgbWFqb3JpdHkgY2xhc3Mgd2l0aCB0aGUgbWlub3JpdHkgY2xhc3MKYmFsYW5jZWRfZGF0YSA8LSByYmluZCh1bmRlcnNhbXBsZWRfbWFqb3JpdHksIG1pbm9yaXR5KQoKIyBDaGVjayB0aGUgbmV3IGNsYXNzIGRpc3RyaWJ1dGlvbgp0YWJsZShiYWxhbmNlZF9kYXRhJGNsYXNzKQpgYGAKCkluIHRoZSBhYm92ZSBjb2RlLAoKMS4gIFRoZSBgc2FtcGxlKClgIGZ1bmN0aW9uIGlzIHVzZWQgdG8gcmFuZG9tbHkgc2VsZWN0IHJvd3MgZnJvbSB0aGUgbWFqb3JpdHkgY2xhc3MuCjIuICBUaGUgc2l6ZSBvZiB0aGUgc2FtcGxlIChgbnJvdyhtaW5vcml0eSlgKSBlbnN1cmVzIHRoYXQgdGhlIG1ham9yaXR5IGNsYXNzIGlzIHJlZHVjZWQgdG8gdGhlIHNpemUgb2YgdGhlIG1pbm9yaXR5IGNsYXNzLgozLiAgVGhlIGByYmluZCgpYCBmdW5jdGlvbiBjb21iaW5lcyB0aGUgcmVkdWNlZCBtYWpvcml0eSBjbGFzcyB3aXRoIHRoZSBtaW5vcml0eSBjbGFzcyBpbnRvIGEgYmFsYW5jZWQgZGF0YXNldC4KClRvIHZpc3VhbGl6ZSB0aGUgZWZmZWN0IG9mIHVuZGVyc2FtcGxpbmcsIHlvdSBjYW4gcGxvdCB0aGUgb3JpZ2luYWwgYW5kIGJhbGFuY2VkIGRhdGFzZXRzOgoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKCiMgUGxvdCBvcmlnaW5hbCBkYXRhCmdncGxvdChkYXRhLCBhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG9yID0gZmFjdG9yKGNsYXNzKSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdndGl0bGUoIk9yaWdpbmFsIEltYmFsYW5jZWQgRGF0YXNldCIpCgojIFBsb3QgYmFsYW5jZWQgZGF0YQpnZ3Bsb3QoYmFsYW5jZWRfZGF0YSwgYWVzKHggPSB4MSwgeSA9IHgyLCBjb2xvciA9IGZhY3RvcihjbGFzcykpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZ3RpdGxlKCJEYXRhc2V0IEFmdGVyIFVuZGVyc2FtcGxpbmciKQpgYGAKClJlbWVtYmVyIHRoYXQgdW5kZXJzYW1wbGluZyBpbXBsaWVzIHBvdGVudGlhbGx5IGE6CgoxLiAgKipMb3NzIG9mIEluZm9ybWF0aW9uKio6IFVuZGVyc2FtcGxpbmcgcmVkdWNlcyB0aGUgc2l6ZSBvZiB0aGUgZGF0YXNldCwgcG90ZW50aWFsbHkgZGlzY2FyZGluZyB1c2VmdWwgaW5mb3JtYXRpb24gZnJvbSB0aGUgbWFqb3JpdHkgY2xhc3MuCjIuICAqKlJpc2sgb2YgT3ZlcmZpdHRpbmcqKjogV2l0aCBhIHNtYWxsIGRhdGFzZXQsIHRoZSBtb2RlbCBtaWdodCBvdmVyZml0IHRvIHRoZSByZWR1Y2VkIGRhdGEuCgojIyMgSHlicmlkIE1ldGhvZHMKCkh5YnJpZCBtZXRob2RzIGNvbWJpbmUgb3ZlcnNhbXBsaW5nIGFuZCB1bmRlcnNhbXBsaW5nIHRvIGFjaGlldmUgYSBiYWxhbmNlZCBkYXRhc2V0IHdpdGggbWluaW1hbCByZWR1bmRhbmN5IGFuZCBpbXByb3ZlZCBzeW50aGV0aWMgc2FtcGxlIHF1YWxpdHkuIFRoZXkgZ2VuZXJhbGx5IHN0YXJ0IGJ5IG92ZXJzYW1wbGluZyB0aGUgbWlub3JpdHkgY2xhc3MgdG8gYSBwcmVkZWZpbmVkIGxldmVsIGFuZCB0aGVuIHVuZGVyc2FtcGxpbmcgdGhlIG1ham9yaXR5IGNsYXNzIHRvIHJlZHVjZSByZWR1bmRhbmN5LiBNZXRob2RzIGxpa2UgU01PVEUtVG9tZWsgTGlua3Mgb3IgU01PVEUtRU5OIGNvbWJpbmUgc3ludGhldGljIG92ZXJzYW1wbGluZyB3aXRoIGNsZWFuaW5nIHRlY2huaXF1ZXMuCgpIeWJyaWQgbWV0aG9kcyB3b3JrIGJlc3Qgd2hlbiB0aGUgZGF0YXNldCBjb250YWlucyBub2lzeSBvciBvdmVybGFwcGluZyBjbGFzcyBib3VuZGFyaWVzLiBJdCBpcyBtb3N0IGVmZmVjdGl2ZSBmb3IgaGlnaGx5IGltYmFsYW5jZWQgZGF0YXNldHMgd2hlcmUgYSBzaW1wbGUgb3ZlcnNhbXBsaW5nIG9yIHVuZGVyc2FtcGxpbmcgYXBwcm9hY2ggbWlnaHQgZmFpbC4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIFN1bW1hcnkgb2YgVXNlIENhc2VzCgoxLiAgKipSYW5kb20gT3ZlcnNhbXBsaW5nKio6IFVzZSBmb3Igc21hbGwgZGF0YXNldHMgb3Igd2hlbiBzeW50aGV0aWMgZ2VuZXJhdGlvbiBtaWdodCBpbnRyb2R1Y2Ugbm9pc2UuCjIuICAqKlNNT1RFKio6IFVzZSB3aGVuIHN5bnRoZXRpYyBzYW1wbGUgcXVhbGl0eSBpcyBlc3NlbnRpYWwgYW5kIHRoZSBtaW5vcml0eSBjbGFzcyBpcyB3ZWxsLWRlZmluZWQuCjMuICAqKlJhbmRvbSBVbmRlcnNhbXBsaW5nKio6IFVzZSBmb3IgbGFyZ2UgZGF0YXNldHMgd2l0aCByZWR1bmRhbnQgbWFqb3JpdHkgc2FtcGxlcy4KNC4gICoqSHlicmlkIE1ldGhvZHMqKjogVXNlIGZvciBub2lzeSBvciBjb21wbGV4IGNsYXNzIGJvdW5kYXJpZXMuCgpCeSBhcHBseWluZyB0aGVzZSB0ZWNobmlxdWVzIGp1ZGljaW91c2x5LCBwcmFjdGl0aW9uZXJzIGNhbiBtaXRpZ2F0ZSB0aGUgYWR2ZXJzZSBlZmZlY3RzIG9mIGNsYXNzIGltYmFsYW5jZSBhbmQgaW1wcm92ZSBtb2RlbCBwZXJmb3JtYW5jZSBvbiBib3RoIHRoZSBtYWpvcml0eSBhbmQgbWlub3JpdHkgY2xhc3Nlcy4KCiMjIFN5bnRoZXRpYyBPdmVyc2FtcGxpbmcgTWV0aG9kcwoKTGV0J3MgdGFrZSBhIGNsb3NlciBsb29rIHdpdGggYWRkaXRpb25hbCBkZXRhaWxzIG9uIHRoZSB0d28gbW9zdCBjb21tb24gb3ZlcnNhbXBsaW5nIG1ldGhvZHM6ICpST1NFKiBhbmQgKlNNT1RFLioKCiMjIyBST1NFCgoqKlJPU0UgKFJhbmRvbSBPdmVyLVNhbXBsaW5nIEV4YW1wbGVzKSoqIGlzIGEgcmVzYW1wbGluZyBtZXRob2QgZGVzaWduZWQgdG8gYWRkcmVzcyBjbGFzcyBpbWJhbGFuY2UgYnkgZ2VuZXJhdGluZyBzeW50aGV0aWMgc2FtcGxlcyBmb3IgdGhlIG1pbm9yaXR5IGNsYXNzLiBVbmxpa2UgYmFzaWMgb3ZlcnNhbXBsaW5nLCB3aGljaCBkdXBsaWNhdGVzIGV4aXN0aW5nIHNhbXBsZXMsIG9yIFNNT1RFLCB3aGljaCBpbnRlcnBvbGF0ZXMgYmV0d2VlbiBleGlzdGluZyBkYXRhIHBvaW50cywgUk9TRSB1c2VzIGEga2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbiBhcHByb2FjaCB0byBnZW5lcmF0ZSBzeW50aGV0aWMgZGF0YSBwb2ludHMuIFRoaXMgbWV0aG9kIGNyZWF0ZXMgYSBzbW9vdGhlciwgbW9yZSByZWFsaXN0aWMgcmVwcmVzZW50YXRpb24gb2YgdGhlIG1pbm9yaXR5IGNsYXNzIGRpc3RyaWJ1dGlvbi4KClJPU0UgZ2VuZXJhdGVzIHN5bnRoZXRpYyBleGFtcGxlcyBmb3IgYm90aCB0aGUgbWlub3JpdHkgYW5kIG1ham9yaXR5IGNsYXNzZXMgYnkgc2FtcGxpbmcgZnJvbSBhIHNtb290aGVkIGFwcHJveGltYXRpb24gb2YgdGhlIG9yaWdpbmFsIGRhdGEgZGlzdHJpYnV0aW9uLiBUaGlzIGlzIGFjaGlldmVkIHRocm91Z2ggdGhlIGZvbGxvd2luZyBzdGVwczoKCjEuICAqKktlcm5lbCBEZW5zaXR5IEVzdGltYXRpb24gKEtERSkqKjogQSBrZXJuZWwgZGVuc2l0eSBmdW5jdGlvbiBpcyBhcHBsaWVkIHRvIHRoZSBkYXRhIHRvIGVzdGltYXRlIHRoZSBwcm9iYWJpbGl0eSBkZW5zaXR5IGZ1bmN0aW9uIG9mIHRoZSBmZWF0dXJlIHNwYWNlLiBUaGlzIGRlbnNpdHkgaXMgdXNlZCB0byByYW5kb21seSBzYW1wbGUgbmV3IGRhdGEgcG9pbnRzLCBlbnN1cmluZyB0aGF0IHRoZSBzeW50aGV0aWMgcG9pbnRzIHJlZmxlY3QgdGhlIHVuZGVybHlpbmcgZGF0YSBkaXN0cmlidXRpb24uCgoyLiAgKipCYWxhbmNlZCBTYW1wbGluZyoqOiBTeW50aGV0aWMgc2FtcGxlcyBhcmUgYWRkZWQgdG8gdGhlIG1pbm9yaXR5IGNsYXNzIHRvIGJhbGFuY2UgdGhlIGRhdGFzZXQuIE9wdGlvbmFsbHksIHNvbWUgcG9pbnRzIGZyb20gdGhlIG1ham9yaXR5IGNsYXNzIGNhbiBhbHNvIGJlIHN5bnRoZXRpY2FsbHkgZ2VuZXJhdGVkIG9yIHJlbW92ZWQgdG8gZW5zdXJlIGJldHRlciBjbGFzcyBib3VuZGFyaWVzLgoKMy4gICoqTm9pc2UgSGFuZGxpbmcqKjogUk9TRSBpbmNvcnBvcmF0ZXMgYSBsZXZlbCBvZiByYW5kb21uZXNzLCByZWR1Y2luZyB0aGUgcmlzayBvZiBjcmVhdGluZyBleGFjdCBkdXBsaWNhdGVzIG9yIG92ZXJseSBzaW1wbGlzdGljIHN5bnRoZXRpYyBwb2ludHMsIHdoaWNoIGhlbHBzIGltcHJvdmUgZ2VuZXJhbGl6YXRpb24uCgojIyMjIEFkdmFudGFnZXMgb2YgUk9TRQoKUk9TRSBoYXMgc29tZSBrZXkgYWR2YW50YWdlcyBvdmVyIG90aGVyIG1ldGhvZHM6CgoxLiAgKipQcmVzZXJ2ZXMgRGF0YSBDaGFyYWN0ZXJpc3RpY3MqKjogU3ludGhldGljIHNhbXBsZXMgY2xvc2VseSByZXNlbWJsZSB0aGUgcmVhbCBkaXN0cmlidXRpb24sIGF2b2lkaW5nIGFydGlmYWN0cyBpbnRyb2R1Y2VkIGJ5IHNpbXBsZXIgbWV0aG9kcyBsaWtlIHJhbmRvbSBkdXBsaWNhdGlvbi4KMi4gICoqUmVkdWNlcyBPdmVyZml0dGluZyoqOiBCeSBnZW5lcmF0aW5nIG5ldyBwb2ludHMgcmF0aGVyIHRoYW4gZHVwbGljYXRpbmcgZXhpc3Rpbmcgb25lcywgUk9TRSBtaXRpZ2F0ZXMgdGhlIHJpc2sgb2Ygb3ZlcmZpdHRpbmcgdG8gdGhlIG1pbm9yaXR5IGNsYXNzLgozLiAgKipJbXByb3ZlcyBDbGFzcyBCb3VuZGFyaWVzKio6IFRoZSBtZXRob2Qgb2Z0ZW4gZ2VuZXJhdGVzIHBvaW50cyBuZWFyIGRlY2lzaW9uIGJvdW5kYXJpZXMsIGltcHJvdmluZyB0aGUgbW9kZWwncyBhYmlsaXR5IHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gY2xhc3Nlcy4KClJPU0UgaXMgcGFydGljdWxhcmx5IGVmZmVjdGl2ZSBpbiBzaXR1YXRpb25zIHdoZXJlIHRoZSBtaW5vcml0eSBjbGFzcyBpcyBoaWdobHkgdW5kZXJyZXByZXNlbnRlZCBhbmQgdGhlIGRhdGFzZXQgY29udGFpbnMgY29tcGxleCBkZWNpc2lvbiBib3VuZGFyaWVzIG9yIG92ZXJsYXBzIGJldHdlZW4gY2xhc3NlcywgYnV0IGl0IGlzIGNvbXB1dGF0aW9uYWxseSBjb21wbGV4LiBIb3dldmVyLCBvZnRlbiBzaW1wbGVyIG1ldGhvZHMgbGlrZSByYW5kb20gb3ZlcnNhbXBsaW5nIG9yIFNNT1RFIGFyZSBpbnN1ZmZpY2llbnQgdG8gY2FwdHVyZSB0aGUgbWlub3JpdHkgY2xhc3MncyBkaXZlcnNpdHkuCgojIyMjIEV4YW1wbGUgb2YgQXBwbHlpbmcgUk9TRSBpbiBSCgpUaGUgYFJPU0VgIHBhY2thZ2UgaW4gUiBwcm92aWRlcyBhbiBlYXN5LXRvLXVzZSBpbXBsZW1lbnRhdGlvbiBvZiB0aGlzIG1ldGhvZC4gSGVyZSdzIGhvdyBpdCBjYW4gYmUgYXBwbGllZDoKCkNvbnNpZGVyIGEgYmluYXJ5IGNsYXNzaWZpY2F0aW9uIHByb2JsZW0gd2l0aCBhbiBpbWJhbGFuY2VkIGRhdGFzZXQuIFRoZSBjb2RlIGJlbG93IGFwcGxpZXMgcmFuZG9tIG92ZXJzYW1wbGluZyB1c2luZyBmdW5jdGlvbnMgZnJvbSB0aGUgKipST1NFKiogcGFja2FnZS4KCmBgYHtyfQpsaWJyYXJ5KFJPU0UpCgojIFNpbXVsYXRlIGltYmFsYW5jZWQgZGF0YQpzZXQuc2VlZCgxMjMpCmRhdGEgPC0gZGF0YS5mcmFtZSgKICB4MSA9IHJub3JtKDEwMCksCiAgeDIgPSBybm9ybSgxMDApLAogIGNsYXNzID0gYyhyZXAoMCwgOTApLCByZXAoMSwgMTApKQopCgojIENoZWNrIGNsYXNzIGRpc3RyaWJ1dGlvbgp0YWJsZShkYXRhJGNsYXNzKQoKIyBBcHBseSBST1NFIHRvIGdlbmVyYXRlIGJhbGFuY2VkIGRhdGEKcm9zZV9kYXRhIDwtIFJPU0UoY2xhc3MgfiAuLCBkYXRhID0gZGF0YSwgc2VlZCA9IDEpJGRhdGEKCiMgQ2hlY2sgdGhlIG5ldyBjbGFzcyBkaXN0cmlidXRpb24KdGFibGUocm9zZV9kYXRhJGNsYXNzKQpgYGAKCllvdSBjYW4gdmlzdWFsaXplIGhvdyBST1NFIGdlbmVyYXRlcyBzeW50aGV0aWMgcG9pbnRzIHVzaW5nIHNjYXR0ZXJwbG90czoKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCgojIE9yaWdpbmFsIGRhdGEKZ2dwbG90KGRhdGEsIGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3IgPSBmYWN0b3IoY2xhc3MpKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2d0aXRsZSgiT3JpZ2luYWwgRGF0YSIpCgojIFJPU0UgZGF0YQpnZ3Bsb3Qocm9zZV9kYXRhLCBhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG9yID0gZmFjdG9yKGNsYXNzKSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdndGl0bGUoIkRhdGEgQWZ0ZXIgUk9TRSIpCmBgYAoKVGhlICoqUk9TRSoqIHBhY2thZ2UgYWxzbyBwcm92aWRlcyBmdW5jdGlvbmFsaXR5IHRvIGV2YWx1YXRlIHRoZSBlZmZlY3RpdmVuZXNzIG9mIHJlc2FtcGxpbmcgdXNpbmcgbW9kZWxzLiBGb3IgZXhhbXBsZToKCmBgYHtyfQojIFRyYWluIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBvbiB0aGUgcmVzYW1wbGVkIGRhdGEKbW9kZWwgPC0gZ2xtKGNsYXNzIH4gLiwgZmFtaWx5ID0gYmlub21pYWwsIGRhdGEgPSByb3NlX2RhdGEpCgojIFByZWRpY3QgYW5kIGV2YWx1YXRlCnByZWQgPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IGRhdGEsIHR5cGUgPSAicmVzcG9uc2UiKQpyb2MuY3VydmUoZGF0YSRjbGFzcywgcHJlZCkKYGBgCgpXaGlsZSBST1NFIGlzIGhpZ2hseSBlZmZlY3RpdmUsIGl0IGhhcyBzb21lIGxpbWl0YXRpb25zOgoKMS4gICoqUG90ZW50aWFsIE5vaXNlIEludHJvZHVjdGlvbioqOiBFeGNlc3NpdmUgcmFuZG9tbmVzcyBpbiBzeW50aGV0aWMgcG9pbnQgZ2VuZXJhdGlvbiBjYW4gaW50cm9kdWNlIG5vaXNlIGlmIHRoZSBkYXRhIGRpc3RyaWJ1dGlvbiBpcyBub3Qgd2VsbC1jYXB0dXJlZCBieSB0aGUga2VybmVsLgoyLiAgKipTY2FsYWJpbGl0eSoqOiBDb21wdXRhdGlvbmFsbHkgaW50ZW5zaXZlIGZvciB2ZXJ5IGxhcmdlIGRhdGFzZXRzLgoKTmV2ZXJ0aGVsZXNzLCBST1NFIGlzIGEgdmVyc2F0aWxlIHN0cmF0ZWd5IGZvciBoYW5kbGluZyBjbGFzcyBpbWJhbGFuY2UgYnkgZ2VuZXJhdGluZyBzeW50aGV0aWMgZXhhbXBsZXMgdGhhdCByZWZsZWN0IHRoZSBvcmlnaW5hbCBkYXRhIGRpc3RyaWJ1dGlvbi4gSXQgaXMgcGFydGljdWxhcmx5IHN1aXRhYmxlIGZvciBkYXRhc2V0cyB3aXRoIGNvbXBsZXggcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGZlYXR1cmVzIGFuZCBjbGFzc2VzLCBwcm92aWRpbmcgYSByb2J1c3QgYWx0ZXJuYXRpdmUgdG8gdHJhZGl0aW9uYWwgcmVzYW1wbGluZyBtZXRob2RzLgoKIyMjIFNNT1RFCgpTTU9URSBpcyBhIHBvcHVsYXIgcmVzYW1wbGluZyB0ZWNobmlxdWUgdXNlZCB0byBhZGRyZXNzIGNsYXNzIGltYmFsYW5jZSBpbiBkYXRhc2V0cyBieSBnZW5lcmF0aW5nIHN5bnRoZXRpYyBzYW1wbGVzIGZvciB0aGUgbWlub3JpdHkgY2xhc3MuIFVubGlrZSByYW5kb20gb3ZlcnNhbXBsaW5nLCB3aGljaCBkdXBsaWNhdGVzIGV4aXN0aW5nIG1pbm9yaXR5IGNsYXNzIHNhbXBsZXMsIFNNT1RFIGNyZWF0ZXMgbmV3IGRhdGEgcG9pbnRzIGJ5IGludGVycG9sYXRpbmcgYmV0d2VlbiBleGlzdGluZyBtaW5vcml0eSBjbGFzcyBzYW1wbGVzLiBUaGlzIG1ldGhvZCBoZWxwcyByZWR1Y2Ugb3ZlcmZpdHRpbmcgYW5kIGVuaGFuY2VzIHRoZSBtaW5vcml0eSBjbGFzcyByZXByZXNlbnRhdGlvbiBpbiB0aGUgZGF0YXNldC4KClNNT1RFIGFwcGxpZXMgdGhlIGZvbGxvd2luZyBzdGVwczoKCjEuICAqKklkZW50aWZ5IE5lYXJlc3QgTmVpZ2hib3JzKio6IEZvciBlYWNoIHNhbXBsZSBpbiB0aGUgbWlub3JpdHkgY2xhc3MsIFNNT1RFIGlkZW50aWZpZXMgaXRzIG5lYXJlc3QgbmVpZ2hib3JzIHdpdGhpbiB0aGUgc2FtZSBjbGFzcyBiYXNlZCBvbiBhIGRpc3RhbmNlIG1ldHJpYyAoY29tbW9ubHkgRXVjbGlkZWFuIGRpc3RhbmNlKS4gVGhpcyBpcyBlc3NlbnRpYWxseSBhbiBhcHBsaWNhdGlvbiBvZiB0aGUgKmtOTiogYWxnb3JpdGhtLiBOb3RlIHRoYXQgdGhpcyByZXF1aXJlcyBjYXRlZ29yaWNhbCBmZWF0dXJlcyB0byBiZSBudW1lcmljYWxseSBlbmNvZGVkIGFuZCBudW1lcmljIGZlYXR1cmVzIHRvIGJlIG5vcm1hbGl6ZWQgdG8gYSBjb21tb24gc2NhbGUuCgoyLiAgKipHZW5lcmF0ZSBTeW50aGV0aWMgU2FtcGxlcyoqOiBOZXcgc2FtcGxlcyBhcmUgY3JlYXRlZCBieSB0YWtpbmcgYSB3ZWlnaHRlZCBhdmVyYWdlIG9mIGEgbWlub3JpdHkgY2xhc3Mgc2FtcGxlIGFuZCBvbmUgb2YgaXRzIG5lYXJlc3QgbmVpZ2hib3JzLiBUaGlzIGludGVycG9sYXRpb24gZW5zdXJlcyB0aGF0IHN5bnRoZXRpYyBzYW1wbGVzIGxpZSBhbG9uZyB0aGUgbGluZSBzZWdtZW50cyBjb25uZWN0aW5nIGV4aXN0aW5nIHNhbXBsZXMuCgozLiAgKipSZXBlYXQgVW50aWwgQmFsYW5jZWQqKjogVGhpcyBwcm9jZXNzIGlzIHJlcGVhdGVkIHVudGlsIHRoZSBkZXNpcmVkIGNsYXNzIGJhbGFuY2UgaXMgYWNoaWV2ZWQuCgojIyMjIEFkdmFudGFnZXMgb2YgU01PVEUKClNNT1RFIGhhcyBzb21lIGtleSBhZHZhbnRhZ2VzIG92ZXIgb3RoZXIgbWV0aG9kczoKCjEuICAqKkF2b2lkcyBPdmVyZml0dGluZyoqOiBCeSBnZW5lcmF0aW5nIG5ldyBzeW50aGV0aWMgc2FtcGxlcyByYXRoZXIgdGhhbiBkdXBsaWNhdGluZyBleGlzdGluZyBvbmVzLCBTTU9URSBtaXRpZ2F0ZXMgdGhlIHJpc2sgb2Ygb3ZlcmZpdHRpbmcgdG8gdGhlIG1pbm9yaXR5IGNsYXNzLgoKMi4gICoqRW5oYW5jZXMgR2VuZXJhbGl6YXRpb24qKjogU01PVEUgZW5jb3VyYWdlcyBtb2RlbHMgdG8gbGVhcm4gYSBicm9hZGVyIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBtaW5vcml0eSBjbGFzcywgZXNwZWNpYWxseSBuZWFyIGNsYXNzIGJvdW5kYXJpZXMuCgozLiAgKipJbXByb3ZlcyBEZWNpc2lvbiBCb3VuZGFyaWVzKio6IEJ5IGNyZWF0aW5nIHNhbXBsZXMgbmVhciB0aGUgZWRnZXMgb2YgdGhlIG1pbm9yaXR5IGNsYXNzLCBTTU9URSBoZWxwcyB0aGUgbW9kZWwgZGlzdGluZ3Vpc2ggYmV0dGVyIGJldHdlZW4gdGhlIG1ham9yaXR5IGFuZCBtaW5vcml0eSBjbGFzc2VzLgoKU01PVEUgaXMgcGFydGljdWxhcmx5IGVmZmVjdGl2ZSBpbiBjYXNlcyB3aGVyZToKCjEuICBUaGUgZGF0YXNldCBoYXMgYSBzaWduaWZpY2FudCBjbGFzcyBpbWJhbGFuY2UuCjIuICBUaGUgbWlub3JpdHkgY2xhc3MgaXMgdW5kZXJyZXByZXNlbnRlZCwgbWFraW5nIGl0IGRpZmZpY3VsdCBmb3IgdGhlIG1vZGVsIHRvIGxlYXJuIGl0cyBwYXR0ZXJucy4KMy4gIFRoZSBtaW5vcml0eSBjbGFzcyBkaXN0cmlidXRpb24gaXMgd2VsbC1kZWZpbmVkIGFuZCBub3Qgbm9pc3kuCgojIyMjIEltcGxlbWVudGF0aW9uIG9mIFNNT1RFIGluIFIKClRoZSAqKkRNd1IqKiBhbmQgKipzbW90ZWZhbWlseSoqIHBhY2thZ2VzIGluIFIgcHJvdmlkZSBpbXBsZW1lbnRhdGlvbnMgb2YgU01PVEUuIEhlcmUncyBhIHN0ZXAtYnktc3RlcCBleHBsYW5hdGlvbiB3aXRoIGV4YW1wbGVzIHVzaW5nIGEgc2ltdWxhdGVkIGltYmFsYW5jZWQgYmluYXJ5IGNsYXNzaWZpY2F0aW9uIGRhdGFzZXQ6CgpgYGB7cn0Kc2V0LnNlZWQoMTIzKQpkYXRhIDwtIGRhdGEuZnJhbWUoCiAgeDEgPSBjKHJub3JtKDkwLCBtZWFuID0gMCksIHJub3JtKDEwLCBtZWFuID0gMykpLAogIHgyID0gYyhybm9ybSg5MCwgbWVhbiA9IDApLCBybm9ybSgxMCwgbWVhbiA9IDMpKSwKICBjbGFzcyA9IGMocmVwKDAsIDkwKSwgcmVwKDEsIDEwKSkKKQoKIyBDaGVjayBjbGFzcyBkaXN0cmlidXRpb24KdGFibGUoZGF0YSRjbGFzcykKYGBgCgpUaGUgZGF0YXNldCBpcyBpbWJhbGFuY2VkLCB3aXRoIG9ubHkgMTAgc2FtcGxlcyBpbiB0aGUgbWlub3JpdHkgY2xhc3MuIE5vdyB3ZSBjYW4gdXNlIHRoZSBgc21vdGUoKClgIGZ1bmN0aW9uIGZyb20gdGhlICoqcGVyZm9ybWFuY2VFc3RpbWF0aW9uKiogcGFja2FnZSB0byBnZW5lcmF0ZSBzeW50aGV0aWMgc2FtcGxlczoKCmBgYHtyfQpsaWJyYXJ5KHBlcmZvcm1hbmNlRXN0aW1hdGlvbikgICAgICMgRm9yIFNNT1RFCgpzZXQuc2VlZCgxMjMpCgojIFNpbXVsYXRlIGFuIGltYmFsYW5jZWQgZGF0YXNldApkYXRhIDwtIGRhdGEuZnJhbWUoCiAgeDEgPSBjKHJub3JtKDkwLCBtZWFuID0gMCksIHJub3JtKDEwLCBtZWFuID0gMykpLAogIHgyID0gYyhybm9ybSg5MCwgbWVhbiA9IDApLCBybm9ybSgxMCwgbWVhbiA9IDMpKSwKICBjbGFzcyA9IGFzLmZhY3RvcihjKHJlcCgwLCA5MCksIHJlcCgxLCAxMCkpKSAgIyA5MCBtYWpvcml0eSAoMCksIDEwIG1pbm9yaXR5ICgxKQopCgojIENoZWNrIGNsYXNzIGRpc3RyaWJ1dGlvbgp0YWJsZShkYXRhJGNsYXNzKQoKIyBBcHBseSBTTU9URQpzbW90ZV9kYXRhIDwtIHBlcmZvcm1hbmNlRXN0aW1hdGlvbjo6c21vdGUoCiAgZm9ybSA9IGNsYXNzIH4gLiwgICAgICAgIyBGb3JtdWxhIHNwZWNpZnlpbmcgdGhlIHRhcmdldCBhbmQgcHJlZGljdG9ycwogIGRhdGEgPSBkYXRhLCAgICAgICAgICAgICMgT3JpZ2luYWwgZGF0YXNldAogIHBlcmMub3ZlciA9IDIwMCwgICAgICAgICMgUGVyY2VudGFnZSBvZiBuZXcgc3ludGhldGljIHNhbXBsZXMgZm9yIHRoZSBtaW5vcml0eSBjbGFzcwogIHBlcmMudW5kZXIgPSAxNTAgICAgICAgICMgUGVyY2VudGFnZSBvZiBtYWpvcml0eSBzYW1wbGVzIHRvIHJldGFpbgopCgojIENoZWNrIHRoZSBuZXcgY2xhc3MgZGlzdHJpYnV0aW9uCnRhYmxlKHNtb3RlX2RhdGEkY2xhc3MpCgpgYGAKCk5vdGUgdGhlIGZvbGxvd2luZyBvdXRjb21lczoKCi0gICBgcGVyYy5vdmVyID0gMjAwYDogSW5jcmVhc2VzIHRoZSBtaW5vcml0eSBjbGFzcyBieSAyMDAlIChhZGRzIHR3aWNlIGFzIG1hbnkgc3ludGhldGljIHNhbXBsZXMpLgotICAgYHBlcmMudW5kZXIgPSAxNTBgOiBBZGp1c3RzIHRoZSBtYWpvcml0eSBjbGFzcyB0byBiYWxhbmNlIHRoZSBkYXRhc2V0LgoKVGhlcmUgYXJlIG90aGVyIHBhY2thZ2VzIGZvciBSIHRoYXQgYWxzbyBpbXBsZW1lbnQgU01PVEUsIHN1Y2ggYXMgdGhlICoqc21vdGVmYW1pbHkqKiBwYWNrYWdlIGV4cGxhaW5lZCB3aXRoIGFuIGV4YW1wbGUgbGF0ZXIgaW4gdGhpcyBzZWN0aW9uLgoKV2UgY2FuIHZpc3VhbGl6ZSB0aGUgZWZmZWN0cyBvZiBTTU9URSBvbiB0aGUgZGF0YXNldCBieSBjb21wYXJpbmcgdGhlIG9yaWdpbmFsIGRhdGEgc2V0cyBkaXN0cmlidXRpb24gb2YgY2xhc3NlcyBhZ2FpbnN0IHRoZSBvdmVyc2FtcGxlZCBkYXRhIHNldDoKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCgojIE9yaWdpbmFsIGRhdGEKZ2dwbG90KGRhdGEsIGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3IgPSBmYWN0b3IoY2xhc3MpKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2d0aXRsZSgiT3JpZ2luYWwgRGF0YXNldCIpCgojIFNNT1RFIGRhdGEKZ2dwbG90KHNtb3RlX2RhdGEsIGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3IgPSBmYWN0b3IoY2xhc3MpKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2d0aXRsZSgiRGF0YXNldCBBZnRlciBTTU9URSIpCmBgYAoKVGhlIGBzbW90ZSgpYCBmdW5jdGlvbiBoYXMgdHdvIGltcG9ydGFudCBwYXJhbWV0ZXJzOgoKMS4gIGBwZXJjLm92ZXJgOiBTcGVjaWZpZXMgdGhlIHBlcmNlbnRhZ2Ugb2YgbmV3IHN5bnRoZXRpYyBzYW1wbGVzIHRvIGdlbmVyYXRlIGZvciB0aGUgbWlub3JpdHkgY2xhc3MuCjIuICBgcGVyYy51bmRlcmA6IERlZmluZXMgdGhlIHByb3BvcnRpb24gb2YgbWFqb3JpdHkgY2xhc3Mgc2FtcGxlcyB0byBrZWVwIGFmdGVyIG92ZXJzYW1wbGluZy4KClRoZSBgc21vdGVmYW1pbHlgIHBhY2thZ2UgcHJvdmlkZXMgYWRkaXRpb25hbCBmbGV4aWJpbGl0eSwgaW5jbHVkaW5nIG9wdGlvbnMgZm9yIG11bHRpLWNsYXNzIGRhdGFzZXRzOgoKYGBge3J9CmxpYnJhcnkoc21vdGVmYW1pbHkpCgpzZXQuc2VlZCgxMjMpCgojIFNpbXVsYXRlIGFuIGltYmFsYW5jZWQgZGF0YXNldApkYXRhIDwtIGRhdGEuZnJhbWUoCiAgeDEgPSBjKHJub3JtKDkwLCBtZWFuID0gMCksIHJub3JtKDEwLCBtZWFuID0gMykpLAogIHgyID0gYyhybm9ybSg5MCwgbWVhbiA9IDApLCBybm9ybSgxMCwgbWVhbiA9IDMpKSwKICBjbGFzcyA9IGFzLmZhY3RvcihjKHJlcCgwLCA5MCksIHJlcCgxLCAxMCkpKSAgIyA5MCBtYWpvcml0eSAoMCksIDEwIG1pbm9yaXR5ICgxKQopCgojIEFwcGx5IFNNT1RFCnNtb3RlX3Jlc3VsdCA8LSBzbW90ZWZhbWlseTo6U01PVEUoCiAgWCA9IGRhdGFbLCBjKCJ4MSIsICJ4MiIpXSwgICMgRmVhdHVyZXMKICB0YXJnZXQgPSBkYXRhJGNsYXNzLCAgICAgICAgIyBUYXJnZXQgdmFyaWFibGUKICBLID0gNSwgICAgICAgICAgICAgICAgICAgICAgIyBOdW1iZXIgb2YgbmVhcmVzdCBuZWlnaGJvcnMKICBkdXBfc2l6ZSA9IDIgICAgICAgICAgICAgICAgIyBOdW1iZXIgb2Ygc3ludGhldGljIHNhbXBsZXMgcGVyIG1pbm9yaXR5IGNsYXNzIGluc3RhbmNlCikKCiMgRXh0cmFjdCB0aGUgcmVzYW1wbGVkIGRhdGEgYXMgYSBkYXRhZnJhbWUKc21vdGVfZGF0YSA8LSBkYXRhLmZyYW1lKHgxID0gc21vdGVfcmVzdWx0JGRhdGEkeDEsCiAgICAgICAgICAgICAgICAgICAgICAgICB4MiA9IHNtb3RlX3Jlc3VsdCRkYXRhJHgyLAogICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3MgPSBhcy5udW1lcmljKHNtb3RlX3Jlc3VsdCRkYXRhJGNsYXNzKSkKCgoKIyBDaGVjayB0aGUgbmV3IGNsYXNzIGRpc3RyaWJ1dGlvbgp0YWJsZShzbW90ZV9kYXRhJGNsYXNzKQpgYGAKCkluIHRoZSBhYm92ZSBleGFtcGxlLCB0aGVyZSBhcmUgdHdvIGtleSBwYXJhbWV0ZXJzIGZvciB0aGUgYFNNT1RFKClgIGZ1bmN0aW9uOgoKMS4gIGBLYDogTnVtYmVyIG9mIG5lYXJlc3QgbmVpZ2hib3JzIHRvIGNvbnNpZGVyLgoyLiAgYGR1cF9zaXplYDogTnVtYmVyIG9mIHN5bnRoZXRpYyBzYW1wbGVzIHRvIGdlbmVyYXRlIHBlciBtaW5vcml0eSBjbGFzcyBpbnN0YW5jZS4KClRvIGFzc2VzcyB0aGUgZWZmZWN0aXZlbmVzcyBvZiBTTU9URSwgdHJhaW4gYSBtb2RlbCBiZWZvcmUgYW5kIGFmdGVyIGFwcGx5aW5nIGl0IGFuZCBjb21wYXJlIHRoZSBtb2RlbCdzIHBlcmZvcm1hbmNlOgoKYGBge3Igd2FybmluZz1GQUxTRX0KbGlicmFyeShST0NSKQoKIyBUcmFpbiBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgb24gdGhlIG9yaWdpbmFsIGRhdGEKbW9kZWxfb3JpZ2luYWwgPC0gZ2xtKGNsYXNzIH4gLiwgZGF0YSA9IGRhdGEsIGZhbWlseSA9IGJpbm9taWFsKQpwcmVkX29yaWdpbmFsIDwtIHByZWRpY3QobW9kZWxfb3JpZ2luYWwsIHR5cGUgPSAicmVzcG9uc2UiKQpyb2Nfb3JpZ2luYWwgPC0gUk9DUjo6cHJlZGljdGlvbihwcmVkX29yaWdpbmFsLCBkYXRhJGNsYXNzKQphdWNfb3JpZ2luYWwgPC0gUk9DUjo6cGVyZm9ybWFuY2Uocm9jX29yaWdpbmFsLCAiYXVjIilAeS52YWx1ZXMKCiMgVHJhaW4gYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIG9uIFNNT1RFIGRhdGEKbW9kZWxfc21vdGUgPC0gZ2xtKGNsYXNzIH4gLiwgZGF0YSA9IHNtb3RlX2RhdGEsIGZhbWlseSA9IGJpbm9taWFsKQpwcmVkX3Ntb3RlIDwtIHByZWRpY3QobW9kZWxfc21vdGUsIG5ld2RhdGEgPSBzbW90ZV9kYXRhLCB0eXBlID0gInJlc3BvbnNlIikKcm9jX3Ntb3RlIDwtIFJPQ1I6OnByZWRpY3Rpb24ocHJlZF9zbW90ZSwgc21vdGVfZGF0YSRjbGFzcykKYXVjX3Ntb3RlIDwtIFJPQ1I6OnBlcmZvcm1hbmNlKHJvY19zbW90ZSwgImF1YyIpQHkudmFsdWVzCgojIENvbXBhcmUgQVVDCnByaW50KHBhc3RlKCJBVUMgQmVmb3JlIFNNT1RFOiIsIGF1Y19vcmlnaW5hbCkpCnByaW50KHBhc3RlKCJBVUMgQWZ0ZXIgU01PVEU6IiwgYXVjX3Ntb3RlKSkKYGBgCgojIyMjIExpbWl0YXRpb25zIG9mIFNNT1RFCgpUaGVyZSBhcmUgc29tZSBpbXBvcnRhbnQgbGltaXRhdGlvbnMgdG8gU01PVEUgd2hpY2ggaW1wbGllcyBqdWRpY2lvdXMgdXNlOgoKMS4gICoqU3ludGhldGljIERhdGEgUXVhbGl0eSoqOgogICAgLSAgIElmIHRoZSBtaW5vcml0eSBjbGFzcyBoYXMgbm9pc3kgb3Igb3ZlcmxhcHBpbmcgc2FtcGxlcywgU01PVEUgbWF5IGFtcGxpZnkgdGhpcyBub2lzZS4KMi4gICoqU2NhbGFiaWxpdHkqKjoKICAgIC0gICBDb21wdXRhdGlvbmFsIGNvc3QgaW5jcmVhc2VzIHdpdGggdGhlIG51bWJlciBvZiBmZWF0dXJlcyBhbmQgc2FtcGxlcy4KMy4gICoqQm91bmRhcnkgT3ZlcmxhcCoqOgogICAgLSAgIFNNT1RFIG1heSBnZW5lcmF0ZSBzeW50aGV0aWMgc2FtcGxlcyB0aGF0IG92ZXJsYXAgd2l0aCB0aGUgbWFqb3JpdHkgY2xhc3MsIGxlYWRpbmcgdG8gbGVzcyBkaXNjcmltaW5hdGl2ZSBkZWNpc2lvbiBib3VuZGFyaWVzLgoKRGVzcGl0ZSBpdHMgcG90ZW50aWFsIGxpbWl0YXRpb25zLCBTTU9URSBpcyBhIHBvd2VyZnVsIHJlc2FtcGxpbmcgdGVjaG5pcXVlIGZvciBhZGRyZXNzaW5nIGNsYXNzIGltYmFsYW5jZSwgcGFydGljdWxhcmx5IGluIGRhdGFzZXRzIHdoZXJlIHRoZSBtaW5vcml0eSBjbGFzcyBoYXMgd2VsbC1kZWZpbmVkIGFuZCByZXByZXNlbnRhdGl2ZSBzYW1wbGVzLiBCeSBnZW5lcmF0aW5nIHN5bnRoZXRpYyBleGFtcGxlcywgaXQgaW1wcm92ZXMgbW9kZWwgcGVyZm9ybWFuY2Ugb24gdGhlIG1pbm9yaXR5IGNsYXNzLCByZXN1bHRpbmcgaW4gYmV0dGVyIGdlbmVyYWxpemF0aW9uLiBIb3dldmVyLCBjYXJlZnVsIHBhcmFtZXRlciB0dW5pbmcgYW5kIGV2YWx1YXRpb24gYXJlIGVzc2VudGlhbCB0byBhdm9pZCBpbnRyb2R1Y2luZyBub2lzZSBvciBvdmVyY29tcGxpY2F0aW5nIGNsYXNzIGJvdW5kYXJpZXMuCgojIyMgUk9TRSB2cyBTTU9URQoKKipTTU9URSoqIChTeW50aGV0aWMgTWlub3JpdHkgT3Zlci1zYW1wbGluZyBUZWNobmlxdWUpIGFuZCAqKlJPU0UqKiAoUmFuZG9tIE92ZXItU2FtcGxpbmcgRXhhbXBsZXMpIGFyZSB0d28ga2V5IHJlc2FtcGxpbmcgdGVjaG5pcXVlcyBkZXNpZ25lZCB0byBhZGRyZXNzIGNsYXNzIGltYmFsYW5jZSBpbiBkYXRhc2V0cyBieSBnZW5lcmF0aW5nIHN5bnRoZXRpYyBzYW1wbGVzLiBIb3dldmVyLCB0aGV5IGRpZmZlciBpbiBtZXRob2RvbG9neSBhbmQgc3VpdGFiaWxpdHkgZm9yIHNwZWNpZmljIHNjZW5hcmlvcy4gVGhlIHRhYmxlIGJlbG93IHN1bW1hcml6ZXMgdGhvc2UgZGlmZmVyZW5jZXM6Cgp8IEZlYXR1cmUgfCBTTU9URSB8IFJPU0UgfAp8LS0tLXwtLS0tfC0tLS18CnwgKipNZXRob2RvbG9neSoqIHwgSW50ZXJwb2xhdGVzIGJldHdlZW4gZXhpc3RpbmcgbWlub3JpdHkgY2xhc3Mgc2FtcGxlcyB1c2luZyBuZWFyZXN0IG5laWdoYm9ycy4gfCBVc2VzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRpb24gdG8gZ2VuZXJhdGUgc3ludGhldGljIHBvaW50cyBiYXNlZCBvbiB0aGUgZW50aXJlIGRhdGEgZGlzdHJpYnV0aW9uLiB8CnwgKipTeW50aGV0aWMgU2FtcGxlcyoqIHwgR2VuZXJhdGVkIGFsb25nIGxpbmUgc2VnbWVudHMgYmV0d2VlbiBleGlzdGluZyBtaW5vcml0eSBzYW1wbGVzLiB8IEdlbmVyYXRlZCByYW5kb21seSBhY3Jvc3MgdGhlIGZlYXR1cmUgc3BhY2UsIHdpdGggc21vb3RoaW5nIHRvIGFwcHJveGltYXRlIHRoZSBkYXRhIGRpc3RyaWJ1dGlvbi4gfAp8ICoqRm9jdXMqKiB8IEVuaGFuY2VzIGRlY2lzaW9uIGJvdW5kYXJpZXMgYnkgY3JlYXRpbmcgc2FtcGxlcyBuZWFyIGV4aXN0aW5nIGRhdGEgcG9pbnRzLiB8IFByb3ZpZGVzIGEgYmFsYW5jZWQgYW5kIHNtb290aGVkIHJlcHJlc2VudGF0aW9uIG9mIGJvdGggY2xhc3Nlcywgbm90IGxpbWl0ZWQgdG8gbWlub3JpdHkgY2xhc3MuIHwKfCAqKk5vaXNlIEhhbmRsaW5nKiogfCBDYW4gYW1wbGlmeSBub2lzZSBvciBvdmVybGFwIGlmIHRoZSBtaW5vcml0eSBjbGFzcyBoYXMgbm9pc3kgc2FtcGxlcy4gfCBMZXNzIHByb25lIHRvIG92ZXJmaXR0aW5nIGJ1dCBjYW4gaW50cm9kdWNlIHVucmVsYXRlZCBwb2ludHMgaWYgdGhlIGRhdGEgZGlzdHJpYnV0aW9uIGlzIHBvb3JseSBlc3RpbWF0ZWQuIHwKfCAqKlNjYWxhYmlsaXR5KiogfCBDb21wdXRhdGlvbmFsbHkgaGVhdmllciBmb3IgbGFyZ2UgZGF0YXNldHMgZHVlIHRvIG5lYXJlc3QtbmVpZ2hib3IgY2FsY3VsYXRpb25zLiB8IFJlbGF0aXZlbHkgbGlnaHRlciBidXQgZGVwZW5kcyBvbiBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0aW9uIGNvbXBsZXhpdHkuIHwKfCAqKkFwcGxpY2FiaWxpdHkqKiB8IFdvcmtzIHdlbGwgd2l0aCBzdHJ1Y3R1cmVkLCBjbGVhbiBkYXRhc2V0cyBhbmQgd2hlbiB0aGUgbWlub3JpdHkgY2xhc3MgaXMgbm90IHRvbyBzcGFyc2UuIHwgRWZmZWN0aXZlIGZvciBkYXRhc2V0cyB3aXRoIG5vaXN5IG9yIG92ZXJsYXBwaW5nIGNsYXNzIGJvdW5kYXJpZXMuIHwKClRvIHN1bW1hcml6ZSwgd2UgZ2VuZXJhbGx5IHdhbnQgdG8gdXNlICoqU01PVEUqKiBmb3Igc3RydWN0dXJlZCBkYXRhc2V0cyByZXF1aXJpbmcgcHJlY2lzZSBzeW50aGV0aWMgZGF0YSwgYW5kIHByZWZlciAqKlJPU0UqKiBmb3IgZGF0YXNldHMgbmVlZGluZyBicm9hZGVyIHNtb290aGluZyBvciB3aGVuIG5vaXNlIGluIHRoZSBtaW5vcml0eSBjbGFzcyBpcyBhIGNvbmNlcm4uIFNvLCBpbiBzaG9ydCwgdXNlOgoKMS4gICoqU01PVEUqKjoKICAgIC0gICB3aGVuIHlvdSBoYXZlIGEgY2xlYW4gZGF0YXNldCB3aXRoIGEgd2VsbC1kZWZpbmVkIG1pbm9yaXR5IGNsYXNzLgogICAgLSAgIHdoZW4gdGhlIGdvYWwgaXMgdG8gY3JlYXRlIHN5bnRoZXRpYyBzYW1wbGVzIHRoYXQgY2xvc2VseSByZXNlbWJsZSB0aGUgb3JpZ2luYWwgbWlub3JpdHkgY2xhc3MuCiAgICAtICAgZm9yIHRhc2tzIHJlcXVpcmluZyBzdHJvbmdlciBkZWNpc2lvbiBib3VuZGFyaWVzLCBzdWNoIGFzIGZyYXVkIGRldGVjdGlvbiBvciBtZWRpY2FsIGRpYWdub3Npcy4KMi4gICoqUk9TRSoqOgogICAgLSAgIHdoZW4gdGhlIGRhdGFzZXQgaXMgbm9pc3kgb3IgaGFzIG92ZXJsYXBwaW5nIGNsYXNzIGJvdW5kYXJpZXMuCiAgICAtICAgd2hlbiB5b3Ugd2FudCBhIGJyb2FkZXIgc21vb3RoaW5nIG9mIHRoZSBmZWF0dXJlIHNwYWNlLCBlc3BlY2lhbGx5IGlmIHRoZSBtaW5vcml0eSBjbGFzcyBkaXN0cmlidXRpb24gaXMgc3BhcnNlLgogICAgLSAgIGZvciBleHBsb3JhdG9yeSBhbmFseXNpcyBvciBtb2RlbHMgc2Vuc2l0aXZlIHRvIHJhbmRvbW5lc3MgaW4gc3ludGhldGljIHNhbXBsZXMuCgojIyBBbGdvcml0aG0gTGV2ZWwgTW9kaWZpY2F0aW9ucwoKQWxnb3JpdGhtLWxldmVsIG1vZGlmaWNhdGlvbnMgdG8gYWRkcmVzcyBjbGFzcyBpbWJhbGFuY2UgaW52b2x2ZSBhZGp1c3RpbmcgdGhlIGxlYXJuaW5nIGFsZ29yaXRobSBpdHNlbGYgdG8gYWNjb3VudCBmb3IgdGhlIGltYmFsYW5jZSB3aXRob3V0IGFsdGVyaW5nIHRoZSBkYXRhc2V0LiBUaGlzIGlzIGFjaGlldmVkIGJ5IG1ha2luZyB0aGUgbW9kZWwgc2Vuc2l0aXZlIHRvIHRoZSBpbXBvcnRhbmNlIG9mIGVhY2ggY2xhc3MsIHR5cGljYWxseSBieSBpbnRyb2R1Y2luZyAqKmNsYXNzIHdlaWdodHMqKiwgKipjb3N0LXNlbnNpdGl2ZSBsZWFybmluZyoqLCBvciAqKmN1c3RvbSBsb3NzIGZ1bmN0aW9ucyoqLiBUaGVzZSBtZXRob2RzIGVuc3VyZSB0aGF0IHRoZSBtaW5vcml0eSBjbGFzcyBoYXMgYSBwcm9wb3J0aW9uYWxseSBsYXJnZXIgaW5mbHVlbmNlIGR1cmluZyB0cmFpbmluZy4KCiMjIyBFeGFtcGxlIEk6IFVzaW5nIENsYXNzIFdlaWdodHMgaW4gTG9naXN0aWMgUmVncmVzc2lvbgoKTWFueSBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobXMgYWxsb3cgc3BlY2lmeWluZyBjbGFzcyB3ZWlnaHRzLCB3aGljaCBhc3NpZ24gaGlnaGVyIGltcG9ydGFuY2UgdG8gdGhlIG1pbm9yaXR5IGNsYXNzIGR1cmluZyB0aGUgdHJhaW5pbmcgcHJvY2Vzcy4gVGhlIGNvZGUgZXhhbXBsZSBiZWxvdyBkZW1vbnN0cmF0ZXMgdGhpcyB3aXRoIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBhbmQgYXNzaWduaW5nIGNsYXNzIHdlaWdodHMuCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQojIFNpbXVsYXRlIGltYmFsYW5jZWQgZGF0YQpzZXQuc2VlZCgxMjMpCmRhdGEgPC0gZGF0YS5mcmFtZSgKICB4MSA9IGMocm5vcm0oOTAsIG1lYW4gPSAwKSwgcm5vcm0oMTAsIG1lYW4gPSAzKSksCiAgeDIgPSBjKHJub3JtKDkwLCBtZWFuID0gMCksIHJub3JtKDEwLCBtZWFuID0gMykpLAogIGNsYXNzID0gYyhyZXAoMCwgOTApLCByZXAoMSwgMTApKQopCgojIENoZWNrIGNsYXNzIGRpc3RyaWJ1dGlvbgp0YWJsZShkYXRhJGNsYXNzKQoKIyBBc3NpZ24gd2VpZ2h0czogaGlnaGVyIGZvciBtaW5vcml0eSBjbGFzcwp3ZWlnaHRzIDwtIGlmZWxzZShkYXRhJGNsYXNzID09IDEsIDksIDEpICAjIFJhdGlvIDkwOjEwLCBzbyBtaW5vcml0eSBnZXRzIDl4IHdlaWdodAoKIyBUcmFpbiBsb2dpc3RpYyByZWdyZXNzaW9uIHdpdGggd2VpZ2h0cwptb2RlbCA8LSBnbG0oY2xhc3MgfiB4MSArIHgyLCBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IGRhdGEsIHdlaWdodHMgPSB3ZWlnaHRzKQoKIyBNb2RlbCBzdW1tYXJ5CnN1bW1hcnkobW9kZWwpCgojIFByZWRpY3QgcHJvYmFiaWxpdGllcwpwcmVkaWN0ZWQgPC0gcHJlZGljdChtb2RlbCwgdHlwZSA9ICJyZXNwb25zZSIpCgojIEV2YWx1YXRlIHBlcmZvcm1hbmNlCmxpYnJhcnkoUk9DUikKcm9jX2N1cnZlIDwtIHByZWRpY3Rpb24ocHJlZGljdGVkLCBkYXRhJGNsYXNzKQphdWMgPC0gcGVyZm9ybWFuY2Uocm9jX2N1cnZlLCAiYXVjIilAeS52YWx1ZXNbWzFdXQpwcmludChwYXN0ZSgiQVVDIHdpdGggd2VpZ2h0czoiLCBhdWMpKQpgYGAKCk5vdGUgdGhhdCBpbiB0aGUgY29kZSBhYm92ZSwgdGhlIGB3ZWlnaHRzYCBwYXJhbWV0ZXIgYWRqdXN0cyB0aGUgaW5mbHVlbmNlIG9mIGVhY2ggY2xhc3MuIEluIHRoaXMgY2FzZSwgbWlub3JpdHkgY2xhc3MgaW5zdGFuY2VzIGFyZSBhc3NpZ25lZCBhIHdlaWdodCBvZiA5LCBhbXBsaWZ5aW5nIHRoZWlyIGltcGFjdCBvbiB0aGUgbW9kZWwuIFRoaXMgYXBwcm9hY2ggd29ya3Mgd2VsbCB3aGVuIHRoZSBkYXRhc2V0IGlzIGhpZ2hseSBpbWJhbGFuY2VkIGJ1dCByZXByZXNlbnRhdGl2ZSBvZiByZWFsLXdvcmxkIHByb3BvcnRpb25zLiBUaGUgY2hvaWNlIG9mIHdlaWdodHMgaXMgZW1waXJpY2FsIGFuZCByZXF1aXJlcyBleHBlcmltZW50YXRpb24uCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyBFeGFtcGxlIElJOiBDb3N0LVNlbnNpdGl2ZSBEZWNpc2lvbiBUcmVlcwoKQ29zdC1zZW5zaXRpdmUgbGVhcm5pbmcgZXhwbGljaXRseSBwZW5hbGl6ZXMgbWlzY2xhc3NpZmljYXRpb25zIG9mIHRoZSBtaW5vcml0eSBjbGFzcyBtb3JlIGhlYXZpbHkgdGhhbiB0aG9zZSBvZiB0aGUgbWFqb3JpdHkgY2xhc3MuIERlY2lzaW9uIHRyZWUgYWxnb3JpdGhtcywgbGlrZSAqKnJwYXJ0KiosIGFsbG93IHNwZWNpZnlpbmcgY29zdCBtYXRyaWNlcyB0byBhY2hpZXZlIHRoaXMuIFRoZSBjb2RlIGJlbG93IGlsbHVzdHJhdGVzIHRoaXMgZm9yIGEgZGVjaXNpb24gdHJlZS4KCmBgYHtyfQpsaWJyYXJ5KHJwYXJ0KQoKIyBEZWZpbmUgY29zdCBtYXRyaXg6IGhpZ2hlciBwZW5hbHR5IGZvciBtaW5vcml0eSBtaXNjbGFzc2lmaWNhdGlvbgpjb3N0X21hdHJpeCA8LSBtYXRyaXgoYygwLCAxMCwgMSwgMCksIG5yb3cgPSAyLCBieXJvdyA9IFRSVUUpCgojIFRyYWluIGEgY29zdC1zZW5zaXRpdmUgZGVjaXNpb24gdHJlZQptb2RlbCA8LSBycGFydCgKICBjbGFzcyB+IHgxICsgeDIsCiAgZGF0YSA9IGRhdGEsCiAgbWV0aG9kID0gImNsYXNzIiwKICBwYXJtcyA9IGxpc3QobG9zcyA9IGNvc3RfbWF0cml4KQopCgojIFByaW50IHRoZSB0cmVlIHN0cnVjdHVyZQpwcmludChtb2RlbCkKCiMgUHJlZGljdCBhbmQgZXZhbHVhdGUKcHJlZGljdGVkIDwtIHByZWRpY3QobW9kZWwsIHR5cGUgPSAiY2xhc3MiKQpjb25mdXNpb25fbWF0cml4IDwtIHRhYmxlKGRhdGEkY2xhc3MsIHByZWRpY3RlZCkKcHJpbnQoIkNvbmZ1c2lvbiBNYXRyaXg6IikKcHJpbnQoY29uZnVzaW9uX21hdHJpeCkKYGBgCgpPbmNlIGFnYWluLCB0aGUgd2VpZ2h0cyBhcmUgY2hvc2VuIGVtcGlyaWNhbGx5IGFuZCB0dW5lZCB0aHJvdWdoIHRyaWFsLWFuZC1lcnJvci4gVGhlIGNvc3QgbWF0cml4IHBlbmFsaXplcyBtaXNjbGFzc2lmaWNhdGlvbiBvZiB0aGUgbWlub3JpdHkgY2xhc3MgKGAxMGApIG1vcmUgaGVhdmlseSB0aGFuIHRoZSBtYWpvcml0eSBjbGFzcyAoYDFgKS4gVGhpcyBtb2RpZmljYXRpb24gZGlyZWN0bHkgaW5mbHVlbmNlcyBob3cgdGhlIGRlY2lzaW9uIHRyZWUgc3BsaXRzIGRhdGEsIHByaW9yaXRpemluZyBtaW5vcml0eSBjbGFzcyBhY2N1cmFjeS4KCiMjIyBFeGFtcGxlIElJSTogQ3VzdG9tIExvc3MgRnVuY3Rpb24gaW4gTmV1cmFsIE5ldHdvcmtzCgpJbiBkZWVwIGxlYXJuaW5nLCBjdXN0b20gbG9zcyBmdW5jdGlvbnMgY2FuIGJlIHVzZWQgdG8gYWRkcmVzcyBjbGFzcyBpbWJhbGFuY2UgYnkgYXNzaWduaW5nIGRpZmZlcmVudCBwZW5hbHRpZXMgdG8gZXJyb3JzIGJhc2VkIG9uIGNsYXNzLiBUaGUgUiBleGFtcGxlIGJlbG93IHVzZXMgYSB3ZWlnaHRlZCBsb3NzIGluICpLZXJhcy4qIFdoaWxlIG5vdCB0aGUgb25seSBwYWNrYWdlIGZvciBkZWVwIGxlYXJuaW5nIGluIFIsIHRoZSAqKmtlcmFzKiogcGFja2FnZSBpbiBSIHByb3ZpZGVzIGFuIGludGVyZmFjZSB0byB0aGUgKktlcmFzKiBkZWVwIGxlYXJuaW5nIGxpYnJhcnksIHdoaWNoIGlzIGJ1aWx0IG9uIHRvcCBvZiAqVGVuc29yRmxvdy4qIEl0IGlzIGRlc2lnbmVkIGZvciBjcmVhdGluZyBhbmQgdHJhaW5pbmcgZGVlcCBsZWFybmluZyBtb2RlbHMgaW4gYSBtb3JlIGFjY2Vzc2libGUgZm9ybSAqS2VyYXMqIGFuZCAqVGVuc29yRmxvdyogYXJlIG1vc3RseSB1c2VkIGZvciB0YXNrcyBzdWNoIGFzIGltYWdlIHJlY29nbml0aW9uLCBuYXR1cmFsIGxhbmd1YWdlIHByb2Nlc3NpbmcsIGFuZCB0aW1lLXNlcmllcyBmb3JlY2FzdGluZy4gKlRlbnNvckZsb3cqIG9wZXJhdGVzIG9uIGEgY29tcHV0YXRpb24gZ3JhcGggbW9kZWwsIHdoZXJlIG9wZXJhdGlvbnMgYXJlIHJlcHJlc2VudGVkIGFzIG5vZGVzLCBhbmQgZGF0YSBmbG93cyBiZXR3ZWVuIHRoZW0gYWxvbmcgZWRnZXMuIFRoZSBmcmFtZXdvcmsgaXMgb3B0aW1pemVkIHRvIGhhbmRsZSBsYXJnZS1zY2FsZSBudW1lcmljYWwgY29tcHV0YXRpb25zIHVzaW5nIHRoaXMgc3RydWN0dXJlLCBwYXJ0aWN1bGFybHkgZm9yIG5ldXJhbCBuZXR3b3Jrcy4gVGhlIHRyYWluaW5nIG9mIGRlZXAgbmV1cmFsIG5ldHdvcmtzIHdpdGggbWFueSBoaWRkZW4gbGF5ZXJzIGlzIGNvbXB1dGF0aW9uYWxseSB2ZXJ5IGV4cGVuc2l2ZSwgc28gdGhlIGNvZGUgYmVsb3cgbWF5IHJlcXVpcmUgc2lnbmlmaWNhbnQgdGltZSB0byBydW4gb24gdHlwaWNhbCBzeXN0ZW1zLgoKYGBge3IgZWNobz1ULCBldmFsPUZ9CmxpYnJhcnkoa2VyYXMpCgojIFByZXBhcmUgaW1iYWxhbmNlZCBkYXRhCnggPC0gYXMubWF0cml4KGRhdGFbLCBjKCJ4MSIsICJ4MiIpXSkKeSA8LSBhcy5udW1lcmljKGRhdGEkY2xhc3MpCgojIERlZmluZSBhIHdlaWdodGVkIGJpbmFyeSBjcm9zcy1lbnRyb3B5IGxvc3MKd2VpZ2h0ZWRfbG9zcyA8LSBmdW5jdGlvbih5X3RydWUsIHlfcHJlZCkgewogIHdlaWdodHMgPC0gaWZlbHNlKHlfdHJ1ZSA9PSAxLCA5LCAxKSAgIyBIaWdoZXIgd2VpZ2h0IGZvciBtaW5vcml0eSBjbGFzcwogIGtlcmFzOjprX21lYW4od2VpZ2h0cyAqIGtfYmluYXJ5X2Nyb3NzZW50cm9weSh5X3RydWUsIHlfcHJlZCksIGF4aXMgPSAtMSkKfQoKIyBCdWlsZCBhIHNpbXBsZSBuZXVyYWwgbmV0d29yawptb2RlbCA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxNiwgYWN0aXZhdGlvbiA9ICJyZWx1IiwgaW5wdXRfc2hhcGUgPSBuY29sKHgpKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDEsIGFjdGl2YXRpb24gPSAic2lnbW9pZCIpCgptb2RlbCAlPiUgY29tcGlsZSgKICBvcHRpbWl6ZXIgPSAiYWRhbSIsCiAgbG9zcyA9IHdlaWdodGVkX2xvc3MsCiAgbWV0cmljcyA9IGMoImFjY3VyYWN5IikKKQoKIyBUcmFpbiB0aGUgbW9kZWwKbW9kZWwgJT4lIGZpdCgKICB4LAogIHksCiAgZXBvY2hzID0gNTAsCiAgYmF0Y2hfc2l6ZSA9IDEwLAogIHZlcmJvc2UgPSAxCikKYGBgCgpIZXJlLCB0aGUgY3VzdG9tIGxvc3MgZnVuY3Rpb24gYXBwbGllcyBoaWdoZXIgcGVuYWx0aWVzIHRvIGVycm9ycyBvbiBtaW5vcml0eSBjbGFzcyBzYW1wbGVzLiBUaGlzIGFwcHJvYWNoIGlzIHBhcnRpY3VsYXJseSBlZmZlY3RpdmUgZm9yIG5ldXJhbCBuZXR3b3JrcyBvciBvdGhlciBhbGdvcml0aG1zIHdoZXJlIGNsYXNzIHdlaWdodHMgbWF5IG5vdCBiZSBkaXJlY3RseSBzdXBwb3J0ZWQuIEhvd2V2ZXIsIG5ldXJhbCBuZXR3b3JrcyBjYXJyeSBhIHNpZ25pZmljYW50IGNvbXB1dGF0aW9uYWwgcGVuYWx0eSBhbmQgYXJlIGNvbXB1dGF0aW9uYWxseSB2ZXJ5IGV4cGVuc2l2ZSBhbmQgdGltZS1jb25zdW1pbmcgdG8gdHJhaW4uCgpBbGdvcml0aG0tTGV2ZWwgTW9kaWZpY2F0aW9ucyBhcmUgYmVzdCB1c2VkIHdoZW4gd2hlbiBjbGFzcyBpbWJhbGFuY2UgaXMgbW9kZXJhdGUsIGFuZCB0aGUgYWxnb3JpdGhtIHN1cHBvcnRzIHdlaWdodCBhZGp1c3RtZW50cyAoKmUuZy4qLCBsb2dpc3RpYyByZWdyZXNzaW9uLCBTVk1zLCBrTk4pIG9yIHdoZW4gbWlzY2xhc3NpZmljYXRpb24gY29zdHMgdmFyeSBzaWduaWZpY2FudGx5IGJldHdlZW4gY2xhc3NlcywgZXNwZWNpYWxseSBpbiBkZWNpc2lvbiB0cmVlcyBvciBlbnNlbWJsZSBtb2RlbHMuIEluIGRlZXAgbGVhcm5pbmcgb3IgYWR2YW5jZWQgbW9kZWxzIHRoYXQgcmVxdWlyZSBmaW5lLWdyYWluZWQgY29udHJvbCBvdmVyIG9wdGltaXphdGlvbiBjdXN0b20gbG9zcyBmdW5jdGlvbnMgYXJlIGFuIG9wdGlvbi4KClRoZXNlIHRlY2huaXF1ZXMgYXJlIGVmZmVjdGl2ZSB3aGVuIHlvdSB3YW50IHRvIGtlZXAgdGhlIGRhdGFzZXQgaW50YWN0ICh3aXRob3V0IHJlc2FtcGxpbmcpIGFuZCByZWx5IG9uIHRoZSBhbGdvcml0aG0gdG8gYmFsYW5jZSB0aGUgbGVhcm5pbmcgcHJvY2Vzcy4gSW4gcHJhY3RpY2UsIGJvdGggbWV0aG9kcywgb3ZlcnNhbXBsaW5nIGFuZCBhbGdvcml0aG0gbW9kaWZpY2F0aW9uLCBjYW4gYmUgdXNlZCB0b2dldGhlci4KCiMjIFN1bW1hcnkKClRoaXMgbGVzc29uIGV4cGxvcmVkIG1ldGhvZHMgZm9yIGFkZHJlc3NpbmcgY2xhc3MgaW1iYWxhbmNlIGluIHN1cGVydmlzZWQgbWFjaGluZSBsZWFybmluZywgZm9jdXNpbmcgb24gZGF0YS1sZXZlbCByZXNhbXBsaW5nIHRlY2huaXF1ZXMgYW5kIGFsZ29yaXRobS1sZXZlbCBtb2RpZmljYXRpb25zLiBSZXNhbXBsaW5nIG1ldGhvZHMgbW9kaWZ5IHRoZSBkYXRhc2V04oCZcyBjbGFzcyBkaXN0cmlidXRpb24gdG8gYmFsYW5jZSB0aGUgcmVwcmVzZW50YXRpb24gb2YgdGhlIG1ham9yaXR5IGFuZCBtaW5vcml0eSBjbGFzc2VzLiBUd28ga2V5IHRlY2huaXF1ZXMgZGlzY3Vzc2VkIHdlcmUgU01PVEUgKFN5bnRoZXRpYyBNaW5vcml0eSBPdmVyLXNhbXBsaW5nIFRlY2huaXF1ZSkgYW5kIFJPU0UgKFJhbmRvbSBPdmVyLVNhbXBsaW5nIEV4YW1wbGVzKS4gU01PVEUgZ2VuZXJhdGVzIHN5bnRoZXRpYyBzYW1wbGVzIGZvciB0aGUgbWlub3JpdHkgY2xhc3MgYnkgaW50ZXJwb2xhdGluZyBiZXR3ZWVuIGV4aXN0aW5nIGluc3RhbmNlcyBhbmQgdGhlaXIgbmVhcmVzdCBuZWlnaGJvcnMsIG1ha2luZyBpdCBwYXJ0aWN1bGFybHkgc3VpdGFibGUgZm9yIHN0cnVjdHVyZWQgZGF0YXNldHMgd2l0aCBhIHdlbGwtZGVmaW5lZCBtaW5vcml0eSBjbGFzcy4gSW4gY29udHJhc3QsIFJPU0UgZW1wbG95cyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0aW9uIHRvIGdlbmVyYXRlIHN5bnRoZXRpYyBzYW1wbGVzIGFjcm9zcyB0aGUgZmVhdHVyZSBzcGFjZSwgbWFraW5nIGl0IGVmZmVjdGl2ZSBmb3IgaGFuZGxpbmcgbm9pc3kgb3Igb3ZlcmxhcHBpbmcgY2xhc3MgYm91bmRhcmllcy4gV2hpbGUgU01PVEUgaXMgaWRlYWwgZm9yIGVuaGFuY2luZyBkZWNpc2lvbiBib3VuZGFyaWVzLCBST1NFIG9mZmVycyB2ZXJzYXRpbGl0eSBmb3Igbm9pc3kgZGF0YXNldHMuCgpBbGdvcml0aG0tbGV2ZWwgbW9kaWZpY2F0aW9ucyBhZGRyZXNzIGNsYXNzIGltYmFsYW5jZSBieSBkaXJlY3RseSBpbmZsdWVuY2luZyB0aGUgbGVhcm5pbmcgcHJvY2Vzcy4gVGhlc2UgaW5jbHVkZSB0aGUgdXNlIG9mIGNsYXNzIHdlaWdodHMsIGNvc3Qtc2Vuc2l0aXZlIGxlYXJuaW5nLCBhbmQgY3VzdG9tIGxvc3MgZnVuY3Rpb25zLiBDbGFzcyB3ZWlnaHRzLCBhcyBzaG93biBpbiBhIHdlaWdodGVkIGxvZ2lzdGljIHJlZ3Jlc3Npb24gZXhhbXBsZSwgYXNzaWduIGhpZ2hlciBpbXBvcnRhbmNlIHRvIG1pbm9yaXR5IGNsYXNzIHNhbXBsZXMsIGVuc3VyaW5nIHRoZXkgaGF2ZSBhIGdyZWF0ZXIgaW1wYWN0IGR1cmluZyBtb2RlbCB0cmFpbmluZy4gQ29zdC1zZW5zaXRpdmUgbGVhcm5pbmcsIHN1Y2ggYXMgd2l0aCBjb3N0LXNlbnNpdGl2ZSBkZWNpc2lvbiB0cmVlcywgcGVuYWxpemVzIG1pc2NsYXNzaWZpY2F0aW9ucyBvZiB0aGUgbWlub3JpdHkgY2xhc3MgbW9yZSBoZWF2aWx5LCBlZmZlY3RpdmVseSBndWlkaW5nIHRoZSBhbGdvcml0aG0gdG8gcHJpb3JpdGl6ZSB0aGVzZSBjYXNlcy4gQ3VzdG9tIGxvc3MgZnVuY3Rpb25zLCBvZnRlbiBhcHBsaWVkIGluIGRlZXAgbGVhcm5pbmcsIGFsbG93IHByZWNpc2UgY29udHJvbCBvdmVyIHRoZSB0cmFpbmluZyBwcm9jZXNzIGJ5IGludHJvZHVjaW5nIHBlbmFsdGllcyB0YWlsb3JlZCB0byB0aGUgZGF0YXNldOKAmXMgbmVlZHMsIHN1Y2ggYXMgd2VpZ2h0ZWQgYmluYXJ5IGNyb3NzLWVudHJvcHkuIFRoZXNlIGFsZ29yaXRobWljIGFwcHJvYWNoZXMgYXJlIHBhcnRpY3VsYXJseSB1c2VmdWwgd2hlbiB0aGUgZGF0YXNldCBpdHNlbGYgc2hvdWxkIHJlbWFpbiB1bmFsdGVyZWQsIGFuZCB0aGUgaW1iYWxhbmNlIGNhbiBiZSBhZGRyZXNzZWQgdGhyb3VnaCBhZGp1c3RtZW50cyB0byB0aGUgbGVhcm5pbmcgZnJhbWV3b3JrLgoKSW4gc3VtbWFyeSwgcmVzYW1wbGluZyBtZXRob2RzIGxpa2UgU01PVEUgYW5kIFJPU0UgYXJlIGJlc3QgdXNlZCB3aGVuIG1vZGlmeWluZyB0aGUgZGF0YXNldCBpcyBmZWFzaWJsZSwgd2l0aCBTTU9URSBiZWluZyBwcmVmZXJyZWQgZm9yIHdlbGwtZGVmaW5lZCBtaW5vcml0eSBjbGFzc2VzIGFuZCBST1NFIGZvciBub2lzeSBvciBzcGFyc2UgZGF0YXNldHMuIEFsZ29yaXRobS1sZXZlbCBtb2RpZmljYXRpb25zLCBpbmNsdWRpbmcgY2xhc3Mgd2VpZ2h0cywgY29zdCBtYXRyaWNlcywgYW5kIGN1c3RvbSBsb3NzIGZ1bmN0aW9ucywgYXJlIHByZWZlcmFibGUgd2hlbiB0aGUgbGVhcm5pbmcgcHJvY2VzcyByZXF1aXJlcyBtb3JlIG51YW5jZWQgYWRqdXN0bWVudHMuIFRvZ2V0aGVyLCB0aGVzZSBzdHJhdGVnaWVzIHByb3ZpZGUgYSByb2J1c3QgdG9vbGtpdCBmb3IgaGFuZGxpbmcgY2xhc3MgaW1iYWxhbmNlLCBlbnN1cmluZyBiZXR0ZXIgcGVyZm9ybWFuY2UgYW5kIGZhaXIgcmVwcmVzZW50YXRpb24gb2YgbWlub3JpdHkgY2xhc3NlcyBpbiBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscy4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgRmlsZXMgJiBSZXNvdXJjZXMKCmBgYHtyIHppcEZpbGVzLCBlY2hvPUZBTFNFfQp6aXBOYW1lID0gc3ByaW50ZigiTGVzc29uRmlsZXMtJXMtJXMuemlwIiwgCiAgICAgICAgICAgICAgICAgcGFyYW1zJGNhdGVnb3J5LAogICAgICAgICAgICAgICAgIHBhcmFtcyRudW1iZXIpCgp0ZXh0QUxpbmsgPSBwYXN0ZTAoIkFsbCBGaWxlcyBmb3IgTGVzc29uICIsIAogICAgICAgICAgICAgICBwYXJhbXMkY2F0ZWdvcnksIi4iLHBhcmFtcyRudW1iZXIpCgojIGRvd25sb2FkRmlsZXNMaW5rKCkgaXMgaW5jbHVkZWQgZnJvbSBfaW5zZXJ0MkRCLlIKa25pdHI6OnJhd19odG1sKGRvd25sb2FkRmlsZXNMaW5rKCIuIiwgemlwTmFtZSwgdGV4dEFMaW5rKSkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIFJlZmVyZW5jZXMKCk5vbmUgeWV0LgoKIyMgRXJyYXRhCgpbTGV0IHVzIGtub3ddKGh0dHBzOi8vZm9ybS5qb3Rmb3JtLmNvbS8yMTIxODcwNzI3ODQxNTcpe3RhcmdldD0iX2JsYW5rIn0uCg==