Introduction
Exploratory Data Analysis (EDA) is a critical process in data science
that involves summarizing the main characteristics of a data set, often
with visual methods. The primary purposes of EDA are
Understanding Data Structure: EDA helps you
understand the underlying structure of the data, including its
distribution, patterns, and anomalies.
Identifying Relationships: It allows you to identify
relationships between variables, which can inform feature selection and
engineering.
Detecting Outliers: EDA helps in detecting outliers
or unusual observations that might affect the performance of your
models.
Checking Assumptions: It is used to check the
assumptions required for statistical tests and models.
With the identified patterns through EDA, we can select appropriate
techniques to extract more hidden information to improve the performance
of the subsequent modeling and related analysis.
In this case study, we use a publicly avaiable data set as an example
to perform exploratory data analysis and detect patterns of the data
Description of the
Data
A population of women who were at least 21 years old, of Pima Indian
heritage, and living near Phoenix, Arizona, was tested for diabetes
according to World Health Organization criteria. The data were collected
by the US National Institute of Diabetes and Digestive and Kidney. The
objective of the data set is to diagnostically predict whether or not a
patient has diabetes, based on certain diagnostic measurements included
in the data set. Several constraints were placed on the selection of
these instances from a larger database.
There are two versions of the data available in the public domain.
This case study uses the version that contains the missing values. The
total number of records in this data set is 768. The data set consists
of 9 variables including the response variable with the name
diabetes
. Predictor variables include the number of
pregnancies the patient has had, their BMI, insulin level, age, and so
on. A detailed description of the variables is given below
pregnant
: Number of times pregnant
glucose
: Plasma glucose concentration 2 hours in an oral
glucose tolerance test
pressure
: Diastolic blood pressure (mm Hg)
triceps
: Triceps skin fold thickness (mm)
insulin
: 2-Hour serum insulin (mu U/ml)
mass
: Body mass index (weight in kg/(height in m)^2)
pedigree
: Diabetes pedigree function
age
: Age (years)
diabetes
: outcome class variable (‘neg’ or ‘pos’)
A copy of this publicly available data is stored at https://pengdsci.github.io/datasets/PimaDiabetes/PimaIndiansDiabetes2.csv.
PimaDiabetes = read.csv("https://pengdsci.github.io/datasets/PimaDiabetes/PimaIndiansDiabetes2.csv")[, -1]
summary(PimaDiabetes)
pregnant glucose pressure triceps
Min. : 0.000 Min. : 44.0 Min. : 24.00 Min. : 7.00
1st Qu.: 1.000 1st Qu.: 99.0 1st Qu.: 64.00 1st Qu.:22.00
Median : 3.000 Median :117.0 Median : 72.00 Median :29.00
Mean : 3.845 Mean :121.7 Mean : 72.41 Mean :29.15
3rd Qu.: 6.000 3rd Qu.:141.0 3rd Qu.: 80.00 3rd Qu.:36.00
Max. :17.000 Max. :199.0 Max. :122.00 Max. :99.00
NA's :5 NA's :35 NA's :227
insulin mass pedigree age
Min. : 14.00 Min. :18.20 Min. :0.0780 Min. :21.00
1st Qu.: 76.25 1st Qu.:27.50 1st Qu.:0.2437 1st Qu.:24.00
Median :125.00 Median :32.30 Median :0.3725 Median :29.00
Mean :155.55 Mean :32.46 Mean :0.4719 Mean :33.24
3rd Qu.:190.00 3rd Qu.:36.60 3rd Qu.:0.6262 3rd Qu.:41.00
Max. :846.00 Max. :67.10 Max. :2.4200 Max. :81.00
NA's :374 NA's :11
diabetes
Length:768
Class :character
Mode :character
Handling Missing
Value
The above summary table indicates that feature variables
glucose
, pressure
, triceps
,
insulin
, and mass
have missing values.
insulin
has nearly 50% missing values. triceps
has 227 missing values. The other three variables have a very low
percentage of missing values.
Missing Value vs No
Value
Missing value means that the information is available but not
collected while no value means that the value does not exist.
Replacing the missing values with proxy values (imputation) or
deleting them from the data are the ways of handling missing values.
Most software programs automatically delete all records with missing
components from the data before modeling if the missing value issue
is not handled.
no-value
should be never imputed in the data processing.
The ways of handling no value
is to either drop all records
with no value
components or the feature variables that have
no values
. The former will change the study population and
the latter will lead to a loss of information.
Glucose Tolerance
(glucose
) vs 2-Hour Serum Insulin
(insulin
)
Both fasting insulin test
and
glucose tolerance test
are used in diabetes diagnosis,
therefore, variables glucose
and insulin
are
correlated. Since nearly 50% of patients did not do the insulin test.
Therefore, we can use glucose
to impute the missing values
in insulin
. We first look at the correlation between the
two variables based on the complete data.
par(mfrow = c(1,2))
plot(PimaDiabetes$glucose, PimaDiabetes$insulin, xlab = "Glucose Level", ylab = "Insulin Level")
plot(PimaDiabetes$glucose, log(PimaDiabetes$insulin), xlab = "Glucose Level", ylab = "log Insulin Level")

The scatter plot shows that the logarithm of the insulin level and
the glucose level are highly linearly correlated. We can use this
relationship to impute the logarithm of insulin level based on the
no-missing glucose level. Since we will use this data set to build
predictive models, the logarithm of insulin will be used directly in the
subsequent models and algorithms.
impute.insulin.lm = lm(log(insulin[-446]) ~ glucose[-446], data = PimaDiabetes)
summary(impute.insulin.lm)
Call:
lm(formula = log(insulin[-446]) ~ glucose[-446], data = PimaDiabetes)
Residuals:
Min 1Q Median 3Q Max
-1.87469 -0.31478 -0.02521 0.33826 1.55711
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 3.0588150 0.1095012 27.93 <2e-16 ***
glucose[-446] 0.0143630 0.0008673 16.56 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.5269 on 390 degrees of freedom
(375 observations deleted due to missingness)
Multiple R-squared: 0.4129, Adjusted R-squared: 0.4114
F-statistic: 274.3 on 1 and 390 DF, p-value: < 2.2e-16
par(mfrow = c(2,2))
plot(impute.insulin.lm)

Next, we use the following linear regression to impute the missing
values in insulin
.
glucose = PimaDiabetes$glucose
impute.log.insulin = log(PimaDiabetes$insulin)
n=length(impute.log.insulin)
for (i in 1:n){
if (is.na(impute.log.insulin[i]) == TRUE && is.na(glucose[i]) == FALSE) impute.log.insulin[i] = sum(coef(impute.insulin.lm)*c(1,glucose[i])) + sample(resid(impute.insulin.lm),1)
}
Visual comparison of the distribution between the original
insulin
and the imputed insulin
.
den.orig.insulin = density(na.omit(log(PimaDiabetes$insulin)))
den.impute.insulin = density(na.omit(impute.log.insulin))
plot(den.impute.insulin, xlab="log insulin", main = "density curve of log of original and imputed insulin",
col = "red")
lines(den.orig.insulin, col = "blue")
legend("topright", c("original log insulin", "inputed log insulin"), col = c("blue", "red"), lty = rep(1,2), cex = 0.9, bty = "n")

The above density curves show that distributions of the imputed log
insulin and original log insulin levels are close to each other.
PimaDiabetes$impute.log.insulin = impute.log.insulin
Triceps Skinfold
Thickness (triceps
) vs Body Mass Index
(mass
)
Clinical variables triceps
(triceps skin-fold thickness,
see the following figure to see how it is measured) and
mass
(body mass index) are clinically correlated.
triceps
has nearly 30% missing values and
mass
has a few missing values. We can use the information
in mass
to impute the missing values in triceps - single
imputation with a linear regression model. To perform imputation,
fit a linear regression model with triceps
being the
response and mass
as the predictor.
use the above-fitted regression to predict triceps
on non-missing mass
.
impute the missing value in triceps
with the
predicted triceps
.
Note that in R, records with missing components will be automatically
deleted in the modeling process.
impute.lm = lm(triceps[-580] ~ mass[-580], data = PimaDiabetes)
summary(impute.lm)
Call:
lm(formula = triceps[-580] ~ mass[-580], data = PimaDiabetes)
Residuals:
Min 1Q Median 3Q Max
-19.6294 -4.9225 -0.4862 5.0930 21.3029
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -3.34070 1.56901 -2.129 0.0337 *
mass[-580] 0.98464 0.04669 21.087 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 7.442 on 536 degrees of freedom
(229 observations deleted due to missingness)
Multiple R-squared: 0.4534, Adjusted R-squared: 0.4524
F-statistic: 444.7 on 1 and 536 DF, p-value: < 2.2e-16
par(mfrow = c(2,2))
plot(impute.lm)

The above-fitted regression line will be used to
impute the missing values in triceps
in
the following.
mass = PimaDiabetes$mass
impute.triceps = PimaDiabetes$triceps
n=length(impute.triceps)
for (i in 1:n){
if (is.na(impute.triceps[i]) == TRUE && is.na(mass[i]) == FALSE) impute.triceps[i] = sum(coef(impute.lm)*c(1,mass[i])) + sample(resid(impute.lm),1)
}
PimaDiabetes$impute.triceps = impute.triceps
Next, we check whether the missing values in triceps
were appropriately imputed.
We look at the density curves of impute.triceps
and the
original triceps
to see the performance of the imputation
and whether a discretization is needed.
den.tri = density(na.omit(PimaDiabetes$triceps))
den.imput.tri = density(na.omit(PimaDiabetes$impute.triceps))
plot(den.imput.tri, col = "red", xlab = "triceps", ylab = "density", main = "original triceps vs imputed triceps")
lines(den.tri, col = "blue")
legend("topright", c("Inputed Triceps", "Original Triceps"), col=c("red", "blue"), lty =rep(1,2), bty="n", cex = 0.8)

