library(ggplot2)

This lecture will cover an introduction to regression with multiple explanatory variables.

Scottish Hill Racing Data Revisited

For the linear model of time against dist , plot residuals against climb .

data(hills, package = "MASS")
Hills.lm <- lm(time ~ dist, data = hills)
plot(resid(Hills.lm) ~ climb, data = hills, xlab = "Climb")

unlabelled

This is a lurking variable plot. That is, it shows that the residuals are correlated with another variable not in the model, implying that if we include that variable in the regression model it will explain more of the data.

The Multiple Linear Regression Model

The multiple linear regression model is \[Y_i = \beta_0 + \beta_1 x_{i1} + \ldots + \beta_p x_{ip} + \varepsilon_i\] for i=1,2,…,n.

Note now that:

Parameter Estimation

Fitted Values and Residuals

New Zealand Climate Data Example

Variable Description
Y= MnJlyTemp Mean July temperature (degrees Celsius)
MnJanTemp Mean January temperature (degrees Celsius)
Lat Degrees of latitude (south)
Long Degrees of longitude (east)
Rain Annual precipitation (mm)
Sun Annual hours of sunshine
Height Average height above sea level (in metres)
Sea 0 = no (inland) 1 = yes is coastal
NorthIsland 0 = no, 1= yes

Suppose our aim is to find a model that explains the mean July Temperature in the different locations, in terms of the other variables. We will sometimes refer to this variable MnJlyTemp as y and the others as x1, x2, …, x8 respectively.

Note it does not matter that x7 = Sea and x8 = NorthIsland are just 0 - 1 variables. These coded variables are called indicator variables and are an extremely important aspect of regression models, so important that they feature heavily in the rest of the course. Look out for them.

Eg. the slope \(\beta_7\) will still represent a one-unit change, i.e. the difference between a non-Coastal (Sea=0) and Coastal (Sea=1) location.

Download Climate.csv to be able to run the examples below.

Climate = read.csv(file = "../../data/Climate.csv", header = TRUE, row.names = 1)
head(Climate)
            Lat  Long MnJanTemp MnJlyTemp Rain  Sun Height Sea NorthIsland
Kaitaia    35.1 173.3      19.3      11.7 1418 2113     80   1           1
Kerikeri   35.2 174.0      18.9      10.8 1682 2004     73   1           1
Dargaville 36.0 173.8      18.6      10.7 1248 1956     20   1           1
Whangarei  35.7 174.3      19.7      11.0 1600 1925     29   1           1
Auckland   36.9 174.8      19.4      11.0 1185 2102     49   1           1
Tauranga   37.7 176.2      18.5       9.3 1349 2277      4   1           1
par(mfrow = c(1, 2))
plot(Lat ~ Long, pch = 2 - Sea, data = Climate)
plot(-Lat ~ Long, pch = NorthIsland + 16, col = NorthIsland + 3, data = Climate)

unlabelled

To get a quick overview of the data we can use the pairs() command to get a scatterplot matrix. Clearly the variables are related.

pairs(Climate)

unlabelled

To fit a multiple regression \[E[Y_i] = \beta_0 + \beta_1 x_{i1} + \ldots + \beta_p x_{ip}\] you simply mention all the variables in the model statement of lm().

Climate.lm = lm(MnJlyTemp ~ MnJanTemp + Lat + Long + Rain + Sun + Height + Sea +
    NorthIsland, data = Climate)
summary(Climate.lm)

Call:
lm(formula = MnJlyTemp ~ MnJanTemp + Lat + Long + Rain + Sun + 
    Height + Sea + NorthIsland, data = Climate)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.2213 -0.5257 -0.2620  0.3796  3.3033 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)   
(Intercept)  5.2829167 24.0200695   0.220  0.82757   
MnJanTemp   -0.0380083  0.2901840  -0.131  0.89676   
Lat         -0.3655501  0.1638323  -2.231  0.03416 * 
Long         0.1012667  0.1253184   0.808  0.42611   
Rain         0.0002901  0.0002971   0.977  0.33744   
Sun         -0.0006941  0.0011596  -0.599  0.55442   
Height      -0.0049986  0.0014557  -3.434  0.00194 **
Sea          1.7298287  0.5472228   3.161  0.00386 **
NorthIsland  1.3589549  0.8087868   1.680  0.10445   
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.9627 on 27 degrees of freedom
Multiple R-squared:  0.9074,    Adjusted R-squared:   0.88 
F-statistic: 33.09 on 8 and 27 DF,  p-value: 5.255e-12

The \(R^2\) is interpreted the same as before: 90.7% of the variation in the Y values (mean July temperatures) is explained by the predictors.

The residual mean square = S is still interpreted as the standard deviation of the residuals. That is, S= 0.9627 represents the typical variation of the raw data (actual Y) around the fitted values. Putting it another way, we expect about \(95\%\) of data to be within \(2S \approx 2\) degrees of the predicted mean July temperature.

The information about residuals at the start of the summary output shows that the maximum residual is 3.3, so that means there is a location which it 3.3 degrees warmer in July than would be predicted by the covariates.

Now if we look at the table of coefficients we see that only three variables have significant P-values: Lat, Height and Sea. The other variables are not significant. So it looks as though one or more of them need to be removed from the regression.

par(mfrow = c(2, 2))
plot(Climate.lm)

unlabelled

Looking at the residual plots, we see that row number 9 (Gisbourne, on the east coast of the North Island) is the location with the big residual. The first plot suggests a hint of curvature, but it is weak and it is too soon to worry about this when we need to remove some covariates.

from the normal Q-Q plot that, apart from row 9, the residuals are fairly normal, but row 9 has a standardized residual\(>3\). Many statistical programs automatically flag points with standardized residual\(>3\) as outliers.

The bottom right graph shows that row 9 is near the boundary for Cooks’ D > 0.5, but it is acceptable to say that no locations are having extraordinary influence on the regression coefficients. One location, row 28, is high leverage, which means its covariate values are unusual compared to those of other locations.

Adjusted \(R^2\) and F test

We will consider the F test in more detail later, but for now you should know that it looks to see whether there is anything in our model which is related to the response (Y) variable, whether that is a single variable or a group of variables. i.e. it tests the hypothesis that all the slopes are zero. \[H_0: \beta_1=\beta_2=...=\beta_p = 0\] If F is large we reject this hypothesis. For the moment we shall just consider the P-value. Since \(P= 5.255\times 10^{-12}\) this is an exceedingly small number, so we reject \(H_0\) and conclude that at least one of the slopes is non-zero.

The adjusted \(R^2\) is only used to compare two models with different numbers of regression parameters. When we add a variable to a regression model, the ordinary multiple \(R^2\) will go up, even if the variable added is rubbish. The adjusted \(R^2\) is an alternative formula which will only go up if the additional model explains more than at least one extra row of data. (This is a very weak criterion indeed).
Conversely if we delete a useless variable, the adjusted \(R^2\) should go up, or at least not go down.

Just for the sake of illustration, suppose we were to remove the variable with the biggest P-value in the previous regression, namely MnJanTemp, with a P-value of 0.897.

Climate.lm1 = update(Climate.lm, . ~ . - MnJanTemp)
summary(Climate.lm1)

Call:
lm(formula = MnJlyTemp ~ Lat + Long + Rain + Sun + Height + Sea + 
    NorthIsland, data = Climate)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.2167 -0.5622 -0.2556  0.3987  3.2860 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  4.0580828 21.7332488   0.187 0.853225    
Lat         -0.3500826  0.1115481  -3.138 0.003978 ** 
Long         0.1011944  0.1230982   0.822 0.417987    
Rain         0.0003045  0.0002712   1.123 0.270963    
Sun         -0.0007389  0.0010886  -0.679 0.502865    
Height      -0.0048888  0.0011689  -4.182 0.000257 ***
Sea          1.7495792  0.5167223   3.386 0.002118 ** 
NorthIsland  1.3678091  0.7916852   1.728 0.095055 .  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.9456 on 28 degrees of freedom
Multiple R-squared:  0.9074,    Adjusted R-squared:  0.8842 
F-statistic: 39.19 on 7 and 28 DF,  p-value: 8.063e-13

The adjusted \(R^2\) here is 0.8842. In the model with the extra variable MnJanTemp the adjusted \(R^2\) is 0.88. That is, if we were to add that variable back in to the model again then the adjusted \(R^2\) would go down. This is clear evidence that we do not need this variable MnJanTemp.

Simplifying the regression model

Clearly not all the variables in the ‘full’ regression model have significant p-values. (By ‘full’ model I mean with all the predictors X1,..,X8 in there). We need to find out which variables are in fact needed.

There are two main approaches to choosing a model

Since the predictors are usually not independent of each other, it is important to make any model changes one variable at a time. (We will see a reason for this later in the course).

Note we will study model selection techniques in more detail later on, where some automated procedures are introduced, including a hybrid forwards/backwards method called ‘stepwise model selection’.

Before we get to that point, we will take the task slowly because we are aiming to understand what is going on.

We start by trying to use forward selection, beginning with the most highly correlated single variable.

round(cor(Climate), 3)
               Lat   Long MnJanTemp MnJlyTemp   Rain    Sun Height    Sea
