Multiple and Logistic Regression

In the previous chapter we studied simple linear regression with a single explanatory variable, but usually the response depends on a number of explanatory (predictor) variables which corresponds to a multiple regression. We have also restricted the response (outcome) variable to be numerical, in this chapter we also learn logistic regression used for predicting categorical outcomes with two levels (binary response variable).

Multiple Regression

Rather than having just one predictor, we now want to determine the value of a continuous response variable \(y\) given the values of \(p > 1\) independent explanatory variables \(x_1, x_2,... x_p\) (predictors). The linear model is defined as:

\[\begin{equation} \hat{y} = E(Y|X_1=x_1,X_2=x_2,...X_p=x_p) = b_0 + b_1 x_1 +b_2 x_2+ ... + b_p x_p \end{equation}\] so that average predicted \(\hat{y}\) is a linear function of predictors \(x_1, x_2,... x_p\) with estimated regression coefficients (slopes) \(b_1, b_2,... b_p\). Note that each slope \(b_i\) is the average change in the response variable \(y\) corresponding to the unit change in variable \(x_i\) while all other explanatory variables are held constant.

In simple linear regression with only one predictor variable, the goal was to find the “line of best fit.” Now, the data is multidimensional and the relationship between response and predictors is a multidimensional plane (hyper-plane), which we will not be able to plot as a simple scatter-plot with a line as before. We want to find the hyper-plane that best fits the multivariate data in terms of minimizing the squared distance between the plane and given y-values. Specifically, the \(b_1, b_2,... b_p\) are found as the values that minimize the sum of squared errors (SSE) or residual sum of squares (RSS): \[\begin{equation} SSE=RSS=\sum_{i=1}^n [y_i -(b_0 + b_1 x_{1,i} +b_2 x_{2,i}+ ... + b_p x_{p,i})]^2 \end{equation}\]

The assumption analysis is in many ways similar to simple regression. There is, however, one additional important and subtle assumption for multiple regression - independence of the predictor variables from each other. If the variables are dependent, they are called collinear. Such variables are strongly correlated which makes it difficult to separate out the individual effects of collinear variables on the response. The collinearity causes the standard error (SE) for each coefficient \(b_i\) to grow. The t-statistic for each predictor is \(b_i/SE\), so \(t\) declines and p-value grows, therefore we may fail to reject H0 : \(\beta_i = 0\). This means that the power of the hypothesis test (the probability of correctly detecting a non-zero coefficient) is reduced by collinearity. Therefore, it reduces the accuracy of the estimates of the regression coefficients.

A first step to detect collinearity is to consider the correlation matrix of the predictors. High correlation would indicate a collinearity problem in the data. However, it is possible for collinearity to exist between three or more variables even if pairwise correlations are moderate - multicollinearity. A better way to assess multicollinearity is to compute the variance inflation factor (VIF) which is the ratio of the variance of \(b_j\) when fitting the full model divided by the variance of \(b_j\) if fit on its own. The smallest possible VIF is 1 if no collinearity is observed. As a rule of thumb, a VIF value that exceeds 5 indicates a problematic amount of collinearity. If collinearity is high, we can drop or combine such variables since they are redundant in the presence of the other variables.

The high VIF values, however, are not always a big problem. For example, we consider later in this chapter polynomial regression with variables \(x\), \(x^2\), \(x^3\), which have high VIF’s, but p-values in this case are not affected by multicollinearity. Analogously, if the model includes interaction of two variables \(x\), \(y\), written as product \(x \cdot y\), multicollinearity (VIF’s) are high but p-values are not affected again. Finally, in the case of categorical variable with three or more categories, if the proportion of cases in the reference category is small, the indicator variables will necessarily have high VIFs. The solution is just to change the reference category.

  1. The variables with high VIFs are indicator (dummy) variables that represent a categorical variable with three or more categories. If the proportion of cases in the reference category is small, the indicator variables will necessarily have high VIFs, even if the categorical variable is not associated with other variables in the regression model.

Suppose, for example, that a marital status variable has three categories: currently married, never married, and formerly married. You choose formerly married as the reference category, with indicator variables for the other two. What happens is that the correlation between those two indicators gets more negative as the fraction of people in the reference category gets smaller. For example, if 45 percent of people are never married, 45 percent are married, and 10 percent are formerly married, the VIFs for the married and never-married indicators will be at least 3.0.

Is this a problem? Well, it does mean that p-values for the indicator variables may be high. But the overall test that all indicators have coefficients of zero is unaffected by the high VIFs. And nothing else in the regression is affected. If you really want to avoid the high VIFs, just choose a reference category with a larger fraction of the cases. That may be desirable in order to avoid situations where none of the individual indicators is statistically significant even though the overall set of indicators is significant.

Note that we used \(r^2\) in simple regression as a fraction of variability in the response that was explained by the linear model: \[ r^2 = 1 - \frac{variability \ in \ residuals}{variability \ in \ outcome} = 1-\frac{SSE}{SST} = 1-\frac{\sum (y_i-\hat{y}_i)^2}{\sum (y_i-\overline{y})^2} \] This strategy is acceptable for just one predictor variable, however, for many predictors, \(r^2\) is a biased estimate of the of variability explained by the model when applied to a new sample of data. The \(r^2\) always increases whether or not added new variables are related to the response. To fix it, an adjusted \(r^2\) is introduced. \[\begin{equation} r^2_{adj} = 1-\frac{\frac{SSE}{n-p-1}}{\frac{SST}{n-1}} \end{equation}\] where \(n\) is the number of cases used to fit the model and \(p\) is the number of predictor variables in the model, i.e. each variance is divided by its degree of freedom. To maximize the \(r^2_{adj}\), we need to minimize \(\frac{SSE}{n-p-1}\). The \(SSE\) always decreases as we add new variables to the model (significant or not), but \(\frac{SSE}{n-p-1}\) may increase or decrease, due to the increasing \(p\) in the denominator. Once all of the relevant variables have been included in the model, adding additional noise variables would result in a very small decrease in \(SSE\), so \(\frac{SSE}{n-p-1}\) will increase, resulting in decreasing \(r^2_{adj}\). Therefore, \(r^2_{adj}\) statistic pays a price for the inclusion of unnecessary variables in the model and they would not be included. In addition, we are generally interested in making predictions for new data, then the un-adjusted \(r^2\) tends to be overly optimistic, while the adjusted \(r^2_{adj}\) helps to correct this bias.

Example
Investigate multiple linear regression model of cesd on some likely predictors in the HELPrct data file.

The code below shows linear model of the cesd depression score vs. several other variables. The age, mcs (mental score), pcs (physical score), pss_fr (perceived support system friends) are numerical, while sex, homeless, and substance are categorical which must be converted to indicator variables against reference levels. The diagnostic figures show that there is no pattern of residuals and constant variability assumption is satisfied because there is no funneling of the residuals. The QQ plot shows that the residuals are almost perfectly normally distributed (no outliers at all, not to mention any extreme outliers). Also, the assumption of independent observations (no time series) is satisfied too (Durbin-Watson is close to 2). Residuals vs Leverage plot also shows no extreme behavior. Most importantly, the correlations of numerical variables and variance inflation coefficients show no sign of collinearity of explanatory variables to each other. Thus, the linear model is applicable.

# load dataset
url="https://raw.githubusercontent.com/leonkag/Statistics0/main/HELPrct.csv"
mydata = read.csv(url,stringsAsFactors = TRUE)   # save as mydata file
cat('\nData Frame dimensions: ',dim(mydata))

Data Frame dimensions:  453 31
mydata = mydata[,c('cesd','age','mcs','pcs','pss_fr','homeless','substance','sex')];
cat('\nAfter choosing variables for our study, the data Frame dimensions: ',dim(mydata))

After choosing variables for our study, the data Frame dimensions:  453 8
summary(mydata)
      cesd            age             mcs              pcs       
 Min.   : 1.00   Min.   :19.00   Min.   : 6.763   Min.   :14.07  
 1st Qu.:25.00   1st Qu.:30.00   1st Qu.:21.676   1st Qu.:40.38  
 Median :34.00   Median :35.00   Median :28.602   Median :48.88  
 Mean   :32.85   Mean   :35.65   Mean   :31.677   Mean   :48.05  
 3rd Qu.:41.00   3rd Qu.:40.00   3rd Qu.:40.941   3rd Qu.:56.95  
 Max.   :60.00   Max.   :60.00   Max.   :62.175   Max.   :74.81  
     pss_fr           homeless     substance       sex     
 Min.   : 0.000   homeless:209   alcohol:177   female:107  
 1st Qu.: 3.000   housed  :244   cocaine:152   male  :346  
 Median : 7.000                  heroin :124               
 Mean   : 6.706                                            
 3rd Qu.:10.000                                            
 Max.   :14.000                                            

The correlation coefficients:

library(Hmisc)
rcorr(as.matrix( subset(mydata,select=c(cesd,age, mcs,pcs,pss_fr))  ))
        cesd   age   mcs   pcs pss_fr
cesd    1.00  0.01 -0.68 -0.29  -0.18
age     0.01  1.00  0.04 -0.23   0.08
mcs    -0.68  0.04  1.00  0.11   0.14
pcs    -0.29 -0.23  0.11  1.00   0.08
pss_fr -0.18  0.08  0.14  0.08   1.00

n= 453 


P
       cesd   age    mcs    pcs    pss_fr
cesd          0.8590 0.0000 0.0000 0.0000
age    0.8590        0.3434 0.0000 0.0884
mcs    0.0000 0.3434        0.0187 0.0033
pcs    0.0000 0.0000 0.0187        0.1038
pss_fr 0.0000 0.0884 0.0033 0.1038       

We can visualize the correlations with correlograms:

# install.packages("corrgram")
library(corrgram)
corrgram(subset(mydata,select=c(cesd,age, mcs,pcs,pss_fr)), 
         order=TRUE, lower.panel=panel.shade,
         upper.panel=panel.pie, text.panel=panel.txt)

Also, the pairs plot can be useful:

library(GGally)
ggpairs(subset(mydata,select=c(cesd,age, mcs,pcs,pss_fr))) 

Linear model:

mymodel = lm('cesd~age+mcs+pcs+pss_fr+homeless+substance+sex', data=mydata)
summary(mymodel)

Call:
lm(formula = "cesd~age+mcs+pcs+pss_fr+homeless+substance+sex", 
    data = mydata)

Residuals:
     Min       1Q   Median       3Q      Max 
-26.5873  -6.0981   0.1166   5.7769  26.0104 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)      70.35402    3.32954  21.130  < 2e-16 ***
age              -0.04944    0.05661  -0.873  0.38299    
mcs              -0.61901    0.03283 -18.857  < 2e-16 ***
pcs              -0.22871    0.04050  -5.647 2.91e-08 ***
pss_fr           -0.24555    0.10508  -2.337  0.01989 *  
homelesshoused   -0.10293    0.84835  -0.121  0.90349    
substancecocaine -2.72873    0.99994  -2.729  0.00661 ** 
substanceheroin  -2.10500    1.06741  -1.972  0.04922 *  
sexmale          -2.55642    0.98619  -2.592  0.00985 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 8.624 on 444 degrees of freedom
Multiple R-squared:  0.5335,    Adjusted R-squared:  0.5251 
F-statistic: 63.47 on 8 and 444 DF,  p-value: < 2.2e-16
confint(mymodel)
                      2.5 %       97.5 %
(Intercept)      63.8104059 76.897641431
age              -0.1606894  0.061819425
mcs              -0.6835285 -0.554499955
pcs              -0.3082977 -0.149112469
pss_fr           -0.4520570 -0.039034926
homelesshoused   -1.7702093  1.564357074
substancecocaine -4.6939362 -0.763514382
substanceheroin  -4.2027999 -0.007204202
sexmale          -4.4946027 -0.618234214

Variance inflation factors

library(car)
vif(mymodel)
              GVIF Df GVIF^(1/(2*Df))
age       1.157716  1        1.075972
mcs       1.079507  1        1.038993
pcs       1.159264  1        1.076691
pss_fr    1.070903  1        1.034844
homeless  1.089299  1        1.043695
substance 1.210434  2        1.048903
sex       1.068666  1        1.033763

Diagnostic Plots:

par(mfrow=c(2,2))
plot(mymodel)
par(mfrow=c(1,1))

The overall ANOVA F statistic test is highly significant.
For the multiple regression, the hypothesis test is performed for each coefficient (slope):
H0: \(\beta_j = 0\) zero coefficient (slope) for \(x_j\), no significant relationship.
H1: \(\beta_j \ne 0\) non zero coefficient (slope) for \(x_j\), significant relationship.
The code provides t-test and p-value for each variable and each indicator variable for categorical predictors. Some of them are significant others are not. The 95% confidence intervals for the coefficients (slopes) confirm the t-test with significant CI’s not containing 0.
Let’s remove the predictor with highest p-value - homeless. Age is also non-significant, but we have to remove variables one by one and examine the summary because each time all the coefficients and their p-values change.

mymodel2 = update(mymodel, . ~ . - homeless)
summodel2 = summary(mymodel2); summodel2

Call:
lm(formula = cesd ~ age + mcs + pcs + pss_fr + substance + sex, 
    data = mydata)

Residuals:
     Min       1Q   Median       3Q      Max 
-26.5458  -6.1094   0.0646   5.7992  25.9648 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)      70.32743    3.31864  21.192  < 2e-16 ***
age              -0.04915    0.05650  -0.870  0.38479    
mcs              -0.61926    0.03273 -18.922  < 2e-16 ***
pcs              -0.22906    0.04035  -5.678 2.47e-08 ***
pss_fr           -0.24744    0.10379  -2.384  0.01754 *  
substancecocaine -2.74372    0.99117  -2.768  0.00587 ** 
substanceheroin  -2.12466    1.05387  -2.016  0.04439 *  
sexmale          -2.54439    0.98011  -2.596  0.00974 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 8.615 on 445 degrees of freedom
Multiple R-squared:  0.5335,    Adjusted R-squared:  0.5261 
F-statistic: 72.69 on 7 and 445 DF,  p-value: < 2.2e-16
confint(mymodel2)      
                      2.5 %      97.5 %
(Intercept)      63.8052804 76.84957305
age              -0.1601861  0.06188454
mcs              -0.6835795 -0.55493905
pcs              -0.3083555 -0.14977316
pss_fr           -0.4514275 -0.04345840
substancecocaine -4.6916867 -0.79576014
substanceheroin  -4.1958390 -0.05348082
sexmale          -4.4706196 -0.61816959

The age is still insignificant, so we remove it as well and obtain the model where all the p-values < 0.05, i.e. all variables are significant.

mymodel3 = update(mymodel2, . ~ . - age)
summodel3 = summary(mymodel3); summodel3

Call:
lm(formula = cesd ~ mcs + pcs + pss_fr + substance + sex, data = mydata)

Residuals:
     Min       1Q   Median       3Q      Max 
-26.2617  -6.1196   0.2528   5.5920  25.4253 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)      68.15424    2.18417  31.204  < 2e-16 ***
mcs              -0.62044    0.03269 -18.979  < 2e-16 ***
pcs              -0.22088    0.03922  -5.632 3.17e-08 ***
pss_fr           -0.25717    0.10316  -2.493  0.01303 *  
substancecocaine -2.58936    0.97490  -2.656  0.00819 ** 
substanceheroin  -1.88331    1.01642  -1.853  0.06456 .  
sexmale          -2.52852    0.97968  -2.581  0.01017 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 8.612 on 446 degrees of freedom
Multiple R-squared:  0.5327,    Adjusted R-squared:  0.5264 
F-statistic: 84.73 on 6 and 446 DF,  p-value: < 2.2e-16
confint(mymodel3)
                      2.5 %      97.5 %
(Intercept)      63.8616930 72.44678630
mcs              -0.6846868 -0.55619276
pcs              -0.2979585 -0.14379448
pss_fr           -0.4599081 -0.05442439
substancecocaine -4.5053251 -0.67339988
substanceheroin  -3.8808841  0.11425842
sexmale          -4.4538723 -0.60316392

