Logistic Regression Study Guide

Key Concepts

1. Categorical Data

  • Definition: Data based on classes (e.g., red/green/blue, true/false).
  • Transformation: Use one-hot encoding to convert non-numeric data into numeric format.
    • Example: Red = [1, 0, 0], Green = [0, 1, 0], Blue = [0, 0, 1].
    • Each category is represented by a binary vector.
  • Discrete Data: Numeric values without fractional parts (e.g., number of rooms, children).
    • Do not one-hot encode discrete data; treat it as numeric.

2. Linear to Logistic Regression

  • Linear regression predicts continuous values, but logistic regression is used for categorical targets.
  • Logistic regression transforms linear outputs into probabilities using the sigmoid function.

3. The Sigmoid Function

  • Formula: \(\sigma(x) = \frac{1}{1 + e^{-x}}\)
  • Squeezes input values into the range (0, 1), representing probabilities.
  • Default classification threshold is 0.5 but can be adjusted based on the use case (e.g., fraud detection).

4. Log Loss (Logarithmic Loss)

  • Measures the distance between predicted probabilities and actual binary targets.
  • Formula:
    \(\text{Log Loss} = -\frac{1}{N} \sum_{i=1}^N \left[ y_i \log(p_i) + (1-y_i) \log(1-p_i) \right]\)
    • \(y_i\): Actual class (0 or 1).
    • \(p_i\): Predicted probability for class 1.
  • Penalizes predictions farther from the actual target.

5. Minimizing Log Loss

  • Uses gradient descent to adjust weights (\(m\)) and minimize loss.
  • Partial derivatives of log loss guide weight updates: \(m_{new} = m_{old} - \alpha \frac{\partial J}{\partial m}\), where \(J\) is the log loss and \(\alpha\) is the learning rate.

6. Multiclass Classification

  • Extends binary logistic regression to multiple categories.
  • Two approaches:
    • One-vs-All (OvA): Train a separate classifier for each class.
    • One-vs-One (OvO): Compare every pair of classes; assign the class with the most wins.
  • Use libraries (e.g., sklearn) for built-in multiclass implementation.

Demonstration of Logistic Regression

Part I: Binary Classification (Breast Cancer Dataset)

Steps:

  1. Data Preparation:
    • Import dataset from sklearn.datasets.
    • Convert data to a DataFrame and add column names.
    • Separate features (X) and targets (y).
  2. Logistic Regression:
    • Use LogisticRegressionCV for cross-validation.
    • Fit the model: model.fit(X, y).
    • Retrieve model coefficients with model.coef_.
  3. Cross-Validation:
    • For unbiased performance metrics, use cross_val_score.

    • Example:

      from sklearn.model_selection import cross_val_score
      scores = cross_val_score(model, X, y, scoring='accuracy', cv=5)
      print("Accuracy:", scores.mean())

Part II: Cross-Validation and Regularization

  • Use LogisticRegressionCV to optimize regularization parameters.
  • Interpret the results and evaluate metrics.

Case Study: Hospital Readmission Prediction

Problem Overview

  • Predict patient readmission within 30 days using logistic regression.
  • Challenges:
    • Missing data must be imputed.
    • Ethical considerations in using sensitive features (e.g., race).
    • Imbalanced classes.

Assignment Steps:

  1. Data Preprocessing:
    • Handle missing data using imputation techniques (e.g., mean, median).
    • Normalize/scale features for better model performance.
  2. Model Development:
    • Build a logistic regression model for each target class (multiclass setup).
    • Use cross-validation to evaluate performance.
  3. Feature Importance:
    • Analyze the top 5 important features contributing to predictions using model coefficients.

Mathematical and Coding Representations

Mathematical Representation

  1. Sigmoid Function:
    \(\sigma(x) = \frac{1}{1 + e^{-x}}\)
  2. Log Loss:
    \(J = -\frac{1}{N} \sum_{i=1}^N \left[ y_i \log(p_i) + (1-y_i) \log(1-p_i) \right]\)
  3. Gradient Update:
    \(m_{new} = m_{old} - \alpha \frac{\partial J}{\partial m}\)

Python Code Representation

  1. One-Hot Encoding:

    import pandas as pd
    from sklearn.preprocessing import OneHotEncoder
    
    data = {'Color': ['Red', 'Blue', 'Green']}
    df = pd.DataFrame(data)
    encoder = OneHotEncoder()
    encoded = encoder.fit_transform(df[['Color']]).toarray()
    print(encoded)
  2. Logistic Regression:

    from sklearn.datasets import load_breast_cancer
    from sklearn.model_selection import train_test_split
    from sklearn.linear_model import LogisticRegressionCV
    
    # Load data
    data = load_breast_cancer()
    X, y = data.data, data.target
    
    # Split data
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
    
    # Logistic Regression with CV
    model = LogisticRegressionCV(cv=5, max_iter=1000).fit(X_train, y_train)
    print("Accuracy:", model.score(X_test, y_test))
  3. Cross-Validation:

    from sklearn.model_selection import cross_val_score
    
    scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')
    print("Cross-Validation Accuracy:", scores.mean())

Visualization of Sigmoid Function

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-10, 10, 100)
sigmoid = 1 / (1 + np.exp(-x))

plt.plot(x, sigmoid)
plt.title('Sigmoid Function')
plt.xlabel('x')
plt.ylabel('Sigmoid(x)')
plt.grid()
plt.show()

Study Guide: Logistic Regression with Mathematical and Coding Representations


1. Categorical Data

  • Definition: Data representing classes or categories (e.g., red, green, blue; true/false).
  • Challenges: Categorical data must be transformed into numerical data to be used in most machine learning models.
  • Transformation:
    • One-Hot Encoding: Converts categorical features into binary columns, where each unique value becomes a column. E.g., for colors red, green, blue:
      • Red: [1, 0, 0]
      • Green: [0, 1, 0]
      • Blue: [0, 0, 1]

Python Example:

import pandas as pd

data = {'Color': ['Red', 'Green', 'Blue']}
df = pd.DataFrame(data)
df_encoded = pd.get_dummies(df, columns=['Color'])

Mathematical Representation: If \(C\) represents the categories and \(x\) is the input, the transformation is: \[ \text{One-hot encoded vector: } \mathbf{x}_{\text{encoded}} = [x_1, x_2, \ldots, x_n], \text{where } x_i = 1 \text{ if } x = C_i, \text{ else } 0. \]


2. Linear to Logistic Regression

  • Linear regression predicts continuous values. Logistic regression transforms this to predict probabilities for binary outcomes.
  • Key Equation: \[ z = \mathbf{w}^T\mathbf{x} + b, \quad p = \sigma(z), \quad \sigma(z) = \frac{1}{1 + e^{-z}} \] Where \(\sigma(z)\) (sigmoid function) squashes \(z\) to range [0, 1].

Python Example:

from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
model.fit(X_train, y_train)

3. The Sigmoid Function

  • Equation: \(\sigma(z) = \frac{1}{1 + e^{-z}}\)
  • Properties:
    • \(z \to +\infty\): \(\sigma(z) \to 1\)
    • \(z \to -\infty\): \(\sigma(z) \to 0\)
    • Outputs probabilities.

Python Example:

import numpy as np

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

z = np.linspace(-10, 10, 100)
sigmoid_values = sigmoid(z)

4. Log Loss

  • Measures the distance between predicted probabilities and actual class labels.
  • Equation: \[ \text{Log Loss: } L(y, \hat{y}) = -\frac{1}{n} \sum_{i=1}^{n} \left[ y_i \log(\hat{y}_i) + (1 - y_i) \log(1 - \hat{y}_i) \right] \] Where:
    • \(y_i\): Actual label.
    • \(\hat{y}_i\): Predicted probability.

Python Example:

from sklearn.metrics import log_loss
loss = log_loss(y_true, y_pred)

5. Minimizing Log Loss

  • Achieved through optimization (e.g., gradient descent).
  • Gradient Descent:
    • Update rule: \(w = w - \alpha \nabla L(w)\), where \(\alpha\) is the learning rate.
  • Python Example:
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(max_iter=100)
model.fit(X_train, y_train)

6. Multiclass Classification

  • Extends binary logistic regression to multiple classes.
  • Approaches:
    • One-vs-Rest (OvR): Train separate classifiers for each class.
    • Softmax Regression: Directly models all classes using probabilities.
  • Softmax Function: \[ \sigma(z_j) = \frac{e^{z_j}}{\sum_{k=1}^K e^{z_k}} \]

Python Example:

from sklearn.linear_model import LogisticRegression
model = LogisticRegression(multi_class='multinomial', solver='lbfgs')
model.fit(X_train, y_train)

Case Study: Predicting Diabetes Readmission

Objective

  • Predict hospital readmission within 30 days using logistic regression.
  • Handle missing data via imputation.

Steps:

  1. Data Preprocessing:
    • Handle missing values (e.g., mean/mode imputation).
    • Encode categorical variables (e.g., one-hot encoding).
  2. Model Building:
    • Train logistic regression for three classes of readmission:
      • No readmission.
      • Readmission in less than 30 days.
      • Readmission in more than 30 days.
    • Evaluate using metrics like log loss or accuracy.
  3. Feature Importance:
    • Extract coefficients to identify top 5 predictive features.

Python Code:

from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split

# Data preprocessing
imputer = SimpleImputer(strategy='mean')
X = imputer.fit_transform(X)

# Train-test split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

# Logistic regression model
model = LogisticRegression(multi_class='multinomial', solver='lbfgs', max_iter=500)
model.fit(X_train, y_train)

# Predictions
y_pred = model.predict(X_test)

# Feature importance
importance = model.coef_
print("Top 5 Features:", importance.argsort()[-5:])

# Accuracy
accuracy = accuracy_score(y_test, y_pred)
print(f"Model Accuracy: {accuracy}")

Variable Importance Interpretation

  • Coefficients reflect the importance of each feature.
  • Analyze top variables to derive insights for readmission patterns.

Assignment

  • Train and evaluate logistic regression on the diabetes dataset.
  • Report top 5 features and their significance.
  • Submit results with a clear explanation of findings.

Deliverable: - Code file (e.g., FirstName_LastName_LogReg_Assignment.py). - Written report on model performance and feature importance.

Key Takeaways

  1. Logistic Regression Bridges Linear Models and Probabilistic Predictions:

    • Logistic regression extends linear regression for categorical target variables by employing the sigmoid function to output probabilities. This allows for binary or multiclass predictions by interpreting probabilities as class memberships.
  2. Log Loss as a Metric:

    • Unlike linear regression’s mean squared error, logistic regression uses log loss to measure the model’s performance. It penalizes predictions further from the true class, ensuring probabilities are accurate.
  3. Handling Multiclass Problems:

    • Multiclass classification can be addressed using methods like one-vs-rest (OvR) or softmax regression. While OvR is computationally efficient for a few classes, it scales poorly with many classes due to class imbalance issues.
  4. Importance of Sigmoid Function in Logistic Regression: The sigmoid function transforms the linear regression output into probabilities, making it possible to handle binary classification tasks effectively. This function ensures that predictions fall within the range [0, 1], which can then be used to classify data into distinct categories.

  5. Log Loss for Classification: Logarithmic loss (log loss) quantifies the error in probabilistic predictions by penalizing predictions that deviate significantly from the true labels. It forms a convex function with a well-defined minimum, facilitating optimization and convergence.

  6. Multiclass Classification Challenges: Logistic regression can be extended to multiclass problems through techniques like One-vs-All and One-vs-One. While these methods allow logistic regression to handle more than two classes, they come with scalability and bias challenges as the number of classes increases.


Questions for Class Discussion

  1. Threshold Adjustment in Logistic Regression:

    • How can adjusting the classification threshold (e.g., moving it from 0.5 to 0.2 for fraud detection) impact the balance between false positives and false negatives? What real-world examples highlight the importance of this?
  2. Log Loss vs. Accuracy:

    • In what scenarios might log loss be a more appropriate evaluation metric than accuracy? Can you provide examples where accuracy might be misleading?
  3. Ethical Considerations in Categorical Variables:

    • When including sensitive variables like race or gender in logistic regression, how can we ensure the model is ethically sound and avoids discriminatory practices while leveraging potentially predictive information?
  4. On Sigmoid Threshold Adjustment: In real-world applications like fraud detection, how do we decide on the optimal threshold for classification beyond the default value of 0.5? What strategies or metrics should guide this decision?

  5. On Handling Missing Data: When dealing with missing values in a dataset, as mentioned in the diabetes case study, what factors should influence the choice of an imputation strategy? How do we ensure the imputation does not introduce bias into the model?

  6. On Ethical Considerations in Feature Selection: In cases where sensitive features like race are included in the dataset, how do we balance the potential utility of such features with ethical concerns and the risk of perpetuating bias in predictions?

