View the latest recording of this lecture

We have seen that p-values for coefficients in a polynomial regression model will change depending upon what terms are included in the model.

This can be explained in terms of correlations between parameter estimates, described by their covariance matrix.

In this lecture we will discuss the construction of orthogonal polynomials, whose regression coefficients are uncorrelated.

Covariance Matrix for Parameter Estimates

We can explore the dependencies between estimates of individual regression coefficients using two matrices.

First, the covariance matrix for the regression parameter estimates which is given by \[\mbox{Var} \left( \hat{\boldsymbol{\beta}} \right) = \left ( \begin{array}{cccc} \mbox{Var} \left( \hat{\beta_0} \right) & \mbox{Cov} \left(\hat{\beta_0}, \hat{\beta_1}\right) & \ldots & \mbox{Cov} \left(\hat{\beta_0}, \hat{\beta_p} \right) \\ \mbox{Cov} \left(\hat{\beta_1}, \hat{\beta_0} \right) & \mbox{Var} \left( \hat{\beta_1} \right) & \ldots & \mbox{Cov} \left(\hat{\beta_1}, \hat{\beta_p} \right) \\ \vdots & \vdots & \ddots & \vdots \\ \mbox{Cov} \left(\hat{\beta_p}, \hat{\beta_0} \right) & \mbox{Cov} \left(\hat{\beta_p}, \hat{\beta_1} \right) & \ldots & \mbox{Var} \left(\hat{\beta_p} \right) \end{array} \right ) = \sigma^2 (X^T X)^{-1}\]

Where \(X\) is the design matrix and \(\sigma^2\) is the estimated standard error of the residuals.

Correlation Matrix for Parameter Estimates

From this, we can compute the second useful matrix: \[\mbox{Corr}\left( \hat{\boldsymbol{\beta}}\right) = \left ( \begin{array}{cccc} 1 & \mbox{Corr} \left(\hat{\beta_0}, \hat{\beta_1} \right) & \ldots & \mbox{Corr} \left(\hat{\beta_0}, \hat{\beta_p} \right) \\ \mbox{Corr} \left(\hat{\beta_1}, \hat{\beta_0} \right) & 1 & \ldots & \mbox{Corr} \left(\hat{\beta_1}, \hat{\beta_p} \right) \\ \vdots & \vdots & \ddots & \vdots \\ \mbox{Corr} \left(\hat{\beta_p}, \hat{\beta_0} \right) & \mbox{Corr} \left(\hat{\beta_p}, \hat{\beta_1} \right) & \ldots & 1 \end{array} \right )\]

following the definition of the correlation: \[cor(X, Y) = \frac{cov(X,Y)}{\sigma_X\sigma_Y}.\]

Crowds for the Brisbane Lions

This Data has the number of members and average crowds sizes for the Brisbane Lions AFL club from 1993 to 2003.

Variables Description
Year Calendar year
Members Number of Lions members
Crowd Average home crowd size

The aim of this Example is to regress Crowd on Year.

Brisbane Lions?
Brisbane Lions?

We first import, check and then plot the data.

Download lions.csv

## Lions <- read.csv(file = "lions.csv", header = TRUE)
str(Lions)
'data.frame':   11 obs. of  3 variables:
 $ Year   : int  1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 ...
 $ Members: int  5750 6158 6893 10267 16769 16108 16931 20295 18330 22288 ...
 $ Crowd  : int  11097 12437 10318 18672 19550 16669 22416 27283 28369 27565 ...
plot(Crowd ~ Year, data = Lions)

unlabelled

and then we can fit the model for the relationship of interest…

Lions.lm1 <- lm(Crowd ~ Year, data = Lions)
summary(Lions.lm1)

Call:
lm(formula = Crowd ~ Year, data = Lions)

Residuals:
    Min      1Q  Median      3Q     Max 
-3856.1  -904.3   503.5  1355.8  2462.1 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -4270960.9   443882.4  -9.622 4.93e-06 ***
Year            2147.9      222.2   9.668 4.74e-06 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2330 on 9 degrees of freedom
Multiple R-squared:  0.9122,    Adjusted R-squared:  0.9024 
F-statistic: 93.47 on 1 and 9 DF,  p-value: 4.736e-06

As expected from the scatter plot, there is overwhelming evidence the Lion’s home crowds are increasing through time.

What does the intercept mean?

The covariance matrix for the estimates of the regression intercept and slope, \(\hat{\beta_0}\) and \(\hat{\beta_1}\), can be extracted using R’s vcov() command.

We can convert this covariance matrix to its corresponding correlation matrix using the cov2cor() command.

vcov(Lions.lm1)
             (Intercept)         Year
(Intercept) 197031551082 -98614142.90
Year           -98614143     49356.43
cov2cor(vcov(Lions.lm1))
            (Intercept)       Year
(Intercept)   1.0000000 -0.9999987
Year         -0.9999987  1.0000000

Notice the dramatic negative correlation between \(\hat{\beta_0}\) and \(\hat{\beta_1}\).

This makes sense: if we change the slope of the regression line even a little bit, this will have a big impact on the value of the intercept (especially since the values of the explanatory variables are so far from 0).

Collinearity

This occurs because the rows of the design matrix (a column of ones, and a column of the Year) are almost perfectly collinear.

Collinearity means that one of the columns is (approximately) equal to a linear combination of the other columns.

In practice this means that it is difficult to distinguish between the effects of the variables in the model.

Hence the regression coefficients tend to be highly variable when collinearity occurs.

Introducing Orthogonal Polynomials

Let \(P_r(x_i)\) denote a polynomial of order \(r\) in \(x_i\), where \(i = 1, \ldots, n\); \[P_r(x_i) = a_0 + a_1x_i + a_2x_i^2 + \ldots + a_r x_i^r\]

The polynomials \(P_r\) and \(P_s\) are orthogonal if

\[\sum_{i=1}^n P_r(x_i) P_s(x_i) = 0~~~~~~(r \ne s)\]

We can fit a polynomial of order p to the data using orthogonal polynomials:

\[E[Y_i] = \alpha_0 + \alpha_1 P_1(x_i) + \alpha_2 P_2(x_i) + \ldots + \alpha_p P_p(x_i)\]

For example: \(E[Y_i] = 2+3x_i+4x_i^2\) can be re-expressed as

\[E[Y_i] = 1+3(\frac{1}{3}x_i+\frac{1}{3})+ 4(x_i^2+\frac{1}{2}x_i).\]

