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")
package 㤼㸱mlbench㤼㸲 was built under R version 3.6.3
library("caret")
package 㤼㸱caret㤼㸲 was built under R version 3.6.2Loading required package: lattice

Attaching package: 㤼㸱caret㤼㸲

The following object is masked from 㤼㸱package:purrr㤼㸲:

    lift
# load the data
data(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

glimpse(PimaIndiansDiabetes)
Rows: 768
Columns: 9
$ pregnant <dbl> 6, 1, 8, 1, 0, 5, 3, 10, 2, 8, 4, 10, 10, 1, 5, 7, 0, 7, 1, 1, 3, 8, 7, 9, 11, 10, 7...
$ glucose  <dbl> 148, 85, 183, 89, 137, 116, 78, 115, 197, 125, 110, 168, 139, 189, 166, 100, 118, 10...
$ pressure <dbl> 72, 66, 64, 66, 40, 74, 50, 0, 70, 96, 92, 74, 80, 60, 72, 0, 84, 74, 30, 70, 88, 84...
$ triceps  <dbl> 35, 29, 0, 23, 35, 0, 32, 0, 45, 0, 0, 0, 0, 23, 19, 0, 47, 0, 38, 30, 41, 0, 0, 35,...
$ insulin  <dbl> 0, 0, 0, 94, 168, 0, 88, 0, 543, 0, 0, 0, 0, 846, 175, 0, 230, 0, 83, 96, 235, 0, 0,...
$ mass     <dbl> 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, 30.1, 2...
$ pedigree <dbl> 0.627, 0.351, 0.672, 0.167, 2.288, 0.201, 0.248, 0.134, 0.158, 0.232, 0.191, 0.537, ...
$ age      <dbl> 50, 31, 32, 21, 33, 30, 26, 29, 53, 54, 30, 34, 57, 59, 51, 32, 31, 31, 33, 32, 27, ...
$ diabetes <fct> pos, neg, pos, neg, pos, neg, pos, neg, pos, pos, neg, pos, neg, pos, pos, pos, pos,...
Data=PimaIndiansDiabetes

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.

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 () # ——————————————————————————————————————-

7.0 WOE FROM Information Pcakage

7.1 Woe Continuous Variables and Factor Variables

summary(mydata)
       X             admit             gre             gpa        rank   
 Min.   :  1.0   Min.   :0.0000   Min.   :220.0   Min.   :2.260   1: 61  
 1st Qu.:100.8   1st Qu.:0.0000   1st Qu.:520.0   1st Qu.:3.130   2:151  
 Median :200.5   Median :0.0000   Median :580.0   Median :3.395   3:121  
 Mean   :200.5   Mean   :0.3175   Mean   :587.7   Mean   :3.390   4: 67  
 3rd Qu.:300.2   3rd Qu.:1.0000   3rd Qu.:660.0   3rd Qu.:3.670          
 Max.   :400.0   Max.   :1.0000   Max.   :800.0   Max.   :4.000          

7.2 Make an independent Variable a Factor

It is important to note here the number of bins for ‘rank’ variable. Since it is a categorical variable, the number of bins would be according to unique values of the factor variable. The parameter bins=10 does not work for a factor variable.


mydata$rank=factor(mydata$rank)

7.3 Compute The Info Value

7.5 Put The Tables if Dataframes

To get WOE table for variable gre, you need to call Tables list from IV list.

We can do this for any of the variables

gre = data.frame(IV$Tables$gre)

gpa = data.frame(IV$Tables$gpa)

X = data.frame(IV$Tables$X)

rank = data.frame(IV$Tables$rank)

7.6 Plot Woe Scores For 1 Variable

We can plot 1 at a time


plot_infotables(IV, "gre")

7.7 Plot Woe Scores For Many Variable

We can plot many at a time


plot_infotables(IV, IV$Summary$Variable[1:4], same_scale=FALSE)

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.1.1 Binning of Numeric Variables

Numeric variables (continuous and ordinal) are binned by merging initial classes with similar frequencies. The number of initial bins results from the min.perc.total parameter: min.perc.total will result in trunc(1/min.perc.total) initial bins, whereby trunc is needed to guarantee bins with similar frequencies. For example min.perc.total=0.07 will cause trunc(14.3)=14 initial classes. Next, if min.perc.class>0, bins with sparse target classes will be merged with the next upper bin, and in case of the last bin with the next lower one. NAs have their own bin and will not be merged with others. Finally nearby bins with most similar weight of evidence (WOE) values are joined step by step until the information value (IV) decreases more than specified by a percentage value (stop.limit parameter) or until two bins are reached.

8.1.2 Binning of Factors

Factors (categorical variables) are binned by merging factor levels. As a start sparse levels (defined via the min.perc.total and min.perc.class parameters) are merged to a ‘miscellaneous’ level: if possible, respective levels (including sparse NAs) are bundled as ‘misc. level pos.’ (associated with positive WOE values), respectively as ‘misc. level neg.’ (associated with negative WOE values). In case a misc. level contains only NAs it will be named ‘Missing’. Afterwards levels with similar WOE values are joined step by step until the information value (IV) decreases more than specified by a percentage value (stop.limit parameter) or until two bins are reached.

8.1.3 Adjustment of 0 Frequencies

In case the crosstab of the bins with the target classes contains frequencies = 0 the column percentages are adjusted to be able to compute the WOE and IV values: the offset 0.0001 (=0.01%) is added to each column percentage cell and the column percentages are recomputed then. This allows considering bins associated with one target class only, but may cause extreme WOE values for these bins. If a correction is not appropriate choose min.perc.class>0; bins with sparse target classes will be merged then before computing any WOE or IV value.

8.1.4 See Also

Other binning functions: woe.tree.binning

8.1.5 Binning Duration on Tree

woe.tree.binning generates a supervised tree-like segmentation of numeric variables and factors with respect to a dichotomous target variable. Its parameters provide flexibility in finding a binning that fits specific data characteristics and practical needs.

https://rdrr.io/cran/woeBinning/man/woe.binning.html # Shift Click to open link

8.2 Examples

9.0 Post Processing on The WoE Above

9.1 Correlation Between Categporical and Numeric Variables

The WoE is now a continuous unit so we can tell the correlation between contuous and categorical Units

We conduct Correlation using the data frame compiled in the WoE Binning Above

9.1a Lets glimpse the DF and get a Sample
glimpse(df.with.binned.vars.added)
Rows: 1,000
Columns: 13
$ creditability                        <fct> good, bad, good, good, bad, good, good, good, good, bad,...
$ credit.amount                        <dbl> 1169, 5951, 2096, 7882, 4870, 9055, 2835, 6948, 3059, 52...
$ duration.in.month                    <dbl> 6, 48, 12, 42, 24, 36, 24, 36, 12, 30, 12, 48, 12, 24, 1...
$ savings.account.and.bonds            <fct> unknown/ no savings account, ... < 100 DM, ... < 100 DM,...
$ purpose                              <fct> radio/television, radio/television, education, furniture...
$ duration.in.month.binned             <fct> "(-Inf,6]", "(30, Inf]", "(6,15]", "(30, Inf]", "(15,30]...
$ woe.duration.in.month.binned         <dbl> -124.59370, 76.63288, -36.53869, 76.63288, 10.83411, 76....
$ savings.account.and.bonds.binned     <fct> unknown/ no savings account + 500 <= ... < 1000 DM, ... ...
$ woe.savings.account.and.bonds.binned <dbl> -70.47080, 25.24534, 25.24534, 25.24534, 25.24534, -70.4...
$ purpose.binned                       <fct> radio/television + car (used), radio/television + car (u...
$ woe.purpose.binned                   <dbl> -50.02820, -50.02820, 27.99201, 27.99201, 27.99201, 27.9...
$ credit.amount.binned                 <fct> "(-Inf,5969.95]", "(-Inf,5969.95]", "(-Inf,5969.95]", "(...
$ woe.credit.amount.binned             <dbl> -13.83897, -13.83897, -13.83897, 44.18328, -13.83897, 44...
library("corrplot")

library(dplyr)



df=df.with.binned.vars.added%>%select(creditability,contains("woe"))

df$creditability=ifelse(df$creditability=="bad",1,0)




cor_mat_df = cor(df  ,use="pairwise.complete.obs", method = "pearson")
corrplot(cor_mat_df, 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

Result is by correlation with 1/0 it seems that there is an order of importance , but lets check this by variable importance and 2 ways, Via WOE and By Factors , ie the binning

9.2 Variable importance By Woe

We will use RFE to gauge variable importance of the above frame


library('Boruta')
set.seed(143)
boruta_output_woe = Boruta(creditability ~ ., data=na.omit(df), doTrace=0) 

Significant_vars = getSelectedAttributes(boruta_output_woe, withTentative = TRUE)
Significant_vars
[1] "woe.duration.in.month.binned"         "woe.savings.account.and.bonds.binned"
[3] "woe.purpose.binned"                   "woe.credit.amount.binned"            
# Neede Roughfix conversionj
roughFixMod <- TentativeRoughFix(boruta_output_woe)
There are no Tentative attributes! Returning original object.
boruta_signif <- getSelectedAttributes(roughFixMod)
print(boruta_signif)
[1] "woe.duration.in.month.binned"         "woe.savings.account.and.bonds.binned"
[3] "woe.purpose.binned"                   "woe.credit.amount.binned"            
plot(boruta_output_woe, cex.axis=.7, las=2, xlab="", main="Variable Importance Woe")  




imps = attStats(roughFixMod)
imps2 = imps[imps$decision != 'Rejected', c('meanImp', 'decision')]
head(imps2[order(-imps2$meanImp), ],10)  # descending sort

# Agrees with Correlation

#
# Now we look at the binning bFactors

df_factors=df.with.binned.vars.added%>%select(creditability,contains("binned"))%>%select_if(is.factor)

glimpse(df_factors)
Rows: 1,000
Columns: 5
$ creditability                    <fct> good, bad, good, good, bad, good, good, good, good, bad, bad...
$ duration.in.month.binned         <fct> "(-Inf,6]", "(30, Inf]", "(6,15]", "(30, Inf]", "(15,30]", "...
$ savings.account.and.bonds.binned <fct> unknown/ no savings account + 500 <= ... < 1000 DM, ... < 10...
$ purpose.binned                   <fct> radio/television + car (used), radio/television + car (used)...
$ credit.amount.binned             <fct> "(-Inf,5969.95]", "(-Inf,5969.95]", "(-Inf,5969.95]", "(5969...
set.seed(143)
boruta_output_binned = Boruta(creditability ~ ., data=na.omit(df_factors), doTrace=0) 

Significant_vars = getSelectedAttributes(boruta_output_binned, withTentative = TRUE)
Significant_vars
[1] "duration.in.month.binned"         "savings.account.and.bonds.binned"
[3] "purpose.binned"                   "credit.amount.binned"            
# Neede Roughfix conversionj
roughFixMod <- TentativeRoughFix(boruta_output_binned)
There are no Tentative attributes! Returning original object.
boruta_signif <- getSelectedAttributes(roughFixMod)
print(boruta_signif)
[1] "duration.in.month.binned"         "savings.account.and.bonds.binned"
[3] "purpose.binned"                   "credit.amount.binned"            
imps = attStats(roughFixMod)
imps2 = imps[imps$decision != 'Rejected', c('meanImp', 'decision')]
head(imps2[order(-imps2$meanImp), ],10)  # descending sort


plot(boruta_output_binned, cex.axis=.7, las=2, xlab="", main="Variable Importance Binned")  

NA
NA

9.3 Decision Trees

We Now use the Binns to do the decision Trees

Use rpart

rpart.plot(tree)
Bad 'data' field in model 'call' (expected a data.frame or a matrix).
To silence this warning:
    Call rpart.plot with roundint=FALSE,
    or rebuild the rpart model with model=TRUE.

10.0 Excerpts From Credit Risk Modelling

10.1a Creditability Vs Duration Of Loan

cross_CredVsDuration=CrossTable(df_factors$duration.in.month.binned,df_factors$creditability,, prop.r = TRUE,
prop.c = FALSE, prop.t = FALSE, prop.chisq = TRUE,chisq = TRUE)

 
   Cell Contents
|-------------------------|
|                       N |
| Chi-square contribution |
|           N / Row Total |
|-------------------------|

 
Total Observations in Table:  1000 

 
                                    | df_factors$creditability 
df_factors$duration.in.month.binned |       bad |      good | Row Total | 
------------------------------------|-----------|-----------|-----------|
                           (-Inf,6] |         9 |        73 |        82 | 
                                    |     9.893 |     4.240 |           | 
                                    |     0.110 |     0.890 |     0.082 | 
------------------------------------|-----------|-----------|-----------|
                             (6,15] |        80 |       269 |       349 | 
                                    |     5.827 |     2.497 |           | 
                                    |     0.229 |     0.771 |     0.349 | 
------------------------------------|-----------|-----------|-----------|
                            (15,30] |       128 |       268 |       396 | 
                                    |     0.712 |     0.305 |           | 
                                    |     0.323 |     0.677 |     0.396 | 
------------------------------------|-----------|-----------|-----------|
                          (30, Inf] |        83 |        90 |       173 | 
                                    |    18.636 |     7.987 |           | 
                                    |     0.480 |     0.520 |     0.173 | 
------------------------------------|-----------|-----------|-----------|
                       Column Total |       300 |       700 |      1000 | 
------------------------------------|-----------|-----------|-----------|

 
Statistics for All Table Factors


Pearson's Chi-squared test 
------------------------------------------------------------
Chi^2 =  50.09743     d.f. =  3     p =  7.616443e-11 


 
plot(cross_CredVsDuration$prop.tbl,col=c("red","green"),main="Creditability Vs Duration",xlab = "Duration",ylab = "Creditability")

Conclusion: The longer the duration the worse the creditability

10.1b Creditability Vs Amount Of Loan

CrossTable(df_factors$credit.amount.binned,df_factors$creditability, prop.r = TRUE,
prop.c = FALSE, prop.t = FALSE, prop.chisq = FALSE)

 
   Cell Contents
|-------------------------|
|                       N |
|           N / Row Total |
|-------------------------|

 
Total Observations in Table:  1000 

 
                                | df_factors$creditability 
df_factors$credit.amount.binned |       bad |      good | Row Total | 
--------------------------------|-----------|-----------|-----------|
                 (-Inf,5969.95] |       231 |       619 |       850 | 
                                |     0.272 |     0.728 |     0.850 | 
--------------------------------|-----------|-----------|-----------|
               (5969.95,9162.7] |        40 |        60 |       100 | 
                                |     0.400 |     0.600 |     0.100 | 
--------------------------------|-----------|-----------|-----------|
                  (9162.7, Inf] |        29 |        21 |        50 | 
                                |     0.580 |     0.420 |     0.050 | 
--------------------------------|-----------|-----------|-----------|
                   Column Total |       300 |       700 |      1000 | 
--------------------------------|-----------|-----------|-----------|

 
cross_CredVsAmount=CrossTable(df_factors$credit.amount.binned,df_factors$creditability,, prop.r = TRUE,
prop.c = FALSE, prop.t = FALSE, prop.chisq = TRUE,chisq = TRUE)

 
   Cell Contents
|-------------------------|
|                       N |
| Chi-square contribution |
|           N / Row Total |
|-------------------------|

 
Total Observations in Table:  1000 

 
                                | df_factors$creditability 
df_factors$credit.amount.binned |       bad |      good | Row Total | 
--------------------------------|-----------|-----------|-----------|
                 (-Inf,5969.95] |       231 |       619 |       850 | 
                                |     2.259 |     0.968 |           | 
                                |     0.272 |     0.728 |     0.850 | 
--------------------------------|-----------|-----------|-----------|
               (5969.95,9162.7] |        40 |        60 |       100 | 
                                |     3.333 |     1.429 |           | 
                                |     0.400 |     0.600 |     0.100 | 
--------------------------------|-----------|-----------|-----------|
                  (9162.7, Inf] |        29 |        21 |        50 | 
                                |    13.067 |     5.600 |           | 
                                |     0.580 |     0.420 |     0.050 | 
--------------------------------|-----------|-----------|-----------|
                   Column Total |       300 |       700 |      1000 | 
--------------------------------|-----------|-----------|-----------|

 
Statistics for All Table Factors


Pearson's Chi-squared test 
------------------------------------------------------------
Chi^2 =  26.65546     d.f. =  2     p =  1.628696e-06 


 
plot(cross_CredVsAmount$prop.tbl,col=c("red","green"),main="Creditability Vs Amount",xlab = "Amount_Bin",ylab = "Creditability")

10.1c Creditability Vs Savings

library(gmodels)
library(gridExtra)

## Lets Plot Some CrossTables - Normally categorical Data to Show Relationship in a Table
#  Tpo help Understand the Tree
#
CrossTable(df_factors$savings.account.and.bonds.binned,df_factors$creditability, prop.r = TRUE,
prop.c = FALSE, prop.t = FALSE, prop.chisq = FALSE)

 
   Cell Contents
|-------------------------|
|                       N |
|           N / Row Total |
|-------------------------|

 
Total Observations in Table:  1000 

 
                                                   | df_factors$creditability 
       df_factors$savings.account.and.bonds.binned |       bad |      good | Row Total | 
---------------------------------------------------|-----------|-----------|-----------|
                ... < 100 DM + 100 <= ... < 500 DM |       251 |       455 |       706 | 
                                                   |     0.356 |     0.644 |     0.706 | 
---------------------------------------------------|-----------|-----------|-----------|
                                  misc. level neg. |         6 |        42 |        48 | 
                                                   |     0.125 |     0.875 |     0.048 | 
---------------------------------------------------|-----------|-----------|-----------|
unknown/ no savings account + 500 <= ... < 1000 DM |        43 |       203 |       246 | 
                                                   |     0.175 |     0.825 |     0.246 | 
---------------------------------------------------|-----------|-----------|-----------|
                                      Column Total |       300 |       700 |      1000 | 
---------------------------------------------------|-----------|-----------|-----------|

 
cross_CredVsSavings=CrossTable(df_factors$savings.account.and.bonds.binned,df_factors$creditability,, prop.r = TRUE,
prop.c = FALSE, prop.t = FALSE, prop.chisq = TRUE,chisq = TRUE)

 
   Cell Contents
|-------------------------|
|                       N |
| Chi-square contribution |
|           N / Row Total |
|-------------------------|

 
Total Observations in Table:  1000 

 
                                                   | df_factors$creditability 
       df_factors$savings.account.and.bonds.binned |       bad |      good | Row Total | 
---------------------------------------------------|-----------|-----------|-----------|
                ... < 100 DM + 100 <= ... < 500 DM |       251 |       455 |       706 | 
                                                   |     7.255 |     3.109 |           | 
                                                   |     0.356 |     0.644 |     0.706 | 
---------------------------------------------------|-----------|-----------|-----------|
                                  misc. level neg. |         6 |        42 |        48 | 
                                                   |     4.900 |     2.100 |           | 
                                                   |     0.125 |     0.875 |     0.048 | 
---------------------------------------------------|-----------|-----------|-----------|
unknown/ no savings account + 500 <= ... < 1000 DM |        43 |       203 |       246 | 
                                                   |    12.854 |     5.509 |           | 
                                                   |     0.175 |     0.825 |     0.246 | 
---------------------------------------------------|-----------|-----------|-----------|
                                      Column Total |       300 |       700 |      1000 | 
---------------------------------------------------|-----------|-----------|-----------|

 
Statistics for All Table Factors


Pearson's Chi-squared test 
------------------------------------------------------------
Chi^2 =  35.72764     d.f. =  2     p =  1.745187e-08 


 
plot(cross_CredVsSavings$prop.tbl,col=c("red","green"),main="Creditability Vs Savings",xlab = "Savings_Bin",ylab = "Creditability")

10.1d Creditability Vs Purpose

library(gmodels)
library(gridExtra)

## Lets Plot Some CrossTables - Normally categorical Data to Show Relationship in a Table
#  Tpo help Understand the Tree
#
CrossTable(df_factors$purpose.binned,df_factors$creditability, prop.r = TRUE,
prop.c = FALSE, prop.t = FALSE, prop.chisq = FALSE)

 
   Cell Contents
|-------------------------|
|                       N |
|           N / Row Total |
|-------------------------|

 
Total Observations in Table:  1000 

 
                                                                          | df_factors$creditability 
                                                df_factors$purpose.binned |       bad |      good | Row Total | 
--------------------------------------------------------------------------|-----------|-----------|-----------|
education + car (new) + misc. level pos. + business + furniture/equipment |       220 |       388 |       608 | 
                                                                          |     0.362 |     0.638 |     0.608 | 
--------------------------------------------------------------------------|-----------|-----------|-----------|
                                            radio/television + car (used) |        79 |       304 |       383 | 
                                                                          |     0.206 |     0.794 |     0.383 | 
--------------------------------------------------------------------------|-----------|-----------|-----------|
                                                         misc. level neg. |         1 |         8 |         9 | 
                                                                          |     0.111 |     0.889 |     0.009 | 
--------------------------------------------------------------------------|-----------|-----------|-----------|
                                                             Column Total |       300 |       700 |      1000 | 
--------------------------------------------------------------------------|-----------|-----------|-----------|

 
cross_CredVsPurpose=CrossTable(df_factors$purpose.binned,df_factors$creditability,, prop.r = TRUE,
prop.c = FALSE, prop.t = FALSE, prop.chisq = TRUE,chisq = TRUE)
Chi-squared approximation may be incorrect

 
   Cell Contents
|-------------------------|
|                       N |
| Chi-square contribution |
|           N / Row Total |
|-------------------------|

 
Total Observations in Table:  1000 

 
                                                                          | df_factors$creditability 
                                                df_factors$purpose.binned |       bad |      good | Row Total | 
--------------------------------------------------------------------------|-----------|-----------|-----------|
education + car (new) + misc. level pos. + business + furniture/equipment |       220 |       388 |       608 | 
                                                                          |     7.751 |     3.322 |           | 
                                                                          |     0.362 |     0.638 |     0.608 | 
--------------------------------------------------------------------------|-----------|-----------|-----------|
                                            radio/television + car (used) |        79 |       304 |       383 | 
                                                                          |    11.217 |     4.807 |           | 
                                                                          |     0.206 |     0.794 |     0.383 | 
--------------------------------------------------------------------------|-----------|-----------|-----------|
                                                         misc. level neg. |         1 |         8 |         9 | 
                                                                          |     1.070 |     0.459 |           | 
                                                                          |     0.111 |     0.889 |     0.009 | 
--------------------------------------------------------------------------|-----------|-----------|-----------|
                                                             Column Total |       300 |       700 |      1000 | 
--------------------------------------------------------------------------|-----------|-----------|-----------|

 
Statistics for All Table Factors


Pearson's Chi-squared test 
------------------------------------------------------------
Chi^2 =  28.62578     d.f. =  2     p =  6.081227e-07 


 
plot(cross_CredVsPurpose$prop.tbl,col=c("red","green"),main="Creditability Vs Purpose",xlab = "Savings_Bin",ylab = "Creditability")

10.2a Decision Trees Reference


library(rpart)
library(rpart.plot)

set.seed(123)
tree <- rpart(creditability ~ ., data=na.omit(df_factors),control = rpart.control(cp = 0.0001),model = TRUE)

tree
n= 1000 

node), split, n, loss, yval, (yprob)
      * denotes terminal node

1) root 1000 300 good (0.3000000 0.7000000)  
  2) savings.account.and.bonds.binned=... < 100 DM + 100 <= ... < 500 DM 706 251 good (0.3555241 0.6444759)  
    4) duration.in.month.binned=(30, Inf] 125  51 bad (0.5920000 0.4080000) *
    5) duration.in.month.binned=(-Inf,6],(6,15],(15,30] 581 177 good (0.3046472 0.6953528) *
  3) savings.account.and.bonds.binned=misc. level neg.,unknown/ no savings account + 500 <= ... < 1000 DM 294  49 good (0.1666667 0.8333333) *
