View the
latest recording of this lecture
We have now considered two types of testing problems in multiple
linear regression:
- Testing whether the response is related to at least one explanatory
variable;
- Testing the effect of a given (single) explanatory variable having
adjusted for other variables.
In this lecture, we investigate the importance of a group of
covariates simultaneously.
- This problem is equivalent to comparing a given model with a
simplified version of that model.
To help our discussion later we may call the simplified model an
“accepted model”, that is, with a set of variables that we think accept
as being useful. The bigger model (with more variables) we may call an
“extended model”, and we may refer to the additional variables as
“candidate” variables.
Matrix formulation of the model
In a linear regression the predictor columns \(X_1, X_2, …,X_p\) are treated as being
linked together into a specific arrangement called a data matrix
X (sometimes also called the model matrix or Design
matrix, usually depending on the amount of pre-planning there is in the
data collection.
The columns don’t have to be next to each other in the worksheet: all
you have to do is specify the column names and the software sets up the
matrix for its own calculations.
If there is an intercept in the model, then the first column of the
data matrix is assumed to be a column of 1s. This is the default. You
don’t need to create this column: the software creates it automatically
and uses it in its internal calculations – unless you specifically tell
the software there is no intercept.
Note that since every entry on every row of this hidden column is the
same number, then it is also referred to as the
Constant column.
Next to the column of 1s, the software places into the data matrix
the first column specified in the list of regression predictors. So in
the example below, columns \(X_1\)=MnJanTemp, …,\(X_8\)=NorthIsland are shown. (This makes 9
columns in all).
If only predictors \(X_2\)=Lat,
\(X_6\)=Height and \(X_1\)=MnJanTemp were included in the model
(in that order), the data matrix would have the column of 1s, the column
of \(X_2\) values, the column of \(X_6\) values, and finally the column of
\(X_1\) values, in that order.
The regression parameters \(\beta_0,
\beta_1, ….,\beta_8\) are similarly treated by the software as
being arranged in an orderly fashion, in a matrix consisting of a single
column of numbers. (A matrix with one column is called a (column)
vector).
The Y values are similarly treated as a vector (one-column
matrix of numbers in their particular order). We denote them by just the
column name Y
.
With the data organised this way in the computer, the mathematics
takes over. Mathematically, the linear regression model can be written
using Matrix Algebra as \[Y = X \beta +
\varepsilon\]
where \(\boldsymbol{\varepsilon}\)
is a vector of errors.
Now as before, our task is to find the estimates of the intercept and
slopes \(\beta_0, \beta_1, …,\beta_p\)
to make the sum of squared errors as small as possible. (i.e. to
minimise \(\varepsilon_1^2 +\varepsilon_2^2+
...+\varepsilon_n^2\))
The extraordinarily nice thing about writing the problem in matrix
terms is that some matrix algebra gives an immediate solution that is
easily programmed.
\[ \boldsymbol{b} =(X^T X)^{-1} X^T
\boldsymbol{y} \]
This holds for all sizes of problem, in terms of both the number of
rows (observations) and columns (variables) in the X model
matrix.
Remember that proviso (condition for validity of the solution): The
columns of X must be linearly independent, or more accurately,
not have any linear dependencies.
A note on the intercept
The other point worth noting about the formula \[\boldsymbol{b} =(X^TX)^{-1} X^T
\boldsymbol{y} \] is that the intercept is not treated any
differently to any other predictor variable - it is just represented by
a column of numbers in the data matrix X (in this case
a column with every number=1).
The significance of this fact is that exactly the same rules and
formulas apply to estimating the intercept \(\beta_0\) as apply to estimating any other
slope \(\beta_1,...,\beta_p\). So if
you read a regression textbook or a paper that gives a formula based on
the slopes, they won’t necessarily give a separate formula for the
intercept.
In what follows, we will occasionally talk about the design matrix
(or data matrix) if it is helpful for understanding the way the
regression models have been created.
Nested Models
A linear model M1 is said to be nested
within another model M2 if M1 can be recovered as a
special case of M1 by setting the parameters of M2 to
the necessary values.
For the Climate data, define model M2 by
\[E[\mbox{MnJlyTemp}] = \beta_0 + \beta_1
\mbox{Lat} + \beta_2 \mbox{Height} + \beta_3 \mbox{Sea} +
\beta_4 \mbox{NorthIsland} + \beta_5 \mbox{Rain}+ \beta_6
\mbox{Sun}+ \beta_7\mbox{Long}+ \beta_8 \mbox{MnJanTemp}\]
Then the model M1 defined by
\[E[\mbox{MnJlyTemp}] = \beta_0 + \beta_1
\mbox{Lat} + \beta_2 \mbox{Height} + \beta_3 \mbox{Sea} + \beta_4
\mbox{NorthIsland} \] is nested within M2 because we can
obtain M1 by setting \(\beta_5 =
\beta_6 = \beta_7 =\beta_8 =0\) in M2.
F Tests for Nested Models
The general ideas about model comparison continue to apply.
- We want a “cheap” model (i.e. one with few parameters);
- We want a model that fits well (i.e. one with small RSS.
- The choice of model will be a question of whether the improvement in
goodness of fit of the extended model M2 over the simpler model
M1 is worth the cost.
Selecting a model is equivalent to testing hypotheses about
parameters of M2 (the more complex model).
- Suppose that linear model M1 has q explanatory
variables (hence q+1 regression parameters, including
intercept).
- Suppose that M1 is nested within linear model M2
which has p > q explanatory variables (hence p+1
regression parameters, including intercept).
Comparison of the models can be achieved by testing
H0: \(\beta_j =
0\) for all \(j \in J\)
(i.e. M1 adequate); versus
H1: \(\beta_j\)
not zero for all \(j \in J\)
(i.e. M2 better)
where J indexes the p-q variables that appear in
M2 but not M1.
The F-statistic to test these hypotheses is
\[F = \frac{[RSS_{M1} -
RSS_{M2}]/(p-q)}{RSS_{M2}/(n-p-1)}\]
As before, extreme, large values of F provide evidence
against H0 (hence evidence that we should prefer
model M2 to M1).
If H0 is correct then F has an
F distribution with (p-q),(n-p-1) degrees of
freedom.
Hence if fobs is the observed value of the test
statistic, and X is a random variable from an
Fp-q,n-p-1 distribution, then the P-value
is given by
\[P= P(X \ge f_{obs})\]
Interpretation of Test Results
As we have seen earlier, the importance of variables in a
multiple regression is dependent upon context (i.e. what other variables
are taken into account).
Retention of H0 (i.e. acceptance that model
M1 is adequate) does not mean that the p-q variables
indexed by J are unrelated to the response.
Rather, retention of H0 indicates that the
variables indexed by J do not provide additional information
about the response having adjusted for all the other (q)
variables.
Model Comparison for the Climate data
Our M2 and M1 were given above.
N.B.
Download Climate.csv
to replicate the following examples.
Climate.lm1 <- lm(MnJlyTemp ~ Lat + Height + Sea + NorthIsland, data = Climate)
anova(Climate.lm1)
Analysis of Variance Table
Response: MnJlyTemp
Df Sum Sq Mean Sq F value Pr(>F)
Lat 1 157.428 157.428 173.4878 3.068e-14 ***
Height 1 71.463 71.463 78.7533 5.122e-10 ***
Sea 1 8.048 8.048 8.8688 0.005591 **
NorthIsland 1 5.260 5.260 5.7971 0.022193 *
Residuals 31 28.130 0.907
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Climate.lm2 <- lm(MnJlyTemp ~ Lat + Height + Sea + NorthIsland + Rain + Sun + Long +
MnJanTemp, data = Climate)
anova(Climate.lm2)
Analysis of Variance Table
Response: MnJlyTemp
Df Sum Sq Mean Sq F value Pr(>F)
Lat 1 157.428 157.428 169.8702 3.664e-13 ***
Height 1 71.463 71.463 77.1111 2.136e-09 ***
Sea 1 8.048 8.048 8.6838 0.006543 **
NorthIsland 1 5.260 5.260 5.6762 0.024500 *
Rain 1 2.343 2.343 2.5280 0.123481
Sun 1 0.145 0.145 0.1563 0.695687
Long 1 0.604 0.604 0.6521 0.426432
MnJanTemp 1 0.016 0.016 0.0172 0.896762
Residuals 27 25.022 0.927
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
- For model
Climate.lm1
, calculations give
RSSM1 = 28.13 and q=4.
- For model
Climate.lm2
, calculations give
RSSM2 = 25.022 and p=8.
\[F = \frac{[RSS_{M1} -
RSS_{M2}]/(p-q)}{RSS_{M2}/(n-p-1)} = \frac{[28.13 -
25.022]/(8-4)}{25.022/(27)} = 0.8384\]
\[P(X \ge 0.8384) =
0.5129~~~~~~~~~~\mbox{where $X \sim F_{4,27}$}\]
- We conclude that H0 can be retained (i.e. model
M1 is adequate). There is no evidence that Rain, Sun, Long and
MnJanTemp (considered together) provide further information about
MnJlyTemp once we have adjusted for Lat, Height, Sea and
NorthIsland.
Can we simplify further?
Suppose we want to know if we really need all four variables that are
in our previous model Climate.lm1
.
Maybe we can get by with an even simpler model.
\[E[\mbox{MnJlyTemp}] = \beta_0 + \beta_1
\mbox{Lat} + \beta_2 \mbox{Height} \]
So in terms of M1 we are setting \(\beta_3=\beta_4 =0\).
Climate.lm0 = lm(MnJlyTemp ~ Lat + Height, data = Climate)
anova(Climate.lm0, Climate.lm1)
Analysis of Variance Table
Model 1: MnJlyTemp ~ Lat + Height
Model 2: MnJlyTemp ~ Lat + Height + Sea + NorthIsland
Res.Df RSS Df Sum of Sq F Pr(>F)
1 33 41.439
2 31 28.130 2 13.308 7.3329 0.002468 **
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Clearly we reject the hypothesis that \(\beta_3=\beta_4 =0\). There is something in
these two variables that we need.
Connection with the Omnibus F Test
- The omnibus F test for model utility discussed earlier is a
particular case of the more general methodology for model comparison
presented in this lecture.
- Specifically, in omnibus F test we always take M1
to be the null model with q=0 explanatory variables,
and M2 to be the full model with p explanatory
variables.
Connection with T Tests for Single Variables
- F tests can be used to compare two models that differ by a
single explanatory variable.
- a T test can also be used to assess the importance of any
given explanatory variable.
There is a connection between the two approaches. Suppose we test
H0: \[\beta_j =
0~~~~\mbox{versus}~~~~H_1:\beta_j \ne 0\] having adjusted for
certain other variables.
If the observed t-test statistic is
tobs, and the observed F test statistic is
fobs, then
- fobs = tobs2.
- The P-value from the two tests will be the same.
More Testing for the Climate Data
Suppose that we wish to test for the importance of
NorthIsland
having adjusted for the other variables
Lat
, Height
and Sea
.
We want to test H0: \[\beta_4 = 0~~~~\mbox{versus}~~~~H_1:\beta_4 \ne
0\]
We will perform t and F tests using R.
Climate.lm00 <- lm(MnJlyTemp ~ Lat + Height + Sea, data = Climate)
anova(Climate.lm0, Climate.lm00, Climate.lm1)
Analysis of Variance Table
Model 1: MnJlyTemp ~ Lat + Height
Model 2: MnJlyTemp ~ Lat + Height + Sea
Model 3: MnJlyTemp ~ Lat + Height + Sea + NorthIsland
Res.Df RSS Df Sum of Sq F Pr(>F)
1 33 41.439
2 32 33.391 1 8.0478 8.8688 0.005591 **
3 31 28.130 1 5.2605 5.7971 0.022193 *
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
- The first F-statistic (from the anova() function output) is
f = 8.869 with corresponding P-value
P=0.00559. So we need Sea in addition to Lat and Height.
- The second F-statistic is f = 5.797 with
corresponding P-value P=0.02219. So we need
NorthIsland in addition to Lat, Height and Sea.
If we look at the summary() output for M1 we get
Call:
lm(formula = MnJlyTemp ~ Lat + Height + Sea + NorthIsland, data = Climate)
Residuals:
Min 1Q Median 3Q Max
-1.2667 -0.5054 -0.2098 0.4707 3.4088
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 21.378217 4.637932 4.609 6.56e-05 ***
Lat -0.375517 0.101816 -3.688 0.000862 ***
Height -0.004416 0.001109 -3.981 0.000385 ***
Sea 1.803422 0.483327 3.731 0.000766 ***
NorthIsland 1.559710 0.647795 2.408 0.022193 *
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.9526 on 31 degrees of freedom
Multiple R-squared: 0.8959, Adjusted R-squared: 0.8825
F-statistic: 66.73 on 4 and 31 DF, p-value: 8.723e-15
- The t-statistic for NorthIsland is t=2.408 with
corresponding P-value P=0.02219.
- Note that f = 5.797 = 2.4082 =
t2.
LS0tDQp0aXRsZTogIkxlY3R1cmUgMTQ6IENvbXBhcmlzb24gb2YgTXVsdGlwbGUgTGluZWFyIFJlZ3Jlc3Npb24gTW9kZWxzIg0Kc3VidGl0bGU6IDE2MS4yNTEgUmVncmVzc2lvbiBNb2RlbGxpbmcNCmF1dGhvcjogIlByZXNlbnRlZCBieSBKb25hdGhhbiBHb2RmcmV5IDxhLmouZ29kZnJleUBtYXNzZXkuYWMubno+IiAgDQpkYXRlOiAiV2VlayA1IG9mIFNlbWVzdGVyIDIsIGByIGx1YnJpZGF0ZTo6eWVhcihsdWJyaWRhdGU6Om5vdygpKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICBodG1sX25vdGVib29rOg0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgaW9zbGlkZXNfcHJlc2VudGF0aW9uOg0KICAgIHdpZGVzY3JlZW46IHRydWUNCiAgICBzbWFsbGVyOiB0cnVlDQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgc2xpZHlfcHJlc2VudGF0aW9uOiANCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KDQoNCg0KDQpbVmlldyB0aGUgbGF0ZXN0IHJlY29yZGluZyBvZiB0aGlzIGxlY3R1cmVdKGh0dHBzOi8vUi1SZXNvdXJjZXMubWFzc2V5LmFjLm56L3ZpZGVvcy8yNTFMMTQubXA0KQ0KPCEtLS0gRGF0YSBpcyBvbg0KaHR0cHM6Ly9yLXJlc291cmNlcy5tYXNzZXkuYWMubnovZGF0YS8xNjEyNTEvDQotLS0+DQoNCmBgYHtyIHNldHVwLCBwdXJsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShrbml0cikNCm9wdHNfY2h1bmskc2V0KGRldj1jKCJwbmciLCAicGRmIikpDQpvcHRzX2NodW5rJHNldChmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD03LCBmaWcucGF0aD0iRmlndXJlcy8iLCBmaWcuYWx0PSJ1bmxhYmVsbGVkIikNCm9wdHNfY2h1bmskc2V0KGNvbW1lbnQ9IiIsIGZpZy5hbGlnbj0iY2VudGVyIiwgdGlkeT1UUlVFKQ0Kb3B0aW9ucyhrbml0ci5rYWJsZS5OQSA9ICcnKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGJyb29tKQ0KYGBgDQoNCg0KPCEtLS0gRG8gbm90IGVkaXQgYW55dGhpbmcgYWJvdmUgdGhpcyBsaW5lLiAtLS0+DQoNCg0KDQpXZSBoYXZlIG5vdyBjb25zaWRlcmVkIHR3byB0eXBlcyBvZiB0ZXN0aW5nIHByb2JsZW1zIGluIG11bHRpcGxlICAgbGluZWFyIHJlZ3Jlc3Npb246DQogICAgDQoxLiAgVGVzdGluZyB3aGV0aGVyIHRoZSByZXNwb25zZSBpcyByZWxhdGVkIHRvIGF0IGxlYXN0IG9uZQ0KICAgICAgICBleHBsYW5hdG9yeSB2YXJpYWJsZTsNCjIuICBUZXN0aW5nIHRoZSBlZmZlY3Qgb2YgYSBnaXZlbiAoc2luZ2xlKSBleHBsYW5hdG9yeSB2YXJpYWJsZSBoYXZpbmcgYWRqdXN0ZWQgZm9yIG90aGVyIHZhcmlhYmxlcy4NCg0KSW4gdGhpcyBsZWN0dXJlLCB3ZSBpbnZlc3RpZ2F0ZSB0aGUgaW1wb3J0YW5jZSBvZiBhIGdyb3VwIG9mIGNvdmFyaWF0ZXMgc2ltdWx0YW5lb3VzbHkuDQoNCi0gVGhpcyBwcm9ibGVtIGlzIGVxdWl2YWxlbnQgdG8gY29tcGFyaW5nIGEgZ2l2ZW4gbW9kZWwgd2l0aCBhIHNpbXBsaWZpZWQgdmVyc2lvbiBvZiB0aGF0IG1vZGVsLg0KDQpUbyBoZWxwIG91ciBkaXNjdXNzaW9uIGxhdGVyIHdlIG1heSBjYWxsIHRoZSBzaW1wbGlmaWVkIG1vZGVsIGFuICJhY2NlcHRlZCBtb2RlbCIsIHRoYXQgaXMsIHdpdGggYSBzZXQgb2YgdmFyaWFibGVzICB0aGF0IHdlIHRoaW5rIGFjY2VwdCBhcyBiZWluZyB1c2VmdWwuIFRoZSBiaWdnZXIgbW9kZWwgKHdpdGggbW9yZSB2YXJpYWJsZXMpIHdlIG1heSBjYWxsIGFuICJleHRlbmRlZCBtb2RlbCIsIGFuZCB3ZSBtYXkgcmVmZXIgdG8gdGhlIGFkZGl0aW9uYWwgdmFyaWFibGVzIGFzICJjYW5kaWRhdGUiIHZhcmlhYmxlcy4gDQoNCg0KIyMgTWF0cml4IGZvcm11bGF0aW9uIG9mIHRoZSBtb2RlbA0KDQpJbiBhIGxpbmVhciByZWdyZXNzaW9uIHRoZSBwcmVkaWN0b3IgY29sdW1ucyAkWF8xLCBYXzIsIOKApixYX3AkICBhcmUgdHJlYXRlZCBhcyBiZWluZyBsaW5rZWQgdG9nZXRoZXIgaW50byBhIHNwZWNpZmljIGFycmFuZ2VtZW50IGNhbGxlZCBhIGRhdGEgbWF0cml4ICAqKlgqKiAoc29tZXRpbWVzIGFsc28gY2FsbGVkIHRoZSBtb2RlbCBtYXRyaXggb3IgRGVzaWduIG1hdHJpeCwgdXN1YWxseSBkZXBlbmRpbmcgb24gdGhlIGFtb3VudCBvZiBwcmUtcGxhbm5pbmcgdGhlcmUgaXMgaW4gdGhlIGRhdGEgY29sbGVjdGlvbi4NCg0KVGhlIGNvbHVtbnMgZG9u4oCZdCBoYXZlIHRvIGJlIG5leHQgdG8gZWFjaCBvdGhlciBpbiB0aGUgd29ya3NoZWV0OiBhbGwgeW91IGhhdmUgdG8gZG8gaXMgc3BlY2lmeSB0aGUgY29sdW1uIG5hbWVzIGFuZCAgdGhlIHNvZnR3YXJlIHNldHMgdXAgdGhlIG1hdHJpeCBmb3IgaXRzIG93biBjYWxjdWxhdGlvbnMuICANCg0KSWYgdGhlcmUgaXMgYW4gaW50ZXJjZXB0IGluIHRoZSBtb2RlbCwgdGhlbiB0aGUgZmlyc3QgY29sdW1uIG9mIHRoZSBkYXRhIG1hdHJpeCBpcyBhc3N1bWVkIHRvIGJlIGEgY29sdW1uIG9mIDFzLiBUaGlzIGlzIHRoZSBkZWZhdWx0LiAgIFlvdSBkb27igJl0IG5lZWQgdG8gY3JlYXRlIHRoaXMgY29sdW1uOiAgdGhlIHNvZnR3YXJlIGNyZWF0ZXMgaXQgYXV0b21hdGljYWxseSBhbmQgdXNlcyBpdCBpbiBpdHMgaW50ZXJuYWwgY2FsY3VsYXRpb25zIOKAkyB1bmxlc3MgeW91IHNwZWNpZmljYWxseSB0ZWxsIHRoZSBzb2Z0d2FyZSB0aGVyZSBpcyBubyBpbnRlcmNlcHQuICAgICAgICAgDQoNCk5vdGUgdGhhdCBzaW5jZSBldmVyeSBlbnRyeSBvbiBldmVyeSByb3cgb2YgdGhpcyBoaWRkZW4gY29sdW1uIGlzIHRoZSBzYW1lIG51bWJlciwgdGhlbiBpdCBpcyBhbHNvIHJlZmVycmVkIHRvIGFzIHRoZSAqKkNvbnN0YW50KiogY29sdW1uLiANCg0KTmV4dCB0byB0aGUgY29sdW1uIG9mIDFzLCB0aGUgc29mdHdhcmUgcGxhY2VzIGludG8gdGhlIGRhdGEgbWF0cml4IHRoZSBmaXJzdCBjb2x1bW4gc3BlY2lmaWVkIGluIHRoZSBsaXN0IG9mIHJlZ3Jlc3Npb24gcHJlZGljdG9ycy4gICBTbyBpbiB0aGUgZXhhbXBsZSBiZWxvdywgY29sdW1ucyAkWF8xJD1NbkphblRlbXAsIOKApiwkWF84JD1Ob3J0aElzbGFuZCBhcmUgc2hvd24uICAoVGhpcyAgbWFrZXMgOSBjb2x1bW5zIGluIGFsbCkuIA0KDQpJZiBvbmx5IHByZWRpY3RvcnMgJFhfMiQ9TGF0LCAkWF82JD1IZWlnaHQgYW5kICRYXzEkPU1uSmFuVGVtcCAgd2VyZSBpbmNsdWRlZCBpbiB0aGUgbW9kZWwgKGluIHRoYXQgb3JkZXIpLCB0aGUgZGF0YSBtYXRyaXggd291bGQgaGF2ZSB0aGUgY29sdW1uIG9mIDFzLCAgdGhlIGNvbHVtbiBvZiAkWF8yJCB2YWx1ZXMsIHRoZSBjb2x1bW4gb2YgJFhfNiQgdmFsdWVzLCBhbmQgZmluYWxseSB0aGUgY29sdW1uIG9mICRYXzEkIHZhbHVlcywgaW4gdGhhdCBvcmRlci4gDQoNClRoZSByZWdyZXNzaW9uIHBhcmFtZXRlcnMgICRcYmV0YV8wLCBcYmV0YV8xLCDigKYuLFxiZXRhXzgkICBhcmUgc2ltaWxhcmx5IHRyZWF0ZWQgYnkgdGhlIHNvZnR3YXJlIGFzIGJlaW5nIGFycmFuZ2VkIGluIGFuIG9yZGVybHkgZmFzaGlvbiwgaW4gYSBtYXRyaXggY29uc2lzdGluZyBvZiBhIHNpbmdsZSBjb2x1bW4gb2YgbnVtYmVycy4gICAoQSBtYXRyaXggd2l0aCBvbmUgY29sdW1uIGlzIGNhbGxlZCBhIChjb2x1bW4pIHZlY3RvcikuDQoNCg0KVGhlICpZKiB2YWx1ZXMgYXJlIHNpbWlsYXJseSB0cmVhdGVkIGFzIGEgdmVjdG9yIChvbmUtY29sdW1uIG1hdHJpeCBvZiBudW1iZXJzIGluIHRoZWlyIHBhcnRpY3VsYXIgb3JkZXIpLiAgICBXZSBkZW5vdGUgdGhlbSBieSBqdXN0IHRoZSBjb2x1bW4gbmFtZSBgWWAuDQoNCldpdGggdGhlIGRhdGEgb3JnYW5pc2VkIHRoaXMgd2F5IGluIHRoZSBjb21wdXRlciwgICB0aGUgbWF0aGVtYXRpY3MgdGFrZXMgb3Zlci4gIE1hdGhlbWF0aWNhbGx5LCANCnRoZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBjYW4gYmUgd3JpdHRlbiB1c2luZyBNYXRyaXggQWxnZWJyYSBhcyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAkJFkgPSBYIFxiZXRhICsgXHZhcmVwc2lsb24kJA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0Kd2hlcmUgJFxib2xkc3ltYm9se1x2YXJlcHNpbG9ufSQgIGlzIGEgdmVjdG9yIG9mIGVycm9ycy4gIA0KDQoNCg0KTm93IGFzIGJlZm9yZSwgb3VyIHRhc2sgaXMgdG8gZmluZCB0aGUgZXN0aW1hdGVzIG9mICB0aGUgaW50ZXJjZXB0IGFuZCBzbG9wZXMgJFxiZXRhXzAsIFxiZXRhXzEsIOKApixcYmV0YV9wJCAgICB0byBtYWtlIHRoZSBzdW0gb2Ygc3F1YXJlZCBlcnJvcnMgYXMgc21hbGwgYXMgcG9zc2libGUuICAgKGkuZS4gIHRvIG1pbmltaXNlICRcdmFyZXBzaWxvbl8xXjIgK1x2YXJlcHNpbG9uXzJeMisgLi4uK1x2YXJlcHNpbG9uX25eMiQpDQoNCg0KVGhlIGV4dHJhb3JkaW5hcmlseSBuaWNlIHRoaW5nIGFib3V0IHdyaXRpbmcgdGhlIHByb2JsZW0gaW4gbWF0cml4IHRlcm1zIGlzIHRoYXQgc29tZSBtYXRyaXggYWxnZWJyYSBnaXZlcyBhbiBpbW1lZGlhdGUgc29sdXRpb24gIHRoYXQgaXMgZWFzaWx5IHByb2dyYW1tZWQuICANCiQkIFxib2xkc3ltYm9se2J9ID0oWF5UIFgpXnstMX0gWF5UIFxib2xkc3ltYm9se3l9ICAkJCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQoNCg0KVGhpcyBob2xkcyBmb3IgYWxsIHNpemVzIG9mIHByb2JsZW0sIGluIHRlcm1zIG9mIGJvdGggdGhlIG51bWJlciBvZiByb3dzIChvYnNlcnZhdGlvbnMpIGFuZCBjb2x1bW5zICh2YXJpYWJsZXMpIGluIHRoZSAqWCogbW9kZWwgbWF0cml4Lg0KDQpSZW1lbWJlciB0aGF0IHByb3Zpc28gKGNvbmRpdGlvbiBmb3IgdmFsaWRpdHkgb2YgdGhlIHNvbHV0aW9uKTogICBUaGUgY29sdW1ucyBvZiAqWCogbXVzdCBiZSBsaW5lYXJseSBpbmRlcGVuZGVudCwgb3IgbW9yZSBhY2N1cmF0ZWx5LCBub3QgaGF2ZSBhbnkgbGluZWFyIGRlcGVuZGVuY2llcy4NCg0KIyMgQSBub3RlIG9uIHRoZSBpbnRlcmNlcHQNCg0KVGhlIG90aGVyIHBvaW50IHdvcnRoIG5vdGluZyBhYm91dCB0aGUgZm9ybXVsYSAgICAgICAgICAkJFxib2xkc3ltYm9se2J9ID0oWF5UWCleey0xfSAgWF5UIFxib2xkc3ltYm9se3l9ICAgJCQNCmlzIHRoYXQgdGhlIGludGVyY2VwdCBpcyBub3QgdHJlYXRlZCBhbnkgZGlmZmVyZW50bHkgdG8gYW55IG90aGVyIHByZWRpY3RvciB2YXJpYWJsZSAtICBpdCBpcyBqdXN0IHJlcHJlc2VudGVkIGJ5IGEgY29sdW1uIG9mIG51bWJlcnMgaW4gdGhlIGRhdGEgbWF0cml4ICoqWCoqIChpbiB0aGlzIGNhc2UgYSBjb2x1bW4gd2l0aCBldmVyeSBudW1iZXI9MSkuIA0KDQpUaGUgc2lnbmlmaWNhbmNlIG9mIHRoaXMgZmFjdCBpcyB0aGF0IGV4YWN0bHkgdGhlIHNhbWUgcnVsZXMgYW5kIGZvcm11bGFzIGFwcGx5IHRvIGVzdGltYXRpbmcgdGhlIGludGVyY2VwdCAkXGJldGFfMCQgYXMgYXBwbHkgdG8gZXN0aW1hdGluZyBhbnkgb3RoZXIgc2xvcGUgICRcYmV0YV8xLC4uLixcYmV0YV9wJC4gIFNvIGlmIHlvdSByZWFkIGEgcmVncmVzc2lvbiB0ZXh0Ym9vayBvciBhIHBhcGVyIHRoYXQgZ2l2ZXMgYSBmb3JtdWxhIGJhc2VkIG9uIHRoZSBzbG9wZXMsIHRoZXkgd29u4oCZdCBuZWNlc3NhcmlseSBnaXZlIGEgc2VwYXJhdGUgZm9ybXVsYSBmb3IgdGhlIGludGVyY2VwdC4gIA0KDQpJbiB3aGF0IGZvbGxvd3MsIHdlIHdpbGwgb2NjYXNpb25hbGx5IHRhbGsgYWJvdXQgdGhlIGRlc2lnbiBtYXRyaXggKG9yIGRhdGEgbWF0cml4KSBpZiBpdCBpcyBoZWxwZnVsIGZvciB1bmRlcnN0YW5kaW5nIHRoZSB3YXkgdGhlIHJlZ3Jlc3Npb24gbW9kZWxzIGhhdmUgYmVlbiBjcmVhdGVkLiANCg0KDQoNCg0KDQoNCg0KIyMgTmVzdGVkIE1vZGVscw0KDQpBIGxpbmVhciBtb2RlbCAqTTEqIGlzIHNhaWQgdG8gYmUgKipuZXN0ZWQqKiB3aXRoaW4gYW5vdGhlcg0KbW9kZWwgKk0yKiBpZiAqTTEqIGNhbiBiZSByZWNvdmVyZWQgYXMgYSBzcGVjaWFsIGNhc2Ugb2YgKk0xKiBieSBzZXR0aW5nIHRoZSBwYXJhbWV0ZXJzIG9mICpNMiogdG8gdGhlIG5lY2Vzc2FyeSB2YWx1ZXMuDQoNCkZvciB0aGUgQ2xpbWF0ZSBkYXRhLCBkZWZpbmUgbW9kZWwgKk0yKiBieQ0KDQokJEVbXG1ib3h7TW5KbHlUZW1wfV0gPSBcYmV0YV8wICsgXGJldGFfMSBcbWJveHtMYXR9ICsgXGJldGFfMiBcbWJveHtIZWlnaHR9ICsgXGJldGFfMyBcbWJveHtTZWF9ICsgXGJldGFfNCAgXG1ib3h7Tm9ydGhJc2xhbmR9ICAgICAgKyBcYmV0YV81IFxtYm94e1JhaW59KyBcYmV0YV82IFxtYm94e1N1bn0rIFxiZXRhXzdcbWJveHtMb25nfSsgXGJldGFfOCBcbWJveHtNbkphblRlbXB9JCQNCg0KVGhlbiB0aGUgbW9kZWwgKk0xKiBkZWZpbmVkIGJ5DQoNCg0KJCRFW1xtYm94e01uSmx5VGVtcH1dID0gXGJldGFfMCArIFxiZXRhXzEgXG1ib3h7TGF0fSArIFxiZXRhXzIgXG1ib3h7SGVpZ2h0fSArIFxiZXRhXzMgXG1ib3h7U2VhfSArIFxiZXRhXzQgXG1ib3h7Tm9ydGhJc2xhbmR9ICQkDQppcyBuZXN0ZWQgd2l0aGluICpNMiogYmVjYXVzZSB3ZSBjYW4gb2J0YWluICpNMSogYnkgc2V0dGluZyAkXGJldGFfNSA9IFxiZXRhXzYgPSBcYmV0YV83ID1cYmV0YV84ID0wJCBpbiAqTTIqLg0KDQoNCiMjICpGKiBUZXN0cyBmb3IgTmVzdGVkIE1vZGVscw0KDQpUaGUgZ2VuZXJhbCBpZGVhcyBhYm91dCBtb2RlbCBjb21wYXJpc29uICAgY29udGludWUgdG8gIGFwcGx5LiAgICANCg0KLSBXZSB3YW50IGEgImNoZWFwIiBtb2RlbCAoaS5lLiBvbmUgd2l0aCBmZXcgcGFyYW1ldGVycyk7DQotIFdlIHdhbnQgYSBtb2RlbCB0aGF0IGZpdHMgd2VsbCAoaS5lLiBvbmUgd2l0aCBzbWFsbCBSU1MuDQotIFRoZSBjaG9pY2Ugb2YgbW9kZWwgd2lsbCBiZSBhIHF1ZXN0aW9uIG9mIHdoZXRoZXIgdGhlIGltcHJvdmVtZW50IGluICAgICBnb29kbmVzcyBvZiBmaXQgb2YgdGhlIGV4dGVuZGVkIG1vZGVsICAqTTIqIG92ZXIgdGhlIHNpbXBsZXIgbW9kZWwgKk0xKiBpcyAgICAgIHdvcnRoIHRoZSBjb3N0Lg0KDQpTZWxlY3RpbmcgYSBtb2RlbCBpcyBlcXVpdmFsZW50IHRvIHRlc3RpbmcgaHlwb3RoZXNlcyBhYm91dCBwYXJhbWV0ZXJzIG9mICpNMiogKHRoZSBtb3JlIGNvbXBsZXggbW9kZWwpLg0KDQoNCi0gU3VwcG9zZSB0aGF0IGxpbmVhciBtb2RlbCAqTTEqIGhhcyAqcSogZXhwbGFuYXRvcnkgdmFyaWFibGVzIChoZW5jZSAqcSsxKiByZWdyZXNzaW9uIHBhcmFtZXRlcnMsIGluY2x1ZGluZyBpbnRlcmNlcHQpLg0KLSBTdXBwb3NlIHRoYXQgKk0xKiBpcyBuZXN0ZWQgd2l0aGluIGxpbmVhciBtb2RlbCAqTTIqIHdoaWNoIGhhcyAgKnAgPiBxKiBleHBsYW5hdG9yeSB2YXJpYWJsZXMgKGhlbmNlICpwKzEqIHJlZ3Jlc3Npb24gICAgIHBhcmFtZXRlcnMsIGluY2x1ZGluZyBpbnRlcmNlcHQpLg0KDQpDb21wYXJpc29uIG9mIHRoZSBtb2RlbHMgY2FuIGJlIGFjaGlldmVkIGJ5IHRlc3RpbmcNCg0KKkh+MH4qOiAkXGJldGFfaiA9IDAkIGZvciBhbGwgJGogXGluIEokIChpLmUuICpNMSogYWRlcXVhdGUpOyB2ZXJzdXMNCg0KKkh+MX4qOiAkXGJldGFfaiQgbm90IHplcm8gZm9yIGFsbCAkaiBcaW4gSiQgKGkuZS4gKk0yKiBiZXR0ZXIpDQogICAgDQp3aGVyZSAqSiogaW5kZXhlcyB0aGUgKnAtcSogdmFyaWFibGVzIHRoYXQgYXBwZWFyIGluICpNMiogYnV0IG5vdCAqTTEqLg0KDQoNClRoZSAqRiotc3RhdGlzdGljIHRvIHRlc3QgdGhlc2UgaHlwb3RoZXNlcyBpcw0KDQokJEYgPSBcZnJhY3tbUlNTX3tNMX0gLSBSU1Nfe00yfV0vKHAtcSl9e1JTU197TTJ9LyhuLXAtMSl9JCQNCg0KQXMgYmVmb3JlLCBleHRyZW1lLCBsYXJnZSB2YWx1ZXMgb2YgKkYqIHByb3ZpZGUgZXZpZGVuY2UgYWdhaW5zdCAqSH4wfiogKGhlbmNlIGV2aWRlbmNlIHRoYXQgd2Ugc2hvdWxkIHByZWZlciBtb2RlbCAqTTIqIHRvICpNMSopLg0KDQpJZiAqSH4wfiogaXMgY29ycmVjdCB0aGVuICpGKiBoYXMgYW4gKkYqIGRpc3RyaWJ1dGlvbiB3aXRoICoocC1xKSwobi1wLTEpKiBkZWdyZWVzIG9mIGZyZWVkb20uDQoNCkhlbmNlIGlmICpmfm9ic34qIGlzIHRoZSBvYnNlcnZlZCB2YWx1ZSBvZiB0aGUgdGVzdCBzdGF0aXN0aWMsIGFuZCAqWCogaXMgYSByYW5kb20gdmFyaWFibGUgZnJvbSBhbiAqRn5wLXEsbi1wLTF+KiBkaXN0cmlidXRpb24sIHRoZW4gdGhlICpQKi12YWx1ZSBpcyBnaXZlbiBieSANCg0KJCRQPSBQKFggXGdlIGZfe29ic30pJCQNCg0KIyMgSW50ZXJwcmV0YXRpb24gb2YgVGVzdCBSZXN1bHRzDQoNCi0gQXMgd2UgaGF2ZSBzZWVuIGVhcmxpZXIsIHRoZSBpbXBvcnRhbmNlIG9mIHZhcmlhYmxlcyBpbiBhIG11bHRpcGxlIHJlZ3Jlc3Npb24gaXMgZGVwZW5kZW50IHVwb24gY29udGV4dCAoaS5lLiB3aGF0IG90aGVyIHZhcmlhYmxlcyBhcmUgdGFrZW4gaW50byBhY2NvdW50KS4NCg0KLSBSZXRlbnRpb24gb2YgKkh+MH4qIChpLmUuIGFjY2VwdGFuY2UgdGhhdCBtb2RlbCAqTTEqIGlzIGFkZXF1YXRlKSBkb2VzIG5vdCBtZWFuIHRoYXQgdGhlICpwLXEqIHZhcmlhYmxlcyBpbmRleGVkIGJ5ICpKKiBhcmUgdW5yZWxhdGVkIHRvIHRoZSByZXNwb25zZS4NCi0gUmF0aGVyLCByZXRlbnRpb24gb2YgKkh+MH4qIGluZGljYXRlcyB0aGF0IHRoZSB2YXJpYWJsZXMgaW5kZXhlZCBieSAqSiogZG8gbm90IHByb3ZpZGUgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgcmVzcG9uc2UgaGF2aW5nIGFkanVzdGVkIGZvciBhbGwgdGhlIG90aGVyICgqcSopIHZhcmlhYmxlcy4NCg0KIyMgTW9kZWwgQ29tcGFyaXNvbiBmb3IgdGhlIENsaW1hdGUgZGF0YQ0KDQpPdXIgKk0yKiBhbmQgKk0xKiB3ZXJlIGdpdmVuIGFib3ZlLiANCg0KDQpOLkIuIGByIHhmdW46OmVtYmVkX2ZpbGUoIi4uLy4uL2RhdGEvQ2xpbWF0ZS5jc3YiKWAgdG8gcmVwbGljYXRlIHRoZSBmb2xsb3dpbmcgZXhhbXBsZXMuDQoNCg0KDQpgYGB7ciBmaXJzdE1vZGVscywgZWNobz0tMX0NCkNsaW1hdGUgPC0gcmVhZC5jc3YoZmlsZT0iLi4vLi4vZGF0YS9DbGltYXRlLmNzdiIsIGhlYWRlcj1UUlVFLCByb3cubmFtZXM9MSkgDQpDbGltYXRlLmxtMSA8LSBsbShNbkpseVRlbXB+IExhdCArIEhlaWdodCsgU2VhKyBOb3J0aElzbGFuZCwgZGF0YT1DbGltYXRlKQ0KYW5vdmEoQ2xpbWF0ZS5sbTEpDQpDbGltYXRlLmxtMiA8LSBsbShNbkpseVRlbXAgfiBMYXQgKyBIZWlnaHQrIFNlYSsgTm9ydGhJc2xhbmQrIFJhaW4rIFN1biArIExvbmcrIE1uSmFuVGVtcCwgZGF0YT1DbGltYXRlKQ0KYW5vdmEoQ2xpbWF0ZS5sbTIpDQpgYGANCg0KLSBGb3IgbW9kZWwgYENsaW1hdGUubG0xYCwgY2FsY3VsYXRpb25zIGdpdmUgKlJTU35NMX4gPSBgciByb3VuZChhbm92YShDbGltYXRlLmxtMSlbIlJlc2lkdWFscyIsICJTdW0gU3EiXSwzKWAqIGFuZCAqcT00Ki4NCi0gRm9yIG1vZGVsIGBDbGltYXRlLmxtMmAsIGNhbGN1bGF0aW9ucyBnaXZlICpSU1N+TTJ+ID0gIGByIHJvdW5kKGFub3ZhKENsaW1hdGUubG0yKVsiUmVzaWR1YWxzIiwgIlN1bSBTcSJdLCAzKWAqIGFuZCAqcD04Ki4NCg0KDQoNCiQkRiA9IFxmcmFje1tSU1Nfe00xfSAtIFJTU197TTJ9XS8ocC1xKX17UlNTX3tNMn0vKG4tcC0xKX0gPSBcZnJhY3tbMjguMTMgLSAyNS4wMjJdLyg4LTQpfXsyNS4wMjIvKDI3KX0gPSAwLjgzODQkJA0KDQoNCg0KDQoNCiQkUChYIFxnZSAwLjgzODQpID0gMC41MTI5fn5+fn5+fn5+flxtYm94e3doZXJlICRYIFxzaW0gRl97NCwyN30kfSQkDQoNCi0gV2UgY29uY2x1ZGUgdGhhdCAqSH4wfiogY2FuIGJlIHJldGFpbmVkIChpLmUuIG1vZGVsICpNMSogaXMgICAgICBhZGVxdWF0ZSkuIFRoZXJlIGlzIG5vIGV2aWRlbmNlIHRoYXQgUmFpbiwgU3VuLCBMb25nIGFuZCBNbkphblRlbXAgKGNvbnNpZGVyZWQgdG9nZXRoZXIpICAgcHJvdmlkZSBmdXJ0aGVyIGluZm9ybWF0aW9uIGFib3V0IE1uSmx5VGVtcCBvbmNlIHdlIGhhdmUgYWRqdXN0ZWQgZm9yIExhdCwgSGVpZ2h0LCBTZWEgYW5kIE5vcnRoSXNsYW5kLg0KDQpDYW4gd2Ugc2ltcGxpZnkgZnVydGhlcj8gDQoNCg0KU3VwcG9zZSB3ZSB3YW50IHRvIGtub3cgaWYgd2UgcmVhbGx5IG5lZWQgYWxsIGZvdXIgdmFyaWFibGVzIHRoYXQgYXJlIGluIG91ciBwcmV2aW91cyBtb2RlbCBgQ2xpbWF0ZS5sbTFgLiANCg0KDQpNYXliZSB3ZSBjYW4gZ2V0IGJ5IHdpdGggYW4gZXZlbiBzaW1wbGVyIG1vZGVsLg0KDQokJEVbXG1ib3h7TW5KbHlUZW1wfV0gPSBcYmV0YV8wICsgXGJldGFfMSBcbWJveHtMYXR9ICsgXGJldGFfMiBcbWJveHtIZWlnaHR9ICQkIA0KDQpTbyBpbiB0ZXJtcyBvZiBNMSB3ZSBhcmUgc2V0dGluZyAkXGJldGFfMz1cYmV0YV80ID0wJC4gDQoNCmBgYHtyIENsaW1hdGUubG0wfQ0KQ2xpbWF0ZS5sbTAgPWxtKCBNbkpseVRlbXAgfiBMYXQrIEhlaWdodCwgZGF0YSA9IENsaW1hdGUpDQphbm92YSggQ2xpbWF0ZS5sbTAsIENsaW1hdGUubG0xKQ0KYGBgDQoNCkNsZWFybHkgd2UgcmVqZWN0IHRoZSBoeXBvdGhlc2lzIHRoYXQgJFxiZXRhXzM9XGJldGFfNCA9MCQuIFRoZXJlIGlzIHNvbWV0aGluZyBpbiB0aGVzZSB0d28gdmFyaWFibGVzIHRoYXQgd2UgbmVlZC4gDQoNCg0KIyMgQ29ubmVjdGlvbiB3aXRoIHRoZSBPbW5pYnVzICpGKiBUZXN0DQoNCi0gVGhlIG9tbmlidXMgKkYqIHRlc3QgZm9yIG1vZGVsIHV0aWxpdHkgZGlzY3Vzc2VkIGVhcmxpZXIgaXMgYSAgICAgIHBhcnRpY3VsYXIgY2FzZSBvZiB0aGUgbW9yZSBnZW5lcmFsIG1ldGhvZG9sb2d5IGZvciBtb2RlbCBjb21wYXJpc29uICAgIHByZXNlbnRlZCBpbiB0aGlzIGxlY3R1cmUuDQotIFNwZWNpZmljYWxseSwgaW4gb21uaWJ1cyAqRiogdGVzdCB3ZSBhbHdheXMgdGFrZSAqTTEqIHRvIGJlIHRoZSAgICAgICpudWxsKiBtb2RlbCB3aXRoICpxPTAqIGV4cGxhbmF0b3J5IHZhcmlhYmxlcywgYW5kICAgICAgICpNMiogdG8gYmUgdGhlIGZ1bGwgbW9kZWwgd2l0aCAqcCogZXhwbGFuYXRvcnkgdmFyaWFibGVzLg0KDQoNCg0KDQojIyBDb25uZWN0aW9uIHdpdGggVCBUZXN0cyBmb3IgU2luZ2xlIFZhcmlhYmxlcw0KDQotICpGKiB0ZXN0cyBjYW4gYmUgdXNlZCB0byBjb21wYXJlIHR3byBtb2RlbHMgdGhhdCBkaWZmZXIgYnkgYSBzaW5nbGUgICAgICBleHBsYW5hdG9yeSB2YXJpYWJsZS4NCi0gYSAqVCogdGVzdCBjYW4gYWxzbyBiZSB1c2VkIHRvIGFzc2VzcyB0aGUgaW1wb3J0YW5jZSBvZiBhbnkgZ2l2ZW4gZXhwbGFuYXRvcnkgdmFyaWFibGUuDQoNCg0KVGhlcmUgaXMgYSBjb25uZWN0aW9uIGJldHdlZW4gdGhlIHR3byBhcHByb2FjaGVzLiBTdXBwb3NlIHdlIHRlc3QgKkh+MH4qOiAkJFxiZXRhX2ogPSAwfn5+flxtYm94e3ZlcnN1c31+fn5+SF8xOlxiZXRhX2ogXG5lIDAkJCBoYXZpbmcgYWRqdXN0ZWQgZm9yIGNlcnRhaW4gb3RoZXIgdmFyaWFibGVzLiANCg0KSWYgdGhlIG9ic2VydmVkICp0Ki10ZXN0IHN0YXRpc3RpYyBpcyAqdH5vYnN+KiwgYW5kIHRoZSBvYnNlcnZlZCAqRiogdGVzdCBzdGF0aXN0aWMgaXMgICAqZn5vYnN+KiwgdGhlbiANCg0KLSAqZn5vYnN+ID0gdH5vYnN+XjJeKi4NCi0gVGhlICpQKi12YWx1ZSBmcm9tIHRoZSB0d28gdGVzdHMgd2lsbCBiZSB0aGUgc2FtZS4NCg0KIyMgTW9yZSBUZXN0aW5nIGZvciB0aGUgQ2xpbWF0ZSAgRGF0YQ0KDQoNCi0gU3VwcG9zZSB0aGF0IHdlIHdpc2ggdG8gdGVzdCBmb3IgdGhlIGltcG9ydGFuY2Ugb2YgYE5vcnRoSXNsYW5kYCBoYXZpbmcgYWRqdXN0ZWQgZm9yIHRoZSBvdGhlciB2YXJpYWJsZXMgYExhdGAsIGBIZWlnaHRgIGFuZCBgU2VhYC4NCg0KLSBXZSB3YW50IHRvIHRlc3QgKkh+MH4qOiAkJFxiZXRhXzQgPSAwfn5+flxtYm94e3ZlcnN1c31+fn5+SF8xOlxiZXRhXzQgXG5lIDAkJCAgIA0KLSBXZSB3aWxsIHBlcmZvcm0gKnQqIGFuZCAqRiogdGVzdHMgdXNpbmcgUi4NCg0KDQpgYGB7ciBzZWNvbmRNb2RlbHN9DQpDbGltYXRlLmxtMDAgPC0gbG0oTW5KbHlUZW1wIH4gTGF0KyBIZWlnaHQrIFNlYSwgZGF0YT1DbGltYXRlKQ0KYW5vdmEoQ2xpbWF0ZS5sbTAsIENsaW1hdGUubG0wMCwgQ2xpbWF0ZS5sbTEpDQpgYGANCg0KLSBUaGUgZmlyc3QgKkYqLXN0YXRpc3RpYyAoZnJvbSB0aGUgYW5vdmEoKSBmdW5jdGlvbiBvdXRwdXQpIGlzICpmID0gOC44NjkqIHdpdGggY29ycmVzcG9uZGluZyAqUCotdmFsdWUgKlA9MC4wMDU1OSouIFNvIHdlIG5lZWQgU2VhIGluIGFkZGl0aW9uIHRvIExhdCBhbmQgSGVpZ2h0Lg0KLSBUaGUgc2Vjb25kICAqRiotc3RhdGlzdGljIGlzICAqZiA9IDUuNzk3KiB3aXRoIGNvcnJlc3BvbmRpbmcgKlAqLXZhbHVlICpQPTAuMDIyMTkqLiBTbyB3ZSBuZWVkIE5vcnRoSXNsYW5kICBpbiBhZGRpdGlvbiB0byBMYXQsIEhlaWdodCBhbmQgU2VhLiANCg0KSWYgd2UgbG9vayBhdCB0aGUgc3VtbWFyeSgpIG91dHB1dCBmb3IgTTEgIHdlIGdldA0KYGBge3Igc3VtbWFyeSBNMX0NCnN1bW1hcnkoQ2xpbWF0ZS5sbTEpDQpgYGANCg0KLSBUaGUgKnQqLXN0YXRpc3RpYyBmb3IgTm9ydGhJc2xhbmQgIGlzICp0PTIuNDA4KiB3aXRoIGNvcnJlc3BvbmRpbmcgKlAqLXZhbHVlICpQPTAuMDIyMTkqLg0KLSBOb3RlIHRoYXQgKmYqID0gNS43OTcgPSAyLjQwOF4yXiA9ICp0XjJeKi4NCg0KDQoNCg==