Victor Enchautegui
11/8/2020
library(ggplot2)
library(e1071)
library(caret)
package 㤼㸱caret㤼㸲 was built under R version 4.0.3Loading required package: lattice
Registered S3 method overwritten by 'data.table':
method from
print.data.table
library("rpart")
library("rpart.plot")
A. Understanding variables and relations in data (2 points)
A.1 Discuss how credit and payment history data such as PAY_AMT1 have an impact on payment default.
|
Variable/Attribute
|
Data Type
|
Potential impact on “Default” and reason
|
|
Limit_Bal
|
Integer
|
The more credit you have, the less likely to default due to more ‘spending power’.
|
|
Pay_0, 2, 3, 4, 5, 6
|
Integer
|
For those that has a status code -1 (payment was made on time), the probability of defaulting is less, especially if it is consecutive.
For code 1 and greater (payments that were delayed by respective number of months), the probability increases for the chance of defaulting.
|
|
Bill_Amt1, 2, 3, 4, 5, 6
|
Integer
|
These columns list the amount billed to each customer. The assumption would be that the higher the bill, the higher the risk, however I would not utilize this attribute on it’s own. When used by itself, I do not believe it has a high potential impact on default because some individual may have the fiances to support such high bills.
If we utilize this attribute with Limit_Bal, it can have higher potential to impact on default. For example, if the debt-to-limit ratio increases over time, this may increase the risk of defaulting as the customer is trying their limits. This is especially true if the customer is delay in payments.
|
|
Pay_Amt1, 2, 3, 4, 5, 6
|
Integer
|
Each of these amounts was paid to settle the preceding month’s bill, either in full or partially. If the payments are low or less than a specific percent of the amount owe, then this may increase the potential impact of defaulting, especially if the bill amount is increasing each month.
|
A.2. Discuss in what ways some of the above attributes contribute to default.payment.next.month together. Please identify at least two pairs of attributes that can be treated together and how.
|
Var1
|
Var2
|
Var3
|
Discuss their relation, how to combine them (ratio, difference, or others) and your reason/theory
|
|
Pay_Amt
|
Bill_Amt
|
|
I believe Pay_Amt to Bill_Amt ratio would help in determining the possibility of defaults as it shows the customers willingness to pay their bill and the amount of their bill. If the Pay_Amt to Bill_Amt ratio is less than .5, this is may mean the customer is at risk.
|
|
Limit_Bal
|
Bill_Amt
|
|
If the debt-to-limit ratio is low, and consecutively getting worse, then this is a red flag that the customer is spending more than he/she is willing to repay, which can lead to a default. These two variables should be the most significant in determining the possibility of defaulting, however, what if the individual is paying their mortgage on their credit card and paying it in full each month just to get “reward points”. This situation happens, it can send off ‘false negatives’ of defaulting.
|
|
Pay_#
|
Bill_Amt
|
Limit_Bal
|
This combination is the best combination to determine if the customer is at risk of defaulting. If the customer is not making payments, the bill amount is increasing each month, and he/she is reaching their credit limit, this is significant red flag for defaulting.
|
B. Data preparation and cleansing (1 points)
B.1 Load data and initial data conversion/transformation:
- Load “UCI_Credit_Card.csv” into data frame variable in R using read.csv().
cc <- read.csv("data/UCI_Credit_Card.csv")
- Convert the following attributes into as nominal (categorical, factor) attributes: Sex, Education, Marriage, and default.payment.next.month.
cc$SEX <- factor(cc$SEX,levels=c(1,2), labels=c("Male","Female"))
cc$MARRIAGE <- factor(cc$MARRIAGE,levels=c(1,2,3), labels=c("Married","Single","Others"))
cc$EDUCATION <- factor(cc$EDUCATION,levels=c(1,2,3,4,5,6), labels=c("Grad School","University","High School", "Others", "Unknown", "Unknown"))
cc$default.payment.next.month <- factor(cc$default.payment.next.month,levels=c(0,1), labels=c("No","Yes"))
- Use class() function check on Sex, Education, Marriage, and default.payment.next.month, they should ALL be “factor” variables.
class(cc$SEX)
[1] "factor"
class(cc$MARRIAGE)
[1] "factor"
class(cc$EDUCATION)
[1] "factor"
class(cc$default.payment.next.month)
[1] "factor"
B.2 Create a filtered dataset with only non-negative amounts.
- Use the subset() function to select only positive values on the 6 BILL_AMT attributes and 6 PAY_AMT attributes. Like (fill the … with actual):
ccpo <- subset(cc, BILL_AMT1>=0 & BILL_AMT2>=0 & BILL_AMT3>=0 & BILL_AMT4>=0 & BILL_AMT5>=0 & BILL_AMT6>=0 & PAY_AMT1>=0 & PAY_AMT2>=0 & PAY_AMT3>=0 & PAY_AMT4>=0 & PAY_AMT5>=0 & PAY_AMT6>=0)
nrow(ccpo)
[1] 28070
- Check the number of rows in the filtered subset and you can use View(ccpo) to double check on the data.
View(ccpo)
- Data Transformation and Classification/Modeling (4 points)
C.1. Pick one classification method, model with default.payment.next.month ~ variables in A.1., and evaluate:
• Select 90% of data for training and 10% for testing;
df <- sort(sample(nrow(ccpo), nrow(ccpo)*.9))
train <- ccpo[df, ]
test <- ccpo[-df, ]
• Build a model with training data (90% data) to predict default.payment.next.month, using at least three variables from A.1.
nbDem <- naiveBayes(default.payment.next.month ~ LIMIT_BAL + BILL_AMT1 + PAY_0, train)
nbDem
Naive Bayes Classifier for Discrete Predictors
Call:
naiveBayes.default(x = X, y = Y, laplace = laplace)
A-priori probabilities:
Y
No Yes
0.774334 0.225666
Conditional probabilities:
LIMIT_BAL
Y [,1] [,2]
No 174956.7 131137.1
Yes 126707.2 113259.6
BILL_AMT1
Y [,1] [,2]
No 54688.26 74788.16
Yes 49737.65 73480.56
PAY_0
Y [,1] [,2]
No -0.1867907 0.9325569
Yes 0.7065427 1.3775166
• Run prediction with the model on test data (10% data) and record the following scores: o Present the confusion table with TP, TN, FP, and FN o Report Accuracy, Precision, Recall, F, and Kappa in Table D.
nb_prediction <- predict(nbDem, test, type = "class")
confusionMatrix(data = nb_prediction,
reference =test$default.payment.next.month,
dnn = c("Predicted", "Actual"),
mode = "prec_recall")
Confusion Matrix and Statistics
Actual
Predicted No Yes
No 2105 409
Yes 85 208
Accuracy : 0.824
95% CI : (0.8094, 0.8379)
No Information Rate : 0.7802
P-Value [Acc > NIR] : 5.138e-09
Kappa : 0.3676
Mcnemar's Test P-Value : < 2.2e-16
Precision : 0.8373
Recall : 0.9612
F1 : 0.8950
Prevalence : 0.7802
Detection Rate : 0.7499
Detection Prevalence : 0.8956
Balanced Accuracy : 0.6492
'Positive' Class : No
C.2. Perform data transformation (with new relational attributes) and redo classification:
- Follow the treatments (at least two relations) of the variable pairs you have identified in A.2
- Create new attributes that compute the relations you have identified in A.2:
ccpo$PAID_BILL <- c(ccpo$PAY_AMT1 / ccpo$BILL_AMT2) #1 or greater means full payment was made; Anything less than 1 is a risk to default
ccpo$DEBT_TO_LIMIT <- c(ccpo$BILL_AMT1 / ccpo$LIMIT_BAL) #Goal is to be close to 0. 1 or greater means the customer is at their credit limit or greater; high risk of defaulting
After reviewing data, I will replacing ‘Inf’ and ‘NaN’ with 1 for PAID_BILL attribute as this means that the customer is either paying a bill that is zero (overpaying) or didn’t pay for a bill as there was no bill to pay. These occurrences is only happening in one of the two attributes.
is.na(ccpo) <- sapply(ccpo, is.infinite)
ccpo[is.na(ccpo)] <- 1
invalid factor level, NA generatedinvalid factor level, NA generated
Build a model with training data (90% data) to predict default.payment.next.month, using the new relational attributes (plus any other variables you would like to include) here.
Run prediction with the model on test data (10% data) and record the following scores:
- Present the confusion table with TP, TN, FP, and FN
- Report Accuracy, Precision, Recall, F, and Kappa in Table D.
df1 <- sort(sample(nrow(ccpo), nrow(ccpo)*.9))
train1 <- ccpo[df1, ]
test1 <- ccpo[-df1, ]
nbDem1 <- naiveBayes(default.payment.next.month ~ PAID_BILL + DEBT_TO_LIMIT + PAY_0, train1)
nbDem1
Naive Bayes Classifier for Discrete Predictors
Call:
naiveBayes.default(x = X, y = Y, laplace = laplace)
A-priori probabilities:
Y
No Yes
0.7753632 0.2246368
Conditional probabilities:
PAID_BILL
Y [,1] [,2]
No 0.6430843 31.7591719
Yes 0.2996887 0.4322557
DEBT_TO_LIMIT
Y [,1] [,2]
No 0.4257157 0.4097742
Yes 0.5088484 0.4109439
PAY_0
Y [,1] [,2]
No -0.1933837 0.9264983
Yes 0.7081938 1.3805876
nb_prediction1 <- predict(nbDem1, test1, type = "class")
confusionMatrix(data = nb_prediction1,
reference =test1$default.payment.next.month,
dnn = c("Predicted", "Actual"),
mode = "prec_recall")
C.3. Examine attribute value distribution (histogram), and perform log transformation on attributes you see fit:
- Create a new attribute that is the logarithm of each attribute with an extremely wide, “skew” distribution.
hist(ccpo$LIMIT_BAL, xlab="Credit Limit")
hist(ccpo$BILL_AMT1, xlab="Last Bill Statement Amount")
hist(ccpo$PAY_0, xlab="Last Payment Amount")
hist(ccpo$PAID_BILL, xlab="Paid Off Previous Statement Ratio")

