Victor Enchautegui

10/25/2020


Data Preparation

A.1. Data Loading and Initial Transformation

Load data and transform the default.payment.next.month variable into a nominal (factor) variable:

library(ggplot2)
cc <- read.csv("data/UCI_Credit_Card.csv")
cc$default.payment.next.month <- factor(cc$default.payment.next.month,levels=c(0,1), labels=c("No","Yes"))

A.2. Demographic Variables

Education

ggplot(cc, aes(x=EDUCATION, fill=default.payment.next.month, color=default.payment.next.month)) + 
  geom_histogram(binwidth=1, position="stack") +
  scale_color_manual(values=c("black","black")) +
  scale_fill_manual(values=c("darkolivegreen4", "red"))

Analysis: EDUCATION: (1=graduate school, 2=university, 3=high school, 4=others, 5=unknown, 6=unknown). Majority of the population has an university education or greater. This variable will help with our analysis as it seems there is a relationship with university level education and defaulting.


Age

ggplot(cc, aes(x=AGE, fill=default.payment.next.month, color=default.payment.next.month)) + 
  geom_histogram(binwidth=1, position="stack") +
  scale_color_manual(values=c("black","black")) +
  scale_fill_manual(values=c("darkolivegreen4", "red"))

Analysis: AGE: Age in years. There seems to be a relationship between the younger age group and defaulting, however this can be due to the fact the population sample is heavy on the left.


A.3. Payment Status Variables

PAY_0

ggplot(cc, aes(x=PAY_0, fill=default.payment.next.month, color=default.payment.next.month)) + 
  geom_histogram(binwidth=1, position="stack") +
  scale_color_manual(values=c("black","black")) +
  scale_fill_manual(values=c("darkolivegreen4", "red"))

Analysis: PAY_0: Repayment status in September, 2005 (-1=pay duly, 1=payment delay for one month, 2=payment delay for two months, … 8=payment delay for eight months, 9=payment delay for nine months and above). However, there are two unnamed labels that are represented within the data, which are -2 and 0. If -1 = ‘paid on-time’, what 0 represent? I read two thread on Kaggle for this issue, and it continues to be unclear and the data didn’t match the responses. For the sake of this analysis, I will retrofit having -2 start as “Pay duly”.

I picked this pay statement as it is the initial statement and there is a focused median.

PAY_3

ggplot(cc, aes(x=PAY_3, fill=default.payment.next.month, color=default.payment.next.month)) + 
  geom_histogram(binwidth=1, position="stack") +
  scale_color_manual(values=c("black","black")) +
  scale_fill_manual(values=c("darkolivegreen4", "red"))

Analysis: There is a spike increase in label 0 of defaulted claims. This may mean that the probability of having consecutive ‘no default’ decreases over time.

PAY_5

ggplot(cc, aes(x=PAY_5, fill=default.payment.next.month, color=default.payment.next.month)) + 
  geom_histogram(binwidth=1, position="stack") +
  scale_color_manual(values=c("black","black")) +
  scale_fill_manual(values=c("pink", "red"))

Analysis: Similar analysis as PAY_3, however there is an increase in -2, -1, 0 and a decrease in 2.


A.4. Transforming Nominal Variables

Transform related demographic variables into nominal values with proper labels:

cc$AGE <- cut(cc$AGE,breaks=c(0,20,30,40,50,60,70,80,100), labels=c("<20s","20s", "30s", "40s", "50s", "60s", "70s", "80s+")) 
cc$AGE <- factor(cc$AGE) 
cc$EDUCATION <- factor(cc$EDUCATION,levels=c(1,2,3,4,5,6), labels=c("Grad School", "University", "High School", "Others", "Unknown", "Unknown"))

Transformed the additional variables to help with section B.2 of the assignment:

cc$PAY_0  <- factor(cc$PAY_0 , levels=c(-2,-1,0,1,2,3,4,5,6,7,8,9), labels=c("Pay duly", "1mo", "2mos", "3mos", "4mos",  "5mos", "6mos", "7mos",  "8mos", "9mos", "10mos", "11mos"))
cc$PAY_2  <- factor(cc$PAY_2 , levels=c(-2,-1,0,1,2,3,4,5,6,7,8,9), labels=c("Pay duly", "1mo", "2mos", "3mos", "4mos",  "5mos", "6mos", "7mos",  "8mos", "9mos", "10mos", "11mos"))
cc$PAY_3  <- factor(cc$PAY_3 , levels=c(-2,-1,0,1,2,3,4,5,6,7,8,9), labels=c("Pay duly", "1mo", "2mos", "3mos", "4mos",  "5mos", "6mos", "7mos",  "8mos", "9mos", "10mos", "11mos"))

View Education:

View(cc$EDUCATION) 

View Age:

View(cc$AGE)


A.5. Selection of Training Data

train <- cc[sample(nrow(cc), 5000), ] 

Check train data:

nrow(train)
[1] 5000

A.6. Selection of Testing Data

test <- cc[c(18,3600),]
test

Data Classification

B.1. Naive Bayes using Demographic Variables

library(e1071)
nbDem <- naiveBayes(default.payment.next.month ~ SEX + EDUCATION + AGE, train)
nbDem

Naive Bayes Classifier for Discrete Predictors

Call:
naiveBayes.default(x = X, y = Y, laplace = laplace)

A-priori probabilities:
Y
   No   Yes 
0.764 0.236 

Conditional probabilities:
     SEX
Y         [,1]      [,2]
  No  1.614398 0.4868009
  Yes 1.571186 0.4951164

     EDUCATION
Y      Grad School   University  High School       Others      Unknown
  No  0.3592563498 0.4642576591 0.1550144017 0.0060225190 0.0154490704
  Yes 0.3033898305 0.5076271186 0.1838983051 0.0008474576 0.0042372881

     AGE