Lat          1.000 -0.754    -0.825    -0.763 -0.036 -0.367  0.136 -0.152
Long        -0.754  1.000     0.707     0.591 -0.231  0.459 -0.102 -0.020
MnJanTemp   -0.825  0.707     1.000     0.743 -0.304  0.556 -0.432  0.235
MnJlyTemp   -0.763  0.591     0.743     1.000  0.033  0.334 -0.613  0.577
Rain        -0.036 -0.231    -0.304     0.033  1.000 -0.440  0.198  0.097
Sun         -0.367  0.459     0.556     0.334 -0.440  1.000 -0.236  0.301
Height       0.136 -0.102    -0.432    -0.613  0.198 -0.236  1.000 -0.659
Sea         -0.152 -0.020     0.235     0.577  0.097  0.301 -0.659  1.000
NorthIsland -0.837  0.829     0.710     0.652 -0.125  0.242 -0.086 -0.070
            NorthIsland
Lat              -0.837
Long              0.829
MnJanTemp         0.710
MnJlyTemp         0.652
Rain             -0.125
Sun               0.242
Height           -0.086
Sea              -0.070
NorthIsland       1.000

Looking at the column for MnJlyTemp we see that the variables with biggest correlation are Lat (correlation = -0.763) and MnJanTemp (correlation = 0.743). Looking at the correlations would give us the same p-value as regressing the predictor by itself.

So why did the previous argument say that MnJanTemp was useless but now the correlations say it is highly related to y?

The answer is that virtually all of the information that MnJanTemp would bring to the regression, is already expressed in the other covariates. So by itself it is informative, but after everything else, MnJanTemp has nothing more to tell us about y = MnJlyTemp.

Single-Variable Model

We will start by regressing MnJlyTemp on Lat.

Climate.lm1 = lm(MnJlyTemp ~ Lat, data = Climate)
summary(Climate.lm1)

Call:
lm(formula = MnJlyTemp ~ Lat, data = Climate)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.6992 -0.9786  0.1566  1.0588  4.7930 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 34.42127    3.94299   8.730 3.35e-10 ***
Lat         -0.66187    0.09613  -6.885 6.25e-08 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 1.822 on 34 degrees of freedom
Multiple R-squared:  0.5824,    Adjusted R-squared:  0.5701 
F-statistic: 47.41 on 1 and 34 DF,  p-value: 6.25e-08

Clearly Lat is significant.

Can you interpret the slope?

Can you interpret the intercept (is this realistic)? Hint: Think a bit about NZ’s location.

Trying a two-variable model

We will look to see what happens if we add in MnJanTemp or Height on top of the existing variable Lat.

Climate.lm2 = lm(MnJlyTemp ~ Lat + MnJanTemp, data = Climate)
summary(Climate.lm2)

Call:
lm(formula = MnJlyTemp ~ Lat + MnJanTemp, data = Climate)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.1955 -1.1055 -0.1844  1.3421  4.2562 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)  
(Intercept)  13.6470    11.6982   1.167   0.2517  
Lat          -0.4073     0.1643  -2.479   0.0184 *
MnJanTemp     0.6127     0.3263   1.878   0.0693 .
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 1.758 on 33 degrees of freedom
Multiple R-squared:  0.6227,    Adjusted R-squared:  0.5998 
F-statistic: 27.23 on 2 and 33 DF,  p-value: 1.037e-07
Climate.lm3 = lm(MnJlyTemp ~ Lat + Height, data = Climate)
summary(Climate.lm3)

Call:
lm(formula = MnJlyTemp ~ Lat + Height, data = Climate)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.0607 -0.6215 -0.1021  0.5145  3.9898 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) 32.8788366  2.4333203  13.512 5.30e-15 ***
Lat         -0.6005121  0.0596687 -10.064 1.38e-11 ***
Height      -0.0071984  0.0009542  -7.544 1.12e-08 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 1.121 on 33 degrees of freedom
Multiple R-squared:  0.8467,    Adjusted R-squared:  0.8374 
F-statistic: 91.14 on 2 and 33 DF,  p-value: 3.639e-14
par(mfrow = c(2, 2))
plot(Climate.lm3)

unlabelled

In the first analysis, MnJanTemp had a non-significant P-value, so again indicating that it is not useful. However the adjusted \(R^2\) does go up so we would not be able to dismiss it entirely.

In the second analysis Height is very significant and the adjusted \(R^2\) has gone up a very big amount, so it is right to include Height in the regression model.

However the diagnostic plots do not show much difference to the plots for the full model.

On regression diagnostics for multiple regression

When we had just one predictor on offer, we should have checked for nonlinearity in the relationship.

It is recommended before the multiple regression to plot Y vs each explanatory variable \(x_1, ..., x_p\) in turn to check for curvature.

But sometimes problems only show up once one has adjusted for other variables. So, after fitting the model plot residuals \(e_i\) against each \(x_i\), or use standardized residuals. Be boringly systematic.

And it’s a good idea to use a lowess smoother to check for possible curvature.

library(ggplot2)
Climate |>
    ggplot(aes(y = residuals(Climate.lm3), x = Height)) + geom_point() + geom_smooth()
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

unlabelled

This graph shows possible curvature with height once latitude is allowed for: perhaps a transformation might give a better model? Something to think about later.

It is better to be alerted to a possible problem and be able to dismiss it, than to be unaware of the possible problem at all.

Outliers

Just as for the simple model, residuals are defined as \(e_i = y_i – \hat y_i\) .
And again, just as for the simple model, the standard errors for the residuals depends on how unusual the corresponding x values are. This complicates the interpretation of the graphs, so we can remove this complication by looking at standardized residuals which are scaled to give equal chance of spread at every x value. Standardized residuals should usually lie within the range \(\pm 2\), and very seldom \(<-3\) or \(> 3\)

Also as before, we can check for whether a particular point is dragging the line towards itself (and thus not appearing as unusual as it should) by computing externally studentized deleted residuals.

It is often helpful to plot the standardized and deleted residuals on the same graph, as that helps show whether the deletion is making a big difference.

plot(rstudent(Climate.lm3) ~ Climate.lm3$fitted.values, col = 2, pch = "+", main = "standardized(o) and studentized(+) residuals vs fitted values")
points(rstandard(Climate.lm3) ~ Climate.lm3$fitted.values)

unlabelled

It is clear that deleting Gisborne would make a big difference to the regression, so we should check to make sure the number is correct. However deleting any other data points would make very little difference.

Checking leverage

The leverage values \(h_{ii}\) can be obtained using the hatvalues() function, and plotted against any variables of interest.

The average value of \(h_{ii}\) is \(p/n\).
Points with \(h_{ii} > 3 p/n\) are regarded as points worth checking.

For the Climate.lm3 model p=3 (intercept+ 2 slopes) and n=36.

plot(hatvalues(Climate.lm3) ~ Lat, data = Climate)
abline(h = 3 * length(coef(Climate.lm3))/nrow(Climate))

unlabelled

plot(hatvalues(Climate.lm3) ~ Height, data = Climate)
abline(h = 3 * length(coef(Climate.lm3))/nrow(Climate))

unlabelled

The leverage is related to \((\frac{x-\bar x}{sd(x)})^2\) combined across each predictor in the the model. So the graph has a characteristic quadratic shape for most of the points.

The first graph indicates that high leverage is not related to Lat. The second graph indicates it is explained by extreme values for Height.

Occasionally you get a point which is high leverage although it is not extreme in either variable. In that case it is because the combination of the predictor variables is unusual.

Interpreting Slopes from a Multiple Regression

Suppose we compare the coefficients from

  1. the simple linear regression of MnJlyTemp on Lat, to those produced by
  2. the multiple regression of MnJlyTemp on Lat and Height.
coef(Climate.lm1)
(Intercept)         Lat 
 34.4212725  -0.6618663 
coef(Climate.lm3)
 (Intercept)          Lat       Height 
32.878836627 -0.600512089 -0.007198381 

We see the slope associated with Lat is a bit different. Why?

The answer is that Lat and Height are slightly correlated so the presence of Height in the model means Lat does not need to explain MnJlyTemp all by itself. Since there are more very high places in the South Island than in the North Island, some of the trend with Lat is taken away by including Height.

So we settle for the following interpretation:

In a combined regression model \[Y = \beta_0 + \beta_1 x_1 + \beta_2 x_2\]

we interpret \(\beta_1\) the rate of change of y per unit increase of \(x_1\) if \(x_2\) is held constant.

Similarly we interpret \(\beta_2\) the rate of change of y per unit increase of \(x_2\) if \(x_1\) is held constant.

We will see in later lectures that if adding a new variable such as \(x_2\) into a model makes a big difference to the slope estimates, then it may be a good idea to include it, even if its P-value did not quite meet the P<0.05 rule.

Confidence intervals for coefficients and Prediction intervals

These are calculated the same way for a multiple regression as was done for a simple linear model.

Note that in order to make point or interval estimates, you need to state which values are associated with which variables and put them in a data.frame().

confint(Climate.lm3)
                   2.5 %       97.5 %
(Intercept) 27.928209206 37.829464049
Lat         -0.721909066 -0.479115112
Height      -0.009139715 -0.005257046
x0 <- data.frame(Lat = 40.3525, Height = 20)
# Predicting Mean July Temperature for Palmerston North

predict(Climate.lm3, newdata = x0, interval = "prediction", level = 0.95)
       fit      lwr      upr
