Introduction
This dataset is titled “Productivity Prediction of Garment Employees”
and shows various explanatory variables and one target/response variable
(actual_productivity). The purpose of collecting this dataset is to see
what factors may cause employees productivity levels to be high or low.
This dataset was collected from Kaggle. This dataset contains 1197
observations and 15 variables (14 feature variables and 1 target
variable).
The date (categorical) shows the date in MM-DD-YYYY format. However,
the date is more of an identifier than a predictor. The quarter
(categorical) shows the portion of the month (divided into 4 quarters).
The department (categorical) shows the department (sewing/finishing).
The day (categorical) shows the day of the week. The team (categorical
represented as numeric) shows the team number. The targeted_productivity
(numerical) shows the targeted productivity levels. The smv (numerical)
shows the Standard Minute Value (allocated time for a task). The wip
(numerical) shows the work in progress (Includes the number of
unfinished items for products). The over_time (numerical) shows the
amount of overtime by each team in minutes. The incentive (numerical)
shows the amount of financial incentive in BDT. The idle_time
(numerical) shows the amount of time when the production was
interrupted. The idle_men (numerical) shows the number of workers who
were idle due to production interruption. The no_of_style_change
(numerical) shows the number of changes in the style of a particular
product. The no_of_workers (numerical) shows the number of of workers in
each team. The target/response variable (actual_productivity) shows the
actual productivity level (between 0-1).
The original dataset has 506 missing values in the wip column. This
can be resolved by MICE imputation.
Based on this dataset, we can formulate a research question. The
question we can form is what variables are the most important in
predicting productivity level of employees. For regression, we can use
actual_productivity (continuous) as our response variable. For
classification, we can create a binary response variable with 1
(productive) and 0 (not productive). We can use the condition if
actual_productivity is greater than the targeted_productivity.
We will use Regularized Linear/logistic Regression and SVM/SVR
(Linear/RBF kernels) to try to answer the research question.
Read in Dataset
Read in dataset.
garment <- read.csv("garments_worker_productivity.csv")
See dataset structure.
str(garment)
'data.frame': 1197 obs. of 15 variables:
$ date : chr "1/1/2015" "1/1/2015" "1/1/2015" "1/1/2015" ...
$ quarter : chr "Quarter1" "Quarter1" "Quarter1" "Quarter1" ...
$ department : chr "sweing" "finishing " "sweing" "sweing" ...
$ day : chr "Thursday" "Thursday" "Thursday" "Thursday" ...
$ team : int 8 1 11 12 6 7 2 3 2 1 ...
$ targeted_productivity: num 0.8 0.75 0.8 0.8 0.8 0.8 0.75 0.75 0.75 0.75 ...
$ smv : num 26.16 3.94 11.41 11.41 25.9 ...
$ wip : int 1108 NA 968 968 1170 984 NA 795 733 681 ...
$ over_time : int 7080 960 3660 3660 1920 6720 960 6900 6000 6900 ...
$ incentive : int 98 0 50 50 50 38 0 45 34 45 ...
$ idle_time : num 0 0 0 0 0 0 0 0 0 0 ...
$ idle_men : int 0 0 0 0 0 0 0 0 0 0 ...
$ no_of_style_change : int 0 0 0 0 0 0 0 0 0 0 ...
$ no_of_workers : num 59 8 30.5 30.5 56 56 8 57.5 55 57.5 ...
$ actual_productivity : num 0.941 0.886 0.801 0.801 0.8 ...
Check for Missing
Values
506 missing values in the wip column.
colSums(is.na(garment))
date quarter department
0 0 0
day team targeted_productivity
0 0 0
smv wip over_time
0 506 0
incentive idle_time idle_men
0 0 0
no_of_style_change no_of_workers actual_productivity
0 0 0
EDA
This section will show the distribution of each individual
feature.
The below figure shows the distribution of the quarter variable.
ggplot(garment, aes(x = quarter)) +
geom_bar() +
labs(title = "quarter")

The below figure shows the distribution of the department variable.
Data entry errors in this column were resolved.
garment$department[garment$department == 'finishing '] <- 'finishing'
ggplot(garment, aes(x = department)) +
geom_bar() +
labs(title = "department")

The below figure shows the distribution of the day variable.
ggplot(garment, aes(x = day)) +
geom_bar() +
labs(title = "day")

The below figure shows the distribution of the team variable.
# ggplot(data = garment, aes(x = team)) +
# geom_boxplot() +
#
# labs(title = "team")
garment$team <- as.factor(garment$team)
ggplot(garment, aes(x = team)) +
geom_bar() +
labs(title = "team")

The below figure shows the distribution of the targeted_productivity
variable.
ggplot(data = garment, aes(x = targeted_productivity)) +
geom_boxplot() +
labs(title = "targeted_productivity")

The below figure shows the distribution of the smv variable.
ggplot(data = garment, aes(x = smv)) +
geom_boxplot() +
labs(title = "smv")

The below figure shows the distribution of the wip variable.
ggplot(data = garment, aes(x = wip)) +
geom_boxplot() +
labs(title = "wip")

The below figure shows the distribution of the over_time
variable.
ggplot(data = garment, aes(x = over_time)) +
geom_boxplot() +
labs(title = "over_time")

The below figure shows the distribution of the incentive
variable.
ggplot(data = garment, aes(x = incentive)) +
geom_boxplot() +
labs(title = "incentive")

The below figure shows the distribution of the idle_time
variable.
ggplot(data = garment, aes(x = idle_time)) +
geom_boxplot() +
labs(title = "idle_time")

The below figure shows the distribution of the idle_men variable.
ggplot(data = garment, aes(x = idle_men)) +
geom_boxplot() +
labs(title = "idle_men")

The below figure shows the distribution of the no_of_style_change
variable.
ggplot(data = garment, aes(x = no_of_style_change)) +
geom_boxplot() +
labs(title = "no_of_style_change")

The below figure shows the distribution of the no_of_workers
variable.
ggplot(data = garment, aes(x = no_of_workers)) +
geom_boxplot() +
labs(title = "no_of_workers")

Imputation
We use the MICE imputation method in this section to replace all
missing values in the wip column.
complete_garment_data is the complete dataset.
init <- mice(garment, maxit = 0)
init$method
date quarter department
"" "" ""
day team targeted_productivity
"" "" ""
smv wip over_time
"" "pmm" ""
incentive idle_time idle_men
"" "" ""
no_of_style_change no_of_workers actual_productivity
"" "" ""
imp <- mice(garment, method = c("","", "", "", "", "", "", "pmm", "", "", "", "", "", "", ""),
maxit = 10,
m = 5,
seed=123,
print=F)
complete_garment_data <- complete(imp)
Feature
Engineering
This section focuses on feature engineering.
The ‘team’ variable was already converted to a categorical/factor
variable in the EDA section. Date was dropped from the analysis as it
really serves as more of an identifier. Quarter was also dropped since
it is reliant on the date and the counts for all quarters vary
significantly, as shown in the EDA section.
Below, we have the ‘department’ variable (sewing/finishing). This
variable is converted to binary, with sewing as 0 and finishing as
1.
complete_garment_data1 <- complete_garment_data
complete_garment_data1$department <- ifelse(complete_garment_data1$department=="sweing",0,1)
complete_garment_data1$department <- as.factor(complete_garment_data1$department)
Below, we do one hot encoding for the ‘day’ variable since the values
are nominal.
encoded_data <- model.matrix(~ day - 1, data = complete_garment_data1)
complete_garment_data1 <- cbind(complete_garment_data1, encoded_data)
complete_garment_data1$dayMonday <- as.factor(complete_garment_data1$dayMonday)
complete_garment_data1$daySaturday <- as.factor(complete_garment_data1$daySaturday)
complete_garment_data1$daySunday <- as.factor(complete_garment_data1$daySunday)
complete_garment_data1$dayThursday <- as.factor(complete_garment_data1$dayThursday)
complete_garment_data1$dayTuesday <- as.factor(complete_garment_data1$dayTuesday)
complete_garment_data1$dayWednesday <- as.factor(complete_garment_data1$dayWednesday)
Regularized Linear
Regression
In this section, we do regularized linear regression. We do lasso,
ridge, and elastic net regularization. For each regularization
technique, we conduct coefficient path analysis to see how well each
predictor variable shrinks. Then we use cross-validation to determine
the optimal regularization parameter for each. Lastly, we generate the
final model based on the optimal regularization parameters.
Coefficient Path
Analysis
Below, we split the data into our training and testing. Then we
perform lasso, ridge, and elastic net.
set.seed(112233)
X <- model.matrix(~ department+team+dayMonday+daySaturday+ daySunday+ dayThursday+ dayTuesday+ dayWednesday, data = complete_garment_data1)[, -1]
X <- cbind(complete_garment_data1$targeted_productivity, complete_garment_data1$smv, complete_garment_data1$wip, complete_garment_data1$over_time, complete_garment_data1$incentive, complete_garment_data1$idle_time, complete_garment_data1$idle_men, complete_garment_data1$no_of_style_change, complete_garment_data1$no_of_workers, X)
colnames(X) <- c('targeted_productivity','smv', 'wip',
'over_time',
'incentive', 'idle_time' , 'idle_men',
'no_of_style_change', 'no_of_workers','department',
'team2','team3','team4','team5','team6', 'team7','team8','team9','team10','team11','team12','dayMonday','daySaturday','daySunday','dayThursday','dayTuesday','dayWednesday')
y <- complete_garment_data1$actual_productivity
train_index <- createDataPartition(y, p = 0.8, list = FALSE)
X_train <- X[train_index, ]
X_test <- X[-train_index, ]
y_train <- y[train_index]
y_test <- y[-train_index]
preprocess_params <- preProcess(X_train, method = c("center", "scale"))
X_train <- predict(preprocess_params, X_train)
X_test <- predict(preprocess_params, X_test)
fit_lasso <- glmnet(X_train,
y_train,
alpha = 1)
fit_ridge <- glmnet(X_train,
y_train,
alpha = 0)
fit_elastic_net <- glmnet(X_train,
y_train,
alpha = 0.5)
cv_lasso <- cv.glmnet(X_train, y_train, alpha = 1)
cv_ridge <- cv.glmnet(X_train, y_train, alpha = 0)
cv_elastic_net <- cv.glmnet(X_train, y_train, alpha = 0.5)
Below shows the plot of the coefficient paths for all the predictor
variables for lasso. It shows how variables are shrunk to exactly 0.
par(mar=c(5,4,6,3))
# Plot coefficient path
plot(fit_lasso, xvar = "lambda", label = TRUE,
lwd = 1.5,
main = "Coefficient Path Analysis: LASSO",
cex.main = 0.9,
col = rainbow(10))
abline(v = 1, col = "purple", lty = 4, lwd = 2)
abline(v = -1, col = "steelblue", lty = 2, lwd = 2)

Below shows the plot of the coefficient paths for all the predictor
variables for ridge. It shows how variables are shrunk towards 0.
par(mar=c(5,4,6,3))
# Plot coefficient path
plot(fit_ridge, xvar = "lambda", label = TRUE,
lwd = 1.5,
main = "Coefficient Path Analysis: ridge",
cex.main = 0.9,
col = rainbow(10))
abline(v = 1, col = "purple", lty = 4, lwd = 2)
abline(v = -1, col = "steelblue", lty = 2, lwd = 2)

Below shows the plot of the coefficient paths for all the predictor
variables for elastic net.
par(mar=c(5,4,6,3))
# Plot coefficient path
plot(fit_elastic_net, xvar = "lambda", label = TRUE,
lwd = 1.5,
main = "Coefficient Path Analysis: elastic net",
cex.main = 0.9,
col = rainbow(10))
abline(v = 1, col = "purple", lty = 4, lwd = 2)
abline(v = -1, col = "steelblue", lty = 2, lwd = 2)

Optimal
Parameters
As shown below, using the cross validation we did for glmnet and the
lambda.min (minimum cross-validated error) generated, we get the optimal
parameters for lasso, ridge, and elastic net. LASSO.opt = 0.1328,
Ridge.opt=0.1334, and Elasticnet.opt = 0.1329
best.lasso.lambda <- cv_lasso$lambda.min
best.ridge.lambda <- cv_ridge$lambda.min
best.elastic.net.lambda <- cv_elastic_net$lambda.min
##
# Lasso Regression (L1 Regularization):
# CAUTION: model formula differs from the regular regression formula
lasso_model.opt <- glmnet(X_train,
y_train,
alpha = 1, # lasso regression
lambda = best.lasso.lambda) # useser selected alpha, optimal lambda
# can be obtained through CV (see below)
lasso_predictions.opt <- predict(lasso_model.opt,
s = best.lasso.lambda, # user selected lambda value
# (regularization paremeter)
newx = X_test) # test data set
# The following RMSE of prediction serves as a validation - one step validation
lasso_rmse.opt <- sqrt(mean((y_test - lasso_predictions.opt)^2))
# Ridge Regression (L2 Regularization)
ridge_model.opt <- glmnet(X_train, y_train, alpha = 0, lambda = best.ridge.lambda)
ridge_predictions.opt <- predict(ridge_model.opt, s = best.ridge.lambda, newx = X_test)
ridge_rmse.opt <- sqrt(mean((y_test - ridge_predictions.opt)^2))
# Elastic Net (Combination of L1 and L2)
elastic_net_model.opt <- glmnet(X_train, y_train, alpha = 0.5, lambda = best.elastic.net.lambda)
elastic_net_predictions.opt <- predict(elastic_net_model.opt, s = 0.1, newx = X_test)
elastic_net_rmse.opt <- sqrt(mean((y_test - elastic_net_predictions.opt)^2))
RMSE.opt = cbind(LASSO.opt = lasso_rmse.opt,
Ridge.opt = ridge_rmse.opt,
Elasticnet.opt = elastic_net_rmse.opt)
pander(RMSE.opt)
Final Model
Equations
For lasso, we get the final model equation below based on the optimal
parameter from the previous section.
# lasso
best_lambda.lasso <- cv_lasso$lambda.min
coefficients.lasso <- coef(cv_lasso, s = best_lambda.lasso)
intercept.lasso <- coefficients.lasso[1]
betas.lasso <- coefficients.lasso[-1]
cat("Model equation: y =", round(intercept.lasso,4), "+",
paste(round(betas.lasso,4), colnames(X), sep = "*", collapse = " + "), "\n")
Model equation: y = 0.7338 + 0.0661*targeted_productivity + -0.0788*smv + 0.0059*wip + -0.0128*over_time + 0.0071*incentive + 0.0053*idle_time + -0.0293*idle_men + -0.0168*no_of_style_change + 0.1039*no_of_workers + 0.0224*department + -0.0145*team2 + 9e-04*team3 + -0.0049*team4 + -0.0113*team5 + -0.0195*team6 + -0.0273*team7 + -0.0256*team8 + -0.0253*team9 + -0.0228*team10 + -0.0366*team11 + -0.0122*team12 + -0.0049*dayMonday + 0.0029*daySaturday + -0.0035*daySunday + -0.0059*dayThursday + 0.0039*dayTuesday + 0*dayWednesday
For ridge, we get the final model equation below based on the optimal
parameter from the previous section.
# ridge
best_lambda.ridge <- cv_ridge$lambda.min
coefficients.ridge <- coef(cv_ridge, s = best_lambda.ridge)
intercept.ridge <- coefficients.ridge[1]
betas.ridge <- coefficients.ridge[-1]
cat("Model equation: y =", round(intercept.ridge,4), "+",
paste(round(betas.ridge,4), colnames(X), sep = "*", collapse = " + "), "\n")
Model equation: y = 0.7338 + 0.0637*targeted_productivity + -0.0532*smv + 0.0072*wip + -0.0083*over_time + 0.0077*incentive + 0.0045*idle_time + -0.0277*idle_men + -0.0164*no_of_style_change + 0.0564*no_of_workers + 0.0042*department + -0.0106*team2 + 0.005*team3 + -0.0013*team4 + -0.0085*team5 + -0.0193*team6 + -0.0242*team7 + -0.0224*team8 + -0.0212*team9 + -0.0187*team10 + -0.0293*team11 + -0.0117*team12 + -0.0038*dayMonday + 0.0041*daySaturday + -0.0028*daySunday + -0.0047*dayThursday + 0.0052*dayTuesday + 0.0019*dayWednesday
For elastic net, we get the final model equation below based on the
optimal parameter from the previous section.
# elastic net
best_lambda.net <- cv_elastic_net$lambda.min
coefficients.net <- coef(cv_elastic_net, s = best_lambda.net)
intercept.net <- coefficients.net[1]
betas.net<- coefficients.net[-1]
cat("Model equation: y =", round(intercept.net,4), "+",
paste(round(betas.net,4), colnames(X), sep = "*", collapse = " + "), "\n")
Model equation: y = 0.7338 + 0.066*targeted_productivity + -0.076*smv + 0.006*wip + -0.012*over_time + 0.0071*incentive + 0.0051*idle_time + -0.029*idle_men + -0.0166*no_of_style_change + 0.0965*no_of_workers + 0.0185*department + -0.0135*team2 + 0.0018*team3 + -0.0038*team4 + -0.0104*team5 + -0.0193*team6 + -0.0265*team7 + -0.0248*team8 + -0.0243*team9 + -0.0219*team10 + -0.0353*team11 + -0.0119*team12 + -0.004*dayMonday + 0.0035*daySaturday + -0.0028*daySunday + -0.0051*dayThursday + 0.0045*dayTuesday + 8e-04*dayWednesday
Regularized Logistic
Regression
Coefficient Path
Analysis
In this section, we do regularized logistic regression. The process
is the same as regularized linear.
First, we create a binary response variable called ‘productivity’,
where 1 represents productive and 0 represents not productive. This is
based on if actual productivity level is greater than the targeted
productivity.
Then we split the data into train/test, generate the lasso, ridge,
and elastic net models, and generate our optimal lambda values.
complete_garment_data1 <- transform(complete_garment_data1, productivity=ifelse(actual_productivity>=targeted_productivity, 1, 0))
set.seed(80)
X1 <- model.matrix(~ department+team+dayMonday+daySaturday+ daySunday+ dayThursday+ dayTuesday+ dayWednesday, data = complete_garment_data1)[, -1]
X1 <- cbind(complete_garment_data1$targeted_productivity, complete_garment_data1$smv, complete_garment_data1$wip, complete_garment_data1$over_time, complete_garment_data1$incentive, complete_garment_data1$idle_time, complete_garment_data1$idle_men, complete_garment_data1$no_of_style_change, complete_garment_data1$no_of_workers, X1)
#X <- as.data.frame(X)
colnames(X1) <- c('targeted_productivity','smv', 'wip',
'over_time',
'incentive', 'idle_time' , 'idle_men',
'no_of_style_change', 'no_of_workers','department',
'team2','team3','team4','team5','team6', 'team7','team8','team9','team10','team11','team12','dayMonday','daySaturday','daySunday','dayThursday','dayTuesday','dayWednesday')
#X1 <- as.matrix(complete_garment_data1[, -c(1,2,4,15,16)])
y1 <- complete_garment_data1$productivity
train_index1 <- createDataPartition(y1, p = 0.8, list = FALSE)
X_train1 <- X1[train_index1, ]
X_test1 <- X1[-train_index1, ]
y_train1 <- y1[train_index1]
y_test1 <- y1[-train_index1]
preprocess_params1 <- preProcess(X_train1, method = c("center", "scale"))
X_train1 <- predict(preprocess_params1, X_train1)
X_test1 <- predict(preprocess_params1, X_test1)
lasso_model1 <- glmnet(X_train1, y_train1, family = "binomial", alpha = 1)
cv_lasso1 <- cv.glmnet(X_train1, y_train1, family = "binomial", alpha = 1)
lambda_lasso1 <- cv_lasso1$lambda.min
lasso_model_opt1 <- glmnet(X_train1, y_train1,
family = "binomial",
alpha = 1,
lambda = lambda_lasso1)
ridge_model1 <- glmnet(X_train1, y_train1, family = "binomial", alpha = 0)
cv_ridge1 <- cv.glmnet(X_train1, y_train1, family = "binomial", alpha = 0)
lambda_ridge1 <- cv_ridge1$lambda.min
ridge_model_opt1 <- glmnet(X_train1, y_train1,
family = "binomial",
alpha = 0,
lambda = lambda_ridge1)
elastic_model1 <- glmnet(X_train1, y_train1, family = "binomial", alpha = 0.5)
cv_elastic1 <- cv.glmnet(X_train1, y_train1, family = "binomial", alpha = 0.5)
lambda_elastic1 <- cv_elastic1$lambda.min
elastic_model_opt1 <- glmnet(X_train1, y_train1,
family = "binomial",
alpha = 0.5,
lambda = lambda_elastic1)
Below shows the plot of the coefficient paths for all the predictor
variables for lasso.
par(mar=c(5,4,6,3))
# Plot coefficient path
plot(lasso_model1, xvar = "lambda", label = TRUE,
lwd = 1.5,
main = "Coefficient Path Analysis: lasso",
cex.main = 0.9,
col = rainbow(10))
abline(v = 1, col = "purple", lty = 4, lwd = 2)
abline(v = -1, col = "steelblue", lty = 2, lwd = 2)