The above density curves indicate that
The two distributions are almost identical, and
both distributions are almost symmetric (except for one outlier
in the original data).
Since the missing values in triceps
were appropriately
imputed, we next add the impute.triceps
to the original
data frame and drop the original triceps
.
To close this imputation section, we reorganized the data set by
dropping the original variables and keeping the imputed variables. At
the same time, we also delete all records with missing components.
PimaDiabetes = na.omit(PimaDiabetes[, c("pregnant", "glucose", "pressure", "mass", "pedigree", "age" ,"impute.triceps", "diabetes")])
Assess
Distributions
This subsection focuses on the potential discretization of continuous
variables and grouping sparse categories of category variables based on
their distribution.
Discretizing
Continuous Variables
The above pairwise scatter plot shows that glucose
,
pressure
(diastolic reading), and age
are
usually discretized in the clinical study. We will use the clinical
standards and practices to discretize these variables
According to Medical News Today (https://www.medicalnewstoday.com/articles/a1c-chart-diabetes-numbers#a-1-c-chart).
The glucose levels \(< 117\), \([117, 137]\), \(>137\) indicate normal, pre-diabetes,
and diabetes.
According to the National Diabetes Statistics Report ( https://www.cdc.gov/media/releases/2017/p0718-diabetes-report.html#:~:text=Rates%20of%20diagnosed%20diabetes%20increased,older%2C%2025%20percent%20had%20diabetes),
rates of diagnosed diabetes increased with age. Among adults ages 18-44,
4 percent had diabetes. Among those ages 45-64 years, 17 percent had
diabetes. And among those ages 65 years and older, 25 percent had
diabetes.
According to The Seventh Report of the Joint National Committee on
Prevention, Detection, Evaluation, and Treatment of High Blood Pressure
(2003 Guideline, https://www.nhlbi.nih.gov/files/docs/guidelines/express.pdf),
The normal diastolic pressure is less than 80 mm Hg, at risk diastolic
reading is between 80 mm Hg and 90 mm Hg, abnormal (hypertension)
diastolic reading is higher than 90 mm Hg.
We will discretize these three variables for future models and
algorithms.
PimaDiabetes$grp.glucose <- ifelse(PimaDiabetes$glucose < 117, '(0, 117)',
ifelse(PimaDiabetes$glucose > 137, '> 137', '[117,137]'))
PimaDiabetes$grp.diastolic <- ifelse(PimaDiabetes$pressure < 80, '(0, 80)',
ifelse(PimaDiabetes$pressure > 90, '> 90', '[80,90]'))
PimaDiabetes$grp.age <- ifelse(PimaDiabetes$age <= 44, '[21, 44]',
ifelse(PimaDiabetes$age >= 65, '65+', '[45, 64]'))
Grouping Sparse
Categories
The number of times pregnant pregnant
is a discrete
numerical variable. We could also consider it as an ordinal categorical
variable.
pregnancy = table(PimaDiabetes$pregnant)
barplot(pregnancy, main = "Distribution of pregnacies", xlab = "Pregnant Times")

There are a few sparse categories in the variable, so we decided to
group this variable into the following: 0, 1, 2, 3-4, 5-7, 8+.
PimaDiabetes$grp.pregnant <- ifelse(PimaDiabetes$pregnant == 0, '0',
ifelse(PimaDiabetes$pregnant == 1, '1',
ifelse(PimaDiabetes$pregnant == 2, '2',
ifelse((PimaDiabetes$pregnant == 3 | PimaDiabetes$pregnant == 4), '3-4',
ifelse((PimaDiabetes$pregnant == 5 | PimaDiabetes$pregnant == 6 | PimaDiabetes$pregnant == 7), '5-7',
ifelse(PimaDiabetes$pregnant >= 8, '8+', "NA"))))))
As the last step, we only keep those variables to be used in the
subsequent modeling.
var.names = c("mass", "pedigree", "impute.triceps", "grp.glucose", "grp.diastolic", "grp.age", "grp.pregnant", "diabetes")
PimaDiabetes = PimaDiabetes[, var.names]
Save Analytic
Dataset
The final analytic data should be saved as permanent data for
subsequent analysis and modeling and the saved data set to the GitHub
data repository for easy access in the future.
write.csv(PimaDiabetes, "C:\\Users\\75CPENG\\OneDrive - West Chester University of PA\\Desktop\\cpeng\\WCU-Teaching\\2023Summer\\STA551\\w03\\AnalyticPimaDiabetes.csv")
The above csv file is also uploaded to the GitHub data repository at
https://pengdsci.github.io/STA551/w03/AnalyticPimaDiabetes.csv.
Pairwise
Association
Depending on the types of variables, there are three different
combinations of two variables: two numeric variables, two categorical
variables, one numeric variable, and one categorical variable. We will
assess the association between two variables graphically based on the
above three scenarios.
Two or More Numeric
Variables
The best visual tool for assessing pairwise linear association
between two numeric variables is a pair-wise scatter plot. The pair-wise
scatter plot and its variants are available in several different R
packages.
ggpairs(PimaDiabetes, # Data frame
columns = 1:3, # Columns
aes(color = diabetes, # Color by group (cat. variable)
alpha = 0.5)) # Transparency

The off-diagonal plots and numbers indicate the correlation between
the pair-wise numeric variables. As expected, triceps and mass are
significantly correlated. Other paired variables have weak
correlations.
The main diagonal stacked density curves show the potential
difference in the distribution of the underlying numeric variable in
diabetes and diabetes-free groups. This means that the stacked density
curves show the relation between numeric and categorical variables.
These stacked density curves are not completely overlapped indicating
some correlation between each of these numeric variables and the binary
response variable.
Because of the above interpretation between numeric variables and the
binary response variable, we will not open a new subsection to
illustrate the relationship between a numeric variable and a categorical
variable.
Two Categorical
Variables
Mosaic plots are convenient to show whether two categorical variables
are dependent. In EDA, we are primarily interested in whether the
response (binary in this case) is independent of categorical variables.
Those categorical variables that are independent of the response
variable should be excluded from any of the subsequent models and
algorithms.
par(mfrow = c(2,2))
mosaicplot(grp.glucose ~ diabetes, data=PimaDiabetes,col=c("Blue","Red"), main="glucose vs diabetes")
mosaicplot(grp.diastolic ~ diabetes, data=PimaDiabetes,col=c("Blue","Red"), main="diastolic vs diabetes")
mosaicplot(grp.age ~ diabetes, data=PimaDiabetes,col=c("Blue","Red"), main="age vs diabetes")
mosaicplot(grp.pregnant ~ diabetes, data=PimaDiabetes,col=c("Blue","Red"), main="pregnant vs diabetes")

The top two mosaic plots demonstrate the positive association between
glucose
levels and diastolic
readings. The
bottom two mosaic plots also show that diabetes is not independent of
age
and pregnant
times because the proportion
of diabetes cases in individual categories is not identical.
One Categorical and
One Numerical Variable
A box-plot is a type of visual shorthand for measures of position
(percentiles) that describe a distribution. It is also useful for
identifying potential outliers. When assessing the relationship between
a numerical and a categorical variable, we look at the distribution of
the numerical variable in each category. If the distributions across the
categories are identical, the numerical and the categorical variables
are not associated.
For ease of illustration, we only use dianetes status and glucose
level in the Pima Indian diabetes data set.
GlucoseDiabetes = na.omit(PimaDiabetes[, c(1,8)])
ggplot(GlucoseDiabetes) +
aes(x = mass, y = diabetes, color = diabetes) +
geom_boxplot() +
ggtitle("PimaIndian Diebetes: diabetes status vs glucose") +
theme(plot.title = element_text(hjust = 0.5),
legend.position="top") +
xlab("diabetes status") +
ylab("glucose level")
The above group box-plot shows that the distributions of glucose
levels of diabetes and diabetes-free sets are different: means and
variances are all different. In addition, the subset of neg
(subset of diabetes-free subjects) as a few outliers.
The next ridge
density plot also shows the difference of
the two density curves.
ggplot(GlucoseDiabetes, aes(x = mass, y = diabetes, fill = diabetes)) +
geom_density_ridges() +
ggtitle("Diabetes status vs glucose level") +
theme(plot.title = element_text(hjust = 0.5),
legend.position="top") +
xlab("Diabetes Status") +
ylab("Glucose Level")
The ridge plot is a 2D plot but has 3D effect. It is aesthetically
pleasant, but could also introduces visual bias in terms of center of
the distributions. We next sketch overlaid density curves with the same
horizontal axis.
ggplot(data = GlucoseDiabetes, aes(x = mass, fill = diabetes)) +
geom_density(alpha = 0.3) +
ggtitle("Diabetes Status vs Glucose Level") +
theme(plot.title = element_text(hjust = 0.5),
legend.position="top") +
xlab("Diabetes Status") +
ylab("Glucose Level")
LS0tDQp0aXRsZTogJ0V4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgLSBDYXNlIFN0dWR5Jw0KYXV0aG9yOiAiQ2hlbmcgUGVuZyINCmRhdGU6ICIgU1RBIDUxMSAtIEZvdWRhdGlvbnMgb2YgRGF0YSBTY2llbmNlIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIHRvY19mbG9hdDogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCiAgICB0aGVtZTogbHVtZW4NCiAgd29yZF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAga2VlcF9tZDogeWVzDQogIHBkZl9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICBmaWdfd2lkdGg6IDMNCiAgICBmaWdfaGVpZ2h0OiAzDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQpgYGB7PWh0bWx9DQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCi8qIENhc2NhZGluZyBTdHlsZSBTaGVldHMgKENTUykgaXMgYSBzdHlsZXNoZWV0IGxhbmd1YWdlIHVzZWQgdG8gZGVzY3JpYmUgdGhlIHByZXNlbnRhdGlvbiBvZiBhIGRvY3VtZW50IHdyaXR0ZW4gaW4gSFRNTCBvciBYTUwuIGl0IGlzIGEgc2ltcGxlIG1lY2hhbmlzbSBmb3IgYWRkaW5nIHN0eWxlIChlLmcuLCBmb250cywgY29sb3JzLCBzcGFjaW5nKSB0byBXZWIgZG9jdW1lbnRzLiAqLw0KDQpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLw0KICBmb250LXNpemU6IDI0cHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7DQp9DQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGF1dGhvcnMgICovDQogIGZvbnQtc2l6ZTogMjBweDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAyMnB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCn0NCmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8NCiAgICBmb250LXNpemU6IDIwcHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KaDMgeyAvKiBIZWFkZXIgMyAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgMyBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoNCB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCA0IHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCi5oaWdobGlnaHRtZSB7IGJhY2tncm91bmQtY29sb3I6eWVsbG93OyB9DQoNCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCjwvc3R5bGU+DQpgYGANCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgDQojIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dCBmaWxlcy4NCmlmICghcmVxdWlyZSgia25pdHIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KICAgbGlicmFyeShrbml0cikNCn0NCmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCn0NCmlmICghcmVxdWlyZSgiR0dhbGx5IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIkdHYWxseSIpDQpsaWJyYXJ5KEdHYWxseSkNCn0NCmlmICghcmVxdWlyZSgiZ2dyaWRnZXMiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dyaWRnZXMiKQ0KICAgbGlicmFyeShnZ3JpZGdlcykNCn0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZ3MgPSBGQUxTRSwgICMgc29tZXRpbWVzLCB5b3UgY29kZSBtYXkgcHJvZHVjZSB3YXJuaW5nIG1lc3NhZ2VzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHlvdSBjYW4gY2hvb3NlIHRvIGluY2x1ZGUgdGhlIHdhcm5pbmcgbWVzc2FnZXMgaW4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCAgICAjIHlvdSBjYW4gYWxzbyBkZWNpZGUgd2hldGhlciB0byBpbmNsdWRlIHRoZSBvdXRwdXQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbiB0aGUgb3V0cHV0IGZpbGUuDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQQ0KICAgICAgICAgICAgICAgICAgICAgICkgIA0KYGBgDQoNCg0KDQpcDQoNCiMgSW50cm9kdWN0aW9uDQoNCkV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgKEVEQSkgaXMgYSBjcml0aWNhbCBwcm9jZXNzIGluIGRhdGEgc2NpZW5jZSB0aGF0IGludm9sdmVzIHN1bW1hcml6aW5nIHRoZSBtYWluIGNoYXJhY3RlcmlzdGljcyBvZiBhIGRhdGEgc2V0LCBvZnRlbiB3aXRoIHZpc3VhbCBtZXRob2RzLiBUaGUgcHJpbWFyeSBwdXJwb3NlcyBvZiBFREEgYXJlDQoNCioqVW5kZXJzdGFuZGluZyBEYXRhIFN0cnVjdHVyZSoqOiBFREEgaGVscHMgeW91IHVuZGVyc3RhbmQgdGhlIHVuZGVybHlpbmcgc3RydWN0dXJlIG9mIHRoZSBkYXRhLCBpbmNsdWRpbmcgaXRzIGRpc3RyaWJ1dGlvbiwgcGF0dGVybnMsIGFuZCBhbm9tYWxpZXMuDQoNCioqSWRlbnRpZnlpbmcgUmVsYXRpb25zaGlwcyoqOiBJdCBhbGxvd3MgeW91IHRvIGlkZW50aWZ5IHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB2YXJpYWJsZXMsIHdoaWNoIGNhbiBpbmZvcm0gZmVhdHVyZSBzZWxlY3Rpb24gYW5kIGVuZ2luZWVyaW5nLg0KDQoqKkRldGVjdGluZyBPdXRsaWVycyoqOiBFREEgaGVscHMgaW4gZGV0ZWN0aW5nIG91dGxpZXJzIG9yIHVudXN1YWwgb2JzZXJ2YXRpb25zIHRoYXQgbWlnaHQgYWZmZWN0IHRoZSBwZXJmb3JtYW5jZSBvZiB5b3VyIG1vZGVscy4NCg0KKipDaGVja2luZyBBc3N1bXB0aW9ucyoqOiBJdCBpcyB1c2VkIHRvIGNoZWNrIHRoZSBhc3N1bXB0aW9ucyByZXF1aXJlZCBmb3Igc3RhdGlzdGljYWwgdGVzdHMgYW5kIG1vZGVscy4NCg0KV2l0aCB0aGUgaWRlbnRpZmllZCBwYXR0ZXJucyB0aHJvdWdoIEVEQSwgd2UgY2FuIHNlbGVjdCBhcHByb3ByaWF0ZSB0ZWNobmlxdWVzIHRvIGV4dHJhY3QgbW9yZSBoaWRkZW4gaW5mb3JtYXRpb24gdG8gaW1wcm92ZSB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIHN1YnNlcXVlbnQgbW9kZWxpbmcgYW5kIHJlbGF0ZWQgYW5hbHlzaXMuDQoNCkluIHRoaXMgY2FzZSBzdHVkeSwgd2UgdXNlIGEgcHVibGljbHkgYXZhaWFibGUgZGF0YSBzZXQgYXMgYW4gZXhhbXBsZSB0byBwZXJmb3JtIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMgYW5kIGRldGVjdCBwYXR0ZXJucyBvZiB0aGUgZGF0YQ0KDQoNCiMgRGVzY3JpcHRpb24gb2YgdGhlIERhdGEgDQoNCkEgcG9wdWxhdGlvbiBvZiB3b21lbiB3aG8gd2VyZSBhdCBsZWFzdCAyMSB5ZWFycyBvbGQsIG9mIFBpbWEgSW5kaWFuIGhlcml0YWdlLCBhbmQgbGl2aW5nIG5lYXIgUGhvZW5peCwgQXJpem9uYSwgd2FzIHRlc3RlZCBmb3IgZGlhYmV0ZXMgYWNjb3JkaW5nIHRvIFdvcmxkIEhlYWx0aCBPcmdhbml6YXRpb24gY3JpdGVyaWEuIFRoZSBkYXRhIHdlcmUgY29sbGVjdGVkIGJ5IHRoZSBVUyBOYXRpb25hbCBJbnN0aXR1dGUgb2YgRGlhYmV0ZXMgYW5kIERpZ2VzdGl2ZSBhbmQgS2lkbmV5LiBUaGUgb2JqZWN0aXZlIG9mIHRoZSBkYXRhIHNldCBpcyB0byBkaWFnbm9zdGljYWxseSBwcmVkaWN0IHdoZXRoZXIgb3Igbm90IGEgcGF0aWVudCBoYXMgZGlhYmV0ZXMsIGJhc2VkIG9uIGNlcnRhaW4gZGlhZ25vc3RpYyBtZWFzdXJlbWVudHMgaW5jbHVkZWQgaW4gdGhlIGRhdGEgc2V0LiBTZXZlcmFsIGNvbnN0cmFpbnRzIHdlcmUgcGxhY2VkIG9uIHRoZSBzZWxlY3Rpb24gb2YgdGhlc2UgaW5zdGFuY2VzIGZyb20gYSBsYXJnZXIgZGF0YWJhc2UuIA0KDQpUaGVyZSBhcmUgdHdvIHZlcnNpb25zIG9mIHRoZSBkYXRhIGF2YWlsYWJsZSBpbiB0aGUgcHVibGljIGRvbWFpbi4gVGhpcyBjYXNlIHN0dWR5IHVzZXMgdGhlIHZlcnNpb24gdGhhdCBjb250YWlucyB0aGUgbWlzc2luZyB2YWx1ZXMuIFRoZSB0b3RhbCBudW1iZXIgb2YgcmVjb3JkcyBpbiB0aGlzIGRhdGEgc2V0IGlzIDc2OC4gVGhlIGRhdGEgc2V0IGNvbnNpc3RzIG9mIDkgdmFyaWFibGVzIGluY2x1ZGluZyB0aGUgcmVzcG9uc2UgdmFyaWFibGUgd2l0aCB0aGUgbmFtZSBgZGlhYmV0ZXNgLiBQcmVkaWN0b3IgdmFyaWFibGVzIGluY2x1ZGUgdGhlIG51bWJlciBvZiBwcmVnbmFuY2llcyB0aGUgcGF0aWVudCBoYXMgaGFkLCB0aGVpciBCTUksIGluc3VsaW4gbGV2ZWwsIGFnZSwgYW5kIHNvIG9uLiBBIGRldGFpbGVkIGRlc2NyaXB0aW9uIG9mIHRoZSB2YXJpYWJsZXMgaXMgZ2l2ZW4gYmVsb3cNCg0KYHByZWduYW50YDogTnVtYmVyIG9mIHRpbWVzIHByZWduYW50DQoNCmBnbHVjb3NlYDogUGxhc21hIGdsdWNvc2UgY29uY2VudHJhdGlvbiAyIGhvdXJzIGluIGFuIG9yYWwgZ2x1Y29zZSB0b2xlcmFuY2UgdGVzdA0KDQpgcHJlc3N1cmVgOiBEaWFzdG9saWMgYmxvb2QgcHJlc3N1cmUgKG1tIEhnKQ0KDQpgdHJpY2Vwc2A6IFRyaWNlcHMgc2tpbiBmb2xkIHRoaWNrbmVzcyAobW0pDQoNCmBpbnN1bGluYDogMi1Ib3VyIHNlcnVtIGluc3VsaW4gKG11IFUvbWwpDQoNCmBtYXNzYDogQm9keSBtYXNzIGluZGV4ICh3ZWlnaHQgaW4ga2cvKGhlaWdodCBpbiBtKV4yKQ0KDQpgcGVkaWdyZWVgOiBEaWFiZXRlcyBwZWRpZ3JlZSBmdW5jdGlvbg0KDQpgYWdlYDogQWdlICh5ZWFycykNCg0KYGRpYWJldGVzYDogb3V0Y29tZSBjbGFzcyB2YXJpYWJsZSAoJ25lZycgb3IgJ3BvcycpDQoNCkEgY29weSBvZiB0aGlzIHB1YmxpY2x5IGF2YWlsYWJsZSBkYXRhIGlzIHN0b3JlZCBhdCA8aHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vZGF0YXNldHMvUGltYURpYWJldGVzL1BpbWFJbmRpYW5zRGlhYmV0ZXMyLmNzdj4uDQoNCmBgYHtyfQ0KUGltYURpYWJldGVzID0gcmVhZC5jc3YoImh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL2RhdGFzZXRzL1BpbWFEaWFiZXRlcy9QaW1hSW5kaWFuc0RpYWJldGVzMi5jc3YiKVssIC0xXQ0Kc3VtbWFyeShQaW1hRGlhYmV0ZXMpDQpgYGANCg0KDQoNCiMjIEhhbmRsaW5nIE1pc3NpbmcgVmFsdWUNCg0KVGhlIGFib3ZlIHN1bW1hcnkgdGFibGUgaW5kaWNhdGVzIHRoYXQgZmVhdHVyZSB2YXJpYWJsZXMgYGdsdWNvc2VgLCBgcHJlc3N1cmVgLCBgdHJpY2Vwc2AsIGBpbnN1bGluYCwgYW5kIGBtYXNzYCBoYXZlIG1pc3NpbmcgdmFsdWVzLiBgaW5zdWxpbmAgaGFzIG5lYXJseSA1MCUgbWlzc2luZyB2YWx1ZXMuIGB0cmljZXBzYCBoYXMgMjI3IG1pc3NpbmcgdmFsdWVzLiBUaGUgb3RoZXIgdGhyZWUgdmFyaWFibGVzIGhhdmUgYSB2ZXJ5IGxvdyBwZXJjZW50YWdlIG9mIG1pc3NpbmcgdmFsdWVzLiANCg0KDQojIyBNaXNzaW5nIFZhbHVlIHZzIE5vIFZhbHVlDQoNCk1pc3NpbmcgdmFsdWUgbWVhbnMgdGhhdCB0aGUgaW5mb3JtYXRpb24gaXMgYXZhaWxhYmxlIGJ1dCBub3QgY29sbGVjdGVkIHdoaWxlIG5vIHZhbHVlIG1lYW5zIHRoYXQgdGhlIHZhbHVlIGRvZXMgbm90IGV4aXN0LiANCg0KUmVwbGFjaW5nIHRoZSBtaXNzaW5nIHZhbHVlcyB3aXRoIHByb3h5IHZhbHVlcyAoaW1wdXRhdGlvbikgb3IgZGVsZXRpbmcgdGhlbSBmcm9tIHRoZSBkYXRhIGFyZSB0aGUgd2F5cyBvZiBoYW5kbGluZyBtaXNzaW5nIHZhbHVlcy4gTW9zdCBzb2Z0d2FyZSBwcm9ncmFtcyAqYXV0b21hdGljYWxseSBkZWxldGUgYWxsIHJlY29yZHMgd2l0aCBtaXNzaW5nIGNvbXBvbmVudHMqIGZyb20gdGhlIGRhdGEgYmVmb3JlIG1vZGVsaW5nIGlmIHRoZSBtaXNzaW5nIHZhbHVlIGlzc3VlIGlzIG5vdCBoYW5kbGVkLiANCg0KYG5vLXZhbHVlYCBzaG91bGQgYmUgbmV2ZXIgaW1wdXRlZCBpbiB0aGUgZGF0YSBwcm9jZXNzaW5nLiBUaGUgd2F5cyBvZiBoYW5kbGluZyBgbm8gdmFsdWVgIGlzIHRvIGVpdGhlciBkcm9wIGFsbCByZWNvcmRzIHdpdGggYG5vIHZhbHVlYCBjb21wb25lbnRzIG9yIHRoZSBmZWF0dXJlIHZhcmlhYmxlcyB0aGF0IGhhdmUgYG5vIHZhbHVlc2AuIFRoZSBmb3JtZXIgd2lsbCBjaGFuZ2UgdGhlIHN0dWR5IHBvcHVsYXRpb24gYW5kIHRoZSBsYXR0ZXIgd2lsbCBsZWFkIHRvIGEgbG9zcyBvZiBpbmZvcm1hdGlvbi4gDQoNClwNCg0KIyMgR2x1Y29zZSBUb2xlcmFuY2UgKGBnbHVjb3NlYCkgdnMgMi1Ib3VyIFNlcnVtIEluc3VsaW4gKGBpbnN1bGluYCkNCg0KQm90aCBgZmFzdGluZyBpbnN1bGluIHRlc3RgIGFuZCBgZ2x1Y29zZSB0b2xlcmFuY2UgdGVzdGAgYXJlIHVzZWQgaW4gZGlhYmV0ZXMgZGlhZ25vc2lzLCB0aGVyZWZvcmUsIHZhcmlhYmxlcyBgZ2x1Y29zZWAgYW5kIGBpbnN1bGluYCBhcmUgY29ycmVsYXRlZC4gU2luY2UgbmVhcmx5IDUwJSBvZiBwYXRpZW50cyBkaWQgbm90IGRvIHRoZSBpbnN1bGluIHRlc3QuIFRoZXJlZm9yZSwgd2UgY2FuIHVzZSBgZ2x1Y29zZWAgdG8gaW1wdXRlIHRoZSBtaXNzaW5nIHZhbHVlcyBpbiBgaW5zdWxpbmAuIFdlIGZpcnN0IGxvb2sgYXQgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHR3byB2YXJpYWJsZXMgYmFzZWQgb24gdGhlIGNvbXBsZXRlIGRhdGEuDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NX0NCnBhcihtZnJvdyA9IGMoMSwyKSkNCnBsb3QoUGltYURpYWJldGVzJGdsdWNvc2UsIFBpbWFEaWFiZXRlcyRpbnN1bGluLCB4bGFiID0gIkdsdWNvc2UgTGV2ZWwiLCB5bGFiID0gIkluc3VsaW4gTGV2ZWwiKQ0KcGxvdChQaW1hRGlhYmV0ZXMkZ2x1Y29zZSwgbG9nKFBpbWFEaWFiZXRlcyRpbnN1bGluKSwgeGxhYiA9ICJHbHVjb3NlIExldmVsIiwgeWxhYiA9ICJsb2cgSW5zdWxpbiBMZXZlbCIpDQpgYGANCg0KDQoNClRoZSBzY2F0dGVyIHBsb3Qgc2hvd3MgdGhhdCB0aGUgbG9nYXJpdGhtIG9mIHRoZSBpbnN1bGluIGxldmVsIGFuZCB0aGUgZ2x1Y29zZSBsZXZlbCBhcmUgaGlnaGx5IGxpbmVhcmx5IGNvcnJlbGF0ZWQuIFdlIGNhbiB1c2UgdGhpcyByZWxhdGlvbnNoaXAgdG8gaW1wdXRlIHRoZSBsb2dhcml0aG0gb2YgaW5zdWxpbiBsZXZlbCBiYXNlZCBvbiB0aGUgbm8tbWlzc2luZyBnbHVjb3NlIGxldmVsLiBTaW5jZSB3ZSB3aWxsIHVzZSB0aGlzIGRhdGEgc2V0IHRvIGJ1aWxkIHByZWRpY3RpdmUgbW9kZWxzLCB0aGUgbG9nYXJpdGhtIG9mIGluc3VsaW4gd2lsbCBiZSB1c2VkIGRpcmVjdGx5IGluIHRoZSBzdWJzZXF1ZW50IG1vZGVscyBhbmQgYWxnb3JpdGhtcy4NCg0KYGBge3J9DQppbXB1dGUuaW5zdWxpbi5sbSA9IGxtKGxvZyhpbnN1bGluWy00NDZdKSB+IGdsdWNvc2VbLTQ0Nl0sIGRhdGEgPSBQaW1hRGlhYmV0ZXMpDQpzdW1tYXJ5KGltcHV0ZS5pbnN1bGluLmxtKQ0KYGBgDQoNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD03fQ0KcGFyKG1mcm93ID0gYygyLDIpKQ0KcGxvdChpbXB1dGUuaW5zdWxpbi5sbSkNCmBgYA0KDQpOZXh0LCB3ZSB1c2UgdGhlIGZvbGxvd2luZyBsaW5lYXIgcmVncmVzc2lvbiB0byBpbXB1dGUgdGhlIG1pc3NpbmcgdmFsdWVzIGluIGBpbnN1bGluYC4NCg0KYGBge3J9DQpnbHVjb3NlID0gUGltYURpYWJldGVzJGdsdWNvc2UNCmltcHV0ZS5sb2cuaW5zdWxpbiA9IGxvZyhQaW1hRGlhYmV0ZXMkaW5zdWxpbikNCm49bGVuZ3RoKGltcHV0ZS5sb2cuaW5zdWxpbikNCmZvciAoaSBpbiAxOm4pew0KICBpZiAoaXMubmEoaW1wdXRlLmxvZy5pbnN1bGluW2ldKSA9PSBUUlVFICYmIGlzLm5hKGdsdWNvc2VbaV0pID09IEZBTFNFKSBpbXB1dGUubG9nLmluc3VsaW5baV0gPSBzdW0oY29lZihpbXB1dGUuaW5zdWxpbi5sbSkqYygxLGdsdWNvc2VbaV0pKSArIHNhbXBsZShyZXNpZChpbXB1dGUuaW5zdWxpbi5sbSksMSkNCn0NCmBgYA0KDQoNClZpc3VhbCBjb21wYXJpc29uIG9mIHRoZSBkaXN0cmlidXRpb24gYmV0d2VlbiB0aGUgb3JpZ2luYWwgYGluc3VsaW5gIGFuZCB0aGUgaW1wdXRlZCBgaW5zdWxpbmAuDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NX0NCmRlbi5vcmlnLmluc3VsaW4gPSBkZW5zaXR5KG5hLm9taXQobG9nKFBpbWFEaWFiZXRlcyRpbnN1bGluKSkpDQpkZW4uaW1wdXRlLmluc3VsaW4gPSBkZW5zaXR5KG5hLm9taXQoaW1wdXRlLmxvZy5pbnN1bGluKSkNCnBsb3QoZGVuLmltcHV0ZS5pbnN1bGluLCB4bGFiPSJsb2cgaW5zdWxpbiIsIG1haW4gPSAiZGVuc2l0eSBjdXJ2ZSBvZiBsb2cgb2Ygb3JpZ2luYWwgYW5kIGltcHV0ZWQgaW5zdWxpbiIsDQogICAgIGNvbCA9ICJyZWQiKQ0KbGluZXMoZGVuLm9yaWcuaW5zdWxpbiwgY29sID0gImJsdWUiKQ0KbGVnZW5kKCJ0b3ByaWdodCIsIGMoIm9yaWdpbmFsIGxvZyBpbnN1bGluIiwgImlucHV0ZWQgbG9nIGluc3VsaW4iKSwgY29sID0gYygiYmx1ZSIsICJyZWQiKSwgbHR5ID0gcmVwKDEsMiksIGNleCA9IDAuOSwgYnR5ID0gIm4iKQ0KYGBgDQoNClRoZSBhYm92ZSBkZW5zaXR5IGN1cnZlcyBzaG93IHRoYXQgZGlzdHJpYnV0aW9ucyBvZiB0aGUgaW1wdXRlZCBsb2cgaW5zdWxpbiBhbmQgb3JpZ2luYWwgbG9nIGluc3VsaW4gbGV2ZWxzIGFyZSBjbG9zZSB0byBlYWNoIG90aGVyLiANCg0KDQpgYGB7cn0NClBpbWFEaWFiZXRlcyRpbXB1dGUubG9nLmluc3VsaW4gPSBpbXB1dGUubG9nLmluc3VsaW4NCmBgYA0KDQoNClwNCg0KIyMgVHJpY2VwcyBTa2luZm9sZCBUaGlja25lc3MgKGB0cmljZXBzYCkgdnMgQm9keSBNYXNzIEluZGV4IChgbWFzc2ApDQoNCkNsaW5pY2FsIHZhcmlhYmxlcyBgdHJpY2Vwc2AgKHRyaWNlcHMgc2tpbi1mb2xkIHRoaWNrbmVzcywgc2VlIHRoZSBmb2xsb3dpbmcgZmlndXJlIHRvIHNlZSBob3cgaXQgaXMgbWVhc3VyZWQpIGFuZCBgbWFzc2AgKGJvZHkgbWFzcyBpbmRleCkgYXJlIGNsaW5pY2FsbHkgY29ycmVsYXRlZC4gDQoNCmBgYHtyIGVjaG89RkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoID0gIjMwJSIsIGZpZy5jYXA9IkZpZ3VyZSAxLiBNZWFzdXJlbWVudCBvZiB0cmljZXBzIHNraW5mb2xkIHVzaW5nIGEgTGFuZ2UgY2FsaXBlci4gV2l0aCB0aGUgc3ViamVjdCdzIGFybSBpbiBhIHJlbGF4ZWQgcG9zaXRpb24sIHRoZSBza2luZm9sZCBpcyBwaWNrZWQgd2l0aCB0aHVtYiBhbmQgaW5kZXggZmluZ2VycyBhdCB0aGUgbWlkcG9pbnQgb2YgdGhlIGFybS4ifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL3RyaWNlcHRzLnBuZyIpDQpgYGANCg0KYHRyaWNlcHNgIGhhcyBuZWFybHkgMzAlIG1pc3NpbmcgdmFsdWVzIGFuZCBgbWFzc2AgaGFzIGEgZmV3IG1pc3NpbmcgdmFsdWVzLiBXZSBjYW4gdXNlIHRoZSBpbmZvcm1hdGlvbiBpbiBgbWFzc2AgdG8gaW1wdXRlIHRoZSBtaXNzaW5nIHZhbHVlcyBpbiB0cmljZXBzIC0gc2luZ2xlIGltcHV0YXRpb24gd2l0aCBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLiBUbyBwZXJmb3JtIGltcHV0YXRpb24sDQoNCjEuIGZpdCBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHdpdGggYHRyaWNlcHNgIGJlaW5nIHRoZSByZXNwb25zZSBhbmQgYG1hc3NgIGFzIHRoZSBwcmVkaWN0b3IuDQoNCjIuIHVzZSB0aGUgYWJvdmUtZml0dGVkIHJlZ3Jlc3Npb24gdG8gcHJlZGljdCBgdHJpY2Vwc2Agb24gbm9uLW1pc3NpbmcgYG1hc3NgLg0KDQozLiBpbXB1dGUgdGhlIG1pc3NpbmcgdmFsdWUgaW4gYHRyaWNlcHNgIHdpdGggdGhlICpwcmVkaWN0ZWQqIGB0cmljZXBzYC4NCg0KTm90ZSB0aGF0IGluIFIsIHJlY29yZHMgd2l0aCBtaXNzaW5nIGNvbXBvbmVudHMgd2lsbCBiZSBhdXRvbWF0aWNhbGx5IGRlbGV0ZWQgaW4gdGhlIG1vZGVsaW5nIHByb2Nlc3MuDQoNCmBgYHtyfQ0KaW1wdXRlLmxtID0gbG0odHJpY2Vwc1stNTgwXSB+IG1hc3NbLTU4MF0sIGRhdGEgPSBQaW1hRGlhYmV0ZXMpDQpzdW1tYXJ5KGltcHV0ZS5sbSkNCmBgYA0KDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9N30NCnBhcihtZnJvdyA9IGMoMiwyKSkNCnBsb3QoaW1wdXRlLmxtKQ0KYGBgDQoNCg0KVGhlIGFib3ZlLWZpdHRlZCByZWdyZXNzaW9uIGxpbmUgd2lsbCBiZSB1c2VkIHRvICoqaW1wdXRlKiogdGhlIG1pc3NpbmcgdmFsdWVzIGluIGB0cmljZXBzYCBpbiB0aGUgZm9sbG93aW5nLg0KDQpgYGB7cn0NCm1hc3MgPSBQaW1hRGlhYmV0ZXMkbWFzcw0KaW1wdXRlLnRyaWNlcHMgPSBQaW1hRGlhYmV0ZXMkdHJpY2Vwcw0Kbj1sZW5ndGgoaW1wdXRlLnRyaWNlcHMpDQpmb3IgKGkgaW4gMTpuKXsNCiAgaWYgKGlzLm5hKGltcHV0ZS50cmljZXBzW2ldKSA9PSBUUlVFICYmIGlzLm5hKG1hc3NbaV0pID09IEZBTFNFKSBpbXB1dGUudHJpY2Vwc1tpXSA9IHN1bShjb2VmKGltcHV0ZS5sbSkqYygxLG1hc3NbaV0pKSArIHNhbXBsZShyZXNpZChpbXB1dGUubG0pLDEpDQp9DQpgYGANCg0KDQpgYGB7cn0NClBpbWFEaWFiZXRlcyRpbXB1dGUudHJpY2VwcyA9IGltcHV0ZS50cmljZXBzDQpgYGANCg0KTmV4dCwgd2UgY2hlY2sgd2hldGhlciB0aGUgbWlzc2luZyB2YWx1ZXMgaW4gYHRyaWNlcHNgIHdlcmUgYXBwcm9wcmlhdGVseSBpbXB1dGVkLiAgDQoNCldlIGxvb2sgYXQgdGhlIGRlbnNpdHkgY3VydmVzIG9mIGBpbXB1dGUudHJpY2Vwc2AgYW5kIHRoZSBvcmlnaW5hbCBgdHJpY2Vwc2AgdG8gc2VlIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgaW1wdXRhdGlvbiBhbmQgd2hldGhlciBhIGRpc2NyZXRpemF0aW9uIGlzIG5lZWRlZC4NCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD02fQ0KZGVuLnRyaSA9IGRlbnNpdHkobmEub21pdChQaW1hRGlhYmV0ZXMkdHJpY2VwcykpDQpkZW4uaW1wdXQudHJpID0gZGVuc2l0eShuYS5vbWl0KFBpbWFEaWFiZXRlcyRpbXB1dGUudHJpY2VwcykpDQpwbG90KGRlbi5pbXB1dC50cmksIGNvbCA9ICJyZWQiLCB4bGFiID0gInRyaWNlcHMiLCB5bGFiID0gImRlbnNpdHkiLCBtYWluID0gIm9yaWdpbmFsIHRyaWNlcHMgdnMgaW1wdXRlZCB0cmljZXBzIikNCmxpbmVzKGRlbi50cmksIGNvbCA9ICJibHVlIikNCmxlZ2VuZCgidG9wcmlnaHQiLCBjKCJJbnB1dGVkIFRyaWNlcHMiLCAiT3JpZ2luYWwgVHJpY2VwcyIpLCBjb2w9YygicmVkIiwgImJsdWUiKSwgbHR5ID1yZXAoMSwyKSwgYnR5PSJuIiwgY2V4ID0gMC44KQ0KYGBgDQoNClRoZSBhYm92ZSBkZW5zaXR5IGN1cnZlcyBpbmRpY2F0ZSB0aGF0IA0KDQoqIFRoZSB0d28gZGlzdHJpYnV0aW9ucyBhcmUgYWxtb3N0IGlkZW50aWNhbCwgYW5kIA0KDQoqIGJvdGggZGlzdHJpYnV0aW9ucyBhcmUgYWxtb3N0IHN5bW1ldHJpYyAoZXhjZXB0IGZvciBvbmUgb3V0bGllciBpbiB0aGUgb3JpZ2luYWwgZGF0YSkuDQoNCg0KU2luY2UgdGhlIG1pc3NpbmcgdmFsdWVzIGluIGB0cmljZXBzYCB3ZXJlIGFwcHJvcHJpYXRlbHkgaW1wdXRlZCwgd2UgbmV4dCBhZGQgdGhlIGBpbXB1dGUudHJpY2Vwc2AgdG8gdGhlIG9yaWdpbmFsIGRhdGEgZnJhbWUgYW5kIGRyb3AgdGhlIG9yaWdpbmFsIGB0cmljZXBzYC4NCg0KDQpUbyBjbG9zZSB0aGlzIGltcHV0YXRpb24gc2VjdGlvbiwgd2UgcmVvcmdhbml6ZWQgdGhlIGRhdGEgc2V0IGJ5IGRyb3BwaW5nIHRoZSBvcmlnaW5hbCB2YXJpYWJsZXMgYW5kIGtlZXBpbmcgdGhlIGltcHV0ZWQgdmFyaWFibGVzLiBBdCB0aGUgc2FtZSB0aW1lLCB3ZSBhbHNvIGRlbGV0ZSBhbGwgcmVjb3JkcyB3aXRoIG1pc3NpbmcgY29tcG9uZW50cy4NCg0KYGBge3J9DQpQaW1hRGlhYmV0ZXMgPSBuYS5vbWl0KFBpbWFEaWFiZXRlc1ssIGMoInByZWduYW50IiwgImdsdWNvc2UiLCAicHJlc3N1cmUiLCAibWFzcyIsICJwZWRpZ3JlZSIsICJhZ2UiICwiaW1wdXRlLnRyaWNlcHMiLCAiZGlhYmV0ZXMiKV0pDQpgYGANCg0KXA0KDQojIEFzc2VzcyBEaXN0cmlidXRpb25zDQoNClRoaXMgc3Vic2VjdGlvbiBmb2N1c2VzIG9uIHRoZSBwb3RlbnRpYWwgZGlzY3JldGl6YXRpb24gb2YgY29udGludW91cyB2YXJpYWJsZXMgYW5kIGdyb3VwaW5nIHNwYXJzZSBjYXRlZ29yaWVzIG9mIGNhdGVnb3J5IHZhcmlhYmxlcyBiYXNlZCBvbiB0aGVpciBkaXN0cmlidXRpb24uDQoNCg0KIyMgRGlzY3JldGl6aW5nIENvbnRpbnVvdXMgVmFyaWFibGVzDQoNClRoZSBhYm92ZSBwYWlyd2lzZSBzY2F0dGVyIHBsb3Qgc2hvd3MgdGhhdCBgZ2x1Y29zZWAsIGBwcmVzc3VyZWAoZGlhc3RvbGljIHJlYWRpbmcpLCAgYW5kIGBhZ2VgIGFyZSB1c3VhbGx5IGRpc2NyZXRpemVkIGluIHRoZSBjbGluaWNhbCBzdHVkeS4gV2Ugd2lsbCB1c2UgdGhlIGNsaW5pY2FsIHN0YW5kYXJkcyBhbmQgcHJhY3RpY2VzIHRvIGRpc2NyZXRpemUgdGhlc2UgdmFyaWFibGVzIA0KDQpBY2NvcmRpbmcgdG8gTWVkaWNhbCBOZXdzIFRvZGF5IChodHRwczovL3d3dy5tZWRpY2FsbmV3c3RvZGF5LmNvbS9hcnRpY2xlcy9hMWMtY2hhcnQtZGlhYmV0ZXMtbnVtYmVycyNhLTEtYy1jaGFydCkuIFRoZSBnbHVjb3NlIGxldmVscyAkPCAxMTckLCAkWzExNywgMTM3XSQsICQ+MTM3JCBpbmRpY2F0ZSBub3JtYWwsIHByZS1kaWFiZXRlcywgYW5kIGRpYWJldGVzLiAgDQoNCg0KQWNjb3JkaW5nIHRvIHRoZSBOYXRpb25hbCBEaWFiZXRlcyBTdGF0aXN0aWNzIFJlcG9ydCAoDQo8aHR0cHM6Ly93d3cuY2RjLmdvdi9tZWRpYS9yZWxlYXNlcy8yMDE3L3AwNzE4LWRpYWJldGVzLXJlcG9ydC5odG1sIzp+OnRleHQ9UmF0ZXMlMjBvZiUyMGRpYWdub3NlZCUyMGRpYWJldGVzJTIwaW5jcmVhc2VkLG9sZGVyJTJDJTIwMjUlMjBwZXJjZW50JTIwaGFkJTIwZGlhYmV0ZXM+KSwgcmF0ZXMgb2YgZGlhZ25vc2VkIGRpYWJldGVzIGluY3JlYXNlZCB3aXRoIGFnZS4gQW1vbmcgYWR1bHRzIGFnZXMgMTgtNDQsIDQgcGVyY2VudCBoYWQgZGlhYmV0ZXMuIEFtb25nIHRob3NlIGFnZXMgNDUtNjQgeWVhcnMsIDE3IHBlcmNlbnQgaGFkIGRpYWJldGVzLiBBbmQgYW1vbmcgdGhvc2UgYWdlcyA2NSB5ZWFycyBhbmQgb2xkZXIsIDI1IHBlcmNlbnQgaGFkIGRpYWJldGVzLg0KDQoNCkFjY29yZGluZyB0byBUaGUgU2V2ZW50aCBSZXBvcnQgb2YgdGhlIEpvaW50IE5hdGlvbmFsIENvbW1pdHRlZSBvbiBQcmV2ZW50aW9uLCBEZXRlY3Rpb24sIEV2YWx1YXRpb24sIGFuZCBUcmVhdG1lbnQgb2YgSGlnaCBCbG9vZCBQcmVzc3VyZSAoMjAwMyBHdWlkZWxpbmUsIDxodHRwczovL3d3dy5uaGxiaS5uaWguZ292L2ZpbGVzL2RvY3MvZ3VpZGVsaW5lcy9leHByZXNzLnBkZj4pLCBUaGUgbm9ybWFsIGRpYXN0b2xpYyBwcmVzc3VyZSBpcyBsZXNzIHRoYW4gODAgbW0gSGcsIGF0IHJpc2sgZGlhc3RvbGljIHJlYWRpbmcgaXMgYmV0d2VlbiA4MCBtbSBIZyBhbmQgOTAgbW0gSGcsIGFibm9ybWFsIChoeXBlcnRlbnNpb24pIGRpYXN0b2xpYyByZWFkaW5nIGlzIGhpZ2hlciB0aGFuIDkwIG1tIEhnLg0KDQpXZSB3aWxsIGRpc2NyZXRpemUgdGhlc2UgdGhyZWUgdmFyaWFibGVzIGZvciBmdXR1cmUgbW9kZWxzIGFuZCBhbGdvcml0aG1zLg0KDQoNCmBgYHtyfQ0KUGltYURpYWJldGVzJGdycC5nbHVjb3NlIDwtIGlmZWxzZShQaW1hRGlhYmV0ZXMkZ2x1Y29zZSA8IDExNywgJygwLCAxMTcpJywNCiAgICAgICAgICAgICAgIGlmZWxzZShQaW1hRGlhYmV0ZXMkZ2x1Y29zZSA+IDEzNywgJz4gMTM3JywgJ1sxMTcsMTM3XScpKQ0KDQpQaW1hRGlhYmV0ZXMkZ3JwLmRpYXN0b2xpYyA8LSBpZmVsc2UoUGltYURpYWJldGVzJHByZXNzdXJlIDwgODAsICcoMCwgODApJywNCiAgICAgICAgICAgICAgIGlmZWxzZShQaW1hRGlhYmV0ZXMkcHJlc3N1cmUgPiA5MCwgJz4gOTAnLCAnWzgwLDkwXScpKQ0KDQpQaW1hRGlhYmV0ZXMkZ3JwLmFnZSA8LSBpZmVsc2UoUGltYURpYWJldGVzJGFnZSA8PSA0NCwgJ1syMSwgNDRdJywNCiAgICAgICAgICAgICAgIGlmZWxzZShQaW1hRGlhYmV0ZXMkYWdlID49IDY1LCAnNjUrJywgJ1s0NSwgNjRdJykpDQpgYGANCg0KXA0KDQojIyBHcm91cGluZyBTcGFyc2UgQ2F0ZWdvcmllcw0KDQpUaGUgbnVtYmVyIG9mIHRpbWVzIHByZWduYW50IGBwcmVnbmFudGAgaXMgYSBkaXNjcmV0ZSBudW1lcmljYWwgdmFyaWFibGUuIFdlIGNvdWxkIGFsc28gY29uc2lkZXIgaXQgYXMgYW4gb3JkaW5hbCBjYXRlZ29yaWNhbCB2YXJpYWJsZS4NCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD00fQ0KcHJlZ25hbmN5ID0gdGFibGUoUGltYURpYWJldGVzJHByZWduYW50KQ0KYmFycGxvdChwcmVnbmFuY3ksIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIHByZWduYWNpZXMiLCB4bGFiID0gIlByZWduYW50IFRpbWVzIikNCmBgYA0KDQpUaGVyZSBhcmUgYSBmZXcgc3BhcnNlIGNhdGVnb3JpZXMgaW4gdGhlIHZhcmlhYmxlLCBzbyB3ZSBkZWNpZGVkIHRvIGdyb3VwIHRoaXMgdmFyaWFibGUgaW50byB0aGUgZm9sbG93aW5nOg0KMCwgMSwgMiwgMy00LCA1LTcsIDgrLg0KDQoNCmBgYHtyfQ0KUGltYURpYWJldGVzJGdycC5wcmVnbmFudCA8LSBpZmVsc2UoUGltYURpYWJldGVzJHByZWduYW50ID09IDAsICcwJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBpbWFEaWFiZXRlcyRwcmVnbmFudCA9PSAxLCAnMScsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUGltYURpYWJldGVzJHByZWduYW50ID09IDIsICcyJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKChQaW1hRGlhYmV0ZXMkcHJlZ25hbnQgPT0gMyB8IFBpbWFEaWFiZXRlcyRwcmVnbmFudCA9PSA0KSwgJzMtNCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZSgoUGltYURpYWJldGVzJHByZWduYW50ID09IDUgfCBQaW1hRGlhYmV0ZXMkcHJlZ25hbnQgPT0gNiB8IFBpbWFEaWFiZXRlcyRwcmVnbmFudCA9PSA3KSwgJzUtNycsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUGltYURpYWJldGVzJHByZWduYW50ID49IDgsICc4KycsICJOQSIpKSkpKSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCmBgYA0KDQpBcyB0aGUgbGFzdCBzdGVwLCB3ZSBvbmx5IGtlZXAgdGhvc2UgdmFyaWFibGVzIHRvIGJlIHVzZWQgaW4gdGhlIHN1YnNlcXVlbnQgbW9kZWxpbmcuDQoNCmBgYHtyfQ0KdmFyLm5hbWVzID0gYygibWFzcyIsICJwZWRpZ3JlZSIsICJpbXB1dGUudHJpY2VwcyIsICJncnAuZ2x1Y29zZSIsICJncnAuZGlhc3RvbGljIiwgImdycC5hZ2UiLCAiZ3JwLnByZWduYW50IiwgImRpYWJldGVzIikgDQpQaW1hRGlhYmV0ZXMgPSBQaW1hRGlhYmV0ZXNbLCB2YXIubmFtZXNdDQpgYGANCg0KIyMgU2F2ZSBBbmFseXRpYyBEYXRhc2V0DQoNClRoZSBmaW5hbCBhbmFseXRpYyBkYXRhIHNob3VsZCBiZSBzYXZlZCBhcyBwZXJtYW5lbnQgZGF0YSBmb3Igc3Vic2VxdWVudCBhbmFseXNpcyBhbmQgbW9kZWxpbmcgYW5kIHRoZSBzYXZlZCBkYXRhIHNldCB0byB0aGUgR2l0SHViIGRhdGEgcmVwb3NpdG9yeSBmb3IgZWFzeSBhY2Nlc3MgaW4gdGhlIGZ1dHVyZS4NCg0KYGBge3J9DQp3cml0ZS5jc3YoUGltYURpYWJldGVzLCAiQzpcXFVzZXJzXFw3NUNQRU5HXFxPbmVEcml2ZSAtIFdlc3QgQ2hlc3RlciBVbml2ZXJzaXR5IG9mIFBBXFxEZXNrdG9wXFxjcGVuZ1xcV0NVLVRlYWNoaW5nXFwyMDIzU3VtbWVyXFxTVEE1NTFcXHcwM1xcQW5hbHl0aWNQaW1hRGlhYmV0ZXMuY3N2IikNCmBgYA0KDQpUaGUgYWJvdmUgY3N2IGZpbGUgaXMgYWxzbyB1cGxvYWRlZCB0byB0aGUgR2l0SHViIGRhdGEgcmVwb3NpdG9yeSBhdCA8aHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vU1RBNTUxL3cwMy9BbmFseXRpY1BpbWFEaWFiZXRlcy5jc3Y+Lg0KDQoNCg0KIyBQYWlyd2lzZSBBc3NvY2lhdGlvbg0KDQpEZXBlbmRpbmcgb24gdGhlIHR5cGVzIG9mIHZhcmlhYmxlcywgdGhlcmUgYXJlIHRocmVlIGRpZmZlcmVudCBjb21iaW5hdGlvbnMgb2YgdHdvIHZhcmlhYmxlczogdHdvIG51bWVyaWMgdmFyaWFibGVzLCB0d28gY2F0ZWdvcmljYWwgdmFyaWFibGVzLCBvbmUgbnVtZXJpYyB2YXJpYWJsZSwgYW5kIG9uZSBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gV2Ugd2lsbCBhc3Nlc3MgdGhlIGFzc29jaWF0aW9uIGJldHdlZW4gdHdvIHZhcmlhYmxlcyBncmFwaGljYWxseSBiYXNlZCBvbiB0aGUgYWJvdmUgdGhyZWUgc2NlbmFyaW9zLg0KDQoNCiMjIFR3byBvciBNb3JlIE51bWVyaWMgVmFyaWFibGVzDQoNClRoZSBiZXN0IHZpc3VhbCB0b29sIGZvciBhc3Nlc3NpbmcgcGFpcndpc2UgbGluZWFyIGFzc29jaWF0aW9uIGJldHdlZW4gdHdvIG51bWVyaWMgdmFyaWFibGVzIGlzIGEgcGFpci13aXNlIHNjYXR0ZXIgcGxvdC4gVGhlIHBhaXItd2lzZSBzY2F0dGVyIHBsb3QgYW5kIGl0cyB2YXJpYW50cyBhcmUgYXZhaWxhYmxlIGluIHNldmVyYWwgZGlmZmVyZW50IFIgcGFja2FnZXMuDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0NCmdncGFpcnMoUGltYURpYWJldGVzLCAgICAgICAgICAjIERhdGEgZnJhbWUNCiAgICAgICAgY29sdW1ucyA9IDE6MywgICAgICAgICAjIENvbHVtbnMNCiAgICAgICAgYWVzKGNvbG9yID0gZGlhYmV0ZXMsICAjIENvbG9yIGJ5IGdyb3VwIChjYXQuIHZhcmlhYmxlKQ0KICAgICAgICAgICAgYWxwaGEgPSAwLjUpKSAgICAgICMgVHJhbnNwYXJlbmN5DQpgYGANCg0KDQoNClRoZSBvZmYtZGlhZ29uYWwgcGxvdHMgYW5kIG51bWJlcnMgaW5kaWNhdGUgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHBhaXItd2lzZSBudW1lcmljIHZhcmlhYmxlcy4gQXMgZXhwZWN0ZWQsIHRyaWNlcHMgYW5kIG1hc3MgYXJlIHNpZ25pZmljYW50bHkgY29ycmVsYXRlZC4gT3RoZXIgcGFpcmVkIHZhcmlhYmxlcyBoYXZlIHdlYWsgY29ycmVsYXRpb25zLg0KDQoNClRoZSBtYWluIGRpYWdvbmFsIHN0YWNrZWQgZGVuc2l0eSBjdXJ2ZXMgc2hvdyB0aGUgcG90ZW50aWFsIGRpZmZlcmVuY2UgaW4gdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdW5kZXJseWluZyBudW1lcmljIHZhcmlhYmxlIGluIGRpYWJldGVzIGFuZCBkaWFiZXRlcy1mcmVlIGdyb3Vwcy4gVGhpcyBtZWFucyB0aGF0IHRoZSBzdGFja2VkIGRlbnNpdHkgY3VydmVzIHNob3cgdGhlIHJlbGF0aW9uIGJldHdlZW4gbnVtZXJpYyBhbmQgY2F0ZWdvcmljYWwgdmFyaWFibGVzLiBUaGVzZSBzdGFja2VkIGRlbnNpdHkgY3VydmVzIGFyZSBub3QgY29tcGxldGVseSBvdmVybGFwcGVkIGluZGljYXRpbmcgc29tZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGVhY2ggb2YgdGhlc2UgbnVtZXJpYyB2YXJpYWJsZXMgYW5kIHRoZSBiaW5hcnkgcmVzcG9uc2UgdmFyaWFibGUuDQoNCkJlY2F1c2Ugb2YgdGhlIGFib3ZlIGludGVycHJldGF0aW9uIGJldHdlZW4gbnVtZXJpYyB2YXJpYWJsZXMgYW5kIHRoZSBiaW5hcnkgcmVzcG9uc2UgdmFyaWFibGUsIHdlIHdpbGwgbm90IG9wZW4gYSBuZXcgc3Vic2VjdGlvbiB0byBpbGx1c3RyYXRlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBhIG51bWVyaWMgdmFyaWFibGUgYW5kIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUuICANCg0KDQpcDQoNCiMjIFR3byBDYXRlZ29yaWNhbCBWYXJpYWJsZXMNCg0KTW9zYWljIHBsb3RzIGFyZSBjb252ZW5pZW50IHRvIHNob3cgd2hldGhlciB0d28gY2F0ZWdvcmljYWwgdmFyaWFibGVzIGFyZSBkZXBlbmRlbnQuIEluIEVEQSwgd2UgYXJlIHByaW1hcmlseSBpbnRlcmVzdGVkIGluIHdoZXRoZXIgdGhlIHJlc3BvbnNlIChiaW5hcnkgaW4gdGhpcyBjYXNlKSBpcyBpbmRlcGVuZGVudCBvZiBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuIFRob3NlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB0aGF0IGFyZSBpbmRlcGVuZGVudCBvZiB0aGUgcmVzcG9uc2UgdmFyaWFibGUgc2hvdWxkIGJlIGV4Y2x1ZGVkIGZyb20gYW55IG9mIHRoZSBzdWJzZXF1ZW50IG1vZGVscyBhbmQgYWxnb3JpdGhtcy4NCg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTh9DQpwYXIobWZyb3cgPSBjKDIsMikpDQptb3NhaWNwbG90KGdycC5nbHVjb3NlIH4gZGlhYmV0ZXMsIGRhdGE9UGltYURpYWJldGVzLGNvbD1jKCJCbHVlIiwiUmVkIiksIG1haW49ImdsdWNvc2UgdnMgZGlhYmV0ZXMiKQ0KbW9zYWljcGxvdChncnAuZGlhc3RvbGljIH4gZGlhYmV0ZXMsIGRhdGE9UGltYURpYWJldGVzLGNvbD1jKCJCbHVlIiwiUmVkIiksIG1haW49ImRpYXN0b2xpYyB2cyBkaWFiZXRlcyIpDQptb3NhaWNwbG90KGdycC5hZ2UgfiBkaWFiZXRlcywgZGF0YT1QaW1hRGlhYmV0ZXMsY29sPWMoIkJsdWUiLCJSZWQiKSwgbWFpbj0iYWdlIHZzIGRpYWJldGVzIikNCm1vc2FpY3Bsb3QoZ3JwLnByZWduYW50IH4gZGlhYmV0ZXMsIGRhdGE9UGltYURpYWJldGVzLGNvbD1jKCJCbHVlIiwiUmVkIiksIG1haW49InByZWduYW50IHZzIGRpYWJldGVzIikNCmBgYA0KDQpUaGUgdG9wIHR3byBtb3NhaWMgcGxvdHMgZGVtb25zdHJhdGUgdGhlIHBvc2l0aXZlIGFzc29jaWF0aW9uIGJldHdlZW4gYGdsdWNvc2VgIGxldmVscyBhbmQgYGRpYXN0b2xpY2AgcmVhZGluZ3MuIFRoZSBib3R0b20gdHdvIG1vc2FpYyBwbG90cyBhbHNvIHNob3cgdGhhdCBkaWFiZXRlcyBpcyBub3QgaW5kZXBlbmRlbnQgb2YgYGFnZWAgYW5kIGBwcmVnbmFudGAgdGltZXMgYmVjYXVzZSB0aGUgcHJvcG9ydGlvbiBvZiBkaWFiZXRlcyBjYXNlcyBpbiBpbmRpdmlkdWFsIGNhdGVnb3JpZXMgaXMgbm90IGlkZW50aWNhbC4NCg0KXA0KDQojIyBPbmUgQ2F0ZWdvcmljYWwgYW5kIE9uZSBOdW1lcmljYWwgVmFyaWFibGUNCg0KQSBib3gtcGxvdCBpcyBhIHR5cGUgb2YgdmlzdWFsIHNob3J0aGFuZCBmb3IgbWVhc3VyZXMgb2YgcG9zaXRpb24gKHBlcmNlbnRpbGVzKSB0aGF0IGRlc2NyaWJlIGEgZGlzdHJpYnV0aW9uLiBJdCBpcyBhbHNvIHVzZWZ1bCBmb3IgaWRlbnRpZnlpbmcgcG90ZW50aWFsIG91dGxpZXJzLiBXaGVuIGFzc2Vzc2luZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYSBudW1lcmljYWwgYW5kIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUsIHdlIGxvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbnVtZXJpY2FsIHZhcmlhYmxlIGluIGVhY2ggY2F0ZWdvcnkuIElmIHRoZSBkaXN0cmlidXRpb25zIGFjcm9zcyB0aGUgY2F0ZWdvcmllcyBhcmUgaWRlbnRpY2FsLCB0aGUgbnVtZXJpY2FsIGFuZCB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGFyZSBub3QgYXNzb2NpYXRlZC4gDQoNCkZvciBlYXNlIG9mIGlsbHVzdHJhdGlvbiwgd2Ugb25seSB1c2UgZGlhbmV0ZXMgc3RhdHVzIGFuZCBnbHVjb3NlIGxldmVsIGluIHRoZSBQaW1hIEluZGlhbiBkaWFiZXRlcyBkYXRhIHNldC4NCg0KYGBge3J9DQpHbHVjb3NlRGlhYmV0ZXMgPSBuYS5vbWl0KFBpbWFEaWFiZXRlc1ssIGMoMSw4KV0pDQpgYGANCg0KDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPSA2LCBmaWcuaGVpZ2h0PTQsIGZpZy5jYXA9Ikdyb3VwIGJveC1wbG90cyBvZiBkaWFiZXRlcyBzdGF0dXMgdnMgZ2x1Y29zZSJ9DQpnZ3Bsb3QoR2x1Y29zZURpYWJldGVzKSArDQogIGFlcyh4ID0gbWFzcywgeSA9IGRpYWJldGVzLCBjb2xvciA9IGRpYWJldGVzKSArDQogIGdlb21fYm94cGxvdCgpICsgDQogIGdndGl0bGUoIlBpbWFJbmRpYW4gRGllYmV0ZXM6ICBkaWFiZXRlcyBzdGF0dXMgdnMgZ2x1Y29zZSIpICsgDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb249InRvcCIpICsNCiAgeGxhYigiZGlhYmV0ZXMgc3RhdHVzIikgKyANCiAgeWxhYigiZ2x1Y29zZSBsZXZlbCIpIA0KYGBgDQoNClRoZSBhYm92ZSBncm91cCBib3gtcGxvdCBzaG93cyB0aGF0IHRoZSBkaXN0cmlidXRpb25zIG9mIGdsdWNvc2UgbGV2ZWxzIG9mIGRpYWJldGVzIGFuZCBkaWFiZXRlcy1mcmVlIHNldHMgYXJlIGRpZmZlcmVudDogbWVhbnMgYW5kIHZhcmlhbmNlcyBhcmUgYWxsIGRpZmZlcmVudC4gSW4gYWRkaXRpb24sIHRoZSBzdWJzZXQgb2YgYG5lZ2AgKHN1YnNldCBvZiBkaWFiZXRlcy1mcmVlIHN1YmplY3RzKSBhcyBhIGZldyBvdXRsaWVycy4NCg0KVGhlIG5leHQgYHJpZGdlYCBkZW5zaXR5IHBsb3QgYWxzbyBzaG93cyB0aGUgZGlmZmVyZW5jZSBvZiB0aGUgdHdvIGRlbnNpdHkgY3VydmVzLg0KDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPSA2LCBmaWcuaGVpZ2h0PTQsIGZpZy5jYXA9IlJpZGdlIHBsb3RzIG9mIGRpYWJldGVzIHN0YXR1cyB2cyBnbHVjb3NlIn0NCmdncGxvdChHbHVjb3NlRGlhYmV0ZXMsIGFlcyh4ID0gbWFzcywgeSA9IGRpYWJldGVzLCBmaWxsID0gZGlhYmV0ZXMpKSArDQogIGdlb21fZGVuc2l0eV9yaWRnZXMoKSArDQogICBnZ3RpdGxlKCJEaWFiZXRlcyBzdGF0dXMgdnMgZ2x1Y29zZSBsZXZlbCIpICsgDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb249InRvcCIpICsNCiAgeGxhYigiRGlhYmV0ZXMgU3RhdHVzIikgKyANCiAgeWxhYigiR2x1Y29zZSBMZXZlbCIpIA0KYGBgDQoNCg0KVGhlIHJpZGdlIHBsb3QgaXMgYSAyRCBwbG90IGJ1dCBoYXMgM0QgZWZmZWN0LiBJdCBpcyBhZXN0aGV0aWNhbGx5IHBsZWFzYW50LCBidXQgY291bGQgYWxzbyBpbnRyb2R1Y2VzIHZpc3VhbCBiaWFzIGluIHRlcm1zIG9mIGNlbnRlciBvZiB0aGUgZGlzdHJpYnV0aW9ucy4gV2UgbmV4dCBza2V0Y2ggb3ZlcmxhaWQgZGVuc2l0eSBjdXJ2ZXMgd2l0aCB0aGUgc2FtZSBob3Jpem9udGFsIGF4aXMuIA0KDQoNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9IDYsIGZpZy5oZWlnaHQ9NCxmaWcuY2FwPSJPdmVybGFpZCBkZW5zaXR5IGN1cnZlcyBvZiBnbHVjb3NlIGxldmVsIGRpYWJldGVzIGxldmVscyJ9DQpnZ3Bsb3QoZGF0YSA9IEdsdWNvc2VEaWFiZXRlcywgYWVzKHggPSBtYXNzLCBmaWxsID0gZGlhYmV0ZXMpKSArDQogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMykgKw0KICBnZ3RpdGxlKCJEaWFiZXRlcyBTdGF0dXMgdnMgR2x1Y29zZSBMZXZlbCIpICsgDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb249InRvcCIpICsNCiAgeGxhYigiRGlhYmV0ZXMgU3RhdHVzIikgKyANCiAgeWxhYigiR2x1Y29zZSBMZXZlbCIpIA0KYGBgDQoNCg0K