Idea of Collinearity and Orthogonality
Consider the model \[Y = \beta_0 +
\beta_1 X_1 + \beta_2X_2 + \varepsilon\] .
We normally interpret the regression parameter \(\beta_1\) as the rate of change in Y per
unit change in \(X_1\), where \(X_2\) is kept constant.
However, if \(X_1\) and \(X_2\) are strongly related, then this
interpretation may not be valid (or even possible). In particular if
\(X_1\) increases, \(X_2\) may increase at the same time. An
example would be if \(X_1\)= personal
income and \(X_2\) =household income.
The effect of \(X_1\) is expressed both
directly (through \(\beta_1\)) and
indirectly (through \(\beta_2\)). This
complicates both the interpretation and the estimation of these
parameters.
Now if \(X_1\) and \(X_2\) have a total lack of any linear
relationship (uncorrelated) then they are said to be
orthogonal.
In practice regressors are rarely orthogonal unless the regression
arises from a carefully designed experiment.
Nevertheless a little non-orthogonality is usually not important to
the analysis, especially in view of all the other variability
involved.
Non-Orthogonality
However sometimes \(X_1\) and \(X_2\) are so closely related that the
regression results are very hard to interpret. For example inclusion of
one variable into a model already containing the other variable can
radically alter the regression parameters. The parameters may also be
very sensitive to slight changes in the data and have large standard
errors.
Severe non-orthogonality is referred to as collinear
data or multicollinearity. This
- can be difficult to detect
- is not strictly a modeling error but a problem of inadequate
data
- requires caution in interpreting results.
So we address the following questions:
- How does multicollinearity affect inference and forecasting?
- How does one detect multicollinearity?
- How does one improve the analysis if multicollinearity is
present?
A Numerical Example
We will consider the effects of multicollinearity in the
A
and B
data considered earlier. Recall the
model is
\[Y = 50 + 5\times A + 5\times B +
\varepsilon ~~~~ (*) \]
Though you would not have noticed it, the A
and
B
data were highly correlated.
Download simulation1.csv
## sim = read_csv("simulation1.csv")
sim_final <- sim |>
mutate(Y = 50 + 5 * A + 5 * B + epsilon)
ggplot(sim_final) + geom_point(mapping = aes(x = A, y = B, size = Y))
This is what resulted in the relatively high standard errors for each
regression coefficient (given the super high \(R^2\))
lm.cor = lm(Y ~ A + B, data = sim_final)
summary(lm.cor)
Call:
lm(formula = Y ~ A + B, data = sim_final)
Residuals:
Min 1Q Median 3Q Max
-1.36984 -0.74294 -0.05982 0.69150 1.63488
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 48.7641 2.1367 22.82 3.43e-14 ***
A 4.7613 0.3383 14.07 8.48e-11 ***
B 5.2896 0.3500 15.11 2.75e-11 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.8985 on 17 degrees of freedom
Multiple R-squared: 0.9977, Adjusted R-squared: 0.9974
F-statistic: 3701 on 2 and 17 DF, p-value: < 2.2e-16
Now suppose we keep exactly the same numbers A, B and epsilon, but
break the correlation between the A and B columns.
As a simple way of spreading them out, we just randomize the order of
numbers in column B, then recompute Y.
sim_rand <- sim_final |>
mutate(B_rand = sample(B)) |>
mutate(Y_rand = 50 + 5 * A + 5 * B_rand + epsilon)
ggplot(sim_rand) + geom_point(mapping = aes(x = A, y = B_rand, size = Y_rand))
lm.uncor = lm(Y_rand ~ A + B_rand, data = sim_rand)
summary(lm.uncor)
Call:
lm(formula = Y_rand ~ A + B_rand, data = sim_rand)
Residuals:
Min 1Q Median 3Q Max
-1.4875 -0.6250 0.1328 0.5686 1.1555
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 52.9939 2.5598 20.70 1.7e-13 ***
A 5.0458 0.1030 48.97 < 2e-16 ***
B_rand 4.7737 0.1066 44.78 < 2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.8147 on 17 degrees of freedom
Multiple R-squared: 0.9965, Adjusted R-squared: 0.9961
F-statistic: 2431 on 2 and 17 DF, p-value: < 2.2e-16
The standard errors of the coefficients are about 1/3 what they
were.
Note that really the only thing we have done here is to break the
correlation between \(A\) and \(B\), but this has greatly improved the
analysis.
Orthogonal Data
Consider the model \[Y = \beta_0 +
\beta_1 X + \beta_2 Z + \varepsilon\]
Recall that if \(X\) and \(Z\) are uncorrelated, they are called
orthogonal. Then
In other words it doesn’t matter whether we talk about the effect of
\(X\) by itself or “\(X\) after \(Z\)”. The effect is the same.
In summary, orthogonality of the explanatory variables allows easier
interpretation of the results of an experiment and is an important goal
in the design of experiments.
Collinear data
Suppose we have three sets of numbers \(X,Z,Y\) such that \[Y = \beta_0 +\beta_1 X + \beta_2 Z
+\varepsilon\]
but that, unknown to us, \[Z = \alpha_0 +
\alpha_1 X\] at least equal up to some level of rounding
error.
Then the regression model could be rewritten in terms of any
arbitrary combination of the X and Z figures, as we can always add on
\(k\) lots of \(\alpha_0 + \alpha_1 X\) and then take off
\(k\) lots of \(Z\) (as they’re the same thing):
\[\begin{aligned}Y &= \beta_0 +\beta_1
X + \beta_2 Z + k(\alpha_0 + \alpha_1 X) - k Z +\varepsilon\\ & =
(\beta_0 + k\alpha_0) + (\beta_1 + k\alpha_1)X + (\beta_2 - k)Z +
\varepsilon\end{aligned}\]
for any real number \(k\).
There are an infinite number of possible solutions, and if we do get
a numerical answer for the regression coefficients, the particular
answer may even just depend on rounding, with different amounts of
rounding could give very different answers.
We describe this situation by saying the regression model is not
estimable.
A consequence of such high correlation between the predictor
variables is that the regression coefficients may become totally
uninterpretable (for example something you know has a positive
relationship with the response ends up with a negative coefficient) and
the regression coefficients may have very high standard errors.
This is the problem of Multicollinearity.
How do we assess whether it is a real problem in our data?
Correlations Among The Predictors
We could look at the correlations between the explanatory variables.
Since correlation is a measure of linear relationship, then if two
variables are highly correlated then it doesn’t make sense to keep them
both in the model.
Example. Consider the data below on household electricity bills with
explanatory variables
- the income of the household;
- the number of persons in the house; and
- the area of the house.
Each of these explanatory variables would be expected to be related
to the Bill, and yet they are also related to each other, so we may not
be able to have them all in the same model.
Download electric.csv
## electric = read_csv("electric.csv")
electric
# A tibble: 34 × 4
Bill Income Persons Area
<dbl> <dbl> <dbl> <dbl>
1 228 3220 2 1160
2 156 2750 1 1080
3 648 3620 2 1720
4 528 3940 1 1840
5 552 4510 3 2240
6 636 3990 4 2190
7 444 2430 1 830
8 144 3070 1 1150
9 744 3750 2 1570
10 1104 4790 5 2660
# ℹ 24 more rows
We could check all pair-wise correlations with the cor()
function:
Bill Income Persons Area
Bill 1.0000000 0.8368788 0.4941247 0.9050448
Income 0.8368788 1.0000000 0.1426486 0.9612801
Persons 0.4941247 0.1426486 1.0000000 0.3656021
Area 0.9050448 0.9612801 0.3656021 1.0000000
This indicates that the Area
of the house is very
strongly correlated to the Income
of the occupants (\(r= 0.961\)), and so both may say much the
same thing about the electricity bill. We may not be able to have both
these in the model.
By contrast the number of persons in the house is almost unrelated (r
= 0.143) to the Income
and only weakly related to the
Area
(r = 0.366) so persons provides more-or-less
independent information.
A pairs plot can be useful in these circumstances.
But how do we know this is causing real problems?
Spotting multicollinearity
electric.lm0 = lm(Bill ~ Area + Persons + Income, data = electric)
summary(electric.lm0)
Call:
lm(formula = Bill ~ Area + Persons + Income, data = electric)
Residuals:
Min 1Q Median 3Q Max
-223.36 -102.88 -8.53 78.61 331.46
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -358.44157 198.73583 -1.804 0.0813 .
Area 0.28110 0.22611 1.243 0.2234
Persons 55.08763 29.04515 1.897 0.0675 .
Income 0.07514 0.13609 0.552 0.5850
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 135.4 on 30 degrees of freedom
Multiple R-squared: 0.8514, Adjusted R-squared: 0.8365
F-statistic: 57.28 on 3 and 30 DF, p-value: 1.585e-12
The regression as a whole is highly significant, yet none of the
regression coefficents are significant. This suggests
multicollinearity.
Contradictory Regression Coefficients
Let’s look at the variable we know are highly correlated
(i.e. Area
and Income
) separately as
predictors:
elec.lm1 = lm(Bill ~ Area, data = electric)
elec.lm2 = lm(Bill ~ Income, data = electric)
summary(elec.lm1)
Call:
lm(formula = Bill ~ Area, data = electric)
Residuals:
Min 1Q Median 3Q Max
-216.58 -102.40 -30.67 106.13 292.44
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -211.64863 73.36173 -2.885 0.00695 **
Area 0.43760 0.03635 12.037 2.02e-13 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 144.7 on 32 degrees of freedom
Multiple R-squared: 0.8191, Adjusted R-squared: 0.8135
F-statistic: 144.9 on 1 and 32 DF, p-value: 2.02e-13
Call:
lm(formula = Bill ~ Income, data = electric)
Residuals:
Min 1Q Median 3Q Max
-288.36 -117.01 -46.72 134.27 441.57
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -425.16251 124.92953 -3.403 0.00181 **
Income 0.25899 0.02995 8.649 6.97e-10 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 186.2 on 32 degrees of freedom
Multiple R-squared: 0.7004, Adjusted R-squared: 0.691
F-statistic: 74.8 on 1 and 32 DF, p-value: 6.974e-10
By themselves both variables are positively related to Bill and have
small Std Errors, as we would expect given the pairs chart.
But let’s see what happens if we put them together:
elec.lm3 <- lm(Bill ~ Area + Income, data = electric)
summary(elec.lm3)
Call:
lm(formula = Bill ~ Area + Income, data = electric)
Residuals:
Min 1Q Median 3Q Max
-221.3 -112.4 -19.8 86.4 297.1
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -52.23835 120.64872 -0.433 0.668
Area 0.64033 0.12857 4.981 2.27e-05 ***
Income -0.13498 0.08229 -1.640 0.111
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 141 on 31 degrees of freedom
Multiple R-squared: 0.8336, Adjusted R-squared: 0.8228
F-statistic: 77.62 on 2 and 31 DF, p-value: 8.507e-13
When put together, one predictor has changed its sign and the Std
Errors are much larger.
This illustrates how multicollinearity can render the regression
coefficients quite misleading, as presumably income IS related to higher
electricity bills (as folk on higher incomes can afford to have more
stuff that consumes energy).
One solution to multicollinearity is to delete one of the variables.
However it can be difficult to decide which one to delete. For example
suppose we particularly want to quantify the effect of Persons
on the electricity bill after adjusting for one of the others:
elec.lm4 = lm(Bill ~ Area + Persons, data = electric)
elec.lm5 = lm(Bill ~ Income + Persons, data = electric)
summary(elec.lm4)
Call:
lm(formula = Bill ~ Area + Persons, data = electric)
Residuals:
Min 1Q Median 3Q Max
-223.77 -101.97 -13.34 83.17 322.35
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -255.94923 70.14230 -3.649 0.000959 ***
Area 0.40429 0.03615 11.183 2.08e-12 ***
Persons 42.03394 16.67947 2.520 0.017096 *
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 133.9 on 31 degrees of freedom
Multiple R-squared: 0.8499, Adjusted R-squared: 0.8402
F-statistic: 87.74 on 2 and 31 DF, p-value: 1.72e-13
Call:
lm(formula = Bill ~ Income + Persons, data = electric)
Residuals:
Min 1Q Median 3Q Max
-220.49 -105.87 -22.33 85.43 345.76
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -575.4106 95.9010 -6.000 1.23e-06 ***
Income 0.2421 0.0222 10.905 3.89e-12 ***
Persons 85.3352 16.0031 5.332 8.28e-06 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 136.6 on 31 degrees of freedom
Multiple R-squared: 0.8437, Adjusted R-squared: 0.8336
F-statistic: 83.68 on 2 and 31 DF, p-value: 3.204e-13
Should we estimate that each extra person adds 42 to the electric
bill, or 83 to the bill? Of course the interpretation of these
regression models differs slightly (in the first model it is the effect
of each additional person among families of the same income, while in
the second it is the effect of person among families of with homes of
the same area). But it still makes one uneasy to see such a big
difference.
Regressing The Predictors Among Themselves
More generally (more than just looking at individual correlations) we
could look at whether any of the variables are strongly predicted by a
combination of the other regressors.
i.e. Whether the \(R^2\) for
regressing variable \(X_i\) on the
other regressors
\(X_1, X_2,..., X_{i-1},
X_{i+1},...,X{p}\) is large. We call this \(R^2_i\).
If it is large then we probably don’t need \(X_i\) (or some other variables) in the
model.
This is more general than just looking at simple correlations between
variables, since the regression model picks up on combinations of
variables.
A “rule of thumb” is that multicollinearity is a big problem if \(R_i^2 > 0.9\), i.e. the \(i\)th variable is more than 90% explained
by the other predictors.
We can get \(R_i^2\) by doing the
regressions ourselves, but it is easier to consider the concept of
variance inflation instead.
Variance Inflation Factors, VIFs
A major impact of multicollinearity is found in the variances (and
hence standard errors) of the regression coefficients.
- If \(X_i\) is alone in the model
\[Y =\beta_0 +\beta_1 X_i
+\varepsilon~,~~~~~\mbox{then}\]
\[\hat{\mbox{var}}(\hat\beta_1) =
\frac{s^2}{(n-1)\mbox{var}(X_i)}\] 2. If \(X_i\) is in a model with other predictors
then \[\hat{\mbox{var}}(\hat\beta_i)
= s^2 \left(X^T X\right)^{-1}_{ii}\] where the last term is
the corresponding diagonal entry of the \((X^T
X )^{-1}\) matrix, the variance-covariance matrix.
It turns out that we can rewrite this as:
\[\hat{\mbox{var}}(\hat\beta_i) =
\frac{s^2}{(n-1)\mbox{var}(X_i)}\cdot \frac{1}{1 - R_i^2}\] where
\(R_i^2\) is the multiple \(R^2\) of the regression of \(X_i\) on the other predictors.
Thus, when going from a model with just \(X_i\) to a full model, the variance of the
coefficent of \(X_i\)
inflates by a factor of
\[\mbox{VIF}_i = \frac{1}{1 -
R_i^2}\]
Variable inflation factors that are large (e.g. \(\mbox{VIF}_i > 10\)) indicates
multicollinearity problems.
On the other hand if the VIF is around 1, then the variable \(X_i\) is pretty much orthogonal to the
other variables.
To obtain VIFs we need to invoke library(car)
.
Area Persons Income
44.141264 3.421738 39.035444
Clearly we need to do something to reduce the multicollinearity.
What we do is application-specific! We might choose to remove a
variable, or we might choose to do something else (e.g. find a better
measure or somehow combine the variables into some average?)
Example: SAP Data
The following example illustrates multicollinearity in a context
where it is not immediately visible. Chatterjee and Price discuss a
dataset relating Sales S
to A
(advertising
expenditure), A1
(last year’s advertising), P
(promotion expenditure), P1
(last year’s promotions
expenditure) and SE
(sales expenses). The analyst
eventually found out that an approximate budgetary constraint
\[ A + A1 + P + P1 = 5 \] was
hidden in the data. The constraint was known to the advertising
executives who had commissioned the study but it had not been mentioned
to the statistical consultant who was given the data to analyze.
Download SAP.csv
## SAP = read_csv("SAP.csv")
SAP
# A tibble: 22 × 6
A P SE A1 P1 S
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1.99 1 0.3 2.02 0 20.1
2 1.94 0 0.3 1.99 1 15.1
3 2.20 0.8 0.35 1.94 0 18.7
4 2.00 0 0.35 2.20 0.8 16.1
5 1.69 1.3 0.3 2.00 0 21.3
6 1.74 0.3 0.32 1.69 1.3 17.8
7 2.07 1 0.31 1.74 0.3 18.9
8 1.02 1 0.41 2.07 1 21.3
9 2.02 0.9 0.45 1.02 1 20.5
10 1.06 1 0.45 2.02 0.9 20.5
# ℹ 12 more rows
We start with a linear model for sales in terms of all other
variables:
SAP.lm = lm(S ~ A + P + SE + A1 + P1, data = SAP)
summary(SAP.lm)
Call:
lm(formula = S ~ A + P + SE + A1 + P1, data = SAP)
Residuals:
Min 1Q Median 3Q Max
-1.8601 -0.9847 0.1323 0.7017 2.2046
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -14.194 18.715 -0.758 0.4592
A 5.361 4.028 1.331 0.2019
P 8.372 3.586 2.334 0.0329 *
SE 22.521 2.142 10.512 1.36e-08 ***
A1 3.855 3.578 1.077 0.2973
P1 4.125 3.895 1.059 0.3053
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 1.32 on 16 degrees of freedom
Multiple R-squared: 0.9169, Adjusted R-squared: 0.8909
F-statistic: 35.3 on 5 and 16 DF, p-value: 4.289e-08
We see that, apart from Sales Expenses, there is only weak evidence
of a relationship between advertising, promotions and sales.
A P SE A1 P1
36.941513 33.473514 1.075962 25.915651 43.520965
The VIF shows high multicollinearity for all advertising and
promotions data.
If we drop one of the variables the others change their coefficients
unpredictably depending on which variable we drop.
SAP.lm2 = lm(S ~ P + SE + A1 + P1, data = SAP)
SAP.lm3 = lm(S ~ A + P + SE + P1, data = SAP)
SAP.lm2 |>
tidy()
# A tibble: 5 × 5
term estimate std.error statistic p.value
<chr> <dbl> <dbl> <dbl> <dbl>
1 (Intercept) 10.5 2.46 4.28 0.000510
2 P 3.70 0.757 4.89 0.000138
3 SE 22.8 2.18 10.5 0.00000000804
4 A1 -0.769 0.875 -0.880 0.391
5 P1 -0.969 0.742 -1.31 0.209
# A tibble: 5 × 5
term estimate std.error statistic p.value
<chr> <dbl> <dbl> <dbl> <dbl>
1 (Intercept) 5.76 2.70 2.14 0.0475
2 A 1.15 0.967 1.19 0.252
3 P 4.61 0.830 5.56 0.0000347
4 SE 22.7 2.15 10.6 0.00000000666
5 P1 0.0315 0.862 0.0365 0.971
Suppose we look at the relationships among the predictor
variables:
SAPA.lm = lm(A ~ P + SE + A1 + P1, data = SAP)
SAPA.lm |>
tidy()
# A tibble: 5 × 5
term estimate std.error statistic p.value
<chr> <dbl> <dbl> <dbl> <dbl>
1 (Intercept) 4.61 0.145 31.8 1.35e-16
2 P -0.871 0.0446 -19.5 4.38e-13
3 SE 0.0510 0.128 0.397 6.96e- 1
4 A1 -0.863 0.0515 -16.7 5.33e-12
5 P1 -0.950 0.0437 -21.7 7.65e-14
This is approximately A = 5 - P -A1 - P1 as in the hidden
constraint.
What do we do about multicollinearity?
As far as 161.251 is concerned, we have few options.
We can remove one (or more) of the correlated variables from the
regression. Our aim is to fix the problem, but sometimes we have a bit
of choice which variable to remove, in which case we would like the
model to be as interpretable as possible. Sometimes the choice is
between a variable with many missing values and one with few, and so we
choose the latter if it allows us a bigger effective sample size \(n\).
We could construct another variable which is a combination of the
two most highly correlated variables. It might be an ordinary average
\((X_1+X_2)/2\), or an average of
standardized versions of the \(X_i\)s
or some other linear combination. Sometimes subtracting or dividing one
variable by another will help. For example the incidence of a particular
disease such as obesity may be higher in countries with high GDP and
high Population. But GDP depends on the size of the economy, which is
correlated with the Population. Dividing gives us per capita GDP which
is a better indicator of average wealth and hence may be more related to
disease incidence.
-
(Not examinable) Those who have studied multivariate statistics may find
it useful to construct principal components or do a factor analysis of a
set of correlated variables. Dropping less important components leads to
a class of models called biased regression. Another form of biased
regression shrinks the regression coefficients towards zero - a method
called shrinkage estimators.
-
(Not examinable) It is possible to fit regression models that impose
linear constraints on the regression coefficients (such as forcing them
to add to 100%). Such restricted least squares is also beyond the scope
of this course. Therefore for this course we focus on method 1, dropping
variables.
LS0tDQp0aXRsZTogIkxlY3R1cmUgMjk6IE11bHRpY29sbGluZWFyaXR5Ig0Kc3VidGl0bGU6IDE2MS4yNTEgUmVncmVzc2lvbiBNb2RlbGxpbmcNCmF1dGhvcjogIlByZXNlbnRlZCBieSBKb25hdGhhbiBNYXJzaGFsbCA8Si5DLm1hcnNoYWxsQG1hc3NleS5hYy5uej4iICANCmRhdGU6ICJXZWVrIDExIG9mIFNlbWVzdGVyIDIsIGByIGx1YnJpZGF0ZTo6eWVhcihsdWJyaWRhdGU6Om5vdygpKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICBodG1sX25vdGVib29rOg0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgaW9zbGlkZXNfcHJlc2VudGF0aW9uOg0KICAgIHdpZGVzY3JlZW46IHRydWUNCiAgICBzbWFsbGVyOiB0cnVlDQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgc2xpZHlfcHJlc2VudGF0aW9uOiANCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KDQoNCg0KPCEtLS0gRGF0YSBpcyBvbg0KaHR0cHM6Ly9yLXJlc291cmNlcy5tYXNzZXkuYWMubnovZGF0YS8xNjEyNTEvDQotLS0+DQoNCmBgYHtyIHNldHVwLCBwdXJsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShrbml0cikNCm9wdHNfY2h1bmskc2V0KGRldj1jKCJwbmciLCAicGRmIikpDQpvcHRzX2NodW5rJHNldChmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD03LCBmaWcucGF0aD0iRmlndXJlcy8iLCBmaWcuYWx0PSJ1bmxhYmVsbGVkIikNCm9wdHNfY2h1bmskc2V0KGNvbW1lbnQ9IiIsIGZpZy5hbGlnbj0iY2VudGVyIiwgdGlkeT1UUlVFKQ0Kb3B0aW9ucyhrbml0ci5rYWJsZS5OQSA9ICcnKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGJyb29tKQ0KYGBgDQoNCg0KPCEtLS0gRG8gbm90IGVkaXQgYW55dGhpbmcgYWJvdmUgdGhpcyBsaW5lLiAtLS0+DQpgYGB7ciBhbm90aGVyTGliLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KGNhcikKYGBgCgojIyAgSWRlYSBvZiBDb2xsaW5lYXJpdHkgYW5kIE9ydGhvZ29uYWxpdHkgIAoKQ29uc2lkZXIgdGhlIG1vZGVsCSQkWSAgPSAgXGJldGFfMCArIFxiZXRhXzEgWF8xICsgXGJldGFfMlhfMiArIFx2YXJlcHNpbG9uJCQgIC4KCldlIG5vcm1hbGx5IGludGVycHJldCB0aGUgcmVncmVzc2lvbiBwYXJhbWV0ZXIgICRcYmV0YV8xJCAgYXMgdGhlIHJhdGUgb2YgY2hhbmdlIGluICBZICBwZXIgdW5pdCBjaGFuZ2UgaW4gICRYXzEkLCB3aGVyZSAgJFhfMiQgIGlzIGtlcHQgY29uc3RhbnQuCgpIb3dldmVyLCBpZiAgJFhfMSQgIGFuZCAgJFhfMiQgIGFyZSBzdHJvbmdseSByZWxhdGVkLCB0aGVuIHRoaXMgaW50ZXJwcmV0YXRpb24gbWF5IG5vdCBiZSB2YWxpZCAob3IgZXZlbiBwb3NzaWJsZSkuICBJbiBwYXJ0aWN1bGFyIGlmICAkWF8xJCAgaW5jcmVhc2VzLCAgJFhfMiQgIG1heSBpbmNyZWFzZSBhdCB0aGUgc2FtZSB0aW1lLiAgQW4gZXhhbXBsZSB3b3VsZCBiZSBpZiAkWF8xJD0gcGVyc29uYWwgaW5jb21lIGFuZCAkWF8yJCA9aG91c2Vob2xkIGluY29tZS4gICAgVGhlIGVmZmVjdCBvZiAgJFhfMSQgIGlzIGV4cHJlc3NlZCBib3RoIGRpcmVjdGx5ICh0aHJvdWdoICRcYmV0YV8xJCkgYW5kIGluZGlyZWN0bHkgKHRocm91Z2ggJFxiZXRhXzIkKS4gIFRoaXMgY29tcGxpY2F0ZXMgYm90aCB0aGUgaW50ZXJwcmV0YXRpb24gYW5kIHRoZSBlc3RpbWF0aW9uIG9mIHRoZXNlIHBhcmFtZXRlcnMuCgogCk5vdyBpZiAkWF8xJCAgYW5kICRYXzIkIGhhdmUgYSB0b3RhbCBsYWNrIG9mIGFueSBsaW5lYXIgcmVsYXRpb25zaGlwICh1bmNvcnJlbGF0ZWQpIHRoZW4gdGhleSBhcmUgc2FpZCB0byBiZSAqKm9ydGhvZ29uYWwqKi4KCkluIHByYWN0aWNlIHJlZ3Jlc3NvcnMgYXJlIHJhcmVseSBvcnRob2dvbmFsIHVubGVzcyB0aGUgcmVncmVzc2lvbiBhcmlzZXMgZnJvbSBhIGNhcmVmdWxseSBkZXNpZ25lZCBleHBlcmltZW50LgoKTmV2ZXJ0aGVsZXNzIGEgbGl0dGxlIG5vbi1vcnRob2dvbmFsaXR5IGlzIHVzdWFsbHkgbm90IGltcG9ydGFudCB0byB0aGUgYW5hbHlzaXMsIGVzcGVjaWFsbHkgaW4gdmlldyBvZiBhbGwgdGhlIG90aGVyIHZhcmlhYmlsaXR5IGludm9sdmVkLgoKIyMgTm9uLU9ydGhvZ29uYWxpdHkKCkhvd2V2ZXIgc29tZXRpbWVzICAkWF8xJCAgYW5kICRYXzIkIGFyZSBzbyBjbG9zZWx5IHJlbGF0ZWQgdGhhdCB0aGUgcmVncmVzc2lvbiByZXN1bHRzIGFyZSB2ZXJ5IGhhcmQgdG8gaW50ZXJwcmV0LiAgRm9yIGV4YW1wbGUgaW5jbHVzaW9uIG9mIG9uZSB2YXJpYWJsZSBpbnRvIGEgbW9kZWwgYWxyZWFkeSBjb250YWluaW5nIHRoZSBvdGhlciB2YXJpYWJsZSBjYW4gcmFkaWNhbGx5IGFsdGVyIHRoZSByZWdyZXNzaW9uIHBhcmFtZXRlcnMuICBUaGUgcGFyYW1ldGVycyBtYXkgYWxzbyBiZSB2ZXJ5IHNlbnNpdGl2ZSB0byBzbGlnaHQgY2hhbmdlcyBpbiB0aGUgZGF0YSBhbmQgaGF2ZSBsYXJnZSBzdGFuZGFyZCBlcnJvcnMuCgpTZXZlcmUgbm9uLW9ydGhvZ29uYWxpdHkgaXMgcmVmZXJyZWQgdG8gYXMgKipjb2xsaW5lYXIqKiBkYXRhIG9yICoqbXVsdGljb2xsaW5lYXJpdHkqKi4gVGhpcwoKKgljYW4gYmUgZGlmZmljdWx0IHRvIGRldGVjdAoqCWlzIG5vdCBzdHJpY3RseSBhIG1vZGVsaW5nIGVycm9yIGJ1dCBhIHByb2JsZW0gb2YgaW5hZGVxdWF0ZSBkYXRhCioJcmVxdWlyZXMgY2F1dGlvbiBpbiBpbnRlcnByZXRpbmcgcmVzdWx0cy4KClNvIHdlIGFkZHJlc3MgdGhlIGZvbGxvd2luZyBxdWVzdGlvbnM6CgotCUhvdyBkb2VzIG11bHRpY29sbGluZWFyaXR5IGFmZmVjdCBpbmZlcmVuY2UgYW5kIGZvcmVjYXN0aW5nPwotCUhvdyBkb2VzIG9uZSBkZXRlY3QgbXVsdGljb2xsaW5lYXJpdHk/Ci0JSG93IGRvZXMgb25lIGltcHJvdmUgdGhlIGFuYWx5c2lzIGlmIG11bHRpY29sbGluZWFyaXR5IGlzIHByZXNlbnQ/CgojIyAgIEEgTnVtZXJpY2FsIEV4YW1wbGUKCldlIHdpbGwgY29uc2lkZXIgdGhlIGVmZmVjdHMgb2YgbXVsdGljb2xsaW5lYXJpdHkgaW4gdGhlIGBBYCBhbmQgYEJgICBkYXRhIGNvbnNpZGVyZWQgZWFybGllci4gIFJlY2FsbCB0aGUgbW9kZWwgaXMgIAokJFkgPSA1MCArIDVcdGltZXMgQSAgKyA1XHRpbWVzIEIgICsgXHZhcmVwc2lsb24gIH5+fn4gKCopICQkCgpUaG91Z2ggeW91IHdvdWxkIG5vdCBoYXZlIG5vdGljZWQgaXQsIHRoZSBgQWAgYW5kIGBCYCBkYXRhIHdlcmUgaGlnaGx5IGNvcnJlbGF0ZWQuCgpgciB4ZnVuOjplbWJlZF9maWxlKCIuLi8uLi9kYXRhL3NpbXVsYXRpb24xLmNzdiIpYCAKCmBgYHtyIHJlYWQgU2ltdWxhdGlvbjEsIGV2YWw9LTEsIGVjaG89LTIsIG1lc3NhZ2U9RkFMU0V9CnNpbSA9IHJlYWRfY3N2KCJzaW11bGF0aW9uMS5jc3YiKQpzaW0gPSByZWFkX2NzdigiLi4vLi4vZGF0YS9zaW11bGF0aW9uMS5jc3YiKQoKc2ltX2ZpbmFsIDwtIHNpbSB8PiBtdXRhdGUoWSA9IDUwICsgNSpBICs1KkIrIGVwc2lsb24pCmBgYAoKIyMKCmBgYHtyLCBmaWcuaGVpZ2h0PTV9CmdncGxvdChzaW1fZmluYWwpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PUEsIHk9Qiwgc2l6ZT1ZKSkKYGBgCgojIwoKVGhpcyBpcyB3aGF0IHJlc3VsdGVkIGluIHRoZSByZWxhdGl2ZWx5IGhpZ2ggc3RhbmRhcmQgZXJyb3JzIGZvciBlYWNoIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgKGdpdmVuIHRoZSBzdXBlciBoaWdoICRSXjIkKQoKYGBge3IgbG0gU2ltdWxhdGlvbjF9CmxtLmNvciA9IGxtKCBZIH4gQSArIEIsIGRhdGE9c2ltX2ZpbmFsKQpzdW1tYXJ5KGxtLmNvcikKYGBgCgojIwoKTm93IHN1cHBvc2Ugd2Uga2VlcCBleGFjdGx5IHRoZSBzYW1lIG51bWJlcnMgQSwgQiBhbmQgZXBzaWxvbiwgYnV0IGJyZWFrIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBBIGFuZCBCIGNvbHVtbnMuCgpBcyBhIHNpbXBsZSB3YXkgb2Ygc3ByZWFkaW5nIHRoZW0gb3V0LCB3ZSBqdXN0IHJhbmRvbWl6ZSB0aGUgb3JkZXIgb2YgbnVtYmVycyBpbiBjb2x1bW4gQiwgdGhlbiByZWNvbXB1dGUgWS4KCmBgYHtyIFNpbSByYW5kb21pemVkfQpzaW1fcmFuZCA8LSBzaW1fZmluYWwgfD4KICBtdXRhdGUoQl9yYW5kID0gc2FtcGxlKEIpKSB8PgogIG11dGF0ZShZX3JhbmQgPSA1MCArIDUqQSArIDUqQl9yYW5kICsgZXBzaWxvbikKYGBgCgojIwoKYGBge3J9CmdncGxvdChzaW1fcmFuZCkgKyAKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9QSwgeT1CX3JhbmQsIHNpemU9WV9yYW5kKSkKYGBgCgojIwpgYGB7cn0KbG0udW5jb3IgPSBsbSggWV9yYW5kIH4gQSArIEJfcmFuZCwgZGF0YT1zaW1fcmFuZCkKc3VtbWFyeShsbS51bmNvcikKYGBgClRoZSBzdGFuZGFyZCBlcnJvcnMgb2YgdGhlIGNvZWZmaWNpZW50cyBhcmUgYWJvdXQgMS8zIHdoYXQgdGhleSB3ZXJlLiAgCgpOb3RlIHRoYXQgcmVhbGx5IHRoZSBvbmx5IHRoaW5nIHdlIGhhdmUgZG9uZSBoZXJlIGlzIHRvIGJyZWFrIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuICRBJCBhbmQgJEIkLCBidXQgdGhpcyBoYXMgZ3JlYXRseSBpbXByb3ZlZCB0aGUgYW5hbHlzaXMuIAoKIyMgT3J0aG9nb25hbCBEYXRhCgpDb25zaWRlciB0aGUgbW9kZWwJJCRZICA9ICBcYmV0YV8wICsgXGJldGFfMSBYICArIFxiZXRhXzIgWiArIFx2YXJlcHNpbG9uJCQgIAoKUmVjYWxsIHRoYXQgaWYgJFgkICBhbmQgICRaJCBhcmUgdW5jb3JyZWxhdGVkLCB0aGV5IGFyZSBjYWxsZWQgb3J0aG9nb25hbC4gIFRoZW4gIAoKKgl0aGUgbGVhc3Qgc3F1YXJlcyBlc3RpbWF0ZXMgJFxoYXRcYmV0YV8xJCBhbmQgJFxoYXRcYmV0YV8yJCAgYXJlIGluZGVwZW5kZW50IG9mIGVhY2ggb3RoZXIKCioJdGhlIG9yZGVyIG9mIGFkZGluZyB0aGUgdmFyaWFibGVzIGRvZXMgbm90IGFmZmVjdCB0aGUgc3VtcyBvZiBzcXVhcmVzIGluIHRoZSBBTk9WQSB0YWJsZS4KCkluIG90aGVyIHdvcmRzICBpdCBkb2Vzbid0IG1hdHRlciB3aGV0aGVyIHdlIHRhbGsgYWJvdXQgdGhlIGVmZmVjdCBvZiAgJFgkIGJ5IGl0c2VsZiBvciAgIiRYJCBhZnRlciAkWiQiLiAgVGhlIGVmZmVjdCBpcyB0aGUgc2FtZS4gICAgCgoqCUludGVycHJldGF0aW9uIG9mIHBhcmFtZXRlcnMgYSBsb3QgZWFzaWVyLgoKKglCdXQgaXQgdXN1YWxseSBvbmx5IGhhcHBlbnMgaW4gZGVzaWduZWQgZXhwZXJpbWVudHMgd2l0aCBiYWxhbmNlZCBudW1iZXJzLgoKSW4gc3VtbWFyeSwgb3J0aG9nb25hbGl0eSBvZiB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzICBhbGxvd3MgZWFzaWVyIGludGVycHJldGF0aW9uIG9mIHRoZSByZXN1bHRzIG9mIGFuIGV4cGVyaW1lbnQgYW5kIGlzIGFuIGltcG9ydGFudCBnb2FsIGluIHRoZSBkZXNpZ24gb2YgZXhwZXJpbWVudHMuCgojIyBDb2xsaW5lYXIgZGF0YQoKU3VwcG9zZSB3ZSBoYXZlIHRocmVlIHNldHMgb2YgbnVtYmVycyAkWCxaLFkkIHN1Y2ggdGhhdCAKJCRZID0gXGJldGFfMCArXGJldGFfMSBYICArIFxiZXRhXzIgWiArXHZhcmVwc2lsb24kJAoKYnV0IHRoYXQsIHVua25vd24gdG8gdXMsCiQkWiA9IFxhbHBoYV8wICsgXGFscGhhXzEgWCQkCmF0IGxlYXN0IGVxdWFsIHVwIHRvIHNvbWUgbGV2ZWwgb2Ygcm91bmRpbmcgZXJyb3IuCgpUaGVuIHRoZSByZWdyZXNzaW9uIG1vZGVsIGNvdWxkIGJlIHJld3JpdHRlbiBpbiB0ZXJtcyBvZiBhbnkgYXJiaXRyYXJ5IGNvbWJpbmF0aW9uIG9mIHRoZSBYIGFuZCBaIGZpZ3VyZXMsIGFzIHdlIGNhbiBhbHdheXMgYWRkIG9uICRrJCBsb3RzIG9mICRcYWxwaGFfMCArIFxhbHBoYV8xIFgkIGFuZCB0aGVuIHRha2Ugb2ZmICRrJCBsb3RzIG9mICRaJCAoYXMgdGhleSdyZSB0aGUgc2FtZSB0aGluZyk6CgokJFxiZWdpbnthbGlnbmVkfVkgJj0gXGJldGFfMCArXGJldGFfMSBYICsgXGJldGFfMiBaICsgayhcYWxwaGFfMCArIFxhbHBoYV8xIFgpIC0gayBaICArXHZhcmVwc2lsb25cXCAmID0gKFxiZXRhXzAgKyBrXGFscGhhXzApICsgKFxiZXRhXzEgKyBrXGFscGhhXzEpWCArIChcYmV0YV8yIC0gaylaICsgXHZhcmVwc2lsb25cZW5ke2FsaWduZWR9JCQKCmZvciBhbnkgcmVhbCBudW1iZXIgJGskLgoKVGhlcmUgYXJlIGFuIGluZmluaXRlIG51bWJlciBvZiBwb3NzaWJsZSBzb2x1dGlvbnMsIGFuZCBpZiB3ZSBkbyBnZXQgYSBudW1lcmljYWwgYW5zd2VyIGZvciB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMsIHRoZSBwYXJ0aWN1bGFyIGFuc3dlciBtYXkgZXZlbiBqdXN0IGRlcGVuZCBvbiByb3VuZGluZywgd2l0aCBkaWZmZXJlbnQgYW1vdW50cyBvZiByb3VuZGluZyBjb3VsZCBnaXZlIHZlcnkgZGlmZmVyZW50IGFuc3dlcnMuCgojIwoKV2UgZGVzY3JpYmUgdGhpcyBzaXR1YXRpb24gYnkgc2F5aW5nIHRoZSByZWdyZXNzaW9uIG1vZGVsIGlzICpub3QgZXN0aW1hYmxlKi4gIAoKQSBjb25zZXF1ZW5jZSBvZiBzdWNoIGhpZ2ggY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgcHJlZGljdG9yIHZhcmlhYmxlcyBpcyB0aGF0IHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBtYXkgYmVjb21lIHRvdGFsbHkgdW5pbnRlcnByZXRhYmxlICAoZm9yIGV4YW1wbGUgc29tZXRoaW5nIHlvdSAgKmtub3cqIGhhcyBhIHBvc2l0aXZlIHJlbGF0aW9uc2hpcCB3aXRoIHRoZSByZXNwb25zZSBlbmRzIHVwIHdpdGggYSBuZWdhdGl2ZSBjb2VmZmljaWVudCkgIGFuZCAgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIG1heSBoYXZlIHZlcnkgIGhpZ2ggc3RhbmRhcmQgZXJyb3JzLgoKVGhpcyBpcyB0aGUgcHJvYmxlbSBvZiAqKk11bHRpY29sbGluZWFyaXR5KiouCgpIb3cgZG8gd2UgYXNzZXNzIHdoZXRoZXIgaXQgaXMgYSByZWFsIHByb2JsZW0gaW4gb3VyIGRhdGE/CgojIyAgQ29ycmVsYXRpb25zIEFtb25nIFRoZSBQcmVkaWN0b3JzCgpXZSBjb3VsZCBsb29rIGF0IHRoZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzLiAgU2luY2UgY29ycmVsYXRpb24gaXMgYSBtZWFzdXJlIG9mIGxpbmVhciByZWxhdGlvbnNoaXAsIHRoZW4gaWYgdHdvIHZhcmlhYmxlcyBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQgdGhlbiBpdCBkb2Vzbid0IG1ha2Ugc2Vuc2UgdG8ga2VlcCB0aGVtIGJvdGggaW4gdGhlIG1vZGVsLgoKRXhhbXBsZS4gIENvbnNpZGVyIHRoZSBkYXRhIGJlbG93IG9uIGhvdXNlaG9sZCBlbGVjdHJpY2l0eSBiaWxscyB3aXRoIGV4cGxhbmF0b3J5IHZhcmlhYmxlcwoKKiB0aGUgaW5jb21lIG9mIHRoZSBob3VzZWhvbGQ7IAoqIHRoZSBudW1iZXIgb2YgcGVyc29ucyBpbiB0aGUgaG91c2U7IGFuZAoqIHRoZSBhcmVhIG9mIHRoZSBob3VzZS4gCgpFYWNoIG9mIHRoZXNlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyB3b3VsZCBiZSBleHBlY3RlZCB0byBiZSByZWxhdGVkIHRvIHRoZSBCaWxsLCBhbmQgeWV0IHRoZXkgYXJlIGFsc28gcmVsYXRlZCB0byBlYWNoIG90aGVyLCBzbyB3ZSBtYXkgbm90IGJlIGFibGUgdG8gaGF2ZSB0aGVtIGFsbCBpbiB0aGUgc2FtZSBtb2RlbC4gICAKCmByIHhmdW46OmVtYmVkX2ZpbGUoIi4uLy4uL2RhdGEvZWxlY3RyaWMuY3N2IilgIAoKYGBge3IgcmVhZCBlbGVjdHJpYywgZXZhbD0tMSwgZWNobz0tMiwgbWVzc2FnZT1GQUxTRX0KZWxlY3RyaWMgPSByZWFkX2NzdigiZWxlY3RyaWMuY3N2IikKZWxlY3RyaWMgPSByZWFkX2NzdigiLi4vLi4vZGF0YS9lbGVjdHJpYy5jc3YiKQplbGVjdHJpYwpgYGAKCiMjCgpXZSBjb3VsZCBjaGVjayBhbGwgcGFpci13aXNlIGNvcnJlbGF0aW9ucyB3aXRoIHRoZSBgY29yKClgIGZ1bmN0aW9uOgoKYGBge3IgZWxlY3RyaWMgY29yfQpjb3IoZWxlY3RyaWMpCmBgYAoKVGhpcyBpbmRpY2F0ZXMgdGhhdCB0aGUgYEFyZWFgIG9mIHRoZSBob3VzZSBpcyB2ZXJ5IHN0cm9uZ2x5IGNvcnJlbGF0ZWQgdG8gdGhlIGBJbmNvbWVgIG9mIHRoZSBvY2N1cGFudHMgKCRyPSAwLjk2MSQpLCBhbmQgc28gYm90aCBtYXkgc2F5IG11Y2ggdGhlIHNhbWUgdGhpbmcgYWJvdXQgdGhlIGVsZWN0cmljaXR5IGJpbGwuICBXZSBtYXkgbm90IGJlIGFibGUgdG8gaGF2ZSBib3RoIHRoZXNlIGluIHRoZSBtb2RlbC4gIAoKQnkgY29udHJhc3QgdGhlIG51bWJlciBvZiBwZXJzb25zIGluIHRoZSBob3VzZSBpcyBhbG1vc3QgdW5yZWxhdGVkIChyICA9ICAwLjE0MykgdG8gdGhlIGBJbmNvbWVgIGFuZCBvbmx5IHdlYWtseSByZWxhdGVkIHRvIHRoZSBgQXJlYWAgKHIgPSAwLjM2Nikgc28gcGVyc29ucyBwcm92aWRlcyBtb3JlLW9yLWxlc3MgaW5kZXBlbmRlbnQgaW5mb3JtYXRpb24uCgpBIHBhaXJzIHBsb3QgY2FuIGJlIHVzZWZ1bCBpbiB0aGVzZSBjaXJjdW1zdGFuY2VzLgoKIyMKCmBgYHtyICBlbGVjdHJpYyBwYWlyc30KcGFpcnMoZWxlY3RyaWMpCmBgYAoKQnV0IGhvdyBkbyB3ZSBrbm93IHRoaXMgaXMgY2F1c2luZyByZWFsIHByb2JsZW1zPyAKCiMjIFNwb3R0aW5nIG11bHRpY29sbGluZWFyaXR5CgpgYGB7ciBsbX0KZWxlY3RyaWMubG0wID0gbG0oQmlsbH4gQXJlYSsgUGVyc29ucyArIEluY29tZSwgZGF0YT1lbGVjdHJpYykKc3VtbWFyeShlbGVjdHJpYy5sbTApCmBgYAoKVGhlIHJlZ3Jlc3Npb24gYXMgYSB3aG9sZSBpcyBoaWdobHkgc2lnbmlmaWNhbnQsIHlldCBub25lIG9mIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNlbnRzIGFyZSBzaWduaWZpY2FudC4gVGhpcyBzdWdnZXN0cyBtdWx0aWNvbGxpbmVhcml0eS4KCiMjIENvbnRyYWRpY3RvcnkgUmVncmVzc2lvbiBDb2VmZmljaWVudHMKCkxldCdzIGxvb2sgYXQgdGhlIHZhcmlhYmxlIHdlIGtub3cgYXJlIGhpZ2hseSBjb3JyZWxhdGVkIChpLmUuIGBBcmVhYCBhbmQgYEluY29tZWApIHNlcGFyYXRlbHkgYXMgcHJlZGljdG9yczoKCmBgYHtyIHNpbmdsZSB2YXJpYWJsZXN9CmVsZWMubG0xID0gbG0oQmlsbCB+IEFyZWEsIGRhdGE9ZWxlY3RyaWMpCmVsZWMubG0yPSBsbShCaWxsIH4gSW5jb21lLCBkYXRhPWVsZWN0cmljKQpzdW1tYXJ5KGVsZWMubG0xKQpgYGAKCiMjCgpgYGB7cn0Kc3VtbWFyeShlbGVjLmxtMikKYGBgCgpCeSB0aGVtc2VsdmVzIGJvdGggdmFyaWFibGVzIGFyZSBwb3NpdGl2ZWx5IHJlbGF0ZWQgdG8gQmlsbCBhbmQgaGF2ZSBzbWFsbCBTdGQgRXJyb3JzLCBhcyB3ZSB3b3VsZCBleHBlY3QgZ2l2ZW4gdGhlIHBhaXJzIGNoYXJ0LgoKQnV0IGxldCdzIHNlZSB3aGF0IGhhcHBlbnMgaWYgd2UgcHV0IHRoZW0gdG9nZXRoZXI6CgojIwoKYGBge3IgY29ycmVsYXRlZCB2YXJpYWJsZXN9CmVsZWMubG0zIDwtIGxtKEJpbGwgfiBBcmVhICsgSW5jb21lLCBkYXRhPWVsZWN0cmljKQpzdW1tYXJ5KGVsZWMubG0zKQpgYGAKCldoZW4gcHV0IHRvZ2V0aGVyLCBvbmUgcHJlZGljdG9yIGhhcyBjaGFuZ2VkIGl0cyBzaWduIGFuZCB0aGUgU3RkIEVycm9ycyBhcmUgbXVjaCBsYXJnZXIuIAoKVGhpcyBpbGx1c3RyYXRlcyBob3cgbXVsdGljb2xsaW5lYXJpdHkgY2FuIHJlbmRlciB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgcXVpdGUgbWlzbGVhZGluZywgYXMgcHJlc3VtYWJseSBpbmNvbWUgSVMgcmVsYXRlZCB0byBoaWdoZXIgZWxlY3RyaWNpdHkgYmlsbHMgKGFzIGZvbGsgb24gaGlnaGVyIGluY29tZXMgY2FuIGFmZm9yZCB0byBoYXZlIG1vcmUgc3R1ZmYgdGhhdCBjb25zdW1lcyBlbmVyZ3kpLgoKIyMKCk9uZSBzb2x1dGlvbiB0byBtdWx0aWNvbGxpbmVhcml0eSBpcyB0byBkZWxldGUgb25lIG9mIHRoZSB2YXJpYWJsZXMuICBIb3dldmVyIGl0IGNhbiBiZSBkaWZmaWN1bHQgdG8gZGVjaWRlIHdoaWNoIG9uZSB0byBkZWxldGUuICAgRm9yIGV4YW1wbGUgc3VwcG9zZSB3ZSBwYXJ0aWN1bGFybHkgd2FudCB0byBxdWFudGlmeSB0aGUgZWZmZWN0IG9mICpQZXJzb25zKiBvbiB0aGUgZWxlY3RyaWNpdHkgYmlsbCBhZnRlciBhZGp1c3RpbmcgZm9yIG9uZSBvZiB0aGUgb3RoZXJzOgoKYGBge3IgcGFpciBvZiB2YXJpYWJsZXN9CmVsZWMubG00ID0gbG0oQmlsbCB+IEFyZWEgKyBQZXJzb25zLCBkYXRhPWVsZWN0cmljKQplbGVjLmxtNT0gbG0oQmlsbCB+IEluY29tZSArIFBlcnNvbnMsIGRhdGE9ZWxlY3RyaWMpCnN1bW1hcnkoZWxlYy5sbTQpCmBgYAoKIyMKCmBgYHtyfQpzdW1tYXJ5KGVsZWMubG01KQpgYGAKClNob3VsZCB3ZSBlc3RpbWF0ZSB0aGF0IGVhY2ggZXh0cmEgcGVyc29uIGFkZHMgNDIgdG8gdGhlIGVsZWN0cmljIGJpbGwsIG9yIDgzIHRvICB0aGUgYmlsbD8gIE9mIGNvdXJzZSB0aGUgaW50ZXJwcmV0YXRpb24gb2YgdGhlc2UgcmVncmVzc2lvbiBtb2RlbHMgZGlmZmVycyBzbGlnaHRseSAoaW4gdGhlIGZpcnN0IG1vZGVsIGl0IGlzIHRoZSBlZmZlY3Qgb2YgZWFjaCBhZGRpdGlvbmFsIHBlcnNvbiBhbW9uZyBmYW1pbGllcyBvZiB0aGUgc2FtZSBpbmNvbWUsIHdoaWxlIGluIHRoZSBzZWNvbmQgaXQgaXMgdGhlIGVmZmVjdCBvZiBwZXJzb24gYW1vbmcgZmFtaWxpZXMgb2Ygd2l0aCBob21lcyBvZiB0aGUgc2FtZSBhcmVhKS4gQnV0IGl0IHN0aWxsIG1ha2VzIG9uZSB1bmVhc3kgdG8gc2VlIHN1Y2ggYSBiaWcgZGlmZmVyZW5jZS4gIAoKIyMgUmVncmVzc2luZyBUaGUgUHJlZGljdG9ycyBBbW9uZyBUaGVtc2VsdmVzCgpNb3JlIGdlbmVyYWxseSAobW9yZSB0aGFuIGp1c3QgbG9va2luZyBhdCBpbmRpdmlkdWFsIGNvcnJlbGF0aW9ucykgd2UgY291bGQgbG9vayBhdCB3aGV0aGVyIGFueSBvZiB0aGUgdmFyaWFibGVzIGFyZSBzdHJvbmdseSBwcmVkaWN0ZWQgYnkgYSBjb21iaW5hdGlvbiBvZiB0aGUgb3RoZXIgcmVncmVzc29ycy4KCmkuZS4gIFdoZXRoZXIgdGhlICRSXjIkICBmb3IgcmVncmVzc2luZyB2YXJpYWJsZSAkWF9pJApvbiB0aGUgb3RoZXIgcmVncmVzc29ycyAgCiRYXzEsIFhfMiwuLi4sIFhfe2ktMX0sIFhfe2krMX0sLi4uLFh7cH0kIGlzIGxhcmdlLiAgV2UgY2FsbCB0aGlzICRSXjJfaSQuICAKCklmIGl0IGlzIGxhcmdlIHRoZW4gd2UgcHJvYmFibHkgZG9u4oCZdCBuZWVkICRYX2kkICAob3Igc29tZSBvdGhlciB2YXJpYWJsZXMpIGluIHRoZSBtb2RlbC4KClRoaXMgaXMgbW9yZSBnZW5lcmFsIHRoYW4ganVzdCBsb29raW5nIGF0IHNpbXBsZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiB2YXJpYWJsZXMsIHNpbmNlIHRoZSByZWdyZXNzaW9uIG1vZGVsIHBpY2tzIHVwIG9uIGNvbWJpbmF0aW9ucyBvZiB2YXJpYWJsZXMuCgpBICJydWxlIG9mIHRodW1iIiBpcyB0aGF0IG11bHRpY29sbGluZWFyaXR5IGlzIGEgYmlnIHByb2JsZW0gaWYgJFJfaV4yID4gMC45JCwgIGkuZS4gIHRoZSAkaSR0aCB2YXJpYWJsZSBpcyBtb3JlIHRoYW4gOTAlIGV4cGxhaW5lZCBieSB0aGUgb3RoZXIgcHJlZGljdG9ycy4KCldlIGNhbiBnZXQgJFJfaV4yJCBieSBkb2luZyB0aGUgcmVncmVzc2lvbnMgb3Vyc2VsdmVzLCBidXQgaXQgaXMgZWFzaWVyIHRvIGNvbnNpZGVyIHRoZSBjb25jZXB0IG9mIHZhcmlhbmNlIGluZmxhdGlvbiBpbnN0ZWFkLgoKIyMgVmFyaWFuY2UgSW5mbGF0aW9uIEZhY3RvcnMsIFZJRnMKCkEgbWFqb3IgaW1wYWN0IG9mIG11bHRpY29sbGluZWFyaXR5IGlzIGZvdW5kIGluIHRoZSB2YXJpYW5jZXMgKGFuZCBoZW5jZSBzdGFuZGFyZCBlcnJvcnMpIG9mIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cy4KCjEuICBJZiAkWF9pJCAgIGlzIGFsb25lICBpbiB0aGUgbW9kZWwJICQkWSAgPVxiZXRhXzAgK1xiZXRhXzEgWF9pICtcdmFyZXBzaWxvbn4sfn5+fn5cbWJveHt0aGVufSQkCgokJFxoYXR7XG1ib3h7dmFyfX0oXGhhdFxiZXRhXzEpID0gXGZyYWN7c14yfXsobi0xKVxtYm94e3Zhcn0oWF9pKX0kJAoyLiBJZiAkWF9pJCAgIGlzIGluIGEgbW9kZWwgd2l0aCBvdGhlciBwcmVkaWN0b3JzIHRoZW4gCiQkXGhhdHtcbWJveHt2YXJ9fShcaGF0XGJldGFfaSkgPSAgc14yICAgXGxlZnQoWF5UIFhccmlnaHQpXnstMX1fe2lpfSQkCndoZXJlIHRoZSBsYXN0IHRlcm0gaXMgdGhlIGNvcnJlc3BvbmRpbmcgZGlhZ29uYWwgZW50cnkgb2YgdGhlICQoWF5UIFggKV57LTF9JCBtYXRyaXgsICB0aGUgKnZhcmlhbmNlLWNvdmFyaWFuY2UqIG1hdHJpeC4KCiMjCgpJdCB0dXJucyBvdXQgdGhhdCB3ZSBjYW4gcmV3cml0ZSB0aGlzIGFzOgoKJCRcaGF0e1xtYm94e3Zhcn19KFxoYXRcYmV0YV9pKSA9IFxmcmFje3NeMn17KG4tMSlcbWJveHt2YXJ9KFhfaSl9XGNkb3QgXGZyYWN7MX17MSAtIFJfaV4yfSQkCndoZXJlICRSX2leMiQgaXMgdGhlIG11bHRpcGxlICRSXjIkIG9mIHRoZSByZWdyZXNzaW9uIG9mICRYX2kkIG9uIHRoZSBvdGhlciBwcmVkaWN0b3JzLgoKVGh1cywgd2hlbiBnb2luZyBmcm9tIGEgbW9kZWwgd2l0aCBqdXN0ICRYX2kkIHRvIGEgZnVsbCBtb2RlbCwgdGhlIHZhcmlhbmNlIG9mIHRoZSBjb2VmZmljZW50IG9mICRYX2kkICoqaW5mbGF0ZXMqKiBieSBhIGZhY3RvciBvZgoKJCRcbWJveHtWSUZ9X2kgPSBcZnJhY3sxfXsxIC0gUl9pXjJ9JCQKClZhcmlhYmxlIGluZmxhdGlvbiBmYWN0b3JzIHRoYXQgYXJlIGxhcmdlIChlLmcuICRcbWJveHtWSUZ9X2kgPiAxMCQpIGluZGljYXRlcyBtdWx0aWNvbGxpbmVhcml0eSBwcm9ibGVtcy4KCk9uIHRoZSBvdGhlciBoYW5kIGlmICB0aGUgVklGIGlzIGFyb3VuZCAxLCB0aGVuIHRoZSB2YXJpYWJsZSAkWF9pJCBpcyBwcmV0dHkgbXVjaCBvcnRob2dvbmFsIHRvIHRoZSBvdGhlciB2YXJpYWJsZXMuCgojIwoKVG8gb2J0YWluIFZJRnMgd2UgbmVlZCB0byBpbnZva2UgYGxpYnJhcnkoY2FyKWAuCgpgYGB7ciB2aWZzMX0KbGlicmFyeShjYXIpCmBgYAoKYGBge3J9CnZpZiggZWxlY3RyaWMubG0wKQpgYGAKCkNsZWFybHkgd2UgbmVlZCB0byBkbyBzb21ldGhpbmcgdG8gcmVkdWNlIHRoZSBtdWx0aWNvbGxpbmVhcml0eS4KCldoYXQgd2UgZG8gaXMgYXBwbGljYXRpb24tc3BlY2lmaWMhIFdlIG1pZ2h0IGNob29zZSB0byByZW1vdmUgYSB2YXJpYWJsZSwgb3Igd2UgbWlnaHQgY2hvb3NlIHRvIGRvIHNvbWV0aGluZyBlbHNlIChlLmcuIGZpbmQgYSBiZXR0ZXIgbWVhc3VyZSBvciBzb21laG93IGNvbWJpbmUgdGhlIHZhcmlhYmxlcyBpbnRvIHNvbWUgYXZlcmFnZT8pCgojIyBFeGFtcGxlOiBTQVAgRGF0YQoKVGhlIGZvbGxvd2luZyBleGFtcGxlIGlsbHVzdHJhdGVzIG11bHRpY29sbGluZWFyaXR5IGluIGEgY29udGV4dCB3aGVyZSBpdCBpcyBub3QgaW1tZWRpYXRlbHkgdmlzaWJsZS4gIENoYXR0ZXJqZWUgYW5kIFByaWNlICBkaXNjdXNzIGEgZGF0YXNldCByZWxhdGluZyAgU2FsZXMgYFNgIHRvICBgQWAgKGFkdmVydGlzaW5nIGV4cGVuZGl0dXJlKSwgIGBBMWAgKGxhc3QgeWVhcidzIGFkdmVydGlzaW5nKSwgIGBQYCAocHJvbW90aW9uIGV4cGVuZGl0dXJlKSwgYFAxYCAobGFzdCB5ZWFy4oCZcyBwcm9tb3Rpb25zIGV4cGVuZGl0dXJlKSBhbmQgYFNFYCAoc2FsZXMgZXhwZW5zZXMpLiAgVGhlIGFuYWx5c3QgZXZlbnR1YWxseSBmb3VuZCBvdXQgdGhhdCBhbiBhcHByb3hpbWF0ZSBidWRnZXRhcnkgY29uc3RyYWludCAKCiQkIEEgKyBBMSArIFAgKyBQMSAgPSAgNSAgJCQKd2FzIGhpZGRlbiBpbiB0aGUgZGF0YS4gIFRoZSBjb25zdHJhaW50IHdhcyBrbm93biB0byB0aGUgYWR2ZXJ0aXNpbmcgZXhlY3V0aXZlcyB3aG8gaGFkIGNvbW1pc3Npb25lZCB0aGUgc3R1ZHkgYnV0IGl0IGhhZCBub3QgYmVlbiBtZW50aW9uZWQgdG8gdGhlIHN0YXRpc3RpY2FsIGNvbnN1bHRhbnQgd2hvIHdhcyBnaXZlbiB0aGUgZGF0YSB0byBhbmFseXplLiAgCgpgciB4ZnVuOjplbWJlZF9maWxlKCIuLi8uLi9kYXRhL1NBUC5jc3YiKWAgCgpgYGB7ciAgcmVhZCBTQVAsIGV2YWw9LTEsIGVjaG89LTIsIG1lc3NhZ2U9RkFMU0V9ClNBUCA9IHJlYWRfY3N2KCJTQVAuY3N2IikKU0FQID0gcmVhZF9jc3YoIi4uLy4uL2RhdGEvU0FQLmNzdiIpClNBUApgYGAKCiMjCgpXZSBzdGFydCB3aXRoIGEgbGluZWFyIG1vZGVsIGZvciBzYWxlcyBpbiB0ZXJtcyBvZiBhbGwgb3RoZXIgdmFyaWFibGVzOgoKYGBge3Igc2FwIGZ1bGx9ClNBUC5sbSA9IGxtKCBTIH4gQSArIFAgKyBTRSArIEExICsgUDEsIGRhdGE9U0FQKQpzdW1tYXJ5KFNBUC5sbSkKYGBgCgojIwoKV2Ugc2VlIHRoYXQsIGFwYXJ0IGZyb20gU2FsZXMgRXhwZW5zZXMsIHRoZXJlIGlzIG9ubHkgd2VhawpldmlkZW5jZSBvZiBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGFkdmVydGlzaW5nLCBwcm9tb3Rpb25zIGFuZCBzYWxlcy4gCgpgYGB7ciAgdmlmIFNBUH0KdmlmKFNBUC5sbSkKYGBgCgpUaGUgVklGIHNob3dzIGhpZ2ggbXVsdGljb2xsaW5lYXJpdHkgZm9yIGFsbCBhZHZlcnRpc2luZyBhbmQgcHJvbW90aW9ucyBkYXRhLiAKCiMjCgpJZiB3ZSBkcm9wIG9uZSBvZiB0aGUgdmFyaWFibGVzIHRoZSBvdGhlcnMgY2hhbmdlIHRoZWlyIGNvZWZmaWNpZW50cyB1bnByZWRpY3RhYmx5ICBkZXBlbmRpbmcgb24gd2hpY2ggdmFyaWFibGUgd2UgZHJvcC4gCgpgYGB7ciBkcm9wcGluZ30KU0FQLmxtMj0gbG0oIFMgfiAgUCArIFNFICtBMSArIFAxLCBkYXRhPVNBUCkKU0FQLmxtMz0gbG0oIFMgfiBBICtQICtTRSAgKyBQMSwgZGF0YT1TQVApClNBUC5sbTIgfD4gdGlkeSgpClNBUC5sbTMgfD4gdGlkeSgpCmBgYAoKIyMKClN1cHBvc2Ugd2UgbG9vayBhdCB0aGUgcmVsYXRpb25zaGlwcyBhbW9uZyB0aGUgcHJlZGljdG9yIHZhcmlhYmxlczogCgpgYGB7ciBsb29raW5nIGlufQpTQVBBLmxtID0gbG0oIEEgfiBQICsgU0UgKyBBMSArIFAxLCBkYXRhPVNBUCkKU0FQQS5sbSB8PiB0aWR5KCkKYGBgCgpUaGlzIGlzIGFwcHJveGltYXRlbHkgQSA9IDUgLSBQIC1BMSAtIFAxIGFzIGluIHRoZSBoaWRkZW4gY29uc3RyYWludC4KCiMjIFdoYXQgZG8gd2UgZG8gYWJvdXQgbXVsdGljb2xsaW5lYXJpdHk/CgpBcyBmYXIgYXMgMTYxLjI1MSBpcyBjb25jZXJuZWQsIHdlIGhhdmUgZmV3IG9wdGlvbnMuIAoKMS4gV2UgY2FuIHJlbW92ZSBvbmUgKG9yIG1vcmUpIG9mIHRoZSBjb3JyZWxhdGVkIHZhcmlhYmxlcyBmcm9tIHRoZSByZWdyZXNzaW9uLiBPdXIgYWltIGlzIHRvIGZpeCB0aGUgcHJvYmxlbSwgYnV0IHNvbWV0aW1lcyB3ZSBoYXZlIGEgYml0IG9mIGNob2ljZSB3aGljaCB2YXJpYWJsZSB0byByZW1vdmUsIGluIHdoaWNoIGNhc2Ugd2Ugd291bGQgbGlrZSB0aGUgbW9kZWwgdG8gYmUgYXMgIGludGVycHJldGFibGUgYXMgcG9zc2libGUuICAgIFNvbWV0aW1lcyB0aGUgY2hvaWNlIGlzIGJldHdlZW4gYSB2YXJpYWJsZSB3aXRoIG1hbnkgbWlzc2luZyB2YWx1ZXMgYW5kIG9uZSB3aXRoIGZldywgYW5kIHNvIHdlIGNob29zZSB0aGUgbGF0dGVyIGlmIGl0IGFsbG93cyB1cyBhIGJpZ2dlciBlZmZlY3RpdmUgc2FtcGxlIHNpemUgJG4kLgoKMi4gIFdlIGNvdWxkIGNvbnN0cnVjdCBhbm90aGVyIHZhcmlhYmxlIHdoaWNoIGlzIGEgY29tYmluYXRpb24gb2YgdGhlIHR3byBtb3N0IGhpZ2hseSBjb3JyZWxhdGVkIHZhcmlhYmxlcy4gIEl0IG1pZ2h0IGJlIGFuIG9yZGluYXJ5IGF2ZXJhZ2UgJChYXzErWF8yKS8yJCwgb3IgYW4gYXZlcmFnZSBvZiBzdGFuZGFyZGl6ZWQgdmVyc2lvbnMgb2YgdGhlICRYX2kkcyBvciBzb21lIG90aGVyIGxpbmVhciBjb21iaW5hdGlvbi4gU29tZXRpbWVzIHN1YnRyYWN0aW5nIG9yIGRpdmlkaW5nIG9uZSB2YXJpYWJsZSBieSBhbm90aGVyIHdpbGwgaGVscC4gICAgRm9yIGV4YW1wbGUgdGhlIGluY2lkZW5jZSBvZiBhIHBhcnRpY3VsYXIgZGlzZWFzZSBzdWNoIGFzIG9iZXNpdHkgbWF5IGJlIGhpZ2hlciBpbiBjb3VudHJpZXMgd2l0aCBoaWdoIEdEUCBhbmQgaGlnaCBQb3B1bGF0aW9uLiAgIEJ1dCBHRFAgZGVwZW5kcyBvbiB0aGUgc2l6ZSBvZiB0aGUgZWNvbm9teSwgd2hpY2ggaXMgY29ycmVsYXRlZCB3aXRoIHRoZSBQb3B1bGF0aW9uLiAgRGl2aWRpbmcgZ2l2ZXMgdXMgcGVyIGNhcGl0YSBHRFAgd2hpY2ggaXMgYSBiZXR0ZXIgaW5kaWNhdG9yIG9mIGF2ZXJhZ2Ugd2VhbHRoIGFuZCBoZW5jZSBtYXkgYmUgbW9yZSByZWxhdGVkIHRvIGRpc2Vhc2UgaW5jaWRlbmNlLiAKCiMjCgo8b2wgc3RhcnQ9Mz4KPGxpPiAoTm90IGV4YW1pbmFibGUpIFRob3NlIHdobyBoYXZlIHN0dWRpZWQgbXVsdGl2YXJpYXRlIHN0YXRpc3RpY3MgbWF5IGZpbmQgaXQgdXNlZnVsIHRvIGNvbnN0cnVjdCBwcmluY2lwYWwgY29tcG9uZW50cyAgb3IgZG8gYSBmYWN0b3IgYW5hbHlzaXMgb2YgYSBzZXQgb2YgY29ycmVsYXRlZCB2YXJpYWJsZXMuICBEcm9wcGluZyBsZXNzIGltcG9ydGFudCBjb21wb25lbnRzIGxlYWRzIHRvIGEgY2xhc3Mgb2YgbW9kZWxzIGNhbGxlZCBiaWFzZWQgcmVncmVzc2lvbi4gIEFub3RoZXIgZm9ybSBvZiBiaWFzZWQgcmVncmVzc2lvbiBzaHJpbmtzIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyB0b3dhcmRzIHplcm8gLSBhIG1ldGhvZCBjYWxsZWQgc2hyaW5rYWdlIGVzdGltYXRvcnMuPC9saT4KCjxsaT4gKE5vdCBleGFtaW5hYmxlKSBJdCBpcyBwb3NzaWJsZSB0byBmaXQgcmVncmVzc2lvbiBtb2RlbHMgdGhhdCBpbXBvc2UgbGluZWFyIGNvbnN0cmFpbnRzIG9uIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyAoc3VjaCBhcyBmb3JjaW5nIHRoZW0gdG8gYWRkIHRvIDEwMCUpLiBTdWNoIHJlc3RyaWN0ZWQgbGVhc3Qgc3F1YXJlcyBpcyBhbHNvIGJleW9uZCB0aGUgc2NvcGUgb2YgdGhpcyBjb3Vyc2UuIApUaGVyZWZvcmUgZm9yIHRoaXMgY291cnNlIHdlIGZvY3VzIG9uIG1ldGhvZCAxLCBkcm9wcGluZyB2YXJpYWJsZXMuIDwvbGk+Cjwvb2w+CgoKCg==