1.0 Feature Selection Introduction
Feature selection is an important topic that requires in-depth knowledge of the problem domain. Having the right features can help the model perform better. For example, removing the highly correlated attributes can lead to a better model and improve prediction.
In this post, I will explore finding highly correlated variables, Recursive Feature Elimination, stepwise elimination, and Boruta feature selection.
1.1 Plot Histograms
First, I will plot a histogram of the variables in the Pima Indians Diabetes dataset. Various variable exhibit skew distributions and should be considered for transformations.
library("mlbench")
library("caret")
# load the data
data(PimaIndiansDiabetes)
DataExplorer::plot_histogram(PimaIndiansDiabetes)

1.2 Correlations
Below we will transform the response variable from a factor to numeric. Changing the variable allows us to see any correlations with other variables. I also created a highly correlated variable by combining glucose and mass. As expected glucose and mass are highly correlated with the combination variable HCorrelated.
library("corrplot")
package 㤼㸱corrplot㤼㸲 was built under R version 3.6.1corrplot 0.84 loaded
indians = PimaIndiansDiabetes
indians$diabetes = ifelse(indians$diabetes =="pos",1,0)
indians$HCorrelated =indians$glucose*indians$mass
cor_mx = cor(indians ,use="pairwise.complete.obs", method = "pearson")
corrplot(cor_mx, method = "color",
type = "upper", order = "original", number.cex = .7,
addCoef.col = "black", # Add coefficient of correlation
tl.col = "black", tl.srt = 90, # Text label color and rotation
# hide correlation coefficient on the principal diagonal
diag = TRUE)

NA
NA
NA
NA
2.0 Recursive Feature Elimination
2.1 Run the Feature Selection
Recursive Feature Elimination(RFE) builds models with different subsets of a dataset to identify a feature that might not be required. Caret provides a rfe function that facilitates this process.
Below we will load the Pima Indians Diabetes and fit the rfe function. Control was implemented using random forest cross-validated with kfold of 10. The final plot indicates that eight variables have an accuracy of 77.73.
# define the control using a random forest selection function
control = rfeControl(functions=rfFuncs, method="cv", number=10, repeats = 1) # method="cv" , leave out repeats to speed up or method = "repeatedcv", and leave out repeats
# run the RFE algorithm
set.seed(143)
#
# NOTE THAT THE PrimaIndians [,9] is a Factor Variable with neg pos levels (factor variable not numeric)- We choose the number of variables we want to see.
#
results = rfe(PimaIndiansDiabetes[,1:8], PimaIndiansDiabetes[,9], sizes=c(1:8), rfeControl=control,
verbose=FALSE)
# summarize the results
print(results)
Recursive feature selection
Outer resampling method: Cross-Validated (10 fold)
Resampling performance over subset size:
The top 5 variables (out of 8):
glucose, mass, age, pregnant, insulin
results
Recursive feature selection
Outer resampling method: Cross-Validated (10 fold)
Resampling performance over subset size:
The top 5 variables (out of 8):
glucose, mass, age, pregnant, insulin
2.2 List the predictors in the Order of Choice
# list the chosen features
predictors(results)
[1] "glucose" "mass" "age" "pregnant" "insulin" "pedigree" "triceps" "pressure"
2.3 Plot The Results
# plot the results
plot(results, type=c("g", "o"))

