Introduction
In the current world of high power data analytics and breadth of
borrower data available for utilization, ensuring efficient borrower
population segmentation and classification is central to the operating
processes of any effective lending institution. Financial institutions
and credit agencies must prioritize creation of accurate and detailed
borrower profiles to accurately predict borrower outcome to ensure
timely and complete repayment of loans as well as prevent fraudulent
borrowing that will result in a complete financial loss for the
institution.
Through detailed customer segmentation lending institutions and
credit agencies can streamline and improve their repayment rates and
overall profit by:
- Identifying borrower populations with greater repayment probability
for targeted reduced APR loan marketing
- Improving prediction accuracy to identify which customers are likely
to go into default and provide preemptive mitigation strategies such as
refinancing or adjusted repayment plans
- Develop better live spending limit adjustments to support borrower
needs and repayment capabilities
- Improve fraud detection to reduce the number of fraudulent
application approvals
Loan default data was obtained from
Applied
Analytics through Case Studies Using SAS and R, Deepti Gupta for
analysis. Each of the below outlined variables as included in the data
set serves as a categorical data point captured to help classify
borrowers and predict their repayment ability.
Variables
Variable Name
|
Variable Type
|
Details
|
Loan Status
|
Categorical
|
Status of bank loan default (Default vs Current)
|
Checking Amount
|
Numeric
|
Amount in borrower’s checking account
|
Term
|
Numeric
|
Loan term in months
|
Credit Score
|
Numeric
|
Borrower’s credit score
|
Gender
|
Categorical
|
Borrower’s gender
|
Marital Status
|
Categorical
|
Borrower’s marital status
|
Employment Status
|
Categorical
|
Borrower’s employment status
|
Amount
|
Numeric
|
Loan amount
|
Saving Amount
|
Numeric
|
Ammount in borrower’s saving account
|
Age
|
Numeric
|
Duration of borrower’s employment in months
|
Number of Credit Accounts
|
Numeric
|
Number of credit accounts in borrower’s name
|
Car Loan
|
Categorical
|
If borrower holds a car loan
|
Personal Loan
|
Categorical
|
If borrower holds a personal loan
|
Home Loan
|
Categorical
|
If borrower holds a home loan
|
Education Loan
|
Categorical
|
If borrower holds an education loan
|
Any Loan
|
Categorical
|
A feature variable measuring how many of the 4 defined loans held by the
borrower (personal, home, education, or car)
|
Total Debt
|
Numeric
|
A feature variable measuring the total number of borrower’s debts (4
defined loans and Number of Credit Accounts)
|
raw.loan <- read.csv("https://nlepera.github.io/sta551/HW04/data/BankLoanDefaultDataset.csv")
raw.loan$Any_loan <- rowSums(raw.loan[c(7:10)] == 1)
raw.loan$Total_debt <- as.numeric(raw.loan$Any_loan) + as.numeric(raw.loan$No_of_credit_acc)
raw.loan$Loan_status <- factor(raw.loan$Default, levels = c(0:1), labels = c("Current","Default"))
raw.loan$Car_loan <- factor(raw.loan$Car_loan, levels = c(0:1), labels = c("No", "Yes"))
raw.loan$Personal_loan <- factor(raw.loan$Personal_loan, levels = c(0:1), labels = c("No", "Yes"))
raw.loan$Home_loan <- factor(raw.loan$Home_loan, levels = c(0:1), labels = c("No", "Yes"))
raw.loan$Education_loan <- factor(raw.loan$Education_loan, levels = c(0:1), labels = c("No", "Yes"))
EDA & Feature
Variable Generation
In order to more efficiently utilize this data for borrower analysis,
the number of variables needs to be reduced in a process called
dimension reduction. The dimensions of this data set were reduced by
first combining the variables Car Loan
,
Personal Loan
, Home Loan
,
Education Loan
and Number of Credit Accounts
to create a new variable entitled Total Debt
that captures
the total count of loans and credit accounts for each borrower. This
effectively reduces five variables into a single variable, with minimal
loss of information.
loan <- raw.loan[-1] %>%
relocate(Loan_status, .before = Checking_amount) %>%
relocate(Amount, .after = Loan_status) %>%
relocate(Term, .after = Amount) %>%
relocate(Total_debt, .after = Term) %>%
relocate(No_of_credit_acc, .before = Car_loan)
kable(head(loan), caption = "A breif glimpse at the borrower data as collected" ) %>%
kable_styling(full_width = FALSE) %>%
scroll_box(width ="100%")
A breif glimpse at the borrower data as collected
Loan_status
|
Amount
|
Term
|
Total_debt
|
Checking_amount
|
Credit_score
|
Gender
|
Marital_status
|
No_of_credit_acc
|
Car_loan
|
Personal_loan
|
Home_loan
|
Education_loan
|
Emp_status
|
Saving_amount
|
Emp_duration
|
Age
|
Any_loan
|
Current
|
1536
|
15
|
2
|
988
|
796
|
Female
|
Single
|
1
|
Yes
|
No
|
No
|
No
|
employed
|
3455
|
12
|
38
|
1
|
Current
|
947
|
15
|
2
|
458
|
813
|
Female
|
Single
|
1
|
Yes
|
No
|
No
|
No
|
employed
|
3600
|
25
|
36
|
1
|
Current
|
1678
|
14
|
2
|
158
|
756
|
Female
|
Single
|
1
|
No
|
Yes
|
No
|
No
|
employed
|
3093
|
43
|
34
|
1
|
Default
|
1804
|
25
|
2
|
300
|
737
|
Female
|
Single
|
1
|
No
|
No
|
No
|
Yes
|
employed
|
2449
|
0
|
29
|
1
|
Default
|
1184
|
24
|
2
|
63
|
662
|
Female
|
Single
|
1
|
No
|
No
|
No
|
Yes
|
unemployed
|
2867
|
4
|
30
|
1
|
Current
|
475
|
20
|
3
|
1071
|
828
|
Male
|
Married
|
2
|
Yes
|
No
|
No
|
No
|
employed
|
3282
|
12
|
32
|
1
|
The distribution of the Total Debt
feature variable is
included for review.
par(mfrow = c(1,2))
hist(x = loan$Total_debt,
prob = TRUE,
main = "Distribution of
Borrower Total Debt",
xlab = "Total Number of Debt Accounts")
plot(x = loan$Loan_status,
y = loan$Total_debt,
col = c("skyblue", "darkred"),
main = "Total Debt by Loan Status
(Current vs Default)",
xlab = "Borrower Loan Default Status
(Current vs Default)",
ylab = "Total Number of Borrower Debt Accounts")

K Means Cluster
Analysis (Full Data)
A rudimentary cluster analysis of the numeric variables (Loan status,
Amount, Term, Checking amount, Credit score, Saving amount, Employment
Duration, Age, Total debt) provides an initial glimpse at the borrower
profile data, and underscores the need for further dimension reduction
prior to cluster analysis.
When assessing fo the optional cluster number utilizing the
silhouette method, it demonstrated that the optimal number of clusters
is two. Such a small number of clusters indicates a poor cluster model
fit and lacks predictive power. As demonstrated by the significant
overlap in the two clusters, a high level cluster analysis of all
numeric borrower profile data does not provide proper borrower
segmentation for identification and loan default prediction.
Utilizing such a poorly fit model for various business purposes could
result in ineffective targeted marketing, poor pre-approval models
resulting in higher back end costs reviewing and rejecting poorly fit
applicants, and overall poor risk analysis in initial borrower
requests.
loan.cluster <- loan[, c(1:6, 15:17)]
loan.cluster$Loan_status <- as.numeric(loan.cluster$Loan_status)
loan.cluster.group <- kmeans(x = loan.cluster, centers = 2)
loan.clustID <- loan.cluster.group$cluster
plot1 <- fviz_nbclust(loan.cluster, FUN = hcut, method = "wss")
plot2 <- fviz_nbclust(loan.cluster, FUN = hcut, method = "silhouette")
gridExtra::grid.arrange(plot1,plot2)

clusplot(loan.cluster,
loan.clustID,
lines = 0,
shade = T,
color = T,
labels = 1,
plotchar = F,
span = T)

Heirarchal Data
Clustering - Agglomerative
The following variables were analyzed to determine the borrower’s
repayment profile:
- Amount in borrower’s saving account
Saving_amount
- Borrower’s credit score
Credit_score
- Loan amount
Amount
- Status of bank loan default (Default vs Current)
Loan_status
Utilizing a ‘bottom-up’ approach the data was split into clusters
starting with single data points and successively merging clusters. This
allows for tuning of the cluster sizes without pre-determining cluster
count. Therefore, overall borrower profiles can be created from the data
without pre-determining segments allowing in improved borrower profiling
and prediction.
All data was run both raw and scaled. Raw data did not present
significant skew, and scaled data presented over fitting issues
resulting in a best fit of two clusters. No missing values were
identified in the data set, therefore no imputation was required.
heirarch.loans <- loan[, c("Checking_amount", "Amount", "Emp_duration", "Loan_status")]
heirarch.distance <- dist(heirarch.loans, method = "euclidean")
hcluster1 <- hclust(heirarch.distance, method = "complete")
Determining Cluster
Count
By examining the current borrower data to determine both the number
and dimensions of the clusters, the borrower profile clusters can be
created to better align with the current borrower profile base. This
will allow for improved accuracy in borrower outcome prediction over the
life cycle of the loan. For example, as a loan progresses, if a borrower
originally profiled as a high likelihood of repayment suddenly has a dip
in employment duration (i.e. laid off), accurate and timely reassessment
of the borrower profile will allow for rapid revocation of any pending
lending offers or credit increases, as well as flag the borrower for
targeted repayment and or refinancing programs to reduce risk of loan
delinquency
plot3 <- fviz_nbclust(heirarch.loans, FUN = hcut, method = "wss")
plot4 <- fviz_nbclust(heirarch.loans, FUN = hcut, method = "silhouette")
gridExtra::grid.arrange(plot3, plot4)

Final Cluster
analysis
Optimal clustering was indicated as 3 clusters via the silhouette
method and was selected as the pre-defined number of clusters for
hierarchical agglomerate cluster analysis. Unlike the clustering as seen
in the initial k-means cluster analysis, by utilizing hierarchical
agglomerate clustering, the clusters are created before the best fit
number of clusters is selected. This unsupervised algorithm allows for
the clustering to be determined directly from the data itself rather
than being pre-assigned based on assumptions.
This hierarchical clustering is visually represented in the below
dendrogram. The clusters as selected are highlighted visually with
different color square overlays. Inside the bounds of each box are the
borrowers falling into that cluster. A scatterplot is also included to
demonstrate the mapping of these clusters as assigned by the hclust()
function.
hcluster2 <- hcluster1
hCluster_group <- cutree(hcluster2, k = 3)
heirarch.loans$hCluster_group = hCluster_group
plot(hcluster2, cex = 0.6, labels = FALSE, hang = -1, xlab = "", main = "Dendogram of Borrower Profile Clustering")
par(lwd = 3)
rect.hclust(hcluster1, k = 3, border = 2:9)