1 8.502705 6.180698 10.82471
LS0tDQp0aXRsZTogIkxlY3R1cmUgOTogSW50cm9kdWN0aW9uIHRvIE11bHRpcGxlIExpbmVhciBSZWdyZXNzaW9uIg0Kc3VidGl0bGU6IDE2MS4yNTEgUmVncmVzc2lvbiBNb2RlbGxpbmcNCmF1dGhvcjogIlByZXNlbnRlZCBieSBNYXR0aGV3IFBhd2xleSA8TS5QYXdsZXlAbWFzc2V5LmFjLm56PiIgIA0KZGF0ZTogIldlZWsgMyBvZiBTZW1lc3RlciAyLCBgciBsdWJyaWRhdGU6OnllYXIobHVicmlkYXRlOjpub3coKSlgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogIGlvc2xpZGVzX3ByZXNlbnRhdGlvbjoNCiAgICB3aWRlc2NyZWVuOiB0cnVlDQogICAgc21hbGxlcjogdHJ1ZQ0KICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0DQogIHNsaWR5X3ByZXNlbnRhdGlvbjogDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KLS0tDQoNCg0KDQoNCjwhLS0tIERhdGEgaXMgb24NCmh0dHBzOi8vci1yZXNvdXJjZXMubWFzc2V5LmFjLm56L2RhdGEvMTYxMjUxLw0KLS0tPg0KDQpgYGB7ciBzZXR1cCwgcHVybD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkoa25pdHIpDQpvcHRzX2NodW5rJHNldChkZXY9YygicG5nIiwgInBkZiIpKQ0Kb3B0c19jaHVuayRzZXQoZmlnLmhlaWdodD02LCBmaWcud2lkdGg9NywgZmlnLnBhdGg9IkZpZ3VyZXMvIiwgZmlnLmFsdD0idW5sYWJlbGxlZCIpDQpvcHRzX2NodW5rJHNldChjb21tZW50PSIiLCBmaWcuYWxpZ249ImNlbnRlciIsIHRpZHk9VFJVRSkNCm9wdGlvbnMoa25pdHIua2FibGUuTkEgPSAnJykNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShicm9vbSkNCmBgYA0KDQoNCjwhLS0tIERvIG5vdCBlZGl0IGFueXRoaW5nIGFib3ZlIHRoaXMgbGluZS4gLS0tPg0KDQpgYGB7ciBleHRyYVBrZ3N9DQpsaWJyYXJ5KGdncGxvdDIpDQpgYGANCg0KVGhpcyBsZWN0dXJlIHdpbGwgY292ZXIgYW4gaW50cm9kdWN0aW9uIHRvIHJlZ3Jlc3Npb24gd2l0aCBtdWx0aXBsZSBleHBsYW5hdG9yeSB2YXJpYWJsZXMuDQoNCg0KIyMgU2NvdHRpc2ggSGlsbCBSYWNpbmcgRGF0YSBSZXZpc2l0ZWQNCg0KDQoNCg0KRm9yIHRoZSBsaW5lYXIgbW9kZWwgb2YgIGB0aW1lYCAgYWdhaW5zdCAgYGRpc3RgICwgcGxvdCByZXNpZHVhbHMgYWdhaW5zdCAgYGNsaW1iYCAuDQoNCmBgYHtyIEhpbGxzTHVya30NCmRhdGEoaGlsbHMsIHBhY2thZ2U9Ik1BU1MiKQ0KSGlsbHMubG0gPC0gbG0odGltZX5kaXN0LCBkYXRhPWhpbGxzKSANCnBsb3QocmVzaWQoSGlsbHMubG0pfmNsaW1iLCBkYXRhPWhpbGxzLCB4bGFiPSJDbGltYiIpDQpgYGANCg0KVGhpcyBpcyBhICoqbHVya2luZyB2YXJpYWJsZSoqIHBsb3QuICBUaGF0IGlzLCBpdCBzaG93cyB0aGF0IHRoZSByZXNpZHVhbHMgYXJlIGNvcnJlbGF0ZWQgd2l0aCBhbm90aGVyIHZhcmlhYmxlIG5vdCBpbiB0aGUgbW9kZWwsIGltcGx5aW5nIHRoYXQgaWYgd2UgaW5jbHVkZSB0aGF0IHZhcmlhYmxlIGluIHRoZSByZWdyZXNzaW9uIG1vZGVsIGl0IHdpbGwgZXhwbGFpbiBtb3JlIG9mIHRoZSBkYXRhLg0KDQoNCg0KDQoNCiAgLSBGb3IgdGhlIFNjb3R0aXNoIEhpbGwgUmFjaW5nIGRhdGEsIHRoZSB0cmVuZCBpbiB0aGUgbHVya2luZyB2YXJpYWJsZQ0KICAgIHBsb3Qgb2YgcmVzaWR1YWxzIGFnYWluc3QgYGNsaW1iYCBpbmRpY2F0ZXMgdGhhdCB0aGUgaGVpZ2h0IGNsaW1iZWQNCiAgICBzaG91bGQgYmUgaW5jbHVkZWQgaW4gdGhlIG1vZGVsIGluIGFkZGl0aW9uIHRvIHRoZSByYWNlIGRpc3RhbmNlLg0KDQogIC0gRm9yIGFwcGxpY2F0aW9ucyBzdWNoIGFzIHRoaXMgd2UgbmVlZCB0byBleHRlbmQgc2ltcGxlIGxpbmVhcg0KICAgIHJlZ3Jlc3Npb24gdG8gaW5jb3Jwb3JhdGUgbXVsdGlwbGUgcHJlZGljdG9ycy4NCg0KICAtIFRoaXMgZXh0ZW5kZWQgbWV0aG9kb2xvZ3kgaXMgY2FsbGVkICoqbXVsdGlwbGUgbGluZWFyICByZWdyZXNzaW9uKiouDQoNCiMjIFRoZSBNdWx0aXBsZSBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbA0KDQpUaGUgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgaXMNCiQkWV9pID0gXGJldGFfMCArIFxiZXRhXzEgeF97aTF9ICsgXGxkb3RzICsgXGJldGFfcCB4X3tpcH0NCisgXHZhcmVwc2lsb25faSQkDQpmb3IgKmk9MSwyLC4uLixuKi4NCg0KTm90ZSBub3cgdGhhdDogDQoNCiAgLSBUaGVyZSBhcmUgKnAqIHByZWRpY3RvciB2YXJpYWJsZXMgKnh+MX4qLCAqeH4yfiosIC4uLiwgKnh+cH4qLg0KDQogIC0gVGhlIG5vdGF0aW9uICp4fmlqfiogZGVub3RlcyB0aGUgdmFsdWUgb2YgKnh+an4qIGZvciB0aGUgKmkqXnRoXg0KICAgIGluZGl2aWR1YWwuDQoNCiAgLSAkXHZhcmVwc2lsb25fMSwgXGxkb3RzIFx2YXJlcHNpbG9uX24kIGFyZSByYW5kb20gZXJyb3JzIHNhdGlzZnlpbmcNCiAgICBhc3N1bXB0aW9ucyBBMSAtLS0gQTQuDQoNCiAgLSAkXGJldGFfMCwgXGJldGFfMSwgXGxkb3RzLCBcYmV0YV9wJCBhcmUgdGhlIHJlZ3Jlc3Npb24gcGFyYW1ldGVycy4NCg0KIyMgUGFyYW1ldGVyIEVzdGltYXRpb24NCg0KICAtIFRoZSBlcXVhdGlvbiBmb3IgdGhlIHJlZ3Jlc3Npb24gbGluZSBhbmQgQTEgaW1wbHkgdGhhdA0KICAgICQkRVtZX2ldID0gXG11X2kgPSBcYmV0YV8wICsgXGJldGFfMSB4X3tpMX0gKyBcbGRvdHMgKyBcYmV0YV9wIHhfe2lwfSQkDQoNCiAgLSBUaGUgcGFyYW1ldGVycyAkXGJldGFfMCxcYmV0YV8xLCBcbGRvdHMsIFxiZXRhX3AkIGNhbiBiZSBlc3RpbWF0ZWQNCiAgICBieSB0aGUgbWV0aG9kIG9mIGxlYXN0IHNxdWFyZXMuDQoNCiAgLSBUaGUgbGVhc3Qgc3F1YXJlcyBlc3RpbWF0ZXMgKExTRXMpICAkXGhhdCBcYmV0YV8wLFxoYXQgXGJldGFfMSwgXGxkb3RzICxcaGF0IFxiZXRhX3AkIGFyZSBzdGlsbCB0aGUgdmFsdWVzDQogICAgdGhhdCBtaW5pbWl6ZSB0aGUgZm9sbG93aW5nIHN1bSBvZiBzcXVhcmVzOiAkJFxiZWdpbnthbGlnbmVkfQ0KICAgIFNTKFxiZXRhXzAsIFxiZXRhXzEsIFxsZG90cyAsIFxiZXRhX3ApICY9JiBcc3VtX3tpPTF9Xm4gKHlfaSAtIFxtdV9pKV4yXFwNCiAgICAgICAgJj0mIFxzdW1fe2k9MX1ebiAoeV9pIC0gXGJldGFfMCAtIFxiZXRhXzEgeF97aTF9IC0gDQogICAgICAgICBcbGRvdHMgLSBcYmV0YV9wIHhfe2lwfSApXjJcZW5ke2FsaWduZWR9JCQNCg0KICAtIFNvZnR3YXJlIHBhY2thZ2VzIGNhbiBxdWlja2x5IGNvbXB1dGUgTFNFcy4NCg0KIyMgRml0dGVkIFZhbHVlcyBhbmQgUmVzaWR1YWxzDQoNCiAgLSBUaGUgZGVmaW5pdGlvbnMgb2YgYSBmaXR0ZWQgdmFsdWUgYW5kIGEgcmVzaWR1YWwgaW4gbXVsdGlwbGUgbGluZWFyDQogICAgcmVncmVzc2lvbiBmb2xsb3cgb24gbmF0dXJhbGx5IGZyb20gc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uLg0KDQogIC0gVGhlICppKl50aF4gZml0dGVkIHZhbHVlIGlzDQogICAgJCRcaGF0IFxtdV9pID0gXGhhdCBcYmV0YV8wICsgXGhhdCBcYmV0YV8xIHhfe2kxfSArIFxsZG90cyArIFxoYXRcYmV0YV9wIHhfe2lwfSQkDQoNCiAgLSBUaGUgKmkqXnRoXiByZXNpZHVhbCBpcyAkZV9pID0geV9pIC0gXGhhdCBcbXVfaSQuDQoNCiAgLSBUaGUgZXJyb3IgdmFyaWFuY2UsICRcc2lnbWFeMiQsIGNhbiBiZSBlc3RpbWF0ZWQgKHVuYmlhc2VkbHkpIGJ5DQogICAgJCRzXjIgPSBcZnJhY3sxfXtuLXAtMX0gUlNTJCQgd2hlcmUgdGhlIHJlc2lkdWFsIHN1bSBvZiBzcXVhcmVzIGlzDQogICAgJFJTUyA9IFxzdW1fe2k9MX1ebiAoeV9pIC0gXGhhdCBcbXVfaSleMiQuDQoNCiMjIE5ldyBaZWFsYW5kIENsaW1hdGUgRGF0YSAgRXhhbXBsZQ0KDQpWYXJpYWJsZSB8IERlc2NyaXB0aW9uDQotLS0gfCAtLS0NCipZKj0gYE1uSmx5VGVtcGAgfCBNZWFuIEp1bHkgdGVtcGVyYXR1cmUgIChkZWdyZWVzIENlbHNpdXMpDQpgTW5KYW5UZW1wYCB8IE1lYW4gSmFudWFyeSB0ZW1wZXJhdHVyZSAgKGRlZ3JlZXMgQ2Vsc2l1cykNCmBMYXRgIHwgRGVncmVlcyBvZiBsYXRpdHVkZSAoc291dGgpIA0KYExvbmdgIHwgRGVncmVlcyBvZiBsb25naXR1ZGUgKGVhc3QpDQpgUmFpbmAgfCBBbm51YWwgcHJlY2lwaXRhdGlvbiAobW0pDQpgU3VuYCB8IEFubnVhbCBob3VycyBvZiBzdW5zaGluZQ0KYEhlaWdodGAgfCBBdmVyYWdlIGhlaWdodCBhYm92ZSBzZWEgbGV2ZWwgKGluIG1ldHJlcykNCmBTZWFgICB8IDAgPSBubyAoaW5sYW5kKSAgMSA9IHllcyAgIGlzIGNvYXN0YWwNCmBOb3J0aElzbGFuZGAgIHwgMCA9IG5vLCAgIDE9IHllcyAgDQoNClN1cHBvc2Ugb3VyICBhaW0gaXMgdG8gZmluZCBhIG1vZGVsIHRoYXQgZXhwbGFpbnMgdGhlIG1lYW4gSnVseSBUZW1wZXJhdHVyZSBpbiB0aGUgZGlmZmVyZW50IGxvY2F0aW9ucywgaW4gdGVybXMgb2YgdGhlIG90aGVyIHZhcmlhYmxlcy4gICBXZSB3aWxsIHNvbWV0aW1lcyByZWZlciB0byB0aGlzIHZhcmlhYmxlICpNbkpseVRlbXAqICBhcyAgKnkqICBhbmQgdGhlIG90aGVycyBhcyAqeH4xfiosICp4fjJ+KiwgLi4uLCAqeH44fiogcmVzcGVjdGl2ZWx5Lg0KDQpOb3RlICBpdCBkb2VzIG5vdCBtYXR0ZXIgIHRoYXQgKnh+N34qID0gYFNlYWAgIGFuZCAqeH44fiogPSBgTm9ydGhJc2xhbmRgICBhcmUganVzdCAwIC0gMSB2YXJpYWJsZXMuIFRoZXNlIGNvZGVkIHZhcmlhYmxlcyBhcmUgY2FsbGVkICoqaW5kaWNhdG9yIHZhcmlhYmxlcyoqIGFuZCBhcmUgYW4gZXh0cmVtZWx5IGltcG9ydGFudCBhc3BlY3Qgb2YgcmVncmVzc2lvbiBtb2RlbHMsIHNvIGltcG9ydGFudCB0aGF0IHRoZXkgZmVhdHVyZSBoZWF2aWx5IGluIHRoZSByZXN0IG9mIHRoZSBjb3Vyc2UuIExvb2sgb3V0IGZvciB0aGVtLg0KDQpFZy4gdGhlIHNsb3BlICRcYmV0YV83JCAgd2lsbCBzdGlsbCByZXByZXNlbnQgYSBvbmUtdW5pdCBjaGFuZ2UsICBpLmUuIHRoZSBkaWZmZXJlbmNlICBiZXR3ZWVuICBhICBub24tQ29hc3RhbCAoU2VhPTApIGFuZCBDb2FzdGFsIChTZWE9MSkgbG9jYXRpb24uDQoNCmByIHhmdW46OmVtYmVkX2ZpbGUoIi4uLy4uL2RhdGEvQ2xpbWF0ZS5jc3YiKWAgdG8gYmUgYWJsZSB0byBydW4gdGhlIGV4YW1wbGVzIGJlbG93Lg0KDQpgYGB7ciByZWFkIENsaW1hdGV9DQpDbGltYXRlPSByZWFkLmNzdihmaWxlPSIuLi8uLi9kYXRhL0NsaW1hdGUuY3N2IiwgaGVhZGVyPVRSVUUsIHJvdy5uYW1lcyA9IDEpDQpoZWFkKENsaW1hdGUpDQpwYXIobWZyb3c9YygxLDIpKSANCnBsb3QoTGF0fkxvbmcscGNoPTItU2VhLCBkYXRhPUNsaW1hdGUpDQpwbG90KC1MYXR+TG9uZyxwY2g9Tm9ydGhJc2xhbmQrMTYsY29sPU5vcnRoSXNsYW5kKzMsIGRhdGE9Q2xpbWF0ZSkNCmBgYA0KDQoNClRvIGdldCBhIHF1aWNrIG92ZXJ2aWV3IG9mIHRoZSBkYXRhIHdlIGNhbiB1c2UgdGhlIHBhaXJzKCkgY29tbWFuZCB0byBnZXQgYSBzY2F0dGVycGxvdCBtYXRyaXguIENsZWFybHkgdGhlIHZhcmlhYmxlcyBhcmUgcmVsYXRlZC4gDQoNCmBgYHtyIHBhaXJzIHBsb3R9DQpwYWlycyhDbGltYXRlICkNCmBgYA0KDQpUbyBmaXQgYSBtdWx0aXBsZSByZWdyZXNzaW9uDQogICQkRVtZX2ldID0gIFxiZXRhXzAgKyBcYmV0YV8xIHhfe2kxfSArIFxsZG90cyArIFxiZXRhX3AgeF97aXB9JCQNCnlvdSBzaW1wbHkgbWVudGlvbiBhbGwgdGhlIHZhcmlhYmxlcyBpbiB0aGUgbW9kZWwgc3RhdGVtZW50IG9mIGxtKCkuDQoNCmBgYHtyIEZpdCBhbGx9DQpDbGltYXRlLmxtPSAgbG0oTW5KbHlUZW1wIH4gTW5KYW5UZW1wICsgTGF0ICtMb25nICsgUmFpbiArIFN1biArIEhlaWdodCArIFNlYSsgTm9ydGhJc2xhbmQsIGRhdGE9Q2xpbWF0ZSkNCnN1bW1hcnkoQ2xpbWF0ZS5sbSkNCmBgYA0KDQpUaGUgICAkUl4yJCAgICBpcyBpbnRlcnByZXRlZCB0aGUgc2FtZSBhcyBiZWZvcmU6ICAgIDkwLjclIG9mIHRoZSB2YXJpYXRpb24gaW4gdGhlICpZKiB2YWx1ZXMgIChtZWFuIEp1bHkgdGVtcGVyYXR1cmVzKSAgIGlzIGV4cGxhaW5lZCBieSB0aGUgcHJlZGljdG9ycy4NCg0KVGhlIHJlc2lkdWFsIG1lYW4gc3F1YXJlICA9ICpTKiBpcyBzdGlsbCBpbnRlcnByZXRlZCBhcyB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSByZXNpZHVhbHMuICAgICAgIFRoYXQgaXMsICAgKlMqPSAwLjk2MjcgcmVwcmVzZW50cyAgdGhlICB0eXBpY2FsIHZhcmlhdGlvbiBvZiB0aGUgcmF3IGRhdGEgKGFjdHVhbCAqWSopIGFyb3VuZCB0aGUgZml0dGVkIHZhbHVlcy4gIFB1dHRpbmcgaXQgYW5vdGhlciB3YXksIHdlIGV4cGVjdCBhYm91dCAkOTVcJSQgb2YgZGF0YSB0byBiZSB3aXRoaW4gJDJTIFxhcHByb3ggMiQgZGVncmVlcyBvZiB0aGUgcHJlZGljdGVkIG1lYW4gSnVseSB0ZW1wZXJhdHVyZS4gDQoNClRoZSBpbmZvcm1hdGlvbiBhYm91dCByZXNpZHVhbHMgYXQgdGhlIHN0YXJ0IG9mIHRoZSBzdW1tYXJ5IG91dHB1dCBzaG93cyB0aGF0IHRoZSBtYXhpbXVtIHJlc2lkdWFsIGlzIDMuMywgIHNvIHRoYXQgbWVhbnMgdGhlcmUgaXMgYSBsb2NhdGlvbiB3aGljaCBpdCAzLjMgZGVncmVlcyB3YXJtZXIgaW4gSnVseSB0aGFuIHdvdWxkIGJlIHByZWRpY3RlZCBieSB0aGUgY292YXJpYXRlcy4gDQoNCk5vdyBpZiB3ZSBsb29rIGF0IHRoZSB0YWJsZSBvZiBjb2VmZmljaWVudHMgd2Ugc2VlIHRoYXQgb25seSB0aHJlZSB2YXJpYWJsZXMgaGF2ZSBzaWduaWZpY2FudCBQLXZhbHVlczogIGBMYXRgLCBgSGVpZ2h0YCBhbmQgYFNlYWAuIA0KVGhlIG90aGVyIHZhcmlhYmxlcyBhcmUgbm90IHNpZ25pZmljYW50LiAgU28gaXQgbG9va3MgYXMgdGhvdWdoIG9uZSBvciBtb3JlIG9mIHRoZW0gbmVlZCB0byBiZSByZW1vdmVkIGZyb20gdGhlIHJlZ3Jlc3Npb24uIA0KDQpgYGB7ciBjbGltYXRlIGxtIHBsb3RzfQ0KcGFyKG1mcm93PWMoMiwyKSkNCnBsb3QoQ2xpbWF0ZS5sbSkNCmBgYA0KDQpMb29raW5nIGF0IHRoZSByZXNpZHVhbCBwbG90cywgd2Ugc2VlIHRoYXQgcm93IG51bWJlciA5IChHaXNib3VybmUsIG9uIHRoZSBlYXN0IGNvYXN0IG9mIHRoZSBOb3J0aCBJc2xhbmQpIGlzIHRoZSAgbG9jYXRpb24gd2l0aCB0aGUgYmlnIHJlc2lkdWFsLiAgVGhlIGZpcnN0IHBsb3Qgc3VnZ2VzdHMgYSBoaW50IG9mIGN1cnZhdHVyZSwgYnV0IGl0IGlzIHdlYWsgYW5kIGl0IGlzIHRvbyBzb29uIHRvIHdvcnJ5IGFib3V0IHRoaXMgd2hlbiB3ZSBuZWVkIHRvIHJlbW92ZSBzb21lIGNvdmFyaWF0ZXMuICANCg0KZnJvbSB0aGUgbm9ybWFsIFEtUSBwbG90IHRoYXQsIGFwYXJ0IGZyb20gcm93IDksIHRoZSByZXNpZHVhbHMgYXJlIGZhaXJseSBub3JtYWwsIGJ1dCByb3cgOSBoYXMgYSBzdGFuZGFyZGl6ZWQgcmVzaWR1YWwkPjMkLg0KTWFueSBzdGF0aXN0aWNhbCBwcm9ncmFtcyBhdXRvbWF0aWNhbGx5IGZsYWcgcG9pbnRzIHdpdGggIHN0YW5kYXJkaXplZCByZXNpZHVhbCQ+MyQgYXMgb3V0bGllcnMuIA0KDQpUaGUgYm90dG9tIHJpZ2h0IGdyYXBoIHNob3dzIHRoYXQgcm93IDkgaXMgbmVhciB0aGUgYm91bmRhcnkgZm9yIENvb2tzJyBEID4gMC41LCBidXQgaXQgaXMgYWNjZXB0YWJsZSB0byBzYXkgdGhhdCBubyBsb2NhdGlvbnMgYXJlIGhhdmluZyBleHRyYW9yZGluYXJ5IGluZmx1ZW5jZSBvbiB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMuICAgT25lIGxvY2F0aW9uLCByb3cgMjgsIGlzIGhpZ2ggbGV2ZXJhZ2UsIHdoaWNoIG1lYW5zIGl0cyBjb3ZhcmlhdGUgdmFsdWVzIGFyZSB1bnVzdWFsIGNvbXBhcmVkIHRvIHRob3NlIG9mIG90aGVyIGxvY2F0aW9ucy4gDQoNCiMjICpBZGp1c3RlZCAkUl4yJCogIGFuZCAqRiogdGVzdA0KDQpXZSB3aWxsIGNvbnNpZGVyIHRoZSAqRiogdGVzdCBpbiBtb3JlIGRldGFpbCBsYXRlciwgYnV0IGZvciBub3cgeW91IHNob3VsZCBrbm93IHRoYXQgaXQgbG9va3MgdG8gc2VlIHdoZXRoZXIgdGhlcmUgaXMgYW55dGhpbmcgaW4gb3VyIG1vZGVsIHdoaWNoIGlzIHJlbGF0ZWQgdG8gdGhlIHJlc3BvbnNlICgqWSopIHZhcmlhYmxlLCB3aGV0aGVyIHRoYXQgaXMgYSBzaW5nbGUgdmFyaWFibGUgb3IgYSBncm91cCBvZiB2YXJpYWJsZXMuIA0KaS5lLiBpdCB0ZXN0cyB0aGUgaHlwb3RoZXNpcyB0aGF0IGFsbCB0aGUgc2xvcGVzIGFyZSB6ZXJvLiANCiQkSF8wOiAgXGJldGFfMT1cYmV0YV8yPS4uLj1cYmV0YV9wID0gMCQkDQpJZiAqRiogaXMgbGFyZ2UgIHdlIHJlamVjdCB0aGlzIGh5cG90aGVzaXMuICBGb3IgdGhlIG1vbWVudCB3ZSBzaGFsbCBqdXN0IGNvbnNpZGVyIHRoZSBQLXZhbHVlLiAgU2luY2UgJFA9IDUuMjU1XHRpbWVzIDEwXnstMTJ9JCB0aGlzIGlzIGFuIGV4Y2VlZGluZ2x5IHNtYWxsIG51bWJlciwgc28gd2UgcmVqZWN0ICRIXzAkIGFuZCBjb25jbHVkZSB0aGF0IGF0IGxlYXN0IG9uZSBvZiB0aGUgc2xvcGVzIGlzIG5vbi16ZXJvLiANCg0KVGhlICphZGp1c3RlZCAkUl4yJCogIGlzIG9ubHkgdXNlZCB0byBjb21wYXJlIHR3byBtb2RlbHMgd2l0aCBkaWZmZXJlbnQgbnVtYmVycyBvZiByZWdyZXNzaW9uIHBhcmFtZXRlcnMuICAgV2hlbiB3ZSBhZGQgYSB2YXJpYWJsZSB0byBhIHJlZ3Jlc3Npb24gbW9kZWwsIHRoZSBvcmRpbmFyeSBtdWx0aXBsZSAkUl4yJCB3aWxsIGdvIHVwLCBldmVuIGlmIHRoZSB2YXJpYWJsZSBhZGRlZCBpcyBydWJiaXNoLiAgIFRoZSBhZGp1c3RlZCAkUl4yJCBpcyBhbiBhbHRlcm5hdGl2ZSBmb3JtdWxhIHdoaWNoIHdpbGwgb25seSBnbyB1cCBpZiB0aGUgYWRkaXRpb25hbCBtb2RlbCBleHBsYWlucyBtb3JlIHRoYW4gYXQgbGVhc3Qgb25lICBleHRyYSByb3cgb2YgZGF0YS4gKFRoaXMgaXMgYSB2ZXJ5IHdlYWsgY3JpdGVyaW9uIGluZGVlZCkuICANCkNvbnZlcnNlbHkgaWYgd2UgZGVsZXRlIGEgdXNlbGVzcyB2YXJpYWJsZSwgdGhlIGFkanVzdGVkICRSXjIkIHNob3VsZCBnbyB1cCwgb3IgYXQgbGVhc3Qgbm90IGdvIGRvd24uDQoNCkp1c3QgZm9yIHRoZSBzYWtlIG9mIGlsbHVzdHJhdGlvbiwgc3VwcG9zZSB3ZSB3ZXJlIHRvIHJlbW92ZSB0aGUgdmFyaWFibGUgd2l0aCB0aGUgYmlnZ2VzdCBQLXZhbHVlIGluIHRoZSBwcmV2aW91cyByZWdyZXNzaW9uLCBuYW1lbHkgKk1uSmFuVGVtcCosIHdpdGggYSBQLXZhbHVlIG9mIDAuODk3LiANCg0KYGBge3IgZGVsZXRpbmcgdXNlbGVzc30NCkNsaW1hdGUubG0xID0gdXBkYXRlKENsaW1hdGUubG0sIC5+Li1NbkphblRlbXApDQpzdW1tYXJ5KENsaW1hdGUubG0xKQ0KYGBgDQoNClRoZSBhZGp1c3RlZCAkUl4yJCBoZXJlIGlzIDAuODg0Mi4gIEluIHRoZSBtb2RlbCB3aXRoIHRoZSBleHRyYSB2YXJpYWJsZSAqTW5KYW5UZW1wKiB0aGUgYWRqdXN0ZWQgJFJeMiQgaXMgMC44OC4gVGhhdCBpcywgaWYgd2Ugd2VyZSB0byBhZGQgdGhhdCB2YXJpYWJsZSBiYWNrICAgaW4gdG8gdGhlIG1vZGVsIGFnYWluIHRoZW4gdGhlIGFkanVzdGVkIA0KJFJeMiQgd291bGQgZ28gZG93bi4gDQpUaGlzIGlzIGNsZWFyIGV2aWRlbmNlIHRoYXQgd2UgZG8gbm90IG5lZWQgdGhpcyB2YXJpYWJsZSAgICpNbkphblRlbXAqLiANCg0KIyMgU2ltcGxpZnlpbmcgdGhlIHJlZ3Jlc3Npb24gbW9kZWwNCg0KQ2xlYXJseSBub3QgYWxsIHRoZSB2YXJpYWJsZXMgaW4gdGhlIOKAmGZ1bGzigJkgcmVncmVzc2lvbiBtb2RlbCBoYXZlIHNpZ25pZmljYW50ICBwLXZhbHVlcy4gICAgIChCeSDigJhmdWxs4oCZIG1vZGVsIEkgbWVhbiB3aXRoIGFsbCB0aGUgcHJlZGljdG9ycyAgWDEsLi4sWDggaW4gdGhlcmUpLiBXZSBuZWVkIHRvIGZpbmQgb3V0IHdoaWNoIHZhcmlhYmxlcyAgYXJlIGluIGZhY3QgbmVlZGVkLg0KDQogVGhlcmUgYXJlIHR3byBtYWluIGFwcHJvYWNoZXMgdG8gY2hvb3NpbmcgYSBtb2RlbCANCiANCi0gKkZvcndhcmQgc2VsZWN0aW9uKiwgd2hlcmUgd2Ugc3RhcnQgZnJvbSBhIGxpa2VseSBvbmUtdmFyaWFibGUgbW9kZWwgYW5kIGJ1aWxkIGl0IHVwIGJ5IGFkZGluZyBpbiBleHRyYSBwcmVkaWN0b3JzIG9uZSBhdCBhIHRpbWUuDQoNCi0gKkJhY2t3YXJkcyBlbGltaW5hdGlvbiosIHdoZXJlIHdlIHN0YXJ0IGZyb20gYSBmdWxsIG1vZGVsIGFuZCBkcm9wIG5vbi1zaWduaWZpY2FudCB2YXJpYWJsZXMgb3V0IG9uZSBhdCBhIHRpbWUuIFRoaXMgaXMgDQp1c3VhbGx5IHRoZSBiZXR0ZXIgbWV0aG9kKS4gIFdlIG1hZGUgYSBzdGFydCBhdCB0aGlzIGJ5IHJlbW92aW5nIE1uSmFuVGVtcCBmcm9tIHRoZSBiaWcgcmVncmVzc2lvbiBtb2RlbC4gICAgICAgLg0KDQpTaW5jZSB0aGUgcHJlZGljdG9ycyBhcmUgdXN1YWxseSBub3QgaW5kZXBlbmRlbnQgb2YgZWFjaCBvdGhlciwgaXQgaXMgaW1wb3J0YW50IHRvIG1ha2UgYW55IG1vZGVsIGNoYW5nZXMgb25lIHZhcmlhYmxlIGF0IGEgdGltZS4gKFdlIHdpbGwgc2VlIGEgcmVhc29uIGZvciB0aGlzIGxhdGVyIGluIHRoZSBjb3Vyc2UpLg0KDQpOb3RlIHdlIHdpbGwgc3R1ZHkgbW9kZWwgc2VsZWN0aW9uIHRlY2huaXF1ZXMgaW4gbW9yZSBkZXRhaWwgbGF0ZXIgb24sIHdoZXJlIHNvbWUgYXV0b21hdGVkIHByb2NlZHVyZXMgYXJlIGludHJvZHVjZWQsIGluY2x1ZGluZyBhIGh5YnJpZCBmb3J3YXJkcy9iYWNrd2FyZHMgbWV0aG9kIGNhbGxlZCAnc3RlcHdpc2UgbW9kZWwgc2VsZWN0aW9u4oCZLg0KDQpCZWZvcmUgd2UgZ2V0IHRvIHRoYXQgcG9pbnQsIHdlIHdpbGwgdGFrZSB0aGUgdGFzayBzbG93bHkgYmVjYXVzZSB3ZSBhcmUgYWltaW5nIHRvIHVuZGVyc3RhbmQgd2hhdCBpcyBnb2luZyBvbi4gICANCg0KV2Ugc3RhcnQgYnkgdHJ5aW5nIHRvICB1c2UgZm9yd2FyZCBzZWxlY3Rpb24sIGJlZ2lubmluZyB3aXRoIHRoZSBtb3N0IGhpZ2hseSBjb3JyZWxhdGVkIHNpbmdsZSB2YXJpYWJsZS4gDQoNCmBgYHtyIGNvcnJlbGF0aW9uc30NCnJvdW5kKGNvcihDbGltYXRlKSwgMykNCmBgYA0KDQpMb29raW5nIGF0IHRoZSBjb2x1bW4gZm9yICpNbkpseVRlbXAqICB3ZSBzZWUgdGhhdCB0aGUgdmFyaWFibGVzIHdpdGggYmlnZ2VzdCBjb3JyZWxhdGlvbiBhcmUgKkxhdCogIChjb3JyZWxhdGlvbiA9IC0wLjc2MykNCiBhbmQgKk1uSmFuVGVtcCogKGNvcnJlbGF0aW9uID0gMC43NDMpLiAgTG9va2luZyBhdCB0aGUgY29ycmVsYXRpb25zIHdvdWxkIGdpdmUgdXMgdGhlIHNhbWUgcC12YWx1ZSBhcyByZWdyZXNzaW5nIHRoZSBwcmVkaWN0b3IgYnkgaXRzZWxmLg0KIA0KU28gd2h5IGRpZCB0aGUgcHJldmlvdXMgYXJndW1lbnQgc2F5IHRoYXQgKk1uSmFuVGVtcCogd2FzIHVzZWxlc3MgYnV0IG5vdyB0aGUgY29ycmVsYXRpb25zIHNheSBpdCBpcyBoaWdobHkgcmVsYXRlZCB0byAqeSo/IA0KDQpUaGUgYW5zd2VyIGlzIHRoYXQgdmlydHVhbGx5IGFsbCBvZiB0aGUgaW5mb3JtYXRpb24gdGhhdCAqTW5KYW5UZW1wKiB3b3VsZCBicmluZyB0byB0aGUgcmVncmVzc2lvbiwgaXMgYWxyZWFkeSAgZXhwcmVzc2VkIGluIHRoZSBvdGhlciBjb3ZhcmlhdGVzLiBTbyBieSBpdHNlbGYgaXQgaXMgaW5mb3JtYXRpdmUsIGJ1dCBhZnRlciBldmVyeXRoaW5nIGVsc2UsICpNbkphblRlbXAqIGhhcyBub3RoaW5nIG1vcmUgdG8gdGVsbCB1cyBhYm91dCAgKnkgPSAgIE1uSmx5VGVtcCouDQoNCg0KIyMjIFNpbmdsZS1WYXJpYWJsZSBNb2RlbA0KIA0KIA0KIFdlIHdpbGwgc3RhcnQgYnkgcmVncmVzc2luZyAqTW5KbHlUZW1wKiBvbiAqTGF0Ki4NCg0KYGBge3IgU2luZ2xlIFZhcmlhYmxlfQ0KIENsaW1hdGUubG0xID1sbShNbkpseVRlbXAgfkxhdCwgZGF0YT1DbGltYXRlKQ0KIHN1bW1hcnkoQ2xpbWF0ZS5sbTEpDQpgYGANCg0KIENsZWFybHkgYExhdGAgaXMgc2lnbmlmaWNhbnQuICANCiANCiBDYW4geW91IGludGVycHJldCB0aGUgc2xvcGU/IA0KIA0KIENhbiB5b3UgaW50ZXJwcmV0IHRoZSBpbnRlcmNlcHQgKGlzIHRoaXMgcmVhbGlzdGljKT8gSGludDogVGhpbmsgYSBiaXQgYWJvdXQgTloncyBsb2NhdGlvbi4NCiANCiANCiMjIyBUcnlpbmcgYSB0d28tdmFyaWFibGUgbW9kZWwNCg0KV2Ugd2lsbCBsb29rIHRvIHNlZSB3aGF0IGhhcHBlbnMgaWYgd2UgYWRkIGluIGBNbkphblRlbXBgIG9yICAgICpIZWlnaHQqICAgIG9uIHRvcCBvZiB0aGUgZXhpc3RpbmcgdmFyaWFibGUgKkxhdCouDQogDQpgYGB7ciBUd28gdmFyaWFibGVzfQ0KIENsaW1hdGUubG0yID0gbG0oTW5KbHlUZW1wIH4gTGF0ICsgTW5KYW5UZW1wICwgZGF0YT1DbGltYXRlKQ0KIHN1bW1hcnkoQ2xpbWF0ZS5sbTIpDQogQ2xpbWF0ZS5sbTMgPSBsbShNbkpseVRlbXAgfiBMYXQgKyBIZWlnaHQsIGRhdGE9Q2xpbWF0ZSkNCiBzdW1tYXJ5KENsaW1hdGUubG0zKQ0KIHBhcihtZnJvdz0gYygyLDIpKQ0KIHBsb3QoQ2xpbWF0ZS5sbTMpDQpgYGANCiANCkluIHRoZSBmaXJzdCBhbmFseXNpcywgYE1uSmFuVGVtcGAgaGFkIGEgbm9uLXNpZ25pZmljYW50IFAtdmFsdWUsIHNvIGFnYWluIGluZGljYXRpbmcgdGhhdCBpdCBpcyBub3QgdXNlZnVsLiBIb3dldmVyIHRoZSAqYWRqdXN0ZWQqICAkUl4yJCBkb2VzIGdvIHVwIHNvIHdlIHdvdWxkIG5vdCBiZSBhYmxlIHRvIGRpc21pc3MgaXQgZW50aXJlbHkuIA0KDQpJbiB0aGUgc2Vjb25kIGFuYWx5c2lzIGBIZWlnaHRgIGlzIHZlcnkgc2lnbmlmaWNhbnQgYW5kIHRoZSBhZGp1c3RlZCAkUl4yJCBoYXMgZ29uZSB1cCBhIHZlcnkgYmlnIGFtb3VudCwgc28gaXQgaXMgcmlnaHQgdG8gaW5jbHVkZSBgSGVpZ2h0YCBpbiB0aGUgcmVncmVzc2lvbiBtb2RlbC4gDQoNCkhvd2V2ZXIgdGhlIGRpYWdub3N0aWMgcGxvdHMgZG8gbm90IHNob3cgbXVjaCBkaWZmZXJlbmNlIHRvIHRoZSBwbG90cyBmb3IgdGhlIGZ1bGwgbW9kZWwuDQoNCiMjIE9uIHJlZ3Jlc3Npb24gZGlhZ25vc3RpY3MgZm9yIG11bHRpcGxlIHJlZ3Jlc3Npb24NCg0KV2hlbiB3ZSBoYWQganVzdCBvbmUgcHJlZGljdG9yIG9uIG9mZmVyLCB3ZSBzaG91bGQgaGF2ZSBjaGVja2VkIGZvciBub25saW5lYXJpdHkgIGluIHRoZSByZWxhdGlvbnNoaXAuIA0KDQogSXQgaXMgcmVjb21tZW5kZWQgYmVmb3JlIHRoZSBtdWx0aXBsZSByZWdyZXNzaW9uIHRvIHBsb3QgICpZKiAgdnMgZWFjaCBleHBsYW5hdG9yeSB2YXJpYWJsZSAkeF8xLCAuLi4sIHhfcCQgICAgaW4gdHVybiB0byBjaGVjayBmb3IgY3VydmF0dXJlLg0KDQpCdXQgIHNvbWV0aW1lcyBwcm9ibGVtcyBvbmx5IHNob3cgdXAgb25jZSBvbmUgaGFzIGFkanVzdGVkIA0KZm9yIG90aGVyIHZhcmlhYmxlcy4gU28sICBhZnRlciBmaXR0aW5nIHRoZSBtb2RlbCAgcGxvdCByZXNpZHVhbHMgJGVfaSQgYWdhaW5zdCBlYWNoICR4X2kkLCBvciB1c2Ugc3RhbmRhcmRpemVkIA0KcmVzaWR1YWxzLiAgKipCZSBib3JpbmdseSBzeXN0ZW1hdGljLiAqKiAgICANCg0KQW5kIGl04oCZcyBhIGdvb2QgaWRlYSB0byB1c2UgYSBsb3dlc3Mgc21vb3RoZXIgdG8gY2hlY2sgZm9yIHBvc3NpYmxlIGN1cnZhdHVyZS4NCg0KYGBge3IgY3VydmF0dXJlSGVpZ2h0fQ0KbGlicmFyeShnZ3Bsb3QyKQ0KQ2xpbWF0ZSB8PiBnZ3Bsb3QoYWVzKHk9cmVzaWR1YWxzKENsaW1hdGUubG0zKSAsIHg9SGVpZ2h0KSkgKyBnZW9tX3BvaW50KCkgK2dlb21fc21vb3RoKCkNCmBgYA0KDQpUaGlzIGdyYXBoIHNob3dzIHBvc3NpYmxlIGN1cnZhdHVyZSB3aXRoIGBoZWlnaHRgIG9uY2UgbGF0aXR1ZGUgaXMgYWxsb3dlZCBmb3I6IHBlcmhhcHMgYSB0cmFuc2Zvcm1hdGlvbiBtaWdodCBnaXZlIGEgYmV0dGVyIG1vZGVsPyAgU29tZXRoaW5nIHRvIHRoaW5rIGFib3V0IGxhdGVyLiANCg0KSXQgaXMgYmV0dGVyIHRvIGJlIGFsZXJ0ZWQgdG8gYSBwb3NzaWJsZSBwcm9ibGVtIGFuZCBiZSBhYmxlIHRvIGRpc21pc3MgaXQsIHRoYW4gdG8gYmUgdW5hd2FyZSBvZiB0aGUgcG9zc2libGUgcHJvYmxlbSBhdCBhbGwuICAgDQogDQojIyMgT3V0bGllcnMNCg0KSnVzdCBhcyBmb3IgdGhlIHNpbXBsZSBtb2RlbCwgICByZXNpZHVhbHMgIGFyZSBkZWZpbmVkIGFzICAgJGVfaSA9IHlfaSDigJMgXGhhdCB5X2kkIC4gIA0KQW5kIGFnYWluLCBqdXN0IGFzIGZvciB0aGUgc2ltcGxlIG1vZGVsLCAgdGhlIHN0YW5kYXJkIGVycm9ycyBmb3IgdGhlIHJlc2lkdWFscyBkZXBlbmRzIG9uIGhvdyB1bnVzdWFsIHRoZSBjb3JyZXNwb25kaW5nICAqeCogdmFsdWVzIGFyZS4gICAgICAgICBUaGlzIGNvbXBsaWNhdGVzIHRoZSBpbnRlcnByZXRhdGlvbiBvZiB0aGUgZ3JhcGhzLCBzbyB3ZSBjYW4gcmVtb3ZlIHRoaXMgY29tcGxpY2F0aW9uIGJ5IGxvb2tpbmcgYXQgc3RhbmRhcmRpemVkIHJlc2lkdWFscyAgICAgICB3aGljaCBhcmUgc2NhbGVkIHRvIGdpdmUgZXF1YWwgY2hhbmNlIG9mIHNwcmVhZCBhdCBldmVyeSAqeCogdmFsdWUuICAgICAgIFN0YW5kYXJkaXplZCByZXNpZHVhbHMgc2hvdWxkIHVzdWFsbHkgbGllIHdpdGhpbiB0aGUgcmFuZ2UgJFxwbSAyJCwgICBhbmQgdmVyeSBzZWxkb20gJDwtMyQgb3IgJD4gMyQNCg0KQWxzbyBhcyBiZWZvcmUsICB3ZSBjYW4gIGNoZWNrIGZvciB3aGV0aGVyIGEgcGFydGljdWxhciBwb2ludCBpcyBkcmFnZ2luZyB0aGUgbGluZSB0b3dhcmRzIGl0c2VsZiAoYW5kIHRodXMgbm90IGFwcGVhcmluZyBhcyB1bnVzdWFsIGFzIGl0IHNob3VsZCkgIGJ5IGNvbXB1dGluZyBleHRlcm5hbGx5IHN0dWRlbnRpemVkIGRlbGV0ZWQgcmVzaWR1YWxzLg0KDQpJdCBpcyBvZnRlbiBoZWxwZnVsIHRvIHBsb3QgdGhlIHN0YW5kYXJkaXplZCBhbmQgZGVsZXRlZCByZXNpZHVhbHMgb24gdGhlIHNhbWUgZ3JhcGgsIGFzIHRoYXQgaGVscHMgc2hvdyB3aGV0aGVyIHRoZSBkZWxldGlvbiBpcyBtYWtpbmcgYSBiaWcgZGlmZmVyZW5jZS4gDQoNCmBgYHtyIHN0YW5kYXJkaXplZFJlc2lkdWFsc0FuZFN0dWRlbnRpemVkRGVsZXRlZFJlc2lkdWFsc30NCnBsb3QocnN0dWRlbnQoQ2xpbWF0ZS5sbTMpIH4gQ2xpbWF0ZS5sbTMkZml0dGVkLnZhbHVlcyxjb2w9MiwgcGNoPScrJywgbWFpbj0nc3RhbmRhcmRpemVkKG8pIGFuZCBzdHVkZW50aXplZCgrKSByZXNpZHVhbHMgdnMgZml0dGVkIHZhbHVlcycpDQpwb2ludHMocnN0YW5kYXJkKENsaW1hdGUubG0zKSB+IENsaW1hdGUubG0zJGZpdHRlZC52YWx1ZXMpDQpgYGANCg0KSXQgaXMgY2xlYXIgdGhhdCBkZWxldGluZyBHaXNib3JuZSB3b3VsZCBtYWtlIGEgYmlnIGRpZmZlcmVuY2UgdG8gdGhlICByZWdyZXNzaW9uLCBzbyB3ZSBzaG91bGQgY2hlY2sgdG8gbWFrZSBzdXJlIHRoZSBudW1iZXIgaXMgY29ycmVjdC4gSG93ZXZlciBkZWxldGluZyBhbnkgb3RoZXIgZGF0YSBwb2ludHMgd291bGQgbWFrZSB2ZXJ5IGxpdHRsZSBkaWZmZXJlbmNlLg0KDQojIyBDaGVja2luZyBsZXZlcmFnZQ0KDQpUaGUgIGxldmVyYWdlIHZhbHVlcyAkaF97aWl9JCBjYW4gYmUgb2J0YWluZWQgdXNpbmcgdGhlIGBoYXR2YWx1ZXMoKWAgZnVuY3Rpb24sICBhbmQgcGxvdHRlZCBhZ2FpbnN0IGFueSB2YXJpYWJsZXMgb2YgaW50ZXJlc3QuDQoNClRoZSBhdmVyYWdlIHZhbHVlIG9mICRoX3tpaX0kIGlzICRwL24kLiAgIA0KUG9pbnRzIHdpdGggJGhfe2lpfSA+IDMgcC9uJCBhcmUgcmVnYXJkZWQgYXMgcG9pbnRzIHdvcnRoIGNoZWNraW5nLiANCg0KRm9yIHRoZSBgQ2xpbWF0ZS5sbTNgIG1vZGVsICpwPTMqIChpbnRlcmNlcHQrIDIgIHNsb3BlcykgYW5kICpuPTM2Ki4NCg0KYGBge3IgbGV2ZXJhZ2V9DQpwbG90KCBoYXR2YWx1ZXMoQ2xpbWF0ZS5sbTMpIH4gTGF0LCBkYXRhPUNsaW1hdGUpDQphYmxpbmUoaD0gMypsZW5ndGgoY29lZihDbGltYXRlLmxtMykpL25yb3coQ2xpbWF0ZSkpDQpwbG90KCBoYXR2YWx1ZXMoQ2xpbWF0ZS5sbTMpIH4gSGVpZ2h0LCBkYXRhPUNsaW1hdGUpDQphYmxpbmUoaD0gMypsZW5ndGgoY29lZihDbGltYXRlLmxtMykpL25yb3coQ2xpbWF0ZSkpDQpgYGANCg0KVGhlIGxldmVyYWdlIGlzIHJlbGF0ZWQgdG8gJChcZnJhY3t4LVxiYXIgeH17c2QoeCl9KV4yJCAgY29tYmluZWQgYWNyb3NzIGVhY2ggcHJlZGljdG9yIGluIHRoZSB0aGUgbW9kZWwuIFNvIHRoZSBncmFwaCBoYXMgYSBjaGFyYWN0ZXJpc3RpYyBxdWFkcmF0aWMgc2hhcGUgZm9yIG1vc3Qgb2YgdGhlIHBvaW50cy4gDQoNClRoZSBmaXJzdCBncmFwaCBpbmRpY2F0ZXMgdGhhdCBoaWdoIGxldmVyYWdlIGlzIG5vdCByZWxhdGVkIHRvIGBMYXRgLiBUaGUgc2Vjb25kIGdyYXBoIGluZGljYXRlcyBpdCBpcyBleHBsYWluZWQgYnkgZXh0cmVtZSB2YWx1ZXMgZm9yIGBIZWlnaHRgLg0KDQpPY2Nhc2lvbmFsbHkgeW91IGdldCBhIHBvaW50IHdoaWNoIGlzIGhpZ2ggbGV2ZXJhZ2UgYWx0aG91Z2ggaXQgaXMgbm90IGV4dHJlbWUgaW4gKmVpdGhlciB2YXJpYWJsZSouICBJbiB0aGF0IGNhc2UgaXQgaXMgYmVjYXVzZSB0aGUgKmNvbWJpbmF0aW9uKiBvZiB0aGUgcHJlZGljdG9yIHZhcmlhYmxlcyBpcyB1bnVzdWFsLiANCg0KIyMgSW50ZXJwcmV0aW5nIFNsb3BlcyBmcm9tIGEgIE11bHRpcGxlIFJlZ3Jlc3Npb24NCg0KU3VwcG9zZSB3ZSBjb21wYXJlIHRoZSBjb2VmZmljaWVudHMgZnJvbSANCg0KIChBKSB0aGUgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG9mIE1uSmx5VGVtcCBvbiBMYXQsIHRvIHRob3NlIHByb2R1Y2VkIGJ5DQogKEIpICB0aGUgbXVsdGlwbGUgcmVncmVzc2lvbiBvZiBNbkpseVRlbXAgb24gTGF0ICBhbmQgSGVpZ2h0LiAgDQoNCmBgYHtyIGNvZWZzfQ0KY29lZihDbGltYXRlLmxtMSkNCmNvZWYoQ2xpbWF0ZS5sbTMpDQpgYGANCg0KV2Ugc2VlIHRoZSBzbG9wZSBhc3NvY2lhdGVkIHdpdGggYExhdGAgaXMgYSBiaXQgZGlmZmVyZW50LiBXaHk/DQoNClRoZSBhbnN3ZXIgaXMgdGhhdCAgYExhdGAgYW5kIGBIZWlnaHRgIGFyZSBzbGlnaHRseSBjb3JyZWxhdGVkDQpzbyB0aGUgcHJlc2VuY2Ugb2YgYEhlaWdodGAgaW4gdGhlIG1vZGVsIG1lYW5zIGBMYXRgIGRvZXMgbm90IG5lZWQgdG8gZXhwbGFpbiBgTW5KbHlUZW1wYCBhbGwgYnkgaXRzZWxmLiAgU2luY2UgdGhlcmUgYXJlIG1vcmUgdmVyeSBoaWdoIHBsYWNlcyBpbiB0aGUgU291dGggSXNsYW5kIHRoYW4gaW4gdGhlIE5vcnRoIElzbGFuZCwgc29tZSBvZiB0aGUgdHJlbmQgd2l0aCBgTGF0YCBpcyB0YWtlbiBhd2F5IGJ5IGluY2x1ZGluZyBgSGVpZ2h0YC4gDQoNClNvIHdlIHNldHRsZSBmb3IgdGhlIGZvbGxvd2luZyBpbnRlcnByZXRhdGlvbjoNCg0KSW4gYSBjb21iaW5lZCByZWdyZXNzaW9uICAgbW9kZWwNCiQkWSA9IFxiZXRhXzAgKyBcYmV0YV8xIHhfMSArIFxiZXRhXzIgeF8yJCQNCg0KIHdlIGludGVycHJldCAkXGJldGFfMSQNCnRoZSByYXRlIG9mIGNoYW5nZSBvZiAqeSoNCnBlciB1bml0IGluY3JlYXNlIG9mICR4XzEkICAgKmlmICR4XzIkIGlzIGhlbGQgY29uc3RhbnQqLg0KDQpTaW1pbGFybHkgIHdlIGludGVycHJldCAkXGJldGFfMiQNCnRoZSByYXRlIG9mIGNoYW5nZSBvZiAqeSoNCnBlciB1bml0IGluY3JlYXNlIG9mICR4XzIkICAgKmlmICR4XzEkIGlzIGhlbGQgY29uc3RhbnQqLg0KDQoNCiANCldlIHdpbGwgc2VlIGluIGxhdGVyIGxlY3R1cmVzIHRoYXQgaWYgYWRkaW5nIGEgbmV3IHZhcmlhYmxlIHN1Y2ggYXMgJHhfMiQgaW50byBhIG1vZGVsIG1ha2VzIGEgYmlnIGRpZmZlcmVuY2UgdG8gdGhlIHNsb3BlIGVzdGltYXRlcywgdGhlbiBpdCBtYXkgYmUgYSBnb29kIGlkZWEgdG8gIGluY2x1ZGUNCml0LCBldmVuIGlmIGl0cyBQLXZhbHVlIGRpZCAgbm90IHF1aXRlIG1lZXQgdGhlIFA8MC4wNSBydWxlLiAgICAgDQoNCg0KIyMgQ29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIGNvZWZmaWNpZW50cyBhbmQgIFByZWRpY3Rpb24gaW50ZXJ2YWxzDQoNClRoZXNlIGFyZSBjYWxjdWxhdGVkIHRoZSBzYW1lIHdheSBmb3IgYSBtdWx0aXBsZSByZWdyZXNzaW9uIGFzIHdhcyBkb25lIGZvciBhIHNpbXBsZSBsaW5lYXIgbW9kZWwuIA0KDQpOb3RlICB0aGF0IGluIG9yZGVyIHRvIG1ha2UgcG9pbnQgb3IgaW50ZXJ2YWwgZXN0aW1hdGVzLCB5b3UgbmVlZCB0byBzdGF0ZSB3aGljaCB2YWx1ZXMgYXJlIGFzc29jaWF0ZWQgd2l0aCB3aGljaCB2YXJpYWJsZXMgYW5kIHB1dCB0aGVtIGluIGEgYGRhdGEuZnJhbWUoKWAuDQoNCmBgYHtyIGluZmVyZW5jZX0NCmNvbmZpbnQoQ2xpbWF0ZS5sbTMpDQoNCngwIDwtIGRhdGEuZnJhbWUoTGF0PTQwLjM1MjUsIEhlaWdodD0gMjApDQojIFByZWRpY3RpbmcgTWVhbiBKdWx5IFRlbXBlcmF0dXJlIGZvciBQYWxtZXJzdG9uIE5vcnRoDQoNCnByZWRpY3QoIENsaW1hdGUubG0zLCAgbmV3ZGF0YT14MCwgaW50ZXJ2YWw9InByZWRpY3Rpb24iLCBsZXZlbD0wLjk1KQ0KYGBgDQoNCg0K