There is also a bit more formal way to compare such nested models using a partial F-statistic. Assume we have one model nested within the other: \[ \hat{y}_{full} = b_0+b_1x_1+ b_2x_2+... +b_px_p+... +b_qx_q \] \[ \hat{y}_{part} = b_0+b_1x_1+ b_2x_2+... +b_px_p\] The partial F-statistics is defined as: \[\begin{equation} F_{partial} = \frac{(R^2_{full}-R^2_{part})(n-q-1)}{(1-R^2_{full})(q-p)} (\#eq:PartialF) \end{equation}\] where \(R^2\) denote the respective coefficients of determination and \(n\) is the sample size of the data. \(F_{partial}\) follows F-distribution with \(df_1 = q - p\), \(df_2 = n - q\) degrees of freedom. The anova() function runs this test. The p-value is given by the right tail of this F-distribution and if significant, it indicates that the improvement in goodness-of-fit is large enough to make the additional complexity worth it. We illustrate it with the code below:

anova(mymodel,mymodel2,mymodel3)
Analysis of Variance Table

Model 1: cesd ~ age + mcs + pcs + pss_fr + homeless + substance + sex
Model 2: cesd ~ age + mcs + pcs + pss_fr + substance + sex
Model 3: cesd ~ mcs + pcs + pss_fr + substance + sex
  Res.Df   RSS Df Sum of Sq      F Pr(>F)
1    444 33024                           
2    445 33025 -1    -1.095 0.0147 0.9035
3    446 33081 -1   -56.167 0.7552 0.3853

As expected mymodel2 and mymodel3 are not significantly different from mymodel.
Now, let’s remove one of the variables with low p-value, say pss_fr:

mymodel4 = update(mymodel3, . ~ . - pss_fr)
anova(mymodel,mymodel2,mymodel3,mymodel4)
Analysis of Variance Table

Model 1: cesd ~ age + mcs + pcs + pss_fr + homeless + substance + sex
Model 2: cesd ~ age + mcs + pcs + pss_fr + substance + sex
Model 3: cesd ~ mcs + pcs + pss_fr + substance + sex
Model 4: cesd ~ mcs + pcs + substance + sex
  Res.Df   RSS Df Sum of Sq      F  Pr(>F)  
1    444 33024                              
2    445 33025 -1     -1.09 0.0147 0.90349  
3    446 33081 -1    -56.17 0.7552 0.38532  
4    447 33542 -1   -460.94 6.1972 0.01316 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Adding pss_fr makes a difference, though and should be added.

As we were removing insignificant variables the adjusted \(r^2_{adj}\) increased very slightly \(0.5251 < 0.5261 < 0.5264\), which implies the improvement of the model. Note that the other measures - \(AIC\), and \(BIC\) explained in the next section will change more noticeably.

Note that even though substance:heroin has p-value slightly above 0.05, substance:cocaine have p-value < 0.05, so we cannot remove the substance variable. Using this model, we can illustrate the meaning of the coefficients (slopes).
If all other variables are kept constant, males have average cesd scores \(2.53\) below than the reference level of females. Analogously, with all other variables being kept constant, cocaine abusers have average cesd score \(2.59\) below reference level of alcoholics and heroin abusers have average cesd score \(1.88\) below the same reference level. Each extra unit of mcs score reduces average cesd score by \(0.62\), again with all other variables kept constant, etc…

We can also use this reduced model to predict cesd score for, say, the male below:

newdata = data.frame(sex="male",substance="alcohol",mcs=30,pcs = 34, pss_fr=10)
predict(mymodel3, newdata, interval = "prediction")
       fit      lwr      upr
1 36.93106 19.89935 53.96278
predict(mymodel3, newdata, interval = "confidence")
       fit      lwr      upr
1 36.93106 35.03477 38.82736


Example
Investigate multiple linear regression model of WT on some likely predictors in the MHEALTH data file.

The code below shows linear model of the weight WT on several predictor variables. This time all the variables are numerical so no indicator variables are needed. The diagnostic figures show that there is a nonlinear pattern of residuals which invalidates the linear model. In addition, there is high correlation between WAIST and BMI etc… and the resulting VIF coefficients are more than 5 for WAIST and BMI. There is no unique answer as to which variable to remove 1st, but in this case, it should be clear that keeping both WAIST and BMI which is directly related to WT is not reasonable, so we are going to remove BMI 1st. In addition, the QQ plot shows that the residuals are not normally distributed. The assumption of independent observations (no time series) is satisfied. Residuals vs Leverage plot shows no extreme behavior.

# MHEALTH  wage multiple regression
rm(list=ls())
url="https://raw.githubusercontent.com/leonkag/Statistics0/main/MHEALTH.csv"
mydata = read.csv(url,stringsAsFactors = TRUE)   # save as mydata file
cat('\nData Frame dimensions: ',dim(mydata))

Data Frame dimensions:  40 14
mydata = subset(mydata, select = -c(MALE));  # removed ID column
cat('\nAfter choosing variables for our study, the data Frame dimensions: ',dim(mydata))

After choosing variables for our study, the data Frame dimensions:  40 13
summary(mydata)
      AGE              HT              WT            WAIST       
 Min.   :17.00   Min.   :61.30   Min.   :119.5   Min.   : 75.20  
 1st Qu.:25.75   1st Qu.:66.30   1st Qu.:152.4   1st Qu.: 84.38  
 Median :32.50   Median :68.30   Median :169.9   Median : 91.20  
 Mean   :35.48   Mean   :68.33   Mean   :172.6   Mean   : 91.28  
 3rd Qu.:44.50   3rd Qu.:70.08   3rd Qu.:189.6   3rd Qu.: 99.90  
 Max.   :73.00   Max.   :76.20   Max.   :237.1   Max.   :108.70  
     PULSE           SYS             DIAS            CHOL       
 Min.   :56.0   Min.   : 95.0   Min.   :44.00   Min.   :  31.0  
 1st Qu.:60.0   1st Qu.:111.5   1st Qu.:67.50   1st Qu.: 163.8  
 Median :66.0   Median :117.0   Median :75.00   Median : 282.5  
 Mean   :69.4   Mean   :118.9   Mean   :73.22   Mean   : 395.2  
 3rd Qu.:76.0   3rd Qu.:125.0   3rd Qu.:81.00   3rd Qu.: 619.2  
 Max.   :96.0   Max.   :153.0   Max.   :87.00   Max.   :1252.0  
      BMI             LEG            ELBOW           WRIST          ARM       
 Min.   :19.60   Min.   :36.00   Min.   :6.500   Min.   :5.2   Min.   :25.90  
 1st Qu.:23.73   1st Qu.:40.73   1st Qu.:7.000   1st Qu.:5.6   1st Qu.:30.70  
 Median :26.20   Median :42.65   Median :7.300   Median :5.8   Median :32.05  
 Mean   :26.00   Mean   :42.57   Mean   :7.295   Mean   :5.8   Mean   :32.39  
 3rd Qu.:27.50   3rd Qu.:44.42   3rd Qu.:7.500   3rd Qu.:6.0   3rd Qu.:33.77  
 Max.   :33.20   Max.   :48.40   Max.   :8.300   Max.   :6.7   Max.   :41.10  
# The correlation coefficients:
library(Hmisc)
rcorr(as.matrix( mydata  ))
        AGE    HT    WT WAIST PULSE   SYS  DIAS  CHOL   BMI   LEG ELBOW WRIST
AGE    1.00  0.29  0.29  0.43 -0.12  0.39  0.52 -0.02  0.14 -0.05  0.23  0.20
HT     0.29  1.00  0.52  0.27 -0.13  0.15  0.17 -0.17 -0.09  0.60  0.53  0.24
WT     0.29  0.52  1.00  0.89  0.06  0.35  0.39 -0.03  0.80  0.37  0.64  0.52
WAIST  0.43  0.27  0.89  1.00  0.14  0.41  0.53  0.10  0.86  0.15  0.41  0.55
PULSE -0.12 -0.13  0.06  0.14  1.00  0.19 -0.02  0.09  0.18  0.05 -0.09 -0.02
SYS    0.39  0.15  0.35  0.41  0.19  1.00  0.55  0.02  0.30 -0.10  0.29  0.28
DIAS   0.52  0.17  0.39  0.53 -0.02  0.55  1.00  0.06  0.33 -0.01  0.10  0.18
CHOL  -0.02 -0.17 -0.03  0.10  0.09  0.02  0.06  1.00  0.11 -0.05 -0.19 -0.05
BMI    0.14 -0.09  0.80  0.86  0.18  0.30  0.33  0.11  1.00  0.01  0.36  0.43
LEG   -0.05  0.60  0.37  0.15  0.05 -0.10 -0.01 -0.05  0.01  1.00  0.24 -0.08
ELBOW  0.23  0.53  0.64  0.41 -0.09  0.29  0.10 -0.19  0.36  0.24  1.00  0.45
WRIST  0.20  0.24  0.52  0.55 -0.02  0.28  0.18 -0.05  0.43 -0.08  0.45  1.00
ARM    0.07  0.19  0.82  0.73 -0.03  0.18  0.27  0.09  0.82  0.20  0.53  0.45
        ARM
AGE    0.07
HT     0.19
WT     0.82
WAIST  0.73
PULSE -0.03
SYS    0.18
DIAS   0.27
CHOL   0.09
BMI    0.82
LEG    0.20
ELBOW  0.53
WRIST  0.45
ARM    1.00

n= 40 


P
      AGE    HT     WT     WAIST  PULSE  SYS    DIAS   CHOL   BMI    LEG   
AGE          0.0708 0.0742 0.0058 0.4569 0.0124 0.0006 0.9250 0.3986 0.7572
HT    0.0708        0.0005 0.0938 0.4159 0.3428 0.2971 0.2910 0.5846 0.0000
WT    0.0742 0.0005        0.0000 0.7322 0.0259 0.0134 0.8739 0.0000 0.0183
WAIST 0.0058 0.0938 0.0000        0.3825 0.0081 0.0005 0.5298 0.0000 0.3410
PULSE 0.4569 0.4159 0.7322 0.3825        0.2415 0.8927 0.5931 0.2641 0.7452
SYS   0.0124 0.3428 0.0259 0.0081 0.2415        0.0002 0.9138 0.0572 0.5536
DIAS  0.0006 0.2971 0.0134 0.0005 0.8927 0.0002        0.6933 0.0349 0.9389
CHOL  0.9250 0.2910 0.8739 0.5298 0.5931 0.9138 0.6933        0.4981 0.7372
BMI   0.3986 0.5846 0.0000 0.0000 0.2641 0.0572 0.0349 0.4981        0.9493
LEG   0.7572 0.0000 0.0183 0.3410 0.7452 0.5536 0.9389 0.7372 0.9493       
ELBOW 0.1567 0.0004 0.0000 0.0090 0.6020 0.0699 0.5278 0.2368 0.0228 0.1345
WRIST 0.2088 0.1369 0.0006 0.0002 0.8877 0.0817 0.2584 0.7414 0.0057 0.6286
ARM   0.6479 0.2493 0.0000 0.0000 0.8374 0.2655 0.0918 0.5786 0.0000 0.2117
      ELBOW  WRIST  ARM   
AGE   0.1567 0.2088 0.6479
HT    0.0004 0.1369 0.2493
WT    0.0000 0.0006 0.0000
WAIST 0.0090 0.0002 0.0000
PULSE 0.6020 0.8877 0.8374
SYS   0.0699 0.0817 0.2655
DIAS  0.5278 0.2584 0.0918
CHOL  0.2368 0.7414 0.5786
BMI   0.0228 0.0057 0.0000
LEG   0.1345 0.6286 0.2117
ELBOW        0.0032 0.0005
WRIST 0.0032        0.0038
ARM   0.0005 0.0038       
library(corrgram)
corrgram(mydata, order=TRUE, lower.panel=panel.shade,
         upper.panel=panel.pie, text.panel=panel.txt)

library(GGally)
ggpairs(subset(mydata,select=c(AGE,HT,WT,WAIST,BMI))) 

# Linear model
mymodel = lm(WT~AGE+HT+WAIST+BMI+PULSE, data=mydata)
summary(mymodel)

Call:
lm(formula = WT ~ AGE + HT + WAIST + BMI + PULSE, data = mydata)

Residuals:
    Min      1Q  Median      3Q     Max 
-5.7891 -0.8003  0.1076  0.8003  5.2434 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -349.75463    9.56153 -36.579   <2e-16 ***
AGE           -0.01881    0.03026  -0.621   0.5384    
HT             5.18833    0.14743  35.191   <2e-16 ***
WAIST          0.02807    0.10395   0.270   0.7887    
BMI            6.51491    0.26826  24.286   <2e-16 ***
PULSE         -0.05053    0.02972  -1.700   0.0983 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.002 on 34 degrees of freedom
Multiple R-squared:  0.995, Adjusted R-squared:  0.9942 
F-statistic:  1341 on 5 and 34 DF,  p-value: < 2.2e-16
confint(mymodel)
                    2.5 %        97.5 %
(Intercept) -369.18600149 -3.303233e+02
AGE           -0.08030589  4.269300e-02
HT             4.88870480  5.487948e+00
WAIST         -0.18317906  2.393288e-01
BMI            5.96974158  7.060087e+00
PULSE         -0.11093769  9.876556e-03
library(car)
vif(mymodel)
      AGE        HT     WAIST       BMI     PULSE 
 1.727504  1.927632 10.221581  8.238270  1.096788 
par(mfrow=c(2,2))

plot(mymodel)
par(mfrow=c(1,1))

First remove BMI:

mymodel2 = update(mymodel, . ~ . - BMI)
summodel2 = summary(mymodel2); summodel2

Call:
lm(formula = WT ~ AGE + HT + WAIST + PULSE, data = mydata)

Residuals:
     Min       1Q   Median       3Q      Max 
-18.5385  -4.6193   0.3536   5.6446  15.0012 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -222.0183    33.7097  -6.586 1.32e-07 ***
AGE           -0.3775     0.1115  -3.385  0.00177 ** 
HT             2.9085     0.4799   6.060 6.44e-07 ***
WAIST          2.3818     0.1587  15.010  < 2e-16 ***
PULSE         -0.1184     0.1249  -0.947  0.34992    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 8.454 on 35 degrees of freedom
Multiple R-squared:  0.9075,    Adjusted R-squared:  0.8969 
F-statistic: 85.81 on 4 and 35 DF,  p-value: < 2.2e-16
vif(mymodel2)
     AGE       HT    WAIST    PULSE 
1.316074 1.146114 1.336336 1.087104 

The PULSE is still insignificant, so we remove it as well and obtain the model where all the p-values < 0.05, i.e. all variables are significant.

mymodel3 = update(mymodel2, . ~ . -PULSE)
summodel3 = summary(mymodel3); summodel3

Call:
lm(formula = WT ~ AGE + HT + WAIST, data = mydata)

Residuals:
    Min      1Q  Median      3Q     Max 
-19.521  -3.729   1.875   3.978  16.087 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -232.0936    31.9428  -7.266 1.47e-08 ***
AGE           -0.3591     0.1097  -3.275  0.00234 ** 
HT             2.9739     0.4743   6.271 3.03e-07 ***
WAIST          2.3460     0.1539  15.243  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 8.442 on 36 degrees of freedom
Multiple R-squared:  0.9051,    Adjusted R-squared:  0.8972 
F-statistic: 114.4 on 3 and 36 DF,  p-value: < 2.2e-16
CIb1 = confint(mymodel3); CIb1
                   2.5 %       97.5 %
(Intercept) -296.8764881 -167.3106159
AGE           -0.5814702   -0.1366994
HT             2.0120757    3.9357859
WAIST          2.0339086    2.6581771
vif(mymodel3)
     AGE       HT    WAIST 
1.276228 1.122369 1.260754 

Now, when we obtained a model with all significant predictors and all VIF’s well below 5, we rerun diagnostic plots and see that non-linearity of the residuals has been removed, normality of the residuals improved and none of the points on residuals vs. leverage graph crosses over the Cook’s level curves. Thus, the latest linear model is reasonable.

par(mfrow=c(2,2))
plot(mymodel3)
par(mfrow=c(1,1))

Using this model, we can illustrate the meaning of the coefficients (slopes).
If all other variables are kept constant, each extra year of age somewhat surprisingly lowers weight by 0.36 lb on average. Each extra inch of height on average increase weight by 2.97 lb. Finally each extra inch of WAIST increases WT by 2.34 lb on average.

We can also use this reduced model to predict WT below:

newdata = data.frame(AGE = 40,HT = 69,WAIST = 90)
predict(mymodel3, newdata, interval = "prediction")
       fit      lwr      upr
1 169.8881 152.5054 187.2709
predict(mymodel3, newdata, interval = "confidence")
       fit      lwr     upr
1 169.8881 166.8803 172.896

Polynomial Tranformation

Sometimes, we can observe clear curvature in a scatter plot and linear model is inadequate for the data. This can often be fixed by including quadratic or cubic terms in the model. For example, a cubic polynomial relationship is represented by \[ \hat{y} = b_0+b_1\cdot x+b_2\cdot x^2+b_3\cdot x^3 \] At order 1, the linear relationship allows no curvature (straight line). At order 2, a quadratic function (parabola) allows one bend. At order 3, the model can cope with two bends in the relationship etc… Although much higher order polynomials introduce artificial wiggles and are to be avoided. It is very important to realize that while non-linear in x, the model is still linear in the coeffients \(b_0, b_1...\). A truly non-linear model in coefficients requires different approach.
Generally, a numeric transformation refers to the application of a mathematical function to numeric observations to rescale them and is not limited to polynomials, it could be any function of \(x_i\)’s.

Example

Consider the built-in mtcars data set. Let’s investigate the dependence of mpg (miles per gallon) vs. hp (horsepower) of a car. The scatter plot shown in the Figure below has a noticeable curve in the relationship, so the regression line shown in this figure appears to be a poor fit. The diagnostic plots also show problems with the pattern in residuals. Note that we don’t need to check VIF’s - they are going to be high because these are the powers of the same variable.

# Polynomial fit mtcars 
plot(mpg ~ hp, data=mtcars, pch = 20)
mymodel1 = lm(mpg ~ hp, data=mtcars)
summary(mymodel1)

Call:
lm(formula = mpg ~ hp, data = mtcars)

Residuals:
    Min      1Q  Median      3Q     Max 
-5.7121 -2.1122 -0.8854  1.5819  8.2360 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 30.09886    1.63392  18.421  < 2e-16 ***
hp          -0.06823    0.01012  -6.742 1.79e-07 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.863 on 30 degrees of freedom
Multiple R-squared:  0.6024,    Adjusted R-squared:  0.5892 
F-statistic: 45.46 on 1 and 30 DF,  p-value: 1.788e-07
abline(mymodel1,lwd=3,col="red")
par(mfrow=c(2,2))

plot(mymodel1)
par(mfrow=c(1,1))

Note that even though the scatterplot and residual plot both show a rather poor fit, the p-value of the slope is still close to 0 which indicates statistical evidence of a negative linear impact of higher horsepower of cars on mileage. Therefore, based purely on the regression output, we would have to reject the H0 assumption of no linear relationship. Thus, as we said before, you should always examine the scatterplot.

Add a quadratic term in hp via I(hp^2). Note that R requires I() function which changes the class of an object to indicate that it should be treated ‘as is’. For a formula, it is used to inhibit the interpretation of operators such as “+”, “-”, “*” and “^” as formula operators, so they are used as arithmetical operators.

mymodel2 = lm(mpg ~ hp+I(hp^2), data=mtcars)
summary(mymodel2)

Call:
lm(formula = mpg ~ hp + I(hp^2), data = mtcars)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.5512 -1.6027 -0.6977  1.5509  8.7213 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  4.041e+01  2.741e+00  14.744 5.23e-15 ***
hp          -2.133e-01  3.488e-02  -6.115 1.16e-06 ***
I(hp^2)      4.208e-04  9.844e-05   4.275 0.000189 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.077 on 29 degrees of freedom
Multiple R-squared:  0.7561,    Adjusted R-squared:  0.7393 
F-statistic: 44.95 on 2 and 29 DF,  p-value: 1.301e-09
x.seq <- seq(50,350,length=1000)   # sequence of values in the range of x variable
mymodel2.pred <- predict(mymodel2,newdata=data.frame(hp=x.seq))
plot(mpg ~ hp, data=mtcars, pch = 20)
abline(mymodel1,lwd=3,col="red")
lines(x.seq,mymodel2.pred,lty=2,col="blue",lwd=3)

the Figure above shows both linear and quadratic fits. To add the quadratic prediction we used seq() function to create a finely spaced set of observations in the entire range of hp’s, predict() function to predict the mpg’s at those values, and lines() to add the quadratic line. The summary(mymodel2) indicates a small p-value (<0.05) for the quadratic term, so the contribution of the squared component is statistically significant. This conclusion is also supported by a higher adjusted \(R^2\).

Would the cubic term fit the data even better? No. The code below shows that the cubic term is not significant (p-value > 0.05) and the Figure below displays linear, quadratic, and cubic fits. The cubic fit is basically the same as quadratic.

mymodel3 = lm(mpg ~ hp+I(hp^2)+I(hp^3), data=mtcars)
summary(mymodel3)

Call:
lm(formula = mpg ~ hp + I(hp^2) + I(hp^3), data = mtcars)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.8605 -1.3972 -0.5736  1.6461  9.0738 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  4.422e+01  5.961e+00   7.419 4.43e-08 ***
hp          -2.945e-01  1.178e-01  -2.500   0.0185 *  
I(hp^2)      9.115e-04  6.863e-04   1.328   0.1949    
I(hp^3)     -8.701e-07  1.204e-06  -0.722   0.4760    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.103 on 28 degrees of freedom
Multiple R-squared:  0.7606,    Adjusted R-squared:  0.7349 
F-statistic: 29.65 on 3 and 28 DF,  p-value: 7.769e-09
mymodel3.pred <- predict(mymodel3,newdata=data.frame(hp=x.seq))
plot(mpg ~ hp, data=mtcars, pch = 20)
abline(mymodel1,lwd=3,col="red")
lines(x.seq,mymodel2.pred,lty=2,col="blue" ,lwd=3)
lines(x.seq,mymodel3.pred,lty=3,col="green",lwd=3)
legend("topright",lty=1:3,
       legend=c("order 1 (linear)","order 2 (quadratic)","order 3 (cubic)"),
       col=c("red","blue","green"),lwd=3)

From R code point of view, there is actually a better way to fit any order polynomial using poly() function with appopriate order. The results are the same.

mymodel3a = lm(mpg ~ poly(hp,3), data=mtcars)
summary(mymodel3a)

Call:
lm(formula = mpg ~ poly(hp, 3), data = mtcars)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.8605 -1.3972 -0.5736  1.6461  9.0738 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)   20.0906     0.5485  36.625  < 2e-16 ***
poly(hp, 3)1 -26.0456     3.1030  -8.394 3.95e-09 ***
poly(hp, 3)2  13.1546     3.1030   4.239 0.000221 ***
poly(hp, 3)3  -2.2419     3.1030  -0.722 0.475987    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.103 on 28 degrees of freedom
Multiple R-squared:  0.7606,    Adjusted R-squared:  0.7349 
F-statistic: 29.65 on 3 and 28 DF,  p-value: 7.769e-09

The relative statistical significance for nested models used here can be also assessed using anova() function:

anova(mymodel1,mymodel2,mymodel3)
Analysis of Variance Table

Model 1: mpg ~ hp
Model 2: mpg ~ hp + I(hp^2)
Model 3: mpg ~ hp + I(hp^2) + I(hp^3)
  Res.Df    RSS Df Sum of Sq      F    Pr(>F)    
1     30 447.67                                  
2     29 274.63  1   173.043 17.971 0.0002205 ***
3     28 269.61  1     5.026  0.522 0.4759872    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

It shows that quadratic term is significant, but cubic is not, similar to individual p-values.
It should also be noted that residuals and assumptions plot for linear model shows quadratic pattern in residuals, while quadratic model fixes this problem and has random distribution of residuals and better Residuals vs. Leverage plot with none of the data points crossing Cook distance level curves.

par(mfrow=c(2,2))
plot(mymodel1)

plot(mymodel2)
par(mfrow=c(1,1))

Note that polynomial and most other non-linear fits are only valid in the data region (interpolation). They have completely different behavior outside of the data range (extrapolation). For example, plotting our linear, quadratic, and cubic approximations on a much wider range, we obtain the Figure below where the quadratic and cubic extrapolations go wildly off course.

x.seq <- seq(50,500,length=1000)   # sequence of values in the range of x variable
mymodel2.pred <- predict(mymodel2,newdata=data.frame(hp=x.seq))
mymodel3.pred <- predict(mymodel3,newdata=data.frame(hp=x.seq))
plot(mpg ~ hp, data=mtcars, pch = 20, xlim=c(0,500),ylim=c(0,40))
abline(mymodel1,lwd=3,col="red")
lines(x.seq,mymodel2.pred,lty=2,col="blue" ,lwd=3)
lines(x.seq,mymodel3.pred,lty=3,col="green",lwd=3)
legend("topright",lty=1:3,
       legend=c("order 1 (linear)","order 2 (quadratic)","order 3 (cubic)"),
       col=c("red","blue","green"),lwd=3)

Interactive Terms

So far, for regression, we looked only at the additive main effects of predictors on outcome. Now, we consider interactions between predictors. An interaction effect occurs when the effect of one variable depends on the value of another variable. For example, medicine interaction effects are very common. Say, statins used for cholesterol redaction negatively interact with the grapefruit juice because it contains chemical compounds that inhibit the drug effects. Interactions can occur between categorical variables, numeric variables, or both. We commonly study interaction between two variables (two-way), higher order interactions are rarely considered as they are hard to interpret. Note also that we don’t need to check for multicollinearity with VIF’s - they are going to be high because it only makes sense to consider examples with strong interactions and these have a lot of correlations. Including the interaction term explicitly accounts for it.

Let’s first consider interaction between one categorical and one continuous variables. The categorical variable changes slope of the continuous predictor with respect to its non-reference levels. A categorical variable with \(k\) levels has \(k - 1\) main effect terms, so there will be also \(k - 1\) interactive terms with continuous variable.

Consider an example of classical iris data file which contains 3 classes of 50 instances each, where each class refers to a type of iris plant and its measurements. Just like for the interaction of terms in Two-Way-ANOVA, we use multiplication sign \(*\) to introduce interaction between the terms which automatically includes the main effects of the terms. ggplot2 package is used to produce an informative plot of regression lines broken by the categorical variable Species as shown in the Figure below.

# Interaction one continuous one categorical
mymodel = lm(Petal.Length ~ Petal.Width + Species, data = iris)
summary(mymodel)

Call:
lm(formula = Petal.Length ~ Petal.Width + Species, data = iris)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.02977 -0.22241 -0.01514  0.18180  1.17449 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)        1.21140    0.06524  18.568  < 2e-16 ***
Petal.Width        1.01871    0.15224   6.691 4.41e-10 ***
Speciesversicolor  1.69779    0.18095   9.383  < 2e-16 ***
Speciesvirginica   2.27669    0.28132   8.093 2.08e-13 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3777 on 146 degrees of freedom
Multiple R-squared:  0.9551,    Adjusted R-squared:  0.9542 
F-statistic:  1036 on 3 and 146 DF,  p-value: < 2.2e-16
library(ggplot2)
ggplot(iris, aes(x = Petal.Width, y = Petal.Length, color = Species,fill=Species)) +
  geom_point() +
  geom_smooth(method = "lm")

The output shows main effects for Petal.Width and Species as well as their interactions for each non-reference levels of the Species. The main effect for Petal.Width is not significant (p-value > 0.05), SpeciesVersicolor is not significant compared to the reference level of Setosa (p-value > 0.05), but SpeciesVirginica is significant (p-value << 0.05) compared to Setosa. Note that if at least one of the non-reference levels of a categorical variable is significant, the entire effect is significant. The interaction term Petal.Width:Speciesversicolor is significant (p-value < 0.05). Note that even though the main effect of Petal.Width is not significant, we cannot drop this variable because there is significant interaction with Species. The Figure above shows that slopes differ by Species which confirms the significance of the interaction. The resulting model is: \[Petal.Length =1.33+0.54 \cdot Petal.Width + \] \[ 0.45 \cdot Speciesversicolor + 2.91 \cdot Speciesvirginica + \] \[1.33 \cdot Petal.Width:Speciesversicolor +0.1 \cdot Petal.Width:Speciesvirginica\] For the reference level of the categorical predictor Species - Setosa, the fitted model is read from the output: \[Petal.Length =1.33+0.54 \cdot Petal.Width\] First consider the effects without interactions.
Changing Species to the level \(SpeciesVersicolor = 1\) (and others 0) adds a term: \[Petal.Length =1.33+0.54 \cdot Petal.Width + 0.45\cdot SpeciesVersicolor = \] \[1.33+0.54 \cdot Petal.Width + 0.45 \cdot 1 = 1.87+0.54 \cdot Petal.Width \] But note that it only changes the y-intercept, not the slope.
Analogously for the level \(Speciesvirginica = 1\) and others 0: \[Petal.Length =1.33+0.54 \cdot Petal.Width + 2.91 \cdot Speciesvirginica =\] \[1.33+0.54 \cdot Petal.Width + 2.91 \cdot 1 = 4.24+0.54 \cdot Petal.Width \] Now, let’s consider the interaction terms. They not only add to intercept, but also to the slopes. For the plant with \(Speciesversicolor = 1\), \[Petal.Length =1.33+0.54 \cdot Petal.Width + \] \[ 0.45 \cdot Speciesversicolor + 1.33 \cdot Petal.Width:Speciesversicolor = \] \[ 1.33+0.54 \cdot Petal.Width + 0.45 \cdot 1 + 1.33 \cdot Petal.Width \cdot 1 = \] \[ 1.88+1.87 \cdot Petal.Width \] Thus, we obtained higher y-intercept and higher slope of \(Petal.Width\).
The predict() function with interaction works exactly the same:

newdata = data.frame(Petal.Width=2,Species="virginica")
predict(mymodel, newdata, interval = "prediction")
       fit     lwr      upr
1 5.525513 4.77148 6.279547
predict(mymodel, newdata, interval = "confidence")
       fit      lwr      upr
1 5.525513 5.419644 5.631383

Next, let’s consider the interaction between two continuous variables.
Two continuous main effects fit a plane and an interaction term modifies the slopes in a continuous way. We use example data from state.x77 that is built into R modeling Income (average) vs. Illistracy and Murder rates including the interaction term.

states <- as.data.frame(state.x77); 
mymodel <- lm(Income ~ Illiteracy * Murder, data = states)
summary(mymodel)

Call:
lm(formula = Income ~ Illiteracy * Murder, data = states)

Residuals:
    Min      1Q  Median      3Q     Max 
-955.20 -325.99   10.66  299.96 1892.12 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)        3822.61     405.33   9.431 2.54e-12 ***
Illiteracy          617.34     434.85   1.420  0.16245    
Murder              146.82      50.33   2.917  0.00544 ** 
Illiteracy:Murder  -117.10      40.13  -2.918  0.00544 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 520.1 on 46 degrees of freedom
Multiple R-squared:  0.3273,    Adjusted R-squared:  0.2834 
F-statistic: 7.461 on 3 and 46 DF,  p-value: 0.000359

The main effect of Illiteracy rate is not significant (p-value > 0.05), but Murder rate is significantly related to Income (p-value < 0.05). The interaction of these two rates is significant (p-value < 0.05). The model is written as: \[Income = 3822.61 + 617.34\cdot Illiteracy + 146.82 \cdot Murder -117.10 \cdot Illiteracy:Murder \] \[ Income = 3822.61 + 617.34\cdot Illiteracy + 146.82 \cdot Murder -117.10 \cdot Illiteracy \cdot Murder\] Note how the 2nd line writes the interaction as actual product of two predictor variables. Note that we could have written the interaction of continuous and categorical variable in the previous example as a product as well, the dummy coding or 0 and 1 would multiply correctly (0 = term is absent, 1 = term is present).

The interpretation of the continuous variables interaction depends on the sign \(\pm\) of the interaction coefficient. If it is negative (as it is here), as predictors increase, the mean response is reduced (after computing the main effects). If it is positive, as the predictors increase, the mean response is additionally amplified. As both illiteracy and murder rates increase, average income is decreased in a state. It is interesting to compare the model with interaction above with the model without interaction and individual models:

mymodel2 <- lm(Income ~ Illiteracy + Murder, data = states)
summary(mymodel2)

Call:
lm(formula = Income ~ Illiteracy + Murder, data = states)

Residuals:
    Min      1Q  Median      3Q     Max 
-880.90 -397.26  -51.31  333.13 1960.68 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  4890.45     187.64  26.063  < 2e-16 ***
Illiteracy   -548.74     184.60  -2.973  0.00465 ** 
Murder         25.40      30.48   0.833  0.40895    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 560.2 on 47 degrees of freedom
Multiple R-squared:  0.2028,    Adjusted R-squared:  0.1689 
F-statistic: 5.979 on 2 and 47 DF,  p-value: 0.004861
mymodel3 <- lm(Income ~ Illiteracy, data = states)
summary(mymodel3)

Call:
lm(formula = Income ~ Illiteracy, data = states)

Residuals:
    Min      1Q  Median      3Q     Max 