par(lwd=1)
clusplot(heirarch.loans,
hCluster_group,
lines = 0,
shade = T,
color = T,
labels = 1,
plotchar = F,
span = T,
main = "Cluster Plot of Raw Borrower Data")

Principal Component
Analysis (PCA) & Reducing dimensions
To reduce, the dimensions must be scaled. All data transformed via
scale()
function as log scaling not possible with
employment duration as some duration = 0 months. (“Checking_amount”,
“Amount”, “Emp_duration”, “Loan_status”)
pca.loans.scale <- heirarch.loans[,-5]
pca.loans.scale$Checking_amount <- scale(pca.loans.scale$Checking_amount)
pca.loans.scale$Amount <- scale(pca.loans.scale$Amount)
pca.loans.scale$Emp_duration <- scale(pca.loans.scale$Emp_duration)
pca.loans.scale$Loan_status <- scale(as.numeric(pca.loans.scale$Loan_status))
pca.loans <- prcomp(pca.loans.scale, center = TRUE, scale = TRUE)
kable(round(pca.loans$rotation, 2), caption = "Factor loading of Borrower Profile PCA") %>%
kable_styling()
Factor loading of Borrower Profile PCA
|
PC1
|
PC2
|
PC3
|
PC4
|
Checking_amount
|
-0.65
|
-0.01
|
0.31
|
-0.69
|
Amount
|
0.30
|
0.61
|
0.73
|
0.04
|
Emp_duration
|
-0.21
|
0.79
|
-0.57
|
-0.07
|
Loan_status
|
0.67
|
-0.03
|
-0.20
|
-0.72
|
kable(round(summary(pca.loans)$importance, 3), caption = "Importance of each componant of Borrower Profile PCA") %>%
kable_styling()
Importance of each componant of Borrower Profile PCA
|
PC1
|
PC2
|
PC3
|
PC4
|
Standard deviation
|
1.243
|
1.009
|
0.949
|
0.733
|
Proportion of Variance
|
0.386
|
0.254
|
0.225
|
0.134
|
Cumulative Proportion
|
0.386
|
0.641
|
0.866
|
1.000
|
As demonstrated by the above PCA tables, the first three principal
components account for 86.6% of the variation in the borrower profile
data. The equation for each principal component is included below:
\[PC_1= -0.65[Checking.amount] +
0.30[Loan.amount] - 0.21[Employment.duration] +
0.67[Loan.status]\] \[PC_2=
-0.01[Checking.amount] + 0.61[Loan.amount] + 0.79[Employment.duration] -
0.03[Loan.status]\] \[PC_3=
0.31[Checking.amount] + 0.73[Loan.amount] - 0.57[Employment.duration] -
0.20[Loan.status]\]
PC1 most impacted by checking amount and loan status with light
impact from loan amount and employment duration, we will call this
measure Immediate Financial Status Index PC2 most impacted by loan
amount and employment duration with near 0 impact from checking amount
and loan status, we will call this measure Repayment Security Index
PC3 will be dropped ass PC2 and PC3 have same largest influence
variables (loan amount and employment duration), but PC2 has a greater
proportion of variance than PC3.
Determne Best Number
of Clusters
The same process was utilized to determine the best fit number of
clusters for the PCA variables, again outlining 3 clusters as the best
fit. Despite this best fit silhouette prediction, four clusters were
utilized to reduce overall cluster overlap.
pca.cluster <- data.frame(Imed_fin_stat_PC1 = pca.loans$x[,1],
Repay_sec_PC2 = pca.loans$x[,2])
plot5 <- fviz_nbclust(pca.cluster, FUN = hcut, method = "wss")
plot6 <- fviz_nbclust(pca.cluster, FUN = hcut, method = "silhouette")
gridExtra::grid.arrange(plot5, plot6)

pca.distance <- dist(pca.cluster, method = "euclidean")
pca.clust <- hclust(pca.distance, method = "complete")
pca.cluster.group <- cutree(pca.clust, 4)
heirarch.loans$pcaCluster = as.character(pca.cluster.group)
clusplot(pca.cluster,
pca.cluster.group,
lines = 0,
shade = T,
color = T,
labels = 1,
xlab = "Immediate Financial Status Index",
ylab = "Repayment Security Index",
plotchar = F,
span = T,
main="Cluster Plot of PCA Components")

In reviewing the principal component variables are found to improve
borrower profile predictive capabilities through dimension reduction. By
combining four variables into two component analysis variables, the data
can be more easily manipulated for borrower outcome predictions at the
time of lending and increased targeted marketing for customers indicated
to have a strong financial status index and repayment security index.
This allows for borrower segmentation into four clusters:
- High immediate financial status & High Repayment Security
- High Immediate Financial Status & Low Repayment Security
- Low Immediate Financial Status & High Repayment Security
- Low Immediate Financial Status & Low Repayment Security
These four clusters that comprise the borrower profile can allow for
a targeted customer based approach. Those with high financial status and
high repayment security can be targeted for low interest rate loans with
a higher down payment marketing to entice further borrowing from a low
risk borrower. Where as those with low immediate financial status &
high repayment security can be targeted for micro to medium sized loans
with minimal to no down payments to entice further borrowing from a low
risk borrower that may not have the funds for a traditional high down
payment loan.
Outlier analysis
Outlier identification can provide further support in both effective
targeted marketing, appropriate borrower application denials, and
fraudulent borrower application identification. Outliers to the borrower
profile groups should be manually reviewed to determine if the borrower
is at risk for adjusting one of their principal component
classifications (ex: moving from high immediate financial status to low,
etc), if the borrower data is potentially fraudulent, or if the borrower
data indicates a borrower that requires potential account termination
and/or future borrower application denial for lack of repayment
ability.
Additionally, properly identifying outliers allows for removal of
outliers prior to PCA or hierarchical agglomerate clustering will result
in increased borrower repayment profile classification and improved
predictive capacity.
Local Outlier Factor
(LOF) Score
A local outlier factor (LOF) score is calculated utilizing the below
equation to compare a data point’s local reachability density (LRD) of
the nearest k neighbors to point \(A_i\) for \(i = 1
, 2, ... , n\).
\[
LOF(A_i) = \frac{\frac{\sum_{i = A_j \in N_kA_i}
{LRD_k(A_j)}}{||N_k(A_i)||}} {LRD_k(A_i)}
\]
The LOF factor acts as an easily filterable scale variable to quickly
identify outlier values. An LOF > 1 indicates a potential outlier,
with the greater values for LOF indiciating more extreme outliers.
lof.pca.cluster <- lof(pca.cluster, minPts = 50)
pca.cluster$LOF <- lof.pca.cluster
pander::pander(summary(lof.pca.cluster), caption = "Summary statistics of LOF scores for Immediate Financial Status Index & Repayment Security Index")
Summary statistics of LOF scores for Immediate Financial Status
Index & Repayment Security Index
0.9571 |
0.9939 |
1.017 |
1.064 |
1.079 |
2.315 |
Based on the summary statistics for the calculated LOF scores for the
PCA feature variables the outlier cutoff was selected as an LOF value of
1.8. This value was selected as the cutoff to ensure less than 1% of the
dataset is identified as an outlier, leading to an outlier flagging of 1
in every 100 borrowers. Selecting an LOF value of 1.7 resulted in >
1% outlier flagging. This 1% outlier flagging will scale well to the
lender or credit agency’s operational constraints until the need for
hyperparameter (k) tuning is required.
plot(x = pca.cluster$Imed_fin_stat_PC1,
y = pca.cluster$Repay_sec_PC2,
pch = "x",
cex = 0.5,
xlab = "Immediate Financial Status Index",
ylab = "Repayment Security Index",
main="Outlier Identification Amongst PCA Components")
points(pca.cluster,
cex = ((lof.pca.cluster - 1)*1.5),
pch = 21,
col = "hotpink")
text(pca.cluster[lof.pca.cluster > 1.8,],
labels = round(lof.pca.cluster, 1)[lof.pca.cluster >1.8],
pos = 2,
cex = 0.6,
col = "darkred")

