Variable selection
In previous lectures we have looked at tests for single explanatory
variables and groups of explanatory variables.
Often, however, a statistician will simply want to know what is the
best group of explanatory variables for describing the response.
In this lecture we will consider various criteria for model
selection, and cover an example on finding a suitable variable set “by
hand”.
Overfitting and Underfitting
We begin by considering the following question: Should all the
explanatory variables be used, or should some be omitted?
Why might we want to omit variables?
Perhaps we want to detect variables which affect the response but
avoid collecting unnecessary measurements in the future. This is called
variable screening
Perhaps we want to predict from the model. Our predictions will
be less accurate if the model is incorrectly specified.
We first consider the risks and benefits of including a predictor
that might have coefficient \(\beta=0\).
Dropping or Including Variables that have \(\beta=0\)
Suppose we have a regression model with several variables. We might
suspect one has regression coefficient \(\beta=0\). Should we drop it?
Some mathematical theory shows that if we omit a variable which
(truly) has regression coefficient \(\beta=0\) then
Both these things are good.
Conversely if we include a variable whose coefficient \(\beta=0\) this increases these variances
unnecessarily. This is called overfitting. We want to
avoid this.
Dropping or Including Variables that have \(\beta \ne 0\)
On the other hand suppose we make a mistake and omit a variable that
has regression coefficient \(\ne
0\).
Then we have the opposite problem, called
underfitting. Mathematical theory shows the consequence
of underfitting is that
if the wrongly-omitted variables are correlated with the
remaining variables then the expected values of the remaining regression
coefficients \(\beta_0,\beta_1,...\beta_{p-1}\) are
biased. (i.e. may be a too big or too small).
Whether or not the omitted variables are correlated with the
existing ones, the predictions \(\hat
y_i\) are biased.
Both these things are bad!
In practice we do not know if parameters are zero.
We could use a hypothesis test to help us decide, but this is not a
definitive answer.
A Numerical Illustration of Under and Overfitting
In this artificial example, we consider an industrial process whose
Yield is thought to depend on the process temperature at stage A of the
process, and possibly also on the temperature at stage B.
i.e. We consider the model \[ Y=\beta_0 +
\beta_1 A+ \beta_2 B +\varepsilon.\]
We will assume the values of \(A\)
and \(B\) and \(\varepsilon\) are given in the data below.
The errors are random \(\varepsilon \sim
\mbox{Normal}(0, 1)\).
Download simulation1.csv
## sim = read.csv("simulation1.csv", header = TRUE)
What we want to know is, what are the implications of using an
incorrect model, i.e. incorrect assumptions about the \(\beta\)s. To understand the implications,
we simulate two scenarios.
Scenario 1: \(\beta_0=50\), \(\beta_1=10\) and \(\beta_2=0\).
For this illustration, the correct model doesn’t depend on \(B\) at all: \[Y = 50 + 10 A + \varepsilon\]
The following shows the result of modelling \(Y\) by (1) \(A\) alone and (2) by \(A\) and \(B\).
dat <- sim |>
mutate(Y = 50 + 10 * A + epsilon)
lm1 = lm(Y ~ A, data = dat)
lm2 = lm(Y ~ A + B, data = dat)
lst(lm1, lm2) |>
map_dfr(tidy, .id = "model")
# A tibble: 5 × 6
model term estimate std.error statistic p.value
<chr> <chr> <dbl> <dbl> <dbl> <dbl>
1 lm1 (Intercept) 49.3 2.03 24.2 3.43e-15
2 lm1 A 10.0 0.112 89.4 2.71e-25
3 lm2 (Intercept) 48.8 2.14 22.8 3.43e-14
4 lm2 A 9.76 0.338 28.9 7.01e-16
5 lm2 B 0.290 0.350 0.827 4.19e- 1
Adding the unnecessary variable doesn’t introduce bias (\(\hat\beta_1 \approx 10\)), but the standard
error of \(\hat\beta_1\) is higher in
the misspecified model.
Scenario 2: \(\beta_0=50\), \(\beta_1=5\) and \(\beta_2=5\).
We consider the model where \(Y\)
depends on both \(A\) and \(B\), \[ Y = 50 + 5 A + 5 B + \varepsilon
\]
dat <- sim |>
mutate(Y = 50 + 5 * A + 5 * B + epsilon)
lm1 = lm(Y ~ A, data = dat)
lm2 = lm(Y ~ A + B, data = dat)
lst(lm1, lm2) |>
map_dfr(tidy, .id = "model")
# A tibble: 5 × 6
model term estimate std.error statistic p.value
<chr> <chr> <dbl> <dbl> <dbl> <dbl>
1 lm1 (Intercept) 57.8 7.57 7.63 4.74e- 7
2 lm1 A 9.58 0.418 22.9 8.94e-15
3 lm2 (Intercept) 48.8 2.14 22.8 3.43e-14
4 lm2 A 4.76 0.338 14.1 8.48e-11
5 lm2 B 5.29 0.350 15.1 2.75e-11
In the first model, \(B\) is wrongly
ignored and \(A\) is both biased and
has a larger standard error. In the second model, the values of \(\hat\beta_1\) and \(\hat\beta_2\) are close to the true ones
and the standard errors are the same as in the second analysis of
Scenario 1.
In summary, if we omit a needed variable then we may introduce bias
into our estimation.
The big problem with bias is that there may be no sign that we have a
mistake.
In some cases we don’t even have the needed variable, so we
can’t try it in the model.
At least in case 1, when we wrongly enter an unnecessary variable, it
doesn’t bias our estimates, just increases our standard errors. So at
least we know exactly how bad things are. But with an omitted variable
we don’t know how bad things are.
For this reason, we may decide to err on the side of caution and add
in variables even if we are uncertain about their relevance. Then at
least we can avoid bias.
Model Selection: Bias and Variance
Model selection, then, comes down to balancing the effects of
variance and bias.
If we omit a variable, it may dramatically decrease the variance
of the other parameter estimates. This is a plus.
But if the variable is important, then the other parameter
estimates may be biased. This is a minus.
So what should we choose? There is no settled rule for every
circumstance, but in general we should probably be more concerned to
avoid bias (especially since we don’t know how bad this is!) than to
avoid a small increase in variance (at least we do know how bad it is -
we can see the standard errors.)
Consequently we may decide to err on the side of including variables
with P-values a little bigger than 0.05, especially if they seem to
induce a sizeable change in the other coefficient estimates. This
is another thing to watch out for in model selection.
Review of Model Selection Criteria
There are a number of measures we can use for model selection:
Coefficient of determination \(R^2\)
Adjusted Coefficient of determination \(R^2_{Adj}\)
Residual Standard Error \(S\)
\(P\)-values of individual
coefficients
Each of these are slightly different, so it pays to consider more
than one.
Coefficient of determination \(R^2\)
Recall that this is the proportion of total variation which is
explained by the model. (Alternatively, 1 minus the ratio of unexplained
variation to total variation): \[ R^2 =
\frac{SS_{Regn}}{SST} = 1 - \frac{\sum(y_i-\hat y_i)^2}{\sum(y_i-\bar
y)^2}. \] \(R^2\) is useful for
comparing two models with the same number of regression
parameters. If models have different numbers of regression parameters we
use the adjusted version.
Adjusted Coefficient of determination \(R^2_{Adj}\)
Recall that this is 1 minus the ratio of Mean Square Error to Mean
Square Total: \[ R^2_{Adj} = 1 -
\frac{MSE}{MST} = 1 - \frac{\sum(y_i-\hat y_i)^2/(n-p)}{\sum(y_i-\bar
y)^2/(n-1)}.\] When considering adding a variable to a
regression, the \(R^2_{Adj}\) goes up
if the variable is “worth” at least one row of data (one degree of
freedom), which is a pretty low bar to cross.
But at least if a variable can’t meet that rule then we know to
exclude it.
Residual Standard Error \(S\)
This is the standard deviation of the residuals, \[ S = \frac{1}{n-p}\sum{(y_i - \hat y_i)^2}
\] or in other words the standard deviation of how the \(y_i\)s vary around the model predictions.
We might choose to include a variable if it reduces the \(S\).
\(P\)-value for individual
coefficients
We usually prefer models that only include variables with coefficient
\(P\)-value is small (e.g. \(< 0.05\)).
However the “0.05” criterion is somewhat arbitrary and occasionally
we allow in variables with slightly higher \(P\)-values to potentially reduce bias.
Example: Model Selection for Climate Data
We wish to model the mean temperature in July (winter) in Aotearoa
New Zealand using data from 36 towns. We have information on location
(latitude, longitude, height (elevation), whether they’re by the sea or
in the North island) as well as climate information (mean temperature in
January, total sunshine hours and total rainfall).
Download climate.csv
## climate = read_csv("climate.csv")
climate
# A tibble: 36 × 10
Place Lat Long MnJanTemp MnJlyTemp Rain Sun Height Sea NorthIsland
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Kaitaia 35.1 173. 19.3 11.7 1418 2113 80 1 1
2 Kerikeri 35.2 174 18.9 10.8 1682 2004 73 1 1
3 Dargavi… 36 174. 18.6 10.7 1248 1956 20 1 1
4 Whangar… 35.7 174. 19.7 11 1600 1925 29 1 1
5 Auckland 36.9 175. 19.4 11 1185 2102 49 1 1
6 Tauranga 37.7 176. 18.5 9.3 1349 2277 4 1 1
7 Hamilton 37.8 175. 17.8 8.3 1201 2006 40 0 1
8 Rotorua 38.2 176. 17.5 7.3 1439 1948 307 0 1
9 Gisborne 38.7 178 18.7 13.6 1058 2204 4 1 1
10 Taupo 38.7 177. 17.3 6.5 1178 2021 376 0 1
# ℹ 26 more rows
Climate Exploratory Data Analysis
climate |>
pivot_longer(-c(Place, MnJlyTemp)) |>
ggplot() + geom_point(mapping = aes(x = value, y = MnJlyTemp)) + facet_wrap(vars(name),
scales = "free_x", ncol = 4)
Higher winter temperature associations
- Lower elevations (height)
- Lower latitudes (further north)
- Higher longitudes (further east - recall how Ao/NZ is oriented on a
map - possibly correlation with latitude, will check next!)
- Higher summer temperatures
- North island (could just be latitude?)
- Increased rainfall - up to a point! (subtropical vs rainy westcoast
- interaction with Island/latitude??)
- Closeness to the sea
- Increased sunshine hours
Many of these are likely correlated.
Climate: Latitude/Longitude and North vs South
ggplot(climate) + geom_point(mapping = aes(x = Long, y = -Lat, shape = as_factor(NorthIsland),
col = MnJlyTemp)) + geom_text(mapping = aes(x = Long, y = -Lat, label = Place),
size = 2, hjust = "right", nudge_x = 0.1, nudge_y = 0.2) + coord_equal()
Climate: Latitude/Longitude and North vs South
Ok, so we can see that the temperature is being driven by latitude
(further north is warmer) - the longitude effect is possibly just due to
orientation. It’s clear there’s an elevation (height) effect as well,
though that is possibly confounded with whether the town is close to the
sea (it can’t really be both!)
So we’d expect some collinearity here and figuring out which are the
best variables to be used might take a bit of playing!
We’ll start with Latitude, then add in Height and Sea:
lm1 = lm(MnJlyTemp ~ Lat, data = climate)
lm2 = lm(MnJlyTemp ~ Lat + Height, data = climate)
lm3 = lm(MnJlyTemp ~ Lat + Height + Sea, data = climate)
Climate: model output
Call:
lm(formula = MnJlyTemp ~ Lat, data = climate)
Residuals:
Min 1Q Median 3Q Max
-3.6992 -0.9786 0.1566 1.0588 4.7930
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 34.42127 3.94299 8.730 3.35e-10 ***
Lat -0.66187 0.09613 -6.885 6.25e-08 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 1.822 on 34 degrees of freedom
Multiple R-squared: 0.5824, Adjusted R-squared: 0.5701
F-statistic: 47.41 on 1 and 34 DF, p-value: 6.25e-08
Call:
lm(formula = MnJlyTemp ~ Lat + Height, data = climate)
Residuals:
Min 1Q Median 3Q Max
-2.0607 -0.6215 -0.1021 0.5145 3.9898
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 32.8788366 2.4333203 13.512 5.30e-15 ***
Lat -0.6005121 0.0596687 -10.064 1.38e-11 ***
Height -0.0071984 0.0009542 -7.544 1.12e-08 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 1.121 on 33 degrees of freedom
Multiple R-squared: 0.8467, Adjusted R-squared: 0.8374
F-statistic: 91.14 on 2 and 33 DF, p-value: 3.639e-14
Call:
lm(formula = MnJlyTemp ~ Lat + Height + Sea, data = climate)
Residuals:
Min 1Q Median 3Q Max
-1.7969 -0.4792 -0.0299 0.5042 3.7850
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 31.290016 2.290747 13.659 6.71e-15 ***
Lat -0.587826 0.054584 -10.769 3.59e-12 ***
Height -0.005121 0.001147 -4.463 9.38e-05 ***
Sea 1.294326 0.466063 2.777 0.00909 **
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 1.022 on 32 degrees of freedom
Multiple R-squared: 0.8765, Adjusted R-squared: 0.8649
F-statistic: 75.69 on 3 and 32 DF, p-value: 1.275e-14
Climate: First three predictors.
For these first three predictors:
- They have significant p-values
- \(S\) has gone down each time
- The adjusted \(R^2\) and has gone
up each time.
There is no reason to doubt that Lat
,
Height
and Sea
should all be included.
Also note the regression coefficient for Height
altered
by about 2 standard errors, which suggests that Sea
is
correcting for bias, all the more reason for including it.
This makes sense - sea level towns will mostly have a very low
height, but there’ll also be an additional warming effect of being close
to the sea.
Climate: Adding island
lm4 = lm(MnJlyTemp ~ Lat + Height + Sea + NorthIsland, data = climate)
lm4 |>
summary()
Call:
lm(formula = MnJlyTemp ~ Lat + Height + Sea + NorthIsland, data = climate)
Residuals:
Min 1Q Median 3Q Max
-1.2667 -0.5054 -0.2098 0.4707 3.4088
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 21.378217 4.637932 4.609 6.56e-05 ***
Lat -0.375517 0.101816 -3.688 0.000862 ***
Height -0.004416 0.001109 -3.981 0.000385 ***
Sea 1.803422 0.483327 3.731 0.000766 ***
NorthIsland 1.559710 0.647795 2.408 0.022193 *
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.9526 on 31 degrees of freedom
Multiple R-squared: 0.8959, Adjusted R-squared: 0.8825
F-statistic: 66.73 on 4 and 31 DF, p-value: 8.723e-15
Same conclusions as before, we should include
NorthIsland
Including it has induced big changes in the coefficients (especially
Lat
) which again is evidence it is important.
Why do you think adding North vs South island is useful over
and above Latitude? Wouldn’t Latitude do everything here??
Adding Rain
lm5 = lm(MnJlyTemp ~ Lat + Height + Sea + NorthIsland + Rain, data = climate)
lm5 |>
summary()
Call:
lm(formula = MnJlyTemp ~ Lat + Height + Sea + NorthIsland + Rain,
data = climate)
Residuals:
Min 1Q Median 3Q Max
-1.2829 -0.5042 -0.1944 0.4422 3.4651
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 19.6651523 4.6317227 4.246 0.000194 ***
Lat -0.3437001 0.1009517 -3.405 0.001900 **
Height -0.0049792 0.0011322 -4.398 0.000127 ***
Sea 1.6435808 0.4802716 3.422 0.001814 **
NorthIsland 1.7682036 0.6430084 2.750 0.010003 *
Rain 0.0003583 0.0002170 1.651 0.109182
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.9271 on 30 degrees of freedom
Multiple R-squared: 0.9046, Adjusted R-squared: 0.8887
F-statistic: 56.9 on 5 and 30 DF, p-value: 2.11e-14
The Rain
variable is not significant (P=0.1) and \(S\) has gone up, which go against including
Rain
. However the adjusted R-squared has gone up. So we may
decide that if our aim is to predict for new data, then we should
include this variable in the model.
Adding Longitude
lm6 = lm(MnJlyTemp ~ Lat + Height + Sea + NorthIsland + Rain + Long, data = climate)
lm6 |>
summary()
Call:
lm(formula = MnJlyTemp ~ Lat + Height + Sea + NorthIsland + Rain +
Long, data = climate)
Residuals:
Min 1Q Median 3Q Max
-1.3360 -0.4832 -0.1712 0.4008 3.2814
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 6.8938252 21.1286658 0.326 0.746557
Lat -0.3272942 0.1053819 -3.106 0.004216 **
Height -0.0050077 0.0011449 -4.374 0.000144 ***
Sea 1.6381187 0.4853578 3.375 0.002113 **
NorthIsland 1.5548897 0.7352242 2.115 0.043156 *
Rain 0.0004002 0.0002295 1.744 0.091750 .
Long 0.0701947 0.1132443 0.620 0.540196
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.9368 on 29 degrees of freedom
Multiple R-squared: 0.9059, Adjusted R-squared: 0.8864
F-statistic: 46.51 on 6 and 29 DF, p-value: 1.402e-13
At this point all the indications are bad: The P-value for
Long
is >0.5, the adjusted R-squared has gone down and S
has gone up.
In conclusion we should include the first four variables, and
optionally the fifth one, Rain
.
LS0tDQp0aXRsZTogIkxlY3R1cmUgMjc6IFZhcmlhYmxlIFNlbGVjdGlvbiINCnN1YnRpdGxlOiAxNjEuMjUxIFJlZ3Jlc3Npb24gTW9kZWxsaW5nDQphdXRob3I6ICJQcmVzZW50ZWQgYnkgSm9uYXRoYW4gTWFyc2hhbGwgPEouQy5tYXJzaGFsbEBtYXNzZXkuYWMubno+IiAgDQpkYXRlOiAiV2VlayAxMCBvZiBTZW1lc3RlciAyLCBgciBsdWJyaWRhdGU6OnllYXIobHVicmlkYXRlOjpub3coKSlgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogIGlvc2xpZGVzX3ByZXNlbnRhdGlvbjoNCiAgICB3aWRlc2NyZWVuOiB0cnVlDQogICAgc21hbGxlcjogdHJ1ZQ0KICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0DQogIHNsaWR5X3ByZXNlbnRhdGlvbjogDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KLS0tDQoNCg0KDQoNCjwhLS0tIERhdGEgaXMgb24NCmh0dHBzOi8vci1yZXNvdXJjZXMubWFzc2V5LmFjLm56L2RhdGEvMTYxMjUxLw0KLS0tPg0KDQpgYGB7ciBzZXR1cCwgcHVybD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkoa25pdHIpDQpvcHRzX2NodW5rJHNldChkZXY9YygicG5nIiwgInBkZiIpKQ0Kb3B0c19jaHVuayRzZXQoZmlnLmhlaWdodD02LCBmaWcud2lkdGg9NywgZmlnLnBhdGg9IkZpZ3VyZXMvIiwgZmlnLmFsdD0idW5sYWJlbGxlZCIpDQpvcHRzX2NodW5rJHNldChjb21tZW50PSIiLCBmaWcuYWxpZ249ImNlbnRlciIsIHRpZHk9VFJVRSkNCm9wdGlvbnMoa25pdHIua2FibGUuTkEgPSAnJykNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShicm9vbSkNCmBgYA0KDQoNCjwhLS0tIERvIG5vdCBlZGl0IGFueXRoaW5nIGFib3ZlIHRoaXMgbGluZS4gLS0tPg0KCiMjIFZhcmlhYmxlIHNlbGVjdGlvbgoKSW4gcHJldmlvdXMgbGVjdHVyZXMgd2UgaGF2ZSBsb29rZWQgYXQgdGVzdHMgZm9yIHNpbmdsZSBleHBsYW5hdG9yeSB2YXJpYWJsZXMgYW5kIGdyb3VwcyBvZiBleHBsYW5hdG9yeSB2YXJpYWJsZXMuCgpPZnRlbiwgaG93ZXZlciwgYSBzdGF0aXN0aWNpYW4gd2lsbCBzaW1wbHkgd2FudCB0byBrbm93IHdoYXQgaXMgdGhlICAgICBiZXN0IGdyb3VwIG9mIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyBmb3IgZGVzY3JpYmluZyB0aGUgcmVzcG9uc2UuCgpJbiB0aGlzIGxlY3R1cmUgd2Ugd2lsbCBjb25zaWRlciB2YXJpb3VzIGNyaXRlcmlhIGZvciBtb2RlbCBzZWxlY3Rpb24sIGFuZCBjb3ZlciBhbiBleGFtcGxlIG9uIGZpbmRpbmcgYSBzdWl0YWJsZSB2YXJpYWJsZSBzZXQgImJ5IGhhbmQiLgoKIyMgT3ZlcmZpdHRpbmcgYW5kIFVuZGVyZml0dGluZwoKV2UgYmVnaW4gYnkgY29uc2lkZXJpbmcgdGhlIGZvbGxvd2luZyBxdWVzdGlvbjogIFNob3VsZCBhbGwgdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyBiZSB1c2VkLCBvciBzaG91bGQgc29tZSBiZSBvbWl0dGVkPwoKV2h5IG1pZ2h0IHdlIHdhbnQgdG8gb21pdCB2YXJpYWJsZXM/IAoKLQlQZXJoYXBzIHdlIHdhbnQgdG8gZGV0ZWN0IHZhcmlhYmxlcyB3aGljaCBhZmZlY3QgdGhlIHJlc3BvbnNlICBidXQgYXZvaWQgY29sbGVjdGluZyB1bm5lY2Vzc2FyeSBtZWFzdXJlbWVudHMgaW4gdGhlIGZ1dHVyZS4gVGhpcyBpcyBjYWxsZWQgdmFyaWFibGUgc2NyZWVuaW5nCgotCVBlcmhhcHMgd2Ugd2FudCB0byBwcmVkaWN0IGZyb20gdGhlIG1vZGVsLiBPdXIgcHJlZGljdGlvbnMgd2lsbCBiZSBsZXNzIGFjY3VyYXRlIGlmIHRoZSBtb2RlbCBpcyBpbmNvcnJlY3RseSBzcGVjaWZpZWQuCgkKV2UgZmlyc3QgY29uc2lkZXIgdGhlIHJpc2tzIGFuZCBiZW5lZml0cyBvZiBpbmNsdWRpbmcgYSBwcmVkaWN0b3IgdGhhdCBtaWdodCBoYXZlIGNvZWZmaWNpZW50ICRcYmV0YT0wJC4KCiMjICBEcm9wcGluZyBvciBJbmNsdWRpbmcgVmFyaWFibGVzIHRoYXQgaGF2ZSAkXGJldGE9MCQKClN1cHBvc2Ugd2UgaGF2ZSBhIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aCBzZXZlcmFsIHZhcmlhYmxlcy4gIFdlIG1pZ2h0IHN1c3BlY3Qgb25lIGhhcyByZWdyZXNzaW9uIGNvZWZmaWNpZW50ICRcYmV0YT0wJC4gIFNob3VsZCB3ZSBkcm9wIGl0PwoKU29tZSBtYXRoZW1hdGljYWwgdGhlb3J5IHNob3dzIHRoYXQgaWYgd2Ugb21pdCBhIHZhcmlhYmxlIHdoaWNoICh0cnVseSkgaGFzIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgICRcYmV0YT0wJCAgdGhlbgoKLSBWYXJpYW5jZXMgb2YgdGhlIHJlbWFpbmluZyByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBhcmUgcmVkdWNlZAoKLSBWYXJpYW5jZXMgb2YgcHJlZGljdGlvbnMgZnJvbSB0aGUgbW9kZWwgIGF0IGFueSBwb2ludCAkeF8wJCAgYXJlIHJlZHVjZWQuIAoKQm90aCB0aGVzZSB0aGluZ3MgYXJlIGdvb2QuCgpDb252ZXJzZWx5IGlmIHdlIGluY2x1ZGUgYSB2YXJpYWJsZSB3aG9zZSBjb2VmZmljaWVudCAkXGJldGE9MCQgdGhpcyAgaW5jcmVhc2VzIHRoZXNlIHZhcmlhbmNlcyB1bm5lY2Vzc2FyaWx5LiBUaGlzIGlzIGNhbGxlZCAqKm92ZXJmaXR0aW5nKiouIFdlIHdhbnQgdG8gYXZvaWQgdGhpcy4KCiMjIERyb3BwaW5nIG9yIEluY2x1ZGluZyBWYXJpYWJsZXMgdGhhdCBoYXZlICRcYmV0YSBcbmUgMCQKCk9uIHRoZSBvdGhlciBoYW5kIHN1cHBvc2Ugd2UgbWFrZSBhIG1pc3Rha2UgYW5kIG9taXQgYSB2YXJpYWJsZSB0aGF0IGhhcyByZWdyZXNzaW9uIGNvZWZmaWNpZW50ICRcbmUgMCQuICAgCgpUaGVuIHdlIGhhdmUgdGhlIG9wcG9zaXRlIHByb2JsZW0sIGNhbGxlZCAqKnVuZGVyZml0dGluZyoqLiAgTWF0aGVtYXRpY2FsIHRoZW9yeSBzaG93cyB0aGUgY29uc2VxdWVuY2Ugb2YgdW5kZXJmaXR0aW5nIGlzIHRoYXQgCgotCWlmIHRoZSB3cm9uZ2x5LW9taXR0ZWQgdmFyaWFibGVzIGFyZSBjb3JyZWxhdGVkIHdpdGggdGhlIHJlbWFpbmluZyB2YXJpYWJsZXMgdGhlbiB0aGUgZXhwZWN0ZWQgdmFsdWVzIG9mIHRoZSByZW1haW5pbmcgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgJFxiZXRhXzAsXGJldGFfMSwuLi5cYmV0YV97cC0xfSQgYXJlICoqYmlhc2VkKiouIChpLmUuIG1heSBiZSBhIHRvbyBiaWcgb3IgdG9vIHNtYWxsKS4KCi0gV2hldGhlciBvciBub3QgdGhlIG9taXR0ZWQgdmFyaWFibGVzIGFyZSBjb3JyZWxhdGVkIHdpdGggdGhlIGV4aXN0aW5nIG9uZXMsIHRoZSBwcmVkaWN0aW9ucyAkXGhhdCB5X2kkIGFyZSBiaWFzZWQuCgpCb3RoIHRoZXNlIHRoaW5ncyBhcmUgYmFkIQoKKipJbiBwcmFjdGljZSB3ZSBkbyBub3Qga25vdyBpZiBwYXJhbWV0ZXJzIGFyZSB6ZXJvLioqIFdlIGNvdWxkIHVzZSBhIGh5cG90aGVzaXMgdGVzdCB0byBoZWxwIHVzIGRlY2lkZSwgYnV0IHRoaXMgaXMgbm90IGEgZGVmaW5pdGl2ZSBhbnN3ZXIuCgojIyBBIE51bWVyaWNhbCBJbGx1c3RyYXRpb24gb2YgVW5kZXIgYW5kIE92ZXJmaXR0aW5nCgpJbiB0aGlzIGFydGlmaWNpYWwgZXhhbXBsZSwgd2UgY29uc2lkZXIgYW4gaW5kdXN0cmlhbCBwcm9jZXNzIHdob3NlIFlpZWxkIGlzIHRob3VnaHQgIHRvIGRlcGVuZCBvbiB0aGUgcHJvY2VzcyB0ZW1wZXJhdHVyZSBhdCBzdGFnZSBBIG9mIHRoZSBwcm9jZXNzLCBhbmQgcG9zc2libHkgYWxzbyBvbiB0aGUgdGVtcGVyYXR1cmUgYXQgc3RhZ2UgQi4gICAKCmkuZS4gIFdlIGNvbnNpZGVyIHRoZSBtb2RlbCAKJCQgWT1cYmV0YV8wICsgXGJldGFfMSBBKyBcYmV0YV8yIEIgK1x2YXJlcHNpbG9uLiQkCgkKV2Ugd2lsbCBhc3N1bWUgdGhlIHZhbHVlcyBvZiAkQSQgYW5kICRCJCBhbmQgJFx2YXJlcHNpbG9uJCBhcmUgZ2l2ZW4gaW4gdGhlIGRhdGEgYmVsb3cuICBUaGUgZXJyb3JzIGFyZSByYW5kb20gJFx2YXJlcHNpbG9uIFxzaW0gXG1ib3h7Tm9ybWFsfSgwLCAxKSQuCgpgciB4ZnVuOjplbWJlZF9maWxlKCIuLi8uLi9kYXRhL3NpbXVsYXRpb24xLmNzdiIpYCAKCmBgYHtyIHJlYWQgU2ltdWxhdGlvbjEsIGV2YWw9LTEsIGVjaG89LTIsIG1lc3NhZ2U9RkFMU0V9CnNpbSA9IHJlYWQuY3N2KCJzaW11bGF0aW9uMS5jc3YiLGhlYWRlcj1UUlVFKQpzaW0gPSByZWFkLmNzdigiLi4vLi4vZGF0YS9zaW11bGF0aW9uMS5jc3YiLGhlYWRlcj1UUlVFKQpgYGAKCgpXaGF0IHdlIHdhbnQgdG8ga25vdyBpcywgd2hhdCBhcmUgdGhlIGltcGxpY2F0aW9ucyBvZiB1c2luZyBhbiBpbmNvcnJlY3QgbW9kZWwsIAlpLmUuIGluY29ycmVjdCBhc3N1bXB0aW9ucyBhYm91dCB0aGUgJFxiZXRhJHMuIFRvIHVuZGVyc3RhbmQgdGhlIGltcGxpY2F0aW9ucywgd2Ugc2ltdWxhdGUgdHdvIHNjZW5hcmlvcy4gCgojIyBTY2VuYXJpbyAxOiAkXGJldGFfMD01MCQsICRcYmV0YV8xPTEwJCBhbmQgJFxiZXRhXzI9MCQuCgpGb3IgdGhpcyBpbGx1c3RyYXRpb24sIHRoZSBjb3JyZWN0IG1vZGVsIGRvZXNuJ3QgZGVwZW5kIG9uICRCJCBhdCBhbGw6CiQkWSAgPSAgNTAgKyAxMCBBICsgXHZhcmVwc2lsb24kJAoKVGhlIGZvbGxvd2luZyBzaG93cyB0aGUgcmVzdWx0IG9mIG1vZGVsbGluZyAkWSQgYnkgKDEpICRBJCBhbG9uZSBhbmQgICgyKSBieSAkQSQgYW5kICRCJC4KCmBgYHtyIG92ZXJmaXR0aW5nfQpkYXQgPC0gc2ltIHw+IG11dGF0ZShZID0gNTAgKyAxMCpBICsgZXBzaWxvbikKbG0xID0gbG0oWSB+IEEsIGRhdGE9ZGF0KQpsbTIgPSBsbShZIH4gQSArIEIsIGRhdGE9ZGF0KQpsc3QobG0xLCBsbTIpIHw+IG1hcF9kZnIodGlkeSwgLmlkPSdtb2RlbCcpCmBgYAoKQWRkaW5nIHRoZSB1bm5lY2Vzc2FyeSB2YXJpYWJsZSBkb2Vzbid0IGludHJvZHVjZSBiaWFzICgkXGhhdFxiZXRhXzEgXGFwcHJveCAxMCQpLCBidXQgdGhlIHN0YW5kYXJkIGVycm9yIG9mICRcaGF0XGJldGFfMSQgaXMgaGlnaGVyIGluIHRoZSBtaXNzcGVjaWZpZWQgbW9kZWwuCgojIyBTY2VuYXJpbyAyOiAkXGJldGFfMD01MCQsICRcYmV0YV8xPTUkIGFuZCAkXGJldGFfMj01JC4KCldlIGNvbnNpZGVyIHRoZSBtb2RlbCB3aGVyZSAkWSQgZGVwZW5kcyBvbiBib3RoICRBJCBhbmQgJEIkLAokJAlZICA9ICA1MCArIDUgQSArIDUgQiArIFx2YXJlcHNpbG9uICQkCgpgYGB7ciB1bmRlcmZpdHRpbmd9CmRhdCA8LSBzaW0gfD4gbXV0YXRlKFkgPSA1MCArIDUqQSArIDUqQiArIGVwc2lsb24pCmxtMSA9IGxtKFkgfiBBLCBkYXRhPWRhdCkKbG0yID0gbG0oWSB+IEEgKyBCLCBkYXRhPWRhdCkKbHN0KGxtMSwgbG0yKSB8PiBtYXBfZGZyKHRpZHksIC5pZD0nbW9kZWwnKQpgYGAKCkluIHRoZSBmaXJzdCBtb2RlbCwgJEIkIGlzIHdyb25nbHkgaWdub3JlZCBhbmQgJEEkIGlzIGJvdGggYmlhc2VkIGFuZCBoYXMgYSBsYXJnZXIgc3RhbmRhcmQgZXJyb3IuIEluIHRoZSBzZWNvbmQgbW9kZWwsIHRoZSB2YWx1ZXMgb2YgJFxoYXRcYmV0YV8xJCBhbmQgJFxoYXRcYmV0YV8yJCBhcmUgY2xvc2UgdG8gdGhlIHRydWUgb25lcyBhbmQgdGhlIHN0YW5kYXJkIGVycm9ycyBhcmUgdGhlIHNhbWUgYXMgaW4gdGhlIHNlY29uZCBhbmFseXNpcyBvZiBTY2VuYXJpbyAxLgoKIyMKCkluIHN1bW1hcnksIGlmIHdlIG9taXQgYSBuZWVkZWQgdmFyaWFibGUgdGhlbiB3ZSBtYXkgaW50cm9kdWNlIGJpYXMgaW50byBvdXIgZXN0aW1hdGlvbi4KCQpUaGUgYmlnIHByb2JsZW0gd2l0aCBiaWFzIGlzIHRoYXQgdGhlcmUgbWF5IGJlIG5vIHNpZ24gdGhhdCB3ZSBoYXZlIGEgbWlzdGFrZS4gCgpJbiBzb21lIGNhc2VzIHdlIGRvbid0IGV2ZW4gKmhhdmUqIHRoZSBuZWVkZWQgdmFyaWFibGUsIHNvIHdlIGNhbid0IHRyeSBpdCBpbiB0aGUgbW9kZWwuIAoKQXQgbGVhc3QgaW4gY2FzZSAxLCB3aGVuIHdlIHdyb25nbHkgZW50ZXIgYW4gdW5uZWNlc3NhcnkgdmFyaWFibGUsIGl0IGRvZXNuJ3QgYmlhcyBvdXIgZXN0aW1hdGVzLCBqdXN0IGluY3JlYXNlcyBvdXIgc3RhbmRhcmQgZXJyb3JzLiBTbyBhdCBsZWFzdCB3ZSBrbm93IGV4YWN0bHkgaG93IGJhZCB0aGluZ3MgYXJlLiBCdXQgd2l0aCBhbiBvbWl0dGVkIHZhcmlhYmxlIHdlIGRvbid0IGtub3cgaG93IGJhZCB0aGluZ3MgYXJlLgoJCkZvciB0aGlzIHJlYXNvbiwgd2UgbWF5IGRlY2lkZSB0byBlcnIgb24gdGhlIHNpZGUgb2YgY2F1dGlvbiBhbmQgYWRkIGluIHZhcmlhYmxlcyBldmVuIGlmIHdlIGFyZSB1bmNlcnRhaW4gYWJvdXQgdGhlaXIgcmVsZXZhbmNlLiAgVGhlbiBhdCBsZWFzdCB3ZSBjYW4gYXZvaWQgYmlhcy4gCgojIyBNb2RlbCBTZWxlY3Rpb246IEJpYXMgYW5kIFZhcmlhbmNlCgpNb2RlbCBzZWxlY3Rpb24sIHRoZW4sIGNvbWVzIGRvd24gdG8gYmFsYW5jaW5nIHRoZSBlZmZlY3RzIG9mIHZhcmlhbmNlIGFuZCBiaWFzLgoKLSBJZiB3ZSBvbWl0IGEgdmFyaWFibGUsIGl0IG1heSBkcmFtYXRpY2FsbHkgZGVjcmVhc2UgdGhlIHZhcmlhbmNlIG9mIHRoZSBvdGhlciBwYXJhbWV0ZXIgZXN0aW1hdGVzLiBUaGlzIGlzIGEgcGx1cy4gCgotIEJ1dCBpZiB0aGUgdmFyaWFibGUgaXMgaW1wb3J0YW50LCB0aGVuIHRoZSBvdGhlciBwYXJhbWV0ZXIgZXN0aW1hdGVzIG1heSBiZSBiaWFzZWQuICBUaGlzIGlzIGEgbWludXMuIAoKU28gd2hhdCBzaG91bGQgd2UgY2hvb3NlPyAgVGhlcmUgaXMgbm8gc2V0dGxlZCBydWxlIGZvciBldmVyeSBjaXJjdW1zdGFuY2UsIGJ1dCBpbiBnZW5lcmFsIHdlIHNob3VsZCBwcm9iYWJseSBiZSBtb3JlIGNvbmNlcm5lZCB0byBhdm9pZCBiaWFzIChlc3BlY2lhbGx5IHNpbmNlIHdlIGRvbid0IGtub3cgaG93IGJhZCB0aGlzIGlzISkgdGhhbiB0byBhdm9pZCBhIHNtYWxsIGluY3JlYXNlIGluIHZhcmlhbmNlIChhdCBsZWFzdCB3ZSBkbyBrbm93IGhvdyBiYWQgaXQgaXMgLSB3ZSBjYW4gc2VlIHRoZSBzdGFuZGFyZCBlcnJvcnMuKQoKQ29uc2VxdWVudGx5IHdlIG1heSBkZWNpZGUgdG8gZXJyIG9uIHRoZSBzaWRlIG9mIGluY2x1ZGluZyB2YXJpYWJsZXMgd2l0aCBQLXZhbHVlcyBhIGxpdHRsZSAgYmlnZ2VyIHRoYW4gMC4wNSwgKmVzcGVjaWFsbHkgaWYgdGhleSBzZWVtIHRvIGluZHVjZSBhIHNpemVhYmxlIGNoYW5nZSBpbiB0aGUgb3RoZXIgY29lZmZpY2llbnQgZXN0aW1hdGVzLiogICBUaGlzIGlzIGFub3RoZXIgdGhpbmcgdG8gd2F0Y2ggb3V0IGZvciBpbiBtb2RlbCBzZWxlY3Rpb24uIAoKIyMgUmV2aWV3IG9mIE1vZGVsIFNlbGVjdGlvbiBDcml0ZXJpYQoKVGhlcmUgYXJlIGEgbnVtYmVyIG9mIG1lYXN1cmVzIHdlIGNhbiB1c2UgZm9yIG1vZGVsIHNlbGVjdGlvbjoKCi0gQ29lZmZpY2llbnQgb2YgZGV0ZXJtaW5hdGlvbiAkUl4yJAoKLSBBZGp1c3RlZCBDb2VmZmljaWVudCBvZiBkZXRlcm1pbmF0aW9uICRSXjJfe0Fkan0kCgotIFJlc2lkdWFsIFN0YW5kYXJkIEVycm9yICRTJAoKLSAkUCQtdmFsdWVzIG9mIGluZGl2aWR1YWwgY29lZmZpY2llbnRzCgpFYWNoIG9mIHRoZXNlIGFyZSBzbGlnaHRseSBkaWZmZXJlbnQsIHNvIGl0IHBheXMgdG8gY29uc2lkZXIgbW9yZSB0aGFuIG9uZS4KCiMjIENvZWZmaWNpZW50IG9mIGRldGVybWluYXRpb24gJFJeMiQKClJlY2FsbCB0aGF0IHRoaXMgaXMgdGhlIHByb3BvcnRpb24gb2YgdG90YWwgdmFyaWF0aW9uICB3aGljaCBpcyBleHBsYWluZWQgYnkgdGhlIG1vZGVsLiAoQWx0ZXJuYXRpdmVseSwgMSBtaW51cyB0aGUgcmF0aW8gb2YgdW5leHBsYWluZWQgdmFyaWF0aW9uIHRvIHRvdGFsIHZhcmlhdGlvbik6CiQkIFJeMiA9IFxmcmFje1NTX3tSZWdufX17U1NUfSAgPSAxIC0gXGZyYWN7XHN1bSh5X2ktXGhhdCB5X2kpXjJ9e1xzdW0oeV9pLVxiYXIgeSleMn0uICQkCiRSXjIkIGlzIHVzZWZ1bCBmb3IgY29tcGFyaW5nIHR3byBtb2RlbHMgd2l0aCB0aGUgKipzYW1lKiogbnVtYmVyIG9mIHJlZ3Jlc3Npb24gcGFyYW1ldGVycy4gICBJZiBtb2RlbHMgaGF2ZSBkaWZmZXJlbnQgbnVtYmVycyBvZiByZWdyZXNzaW9uIHBhcmFtZXRlcnMgd2UgdXNlIHRoZSBhZGp1c3RlZCB2ZXJzaW9uLgoKIyMgQWRqdXN0ZWQgQ29lZmZpY2llbnQgb2YgZGV0ZXJtaW5hdGlvbiAkUl4yX3tBZGp9JAoKUmVjYWxsIHRoYXQgdGhpcyBpcyAxIG1pbnVzIHRoZSByYXRpbyBvZiBNZWFuIFNxdWFyZSBFcnJvciB0byBNZWFuIFNxdWFyZSBUb3RhbDogCiQkIFJeMl97QWRqfSA9IDEgLSBcZnJhY3tNU0V9e01TVH0gID0gMSAtIFxmcmFje1xzdW0oeV9pLVxoYXQgeV9pKV4yLyhuLXApfXtcc3VtKHlfaS1cYmFyIHkpXjIvKG4tMSl9LiQkCldoZW4gY29uc2lkZXJpbmcgYWRkaW5nIGEgdmFyaWFibGUgdG8gYSByZWdyZXNzaW9uLCB0aGUgJFJeMl97QWRqfSQgZ29lcyB1cCBpZiB0aGUgdmFyaWFibGUgaXMgIndvcnRoIiBhdCBsZWFzdCBvbmUgcm93IG9mIGRhdGEgKG9uZSBkZWdyZWUgb2YgZnJlZWRvbSksIHdoaWNoIGlzIGEgcHJldHR5IGxvdyBiYXIgdG8gY3Jvc3MuCgpCdXQgYXQgbGVhc3QgaWYgYSB2YXJpYWJsZSBjYW4ndCBtZWV0IHRoYXQgcnVsZSB0aGVuIHdlIGtub3cgdG8gZXhjbHVkZSBpdC4KCiMjIFJlc2lkdWFsIFN0YW5kYXJkIEVycm9yICRTJAoKVGhpcyBpcyB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSByZXNpZHVhbHMsIAokJCBTID0gXGZyYWN7MX17bi1wfVxzdW17KHlfaSAtIFxoYXQgeV9pKV4yfSAkJApvciBpbiBvdGhlciB3b3JkcyB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIGhvdyB0aGUgJHlfaSRzIHZhcnkgYXJvdW5kIHRoZSBtb2RlbCBwcmVkaWN0aW9ucy4gV2UgbWlnaHQgY2hvb3NlIHRvIGluY2x1ZGUgYSB2YXJpYWJsZSBpZiBpdCByZWR1Y2VzIHRoZSAkUyQuCgojIyAkUCQtdmFsdWUgZm9yIGluZGl2aWR1YWwgY29lZmZpY2llbnRzCgpXZSB1c3VhbGx5IHByZWZlciBtb2RlbHMgdGhhdCBvbmx5IGluY2x1ZGUgdmFyaWFibGVzIHdpdGggY29lZmZpY2llbnQgJFAkLXZhbHVlIGlzIHNtYWxsIChlLmcuICQ8IDAuMDUkKS4KCkhvd2V2ZXIgdGhlICIwLjA1IiBjcml0ZXJpb24gaXMgc29tZXdoYXQgYXJiaXRyYXJ5IGFuZCBvY2Nhc2lvbmFsbHkgd2UgYWxsb3cgaW4gdmFyaWFibGVzIHdpdGggc2xpZ2h0bHkgaGlnaGVyICRQJC12YWx1ZXMgdG8gcG90ZW50aWFsbHkgcmVkdWNlIGJpYXMuCgojIyBFeGFtcGxlOiAgTW9kZWwgU2VsZWN0aW9uIGZvciBDbGltYXRlIERhdGEKCldlIHdpc2ggdG8gbW9kZWwgdGhlIG1lYW4gdGVtcGVyYXR1cmUgaW4gSnVseSAod2ludGVyKSBpbiBBb3RlYXJvYSBOZXcgWmVhbGFuZCB1c2luZyBkYXRhIGZyb20gMzYgdG93bnMuIFdlIGhhdmUgaW5mb3JtYXRpb24gb24gbG9jYXRpb24gKGxhdGl0dWRlLCBsb25naXR1ZGUsIGhlaWdodCAoZWxldmF0aW9uKSwgd2hldGhlciB0aGV5J3JlIGJ5IHRoZSBzZWEgb3IgaW4gdGhlIE5vcnRoIGlzbGFuZCkgYXMgd2VsbCBhcyBjbGltYXRlIGluZm9ybWF0aW9uIChtZWFuIHRlbXBlcmF0dXJlIGluIEphbnVhcnksIHRvdGFsIHN1bnNoaW5lIGhvdXJzIGFuZCB0b3RhbCByYWluZmFsbCkuCgpgciB4ZnVuOjplbWJlZF9maWxlKCIuLi8uLi9kYXRhL2NsaW1hdGUuY3N2IilgIAoKYGBge3IgcmVhZCBjbGltYXRlLGV2YWw9LTEsIGVjaG89LTIsIG1lc3NhZ2U9RkFMU0V9CmNsaW1hdGUgPSByZWFkX2NzdigiY2xpbWF0ZS5jc3YiKQpjbGltYXRlID0gcmVhZF9jc3YoIi4uLy4uL2RhdGEvY2xpbWF0ZS5jc3YiKQpjbGltYXRlCmBgYAoKIyMgQ2xpbWF0ZSBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTR9CmNsaW1hdGUgfD4KICBwaXZvdF9sb25nZXIoLWMoUGxhY2UsIE1uSmx5VGVtcCkpIHw+CiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQobWFwcGluZz1hZXMoeD12YWx1ZSwgeT1NbkpseVRlbXApKSArCiAgZmFjZXRfd3JhcCh2YXJzKG5hbWUpLCBzY2FsZXMgPSAnZnJlZV94JywgbmNvbD00KQpgYGAKCiMjIEhpZ2hlciB3aW50ZXIgdGVtcGVyYXR1cmUgYXNzb2NpYXRpb25zCgotIExvd2VyIGVsZXZhdGlvbnMgKGhlaWdodCkKLSBMb3dlciBsYXRpdHVkZXMgKGZ1cnRoZXIgbm9ydGgpCi0gSGlnaGVyIGxvbmdpdHVkZXMgKGZ1cnRoZXIgZWFzdCAtIHJlY2FsbCBob3cgQW8vTlogaXMgb3JpZW50ZWQgb24gYSBtYXAgLSBwb3NzaWJseSBjb3JyZWxhdGlvbiB3aXRoIGxhdGl0dWRlLCB3aWxsIGNoZWNrIG5leHQhKQotIEhpZ2hlciBzdW1tZXIgdGVtcGVyYXR1cmVzCi0gTm9ydGggaXNsYW5kIChjb3VsZCBqdXN0IGJlIGxhdGl0dWRlPykKLSBJbmNyZWFzZWQgcmFpbmZhbGwgLSB1cCB0byBhIHBvaW50ISAoc3VidHJvcGljYWwgdnMgcmFpbnkgd2VzdGNvYXN0IC0gaW50ZXJhY3Rpb24gd2l0aCBJc2xhbmQvbGF0aXR1ZGU/PykKLSBDbG9zZW5lc3MgdG8gdGhlIHNlYQotIEluY3JlYXNlZCBzdW5zaGluZSBob3VycwoKTWFueSBvZiB0aGVzZSBhcmUgbGlrZWx5IGNvcnJlbGF0ZWQuCgojIyBDbGltYXRlOiBMYXRpdHVkZS9Mb25naXR1ZGUgYW5kIE5vcnRoIHZzIFNvdXRoCgpgYGB7cn0KZ2dwbG90KGNsaW1hdGUpICsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9TG9uZywgeT0tTGF0LCBzaGFwZT1hc19mYWN0b3IoTm9ydGhJc2xhbmQpLCBjb2w9TW5KbHlUZW1wKSkgKwogIGdlb21fdGV4dChtYXBwaW5nPWFlcyh4PUxvbmcsIHk9LUxhdCwgbGFiZWw9UGxhY2UpLCBzaXplPTIsIGhqdXN0PSdyaWdodCcsIG51ZGdlX3g9MC4xLCBudWRnZV95PTAuMikgKwogIGNvb3JkX2VxdWFsKCkKYGBgCgojIyBDbGltYXRlOiBMYXRpdHVkZS9Mb25naXR1ZGUgYW5kIE5vcnRoIHZzIFNvdXRoCgpPaywgc28gd2UgY2FuIHNlZSB0aGF0IHRoZSB0ZW1wZXJhdHVyZSBpcyBiZWluZyBkcml2ZW4gYnkgbGF0aXR1ZGUgKGZ1cnRoZXIgbm9ydGggaXMgd2FybWVyKSAtIHRoZSBsb25naXR1ZGUgZWZmZWN0IGlzIHBvc3NpYmx5IGp1c3QgZHVlIHRvIG9yaWVudGF0aW9uLiBJdCdzIGNsZWFyIHRoZXJlJ3MgYW4gZWxldmF0aW9uIChoZWlnaHQpIGVmZmVjdCBhcyB3ZWxsLCB0aG91Z2ggdGhhdCBpcyBwb3NzaWJseSBjb25mb3VuZGVkIHdpdGggd2hldGhlciB0aGUgdG93biBpcyBjbG9zZSB0byB0aGUgc2VhIChpdCBjYW4ndCByZWFsbHkgYmUgYm90aCEpCgpTbyB3ZSdkIGV4cGVjdCBzb21lIGNvbGxpbmVhcml0eSBoZXJlIGFuZCBmaWd1cmluZyBvdXQgd2hpY2ggYXJlIHRoZSBiZXN0IHZhcmlhYmxlcyB0byBiZSB1c2VkIG1pZ2h0IHRha2UgYSBiaXQgb2YgcGxheWluZyEKCldlJ2xsIHN0YXJ0IHdpdGggTGF0aXR1ZGUsIHRoZW4gYWRkIGluIEhlaWdodCBhbmQgU2VhOgoKYGBge3J9CmxtMSA9IGxtKCBNbkpseVRlbXAgfiBMYXQsIGRhdGE9Y2xpbWF0ZSkKbG0yID0gbG0oIE1uSmx5VGVtcCB+IExhdCArIEhlaWdodCwgZGF0YT1jbGltYXRlKQpsbTMgPSBsbSggTW5KbHlUZW1wIH4gTGF0ICsgSGVpZ2h0KyBTZWEsIGRhdGE9Y2xpbWF0ZSkKYGBgCgojIyBDbGltYXRlOiBtb2RlbCBvdXRwdXQKCmBgYHtyfQpsbTEgfD4gc3VtbWFyeSgpCmBgYAoKIyMKCmBgYHtyfQpsbTIgfD4gc3VtbWFyeSgpCmBgYAoKIyMKCmBgYHtyfQpsbTMgfD4gc3VtbWFyeSgpCmBgYAoKIyMgQ2xpbWF0ZTogRmlyc3QgdGhyZWUgcHJlZGljdG9ycy4KCkZvciB0aGVzZSBmaXJzdCB0aHJlZSBwcmVkaWN0b3JzOgoKIC0gVGhleSAgaGF2ZSBzaWduaWZpY2FudCBwLXZhbHVlcwogLSAkUyQgaGFzIGdvbmUgZG93biBlYWNoIHRpbWUKIC0gVGhlIGFkanVzdGVkICRSXjIkIGFuZCBoYXMgZ29uZSB1cCBlYWNoIHRpbWUuCiAKVGhlcmUgaXMgbm8gcmVhc29uIHRvIGRvdWJ0IHRoYXQgYExhdGAsIGBIZWlnaHRgIGFuZCBgU2VhYCBzaG91bGQgYWxsIGJlIGluY2x1ZGVkLiAgIAoKQWxzbyBub3RlIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50IGZvciBgSGVpZ2h0YCBhbHRlcmVkIGJ5IGFib3V0IDIgc3RhbmRhcmQgZXJyb3JzLCB3aGljaCBzdWdnZXN0cyB0aGF0IGBTZWFgIGlzIGNvcnJlY3RpbmcgZm9yIGJpYXMsIGFsbCB0aGUgbW9yZSByZWFzb24gZm9yIGluY2x1ZGluZyBpdC4KClRoaXMgbWFrZXMgc2Vuc2UgLSBzZWEgbGV2ZWwgdG93bnMgd2lsbCBtb3N0bHkgaGF2ZSBhIHZlcnkgbG93IGhlaWdodCwgYnV0IHRoZXJlJ2xsIGFsc28gYmUgYW4gYWRkaXRpb25hbCB3YXJtaW5nIGVmZmVjdCBvZiBiZWluZyBjbG9zZSB0byB0aGUgc2VhLgoKIyMgQ2xpbWF0ZTogQWRkaW5nIGlzbGFuZAoKYGBge3IgZm91cnRoIHZhcmlhYmxlfQpsbTQgPSBsbSggTW5KbHlUZW1wIH4gTGF0KyBIZWlnaHQrIFNlYSArIE5vcnRoSXNsYW5kLCBkYXRhPWNsaW1hdGUpCmxtNCB8PiBzdW1tYXJ5KCkKYGBgCgojIwoKU2FtZSBjb25jbHVzaW9ucyBhcyBiZWZvcmUsIHdlIHNob3VsZCBpbmNsdWRlIGBOb3J0aElzbGFuZGAKCkluY2x1ZGluZyBpdCBoYXMgaW5kdWNlZCBiaWcgY2hhbmdlcyBpbiB0aGUgY29lZmZpY2llbnRzIChlc3BlY2lhbGx5IGBMYXRgKSB3aGljaCBhZ2FpbiBpcyBldmlkZW5jZSBpdCBpcyBpbXBvcnRhbnQuCgoqKldoeSBkbyB5b3UgdGhpbmsgYWRkaW5nIE5vcnRoIHZzIFNvdXRoIGlzbGFuZCBpcyB1c2VmdWwgb3ZlciBhbmQgYWJvdmUgTGF0aXR1ZGU/IFdvdWxkbid0IExhdGl0dWRlIGRvIGV2ZXJ5dGhpbmcgaGVyZT8/KioKCiMjIEFkZGluZyBSYWluCgpgYGB7ciBmaWZ0aCB2YXJpYWJsZX0KbG01ID0gbG0oIE1uSmx5VGVtcCB+IExhdCsgSGVpZ2h0KyBTZWEgKyBOb3J0aElzbGFuZCsgUmFpbiwgZGF0YT1jbGltYXRlKQpsbTUgfD4gc3VtbWFyeSgpCmBgYAoKIyMKClRoZSBgUmFpbmAgdmFyaWFibGUgaXMgbm90IHNpZ25pZmljYW50IChQPTAuMSkgYW5kICRTJCBoYXMgZ29uZSB1cCwgd2hpY2ggZ28gYWdhaW5zdCBpbmNsdWRpbmcgYFJhaW5gLiAgSG93ZXZlciB0aGUgYWRqdXN0ZWQgUi1zcXVhcmVkIGhhcyBnb25lIHVwLiAgU28gd2UgbWF5IGRlY2lkZSB0aGF0IGlmIG91ciBhaW0gaXMgdG8gcHJlZGljdCBmb3IgbmV3IGRhdGEsIHRoZW4gd2Ugc2hvdWxkIGluY2x1ZGUgdGhpcyB2YXJpYWJsZSBpbiB0aGUgbW9kZWwuCgojIyBBZGRpbmcgTG9uZ2l0dWRlCgpgYGB7ciBzaXh0aCB2YXJpYWJsZX0KbG02ID0gbG0oIE1uSmx5VGVtcCB+IExhdCsgSGVpZ2h0KyBTZWEgKyBOb3J0aElzbGFuZCsgUmFpbiArIExvbmcsIGRhdGE9Y2xpbWF0ZSkKbG02IHw+IHN1bW1hcnkoKQpgYGAKCiMjCgpBdCB0aGlzIHBvaW50IGFsbCB0aGUgaW5kaWNhdGlvbnMgYXJlIGJhZDogIFRoZSBQLXZhbHVlIGZvciBgTG9uZ2AgaXMgPjAuNSwgdGhlIGFkanVzdGVkIFItc3F1YXJlZCBoYXMgZ29uZSBkb3duIGFuZCBTIGhhcyBnb25lIHVwLgoKSW4gY29uY2x1c2lvbiB3ZSBzaG91bGQgaW5jbHVkZSB0aGUgZmlyc3QgZm91ciB2YXJpYWJsZXMsIGFuZCBvcHRpb25hbGx5IHRoZSBmaWZ0aCBvbmUsIGBSYWluYC4=