This will be completely equivalent to fitting \(E[Y_i] = \beta_0 + \beta_1 x_i + \beta_2 x_i^2 + \ldots + \beta_p x_i^p\) with the \(\beta\)’s being a simple transformation of the \(\alpha\)’s.

Fitting a polynomial regression using orthogonal polynomials means the resulting estimated regression coefficients are uncorrelated.

Constructing orthogonal polynomials for a simple regression

We can create a design matrix as follows:

\[X = \left [ \begin{array}{ll} 1 & x_1 - \bar x \\ 1 & x_2 - \bar x \\ \vdots & \vdots \\ 1 & x_n - \bar x \end{array} \right ]\]

Note: Columns of \(X\) are orthogonal because they can be expressed as two orthogonal polynomials.

Column 1 is \(P_0 = 1\).

Column 2 is \(P_1(x_i) = x_i - \bar{x}\).

We can show that \(\sum_{i=1}^n {P_0(x_i) P_1(x_i)} = \sum_{i=1}^n {(x_i - \bar{x})} = 0\).

When we have an orthogonal matrix X, wen have the following implications:

\(\rightarrow X^T X\) is a diagonal matrix

\(\rightarrow \sigma^2 (X^TX)^{-1}\) is also a diagonal matrix

\(\rightarrow\) parameter estimates based on a model with this design matrix will be uncorrelated.

Return to Brisbane Lions Example

We will define Yr to be a centred version of Year.

Lions$Yr <- Lions$Year - mean(Lions$Year)
Lions.lm2 <- lm(Crowd ~ Yr, data = Lions)
summary(Lions.lm2)

Call:
lm(formula = Crowd ~ Yr, data = Lions)

Residuals:
    Min      1Q  Median      3Q     Max 
-3856.1  -904.3   503.5  1355.8  2462.1 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  20525.1      702.5  29.215 3.15e-10 ***
Yr            2147.9      222.2   9.668 4.74e-06 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2330 on 9 degrees of freedom
Multiple R-squared:  0.9122,    Adjusted R-squared:  0.9024 
F-statistic: 93.47 on 1 and 9 DF,  p-value: 4.736e-06

The models Lions.lm1 and Lions.lm2 have the same estimated slope parameters (as expected).

The intercept parameter has changed, where \(\hat{\beta_0}\) now represents the mean value of the response when the covariate is at its sample mean value \(\bar{x}\).

cov2cor(vcov(Lions.lm2))
            (Intercept)          Yr
(Intercept) 1.00000e+00 1.69369e-16
Yr          1.69369e-16 1.00000e+00

The regression parameter estimates now have zero correlation (R’s output is subject to slight numerical rounding errors).

Even if we change a little bit the slope of the regression line, it will still intersect with the x axis at the same intercept value.

Revisiting the FEV Data Example

Recall that in a previous Example we were fitting a polynomial regression of FEV (a measure of lung function) on Age.

The estimated regression coefficients for this model are highly correlated.

Download fev.csv

## Fev <- read.csv(file = "fev.csv", header = TRUE)
Fev.lm6 <- lm(FEV ~ poly(Age, degree = 6, raw = TRUE), data = Fev)
cov2cor(vcov(Fev.lm6))
                                   (Intercept)
(Intercept)                          1.0000000
poly(Age, degree = 6, raw = TRUE)1  -0.9934543
poly(Age, degree = 6, raw = TRUE)2   0.9774233
poly(Age, degree = 6, raw = TRUE)3  -0.9558205
poly(Age, degree = 6, raw = TRUE)4   0.9313454
poly(Age, degree = 6, raw = TRUE)5  -0.9058334
poly(Age, degree = 6, raw = TRUE)6   0.8804847
                                   poly(Age, degree = 6, raw = TRUE)1
(Intercept)                                                -0.9934543
poly(Age, degree = 6, raw = TRUE)1                          1.0000000
poly(Age, degree = 6, raw = TRUE)2                         -0.9949850
poly(Age, degree = 6, raw = TRUE)3                          0.9823511
poly(Age, degree = 6, raw = TRUE)4                         -0.9650155
poly(Age, degree = 6, raw = TRUE)5                          0.9450941
poly(Age, degree = 6, raw = TRUE)6                         -0.9240672
                                   poly(Age, degree = 6, raw = TRUE)2
(Intercept)                                                 0.9774233
poly(Age, degree = 6, raw = TRUE)1                         -0.9949850
poly(Age, degree = 6, raw = TRUE)2                          1.0000000
poly(Age, degree = 6, raw = TRUE)3                         -0.9960496
poly(Age, degree = 6, raw = TRUE)4                          0.9859955
poly(Age, degree = 6, raw = TRUE)5                         -0.9720506
poly(Age, degree = 6, raw = TRUE)6                          0.9558551
                                   poly(Age, degree = 6, raw = TRUE)3
(Intercept)                                                -0.9558205
poly(Age, degree = 6, raw = TRUE)1                          0.9823511
poly(Age, degree = 6, raw = TRUE)2                         -0.9960496
poly(Age, degree = 6, raw = TRUE)3                          1.0000000
poly(Age, degree = 6, raw = TRUE)4                         -0.9968602
poly(Age, degree = 6, raw = TRUE)5                          0.9888110
poly(Age, degree = 6, raw = TRUE)6                         -0.9775523
                                   poly(Age, degree = 6, raw = TRUE)4
(Intercept)                                                 0.9313454
poly(Age, degree = 6, raw = TRUE)1                         -0.9650155
poly(Age, degree = 6, raw = TRUE)2                          0.9859955
poly(Age, degree = 6, raw = TRUE)3                         -0.9968602
poly(Age, degree = 6, raw = TRUE)4                          1.0000000
poly(Age, degree = 6, raw = TRUE)5                         -0.9974864
poly(Age, degree = 6, raw = TRUE)6                          0.9910050
                                   poly(Age, degree = 6, raw = TRUE)5
(Intercept)                                                -0.9058334
poly(Age, degree = 6, raw = TRUE)1                          0.9450941
poly(Age, degree = 6, raw = TRUE)2                         -0.9720506
poly(Age, degree = 6, raw = TRUE)3                          0.9888110
poly(Age, degree = 6, raw = TRUE)4                         -0.9974864
poly(Age, degree = 6, raw = TRUE)5                          1.0000000
poly(Age, degree = 6, raw = TRUE)6                         -0.9979756
                                   poly(Age, degree = 6, raw = TRUE)6