hist(ccpo$DEBT_TO_LIMIT, xlab="Debt to Credit Ratio")

The following attributes I have identified to have extremely wide “skew” distribution, which I will LOG:
ccpo$LIMIT_BAL_LOG <- log10(ccpo$LIMIT_BAL)
ccpo$BILL_AMT1_LOG <-log10(ccpo$BILL_AMT1)
ccpo$PAID_BILL_LOG <-log10(ccpo$PAID_BILL)
ccpo$DEBT_TO_LIMIT_LOG <-log10(ccpo$DEBT_TO_LIMIT)
hist(ccpo$LIMIT_BAL_LOG, xlab="Credit Limit")

hist(ccpo$BILL_AMT1_LOG, xlab="Last Bill Statement Amount")

hist(ccpo$PAID_BILL_LOG, xlab="Paid Off Previous Statement Ratio")

hist(ccpo$DEBT_TO_LIMIT_LOG, xlab="Debt to Credit Ratio")

- Build a model with training data (90% data) to predict default.payment.next.month, using at the new relational (and log-transformed) attributes plus any other variables you would like to include here.
is.na(ccpo) <- sapply(ccpo, is.infinite)
ccpo[is.na(ccpo)] <- 0
invalid factor level, NA generatedinvalid factor level, NA generated
df_log <- sort(sample(nrow(ccpo), nrow(ccpo)*.9))
train_log <- ccpo[df_log, ]
test_log <- ccpo[-df_log, ]
nbDem_log <- naiveBayes(default.payment.next.month ~ LIMIT_BAL_LOG + BILL_AMT1_LOG + PAY_0, train_log)
nbDem_log
Naive Bayes Classifier for Discrete Predictors
Call:
naiveBayes.default(x = X, y = Y, laplace = laplace)
A-priori probabilities:
Y
No Yes
0.7740569 0.2259431
Conditional probabilities:
LIMIT_BAL_LOG
Y [,1] [,2]
No 5.096040 0.3971029
Yes 4.921738 0.4193922
BILL_AMT1_LOG
Y [,1] [,2]
No 4.049380 1.301126
Yes 3.953266 1.372574
PAY_0
Y [,1] [,2]
No -0.1893633 0.9308117
Yes 0.7051507 1.3772265
- Run prediction with the model on test data (10% data) and record the following scores:
- Present the confusion table with TP, TN, FP, and FN
- Report Accuracy, Precision, Recall, F, and Kappa in Table D.
nb_prediction_log <- predict(nbDem_log, test_log, type = "class")
confusionMatrix(data = nb_prediction_log,
reference =test_log$default.payment.next.month,
dnn = c("Predicted", "Actual"),
mode = "prec_recall")
Confusion Matrix and Statistics
Actual
Predicted No Yes
No 2088 387
Yes 109 223
Accuracy : 0.8233
95% CI : (0.8087, 0.8372)
No Information Rate : 0.7827
P-Value [Acc > NIR] : 5.243e-08
Kappa : 0.3782
Mcnemar's Test P-Value : < 2.2e-16
Precision : 0.8436
Recall : 0.9504
F1 : 0.8938
Prevalence : 0.7827
Detection Rate : 0.7439
Detection Prevalence : 0.8817
Balanced Accuracy : 0.6580
'Positive' Class : No
C.4. Pick another classification model or the same model with different parameter values, and repeat the modeling and evaluation as in C.3. Report the confusion table and results to Table D.
df_log1 <- sort(sample(nrow(ccpo), nrow(ccpo)*.9))
train_log1 <- ccpo[df_log1, ]
test_log1 <- ccpo[-df_log1, ]
nbDem_log1 <- naiveBayes(default.payment.next.month ~ PAID_BILL_LOG + DEBT_TO_LIMIT_LOG + PAY_0, train_log1)
nbDem_log1
Naive Bayes Classifier for Discrete Predictors
Call:
naiveBayes.default(x = X, y = Y, laplace = laplace)
A-priori probabilities:
Y
No Yes
0.7755611 0.2244389
Conditional probabilities:
PAID_BILL_LOG
Y [,1] [,2]
No -0.7105151 0.6440783
Yes -0.6839220 0.6692453
DEBT_TO_LIMIT_LOG
Y [,1] [,2]
No -0.7097247 0.8213071
Yes -0.5492607 0.7828808
PAY_0
Y [,1] [,2]
No -0.1909866 0.9306641
Yes 0.7029982 1.3653377
nb_prediction_log1 <- predict(nbDem_log1, test_log1, type = "class")
confusionMatrix(data = nb_prediction_log1,
reference =test_log1$default.payment.next.month,
dnn = c("Predicted", "Actual"),
mode = "prec_recall")
Confusion Matrix and Statistics
Actual
Predicted No Yes
No 2055 429
Yes 104 219
Accuracy : 0.8101
95% CI : (0.7951, 0.8245)
No Information Rate : 0.7691
P-Value [Acc > NIR] : 8e-08
Kappa : 0.3515
Mcnemar's Test P-Value : <2e-16
Precision : 0.8273
Recall : 0.9518
F1 : 0.8852
Prevalence : 0.7691
Detection Rate : 0.7321
Detection Prevalence : 0.8849
Balanced Accuracy : 0.6449
'Positive' Class : No
- Evaluation and Results (2 points)
|
|
Method
|
C.1. Classification without Transformation
|
|
Correct %
|
Precision
|
Recall
|
F
|
Kappa
|
|
C.1
|
Model 1: nb_prediction LIMIT_BAL, BILL_AMT1, PAY_0
|
82.4%
|
.8373
|
.9612
|
.8956
|
.3676
|
|
C.2
|
Model 2: nb_prediction1 PAID_BILL, DEBT_TO_LIMIT, PAY_0
|
81.8%
|
.8331
|
.9589
|
.8988
|
.3404
|
|
C.3
|
Model 3: nb_prediction_log LIMIT_BAL_LOG, BILL_AMT1_LOG, PAY_0
|
82.33%
|
.8436
|
.9504
|
.8938
|
.3782
|
|
C.4
|
Model 4: nb_prediction_log1 PAID_BILL_LOG, DEBT_TO_LIMIT_LOG, PAY_0
|
81.01%
|
.8273
|
.9518
|
.8852
|
.3515
|
- Report with Interpretation and Conclusion (3 points)
E.1. In terms of the reasons and theories presented in tasks A1 through A2, which ones have been confirmed by your analysis? Please discuss even if there is no obvious answer.
A1 ‘Reasons and Theories’
Limit_Bal: My assumption was correct with this attribute when looking at the Naive Bayes model C1. For those that will not default had a higher mean ($174,956.70) compared to those that defaulted ($126,707.20).
PAY_#: My assumption was incorrect. Although the mean for those that will not default was in the negative as expected, the mean for those that would default was .70. I was expecting the mean of those that would default to be significantly higher.
BILL_AMT#: My assumption was correct. In fact, those that did not default has a higher mean compared to those that did defaulted. The assumption can be made that those with less spending power are most likely default, however the gap between the defaulted and not defaulted isn’t wide when looking at the Naive Bayes model C1.
A2 ‘Reasons and Theories’
DEBT_TO_LIMIT: My assumption was correct, but not significantly. Those that has a lower Debt to Credit ratio will be least likely to default based upon C2’s Naive Bayes. Non-defaulters has a mean of .43 while defaulters has .51. On scale from 0 to 1, this is enough of a gap to say that the assumption is correct.
PAID_BILL: My assumption was significantly correct for those that paid their bill. This ratio took the current month payment divided by the previous month’s bill amount; 1 or greater meant that the customer paid their bill or more. For those that did not defaulted, the mean was .64. Those that did defaulted has a payment-to-bill ratio of .3.
E.2. Does data transformation (with new relational variables in C.2) help? Which one helps most and why? Or which does not? Surprisingly the data transformation with the new relational variable did not help. Accuracy, Precision, Recall, and Kappa scored less when compared to C1. F1 was slightly higher compared to C1 by .0032. The Kappa were low between .34 and .37. which means the agreement between classification and truth values represent low agreement. There was no improvement after the transformation.
E.3. Which classification method(s) and/or parameters appear to perform well? Which ones do not? I utilized Naive Bayes as my classification method throughout the exercises as I was hoping and expecting to have better results after the transformation. Sadly, the basic parameters (PAY_0, BILL_AMIT1, and LIMIT_BAL) without transforming did as good or better than the other models. All models scored high in each metric except for Kappa.
E.4. Reviewing results in Task D, which evaluation metrics (of Correct%, Kappa, F, Precision, and Recall) best capture how good/poor the result is? Which metric is not as helpful?
Although Accuracy would be the primary metric to use, Kappa metric is a robust way to find the degree of agreement between the variables that are being used. Kappa is more informative than Accuracy when working with unbalanced data.
The least helpful would be Accuracy. A good model should have a high accuracy score, but having a high accuracy score alone does not guarantee the model is well established.
As for the other metrics:
Precision identifies how accurately the model predicted the positive classes. The number of true positive events is divided by the sum of positive true and false events.
Recall measures the ratio of predicted the positive classes. The number of true positive events is divided by the sum of true positive and false negative events.
F1 is the weighted average score of recall and precision. The value at 1 is the best performance and at 0 is the worst.
All are good metrics to use depending on the dataset that is being used.
E.5. Pick the most helpful evaluation metric, which method (with what data transformation if applicable) is the overall winner of the results? Reason about why the method performs well.
The Accuracy, Precision, Recall, and F1 scores for all models were high ranging from .81 to .96, except for Kappa. Kappa scores were between .34 and .37. Overall there wasn’t much of a difference among the four models, however I would pick model C3 as the Kappa and Precision scores highest among the other modals.
LS0tDQp0aXRsZTogIklORk8gNjU5IEFzc2lnbm1lbnQgIzMgKDEwIHBvaW50cykiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KPGgzPlZpY3RvciBFbmNoYXV0ZWd1aTwvaDM+DQo8aDQ+MTEvOC8yMDIwPC9oND4NCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGUxMDcxKQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkoInJwYXJ0IikNCmxpYnJhcnkoInJwYXJ0LnBsb3QiKQ0KDQpgYGANCg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCi50ZyAge2JvcmRlci1jb2xsYXBzZTpjb2xsYXBzZTtib3JkZXItY29sb3I6I2NjYztib3JkZXItc3BhY2luZzowOyB3aWR0aDoxMDAlO30NCi50ZyB0ZHtiYWNrZ3JvdW5kLWNvbG9yOiNmZmY7Ym9yZGVyLWNvbG9yOiNjY2M7Ym9yZGVyLXN0eWxlOnNvbGlkO2JvcmRlci13aWR0aDoxcHg7Y29sb3I6IzMzMzsNCiAgZm9udC1mYW1pbHk6QXJpYWwsIHNhbnMtc2VyaWY7Zm9udC1zaXplOjE0cHg7b3ZlcmZsb3c6aGlkZGVuO3BhZGRpbmc6MTBweCA1cHg7d29yZC1icmVhazpub3JtYWw7fQ0KLnRnIHRoe2JhY2tncm91bmQtY29sb3I6I2YwZjBmMDtib3JkZXItY29sb3I6I2NjYztib3JkZXItc3R5bGU6c29saWQ7Ym9yZGVyLXdpZHRoOjFweDtjb2xvcjojMzMzOw0KICBmb250LWZhbWlseTpBcmlhbCwgc2Fucy1zZXJpZjtmb250LXNpemU6MTRweDtmb250LXdlaWdodDpub3JtYWw7b3ZlcmZsb3c6aGlkZGVuO3BhZGRpbmc6MTBweCA1cHg7d29yZC1icmVhazpub3JtYWw7fQ0KLnRnIC50Zy0wbGF4e3RleHQtYWxpZ246bGVmdDt2ZXJ0aWNhbC1hbGlnbjp0b3B9DQo8L3N0eWxlPg0KDQo8aDM+QS4gVW5kZXJzdGFuZGluZyB2YXJpYWJsZXMgYW5kIHJlbGF0aW9ucyBpbiBkYXRhICgyIHBvaW50cyk8L2gzPg0KPGg0PkEuMSBEaXNjdXNzIGhvdyBjcmVkaXQgYW5kIHBheW1lbnQgaGlzdG9yeSBkYXRhIHN1Y2ggYXMgUEFZX0FNVDEgaGF2ZSBhbiBpbXBhY3Qgb24gcGF5bWVudCBkZWZhdWx0LjwvaDQ+DQo8dGFibGUgY2xhc3M9InRnIj4NCjx0aGVhZD4NCiAgPHRyPg0KICAgIDx0aCBjbGFzcz0idGctMGxheCI+VmFyaWFibGUvQXR0cmlidXRlPC90aD4NCiAgICA8dGggY2xhc3M9InRnLTBsYXgiPkRhdGEgVHlwZTwvdGg+DQogICAgPHRoIGNsYXNzPSJ0Zy0wbGF4Ij5Qb3RlbnRpYWwgaW1wYWN0IG9uICJEZWZhdWx0IiBhbmQgcmVhc29uPC90aD4NCiAgPC90cj4NCjwvdGhlYWQ+DQo8dGJvZHk+DQogIDx0cj4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPkxpbWl0X0JhbDwvdGQ+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij5JbnRlZ2VyPC90ZD4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPg0KICAgIFRoZSBtb3JlIGNyZWRpdCB5b3UgaGF2ZSwgdGhlIGxlc3MgbGlrZWx5IHRvIGRlZmF1bHQgZHVlIHRvIG1vcmUg4oCYc3BlbmRpbmcgcG93ZXLigJkuIA0KICAgIDwvdGQ+DQogIDwvdHI+DQogIDx0cj4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPlBheV8wLCAyLCAzLCA0LCA1LCA2IDwvdGQ+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij5JbnRlZ2VyPC90ZD4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPg0KICAgIEZvciB0aG9zZSB0aGF0IGhhcyBhIHN0YXR1cyBjb2RlIC0xIChwYXltZW50IHdhcyBtYWRlIG9uIHRpbWUpLCB0aGUgcHJvYmFiaWxpdHkgb2YgZGVmYXVsdGluZyBpcyBsZXNzLCBlc3BlY2lhbGx5IGlmIGl0IGlzIGNvbnNlY3V0aXZlLiANCg0KRm9yIGNvZGUgMSBhbmQgZ3JlYXRlciAocGF5bWVudHMgdGhhdCB3ZXJlIGRlbGF5ZWQgYnkgcmVzcGVjdGl2ZSBudW1iZXIgb2YgbW9udGhzKSwgdGhlIHByb2JhYmlsaXR5IGluY3JlYXNlcyBmb3IgdGhlIGNoYW5jZSBvZiBkZWZhdWx0aW5nLiANCiAgICA8L3RkPg0KICA8L3RyPg0KICA8dHI+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij5CaWxsX0FtdDEsIDIsIDMsIDQsIDUsIDYgPC90ZD4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPkludGVnZXI8L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+DQogICAgVGhlc2UgY29sdW1ucyBsaXN0IHRoZSBhbW91bnQgYmlsbGVkIHRvIGVhY2ggY3VzdG9tZXIuIFRoZSBhc3N1bXB0aW9uIHdvdWxkIGJlIHRoYXQgdGhlIGhpZ2hlciB0aGUgYmlsbCwgdGhlIGhpZ2hlciB0aGUgcmlzaywgaG93ZXZlciBJIHdvdWxkIG5vdCB1dGlsaXplIHRoaXMgYXR0cmlidXRlIG9uIGl0J3Mgb3duLiBXaGVuIHVzZWQgYnkgaXRzZWxmLCBJIGRvIG5vdCBiZWxpZXZlIGl0IGhhcyBhIGhpZ2ggcG90ZW50aWFsIGltcGFjdCBvbiBkZWZhdWx0IGJlY2F1c2Ugc29tZSBpbmRpdmlkdWFsIG1heSBoYXZlIHRoZSBmaWFuY2VzIHRvIHN1cHBvcnQgc3VjaCBoaWdoIGJpbGxzLiANCiAgICANCiAgICA8YnI+SWYgd2UgdXRpbGl6ZSB0aGlzIGF0dHJpYnV0ZSB3aXRoIExpbWl0X0JhbCwgaXQgY2FuIGhhdmUgaGlnaGVyIHBvdGVudGlhbCB0byBpbXBhY3Qgb24gZGVmYXVsdC4gRm9yIGV4YW1wbGUsIGlmIHRoZSBkZWJ0LXRvLWxpbWl0IHJhdGlvIGluY3JlYXNlcyBvdmVyIHRpbWUsIHRoaXMgbWF5IGluY3JlYXNlIHRoZSByaXNrIG9mIGRlZmF1bHRpbmcgYXMgdGhlIGN1c3RvbWVyIGlzIHRyeWluZyB0aGVpciBsaW1pdHMuIFRoaXMgaXMgZXNwZWNpYWxseSB0cnVlIGlmIHRoZSBjdXN0b21lciBpcyBkZWxheSBpbiBwYXltZW50cy4NCiAgICA8L3RkPg0KICA8L3RyPg0KICA8dHI+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij5QYXlfQW10MSwgMiwgMywgNCwgNSwgNiA8L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+SW50ZWdlcjwvdGQ+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij4NCiAgICBFYWNoIG9mIHRoZXNlIGFtb3VudHMgd2FzIHBhaWQgdG8gc2V0dGxlIHRoZSBwcmVjZWRpbmcgbW9udGgncyBiaWxsLCBlaXRoZXIgaW4gZnVsbCBvciBwYXJ0aWFsbHkuIElmIHRoZSBwYXltZW50cyBhcmUgbG93IG9yIGxlc3MgdGhhbiBhIHNwZWNpZmljIHBlcmNlbnQgb2YgdGhlIGFtb3VudCBvd2UsIHRoZW4gdGhpcyBtYXkgaW5jcmVhc2UgdGhlIHBvdGVudGlhbCBpbXBhY3Qgb2YgZGVmYXVsdGluZywgZXNwZWNpYWxseSBpZiB0aGUgYmlsbCBhbW91bnQgaXMgaW5jcmVhc2luZyBlYWNoIG1vbnRoLg0KICAgIDwvdGQ+DQogIDwvdHI+DQo8L3Rib2R5Pg0KPC90YWJsZT4NCg0KPGg0PkEuMi4gRGlzY3VzcyBpbiB3aGF0IHdheXMgc29tZSBvZiB0aGUgYWJvdmUgYXR0cmlidXRlcyBjb250cmlidXRlIHRvIGRlZmF1bHQucGF5bWVudC5uZXh0Lm1vbnRoIHRvZ2V0aGVyLiBQbGVhc2UgaWRlbnRpZnkgYXQgbGVhc3QgdHdvIHBhaXJzIG9mIGF0dHJpYnV0ZXMgdGhhdCBjYW4gYmUgdHJlYXRlZCB0b2dldGhlciBhbmQgaG93LjwvaDQ+DQo8dGFibGUgY2xhc3M9InRnIj4NCjx0aGVhZD4NCiAgPHRyPg0KICAgIDx0aCBjbGFzcz0idGctMGxheCI+VmFyMTwvdGg+DQogICAgPHRoIGNsYXNzPSJ0Zy0wbGF4Ij5WYXIyPC90aD4NCiAgICA8dGggY2xhc3M9InRnLTBsYXgiPlZhcjM8L3RoPg0KICAgIDx0aCBjbGFzcz0idGctMGxheCI+RGlzY3VzcyB0aGVpciByZWxhdGlvbiwgaG93IHRvIGNvbWJpbmUgdGhlbSAocmF0aW8sIGRpZmZlcmVuY2UsIG9yIG90aGVycykgYW5kIHlvdXIgcmVhc29uL3RoZW9yeTwvdGg+DQogIDwvdHI+DQo8L3RoZWFkPg0KPHRib2R5Pg0KICA8dHI+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij5QYXlfQW10PC90ZD4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPkJpbGxfQW10PC90ZD4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPjwvdGQ+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij4NCkkgYmVsaWV2ZSBQYXlfQW10IHRvIEJpbGxfQW10IHJhdGlvIHdvdWxkIGhlbHAgaW4gZGV0ZXJtaW5pbmcgdGhlIHBvc3NpYmlsaXR5IG9mIGRlZmF1bHRzIGFzIGl0IHNob3dzIHRoZSBjdXN0b21lcnMgd2lsbGluZ25lc3MgdG8gcGF5IHRoZWlyIGJpbGwgYW5kIHRoZSBhbW91bnQgb2YgdGhlaXIgYmlsbC4gSWYgdGhlIFBheV9BbXQgdG8gQmlsbF9BbXQgcmF0aW8gaXMgbGVzcyB0aGFuIC41LCB0aGlzIGlzIG1heSBtZWFuIHRoZSBjdXN0b21lciBpcyBhdCByaXNrLiAgICANCiAgICA8L3RkPg0KICA8L3RyPg0KICA8dHI+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij5MaW1pdF9CYWw8L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+QmlsbF9BbXQ8L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+PC90ZD4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPg0KSWYgdGhlIGRlYnQtdG8tbGltaXQgcmF0aW8gaXMgbG93LCBhbmQgY29uc2VjdXRpdmVseSBnZXR0aW5nIHdvcnNlLCB0aGVuIHRoaXMgaXMgYSByZWQgZmxhZyB0aGF0IHRoZSBjdXN0b21lciBpcyBzcGVuZGluZyBtb3JlIHRoYW4gaGUvc2hlIGlzIHdpbGxpbmcgdG8gcmVwYXksIHdoaWNoIGNhbiBsZWFkIHRvIGEgZGVmYXVsdC4gVGhlc2UgdHdvIHZhcmlhYmxlcyBzaG91bGQgYmUgdGhlIG1vc3Qgc2lnbmlmaWNhbnQgaW4gZGV0ZXJtaW5pbmcgdGhlIHBvc3NpYmlsaXR5IG9mIGRlZmF1bHRpbmcsIGhvd2V2ZXIsIHdoYXQgaWYgdGhlIGluZGl2aWR1YWwgaXMgcGF5aW5nIHRoZWlyIG1vcnRnYWdlIG9uIHRoZWlyIGNyZWRpdCBjYXJkIGFuZCBwYXlpbmcgaXQgaW4gZnVsbCBlYWNoIG1vbnRoIGp1c3QgdG8gZ2V0IOKAnHJld2FyZCBwb2ludHPigJ0uIFRoaXMgc2l0dWF0aW9uIGhhcHBlbnMsIGl0IGNhbiBzZW5kIG9mZiDigJhmYWxzZSBuZWdhdGl2ZXPigJkgb2YgZGVmYXVsdGluZy4gICAgDQogICAgPC90ZD4NCiAgPC90cj4NCiAgPHRyPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+UGF5XyM8L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+QmlsbF9BbXQ8L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+TGltaXRfQmFsPC90ZD4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPg0KVGhpcyBjb21iaW5hdGlvbiBpcyB0aGUgYmVzdCBjb21iaW5hdGlvbiB0byBkZXRlcm1pbmUgaWYgdGhlIGN1c3RvbWVyIGlzIGF0IHJpc2sgb2YgZGVmYXVsdGluZy4gSWYgdGhlIGN1c3RvbWVyIGlzIG5vdCBtYWtpbmcgcGF5bWVudHMsIHRoZSBiaWxsIGFtb3VudCBpcyBpbmNyZWFzaW5nIGVhY2ggbW9udGgsIGFuZCBoZS9zaGUgaXMgcmVhY2hpbmcgdGhlaXIgY3JlZGl0IGxpbWl0LCB0aGlzIGlzIHNpZ25pZmljYW50IHJlZCBmbGFnIGZvciBkZWZhdWx0aW5nLiAgICANCiAgICA8L3RkPg0KICA8L3RyPg0KPC90Ym9keT4NCjwvdGFibGU+DQoNCjxoMz5CLiBEYXRhIHByZXBhcmF0aW9uIGFuZCBjbGVhbnNpbmcgKDEgcG9pbnRzKTwvaDM+DQo8aDQ+Qi4xIExvYWQgZGF0YSBhbmQgaW5pdGlhbCBkYXRhIGNvbnZlcnNpb24vdHJhbnNmb3JtYXRpb246IDwvaDQ+DQoxKQlMb2FkIOKAnFVDSV9DcmVkaXRfQ2FyZC5jc3bigJ0gaW50byBkYXRhIGZyYW1lIHZhcmlhYmxlIGluIFIgdXNpbmcgcmVhZC5jc3YoKS4gDQpgYGB7cn0NCmNjIDwtIHJlYWQuY3N2KCJkYXRhL1VDSV9DcmVkaXRfQ2FyZC5jc3YiKQ0KYGBgDQoyKQlDb252ZXJ0IHRoZSBmb2xsb3dpbmcgYXR0cmlidXRlcyBpbnRvIGFzIG5vbWluYWwgKGNhdGVnb3JpY2FsLCBmYWN0b3IpIGF0dHJpYnV0ZXM6IFNleCwgRWR1Y2F0aW9uLCBNYXJyaWFnZSwgYW5kIGRlZmF1bHQucGF5bWVudC5uZXh0Lm1vbnRoLiANCmBgYHtyfQ0KY2MkU0VYIDwtIGZhY3RvcihjYyRTRVgsbGV2ZWxzPWMoMSwyKSwgbGFiZWxzPWMoIk1hbGUiLCJGZW1hbGUiKSkNCmNjJE1BUlJJQUdFIDwtIGZhY3RvcihjYyRNQVJSSUFHRSxsZXZlbHM9YygxLDIsMyksIGxhYmVscz1jKCJNYXJyaWVkIiwiU2luZ2xlIiwiT3RoZXJzIikpDQpjYyRFRFVDQVRJT04gPC0gZmFjdG9yKGNjJEVEVUNBVElPTixsZXZlbHM9YygxLDIsMyw0LDUsNiksIGxhYmVscz1jKCJHcmFkIFNjaG9vbCIsIlVuaXZlcnNpdHkiLCJIaWdoIFNjaG9vbCIsICJPdGhlcnMiLCAiVW5rbm93biIsICJVbmtub3duIikpDQpjYyRkZWZhdWx0LnBheW1lbnQubmV4dC5tb250aCA8LSBmYWN0b3IoY2MkZGVmYXVsdC5wYXltZW50Lm5leHQubW9udGgsbGV2ZWxzPWMoMCwxKSwgbGFiZWxzPWMoIk5vIiwiWWVzIikpDQpgYGANCjMpICAgVXNlIGNsYXNzKCkgZnVuY3Rpb24gY2hlY2sgb24gU2V4LCBFZHVjYXRpb24sIE1hcnJpYWdlLCBhbmQgZGVmYXVsdC5wYXltZW50Lm5leHQubW9udGgsIHRoZXkgc2hvdWxkIEFMTCBiZSDigJxmYWN0b3LigJ0gdmFyaWFibGVzLiANCmBgYHtyfQ0KY2xhc3MoY2MkU0VYKQ0KYGBgDQpgYGB7cn0NCmNsYXNzKGNjJE1BUlJJQUdFKQ0KYGBgDQpgYGB7cn0NCmNsYXNzKGNjJEVEVUNBVElPTikNCmBgYA0KYGBge3J9DQpjbGFzcyhjYyRkZWZhdWx0LnBheW1lbnQubmV4dC5tb250aCkNCmBgYA0KPGg0PkIuMiBDcmVhdGUgYSBmaWx0ZXJlZCBkYXRhc2V0IHdpdGggb25seSBub24tbmVnYXRpdmUgYW1vdW50cy4gICA8L2g0Pg0KMSkJVXNlIHRoZSBzdWJzZXQoKSBmdW5jdGlvbiB0byBzZWxlY3Qgb25seSBwb3NpdGl2ZSB2YWx1ZXMgb24gdGhlIDYgQklMTF9BTVQgYXR0cmlidXRlcyBhbmQgNiBQQVlfQU1UIGF0dHJpYnV0ZXMuIExpa2UgKGZpbGwgdGhlIOKApiB3aXRoIGFjdHVhbCk6IA0KYGBge3J9DQpjY3BvIDwtIHN1YnNldChjYywgQklMTF9BTVQxPj0wICYgQklMTF9BTVQyPj0wICYgQklMTF9BTVQzPj0wICYgQklMTF9BTVQ0Pj0wICYgQklMTF9BTVQ1Pj0wICYgQklMTF9BTVQ2Pj0wICYgUEFZX0FNVDE+PTAgJiBQQVlfQU1UMj49MCAmIFBBWV9BTVQzPj0wICYgUEFZX0FNVDQ+PTAgJiBQQVlfQU1UNT49MCAmIFBBWV9BTVQ2Pj0wKQ0KbnJvdyhjY3BvKQ0KYGBgDQoyKQlDaGVjayB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gdGhlIGZpbHRlcmVkIHN1YnNldCBhbmQgeW91IGNhbiB1c2UgVmlldyhjY3BvKSB0byBkb3VibGUgY2hlY2sgb24gdGhlIGRhdGEuIA0KYGBge3J9DQpWaWV3KGNjcG8pDQpgYGANCg0KPGgzPkMuCURhdGEgVHJhbnNmb3JtYXRpb24gYW5kIENsYXNzaWZpY2F0aW9uL01vZGVsaW5nICg0IHBvaW50cyk8L2gzPg0KPGg0PkMuMS4gUGljayBvbmUgY2xhc3NpZmljYXRpb24gbWV0aG9kLCBtb2RlbCB3aXRoIGRlZmF1bHQucGF5bWVudC5uZXh0Lm1vbnRoIH4gdmFyaWFibGVzIGluIEEuMS4sIGFuZCBldmFsdWF0ZTo8L2g0Pg0K4oCiCVNlbGVjdCA5MCUgb2YgZGF0YSBmb3IgdHJhaW5pbmcgYW5kIDEwJSBmb3IgdGVzdGluZzsgDQpgYGB7cn0NCmRmIDwtIHNvcnQoc2FtcGxlKG5yb3coY2NwbyksIG5yb3coY2NwbykqLjkpKQ0KdHJhaW4gPC0gY2Nwb1tkZiwgXQ0KdGVzdCA8LSBjY3BvWy1kZiwgXQ0KYGBgDQoNCuKAoglCdWlsZCBhIG1vZGVsIHdpdGggdHJhaW5pbmcgZGF0YSAoOTAlIGRhdGEpIHRvIHByZWRpY3QgZGVmYXVsdC5wYXltZW50Lm5leHQubW9udGgsIHVzaW5nIGF0IGxlYXN0IHRocmVlIHZhcmlhYmxlcyBmcm9tIEEuMS4gDQpgYGB7cn0NCm5iRGVtIDwtIG5haXZlQmF5ZXMoZGVmYXVsdC5wYXltZW50Lm5leHQubW9udGggfiBMSU1JVF9CQUwgKyBCSUxMX0FNVDEgKyBQQVlfMCwgdHJhaW4pDQpuYkRlbQ0KYGBgDQoNCuKAoglSdW4gcHJlZGljdGlvbiB3aXRoIHRoZSBtb2RlbCBvbiB0ZXN0IGRhdGEgKDEwJSBkYXRhKSBhbmQgcmVjb3JkIHRoZSBmb2xsb3dpbmcgc2NvcmVzOiANCm8JUHJlc2VudCB0aGUgY29uZnVzaW9uIHRhYmxlIHdpdGggVFAsIFROLCBGUCwgYW5kIEZODQpvCVJlcG9ydCBBY2N1cmFjeSwgUHJlY2lzaW9uLCBSZWNhbGwsIEYsIGFuZCBLYXBwYSBpbiBUYWJsZSBELg0KYGBge3J9DQpuYl9wcmVkaWN0aW9uIDwtIHByZWRpY3QobmJEZW0sIHRlc3QsIHR5cGUgPSAiY2xhc3MiKQ0KDQpjb25mdXNpb25NYXRyaXgoZGF0YSA9IG5iX3ByZWRpY3Rpb24sDQogICAgICAgICAgICAgICAgcmVmZXJlbmNlID10ZXN0JGRlZmF1bHQucGF5bWVudC5uZXh0Lm1vbnRoLCANCiAgICAgICAgICAgICAgICBkbm4gPSBjKCJQcmVkaWN0ZWQiLCAiQWN0dWFsIiksDQogICAgICAgICAgICAgICAgbW9kZSA9ICJwcmVjX3JlY2FsbCIpDQpgYGANCjxoND5DLjIuIFBlcmZvcm0gZGF0YSB0cmFuc2Zvcm1hdGlvbiAod2l0aCBuZXcgcmVsYXRpb25hbCBhdHRyaWJ1dGVzKSBhbmQgcmVkbyBjbGFzc2lmaWNhdGlvbjogPC9oND4NCjEpCUZvbGxvdyB0aGUgdHJlYXRtZW50cyAoYXQgbGVhc3QgdHdvIHJlbGF0aW9ucykgb2YgdGhlIHZhcmlhYmxlIHBhaXJzIHlvdSBoYXZlIGlkZW50aWZpZWQgaW4gQS4yDQoyKQlDcmVhdGUgbmV3IGF0dHJpYnV0ZXMgdGhhdCBjb21wdXRlIHRoZSByZWxhdGlvbnMgeW91IGhhdmUgaWRlbnRpZmllZCBpbiBBLjI6DQpgYGB7cn0NCmNjcG8kUEFJRF9CSUxMIDwtIGMoY2NwbyRQQVlfQU1UMSAvIGNjcG8kQklMTF9BTVQyKSAjMSBvciBncmVhdGVyIG1lYW5zIGZ1bGwgcGF5bWVudCB3YXMgbWFkZTsgQW55dGhpbmcgbGVzcyB0aGFuIDEgaXMgYSByaXNrIHRvIGRlZmF1bHQNCmNjcG8kREVCVF9UT19MSU1JVCA8LSBjKGNjcG8kQklMTF9BTVQxIC8gY2NwbyRMSU1JVF9CQUwpICNHb2FsIGlzIHRvIGJlIGNsb3NlIHRvIDAuIDEgb3IgZ3JlYXRlciBtZWFucyB0aGUgY3VzdG9tZXIgaXMgYXQgdGhlaXIgY3JlZGl0IGxpbWl0IG9yIGdyZWF0ZXI7IGhpZ2ggcmlzayBvZiBkZWZhdWx0aW5nDQpgYGANCg0KQWZ0ZXIgcmV2aWV3aW5nIGRhdGEsIEkgd2lsbCByZXBsYWNpbmcgJ0luZicgYW5kICdOYU4nIHdpdGggMSBmb3IgUEFJRF9CSUxMIGF0dHJpYnV0ZSBhcyB0aGlzIG1lYW5zIHRoYXQgdGhlIGN1c3RvbWVyIGlzIGVpdGhlciBwYXlpbmcgYSBiaWxsIHRoYXQgaXMgemVybyAob3ZlcnBheWluZykgb3IgZGlkbid0IHBheSBmb3IgYSBiaWxsIGFzIHRoZXJlIHdhcyBubyBiaWxsIHRvIHBheS4gVGhlc2Ugb2NjdXJyZW5jZXMgaXMgb25seSBoYXBwZW5pbmcgaW4gb25lIG9mIHRoZSB0d28gYXR0cmlidXRlcy4NCmBgYHtyfQ0KaXMubmEoY2NwbykgPC0gc2FwcGx5KGNjcG8sIGlzLmluZmluaXRlKQ0KY2Nwb1tpcy5uYShjY3BvKV0gPC0gMQ0KYGBgDQoNCjMpCUJ1aWxkIGEgbW9kZWwgd2l0aCB0cmFpbmluZyBkYXRhICg5MCUgZGF0YSkgdG8gcHJlZGljdCBkZWZhdWx0LnBheW1lbnQubmV4dC5tb250aCwgdXNpbmcgdGhlIG5ldyByZWxhdGlvbmFsIGF0dHJpYnV0ZXMgKHBsdXMgYW55IG90aGVyIHZhcmlhYmxlcyB5b3Ugd291bGQgbGlrZSB0byBpbmNsdWRlKSBoZXJlLiANCg0KNCkJUnVuIHByZWRpY3Rpb24gd2l0aCB0aGUgbW9kZWwgb24gdGVzdCBkYXRhICgxMCUgZGF0YSkgYW5kIHJlY29yZCB0aGUgZm9sbG93aW5nIHNjb3JlczogDQphLglQcmVzZW50IHRoZSBjb25mdXNpb24gdGFibGUgd2l0aCBUUCwgVE4sIEZQLCBhbmQgRk4NCmIuCVJlcG9ydCBBY2N1cmFjeSwgUHJlY2lzaW9uLCBSZWNhbGwsIEYsIGFuZCBLYXBwYSBpbiBUYWJsZSBELg0KYGBge3J9DQpkZjEgPC0gc29ydChzYW1wbGUobnJvdyhjY3BvKSwgbnJvdyhjY3BvKSouOSkpDQp0cmFpbjEgPC0gY2Nwb1tkZjEsIF0NCnRlc3QxIDwtIGNjcG9bLWRmMSwgXQ0KDQpuYkRlbTEgPC0gbmFpdmVCYXllcyhkZWZhdWx0LnBheW1lbnQubmV4dC5tb250aCB+IFBBSURfQklMTCArIERFQlRfVE9fTElNSVQgKyBQQVlfMCwgdHJhaW4xKQ0KbmJEZW0xDQpgYGANCg0KYGBge3J9DQpuYl9wcmVkaWN0aW9uMSA8LSBwcmVkaWN0KG5iRGVtMSwgdGVzdDEsIHR5cGUgPSAiY2xhc3MiKQ0KDQpjb25mdXNpb25NYXRyaXgoZGF0YSA9IG5iX3ByZWRpY3Rpb24xLA0KICAgICAgICAgICAgICAgIHJlZmVyZW5jZSA9dGVzdDEkZGVmYXVsdC5wYXltZW50Lm5leHQubW9udGgsIA0KICAgICAgICAgICAgICAgIGRubiA9IGMoIlByZWRpY3RlZCIsICJBY3R1YWwiKSwNCiAgICAgICAgICAgICAgICBtb2RlID0gInByZWNfcmVjYWxsIikNCg0KYGBgDQoNCjxoND5DLjMuIEV4YW1pbmUgYXR0cmlidXRlIHZhbHVlIGRpc3RyaWJ1dGlvbiAoaGlzdG9ncmFtKSwgYW5kIHBlcmZvcm0gbG9nIHRyYW5zZm9ybWF0aW9uIG9uIGF0dHJpYnV0ZXMgeW91IHNlZSBmaXQ6ICAgPC9oND4NCjEpCUNyZWF0ZSBhIG5ldyBhdHRyaWJ1dGUgdGhhdCBpcyB0aGUgbG9nYXJpdGhtIG9mIGVhY2ggYXR0cmlidXRlIHdpdGggYW4gZXh0cmVtZWx5IHdpZGUsIOKAnHNrZXfigJ0gZGlzdHJpYnV0aW9uLiANCg0KYGBge3J9DQpoaXN0KGNjcG8kTElNSVRfQkFMLCB4bGFiPSJDcmVkaXQgTGltaXQiKQ0KYGBgDQoNCmBgYHtyfQ0KaGlzdChjY3BvJEJJTExfQU1UMSwgeGxhYj0iTGFzdCBCaWxsIFN0YXRlbWVudCBBbW91bnQiKQ0KYGBgDQoNCmBgYHtyfQ0KaGlzdChjY3BvJFBBWV8wLCB4bGFiPSJMYXN0IFBheW1lbnQgQW1vdW50IikNCmBgYA0KDQpgYGB7cn0NCmhpc3QoY2NwbyRQQUlEX0JJTEwsIHhsYWI9IlBhaWQgT2ZmIFByZXZpb3VzIFN0YXRlbWVudCBSYXRpbyIpDQpgYGANCg0KYGBge3J9DQpoaXN0KGNjcG8kREVCVF9UT19MSU1JVCwgeGxhYj0iRGVidCB0byBDcmVkaXQgUmF0aW8iKQ0KYGBgDQoNClRoZSBmb2xsb3dpbmcgYXR0cmlidXRlcyBJIGhhdmUgaWRlbnRpZmllZCB0byBoYXZlIGV4dHJlbWVseSB3aWRlIOKAnHNrZXfigJ0gZGlzdHJpYnV0aW9uLCB3aGljaCBJIHdpbGwgTE9HOg0KYGBge3J9DQpjY3BvJExJTUlUX0JBTF9MT0cgPC0gbG9nMTAoY2NwbyRMSU1JVF9CQUwpDQpjY3BvJEJJTExfQU1UMV9MT0cgPC1sb2cxMChjY3BvJEJJTExfQU1UMSkgDQpjY3BvJFBBSURfQklMTF9MT0cgPC1sb2cxMChjY3BvJFBBSURfQklMTCkNCmNjcG8kREVCVF9UT19MSU1JVF9MT0cgPC1sb2cxMChjY3BvJERFQlRfVE9fTElNSVQpDQpgYGANCg0KYGBge3J9DQpoaXN0KGNjcG8kTElNSVRfQkFMX0xPRywgeGxhYj0iQ3JlZGl0IExpbWl0IikNCmBgYA0KDQoNCmBgYHtyfQ0KaGlzdChjY3BvJEJJTExfQU1UMV9MT0csIHhsYWI9Ikxhc3QgQmlsbCBTdGF0ZW1lbnQgQW1vdW50IikNCmBgYA0KDQpgYGB7cn0NCmhpc3QoY2NwbyRQQUlEX0JJTExfTE9HLCB4bGFiPSJQYWlkIE9mZiBQcmV2aW91cyBTdGF0ZW1lbnQgUmF0aW8iKQ0KYGBgDQoNCmBgYHtyfQ0KaGlzdChjY3BvJERFQlRfVE9fTElNSVRfTE9HLCB4bGFiPSJEZWJ0IHRvIENyZWRpdCBSYXRpbyIpDQpgYGANCg0KDQoNCg0KNCkJQnVpbGQgYSBtb2RlbCB3aXRoIHRyYWluaW5nIGRhdGEgKDkwJSBkYXRhKSB0byBwcmVkaWN0IGRlZmF1bHQucGF5bWVudC5uZXh0Lm1vbnRoLCB1c2luZyBhdCB0aGUgbmV3IHJlbGF0aW9uYWwgKGFuZCBsb2ctdHJhbnNmb3JtZWQpIGF0dHJpYnV0ZXMgcGx1cyBhbnkgb3RoZXIgdmFyaWFibGVzIHlvdSB3b3VsZCBsaWtlIHRvIGluY2x1ZGUgaGVyZS4gDQoNCmBgYHtyfQ0KaXMubmEoY2NwbykgPC0gc2FwcGx5KGNjcG8sIGlzLmluZmluaXRlKQ0KY2Nwb1tpcy5uYShjY3BvKV0gPC0gMA0KYGBgDQoNCmBgYHtyfQ0KZGZfbG9nIDwtIHNvcnQoc2FtcGxlKG5yb3coY2NwbyksIG5yb3coY2NwbykqLjkpKQ0KdHJhaW5fbG9nIDwtIGNjcG9bZGZfbG9nLCBdDQp0ZXN0X2xvZyA8LSBjY3BvWy1kZl9sb2csIF0NCm5iRGVtX2xvZyA8LSBuYWl2ZUJheWVzKGRlZmF1bHQucGF5bWVudC5uZXh0Lm1vbnRoIH4gTElNSVRfQkFMX0xPRyArIEJJTExfQU1UMV9MT0cgKyBQQVlfMCwgdHJhaW5fbG9nKQ0KbmJEZW1fbG9nDQpgYGANCg0KDQo1KQlSdW4gcHJlZGljdGlvbiB3aXRoIHRoZSBtb2RlbCBvbiB0ZXN0IGRhdGEgKDEwJSBkYXRhKSBhbmQgcmVjb3JkIHRoZSBmb2xsb3dpbmcgc2NvcmVzOiANCmEuCVByZXNlbnQgdGhlIGNvbmZ1c2lvbiB0YWJsZSB3aXRoIFRQLCBUTiwgRlAsIGFuZCBGTg0KYi4JUmVwb3J0IEFjY3VyYWN5LCBQcmVjaXNpb24sIFJlY2FsbCwgRiwgYW5kIEthcHBhIGluIFRhYmxlIEQuDQpgYGB7cn0NCm5iX3ByZWRpY3Rpb25fbG9nIDwtIHByZWRpY3QobmJEZW1fbG9nLCB0ZXN0X2xvZywgdHlwZSA9ICJjbGFzcyIpDQoNCmNvbmZ1c2lvbk1hdHJpeChkYXRhID0gbmJfcHJlZGljdGlvbl9sb2csDQogICAgICAgICAgICAgICAgcmVmZXJlbmNlID10ZXN0X2xvZyRkZWZhdWx0LnBheW1lbnQubmV4dC5tb250aCwgDQogICAgICAgICAgICAgICAgZG5uID0gYygiUHJlZGljdGVkIiwgIkFjdHVhbCIpLA0KICAgICAgICAgICAgICAgIG1vZGUgPSAicHJlY19yZWNhbGwiKQ0KYGBgDQpDLjQuIFBpY2sgYW5vdGhlciBjbGFzc2lmaWNhdGlvbiBtb2RlbCBvciB0aGUgc2FtZSBtb2RlbCB3aXRoIGRpZmZlcmVudCBwYXJhbWV0ZXIgdmFsdWVzLCBhbmQgcmVwZWF0IHRoZSBtb2RlbGluZyBhbmQgZXZhbHVhdGlvbiBhcyBpbiBDLjMuIFJlcG9ydCB0aGUgY29uZnVzaW9uIHRhYmxlIGFuZCByZXN1bHRzIHRvIFRhYmxlIEQuDQoNCmBgYHtyfQ0KZGZfbG9nMSA8LSBzb3J0KHNhbXBsZShucm93KGNjcG8pLCBucm93KGNjcG8pKi45KSkNCnRyYWluX2xvZzEgPC0gY2Nwb1tkZl9sb2cxLCBdDQp0ZXN0X2xvZzEgPC0gY2Nwb1stZGZfbG9nMSwgXQ0KbmJEZW1fbG9nMSA8LSBuYWl2ZUJheWVzKGRlZmF1bHQucGF5bWVudC5uZXh0Lm1vbnRoIH4gUEFJRF9CSUxMX0xPRyArIERFQlRfVE9fTElNSVRfTE9HICsgUEFZXzAsIHRyYWluX2xvZzEpDQpuYkRlbV9sb2cxDQpgYGANCmBgYHtyfQ0KbmJfcHJlZGljdGlvbl9sb2cxIDwtIHByZWRpY3QobmJEZW1fbG9nMSwgdGVzdF9sb2cxLCB0eXBlID0gImNsYXNzIikNCg0KY29uZnVzaW9uTWF0cml4KGRhdGEgPSBuYl9wcmVkaWN0aW9uX2xvZzEsDQogICAgICAgICAgICAgICAgcmVmZXJlbmNlID10ZXN0X2xvZzEkZGVmYXVsdC5wYXltZW50Lm5leHQubW9udGgsIA0KICAgICAgICAgICAgICAgIGRubiA9IGMoIlByZWRpY3RlZCIsICJBY3R1YWwiKSwNCiAgICAgICAgICAgICAgICBtb2RlID0gInByZWNfcmVjYWxsIikNCmBgYA0KDQo8aDM+RC4JRXZhbHVhdGlvbiBhbmQgUmVzdWx0cyAoMiBwb2ludHMpPC9oMz4NCjx0YWJsZSBjbGFzcz0idGciPg0KPHRoZWFkPg0KICA8dHI+DQogICAgPHRoIGNsYXNzPSJ0Zy0wbGF4IiByb3dzcGFuPSIyIj48L3RoPg0KICAgIDx0aCBjbGFzcz0idGctMGxheCIgcm93c3Bhbj0iMiI+TWV0aG9kPC90aD4NCiAgICA8dGggY2xhc3M9InRnLTBsYXgiIGNvbHNwYW49IjUiPkMuMS4gQ2xhc3NpZmljYXRpb24gd2l0aG91dCBUcmFuc2Zvcm1hdGlvbjwvdGg+DQogIDwvdHI+DQogIDx0cj4NCiAgCTx0aCBjbGFzcz0idGctMGxheCI+Q29ycmVjdCAlPC90aD4NCiAgCTx0aCBjbGFzcz0idGctMGxheCI+UHJlY2lzaW9uPC90aD4NCiAgCTx0aCBjbGFzcz0idGctMGxheCI+UmVjYWxsPC90aD4NCiAgCTx0aCBjbGFzcz0idGctMGxheCI+RjwvdGg+DQogIAk8dGggY2xhc3M9InRnLTBsYXgiPkthcHBhPC90aD4NCiAgPC90cj4NCjwvdGhlYWQ+DQo8dGJvZHk+DQogIDx0cj4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPjxiPkMuMTwvYj48L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+DQoJPGI+TW9kZWwgMTo8L2I+IG5iX3ByZWRpY3Rpb24NCgk8YnI+PGk+TElNSVRfQkFMLCBCSUxMX0FNVDEsIFBBWV8wPC9pPg0KCTwvdGQ+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij4gODIuNCU8L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+LjgzNzM8L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+Ljk2MTI8L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+Ljg5NTY8L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+LjM2NzY8L3RkPg0KICA8L3RyPg0KICA8dHI+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij48Yj5DLjI8L2I+PC90ZD4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPg0KCTxiPk1vZGVsIDI6PC9iPiBuYl9wcmVkaWN0aW9uMQ0KCTxicj48aT5QQUlEX0JJTEwsIERFQlRfVE9fTElNSVQsIFBBWV8wPC9pPgkNCgk8L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+IDgxLjglPC90ZD4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPiAuODMzMTwvdGQ+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij4gLjk1ODk8L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+IC44OTg4PC90ZD4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPiAuMzQwNDwvdGQ+DQogIDwvdHI+DQogIDx0cj4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPjxiPkMuMzwvYj48L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+DQoJPGI+TW9kZWwgMzo8L2I+IG5iX3ByZWRpY3Rpb25fbG9nDQoJPGJyPjxpPkxJTUlUX0JBTF9MT0csIEJJTExfQU1UMV9MT0csIFBBWV8wPC9pPgkNCgk8L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+IDgyLjMzJTwvdGQ+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij4gLjg0MzY8L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+IC45NTA0PC90ZD4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPiAuODkzODwvdGQ+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij4gLjM3ODI8L3RkPg0KICA8L3RyPg0KICA8dHI+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij48Yj5DLjQ8L2I+PC90ZD4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPg0KDQoJPGI+TW9kZWwgNDo8L2I+IG5iX3ByZWRpY3Rpb25fbG9nMQ0KCTxicj48aT5QQUlEX0JJTExfTE9HLCBERUJUX1RPX0xJTUlUX0xPRywgUEFZXzA8L2k+CQ0KCTwvdGQ+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij4gODEuMDElPC90ZD4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPiAuODI3MzwvdGQ+DQogICAgPHRkIGNsYXNzPSJ0Zy0wbGF4Ij4gLjk1MTg8L3RkPg0KICAgIDx0ZCBjbGFzcz0idGctMGxheCI+IC44ODUyPC90ZD4NCiAgICA8dGQgY2xhc3M9InRnLTBsYXgiPiAuMzUxNTwvdGQ+DQogIDwvdHI+DQo8L3Rib2R5Pg0KPC90YWJsZT4NCjxicj4NCg0KDQo8aDM+RS4JUmVwb3J0IHdpdGggSW50ZXJwcmV0YXRpb24gYW5kIENvbmNsdXNpb24gKDMgcG9pbnRzKTwvaDM+DQoNCjxiPkUuMS4gSW4gdGVybXMgb2YgdGhlIHJlYXNvbnMgYW5kIHRoZW9yaWVzIHByZXNlbnRlZCBpbiB0YXNrcyBBMSB0aHJvdWdoIEEyLCB3aGljaCBvbmVzIGhhdmUgYmVlbiBjb25maXJtZWQgYnkgeW91ciBhbmFseXNpcz8gUGxlYXNlIGRpc2N1c3MgZXZlbiBpZiB0aGVyZSBpcyBubyBvYnZpb3VzIGFuc3dlci48L2I+DQoNCjx1PkExICdSZWFzb25zIGFuZCBUaGVvcmllcyc8L3U+PGJyPg0KPGI+TGltaXRfQmFsOjwvYj4gTXkgYXNzdW1wdGlvbiB3YXMgY29ycmVjdCB3aXRoIHRoaXMgYXR0cmlidXRlIHdoZW4gbG9va2luZyBhdCB0aGUgTmFpdmUgQmF5ZXMgbW9kZWwgQzEuIEZvciB0aG9zZSB0aGF0IHdpbGwgbm90IGRlZmF1bHQgaGFkIGEgaGlnaGVyIG1lYW4gKCQxNzQsOTU2LjcwKSBjb21wYXJlZCB0byB0aG9zZSB0aGF0IGRlZmF1bHRlZCAoJDEyNiw3MDcuMjApLg0KPGJyPjxicj4NCjxiPlBBWV8jOjwvYj4gTXkgYXNzdW1wdGlvbiB3YXMgaW5jb3JyZWN0LiBBbHRob3VnaCB0aGUgbWVhbiBmb3IgdGhvc2UgdGhhdCB3aWxsIG5vdCBkZWZhdWx0IHdhcyBpbiB0aGUgbmVnYXRpdmUgYXMgZXhwZWN0ZWQsIHRoZSBtZWFuIGZvciB0aG9zZSB0aGF0IHdvdWxkIGRlZmF1bHQgd2FzIC43MC4gSSB3YXMgZXhwZWN0aW5nIHRoZSBtZWFuIG9mIHRob3NlIHRoYXQgd291bGQgZGVmYXVsdCB0byBiZSBzaWduaWZpY2FudGx5IGhpZ2hlci4NCjxicj48YnI+DQo8Yj5CSUxMX0FNVCM6PC9iPiBNeSBhc3N1bXB0aW9uIHdhcyBjb3JyZWN0LiBJbiBmYWN0LCB0aG9zZSB0aGF0IGRpZCBub3QgZGVmYXVsdCBoYXMgYSBoaWdoZXIgbWVhbiBjb21wYXJlZCB0byB0aG9zZSB0aGF0IGRpZCBkZWZhdWx0ZWQuIFRoZSBhc3N1bXB0aW9uIGNhbiBiZSBtYWRlIHRoYXQgdGhvc2Ugd2l0aCBsZXNzIHNwZW5kaW5nIHBvd2VyIGFyZSBtb3N0IGxpa2VseSBkZWZhdWx0LCBob3dldmVyIHRoZSBnYXAgYmV0d2VlbiB0aGUgZGVmYXVsdGVkIGFuZCBub3QgZGVmYXVsdGVkIGlzbid0IHdpZGUgd2hlbiBsb29raW5nIGF0IHRoZSBOYWl2ZSBCYXllcyBtb2RlbCBDMS4NCjxicj48YnI+DQo8dT5BMiAnUmVhc29ucyBhbmQgVGhlb3JpZXMnPC91Pjxicj4NCjxiPkRFQlRfVE9fTElNSVQ6PC9iPiBNeSBhc3N1bXB0aW9uIHdhcyBjb3JyZWN0LCBidXQgbm90IHNpZ25pZmljYW50bHkuIFRob3NlIHRoYXQgaGFzIGEgbG93ZXIgRGVidCB0byBDcmVkaXQgcmF0aW8gd2lsbCBiZSBsZWFzdCBsaWtlbHkgdG8gZGVmYXVsdCBiYXNlZCB1cG9uIEMyJ3MgTmFpdmUgQmF5ZXMuIE5vbi1kZWZhdWx0ZXJzIGhhcyBhIG1lYW4gb2YgLjQzIHdoaWxlIGRlZmF1bHRlcnMgaGFzIC41MS4gT24gc2NhbGUgZnJvbSAwIHRvIDEsIHRoaXMgaXMgZW5vdWdoIG9mIGEgZ2FwIHRvIHNheSB0aGF0IHRoZSBhc3N1bXB0aW9uIGlzIGNvcnJlY3QuDQo8YnI+PGJyPg0KPGI+UEFJRF9CSUxMOjwvYj4gTXkgYXNzdW1wdGlvbiB3YXMgc2lnbmlmaWNhbnRseSBjb3JyZWN0IGZvciB0aG9zZSB0aGF0IHBhaWQgdGhlaXIgYmlsbC4gVGhpcyByYXRpbyB0b29rIHRoZSBjdXJyZW50IG1vbnRoIHBheW1lbnQgZGl2aWRlZCBieSB0aGUgcHJldmlvdXMgbW9udGgncyBiaWxsIGFtb3VudDsgMSBvciBncmVhdGVyIG1lYW50IHRoYXQgdGhlIGN1c3RvbWVyIHBhaWQgdGhlaXIgYmlsbCBvciBtb3JlLiBGb3IgdGhvc2UgdGhhdCBkaWQgbm90IGRlZmF1bHRlZCwgdGhlIG1lYW4gd2FzIC42NC4gVGhvc2UgdGhhdCBkaWQgZGVmYXVsdGVkIGhhcyBhIHBheW1lbnQtdG8tYmlsbCByYXRpbyBvZiAuMy4NCjxicj48YnI+PGJyPg0KDQoNCjxiPkUuMi4gRG9lcyBkYXRhIHRyYW5zZm9ybWF0aW9uICh3aXRoIG5ldyByZWxhdGlvbmFsIHZhcmlhYmxlcyBpbiBDLjIpIGhlbHA/IFdoaWNoIG9uZSBoZWxwcyBtb3N0IGFuZCB3aHk/IE9yIHdoaWNoIGRvZXMgbm90PzwvYj4NClN1cnByaXNpbmdseSB0aGUgZGF0YSB0cmFuc2Zvcm1hdGlvbiB3aXRoIHRoZSBuZXcgcmVsYXRpb25hbCB2YXJpYWJsZSBkaWQgbm90IGhlbHAuIEFjY3VyYWN5LCBQcmVjaXNpb24sIFJlY2FsbCwgYW5kIEthcHBhIHNjb3JlZCBsZXNzIHdoZW4gY29tcGFyZWQgdG8gQzEuIEYxIHdhcyBzbGlnaHRseSBoaWdoZXIgY29tcGFyZWQgdG8gQzEgYnkgLjAwMzIuIFRoZSBLYXBwYSB3ZXJlIGxvdyBiZXR3ZWVuIC4zNCBhbmQgLjM3LiB3aGljaCBtZWFucyB0aGUgYWdyZWVtZW50IGJldHdlZW4gY2xhc3NpZmljYXRpb24gYW5kIHRydXRoIHZhbHVlcyByZXByZXNlbnQgbG93IGFncmVlbWVudC4gVGhlcmUgd2FzIG5vIGltcHJvdmVtZW50IGFmdGVyIHRoZSB0cmFuc2Zvcm1hdGlvbi4gDQo8YnI+PGJyPjxicj4NCg0KPGI+RS4zLiBXaGljaCBjbGFzc2lmaWNhdGlvbiBtZXRob2QocykgYW5kL29yIHBhcmFtZXRlcnMgYXBwZWFyIHRvIHBlcmZvcm0gd2VsbD8gV2hpY2ggb25lcyBkbyBub3Q/PC9iPg0KSSB1dGlsaXplZCBOYWl2ZSBCYXllcyBhcyBteSBjbGFzc2lmaWNhdGlvbiBtZXRob2QgdGhyb3VnaG91dCB0aGUgZXhlcmNpc2VzIGFzIEkgd2FzIGhvcGluZyBhbmQgZXhwZWN0aW5nIHRvIGhhdmUgYmV0dGVyIHJlc3VsdHMgYWZ0ZXIgdGhlIHRyYW5zZm9ybWF0aW9uLiBTYWRseSwgdGhlIGJhc2ljIHBhcmFtZXRlcnMgKFBBWV8wLCBCSUxMX0FNSVQxLCBhbmQgTElNSVRfQkFMKSB3aXRob3V0IHRyYW5zZm9ybWluZyBkaWQgYXMgZ29vZCBvciBiZXR0ZXIgdGhhbiB0aGUgb3RoZXIgbW9kZWxzLiBBbGwgbW9kZWxzIHNjb3JlZCBoaWdoIGluIGVhY2ggbWV0cmljIGV4Y2VwdCBmb3IgS2FwcGEuDQoNCg0KPGI+RS40LiBSZXZpZXdpbmcgcmVzdWx0cyBpbiBUYXNrIEQsIHdoaWNoIGV2YWx1YXRpb24gbWV0cmljcyAob2YgQ29ycmVjdCUsIEthcHBhLCBGLCBQcmVjaXNpb24sIGFuZCBSZWNhbGwpIGJlc3QgY2FwdHVyZSBob3cgZ29vZC9wb29yIHRoZSByZXN1bHQgaXM/IFdoaWNoIG1ldHJpYyBpcyBub3QgYXMgaGVscGZ1bD88L2I+DQoNCkFsdGhvdWdoIEFjY3VyYWN5IHdvdWxkIGJlIHRoZSBwcmltYXJ5IG1ldHJpYyB0byB1c2UsIEthcHBhIG1ldHJpYyBpcyBhIHJvYnVzdCB3YXkgdG8gZmluZCB0aGUgZGVncmVlIG9mIGFncmVlbWVudCBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMgdGhhdCBhcmUgYmVpbmcgdXNlZC4gS2FwcGEgaXMgbW9yZSBpbmZvcm1hdGl2ZSB0aGFuIEFjY3VyYWN5IHdoZW4gd29ya2luZyB3aXRoIHVuYmFsYW5jZWQgZGF0YS4NCg0KVGhlIGxlYXN0IGhlbHBmdWwgd291bGQgYmUgQWNjdXJhY3kuIEEgZ29vZCBtb2RlbCBzaG91bGQgaGF2ZSBhIGhpZ2ggYWNjdXJhY3kgc2NvcmUsIGJ1dCBoYXZpbmcgYSBoaWdoIGFjY3VyYWN5IHNjb3JlIGFsb25lIGRvZXMgbm90IGd1YXJhbnRlZSB0aGUgbW9kZWwgaXMgd2VsbCBlc3RhYmxpc2hlZC4gDQoNCkFzIGZvciB0aGUgb3RoZXIgbWV0cmljczoNCg0KPGI+PHU+UHJlY2lzaW9uPC91PjwvYj4gaWRlbnRpZmllcyBob3cgYWNjdXJhdGVseSB0aGUgbW9kZWwgcHJlZGljdGVkIHRoZSBwb3NpdGl2ZSBjbGFzc2VzLiBUaGUgbnVtYmVyIG9mIHRydWUgcG9zaXRpdmUgZXZlbnRzIGlzIGRpdmlkZWQgYnkgdGhlIHN1bSBvZiBwb3NpdGl2ZSB0cnVlIGFuZCBmYWxzZSBldmVudHMuDQoNCjxiPjx1PlJlY2FsbDwvdT48L2I+IG1lYXN1cmVzIHRoZSByYXRpbyBvZiBwcmVkaWN0ZWQgdGhlIHBvc2l0aXZlIGNsYXNzZXMuIFRoZSBudW1iZXIgb2YgdHJ1ZSBwb3NpdGl2ZSBldmVudHMgaXMgZGl2aWRlZCBieSB0aGUgc3VtIG9mIHRydWUgcG9zaXRpdmUgYW5kIGZhbHNlIG5lZ2F0aXZlIGV2ZW50cy4NCg0KPGI+PHU+RjE8L3U+PC9iPiBpcyB0aGUgd2VpZ2h0ZWQgYXZlcmFnZSBzY29yZSBvZiByZWNhbGwgYW5kIHByZWNpc2lvbi4gVGhlIHZhbHVlIGF0IDEgaXMgdGhlIGJlc3QgcGVyZm9ybWFuY2UgYW5kIGF0IDAgaXMgdGhlIHdvcnN0Lg0KDQpBbGwgYXJlIGdvb2QgbWV0cmljcyB0byB1c2UgZGVwZW5kaW5nIG9uIHRoZSBkYXRhc2V0IHRoYXQgaXMgYmVpbmcgdXNlZC4gDQoNCg0KPGI+RS41LiBQaWNrIHRoZSBtb3N0IGhlbHBmdWwgZXZhbHVhdGlvbiBtZXRyaWMsIHdoaWNoIG1ldGhvZCAod2l0aCB3aGF0IGRhdGEgdHJhbnNmb3JtYXRpb24gaWYgYXBwbGljYWJsZSkgaXMgdGhlIG92ZXJhbGwgd2lubmVyIG9mIHRoZSByZXN1bHRzPyBSZWFzb24gYWJvdXQgd2h5IHRoZSBtZXRob2QgcGVyZm9ybXMgd2VsbC48L2I+DQoNClRoZSBBY2N1cmFjeSwgUHJlY2lzaW9uLCBSZWNhbGwsIGFuZCBGMSBzY29yZXMgZm9yIGFsbCBtb2RlbHMgd2VyZSBoaWdoIHJhbmdpbmcgZnJvbSAuODEgdG8gLjk2LCBleGNlcHQgZm9yIEthcHBhLiBLYXBwYSBzY29yZXMgd2VyZSBiZXR3ZWVuIC4zNCBhbmQgLjM3LiBPdmVyYWxsIHRoZXJlIHdhc24ndCBtdWNoIG9mIGEgZGlmZmVyZW5jZSBhbW9uZyB0aGUgZm91ciBtb2RlbHMsIGhvd2V2ZXIgSSB3b3VsZCBwaWNrIG1vZGVsIEMzIGFzIHRoZSBLYXBwYSBhbmQgUHJlY2lzaW9uIHNjb3JlcyBoaWdoZXN0IGFtb25nIHRoZSBvdGhlciBtb2RhbHMuIA0KDQoNCg0KDQoNCg0KDQoNCg0K