Background
This week we will see how to fit linear models in R,
and estimate parameter uncertainty and confidence intervals. The data
come from a paper by Johnson & Raven (1973, Science
179:893-895) and consist of the diversity of plants species across 30
islands in the Galapagos Archipelago. The data set also contains
measurements of 5 possible explanatory variables:
Area: the area of the island (km2)
Elevation: the highest elevation of the island
(m)
Nearest: the distance from the nearest island
(km)
Scruz: the distance from Santa Cruz Island (km)
Adjacent: the area of the adjacent island
(km2)
The gala dataset is included in the
{faraway} package.
Load the package and inspect the data.
## load package
library(faraway)
## inspect the first few rows
head(gala)
## Species Endemics Area Elevation Nearest Scruz Adjacent
## Baltra 58 23 25.09 346 0.6 0.6 1.84
## Bartolome 31 21 1.24 109 0.6 26.3 572.33
## Caldwell 3 3 0.21 114 2.8 58.7 0.78
## Champion 25 9 0.10 46 1.9 47.4 0.18
## Coamano 2 1 0.05 77 1.9 1.9 903.82
## Daphne.Major 18 11 0.34 119 8.0 8.0 1.84
The second column Endemics is the number of plant
species that are endemic to each island, but we will just use the total
number of plant species recorded in Species.
A simple model for plant diversity
We seek a linear model that would help us explain the diversity
(number) of plant species as a function of some predictors, which are
sometimes referred to as covariates. From the theory of island
biogeography, it would seem reasonable that plant diversity is related
to the area of an island, such that
species diversity = \(f\)(area)
More formally, we can write our explicit linear regression model
as
\[
\text{species}_i = \beta_0 + \beta_1 \text{area}_i + e_i
\]
where \(i\) indicates an island.
Plot the number of species (response) against island area (predictor)
to see what the relationship looks like.

Estimating \(\boldsymbol{\hat{\beta}}\)
We saw in lecture that we can estimate the regression parameters
as
\[
\boldsymbol{\hat{\beta}} = (\mathbf{X}^{\top} \mathbf{X})^{-1}
\mathbf{X}^{\top} \mathbf{y}.
\]
Begin by creating the matrix of response values \(\mathbf{y}\), which should have dimensions
\(n \times 1\).
## sample size
nn <- nrow(gala)
## vector (matrix) with responses
yy <- matrix(gala$Species, nrow = nn, ncol = 1)
## inspect the vector of responses
head(yy)
## [,1]
## [1,] 58
## [2,] 31
## [3,] 3
## [4,] 25
## [5,] 2
## [6,] 18
Next let’s create the matrix of predictors \(\mathbf{X}\). There is only one predictor
in the model (Area), so our matrix should consist of a
column of 1’s and a column with the area of each island.
## vector of 1's for intercept
Intercept <- rep(1, nn)
## vector of island areas
Area <- gala$Area
## create design matrix
XX <- cbind(Intercept, Area)
## inspect the first few rows
head(XX)
## Intercept Area
## [1,] 1 25.09
## [2,] 1 1.24
## [3,] 1 0.21
## [4,] 1 0.10
## [5,] 1 0.05
## [6,] 1 0.34
Matrix multiplication in R uses the %*%
operator. You can transpose a matrix with t() and invert a
matrix with solve().
Estimate the regression parameters in \(\boldsymbol{\hat{\beta}}\).
## parameter (alpha & beta) estimates
(beta_hat <- solve(t(XX) %*% XX) %*% t(XX) %*% yy)
## [,1]
## Intercept 63.78286147
## Area 0.08196317
We can interpret these coefficients to mean that an island with an
area of 0 km2 would be expected to have ~64 species of
plants(!), and that you would add ~8 species per 100 km2 of
island.
Estimating \(\mathbf{\hat{y}}\)
Recall that the estimated (fitted) values are given by
\[
\begin{align}
\mathbf{\hat{y}} &= \mathbf{X} \boldsymbol{\hat{\beta}} \\
&= \mathbf{X} \left( (\mathbf{X}^{\top} \mathbf{X})^{-1}
\mathbf{X}^{\top} \mathbf{y} \right) \\
&= \mathbf{X} (\mathbf{X}^{\top} \mathbf{X})^{-1}
\mathbf{X}^{\top} \mathbf{y} \\
&= \mathbf{H} \mathbf{y}.
\end{align}
\]
Calculate the hat matrix \(\mathbf{H}\) and solve for \(\mathbf{\hat{y}}\)
## calculate hat matrix
HH <- XX %*% solve(t(XX) %*% XX) %*% t(XX)
## solve for y_hat
y_hat <- HH %*% yy
Add the predicted values to our plot of the data.
par(mai = c(0.9, 0.9, 0.3, 0.1), omi = rep(0, 4))
## plot the data
plot(gala$Area, gala$Species, pch = 16, col = "dark green",
ylab = "Number of plant species",
xlab = expression(paste("Area of island (", km^2, ")")))
## add the fitted values (line)
abline(a = beta_hat[1], b = beta_hat[2])