-948.89 -376.20  -49.77  347.00 2024.60 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   4951.3      172.3  28.739  < 2e-16 ***
Illiteracy    -440.6      130.9  -3.367  0.00151 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 558.4 on 48 degrees of freedom
Multiple R-squared:  0.191, Adjusted R-squared:  0.1742 
F-statistic: 11.34 on 1 and 48 DF,  p-value: 0.001505
mymodel4 <- lm(Income ~ Murder, data = states)
summary(mymodel4)

Call:
lm(formula = Income ~ Murder, data = states)

Residuals:
     Min       1Q   Median       3Q      Max 
-1141.64  -483.71   -19.01   403.31  2029.40 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  4718.36     192.51  24.510   <2e-16 ***
Murder        -38.30      23.38  -1.638    0.108    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 604.2 on 48 degrees of freedom
Multiple R-squared:  0.05294,   Adjusted R-squared:  0.03321 
F-statistic: 2.683 on 1 and 48 DF,  p-value: 0.108
newdata = data.frame(Illiteracy=0.6,Murder=4)
predict(mymodel, newdata, interval = "prediction")
       fit      lwr      upr
1 4499.252 3425.301 5573.204
predict(mymodel, newdata, interval = "confidence")
       fit      lwr      upr
1 4499.252 4260.184 4738.321

We can see that without interaction the illiteracy effect is reversed and both individual models have negative effects of Illiteracy and Murder respectively.

Finally, let’s consider two categorical variables interaction.
We have actually considered the interactions between two categorical explanatory variables in the two-way ANOVA. Therefore, the same ToothGrowth file from Base R data sets is used. This file contains the data on the effect of vitamin C on tooth growth in guinea pigs. Each animal received one of three dose levels of vitamin C (0.5, 1, and 2 mg/day) by one of two delivery methods, orange juice or ascorbic acid (OJ vs. VC).

# Interaction two categorical 
mydata = ToothGrowth
mydata$dose = factor(mydata$dose)
mymodel = lm(len ~ supp*dose,data=mydata)
summary(mymodel)

Call:
lm(formula = len ~ supp * dose, data = mydata)

Residuals:
   Min     1Q Median     3Q    Max 
 -8.20  -2.72  -0.27   2.65   8.27 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)    13.230      1.148  11.521 3.60e-16 ***
suppVC         -5.250      1.624  -3.233  0.00209 ** 
dose1           9.470      1.624   5.831 3.18e-07 ***
dose2          12.830      1.624   7.900 1.43e-10 ***
suppVC:dose1   -0.680      2.297  -0.296  0.76831    
suppVC:dose2    5.330      2.297   2.321  0.02411 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.631 on 54 degrees of freedom
Multiple R-squared:  0.7937,    Adjusted R-squared:  0.7746 
F-statistic: 41.56 on 5 and 54 DF,  p-value: < 2.2e-16
library(ggpubr); 
ggline(mydata, x = "dose", y = "len", color = "supp",
       add = c("mean_se", "dotplot"))

interaction.plot(x.factor = mydata$dose, #x-axis variable
                 trace.factor = mydata$supp, #variable for lines
                 response = mydata$len, #y-axis variable
                 fun = median, #metric to plot
                 ylab = "tooth length", xlab = "dose",
                 col = c("red", "blue"), trace.label = "supp")

There is a term for each non-reference level of the first predictor combined with all non-reference levels of the second predictor. The delivery method VC is significant (p-value < 0.05) compared to the reference level OJ. The dose1 and dose2 are both significant compared to the reference dose of 0.5 (p-value << 0.05). The interaction of delivery suppVC with dose1 is not significant (p-value > 0.05), but suppVC with dose2 is significant (p-value < 0.05).
These results provide the same conclusion as the ANOVA analysis shown below:

anovamod = aov(len ~ supp*dose,data=mydata)
summary(anovamod)
            Df Sum Sq Mean Sq F value   Pr(>F)    
supp         1  205.4   205.4  15.572 0.000231 ***
dose         2 2426.4  1213.2  92.000  < 2e-16 ***
supp:dose    2  108.3    54.2   4.107 0.021860 *  
Residuals   54  712.1    13.2                     
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

The model is: \[len = 13.23-5.25 \cdot suppVC + 9.47\cdot dose1 + 12.83\cdot dose2 \] \[ -0.68 \cdot suppVC \cdot dose1 + 5.33 \cdot suppVC \cdot dose2\] The model is seen as a series of additive terms to the overall intercept. Depending on a particular choice of the predictors, we get average estimate. For example, for a pig given supplement via ascorbic acid (\(VC\)) with \(dose = 2\), we obtain the following predictions:

newdata = data.frame(supp="VC",dose="2")
predict(mymodel, newdata, interval = "prediction")
    fit     lwr     upr
1 26.14 18.5041 33.7759
predict(mymodel, newdata, interval = "confidence")
    fit      lwr      upr
1 26.14 23.83769 28.44231

Model selection

In the the beginning of this multiple regression chapter, we have started with a number of predictors which might be related to the response variable and removed them one by one using the highest (least significant) p-value. Keeping such irrelevant variables would only hinder the accuracy and explanatory power of the model. There are many other selection strategies which help us to eliminate (prune out) unnecessary predictors from the model to obtain a better model. A model that includes all available explanatory variables is called the full model.

The main goal of fitting a statistical model is to faithfully represent the data and the relationships held within them. It is a balancing act between goodness-of-fit and complexity. Complexity is tied to the number of terms and additional functions (transformations, interactions, etc…). The principle of parsimony is the principle of model selection to find a model that’s as simple as possible (relatively low complexity), without sacrificing goodness-of-fit in a major way. Of course, assessing the significance of the effect of predictors or functions of predictors on the response plays the major role in these decision. The number of possibilities for variable selection grows exponentially with the number of variables, so the systematic selection algorithms are essential in this analysis. There are a number of model selection algorithms which often lead to different final models. As George Box once said “All models are wrong, but some are useful.”

It should be noted also, that the data needs to be split into training part on which the model is created and testing part on which we test the model. If we test the model on the same data on which the model was created the fit tends to be too optimistic and the overfitting to the particular data set tends to occur with worse results for the new test data…

The general guidelines on predictor variables inclusion/exclusion are:
1) Individual insignificant levels of categorical predictor cannot be removed if at least one of the non-reference levels is significant.
2) If an interaction is present in the model, all lower-order main effects of the interacting predictors cannot be removed even if they are not statistically significant.
3) In polynomial transformation models, keep all lower-order polynomial terms in the model if the highest is deemed significant.

Prediction Accuracy
Consider the expected prediction error at point x: \[ Error(x) = E( Y - \hat{f}(x))^2\] It can be decomposed into three terms: \[ Error(x) = (E[\hat{f}(x)]-f(x))^2 + E[(\hat{f}(x)-E[\hat{f}(x)])^2] + \sigma_{\epsilon}^2\] \[ Error(x) = \ \ Bias^2 + \ \ \ \ \ \ \ \ \ \ Variance + \ \ \ \ \ \ Irreducible \ Error\]

The bias is the error in approximating a real-life problem by a model. For example, linear regression assumes that there is a linear relationship between the response and predictors. If the true \(f(x)\) substantially non-linear, it will not be possible to produce an accurate estimate using linear regression - high bias (underfitting) . Generally, more flexible methods result in less bias.

The variance is the change in \(\hat{f}\) if it is estimated with a different training data set. More flexible statistical methods have higher variance. In a flexible nonlinear fit, changing any data point may cause the estimate \(\hat{f}\) to change considerably resulting in high variance (overfitting). A least squares line fit is relatively inflexible and has low variance, because moving any single observation slightly will likely cause only a small shift in the position of the line.

The ideas of Bias and Variance are well illustrated in the Figure below:

# install.packages("imager")
library(imager)
file = "https://raw.githubusercontent.com/leonkag/Statistics0/main/VarianceBiasTarget.JPG"
im <- load.image(file)
trying URL 'https://raw.githubusercontent.com/leonkag/Statistics0/main/VarianceBiasTarget.JPG'
Content type 'image/jpeg' length 38670 bytes (37 KB)
downloaded 37 KB
plot(im)

As we increase flexibility of the methods, the variance will increase and the bias will decrease. However, the bias tends to initially decrease faster than the variance increases. Consequently, the expected test Mean Squared Error (\(MSE\)) declines. However, at some point increasing flexibility provides only small improvements to the bias but significantly increases the variance. Then the test \(MSE\) increases. We can see this bias-variance trade-off in the Figure below.

library(imager)
file = "https://raw.githubusercontent.com/leonkag/Statistics0/main/BiasVarianceTradeoff.JPG"
im <- load.image(file)
trying URL 'https://raw.githubusercontent.com/leonkag/Statistics0/main/BiasVarianceTradeoff.JPG'
Content type 'image/jpeg' length 33961 bytes (33 KB)
downloaded 33 KB
plot(im)

Model Selection Criteria

Which criteria can be used in the selection algorithms? The least squares model is designed to minimize the Residual Sum of Squares which we defined before, but we repeat it again here: \[RSS=SSE=\sum_{i=1}^n [y_i -(b_0 + b_1 x_{1,i} +b_2 x_{2,i}+ ... + b_p x_{p,i})]^2\]

Generally, the training set Mean Squared Error (\(MSE\)) is an underestimate of the \(MSE\) for the test data set. The reason is that a model is fit to the training data using least squares, the regression coefficients are chosen to minimize the training \(RSS\). The training \(RSS\) always decreases as we add more features to the model, but the test error may increase. Therefore the training \(RSS\) and \(r^2\) may not be used for selecting the best model without the adjustment for this underestimation.

Adding new predictors (even irrelevant) can only reduce \(RSS\) and increase regular coefficient of determination, so we defined an adjusted \(r^2_{adj}\): \[r^2_{adj} = 1-\frac{\frac{SSE}{n-p-1}}{\frac{SST}{n-1}} \] However, it tends to vary only slightly when we reduce our models. There are many other, more efficient measures, used for model selection.

For example, Mallows’s Cp \[C_p=\frac{1}{n}(RSS +2p\hat{\sigma}^2) \] where \(RSS\) is the residual sum of squares on a training set of data, \(p\) is the number of predictors and \(\hat{\sigma }^{2}\) refers to an estimate of the variance associated with each response in the linear model (estimated on a model containing all predictors). A small value of \(C_p\) means that the model is relatively precise. The \(C_p\) adds a penalty of \(2p\hat{\sigma}^2\) to the training \(RSS\) in order to adjust for the fact that the training error tends to underestimate the new data test error. Only if the decrease in \(RSS\) is more than increase in penalty term, will \(C_p\) go down.

For our least squares model, Mallows’s \(C_p\) is equivalent to Akaike information criterion (\(AIC\)) defined for a large class of models fit by maximum likelihood \(\hat{L}\).
\[AIC = 2p-\ln(\hat{L}) = \frac{1}{n\hat{\sigma}^2}(RSS +2p\hat{\sigma}^2)\] Bayes Information Criterion (\(BIC\)) is derived from a Bayesian Probability theory. For the least squares model with \(p\) predictors, the \(BIC\) is, up to irrelevant constants, given by: \[BIC=\frac{1}{n}(RSS +\log(n)p\hat{\sigma}^2) \] Like \(C_p\), the \(BIC\) is smaller for models with lower test error, so we are looking for model with the lowest BIC value. \(log(n) > 2\) for any reasonable sample size \(n\), so BIC has bigger penalty on models with many variables, and therefore produces smaller models than \(C_p\).

Best Subset Selection - a least squares regression is fit for each possible combination of the \(p\) predictors. There are \(p\) models with one predictor, \(\frac{p(p-1)}{2}\) models with two predictors, etc… Each predictor can be in or out of the model, so there are \(2^p\) possible models. Say, for \(p=10\) predictors, there are \(2^{10}=1024\) models to investigate. The algorithm proceeds as follows:
1) Let \(M_0\) be the null model with no predictors. It simply predicts the sample mean for each observation.
2) For \(k = 1, 2, . . . p\), fit all \(_kC_p\) models with exactly \(k\) predictors. Pick the best according to smallest \(RSS\) and call it \(M_k\).
3) Select the best model from among \(M_0,..., M_p\) using \(C_p (AIC)\), \(BIC\), \(r^2_{adj}\), or cross validated predicton error.
Step 2 identifies the best model for the training data, then step 3 picks the best using the criteria penalizing for inclusion of non-relevant predictors.

The exponential growth of the number of models is a serious computational issue. Also, the models with very large number of predictors are hard to interpret. Therefore, forward and backward stepwise selection is often used instead. Forward stepwise selection begins with no-predictors model and adds a predictor that gives the greatest additional improvement to the \(RSS\) fit one-at-a-time. Backward stepwise selection begins with the full model with all \(p\) predictors, and then iteratively removes the least useful predictor, one-at-a-time.

Subset Selection Methods

In the Best Subset Selection a least squares regression is fit for each possible combination of the \(p\) predictors. There are \(p\) models with one predictor, \(\frac{p(p-1)}{2}\) models with two predictors, etc… Each predictor can be in or out of the model, so there are \(2^p\) possible models. Say, for \(p=10\) predictors, there are \(2^{10}=1024\) models to investigate. The algorithm proceeds as follows:
1) Let \(M_0\) be the null model with no predictors. It simply predicts the sample mean for each observation.
2) For \(k = 1, 2, . . . p\), fit all \(_kC_p\) models with exactly \(k\) predictors. Pick the best according to smallest \(RSS\) and call it \(M_k\).
3) Select the best model from among \(M_0,..., M_p\) using \(C_p (AIC)\), \(BIC\), \(r^2_{adj}\), or cross validated predicton error.
Step 2 identifies the best model for the training data, then step 3 picks the best using the criteria penalizing for inclusion of non-relevant predictors.

