Overview
Evaluating classification models is a crucial aspect of supervised machine learning. It helps us understand how well our models are performing and guides us in improving their accuracy and effectiveness. There are several metrics used for this purpose, each with its own strengths and context of use. Some of the most commonly used metrics include:
Accuracy: This is the most intuitive performance measure and it is simply a ratio of correctly predicted observations to the total observations. It’s best used when the class distribution is similar and the costs of false positives and false negatives are roughly the same.
Precision: Precision is the ratio of correctly predicted positive observations to the total predicted positive observations. It’s important when the cost of false positives is high.
Recall (Sensitivity): Recall is the ratio of correctly predicted positive observations to all observations in actual class. It’s used when the cost of false negatives is high.
F1 Score: F1 Score is the weighted average of Precision and Recall. It takes both false positives and false negatives into account. It’s useful when you want to balance precision and recall.
Confusion Matrix: A confusion matrix is a table that is often used to describe the performance of a classification model on a set of test data for which the true values are known.
ROC Curve and AUC: The ROC curve is a graphical representation of the trade-off between the true positive rate and false positive rate at various thresholds. AUC represents the degree or measure of separability achieved by the model.
Specificity: Specificity measures the proportion of actual negatives that are correctly identified as such. It’s important in contexts where the cost of false positives is high.
Log Loss: Also known as logistic loss or logit loss, it measures the performance of a classification model where the prediction is a probability between 0 and 1.
Each of these metrics provides different insights into the performance of a classification model, and the choice of metrics often depends on the specific requirements and context of the problem being solved.
The labeling of the target variable is a decision made by the analyst, i.e., what is “positive” and what is “negative” is a business decision and is not of consequence. For example, in disease detection, the presence of a disease is commonly marked as “positive”, while the absence would be marked a “negative”. If we were to evaluate a model predicting whether a customer is likely to buy again, the “positive” might best be the outcome of “customer buys again”.
When classification is between two classes (“positive” and “negative”), we call this a binary classification. When there are more than two classes, then it is a multiclass (or multivariate or multilevel) classification. Some supervised machine learning algorithms are principally used for binary classification (_e.g. logistic regression), while others are inherently better for multiclass classification (e.g., kNN, decision tree, and random forest).
Model Evaluation
There are several methods for evaluating classification models. All methods involve, in some way, the training of a classification model using a supervised machine learning algorithm (e.g., kNN or logistic regression) on a labeled training data set. The most common methods are listed below; the most common among these are the holdout method and k-fold cross validation (kCV).
Besides the holdout method, several other techniques are used for validating supervised machine learning models. Each method has its advantages and is suited to different scenarios. Some of the most commonly used methods include:
- K-Fold Cross-Validation:
- Description: The dataset is divided into two parts: the training set and the testing (or holdout) set. The model is trained using the training data and evaluated using the testing data set.
- Advantages: Simple to apply.
- Disadvantages: The model and the evaluation can be greatly influenced by the choice of training data.
- K-Fold Cross-Validation:
- Description: The dataset is divided into ‘k’ equally (or nearly equally) sized folds or subsets. The model is trained ‘k’ times, each time using a different fold as the testing set and the remaining ‘k-1’ folds as the training set.
- Advantages: It ensures that every observation from the original dataset has the chance of appearing in the training and test set. This is especially useful with smaller datasets.
- Disadvantages: It can be computationally intensive, especially for large datasets or complex models.
- Stratified K-Fold Cross-Validation:
- Description: Similar to K-Fold, but the folds are made by preserving the percentage of samples for each class. This is crucial in dealing with imbalanced datasets.
- Advantages: Maintains a balanced representation of the original dataset, particularly important for classification problems with imbalanced class distributions.
- Disadvantages: More complex to implement than simple K-Fold cross-validation.
- Leave-One-Out Cross-Validation (LOOCV):
- Description: A special case of K-Fold cross-validation where ‘k’ equals the number of observations in the dataset. Essentially, each observation is used as a single test example, and the rest are used for training.
- Advantages: Maximizes the amount of data used for training the model.
- Disadvantages: Extremely computationally expensive and impractical with large datasets. It can also have high variance as a single observation can sometimes be a poor representation of the dataset.
- Leave-P-Out Cross-Validation:
- Description: Similar to LOOCV, but instead of leaving out one observation at a time, ‘p’ observations are left out.
- Advantages: Allows for more thorough testing than LOOCV in certain cases.
- Disadvantages: Computationally very intensive and less commonly used.
- Bootstrap Method:
- Description: Involves randomly sampling with replacement from the dataset to create multiple training datasets. The model is trained on these bootstrap samples and tested on the unseen instances.
- Advantages: Useful for estimating the distribution of a statistic (e.g., mean, variance) and provides a measure of uncertainty.
- Disadvantages: Can lead to overfitting if not implemented correctly, as it involves sampling with replacement.
- Time Series Split:
- Description: Specifically used for time series data. The dataset is split into a sequence of training and test sets, where each successive test set is ‘moved forward in time’.
- Advantages: Respects the temporal order of observations, which is critical in time-series analysis.
- Disadvantages: Not applicable to non-time-series datasets and can be sensitive to the period chosen for training and testing.
- Random Subsampling:
- Description: Similar to the holdout method, but the process is repeated multiple times with different random splits of the dataset into training and test sets.
- Advantages: Simpler and less computationally intensive than K-Fold cross-validation.
- Disadvantages: Less comprehensive and can have high variance depending on the splits.
Each of these methods offers a different approach to assessing the performance of a machine learning model, and the choice of method can depend on the specific characteristics of the data and the practical constraints of model training and evaluation.
Holdout Method
The holdout method is a simple and widely used technique for validating supervised machine learning models, particularly in classification tasks. This method involves splitting the dataset into two subsets: one for training the model and the other for testing its performance.
The process for evaluating a classification model obtained from a supervised machine learning algorithm generally follows these steps:
- Splitting the Dataset:
- The dataset is divided into two parts: the training set and the testing (or holdout) set.
- A common split ratio is 70% of the data for training and 30% for testing, but this can vary based on the dataset size and specific requirements.
- Training the Model:
- The model is trained exclusively on the training set. This set is used to fit the model parameters.
- Testing the Model:
- After training, the model is evaluated on the testing set. This set is not used during the training phase, so it provides an unbiased evaluation of the model.
- Performance metrics such as accuracy, precision, recall, F1 score, ROC-AUC, etc., are computed to assess the model’s performance.
Advantages of the Holdout Method
- Simplicity: It is straightforward and easy to implement.
- Speed: Less computationally expensive compared to methods like cross-validation, especially for large datasets.
Disadvantages
- Data Split Dependence: The performance estimate can be highly dependent on how the data is split. If the split is not representative of the overall dataset, it can lead to misleading performance estimates.
- Limited Data Utilization: Since a portion of the data is set aside for testing, it’s not used for training. This can be a drawback, especially with small datasets.
Example Scenario
Imagine you have a dataset of 10,000 images to build a model that classifies images as either cats or dogs. Using the holdout method, you might:
- Use 7,000 images to train the model.
- Reserve the remaining 3,000 images to test the model.
After training, you evaluate the model’s performance on the 3,000 test images. The accuracy, precision, recall, and other relevant metrics calculated from this test set give you an estimate of how well your model will perform on unseen data.
Best Practices
- Random Split: Ensure that the split between the training and testing sets is random. This helps in making the split representative of the whole dataset.
- Stratified Split: If the dataset is imbalanced (e.g., 90% cats and 10% dogs), use stratified sampling to maintain the same proportion in both training and testing sets.
- Iterative Approach: For more robust validation, consider using the holdout method in combination with techniques like cross-validation, especially when dealing with smaller datasets.
The holdout method, despite its simplicity, can be a powerful tool in model validation, provided it’s used correctly and the limitations are acknowledged.
k-Fold Cross Validation
K-Fold Cross-Validation is a robust method for assessing the performance of machine learning models, particularly useful for its ability to provide a more reliable estimate of model performance on unseen data.
Description of K-Fold Cross-Validation
- Splitting the Dataset:
- The entire dataset is divided into ‘k’ equal (or nearly equal) sized subsets or ‘folds’.
- Common choices for ‘k’ include 5 or 10, but the optimal number can depend on the size and specifics of the dataset.
- Model Training and Validation Process:
- The process is repeated ‘k’ times, with each of the ‘k’ folds used exactly once as the validation set.
- In each iteration, a different fold is treated as the validation set, and the remaining ‘k-1’ folds are combined to form the training set.
- The model is trained on the training set and evaluated on the validation set.
- After ‘k’ iterations, every data point has been used both for training and validation.
- Aggregating Results:
- The performance measure (e.g., accuracy, precision, recall) is calculated for each of the ‘k’ iterations.
- The final model performance is typically reported as the average of these ‘k’ performance measures.
Advantages
- Reduced Bias: Since every observation gets to be in a test set exactly once and in a training set ‘k-1’ times, it reduces bias compared to methods like the holdout method.
- Utilization of All Data: It allows for both training and testing on all available data, maximizing the use of data, which is particularly beneficial for smaller datasets.
- Robust Performance Estimate: Provides a more accurate and robust estimate of model performance, as it averages the results from ‘k’ iterations.
- Useful for Limited Data: Ideal for scenarios with limited data, where it’s essential to use the dataset efficiently.
Disadvantages
- Computational Cost: It can be computationally expensive, especially for large datasets and complex models, as it requires the model to be trained and evaluated ‘k’ times.
- Time-Consuming: The increased computational cost translates to longer training times, which can be a significant drawback in time-sensitive projects.
- Variance in Performance: The performance might still vary depending on how the data is split into folds, though less so than with the holdout method.
- Choice of ‘k’: Selecting the appropriate value of ‘k’ can be challenging. A larger ‘k’ provides less bias towards overestimating the true expected error (as each test set is smaller), but the variance of the resulting estimate can be higher.
Example
In a dataset with 100 observations, using 10-fold cross-validation would involve: - Splitting the data into 10 folds of 10 observations each. - In each iteration, 9 folds (90 observations) are used for training, and 1 fold (10 observations) is used for validation. - After 10 iterations, the performance metric (e.g., accuracy) for each iteration is averaged to provide an overall performance estimate.
K-Fold Cross-Validation is widely used due to its balance of efficiency and effectiveness, particularly in scenarios where the available data is limited and one needs to get the most reliable performance estimate possible from the dataset.
Accuracy
Accuracy is a fundamental metric in both binary and multiclass classification problems in supervised machine learning. It measures the proportion of correct predictions (both true positives and true negatives) made by the model out of all predictions.
Binary Classification
In binary classification, there are only two classes (often labeled as positive and negative, or 1 and 0).
Example
Imagine a medical test for a disease where: - 90 people are correctly identified as having the disease (TP). - 900 people are correctly identified as not having the disease (TN). - 10 people are incorrectly identified as having the disease (FP). - 100 people are incorrectly identified as not having the disease (FN).
The accuracy of this test is calculated as:
\[ \text{Accuracy} = \frac{90 + 900}{90 + 900 + 10 + 100} = \frac{990}{1100} \approx 0.90 \]
This means the test correctly identifies the disease status 90% of the time.
Multiclass Classification
In multiclass classification, there are more than two classes.
Example
Consider a classification problem with three classes (A, B, and C) and a dataset with the following results: - Class A: 30 correct predictions, 5 incorrect predictions. - Class B: 40 correct predictions, 15 incorrect predictions. - Class C: 50 correct predictions, 10 incorrect predictions.
The total number of observations is \(30 + 5 + 40 + 15 + 50 + 10 = 150\).
The accuracy of the model is:
\[ \text{Accuracy} = \frac{30 + 40 + 50}{150} = \frac{120}{150} = 0.80 \]
This means the model correctly predicts the class 80% of the time.
Key Points
- Binary Classification: Accuracy is straightforward, focusing on TP and TN out of all observations.
- Multiclass Classification: Accuracy considers correct predictions across all classes.
- Limitations: Accuracy can be misleading in imbalanced datasets where one class significantly outweighs others. In such cases, other metrics like precision, recall, and F1 score might provide a more nuanced understanding of the model’s performance.
Accuracy is a good initial indicator of model performance but should be used alongside other metrics for a comprehensive evaluation, especially in cases of class imbalance or when the costs of different types of errors vary significantly.
Precision
Precision is a common and often used metric in the evaluation of classification models, especially in scenarios where the cost of false positives (incorrectly predicting the positive class) is high. It gives us insight into the accuracy of the positive predictions made by the model.
Definition of Precision
Precision is defined as the ratio of correctly predicted positive observations to the total predicted positive observations. In simpler terms, it answers the question: “Of all the instances the model labeled as positive, how many were actually positive?”
Calculating Precision
The formula for precision is:
\[ \text{Precision} = \frac{\text{True Positives (TP)}}{\text{True Positives (TP) + False Positives (FP)}} \]
where,
- True Positives (TP) are the instances correctly predicted as positive
- False Positives (FP) are the instances incorrectly predicted as positive
Interpretation
- A precision of 1.0 means that every item labeled as positive is indeed positive (but says nothing about the items labeled as negative).
- A lower precision indicates a high number of false positives among the labeled positives.
Common Use
Precision is particularly important in fields where the cost of a false positive is high. For example, in email spam detection, a false positive (labeling a good email as spam) is more problematic than a false negative (failing to identify a spam email). Similarly, in medical testing, falsely diagnosing a healthy patient with a disease could be more critical than missing the disease in its early stages.
Examples
Example I: Email Spam Filtering
Context: In email spam filtering systems, the goal is to identify and filter out spam emails while ensuring legitimate emails reach the user’s inbox.
- True Positive (TP): A spam email correctly identified as spam.
- False Positive (FP): A legitimate email incorrectly identified as spam (this is particularly undesirable as it could lead to missing important emails).
Precision in this Scenario: - High precision means that most of the emails identified as spam are indeed spam, minimizing the risk of important emails being wrongly filtered out. - If a spam filter has a precision of 0.95, it means that 95% of the emails it marks as spam are actually spam, and only 5% are legitimate emails mistakenly identified as spam.
Importance: - In email filtering, users typically prefer to receive a few spam emails in their inbox rather than miss an important legitimate email. Therefore, maintaining high precision is crucial to avoid the inconvenience and potential loss caused by missing important emails.
Example II: Medical Diagnosis for a Serious Disease
Context: Consider a medical test designed to diagnose a serious, potentially life-threatening disease.
- True Positive (TP): Correctly identifying a patient with the disease.
- False Positive (FP): Incorrectly diagnosing a healthy person with the disease.
Precision in this Scenario:
- High precision indicates that a high proportion of patients diagnosed with the disease actually have the disease.
- For instance, if a test has a precision of 0.90, it implies that 90% of the diagnosed cases are true positives, whereas 10% are false positives.
Importance:
- In medical diagnostics, especially for serious conditions, a false positive can lead to unnecessary stress, further invasive testing, and potentially harmful treatment for the patient. Therefore, having a high precision rate is crucial to minimize these risks.
In both examples, while high precision is desirable, it is also essential to balance it with other metrics like recall, especially in medical scenarios where missing a true case (high recall) can be as critical as avoiding false alarms (high precision). These examples highlight the importance of precision in contexts where the consequences of false positives are significant.
Precision in Multivariate Classification
In a multiclass (or multivariate) classification scenario, where there are more than two possible outcomes, precision is calculated for each class separately and then can be averaged to obtain an overall precision. This process involves considering each class as the “positive” class (of interest) and all other classes as “negative” (not of interest) for the purpose of the calculation.
Steps to Calculate Precision in Multiclass Classification
- Calculate Precision for Each Class:
- For each class, calculate precision as: \[ \text{Precision}_{\text{class}} = \frac{\text{True Positives (TP)}_{\text{class}}}{\text{True Positives (TP)}_{\text{class}} + \text{False Positives (FP)}_{\text{class}}} \]
- Here, TP for a class is the number of times the class was correctly predicted, and FP is the number of times other classes were incorrectly predicted as this class.
- Average the Precision Scores:
- Macro-average Precision: Calculate the average of the precision scores for each class. This treats all classes equally, regardless of their frequency in the dataset. \[ \text{Macro-average Precision} = \frac{\sum \text{Precision}_{\text{class}}}{\text{Number of classes}} \]
- Weighted-average Precision: Calculate the average of precision scores for each class, weighted by the number of true instances for each class. This accounts for class imbalance. \[ \text{Weighted-average Precision} = \sum \left( \frac{\text{Number of true instances in class}}{\text{Total number of instances}} \times \text{Precision}_{\text{class}} \right) \]
Example Calculation
Imagine a classification problem with three classes: A, B, and C. Let’s say we have the following counts:
- Class A: \(\text{TP}_A = 30, \text{FP}_A = 10\)
- Class B: \(\text{TP}_B = 40, \text{FP}_B = 20\)
- Class C: \(\text{TP}_C = 50, \text{FP}_C = 5\)
The precision for each class would be:
- Precision for Class A: \(\text{Precision}_A = \frac{30}{30 + 10} = 0.75\)
- Precision for Class B: \(\text{Precision}_B = \frac{40}{40 + 20} = 0.67\)
- Precision for Class C: \(\text{Precision}_C = \frac{50}{50 + 5} = 0.91\)
Then, the macro-average precision across all classes would be:
\[ \text{Macro-average Precision} = \frac{0.75 + 0.67 + 0.91}{3} \approx 0.78 \]
And if we were to calculate weighted-average precision (assuming equal distribution of true instances among classes for simplicity), it would be similar in this case.
Using these methods, you can account for the performance of a multiclass classification model in correctly identifying each class while considering the specific importance or frequency of each class.
Recall
Recall, also known as sensitivity, is an important metric in classification problems, used to measure the proportion of actual positives that are correctly identified by the classification model.
Recall in Binary Classification
In binary classification, there are two possible outcomes: positive and negative.
Example
Consider a medical test to identify a disease:
- The test correctly identifies 80 patients with the disease (TP).
- 20 patients with the disease are missed by the test (FN).
- The total number of actual patients with the disease is 100 (80 TP + 20 FN).
The recall of this test is:
\[ \text{Recall} = \frac{80}{80 + 20} = \frac{80}{100} = 0.80 \]
This means that the test correctly identifies 80% of the patients who actually have the disease.
Recall in Multiclass Classification
In multiclass classification, there are more than two possible outcomes.
Example
Consider a classification problem with three classes (A, B, C) with the following results: - Class A: 30 TP, 5 FN. - Class B: 40 TP, 10 FN. - Class C: 50 TP, 20 FN.
Recall for each class would be: - Class A: \(\text{Recall}_A = \frac{30}{30 + 5} = 0.86\) - Class B: \(\text{Recall}_B = \frac{40}{40 + 10} = 0.80\) - Class C: \(\text{Recall}_C = \frac{50}{50 + 20} = 0.71\)
The macro-average recall across all classes would be the average of these three values.
Key Points
- Binary Classification: Recall measures the proportion of actual positives correctly identified.
- Multiclass Classification: Recall is computed for each class individually and then averaged.
- Importance: High recall indicates a lower number of false negatives. It is particularly important in scenarios like medical diagnosis, where missing an actual positive case (a disease) can be critical.
Recall is a valuable metric, especially when the consequences of false negatives are significant. However, it should be balanced with other metrics like precision and accuracy for a well-rounded model evaluation.
F1 Score
The F1 score is a metric that combines precision and recall into a single number, providing a balanced measure of a model’s accuracy, especially when dealing with imbalanced datasets.
F1 Score in Binary Classification
In binary classification, where outcomes are labeled as positive or negative, the F1 score is particularly useful.
Example
Consider a binary classification task: - Precision = 0.75 (75% of the predicted positives are correct) - Recall = 0.60 (60% of actual positives are correctly identified)
The F1 score would be:
\[ \text{F1 Score} = 2 \times \frac{0.75 \times 0.60}{0.75 + 0.60} \approx 0.67 \]
F1 Score in Multiclass Classification
In multiclass classification, the F1 score needs to be calculated for each class and then averaged.
Steps to Calculate F1 Score
- Calculate Precision and Recall for Each Class: Treat each class as the positive class and calculate precision and recall.
- Calculate F1 Score for Each Class: Use the formula for each class individually.
- Average the F1 Scores: You can use either:
- Macro-average: Simply average the F1 scores of all classes.
- Weighted-average: Average the F1 scores, weighted by the number of true instances for each class.
Example
Let’s say we have a classification problem with three classes (A, B, and C), and we calculated the following precision and recall for each class: - Class A: Precision = 0.80, Recall = 0.70 - Class B: Precision = 0.60, Recall = 0.50 - Class C: Precision = 0.90, Recall = 0.85
The F1 scores for each class would be: - Class A: \(\text{F1}_A = 2 \times \frac{0.80 \times 0.70}{0.80 + 0.70} \approx 0.74\) - Class B: \(\text{F1}_B = 2 \times \frac{0.60 \times 0.50}{0.60 + 0.50} \approx 0.55\) - Class C: \(\text{F1}_C = 2 \times \frac{0.90 \times 0.85}{0.90 + 0.85} \approx 0.87\)
The macro-average F1 score would be the average of these values.
Key Points
- The F1 score provides a balance between precision and recall, being particularly useful in situations where there is an imbalance in the dataset or when false positives and false negatives carry different costs.
- In binary classification, it’s straightforward as it directly combines the model’s precision and recall.
- In multiclass classification, the F1 score is calculated for each class and then averaged, providing a comprehensive view of the model’s performance across all classes.
The F1 score is a useful metric in many scenarios, as it accounts for both the precision and recall of the model, providing a more holistic view of its performance.
Confusion Matrix
A confusion matrix is a tool used in supervised learning to visualize the performance of a classification model. It’s particularly useful for understanding the types of errors a model is making.
Confusion Matrix in Binary Classification
In binary classification, the confusion matrix is a 2x2 table that shows the number of true positives, true negatives, false positives, and false negatives.
Components of a Binary Confusion Matrix
- True Positives (TP): Correctly predicted positive cases.
- True Negatives (TN): Correctly predicted negative cases.
- False Positives (FP): Incorrectly predicted positive cases (Type I error).
- False Negatives (FN): Incorrectly predicted negative cases (Type II error).
Example
Imagine a medical test for a disease: - 50 patients have the disease and the test correctly identifies 40 (TP = 40). - 100 patients do not have the disease and the test correctly identifies 90 (TN = 90). - The test incorrectly identifies 10 healthy patients as having the disease (FP = 10). - The test fails to identify the disease in 10 patients who have it (FN = 10).
The confusion matrix would look like this:
Actual Positive |
40 (TP) |
10 (FN) |
Actual Negative |
10 (FP) |
90 (TN) |
Confusion Matrix in Multiclass Classification
In multiclass classification, the confusion matrix is larger, with dimensions equal to the number of classes. Each row represents the instances in an actual class, and each column represents the instances in a predicted class.
Steps to Calculate a Multiclass Confusion Matrix
- Determine the Number of Classes: Suppose there are N classes.
- Create an NxN Matrix: Each cell (i, j) in the matrix represents the number of instances of class i (actual) predicted as class j.
Example
Consider a classification problem with three classes: A, B, and C. After applying the model on a dataset, we get the following results:
- 30 of Class A were correctly classified (TP for A), 5 were classified as B, and 5 as C.
- 4 of Class B were incorrectly classified as A, 40 were correctly classified (TP for B), and 6 as C.
- 6 of Class C were classified as A, 8 as B, and 46 were correctly classified (TP for C).
The confusion matrix would look like this:
Actual A |
30 |
5 |
5 |
Actual B |
4 |
40 |
6 |
Actual C |
6 |
8 |
46 |
Key Points
- In binary classification, the confusion matrix is a simple 2x2 table, whereas, in multiclass classification, it expands to accommodate all classes.
- The diagonal cells (top-left to bottom-right) represent the number of correct predictions (true positives for each class).
- Off-diagonal cells show the distribution of errors, indicating which classes are being confused with others.
A confusion matrix provides a detailed breakdown of a model’s performance and is especially useful for identifying whether a model is confusing two classes, which can be crucial for improving model accuracy.
Specificity
Specificity is a metric used in classification tasks to measure the proportion of actual negatives that are correctly identified. It’s particularly important in contexts where the cost of false positives is high.
Specificity in Binary Classification
In binary classification, where there are only two classes (positive and negative), specificity is straightforward.
Example
Consider a medical test for a disease: - 100 healthy individuals are tested, 90 of whom are correctly identified as not having the disease (TN = 90). - 10 healthy individuals are incorrectly identified as having the disease (FP = 10).
The specificity of this test is:
\[ \text{Specificity} = \frac{90}{90 + 10} = \frac{90}{100} = 0.90 \]
This means the test correctly identifies 90% of healthy individuals.
Specificity in Multiclass Classification
In multiclass classification, specificity is calculated for each class by considering it as the “negative” class while grouping all other classes as “positive.”
Steps to Calculate Specificity for Each Class
- Treat Each Class as Negative Once: For each class, calculate TN and FP with respect to that class.
- Calculate Specificity for Each Class: Use the binary specificity formula for each class.
- Average the Specificity Scores: You can calculate an average or weighted-average specificity, similar to precision and recall.
Example
Consider a classification problem with three classes (A, B, and C). After applying the model, we have the following confusion matrix:
Actual A |
30 (TP for A) |
5 (FP for C) |
5 (FP for C) |
Actual B |
4 (FP for A) |
40 (TP for B) |
6 (FP for C) |
Actual C |
6 (FP for A) |
8 (FP for B) |
46 (TP for C) |
To calculate specificity for each class: - For Class A: Consider B and C predictions as “positive” and A as “negative”. - For Class B: Consider A and C predictions as “positive” and B as “negative”. - For Class C: Consider A and B predictions as “positive” and C as “negative”.
Then calculate specificity for each class using the binary formula.
Key Points
- Binary Classification: Specificity measures the accuracy in identifying actual negatives.
- Multiclass Classification: Specificity is calculated for each class by treating it as the “negative” class once.
- Importance: Specificity is crucial in scenarios where false positives carry significant consequences.
Specificity is often used alongside sensitivity (recall) to provide a comprehensive view of a model’s performance, especially in medical testing, where distinguishing between healthy and diseased individuals accurately is vital.
Precision vs Specificity
Precision and specificity are both metrics used in classification tasks, but they focus on different aspects of the model’s performance.
Precision
- Definition: Precision measures the accuracy of the positive predictions made by the model. It is the ratio of true positives (correct positive predictions) to the total number of positive predictions made by the model (both true positives and false positives).
- Formula: \[ \text{Precision} = \frac{\text{True Positives (TP)}}{\text{True Positives (TP) + False Positives (FP)}} \]
- Use: Precision is used when the cost of a false positive is high. In scenarios where it’s critical not to label a negative instance as positive, precision becomes a key metric.
- Example: In email spam detection, precision is important. If a spam filter has low precision, it means many legitimate emails are incorrectly marked as spam (false positives), which could lead to important emails being missed.
Specificity
- Definition: Specificity measures the ability of the model to correctly identify negatives. It is the ratio of true negatives (correct negative predictions) to the total number of actual negative instances (both true negatives and false positives).
- Formula: \[ \text{Specificity} = \frac{\text{True Negatives (TN)}}{\text{True Negatives (TN) + False Positives (FP)}} \]
- Use: Specificity is used when it is crucial to correctly identify negative cases. It’s important in cases where missing a negative can have serious consequences.
- Example: In a medical test for a rare but serious disease, specificity is crucial. A low specificity means many healthy individuals are incorrectly diagnosed with the disease (false positives), leading to unnecessary stress and potentially harmful treatments.
Key Differences
- Focus: Precision focuses on the proportion of correct positive predictions out of all positive predictions, while specificity focuses on correctly identifying negative cases.
- False Positives: Precision is affected by false positives in the context of positive predictions, whereas specificity is affected by false positives in the context of negative cases.
- Scenarios: Precision is key in scenarios where wrongly labeling negatives as positives is problematic, whereas specificity is key in scenarios where failing to identify true negatives is problematic.
Precision and specificity address different aspects of a model’s performance. Precision is about how many selected items are relevant, while specificity is about how many relevant items are selected, particularly in the context of negatives. Depending on the application and the consequences of different types of errors (false positives vs. false negatives), one may prioritize one metric over the other.
ROC Curve and AUC
The Receiver Operating Characteristic (ROC) curve and the Area Under the Curve (AUC) are powerful tools used for evaluating the performance of classification models, particularly in binary classification. They can also be adapted for multiclass classification.
ROC Curve and AUC in Binary Classification
ROC Curve
- What It Represents: The ROC curve is a graphical representation that illustrates the diagnostic ability of a binary classifier as its discrimination threshold is varied.
- Plot Components: The ROC curve plots the True Positive Rate (TPR, or Recall) against the False Positive Rate (FPR) at various threshold settings.
- True Positive Rate (TPR): TPR = TP / (TP + FN)
- False Positive Rate (FPR): FPR = FP / (FP + TN)
AUC (Area Under the ROC Curve)
- What It Represents: The AUC provides an aggregate measure of the model’s performance across all possible classification thresholds. It ranges from 0 to 1, with a higher AUC indicating better model performance.
- Interpretation:
- An AUC of 0.5 suggests no discriminative ability (equivalent to random guessing).
- An AUC of 1.0 suggests perfect classification.
Example
Consider a medical test for a disease: - By adjusting the threshold for what counts as a positive prediction, you generate different sets of TPR and FPR, plotting these on the ROC curve. - If the test is highly accurate, the ROC curve will bow towards the top left corner of the plot, indicating high TPR and low FPR. - If the AUC is close to 1, it suggests the test is highly effective at distinguishing between patients with and without the disease.
ROC Curve and AUC in Multiclass Classification
In multiclass classification, the ROC curve and AUC are extended to handle multiple classes through a few different methods:
One-vs-Rest (OvR) Approach
- Method: Treat each class as a binary classification (the class versus all other classes).
- Calculation: Calculate the ROC curve and AUC for each class separately, then average the results.
- This could be a simple average (macro-average) or a weighted average based on the prevalence of each class.
One-vs-One (OvO) Approach
- Method: For N classes, construct ROC curves for each pair of classes.
- Calculation: Average the AUCs from these ROC curves.
Example
Imagine a classification problem with three classes (A, B, and C). For the OvR method: - Calculate the ROC curve and AUC treating A as the positive class and B+C as the negative class, then repeat for B and C. - Average these AUC scores to get a single performance measure.
Key Points
- Binary Classification: ROC and AUC provide a comprehensive measure of model performance across all thresholds.
- Multiclass Classification: Extended through OvR or OvO approaches to handle multiple classes.
- Usefulness: These metrics are particularly useful for evaluating and comparing models, especially when dealing with imbalanced datasets or when the costs of different types of errors vary significantly.
Log Loss
Log Loss, also known as logistic loss or cross-entropy loss, is a performance metric that measures the accuracy of a classifier. It’s a probability-based metric, offering a more nuanced view of model performance, especially when the outputs are probabilities.
Log Loss in Binary Classification
In binary classification, log loss measures the uncertainty of the probability estimates by penalizing false classifications.
Example
Consider a binary classification task with 3 samples, and the model outputs the following probabilities and actual labels:
- Sample 1: Predicted probability for class 1 = 0.9, Actual label = 1
- Sample 2: Predicted probability for class 1 = 0.3, Actual label = 0
- Sample 3: Predicted probability for class 1 = 0.6, Actual label = 1
The log loss would be calculated as:
\[ \text{Log Loss} = - \frac{1}{3} [(1 \cdot \log(0.9) + (1 - 1) \cdot \log(1 - 0.9)) + (0 \cdot \log(0.3) + (1 - 0) \cdot \log(1 - 0.3)) + (1 \cdot \log(0.6) + (1 - 1) \cdot \log(1 - 0.6))] \]
Log Loss in Multiclass Classification
In multiclass classification, log loss is extended to cover multiple classes.
Example
Consider a classification problem with 3 samples and 3 classes:
- Sample 1: Predicted probabilities = [0.7, 0.2, 0.1], Actual class = 1
- Sample 2: Predicted probabilities = [0.1, 0.8, 0.1], Actual class = 2
- Sample 3: Predicted probabilities = [0.2, 0.2, 0.6], Actual class = 3
The log loss is calculated for each class and then averaged.
Key Points
- Binary Classification: Log loss provides a measure of how close the predicted probabilities are to the actual labels.
- Multiclass Classification: The concept is extended to multiple classes, summing over all classes for each sample.
- Interpretation: Lower log loss values indicate better model performance, with a log loss of 0 representing perfect predictions.
- Usefulness: Log loss is particularly useful when the output of the model is a probability, giving insight into the uncertainty of the predictions.
Sample Implementation in R
# Loading dataset using url
url1 <- "https://drive.google.com/uc?export=download&id=12gzLfZBMSl2d-sF63D-qZJbCEocUdTKK"
df.wine1 <- read.csv(file = url1, header = TRUE, sep = ";")
df.wine1 <- df.wine1[,c(1, 2, 3, 4, 12)]
library(scales)
# Performing min-max transformation on my dataset without outliers
quality <- df.wine1$quality
wine.1 <- df.wine1[,-5]
#str(win)
normaliz <- function(colum){
normalized <- scales::rescale(colum, to=c(0,1))
}
for (x in colnames(wine.1)){
normalized.column <- normaliz(wine.1[, x])
# here the column is getting normalized above but
# we have to assign it to the data frame to make changes
wine.1[,x] <- normalized.column
}
wine.1$quality <- quality
summary(wine.1)
## fixed.acidity volatile.acidity citric.acid residual.sugar quality
## Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.00000 Min. :3.000
## 1st Qu.:0.2404 1st Qu.:0.1275 1st Qu.:0.1627 1st Qu.:0.01687 1st Qu.:5.000
## Median :0.2885 Median :0.1765 Median :0.1928 Median :0.07055 Median :6.000
## Mean :0.2937 Mean :0.1944 Mean :0.2013 Mean :0.08883 Mean :5.878
## 3rd Qu.:0.3365 3rd Qu.:0.2353 3rd Qu.:0.2349 3rd Qu.:0.14264 3rd Qu.:6.000
## Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.00000 Max. :9.000
wine.1$quality <- as.factor(wine.1$quality)
# Splitting the dataset into training and testing subsets
set.seed(101)
N = nrow(wine.1)
split <- 0.7
rows <- sample(nrow(wine.1))
training_ind <- sample(1:N, size = round(N * split),replace = FALSE)
train <- wine.1[training_ind,]
test <- wine.1[-training_ind,]
train_labels <- train$quality
test_labels <- test$quality
length(train_labels)
## [1] 3429
## [1] 1469 5
## [1] 3429 5
library(class)
# kNN model
# The 5th column is the quality column
knn_model <- knn(train = train[,-5], test = test[,-5], cl= train_labels, k=3)
# Evaluating model
confusion_matrix <- table(knn_model, test_labels)
confusion_matrix
## test_labels
## knn_model 3 4 5 6 7 8 9
## 3 0 0 1 1 0 0 0
## 4 0 7 14 7 5 5 0
## 5 1 22 194 137 39 12 0
## 6 7 13 179 380 103 24 1
## 7 0 6 39 104 112 5 1
## 8 0 1 9 20 11 8 1
## 9 0 0 0 0 0 0 0
accuracy <- sum(diag(confusion_matrix)) / sum(confusion_matrix)
accuracy
## [1] 0.4771954
precision.per.class <- c(ncol(confusion_matrix))
for (col in 1:ncol(confusion_matrix)) {
precision.per.class[col] <- confusion_matrix[col,col] /
sum(confusion_matrix[1:nrow(confusion_matrix),col])
}
precision <- mean(precision.per.class)
Summary
In this lesson, we discussed various aspects of evaluating classification models in supervised machine learning, focusing particularly on accuracy, precision, recall, and F1 score and how to interpret the outcomes for these metrics in relation to each other.
LS0tCnRpdGxlOiAiRXZhbHVhdGluZyBDbGFzc2lmaWNhdGlvbiBNb2RlbHMiCnBhcmFtczoKICBjYXRlZ29yeTogMwogIHN0YWNrczogMAogIG51bWJlcjogMjEyCiAgdGltZTogNDAKICBsZXZlbDogYmVnaW5uZXIKICB0YWdzOiBrbm4scHJlY2lzaW9uLHJlY2FsbCxzcGVjaWZpY2l0eSxGMS1TY29yZSxGMSxjbGFzc2lmaWNhdGlvbixhY2N1cmFjeQogIGRlc2NyaXB0aW9uOiAiRXhwbGFpbnMgY29tbW9uIGV2YWx1YXRpb24gbWV0cmljcyBmb3IgY2xhc3NpZmljYXRpb24gbW9kZWxzLAogICAgICAgICAgICAgICAgaW5jdWRpbmcgYWNjdXJhY3ksIHByZWNpc2lvbiwgcmVjYWxsLCBzZW5zaXRpdml0eSwgRjEgU2NvcmUsCiAgICAgICAgICAgICAgICBhbW9uZyBvdGhlcnMuIFNob3dzIGhvdyB0byBjYWxjdWxhdGUgdGhlc2UgbWV0cmljcyBhbmQgaG93CiAgICAgICAgICAgICAgICB0byBpbnRlcnByZXQgdGhlIHJlc3VsdHMuIgpkYXRlOiAiPHNtYWxsPmByIFN5cy5EYXRlKClgPC9zbWFsbD4iCmF1dGhvcjogIjxzbWFsbD5NYXJ0aW4gU2NoZWRsYmF1ZXI8L3NtYWxsPiIKZW1haWw6ICJtLnNjaGVkbGJhdWVyQG5ldS5lZHUiCmFmZmlsaXRhdGlvbjogIk5vcnRoZWFzdGVybiBVbml2ZXJzaXR5IgpvdXRwdXQ6IAogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0aGVtZTogcmVhZGFibGUKICAgIGhpZ2hsaWdodDogdGFuZ28KLS0tCgotLS0KdGl0bGU6ICI8c21hbGw+YHIgcGFyYW1zJGNhdGVnb3J5YC5gciBwYXJhbXMkbnVtYmVyYDwvc21hbGw+PGJyLz48c3BhbiBzdHlsZT0nY29sb3I6ICMyRTQwNTM7IGZvbnQtc2l6ZTogMC45ZW0nPmByIHJtYXJrZG93bjo6bWV0YWRhdGEkdGl0bGVgPC9zcGFuPiIKLS0tCgpgYGB7ciBjb2RlPXhmdW46OnJlYWRfdXRmOChwYXN0ZTAoaGVyZTo6aGVyZSgpLCcvUi9faW5zZXJ0MkRCLlInKSksIGluY2x1ZGUgPSBGQUxTRX0KYGBgCgojIyBPdmVydmlldwoKRXZhbHVhdGluZyBjbGFzc2lmaWNhdGlvbiBtb2RlbHMgaXMgYSBjcnVjaWFsIGFzcGVjdCBvZiBzdXBlcnZpc2VkIG1hY2hpbmUgbGVhcm5pbmcuIEl0IGhlbHBzIHVzIHVuZGVyc3RhbmQgaG93IHdlbGwgb3VyIG1vZGVscyBhcmUgcGVyZm9ybWluZyBhbmQgZ3VpZGVzIHVzIGluIGltcHJvdmluZyB0aGVpciBhY2N1cmFjeSBhbmQgZWZmZWN0aXZlbmVzcy4gVGhlcmUgYXJlIHNldmVyYWwgbWV0cmljcyB1c2VkIGZvciB0aGlzIHB1cnBvc2UsIGVhY2ggd2l0aCBpdHMgb3duIHN0cmVuZ3RocyBhbmQgY29udGV4dCBvZiB1c2UuIFNvbWUgb2YgdGhlIG1vc3QgY29tbW9ubHkgdXNlZCBtZXRyaWNzIGluY2x1ZGU6CgoxLiAgKipBY2N1cmFjeSoqOiBUaGlzIGlzIHRoZSBtb3N0IGludHVpdGl2ZSBwZXJmb3JtYW5jZSBtZWFzdXJlIGFuZCBpdCBpcyBzaW1wbHkgYSByYXRpbyBvZiBjb3JyZWN0bHkgcHJlZGljdGVkIG9ic2VydmF0aW9ucyB0byB0aGUgdG90YWwgb2JzZXJ2YXRpb25zLiBJdCdzIGJlc3QgdXNlZCB3aGVuIHRoZSBjbGFzcyBkaXN0cmlidXRpb24gaXMgc2ltaWxhciBhbmQgdGhlIGNvc3RzIG9mIGZhbHNlIHBvc2l0aXZlcyBhbmQgZmFsc2UgbmVnYXRpdmVzIGFyZSByb3VnaGx5IHRoZSBzYW1lLgoKMi4gICoqUHJlY2lzaW9uKio6IFByZWNpc2lvbiBpcyB0aGUgcmF0aW8gb2YgY29ycmVjdGx5IHByZWRpY3RlZCBwb3NpdGl2ZSBvYnNlcnZhdGlvbnMgdG8gdGhlIHRvdGFsIHByZWRpY3RlZCBwb3NpdGl2ZSBvYnNlcnZhdGlvbnMuIEl0J3MgaW1wb3J0YW50IHdoZW4gdGhlIGNvc3Qgb2YgZmFsc2UgcG9zaXRpdmVzIGlzIGhpZ2guCgozLiAgKipSZWNhbGwgKFNlbnNpdGl2aXR5KSoqOiBSZWNhbGwgaXMgdGhlIHJhdGlvIG9mIGNvcnJlY3RseSBwcmVkaWN0ZWQgcG9zaXRpdmUgb2JzZXJ2YXRpb25zIHRvIGFsbCBvYnNlcnZhdGlvbnMgaW4gYWN0dWFsIGNsYXNzLiBJdCdzIHVzZWQgd2hlbiB0aGUgY29zdCBvZiBmYWxzZSBuZWdhdGl2ZXMgaXMgaGlnaC4KCjQuICAqKkYxIFNjb3JlKio6IEYxIFNjb3JlIGlzIHRoZSB3ZWlnaHRlZCBhdmVyYWdlIG9mIFByZWNpc2lvbiBhbmQgUmVjYWxsLiBJdCB0YWtlcyBib3RoIGZhbHNlIHBvc2l0aXZlcyBhbmQgZmFsc2UgbmVnYXRpdmVzIGludG8gYWNjb3VudC4gSXQncyB1c2VmdWwgd2hlbiB5b3Ugd2FudCB0byBiYWxhbmNlIHByZWNpc2lvbiBhbmQgcmVjYWxsLgoKNS4gICoqQ29uZnVzaW9uIE1hdHJpeCoqOiBBIGNvbmZ1c2lvbiBtYXRyaXggaXMgYSB0YWJsZSB0aGF0IGlzIG9mdGVuIHVzZWQgdG8gZGVzY3JpYmUgdGhlIHBlcmZvcm1hbmNlIG9mIGEgY2xhc3NpZmljYXRpb24gbW9kZWwgb24gYSBzZXQgb2YgdGVzdCBkYXRhIGZvciB3aGljaCB0aGUgdHJ1ZSB2YWx1ZXMgYXJlIGtub3duLgoKNi4gICoqUk9DIEN1cnZlIGFuZCBBVUMqKjogVGhlIFJPQyBjdXJ2ZSBpcyBhIGdyYXBoaWNhbCByZXByZXNlbnRhdGlvbiBvZiB0aGUgdHJhZGUtb2ZmIGJldHdlZW4gdGhlIHRydWUgcG9zaXRpdmUgcmF0ZSBhbmQgZmFsc2UgcG9zaXRpdmUgcmF0ZSBhdCB2YXJpb3VzIHRocmVzaG9sZHMuIEFVQyByZXByZXNlbnRzIHRoZSBkZWdyZWUgb3IgbWVhc3VyZSBvZiBzZXBhcmFiaWxpdHkgYWNoaWV2ZWQgYnkgdGhlIG1vZGVsLgoKNy4gICoqU3BlY2lmaWNpdHkqKjogU3BlY2lmaWNpdHkgbWVhc3VyZXMgdGhlIHByb3BvcnRpb24gb2YgYWN0dWFsIG5lZ2F0aXZlcyB0aGF0IGFyZSBjb3JyZWN0bHkgaWRlbnRpZmllZCBhcyBzdWNoLiBJdCdzIGltcG9ydGFudCBpbiBjb250ZXh0cyB3aGVyZSB0aGUgY29zdCBvZiBmYWxzZSBwb3NpdGl2ZXMgaXMgaGlnaC4KCjguICAqKkxvZyBMb3NzKio6IEFsc28ga25vd24gYXMgbG9naXN0aWMgbG9zcyBvciBsb2dpdCBsb3NzLCBpdCBtZWFzdXJlcyB0aGUgcGVyZm9ybWFuY2Ugb2YgYSBjbGFzc2lmaWNhdGlvbiBtb2RlbCB3aGVyZSB0aGUgcHJlZGljdGlvbiBpcyBhIHByb2JhYmlsaXR5IGJldHdlZW4gMCBhbmQgMS4KCkVhY2ggb2YgdGhlc2UgbWV0cmljcyBwcm92aWRlcyBkaWZmZXJlbnQgaW5zaWdodHMgaW50byB0aGUgcGVyZm9ybWFuY2Ugb2YgYSBjbGFzc2lmaWNhdGlvbiBtb2RlbCwgYW5kIHRoZSBjaG9pY2Ugb2YgbWV0cmljcyBvZnRlbiBkZXBlbmRzIG9uIHRoZSBzcGVjaWZpYyByZXF1aXJlbWVudHMgYW5kIGNvbnRleHQgb2YgdGhlIHByb2JsZW0gYmVpbmcgc29sdmVkLgoKVGhlIGxhYmVsaW5nIG9mIHRoZSB0YXJnZXQgdmFyaWFibGUgaXMgYSBkZWNpc2lvbiBtYWRlIGJ5IHRoZSBhbmFseXN0LCAqaS5lLiosIHdoYXQgaXMgInBvc2l0aXZlIiBhbmQgd2hhdCBpcyAibmVnYXRpdmUiIGlzIGEgYnVzaW5lc3MgZGVjaXNpb24gYW5kIGlzIG5vdCBvZiBjb25zZXF1ZW5jZS4gRm9yIGV4YW1wbGUsIGluIGRpc2Vhc2UgZGV0ZWN0aW9uLCB0aGUgcHJlc2VuY2Ugb2YgYSBkaXNlYXNlIGlzIGNvbW1vbmx5IG1hcmtlZCBhcyAicG9zaXRpdmUiLCB3aGlsZSB0aGUgYWJzZW5jZSB3b3VsZCBiZSBtYXJrZWQgYSAibmVnYXRpdmUiLiBJZiB3ZSB3ZXJlIHRvIGV2YWx1YXRlIGEgbW9kZWwgcHJlZGljdGluZyB3aGV0aGVyIGEgY3VzdG9tZXIgaXMgbGlrZWx5IHRvIGJ1eSBhZ2FpbiwgdGhlICJwb3NpdGl2ZSIgbWlnaHQgYmVzdCBiZSB0aGUgb3V0Y29tZSBvZiAiY3VzdG9tZXIgYnV5cyBhZ2FpbiIuCgpXaGVuIGNsYXNzaWZpY2F0aW9uIGlzIGJldHdlZW4gdHdvIGNsYXNzZXMgKCJwb3NpdGl2ZSIgYW5kICJuZWdhdGl2ZSIpLCB3ZSBjYWxsIHRoaXMgYSBiaW5hcnkgY2xhc3NpZmljYXRpb24uIFdoZW4gdGhlcmUgYXJlIG1vcmUgdGhhbiB0d28gY2xhc3NlcywgdGhlbiBpdCBpcyBhIG11bHRpY2xhc3MgKG9yIG11bHRpdmFyaWF0ZSBvciBtdWx0aWxldmVsKSBjbGFzc2lmaWNhdGlvbi4gU29tZSBzdXBlcnZpc2VkIG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcyBhcmUgcHJpbmNpcGFsbHkgdXNlZCBmb3IgYmluYXJ5IGNsYXNzaWZpY2F0aW9uIChcX2UuZy4gbG9naXN0aWMgcmVncmVzc2lvbiksIHdoaWxlIG90aGVycyBhcmUgaW5oZXJlbnRseSBiZXR0ZXIgZm9yIG11bHRpY2xhc3MgY2xhc3NpZmljYXRpb24gKCplLmcuKiwga05OLCBkZWNpc2lvbiB0cmVlLCBhbmQgcmFuZG9tIGZvcmVzdCkuCgojIyBNb2RlbCBFdmFsdWF0aW9uCgpUaGVyZSBhcmUgc2V2ZXJhbCBtZXRob2RzIGZvciBldmFsdWF0aW5nIGNsYXNzaWZpY2F0aW9uIG1vZGVscy4gQWxsIG1ldGhvZHMgaW52b2x2ZSwgaW4gc29tZSB3YXksIHRoZSB0cmFpbmluZyBvZiBhIGNsYXNzaWZpY2F0aW9uIG1vZGVsIHVzaW5nIGEgc3VwZXJ2aXNlZCBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobSAoKmUuZy4qLCBrTk4gb3IgbG9naXN0aWMgcmVncmVzc2lvbikgb24gYSBsYWJlbGVkIHRyYWluaW5nIGRhdGEgc2V0LiBUaGUgbW9zdCBjb21tb24gbWV0aG9kcyBhcmUgbGlzdGVkIGJlbG93OyB0aGUgbW9zdCBjb21tb24gYW1vbmcgdGhlc2UgYXJlIHRoZSBob2xkb3V0IG1ldGhvZCBhbmQgay1mb2xkIGNyb3NzIHZhbGlkYXRpb24gKGtDVikuCgpCZXNpZGVzIHRoZSBob2xkb3V0IG1ldGhvZCwgc2V2ZXJhbCBvdGhlciB0ZWNobmlxdWVzIGFyZSB1c2VkIGZvciB2YWxpZGF0aW5nIHN1cGVydmlzZWQgbWFjaGluZSBsZWFybmluZyBtb2RlbHMuIEVhY2ggbWV0aG9kIGhhcyBpdHMgYWR2YW50YWdlcyBhbmQgaXMgc3VpdGVkIHRvIGRpZmZlcmVudCBzY2VuYXJpb3MuIFNvbWUgb2YgdGhlIG1vc3QgY29tbW9ubHkgdXNlZCBtZXRob2RzIGluY2x1ZGU6CgoxLiAgKipLLUZvbGQgQ3Jvc3MtVmFsaWRhdGlvbioqOgogICAgLSAgICoqRGVzY3JpcHRpb24qKjogVGhlIGRhdGFzZXQgaXMgZGl2aWRlZCBpbnRvIHR3byBwYXJ0czogdGhlIHRyYWluaW5nIHNldCBhbmQgdGhlIHRlc3RpbmcgKG9yIGhvbGRvdXQpIHNldC4gVGhlIG1vZGVsIGlzIHRyYWluZWQgdXNpbmcgdGhlIHRyYWluaW5nIGRhdGEgYW5kIGV2YWx1YXRlZCB1c2luZyB0aGUgdGVzdGluZyBkYXRhIHNldC4KICAgIC0gICAqKkFkdmFudGFnZXMqKjogU2ltcGxlIHRvIGFwcGx5LgogICAgLSAgICoqRGlzYWR2YW50YWdlcyoqOiBUaGUgbW9kZWwgYW5kIHRoZSBldmFsdWF0aW9uIGNhbiBiZSBncmVhdGx5IGluZmx1ZW5jZWQgYnkgdGhlIGNob2ljZSBvZiB0cmFpbmluZyBkYXRhLgoyLiAgKipLLUZvbGQgQ3Jvc3MtVmFsaWRhdGlvbioqOgogICAgLSAgICoqRGVzY3JpcHRpb24qKjogVGhlIGRhdGFzZXQgaXMgZGl2aWRlZCBpbnRvICdrJyBlcXVhbGx5IChvciBuZWFybHkgZXF1YWxseSkgc2l6ZWQgZm9sZHMgb3Igc3Vic2V0cy4gVGhlIG1vZGVsIGlzIHRyYWluZWQgJ2snIHRpbWVzLCBlYWNoIHRpbWUgdXNpbmcgYSBkaWZmZXJlbnQgZm9sZCBhcyB0aGUgdGVzdGluZyBzZXQgYW5kIHRoZSByZW1haW5pbmcgJ2stMScgZm9sZHMgYXMgdGhlIHRyYWluaW5nIHNldC4KICAgIC0gICAqKkFkdmFudGFnZXMqKjogSXQgZW5zdXJlcyB0aGF0IGV2ZXJ5IG9ic2VydmF0aW9uIGZyb20gdGhlIG9yaWdpbmFsIGRhdGFzZXQgaGFzIHRoZSBjaGFuY2Ugb2YgYXBwZWFyaW5nIGluIHRoZSB0cmFpbmluZyBhbmQgdGVzdCBzZXQuIFRoaXMgaXMgZXNwZWNpYWxseSB1c2VmdWwgd2l0aCBzbWFsbGVyIGRhdGFzZXRzLgogICAgLSAgICoqRGlzYWR2YW50YWdlcyoqOiBJdCBjYW4gYmUgY29tcHV0YXRpb25hbGx5IGludGVuc2l2ZSwgZXNwZWNpYWxseSBmb3IgbGFyZ2UgZGF0YXNldHMgb3IgY29tcGxleCBtb2RlbHMuCjMuICAqKlN0cmF0aWZpZWQgSy1Gb2xkIENyb3NzLVZhbGlkYXRpb24qKjoKICAgIC0gICAqKkRlc2NyaXB0aW9uKio6IFNpbWlsYXIgdG8gSy1Gb2xkLCBidXQgdGhlIGZvbGRzIGFyZSBtYWRlIGJ5IHByZXNlcnZpbmcgdGhlIHBlcmNlbnRhZ2Ugb2Ygc2FtcGxlcyBmb3IgZWFjaCBjbGFzcy4gVGhpcyBpcyBjcnVjaWFsIGluIGRlYWxpbmcgd2l0aCBpbWJhbGFuY2VkIGRhdGFzZXRzLgogICAgLSAgICoqQWR2YW50YWdlcyoqOiBNYWludGFpbnMgYSBiYWxhbmNlZCByZXByZXNlbnRhdGlvbiBvZiB0aGUgb3JpZ2luYWwgZGF0YXNldCwgcGFydGljdWxhcmx5IGltcG9ydGFudCBmb3IgY2xhc3NpZmljYXRpb24gcHJvYmxlbXMgd2l0aCBpbWJhbGFuY2VkIGNsYXNzIGRpc3RyaWJ1dGlvbnMuCiAgICAtICAgKipEaXNhZHZhbnRhZ2VzKio6IE1vcmUgY29tcGxleCB0byBpbXBsZW1lbnQgdGhhbiBzaW1wbGUgSy1Gb2xkIGNyb3NzLXZhbGlkYXRpb24uCjQuICAqKkxlYXZlLU9uZS1PdXQgQ3Jvc3MtVmFsaWRhdGlvbiAoTE9PQ1YpKio6CiAgICAtICAgKipEZXNjcmlwdGlvbioqOiBBIHNwZWNpYWwgY2FzZSBvZiBLLUZvbGQgY3Jvc3MtdmFsaWRhdGlvbiB3aGVyZSAnaycgZXF1YWxzIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIHRoZSBkYXRhc2V0LiBFc3NlbnRpYWxseSwgZWFjaCBvYnNlcnZhdGlvbiBpcyB1c2VkIGFzIGEgc2luZ2xlIHRlc3QgZXhhbXBsZSwgYW5kIHRoZSByZXN0IGFyZSB1c2VkIGZvciB0cmFpbmluZy4KICAgIC0gICAqKkFkdmFudGFnZXMqKjogTWF4aW1pemVzIHRoZSBhbW91bnQgb2YgZGF0YSB1c2VkIGZvciB0cmFpbmluZyB0aGUgbW9kZWwuCiAgICAtICAgKipEaXNhZHZhbnRhZ2VzKio6IEV4dHJlbWVseSBjb21wdXRhdGlvbmFsbHkgZXhwZW5zaXZlIGFuZCBpbXByYWN0aWNhbCB3aXRoIGxhcmdlIGRhdGFzZXRzLiBJdCBjYW4gYWxzbyBoYXZlIGhpZ2ggdmFyaWFuY2UgYXMgYSBzaW5nbGUgb2JzZXJ2YXRpb24gY2FuIHNvbWV0aW1lcyBiZSBhIHBvb3IgcmVwcmVzZW50YXRpb24gb2YgdGhlIGRhdGFzZXQuCjUuICAqKkxlYXZlLVAtT3V0IENyb3NzLVZhbGlkYXRpb24qKjoKICAgIC0gICAqKkRlc2NyaXB0aW9uKio6IFNpbWlsYXIgdG8gTE9PQ1YsIGJ1dCBpbnN0ZWFkIG9mIGxlYXZpbmcgb3V0IG9uZSBvYnNlcnZhdGlvbiBhdCBhIHRpbWUsICdwJyBvYnNlcnZhdGlvbnMgYXJlIGxlZnQgb3V0LgogICAgLSAgICoqQWR2YW50YWdlcyoqOiBBbGxvd3MgZm9yIG1vcmUgdGhvcm91Z2ggdGVzdGluZyB0aGFuIExPT0NWIGluIGNlcnRhaW4gY2FzZXMuCiAgICAtICAgKipEaXNhZHZhbnRhZ2VzKio6IENvbXB1dGF0aW9uYWxseSB2ZXJ5IGludGVuc2l2ZSBhbmQgbGVzcyBjb21tb25seSB1c2VkLgo2LiAgKipCb290c3RyYXAgTWV0aG9kKio6CiAgICAtICAgKipEZXNjcmlwdGlvbioqOiBJbnZvbHZlcyByYW5kb21seSBzYW1wbGluZyB3aXRoIHJlcGxhY2VtZW50IGZyb20gdGhlIGRhdGFzZXQgdG8gY3JlYXRlIG11bHRpcGxlIHRyYWluaW5nIGRhdGFzZXRzLiBUaGUgbW9kZWwgaXMgdHJhaW5lZCBvbiB0aGVzZSBib290c3RyYXAgc2FtcGxlcyBhbmQgdGVzdGVkIG9uIHRoZSB1bnNlZW4gaW5zdGFuY2VzLgogICAgLSAgICoqQWR2YW50YWdlcyoqOiBVc2VmdWwgZm9yIGVzdGltYXRpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhIHN0YXRpc3RpYyAoZS5nLiwgbWVhbiwgdmFyaWFuY2UpIGFuZCBwcm92aWRlcyBhIG1lYXN1cmUgb2YgdW5jZXJ0YWludHkuCiAgICAtICAgKipEaXNhZHZhbnRhZ2VzKio6IENhbiBsZWFkIHRvIG92ZXJmaXR0aW5nIGlmIG5vdCBpbXBsZW1lbnRlZCBjb3JyZWN0bHksIGFzIGl0IGludm9sdmVzIHNhbXBsaW5nIHdpdGggcmVwbGFjZW1lbnQuCjcuICAqKlRpbWUgU2VyaWVzIFNwbGl0Kio6CiAgICAtICAgKipEZXNjcmlwdGlvbioqOiBTcGVjaWZpY2FsbHkgdXNlZCBmb3IgdGltZSBzZXJpZXMgZGF0YS4gVGhlIGRhdGFzZXQgaXMgc3BsaXQgaW50byBhIHNlcXVlbmNlIG9mIHRyYWluaW5nIGFuZCB0ZXN0IHNldHMsIHdoZXJlIGVhY2ggc3VjY2Vzc2l2ZSB0ZXN0IHNldCBpcyAnbW92ZWQgZm9yd2FyZCBpbiB0aW1lJy4KICAgIC0gICAqKkFkdmFudGFnZXMqKjogUmVzcGVjdHMgdGhlIHRlbXBvcmFsIG9yZGVyIG9mIG9ic2VydmF0aW9ucywgd2hpY2ggaXMgY3JpdGljYWwgaW4gdGltZS1zZXJpZXMgYW5hbHlzaXMuCiAgICAtICAgKipEaXNhZHZhbnRhZ2VzKio6IE5vdCBhcHBsaWNhYmxlIHRvIG5vbi10aW1lLXNlcmllcyBkYXRhc2V0cyBhbmQgY2FuIGJlIHNlbnNpdGl2ZSB0byB0aGUgcGVyaW9kIGNob3NlbiBmb3IgdHJhaW5pbmcgYW5kIHRlc3RpbmcuCjguICAqKlJhbmRvbSBTdWJzYW1wbGluZyoqOgogICAgLSAgICoqRGVzY3JpcHRpb24qKjogU2ltaWxhciB0byB0aGUgaG9sZG91dCBtZXRob2QsIGJ1dCB0aGUgcHJvY2VzcyBpcyByZXBlYXRlZCBtdWx0aXBsZSB0aW1lcyB3aXRoIGRpZmZlcmVudCByYW5kb20gc3BsaXRzIG9mIHRoZSBkYXRhc2V0IGludG8gdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cy4KICAgIC0gICAqKkFkdmFudGFnZXMqKjogU2ltcGxlciBhbmQgbGVzcyBjb21wdXRhdGlvbmFsbHkgaW50ZW5zaXZlIHRoYW4gSy1Gb2xkIGNyb3NzLXZhbGlkYXRpb24uCiAgICAtICAgKipEaXNhZHZhbnRhZ2VzKio6IExlc3MgY29tcHJlaGVuc2l2ZSBhbmQgY2FuIGhhdmUgaGlnaCB2YXJpYW5jZSBkZXBlbmRpbmcgb24gdGhlIHNwbGl0cy4KCkVhY2ggb2YgdGhlc2UgbWV0aG9kcyBvZmZlcnMgYSBkaWZmZXJlbnQgYXBwcm9hY2ggdG8gYXNzZXNzaW5nIHRoZSBwZXJmb3JtYW5jZSBvZiBhIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWwsIGFuZCB0aGUgY2hvaWNlIG9mIG1ldGhvZCBjYW4gZGVwZW5kIG9uIHRoZSBzcGVjaWZpYyBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlIGRhdGEgYW5kIHRoZSBwcmFjdGljYWwgY29uc3RyYWludHMgb2YgbW9kZWwgdHJhaW5pbmcgYW5kIGV2YWx1YXRpb24uCgojIyMgSG9sZG91dCBNZXRob2QKClRoZSBob2xkb3V0IG1ldGhvZCBpcyBhIHNpbXBsZSBhbmQgd2lkZWx5IHVzZWQgdGVjaG5pcXVlIGZvciB2YWxpZGF0aW5nIHN1cGVydmlzZWQgbWFjaGluZSBsZWFybmluZyBtb2RlbHMsIHBhcnRpY3VsYXJseSBpbiBjbGFzc2lmaWNhdGlvbiB0YXNrcy4gVGhpcyBtZXRob2QgaW52b2x2ZXMgc3BsaXR0aW5nIHRoZSBkYXRhc2V0IGludG8gdHdvIHN1YnNldHM6IG9uZSBmb3IgdHJhaW5pbmcgdGhlIG1vZGVsIGFuZCB0aGUgb3RoZXIgZm9yIHRlc3RpbmcgaXRzIHBlcmZvcm1hbmNlLgoKVGhlIHByb2Nlc3MgZm9yIGV2YWx1YXRpbmcgYSBjbGFzc2lmaWNhdGlvbiBtb2RlbCBvYnRhaW5lZCBmcm9tIGEgc3VwZXJ2aXNlZCBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobSBnZW5lcmFsbHkgZm9sbG93cyB0aGVzZSBzdGVwczoKCjEuICAqKlNwbGl0dGluZyB0aGUgRGF0YXNldCoqOgogICAgLSAgIFRoZSBkYXRhc2V0IGlzIGRpdmlkZWQgaW50byB0d28gcGFydHM6IHRoZSB0cmFpbmluZyBzZXQgYW5kIHRoZSB0ZXN0aW5nIChvciBob2xkb3V0KSBzZXQuCiAgICAtICAgQSBjb21tb24gc3BsaXQgcmF0aW8gaXMgNzAlIG9mIHRoZSBkYXRhIGZvciB0cmFpbmluZyBhbmQgMzAlIGZvciB0ZXN0aW5nLCBidXQgdGhpcyBjYW4gdmFyeSBiYXNlZCBvbiB0aGUgZGF0YXNldCBzaXplIGFuZCBzcGVjaWZpYyByZXF1aXJlbWVudHMuCjIuICAqKlRyYWluaW5nIHRoZSBNb2RlbCoqOgogICAgLSAgIFRoZSBtb2RlbCBpcyB0cmFpbmVkIGV4Y2x1c2l2ZWx5IG9uIHRoZSB0cmFpbmluZyBzZXQuIFRoaXMgc2V0IGlzIHVzZWQgdG8gZml0IHRoZSBtb2RlbCBwYXJhbWV0ZXJzLgozLiAgKipUZXN0aW5nIHRoZSBNb2RlbCoqOgogICAgLSAgIEFmdGVyIHRyYWluaW5nLCB0aGUgbW9kZWwgaXMgZXZhbHVhdGVkIG9uIHRoZSB0ZXN0aW5nIHNldC4gVGhpcyBzZXQgaXMgbm90IHVzZWQgZHVyaW5nIHRoZSB0cmFpbmluZyBwaGFzZSwgc28gaXQgcHJvdmlkZXMgYW4gdW5iaWFzZWQgZXZhbHVhdGlvbiBvZiB0aGUgbW9kZWwuCiAgICAtICAgUGVyZm9ybWFuY2UgbWV0cmljcyBzdWNoIGFzIGFjY3VyYWN5LCBwcmVjaXNpb24sIHJlY2FsbCwgRjEgc2NvcmUsIFJPQy1BVUMsIGV0Yy4sIGFyZSBjb21wdXRlZCB0byBhc3Nlc3MgdGhlIG1vZGVsJ3MgcGVyZm9ybWFuY2UuCgojIyMjIEFkdmFudGFnZXMgb2YgdGhlIEhvbGRvdXQgTWV0aG9kCgotICAgKipTaW1wbGljaXR5Kio6IEl0IGlzIHN0cmFpZ2h0Zm9yd2FyZCBhbmQgZWFzeSB0byBpbXBsZW1lbnQuCi0gICAqKlNwZWVkKio6IExlc3MgY29tcHV0YXRpb25hbGx5IGV4cGVuc2l2ZSBjb21wYXJlZCB0byBtZXRob2RzIGxpa2UgY3Jvc3MtdmFsaWRhdGlvbiwgZXNwZWNpYWxseSBmb3IgbGFyZ2UgZGF0YXNldHMuCgojIyMjIERpc2FkdmFudGFnZXMKCi0gICAqKkRhdGEgU3BsaXQgRGVwZW5kZW5jZSoqOiBUaGUgcGVyZm9ybWFuY2UgZXN0aW1hdGUgY2FuIGJlIGhpZ2hseSBkZXBlbmRlbnQgb24gaG93IHRoZSBkYXRhIGlzIHNwbGl0LiBJZiB0aGUgc3BsaXQgaXMgbm90IHJlcHJlc2VudGF0aXZlIG9mIHRoZSBvdmVyYWxsIGRhdGFzZXQsIGl0IGNhbiBsZWFkIHRvIG1pc2xlYWRpbmcgcGVyZm9ybWFuY2UgZXN0aW1hdGVzLgotICAgKipMaW1pdGVkIERhdGEgVXRpbGl6YXRpb24qKjogU2luY2UgYSBwb3J0aW9uIG9mIHRoZSBkYXRhIGlzIHNldCBhc2lkZSBmb3IgdGVzdGluZywgaXQncyBub3QgdXNlZCBmb3IgdHJhaW5pbmcuIFRoaXMgY2FuIGJlIGEgZHJhd2JhY2ssIGVzcGVjaWFsbHkgd2l0aCBzbWFsbCBkYXRhc2V0cy4KCiMjIyMgRXhhbXBsZSBTY2VuYXJpbwoKSW1hZ2luZSB5b3UgaGF2ZSBhIGRhdGFzZXQgb2YgMTAsMDAwIGltYWdlcyB0byBidWlsZCBhIG1vZGVsIHRoYXQgY2xhc3NpZmllcyBpbWFnZXMgYXMgZWl0aGVyIGNhdHMgb3IgZG9ncy4gVXNpbmcgdGhlIGhvbGRvdXQgbWV0aG9kLCB5b3UgbWlnaHQ6CgotICAgVXNlIDcsMDAwIGltYWdlcyB0byB0cmFpbiB0aGUgbW9kZWwuCi0gICBSZXNlcnZlIHRoZSByZW1haW5pbmcgMywwMDAgaW1hZ2VzIHRvIHRlc3QgdGhlIG1vZGVsLgoKQWZ0ZXIgdHJhaW5pbmcsIHlvdSBldmFsdWF0ZSB0aGUgbW9kZWwncyBwZXJmb3JtYW5jZSBvbiB0aGUgMywwMDAgdGVzdCBpbWFnZXMuIFRoZSBhY2N1cmFjeSwgcHJlY2lzaW9uLCByZWNhbGwsIGFuZCBvdGhlciByZWxldmFudCBtZXRyaWNzIGNhbGN1bGF0ZWQgZnJvbSB0aGlzIHRlc3Qgc2V0IGdpdmUgeW91IGFuIGVzdGltYXRlIG9mIGhvdyB3ZWxsIHlvdXIgbW9kZWwgd2lsbCBwZXJmb3JtIG9uIHVuc2VlbiBkYXRhLgoKIyMjIyBCZXN0IFByYWN0aWNlcwoKLSAgICoqUmFuZG9tIFNwbGl0Kio6IEVuc3VyZSB0aGF0IHRoZSBzcGxpdCBiZXR3ZWVuIHRoZSB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzIGlzIHJhbmRvbS4gVGhpcyBoZWxwcyBpbiBtYWtpbmcgdGhlIHNwbGl0IHJlcHJlc2VudGF0aXZlIG9mIHRoZSB3aG9sZSBkYXRhc2V0LgotICAgKipTdHJhdGlmaWVkIFNwbGl0Kio6IElmIHRoZSBkYXRhc2V0IGlzIGltYmFsYW5jZWQgKGUuZy4sIDkwJSBjYXRzIGFuZCAxMCUgZG9ncyksIHVzZSBzdHJhdGlmaWVkIHNhbXBsaW5nIHRvIG1haW50YWluIHRoZSBzYW1lIHByb3BvcnRpb24gaW4gYm90aCB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzLgotICAgKipJdGVyYXRpdmUgQXBwcm9hY2gqKjogRm9yIG1vcmUgcm9idXN0IHZhbGlkYXRpb24sIGNvbnNpZGVyIHVzaW5nIHRoZSBob2xkb3V0IG1ldGhvZCBpbiBjb21iaW5hdGlvbiB3aXRoIHRlY2huaXF1ZXMgbGlrZSBjcm9zcy12YWxpZGF0aW9uLCBlc3BlY2lhbGx5IHdoZW4gZGVhbGluZyB3aXRoIHNtYWxsZXIgZGF0YXNldHMuCgpUaGUgaG9sZG91dCBtZXRob2QsIGRlc3BpdGUgaXRzIHNpbXBsaWNpdHksIGNhbiBiZSBhIHBvd2VyZnVsIHRvb2wgaW4gbW9kZWwgdmFsaWRhdGlvbiwgcHJvdmlkZWQgaXQncyB1c2VkIGNvcnJlY3RseSBhbmQgdGhlIGxpbWl0YXRpb25zIGFyZSBhY2tub3dsZWRnZWQuCgojIyMgay1Gb2xkIENyb3NzIFZhbGlkYXRpb24KCkstRm9sZCBDcm9zcy1WYWxpZGF0aW9uIGlzIGEgcm9idXN0IG1ldGhvZCBmb3IgYXNzZXNzaW5nIHRoZSBwZXJmb3JtYW5jZSBvZiBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscywgcGFydGljdWxhcmx5IHVzZWZ1bCBmb3IgaXRzIGFiaWxpdHkgdG8gcHJvdmlkZSBhIG1vcmUgcmVsaWFibGUgZXN0aW1hdGUgb2YgbW9kZWwgcGVyZm9ybWFuY2Ugb24gdW5zZWVuIGRhdGEuCgojIyMjIERlc2NyaXB0aW9uIG9mIEstRm9sZCBDcm9zcy1WYWxpZGF0aW9uCgoxLiAgKipTcGxpdHRpbmcgdGhlIERhdGFzZXQqKjoKICAgIC0gICBUaGUgZW50aXJlIGRhdGFzZXQgaXMgZGl2aWRlZCBpbnRvICdrJyBlcXVhbCAob3IgbmVhcmx5IGVxdWFsKSBzaXplZCBzdWJzZXRzIG9yICdmb2xkcycuCiAgICAtICAgQ29tbW9uIGNob2ljZXMgZm9yICdrJyBpbmNsdWRlIDUgb3IgMTAsIGJ1dCB0aGUgb3B0aW1hbCBudW1iZXIgY2FuIGRlcGVuZCBvbiB0aGUgc2l6ZSBhbmQgc3BlY2lmaWNzIG9mIHRoZSBkYXRhc2V0LgoyLiAgKipNb2RlbCBUcmFpbmluZyBhbmQgVmFsaWRhdGlvbiBQcm9jZXNzKio6CiAgICAtICAgVGhlIHByb2Nlc3MgaXMgcmVwZWF0ZWQgJ2snIHRpbWVzLCB3aXRoIGVhY2ggb2YgdGhlICdrJyBmb2xkcyB1c2VkIGV4YWN0bHkgb25jZSBhcyB0aGUgdmFsaWRhdGlvbiBzZXQuCiAgICAtICAgSW4gZWFjaCBpdGVyYXRpb24sIGEgZGlmZmVyZW50IGZvbGQgaXMgdHJlYXRlZCBhcyB0aGUgdmFsaWRhdGlvbiBzZXQsIGFuZCB0aGUgcmVtYWluaW5nICdrLTEnIGZvbGRzIGFyZSBjb21iaW5lZCB0byBmb3JtIHRoZSB0cmFpbmluZyBzZXQuCiAgICAtICAgVGhlIG1vZGVsIGlzIHRyYWluZWQgb24gdGhlIHRyYWluaW5nIHNldCBhbmQgZXZhbHVhdGVkIG9uIHRoZSB2YWxpZGF0aW9uIHNldC4KICAgIC0gICBBZnRlciAnaycgaXRlcmF0aW9ucywgZXZlcnkgZGF0YSBwb2ludCBoYXMgYmVlbiB1c2VkIGJvdGggZm9yIHRyYWluaW5nIGFuZCB2YWxpZGF0aW9uLgozLiAgKipBZ2dyZWdhdGluZyBSZXN1bHRzKio6CiAgICAtICAgVGhlIHBlcmZvcm1hbmNlIG1lYXN1cmUgKGUuZy4sIGFjY3VyYWN5LCBwcmVjaXNpb24sIHJlY2FsbCkgaXMgY2FsY3VsYXRlZCBmb3IgZWFjaCBvZiB0aGUgJ2snIGl0ZXJhdGlvbnMuCiAgICAtICAgVGhlIGZpbmFsIG1vZGVsIHBlcmZvcm1hbmNlIGlzIHR5cGljYWxseSByZXBvcnRlZCBhcyB0aGUgYXZlcmFnZSBvZiB0aGVzZSAnaycgcGVyZm9ybWFuY2UgbWVhc3VyZXMuCgojIyMjIEFkdmFudGFnZXMKCjEuICAqKlJlZHVjZWQgQmlhcyoqOiBTaW5jZSBldmVyeSBvYnNlcnZhdGlvbiBnZXRzIHRvIGJlIGluIGEgdGVzdCBzZXQgZXhhY3RseSBvbmNlIGFuZCBpbiBhIHRyYWluaW5nIHNldCAnay0xJyB0aW1lcywgaXQgcmVkdWNlcyBiaWFzIGNvbXBhcmVkIHRvIG1ldGhvZHMgbGlrZSB0aGUgaG9sZG91dCBtZXRob2QuCjIuICAqKlV0aWxpemF0aW9uIG9mIEFsbCBEYXRhKio6IEl0IGFsbG93cyBmb3IgYm90aCB0cmFpbmluZyBhbmQgdGVzdGluZyBvbiBhbGwgYXZhaWxhYmxlIGRhdGEsIG1heGltaXppbmcgdGhlIHVzZSBvZiBkYXRhLCB3aGljaCBpcyBwYXJ0aWN1bGFybHkgYmVuZWZpY2lhbCBmb3Igc21hbGxlciBkYXRhc2V0cy4KMy4gICoqUm9idXN0IFBlcmZvcm1hbmNlIEVzdGltYXRlKio6IFByb3ZpZGVzIGEgbW9yZSBhY2N1cmF0ZSBhbmQgcm9idXN0IGVzdGltYXRlIG9mIG1vZGVsIHBlcmZvcm1hbmNlLCBhcyBpdCBhdmVyYWdlcyB0aGUgcmVzdWx0cyBmcm9tICdrJyBpdGVyYXRpb25zLgo0LiAgKipVc2VmdWwgZm9yIExpbWl0ZWQgRGF0YSoqOiBJZGVhbCBmb3Igc2NlbmFyaW9zIHdpdGggbGltaXRlZCBkYXRhLCB3aGVyZSBpdCdzIGVzc2VudGlhbCB0byB1c2UgdGhlIGRhdGFzZXQgZWZmaWNpZW50bHkuCgojIyMjIERpc2FkdmFudGFnZXMKCjEuICAqKkNvbXB1dGF0aW9uYWwgQ29zdCoqOiBJdCBjYW4gYmUgY29tcHV0YXRpb25hbGx5IGV4cGVuc2l2ZSwgZXNwZWNpYWxseSBmb3IgbGFyZ2UgZGF0YXNldHMgYW5kIGNvbXBsZXggbW9kZWxzLCBhcyBpdCByZXF1aXJlcyB0aGUgbW9kZWwgdG8gYmUgdHJhaW5lZCBhbmQgZXZhbHVhdGVkICdrJyB0aW1lcy4KMi4gICoqVGltZS1Db25zdW1pbmcqKjogVGhlIGluY3JlYXNlZCBjb21wdXRhdGlvbmFsIGNvc3QgdHJhbnNsYXRlcyB0byBsb25nZXIgdHJhaW5pbmcgdGltZXMsIHdoaWNoIGNhbiBiZSBhIHNpZ25pZmljYW50IGRyYXdiYWNrIGluIHRpbWUtc2Vuc2l0aXZlIHByb2plY3RzLgozLiAgKipWYXJpYW5jZSBpbiBQZXJmb3JtYW5jZSoqOiBUaGUgcGVyZm9ybWFuY2UgbWlnaHQgc3RpbGwgdmFyeSBkZXBlbmRpbmcgb24gaG93IHRoZSBkYXRhIGlzIHNwbGl0IGludG8gZm9sZHMsIHRob3VnaCBsZXNzIHNvIHRoYW4gd2l0aCB0aGUgaG9sZG91dCBtZXRob2QuCjQuICAqKkNob2ljZSBvZiAnaycqKjogU2VsZWN0aW5nIHRoZSBhcHByb3ByaWF0ZSB2YWx1ZSBvZiAnaycgY2FuIGJlIGNoYWxsZW5naW5nLiBBIGxhcmdlciAnaycgcHJvdmlkZXMgbGVzcyBiaWFzIHRvd2FyZHMgb3ZlcmVzdGltYXRpbmcgdGhlIHRydWUgZXhwZWN0ZWQgZXJyb3IgKGFzIGVhY2ggdGVzdCBzZXQgaXMgc21hbGxlciksIGJ1dCB0aGUgdmFyaWFuY2Ugb2YgdGhlIHJlc3VsdGluZyBlc3RpbWF0ZSBjYW4gYmUgaGlnaGVyLgoKIyMjIyBFeGFtcGxlCgpJbiBhIGRhdGFzZXQgd2l0aCAxMDAgb2JzZXJ2YXRpb25zLCB1c2luZyAxMC1mb2xkIGNyb3NzLXZhbGlkYXRpb24gd291bGQgaW52b2x2ZTogLSBTcGxpdHRpbmcgdGhlIGRhdGEgaW50byAxMCBmb2xkcyBvZiAxMCBvYnNlcnZhdGlvbnMgZWFjaC4gLSBJbiBlYWNoIGl0ZXJhdGlvbiwgOSBmb2xkcyAoOTAgb2JzZXJ2YXRpb25zKSBhcmUgdXNlZCBmb3IgdHJhaW5pbmcsIGFuZCAxIGZvbGQgKDEwIG9ic2VydmF0aW9ucykgaXMgdXNlZCBmb3IgdmFsaWRhdGlvbi4gLSBBZnRlciAxMCBpdGVyYXRpb25zLCB0aGUgcGVyZm9ybWFuY2UgbWV0cmljIChlLmcuLCBhY2N1cmFjeSkgZm9yIGVhY2ggaXRlcmF0aW9uIGlzIGF2ZXJhZ2VkIHRvIHByb3ZpZGUgYW4gb3ZlcmFsbCBwZXJmb3JtYW5jZSBlc3RpbWF0ZS4KCkstRm9sZCBDcm9zcy1WYWxpZGF0aW9uIGlzIHdpZGVseSB1c2VkIGR1ZSB0byBpdHMgYmFsYW5jZSBvZiBlZmZpY2llbmN5IGFuZCBlZmZlY3RpdmVuZXNzLCBwYXJ0aWN1bGFybHkgaW4gc2NlbmFyaW9zIHdoZXJlIHRoZSBhdmFpbGFibGUgZGF0YSBpcyBsaW1pdGVkIGFuZCBvbmUgbmVlZHMgdG8gZ2V0IHRoZSBtb3N0IHJlbGlhYmxlIHBlcmZvcm1hbmNlIGVzdGltYXRlIHBvc3NpYmxlIGZyb20gdGhlIGRhdGFzZXQuCgojIyBBY2N1cmFjeQoKQWNjdXJhY3kgaXMgYSBmdW5kYW1lbnRhbCBtZXRyaWMgaW4gYm90aCBiaW5hcnkgYW5kIG11bHRpY2xhc3MgY2xhc3NpZmljYXRpb24gcHJvYmxlbXMgaW4gc3VwZXJ2aXNlZCBtYWNoaW5lIGxlYXJuaW5nLiBJdCBtZWFzdXJlcyB0aGUgcHJvcG9ydGlvbiBvZiBjb3JyZWN0IHByZWRpY3Rpb25zIChib3RoIHRydWUgcG9zaXRpdmVzIGFuZCB0cnVlIG5lZ2F0aXZlcykgbWFkZSBieSB0aGUgbW9kZWwgb3V0IG9mIGFsbCBwcmVkaWN0aW9ucy4KCiMjIyBCaW5hcnkgQ2xhc3NpZmljYXRpb24KCkluIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiwgdGhlcmUgYXJlIG9ubHkgdHdvIGNsYXNzZXMgKG9mdGVuIGxhYmVsZWQgYXMgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlLCBvciAxIGFuZCAwKS4KCiMjIyMgRm9ybXVsYSBmb3IgQWNjdXJhY3kKClRoZSBmb3JtdWxhIGZvciBhY2N1cmFjeSBpbiBiaW5hcnkgY2xhc3NpZmljYXRpb24gaXM6CgokJCBcdGV4dHtBY2N1cmFjeX0gPSBcZnJhY3tcdGV4dHtUcnVlIFBvc2l0aXZlcyAoVFApICsgVHJ1ZSBOZWdhdGl2ZXMgKFROKX19e1x0ZXh0e1RvdGFsIE51bWJlciBvZiBPYnNlcnZhdGlvbnN9fSAkJAoKV2hlcmU6IC0gKipUcnVlIFBvc2l0aXZlcyAoVFApKio6IENvcnJlY3RseSBwcmVkaWN0ZWQgcG9zaXRpdmUgb2JzZXJ2YXRpb25zLiAtICoqVHJ1ZSBOZWdhdGl2ZXMgKFROKSoqOiBDb3JyZWN0bHkgcHJlZGljdGVkIG5lZ2F0aXZlIG9ic2VydmF0aW9ucy4KCiMjIyMgRXhhbXBsZQoKSW1hZ2luZSBhIG1lZGljYWwgdGVzdCBmb3IgYSBkaXNlYXNlIHdoZXJlOiAtIDkwIHBlb3BsZSBhcmUgY29ycmVjdGx5IGlkZW50aWZpZWQgYXMgaGF2aW5nIHRoZSBkaXNlYXNlIChUUCkuIC0gOTAwIHBlb3BsZSBhcmUgY29ycmVjdGx5IGlkZW50aWZpZWQgYXMgbm90IGhhdmluZyB0aGUgZGlzZWFzZSAoVE4pLiAtIDEwIHBlb3BsZSBhcmUgaW5jb3JyZWN0bHkgaWRlbnRpZmllZCBhcyBoYXZpbmcgdGhlIGRpc2Vhc2UgKEZQKS4gLSAxMDAgcGVvcGxlIGFyZSBpbmNvcnJlY3RseSBpZGVudGlmaWVkIGFzIG5vdCBoYXZpbmcgdGhlIGRpc2Vhc2UgKEZOKS4KClRoZSBhY2N1cmFjeSBvZiB0aGlzIHRlc3QgaXMgY2FsY3VsYXRlZCBhczoKCiQkIFx0ZXh0e0FjY3VyYWN5fSA9IFxmcmFjezkwICsgOTAwfXs5MCArIDkwMCArIDEwICsgMTAwfSA9IFxmcmFjezk5MH17MTEwMH0gXGFwcHJveCAwLjkwICQkCgpUaGlzIG1lYW5zIHRoZSB0ZXN0IGNvcnJlY3RseSBpZGVudGlmaWVzIHRoZSBkaXNlYXNlIHN0YXR1cyA5MCUgb2YgdGhlIHRpbWUuCgojIyMgTXVsdGljbGFzcyBDbGFzc2lmaWNhdGlvbgoKSW4gbXVsdGljbGFzcyBjbGFzc2lmaWNhdGlvbiwgdGhlcmUgYXJlIG1vcmUgdGhhbiB0d28gY2xhc3Nlcy4KCiMjIyMgRm9ybXVsYSBmb3IgQWNjdXJhY3kKClRoZSBmb3JtdWxhIGZvciBhY2N1cmFjeSBpbiBtdWx0aWNsYXNzIGNsYXNzaWZpY2F0aW9uIGlzIHNpbWlsYXIgdG8gdGhhdCBpbiBiaW5hcnkgY2xhc3NpZmljYXRpb24sIGJ1dCBpdCBjb25zaWRlcnMgYWxsIGNsYXNzZXM6CgokJCBcdGV4dHtBY2N1cmFjeX0gPSBcZnJhY3tcdGV4dHtTdW0gb2YgQ29ycmVjdCBQcmVkaWN0aW9ucyBhY3Jvc3MgYWxsIGNsYXNzZXN9fXtcdGV4dHtUb3RhbCBOdW1iZXIgb2YgT2JzZXJ2YXRpb25zfX0gJCQKCiMjIyMgRXhhbXBsZQoKQ29uc2lkZXIgYSBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtIHdpdGggdGhyZWUgY2xhc3NlcyAoQSwgQiwgYW5kIEMpIGFuZCBhIGRhdGFzZXQgd2l0aCB0aGUgZm9sbG93aW5nIHJlc3VsdHM6IC0gQ2xhc3MgQTogMzAgY29ycmVjdCBwcmVkaWN0aW9ucywgNSBpbmNvcnJlY3QgcHJlZGljdGlvbnMuIC0gQ2xhc3MgQjogNDAgY29ycmVjdCBwcmVkaWN0aW9ucywgMTUgaW5jb3JyZWN0IHByZWRpY3Rpb25zLiAtIENsYXNzIEM6IDUwIGNvcnJlY3QgcHJlZGljdGlvbnMsIDEwIGluY29ycmVjdCBwcmVkaWN0aW9ucy4KClRoZSB0b3RhbCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGlzICQzMCArIDUgKyA0MCArIDE1ICsgNTAgKyAxMCA9IDE1MCQuCgpUaGUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsIGlzOgoKJCQgXHRleHR7QWNjdXJhY3l9ID0gXGZyYWN7MzAgKyA0MCArIDUwfXsxNTB9ID0gXGZyYWN7MTIwfXsxNTB9ID0gMC44MCAkJAoKVGhpcyBtZWFucyB0aGUgbW9kZWwgY29ycmVjdGx5IHByZWRpY3RzIHRoZSBjbGFzcyA4MCUgb2YgdGhlIHRpbWUuCgojIyMgS2V5IFBvaW50cwoKLSAgICoqQmluYXJ5IENsYXNzaWZpY2F0aW9uKio6IEFjY3VyYWN5IGlzIHN0cmFpZ2h0Zm9yd2FyZCwgZm9jdXNpbmcgb24gKlRQKiBhbmQgKlROKiBvdXQgb2YgYWxsIG9ic2VydmF0aW9ucy4KLSAgICoqTXVsdGljbGFzcyBDbGFzc2lmaWNhdGlvbioqOiBBY2N1cmFjeSBjb25zaWRlcnMgY29ycmVjdCBwcmVkaWN0aW9ucyBhY3Jvc3MgYWxsIGNsYXNzZXMuCi0gICAqKkxpbWl0YXRpb25zKio6IEFjY3VyYWN5IGNhbiBiZSBtaXNsZWFkaW5nIGluIGltYmFsYW5jZWQgZGF0YXNldHMgd2hlcmUgb25lIGNsYXNzIHNpZ25pZmljYW50bHkgb3V0d2VpZ2hzIG90aGVycy4gSW4gc3VjaCBjYXNlcywgb3RoZXIgbWV0cmljcyBsaWtlIHByZWNpc2lvbiwgcmVjYWxsLCBhbmQgKkYxKiBzY29yZSBtaWdodCBwcm92aWRlIGEgbW9yZSBudWFuY2VkIHVuZGVyc3RhbmRpbmcgb2YgdGhlIG1vZGVsJ3MgcGVyZm9ybWFuY2UuCgpBY2N1cmFjeSBpcyBhIGdvb2QgaW5pdGlhbCBpbmRpY2F0b3Igb2YgbW9kZWwgcGVyZm9ybWFuY2UgYnV0IHNob3VsZCBiZSB1c2VkIGFsb25nc2lkZSBvdGhlciBtZXRyaWNzIGZvciBhIGNvbXByZWhlbnNpdmUgZXZhbHVhdGlvbiwgZXNwZWNpYWxseSBpbiBjYXNlcyBvZiBjbGFzcyBpbWJhbGFuY2Ugb3Igd2hlbiB0aGUgY29zdHMgb2YgZGlmZmVyZW50IHR5cGVzIG9mIGVycm9ycyB2YXJ5IHNpZ25pZmljYW50bHkuCgojIyBQcmVjaXNpb24KClByZWNpc2lvbiBpcyBhIGNvbW1vbiBhbmQgb2Z0ZW4gdXNlZCBtZXRyaWMgaW4gdGhlIGV2YWx1YXRpb24gb2YgY2xhc3NpZmljYXRpb24gbW9kZWxzLCBlc3BlY2lhbGx5IGluIHNjZW5hcmlvcyB3aGVyZSB0aGUgY29zdCBvZiBmYWxzZSBwb3NpdGl2ZXMgKGluY29ycmVjdGx5IHByZWRpY3RpbmcgdGhlIHBvc2l0aXZlIGNsYXNzKSBpcyBoaWdoLiBJdCBnaXZlcyB1cyBpbnNpZ2h0IGludG8gdGhlIGFjY3VyYWN5IG9mIHRoZSBwb3NpdGl2ZSBwcmVkaWN0aW9ucyBtYWRlIGJ5IHRoZSBtb2RlbC4KCiMjIERlZmluaXRpb24gb2YgUHJlY2lzaW9uCgpQcmVjaXNpb24gaXMgZGVmaW5lZCBhcyB0aGUgcmF0aW8gb2YgY29ycmVjdGx5IHByZWRpY3RlZCBwb3NpdGl2ZSBvYnNlcnZhdGlvbnMgdG8gdGhlIHRvdGFsIHByZWRpY3RlZCBwb3NpdGl2ZSBvYnNlcnZhdGlvbnMuIEluIHNpbXBsZXIgdGVybXMsIGl0IGFuc3dlcnMgdGhlIHF1ZXN0aW9uOiAiT2YgYWxsIHRoZSBpbnN0YW5jZXMgdGhlIG1vZGVsIGxhYmVsZWQgYXMgcG9zaXRpdmUsIGhvdyBtYW55IHdlcmUgYWN0dWFsbHkgcG9zaXRpdmU/IgoKIyMjIENhbGN1bGF0aW5nIFByZWNpc2lvbgoKVGhlIGZvcm11bGEgZm9yIHByZWNpc2lvbiBpczoKCiQkIFx0ZXh0e1ByZWNpc2lvbn0gPSBcZnJhY3tcdGV4dHtUcnVlIFBvc2l0aXZlcyAoVFApfX17XHRleHR7VHJ1ZSBQb3NpdGl2ZXMgKFRQKSArIEZhbHNlIFBvc2l0aXZlcyAoRlApfX0gJCQKCndoZXJlLAoKLSAgICoqVHJ1ZSBQb3NpdGl2ZXMgKFRQKSoqIGFyZSB0aGUgaW5zdGFuY2VzIGNvcnJlY3RseSBwcmVkaWN0ZWQgYXMgcG9zaXRpdmUKLSAgICoqRmFsc2UgUG9zaXRpdmVzIChGUCkqKiBhcmUgdGhlIGluc3RhbmNlcyBpbmNvcnJlY3RseSBwcmVkaWN0ZWQgYXMgcG9zaXRpdmUKCiMjIyBJbnRlcnByZXRhdGlvbgoKLSAgIEEgcHJlY2lzaW9uIG9mIDEuMCBtZWFucyB0aGF0IGV2ZXJ5IGl0ZW0gbGFiZWxlZCBhcyBwb3NpdGl2ZSBpcyBpbmRlZWQgcG9zaXRpdmUgKGJ1dCBzYXlzIG5vdGhpbmcgYWJvdXQgdGhlIGl0ZW1zIGxhYmVsZWQgYXMgbmVnYXRpdmUpLgotICAgQSBsb3dlciBwcmVjaXNpb24gaW5kaWNhdGVzIGEgaGlnaCBudW1iZXIgb2YgZmFsc2UgcG9zaXRpdmVzIGFtb25nIHRoZSBsYWJlbGVkIHBvc2l0aXZlcy4KCiMjIyBDb21tb24gVXNlCgpQcmVjaXNpb24gaXMgcGFydGljdWxhcmx5IGltcG9ydGFudCBpbiBmaWVsZHMgd2hlcmUgdGhlIGNvc3Qgb2YgYSBmYWxzZSBwb3NpdGl2ZSBpcyBoaWdoLiBGb3IgZXhhbXBsZSwgaW4gZW1haWwgc3BhbSBkZXRlY3Rpb24sIGEgZmFsc2UgcG9zaXRpdmUgKGxhYmVsaW5nIGEgZ29vZCBlbWFpbCBhcyBzcGFtKSBpcyBtb3JlIHByb2JsZW1hdGljIHRoYW4gYSBmYWxzZSBuZWdhdGl2ZSAoZmFpbGluZyB0byBpZGVudGlmeSBhIHNwYW0gZW1haWwpLiBTaW1pbGFybHksIGluIG1lZGljYWwgdGVzdGluZywgZmFsc2VseSBkaWFnbm9zaW5nIGEgaGVhbHRoeSBwYXRpZW50IHdpdGggYSBkaXNlYXNlIGNvdWxkIGJlIG1vcmUgY3JpdGljYWwgdGhhbiBtaXNzaW5nIHRoZSBkaXNlYXNlIGluIGl0cyBlYXJseSBzdGFnZXMuCgojIyMgRXhhbXBsZXMKCiMjIyMgRXhhbXBsZSBJOiBFbWFpbCBTcGFtIEZpbHRlcmluZwoKKipDb250ZXh0Kio6IEluIGVtYWlsIHNwYW0gZmlsdGVyaW5nIHN5c3RlbXMsIHRoZSBnb2FsIGlzIHRvIGlkZW50aWZ5IGFuZCBmaWx0ZXIgb3V0IHNwYW0gZW1haWxzIHdoaWxlIGVuc3VyaW5nIGxlZ2l0aW1hdGUgZW1haWxzIHJlYWNoIHRoZSB1c2VyJ3MgaW5ib3guCgotICAgKipUcnVlIFBvc2l0aXZlIChUUCkqKjogQSBzcGFtIGVtYWlsIGNvcnJlY3RseSBpZGVudGlmaWVkIGFzIHNwYW0uCi0gICAqKkZhbHNlIFBvc2l0aXZlIChGUCkqKjogQSBsZWdpdGltYXRlIGVtYWlsIGluY29ycmVjdGx5IGlkZW50aWZpZWQgYXMgc3BhbSAodGhpcyBpcyBwYXJ0aWN1bGFybHkgdW5kZXNpcmFibGUgYXMgaXQgY291bGQgbGVhZCB0byBtaXNzaW5nIGltcG9ydGFudCBlbWFpbHMpLgoKKipQcmVjaXNpb24gaW4gdGhpcyBTY2VuYXJpbyoqOiAtIEhpZ2ggcHJlY2lzaW9uIG1lYW5zIHRoYXQgbW9zdCBvZiB0aGUgZW1haWxzIGlkZW50aWZpZWQgYXMgc3BhbSBhcmUgaW5kZWVkIHNwYW0sIG1pbmltaXppbmcgdGhlIHJpc2sgb2YgaW1wb3J0YW50IGVtYWlscyBiZWluZyB3cm9uZ2x5IGZpbHRlcmVkIG91dC4gLSBJZiBhIHNwYW0gZmlsdGVyIGhhcyBhIHByZWNpc2lvbiBvZiAwLjk1LCBpdCBtZWFucyB0aGF0IDk1JSBvZiB0aGUgZW1haWxzIGl0IG1hcmtzIGFzIHNwYW0gYXJlIGFjdHVhbGx5IHNwYW0sIGFuZCBvbmx5IDUlIGFyZSBsZWdpdGltYXRlIGVtYWlscyBtaXN0YWtlbmx5IGlkZW50aWZpZWQgYXMgc3BhbS4KCioqSW1wb3J0YW5jZSoqOiAtIEluIGVtYWlsIGZpbHRlcmluZywgdXNlcnMgdHlwaWNhbGx5IHByZWZlciB0byByZWNlaXZlIGEgZmV3IHNwYW0gZW1haWxzIGluIHRoZWlyIGluYm94IHJhdGhlciB0aGFuIG1pc3MgYW4gaW1wb3J0YW50IGxlZ2l0aW1hdGUgZW1haWwuIFRoZXJlZm9yZSwgbWFpbnRhaW5pbmcgaGlnaCBwcmVjaXNpb24gaXMgY3J1Y2lhbCB0byBhdm9pZCB0aGUgaW5jb252ZW5pZW5jZSBhbmQgcG90ZW50aWFsIGxvc3MgY2F1c2VkIGJ5IG1pc3NpbmcgaW1wb3J0YW50IGVtYWlscy4KCiMjIyMgRXhhbXBsZSBJSTogTWVkaWNhbCBEaWFnbm9zaXMgZm9yIGEgU2VyaW91cyBEaXNlYXNlCgoqKkNvbnRleHQqKjogQ29uc2lkZXIgYSBtZWRpY2FsIHRlc3QgZGVzaWduZWQgdG8gZGlhZ25vc2UgYSBzZXJpb3VzLCBwb3RlbnRpYWxseSBsaWZlLXRocmVhdGVuaW5nIGRpc2Vhc2UuCgotICAgKipUcnVlIFBvc2l0aXZlIChUUCkqKjogQ29ycmVjdGx5IGlkZW50aWZ5aW5nIGEgcGF0aWVudCB3aXRoIHRoZSBkaXNlYXNlLgotICAgKipGYWxzZSBQb3NpdGl2ZSAoRlApKio6IEluY29ycmVjdGx5IGRpYWdub3NpbmcgYSBoZWFsdGh5IHBlcnNvbiB3aXRoIHRoZSBkaXNlYXNlLgoKKipQcmVjaXNpb24gaW4gdGhpcyBTY2VuYXJpbyoqOgoKLSAgIEhpZ2ggcHJlY2lzaW9uIGluZGljYXRlcyB0aGF0IGEgaGlnaCBwcm9wb3J0aW9uIG9mIHBhdGllbnRzIGRpYWdub3NlZCB3aXRoIHRoZSBkaXNlYXNlIGFjdHVhbGx5IGhhdmUgdGhlIGRpc2Vhc2UuCi0gICBGb3IgaW5zdGFuY2UsIGlmIGEgdGVzdCBoYXMgYSBwcmVjaXNpb24gb2YgMC45MCwgaXQgaW1wbGllcyB0aGF0IDkwJSBvZiB0aGUgZGlhZ25vc2VkIGNhc2VzIGFyZSB0cnVlIHBvc2l0aXZlcywgd2hlcmVhcyAxMCUgYXJlIGZhbHNlIHBvc2l0aXZlcy4KCioqSW1wb3J0YW5jZSoqOgoKLSAgIEluIG1lZGljYWwgZGlhZ25vc3RpY3MsIGVzcGVjaWFsbHkgZm9yIHNlcmlvdXMgY29uZGl0aW9ucywgYSBmYWxzZSBwb3NpdGl2ZSBjYW4gbGVhZCB0byB1bm5lY2Vzc2FyeSBzdHJlc3MsIGZ1cnRoZXIgaW52YXNpdmUgdGVzdGluZywgYW5kIHBvdGVudGlhbGx5IGhhcm1mdWwgdHJlYXRtZW50IGZvciB0aGUgcGF0aWVudC4gVGhlcmVmb3JlLCBoYXZpbmcgYSBoaWdoIHByZWNpc2lvbiByYXRlIGlzIGNydWNpYWwgdG8gbWluaW1pemUgdGhlc2Ugcmlza3MuCgpJbiBib3RoIGV4YW1wbGVzLCB3aGlsZSBoaWdoIHByZWNpc2lvbiBpcyBkZXNpcmFibGUsIGl0IGlzIGFsc28gZXNzZW50aWFsIHRvIGJhbGFuY2UgaXQgd2l0aCBvdGhlciBtZXRyaWNzIGxpa2UgcmVjYWxsLCBlc3BlY2lhbGx5IGluIG1lZGljYWwgc2NlbmFyaW9zIHdoZXJlIG1pc3NpbmcgYSB0cnVlIGNhc2UgKGhpZ2ggcmVjYWxsKSBjYW4gYmUgYXMgY3JpdGljYWwgYXMgYXZvaWRpbmcgZmFsc2UgYWxhcm1zIChoaWdoIHByZWNpc2lvbikuIFRoZXNlIGV4YW1wbGVzIGhpZ2hsaWdodCB0aGUgaW1wb3J0YW5jZSBvZiBwcmVjaXNpb24gaW4gY29udGV4dHMgd2hlcmUgdGhlIGNvbnNlcXVlbmNlcyBvZiBmYWxzZSBwb3NpdGl2ZXMgYXJlIHNpZ25pZmljYW50LgoKIyMjIFByZWNpc2lvbiBpbiBNdWx0aXZhcmlhdGUgQ2xhc3NpZmljYXRpb24KCkluIGEgbXVsdGljbGFzcyAob3IgbXVsdGl2YXJpYXRlKSBjbGFzc2lmaWNhdGlvbiBzY2VuYXJpbywgd2hlcmUgdGhlcmUgYXJlIG1vcmUgdGhhbiB0d28gcG9zc2libGUgb3V0Y29tZXMsIHByZWNpc2lvbiBpcyBjYWxjdWxhdGVkIGZvciBlYWNoIGNsYXNzIHNlcGFyYXRlbHkgYW5kIHRoZW4gY2FuIGJlIGF2ZXJhZ2VkIHRvIG9idGFpbiBhbiBvdmVyYWxsIHByZWNpc2lvbi4gVGhpcyBwcm9jZXNzIGludm9sdmVzIGNvbnNpZGVyaW5nIGVhY2ggY2xhc3MgYXMgdGhlICJwb3NpdGl2ZSIgY2xhc3MgKG9mIGludGVyZXN0KSBhbmQgYWxsIG90aGVyIGNsYXNzZXMgYXMgIm5lZ2F0aXZlIiAobm90IG9mIGludGVyZXN0KSBmb3IgdGhlIHB1cnBvc2Ugb2YgdGhlIGNhbGN1bGF0aW9uLgoKIyMjIyBTdGVwcyB0byBDYWxjdWxhdGUgUHJlY2lzaW9uIGluIE11bHRpY2xhc3MgQ2xhc3NpZmljYXRpb24KCjEuICAqKkNhbGN1bGF0ZSBQcmVjaXNpb24gZm9yIEVhY2ggQ2xhc3MqKjoKICAgIC0gICBGb3IgZWFjaCBjbGFzcywgY2FsY3VsYXRlIHByZWNpc2lvbiBhczogJCQgXHRleHR7UHJlY2lzaW9ufV97XHRleHR7Y2xhc3N9fSA9IFxmcmFje1x0ZXh0e1RydWUgUG9zaXRpdmVzIChUUCl9X3tcdGV4dHtjbGFzc319fXtcdGV4dHtUcnVlIFBvc2l0aXZlcyAoVFApfV97XHRleHR7Y2xhc3N9fSArIFx0ZXh0e0ZhbHNlIFBvc2l0aXZlcyAoRlApfV97XHRleHR7Y2xhc3N9fX0gJCQKICAgIC0gICBIZXJlLCAqKlRQKiogZm9yIGEgY2xhc3MgaXMgdGhlIG51bWJlciBvZiB0aW1lcyB0aGUgY2xhc3Mgd2FzIGNvcnJlY3RseSBwcmVkaWN0ZWQsIGFuZCAqKkZQKiogaXMgdGhlIG51bWJlciBvZiB0aW1lcyBvdGhlciBjbGFzc2VzIHdlcmUgaW5jb3JyZWN0bHkgcHJlZGljdGVkIGFzIHRoaXMgY2xhc3MuCjIuICAqKkF2ZXJhZ2UgdGhlIFByZWNpc2lvbiBTY29yZXMqKjoKICAgIC0gICAqKk1hY3JvLWF2ZXJhZ2UgUHJlY2lzaW9uKio6IENhbGN1bGF0ZSB0aGUgYXZlcmFnZSBvZiB0aGUgcHJlY2lzaW9uIHNjb3JlcyBmb3IgZWFjaCBjbGFzcy4gVGhpcyB0cmVhdHMgYWxsIGNsYXNzZXMgZXF1YWxseSwgcmVnYXJkbGVzcyBvZiB0aGVpciBmcmVxdWVuY3kgaW4gdGhlIGRhdGFzZXQuICQkIFx0ZXh0e01hY3JvLWF2ZXJhZ2UgUHJlY2lzaW9ufSA9IFxmcmFje1xzdW0gXHRleHR7UHJlY2lzaW9ufV97XHRleHR7Y2xhc3N9fX17XHRleHR7TnVtYmVyIG9mIGNsYXNzZXN9fSAkJAogICAgLSAgICoqV2VpZ2h0ZWQtYXZlcmFnZSBQcmVjaXNpb24qKjogQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIG9mIHByZWNpc2lvbiBzY29yZXMgZm9yIGVhY2ggY2xhc3MsIHdlaWdodGVkIGJ5IHRoZSBudW1iZXIgb2YgdHJ1ZSBpbnN0YW5jZXMgZm9yIGVhY2ggY2xhc3MuIFRoaXMgYWNjb3VudHMgZm9yIGNsYXNzIGltYmFsYW5jZS4gJCQgXHRleHR7V2VpZ2h0ZWQtYXZlcmFnZSBQcmVjaXNpb259ID0gXHN1bSBcbGVmdCggXGZyYWN7XHRleHR7TnVtYmVyIG9mIHRydWUgaW5zdGFuY2VzIGluIGNsYXNzfX17XHRleHR7VG90YWwgbnVtYmVyIG9mIGluc3RhbmNlc319IFx0aW1lcyBcdGV4dHtQcmVjaXNpb259X3tcdGV4dHtjbGFzc319IFxyaWdodCkgJCQKCiMjIyMgRXhhbXBsZSBDYWxjdWxhdGlvbgoKSW1hZ2luZSBhIGNsYXNzaWZpY2F0aW9uIHByb2JsZW0gd2l0aCB0aHJlZSBjbGFzc2VzOiBBLCBCLCBhbmQgQy4gTGV0J3Mgc2F5IHdlIGhhdmUgdGhlIGZvbGxvd2luZyBjb3VudHM6CgotICAgQ2xhc3MgQTogJFx0ZXh0e1RQfV9BID0gMzAsIFx0ZXh0e0ZQfV9BID0gMTAkCi0gICBDbGFzcyBCOiAkXHRleHR7VFB9X0IgPSA0MCwgXHRleHR7RlB9X0IgPSAyMCQKLSAgIENsYXNzIEM6ICRcdGV4dHtUUH1fQyA9IDUwLCBcdGV4dHtGUH1fQyA9IDUkCgpUaGUgcHJlY2lzaW9uIGZvciBlYWNoIGNsYXNzIHdvdWxkIGJlOgoKLSAgIFByZWNpc2lvbiBmb3IgQ2xhc3MgQTogJFx0ZXh0e1ByZWNpc2lvbn1fQSA9IFxmcmFjezMwfXszMCArIDEwfSA9IDAuNzUkCi0gICBQcmVjaXNpb24gZm9yIENsYXNzIEI6ICRcdGV4dHtQcmVjaXNpb259X0IgPSBcZnJhY3s0MH17NDAgKyAyMH0gPSAwLjY3JAotICAgUHJlY2lzaW9uIGZvciBDbGFzcyBDOiAkXHRleHR7UHJlY2lzaW9ufV9DID0gXGZyYWN7NTB9ezUwICsgNX0gPSAwLjkxJAoKVGhlbiwgdGhlIG1hY3JvLWF2ZXJhZ2UgcHJlY2lzaW9uIGFjcm9zcyBhbGwgY2xhc3NlcyB3b3VsZCBiZToKCiQkIFx0ZXh0e01hY3JvLWF2ZXJhZ2UgUHJlY2lzaW9ufSA9IFxmcmFjezAuNzUgKyAwLjY3ICsgMC45MX17M30gXGFwcHJveCAwLjc4ICQkCgpBbmQgaWYgd2Ugd2VyZSB0byBjYWxjdWxhdGUgd2VpZ2h0ZWQtYXZlcmFnZSBwcmVjaXNpb24gKGFzc3VtaW5nIGVxdWFsIGRpc3RyaWJ1dGlvbiBvZiB0cnVlIGluc3RhbmNlcyBhbW9uZyBjbGFzc2VzIGZvciBzaW1wbGljaXR5KSwgaXQgd291bGQgYmUgc2ltaWxhciBpbiB0aGlzIGNhc2UuCgpVc2luZyB0aGVzZSBtZXRob2RzLCB5b3UgY2FuIGFjY291bnQgZm9yIHRoZSBwZXJmb3JtYW5jZSBvZiBhIG11bHRpY2xhc3MgY2xhc3NpZmljYXRpb24gbW9kZWwgaW4gY29ycmVjdGx5IGlkZW50aWZ5aW5nIGVhY2ggY2xhc3Mgd2hpbGUgY29uc2lkZXJpbmcgdGhlIHNwZWNpZmljIGltcG9ydGFuY2Ugb3IgZnJlcXVlbmN5IG9mIGVhY2ggY2xhc3MuCgojIyBSZWNhbGwKClJlY2FsbCwgYWxzbyBrbm93biBhcyBzZW5zaXRpdml0eSwgaXMgYW4gaW1wb3J0YW50IG1ldHJpYyBpbiBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtcywgdXNlZCB0byBtZWFzdXJlIHRoZSBwcm9wb3J0aW9uIG9mIGFjdHVhbCBwb3NpdGl2ZXMgdGhhdCBhcmUgY29ycmVjdGx5IGlkZW50aWZpZWQgYnkgdGhlIGNsYXNzaWZpY2F0aW9uIG1vZGVsLgoKIyMjIFJlY2FsbCBpbiBCaW5hcnkgQ2xhc3NpZmljYXRpb24KCkluIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiwgdGhlcmUgYXJlIHR3byBwb3NzaWJsZSBvdXRjb21lczogcG9zaXRpdmUgYW5kIG5lZ2F0aXZlLgoKIyMjIyBGb3JtdWxhIGZvciBSZWNhbGwKClRoZSBmb3JtdWxhIGZvciByZWNhbGwgaW4gYmluYXJ5IGNsYXNzaWZpY2F0aW9uIGlzOgoKJCQgXHRleHR7UmVjYWxsfSA9IFxmcmFje1x0ZXh0e1RydWUgUG9zaXRpdmVzIChUUCl9fXtcdGV4dHtUcnVlIFBvc2l0aXZlcyAoVFApICsgRmFsc2UgTmVnYXRpdmVzIChGTil9fSAkJAoKLSAgICoqVHJ1ZSBQb3NpdGl2ZXMgKFRQKSoqOiBDb3JyZWN0bHkgcHJlZGljdGVkIHBvc2l0aXZlIG9ic2VydmF0aW9ucy4KLSAgICoqRmFsc2UgTmVnYXRpdmVzIChGTikqKjogQWN0dWFsIHBvc2l0aXZlcyB0aGF0IHRoZSBtb2RlbCBpbmNvcnJlY3RseSBwcmVkaWN0ZWQgYXMgbmVnYXRpdmUuCgojIyMjIEV4YW1wbGUKCkNvbnNpZGVyIGEgbWVkaWNhbCB0ZXN0IHRvIGlkZW50aWZ5IGEgZGlzZWFzZToKCi0gICBUaGUgdGVzdCBjb3JyZWN0bHkgaWRlbnRpZmllcyA4MCBwYXRpZW50cyB3aXRoIHRoZSBkaXNlYXNlICgqVFAqKS4KLSAgIDIwIHBhdGllbnRzIHdpdGggdGhlIGRpc2Vhc2UgYXJlIG1pc3NlZCBieSB0aGUgdGVzdCAoKkZOKikuCi0gICBUaGUgdG90YWwgbnVtYmVyIG9mIGFjdHVhbCBwYXRpZW50cyB3aXRoIHRoZSBkaXNlYXNlIGlzIDEwMCAoKjgwIFRQICsgMjAgRk4qKS4KClRoZSByZWNhbGwgb2YgdGhpcyB0ZXN0IGlzOgoKJCQgXHRleHR7UmVjYWxsfSA9IFxmcmFjezgwfXs4MCArIDIwfSA9IFxmcmFjezgwfXsxMDB9ID0gMC44MCAkJAoKVGhpcyBtZWFucyB0aGF0IHRoZSB0ZXN0IGNvcnJlY3RseSBpZGVudGlmaWVzIDgwJSBvZiB0aGUgcGF0aWVudHMgd2hvIGFjdHVhbGx5IGhhdmUgdGhlIGRpc2Vhc2UuCgojIyMgUmVjYWxsIGluIE11bHRpY2xhc3MgQ2xhc3NpZmljYXRpb24KCkluIG11bHRpY2xhc3MgY2xhc3NpZmljYXRpb24sIHRoZXJlIGFyZSBtb3JlIHRoYW4gdHdvIHBvc3NpYmxlIG91dGNvbWVzLgoKIyMjIyBGb3JtdWxhIGZvciBSZWNhbGwKClJlY2FsbCBmb3IgZWFjaCBjbGFzcyBpbiBhIG11bHRpY2xhc3Mgc2V0dGluZyBpcyBjYWxjdWxhdGVkIGJ5IGNvbnNpZGVyaW5nIGVhY2ggY2xhc3MgYXMgdGhlIHBvc2l0aXZlIGNsYXNzIGFuZCB0aGUgcmVzdCBhcyBuZWdhdGl2ZSwgdGhlbiBhdmVyYWdpbmcgdGhlIHJlc3VsdHMuCgokJCBcdGV4dHtSZWNhbGx9X3tcdGV4dHtjbGFzc319ID0gXGZyYWN7XHRleHR7VHJ1ZSBQb3NpdGl2ZXMgKFRQKX1fe1x0ZXh0e2NsYXNzfX19e1x0ZXh0e1RydWUgUG9zaXRpdmVzIChUUCl9X3tcdGV4dHtjbGFzc319ICsgXHRleHR7RmFsc2UgTmVnYXRpdmVzIChGTil9X3tcdGV4dHtjbGFzc319fSAkJAoKWW91IGNhbiB0aGVuIGNhbGN1bGF0ZSBlaXRoZXIgdGhlIG1hY3JvLWF2ZXJhZ2Ugb3Igd2VpZ2h0ZWQtYXZlcmFnZSByZWNhbGw6CgotICAgKipNYWNyby1hdmVyYWdlIFJlY2FsbCoqOiBDYWxjdWxhdGUgdGhlIGF2ZXJhZ2Ugb2YgcmVjYWxsIHZhbHVlcyBmb3IgZWFjaCBjbGFzcy4KLSAgICoqV2VpZ2h0ZWQtYXZlcmFnZSBSZWNhbGwqKjogQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIG9mIHJlY2FsbCB2YWx1ZXMgZm9yIGVhY2ggY2xhc3MsIHdlaWdodGVkIGJ5IHRoZSBudW1iZXIgb2YgdHJ1ZSBpbnN0YW5jZXMgaW4gZWFjaCBjbGFzcy4KCiMjIyMgRXhhbXBsZQoKQ29uc2lkZXIgYSBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtIHdpdGggdGhyZWUgY2xhc3NlcyAoQSwgQiwgQykgd2l0aCB0aGUgZm9sbG93aW5nIHJlc3VsdHM6IC0gQ2xhc3MgQTogMzAgVFAsIDUgRk4uIC0gQ2xhc3MgQjogNDAgVFAsIDEwIEZOLiAtIENsYXNzIEM6IDUwIFRQLCAyMCBGTi4KClJlY2FsbCBmb3IgZWFjaCBjbGFzcyB3b3VsZCBiZTogLSBDbGFzcyBBOiAkXHRleHR7UmVjYWxsfV9BID0gXGZyYWN7MzB9ezMwICsgNX0gPSAwLjg2JCAtIENsYXNzIEI6ICRcdGV4dHtSZWNhbGx9X0IgPSBcZnJhY3s0MH17NDAgKyAxMH0gPSAwLjgwJCAtIENsYXNzIEM6ICRcdGV4dHtSZWNhbGx9X0MgPSBcZnJhY3s1MH17NTAgKyAyMH0gPSAwLjcxJAoKVGhlIG1hY3JvLWF2ZXJhZ2UgcmVjYWxsIGFjcm9zcyBhbGwgY2xhc3NlcyB3b3VsZCBiZSB0aGUgYXZlcmFnZSBvZiB0aGVzZSB0aHJlZSB2YWx1ZXMuCgojIyMgS2V5IFBvaW50cwoKLSAgICoqQmluYXJ5IENsYXNzaWZpY2F0aW9uKio6IFJlY2FsbCBtZWFzdXJlcyB0aGUgcHJvcG9ydGlvbiBvZiBhY3R1YWwgcG9zaXRpdmVzIGNvcnJlY3RseSBpZGVudGlmaWVkLgotICAgKipNdWx0aWNsYXNzIENsYXNzaWZpY2F0aW9uKio6IFJlY2FsbCBpcyBjb21wdXRlZCBmb3IgZWFjaCBjbGFzcyBpbmRpdmlkdWFsbHkgYW5kIHRoZW4gYXZlcmFnZWQuCi0gICAqKkltcG9ydGFuY2UqKjogSGlnaCByZWNhbGwgaW5kaWNhdGVzIGEgbG93ZXIgbnVtYmVyIG9mIGZhbHNlIG5lZ2F0aXZlcy4gSXQgaXMgcGFydGljdWxhcmx5IGltcG9ydGFudCBpbiBzY2VuYXJpb3MgbGlrZSBtZWRpY2FsIGRpYWdub3Npcywgd2hlcmUgbWlzc2luZyBhbiBhY3R1YWwgcG9zaXRpdmUgY2FzZSAoYSBkaXNlYXNlKSBjYW4gYmUgY3JpdGljYWwuCgpSZWNhbGwgaXMgYSB2YWx1YWJsZSBtZXRyaWMsIGVzcGVjaWFsbHkgd2hlbiB0aGUgY29uc2VxdWVuY2VzIG9mIGZhbHNlIG5lZ2F0aXZlcyBhcmUgc2lnbmlmaWNhbnQuIEhvd2V2ZXIsIGl0IHNob3VsZCBiZSBiYWxhbmNlZCB3aXRoIG90aGVyIG1ldHJpY3MgbGlrZSBwcmVjaXNpb24gYW5kIGFjY3VyYWN5IGZvciBhIHdlbGwtcm91bmRlZCBtb2RlbCBldmFsdWF0aW9uLgoKIyMgRjEgU2NvcmUKClRoZSBGMSBzY29yZSBpcyBhIG1ldHJpYyB0aGF0IGNvbWJpbmVzIHByZWNpc2lvbiBhbmQgcmVjYWxsIGludG8gYSBzaW5nbGUgbnVtYmVyLCBwcm92aWRpbmcgYSBiYWxhbmNlZCBtZWFzdXJlIG9mIGEgbW9kZWwncyBhY2N1cmFjeSwgZXNwZWNpYWxseSB3aGVuIGRlYWxpbmcgd2l0aCBpbWJhbGFuY2VkIGRhdGFzZXRzLgoKIyMjIEYxIFNjb3JlIGluIEJpbmFyeSBDbGFzc2lmaWNhdGlvbgoKSW4gYmluYXJ5IGNsYXNzaWZpY2F0aW9uLCB3aGVyZSBvdXRjb21lcyBhcmUgbGFiZWxlZCBhcyBwb3NpdGl2ZSBvciBuZWdhdGl2ZSwgdGhlIEYxIHNjb3JlIGlzIHBhcnRpY3VsYXJseSB1c2VmdWwuCgojIyMjIEZvcm11bGEgZm9yIEYxIFNjb3JlCgpUaGUgZm9ybXVsYSBmb3IgdGhlIEYxIHNjb3JlIGluIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiBpcyB0aGUgaGFybW9uaWMgbWVhbiBvZiBwcmVjaXNpb24gYW5kIHJlY2FsbDoKCiQkIFx0ZXh0e0YxIFNjb3JlfSA9IDIgXHRpbWVzIFxmcmFje1x0ZXh0e1ByZWNpc2lvbn0gXHRpbWVzIFx0ZXh0e1JlY2FsbH19e1x0ZXh0e1ByZWNpc2lvbn0gKyBcdGV4dHtSZWNhbGx9fSAkJAoKV2hlcmU6CgotICAgKipQcmVjaXNpb24qKjogVHJ1ZSBQb3NpdGl2ZXMgLyAoVHJ1ZSBQb3NpdGl2ZXMgKyBGYWxzZSBQb3NpdGl2ZXMpCi0gICAqKlJlY2FsbCoqOiBUcnVlIFBvc2l0aXZlcyAvIChUcnVlIFBvc2l0aXZlcyArIEZhbHNlIE5lZ2F0aXZlcykKCiMjIyMgRXhhbXBsZQoKQ29uc2lkZXIgYSBiaW5hcnkgY2xhc3NpZmljYXRpb24gdGFzazogLSBQcmVjaXNpb24gPSAwLjc1ICg3NSUgb2YgdGhlIHByZWRpY3RlZCBwb3NpdGl2ZXMgYXJlIGNvcnJlY3QpIC0gUmVjYWxsID0gMC42MCAoNjAlIG9mIGFjdHVhbCBwb3NpdGl2ZXMgYXJlIGNvcnJlY3RseSBpZGVudGlmaWVkKQoKVGhlIEYxIHNjb3JlIHdvdWxkIGJlOgoKJCQgXHRleHR7RjEgU2NvcmV9ID0gMiBcdGltZXMgXGZyYWN7MC43NSBcdGltZXMgMC42MH17MC43NSArIDAuNjB9IFxhcHByb3ggMC42NyAkJAoKIyMjIEYxIFNjb3JlIGluIE11bHRpY2xhc3MgQ2xhc3NpZmljYXRpb24KCkluIG11bHRpY2xhc3MgY2xhc3NpZmljYXRpb24sIHRoZSBGMSBzY29yZSBuZWVkcyB0byBiZSBjYWxjdWxhdGVkIGZvciBlYWNoIGNsYXNzIGFuZCB0aGVuIGF2ZXJhZ2VkLgoKIyMjIyBTdGVwcyB0byBDYWxjdWxhdGUgRjEgU2NvcmUKCjEuICAqKkNhbGN1bGF0ZSBQcmVjaXNpb24gYW5kIFJlY2FsbCBmb3IgRWFjaCBDbGFzcyoqOiBUcmVhdCBlYWNoIGNsYXNzIGFzIHRoZSBwb3NpdGl2ZSBjbGFzcyBhbmQgY2FsY3VsYXRlIHByZWNpc2lvbiBhbmQgcmVjYWxsLgoyLiAgKipDYWxjdWxhdGUgRjEgU2NvcmUgZm9yIEVhY2ggQ2xhc3MqKjogVXNlIHRoZSBmb3JtdWxhIGZvciBlYWNoIGNsYXNzIGluZGl2aWR1YWxseS4KMy4gICoqQXZlcmFnZSB0aGUgRjEgU2NvcmVzKio6IFlvdSBjYW4gdXNlIGVpdGhlcjoKICAgIC0gICAqKk1hY3JvLWF2ZXJhZ2UqKjogU2ltcGx5IGF2ZXJhZ2UgdGhlIEYxIHNjb3JlcyBvZiBhbGwgY2xhc3Nlcy4KICAgIC0gICAqKldlaWdodGVkLWF2ZXJhZ2UqKjogQXZlcmFnZSB0aGUgRjEgc2NvcmVzLCB3ZWlnaHRlZCBieSB0aGUgbnVtYmVyIG9mIHRydWUgaW5zdGFuY2VzIGZvciBlYWNoIGNsYXNzLgoKIyMjIyBFeGFtcGxlCgpMZXQncyBzYXkgd2UgaGF2ZSBhIGNsYXNzaWZpY2F0aW9uIHByb2JsZW0gd2l0aCB0aHJlZSBjbGFzc2VzIChBLCBCLCBhbmQgQyksIGFuZCB3ZSBjYWxjdWxhdGVkIHRoZSBmb2xsb3dpbmcgcHJlY2lzaW9uIGFuZCByZWNhbGwgZm9yIGVhY2ggY2xhc3M6IC0gQ2xhc3MgQTogUHJlY2lzaW9uID0gMC44MCwgUmVjYWxsID0gMC43MCAtIENsYXNzIEI6IFByZWNpc2lvbiA9IDAuNjAsIFJlY2FsbCA9IDAuNTAgLSBDbGFzcyBDOiBQcmVjaXNpb24gPSAwLjkwLCBSZWNhbGwgPSAwLjg1CgpUaGUgRjEgc2NvcmVzIGZvciBlYWNoIGNsYXNzIHdvdWxkIGJlOiAtIENsYXNzIEE6ICRcdGV4dHtGMX1fQSA9IDIgXHRpbWVzIFxmcmFjezAuODAgXHRpbWVzIDAuNzB9ezAuODAgKyAwLjcwfSBcYXBwcm94IDAuNzQkIC0gQ2xhc3MgQjogJFx0ZXh0e0YxfV9CID0gMiBcdGltZXMgXGZyYWN7MC42MCBcdGltZXMgMC41MH17MC42MCArIDAuNTB9IFxhcHByb3ggMC41NSQgLSBDbGFzcyBDOiAkXHRleHR7RjF9X0MgPSAyIFx0aW1lcyBcZnJhY3swLjkwIFx0aW1lcyAwLjg1fXswLjkwICsgMC44NX0gXGFwcHJveCAwLjg3JAoKVGhlIG1hY3JvLWF2ZXJhZ2UgRjEgc2NvcmUgd291bGQgYmUgdGhlIGF2ZXJhZ2Ugb2YgdGhlc2UgdmFsdWVzLgoKIyMjIEtleSBQb2ludHMKCi0gICBUaGUgRjEgc2NvcmUgcHJvdmlkZXMgYSBiYWxhbmNlIGJldHdlZW4gcHJlY2lzaW9uIGFuZCByZWNhbGwsIGJlaW5nIHBhcnRpY3VsYXJseSB1c2VmdWwgaW4gc2l0dWF0aW9ucyB3aGVyZSB0aGVyZSBpcyBhbiBpbWJhbGFuY2UgaW4gdGhlIGRhdGFzZXQgb3Igd2hlbiBmYWxzZSBwb3NpdGl2ZXMgYW5kIGZhbHNlIG5lZ2F0aXZlcyBjYXJyeSBkaWZmZXJlbnQgY29zdHMuCi0gICBJbiBiaW5hcnkgY2xhc3NpZmljYXRpb24sIGl0J3Mgc3RyYWlnaHRmb3J3YXJkIGFzIGl0IGRpcmVjdGx5IGNvbWJpbmVzIHRoZSBtb2RlbCdzIHByZWNpc2lvbiBhbmQgcmVjYWxsLgotICAgSW4gbXVsdGljbGFzcyBjbGFzc2lmaWNhdGlvbiwgdGhlIEYxIHNjb3JlIGlzIGNhbGN1bGF0ZWQgZm9yIGVhY2ggY2xhc3MgYW5kIHRoZW4gYXZlcmFnZWQsIHByb3ZpZGluZyBhIGNvbXByZWhlbnNpdmUgdmlldyBvZiB0aGUgbW9kZWwncyBwZXJmb3JtYW5jZSBhY3Jvc3MgYWxsIGNsYXNzZXMuCgpUaGUgRjEgc2NvcmUgaXMgYSB1c2VmdWwgbWV0cmljIGluIG1hbnkgc2NlbmFyaW9zLCBhcyBpdCBhY2NvdW50cyBmb3IgYm90aCB0aGUgcHJlY2lzaW9uIGFuZCByZWNhbGwgb2YgdGhlIG1vZGVsLCBwcm92aWRpbmcgYSBtb3JlIGhvbGlzdGljIHZpZXcgb2YgaXRzIHBlcmZvcm1hbmNlLgoKIyMgQ29uZnVzaW9uIE1hdHJpeAoKQSBjb25mdXNpb24gbWF0cml4IGlzIGEgdG9vbCB1c2VkIGluIHN1cGVydmlzZWQgbGVhcm5pbmcgdG8gdmlzdWFsaXplIHRoZSBwZXJmb3JtYW5jZSBvZiBhIGNsYXNzaWZpY2F0aW9uIG1vZGVsLiBJdCdzIHBhcnRpY3VsYXJseSB1c2VmdWwgZm9yIHVuZGVyc3RhbmRpbmcgdGhlIHR5cGVzIG9mIGVycm9ycyBhIG1vZGVsIGlzIG1ha2luZy4KCiMjIyBDb25mdXNpb24gTWF0cml4IGluIEJpbmFyeSBDbGFzc2lmaWNhdGlvbgoKSW4gYmluYXJ5IGNsYXNzaWZpY2F0aW9uLCB0aGUgY29uZnVzaW9uIG1hdHJpeCBpcyBhIDJ4MiB0YWJsZSB0aGF0IHNob3dzIHRoZSBudW1iZXIgb2YgdHJ1ZSBwb3NpdGl2ZXMsIHRydWUgbmVnYXRpdmVzLCBmYWxzZSBwb3NpdGl2ZXMsIGFuZCBmYWxzZSBuZWdhdGl2ZXMuCgojIyMjIENvbXBvbmVudHMgb2YgYSBCaW5hcnkgQ29uZnVzaW9uIE1hdHJpeAoKMS4gICoqVHJ1ZSBQb3NpdGl2ZXMgKFRQKSoqOiBDb3JyZWN0bHkgcHJlZGljdGVkIHBvc2l0aXZlIGNhc2VzLgoyLiAgKipUcnVlIE5lZ2F0aXZlcyAoVE4pKio6IENvcnJlY3RseSBwcmVkaWN0ZWQgbmVnYXRpdmUgY2FzZXMuCjMuICAqKkZhbHNlIFBvc2l0aXZlcyAoRlApKio6IEluY29ycmVjdGx5IHByZWRpY3RlZCBwb3NpdGl2ZSBjYXNlcyAoVHlwZSBJIGVycm9yKS4KNC4gICoqRmFsc2UgTmVnYXRpdmVzIChGTikqKjogSW5jb3JyZWN0bHkgcHJlZGljdGVkIG5lZ2F0aXZlIGNhc2VzIChUeXBlIElJIGVycm9yKS4KCiMjIyMgRXhhbXBsZQoKSW1hZ2luZSBhIG1lZGljYWwgdGVzdCBmb3IgYSBkaXNlYXNlOiAtIDUwIHBhdGllbnRzIGhhdmUgdGhlIGRpc2Vhc2UgYW5kIHRoZSB0ZXN0IGNvcnJlY3RseSBpZGVudGlmaWVzIDQwIChUUCA9IDQwKS4gLSAxMDAgcGF0aWVudHMgZG8gbm90IGhhdmUgdGhlIGRpc2Vhc2UgYW5kIHRoZSB0ZXN0IGNvcnJlY3RseSBpZGVudGlmaWVzIDkwIChUTiA9IDkwKS4gLSBUaGUgdGVzdCBpbmNvcnJlY3RseSBpZGVudGlmaWVzIDEwIGhlYWx0aHkgcGF0aWVudHMgYXMgaGF2aW5nIHRoZSBkaXNlYXNlIChGUCA9IDEwKS4gLSBUaGUgdGVzdCBmYWlscyB0byBpZGVudGlmeSB0aGUgZGlzZWFzZSBpbiAxMCBwYXRpZW50cyB3aG8gaGF2ZSBpdCAoRk4gPSAxMCkuCgpUaGUgY29uZnVzaW9uIG1hdHJpeCB3b3VsZCBsb29rIGxpa2UgdGhpczoKCnwgICAgICAgICAgICAgICAgICAgICB8IFByZWRpY3RlZCBQb3NpdGl2ZSB8IFByZWRpY3RlZCBOZWdhdGl2ZSB8CnwtLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS18CnwgKipBY3R1YWwgUG9zaXRpdmUqKiB8IDQwIChUUCkgICAgICAgICAgICB8IDEwIChGTikgICAgICAgICAgICB8CnwgKipBY3R1YWwgTmVnYXRpdmUqKiB8IDEwIChGUCkgICAgICAgICAgICB8IDkwIChUTikgICAgICAgICAgICB8CgojIyMgQ29uZnVzaW9uIE1hdHJpeCBpbiBNdWx0aWNsYXNzIENsYXNzaWZpY2F0aW9uCgpJbiBtdWx0aWNsYXNzIGNsYXNzaWZpY2F0aW9uLCB0aGUgY29uZnVzaW9uIG1hdHJpeCBpcyBsYXJnZXIsIHdpdGggZGltZW5zaW9ucyBlcXVhbCB0byB0aGUgbnVtYmVyIG9mIGNsYXNzZXMuIEVhY2ggcm93IHJlcHJlc2VudHMgdGhlIGluc3RhbmNlcyBpbiBhbiBhY3R1YWwgY2xhc3MsIGFuZCBlYWNoIGNvbHVtbiByZXByZXNlbnRzIHRoZSBpbnN0YW5jZXMgaW4gYSBwcmVkaWN0ZWQgY2xhc3MuCgojIyMjIFN0ZXBzIHRvIENhbGN1bGF0ZSBhIE11bHRpY2xhc3MgQ29uZnVzaW9uIE1hdHJpeAoKMS4gICoqRGV0ZXJtaW5lIHRoZSBOdW1iZXIgb2YgQ2xhc3NlcyoqOiBTdXBwb3NlIHRoZXJlIGFyZSBOIGNsYXNzZXMuCjIuICAqKkNyZWF0ZSBhbiBOeE4gTWF0cml4Kio6IEVhY2ggY2VsbCAoaSwgaikgaW4gdGhlIG1hdHJpeCByZXByZXNlbnRzIHRoZSBudW1iZXIgb2YgaW5zdGFuY2VzIG9mIGNsYXNzIGkgKGFjdHVhbCkgcHJlZGljdGVkIGFzIGNsYXNzIGouCgojIyMjIEV4YW1wbGUKCkNvbnNpZGVyIGEgY2xhc3NpZmljYXRpb24gcHJvYmxlbSB3aXRoIHRocmVlIGNsYXNzZXM6IEEsIEIsIGFuZCBDLiBBZnRlciBhcHBseWluZyB0aGUgbW9kZWwgb24gYSBkYXRhc2V0LCB3ZSBnZXQgdGhlIGZvbGxvd2luZyByZXN1bHRzOgoKLSAgIDMwIG9mIENsYXNzIEEgd2VyZSBjb3JyZWN0bHkgY2xhc3NpZmllZCAoVFAgZm9yIEEpLCA1IHdlcmUgY2xhc3NpZmllZCBhcyBCLCBhbmQgNSBhcyBDLgotICAgNCBvZiBDbGFzcyBCIHdlcmUgaW5jb3JyZWN0bHkgY2xhc3NpZmllZCBhcyBBLCA0MCB3ZXJlIGNvcnJlY3RseSBjbGFzc2lmaWVkIChUUCBmb3IgQiksIGFuZCA2IGFzIEMuCi0gICA2IG9mIENsYXNzIEMgd2VyZSBjbGFzc2lmaWVkIGFzIEEsIDggYXMgQiwgYW5kIDQ2IHdlcmUgY29ycmVjdGx5IGNsYXNzaWZpZWQgKFRQIGZvciBDKS4KClRoZSBjb25mdXNpb24gbWF0cml4IHdvdWxkIGxvb2sgbGlrZSB0aGlzOgoKfCAgICAgICAgICAgICAgfCBQcmVkaWN0ZWQgQSB8IFByZWRpY3RlZCBCIHwgUHJlZGljdGVkIEMgfAp8LS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS18CnwgKipBY3R1YWwgQSoqIHwgMzAgICAgICAgICAgfCA1ICAgICAgICAgICB8IDUgICAgICAgICAgIHwKfCAqKkFjdHVhbCBCKiogfCA0ICAgICAgICAgICB8IDQwICAgICAgICAgIHwgNiAgICAgICAgICAgfAp8ICoqQWN0dWFsIEMqKiB8IDYgICAgICAgICAgIHwgOCAgICAgICAgICAgfCA0NiAgICAgICAgICB8CgojIyMgS2V5IFBvaW50cwoKLSAgIEluIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiwgdGhlIGNvbmZ1c2lvbiBtYXRyaXggaXMgYSBzaW1wbGUgMngyIHRhYmxlLCB3aGVyZWFzLCBpbiBtdWx0aWNsYXNzIGNsYXNzaWZpY2F0aW9uLCBpdCBleHBhbmRzIHRvIGFjY29tbW9kYXRlIGFsbCBjbGFzc2VzLgotICAgVGhlIGRpYWdvbmFsIGNlbGxzICh0b3AtbGVmdCB0byBib3R0b20tcmlnaHQpIHJlcHJlc2VudCB0aGUgbnVtYmVyIG9mIGNvcnJlY3QgcHJlZGljdGlvbnMgKHRydWUgcG9zaXRpdmVzIGZvciBlYWNoIGNsYXNzKS4KLSAgIE9mZi1kaWFnb25hbCBjZWxscyBzaG93IHRoZSBkaXN0cmlidXRpb24gb2YgZXJyb3JzLCBpbmRpY2F0aW5nIHdoaWNoIGNsYXNzZXMgYXJlIGJlaW5nIGNvbmZ1c2VkIHdpdGggb3RoZXJzLgoKQSBjb25mdXNpb24gbWF0cml4IHByb3ZpZGVzIGEgZGV0YWlsZWQgYnJlYWtkb3duIG9mIGEgbW9kZWwncyBwZXJmb3JtYW5jZSBhbmQgaXMgZXNwZWNpYWxseSB1c2VmdWwgZm9yIGlkZW50aWZ5aW5nIHdoZXRoZXIgYSBtb2RlbCBpcyBjb25mdXNpbmcgdHdvIGNsYXNzZXMsIHdoaWNoIGNhbiBiZSBjcnVjaWFsIGZvciBpbXByb3ZpbmcgbW9kZWwgYWNjdXJhY3kuCgojIyBTcGVjaWZpY2l0eQoKU3BlY2lmaWNpdHkgaXMgYSBtZXRyaWMgdXNlZCBpbiBjbGFzc2lmaWNhdGlvbiB0YXNrcyB0byBtZWFzdXJlIHRoZSBwcm9wb3J0aW9uIG9mIGFjdHVhbCBuZWdhdGl2ZXMgdGhhdCBhcmUgY29ycmVjdGx5IGlkZW50aWZpZWQuIEl0J3MgcGFydGljdWxhcmx5IGltcG9ydGFudCBpbiBjb250ZXh0cyB3aGVyZSB0aGUgY29zdCBvZiBmYWxzZSBwb3NpdGl2ZXMgaXMgaGlnaC4KCiMjIyBTcGVjaWZpY2l0eSBpbiBCaW5hcnkgQ2xhc3NpZmljYXRpb24KCkluIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiwgd2hlcmUgdGhlcmUgYXJlIG9ubHkgdHdvIGNsYXNzZXMgKHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSksIHNwZWNpZmljaXR5IGlzIHN0cmFpZ2h0Zm9yd2FyZC4KCiMjIyMgRm9ybXVsYSBmb3IgU3BlY2lmaWNpdHkKClRoZSBmb3JtdWxhIGZvciBzcGVjaWZpY2l0eSBpbiBiaW5hcnkgY2xhc3NpZmljYXRpb24gaXM6CgokJCBcdGV4dHtTcGVjaWZpY2l0eX0gPSBcZnJhY3tcdGV4dHtUcnVlIE5lZ2F0aXZlcyAoVE4pfX17XHRleHR7VHJ1ZSBOZWdhdGl2ZXMgKFROKSArIEZhbHNlIFBvc2l0aXZlcyAoRlApfX0gJCQKCi0gICAqKlRydWUgTmVnYXRpdmVzIChUTikqKjogQ29ycmVjdGx5IHByZWRpY3RlZCBuZWdhdGl2ZSBvYnNlcnZhdGlvbnMuCi0gICAqKkZhbHNlIFBvc2l0aXZlcyAoRlApKio6IEluY29ycmVjdGx5IHByZWRpY3RlZCBwb3NpdGl2ZSBvYnNlcnZhdGlvbnMuCgojIyMjIEV4YW1wbGUKCkNvbnNpZGVyIGEgbWVkaWNhbCB0ZXN0IGZvciBhIGRpc2Vhc2U6IC0gMTAwIGhlYWx0aHkgaW5kaXZpZHVhbHMgYXJlIHRlc3RlZCwgOTAgb2Ygd2hvbSBhcmUgY29ycmVjdGx5IGlkZW50aWZpZWQgYXMgbm90IGhhdmluZyB0aGUgZGlzZWFzZSAoVE4gPSA5MCkuIC0gMTAgaGVhbHRoeSBpbmRpdmlkdWFscyBhcmUgaW5jb3JyZWN0bHkgaWRlbnRpZmllZCBhcyBoYXZpbmcgdGhlIGRpc2Vhc2UgKEZQID0gMTApLgoKVGhlIHNwZWNpZmljaXR5IG9mIHRoaXMgdGVzdCBpczoKCiQkIFx0ZXh0e1NwZWNpZmljaXR5fSA9IFxmcmFjezkwfXs5MCArIDEwfSA9IFxmcmFjezkwfXsxMDB9ID0gMC45MCAkJAoKVGhpcyBtZWFucyB0aGUgdGVzdCBjb3JyZWN0bHkgaWRlbnRpZmllcyA5MCUgb2YgaGVhbHRoeSBpbmRpdmlkdWFscy4KCiMjIyBTcGVjaWZpY2l0eSBpbiBNdWx0aWNsYXNzIENsYXNzaWZpY2F0aW9uCgpJbiBtdWx0aWNsYXNzIGNsYXNzaWZpY2F0aW9uLCBzcGVjaWZpY2l0eSBpcyBjYWxjdWxhdGVkIGZvciBlYWNoIGNsYXNzIGJ5IGNvbnNpZGVyaW5nIGl0IGFzIHRoZSAibmVnYXRpdmUiIGNsYXNzIHdoaWxlIGdyb3VwaW5nIGFsbCBvdGhlciBjbGFzc2VzIGFzICJwb3NpdGl2ZS4iCgojIyMjIFN0ZXBzIHRvIENhbGN1bGF0ZSBTcGVjaWZpY2l0eSBmb3IgRWFjaCBDbGFzcwoKMS4gICoqVHJlYXQgRWFjaCBDbGFzcyBhcyBOZWdhdGl2ZSBPbmNlKio6IEZvciBlYWNoIGNsYXNzLCBjYWxjdWxhdGUgVE4gYW5kIEZQIHdpdGggcmVzcGVjdCB0byB0aGF0IGNsYXNzLgoyLiAgKipDYWxjdWxhdGUgU3BlY2lmaWNpdHkgZm9yIEVhY2ggQ2xhc3MqKjogVXNlIHRoZSBiaW5hcnkgc3BlY2lmaWNpdHkgZm9ybXVsYSBmb3IgZWFjaCBjbGFzcy4KMy4gICoqQXZlcmFnZSB0aGUgU3BlY2lmaWNpdHkgU2NvcmVzKio6IFlvdSBjYW4gY2FsY3VsYXRlIGFuIGF2ZXJhZ2Ugb3Igd2VpZ2h0ZWQtYXZlcmFnZSBzcGVjaWZpY2l0eSwgc2ltaWxhciB0byBwcmVjaXNpb24gYW5kIHJlY2FsbC4KCiMjIyMgRXhhbXBsZQoKQ29uc2lkZXIgYSBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtIHdpdGggdGhyZWUgY2xhc3NlcyAoQSwgQiwgYW5kIEMpLiBBZnRlciBhcHBseWluZyB0aGUgbW9kZWwsIHdlIGhhdmUgdGhlIGZvbGxvd2luZyBjb25mdXNpb24gbWF0cml4OgoKfCAgICAgICAgICAgICAgfCBQcmVkaWN0ZWQgQSAgIHwgUHJlZGljdGVkIEIgICB8IFByZWRpY3RlZCBDICAgfAp8LS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS18CnwgKipBY3R1YWwgQSoqIHwgMzAgKFRQIGZvciBBKSB8IDUgKEZQIGZvciBDKSAgfCA1IChGUCBmb3IgQykgIHwKfCAqKkFjdHVhbCBCKiogfCA0IChGUCBmb3IgQSkgIHwgNDAgKFRQIGZvciBCKSB8IDYgKEZQIGZvciBDKSAgfAp8ICoqQWN0dWFsIEMqKiB8IDYgKEZQIGZvciBBKSAgfCA4IChGUCBmb3IgQikgIHwgNDYgKFRQIGZvciBDKSB8CgpUbyBjYWxjdWxhdGUgc3BlY2lmaWNpdHkgZm9yIGVhY2ggY2xhc3M6IC0gRm9yIENsYXNzIEE6IENvbnNpZGVyIEIgYW5kIEMgcHJlZGljdGlvbnMgYXMgInBvc2l0aXZlIiBhbmQgQSBhcyAibmVnYXRpdmUiLiAtIEZvciBDbGFzcyBCOiBDb25zaWRlciBBIGFuZCBDIHByZWRpY3Rpb25zIGFzICJwb3NpdGl2ZSIgYW5kIEIgYXMgIm5lZ2F0aXZlIi4gLSBGb3IgQ2xhc3MgQzogQ29uc2lkZXIgQSBhbmQgQiBwcmVkaWN0aW9ucyBhcyAicG9zaXRpdmUiIGFuZCBDIGFzICJuZWdhdGl2ZSIuCgpUaGVuIGNhbGN1bGF0ZSBzcGVjaWZpY2l0eSBmb3IgZWFjaCBjbGFzcyB1c2luZyB0aGUgYmluYXJ5IGZvcm11bGEuCgojIyMgS2V5IFBvaW50cwoKLSAgICoqQmluYXJ5IENsYXNzaWZpY2F0aW9uKio6IFNwZWNpZmljaXR5IG1lYXN1cmVzIHRoZSBhY2N1cmFjeSBpbiBpZGVudGlmeWluZyBhY3R1YWwgbmVnYXRpdmVzLgotICAgKipNdWx0aWNsYXNzIENsYXNzaWZpY2F0aW9uKio6IFNwZWNpZmljaXR5IGlzIGNhbGN1bGF0ZWQgZm9yIGVhY2ggY2xhc3MgYnkgdHJlYXRpbmcgaXQgYXMgdGhlICJuZWdhdGl2ZSIgY2xhc3Mgb25jZS4KLSAgICoqSW1wb3J0YW5jZSoqOiBTcGVjaWZpY2l0eSBpcyBjcnVjaWFsIGluIHNjZW5hcmlvcyB3aGVyZSBmYWxzZSBwb3NpdGl2ZXMgY2Fycnkgc2lnbmlmaWNhbnQgY29uc2VxdWVuY2VzLgoKU3BlY2lmaWNpdHkgaXMgb2Z0ZW4gdXNlZCBhbG9uZ3NpZGUgc2Vuc2l0aXZpdHkgKHJlY2FsbCkgdG8gcHJvdmlkZSBhIGNvbXByZWhlbnNpdmUgdmlldyBvZiBhIG1vZGVsJ3MgcGVyZm9ybWFuY2UsIGVzcGVjaWFsbHkgaW4gbWVkaWNhbCB0ZXN0aW5nLCB3aGVyZSBkaXN0aW5ndWlzaGluZyBiZXR3ZWVuIGhlYWx0aHkgYW5kIGRpc2Vhc2VkIGluZGl2aWR1YWxzIGFjY3VyYXRlbHkgaXMgdml0YWwuCgojIyBQcmVjaXNpb24gdnMgU3BlY2lmaWNpdHkKClByZWNpc2lvbiBhbmQgc3BlY2lmaWNpdHkgYXJlIGJvdGggbWV0cmljcyB1c2VkIGluIGNsYXNzaWZpY2F0aW9uIHRhc2tzLCBidXQgdGhleSBmb2N1cyBvbiBkaWZmZXJlbnQgYXNwZWN0cyBvZiB0aGUgbW9kZWwncyBwZXJmb3JtYW5jZS4KCiMjIyBQcmVjaXNpb24KCi0gICAqKkRlZmluaXRpb24qKjogUHJlY2lzaW9uIG1lYXN1cmVzIHRoZSBhY2N1cmFjeSBvZiB0aGUgcG9zaXRpdmUgcHJlZGljdGlvbnMgbWFkZSBieSB0aGUgbW9kZWwuIEl0IGlzIHRoZSByYXRpbyBvZiB0cnVlIHBvc2l0aXZlcyAoY29ycmVjdCBwb3NpdGl2ZSBwcmVkaWN0aW9ucykgdG8gdGhlIHRvdGFsIG51bWJlciBvZiBwb3NpdGl2ZSBwcmVkaWN0aW9ucyBtYWRlIGJ5IHRoZSBtb2RlbCAoYm90aCB0cnVlIHBvc2l0aXZlcyBhbmQgZmFsc2UgcG9zaXRpdmVzKS4KLSAgICoqRm9ybXVsYSoqOiAkJCBcdGV4dHtQcmVjaXNpb259ID0gXGZyYWN7XHRleHR7VHJ1ZSBQb3NpdGl2ZXMgKFRQKX19e1x0ZXh0e1RydWUgUG9zaXRpdmVzIChUUCkgKyBGYWxzZSBQb3NpdGl2ZXMgKEZQKX19ICQkCi0gICAqKlVzZSoqOiBQcmVjaXNpb24gaXMgdXNlZCB3aGVuIHRoZSBjb3N0IG9mIGEgZmFsc2UgcG9zaXRpdmUgaXMgaGlnaC4gSW4gc2NlbmFyaW9zIHdoZXJlIGl0J3MgY3JpdGljYWwgbm90IHRvIGxhYmVsIGEgbmVnYXRpdmUgaW5zdGFuY2UgYXMgcG9zaXRpdmUsIHByZWNpc2lvbiBiZWNvbWVzIGEga2V5IG1ldHJpYy4KLSAgICoqRXhhbXBsZSoqOiBJbiBlbWFpbCBzcGFtIGRldGVjdGlvbiwgcHJlY2lzaW9uIGlzIGltcG9ydGFudC4gSWYgYSBzcGFtIGZpbHRlciBoYXMgbG93IHByZWNpc2lvbiwgaXQgbWVhbnMgbWFueSBsZWdpdGltYXRlIGVtYWlscyBhcmUgaW5jb3JyZWN0bHkgbWFya2VkIGFzIHNwYW0gKGZhbHNlIHBvc2l0aXZlcyksIHdoaWNoIGNvdWxkIGxlYWQgdG8gaW1wb3J0YW50IGVtYWlscyBiZWluZyBtaXNzZWQuCgojIyMgU3BlY2lmaWNpdHkKCi0gICAqKkRlZmluaXRpb24qKjogU3BlY2lmaWNpdHkgbWVhc3VyZXMgdGhlIGFiaWxpdHkgb2YgdGhlIG1vZGVsIHRvIGNvcnJlY3RseSBpZGVudGlmeSBuZWdhdGl2ZXMuIEl0IGlzIHRoZSByYXRpbyBvZiB0cnVlIG5lZ2F0aXZlcyAoY29ycmVjdCBuZWdhdGl2ZSBwcmVkaWN0aW9ucykgdG8gdGhlIHRvdGFsIG51bWJlciBvZiBhY3R1YWwgbmVnYXRpdmUgaW5zdGFuY2VzIChib3RoIHRydWUgbmVnYXRpdmVzIGFuZCBmYWxzZSBwb3NpdGl2ZXMpLgotICAgKipGb3JtdWxhKio6ICQkIFx0ZXh0e1NwZWNpZmljaXR5fSA9IFxmcmFje1x0ZXh0e1RydWUgTmVnYXRpdmVzIChUTil9fXtcdGV4dHtUcnVlIE5lZ2F0aXZlcyAoVE4pICsgRmFsc2UgUG9zaXRpdmVzIChGUCl9fSAkJAotICAgKipVc2UqKjogU3BlY2lmaWNpdHkgaXMgdXNlZCB3aGVuIGl0IGlzIGNydWNpYWwgdG8gY29ycmVjdGx5IGlkZW50aWZ5IG5lZ2F0aXZlIGNhc2VzLiBJdCdzIGltcG9ydGFudCBpbiBjYXNlcyB3aGVyZSBtaXNzaW5nIGEgbmVnYXRpdmUgY2FuIGhhdmUgc2VyaW91cyBjb25zZXF1ZW5jZXMuCi0gICAqKkV4YW1wbGUqKjogSW4gYSBtZWRpY2FsIHRlc3QgZm9yIGEgcmFyZSBidXQgc2VyaW91cyBkaXNlYXNlLCBzcGVjaWZpY2l0eSBpcyBjcnVjaWFsLiBBIGxvdyBzcGVjaWZpY2l0eSBtZWFucyBtYW55IGhlYWx0aHkgaW5kaXZpZHVhbHMgYXJlIGluY29ycmVjdGx5IGRpYWdub3NlZCB3aXRoIHRoZSBkaXNlYXNlIChmYWxzZSBwb3NpdGl2ZXMpLCBsZWFkaW5nIHRvIHVubmVjZXNzYXJ5IHN0cmVzcyBhbmQgcG90ZW50aWFsbHkgaGFybWZ1bCB0cmVhdG1lbnRzLgoKIyMjIEtleSBEaWZmZXJlbmNlcwoKLSAgICoqRm9jdXMqKjogUHJlY2lzaW9uIGZvY3VzZXMgb24gdGhlIHByb3BvcnRpb24gb2YgY29ycmVjdCBwb3NpdGl2ZSBwcmVkaWN0aW9ucyBvdXQgb2YgYWxsIHBvc2l0aXZlIHByZWRpY3Rpb25zLCB3aGlsZSBzcGVjaWZpY2l0eSBmb2N1c2VzIG9uIGNvcnJlY3RseSBpZGVudGlmeWluZyBuZWdhdGl2ZSBjYXNlcy4KLSAgICoqRmFsc2UgUG9zaXRpdmVzKio6IFByZWNpc2lvbiBpcyBhZmZlY3RlZCBieSBmYWxzZSBwb3NpdGl2ZXMgaW4gdGhlIGNvbnRleHQgb2YgcG9zaXRpdmUgcHJlZGljdGlvbnMsIHdoZXJlYXMgc3BlY2lmaWNpdHkgaXMgYWZmZWN0ZWQgYnkgZmFsc2UgcG9zaXRpdmVzIGluIHRoZSBjb250ZXh0IG9mIG5lZ2F0aXZlIGNhc2VzLgotICAgKipTY2VuYXJpb3MqKjogUHJlY2lzaW9uIGlzIGtleSBpbiBzY2VuYXJpb3Mgd2hlcmUgd3JvbmdseSBsYWJlbGluZyBuZWdhdGl2ZXMgYXMgcG9zaXRpdmVzIGlzIHByb2JsZW1hdGljLCB3aGVyZWFzIHNwZWNpZmljaXR5IGlzIGtleSBpbiBzY2VuYXJpb3Mgd2hlcmUgZmFpbGluZyB0byBpZGVudGlmeSB0cnVlIG5lZ2F0aXZlcyBpcyBwcm9ibGVtYXRpYy4KClByZWNpc2lvbiBhbmQgc3BlY2lmaWNpdHkgYWRkcmVzcyBkaWZmZXJlbnQgYXNwZWN0cyBvZiBhIG1vZGVsJ3MgcGVyZm9ybWFuY2UuIFByZWNpc2lvbiBpcyBhYm91dCBob3cgbWFueSBzZWxlY3RlZCBpdGVtcyBhcmUgcmVsZXZhbnQsIHdoaWxlIHNwZWNpZmljaXR5IGlzIGFib3V0IGhvdyBtYW55IHJlbGV2YW50IGl0ZW1zIGFyZSBzZWxlY3RlZCwgcGFydGljdWxhcmx5IGluIHRoZSBjb250ZXh0IG9mIG5lZ2F0aXZlcy4gRGVwZW5kaW5nIG9uIHRoZSBhcHBsaWNhdGlvbiBhbmQgdGhlIGNvbnNlcXVlbmNlcyBvZiBkaWZmZXJlbnQgdHlwZXMgb2YgZXJyb3JzIChmYWxzZSBwb3NpdGl2ZXMgdnMuIGZhbHNlIG5lZ2F0aXZlcyksIG9uZSBtYXkgcHJpb3JpdGl6ZSBvbmUgbWV0cmljIG92ZXIgdGhlIG90aGVyLgoKIyMgUk9DIEN1cnZlIGFuZCBBVUMKClRoZSBSZWNlaXZlciBPcGVyYXRpbmcgQ2hhcmFjdGVyaXN0aWMgKFJPQykgY3VydmUgYW5kIHRoZSBBcmVhIFVuZGVyIHRoZSBDdXJ2ZSAoQVVDKSBhcmUgcG93ZXJmdWwgdG9vbHMgdXNlZCBmb3IgZXZhbHVhdGluZyB0aGUgcGVyZm9ybWFuY2Ugb2YgY2xhc3NpZmljYXRpb24gbW9kZWxzLCBwYXJ0aWN1bGFybHkgaW4gYmluYXJ5IGNsYXNzaWZpY2F0aW9uLiBUaGV5IGNhbiBhbHNvIGJlIGFkYXB0ZWQgZm9yIG11bHRpY2xhc3MgY2xhc3NpZmljYXRpb24uCgojIyMgUk9DIEN1cnZlIGFuZCBBVUMgaW4gQmluYXJ5IENsYXNzaWZpY2F0aW9uCgojIyMjIFJPQyBDdXJ2ZQoKLSAgICoqV2hhdCBJdCBSZXByZXNlbnRzKio6IFRoZSBST0MgY3VydmUgaXMgYSBncmFwaGljYWwgcmVwcmVzZW50YXRpb24gdGhhdCBpbGx1c3RyYXRlcyB0aGUgZGlhZ25vc3RpYyBhYmlsaXR5IG9mIGEgYmluYXJ5IGNsYXNzaWZpZXIgYXMgaXRzIGRpc2NyaW1pbmF0aW9uIHRocmVzaG9sZCBpcyB2YXJpZWQuCi0gICAqKlBsb3QgQ29tcG9uZW50cyoqOiBUaGUgUk9DIGN1cnZlIHBsb3RzIHRoZSBUcnVlIFBvc2l0aXZlIFJhdGUgKFRQUiwgb3IgUmVjYWxsKSBhZ2FpbnN0IHRoZSBGYWxzZSBQb3NpdGl2ZSBSYXRlIChGUFIpIGF0IHZhcmlvdXMgdGhyZXNob2xkIHNldHRpbmdzLgogICAgLSAgICoqVHJ1ZSBQb3NpdGl2ZSBSYXRlIChUUFIpKio6IFRQUiA9IFRQIC8gKFRQICsgRk4pCiAgICAtICAgKipGYWxzZSBQb3NpdGl2ZSBSYXRlIChGUFIpKio6IEZQUiA9IEZQIC8gKEZQICsgVE4pCgojIyMjIEFVQyAoQXJlYSBVbmRlciB0aGUgUk9DIEN1cnZlKQoKLSAgICoqV2hhdCBJdCBSZXByZXNlbnRzKio6IFRoZSBBVUMgcHJvdmlkZXMgYW4gYWdncmVnYXRlIG1lYXN1cmUgb2YgdGhlIG1vZGVsJ3MgcGVyZm9ybWFuY2UgYWNyb3NzIGFsbCBwb3NzaWJsZSBjbGFzc2lmaWNhdGlvbiB0aHJlc2hvbGRzLiBJdCByYW5nZXMgZnJvbSAwIHRvIDEsIHdpdGggYSBoaWdoZXIgQVVDIGluZGljYXRpbmcgYmV0dGVyIG1vZGVsIHBlcmZvcm1hbmNlLgotICAgKipJbnRlcnByZXRhdGlvbioqOgogICAgLSAgIEFuIEFVQyBvZiAwLjUgc3VnZ2VzdHMgbm8gZGlzY3JpbWluYXRpdmUgYWJpbGl0eSAoZXF1aXZhbGVudCB0byByYW5kb20gZ3Vlc3NpbmcpLgogICAgLSAgIEFuIEFVQyBvZiAxLjAgc3VnZ2VzdHMgcGVyZmVjdCBjbGFzc2lmaWNhdGlvbi4KCiMjIyMgRXhhbXBsZQoKQ29uc2lkZXIgYSBtZWRpY2FsIHRlc3QgZm9yIGEgZGlzZWFzZTogLSBCeSBhZGp1c3RpbmcgdGhlIHRocmVzaG9sZCBmb3Igd2hhdCBjb3VudHMgYXMgYSBwb3NpdGl2ZSBwcmVkaWN0aW9uLCB5b3UgZ2VuZXJhdGUgZGlmZmVyZW50IHNldHMgb2YgVFBSIGFuZCBGUFIsIHBsb3R0aW5nIHRoZXNlIG9uIHRoZSBST0MgY3VydmUuIC0gSWYgdGhlIHRlc3QgaXMgaGlnaGx5IGFjY3VyYXRlLCB0aGUgUk9DIGN1cnZlIHdpbGwgYm93IHRvd2FyZHMgdGhlIHRvcCBsZWZ0IGNvcm5lciBvZiB0aGUgcGxvdCwgaW5kaWNhdGluZyBoaWdoIFRQUiBhbmQgbG93IEZQUi4gLSBJZiB0aGUgQVVDIGlzIGNsb3NlIHRvIDEsIGl0IHN1Z2dlc3RzIHRoZSB0ZXN0IGlzIGhpZ2hseSBlZmZlY3RpdmUgYXQgZGlzdGluZ3Vpc2hpbmcgYmV0d2VlbiBwYXRpZW50cyB3aXRoIGFuZCB3aXRob3V0IHRoZSBkaXNlYXNlLgoKIyMjIFJPQyBDdXJ2ZSBhbmQgQVVDIGluIE11bHRpY2xhc3MgQ2xhc3NpZmljYXRpb24KCkluIG11bHRpY2xhc3MgY2xhc3NpZmljYXRpb24sIHRoZSBST0MgY3VydmUgYW5kIEFVQyBhcmUgZXh0ZW5kZWQgdG8gaGFuZGxlIG11bHRpcGxlIGNsYXNzZXMgdGhyb3VnaCBhIGZldyBkaWZmZXJlbnQgbWV0aG9kczoKCiMjIyMgT25lLXZzLVJlc3QgKE92UikgQXBwcm9hY2gKCi0gICAqKk1ldGhvZCoqOiBUcmVhdCBlYWNoIGNsYXNzIGFzIGEgYmluYXJ5IGNsYXNzaWZpY2F0aW9uICh0aGUgY2xhc3MgdmVyc3VzIGFsbCBvdGhlciBjbGFzc2VzKS4KLSAgICoqQ2FsY3VsYXRpb24qKjogQ2FsY3VsYXRlIHRoZSBST0MgY3VydmUgYW5kIEFVQyBmb3IgZWFjaCBjbGFzcyBzZXBhcmF0ZWx5LCB0aGVuIGF2ZXJhZ2UgdGhlIHJlc3VsdHMuCiAgICAtICAgVGhpcyBjb3VsZCBiZSBhIHNpbXBsZSBhdmVyYWdlIChtYWNyby1hdmVyYWdlKSBvciBhIHdlaWdodGVkIGF2ZXJhZ2UgYmFzZWQgb24gdGhlIHByZXZhbGVuY2Ugb2YgZWFjaCBjbGFzcy4KCiMjIyMgT25lLXZzLU9uZSAoT3ZPKSBBcHByb2FjaAoKLSAgICoqTWV0aG9kKio6IEZvciBOIGNsYXNzZXMsIGNvbnN0cnVjdCBST0MgY3VydmVzIGZvciBlYWNoIHBhaXIgb2YgY2xhc3Nlcy4KLSAgICoqQ2FsY3VsYXRpb24qKjogQXZlcmFnZSB0aGUgQVVDcyBmcm9tIHRoZXNlIFJPQyBjdXJ2ZXMuCgojIyMjIEV4YW1wbGUKCkltYWdpbmUgYSBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtIHdpdGggdGhyZWUgY2xhc3NlcyAoQSwgQiwgYW5kIEMpLiBGb3IgdGhlIE92UiBtZXRob2Q6IC0gQ2FsY3VsYXRlIHRoZSBST0MgY3VydmUgYW5kIEFVQyB0cmVhdGluZyBBIGFzIHRoZSBwb3NpdGl2ZSBjbGFzcyBhbmQgQitDIGFzIHRoZSBuZWdhdGl2ZSBjbGFzcywgdGhlbiByZXBlYXQgZm9yIEIgYW5kIEMuIC0gQXZlcmFnZSB0aGVzZSBBVUMgc2NvcmVzIHRvIGdldCBhIHNpbmdsZSBwZXJmb3JtYW5jZSBtZWFzdXJlLgoKIyMjIEtleSBQb2ludHMKCi0gICAqKkJpbmFyeSBDbGFzc2lmaWNhdGlvbioqOiBST0MgYW5kIEFVQyBwcm92aWRlIGEgY29tcHJlaGVuc2l2ZSBtZWFzdXJlIG9mIG1vZGVsIHBlcmZvcm1hbmNlIGFjcm9zcyBhbGwgdGhyZXNob2xkcy4KLSAgICoqTXVsdGljbGFzcyBDbGFzc2lmaWNhdGlvbioqOiBFeHRlbmRlZCB0aHJvdWdoIE92UiBvciBPdk8gYXBwcm9hY2hlcyB0byBoYW5kbGUgbXVsdGlwbGUgY2xhc3Nlcy4KLSAgICoqVXNlZnVsbmVzcyoqOiBUaGVzZSBtZXRyaWNzIGFyZSBwYXJ0aWN1bGFybHkgdXNlZnVsIGZvciBldmFsdWF0aW5nIGFuZCBjb21wYXJpbmcgbW9kZWxzLCBlc3BlY2lhbGx5IHdoZW4gZGVhbGluZyB3aXRoIGltYmFsYW5jZWQgZGF0YXNldHMgb3Igd2hlbiB0aGUgY29zdHMgb2YgZGlmZmVyZW50IHR5cGVzIG9mIGVycm9ycyB2YXJ5IHNpZ25pZmljYW50bHkuCgojIyBMb2cgTG9zcwoKTG9nIExvc3MsIGFsc28ga25vd24gYXMgbG9naXN0aWMgbG9zcyBvciBjcm9zcy1lbnRyb3B5IGxvc3MsIGlzIGEgcGVyZm9ybWFuY2UgbWV0cmljIHRoYXQgbWVhc3VyZXMgdGhlIGFjY3VyYWN5IG9mIGEgY2xhc3NpZmllci4gSXQncyBhIHByb2JhYmlsaXR5LWJhc2VkIG1ldHJpYywgb2ZmZXJpbmcgYSBtb3JlIG51YW5jZWQgdmlldyBvZiBtb2RlbCBwZXJmb3JtYW5jZSwgZXNwZWNpYWxseSB3aGVuIHRoZSBvdXRwdXRzIGFyZSBwcm9iYWJpbGl0aWVzLgoKIyMjIExvZyBMb3NzIGluIEJpbmFyeSBDbGFzc2lmaWNhdGlvbgoKSW4gYmluYXJ5IGNsYXNzaWZpY2F0aW9uLCBsb2cgbG9zcyBtZWFzdXJlcyB0aGUgdW5jZXJ0YWludHkgb2YgdGhlIHByb2JhYmlsaXR5IGVzdGltYXRlcyBieSBwZW5hbGl6aW5nIGZhbHNlIGNsYXNzaWZpY2F0aW9ucy4KCiMjIyMgRm9ybXVsYSBmb3IgTG9nIExvc3MKCkZvciBiaW5hcnkgY2xhc3NpZmljYXRpb24sIHRoZSBmb3JtdWxhIGZvciBsb2cgbG9zcyBpczoKCiQkIFx0ZXh0e0xvZyBMb3NzfSA9IC0gXGZyYWN7MX17Tn0gXHN1bV97aT0xfV57Tn0gW3lfaSBcY2RvdCBcbG9nKHBfaSkgKyAoMSAtIHlfaSkgXGNkb3QgXGxvZygxIC0gcF9pKV0gJCQKCldoZXJlOiAtICROJCBpcyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucy4gLSAkeV9pJCBpcyB0aGUgYWN0dWFsIGxhYmVsICgwIG9yIDEpLiAtICRwX2kkIGlzIHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdHkgb2YgdGhlIG9ic2VydmF0aW9uIGJlaW5nIGluIGNsYXNzIDEuIC0gJFxsb2ckIGlzIHRoZSBuYXR1cmFsIGxvZ2FyaXRobS4KCiMjIyMgRXhhbXBsZQoKQ29uc2lkZXIgYSBiaW5hcnkgY2xhc3NpZmljYXRpb24gdGFzayB3aXRoIDMgc2FtcGxlcywgYW5kIHRoZSBtb2RlbCBvdXRwdXRzIHRoZSBmb2xsb3dpbmcgcHJvYmFiaWxpdGllcyBhbmQgYWN0dWFsIGxhYmVsczoKCi0gICBTYW1wbGUgMTogUHJlZGljdGVkIHByb2JhYmlsaXR5IGZvciBjbGFzcyAxID0gMC45LCBBY3R1YWwgbGFiZWwgPSAxCi0gICBTYW1wbGUgMjogUHJlZGljdGVkIHByb2JhYmlsaXR5IGZvciBjbGFzcyAxID0gMC4zLCBBY3R1YWwgbGFiZWwgPSAwCi0gICBTYW1wbGUgMzogUHJlZGljdGVkIHByb2JhYmlsaXR5IGZvciBjbGFzcyAxID0gMC42LCBBY3R1YWwgbGFiZWwgPSAxCgpUaGUgbG9nIGxvc3Mgd291bGQgYmUgY2FsY3VsYXRlZCBhczoKCiQkIFx0ZXh0e0xvZyBMb3NzfSA9IC0gXGZyYWN7MX17M30gWygxIFxjZG90IFxsb2coMC45KSArICgxIC0gMSkgXGNkb3QgXGxvZygxIC0gMC45KSkgKyAoMCBcY2RvdCBcbG9nKDAuMykgKyAoMSAtIDApIFxjZG90IFxsb2coMSAtIDAuMykpICsgKDEgXGNkb3QgXGxvZygwLjYpICsgKDEgLSAxKSBcY2RvdCBcbG9nKDEgLSAwLjYpKV0gJCQKCiMjIyBMb2cgTG9zcyBpbiBNdWx0aWNsYXNzIENsYXNzaWZpY2F0aW9uCgpJbiBtdWx0aWNsYXNzIGNsYXNzaWZpY2F0aW9uLCBsb2cgbG9zcyBpcyBleHRlbmRlZCB0byBjb3ZlciBtdWx0aXBsZSBjbGFzc2VzLgoKIyMjIyBGb3JtdWxhIGZvciBNdWx0aWNsYXNzIExvZyBMb3NzCgpUaGUgZm9ybXVsYSBmb3IgbXVsdGljbGFzcyBsb2cgbG9zcyBpczoKCiQkIFx0ZXh0e0xvZyBMb3NzfSA9IC0gXGZyYWN7MX17Tn0gXHN1bV97aT0xfV57Tn0gXHN1bV97aj0xfV57TX0geV97aWp9IFxjZG90IFxsb2cocF97aWp9KSAkJAoKV2hlcmU6IC0gJE4kIGlzIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zLiAtICRNJCBpcyB0aGUgbnVtYmVyIG9mIGNsYXNzZXMuIC0gJHlfe2lqfSQgaXMgMSBpZiBvYnNlcnZhdGlvbiAkaSQgYmVsb25ncyB0byBjbGFzcyAkaiQsIGFuZCAwIG90aGVyd2lzZS4gLSAkcF97aWp9JCBpcyB0aGUgcHJlZGljdGVkIHByb2JhYmlsaXR5IG9mIG9ic2VydmF0aW9uICRpJCBiZWxvbmdpbmcgdG8gY2xhc3MgJGokLgoKIyMjIyBFeGFtcGxlCgpDb25zaWRlciBhIGNsYXNzaWZpY2F0aW9uIHByb2JsZW0gd2l0aCAzIHNhbXBsZXMgYW5kIDMgY2xhc3NlczoKCi0gICBTYW1wbGUgMTogUHJlZGljdGVkIHByb2JhYmlsaXRpZXMgPSBbMC43LCAwLjIsIDAuMV0sIEFjdHVhbCBjbGFzcyA9IDEKLSAgIFNhbXBsZSAyOiBQcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyA9IFswLjEsIDAuOCwgMC4xXSwgQWN0dWFsIGNsYXNzID0gMgotICAgU2FtcGxlIDM6IFByZWRpY3RlZCBwcm9iYWJpbGl0aWVzID0gWzAuMiwgMC4yLCAwLjZdLCBBY3R1YWwgY2xhc3MgPSAzCgpUaGUgbG9nIGxvc3MgaXMgY2FsY3VsYXRlZCBmb3IgZWFjaCBjbGFzcyBhbmQgdGhlbiBhdmVyYWdlZC4KCiMjIyBLZXkgUG9pbnRzCgotICAgKipCaW5hcnkgQ2xhc3NpZmljYXRpb24qKjogTG9nIGxvc3MgcHJvdmlkZXMgYSBtZWFzdXJlIG9mIGhvdyBjbG9zZSB0aGUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgYXJlIHRvIHRoZSBhY3R1YWwgbGFiZWxzLgotICAgKipNdWx0aWNsYXNzIENsYXNzaWZpY2F0aW9uKio6IFRoZSBjb25jZXB0IGlzIGV4dGVuZGVkIHRvIG11bHRpcGxlIGNsYXNzZXMsIHN1bW1pbmcgb3ZlciBhbGwgY2xhc3NlcyBmb3IgZWFjaCBzYW1wbGUuCi0gICAqKkludGVycHJldGF0aW9uKio6IExvd2VyIGxvZyBsb3NzIHZhbHVlcyBpbmRpY2F0ZSBiZXR0ZXIgbW9kZWwgcGVyZm9ybWFuY2UsIHdpdGggYSBsb2cgbG9zcyBvZiAwIHJlcHJlc2VudGluZyBwZXJmZWN0IHByZWRpY3Rpb25zLgotICAgKipVc2VmdWxuZXNzKio6IExvZyBsb3NzIGlzIHBhcnRpY3VsYXJseSB1c2VmdWwgd2hlbiB0aGUgb3V0cHV0IG9mIHRoZSBtb2RlbCBpcyBhIHByb2JhYmlsaXR5LCBnaXZpbmcgaW5zaWdodCBpbnRvIHRoZSB1bmNlcnRhaW50eSBvZiB0aGUgcHJlZGljdGlvbnMuCgojIyBTYW1wbGUgSW1wbGVtZW50YXRpb24gaW4gUgoKYGBge3J9CiMgTG9hZGluZyBkYXRhc2V0IHVzaW5nIHVybAoKdXJsMSA8LSAiaHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL3VjP2V4cG9ydD1kb3dubG9hZCZpZD0xMmd6TGZaQk1TbDJkLXNGNjNELXFaSmJDRW9jVWRUS0siCmRmLndpbmUxIDwtIHJlYWQuY3N2KGZpbGUgPSB1cmwxLCBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiOyIpCmBgYAoKYGBge3J9CmRmLndpbmUxIDwtIGRmLndpbmUxWyxjKDEsIDIsIDMsIDQsIDEyKV0KYGBgCgpgYGB7cn0KbGlicmFyeShzY2FsZXMpCiMgUGVyZm9ybWluZyBtaW4tbWF4IHRyYW5zZm9ybWF0aW9uIG9uIG15IGRhdGFzZXQgd2l0aG91dCBvdXRsaWVycwoKcXVhbGl0eSA8LSBkZi53aW5lMSRxdWFsaXR5CndpbmUuMSA8LSBkZi53aW5lMVssLTVdCiNzdHIod2luKQoKbm9ybWFsaXogPC0gZnVuY3Rpb24oY29sdW0pewpub3JtYWxpemVkIDwtIHNjYWxlczo6cmVzY2FsZShjb2x1bSwgdG89YygwLDEpKQp9Cgpmb3IgKHggaW4gY29sbmFtZXMod2luZS4xKSl7Cm5vcm1hbGl6ZWQuY29sdW1uIDwtIG5vcm1hbGl6KHdpbmUuMVssIHhdKQojIGhlcmUgdGhlIGNvbHVtbiBpcyBnZXR0aW5nIG5vcm1hbGl6ZWQgYWJvdmUgYnV0CiMgd2UgaGF2ZSB0byBhc3NpZ24gaXQgdG8gdGhlIGRhdGEgZnJhbWUgdG8gbWFrZSBjaGFuZ2VzCndpbmUuMVsseF0gPC0gbm9ybWFsaXplZC5jb2x1bW4KfQoKd2luZS4xJHF1YWxpdHkgPC0gcXVhbGl0eQpzdW1tYXJ5KHdpbmUuMSkKYGBgCgpgYGB7cn0Kd2luZS4xJHF1YWxpdHkgPC0gYXMuZmFjdG9yKHdpbmUuMSRxdWFsaXR5KQojIFNwbGl0dGluZyB0aGUgZGF0YXNldCBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHN1YnNldHMKc2V0LnNlZWQoMTAxKQpOID0gbnJvdyh3aW5lLjEpCnNwbGl0IDwtIDAuNwpyb3dzIDwtIHNhbXBsZShucm93KHdpbmUuMSkpCgp0cmFpbmluZ19pbmQgPC0gc2FtcGxlKDE6Tiwgc2l6ZSA9IHJvdW5kKE4gKiBzcGxpdCkscmVwbGFjZSA9IEZBTFNFKQp0cmFpbiA8LSB3aW5lLjFbdHJhaW5pbmdfaW5kLF0KdGVzdCA8LSB3aW5lLjFbLXRyYWluaW5nX2luZCxdCnRyYWluX2xhYmVscyA8LSB0cmFpbiRxdWFsaXR5CnRlc3RfbGFiZWxzIDwtIHRlc3QkcXVhbGl0eQpsZW5ndGgodHJhaW5fbGFiZWxzKQpkaW0odGVzdCkKZGltKHRyYWluKQojIHRlc3RfbGFiZWxzCmBgYAoKYGBge3J9CmxpYnJhcnkoY2xhc3MpCiMga05OIG1vZGVsCiMgVGhlIDV0aCBjb2x1bW4gaXMgdGhlIHF1YWxpdHkgY29sdW1uIAprbm5fbW9kZWwgPC0ga25uKHRyYWluID0gdHJhaW5bLC01XSwgdGVzdCA9IHRlc3RbLC01XSwgY2w9IHRyYWluX2xhYmVscywgaz0zKQpgYGAKCmBgYHtyfQojIEV2YWx1YXRpbmcgbW9kZWwKY29uZnVzaW9uX21hdHJpeCA8LSB0YWJsZShrbm5fbW9kZWwsIHRlc3RfbGFiZWxzKQpjb25mdXNpb25fbWF0cml4CmBgYAoKYGBge3J9CmFjY3VyYWN5IDwtIHN1bShkaWFnKGNvbmZ1c2lvbl9tYXRyaXgpKSAvIHN1bShjb25mdXNpb25fbWF0cml4KQphY2N1cmFjeQpgYGAKCmBgYHtyfQpwcmVjaXNpb24ucGVyLmNsYXNzIDwtIGMobmNvbChjb25mdXNpb25fbWF0cml4KSkKCmZvciAoY29sIGluIDE6bmNvbChjb25mdXNpb25fbWF0cml4KSkgewogIHByZWNpc2lvbi5wZXIuY2xhc3NbY29sXSA8LSBjb25mdXNpb25fbWF0cml4W2NvbCxjb2xdIC8gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShjb25mdXNpb25fbWF0cml4WzE6bnJvdyhjb25mdXNpb25fbWF0cml4KSxjb2xdKQp9CgpwcmVjaXNpb24gPC0gbWVhbihwcmVjaXNpb24ucGVyLmNsYXNzKQpgYGAKCiMjIFN1bW1hcnkKCkluIHRoaXMgbGVzc29uLCB3ZSBkaXNjdXNzZWQgdmFyaW91cyBhc3BlY3RzIG9mIGV2YWx1YXRpbmcgY2xhc3NpZmljYXRpb24gbW9kZWxzIGluIHN1cGVydmlzZWQgbWFjaGluZSBsZWFybmluZywgZm9jdXNpbmcgcGFydGljdWxhcmx5IG9uIGFjY3VyYWN5LCBwcmVjaXNpb24sIHJlY2FsbCwgYW5kIEYxIHNjb3JlIGFuZCBob3cgdG8gaW50ZXJwcmV0IHRoZSBvdXRjb21lcyBmb3IgdGhlc2UgbWV0cmljcyBpbiByZWxhdGlvbiB0byBlYWNoIG90aGVyLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBGaWxlcyAmIFJlc291cmNlcwoKYGBge3IgemlwRmlsZXMsIGVjaG89RkFMU0V9CnppcE5hbWUgPSBzcHJpbnRmKCJMZXNzb25GaWxlcy0lcy0lcy56aXAiLCAKICAgICAgICAgICAgICAgICBwYXJhbXMkY2F0ZWdvcnksCiAgICAgICAgICAgICAgICAgcGFyYW1zJG51bWJlcikKCnRleHRBTGluayA9IHBhc3RlMCgiQWxsIEZpbGVzIGZvciBMZXNzb24gIiwgCiAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwiLiIscGFyYW1zJG51bWJlcikKCiMgZG93bmxvYWRGaWxlc0xpbmsoKSBpcyBpbmNsdWRlZCBmcm9tIF9pbnNlcnQyREIuUgprbml0cjo6cmF3X2h0bWwoZG93bmxvYWRGaWxlc0xpbmsoIi4iLCB6aXBOYW1lLCB0ZXh0QUxpbmspKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgUmVmZXJlbmNlcwoKW2h0dHBzOi8vbWVkaXVtLmNvbS9cQHNocnV0aXNheGVuYTA2MTcvcHJlY2lzaW9uLXZzLXJlY2FsbC0zODZjZjlmODk0ODhdKGh0dHBzOi8vbWVkaXVtLmNvbS9Ac2hydXRpc2F4ZW5hMDYxNy9wcmVjaXNpb24tdnMtcmVjYWxsLTM4NmNmOWY4OTQ4OCl7LnVyaX0KCiMjIEVycmF0YQoKW0xldCB1cyBrbm93XShodHRwczovL2Zvcm0uam90Zm9ybS5jb20vMjEyMTg3MDcyNzg0MTU3KXt0YXJnZXQ9Il9ibGFuayJ9Lgo=