rpart.plot(tree)

NA
NA

10.2b Adjust Priors and Pruning


library(rpart)
library(rpart.plot)

set.seed(123)
tree <- rpart(creditability ~ ., data=na.omit(df_factors),control = rpart.control(cp = 0.000001),model = TRUE,parms = list(prior = c(0.7, 0.3)))

tree
n= 1000 

node), split, n, loss, yval, (yprob)
      * denotes terminal node

 1) root 1000 300.000000 bad (0.7000000 0.3000000)  
   2) savings.account.and.bonds.binned=... < 100 DM + 100 <= ... < 500 DM 706 195.000000 bad (0.7502135 0.2497865)  
     4) duration.in.month.binned=(15,30],(30, Inf] 400  94.714290 bad (0.8151487 0.1848513)  
       8) purpose.binned=education + car (new) + misc. level pos. + business + furniture/equipment 252  51.857140 bad (0.8549547 0.1450453) *
       9) purpose.binned=radio/television + car (used),misc. level neg. 148  42.857140 bad (0.7232472 0.2767528)  
        18) duration.in.month.binned=(30, Inf] 47   9.857143 bad (0.8503254 0.1496746) *
        19) duration.in.month.binned=(15,30] 101  33.000000 bad (0.6292135 0.3707865)  
          38) credit.amount.binned=(-Inf,5969.95],(9162.7, Inf] 92  29.142860 bad (0.6577181 0.3422819) *
          39) credit.amount.binned=(5969.95,9162.7] 9   0.000000 good (0.0000000 1.0000000) *
     5) duration.in.month.binned=(-Inf,6],(6,15] 306 100.285700 bad (0.6261981 0.3738019)  
      10) purpose.binned=education + car (new) + misc. level pos. + business + furniture/equipment,misc. level neg. 198  61.285710 bad (0.6767956 0.3232044) *
      11) purpose.binned=radio/television + car (used) 108  39.000000 bad (0.5042373 0.4957627)  
        22) duration.in.month.binned=(6,15] 89  30.857140 bad (0.5624578 0.4375422) *
        23) duration.in.month.binned=(-Inf,6] 19   0.000000 good (0.0000000 1.0000000) *
   3) savings.account.and.bonds.binned=misc. level neg.,unknown/ no savings account + 500 <= ... < 1000 DM 294 105.000000 bad (0.5212766 0.4787234)  
     6) purpose.binned=education + car (new) + misc. level pos. + business + furniture/equipment 161  54.000000 bad (0.6019656 0.3980344)  
      12) duration.in.month.binned=(15,30],(30, Inf] 87  27.428570 bad (0.6617733 0.3382267)  
        24) savings.account.and.bonds.binned=unknown/ no savings account + 500 <= ... < 1000 DM 73  21.857140 bad (0.7013663 0.2986337) *
        25) savings.account.and.bonds.binned=misc. level neg. 14   2.333333 good (0.2951807 0.7048193) *
      13) duration.in.month.binned=(-Inf,6],(6,15] 74  26.571430 bad (0.5130890 0.4869110)  
        26) savings.account.and.bonds.binned=misc. level neg. 18   6.000000 bad (0.6086957 0.3913043) *
        27) savings.account.and.bonds.binned=unknown/ no savings account + 500 <= ... < 1000 DM 56  18.666670 good (0.4757282 0.5242718) *
     7) purpose.binned=radio/television + car (used),misc. level neg. 133  32.666670 good (0.3904382 0.6095618)  
      14) credit.amount.binned=(5969.95,9162.7],(9162.7, Inf] 27   9.000000 bad (0.6086957 0.3913043) *
      15) credit.amount.binned=(-Inf,5969.95] 106  18.666670 good (0.3076923 0.6923077) *
rpart.plot(tree)


prunetree=prune(tree,cp=0.01)


rpart.plot(prunetree)

10.2c Loss Matrix

Change the code provided in the video such that a decision tree is constructed using a loss matrix penalizing 10 times more heavily for misclassified defaults.

set.seed(123)
treelossm <- rpart(creditability ~ ., data=na.omit(df_factors),control = rpart.control(cp = 0.001),model = TRUE,parms = list(loss = matrix(c(0, 3, 1, 0), ncol = 2)))
tree
n= 1000 

node), split, n, loss, yval, (yprob)
      * denotes terminal node

1) root 1000 300 good (0.3000000 0.7000000)  
  2) duration.in.month.binned=(30, Inf] 173  83 good (0.4797688 0.5202312) *
  3) duration.in.month.binned=(-Inf,6],(6,15],(15,30] 827 217 good (0.2623942 0.7376058)  
    6) credit.amount.binned=(9162.7, Inf] 11   6 bad (0.8181818 0.1818182) *
    7) credit.amount.binned=(-Inf,5969.95],(5969.95,9162.7] 816 208 good (0.2549020 0.7450980) *
rpart.plot(treelossm)

10.3 Pruning The Tree

10.3.1 Using Printcp and Plotcp

Using printcp we get the complexity parameter ( We can use this to set the tree depth, ie pruning ), we set the complexity parameter to get to the complexity level

Need to minimise xerror in the second last column of the table below

Need to set a seed to get the results reproducable


# We use printcp on the above tree 
printcp(tree)

Classification tree:
rpart(formula = creditability ~ ., data = na.omit(df_factors), 
    model = TRUE, parms = list(prior = c(0.7, 0.3)), control = rpart.control(cp = 1e-06))

Variables actually used in tree construction:
[1] credit.amount.binned             duration.in.month.binned         purpose.binned                   savings.account.and.bonds.binned

Root node error: 300/1000 = 0.3

n= 1000 

         CP nsplit rel error  xerror     xstd
1 0.0305556      0   1.00000 1.00000 0.020702
2 0.0166667      2   0.93889 1.01397 0.038818
3 0.0090476      3   0.92222 0.97571 0.036479
4 0.0057143      6   0.89508 0.99508 0.041416
5 0.0042857      9   0.87794 0.98365 0.041492
6 0.0000010     12   0.86508 0.94111 0.039309
plotcp(tree)

NA
NA

10.3.2 Using prune() to Prune the tree


ptree_pruned = prune(tree,cp=0.0002
                     )


rpart.plot(ptree_pruned,uniform=TRUE)


# prp function

prp(ptree_pruned,extra=1)

10.3.3 Other Tree Options

Weights: can use this to counter imbalance in tree

Other arguments are the rpart.control arguments

minsplit: min number in a node to split: default is 20 ..may be useful to lower it when data is unbalanced minbuckets: minimum in a leaf node (default is 1/3 of minsplit)

Evaluation : use the predict function

confusionMatrix(pred_tree_class,df_factors$creditability)
Confusion Matrix and Statistics

          Reference
Prediction bad good
      bad  283  513
      good  17  187
                                          
               Accuracy : 0.47            
                 95% CI : (0.4387, 0.5015)
    No Information Rate : 0.7             
    P-Value [Acc > NIR] : 1               
                                          
                  Kappa : 0.1429          
                                          
 Mcnemar's Test P-Value : <2e-16          
                                          
            Sensitivity : 0.9433          
            Specificity : 0.2671          
         Pos Pred Value : 0.3555          
         Neg Pred Value : 0.9167          
             Prevalence : 0.3000          
         Detection Rate : 0.2830          
   Detection Prevalence : 0.7960          
      Balanced Accuracy : 0.6052          
                                          
       'Positive' Class : bad             
                                          

10.4 Evaluating The Credit Risk Model

10.4.1 Strategy Curve

We dont want to exceed a certain % of defaults

10.4.2 ROC Curve

What is the best model overall ?

Accuracy is Maximized when a very high cutoff is chosen; when all testset items are classified as non default

ROC SHOWS CROSSOVER .. Harder to tell which is better

auc(ROC_tree)
Area under the curve: 0.7114

Which Variables do many AUC and compare

AUC Based Pruning..


# Example 1

# # Build four models each time deleting one variable in log_3_remove_ir
# log_4_remove_amnt <- glm(loan_status ~ grade + annual_inc + emp_cat, 
#                          family = binomial, data = training_set) 
# log_4_remove_grade <- glm(loan_status ~ loan_amnt  + annual_inc + emp_cat, 
#                           family = binomial, data = training_set)
# log_4_remove_inc <- glm(loan_status ~ loan_amnt + grade  + emp_cat , 
#                         family = binomial, data = training_set)
# log_4_remove_emp <- glm(loan_status ~ loan_amnt + grade + annual_inc, 
#                         family = binomial, data = training_set)
# 
# # Make PD-predictions for each of the models
# pred_4_remove_amnt <- predict(log_4_remove_amnt, newdata = test_set, type = "response")
# pred_4_remove_grade <- predict(log_4_remove_grade, newdata = test_set, type = "response")
# pred_4_remove_inc <- predict(log_4_remove_inc, newdata = test_set, type = "response")
# pred_4_remove_emp <- predict(log_4_remove_emp, newdata = test_set, type = "response")
# 
# # Compute the AUCs
# auc(test_set$loan_status, pred_4_remove_amnt)
# auc(test_set$loan_status, pred_4_remove_grade)
# auc(test_set$loan_status, pred_4_remove_inc)
# auc(test_set$loan_status, pred_4_remove_emp)


# Example 2

# # Build four models each time deleting one variable in log_3_remove_ir
# log_4_remove_amnt <- glm(loan_status ~ grade + annual_inc + emp_cat, 
#                          family = binomial, data = training_set) 
# log_4_remove_grade <- glm(loan_status ~ loan_amnt  + annual_inc + emp_cat, 
#                           family = binomial, data = training_set)
# log_4_remove_inc <- glm(loan_status ~ loan_amnt + grade  + emp_cat , 
#                         family = binomial, data = training_set)
# log_4_remove_emp <- glm(loan_status ~ loan_amnt + grade + annual_inc, 
#                         family = binomial, data = training_set)
# 
# # Make PD-predictions for each of the models
# pred_4_remove_amnt <- predict(log_4_remove_amnt, newdata = test_set, type = "response")
# pred_4_remove_grade <- predict(log_4_remove_grade, newdata = test_set, type = "response")
# pred_4_remove_inc <- predict(log_4_remove_inc, newdata = test_set, type = "response")
# pred_4_remove_emp <- predict(log_4_remove_emp, newdata = test_set, type = "response")
# 
# # Compute the AUCs
# auc(test_set$loan_status, pred_4_remove_amnt)
# auc(test_set$loan_status, pred_4_remove_grade)
# auc(test_set$loan_status, pred_4_remove_inc)
# auc(test_set$loan_status, pred_4_remove_emp)

11.0 Model Explanation

