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.

Bivariate plot of the number of species on an island versus the area of the island in kilometers.

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])

Bivariate plot of the number of species on an island versus the area of the island in kilometers, including the estimated regression line through the data.

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