kable(filter(pca.cluster, LOF > 1.8), caption = "listing of all LOF scores > 1.8") %>%
kable_styling()
listing of all LOF scores > 1.8
Imed_fin_stat_PC1
|
Repay_sec_PC2
|
LOF
|
-2.481105
|
-2.2764407
|
2.009484
|
3.396802
|
0.6592382
|
1.882003
|
-3.034711
|
-0.0338831
|
1.888966
|
-2.282020
|
-2.8414007
|
2.315474
|
2.926977
|
2.3990462
|
1.973623
|
Conclusions
Overall proper borrower profile segmentation will allow for improved
loan default prediction models, improved identification of fraudulent
pre-approval applications, and improved targeted marketing to drive up
borrowing rates from borrowers with a high repayment profile. Overall
this borrower segmentation and classification may also be used for
predictive analysis regarding borrower pre-approval determinations.
Overall borrower population segmentation remains a highly effective tool
for managing and predicting overall loan outcomes.
LS0tDQp0aXRsZTogIkNhbiB5b3UgcGF5IG1lIGJhY2s/IDo8aW1nIHNyYz1cImh0dHBzOi8vbmxlcGVyYS5naXRodWIuaW8vc3RhNTUxL0hXMDEvaW1nL3Blbmd1aW5fY3V0ZS5wbmdcIiBzdHlsZT1cImZsb2F0OiByaWdodDsgd2lkdGg6IDEyJVwiLz4iDQpzdWJ0aXRsZTogIkxvYW4gRGVmYXVsdCBCb3Jyb3dlciBEYXRhIENsdXN0ZXJpbmcgJiBBbmFseXNpcyINCmF1dGhvcjoNCi0gbmFtZTogTmF0YWxpZSBMZVBlcmENCiAgYWZmaWxpYXRpb246IFdlc3QgQ2hlc3RlciBVbml2ZXJzaXR5IHwgU1RBNTUxIC0gSFcgMDQNCmRhdGU6ICIxMiBEZWMgMjAyNCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIHRvY19jb2xsYXBzZTogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlDQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgZmlnX2FsaWduOiBjZW50ZXINCiAgICBkZl9wcmludDoga2FibGUNCi0tLQ0KDQpgYGB7Y3NzLCBlY2hvID0gRkFMU0V9DQpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLw0KICBmb250LXdlaWdodDpib2xkOw0KICBjb2xvcjogZGFya21hZ2VudGEgOw0KfQ0KaDEuc3VidGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8NCiAgZm9udC13ZWlnaHQ6Ym9sZDsNCiAgY29sb3I6IGRhcmttYWdlbnRhIDsNCn0NCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgYXV0aG9ycyAgKi8NCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IG5hdnk7DQp9DQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8NCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IG5hdnk7DQp9DQpoMSB7IC8qIEhlYWRlciAxIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMSBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtd2VpZ2h0OmJvbGQ7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8NCiAgICBmb250LXdlaWdodDpib2xkOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDMgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXdlaWdodDpib2xkOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpib2R5IHsNCiAgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsNCn0NCg0KLmhpZ2hsaWdodG1lIHsgDQogIGJhY2tncm91bmQtY29sb3I6eWVsbG93OyANCn0NCg0KcCB7IA0KICBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyANCn0NCg0KaDUgew0KICBjb2xvcjogbmF2eTsNCn0NCg0KLmlmcmFtZSB7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCg0KYTpsaW5rIHsNCiAgY29sb3I6IGRhcmttYWdlbnRhOw0KfQ0KDQouZmlnbGFiZWwgew0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGNvbG9yOiBkYXJrc2xhdGVncmF5Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1zaXplOiAxODsNCn0NCg0KLnRkMSB7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQp0aCwgdGQgew0KICBib3JkZXItYm90dG9tOiAxcHggc29saWQgI2RkZDsNCiAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KdHI6aG92ZXIge2JhY2tncm91bmQtY29sb3I6IGNvcmFsO30NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmlmICghcmVxdWlyZSgiZHBseXIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImRwbHlyIikNCn0NCg0KaWYgKCFyZXF1aXJlKCJwbHlyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJwbHlyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInBseXIiKQ0KfQ0KDQppZiAoIXJlcXVpcmUoInN0cmluZ3IiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInN0cmluZ3IiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgic3RyaW5nciIpDQp9DQoNCmlmICghcmVxdWlyZSgicGxvdGx5IikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgicGxvdGx5IikNCn0NCg0KaWYgKCFyZXF1aXJlKCJwYW5kb2MiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInBhbmRvYyIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJwYW5kb2MiKQ0KfQ0KDQppZiAoIXJlcXVpcmUoImdyaWRFeHRyYSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ3JpZEV4dHJhIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdyaWRFeHRyYSIpDQp9DQoNCmlmICghcmVxdWlyZSgiZ3JpZCIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ3JpZCIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJncmlkIikNCn0NCmlmICghcmVxdWlyZSgicmFzdGVyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJyYXN0ZXIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgicmFzdGVyIikNCn0NCmlmICghcmVxdWlyZSgiZGJzY2FuIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJkYnNjYW4iKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZGJzY2FuIikNCn0NCmlmICghcmVxdWlyZSgicFJPQyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicFJPQyIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJwUk9DIikNCn0NCmlmICghcmVxdWlyZSgiZ2dyaWRnZXMiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdncmlkZ2VzIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdncmlkZ2VzIikNCn0NCmlmICghcmVxdWlyZSgia25pdHIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImtuaXRyIikNCn0NCmlmICghcmVxdWlyZSgiR0dhbGx5IikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiR0dhbGx5IikNCn0NCmlmICghcmVxdWlyZSgiZ2dwbG90MiIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnZ2xwb3QyIikNCn0NCmlmICghcmVxdWlyZSgiY2x1c3RlciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiY2x1c3RlciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJjbHVzdGVyIikNCn0NCmlmICghcmVxdWlyZSgia2FibGVFeHRyYSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygia2FibGVFeHRyYSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJrYWJsZUV4dHJhIikNCn0NCmlmICghcmVxdWlyZSgiZm9yY2F0cyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZm9yY2F0cyIsIGRlcGVuZGVuY2llcyA9IFRSVUUpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJmb3JjYXRzIikNCn0NCmlmICghcmVxdWlyZSgicnBhcnQiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInJwYXJ0IiwgZGVwZW5kZW5jaWVzID0gVFJVRSkgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInJwYXJ0IikNCn0NCmlmICghcmVxdWlyZSgicnBhcnQucGxvdCIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicnBhcnQucGxvdCIsIGRlcGVuZGVuY2llcyA9IFRSVUUpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJycGFydC5wbG90IikNCn0NCmlmICghcmVxdWlyZSgibWV0YW4iKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIm1ldGFuIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoIm1ldGFuIikNCn0NCiBpZiAoIXJlcXVpcmUoImZhY3RvZXh0cmEiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZmFjdG9leHRyYSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICBsaWJyYXJ5KCJmYWN0b2V4dHJhIikNCiB9DQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IFRSVUUsICAgDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQSwNCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ24gPSAnY2VudGVyJykNCg0Kb3B0aW9ucyhEVC5vcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gNSwgc2Nyb2xsWCA9IFRSVUUpKQ0KYGBgDQoNCg0KIyBJbnRyb2R1Y3Rpb24NCg0KSW4gdGhlIGN1cnJlbnQgd29ybGQgb2YgaGlnaCBwb3dlciBkYXRhIGFuYWx5dGljcyBhbmQgYnJlYWR0aCBvZiBib3Jyb3dlciBkYXRhIGF2YWlsYWJsZSBmb3IgdXRpbGl6YXRpb24sIGVuc3VyaW5nIGVmZmljaWVudCBib3Jyb3dlciBwb3B1bGF0aW9uIHNlZ21lbnRhdGlvbiBhbmQgY2xhc3NpZmljYXRpb24gaXMgY2VudHJhbCB0byB0aGUgb3BlcmF0aW5nIHByb2Nlc3NlcyBvZiBhbnkgZWZmZWN0aXZlIGxlbmRpbmcgaW5zdGl0dXRpb24uIEZpbmFuY2lhbCBpbnN0aXR1dGlvbnMgYW5kIGNyZWRpdCBhZ2VuY2llcyBtdXN0IHByaW9yaXRpemUgY3JlYXRpb24gb2YgYWNjdXJhdGUgYW5kIGRldGFpbGVkIGJvcnJvd2VyIHByb2ZpbGVzIHRvIGFjY3VyYXRlbHkgcHJlZGljdCBib3Jyb3dlciBvdXRjb21lIHRvIGVuc3VyZSB0aW1lbHkgYW5kIGNvbXBsZXRlIHJlcGF5bWVudCBvZiBsb2FucyBhcyB3ZWxsIGFzIHByZXZlbnQgZnJhdWR1bGVudCBib3Jyb3dpbmcgdGhhdCB3aWxsIHJlc3VsdCBpbiBhIGNvbXBsZXRlIGZpbmFuY2lhbCBsb3NzIGZvciB0aGUgaW5zdGl0dXRpb24uICAgDQoNCg0KVGhyb3VnaCBkZXRhaWxlZCBjdXN0b21lciBzZWdtZW50YXRpb24gbGVuZGluZyBpbnN0aXR1dGlvbnMgYW5kIGNyZWRpdCBhZ2VuY2llcyBjYW4gc3RyZWFtbGluZSBhbmQgaW1wcm92ZSB0aGVpciByZXBheW1lbnQgcmF0ZXMgYW5kIG92ZXJhbGwgcHJvZml0IGJ5Og0KDQogIC0gSWRlbnRpZnlpbmcgYm9ycm93ZXIgcG9wdWxhdGlvbnMgd2l0aCBncmVhdGVyIHJlcGF5bWVudCBwcm9iYWJpbGl0eSBmb3IgdGFyZ2V0ZWQgcmVkdWNlZCBBUFIgbG9hbiBtYXJrZXRpbmcNCiAgLSBJbXByb3ZpbmcgcHJlZGljdGlvbiBhY2N1cmFjeSB0byBpZGVudGlmeSB3aGljaCBjdXN0b21lcnMgYXJlIGxpa2VseSB0byBnbyBpbnRvIGRlZmF1bHQgYW5kIHByb3ZpZGUgcHJlZW1wdGl2ZSBtaXRpZ2F0aW9uIHN0cmF0ZWdpZXMgc3VjaCBhcyByZWZpbmFuY2luZyBvciBhZGp1c3RlZCByZXBheW1lbnQgcGxhbnMNCiAgLSBEZXZlbG9wIGJldHRlciBsaXZlIHNwZW5kaW5nIGxpbWl0IGFkanVzdG1lbnRzIHRvIHN1cHBvcnQgYm9ycm93ZXIgbmVlZHMgYW5kIHJlcGF5bWVudCBjYXBhYmlsaXRpZXMNCiAgLSBJbXByb3ZlIGZyYXVkIGRldGVjdGlvbiB0byByZWR1Y2UgdGhlIG51bWJlciBvZiBmcmF1ZHVsZW50IGFwcGxpY2F0aW9uIGFwcHJvdmFscw0KDQpMb2FuIGRlZmF1bHQgZGF0YSB3YXMgb2J0YWluZWQgZnJvbSA8YSBocmVmPSJodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9kYXRhc2V0cy9Mb2FuRGF0YTIvQmFua0xvYW5EZWZhdWx0RGF0YXNldC5jc3YiPkFwcGxpZWQgQW5hbHl0aWNzIHRocm91Z2ggQ2FzZSBTdHVkaWVzIFVzaW5nIFNBUyBhbmQgUiwgRGVlcHRpIEd1cHRhPC9hPiBmb3IgYW5hbHlzaXMuIEVhY2ggb2YgdGhlIGJlbG93IG91dGxpbmVkIHZhcmlhYmxlcyBhcyBpbmNsdWRlZCBpbiB0aGUgZGF0YSBzZXQgc2VydmVzIGFzIGEgY2F0ZWdvcmljYWwgZGF0YSBwb2ludCBjYXB0dXJlZCB0byBoZWxwIGNsYXNzaWZ5IGJvcnJvd2VycyBhbmQgcHJlZGljdCB0aGVpciByZXBheW1lbnQgYWJpbGl0eS4gDQoNCjxoNT5WYXJpYWJsZXM8L2g1Pg0KDQo8dGFibGUgc3R5bGU9IndpZHRoOjEwMCUiPg0KPHRoZWFkPjx0cj4NCjx0aD5WYXJpYWJsZSBOYW1lPC90aD4NCjx0aD5WYXJpYWJsZSBUeXBlPC90aD4NCjx0aD5EZXRhaWxzPC90aD4NCjwvdHI+PC90aGVhZD4NCjx0cj48dGQgY2xhc3MgPSAidGQxIj5Mb2FuIFN0YXR1czwvdGQ+PHRkPkNhdGVnb3JpY2FsPC90ZD48dGQ+U3RhdHVzIG9mIGJhbmsgbG9hbiBkZWZhdWx0IChEZWZhdWx0IHZzIEN1cnJlbnQpPC90ZD48L3RyPg0KPHRyPjx0ZCBjbGFzcyA9ICJ0ZDEiPkNoZWNraW5nIEFtb3VudDwvdGQ+PHRkPk51bWVyaWM8L3RkPjx0ZD5BbW91bnQgaW4gYm9ycm93ZXIncyBjaGVja2luZyBhY2NvdW50PC90ZD4NCjx0cj48dGQgY2xhc3MgPSAidGQxIj5UZXJtPC90ZD48dGQ+TnVtZXJpYzwvdGQ+PHRkPkxvYW4gdGVybSBpbiBtb250aHM8L3RkPjwvdHI+DQo8dHI+PHRkIGNsYXNzID0gInRkMSI+Q3JlZGl0IFNjb3JlPC90ZD48dGQ+TnVtZXJpYzwvdGQ+PHRkPkJvcnJvd2VyJ3MgY3JlZGl0IHNjb3JlPC90ZD48L3RyPg0KPHRyPjx0ZCBjbGFzcyA9ICJ0ZDEiPkdlbmRlcjwvdGQ+PHRkPkNhdGVnb3JpY2FsPC90ZD48dGQ+Qm9ycm93ZXIncyBnZW5kZXI8L3RkPjwvdHI+DQo8dHI+PHRkIGNsYXNzID0gInRkMSI+TWFyaXRhbCBTdGF0dXM8L3RkPjx0ZD5DYXRlZ29yaWNhbDwvdGQ+PHRkPkJvcnJvd2VyJ3MgbWFyaXRhbCBzdGF0dXM8L3RkPjwvdHI+DQo8dHI+PHRkIGNsYXNzID0gInRkMSI+RW1wbG95bWVudCBTdGF0dXM8L3RkPjx0ZD5DYXRlZ29yaWNhbDwvdGQ+PHRkPkJvcnJvd2VyJ3MgZW1wbG95bWVudCBzdGF0dXM8L3RkPjwvdHI+DQo8dHI+PHRkIGNsYXNzID0gInRkMSI+QW1vdW50PC90ZD48dGQ+TnVtZXJpYzwvdGQ+PHRkPkxvYW4gYW1vdW50PC90ZD48L3RyPg0KPHRyPjx0ZCBjbGFzcyA9ICJ0ZDEiPlNhdmluZyBBbW91bnQ8L3RkPjx0ZD5OdW1lcmljPC90ZD48dGQ+QW1tb3VudCBpbiBib3Jyb3dlcidzIHNhdmluZyBhY2NvdW50PC90ZD48L3RyPg0KPHRyPjx0ZCBjbGFzcyA9ICJ0ZDEiPkFnZTwvdGQ+PHRkPk51bWVyaWM8L3RkPjx0ZD5EdXJhdGlvbiBvZiBib3Jyb3dlcidzIGVtcGxveW1lbnQgaW4gbW9udGhzPC90ZD48L3RyPg0KPHRyPjx0ZCBjbGFzcyA9ICJ0ZDEiPk51bWJlciBvZiBDcmVkaXQgQWNjb3VudHM8L3RkPjx0ZD5OdW1lcmljPC90ZD48dGQ+TnVtYmVyIG9mIGNyZWRpdCBhY2NvdW50cyBpbiBib3Jyb3dlcidzIG5hbWU8L3RkPjwvdHI+DQo8dHI+PHRkIGNsYXNzID0gInRkMSI+Q2FyIExvYW48L3RkPjx0ZD5DYXRlZ29yaWNhbDwvdGQ+PHRkPklmIGJvcnJvd2VyIGhvbGRzIGEgY2FyIGxvYW48L3RkPjwvdHI+DQo8dHI+PHRkIGNsYXNzID0gInRkMSI+UGVyc29uYWwgTG9hbjwvdGQ+PHRkPkNhdGVnb3JpY2FsPC90ZD48dGQ+SWYgYm9ycm93ZXIgaG9sZHMgYSBwZXJzb25hbCBsb2FuPC90ZD48L3RyPg0KPHRyPjx0ZCBjbGFzcyA9ICJ0ZDEiPkhvbWUgTG9hbjwvdGQ+PHRkPkNhdGVnb3JpY2FsPC90ZD48dGQ+SWYgYm9ycm93ZXIgaG9sZHMgYSBob21lIGxvYW48L3RkPjwvdHI+DQo8dHI+PHRkIGNsYXNzID0gInRkMSI+RWR1Y2F0aW9uIExvYW48L3RkPjx0ZD5DYXRlZ29yaWNhbDwvdGQ+PHRkPklmIGJvcnJvd2VyIGhvbGRzIGFuIGVkdWNhdGlvbiBsb2FuPC90ZD48L3RyPg0KPHRyPjx0ZCBjbGFzcyA9ICJ0ZDEiPkFueSBMb2FuPC90ZD48dGQ+Q2F0ZWdvcmljYWw8L3RkPjx0ZD5BIGZlYXR1cmUgdmFyaWFibGUgbWVhc3VyaW5nIGhvdyBtYW55IG9mIHRoZSA0IGRlZmluZWQgbG9hbnMgaGVsZCBieSB0aGUgYm9ycm93ZXIgKHBlcnNvbmFsLCBob21lLCBlZHVjYXRpb24sIG9yIGNhcik8L3RkPjwvdHI+DQo8dHI+PHRkIGNsYXNzID0gInRkMSI+VG90YWwgRGVidDwvdGQ+PHRkPk51bWVyaWM8L3RkPjx0ZD5BIGZlYXR1cmUgdmFyaWFibGUgbWVhc3VyaW5nIHRoZSB0b3RhbCBudW1iZXIgb2YgYm9ycm93ZXIncyBkZWJ0cyAoNCBkZWZpbmVkIGxvYW5zIGFuZCBOdW1iZXIgb2YgQ3JlZGl0IEFjY291bnRzKTwvdGQ+PC90cj4NCjwvdGFibGU+DQoNCmBgYHtyfQ0KcmF3LmxvYW4gPC0gcmVhZC5jc3YoImh0dHBzOi8vbmxlcGVyYS5naXRodWIuaW8vc3RhNTUxL0hXMDQvZGF0YS9CYW5rTG9hbkRlZmF1bHREYXRhc2V0LmNzdiIpDQpyYXcubG9hbiRBbnlfbG9hbiA8LSByb3dTdW1zKHJhdy5sb2FuW2MoNzoxMCldID09IDEpDQpyYXcubG9hbiRUb3RhbF9kZWJ0IDwtIGFzLm51bWVyaWMocmF3LmxvYW4kQW55X2xvYW4pICsgYXMubnVtZXJpYyhyYXcubG9hbiROb19vZl9jcmVkaXRfYWNjKQ0KcmF3LmxvYW4kTG9hbl9zdGF0dXMgPC0gZmFjdG9yKHJhdy5sb2FuJERlZmF1bHQsIGxldmVscyA9IGMoMDoxKSwgbGFiZWxzID0gYygiQ3VycmVudCIsIkRlZmF1bHQiKSkNCnJhdy5sb2FuJENhcl9sb2FuIDwtIGZhY3RvcihyYXcubG9hbiRDYXJfbG9hbiwgbGV2ZWxzID0gYygwOjEpLCBsYWJlbHMgPSBjKCJObyIsICJZZXMiKSkNCnJhdy5sb2FuJFBlcnNvbmFsX2xvYW4gPC0gZmFjdG9yKHJhdy5sb2FuJFBlcnNvbmFsX2xvYW4sIGxldmVscyA9IGMoMDoxKSwgbGFiZWxzID0gYygiTm8iLCAiWWVzIikpDQpyYXcubG9hbiRIb21lX2xvYW4gPC0gZmFjdG9yKHJhdy5sb2FuJEhvbWVfbG9hbiwgbGV2ZWxzID0gYygwOjEpLCBsYWJlbHMgPSBjKCJObyIsICJZZXMiKSkNCnJhdy5sb2FuJEVkdWNhdGlvbl9sb2FuIDwtIGZhY3RvcihyYXcubG9hbiRFZHVjYXRpb25fbG9hbiwgbGV2ZWxzID0gYygwOjEpLCBsYWJlbHMgPSBjKCJObyIsICJZZXMiKSkNCg0KYGBgDQoNCiMjIEVEQSAmIEZlYXR1cmUgVmFyaWFibGUgR2VuZXJhdGlvbg0KDQpJbiBvcmRlciB0byBtb3JlIGVmZmljaWVudGx5IHV0aWxpemUgdGhpcyBkYXRhIGZvciBib3Jyb3dlciBhbmFseXNpcywgdGhlIG51bWJlciBvZiB2YXJpYWJsZXMgbmVlZHMgdG8gYmUgcmVkdWNlZCBpbiBhIHByb2Nlc3MgY2FsbGVkIGRpbWVuc2lvbiByZWR1Y3Rpb24uICBUaGUgZGltZW5zaW9ucyBvZiB0aGlzIGRhdGEgc2V0IHdlcmUgcmVkdWNlZCBieSBmaXJzdCBjb21iaW5pbmcgdGhlIHZhcmlhYmxlcyBgQ2FyIExvYW5gLCBgUGVyc29uYWwgTG9hbmAsIGBIb21lIExvYW5gLCBgRWR1Y2F0aW9uIExvYW5gIGFuZCBgTnVtYmVyIG9mIENyZWRpdCBBY2NvdW50c2AgdG8gY3JlYXRlIGEgbmV3IHZhcmlhYmxlIGVudGl0bGVkIGBUb3RhbCBEZWJ0YCB0aGF0IGNhcHR1cmVzIHRoZSB0b3RhbCBjb3VudCBvZiBsb2FucyBhbmQgY3JlZGl0IGFjY291bnRzIGZvciBlYWNoIGJvcnJvd2VyLiAgVGhpcyBlZmZlY3RpdmVseSByZWR1Y2VzIGZpdmUgdmFyaWFibGVzIGludG8gYSBzaW5nbGUgdmFyaWFibGUsIHdpdGggbWluaW1hbCBsb3NzIG9mIGluZm9ybWF0aW9uLiANCg0KYGBge3J9DQpsb2FuIDwtIHJhdy5sb2FuWy0xXSAlPiUgDQogIHJlbG9jYXRlKExvYW5fc3RhdHVzLCAuYmVmb3JlID0gQ2hlY2tpbmdfYW1vdW50KSAlPiUNCiAgcmVsb2NhdGUoQW1vdW50LCAuYWZ0ZXIgPSBMb2FuX3N0YXR1cykgJT4lIA0KICByZWxvY2F0ZShUZXJtLCAuYWZ0ZXIgPSBBbW91bnQpICU+JSANCiAgcmVsb2NhdGUoVG90YWxfZGVidCwgLmFmdGVyID0gVGVybSkgJT4lIA0KICByZWxvY2F0ZShOb19vZl9jcmVkaXRfYWNjLCAuYmVmb3JlID0gQ2FyX2xvYW4pDQoNCmthYmxlKGhlYWQobG9hbiksIGNhcHRpb24gPSAiQSBicmVpZiBnbGltcHNlIGF0IHRoZSBib3Jyb3dlciBkYXRhIGFzIGNvbGxlY3RlZCIgKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFKSAlPiUgDQogIHNjcm9sbF9ib3god2lkdGggPSIxMDAlIikNCmBgYA0KDQoNClRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGBUb3RhbCBEZWJ0YCBmZWF0dXJlIHZhcmlhYmxlIGlzIGluY2x1ZGVkIGZvciByZXZpZXcuDQoNCmBgYHtyLCBmaWcud2lkdGg9OX0NCg0KcGFyKG1mcm93ID0gYygxLDIpKQ0KaGlzdCh4ID0gbG9hbiRUb3RhbF9kZWJ0LCANCiAgICAgcHJvYiA9IFRSVUUsDQogICAgIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIA0KQm9ycm93ZXIgVG90YWwgRGVidCIsDQogICAgIHhsYWIgPSAiVG90YWwgTnVtYmVyIG9mIERlYnQgQWNjb3VudHMiKQ0KDQpwbG90KHggPSBsb2FuJExvYW5fc3RhdHVzLA0KICAgICB5ID0gbG9hbiRUb3RhbF9kZWJ0LA0KICAgICBjb2wgPSBjKCJza3libHVlIiwgImRhcmtyZWQiKSwNCiAgICAgbWFpbiA9ICJUb3RhbCBEZWJ0IGJ5IExvYW4gU3RhdHVzDQooQ3VycmVudCB2cyBEZWZhdWx0KSIsDQogICAgIHhsYWIgPSAiQm9ycm93ZXIgTG9hbiBEZWZhdWx0IFN0YXR1cw0KKEN1cnJlbnQgdnMgRGVmYXVsdCkiLA0KICAgICB5bGFiID0gIlRvdGFsIE51bWJlciBvZiBCb3Jyb3dlciBEZWJ0IEFjY291bnRzIikNCmBgYA0KDQoNCg0KDQojIEsgTWVhbnMgQ2x1c3RlciBBbmFseXNpcyAoRnVsbCBEYXRhKQ0KDQpBIHJ1ZGltZW50YXJ5IGNsdXN0ZXIgYW5hbHlzaXMgb2YgdGhlIG51bWVyaWMgdmFyaWFibGVzIChMb2FuIHN0YXR1cywgQW1vdW50LCBUZXJtLCBDaGVja2luZyBhbW91bnQsIENyZWRpdCBzY29yZSwgU2F2aW5nIGFtb3VudCwgRW1wbG95bWVudCBEdXJhdGlvbiwgQWdlLCBUb3RhbCBkZWJ0KSBwcm92aWRlcyBhbiBpbml0aWFsIGdsaW1wc2UgYXQgdGhlIGJvcnJvd2VyIHByb2ZpbGUgZGF0YSwgYW5kIHVuZGVyc2NvcmVzIHRoZSBuZWVkIGZvciBmdXJ0aGVyIGRpbWVuc2lvbiByZWR1Y3Rpb24gcHJpb3IgdG8gY2x1c3RlciBhbmFseXNpcy4gIA0KDQpXaGVuIGFzc2Vzc2luZyBmbyB0aGUgb3B0aW9uYWwgY2x1c3RlciBudW1iZXIgdXRpbGl6aW5nIHRoZSBzaWxob3VldHRlIG1ldGhvZCwgaXQgZGVtb25zdHJhdGVkIHRoYXQgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIGlzIHR3by4gIFN1Y2ggYSBzbWFsbCBudW1iZXIgb2YgY2x1c3RlcnMgaW5kaWNhdGVzIGEgcG9vciBjbHVzdGVyIG1vZGVsIGZpdCBhbmQgbGFja3MgcHJlZGljdGl2ZSBwb3dlci4gIEFzIGRlbW9uc3RyYXRlZCBieSB0aGUgc2lnbmlmaWNhbnQgb3ZlcmxhcCBpbiB0aGUgdHdvIGNsdXN0ZXJzLCBhIGhpZ2ggbGV2ZWwgY2x1c3RlciBhbmFseXNpcyBvZiBhbGwgbnVtZXJpYyBib3Jyb3dlciBwcm9maWxlIGRhdGEgZG9lcyBub3QgcHJvdmlkZSBwcm9wZXIgYm9ycm93ZXIgc2VnbWVudGF0aW9uIGZvciBpZGVudGlmaWNhdGlvbiBhbmQgbG9hbiBkZWZhdWx0IHByZWRpY3Rpb24uDQoNClV0aWxpemluZyBzdWNoIGEgcG9vcmx5IGZpdCBtb2RlbCBmb3IgdmFyaW91cyBidXNpbmVzcyBwdXJwb3NlcyBjb3VsZCByZXN1bHQgaW4gaW5lZmZlY3RpdmUgdGFyZ2V0ZWQgbWFya2V0aW5nLCBwb29yIHByZS1hcHByb3ZhbCBtb2RlbHMgcmVzdWx0aW5nIGluIGhpZ2hlciBiYWNrIGVuZCBjb3N0cyByZXZpZXdpbmcgYW5kIHJlamVjdGluZyBwb29ybHkgZml0IGFwcGxpY2FudHMsIGFuZCBvdmVyYWxsIHBvb3IgcmlzayBhbmFseXNpcyBpbiBpbml0aWFsIGJvcnJvd2VyIHJlcXVlc3RzLiANCg0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD05fQ0KbG9hbi5jbHVzdGVyIDwtIGxvYW5bLCBjKDE6NiwgMTU6MTcpXQ0KbG9hbi5jbHVzdGVyJExvYW5fc3RhdHVzIDwtIGFzLm51bWVyaWMobG9hbi5jbHVzdGVyJExvYW5fc3RhdHVzKQ0KbG9hbi5jbHVzdGVyLmdyb3VwIDwtIGttZWFucyh4ID0gbG9hbi5jbHVzdGVyLCBjZW50ZXJzID0gMikNCmxvYW4uY2x1c3RJRCA8LSBsb2FuLmNsdXN0ZXIuZ3JvdXAkY2x1c3Rlcg0KDQoNCnBsb3QxIDwtIGZ2aXpfbmJjbHVzdChsb2FuLmNsdXN0ZXIsIEZVTiA9IGhjdXQsIG1ldGhvZCA9ICJ3c3MiKQ0KcGxvdDIgPC0gZnZpel9uYmNsdXN0KGxvYW4uY2x1c3RlciwgRlVOID0gaGN1dCwgbWV0aG9kID0gInNpbGhvdWV0dGUiKQ0KDQoNCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHBsb3QxLHBsb3QyKQ0KDQpjbHVzcGxvdChsb2FuLmNsdXN0ZXIsDQogICAgICAgICBsb2FuLmNsdXN0SUQsDQogICAgICAgICBsaW5lcyA9IDAsDQogICAgICAgICBzaGFkZSA9IFQsDQogICAgICAgICBjb2xvciA9IFQsDQogICAgICAgICBsYWJlbHMgPSAxLA0KICAgICAgICAgcGxvdGNoYXIgPSBGLA0KICAgICAgICAgc3BhbiA9IFQpDQpgYGANCg0KDQoNCiMgSGVpcmFyY2hhbCBEYXRhIENsdXN0ZXJpbmcgLSBBZ2dsb21lcmF0aXZlDQoNClRoZSBmb2xsb3dpbmcgdmFyaWFibGVzIHdlcmUgYW5hbHl6ZWQgdG8gZGV0ZXJtaW5lIHRoZSBib3Jyb3dlcidzIHJlcGF5bWVudCBwcm9maWxlOg0KDQogIC0gQW1vdW50IGluIGJvcnJvd2VyJ3Mgc2F2aW5nIGFjY291bnQgYFNhdmluZ19hbW91bnRgDQogIC0gQm9ycm93ZXIncyBjcmVkaXQgc2NvcmUgYENyZWRpdF9zY29yZWANCiAgLSBMb2FuIGFtb3VudCBgQW1vdW50YA0KICAtIFN0YXR1cyBvZiBiYW5rIGxvYW4gZGVmYXVsdCAoRGVmYXVsdCB2cyBDdXJyZW50KSBgTG9hbl9zdGF0dXNgDQogIA0KDQpVdGlsaXppbmcgYSAnYm90dG9tLXVwJyBhcHByb2FjaCB0aGUgZGF0YSB3YXMgc3BsaXQgaW50byBjbHVzdGVycyBzdGFydGluZyB3aXRoIHNpbmdsZSBkYXRhIHBvaW50cyBhbmQgc3VjY2Vzc2l2ZWx5IG1lcmdpbmcgY2x1c3RlcnMuIFRoaXMgYWxsb3dzIGZvciB0dW5pbmcgb2YgdGhlIGNsdXN0ZXIgc2l6ZXMgd2l0aG91dCBwcmUtZGV0ZXJtaW5pbmcgY2x1c3RlciBjb3VudC4gIFRoZXJlZm9yZSwgb3ZlcmFsbCBib3Jyb3dlciBwcm9maWxlcyBjYW4gYmUgY3JlYXRlZCBmcm9tIHRoZSBkYXRhIHdpdGhvdXQgcHJlLWRldGVybWluaW5nIHNlZ21lbnRzIGFsbG93aW5nIGluIGltcHJvdmVkIGJvcnJvd2VyIHByb2ZpbGluZyBhbmQgcHJlZGljdGlvbi4NCg0KDQpBbGwgZGF0YSB3YXMgcnVuIGJvdGggcmF3IGFuZCBzY2FsZWQuICBSYXcgZGF0YSBkaWQgbm90IHByZXNlbnQgc2lnbmlmaWNhbnQgc2tldywgYW5kIHNjYWxlZCBkYXRhIHByZXNlbnRlZCBvdmVyIGZpdHRpbmcgaXNzdWVzIHJlc3VsdGluZyBpbiBhIGJlc3QgZml0IG9mIHR3byBjbHVzdGVycy4gTm8gbWlzc2luZyB2YWx1ZXMgd2VyZSBpZGVudGlmaWVkIGluIHRoZSBkYXRhIHNldCwgdGhlcmVmb3JlIG5vIGltcHV0YXRpb24gd2FzIHJlcXVpcmVkLiANCg0KYGBge3J9DQpoZWlyYXJjaC5sb2FucyA8LSBsb2FuWywgYygiQ2hlY2tpbmdfYW1vdW50IiwgIkFtb3VudCIsICJFbXBfZHVyYXRpb24iLCAiTG9hbl9zdGF0dXMiKV0NCmhlaXJhcmNoLmRpc3RhbmNlIDwtIGRpc3QoaGVpcmFyY2gubG9hbnMsIG1ldGhvZCA9ICJldWNsaWRlYW4iKQ0KaGNsdXN0ZXIxIDwtIGhjbHVzdChoZWlyYXJjaC5kaXN0YW5jZSwgbWV0aG9kID0gImNvbXBsZXRlIikNCmBgYA0KDQojIyBEZXRlcm1pbmluZyBDbHVzdGVyIENvdW50DQoNCkJ5IGV4YW1pbmluZyB0aGUgY3VycmVudCBib3Jyb3dlciBkYXRhIHRvIGRldGVybWluZSBib3RoIHRoZSBudW1iZXIgYW5kIGRpbWVuc2lvbnMgb2YgdGhlIGNsdXN0ZXJzLCB0aGUgYm9ycm93ZXIgcHJvZmlsZSBjbHVzdGVycyBjYW4gYmUgY3JlYXRlZCB0byBiZXR0ZXIgYWxpZ24gd2l0aCB0aGUgY3VycmVudCBib3Jyb3dlciBwcm9maWxlIGJhc2UuICBUaGlzIHdpbGwgYWxsb3cgZm9yIGltcHJvdmVkIGFjY3VyYWN5IGluIGJvcnJvd2VyIG91dGNvbWUgcHJlZGljdGlvbiBvdmVyIHRoZSBsaWZlIGN5Y2xlIG9mIHRoZSBsb2FuLiAgRm9yIGV4YW1wbGUsIGFzIGEgbG9hbiBwcm9ncmVzc2VzLCBpZiBhIGJvcnJvd2VyIG9yaWdpbmFsbHkgcHJvZmlsZWQgYXMgYSBoaWdoIGxpa2VsaWhvb2Qgb2YgcmVwYXltZW50IHN1ZGRlbmx5IGhhcyBhIGRpcCBpbiBlbXBsb3ltZW50IGR1cmF0aW9uIChpLmUuIGxhaWQgb2ZmKSwgYWNjdXJhdGUgYW5kIHRpbWVseSByZWFzc2Vzc21lbnQgb2YgdGhlIGJvcnJvd2VyIHByb2ZpbGUgd2lsbCBhbGxvdyBmb3IgcmFwaWQgcmV2b2NhdGlvbiBvZiBhbnkgcGVuZGluZyBsZW5kaW5nIG9mZmVycyBvciBjcmVkaXQgaW5jcmVhc2VzLCBhcyB3ZWxsIGFzIGZsYWcgdGhlIGJvcnJvd2VyIGZvciB0YXJnZXRlZCByZXBheW1lbnQgYW5kIG9yIHJlZmluYW5jaW5nIHByb2dyYW1zIHRvIHJlZHVjZSByaXNrIG9mIGxvYW4gZGVsaW5xdWVuY3kgDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTksIGZpZy53aWR0aD03fQ0KcGxvdDMgPC0gZnZpel9uYmNsdXN0KGhlaXJhcmNoLmxvYW5zLCBGVU4gPSBoY3V0LCBtZXRob2QgPSAid3NzIikNCnBsb3Q0IDwtIGZ2aXpfbmJjbHVzdChoZWlyYXJjaC5sb2FucywgRlVOID0gaGN1dCwgbWV0aG9kID0gInNpbGhvdWV0dGUiKQ0KZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocGxvdDMsIHBsb3Q0KQ0KYGBgDQoNCg0KDQojIyBGaW5hbCBDbHVzdGVyIGFuYWx5c2lzDQoNCk9wdGltYWwgY2x1c3RlcmluZyB3YXMgaW5kaWNhdGVkIGFzIDMgY2x1c3RlcnMgdmlhIHRoZSBzaWxob3VldHRlIG1ldGhvZCBhbmQgd2FzIHNlbGVjdGVkIGFzIHRoZSBwcmUtZGVmaW5lZCBudW1iZXIgb2YgY2x1c3RlcnMgZm9yIGhpZXJhcmNoaWNhbCBhZ2dsb21lcmF0ZSBjbHVzdGVyIGFuYWx5c2lzLiAgVW5saWtlIHRoZSBjbHVzdGVyaW5nIGFzIHNlZW4gaW4gdGhlIGluaXRpYWwgay1tZWFucyBjbHVzdGVyIGFuYWx5c2lzLCBieSB1dGlsaXppbmcgaGllcmFyY2hpY2FsIGFnZ2xvbWVyYXRlIGNsdXN0ZXJpbmcsIHRoZSBjbHVzdGVycyBhcmUgY3JlYXRlZCBiZWZvcmUgdGhlIGJlc3QgZml0IG51bWJlciBvZiBjbHVzdGVycyBpcyBzZWxlY3RlZC4gIFRoaXMgdW5zdXBlcnZpc2VkIGFsZ29yaXRobSBhbGxvd3MgZm9yIHRoZSBjbHVzdGVyaW5nIHRvIGJlIGRldGVybWluZWQgZGlyZWN0bHkgZnJvbSB0aGUgZGF0YSBpdHNlbGYgcmF0aGVyIHRoYW4gYmVpbmcgcHJlLWFzc2lnbmVkIGJhc2VkIG9uIGFzc3VtcHRpb25zLg0KDQpUaGlzIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIGlzIHZpc3VhbGx5IHJlcHJlc2VudGVkIGluIHRoZSBiZWxvdyBkZW5kcm9ncmFtLiAgVGhlIGNsdXN0ZXJzIGFzIHNlbGVjdGVkIGFyZSBoaWdobGlnaHRlZCB2aXN1YWxseSB3aXRoIGRpZmZlcmVudCBjb2xvciBzcXVhcmUgb3ZlcmxheXMuIEluc2lkZSB0aGUgYm91bmRzIG9mIGVhY2ggYm94IGFyZSB0aGUgYm9ycm93ZXJzIGZhbGxpbmcgaW50byB0aGF0IGNsdXN0ZXIuICBBIHNjYXR0ZXJwbG90IGlzIGFsc28gaW5jbHVkZWQgdG8gZGVtb25zdHJhdGUgdGhlIG1hcHBpbmcgb2YgdGhlc2UgY2x1c3RlcnMgYXMgYXNzaWduZWQgYnkgdGhlIGhjbHVzdCgpIGZ1bmN0aW9uLiANCg0KYGBge3IsIGZpZy53aWR0aD05fQ0KDQpoY2x1c3RlcjIgPC0gaGNsdXN0ZXIxDQpoQ2x1c3Rlcl9ncm91cCA8LSBjdXRyZWUoaGNsdXN0ZXIyLCBrID0gMykNCmhlaXJhcmNoLmxvYW5zJGhDbHVzdGVyX2dyb3VwID0gaENsdXN0ZXJfZ3JvdXANCg0KcGxvdChoY2x1c3RlcjIsIGNleCA9IDAuNiwgbGFiZWxzID0gRkFMU0UsIGhhbmcgPSAtMSwgeGxhYiA9ICIiLCBtYWluID0gIkRlbmRvZ3JhbSBvZiBCb3Jyb3dlciBQcm9maWxlIENsdXN0ZXJpbmciKQ0KcGFyKGx3ZCA9IDMpDQpyZWN0LmhjbHVzdChoY2x1c3RlcjEsIGsgPSAzLCBib3JkZXIgPSAyOjkpDQoNCnBhcihsd2Q9MSkNCmNsdXNwbG90KGhlaXJhcmNoLmxvYW5zLA0KICAgICAgICAgaENsdXN0ZXJfZ3JvdXAsDQogICAgICAgICBsaW5lcyA9IDAsDQogICAgICAgICBzaGFkZSA9IFQsDQogICAgICAgICBjb2xvciA9IFQsDQogICAgICAgICBsYWJlbHMgPSAxLA0KICAgICAgICAgcGxvdGNoYXIgPSBGLA0KICAgICAgICAgc3BhbiA9IFQsDQogICAgICAgICBtYWluID0gIkNsdXN0ZXIgUGxvdCBvZiBSYXcgQm9ycm93ZXIgRGF0YSIpDQoNCg0KYGBgDQoNCg0KDQoNCiMgUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcyAoUENBKSAmIFJlZHVjaW5nIGRpbWVuc2lvbnMNCg0KVG8gcmVkdWNlLCB0aGUgZGltZW5zaW9ucyBtdXN0IGJlIHNjYWxlZC4gQWxsIGRhdGEgdHJhbnNmb3JtZWQgdmlhIGBzY2FsZSgpYCBmdW5jdGlvbiBhcyBsb2cgc2NhbGluZyBub3QgcG9zc2libGUgd2l0aCBlbXBsb3ltZW50IGR1cmF0aW9uIGFzIHNvbWUgZHVyYXRpb24gPSAwIG1vbnRocy4gKCJDaGVja2luZ19hbW91bnQiLCAiQW1vdW50IiwgIkVtcF9kdXJhdGlvbiIsICJMb2FuX3N0YXR1cyIpDQoNCmBgYHtyfQ0KcGNhLmxvYW5zLnNjYWxlIDwtIGhlaXJhcmNoLmxvYW5zWywtNV0NCg0KcGNhLmxvYW5zLnNjYWxlJENoZWNraW5nX2Ftb3VudCA8LSBzY2FsZShwY2EubG9hbnMuc2NhbGUkQ2hlY2tpbmdfYW1vdW50KQ0KcGNhLmxvYW5zLnNjYWxlJEFtb3VudCA8LSBzY2FsZShwY2EubG9hbnMuc2NhbGUkQW1vdW50KQ0KcGNhLmxvYW5zLnNjYWxlJEVtcF9kdXJhdGlvbiA8LSBzY2FsZShwY2EubG9hbnMuc2NhbGUkRW1wX2R1cmF0aW9uKQ0KcGNhLmxvYW5zLnNjYWxlJExvYW5fc3RhdHVzIDwtIHNjYWxlKGFzLm51bWVyaWMocGNhLmxvYW5zLnNjYWxlJExvYW5fc3RhdHVzKSkNCg0KDQpwY2EubG9hbnMgPC0gcHJjb21wKHBjYS5sb2Fucy5zY2FsZSwgY2VudGVyID0gVFJVRSwgc2NhbGUgPSBUUlVFKQ0KDQoNCmthYmxlKHJvdW5kKHBjYS5sb2FucyRyb3RhdGlvbiwgMiksIGNhcHRpb24gPSAiRmFjdG9yIGxvYWRpbmcgb2YgQm9ycm93ZXIgUHJvZmlsZSBQQ0EiKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0Ka2FibGUocm91bmQoc3VtbWFyeShwY2EubG9hbnMpJGltcG9ydGFuY2UsIDMpLCBjYXB0aW9uID0gIkltcG9ydGFuY2Ugb2YgZWFjaCBjb21wb25hbnQgb2YgQm9ycm93ZXIgUHJvZmlsZSBQQ0EiKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCg0KQXMgZGVtb25zdHJhdGVkIGJ5IHRoZSBhYm92ZSBQQ0EgdGFibGVzLCB0aGUgZmlyc3QgdGhyZWUgcHJpbmNpcGFsIGNvbXBvbmVudHMgYWNjb3VudCBmb3IgODYuNiUgb2YgdGhlIHZhcmlhdGlvbiBpbiB0aGUgYm9ycm93ZXIgcHJvZmlsZSBkYXRhLiAgVGhlIGVxdWF0aW9uIGZvciBlYWNoIHByaW5jaXBhbCBjb21wb25lbnQgaXMgaW5jbHVkZWQgYmVsb3c6IA0KDQokJFBDXzE9IC0wLjY1W0NoZWNraW5nLmFtb3VudF0gKyAwLjMwW0xvYW4uYW1vdW50XSAtIDAuMjFbRW1wbG95bWVudC5kdXJhdGlvbl0gKyAwLjY3W0xvYW4uc3RhdHVzXSQkDQokJFBDXzI9IC0wLjAxW0NoZWNraW5nLmFtb3VudF0gKyAwLjYxW0xvYW4uYW1vdW50XSArIDAuNzlbRW1wbG95bWVudC5kdXJhdGlvbl0gLSAwLjAzW0xvYW4uc3RhdHVzXSQkDQokJFBDXzM9IDAuMzFbQ2hlY2tpbmcuYW1vdW50XSArIDAuNzNbTG9hbi5hbW91bnRdIC0gMC41N1tFbXBsb3ltZW50LmR1cmF0aW9uXSAtIDAuMjBbTG9hbi5zdGF0dXNdJCQNCg0KUEMxIG1vc3QgaW1wYWN0ZWQgYnkgY2hlY2tpbmcgYW1vdW50IGFuZCBsb2FuIHN0YXR1cyB3aXRoIGxpZ2h0IGltcGFjdCBmcm9tIGxvYW4gYW1vdW50IGFuZCBlbXBsb3ltZW50IGR1cmF0aW9uLCB3ZSB3aWxsIGNhbGwgdGhpcyBtZWFzdXJlIEltbWVkaWF0ZSBGaW5hbmNpYWwgU3RhdHVzIEluZGV4DQpQQzIgbW9zdCBpbXBhY3RlZCBieSBsb2FuIGFtb3VudCBhbmQgZW1wbG95bWVudCBkdXJhdGlvbiB3aXRoIG5lYXIgMCBpbXBhY3QgZnJvbSBjaGVja2luZyBhbW91bnQgYW5kIGxvYW4gc3RhdHVzLCB3ZSB3aWxsIGNhbGwgdGhpcyBtZWFzdXJlIFJlcGF5bWVudCBTZWN1cml0eSBJbmRleCANCg0KUEMzIHdpbGwgYmUgZHJvcHBlZCBhc3MgUEMyIGFuZCBQQzMgaGF2ZSBzYW1lIGxhcmdlc3QgaW5mbHVlbmNlIHZhcmlhYmxlcyAobG9hbiBhbW91bnQgYW5kIGVtcGxveW1lbnQgZHVyYXRpb24pLCBidXQgUEMyIGhhcyBhIGdyZWF0ZXIgcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSB0aGFuIFBDMy4gDQoNCiMjIERldGVybW5lIEJlc3QgTnVtYmVyIG9mIENsdXN0ZXJzDQoNClRoZSBzYW1lIHByb2Nlc3Mgd2FzIHV0aWxpemVkIHRvIGRldGVybWluZSB0aGUgYmVzdCBmaXQgbnVtYmVyIG9mIGNsdXN0ZXJzIGZvciB0aGUgUENBIHZhcmlhYmxlcywgYWdhaW4gb3V0bGluaW5nIDMgY2x1c3RlcnMgYXMgdGhlIGJlc3QgZml0LiAgRGVzcGl0ZSB0aGlzIGJlc3QgZml0IHNpbGhvdWV0dGUgcHJlZGljdGlvbiwgZm91ciBjbHVzdGVycyB3ZXJlIHV0aWxpemVkIHRvIHJlZHVjZSBvdmVyYWxsIGNsdXN0ZXIgb3ZlcmxhcC4gDQoNCmBgYHtyfQ0KDQpwY2EuY2x1c3RlciA8LSBkYXRhLmZyYW1lKEltZWRfZmluX3N0YXRfUEMxID0gcGNhLmxvYW5zJHhbLDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBSZXBheV9zZWNfUEMyID0gcGNhLmxvYW5zJHhbLDJdKQ0KDQpwbG90NSA8LSBmdml6X25iY2x1c3QocGNhLmNsdXN0ZXIsIEZVTiA9IGhjdXQsIG1ldGhvZCA9ICJ3c3MiKQ0KcGxvdDYgPC0gZnZpel9uYmNsdXN0KHBjYS5jbHVzdGVyLCBGVU4gPSBoY3V0LCBtZXRob2QgPSAic2lsaG91ZXR0ZSIpDQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwbG90NSwgcGxvdDYpDQoNCmBgYA0KDQpgYGB7cn0NCg0KDQpwY2EuZGlzdGFuY2UgPC0gZGlzdChwY2EuY2x1c3RlciwgbWV0aG9kID0gImV1Y2xpZGVhbiIpDQpwY2EuY2x1c3QgPC0gaGNsdXN0KHBjYS5kaXN0YW5jZSwgbWV0aG9kID0gImNvbXBsZXRlIikNCnBjYS5jbHVzdGVyLmdyb3VwIDwtIGN1dHJlZShwY2EuY2x1c3QsIDQpDQoNCmhlaXJhcmNoLmxvYW5zJHBjYUNsdXN0ZXIgPSBhcy5jaGFyYWN0ZXIocGNhLmNsdXN0ZXIuZ3JvdXApDQoNCmNsdXNwbG90KHBjYS5jbHVzdGVyLA0KICAgICAgICAgcGNhLmNsdXN0ZXIuZ3JvdXAsDQogICAgICAgICBsaW5lcyA9IDAsDQogICAgICAgICBzaGFkZSA9IFQsDQogICAgICAgICBjb2xvciA9IFQsDQogICAgICAgICBsYWJlbHMgPSAxLA0KICAgICAgICAgeGxhYiA9ICJJbW1lZGlhdGUgRmluYW5jaWFsIFN0YXR1cyBJbmRleCIsDQogICAgICAgICB5bGFiID0gIlJlcGF5bWVudCBTZWN1cml0eSBJbmRleCIsDQogICAgICAgICBwbG90Y2hhciA9IEYsDQogICAgICAgICBzcGFuID0gVCwNCiAgICAgICAgIG1haW49IkNsdXN0ZXIgUGxvdCBvZiBQQ0EgQ29tcG9uZW50cyIpDQoNCmBgYA0KDQpJbiByZXZpZXdpbmcgdGhlIHByaW5jaXBhbCBjb21wb25lbnQgdmFyaWFibGVzIGFyZSBmb3VuZCB0byBpbXByb3ZlIGJvcnJvd2VyIHByb2ZpbGUgcHJlZGljdGl2ZSBjYXBhYmlsaXRpZXMgdGhyb3VnaCBkaW1lbnNpb24gcmVkdWN0aW9uLiAgQnkgY29tYmluaW5nIGZvdXIgdmFyaWFibGVzIGludG8gdHdvIGNvbXBvbmVudCBhbmFseXNpcyB2YXJpYWJsZXMsIHRoZSBkYXRhIGNhbiBiZSBtb3JlIGVhc2lseSBtYW5pcHVsYXRlZCBmb3IgYm9ycm93ZXIgb3V0Y29tZSBwcmVkaWN0aW9ucyBhdCB0aGUgdGltZSBvZiBsZW5kaW5nIGFuZCBpbmNyZWFzZWQgdGFyZ2V0ZWQgbWFya2V0aW5nIGZvciBjdXN0b21lcnMgaW5kaWNhdGVkIHRvIGhhdmUgYSBzdHJvbmcgZmluYW5jaWFsIHN0YXR1cyBpbmRleCBhbmQgcmVwYXltZW50IHNlY3VyaXR5IGluZGV4LiAgIFRoaXMgYWxsb3dzIGZvciBib3Jyb3dlciBzZWdtZW50YXRpb24gaW50byBmb3VyIGNsdXN0ZXJzOg0KDQogIC0gSGlnaCBpbW1lZGlhdGUgZmluYW5jaWFsIHN0YXR1cyAmIEhpZ2ggUmVwYXltZW50IFNlY3VyaXR5DQogIC0gSGlnaCBJbW1lZGlhdGUgRmluYW5jaWFsIFN0YXR1cyAmIExvdyBSZXBheW1lbnQgU2VjdXJpdHkNCiAgLSBMb3cgSW1tZWRpYXRlIEZpbmFuY2lhbCBTdGF0dXMgJiBIaWdoIFJlcGF5bWVudCBTZWN1cml0eQ0KICAtIExvdyBJbW1lZGlhdGUgRmluYW5jaWFsIFN0YXR1cyAmIExvdyBSZXBheW1lbnQgU2VjdXJpdHkNCiAgDQoNClRoZXNlIGZvdXIgY2x1c3RlcnMgdGhhdCBjb21wcmlzZSB0aGUgYm9ycm93ZXIgcHJvZmlsZSBjYW4gYWxsb3cgZm9yIGEgdGFyZ2V0ZWQgY3VzdG9tZXIgYmFzZWQgYXBwcm9hY2guICBUaG9zZSB3aXRoIGhpZ2ggZmluYW5jaWFsIHN0YXR1cyBhbmQgaGlnaCByZXBheW1lbnQgc2VjdXJpdHkgY2FuIGJlIHRhcmdldGVkIGZvciBsb3cgaW50ZXJlc3QgcmF0ZSBsb2FucyB3aXRoIGEgaGlnaGVyIGRvd24gcGF5bWVudCBtYXJrZXRpbmcgdG8gZW50aWNlIGZ1cnRoZXIgYm9ycm93aW5nIGZyb20gYSBsb3cgcmlzayBib3Jyb3dlci4gIFdoZXJlIGFzIHRob3NlIHdpdGggbG93IGltbWVkaWF0ZSBmaW5hbmNpYWwgc3RhdHVzICYgaGlnaCByZXBheW1lbnQgc2VjdXJpdHkgY2FuIGJlIHRhcmdldGVkIGZvciBtaWNybyB0byBtZWRpdW0gc2l6ZWQgbG9hbnMgd2l0aCBtaW5pbWFsIHRvIG5vIGRvd24gcGF5bWVudHMgdG8gZW50aWNlIGZ1cnRoZXIgYm9ycm93aW5nIGZyb20gYSBsb3cgcmlzayBib3Jyb3dlciB0aGF0IG1heSBub3QgaGF2ZSB0aGUgZnVuZHMgZm9yIGEgdHJhZGl0aW9uYWwgaGlnaCBkb3duIHBheW1lbnQgbG9hbi4gDQoNCiMgT3V0bGllciBhbmFseXNpcw0KDQpPdXRsaWVyIGlkZW50aWZpY2F0aW9uIGNhbiBwcm92aWRlIGZ1cnRoZXIgc3VwcG9ydCBpbiBib3RoIGVmZmVjdGl2ZSB0YXJnZXRlZCBtYXJrZXRpbmcsIGFwcHJvcHJpYXRlIGJvcnJvd2VyIGFwcGxpY2F0aW9uIGRlbmlhbHMsIGFuZCBmcmF1ZHVsZW50IGJvcnJvd2VyIGFwcGxpY2F0aW9uIGlkZW50aWZpY2F0aW9uLiBPdXRsaWVycyB0byB0aGUgYm9ycm93ZXIgcHJvZmlsZSBncm91cHMgc2hvdWxkIGJlIG1hbnVhbGx5IHJldmlld2VkIHRvIGRldGVybWluZSBpZiB0aGUgYm9ycm93ZXIgaXMgYXQgcmlzayBmb3IgYWRqdXN0aW5nIG9uZSBvZiB0aGVpciBwcmluY2lwYWwgY29tcG9uZW50IGNsYXNzaWZpY2F0aW9ucyAoZXg6IG1vdmluZyBmcm9tIGhpZ2ggaW1tZWRpYXRlIGZpbmFuY2lhbCBzdGF0dXMgdG8gbG93LCBldGMpLCBpZiB0aGUgYm9ycm93ZXIgZGF0YSBpcyBwb3RlbnRpYWxseSBmcmF1ZHVsZW50LCBvciBpZiB0aGUgYm9ycm93ZXIgZGF0YSBpbmRpY2F0ZXMgYSBib3Jyb3dlciB0aGF0IHJlcXVpcmVzIHBvdGVudGlhbCBhY2NvdW50IHRlcm1pbmF0aW9uIGFuZC9vciBmdXR1cmUgYm9ycm93ZXIgYXBwbGljYXRpb24gZGVuaWFsIGZvciBsYWNrIG9mIHJlcGF5bWVudCBhYmlsaXR5LiAgDQoNCkFkZGl0aW9uYWxseSwgcHJvcGVybHkgaWRlbnRpZnlpbmcgb3V0bGllcnMgYWxsb3dzIGZvciByZW1vdmFsIG9mIG91dGxpZXJzIHByaW9yIHRvIFBDQSBvciBoaWVyYXJjaGljYWwgYWdnbG9tZXJhdGUgY2x1c3RlcmluZyB3aWxsIHJlc3VsdCBpbiBpbmNyZWFzZWQgYm9ycm93ZXIgcmVwYXltZW50IHByb2ZpbGUgY2xhc3NpZmljYXRpb24gYW5kIGltcHJvdmVkIHByZWRpY3RpdmUgY2FwYWNpdHkuDQoNCiMjIExvY2FsIE91dGxpZXIgRmFjdG9yIChMT0YpIFNjb3JlDQoNCkEgbG9jYWwgb3V0bGllciBmYWN0b3IgKExPRikgc2NvcmUgaXMgY2FsY3VsYXRlZCB1dGlsaXppbmcgdGhlIGJlbG93IGVxdWF0aW9uIHRvIGNvbXBhcmUgYSBkYXRhIHBvaW50J3MgbG9jYWwgcmVhY2hhYmlsaXR5IGRlbnNpdHkgKExSRCkgb2YgdGhlIG5lYXJlc3QgayBuZWlnaGJvcnMgdG8gcG9pbnQgJEFfaSQgZm9yICRpID0gMSAsIDIsIC4uLiAsIG4kLiANCg0KJCQNCkxPRihBX2kpID0gXGZyYWN7XGZyYWN7XHN1bV97aSA9IEFfaiBcaW4gTl9rQV9pfSB7TFJEX2soQV9qKX19e3x8Tl9rKEFfaSl8fH19IHtMUkRfayhBX2kpfQ0KJCQNCg0KVGhlIExPRiBmYWN0b3IgYWN0cyBhcyBhbiBlYXNpbHkgZmlsdGVyYWJsZSBzY2FsZSB2YXJpYWJsZSB0byBxdWlja2x5IGlkZW50aWZ5IG91dGxpZXIgdmFsdWVzLiAgQW4gTE9GID4gMSBpbmRpY2F0ZXMgYSBwb3RlbnRpYWwgb3V0bGllciwgd2l0aCB0aGUgZ3JlYXRlciB2YWx1ZXMgZm9yIExPRiBpbmRpY2lhdGluZyBtb3JlIGV4dHJlbWUgb3V0bGllcnMuIA0KDQpgYGB7cn0NCg0KbG9mLnBjYS5jbHVzdGVyIDwtIGxvZihwY2EuY2x1c3RlciwgbWluUHRzID0gNTApDQpwY2EuY2x1c3RlciRMT0YgPC0gbG9mLnBjYS5jbHVzdGVyDQoNCnBhbmRlcjo6cGFuZGVyKHN1bW1hcnkobG9mLnBjYS5jbHVzdGVyKSwgY2FwdGlvbiA9ICJTdW1tYXJ5IHN0YXRpc3RpY3Mgb2YgTE9GIHNjb3JlcyBmb3IgSW1tZWRpYXRlIEZpbmFuY2lhbCBTdGF0dXMgSW5kZXggJiBSZXBheW1lbnQgU2VjdXJpdHkgSW5kZXgiKQ0KYGBgDQoNCkJhc2VkIG9uIHRoZSBzdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIHRoZSBjYWxjdWxhdGVkIExPRiBzY29yZXMgZm9yIHRoZSBQQ0EgZmVhdHVyZSB2YXJpYWJsZXMgdGhlIG91dGxpZXIgY3V0b2ZmIHdhcyBzZWxlY3RlZCBhcyBhbiBMT0YgdmFsdWUgb2YgMS44LiAgVGhpcyB2YWx1ZSB3YXMgc2VsZWN0ZWQgYXMgdGhlIGN1dG9mZiB0byBlbnN1cmUgbGVzcyB0aGFuIDElIG9mIHRoZSBkYXRhc2V0IGlzIGlkZW50aWZpZWQgYXMgYW4gb3V0bGllciwgbGVhZGluZyB0byBhbiBvdXRsaWVyIGZsYWdnaW5nIG9mIDEgaW4gZXZlcnkgMTAwIGJvcnJvd2Vycy4gU2VsZWN0aW5nIGFuIExPRiB2YWx1ZSBvZiAxLjcgcmVzdWx0ZWQgaW4gPiAxJSBvdXRsaWVyIGZsYWdnaW5nLiBUaGlzIDElIG91dGxpZXIgZmxhZ2dpbmcgd2lsbCBzY2FsZSB3ZWxsIHRvIHRoZSBsZW5kZXIgb3IgY3JlZGl0IGFnZW5jeSdzIG9wZXJhdGlvbmFsIGNvbnN0cmFpbnRzIHVudGlsIHRoZSBuZWVkIGZvciBoeXBlcnBhcmFtZXRlciAoaykgdHVuaW5nIGlzIHJlcXVpcmVkLiANCg0KYGBge3J9DQpwbG90KHggPSBwY2EuY2x1c3RlciRJbWVkX2Zpbl9zdGF0X1BDMSwNCiAgICAgeSA9IHBjYS5jbHVzdGVyJFJlcGF5X3NlY19QQzIsDQogICAgIHBjaCA9ICJ4IiwNCiAgICAgY2V4ID0gMC41LA0KICAgICB4bGFiID0gIkltbWVkaWF0ZSBGaW5hbmNpYWwgU3RhdHVzIEluZGV4IiwNCiAgICAgeWxhYiA9ICJSZXBheW1lbnQgU2VjdXJpdHkgSW5kZXgiLA0KICAgICBtYWluPSJPdXRsaWVyIElkZW50aWZpY2F0aW9uIEFtb25nc3QgUENBIENvbXBvbmVudHMiKQ0KcG9pbnRzKHBjYS5jbHVzdGVyLA0KICAgICAgIGNleCA9ICgobG9mLnBjYS5jbHVzdGVyIC0gMSkqMS41KSwNCiAgICAgICBwY2ggPSAyMSwNCiAgICAgICBjb2wgPSAiaG90cGluayIpDQp0ZXh0KHBjYS5jbHVzdGVyW2xvZi5wY2EuY2x1c3RlciA+IDEuOCxdLA0KICAgICBsYWJlbHMgPSByb3VuZChsb2YucGNhLmNsdXN0ZXIsIDEpW2xvZi5wY2EuY2x1c3RlciA+MS44XSwNCiAgICAgcG9zID0gMiwNCiAgICAgY2V4ID0gMC42LA0KICAgICBjb2wgPSAiZGFya3JlZCIpDQoNCmthYmxlKGZpbHRlcihwY2EuY2x1c3RlciwgTE9GID4gMS44KSwgY2FwdGlvbiA9ICJsaXN0aW5nIG9mIGFsbCBMT0Ygc2NvcmVzID4gMS44IikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQojIENvbmNsdXNpb25zDQoNCk92ZXJhbGwgcHJvcGVyIGJvcnJvd2VyIHByb2ZpbGUgc2VnbWVudGF0aW9uIHdpbGwgYWxsb3cgZm9yIGltcHJvdmVkIGxvYW4gZGVmYXVsdCBwcmVkaWN0aW9uIG1vZGVscywgaW1wcm92ZWQgaWRlbnRpZmljYXRpb24gb2YgZnJhdWR1bGVudCBwcmUtYXBwcm92YWwgYXBwbGljYXRpb25zLCBhbmQgaW1wcm92ZWQgdGFyZ2V0ZWQgbWFya2V0aW5nIHRvIGRyaXZlIHVwIGJvcnJvd2luZyByYXRlcyBmcm9tIGJvcnJvd2VycyB3aXRoIGEgaGlnaCByZXBheW1lbnQgcHJvZmlsZS4gIE92ZXJhbGwgdGhpcyBib3Jyb3dlciBzZWdtZW50YXRpb24gYW5kIGNsYXNzaWZpY2F0aW9uIG1heSBhbHNvIGJlIHVzZWQgZm9yIHByZWRpY3RpdmUgYW5hbHlzaXMgcmVnYXJkaW5nIGJvcnJvd2VyIHByZS1hcHByb3ZhbCBkZXRlcm1pbmF0aW9ucy4gIE92ZXJhbGwgYm9ycm93ZXIgcG9wdWxhdGlvbiBzZWdtZW50YXRpb24gcmVtYWlucyBhIGhpZ2hseSBlZmZlY3RpdmUgdG9vbCBmb3IgbWFuYWdpbmcgYW5kIHByZWRpY3Rpbmcgb3ZlcmFsbCBsb2FuIG91dGNvbWVzLg0KDQoNCiMgUmVmZXJlbmNlcw0KDQpEYXRhIHNvdXJjZToNCg0KQXBwbGllZCBBbmFseXRpY3MgdGhyb3VnaCBDYXNlIFN0dWRpZXMgVXNpbmcgU0FTIGFuZCBSLCBEZWVwdGkgR3VwdGEgYnkgQVByZXNzLCBJU0JOIC0gOTc4LTEtNDg0Mi0zNTI1LTYNCkFjY2Vzc2VkIHZpYTogPGEgaHJlZj0iaHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vZGF0YXNldHMvTG9hbkRhdGEyL0JhbmtMb2FuRGVmYXVsdERhdGFzZXQuY3N2Ij5odHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9kYXRhc2V0cy9Mb2FuRGF0YTIvQmFua0xvYW5EZWZhdWx0RGF0YXNldC5jc3Y8L2E+