(Intercept)                                                 0.8804847
poly(Age, degree = 6, raw = TRUE)1                         -0.9240672
poly(Age, degree = 6, raw = TRUE)2                          0.9558551
poly(Age, degree = 6, raw = TRUE)3                         -0.9775523
poly(Age, degree = 6, raw = TRUE)4                          0.9910050
poly(Age, degree = 6, raw = TRUE)5                         -0.9979756
poly(Age, degree = 6, raw = TRUE)6                          1.0000000

If we use the poly() function with the Age variable centred, we get…

Fev.lm.poly <- lm(FEV ~ poly(Age, degree = 6), data = Fev)
summary(Fev.lm.poly)

Call:
lm(formula = FEV ~ poly(Age, degree = 6), data = Fev)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.2047 -0.2791  0.0186  0.2509  0.9210 

Coefficients:
                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)             2.45117    0.02188 112.025  < 2e-16 ***
poly(Age, degree = 6)1  8.49647    0.39018  21.775  < 2e-16 ***
poly(Age, degree = 6)2 -3.14027    0.39018  -8.048 1.79e-14 ***
poly(Age, degree = 6)3 -0.35524    0.39018  -0.910    0.363    
poly(Age, degree = 6)4  1.56999    0.39018   4.024 7.20e-05 ***
poly(Age, degree = 6)5  0.32921    0.39018   0.844    0.399    
poly(Age, degree = 6)6 -0.28682    0.39018  -0.735    0.463    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.3902 on 311 degrees of freedom
Multiple R-squared:  0.6418,    Adjusted R-squared:  0.6349 
F-statistic: 92.87 on 6 and 311 DF,  p-value: < 2.2e-16
round(cov2cor(vcov(Fev.lm.poly)), digits = 6)
                       (Intercept) poly(Age, degree = 6)1
(Intercept)                      1                      0
poly(Age, degree = 6)1           0                      1
poly(Age, degree = 6)2           0                      0
poly(Age, degree = 6)3           0                      0
poly(Age, degree = 6)4           0                      0
poly(Age, degree = 6)5           0                      0
poly(Age, degree = 6)6           0                      0
                       poly(Age, degree = 6)2 poly(Age, degree = 6)3
(Intercept)                                 0                      0
poly(Age, degree = 6)1                      0                      0
poly(Age, degree = 6)2                      1                      0
poly(Age, degree = 6)3                      0                      1
poly(Age, degree = 6)4                      0                      0
poly(Age, degree = 6)5                      0                      0
poly(Age, degree = 6)6                      0                      0
                       poly(Age, degree = 6)4 poly(Age, degree = 6)5
(Intercept)                                 0                      0
poly(Age, degree = 6)1                      0                      0
poly(Age, degree = 6)2                      0                      0
poly(Age, degree = 6)3                      0                      0
poly(Age, degree = 6)4                      1                      0
poly(Age, degree = 6)5                      0                      1
poly(Age, degree = 6)6                      0                      0
                       poly(Age, degree = 6)6
(Intercept)                                 0
poly(Age, degree = 6)1                      0
poly(Age, degree = 6)2                      0
poly(Age, degree = 6)3                      0
poly(Age, degree = 6)4                      0
poly(Age, degree = 6)5                      0
poly(Age, degree = 6)6                      1

Comments

We have fitted a 6th order polynomial regression of FEV onAge using orthogonal polynomials.

The R function constructed the necessary polynomials in a manner that can be used as explanatory variables in a linear model.

Note that the poly function will construct by default orthogonal polynomials. To get non-orthogonal polynomials we need to add raw = TRUE inside the poly() function.

The fitted 6th order model using orthogonal polynomials produces identical fits (and predictions) to the 6th order model constructed in the original case study using the raw polynomial values or the I() function.

We use the round() command to remove rounding error in the output.

You should see that you get an identity matrix in return. This tells you that the estimate regression coefficients for the model are uncorrelated.

As a result of the estimates not being correlated, we can interpret t-test statistics and p-values in the summary table independently.

That is, p-values will not change much when we remove terms from the model.

The summary table shows us straight away that we need a polynomial of 4th order to model FEV as a function of Age.

Obtaining the design matrix

The model.matrix() command extracts the X matrix used in model fitting. It is quite large for the FEV data, so the following example compares the matrices used for the Lions example above.

model.matrix(Lions.lm1)
   (Intercept) Year
1            1 1993
2            1 1994
3            1 1995
4            1 1996
5            1 1997
6            1 1998
7            1 1999
8            1 2000
9            1 2001
10           1 2002
11           1 2003
attr(,"assign")
[1] 0 1
model.matrix(Lions.lm2)
   (Intercept) Yr
1            1 -5
2            1 -4
3            1 -3
4            1 -2
5            1 -1
6            1  0
7            1  1
8            1  2
9            1  3
10           1  4
11           1  5
attr(,"assign")
[1] 0 1

In this simple regression, it is relatively easy to see the manipulation that took effect. We will see another situation when the design matrix helps us understand the way our models are fitted in a lecture coming soon…

The re-scaling of a variable is not limited to polynomial regression situations. Re-scaling of a variable could prove worthwhile in any regression context when that variable has a relatively small range as compared to its distance from zero.