Below shows the plot of the coefficient paths for all the predictor
variables for ridge.
par(mar=c(5,4,6,3))
# Plot coefficient path
plot(ridge_model1, xvar = "lambda", label = TRUE,
lwd = 1.5,
main = "Coefficient Path Analysis: ridge",
cex.main = 0.9,
col = rainbow(10))
abline(v = 1, col = "purple", lty = 4, lwd = 2)
abline(v = -1, col = "steelblue", lty = 2, lwd = 2)

Below shows the plot of the coefficient paths for all the predictor
variables for elastic net.
par(mar=c(5,4,6,3))
# Plot coefficient path
plot(elastic_model1, xvar = "lambda", label = TRUE,
lwd = 1.5,
main = "Coefficient Path Analysis: elastic net",
cex.main = 0.9,
col = rainbow(10))
abline(v = 1, col = "purple", lty = 4, lwd = 2)
abline(v = -1, col = "steelblue", lty = 2, lwd = 2)

Regularization
Parameters
Below, we show the coefficients for each predictor variable based on
the optimal lambda values (for lasso/ridge/elastic).
lasso.coef1 <- as.matrix(coef(lasso_model_opt1))
ridge.coef1 <- as.matrix(coef(ridge_model_opt1))
elastic.coef1 <- as.matrix(coef(elastic_model_opt1))
regularized.coef1 <- data.frame(lasso = lasso.coef1[,1],
ridge = ridge.coef1[,1],
elasticnet = elastic.coef1[,1])
pander(regularized.coef1)
| (Intercept) |
1.263 |
1.254 |
1.298 |
| targeted_productivity |
0 |
0.007142 |
0 |
| smv |
-0.8924 |
-0.628 |
-0.9713 |
| wip |
0.2414 |
0.3189 |
0.3496 |
| over_time |
0 |
0.006669 |
-0.01018 |
| incentive |
0.0507 |
0.07586 |
0.05811 |
| idle_time |
0.01121 |
0.04312 |
0.04022 |
| idle_men |
-0.3018 |
-0.3066 |
-0.3264 |
| no_of_style_change |
-0.1462 |
-0.1505 |
-0.1646 |
| no_of_workers |
0.6497 |
0.5627 |
0.7433 |
| department |
-0.886 |
-0.6879 |
-0.8928 |
| team2 |
-0.0874 |
-0.1596 |
-0.1803 |
| team3 |
0.07338 |
0.05198 |
0.02416 |
| team4 |
0 |
-0.05696 |
-0.06196 |
| team5 |
-0.06979 |
-0.1444 |
-0.1531 |
| team6 |
-0.1828 |
-0.2255 |
-0.2642 |
| team7 |
-0.1707 |
-0.234 |
-0.2493 |
| team8 |
-0.2553 |
-0.3233 |
-0.3415 |
| team9 |
-0.1998 |
-0.2619 |
-0.2942 |
| team10 |
-0.1458 |
-0.2048 |
-0.2324 |
| team11 |
-0.2054 |
-0.2433 |
-0.2937 |
| team12 |
0 |
-0.04955 |
-0.07529 |
| dayMonday |
0 |
0.0559 |
0.01095 |
| daySaturday |
0.0003255 |
0.05972 |
0.01684 |
| daySunday |
-0.1297 |
-0.1019 |
-0.1377 |
| dayThursday |
-0.1334 |
-0.09887 |
-0.1433 |
| dayTuesday |
0 |
0.06016 |
0.01499 |
| dayWednesday |
0 |
0.03093 |
0 |
Optimal Cutoff and
ROC/AUC
The below figure shows the optimal cut-off probability for the
lasso/ridge/elastic net models. This will allow us to use the fitted
model for predicting the response variable (productivity). All the
optimal cut-off probabilities are approx 0.55
predict_lasso <- predict(lasso_model_opt1, newx = X_test1, type = "response")
predict_ridge <- predict(ridge_model_opt1, newx = X_test1, type = "response")
predict_elastic <- predict(elastic_model_opt1, newx = X_test1, type = "response")
###########################################
## Optimal cutoff probability determination
seq.cut <- seq(0,1, length=50)
# y is a vector of 0 and 1
acc.lasso <- NULL
acc.ridge <- NULL
acc.elastic <- NULL
for (i in 1:length(seq.cut)){
predy.lasso <- ifelse(predict_lasso >seq.cut[i], 1, 0)
predy.ridge<- ifelse(predict_ridge >seq.cut[i], 1, 0)
predy.elastic<- ifelse(predict_elastic >seq.cut[i], 1, 0)
##
acc.lasso[i] <- mean(y_test1 == predy.lasso)
acc.ridge[i] <- mean(y_test1 == predy.ridge)
acc.elastic[i] <- mean(y_test1 == predy.elastic)
}
## optimal cut-off: if the maximum accuracy occurs at multiple
## cut-off probabilities, the average of these cutoff probabilities
## will be defined as the optimal cutoff probability
opt.cut.lasso <- mean(seq.cut[which(acc.lasso==max(acc.lasso))])
opt.cut.ridge<- mean(seq.cut[which(acc.ridge==max(acc.ridge))])
opt.cut.elastic <- mean(seq.cut[which(acc.elastic==max(acc.elastic))])
##
acc.data <- data.frame(prob = rep(seq.cut,3),
acc=c(acc.lasso, acc.ridge, acc.elastic),
group = c(rep("lasso",50), rep("ridge",50), rep("elastic",50)))
##
gg.acc <- ggplot(data = acc.data, aes(x=prob, y = acc, color = group)) +
geom_line() +
annotate("text", x = 0.6, y = 0.45,
label = paste("LASSO cutoff: ", round(opt.cut.lasso,5), "Accuracy: ", round(max(acc.lasso),5),
"\nRidge cutoff: ", round(opt.cut.ridge,5), "Accuracy: ", round(max(acc.ridge),5),
"\nElastic cutoff: ", round(opt.cut.elastic,5), "Accuracy: ", round(max(acc.elastic),5)),
size = 3,
color = "navy") +
ggtitle("Cut-off Probability vs Accuracy") +
labs(x = "cut-off Probability",
y = "accuracy", color = "Group") +
theme(plot.title = element_text(hjust = 0.5))
##
ggplotly(gg.acc)
We can compare the performance of the lasso, Ridge, and Elastic Net
models on our test data using ROC curves. All models have similar
performances with AUC of approx 0.75.
prob_lasso <- predict(lasso_model_opt1, newx = X_test1, type = "response")
prob_ridge <- predict(ridge_model_opt1, newx = X_test1, type = "response")
prob_elastic <- predict(elastic_model_opt1, newx = X_test1, type = "response")
# Compute ROC curves: roc object contains a lot information including
# sensitivity, specificity, AUC, etc.
roc_lasso <- roc(y_test1, prob_lasso)
roc_ridge <- roc(y_test1, prob_ridge)
roc_elastic <- roc(y_test1, prob_elastic)
# Compute AUC values
auc_lasso <- auc(roc_lasso)
auc_ridge <- auc(roc_ridge)
auc_elastic <- auc(roc_elastic)
## LASSO
sen.lasso <- roc_lasso$sensitivities
spe.lasso <- roc_lasso$specificities
auc.lasso <- roc_lasso$auc
## Ridge
sen.ridge <- roc_ridge$sensitivities
spe.ridge <- roc_ridge$specificities
auc.ridge <- roc_ridge$auc
## Elastic Net
sen.elastic <- roc_elastic$sensitivities
spe.elastic <- roc_elastic$specificities
auc.elastic <- roc_elastic$auc
## Plotting the ROC curves: three colors - green, orange, and purple
plot(1-spe.lasso, sen.lasso,
type = "l",
col = "green",
xlim=c(0,1),
xlab = "1 - specificity",
ylab = "sensitivity",
main = "ROC Curves for LASSO, Ridge, and Elastic Net")
lines(1-spe.ridge, sen.ridge, col = "orange")
lines(1-spe.elastic, sen.elastic, col = "purple")
#abline(0,1, type = "l", lty = 2, col = "steelblue", lwd = 1)
abline(0,1, , lty = 2, col = "steelblue", lwd = 1)
# Add legend
legend("bottomright", legend = c(paste("LASSO (AUC =", round(auc_lasso, 3), ")"),
paste("Ridge (AUC =", round(auc_ridge, 3), ")"),
paste("Elastic Net (AUC =", round(auc_elastic, 3), ")")),
col = c("green", "orange", "purple"), lty = 1, cex = 0.8, bty = "n")

SVM
This section uses Support Vector Machines. Since our target variable
(productivity) has two classes, SVM will find a hyperplane that
maximizes the margin between the two classes.
The below code chunks will perform SVM by using 5-fold cross
validation to search for the optimal hyperparameters and using those to
train the final model. We will use linear and RBF kernels.
complete_garment_data1$productivity <- as.factor(complete_garment_data1$productivity)
set.seed(123)
index <- sample(1:nrow(complete_garment_data1), 0.8 * nrow(complete_garment_data1))
train.data <- complete_garment_data1[index, ]
test.data <- complete_garment_data1[-index, ]
tune_control <- tune.control(
cross = 5,
nrepeat = 1
)
tune.RBF <- tune(
svm,
productivity ~ department + team + targeted_productivity + smv + wip + over_time + incentive + idle_time + idle_men +
no_of_style_change + no_of_workers + dayMonday + daySaturday+ daySunday + dayThursday + dayTuesday + dayWednesday,
data = train.data,
kernel = "radial",
ranges = list(
cost = 10^(-1:2),
gamma = c(0.1, 0.5, 1, 2)
),
tunecontrol = tune_control
)
best.RBF <- tune.RBF$best.model
best.cost.RBF <- best.RBF$cost
best.gamma.RBF <- best.RBF$gamma
final.RBF <- svm(
productivity ~ department + team + targeted_productivity + smv + wip + over_time + incentive + idle_time + idle_men +
no_of_style_change + no_of_workers + dayMonday + daySaturday+ daySunday + dayThursday + dayTuesday + dayWednesday,
data = train.data,
kernel = "radial",
cost = best.cost.RBF,
gamma = best.gamma.RBF,
probability = TRUE
)
##################
# tune.control <- tune.control(
# cross = 5, # Use 5-fold cross-validation, the default is 10-fold cross-validation
# nrepeat = 1 # Number of repetitions (for repeated cross-validation)
# )
tune.lin <- tune(
svm,
productivity ~ department + team + targeted_productivity + smv + wip + over_time + incentive + idle_time + idle_men +
no_of_style_change + no_of_workers + dayMonday + daySaturday+ daySunday + dayThursday + dayTuesday + dayWednesday,
data = train.data,
kernel = "linear",
ranges = list(
cost = 10^(-1:2)
),
tunecontrol = tune_control
)
best.lin <- tune.lin$best.model
best.cost.lin <- best.lin$cost
final.lin <- svm(
productivity ~ department + team + targeted_productivity + smv + wip + over_time + incentive + idle_time + idle_men +
no_of_style_change + no_of_workers + dayMonday + daySaturday+ daySunday + dayThursday + dayTuesday + dayWednesday,
data = train.data,
kernel = "linear",
cost = best.cost.lin,
probability = TRUE
)
logit.fit <- glm(productivity ~ department + team + targeted_productivity + smv + wip + over_time + incentive + idle_time + idle_men + no_of_style_change + no_of_workers+ dayMonday + daySaturday+ daySunday + dayThursday + dayTuesday + dayWednesday, data = train.data, family = binomial)
AIC.logit <- step(logit.fit, direction = "both", trace = 0)
pred.logit <- predict(AIC.logit, test.data, type = "response")
pred.prob.lin <- predict(final.lin, test.data, probability = TRUE)
pred.prob.RBF <- predict(final.RBF, test.data, probability = TRUE)
#####
prob.linear <- attr(pred.prob.lin, "probabilities")[, 2]
prob.radial <- attr(pred.prob.RBF, "probabilities")[, 2]
#####
roc_lin <- roc(test.data$productivity, prob.linear)
roc_RBF <- roc(test.data$productivity, prob.radial)
roc_logit <- roc(test.data$productivity, pred.logit)
lin.sen <- roc_lin$sensitivities
lin.spe <- roc_lin$specificities
rad.sen <- roc_RBF$sensitivities
rad.spe <- roc_RBF$specificities
logit.sen <- roc_logit$sensitivities
logit.spe <- roc_logit$specificities
auc.lin <- roc_lin$auc
auc.rad <- roc_RBF$auc
auc.logit <- roc_logit$auc
Below we plot the ROC for linear, RBF, and standard logistic
regression. Based on the AUC, the SVM linear, SVM RBF, and standard
logistic are similar with area of approx 0.8.
plot(1-lin.spe, lin.sen,
xlab = "1 - specificity",
ylab = "sensitivity",
col = "darkred",
type = "l",
lty = 1,
lwd = 1,
main = "ROC Curves of SVM")
lines(1-rad.spe, rad.sen,
col = "blue",
lty = 1,
lwd = 1)
lines(1-logit.spe, logit.sen,
col = "orange",
lty = 1,
lwd = 1)
abline(0,1, col = "skyblue3", lty = 2, lwd = 2)
abline(v=c(0.049,0.151), lty = 3, col = "darkgreen")
legend("bottomright", c("Linear Kernel", "Radial Kernel", "Logistic Regression"),
lty = c(1,1,1), lwd = rep(1,3),
col = c("red", "blue", "orange"),
bty="n",cex = 0.8)
text(0.8, 0.46, paste("Linear AUC: ", round(auc.lin,4)), cex = 0.8)
text(0.8, 0.4, paste("Radial AUC: ", round(auc.rad,4)), cex = 0.8)
text(0.8, 0.34, paste("Logistic AUC: ", round(auc.logit,4)), cex = 0.8)

SVR
In this section, we will do SVR. Since we have a continuous target
variable, we cannot use a hyperplane to separate classes. Here we try to
fit the predicted data within a specified margin of error. Below we
train the SVR model. We use RBF/linear kernels and standard linear
regression.
X2 <- model.matrix(~ department+team+ dayMonday + daySaturday+ daySunday + dayThursday + dayTuesday + dayWednesday, data = complete_garment_data1)[, -1]
X2 <- cbind(complete_garment_data1$targeted_productivity, complete_garment_data1$smv, complete_garment_data1$wip, complete_garment_data1$over_time, complete_garment_data1$incentive, complete_garment_data1$idle_time, complete_garment_data1$idle_men, complete_garment_data1$no_of_style_change, complete_garment_data1$no_of_workers, X2)
#X <- as.data.frame(X)
colnames(X2) <- c('targeted_productivity','smv', 'wip',
'over_time',
'incentive', 'idle_time' , 'idle_men',
'no_of_style_change', 'no_of_workers','department',
'team2','team3','team4','team5','team6', 'team7','team8','team9','team10','team11','team12','dayMonday','daySaturday','daySunday','dayThursday','dayTuesday','dayWednesday')
X2 <- as.data.frame(X2)
#
# X2 <- cbind(complete_garment_data1[, 15] ,X2)
# #
# #
# colnames(X2) <- c('actual_productivity', 'targeted_productivity','smv', 'wip',
# 'over_time',
# 'incentive', 'idle_time' , 'idle_men',
# 'no_of_style_change', 'no_of_workers','department',
# 'team2','team3','team4','team5','team6', 'team7','team8','team9','team10','team11','team12')
#X2 <- complete_garment_data1[, -c(1,2,4,15,16)]
y2 <- complete_garment_data1[, 15]
set.seed(123)
train.index2 <- sample(1:nrow(X2), 0.8 * nrow(X2))
X.train2 <- X2[train.index2, ]
y.train2 <- y2[train.index2]
X.test2 <- X2[-train.index2, ]
y.test2 <- y2[-train.index2]
tune.RBF2 <- tune(svm, train.x = X.train2, train.y = y.train2,
ranges = list(epsilon = seq(0.1, 0.5, 0.1),
cost = c(1, 10, 100),
gamma = c(0.01, 0.1, 1)), # Hyperpar in RBF
tunecontrol = tune_control
)
final.RBF2 <- svm(X.train2, y.train2,
type = "eps-regression", # Use "nu-regression" for nu-SVR
kernel = "radial",
epsilon = tune.RBF2$best.parameters$epsilon,
cost = tune.RBF2$best.parameters$cost,
gamma = tune.RBF2$best.parameters$gamma)
pred.RBF <- predict(final.RBF2, X.test2)
# Evaluate performance
mse.RBF <- mean((y.test2 - pred.RBF)^2) # mean square error
mae.RBF <- mean(abs(y.test2 - pred.RBF))
#############################################################
tune.lin2 <- tune(svm, train.x = X.train2, train.y = y.train2,
ranges = list(epsilon = seq(0.1, 0.5, 0.1),
cost = c(1, 10, 100)),
tunecontrol = tune_control
)
final.lin2 <- svm(X.train2, y.train2,
type = "eps-regression",
kernel = "linear",
epsilon = tune.lin2$best.parameters$epsilon,
cost = tune.lin2$best.parameters$cost)
pred.lin <- predict(final.lin2, X.test2)
# Evaluate performance
mse.lin <- mean((y.test2 - pred.lin)^2) # mean square error
mae.lin <- mean(abs(y.test2 - pred.lin))
X2 <- cbind(complete_garment_data1[, 15] ,X2)
colnames(X2) <- c('actual_productivity', 'targeted_productivity','smv', 'wip',
'over_time',
'incentive', 'idle_time' , 'idle_men',
'no_of_style_change', 'no_of_workers','department',
'team2','team3','team4','team5','team6', 'team7','team8','team9','team10','team11','team12','dayMonday','daySaturday','daySunday','dayThursday','dayTuesday','dayWednesday')
garment.train <- X2[train.index2, ]
garment.test <- X2[-train.index2, ]
# X3 <- cbind(complete_garment_data1[, 15] ,X3)
#
#
# colnames(X3) <- c('actual_productivity', 'targeted_productivity','smv', 'wip',
# 'over_time',
# 'incentive', 'idle_time' , 'idle_men',
# 'no_of_style_change', 'no_of_workers','department',
# 'team2','team3','team4','team5','team6', 'team7','team8','team9','team10','team11','team12')
lse.fit <- lm(actual_productivity ~ targeted_productivity + smv + wip + over_time + incentive + idle_time + idle_men + no_of_style_change + no_of_workers + department + team2+ team3+ team4+ team5+ team6+ team7+ team8+ team9+ team10+ team11+ team12 + dayMonday + daySaturday + daySunday + dayThursday + dayTuesday + dayWednesday,data=garment.train)
AIC.fit <- stepAIC(lse.fit,direction="both", trace = FALSE)
pred.lse <- predict(AIC.fit, X.test2)
mse.lse <- mean((y.test2 - pred.lse)^2) # mean square error
mae.lse <- mean(abs(y.test2 - pred.lse)) # mean absolute error
###
par(mfrow=c(2,2), mar=c(2,2,2,2))
plot(AIC.fit)

