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")
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.
For the Scottish Hill Racing data, the trend in the lurking
variable plot of residuals against climb
indicates that the
height climbed should be included in the model in addition to the race
distance.
For applications such as this we need to extend simple linear
regression to incorporate multiple predictors.
This extended methodology is called multiple linear
regression.
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:
There are p predictor variables x1,
x2, …, xp.
The notation xij denotes the value of
xj for the ith
individual.
\(\varepsilon_1, \ldots
\varepsilon_n\) are random errors satisfying assumptions A1 —
A4.
\(\beta_0, \beta_1, \ldots,
\beta_p\) are the regression parameters.
Parameter Estimation
The equation for the regression line and A1 imply that \[E[Y_i] = \mu_i = \beta_0 + \beta_1 x_{i1} +
\ldots + \beta_p x_{ip}\]
The parameters \(\beta_0,\beta_1,
\ldots, \beta_p\) can be estimated by the method of least
squares.
The least squares estimates (LSEs) \(\hat \beta_0,\hat \beta_1, \ldots ,\hat
\beta_p\) are still the values that minimize the following sum of
squares: \[\begin{aligned}
SS(\beta_0, \beta_1, \ldots , \beta_p) &=& \sum_{i=1}^n (y_i -
\mu_i)^2\\
&=& \sum_{i=1}^n (y_i - \beta_0 - \beta_1 x_{i1} -
\ldots - \beta_p x_{ip} )^2\end{aligned}\]
Software packages can quickly compute LSEs.
Fitted Values and Residuals
The definitions of a fitted value and a residual in multiple
linear regression follow on naturally from simple linear
regression.
The ith fitted value is \[\hat \mu_i = \hat \beta_0 + \hat \beta_1 x_{i1} +
\ldots + \hat\beta_p x_{ip}\]
The ith residual is \(e_i = y_i - \hat \mu_i\).
The error variance, \(\sigma^2\), can be estimated (unbiasedly)
by \[s^2 = \frac{1}{n-p-1} RSS\] where
the residual sum of squares is \(RSS =
\sum_{i=1}^n (y_i - \hat \mu_i)^2\).
New Zealand Climate Data Example
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)
To get a quick overview of the data we can use the pairs() command to
get a scatterplot matrix. Clearly the variables are related.
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)
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
Forward selection, where we start from a likely
one-variable model and build it up by adding in extra predictors one at
a time.
Backwards elimination, where we start from a full model
and drop non-significant variables out one at a time. This is usually
the better method). We made a start at this by removing MnJanTemp from
the big regression 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.
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)
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'
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)
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))
plot(hatvalues(Climate.lm3) ~ Height, data = Climate)
abline(h = 3 * length(coef(Climate.lm3))/nrow(Climate))
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
- the simple linear regression of MnJlyTemp on Lat, to those produced
by
- the multiple regression of MnJlyTemp on Lat and Height.
(Intercept) Lat
34.4212725 -0.6618663
(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()
.
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