Best Takeaways

  1. Logistic Regression Bridges Linear Models and Probabilistic Predictions:
    • Logistic regression extends linear regression for categorical target variables by employing the sigmoid function to output probabilities. This allows for binary or multiclass predictions by interpreting probabilities as class memberships.
  2. Log Loss for Classification:
    • Logarithmic loss (log loss) quantifies the error in probabilistic predictions by penalizing predictions that deviate significantly from the true labels. It forms a convex function with a well-defined minimum, facilitating optimization and convergence.

Best Questions for Class Discussion

  1. Threshold Adjustment in Logistic Regression:
    • How can adjusting the classification threshold (e.g., moving it from 0.5 to 0.2 for fraud detection) impact the balance between false positives and false negatives? What real-world examples highlight the importance of this?
  2. Ethical Considerations in Categorical Variables:
    • When including sensitive variables like race or gender in logistic regression, how can we ensure the model is ethically sound and avoids discriminatory practices while leveraging potentially predictive information?
  3. The correct answer is:

Discrete data has specific values that can be numeric, while categorical should always be one hot encoded.

Explanation:

  • Categorical Data: Represents categories or classes without an inherent order (e.g., colors, types of animals). It must be one-hot encoded for numerical representation in most machine learning models.
  • Discrete Data: Represents countable, distinct values that are numeric in nature (e.g., number of rooms in a house, number of cars a family owns). Discrete data typically does not require one-hot encoding and can often be left as is.

Why Not the Other Choices?

  1. “Only discrete data should be one-hot encoded”: Incorrect. Discrete data is usually numeric and does not need one-hot encoding, while categorical data typically does.

  2. “Categorical data cannot be used in regression”: Incorrect. Categorical data can be used in regression after proper encoding.

  3. “Categorical data can have multiple values, while discrete data only have two”: Incorrect. Both categorical and discrete data can have multiple values.

The correct answer is:

Discrete data has specific values that can be numeric, while categorical should always be one hot encoded.

Explanation:

  • Categorical data: Represents labels or classes that do not have an inherent numeric order (e.g., “red”, “blue”, “green”). These must be one-hot encoded to be used in machine learning models.
  • Discrete data: Represents numeric values that take on specific, distinct numbers (e.g., the number of rooms, cars owned). Discrete data does not need one-hot encoding because the numeric values are meaningful and preserve order.
  1. The correct answer is:

Is the same as linear regression

Explanation:

  • Slope Update Rule: The slope update rule in logistic regression is mathematically similar to linear regression. The key difference lies in how the error is calculated due to the use of the sigmoid function and log loss. However, the gradient descent procedure (used to minimize the loss function) remains the same.

Why Not the Other Choices?

  1. “Involves the sigmoid”: While the sigmoid function is integral to logistic regression, it is part of the prediction mechanism, not the slope update rule itself.

  2. “Uses cross-entropy loss”: Logistic regression can be associated with cross-entropy loss in multiclass classification, but this isn’t specific to the slope update rule.

  3. “Uses the log loss”: Log loss is the cost function minimized in logistic regression, but it is not the slope update rule itself. The rule is derived using the gradient of the log loss.

The correct answer is:

Is the same as linear regression

Explanation:

  • The slope update rule for logistic regression is mathematically the same as in linear regression because it is derived using gradient descent. The update rule adjusts weights (slopes) iteratively to minimize the loss function.

Why the other options are incorrect:

  1. Involves the sigmoid: The sigmoid function is used to transform outputs into probabilities, but it is not directly part of the slope update rule.

  2. Uses cross-entropy loss: While cross-entropy loss (log loss) is the loss function for logistic regression, it is separate from the actual slope update rule.

  3. Uses the log loss: Log loss is minimized during training, but the slope update rule itself remains identical to that in linear regression.

  4. The correct answer is:

Can produce negative outputs


Explanation:

  • Outputs always between 0 and 1: True. The sigmoid function \(\sigma(z) = \frac{1}{1 + e^{-z}}\) always produces outputs in the range \((0, 1)\).
  • Can take negative inputs: True. The sigmoid function accepts any real number \(z\), including negative values.
  • A simulated step function: True. For very large positive or negative inputs, the sigmoid approximates a step function with outputs close to 1 or 0, respectively.
  • Can produce negative outputs: False. By definition, the sigmoid function only outputs values between 0 and 1, so it cannot produce negative outputs.

The correct answer is:

Can produce negative outputs

Explanation:

The sigmoid function has the following properties: 1. Outputs always between 0 and 1: The sigmoid function maps all inputs to values within this range. 2. Can take negative inputs: The function accepts any real number as input, including negative values. 3. A simulated step function: For very large positive or negative inputs, the sigmoid function behaves like a step function.

However: - It cannot produce negative outputs, as its range is strictly between 0 and 1.

  1. The correct answer is:

Log loss has two terms.


Explanation:

  1. Log loss:
    • Used in classification tasks, especially for logistic regression.
    • It is defined as:
      \[ \text{Log Loss: } -\frac{1}{n} \sum_{i=1}^{n} \left[ y_i \log(\hat{y}_i) + (1 - y_i) \log(1 - \hat{y}_i) \right] \]
    • The formula has two terms, one for \(y_i = 1\) and one for \(y_i = 0\), to handle the binary nature of the classification problem.
  2. Mean Squared Error (MSE) and Mean Absolute Error (MAE):
    • Used in regression tasks for continuous outputs.
    • Both have a single term that measures the difference between the predicted value and the actual value:
      • MSE: \(\frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2\)
      • MAE: \(\frac{1}{n} \sum_{i=1}^{n} |y_i - \hat{y}_i|\)

Why Not the Other Choices?

  1. Log loss is not continuous: False. Log loss is a continuous function of its inputs.
  2. Log loss is not convex: False. Log loss is a convex function, which ensures optimization algorithms converge to a global minimum.
  3. Log loss is not differentiable: False. Log loss is differentiable, and gradient descent relies on this property for optimization.

The correct answer is:

Log loss has two terms.

Explanation:

  1. Log loss:
    • Specifically designed for classification problems, particularly binary classification.
    • It has two terms, one for when the actual class is 1 and another for when the actual class is 0.
    • These terms correspond to the negative log of predicted probabilities for the correct class.
  2. Why the other options are incorrect:
    • Log loss is not continuous: Incorrect. Log loss is a continuous function.
    • Log loss is not convex: Incorrect. Log loss is convex, which ensures that gradient descent can find the global minimum.
    • Log loss is not differentiable: Incorrect. Log loss is differentiable, allowing for optimization using gradient descent.

Key Difference with MSE/MAE:

  • Mean squared error (MSE) and mean absolute error (MAE) measure the distance between predictions and targets for regression tasks.
  • Log loss measures the probability assigned to the correct class for classification tasks. It penalizes incorrect predictions more heavily as the predicted probability moves further away from the true label.
  1. The correct answer is:

We can use the one vs. rest method.


Explanation:

  1. One-vs-Rest Method:
    • In multiclass classification with logistic regression, the one-vs-rest (OvR) approach trains a separate binary classifier for each class.
    • For a class \(C_i\), the model predicts whether a data point belongs to \(C_i\) (1) or not (0).
    • During inference, the model chooses the class with the highest probability.
  2. One-vs-One Method:
    • While the one-vs-one (OvO) method is used in some classification tasks, it is not directly associated with logistic regression using the sigmoid function. OvO involves creating a binary classifier for each pair of classes, which can become computationally expensive as the number of classes increases.
  3. Multiclass is impossible with log loss:
    • This is false. Multiclass logistic regression can use a generalization of log loss (e.g., softmax cross-entropy) to handle multiple classes directly.

Multiclass with Logistic Regression:

For a direct multiclass approach (e.g., softmax regression):
- Use the softmax function instead of the sigmoid function, which generalizes probability assignment to \(K\) classes.
- The log loss in this case is adapted for multiclass problems.

The correct answer is:

We can use the one vs. one method.
We can use the one vs. rest method.

Explanation:

  1. One-vs-Rest (One-vs-All):
    • For each class, a separate classifier is trained to distinguish that class from all other classes.
    • The class with the highest probability is selected as the predicted class.
    • Example: For classes Red, Green, and Blue:
      • Red vs. Not Red
      • Green vs. Not Green
      • Blue vs. Not Blue
  2. One-vs-One:
    • A classifier is trained for every pair of classes.
    • Each pairwise comparison predicts a winner, and the class with the most wins is selected.
    • Example: For classes Red, Green, and Blue:
      • Red vs. Green
      • Red vs. Blue
      • Green vs. Blue
  3. Why “Multiclass is impossible with log loss” is incorrect:
    • Log loss can be extended to multiclass problems using softmax instead of sigmoid. This is common in frameworks like neural networks.

Summary:

Multiclass problems can be handled with sigmoid-based methods like One-vs-Rest or One-vs-One, although other techniques (e.g., softmax) are often more efficient for larger class sizes.