LS0tDQp0aXRsZTogIkZlYXR1cmUgU2VsZWN0aW9uIEJsb2ciDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyMgIDEuMCBGZWF0dXJlIFNlbGVjdGlvbiBJbnRyb2R1Y3Rpb24NCg0KRmVhdHVyZSBzZWxlY3Rpb24gaXMgYW4gaW1wb3J0YW50IHRvcGljIHRoYXQgcmVxdWlyZXMgaW4tZGVwdGgga25vd2xlZGdlIG9mIHRoZSBwcm9ibGVtIGRvbWFpbi4gSGF2aW5nIHRoZSByaWdodCBmZWF0dXJlcyBjYW4gaGVscCB0aGUgbW9kZWwgcGVyZm9ybSBiZXR0ZXIuIEZvciBleGFtcGxlLCByZW1vdmluZyB0aGUgaGlnaGx5IGNvcnJlbGF0ZWQgYXR0cmlidXRlcyBjYW4gbGVhZCB0byBhIGJldHRlciBtb2RlbCBhbmQgaW1wcm92ZSBwcmVkaWN0aW9uLg0KDQpJbiB0aGlzIHBvc3QsIEkgd2lsbCBleHBsb3JlIGZpbmRpbmcgaGlnaGx5IGNvcnJlbGF0ZWQgdmFyaWFibGVzLCBSZWN1cnNpdmUgRmVhdHVyZSBFbGltaW5hdGlvbiwgc3RlcHdpc2UgZWxpbWluYXRpb24sIGFuZCBCb3J1dGEgZmVhdHVyZSBzZWxlY3Rpb24uDQoNCiMjIyAxLjEgUGxvdCBIaXN0b2dyYW1zDQoNCkZpcnN0LCBJIHdpbGwgcGxvdCBhIGhpc3RvZ3JhbSBvZiB0aGUgdmFyaWFibGVzIGluIHRoZSBQaW1hIEluZGlhbnMgRGlhYmV0ZXMgZGF0YXNldC4gVmFyaW91cyB2YXJpYWJsZSBleGhpYml0IHNrZXcgZGlzdHJpYnV0aW9ucyBhbmQgc2hvdWxkIGJlIGNvbnNpZGVyZWQgZm9yIHRyYW5zZm9ybWF0aW9ucy4NCg0KDQpgYGB7ciBQbG90IEhpc3RvZ3JhbXN9DQpsaWJyYXJ5KCJtbGJlbmNoIikNCmxpYnJhcnkoImNhcmV0IikNCiMgbG9hZCB0aGUgZGF0YQ0KZGF0YShQaW1hSW5kaWFuc0RpYWJldGVzKQ0KRGF0YUV4cGxvcmVyOjpwbG90X2hpc3RvZ3JhbShQaW1hSW5kaWFuc0RpYWJldGVzKQ0KYGBgDQoNCiMjIyAxLjIgQ29ycmVsYXRpb25zDQoNCkJlbG93IHdlIHdpbGwgdHJhbnNmb3JtIHRoZSByZXNwb25zZSB2YXJpYWJsZSBmcm9tIGEgZmFjdG9yIHRvIG51bWVyaWMuIENoYW5naW5nIHRoZSB2YXJpYWJsZSBhbGxvd3MgdXMgdG8gc2VlIGFueSBjb3JyZWxhdGlvbnMgd2l0aCBvdGhlciB2YXJpYWJsZXMuIEkgYWxzbyBjcmVhdGVkIGEgaGlnaGx5IGNvcnJlbGF0ZWQgdmFyaWFibGUgYnkgY29tYmluaW5nIGdsdWNvc2UgYW5kIG1hc3MuIEFzIGV4cGVjdGVkIGdsdWNvc2UgYW5kIG1hc3MgYXJlIGhpZ2hseSBjb3JyZWxhdGVkIHdpdGggdGhlIGNvbWJpbmF0aW9uIHZhcmlhYmxlIEhDb3JyZWxhdGVkLg0KDQpgYGB7ciAgQ29ycmVsYXRpb25zfQ0KDQpsaWJyYXJ5KCJjb3JycGxvdCIpDQppbmRpYW5zID0gUGltYUluZGlhbnNEaWFiZXRlcw0KaW5kaWFucyRkaWFiZXRlcyA9IGlmZWxzZShpbmRpYW5zJGRpYWJldGVzID09InBvcyIsMSwwKQ0KaW5kaWFucyRIQ29ycmVsYXRlZCA9aW5kaWFucyRnbHVjb3NlKmluZGlhbnMkbWFzcw0KDQpjb3JfbXggPSBjb3IoaW5kaWFucyAgLHVzZT0icGFpcndpc2UuY29tcGxldGUub2JzIiwgbWV0aG9kID0gInBlYXJzb24iKQ0KY29ycnBsb3QoY29yX214LCBtZXRob2QgPSAiY29sb3IiLCANCiAgICAgICAgIHR5cGUgPSAidXBwZXIiLCBvcmRlciA9ICJvcmlnaW5hbCIsIG51bWJlci5jZXggPSAuNywNCiAgICAgICAgIGFkZENvZWYuY29sID0gImJsYWNrIiwgIyBBZGQgY29lZmZpY2llbnQgb2YgY29ycmVsYXRpb24NCiAgICAgICAgIHRsLmNvbCA9ICJibGFjayIsIHRsLnNydCA9IDkwLCAjIFRleHQgbGFiZWwgY29sb3IgYW5kIHJvdGF0aW9uDQogICAgICAgICAgICAgICAgICAjIGhpZGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgb24gdGhlIHByaW5jaXBhbCBkaWFnb25hbA0KICAgICAgICAgZGlhZyA9IFRSVUUpDQoNCg0KDQoNCmBgYA0KDQojIyMgMi4wIFJlY3Vyc2l2ZSBGZWF0dXJlIEVsaW1pbmF0aW9uDQoNCiMjIyMgMi4xIFJ1biB0aGUgRmVhdHVyZSBTZWxlY3Rpb24NCg0KUmVjdXJzaXZlIEZlYXR1cmUgRWxpbWluYXRpb24oUkZFKSBidWlsZHMgbW9kZWxzIHdpdGggZGlmZmVyZW50IHN1YnNldHMgb2YgYSBkYXRhc2V0IHRvIGlkZW50aWZ5IGEgZmVhdHVyZSB0aGF0IG1pZ2h0IG5vdCBiZSByZXF1aXJlZC4gQ2FyZXQgcHJvdmlkZXMgYSByZmUgZnVuY3Rpb24gdGhhdCBmYWNpbGl0YXRlcyB0aGlzIHByb2Nlc3MuDQoNCkJlbG93IHdlIHdpbGwgbG9hZCB0aGUgUGltYSBJbmRpYW5zIERpYWJldGVzIGFuZCBmaXQgdGhlIHJmZSBmdW5jdGlvbi4gQ29udHJvbCB3YXMgaW1wbGVtZW50ZWQgdXNpbmcgcmFuZG9tIGZvcmVzdCBjcm9zcy12YWxpZGF0ZWQgd2l0aCBrZm9sZCBvZiAxMC4gVGhlIGZpbmFsIHBsb3QgaW5kaWNhdGVzIHRoYXQgZWlnaHQgdmFyaWFibGVzIGhhdmUgYW4gYWNjdXJhY3kgb2YgNzcuNzMuDQoNCmBgYHtyIFJGRX0NCiMgZGVmaW5lIHRoZSBjb250cm9sIHVzaW5nIGEgcmFuZG9tIGZvcmVzdCBzZWxlY3Rpb24gZnVuY3Rpb24NCmNvbnRyb2wgPSByZmVDb250cm9sKGZ1bmN0aW9ucz1yZkZ1bmNzLCBtZXRob2Q9ImN2IiwgbnVtYmVyPTEwLCByZXBlYXRzID0gMSkgIyBtZXRob2Q9ImN2IiAsIGxlYXZlIG91dCByZXBlYXRzIHRvIHNwZWVkIHVwIG9yIG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwgYW5kIGxlYXZlIG91dCByZXBlYXRzIA0KIyBydW4gdGhlIFJGRSBhbGdvcml0aG0NCnNldC5zZWVkKDE0MykNCiMNCiMgICBOT1RFIFRIQVQgVEhFIFByaW1hSW5kaWFucyBbLDldIGlzIGEgRmFjdG9yIFZhcmlhYmxlIHdpdGggbmVnIHBvcyBsZXZlbHMgKGZhY3RvciB2YXJpYWJsZSBub3QgbnVtZXJpYyktIFdlIGNob29zZSB0aGUgbnVtYmVyIG9mIHZhcmlhYmxlcyB3ZSB3YW50IHRvIHNlZS4NCiMNCnJlc3VsdHMgPSByZmUoUGltYUluZGlhbnNEaWFiZXRlc1ssMTo4XSwgUGltYUluZGlhbnNEaWFiZXRlc1ssOV0sIHNpemVzPWMoMTo4KSwgcmZlQ29udHJvbD1jb250cm9sLA0KICAgICAgICAgICAgICB2ZXJib3NlPUZBTFNFKQ0KIyBzdW1tYXJpemUgdGhlIHJlc3VsdHMNCnByaW50KHJlc3VsdHMpDQoNCnJlc3VsdHMNCg0KDQpgYGANCg0KDQojIyMjIDIuMiBMaXN0IHRoZSBwcmVkaWN0b3JzIGluIHRoZSBPcmRlciBvZiBDaG9pY2UNCg0KYGBge3IgTGlzdCBQcmVkaWN0b3JzfQ0KDQojIGxpc3QgdGhlIGNob3NlbiBmZWF0dXJlcw0KcHJlZGljdG9ycyhyZXN1bHRzKQ0KDQoNCmBgYA0KDQojIyMjIDIuMyBQbG90IFRoZSBSZXN1bHRzDQoNCmBgYHtyfQ0KIyBwbG90IHRoZSByZXN1bHRzDQpwbG90KHJlc3VsdHMsIHR5cGU9YygiZyIsICJvIikpDQpgYGANCg0KQW5vdGhlciBtZXRob2QgcmVsaWVzIG9uIGZpdHRpbmcgYSByYW5kb20gZm9yZXN0IG1vZGVsIGFuZCBpZGVudGlmeWluZyB2YXJpYWJsZSBpbXBvcnRhbmNlLiBJbiB0aGlzIG1ldGhvZCwgdmFyaWFibGUgaW1wb3J0YW5jZSBjYW4gdmFyeSBieSBtb2RlbC4NCg0KIyMjIDMuMCBWYXJpYWJsZSBJbXBvcnRhbmNlDQoNCmBgYHtyIFRlc3QgV2l0aCBBcnRpY2xlIERhdGF9DQojIEFub3RoZXIgbWV0aG9kIHJlbGllcyBvbiBmaXR0aW5nIGEgcmFuZG9tIGZvcmVzdCBtb2RlbCBhbmQgaWRlbnRpZnlpbmcgdmFyaWFibGUgaW1wb3J0YW5jZS4gSW4gdGhpcyBtZXRob2QsIHZhcmlhYmxlIGltcG9ydGFuY2UgY2FuIHZhcnkgYnkgbW9kZWwuDQoNCmdsaW1wc2UoUGltYUluZGlhbnNEaWFiZXRlcykNCkRhdGE9UGltYUluZGlhbnNEaWFiZXRlcw0KDQpzZXQuc2VlZCgxNDMpDQpyUGFydE1vZCA9IHRyYWluKGRpYWJldGVzIH4gLiwgZGF0YT1EYXRhLCBtZXRob2Q9InJwYXJ0IikNCnJwYXJ0SW1wID0gdmFySW1wKHJQYXJ0TW9kLDEwKQ0KcGxvdChycGFydEltcCwgdG9wID0gOCwgbWFpbj0nVmFyaWFibGUgSW1wb3J0YW5jZSBVc2luZyBQaW1hSW5kaWFucyBEYXRhc2V0JykNCg0KDQpgYGANCg0KDQojIyMgNC4wIFN0ZXBXaXNlIFNlbGVjdGlvbg0KDQpTdGVwd2lzZSBzZWxlY3Rpb24gaXMgYSBtZXRob2QgdGhhdCBhbGxvd3MgZm9yIHZhcmlhYmxlcyB0byBiZSBhZGRlZCBvciByZW1vdmUgaW4gZWl0aGVyIGRpcmVjdGlvbi4gVGhlIG1vZGVsIHBlcmZvcm1hbmNlIGlzIG1lYXN1cmVkIGluIEFJQy4gQWthaWtlIGluZm9ybWF0aW9uIGNyaXRlcmlvbiAoQUlDKSBlc3RpbWF0ZXMgdGhlIHF1YWxpdHkgb2YgZWFjaCBtb2RlbCByZWxhdGl2ZSB0byBlYWNoIG1vZGVsIHdpdGggdGhlIGxvd2VzdCBBSUMgYmVpbmcgdGhlIGJlc3QgbW9kZWwuDQoNCg0KIyMjIyA0LjFhIEJhY2t3YXJkIFNlbGVjdGlvbiANCg0KDQpUaGUgYmFja3dhcmQgcHJvY2VkdXJlIGJlZ2lucyB3aXRoIGEgZ2VuZXJhbCBtb2RlbCB0aGF0IGluY2x1ZGVzIGFsbCB2YXJpYWJsZXMgYW5kIGVsaW1pbmF0ZXMgb25lIHZhcmlhYmxlIGF0IGEgdGltZS4NCg0KYGBge3IgQmFja3dhcmR9DQoNCg0KI2dsbShkaWFiZXRlcyB+IC4sIGRhdGE9RGF0YSwgZmFtaWx5PSJiaW5vbWlhbCIpDQpzdGVwKGdsbShkaWFiZXRlcyB+IC4sIGRhdGE9RGF0YSwgZmFtaWx5PSJiaW5vbWlhbCIpLGRpcmVjdGlvbj0iYmFja3dhcmQiKQ0KDQpgYGANCg0KIyMjIyA0LjFiIEZvcndhcmQgIFNlbGVjdGlvbiANCg0KDQpUaGUgZm9yd2FyZCBtZXRob2QgYmVnaW5zIHdpdGggYSBzaW1wbGUgbW9kZWwgdGhlbiBhZGRzIHN1aXRhYmxlIHZhcmlhYmxlIG9uZSBhdCBhIHRpbWUgdW50aWwgdGhlIGJlc3QgbW9kZWwgaXMgb2J0YWluZWQuDQoNCmBgYHtyIEZvcndhcmR9DQoNCg0KI2dsbShkaWFiZXRlcyB+IC4sIGRhdGE9RGF0YSwgZmFtaWx5PSJiaW5vbWlhbCIpDQpzdGVwKGdsbShkaWFiZXRlcyB+IC4sIGRhdGE9RGF0YSwgZmFtaWx5PSJiaW5vbWlhbCIpLGRpcmVjdGlvbj0iZm9yd2FyZCIpDQoNCmBgYA0KDQojIyMjIDQuMWMgQm90aCBTZWxlY3Rpb24NCg0KDQpUaGUgYm90aCBtZXRob2QgaXMgdGhlIGNvbWJpbmF0aW9uIG9mIGJhY2t3cmFkIGFuZCBmb3J3YXJkIHByb2NlZHVyZXMuDQoNCmBgYHtyIEJvdGggfQ0KDQoNCiNnbG0oZGlhYmV0ZXMgfiAuLCBkYXRhPURhdGEsIGZhbWlseT0iYmlub21pYWwiKQ0Kc3RlcChnbG0oZGlhYmV0ZXMgfiAuLCBkYXRhPURhdGEsIGZhbWlseT0iYmlub21pYWwiKSxkaXJlY3Rpb249ImJvdGgiKQ0KDQpgYGANCg0KU2VlbXMgdGhhdCB0aGUgbW9kZWwgd2l0aG91dCBUcmljZXBzIGlzIEJlc3QgDQoNCiMjIyA1LjAgQm9ydXRhDQoNCkJvcnV0YSBpcyBhIGZlYXR1cmUgcmFua2luZyBhbmQgc2VsZWN0aW9uIGFsZ29yaXRobSBiYXNlZCBvbiByYW5kb20gZm9yZXN0IGFsZ29yaXRobS4gVGhlIGFkdmFudGFnZXMgb2YgdXNpbmcgdGhpcyBwYWNrYWdlIGFyZSB0aGUgZWFzZSBvZiB2YXJpYWJsZXMgc2VsZWN0aW9uIGFuZCB0aGUgYWJpbGl0eSB0byBhZGp1c3QgdmFyaWFibGUgc2VsZWN0aW9uLg0KDQpCZWxvdyBJIGZpdHRlZCB0aGUgQm9ydXRhIGZ1bmN0aW9uIHdpdGggdGhlIGRhdGFzZXQgZm9yIGV2YWx1YXRpb24uDQoNCmBgYHtyfQ0KbGlicmFyeSgnQm9ydXRhJykNCnNldC5zZWVkKDE0MykNCmJvcnV0YV9vdXRwdXQgPSBCb3J1dGEoZGlhYmV0ZXMgfiAuLCBkYXRhPW5hLm9taXQoRGF0YSksIGRvVHJhY2U9MCkgDQpgYGANCg0KIyMjIyA1LjEgU2lnbmlmaWNhbnQgVmFyaWFibGVzDQoNClRoZSBzaWduaWZpY2FudCB2YXJpYWJsZXMgY2FuIGJlIGV4dHJhY3RlZCBmcm9tIHRoZSBzZWxlY3Rpb24uIFRlbnRhdGl2ZSB2YXJpYWJsZXMgYXJlIHZhcmlhYmxlcyB0aGF0IGNhbiBiZSBkcm9wcGVkIG9yIGtlcHQuDQoNCg0KYGBge3IgU2lnaWZpY2FudCBWYXJpYWJsZXN9DQoNClNpZ25pZmljYW50X3ZhcnMgPSBnZXRTZWxlY3RlZEF0dHJpYnV0ZXMoYm9ydXRhX291dHB1dCwgd2l0aFRlbnRhdGl2ZSA9IFRSVUUpDQpTaWduaWZpY2FudF92YXJzDQoNCg0KDQpgYGANCg0KU2VlbXMgYWxsIGFyZSBzaWduaWZpY2FudA0KDQpCb3J1dGEgaGFzIGEgbWV0aG9kIGZvciBtYWtpbmcgdGhlIHNlbGVjdGluZyB0ZW50YXRpdmUgdmFyaWFibGUgZm9yIHRoZSB1c2VyLg0KDQpgYGB7cn0NClNpZ25pZmljYW50X3ZhcnMgPSBnZXRTZWxlY3RlZEF0dHJpYnV0ZXMoYm9ydXRhX291dHB1dCwgd2l0aFRlbnRhdGl2ZSA9IFRSVUUpDQpTaWduaWZpY2FudF92YXJzDQoNCmBgYA0KDQoNClRoZSBpbXBvcnRhbmNlIG9mIHZhcmlhYmxlcyBjYW4gYmUgc2hvd24gYnkgdGhlIGJlbG93IG1ldGhvZCB3aXRoICBiZWluZyB0aGUgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGUuDQoNCg0KIyMjIyA1LjIgSW1wb3J0YW5jZSBvZiBWYXJpYWJsZXMNCg0KDQpUaGUgaW1wb3J0YW5jZSBvZiB2YXJpYWJsZXMgY2FuIGJlIHNob3duIGJ5IHRoZSBiZWxvdyBtZXRob2Qgd2l0aCB2YXJpIGJlaW5nIHRoZSBtb3N0IGltcG9ydGFudCB2YXJpYWJsZS4NCg0KYGBge3IgVmFyaWFibGUgSW1wb3J0YW5jZX0NCg0KIyBWYXJpYWJsZSBJbXBvcnRhbmNlIFNjb3Jlcw0KaW1wcyA9IGF0dFN0YXRzKHJvdWdoRml4TW9kKQ0KaW1wczIgPSBpbXBzW2ltcHMkZGVjaXNpb24gIT0gJ1JlamVjdGVkJywgYygnbWVhbkltcCcsICdkZWNpc2lvbicpXQ0KaGVhZChpbXBzMltvcmRlcigtaW1wczIkbWVhbkltcCksIF0sMTApICAjIGRlc2NlbmRpbmcgc29ydA0KDQoNCmBgYA0KDQoNCiMjIyMgNS4zIFBsb3R0aW5nIEltcG9ydGFuY2Ugb2YgVmFyaWFibGVzDQoNCmBgYHtyIFBsb3R0aW5nfQ0KDQpwbG90KGJvcnV0YV9vdXRwdXQsIGNleC5heGlzPS43LCBsYXM9MiwgeGxhYj0iIiwgbWFpbj0iVmFyaWFibGUgSW1wb3J0YW5jZSIpICANCg0KYGBgDQoNCiMjIyA2LjAgSW5mb3JtYXRpb24gVmFsdWUgYW5kIFdlaWdodHMgb2YgRXZpZGVuY2UgKENhdGVnb3JpY2FsIFZhcmlhYmxlcykNCg0KVGhlIEluZm9ybWF0aW9uIFZhbHVlIGNhbiBiZSB1c2VkIHRvIGp1ZGdlIGhvdyBpbXBvcnRhbnQgYSBnaXZlbiBjYXRlZ29yaWNhbCB2YXJpYWJsZSBpcyBpbiBleHBsYWluaW5nIHRoZSBiaW5hcnkgWSB2YXJpYWJsZS4gSXQgZ29lcyB3ZWxsIHdpdGggbG9naXN0aWMgcmVncmVzc2lvbiBhbmQgb3RoZXIgY2xhc3NpZmljYXRpb24gbW9kZWxzIHRoYXQgY2FuIG1vZGVsIGJpbmFyeSB2YXJpYWJsZXMuDQoNCkxldOKAmXMgdHJ5IHRvIGZpbmQgb3V0IGhvdyBpbXBvcnRhbnQgdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBhcmUgaW4gcHJlZGljdGluZyBpZiBhbiBpbmRpdmlkdWFsIHdpbGwgZWFybiA+NTBrIGZyb20gdGhlIOKAmGFkdWx0LmNzduKAmSBkYXRhc2V0LiBKdXN0IHJ1biB0aGUgY29kZSBiZWxvdyB0byBpbXBvcnQgdGhlIGRhdGFzZXQuDQoNCmBgYHtyfQ0KbGlicmFyeShJbmZvcm1hdGlvblZhbHVlKQ0KaW5wdXREYXRhIDwtIHJlYWQuY3N2KCIuL0RhdGEvYWR1bHQuY3N2IikNCnByaW50KGhlYWQoaW5wdXREYXRhKSkNCmBgYA0KDQpBbHJpZ2h0LCBsZXTigJlzIG5vdyBmaW5kIHRoZSBpbmZvcm1hdGlvbiB2YWx1ZSBmb3IgdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBpbiB0aGUgaW5wdXREYXRhLg0KDQpgYGB7ciBJbmZvcm1hdGlvbiBWYWx1ZX0NCmlucHV0RGF0YSRpbmNvbWU9aWZlbHNlKGlucHV0RGF0YSRpbmNvbWU9PSI8PTUwSyIsMCwxKQ0KIyBDaG9vc2UgQ2F0ZWdvcmljYWwgVmFyaWFibGVzIHRvIGNvbXB1dGUgSW5mbyBWYWx1ZS4gQ2hhbmdlIFZhciBuYW1lcyANCmNhdF92YXJzIDwtIGMgKCJ3b3JrY2xhc3MiLCAiZWR1Y2F0aW9uIiwgIm1hcml0YWwuc3RhdHVzIiwgIm9jY3VwYXRpb24iLCAicmVsYXRpb25zaGlwIiwgInJhY2UiLCAiZ2VuZGVyIiwgIm5hdGl2ZS5jb3VudHJ5IikgICMgZ2V0IGFsbCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMNCg0KIyBJbml0IE91dHB1dA0KZGZfaXYgPC0gZGF0YS5mcmFtZShWQVJTPWNhdF92YXJzLCBJVj1udW1lcmljKGxlbmd0aChjYXRfdmFycykpLCBTVFJFTkdUSD1jaGFyYWN0ZXIobGVuZ3RoKGNhdF92YXJzKSksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSAgIyBpbml0IG91dHB1dCBkYXRhZnJhbWUNCg0KDQoNCg0KIyBHZXQgSW5mb3JtYXRpb24gVmFsdWUgZm9yIGVhY2ggdmFyaWFibGUNCmZvciAoZmFjdG9yX3ZhciBpbiBjYXRfdmFycyl7DQogIGRmX2l2W2RmX2l2JFZBUlMgPT0gZmFjdG9yX3ZhciwgIklWIl0gPC0gSW5mb3JtYXRpb25WYWx1ZTo6SVYoWD1pbnB1dERhdGFbLCBmYWN0b3JfdmFyXSwgWT1pbnB1dERhdGEkaW5jb21lKQ0KICBkZl9pdltkZl9pdiRWQVJTID09IGZhY3Rvcl92YXIsICJTVFJFTkdUSCJdIDwtIGF0dHIoSW5mb3JtYXRpb25WYWx1ZTo6SVYoWD1pbnB1dERhdGFbLCBmYWN0b3JfdmFyXSwgWT1pbnB1dERhdGEkaW5jb21lKSwgImhvd2dvb2QiKQ0KfQ0KDQojIFNvcnQNCmRmX2l2IDwtIGRmX2l2W29yZGVyKC1kZl9pdiRJViksIF0NCg0KZGZfaXYNCg0KDQoNCg0KYGBgDQoNCkhlcmUgaXMgd2hhdCB0aGUgcXVhbnR1bSBvZiBJbmZvcm1hdGlvbiBWYWx1ZSBtZWFuczoNCg0KTGVzcyB0aGFuIDAuMDIsIHRoZW4gdGhlIHByZWRpY3RvciBpcyBub3QgdXNlZnVsIGZvciBtb2RlbGluZyAoc2VwYXJhdGluZyB0aGUgR29vZHMgZnJvbSB0aGUgQmFkcykNCjAuMDIgdG8gMC4xLCB0aGVuIHRoZSBwcmVkaWN0b3IgaGFzIG9ubHkgYSB3ZWFrIHJlbGF0aW9uc2hpcC4NCjAuMSB0byAwLjMsIHRoZW4gdGhlIHByZWRpY3RvciBoYXMgYSBtZWRpdW0gc3RyZW5ndGggcmVsYXRpb25zaGlwLg0KMC4zIG9yIGhpZ2hlciwgdGhlbiB0aGUgcHJlZGljdG9yIGhhcyBhIHN0cm9uZyByZWxhdGlvbnNoaXAuDQpUaGF0IHdhcyBhYm91dCBJVi4gVGhlbiB3aGF0IGlzIFdlaWdodCBvZiBFdmlkZW5jZT8NCg0KV2VpZ2h0cyBvZiBldmlkZW5jZSBjYW4gYmUgdXNlZnVsIHRvIGZpbmQgb3V0IGhvdyBpbXBvcnRhbnQgYSBnaXZlbiBjYXRlZ29yaWNhbCB2YXJpYWJsZSBpcyBpbiBleHBsYWluaW5nIHRoZSDigJhldmVudHPigJkgKGNhbGxlZCDigJhHb29kc+KAmSBpbiBiZWxvdyB0YWJsZS4pDQoNCldPRSA9IGxuKCVnb29kIG9mIGFsbCBnb29kLyViYWQgb2YgYWxsIGJhZCkNCg0KSGVyZSBpcyB3aGF0IHRoZSBxdWFudHVtIG9mIEluZm9ybWF0aW9uIFZhbHVlIG1lYW5zOg0KDQpMZXNzIHRoYW4gMC4wMiwgdGhlbiB0aGUgcHJlZGljdG9yIGlzIG5vdCB1c2VmdWwgZm9yIG1vZGVsaW5nIChzZXBhcmF0aW5nIHRoZSBHb29kcyBmcm9tIHRoZSBCYWRzKQ0KMC4wMiB0byAwLjEsIHRoZW4gdGhlIHByZWRpY3RvciBoYXMgb25seSBhIHdlYWsgcmVsYXRpb25zaGlwLg0KMC4xIHRvIDAuMywgdGhlbiB0aGUgcHJlZGljdG9yIGhhcyBhIG1lZGl1bSBzdHJlbmd0aCByZWxhdGlvbnNoaXAuDQowLjMgb3IgaGlnaGVyLCB0aGVuIHRoZSBwcmVkaWN0b3IgaGFzIGEgc3Ryb25nIHJlbGF0aW9uc2hpcC4NClRoYXQgd2FzIGFib3V0IElWLiBUaGVuIHdoYXQgaXMgV2VpZ2h0IG9mIEV2aWRlbmNlPw0KDQpXZWlnaHRzIG9mIGV2aWRlbmNlIGNhbiBiZSB1c2VmdWwgdG8gZmluZCBvdXQgaG93IGltcG9ydGFudCBhIGdpdmVuIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGlzIGluIGV4cGxhaW5pbmcgdGhlIOKAmGV2ZW50c+KAmSAoY2FsbGVkIOKAmEdvb2Rz4oCZIGluIGJlbG93IHRhYmxlLikNCg0KDQpgYGB7cn0NCiMgVGhlIOKAmEluZm9ybWF0aW9uIFZhbHVl4oCZIG9mIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBjYW4gdGhlbiBiZSBkZXJpdmVkIGZyb20gdGhlIHJlc3BlY3RpdmUgV09FIHZhbHVlcy4NCiMgDQojIElWPz0/KHBlcmMgZ29vZCBvZiBhbGwgZ29vZHM/cGVyYyBiYWQgb2YgYWxsIGJhZHMpPyo/V09FDQojIA0KIyBUaGUg4oCYV09FVGFibGXigJkgYmVsb3cgZ2l2ZW4gdGhlIGNvbXB1dGF0aW9uIGluIG1vcmUgZGV0YWlsLg0KDQpXT0VUYWJsZShYPWlucHV0RGF0YVssICd3b3JrY2xhc3MnXSwgWT1pbnB1dERhdGEkaW5jb21lKQ0KYGBgDQoNClRoZSB0b3RhbCBJViBvZiBhIHZhcmlhYmxlIGlzIHRoZSBzdW0gb2YgSVbvv71zIG9mIGl0cyBjYXRlZ29yaWVzLg0KDQoNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMjICAgICAgICAgICAgICAgICAgICAgICAgIFRVVE9SSUFMIFVTSU5HIE9QVElNQUwgQklOTklORyAoKQ0KIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyA3LjAgV09FIEZST00gSW5mb3JtYXRpb24gUGNha2FnZQ0KDQojIyMjIDcuMSBXb2UgQ29udGludW91cyBWYXJpYWJsZXMgYW5kIEZhY3RvciBWYXJpYWJsZXMNCg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygiSW5mb3JtYXRpb24iKQ0KbGlicmFyeShJbmZvcm1hdGlvbikNCg0KIyBteWRhdGEgPC0gcmVhZC5jc3YoImh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9zdGF0L2RhdGEvYmluYXJ5LmNzdiIpDQojIA0KIyBoZWFkKG15ZGF0YSkNCg0KIyBTdG9yZSBEYXRhIExvY2FsbHkNCiMNCiN3cml0ZS5jc3YobXlkYXRhLCIuL0RhdGEvbXlkYXRhLmNzdiIpDQojDQojIFJlYWQgdGhlIGRhdGEgaW4gbG9jYWxseQ0KDQoNCm15ZGF0YT1yZWFkLmNzdigiLi9EYXRhL215ZGF0YS5jc3YiKQ0KDQpzdW1tYXJ5KG15ZGF0YSkNCg0KDQoNCmBgYA0KDQoNCiMjIyMgNy4yIE1ha2UgYW4gIGluZGVwZW5kZW50IFZhcmlhYmxlIGEgRmFjdG9yDQoNCkl0IGlzIGltcG9ydGFudCB0byBub3RlIGhlcmUgdGhlIG51bWJlciBvZiBiaW5zIGZvciAncmFuaycgdmFyaWFibGUuIFNpbmNlIGl0IGlzIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUsIHRoZSBudW1iZXIgb2YgYmlucyB3b3VsZCBiZSBhY2NvcmRpbmcgdG8gdW5pcXVlIHZhbHVlcyBvZiB0aGUgZmFjdG9yIHZhcmlhYmxlLiBUaGUgcGFyYW1ldGVyIGJpbnM9MTAgZG9lcyBub3Qgd29yayBmb3IgYSBmYWN0b3IgdmFyaWFibGUuDQoNCmBgYHtyfQ0KDQpteWRhdGEkcmFuaz1mYWN0b3IobXlkYXRhJHJhbmspDQoNCmBgYA0KDQojIyMjIDcuMyBDb21wdXRlIFRoZSBJbmZvIFZhbHVlDQoNCmBgYHtyIElWIH0NCg0KIyBNdXN0IFB1dCBJbiBhbiBlc3RpbWF0ZSBvZiB0aGUgQmlucyANCg0KSVYgPC0gY3JlYXRlX2luZm90YWJsZXMoZGF0YT1teWRhdGEsIHk9ImFkbWl0IiwgYmlucz0xMCwgcGFyYWxsZWw9RkFMU0UpDQoNCg0KSVYgPC0gY3JlYXRlX2luZm90YWJsZXMoZGF0YT1teWRhdGEsIHk9ImFkbWl0IiwgYmlucz0xMCwgcGFyYWxsZWw9VFJVRSkNCg0KDQojIFByb2R1Y2UgYW4gSVYgVmFsdWUgRnJhbWUNCg0KSVZfVmFsdWUgPSBkYXRhLmZyYW1lKElWJFN1bW1hcnkpDQoNCklWX1ZhbHVlDQoNCg0KDQpgYGANCg0KIyMjIyA3LjQgUHJpbnQgdGhlIElWIFRhYmxlcw0KDQpUbyBnZXQgV09FIHRhYmxlIGZvciB2YXJpYWJsZSBncmUsIHlvdSBuZWVkIHRvIGNhbGwgVGFibGVzIGxpc3QgZnJvbSBJViBsaXN0Lg0KDQpXZSBjYW4gZG8gdGhpcyBmb3IgYW55IG9mIHRoZSB2YXJpYWJsZXMNCg0KYGBge3J9DQpwcmludChJViRUYWJsZXMkZ3JlLCByb3cubmFtZXM9RkFMU0UpDQpwcmludChJViRUYWJsZXMkZ3BhLCByb3cubmFtZXM9RkFMU0UpDQpwcmludChJViRUYWJsZXMkcmFuaywgcm93Lm5hbWVzPUZBTFNFKQ0KcHJpbnQoSVYkVGFibGVzJFgsIHJvdy5uYW1lcz1GQUxTRSkNCmBgYA0KDQojIyMjIDcuNSBQdXQgVGhlIFRhYmxlcyBpZiBEYXRhZnJhbWVzDQoNClRvIGdldCBXT0UgdGFibGUgZm9yIHZhcmlhYmxlIGdyZSwgeW91IG5lZWQgdG8gY2FsbCBUYWJsZXMgbGlzdCBmcm9tIElWIGxpc3QuDQoNCldlIGNhbiBkbyB0aGlzIGZvciBhbnkgb2YgdGhlIHZhcmlhYmxlcw0KDQpgYGB7ciBEYXRhRnJhbWVzfQ0KZ3JlID0gZGF0YS5mcmFtZShJViRUYWJsZXMkZ3JlKQ0KDQpncGEgPSBkYXRhLmZyYW1lKElWJFRhYmxlcyRncGEpDQoNClggPSBkYXRhLmZyYW1lKElWJFRhYmxlcyRYKQ0KDQpyYW5rID0gZGF0YS5mcmFtZShJViRUYWJsZXMkcmFuaykNCg0KDQpgYGANCg0KDQojIyMjIDcuNiBQbG90IFdvZSBTY29yZXMgRm9yIDEgVmFyaWFibGUgDQoNCldlIGNhbiBwbG90IDEgYXQgYSB0aW1lDQoNCmBgYHtyICBQbG90IDEgfQ0KDQpwbG90X2luZm90YWJsZXMoSVYsICJncmUiKQ0KDQpgYGANCg0KDQojIyMjIDcuNyBQbG90IFdvZSBTY29yZXMgRm9yIE1hbnkgVmFyaWFibGUgDQoNCldlIGNhbiBwbG90IG1hbnkgYXQgYSB0aW1lDQoNCmBgYHtyIFBsb3QgTWFueSB9DQoNCnBsb3RfaW5mb3RhYmxlcyhJViwgSVYkU3VtbWFyeSRWYXJpYWJsZVsxOjRdLCBzYW1lX3NjYWxlPUZBTFNFKQ0KDQpgYGANCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIyA4LjAgIFdPRSBCaW1tbm5pbmcNCiMNClRoaXMgcGFja2FnZSBnZW5lcmF0ZXMsIHZpc3VhbGl6ZXMsIHRhYnVsYXRlcyBhbmQgZGVwbG95cyBhIHN1cGVydmlzZWQgd2VpZ2h0IG9mIGV2aWRlbmNlIChXT0UpIGJpbm5pbmcgb2YgdmFyaWFibGVzLg0KDQpEZXRhaWxzDQoNClRoaXMgcGFja2FnZSBnZW5lcmF0ZXMsIHZpc3VhbGl6ZXMsIHRhYnVsYXRlcyBhbmQgZGVwbG95cyBhIHN1cGVydmlzZWQgd2VpZ2h0IG9mIGV2aWRlbmNlIChXT0UpIGJpbm5pbmcgb2YgdmFyaWFibGVzLg0KDQpUaGUgcGFja2FnZSB3b2VCaW5uaW5nIGF1dG9tYXRlcyB0aGUgcHJvY2VzcyBvZiBiaW5uaW5nIG9mIG51bWVyaWMgdmFyaWFibGVzIGFuZCBmYWN0b3JzIHdpdGggcmVzcGVjdCB0byBhIGRpY2hvdG9tb3VzIHRhcmdldCB2YXJpYWJsZS4gQWRkaXRpb25hbGx5LCBpdCB2aXN1YWxpemVzIHRoZSByZWFsaXplZCBiaW5uaW5nIHNvbHV0aW9uLCB0YWJ1bGF0ZXMgaXQgYW5kIGRlcGxveXMgaXQgdG8gKG5ldykgZGF0YS4gQWxsIGZ1bmN0aW9ucyBjYW4gYmUgdXNlZCB3aXRoIHNpbmdsZSB2YXJpYWJsZXMgb3IgYW4gZW50aXJlIGRhdGEgZnJhbWUuDQoNCg0KIyMjIDguMSBCaW5uaW5nDQoNCndvZS5iaW5uaW5nIGdlbmVyYXRlcyBhIHN1cGVydmlzZWQgZmluZSBhbmQgY29hcnNlIGNsYXNzaW5nIG9mIG51bWVyaWMgdmFyaWFibGVzIGFuZCBmYWN0b3JzLg0KDQp3b2UudHJlZS5iaW5uaW5nIGdlbmVyYXRlcyBhIHN1cGVydmlzZWQgdHJlZS1saWtlIHNlZ21lbnRhdGlvbiBvZiBudW1lcmljIHZhcmlhYmxlcyBhbmQgZmFjdG9ycy4NCg0Kd29lLmJpbm5pbmcucGxvdCB2aXN1YWxpemVzIHRoZSBiaW5uaW5nIHNvbHV0aW9uIGdlbmVyYXRlZCBhbmQgc2F2ZWQgdmlhIHdvZS5iaW5uaW5nIG9yIHdvZS50cmVlLmJpbm5pbmcuDQoNCndvZS5iaW5uaW5nLnRhYmxlIHRhYnVsYXRlcyB0aGUgYmlubmluZyBzb2x1dGlvbiBnZW5lcmF0ZWQgYW5kIHNhdmVkIHZpYSB3b2UuYmlubmluZyBvciB3b2UudHJlZS5iaW5uaW5nLg0KDQp3b2UuYmlubmluZy5kZXBsb3kgZGVwbG95cyB0aGUgYmlubmluZyBzb2x1dGlvbiBnZW5lcmF0ZWQgYW5kIHNhdmVkIHZpYSB3b2UuYmlubmluZyBvciB3b2UudHJlZS5iaW5uaW5nIHRvIChuZXcpIGRhdGEuDQoNClJlZmVyZW5jZXMgDQoNClNpZGRpcWksIE4uIDIwMDY6IENyZWRpdCBSaXNrIFNjb3JlY2FyZHM6IERldmVsb3BpbmcgYW5kIEltcGxlbWVudGluZyBJbnRlbGxpZ2VudCBDcmVkaXQgU2NvcmluZy4gSG9ib2tlbiwgTmV3IEplcnNleTogSm9obiBXaWxleSAmIFNvbnMuDQoNCkFuZGVyc29uLCBSLiAyMDA3OiBUaGUgQ3JlZGl0IFNjb3JpbmcgVG9vbGtpdDogVGhlb3J5IGFuZCBQcmFjdGljZSBmb3IgUmV0YWlsIENyZWRpdCBSaXNrIE1hbmFnZW1lbnQgYW5kIERlY2lzaW9uIEF1dG9tYXRpb24uIE94Zm9yZCAvIE5ldyBZb3JrOiBPeGZvcmQgVW5pdmVyc2l0eSBQcmVzcy4NCg0KDQojIyMjIDguMS4xIEJpbm5pbmcgb2YgTnVtZXJpYyBWYXJpYWJsZXMNCg0KTnVtZXJpYyB2YXJpYWJsZXMgKGNvbnRpbnVvdXMgYW5kIG9yZGluYWwpIGFyZSBiaW5uZWQgYnkgbWVyZ2luZyBpbml0aWFsIGNsYXNzZXMgd2l0aCBzaW1pbGFyIGZyZXF1ZW5jaWVzLiBUaGUgbnVtYmVyIG9mIGluaXRpYWwgYmlucyByZXN1bHRzIGZyb20gdGhlIG1pbi5wZXJjLnRvdGFsIHBhcmFtZXRlcjogbWluLnBlcmMudG90YWwgd2lsbCByZXN1bHQgaW4gdHJ1bmMoMS9taW4ucGVyYy50b3RhbCkgaW5pdGlhbCBiaW5zLCB3aGVyZWJ5IHRydW5jIGlzIG5lZWRlZCB0byBndWFyYW50ZWUgYmlucyB3aXRoIHNpbWlsYXIgZnJlcXVlbmNpZXMuIEZvciBleGFtcGxlIG1pbi5wZXJjLnRvdGFsPTAuMDcgd2lsbCBjYXVzZSB0cnVuYygxNC4zKT0xNCBpbml0aWFsIGNsYXNzZXMuIE5leHQsIGlmIG1pbi5wZXJjLmNsYXNzPjAsIGJpbnMgd2l0aCBzcGFyc2UgdGFyZ2V0IGNsYXNzZXMgd2lsbCBiZSBtZXJnZWQgd2l0aCB0aGUgbmV4dCB1cHBlciBiaW4sIGFuZCBpbiBjYXNlIG9mIHRoZSBsYXN0IGJpbiB3aXRoIHRoZSBuZXh0IGxvd2VyIG9uZS4gTkFzIGhhdmUgdGhlaXIgb3duIGJpbiBhbmQgd2lsbCBub3QgYmUgbWVyZ2VkIHdpdGggb3RoZXJzLiBGaW5hbGx5IG5lYXJieSBiaW5zIHdpdGggbW9zdCBzaW1pbGFyIHdlaWdodCBvZiBldmlkZW5jZSAoV09FKSB2YWx1ZXMgYXJlIGpvaW5lZCBzdGVwIGJ5IHN0ZXAgdW50aWwgdGhlIGluZm9ybWF0aW9uIHZhbHVlIChJVikgZGVjcmVhc2VzIG1vcmUgdGhhbiBzcGVjaWZpZWQgYnkgYSBwZXJjZW50YWdlIHZhbHVlIChzdG9wLmxpbWl0IHBhcmFtZXRlcikgb3IgdW50aWwgdHdvIGJpbnMgYXJlIHJlYWNoZWQuDQoNCg0KIyMjIyA4LjEuMiBCaW5uaW5nIG9mIEZhY3RvcnMNCg0KRmFjdG9ycyAoY2F0ZWdvcmljYWwgdmFyaWFibGVzKSBhcmUgYmlubmVkIGJ5IG1lcmdpbmcgZmFjdG9yIGxldmVscy4gQXMgYSBzdGFydCBzcGFyc2UgbGV2ZWxzIChkZWZpbmVkIHZpYSB0aGUgbWluLnBlcmMudG90YWwgYW5kIG1pbi5wZXJjLmNsYXNzIHBhcmFtZXRlcnMpIGFyZSBtZXJnZWQgdG8gYSDigJhtaXNjZWxsYW5lb3Vz4oCZIGxldmVsOiBpZiBwb3NzaWJsZSwgcmVzcGVjdGl2ZSBsZXZlbHMgKGluY2x1ZGluZyBzcGFyc2UgTkFzKSBhcmUgYnVuZGxlZCBhcyDigJhtaXNjLiBsZXZlbCBwb3Mu4oCZIChhc3NvY2lhdGVkIHdpdGggcG9zaXRpdmUgV09FIHZhbHVlcyksIHJlc3BlY3RpdmVseSBhcyDigJhtaXNjLiBsZXZlbCBuZWcu4oCZIChhc3NvY2lhdGVkIHdpdGggbmVnYXRpdmUgV09FIHZhbHVlcykuIEluIGNhc2UgYSBtaXNjLiBsZXZlbCBjb250YWlucyBvbmx5IE5BcyBpdCB3aWxsIGJlIG5hbWVkIOKAmE1pc3NpbmfigJkuIEFmdGVyd2FyZHMgbGV2ZWxzIHdpdGggc2ltaWxhciBXT0UgdmFsdWVzIGFyZSBqb2luZWQgc3RlcCBieSBzdGVwIHVudGlsIHRoZSBpbmZvcm1hdGlvbiB2YWx1ZSAoSVYpIGRlY3JlYXNlcyBtb3JlIHRoYW4gc3BlY2lmaWVkIGJ5IGEgcGVyY2VudGFnZSB2YWx1ZSAoc3RvcC5saW1pdCBwYXJhbWV0ZXIpIG9yIHVudGlsIHR3byBiaW5zIGFyZSByZWFjaGVkLg0KDQojIyMjIDguMS4zIEFkanVzdG1lbnQgb2YgMCBGcmVxdWVuY2llcw0KDQpJbiBjYXNlIHRoZSBjcm9zc3RhYiBvZiB0aGUgYmlucyB3aXRoIHRoZSB0YXJnZXQgY2xhc3NlcyBjb250YWlucyBmcmVxdWVuY2llcyA9IDAgdGhlIGNvbHVtbiBwZXJjZW50YWdlcyBhcmUgYWRqdXN0ZWQgdG8gYmUgYWJsZSB0byBjb21wdXRlIHRoZSBXT0UgYW5kIElWIHZhbHVlczogdGhlIG9mZnNldCAwLjAwMDEgKD0wLjAxJSkgaXMgYWRkZWQgdG8gZWFjaCBjb2x1bW4gcGVyY2VudGFnZSBjZWxsIGFuZCB0aGUgY29sdW1uIHBlcmNlbnRhZ2VzIGFyZSByZWNvbXB1dGVkIHRoZW4uIFRoaXMgYWxsb3dzIGNvbnNpZGVyaW5nIGJpbnMgYXNzb2NpYXRlZCB3aXRoIG9uZSB0YXJnZXQgY2xhc3Mgb25seSwgYnV0IG1heSBjYXVzZSBleHRyZW1lIFdPRSB2YWx1ZXMgZm9yIHRoZXNlIGJpbnMuIElmIGEgY29ycmVjdGlvbiBpcyBub3QgYXBwcm9wcmlhdGUgY2hvb3NlIG1pbi5wZXJjLmNsYXNzPjA7IGJpbnMgd2l0aCBzcGFyc2UgdGFyZ2V0IGNsYXNzZXMgd2lsbCBiZSBtZXJnZWQgdGhlbiBiZWZvcmUgY29tcHV0aW5nIGFueSBXT0Ugb3IgSVYgdmFsdWUuDQoNCiMjIyMgOC4xLjQgU2VlIEFsc28NCg0KT3RoZXIgYmlubmluZyBmdW5jdGlvbnM6IHdvZS50cmVlLmJpbm5pbmcNCg0KDQojIyMjIDguMS41IEJpbm5pbmcgRHVyYXRpb24gb24gVHJlZQ0KDQp3b2UudHJlZS5iaW5uaW5nIGdlbmVyYXRlcyBhIHN1cGVydmlzZWQgdHJlZS1saWtlIHNlZ21lbnRhdGlvbiBvZiBudW1lcmljIHZhcmlhYmxlcyBhbmQgZmFjdG9ycyB3aXRoIHJlc3BlY3QgdG8gYSBkaWNob3RvbW91cyB0YXJnZXQgdmFyaWFibGUuIEl0cyBwYXJhbWV0ZXJzIHByb3ZpZGUgZmxleGliaWxpdHkgaW4gZmluZGluZyBhIGJpbm5pbmcgdGhhdCBmaXRzIHNwZWNpZmljIGRhdGEgY2hhcmFjdGVyaXN0aWNzIGFuZCBwcmFjdGljYWwgbmVlZHMuDQoNCmh0dHBzOi8vcmRyci5pby9jcmFuL3dvZUJpbm5pbmcvbWFuL3dvZS5iaW5uaW5nLmh0bWwgICMgU2hpZnQgQ2xpY2sgdG8gb3BlbiBsaW5rIA0KDQoNCiMjIyA4LjIgRXhhbXBsZXMNCg0KYGBge3J9DQpsaWJyYXJ5KHdvZUJpbm5pbmcpDQoNCiNodHRwczovL3JkcnIuaW8vY3Jhbi93b2VCaW5uaW5nL21hbi93b2UuYmlubmluZy5odG1sDQoNCmxpYnJhcnkodGlkeXZlcnNlKQ0KI2xpYnJhcnkobW1hcCkNCg0KZGF0YShnZXJtYW5jcmVkaXQpDQoNCmdsaW1wc2UoZ2VybWFuY3JlZGl0KQ0KDQoNCiNnZXJtYW5jcmVkaXQ9cmVhZC5jc3YoIi4vRGF0YS9nZXJtYW5fY3JlZGl0LmNzdiIpDQojIExvYWQgR2VybWFuIGNyZWRpdCBkYXRhIGFuZCBjcmVhdGUgc3Vic2V0DQojZGF0YShnZXJtYW5jcmVkaXQpDQpkZiA8LSBnZXJtYW5jcmVkaXRbLCBjKCdjcmVkaXRhYmlsaXR5JywgJ2NyZWRpdC5hbW91bnQnLCAnZHVyYXRpb24uaW4ubW9udGgnLA0KICAgICAgICAgICAgICAgICAgJ3NhdmluZ3MuYWNjb3VudC5hbmQuYm9uZHMnLCAncHVycG9zZScpXQ0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojICAgICAgICAgIEhPVyBUTyBCSU4gQSBTSU5HTEUgTlVNRVJJQ0FMIFZBUklBQkxFIFVTSU5HIENVU1RPTUlTRUQgU0VUVElOR1MgDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgQmluIGEgc2luZ2xlIG51bWVyaWMgdmFyaWFibGUNCmJpbm5pbmdfZHVyYXRpb24gPC0gd29lLmJpbm5pbmcoZGYsICdjcmVkaXRhYmlsaXR5JywgJ2R1cmF0aW9uLmluLm1vbnRoJywNCiAgICAgICAgICAgICAgICAgICAgICAgbWluLnBlcmMudG90YWw9MC4wNSwgbWluLnBlcmMuY2xhc3M9MC4wMSwNCiAgICAgICAgICAgICAgICAgICAgICAgc3RvcC5saW1pdD0wLjEsIGV2ZW50LmNsYXNzPSdiYWQnKQ0KDQp3b2UuYmlubmluZy5wbG90KGJpbm5pbmdfZHVyYXRpb24pDQoNCiMgVGFidWxhdGUgdGhlIGJpbm5lZCB2YXJpYWJsZXMNCnRhYnVsYXRlLmJpbm5pbmcuZHVyYXRpb24gPC0gd29lLmJpbm5pbmcudGFibGUoYmlubmluZ19kdXJhdGlvbikNCg0KIyBFeHRyYWN0IHRoZSB0YWJ1bGF0aW9uDQp0YWJ1bGF0ZS5iaW5uaW5nLmR1cmF0aW9uJGBXT0UgVGFibGUgZm9yIGR1cmF0aW9uLmluLm1vbnRoYA0KDQojIFBsb3QgVGhlIEJpbm5pbmcNCndvZS5iaW5uaW5nLnBsb3QoYmlubmluZ19kdXJhdGlvbikNCg0KI0xldHMgZG8gVHJlZSBCaW5uaW5nIG9uIGEgU2luZ2xlIE51bWVyaWMgVmFyaWFibGUNCmJpbm5pbmdfZHVyYXRpb25fdHJlZSA8LSB3b2UudHJlZS5iaW5uaW5nKGRmLCAnY3JlZGl0YWJpbGl0eScsICdkdXJhdGlvbi5pbi5tb250aCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4ucGVyYy50b3RhbD0wLjAxLCBtaW4ucGVyYy5jbGFzcz0wLjAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RvcC5saW1pdD0wLjEsIGV2ZW50LmNsYXNzPSdiYWQnKQ0KDQp3b2UuYmlubmluZy5wbG90KGJpbm5pbmdfZHVyYXRpb25fdHJlZSkgICMgVmVyeSBzaW1pbGFyIHJlc3VsdCBhcyBhYm92ZSANCg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojICAgICAgICAgIEhPVyBUTyBCSU4gQSBTSU5HTEUgRkFDVE9SIFZBUklBQkxFIFVTSU5HIENVU1RPTUlTRUQgU0VUVElOR1MgDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgQmluIGEgc2luZ2xlIGZhY3Rvcg0KYmlubmluZ19wdXJwb3NlIDwtIHdvZS5iaW5uaW5nKGRmLCAnY3JlZGl0YWJpbGl0eScsICdwdXJwb3NlJywNCiAgICAgICAgICAgICAgICAgICAgICAgbWluLnBlcmMudG90YWw9MC4wNSwgbWluLnBlcmMuY2xhc3M9MCwgc3RvcC5saW1pdD0wLjEsDQogICAgICAgICAgICAgICAgICAgICAgIGFiYnJldi5mYWN0LmxldmVscz01MCwgZXZlbnQuY2xhc3M9J2JhZCcpDQoNCndvZS5iaW5uaW5nLnBsb3QoYmlubmluZ19wdXJwb3NlKQ0KDQojIFRhYnVsYXRlIHRoZSBiaW5uZWQgdmFyaWFibGVzDQp0YWJ1bGF0ZS5iaW5uaW5nLnB1cnBvc2UgPC0gd29lLmJpbm5pbmcudGFibGUoYmlubmluZ19wdXJwb3NlKQ0KDQojIEV4dHJhY3QgdGhlIHRhYnVsYXRpb24NCnRhYnVsYXRlLmJpbm5pbmcucHVycG9zZSRgV09FIFRhYmxlIGZvciBwdXJwb3NlYA0KDQojTGV0cyBkbyBUcmVlIEJpbm5pbmcgb24gYSBTaW5nbGUgTnVtZXJpYyBWYXJpYWJsZQ0KYmlubmluZ19wdXJwb3NlX3RyZWUgPC0gd29lLnRyZWUuYmlubmluZyhkZiwgJ2NyZWRpdGFiaWxpdHknLCAncHVycG9zZScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4ucGVyYy50b3RhbD0wLjAxLCBtaW4ucGVyYy5jbGFzcz0wLjAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RvcC5saW1pdD0wLjEsIGV2ZW50LmNsYXNzPSdiYWQnKQ0KDQp3b2UuYmlubmluZy5wbG90KGJpbm5pbmdfcHVycG9zZV90cmVlKSAgIyBWZXJ5IHNpbWlsYXIgcmVzdWx0IGFzIGFib3ZlIA0KDQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgICAgICAgICAgSE9XIFRPIEJJTiBBIE5VTUVSSUMgQU5EICBGQUNUT1IgVkFSSUFCTEUgVVNJTkcgREVGQVVMVCAgU0VUVElOR1MgKDEgb2YgRWFjaCkgDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KIyBCaW4gdHdvIHZhcmlhYmxlcyAob25lIG51bWVyaWMgYW5kIG9uZSBmYWN0b3IpDQojIHdpdGggZGVmYXVsdCBwYXJhbWV0ZXIgc2V0dGluZ3MNCmJpbm5pbmdfbWl4ZWQgPC0gd29lLmJpbm5pbmcoZGYsICdjcmVkaXRhYmlsaXR5JywgYygnY3JlZGl0LmFtb3VudCcsJ3B1cnBvc2UnKSkNCg0Kd29lLmJpbm5pbmcucGxvdChiaW5uaW5nX21peGVkKQ0KDQojIFRhYnVsYXRlIHRoZSBCaW5uaW5nIE1peGVkIA0KDQojIFRhYnVsYXRlIHRoZSBiaW5uZWQgdmFyaWFibGVzDQp0YWJ1bGF0ZS5iaW5uaW5nLm1peGVkPC0gd29lLmJpbm5pbmcudGFibGUoYmlubmluZ19taXhlZCkNCg0KIyBFeHRyYWN0IHRoZSB0YWJ1bGF0aW9uDQojDQojIEZvciBwdXJwb3NlIChjYXRlZ29yaWNhbCkNCg0KdGFidWxhdGUuYmlubmluZy5taXhlZCRgV09FIFRhYmxlIGZvciBwdXJwb3NlYA0KDQojIEZvciBjcmVkaXQuYW1vdW50XA0KIw0KdGFidWxhdGUuYmlubmluZy5taXhlZCRgV09FIFRhYmxlIGZvciBjcmVkaXQuYW1vdW50YA0KIw0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojICAgICAgICAgICAgICAgICAgIEJJTk5JTkcgQU5EIERFUExPWUlORyBBTEwgVEhFIFZBUklBQkxFUyBPRiBUSEUgQUJPVkUgREFUQSBGUkFNRSAoNCkgKCBUSEVSRSBBUkUgTUFOWSBNT1JFICkgDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMzIyMjIyMjDQoNCiMgQmluIGFsbCB2YXJpYWJsZXMgb2YgdGhlIGRhdGEgZnJhbWUgKGFwYXJ0IGZyb20gdGhlIHRhcmdldCB2YXJpYWJsZSkNCiMgd2l0aCBkZWZhdWx0IHBhcmFtZXRlciBzZXR0aW5ncw0KYmlubmluZ19hbGwgPC0gd29lLmJpbm5pbmcoZGYsICdjcmVkaXRhYmlsaXR5JywgZGYpDQoNCiMgUGxvdCB0aGUgYmlubmVkIHZhcmlhYmxlcw0Kd29lLmJpbm5pbmcucGxvdChiaW5uaW5nX2FsbCkNCg0KIyBUYWJ1bGF0ZSB0aGUgYmlubmVkIHZhcmlhYmxlcw0KdGFidWxhdGUuYmlubmluZy5hbGwgPC0gd29lLmJpbm5pbmcudGFibGUoYmlubmluZ19hbGwpDQojc3RyKHRhYnVsYXRlLmJpbm5pbmcpDQoNCiMgRXh0cmFjdCB0aGUgV29FIGZvciBhbGxsIHRoZSBWYXJpYWJsZXMgaW4gc2VwYXJ0YWUgREZzDQoNCiMgRHVyYXRpb24NCnRhYnVsYXRlLmJpbm5pbmcuYWxsJGBXT0UgVGFibGUgZm9yIGR1cmF0aW9uLmluLm1vbnRoYA0KDQojIFNhdmluZ3MNCnRhYnVsYXRlLmJpbm5pbmcuYWxsJGBXT0UgVGFibGUgZm9yIHNhdmluZ3MuYWNjb3VudC5hbmQuYm9uZHNgDQoNCiMgUHVycG9zZQ0KdGFidWxhdGUuYmlubmluZy5hbGwkYFdPRSBUYWJsZSBmb3IgcHVycG9zZWANCg0KIyBDcmVkaXQgQW1vdW50DQp0YWJ1bGF0ZS5iaW5uaW5nLmFsbCRgV09FIFRhYmxlIGZvciBjcmVkaXQuYW1vdW50YA0KDQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgICAgICAgICAgICAgICAgICAgICAgREVQTE9ZIFRPIERBVEEgRlJBTUUNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCgkJCQkJCSAgDQojIERlcGxveSB0aGUgYmlubmluZyBzb2x1dGlvbiB0byB0aGUgZGF0YSBmcmFtZQ0KIyAoaS5lLiBhZGQgYmlubmVkIHZhcmlhYmxlcyBhbmQgY29ycmVzcG9uZGluZyBXT0UgdmFyaWFibGVzKQ0KZGYud2l0aC5iaW5uZWQudmFycy5hZGRlZCA8LSB3b2UuYmlubmluZy5kZXBsb3koZGYsIGJpbm5pbmdfYWxsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZGQud29lLm9yLmR1bS52YXI9J3dvZScpDQoNCmBgYA0KDQoNCiMjIyA5LjAgIFBvc3QgUHJvY2Vzc2luZyBvbiBUaGUgV29FIEFib3ZlDQoNCiMjIyMgOS4xIENvcnJlbGF0aW9uIEJldHdlZW4gQ2F0ZWdwb3JpY2FsIGFuZCBOdW1lcmljIFZhcmlhYmxlcw0KDQpUaGUgV29FIGlzIG5vdyBhIGNvbnRpbnVvdXMgdW5pdCBzbyB3ZSBjYW4gdGVsbCB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBjb250dW91cyBhbmQgY2F0ZWdvcmljYWwgVW5pdHMNCg0KV2UgY29uZHVjdCBDb3JyZWxhdGlvbiB1c2luZyB0aGUgZGF0YSBmcmFtZSBjb21waWxlZCBpbiB0aGUgV29FIEJpbm5pbmcgQWJvdmUNCg0KIyMjIyMgOS4xYSBMZXRzIGdsaW1wc2UgdGhlIERGIGFuZCBnZXQgYSBTYW1wbGUgDQoNCg0KYGBge3IgZ2xpbXBzZSBmcmFtZX0NCmdsaW1wc2UoZGYud2l0aC5iaW5uZWQudmFycy5hZGRlZCkNCg0KYGBgDQoNCg0KDQoNCmBgYHtyIENvcnJlbGF0aW9ufQ0KbGlicmFyeSgiY29ycnBsb3QiKQ0KDQpsaWJyYXJ5KGRwbHlyKQ0KDQoNCg0KZGY9ZGYud2l0aC5iaW5uZWQudmFycy5hZGRlZCU+JXNlbGVjdChjcmVkaXRhYmlsaXR5LGNvbnRhaW5zKCJ3b2UiKSkNCg0KZGYkY3JlZGl0YWJpbGl0eT1pZmVsc2UoZGYkY3JlZGl0YWJpbGl0eT09ImJhZCIsMSwwKQ0KDQoNCg0KDQpjb3JfbWF0X2RmID0gY29yKGRmICAsdXNlPSJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiLCBtZXRob2QgPSAicGVhcnNvbiIpDQpjb3JycGxvdChjb3JfbWF0X2RmLCBtZXRob2QgPSAiY29sb3IiLCANCiAgICAgICAgIHR5cGUgPSAidXBwZXIiLCBvcmRlciA9ICJvcmlnaW5hbCIsIG51bWJlci5jZXggPSAuNywNCiAgICAgICAgIGFkZENvZWYuY29sID0gImJsYWNrIiwgIyBBZGQgY29lZmZpY2llbnQgb2YgY29ycmVsYXRpb24NCiAgICAgICAgIHRsLmNvbCA9ICJibGFjayIsIHRsLnNydCA9IDkwLCAjIFRleHQgbGFiZWwgY29sb3IgYW5kIHJvdGF0aW9uDQogICAgICAgICAgICAgICAgICAjIGhpZGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgb24gdGhlIHByaW5jaXBhbCBkaWFnb25hbA0KICAgICAgICAgZGlhZyA9IFRSVUUpDQoNCg0KDQpgYGANCg0KDQpSZXN1bHQgaXMgYnkgY29ycmVsYXRpb24gd2l0aCAxLzAgaXQgc2VlbXMgdGhhdCB0aGVyZSBpcyBhbiBvcmRlciBvZiBpbXBvcnRhbmNlICwgYnV0IGxldHMgY2hlY2sgdGhpcyBieSB2YXJpYWJsZSBpbXBvcnRhbmNlIGFuZCAyIHdheXMsIFZpYSBXT0UgYW5kIEJ5IEZhY3RvcnMgLCBpZSB0aGUgYmlubmluZw0KDQoNCiMjIyA5LjIgVmFyaWFibGUgaW1wb3J0YW5jZSBCeSBXb2UNCg0KV2Ugd2lsbCB1c2UgUkZFIHRvIGdhdWdlIHZhcmlhYmxlIGltcG9ydGFuY2Ugb2YgdGhlIGFib3ZlIGZyYW1lIA0KDQpgYGB7cn0NCg0KbGlicmFyeSgnQm9ydXRhJykNCnNldC5zZWVkKDE0MykNCmJvcnV0YV9vdXRwdXRfd29lID0gQm9ydXRhKGNyZWRpdGFiaWxpdHkgfiAuLCBkYXRhPW5hLm9taXQoZGYpLCBkb1RyYWNlPTApIA0KDQpTaWduaWZpY2FudF92YXJzID0gZ2V0U2VsZWN0ZWRBdHRyaWJ1dGVzKGJvcnV0YV9vdXRwdXRfd29lLCB3aXRoVGVudGF0aXZlID0gVFJVRSkNClNpZ25pZmljYW50X3ZhcnMNCg0KIyBOZWVkZSBSb3VnaGZpeCBjb252ZXJzaW9uag0Kcm91Z2hGaXhNb2QgPC0gVGVudGF0aXZlUm91Z2hGaXgoYm9ydXRhX291dHB1dF93b2UpDQpib3J1dGFfc2lnbmlmIDwtIGdldFNlbGVjdGVkQXR0cmlidXRlcyhyb3VnaEZpeE1vZCkNCnByaW50KGJvcnV0YV9zaWduaWYpDQoNCnBsb3QoYm9ydXRhX291dHB1dF93b2UsIGNleC5heGlzPS43LCBsYXM9MiwgeGxhYj0iIiwgbWFpbj0iVmFyaWFibGUgSW1wb3J0YW5jZSBXb2UiKSAgDQoNCg0KDQppbXBzID0gYXR0U3RhdHMocm91Z2hGaXhNb2QpDQppbXBzMiA9IGltcHNbaW1wcyRkZWNpc2lvbiAhPSAnUmVqZWN0ZWQnLCBjKCdtZWFuSW1wJywgJ2RlY2lzaW9uJyldDQpoZWFkKGltcHMyW29yZGVyKC1pbXBzMiRtZWFuSW1wKSwgXSwxMCkgICMgZGVzY2VuZGluZyBzb3J0DQoNCiMgQWdyZWVzIHdpdGggQ29ycmVsYXRpb24NCg0KIw0KIyBOb3cgd2UgbG9vayBhdCB0aGUgYmlubmluZyBiRmFjdG9ycw0KDQpkZl9mYWN0b3JzPWRmLndpdGguYmlubmVkLnZhcnMuYWRkZWQlPiVzZWxlY3QoY3JlZGl0YWJpbGl0eSxjb250YWlucygiYmlubmVkIikpJT4lc2VsZWN0X2lmKGlzLmZhY3RvcikNCg0KZ2xpbXBzZShkZl9mYWN0b3JzKQ0KDQpzZXQuc2VlZCgxNDMpDQpib3J1dGFfb3V0cHV0X2Jpbm5lZCA9IEJvcnV0YShjcmVkaXRhYmlsaXR5IH4gLiwgZGF0YT1uYS5vbWl0KGRmX2ZhY3RvcnMpLCBkb1RyYWNlPTApIA0KDQpTaWduaWZpY2FudF92YXJzID0gZ2V0U2VsZWN0ZWRBdHRyaWJ1dGVzKGJvcnV0YV9vdXRwdXRfYmlubmVkLCB3aXRoVGVudGF0aXZlID0gVFJVRSkNClNpZ25pZmljYW50X3ZhcnMNCg0KIyBOZWVkZSBSb3VnaGZpeCBjb252ZXJzaW9uag0Kcm91Z2hGaXhNb2QgPC0gVGVudGF0aXZlUm91Z2hGaXgoYm9ydXRhX291dHB1dF9iaW5uZWQpDQpib3J1dGFfc2lnbmlmIDwtIGdldFNlbGVjdGVkQXR0cmlidXRlcyhyb3VnaEZpeE1vZCkNCnByaW50KGJvcnV0YV9zaWduaWYpDQoNCg0KDQoNCg0KaW1wcyA9IGF0dFN0YXRzKHJvdWdoRml4TW9kKQ0KaW1wczIgPSBpbXBzW2ltcHMkZGVjaXNpb24gIT0gJ1JlamVjdGVkJywgYygnbWVhbkltcCcsICdkZWNpc2lvbicpXQ0KaGVhZChpbXBzMltvcmRlcigtaW1wczIkbWVhbkltcCksIF0sMTApICAjIGRlc2NlbmRpbmcgc29ydA0KDQoNCnBsb3QoYm9ydXRhX291dHB1dF9iaW5uZWQsIGNleC5heGlzPS43LCBsYXM9MiwgeGxhYj0iIiwgbWFpbj0iVmFyaWFibGUgSW1wb3J0YW5jZSBCaW5uZWQiKSAgDQoNCg0KYGBgDQoNCg0KDQoNCg0KDQojIyMgOS4zIERlY2lzaW9uIFRyZWVzDQoNCldlIE5vdyB1c2UgdGhlIEJpbm5zIHRvIGRvIHRoZSBkZWNpc2lvbiBUcmVlcw0KDQpVc2UgcnBhcnQgDQoNCmBgYHtyIHJwYXJ0fQ0KDQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KDQpzZXQuc2VlZCgxMjMpDQp0cmVlIDwtIHJwYXJ0KGNyZWRpdGFiaWxpdHkgfiAuLCBkYXRhPW5hLm9taXQoZGZfZmFjdG9ycyksY29udHJvbCA9IHJwYXJ0LmNvbnRyb2woY3AgPSAwLjAwMDEpKQ0KDQp0cmVlDQoNCnJwYXJ0LnBsb3QodHJlZSkNCg0KDQoNCg0KDQoNCg0KYGBgDQoNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojICAgDQojIyMgMTAuMCAgRXhjZXJwdHMgRnJvbSBDcmVkaXQgUmlzayBNb2RlbGxpbmcNCg0KDQojIyMgMTAuMWEgQ3JlZGl0YWJpbGl0eSBWcyBEdXJhdGlvbiBPZiBMb2FuDQoNCmBgYHtyIENyb3NzdGFibGUxfQ0KbGlicmFyeShnbW9kZWxzKQ0KbGlicmFyeShncmlkRXh0cmEpDQoNCiMjIExldHMgUGxvdCBTb21lIENyb3NzVGFibGVzIC0gTm9ybWFsbHkgY2F0ZWdvcmljYWwgRGF0YSB0byBTaG93IFJlbGF0aW9uc2hpcCBpbiBhIFRhYmxlDQojICBUcG8gaGVscCBVbmRlcnN0YW5kIHRoZSBUcmVlDQojDQpDcm9zc1RhYmxlKGRmX2ZhY3RvcnMkZHVyYXRpb24uaW4ubW9udGguYmlubmVkLGRmX2ZhY3RvcnMkY3JlZGl0YWJpbGl0eSwgcHJvcC5yID0gVFJVRSwNCnByb3AuYyA9IEZBTFNFLCBwcm9wLnQgPSBGQUxTRSwgcHJvcC5jaGlzcSA9IEZBTFNFKQ0KDQpjcm9zc19DcmVkVnNEdXJhdGlvbj1Dcm9zc1RhYmxlKGRmX2ZhY3RvcnMkZHVyYXRpb24uaW4ubW9udGguYmlubmVkLGRmX2ZhY3RvcnMkY3JlZGl0YWJpbGl0eSwsIHByb3AuciA9IFRSVUUsDQpwcm9wLmMgPSBGQUxTRSwgcHJvcC50ID0gRkFMU0UsIHByb3AuY2hpc3EgPSBUUlVFLGNoaXNxID0gVFJVRSkNCg0KcGxvdChjcm9zc19DcmVkVnNEdXJhdGlvbiRwcm9wLnRibCxjb2w9YygicmVkIiwiZ3JlZW4iKSxtYWluPSJDcmVkaXRhYmlsaXR5IFZzIER1cmF0aW9uIix4bGFiID0gIkR1cmF0aW9uIix5bGFiID0gIkNyZWRpdGFiaWxpdHkiKQ0KDQpgYGANCg0KQ29uY2x1c2lvbjogVGhlIGxvbmdlciB0aGUgZHVyYXRpb24gdGhlIHdvcnNlIHRoZSBjcmVkaXRhYmlsaXR5DQoNCg0KIyMjIDEwLjFiIENyZWRpdGFiaWxpdHkgVnMgQW1vdW50IE9mIExvYW4NCg0KYGBge3IgQ3Jvc3N0YWJsZTJ9DQpsaWJyYXJ5KGdtb2RlbHMpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCg0KIyMgTGV0cyBQbG90IFNvbWUgQ3Jvc3NUYWJsZXMgLSBOb3JtYWxseSBjYXRlZ29yaWNhbCBEYXRhIHRvIFNob3cgUmVsYXRpb25zaGlwIGluIGEgVGFibGUNCiMgIFRwbyBoZWxwIFVuZGVyc3RhbmQgdGhlIFRyZWUNCiMNCkNyb3NzVGFibGUoZGZfZmFjdG9ycyRjcmVkaXQuYW1vdW50LmJpbm5lZCxkZl9mYWN0b3JzJGNyZWRpdGFiaWxpdHksIHByb3AuciA9IFRSVUUsDQpwcm9wLmMgPSBGQUxTRSwgcHJvcC50ID0gRkFMU0UsIHByb3AuY2hpc3EgPSBGQUxTRSkNCg0KY3Jvc3NfQ3JlZFZzQW1vdW50PUNyb3NzVGFibGUoZGZfZmFjdG9ycyRjcmVkaXQuYW1vdW50LmJpbm5lZCxkZl9mYWN0b3JzJGNyZWRpdGFiaWxpdHksLCBwcm9wLnIgPSBUUlVFLA0KcHJvcC5jID0gRkFMU0UsIHByb3AudCA9IEZBTFNFLCBwcm9wLmNoaXNxID0gVFJVRSxjaGlzcSA9IFRSVUUpDQoNCnBsb3QoY3Jvc3NfQ3JlZFZzQW1vdW50JHByb3AudGJsLGNvbD1jKCJyZWQiLCJncmVlbiIpLG1haW49IkNyZWRpdGFiaWxpdHkgVnMgQW1vdW50Iix4bGFiID0gIkFtb3VudF9CaW4iLHlsYWIgPSAiQ3JlZGl0YWJpbGl0eSIpDQoNCmBgYA0KDQojIyMgMTAuMWMgQ3JlZGl0YWJpbGl0eSBWcyBTYXZpbmdzDQoNCmBgYHtyIENyb3NzdGFibGUzfQ0KbGlicmFyeShnbW9kZWxzKQ0KbGlicmFyeShncmlkRXh0cmEpDQoNCiMjIExldHMgUGxvdCBTb21lIENyb3NzVGFibGVzIC0gTm9ybWFsbHkgY2F0ZWdvcmljYWwgRGF0YSB0byBTaG93IFJlbGF0aW9uc2hpcCBpbiBhIFRhYmxlDQojICBUcG8gaGVscCBVbmRlcnN0YW5kIHRoZSBUcmVlDQojDQpDcm9zc1RhYmxlKGRmX2ZhY3RvcnMkc2F2aW5ncy5hY2NvdW50LmFuZC5ib25kcy5iaW5uZWQsZGZfZmFjdG9ycyRjcmVkaXRhYmlsaXR5LCBwcm9wLnIgPSBUUlVFLA0KcHJvcC5jID0gRkFMU0UsIHByb3AudCA9IEZBTFNFLCBwcm9wLmNoaXNxID0gRkFMU0UpDQoNCmNyb3NzX0NyZWRWc1NhdmluZ3M9Q3Jvc3NUYWJsZShkZl9mYWN0b3JzJHNhdmluZ3MuYWNjb3VudC5hbmQuYm9uZHMuYmlubmVkLGRmX2ZhY3RvcnMkY3JlZGl0YWJpbGl0eSwsIHByb3AuciA9IFRSVUUsDQpwcm9wLmMgPSBGQUxTRSwgcHJvcC50ID0gRkFMU0UsIHByb3AuY2hpc3EgPSBUUlVFLGNoaXNxID0gVFJVRSkNCg0KcGxvdChjcm9zc19DcmVkVnNTYXZpbmdzJHByb3AudGJsLGNvbD1jKCJyZWQiLCJncmVlbiIpLG1haW49IkNyZWRpdGFiaWxpdHkgVnMgU2F2aW5ncyIseGxhYiA9ICJTYXZpbmdzX0JpbiIseWxhYiA9ICJDcmVkaXRhYmlsaXR5IikNCg0KYGBgDQoNCiMjIyAxMC4xZCBDcmVkaXRhYmlsaXR5IFZzIFB1cnBvc2UNCg0KYGBge3IgQ3Jvc3N0YWJsZTR9DQpsaWJyYXJ5KGdtb2RlbHMpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCg0KIyMgTGV0cyBQbG90IFNvbWUgQ3Jvc3NUYWJsZXMgLSBOb3JtYWxseSBjYXRlZ29yaWNhbCBEYXRhIHRvIFNob3cgUmVsYXRpb25zaGlwIGluIGEgVGFibGUNCiMgIFRwbyBoZWxwIFVuZGVyc3RhbmQgdGhlIFRyZWUNCiMNCkNyb3NzVGFibGUoZGZfZmFjdG9ycyRwdXJwb3NlLmJpbm5lZCxkZl9mYWN0b3JzJGNyZWRpdGFiaWxpdHksIHByb3AuciA9IFRSVUUsDQpwcm9wLmMgPSBGQUxTRSwgcHJvcC50ID0gRkFMU0UsIHByb3AuY2hpc3EgPSBGQUxTRSkNCg0KY3Jvc3NfQ3JlZFZzUHVycG9zZT1Dcm9zc1RhYmxlKGRmX2ZhY3RvcnMkcHVycG9zZS5iaW5uZWQsZGZfZmFjdG9ycyRjcmVkaXRhYmlsaXR5LCwgcHJvcC5yID0gVFJVRSwNCnByb3AuYyA9IEZBTFNFLCBwcm9wLnQgPSBGQUxTRSwgcHJvcC5jaGlzcSA9IFRSVUUsY2hpc3EgPSBUUlVFKQ0KDQpwbG90KGNyb3NzX0NyZWRWc1B1cnBvc2UkcHJvcC50YmwsY29sPWMoInJlZCIsImdyZWVuIiksbWFpbj0iQ3JlZGl0YWJpbGl0eSBWcyBQdXJwb3NlIix4bGFiID0gIlNhdmluZ3NfQmluIix5bGFiID0gIkNyZWRpdGFiaWxpdHkiKQ0KDQpgYGANCg0KDQoNCiMjIyAxMC4yYSBEZWNpc2lvbiBUcmVlcyBSZWZlcmVuY2UgDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KDQpzZXQuc2VlZCgxMjMpDQp0cmVlIDwtIHJwYXJ0KGNyZWRpdGFiaWxpdHkgfiAuLCBkYXRhPW5hLm9taXQoZGZfZmFjdG9ycyksY29udHJvbCA9IHJwYXJ0LmNvbnRyb2woY3AgPSAwLjAwMDEpLG1vZGVsID0gVFJVRSkNCg0KdHJlZQ0KDQpycGFydC5wbG90KHRyZWUpDQoNCg0KYGBgDQoNCiMjIyAxMC4yYiBBZGp1c3QgUHJpb3JzIGFuZCBQcnVuaW5nDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KDQpzZXQuc2VlZCgxMjMpDQp0cmVlIDwtIHJwYXJ0KGNyZWRpdGFiaWxpdHkgfiAuLCBkYXRhPW5hLm9taXQoZGZfZmFjdG9ycyksY29udHJvbCA9IHJwYXJ0LmNvbnRyb2woY3AgPSAwLjAwMDAwMSksbW9kZWwgPSBUUlVFLHBhcm1zID0gbGlzdChwcmlvciA9IGMoMC43LCAwLjMpKSkNCg0KdHJlZQ0KDQpycGFydC5wbG90KHRyZWUpDQoNCnBydW5ldHJlZT1wcnVuZSh0cmVlLGNwPTAuMDEpDQoNCg0KcnBhcnQucGxvdChwcnVuZXRyZWUpDQoNCmBgYA0KDQoNCiMjIyAxMC4yYyBMb3NzIE1hdHJpeA0KDQpDaGFuZ2UgdGhlIGNvZGUgcHJvdmlkZWQgaW4gdGhlIHZpZGVvIHN1Y2ggdGhhdCBhIGRlY2lzaW9uIHRyZWUgaXMgY29uc3RydWN0ZWQgdXNpbmcgYSBsb3NzIG1hdHJpeCBwZW5hbGl6aW5nIDEwIHRpbWVzIG1vcmUgaGVhdmlseSBmb3IgbWlzY2xhc3NpZmllZCBkZWZhdWx0cy4NCg0KYGBge3J9DQoNCmxpYnJhcnkocnBhcnQpDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQoNCg0Kc2V0LnNlZWQoMTIzKQ0KdHJlZWxvc3NtIDwtIHJwYXJ0KGNyZWRpdGFiaWxpdHkgfiAuLCBkYXRhPW5hLm9taXQoZGZfZmFjdG9ycyksY29udHJvbCA9IHJwYXJ0LmNvbnRyb2woY3AgPSAwLjAwMSksbW9kZWwgPSBUUlVFLHBhcm1zID0gbGlzdChsb3NzID0gbWF0cml4KGMoMCwgMywgMSwgMCksIG5jb2wgPSAyKSkpDQp0cmVlDQoNCnJwYXJ0LnBsb3QodHJlZWxvc3NtKQ0KDQoNCmBgYA0KDQoNCiMjIyAxMC4zIFBydW5pbmcgVGhlIFRyZWUNCg0KDQojIyMjIDEwLjMuMSBVc2luZyBQcmludGNwIGFuZCBQbG90Y3ANCg0KVXNpbmcgcHJpbnRjcCB3ZSBnZXQgdGhlIGNvbXBsZXhpdHkgcGFyYW1ldGVyICggV2UgY2FuIHVzZSB0aGlzIHRvIHNldCB0aGUgdHJlZSBkZXB0aCwgaWUgcHJ1bmluZyApLCB3ZSBzZXQgdGhlIGNvbXBsZXhpdHkgcGFyYW1ldGVyIHRvIGdldCB0byB0aGUgY29tcGxleGl0eSBsZXZlbA0KDQpOZWVkIHRvIG1pbmltaXNlIHhlcnJvciBpbiB0aGUgc2Vjb25kIGxhc3QgY29sdW1uIG9mIHRoZSB0YWJsZSBiZWxvdw0KDQpOZWVkIHRvIHNldCBhIHNlZWQgdG8gZ2V0IHRoZSByZXN1bHRzIHJlcHJvZHVjYWJsZSANCg0KYGBge3IgQ29tcGxleGl0eSBMZXZlbH0NCg0KIyBXZSB1c2UgcHJpbnRjcCBvbiB0aGUgYWJvdmUgdHJlZSANCnByaW50Y3AodHJlZSkNCg0KcGxvdGNwKHRyZWUpDQoNCg0KYGBgDQoNCg0KDQoNCiMjIyMgMTAuMy4yIFVzaW5nIHBydW5lKCkgdG8gUHJ1bmUgdGhlIHRyZWUNCg0KDQoNCg0KYGBge3IgUHJ1bmUgVHJlZX0NCg0KcHRyZWVfcHJ1bmVkID0gcHJ1bmUodHJlZSxjcD0wLjAwMDINCiAgICAgICAgICAgICAgICAgICAgICkNCg0KDQpycGFydC5wbG90KHB0cmVlX3BydW5lZCx1bmlmb3JtPVRSVUUpDQoNCiMgcHJwIGZ1bmN0aW9uDQoNCnBycChwdHJlZV9wcnVuZWQsZXh0cmE9MSkNCg0KYGBgDQoNCiMjIyAxMC4zLjMgT3RoZXIgVHJlZSBPcHRpb25zDQoNCldlaWdodHM6IGNhbiB1c2UgdGhpcyB0byBjb3VudGVyIGltYmFsYW5jZSBpbiB0cmVlDQoNCk90aGVyIGFyZ3VtZW50cyBhcmUgdGhlIHJwYXJ0LmNvbnRyb2wgYXJndW1lbnRzDQoNCm1pbnNwbGl0OiBtaW4gbnVtYmVyIGluIGEgbm9kZSB0byBzcGxpdDogZGVmYXVsdCBpcyAyMCAuLm1heSBiZSB1c2VmdWwgdG8gbG93ZXIgaXQgd2hlbiBkYXRhIGlzIHVuYmFsYW5jZWQNCm1pbmJ1Y2tldHM6IG1pbmltdW0gaW4gYSBsZWFmIG5vZGUgKGRlZmF1bHQgaXMgMS8zIG9mIG1pbnNwbGl0KQ0KDQpFdmFsdWF0aW9uIDogdXNlIHRoZSBwcmVkaWN0IGZ1bmN0aW9uDQoNCmBgYHtyfQ0KDQoNCnByZWRfdHJlZV9jbGFzcz1wcmVkaWN0KHRyZWUsIG5ld2RhdGE9ZGZfZmFjdG9ycyx0eXBlPSJjbGFzcyIpDQoNCnByZWRfdHJlZV9jbGFzc1sxOjEwXQ0KDQpwcmVkX3RyZWVfcHJvYj1wcmVkaWN0KHRyZWUsIG5ld2RhdGE9ZGZfZmFjdG9ycyx0eXBlPSJwcm9iIikNCg0KcHJlZF90cmVlX3Byb2JbMToxMF0NCg0KcHJlZF90cmVlX2RlZmF1bHQ9cHJlZGljdCh0cmVlLG5ld2RhdGE9ZGZfZmFjdG9ycykNCg0KcHJlZF90cmVlX2RlZmF1bHRbMToxMCxdDQoNCiMgQ29uc3RydWN0IENvbmZ1c2lvbiBNYXRyaXgNCg0KdGFibGUoZGZfZmFjdG9ycyRjcmVkaXRhYmlsaXR5LHByZWRfdHJlZV9jbGFzcykNCg0KdGFibGUoZGZfZmFjdG9ycyRjcmVkaXRhYmlsaXR5KQ0KDQpsaWJyYXJ5KGNhcmV0KQ0KDQpjb25mdXNpb25NYXRyaXgocHJlZF90cmVlX2NsYXNzLGRmX2ZhY3RvcnMkY3JlZGl0YWJpbGl0eSkNCg0KYGBgDQoNCg0KIyMjIDEwLjQgRXZhbHVhdGluZyBUaGUgQ3JlZGl0IFJpc2sgTW9kZWwNCg0KDQojIyMjIDEwLjQuMSBTdHJhdGVneSBDdXJ2ZQ0KDQpXZSBkb250IHdhbnQgdG8gZXhjZWVkIGEgY2VydGFpbiAlIG9mIGRlZmF1bHRzDQoNCg0KYGBge3J9DQoNCiMgTGV0cyBsb29rIGF0IHRoZSBhYm92ZSBwcmVkaWN0aW9ucw0KDQpwcmVkX3RyZWVfcHJvYj1wcmVkaWN0KHRyZWUsIG5ld2RhdGE9ZGZfZmFjdG9ycyx0eXBlPSJwcm9iIikNCg0KY3V0b2ZmID0gcXVhbnRpbGUocHJlZF90cmVlX3Byb2JbLDFdLDAuOCkNCg0KDQoNCmN1dG9mZg0KIzAuOA0KDQojIHNldCBjdXRvZmYgYXQgMC44DQoNCnByZWRfdHJlZV9wcm9iWywxXQ0KDQpwcmVkX2Z1bGxfMjAgPSBhcy5mYWN0b3IoaWZlbHNlKHByZWRfdHJlZV9wcm9iWywxXT4gMC44LDEsMCkpDQoNCmxldmVscyhwcmVkX2Z1bGxfMjApPWxpc3QoZ29vZD0iMCIsYmFkPSIxIikNCg0KdGFibGUocHJlZF9mdWxsXzIwKQ0KDQpkZl9mYWN0b3JzJGNyZWRpdGFiaWxpdHkgPC0gZmFjdG9yKGRmX2ZhY3RvcnMkY3JlZGl0YWJpbGl0eSwgbGV2ZWxzID0gYygiZ29vZCIsImJhZCIpKQ0KDQpjb25mdXNpb25NYXRyaXgocHJlZF9mdWxsXzIwLGRmX2ZhY3RvcnMkY3JlZGl0YWJpbGl0eSkNCg0KDQojIE5vdyB3ZSBiaW5kIHRoZSBuZXcgbG9hbnMgdG8gdGhlIGFjdHVhbCBkZWZhdWx0cw0KDQojDQp0cnVlX2FuZF9wcmVkaWN0ZWQ9IGNiaW5kKGRmX2ZhY3RvcnMkY3JlZGl0YWJpbGl0eSxwcmVkX2Z1bGxfMjApDQoNCmFjY2VwdGVkX2xvYW5zPXRydWVfYW5kX3ByZWRpY3RlZFtwcmVkX2Z1bGxfMjA9PSJnb29kIiwxXQ0KDQpiYWRfcmF0ZT1zdW0oYWNjZXB0ZWRfbG9hbnM9PTIpL2xlbmd0aChhY2NlcHRlZF9sb2FucykNCmJhZF9yYXRlDQoNCiMyMCUNCg0KDQojIENhbiBkbyBhIHN0cmFnZXkgY3VydmUgDQoNCiMgRnVuY3Rpb24gRm9yIGEgU3RyYWdlbnkgQ3VydmUgDQoNCnN0cmF0ZWd5X2Jhbms9ZnVuY3Rpb24ocHJvYl9vZl9kZWYpew0KY3V0b2ZmPXJlcChOQSwgMjEpDQpiYWRfcmF0ZT1yZXAoTkEsIDIxKQ0KYWNjZXB0X3JhdGU9c2VxKDEsMCxieT0tMC4wNSkNCmZvciAoaSBpbiAxOjIxKXsNCiAgY3V0b2ZmW2ldPXF1YW50aWxlKHByb2Jfb2ZfZGVmLGFjY2VwdF9yYXRlW2ldKQ0KICBwcmVkX2k9aWZlbHNlKHByb2Jfb2ZfZGVmPiBjdXRvZmZbaV0sIDEsIDApDQogIHByZWRfYXNfZ29vZD1kZl9mYWN0b3JzJGNyZWRpdGFiaWxpdHlbcHJlZF9pPT0wXQ0KICBiYWRfcmF0ZVtpXT1zdW0ocHJlZF9hc19nb29kPT0iYmFkIikvbGVuZ3RoKHByZWRfYXNfZ29vZCl9DQp0YWJsZT1jYmluZChhY2NlcHRfcmF0ZSxjdXRvZmY9cm91bmQoY3V0b2ZmLDQpLGJhZF9yYXRlPXJvdW5kKGJhZF9yYXRlLDQpKQ0KcmV0dXJuKGxpc3QodGFibGU9dGFibGUsYmFkX3JhdGU9YmFkX3JhdGUsIGFjY2VwdF9yYXRlPWFjY2VwdF9yYXRlLCBjdXRvZmY9Y3V0b2ZmKSkNCn0NCg0KcHJvYl9vZl9kZWZhdWx0PXByZWRfdHJlZV9wcm9iWywxXQ0KDQpTdHJhdGVneV90cmVlPXN0cmF0ZWd5X2JhbmsocHJvYl9vZl9kZWZhdWx0KQ0KDQpwbG90KFN0cmF0ZWd5X3RyZWUkYWNjZXB0X3JhdGUsIFN0cmF0ZWd5X3RyZWUkYmFkX3JhdGUsIA0KICAgICB0eXBlID0gImwiLCB4bGFiID0gIkFjY2VwdGFuY2UgcmF0ZSIsIHlsYWIgPSAiQmFkIHJhdGUiLCANCiAgICAgbHdkID0gMiwgbWFpbiA9ICJUcmVlIikNCg0KDQoNCg0KYGBgDQoNCg0KDQojIyMjIDEwLjQuMiBST0MgQ3VydmUNCg0KDQpXaGF0IGlzIHRoZSBiZXN0IG1vZGVsIG92ZXJhbGwgPw0KDQpBY2N1cmFjeSBpcyBNYXhpbWl6ZWQgd2hlbiBhIHZlcnkgaGlnaCBjdXRvZmYgaXMgY2hvc2VuOyB3aGVuIGFsbCB0ZXN0c2V0IGl0ZW1zIGFyZSBjbGFzc2lmaWVkIGFzIG5vbiBkZWZhdWx0DQoNClJPQyBTSE9XUyBDUk9TU09WRVIgLi4gSGFyZGVyIHRvIHRlbGwgd2hpY2ggaXMgYmV0dGVyDQoNCmBgYHtyfQ0KDQpsaWJyYXJ5KHBST0MpDQpST0NfdHJlZT1yb2MoZGZfZmFjdG9ycyRjcmVkaXRhYmlsaXR5LHByZWRfdHJlZV9wcm9iWywxXSkNCg0KcGxvdChST0NfdHJlZSkNCg0KDQojIEFkZGluZyBMaW5lcw0KDQojIFJPQ19sb2dpdCA8LSByb2ModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRpY3Rpb25zX2xvZ2l0KQ0KIyBST0NfcHJvYml0IDwtIHJvYyh0ZXN0X3NldCRsb2FuX3N0YXR1cywgcHJlZGljdGlvbnNfcHJvYml0KQ0KIyBST0NfY2xvZ2xvZyA8LSByb2ModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRpY3Rpb25zX2Nsb2dsb2cpDQojIFJPQ19hbGxfZnVsbCA8LSByb2ModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRpY3Rpb25zX2FsbF9mdWxsKQ0KIyANCiMgIyBEcmF3IGFsbCBST0NzIG9uIG9uZSBwbG90DQojIHBsb3QoUk9DX2xvZ2l0KQ0KIyBsaW5lcyhST0NfcHJvYml0LCBjb2wgPSAiYmx1ZSIpDQojIGxpbmVzKFJPQ19jbG9nbG9nLCBjb2wgPSAicmVkIikNCiMgbGluZXMoUk9DX2FsbF9mdWxsLCBjb2wgPSAiZ3JlZW4iKQ0KDQoNCmF1YyhST0NfdHJlZSkNCg0KIyBFeGFtcGxlcyBGb3IgVHJlZSBCYXNlZCBNb2RlbHMNCg0KIyAjIENvbnN0cnVjdCB0aGUgb2JqZWN0cyBjb250YWluaW5nIFJPQy1pbmZvcm1hdGlvbg0KIyBST0NfdW5kZXJzYW1wbGUgPC0gcm9jKHRlc3Rfc2V0JGxvYW5fc3RhdHVzLCBwcmVkaWN0aW9uc191bmRlcnNhbXBsZSkNCiMgUk9DX3ByaW9yIDwtIHJvYyh0ZXN0X3NldCRsb2FuX3N0YXR1cywgcHJlZGljdGlvbnNfcHJpb3IpDQojIFJPQ19sb3NzX21hdHJpeCA8LSByb2ModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRpY3Rpb25zX2xvc3NfbWF0cml4KQ0KIyBST0Nfd2VpZ2h0cyA8LSByb2ModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRpY3Rpb25zX3dlaWdodHMpDQojIA0KIyAjIERyYXcgdGhlIFJPQy1jdXJ2ZXMgaW4gb25lIHBsb3QNCiMgcGxvdChST0NfdW5kZXJzYW1wbGUpDQojIGxpbmVzKFJPQ19wcmlvciwgY29sPSJibHVlIikNCiMgbGluZXMoUk9DX2xvc3NfbWF0cml4LCBjb2w9InJlZCIpDQojIGxpbmVzKFJPQ193ZWlnaHRzLCBjb2w9ImdyZWVuIikNCiMgDQojICMgQ29tcHV0ZSB0aGUgQVVDcw0KIyBhdWMoUk9DX3VuZGVyc2FtcGxlKQ0KIyBhdWMoUk9DX3ByaW9yKQ0KIyBhdWMoUk9DX2xvc3NfbWF0cml4KQ0KIyBhdWMoUk9DX3dlaWdodHMpDQoNCmBgYA0KDQoNCg0KIyMjIFdoaWNoIFZhcmlhYmxlcyAgZG8gbWFueSBBVUMgYW5kIGNvbXBhcmUNCg0KQVVDIEJhc2VkIFBydW5pbmcuLg0KDQoNCmBgYHtyIFJlcGVhdCBmb3IgbWFueSBBVUN9DQoNCiMgRXhhbXBsZSAxDQoNCiMgIyBCdWlsZCBmb3VyIG1vZGVscyBlYWNoIHRpbWUgZGVsZXRpbmcgb25lIHZhcmlhYmxlIGluIGxvZ18zX3JlbW92ZV9pcg0KIyBsb2dfNF9yZW1vdmVfYW1udCA8LSBnbG0obG9hbl9zdGF0dXMgfiBncmFkZSArIGFubnVhbF9pbmMgKyBlbXBfY2F0LCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsLCBkYXRhID0gdHJhaW5pbmdfc2V0KSANCiMgbG9nXzRfcmVtb3ZlX2dyYWRlIDwtIGdsbShsb2FuX3N0YXR1cyB+IGxvYW5fYW1udCAgKyBhbm51YWxfaW5jICsgZW1wX2NhdCwgDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwsIGRhdGEgPSB0cmFpbmluZ19zZXQpDQojIGxvZ180X3JlbW92ZV9pbmMgPC0gZ2xtKGxvYW5fc3RhdHVzIH4gbG9hbl9hbW50ICsgZ3JhZGUgICsgZW1wX2NhdCAsIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IHRyYWluaW5nX3NldCkNCiMgbG9nXzRfcmVtb3ZlX2VtcCA8LSBnbG0obG9hbl9zdGF0dXMgfiBsb2FuX2FtbnQgKyBncmFkZSArIGFubnVhbF9pbmMsIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IHRyYWluaW5nX3NldCkNCiMgDQojICMgTWFrZSBQRC1wcmVkaWN0aW9ucyBmb3IgZWFjaCBvZiB0aGUgbW9kZWxzDQojIHByZWRfNF9yZW1vdmVfYW1udCA8LSBwcmVkaWN0KGxvZ180X3JlbW92ZV9hbW50LCBuZXdkYXRhID0gdGVzdF9zZXQsIHR5cGUgPSAicmVzcG9uc2UiKQ0KIyBwcmVkXzRfcmVtb3ZlX2dyYWRlIDwtIHByZWRpY3QobG9nXzRfcmVtb3ZlX2dyYWRlLCBuZXdkYXRhID0gdGVzdF9zZXQsIHR5cGUgPSAicmVzcG9uc2UiKQ0KIyBwcmVkXzRfcmVtb3ZlX2luYyA8LSBwcmVkaWN0KGxvZ180X3JlbW92ZV9pbmMsIG5ld2RhdGEgPSB0ZXN0X3NldCwgdHlwZSA9ICJyZXNwb25zZSIpDQojIHByZWRfNF9yZW1vdmVfZW1wIDwtIHByZWRpY3QobG9nXzRfcmVtb3ZlX2VtcCwgbmV3ZGF0YSA9IHRlc3Rfc2V0LCB0eXBlID0gInJlc3BvbnNlIikNCiMgDQojICMgQ29tcHV0ZSB0aGUgQVVDcw0KIyBhdWModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRfNF9yZW1vdmVfYW1udCkNCiMgYXVjKHRlc3Rfc2V0JGxvYW5fc3RhdHVzLCBwcmVkXzRfcmVtb3ZlX2dyYWRlKQ0KIyBhdWModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRfNF9yZW1vdmVfaW5jKQ0KIyBhdWModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRfNF9yZW1vdmVfZW1wKQ0KDQoNCiMgRXhhbXBsZSAyDQoNCiMgIyBCdWlsZCBmb3VyIG1vZGVscyBlYWNoIHRpbWUgZGVsZXRpbmcgb25lIHZhcmlhYmxlIGluIGxvZ18zX3JlbW92ZV9pcg0KIyBsb2dfNF9yZW1vdmVfYW1udCA8LSBnbG0obG9hbl9zdGF0dXMgfiBncmFkZSArIGFubnVhbF9pbmMgKyBlbXBfY2F0LCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsLCBkYXRhID0gdHJhaW5pbmdfc2V0KSANCiMgbG9nXzRfcmVtb3ZlX2dyYWRlIDwtIGdsbShsb2FuX3N0YXR1cyB+IGxvYW5fYW1udCAgKyBhbm51YWxfaW5jICsgZW1wX2NhdCwgDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwsIGRhdGEgPSB0cmFpbmluZ19zZXQpDQojIGxvZ180X3JlbW92ZV9pbmMgPC0gZ2xtKGxvYW5fc3RhdHVzIH4gbG9hbl9hbW50ICsgZ3JhZGUgICsgZW1wX2NhdCAsIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IHRyYWluaW5nX3NldCkNCiMgbG9nXzRfcmVtb3ZlX2VtcCA8LSBnbG0obG9hbl9zdGF0dXMgfiBsb2FuX2FtbnQgKyBncmFkZSArIGFubnVhbF9pbmMsIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IHRyYWluaW5nX3NldCkNCiMgDQojICMgTWFrZSBQRC1wcmVkaWN0aW9ucyBmb3IgZWFjaCBvZiB0aGUgbW9kZWxzDQojIHByZWRfNF9yZW1vdmVfYW1udCA8LSBwcmVkaWN0KGxvZ180X3JlbW92ZV9hbW50LCBuZXdkYXRhID0gdGVzdF9zZXQsIHR5cGUgPSAicmVzcG9uc2UiKQ0KIyBwcmVkXzRfcmVtb3ZlX2dyYWRlIDwtIHByZWRpY3QobG9nXzRfcmVtb3ZlX2dyYWRlLCBuZXdkYXRhID0gdGVzdF9zZXQsIHR5cGUgPSAicmVzcG9uc2UiKQ0KIyBwcmVkXzRfcmVtb3ZlX2luYyA8LSBwcmVkaWN0KGxvZ180X3JlbW92ZV9pbmMsIG5ld2RhdGEgPSB0ZXN0X3NldCwgdHlwZSA9ICJyZXNwb25zZSIpDQojIHByZWRfNF9yZW1vdmVfZW1wIDwtIHByZWRpY3QobG9nXzRfcmVtb3ZlX2VtcCwgbmV3ZGF0YSA9IHRlc3Rfc2V0LCB0eXBlID0gInJlc3BvbnNlIikNCiMgDQojICMgQ29tcHV0ZSB0aGUgQVVDcw0KIyBhdWModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRfNF9yZW1vdmVfYW1udCkNCiMgYXVjKHRlc3Rfc2V0JGxvYW5fc3RhdHVzLCBwcmVkXzRfcmVtb3ZlX2dyYWRlKQ0KIyBhdWModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRfNF9yZW1vdmVfaW5jKQ0KIyBhdWModGVzdF9zZXQkbG9hbl9zdGF0dXMsIHByZWRfNF9yZW1vdmVfZW1wKQ0KDQoNCg0KDQoNCg0KYGBgDQoNCg0KDQojIyMgMTEuMCBNb2RlbCBFeHBsYW5hdGlvbiANCg0KDQoNCg0KDQoNCg0KDQo=