library(tidyverse)
library(openintro)
data <- read_csv("~/Desktop/School Portfolio/UTSA/Undergrad/Data Mining/Project/HR_Analytics.csv")
## Rows: 1470 Columns: 35
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (9): Attrition, BusinessTravel, Department, EducationField, Gender, Job...
## dbl (26): Age, DailyRate, DistanceFromHome, Education, EmployeeCount, Employ...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# data clean up
# Check the structure of the dataset
str(data)
## spc_tbl_ [1,470 × 35] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ Age : num [1:1470] 41 49 37 33 27 32 59 30 38 36 ...
## $ Attrition : chr [1:1470] "Yes" "No" "Yes" "No" ...
## $ BusinessTravel : chr [1:1470] "Travel_Rarely" "Travel_Frequently" "Travel_Rarely" "Travel_Frequently" ...
## $ DailyRate : num [1:1470] 1102 279 1373 1392 591 ...
## $ Department : chr [1:1470] "Sales" "Research & Development" "Research & Development" "Research & Development" ...
## $ DistanceFromHome : num [1:1470] 1 8 2 3 2 2 3 24 23 27 ...
## $ Education : num [1:1470] 2 1 2 4 1 2 3 1 3 3 ...
## $ EducationField : chr [1:1470] "Life Sciences" "Life Sciences" "Other" "Life Sciences" ...
## $ EmployeeCount : num [1:1470] 1 1 1 1 1 1 1 1 1 1 ...
## $ EmployeeNumber : num [1:1470] 1 2 4 5 7 8 10 11 12 13 ...
## $ EnvironmentSatisfaction : num [1:1470] 2 3 4 4 1 4 3 4 4 3 ...
## $ Gender : chr [1:1470] "Female" "Male" "Male" "Female" ...
## $ HourlyRate : num [1:1470] 94 61 92 56 40 79 81 67 44 94 ...
## $ JobInvolvement : num [1:1470] 3 2 2 3 3 3 4 3 2 3 ...
## $ JobLevel : num [1:1470] 2 2 1 1 1 1 1 1 3 2 ...
## $ JobRole : chr [1:1470] "Sales Executive" "Research Scientist" "Laboratory Technician" "Research Scientist" ...
## $ JobSatisfaction : num [1:1470] 4 2 3 3 2 4 1 3 3 3 ...
## $ MaritalStatus : chr [1:1470] "Single" "Married" "Single" "Married" ...
## $ MonthlyIncome : num [1:1470] 5993 5130 2090 2909 3468 ...
## $ MonthlyRate : num [1:1470] 19479 24907 2396 23159 16632 ...
## $ NumCompaniesWorked : num [1:1470] 8 1 6 1 9 0 4 1 0 6 ...
## $ Over18 : chr [1:1470] "Y" "Y" "Y" "Y" ...
## $ OverTime : chr [1:1470] "Yes" "No" "Yes" "Yes" ...
## $ PercentSalaryHike : num [1:1470] 11 23 15 11 12 13 20 22 21 13 ...
## $ PerformanceRating : num [1:1470] 3 4 3 3 3 3 4 4 4 3 ...
## $ RelationshipSatisfaction: num [1:1470] 1 4 2 3 4 3 1 2 2 2 ...
## $ StandardHours : num [1:1470] 80 80 80 80 80 80 80 80 80 80 ...
## $ StockOptionLevel : num [1:1470] 0 1 0 0 1 0 3 1 0 2 ...
## $ TotalWorkingYears : num [1:1470] 8 10 7 8 6 8 12 1 10 17 ...
## $ TrainingTimesLastYear : num [1:1470] 0 3 3 3 3 2 3 2 2 3 ...
## $ WorkLifeBalance : num [1:1470] 1 3 3 3 3 2 2 3 3 2 ...
## $ YearsAtCompany : num [1:1470] 6 10 0 8 2 7 1 1 9 7 ...
## $ YearsInCurrentRole : num [1:1470] 4 7 0 7 2 7 0 0 7 7 ...
## $ YearsSinceLastPromotion : num [1:1470] 0 1 0 3 2 3 0 0 1 7 ...
## $ YearsWithCurrManager : num [1:1470] 5 7 0 0 2 6 0 0 8 7 ...
## - attr(*, "spec")=
## .. cols(
## .. Age = col_double(),
## .. Attrition = col_character(),
## .. BusinessTravel = col_character(),
## .. DailyRate = col_double(),
## .. Department = col_character(),
## .. DistanceFromHome = col_double(),
## .. Education = col_double(),
## .. EducationField = col_character(),
## .. EmployeeCount = col_double(),
## .. EmployeeNumber = col_double(),
## .. EnvironmentSatisfaction = col_double(),
## .. Gender = col_character(),
## .. HourlyRate = col_double(),
## .. JobInvolvement = col_double(),
## .. JobLevel = col_double(),
## .. JobRole = col_character(),
## .. JobSatisfaction = col_double(),
## .. MaritalStatus = col_character(),
## .. MonthlyIncome = col_double(),
## .. MonthlyRate = col_double(),
## .. NumCompaniesWorked = col_double(),
## .. Over18 = col_character(),
## .. OverTime = col_character(),
## .. PercentSalaryHike = col_double(),
## .. PerformanceRating = col_double(),
## .. RelationshipSatisfaction = col_double(),
## .. StandardHours = col_double(),
## .. StockOptionLevel = col_double(),
## .. TotalWorkingYears = col_double(),
## .. TrainingTimesLastYear = col_double(),
## .. WorkLifeBalance = col_double(),
## .. YearsAtCompany = col_double(),
## .. YearsInCurrentRole = col_double(),
## .. YearsSinceLastPromotion = col_double(),
## .. YearsWithCurrManager = col_double()
## .. )
## - attr(*, "problems")=<externalptr>
## # A tibble: 6 × 35
## Age Attrition BusinessTravel DailyRate Department DistanceFromHome Education
## <dbl> <chr> <chr> <dbl> <chr> <dbl> <dbl>
## 1 41 Yes Travel_Rarely 1102 Sales 1 2
## 2 49 No Travel_Freque… 279 Research … 8 1
## 3 37 Yes Travel_Rarely 1373 Research … 2 2
## 4 33 No Travel_Freque… 1392 Research … 3 4
## 5 27 No Travel_Rarely 591 Research … 2 1
## 6 32 No Travel_Freque… 1005 Research … 2 2
## # ℹ 28 more variables: EducationField <chr>, EmployeeCount <dbl>,
## # EmployeeNumber <dbl>, EnvironmentSatisfaction <dbl>, Gender <chr>,
## # HourlyRate <dbl>, JobInvolvement <dbl>, JobLevel <dbl>, JobRole <chr>,
## # JobSatisfaction <dbl>, MaritalStatus <chr>, MonthlyIncome <dbl>,
## # MonthlyRate <dbl>, NumCompaniesWorked <dbl>, Over18 <chr>, OverTime <chr>,
## # PercentSalaryHike <dbl>, PerformanceRating <dbl>,
## # RelationshipSatisfaction <dbl>, StandardHours <dbl>, …
# Check for nulls and redundant constant fields, constant variables mess up the selection process since it’s based on variability
# Check for null values
null_columns <- sapply(data, function(x) any(is.na(x)))
if (any(null_columns)) {
print("Null values detected.")
print("Columns with null values:")
print(names(data)[null_columns])
} else {
print("No null values detected.")
}
## [1] "No null values detected."
# Check for constant fields
constant_columns <- sapply(data, function(x) length(unique(x)) == 1)
if (any(constant_columns)) {
print("Constant fields detected.")
print("Columns with constant values:")
print(names(data)[constant_columns])
} else {
print("No constant fields detected.")
}
## [1] "Constant fields detected."
## [1] "Columns with constant values:"
## [1] "EmployeeCount" "Over18" "StandardHours"
# Drop constant fields and "EmployeeNumber" variable, I’m dropping EmployeeNumber since this alone can’t be a good indicator of whether an employee will term as it’s unique for every employee and only differentiates employees from one another
data <- data %>%
select(-where(~length(unique(.)) == 1), -EmployeeNumber)
#This step may not be necessary but it shouldn’t affect model performance
# Convert character variables to factors
data <- data %>%
mutate_if(is.character, as.factor)
# Check the structure of the dataset
str(data)
## tibble [1,470 × 31] (S3: tbl_df/tbl/data.frame)
## $ Age : num [1:1470] 41 49 37 33 27 32 59 30 38 36 ...
## $ Attrition : Factor w/ 2 levels "No","Yes": 2 1 2 1 1 1 1 1 1 1 ...
## $ BusinessTravel : Factor w/ 3 levels "Non-Travel","Travel_Frequently",..: 3 2 3 2 3 2 3 3 2 3 ...
## $ DailyRate : num [1:1470] 1102 279 1373 1392 591 ...
## $ Department : Factor w/ 3 levels "Human Resources",..: 3 2 2 2 2 2 2 2 2 2 ...
## $ DistanceFromHome : num [1:1470] 1 8 2 3 2 2 3 24 23 27 ...
## $ Education : num [1:1470] 2 1 2 4 1 2 3 1 3 3 ...
## $ EducationField : Factor w/ 6 levels "Human Resources",..: 2 2 5 2 4 2 4 2 2 4 ...
## $ EnvironmentSatisfaction : num [1:1470] 2 3 4 4 1 4 3 4 4 3 ...
## $ Gender : Factor w/ 2 levels "Female","Male": 1 2 2 1 2 2 1 2 2 2 ...
## $ HourlyRate : num [1:1470] 94 61 92 56 40 79 81 67 44 94 ...
## $ JobInvolvement : num [1:1470] 3 2 2 3 3 3 4 3 2 3 ...
## $ JobLevel : num [1:1470] 2 2 1 1 1 1 1 1 3 2 ...
## $ JobRole : Factor w/ 9 levels "Healthcare Representative",..: 8 7 3 7 3 3 3 3 5 1 ...
## $ JobSatisfaction : num [1:1470] 4 2 3 3 2 4 1 3 3 3 ...
## $ MaritalStatus : Factor w/ 3 levels "Divorced","Married",..: 3 2 3 2 2 3 2 1 3 2 ...
## $ MonthlyIncome : num [1:1470] 5993 5130 2090 2909 3468 ...
## $ MonthlyRate : num [1:1470] 19479 24907 2396 23159 16632 ...
## $ NumCompaniesWorked : num [1:1470] 8 1 6 1 9 0 4 1 0 6 ...
## $ OverTime : Factor w/ 2 levels "No","Yes": 2 1 2 2 1 1 2 1 1 1 ...
## $ PercentSalaryHike : num [1:1470] 11 23 15 11 12 13 20 22 21 13 ...
## $ PerformanceRating : num [1:1470] 3 4 3 3 3 3 4 4 4 3 ...
## $ RelationshipSatisfaction: num [1:1470] 1 4 2 3 4 3 1 2 2 2 ...
## $ StockOptionLevel : num [1:1470] 0 1 0 0 1 0 3 1 0 2 ...
## $ TotalWorkingYears : num [1:1470] 8 10 7 8 6 8 12 1 10 17 ...
## $ TrainingTimesLastYear : num [1:1470] 0 3 3 3 3 2 3 2 2 3 ...
## $ WorkLifeBalance : num [1:1470] 1 3 3 3 3 2 2 3 3 2 ...
## $ YearsAtCompany : num [1:1470] 6 10 0 8 2 7 1 1 9 7 ...
## $ YearsInCurrentRole : num [1:1470] 4 7 0 7 2 7 0 0 7 7 ...
## $ YearsSinceLastPromotion : num [1:1470] 0 1 0 3 2 3 0 0 1 7 ...
## $ YearsWithCurrManager : num [1:1470] 5 7 0 0 2 6 0 0 8 7 ...
set.seed(1)
test_sample <- sample(1:nrow(data), nrow(data)/4)
train <- data[-test_sample, ]
test <- data[test_sample, ]
library(leaps)
fwd <- regsubsets(Attrition ~ ., data=train, nvmax=17, method='forward')
fwd_sum <- summary(fwd)
par(mfrow=c(2,2))
plot(fwd_sum$cp ,xlab="Number of Variables ", ylab="Cp",
type="b")
points(which.min(fwd_sum$cp), fwd_sum$cp[which.min(fwd_sum$cp)], col="red",
cex=2, pch=20)
plot(fwd_sum$bic ,xlab="Number of Variables ",
ylab="BIC",type="b")
points(which.min(fwd_sum$bic), fwd_sum$bic[which.min(fwd_sum$bic)], col="red",
cex=2, pch=20)
plot(fwd_sum$adjr2 ,xlab="Number of Variables ",
ylab="Adjusted R^2^",type="b")
points(which.max(fwd_sum$adjr2), fwd_sum$adjr2[which.max(fwd_sum$adjr2)],
col="red", cex=2, pch=20)
which.min(fwd_sum$cp)
## [1] 17
## [1] 10
## [1] 17
test_matrix <- model.matrix(Attrition~., data=test)
val.errors <- rep(NA,17)
for(i in 1:17){
coefi <- coef(fwd,id=i)
pred <- test_matrix[,names(coefi)]%*%coefi
pred_binary <- ifelse(pred >= 1.5, 2, 1)
val.errors[i] <- mean((as.numeric(test$Attrition) - pred_binary)^2)
}
which.min(val.errors)
## [1] 16
plot(val.errors, type='b')
points(which.min(val.errors), val.errors[10], col='red', pch=20, cex=2)