LS0tDQp0aXRsZTogIjczMzMzIE1vZHVsZSAzIFRyYXNuY3JpcHQgYW5kIFN1bW1hcmllcyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMgTG9naXN0aWMgUmVncmVzc2lvbiBTdHVkeSBHdWlkZQ0KDQojIyBLZXkgQ29uY2VwdHMNCg0KIyMjIDEuIENhdGVnb3JpY2FsIERhdGENCi0gKipEZWZpbml0aW9uKio6IERhdGEgYmFzZWQgb24gY2xhc3NlcyAoZS5nLiwgcmVkL2dyZWVuL2JsdWUsIHRydWUvZmFsc2UpLg0KLSAqKlRyYW5zZm9ybWF0aW9uKio6IFVzZSAqKm9uZS1ob3QgZW5jb2RpbmcqKiB0byBjb252ZXJ0IG5vbi1udW1lcmljIGRhdGEgaW50byBudW1lcmljIGZvcm1hdC4NCiAgLSBFeGFtcGxlOiBSZWQgPSBbMSwgMCwgMF0sIEdyZWVuID0gWzAsIDEsIDBdLCBCbHVlID0gWzAsIDAsIDFdLg0KICAtIEVhY2ggY2F0ZWdvcnkgaXMgcmVwcmVzZW50ZWQgYnkgYSBiaW5hcnkgdmVjdG9yLg0KLSAqKkRpc2NyZXRlIERhdGEqKjogTnVtZXJpYyB2YWx1ZXMgd2l0aG91dCBmcmFjdGlvbmFsIHBhcnRzIChlLmcuLCBudW1iZXIgb2Ygcm9vbXMsIGNoaWxkcmVuKS4NCiAgLSAqKkRvIG5vdCBvbmUtaG90IGVuY29kZSBkaXNjcmV0ZSBkYXRhKio7IHRyZWF0IGl0IGFzIG51bWVyaWMuDQoNCiMjIyAyLiBMaW5lYXIgdG8gTG9naXN0aWMgUmVncmVzc2lvbg0KLSBMaW5lYXIgcmVncmVzc2lvbiBwcmVkaWN0cyBjb250aW51b3VzIHZhbHVlcywgYnV0IGxvZ2lzdGljIHJlZ3Jlc3Npb24gaXMgdXNlZCBmb3IgY2F0ZWdvcmljYWwgdGFyZ2V0cy4NCi0gTG9naXN0aWMgcmVncmVzc2lvbiB0cmFuc2Zvcm1zIGxpbmVhciBvdXRwdXRzIGludG8gcHJvYmFiaWxpdGllcyB1c2luZyB0aGUgKipzaWdtb2lkIGZ1bmN0aW9uKiouDQoNCiMjIyAzLiBUaGUgU2lnbW9pZCBGdW5jdGlvbg0KLSAqKkZvcm11bGEqKjogXCggXHNpZ21hKHgpID0gXGZyYWN7MX17MSArIGVeey14fX0gXCkNCi0gU3F1ZWV6ZXMgaW5wdXQgdmFsdWVzIGludG8gdGhlIHJhbmdlICgwLCAxKSwgcmVwcmVzZW50aW5nIHByb2JhYmlsaXRpZXMuDQotIERlZmF1bHQgY2xhc3NpZmljYXRpb24gdGhyZXNob2xkIGlzIDAuNSBidXQgY2FuIGJlIGFkanVzdGVkIGJhc2VkIG9uIHRoZSB1c2UgY2FzZSAoZS5nLiwgZnJhdWQgZGV0ZWN0aW9uKS4NCg0KIyMjIDQuIExvZyBMb3NzIChMb2dhcml0aG1pYyBMb3NzKQ0KLSBNZWFzdXJlcyB0aGUgZGlzdGFuY2UgYmV0d2VlbiBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBhbmQgYWN0dWFsIGJpbmFyeSB0YXJnZXRzLg0KLSAqKkZvcm11bGEqKjogIA0KICBcKCBcdGV4dHtMb2cgTG9zc30gPSAtXGZyYWN7MX17Tn0gXHN1bV97aT0xfV5OIFxsZWZ0WyB5X2kgXGxvZyhwX2kpICsgKDEteV9pKSBcbG9nKDEtcF9pKSBccmlnaHRdIFwpICANCiAgLSBcKCB5X2kgXCk6IEFjdHVhbCBjbGFzcyAoMCBvciAxKS4NCiAgLSBcKCBwX2kgXCk6IFByZWRpY3RlZCBwcm9iYWJpbGl0eSBmb3IgY2xhc3MgMS4NCi0gUGVuYWxpemVzIHByZWRpY3Rpb25zIGZhcnRoZXIgZnJvbSB0aGUgYWN0dWFsIHRhcmdldC4NCg0KIyMjIDUuIE1pbmltaXppbmcgTG9nIExvc3MNCi0gVXNlcyBncmFkaWVudCBkZXNjZW50IHRvIGFkanVzdCB3ZWlnaHRzIChcKG1cKSkgYW5kIG1pbmltaXplIGxvc3MuDQotIFBhcnRpYWwgZGVyaXZhdGl2ZXMgb2YgbG9nIGxvc3MgZ3VpZGUgd2VpZ2h0IHVwZGF0ZXM6DQogIFwoIG1fe25ld30gPSBtX3tvbGR9IC0gXGFscGhhIFxmcmFje1xwYXJ0aWFsIEp9e1xwYXJ0aWFsIG19IFwpLA0KICB3aGVyZSBcKCBKIFwpIGlzIHRoZSBsb2cgbG9zcyBhbmQgXCggXGFscGhhIFwpIGlzIHRoZSBsZWFybmluZyByYXRlLg0KDQojIyMgNi4gTXVsdGljbGFzcyBDbGFzc2lmaWNhdGlvbg0KLSBFeHRlbmRzIGJpbmFyeSBsb2dpc3RpYyByZWdyZXNzaW9uIHRvIG11bHRpcGxlIGNhdGVnb3JpZXMuDQotIFR3byBhcHByb2FjaGVzOg0KICAtICoqT25lLXZzLUFsbCAoT3ZBKSoqOiBUcmFpbiBhIHNlcGFyYXRlIGNsYXNzaWZpZXIgZm9yIGVhY2ggY2xhc3MuDQogIC0gKipPbmUtdnMtT25lIChPdk8pKio6IENvbXBhcmUgZXZlcnkgcGFpciBvZiBjbGFzc2VzOyBhc3NpZ24gdGhlIGNsYXNzIHdpdGggdGhlIG1vc3Qgd2lucy4NCi0gVXNlIGxpYnJhcmllcyAoZS5nLiwgYHNrbGVhcm5gKSBmb3IgYnVpbHQtaW4gbXVsdGljbGFzcyBpbXBsZW1lbnRhdGlvbi4NCg0KLS0tDQoNCiMjIERlbW9uc3RyYXRpb24gb2YgTG9naXN0aWMgUmVncmVzc2lvbg0KDQojIyMgUGFydCBJOiBCaW5hcnkgQ2xhc3NpZmljYXRpb24gKEJyZWFzdCBDYW5jZXIgRGF0YXNldCkNCiMjIyMgU3RlcHM6DQoxLiAqKkRhdGEgUHJlcGFyYXRpb24qKjoNCiAgIC0gSW1wb3J0IGRhdGFzZXQgZnJvbSBgc2tsZWFybi5kYXRhc2V0c2AuDQogICAtIENvbnZlcnQgZGF0YSB0byBhIERhdGFGcmFtZSBhbmQgYWRkIGNvbHVtbiBuYW1lcy4NCiAgIC0gU2VwYXJhdGUgZmVhdHVyZXMgKFgpIGFuZCB0YXJnZXRzICh5KS4NCjIuICoqTG9naXN0aWMgUmVncmVzc2lvbioqOg0KICAgLSBVc2UgYExvZ2lzdGljUmVncmVzc2lvbkNWYCBmb3IgY3Jvc3MtdmFsaWRhdGlvbi4NCiAgIC0gRml0IHRoZSBtb2RlbDogYG1vZGVsLmZpdChYLCB5KWAuDQogICAtIFJldHJpZXZlIG1vZGVsIGNvZWZmaWNpZW50cyB3aXRoIGBtb2RlbC5jb2VmX2AuDQoNCjMuICoqQ3Jvc3MtVmFsaWRhdGlvbioqOg0KICAgLSBGb3IgdW5iaWFzZWQgcGVyZm9ybWFuY2UgbWV0cmljcywgdXNlIGBjcm9zc192YWxfc2NvcmVgLg0KICAgLSBFeGFtcGxlOiAgDQogICAgIGBgYHB5dGhvbg0KICAgICBmcm9tIHNrbGVhcm4ubW9kZWxfc2VsZWN0aW9uIGltcG9ydCBjcm9zc192YWxfc2NvcmUNCiAgICAgc2NvcmVzID0gY3Jvc3NfdmFsX3Njb3JlKG1vZGVsLCBYLCB5LCBzY29yaW5nPSdhY2N1cmFjeScsIGN2PTUpDQogICAgIHByaW50KCJBY2N1cmFjeToiLCBzY29yZXMubWVhbigpKQ0KICAgICBgYGANCg0KIyMjIFBhcnQgSUk6IENyb3NzLVZhbGlkYXRpb24gYW5kIFJlZ3VsYXJpemF0aW9uDQotIFVzZSBgTG9naXN0aWNSZWdyZXNzaW9uQ1ZgIHRvIG9wdGltaXplIHJlZ3VsYXJpemF0aW9uIHBhcmFtZXRlcnMuDQotIEludGVycHJldCB0aGUgcmVzdWx0cyBhbmQgZXZhbHVhdGUgbWV0cmljcy4NCg0KLS0tDQoNCiMjIENhc2UgU3R1ZHk6IEhvc3BpdGFsIFJlYWRtaXNzaW9uIFByZWRpY3Rpb24NCg0KIyMjIFByb2JsZW0gT3ZlcnZpZXcNCi0gUHJlZGljdCBwYXRpZW50IHJlYWRtaXNzaW9uIHdpdGhpbiAzMCBkYXlzIHVzaW5nIGxvZ2lzdGljIHJlZ3Jlc3Npb24uDQotICoqQ2hhbGxlbmdlcyoqOg0KICAtIE1pc3NpbmcgZGF0YSBtdXN0IGJlIGltcHV0ZWQuDQogIC0gRXRoaWNhbCBjb25zaWRlcmF0aW9ucyBpbiB1c2luZyBzZW5zaXRpdmUgZmVhdHVyZXMgKGUuZy4sIHJhY2UpLg0KICAtIEltYmFsYW5jZWQgY2xhc3Nlcy4NCg0KIyMjIEFzc2lnbm1lbnQgU3RlcHM6DQoxLiAqKkRhdGEgUHJlcHJvY2Vzc2luZyoqOg0KICAgLSBIYW5kbGUgbWlzc2luZyBkYXRhIHVzaW5nIGltcHV0YXRpb24gdGVjaG5pcXVlcyAoZS5nLiwgbWVhbiwgbWVkaWFuKS4NCiAgIC0gTm9ybWFsaXplL3NjYWxlIGZlYXR1cmVzIGZvciBiZXR0ZXIgbW9kZWwgcGVyZm9ybWFuY2UuDQoyLiAqKk1vZGVsIERldmVsb3BtZW50Kio6DQogICAtIEJ1aWxkIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBmb3IgZWFjaCB0YXJnZXQgY2xhc3MgKG11bHRpY2xhc3Mgc2V0dXApLg0KICAgLSBVc2UgY3Jvc3MtdmFsaWRhdGlvbiB0byBldmFsdWF0ZSBwZXJmb3JtYW5jZS4NCjMuICoqRmVhdHVyZSBJbXBvcnRhbmNlKio6DQogICAtIEFuYWx5emUgdGhlIHRvcCA1IGltcG9ydGFudCBmZWF0dXJlcyBjb250cmlidXRpbmcgdG8gcHJlZGljdGlvbnMgdXNpbmcgbW9kZWwgY29lZmZpY2llbnRzLg0KDQotLS0NCg0KIyMgTWF0aGVtYXRpY2FsIGFuZCBDb2RpbmcgUmVwcmVzZW50YXRpb25zDQoNCiMjIyBNYXRoZW1hdGljYWwgUmVwcmVzZW50YXRpb24NCjEuICoqU2lnbW9pZCBGdW5jdGlvbioqOiAgDQogICBcKCBcc2lnbWEoeCkgPSBcZnJhY3sxfXsxICsgZV57LXh9fSBcKQ0KMi4gKipMb2cgTG9zcyoqOiAgDQogICBcKCBKID0gLVxmcmFjezF9e059IFxzdW1fe2k9MX1eTiBcbGVmdFsgeV9pIFxsb2cocF9pKSArICgxLXlfaSkgXGxvZygxLXBfaSkgXHJpZ2h0XSBcKQ0KMy4gKipHcmFkaWVudCBVcGRhdGUqKjogIA0KICAgXCggbV97bmV3fSA9IG1fe29sZH0gLSBcYWxwaGEgXGZyYWN7XHBhcnRpYWwgSn17XHBhcnRpYWwgbX0gXCkNCg0KIyMjIFB5dGhvbiBDb2RlIFJlcHJlc2VudGF0aW9uDQoxLiAqKk9uZS1Ib3QgRW5jb2RpbmcqKjoNCiAgIGBgYHB5dGhvbg0KICAgaW1wb3J0IHBhbmRhcyBhcyBwZA0KICAgZnJvbSBza2xlYXJuLnByZXByb2Nlc3NpbmcgaW1wb3J0IE9uZUhvdEVuY29kZXINCiAgIA0KICAgZGF0YSA9IHsnQ29sb3InOiBbJ1JlZCcsICdCbHVlJywgJ0dyZWVuJ119DQogICBkZiA9IHBkLkRhdGFGcmFtZShkYXRhKQ0KICAgZW5jb2RlciA9IE9uZUhvdEVuY29kZXIoKQ0KICAgZW5jb2RlZCA9IGVuY29kZXIuZml0X3RyYW5zZm9ybShkZltbJ0NvbG9yJ11dKS50b2FycmF5KCkNCiAgIHByaW50KGVuY29kZWQpDQogICBgYGANCjIuICoqTG9naXN0aWMgUmVncmVzc2lvbioqOg0KICAgYGBgcHl0aG9uDQogICBmcm9tIHNrbGVhcm4uZGF0YXNldHMgaW1wb3J0IGxvYWRfYnJlYXN0X2NhbmNlcg0KICAgZnJvbSBza2xlYXJuLm1vZGVsX3NlbGVjdGlvbiBpbXBvcnQgdHJhaW5fdGVzdF9zcGxpdA0KICAgZnJvbSBza2xlYXJuLmxpbmVhcl9tb2RlbCBpbXBvcnQgTG9naXN0aWNSZWdyZXNzaW9uQ1YNCiAgIA0KICAgIyBMb2FkIGRhdGENCiAgIGRhdGEgPSBsb2FkX2JyZWFzdF9jYW5jZXIoKQ0KICAgWCwgeSA9IGRhdGEuZGF0YSwgZGF0YS50YXJnZXQNCiAgIA0KICAgIyBTcGxpdCBkYXRhDQogICBYX3RyYWluLCBYX3Rlc3QsIHlfdHJhaW4sIHlfdGVzdCA9IHRyYWluX3Rlc3Rfc3BsaXQoWCwgeSwgdGVzdF9zaXplPTAuMiwgcmFuZG9tX3N0YXRlPTQyKQ0KICAgDQogICAjIExvZ2lzdGljIFJlZ3Jlc3Npb24gd2l0aCBDVg0KICAgbW9kZWwgPSBMb2dpc3RpY1JlZ3Jlc3Npb25DVihjdj01LCBtYXhfaXRlcj0xMDAwKS5maXQoWF90cmFpbiwgeV90cmFpbikNCiAgIHByaW50KCJBY2N1cmFjeToiLCBtb2RlbC5zY29yZShYX3Rlc3QsIHlfdGVzdCkpDQogICBgYGANCg0KMy4gKipDcm9zcy1WYWxpZGF0aW9uKio6DQogICBgYGBweXRob24NCiAgIGZyb20gc2tsZWFybi5tb2RlbF9zZWxlY3Rpb24gaW1wb3J0IGNyb3NzX3ZhbF9zY29yZQ0KICAgDQogICBzY29yZXMgPSBjcm9zc192YWxfc2NvcmUobW9kZWwsIFgsIHksIGN2PTUsIHNjb3Jpbmc9J2FjY3VyYWN5JykNCiAgIHByaW50KCJDcm9zcy1WYWxpZGF0aW9uIEFjY3VyYWN5OiIsIHNjb3Jlcy5tZWFuKCkpDQogICBgYGANCg0KIyMjIFZpc3VhbGl6YXRpb24gb2YgU2lnbW9pZCBGdW5jdGlvbg0KYGBgcHl0aG9uDQppbXBvcnQgbnVtcHkgYXMgbnANCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQNCg0KeCA9IG5wLmxpbnNwYWNlKC0xMCwgMTAsIDEwMCkNCnNpZ21vaWQgPSAxIC8gKDEgKyBucC5leHAoLXgpKQ0KDQpwbHQucGxvdCh4LCBzaWdtb2lkKQ0KcGx0LnRpdGxlKCdTaWdtb2lkIEZ1bmN0aW9uJykNCnBsdC54bGFiZWwoJ3gnKQ0KcGx0LnlsYWJlbCgnU2lnbW9pZCh4KScpDQpwbHQuZ3JpZCgpDQpwbHQuc2hvdygpDQpgYGANCg0KIyMjIFN0dWR5IEd1aWRlOiBMb2dpc3RpYyBSZWdyZXNzaW9uIHdpdGggTWF0aGVtYXRpY2FsIGFuZCBDb2RpbmcgUmVwcmVzZW50YXRpb25zDQoNCi0tLQ0KDQojIyMjICoqMS4gQ2F0ZWdvcmljYWwgRGF0YSoqDQotICoqRGVmaW5pdGlvbioqOiBEYXRhIHJlcHJlc2VudGluZyBjbGFzc2VzIG9yIGNhdGVnb3JpZXMgKGUuZy4sIHJlZCwgZ3JlZW4sIGJsdWU7IHRydWUvZmFsc2UpLg0KLSAqKkNoYWxsZW5nZXMqKjogQ2F0ZWdvcmljYWwgZGF0YSBtdXN0IGJlIHRyYW5zZm9ybWVkIGludG8gbnVtZXJpY2FsIGRhdGEgdG8gYmUgdXNlZCBpbiBtb3N0IG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzLg0KLSAqKlRyYW5zZm9ybWF0aW9uKio6DQogIC0gKipPbmUtSG90IEVuY29kaW5nKio6IENvbnZlcnRzIGNhdGVnb3JpY2FsIGZlYXR1cmVzIGludG8gYmluYXJ5IGNvbHVtbnMsIHdoZXJlIGVhY2ggdW5pcXVlIHZhbHVlIGJlY29tZXMgYSBjb2x1bW4uIEUuZy4sIGZvciBjb2xvcnMgYHJlZGAsIGBncmVlbmAsIGBibHVlYDoNCiAgICAtIFJlZDogYFsxLCAwLCAwXWANCiAgICAtIEdyZWVuOiBgWzAsIDEsIDBdYA0KICAgIC0gQmx1ZTogYFswLCAwLCAxXWANCg0KKipQeXRob24gRXhhbXBsZSoqOg0KYGBgcHl0aG9uDQppbXBvcnQgcGFuZGFzIGFzIHBkDQoNCmRhdGEgPSB7J0NvbG9yJzogWydSZWQnLCAnR3JlZW4nLCAnQmx1ZSddfQ0KZGYgPSBwZC5EYXRhRnJhbWUoZGF0YSkNCmRmX2VuY29kZWQgPSBwZC5nZXRfZHVtbWllcyhkZiwgY29sdW1ucz1bJ0NvbG9yJ10pDQpgYGANCg0KKipNYXRoZW1hdGljYWwgUmVwcmVzZW50YXRpb24qKjoNCklmIFwoIEMgXCkgcmVwcmVzZW50cyB0aGUgY2F0ZWdvcmllcyBhbmQgXCggeCBcKSBpcyB0aGUgaW5wdXQsIHRoZSB0cmFuc2Zvcm1hdGlvbiBpczoNClxbDQpcdGV4dHtPbmUtaG90IGVuY29kZWQgdmVjdG9yOiB9IFxtYXRoYmZ7eH1fe1x0ZXh0e2VuY29kZWR9fSA9IFt4XzEsIHhfMiwgXGxkb3RzLCB4X25dLCBcdGV4dHt3aGVyZSB9IHhfaSA9IDEgXHRleHR7IGlmIH0geCA9IENfaSwgXHRleHR7IGVsc2UgfSAwLg0KXF0NCg0KLS0tDQoNCiMjIyMgKioyLiBMaW5lYXIgdG8gTG9naXN0aWMgUmVncmVzc2lvbioqDQotIExpbmVhciByZWdyZXNzaW9uIHByZWRpY3RzIGNvbnRpbnVvdXMgdmFsdWVzLiBMb2dpc3RpYyByZWdyZXNzaW9uIHRyYW5zZm9ybXMgdGhpcyB0byBwcmVkaWN0IHByb2JhYmlsaXRpZXMgZm9yIGJpbmFyeSBvdXRjb21lcy4NCi0gKipLZXkgRXF1YXRpb24qKjoNCiAgXFsNCiAgeiA9IFxtYXRoYmZ7d31eVFxtYXRoYmZ7eH0gKyBiLCBccXVhZCBwID0gXHNpZ21hKHopLCBccXVhZCBcc2lnbWEoeikgPSBcZnJhY3sxfXsxICsgZV57LXp9fQ0KICBcXQ0KICBXaGVyZSBcKCBcc2lnbWEoeikgXCkgKHNpZ21vaWQgZnVuY3Rpb24pIHNxdWFzaGVzIFwoIHogXCkgdG8gcmFuZ2UgWzAsIDFdLg0KDQoqKlB5dGhvbiBFeGFtcGxlKio6DQpgYGBweXRob24NCmZyb20gc2tsZWFybi5saW5lYXJfbW9kZWwgaW1wb3J0IExvZ2lzdGljUmVncmVzc2lvbg0KbW9kZWwgPSBMb2dpc3RpY1JlZ3Jlc3Npb24oKQ0KbW9kZWwuZml0KFhfdHJhaW4sIHlfdHJhaW4pDQpgYGANCg0KLS0tDQoNCiMjIyMgKiozLiBUaGUgU2lnbW9pZCBGdW5jdGlvbioqDQotICoqRXF1YXRpb24qKjogXCggXHNpZ21hKHopID0gXGZyYWN7MX17MSArIGVeey16fX0gXCkNCi0gKipQcm9wZXJ0aWVzKio6DQogIC0gXCggeiBcdG8gK1xpbmZ0eSBcKTogXCggXHNpZ21hKHopIFx0byAxIFwpDQogIC0gXCggeiBcdG8gLVxpbmZ0eSBcKTogXCggXHNpZ21hKHopIFx0byAwIFwpDQogIC0gT3V0cHV0cyBwcm9iYWJpbGl0aWVzLg0KDQoqKlB5dGhvbiBFeGFtcGxlKio6DQpgYGBweXRob24NCmltcG9ydCBudW1weSBhcyBucA0KDQpkZWYgc2lnbW9pZCh6KToNCiAgICByZXR1cm4gMSAvICgxICsgbnAuZXhwKC16KSkNCg0KeiA9IG5wLmxpbnNwYWNlKC0xMCwgMTAsIDEwMCkNCnNpZ21vaWRfdmFsdWVzID0gc2lnbW9pZCh6KQ0KYGBgDQoNCi0tLQ0KDQojIyMjICoqNC4gTG9nIExvc3MqKg0KLSBNZWFzdXJlcyB0aGUgZGlzdGFuY2UgYmV0d2VlbiBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBhbmQgYWN0dWFsIGNsYXNzIGxhYmVscy4NCi0gKipFcXVhdGlvbioqOg0KICBcWw0KICBcdGV4dHtMb2cgTG9zczogfSBMKHksIFxoYXR7eX0pID0gLVxmcmFjezF9e259IFxzdW1fe2k9MX1ee259IFxsZWZ0WyB5X2kgXGxvZyhcaGF0e3l9X2kpICsgKDEgLSB5X2kpIFxsb2coMSAtIFxoYXR7eX1faSkgXHJpZ2h0XQ0KICBcXQ0KICBXaGVyZToNCiAgLSBcKCB5X2kgXCk6IEFjdHVhbCBsYWJlbC4NCiAgLSBcKCBcaGF0e3l9X2kgXCk6IFByZWRpY3RlZCBwcm9iYWJpbGl0eS4NCg0KKipQeXRob24gRXhhbXBsZSoqOg0KYGBgcHl0aG9uDQpmcm9tIHNrbGVhcm4ubWV0cmljcyBpbXBvcnQgbG9nX2xvc3MNCmxvc3MgPSBsb2dfbG9zcyh5X3RydWUsIHlfcHJlZCkNCmBgYA0KDQotLS0NCg0KIyMjIyAqKjUuIE1pbmltaXppbmcgTG9nIExvc3MqKg0KLSBBY2hpZXZlZCB0aHJvdWdoIG9wdGltaXphdGlvbiAoZS5nLiwgZ3JhZGllbnQgZGVzY2VudCkuDQotICoqR3JhZGllbnQgRGVzY2VudCoqOg0KICAtIFVwZGF0ZSBydWxlOiBcKCB3ID0gdyAtIFxhbHBoYSBcbmFibGEgTCh3KSBcKSwgd2hlcmUgXCggXGFscGhhIFwpIGlzIHRoZSBsZWFybmluZyByYXRlLg0KLSAqKlB5dGhvbiBFeGFtcGxlKio6DQpgYGBweXRob24NCmZyb20gc2tsZWFybi5saW5lYXJfbW9kZWwgaW1wb3J0IExvZ2lzdGljUmVncmVzc2lvbg0KbW9kZWwgPSBMb2dpc3RpY1JlZ3Jlc3Npb24obWF4X2l0ZXI9MTAwKQ0KbW9kZWwuZml0KFhfdHJhaW4sIHlfdHJhaW4pDQpgYGANCg0KLS0tDQoNCiMjIyMgKio2LiBNdWx0aWNsYXNzIENsYXNzaWZpY2F0aW9uKioNCi0gRXh0ZW5kcyBiaW5hcnkgbG9naXN0aWMgcmVncmVzc2lvbiB0byBtdWx0aXBsZSBjbGFzc2VzLg0KLSAqKkFwcHJvYWNoZXMqKjoNCiAgLSAqKk9uZS12cy1SZXN0IChPdlIpKio6IFRyYWluIHNlcGFyYXRlIGNsYXNzaWZpZXJzIGZvciBlYWNoIGNsYXNzLg0KICAtICoqU29mdG1heCBSZWdyZXNzaW9uKio6IERpcmVjdGx5IG1vZGVscyBhbGwgY2xhc3NlcyB1c2luZyBwcm9iYWJpbGl0aWVzLg0KLSAqKlNvZnRtYXggRnVuY3Rpb24qKjoNCiAgXFsNCiAgXHNpZ21hKHpfaikgPSBcZnJhY3tlXnt6X2p9fXtcc3VtX3trPTF9XksgZV57el9rfX0NCiAgXF0NCg0KKipQeXRob24gRXhhbXBsZSoqOg0KYGBgcHl0aG9uDQpmcm9tIHNrbGVhcm4ubGluZWFyX21vZGVsIGltcG9ydCBMb2dpc3RpY1JlZ3Jlc3Npb24NCm1vZGVsID0gTG9naXN0aWNSZWdyZXNzaW9uKG11bHRpX2NsYXNzPSdtdWx0aW5vbWlhbCcsIHNvbHZlcj0nbGJmZ3MnKQ0KbW9kZWwuZml0KFhfdHJhaW4sIHlfdHJhaW4pDQpgYGANCg0KLS0tDQoNCiMjIyBDYXNlIFN0dWR5OiBQcmVkaWN0aW5nIERpYWJldGVzIFJlYWRtaXNzaW9uDQoNCiMjIyMgKipPYmplY3RpdmUqKg0KLSBQcmVkaWN0IGhvc3BpdGFsIHJlYWRtaXNzaW9uIHdpdGhpbiAzMCBkYXlzIHVzaW5nIGxvZ2lzdGljIHJlZ3Jlc3Npb24uDQotIEhhbmRsZSBtaXNzaW5nIGRhdGEgdmlhIGltcHV0YXRpb24uDQoNCiMjIyMgKipTdGVwcyoqOg0KMS4gKipEYXRhIFByZXByb2Nlc3NpbmcqKjoNCiAgIC0gSGFuZGxlIG1pc3NpbmcgdmFsdWVzIChlLmcuLCBtZWFuL21vZGUgaW1wdXRhdGlvbikuDQogICAtIEVuY29kZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgKGUuZy4sIG9uZS1ob3QgZW5jb2RpbmcpLg0KDQoyLiAqKk1vZGVsIEJ1aWxkaW5nKio6DQogICAtIFRyYWluIGxvZ2lzdGljIHJlZ3Jlc3Npb24gZm9yIHRocmVlIGNsYXNzZXMgb2YgcmVhZG1pc3Npb246DQogICAgIC0gTm8gcmVhZG1pc3Npb24uDQogICAgIC0gUmVhZG1pc3Npb24gaW4gbGVzcyB0aGFuIDMwIGRheXMuDQogICAgIC0gUmVhZG1pc3Npb24gaW4gbW9yZSB0aGFuIDMwIGRheXMuDQogICAtIEV2YWx1YXRlIHVzaW5nIG1ldHJpY3MgbGlrZSBsb2cgbG9zcyBvciBhY2N1cmFjeS4NCg0KMy4gKipGZWF0dXJlIEltcG9ydGFuY2UqKjoNCiAgIC0gRXh0cmFjdCBjb2VmZmljaWVudHMgdG8gaWRlbnRpZnkgdG9wIDUgcHJlZGljdGl2ZSBmZWF0dXJlcy4NCg0KKipQeXRob24gQ29kZSoqOg0KYGBgcHl0aG9uDQpmcm9tIHNrbGVhcm4uaW1wdXRlIGltcG9ydCBTaW1wbGVJbXB1dGVyDQpmcm9tIHNrbGVhcm4ubGluZWFyX21vZGVsIGltcG9ydCBMb2dpc3RpY1JlZ3Jlc3Npb24NCmZyb20gc2tsZWFybi5tZXRyaWNzIGltcG9ydCBhY2N1cmFjeV9zY29yZQ0KZnJvbSBza2xlYXJuLm1vZGVsX3NlbGVjdGlvbiBpbXBvcnQgdHJhaW5fdGVzdF9zcGxpdA0KDQojIERhdGEgcHJlcHJvY2Vzc2luZw0KaW1wdXRlciA9IFNpbXBsZUltcHV0ZXIoc3RyYXRlZ3k9J21lYW4nKQ0KWCA9IGltcHV0ZXIuZml0X3RyYW5zZm9ybShYKQ0KDQojIFRyYWluLXRlc3Qgc3BsaXQNClhfdHJhaW4sIFhfdGVzdCwgeV90cmFpbiwgeV90ZXN0ID0gdHJhaW5fdGVzdF9zcGxpdChYLCB5LCB0ZXN0X3NpemU9MC4yKQ0KDQojIExvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwNCm1vZGVsID0gTG9naXN0aWNSZWdyZXNzaW9uKG11bHRpX2NsYXNzPSdtdWx0aW5vbWlhbCcsIHNvbHZlcj0nbGJmZ3MnLCBtYXhfaXRlcj01MDApDQptb2RlbC5maXQoWF90cmFpbiwgeV90cmFpbikNCg0KIyBQcmVkaWN0aW9ucw0KeV9wcmVkID0gbW9kZWwucHJlZGljdChYX3Rlc3QpDQoNCiMgRmVhdHVyZSBpbXBvcnRhbmNlDQppbXBvcnRhbmNlID0gbW9kZWwuY29lZl8NCnByaW50KCJUb3AgNSBGZWF0dXJlczoiLCBpbXBvcnRhbmNlLmFyZ3NvcnQoKVstNTpdKQ0KDQojIEFjY3VyYWN5DQphY2N1cmFjeSA9IGFjY3VyYWN5X3Njb3JlKHlfdGVzdCwgeV9wcmVkKQ0KcHJpbnQoZiJNb2RlbCBBY2N1cmFjeToge2FjY3VyYWN5fSIpDQpgYGANCg0KIyMjIyAqKlZhcmlhYmxlIEltcG9ydGFuY2UgSW50ZXJwcmV0YXRpb24qKg0KLSBDb2VmZmljaWVudHMgcmVmbGVjdCB0aGUgaW1wb3J0YW5jZSBvZiBlYWNoIGZlYXR1cmUuDQotIEFuYWx5emUgdG9wIHZhcmlhYmxlcyB0byBkZXJpdmUgaW5zaWdodHMgZm9yIHJlYWRtaXNzaW9uIHBhdHRlcm5zLg0KDQotLS0NCg0KIyMjIyAqKkFzc2lnbm1lbnQqKg0KLSBUcmFpbiBhbmQgZXZhbHVhdGUgbG9naXN0aWMgcmVncmVzc2lvbiBvbiB0aGUgZGlhYmV0ZXMgZGF0YXNldC4NCi0gUmVwb3J0IHRvcCA1IGZlYXR1cmVzIGFuZCB0aGVpciBzaWduaWZpY2FuY2UuDQotIFN1Ym1pdCByZXN1bHRzIHdpdGggYSBjbGVhciBleHBsYW5hdGlvbiBvZiBmaW5kaW5ncy4NCg0KKipEZWxpdmVyYWJsZSoqOg0KLSBDb2RlIGZpbGUgKGUuZy4sIGBGaXJzdE5hbWVfTGFzdE5hbWVfTG9nUmVnX0Fzc2lnbm1lbnQucHlgKS4NCi0gV3JpdHRlbiByZXBvcnQgb24gbW9kZWwgcGVyZm9ybWFuY2UgYW5kIGZlYXR1cmUgaW1wb3J0YW5jZS4NCg0KDQoNCg0KIyMjICoqS2V5IFRha2Vhd2F5cyoqDQoxLiAqKkxvZ2lzdGljIFJlZ3Jlc3Npb24gQnJpZGdlcyBMaW5lYXIgTW9kZWxzIGFuZCBQcm9iYWJpbGlzdGljIFByZWRpY3Rpb25zKio6DQogICAtIExvZ2lzdGljIHJlZ3Jlc3Npb24gZXh0ZW5kcyBsaW5lYXIgcmVncmVzc2lvbiBmb3IgY2F0ZWdvcmljYWwgdGFyZ2V0IHZhcmlhYmxlcyBieSBlbXBsb3lpbmcgdGhlIHNpZ21vaWQgZnVuY3Rpb24gdG8gb3V0cHV0IHByb2JhYmlsaXRpZXMuIFRoaXMgYWxsb3dzIGZvciBiaW5hcnkgb3IgbXVsdGljbGFzcyBwcmVkaWN0aW9ucyBieSBpbnRlcnByZXRpbmcgcHJvYmFiaWxpdGllcyBhcyBjbGFzcyBtZW1iZXJzaGlwcy4NCg0KMi4gKipMb2cgTG9zcyBhcyBhIE1ldHJpYyoqOg0KICAgLSBVbmxpa2UgbGluZWFyIHJlZ3Jlc3Npb24ncyBtZWFuIHNxdWFyZWQgZXJyb3IsIGxvZ2lzdGljIHJlZ3Jlc3Npb24gdXNlcyBsb2cgbG9zcyB0byBtZWFzdXJlIHRoZSBtb2RlbCdzIHBlcmZvcm1hbmNlLiBJdCBwZW5hbGl6ZXMgcHJlZGljdGlvbnMgZnVydGhlciBmcm9tIHRoZSB0cnVlIGNsYXNzLCBlbnN1cmluZyBwcm9iYWJpbGl0aWVzIGFyZSBhY2N1cmF0ZS4NCg0KMy4gKipIYW5kbGluZyBNdWx0aWNsYXNzIFByb2JsZW1zKio6DQogICAtIE11bHRpY2xhc3MgY2xhc3NpZmljYXRpb24gY2FuIGJlIGFkZHJlc3NlZCB1c2luZyBtZXRob2RzIGxpa2Ugb25lLXZzLXJlc3QgKE92Uikgb3Igc29mdG1heCByZWdyZXNzaW9uLiBXaGlsZSBPdlIgaXMgY29tcHV0YXRpb25hbGx5IGVmZmljaWVudCBmb3IgYSBmZXcgY2xhc3NlcywgaXQgc2NhbGVzIHBvb3JseSB3aXRoIG1hbnkgY2xhc3NlcyBkdWUgdG8gY2xhc3MgaW1iYWxhbmNlIGlzc3Vlcy4NCiAgIA0KNC4gKipJbXBvcnRhbmNlIG9mIFNpZ21vaWQgRnVuY3Rpb24gaW4gTG9naXN0aWMgUmVncmVzc2lvbioqOiBUaGUgc2lnbW9pZCBmdW5jdGlvbiB0cmFuc2Zvcm1zIHRoZSBsaW5lYXIgcmVncmVzc2lvbiBvdXRwdXQgaW50byBwcm9iYWJpbGl0aWVzLCBtYWtpbmcgaXQgcG9zc2libGUgdG8gaGFuZGxlIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiB0YXNrcyBlZmZlY3RpdmVseS4gVGhpcyBmdW5jdGlvbiBlbnN1cmVzIHRoYXQgcHJlZGljdGlvbnMgZmFsbCB3aXRoaW4gdGhlIHJhbmdlIFswLCAxXSwgd2hpY2ggY2FuIHRoZW4gYmUgdXNlZCB0byBjbGFzc2lmeSBkYXRhIGludG8gZGlzdGluY3QgY2F0ZWdvcmllcy4NCg0KNS4gKipMb2cgTG9zcyBmb3IgQ2xhc3NpZmljYXRpb24qKjogTG9nYXJpdGhtaWMgbG9zcyAobG9nIGxvc3MpIHF1YW50aWZpZXMgdGhlIGVycm9yIGluIHByb2JhYmlsaXN0aWMgcHJlZGljdGlvbnMgYnkgcGVuYWxpemluZyBwcmVkaWN0aW9ucyB0aGF0IGRldmlhdGUgc2lnbmlmaWNhbnRseSBmcm9tIHRoZSB0cnVlIGxhYmVscy4gSXQgZm9ybXMgYSBjb252ZXggZnVuY3Rpb24gd2l0aCBhIHdlbGwtZGVmaW5lZCBtaW5pbXVtLCBmYWNpbGl0YXRpbmcgb3B0aW1pemF0aW9uIGFuZCBjb252ZXJnZW5jZS4NCg0KNi4gKipNdWx0aWNsYXNzIENsYXNzaWZpY2F0aW9uIENoYWxsZW5nZXMqKjogTG9naXN0aWMgcmVncmVzc2lvbiBjYW4gYmUgZXh0ZW5kZWQgdG8gbXVsdGljbGFzcyBwcm9ibGVtcyB0aHJvdWdoIHRlY2huaXF1ZXMgbGlrZSBPbmUtdnMtQWxsIGFuZCBPbmUtdnMtT25lLiBXaGlsZSB0aGVzZSBtZXRob2RzIGFsbG93IGxvZ2lzdGljIHJlZ3Jlc3Npb24gdG8gaGFuZGxlIG1vcmUgdGhhbiB0d28gY2xhc3NlcywgdGhleSBjb21lIHdpdGggc2NhbGFiaWxpdHkgYW5kIGJpYXMgY2hhbGxlbmdlcyBhcyB0aGUgbnVtYmVyIG9mIGNsYXNzZXMgaW5jcmVhc2VzLg0KDQotLS0NCg0KIyMjICoqUXVlc3Rpb25zIGZvciBDbGFzcyBEaXNjdXNzaW9uKioNCjEuICoqVGhyZXNob2xkIEFkanVzdG1lbnQgaW4gTG9naXN0aWMgUmVncmVzc2lvbioqOg0KICAgLSBIb3cgY2FuIGFkanVzdGluZyB0aGUgY2xhc3NpZmljYXRpb24gdGhyZXNob2xkIChlLmcuLCBtb3ZpbmcgaXQgZnJvbSAwLjUgdG8gMC4yIGZvciBmcmF1ZCBkZXRlY3Rpb24pIGltcGFjdCB0aGUgYmFsYW5jZSBiZXR3ZWVuIGZhbHNlIHBvc2l0aXZlcyBhbmQgZmFsc2UgbmVnYXRpdmVzPyBXaGF0IHJlYWwtd29ybGQgZXhhbXBsZXMgaGlnaGxpZ2h0IHRoZSBpbXBvcnRhbmNlIG9mIHRoaXM/DQoNCjIuICoqTG9nIExvc3MgdnMuIEFjY3VyYWN5Kio6DQogICAtIEluIHdoYXQgc2NlbmFyaW9zIG1pZ2h0IGxvZyBsb3NzIGJlIGEgbW9yZSBhcHByb3ByaWF0ZSBldmFsdWF0aW9uIG1ldHJpYyB0aGFuIGFjY3VyYWN5PyBDYW4geW91IHByb3ZpZGUgZXhhbXBsZXMgd2hlcmUgYWNjdXJhY3kgbWlnaHQgYmUgbWlzbGVhZGluZz8NCg0KMy4gKipFdGhpY2FsIENvbnNpZGVyYXRpb25zIGluIENhdGVnb3JpY2FsIFZhcmlhYmxlcyoqOg0KICAgLSBXaGVuIGluY2x1ZGluZyBzZW5zaXRpdmUgdmFyaWFibGVzIGxpa2UgcmFjZSBvciBnZW5kZXIgaW4gbG9naXN0aWMgcmVncmVzc2lvbiwgaG93IGNhbiB3ZSBlbnN1cmUgdGhlIG1vZGVsIGlzIGV0aGljYWxseSBzb3VuZCBhbmQgYXZvaWRzIGRpc2NyaW1pbmF0b3J5IHByYWN0aWNlcyB3aGlsZSBsZXZlcmFnaW5nIHBvdGVudGlhbGx5IHByZWRpY3RpdmUgaW5mb3JtYXRpb24/DQogICANCjQuICoqT24gU2lnbW9pZCBUaHJlc2hvbGQgQWRqdXN0bWVudCoqOiBJbiByZWFsLXdvcmxkIGFwcGxpY2F0aW9ucyBsaWtlIGZyYXVkIGRldGVjdGlvbiwgaG93IGRvIHdlIGRlY2lkZSBvbiB0aGUgb3B0aW1hbCB0aHJlc2hvbGQgZm9yIGNsYXNzaWZpY2F0aW9uIGJleW9uZCB0aGUgZGVmYXVsdCB2YWx1ZSBvZiAwLjU/IFdoYXQgc3RyYXRlZ2llcyBvciBtZXRyaWNzIHNob3VsZCBndWlkZSB0aGlzIGRlY2lzaW9uPw0KDQo1LiAqKk9uIEhhbmRsaW5nIE1pc3NpbmcgRGF0YSoqOiBXaGVuIGRlYWxpbmcgd2l0aCBtaXNzaW5nIHZhbHVlcyBpbiBhIGRhdGFzZXQsIGFzIG1lbnRpb25lZCBpbiB0aGUgZGlhYmV0ZXMgY2FzZSBzdHVkeSwgd2hhdCBmYWN0b3JzIHNob3VsZCBpbmZsdWVuY2UgdGhlIGNob2ljZSBvZiBhbiBpbXB1dGF0aW9uIHN0cmF0ZWd5PyBIb3cgZG8gd2UgZW5zdXJlIHRoZSBpbXB1dGF0aW9uIGRvZXMgbm90IGludHJvZHVjZSBiaWFzIGludG8gdGhlIG1vZGVsPw0KDQo2LiAqKk9uIEV0aGljYWwgQ29uc2lkZXJhdGlvbnMgaW4gRmVhdHVyZSBTZWxlY3Rpb24qKjogSW4gY2FzZXMgd2hlcmUgc2Vuc2l0aXZlIGZlYXR1cmVzIGxpa2UgcmFjZSBhcmUgaW5jbHVkZWQgaW4gdGhlIGRhdGFzZXQsIGhvdyBkbyB3ZSBiYWxhbmNlIHRoZSBwb3RlbnRpYWwgdXRpbGl0eSBvZiBzdWNoIGZlYXR1cmVzIHdpdGggZXRoaWNhbCBjb25jZXJucyBhbmQgdGhlIHJpc2sgb2YgcGVycGV0dWF0aW5nIGJpYXMgaW4gcHJlZGljdGlvbnM/DQoNCg0KICAgDQojIyMgKipCZXN0IFRha2Vhd2F5cyoqDQoNCjEuICoqTG9naXN0aWMgUmVncmVzc2lvbiBCcmlkZ2VzIExpbmVhciBNb2RlbHMgYW5kIFByb2JhYmlsaXN0aWMgUHJlZGljdGlvbnMqKjoNCiAgIC0gTG9naXN0aWMgcmVncmVzc2lvbiBleHRlbmRzIGxpbmVhciByZWdyZXNzaW9uIGZvciBjYXRlZ29yaWNhbCB0YXJnZXQgdmFyaWFibGVzIGJ5IGVtcGxveWluZyB0aGUgc2lnbW9pZCBmdW5jdGlvbiB0byBvdXRwdXQgcHJvYmFiaWxpdGllcy4gVGhpcyBhbGxvd3MgZm9yIGJpbmFyeSBvciBtdWx0aWNsYXNzIHByZWRpY3Rpb25zIGJ5IGludGVycHJldGluZyBwcm9iYWJpbGl0aWVzIGFzIGNsYXNzIG1lbWJlcnNoaXBzLg0KDQoyLiAqKkxvZyBMb3NzIGZvciBDbGFzc2lmaWNhdGlvbioqOg0KICAgLSBMb2dhcml0aG1pYyBsb3NzIChsb2cgbG9zcykgcXVhbnRpZmllcyB0aGUgZXJyb3IgaW4gcHJvYmFiaWxpc3RpYyBwcmVkaWN0aW9ucyBieSBwZW5hbGl6aW5nIHByZWRpY3Rpb25zIHRoYXQgZGV2aWF0ZSBzaWduaWZpY2FudGx5IGZyb20gdGhlIHRydWUgbGFiZWxzLiBJdCBmb3JtcyBhIGNvbnZleCBmdW5jdGlvbiB3aXRoIGEgd2VsbC1kZWZpbmVkIG1pbmltdW0sIGZhY2lsaXRhdGluZyBvcHRpbWl6YXRpb24gYW5kIGNvbnZlcmdlbmNlLg0KDQotLS0NCg0KIyMjICoqQmVzdCBRdWVzdGlvbnMgZm9yIENsYXNzIERpc2N1c3Npb24qKg0KDQoxLiAqKlRocmVzaG9sZCBBZGp1c3RtZW50IGluIExvZ2lzdGljIFJlZ3Jlc3Npb24qKjoNCiAgIC0gSG93IGNhbiBhZGp1c3RpbmcgdGhlIGNsYXNzaWZpY2F0aW9uIHRocmVzaG9sZCAoZS5nLiwgbW92aW5nIGl0IGZyb20gMC41IHRvIDAuMiBmb3IgZnJhdWQgZGV0ZWN0aW9uKSBpbXBhY3QgdGhlIGJhbGFuY2UgYmV0d2VlbiBmYWxzZSBwb3NpdGl2ZXMgYW5kIGZhbHNlIG5lZ2F0aXZlcz8gV2hhdCByZWFsLXdvcmxkIGV4YW1wbGVzIGhpZ2hsaWdodCB0aGUgaW1wb3J0YW5jZSBvZiB0aGlzPw0KDQoyLiAqKkV0aGljYWwgQ29uc2lkZXJhdGlvbnMgaW4gQ2F0ZWdvcmljYWwgVmFyaWFibGVzKio6DQogICAtIFdoZW4gaW5jbHVkaW5nIHNlbnNpdGl2ZSB2YXJpYWJsZXMgbGlrZSByYWNlIG9yIGdlbmRlciBpbiBsb2dpc3RpYyByZWdyZXNzaW9uLCBob3cgY2FuIHdlIGVuc3VyZSB0aGUgbW9kZWwgaXMgZXRoaWNhbGx5IHNvdW5kIGFuZCBhdm9pZHMgZGlzY3JpbWluYXRvcnkgcHJhY3RpY2VzIHdoaWxlIGxldmVyYWdpbmcgcG90ZW50aWFsbHkgcHJlZGljdGl2ZSBpbmZvcm1hdGlvbj8NCg0KMS4gVGhlIGNvcnJlY3QgYW5zd2VyIGlzOg0KDQoqKkRpc2NyZXRlIGRhdGEgaGFzIHNwZWNpZmljIHZhbHVlcyB0aGF0IGNhbiBiZSBudW1lcmljLCB3aGlsZSBjYXRlZ29yaWNhbCBzaG91bGQgYWx3YXlzIGJlIG9uZSBob3QgZW5jb2RlZC4qKg0KDQojIyMgRXhwbGFuYXRpb246DQotICoqQ2F0ZWdvcmljYWwgRGF0YSoqOiBSZXByZXNlbnRzIGNhdGVnb3JpZXMgb3IgY2xhc3NlcyB3aXRob3V0IGFuIGluaGVyZW50IG9yZGVyIChlLmcuLCBjb2xvcnMsIHR5cGVzIG9mIGFuaW1hbHMpLiBJdCBtdXN0IGJlIG9uZS1ob3QgZW5jb2RlZCBmb3IgbnVtZXJpY2FsIHJlcHJlc2VudGF0aW9uIGluIG1vc3QgbWFjaGluZSBsZWFybmluZyBtb2RlbHMuDQotICoqRGlzY3JldGUgRGF0YSoqOiBSZXByZXNlbnRzIGNvdW50YWJsZSwgZGlzdGluY3QgdmFsdWVzIHRoYXQgYXJlIG51bWVyaWMgaW4gbmF0dXJlIChlLmcuLCBudW1iZXIgb2Ygcm9vbXMgaW4gYSBob3VzZSwgbnVtYmVyIG9mIGNhcnMgYSBmYW1pbHkgb3ducykuIERpc2NyZXRlIGRhdGEgdHlwaWNhbGx5IGRvZXMgbm90IHJlcXVpcmUgb25lLWhvdCBlbmNvZGluZyBhbmQgY2FuIG9mdGVuIGJlIGxlZnQgYXMgaXMuDQoNCi0tLQ0KDQojIyMgV2h5IE5vdCB0aGUgT3RoZXIgQ2hvaWNlcz8NCjEuICoqIk9ubHkgZGlzY3JldGUgZGF0YSBzaG91bGQgYmUgb25lLWhvdCBlbmNvZGVkIjoqKiBJbmNvcnJlY3QuIERpc2NyZXRlIGRhdGEgaXMgdXN1YWxseSBudW1lcmljIGFuZCBkb2VzIG5vdCBuZWVkIG9uZS1ob3QgZW5jb2RpbmcsIHdoaWxlIGNhdGVnb3JpY2FsIGRhdGEgdHlwaWNhbGx5IGRvZXMuDQogICANCjIuICoqIkNhdGVnb3JpY2FsIGRhdGEgY2Fubm90IGJlIHVzZWQgaW4gcmVncmVzc2lvbiI6KiogSW5jb3JyZWN0LiBDYXRlZ29yaWNhbCBkYXRhIGNhbiBiZSB1c2VkIGluIHJlZ3Jlc3Npb24gYWZ0ZXIgcHJvcGVyIGVuY29kaW5nLg0KDQozLiAqKiJDYXRlZ29yaWNhbCBkYXRhIGNhbiBoYXZlIG11bHRpcGxlIHZhbHVlcywgd2hpbGUgZGlzY3JldGUgZGF0YSBvbmx5IGhhdmUgdHdvIjoqKiBJbmNvcnJlY3QuIEJvdGggY2F0ZWdvcmljYWwgYW5kIGRpc2NyZXRlIGRhdGEgY2FuIGhhdmUgbXVsdGlwbGUgdmFsdWVzLg0KDQoNCg0KVGhlIGNvcnJlY3QgYW5zd2VyIGlzOg0KDQoqKkRpc2NyZXRlIGRhdGEgaGFzIHNwZWNpZmljIHZhbHVlcyB0aGF0IGNhbiBiZSBudW1lcmljLCB3aGlsZSBjYXRlZ29yaWNhbCBzaG91bGQgYWx3YXlzIGJlIG9uZSBob3QgZW5jb2RlZC4qKiANCg0KIyMjIEV4cGxhbmF0aW9uOg0KLSAqKkNhdGVnb3JpY2FsIGRhdGEqKjogUmVwcmVzZW50cyBsYWJlbHMgb3IgY2xhc3NlcyB0aGF0IGRvIG5vdCBoYXZlIGFuIGluaGVyZW50IG51bWVyaWMgb3JkZXIgKGUuZy4sICJyZWQiLCAiYmx1ZSIsICJncmVlbiIpLiBUaGVzZSBtdXN0IGJlICoqb25lLWhvdCBlbmNvZGVkKiogdG8gYmUgdXNlZCBpbiBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscy4NCi0gKipEaXNjcmV0ZSBkYXRhKio6IFJlcHJlc2VudHMgbnVtZXJpYyB2YWx1ZXMgdGhhdCB0YWtlIG9uIHNwZWNpZmljLCBkaXN0aW5jdCBudW1iZXJzIChlLmcuLCB0aGUgbnVtYmVyIG9mIHJvb21zLCBjYXJzIG93bmVkKS4gRGlzY3JldGUgZGF0YSAqKmRvZXMgbm90IG5lZWQgb25lLWhvdCBlbmNvZGluZyoqIGJlY2F1c2UgdGhlIG51bWVyaWMgdmFsdWVzIGFyZSBtZWFuaW5nZnVsIGFuZCBwcmVzZXJ2ZSBvcmRlci4NCg0KDQoyLiANClRoZSBjb3JyZWN0IGFuc3dlciBpczoNCg0KKipJcyB0aGUgc2FtZSBhcyBsaW5lYXIgcmVncmVzc2lvbioqDQoNCiMjIyBFeHBsYW5hdGlvbjoNCi0gKipTbG9wZSBVcGRhdGUgUnVsZSoqOiBUaGUgc2xvcGUgdXBkYXRlIHJ1bGUgaW4gbG9naXN0aWMgcmVncmVzc2lvbiBpcyBtYXRoZW1hdGljYWxseSBzaW1pbGFyIHRvIGxpbmVhciByZWdyZXNzaW9uLiBUaGUga2V5IGRpZmZlcmVuY2UgbGllcyBpbiBob3cgdGhlIGVycm9yIGlzIGNhbGN1bGF0ZWQgZHVlIHRvIHRoZSB1c2Ugb2YgdGhlICoqc2lnbW9pZCBmdW5jdGlvbioqIGFuZCAqKmxvZyBsb3NzKiouIEhvd2V2ZXIsIHRoZSBncmFkaWVudCBkZXNjZW50IHByb2NlZHVyZSAodXNlZCB0byBtaW5pbWl6ZSB0aGUgbG9zcyBmdW5jdGlvbikgcmVtYWlucyB0aGUgc2FtZS4NCg0KLS0tDQoNCiMjIyBXaHkgTm90IHRoZSBPdGhlciBDaG9pY2VzPw0KMS4gKioiSW52b2x2ZXMgdGhlIHNpZ21vaWQiOioqIFdoaWxlIHRoZSBzaWdtb2lkIGZ1bmN0aW9uIGlzIGludGVncmFsIHRvIGxvZ2lzdGljIHJlZ3Jlc3Npb24sIGl0IGlzIHBhcnQgb2YgdGhlIHByZWRpY3Rpb24gbWVjaGFuaXNtLCBub3QgdGhlIHNsb3BlIHVwZGF0ZSBydWxlIGl0c2VsZi4NCg0KMi4gKioiVXNlcyBjcm9zcy1lbnRyb3B5IGxvc3MiOioqIExvZ2lzdGljIHJlZ3Jlc3Npb24gY2FuIGJlIGFzc29jaWF0ZWQgd2l0aCBjcm9zcy1lbnRyb3B5IGxvc3MgaW4gbXVsdGljbGFzcyBjbGFzc2lmaWNhdGlvbiwgYnV0IHRoaXMgaXNuJ3Qgc3BlY2lmaWMgdG8gdGhlIHNsb3BlIHVwZGF0ZSBydWxlLg0KDQozLiAqKiJVc2VzIHRoZSBsb2cgbG9zcyI6KiogTG9nIGxvc3MgaXMgdGhlIGNvc3QgZnVuY3Rpb24gbWluaW1pemVkIGluIGxvZ2lzdGljIHJlZ3Jlc3Npb24sIGJ1dCBpdCBpcyBub3QgdGhlIHNsb3BlIHVwZGF0ZSBydWxlIGl0c2VsZi4gVGhlIHJ1bGUgaXMgZGVyaXZlZCB1c2luZyB0aGUgZ3JhZGllbnQgb2YgdGhlIGxvZyBsb3NzLg0KDQpUaGUgY29ycmVjdCBhbnN3ZXIgaXM6DQoNCioqSXMgdGhlIHNhbWUgYXMgbGluZWFyIHJlZ3Jlc3Npb24qKg0KDQojIyMgRXhwbGFuYXRpb246DQotIFRoZSAqKnNsb3BlIHVwZGF0ZSBydWxlKiogZm9yIGxvZ2lzdGljIHJlZ3Jlc3Npb24gaXMgbWF0aGVtYXRpY2FsbHkgdGhlIHNhbWUgYXMgaW4gbGluZWFyIHJlZ3Jlc3Npb24gYmVjYXVzZSBpdCBpcyBkZXJpdmVkIHVzaW5nIGdyYWRpZW50IGRlc2NlbnQuIFRoZSB1cGRhdGUgcnVsZSBhZGp1c3RzIHdlaWdodHMgKHNsb3BlcykgaXRlcmF0aXZlbHkgdG8gbWluaW1pemUgdGhlIGxvc3MgZnVuY3Rpb24uDQogIA0KIyMjIFdoeSB0aGUgb3RoZXIgb3B0aW9ucyBhcmUgaW5jb3JyZWN0Og0KMS4gKipJbnZvbHZlcyB0aGUgc2lnbW9pZCoqOiBUaGUgc2lnbW9pZCBmdW5jdGlvbiBpcyB1c2VkIHRvIHRyYW5zZm9ybSBvdXRwdXRzIGludG8gcHJvYmFiaWxpdGllcywgYnV0IGl0IGlzIG5vdCBkaXJlY3RseSBwYXJ0IG9mIHRoZSBzbG9wZSB1cGRhdGUgcnVsZS4NCjIuICoqVXNlcyBjcm9zcy1lbnRyb3B5IGxvc3MqKjogV2hpbGUgY3Jvc3MtZW50cm9weSBsb3NzIChsb2cgbG9zcykgaXMgdGhlIGxvc3MgZnVuY3Rpb24gZm9yIGxvZ2lzdGljIHJlZ3Jlc3Npb24sIGl0IGlzIHNlcGFyYXRlIGZyb20gdGhlIGFjdHVhbCBzbG9wZSB1cGRhdGUgcnVsZS4NCjMuICoqVXNlcyB0aGUgbG9nIGxvc3MqKjogTG9nIGxvc3MgaXMgbWluaW1pemVkIGR1cmluZyB0cmFpbmluZywgYnV0IHRoZSBzbG9wZSB1cGRhdGUgcnVsZSBpdHNlbGYgcmVtYWlucyBpZGVudGljYWwgdG8gdGhhdCBpbiBsaW5lYXIgcmVncmVzc2lvbi4NCg0KDQoNCjMuIFRoZSBjb3JyZWN0IGFuc3dlciBpczogIA0KDQoqKkNhbiBwcm9kdWNlIG5lZ2F0aXZlIG91dHB1dHMqKiAgDQoNCi0tLQ0KDQojIyMgRXhwbGFuYXRpb246DQotICoqT3V0cHV0cyBhbHdheXMgYmV0d2VlbiAwIGFuZCAxKio6IFRydWUuIFRoZSBzaWdtb2lkIGZ1bmN0aW9uIFwoIFxzaWdtYSh6KSA9IFxmcmFjezF9ezEgKyBlXnsten19IFwpIGFsd2F5cyBwcm9kdWNlcyBvdXRwdXRzIGluIHRoZSByYW5nZSBcKCAoMCwgMSkgXCkuICANCi0gKipDYW4gdGFrZSBuZWdhdGl2ZSBpbnB1dHMqKjogVHJ1ZS4gVGhlIHNpZ21vaWQgZnVuY3Rpb24gYWNjZXB0cyBhbnkgcmVhbCBudW1iZXIgXCggeiBcKSwgaW5jbHVkaW5nIG5lZ2F0aXZlIHZhbHVlcy4gIA0KLSAqKkEgc2ltdWxhdGVkIHN0ZXAgZnVuY3Rpb24qKjogVHJ1ZS4gRm9yIHZlcnkgbGFyZ2UgcG9zaXRpdmUgb3IgbmVnYXRpdmUgaW5wdXRzLCB0aGUgc2lnbW9pZCBhcHByb3hpbWF0ZXMgYSBzdGVwIGZ1bmN0aW9uIHdpdGggb3V0cHV0cyBjbG9zZSB0byAxIG9yIDAsIHJlc3BlY3RpdmVseS4gIA0KLSAqKkNhbiBwcm9kdWNlIG5lZ2F0aXZlIG91dHB1dHMqKjogRmFsc2UuIEJ5IGRlZmluaXRpb24sIHRoZSBzaWdtb2lkIGZ1bmN0aW9uIG9ubHkgb3V0cHV0cyB2YWx1ZXMgYmV0d2VlbiAwIGFuZCAxLCBzbyBpdCBjYW5ub3QgcHJvZHVjZSBuZWdhdGl2ZSBvdXRwdXRzLg0KDQpUaGUgY29ycmVjdCBhbnN3ZXIgaXM6DQoNCioqQ2FuIHByb2R1Y2UgbmVnYXRpdmUgb3V0cHV0cyoqDQoNCiMjIyBFeHBsYW5hdGlvbjoNClRoZSBzaWdtb2lkIGZ1bmN0aW9uIGhhcyB0aGUgZm9sbG93aW5nIHByb3BlcnRpZXM6DQoxLiAqKk91dHB1dHMgYWx3YXlzIGJldHdlZW4gMCBhbmQgMSoqOiBUaGUgc2lnbW9pZCBmdW5jdGlvbiBtYXBzIGFsbCBpbnB1dHMgdG8gdmFsdWVzIHdpdGhpbiB0aGlzIHJhbmdlLg0KMi4gKipDYW4gdGFrZSBuZWdhdGl2ZSBpbnB1dHMqKjogVGhlIGZ1bmN0aW9uIGFjY2VwdHMgYW55IHJlYWwgbnVtYmVyIGFzIGlucHV0LCBpbmNsdWRpbmcgbmVnYXRpdmUgdmFsdWVzLg0KMy4gKipBIHNpbXVsYXRlZCBzdGVwIGZ1bmN0aW9uKio6IEZvciB2ZXJ5IGxhcmdlIHBvc2l0aXZlIG9yIG5lZ2F0aXZlIGlucHV0cywgdGhlIHNpZ21vaWQgZnVuY3Rpb24gYmVoYXZlcyBsaWtlIGEgc3RlcCBmdW5jdGlvbi4NCg0KSG93ZXZlcjoNCi0gKipJdCBjYW5ub3QgcHJvZHVjZSBuZWdhdGl2ZSBvdXRwdXRzKiosIGFzIGl0cyByYW5nZSBpcyBzdHJpY3RseSBiZXR3ZWVuIDAgYW5kIDEuDQoNCg0KDQo0LiAgVGhlIGNvcnJlY3QgYW5zd2VyIGlzOiAgDQoNCioqTG9nIGxvc3MgaGFzIHR3byB0ZXJtcy4qKiAgDQoNCi0tLQ0KDQojIyMgRXhwbGFuYXRpb246ICANCg0KMS4gKipMb2cgbG9zcyoqOiAgDQogICAtIFVzZWQgaW4gY2xhc3NpZmljYXRpb24gdGFza3MsIGVzcGVjaWFsbHkgZm9yIGxvZ2lzdGljIHJlZ3Jlc3Npb24uICANCiAgIC0gSXQgaXMgZGVmaW5lZCBhczogIA0KICAgICBcWw0KICAgICBcdGV4dHtMb2cgTG9zczogfSAtXGZyYWN7MX17bn0gXHN1bV97aT0xfV57bn0gXGxlZnRbIHlfaSBcbG9nKFxoYXR7eX1faSkgKyAoMSAtIHlfaSkgXGxvZygxIC0gXGhhdHt5fV9pKSBccmlnaHRdDQogICAgIFxdDQogICAtIFRoZSBmb3JtdWxhIGhhcyAqKnR3byB0ZXJtcyoqLCBvbmUgZm9yIFwoIHlfaSA9IDEgXCkgYW5kIG9uZSBmb3IgXCggeV9pID0gMCBcKSwgdG8gaGFuZGxlIHRoZSBiaW5hcnkgbmF0dXJlIG9mIHRoZSBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtLiAgDQoNCjIuICoqTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpIGFuZCBNZWFuIEFic29sdXRlIEVycm9yIChNQUUpKio6ICANCiAgIC0gVXNlZCBpbiByZWdyZXNzaW9uIHRhc2tzIGZvciBjb250aW51b3VzIG91dHB1dHMuDQogICAtIEJvdGggaGF2ZSBhIHNpbmdsZSB0ZXJtIHRoYXQgbWVhc3VyZXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcHJlZGljdGVkIHZhbHVlIGFuZCB0aGUgYWN0dWFsIHZhbHVlOiAgDQogICAgIC0gTVNFOiBcKCBcZnJhY3sxfXtufSBcc3VtX3tpPTF9XntufSAoeV9pIC0gXGhhdHt5fV9pKV4yIFwpICANCiAgICAgLSBNQUU6IFwoIFxmcmFjezF9e259IFxzdW1fe2k9MX1ee259IHx5X2kgLSBcaGF0e3l9X2l8IFwpICANCg0KLS0tDQoNCiMjIyBXaHkgTm90IHRoZSBPdGhlciBDaG9pY2VzPw0KMS4gKipMb2cgbG9zcyBpcyBub3QgY29udGludW91cyoqOiBGYWxzZS4gTG9nIGxvc3MgaXMgYSBjb250aW51b3VzIGZ1bmN0aW9uIG9mIGl0cyBpbnB1dHMuICANCjIuICoqTG9nIGxvc3MgaXMgbm90IGNvbnZleCoqOiBGYWxzZS4gTG9nIGxvc3MgaXMgYSBjb252ZXggZnVuY3Rpb24sIHdoaWNoIGVuc3VyZXMgb3B0aW1pemF0aW9uIGFsZ29yaXRobXMgY29udmVyZ2UgdG8gYSBnbG9iYWwgbWluaW11bS4gIA0KMy4gKipMb2cgbG9zcyBpcyBub3QgZGlmZmVyZW50aWFibGUqKjogRmFsc2UuIExvZyBsb3NzIGlzIGRpZmZlcmVudGlhYmxlLCBhbmQgZ3JhZGllbnQgZGVzY2VudCByZWxpZXMgb24gdGhpcyBwcm9wZXJ0eSBmb3Igb3B0aW1pemF0aW9uLiAgDQoNCg0KVGhlIGNvcnJlY3QgYW5zd2VyIGlzOg0KDQoqKkxvZyBsb3NzIGhhcyB0d28gdGVybXMuKioNCg0KIyMjIEV4cGxhbmF0aW9uOg0KMS4gKipMb2cgbG9zcyoqOg0KICAgLSBTcGVjaWZpY2FsbHkgZGVzaWduZWQgZm9yIGNsYXNzaWZpY2F0aW9uIHByb2JsZW1zLCBwYXJ0aWN1bGFybHkgYmluYXJ5IGNsYXNzaWZpY2F0aW9uLg0KICAgLSBJdCBoYXMgKip0d28gdGVybXMqKiwgb25lIGZvciB3aGVuIHRoZSBhY3R1YWwgY2xhc3MgaXMgMSBhbmQgYW5vdGhlciBmb3Igd2hlbiB0aGUgYWN0dWFsIGNsYXNzIGlzIDAuDQogICAtIFRoZXNlIHRlcm1zIGNvcnJlc3BvbmQgdG8gdGhlIG5lZ2F0aXZlIGxvZyBvZiBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBmb3IgdGhlIGNvcnJlY3QgY2xhc3MuDQoNCjIuICoqV2h5IHRoZSBvdGhlciBvcHRpb25zIGFyZSBpbmNvcnJlY3QqKjoNCiAgIC0gKipMb2cgbG9zcyBpcyBub3QgY29udGludW91cyoqOiBJbmNvcnJlY3QuIExvZyBsb3NzIGlzIGEgY29udGludW91cyBmdW5jdGlvbi4NCiAgIC0gKipMb2cgbG9zcyBpcyBub3QgY29udmV4Kio6IEluY29ycmVjdC4gTG9nIGxvc3MgaXMgY29udmV4LCB3aGljaCBlbnN1cmVzIHRoYXQgZ3JhZGllbnQgZGVzY2VudCBjYW4gZmluZCB0aGUgZ2xvYmFsIG1pbmltdW0uDQogICAtICoqTG9nIGxvc3MgaXMgbm90IGRpZmZlcmVudGlhYmxlKio6IEluY29ycmVjdC4gTG9nIGxvc3MgaXMgZGlmZmVyZW50aWFibGUsIGFsbG93aW5nIGZvciBvcHRpbWl6YXRpb24gdXNpbmcgZ3JhZGllbnQgZGVzY2VudC4NCg0KIyMjIEtleSBEaWZmZXJlbmNlIHdpdGggTVNFL01BRToNCi0gTWVhbiBzcXVhcmVkIGVycm9yIChNU0UpIGFuZCBtZWFuIGFic29sdXRlIGVycm9yIChNQUUpIG1lYXN1cmUgdGhlIGRpc3RhbmNlIGJldHdlZW4gcHJlZGljdGlvbnMgYW5kIHRhcmdldHMgZm9yICoqcmVncmVzc2lvbiB0YXNrcyoqLg0KLSBMb2cgbG9zcyBtZWFzdXJlcyB0aGUgcHJvYmFiaWxpdHkgYXNzaWduZWQgdG8gdGhlIGNvcnJlY3QgY2xhc3MgZm9yICoqY2xhc3NpZmljYXRpb24gdGFza3MqKi4gSXQgcGVuYWxpemVzIGluY29ycmVjdCBwcmVkaWN0aW9ucyBtb3JlIGhlYXZpbHkgYXMgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0eSBtb3ZlcyBmdXJ0aGVyIGF3YXkgZnJvbSB0aGUgdHJ1ZSBsYWJlbC4NCg0KDQoNCg0KNS4gVGhlIGNvcnJlY3QgYW5zd2VyIGlzOiAgDQoNCioqV2UgY2FuIHVzZSB0aGUgb25lIHZzLiByZXN0IG1ldGhvZC4qKiAgDQoNCi0tLQ0KDQojIyMgRXhwbGFuYXRpb246ICANCjEuICoqT25lLXZzLVJlc3QgTWV0aG9kKio6ICANCiAgIC0gSW4gbXVsdGljbGFzcyBjbGFzc2lmaWNhdGlvbiB3aXRoIGxvZ2lzdGljIHJlZ3Jlc3Npb24sIHRoZSAqKm9uZS12cy1yZXN0IChPdlIpKiogYXBwcm9hY2ggdHJhaW5zIGEgc2VwYXJhdGUgYmluYXJ5IGNsYXNzaWZpZXIgZm9yIGVhY2ggY2xhc3MuICANCiAgIC0gRm9yIGEgY2xhc3MgXCggQ19pIFwpLCB0aGUgbW9kZWwgcHJlZGljdHMgd2hldGhlciBhIGRhdGEgcG9pbnQgYmVsb25ncyB0byBcKCBDX2kgXCkgKDEpIG9yIG5vdCAoMCkuICANCiAgIC0gRHVyaW5nIGluZmVyZW5jZSwgdGhlIG1vZGVsIGNob29zZXMgdGhlIGNsYXNzIHdpdGggdGhlIGhpZ2hlc3QgcHJvYmFiaWxpdHkuDQoNCjIuICoqT25lLXZzLU9uZSBNZXRob2QqKjogIA0KICAgLSBXaGlsZSB0aGUgKipvbmUtdnMtb25lIChPdk8pKiogbWV0aG9kIGlzIHVzZWQgaW4gc29tZSBjbGFzc2lmaWNhdGlvbiB0YXNrcywgaXQgaXMgbm90IGRpcmVjdGx5IGFzc29jaWF0ZWQgd2l0aCBsb2dpc3RpYyByZWdyZXNzaW9uIHVzaW5nIHRoZSBzaWdtb2lkIGZ1bmN0aW9uLiBPdk8gaW52b2x2ZXMgY3JlYXRpbmcgYSBiaW5hcnkgY2xhc3NpZmllciBmb3IgZWFjaCBwYWlyIG9mIGNsYXNzZXMsIHdoaWNoIGNhbiBiZWNvbWUgY29tcHV0YXRpb25hbGx5IGV4cGVuc2l2ZSBhcyB0aGUgbnVtYmVyIG9mIGNsYXNzZXMgaW5jcmVhc2VzLg0KDQozLiAqKk11bHRpY2xhc3MgaXMgaW1wb3NzaWJsZSB3aXRoIGxvZyBsb3NzKio6ICANCiAgIC0gVGhpcyBpcyBmYWxzZS4gTXVsdGljbGFzcyBsb2dpc3RpYyByZWdyZXNzaW9uIGNhbiB1c2UgYSBnZW5lcmFsaXphdGlvbiBvZiBsb2cgbG9zcyAoZS5nLiwgc29mdG1heCBjcm9zcy1lbnRyb3B5KSB0byBoYW5kbGUgbXVsdGlwbGUgY2xhc3NlcyBkaXJlY3RseS4NCg0KLS0tIA0KDQojIyMgTXVsdGljbGFzcyB3aXRoIExvZ2lzdGljIFJlZ3Jlc3Npb246ICANCkZvciBhIGRpcmVjdCBtdWx0aWNsYXNzIGFwcHJvYWNoIChlLmcuLCAqKnNvZnRtYXggcmVncmVzc2lvbioqKTogIA0KLSBVc2UgdGhlIHNvZnRtYXggZnVuY3Rpb24gaW5zdGVhZCBvZiB0aGUgc2lnbW9pZCBmdW5jdGlvbiwgd2hpY2ggZ2VuZXJhbGl6ZXMgcHJvYmFiaWxpdHkgYXNzaWdubWVudCB0byBcKCBLIFwpIGNsYXNzZXMuICANCi0gVGhlIGxvZyBsb3NzIGluIHRoaXMgY2FzZSBpcyBhZGFwdGVkIGZvciBtdWx0aWNsYXNzIHByb2JsZW1zLg0KDQpUaGUgY29ycmVjdCBhbnN3ZXIgaXM6DQoNCioqV2UgY2FuIHVzZSB0aGUgb25lIHZzLiBvbmUgbWV0aG9kLioqICANCioqV2UgY2FuIHVzZSB0aGUgb25lIHZzLiByZXN0IG1ldGhvZC4qKg0KDQojIyMgRXhwbGFuYXRpb246DQoxLiAqKk9uZS12cy1SZXN0IChPbmUtdnMtQWxsKSoqOg0KICAgLSBGb3IgZWFjaCBjbGFzcywgYSBzZXBhcmF0ZSBjbGFzc2lmaWVyIGlzIHRyYWluZWQgdG8gZGlzdGluZ3Vpc2ggdGhhdCBjbGFzcyBmcm9tIGFsbCBvdGhlciBjbGFzc2VzLg0KICAgLSBUaGUgY2xhc3Mgd2l0aCB0aGUgaGlnaGVzdCBwcm9iYWJpbGl0eSBpcyBzZWxlY3RlZCBhcyB0aGUgcHJlZGljdGVkIGNsYXNzLg0KICAgLSBFeGFtcGxlOiBGb3IgY2xhc3NlcyBSZWQsIEdyZWVuLCBhbmQgQmx1ZToNCiAgICAgLSBSZWQgdnMuIE5vdCBSZWQNCiAgICAgLSBHcmVlbiB2cy4gTm90IEdyZWVuDQogICAgIC0gQmx1ZSB2cy4gTm90IEJsdWUNCg0KMi4gKipPbmUtdnMtT25lKio6DQogICAtIEEgY2xhc3NpZmllciBpcyB0cmFpbmVkIGZvciBldmVyeSBwYWlyIG9mIGNsYXNzZXMuDQogICAtIEVhY2ggcGFpcndpc2UgY29tcGFyaXNvbiBwcmVkaWN0cyBhIHdpbm5lciwgYW5kIHRoZSBjbGFzcyB3aXRoIHRoZSBtb3N0IHdpbnMgaXMgc2VsZWN0ZWQuDQogICAtIEV4YW1wbGU6IEZvciBjbGFzc2VzIFJlZCwgR3JlZW4sIGFuZCBCbHVlOg0KICAgICAtIFJlZCB2cy4gR3JlZW4NCiAgICAgLSBSZWQgdnMuIEJsdWUNCiAgICAgLSBHcmVlbiB2cy4gQmx1ZQ0KDQozLiAqKldoeSAiTXVsdGljbGFzcyBpcyBpbXBvc3NpYmxlIHdpdGggbG9nIGxvc3MiIGlzIGluY29ycmVjdCoqOg0KICAgLSBMb2cgbG9zcyBjYW4gYmUgZXh0ZW5kZWQgdG8gbXVsdGljbGFzcyBwcm9ibGVtcyB1c2luZyAqKnNvZnRtYXgqKiBpbnN0ZWFkIG9mIHNpZ21vaWQuIFRoaXMgaXMgY29tbW9uIGluIGZyYW1ld29ya3MgbGlrZSBuZXVyYWwgbmV0d29ya3MuDQoNCiMjIyBTdW1tYXJ5Og0KTXVsdGljbGFzcyBwcm9ibGVtcyBjYW4gYmUgaGFuZGxlZCB3aXRoIHNpZ21vaWQtYmFzZWQgbWV0aG9kcyBsaWtlIE9uZS12cy1SZXN0IG9yIE9uZS12cy1PbmUsIGFsdGhvdWdoIG90aGVyIHRlY2huaXF1ZXMgKGUuZy4sIHNvZnRtYXgpIGFyZSBvZnRlbiBtb3JlIGVmZmljaWVudCBmb3IgbGFyZ2VyIGNsYXNzIHNpemVzLg0K