The above shows the residual plots when we did the OLS regression.
The residuals vs fitted plot does not seem to show any curve patterns so
no transformation is necessary.
Below we compare the performances of SVR Linear, SVR RBF, and OLS
regression. The MSE and MAE values are all similar, with RBF kernel
doing the best.
Performance <- data.frame(RBF.SVR=c(mse.RBF, mae.RBF),
Linear.SVR = c(mse.lin, mae.lin),
LSE.Reg =c(mse.lse, mae.lse))
row.names(Performance) <- c("MSE", "MAE")
##
pander(Performance)
| MSE |
0.01842 |
0.02233 |
0.02067 |
| MAE |
0.08615 |
0.09348 |
0.1006 |
Results/Conclusion
For regularized linear regression, the optimal parameters for lasso
is 0.1328, 0.1334 for ridge, and 0.1329 for elastic net. These RMSE
values are based on lambda.min (value of lambda that gives the minimum
cross-validated error). Based on these values, we get this equation for
lasso:
Model equation: y = 0.7338 + 0.0661targeted_productivity +
-0.0788smv + 0.0059wip + -0.0128over_time +
0.0071incentive + 0.0053idle_time + -0.0293idle_men +
-0.0168no_of_style_change + 0.1039no_of_workers +
0.0224department + -0.0145team2 + 9e-04team3 +
-0.0049team4 + -0.0113team5 + -0.0195team6 +
-0.0273team7 + -0.0256team8 + -0.0253team9 +
-0.0228team10 + -0.0366team11 + -0.0122team12 +
-0.0049dayMonday + 0.0029daySaturday + -0.0035daySunday +
-0.0059dayThursday + 0.0039dayTuesday + 0*dayWednesday
This equation for ridge:
Model equation: y = 0.7338 + 0.0637targeted_productivity +
-0.0532smv + 0.0072wip + -0.0083over_time +
0.0077incentive + 0.0045idle_time + -0.0277idle_men +
-0.0164no_of_style_change + 0.0564no_of_workers +
0.0042department + -0.0106team2 + 0.005team3 +
-0.0013team4 + -0.0085team5 + -0.0193team6 +
-0.0242team7 + -0.0224team8 + -0.0212team9 +
-0.0187team10 + -0.0293team11 + -0.0117team12 +
-0.0038dayMonday + 0.0041daySaturday + -0.0028daySunday +
-0.0047dayThursday + 0.0052dayTuesday + 0.0019*dayWednesday
This equation for elastic net:
Model equation: y = 0.7338 + 0.066targeted_productivity +
-0.076smv + 0.006wip + -0.012over_time +
0.0071incentive + 0.0051idle_time + -0.029idle_men +
-0.0166no_of_style_change + 0.0965no_of_workers +
0.0185department + -0.0135team2 + 0.0018team3 +
-0.0038team4 + -0.0104team5 + -0.0193team6 +
-0.0265team7 + -0.0248team8 + -0.0243team9 +
-0.0219team10 + -0.0353team11 + -0.0119team12 +
-0.004dayMonday + 0.0035daySaturday + -0.0028daySunday +
-0.0051dayThursday + 0.0045dayTuesday + 8e-04*dayWednesday
For lasso, ridge, and elastic in regularized logistic regression we
get the coefficients shown in ‘regularized.coef1’. When we compare the
performance of the lasso, Ridge, and Elastic Net models on our test data
using ROC curves, we see that all models have similar performances with
AUC of 0.762 for lasso, 0.757 for ridge, and 0.758 for elastic.
Since the optimal parameter was the lowest in regularized linear and
the AUC was highest in regularized logistic regression, a lasso model is
recommended. Lasso is preferred because it performs feature selection
(shrinking coefficients to 0), easier to interpret, and better for
High-dimensional data.
For SVM, the AUC for SVM (linear kernel) is 0.789, 0.8193 for SVM
(RBF kernel), and 0.79 for standard stepwise logistic regression.
Overall, since the AUC for SVM (both linear/RBF) are greater than the
AUCs in the regularized logistic regression, we can assume that SVM is
better because they are robust to overfitting, can use kernels, and can
handle linear/nonlinear data.
For SVR, SVR RBF has an MSE of 0.01842, 0.02233 for SVR Linear, and
0.02067 for OLS regression. Since the MSE values are lower than that in
regularized linear regression, we can assume that SVR is better.
LS0tDQp0aXRsZTogIlByb2R1Y3Rpdml0eSBQcmVkaWN0aW9uIG9mIEdhcm1lbnQgRW1wbG95ZWVzIERhdGFzZXQgRmluYWwgUmVwb3J0Ig0KYXV0aG9yOiAiRXJpYyBaaHUiDQpkYXRlOiAiMjAyNS0wMy0zMSINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jX2NvbGxhcHNlZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogeWVzDQogICAgdGhlbWU6IGx1bWVuDQogIHBkZl9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIGZpZ193aWR0aDogMw0KICAgIGZpZ19oZWlnaHQ6IDMNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHs9aHRtbH0NCg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCg0KLyogQ2FzY2FkaW5nIFN0eWxlIFNoZWV0cyAoQ1NTKSBpcyBhIHN0eWxlc2hlZXQgbGFuZ3VhZ2UgdXNlZCB0byBkZXNjcmliZSB0aGUgcHJlc2VudGF0aW9uIG9mIGEgZG9jdW1lbnQgd3JpdHRlbiBpbiBIVE1MIG9yIFhNTC4gaXQgaXMgYSBzaW1wbGUgbWVjaGFuaXNtIGZvciBhZGRpbmcgc3R5bGUgKGUuZy4sIGZvbnRzLCBjb2xvcnMsIHNwYWNpbmcpIHRvIFdlYiBkb2N1bWVudHMuICovDQoNCmgxLnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovDQogIGZvbnQtc2l6ZTogMjRweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGNvbG9yOiBuYXZ5Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsNCn0NCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgYXV0aG9ycyAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBmb250LXdlaWdodDogYm9sZDsNCiAgY29sb3I6IG5hdnk7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmg0LmRhdGUgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIHRoZSBkYXRlICAqLw0KICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAyMHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCn0NCmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KaDMgeyAvKiBIZWFkZXIgMyAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgMyBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMTZweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoNCB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCA0IHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxNHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCi5oaWdobGlnaHRtZSB7IGJhY2tncm91bmQtY29sb3I6eWVsbG93OyB9DQoNCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCjwvc3R5bGU+DQoNCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCANCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLg0KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQogICBsaWJyYXJ5KGtuaXRyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KfQ0KaWYgKCFyZXF1aXJlKCJyZWFkeGwiKSkgeyAjIFNWTSBtZXRob2RvbG9neQ0KICAgaW5zdGFsbC5wYWNrYWdlcygicmVhZHhsIikNCmxpYnJhcnkocmVhZHhsKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnZ3Bsb3QyIikpIHsgIyBTVk0gbWV0aG9kb2xvZ3kNCiAgIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJJU0xSIikpIHsgIyBjb250YWlucyBleGFtcGxlIGRhdGEgc2V0ICJLaGFuIg0KICAgaW5zdGFsbC5wYWNrYWdlcygiSVNMUiIpDQpsaWJyYXJ5KElTTFIpDQp9DQppZiAoIXJlcXVpcmUoIk1BU1NFeHRyYSIpKSB7ICMgY29udGFpbnMgZXhhbXBsZSBkYXRhIHNldCAiS2hhbiINCiAgIGluc3RhbGwucGFja2FnZXMoIk1BU1NFeHRyYSIpDQpsaWJyYXJ5KE1BU1NFeHRyYSkNCn0NCmlmICghcmVxdWlyZSgibWlzc01ldGhvZHMiKSkgeyAjIGN1c3RvbWl6ZWQgY29sb3Jpbmcgb2YgcGxvdHMNCiAgIGluc3RhbGwucGFja2FnZXMoIm1pc3NNZXRob2RzIikNCmxpYnJhcnkobWlzc01ldGhvZHMpDQp9DQppZiAoIXJlcXVpcmUoIkhtaXNjIikpIHsgIyBjdXN0b21pemVkIGNvbG9yaW5nIG9mIHBsb3RzDQogICBpbnN0YWxsLnBhY2thZ2VzKCJIbWlzYyIpDQpsaWJyYXJ5KEhtaXNjKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnbG1uZXQiKSkgeyAjIGN1c3RvbWl6ZWQgY29sb3Jpbmcgb2YgcGxvdHMNCiAgIGluc3RhbGwucGFja2FnZXMoImdsbW5ldCIpDQpsaWJyYXJ5KGdsbW5ldCkNCn0NCmlmICghcmVxdWlyZSgiR0dhbGx5IikpIHsgIyBjdXN0b21pemVkIGNvbG9yaW5nIG9mIHBsb3RzDQogICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQ0KbGlicmFyeShHR2FsbHkpDQp9DQppZiAoIXJlcXVpcmUoIm1pY2UiKSkgeyAjIGN1c3RvbWl6ZWQgY29sb3Jpbmcgb2YgcGxvdHMNCiAgIGluc3RhbGwucGFja2FnZXMoIm1pY2UiKQ0KbGlicmFyeShtaWNlKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwbG90bHkiKSkgeyAjIGN1c3RvbWl6ZWQgY29sb3Jpbmcgb2YgcGxvdHMNCiAgIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQpsaWJyYXJ5KHBsb3RseSkNCn0NCmlmICghcmVxdWlyZSgiY2FyZXQiKSkgeyAjIGN1c3RvbWl6ZWQgY29sb3Jpbmcgb2YgcGxvdHMNCiAgIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikNCmxpYnJhcnkoY2FyZXQpDQp9DQppZiAoIXJlcXVpcmUoInBST0MiKSkgeyAjIGN1c3RvbWl6ZWQgY29sb3Jpbmcgb2YgcGxvdHMNCiAgIGluc3RhbGwucGFja2FnZXMoInBST0MiKQ0KbGlicmFyeShwUk9DKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwYW5kZXIiKSkgeyAjIGN1c3RvbWl6ZWQgY29sb3Jpbmcgb2YgcGxvdHMNCiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIpDQpsaWJyYXJ5KHBhbmRlcikNCn0NCmlmICghcmVxdWlyZSgiZTEwNzEiKSkgeyAjIGN1c3RvbWl6ZWQgY29sb3Jpbmcgb2YgcGxvdHMNCiAgIGluc3RhbGwucGFja2FnZXMoImUxMDcxIikNCmxpYnJhcnkoZTEwNzEpDQp9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgICMgaW5jbHVkZSBjb2RlIGNodW5rIGluIHRoZSBvdXRwdXQgZmlsZQ0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgICAjIHNvbWV0aW1lcywgeW91IGNvZGUgbWF5IHByb2R1Y2Ugd2FybmluZyBtZXNzYWdlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIG91dHB1dCBmaWxlLiANCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHRzID0gVFJVRSwgICAgIyB5b3UgY2FuIGFsc28gZGVjaWRlIHdoZXRoZXIgdG8gaW5jbHVkZSB0aGUgb3V0cHV0DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLg0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkENCiAgICAgICAgICAgICAgICAgICAgICApDQpgYGANCg0KDQojIEludHJvZHVjdGlvbg0KDQpUaGlzIGRhdGFzZXQgaXMgdGl0bGVkIOKAnFByb2R1Y3Rpdml0eSBQcmVkaWN0aW9uIG9mIEdhcm1lbnQgRW1wbG95ZWVz4oCdIGFuZCBzaG93cyB2YXJpb3VzIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyBhbmQgb25lIHRhcmdldC9yZXNwb25zZSB2YXJpYWJsZSAoYWN0dWFsX3Byb2R1Y3Rpdml0eSkuIFRoZSBwdXJwb3NlIG9mIGNvbGxlY3RpbmcgdGhpcyBkYXRhc2V0IGlzIHRvIHNlZSB3aGF0IGZhY3RvcnMgbWF5IGNhdXNlIGVtcGxveWVlcyBwcm9kdWN0aXZpdHkgbGV2ZWxzIHRvIGJlIGhpZ2ggb3IgbG93LiBUaGlzIGRhdGFzZXQgd2FzIGNvbGxlY3RlZCBmcm9tIEthZ2dsZS4gVGhpcyBkYXRhc2V0IGNvbnRhaW5zIDExOTcgb2JzZXJ2YXRpb25zIGFuZCAxNSB2YXJpYWJsZXMgKDE0IGZlYXR1cmUgdmFyaWFibGVzIGFuZCAxIHRhcmdldCB2YXJpYWJsZSkuDQoNClRoZSBkYXRlIChjYXRlZ29yaWNhbCkgc2hvd3MgdGhlIGRhdGUgaW4gTU0tREQtWVlZWSBmb3JtYXQuIEhvd2V2ZXIsIHRoZSBkYXRlIGlzIG1vcmUgb2YgYW4gaWRlbnRpZmllciB0aGFuIGEgcHJlZGljdG9yLiBUaGUgcXVhcnRlciAoY2F0ZWdvcmljYWwpIHNob3dzIHRoZSBwb3J0aW9uIG9mIHRoZSBtb250aCAoZGl2aWRlZCBpbnRvIDQgcXVhcnRlcnMpLiBUaGUgZGVwYXJ0bWVudCAoY2F0ZWdvcmljYWwpIHNob3dzIHRoZSBkZXBhcnRtZW50IChzZXdpbmcvZmluaXNoaW5nKS4gVGhlIGRheSAoY2F0ZWdvcmljYWwpIHNob3dzIHRoZSBkYXkgb2YgdGhlIHdlZWsuIFRoZSB0ZWFtIChjYXRlZ29yaWNhbCByZXByZXNlbnRlZCBhcyBudW1lcmljKSBzaG93cyB0aGUgdGVhbSBudW1iZXIuIFRoZSB0YXJnZXRlZF9wcm9kdWN0aXZpdHkgKG51bWVyaWNhbCkgc2hvd3MgdGhlIHRhcmdldGVkIHByb2R1Y3Rpdml0eSBsZXZlbHMuIFRoZSBzbXYgKG51bWVyaWNhbCkgc2hvd3MgdGhlIFN0YW5kYXJkIE1pbnV0ZSBWYWx1ZSAoYWxsb2NhdGVkIHRpbWUgZm9yIGEgdGFzaykuIFRoZSB3aXAgKG51bWVyaWNhbCkgc2hvd3MgdGhlIHdvcmsgaW4gcHJvZ3Jlc3MgKEluY2x1ZGVzIHRoZSBudW1iZXIgb2YgdW5maW5pc2hlZCBpdGVtcyBmb3IgcHJvZHVjdHMpLiBUaGUgb3Zlcl90aW1lIChudW1lcmljYWwpIHNob3dzIHRoZSBhbW91bnQgb2Ygb3ZlcnRpbWUgYnkgZWFjaCB0ZWFtIGluIG1pbnV0ZXMuIFRoZSBpbmNlbnRpdmUgKG51bWVyaWNhbCkgc2hvd3MgdGhlIGFtb3VudCBvZiBmaW5hbmNpYWwgaW5jZW50aXZlIGluIEJEVC4gVGhlIGlkbGVfdGltZSAobnVtZXJpY2FsKSBzaG93cyB0aGUgYW1vdW50IG9mIHRpbWUgd2hlbiB0aGUgcHJvZHVjdGlvbiB3YXMgaW50ZXJydXB0ZWQuIFRoZSBpZGxlX21lbiAobnVtZXJpY2FsKSBzaG93cyB0aGUgbnVtYmVyIG9mIHdvcmtlcnMgd2hvIHdlcmUgaWRsZSBkdWUgdG8gcHJvZHVjdGlvbiBpbnRlcnJ1cHRpb24uIFRoZSBub19vZl9zdHlsZV9jaGFuZ2UgKG51bWVyaWNhbCkgc2hvd3MgdGhlIG51bWJlciBvZiBjaGFuZ2VzIGluIHRoZSBzdHlsZSBvZiBhIHBhcnRpY3VsYXIgcHJvZHVjdC4gVGhlIG5vX29mX3dvcmtlcnMgKG51bWVyaWNhbCkgc2hvd3MgdGhlIG51bWJlciBvZiBvZiB3b3JrZXJzIGluIGVhY2ggdGVhbS4gVGhlIHRhcmdldC9yZXNwb25zZSB2YXJpYWJsZSAoYWN0dWFsX3Byb2R1Y3Rpdml0eSkgc2hvd3MgdGhlIGFjdHVhbCBwcm9kdWN0aXZpdHkgbGV2ZWwgKGJldHdlZW4gMC0xKS4NCg0KVGhlIG9yaWdpbmFsIGRhdGFzZXQgaGFzIDUwNiBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgd2lwIGNvbHVtbi4gVGhpcyBjYW4gYmUgcmVzb2x2ZWQgYnkgTUlDRSBpbXB1dGF0aW9uLg0KDQpCYXNlZCBvbiB0aGlzIGRhdGFzZXQsIHdlIGNhbiBmb3JtdWxhdGUgYSByZXNlYXJjaCBxdWVzdGlvbi4gVGhlIHF1ZXN0aW9uIHdlIGNhbiBmb3JtIGlzIHdoYXQgdmFyaWFibGVzIGFyZSB0aGUgbW9zdCBpbXBvcnRhbnQgaW4gcHJlZGljdGluZyBwcm9kdWN0aXZpdHkgbGV2ZWwgb2YgZW1wbG95ZWVzLiBGb3IgcmVncmVzc2lvbiwgd2UgY2FuIHVzZSBhY3R1YWxfcHJvZHVjdGl2aXR5IChjb250aW51b3VzKSBhcyBvdXIgcmVzcG9uc2UgdmFyaWFibGUuIEZvciBjbGFzc2lmaWNhdGlvbiwgd2UgY2FuIGNyZWF0ZSBhIGJpbmFyeSByZXNwb25zZSB2YXJpYWJsZSB3aXRoIDEgKHByb2R1Y3RpdmUpIGFuZCAwIChub3QgcHJvZHVjdGl2ZSkuIFdlIGNhbiB1c2UgdGhlIGNvbmRpdGlvbiBpZiBhY3R1YWxfcHJvZHVjdGl2aXR5IGlzIGdyZWF0ZXIgdGhhbiB0aGUgdGFyZ2V0ZWRfcHJvZHVjdGl2aXR5Lg0KDQpXZSB3aWxsIHVzZSBSZWd1bGFyaXplZCBMaW5lYXIvbG9naXN0aWMgUmVncmVzc2lvbiBhbmQgU1ZNL1NWUiAoTGluZWFyL1JCRiBrZXJuZWxzKSB0byB0cnkgdG8gYW5zd2VyIHRoZSByZXNlYXJjaCBxdWVzdGlvbi4NCg0KIyBSZWFkIGluIERhdGFzZXQNCg0KUmVhZCBpbiBkYXRhc2V0Lg0KDQpgYGB7cn0NCmdhcm1lbnQgPC0gcmVhZC5jc3YoImdhcm1lbnRzX3dvcmtlcl9wcm9kdWN0aXZpdHkuY3N2IikNCmBgYA0KDQpTZWUgZGF0YXNldCBzdHJ1Y3R1cmUuDQoNCmBgYHtyfQ0Kc3RyKGdhcm1lbnQpDQpgYGANCg0KDQojIyBDaGVjayBmb3IgTWlzc2luZyBWYWx1ZXMNCg0KNTA2IG1pc3NpbmcgdmFsdWVzIGluIHRoZSB3aXAgY29sdW1uLg0KDQpgYGB7cn0NCmNvbFN1bXMoaXMubmEoZ2FybWVudCkpDQpgYGANCg0KDQojIEVEQQ0KDQpUaGlzIHNlY3Rpb24gd2lsbCBzaG93IHRoZSBkaXN0cmlidXRpb24gb2YgZWFjaCBpbmRpdmlkdWFsIGZlYXR1cmUuDQoNClRoZSBiZWxvdyBmaWd1cmUgc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcXVhcnRlciB2YXJpYWJsZS4NCg0KYGBge3J9DQpnZ3Bsb3QoZ2FybWVudCwgYWVzKHggPSBxdWFydGVyKSkgKyANCiAgDQogIGdlb21fYmFyKCkgKw0KICBsYWJzKHRpdGxlID0gInF1YXJ0ZXIiKQ0KYGBgDQoNClRoZSBiZWxvdyBmaWd1cmUgc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGVwYXJ0bWVudCB2YXJpYWJsZS4gRGF0YSBlbnRyeSBlcnJvcnMgaW4gdGhpcyBjb2x1bW4gd2VyZSByZXNvbHZlZC4NCg0KYGBge3J9DQoNCmdhcm1lbnQkZGVwYXJ0bWVudFtnYXJtZW50JGRlcGFydG1lbnQgPT0gJ2ZpbmlzaGluZyAnXSA8LSAnZmluaXNoaW5nJw0KDQpnZ3Bsb3QoZ2FybWVudCwgYWVzKHggPSBkZXBhcnRtZW50KSkgKyANCiAgDQogIGdlb21fYmFyKCkgKw0KICBsYWJzKHRpdGxlID0gImRlcGFydG1lbnQiKQ0KYGBgDQoNClRoZSBiZWxvdyBmaWd1cmUgc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF5IHZhcmlhYmxlLg0KDQpgYGB7cn0NCmdncGxvdChnYXJtZW50LCBhZXMoeCA9IGRheSkpICsgDQogIA0KICBnZW9tX2JhcigpICsNCiAgbGFicyh0aXRsZSA9ICJkYXkiKQ0KYGBgDQoNClRoZSBiZWxvdyBmaWd1cmUgc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdGVhbSB2YXJpYWJsZS4NCg0KYGBge3J9DQojIGdncGxvdChkYXRhID0gZ2FybWVudCwgYWVzKHggPSB0ZWFtKSkgKyANCiMgICBnZW9tX2JveHBsb3QoKSArIA0KIyAgIA0KIyAgIGxhYnModGl0bGUgPSAidGVhbSIpDQpnYXJtZW50JHRlYW0gPC0gYXMuZmFjdG9yKGdhcm1lbnQkdGVhbSkNCg0KZ2dwbG90KGdhcm1lbnQsIGFlcyh4ID0gdGVhbSkpICsgDQogIA0KICBnZW9tX2JhcigpICsNCiAgbGFicyh0aXRsZSA9ICJ0ZWFtIikNCmBgYA0KDQpUaGUgYmVsb3cgZmlndXJlIHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHRhcmdldGVkX3Byb2R1Y3Rpdml0eSB2YXJpYWJsZS4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGdhcm1lbnQsIGFlcyh4ID0gdGFyZ2V0ZWRfcHJvZHVjdGl2aXR5KSkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgDQogIGxhYnModGl0bGUgPSAidGFyZ2V0ZWRfcHJvZHVjdGl2aXR5IikNCmBgYA0KDQpUaGUgYmVsb3cgZmlndXJlIHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHNtdiB2YXJpYWJsZS4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGdhcm1lbnQsIGFlcyh4ID0gc212KSkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgDQogIGxhYnModGl0bGUgPSAic212IikNCmBgYA0KDQpUaGUgYmVsb3cgZmlndXJlIHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHdpcCB2YXJpYWJsZS4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGdhcm1lbnQsIGFlcyh4ID0gd2lwKSkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgDQogIGxhYnModGl0bGUgPSAid2lwIikNCmBgYA0KDQpUaGUgYmVsb3cgZmlndXJlIHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIG92ZXJfdGltZSB2YXJpYWJsZS4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGdhcm1lbnQsIGFlcyh4ID0gb3Zlcl90aW1lKSkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgDQogIGxhYnModGl0bGUgPSAib3Zlcl90aW1lIikNCmBgYA0KDQpUaGUgYmVsb3cgZmlndXJlIHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGluY2VudGl2ZSB2YXJpYWJsZS4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGdhcm1lbnQsIGFlcyh4ID0gaW5jZW50aXZlKSkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgDQogIGxhYnModGl0bGUgPSAiaW5jZW50aXZlIikNCmBgYA0KDQpUaGUgYmVsb3cgZmlndXJlIHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGlkbGVfdGltZSB2YXJpYWJsZS4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGdhcm1lbnQsIGFlcyh4ID0gaWRsZV90aW1lKSkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgDQogIGxhYnModGl0bGUgPSAiaWRsZV90aW1lIikNCmBgYA0KDQpUaGUgYmVsb3cgZmlndXJlIHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGlkbGVfbWVuIHZhcmlhYmxlLg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gZ2FybWVudCwgYWVzKHggPSBpZGxlX21lbikpICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIA0KICBsYWJzKHRpdGxlID0gImlkbGVfbWVuIikNCmBgYA0KDQpUaGUgYmVsb3cgZmlndXJlIHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIG5vX29mX3N0eWxlX2NoYW5nZSB2YXJpYWJsZS4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGdhcm1lbnQsIGFlcyh4ID0gbm9fb2Zfc3R5bGVfY2hhbmdlKSkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgDQogIGxhYnModGl0bGUgPSAibm9fb2Zfc3R5bGVfY2hhbmdlIikNCmBgYA0KDQpUaGUgYmVsb3cgZmlndXJlIHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIG5vX29mX3dvcmtlcnMgdmFyaWFibGUuDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBnYXJtZW50LCBhZXMoeCA9IG5vX29mX3dvcmtlcnMpKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICANCiAgbGFicyh0aXRsZSA9ICJub19vZl93b3JrZXJzIikNCmBgYA0KDQojIEltcHV0YXRpb24NCg0KV2UgdXNlIHRoZSBNSUNFIGltcHV0YXRpb24gbWV0aG9kIGluIHRoaXMgc2VjdGlvbiB0byByZXBsYWNlIGFsbCBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgd2lwIGNvbHVtbi4NCg0KY29tcGxldGVfZ2FybWVudF9kYXRhIGlzIHRoZSBjb21wbGV0ZSBkYXRhc2V0Lg0KDQpgYGB7cn0NCmluaXQgPC0gbWljZShnYXJtZW50LCBtYXhpdCA9IDApDQppbml0JG1ldGhvZA0KYGBgDQoNCmBgYHtyfQ0KaW1wIDwtIG1pY2UoZ2FybWVudCwgbWV0aG9kID0gYygiIiwiIiwgIiIsICIiLCAgIiIsICIiLCAiIiwgInBtbSIsICIiLCAiIiwgIiIsICIiLCAiIiwgIiIsICIiKSwgDQogICAgICAgICAgICAgICAgIG1heGl0ID0gMTAsICANCiAgICAgICAgICAgICAgICAgbSA9IDUsIA0KICAgICAgICAgICAgICAgICBzZWVkPTEyMywNCiAgICAgICAgICAgICAgICAgcHJpbnQ9RikgICAgIA0KDQoNCg0KY29tcGxldGVfZ2FybWVudF9kYXRhIDwtIGNvbXBsZXRlKGltcCkNCmBgYA0KDQojIEZlYXR1cmUgRW5naW5lZXJpbmcNCg0KVGhpcyBzZWN0aW9uIGZvY3VzZXMgb24gZmVhdHVyZSBlbmdpbmVlcmluZy4NCg0KVGhlICd0ZWFtJyB2YXJpYWJsZSB3YXMgYWxyZWFkeSBjb252ZXJ0ZWQgdG8gYSBjYXRlZ29yaWNhbC9mYWN0b3IgdmFyaWFibGUgaW4gdGhlIEVEQSBzZWN0aW9uLiBEYXRlIHdhcyBkcm9wcGVkIGZyb20gdGhlIGFuYWx5c2lzIGFzIGl0IHJlYWxseSBzZXJ2ZXMgYXMgbW9yZSBvZiBhbiBpZGVudGlmaWVyLiBRdWFydGVyIHdhcyBhbHNvIGRyb3BwZWQgc2luY2UgaXQgaXMgcmVsaWFudCBvbiB0aGUgZGF0ZSBhbmQgdGhlIGNvdW50cyBmb3IgYWxsIHF1YXJ0ZXJzIHZhcnkgc2lnbmlmaWNhbnRseSwgYXMgc2hvd24gaW4gdGhlIEVEQSBzZWN0aW9uLg0KDQpCZWxvdywgd2UgaGF2ZSB0aGUgJ2RlcGFydG1lbnQnIHZhcmlhYmxlIChzZXdpbmcvZmluaXNoaW5nKS4gVGhpcyB2YXJpYWJsZSBpcyBjb252ZXJ0ZWQgdG8gYmluYXJ5LCB3aXRoIHNld2luZyBhcyAwIGFuZCBmaW5pc2hpbmcgYXMgMS4NCg0KYGBge3J9DQoNCmNvbXBsZXRlX2dhcm1lbnRfZGF0YTEgPC0gY29tcGxldGVfZ2FybWVudF9kYXRhDQoNCg0KDQoNCg0KDQpjb21wbGV0ZV9nYXJtZW50X2RhdGExJGRlcGFydG1lbnQgPC0gaWZlbHNlKGNvbXBsZXRlX2dhcm1lbnRfZGF0YTEkZGVwYXJ0bWVudD09InN3ZWluZyIsMCwxKQ0KDQoNCg0KY29tcGxldGVfZ2FybWVudF9kYXRhMSRkZXBhcnRtZW50IDwtIGFzLmZhY3Rvcihjb21wbGV0ZV9nYXJtZW50X2RhdGExJGRlcGFydG1lbnQpDQoNCg0KDQpgYGANCg0KQmVsb3csIHdlIGRvIG9uZSBob3QgZW5jb2RpbmcgZm9yIHRoZSAnZGF5JyB2YXJpYWJsZSBzaW5jZSB0aGUgdmFsdWVzIGFyZSBub21pbmFsLg0KDQpgYGB7cn0NCmVuY29kZWRfZGF0YSA8LSBtb2RlbC5tYXRyaXgofiBkYXkgLSAxLCBkYXRhID0gY29tcGxldGVfZ2FybWVudF9kYXRhMSkNCg0KY29tcGxldGVfZ2FybWVudF9kYXRhMSA8LSBjYmluZChjb21wbGV0ZV9nYXJtZW50X2RhdGExLCBlbmNvZGVkX2RhdGEpDQoNCmNvbXBsZXRlX2dhcm1lbnRfZGF0YTEkZGF5TW9uZGF5IDwtIGFzLmZhY3Rvcihjb21wbGV0ZV9nYXJtZW50X2RhdGExJGRheU1vbmRheSkNCmNvbXBsZXRlX2dhcm1lbnRfZGF0YTEkZGF5U2F0dXJkYXkgPC0gYXMuZmFjdG9yKGNvbXBsZXRlX2dhcm1lbnRfZGF0YTEkZGF5U2F0dXJkYXkpDQpjb21wbGV0ZV9nYXJtZW50X2RhdGExJGRheVN1bmRheSA8LSBhcy5mYWN0b3IoY29tcGxldGVfZ2FybWVudF9kYXRhMSRkYXlTdW5kYXkpDQpjb21wbGV0ZV9nYXJtZW50X2RhdGExJGRheVRodXJzZGF5IDwtIGFzLmZhY3Rvcihjb21wbGV0ZV9nYXJtZW50X2RhdGExJGRheVRodXJzZGF5KQ0KY29tcGxldGVfZ2FybWVudF9kYXRhMSRkYXlUdWVzZGF5IDwtIGFzLmZhY3Rvcihjb21wbGV0ZV9nYXJtZW50X2RhdGExJGRheVR1ZXNkYXkpDQpjb21wbGV0ZV9nYXJtZW50X2RhdGExJGRheVdlZG5lc2RheSA8LSBhcy5mYWN0b3IoY29tcGxldGVfZ2FybWVudF9kYXRhMSRkYXlXZWRuZXNkYXkpDQpgYGANCg0KIyBSZWd1bGFyaXplZCBMaW5lYXIgUmVncmVzc2lvbg0KDQpJbiB0aGlzIHNlY3Rpb24sIHdlIGRvIHJlZ3VsYXJpemVkIGxpbmVhciByZWdyZXNzaW9uLiBXZSBkbyBsYXNzbywgcmlkZ2UsIGFuZCBlbGFzdGljIG5ldCByZWd1bGFyaXphdGlvbi4gRm9yIGVhY2ggcmVndWxhcml6YXRpb24gdGVjaG5pcXVlLCB3ZSBjb25kdWN0IGNvZWZmaWNpZW50IHBhdGggYW5hbHlzaXMgdG8gc2VlIGhvdyB3ZWxsIGVhY2ggcHJlZGljdG9yIHZhcmlhYmxlIHNocmlua3MuIFRoZW4gd2UgdXNlIGNyb3NzLXZhbGlkYXRpb24gdG8gZGV0ZXJtaW5lIHRoZSBvcHRpbWFsIHJlZ3VsYXJpemF0aW9uIHBhcmFtZXRlciBmb3IgZWFjaC4gTGFzdGx5LCB3ZSBnZW5lcmF0ZSB0aGUgZmluYWwgbW9kZWwgYmFzZWQgb24gdGhlIG9wdGltYWwgcmVndWxhcml6YXRpb24gcGFyYW1ldGVycy4NCg0KIyMgQ29lZmZpY2llbnQgUGF0aCBBbmFseXNpcw0KDQpCZWxvdywgd2Ugc3BsaXQgdGhlIGRhdGEgaW50byBvdXIgdHJhaW5pbmcgYW5kIHRlc3RpbmcuIFRoZW4gd2UgcGVyZm9ybSBsYXNzbywgcmlkZ2UsIGFuZCBlbGFzdGljIG5ldC4NCg0KYGBge3J9DQpzZXQuc2VlZCgxMTIyMzMpDQoNClggPC0gbW9kZWwubWF0cml4KH4gZGVwYXJ0bWVudCt0ZWFtK2RheU1vbmRheStkYXlTYXR1cmRheSsgZGF5U3VuZGF5KyBkYXlUaHVyc2RheSsgZGF5VHVlc2RheSsgZGF5V2VkbmVzZGF5LCBkYXRhID0gY29tcGxldGVfZ2FybWVudF9kYXRhMSlbLCAtMV0NClggPC0gY2JpbmQoY29tcGxldGVfZ2FybWVudF9kYXRhMSR0YXJnZXRlZF9wcm9kdWN0aXZpdHksIGNvbXBsZXRlX2dhcm1lbnRfZGF0YTEkc212LCBjb21wbGV0ZV9nYXJtZW50X2RhdGExJHdpcCwgY29tcGxldGVfZ2FybWVudF9kYXRhMSRvdmVyX3RpbWUsIGNvbXBsZXRlX2dhcm1lbnRfZGF0YTEkaW5jZW50aXZlLCBjb21wbGV0ZV9nYXJtZW50X2RhdGExJGlkbGVfdGltZSwgY29tcGxldGVfZ2FybWVudF9kYXRhMSRpZGxlX21lbiwgY29tcGxldGVfZ2FybWVudF9kYXRhMSRub19vZl9zdHlsZV9jaGFuZ2UsIGNvbXBsZXRlX2dhcm1lbnRfZGF0YTEkbm9fb2Zfd29ya2VycywgWCkNCg0KDQpjb2xuYW1lcyhYKSA8LSBjKCd0YXJnZXRlZF9wcm9kdWN0aXZpdHknLCdzbXYnLCAnd2lwJywgDQogICAgICAgICAgICAgICAgICAgICAgICdvdmVyX3RpbWUnLCANCiAgICAgICAgICAgICAgICAgICAgICAgJ2luY2VudGl2ZScsICdpZGxlX3RpbWUnICwgJ2lkbGVfbWVuJywNCiAgICAgICAgICAgICAgICAgICAgICAgJ25vX29mX3N0eWxlX2NoYW5nZScsICdub19vZl93b3JrZXJzJywnZGVwYXJ0bWVudCcsDQogICAgICAgICAgICAgICAgICAgICAgICd0ZWFtMicsJ3RlYW0zJywndGVhbTQnLCd0ZWFtNScsJ3RlYW02JywgJ3RlYW03JywndGVhbTgnLCd0ZWFtOScsJ3RlYW0xMCcsJ3RlYW0xMScsJ3RlYW0xMicsJ2RheU1vbmRheScsJ2RheVNhdHVyZGF5JywnZGF5U3VuZGF5JywnZGF5VGh1cnNkYXknLCdkYXlUdWVzZGF5JywnZGF5V2VkbmVzZGF5JykNCg0KeSA8LSBjb21wbGV0ZV9nYXJtZW50X2RhdGExJGFjdHVhbF9wcm9kdWN0aXZpdHkNCg0KdHJhaW5faW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5LCBwID0gMC44LCBsaXN0ID0gRkFMU0UpDQpYX3RyYWluIDwtIFhbdHJhaW5faW5kZXgsIF0NClhfdGVzdCA8LSBYWy10cmFpbl9pbmRleCwgXQ0KeV90cmFpbiA8LSB5W3RyYWluX2luZGV4XQ0KeV90ZXN0IDwtIHlbLXRyYWluX2luZGV4XQ0KDQpwcmVwcm9jZXNzX3BhcmFtcyA8LSBwcmVQcm9jZXNzKFhfdHJhaW4sIG1ldGhvZCA9IGMoImNlbnRlciIsICJzY2FsZSIpKQ0KWF90cmFpbiA8LSBwcmVkaWN0KHByZXByb2Nlc3NfcGFyYW1zLCBYX3RyYWluKQ0KWF90ZXN0IDwtIHByZWRpY3QocHJlcHJvY2Vzc19wYXJhbXMsIFhfdGVzdCkNCg0KZml0X2xhc3NvIDwtIGdsbW5ldChYX3RyYWluLCANCiAgICAgICAgICAgICAgICAgICAgICB5X3RyYWluLCANCiAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDEpICAgICAgICAgDQpmaXRfcmlkZ2UgPC0gZ2xtbmV0KFhfdHJhaW4sIA0KICAgICAgICAgICAgICAgICAgICB5X3RyYWluLCANCiAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwKSAgICAgICAgICANCmZpdF9lbGFzdGljX25ldCA8LSBnbG1uZXQoWF90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHlfdHJhaW4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgDQoNCmN2X2xhc3NvIDwtIGN2LmdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDEpICANCmN2X3JpZGdlIDwtIGN2LmdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDApICANCmN2X2VsYXN0aWNfbmV0IDwtIGN2LmdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDAuNSkgDQpgYGANCg0KQmVsb3cgc2hvd3MgdGhlIHBsb3Qgb2YgdGhlIGNvZWZmaWNpZW50IHBhdGhzIGZvciBhbGwgdGhlIHByZWRpY3RvciB2YXJpYWJsZXMgZm9yIGxhc3NvLiBJdCBzaG93cyBob3cgdmFyaWFibGVzIGFyZSBzaHJ1bmsgdG8gZXhhY3RseSAwLg0KDQpgYGB7cn0NCnBhcihtYXI9Yyg1LDQsNiwzKSkNCiMgUGxvdCBjb2VmZmljaWVudCBwYXRoDQpwbG90KGZpdF9sYXNzbywgeHZhciA9ICJsYW1iZGEiLCBsYWJlbCA9IFRSVUUsDQogICAgIGx3ZCA9IDEuNSwNCiAgICAgbWFpbiA9ICJDb2VmZmljaWVudCBQYXRoIEFuYWx5c2lzOiBMQVNTTyIsDQogICAgIGNleC5tYWluID0gMC45LA0KICAgICBjb2wgPSByYWluYm93KDEwKSkNCmFibGluZSh2ID0gMSwgY29sID0gInB1cnBsZSIsIGx0eSA9IDQsIGx3ZCA9IDIpDQphYmxpbmUodiA9IC0xLCBjb2wgPSAic3RlZWxibHVlIiwgbHR5ID0gMiwgbHdkID0gMikNCmBgYA0KDQpCZWxvdyBzaG93cyB0aGUgcGxvdCBvZiB0aGUgY29lZmZpY2llbnQgcGF0aHMgZm9yIGFsbCB0aGUgcHJlZGljdG9yIHZhcmlhYmxlcyBmb3IgcmlkZ2UuIEl0IHNob3dzIGhvdyB2YXJpYWJsZXMgYXJlIHNocnVuayB0b3dhcmRzIDAuDQoNCmBgYHtyfQ0KcGFyKG1hcj1jKDUsNCw2LDMpKQ0KIyBQbG90IGNvZWZmaWNpZW50IHBhdGgNCnBsb3QoZml0X3JpZGdlLCB4dmFyID0gImxhbWJkYSIsIGxhYmVsID0gVFJVRSwNCiAgICAgbHdkID0gMS41LA0KICAgICBtYWluID0gIkNvZWZmaWNpZW50IFBhdGggQW5hbHlzaXM6IHJpZGdlIiwNCiAgICAgY2V4Lm1haW4gPSAwLjksDQogICAgIGNvbCA9IHJhaW5ib3coMTApKQ0KYWJsaW5lKHYgPSAxLCBjb2wgPSAicHVycGxlIiwgbHR5ID0gNCwgbHdkID0gMikNCmFibGluZSh2ID0gLTEsIGNvbCA9ICJzdGVlbGJsdWUiLCBsdHkgPSAyLCBsd2QgPSAyKQ0KYGBgDQoNCkJlbG93IHNob3dzIHRoZSBwbG90IG9mIHRoZSBjb2VmZmljaWVudCBwYXRocyBmb3IgYWxsIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIGZvciBlbGFzdGljIG5ldC4NCg0KYGBge3J9DQpwYXIobWFyPWMoNSw0LDYsMykpDQojIFBsb3QgY29lZmZpY2llbnQgcGF0aA0KcGxvdChmaXRfZWxhc3RpY19uZXQsIHh2YXIgPSAibGFtYmRhIiwgbGFiZWwgPSBUUlVFLA0KICAgICBsd2QgPSAxLjUsDQogICAgIG1haW4gPSAiQ29lZmZpY2llbnQgUGF0aCBBbmFseXNpczogZWxhc3RpYyBuZXQiLA0KICAgICBjZXgubWFpbiA9IDAuOSwNCiAgICAgY29sID0gcmFpbmJvdygxMCkpDQphYmxpbmUodiA9IDEsIGNvbCA9ICJwdXJwbGUiLCBsdHkgPSA0LCBsd2QgPSAyKQ0KYWJsaW5lKHYgPSAtMSwgY29sID0gInN0ZWVsYmx1ZSIsIGx0eSA9IDIsIGx3ZCA9IDIpDQpgYGANCg0KDQojIyBPcHRpbWFsIFBhcmFtZXRlcnMNCg0KQXMgc2hvd24gYmVsb3csIHVzaW5nIHRoZSBjcm9zcyB2YWxpZGF0aW9uIHdlIGRpZCBmb3IgZ2xtbmV0IGFuZCB0aGUgbGFtYmRhLm1pbiAobWluaW11bSBjcm9zcy12YWxpZGF0ZWQgZXJyb3IpIGdlbmVyYXRlZCwgd2UgZ2V0IHRoZSBvcHRpbWFsIHBhcmFtZXRlcnMgZm9yIGxhc3NvLCByaWRnZSwgYW5kIGVsYXN0aWMgbmV0LiBMQVNTTy5vcHQgPSAwLjEzMjgsIFJpZGdlLm9wdD0wLjEzMzQsIGFuZCBFbGFzdGljbmV0Lm9wdCA9IDAuMTMyOQ0KDQpgYGB7cn0NCmJlc3QubGFzc28ubGFtYmRhIDwtIGN2X2xhc3NvJGxhbWJkYS5taW4NCmJlc3QucmlkZ2UubGFtYmRhIDwtIGN2X3JpZGdlJGxhbWJkYS5taW4NCmJlc3QuZWxhc3RpYy5uZXQubGFtYmRhIDwtIGN2X2VsYXN0aWNfbmV0JGxhbWJkYS5taW4NCiMjDQojIExhc3NvIFJlZ3Jlc3Npb24gKEwxIFJlZ3VsYXJpemF0aW9uKTogDQojIENBVVRJT046IG1vZGVsIGZvcm11bGEgZGlmZmVycyBmcm9tIHRoZSByZWd1bGFyIHJlZ3Jlc3Npb24gZm9ybXVsYSANCmxhc3NvX21vZGVsLm9wdCA8LSBnbG1uZXQoWF90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgICAgeV90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAxLCAgICAgICMgbGFzc28gcmVncmVzc2lvbiANCiAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEgPSBiZXN0Lmxhc3NvLmxhbWJkYSkgICAjIHVzZXNlciBzZWxlY3RlZCBhbHBoYSwgb3B0aW1hbCBsYW1iZGENCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjYW4gYmUgb2J0YWluZWQgdGhyb3VnaCBDViAoc2VlIGJlbG93KQ0KbGFzc29fcHJlZGljdGlvbnMub3B0IDwtIHByZWRpY3QobGFzc29fbW9kZWwub3B0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcyA9IGJlc3QubGFzc28ubGFtYmRhLCAjIHVzZXIgc2VsZWN0ZWQgbGFtYmRhIHZhbHVlIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIChyZWd1bGFyaXphdGlvbiBwYXJlbWV0ZXIpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld3ggPSBYX3Rlc3QpICAjIHRlc3QgZGF0YSBzZXQgDQojIFRoZSBmb2xsb3dpbmcgUk1TRSBvZiBwcmVkaWN0aW9uIHNlcnZlcyBhcyBhIHZhbGlkYXRpb24gLSBvbmUgc3RlcCB2YWxpZGF0aW9uDQpsYXNzb19ybXNlLm9wdCA8LSBzcXJ0KG1lYW4oKHlfdGVzdCAtIGxhc3NvX3ByZWRpY3Rpb25zLm9wdCleMikpICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiMgUmlkZ2UgUmVncmVzc2lvbiAoTDIgUmVndWxhcml6YXRpb24pDQpyaWRnZV9tb2RlbC5vcHQgPC0gZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMCwgbGFtYmRhID0gYmVzdC5yaWRnZS5sYW1iZGEpDQpyaWRnZV9wcmVkaWN0aW9ucy5vcHQgPC0gcHJlZGljdChyaWRnZV9tb2RlbC5vcHQsIHMgPSBiZXN0LnJpZGdlLmxhbWJkYSwgbmV3eCA9IFhfdGVzdCkNCnJpZGdlX3Jtc2Uub3B0IDwtIHNxcnQobWVhbigoeV90ZXN0IC0gcmlkZ2VfcHJlZGljdGlvbnMub3B0KV4yKSkNCg0KIyBFbGFzdGljIE5ldCAoQ29tYmluYXRpb24gb2YgTDEgYW5kIEwyKQ0KZWxhc3RpY19uZXRfbW9kZWwub3B0IDwtIGdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDAuNSwgbGFtYmRhID0gYmVzdC5lbGFzdGljLm5ldC5sYW1iZGEpDQplbGFzdGljX25ldF9wcmVkaWN0aW9ucy5vcHQgPC0gcHJlZGljdChlbGFzdGljX25ldF9tb2RlbC5vcHQsIHMgPSAwLjEsIG5ld3ggPSBYX3Rlc3QpDQplbGFzdGljX25ldF9ybXNlLm9wdCA8LSBzcXJ0KG1lYW4oKHlfdGVzdCAtIGVsYXN0aWNfbmV0X3ByZWRpY3Rpb25zLm9wdCleMikpDQoNClJNU0Uub3B0ID0gY2JpbmQoTEFTU08ub3B0ID0gbGFzc29fcm1zZS5vcHQsIA0KICAgICAgICAgICAgICAgICBSaWRnZS5vcHQgPSAgcmlkZ2Vfcm1zZS5vcHQsIA0KICAgICAgICAgICAgICAgICBFbGFzdGljbmV0Lm9wdCA9IGVsYXN0aWNfbmV0X3Jtc2Uub3B0KQ0KcGFuZGVyKFJNU0Uub3B0KQ0KYGBgDQoNCiMjIEZpbmFsIE1vZGVsIEVxdWF0aW9ucw0KDQpGb3IgbGFzc28sIHdlIGdldCB0aGUgZmluYWwgbW9kZWwgZXF1YXRpb24gYmVsb3cgYmFzZWQgb24gdGhlIG9wdGltYWwgcGFyYW1ldGVyIGZyb20gdGhlIHByZXZpb3VzIHNlY3Rpb24uDQoNCmBgYHtyfQ0KIyBsYXNzbw0KDQpiZXN0X2xhbWJkYS5sYXNzbyA8LSBjdl9sYXNzbyRsYW1iZGEubWluDQpjb2VmZmljaWVudHMubGFzc28gPC0gY29lZihjdl9sYXNzbywgcyA9IGJlc3RfbGFtYmRhLmxhc3NvKQ0KDQppbnRlcmNlcHQubGFzc28gPC0gY29lZmZpY2llbnRzLmxhc3NvWzFdDQpiZXRhcy5sYXNzbyA8LSBjb2VmZmljaWVudHMubGFzc29bLTFdDQoNCmNhdCgiTW9kZWwgZXF1YXRpb246IHkgPSIsIHJvdW5kKGludGVyY2VwdC5sYXNzbyw0KSwgIisiLCANCnBhc3RlKHJvdW5kKGJldGFzLmxhc3NvLDQpLCBjb2xuYW1lcyhYKSwgc2VwID0gIioiLCBjb2xsYXBzZSA9ICIgKyAiKSwgIlxuIikNCmBgYA0KDQpGb3IgcmlkZ2UsIHdlIGdldCB0aGUgZmluYWwgbW9kZWwgZXF1YXRpb24gYmVsb3cgYmFzZWQgb24gdGhlIG9wdGltYWwgcGFyYW1ldGVyIGZyb20gdGhlIHByZXZpb3VzIHNlY3Rpb24uDQoNCmBgYHtyfQ0KIyByaWRnZQ0KDQpiZXN0X2xhbWJkYS5yaWRnZSA8LSBjdl9yaWRnZSRsYW1iZGEubWluDQpjb2VmZmljaWVudHMucmlkZ2UgPC0gY29lZihjdl9yaWRnZSwgcyA9IGJlc3RfbGFtYmRhLnJpZGdlKQ0KDQppbnRlcmNlcHQucmlkZ2UgPC0gY29lZmZpY2llbnRzLnJpZGdlWzFdDQpiZXRhcy5yaWRnZSA8LSBjb2VmZmljaWVudHMucmlkZ2VbLTFdDQoNCmNhdCgiTW9kZWwgZXF1YXRpb246IHkgPSIsIHJvdW5kKGludGVyY2VwdC5yaWRnZSw0KSwgIisiLCANCnBhc3RlKHJvdW5kKGJldGFzLnJpZGdlLDQpLCBjb2xuYW1lcyhYKSwgc2VwID0gIioiLCBjb2xsYXBzZSA9ICIgKyAiKSwgIlxuIikNCmBgYA0KDQpGb3IgZWxhc3RpYyBuZXQsIHdlIGdldCB0aGUgZmluYWwgbW9kZWwgZXF1YXRpb24gYmVsb3cgYmFzZWQgb24gdGhlIG9wdGltYWwgcGFyYW1ldGVyIGZyb20gdGhlIHByZXZpb3VzIHNlY3Rpb24uDQoNCmBgYHtyfQ0KIyBlbGFzdGljIG5ldA0KDQpiZXN0X2xhbWJkYS5uZXQgPC0gY3ZfZWxhc3RpY19uZXQkbGFtYmRhLm1pbg0KY29lZmZpY2llbnRzLm5ldCA8LSBjb2VmKGN2X2VsYXN0aWNfbmV0LCBzID0gYmVzdF9sYW1iZGEubmV0KQ0KDQppbnRlcmNlcHQubmV0IDwtIGNvZWZmaWNpZW50cy5uZXRbMV0NCmJldGFzLm5ldDwtIGNvZWZmaWNpZW50cy5uZXRbLTFdDQoNCmNhdCgiTW9kZWwgZXF1YXRpb246IHkgPSIsIHJvdW5kKGludGVyY2VwdC5uZXQsNCksICIrIiwgDQogcGFzdGUocm91bmQoYmV0YXMubmV0LDQpLCBjb2xuYW1lcyhYKSwgc2VwID0gIioiLCBjb2xsYXBzZSA9ICIgKyAiKSwgIlxuIikNCmBgYA0KDQojIFJlZ3VsYXJpemVkIExvZ2lzdGljIFJlZ3Jlc3Npb24NCg0KIyMgQ29lZmZpY2llbnQgUGF0aCBBbmFseXNpcw0KDQpJbiB0aGlzIHNlY3Rpb24sIHdlIGRvIHJlZ3VsYXJpemVkIGxvZ2lzdGljIHJlZ3Jlc3Npb24uIFRoZSBwcm9jZXNzIGlzIHRoZSBzYW1lIGFzIHJlZ3VsYXJpemVkIGxpbmVhci4NCg0KRmlyc3QsIHdlIGNyZWF0ZSBhIGJpbmFyeSByZXNwb25zZSB2YXJpYWJsZSBjYWxsZWQgJ3Byb2R1Y3Rpdml0eScsIHdoZXJlIDEgcmVwcmVzZW50cyBwcm9kdWN0aXZlIGFuZCAwIHJlcHJlc2VudHMgbm90IHByb2R1Y3RpdmUuIFRoaXMgaXMgYmFzZWQgb24gaWYgYWN0dWFsIHByb2R1Y3Rpdml0eSBsZXZlbCBpcyBncmVhdGVyIHRoYW4gdGhlIHRhcmdldGVkIHByb2R1Y3Rpdml0eS4NCg0KVGhlbiB3ZSBzcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluL3Rlc3QsIGdlbmVyYXRlIHRoZSBsYXNzbywgcmlkZ2UsIGFuZCBlbGFzdGljIG5ldCBtb2RlbHMsIGFuZCBnZW5lcmF0ZSBvdXIgb3B0aW1hbCBsYW1iZGEgdmFsdWVzLg0KDQpgYGB7cn0NCg0KDQoNCmNvbXBsZXRlX2dhcm1lbnRfZGF0YTEgPC0gdHJhbnNmb3JtKGNvbXBsZXRlX2dhcm1lbnRfZGF0YTEsIHByb2R1Y3Rpdml0eT1pZmVsc2UoYWN0dWFsX3Byb2R1Y3Rpdml0eT49dGFyZ2V0ZWRfcHJvZHVjdGl2aXR5LCAxLCAwKSkNCg0KDQoNCg0KDQoNCg0Kc2V0LnNlZWQoODApDQoNClgxIDwtIG1vZGVsLm1hdHJpeCh+IGRlcGFydG1lbnQrdGVhbStkYXlNb25kYXkrZGF5U2F0dXJkYXkrIGRheVN1bmRheSsgZGF5VGh1cnNkYXkrIGRheVR1ZXNkYXkrIGRheVdlZG5lc2RheSwgZGF0YSA9IGNvbXBsZXRlX2dhcm1lbnRfZGF0YTEpWywgLTFdDQpYMSA8LSBjYmluZChjb21wbGV0ZV9nYXJtZW50X2RhdGExJHRhcmdldGVkX3Byb2R1Y3Rpdml0eSwgY29tcGxldGVfZ2FybWVudF9kYXRhMSRzbXYsIGNvbXBsZXRlX2dhcm1lbnRfZGF0YTEkd2lwLCBjb21wbGV0ZV9nYXJtZW50X2RhdGExJG92ZXJfdGltZSwgY29tcGxldGVfZ2FybWVudF9kYXRhMSRpbmNlbnRpdmUsIGNvbXBsZXRlX2dhcm1lbnRfZGF0YTEkaWRsZV90aW1lLCBjb21wbGV0ZV9nYXJtZW50X2RhdGExJGlkbGVfbWVuLCBjb21wbGV0ZV9nYXJtZW50X2RhdGExJG5vX29mX3N0eWxlX2NoYW5nZSwgY29tcGxldGVfZ2FybWVudF9kYXRhMSRub19vZl93b3JrZXJzLCBYMSkNCiNYIDwtIGFzLmRhdGEuZnJhbWUoWCkNCg0KY29sbmFtZXMoWDEpIDwtIGMoJ3RhcmdldGVkX3Byb2R1Y3Rpdml0eScsJ3NtdicsICd3aXAnLCANCiAgICAgICAgICAgICAgICAgICAgICAgJ292ZXJfdGltZScsIA0KICAgICAgICAgICAgICAgICAgICAgICAnaW5jZW50aXZlJywgJ2lkbGVfdGltZScgLCAnaWRsZV9tZW4nLA0KICAgICAgICAgICAgICAgICAgICAgICAnbm9fb2Zfc3R5bGVfY2hhbmdlJywgJ25vX29mX3dvcmtlcnMnLCdkZXBhcnRtZW50JywNCiAgICAgICAgICAgICAgICAgICAgICAgJ3RlYW0yJywndGVhbTMnLCd0ZWFtNCcsJ3RlYW01JywndGVhbTYnLCAndGVhbTcnLCd0ZWFtOCcsJ3RlYW05JywndGVhbTEwJywndGVhbTExJywndGVhbTEyJywnZGF5TW9uZGF5JywnZGF5U2F0dXJkYXknLCdkYXlTdW5kYXknLCdkYXlUaHVyc2RheScsJ2RheVR1ZXNkYXknLCdkYXlXZWRuZXNkYXknKQ0KDQojWDEgPC0gYXMubWF0cml4KGNvbXBsZXRlX2dhcm1lbnRfZGF0YTFbLCAtYygxLDIsNCwxNSwxNildKQ0KeTEgPC0gY29tcGxldGVfZ2FybWVudF9kYXRhMSRwcm9kdWN0aXZpdHkNCg0KdHJhaW5faW5kZXgxIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeTEsIHAgPSAwLjgsIGxpc3QgPSBGQUxTRSkNClhfdHJhaW4xIDwtIFgxW3RyYWluX2luZGV4MSwgXQ0KWF90ZXN0MSA8LSBYMVstdHJhaW5faW5kZXgxLCBdDQp5X3RyYWluMSA8LSB5MVt0cmFpbl9pbmRleDFdDQp5X3Rlc3QxIDwtIHkxWy10cmFpbl9pbmRleDFdDQoNCiBwcmVwcm9jZXNzX3BhcmFtczEgPC0gcHJlUHJvY2VzcyhYX3RyYWluMSwgbWV0aG9kID0gYygiY2VudGVyIiwgInNjYWxlIikpDQogWF90cmFpbjEgPC0gcHJlZGljdChwcmVwcm9jZXNzX3BhcmFtczEsIFhfdHJhaW4xKQ0KIFhfdGVzdDEgPC0gcHJlZGljdChwcmVwcm9jZXNzX3BhcmFtczEsIFhfdGVzdDEpDQogDQogDQpsYXNzb19tb2RlbDEgPC0gZ2xtbmV0KFhfdHJhaW4xLCB5X3RyYWluMSwgZmFtaWx5ID0gImJpbm9taWFsIiwgYWxwaGEgPSAxKQ0KDQoNCmN2X2xhc3NvMSA8LSBjdi5nbG1uZXQoWF90cmFpbjEsIHlfdHJhaW4xLCBmYW1pbHkgPSAiYmlub21pYWwiLCBhbHBoYSA9IDEpDQoNCmxhbWJkYV9sYXNzbzEgPC0gY3ZfbGFzc28xJGxhbWJkYS5taW4NCg0KDQpsYXNzb19tb2RlbF9vcHQxIDwtIGdsbW5ldChYX3RyYWluMSwgeV90cmFpbjEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSAiYmlub21pYWwiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAxLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhID0gbGFtYmRhX2xhc3NvMSkNCiANCiANCg0KYGBgDQoNCmBgYHtyfQ0KcmlkZ2VfbW9kZWwxIDwtIGdsbW5ldChYX3RyYWluMSwgeV90cmFpbjEsIGZhbWlseSA9ICJiaW5vbWlhbCIsIGFscGhhID0gMCkNCg0KDQpjdl9yaWRnZTEgPC0gY3YuZ2xtbmV0KFhfdHJhaW4xLCB5X3RyYWluMSwgZmFtaWx5ID0gImJpbm9taWFsIiwgYWxwaGEgPSAwKQ0KDQoNCmxhbWJkYV9yaWRnZTEgPC0gY3ZfcmlkZ2UxJGxhbWJkYS5taW4NCg0KDQpyaWRnZV9tb2RlbF9vcHQxIDwtIGdsbW5ldChYX3RyYWluMSwgeV90cmFpbjEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSAiYmlub21pYWwiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhID0gbGFtYmRhX3JpZGdlMSkNCg0KDQoNCmVsYXN0aWNfbW9kZWwxIDwtIGdsbW5ldChYX3RyYWluMSwgeV90cmFpbjEsIGZhbWlseSA9ICJiaW5vbWlhbCIsIGFscGhhID0gMC41KQ0KDQoNCmN2X2VsYXN0aWMxIDwtIGN2LmdsbW5ldChYX3RyYWluMSwgeV90cmFpbjEsIGZhbWlseSA9ICJiaW5vbWlhbCIsIGFscGhhID0gMC41KQ0KDQoNCmxhbWJkYV9lbGFzdGljMSA8LSBjdl9lbGFzdGljMSRsYW1iZGEubWluDQoNCg0KZWxhc3RpY19tb2RlbF9vcHQxIDwtIGdsbW5ldChYX3RyYWluMSwgeV90cmFpbjEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC41LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEgPSBsYW1iZGFfZWxhc3RpYzEpDQpgYGANCg0KQmVsb3cgc2hvd3MgdGhlIHBsb3Qgb2YgdGhlIGNvZWZmaWNpZW50IHBhdGhzIGZvciBhbGwgdGhlIHByZWRpY3RvciB2YXJpYWJsZXMgZm9yIGxhc3NvLg0KDQpgYGB7cn0NCnBhcihtYXI9Yyg1LDQsNiwzKSkNCiMgUGxvdCBjb2VmZmljaWVudCBwYXRoDQpwbG90KGxhc3NvX21vZGVsMSwgeHZhciA9ICJsYW1iZGEiLCBsYWJlbCA9IFRSVUUsDQogICAgIGx3ZCA9IDEuNSwNCiAgICAgbWFpbiA9ICJDb2VmZmljaWVudCBQYXRoIEFuYWx5c2lzOiBsYXNzbyIsDQogICAgIGNleC5tYWluID0gMC45LA0KICAgICBjb2wgPSByYWluYm93KDEwKSkNCmFibGluZSh2ID0gMSwgY29sID0gInB1cnBsZSIsIGx0eSA9IDQsIGx3ZCA9IDIpDQphYmxpbmUodiA9IC0xLCBjb2wgPSAic3RlZWxibHVlIiwgbHR5ID0gMiwgbHdkID0gMikNCmBgYA0KDQpCZWxvdyBzaG93cyB0aGUgcGxvdCBvZiB0aGUgY29lZmZpY2llbnQgcGF0aHMgZm9yIGFsbCB0aGUgcHJlZGljdG9yIHZhcmlhYmxlcyBmb3IgcmlkZ2UuDQoNCmBgYHtyfQ0KcGFyKG1hcj1jKDUsNCw2LDMpKQ0KIyBQbG90IGNvZWZmaWNpZW50IHBhdGgNCnBsb3QocmlkZ2VfbW9kZWwxLCB4dmFyID0gImxhbWJkYSIsIGxhYmVsID0gVFJVRSwNCiAgICAgbHdkID0gMS41LA0KICAgICBtYWluID0gIkNvZWZmaWNpZW50IFBhdGggQW5hbHlzaXM6IHJpZGdlIiwNCiAgICAgY2V4Lm1haW4gPSAwLjksDQogICAgIGNvbCA9IHJhaW5ib3coMTApKQ0KYWJsaW5lKHYgPSAxLCBjb2wgPSAicHVycGxlIiwgbHR5ID0gNCwgbHdkID0gMikNCmFibGluZSh2ID0gLTEsIGNvbCA9ICJzdGVlbGJsdWUiLCBsdHkgPSAyLCBsd2QgPSAyKQ0KYGBgDQoNCkJlbG93IHNob3dzIHRoZSBwbG90IG9mIHRoZSBjb2VmZmljaWVudCBwYXRocyBmb3IgYWxsIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIGZvciBlbGFzdGljIG5ldC4NCg0KYGBge3J9DQpwYXIobWFyPWMoNSw0LDYsMykpDQojIFBsb3QgY29lZmZpY2llbnQgcGF0aA0KcGxvdChlbGFzdGljX21vZGVsMSwgeHZhciA9ICJsYW1iZGEiLCBsYWJlbCA9IFRSVUUsDQogICAgIGx3ZCA9IDEuNSwNCiAgICAgbWFpbiA9ICJDb2VmZmljaWVudCBQYXRoIEFuYWx5c2lzOiBlbGFzdGljIG5ldCIsDQogICAgIGNleC5tYWluID0gMC45LA0KICAgICBjb2wgPSByYWluYm93KDEwKSkNCmFibGluZSh2ID0gMSwgY29sID0gInB1cnBsZSIsIGx0eSA9IDQsIGx3ZCA9IDIpDQphYmxpbmUodiA9IC0xLCBjb2wgPSAic3RlZWxibHVlIiwgbHR5ID0gMiwgbHdkID0gMikNCmBgYA0KDQojIyBSZWd1bGFyaXphdGlvbiBQYXJhbWV0ZXJzDQoNCkJlbG93LCB3ZSBzaG93IHRoZSBjb2VmZmljaWVudHMgZm9yIGVhY2ggcHJlZGljdG9yIHZhcmlhYmxlIGJhc2VkIG9uIHRoZSBvcHRpbWFsIGxhbWJkYSB2YWx1ZXMgKGZvciBsYXNzby9yaWRnZS9lbGFzdGljKS4NCg0KYGBge3J9DQpsYXNzby5jb2VmMSA8LSBhcy5tYXRyaXgoY29lZihsYXNzb19tb2RlbF9vcHQxKSkNCnJpZGdlLmNvZWYxIDwtIGFzLm1hdHJpeChjb2VmKHJpZGdlX21vZGVsX29wdDEpKQ0KZWxhc3RpYy5jb2VmMSA8LSBhcy5tYXRyaXgoY29lZihlbGFzdGljX21vZGVsX29wdDEpKQ0KcmVndWxhcml6ZWQuY29lZjEgPC0gZGF0YS5mcmFtZShsYXNzbyA9IGxhc3NvLmNvZWYxWywxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByaWRnZSA9IHJpZGdlLmNvZWYxWywxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZWxhc3RpY25ldCA9IGVsYXN0aWMuY29lZjFbLDFdKQ0KcGFuZGVyKHJlZ3VsYXJpemVkLmNvZWYxKQ0KYGBgDQoNCiMjIE9wdGltYWwgQ3V0b2ZmIGFuZCBST0MvQVVDDQoNClRoZSBiZWxvdyBmaWd1cmUgc2hvd3MgdGhlIG9wdGltYWwgY3V0LW9mZiBwcm9iYWJpbGl0eSBmb3IgdGhlIGxhc3NvL3JpZGdlL2VsYXN0aWMgbmV0IG1vZGVscy4gVGhpcyB3aWxsIGFsbG93IHVzIHRvIHVzZSB0aGUgZml0dGVkIG1vZGVsIGZvciBwcmVkaWN0aW5nIHRoZSByZXNwb25zZSB2YXJpYWJsZSAocHJvZHVjdGl2aXR5KS4gQWxsIHRoZSBvcHRpbWFsIGN1dC1vZmYgcHJvYmFiaWxpdGllcyBhcmUgYXBwcm94IDAuNTUgDQoNCmBgYHtyfQ0KcHJlZGljdF9sYXNzbyA8LSBwcmVkaWN0KGxhc3NvX21vZGVsX29wdDEsIG5ld3ggPSBYX3Rlc3QxLCB0eXBlID0gInJlc3BvbnNlIikNCnByZWRpY3RfcmlkZ2UgPC0gcHJlZGljdChyaWRnZV9tb2RlbF9vcHQxLCBuZXd4ID0gWF90ZXN0MSwgdHlwZSA9ICJyZXNwb25zZSIpDQpwcmVkaWN0X2VsYXN0aWMgPC0gcHJlZGljdChlbGFzdGljX21vZGVsX29wdDEsIG5ld3ggPSBYX3Rlc3QxLCB0eXBlID0gInJlc3BvbnNlIikNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMgT3B0aW1hbCBjdXRvZmYgcHJvYmFiaWxpdHkgZGV0ZXJtaW5hdGlvbg0Kc2VxLmN1dCA8LSBzZXEoMCwxLCBsZW5ndGg9NTApDQojIHkgaXMgYSB2ZWN0b3Igb2YgMCBhbmQgMQ0KYWNjLmxhc3NvIDwtIE5VTEwNCmFjYy5yaWRnZSA8LSBOVUxMDQphY2MuZWxhc3RpYyA8LSBOVUxMDQpmb3IgKGkgaW4gMTpsZW5ndGgoc2VxLmN1dCkpew0KICAgcHJlZHkubGFzc28gPC0gaWZlbHNlKHByZWRpY3RfbGFzc28gPnNlcS5jdXRbaV0sIDEsIDApDQogICBwcmVkeS5yaWRnZTwtIGlmZWxzZShwcmVkaWN0X3JpZGdlID5zZXEuY3V0W2ldLCAxLCAwKQ0KICAgcHJlZHkuZWxhc3RpYzwtIGlmZWxzZShwcmVkaWN0X2VsYXN0aWMgPnNlcS5jdXRbaV0sIDEsIDApDQogICAjIw0KICAgYWNjLmxhc3NvW2ldIDwtIG1lYW4oeV90ZXN0MSAgPT0gcHJlZHkubGFzc28pDQogICBhY2MucmlkZ2VbaV0gPC0gbWVhbih5X3Rlc3QxID09IHByZWR5LnJpZGdlKQ0KICAgYWNjLmVsYXN0aWNbaV0gPC0gbWVhbih5X3Rlc3QxICA9PSBwcmVkeS5lbGFzdGljKQ0KfQ0KIyMgb3B0aW1hbCBjdXQtb2ZmOiBpZiB0aGUgbWF4aW11bSBhY2N1cmFjeSBvY2N1cnMgYXQgbXVsdGlwbGUNCiMjIGN1dC1vZmYgcHJvYmFiaWxpdGllcywgdGhlIGF2ZXJhZ2Ugb2YgdGhlc2UgY3V0b2ZmIHByb2JhYmlsaXRpZXMNCiMjIHdpbGwgYmUgZGVmaW5lZCBhcyB0aGUgb3B0aW1hbCBjdXRvZmYgcHJvYmFiaWxpdHkNCm9wdC5jdXQubGFzc28gPC0gbWVhbihzZXEuY3V0W3doaWNoKGFjYy5sYXNzbz09bWF4KGFjYy5sYXNzbykpXSkNCm9wdC5jdXQucmlkZ2U8LSBtZWFuKHNlcS5jdXRbd2hpY2goYWNjLnJpZGdlPT1tYXgoYWNjLnJpZGdlKSldKQ0Kb3B0LmN1dC5lbGFzdGljIDwtIG1lYW4oc2VxLmN1dFt3aGljaChhY2MuZWxhc3RpYz09bWF4KGFjYy5lbGFzdGljKSldKQ0KIyMNCmFjYy5kYXRhIDwtIGRhdGEuZnJhbWUocHJvYiA9IHJlcChzZXEuY3V0LDMpLCANCiAgICAgICAgICAgICAgICAgICAgICAgYWNjPWMoYWNjLmxhc3NvLCBhY2MucmlkZ2UsIGFjYy5lbGFzdGljKSwgDQogICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gYyhyZXAoImxhc3NvIiw1MCksIHJlcCgicmlkZ2UiLDUwKSwgcmVwKCJlbGFzdGljIiw1MCkpKQ0KYGBgDQoNCmBgYHtyfQ0KIyMNCmdnLmFjYyA8LSBnZ3Bsb3QoZGF0YSA9IGFjYy5kYXRhLCBhZXMoeD1wcm9iLCB5ID0gYWNjLCBjb2xvciA9IGdyb3VwKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDAuNiwgeSA9IDAuNDUsIA0KICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJMQVNTTyBjdXRvZmY6ICIsIHJvdW5kKG9wdC5jdXQubGFzc28sNSksICJBY2N1cmFjeTogIiwgcm91bmQobWF4KGFjYy5sYXNzbyksNSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICJcblJpZGdlIGN1dG9mZjogIiwgcm91bmQob3B0LmN1dC5yaWRnZSw1KSwgIkFjY3VyYWN5OiAiLCByb3VuZChtYXgoYWNjLnJpZGdlKSw1KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIlxuRWxhc3RpYyBjdXRvZmY6ICIsIHJvdW5kKG9wdC5jdXQuZWxhc3RpYyw1KSwgIkFjY3VyYWN5OiAiLCByb3VuZChtYXgoYWNjLmVsYXN0aWMpLDUpKSwgDQogICAgICAgICAgIHNpemUgPSAzLCANCiAgICAgICAgICAgY29sb3IgPSAibmF2eSIpICsNCiAgZ2d0aXRsZSgiQ3V0LW9mZiBQcm9iYWJpbGl0eSB2cyBBY2N1cmFjeSIpICsNCiAgbGFicyh4ID0gImN1dC1vZmYgUHJvYmFiaWxpdHkiLCANCiAgICAgICB5ID0gImFjY3VyYWN5IiwgY29sb3IgPSAiR3JvdXAiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KDQojIw0KZ2dwbG90bHkoZ2cuYWNjKQ0KYGBgDQoNCldlIGNhbiBjb21wYXJlIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgbGFzc28sIFJpZGdlLCBhbmQgRWxhc3RpYyBOZXQgbW9kZWxzIG9uIG91ciB0ZXN0IGRhdGEgdXNpbmcgUk9DIGN1cnZlcy4gQWxsIG1vZGVscyBoYXZlIHNpbWlsYXIgcGVyZm9ybWFuY2VzIHdpdGggQVVDIG9mIGFwcHJveCAwLjc1Lg0KDQpgYGB7cn0NCnByb2JfbGFzc28gPC0gcHJlZGljdChsYXNzb19tb2RlbF9vcHQxLCBuZXd4ID0gWF90ZXN0MSwgdHlwZSA9ICJyZXNwb25zZSIpDQpwcm9iX3JpZGdlIDwtIHByZWRpY3QocmlkZ2VfbW9kZWxfb3B0MSwgbmV3eCA9IFhfdGVzdDEsIHR5cGUgPSAicmVzcG9uc2UiKQ0KcHJvYl9lbGFzdGljIDwtIHByZWRpY3QoZWxhc3RpY19tb2RlbF9vcHQxLCBuZXd4ID0gWF90ZXN0MSwgdHlwZSA9ICJyZXNwb25zZSIpDQoNCiMgQ29tcHV0ZSBST0MgY3VydmVzOiByb2Mgb2JqZWN0IGNvbnRhaW5zIGEgbG90IGluZm9ybWF0aW9uIGluY2x1ZGluZw0KIyBzZW5zaXRpdml0eSwgc3BlY2lmaWNpdHksIEFVQywgZXRjLg0Kcm9jX2xhc3NvIDwtIHJvYyh5X3Rlc3QxLCBwcm9iX2xhc3NvKQ0Kcm9jX3JpZGdlIDwtIHJvYyh5X3Rlc3QxLCBwcm9iX3JpZGdlKQ0Kcm9jX2VsYXN0aWMgPC0gcm9jKHlfdGVzdDEsIHByb2JfZWxhc3RpYykNCg0KIyBDb21wdXRlIEFVQyB2YWx1ZXMNCmF1Y19sYXNzbyA8LSBhdWMocm9jX2xhc3NvKQ0KYXVjX3JpZGdlIDwtIGF1Yyhyb2NfcmlkZ2UpDQphdWNfZWxhc3RpYyA8LSBhdWMocm9jX2VsYXN0aWMpDQoNCiMjIExBU1NPDQpzZW4ubGFzc28gPC0gcm9jX2xhc3NvJHNlbnNpdGl2aXRpZXMNCnNwZS5sYXNzbyA8LSByb2NfbGFzc28kc3BlY2lmaWNpdGllcw0KYXVjLmxhc3NvIDwtIHJvY19sYXNzbyRhdWMNCg0KIyMgUmlkZ2UNCnNlbi5yaWRnZSA8LSByb2NfcmlkZ2Ukc2Vuc2l0aXZpdGllcw0Kc3BlLnJpZGdlIDwtIHJvY19yaWRnZSRzcGVjaWZpY2l0aWVzDQphdWMucmlkZ2UgPC0gcm9jX3JpZGdlJGF1Yw0KDQojIyBFbGFzdGljIE5ldA0Kc2VuLmVsYXN0aWMgPC0gcm9jX2VsYXN0aWMkc2Vuc2l0aXZpdGllcw0Kc3BlLmVsYXN0aWMgPC0gcm9jX2VsYXN0aWMkc3BlY2lmaWNpdGllcw0KYXVjLmVsYXN0aWMgPC0gcm9jX2VsYXN0aWMkYXVjDQoNCiMjIFBsb3R0aW5nIHRoZSBST0MgY3VydmVzOiB0aHJlZSBjb2xvcnMgLSBncmVlbiwgb3JhbmdlLCBhbmQgcHVycGxlDQoNCnBsb3QoMS1zcGUubGFzc28sIHNlbi5sYXNzbywgDQogICAgIHR5cGUgPSAibCIsDQogICAgIGNvbCA9ICJncmVlbiIsIA0KICAgICB4bGltPWMoMCwxKSwNCiAgICAgeGxhYiA9ICIxIC0gc3BlY2lmaWNpdHkiLA0KICAgICB5bGFiID0gInNlbnNpdGl2aXR5IiwNCiAgICAgbWFpbiA9ICJST0MgQ3VydmVzIGZvciBMQVNTTywgUmlkZ2UsIGFuZCBFbGFzdGljIE5ldCIpDQpsaW5lcygxLXNwZS5yaWRnZSwgc2VuLnJpZGdlLCBjb2wgPSAib3JhbmdlIikNCmxpbmVzKDEtc3BlLmVsYXN0aWMsIHNlbi5lbGFzdGljLCBjb2wgPSAicHVycGxlIikNCiNhYmxpbmUoMCwxLCB0eXBlID0gImwiLCBsdHkgPSAyLCBjb2wgPSAic3RlZWxibHVlIiwgbHdkID0gMSkNCmFibGluZSgwLDEsICwgbHR5ID0gMiwgY29sID0gInN0ZWVsYmx1ZSIsIGx3ZCA9IDEpDQojIEFkZCBsZWdlbmQNCmxlZ2VuZCgiYm90dG9tcmlnaHQiLCBsZWdlbmQgPSBjKHBhc3RlKCJMQVNTTyAoQVVDID0iLCByb3VuZChhdWNfbGFzc28sIDMpLCAiKSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgiUmlkZ2UgKEFVQyA9Iiwgcm91bmQoYXVjX3JpZGdlLCAzKSwgIikiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoIkVsYXN0aWMgTmV0IChBVUMgPSIsIHJvdW5kKGF1Y19lbGFzdGljLCAzKSwgIikiKSksDQogICAgICAgY29sID0gYygiZ3JlZW4iLCAib3JhbmdlIiwgInB1cnBsZSIpLCBsdHkgPSAxLCBjZXggPSAwLjgsIGJ0eSA9ICJuIikNCmBgYA0KDQojIFNWTQ0KDQpUaGlzIHNlY3Rpb24gdXNlcyBTdXBwb3J0IFZlY3RvciBNYWNoaW5lcy4gU2luY2Ugb3VyIHRhcmdldCB2YXJpYWJsZSAocHJvZHVjdGl2aXR5KSBoYXMgdHdvIGNsYXNzZXMsIFNWTSB3aWxsIGZpbmQgYSBoeXBlcnBsYW5lIHRoYXQgbWF4aW1pemVzIHRoZSBtYXJnaW4gYmV0d2VlbiB0aGUgdHdvIGNsYXNzZXMuDQoNClRoZSBiZWxvdyBjb2RlIGNodW5rcyB3aWxsIHBlcmZvcm0gU1ZNIGJ5IHVzaW5nIDUtZm9sZCBjcm9zcyB2YWxpZGF0aW9uIHRvIHNlYXJjaCBmb3IgdGhlIG9wdGltYWwgaHlwZXJwYXJhbWV0ZXJzIGFuZCB1c2luZyB0aG9zZSB0byB0cmFpbiB0aGUgZmluYWwgbW9kZWwuIFdlIHdpbGwgdXNlIGxpbmVhciBhbmQgUkJGIGtlcm5lbHMuDQoNCmBgYHtyfQ0KY29tcGxldGVfZ2FybWVudF9kYXRhMSRwcm9kdWN0aXZpdHkgPC0gYXMuZmFjdG9yKGNvbXBsZXRlX2dhcm1lbnRfZGF0YTEkcHJvZHVjdGl2aXR5KQ0KDQpzZXQuc2VlZCgxMjMpICAgIA0KaW5kZXggPC0gc2FtcGxlKDE6bnJvdyhjb21wbGV0ZV9nYXJtZW50X2RhdGExKSwgMC44ICogbnJvdyhjb21wbGV0ZV9nYXJtZW50X2RhdGExKSkNCnRyYWluLmRhdGEgPC0gY29tcGxldGVfZ2FybWVudF9kYXRhMVtpbmRleCwgXQ0KdGVzdC5kYXRhIDwtIGNvbXBsZXRlX2dhcm1lbnRfZGF0YTFbLWluZGV4LCBdDQoNCnR1bmVfY29udHJvbCA8LSB0dW5lLmNvbnRyb2woDQogIGNyb3NzID0gNSwgIA0KICBucmVwZWF0ID0gMSANCikNCg0KdHVuZS5SQkYgPC0gdHVuZSgNCiAgc3ZtLCAgICAgICAgICANCiAgcHJvZHVjdGl2aXR5IH4gZGVwYXJ0bWVudCArIHRlYW0gKyB0YXJnZXRlZF9wcm9kdWN0aXZpdHkgKyBzbXYgKyB3aXAgKyBvdmVyX3RpbWUgKyBpbmNlbnRpdmUgKyBpZGxlX3RpbWUgKyBpZGxlX21lbiArDQogICAgbm9fb2Zfc3R5bGVfY2hhbmdlICsgbm9fb2Zfd29ya2VycyArIGRheU1vbmRheSArIGRheVNhdHVyZGF5KyBkYXlTdW5kYXkgKyBkYXlUaHVyc2RheSArIGRheVR1ZXNkYXkgKyBkYXlXZWRuZXNkYXksIA0KICBkYXRhID0gdHJhaW4uZGF0YSwNCiAga2VybmVsID0gInJhZGlhbCIsICAgIA0KICByYW5nZXMgPSBsaXN0KA0KICAgIGNvc3QgPSAxMF4oLTE6MiksICAgDQogICAgZ2FtbWEgPSBjKDAuMSwgMC41LCAxLCAyKSANCiAgKSwNCiAgdHVuZWNvbnRyb2wgPSB0dW5lX2NvbnRyb2wgIA0KKQ0KDQoNCmJlc3QuUkJGIDwtIHR1bmUuUkJGJGJlc3QubW9kZWwNCmJlc3QuY29zdC5SQkYgPC0gYmVzdC5SQkYkY29zdA0KYmVzdC5nYW1tYS5SQkYgPC0gYmVzdC5SQkYkZ2FtbWENCg0KDQpmaW5hbC5SQkYgPC0gc3ZtKA0KICBwcm9kdWN0aXZpdHkgfiBkZXBhcnRtZW50ICsgdGVhbSArIHRhcmdldGVkX3Byb2R1Y3Rpdml0eSArIHNtdiArIHdpcCArIG92ZXJfdGltZSArIGluY2VudGl2ZSArIGlkbGVfdGltZSArIGlkbGVfbWVuICsNCiAgICBub19vZl9zdHlsZV9jaGFuZ2UgKyBub19vZl93b3JrZXJzICsgZGF5TW9uZGF5ICsgZGF5U2F0dXJkYXkrIGRheVN1bmRheSArIGRheVRodXJzZGF5ICsgZGF5VHVlc2RheSArIGRheVdlZG5lc2RheSwNCiAgZGF0YSA9IHRyYWluLmRhdGEsDQogIGtlcm5lbCA9ICJyYWRpYWwiLA0KICBjb3N0ID0gYmVzdC5jb3N0LlJCRiwNCiAgZ2FtbWEgPSBiZXN0LmdhbW1hLlJCRiwNCiAgcHJvYmFiaWxpdHkgPSBUUlVFDQopDQoNCg0KIyMjIyMjIyMjIyMjIyMjIyMjDQoNCiMgdHVuZS5jb250cm9sIDwtIHR1bmUuY29udHJvbCgNCiMgICBjcm9zcyA9IDUsICAjIFVzZSA1LWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiwgdGhlIGRlZmF1bHQgaXMgMTAtZm9sZCBjcm9zcy12YWxpZGF0aW9uDQojICAgbnJlcGVhdCA9IDEgIyBOdW1iZXIgb2YgcmVwZXRpdGlvbnMgKGZvciByZXBlYXRlZCBjcm9zcy12YWxpZGF0aW9uKQ0KIyApDQoNCnR1bmUubGluIDwtIHR1bmUoDQogIHN2bSwgICAgICAgICAgDQogIHByb2R1Y3Rpdml0eSB+IGRlcGFydG1lbnQgKyB0ZWFtICsgdGFyZ2V0ZWRfcHJvZHVjdGl2aXR5ICsgc212ICsgd2lwICsgb3Zlcl90aW1lICsgaW5jZW50aXZlICsgaWRsZV90aW1lICsgaWRsZV9tZW4gKw0KICAgIG5vX29mX3N0eWxlX2NoYW5nZSArIG5vX29mX3dvcmtlcnMgKyBkYXlNb25kYXkgKyBkYXlTYXR1cmRheSsgZGF5U3VuZGF5ICsgZGF5VGh1cnNkYXkgKyBkYXlUdWVzZGF5ICsgZGF5V2VkbmVzZGF5LCANCiAgZGF0YSA9IHRyYWluLmRhdGEsDQogIGtlcm5lbCA9ICJsaW5lYXIiLCAgICANCiAgcmFuZ2VzID0gbGlzdCgNCiAgICBjb3N0ID0gMTBeKC0xOjIpICAgDQogICksDQogIHR1bmVjb250cm9sID0gdHVuZV9jb250cm9sICANCikNCg0KYmVzdC5saW4gPC0gdHVuZS5saW4kYmVzdC5tb2RlbA0KYmVzdC5jb3N0LmxpbiA8LSBiZXN0LmxpbiRjb3N0DQoNCmZpbmFsLmxpbiA8LSBzdm0oDQogIHByb2R1Y3Rpdml0eSB+IGRlcGFydG1lbnQgKyB0ZWFtICsgdGFyZ2V0ZWRfcHJvZHVjdGl2aXR5ICsgc212ICsgd2lwICsgb3Zlcl90aW1lICsgaW5jZW50aXZlICsgaWRsZV90aW1lICsgaWRsZV9tZW4gKw0KICAgIG5vX29mX3N0eWxlX2NoYW5nZSArIG5vX29mX3dvcmtlcnMgKyBkYXlNb25kYXkgKyBkYXlTYXR1cmRheSsgZGF5U3VuZGF5ICsgZGF5VGh1cnNkYXkgKyBkYXlUdWVzZGF5ICsgZGF5V2VkbmVzZGF5LA0KICBkYXRhID0gdHJhaW4uZGF0YSwNCiAga2VybmVsID0gImxpbmVhciIsDQogIGNvc3QgPSBiZXN0LmNvc3QubGluLA0KICBwcm9iYWJpbGl0eSA9IFRSVUUNCikNCmBgYA0KDQpgYGB7cn0NCmxvZ2l0LmZpdCA8LSBnbG0ocHJvZHVjdGl2aXR5IH4gZGVwYXJ0bWVudCArIHRlYW0gKyB0YXJnZXRlZF9wcm9kdWN0aXZpdHkgKyBzbXYgKyB3aXAgKyBvdmVyX3RpbWUgKyBpbmNlbnRpdmUgKyBpZGxlX3RpbWUgKyBpZGxlX21lbiArIG5vX29mX3N0eWxlX2NoYW5nZSArIG5vX29mX3dvcmtlcnMrIGRheU1vbmRheSArIGRheVNhdHVyZGF5KyBkYXlTdW5kYXkgKyBkYXlUaHVyc2RheSArIGRheVR1ZXNkYXkgKyBkYXlXZWRuZXNkYXksIGRhdGEgPSB0cmFpbi5kYXRhLCBmYW1pbHkgPSBiaW5vbWlhbCkNCkFJQy5sb2dpdCA8LSBzdGVwKGxvZ2l0LmZpdCwgZGlyZWN0aW9uID0gImJvdGgiLCB0cmFjZSA9IDApDQpwcmVkLmxvZ2l0IDwtIHByZWRpY3QoQUlDLmxvZ2l0LCB0ZXN0LmRhdGEsIHR5cGUgPSAicmVzcG9uc2UiKQ0KDQoNCnByZWQucHJvYi5saW4gPC0gcHJlZGljdChmaW5hbC5saW4sIHRlc3QuZGF0YSwgcHJvYmFiaWxpdHkgPSBUUlVFKQ0KcHJlZC5wcm9iLlJCRiA8LSBwcmVkaWN0KGZpbmFsLlJCRiwgdGVzdC5kYXRhLCBwcm9iYWJpbGl0eSA9IFRSVUUpDQoNCiMjIyMjDQpwcm9iLmxpbmVhciA8LSBhdHRyKHByZWQucHJvYi5saW4sICJwcm9iYWJpbGl0aWVzIilbLCAyXQ0KcHJvYi5yYWRpYWwgPC0gYXR0cihwcmVkLnByb2IuUkJGLCAicHJvYmFiaWxpdGllcyIpWywgMl0NCiMjIyMjDQoNCnJvY19saW4gPC0gcm9jKHRlc3QuZGF0YSRwcm9kdWN0aXZpdHksIHByb2IubGluZWFyKQ0Kcm9jX1JCRiA8LSByb2ModGVzdC5kYXRhJHByb2R1Y3Rpdml0eSwgcHJvYi5yYWRpYWwpDQpyb2NfbG9naXQgPC0gcm9jKHRlc3QuZGF0YSRwcm9kdWN0aXZpdHksIHByZWQubG9naXQpDQoNCmxpbi5zZW4gPC0gcm9jX2xpbiRzZW5zaXRpdml0aWVzDQpsaW4uc3BlIDwtIHJvY19saW4kc3BlY2lmaWNpdGllcw0KcmFkLnNlbiA8LSByb2NfUkJGJHNlbnNpdGl2aXRpZXMNCnJhZC5zcGUgPC0gcm9jX1JCRiRzcGVjaWZpY2l0aWVzDQpsb2dpdC5zZW4gPC0gcm9jX2xvZ2l0JHNlbnNpdGl2aXRpZXMNCmxvZ2l0LnNwZSA8LSByb2NfbG9naXQkc3BlY2lmaWNpdGllcw0KYXVjLmxpbiA8LSByb2NfbGluJGF1Yw0KYXVjLnJhZCA8LSByb2NfUkJGJGF1Yw0KYXVjLmxvZ2l0IDwtIHJvY19sb2dpdCRhdWMNCmBgYA0KDQpCZWxvdyB3ZSBwbG90IHRoZSBST0MgZm9yIGxpbmVhciwgUkJGLCBhbmQgc3RhbmRhcmQgbG9naXN0aWMgcmVncmVzc2lvbi4gQmFzZWQgb24gdGhlIEFVQywgdGhlIFNWTSBsaW5lYXIsIFNWTSBSQkYsIGFuZCBzdGFuZGFyZCBsb2dpc3RpYyBhcmUgc2ltaWxhciB3aXRoIGFyZWEgb2YgYXBwcm94IDAuOC4NCg0KYGBge3J9DQpwbG90KDEtbGluLnNwZSwgbGluLnNlbiwgIA0KICAgICB4bGFiID0gIjEgLSBzcGVjaWZpY2l0eSIsDQogICAgIHlsYWIgPSAic2Vuc2l0aXZpdHkiLA0KICAgICBjb2wgPSAiZGFya3JlZCIsDQogICAgIHR5cGUgPSAibCIsDQogICAgIGx0eSA9IDEsDQogICAgIGx3ZCA9IDEsDQogICAgIG1haW4gPSAiUk9DIEN1cnZlcyBvZiBTVk0iKQ0KbGluZXMoMS1yYWQuc3BlLCByYWQuc2VuLCANCiAgICAgIGNvbCA9ICJibHVlIiwNCiAgICAgIGx0eSA9IDEsDQogICAgICBsd2QgPSAxKQ0KbGluZXMoMS1sb2dpdC5zcGUsIGxvZ2l0LnNlbiwgICAgICANCiAgICAgIGNvbCA9ICJvcmFuZ2UiLA0KICAgICAgbHR5ID0gMSwNCiAgICAgIGx3ZCA9IDEpDQphYmxpbmUoMCwxLCBjb2wgPSAic2t5Ymx1ZTMiLCBsdHkgPSAyLCBsd2QgPSAyKQ0KYWJsaW5lKHY9YygwLjA0OSwwLjE1MSksIGx0eSA9IDMsIGNvbCA9ICJkYXJrZ3JlZW4iKQ0KbGVnZW5kKCJib3R0b21yaWdodCIsIGMoIkxpbmVhciBLZXJuZWwiLCAiUmFkaWFsIEtlcm5lbCIsICJMb2dpc3RpYyBSZWdyZXNzaW9uIiksDQogICAgICAgbHR5ID0gYygxLDEsMSksIGx3ZCA9IHJlcCgxLDMpLA0KICAgICAgIGNvbCA9IGMoInJlZCIsICJibHVlIiwgIm9yYW5nZSIpLA0KICAgICAgIGJ0eT0ibiIsY2V4ID0gMC44KQ0KDQp0ZXh0KDAuOCwgMC40NiwgcGFzdGUoIkxpbmVhciBBVUM6ICIsIHJvdW5kKGF1Yy5saW4sNCkpLCBjZXggPSAwLjgpDQp0ZXh0KDAuOCwgMC40LCBwYXN0ZSgiUmFkaWFsIEFVQzogIiwgcm91bmQoYXVjLnJhZCw0KSksIGNleCA9IDAuOCkNCnRleHQoMC44LCAwLjM0LCBwYXN0ZSgiTG9naXN0aWMgQVVDOiAiLCByb3VuZChhdWMubG9naXQsNCkpLCBjZXggPSAwLjgpDQpgYGANCg0KDQojIFNWUg0KDQpJbiB0aGlzIHNlY3Rpb24sIHdlIHdpbGwgZG8gU1ZSLiBTaW5jZSB3ZSBoYXZlIGEgY29udGludW91cyB0YXJnZXQgdmFyaWFibGUsIHdlIGNhbm5vdCB1c2UgYSBoeXBlcnBsYW5lIHRvIHNlcGFyYXRlIGNsYXNzZXMuIEhlcmUgd2UgdHJ5IHRvIGZpdCB0aGUgcHJlZGljdGVkIGRhdGEgd2l0aGluIGEgc3BlY2lmaWVkIG1hcmdpbiBvZiBlcnJvci4gQmVsb3cgd2UgdHJhaW4gdGhlIFNWUiBtb2RlbC4gV2UgdXNlIFJCRi9saW5lYXIga2VybmVscyBhbmQgc3RhbmRhcmQgbGluZWFyIHJlZ3Jlc3Npb24uDQoNCmBgYHtyfQ0KDQpYMiA8LSBtb2RlbC5tYXRyaXgofiBkZXBhcnRtZW50K3RlYW0rIGRheU1vbmRheSArIGRheVNhdHVyZGF5KyBkYXlTdW5kYXkgKyBkYXlUaHVyc2RheSArIGRheVR1ZXNkYXkgKyBkYXlXZWRuZXNkYXksIGRhdGEgPSBjb21wbGV0ZV9nYXJtZW50X2RhdGExKVssIC0xXQ0KWDIgPC0gY2JpbmQoY29tcGxldGVfZ2FybWVudF9kYXRhMSR0YXJnZXRlZF9wcm9kdWN0aXZpdHksIGNvbXBsZXRlX2dhcm1lbnRfZGF0YTEkc212LCBjb21wbGV0ZV9nYXJtZW50X2RhdGExJHdpcCwgY29tcGxldGVfZ2FybWVudF9kYXRhMSRvdmVyX3RpbWUsIGNvbXBsZXRlX2dhcm1lbnRfZGF0YTEkaW5jZW50aXZlLCBjb21wbGV0ZV9nYXJtZW50X2RhdGExJGlkbGVfdGltZSwgY29tcGxldGVfZ2FybWVudF9kYXRhMSRpZGxlX21lbiwgY29tcGxldGVfZ2FybWVudF9kYXRhMSRub19vZl9zdHlsZV9jaGFuZ2UsIGNvbXBsZXRlX2dhcm1lbnRfZGF0YTEkbm9fb2Zfd29ya2VycywgWDIpDQojWCA8LSBhcy5kYXRhLmZyYW1lKFgpDQoNCmNvbG5hbWVzKFgyKSA8LSBjKCd0YXJnZXRlZF9wcm9kdWN0aXZpdHknLCdzbXYnLCAnd2lwJywgDQogICAgICAgICAgICAgICAgICAgICAgICdvdmVyX3RpbWUnLCANCiAgICAgICAgICAgICAgICAgICAgICAgJ2luY2VudGl2ZScsICdpZGxlX3RpbWUnICwgJ2lkbGVfbWVuJywNCiAgICAgICAgICAgICAgICAgICAgICAgJ25vX29mX3N0eWxlX2NoYW5nZScsICdub19vZl93b3JrZXJzJywnZGVwYXJ0bWVudCcsDQogICAgICAgICAgICAgICAgICAgICAgICd0ZWFtMicsJ3RlYW0zJywndGVhbTQnLCd0ZWFtNScsJ3RlYW02JywgJ3RlYW03JywndGVhbTgnLCd0ZWFtOScsJ3RlYW0xMCcsJ3RlYW0xMScsJ3RlYW0xMicsJ2RheU1vbmRheScsJ2RheVNhdHVyZGF5JywnZGF5U3VuZGF5JywnZGF5VGh1cnNkYXknLCdkYXlUdWVzZGF5JywnZGF5V2VkbmVzZGF5JykNCg0KIFgyIDwtIGFzLmRhdGEuZnJhbWUoWDIpDQojIA0KIyAgWDIgPC0gY2JpbmQoY29tcGxldGVfZ2FybWVudF9kYXRhMVssIDE1XSAsWDIpDQojICMgDQojICMgDQojICBjb2xuYW1lcyhYMikgPC0gYygnYWN0dWFsX3Byb2R1Y3Rpdml0eScsICd0YXJnZXRlZF9wcm9kdWN0aXZpdHknLCdzbXYnLCAnd2lwJywgDQojICAgICAgICAgICAgICAgICAgICAgICAgICdvdmVyX3RpbWUnLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgJ2luY2VudGl2ZScsICdpZGxlX3RpbWUnICwgJ2lkbGVfbWVuJywNCiMgICAgICAgICAgICAgICAgICAgICAgICAgJ25vX29mX3N0eWxlX2NoYW5nZScsICdub19vZl93b3JrZXJzJywnZGVwYXJ0bWVudCcsDQojICAgICAgICAgICAgICAgICAgICAgICAgJ3RlYW0yJywndGVhbTMnLCd0ZWFtNCcsJ3RlYW01JywndGVhbTYnLCAndGVhbTcnLCd0ZWFtOCcsJ3RlYW05JywndGVhbTEwJywndGVhbTExJywndGVhbTEyJykNCg0KI1gyIDwtIGNvbXBsZXRlX2dhcm1lbnRfZGF0YTFbLCAtYygxLDIsNCwxNSwxNildIA0KeTIgPC0gY29tcGxldGVfZ2FybWVudF9kYXRhMVssIDE1XSANCg0Kc2V0LnNlZWQoMTIzKQ0KdHJhaW4uaW5kZXgyIDwtIHNhbXBsZSgxOm5yb3coWDIpLCAwLjggKiBucm93KFgyKSkNClgudHJhaW4yIDwtIFgyW3RyYWluLmluZGV4MiwgXQ0KeS50cmFpbjIgPC0geTJbdHJhaW4uaW5kZXgyXQ0KWC50ZXN0MiA8LSBYMlstdHJhaW4uaW5kZXgyLCBdDQp5LnRlc3QyIDwtIHkyWy10cmFpbi5pbmRleDJdDQoNCnR1bmUuUkJGMiA8LSB0dW5lKHN2bSwgdHJhaW4ueCA9IFgudHJhaW4yLCB0cmFpbi55ID0geS50cmFpbjIsIA0KICAgICAgICAgICAgICAgICAgICByYW5nZXMgPSBsaXN0KGVwc2lsb24gPSBzZXEoMC4xLCAwLjUsIDAuMSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvc3QgPSBjKDEsIDEwLCAxMDApLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnYW1tYSA9IGMoMC4wMSwgMC4xLCAxKSksICMgSHlwZXJwYXIgaW4gUkJGDQogICAgICAgICAgICAgICAgICAgIHR1bmVjb250cm9sID0gdHVuZV9jb250cm9sDQogICAgICAgICAgICAgICAgICAgICkNCg0KZmluYWwuUkJGMiA8LSBzdm0oWC50cmFpbjIsIHkudHJhaW4yLCANCiAgICAgICAgICAgICAgICAgICB0eXBlID0gImVwcy1yZWdyZXNzaW9uIiwgICMgVXNlICJudS1yZWdyZXNzaW9uIiBmb3IgbnUtU1ZSDQogICAgICAgICAgICAgICAgICAga2VybmVsID0gInJhZGlhbCIsIA0KICAgICAgICAgICAgICAgICAgIGVwc2lsb24gPSB0dW5lLlJCRjIkYmVzdC5wYXJhbWV0ZXJzJGVwc2lsb24sIA0KICAgICAgICAgICAgICAgICAgIGNvc3QgPSB0dW5lLlJCRjIkYmVzdC5wYXJhbWV0ZXJzJGNvc3QsIA0KICAgICAgICAgICAgICAgICAgIGdhbW1hID0gdHVuZS5SQkYyJGJlc3QucGFyYW1ldGVycyRnYW1tYSkNCg0KcHJlZC5SQkYgPC0gcHJlZGljdChmaW5hbC5SQkYyLCBYLnRlc3QyKQ0KDQojIEV2YWx1YXRlIHBlcmZvcm1hbmNlDQptc2UuUkJGIDwtIG1lYW4oKHkudGVzdDIgLSBwcmVkLlJCRileMikgICAgIyBtZWFuIHNxdWFyZSBlcnJvcg0KbWFlLlJCRiA8LSBtZWFuKGFicyh5LnRlc3QyIC0gcHJlZC5SQkYpKQ0KDQoNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQp0dW5lLmxpbjIgPC0gdHVuZShzdm0sIHRyYWluLnggPSBYLnRyYWluMiwgdHJhaW4ueSA9IHkudHJhaW4yLCANCiAgICAgICAgICAgICAgICAgICAgcmFuZ2VzID0gbGlzdChlcHNpbG9uID0gc2VxKDAuMSwgMC41LCAwLjEpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3N0ID0gYygxLCAxMCwgMTAwKSksIA0KICAgICAgICAgICAgICAgICAgICB0dW5lY29udHJvbCA9IHR1bmVfY29udHJvbA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgKQ0KDQoNCmZpbmFsLmxpbjIgPC0gc3ZtKFgudHJhaW4yLCB5LnRyYWluMiwgDQogICAgICAgICAgICAgICAgICAgdHlwZSA9ICJlcHMtcmVncmVzc2lvbiIsICANCiAgICAgICAgICAgICAgICAgICBrZXJuZWwgPSAibGluZWFyIiwgDQogICAgICAgICAgICAgICAgICAgZXBzaWxvbiA9IHR1bmUubGluMiRiZXN0LnBhcmFtZXRlcnMkZXBzaWxvbiwgDQogICAgICAgICAgICAgICAgICAgY29zdCA9IHR1bmUubGluMiRiZXN0LnBhcmFtZXRlcnMkY29zdCkNCg0KcHJlZC5saW4gPC0gcHJlZGljdChmaW5hbC5saW4yLCBYLnRlc3QyKQ0KDQojIEV2YWx1YXRlIHBlcmZvcm1hbmNlDQptc2UubGluIDwtIG1lYW4oKHkudGVzdDIgLSBwcmVkLmxpbileMikgICAgIyBtZWFuIHNxdWFyZSBlcnJvcg0KbWFlLmxpbiA8LSBtZWFuKGFicyh5LnRlc3QyIC0gcHJlZC5saW4pKQ0KYGBgDQoNCmBgYHtyfQ0KDQpYMiA8LSBjYmluZChjb21wbGV0ZV9nYXJtZW50X2RhdGExWywgMTVdICxYMikNCg0KDQpjb2xuYW1lcyhYMikgPC0gYygnYWN0dWFsX3Byb2R1Y3Rpdml0eScsICd0YXJnZXRlZF9wcm9kdWN0aXZpdHknLCdzbXYnLCAnd2lwJywNCiAgICAgICAgICAgICAgICAgICAgICAgJ292ZXJfdGltZScsDQogICAgICAgICAgICAgICAgICAgICAgICdpbmNlbnRpdmUnLCAnaWRsZV90aW1lJyAsICdpZGxlX21lbicsDQogICAgICAgICAgICAgICAgICAgICAgICdub19vZl9zdHlsZV9jaGFuZ2UnLCAnbm9fb2Zfd29ya2VycycsJ2RlcGFydG1lbnQnLA0KICAgICAgICAgICAgICAgICAgICAgICAndGVhbTInLCd0ZWFtMycsJ3RlYW00JywndGVhbTUnLCd0ZWFtNicsICd0ZWFtNycsJ3RlYW04JywndGVhbTknLCd0ZWFtMTAnLCd0ZWFtMTEnLCd0ZWFtMTInLCdkYXlNb25kYXknLCdkYXlTYXR1cmRheScsJ2RheVN1bmRheScsJ2RheVRodXJzZGF5JywnZGF5VHVlc2RheScsJ2RheVdlZG5lc2RheScpDQoNCmdhcm1lbnQudHJhaW4gPC0gWDJbdHJhaW4uaW5kZXgyLCBdDQpnYXJtZW50LnRlc3QgPC0gWDJbLXRyYWluLmluZGV4MiwgXQ0KDQojIFgzIDwtIGNiaW5kKGNvbXBsZXRlX2dhcm1lbnRfZGF0YTFbLCAxNV0gLFgzKQ0KIyANCiMgDQojIGNvbG5hbWVzKFgzKSA8LSBjKCdhY3R1YWxfcHJvZHVjdGl2aXR5JywgJ3RhcmdldGVkX3Byb2R1Y3Rpdml0eScsJ3NtdicsICd3aXAnLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAnb3Zlcl90aW1lJywgDQojICAgICAgICAgICAgICAgICAgICAgICAgJ2luY2VudGl2ZScsICdpZGxlX3RpbWUnICwgJ2lkbGVfbWVuJywNCiMgICAgICAgICAgICAgICAgICAgICAgICAnbm9fb2Zfc3R5bGVfY2hhbmdlJywgJ25vX29mX3dvcmtlcnMnLCdkZXBhcnRtZW50JywNCiMgICAgICAgICAgICAgICAgICAgICAgICAndGVhbTInLCd0ZWFtMycsJ3RlYW00JywndGVhbTUnLCd0ZWFtNicsICd0ZWFtNycsJ3RlYW04JywndGVhbTknLCd0ZWFtMTAnLCd0ZWFtMTEnLCd0ZWFtMTInKQ0KDQpsc2UuZml0IDwtIGxtKGFjdHVhbF9wcm9kdWN0aXZpdHkgfiB0YXJnZXRlZF9wcm9kdWN0aXZpdHkgKyBzbXYgKyB3aXAgKyBvdmVyX3RpbWUgKyBpbmNlbnRpdmUgKyBpZGxlX3RpbWUgKyBpZGxlX21lbiArIG5vX29mX3N0eWxlX2NoYW5nZSArIG5vX29mX3dvcmtlcnMgKyBkZXBhcnRtZW50ICsgdGVhbTIrIHRlYW0zKyB0ZWFtNCsgdGVhbTUrIHRlYW02KyB0ZWFtNysgdGVhbTgrIHRlYW05KyB0ZWFtMTArIHRlYW0xMSsgdGVhbTEyICsgZGF5TW9uZGF5ICsgZGF5U2F0dXJkYXkgKyBkYXlTdW5kYXkgKyBkYXlUaHVyc2RheSArIGRheVR1ZXNkYXkgKyBkYXlXZWRuZXNkYXksZGF0YT1nYXJtZW50LnRyYWluKQ0KQUlDLmZpdCA8LSBzdGVwQUlDKGxzZS5maXQsZGlyZWN0aW9uPSJib3RoIiwgdHJhY2UgPSBGQUxTRSkNCg0KDQoNCnByZWQubHNlIDwtIHByZWRpY3QoQUlDLmZpdCwgWC50ZXN0MikNCm1zZS5sc2UgPC0gbWVhbigoeS50ZXN0MiAtIHByZWQubHNlKV4yKSAgICAjIG1lYW4gc3F1YXJlIGVycm9yDQptYWUubHNlIDwtIG1lYW4oYWJzKHkudGVzdDIgLSBwcmVkLmxzZSkpICAgIyBtZWFuIGFic29sdXRlIGVycm9yDQojIyMNCnBhcihtZnJvdz1jKDIsMiksIG1hcj1jKDIsMiwyLDIpKQ0KcGxvdChBSUMuZml0KQ0KDQoNCmBgYA0KDQpUaGUgYWJvdmUgc2hvd3MgdGhlIHJlc2lkdWFsIHBsb3RzIHdoZW4gd2UgZGlkIHRoZSBPTFMgcmVncmVzc2lvbi4gVGhlIHJlc2lkdWFscyB2cyBmaXR0ZWQgcGxvdCBkb2VzIG5vdCBzZWVtIHRvIHNob3cgYW55IGN1cnZlIHBhdHRlcm5zIHNvIG5vIHRyYW5zZm9ybWF0aW9uIGlzIG5lY2Vzc2FyeS4NCg0KQmVsb3cgd2UgY29tcGFyZSB0aGUgcGVyZm9ybWFuY2VzIG9mIFNWUiBMaW5lYXIsIFNWUiBSQkYsIGFuZCBPTFMgcmVncmVzc2lvbi4gVGhlIE1TRSBhbmQgTUFFIHZhbHVlcyBhcmUgYWxsIHNpbWlsYXIsIHdpdGggUkJGIGtlcm5lbCBkb2luZyB0aGUgYmVzdC4NCg0KYGBge3J9DQpQZXJmb3JtYW5jZSA8LSBkYXRhLmZyYW1lKFJCRi5TVlI9Yyhtc2UuUkJGLCBtYWUuUkJGKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgTGluZWFyLlNWUiA9IGMobXNlLmxpbiwgbWFlLmxpbiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIExTRS5SZWcgPWMobXNlLmxzZSwgbWFlLmxzZSkpDQpyb3cubmFtZXMoUGVyZm9ybWFuY2UpIDwtIGMoIk1TRSIsICJNQUUiKQ0KIyMNCnBhbmRlcihQZXJmb3JtYW5jZSkNCmBgYA0KDQojIFJlc3VsdHMvQ29uY2x1c2lvbg0KDQpGb3IgcmVndWxhcml6ZWQgbGluZWFyIHJlZ3Jlc3Npb24sIHRoZSBvcHRpbWFsIHBhcmFtZXRlcnMgZm9yIGxhc3NvIGlzIDAuMTMyOCwgMC4xMzM0IGZvciByaWRnZSwgYW5kIDAuMTMyOSBmb3IgZWxhc3RpYyBuZXQuIFRoZXNlIFJNU0UgdmFsdWVzIGFyZSBiYXNlZCBvbiBsYW1iZGEubWluICh2YWx1ZSBvZiBsYW1iZGEgdGhhdCBnaXZlcyB0aGUgbWluaW11bSBjcm9zcy12YWxpZGF0ZWQgZXJyb3IpLiBCYXNlZCBvbiB0aGVzZSB2YWx1ZXMsIHdlIGdldCB0aGlzIGVxdWF0aW9uIGZvciBsYXNzbzoNCg0KTW9kZWwgZXF1YXRpb246IHkgPSAwLjczMzggKyAwLjA2NjEqdGFyZ2V0ZWRfcHJvZHVjdGl2aXR5ICsgLTAuMDc4OCpzbXYgKyAwLjAwNTkqd2lwICsgLTAuMDEyOCpvdmVyX3RpbWUgKyAwLjAwNzEqaW5jZW50aXZlICsgMC4wMDUzKmlkbGVfdGltZSArIC0wLjAyOTMqaWRsZV9tZW4gKyAtMC4wMTY4Km5vX29mX3N0eWxlX2NoYW5nZSArIDAuMTAzOSpub19vZl93b3JrZXJzICsgMC4wMjI0KmRlcGFydG1lbnQgKyAtMC4wMTQ1KnRlYW0yICsgOWUtMDQqdGVhbTMgKyAtMC4wMDQ5KnRlYW00ICsgLTAuMDExMyp0ZWFtNSArIC0wLjAxOTUqdGVhbTYgKyAtMC4wMjczKnRlYW03ICsgLTAuMDI1Nip0ZWFtOCArIC0wLjAyNTMqdGVhbTkgKyAtMC4wMjI4KnRlYW0xMCArIC0wLjAzNjYqdGVhbTExICsgLTAuMDEyMip0ZWFtMTIgKyAtMC4wMDQ5KmRheU1vbmRheSArIDAuMDAyOSpkYXlTYXR1cmRheSArIC0wLjAwMzUqZGF5U3VuZGF5ICsgLTAuMDA1OSpkYXlUaHVyc2RheSArIDAuMDAzOSpkYXlUdWVzZGF5ICsgMCpkYXlXZWRuZXNkYXkgDQoNCg0KVGhpcyBlcXVhdGlvbiBmb3IgcmlkZ2U6DQoNCk1vZGVsIGVxdWF0aW9uOiB5ID0gMC43MzM4ICsgMC4wNjM3KnRhcmdldGVkX3Byb2R1Y3Rpdml0eSArIC0wLjA1MzIqc212ICsgMC4wMDcyKndpcCArIC0wLjAwODMqb3Zlcl90aW1lICsgMC4wMDc3KmluY2VudGl2ZSArIDAuMDA0NSppZGxlX3RpbWUgKyAtMC4wMjc3KmlkbGVfbWVuICsgLTAuMDE2NCpub19vZl9zdHlsZV9jaGFuZ2UgKyAwLjA1NjQqbm9fb2Zfd29ya2VycyArIDAuMDA0MipkZXBhcnRtZW50ICsgLTAuMDEwNip0ZWFtMiArIDAuMDA1KnRlYW0zICsgLTAuMDAxMyp0ZWFtNCArIC0wLjAwODUqdGVhbTUgKyAtMC4wMTkzKnRlYW02ICsgLTAuMDI0Mip0ZWFtNyArIC0wLjAyMjQqdGVhbTggKyAtMC4wMjEyKnRlYW05ICsgLTAuMDE4Nyp0ZWFtMTAgKyAtMC4wMjkzKnRlYW0xMSArIC0wLjAxMTcqdGVhbTEyICsgLTAuMDAzOCpkYXlNb25kYXkgKyAwLjAwNDEqZGF5U2F0dXJkYXkgKyAtMC4wMDI4KmRheVN1bmRheSArIC0wLjAwNDcqZGF5VGh1cnNkYXkgKyAwLjAwNTIqZGF5VHVlc2RheSArIDAuMDAxOSpkYXlXZWRuZXNkYXkgDQoNCg0KVGhpcyBlcXVhdGlvbiBmb3IgZWxhc3RpYyBuZXQ6DQoNCk1vZGVsIGVxdWF0aW9uOiB5ID0gMC43MzM4ICsgMC4wNjYqdGFyZ2V0ZWRfcHJvZHVjdGl2aXR5ICsgLTAuMDc2KnNtdiArIDAuMDA2KndpcCArIC0wLjAxMipvdmVyX3RpbWUgKyAwLjAwNzEqaW5jZW50aXZlICsgMC4wMDUxKmlkbGVfdGltZSArIC0wLjAyOSppZGxlX21lbiArIC0wLjAxNjYqbm9fb2Zfc3R5bGVfY2hhbmdlICsgMC4wOTY1Km5vX29mX3dvcmtlcnMgKyAwLjAxODUqZGVwYXJ0bWVudCArIC0wLjAxMzUqdGVhbTIgKyAwLjAwMTgqdGVhbTMgKyAtMC4wMDM4KnRlYW00ICsgLTAuMDEwNCp0ZWFtNSArIC0wLjAxOTMqdGVhbTYgKyAtMC4wMjY1KnRlYW03ICsgLTAuMDI0OCp0ZWFtOCArIC0wLjAyNDMqdGVhbTkgKyAtMC4wMjE5KnRlYW0xMCArIC0wLjAzNTMqdGVhbTExICsgLTAuMDExOSp0ZWFtMTIgKyAtMC4wMDQqZGF5TW9uZGF5ICsgMC4wMDM1KmRheVNhdHVyZGF5ICsgLTAuMDAyOCpkYXlTdW5kYXkgKyAtMC4wMDUxKmRheVRodXJzZGF5ICsgMC4wMDQ1KmRheVR1ZXNkYXkgKyA4ZS0wNCpkYXlXZWRuZXNkYXkgDQoNCkZvciBsYXNzbywgcmlkZ2UsIGFuZCBlbGFzdGljIGluIHJlZ3VsYXJpemVkIGxvZ2lzdGljIHJlZ3Jlc3Npb24gd2UgZ2V0IHRoZSBjb2VmZmljaWVudHMgc2hvd24gaW4gJ3JlZ3VsYXJpemVkLmNvZWYxJy4gV2hlbiB3ZSBjb21wYXJlIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgbGFzc28sIFJpZGdlLCBhbmQgRWxhc3RpYyBOZXQgbW9kZWxzIG9uIG91ciB0ZXN0IGRhdGEgdXNpbmcgUk9DIGN1cnZlcywgd2Ugc2VlIHRoYXQgYWxsIG1vZGVscyBoYXZlIHNpbWlsYXIgcGVyZm9ybWFuY2VzIHdpdGggQVVDIG9mICAwLjc2MiBmb3IgbGFzc28sIDAuNzU3IGZvciByaWRnZSwgYW5kIDAuNzU4IGZvciBlbGFzdGljLg0KDQpTaW5jZSB0aGUgb3B0aW1hbCBwYXJhbWV0ZXIgd2FzIHRoZSBsb3dlc3QgaW4gcmVndWxhcml6ZWQgbGluZWFyIGFuZCB0aGUgQVVDIHdhcyBoaWdoZXN0IGluIHJlZ3VsYXJpemVkIGxvZ2lzdGljIHJlZ3Jlc3Npb24sIGEgbGFzc28gbW9kZWwgaXMgcmVjb21tZW5kZWQuIExhc3NvIGlzIHByZWZlcnJlZCBiZWNhdXNlIGl0IHBlcmZvcm1zIGZlYXR1cmUgc2VsZWN0aW9uIChzaHJpbmtpbmcgY29lZmZpY2llbnRzIHRvIDApLCBlYXNpZXIgdG8gaW50ZXJwcmV0LCBhbmQgYmV0dGVyIGZvciBIaWdoLWRpbWVuc2lvbmFsIGRhdGEuDQoNCkZvciBTVk0sIHRoZSBBVUMgZm9yIFNWTSAobGluZWFyIGtlcm5lbCkgaXMgMC43ODksIDAuODE5MyBmb3IgU1ZNIChSQkYga2VybmVsKSwgYW5kIDAuNzkgZm9yIHN0YW5kYXJkIHN0ZXB3aXNlIGxvZ2lzdGljIHJlZ3Jlc3Npb24uIE92ZXJhbGwsIHNpbmNlIHRoZSBBVUMgZm9yIFNWTSAoYm90aCBsaW5lYXIvUkJGKSBhcmUgZ3JlYXRlciB0aGFuIHRoZSBBVUNzIGluIHRoZSByZWd1bGFyaXplZCBsb2dpc3RpYyByZWdyZXNzaW9uLCB3ZSBjYW4gYXNzdW1lIHRoYXQgU1ZNIGlzIGJldHRlciBiZWNhdXNlIHRoZXkgYXJlIHJvYnVzdCB0byBvdmVyZml0dGluZywgY2FuIHVzZSBrZXJuZWxzLCBhbmQgY2FuIGhhbmRsZSBsaW5lYXIvbm9ubGluZWFyIGRhdGEuDQoNCkZvciBTVlIsIFNWUiBSQkYgaGFzIGFuIE1TRSBvZiAwLjAxODQyLCAwLjAyMjMzIGZvciBTVlIgTGluZWFyLCBhbmQgMC4wMjA2NyBmb3IgT0xTIHJlZ3Jlc3Npb24uIFNpbmNlIHRoZSBNU0UgdmFsdWVzIGFyZSBsb3dlciB0aGFuIHRoYXQgaW4gcmVndWxhcml6ZWQgbGluZWFyIHJlZ3Jlc3Npb24sIHdlIGNhbiBhc3N1bWUgdGhhdCBTVlIgaXMgYmV0dGVyLg0KDQoNCg0KDQo=