fwd_full <- regsubsets(Attrition ~ ., data=data, nvmax=17, method='forward')
coef(fwd_full, 10)
## (Intercept) BusinessTravelTravel_Frequently
## 1.447840578 0.093717866
## EnvironmentSatisfaction JobInvolvement
## -0.042274767 -0.063619678
## JobRoleLaboratory Technician JobRoleSales Representative
## 0.084311789 0.204129425
## JobSatisfaction MaritalStatusSingle
## -0.038640531 0.119075546
## NumCompaniesWorked OverTimeYes
## 0.014978062 0.214539213
## TotalWorkingYears
## -0.006703014
# Get the indices of the selected variables
selected_indices <- which.min(fwd_sum$bic)
# Extract the names of the selected variables
selected_variables <- names(which(fwd_sum$which[selected_indices, ] != 0, arr.ind = TRUE))
sel_var <-selected_variables[-1]
# Print the selected variables
print(sel_var)
## [1] "BusinessTravelTravel_Frequently" "EnvironmentSatisfaction"
## [3] "JobInvolvement" "JobRoleSales Representative"
## [5] "JobSatisfaction" "MaritalStatusSingle"
## [7] "NumCompaniesWorked" "OverTimeYes"
## [9] "TotalWorkingYears" "WorkLifeBalance"
# Identify the columns that need one-hot encoding
columns_to_encode <- c("MaritalStatus", "BusinessTravel","JobRole","OverTime")
# One-hot encode selected columns
encoded_train <- model.matrix(~ . - 1, data = train[columns_to_encode])
encoded_test<-model.matrix(~ . - 1, data = test[columns_to_encode])
# Combine encoded columns with original dataset
encoded_train <- cbind(train, encoded_train)
encoded_test <- cbind(test, encoded_test)
# Filter out columns from the final dataset
train_fnl <- encoded_train[, c("Attrition", sel_var)]
test_fnl <- encoded_test[, c("Attrition", sel_var)]
## Loading required package: lattice
##
## Attaching package: 'lattice'
## The following objects are masked from 'package:openintro':
##
## ethanol, lsegments
##
## Attaching package: 'caret'
## The following object is masked from 'package:openintro':
##
## dotPlot
## The following object is masked from 'package:purrr':
##
## lift
## Type 'citation("pROC")' for a citation.
##
## Attaching package: 'pROC'
## The following objects are masked from 'package:stats':
##
## cov, smooth, var
#Log model
# Fit logistic regression model using train_fnl
cv <- trainControl(method='cv', number=10, savePredictions = T)
logistic_model <- train(Attrition ~ ., data = train_fnl, method='glm', family = binomial, trControl=cv)
logistic_model
## Generalized Linear Model
##
## 1103 samples
## 10 predictor
## 2 classes: 'No', 'Yes'
##
## No pre-processing
## Resampling: Cross-Validated (10 fold)
## Summary of sample sizes: 993, 993, 993, 993, 993, 993, ...
## Resampling results:
##
## Accuracy Kappa
## 0.8694758 0.3854704
# Calculate precision, recall, and F1-score using the confusion matrix
predicted <- logistic_model$pred$pred
observed <- logistic_model$pred$obs
conf_matrix <- confusionMatrix(predicted, observed)
accuracy <- sum(conf_matrix$table[1,1], conf_matrix$table[2,2]) / sum(conf_matrix$table)
precision <- conf_matrix$byClass["Pos Pred Value"]
recall <- conf_matrix$byClass["Sensitivity"]
f1_score <- 2 * (precision * recall) / (precision + recall)
# Calculate AUC
roc_curve <- roc(as.numeric(observed), as.numeric(predicted))
## Setting levels: control = 1, case = 2
## Setting direction: controls < cases
auc_value <- auc(roc_curve)
# Display evaluation metrics
print(paste("Accuracy:", round(accuracy, 4)))
## [1] "Accuracy: 0.8694"
print(paste("Precision:", round(precision, 4)))
## [1] "Precision: 0.8824"
print(paste("Recall:", round(recall, 4)))
## [1] "Recall: 0.974"
print(paste("F1-score:", round(f1_score, 4)))
## [1] "F1-score: 0.9259"
print(paste("AUC:", round(auc_value, 4)))
## [1] "AUC: 0.6518"
# Plot ROC curve
plot(roc_curve, main = "ROC Curve", col = "blue", lwd = 2)
text(0.95, 0, paste("AUC =", round(auc(roc_curve), 2)), adj = c(1, 0), pos = 4)