Y             20s         30s         40s         50s         60s         70s
  No  0.365706806 0.374345550 0.191623037 0.060471204 0.007853403 0.000000000
  Yes 0.372033898 0.315254237 0.209322034 0.091525424 0.011864407 0.000000000

SEX: The probability of defaulting is higher for women than it is for men. However it does not make sense as the probability of not defaulting is higher for women than it is for men.

EDUCATION: The probability of defaulting is higher for University than it is for others. However it does not make sense as the probability of not defaulting is higher for University than it is for others

AGE: The probability of defaulting is higher for younger individuals than it is for others. However it does not make sense as the probability of not defaulting is higher for younger individuals than it is for others

At first, the data does not make sense when comparing the ‘Yes’ and ‘No’, however the model created the conditional probability for each feature separately; not comparing it to the total distribution:

P(Female | no default) P(Male | no default) P(Female | default) P(Male | default)

Run model on first row of test data:

predict(nbDem, test[1,])
[1] No
Levels: No Yes

Run model on second row of test data:

predict(nbDem, test[2,])
[1] No
Levels: No Yes
The predictions are correct for both as probability of defaulting is low for both individuals for each conditional (except for the sex conditional which slightly higher to default for women). Looking at the overall probability of not defaulting is skewed to not defaulting by 78%, so the predictions are correct.

B.2. Naive Bayes using Demographic Variables

nbPay <- naiveBayes(default.payment.next.month ~ PAY_0 + PAY_2 + PAY_3, train)
nbPay

Naive Bayes Classifier for Discrete Predictors

Call:
naiveBayes.default(x = X, y = Y, laplace = laplace)

A-priori probabilities:
Y
   No   Yes 
0.764 0.236 

Conditional probabilities:
     PAY_0
Y         Pay duly          1mo         2mos         3mos         4mos         5mos         6mos         7mos         8mos         9mos        10mos        11mos
  No  0.1005235602 0.1965968586 0.5599476440 0.1036649215 0.0345549738 0.0036649215 0.0002617801 0.0002617801 0.0000000000 0.0002617801 0.0002617801 0.0000000000
  Yes 0.0491525424 0.1457627119 0.3101694915 0.1872881356 0.2593220339 0.0381355932 0.0067796610 0.0016949153 0.0000000000 0.0000000000 0.0016949153 0.0000000000

     PAY_2
Y         Pay duly          1mo         2mos         3mos         4mos         5mos         6mos         7mos         8mos         9mos        10mos        11mos
  No  0.1311518325 0.2172774869 0.5657068063 0.0002617801 0.0798429319 0.0036649215 0.0013089005 0.0000000000 0.0005235602 0.0002617801 0.0000000000 0.0000000000
  Yes 0.0949152542 0.1415254237 0.3991525424 0.0016949153 0.3313559322 0.0220338983 0.0059322034 0.0008474576 0.0008474576 0.0016949153 0.0000000000 0.0000000000

     PAY_3
Y         Pay duly          1mo         2mos         3mos         4mos         5mos         6mos         7mos         8mos         9mos        10mos        11mos
  No  0.1350785340 0.2185863874 0.5541884817 0.0000000000 0.0861256545 0.0031413613 0.0013089005 0.0005235602 0.0002617801 0.0005235602 0.0002617801 0.0000000000
  Yes 0.1161016949 0.1313559322 0.4228813559 0.0008474576 0.3000000000 0.0203389831 0.0025423729 0.0016949153 0.0025423729 0.0016949153 0.0000000000 0.0000000000
For ‘Pay duly’ and payments delay for 1 or 2 month, the probability of defaulting is higher than not defaulting. However, this switches after 3 or more months for each pay statement (variables). If the client has more than 3 months of late payments, there’s a probability of defaulting again.

Run model on first row of test data:

predict(nbPay, test[1,])
[1] No
Levels: No Yes

Run model on second row of test data:

predict(nbPay, test[2,])
[1] No
Levels: No Yes

Analysis: The first individual had 2 months of delayed payments, however he made payment to avoid being delayed 3 months. He is was marked as ‘Pay duly’, which corresponds with the prediction.

The second individuals was always on-time with her payments and she continued to be on-time, which corresponds with the prediction.

After reviewing both individuals, the modal has proven to predicted correctly.

B.3. Smoothed Naive Bayes using Payment Status

nbPay <- naiveBayes(default.payment.next.month ~ PAY_0 + PAY_2 + PAY_3, train, laplace=1.5)
predict(nbPay, test[1,])
[1] No
Levels: No Yes
nbPay <- naiveBayes(default.payment.next.month ~ PAY_0 + PAY_2 + PAY_3, train, laplace=1.5)
predict(nbPay, test[2,])
[1] No
Levels: No Yes
Laplace smoothing did not produce any differences within the predictions for both individuals.

Classification with Decision Tree

C.1. Basic Decision Tree

library("rpart")
library("rpart.plot")
dtPay <- rpart(default.payment.next.month ~ PAY_0 + PAY_2 + PAY_3,
            method="class",
            data=train, parms=list(split='information'), 
            minsplit=20, cp=0.02)
rpart.plot(dtPay, type=4, extra=1)

Analysis: The decision tree visualization provides a decision stump (one-level decision tree). A decision stump makes a prediction based on the value of just a single input feature. There is additional variables to consider that will improve this decision tree.

Run model on first row of test data:

predict(nbPay, test[1,])
[1] No
Levels: No Yes

Run model on second row of test data:

predict(nbPay, test[2,])
[1] No
Levels: No Yes
Analysis: Both individuals are unlikely to default. After reviewing the actual results, the predictions are correct that both individuals did not default. The probability of not defaulting is 78%.

C.2. Decision Tree with a Different Complexity Parameter

dtPay <- rpart(default.payment.next.month ~ PAY_0 + PAY_2 + PAY_3,
            method="class",
            data=train, parms=list(split='information'), 
            minsplit=20, cp=0.001)
