1 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.

2 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 ...

2.1 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 

3 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")

4 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)

5 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)

6 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.

6.1 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)

6.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)
LASSO.opt Ridge.opt Elasticnet.opt
0.1328 0.1334 0.1329

6.3 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 

7 Regularized Logistic Regression

7.1 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)

7.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)
  lasso ridge elasticnet
(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

7.3 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")

8 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)

9 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)
  RBF.SVR Linear.SVR LSE.Reg
MSE 0.01842 0.02233 0.02067
MAE 0.08615 0.09348 0.1006

10 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=