##LDA
# Fit LDA model using train_fnl
cv <- trainControl(method = 'cv', number = 10, savePredictions = TRUE)
lda_model <- train(Attrition ~ ., data = train_fnl, method = 'lda', trControl = cv)
lda_model
## Linear Discriminant Analysis
##
## 1103 samples
## 10 predictor
## 2 classes: 'No', 'Yes'
##
## No pre-processing
## Resampling: Cross-Validated (10 fold)
## Summary of sample sizes: 994, 993, 993, 992, 993, 993, ...
## Resampling results:
##
## Accuracy Kappa
## 0.8603583 0.3643567
# Calculate precision, recall, and F1-score using the confusion matrix
lda_predicted <- lda_model$pred$pred
lda_observed <- lda_model$pred$obs
lda_conf_matrix <- confusionMatrix(lda_predicted, lda_observed)
lda_accuracy <- sum(lda_conf_matrix$table[1,1], lda_conf_matrix$table[2,2]) / sum(lda_conf_matrix$table)
lda_precision <- lda_conf_matrix$byClass["Pos Pred Value"]
lda_recall <- lda_conf_matrix$byClass["Sensitivity"]
lda_f1_score <- 2 * (lda_precision * lda_recall) / (lda_precision + lda_recall)
# Calculate AUC
lda_roc_curve <- roc(as.numeric(lda_observed), as.numeric(lda_predicted))
## Setting levels: control = 1, case = 2
## Setting direction: controls < cases
lda_auc_value <- auc(lda_roc_curve)
# Display evaluation metrics
print(paste("Accuracy:", round(lda_accuracy, 4)))
## [1] "Accuracy: 0.8604"
print(paste("Precision:", round(lda_precision, 4)))
## [1] "Precision: 0.8827"
print(paste("Recall:", round(lda_recall, 4)))
## [1] "Recall: 0.961"
print(paste("F1-score:", round(lda_f1_score, 4)))
## [1] "F1-score: 0.9202"
print(paste("AUC:", round(lda_auc_value, 4)))
## [1] "AUC: 0.6509"
# Plot ROC curve
plot(lda_roc_curve, main = "ROC Curve", col = "blue", lwd = 2)
text(0.95, 0, paste("AUC =", round(auc(lda_roc_curve), 2)), adj = c(1, 0), pos = 4)