Another method relies on fitting a random forest model and identifying variable importance. In this method, variable importance can vary by model.
3.0 Variable Importance
# Another method relies on fitting a random forest model and identifying variable importance. In this method, variable importance can vary by model.
glimpse(PimaIndiansDiabetes)
Rows: 768
Columns: 9
$ pregnant [3m[38;5;246m<dbl>[39m[23m 6, 1, 8, 1, 0, 5, 3, 10, 2, 8, 4, 10, 10, 1, 5, 7, 0, 7, 1, 1, 3, 8, 7, 9, ...
$ glucose [3m[38;5;246m<dbl>[39m[23m 148, 85, 183, 89, 137, 116, 78, 115, 197, 125, 110, 168, 139, 189, 166, 100...
$ pressure [3m[38;5;246m<dbl>[39m[23m 72, 66, 64, 66, 40, 74, 50, 0, 70, 96, 92, 74, 80, 60, 72, 0, 84, 74, 30, 7...
$ triceps [3m[38;5;246m<dbl>[39m[23m 35, 29, 0, 23, 35, 0, 32, 0, 45, 0, 0, 0, 0, 23, 19, 0, 47, 0, 38, 30, 41, ...
$ insulin [3m[38;5;246m<dbl>[39m[23m 0, 0, 0, 94, 168, 0, 88, 0, 543, 0, 0, 0, 0, 846, 175, 0, 230, 0, 83, 96, 2...
$ mass [3m[38;5;246m<dbl>[39m[23m 33.6, 26.6, 23.3, 28.1, 43.1, 25.6, 31.0, 35.3, 30.5, 0.0, 37.6, 38.0, 27.1...
$ pedigree [3m[38;5;246m<dbl>[39m[23m 0.627, 0.351, 0.672, 0.167, 2.288, 0.201, 0.248, 0.134, 0.158, 0.232, 0.191...
$ age [3m[38;5;246m<dbl>[39m[23m 50, 31, 32, 21, 33, 30, 26, 29, 53, 54, 30, 34, 57, 59, 51, 32, 31, 31, 33,...
$ diabetes [3m[38;5;246m<fct>[39m[23m pos, neg, pos, neg, pos, neg, pos, neg, pos, pos, neg, pos, neg, pos, pos, ...
Data=PimaIndiansDiabetes
set.seed(143)
rPartMod = train(diabetes ~ ., data=Data, method="rpart")
rpartImp = varImp(rPartMod,10)
plot(rpartImp, top = 8, main='Variable Importance Using Glaucoma Dataset')

NA
NA
4.0 StepWise Selection
Stepwise selection is a method that allows for variables to be added or remove in either direction. The model performance is measured in AIC. Akaike information criterion (AIC) estimates the quality of each model relative to each model with the lowest AIC being the best model.
4.1a Backward Selection
The backward procedure begins with a general model that includes all variables and eliminates one variable at a time.
#glm(diabetes ~ ., data=Data, family="binomial")
step(glm(diabetes ~ ., data=Data, family="binomial"),direction="backward")
Start: AIC=741.45
diabetes ~ pregnant + glucose + pressure + triceps + insulin +
mass + pedigree + age
Df Deviance AIC
- triceps 1 723.45 739.45
- insulin 1 725.19 741.19
<none> 723.45 741.45
- age 1 725.97 741.97
- pressure 1 729.99 745.99
- pedigree 1 733.78 749.78
- pregnant 1 738.68 754.68
- mass 1 764.22 780.22
- glucose 1 838.37 854.37
Step: AIC=739.45
diabetes ~ pregnant + glucose + pressure + insulin + mass + pedigree +
age
Df Deviance AIC
<none> 723.45 739.45
- insulin 1 725.46 739.46
- age 1 725.97 739.97
- pressure 1 730.13 744.13
- pedigree 1 733.92 747.92
- pregnant 1 738.69 752.69
- mass 1 768.77 782.77
- glucose 1 840.87 854.87
Call: glm(formula = diabetes ~ pregnant + glucose + pressure + insulin +
mass + pedigree + age, family = "binomial", data = Data)
Coefficients:
(Intercept) pregnant glucose pressure insulin mass pedigree
-8.405136 0.123172 0.035112 -0.013214 -0.001157 0.090089 0.947595
age
0.014789
Degrees of Freedom: 767 Total (i.e. Null); 760 Residual
Null Deviance: 993.5
Residual Deviance: 723.5 AIC: 739.5
4.1b Forward Selection
The forward method begins with a simple model then adds suitable variable one at a time until the best model is obtained.
#glm(diabetes ~ ., data=Data, family="binomial")
step(glm(diabetes ~ ., data=Data, family="binomial"),direction="forward")
Start: AIC=741.45
diabetes ~ pregnant + glucose + pressure + triceps + insulin +
mass + pedigree + age
Call: glm(formula = diabetes ~ pregnant + glucose + pressure + triceps +
insulin + mass + pedigree + age, family = "binomial", data = Data)
Coefficients:
(Intercept) pregnant glucose pressure triceps insulin mass
-8.404696 0.123182 0.035164 -0.013296 0.000619 -0.001192 0.089701
pedigree age
0.945180 0.014869
Degrees of Freedom: 767 Total (i.e. Null); 759 Residual
Null Deviance: 993.5
Residual Deviance: 723.4 AIC: 741.4
4.1c Both Selection
The both method is the combination of backwrad and forward procedures.
#glm(diabetes ~ ., data=Data, family="binomial")
step(glm(diabetes ~ ., data=Data, family="binomial"),direction="both")
Start: AIC=741.45
diabetes ~ pregnant + glucose + pressure + triceps + insulin +
mass + pedigree + age
Df Deviance AIC
- triceps 1 723.45 739.45
- insulin 1 725.19 741.19
<none> 723.45 741.45
- age 1 725.97 741.97
- pressure 1 729.99 745.99
- pedigree 1 733.78 749.78
- pregnant 1 738.68 754.68
- mass 1 764.22 780.22
- glucose 1 838.37 854.37
Step: AIC=739.45
diabetes ~ pregnant + glucose + pressure + insulin + mass + pedigree +
age
Df Deviance AIC
<none> 723.45 739.45
- insulin 1 725.46 739.46
- age 1 725.97 739.97
+ triceps 1 723.45 741.45
- pressure 1 730.13 744.13
- pedigree 1 733.92 747.92
- pregnant 1 738.69 752.69
- mass 1 768.77 782.77
- glucose 1 840.87 854.87
Call: glm(formula = diabetes ~ pregnant + glucose + pressure + insulin +
mass + pedigree + age, family = "binomial", data = Data)
Coefficients:
(Intercept) pregnant glucose pressure insulin mass pedigree
-8.405136 0.123172 0.035112 -0.013214 -0.001157 0.090089 0.947595
age
0.014789
Degrees of Freedom: 767 Total (i.e. Null); 760 Residual
Null Deviance: 993.5
Residual Deviance: 723.5 AIC: 739.5
Seems that the model without Triceps is Best
5.0 Boruta
Boruta is a feature ranking and selection algorithm based on random forest algorithm. The advantages of using this package are the ease of variables selection and the ability to adjust variable selection.
Below I fitted the Boruta function with the dataset for evaluation.
library('Boruta')
set.seed(143)
boruta_output = Boruta(diabetes ~ ., data=na.omit(Data), doTrace=0)
5.1 Significant Variables
The significant variables can be extracted from the selection. Tentative variables are variables that can be dropped or kept.
Significant_vars = getSelectedAttributes(boruta_output, withTentative = TRUE)
Significant_vars
[1] "pregnant" "glucose" "pressure" "triceps" "insulin" "mass" "pedigree" "age"
Seems all are significant
Boruta has a method for making the selecting tentative variable for the user.
roughFixMod = TentativeRoughFix(boruta_output)
boruta_signif = getSelectedAttributes(roughFixMod)
boruta_signif
[1] "pregnant" "glucose" "pressure" "triceps" "insulin" "mass" "pedigree" "age"
The importance of variables can be shown by the below method with being the most important variable.
5.2 Importance of Variables
The importance of variables can be shown by the below method with vari being the most important variable.
# Variable Importance Scores
imps = attStats(roughFixMod)
imps2 = imps[imps$decision != 'Rejected', c('meanImp', 'decision')]
head(imps2[order(-imps2$meanImp), ],10) # descending sort
NA
NA
5.3 Plotting Importance of Variables
plot(boruta_output, cex.axis=.7, las=2, xlab="", main="Variable Importance")

6.0 Information Value and Weights of Evidence (Categorical Variables)
The Information Value can be used to judge how important a given categorical variable is in explaining the binary Y variable. It goes well with logistic regression and other classification models that can model binary variables.
Let’s try to find out how important the categorical variables are in predicting if an individual will earn >50k from the ‘adult.csv’ dataset. Just run the code below to import the dataset.
library(InformationValue)
inputData <- read.csv("./Data/adult.csv")
print(head(inputData))
Alright, let’s now find the information value for the categorical variables in the inputData.
Here is what the quantum of Information Value means:
Less than 0.02, then the predictor is not useful for modeling (separating the Goods from the Bads) 0.02 to 0.1, then the predictor has only a weak relationship. 0.1 to 0.3, then the predictor has a medium strength relationship. 0.3 or higher, then the predictor has a strong relationship. That was about IV. Then what is Weight of Evidence?
Weights of evidence can be useful to find out how important a given categorical variable is in explaining the ‘events’ (called ‘Goods’ in below table.)
WOE = ln(%good of all good/%bad of all bad)
Here is what the quantum of Information Value means:
Less than 0.02, then the predictor is not useful for modeling (separating the Goods from the Bads) 0.02 to 0.1, then the predictor has only a weak relationship. 0.1 to 0.3, then the predictor has a medium strength relationship. 0.3 or higher, then the predictor has a strong relationship. That was about IV. Then what is Weight of Evidence?
Weights of evidence can be useful to find out how important a given categorical variable is in explaining the ‘events’ (called ‘Goods’ in below table.)
# The ‘Information Value’ of the categorical variable can then be derived from the respective WOE values.
#
# IV?=?(perc good of all goods?perc bad of all bads)?*?WOE
#
# The ‘WOETable’ below given the computation in more detail.
WOETable(X=inputData[, 'workclass'], Y=inputData$income)
The total IV of a variable is the sum of IV�s of its categories.
#—————————————————————————————————————- ## TUTORIAL USING OPTIMAL BINNING () # ——————————————————————————————————————-
8.0 WOE Bimmnning
This package generates, visualizes, tabulates and deploys a supervised weight of evidence (WOE) binning of variables.
Details
This package generates, visualizes, tabulates and deploys a supervised weight of evidence (WOE) binning of variables.
The package woeBinning automates the process of binning of numeric variables and factors with respect to a dichotomous target variable. Additionally, it visualizes the realized binning solution, tabulates it and deploys it to (new) data. All functions can be used with single variables or an entire data frame.
8.1 Binning
woe.binning generates a supervised fine and coarse classing of numeric variables and factors.
woe.tree.binning generates a supervised tree-like segmentation of numeric variables and factors.
woe.binning.plot visualizes the binning solution generated and saved via woe.binning or woe.tree.binning.
woe.binning.table tabulates the binning solution generated and saved via woe.binning or woe.tree.binning.
woe.binning.deploy deploys the binning solution generated and saved via woe.binning or woe.tree.binning to (new) data.
References
Siddiqi, N. 2006: Credit Risk Scorecards: Developing and Implementing Intelligent Credit Scoring. Hoboken, New Jersey: John Wiley & Sons.
Anderson, R. 2007: The Credit Scoring Toolkit: Theory and Practice for Retail Credit Risk Management and Decision Automation. Oxford / New York: Oxford University Press.
8.2 Examples
# Load German credit data and create subset
data(germancredit)
data set 㤼㸱germancredit㤼㸲 not found
df <- germancredit[, c('creditability', 'credit.amount', 'duration.in.month',
'savings.account.and.bonds', 'purpose')]
Error: object 'germancredit' not found
LS0tDQp0aXRsZTogIkZlYXR1cmUgU2VsZWN0aW9uIEJsb2ciDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyMgIDEuMCBGZWF0dXJlIFNlbGVjdGlvbiBJbnRyb2R1Y3Rpb24NCg0KRmVhdHVyZSBzZWxlY3Rpb24gaXMgYW4gaW1wb3J0YW50IHRvcGljIHRoYXQgcmVxdWlyZXMgaW4tZGVwdGgga25vd2xlZGdlIG9mIHRoZSBwcm9ibGVtIGRvbWFpbi4gSGF2aW5nIHRoZSByaWdodCBmZWF0dXJlcyBjYW4gaGVscCB0aGUgbW9kZWwgcGVyZm9ybSBiZXR0ZXIuIEZvciBleGFtcGxlLCByZW1vdmluZyB0aGUgaGlnaGx5IGNvcnJlbGF0ZWQgYXR0cmlidXRlcyBjYW4gbGVhZCB0byBhIGJldHRlciBtb2RlbCBhbmQgaW1wcm92ZSBwcmVkaWN0aW9uLg0KDQpJbiB0aGlzIHBvc3QsIEkgd2lsbCBleHBsb3JlIGZpbmRpbmcgaGlnaGx5IGNvcnJlbGF0ZWQgdmFyaWFibGVzLCBSZWN1cnNpdmUgRmVhdHVyZSBFbGltaW5hdGlvbiwgc3RlcHdpc2UgZWxpbWluYXRpb24sIGFuZCBCb3J1dGEgZmVhdHVyZSBzZWxlY3Rpb24uDQoNCiMjIyAxLjEgUGxvdCBIaXN0b2dyYW1zDQoNCkZpcnN0LCBJIHdpbGwgcGxvdCBhIGhpc3RvZ3JhbSBvZiB0aGUgdmFyaWFibGVzIGluIHRoZSBQaW1hIEluZGlhbnMgRGlhYmV0ZXMgZGF0YXNldC4gVmFyaW91cyB2YXJpYWJsZSBleGhpYml0IHNrZXcgZGlzdHJpYnV0aW9ucyBhbmQgc2hvdWxkIGJlIGNvbnNpZGVyZWQgZm9yIHRyYW5zZm9ybWF0aW9ucy4NCg0KDQpgYGB7ciBQbG90IEhpc3RvZ3JhbXN9DQpsaWJyYXJ5KCJtbGJlbmNoIikNCmxpYnJhcnkoImNhcmV0IikNCiMgbG9hZCB0aGUgZGF0YQ0KZGF0YShQaW1hSW5kaWFuc0RpYWJldGVzKQ0KRGF0YUV4cGxvcmVyOjpwbG90X2hpc3RvZ3JhbShQaW1hSW5kaWFuc0RpYWJldGVzKQ0KYGBgDQoNCiMjIyAxLjIgQ29ycmVsYXRpb25zDQoNCkJlbG93IHdlIHdpbGwgdHJhbnNmb3JtIHRoZSByZXNwb25zZSB2YXJpYWJsZSBmcm9tIGEgZmFjdG9yIHRvIG51bWVyaWMuIENoYW5naW5nIHRoZSB2YXJpYWJsZSBhbGxvd3MgdXMgdG8gc2VlIGFueSBjb3JyZWxhdGlvbnMgd2l0aCBvdGhlciB2YXJpYWJsZXMuIEkgYWxzbyBjcmVhdGVkIGEgaGlnaGx5IGNvcnJlbGF0ZWQgdmFyaWFibGUgYnkgY29tYmluaW5nIGdsdWNvc2UgYW5kIG1hc3MuIEFzIGV4cGVjdGVkIGdsdWNvc2UgYW5kIG1hc3MgYXJlIGhpZ2hseSBjb3JyZWxhdGVkIHdpdGggdGhlIGNvbWJpbmF0aW9uIHZhcmlhYmxlIEhDb3JyZWxhdGVkLg0KDQpgYGB7ciAgQ29ycmVsYXRpb25zfQ0KDQpsaWJyYXJ5KCJjb3JycGxvdCIpDQppbmRpYW5zID0gUGltYUluZGlhbnNEaWFiZXRlcw0KaW5kaWFucyRkaWFiZXRlcyA9IGlmZWxzZShpbmRpYW5zJGRpYWJldGVzID09InBvcyIsMSwwKQ0KaW5kaWFucyRIQ29ycmVsYXRlZCA9aW5kaWFucyRnbHVjb3NlKmluZGlhbnMkbWFzcw0KDQpjb3JfbXggPSBjb3IoaW5kaWFucyAgLHVzZT0icGFpcndpc2UuY29tcGxldGUub2JzIiwgbWV0aG9kID0gInBlYXJzb24iKQ0KY29ycnBsb3QoY29yX214LCBtZXRob2QgPSAiY29sb3IiLCANCiAgICAgICAgIHR5cGUgPSAidXBwZXIiLCBvcmRlciA9ICJvcmlnaW5hbCIsIG51bWJlci5jZXggPSAuNywNCiAgICAgICAgIGFkZENvZWYuY29sID0gImJsYWNrIiwgIyBBZGQgY29lZmZpY2llbnQgb2YgY29ycmVsYXRpb24NCiAgICAgICAgIHRsLmNvbCA9ICJibGFjayIsIHRsLnNydCA9IDkwLCAjIFRleHQgbGFiZWwgY29sb3IgYW5kIHJvdGF0aW9uDQogICAgICAgICAgICAgICAgICAjIGhpZGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgb24gdGhlIHByaW5jaXBhbCBkaWFnb25hbA0KICAgICAgICAgZGlhZyA9IFRSVUUpDQoNCg0KDQoNCmBgYA0KDQojIyMgMi4wIFJlY3Vyc2l2ZSBGZWF0dXJlIEVsaW1pbmF0aW9uDQoNCiMjIyMgMi4xIFJ1biB0aGUgRmVhdHVyZSBTZWxlY3Rpb24NCg0KUmVjdXJzaXZlIEZlYXR1cmUgRWxpbWluYXRpb24oUkZFKSBidWlsZHMgbW9kZWxzIHdpdGggZGlmZmVyZW50IHN1YnNldHMgb2YgYSBkYXRhc2V0IHRvIGlkZW50aWZ5IGEgZmVhdHVyZSB0aGF0IG1pZ2h0IG5vdCBiZSByZXF1aXJlZC4gQ2FyZXQgcHJvdmlkZXMgYSByZmUgZnVuY3Rpb24gdGhhdCBmYWNpbGl0YXRlcyB0aGlzIHByb2Nlc3MuDQoNCkJlbG93IHdlIHdpbGwgbG9hZCB0aGUgUGltYSBJbmRpYW5zIERpYWJldGVzIGFuZCBmaXQgdGhlIHJmZSBmdW5jdGlvbi4gQ29udHJvbCB3YXMgaW1wbGVtZW50ZWQgdXNpbmcgcmFuZG9tIGZvcmVzdCBjcm9zcy12YWxpZGF0ZWQgd2l0aCBrZm9sZCBvZiAxMC4gVGhlIGZpbmFsIHBsb3QgaW5kaWNhdGVzIHRoYXQgZWlnaHQgdmFyaWFibGVzIGhhdmUgYW4gYWNjdXJhY3kgb2YgNzcuNzMuDQoNCmBgYHtyIFJGRX0NCiMgZGVmaW5lIHRoZSBjb250cm9sIHVzaW5nIGEgcmFuZG9tIGZvcmVzdCBzZWxlY3Rpb24gZnVuY3Rpb24NCmNvbnRyb2wgPSByZmVDb250cm9sKGZ1bmN0aW9ucz1yZkZ1bmNzLCBtZXRob2Q9ImN2IiwgbnVtYmVyPTEwLCByZXBlYXRzID0gMSkgIyBtZXRob2Q9ImN2IiAsIGxlYXZlIG91dCByZXBlYXRzIHRvIHNwZWVkIHVwIG9yIG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwgYW5kIGxlYXZlIG91dCByZXBlYXRzIA0KIyBydW4gdGhlIFJGRSBhbGdvcml0aG0NCnNldC5zZWVkKDE0MykNCiMNCiMgICBOT1RFIFRIQVQgVEhFIFByaW1hSW5kaWFucyBbLDldIGlzIGEgRmFjdG9yIFZhcmlhYmxlIHdpdGggbmVnIHBvcyBsZXZlbHMgKGZhY3RvciB2YXJpYWJsZSBub3QgbnVtZXJpYyktIFdlIGNob29zZSB0aGUgbnVtYmVyIG9mIHZhcmlhYmxlcyB3ZSB3YW50IHRvIHNlZS4NCiMNCnJlc3VsdHMgPSByZmUoUGltYUluZGlhbnNEaWFiZXRlc1ssMTo4XSwgUGltYUluZGlhbnNEaWFiZXRlc1ssOV0sIHNpemVzPWMoMTo4KSwgcmZlQ29udHJvbD1jb250cm9sLA0KICAgICAgICAgICAgICB2ZXJib3NlPUZBTFNFKQ0KIyBzdW1tYXJpemUgdGhlIHJlc3VsdHMNCnByaW50KHJlc3VsdHMpDQoNCnJlc3VsdHMNCg0KDQpgYGANCg0KDQojIyMjIDIuMiBMaXN0IHRoZSBwcmVkaWN0b3JzIGluIHRoZSBPcmRlciBvZiBDaG9pY2UNCg0KYGBge3IgTGlzdCBQcmVkaWN0b3JzfQ0KDQojIGxpc3QgdGhlIGNob3NlbiBmZWF0dXJlcw0KcHJlZGljdG9ycyhyZXN1bHRzKQ0KDQoNCmBgYA0KDQojIyMjIDIuMyBQbG90IFRoZSBSZXN1bHRzDQoNCmBgYHtyfQ0KIyBwbG90IHRoZSByZXN1bHRzDQpwbG90KHJlc3VsdHMsIHR5cGU9YygiZyIsICJvIikpDQpgYGANCg0KQW5vdGhlciBtZXRob2QgcmVsaWVzIG9uIGZpdHRpbmcgYSByYW5kb20gZm9yZXN0IG1vZGVsIGFuZCBpZGVudGlmeWluZyB2YXJpYWJsZSBpbXBvcnRhbmNlLiBJbiB0aGlzIG1ldGhvZCwgdmFyaWFibGUgaW1wb3J0YW5jZSBjYW4gdmFyeSBieSBtb2RlbC4NCg0KIyMjIDMuMCBWYXJpYWJsZSBJbXBvcnRhbmNlDQoNCmBgYHtyIFRlc3QgV2l0aCBBcnRpY2xlIERhdGF9DQojIEFub3RoZXIgbWV0aG9kIHJlbGllcyBvbiBmaXR0aW5nIGEgcmFuZG9tIGZvcmVzdCBtb2RlbCBhbmQgaWRlbnRpZnlpbmcgdmFyaWFibGUgaW1wb3J0YW5jZS4gSW4gdGhpcyBtZXRob2QsIHZhcmlhYmxlIGltcG9ydGFuY2UgY2FuIHZhcnkgYnkgbW9kZWwuDQoNCmdsaW1wc2UoUGltYUluZGlhbnNEaWFiZXRlcykNCkRhdGE9UGltYUluZGlhbnNEaWFiZXRlcw0KDQpzZXQuc2VlZCgxNDMpDQpyUGFydE1vZCA9IHRyYWluKGRpYWJldGVzIH4gLiwgZGF0YT1EYXRhLCBtZXRob2Q9InJwYXJ0IikNCnJwYXJ0SW1wID0gdmFySW1wKHJQYXJ0TW9kLDEwKQ0KcGxvdChycGFydEltcCwgdG9wID0gOCwgbWFpbj0nVmFyaWFibGUgSW1wb3J0YW5jZSBVc2luZyBQaW1hSW5kaWFucyBEYXRhc2V0JykNCg0KDQpgYGANCg0KDQojIyMgNC4wIFN0ZXBXaXNlIFNlbGVjdGlvbg0KDQpTdGVwd2lzZSBzZWxlY3Rpb24gaXMgYSBtZXRob2QgdGhhdCBhbGxvd3MgZm9yIHZhcmlhYmxlcyB0byBiZSBhZGRlZCBvciByZW1vdmUgaW4gZWl0aGVyIGRpcmVjdGlvbi4gVGhlIG1vZGVsIHBlcmZvcm1hbmNlIGlzIG1lYXN1cmVkIGluIEFJQy4gQWthaWtlIGluZm9ybWF0aW9uIGNyaXRlcmlvbiAoQUlDKSBlc3RpbWF0ZXMgdGhlIHF1YWxpdHkgb2YgZWFjaCBtb2RlbCByZWxhdGl2ZSB0byBlYWNoIG1vZGVsIHdpdGggdGhlIGxvd2VzdCBBSUMgYmVpbmcgdGhlIGJlc3QgbW9kZWwuDQoNCg0KIyMjIyA0LjFhIEJhY2t3YXJkIFNlbGVjdGlvbiANCg0KDQpUaGUgYmFja3dhcmQgcHJvY2VkdXJlIGJlZ2lucyB3aXRoIGEgZ2VuZXJhbCBtb2RlbCB0aGF0IGluY2x1ZGVzIGFsbCB2YXJpYWJsZXMgYW5kIGVsaW1pbmF0ZXMgb25lIHZhcmlhYmxlIGF0IGEgdGltZS4NCg0KYGBge3IgQmFja3dhcmR9DQoNCg0KI2dsbShkaWFiZXRlcyB+IC4sIGRhdGE9RGF0YSwgZmFtaWx5PSJiaW5vbWlhbCIpDQpzdGVwKGdsbShkaWFiZXRlcyB+IC4sIGRhdGE9RGF0YSwgZmFtaWx5PSJiaW5vbWlhbCIpLGRpcmVjdGlvbj0iYmFja3dhcmQiKQ0KDQpgYGANCg0KIyMjIyA0LjFiIEZvcndhcmQgIFNlbGVjdGlvbiANCg0KDQpUaGUgZm9yd2FyZCBtZXRob2QgYmVnaW5zIHdpdGggYSBzaW1wbGUgbW9kZWwgdGhlbiBhZGRzIHN1aXRhYmxlIHZhcmlhYmxlIG9uZSBhdCBhIHRpbWUgdW50aWwgdGhlIGJlc3QgbW9kZWwgaXMgb2J0YWluZWQuDQoNCmBgYHtyIEZvcndhcmR9DQoNCg0KI2dsbShkaWFiZXRlcyB+IC4sIGRhdGE9RGF0YSwgZmFtaWx5PSJiaW5vbWlhbCIpDQpzdGVwKGdsbShkaWFiZXRlcyB+IC4sIGRhdGE9RGF0YSwgZmFtaWx5PSJiaW5vbWlhbCIpLGRpcmVjdGlvbj0iZm9yd2FyZCIpDQoNCmBgYA0KDQojIyMjIDQuMWMgQm90aCBTZWxlY3Rpb24NCg0KDQpUaGUgYm90aCBtZXRob2QgaXMgdGhlIGNvbWJpbmF0aW9uIG9mIGJhY2t3cmFkIGFuZCBmb3J3YXJkIHByb2NlZHVyZXMuDQoNCmBgYHtyIEJvdGggfQ0KDQoNCiNnbG0oZGlhYmV0ZXMgfiAuLCBkYXRhPURhdGEsIGZhbWlseT0iYmlub21pYWwiKQ0Kc3RlcChnbG0oZGlhYmV0ZXMgfiAuLCBkYXRhPURhdGEsIGZhbWlseT0iYmlub21pYWwiKSxkaXJlY3Rpb249ImJvdGgiKQ0KDQpgYGANCg0KU2VlbXMgdGhhdCB0aGUgbW9kZWwgd2l0aG91dCBUcmljZXBzIGlzIEJlc3QgDQoNCiMjIyA1LjAgQm9ydXRhDQoNCkJvcnV0YSBpcyBhIGZlYXR1cmUgcmFua2luZyBhbmQgc2VsZWN0aW9uIGFsZ29yaXRobSBiYXNlZCBvbiByYW5kb20gZm9yZXN0IGFsZ29yaXRobS4gVGhlIGFkdmFudGFnZXMgb2YgdXNpbmcgdGhpcyBwYWNrYWdlIGFyZSB0aGUgZWFzZSBvZiB2YXJpYWJsZXMgc2VsZWN0aW9uIGFuZCB0aGUgYWJpbGl0eSB0byBhZGp1c3QgdmFyaWFibGUgc2VsZWN0aW9uLg0KDQpCZWxvdyBJIGZpdHRlZCB0aGUgQm9ydXRhIGZ1bmN0aW9uIHdpdGggdGhlIGRhdGFzZXQgZm9yIGV2YWx1YXRpb24uDQoNCmBgYHtyfQ0KbGlicmFyeSgnQm9ydXRhJykNCnNldC5zZWVkKDE0MykNCmJvcnV0YV9vdXRwdXQgPSBCb3J1dGEoZGlhYmV0ZXMgfiAuLCBkYXRhPW5hLm9taXQoRGF0YSksIGRvVHJhY2U9MCkgDQpgYGANCg0KIyMjIyA1LjEgU2lnbmlmaWNhbnQgVmFyaWFibGVzDQoNClRoZSBzaWduaWZpY2FudCB2YXJpYWJsZXMgY2FuIGJlIGV4dHJhY3RlZCBmcm9tIHRoZSBzZWxlY3Rpb24uIFRlbnRhdGl2ZSB2YXJpYWJsZXMgYXJlIHZhcmlhYmxlcyB0aGF0IGNhbiBiZSBkcm9wcGVkIG9yIGtlcHQuDQoNCg0KYGBge3IgU2lnaWZpY2FudCBWYXJpYWJsZXN9DQoNClNpZ25pZmljYW50X3ZhcnMgPSBnZXRTZWxlY3RlZEF0dHJpYnV0ZXMoYm9ydXRhX291dHB1dCwgd2l0aFRlbnRhdGl2ZSA9IFRSVUUpDQpTaWduaWZpY2FudF92YXJzDQoNCg0KDQpgYGANCg0KU2VlbXMgYWxsIGFyZSBzaWduaWZpY2FudA0KDQpCb3J1dGEgaGFzIGEgbWV0aG9kIGZvciBtYWtpbmcgdGhlIHNlbGVjdGluZyB0ZW50YXRpdmUgdmFyaWFibGUgZm9yIHRoZSB1c2VyLg0KDQpgYGB7cn0NCnJvdWdoRml4TW9kID0gVGVudGF0aXZlUm91Z2hGaXgoYm9ydXRhX291dHB1dCkNCmJvcnV0YV9zaWduaWYgPSBnZXRTZWxlY3RlZEF0dHJpYnV0ZXMocm91Z2hGaXhNb2QpDQpib3J1dGFfc2lnbmlmDQpgYGANCg0KDQpUaGUgaW1wb3J0YW5jZSBvZiB2YXJpYWJsZXMgY2FuIGJlIHNob3duIGJ5IHRoZSBiZWxvdyBtZXRob2Qgd2l0aCAgYmVpbmcgdGhlIG1vc3QgaW1wb3J0YW50IHZhcmlhYmxlLg0KDQoNCiMjIyMgNS4yIEltcG9ydGFuY2Ugb2YgVmFyaWFibGVzDQoNCg0KVGhlIGltcG9ydGFuY2Ugb2YgdmFyaWFibGVzIGNhbiBiZSBzaG93biBieSB0aGUgYmVsb3cgbWV0aG9kIHdpdGggdmFyaSBiZWluZyB0aGUgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGUuDQoNCmBgYHtyIFZhcmlhYmxlIEltcG9ydGFuY2V9DQoNCiMgVmFyaWFibGUgSW1wb3J0YW5jZSBTY29yZXMNCmltcHMgPSBhdHRTdGF0cyhyb3VnaEZpeE1vZCkNCmltcHMyID0gaW1wc1tpbXBzJGRlY2lzaW9uICE9ICdSZWplY3RlZCcsIGMoJ21lYW5JbXAnLCAnZGVjaXNpb24nKV0NCmhlYWQoaW1wczJbb3JkZXIoLWltcHMyJG1lYW5JbXApLCBdLDEwKSAgIyBkZXNjZW5kaW5nIHNvcnQNCg0KDQpgYGANCg0KDQojIyMjIDUuMyBQbG90dGluZyBJbXBvcnRhbmNlIG9mIFZhcmlhYmxlcw0KDQpgYGB7ciBQbG90dGluZ30NCg0KcGxvdChib3J1dGFfb3V0cHV0LCBjZXguYXhpcz0uNywgbGFzPTIsIHhsYWI9IiIsIG1haW49IlZhcmlhYmxlIEltcG9ydGFuY2UiKSAgDQoNCmBgYA0KDQojIyMgNi4wIEluZm9ybWF0aW9uIFZhbHVlIGFuZCBXZWlnaHRzIG9mIEV2aWRlbmNlIChDYXRlZ29yaWNhbCBWYXJpYWJsZXMpDQoNClRoZSBJbmZvcm1hdGlvbiBWYWx1ZSBjYW4gYmUgdXNlZCB0byBqdWRnZSBob3cgaW1wb3J0YW50IGEgZ2l2ZW4gY2F0ZWdvcmljYWwgdmFyaWFibGUgaXMgaW4gZXhwbGFpbmluZyB0aGUgYmluYXJ5IFkgdmFyaWFibGUuIEl0IGdvZXMgd2VsbCB3aXRoIGxvZ2lzdGljIHJlZ3Jlc3Npb24gYW5kIG90aGVyIGNsYXNzaWZpY2F0aW9uIG1vZGVscyB0aGF0IGNhbiBtb2RlbCBiaW5hcnkgdmFyaWFibGVzLg0KDQpMZXTigJlzIHRyeSB0byBmaW5kIG91dCBob3cgaW1wb3J0YW50IHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgYXJlIGluIHByZWRpY3RpbmcgaWYgYW4gaW5kaXZpZHVhbCB3aWxsIGVhcm4gPjUwayBmcm9tIHRoZSDigJhhZHVsdC5jc3bigJkgZGF0YXNldC4gSnVzdCBydW4gdGhlIGNvZGUgYmVsb3cgdG8gaW1wb3J0IHRoZSBkYXRhc2V0Lg0KDQpgYGB7cn0NCmxpYnJhcnkoSW5mb3JtYXRpb25WYWx1ZSkNCmlucHV0RGF0YSA8LSByZWFkLmNzdigiLi9EYXRhL2FkdWx0LmNzdiIpDQpwcmludChoZWFkKGlucHV0RGF0YSkpDQpgYGANCg0KQWxyaWdodCwgbGV04oCZcyBub3cgZmluZCB0aGUgaW5mb3JtYXRpb24gdmFsdWUgZm9yIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgaW4gdGhlIGlucHV0RGF0YS4NCg0KYGBge3IgSW5mb3JtYXRpb24gVmFsdWV9DQppbnB1dERhdGEkaW5jb21lPWlmZWxzZShpbnB1dERhdGEkaW5jb21lPT0iPD01MEsiLDAsMSkNCiMgQ2hvb3NlIENhdGVnb3JpY2FsIFZhcmlhYmxlcyB0byBjb21wdXRlIEluZm8gVmFsdWUuIENoYW5nZSBWYXIgbmFtZXMgDQpjYXRfdmFycyA8LSBjICgid29ya2NsYXNzIiwgImVkdWNhdGlvbiIsICJtYXJpdGFsLnN0YXR1cyIsICJvY2N1cGF0aW9uIiwgInJlbGF0aW9uc2hpcCIsICJyYWNlIiwgImdlbmRlciIsICJuYXRpdmUuY291bnRyeSIpICAjIGdldCBhbGwgY2F0ZWdvcmljYWwgdmFyaWFibGVzDQoNCiMgSW5pdCBPdXRwdXQNCmRmX2l2IDwtIGRhdGEuZnJhbWUoVkFSUz1jYXRfdmFycywgSVY9bnVtZXJpYyhsZW5ndGgoY2F0X3ZhcnMpKSwgU1RSRU5HVEg9Y2hhcmFjdGVyKGxlbmd0aChjYXRfdmFycykpLCBzdHJpbmdzQXNGYWN0b3JzID0gRikgICMgaW5pdCBvdXRwdXQgZGF0YWZyYW1lDQoNCg0KDQoNCiMgR2V0IEluZm9ybWF0aW9uIFZhbHVlIGZvciBlYWNoIHZhcmlhYmxlDQpmb3IgKGZhY3Rvcl92YXIgaW4gY2F0X3ZhcnMpew0KICBkZl9pdltkZl9pdiRWQVJTID09IGZhY3Rvcl92YXIsICJJViJdIDwtIEluZm9ybWF0aW9uVmFsdWU6OklWKFg9aW5wdXREYXRhWywgZmFjdG9yX3Zhcl0sIFk9aW5wdXREYXRhJGluY29tZSkNCiAgZGZfaXZbZGZfaXYkVkFSUyA9PSBmYWN0b3JfdmFyLCAiU1RSRU5HVEgiXSA8LSBhdHRyKEluZm9ybWF0aW9uVmFsdWU6OklWKFg9aW5wdXREYXRhWywgZmFjdG9yX3Zhcl0sIFk9aW5wdXREYXRhJGluY29tZSksICJob3dnb29kIikNCn0NCg0KIyBTb3J0DQpkZl9pdiA8LSBkZl9pdltvcmRlcigtZGZfaXYkSVYpLCBdDQoNCmRmX2l2DQoNCg0KDQoNCmBgYA0KDQpIZXJlIGlzIHdoYXQgdGhlIHF1YW50dW0gb2YgSW5mb3JtYXRpb24gVmFsdWUgbWVhbnM6DQoNCkxlc3MgdGhhbiAwLjAyLCB0aGVuIHRoZSBwcmVkaWN0b3IgaXMgbm90IHVzZWZ1bCBmb3IgbW9kZWxpbmcgKHNlcGFyYXRpbmcgdGhlIEdvb2RzIGZyb20gdGhlIEJhZHMpDQowLjAyIHRvIDAuMSwgdGhlbiB0aGUgcHJlZGljdG9yIGhhcyBvbmx5IGEgd2VhayByZWxhdGlvbnNoaXAuDQowLjEgdG8gMC4zLCB0aGVuIHRoZSBwcmVkaWN0b3IgaGFzIGEgbWVkaXVtIHN0cmVuZ3RoIHJlbGF0aW9uc2hpcC4NCjAuMyBvciBoaWdoZXIsIHRoZW4gdGhlIHByZWRpY3RvciBoYXMgYSBzdHJvbmcgcmVsYXRpb25zaGlwLg0KVGhhdCB3YXMgYWJvdXQgSVYuIFRoZW4gd2hhdCBpcyBXZWlnaHQgb2YgRXZpZGVuY2U/DQoNCldlaWdodHMgb2YgZXZpZGVuY2UgY2FuIGJlIHVzZWZ1bCB0byBmaW5kIG91dCBob3cgaW1wb3J0YW50IGEgZ2l2ZW4gY2F0ZWdvcmljYWwgdmFyaWFibGUgaXMgaW4gZXhwbGFpbmluZyB0aGUg4oCYZXZlbnRz4oCZIChjYWxsZWQg4oCYR29vZHPigJkgaW4gYmVsb3cgdGFibGUuKQ0KDQpXT0UgPSBsbiglZ29vZCBvZiBhbGwgZ29vZC8lYmFkIG9mIGFsbCBiYWQpDQoNCkhlcmUgaXMgd2hhdCB0aGUgcXVhbnR1bSBvZiBJbmZvcm1hdGlvbiBWYWx1ZSBtZWFuczoNCg0KTGVzcyB0aGFuIDAuMDIsIHRoZW4gdGhlIHByZWRpY3RvciBpcyBub3QgdXNlZnVsIGZvciBtb2RlbGluZyAoc2VwYXJhdGluZyB0aGUgR29vZHMgZnJvbSB0aGUgQmFkcykNCjAuMDIgdG8gMC4xLCB0aGVuIHRoZSBwcmVkaWN0b3IgaGFzIG9ubHkgYSB3ZWFrIHJlbGF0aW9uc2hpcC4NCjAuMSB0byAwLjMsIHRoZW4gdGhlIHByZWRpY3RvciBoYXMgYSBtZWRpdW0gc3RyZW5ndGggcmVsYXRpb25zaGlwLg0KMC4zIG9yIGhpZ2hlciwgdGhlbiB0aGUgcHJlZGljdG9yIGhhcyBhIHN0cm9uZyByZWxhdGlvbnNoaXAuDQpUaGF0IHdhcyBhYm91dCBJVi4gVGhlbiB3aGF0IGlzIFdlaWdodCBvZiBFdmlkZW5jZT8NCg0KV2VpZ2h0cyBvZiBldmlkZW5jZSBjYW4gYmUgdXNlZnVsIHRvIGZpbmQgb3V0IGhvdyBpbXBvcnRhbnQgYSBnaXZlbiBjYXRlZ29yaWNhbCB2YXJpYWJsZSBpcyBpbiBleHBsYWluaW5nIHRoZSDigJhldmVudHPigJkgKGNhbGxlZCDigJhHb29kc+KAmSBpbiBiZWxvdyB0YWJsZS4pDQoNCg0KYGBge3J9DQojIFRoZSDigJhJbmZvcm1hdGlvbiBWYWx1ZeKAmSBvZiB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGUgY2FuIHRoZW4gYmUgZGVyaXZlZCBmcm9tIHRoZSByZXNwZWN0aXZlIFdPRSB2YWx1ZXMuDQojIA0KIyBJVj89PyhwZXJjIGdvb2Qgb2YgYWxsIGdvb2RzP3BlcmMgYmFkIG9mIGFsbCBiYWRzKT8qP1dPRQ0KIyANCiMgVGhlIOKAmFdPRVRhYmxl4oCZIGJlbG93IGdpdmVuIHRoZSBjb21wdXRhdGlvbiBpbiBtb3JlIGRldGFpbC4NCg0KV09FVGFibGUoWD1pbnB1dERhdGFbLCAnd29ya2NsYXNzJ10sIFk9aW5wdXREYXRhJGluY29tZSkNCmBgYA0KDQpUaGUgdG90YWwgSVYgb2YgYSB2YXJpYWJsZSBpcyB0aGUgc3VtIG9mIElW77+9cyBvZiBpdHMgY2F0ZWdvcmllcy4NCg0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIyAgICAgICAgICAgICAgICAgICAgICAgICBUVVRPUklBTCBVU0lORyBPUFRJTUFMIEJJTk5JTkcgKCkNCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgNy4wIFdPRSBGUk9NIEluZm9ybWF0aW9uIFBjYWthZ2UNCg0KIyMjIyA3LjEgV29lIENvbnRpbnVvdXMgVmFyaWFibGVzIGFuZCBGYWN0b3IgVmFyaWFibGVzDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoIkluZm9ybWF0aW9uIikNCmxpYnJhcnkoSW5mb3JtYXRpb24pDQoNCiMgbXlkYXRhIDwtIHJlYWQuY3N2KCJodHRwczovL3N0YXRzLmlkcmUudWNsYS5lZHUvc3RhdC9kYXRhL2JpbmFyeS5jc3YiKQ0KIyANCiMgaGVhZChteWRhdGEpDQoNCiMgU3RvcmUgRGF0YSBMb2NhbGx5DQojDQojd3JpdGUuY3N2KG15ZGF0YSwiLi9EYXRhL215ZGF0YS5jc3YiKQ0KIw0KIyBSZWFkIHRoZSBkYXRhIGluIGxvY2FsbHkNCg0KDQpteWRhdGE9cmVhZC5jc3YoIi4vRGF0YS9teWRhdGEuY3N2IikNCg0Kc3VtbWFyeShteWRhdGEpDQoNCg0KDQpgYGANCg0KDQojIyMjIDcuMiBNYWtlIGFuICBpbmRlcGVuZGVudCBWYXJpYWJsZSBhIEZhY3Rvcg0KDQpJdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSBoZXJlIHRoZSBudW1iZXIgb2YgYmlucyBmb3IgJ3JhbmsnIHZhcmlhYmxlLiBTaW5jZSBpdCBpcyBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlLCB0aGUgbnVtYmVyIG9mIGJpbnMgd291bGQgYmUgYWNjb3JkaW5nIHRvIHVuaXF1ZSB2YWx1ZXMgb2YgdGhlIGZhY3RvciB2YXJpYWJsZS4gVGhlIHBhcmFtZXRlciBiaW5zPTEwIGRvZXMgbm90IHdvcmsgZm9yIGEgZmFjdG9yIHZhcmlhYmxlLg0KDQpgYGB7cn0NCg0KbXlkYXRhJHJhbms9ZmFjdG9yKG15ZGF0YSRyYW5rKQ0KDQpgYGANCg0KIyMjIyA3LjMgQ29tcHV0ZSBUaGUgSW5mbyBWYWx1ZQ0KDQpgYGB7ciBJViB9DQoNCiMgTXVzdCBQdXQgSW4gYW4gZXN0aW1hdGUgb2YgdGhlIEJpbnMgDQoNCklWIDwtIGNyZWF0ZV9pbmZvdGFibGVzKGRhdGE9bXlkYXRhLCB5PSJhZG1pdCIsIGJpbnM9MTAsIHBhcmFsbGVsPUZBTFNFKQ0KDQoNCklWIDwtIGNyZWF0ZV9pbmZvdGFibGVzKGRhdGE9bXlkYXRhLCB5PSJhZG1pdCIsIGJpbnM9MTAsIHBhcmFsbGVsPVRSVUUpDQoNCg0KIyBQcm9kdWNlIGFuIElWIFZhbHVlIEZyYW1lDQoNCklWX1ZhbHVlID0gZGF0YS5mcmFtZShJViRTdW1tYXJ5KQ0KDQpJVl9WYWx1ZQ0KDQoNCg0KYGBgDQoNCiMjIyMgNy40IFByaW50IHRoZSBJViBUYWJsZXMNCg0KVG8gZ2V0IFdPRSB0YWJsZSBmb3IgdmFyaWFibGUgZ3JlLCB5b3UgbmVlZCB0byBjYWxsIFRhYmxlcyBsaXN0IGZyb20gSVYgbGlzdC4NCg0KV2UgY2FuIGRvIHRoaXMgZm9yIGFueSBvZiB0aGUgdmFyaWFibGVzDQoNCmBgYHtyfQ0KcHJpbnQoSVYkVGFibGVzJGdyZSwgcm93Lm5hbWVzPUZBTFNFKQ0KcHJpbnQoSVYkVGFibGVzJGdwYSwgcm93Lm5hbWVzPUZBTFNFKQ0KcHJpbnQoSVYkVGFibGVzJHJhbmssIHJvdy5uYW1lcz1GQUxTRSkNCnByaW50KElWJFRhYmxlcyRYLCByb3cubmFtZXM9RkFMU0UpDQpgYGANCg0KIyMjIyA3LjUgUHV0IFRoZSBUYWJsZXMgaWYgRGF0YWZyYW1lcw0KDQpUbyBnZXQgV09FIHRhYmxlIGZvciB2YXJpYWJsZSBncmUsIHlvdSBuZWVkIHRvIGNhbGwgVGFibGVzIGxpc3QgZnJvbSBJViBsaXN0Lg0KDQpXZSBjYW4gZG8gdGhpcyBmb3IgYW55IG9mIHRoZSB2YXJpYWJsZXMNCg0KYGBge3IgRGF0YUZyYW1lc30NCmdyZSA9IGRhdGEuZnJhbWUoSVYkVGFibGVzJGdyZSkNCg0KZ3BhID0gZGF0YS5mcmFtZShJViRUYWJsZXMkZ3BhKQ0KDQpYID0gZGF0YS5mcmFtZShJViRUYWJsZXMkWCkNCg0KcmFuayA9IGRhdGEuZnJhbWUoSVYkVGFibGVzJHJhbmspDQoNCg0KYGBgDQoNCg0KIyMjIyA3LjYgUGxvdCBXb2UgU2NvcmVzIEZvciAxIFZhcmlhYmxlIA0KDQpXZSBjYW4gcGxvdCAxIGF0IGEgdGltZQ0KDQpgYGB7ciAgUGxvdCAxIH0NCg0KcGxvdF9pbmZvdGFibGVzKElWLCAiZ3JlIikNCg0KYGBgDQoNCg0KIyMjIyA3LjcgUGxvdCBXb2UgU2NvcmVzIEZvciBNYW55IFZhcmlhYmxlIA0KDQpXZSBjYW4gcGxvdCBtYW55IGF0IGEgdGltZQ0KDQpgYGB7ciBQbG90IE1hbnkgfQ0KDQpwbG90X2luZm90YWJsZXMoSVYsIElWJFN1bW1hcnkkVmFyaWFibGVbMTo0XSwgc2FtZV9zY2FsZT1GQUxTRSkNCg0KYGBgDQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMgOC4wICBXT0UgQmltbW5uaW5nDQojDQpUaGlzIHBhY2thZ2UgZ2VuZXJhdGVzLCB2aXN1YWxpemVzLCB0YWJ1bGF0ZXMgYW5kIGRlcGxveXMgYSBzdXBlcnZpc2VkIHdlaWdodCBvZiBldmlkZW5jZSAoV09FKSBiaW5uaW5nIG9mIHZhcmlhYmxlcy4NCg0KRGV0YWlscw0KDQpUaGlzIHBhY2thZ2UgZ2VuZXJhdGVzLCB2aXN1YWxpemVzLCB0YWJ1bGF0ZXMgYW5kIGRlcGxveXMgYSBzdXBlcnZpc2VkIHdlaWdodCBvZiBldmlkZW5jZSAoV09FKSBiaW5uaW5nIG9mIHZhcmlhYmxlcy4NCg0KVGhlIHBhY2thZ2Ugd29lQmlubmluZyBhdXRvbWF0ZXMgdGhlIHByb2Nlc3Mgb2YgYmlubmluZyBvZiBudW1lcmljIHZhcmlhYmxlcyBhbmQgZmFjdG9ycyB3aXRoIHJlc3BlY3QgdG8gYSBkaWNob3RvbW91cyB0YXJnZXQgdmFyaWFibGUuIEFkZGl0aW9uYWxseSwgaXQgdmlzdWFsaXplcyB0aGUgcmVhbGl6ZWQgYmlubmluZyBzb2x1dGlvbiwgdGFidWxhdGVzIGl0IGFuZCBkZXBsb3lzIGl0IHRvIChuZXcpIGRhdGEuIEFsbCBmdW5jdGlvbnMgY2FuIGJlIHVzZWQgd2l0aCBzaW5nbGUgdmFyaWFibGVzIG9yIGFuIGVudGlyZSBkYXRhIGZyYW1lLg0KDQoNCiMjIyA4LjEgQmlubmluZw0KDQp3b2UuYmlubmluZyBnZW5lcmF0ZXMgYSBzdXBlcnZpc2VkIGZpbmUgYW5kIGNvYXJzZSBjbGFzc2luZyBvZiBudW1lcmljIHZhcmlhYmxlcyBhbmQgZmFjdG9ycy4NCg0Kd29lLnRyZWUuYmlubmluZyBnZW5lcmF0ZXMgYSBzdXBlcnZpc2VkIHRyZWUtbGlrZSBzZWdtZW50YXRpb24gb2YgbnVtZXJpYyB2YXJpYWJsZXMgYW5kIGZhY3RvcnMuDQoNCndvZS5iaW5uaW5nLnBsb3QgdmlzdWFsaXplcyB0aGUgYmlubmluZyBzb2x1dGlvbiBnZW5lcmF0ZWQgYW5kIHNhdmVkIHZpYSB3b2UuYmlubmluZyBvciB3b2UudHJlZS5iaW5uaW5nLg0KDQp3b2UuYmlubmluZy50YWJsZSB0YWJ1bGF0ZXMgdGhlIGJpbm5pbmcgc29sdXRpb24gZ2VuZXJhdGVkIGFuZCBzYXZlZCB2aWEgd29lLmJpbm5pbmcgb3Igd29lLnRyZWUuYmlubmluZy4NCg0Kd29lLmJpbm5pbmcuZGVwbG95IGRlcGxveXMgdGhlIGJpbm5pbmcgc29sdXRpb24gZ2VuZXJhdGVkIGFuZCBzYXZlZCB2aWEgd29lLmJpbm5pbmcgb3Igd29lLnRyZWUuYmlubmluZyB0byAobmV3KSBkYXRhLg0KDQpSZWZlcmVuY2VzIA0KDQpTaWRkaXFpLCBOLiAyMDA2OiBDcmVkaXQgUmlzayBTY29yZWNhcmRzOiBEZXZlbG9waW5nIGFuZCBJbXBsZW1lbnRpbmcgSW50ZWxsaWdlbnQgQ3JlZGl0IFNjb3JpbmcuIEhvYm9rZW4sIE5ldyBKZXJzZXk6IEpvaG4gV2lsZXkgJiBTb25zLg0KDQpBbmRlcnNvbiwgUi4gMjAwNzogVGhlIENyZWRpdCBTY29yaW5nIFRvb2xraXQ6IFRoZW9yeSBhbmQgUHJhY3RpY2UgZm9yIFJldGFpbCBDcmVkaXQgUmlzayBNYW5hZ2VtZW50IGFuZCBEZWNpc2lvbiBBdXRvbWF0aW9uLiBPeGZvcmQgLyBOZXcgWW9yazogT3hmb3JkIFVuaXZlcnNpdHkgUHJlc3MuDQoNCg0KIyMjIDguMiBFeGFtcGxlcw0KDQpgYGB7cn0NCg0KIyBMb2FkIEdlcm1hbiBjcmVkaXQgZGF0YSBhbmQgY3JlYXRlIHN1YnNldA0KZGF0YShnZXJtYW5jcmVkaXQpDQpkZiA8LSBnZXJtYW5jcmVkaXRbLCBjKCdjcmVkaXRhYmlsaXR5JywgJ2NyZWRpdC5hbW91bnQnLCAnZHVyYXRpb24uaW4ubW9udGgnLA0KICAgICAgICAgICAgICAgICAgJ3NhdmluZ3MuYWNjb3VudC5hbmQuYm9uZHMnLCAncHVycG9zZScpXQ0KDQojIEJpbiBhbGwgdmFyaWFibGVzIG9mIHRoZSBkYXRhIGZyYW1lIChhcGFydCBmcm9tIHRoZSB0YXJnZXQgdmFyaWFibGUpDQojIHdpdGggZGVmYXVsdCBwYXJhbWV0ZXIgc2V0dGluZ3MNCmJpbm5pbmcgPC0gd29lLmJpbm5pbmcoZGYsICdjcmVkaXRhYmlsaXR5JywgZGYpDQoNCiMgUGxvdCB0aGUgYmlubmVkIHZhcmlhYmxlcw0Kd29lLmJpbm5pbmcucGxvdChiaW5uaW5nKQ0KDQojIFRhYnVsYXRlIHRoZSBiaW5uZWQgdmFyaWFibGVzDQp0YWJ1bGF0ZS5iaW5uaW5nIDwtIHdvZS5iaW5uaW5nLnRhYmxlKGJpbm5pbmcpDQp0YWJ1bGF0ZS5iaW5uaW5nDQoJCQkJCQkgIA0KIyBEZXBsb3kgdGhlIGJpbm5pbmcgc29sdXRpb24gdG8gdGhlIGRhdGEgZnJhbWUNCiMgKGkuZS4gYWRkIGJpbm5lZCB2YXJpYWJsZXMgYW5kIGNvcnJlc3BvbmRpbmcgV09FIHZhcmlhYmxlcykNCmRmLndpdGguYmlubmVkLnZhcnMuYWRkZWQgPC0gd29lLmJpbm5pbmcuZGVwbG95KGRmLCBiaW5uaW5nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZGQud29lLm9yLmR1bS52YXI9J3dvZScpDQoNCg0KDQoNCmBgYA0KDQoNCg0KDQoNCg0KDQo=