The exponential growth of the number of models is a serious computational issue. Also, the models with very large number of predictors are hard to interpret. Therefore, forward and backward stepwise selection is often used instead. Forward stepwise selection begins with no-predictors model and adds a predictor that gives the greatest additional improvement to the \(RSS\) fit one-at-a-time. Backward stepwise selection begins with the full model with all \(p\) predictors, and then iteratively removes the least useful predictor, one-at-a-time.

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmVkaXRvcl9vcHRpb25zOiANCiAgbWFya2Rvd246IA0KICAgIHdyYXA6IDcyDQotLS0NCg0KIyBNdWx0aXBsZSBhbmQgTG9naXN0aWMgUmVncmVzc2lvbg0KDQpJbiB0aGUgcHJldmlvdXMgY2hhcHRlciB3ZSBzdHVkaWVkIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiB3aXRoIGENCnNpbmdsZSBleHBsYW5hdG9yeSB2YXJpYWJsZSwgYnV0IHVzdWFsbHkgdGhlIHJlc3BvbnNlIGRlcGVuZHMgb24gYQ0KbnVtYmVyIG9mIGV4cGxhbmF0b3J5IChwcmVkaWN0b3IpIHZhcmlhYmxlcyB3aGljaCBjb3JyZXNwb25kcyB0byBhDQoqKm11bHRpcGxlIHJlZ3Jlc3Npb24qKi4gV2UgaGF2ZSBhbHNvIHJlc3RyaWN0ZWQgdGhlIHJlc3BvbnNlIChvdXRjb21lKQ0KdmFyaWFibGUgdG8gYmUgbnVtZXJpY2FsLCBpbiB0aGlzIGNoYXB0ZXIgd2UgYWxzbyBsZWFybiAqKmxvZ2lzdGljDQpyZWdyZXNzaW9uKiogdXNlZCBmb3IgcHJlZGljdGluZyBjYXRlZ29yaWNhbCBvdXRjb21lcyB3aXRoIHR3byBsZXZlbHMNCihiaW5hcnkgcmVzcG9uc2UgdmFyaWFibGUpLg0KDQojIyBNdWx0aXBsZSBSZWdyZXNzaW9uDQoNClJhdGhlciB0aGFuIGhhdmluZyBqdXN0IG9uZSBwcmVkaWN0b3IsIHdlIG5vdyB3YW50IHRvIGRldGVybWluZSB0aGUNCnZhbHVlIG9mIGEgKipjb250aW51b3VzIHJlc3BvbnNlKiogdmFyaWFibGUgJHkkIGdpdmVuIHRoZSB2YWx1ZXMgb2YNCiRwID4gMSQgaW5kZXBlbmRlbnQgZXhwbGFuYXRvcnkgdmFyaWFibGVzICR4XzEsIHhfMiwuLi4geF9wJA0KKHByZWRpY3RvcnMpLiBUaGUgbGluZWFyIG1vZGVsIGlzIGRlZmluZWQgYXM6DQoNClxiZWdpbntlcXVhdGlvbn0gDQpcaGF0e3l9ID0gRShZfFhfMT14XzEsWF8yPXhfMiwuLi5YX3A9eF9wKSA9IGJfMCArIGJfMSAgeF8xICtiXzIgIHhfMisgLi4uICsgYl9wICB4X3AgDQpcZW5ke2VxdWF0aW9ufSBzbyB0aGF0IGF2ZXJhZ2UgcHJlZGljdGVkICRcaGF0e3l9JCBpcyBhIGxpbmVhciBmdW5jdGlvbg0Kb2YgcHJlZGljdG9ycyAkeF8xLCB4XzIsLi4uIHhfcCQgd2l0aCBlc3RpbWF0ZWQgcmVncmVzc2lvbiBjb2VmZmljaWVudHMNCihzbG9wZXMpICRiXzEsIGJfMiwuLi4gYl9wJC4gTm90ZSB0aGF0IGVhY2ggc2xvcGUgJGJfaSQgaXMgdGhlIGF2ZXJhZ2UNCmNoYW5nZSBpbiB0aGUgcmVzcG9uc2UgdmFyaWFibGUgJHkkIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHVuaXQgY2hhbmdlIGluDQp2YXJpYWJsZSAkeF9pJCAqKndoaWxlIGFsbCBvdGhlciBleHBsYW5hdG9yeSB2YXJpYWJsZXMgYXJlIGhlbGQNCmNvbnN0YW50KiouDQoNCkluIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiB3aXRoIG9ubHkgb25lIHByZWRpY3RvciB2YXJpYWJsZSwgdGhlIGdvYWwNCndhcyB0byBmaW5kIHRoZSAibGluZSBvZiBiZXN0IGZpdC4iIE5vdywgdGhlIGRhdGEgaXMgbXVsdGlkaW1lbnNpb25hbA0KYW5kIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiByZXNwb25zZSBhbmQgcHJlZGljdG9ycyBpcyBhDQptdWx0aWRpbWVuc2lvbmFsIHBsYW5lIChoeXBlci1wbGFuZSksIHdoaWNoIHdlIHdpbGwgbm90IGJlIGFibGUgdG8gcGxvdA0KYXMgYSBzaW1wbGUgc2NhdHRlci1wbG90IHdpdGggYSBsaW5lIGFzIGJlZm9yZS4gV2Ugd2FudCB0byBmaW5kIHRoZQ0KaHlwZXItcGxhbmUgdGhhdCBiZXN0IGZpdHMgdGhlIG11bHRpdmFyaWF0ZSBkYXRhIGluIHRlcm1zIG9mIG1pbmltaXppbmcNCnRoZSBzcXVhcmVkIGRpc3RhbmNlIGJldHdlZW4gdGhlIHBsYW5lIGFuZCBnaXZlbiB5LXZhbHVlcy4gU3BlY2lmaWNhbGx5LA0KdGhlICRiXzEsIGJfMiwuLi4gYl9wJCBhcmUgZm91bmQgYXMgdGhlIHZhbHVlcyB0aGF0IG1pbmltaXplIHRoZSAqKnN1bQ0Kb2Ygc3F1YXJlZCBlcnJvcnMgKFNTRSkgb3IgcmVzaWR1YWwgc3VtIG9mIHNxdWFyZXMgKFJTUykqKjoNClxiZWdpbntlcXVhdGlvbn0gDQpTU0U9UlNTPVxzdW1fe2k9MX1ebiBbeV9pIC0oYl8wICsgYl8xICB4X3sxLGl9ICtiXzIgIHhfezIsaX0rIC4uLiArIGJfcCAgeF97cCxpfSldXjINClxlbmR7ZXF1YXRpb259DQoNClRoZSBhc3N1bXB0aW9uIGFuYWx5c2lzIGlzIGluIG1hbnkgd2F5cyBzaW1pbGFyIHRvIHNpbXBsZSByZWdyZXNzaW9uLg0KVGhlcmUgaXMsIGhvd2V2ZXIsIG9uZSBhZGRpdGlvbmFsIGltcG9ydGFudCBhbmQgc3VidGxlIGFzc3VtcHRpb24gZm9yDQoqbXVsdGlwbGUgcmVncmVzc2lvbiogLSAqKmluZGVwZW5kZW5jZSBvZiB0aGUgcHJlZGljdG9yIHZhcmlhYmxlcyBmcm9tDQplYWNoIG90aGVyKiouIElmIHRoZSB2YXJpYWJsZXMgYXJlIGRlcGVuZGVudCwgdGhleSBhcmUgY2FsbGVkDQoqKmNvbGxpbmVhcioqLiBTdWNoIHZhcmlhYmxlcyBhcmUgc3Ryb25nbHkgY29ycmVsYXRlZCB3aGljaCBtYWtlcyBpdA0KZGlmZmljdWx0IHRvIHNlcGFyYXRlIG91dCB0aGUgaW5kaXZpZHVhbCBlZmZlY3RzIG9mIGNvbGxpbmVhciB2YXJpYWJsZXMNCm9uIHRoZSByZXNwb25zZS4gVGhlIGNvbGxpbmVhcml0eSBjYXVzZXMgdGhlIHN0YW5kYXJkIGVycm9yIChTRSkgZm9yDQplYWNoIGNvZWZmaWNpZW50ICRiX2kkIHRvIGdyb3cuIFRoZSB0LXN0YXRpc3RpYyBmb3IgZWFjaCBwcmVkaWN0b3IgaXMNCiRiX2kvU0UkLCBzbyAkdCQgZGVjbGluZXMgYW5kIHAtdmFsdWUgZ3Jvd3MsIHRoZXJlZm9yZSB3ZSBtYXkgZmFpbCB0bw0KcmVqZWN0IEgwIDogJFxiZXRhX2kgPSAwJC4gVGhpcyBtZWFucyB0aGF0IHRoZSBwb3dlciBvZiB0aGUgaHlwb3RoZXNpcw0KdGVzdCAodGhlIHByb2JhYmlsaXR5IG9mIGNvcnJlY3RseSBkZXRlY3RpbmcgYSBub24temVybyBjb2VmZmljaWVudCkgaXMNCnJlZHVjZWQgYnkgY29sbGluZWFyaXR5LiBUaGVyZWZvcmUsIGl0IHJlZHVjZXMgdGhlIGFjY3VyYWN5IG9mIHRoZQ0KZXN0aW1hdGVzIG9mIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cy4NCg0KQSBmaXJzdCBzdGVwIHRvIGRldGVjdCBjb2xsaW5lYXJpdHkgaXMgdG8gY29uc2lkZXIgdGhlIGNvcnJlbGF0aW9uDQptYXRyaXggb2YgdGhlIHByZWRpY3RvcnMuIEhpZ2ggY29ycmVsYXRpb24gd291bGQgaW5kaWNhdGUgYSBjb2xsaW5lYXJpdHkNCnByb2JsZW0gaW4gdGhlIGRhdGEuIEhvd2V2ZXIsIGl0IGlzIHBvc3NpYmxlIGZvciBjb2xsaW5lYXJpdHkgdG8gZXhpc3QNCmJldHdlZW4gdGhyZWUgb3IgbW9yZSB2YXJpYWJsZXMgZXZlbiBpZiBwYWlyd2lzZSBjb3JyZWxhdGlvbnMgYXJlDQptb2RlcmF0ZSAtICoqbXVsdGljb2xsaW5lYXJpdHkqKi4gQSBiZXR0ZXIgd2F5IHRvIGFzc2Vzcw0KbXVsdGljb2xsaW5lYXJpdHkgaXMgdG8gY29tcHV0ZSB0aGUgdmFyaWFuY2UgaW5mbGF0aW9uIGZhY3RvciAoVklGKQ0Kd2hpY2ggaXMgdGhlIHJhdGlvIG9mIHRoZSB2YXJpYW5jZSBvZiAkYl9qJCB3aGVuIGZpdHRpbmcgdGhlIGZ1bGwgbW9kZWwNCmRpdmlkZWQgYnkgdGhlIHZhcmlhbmNlIG9mICRiX2okIGlmIGZpdCBvbiBpdHMgb3duLiBUaGUgc21hbGxlc3QNCnBvc3NpYmxlIFZJRiBpcyAxIGlmIG5vIGNvbGxpbmVhcml0eSBpcyBvYnNlcnZlZC4gQXMgYSBydWxlIG9mIHRodW1iLCBhDQpWSUYgdmFsdWUgdGhhdCBleGNlZWRzIDUgaW5kaWNhdGVzIGEgcHJvYmxlbWF0aWMgYW1vdW50IG9mIGNvbGxpbmVhcml0eS4NCklmIGNvbGxpbmVhcml0eSBpcyBoaWdoLCB3ZSBjYW4gZHJvcCBvciBjb21iaW5lIHN1Y2ggdmFyaWFibGVzIHNpbmNlDQp0aGV5IGFyZSByZWR1bmRhbnQgaW4gdGhlIHByZXNlbmNlIG9mIHRoZSBvdGhlciB2YXJpYWJsZXMuDQoNClRoZSBoaWdoIFZJRiB2YWx1ZXMsIGhvd2V2ZXIsIGFyZSAqbm90IGFsd2F5cyogYSBiaWcgcHJvYmxlbS4gRm9yDQpleGFtcGxlLCB3ZSBjb25zaWRlciBsYXRlciBpbiB0aGlzIGNoYXB0ZXIgcG9seW5vbWlhbCByZWdyZXNzaW9uIHdpdGgNCnZhcmlhYmxlcyAkeCQsICR4XjIkLCAkeF4zJCwgd2hpY2ggaGF2ZSBoaWdoIFZJRidzLCBidXQgcC12YWx1ZXMgaW4gdGhpcw0KY2FzZSBhcmUgbm90IGFmZmVjdGVkIGJ5IG11bHRpY29sbGluZWFyaXR5LiBBbmFsb2dvdXNseSwgaWYgdGhlIG1vZGVsDQppbmNsdWRlcyBpbnRlcmFjdGlvbiBvZiB0d28gdmFyaWFibGVzICR4JCwgJHkkLCB3cml0dGVuIGFzIHByb2R1Y3QNCiR4IFxjZG90IHkkLCBtdWx0aWNvbGxpbmVhcml0eSAoVklGJ3MpIGFyZSBoaWdoIGJ1dCBwLXZhbHVlcyBhcmUgbm90DQphZmZlY3RlZCBhZ2Fpbi4gRmluYWxseSwgaW4gdGhlIGNhc2Ugb2YgY2F0ZWdvcmljYWwgdmFyaWFibGUgd2l0aCB0aHJlZQ0Kb3IgbW9yZSBjYXRlZ29yaWVzLCBpZiB0aGUgcHJvcG9ydGlvbiBvZiBjYXNlcyBpbiB0aGUgcmVmZXJlbmNlIGNhdGVnb3J5DQppcyBzbWFsbCwgdGhlIGluZGljYXRvciB2YXJpYWJsZXMgd2lsbCBuZWNlc3NhcmlseSBoYXZlIGhpZ2ggVklGcy4gVGhlDQpzb2x1dGlvbiBpcyBqdXN0IHRvIGNoYW5nZSB0aGUgcmVmZXJlbmNlIGNhdGVnb3J5Lg0KDQozLiAgVGhlIHZhcmlhYmxlcyB3aXRoIGhpZ2ggVklGcyBhcmUgaW5kaWNhdG9yIChkdW1teSkgdmFyaWFibGVzIHRoYXQNCiAgICByZXByZXNlbnQgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSB3aXRoIHRocmVlIG9yIG1vcmUgY2F0ZWdvcmllcy4gSWYNCiAgICB0aGUgcHJvcG9ydGlvbiBvZiBjYXNlcyBpbiB0aGUgcmVmZXJlbmNlIGNhdGVnb3J5IGlzIHNtYWxsLCB0aGUNCiAgICBpbmRpY2F0b3IgdmFyaWFibGVzIHdpbGwgbmVjZXNzYXJpbHkgaGF2ZSBoaWdoIFZJRnMsIGV2ZW4gaWYgdGhlDQogICAgY2F0ZWdvcmljYWwgdmFyaWFibGUgaXMgbm90IGFzc29jaWF0ZWQgd2l0aCBvdGhlciB2YXJpYWJsZXMgaW4gdGhlDQogICAgcmVncmVzc2lvbiBtb2RlbC4NCg0KU3VwcG9zZSwgZm9yIGV4YW1wbGUsIHRoYXQgYSBtYXJpdGFsIHN0YXR1cyB2YXJpYWJsZSBoYXMgdGhyZWUNCmNhdGVnb3JpZXM6IGN1cnJlbnRseSBtYXJyaWVkLCBuZXZlciBtYXJyaWVkLCBhbmQgZm9ybWVybHkgbWFycmllZC4gWW91DQpjaG9vc2UgZm9ybWVybHkgbWFycmllZCBhcyB0aGUgcmVmZXJlbmNlIGNhdGVnb3J5LCB3aXRoIGluZGljYXRvcg0KdmFyaWFibGVzIGZvciB0aGUgb3RoZXIgdHdvLiBXaGF0IGhhcHBlbnMgaXMgdGhhdCB0aGUgY29ycmVsYXRpb24NCmJldHdlZW4gdGhvc2UgdHdvIGluZGljYXRvcnMgZ2V0cyBtb3JlIG5lZ2F0aXZlIGFzIHRoZSBmcmFjdGlvbiBvZg0KcGVvcGxlIGluIHRoZSByZWZlcmVuY2UgY2F0ZWdvcnkgZ2V0cyBzbWFsbGVyLiBGb3IgZXhhbXBsZSwgaWYgNDUNCnBlcmNlbnQgb2YgcGVvcGxlIGFyZSBuZXZlciBtYXJyaWVkLCA0NSBwZXJjZW50IGFyZSBtYXJyaWVkLCBhbmQgMTANCnBlcmNlbnQgYXJlIGZvcm1lcmx5IG1hcnJpZWQsIHRoZSBWSUZzIGZvciB0aGUgbWFycmllZCBhbmQgbmV2ZXItbWFycmllZA0KaW5kaWNhdG9ycyB3aWxsIGJlIGF0IGxlYXN0IDMuMC4NCg0KSXMgdGhpcyBhIHByb2JsZW0/IFdlbGwsIGl0IGRvZXMgbWVhbiB0aGF0IHAtdmFsdWVzIGZvciB0aGUgaW5kaWNhdG9yDQp2YXJpYWJsZXMgbWF5IGJlIGhpZ2guIEJ1dCB0aGUgb3ZlcmFsbCB0ZXN0IHRoYXQgYWxsIGluZGljYXRvcnMgaGF2ZQ0KY29lZmZpY2llbnRzIG9mIHplcm8gaXMgdW5hZmZlY3RlZCBieSB0aGUgaGlnaCBWSUZzLiBBbmQgbm90aGluZyBlbHNlIGluDQp0aGUgcmVncmVzc2lvbiBpcyBhZmZlY3RlZC4gSWYgeW91IHJlYWxseSB3YW50IHRvIGF2b2lkIHRoZSBoaWdoIFZJRnMsDQpqdXN0IGNob29zZSBhIHJlZmVyZW5jZSBjYXRlZ29yeSB3aXRoIGEgbGFyZ2VyIGZyYWN0aW9uIG9mIHRoZSBjYXNlcy4NClRoYXQgbWF5IGJlIGRlc2lyYWJsZSBpbiBvcmRlciB0byBhdm9pZCBzaXR1YXRpb25zIHdoZXJlIG5vbmUgb2YgdGhlDQppbmRpdmlkdWFsIGluZGljYXRvcnMgaXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBldmVuIHRob3VnaCB0aGUNCm92ZXJhbGwgc2V0IG9mIGluZGljYXRvcnMgaXMgc2lnbmlmaWNhbnQuDQoNCk5vdGUgdGhhdCB3ZSB1c2VkICRyXjIkIGluIHNpbXBsZSByZWdyZXNzaW9uIGFzIGEgZnJhY3Rpb24gb2YNCnZhcmlhYmlsaXR5IGluIHRoZSByZXNwb25zZSB0aGF0IHdhcyBleHBsYWluZWQgYnkgdGhlIGxpbmVhciBtb2RlbDoNCiQkICByXjIgPSAxIC0gXGZyYWN7dmFyaWFiaWxpdHkgXCBpbiBcIHJlc2lkdWFsc317dmFyaWFiaWxpdHkgXCBpbiBcIG91dGNvbWV9ID0gMS1cZnJhY3tTU0V9e1NTVH0gPSAxLVxmcmFje1xzdW0gKHlfaS1caGF0e3l9X2kpXjJ9e1xzdW0gKHlfaS1cb3ZlcmxpbmV7eX0pXjJ9ICAkJA0KVGhpcyBzdHJhdGVneSBpcyBhY2NlcHRhYmxlIGZvciBqdXN0IG9uZSBwcmVkaWN0b3IgdmFyaWFibGUsIGhvd2V2ZXIsDQpmb3IgbWFueSBwcmVkaWN0b3JzLCAkcl4yJCBpcyBhIGJpYXNlZCBlc3RpbWF0ZSBvZiB0aGUgb2YgdmFyaWFiaWxpdHkNCmV4cGxhaW5lZCBieSB0aGUgbW9kZWwgd2hlbiBhcHBsaWVkIHRvIGEgKm5ldyBzYW1wbGUgb2YgZGF0YSouIFRoZSAkcl4yJA0KYWx3YXlzIGluY3JlYXNlcyB3aGV0aGVyIG9yIG5vdCBhZGRlZCBuZXcgdmFyaWFibGVzIGFyZSByZWxhdGVkIHRvIHRoZQ0KcmVzcG9uc2UuIFRvIGZpeCBpdCwgYW4gYWRqdXN0ZWQgJHJeMiQgaXMgaW50cm9kdWNlZC4gXGJlZ2lue2VxdWF0aW9ufSANCiAgcl4yX3thZGp9ICA9IDEtXGZyYWN7XGZyYWN7U1NFfXtuLXAtMX19e1xmcmFje1NTVH17bi0xfX0gDQpcZW5ke2VxdWF0aW9ufSB3aGVyZSAkbiQgaXMgdGhlIG51bWJlciBvZiBjYXNlcyB1c2VkIHRvIGZpdCB0aGUgbW9kZWwNCmFuZCAkcCQgaXMgdGhlIG51bWJlciBvZiBwcmVkaWN0b3IgdmFyaWFibGVzIGluIHRoZSBtb2RlbCwgaS5lLiBlYWNoDQp2YXJpYW5jZSBpcyBkaXZpZGVkIGJ5IGl0cyBkZWdyZWUgb2YgZnJlZWRvbS4gVG8gbWF4aW1pemUgdGhlDQokcl4yX3thZGp9JCwgd2UgbmVlZCB0byBtaW5pbWl6ZSAkXGZyYWN7U1NFfXtuLXAtMX0kLiBUaGUgJFNTRSQgYWx3YXlzDQpkZWNyZWFzZXMgYXMgd2UgYWRkIG5ldyB2YXJpYWJsZXMgdG8gdGhlIG1vZGVsIChzaWduaWZpY2FudCBvciBub3QpLCBidXQNCiRcZnJhY3tTU0V9e24tcC0xfSQgbWF5IGluY3JlYXNlIG9yIGRlY3JlYXNlLCBkdWUgdG8gdGhlIGluY3JlYXNpbmcgJHAkDQppbiB0aGUgZGVub21pbmF0b3IuIE9uY2UgYWxsIG9mIHRoZSByZWxldmFudCB2YXJpYWJsZXMgaGF2ZSBiZWVuDQppbmNsdWRlZCBpbiB0aGUgbW9kZWwsIGFkZGluZyBhZGRpdGlvbmFsIG5vaXNlIHZhcmlhYmxlcyB3b3VsZCByZXN1bHQgaW4NCmEgdmVyeSBzbWFsbCBkZWNyZWFzZSBpbiAkU1NFJCwgc28gJFxmcmFje1NTRX17bi1wLTF9JCB3aWxsIGluY3JlYXNlLA0KcmVzdWx0aW5nIGluIGRlY3JlYXNpbmcgJHJeMl97YWRqfSQuIFRoZXJlZm9yZSwgJHJeMl97YWRqfSQgc3RhdGlzdGljDQpwYXlzIGEgcHJpY2UgZm9yIHRoZSBpbmNsdXNpb24gb2YgdW5uZWNlc3NhcnkgdmFyaWFibGVzIGluIHRoZSBtb2RlbCBhbmQNCnRoZXkgd291bGQgbm90IGJlIGluY2x1ZGVkLiBJbiBhZGRpdGlvbiwgd2UgYXJlIGdlbmVyYWxseSBpbnRlcmVzdGVkIGluDQptYWtpbmcgcHJlZGljdGlvbnMgZm9yICpuZXcgZGF0YSosIHRoZW4gdGhlIHVuLWFkanVzdGVkICRyXjIkIHRlbmRzIHRvDQpiZSBvdmVybHkgb3B0aW1pc3RpYywgd2hpbGUgdGhlIGFkanVzdGVkICRyXjJfe2Fkan0kIGhlbHBzIHRvIGNvcnJlY3QNCnRoaXMgYmlhcy4NCg0KKipFeGFtcGxlKipcDQpJbnZlc3RpZ2F0ZSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBvZiBjZXNkIG9uIHNvbWUgbGlrZWx5DQpwcmVkaWN0b3JzIGluIHRoZSBIRUxQcmN0IGRhdGEgZmlsZS4NCg0KVGhlIGNvZGUgYmVsb3cgc2hvd3MgbGluZWFyIG1vZGVsIG9mIHRoZSBjZXNkIGRlcHJlc3Npb24gc2NvcmUgdnMuDQpzZXZlcmFsIG90aGVyIHZhcmlhYmxlcy4gVGhlIGFnZSwgbWNzIChtZW50YWwgc2NvcmUpLCBwY3MgKHBoeXNpY2FsDQpzY29yZSksIHBzc19mciAocGVyY2VpdmVkIHN1cHBvcnQgc3lzdGVtIGZyaWVuZHMpIGFyZSBudW1lcmljYWwsIHdoaWxlDQpzZXgsIGhvbWVsZXNzLCBhbmQgc3Vic3RhbmNlIGFyZSBjYXRlZ29yaWNhbCB3aGljaCBtdXN0IGJlIGNvbnZlcnRlZCB0bw0KaW5kaWNhdG9yIHZhcmlhYmxlcyBhZ2FpbnN0IHJlZmVyZW5jZSBsZXZlbHMuIFRoZSBkaWFnbm9zdGljIGZpZ3VyZXMNCnNob3cgdGhhdCB0aGVyZSBpcyBubyBwYXR0ZXJuIG9mIHJlc2lkdWFscyBhbmQgY29uc3RhbnQgdmFyaWFiaWxpdHkNCmFzc3VtcHRpb24gaXMgc2F0aXNmaWVkIGJlY2F1c2UgdGhlcmUgaXMgbm8gZnVubmVsaW5nIG9mIHRoZSByZXNpZHVhbHMuDQpUaGUgUVEgcGxvdCBzaG93cyB0aGF0IHRoZSByZXNpZHVhbHMgYXJlIGFsbW9zdCBwZXJmZWN0bHkgbm9ybWFsbHkNCmRpc3RyaWJ1dGVkIChubyBvdXRsaWVycyBhdCBhbGwsIG5vdCB0byBtZW50aW9uIGFueSBleHRyZW1lIG91dGxpZXJzKS4NCkFsc28sIHRoZSBhc3N1bXB0aW9uIG9mIGluZGVwZW5kZW50IG9ic2VydmF0aW9ucyAobm8gdGltZSBzZXJpZXMpIGlzDQpzYXRpc2ZpZWQgdG9vIChEdXJiaW4tV2F0c29uIGlzIGNsb3NlIHRvIDIpLiBSZXNpZHVhbHMgdnMgTGV2ZXJhZ2UgcGxvdA0KYWxzbyBzaG93cyBubyBleHRyZW1lIGJlaGF2aW9yLiBNb3N0IGltcG9ydGFudGx5LCB0aGUgY29ycmVsYXRpb25zIG9mDQpudW1lcmljYWwgdmFyaWFibGVzIGFuZCB2YXJpYW5jZSBpbmZsYXRpb24gY29lZmZpY2llbnRzIHNob3cgbm8gc2lnbiBvZg0KY29sbGluZWFyaXR5IG9mIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyB0byBlYWNoIG90aGVyLiBUaHVzLCB0aGUgbGluZWFyDQptb2RlbCBpcyBhcHBsaWNhYmxlLg0KDQpgYGB7cn0NCiMgbG9hZCBkYXRhc2V0DQp1cmw9Imh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9sZW9ua2FnL1N0YXRpc3RpY3MwL21haW4vSEVMUHJjdC5jc3YiDQpteWRhdGEgPSByZWFkLmNzdih1cmwsc3RyaW5nc0FzRmFjdG9ycyA9IFRSVUUpICAgIyBzYXZlIGFzIG15ZGF0YSBmaWxlDQpjYXQoJ1xuRGF0YSBGcmFtZSBkaW1lbnNpb25zOiAnLGRpbShteWRhdGEpKQ0KDQpteWRhdGEgPSBteWRhdGFbLGMoJ2Nlc2QnLCdhZ2UnLCdtY3MnLCdwY3MnLCdwc3NfZnInLCdob21lbGVzcycsJ3N1YnN0YW5jZScsJ3NleCcpXTsNCmNhdCgnXG5BZnRlciBjaG9vc2luZyB2YXJpYWJsZXMgZm9yIG91ciBzdHVkeSwgdGhlIGRhdGEgRnJhbWUgZGltZW5zaW9uczogJyxkaW0obXlkYXRhKSkNCnN1bW1hcnkobXlkYXRhKQ0KYGBgDQoNClRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHM6DQoNCmBgYHtyfQ0KbGlicmFyeShIbWlzYykNCnJjb3JyKGFzLm1hdHJpeCggc3Vic2V0KG15ZGF0YSxzZWxlY3Q9YyhjZXNkLGFnZSwgbWNzLHBjcyxwc3NfZnIpKSAgKSkNCmBgYA0KDQpXZSBjYW4gdmlzdWFsaXplIHRoZSBjb3JyZWxhdGlvbnMgd2l0aCBjb3JyZWxvZ3JhbXM6DQoNCmBgYHtyfQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJjb3JyZ3JhbSIpDQpsaWJyYXJ5KGNvcnJncmFtKQ0KY29ycmdyYW0oc3Vic2V0KG15ZGF0YSxzZWxlY3Q9YyhjZXNkLGFnZSwgbWNzLHBjcyxwc3NfZnIpKSwgDQogICAgICAgICBvcmRlcj1UUlVFLCBsb3dlci5wYW5lbD1wYW5lbC5zaGFkZSwNCiAgICAgICAgIHVwcGVyLnBhbmVsPXBhbmVsLnBpZSwgdGV4dC5wYW5lbD1wYW5lbC50eHQpDQpgYGANCg0KQWxzbywgdGhlIHBhaXJzIHBsb3QgY2FuIGJlIHVzZWZ1bDoNCg0KYGBge3J9DQpsaWJyYXJ5KEdHYWxseSkNCmdncGFpcnMoc3Vic2V0KG15ZGF0YSxzZWxlY3Q9YyhjZXNkLGFnZSwgbWNzLHBjcyxwc3NfZnIpKSkgDQpgYGANCg0KTGluZWFyIG1vZGVsOg0KDQpgYGB7cn0NCm15bW9kZWwgPSBsbSgnY2VzZH5hZ2UrbWNzK3Bjcytwc3NfZnIraG9tZWxlc3Mrc3Vic3RhbmNlK3NleCcsIGRhdGE9bXlkYXRhKQ0Kc3VtbWFyeShteW1vZGVsKQ0KY29uZmludChteW1vZGVsKQ0KYGBgDQoNClZhcmlhbmNlIGluZmxhdGlvbiBmYWN0b3JzDQoNCmBgYHtyfQ0KbGlicmFyeShjYXIpDQp2aWYobXltb2RlbCkNCmBgYA0KDQpEaWFnbm9zdGljIFBsb3RzOg0KDQpgYGB7cn0NCnBhcihtZnJvdz1jKDIsMikpDQpwbG90KG15bW9kZWwpDQpwYXIobWZyb3c9YygxLDEpKQ0KYGBgDQoNClRoZSBvdmVyYWxsIEFOT1ZBIEYgc3RhdGlzdGljIHRlc3QgaXMgaGlnaGx5IHNpZ25pZmljYW50LlwNCkZvciB0aGUgbXVsdGlwbGUgcmVncmVzc2lvbiwgdGhlIGh5cG90aGVzaXMgdGVzdCBpcyBwZXJmb3JtZWQgZm9yIGVhY2gNCmNvZWZmaWNpZW50IChzbG9wZSk6XA0KSDA6ICRcYmV0YV9qID0gMCQgemVybyBjb2VmZmljaWVudCAoc2xvcGUpIGZvciAkeF9qJCwgbm8gc2lnbmlmaWNhbnQNCnJlbGF0aW9uc2hpcC5cDQpIMTogJFxiZXRhX2ogXG5lIDAkIG5vbiB6ZXJvIGNvZWZmaWNpZW50IChzbG9wZSkgZm9yICR4X2okLCBzaWduaWZpY2FudA0KcmVsYXRpb25zaGlwLlwNClRoZSBjb2RlIHByb3ZpZGVzIHQtdGVzdCBhbmQgcC12YWx1ZSBmb3IgZWFjaCB2YXJpYWJsZSBhbmQgZWFjaA0KaW5kaWNhdG9yIHZhcmlhYmxlIGZvciBjYXRlZ29yaWNhbCBwcmVkaWN0b3JzLiBTb21lIG9mIHRoZW0gYXJlDQpzaWduaWZpY2FudCBvdGhlcnMgYXJlIG5vdC4gVGhlIDk1JSBjb25maWRlbmNlIGludGVydmFscyBmb3IgdGhlDQpjb2VmZmljaWVudHMgKHNsb3BlcykgY29uZmlybSB0aGUgdC10ZXN0IHdpdGggc2lnbmlmaWNhbnQgQ0kncyBub3QNCmNvbnRhaW5pbmcgMC5cDQpMZXQncyByZW1vdmUgdGhlIHByZWRpY3RvciB3aXRoIGhpZ2hlc3QgcC12YWx1ZSAtIGhvbWVsZXNzLiBBZ2UgaXMgYWxzbw0Kbm9uLXNpZ25pZmljYW50LCBidXQgd2UgaGF2ZSB0byByZW1vdmUgdmFyaWFibGVzIG9uZSBieSBvbmUgYW5kIGV4YW1pbmUNCnRoZSBzdW1tYXJ5IGJlY2F1c2UgZWFjaCB0aW1lICphbGwgdGhlIGNvZWZmaWNpZW50cyBhbmQgdGhlaXIgcC12YWx1ZXMNCmNoYW5nZSouDQoNCmBgYHtyfQ0KbXltb2RlbDIgPSB1cGRhdGUobXltb2RlbCwgLiB+IC4gLSBob21lbGVzcykNCnN1bW1vZGVsMiA9IHN1bW1hcnkobXltb2RlbDIpOyBzdW1tb2RlbDINCmNvbmZpbnQobXltb2RlbDIpICAgICAgDQpgYGANCg0KVGhlIGFnZSBpcyBzdGlsbCBpbnNpZ25pZmljYW50LCBzbyB3ZSByZW1vdmUgaXQgYXMgd2VsbCBhbmQgb2J0YWluIHRoZQ0KbW9kZWwgd2hlcmUgYWxsIHRoZSBwLXZhbHVlcyBcPCAwLjA1LCBpLmUuIGFsbCB2YXJpYWJsZXMgYXJlDQpzaWduaWZpY2FudC4NCg0KYGBge3J9DQpteW1vZGVsMyA9IHVwZGF0ZShteW1vZGVsMiwgLiB+IC4gLSBhZ2UpDQpzdW1tb2RlbDMgPSBzdW1tYXJ5KG15bW9kZWwzKTsgc3VtbW9kZWwzDQpjb25maW50KG15bW9kZWwzKQ0KYGBgDQoNClRoZXJlIGlzIGFsc28gYSBiaXQgbW9yZSBmb3JtYWwgd2F5IHRvIGNvbXBhcmUgc3VjaCAqKm5lc3RlZCBtb2RlbHMqKg0KdXNpbmcgYSAqKnBhcnRpYWwgRi1zdGF0aXN0aWMqKi4gQXNzdW1lIHdlIGhhdmUgb25lIG1vZGVsIG5lc3RlZCB3aXRoaW4NCnRoZSBvdGhlcjoNCiQkICBcaGF0e3l9X3tmdWxsfSA9IGJfMCtiXzF4XzErIGJfMnhfMisuLi4gK2JfcHhfcCsuLi4gK2JfcXhfcSAkJA0KJCQgICAgIFxoYXR7eX1fe3BhcnR9ID0gYl8wK2JfMXhfMSsgYl8yeF8yKy4uLiArYl9weF9wJCQgVGhlIHBhcnRpYWwNCkYtc3RhdGlzdGljcyBpcyBkZWZpbmVkIGFzOiBcYmVnaW57ZXF1YXRpb259IA0KRl97cGFydGlhbH0gPSBcZnJhY3soUl4yX3tmdWxsfS1SXjJfe3BhcnR9KShuLXEtMSl9eygxLVJeMl97ZnVsbH0pKHEtcCl9DQogIChcI2VxOlBhcnRpYWxGKQ0KXGVuZHtlcXVhdGlvbn0gd2hlcmUgJFJeMiQgZGVub3RlIHRoZSByZXNwZWN0aXZlIGNvZWZmaWNpZW50cyBvZg0KZGV0ZXJtaW5hdGlvbiBhbmQgJG4kIGlzIHRoZSBzYW1wbGUgc2l6ZSBvZiB0aGUgZGF0YS4gJEZfe3BhcnRpYWx9JA0KZm9sbG93cyBGLWRpc3RyaWJ1dGlvbiB3aXRoICRkZl8xID0gcSAtIHAkLCAkZGZfMiA9IG4gLSBxJCBkZWdyZWVzIG9mDQpmcmVlZG9tLiBUaGUgYW5vdmEoKSBmdW5jdGlvbiBydW5zIHRoaXMgdGVzdC4gVGhlIHAtdmFsdWUgaXMgZ2l2ZW4gYnkNCnRoZSByaWdodCB0YWlsIG9mIHRoaXMgRi1kaXN0cmlidXRpb24gYW5kIGlmIHNpZ25pZmljYW50LCBpdCBpbmRpY2F0ZXMNCnRoYXQgdGhlIGltcHJvdmVtZW50IGluIGdvb2RuZXNzLW9mLWZpdCBpcyBsYXJnZSBlbm91Z2ggdG8gbWFrZSB0aGUNCmFkZGl0aW9uYWwgY29tcGxleGl0eSB3b3J0aCBpdC4gV2UgaWxsdXN0cmF0ZSBpdCB3aXRoIHRoZSBjb2RlIGJlbG93Og0KDQpgYGB7cn0NCmFub3ZhKG15bW9kZWwsbXltb2RlbDIsbXltb2RlbDMpDQpgYGANCg0KQXMgZXhwZWN0ZWQgbXltb2RlbDIgYW5kIG15bW9kZWwzIGFyZSBub3Qgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZnJvbQ0KbXltb2RlbC5cDQpOb3csIGxldCdzIHJlbW92ZSBvbmUgb2YgdGhlIHZhcmlhYmxlcyB3aXRoIGxvdyBwLXZhbHVlLCBzYXkgcHNzX2ZyOg0KDQpgYGB7cn0NCm15bW9kZWw0ID0gdXBkYXRlKG15bW9kZWwzLCAuIH4gLiAtIHBzc19mcikNCmFub3ZhKG15bW9kZWwsbXltb2RlbDIsbXltb2RlbDMsbXltb2RlbDQpDQpgYGANCg0KQWRkaW5nIHBzc19mciBtYWtlcyBhIGRpZmZlcmVuY2UsIHRob3VnaCBhbmQgc2hvdWxkIGJlIGFkZGVkLg0KDQpBcyB3ZSB3ZXJlIHJlbW92aW5nIGluc2lnbmlmaWNhbnQgdmFyaWFibGVzIHRoZSBhZGp1c3RlZCAkcl4yX3thZGp9JA0KaW5jcmVhc2VkIHZlcnkgc2xpZ2h0bHkgJDAuNTI1MSA8IDAuNTI2MSA8IDAuNTI2NCQsIHdoaWNoIGltcGxpZXMgdGhlDQppbXByb3ZlbWVudCBvZiB0aGUgbW9kZWwuIE5vdGUgdGhhdCB0aGUgb3RoZXIgbWVhc3VyZXMgLSAkQUlDJCwgYW5kDQokQklDJCBleHBsYWluZWQgaW4gdGhlIG5leHQgc2VjdGlvbiB3aWxsIGNoYW5nZSBtb3JlIG5vdGljZWFibHkuDQoNCk5vdGUgdGhhdCBldmVuIHRob3VnaCBzdWJzdGFuY2U6aGVyb2luIGhhcyBwLXZhbHVlIHNsaWdodGx5IGFib3ZlIDAuMDUsDQpzdWJzdGFuY2U6Y29jYWluZSBoYXZlIHAtdmFsdWUgXDwgMC4wNSwgc28gd2UgY2Fubm90IHJlbW92ZSB0aGUNCnN1YnN0YW5jZSB2YXJpYWJsZS4gVXNpbmcgdGhpcyBtb2RlbCwgd2UgY2FuIGlsbHVzdHJhdGUgdGhlIG1lYW5pbmcgb2YNCnRoZSBjb2VmZmljaWVudHMgKHNsb3BlcykuXA0KSWYgYWxsIG90aGVyIHZhcmlhYmxlcyBhcmUga2VwdCBjb25zdGFudCwgbWFsZXMgaGF2ZSAqYXZlcmFnZSogY2VzZA0Kc2NvcmVzICQyLjUzJCBiZWxvdyB0aGFuIHRoZSByZWZlcmVuY2UgbGV2ZWwgb2YgZmVtYWxlcy4gQW5hbG9nb3VzbHksDQp3aXRoIGFsbCBvdGhlciB2YXJpYWJsZXMgYmVpbmcga2VwdCBjb25zdGFudCwgY29jYWluZSBhYnVzZXJzIGhhdmUNCmF2ZXJhZ2UgY2VzZCBzY29yZSAkMi41OSQgYmVsb3cgcmVmZXJlbmNlIGxldmVsIG9mIGFsY29ob2xpY3MgYW5kIGhlcm9pbg0KYWJ1c2VycyBoYXZlIGF2ZXJhZ2UgY2VzZCBzY29yZSAkMS44OCQgYmVsb3cgdGhlIHNhbWUgcmVmZXJlbmNlIGxldmVsLg0KRWFjaCBleHRyYSB1bml0IG9mIG1jcyBzY29yZSByZWR1Y2VzIGF2ZXJhZ2UgY2VzZCBzY29yZSBieSAkMC42MiQsIGFnYWluDQp3aXRoIGFsbCBvdGhlciB2YXJpYWJsZXMga2VwdCBjb25zdGFudCwgZXRjLi4uDQoNCldlIGNhbiBhbHNvIHVzZSB0aGlzIHJlZHVjZWQgbW9kZWwgdG8gcHJlZGljdCBjZXNkIHNjb3JlIGZvciwgc2F5LCB0aGUNCm1hbGUgYmVsb3c6DQoNCmBgYHtyfQ0KbmV3ZGF0YSA9IGRhdGEuZnJhbWUoc2V4PSJtYWxlIixzdWJzdGFuY2U9ImFsY29ob2wiLG1jcz0zMCxwY3MgPSAzNCwgcHNzX2ZyPTEwKQ0KcHJlZGljdChteW1vZGVsMywgbmV3ZGF0YSwgaW50ZXJ2YWwgPSAicHJlZGljdGlvbiIpDQpwcmVkaWN0KG15bW9kZWwzLCBuZXdkYXRhLCBpbnRlcnZhbCA9ICJjb25maWRlbmNlIikNCmBgYA0KDQo8YnIvPiAqKkV4YW1wbGUqKlwNCkludmVzdGlnYXRlIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIG9mIFdUIG9uIHNvbWUgbGlrZWx5DQpwcmVkaWN0b3JzIGluIHRoZSBNSEVBTFRIIGRhdGEgZmlsZS4NCg0KVGhlIGNvZGUgYmVsb3cgc2hvd3MgbGluZWFyIG1vZGVsIG9mIHRoZSB3ZWlnaHQgV1Qgb24gc2V2ZXJhbCBwcmVkaWN0b3INCnZhcmlhYmxlcy4gVGhpcyB0aW1lIGFsbCB0aGUgdmFyaWFibGVzIGFyZSBudW1lcmljYWwgc28gbm8gaW5kaWNhdG9yDQp2YXJpYWJsZXMgYXJlIG5lZWRlZC4gVGhlIGRpYWdub3N0aWMgZmlndXJlcyBzaG93IHRoYXQgdGhlcmUgaXMgYQ0Kbm9ubGluZWFyIHBhdHRlcm4gb2YgcmVzaWR1YWxzIHdoaWNoIGludmFsaWRhdGVzIHRoZSBsaW5lYXIgbW9kZWwuIEluDQphZGRpdGlvbiwgdGhlcmUgaXMgaGlnaCBjb3JyZWxhdGlvbiBiZXR3ZWVuIFdBSVNUIGFuZCBCTUkgZXRjLi4uIGFuZCB0aGUNCnJlc3VsdGluZyBWSUYgY29lZmZpY2llbnRzIGFyZSBtb3JlIHRoYW4gNSBmb3IgV0FJU1QgYW5kIEJNSS4gVGhlcmUgaXMNCm5vIHVuaXF1ZSBhbnN3ZXIgYXMgdG8gd2hpY2ggdmFyaWFibGUgdG8gcmVtb3ZlIDFzdCwgYnV0IGluIHRoaXMgY2FzZSwNCml0IHNob3VsZCBiZSBjbGVhciB0aGF0IGtlZXBpbmcgYm90aCBXQUlTVCBhbmQgQk1JIHdoaWNoIGlzIGRpcmVjdGx5DQpyZWxhdGVkIHRvIFdUIGlzIG5vdCByZWFzb25hYmxlLCBzbyB3ZSBhcmUgZ29pbmcgdG8gcmVtb3ZlIEJNSSAxc3QuIEluDQphZGRpdGlvbiwgdGhlIFFRIHBsb3Qgc2hvd3MgdGhhdCB0aGUgcmVzaWR1YWxzIGFyZSBub3Qgbm9ybWFsbHkNCmRpc3RyaWJ1dGVkLiBUaGUgYXNzdW1wdGlvbiBvZiBpbmRlcGVuZGVudCBvYnNlcnZhdGlvbnMgKG5vIHRpbWUgc2VyaWVzKQ0KaXMgc2F0aXNmaWVkLiBSZXNpZHVhbHMgdnMgTGV2ZXJhZ2UgcGxvdCBzaG93cyBubyBleHRyZW1lIGJlaGF2aW9yLg0KDQpgYGB7cn0NCiMgTUhFQUxUSCAgd2FnZSBtdWx0aXBsZSByZWdyZXNzaW9uDQpybShsaXN0PWxzKCkpDQp1cmw9Imh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9sZW9ua2FnL1N0YXRpc3RpY3MwL21haW4vTUhFQUxUSC5jc3YiDQpteWRhdGEgPSByZWFkLmNzdih1cmwsc3RyaW5nc0FzRmFjdG9ycyA9IFRSVUUpICAgIyBzYXZlIGFzIG15ZGF0YSBmaWxlDQpjYXQoJ1xuRGF0YSBGcmFtZSBkaW1lbnNpb25zOiAnLGRpbShteWRhdGEpKQ0KDQpteWRhdGEgPSBzdWJzZXQobXlkYXRhLCBzZWxlY3QgPSAtYyhNQUxFKSk7ICAjIHJlbW92ZWQgSUQgY29sdW1uDQpjYXQoJ1xuQWZ0ZXIgY2hvb3NpbmcgdmFyaWFibGVzIGZvciBvdXIgc3R1ZHksIHRoZSBkYXRhIEZyYW1lIGRpbWVuc2lvbnM6ICcsZGltKG15ZGF0YSkpDQpzdW1tYXJ5KG15ZGF0YSkNCg0KIyBUaGUgY29ycmVsYXRpb24gY29lZmZpY2llbnRzOg0KbGlicmFyeShIbWlzYykNCnJjb3JyKGFzLm1hdHJpeCggbXlkYXRhICApKQ0KbGlicmFyeShjb3JyZ3JhbSkNCmNvcnJncmFtKG15ZGF0YSwgb3JkZXI9VFJVRSwgbG93ZXIucGFuZWw9cGFuZWwuc2hhZGUsDQogICAgICAgICB1cHBlci5wYW5lbD1wYW5lbC5waWUsIHRleHQucGFuZWw9cGFuZWwudHh0KQ0KbGlicmFyeShHR2FsbHkpDQpnZ3BhaXJzKHN1YnNldChteWRhdGEsc2VsZWN0PWMoQUdFLEhULFdULFdBSVNULEJNSSkpKSANCg0KIyBMaW5lYXIgbW9kZWwNCm15bW9kZWwgPSBsbShXVH5BR0UrSFQrV0FJU1QrQk1JK1BVTFNFLCBkYXRhPW15ZGF0YSkNCnN1bW1hcnkobXltb2RlbCkNCmNvbmZpbnQobXltb2RlbCkNCg0KbGlicmFyeShjYXIpDQp2aWYobXltb2RlbCkNCg0KcGFyKG1mcm93PWMoMiwyKSkNCnBsb3QobXltb2RlbCkNCnBhcihtZnJvdz1jKDEsMSkpDQpgYGANCg0KRmlyc3QgcmVtb3ZlIEJNSToNCg0KYGBge3J9DQpteW1vZGVsMiA9IHVwZGF0ZShteW1vZGVsLCAuIH4gLiAtIEJNSSkNCnN1bW1vZGVsMiA9IHN1bW1hcnkobXltb2RlbDIpOyBzdW1tb2RlbDINCnZpZihteW1vZGVsMikNCmBgYA0KDQpUaGUgUFVMU0UgaXMgc3RpbGwgaW5zaWduaWZpY2FudCwgc28gd2UgcmVtb3ZlIGl0IGFzIHdlbGwgYW5kIG9idGFpbiB0aGUNCm1vZGVsIHdoZXJlIGFsbCB0aGUgcC12YWx1ZXMgXDwgMC4wNSwgaS5lLiBhbGwgdmFyaWFibGVzIGFyZQ0Kc2lnbmlmaWNhbnQuDQoNCmBgYHtyfQ0KbXltb2RlbDMgPSB1cGRhdGUobXltb2RlbDIsIC4gfiAuIC1QVUxTRSkNCnN1bW1vZGVsMyA9IHN1bW1hcnkobXltb2RlbDMpOyBzdW1tb2RlbDMNCkNJYjEgPSBjb25maW50KG15bW9kZWwzKTsgQ0liMQ0KdmlmKG15bW9kZWwzKQ0KYGBgDQoNCk5vdywgd2hlbiB3ZSBvYnRhaW5lZCBhIG1vZGVsIHdpdGggYWxsIHNpZ25pZmljYW50IHByZWRpY3RvcnMgYW5kIGFsbA0KVklGJ3Mgd2VsbCBiZWxvdyA1LCB3ZSByZXJ1biBkaWFnbm9zdGljIHBsb3RzIGFuZCBzZWUgdGhhdCBub24tbGluZWFyaXR5DQpvZiB0aGUgcmVzaWR1YWxzIGhhcyBiZWVuIHJlbW92ZWQsIG5vcm1hbGl0eSBvZiB0aGUgcmVzaWR1YWxzIGltcHJvdmVkDQphbmQgbm9uZSBvZiB0aGUgcG9pbnRzIG9uIHJlc2lkdWFscyB2cy4gbGV2ZXJhZ2UgZ3JhcGggY3Jvc3NlcyBvdmVyIHRoZQ0KQ29vaydzIGxldmVsIGN1cnZlcy4gVGh1cywgdGhlIGxhdGVzdCBsaW5lYXIgbW9kZWwgaXMgcmVhc29uYWJsZS4NCg0KYGBge3J9DQpwYXIobWZyb3c9YygyLDIpKQ0KcGxvdChteW1vZGVsMykNCnBhcihtZnJvdz1jKDEsMSkpDQpgYGANCg0KVXNpbmcgdGhpcyBtb2RlbCwgd2UgY2FuIGlsbHVzdHJhdGUgdGhlIG1lYW5pbmcgb2YgdGhlIGNvZWZmaWNpZW50cw0KKHNsb3BlcykuXA0KSWYgYWxsIG90aGVyIHZhcmlhYmxlcyBhcmUga2VwdCBjb25zdGFudCwgZWFjaCBleHRyYSB5ZWFyIG9mIGFnZQ0Kc29tZXdoYXQgc3VycHJpc2luZ2x5IGxvd2VycyB3ZWlnaHQgYnkgMC4zNiBsYiBvbiBhdmVyYWdlLiBFYWNoIGV4dHJhDQppbmNoIG9mIGhlaWdodCBvbiBhdmVyYWdlIGluY3JlYXNlIHdlaWdodCBieSAyLjk3IGxiLiBGaW5hbGx5IGVhY2ggZXh0cmENCmluY2ggb2YgV0FJU1QgaW5jcmVhc2VzIFdUIGJ5IDIuMzQgbGIgb24gYXZlcmFnZS4NCg0KV2UgY2FuIGFsc28gdXNlIHRoaXMgcmVkdWNlZCBtb2RlbCB0byBwcmVkaWN0IFdUIGJlbG93Og0KDQpgYGB7cn0NCm5ld2RhdGEgPSBkYXRhLmZyYW1lKEFHRSA9IDQwLEhUID0gNjksV0FJU1QgPSA5MCkNCnByZWRpY3QobXltb2RlbDMsIG5ld2RhdGEsIGludGVydmFsID0gInByZWRpY3Rpb24iKQ0KcHJlZGljdChteW1vZGVsMywgbmV3ZGF0YSwgaW50ZXJ2YWwgPSAiY29uZmlkZW5jZSIpDQpgYGANCg0KIyMgUG9seW5vbWlhbCBUcmFuZm9ybWF0aW9uDQoNClNvbWV0aW1lcywgd2UgY2FuIG9ic2VydmUgY2xlYXIgY3VydmF0dXJlIGluIGEgc2NhdHRlciBwbG90IGFuZCBsaW5lYXINCm1vZGVsIGlzIGluYWRlcXVhdGUgZm9yIHRoZSBkYXRhLiBUaGlzIGNhbiBvZnRlbiBiZSBmaXhlZCBieSBpbmNsdWRpbmcNCnF1YWRyYXRpYyBvciBjdWJpYyB0ZXJtcyBpbiB0aGUgbW9kZWwuIEZvciBleGFtcGxlLCBhIGN1YmljIHBvbHlub21pYWwNCnJlbGF0aW9uc2hpcCBpcyByZXByZXNlbnRlZCBieQ0KJCQgXGhhdHt5fSA9IGJfMCtiXzFcY2RvdCB4K2JfMlxjZG90IHheMitiXzNcY2RvdCB4XjMgJCQgQXQgb3JkZXIgMSwgdGhlDQpsaW5lYXIgcmVsYXRpb25zaGlwIGFsbG93cyBubyBjdXJ2YXR1cmUgKHN0cmFpZ2h0IGxpbmUpLiBBdCBvcmRlciAyLCBhDQpxdWFkcmF0aWMgZnVuY3Rpb24gKHBhcmFib2xhKSBhbGxvd3Mgb25lIGJlbmQuIEF0IG9yZGVyIDMsIHRoZSBtb2RlbCBjYW4NCmNvcGUgd2l0aCB0d28gYmVuZHMgaW4gdGhlIHJlbGF0aW9uc2hpcCBldGMuLi4gQWx0aG91Z2ggbXVjaCBoaWdoZXINCm9yZGVyIHBvbHlub21pYWxzIGludHJvZHVjZSBhcnRpZmljaWFsIHdpZ2dsZXMgYW5kIGFyZSB0byBiZSBhdm9pZGVkLiBJdA0KaXMgdmVyeSBpbXBvcnRhbnQgdG8gcmVhbGl6ZSB0aGF0IHdoaWxlICoqbm9uLWxpbmVhciBpbiB4LCB0aGUgbW9kZWwgaXMNCnN0aWxsIGxpbmVhciBpbiB0aGUgY29lZmZpZW50cyoqICRiXzAsIGJfMS4uLiQuIEEgdHJ1bHkgbm9uLWxpbmVhciBtb2RlbA0KaW4gY29lZmZpY2llbnRzIHJlcXVpcmVzIGRpZmZlcmVudCBhcHByb2FjaC5cDQpHZW5lcmFsbHksIGEgbnVtZXJpYyB0cmFuc2Zvcm1hdGlvbiByZWZlcnMgdG8gdGhlIGFwcGxpY2F0aW9uIG9mIGENCm1hdGhlbWF0aWNhbCBmdW5jdGlvbiB0byBudW1lcmljIG9ic2VydmF0aW9ucyB0byByZXNjYWxlIHRoZW0gYW5kIGlzIG5vdA0KbGltaXRlZCB0byBwb2x5bm9taWFscywgaXQgY291bGQgYmUgYW55IGZ1bmN0aW9uIG9mICR4X2kkJ3MuDQoNCioqRXhhbXBsZSoqDQoNCkNvbnNpZGVyIHRoZSBidWlsdC1pbiBtdGNhcnMgZGF0YSBzZXQuIExldCdzIGludmVzdGlnYXRlIHRoZSBkZXBlbmRlbmNlDQpvZiBtcGcgKG1pbGVzIHBlciBnYWxsb24pIHZzLiBocCAoaG9yc2Vwb3dlcikgb2YgYSBjYXIuIFRoZSBzY2F0dGVyIHBsb3QNCnNob3duIGluIHRoZSBGaWd1cmUgYmVsb3cgaGFzIGEgbm90aWNlYWJsZSBjdXJ2ZSBpbiB0aGUgcmVsYXRpb25zaGlwLCBzbw0KdGhlIHJlZ3Jlc3Npb24gbGluZSBzaG93biBpbiB0aGlzIGZpZ3VyZSBhcHBlYXJzIHRvIGJlIGEgcG9vciBmaXQuIFRoZQ0KZGlhZ25vc3RpYyBwbG90cyBhbHNvIHNob3cgcHJvYmxlbXMgd2l0aCB0aGUgcGF0dGVybiBpbiByZXNpZHVhbHMuIE5vdGUNCnRoYXQgd2UgZG9uJ3QgbmVlZCB0byBjaGVjayBWSUYncyAtIHRoZXkgYXJlIGdvaW5nIHRvIGJlIGhpZ2ggYmVjYXVzZQ0KdGhlc2UgYXJlIHRoZSBwb3dlcnMgb2YgdGhlIHNhbWUgdmFyaWFibGUuDQoNCmBgYHtyIFBvbHlub21SZWdyTGluLCBmaWcuY2FwPSdQb2x5bm9taWFsIEZpdCAtIExpbmVhciBtb2RlbCd9DQojIFBvbHlub21pYWwgZml0IG10Y2FycyANCnBsb3QobXBnIH4gaHAsIGRhdGE9bXRjYXJzLCBwY2ggPSAyMCkNCm15bW9kZWwxID0gbG0obXBnIH4gaHAsIGRhdGE9bXRjYXJzKQ0Kc3VtbWFyeShteW1vZGVsMSkNCmFibGluZShteW1vZGVsMSxsd2Q9Myxjb2w9InJlZCIpDQpwYXIobWZyb3c9YygyLDIpKQ0KcGxvdChteW1vZGVsMSkNCnBhcihtZnJvdz1jKDEsMSkpDQpgYGANCg0KTm90ZSB0aGF0IGV2ZW4gdGhvdWdoIHRoZSBzY2F0dGVycGxvdCBhbmQgcmVzaWR1YWwgcGxvdCBib3RoIHNob3cgYQ0KcmF0aGVyIHBvb3IgZml0LCB0aGUgcC12YWx1ZSBvZiB0aGUgc2xvcGUgaXMgc3RpbGwgY2xvc2UgdG8gMCB3aGljaA0KaW5kaWNhdGVzIHN0YXRpc3RpY2FsIGV2aWRlbmNlIG9mIGEgbmVnYXRpdmUgbGluZWFyIGltcGFjdCBvZiBoaWdoZXINCmhvcnNlcG93ZXIgb2YgY2FycyBvbiBtaWxlYWdlLiBUaGVyZWZvcmUsIGJhc2VkIHB1cmVseSBvbiB0aGUgcmVncmVzc2lvbg0Kb3V0cHV0LCB3ZSB3b3VsZCBoYXZlIHRvIHJlamVjdCB0aGUgSDAgYXNzdW1wdGlvbiBvZiBubyBsaW5lYXINCnJlbGF0aW9uc2hpcC4gVGh1cywgYXMgd2Ugc2FpZCBiZWZvcmUsIHlvdSBzaG91bGQgYWx3YXlzIGV4YW1pbmUgdGhlDQpzY2F0dGVycGxvdC4NCg0KQWRkIGEgcXVhZHJhdGljIHRlcm0gaW4gaHAgdmlhIEkoaHBcXjIpLiBOb3RlIHRoYXQgUiByZXF1aXJlcyBJKCkNCmZ1bmN0aW9uIHdoaWNoIGNoYW5nZXMgdGhlIGNsYXNzIG9mIGFuIG9iamVjdCB0byBpbmRpY2F0ZSB0aGF0IGl0IHNob3VsZA0KYmUgdHJlYXRlZCAnYXMgaXMnLiBGb3IgYSBmb3JtdWxhLCBpdCBpcyB1c2VkIHRvIGluaGliaXQgdGhlDQppbnRlcnByZXRhdGlvbiBvZiBvcGVyYXRvcnMgc3VjaCBhcyAiKyIsICItIiwgIlwqIiBhbmQgIlxeIiBhcyBmb3JtdWxhDQpvcGVyYXRvcnMsIHNvIHRoZXkgYXJlIHVzZWQgYXMgYXJpdGhtZXRpY2FsIG9wZXJhdG9ycy4NCg0KYGBge3IgUG9seW5vbVJlZ3JMaW4yLCBmaWcuY2FwPSdQb2x5bm9taWFsIEZpdCAtIExpbmVhciBhbmQgUXVhZHJhdGljIG1vZGVscyd9DQpteW1vZGVsMiA9IGxtKG1wZyB+IGhwK0koaHBeMiksIGRhdGE9bXRjYXJzKQ0Kc3VtbWFyeShteW1vZGVsMikNCg0KeC5zZXEgPC0gc2VxKDUwLDM1MCxsZW5ndGg9MTAwMCkgICAjIHNlcXVlbmNlIG9mIHZhbHVlcyBpbiB0aGUgcmFuZ2Ugb2YgeCB2YXJpYWJsZQ0KbXltb2RlbDIucHJlZCA8LSBwcmVkaWN0KG15bW9kZWwyLG5ld2RhdGE9ZGF0YS5mcmFtZShocD14LnNlcSkpDQpwbG90KG1wZyB+IGhwLCBkYXRhPW10Y2FycywgcGNoID0gMjApDQphYmxpbmUobXltb2RlbDEsbHdkPTMsY29sPSJyZWQiKQ0KbGluZXMoeC5zZXEsbXltb2RlbDIucHJlZCxsdHk9Mixjb2w9ImJsdWUiLGx3ZD0zKQ0KYGBgDQoNCnRoZSBGaWd1cmUgYWJvdmUgc2hvd3MgYm90aCBsaW5lYXIgYW5kIHF1YWRyYXRpYyBmaXRzLiBUbyBhZGQgdGhlDQpxdWFkcmF0aWMgcHJlZGljdGlvbiB3ZSB1c2VkIHNlcSgpIGZ1bmN0aW9uIHRvIGNyZWF0ZSBhIGZpbmVseSBzcGFjZWQNCnNldCBvZiBvYnNlcnZhdGlvbnMgaW4gdGhlIGVudGlyZSByYW5nZSBvZiBocCdzLCBwcmVkaWN0KCkgZnVuY3Rpb24gdG8NCnByZWRpY3QgdGhlIG1wZydzIGF0IHRob3NlIHZhbHVlcywgYW5kIGxpbmVzKCkgdG8gYWRkIHRoZSBxdWFkcmF0aWMNCmxpbmUuIFRoZSBzdW1tYXJ5KG15bW9kZWwyKSBpbmRpY2F0ZXMgYSBzbWFsbCBwLXZhbHVlIChcPDAuMDUpIGZvciB0aGUNCnF1YWRyYXRpYyB0ZXJtLCBzbyB0aGUgY29udHJpYnV0aW9uIG9mIHRoZSBzcXVhcmVkIGNvbXBvbmVudCBpcw0Kc3RhdGlzdGljYWxseSBzaWduae+sgWNhbnQuIFRoaXMgY29uY2x1c2lvbiBpcyBhbHNvIHN1cHBvcnRlZCBieSBhIGhpZ2hlcg0KYWRqdXN0ZWQgJFJeMiQuDQoNCldvdWxkIHRoZSBjdWJpYyB0ZXJtIGZpdCB0aGUgZGF0YSBldmVuIGJldHRlcj8gTm8uIFRoZSBjb2RlIGJlbG93IHNob3dzDQp0aGF0IHRoZSBjdWJpYyB0ZXJtIGlzIG5vdCBzaWduaWZpY2FudCAocC12YWx1ZSBcPiAwLjA1KSBhbmQgdGhlIEZpZ3VyZQ0KYmVsb3cgZGlzcGxheXMgbGluZWFyLCBxdWFkcmF0aWMsIGFuZCBjdWJpYyBmaXRzLiBUaGUgY3ViaWMgZml0IGlzDQpiYXNpY2FsbHkgdGhlIHNhbWUgYXMgcXVhZHJhdGljLg0KDQpgYGB7ciBQb2x5bm9tUmVnckxpbjMsIGZpZy5jYXA9J1BvbHlub21pYWwgRml0IC0gTGluZWFyIHRocm91Z2ggQ3ViaWMgbW9kZWxzJ30NCm15bW9kZWwzID0gbG0obXBnIH4gaHArSShocF4yKStJKGhwXjMpLCBkYXRhPW10Y2FycykNCnN1bW1hcnkobXltb2RlbDMpDQoNCm15bW9kZWwzLnByZWQgPC0gcHJlZGljdChteW1vZGVsMyxuZXdkYXRhPWRhdGEuZnJhbWUoaHA9eC5zZXEpKQ0KcGxvdChtcGcgfiBocCwgZGF0YT1tdGNhcnMsIHBjaCA9IDIwKQ0KYWJsaW5lKG15bW9kZWwxLGx3ZD0zLGNvbD0icmVkIikNCmxpbmVzKHguc2VxLG15bW9kZWwyLnByZWQsbHR5PTIsY29sPSJibHVlIiAsbHdkPTMpDQpsaW5lcyh4LnNlcSxteW1vZGVsMy5wcmVkLGx0eT0zLGNvbD0iZ3JlZW4iLGx3ZD0zKQ0KbGVnZW5kKCJ0b3ByaWdodCIsbHR5PTE6MywNCiAgICAgICBsZWdlbmQ9Yygib3JkZXIgMSAobGluZWFyKSIsIm9yZGVyIDIgKHF1YWRyYXRpYykiLCJvcmRlciAzIChjdWJpYykiKSwNCiAgICAgICBjb2w9YygicmVkIiwiYmx1ZSIsImdyZWVuIiksbHdkPTMpDQpgYGANCg0KRnJvbSBSIGNvZGUgcG9pbnQgb2YgdmlldywgdGhlcmUgaXMgYWN0dWFsbHkgYSBiZXR0ZXIgd2F5IHRvIGZpdCBhbnkNCm9yZGVyIHBvbHlub21pYWwgdXNpbmcgcG9seSgpIGZ1bmN0aW9uIHdpdGggYXBwb3ByaWF0ZSBvcmRlci4gVGhlDQpyZXN1bHRzIGFyZSB0aGUgc2FtZS4NCg0KYGBge3J9DQpteW1vZGVsM2EgPSBsbShtcGcgfiBwb2x5KGhwLDMpLCBkYXRhPW10Y2FycykNCnN1bW1hcnkobXltb2RlbDNhKQ0KYGBgDQoNClRoZSByZWxhdGl2ZSBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UgZm9yIG5lc3RlZCBtb2RlbHMgdXNlZCBoZXJlIGNhbiBiZQ0KYWxzbyBhc3Nlc3NlZCB1c2luZyBhbm92YSgpIGZ1bmN0aW9uOg0KDQpgYGB7cn0NCmFub3ZhKG15bW9kZWwxLG15bW9kZWwyLG15bW9kZWwzKQ0KYGBgDQoNCkl0IHNob3dzIHRoYXQgcXVhZHJhdGljIHRlcm0gaXMgc2lnbmlmaWNhbnQsIGJ1dCBjdWJpYyBpcyBub3QsIHNpbWlsYXINCnRvIGluZGl2aWR1YWwgcC12YWx1ZXMuXA0KSXQgc2hvdWxkIGFsc28gYmUgbm90ZWQgdGhhdCByZXNpZHVhbHMgYW5kIGFzc3VtcHRpb25zIHBsb3QgZm9yIGxpbmVhcg0KbW9kZWwgc2hvd3MgcXVhZHJhdGljIHBhdHRlcm4gaW4gcmVzaWR1YWxzLCB3aGlsZSBxdWFkcmF0aWMgbW9kZWwgZml4ZXMNCnRoaXMgcHJvYmxlbSBhbmQgaGFzIHJhbmRvbSBkaXN0cmlidXRpb24gb2YgcmVzaWR1YWxzIGFuZCBiZXR0ZXINClJlc2lkdWFscyB2cy4gTGV2ZXJhZ2UgcGxvdCB3aXRoIG5vbmUgb2YgdGhlIGRhdGEgcG9pbnRzIGNyb3NzaW5nIENvb2sNCmRpc3RhbmNlIGxldmVsIGN1cnZlcy4NCg0KYGBge3J9DQpwYXIobWZyb3c9YygyLDIpKQ0KcGxvdChteW1vZGVsMSkNCnBsb3QobXltb2RlbDIpDQpwYXIobWZyb3c9YygxLDEpKQ0KYGBgDQoNCk5vdGUgdGhhdCBwb2x5bm9taWFsIGFuZCBtb3N0IG90aGVyIG5vbi1saW5lYXIgZml0cyBhcmUgb25seSB2YWxpZCBpbg0KdGhlIGRhdGEgcmVnaW9uIChpbnRlcnBvbGF0aW9uKS4gVGhleSBoYXZlIGNvbXBsZXRlbHkgZGlmZmVyZW50IGJlaGF2aW9yDQpvdXRzaWRlIG9mIHRoZSBkYXRhIHJhbmdlIChleHRyYXBvbGF0aW9uKS4gRm9yIGV4YW1wbGUsIHBsb3R0aW5nIG91cg0KbGluZWFyLCBxdWFkcmF0aWMsIGFuZCBjdWJpYyBhcHByb3hpbWF0aW9ucyBvbiBhIG11Y2ggd2lkZXIgcmFuZ2UsIHdlDQpvYnRhaW4gdGhlIEZpZ3VyZSBiZWxvdyB3aGVyZSB0aGUgcXVhZHJhdGljIGFuZCBjdWJpYyBleHRyYXBvbGF0aW9ucyBnbw0Kd2lsZGx5IG9mZiBjb3Vyc2UuDQoNCmBgYHtyIFBvbHlub21SZWdyRXh0cmEsIGZpZy5jYXA9J1BvbHlub21pYWwgRml0IC0gTGluZWFyIHRocm91Z2ggQ3ViaWMgbW9kZWxzJ30NCnguc2VxIDwtIHNlcSg1MCw1MDAsbGVuZ3RoPTEwMDApICAgIyBzZXF1ZW5jZSBvZiB2YWx1ZXMgaW4gdGhlIHJhbmdlIG9mIHggdmFyaWFibGUNCm15bW9kZWwyLnByZWQgPC0gcHJlZGljdChteW1vZGVsMixuZXdkYXRhPWRhdGEuZnJhbWUoaHA9eC5zZXEpKQ0KbXltb2RlbDMucHJlZCA8LSBwcmVkaWN0KG15bW9kZWwzLG5ld2RhdGE9ZGF0YS5mcmFtZShocD14LnNlcSkpDQpwbG90KG1wZyB+IGhwLCBkYXRhPW10Y2FycywgcGNoID0gMjAsIHhsaW09YygwLDUwMCkseWxpbT1jKDAsNDApKQ0KYWJsaW5lKG15bW9kZWwxLGx3ZD0zLGNvbD0icmVkIikNCmxpbmVzKHguc2VxLG15bW9kZWwyLnByZWQsbHR5PTIsY29sPSJibHVlIiAsbHdkPTMpDQpsaW5lcyh4LnNlcSxteW1vZGVsMy5wcmVkLGx0eT0zLGNvbD0iZ3JlZW4iLGx3ZD0zKQ0KbGVnZW5kKCJ0b3ByaWdodCIsbHR5PTE6MywNCiAgICAgICBsZWdlbmQ9Yygib3JkZXIgMSAobGluZWFyKSIsIm9yZGVyIDIgKHF1YWRyYXRpYykiLCJvcmRlciAzIChjdWJpYykiKSwNCiAgICAgICBjb2w9YygicmVkIiwiYmx1ZSIsImdyZWVuIiksbHdkPTMpDQpgYGANCg0KIyMgSW50ZXJhY3RpdmUgVGVybXMNCg0KU28gZmFyLCBmb3IgcmVncmVzc2lvbiwgd2UgbG9va2VkIG9ubHkgYXQgdGhlIGFkZGl0aXZlIG1haW4gZWZmZWN0cyBvZg0KcHJlZGljdG9ycyBvbiBvdXRjb21lLiBOb3csIHdlIGNvbnNpZGVyIGludGVyYWN0aW9ucyBiZXR3ZWVuIHByZWRpY3RvcnMuDQpBbiBpbnRlcmFjdGlvbiBlZmZlY3Qgb2NjdXJzIHdoZW4gdGhlIGVmZmVjdCBvZiBvbmUgdmFyaWFibGUgZGVwZW5kcyBvbg0KdGhlIHZhbHVlIG9mIGFub3RoZXIgdmFyaWFibGUuIEZvciBleGFtcGxlLCBtZWRpY2luZSBpbnRlcmFjdGlvbiBlZmZlY3RzDQphcmUgdmVyeSBjb21tb24uIFNheSwgc3RhdGlucyB1c2VkIGZvciBjaG9sZXN0ZXJvbCByZWRhY3Rpb24gbmVnYXRpdmVseQ0KaW50ZXJhY3Qgd2l0aCB0aGUgZ3JhcGVmcnVpdCBqdWljZSBiZWNhdXNlIGl0IGNvbnRhaW5zIGNoZW1pY2FsDQpjb21wb3VuZHMgdGhhdCBpbmhpYml0IHRoZSBkcnVnIGVmZmVjdHMuIEludGVyYWN0aW9ucyBjYW4gb2NjdXIgYmV0d2Vlbg0KY2F0ZWdvcmljYWwgdmFyaWFibGVzLCBudW1lcmljIHZhcmlhYmxlcywgb3IgYm90aC4gV2UgY29tbW9ubHkgc3R1ZHkNCmludGVyYWN0aW9uIGJldHdlZW4gdHdvIHZhcmlhYmxlcyAodHdvLXdheSksIGhpZ2hlciBvcmRlciBpbnRlcmFjdGlvbnMNCmFyZSByYXJlbHkgY29uc2lkZXJlZCBhcyB0aGV5IGFyZSBoYXJkIHRvIGludGVycHJldC4gTm90ZSBhbHNvIHRoYXQgd2UNCmRvbid0IG5lZWQgdG8gY2hlY2sgZm9yIG11bHRpY29sbGluZWFyaXR5IHdpdGggVklGJ3MgLSB0aGV5IGFyZSBnb2luZyB0bw0KYmUgaGlnaCBiZWNhdXNlIGl0IG9ubHkgbWFrZXMgc2Vuc2UgdG8gY29uc2lkZXIgZXhhbXBsZXMgd2l0aCBzdHJvbmcNCmludGVyYWN0aW9ucyBhbmQgdGhlc2UgaGF2ZSBhIGxvdCBvZiBjb3JyZWxhdGlvbnMuIEluY2x1ZGluZyB0aGUNCmludGVyYWN0aW9uIHRlcm0gZXhwbGljaXRseSBhY2NvdW50cyBmb3IgaXQuDQoNCkxldCdzIGZpcnN0IGNvbnNpZGVyIGludGVyYWN0aW9uIGJldHdlZW4gKipvbmUgY2F0ZWdvcmljYWwgYW5kIG9uZQ0KY29udGludW91cyoqIHZhcmlhYmxlcy4gVGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGNoYW5nZXMgc2xvcGUgb2YgdGhlDQpjb250aW51b3VzIHByZWRpY3RvciB3aXRoIHJlc3BlY3QgdG8gaXRzIG5vbi1yZWZlcmVuY2UgbGV2ZWxzLiBBDQpjYXRlZ29yaWNhbCB2YXJpYWJsZSB3aXRoICRrJCBsZXZlbHMgaGFzICRrIC0gMSQgbWFpbiBlZmZlY3QgdGVybXMsIHNvDQp0aGVyZSB3aWxsIGJlIGFsc28gJGsgLSAxJCBpbnRlcmFjdGl2ZSB0ZXJtcyB3aXRoIGNvbnRpbnVvdXMgdmFyaWFibGUuDQoNCkNvbnNpZGVyIGFuIGV4YW1wbGUgb2YgY2xhc3NpY2FsIGlyaXMgZGF0YSBmaWxlIHdoaWNoIGNvbnRhaW5zIDMgY2xhc3Nlcw0Kb2YgNTAgaW5zdGFuY2VzIGVhY2gsIHdoZXJlIGVhY2ggY2xhc3MgcmVmZXJzIHRvIGEgdHlwZSBvZiBpcmlzIHBsYW50DQphbmQgaXRzIG1lYXN1cmVtZW50cy4gSnVzdCBsaWtlIGZvciB0aGUgaW50ZXJhY3Rpb24gb2YgdGVybXMgaW4NClR3by1XYXktQU5PVkEsIHdlIHVzZSBtdWx0aXBsaWNhdGlvbiBzaWduICQqJCB0byBpbnRyb2R1Y2UgaW50ZXJhY3Rpb24NCmJldHdlZW4gdGhlIHRlcm1zIHdoaWNoIGF1dG9tYXRpY2FsbHkgaW5jbHVkZXMgdGhlIG1haW4gZWZmZWN0cyBvZiB0aGUNCnRlcm1zLiBnZ3Bsb3QyIHBhY2thZ2UgaXMgdXNlZCB0byBwcm9kdWNlIGFuIGluZm9ybWF0aXZlIHBsb3Qgb2YNCnJlZ3Jlc3Npb24gbGluZXMgYnJva2VuIGJ5IHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBTcGVjaWVzIGFzIHNob3duIGluDQp0aGUgRmlndXJlIGJlbG93Lg0KDQpgYGB7ciBJbnRlcmFjdENvbnRDYXRlZywgZmlnLmNhcD0nSW50ZXJhY3Rpb24gQ29udGludW91cyB3aXRoIENhdGVnb3JpY2FsIFZhcmlhYmxlJ30NCiMgSW50ZXJhY3Rpb24gb25lIGNvbnRpbnVvdXMgb25lIGNhdGVnb3JpY2FsDQpteW1vZGVsID0gbG0oUGV0YWwuTGVuZ3RoIH4gUGV0YWwuV2lkdGggKyBTcGVjaWVzLCBkYXRhID0gaXJpcykNCnN1bW1hcnkobXltb2RlbCkNCg0KbGlicmFyeShnZ3Bsb3QyKQ0KZ2dwbG90KGlyaXMsIGFlcyh4ID0gUGV0YWwuV2lkdGgsIHkgPSBQZXRhbC5MZW5ndGgsIGNvbG9yID0gU3BlY2llcyxmaWxsPVNwZWNpZXMpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpDQpgYGANCg0KVGhlIG91dHB1dCBzaG93cyBtYWluIGVmZmVjdHMgZm9yIFBldGFsLldpZHRoIGFuZCBTcGVjaWVzIGFzIHdlbGwgYXMNCnRoZWlyIGludGVyYWN0aW9ucyBmb3IgZWFjaCBub24tcmVmZXJlbmNlIGxldmVscyBvZiB0aGUgU3BlY2llcy4gVGhlDQptYWluIGVmZmVjdCBmb3IgUGV0YWwuV2lkdGggaXMgbm90IHNpZ25pZmljYW50IChwLXZhbHVlIFw+IDAuMDUpLA0KU3BlY2llc1ZlcnNpY29sb3IgaXMgbm90IHNpZ25pZmljYW50IGNvbXBhcmVkIHRvIHRoZSByZWZlcmVuY2UgbGV2ZWwgb2YNClNldG9zYSAocC12YWx1ZSBcPiAwLjA1KSwgYnV0IFNwZWNpZXNWaXJnaW5pY2EgaXMgc2lnbmlmaWNhbnQgKHAtdmFsdWUNClw8XDwgMC4wNSkgY29tcGFyZWQgdG8gU2V0b3NhLiBOb3RlIHRoYXQgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZQ0Kbm9uLXJlZmVyZW5jZSBsZXZlbHMgb2YgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBpcyBzaWduae+sgWNhbnQsIHRoZSBlbnRpcmUNCmVmZmVjdCBpcyBzaWduaWZpY2FudC4gVGhlIGludGVyYWN0aW9uIHRlcm0NClBldGFsLldpZHRoOlNwZWNpZXN2ZXJzaWNvbG9yIGlzIHNpZ25pZmljYW50IChwLXZhbHVlIFw8IDAuMDUpLiBOb3RlDQp0aGF0IGV2ZW4gdGhvdWdoIHRoZSBtYWluIGVmZmVjdCBvZiBQZXRhbC5XaWR0aCBpcyBub3Qgc2lnbmlmaWNhbnQsIHdlDQpjYW5ub3QgZHJvcCB0aGlzIHZhcmlhYmxlIGJlY2F1c2UgdGhlcmUgaXMgc2lnbmlmaWNhbnQgaW50ZXJhY3Rpb24gd2l0aA0KU3BlY2llcy4gVGhlIEZpZ3VyZSBhYm92ZSBzaG93cyB0aGF0IHNsb3BlcyBkaWZmZXIgYnkgU3BlY2llcyB3aGljaA0KY29uZmlybXMgdGhlIHNpZ25pZmljYW5jZSBvZiB0aGUgaW50ZXJhY3Rpb24uIFRoZSByZXN1bHRpbmcgbW9kZWwgaXM6DQokJFBldGFsLkxlbmd0aCA9MS4zMyswLjU0IFxjZG90IFBldGFsLldpZHRoICsgJCQNCiQkIDAuNDUgXGNkb3QgU3BlY2llc3ZlcnNpY29sb3IgKyAyLjkxIFxjZG90IFNwZWNpZXN2aXJnaW5pY2EgKyAkJA0KJCQxLjMzIFxjZG90IFBldGFsLldpZHRoOlNwZWNpZXN2ZXJzaWNvbG9yICswLjEgXGNkb3QgUGV0YWwuV2lkdGg6U3BlY2llc3ZpcmdpbmljYSQkDQpGb3IgdGhlIHJlZmVyZW5jZSBsZXZlbCBvZiB0aGUgY2F0ZWdvcmljYWwgcHJlZGljdG9yIFNwZWNpZXMgLSBTZXRvc2EsDQp0aGUgZml0dGVkIG1vZGVsIGlzIHJlYWQgZnJvbSB0aGUgb3V0cHV0Og0KJCRQZXRhbC5MZW5ndGggPTEuMzMrMC41NCBcY2RvdCBQZXRhbC5XaWR0aCQkIA0KRmlyc3QgY29uc2lkZXIgdGhlIGVmZmVjdHMgd2l0aG91dCBpbnRlcmFjdGlvbnMuICANCkNoYW5naW5nIFNwZWNpZXMgdG8gdGhlIGxldmVsICRTcGVjaWVzVmVyc2ljb2xvciA9IDEkIChhbmQgb3RoZXJzIDApIGFkZHMgYSB0ZXJtOg0KJCRQZXRhbC5MZW5ndGggPTEuMzMrMC41NCBcY2RvdCBQZXRhbC5XaWR0aCArIDAuNDVcY2RvdCBTcGVjaWVzVmVyc2ljb2xvciAgPSAkJA0KJCQxLjMzKzAuNTQgXGNkb3QgUGV0YWwuV2lkdGggKyAwLjQ1IFxjZG90IDEgPSAxLjg3KzAuNTQgXGNkb3QgUGV0YWwuV2lkdGggJCQNCkJ1dCBub3RlIHRoYXQgaXQgb25seSBjaGFuZ2VzIHRoZSB5LWludGVyY2VwdCwgbm90IHRoZSBzbG9wZS5cDQpBbmFsb2dvdXNseSBmb3IgdGhlIGxldmVsICRTcGVjaWVzdmlyZ2luaWNhID0gMSQgYW5kIG90aGVycyAwOg0KJCRQZXRhbC5MZW5ndGggPTEuMzMrMC41NCBcY2RvdCBQZXRhbC5XaWR0aCArIDIuOTEgXGNkb3QgU3BlY2llc3ZpcmdpbmljYSA9JCQNCiQkMS4zMyswLjU0IFxjZG90IFBldGFsLldpZHRoICsgMi45MSBcY2RvdCAxID0gNC4yNCswLjU0IFxjZG90IFBldGFsLldpZHRoICQkDQpOb3csIGxldCdzIGNvbnNpZGVyIHRoZSBpbnRlcmFjdGlvbiB0ZXJtcy4gVGhleSBub3Qgb25seSBhZGQgdG8NCmludGVyY2VwdCwgYnV0IGFsc28gdG8gdGhlIHNsb3Blcy4gRm9yIHRoZSBwbGFudCB3aXRoDQokU3BlY2llc3ZlcnNpY29sb3IgPSAxJCwNCiQkUGV0YWwuTGVuZ3RoID0xLjMzKzAuNTQgXGNkb3QgUGV0YWwuV2lkdGggKyAkJA0KJCQgMC40NSBcY2RvdCBTcGVjaWVzdmVyc2ljb2xvciArICAxLjMzIFxjZG90IFBldGFsLldpZHRoOlNwZWNpZXN2ZXJzaWNvbG9yID0gJCQNCiQkIDEuMzMrMC41NCBcY2RvdCBQZXRhbC5XaWR0aCArIDAuNDUgXGNkb3QgMSArICAgMS4zMyBcY2RvdCBQZXRhbC5XaWR0aCBcY2RvdCAxID0gJCQNCiQkIDEuODgrMS44NyBcY2RvdCBQZXRhbC5XaWR0aCAkJCBUaHVzLCB3ZSBvYnRhaW5lZCBoaWdoZXIgeS1pbnRlcmNlcHQNCmFuZCBoaWdoZXIgc2xvcGUgb2YgJFBldGFsLldpZHRoJC5cDQpUaGUgcHJlZGljdCgpIGZ1bmN0aW9uIHdpdGggaW50ZXJhY3Rpb24gd29ya3MgZXhhY3RseSB0aGUgc2FtZToNCg0KYGBge3J9DQpuZXdkYXRhID0gZGF0YS5mcmFtZShQZXRhbC5XaWR0aD0yLFNwZWNpZXM9InZpcmdpbmljYSIpDQpwcmVkaWN0KG15bW9kZWwsIG5ld2RhdGEsIGludGVydmFsID0gInByZWRpY3Rpb24iKQ0KcHJlZGljdChteW1vZGVsLCBuZXdkYXRhLCBpbnRlcnZhbCA9ICJjb25maWRlbmNlIikNCmBgYA0KDQpOZXh0LCBsZXQncyBjb25zaWRlciB0aGUgaW50ZXJhY3Rpb24gYmV0d2VlbiAqKnR3byBjb250aW51b3VzDQp2YXJpYWJsZXMqKi5cDQpUd28gY29udGludW91cyBtYWluIGVmZmVjdHMgZml0IGEgcGxhbmUgYW5kIGFuIGludGVyYWN0aW9uIHRlcm0gbW9kaWZpZXMNCnRoZSBzbG9wZXMgaW4gYSBjb250aW51b3VzIHdheS4gV2UgdXNlIGV4YW1wbGUgZGF0YSBmcm9tIHN0YXRlLng3NyB0aGF0DQppcyBidWlsdCBpbnRvIFIgbW9kZWxpbmcgSW5jb21lIChhdmVyYWdlKSB2cy4gSWxsaXN0cmFjeSBhbmQgTXVyZGVyDQpyYXRlcyBpbmNsdWRpbmcgdGhlIGludGVyYWN0aW9uIHRlcm0uDQoNCmBgYHtyfQ0Kc3RhdGVzIDwtIGFzLmRhdGEuZnJhbWUoc3RhdGUueDc3KTsgDQpteW1vZGVsIDwtIGxtKEluY29tZSB+IElsbGl0ZXJhY3kgKiBNdXJkZXIsIGRhdGEgPSBzdGF0ZXMpDQpzdW1tYXJ5KG15bW9kZWwpDQpgYGANCg0KVGhlIG1haW4gZWZmZWN0IG9mIElsbGl0ZXJhY3kgcmF0ZSBpcyBub3Qgc2lnbmlmaWNhbnQgKHAtdmFsdWUgXD4gMC4wNSksDQpidXQgTXVyZGVyIHJhdGUgaXMgc2lnbmlmaWNhbnRseSByZWxhdGVkIHRvIEluY29tZSAocC12YWx1ZSBcPCAwLjA1KS4NClRoZSBpbnRlcmFjdGlvbiBvZiB0aGVzZSB0d28gcmF0ZXMgaXMgc2lnbmlmaWNhbnQgKHAtdmFsdWUgXDwgMC4wNSkuIFRoZQ0KbW9kZWwgaXMgd3JpdHRlbiBhczoNCiQkSW5jb21lID0gIDM4MjIuNjEgKyA2MTcuMzRcY2RvdCBJbGxpdGVyYWN5ICsgMTQ2LjgyIFxjZG90IE11cmRlciAtMTE3LjEwIFxjZG90IElsbGl0ZXJhY3k6TXVyZGVyICQkDQokJCBJbmNvbWUgPSAgMzgyMi42MSArIDYxNy4zNFxjZG90IElsbGl0ZXJhY3kgKyAxNDYuODIgXGNkb3QgTXVyZGVyIC0xMTcuMTAgXGNkb3QgSWxsaXRlcmFjeSBcY2RvdCBNdXJkZXIkJA0KTm90ZSBob3cgdGhlIDJuZCBsaW5lIHdyaXRlcyB0aGUgaW50ZXJhY3Rpb24gYXMgYWN0dWFsIHByb2R1Y3Qgb2YgdHdvDQpwcmVkaWN0b3IgdmFyaWFibGVzLiBOb3RlIHRoYXQgd2UgY291bGQgaGF2ZSB3cml0dGVuIHRoZSBpbnRlcmFjdGlvbiBvZg0KY29udGludW91cyBhbmQgY2F0ZWdvcmljYWwgdmFyaWFibGUgaW4gdGhlIHByZXZpb3VzIGV4YW1wbGUgYXMgYSBwcm9kdWN0DQphcyB3ZWxsLCB0aGUgZHVtbXkgY29kaW5nIG9yIDAgYW5kIDEgd291bGQgbXVsdGlwbHkgY29ycmVjdGx5ICgwID0gdGVybQ0KaXMgYWJzZW50LCAxID0gdGVybSBpcyBwcmVzZW50KS4NCg0KVGhlIGludGVycHJldGF0aW9uIG9mIHRoZSBjb250aW51b3VzIHZhcmlhYmxlcyBpbnRlcmFjdGlvbiBkZXBlbmRzIG9uDQp0aGUgc2lnbiAkXHBtJCBvZiB0aGUgaW50ZXJhY3Rpb24gY29lZmZpY2llbnQuIElmIGl0IGlzIG5lZ2F0aXZlIChhcyBpdA0KaXMgaGVyZSksIGFzIHByZWRpY3RvcnMgaW5jcmVhc2UsIHRoZSBtZWFuIHJlc3BvbnNlIGlzIHJlZHVjZWQgKGFmdGVyDQpjb21wdXRpbmcgdGhlIG1haW4gZWZmZWN0cykuIElmIGl0IGlzIHBvc2l0aXZlLCBhcyB0aGUgcHJlZGljdG9ycw0KaW5jcmVhc2UsIHRoZSBtZWFuIHJlc3BvbnNlIGlzIGFkZGl0aW9uYWxseSBhbXBsaWZpZWQuIEFzIGJvdGgNCmlsbGl0ZXJhY3kgYW5kIG11cmRlciByYXRlcyBpbmNyZWFzZSwgYXZlcmFnZSBpbmNvbWUgaXMgZGVjcmVhc2VkIGluIGENCnN0YXRlLiBJdCBpcyBpbnRlcmVzdGluZyB0byBjb21wYXJlIHRoZSBtb2RlbCB3aXRoIGludGVyYWN0aW9uIGFib3ZlDQp3aXRoIHRoZSBtb2RlbCB3aXRob3V0IGludGVyYWN0aW9uIGFuZCBpbmRpdmlkdWFsIG1vZGVsczoNCg0KYGBge3J9DQpteW1vZGVsMiA8LSBsbShJbmNvbWUgfiBJbGxpdGVyYWN5ICsgTXVyZGVyLCBkYXRhID0gc3RhdGVzKQ0Kc3VtbWFyeShteW1vZGVsMikNCg0KbXltb2RlbDMgPC0gbG0oSW5jb21lIH4gSWxsaXRlcmFjeSwgZGF0YSA9IHN0YXRlcykNCnN1bW1hcnkobXltb2RlbDMpDQoNCm15bW9kZWw0IDwtIGxtKEluY29tZSB+IE11cmRlciwgZGF0YSA9IHN0YXRlcykNCnN1bW1hcnkobXltb2RlbDQpDQoNCm5ld2RhdGEgPSBkYXRhLmZyYW1lKElsbGl0ZXJhY3k9MC42LE11cmRlcj00KQ0KcHJlZGljdChteW1vZGVsLCBuZXdkYXRhLCBpbnRlcnZhbCA9ICJwcmVkaWN0aW9uIikNCnByZWRpY3QobXltb2RlbCwgbmV3ZGF0YSwgaW50ZXJ2YWwgPSAiY29uZmlkZW5jZSIpDQpgYGANCg0KV2UgY2FuIHNlZSB0aGF0IHdpdGhvdXQgaW50ZXJhY3Rpb24gdGhlIGlsbGl0ZXJhY3kgZWZmZWN0IGlzIHJldmVyc2VkDQphbmQgYm90aCBpbmRpdmlkdWFsIG1vZGVscyBoYXZlIG5lZ2F0aXZlIGVmZmVjdHMgb2YgSWxsaXRlcmFjeSBhbmQNCk11cmRlciByZXNwZWN0aXZlbHkuDQoNCkZpbmFsbHksIGxldCdzIGNvbnNpZGVyICoqdHdvIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyoqIGludGVyYWN0aW9uLlwNCldlIGhhdmUgYWN0dWFsbHkgY29uc2lkZXJlZCB0aGUgaW50ZXJhY3Rpb25zIGJldHdlZW4gdHdvIGNhdGVnb3JpY2FsDQpleHBsYW5hdG9yeSB2YXJpYWJsZXMgaW4gdGhlIHR3by13YXkgQU5PVkEuIFRoZXJlZm9yZSwgdGhlIHNhbWUNClRvb3RoR3Jvd3RoIGZpbGUgZnJvbSBCYXNlIFIgZGF0YSBzZXRzIGlzIHVzZWQuIFRoaXMgZmlsZSBjb250YWlucyB0aGUNCmRhdGEgb24gdGhlIGVmZmVjdCBvZiB2aXRhbWluIEMgb24gdG9vdGggZ3Jvd3RoIGluIGd1aW5lYSBwaWdzLiBFYWNoDQphbmltYWwgcmVjZWl2ZWQgb25lIG9mIHRocmVlIGRvc2UgbGV2ZWxzIG9mIHZpdGFtaW4gQyAoMC41LCAxLCBhbmQgMg0KbWcvZGF5KSBieSBvbmUgb2YgdHdvIGRlbGl2ZXJ5IG1ldGhvZHMsIG9yYW5nZSBqdWljZSBvciBhc2NvcmJpYyBhY2lkDQooT0ogdnMuIFZDKS4NCg0KYGBge3IgSW50ZXJhY3RDYXRlZ0NhdGVnLCBmaWcuY2FwPSdJbnRlcmFjdGlvbiBDYXRlZ29yaWNhbCB3aXRoIENhdGVnb3JpY2FsIFZhcmlhYmxlJ30NCiMgSW50ZXJhY3Rpb24gdHdvIGNhdGVnb3JpY2FsIA0KbXlkYXRhID0gVG9vdGhHcm93dGgNCm15ZGF0YSRkb3NlID0gZmFjdG9yKG15ZGF0YSRkb3NlKQ0KbXltb2RlbCA9IGxtKGxlbiB+IHN1cHAqZG9zZSxkYXRhPW15ZGF0YSkNCnN1bW1hcnkobXltb2RlbCkNCg0KbGlicmFyeShnZ3B1YnIpOyANCmdnbGluZShteWRhdGEsIHggPSAiZG9zZSIsIHkgPSAibGVuIiwgY29sb3IgPSAic3VwcCIsDQogICAgICAgYWRkID0gYygibWVhbl9zZSIsICJkb3RwbG90IikpDQpgYGANCg0KYGBge3J9DQppbnRlcmFjdGlvbi5wbG90KHguZmFjdG9yID0gbXlkYXRhJGRvc2UsICN4LWF4aXMgdmFyaWFibGUNCiAgICAgICAgICAgICAgICAgdHJhY2UuZmFjdG9yID0gbXlkYXRhJHN1cHAsICN2YXJpYWJsZSBmb3IgbGluZXMNCiAgICAgICAgICAgICAgICAgcmVzcG9uc2UgPSBteWRhdGEkbGVuLCAjeS1heGlzIHZhcmlhYmxlDQogICAgICAgICAgICAgICAgIGZ1biA9IG1lZGlhbiwgI21ldHJpYyB0byBwbG90DQogICAgICAgICAgICAgICAgIHlsYWIgPSAidG9vdGggbGVuZ3RoIiwgeGxhYiA9ICJkb3NlIiwNCiAgICAgICAgICAgICAgICAgY29sID0gYygicmVkIiwgImJsdWUiKSwgdHJhY2UubGFiZWwgPSAic3VwcCIpDQpgYGANClRoZXJlIGlzIGEgdGVybSBmb3IgZWFjaCBub24tcmVmZXJlbmNlIGxldmVsIG9mIHRoZSBmaXJzdCBwcmVkaWN0b3INCmNvbWJpbmVkIHdpdGggYWxsIG5vbi1yZWZlcmVuY2UgbGV2ZWxzIG9mIHRoZSBzZWNvbmQgcHJlZGljdG9yLiBUaGUNCmRlbGl2ZXJ5IG1ldGhvZCBWQyBpcyBzaWduaWZpY2FudCAocC12YWx1ZSBcPCAwLjA1KSBjb21wYXJlZCB0byB0aGUNCnJlZmVyZW5jZSBsZXZlbCBPSi4gVGhlIGRvc2UxIGFuZCBkb3NlMiBhcmUgYm90aCBzaWduaWZpY2FudCBjb21wYXJlZCB0bw0KdGhlIHJlZmVyZW5jZSBkb3NlIG9mIDAuNSAocC12YWx1ZSBcPFw8IDAuMDUpLiBUaGUgaW50ZXJhY3Rpb24gb2YNCmRlbGl2ZXJ5IHN1cHBWQyB3aXRoIGRvc2UxIGlzIG5vdCBzaWduaWZpY2FudCAocC12YWx1ZSBcPiAwLjA1KSwgYnV0DQpzdXBwVkMgd2l0aCBkb3NlMiBpcyBzaWduaWZpY2FudCAocC12YWx1ZSBcPCAwLjA1KS5cDQpUaGVzZSByZXN1bHRzIHByb3ZpZGUgdGhlIHNhbWUgY29uY2x1c2lvbiBhcyB0aGUgQU5PVkEgYW5hbHlzaXMgc2hvd24NCmJlbG93Og0KDQpgYGB7cn0NCmFub3ZhbW9kID0gYW92KGxlbiB+IHN1cHAqZG9zZSxkYXRhPW15ZGF0YSkNCnN1bW1hcnkoYW5vdmFtb2QpDQpgYGANCg0KVGhlIG1vZGVsIGlzOg0KJCRsZW4gPSAxMy4yMy01LjI1IFxjZG90IHN1cHBWQyArIDkuNDdcY2RvdCBkb3NlMSArIDEyLjgzXGNkb3QgZG9zZTIgJCQNCiQkICAgIC0wLjY4IFxjZG90IHN1cHBWQyBcY2RvdCBkb3NlMSArIDUuMzMgXGNkb3Qgc3VwcFZDIFxjZG90IGRvc2UyJCQNClRoZSBtb2RlbCBpcyBzZWVuIGFzIGEgc2VyaWVzIG9mIGFkZGl0aXZlIHRlcm1zIHRvIHRoZSBvdmVyYWxsDQppbnRlcmNlcHQuIERlcGVuZGluZyBvbiBhIHBhcnRpY3VsYXIgY2hvaWNlIG9mIHRoZSBwcmVkaWN0b3JzLCB3ZSBnZXQNCmF2ZXJhZ2UgZXN0aW1hdGUuIEZvciBleGFtcGxlLCBmb3IgYSBwaWcgZ2l2ZW4gc3VwcGxlbWVudCB2aWEgYXNjb3JiaWMNCmFjaWQgKCRWQyQpIHdpdGggJGRvc2UgPSAyJCwgd2Ugb2J0YWluIHRoZSBmb2xsb3dpbmcgcHJlZGljdGlvbnM6DQoNCmBgYHtyfQ0KbmV3ZGF0YSA9IGRhdGEuZnJhbWUoc3VwcD0iVkMiLGRvc2U9IjIiKQ0KcHJlZGljdChteW1vZGVsLCBuZXdkYXRhLCBpbnRlcnZhbCA9ICJwcmVkaWN0aW9uIikNCnByZWRpY3QobXltb2RlbCwgbmV3ZGF0YSwgaW50ZXJ2YWwgPSAiY29uZmlkZW5jZSIpDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQojIyBNb2RlbCBzZWxlY3Rpb24NCg0KSW4gdGhlIHRoZSBiZWdpbm5pbmcgb2YgdGhpcyBtdWx0aXBsZSByZWdyZXNzaW9uIGNoYXB0ZXIsIHdlIGhhdmUgc3RhcnRlZCB3aXRoDQphIG51bWJlciBvZiBwcmVkaWN0b3JzIHdoaWNoIG1pZ2h0IGJlIHJlbGF0ZWQgdG8gdGhlIHJlc3BvbnNlIHZhcmlhYmxlIGFuZA0KcmVtb3ZlZCB0aGVtIG9uZSBieSBvbmUgdXNpbmcgdGhlIGhpZ2hlc3QgKGxlYXN0IHNpZ25pZmljYW50KSBwLXZhbHVlLiBLZWVwaW5nIHN1Y2ggaXJyZWxldmFudCB2YXJpYWJsZXMgd291bGQgb25seSBoaW5kZXIgdGhlIGFjY3VyYWN5IGFuZCBleHBsYW5hdG9yeSBwb3dlciBvZiB0aGUgbW9kZWwuIFRoZXJlIGFyZSBtYW55IG90aGVyIHNlbGVjdGlvbiBzdHJhdGVnaWVzIHdoaWNoIGhlbHAgdXMgdG8gZWxpbWluYXRlIChwcnVuZSBvdXQpIHVubmVjZXNzYXJ5IHByZWRpY3RvcnMgZnJvbSB0aGUgbW9kZWwgdG8gb2J0YWluIGEgYmV0dGVyIG1vZGVsLiAgQSBtb2RlbCB0aGF0IGluY2x1ZGVzIGFsbCBhdmFpbGFibGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzIGlzIGNhbGxlZCB0aGUgX19mdWxsIG1vZGVsX18uIA0KDQpUaGUgbWFpbiBnb2FsIG9mIGZpdHRpbmcgYSBzdGF0aXN0aWNhbCBtb2RlbCBpcyB0byBfZmFpdGhmdWxseSByZXByZXNlbnQNCnRoZSBkYXRhIGFuZCB0aGUgcmVsYXRpb25zaGlwcyBoZWxkIHdpdGhpbiB0aGVtXy4gSXQgaXMgIGEgX2JhbGFuY2luZyBhY3RfIGJldHdlZW4gX2dvb2RuZXNzLW9mLWZpdCBhbmQgY29tcGxleGl0eV8uIF9fQ29tcGxleGl0eV9fIGlzIHRpZWQgdG8gdGhlIG51bWJlciBvZiB0ZXJtcyBhbmQgYWRkaXRpb25hbCBmdW5jdGlvbnMgKHRyYW5zZm9ybWF0aW9ucywgaW50ZXJhY3Rpb25zLCBldGMuLi4pLiBUaGUgX19wcmluY2lwbGUgb2YgcGFyc2ltb255X18gaXMgdGhlIHByaW5jaXBsZSBvZiBtb2RlbCBzZWxlY3Rpb24gdG8gZmluZCBhIG1vZGVsIHRoYXTigJlzIGFzIHNpbXBsZSBhcyBwb3NzaWJsZSAocmVsYXRpdmVseSBsb3cgY29tcGxleGl0eSksIHdpdGhvdXQgIHNhY3JpZmljaW5nIGdvb2RuZXNzLW9mLWZpdCBpbiBhIG1ham9yIHdheS4gICBPZiBjb3Vyc2UsIGFzc2Vzc2luZyB0aGUgX3NpZ25pZmljYW5jZSBvZiB0aGUgZWZmZWN0IG9mIHByZWRpY3RvcnMgb3IgZnVuY3Rpb25zIG9mIHByZWRpY3RvcnNfIG9uIHRoZSByZXNwb25zZSBwbGF5cyB0aGUgbWFqb3Igcm9sZSBpbiB0aGVzZSBkZWNpc2lvbi4gVGhlIG51bWJlciBvZiBwb3NzaWJpbGl0aWVzIGZvciB2YXJpYWJsZSBzZWxlY3Rpb24gZ3Jvd3MgZXhwb25lbnRpYWxseSB3aXRoIHRoZSBudW1iZXIgb2YgdmFyaWFibGVzLCBzbyAgdGhlIHN5c3RlbWF0aWMgc2VsZWN0aW9uIGFsZ29yaXRobXMgYXJlIGVzc2VudGlhbCBpbiB0aGlzIGFuYWx5c2lzLiBUaGVyZSBhcmUgYSBudW1iZXIgb2YgbW9kZWwgc2VsZWN0aW9uIGFsZ29yaXRobXMgd2hpY2ggb2Z0ZW4gbGVhZCB0byBkaWZmZXJlbnQgZmluYWwgbW9kZWxzLiAgIEFzIEdlb3JnZSBCb3ggb25jZSBzYWlkIOKAnEFsbCBtb2RlbHMgYXJlIHdyb25nLCBidXQgc29tZSBhcmUgdXNlZnVsLuKAnSAgDQoNCkl0IHNob3VsZCBiZSBub3RlZCBhbHNvLCB0aGF0IHRoZSBkYXRhIG5lZWRzIHRvIGJlIHNwbGl0IGludG8gdHJhaW5pbmcgcGFydCBvbiB3aGljaCB0aGUgbW9kZWwgaXMgY3JlYXRlZCBhbmQgdGVzdGluZyBwYXJ0IG9uIHdoaWNoIHdlIHRlc3QgdGhlIG1vZGVsLiAgSWYgd2UgdGVzdCB0aGUgbW9kZWwgb24gdGhlIHNhbWUgZGF0YSBvbiB3aGljaCB0aGUgbW9kZWwgd2FzIGNyZWF0ZWQgdGhlIGZpdCB0ZW5kcyB0byBiZSB0b28gb3B0aW1pc3RpYyBhbmQgdGhlIG92ZXJmaXR0aW5nIHRvIHRoZSBwYXJ0aWN1bGFyIGRhdGEgc2V0IHRlbmRzIHRvIG9jY3VyIHdpdGggd29yc2UgcmVzdWx0cyBmb3IgdGhlIG5ldyB0ZXN0IGRhdGEuLi4NCg0KVGhlIF9fZ2VuZXJhbCBndWlkZWxpbmVzX18gb24gcHJlZGljdG9yIHZhcmlhYmxlcyBpbmNsdXNpb24vZXhjbHVzaW9uIGFyZTogICAgDQoxKSBJbmRpdmlkdWFsIGluc2lnbmlmaWNhbnQgbGV2ZWxzIG9mIGNhdGVnb3JpY2FsIHByZWRpY3RvciBjYW5ub3QgYmUgcmVtb3ZlZCBpZiBhdCBsZWFzdCBvbmUgb2YgdGhlIG5vbi1yZWZlcmVuY2UgbGV2ZWxzIGlzIHNpZ25pZmljYW50LiAgICAgICANCjIpIElmIGFuIGludGVyYWN0aW9uIGlzIHByZXNlbnQgaW4gdGhlIG1vZGVsLCBhbGwgbG93ZXItb3JkZXIgbWFpbiBlZmZlY3RzIG9mIHRoZSBpbnRlcmFjdGluZyBwcmVkaWN0b3JzIGNhbm5vdCBiZSByZW1vdmVkIGV2ZW4gaWYgdGhleSBhcmUgbm90IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuICAgIA0KMykgSW4gcG9seW5vbWlhbCB0cmFuc2Zvcm1hdGlvbiBtb2RlbHMsIGtlZXAgYWxsIGxvd2VyLW9yZGVyDQpwb2x5bm9taWFsIHRlcm1zIGluIHRoZSBtb2RlbCBpZiB0aGUgaGlnaGVzdCBpcyBkZWVtZWQgc2lnbmlmaWNhbnQuDQoNCiANCl9fUHJlZGljdGlvbiBBY2N1cmFjeV9fICANCkNvbnNpZGVyIHRoZSBleHBlY3RlZCBwcmVkaWN0aW9uIGVycm9yIGF0IHBvaW50IHg6DQokJCBFcnJvcih4KSA9IEUoIFkgLSBcaGF0e2Z9KHgpKV4yJCQNCkl0IGNhbiBiZSBkZWNvbXBvc2VkIGludG8gdGhyZWUgdGVybXM6DQokJCBFcnJvcih4KSA9IChFW1xoYXR7Zn0oeCldLWYoeCkpXjIgKyBFWyhcaGF0e2Z9KHgpLUVbXGhhdHtmfSh4KV0pXjJdICsgXHNpZ21hX3tcZXBzaWxvbn1eMiQkDQokJCBFcnJvcih4KSA9IFwgXCAgQmlhc14yICsgXCBcIFwgXCBcIFwgXCBcIFwgXCBWYXJpYW5jZSArIFwgXCBcIFwgXCBcIElycmVkdWNpYmxlIFwgRXJyb3IkJA0KDQpUaGUgIF9fYmlhc19fIGlzIHRoZSBlcnJvciBpbiBhcHByb3hpbWF0aW5nIGEgcmVhbC1saWZlIHByb2JsZW0gYnkgYSBtb2RlbC4gRm9yIGV4YW1wbGUsIGxpbmVhciByZWdyZXNzaW9uIGFzc3VtZXMgdGhhdCB0aGVyZSBpcyBhIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgcmVzcG9uc2UgYW5kIHByZWRpY3RvcnMuICBJZiB0aGUgdHJ1ZSAkZih4KSQgc3Vic3RhbnRpYWxseSBub24tbGluZWFyLCBpdCB3aWxsIG5vdCBiZSBwb3NzaWJsZSB0byBwcm9kdWNlIGFuIGFjY3VyYXRlIGVzdGltYXRlIHVzaW5nIGxpbmVhciByZWdyZXNzaW9uIC0gaGlnaCBiaWFzIChfX3VuZGVyZml0dGluZ19fKSAgLiAgR2VuZXJhbGx5LCBtb3JlIO+sgmV4aWJsZSBtZXRob2RzIHJlc3VsdCBpbiBsZXNzIGJpYXMuICAgIA0KDQpUaGUgX192YXJpYW5jZV9fIGlzIHRoZSBjaGFuZ2UgaW4gJFxoYXR7Zn0kIGlmIGl0IGlzIGVzdGltYXRlZCB3aXRoIGEgZGlmZmVyZW50IHRyYWluaW5nIGRhdGEgc2V0LiBNb3JlIO+sgmV4aWJsZSBzdGF0aXN0aWNhbCBtZXRob2RzIGhhdmUgaGlnaGVyIHZhcmlhbmNlLiBJbiBhIGZsZXhpYmxlIG5vbmxpbmVhciBmaXQsIGNoYW5naW5nIGFueSBkYXRhIHBvaW50ICBtYXkgY2F1c2UgdGhlIGVzdGltYXRlICRcaGF0e2Z9JCB0byBjaGFuZ2UgY29uc2lkZXJhYmx5IHJlc3VsdGluZyBpbiBoaWdoIHZhcmlhbmNlIChfX292ZXJmaXR0aW5nX18pLiAgQSBsZWFzdCBzcXVhcmVzIGxpbmUgZml0IGlzIHJlbGF0aXZlbHkgaW5mbGV4aWJsZSBhbmQgaGFzIGxvdyB2YXJpYW5jZSwgYmVjYXVzZSBtb3ZpbmcgYW55IHNpbmdsZSBvYnNlcnZhdGlvbiBzbGlnaHRseSB3aWxsIGxpa2VseSBjYXVzZSBvbmx5IGEgc21hbGwgc2hpZnQgaW4gdGhlIHBvc2l0aW9uIG9mIHRoZSBsaW5lLiAgDQoNClRoZSBpZGVhcyBvZiBCaWFzIGFuZCBWYXJpYW5jZSBhcmUgd2VsbCBpbGx1c3RyYXRlZCBpbiB0aGUgRmlndXJlIGJlbG93Og0KDQpgYGB7ciB9DQojIGluc3RhbGwucGFja2FnZXMoImltYWdlciIpDQpsaWJyYXJ5KGltYWdlcikNCmZpbGUgPSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2xlb25rYWcvU3RhdGlzdGljczAvbWFpbi9WYXJpYW5jZUJpYXNUYXJnZXQuSlBHIg0KaW0gPC0gbG9hZC5pbWFnZShmaWxlKQ0KcGxvdChpbSkNCmBgYA0KQXMgd2UgaW5jcmVhc2Ug76yCZXhpYmlsaXR5IG9mIHRoZSBtZXRob2RzLCB0aGUgdmFyaWFuY2Ugd2lsbCBpbmNyZWFzZSBhbmQgdGhlIGJpYXMgd2lsbCBkZWNyZWFzZS4gIEhvd2V2ZXIsIHRoZSBiaWFzIHRlbmRzIHRvIGluaXRpYWxseSBkZWNyZWFzZSBmYXN0ZXIgdGhhbiB0aGUgdmFyaWFuY2UgaW5jcmVhc2VzLiBDb25zZXF1ZW50bHksIHRoZSBleHBlY3RlZCB0ZXN0IE1lYW4gU3F1YXJlZCBFcnJvciAoJE1TRSQpIGRlY2xpbmVzLiBIb3dldmVyLCBhdCBzb21lIHBvaW50IGluY3JlYXNpbmcg76yCZXhpYmlsaXR5IHByb3ZpZGVzIG9ubHkgc21hbGwgaW1wcm92ZW1lbnRzIHRvIHRoZSBiaWFzIGJ1dCBzaWduae+sgWNhbnRseSBpbmNyZWFzZXMgdGhlIHZhcmlhbmNlLiBUaGVuIHRoZSB0ZXN0ICRNU0UkIGluY3JlYXNlcy4gIFdlIGNhbiBzZWUgdGhpcyBfX2JpYXMtdmFyaWFuY2UgdHJhZGUtb2ZmX18gaW4gdGhlIEZpZ3VyZSBiZWxvdy4gDQpgYGB7ciB9DQpsaWJyYXJ5KGltYWdlcikNCmZpbGUgPSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2xlb25rYWcvU3RhdGlzdGljczAvbWFpbi9CaWFzVmFyaWFuY2VUcmFkZW9mZi5KUEciDQppbSA8LSBsb2FkLmltYWdlKGZpbGUpDQpwbG90KGltKQ0KYGBgDQoNCg0KDQojIyMgTW9kZWwgU2VsZWN0aW9uIENyaXRlcmlhDQoNCldoaWNoIGNyaXRlcmlhIGNhbiBiZSB1c2VkIGluIHRoZSBzZWxlY3Rpb24gYWxnb3JpdGhtcz8gIFRoZSBsZWFzdCBzcXVhcmVzIG1vZGVsIGlzIGRlc2lnbmVkIHRvIG1pbmltaXplIHRoZSBSZXNpZHVhbCBTdW0gb2YgU3F1YXJlcyAgICAgd2hpY2ggd2UgZGVmaW5lZCBiZWZvcmUsIGJ1dCB3ZSByZXBlYXQgaXQgYWdhaW4gaGVyZToNCiQkUlNTPVNTRT1cc3VtX3tpPTF9Xm4gW3lfaSAtKGJfMCArIGJfMSAgeF97MSxpfSArYl8yICB4X3syLGl9KyAuLi4gKyBiX3AgIHhfe3AsaX0pXV4yJCQNCg0KR2VuZXJhbGx5LCB0aGUgdHJhaW5pbmcgc2V0IE1lYW4gU3F1YXJlZCBFcnJvciAoJE1TRSQpIGlzICBhbiB1bmRlcmVzdGltYXRlIG9mIHRoZSAkTVNFJCBmb3IgdGhlIHRlc3QgZGF0YSBzZXQuIFRoZSByZWFzb24gaXMgdGhhdCBhIG1vZGVsIGlzIGZpdCB0byB0aGUgdHJhaW5pbmcgZGF0YSB1c2luZyBsZWFzdCBzcXVhcmVzLCB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgYXJlIGNob3NlbiB0byBtaW5pbWl6ZSB0aGUgdHJhaW5pbmcgJFJTUyQuIFRoZSB0cmFpbmluZyAkUlNTJCBhbHdheXMgZGVjcmVhc2VzIGFzIHdlIGFkZCBtb3JlIGZlYXR1cmVzIHRvIHRoZSBtb2RlbCwgYnV0IHRoZSB0ZXN0IGVycm9yIG1heSBpbmNyZWFzZS4gVGhlcmVmb3JlIHRoZSB0cmFpbmluZyAkUlNTJCBhbmQgJHJeMiQgbWF5IG5vdCBiZSB1c2VkIGZvciBzZWxlY3RpbmcgdGhlIGJlc3QgbW9kZWwgd2l0aG91dCB0aGUgYWRqdXN0bWVudCBmb3IgdGhpcyB1bmRlcmVzdGltYXRpb24uDQoNCkFkZGluZyBuZXcgcHJlZGljdG9ycyAoZXZlbiBpcnJlbGV2YW50KSBjYW4gb25seSByZWR1Y2UgJFJTUyQgYW5kIGluY3JlYXNlIHJlZ3VsYXIgY29lZmZpY2llbnQgb2YgZGV0ZXJtaW5hdGlvbiwgc28gd2UgZGVmaW5lZCBhbiBfX2FkanVzdGVkX18gJHJeMl97YWRqfSQ6DQokJHJeMl97YWRqfSAgPSAxLVxmcmFje1xmcmFje1NTRX17bi1wLTF9fXtcZnJhY3tTU1R9e24tMX19ICQkDQpIb3dldmVyLCBpdCB0ZW5kcyB0byB2YXJ5IG9ubHkgc2xpZ2h0bHkgd2hlbiB3ZSByZWR1Y2Ugb3VyIG1vZGVscy4gIFRoZXJlIGFyZSBtYW55IG90aGVyLCBtb3JlIGVmZmljaWVudCBtZWFzdXJlcywgdXNlZCBmb3IgbW9kZWwgc2VsZWN0aW9uLg0KDQoNCg0KRm9yIGV4YW1wbGUsIE1hbGxvd3PigJlzIENwDQokJENfcD1cZnJhY3sxfXtufShSU1MgKzJwXGhhdHtcc2lnbWF9XjIpICQkDQp3aGVyZSAkUlNTJCBpcyB0aGUgcmVzaWR1YWwgc3VtIG9mIHNxdWFyZXMgb24gYSB0cmFpbmluZyBzZXQgb2YgZGF0YSwgJHAkIGlzIHRoZSBudW1iZXIgb2YgcHJlZGljdG9ycyBhbmQgJFxoYXR7XHNpZ21hIH1eezJ9JCAgcmVmZXJzIHRvIGFuIGVzdGltYXRlIG9mIHRoZSB2YXJpYW5jZSBhc3NvY2lhdGVkIHdpdGggZWFjaCByZXNwb25zZSBpbiB0aGUgbGluZWFyIG1vZGVsIChlc3RpbWF0ZWQgb24gYSBtb2RlbCBjb250YWluaW5nIGFsbCBwcmVkaWN0b3JzKS4gICBBIHNtYWxsIHZhbHVlIG9mICRDX3AkIG1lYW5zIHRoYXQgdGhlIG1vZGVsIGlzIHJlbGF0aXZlbHkgcHJlY2lzZS4gIFRoZSAkQ19wJCBhZGRzIGEgcGVuYWx0eSBvZiAkMnBcaGF0e1xzaWdtYX1eMiQgdG8gdGhlIHRyYWluaW5nICRSU1MkIGluIG9yZGVyIHRvIGFkanVzdCBmb3IgdGhlIGZhY3QgdGhhdCB0aGUgdHJhaW5pbmcgZXJyb3IgdGVuZHMgdG8gdW5kZXJlc3RpbWF0ZSB0aGUgbmV3IGRhdGEgdGVzdCBlcnJvci4gT25seSBpZiB0aGUgZGVjcmVhc2UgaW4gJFJTUyQgaXMgbW9yZSB0aGFuICBpbmNyZWFzZSBpbiBwZW5hbHR5IHRlcm0sIHdpbGwgJENfcCQgZ28gZG93bi4gDQoNCkZvciBvdXIgbGVhc3Qgc3F1YXJlcyBtb2RlbCwgTWFsbG93c+KAmXMgJENfcCQgaXMgZXF1aXZhbGVudCB0byBfX0FrYWlrZSBpbmZvcm1hdGlvbiBjcml0ZXJpb25fXyAoJEFJQyQpIGRlZmluZWQgZm9yIGEgbGFyZ2UgY2xhc3Mgb2YgbW9kZWxzIGZpdCBieSBtYXhpbXVtIGxpa2VsaWhvb2QgJFxoYXR7TH0kLiAgDQokJEFJQyA9IDJwLVxsbihcaGF0e0x9KSA9IFxmcmFjezF9e25caGF0e1xzaWdtYX1eMn0oUlNTICsycFxoYXR7XHNpZ21hfV4yKSQkDQpfX0JheWVzIEluZm9ybWF0aW9uIENyaXRlcmlvbl9fICgkQklDJCkgaXMgZGVyaXZlZCBmcm9tIGEgQmF5ZXNpYW4gUHJvYmFiaWxpdHkgdGhlb3J5LiBGb3IgdGhlIGxlYXN0IHNxdWFyZXMgbW9kZWwgd2l0aCAkcCQgcHJlZGljdG9ycywgdGhlICRCSUMkIGlzLCB1cCB0byBpcnJlbGV2YW50IGNvbnN0YW50cywgZ2l2ZW4gYnk6DQokJEJJQz1cZnJhY3sxfXtufShSU1MgK1xsb2cobilwXGhhdHtcc2lnbWF9XjIpICQkDQpMaWtlICRDX3AkLCB0aGUgJEJJQyQgaXMgc21hbGxlciBmb3IgbW9kZWxzIHdpdGggbG93ZXIgdGVzdCBlcnJvciwgc28gd2UgYXJlIGxvb2tpbmcgZm9yIG1vZGVsIHdpdGggdGhlIGxvd2VzdCBCSUMgdmFsdWUuICRsb2cobikgPiAyJCBmb3IgYW55IHJlYXNvbmFibGUgc2FtcGxlIHNpemUgJG4kLCBzbyAgQklDIGhhcyBiaWdnZXIgcGVuYWx0eSBvbiBtb2RlbHMgd2l0aCBtYW55IHZhcmlhYmxlcywgYW5kIHRoZXJlZm9yZSBwcm9kdWNlcyBzbWFsbGVyIG1vZGVscyB0aGFuICRDX3AkLg0KDQoNCl9fQmVzdCBTdWJzZXQgU2VsZWN0aW9uX18gLSBhIGxlYXN0IHNxdWFyZXMgcmVncmVzc2lvbiBpcyBmaXQgZm9yIGVhY2ggcG9zc2libGUgY29tYmluYXRpb24gb2YgdGhlICRwJCBwcmVkaWN0b3JzLiBUaGVyZSBhcmUgJHAkIG1vZGVscyB3aXRoIG9uZSBwcmVkaWN0b3IsICRcZnJhY3twKHAtMSl9ezJ9JCBtb2RlbHMgd2l0aCB0d28gcHJlZGljdG9ycywgZXRjLi4uICBFYWNoIHByZWRpY3RvciBjYW4gYmUgaW4gb3Igb3V0IG9mIHRoZSBtb2RlbCwgc28gdGhlcmUgYXJlICQyXnAkIHBvc3NpYmxlIG1vZGVscy4gIFNheSwgZm9yICRwPTEwJCBwcmVkaWN0b3JzLCB0aGVyZSBhcmUgJDJeezEwfT0xMDI0JCBtb2RlbHMgdG8gaW52ZXN0aWdhdGUuICAgVGhlIGFsZ29yaXRobSBwcm9jZWVkcyBhcyBmb2xsb3dzOiAgICANCjEpIExldCAkTV8wJCBiZSB0aGUgbnVsbCBtb2RlbCB3aXRoIG5vIHByZWRpY3RvcnMuIEl0IHNpbXBseSBwcmVkaWN0cyB0aGUgc2FtcGxlIG1lYW4gZm9yIGVhY2ggb2JzZXJ2YXRpb24uICAgICAgIA0KMikgRm9yICRrID0gMSwgMiwgLiAuIC4gcCQsIGZpdCBhbGwgJF9rQ19wJAkgbW9kZWxzIHdpdGggZXhhY3RseSAkayQgcHJlZGljdG9ycy4gUGljayB0aGUgYmVzdCBhY2NvcmRpbmcgdG8gc21hbGxlc3QgJFJTUyQgYW5kIGNhbGwgaXQgJE1fayQuICAgICANCjMpIFNlbGVjdCB0aGUgYmVzdCBtb2RlbCBmcm9tIGFtb25nICRNXzAsLi4uLCBNX3AkIHVzaW5nICRDX3AgKEFJQykkLCAkQklDJCwgICRyXjJfe2Fkan0kLCBvciBjcm9zcyB2YWxpZGF0ZWQgcHJlZGljdG9uIGVycm9yLiAgIA0KU3RlcCAyIGlkZW50aWZpZXMgdGhlIGJlc3QgbW9kZWwgZm9yIHRoZSB0cmFpbmluZyBkYXRhLCB0aGVuIHN0ZXAgMyBwaWNrcyB0aGUgYmVzdCB1c2luZyB0aGUgY3JpdGVyaWEgcGVuYWxpemluZyBmb3IgaW5jbHVzaW9uIG9mIG5vbi1yZWxldmFudCBwcmVkaWN0b3JzLg0KDQpUaGUgZXhwb25lbnRpYWwgZ3Jvd3RoIG9mIHRoZSBudW1iZXIgb2YgbW9kZWxzIGlzIGEgc2VyaW91cyBjb21wdXRhdGlvbmFsIGlzc3VlLiAgQWxzbywgdGhlIG1vZGVscyB3aXRoIHZlcnkgbGFyZ2UgbnVtYmVyIG9mIHByZWRpY3RvcnMgYXJlIGhhcmQgdG8gaW50ZXJwcmV0LiAgVGhlcmVmb3JlLCAgZm9yd2FyZCBhbmQgYmFja3dhcmQgc3RlcHdpc2Ugc2VsZWN0aW9uIGlzIG9mdGVuIHVzZWQgaW5zdGVhZC4gIF9fRm9yd2FyZCBzdGVwd2lzZSBzZWxlY3Rpb25fXyBiZWdpbnMgd2l0aCBuby1wcmVkaWN0b3JzIG1vZGVsIGFuZCBhZGRzIGEgcHJlZGljdG9yIHRoYXQgZ2l2ZXMgdGhlIGdyZWF0ZXN0IGFkZGl0aW9uYWwgaW1wcm92ZW1lbnQgdG8gdGhlICRSU1MkIGZpdCBvbmUtYXQtYS10aW1lLiBfX0JhY2t3YXJkIHN0ZXB3aXNlIHNlbGVjdGlvbl9fIGJlZ2lucyB3aXRoIHRoZSBmdWxsIG1vZGVsIHdpdGggYWxsICRwJCBwcmVkaWN0b3JzLCBhbmQgdGhlbiBpdGVyYXRpdmVseSByZW1vdmVzIHRoZSBsZWFzdCB1c2VmdWwgcHJlZGljdG9yLCBvbmUtYXQtYS10aW1lLg0KDQoNCiMjIyBTdWJzZXQgU2VsZWN0aW9uIE1ldGhvZHMNCg0KSW4gdGhlIF9fQmVzdCBTdWJzZXQgU2VsZWN0aW9uX18gYSBsZWFzdCBzcXVhcmVzIHJlZ3Jlc3Npb24gaXMgZml0IGZvciBlYWNoIHBvc3NpYmxlIGNvbWJpbmF0aW9uIG9mIHRoZSAkcCQgcHJlZGljdG9ycy4gVGhlcmUgYXJlICRwJCBtb2RlbHMgd2l0aCBvbmUgcHJlZGljdG9yLCAkXGZyYWN7cChwLTEpfXsyfSQgbW9kZWxzIHdpdGggdHdvIHByZWRpY3RvcnMsIGV0Yy4uLiAgRWFjaCBwcmVkaWN0b3IgY2FuIGJlIGluIG9yIG91dCBvZiB0aGUgbW9kZWwsIHNvIHRoZXJlIGFyZSAkMl5wJCBwb3NzaWJsZSBtb2RlbHMuICBTYXksIGZvciAkcD0xMCQgcHJlZGljdG9ycywgdGhlcmUgYXJlICQyXnsxMH09MTAyNCQgbW9kZWxzIHRvIGludmVzdGlnYXRlLiAgIFRoZSBhbGdvcml0aG0gcHJvY2VlZHMgYXMgZm9sbG93czogICAgDQoxKSBMZXQgJE1fMCQgYmUgdGhlIG51bGwgbW9kZWwgd2l0aCBubyBwcmVkaWN0b3JzLiBJdCBzaW1wbHkgcHJlZGljdHMgdGhlIHNhbXBsZSBtZWFuIGZvciBlYWNoIG9ic2VydmF0aW9uLiAgICAgICANCjIpIEZvciAkayA9IDEsIDIsIC4gLiAuIHAkLCBmaXQgYWxsICRfa0NfcCQJIG1vZGVscyB3aXRoIGV4YWN0bHkgJGskIHByZWRpY3RvcnMuIFBpY2sgdGhlIGJlc3QgYWNjb3JkaW5nIHRvIHNtYWxsZXN0ICRSU1MkIGFuZCBjYWxsIGl0ICRNX2skLiAgICAgDQozKSBTZWxlY3QgdGhlIGJlc3QgbW9kZWwgZnJvbSBhbW9uZyAkTV8wLC4uLiwgTV9wJCB1c2luZyAkQ19wIChBSUMpJCwgJEJJQyQsICAkcl4yX3thZGp9JCwgb3IgY3Jvc3MgdmFsaWRhdGVkIHByZWRpY3RvbiBlcnJvci4gICANClN0ZXAgMiBpZGVudGlmaWVzIHRoZSBiZXN0IG1vZGVsIGZvciB0aGUgdHJhaW5pbmcgZGF0YSwgdGhlbiBzdGVwIDMgcGlja3MgdGhlIGJlc3QgdXNpbmcgdGhlIGNyaXRlcmlhIHBlbmFsaXppbmcgZm9yIGluY2x1c2lvbiBvZiBub24tcmVsZXZhbnQgcHJlZGljdG9ycy4NCg0KVGhlIGV4cG9uZW50aWFsIGdyb3d0aCBvZiB0aGUgbnVtYmVyIG9mIG1vZGVscyBpcyBhIHNlcmlvdXMgY29tcHV0YXRpb25hbCBpc3N1ZS4gIEFsc28sIHRoZSBtb2RlbHMgd2l0aCB2ZXJ5IGxhcmdlIG51bWJlciBvZiBwcmVkaWN0b3JzIGFyZSBoYXJkIHRvIGludGVycHJldC4gIFRoZXJlZm9yZSwgIGZvcndhcmQgYW5kIGJhY2t3YXJkIHN0ZXB3aXNlIHNlbGVjdGlvbiBpcyBvZnRlbiB1c2VkIGluc3RlYWQuICBfX0ZvcndhcmQgc3RlcHdpc2Ugc2VsZWN0aW9uX18gYmVnaW5zIHdpdGggbm8tcHJlZGljdG9ycyBtb2RlbCBhbmQgYWRkcyBhIHByZWRpY3RvciB0aGF0IGdpdmVzIHRoZSBncmVhdGVzdCBhZGRpdGlvbmFsIGltcHJvdmVtZW50IHRvIHRoZSAkUlNTJCBmaXQgb25lLWF0LWEtdGltZS4gX19CYWNrd2FyZCBzdGVwd2lzZSBzZWxlY3Rpb25fXyBiZWdpbnMgd2l0aCB0aGUgZnVsbCBtb2RlbCB3aXRoIGFsbCAkcCQgcHJlZGljdG9ycywgYW5kIHRoZW4gaXRlcmF0aXZlbHkgcmVtb3ZlcyB0aGUgbGVhc3QgdXNlZnVsIHByZWRpY3Rvciwgb25lLWF0LWEtdGltZS4NCg==