LS0tDQp0aXRsZTogIkxlY3R1cmUgMTc6IE1vZGVscyB1c2luZyBvcnRob2dvbmFsIHBvbHlub21pYWxzIg0Kc3VidGl0bGU6IDE2MS4yNTEgUmVncmVzc2lvbiBNb2RlbGxpbmcNCmF1dGhvcjogIlByZXNlbnRlZCBieSBKb25hdGhhbiBHb2RmcmV5IDxhLmouZ29kZnJleUBtYXNzZXkuYWMubno+IiAgDQpkYXRlOiAiV2VlayA2IG9mIFNlbWVzdGVyIDIsIGByIGx1YnJpZGF0ZTo6eWVhcihsdWJyaWRhdGU6Om5vdygpKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICBodG1sX25vdGVib29rOg0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgaW9zbGlkZXNfcHJlc2VudGF0aW9uOg0KICAgIHdpZGVzY3JlZW46IHRydWUNCiAgICBzbWFsbGVyOiB0cnVlDQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgc2xpZHlfcHJlc2VudGF0aW9uOiANCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KDQoNCg0KDQpbVmlldyB0aGUgbGF0ZXN0IHJlY29yZGluZyBvZiB0aGlzIGxlY3R1cmVdKGh0dHBzOi8vUi1SZXNvdXJjZXMubWFzc2V5LmFjLm56L3ZpZGVvcy8yNTFMMTcubXA0KQ0KPCEtLS0gRGF0YSBpcyBvbg0KaHR0cHM6Ly9yLXJlc291cmNlcy5tYXNzZXkuYWMubnovZGF0YS8xNjEyNTEvDQotLS0+DQoNCmBgYHtyIHNldHVwLCBwdXJsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShrbml0cikNCm9wdHNfY2h1bmskc2V0KGRldj1jKCJwbmciLCAicGRmIikpDQpvcHRzX2NodW5rJHNldChmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD03LCBmaWcucGF0aD0iRmlndXJlcy8iLCBmaWcuYWx0PSJ1bmxhYmVsbGVkIikNCm9wdHNfY2h1bmskc2V0KGNvbW1lbnQ9IiIsIGZpZy5hbGlnbj0iY2VudGVyIiwgdGlkeT1UUlVFKQ0Kb3B0aW9ucyhrbml0ci5rYWJsZS5OQSA9ICcnKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGJyb29tKQ0KYGBgDQoNCg0KPCEtLS0gRG8gbm90IGVkaXQgYW55dGhpbmcgYWJvdmUgdGhpcyBsaW5lLiAtLS0+DQoNCldlIGhhdmUgc2VlbiB0aGF0ICpwKi12YWx1ZXMgZm9yIGNvZWZmaWNpZW50cyBpbiBhIHBvbHlub21pYWwNCiAgICByZWdyZXNzaW9uIG1vZGVsIHdpbGwgY2hhbmdlIGRlcGVuZGluZyB1cG9uIHdoYXQgdGVybXMgYXJlIGluY2x1ZGVkDQogICAgaW4gdGhlIG1vZGVsLg0KDQpUaGlzIGNhbiBiZSBleHBsYWluZWQgaW4gdGVybXMgb2YgY29ycmVsYXRpb25zIGJldHdlZW4gcGFyYW1ldGVyDQogICAgZXN0aW1hdGVzLCBkZXNjcmliZWQgYnkgdGhlaXIgY292YXJpYW5jZSBtYXRyaXguDQoNCkluIHRoaXMgbGVjdHVyZSB3ZSB3aWxsIGRpc2N1c3MgdGhlIGNvbnN0cnVjdGlvbiBvZg0KICAgICoqb3J0aG9nb25hbCBwb2x5bm9taWFscyoqLCB3aG9zZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cw0KICAgIGFyZSB1bmNvcnJlbGF0ZWQuDQoNCiMjIENvdmFyaWFuY2UgTWF0cml4IGZvciBQYXJhbWV0ZXIgRXN0aW1hdGVzDQoNCldlIGNhbiBleHBsb3JlIHRoZSBkZXBlbmRlbmNpZXMgYmV0d2VlbiBlc3RpbWF0ZXMgb2YgaW5kaXZpZHVhbA0KcmVncmVzc2lvbiBjb2VmZmljaWVudHMgdXNpbmcgdHdvIG1hdHJpY2VzLg0KDQpGaXJzdCwgdGhlIGNvdmFyaWFuY2UgbWF0cml4IGZvciB0aGUgKipyZWdyZXNzaW9uKiogcGFyYW1ldGVyDQplc3RpbWF0ZXMgd2hpY2ggaXMgZ2l2ZW4gYnkgJCRcbWJveHtWYXJ9IFxsZWZ0KCBcaGF0e1xib2xkc3ltYm9se1xiZXRhfX0gXHJpZ2h0KSA9IA0KXGxlZnQgKA0KXGJlZ2lue2FycmF5fXtjY2NjfQ0KXG1ib3h7VmFyfSBcbGVmdCggXGhhdHtcYmV0YV8wfSBccmlnaHQpICYgXG1ib3h7Q292fSBcbGVmdChcaGF0e1xiZXRhXzB9LCBcaGF0e1xiZXRhXzF9XHJpZ2h0KSAmIFxsZG90cyAmIFxtYm94e0Nvdn0gXGxlZnQoXGhhdHtcYmV0YV8wfSwgXGhhdHtcYmV0YV9wfSBccmlnaHQpIFxcDQpcbWJveHtDb3Z9IFxsZWZ0KFxoYXR7XGJldGFfMX0sIFxoYXR7XGJldGFfMH0gXHJpZ2h0KSAmIFxtYm94e1Zhcn0gXGxlZnQoIFxoYXR7XGJldGFfMX0gXHJpZ2h0KSAmIFxsZG90cyAmIFxtYm94e0Nvdn0gXGxlZnQoXGhhdHtcYmV0YV8xfSwgXGhhdHtcYmV0YV9wfSBccmlnaHQpIFxcDQpcdmRvdHMgJiBcdmRvdHMgJiBcZGRvdHMgJiBcdmRvdHMgXFwNClxtYm94e0Nvdn0gXGxlZnQoXGhhdHtcYmV0YV9wfSwgXGhhdHtcYmV0YV8wfSBccmlnaHQpICYgXG1ib3h7Q292fSBcbGVmdChcaGF0e1xiZXRhX3B9LCBcaGF0e1xiZXRhXzF9IFxyaWdodCkgJiBcbGRvdHMgJiBcbWJveHtWYXJ9IFxsZWZ0KFxoYXR7XGJldGFfcH0gXHJpZ2h0KSANClxlbmR7YXJyYXl9DQpccmlnaHQgKQ0KPSBcc2lnbWFeMiAoWF5UIFgpXnstMX0kJA0KDQpXaGVyZSAkWCQgaXMgdGhlIGRlc2lnbiBtYXRyaXggYW5kICRcc2lnbWFeMiQgaXMgdGhlIGVzdGltYXRlZCBzdGFuZGFyZCBlcnJvciBvZiB0aGUgcmVzaWR1YWxzLg0KDQojIyBDb3JyZWxhdGlvbiBNYXRyaXggZm9yIFBhcmFtZXRlciBFc3RpbWF0ZXMNCg0KRnJvbSB0aGlzLCB3ZSBjYW4gY29tcHV0ZSB0aGUgc2Vjb25kIHVzZWZ1bCBtYXRyaXg6DQokJFxtYm94e0NvcnJ9XGxlZnQoIFxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fVxyaWdodCkgID0gDQpcbGVmdCAoDQpcYmVnaW57YXJyYXl9e2NjY2N9DQoxICYgXG1ib3h7Q29ycn0gXGxlZnQoXGhhdHtcYmV0YV8wfSwgXGhhdHtcYmV0YV8xfSBccmlnaHQpICYgXGxkb3RzICYgXG1ib3h7Q29ycn0gXGxlZnQoXGhhdHtcYmV0YV8wfSwgXGhhdHtcYmV0YV9wfSBccmlnaHQpIFxcDQpcbWJveHtDb3JyfSBcbGVmdChcaGF0e1xiZXRhXzF9LCBcaGF0e1xiZXRhXzB9IFxyaWdodCkgJiAxICYgXGxkb3RzICYgXG1ib3h7Q29ycn0gXGxlZnQoXGhhdHtcYmV0YV8xfSwgXGhhdHtcYmV0YV9wfSBccmlnaHQpIFxcDQpcdmRvdHMgJiBcdmRvdHMgJiBcZGRvdHMgJiBcdmRvdHMgXFwNClxtYm94e0NvcnJ9IFxsZWZ0KFxoYXR7XGJldGFfcH0sIFxoYXR7XGJldGFfMH0gXHJpZ2h0KSAmIFxtYm94e0NvcnJ9IFxsZWZ0KFxoYXR7XGJldGFfcH0sIFxoYXR7XGJldGFfMX0gXHJpZ2h0KSAmIFxsZG90cyAmIDENClxlbmR7YXJyYXl9DQpccmlnaHQgKSQkDQoNCmZvbGxvd2luZyB0aGUgZGVmaW5pdGlvbiBvZiB0aGUgY29ycmVsYXRpb246DQokJGNvcihYLCBZKSA9IFxmcmFje2NvdihYLFkpfXtcc2lnbWFfWFxzaWdtYV9ZfS4kJA0KDQojIyBDcm93ZHMgZm9yIHRoZSBCcmlzYmFuZSBMaW9ucw0KDQoNClRoaXMgRGF0YSBoYXMgdGhlIG51bWJlciBvZiBtZW1iZXJzIGFuZCBhdmVyYWdlIGNyb3dkcyBzaXplcyBmb3IgdGhlDQogICAgQnJpc2JhbmUgTGlvbnMgQUZMIGNsdWIgZnJvbSAxOTkzIHRvIDIwMDMuDQoNCg0KICAgIA0KfCAgVmFyaWFibGVzIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgIHwNCnwgOi0gfCA6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8DQp8ICBZZWFyIHwgQ2FsZW5kYXIgeWVhciAgICAgICB8DQp8IE1lbWJlcnMgfCBOdW1iZXIgb2YgTGlvbnMgbWVtYmVycyB8DQp8ICBDcm93ZCB8IEF2ZXJhZ2UgaG9tZSBjcm93ZCBzaXplIHwNCiAgICANCg0KVGhlIGFpbSBvZiB0aGlzIEV4YW1wbGUgaXMgdG8gcmVncmVzcyBgQ3Jvd2RgIG9uIGBZZWFyYC4NCg0KDQoNCiFbQnJpc2JhbmUgTGlvbnM/XSguLi9ncmFwaGljcy9saW9ucy5qcGcpDQoNCg0KV2UgZmlyc3QgaW1wb3J0LCBjaGVjayBhbmQgdGhlbiBwbG90IHRoZSBkYXRhLiAgDQoNCmByIHhmdW46OmVtYmVkX2ZpbGUoIi4uLy4uL2RhdGEvbGlvbnMuY3N2IilgDQoNCg0KDQpgYGB7ciBnZXRMaW9uc0RhdGEsIGVjaG89LTEsIGV2YWw9LTJ9DQpMaW9ucyA8LSByZWFkLmNzdihmaWxlPSIuLi8uLi9kYXRhL2xpb25zLmNzdiIsIGhlYWRlcj1UUlVFKQ0KTGlvbnMgPC0gcmVhZC5jc3YoZmlsZT0ibGlvbnMuY3N2IiwgaGVhZGVyPVRSVUUpDQpzdHIoTGlvbnMpDQpwbG90KENyb3dkflllYXIsIGRhdGE9TGlvbnMpDQpgYGANCg0KYW5kIHRoZW4gd2UgY2FuIGZpdCB0aGUgbW9kZWwgZm9yIHRoZSByZWxhdGlvbnNoaXAgb2YgaW50ZXJlc3QuLi4NCg0KYGBge3IgTGlvbnMubG0xfQ0KTGlvbnMubG0xIDwtIGxtKENyb3dkflllYXIsIGRhdGE9TGlvbnMpIA0Kc3VtbWFyeShMaW9ucy5sbTEpDQpgYGANCg0KQXMgZXhwZWN0ZWQgZnJvbSB0aGUgc2NhdHRlciBwbG90LCB0aGVyZSBpcyBvdmVyd2hlbG1pbmcgZXZpZGVuY2UgdGhlDQpMaW9uJ3MgaG9tZSBjcm93ZHMgYXJlIGluY3JlYXNpbmcgdGhyb3VnaCB0aW1lLg0KDQpXaGF0IGRvZXMgdGhlIGludGVyY2VwdCBtZWFuPw0KDQoNClRoZSBjb3ZhcmlhbmNlIG1hdHJpeCBmb3IgdGhlIGVzdGltYXRlcyBvZiB0aGUgcmVncmVzc2lvbiBpbnRlcmNlcHQgYW5kDQpzbG9wZSwgJFxoYXR7XGJldGFfMH0kIGFuZCAkXGhhdHtcYmV0YV8xfSQsIGNhbiBiZSBleHRyYWN0ZWQgdXNpbmcgUidzIGB2Y292KClgDQpjb21tYW5kLg0KDQpXZSBjYW4gY29udmVydCB0aGlzIGNvdmFyaWFuY2UgbWF0cml4IHRvIGl0cyBjb3JyZXNwb25kaW5nIGNvcnJlbGF0aW9uIG1hdHJpeCB1c2luZyB0aGUgYGNvdjJjb3IoKWAgY29tbWFuZC4NCg0KYGBge3IgdmNvdkxpb25zLmxtMX0NCnZjb3YoTGlvbnMubG0xKQ0KY292MmNvcih2Y292KExpb25zLmxtMSkNCikNCmBgYA0KDQpOb3RpY2UgdGhlIGRyYW1hdGljIG5lZ2F0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gJFxoYXR7XGJldGFfMH0kIGFuZA0KJFxoYXR7XGJldGFfMX0kLg0KDQpUaGlzIG1ha2VzIHNlbnNlOiBpZiB3ZSBjaGFuZ2UgdGhlIHNsb3BlIG9mIHRoZSByZWdyZXNzaW9uIGxpbmUgZXZlbiBhIGxpdHRsZSBiaXQsIHRoaXMgd2lsbCBoYXZlIGEgYmlnIGltcGFjdCBvbiB0aGUgdmFsdWUgb2YgdGhlIGludGVyY2VwdCAoZXNwZWNpYWxseSBzaW5jZSB0aGUgdmFsdWVzIG9mIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZXMgYXJlIHNvIGZhciBmcm9tIDApLg0KDQoNCiMjIENvbGxpbmVhcml0eQ0KDQpUaGlzIG9jY3VycyBiZWNhdXNlIHRoZSByb3dzIG9mIHRoZSBkZXNpZ24gbWF0cml4IChhDQogICAgY29sdW1uIG9mIG9uZXMsIGFuZCBhIGNvbHVtbiBvZiB0aGUgYFllYXJgKSBhcmUgYWxtb3N0IHBlcmZlY3RseQ0KICAgICoqY29sbGluZWFyKiouDQoNCioqQ29sbGluZWFyaXR5KiogbWVhbnMgdGhhdCBvbmUgb2YgdGhlIGNvbHVtbnMgaXMgKGFwcHJveGltYXRlbHkpIGVxdWFsDQogICAgdG8gYSBsaW5lYXIgY29tYmluYXRpb24gb2YgdGhlIG90aGVyIGNvbHVtbnMuDQoNCkluIHByYWN0aWNlIHRoaXMgbWVhbnMgdGhhdCBpdCBpcyBkaWZmaWN1bHQgdG8gZGlzdGluZ3Vpc2ggYmV0d2Vlbg0KICAgIHRoZSBlZmZlY3RzIG9mIHRoZSB2YXJpYWJsZXMgaW4gdGhlIG1vZGVsLg0KDQpIZW5jZSB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgdGVuZCB0byBiZSBoaWdobHkgdmFyaWFibGUgd2hlbg0KICAgIGNvbGxpbmVhcml0eSBvY2N1cnMuDQoNCiMjIEludHJvZHVjaW5nIE9ydGhvZ29uYWwgUG9seW5vbWlhbHMNCg0KTGV0ICRQX3IoeF9pKSQgZGVub3RlIGEgcG9seW5vbWlhbCBvZiBvcmRlciAkciQgaW4gJHhfaSQsDQogICAgd2hlcmUgJGkgPSAxLCBcbGRvdHMsIG4kOyAkJFBfcih4X2kpID0gYV8wICsgYV8xeF9pICsgYV8yeF9pXjIgKyBcbGRvdHMgKyBhX3IgeF9pXnIkJA0KDQpUaGUgcG9seW5vbWlhbHMgJFBfciQgYW5kICRQX3MkIGFyZSAqKm9ydGhvZ29uYWwqKiBpZg0KDQokJFxzdW1fe2k9MX1ebiBQX3IoeF9pKSBQX3MoeF9pKSA9IDB+fn5+fn4ociBcbmUgcykkJA0KDQpXZSBjYW4gZml0IGEgcG9seW5vbWlhbCBvZiBvcmRlciAqcCogdG8gdGhlIGRhdGEgdXNpbmcgb3J0aG9nb25hbA0KICAgIHBvbHlub21pYWxzOg0KDQokJEVbWV9pXSA9IFxhbHBoYV8wICsgXGFscGhhXzEgUF8xKHhfaSkgKyBcYWxwaGFfMiBQXzIoeF9pKSArIFxsZG90cyArIFxhbHBoYV9wIFBfcCh4X2kpJCQNCg0KDQpGb3IgZXhhbXBsZTogJEVbWV9pXSA9IDIrM3hfaSs0eF9pXjIkIGNhbiBiZSByZS1leHByZXNzZWQgYXMNCg0KJCRFW1lfaV0gPSAxKzMoXGZyYWN7MX17M314X2krXGZyYWN7MX17M30pKyA0KHhfaV4yK1xmcmFjezF9ezJ9eF9pKS4kJA0KDQpUaGlzIHdpbGwgYmUgY29tcGxldGVseSBlcXVpdmFsZW50IHRvIGZpdHRpbmcgJEVbWV9pXSA9IFxiZXRhXzAgKyBcYmV0YV8xIHhfaSArIFxiZXRhXzIgeF9pXjIgKyBcbGRvdHMgKyBcYmV0YV9wIHhfaV5wJCB3aXRoIHRoZSAkXGJldGEkJ3MgYmVpbmcgYSBzaW1wbGUgdHJhbnNmb3JtYXRpb24gb2YgdGhlICRcYWxwaGEkJ3MuDQoNCkZpdHRpbmcgYSBwb2x5bm9taWFsIHJlZ3Jlc3Npb24gdXNpbmcgb3J0aG9nb25hbCBwb2x5bm9taWFscyBtZWFucyB0aGUNCnJlc3VsdGluZyBlc3RpbWF0ZWQgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgYXJlIHVuY29ycmVsYXRlZC4NCg0KIyMgQ29uc3RydWN0aW5nIG9ydGhvZ29uYWwgcG9seW5vbWlhbHMgZm9yIGEgc2ltcGxlIHJlZ3Jlc3Npb24NCg0KV2UgY2FuIGNyZWF0ZSBhIGRlc2lnbiBtYXRyaXggYXMgZm9sbG93czoNCg0KJCRYID0gXGxlZnQgWyBcYmVnaW57YXJyYXl9e2xsfQ0KICAgIDEgJiB4XzEgLSBcYmFyIHggXFwNCiAgICAxICYgeF8yIC0gXGJhciB4IFxcDQogICAgXHZkb3RzICYgXHZkb3RzIFxcDQogICAgMSAmIHhfbiAtIFxiYXIgeA0KICAgIFxlbmR7YXJyYXl9DQogICAgXHJpZ2h0IF0kJA0KDQpOb3RlOiBDb2x1bW5zIG9mICRYJCBhcmUgb3J0aG9nb25hbCBiZWNhdXNlIHRoZXkgY2FuIGJlIGV4cHJlc3NlZA0KICAgIGFzIHR3byBvcnRob2dvbmFsIHBvbHlub21pYWxzLg0KDQpDb2x1bW4gMSBpcyAkUF8wID0gMSQuDQoNCkNvbHVtbiAyIGlzICRQXzEoeF9pKSA9IHhfaSAtIFxiYXJ7eH0kLg0KDQpXZSBjYW4gc2hvdyB0aGF0ICRcc3VtX3tpPTF9Xm4ge1BfMCh4X2kpIFBfMSh4X2kpfSA9IFxzdW1fe2k9MX1ebiB7KHhfaSAtIFxiYXJ7eH0pfSA9IDAkLg0KDQpXaGVuIHdlIGhhdmUgYW4gb3J0aG9nb25hbCBtYXRyaXggKlgqLCB3ZW4gaGF2ZSB0aGUgZm9sbG93aW5nIGltcGxpY2F0aW9uczogDQoNCiRccmlnaHRhcnJvdyBYXlQgWCQgaXMgYSBkaWFnb25hbCBtYXRyaXgNCg0KJFxyaWdodGFycm93IFxzaWdtYV4yIChYXlRYKV57LTF9JCBpcyBhbHNvIGEgZGlhZ29uYWwgbWF0cml4DQoNCiRccmlnaHRhcnJvdyQgcGFyYW1ldGVyIGVzdGltYXRlcyBiYXNlZCBvbiBhIG1vZGVsIHdpdGggdGhpcyAgICBkZXNpZ24gbWF0cml4IHdpbGwgYmUgdW5jb3JyZWxhdGVkLg0KDQojIyBSZXR1cm4gdG8gQnJpc2JhbmUgTGlvbnMgRXhhbXBsZSANCg0KV2Ugd2lsbCBkZWZpbmUgYFlyYCB0byBiZSBhICoqY2VudHJlZCoqIHZlcnNpb24gb2YgYFllYXJgLg0KDQpgYGB7ciBMaW9ucy5sbTJ9DQpMaW9ucyRZciA8LSBMaW9ucyRZZWFyIC0gbWVhbihMaW9ucyRZZWFyKQ0KTGlvbnMubG0yIDwtIGxtKENyb3dkfllyLCBkYXRhPUxpb25zKQ0Kc3VtbWFyeShMaW9ucy5sbTIpDQpgYGANCg0KDQpUaGUgbW9kZWxzIGBMaW9ucy5sbTFgIGFuZCBgTGlvbnMubG0yYCBoYXZlIHRoZSBzYW1lIGVzdGltYXRlZCBzbG9wZSBwYXJhbWV0ZXJzIChhcw0KICAgIGV4cGVjdGVkKS4NCg0KVGhlIGludGVyY2VwdCBwYXJhbWV0ZXIgaGFzIGNoYW5nZWQsIHdoZXJlICRcaGF0e1xiZXRhXzB9JCBub3cNCiAgICByZXByZXNlbnRzIHRoZSBtZWFuIHZhbHVlIG9mIHRoZSByZXNwb25zZSB3aGVuIHRoZSBjb3ZhcmlhdGUgaXMgYXQNCiAgICBpdHMgc2FtcGxlIG1lYW4gdmFsdWUgJFxiYXJ7eH0kLg0KDQpgYGB7ciBjb3YyY29yLnZjb3ZMaW9ucy5sbTJ9DQpjb3YyY29yKHZjb3YoTGlvbnMubG0yKSkNCmBgYA0KDQpUaGUgcmVncmVzc2lvbiBwYXJhbWV0ZXIgZXN0aW1hdGVzIG5vdyBoYXZlIHplcm8gY29ycmVsYXRpb24gKFIncyBvdXRwdXQNCmlzIHN1YmplY3QgdG8gc2xpZ2h0IG51bWVyaWNhbCByb3VuZGluZyBlcnJvcnMpLg0KDQpFdmVuIGlmIHdlIGNoYW5nZSBhIGxpdHRsZSBiaXQgdGhlIHNsb3BlIG9mIHRoZSByZWdyZXNzaW9uIGxpbmUsIGl0IHdpbGwgc3RpbGwgaW50ZXJzZWN0IHdpdGggdGhlIHggYXhpcyBhdCB0aGUgc2FtZSBpbnRlcmNlcHQgdmFsdWUuDQoNCg0KIyMgUmV2aXNpdGluZyB0aGUgRkVWIERhdGEgRXhhbXBsZQ0KDQpSZWNhbGwgdGhhdCBpbiBhIHByZXZpb3VzIEV4YW1wbGUgd2Ugd2VyZSBmaXR0aW5nIGEgcG9seW5vbWlhbA0KICAgIHJlZ3Jlc3Npb24gb2YgYEZFVmAgKGEgbWVhc3VyZSBvZiBsdW5nIGZ1bmN0aW9uKSBvbiBgQWdlYC4NCg0KVGhlIGVzdGltYXRlZCByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBmb3IgdGhpcyBtb2RlbCBhcmUgaGlnaGx5DQogICAgY29ycmVsYXRlZC4NCg0KDQpgciB4ZnVuOjplbWJlZF9maWxlKCIuLi8uLi9kYXRhL2Zldi5jc3YiKWANCg0KYGBge3IgY292MmNvci52Y292RmV2LmxtNiwgZWNobz0tMSwgZXZhbD0tMn0NCkZldiA8LSByZWFkLmNzdihmaWxlPSIuLi8uLi9kYXRhL2Zldi5jc3YiLCBoZWFkZXI9VFJVRSkNCkZldiA8LSByZWFkLmNzdihmaWxlPSJmZXYuY3N2IiwgaGVhZGVyPVRSVUUpDQpGZXYubG02IDwtIGxtKEZFVn5wb2x5KEFnZSxkZWdyZWU9NiwgcmF3PVRSVUUpLCBkYXRhPUZldikNCmNvdjJjb3IodmNvdihGZXYubG02KSkNCmBgYA0KDQpJZiB3ZSB1c2UgdGhlIGBwb2x5KClgIGZ1bmN0aW9uIHdpdGggdGhlIGBBZ2VgIHZhcmlhYmxlIGNlbnRyZWQsIHdlIGdldC4uLg0KDQpgYGB7ciBGZXYubG0ucG9seX0NCkZldi5sbS5wb2x5IDwtIGxtKEZFVn5wb2x5KEFnZSxkZWdyZWU9NiksZGF0YT1GZXYpDQpzdW1tYXJ5KEZldi5sbS5wb2x5KQ0Kcm91bmQoY292MmNvcih2Y292KEZldi5sbS5wb2x5KSksZGlnaXRzPTYpDQpgYGANCg0KIyMjIENvbW1lbnRzDQoNCldlIGhhdmUgZml0dGVkIGEgNnRoIG9yZGVyIHBvbHlub21pYWwgcmVncmVzc2lvbiBvZiBgRkVWYCBvbmBBZ2VgICB1c2luZw0KICAgIG9ydGhvZ29uYWwgcG9seW5vbWlhbHMuDQoNClRoZSBSIGZ1bmN0aW9uIGNvbnN0cnVjdGVkIHRoZSBuZWNlc3NhcnkgcG9seW5vbWlhbHMgaW4gYSBtYW5uZXINCiAgICB0aGF0IGNhbiBiZSB1c2VkIGFzIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyBpbiBhIGxpbmVhciBtb2RlbC4NCiAgICANCk5vdGUgdGhhdCB0aGUgYHBvbHlgIGZ1bmN0aW9uIHdpbGwgY29uc3RydWN0IGJ5IGRlZmF1bHQgb3J0aG9nb25hbCBwb2x5bm9taWFscy4gVG8gZ2V0IA0Kbm9uLW9ydGhvZ29uYWwgcG9seW5vbWlhbHMgd2UgbmVlZCB0byBhZGQgYHJhdyA9IFRSVUVgIGluc2lkZSB0aGUgYHBvbHkoKWAgZnVuY3Rpb24uDQoNClRoZSBmaXR0ZWQgNnRoIG9yZGVyIG1vZGVsIHVzaW5nIG9ydGhvZ29uYWwgcG9seW5vbWlhbHMgcHJvZHVjZXMNCiAgICBpZGVudGljYWwgZml0cyAoYW5kIHByZWRpY3Rpb25zKSB0byB0aGUgNnRoIG9yZGVyIG1vZGVsIGNvbnN0cnVjdGVkDQogICAgaW4gdGhlIG9yaWdpbmFsIGNhc2Ugc3R1ZHkgdXNpbmcgdGhlIHJhdyBwb2x5bm9taWFsIHZhbHVlcyBvciB0aGUgYEkoKWAgZnVuY3Rpb24uDQoNCg0KDQoNCldlIHVzZSB0aGUgYHJvdW5kKClgIGNvbW1hbmQgdG8gcmVtb3ZlIHJvdW5kaW5nIGVycm9yIGluIHRoZSBvdXRwdXQuDQoNCllvdSBzaG91bGQgc2VlIHRoYXQgeW91IGdldCBhbiBpZGVudGl0eSBtYXRyaXggaW4gcmV0dXJuLiBUaGlzIHRlbGxzIHlvdQ0KdGhhdCB0aGUgZXN0aW1hdGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgZm9yIHRoZSBtb2RlbCBhcmUNCnVuY29ycmVsYXRlZC4NCg0KQXMgYSByZXN1bHQgb2YgdGhlIGVzdGltYXRlcyBub3QgYmVpbmcgY29ycmVsYXRlZCwgd2UgY2FuIGludGVycHJldA0KICAgICp0Ki10ZXN0IHN0YXRpc3RpY3MgYW5kICpwKi12YWx1ZXMgaW4gdGhlIHN1bW1hcnkgdGFibGUgaW5kZXBlbmRlbnRseS4NCg0KVGhhdCBpcywgKnAqLXZhbHVlcyB3aWxsIG5vdCBjaGFuZ2UgbXVjaCB3aGVuIHdlIHJlbW92ZSB0ZXJtcyBmcm9tIHRoZQ0KICAgIG1vZGVsLg0KDQpUaGUgc3VtbWFyeSB0YWJsZSBzaG93cyB1cyBzdHJhaWdodCBhd2F5IHRoYXQgd2UgbmVlZCBhIHBvbHlub21pYWwgb2YgIDR0aCBvcmRlciB0byBtb2RlbCBgRkVWYCBhcyBhIGZ1bmN0aW9uIG9mIGBBZ2VgLg0KDQojIyBPYnRhaW5pbmcgdGhlIGRlc2lnbiBtYXRyaXgNCg0KVGhlIGBtb2RlbC5tYXRyaXgoKWAgY29tbWFuZCBleHRyYWN0cyB0aGUgKlgqIG1hdHJpeCB1c2VkIGluIG1vZGVsIGZpdHRpbmcuIEl0IGlzIHF1aXRlIGxhcmdlIGZvciB0aGUgYEZFVmAgZGF0YSwgc28gdGhlIGZvbGxvd2luZyBleGFtcGxlIGNvbXBhcmVzIHRoZSBtYXRyaWNlcyB1c2VkIGZvciB0aGUgTGlvbnMgZXhhbXBsZSBhYm92ZS4NCg0KYGBge3IgZ2V0TW9kZWxNYXRzfQ0KbW9kZWwubWF0cml4KExpb25zLmxtMSkNCm1vZGVsLm1hdHJpeChMaW9ucy5sbTIpDQpgYGANCg0KSW4gdGhpcyBzaW1wbGUgcmVncmVzc2lvbiwgaXQgaXMgcmVsYXRpdmVseSBlYXN5IHRvIHNlZSB0aGUgbWFuaXB1bGF0aW9uIHRoYXQgdG9vayBlZmZlY3QuIFdlIHdpbGwgc2VlIGFub3RoZXIgc2l0dWF0aW9uIHdoZW4gdGhlIGRlc2lnbiBtYXRyaXggaGVscHMgdXMgdW5kZXJzdGFuZCB0aGUgd2F5IG91ciBtb2RlbHMgYXJlIGZpdHRlZCBpbiBhIGxlY3R1cmUgY29taW5nIHNvb24uLi4NCg0KDQpUaGUgcmUtc2NhbGluZyBvZiBhIHZhcmlhYmxlIGlzIG5vdCBsaW1pdGVkIHRvIHBvbHlub21pYWwgcmVncmVzc2lvbiBzaXR1YXRpb25zLiBSZS1zY2FsaW5nIG9mIGEgdmFyaWFibGUgIGNvdWxkIHByb3ZlIHdvcnRod2hpbGUgaW4gYW55IHJlZ3Jlc3Npb24gY29udGV4dCB3aGVuIHRoYXQgdmFyaWFibGUgaGFzIGEgcmVsYXRpdmVseSBzbWFsbCByYW5nZSBhcyBjb21wYXJlZCB0byBpdHMgZGlzdGFuY2UgZnJvbSB6ZXJvLg0KDQo=