#QDA
# Fit QDA model using train_fnl
cv <- trainControl(method = 'cv', number = 10, savePredictions = TRUE)
qda_model <- train(Attrition ~ ., data = train_fnl, method = 'qda', trControl = cv)
qda_model
## Quadratic Discriminant Analysis
##
## 1103 samples
## 10 predictor
## 2 classes: 'No', 'Yes'
##
## No pre-processing
## Resampling: Cross-Validated (10 fold)
## Summary of sample sizes: 993, 992, 993, 993, 993, 993, ...
## Resampling results:
##
## Accuracy Kappa
## 0.8377068 0.3049307
# Calculate precision, recall, and F1-score using the confusion matrix
qda_predicted <- qda_model$pred$pred
qda_observed <- qda_model$pred$obs
qda_conf_matrix <- confusionMatrix(qda_predicted, qda_observed)
qda_accuracy <- sum(qda_conf_matrix$table[1,1], qda_conf_matrix$table[2,2]) / sum(qda_conf_matrix$table)
qda_precision <- qda_conf_matrix$byClass["Pos Pred Value"]
qda_recall <- qda_conf_matrix$byClass["Sensitivity"]
qda_f1_score <- 2 * (qda_precision * qda_recall) / (qda_precision + qda_recall)
# Calculate AUC
qda_roc_curve <- roc(as.numeric(qda_observed), as.numeric(qda_predicted))
## Setting levels: control = 1, case = 2
## Setting direction: controls < cases
qda_auc_value <- auc(qda_roc_curve)
# Display evaluation metrics
print(paste("Accuracy:", round(qda_accuracy, 4)))
## [1] "Accuracy: 0.8377"
print(paste("Precision:", round(qda_precision, 4)))
## [1] "Precision: 0.8789"
print(paste("Recall:", round(qda_recall, 4)))
## [1] "Recall: 0.9351"
print(paste("F1-score:", round(qda_f1_score, 4)))
## [1] "F1-score: 0.9061"
print(paste("AUC:", round(qda_auc_value, 4)))
## [1] "AUC: 0.6351"
# Plot ROC curve
plot(qda_roc_curve, main = "ROC Curve", col = "blue", lwd = 2)
text(0.95, 0, paste("AUC =", round(auc(qda_roc_curve), 2)), adj = c(1, 0), pos = 4)