rpart.plot(dtPay, type=4, extra=1)

Analysis: This decision tree shows all the possible branches and likelihood of path based upon the sample size of 5,000 individuals.

The left side represent individuals that did not default; right side represent those that defaulted.

Following to the root of the tree, only 3,476 (70%) of the 5,000 population did not defaulted consecutively.

Run model on first row of test data:

predict(nbPay, test[1,])
[1] No
Levels: No Yes

Run model on second row of test data:

predict(nbPay, test[2,])
[1] No
Levels: No Yes

Analysis: Both individuals are unlikely to have default.

Conclusion

Both models predictions were correct. However, I feel there was a large population of “No defaults” which made it feel too obvious too predict.

As for the two models, I prefer to utilize the Naive Bayes model as the data was easily to understand and read from this workbook. With the Decision Tree, I had to perform several methods to zoom into the decision tree to understand how to use it.

Issues with the data. So, there is no clear understanding of -2 and 0. I have read the threads on Kaggle, however the suggested commentary did not aligned with the data points. Also, how can 110 individuals (for example: 46, 275, 2298, 2538, 3151) with ZERO for all billing statements and marked as ‘default’ for next month? It does not make sense.

LS0tDQp0aXRsZTogIklORk8gNjU5IEFzc2lnbm1lbnQgIzIgKDEwIHBvaW50cykiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KPGgzPlZpY3RvciBFbmNoYXV0ZWd1aTwvaDM+DQo8aDQ+MTAvMjUvMjAyMDwvaDQ+DQo8YnI+DQo8aDI+PGI+RGF0YSBQcmVwYXJhdGlvbjwvYj48L2gyPg0KPGgzPjxiPkEuMS4gRGF0YSBMb2FkaW5nIGFuZCBJbml0aWFsIFRyYW5zZm9ybWF0aW9uPC9iPjwvaDM+DQpMb2FkIGRhdGEgYW5kIHRyYW5zZm9ybSB0aGUgZGVmYXVsdC5wYXltZW50Lm5leHQubW9udGggdmFyaWFibGUgaW50byBhIG5vbWluYWwgKGZhY3RvcikgdmFyaWFibGU6DQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmNjIDwtIHJlYWQuY3N2KCJkYXRhL1VDSV9DcmVkaXRfQ2FyZC5jc3YiKQ0KY2MkZGVmYXVsdC5wYXltZW50Lm5leHQubW9udGggPC0gZmFjdG9yKGNjJGRlZmF1bHQucGF5bWVudC5uZXh0Lm1vbnRoLGxldmVscz1jKDAsMSksIGxhYmVscz1jKCJObyIsIlllcyIpKQ0KDQpgYGANCjxoMz48Yj5BLjIuIERlbW9ncmFwaGljIFZhcmlhYmxlczwvYj48L2gzPg0KPGg0Pjx1PkVkdWNhdGlvbjwvdT48L2g0Pg0KYGBge3J9DQpnZ3Bsb3QoY2MsIGFlcyh4PUVEVUNBVElPTiwgZmlsbD1kZWZhdWx0LnBheW1lbnQubmV4dC5tb250aCwgY29sb3I9ZGVmYXVsdC5wYXltZW50Lm5leHQubW9udGgpKSArIA0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0xLCBwb3NpdGlvbj0ic3RhY2siKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiYmxhY2siLCJibGFjayIpKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJkYXJrb2xpdmVncmVlbjQiLCAicmVkIikpDQpgYGANCjxiPkFuYWx5c2lzOjwvYj4NCkVEVUNBVElPTjogKDE9Z3JhZHVhdGUgc2Nob29sLCAyPXVuaXZlcnNpdHksIDM9aGlnaCBzY2hvb2wsIDQ9b3RoZXJzLCA1PXVua25vd24sIDY9dW5rbm93bikuIE1ham9yaXR5IG9mIHRoZSBwb3B1bGF0aW9uIGhhcyBhbiB1bml2ZXJzaXR5IGVkdWNhdGlvbiBvciBncmVhdGVyLiBUaGlzIHZhcmlhYmxlIHdpbGwgaGVscCB3aXRoIG91ciBhbmFseXNpcyBhcyBpdCBzZWVtcyB0aGVyZSBpcyBhIHJlbGF0aW9uc2hpcCB3aXRoIHVuaXZlcnNpdHkgbGV2ZWwgZWR1Y2F0aW9uIGFuZCBkZWZhdWx0aW5nLiANCg0KPGJyPg0KPGg0Pjx1PkFnZTwvdT48L2g0Pg0KYGBge3J9DQpnZ3Bsb3QoY2MsIGFlcyh4PUFHRSwgZmlsbD1kZWZhdWx0LnBheW1lbnQubmV4dC5tb250aCwgY29sb3I9ZGVmYXVsdC5wYXltZW50Lm5leHQubW9udGgpKSArIA0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0xLCBwb3NpdGlvbj0ic3RhY2siKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiYmxhY2siLCJibGFjayIpKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJkYXJrb2xpdmVncmVlbjQiLCAicmVkIikpDQpgYGANCjxiPkFuYWx5c2lzOjwvYj4NCkFHRTogQWdlIGluIHllYXJzLiBUaGVyZSBzZWVtcyB0byBiZSBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB5b3VuZ2VyIGFnZSBncm91cCBhbmQgZGVmYXVsdGluZywgaG93ZXZlciB0aGlzIGNhbiBiZSBkdWUgdG8gdGhlIGZhY3QgdGhlIHBvcHVsYXRpb24gc2FtcGxlIGlzIGhlYXZ5IG9uIHRoZSBsZWZ0Lg0KDQo8YnI+DQo8aDM+PGI+QS4zLiBQYXltZW50IFN0YXR1cyBWYXJpYWJsZXM8L2I+PC9oMz4NCjxoND48dT5QQVlfMDwvdT48L2g0Pg0KYGBge3J9DQpnZ3Bsb3QoY2MsIGFlcyh4PVBBWV8wLCBmaWxsPWRlZmF1bHQucGF5bWVudC5uZXh0Lm1vbnRoLCBjb2xvcj1kZWZhdWx0LnBheW1lbnQubmV4dC5tb250aCkpICsgDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTEsIHBvc2l0aW9uPSJzdGFjayIpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJibGFjayIsImJsYWNrIikpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoImRhcmtvbGl2ZWdyZWVuNCIsICJyZWQiKSkNCmBgYA0KPGI+QW5hbHlzaXM6PC9iPg0KUEFZXzA6IFJlcGF5bWVudCBzdGF0dXMgaW4gU2VwdGVtYmVyLCAyMDA1ICgtMT1wYXkgZHVseSwgMT1wYXltZW50IGRlbGF5IGZvciBvbmUgbW9udGgsIDI9cGF5bWVudCBkZWxheSBmb3IgdHdvIG1vbnRocywg4oCmIDg9cGF5bWVudCBkZWxheSBmb3IgZWlnaHQgbW9udGhzLCA5PXBheW1lbnQgZGVsYXkgZm9yIG5pbmUgbW9udGhzIGFuZCBhYm92ZSkuIEhvd2V2ZXIsIHRoZXJlIGFyZSB0d28gdW5uYW1lZCBsYWJlbHMgdGhhdCBhcmUgcmVwcmVzZW50ZWQgd2l0aGluIHRoZSBkYXRhLCB3aGljaCBhcmUgLTIgYW5kIDAuIElmIC0xID0gJ3BhaWQgb24tdGltZScsIHdoYXQgMCByZXByZXNlbnQ/IEkgcmVhZCB0d28gdGhyZWFkIG9uIEthZ2dsZSBmb3IgdGhpcyBpc3N1ZSwgYW5kIGl0IGNvbnRpbnVlcyB0byBiZSB1bmNsZWFyIGFuZCB0aGUgZGF0YSBkaWRuJ3QgbWF0Y2ggdGhlIHJlc3BvbnNlcy4gIEZvciB0aGUgc2FrZSBvZiB0aGlzIGFuYWx5c2lzLCBJIHdpbGwgcmV0cm9maXQgaGF2aW5nIC0yIHN0YXJ0IGFzICJQYXkgZHVseSIuDQoNCkkgcGlja2VkIHRoaXMgcGF5IHN0YXRlbWVudCBhcyBpdCBpcyB0aGUgaW5pdGlhbCBzdGF0ZW1lbnQgYW5kIHRoZXJlIGlzIGEgZm9jdXNlZCBtZWRpYW4uIA0KPGJyPg0KPGg0Pjx1PlBBWV8zPC91PjwvaDQ+DQpgYGB7cn0NCmdncGxvdChjYywgYWVzKHg9UEFZXzMsIGZpbGw9ZGVmYXVsdC5wYXltZW50Lm5leHQubW9udGgsIGNvbG9yPWRlZmF1bHQucGF5bWVudC5uZXh0Lm1vbnRoKSkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MSwgcG9zaXRpb249InN0YWNrIikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImJsYWNrIiwiYmxhY2siKSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiZGFya29saXZlZ3JlZW40IiwgInJlZCIpKQ0KYGBgDQo8Yj5BbmFseXNpczo8L2I+DQpUaGVyZSBpcyBhIHNwaWtlIGluY3JlYXNlIGluIGxhYmVsIDAgb2YgZGVmYXVsdGVkIGNsYWltcy4gVGhpcyBtYXkgbWVhbiB0aGF0IHRoZSBwcm9iYWJpbGl0eSBvZiBoYXZpbmcgY29uc2VjdXRpdmUgJ25vIGRlZmF1bHQnIGRlY3JlYXNlcyBvdmVyIHRpbWUuDQo8YnI+DQo8aDQ+PHU+UEFZXzU8L3U+PC9oND4NCmBgYHtyfQ0KZ2dwbG90KGNjLCBhZXMoeD1QQVlfNSwgZmlsbD1kZWZhdWx0LnBheW1lbnQubmV4dC5tb250aCwgY29sb3I9ZGVmYXVsdC5wYXltZW50Lm5leHQubW9udGgpKSArIA0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0xLCBwb3NpdGlvbj0ic3RhY2siKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiYmxhY2siLCJibGFjayIpKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJwaW5rIiwgInJlZCIpKQ0KYGBgDQo8Yj5BbmFseXNpczo8L2I+DQpTaW1pbGFyIGFuYWx5c2lzIGFzIFBBWV8zLCBob3dldmVyIHRoZXJlIGlzIGFuIGluY3JlYXNlIGluIC0yLCAtMSwgMCBhbmQgYSBkZWNyZWFzZSBpbiAyLg0KDQo8YnI+DQoNCjxoMz48Yj5BLjQuIFRyYW5zZm9ybWluZyBOb21pbmFsIFZhcmlhYmxlczwvYj48L2gzPg0KPGg0PlRyYW5zZm9ybSByZWxhdGVkIGRlbW9ncmFwaGljIHZhcmlhYmxlcyBpbnRvIG5vbWluYWwgdmFsdWVzIHdpdGggcHJvcGVyIGxhYmVsczo8L2g0Pg0KYGBge3J9DQpjYyRBR0UgPC0gY3V0KGNjJEFHRSxicmVha3M9YygwLDIwLDMwLDQwLDUwLDYwLDcwLDgwLDEwMCksIGxhYmVscz1jKCI8MjBzIiwiMjBzIiwgIjMwcyIsICI0MHMiLCAiNTBzIiwgIjYwcyIsICI3MHMiLCAiODBzKyIpKSANCmBgYA0KYGBge3J9DQpjYyRBR0UgPC0gZmFjdG9yKGNjJEFHRSkgDQpgYGANCmBgYHtyfQ0KY2MkRURVQ0FUSU9OIDwtIGZhY3RvcihjYyRFRFVDQVRJT04sbGV2ZWxzPWMoMSwyLDMsNCw1LDYpLCBsYWJlbHM9YygiR3JhZCBTY2hvb2wiLCAiVW5pdmVyc2l0eSIsICJIaWdoIFNjaG9vbCIsICJPdGhlcnMiLCAiVW5rbm93biIsICJVbmtub3duIikpDQpgYGANCg0KVHJhbnNmb3JtZWQgdGhlIGFkZGl0aW9uYWwgdmFyaWFibGVzIHRvIGhlbHAgd2l0aCBzZWN0aW9uIEIuMiBvZiB0aGUgYXNzaWdubWVudDoNCmBgYHtyfQ0KY2MkUEFZXzAgIDwtIGZhY3RvcihjYyRQQVlfMCAsIGxldmVscz1jKC0yLC0xLDAsMSwyLDMsNCw1LDYsNyw4LDkpLCBsYWJlbHM9YygiUGF5IGR1bHkiLCAiMW1vIiwgIjJtb3MiLCAiM21vcyIsICI0bW9zIiwgICI1bW9zIiwgIjZtb3MiLCAiN21vcyIsICAiOG1vcyIsICI5bW9zIiwgIjEwbW9zIiwgIjExbW9zIikpDQpgYGANCmBgYHtyfQ0KY2MkUEFZXzIgIDwtIGZhY3RvcihjYyRQQVlfMiAsIGxldmVscz1jKC0yLC0xLDAsMSwyLDMsNCw1LDYsNyw4LDkpLCBsYWJlbHM9YygiUGF5IGR1bHkiLCAiMW1vIiwgIjJtb3MiLCAiM21vcyIsICI0bW9zIiwgICI1bW9zIiwgIjZtb3MiLCAiN21vcyIsICAiOG1vcyIsICI5bW9zIiwgIjEwbW9zIiwgIjExbW9zIikpDQpgYGANCmBgYHtyfQ0KY2MkUEFZXzMgIDwtIGZhY3RvcihjYyRQQVlfMyAsIGxldmVscz1jKC0yLC0xLDAsMSwyLDMsNCw1LDYsNyw4LDkpLCBsYWJlbHM9YygiUGF5IGR1bHkiLCAiMW1vIiwgIjJtb3MiLCAiM21vcyIsICI0bW9zIiwgICI1bW9zIiwgIjZtb3MiLCAiN21vcyIsICAiOG1vcyIsICI5bW9zIiwgIjEwbW9zIiwgIjExbW9zIikpDQpgYGANCjxicj4NCjxoND5WaWV3IEVkdWNhdGlvbjo8L2g0Pg0KYGBge3J9DQpWaWV3KGNjJEVEVUNBVElPTikgDQpgYGANCjxicj4NCjxoND5WaWV3IEFnZTo8L2g0Pg0KYGBge3J9DQpWaWV3KGNjJEFHRSkNCmBgYA0KPGJyPg0KDQo8aDM+PGI+QS41LiBTZWxlY3Rpb24gb2YgVHJhaW5pbmcgRGF0YTwvYj48L2gzPg0KYGBge3J9DQp0cmFpbiA8LSBjY1tzYW1wbGUobnJvdyhjYyksIDUwMDApLCBdIA0KYGBgDQo8aDQ+Q2hlY2sgdHJhaW4gZGF0YTo8L2g0Pg0KYGBge3J9DQpucm93KHRyYWluKQ0KYGBgDQoNCjxoMz48Yj5BLjYuIFNlbGVjdGlvbiBvZiBUZXN0aW5nIERhdGE8L2I+PC9oMz4NCmBgYHtyfQ0KdGVzdCA8LSBjY1tjKDE4LDM2MDApLF0NCnRlc3QNCmBgYA0KDQoNCjxoMj48Yj5EYXRhIENsYXNzaWZpY2F0aW9uPC9iPjwvaDI+DQo8aDM+PGI+Qi4xLiBOYWl2ZSBCYXllcyB1c2luZyBEZW1vZ3JhcGhpYyBWYXJpYWJsZXM8L2I+PC9oMz4NCmBgYHtyfQ0KbGlicmFyeShlMTA3MSkNCm5iRGVtIDwtIG5haXZlQmF5ZXMoZGVmYXVsdC5wYXltZW50Lm5leHQubW9udGggfiBTRVggKyBFRFVDQVRJT04gKyBBR0UsIHRyYWluKQ0KbmJEZW0NCmBgYA0KPGI+PHU+U0VYOjwvdT48L2I+IFRoZSBwcm9iYWJpbGl0eSBvZiBkZWZhdWx0aW5nIGlzIGhpZ2hlciBmb3Igd29tZW4gdGhhbiBpdCBpcyBmb3IgbWVuLiBIb3dldmVyIGl0IGRvZXMgbm90IG1ha2Ugc2Vuc2UgYXMgdGhlIHByb2JhYmlsaXR5IG9mIG5vdCBkZWZhdWx0aW5nIGlzIGhpZ2hlciBmb3Igd29tZW4gdGhhbiBpdCBpcyBmb3IgbWVuLiANCg0KPGI+PHU+RURVQ0FUSU9OOjwvdT48L2I+IFRoZSBwcm9iYWJpbGl0eSBvZiBkZWZhdWx0aW5nIGlzIGhpZ2hlciBmb3IgVW5pdmVyc2l0eSB0aGFuIGl0IGlzIGZvciBvdGhlcnMuIEhvd2V2ZXIgaXQgZG9lcyBub3QgbWFrZSBzZW5zZSBhcyB0aGUgcHJvYmFiaWxpdHkgb2Ygbm90IGRlZmF1bHRpbmcgaXMgaGlnaGVyIGZvciBVbml2ZXJzaXR5IHRoYW4gaXQgaXMgZm9yIG90aGVycyANCg0KPGI+PHU+QUdFOjwvdT48L2I+IFRoZSBwcm9iYWJpbGl0eSBvZiBkZWZhdWx0aW5nIGlzIGhpZ2hlciBmb3IgeW91bmdlciBpbmRpdmlkdWFscyB0aGFuIGl0IGlzIGZvciBvdGhlcnMuIEhvd2V2ZXIgaXQgZG9lcyBub3QgbWFrZSBzZW5zZSBhcyB0aGUgcHJvYmFiaWxpdHkgb2Ygbm90IGRlZmF1bHRpbmcgaXMgaGlnaGVyIGZvciB5b3VuZ2VyIGluZGl2aWR1YWxzIHRoYW4gaXQgaXMgZm9yIG90aGVycyANCg0KQXQgZmlyc3QsIHRoZSBkYXRhIGRvZXMgbm90IG1ha2Ugc2Vuc2Ugd2hlbiBjb21wYXJpbmcgdGhlICdZZXMnIGFuZCAnTm8nLCBob3dldmVyIHRoZSBtb2RlbCBjcmVhdGVkIHRoZSBjb25kaXRpb25hbCBwcm9iYWJpbGl0eSBmb3IgZWFjaCBmZWF0dXJlIHNlcGFyYXRlbHk7IG5vdCBjb21wYXJpbmcgaXQgdG8gdGhlIHRvdGFsIGRpc3RyaWJ1dGlvbjoNCg0KUChGZW1hbGUgfCBubyBkZWZhdWx0KSAgICAgUChNYWxlIHwgbm8gZGVmYXVsdCkgDQpQKEZlbWFsZSB8IGRlZmF1bHQpICAgICAgICBQKE1hbGUgfCBkZWZhdWx0KQ0KPGJyPg0KPGg0PjxiPlJ1biBtb2RlbCBvbiBmaXJzdCByb3cgb2YgdGVzdCBkYXRhOjwvYj48L2g0Pg0KYGBge3J9DQpwcmVkaWN0KG5iRGVtLCB0ZXN0WzEsXSkNCmBgYA0KPGJyPg0KPGg0PjxiPlJ1biBtb2RlbCBvbiBzZWNvbmQgcm93IG9mIHRlc3QgZGF0YTo8L2I+PC9oND4NCmBgYHtyfQ0KcHJlZGljdChuYkRlbSwgdGVzdFsyLF0pDQpgYGANClRoZSBwcmVkaWN0aW9ucyBhcmUgY29ycmVjdCBmb3IgYm90aCBhcyBwcm9iYWJpbGl0eSBvZiBkZWZhdWx0aW5nIGlzIGxvdyBmb3IgYm90aCBpbmRpdmlkdWFscyBmb3IgZWFjaCBjb25kaXRpb25hbCAoZXhjZXB0IGZvciB0aGUgc2V4IGNvbmRpdGlvbmFsIHdoaWNoIHNsaWdodGx5IGhpZ2hlciB0byBkZWZhdWx0IGZvciB3b21lbikuIExvb2tpbmcgYXQgdGhlIG92ZXJhbGwgcHJvYmFiaWxpdHkgb2Ygbm90IGRlZmF1bHRpbmcgaXMgc2tld2VkIHRvIG5vdCBkZWZhdWx0aW5nIGJ5IDc4JSwgc28gdGhlIHByZWRpY3Rpb25zIGFyZSBjb3JyZWN0Lg0KPGJyPg0KPGgzPjxiPkIuMi4gTmFpdmUgQmF5ZXMgdXNpbmcgRGVtb2dyYXBoaWMgVmFyaWFibGVzPC9iPjwvaDM+DQpgYGB7cn0NCm5iUGF5IDwtIG5haXZlQmF5ZXMoZGVmYXVsdC5wYXltZW50Lm5leHQubW9udGggfiBQQVlfMCArIFBBWV8yICsgUEFZXzMsIHRyYWluKQ0KbmJQYXkNCmBgYA0KRm9yICdQYXkgZHVseScgYW5kIHBheW1lbnRzIGRlbGF5IGZvciAxIG9yIDIgbW9udGgsIHRoZSBwcm9iYWJpbGl0eSBvZiBkZWZhdWx0aW5nIGlzIGhpZ2hlciB0aGFuIG5vdCBkZWZhdWx0aW5nLiBIb3dldmVyLCB0aGlzIHN3aXRjaGVzIGFmdGVyIDMgb3IgbW9yZSBtb250aHMgZm9yIGVhY2ggcGF5IHN0YXRlbWVudCAodmFyaWFibGVzKS4gSWYgdGhlIGNsaWVudCBoYXMgbW9yZSB0aGFuIDMgbW9udGhzIG9mIGxhdGUgcGF5bWVudHMsIHRoZXJlJ3MgYSBwcm9iYWJpbGl0eSBvZiBkZWZhdWx0aW5nIGFnYWluLg0KPGJyPg0KPGg0PjxiPlJ1biBtb2RlbCBvbiBmaXJzdCByb3cgb2YgdGVzdCBkYXRhOjwvYj48L2g0Pg0KYGBge3J9DQpwcmVkaWN0KG5iUGF5LCB0ZXN0WzEsXSkNCmBgYA0KPGg0PjxiPlJ1biBtb2RlbCBvbiBzZWNvbmQgcm93IG9mIHRlc3QgZGF0YTo8L2I+PC9oND4NCmBgYHtyfQ0KcHJlZGljdChuYlBheSwgdGVzdFsyLF0pDQpgYGANCjxiPkFuYWx5c2lzOjwvYj4NClRoZSBmaXJzdCBpbmRpdmlkdWFsIGhhZCAyIG1vbnRocyBvZiBkZWxheWVkIHBheW1lbnRzLCBob3dldmVyIGhlIG1hZGUgcGF5bWVudCB0byBhdm9pZCBiZWluZyBkZWxheWVkIDMgbW9udGhzLiBIZSBpcyB3YXMgbWFya2VkIGFzICdQYXkgZHVseScsIHdoaWNoIGNvcnJlc3BvbmRzIHdpdGggdGhlIHByZWRpY3Rpb24uDQoNClRoZSBzZWNvbmQgaW5kaXZpZHVhbHMgd2FzIGFsd2F5cyBvbi10aW1lIHdpdGggaGVyIHBheW1lbnRzIGFuZCBzaGUgY29udGludWVkIHRvIGJlIG9uLXRpbWUsIHdoaWNoIGNvcnJlc3BvbmRzIHdpdGggdGhlIHByZWRpY3Rpb24uDQoNCkFmdGVyIHJldmlld2luZyBib3RoIGluZGl2aWR1YWxzLCB0aGUgbW9kYWwgaGFzIHByb3ZlbiB0byBwcmVkaWN0ZWQgY29ycmVjdGx5LiANCjxicj4NCjxoMz48Yj5CLjMuIFNtb290aGVkIE5haXZlIEJheWVzIHVzaW5nIFBheW1lbnQgU3RhdHVzPC9iPjwvaDM+DQoNCmBgYHtyfQ0KbmJQYXkgPC0gbmFpdmVCYXllcyhkZWZhdWx0LnBheW1lbnQubmV4dC5tb250aCB+IFBBWV8wICsgUEFZXzIgKyBQQVlfMywgdHJhaW4sIGxhcGxhY2U9MS41KQ0KcHJlZGljdChuYlBheSwgdGVzdFsxLF0pDQpgYGANCmBgYHtyfQ0KbmJQYXkgPC0gbmFpdmVCYXllcyhkZWZhdWx0LnBheW1lbnQubmV4dC5tb250aCB+IFBBWV8wICsgUEFZXzIgKyBQQVlfMywgdHJhaW4sIGxhcGxhY2U9MS41KQ0KcHJlZGljdChuYlBheSwgdGVzdFsyLF0pDQpgYGANCkxhcGxhY2Ugc21vb3RoaW5nIGRpZCBub3QgcHJvZHVjZSBhbnkgZGlmZmVyZW5jZXMgd2l0aGluIHRoZSBwcmVkaWN0aW9ucyBmb3IgYm90aCBpbmRpdmlkdWFscy4NCjxoMj48Yj5DbGFzc2lmaWNhdGlvbiB3aXRoIERlY2lzaW9uIFRyZWU8L2I+PC9oMj4NCjxoMz48Yj5DLjEuIEJhc2ljIERlY2lzaW9uIFRyZWU8L2I+PC9oMz4NCmBgYHtyfQ0KbGlicmFyeSgicnBhcnQiKQ0KbGlicmFyeSgicnBhcnQucGxvdCIpDQpkdFBheSA8LSBycGFydChkZWZhdWx0LnBheW1lbnQubmV4dC5tb250aCB+IFBBWV8wICsgUEFZXzIgKyBQQVlfMywNCiAgICAgICAgICAgIG1ldGhvZD0iY2xhc3MiLA0KICAgICAgICAgICAgZGF0YT10cmFpbiwgcGFybXM9bGlzdChzcGxpdD0naW5mb3JtYXRpb24nKSwgDQogICAgICAgICAgICBtaW5zcGxpdD0yMCwgY3A9MC4wMikNCmBgYA0KYGBge3J9DQpycGFydC5wbG90KGR0UGF5LCB0eXBlPTQsIGV4dHJhPTEpDQpgYGANCjxiPkFuYWx5c2lzOjwvYj4NClRoZSBkZWNpc2lvbiB0cmVlIHZpc3VhbGl6YXRpb24gcHJvdmlkZXMgYSBkZWNpc2lvbiBzdHVtcCAob25lLWxldmVsIGRlY2lzaW9uIHRyZWUpLiBBIGRlY2lzaW9uIHN0dW1wIG1ha2VzIGEgcHJlZGljdGlvbiBiYXNlZCBvbiB0aGUgdmFsdWUgb2YganVzdCBhIHNpbmdsZSBpbnB1dCBmZWF0dXJlLiBUaGVyZSBpcyBhZGRpdGlvbmFsIHZhcmlhYmxlcyB0byBjb25zaWRlciB0aGF0IHdpbGwgaW1wcm92ZSB0aGlzIGRlY2lzaW9uIHRyZWUuDQo8YnI+DQo8aDQ+PGI+UnVuIG1vZGVsIG9uIGZpcnN0IHJvdyBvZiB0ZXN0IGRhdGE6PC9iPjwvaDQ+DQpgYGB7cn0NCnByZWRpY3QobmJQYXksIHRlc3RbMSxdKQ0KYGBgDQo8aDQ+PGI+UnVuIG1vZGVsIG9uIHNlY29uZCByb3cgb2YgdGVzdCBkYXRhOjwvYj48L2g0Pg0KYGBge3J9DQpwcmVkaWN0KG5iUGF5LCB0ZXN0WzIsXSkNCmBgYA0KPGI+QW5hbHlzaXM6PC9iPg0KQm90aCBpbmRpdmlkdWFscyBhcmUgdW5saWtlbHkgdG8gZGVmYXVsdC4gQWZ0ZXIgcmV2aWV3aW5nIHRoZSBhY3R1YWwgcmVzdWx0cywgdGhlIHByZWRpY3Rpb25zIGFyZSBjb3JyZWN0IHRoYXQgYm90aCBpbmRpdmlkdWFscyBkaWQgbm90IGRlZmF1bHQuIFRoZSBwcm9iYWJpbGl0eSBvZiBub3QgZGVmYXVsdGluZyBpcyA3OCUuIA0KPGJyPg0KPGgzPjxiPkMuMi4gRGVjaXNpb24gVHJlZSB3aXRoIGEgRGlmZmVyZW50IENvbXBsZXhpdHkgUGFyYW1ldGVyPC9iPjwvaDM+DQpgYGB7cn0NCmR0UGF5IDwtIHJwYXJ0KGRlZmF1bHQucGF5bWVudC5uZXh0Lm1vbnRoIH4gUEFZXzAgKyBQQVlfMiArIFBBWV8zLA0KICAgICAgICAgICAgbWV0aG9kPSJjbGFzcyIsDQogICAgICAgICAgICBkYXRhPXRyYWluLCBwYXJtcz1saXN0KHNwbGl0PSdpbmZvcm1hdGlvbicpLCANCiAgICAgICAgICAgIG1pbnNwbGl0PTIwLCBjcD0wLjAwMSkNCmBgYA0KDQpgYGB7cn0NCnJwYXJ0LnBsb3QoZHRQYXksIHR5cGU9NCwgZXh0cmE9MSkNCmBgYA0KPGI+QW5hbHlzaXM6PC9iPg0KVGhpcyBkZWNpc2lvbiB0cmVlIHNob3dzIGFsbCB0aGUgcG9zc2libGUgYnJhbmNoZXMgYW5kIGxpa2VsaWhvb2Qgb2YgcGF0aCBiYXNlZCB1cG9uIHRoZSBzYW1wbGUgc2l6ZSBvZiA1LDAwMCBpbmRpdmlkdWFscy4NCg0KVGhlIGxlZnQgc2lkZSByZXByZXNlbnQgaW5kaXZpZHVhbHMgdGhhdCBkaWQgbm90IGRlZmF1bHQ7IHJpZ2h0IHNpZGUgcmVwcmVzZW50IHRob3NlIHRoYXQgZGVmYXVsdGVkLg0KDQpGb2xsb3dpbmcgdG8gdGhlIHJvb3Qgb2YgdGhlIHRyZWUsIG9ubHkgMyw0NzYgKDcwJSkgb2YgdGhlIDUsMDAwIHBvcHVsYXRpb24gZGlkIG5vdCBkZWZhdWx0ZWQgY29uc2VjdXRpdmVseS4NCjxicj4NCjxoND48Yj5SdW4gbW9kZWwgb24gZmlyc3Qgcm93IG9mIHRlc3QgZGF0YTo8L2I+PC9oND4NCmBgYHtyfQ0KcHJlZGljdChuYlBheSwgdGVzdFsxLF0pDQpgYGANCjxoND48Yj5SdW4gbW9kZWwgb24gc2Vjb25kIHJvdyBvZiB0ZXN0IGRhdGE6PC9iPjwvaDQ+DQpgYGB7cn0NCnByZWRpY3QobmJQYXksIHRlc3RbMixdKQ0KYGBgDQo8Yj5BbmFseXNpczo8L2I+DQpCb3RoIGluZGl2aWR1YWxzIGFyZSB1bmxpa2VseSB0byBoYXZlIGRlZmF1bHQuDQoNCjxoMj48Yj5Db25jbHVzaW9uPC9iPjwvaDI+DQpCb3RoIG1vZGVscyBwcmVkaWN0aW9ucyB3ZXJlIGNvcnJlY3QuIEhvd2V2ZXIsIEkgZmVlbCB0aGVyZSB3YXMgYSBsYXJnZSBwb3B1bGF0aW9uIG9mICJObyBkZWZhdWx0cyIgd2hpY2ggbWFkZSBpdCBmZWVsIHRvbyBvYnZpb3VzIHRvbyBwcmVkaWN0Lg0KDQpBcyBmb3IgdGhlIHR3byBtb2RlbHMsIEkgcHJlZmVyIHRvIHV0aWxpemUgdGhlIE5haXZlIEJheWVzIG1vZGVsIGFzIHRoZSBkYXRhIHdhcyBlYXNpbHkgdG8gdW5kZXJzdGFuZCBhbmQgcmVhZCBmcm9tIHRoaXMgd29ya2Jvb2suIFdpdGggdGhlIERlY2lzaW9uIFRyZWUsIEkgaGFkIHRvIHBlcmZvcm0gc2V2ZXJhbCBtZXRob2RzIHRvIHpvb20gaW50byB0aGUgZGVjaXNpb24gdHJlZSB0byB1bmRlcnN0YW5kIGhvdyB0byB1c2UgaXQuDQoNCklzc3VlcyB3aXRoIHRoZSBkYXRhLiBTbywgdGhlcmUgaXMgbm8gY2xlYXIgdW5kZXJzdGFuZGluZyBvZiAtMiBhbmQgMC4gSSBoYXZlIHJlYWQgdGhlIHRocmVhZHMgb24gS2FnZ2xlLCBob3dldmVyIHRoZSBzdWdnZXN0ZWQgY29tbWVudGFyeSBkaWQgbm90IGFsaWduZWQgd2l0aCB0aGUgZGF0YSBwb2ludHMuIEFsc28sIGhvdyBjYW4gMTEwIGluZGl2aWR1YWxzIChmb3IgZXhhbXBsZTogNDYsIDI3NSwgMjI5OCwgMjUzOCwgMzE1MSkgd2l0aCBaRVJPIGZvciBhbGwgYmlsbGluZyBzdGF0ZW1lbnRzIGFuZCBtYXJrZWQgYXMgJ2RlZmF1bHQnIGZvciBuZXh0IG1vbnRoPyBJdCBkb2VzIG5vdCBtYWtlIHNlbnNlLiANCg==