This is not a great fit, but it’s a decent start. We’ll expand upon
our model later.
A shortcut to model fitting
As we saw briefly in lecture, it’s easy to fit a linear regression
model using the function lm(), which has syntax akin to
lm(respone ~ predictor(s), data_to_use)
Type ?lm at the R command prompt to see the arguments
and returned values.
Use lm() to fit the same model from above.
## fit simple linear regression
simple_model <- lm(Species ~ Area, gala)
## inspect the results
simple_model
##
## Call:
## lm(formula = Species ~ Area, data = gala)
##
## Coefficients:
## (Intercept) Area
## 63.78286 0.08196
Note that these are the same estimates for alpha (intercept) and beta
(slope) that we obtained earlier when calculating the estimates the long
way.
We can obtain the estimates of the fitted values \(\mathbf{\hat{y}}\) via one of two functions
in R: fitted() and predict().
We’ll see later that predict() has additional functionality
that we can use.
Compare the results of fitted() and
predict().
## get model estimates
## via fitted
y_hat_f <- fitted(simple_model)
## via predict
y_hat_p <- predict(simple_model, type = "response")
## compare these to each other
all.equal(y_hat_f, y_hat_p)
## [1] TRUE
Goodness-of-fit
We saw in lecture that we can use the coefficient of
determination \((R^2)\) to measure
how well a model fits the data; a value of 0 means no relationship
whatsoever and a value of 1 means a perfect relationship between the
two. The formula is
\[
R^2 = 1 - \frac{SSE}{SSTO}
\]
We can easily compute these quantities from our model fit and
data.
## SSE
resids <- yy - y_hat
SSE <- sum(resids^2) # = t(resids) %*% resids
## SSTO
SSTO <- sum((yy - mean(yy))^2)
## R^2
1 - SSE / SSTO
## [1] 0.3817301
Compare this to the Multiple R-squared value in the
ANOVA table that R produces.
summary(simple_model)
##
## Call:
## lm(formula = Species ~ Area, data = gala)
##
## Residuals:
## Min 1Q Median 3Q Max
## -99.495 -53.431 -29.045 3.423 306.137
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 63.78286 17.52442 3.640 0.001094 **
## Area 0.08196 0.01971 4.158 0.000275 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 91.73 on 28 degrees of freedom
## Multiple R-squared: 0.3817, Adjusted R-squared: 0.3596
## F-statistic: 17.29 on 1 and 28 DF, p-value: 0.0002748
The estimates are indeed the same.
Adjusted \(R^2\)
The ANOVA table above also reports the
Adjusted R-squared, which we haven’t discussed yet. It
turns out that one can always increase the \(SSR\) of a model by including more
predictors, even if they are completed unrelated to the response
variable. Recall that our objective in least squares estimation is
\[
\min \sum SSE = \min \sum_i (y_i - X_i \beta).
\]
Increasing the dimension of \(\mathbf{X}\) by adding more predictors
(columns) necessitates an increased space within which to minimize this
objective function, and hence you can get a better fit to the data.
We can demonstrate this with a simple example. Suppose we added
another predictor to our simple model that was nothing more than a
vector of Gaussian (normal) white noise, which is unrelated to the
response.
You can use rnorm(mean = mu, sd = sigma, n) to generate
n random draws from a normal distribution with mean \(\mu\) and standard deviation \(\sigma\).
## make this reproducible
set.seed(514)
## generate a vector of Gaussian white noise
WN <- rnorm(mean = 0, sd = 1, nn)
## add this column to our Galapagos design matrix
gala$WN <- WN
## fit a model with both Area & WN
summary(lm(Species ~ Area + WN, gala))
##
## Call:
## lm(formula = Species ~ Area + WN, data = gala)
##
## Residuals:
## Min 1Q Median 3Q Max
## -93.86 -44.67 -21.73 15.93 308.84
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 70.68115 17.82480 3.965 0.000485 ***
## Area 0.07892 0.01944 4.059 0.000378 ***
## WN -20.27151 13.91996 -1.456 0.156843
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 89.95 on 27 degrees of freedom
## Multiple R-squared: 0.4268, Adjusted R-squared: 0.3843
## F-statistic: 10.05 on 2 and 27 DF, p-value: 0.0005465
Our \(R^2\) value has indeed
increased.
To account for this, we can use a so-called adjusted \(R^2\), which corrects for these additional
degrees of freedom and is given by
\[
\bar{R}^2 = 1 - \frac{SSE ~ / ~ df_{error}}{SSTO ~ / ~ df_{total}}
\]
Calculate the \(\bar{R}^2\) for our
simple model and confirm that it matches the value in the ANOVA table
for the model with only Area as a predictor.
## degrees of freedom, error
df_e <- nn - 2
## degrees of freedom, total
df_t <- nn - 1
## adjusted R2
(R2_adj <- 1 - (SSE / df_e) / (SSTO / df_t))
## [1] 0.359649
A better model for plant diversity
Let’s expand our model from above to include some more predictors. In
particular, we’ll add in the effects of an island’s elevation and the
distance to the nearest island, such that
plant diversity = \(f\)(area,
elevation, distance to nearest island)
More formally, we can write our explicit linear regression model
as
\[
\text{species}_i = \beta_0 + \beta_1 \text{area}_i + \beta_2
\text{elevation}_i + \beta_3 \text{nearest}_i + e_i
\]
where \(i\) indicates an island.
Fit the model
Use lm() to fit this model and estimate the
parameters.
## fit larger model
(full_mod <- lm(Species ~ Area + Elevation + Nearest, gala))
##
## Call:
## lm(formula = Species ~ Area + Elevation + Nearest, data = gala)
##
## Coefficients:
## (Intercept) Area Elevation Nearest
## 16.46471 0.01908 0.17134 0.07123
Our estimates for the intercept \(\beta_0\) and the effect of island area
\(\beta_1\) changed when we accounted
for additional predictors in the model.
Hypothesis tests to compare models
We saw in lecture a number of different hypothesis tests we might
wish to undertake in a scenario like this. Let’s run through examples of
those here.
Test of all predictors in a model
Suppose we wanted to test whether this collection of 3 predictors in
our model were better than simply estimating the data by their mean. In
mathematical notation, we could express these two models as
\[
\Theta: \mathbf{y} = \mathbf{X} \boldsymbol{\beta} + \mathbf{e} \\
~ \\
\theta: \mathbf{y} = \boldsymbol{\mu} + \mathbf{e}
\]
Our null hypothesis for this test is given by
\[
H_0: \beta_1 = \beta_2 = \dots = \beta_k = 0
\]
and we would reject \(H_0\) if \(F > F^{(\alpha)}_{k_{\Theta} - k_{\theta}, n -
k_{\Theta}}\)
Recall that we will base this test on an \(F\)-distribution, such that
\[
SSE_{\Theta} = \left( \mathbf{y} - \mathbf{X} \boldsymbol{\beta}
\right)^{\top} \left( \mathbf{y} - \mathbf{X} \boldsymbol{\beta} \right)
= \mathbf{e}^{\top} \mathbf{e} = SSE \\
SSE_{\theta} = \left( \mathbf{y} - \bar{y} \right)^{\top} \left(
\mathbf{y} - \bar{y} \right) = SSTO \\
\Downarrow \\
F = \frac{ \left( SSTO - SSE \right) / (k - 1) } { SSE / (n - k)}
\]
\(F\)-test by hand
Calculating the sums-of-squares in R is
straightforward with matrix operations.
You can obtain p-values from the F distribution using the
pf() function
## matrix of predictors
XX <- model.matrix(full_mod)
## estimated beta
beta_hat <- solve(t(XX) %*% XX) %*% t(XX) %*% yy
## error sum of squares
SSE <- t(yy - XX %*% beta_hat) %*% (yy - XX %*% beta_hat)
## total sum of squares
SSTO <- t(yy - mean(yy)) %*% (yy - mean(yy))
## F statistic
(F_stat <- ((SSTO - SSE) / (4 - 1)) / (SSE / (nn - 4)))
## [,1]
## [1,] 10.77043
## F test
pf(q = F_stat, df1 = 4-1, df2 = nn-4, lower.tail = F)
## [,1]
## [1,] 8.816845e-05
This \(F\)-statistic is quite large
and the \(p\)-value is very small, so
we would reject the null hypothesis that we would be justified in
dropping the 3 predictors from this model in favor of a mean-only
model.
\(F\)-test with built-in
functions
We can use the anova() function in R to
conduct this \(F\)-test.
## fit null model; the '1' indicates an intercept-only model
null_mod <- lm(Species ~ 1, gala)
## use `anova('simple', 'complex')` to get the F-test results
anova(null_mod, full_mod)
## Analysis of Variance Table
##
## Model 1: Species ~ 1
## Model 2: Species ~ Area + Elevation + Nearest
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 29 381081
## 2 26 169918 3 211164 10.77 8.817e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
We can see that we get the same \(F\)-statistic and \(p\)-value as earlier
Testing one predictor
We might ask whether any single predictor could be dropped from our
model. For example, can nearest be dropped from this model?
One option is to fit this reduced model and compare to it to the full
model via our \(F\)-test with \(H_0: \beta_3 = 0\)
\[
\begin{aligned}
\Theta: \text{species}_i &= \beta_0 + \beta_1 \text{area}_i +
\beta_2 \text{elevation}_i + \beta_3 \text{nearest}_i + e_i \\
~ \\
\theta: \text{species}_i &= \beta_0 + \beta_1 \text{area}_i +
\beta_2 \text{elevation}_i + e_i
\end{aligned}
\]
Here’s our test in R.
## full model as before
full_mod <- lm(Species ~ Area + Elevation + Nearest, gala)
## reduced model without `nearest`
reduced_mod <- lm(Species ~ Area + Elevation, gala)
## use `anova('reduced', 'full')` to get the F-test results
anova(reduced_mod, full_mod)
## Analysis of Variance Table
##
## Model 1: Species ~ Area + Elevation
## Model 2: Species ~ Area + Elevation + Nearest
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 27 169947
## 2 26 169918 1 29.243 0.0045 0.9472
The \(F\)-statistic is small and the
\(p\)-value is large so we cannot
reject \(H_0\), and hence we conclude
that we would be justified in dropping Nearest from our
model.
Using a \(t\)-test
Another option for testing the signficance of one predictor is to
estimate a \(t\)-statistic as
\[
t_i = \frac{\beta_i}{\text{SE} \left( \beta_i \right)}
\]
and compare it to a \(t\)-distribution with \(n - k\) degrees of freedom.
We can get the results of the \(t\)-test with summary() from
base R or sumary() from the
{faraway} pkg, which is much less verbose.
## via `base::summary()`
summary(full_mod)
##
## Call:
## lm(formula = Species ~ Area + Elevation + Nearest, data = gala)
##
## Residuals:
## Min 1Q Median 3Q Max
## -191.856 -33.111 -18.626 5.673 262.209
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 16.46471 23.38884 0.704 0.48772
## Area 0.01908 0.02676 0.713 0.48216
## Elevation 0.17134 0.05452 3.143 0.00415 **
## Nearest 0.07123 1.06481 0.067 0.94718
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 80.84 on 26 degrees of freedom
## Multiple R-squared: 0.5541, Adjusted R-squared: 0.5027
## F-statistic: 10.77 on 3 and 26 DF, p-value: 8.817e-05
## via `faraway::sumary()`
sumary(full_mod)
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 16.464711 23.388841 0.7040 0.487718
## Area 0.019085 0.026764 0.7131 0.482158
## Elevation 0.171336 0.054519 3.1427 0.004151
## Nearest 0.071227 1.064806 0.0669 0.947179
##
## n = 30, p = 4, Residual SE = 80.84116, R-Squared = 0.55
Here we get \(t = \sqrt{F}\) and the
same \(p\)-value as before.
Testing 2+ predictors
Sometimes we might want to know whether we can drop 2+ predictors
from a model. For example, could we drop both \(\text{area}\) and \(\text{nearest}\) from our full model?
\[
\begin{aligned}
\Theta: \text{species}_i &= \beta_0 + \beta_1 \text{area}_i +
\beta_2 \text{elevation}_i + \beta_3 \text{nearest}_i + e_i \\
~ \\
\theta: \text{species}_i &= \beta_0 + \beta_2 \text{elevation}_i +
e_i
\end{aligned}
\]
\(H_0 : \beta_1 = \beta_3 = 0\)
Again we can conduct this test by fitting both models and calculating
\(F\).
## reduced model without `area` and `nearest`
reduced_mod <- lm(Species ~ Elevation, gala)
## use `anova('reduced', 'full')` to get the F-test results
anova(reduced_mod, full_mod)
## Analysis of Variance Table
##
## Model 1: Species ~ Elevation
## Model 2: Species ~ Area + Elevation + Nearest
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 28 173254
## 2 26 169918 2 3336.3 0.2552 0.7766
The \(F\)-statistic is rather small
and the \(p\)-value is big so we cannot
reject \(H_0\), suggesting we would be
justified in dropping area and elevation from the model.
Multiple \(t\)-tests versus one
\(F\)-test
Could we instead evaluate our null hypothesis by examining our table
of \(t\)-statistics and associated
\(p\)-values?
## t-statistics from our full model
sumary(full_mod)
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 16.464711 23.388841 0.7040 0.487718
## Area 0.019085 0.026764 0.7131 0.482158
## Elevation 0.171336 0.054519 3.1427 0.004151
## Nearest 0.071227 1.064806 0.0669 0.947179
##
## n = 30, p = 4, Residual SE = 80.84116, R-Squared = 0.55
This creates the problem of having to make our decision based on 2
\(p\)-values rather than one and each
\(p\)-value corresponds to a separate
\(t\)-test with the other predictor in
the model. Thus, we must use the single \(F\)-test.
Testing a subspace
Some tests cannot be expressed in terms of the inclusion or exclusion
of predictors. For example, consider a test of whether the areas of the
current and adjacent island could be added together and used in place of
the two separate predictors:
\[
\text{species}_i = \beta_0 + \beta_1 \text{area}_i + \beta_2
\text{adjacent}_i + \dots + e_i \\
~ \\
\text{species}_i = \beta_0 + \beta_1 \text{(area + adjacent)}_i + \dots
+ e_i
\]
\(H_0 : \beta_{\text{area}} =
\beta_{\text{adjacent}}\)
We can do this by using the I() function, which tells
R to treat things inside the () as an
operation.
## full model as before
full_mod <- lm(Species ~ Area + Adjacent + Elevation + Nearest, gala)
## reduced model without `elevation + nearest`
comb_mod <- lm(Species ~ I(Area + Adjacent) + Elevation + Nearest, gala)
## use `anova('combined', 'full')` to get the F-test results
anova(comb_mod, full_mod)
## Analysis of Variance Table
##
## Model 1: Species ~ I(Area + Adjacent) + Elevation + Nearest
## Model 2: Species ~ Area + Adjacent + Elevation + Nearest
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 26 116865
## 2 25 93867 1 22998 6.1252 0.02046 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
The \(F\)-statistic is large and the
\(p\)-value is reasonably small so we
reject \(H_0\).
What if we wanted to test whether a predictor had a specific
(non-zero) value? For example, is there a 1:1 relationship between \(\text{species}\) and \(\text{elevation}\) after controlling for
the other predictors? We could write this model as
\[
\text{species}_i = \beta_0 + \beta_1 \text{area}_i + (1 \times
\text{elevation}_i) + \beta_3 \text{nearest}_i + e_i
\]
\(H_0 : \beta_2 = 1\)
We can do this in R by using the
offset() function, which allows us to set a fixed beta
rather than treat it as unknown and estimate it.
## full model
full_mod <- lm(Species ~ Area + Elevation + Nearest, gala)
## model with effect of `elevation` = 1
fixed_mod <- lm(Species ~ Area + offset(1 * Elevation) + Nearest, gala)
## use `anova('comb', 'full')` to get the F-test results
anova(fixed_mod, full_mod)
## Analysis of Variance Table
##
## Model 1: Species ~ Area + offset(1 * Elevation) + Nearest
## Model 2: Species ~ Area + Elevation + Nearest
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 27 1679731
## 2 26 169918 1 1509813 231.02 1.891e-14 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
The \(F\)-statistic is very large
and the \(p\)-value is tiny so we
reject \(H_0\).
Using a \(t\)-test
We can also modify our \(t\)-test
from before and use it for our comparison by including the hypothesized
\(\hat{\beta}_{H_0}\) as an offset.
\[
t_i = \frac{(\hat{\beta_i} - \beta_{H_0})}{\text{SE} \left(
\hat{\beta}_i \right)}
\]
We can get \(\hat{\beta}_{\text{elevation}}\) and \(\text{SE}(\hat{\beta}_{\text{elevation}})\)
from our model summary.
## full model
sumary(full_mod)
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 16.464711 23.388841 0.7040 0.487718
## Area 0.019085 0.026764 0.7131 0.482158
## Elevation 0.171336 0.054519 3.1427 0.004151
## Nearest 0.071227 1.064806 0.0669 0.947179
##
## n = 30, p = 4, Residual SE = 80.84116, R-Squared = 0.55
This leaves us with \(\hat{\beta}_{\text{elevation}} \approx
0.171\) and \(\text{SE}(\hat{\beta}_{\text{elevation}}) \approx
0.0545\).
Calculate the \(t\)-test by hand and
compare the results to the above \(F\)-test
## t statistic
t_value <- (0.171336 - 1) / 0.054519
## p-value = t_alpha * Pr(t_value, df); `pt()` is the pdf for a t-dist
(p_value <- 1.96 * pt(q = t_value, df = (30 - 4)))
## verify t^2 = F
all.equal(t_value^2, anova(fixed_mod, full_mod)$F[2], tolerance = 0.0001)
## [1] 1.853024e-14
## [1] TRUE
The \(p\)-value is the same and
\(t^2 = F\) as before
Confidence intervals for \(\beta\)
We can also use confidence intervals (CI’s) to express uncertainty in
\(\hat{\beta_i}\), which take the
form
\[
100(1 - \alpha)\% ~ \text{CI}: \hat{\beta_i} \pm t_{n-p}^{(\alpha / 2)}
\operatorname{SE} ( \hat{\beta} )
\]
where here \(\alpha\) is our
predetermined Type-I error rate. To do so, we need the
information from the model summary.
## t-statistics from our full model
sumary(full_mod)
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 16.464711 23.388841 0.7040 0.487718
## Area 0.019085 0.026764 0.7131 0.482158
## Elevation 0.171336 0.054519 3.1427 0.004151
## Nearest 0.071227 1.064806 0.0669 0.947179
##
## n = 30, p = 4, Residual SE = 80.84116, R-Squared = 0.55
We can construct a 95% CI on \(\hat{\beta}\) using the 2.5% and 97.5%
percentiles of the \(t\)-distribution
with \(df = 30 - 4 = 26\).
## critical value for the t-distribution
## `qt()` is the quantile function for the t-dist; `p` is the (1-alpha/2) value
t_crit <- qt(p = 0.975, df = 30-4)
## 95% CI
CI95_beta <- 0.019085 + c(-1,1) * t_crit * 0.026764
CI95_beta |> round(3)
## [1] -0.036 0.074
This 95% CI includes zero so we would conclude that we cannot reject
the null hypothesis that \(\beta =
0\).
Confidence intervals for all \(\beta\)
We can quickly get the 95% CI for all the \(\beta_i\) using the function
confint().
## 95% CI's for all betas (rounded to the nearest thousandth)
confint(full_mod) |> round(3)
## 2.5 % 97.5 %
## (Intercept) -31.612 64.541
## Area -0.036 0.074
## Elevation 0.059 0.283
## Nearest -2.118 2.260
Bootstrap confidence intervals
The above \(F\)- and \(t\)-based CI’s depend on the assumption of
normality. Recall that the bootstrap method provides a way to construct
CI’s without this assumption.
Bootstrap procedure
Step 1: Fit your model to the data
## we already fit `full_mod` above
Step 2: Calculate \(\mathbf{e} = \mathbf{y}
- \mathbf{X} \boldsymbol{\hat{\beta}}\)
## residuals from our full model
resids <- residuals(full_mod)
Step 3a: Generate \(\mathbf{e}^*\)
by sampling with replacement from \(\mathbf{e}\)
Step 3b: Calculate \(\mathbf{y}^* = \mathbf{X}
\boldsymbol{\hat{\beta}} + \mathbf{e}^*\)
Step 3c: Estimate \(\boldsymbol{\hat{\beta}}^*\) from \(\mathbf{X}\) & \(\mathbf{y}^*\)
## number of boostrap samples
nb <- 1000
## empty matrix for beta estimates
beta_est <- matrix(NA, nb, 4)
## fitted values from our full model = X*beta
Xbeta <- fitted(full_mod)
## sample many times
for(i in 1:nb) {
## 3a: sample w/ replacement from e
e_star <- sample(resids, rep = TRUE)
## 3b: calculate y_star
y_star <- Xbeta + e_star
## 3c: re-estimate beta_star from X & y_star
beta_star <- update(full_mod, y_star ~ .)
## save estimated betas
beta_est[i,] <- coef(beta_star)
}
Step 4: Select the \(\tfrac{\alpha}{2}\) and \((1 - \tfrac{\alpha}{2})\) percentiles from
the saved \(\boldsymbol{\hat{\beta}}^*\)
## extract 2.5% and 97.5% values
CI95 <- apply(beta_est, 2, quantile, c(0.025, 0.975))
colnames(CI95) <- c("Intercept", colnames(gala[,3:5]))
t(round(CI95, 3))
## 2.5% 97.5%
## Intercept -22.402 61.470
## Area -0.027 0.084
## Elevation 0.073 0.277
## Nearest -1.918 2.150
Confidence interval for new predictions
Given a fitted model \(\mathbf{y} =
\mathbf{X} \boldsymbol{\beta} + \mathbf{e}\), we might want to
know the uncertainty around a new estimate \(\mathbf{y}^*\) given some new predictor
\(\mathbf{X}^*\).
CI for the mean response
Think back to our simple model for plant diversity as a function of
island area, such that
\[
\text{species}_i = \beta_0 + \beta_1 \text{area}_i + e_i
\]
Suppose we wanted to estimate the uncertainty in the average
diversity for an island with an area of 2000 km2. Recall that
our CI on the mean response is given by
\[
\mathbf{\hat{y}}^* \pm ~ t^{(\alpha / 2)}_{df} \sigma \sqrt{
{\mathbf{X}^*}^{\top} (\mathbf{X}^{\top} \mathbf{X})^{-1} \mathbf{X}^* }
\]
and here \(\mathbf{X}^* = [1 ~~
2000]\) (ie, a \(1 \times 2\)
row matrix).
CI by hand
Calculate the CI by hand using matrix operations in
R.
## matrix of predictors
XX <- model.matrix(simple_model)
## new X; vector for now
X_star <- c(Intercept = 1, Area = 2000)
## inside sqrt
inner_X <- t(X_star) %*% solve(t(XX) %*% XX) %*% X_star
## critical t-value
t_crit <- qt(0.975, df = nn-2)
## estimated SD
sigma <- summary(simple_model)$sigma
## predicted y
y_star <- sum(X_star * coef(simple_model))
## 95% CI
y_star + c(-1,1) * t_crit * sigma * sqrt(inner_X)
## [1] 149.5818 305.8366
CI with predict()
It’s much easier to use the predict() function to give
us a CI. Type ?predict at the command prompt to see the
function’s arguments.
predict(simple_model,
new = data.frame(t(X_star)),
level = 0.95,
interval = "confidence")
## fit lwr upr
## 1 227.7092 149.5818 305.8366
The estimated lower and upper bounds are the same as those we
obtained by hand.
Prediction interval for a new response
We saw in lecture that we can also estimate the uncertainty for a
specific prediction rather than the average response. Let’s now
imagine we wanted to predict how many plant species we might find on a
hypothetical island that might one day emerge from an underwater volcano
and is ultimately 2000 km2. Recall that a PI is given by
\[
\mathbf{\hat{y}}^* \pm ~ t^{(\alpha / 2)}_{df} \sigma \sqrt{1 +
{\mathbf{X}^*}^{\top} (\mathbf{X}^{\top} \mathbf{X})^{-1} \mathbf{X}^* }
\]
Calculating the PI by hand simply requires a different calculation
for inner_X than we made above.
## new X_star
X_star <- c(Intercept = 1, Area = 2000)
## inside sqrt
inner_X <- 1 + t(X_star) %*% solve(t(XX) %*% XX) %*% X_star
## 95% CI
y_star + c(-1,1) * t_crit * sigma * sqrt(inner_X)
## [1] 24.21065 431.20776
Notice how much wider this interval is than the CI we estimated
above.
We could instead use predict() as we did above for the
CI by switching the interval argument to
"prediction".
predict(simple_model,
new = data.frame(t(X_star)),
level = 0.95,
interval = "prediction")
## fit lwr upr
## 1 227.7092 24.21065 431.2078
LS0tCnRpdGxlOiAiRml0dGluZyBsaW5lYXIgbW9kZWxzIGluIFIiCmF1dGhvcjogIk1hcmsgU2NoZXVlcmVsbCIKZGF0ZTogIjEwIEFwcmlsIDIwMjYiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IGpvdXJuYWwKICAgIGhpZ2hsaWdodDogdGV4dG1hdGUKICAgIGNzczogLi4vbGVjdHVyZV9pbnN0LmNzcwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogNAotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKIyBCYWNrZ3JvdW5kCgpUaGlzIHdlZWsgd2Ugd2lsbCBzZWUgaG93IHRvIGZpdCBsaW5lYXIgbW9kZWxzIGluICoqUioqLCBhbmQgZXN0aW1hdGUgcGFyYW1ldGVyIHVuY2VydGFpbnR5IGFuZCBjb25maWRlbmNlIGludGVydmFscy4gVGhlIGRhdGEgY29tZSBmcm9tIGEgcGFwZXIgYnkgSm9obnNvbiAmIFJhdmVuICgxOTczLCAqU2NpZW5jZSogMTc5Ojg5My04OTUpIGFuZCBjb25zaXN0IG9mIHRoZSBkaXZlcnNpdHkgb2YgcGxhbnRzIHNwZWNpZXMgYWNyb3NzIDMwIGlzbGFuZHMgaW4gdGhlIEdhbGFwYWdvcyBBcmNoaXBlbGFnby4gVGhlIGRhdGEgc2V0IGFsc28gY29udGFpbnMgbWVhc3VyZW1lbnRzIG9mIDUgcG9zc2libGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzOgoKKiBgQXJlYWA6IHRoZSBhcmVhIG9mIHRoZSBpc2xhbmQgKGttPHN1cD4yPC9zdXA+KSAgCiogYEVsZXZhdGlvbmA6IHRoZSBoaWdoZXN0IGVsZXZhdGlvbiBvZiB0aGUgaXNsYW5kIChtKSAgCiogYE5lYXJlc3RgOiB0aGUgZGlzdGFuY2UgZnJvbSB0aGUgbmVhcmVzdCBpc2xhbmQgKGttKSAgCiogYFNjcnV6YDogdGhlIGRpc3RhbmNlIGZyb20gU2FudGEgQ3J1eiBJc2xhbmQgKGttKSAgCiogYEFkamFjZW50YDogdGhlIGFyZWEgb2YgdGhlIGFkamFjZW50IGlzbGFuZCAoa208c3VwPjI8L3N1cD4pCgpUaGUgYGdhbGFgIGRhdGFzZXQgaXMgaW5jbHVkZWQgaW4gdGhlICoqe2ZhcmF3YXl9KiogcGFja2FnZS4gCgo6OjogdGFzawoKTG9hZCB0aGUgcGFja2FnZSBhbmQgaW5zcGVjdCB0aGUgZGF0YS4KCjo6OgoKYGBge3IgbG9hZF9nYWxhX2RhdGFzZXR9CiMjIGxvYWQgcGFja2FnZQpsaWJyYXJ5KGZhcmF3YXkpCgojIyBpbnNwZWN0IHRoZSBmaXJzdCBmZXcgcm93cwpoZWFkKGdhbGEpCmBgYAoKVGhlIHNlY29uZCBjb2x1bW4gYEVuZGVtaWNzYCBpcyB0aGUgbnVtYmVyIG9mIHBsYW50IHNwZWNpZXMgdGhhdCBhcmUgZW5kZW1pYyB0byBlYWNoIGlzbGFuZCwgYnV0IHdlIHdpbGwganVzdCB1c2UgdGhlIHRvdGFsIG51bWJlciBvZiBwbGFudCBzcGVjaWVzIHJlY29yZGVkIGluIGBTcGVjaWVzYC4KCioqKgoKIyBBIHNpbXBsZSBtb2RlbCBmb3IgcGxhbnQgZGl2ZXJzaXR5CgpXZSBzZWVrIGEgbGluZWFyIG1vZGVsIHRoYXQgd291bGQgaGVscCB1cyBleHBsYWluIHRoZSBkaXZlcnNpdHkgKG51bWJlcikgb2YgcGxhbnQgc3BlY2llcyBhcyBhIGZ1bmN0aW9uIG9mIHNvbWUgcHJlZGljdG9ycywgd2hpY2ggYXJlIHNvbWV0aW1lcyByZWZlcnJlZCB0byBhcyAqY292YXJpYXRlcyouIEZyb20gdGhlIHRoZW9yeSBvZiBpc2xhbmQgYmlvZ2VvZ3JhcGh5LCBpdCB3b3VsZCBzZWVtIHJlYXNvbmFibGUgdGhhdCBwbGFudCBkaXZlcnNpdHkgaXMgcmVsYXRlZCB0byB0aGUgYXJlYSBvZiBhbiBpc2xhbmQsIHN1Y2ggdGhhdAoKPGNlbnRlcj5zcGVjaWVzIGRpdmVyc2l0eSA9ICRmJChhcmVhKTwvY2VudGVyPjxicj4KCk1vcmUgZm9ybWFsbHksIHdlIGNhbiB3cml0ZSBvdXIgZXhwbGljaXQgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgYXMKCiQkClx0ZXh0e3NwZWNpZXN9X2kgPSBcYmV0YV8wICsgXGJldGFfMSBcdGV4dHthcmVhfV9pICsgZV9pCiQkCgp3aGVyZSAkaSQgaW5kaWNhdGVzIGFuIGlzbGFuZC4KCjo6OiB0YXNrCgpQbG90IHRoZSBudW1iZXIgb2Ygc3BlY2llcyAocmVzcG9uc2UpIGFnYWluc3QgaXNsYW5kIGFyZWEgKHByZWRpY3RvcikgdG8gc2VlIHdoYXQgdGhlIHJlbGF0aW9uc2hpcCBsb29rcyBsaWtlLgoKOjo6CgpgYGB7ciBwbG90X2dhbGEsIGVjaG8gPSBGQUxTRSwgZmlnLmhlaWdodCA9IDQuNSwgZmlnLndpZHRoID0gNSwgZmlnLmFsaWduID0gImNlbnRlciIsIGZpZy5hbHQgPSAiQml2YXJpYXRlIHBsb3Qgb2YgdGhlIG51bWJlciBvZiBzcGVjaWVzIG9uIGFuIGlzbGFuZCB2ZXJzdXMgdGhlIGFyZWEgb2YgdGhlIGlzbGFuZCBpbiBraWxvbWV0ZXJzLiJ9CnBhcihtYWkgPSBjKDAuOSwgMC45LCAwLjMsIDAuMSksIG9taSA9IHJlcCgwLCA0KSkKcGxvdChnYWxhJEFyZWEsIGdhbGEkU3BlY2llcywgcGNoID0gMTYsIGNvbCA9ICJkYXJrIGdyZWVuIiwKICAgICB5bGFiID0gIk51bWJlciBvZiBzcGVjaWVzIiwKICAgICB4bGFiID0gZXhwcmVzc2lvbihwYXN0ZSgiQXJlYSBvZiBpc2xhbmQgKCIsIGttXjIsICIpIikpKQpgYGAKCiMjIEVzdGltYXRpbmcgJFxib2xkc3ltYm9se1xoYXR7XGJldGF9fSQKCldlIHNhdyBpbiBsZWN0dXJlIHRoYXQgd2UgY2FuIGVzdGltYXRlIHRoZSByZWdyZXNzaW9uIHBhcmFtZXRlcnMgYXMKCiQkClxib2xkc3ltYm9se1xoYXR7XGJldGF9fSA9IChcbWF0aGJme1h9XntcdG9wfSBcbWF0aGJme1h9KV57LTF9IFxtYXRoYmZ7WH1ee1x0b3B9IFxtYXRoYmZ7eX0uCiQkCgo6OjogdGFzawoKQmVnaW4gYnkgY3JlYXRpbmcgdGhlIG1hdHJpeCBvZiByZXNwb25zZSB2YWx1ZXMgJFxtYXRoYmZ7eX0kLCB3aGljaCBzaG91bGQgaGF2ZSBkaW1lbnNpb25zICRuIFx0aW1lcyAxJC4KCjo6OgoKYGBge3IgY3JlYXRlX3lfbWF0cml4fQojIyBzYW1wbGUgc2l6ZQpubiA8LSBucm93KGdhbGEpCgojIyB2ZWN0b3IgKG1hdHJpeCkgd2l0aCByZXNwb25zZXMKeXkgPC0gbWF0cml4KGdhbGEkU3BlY2llcywgbnJvdyA9IG5uLCBuY29sID0gMSkKCiMjIGluc3BlY3QgdGhlIHZlY3RvciBvZiByZXNwb25zZXMKaGVhZCh5eSkKYGBgCgo6OjogdGFzawoKTmV4dCBsZXQncyBjcmVhdGUgdGhlIG1hdHJpeCBvZiBwcmVkaWN0b3JzICRcbWF0aGJme1h9JC4gVGhlcmUgaXMgb25seSBvbmUgcHJlZGljdG9yIGluIHRoZSBtb2RlbCAoYEFyZWFgKSwgc28gb3VyIG1hdHJpeCBzaG91bGQgY29uc2lzdCBvZiBhIGNvbHVtbiBvZiAxJ3MgYW5kIGEgY29sdW1uIHdpdGggdGhlIGFyZWEgb2YgZWFjaCBpc2xhbmQuCgo6OjoKCmBgYHtyIGNyZWF0ZV9kZXNpZ25fbWF0cml4fQojIyB2ZWN0b3Igb2YgMSdzIGZvciBpbnRlcmNlcHQKSW50ZXJjZXB0IDwtIHJlcCgxLCBubikKCiMjIHZlY3RvciBvZiBpc2xhbmQgYXJlYXMKQXJlYSA8LSBnYWxhJEFyZWEKCiMjIGNyZWF0ZSBkZXNpZ24gbWF0cml4ClhYIDwtIGNiaW5kKEludGVyY2VwdCwgQXJlYSkKCiMjIGluc3BlY3QgdGhlIGZpcnN0IGZldyByb3dzCmhlYWQoWFgpCmBgYAoKOjo6IHRpcAoKTWF0cml4IG11bHRpcGxpY2F0aW9uIGluICoqUioqIHVzZXMgdGhlIGAlKiVgIG9wZXJhdG9yLiBZb3UgY2FuIHRyYW5zcG9zZSBhIG1hdHJpeCB3aXRoIGB0KClgIGFuZCBpbnZlcnQgYSBtYXRyaXggd2l0aCBgc29sdmUoKWAuIAoKOjo6Cgo6OjogdGFzawoKRXN0aW1hdGUgdGhlIHJlZ3Jlc3Npb24gcGFyYW1ldGVycyBpbiAkXGJvbGRzeW1ib2x7XGhhdHtcYmV0YX19JC4KCjo6OgoKYGBge3IgZXN0X2JldGF9CiMjIHBhcmFtZXRlciAoYWxwaGEgJiBiZXRhKSBlc3RpbWF0ZXMKKGJldGFfaGF0IDwtIHNvbHZlKHQoWFgpICUqJSBYWCkgJSolIHQoWFgpICUqJSB5eSkKYGBgCgo6Ojogc3VjY2VzcwoKV2UgY2FuIGludGVycHJldCB0aGVzZSBjb2VmZmljaWVudHMgdG8gbWVhbiB0aGF0IGFuIGlzbGFuZCB3aXRoIGFuIGFyZWEgb2YgMCBrbTxzdXA+Mjwvc3VwPiB3b3VsZCBiZSBleHBlY3RlZCB0byBoYXZlIH5gciByb3VuZChiZXRhX2hhdFsxXSwgMClgIHNwZWNpZXMgb2YgcGxhbnRzKCEpLCBhbmQgdGhhdCB5b3Ugd291bGQgYWRkIH5gciByb3VuZChiZXRhX2hhdFsyXSoxMDAsIDApYCBzcGVjaWVzIHBlciAxMDAga208c3VwPjI8L3N1cD4gb2YgaXNsYW5kLgoKOjo6CgoKIyMgRXN0aW1hdGluZyAkXG1hdGhiZntcaGF0e3l9fSQKClJlY2FsbCB0aGF0IHRoZSBlc3RpbWF0ZWQgKGZpdHRlZCkgdmFsdWVzIGFyZSBnaXZlbiBieQoKJCQKXGJlZ2lue2FsaWdufQpcbWF0aGJme1xoYXR7eX19ICY9IFxtYXRoYmZ7WH0gXGJvbGRzeW1ib2x7XGhhdHtcYmV0YX19IFxcCiAgJj0gXG1hdGhiZntYfSBcbGVmdCggKFxtYXRoYmZ7WH1ee1x0b3B9IFxtYXRoYmZ7WH0pXnstMX0gXG1hdGhiZntYfV57XHRvcH0gXG1hdGhiZnt5fSBccmlnaHQpIFxcCiAgJj0gXG1hdGhiZntYfSAoXG1hdGhiZntYfV57XHRvcH0gXG1hdGhiZntYfSleey0xfSBcbWF0aGJme1h9XntcdG9wfSBcbWF0aGJme3l9IFxcCiAgJj0gXG1hdGhiZntIfSBcbWF0aGJme3l9LgpcZW5ke2FsaWdufQokJAoKOjo6IHRhc2sKCkNhbGN1bGF0ZSB0aGUgaGF0IG1hdHJpeCAkXG1hdGhiZntIfSQgYW5kIHNvbHZlIGZvciAkXG1hdGhiZntcaGF0e3l9fSQKCjo6OgoKYGBge3IgZ2V0X2hhdG1hdH0KIyMgY2FsY3VsYXRlIGhhdCBtYXRyaXgKSEggPC0gWFggJSolIHNvbHZlKHQoWFgpICUqJSBYWCkgJSolIHQoWFgpCgojIyBzb2x2ZSBmb3IgeV9oYXQKeV9oYXQgPC0gSEggJSolIHl5CmBgYAoKOjo6IHRhc2sKCkFkZCB0aGUgcHJlZGljdGVkIHZhbHVlcyB0byBvdXIgcGxvdCBvZiB0aGUgZGF0YS4KCjo6OgoKYGBge3IgcGxvdF9nYWxhX2ZpdCwgZmlnLmhlaWdodCA9IDQuNSwgZmlnLndpZHRoID0gNSwgZmlnLmFsaWduID0gImNlbnRlciIsIGZpZy5hbHQgPSAiQml2YXJpYXRlIHBsb3Qgb2YgdGhlIG51bWJlciBvZiBzcGVjaWVzIG9uIGFuIGlzbGFuZCB2ZXJzdXMgdGhlIGFyZWEgb2YgdGhlIGlzbGFuZCBpbiBraWxvbWV0ZXJzLCBpbmNsdWRpbmcgdGhlIGVzdGltYXRlZCByZWdyZXNzaW9uIGxpbmUgdGhyb3VnaCB0aGUgZGF0YS4ifQpwYXIobWFpID0gYygwLjksIDAuOSwgMC4zLCAwLjEpLCBvbWkgPSByZXAoMCwgNCkpCiMjIHBsb3QgdGhlIGRhdGEKcGxvdChnYWxhJEFyZWEsIGdhbGEkU3BlY2llcywgcGNoID0gMTYsIGNvbCA9ICJkYXJrIGdyZWVuIiwKICAgICB5bGFiID0gIk51bWJlciBvZiBwbGFudCBzcGVjaWVzIiwKICAgICB4bGFiID0gZXhwcmVzc2lvbihwYXN0ZSgiQXJlYSBvZiBpc2xhbmQgKCIsIGttXjIsICIpIikpKQojIyBhZGQgdGhlIGZpdHRlZCB2YWx1ZXMgKGxpbmUpCmFibGluZShhID0gYmV0YV9oYXRbMV0sIGIgPSBiZXRhX2hhdFsyXSkKYGBgCgpUaGlzIGlzIG5vdCBhIGdyZWF0IGZpdCwgYnV0IGl0J3MgYSBkZWNlbnQgc3RhcnQuIFdlJ2xsIGV4cGFuZCB1cG9uIG91ciBtb2RlbCBsYXRlci4KCjxicj4KCiMjIEEgc2hvcnRjdXQgdG8gbW9kZWwgZml0dGluZwoKQXMgd2Ugc2F3IGJyaWVmbHkgaW4gbGVjdHVyZSwgaXQncyBlYXN5IHRvIGZpdCBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIHRoZSBmdW5jdGlvbiBgbG0oKWAsIHdoaWNoIGhhcyBzeW50YXggYWtpbiB0bwoKYGxtKHJlc3BvbmUgfiBwcmVkaWN0b3IocyksIGRhdGFfdG9fdXNlKWAKCjo6OiB0aXAKClR5cGUgYD9sbWAgYXQgdGhlIFIgY29tbWFuZCBwcm9tcHQgdG8gc2VlIHRoZSBhcmd1bWVudHMgYW5kIHJldHVybmVkIHZhbHVlcy4KCjo6OgoKOjo6IHRhc2sKClVzZSBgbG0oKWAgdG8gZml0IHRoZSBzYW1lIG1vZGVsIGZyb20gYWJvdmUuCgo6OjoKCmBgYHtyIGZpdF93aXRoX2xtfQojIyBmaXQgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uCnNpbXBsZV9tb2RlbCA8LSBsbShTcGVjaWVzIH4gQXJlYSwgZ2FsYSkKCiMjIGluc3BlY3QgdGhlIHJlc3VsdHMKc2ltcGxlX21vZGVsCmBgYAoKOjo6IHN1Y2Nlc3MKCk5vdGUgdGhhdCB0aGVzZSBhcmUgdGhlIHNhbWUgZXN0aW1hdGVzIGZvciBhbHBoYSAoaW50ZXJjZXB0KSBhbmQgYmV0YSAoc2xvcGUpIHRoYXQgd2Ugb2J0YWluZWQgZWFybGllciB3aGVuIGNhbGN1bGF0aW5nIHRoZSBlc3RpbWF0ZXMgdGhlIGxvbmcgd2F5LgoKOjo6Cgo6OjogdGlwCgpXZSBjYW4gb2J0YWluIHRoZSBlc3RpbWF0ZXMgb2YgdGhlIGZpdHRlZCB2YWx1ZXMgJFxtYXRoYmZ7XGhhdHt5fX0kIHZpYSBvbmUgb2YgdHdvIGZ1bmN0aW9ucyBpbiAqKlIqKjogYGZpdHRlZCgpYCBhbmQgYHByZWRpY3QoKWAuIFdlJ2xsIHNlZSBsYXRlciB0aGF0IGBwcmVkaWN0KClgIGhhcyBhZGRpdGlvbmFsIGZ1bmN0aW9uYWxpdHkgdGhhdCB3ZSBjYW4gdXNlLgoKOjo6Cgo6OjogdGFzawoKQ29tcGFyZSB0aGUgcmVzdWx0cyBvZiBgZml0dGVkKClgIGFuZCBgcHJlZGljdCgpYC4KCjo6OgoKYGBge3IgZ2V0X2ZpdHRlZF95fQojIyBnZXQgbW9kZWwgZXN0aW1hdGVzCgojIyB2aWEgZml0dGVkCnlfaGF0X2YgPC0gZml0dGVkKHNpbXBsZV9tb2RlbCkKCiMjIHZpYSBwcmVkaWN0CnlfaGF0X3AgPC0gcHJlZGljdChzaW1wbGVfbW9kZWwsIHR5cGUgPSAicmVzcG9uc2UiKQoKIyMgY29tcGFyZSB0aGVzZSB0byBlYWNoIG90aGVyCmFsbC5lcXVhbCh5X2hhdF9mLCB5X2hhdF9wKQpgYGAKCiMjIEdvb2RuZXNzLW9mLWZpdAoKV2Ugc2F3IGluIGxlY3R1cmUgdGhhdCB3ZSBjYW4gdXNlIHRoZSAqY29lZmZpY2llbnQgb2YgZGV0ZXJtaW5hdGlvbiogJChSXjIpJCB0byBtZWFzdXJlIGhvdyB3ZWxsIGEgbW9kZWwgZml0cyB0aGUgZGF0YTsgYSB2YWx1ZSBvZiAwIG1lYW5zIG5vIHJlbGF0aW9uc2hpcCB3aGF0c29ldmVyIGFuZCBhIHZhbHVlIG9mIDEgbWVhbnMgYSBwZXJmZWN0IHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB0d28uIFRoZSBmb3JtdWxhIGlzCgokJApSXjIgPSAxIC0gXGZyYWN7U1NFfXtTU1RPfQokJAoKV2UgY2FuIGVhc2lseSBjb21wdXRlIHRoZXNlIHF1YW50aXRpZXMgZnJvbSBvdXIgbW9kZWwgZml0IGFuZCBkYXRhLgoKPGJyPgoKYGBge3IgY29tcHV0ZV9SMn0KIyMgU1NFCnJlc2lkcyA8LSB5eSAtIHlfaGF0ClNTRSA8LSBzdW0ocmVzaWRzXjIpICAgIyA9IHQocmVzaWRzKSAlKiUgcmVzaWRzCgojIyBTU1RPClNTVE8gPC0gc3VtKCh5eSAtIG1lYW4oeXkpKV4yKQoKIyMgUl4yCjEgLSBTU0UgLyBTU1RPCmBgYAoKOjo6IHRhc2sKCkNvbXBhcmUgdGhpcyB0byB0aGUgYE11bHRpcGxlIFItc3F1YXJlZGAgdmFsdWUgaW4gdGhlIEFOT1ZBIHRhYmxlIHRoYXQgKipSKiogcHJvZHVjZXMuCgo6OjoKCmBgYHtyIGNoZWNrX1IyfQpzdW1tYXJ5KHNpbXBsZV9tb2RlbCkKYGBgCgo6Ojogc3VjY2VzcwoKVGhlIGVzdGltYXRlcyBhcmUgaW5kZWVkIHRoZSBzYW1lLgoKOjo6CgojIyMgQWRqdXN0ZWQgJFJeMiQKClRoZSBBTk9WQSB0YWJsZSBhYm92ZSBhbHNvIHJlcG9ydHMgdGhlIGBBZGp1c3RlZCBSLXNxdWFyZWRgLCB3aGljaCB3ZSBoYXZlbid0IGRpc2N1c3NlZCB5ZXQuIEl0IHR1cm5zIG91dCB0aGF0IG9uZSBjYW4gYWx3YXlzIGluY3JlYXNlIHRoZSAkU1NSJCBvZiBhIG1vZGVsIGJ5IGluY2x1ZGluZyBtb3JlIHByZWRpY3RvcnMsIGV2ZW4gaWYgdGhleSBhcmUgY29tcGxldGVkIHVucmVsYXRlZCB0byB0aGUgcmVzcG9uc2UgdmFyaWFibGUuIFJlY2FsbCB0aGF0IG91ciBvYmplY3RpdmUgaW4gbGVhc3Qgc3F1YXJlcyBlc3RpbWF0aW9uIGlzCgokJApcbWluIFxzdW0gU1NFID0gXG1pbiBcc3VtX2kgKHlfaSAtIFhfaSBcYmV0YSkuCiQkCgpJbmNyZWFzaW5nIHRoZSBkaW1lbnNpb24gb2YgJFxtYXRoYmZ7WH0kIGJ5IGFkZGluZyBtb3JlIHByZWRpY3RvcnMgKGNvbHVtbnMpIG5lY2Vzc2l0YXRlcyBhbiBpbmNyZWFzZWQgc3BhY2Ugd2l0aGluIHdoaWNoIHRvIG1pbmltaXplIHRoaXMgb2JqZWN0aXZlIGZ1bmN0aW9uLCBhbmQgaGVuY2UgeW91IGNhbiBnZXQgYSBiZXR0ZXIgZml0IHRvIHRoZSBkYXRhLgoKV2UgY2FuIGRlbW9uc3RyYXRlIHRoaXMgd2l0aCBhIHNpbXBsZSBleGFtcGxlLiBTdXBwb3NlIHdlIGFkZGVkIGFub3RoZXIgcHJlZGljdG9yIHRvIG91ciBzaW1wbGUgbW9kZWwgdGhhdCB3YXMgbm90aGluZyBtb3JlIHRoYW4gYSB2ZWN0b3Igb2YgR2F1c3NpYW4gKG5vcm1hbCkgd2hpdGUgbm9pc2UsIHdoaWNoIGlzIHVucmVsYXRlZCB0byB0aGUgcmVzcG9uc2UuCgo6OjogdGlwCgpZb3UgY2FuIHVzZSBgcm5vcm0obWVhbiA9IG11LCBzZCA9IHNpZ21hLCBuKWAgdG8gZ2VuZXJhdGUgYG5gIHJhbmRvbSBkcmF3cyBmcm9tIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiB3aXRoIG1lYW4gJFxtdSQgYW5kIHN0YW5kYXJkIGRldmlhdGlvbiAkXHNpZ21hJC4KCjo6OgoKYGBge3IgZGVtb19hZGpfUjJ9CiMjIG1ha2UgdGhpcyByZXByb2R1Y2libGUKc2V0LnNlZWQoNTE0KQoKIyMgZ2VuZXJhdGUgYSB2ZWN0b3Igb2YgR2F1c3NpYW4gd2hpdGUgbm9pc2UKV04gPC0gcm5vcm0obWVhbiA9IDAsIHNkID0gMSwgbm4pCgojIyBhZGQgdGhpcyBjb2x1bW4gdG8gb3VyIEdhbGFwYWdvcyBkZXNpZ24gbWF0cml4CmdhbGEkV04gPC0gV04gCgojIyBmaXQgYSBtb2RlbCB3aXRoIGJvdGggQXJlYSAmIFdOCnN1bW1hcnkobG0oU3BlY2llcyB+IEFyZWEgKyBXTiwgZ2FsYSkpCmBgYAoKOjo6IG5vdGUKCk91ciAkUl4yJCB2YWx1ZSBoYXMgaW5kZWVkIGluY3JlYXNlZC4KCjo6OgoKOjo6IHRpcAoKVG8gYWNjb3VudCBmb3IgdGhpcywgd2UgY2FuIHVzZSBhIHNvLWNhbGxlZCAqYWRqdXN0ZWQqICRSXjIkLCB3aGljaCBjb3JyZWN0cyBmb3IgdGhlc2UgYWRkaXRpb25hbCBkZWdyZWVzIG9mIGZyZWVkb20gYW5kIGlzIGdpdmVuIGJ5CgokJApcYmFye1J9XjIgPSAxIC0gXGZyYWN7U1NFIH4gLyB+IGRmX3tlcnJvcn19e1NTVE8gfiAvIH4gZGZfe3RvdGFsfX0KJCQKCjo6OgoKOjo6IHRhc2sKCkNhbGN1bGF0ZSB0aGUgJFxiYXJ7Un1eMiQgZm9yIG91ciBzaW1wbGUgbW9kZWwgYW5kIGNvbmZpcm0gdGhhdCBpdCBtYXRjaGVzIHRoZSB2YWx1ZSBpbiB0aGUgQU5PVkEgdGFibGUgZm9yIHRoZSBtb2RlbCB3aXRoIG9ubHkgYEFyZWFgIGFzIGEgcHJlZGljdG9yLgoKOjo6CgpgYGB7ciBhZGp1c3RlZF9SMn0KIyMgZGVncmVlcyBvZiBmcmVlZG9tLCBlcnJvcgpkZl9lIDwtIG5uIC0gMgoKIyMgZGVncmVlcyBvZiBmcmVlZG9tLCB0b3RhbApkZl90IDwtIG5uIC0gMQoKIyMgYWRqdXN0ZWQgUjIKKFIyX2FkaiA8LSAxIC0gKFNTRSAvIGRmX2UpIC8gKFNTVE8gLyBkZl90KSkKYGBgCgoqKioKCiMgQSBiZXR0ZXIgbW9kZWwgZm9yIHBsYW50IGRpdmVyc2l0eQoKTGV0J3MgZXhwYW5kIG91ciBtb2RlbCBmcm9tIGFib3ZlIHRvIGluY2x1ZGUgc29tZSBtb3JlIHByZWRpY3RvcnMuIEluIHBhcnRpY3VsYXIsIHdlJ2xsIGFkZCBpbiB0aGUgZWZmZWN0cyBvZiBhbiBpc2xhbmQncyBlbGV2YXRpb24gYW5kIHRoZSBkaXN0YW5jZSB0byB0aGUgbmVhcmVzdCBpc2xhbmQsIHN1Y2ggdGhhdAoKPGNlbnRlcj5wbGFudCBkaXZlcnNpdHkgPSAkZiQoYXJlYSwgZWxldmF0aW9uLCBkaXN0YW5jZSB0byBuZWFyZXN0IGlzbGFuZCk8L2NlbnRlcj48YnI+ICAKCk1vcmUgZm9ybWFsbHksIHdlIGNhbiB3cml0ZSBvdXIgZXhwbGljaXQgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgYXMKCiQkClx0ZXh0e3NwZWNpZXN9X2kgPSBcYmV0YV8wICsgXGJldGFfMSBcdGV4dHthcmVhfV9pICsgXGJldGFfMiBcdGV4dHtlbGV2YXRpb259X2kgKyBcYmV0YV8zIFx0ZXh0e25lYXJlc3R9X2kgKyBlX2kKJCQKCndoZXJlICRpJCBpbmRpY2F0ZXMgYW4gaXNsYW5kLgoKIyMgRml0IHRoZSBtb2RlbAoKOjo6IHRhc2sKClVzZSBgbG0oKWAgdG8gZml0IHRoaXMgbW9kZWwgYW5kIGVzdGltYXRlIHRoZSBwYXJhbWV0ZXJzLgoKOjo6CgpgYGB7ciBmaXRfbXVsdGlwbGVfcmVncn0KIyMgZml0IGxhcmdlciBtb2RlbAooZnVsbF9tb2QgPC0gbG0oU3BlY2llcyB+IEFyZWEgKyBFbGV2YXRpb24gKyBOZWFyZXN0LCBnYWxhKSkKYGBgCgo6Ojogbm90ZQoKT3VyIGVzdGltYXRlcyBmb3IgdGhlIGludGVyY2VwdCAkXGJldGFfMCQgYW5kIHRoZSBlZmZlY3Qgb2YgaXNsYW5kIGFyZWEgJFxiZXRhXzEkIGNoYW5nZWQgd2hlbiB3ZSBhY2NvdW50ZWQgZm9yIGFkZGl0aW9uYWwgcHJlZGljdG9ycyBpbiB0aGUgbW9kZWwuCgo6OjoKCiMjIEh5cG90aGVzaXMgdGVzdHMgdG8gY29tcGFyZSBtb2RlbHMKCldlIHNhdyBpbiBsZWN0dXJlIGEgbnVtYmVyIG9mIGRpZmZlcmVudCBoeXBvdGhlc2lzIHRlc3RzIHdlIG1pZ2h0IHdpc2ggdG8gdW5kZXJ0YWtlIGluIGEgc2NlbmFyaW8gbGlrZSB0aGlzLiBMZXQncyBydW4gdGhyb3VnaCBleGFtcGxlcyBvZiB0aG9zZSBoZXJlLgoKIyMjIFRlc3Qgb2YgKmFsbCogcHJlZGljdG9ycyBpbiBhIG1vZGVsCgpTdXBwb3NlIHdlIHdhbnRlZCB0byB0ZXN0IHdoZXRoZXIgdGhpcyBjb2xsZWN0aW9uIG9mIDMgcHJlZGljdG9ycyBpbiBvdXIgbW9kZWwgd2VyZSBiZXR0ZXIgdGhhbiBzaW1wbHkgZXN0aW1hdGluZyB0aGUgZGF0YSBieSB0aGVpciBtZWFuLiBJbiBtYXRoZW1hdGljYWwgbm90YXRpb24sIHdlIGNvdWxkIGV4cHJlc3MgdGhlc2UgdHdvIG1vZGVscyBhcwoKJCQKXFRoZXRhOiBcbWF0aGJme3l9ID0gXG1hdGhiZntYfSBcYm9sZHN5bWJvbHtcYmV0YX0gKyBcbWF0aGJme2V9IFxcCn4gXFwKXHRoZXRhOiBcbWF0aGJme3l9ID0gXGJvbGRzeW1ib2x7XG11fSArIFxtYXRoYmZ7ZX0gCiQkCgpPdXIgbnVsbCBoeXBvdGhlc2lzIGZvciB0aGlzIHRlc3QgaXMgZ2l2ZW4gYnkKCiQkCkhfMDogXGJldGFfMSA9IFxiZXRhXzIgPSBcZG90cyA9IFxiZXRhX2sgPSAwCiQkCgphbmQgd2Ugd291bGQgcmVqZWN0ICRIXzAkIGlmICRGID4gRl57KFxhbHBoYSl9X3trX3tcVGhldGF9IC0ga197XHRoZXRhfSwgbiAtIGtfe1xUaGV0YX19JAoKUmVjYWxsIHRoYXQgd2Ugd2lsbCBiYXNlIHRoaXMgdGVzdCBvbiBhbiAkRiQtZGlzdHJpYnV0aW9uLCBzdWNoIHRoYXQKCiQkClNTRV97XFRoZXRhfSA9IFxsZWZ0KCBcbWF0aGJme3l9IC0gXG1hdGhiZntYfSBcYm9sZHN5bWJvbHtcYmV0YX0gXHJpZ2h0KV57XHRvcH0gXGxlZnQoIFxtYXRoYmZ7eX0gLSBcbWF0aGJme1h9IFxib2xkc3ltYm9se1xiZXRhfSBccmlnaHQpID0gXG1hdGhiZntlfV57XHRvcH0gXG1hdGhiZntlfSA9IFNTRSBcXApTU0Vfe1x0aGV0YX0gPSBcbGVmdCggXG1hdGhiZnt5fSAtIFxiYXJ7eX0gXHJpZ2h0KV57XHRvcH0gXGxlZnQoIFxtYXRoYmZ7eX0gLSBcYmFye3l9IFxyaWdodCkgPSAgU1NUTyBcXApcRG93bmFycm93IFxcCkYgPSBcZnJhY3sgXGxlZnQoIFNTVE8gLSBTU0UgXHJpZ2h0KSAgLyAoayAtIDEpIH0geyBTU0UgIC8gKG4gLSBrKX0KJCQKCiMjIyMgJEYkLXRlc3QgYnkgaGFuZAoKQ2FsY3VsYXRpbmcgdGhlIHN1bXMtb2Ytc3F1YXJlcyBpbiAqKlIqKiBpcyBzdHJhaWdodGZvcndhcmQgd2l0aCBtYXRyaXggb3BlcmF0aW9ucy4KCjo6OiB0aXAKCllvdSBjYW4gb2J0YWluIHAtdmFsdWVzIGZyb20gdGhlIF9GXyBkaXN0cmlidXRpb24gdXNpbmcgdGhlIGBwZigpYCBmdW5jdGlvbgoKOjo6CgpgYGB7ciBGX3Rlc3RfYnlfaGFuZH0KIyMgbWF0cml4IG9mIHByZWRpY3RvcnMKWFggPC0gbW9kZWwubWF0cml4KGZ1bGxfbW9kKQoKIyMgZXN0aW1hdGVkIGJldGEKYmV0YV9oYXQgPC0gc29sdmUodChYWCkgJSolIFhYKSAlKiUgdChYWCkgJSolIHl5CgojIyBlcnJvciBzdW0gb2Ygc3F1YXJlcwpTU0UgPC0gdCh5eSAtIFhYICUqJSBiZXRhX2hhdCkgJSolICh5eSAtIFhYICUqJSBiZXRhX2hhdCkKCiMjIHRvdGFsIHN1bSBvZiBzcXVhcmVzClNTVE8gPC0gdCh5eSAtIG1lYW4oeXkpKSAlKiUgKHl5IC0gbWVhbih5eSkpCgojIyBGIHN0YXRpc3RpYwooRl9zdGF0IDwtICgoU1NUTyAtIFNTRSkgLyAoNCAtIDEpKSAvIChTU0UgLyAobm4gLSA0KSkpCgojIyBGIHRlc3QKcGYocSA9IEZfc3RhdCwgZGYxID0gNC0xLCBkZjIgPSBubi00LCBsb3dlci50YWlsID0gRikKYGBgCgo6Ojogbm90ZQoKVGhpcyAkRiQtc3RhdGlzdGljIGlzIHF1aXRlIGxhcmdlIGFuZCB0aGUgJHAkLXZhbHVlIGlzIHZlcnkgc21hbGwsIHNvIHdlIHdvdWxkIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIHRoYXQgd2Ugd291bGQgYmUganVzdGlmaWVkIGluIGRyb3BwaW5nIHRoZSAzIHByZWRpY3RvcnMgZnJvbSB0aGlzIG1vZGVsIGluIGZhdm9yIG9mIGEgbWVhbi1vbmx5IG1vZGVsLgoKOjo6CgojIyMjICRGJC10ZXN0IHdpdGggYnVpbHQtaW4gZnVuY3Rpb25zCgo6OjogdGlwCgpXZSBjYW4gdXNlIHRoZSBgYW5vdmEoKWAgZnVuY3Rpb24gaW4gKipSKiogdG8gY29uZHVjdCB0aGlzICRGJC10ZXN0LgoKOjo6CgpgYGB7ciBnYWxhX2Z1bGwsIGVjaG8gPSBUUlVFfQojIyBmaXQgbnVsbCBtb2RlbDsgdGhlICcxJyBpbmRpY2F0ZXMgYW4gaW50ZXJjZXB0LW9ubHkgbW9kZWwKbnVsbF9tb2QgPC0gbG0oU3BlY2llcyB+IDEsIGdhbGEpCgojIyB1c2UgYGFub3ZhKCdzaW1wbGUnLCAnY29tcGxleCcpYCB0byBnZXQgdGhlIEYtdGVzdCByZXN1bHRzCmFub3ZhKG51bGxfbW9kLCBmdWxsX21vZCkKYGBgCgo6Ojogc3VjY2VzcwoKV2UgY2FuIHNlZSB0aGF0IHdlIGdldCB0aGUgc2FtZSAkRiQtc3RhdGlzdGljIGFuZCAkcCQtdmFsdWUgYXMgZWFybGllcgoKOjo6CgoKIyMjIFRlc3Rpbmcgb25lIHByZWRpY3RvcgoKV2UgbWlnaHQgYXNrIHdoZXRoZXIgYW55IHNpbmdsZSBwcmVkaWN0b3IgY291bGQgYmUgZHJvcHBlZCBmcm9tIG91ciBtb2RlbC4gRm9yIGV4YW1wbGUsIGNhbiBgbmVhcmVzdGAgYmUgZHJvcHBlZCBmcm9tIHRoaXMgbW9kZWw/IE9uZSBvcHRpb24gaXMgdG8gZml0IHRoaXMgcmVkdWNlZCBtb2RlbCBhbmQgY29tcGFyZSB0byBpdCB0byB0aGUgZnVsbCBtb2RlbCB2aWEgb3VyICRGJC10ZXN0IHdpdGggJEhfMDogXGJldGFfMyA9IDAkCgokJApcYmVnaW57YWxpZ25lZH0KXFRoZXRhOiBcdGV4dHtzcGVjaWVzfV9pICY9IFxiZXRhXzAgKyBcYmV0YV8xIFx0ZXh0e2FyZWF9X2kgKyBcYmV0YV8yIFx0ZXh0e2VsZXZhdGlvbn1faSArIFxiZXRhXzMgXHRleHR7bmVhcmVzdH1faSArIGVfaSBcXAp+IFxcIApcdGhldGE6IFx0ZXh0e3NwZWNpZXN9X2kgJj0gXGJldGFfMCArIFxiZXRhXzEgXHRleHR7YXJlYX1faSArIFxiZXRhXzIgXHRleHR7ZWxldmF0aW9ufV9pICsgZV9pClxlbmR7YWxpZ25lZH0KJCQKCkhlcmUncyBvdXIgdGVzdCBpbiAqKlIqKi4KCmBgYHtyIGdhbGFfb25lX3ByZWRpY3Rvcl9GLCBlY2hvID0gVFJVRX0KIyMgZnVsbCBtb2RlbCBhcyBiZWZvcmUKZnVsbF9tb2QgPC0gbG0oU3BlY2llcyB+IEFyZWEgKyBFbGV2YXRpb24gKyBOZWFyZXN0LCBnYWxhKQoKIyMgcmVkdWNlZCBtb2RlbCB3aXRob3V0IGBuZWFyZXN0YApyZWR1Y2VkX21vZCA8LSBsbShTcGVjaWVzIH4gQXJlYSArIEVsZXZhdGlvbiwgZ2FsYSkKCiMjIHVzZSBgYW5vdmEoJ3JlZHVjZWQnLCAnZnVsbCcpYCB0byBnZXQgdGhlIEYtdGVzdCByZXN1bHRzCmFub3ZhKHJlZHVjZWRfbW9kLCBmdWxsX21vZCkKYGBgCgo6Ojogbm90ZQoKVGhlICRGJC1zdGF0aXN0aWMgaXMgc21hbGwgYW5kIHRoZSAkcCQtdmFsdWUgaXMgbGFyZ2Ugc28gd2UgY2Fubm90IHJlamVjdCAkSF8wJCwgYW5kIGhlbmNlIHdlIGNvbmNsdWRlIHRoYXQgd2Ugd291bGQgYmUganVzdGlmaWVkIGluIGRyb3BwaW5nIGBOZWFyZXN0YCBmcm9tIG91ciBtb2RlbC4KCjo6OgoKIyMjIyBVc2luZyBhICR0JC10ZXN0CgpBbm90aGVyIG9wdGlvbiBmb3IgdGVzdGluZyB0aGUgc2lnbmZpY2FuY2Ugb2Ygb25lIHByZWRpY3RvciBpcyB0byBlc3RpbWF0ZSBhICR0JC1zdGF0aXN0aWMgYXMKCiQkCnRfaSA9IFxmcmFje1xiZXRhX2l9e1x0ZXh0e1NFfSBcbGVmdCggXGJldGFfaSBccmlnaHQpfQokJAoKYW5kIGNvbXBhcmUgaXQgdG8gYSAkdCQtZGlzdHJpYnV0aW9uIHdpdGggJG4gLSBrJCBkZWdyZWVzIG9mIGZyZWVkb20uIAoKOjo6IHRpcAoKV2UgY2FuIGdldCB0aGUgcmVzdWx0cyBvZiB0aGUgJHQkLXRlc3Qgd2l0aCBgc3VtbWFyeSgpYCBmcm9tIGJhc2UgKipSKiogb3IgYHN1bWFyeSgpYCBmcm9tIHRoZSAqKntmYXJhd2F5fSoqIHBrZywgd2hpY2ggaXMgbXVjaCBsZXNzIHZlcmJvc2UuCgo6OjoKCmBgYHtyIGdhbGFfb25lX3ByZWRpY3Rvcl90LCBlY2hvID0gVFJVRX0KIyMgdmlhIGBiYXNlOjpzdW1tYXJ5KClgCnN1bW1hcnkoZnVsbF9tb2QpCgojIyB2aWEgYGZhcmF3YXk6OnN1bWFyeSgpYApzdW1hcnkoZnVsbF9tb2QpCmBgYAoKOjo6IG5vdGUKCkhlcmUgd2UgZ2V0ICR0ID0gXHNxcnR7Rn0kIGFuZCB0aGUgc2FtZSAkcCQtdmFsdWUgYXMgYmVmb3JlLgoKOjo6CgoKIyMjIFRlc3RpbmcgMisgcHJlZGljdG9ycwoKU29tZXRpbWVzIHdlIG1pZ2h0IHdhbnQgdG8ga25vdyB3aGV0aGVyIHdlIGNhbiBkcm9wIDIrIHByZWRpY3RvcnMgZnJvbSBhIG1vZGVsLiBGb3IgZXhhbXBsZSwgY291bGQgd2UgZHJvcCBib3RoICRcdGV4dHthcmVhfSQgYW5kICRcdGV4dHtuZWFyZXN0fSQgZnJvbSBvdXIgZnVsbCBtb2RlbD8KCiQkClxiZWdpbnthbGlnbmVkfQpcVGhldGE6IFx0ZXh0e3NwZWNpZXN9X2kgJj0gXGJldGFfMCArIFxiZXRhXzEgXHRleHR7YXJlYX1faSArIFxiZXRhXzIgXHRleHR7ZWxldmF0aW9ufV9pICsgXGJldGFfMyBcdGV4dHtuZWFyZXN0fV9pICsgZV9pIFxcCn4gXFwgClx0aGV0YTogXHRleHR7c3BlY2llc31faSAmPSBcYmV0YV8wICsgXGJldGFfMiBcdGV4dHtlbGV2YXRpb259X2kgKyBlX2kKXGVuZHthbGlnbmVkfQokJAoKJEhfMCA6IFxiZXRhXzEgPSBcYmV0YV8zID0gMCQKCjo6OiB0aXAKCkFnYWluIHdlIGNhbiBjb25kdWN0IHRoaXMgdGVzdCBieSBmaXR0aW5nIGJvdGggbW9kZWxzIGFuZCBjYWxjdWxhdGluZyAkRiQuCgo6OjoKCmBgYHtyIGdhbGFfb25lX3ByZWRpY3Rvcl9GMiwgZWNobyA9IFRSVUV9CiMjIHJlZHVjZWQgbW9kZWwgd2l0aG91dCBgYXJlYWAgYW5kIGBuZWFyZXN0YApyZWR1Y2VkX21vZCA8LSBsbShTcGVjaWVzIH4gRWxldmF0aW9uLCBnYWxhKQojIyB1c2UgYGFub3ZhKCdyZWR1Y2VkJywgJ2Z1bGwnKWAgdG8gZ2V0IHRoZSBGLXRlc3QgcmVzdWx0cwphbm92YShyZWR1Y2VkX21vZCwgZnVsbF9tb2QpCmBgYAoKOjo6IG5vdGUKClRoZSAkRiQtc3RhdGlzdGljIGlzIHJhdGhlciBzbWFsbCBhbmQgdGhlICRwJC12YWx1ZSBpcyBiaWcgc28gd2UgY2Fubm90IHJlamVjdCAkSF8wJCwgc3VnZ2VzdGluZyB3ZSB3b3VsZCBiZSBqdXN0aWZpZWQgaW4gZHJvcHBpbmcgYXJlYSBhbmQgZWxldmF0aW9uIGZyb20gdGhlIG1vZGVsLgoKOjo6CgoKIyMjIyBNdWx0aXBsZSAkdCQtdGVzdHMgdmVyc3VzIG9uZSAkRiQtdGVzdAoKQ291bGQgd2UgaW5zdGVhZCBldmFsdWF0ZSBvdXIgbnVsbCBoeXBvdGhlc2lzIGJ5IGV4YW1pbmluZyBvdXIgdGFibGUgb2YgJHQkLXN0YXRpc3RpY3MgYW5kIGFzc29jaWF0ZWQgJHAkLXZhbHVlcz8KCmBgYHtyIGdhbGFfb25lX3ByZWRpY3Rvcl90MiwgZWNobyA9IFRSVUV9CiMjIHQtc3RhdGlzdGljcyBmcm9tIG91ciBmdWxsIG1vZGVsCnN1bWFyeShmdWxsX21vZCkKYGBgCgpUaGlzIGNyZWF0ZXMgdGhlIHByb2JsZW0gb2YgaGF2aW5nIHRvIG1ha2Ugb3VyIGRlY2lzaW9uIGJhc2VkIG9uIDIgJHAkLXZhbHVlcyByYXRoZXIgdGhhbiBvbmUgYW5kIGVhY2ggJHAkLXZhbHVlIGNvcnJlc3BvbmRzIHRvIGEgc2VwYXJhdGUgJHQkLXRlc3Qgd2l0aCB0aGUgb3RoZXIgcHJlZGljdG9yIGluIHRoZSBtb2RlbC4gVGh1cywgd2UgbXVzdCB1c2UgdGhlIHNpbmdsZSAkRiQtdGVzdC4gCgoKIyMjIFRlc3RpbmcgYSBzdWJzcGFjZQoKU29tZSB0ZXN0cyBjYW5ub3QgYmUgZXhwcmVzc2VkIGluIHRlcm1zIG9mIHRoZSBpbmNsdXNpb24gb3IgZXhjbHVzaW9uIG9mIHByZWRpY3RvcnMuIEZvciBleGFtcGxlLCBjb25zaWRlciBhIHRlc3Qgb2Ygd2hldGhlciB0aGUgYXJlYXMgb2YgdGhlIGN1cnJlbnQgYW5kIGFkamFjZW50IGlzbGFuZCBjb3VsZCBiZSBhZGRlZCB0b2dldGhlciBhbmQgdXNlZCBpbiBwbGFjZSBvZiB0aGUgdHdvIHNlcGFyYXRlIHByZWRpY3RvcnM6CgokJApcdGV4dHtzcGVjaWVzfV9pID0gXGJldGFfMCArIFxiZXRhXzEgXHRleHR7YXJlYX1faSArIFxiZXRhXzIgXHRleHR7YWRqYWNlbnR9X2kgKyBcZG90cyArIGVfaSBcXAp+IFxcClx0ZXh0e3NwZWNpZXN9X2kgPSBcYmV0YV8wICsgXGJldGFfMSBcdGV4dHsoYXJlYSArIGFkamFjZW50KX1faSArIFxkb3RzICsgZV9pCiQkCgokSF8wIDogXGJldGFfe1x0ZXh0e2FyZWF9fSA9IFxiZXRhX3tcdGV4dHthZGphY2VudH19JAoKOjo6IHRpcAoKV2UgY2FuIGRvIHRoaXMgYnkgdXNpbmcgdGhlIGBJKClgIGZ1bmN0aW9uLCB3aGljaCB0ZWxscyAqKlIqKiB0byB0cmVhdCB0aGluZ3MgaW5zaWRlIHRoZSBgKClgIGFzIGFuIG9wZXJhdGlvbi4KCjo6OgoKYGBge3IgZ2FsYV9zdWJzcGFjZSwgZWNobyA9IFRSVUV9CiMjIGZ1bGwgbW9kZWwgYXMgYmVmb3JlCmZ1bGxfbW9kIDwtIGxtKFNwZWNpZXMgfiBBcmVhICsgQWRqYWNlbnQgKyBFbGV2YXRpb24gKyBOZWFyZXN0LCBnYWxhKQoKIyMgcmVkdWNlZCBtb2RlbCB3aXRob3V0IGBlbGV2YXRpb24gKyBuZWFyZXN0YApjb21iX21vZCA8LSBsbShTcGVjaWVzIH4gSShBcmVhICsgQWRqYWNlbnQpICsgRWxldmF0aW9uICsgTmVhcmVzdCwgZ2FsYSkKCiMjIHVzZSBgYW5vdmEoJ2NvbWJpbmVkJywgJ2Z1bGwnKWAgdG8gZ2V0IHRoZSBGLXRlc3QgcmVzdWx0cwphbm92YShjb21iX21vZCwgZnVsbF9tb2QpCmBgYAoKOjo6IG5vdGUKClRoZSAkRiQtc3RhdGlzdGljIGlzIGxhcmdlIGFuZCB0aGUgJHAkLXZhbHVlIGlzIHJlYXNvbmFibHkgc21hbGwgc28gd2UgcmVqZWN0ICRIXzAkLgoKOjo6Cgo8YnI+CgpXaGF0IGlmIHdlIHdhbnRlZCB0byB0ZXN0IHdoZXRoZXIgYSBwcmVkaWN0b3IgaGFkIGEgc3BlY2lmaWMgKG5vbi16ZXJvKSB2YWx1ZT8gRm9yIGV4YW1wbGUsIGlzIHRoZXJlIGEgMToxIHJlbGF0aW9uc2hpcCBiZXR3ZWVuICRcdGV4dHtzcGVjaWVzfSQgYW5kICRcdGV4dHtlbGV2YXRpb259JCBhZnRlciBjb250cm9sbGluZyBmb3IgdGhlIG90aGVyIHByZWRpY3RvcnM/IFdlIGNvdWxkIHdyaXRlIHRoaXMgbW9kZWwgYXMKCiQkClx0ZXh0e3NwZWNpZXN9X2kgPSBcYmV0YV8wICsgXGJldGFfMSBcdGV4dHthcmVhfV9pICsgKDEgXHRpbWVzIFx0ZXh0e2VsZXZhdGlvbn1faSkgKyBcYmV0YV8zIFx0ZXh0e25lYXJlc3R9X2kgKyBlX2kKJCQKCiRIXzAgOiBcYmV0YV8yID0gMSQKCjo6OiB0aXAKCldlIGNhbiBkbyB0aGlzIGluICoqUioqIGJ5IHVzaW5nIHRoZSBgb2Zmc2V0KClgIGZ1bmN0aW9uLCB3aGljaCBhbGxvd3MgdXMgdG8gc2V0IGEgZml4ZWQgYmV0YSByYXRoZXIgdGhhbiB0cmVhdCBpdCBhcyB1bmtub3duIGFuZCBlc3RpbWF0ZSBpdC4KCjo6OgoKYGBge3IgZ2FsYV9zdWJzcGFjZV9lbGV2LCBlY2hvID0gVFJVRX0KIyMgZnVsbCBtb2RlbApmdWxsX21vZCA8LSBsbShTcGVjaWVzIH4gQXJlYSArIEVsZXZhdGlvbiArIE5lYXJlc3QsIGdhbGEpCgojIyBtb2RlbCB3aXRoIGVmZmVjdCBvZiBgZWxldmF0aW9uYCA9IDEKZml4ZWRfbW9kIDwtIGxtKFNwZWNpZXMgfiBBcmVhICsgb2Zmc2V0KDEgKiBFbGV2YXRpb24pICsgTmVhcmVzdCwgZ2FsYSkKCiMjIHVzZSBgYW5vdmEoJ2NvbWInLCAnZnVsbCcpYCB0byBnZXQgdGhlIEYtdGVzdCByZXN1bHRzCmFub3ZhKGZpeGVkX21vZCwgZnVsbF9tb2QpCmBgYAoKOjo6IG5vdGUKClRoZSAkRiQtc3RhdGlzdGljIGlzIHZlcnkgbGFyZ2UgYW5kIHRoZSAkcCQtdmFsdWUgaXMgdGlueSBzbyB3ZSByZWplY3QgJEhfMCQuCgo6OjoKCiMjIyMgVXNpbmcgYSAkdCQtdGVzdAoKV2UgY2FuIGFsc28gbW9kaWZ5IG91ciAkdCQtdGVzdCBmcm9tIGJlZm9yZSBhbmQgdXNlIGl0IGZvciBvdXIgY29tcGFyaXNvbiBieSBpbmNsdWRpbmcgdGhlIGh5cG90aGVzaXplZCAkXGhhdHtcYmV0YX1fe0hfMH0kIGFzIGFuIG9mZnNldC4KCiQkCnRfaSA9IFxmcmFjeyhcaGF0e1xiZXRhX2l9IC0gXGJldGFfe0hfMH0pfXtcdGV4dHtTRX0gXGxlZnQoIFxoYXR7XGJldGF9X2kgXHJpZ2h0KX0KJCQKCjo6OiB0aXAKCldlIGNhbiBnZXQgJFxoYXR7XGJldGF9X3tcdGV4dHtlbGV2YXRpb259fSQgYW5kICRcdGV4dHtTRX0oXGhhdHtcYmV0YX1fe1x0ZXh0e2VsZXZhdGlvbn19KSQgZnJvbSBvdXIgbW9kZWwgc3VtbWFyeS4KCjo6OgoKYGBge3IgZ2FsYV9zdWJzcGFjZV9lbGV2XzIsIGVjaG8gPSBUUlVFfQojIyBmdWxsIG1vZGVsCnN1bWFyeShmdWxsX21vZCkKYGBgCgpUaGlzIGxlYXZlcyB1cyB3aXRoICRcaGF0e1xiZXRhfV97XHRleHR7ZWxldmF0aW9ufX0gXGFwcHJveCAwLjE3MSQgYW5kICRcdGV4dHtTRX0oXGhhdHtcYmV0YX1fe1x0ZXh0e2VsZXZhdGlvbn19KSBcYXBwcm94IDAuMDU0NSQuIAoKOjo6IHRhc2sKCkNhbGN1bGF0ZSB0aGUgJHQkLXRlc3QgYnkgaGFuZCBhbmQgY29tcGFyZSB0aGUgcmVzdWx0cyB0byB0aGUgYWJvdmUgJEYkLXRlc3QKCjo6OgoKYGBge3IgdC10ZXN0X3N1YnNwYWNlLCBlY2hvID0gVFJVRSwgcmVzdWx0cyA9ICJob2xkIn0KIyMgdCBzdGF0aXN0aWMKdF92YWx1ZSA8LSAoMC4xNzEzMzYgLSAxKSAvIDAuMDU0NTE5CgojIyBwLXZhbHVlID0gdF9hbHBoYSAqIFByKHRfdmFsdWUsIGRmKTsgYHB0KClgIGlzIHRoZSBwZGYgZm9yIGEgdC1kaXN0CihwX3ZhbHVlIDwtIDEuOTYgKiBwdChxID0gdF92YWx1ZSwgZGYgPSAoMzAgLSA0KSkpCgojIyB2ZXJpZnkgdF4yID0gRgphbGwuZXF1YWwodF92YWx1ZV4yLCBhbm92YShmaXhlZF9tb2QsIGZ1bGxfbW9kKSRGWzJdLCB0b2xlcmFuY2UgPSAwLjAwMDEpCmBgYAoKOjo6IG5vdGUKClRoZSAkcCQtdmFsdWUgaXMgdGhlIHNhbWUgYW5kICR0XjIgPSBGJCBhcyBiZWZvcmUKCjo6OgoKCiMjIENvbmZpZGVuY2UgaW50ZXJ2YWxzIGZvciAkXGJldGEkCgpXZSBjYW4gYWxzbyB1c2UgY29uZmlkZW5jZSBpbnRlcnZhbHMgKENJJ3MpIHRvIGV4cHJlc3MgdW5jZXJ0YWludHkgaW4gJFxoYXR7XGJldGFfaX0kLCB3aGljaCB0YWtlIHRoZSBmb3JtCgokJAoxMDAoMSAtIFxhbHBoYSlcJSB+IFx0ZXh0e0NJfTogXGhhdHtcYmV0YV9pfSBccG0gdF97bi1wfV57KFxhbHBoYSAvIDIpfSBcb3BlcmF0b3JuYW1le1NFfSAoIFxoYXR7XGJldGF9ICkKJCQKCndoZXJlIGhlcmUgJFxhbHBoYSQgaXMgb3VyICpwcmVkZXRlcm1pbmVkKiBUeXBlLUkgZXJyb3IgcmF0ZS4gVG8gZG8gc28sIHdlIG5lZWQgdGhlIGluZm9ybWF0aW9uIGZyb20gdGhlIG1vZGVsIHN1bW1hcnkuCgpgYGB7ciBnYWxhX0NJLCBlY2hvID0gVFJVRX0KIyMgdC1zdGF0aXN0aWNzIGZyb20gb3VyIGZ1bGwgbW9kZWwKc3VtYXJ5KGZ1bGxfbW9kKQpgYGAKCjo6OiB0aXAKCldlIGNhbiBjb25zdHJ1Y3QgYSA5NSUgQ0kgb24gJFxoYXR7XGJldGF9JCB1c2luZyB0aGUgMi41JSBhbmQgOTcuNSUgcGVyY2VudGlsZXMgb2YgdGhlICR0JC1kaXN0cmlidXRpb24gd2l0aCAkZGYgPSAzMCAtIDQgPSAyNiQuCgo6OjoKCmBgYHtyIGdhbGFfQ0lfMiwgZWNobyA9IFRSVUV9CiMjIGNyaXRpY2FsIHZhbHVlIGZvciB0aGUgdC1kaXN0cmlidXRpb24KIyMgYHF0KClgIGlzIHRoZSBxdWFudGlsZSBmdW5jdGlvbiBmb3IgdGhlIHQtZGlzdDsgYHBgIGlzIHRoZSAoMS1hbHBoYS8yKSB2YWx1ZSAKdF9jcml0IDwtIHF0KHAgPSAwLjk3NSwgZGYgPSAzMC00KQoKIyMgOTUlIENJCkNJOTVfYmV0YSA8LSAwLjAxOTA4NSArIGMoLTEsMSkgKiB0X2NyaXQgKiAwLjAyNjc2NApDSTk1X2JldGEgfD4gcm91bmQoMykKYGBgCgo6Ojogbm90ZQoKVGhpcyA5NSUgQ0kgaW5jbHVkZXMgemVybyBzbyB3ZSB3b3VsZCBjb25jbHVkZSB0aGF0IHdlIGNhbm5vdCByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyB0aGF0ICRcYmV0YSA9IDAkLgoKOjo6CgojIyMgQ29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIGFsbCAkXGJldGEkCgo6OjogdGlwCgpXZSBjYW4gcXVpY2tseSBnZXQgdGhlIDk1JSBDSSBmb3IgYWxsIHRoZSAkXGJldGFfaSQgdXNpbmcgdGhlIGZ1bmN0aW9uIGBjb25maW50KClgLgoKOjo6CgpgYGB7ciBnYWxhX0NJX2FsbCwgZWNobyA9IFRSVUV9CiMjIDk1JSBDSSdzIGZvciBhbGwgYmV0YXMgKHJvdW5kZWQgdG8gdGhlIG5lYXJlc3QgdGhvdXNhbmR0aCkKY29uZmludChmdWxsX21vZCkgfD4gcm91bmQoMykKYGBgCgoKIyMgQm9vdHN0cmFwIGNvbmZpZGVuY2UgaW50ZXJ2YWxzCgpUaGUgYWJvdmUgJEYkLSBhbmQgJHQkLWJhc2VkIENJJ3MgZGVwZW5kIG9uIHRoZSBhc3N1bXB0aW9uIG9mIG5vcm1hbGl0eS4gUmVjYWxsIHRoYXQgdGhlIGJvb3RzdHJhcCBtZXRob2QgcHJvdmlkZXMgYSB3YXkgdG8gY29uc3RydWN0IENJJ3Mgd2l0aG91dCB0aGlzIGFzc3VtcHRpb24uCgojIyMgQm9vdHN0cmFwIHByb2NlZHVyZQoKOjo6IHRhc2sKClN0ZXAgMTogRml0IHlvdXIgbW9kZWwgdG8gdGhlIGRhdGEKCmBgYHtyfQojIyB3ZSBhbHJlYWR5IGZpdCBgZnVsbF9tb2RgIGFib3ZlCmBgYAoKU3RlcCAyOiBDYWxjdWxhdGUgJFxtYXRoYmZ7ZX0gPSBcbWF0aGJme3l9IC0gXG1hdGhiZntYfSBcYm9sZHN5bWJvbHtcaGF0e1xiZXRhfX0kIAoKYGBge3IgYm9vdHN0cmFwX0NJXzEtMiwgZWNobyA9IFRSVUV9CiMjIHJlc2lkdWFscyBmcm9tIG91ciBmdWxsIG1vZGVsCnJlc2lkcyA8LSByZXNpZHVhbHMoZnVsbF9tb2QpCmBgYAoKU3RlcCAzYTogR2VuZXJhdGUgJFxtYXRoYmZ7ZX1eKiQgYnkgc2FtcGxpbmcgKndpdGggcmVwbGFjZW1lbnQqIGZyb20gJFxtYXRoYmZ7ZX0kICAKU3RlcCAzYjogQ2FsY3VsYXRlICRcbWF0aGJme3l9XiogPSBcbWF0aGJme1h9IFxib2xkc3ltYm9se1xoYXR7XGJldGF9fSArIFxtYXRoYmZ7ZX1eKiQgIApTdGVwIDNjOiBFc3RpbWF0ZSAkXGJvbGRzeW1ib2x7XGhhdHtcYmV0YX19XiokIGZyb20gJFxtYXRoYmZ7WH0kICYgJFxtYXRoYmZ7eX1eKiQgIAoKYGBge3IgYm9vdHN0cmFwX0NJXzMsIGVjaG8gPSBUUlVFfQojIyBudW1iZXIgb2YgYm9vc3RyYXAgc2FtcGxlcwpuYiA8LSAxMDAwCiMjIGVtcHR5IG1hdHJpeCBmb3IgYmV0YSBlc3RpbWF0ZXMKYmV0YV9lc3QgPC0gbWF0cml4KE5BLCBuYiwgNCkKIyMgZml0dGVkIHZhbHVlcyBmcm9tIG91ciBmdWxsIG1vZGVsID0gWCpiZXRhClhiZXRhIDwtIGZpdHRlZChmdWxsX21vZCkKIyMgc2FtcGxlIG1hbnkgdGltZXMKZm9yKGkgaW4gMTpuYikgewogICMjIDNhOiBzYW1wbGUgdy8gcmVwbGFjZW1lbnQgZnJvbSBlCiAgZV9zdGFyIDwtIHNhbXBsZShyZXNpZHMsIHJlcCA9IFRSVUUpCiAgIyMgM2I6IGNhbGN1bGF0ZSB5X3N0YXIKICB5X3N0YXIgPC0gWGJldGEgKyBlX3N0YXIKICAjIyAzYzogcmUtZXN0aW1hdGUgYmV0YV9zdGFyIGZyb20gWCAmIHlfc3RhcgogIGJldGFfc3RhciA8LSB1cGRhdGUoZnVsbF9tb2QsIHlfc3RhciB+IC4pCiAgIyMgc2F2ZSBlc3RpbWF0ZWQgYmV0YXMKICBiZXRhX2VzdFtpLF0gPC0gY29lZihiZXRhX3N0YXIpCn0KYGBgCgpTdGVwIDQ6IFNlbGVjdCB0aGUgJFx0ZnJhY3tcYWxwaGF9ezJ9JCBhbmQgJCgxIC0gXHRmcmFje1xhbHBoYX17Mn0pJCBwZXJjZW50aWxlcyBmcm9tIHRoZSBzYXZlZCAkXGJvbGRzeW1ib2x7XGhhdHtcYmV0YX19XiokCgpgYGB7ciBib290c3RyYXBfQ0lfNCwgZWNobyA9IFRSVUV9CiMjIGV4dHJhY3QgMi41JSBhbmQgOTcuNSUgdmFsdWVzCkNJOTUgPC0gYXBwbHkoYmV0YV9lc3QsIDIsIHF1YW50aWxlLCBjKDAuMDI1LCAwLjk3NSkpCmNvbG5hbWVzKENJOTUpIDwtIGMoIkludGVyY2VwdCIsIGNvbG5hbWVzKGdhbGFbLDM6NV0pKQp0KHJvdW5kKENJOTUsIDMpKQpgYGAKCjo6OgoKIyMgQ29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgbmV3IHByZWRpY3Rpb25zCgpHaXZlbiBhIGZpdHRlZCBtb2RlbCAkXG1hdGhiZnt5fSA9IFxtYXRoYmZ7WH0gXGJvbGRzeW1ib2x7XGJldGF9ICsgXG1hdGhiZntlfSQsIHdlIG1pZ2h0IHdhbnQgdG8ga25vdyB0aGUgdW5jZXJ0YWludHkgYXJvdW5kIGEgbmV3IGVzdGltYXRlICRcbWF0aGJme3l9XiokIGdpdmVuIHNvbWUgbmV3IHByZWRpY3RvciAkXG1hdGhiZntYfV4qJC4gCgojIyMgQ0kgZm9yIHRoZSBtZWFuIHJlc3BvbnNlCgpUaGluayBiYWNrIHRvIG91ciBzaW1wbGUgbW9kZWwgZm9yIHBsYW50IGRpdmVyc2l0eSBhcyBhIGZ1bmN0aW9uIG9mIGlzbGFuZCBhcmVhLCBzdWNoIHRoYXQKCiQkClx0ZXh0e3NwZWNpZXN9X2kgPSBcYmV0YV8wICsgXGJldGFfMSBcdGV4dHthcmVhfV9pICsgZV9pCiQkCgpTdXBwb3NlIHdlIHdhbnRlZCB0byBlc3RpbWF0ZSB0aGUgdW5jZXJ0YWludHkgaW4gdGhlICphdmVyYWdlKiBkaXZlcnNpdHkgZm9yIGFuIGlzbGFuZCB3aXRoIGFuIGFyZWEgb2YgMjAwMCBrbTxzdXA+Mjwvc3VwPi4gUmVjYWxsIHRoYXQgb3VyIENJIG9uIHRoZSBtZWFuIHJlc3BvbnNlIGlzIGdpdmVuIGJ5CgokJApcbWF0aGJme1xoYXR7eX19XiogXHBtIH4gdF57KFxhbHBoYSAvIDIpfV97ZGZ9IFxzaWdtYSBcc3FydHsge1xtYXRoYmZ7WH1eKn1ee1x0b3B9IChcbWF0aGJme1h9XntcdG9wfSBcbWF0aGJme1h9KV57LTF9IFxtYXRoYmZ7WH1eKiB9IAokJAoKYW5kIGhlcmUgJFxtYXRoYmZ7WH1eKiA9IFsxIH5+IDIwMDBdJCAoaWUsIGEgJDEgXHRpbWVzIDIkIHJvdyBtYXRyaXgpLgoKCiMjIyMgQ0kgYnkgaGFuZAoKOjo6IHRhc2sKCkNhbGN1bGF0ZSB0aGUgQ0kgYnkgaGFuZCB1c2luZyBtYXRyaXggb3BlcmF0aW9ucyBpbiAqKlIqKi4KCjo6OgoKYGBge3IgQ0lfYnlfaGFuZCwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyMgbWF0cml4IG9mIHByZWRpY3RvcnMKWFggPC0gbW9kZWwubWF0cml4KHNpbXBsZV9tb2RlbCkKCiMjIG5ldyBYOyB2ZWN0b3IgZm9yIG5vdwpYX3N0YXIgPC0gYyhJbnRlcmNlcHQgPSAxLCBBcmVhID0gMjAwMCkKCiMjIGluc2lkZSBzcXJ0CmlubmVyX1ggPC0gdChYX3N0YXIpICUqJSBzb2x2ZSh0KFhYKSAlKiUgWFgpICUqJSBYX3N0YXIKCiMjIGNyaXRpY2FsIHQtdmFsdWUKdF9jcml0IDwtIHF0KDAuOTc1LCBkZiA9IG5uLTIpCgojIyBlc3RpbWF0ZWQgU0QKc2lnbWEgPC0gc3VtbWFyeShzaW1wbGVfbW9kZWwpJHNpZ21hCgojIyBwcmVkaWN0ZWQgeQp5X3N0YXIgPC0gc3VtKFhfc3RhciAqIGNvZWYoc2ltcGxlX21vZGVsKSkKCiMjIDk1JSBDSQp5X3N0YXIgKyBjKC0xLDEpICogdF9jcml0ICogc2lnbWEgKiBzcXJ0KGlubmVyX1gpCmBgYAoKIyMjIyBDSSB3aXRoIGBwcmVkaWN0KClgCgo6OjogdGlwCgpJdCdzIG11Y2ggZWFzaWVyIHRvIHVzZSB0aGUgYHByZWRpY3QoKWAgZnVuY3Rpb24gdG8gZ2l2ZSB1cyBhIENJLiBUeXBlIGA/cHJlZGljdGAgYXQgdGhlIGNvbW1hbmQgcHJvbXB0IHRvIHNlZSB0aGUgZnVuY3Rpb24ncyBhcmd1bWVudHMuCgo6OjoKCmBgYHtyIENJX3dpdGhfcHJlZGljdH0KcHJlZGljdChzaW1wbGVfbW9kZWwsCiAgICAgICAgbmV3ID0gZGF0YS5mcmFtZSh0KFhfc3RhcikpLAogICAgICAgIGxldmVsID0gMC45NSwKICAgICAgICBpbnRlcnZhbCA9ICJjb25maWRlbmNlIikKYGBgCgo6Ojogc3VjY2VzcwoKVGhlIGVzdGltYXRlZCBsb3dlciBhbmQgdXBwZXIgYm91bmRzIGFyZSB0aGUgc2FtZSBhcyB0aG9zZSB3ZSBvYnRhaW5lZCBieSBoYW5kLgoKOjo6CgojIyMgUHJlZGljdGlvbiBpbnRlcnZhbCBmb3IgYSBuZXcgcmVzcG9uc2UKCldlIHNhdyBpbiBsZWN0dXJlIHRoYXQgd2UgY2FuIGFsc28gZXN0aW1hdGUgdGhlIHVuY2VydGFpbnR5IGZvciBhICpzcGVjaWZpYyogcHJlZGljdGlvbiByYXRoZXIgdGhhbiB0aGUgYXZlcmFnZSByZXNwb25zZS4gTGV0J3Mgbm93IGltYWdpbmUgd2Ugd2FudGVkIHRvIHByZWRpY3QgaG93IG1hbnkgcGxhbnQgc3BlY2llcyB3ZSBtaWdodCBmaW5kIG9uIGEgaHlwb3RoZXRpY2FsIGlzbGFuZCB0aGF0IG1pZ2h0IG9uZSBkYXkgZW1lcmdlIGZyb20gYW4gdW5kZXJ3YXRlciB2b2xjYW5vIGFuZCBpcyB1bHRpbWF0ZWx5IDIwMDAga208c3VwPjI8L3N1cD4uIFJlY2FsbCB0aGF0IGEgUEkgaXMgZ2l2ZW4gYnkgCgokJApcbWF0aGJme1xoYXR7eX19XiogXHBtIH4gdF57KFxhbHBoYSAvIDIpfV97ZGZ9IFxzaWdtYSBcc3FydHsxICsge1xtYXRoYmZ7WH1eKn1ee1x0b3B9IChcbWF0aGJme1h9XntcdG9wfSBcbWF0aGJme1h9KV57LTF9IFxtYXRoYmZ7WH1eKiB9IAokJAoKOjo6IHRpcAoKQ2FsY3VsYXRpbmcgdGhlIFBJIGJ5IGhhbmQgc2ltcGx5IHJlcXVpcmVzIGEgZGlmZmVyZW50IGNhbGN1bGF0aW9uIGZvciBgaW5uZXJfWGAgdGhhbiB3ZSBtYWRlIGFib3ZlLgoKOjo6CgpgYGB7ciBQSV9ieV9oYW5kLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIyBuZXcgWF9zdGFyClhfc3RhciA8LSBjKEludGVyY2VwdCA9IDEsIEFyZWEgPSAyMDAwKQoKIyMgaW5zaWRlIHNxcnQKaW5uZXJfWCA8LSAxICsgdChYX3N0YXIpICUqJSBzb2x2ZSh0KFhYKSAlKiUgWFgpICUqJSBYX3N0YXIKCiMjIDk1JSBDSQp5X3N0YXIgKyBjKC0xLDEpICogdF9jcml0ICogc2lnbWEgKiBzcXJ0KGlubmVyX1gpCmBgYAoKTm90aWNlIGhvdyBtdWNoIHdpZGVyIHRoaXMgaW50ZXJ2YWwgaXMgdGhhbiB0aGUgQ0kgd2UgZXN0aW1hdGVkIGFib3ZlLgoKOjo6IHRpcAoKV2UgY291bGQgaW5zdGVhZCB1c2UgYHByZWRpY3QoKWAgYXMgd2UgZGlkIGFib3ZlIGZvciB0aGUgQ0kgYnkgc3dpdGNoaW5nIHRoZSBgaW50ZXJ2YWxgIGFyZ3VtZW50IHRvIGAicHJlZGljdGlvbiJgLgoKOjo6CgpgYGB7ciBQSV93aXRoX3ByZWRpY3R9CnByZWRpY3Qoc2ltcGxlX21vZGVsLCAKICAgICAgICBuZXcgPSBkYXRhLmZyYW1lKHQoWF9zdGFyKSksCiAgICAgICAgbGV2ZWwgPSAwLjk1LCAKICAgICAgICBpbnRlcnZhbCA9ICJwcmVkaWN0aW9uIikKYGBgCgoK