# Parse out P values
log_p<-conf_matrix$overall["AccuracyPValue"]
lda_p<-lda_conf_matrix$overall["AccuracyPValue"]
qda_p<-qda_conf_matrix$overall["AccuracyPValue"]
# Create vectors for accuracy, recall, and AUC values for different models
model_accuracy <- c(accuracy, lda_accuracy, qda_accuracy)
model_recall <- c(recall, lda_recall, qda_recall)
model_auc_value <- c(auc_value, lda_auc_value, qda_auc_value)
model_pvalue <- c(log_p, lda_p, qda_p)
# Create a dataframe
comparison_table <- data.frame(Model = c("LOG", "LDA", "QDA"),
Accuracy = model_accuracy,
Recall = model_recall,
AUC_Value = model_auc_value,
P_Value = model_pvalue)
# Gives us a general look at some metrics we can compare, maybe something like this can be included in the slide deck
print(comparison_table)
## Model Accuracy Recall AUC_Value P_Value
## 1 LOG 0.8694470 0.9740260 0.6518175 0.001939485
## 2 LDA 0.8603808 0.9610390 0.6509105 0.021153476
## 3 QDA 0.8377153 0.9350649 0.6351302 0.519942645
LS0tCnRpdGxlOiAiUHJvamVjdCIKYXV0aG9yOiAiQ2hleWVubmUgQWlyaW5ndG9uLCBSYW5pIE1pc3JhIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogb3BlbmludHJvOjpsYWJfcmVwb3J0Ci0tLQoKYGBge3IgbG9hZC1wYWNrYWdlcywgbWVzc2FnZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkob3BlbmludHJvKQpgYGAKCmBgYHtyfQpkYXRhIDwtIHJlYWRfY3N2KCJ+L0Rlc2t0b3AvU2Nob29sIFBvcnRmb2xpby9VVFNBL1VuZGVyZ3JhZC9EYXRhIE1pbmluZy9Qcm9qZWN0L0hSX0FuYWx5dGljcy5jc3YiKQoKIyBkYXRhIGNsZWFuIHVwIAojIENoZWNrIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGFzZXQKc3RyKGRhdGEpCmhlYWQoZGF0YSkKCiMgQ2hlY2sgZm9yIG51bGxzIGFuZCByZWR1bmRhbnQgY29uc3RhbnQgZmllbGRzLCBjb25zdGFudCB2YXJpYWJsZXMgbWVzcyB1cCB0aGUgc2VsZWN0aW9uIHByb2Nlc3Mgc2luY2UgaXTigJlzIGJhc2VkIG9uIHZhcmlhYmlsaXR5CgojIENoZWNrIGZvciBudWxsIHZhbHVlcwpudWxsX2NvbHVtbnMgPC0gc2FwcGx5KGRhdGEsIGZ1bmN0aW9uKHgpIGFueShpcy5uYSh4KSkpCmlmIChhbnkobnVsbF9jb2x1bW5zKSkgewogIHByaW50KCJOdWxsIHZhbHVlcyBkZXRlY3RlZC4iKQogIHByaW50KCJDb2x1bW5zIHdpdGggbnVsbCB2YWx1ZXM6IikKICBwcmludChuYW1lcyhkYXRhKVtudWxsX2NvbHVtbnNdKQp9IGVsc2UgewogIHByaW50KCJObyBudWxsIHZhbHVlcyBkZXRlY3RlZC4iKQp9CgojIENoZWNrIGZvciBjb25zdGFudCBmaWVsZHMKY29uc3RhbnRfY29sdW1ucyA8LSBzYXBwbHkoZGF0YSwgZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4KSkgPT0gMSkKaWYgKGFueShjb25zdGFudF9jb2x1bW5zKSkgewogIHByaW50KCJDb25zdGFudCBmaWVsZHMgZGV0ZWN0ZWQuIikKICBwcmludCgiQ29sdW1ucyB3aXRoIGNvbnN0YW50IHZhbHVlczoiKQogIHByaW50KG5hbWVzKGRhdGEpW2NvbnN0YW50X2NvbHVtbnNdKQp9IGVsc2UgewogIHByaW50KCJObyBjb25zdGFudCBmaWVsZHMgZGV0ZWN0ZWQuIikKfQoKIyBEcm9wIGNvbnN0YW50IGZpZWxkcyBhbmQgIkVtcGxveWVlTnVtYmVyIiB2YXJpYWJsZSwgSeKAmW0gZHJvcHBpbmcgRW1wbG95ZWVOdW1iZXIgc2luY2UgdGhpcyBhbG9uZSBjYW7igJl0IGJlIGEgZ29vZCBpbmRpY2F0b3Igb2Ygd2hldGhlciBhbiBlbXBsb3llZSB3aWxsIHRlcm0gYXMgaXTigJlzIHVuaXF1ZSBmb3IgZXZlcnkgZW1wbG95ZWUgYW5kIG9ubHkgZGlmZmVyZW50aWF0ZXMgZW1wbG95ZWVzIGZyb20gb25lIGFub3RoZXIKCmRhdGEgPC0gZGF0YSAlPiUKICBzZWxlY3QoLXdoZXJlKH5sZW5ndGgodW5pcXVlKC4pKSA9PSAxKSwgLUVtcGxveWVlTnVtYmVyKQoKI1RoaXMgc3RlcCBtYXkgbm90IGJlIG5lY2Vzc2FyeSBidXQgaXQgc2hvdWxkbuKAmXQgYWZmZWN0IG1vZGVsIHBlcmZvcm1hbmNlCiMgQ29udmVydCBjaGFyYWN0ZXIgdmFyaWFibGVzIHRvIGZhY3RvcnMKZGF0YSA8LSBkYXRhICU+JQogIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGFzLmZhY3RvcikKCiMgQ2hlY2sgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YXNldApzdHIoZGF0YSkKYGBgCgpgYGB7cn0Kc2V0LnNlZWQoMSkKdGVzdF9zYW1wbGUgPC0gc2FtcGxlKDE6bnJvdyhkYXRhKSwgbnJvdyhkYXRhKS80KQp0cmFpbiA8LSBkYXRhWy10ZXN0X3NhbXBsZSwgXQp0ZXN0IDwtIGRhdGFbdGVzdF9zYW1wbGUsIF0KCmxpYnJhcnkobGVhcHMpCgpmd2QgPC0gcmVnc3Vic2V0cyhBdHRyaXRpb24gfiAuLCBkYXRhPXRyYWluLCBudm1heD0xNywgbWV0aG9kPSdmb3J3YXJkJykKZndkX3N1bSA8LSBzdW1tYXJ5KGZ3ZCkKCnBhcihtZnJvdz1jKDIsMikpCnBsb3QoZndkX3N1bSRjcCAseGxhYj0iTnVtYmVyIG9mIFZhcmlhYmxlcyAiLCB5bGFiPSJDcCIsCnR5cGU9ImIiKQpwb2ludHMod2hpY2gubWluKGZ3ZF9zdW0kY3ApLCBmd2Rfc3VtJGNwW3doaWNoLm1pbihmd2Rfc3VtJGNwKV0sIGNvbD0icmVkIiwgCiAgICAgICBjZXg9MiwgcGNoPTIwKQoKcGxvdChmd2Rfc3VtJGJpYyAseGxhYj0iTnVtYmVyIG9mIFZhcmlhYmxlcyAiLCAKeWxhYj0iQklDIix0eXBlPSJiIikKcG9pbnRzKHdoaWNoLm1pbihmd2Rfc3VtJGJpYyksIGZ3ZF9zdW0kYmljW3doaWNoLm1pbihmd2Rfc3VtJGJpYyldLCBjb2w9InJlZCIsIAogICAgICAgY2V4PTIsIHBjaD0yMCkKCnBsb3QoZndkX3N1bSRhZGpyMiAseGxhYj0iTnVtYmVyIG9mIFZhcmlhYmxlcyAiLCAKeWxhYj0iQWRqdXN0ZWQgUl4yXiIsdHlwZT0iYiIpCnBvaW50cyh3aGljaC5tYXgoZndkX3N1bSRhZGpyMiksIGZ3ZF9zdW0kYWRqcjJbd2hpY2gubWF4KGZ3ZF9zdW0kYWRqcjIpXSwgCiAgICAgICBjb2w9InJlZCIsIGNleD0yLCBwY2g9MjApCgp3aGljaC5taW4oZndkX3N1bSRjcCkKd2hpY2gubWluKGZ3ZF9zdW0kYmljKQp3aGljaC5tYXgoZndkX3N1bSRhZGpyMikKCnRlc3RfbWF0cml4IDwtIG1vZGVsLm1hdHJpeChBdHRyaXRpb25+LiwgZGF0YT10ZXN0KQoKdmFsLmVycm9ycyA8LSByZXAoTkEsMTcpCmZvcihpIGluIDE6MTcpewogIGNvZWZpIDwtIGNvZWYoZndkLGlkPWkpCiAgcHJlZCA8LSB0ZXN0X21hdHJpeFssbmFtZXMoY29lZmkpXSUqJWNvZWZpCiAgcHJlZF9iaW5hcnkgPC0gaWZlbHNlKHByZWQgPj0gMS41LCAyLCAxKQogIHZhbC5lcnJvcnNbaV0gPC0gbWVhbigoYXMubnVtZXJpYyh0ZXN0JEF0dHJpdGlvbikgLSBwcmVkX2JpbmFyeSleMikgCn0KCndoaWNoLm1pbih2YWwuZXJyb3JzKQoKcGxvdCh2YWwuZXJyb3JzLCB0eXBlPSdiJykKcG9pbnRzKHdoaWNoLm1pbih2YWwuZXJyb3JzKSwgdmFsLmVycm9yc1sxMF0sIGNvbD0ncmVkJywgcGNoPTIwLCBjZXg9MikKCgpgYGAKCmBgYHtyfQpmd2RfZnVsbCA8LSByZWdzdWJzZXRzKEF0dHJpdGlvbiB+IC4sIGRhdGE9ZGF0YSwgbnZtYXg9MTcsIG1ldGhvZD0nZm9yd2FyZCcpCmNvZWYoZndkX2Z1bGwsIDEwKQpgYGAKCmBgYHtyfQojIEdldCB0aGUgaW5kaWNlcyBvZiB0aGUgc2VsZWN0ZWQgdmFyaWFibGVzCnNlbGVjdGVkX2luZGljZXMgPC0gd2hpY2gubWluKGZ3ZF9zdW0kYmljKQoKIyBFeHRyYWN0IHRoZSBuYW1lcyBvZiB0aGUgc2VsZWN0ZWQgdmFyaWFibGVzCnNlbGVjdGVkX3ZhcmlhYmxlcyA8LSBuYW1lcyh3aGljaChmd2Rfc3VtJHdoaWNoW3NlbGVjdGVkX2luZGljZXMsIF0gIT0gMCwgYXJyLmluZCA9IFRSVUUpKQpzZWxfdmFyIDwtc2VsZWN0ZWRfdmFyaWFibGVzWy0xXQojIFByaW50IHRoZSBzZWxlY3RlZCB2YXJpYWJsZXMKcHJpbnQoc2VsX3ZhcikKCiMgSWRlbnRpZnkgdGhlIGNvbHVtbnMgdGhhdCBuZWVkIG9uZS1ob3QgZW5jb2RpbmcKY29sdW1uc190b19lbmNvZGUgPC0gYygiTWFyaXRhbFN0YXR1cyIsICJCdXNpbmVzc1RyYXZlbCIsIkpvYlJvbGUiLCJPdmVyVGltZSIpCgojIE9uZS1ob3QgZW5jb2RlIHNlbGVjdGVkIGNvbHVtbnMKZW5jb2RlZF90cmFpbiA8LSBtb2RlbC5tYXRyaXgofiAuIC0gMSwgZGF0YSA9IHRyYWluW2NvbHVtbnNfdG9fZW5jb2RlXSkKZW5jb2RlZF90ZXN0PC1tb2RlbC5tYXRyaXgofiAuIC0gMSwgZGF0YSA9IHRlc3RbY29sdW1uc190b19lbmNvZGVdKQoKIyBDb21iaW5lIGVuY29kZWQgY29sdW1ucyB3aXRoIG9yaWdpbmFsIGRhdGFzZXQKZW5jb2RlZF90cmFpbiA8LSBjYmluZCh0cmFpbiwgZW5jb2RlZF90cmFpbikKZW5jb2RlZF90ZXN0IDwtIGNiaW5kKHRlc3QsIGVuY29kZWRfdGVzdCkKCiMgRmlsdGVyIG91dCBjb2x1bW5zIGZyb20gdGhlIGZpbmFsIGRhdGFzZXQKdHJhaW5fZm5sIDwtIGVuY29kZWRfdHJhaW5bLCBjKCJBdHRyaXRpb24iLCBzZWxfdmFyKV0KdGVzdF9mbmwgPC0gZW5jb2RlZF90ZXN0WywgYygiQXR0cml0aW9uIiwgc2VsX3ZhcildCmBgYAoKYGBge3J9CmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkocFJPQykKCiNMb2cgbW9kZWwKCiMgRml0IGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgdXNpbmcgdHJhaW5fZm5sCmN2IDwtIHRyYWluQ29udHJvbChtZXRob2Q9J2N2JywgbnVtYmVyPTEwLCBzYXZlUHJlZGljdGlvbnMgPSBUKQpsb2dpc3RpY19tb2RlbCA8LSB0cmFpbihBdHRyaXRpb24gfiAuLCBkYXRhID0gdHJhaW5fZm5sLCBtZXRob2Q9J2dsbScsIGZhbWlseSA9IGJpbm9taWFsLCB0ckNvbnRyb2w9Y3YpCmxvZ2lzdGljX21vZGVsCgojIENhbGN1bGF0ZSBwcmVjaXNpb24sIHJlY2FsbCwgYW5kIEYxLXNjb3JlIHVzaW5nIHRoZSBjb25mdXNpb24gbWF0cml4CnByZWRpY3RlZCA8LSBsb2dpc3RpY19tb2RlbCRwcmVkJHByZWQKb2JzZXJ2ZWQgPC0gbG9naXN0aWNfbW9kZWwkcHJlZCRvYnMKY29uZl9tYXRyaXggPC0gY29uZnVzaW9uTWF0cml4KHByZWRpY3RlZCwgb2JzZXJ2ZWQpCgphY2N1cmFjeSA8LSBzdW0oY29uZl9tYXRyaXgkdGFibGVbMSwxXSwgY29uZl9tYXRyaXgkdGFibGVbMiwyXSkgLyBzdW0oY29uZl9tYXRyaXgkdGFibGUpCnByZWNpc2lvbiA8LSBjb25mX21hdHJpeCRieUNsYXNzWyJQb3MgUHJlZCBWYWx1ZSJdCnJlY2FsbCA8LSBjb25mX21hdHJpeCRieUNsYXNzWyJTZW5zaXRpdml0eSJdCmYxX3Njb3JlIDwtIDIgKiAocHJlY2lzaW9uICogcmVjYWxsKSAvIChwcmVjaXNpb24gKyByZWNhbGwpCgojIENhbGN1bGF0ZSBBVUMKcm9jX2N1cnZlIDwtIHJvYyhhcy5udW1lcmljKG9ic2VydmVkKSwgYXMubnVtZXJpYyhwcmVkaWN0ZWQpKQphdWNfdmFsdWUgPC0gYXVjKHJvY19jdXJ2ZSkKCiMgRGlzcGxheSBldmFsdWF0aW9uIG1ldHJpY3MKcHJpbnQocGFzdGUoIkFjY3VyYWN5OiIsIHJvdW5kKGFjY3VyYWN5LCA0KSkpCnByaW50KHBhc3RlKCJQcmVjaXNpb246Iiwgcm91bmQocHJlY2lzaW9uLCA0KSkpCnByaW50KHBhc3RlKCJSZWNhbGw6Iiwgcm91bmQocmVjYWxsLCA0KSkpCnByaW50KHBhc3RlKCJGMS1zY29yZToiLCByb3VuZChmMV9zY29yZSwgNCkpKQpwcmludChwYXN0ZSgiQVVDOiIsIHJvdW5kKGF1Y192YWx1ZSwgNCkpKQoKIyBQbG90IFJPQyBjdXJ2ZQpwbG90KHJvY19jdXJ2ZSwgbWFpbiA9ICJST0MgQ3VydmUiLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIpCnRleHQoMC45NSwgMCwgcGFzdGUoIkFVQyA9Iiwgcm91bmQoYXVjKHJvY19jdXJ2ZSksIDIpKSwgYWRqID0gYygxLCAwKSwgcG9zID0gNCkKYGBgCgpgYGB7cn0KIyNMREEKCiMgRml0IExEQSBtb2RlbCB1c2luZyB0cmFpbl9mbmwKY3YgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICdjdicsIG51bWJlciA9IDEwLCBzYXZlUHJlZGljdGlvbnMgPSBUUlVFKQpsZGFfbW9kZWwgPC0gdHJhaW4oQXR0cml0aW9uIH4gLiwgZGF0YSA9IHRyYWluX2ZubCwgbWV0aG9kID0gJ2xkYScsIHRyQ29udHJvbCA9IGN2KQpsZGFfbW9kZWwKCiMgQ2FsY3VsYXRlIHByZWNpc2lvbiwgcmVjYWxsLCBhbmQgRjEtc2NvcmUgdXNpbmcgdGhlIGNvbmZ1c2lvbiBtYXRyaXgKbGRhX3ByZWRpY3RlZCA8LSBsZGFfbW9kZWwkcHJlZCRwcmVkCmxkYV9vYnNlcnZlZCA8LSBsZGFfbW9kZWwkcHJlZCRvYnMKbGRhX2NvbmZfbWF0cml4IDwtIGNvbmZ1c2lvbk1hdHJpeChsZGFfcHJlZGljdGVkLCBsZGFfb2JzZXJ2ZWQpCgpsZGFfYWNjdXJhY3kgPC0gc3VtKGxkYV9jb25mX21hdHJpeCR0YWJsZVsxLDFdLCBsZGFfY29uZl9tYXRyaXgkdGFibGVbMiwyXSkgLyBzdW0obGRhX2NvbmZfbWF0cml4JHRhYmxlKQpsZGFfcHJlY2lzaW9uIDwtIGxkYV9jb25mX21hdHJpeCRieUNsYXNzWyJQb3MgUHJlZCBWYWx1ZSJdCmxkYV9yZWNhbGwgPC0gbGRhX2NvbmZfbWF0cml4JGJ5Q2xhc3NbIlNlbnNpdGl2aXR5Il0KbGRhX2YxX3Njb3JlIDwtIDIgKiAobGRhX3ByZWNpc2lvbiAqIGxkYV9yZWNhbGwpIC8gKGxkYV9wcmVjaXNpb24gKyBsZGFfcmVjYWxsKQoKIyBDYWxjdWxhdGUgQVVDCmxkYV9yb2NfY3VydmUgPC0gcm9jKGFzLm51bWVyaWMobGRhX29ic2VydmVkKSwgYXMubnVtZXJpYyhsZGFfcHJlZGljdGVkKSkKbGRhX2F1Y192YWx1ZSA8LSBhdWMobGRhX3JvY19jdXJ2ZSkKCiMgRGlzcGxheSBldmFsdWF0aW9uIG1ldHJpY3MKcHJpbnQocGFzdGUoIkFjY3VyYWN5OiIsIHJvdW5kKGxkYV9hY2N1cmFjeSwgNCkpKQpwcmludChwYXN0ZSgiUHJlY2lzaW9uOiIsIHJvdW5kKGxkYV9wcmVjaXNpb24sIDQpKSkKcHJpbnQocGFzdGUoIlJlY2FsbDoiLCByb3VuZChsZGFfcmVjYWxsLCA0KSkpCnByaW50KHBhc3RlKCJGMS1zY29yZToiLCByb3VuZChsZGFfZjFfc2NvcmUsIDQpKSkKcHJpbnQocGFzdGUoIkFVQzoiLCByb3VuZChsZGFfYXVjX3ZhbHVlLCA0KSkpCgojIFBsb3QgUk9DIGN1cnZlCnBsb3QobGRhX3JvY19jdXJ2ZSwgbWFpbiA9ICJST0MgQ3VydmUiLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIpCnRleHQoMC45NSwgMCwgcGFzdGUoIkFVQyA9Iiwgcm91bmQoYXVjKGxkYV9yb2NfY3VydmUpLCAyKSksIGFkaiA9IGMoMSwgMCksIHBvcyA9IDQpCmBgYAoKYGBge3J9CiNRREEKIyBGaXQgUURBIG1vZGVsIHVzaW5nIHRyYWluX2ZubApjdiA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gJ2N2JywgbnVtYmVyID0gMTAsIHNhdmVQcmVkaWN0aW9ucyA9IFRSVUUpCnFkYV9tb2RlbCA8LSB0cmFpbihBdHRyaXRpb24gfiAuLCBkYXRhID0gdHJhaW5fZm5sLCBtZXRob2QgPSAncWRhJywgdHJDb250cm9sID0gY3YpCnFkYV9tb2RlbAoKIyBDYWxjdWxhdGUgcHJlY2lzaW9uLCByZWNhbGwsIGFuZCBGMS1zY29yZSB1c2luZyB0aGUgY29uZnVzaW9uIG1hdHJpeApxZGFfcHJlZGljdGVkIDwtIHFkYV9tb2RlbCRwcmVkJHByZWQKcWRhX29ic2VydmVkIDwtIHFkYV9tb2RlbCRwcmVkJG9icwpxZGFfY29uZl9tYXRyaXggPC0gY29uZnVzaW9uTWF0cml4KHFkYV9wcmVkaWN0ZWQsIHFkYV9vYnNlcnZlZCkKCnFkYV9hY2N1cmFjeSA8LSBzdW0ocWRhX2NvbmZfbWF0cml4JHRhYmxlWzEsMV0sIHFkYV9jb25mX21hdHJpeCR0YWJsZVsyLDJdKSAvIHN1bShxZGFfY29uZl9tYXRyaXgkdGFibGUpCnFkYV9wcmVjaXNpb24gPC0gcWRhX2NvbmZfbWF0cml4JGJ5Q2xhc3NbIlBvcyBQcmVkIFZhbHVlIl0KcWRhX3JlY2FsbCA8LSBxZGFfY29uZl9tYXRyaXgkYnlDbGFzc1siU2Vuc2l0aXZpdHkiXQpxZGFfZjFfc2NvcmUgPC0gMiAqIChxZGFfcHJlY2lzaW9uICogcWRhX3JlY2FsbCkgLyAocWRhX3ByZWNpc2lvbiArIHFkYV9yZWNhbGwpCgojIENhbGN1bGF0ZSBBVUMKcWRhX3JvY19jdXJ2ZSA8LSByb2MoYXMubnVtZXJpYyhxZGFfb2JzZXJ2ZWQpLCBhcy5udW1lcmljKHFkYV9wcmVkaWN0ZWQpKQpxZGFfYXVjX3ZhbHVlIDwtIGF1YyhxZGFfcm9jX2N1cnZlKQoKIyBEaXNwbGF5IGV2YWx1YXRpb24gbWV0cmljcwpwcmludChwYXN0ZSgiQWNjdXJhY3k6Iiwgcm91bmQocWRhX2FjY3VyYWN5LCA0KSkpCnByaW50KHBhc3RlKCJQcmVjaXNpb246Iiwgcm91bmQocWRhX3ByZWNpc2lvbiwgNCkpKQpwcmludChwYXN0ZSgiUmVjYWxsOiIsIHJvdW5kKHFkYV9yZWNhbGwsIDQpKSkKcHJpbnQocGFzdGUoIkYxLXNjb3JlOiIsIHJvdW5kKHFkYV9mMV9zY29yZSwgNCkpKQpwcmludChwYXN0ZSgiQVVDOiIsIHJvdW5kKHFkYV9hdWNfdmFsdWUsIDQpKSkKCiMgUGxvdCBST0MgY3VydmUKcGxvdChxZGFfcm9jX2N1cnZlLCBtYWluID0gIlJPQyBDdXJ2ZSIsIGNvbCA9ICJibHVlIiwgbHdkID0gMikKdGV4dCgwLjk1LCAwLCBwYXN0ZSgiQVVDID0iLCByb3VuZChhdWMocWRhX3JvY19jdXJ2ZSksIDIpKSwgYWRqID0gYygxLCAwKSwgcG9zID0gNCkKYGBgCgpgYGB7cn0KIyBQYXJzZSBvdXQgUCB2YWx1ZXMKbG9nX3A8LWNvbmZfbWF0cml4JG92ZXJhbGxbIkFjY3VyYWN5UFZhbHVlIl0KbGRhX3A8LWxkYV9jb25mX21hdHJpeCRvdmVyYWxsWyJBY2N1cmFjeVBWYWx1ZSJdCnFkYV9wPC1xZGFfY29uZl9tYXRyaXgkb3ZlcmFsbFsiQWNjdXJhY3lQVmFsdWUiXQoKIyBDcmVhdGUgdmVjdG9ycyBmb3IgYWNjdXJhY3ksIHJlY2FsbCwgYW5kIEFVQyB2YWx1ZXMgZm9yIGRpZmZlcmVudCBtb2RlbHMKbW9kZWxfYWNjdXJhY3kgPC0gYyhhY2N1cmFjeSwgbGRhX2FjY3VyYWN5LCBxZGFfYWNjdXJhY3kpCm1vZGVsX3JlY2FsbCA8LSBjKHJlY2FsbCwgbGRhX3JlY2FsbCwgcWRhX3JlY2FsbCkKbW9kZWxfYXVjX3ZhbHVlIDwtIGMoYXVjX3ZhbHVlLCBsZGFfYXVjX3ZhbHVlLCBxZGFfYXVjX3ZhbHVlKQptb2RlbF9wdmFsdWUgPC0gYyhsb2dfcCwgbGRhX3AsIHFkYV9wKQoKIyBDcmVhdGUgYSBkYXRhZnJhbWUKY29tcGFyaXNvbl90YWJsZSA8LSBkYXRhLmZyYW1lKE1vZGVsID0gYygiTE9HIiwgIkxEQSIsICJRREEiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFjY3VyYWN5ID0gbW9kZWxfYWNjdXJhY3ksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZWNhbGwgPSBtb2RlbF9yZWNhbGwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBVUNfVmFsdWUgPSBtb2RlbF9hdWNfdmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQX1ZhbHVlID0gbW9kZWxfcHZhbHVlKQoKIyBHaXZlcyB1cyBhIGdlbmVyYWwgbG9vayBhdCBzb21lIG1ldHJpY3Mgd2UgY2FuIGNvbXBhcmUsIG1heWJlIHNvbWV0aGluZyBsaWtlIHRoaXMgY2FuIGJlIGluY2x1ZGVkIGluIHRoZSBzbGlkZSBkZWNrCnByaW50KGNvbXBhcmlzb25fdGFibGUpCmBgYAoK