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:
- Data Preparation:
- Import dataset from
sklearn.datasets
.
- Convert data to a DataFrame and add column names.
- Separate features (X) and targets (y).
- Logistic Regression:
- Use
LogisticRegressionCV
for cross-validation.
- Fit the model:
model.fit(X, y)
.
- Retrieve model coefficients with
model.coef_
.
- 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:
- Data Preprocessing:
- Handle missing data using imputation techniques (e.g., mean,
median).
- Normalize/scale features for better model performance.
- Model Development:
- Build a logistic regression model for each target class (multiclass
setup).
- Use cross-validation to evaluate performance.
- Feature Importance:
- Analyze the top 5 important features contributing to predictions
using model coefficients.
Mathematical and Coding Representations
Mathematical Representation
- Sigmoid Function:
\(\sigma(x) = \frac{1}{1 +
e^{-x}}\)
- Log Loss:
\(J = -\frac{1}{N} \sum_{i=1}^N \left[ y_i
\log(p_i) + (1-y_i) \log(1-p_i) \right]\)
- Gradient Update:
\(m_{new} = m_{old} - \alpha \frac{\partial
J}{\partial m}\)
Python Code Representation
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)
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))
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:
- Data Preprocessing:
- Handle missing values (e.g., mean/mode imputation).
- Encode categorical variables (e.g., one-hot encoding).
- 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.
- 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
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.
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.
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.
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.
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.
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
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?
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?
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?
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?
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?
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
- 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.
- 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
- 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?
- 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?
- 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?
“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.
“Categorical data cannot be used in regression”:
Incorrect. Categorical data can be used in regression after proper
encoding.
“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.
- 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?
“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.
“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.
“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:
Involves the sigmoid: The sigmoid function is
used to transform outputs into probabilities, but it is not directly
part of the slope update rule.
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.
Uses the log loss: Log loss is minimized during
training, but the slope update rule itself remains identical to that in
linear regression.
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.
- The correct answer is:
Log loss has two terms.
Explanation:
- 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.
- 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?
- Log loss is not continuous: False. Log loss is a
continuous function of its inputs.
- Log loss is not convex: False. Log loss is a convex
function, which ensures optimization algorithms converge to a global
minimum.
- 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:
- 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.
- 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.
- The correct answer is:
We can use the one vs. rest method.
Explanation:
- 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.
- 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.
- 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:
- 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
- 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
- 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