From model comparison to model selection
In Lecture 26, the scientific question gave us a reduced model and a
full model to compare. We knew in advance which additional terms we
wanted to test.
Real modelling problems are usually messier. We may have several
plausible predictors and a lot of reasonable-looking models, with no
single reduced-versus-full comparison that settles the question.
At that point we have moved from model comparison to
model selection.
The central question is:
How do we balance model fit against model complexity when several
models are plausible?
What are we trying to do?
Before choosing a model, identify the modelling goal:
- Explanation or inference: estimate and interpret
effects after adjustment for other variables.
- Prediction: predict new observations
accurately.
- Description: find a compact model that describes
the observed relationships.
These are different jobs, and they can favour different models. A
variable might matter scientifically without helping prediction much,
while a strong predictor does not automatically have a causal
interpretation.
Overfitting and underfitting
So why not just put everything into the model?
Warning in geom_segment(aes(x = 1.4, xend = 8.6, y = 0, yend = 0), arrow = grid::arrow(ends = "both", : All aesthetics have length 1, but the data has 3 rows.
ℹ Please consider using `annotate()` or provide this layer with data containing
a single row.
This is the bias-variance trade-off. A model that is
too simple can omit structure. A model that is too complicated can fit
accidental features of the current data and produce less stable
estimates.
What if \(\beta=0\)?
Suppose one candidate variable really has coefficient \(\beta=0\). If we leave it out, the
remaining coefficients and predictions are usually estimated more
precisely. If we include it anyway, we spend a degree of freedom
estimating something that is not contributing signal.
That is one form of overfitting: the model is more
complicated than it needs to be.
What if \(\beta \ne 0\)?
The opposite mistake is to leave out a variable that really belongs
in the model. That is underfitting.
If the omitted predictor overlaps with predictors that remain in the
model, their coefficient estimates can be biased because they are now
being asked to absorb some of the missing structure. Predictions can
also be biased because the fitted mean structure is wrong.
The awkward part is that we do not know in advance which coefficients
are truly zero. A hypothesis test gives us evidence, but it does not
solve the model-selection problem by itself.
A small simulation
To make this concrete, consider an artificial industrial process
where Yield depends on the process temperature at stage A, and may also
depend on the temperature at stage B.
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)
We will use two simple scenarios: one where the larger model is
unnecessary, and one where the smaller model leaves something important
out.
Scenario 1: \(\beta_0=50\), \(\beta_1=10\) and \(\beta_2=0\).
In the first scenario, \(B\)
genuinely does nothing: \[Y = 50 + 10 A +
\varepsilon\]
Compare the correct model using \(A\) alone with an unnecessarily larger
model that also includes \(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.
Under and Overfitting con’t
Scenario 2: \(\beta_0=50\), \(\beta_1=5\) and \(\beta_2=5\).
Now both predictors genuinely matter: \[ 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
When we wrongly omit \(B\), the
estimated coefficient for \(A\) is
pulled away from its true value. Once both predictors are included, the
fitted coefficients are close to the values used to generate the
data.
What did the simulation show?
Omitting a needed variable can bias the remaining estimates and
predictions, especially when the omitted predictor overlaps with
variables retained in the model. Including an unnecessary variable can
leave the estimates centred correctly while increasing their standard
errors.
In practice we do not know which situation we are in. Model selection
therefore requires a modelling goal and criteria that reflect the cost
of complexity. A hypothesis test can contribute evidence, but it is not
a complete decision rule.
Partial F tests and AIC
Partial F tests are useful when we have a particular nested
change in mind. AIC provides another criterion:
\[AIC = n\log(RSS/n) + 2p +
\text{constant},\]
where \(p\) is the number of fitted
regression parameters. Smaller AIC is preferred when comparing candidate
models fitted to the same response and observations.
Unlike the partial F test, AIC can compare models that are
not nested. It is still just a criterion, not a machine for finding the
one true model, but it gives us a practical way to trade fit against
complexity.
Individual coefficient P-values answer a more specific
question about one coefficient under one parameterisation. They should
not be treated as a complete model-selection rule.
Cross-validation: a prediction question
Everything above is based on the data used to fit the model. If
prediction is the goal, the more useful question is:
How well does the model predict observations it did not use to fit
itself?
The basic cross-validation idea is:
For now, focus on the idea rather than the implementation. We will
use cross-validation in Lecture 28 when we start fitting larger
models.
Select terms, not arbitrary coefficients
One more thing before we start selecting models: a model term is not
always the same thing as one coefficient. A four-level factor creates
three treatment coefficients, but scientifically it is still one
predictor.
Similarly, in
y ~ A * x
A:x is one interaction term, even though it can
correspond to several interaction coefficients. If we retain an
interaction, we normally retain the corresponding main effects as well.
This is the hierarchy principle.
Example: Model Selection for Climate Data
We will build a model for mean July temperature across 36 towns in
Aotearoa New Zealand. We have location variables such as latitude,
longitude, elevation, coastal location and island, together with summer
temperature, sunshine and 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
Several of these predictors are clearly telling us partly the same
story.
Climate: Latitude/Longitude and North vs South

The map makes the geography easier to see: the North Island towns are
generally warmer, while longitude mostly reflects the orientation of the
islands. Elevation also appears to matter, although it may be confounded
with whether a town is close to the sea (it cannot 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!
Climate: Latitude/Longitude and North vs South
Let’s 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)
Building the Climate Model
Model 1: Lat only
lm1 |>
summary()
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
Model 2: Lat + Height
lm2 |>
summary()
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
Model 3: Lat + Height + Sea
lm3 |>
summary()
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
First three predictors: assessment
The first three additions agree across the measures we have
considered: the added-term P-values are small, \(S\) decreases, and adjusted \(R^2\) increases. This is consistent with
retaining Lat, Height and Sea for
this candidate model path.
The Height coefficient also changes noticeably when
Sea is added. This tells us that the interpretation of
Height depends on whether Sea is adjusted for.
The predictors overlap in the information they carry, so the coefficient
in a multiple regression is a conditional association, not an isolated
effect of height.
Model 4: Adding NorthIsland
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
The added-term P-value is small, and the fit measures
improve. The large change in the Lat coefficient tells us
that the interpretation of latitude depends on whether
NorthIsland is included. It does not, by itself, prove that
the new variable is correcting bias.
Why do you think adding North vs South island is useful over
and above Latitude? Wouldn’t Latitude do everything here??
Model 5: 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 evidence for Rain is mixed. Its coefficient
P-value is approximately 0.10, while \(S\) decreases and adjusted \(R^2\) increases. These are all in-sample
measures, so they do not establish that Rain will improve
prediction for new towns. If prediction were the primary goal, this
would be a natural case for validation or cross-validation.
Model 6: 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
The evidence against adding Long is consistent: its
P-value is greater than 0.5, adjusted \(R^2\) decreases, and \(S\) increases. This candidate addition does
not improve the fitted model by these measures.
Climate model-building progression
We have followed one sensible path through the candidate models. That
does not prove we have found the only correct formula. The table just
puts the evidence from each step in one place.
Climate candidate models and in-sample comparison
measures
| lm 1 |
Lat |
0.570 |
1.822 |
149.312 |
< 0.001 |
| lm 2 |
Height |
0.837 |
1.121 |
115.229 |
< 0.001 |
| lm 3 |
Sea |
0.865 |
1.022 |
109.455 |
< 0.001 |
| lm 4 |
NorthIsland |
0.883 |
0.953 |
105.283 |
< 0.001 |
| lm 5 |
Rain |
0.889 |
0.927 |
104.153 |
0.10 |
| lm 6 |
Long |
0.886 |
0.937 |
105.679 |
> 0.5 |
The table is useful because it shows what each extra term buys us. It
still does not replace the modelling goal, subject-matter knowledge, or
proper validation if prediction is what we care about.
Final Model Diagnostic Plots
A final model check
Once we have a plausible mean structure, we still need to check
whether the fitted model shows obvious problems with the LINE
assumptions.
lm(MnJlyTemp ~ Lat + Height + Sea + NorthIsland + Rain, data = climate).

Interpretation of diagnostic plots
- Residuals vs. Fitted: the points show no strong systematic pattern,
so there is not strong evidence of a problem with the linear mean
structure.
- Normal Q-Q: the points are reasonably close to the reference line,
with one noticeable departure. This is not proof of normality, but it
gives no strong warning at this scale.
- Scale-Location: the spread does not show a strong trend across
fitted values, so there is not strong evidence of changing
variance.
- Residuals vs. Leverage: no observation stands out as obviously
influential from this plot alone.
These plots do not certify the model. They tell us whether there are
obvious departures that need more attention.
A more realistic running example
From this point in the course, we are going to keep coming back to
one more complex dataset rather than starting from scratch with a new
toy example each time. The data are hourly bike rentals in Seoul, South
Korea, together with weather and calendar variables that might help us
explain or predict demand.
We will build this model up as the course goes on. Here we start with
several plausible predictors. Later we will come back to the same data
for automated variable selection, penalised regression,
multicollinearity and the other problems that appear once a model starts
looking more like something we would fit in practice.
Download seoul_bike_hourly.csv
seoul_hourly = read_csv("../data/seoul_bike_hourly.csv")
seoul_hourly
# A tibble: 8,760 × 16
date datetime day_index hour rented_bike_count temperature
<date> <dttm> <dbl> <dbl> <dbl> <dbl>
1 2017-12-01 2017-12-01 00:00:00 1 0 254 -5.2
2 2017-12-01 2017-12-01 01:00:00 1 1 204 -5.5
3 2017-12-01 2017-12-01 02:00:00 1 2 173 -6
4 2017-12-01 2017-12-01 03:00:00 1 3 107 -6.2
5 2017-12-01 2017-12-01 04:00:00 1 4 78 -6
6 2017-12-01 2017-12-01 05:00:00 1 5 100 -6.4
7 2017-12-01 2017-12-01 06:00:00 1 6 181 -6.6
8 2017-12-01 2017-12-01 07:00:00 1 7 460 -7.4
9 2017-12-01 2017-12-01 08:00:00 1 8 930 -7.6
10 2017-12-01 2017-12-01 09:00:00 1 9 490 -6.5
# ℹ 8,750 more rows
# ℹ 10 more variables: humidity <dbl>, wind_speed <dbl>, visibility <dbl>,
# dew_point_temperature <dbl>, solar_radiation <dbl>, rainfall <dbl>,
# snowfall <dbl>, season <chr>, holiday <chr>, functioning_day <chr>
The response is rented_bike_count. Potential explanatory
variables include weather measurements such as temperature, humidity,
wind speed and rainfall, as well as calendar information such as hour,
season and whether the day is a holiday.
There is no obvious single model here. Several predictors are
plausible, some are clearly related to one another, and the number of
possible models gets large very quickly. That is exactly why this is
useful as a running example.
seoul_basic = lm(rented_bike_count ~ temperature + humidity + wind_speed, data = seoul_hourly)
seoul_richer = lm(rented_bike_count ~ temperature + humidity + wind_speed + visibility +
dew_point_temperature + solar_radiation + rainfall + snowfall + hour + season +
holiday + functioning_day, data = seoul_hourly)
tibble(model = c("basic weather model", "richer weather + calendar model"), adj_r_squared = c(summary(seoul_basic)$adj.r.squared,
summary(seoul_richer)$adj.r.squared), sigma = c(summary(seoul_basic)$sigma, summary(seoul_richer)$sigma),
aic = c(AIC(seoul_basic), AIC(seoul_richer)))
# A tibble: 2 × 4
model adj_r_squared sigma aic
<chr> <dbl> <dbl> <dbl>
1 basic weather model 0.376 510. 134080.
2 richer weather + calendar model 0.550 433. 131228.
The richer model looks better on all three in-sample criteria:
adjusted \(R^2\) is higher, while
residual standard error and AIC are lower. That tells us it fits these
data better after accounting, to some extent, for the extra complexity.
It does not yet tell us that it will predict new bike demand better.
With only a few candidate models we can still do this by hand. Once
the predictor list gets longer, that becomes painful. In the next
lecture we look at two ways forward: automate the search through
candidate models, or keep a larger model and shrink the coefficients
instead of repeatedly adding and removing terms.
LS0tDQp0aXRsZTogIkxlY3R1cmUgMjc6IFZhcmlhYmxlIFNlbGVjdGlvbiINCnN1YnRpdGxlOiAxNjEuMjUxIFJlZ3Jlc3Npb24gTW9kZWxsaW5nDQphdXRob3I6ICJQcmVzZW50ZWQgYnkgTmljayBLbm93bHRvbiA8Ti5Lbm93bHRvbkBtYXNzZXkuYWMubno+IiAgDQpkYXRlOiAiV2VlayA5IG9mIFNlbWVzdGVyIDIsIGByIGx1YnJpZGF0ZTo6eWVhcihsdWJyaWRhdGU6Om5vdygpKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0X3N0eWxlOiBweWdtZW50cw0KICBodG1sX25vdGVib29rOg0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodF9zdHlsZTogcHlnbWVudHMNCiAgaW9zbGlkZXNfcHJlc2VudGF0aW9uOg0KICAgIHdpZGVzY3JlZW46IHRydWUNCiAgICBzbWFsbGVyOiB0cnVlDQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgc2xpZHlfcHJlc2VudGF0aW9uOiANCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodF9zdHlsZTogcHlnbWVudHMNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KDQoNCg0KPCEtLS0gRGF0YSBpcyBvbg0KaHR0cHM6Ly9yLXJlc291cmNlcy5tYXNzZXkuYWMubnovZGF0YS8xNjEyNTEvDQotLS0+DQoNCmBgYHtyIHNldHVwLCBwdXJsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShrbml0cikNCm9wdHNfY2h1bmskc2V0KGRldj1jKCJwbmciLCAicGRmIikpDQpvcHRzX2NodW5rJHNldChmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD03LCBmaWcucGF0aD0iRmlndXJlcy8iLCBmaWcuYWx0PSJ1bmxhYmVsbGVkIikNCm9wdHNfY2h1bmskc2V0KGNvbW1lbnQ9IiIsIGZpZy5hbGlnbj0iY2VudGVyIiwgdGlkeT1UUlVFKQ0Kb3B0aW9ucyhrbml0ci5rYWJsZS5OQSA9ICcnKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGJyb29tKQ0KYGBgDQoNCg0KPCEtLS0gRG8gbm90IGVkaXQgYW55dGhpbmcgYWJvdmUgdGhpcyBsaW5lLiAtLS0+DQoKIyMgRnJvbSBtb2RlbCBjb21wYXJpc29uIHRvIG1vZGVsIHNlbGVjdGlvbgoKSW4gTGVjdHVyZSAyNiwgdGhlIHNjaWVudGlmaWMgcXVlc3Rpb24gZ2F2ZSB1cyBhIHJlZHVjZWQgbW9kZWwgYW5kIGEgZnVsbCBtb2RlbCB0byBjb21wYXJlLiBXZSBrbmV3IGluIGFkdmFuY2Ugd2hpY2ggYWRkaXRpb25hbCB0ZXJtcyB3ZSB3YW50ZWQgdG8gdGVzdC4KClJlYWwgbW9kZWxsaW5nIHByb2JsZW1zIGFyZSB1c3VhbGx5IG1lc3NpZXIuIFdlIG1heSBoYXZlIHNldmVyYWwgcGxhdXNpYmxlIHByZWRpY3RvcnMgYW5kIGEgbG90IG9mIHJlYXNvbmFibGUtbG9va2luZyBtb2RlbHMsIHdpdGggbm8gc2luZ2xlIHJlZHVjZWQtdmVyc3VzLWZ1bGwgY29tcGFyaXNvbiB0aGF0IHNldHRsZXMgdGhlIHF1ZXN0aW9uLgoKQXQgdGhhdCBwb2ludCB3ZSBoYXZlIG1vdmVkIGZyb20gKiptb2RlbCBjb21wYXJpc29uKiogdG8gKiptb2RlbCBzZWxlY3Rpb24qKi4KClRoZSBjZW50cmFsIHF1ZXN0aW9uIGlzOgoKPiBIb3cgZG8gd2UgYmFsYW5jZSBtb2RlbCBmaXQgYWdhaW5zdCBtb2RlbCBjb21wbGV4aXR5IHdoZW4gc2V2ZXJhbCBtb2RlbHMgYXJlIHBsYXVzaWJsZT8KCiMjIyBXaGF0IGFyZSB3ZSB0cnlpbmcgdG8gZG8/CgpCZWZvcmUgY2hvb3NpbmcgYSBtb2RlbCwgaWRlbnRpZnkgdGhlIG1vZGVsbGluZyBnb2FsOgoKLSAqKkV4cGxhbmF0aW9uIG9yIGluZmVyZW5jZToqKiBlc3RpbWF0ZSBhbmQgaW50ZXJwcmV0IGVmZmVjdHMgYWZ0ZXIgYWRqdXN0bWVudCBmb3Igb3RoZXIgdmFyaWFibGVzLgotICoqUHJlZGljdGlvbjoqKiBwcmVkaWN0IG5ldyBvYnNlcnZhdGlvbnMgYWNjdXJhdGVseS4KLSAqKkRlc2NyaXB0aW9uOioqIGZpbmQgYSBjb21wYWN0IG1vZGVsIHRoYXQgZGVzY3JpYmVzIHRoZSBvYnNlcnZlZCByZWxhdGlvbnNoaXBzLgoKVGhlc2UgYXJlIGRpZmZlcmVudCBqb2JzLCBhbmQgdGhleSBjYW4gZmF2b3VyIGRpZmZlcmVudCBtb2RlbHMuIEEgdmFyaWFibGUgbWlnaHQgbWF0dGVyIHNjaWVudGlmaWNhbGx5IHdpdGhvdXQgaGVscGluZyBwcmVkaWN0aW9uIG11Y2gsIHdoaWxlIGEgc3Ryb25nIHByZWRpY3RvciBkb2VzIG5vdCBhdXRvbWF0aWNhbGx5IGhhdmUgYSBjYXVzYWwgaW50ZXJwcmV0YXRpb24uCgojIyBPdmVyZml0dGluZyBhbmQgdW5kZXJmaXR0aW5nCgpTbyB3aHkgbm90IGp1c3QgcHV0IGV2ZXJ5dGhpbmcgaW50byB0aGUgbW9kZWw/IAoKYGBge3IgYmlhcy12YXJpYW5jZS1jb250aW51dW0sIGVjaG89RkFMU0UsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTEuOCwgb3V0LndpZHRoPSI5MCUiLCBmaWcuY2FwPSJUaGUgYmlhcy12YXJpYW5jZSB0cmFkZS1vZmYgYXMgbW9kZWwgZmxleGliaWxpdHkgY2hhbmdlcy4ifQpjb250aW51dW0gPC0gdGliYmxlKAogIHggPSBjKDEsIDUsIDkpLAogIGxhYmVsID0gYygiVG9vIHNpbXBsZVxudW5kZXJmaXR0aW5nXG5tb3JlIHBvdGVudGlhbCBiaWFzIiwgIkEgdXNlZnVsIGJhbGFuY2UiLCAiVG9vIGZsZXhpYmxlXG5vdmVyZml0dGluZ1xubW9yZSB2YXJpYW5jZSIpCikKCmdncGxvdChjb250aW51dW0sIGFlcyh4ID0geCwgeSA9IDApKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMS40LCB4ZW5kID0gOC42LCB5ID0gMCwgeWVuZCA9IDApLAogICAgICAgICAgICAgICBhcnJvdyA9IGdyaWQ6OmFycm93KGVuZHMgPSAiYm90aCIsIGxlbmd0aCA9IGdyaWQ6OnVuaXQoMC4xMiwgImluY2hlcyIpKSwKICAgICAgICAgICAgICAgY29sb3VyID0gIiMzMjVkODgiLCBsaW5ld2lkdGggPSAxLjIpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBsYWJlbCksIHNpemUgPSA0LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGxhYmVsKSwgdmp1c3QgPSAtMC44LCBsaW5laGVpZ2h0ID0gMC45NSwgc2l6ZSA9IDMuNykgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiI2Y0N2MzYyIsICIjMzI1ZDg4IiwgIiM5M2M1NGIiKSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLjUsIDkuNSksIHlsaW0gPSBjKC0wLjIsIDEuOCksIGV4cGFuZCA9IEZBTFNFKSArCiAgdGhlbWVfdm9pZCgpCmBgYAoKVGhpcyBpcyB0aGUgKipiaWFzLXZhcmlhbmNlIHRyYWRlLW9mZioqLiBBIG1vZGVsIHRoYXQgaXMgdG9vIHNpbXBsZSBjYW4gb21pdCBzdHJ1Y3R1cmUuIEEgbW9kZWwgdGhhdCBpcyB0b28gY29tcGxpY2F0ZWQgY2FuIGZpdCBhY2NpZGVudGFsIGZlYXR1cmVzIG9mIHRoZSBjdXJyZW50IGRhdGEgYW5kIHByb2R1Y2UgbGVzcyBzdGFibGUgZXN0aW1hdGVzLgoKIyMjIFdoYXQgaWYgJFxiZXRhPTAkPwoKU3VwcG9zZSBvbmUgY2FuZGlkYXRlIHZhcmlhYmxlIHJlYWxseSBoYXMgY29lZmZpY2llbnQgJFxiZXRhPTAkLiBJZiB3ZSBsZWF2ZSBpdCBvdXQsIHRoZSByZW1haW5pbmcgY29lZmZpY2llbnRzIGFuZCBwcmVkaWN0aW9ucyBhcmUgdXN1YWxseSBlc3RpbWF0ZWQgbW9yZSBwcmVjaXNlbHkuIElmIHdlIGluY2x1ZGUgaXQgYW55d2F5LCB3ZSBzcGVuZCBhIGRlZ3JlZSBvZiBmcmVlZG9tIGVzdGltYXRpbmcgc29tZXRoaW5nIHRoYXQgaXMgbm90IGNvbnRyaWJ1dGluZyBzaWduYWwuCgpUaGF0IGlzIG9uZSBmb3JtIG9mICoqb3ZlcmZpdHRpbmcqKjogdGhlIG1vZGVsIGlzIG1vcmUgY29tcGxpY2F0ZWQgdGhhbiBpdCBuZWVkcyB0byBiZS4KCiMjIyBXaGF0IGlmICRcYmV0YSBcbmUgMCQ/CgpUaGUgb3Bwb3NpdGUgbWlzdGFrZSBpcyB0byBsZWF2ZSBvdXQgYSB2YXJpYWJsZSB0aGF0IHJlYWxseSBiZWxvbmdzIGluIHRoZSBtb2RlbC4gVGhhdCBpcyAqKnVuZGVyZml0dGluZyoqLgoKSWYgdGhlIG9taXR0ZWQgcHJlZGljdG9yIG92ZXJsYXBzIHdpdGggcHJlZGljdG9ycyB0aGF0IHJlbWFpbiBpbiB0aGUgbW9kZWwsIHRoZWlyIGNvZWZmaWNpZW50IGVzdGltYXRlcyBjYW4gYmUgYmlhc2VkIGJlY2F1c2UgdGhleSBhcmUgbm93IGJlaW5nIGFza2VkIHRvIGFic29yYiBzb21lIG9mIHRoZSBtaXNzaW5nIHN0cnVjdHVyZS4gUHJlZGljdGlvbnMgY2FuIGFsc28gYmUgYmlhc2VkIGJlY2F1c2UgdGhlIGZpdHRlZCBtZWFuIHN0cnVjdHVyZSBpcyB3cm9uZy4KClRoZSBhd2t3YXJkIHBhcnQgaXMgdGhhdCB3ZSBkbyBub3Qga25vdyBpbiBhZHZhbmNlIHdoaWNoIGNvZWZmaWNpZW50cyBhcmUgdHJ1bHkgemVyby4gQSBoeXBvdGhlc2lzIHRlc3QgZ2l2ZXMgdXMgZXZpZGVuY2UsIGJ1dCBpdCBkb2VzIG5vdCBzb2x2ZSB0aGUgbW9kZWwtc2VsZWN0aW9uIHByb2JsZW0gYnkgaXRzZWxmLgoKIyMgQSBzbWFsbCBzaW11bGF0aW9uCgpUbyBtYWtlIHRoaXMgY29uY3JldGUsIGNvbnNpZGVyIGFuIGFydGlmaWNpYWwgaW5kdXN0cmlhbCBwcm9jZXNzIHdoZXJlIFlpZWxkIGRlcGVuZHMgb24gdGhlIHByb2Nlc3MgdGVtcGVyYXR1cmUgYXQgc3RhZ2UgQSwgYW5kIG1heSBhbHNvIGRlcGVuZCBvbiB0aGUgdGVtcGVyYXR1cmUgYXQgc3RhZ2UgQi4KCldlIGNvbnNpZGVyIHRoZSBtb2RlbCAKJCQgWT1cYmV0YV8wICsgXGJldGFfMSBBKyBcYmV0YV8yIEIgK1x2YXJlcHNpbG9uLiQkCgkKV2Ugd2lsbCBhc3N1bWUgdGhlIHZhbHVlcyBvZiAkQSQgYW5kICRCJCBhbmQgJFx2YXJlcHNpbG9uJCBhcmUgZ2l2ZW4gaW4gdGhlIGRhdGEgYmVsb3cuICBUaGUgZXJyb3JzIGFyZSByYW5kb20gJFx2YXJlcHNpbG9uIFxzaW0gXG1ib3h7Tm9ybWFsfSgwLCAxKSQuCgpgciB4ZnVuOjplbWJlZF9maWxlKCIuLi9kYXRhL3NpbXVsYXRpb24xLmNzdiIpYCAKCmBgYHtyIHJlYWQgU2ltdWxhdGlvbjEsIGV2YWw9LTEsIGVjaG89LTIsIG1lc3NhZ2U9RkFMU0V9CnNpbSA9IHJlYWQuY3N2KCJzaW11bGF0aW9uMS5jc3YiLGhlYWRlcj1UUlVFKQpzaW0gPSByZWFkLmNzdigiLi4vZGF0YS9zaW11bGF0aW9uMS5jc3YiLGhlYWRlcj1UUlVFKQpgYGAKCgpXZSB3aWxsIHVzZSB0d28gc2ltcGxlIHNjZW5hcmlvczogb25lIHdoZXJlIHRoZSBsYXJnZXIgbW9kZWwgaXMgdW5uZWNlc3NhcnksIGFuZCBvbmUgd2hlcmUgdGhlIHNtYWxsZXIgbW9kZWwgbGVhdmVzIHNvbWV0aGluZyBpbXBvcnRhbnQgb3V0LgoJCiMjIyBTY2VuYXJpbyAxOiAkXGJldGFfMD01MCQsICRcYmV0YV8xPTEwJCBhbmQgJFxiZXRhXzI9MCQuCgpJbiB0aGUgZmlyc3Qgc2NlbmFyaW8sICRCJCBnZW51aW5lbHkgZG9lcyBub3RoaW5nOgokJFkgID0gIDUwICsgMTAgQSArIFx2YXJlcHNpbG9uJCQKCkNvbXBhcmUgdGhlIGNvcnJlY3QgbW9kZWwgdXNpbmcgJEEkIGFsb25lIHdpdGggYW4gdW5uZWNlc3NhcmlseSBsYXJnZXIgbW9kZWwgdGhhdCBhbHNvIGluY2x1ZGVzICRCJC4KCmBgYHtyIG92ZXJmaXR0aW5nfQpkYXQgPC0gc2ltIHw+IG11dGF0ZShZID0gNTAgKyAxMCpBICsgZXBzaWxvbikKbG0xID0gbG0oWSB+IEEsIGRhdGE9ZGF0KQpsbTIgPSBsbShZIH4gQSArIEIsIGRhdGE9ZGF0KQpsc3QobG0xLCBsbTIpIHw+IG1hcF9kZnIodGlkeSwgLmlkPSdtb2RlbCcpCmBgYAoKQWRkaW5nIHRoZSB1bm5lY2Vzc2FyeSB2YXJpYWJsZSBkb2Vzbid0IGludHJvZHVjZSBiaWFzICgkXGhhdFxiZXRhXzEgXGFwcHJveCAxMCQpLCBidXQgdGhlIHN0YW5kYXJkIGVycm9yIG9mICRcaGF0XGJldGFfMSQgaXMgaGlnaGVyIGluIHRoZSBtaXNzcGVjaWZpZWQgbW9kZWwuCgojIyBVbmRlciBhbmQgT3ZlcmZpdHRpbmcgY29uJ3QKCiMjIyBTY2VuYXJpbyAyOiAkXGJldGFfMD01MCQsICRcYmV0YV8xPTUkIGFuZCAkXGJldGFfMj01JC4KCk5vdyBib3RoIHByZWRpY3RvcnMgZ2VudWluZWx5IG1hdHRlcjoKJCQJWSAgPSAgNTAgKyA1IEEgKyA1IEIgKyBcdmFyZXBzaWxvbiAkJAoKYGBge3IgdW5kZXJmaXR0aW5nfQpkYXQgPC0gc2ltIHw+IG11dGF0ZShZID0gNTAgKyA1KkEgKyA1KkIgKyBlcHNpbG9uKQpsbTEgPSBsbShZIH4gQSwgZGF0YT1kYXQpCmxtMiA9IGxtKFkgfiBBICsgQiwgZGF0YT1kYXQpCmxzdChsbTEsIGxtMikgfD4gbWFwX2Rmcih0aWR5LCAuaWQ9J21vZGVsJykKYGBgCgpXaGVuIHdlIHdyb25nbHkgb21pdCAkQiQsIHRoZSBlc3RpbWF0ZWQgY29lZmZpY2llbnQgZm9yICRBJCBpcyBwdWxsZWQgYXdheSBmcm9tIGl0cyB0cnVlIHZhbHVlLiBPbmNlIGJvdGggcHJlZGljdG9ycyBhcmUgaW5jbHVkZWQsIHRoZSBmaXR0ZWQgY29lZmZpY2llbnRzIGFyZSBjbG9zZSB0byB0aGUgdmFsdWVzIHVzZWQgdG8gZ2VuZXJhdGUgdGhlIGRhdGEuCgojIyMgV2hhdCBkaWQgdGhlIHNpbXVsYXRpb24gc2hvdz8KCk9taXR0aW5nIGEgbmVlZGVkIHZhcmlhYmxlIGNhbiBiaWFzIHRoZSByZW1haW5pbmcgZXN0aW1hdGVzIGFuZCBwcmVkaWN0aW9ucywgZXNwZWNpYWxseSB3aGVuIHRoZSBvbWl0dGVkIHByZWRpY3RvciBvdmVybGFwcyB3aXRoIHZhcmlhYmxlcyByZXRhaW5lZCBpbiB0aGUgbW9kZWwuIEluY2x1ZGluZyBhbiB1bm5lY2Vzc2FyeSB2YXJpYWJsZSBjYW4gbGVhdmUgdGhlIGVzdGltYXRlcyBjZW50cmVkIGNvcnJlY3RseSB3aGlsZSBpbmNyZWFzaW5nIHRoZWlyIHN0YW5kYXJkIGVycm9ycy4KCkluIHByYWN0aWNlIHdlIGRvIG5vdCBrbm93IHdoaWNoIHNpdHVhdGlvbiB3ZSBhcmUgaW4uIE1vZGVsIHNlbGVjdGlvbiB0aGVyZWZvcmUgcmVxdWlyZXMgYSBtb2RlbGxpbmcgZ29hbCBhbmQgY3JpdGVyaWEgdGhhdCByZWZsZWN0IHRoZSBjb3N0IG9mIGNvbXBsZXhpdHkuIEEgaHlwb3RoZXNpcyB0ZXN0IGNhbiBjb250cmlidXRlIGV2aWRlbmNlLCBidXQgaXQgaXMgbm90IGEgY29tcGxldGUgZGVjaXNpb24gcnVsZS4KCiMjIEhvdyBzaG91bGQgd2UganVkZ2UgZXh0cmEgY29tcGxleGl0eT8KCkFkZGluZyB2YXJpYWJsZXMgd2lsbCBhbHdheXMgbWFrZSBSU1Mgc21hbGxlciwgc28gdGhhdCBhbG9uZSBjYW5ub3QgdGVsbCB1cyB3aGV0aGVyIHRoZSBleHRyYSBjb21wbGV4aXR5IHdhcyB3b3J0aCBpdC4gV2UgbmVlZCBjcml0ZXJpYSB0aGF0IHB1dCB0aGUgaW1wcm92ZW1lbnQgaW4gZml0IGludG8gY29udGV4dC4KCiMjIyBGaXQgbWVhc3VyZXMKClRoZSBjb2VmZmljaWVudCBvZiBkZXRlcm1pbmF0aW9uCgokJFJeMiA9IDEgLSBcZnJhY3tcc3VtKHlfaS1caGF0IHlfaSleMn17XHN1bSh5X2ktXGJhciB5KV4yfSQkCgptZWFzdXJlcyB0aGUgcHJvcG9ydGlvbiBvZiBzYW1wbGUgcmVzcG9uc2UgdmFyaWF0aW9uIGFjY291bnRlZCBmb3IgYnkgdGhlIGZpdHRlZCBtb2RlbC4gSXQgZG9lcyBub3QgcGVuYWxpc2UgY29tcGxleGl0eSwgc28gJFJeMiQgY2Fubm90IGRlY3JlYXNlIHdoZW4gdmFyaWFibGVzIGFyZSBhZGRlZC4KCkFkanVzdGVkICRSXjIkIGFjY291bnRzIGZvciByZXNpZHVhbCBkZWdyZWVzIG9mIGZyZWVkb206CgokJFJeMl97XG1hdGhybXtBZGp9fSA9IDEgLSBcZnJhY3tcc3VtKHlfaS1caGF0IHlfaSleMi8obi1wKX17XHN1bSh5X2ktXGJhciB5KV4yLyhuLTEpfS4kJAoKSXQgY2FuIGRlY3JlYXNlIHdoZW4gYW4gYWRkZWQgdGVybSBkb2VzIG5vdCByZWR1Y2UgUlNTIGVub3VnaCB0byBqdXN0aWZ5IGl0cyBkZWdyZWVzIG9mIGZyZWVkb20uIEEgZGVjcmVhc2UgaXMgZXZpZGVuY2UgYWdhaW5zdCB0aGF0IHBhcnRpY3VsYXIgYWRkZWQgc3RydWN0dXJlLCBub3QgcHJvb2YgdGhhdCB0aGUgdmFyaWFibGUgc2hvdWxkIG5ldmVyIGJlIHVzZWQuCgpUaGUgcmVzaWR1YWwgc3RhbmRhcmQgZXJyb3IgaXMgdGhlIHJlc2lkdWFsIHNjYWxlIGVzdGltYXRlOgoKJCRTID0gXHNxcnR7XGZyYWN7UlNTfXtkZl97XG1hdGhybXtyZXNpZHVhbH19fX0uJCQKCkl0IG1lYXN1cmVzIHJlbWFpbmluZyB1bmV4cGxhaW5lZCB2YXJpYXRpb24gb24gdGhlIHJlc3BvbnNlIHNjYWxlLiBJdCBpcyB1c2VmdWwgZm9yIGNvbXBhcmluZyBmaXR0ZWQgbW9kZWxzIHRvIHRoZSBleHRlbnQgdGhhdCB0aGV5IGhhdmUgdGhlIHNhbWUgcmVzcG9uc2UgYW5kIG9ic2VydmF0aW9ucy4KCiMjIFBhcnRpYWwgKkYqIHRlc3RzIGFuZCBBSUMKClBhcnRpYWwgKkYqIHRlc3RzIGFyZSB1c2VmdWwgd2hlbiB3ZSBoYXZlIGEgcGFydGljdWxhciBuZXN0ZWQgY2hhbmdlIGluIG1pbmQuIEFJQyBwcm92aWRlcyBhbm90aGVyIGNyaXRlcmlvbjoKCiQkQUlDID0gblxsb2coUlNTL24pICsgMnAgKyBcdGV4dHtjb25zdGFudH0sJCQKCndoZXJlICRwJCBpcyB0aGUgbnVtYmVyIG9mIGZpdHRlZCByZWdyZXNzaW9uIHBhcmFtZXRlcnMuIFNtYWxsZXIgQUlDIGlzIHByZWZlcnJlZCB3aGVuIGNvbXBhcmluZyBjYW5kaWRhdGUgbW9kZWxzIGZpdHRlZCB0byB0aGUgc2FtZSByZXNwb25zZSBhbmQgb2JzZXJ2YXRpb25zLgoKVW5saWtlIHRoZSBwYXJ0aWFsICpGKiB0ZXN0LCBBSUMgY2FuIGNvbXBhcmUgbW9kZWxzIHRoYXQgYXJlIG5vdCBuZXN0ZWQuIEl0IGlzIHN0aWxsIGp1c3QgYSBjcml0ZXJpb24sIG5vdCBhIG1hY2hpbmUgZm9yIGZpbmRpbmcgdGhlIG9uZSB0cnVlIG1vZGVsLCBidXQgaXQgZ2l2ZXMgdXMgYSBwcmFjdGljYWwgd2F5IHRvIHRyYWRlIGZpdCBhZ2FpbnN0IGNvbXBsZXhpdHkuCgpJbmRpdmlkdWFsIGNvZWZmaWNpZW50ICpQKi12YWx1ZXMgYW5zd2VyIGEgbW9yZSBzcGVjaWZpYyBxdWVzdGlvbiBhYm91dCBvbmUgY29lZmZpY2llbnQgdW5kZXIgb25lIHBhcmFtZXRlcmlzYXRpb24uIFRoZXkgc2hvdWxkIG5vdCBiZSB0cmVhdGVkIGFzIGEgY29tcGxldGUgbW9kZWwtc2VsZWN0aW9uIHJ1bGUuCgojIyBDcm9zcy12YWxpZGF0aW9uOiBhIHByZWRpY3Rpb24gcXVlc3Rpb24KCkV2ZXJ5dGhpbmcgYWJvdmUgaXMgYmFzZWQgb24gdGhlIGRhdGEgdXNlZCB0byBmaXQgdGhlIG1vZGVsLiBJZiBwcmVkaWN0aW9uIGlzIHRoZSBnb2FsLCB0aGUgbW9yZSB1c2VmdWwgcXVlc3Rpb24gaXM6Cgo+IEhvdyB3ZWxsIGRvZXMgdGhlIG1vZGVsIHByZWRpY3Qgb2JzZXJ2YXRpb25zIGl0IGRpZCBub3QgdXNlIHRvIGZpdCBpdHNlbGY/CgpUaGUgYmFzaWMgY3Jvc3MtdmFsaWRhdGlvbiBpZGVhIGlzOgoKYGBge3IgY3Jvc3MtdmFsaWRhdGlvbi1kaWFncmFtLCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjkwJSIsIGZpZy5jYXA9IlRoZSBiYXNpYyBjcm9zcy12YWxpZGF0aW9uIGN5Y2xlLiJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuLi9yZXNvdXJjZXMvY3ZfYmFzaWMuc3ZnIikKYGBgCgpgYGB7ciBjcm9zcy12YWxpZGF0aW9uLWdpZiwgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI5MCUiLCBmaWcuY2FwPSJGaXZlLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiBpbiBhY3Rpb24uIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4uL3Jlc291cmNlcy9jdl81Zm9sZC5naWYiKQpgYGAKCkZvciBub3csIGZvY3VzIG9uIHRoZSBpZGVhIHJhdGhlciB0aGFuIHRoZSBpbXBsZW1lbnRhdGlvbi4gV2Ugd2lsbCB1c2UgY3Jvc3MtdmFsaWRhdGlvbiBpbiBMZWN0dXJlIDI4IHdoZW4gd2Ugc3RhcnQgZml0dGluZyBsYXJnZXIgbW9kZWxzLgoKIyMgU2VsZWN0IHRlcm1zLCBub3QgYXJiaXRyYXJ5IGNvZWZmaWNpZW50cwoKT25lIG1vcmUgdGhpbmcgYmVmb3JlIHdlIHN0YXJ0IHNlbGVjdGluZyBtb2RlbHM6IGEgbW9kZWwgdGVybSBpcyBub3QgYWx3YXlzIHRoZSBzYW1lIHRoaW5nIGFzIG9uZSBjb2VmZmljaWVudC4gQSBmb3VyLWxldmVsIGZhY3RvciBjcmVhdGVzIHRocmVlIHRyZWF0bWVudCBjb2VmZmljaWVudHMsIGJ1dCBzY2llbnRpZmljYWxseSBpdCBpcyBzdGlsbCBvbmUgcHJlZGljdG9yLgoKU2ltaWxhcmx5LCBpbgoKYGBgCnkgfiBBICogeApgYGAKCmBBOnhgIGlzIG9uZSBpbnRlcmFjdGlvbiB0ZXJtLCBldmVuIHRob3VnaCBpdCBjYW4gY29ycmVzcG9uZCB0byBzZXZlcmFsIGludGVyYWN0aW9uIGNvZWZmaWNpZW50cy4gSWYgd2UgcmV0YWluIGFuIGludGVyYWN0aW9uLCB3ZSBub3JtYWxseSByZXRhaW4gdGhlIGNvcnJlc3BvbmRpbmcgbWFpbiBlZmZlY3RzIGFzIHdlbGwuIFRoaXMgaXMgdGhlIGhpZXJhcmNoeSBwcmluY2lwbGUuCgojIyBFeGFtcGxlOiAgTW9kZWwgU2VsZWN0aW9uIGZvciBDbGltYXRlIERhdGEKCldlIHdpbGwgYnVpbGQgYSBtb2RlbCBmb3IgbWVhbiBKdWx5IHRlbXBlcmF0dXJlIGFjcm9zcyAzNiB0b3ducyBpbiBBb3RlYXJvYSBOZXcgWmVhbGFuZC4gV2UgaGF2ZSBsb2NhdGlvbiB2YXJpYWJsZXMgc3VjaCBhcyBsYXRpdHVkZSwgbG9uZ2l0dWRlLCBlbGV2YXRpb24sIGNvYXN0YWwgbG9jYXRpb24gYW5kIGlzbGFuZCwgdG9nZXRoZXIgd2l0aCBzdW1tZXIgdGVtcGVyYXR1cmUsIHN1bnNoaW5lIGFuZCByYWluZmFsbC4KCmByIHhmdW46OmVtYmVkX2ZpbGUoIi4uL2RhdGEvY2xpbWF0ZS5jc3YiKWAgCgpgYGB7ciByZWFkIGNsaW1hdGUsZXZhbD0tMSwgZWNobz0tMiwgbWVzc2FnZT1GQUxTRX0KY2xpbWF0ZSA9IHJlYWRfY3N2KCJjbGltYXRlLmNzdiIpCmNsaW1hdGUgPSByZWFkX2NzdigiLi4vZGF0YS9jbGltYXRlLmNzdiIpCmNsaW1hdGUKYGBgCgojIyBDbGltYXRlIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NH0KY2xpbWF0ZSB8PgogIHBpdm90X2xvbmdlcigtYyhQbGFjZSwgTW5KbHlUZW1wKSkgfD4KICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PXZhbHVlLCB5PU1uSmx5VGVtcCkpICsKICBmYWNldF93cmFwKHZhcnMobmFtZSksIHNjYWxlcyA9ICdmcmVlX3gnLCBuY29sPTQpCmBgYAoKIyMjIEhpZ2hlciB3aW50ZXIgdGVtcGVyYXR1cmUgYXNzb2NpYXRpb25zCgotIExvd2VyIGVsZXZhdGlvbnMgKGhlaWdodCkKLSBMb3dlciBsYXRpdHVkZXMgKGZ1cnRoZXIgbm9ydGgpCi0gSGlnaGVyIGxvbmdpdHVkZXMgKGZ1cnRoZXIgZWFzdCAtIHJlY2FsbCBob3cgQW8vTlogaXMgb3JpZW50ZWQgb24gYSBtYXAgLSBwb3NzaWJseSBjb3JyZWxhdGlvbiB3aXRoIGxhdGl0dWRlLCB3aWxsIGNoZWNrIG5leHQhKQotIEhpZ2hlciBzdW1tZXIgdGVtcGVyYXR1cmVzCi0gTm9ydGggaXNsYW5kIChjb3VsZCBqdXN0IGJlIGxhdGl0dWRlPykKLSBJbmNyZWFzZWQgcmFpbmZhbGwgLSB1cCB0byBhIHBvaW50ISAoc3VidHJvcGljYWwgdnMgcmFpbnkgd2VzdGNvYXN0IC0gaW50ZXJhY3Rpb24gd2l0aCBJc2xhbmQvbGF0aXR1ZGU/PykKLSBDbG9zZW5lc3MgdG8gdGhlIHNlYQotIEluY3JlYXNlZCBzdW5zaGluZSBob3VycwoKU2V2ZXJhbCBvZiB0aGVzZSBwcmVkaWN0b3JzIGFyZSBjbGVhcmx5IHRlbGxpbmcgdXMgcGFydGx5IHRoZSBzYW1lIHN0b3J5LgoKIyMgQ2xpbWF0ZTogTGF0aXR1ZGUvTG9uZ2l0dWRlIGFuZCBOb3J0aCB2cyBTb3V0aAoKYGBge3IgY2xpbWF0ZS1tYXAsIGVjaG89RkFMU0V9Cm56IDwtIHJlYWRfY3N2KCIuLi9yZXNvdXJjZXMvbnpfb3V0bGluZS5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQoKY2xpbWF0ZV9tYXAgPC0gY2xpbWF0ZSB8PgogIG11dGF0ZShJc2xhbmQgPSBpZl9lbHNlKE5vcnRoSXNsYW5kID09IDEsICJOb3J0aCBJc2xhbmQiLCAiU291dGggSXNsYW5kIikpCgpnZ3Bsb3QoKSArCiAgZ2VvbV9wb2x5Z29uKAogICAgZGF0YSA9IG56LAogICAgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwKICAgIGZpbGwgPSAiI2U5ZjBmMyIsIGNvbG91ciA9ICIjMzI1ZDg4IiwgbGluZXdpZHRoID0gMC40CiAgKSArCiAgZ2VvbV9wb2ludCgKICAgIGRhdGEgPSBjbGltYXRlX21hcCwKICAgIGFlcyh4ID0gTG9uZywgeSA9IC1MYXQsIGZpbGwgPSBNbkpseVRlbXAsIHNoYXBlID0gSXNsYW5kKSwKICAgIGNvbG91ciA9ICIjMjIzMTNGIiwgc2l6ZSA9IDMuOCwgc3Ryb2tlID0gMC44CiAgKSArCiAgZ2VvbV90ZXh0KAogICAgZGF0YSA9IGNsaW1hdGVfbWFwLAogICAgYWVzKHggPSBMb25nLCB5ID0gLUxhdCwgbGFiZWwgPSBQbGFjZSksCiAgICBzaXplID0gMi42LCBoanVzdCA9IDAsIG51ZGdlX3ggPSAwLjEyLCBudWRnZV95ID0gMC4wOAogICkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKAogICAgb3B0aW9uID0gIkMiLCBuYW1lID0gIk1lYW4gSnVseSB0ZW1wZXJhdHVyZSIKICApICsKICBzY2FsZV9zaGFwZV9tYW51YWwoCiAgICBuYW1lID0gIklzbGFuZCIsCiAgICB2YWx1ZXMgPSBjKCJOb3J0aCBJc2xhbmQiID0gMjEsICJTb3V0aCBJc2xhbmQiID0gMjQpCiAgKSArCiAgY29vcmRfcXVpY2ttYXAoeGxpbSA9IGMoMTY1LjUsIDE4MCksIHlsaW0gPSBjKC00OCwgLTMzLjUpLCBleHBhbmQgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMKSArCiAgdGhlbWVfdm9pZCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKYGBgCgoKVGhlIG1hcCBtYWtlcyB0aGUgZ2VvZ3JhcGh5IGVhc2llciB0byBzZWU6IHRoZSBOb3J0aCBJc2xhbmQgdG93bnMgYXJlIGdlbmVyYWxseSB3YXJtZXIsIHdoaWxlIGxvbmdpdHVkZSBtb3N0bHkgcmVmbGVjdHMgdGhlIG9yaWVudGF0aW9uIG9mIHRoZSBpc2xhbmRzLiBFbGV2YXRpb24gYWxzbyBhcHBlYXJzIHRvIG1hdHRlciwgYWx0aG91Z2ggaXQgbWF5IGJlIGNvbmZvdW5kZWQgd2l0aCB3aGV0aGVyIGEgdG93biBpcyBjbG9zZSB0byB0aGUgc2VhIChpdCBjYW5ub3QgcmVhbGx5IGJlIGJvdGghKS4KClNvIHdlJ2QgZXhwZWN0IHNvbWUgY29sbGluZWFyaXR5IGhlcmUgYW5kIGZpZ3VyaW5nIG91dCB3aGljaCBhcmUgdGhlIGJlc3QgdmFyaWFibGVzIHRvIGJlIHVzZWQgbWlnaHQgdGFrZSBhIGJpdCBvZiBwbGF5aW5nIQoKIyMgQ2xpbWF0ZTogTGF0aXR1ZGUvTG9uZ2l0dWRlIGFuZCBOb3J0aCB2cyBTb3V0aAoKTGV0J3Mgc3RhcnQgd2l0aCBMYXRpdHVkZSwgdGhlbiBhZGQgaW4gSGVpZ2h0IGFuZCBTZWE6CgpgYGB7cn0KbG0xID0gbG0oIE1uSmx5VGVtcCB+IExhdCwgZGF0YT1jbGltYXRlKQpsbTIgPSBsbSggTW5KbHlUZW1wIH4gTGF0ICsgSGVpZ2h0LCBkYXRhPWNsaW1hdGUpCmxtMyA9IGxtKCBNbkpseVRlbXAgfiBMYXQgKyBIZWlnaHQgKyBTZWEsIGRhdGE9Y2xpbWF0ZSkKYGBgCgojIyBCdWlsZGluZyB0aGUgQ2xpbWF0ZSBNb2RlbAoKIyMjIyBNb2RlbCAxOiBMYXQgb25seQoKYGBge3J9CmxtMSB8PiBzdW1tYXJ5KCkKYGBgCgojIyMjIE1vZGVsIDI6IExhdCArIEhlaWdodAoKYGBge3J9CmxtMiB8PiBzdW1tYXJ5KCkKYGBgCgojIyMjIE1vZGVsIDM6IExhdCArIEhlaWdodCArIFNlYQoKYGBge3J9CmxtMyB8PiBzdW1tYXJ5KCkKYGBgCgojIyMjIEZpcnN0IHRocmVlIHByZWRpY3RvcnM6IGFzc2Vzc21lbnQKClRoZSBmaXJzdCB0aHJlZSBhZGRpdGlvbnMgYWdyZWUgYWNyb3NzIHRoZSBtZWFzdXJlcyB3ZSBoYXZlIGNvbnNpZGVyZWQ6IHRoZSBhZGRlZC10ZXJtICpQKi12YWx1ZXMgYXJlIHNtYWxsLCAkUyQgZGVjcmVhc2VzLCBhbmQgYWRqdXN0ZWQgJFJeMiQgaW5jcmVhc2VzLiBUaGlzIGlzIGNvbnNpc3RlbnQgd2l0aCByZXRhaW5pbmcgYExhdGAsIGBIZWlnaHRgIGFuZCBgU2VhYCBmb3IgdGhpcyBjYW5kaWRhdGUgbW9kZWwgcGF0aC4KClRoZSBgSGVpZ2h0YCBjb2VmZmljaWVudCBhbHNvIGNoYW5nZXMgbm90aWNlYWJseSB3aGVuIGBTZWFgIGlzIGFkZGVkLiBUaGlzIHRlbGxzIHVzIHRoYXQgdGhlIGludGVycHJldGF0aW9uIG9mIGBIZWlnaHRgIGRlcGVuZHMgb24gd2hldGhlciBgU2VhYCBpcyBhZGp1c3RlZCBmb3IuIFRoZSBwcmVkaWN0b3JzIG92ZXJsYXAgaW4gdGhlIGluZm9ybWF0aW9uIHRoZXkgY2FycnksIHNvIHRoZSBjb2VmZmljaWVudCBpbiBhIG11bHRpcGxlIHJlZ3Jlc3Npb24gaXMgYSBjb25kaXRpb25hbCBhc3NvY2lhdGlvbiwgbm90IGFuIGlzb2xhdGVkIGVmZmVjdCBvZiBoZWlnaHQuCgojIyBNb2RlbCA0OiBBZGRpbmcgTm9ydGhJc2xhbmQKCmBgYHtyIGZvdXJ0aCB2YXJpYWJsZX0KbG00ID0gbG0oIE1uSmx5VGVtcCB+IExhdCsgSGVpZ2h0ICsgU2VhICsgTm9ydGhJc2xhbmQsIGRhdGE9Y2xpbWF0ZSkKbG00IHw+IHN1bW1hcnkoKQpgYGAKClRoZSBhZGRlZC10ZXJtICpQKi12YWx1ZSBpcyBzbWFsbCwgYW5kIHRoZSBmaXQgbWVhc3VyZXMgaW1wcm92ZS4gVGhlIGxhcmdlIGNoYW5nZSBpbiB0aGUgYExhdGAgY29lZmZpY2llbnQgdGVsbHMgdXMgdGhhdCB0aGUgaW50ZXJwcmV0YXRpb24gb2YgbGF0aXR1ZGUgZGVwZW5kcyBvbiB3aGV0aGVyIGBOb3J0aElzbGFuZGAgaXMgaW5jbHVkZWQuIEl0IGRvZXMgbm90LCBieSBpdHNlbGYsIHByb3ZlIHRoYXQgdGhlIG5ldyB2YXJpYWJsZSBpcyBjb3JyZWN0aW5nIGJpYXMuCgoqKldoeSBkbyB5b3UgdGhpbmsgYWRkaW5nIE5vcnRoIHZzIFNvdXRoIGlzbGFuZCBpcyB1c2VmdWwgb3ZlciBhbmQgYWJvdmUgTGF0aXR1ZGU/IFdvdWxkbid0IExhdGl0dWRlIGRvIGV2ZXJ5dGhpbmcgaGVyZT8/KioKCiMjIE1vZGVsIDU6IEFkZGluZyBSYWluCgpgYGB7ciBmaWZ0aCB2YXJpYWJsZX0KbG01ID0gbG0oIE1uSmx5VGVtcCB+IExhdCsgSGVpZ2h0KyBTZWEgKyBOb3J0aElzbGFuZCArIFJhaW4sIGRhdGE9Y2xpbWF0ZSkKbG01IHw+IHN1bW1hcnkoKQpgYGAKCgpUaGUgZXZpZGVuY2UgZm9yIGBSYWluYCBpcyBtaXhlZC4gSXRzIGNvZWZmaWNpZW50ICpQKi12YWx1ZSBpcyBhcHByb3hpbWF0ZWx5IDAuMTAsIHdoaWxlICRTJCBkZWNyZWFzZXMgYW5kIGFkanVzdGVkICRSXjIkIGluY3JlYXNlcy4gVGhlc2UgYXJlIGFsbCBpbi1zYW1wbGUgbWVhc3VyZXMsIHNvIHRoZXkgZG8gbm90IGVzdGFibGlzaCB0aGF0IGBSYWluYCB3aWxsIGltcHJvdmUgcHJlZGljdGlvbiBmb3IgbmV3IHRvd25zLiBJZiBwcmVkaWN0aW9uIHdlcmUgdGhlIHByaW1hcnkgZ29hbCwgdGhpcyB3b3VsZCBiZSBhIG5hdHVyYWwgY2FzZSBmb3IgdmFsaWRhdGlvbiBvciBjcm9zcy12YWxpZGF0aW9uLgoKIyMgTW9kZWwgNjogQWRkaW5nIExvbmdpdHVkZQoKYGBge3Igc2l4dGggdmFyaWFibGUsIHdhcm5pbmc9RkFMU0V9CmxtNiA9IGxtKCBNbkpseVRlbXAgfiBMYXQrIEhlaWdodCsgU2VhICsgTm9ydGhJc2xhbmQrIFJhaW4gKyBMb25nLCBkYXRhPWNsaW1hdGUpCmxtNiB8PiBzdW1tYXJ5KCkKYGBgCgpUaGUgZXZpZGVuY2UgYWdhaW5zdCBhZGRpbmcgYExvbmdgIGlzIGNvbnNpc3RlbnQ6IGl0cyAqUCotdmFsdWUgaXMgZ3JlYXRlciB0aGFuIDAuNSwgYWRqdXN0ZWQgJFJeMiQgZGVjcmVhc2VzLCBhbmQgJFMkIGluY3JlYXNlcy4gVGhpcyBjYW5kaWRhdGUgYWRkaXRpb24gZG9lcyBub3QgaW1wcm92ZSB0aGUgZml0dGVkIG1vZGVsIGJ5IHRoZXNlIG1lYXN1cmVzLgoKIyMgQ2xpbWF0ZSBtb2RlbC1idWlsZGluZyBwcm9ncmVzc2lvbgoKV2UgaGF2ZSBmb2xsb3dlZCBvbmUgc2Vuc2libGUgcGF0aCB0aHJvdWdoIHRoZSBjYW5kaWRhdGUgbW9kZWxzLiBUaGF0IGRvZXMgbm90IHByb3ZlIHdlIGhhdmUgZm91bmQgdGhlIG9ubHkgY29ycmVjdCBmb3JtdWxhLiBUaGUgdGFibGUganVzdCBwdXRzIHRoZSBldmlkZW5jZSBmcm9tIGVhY2ggc3RlcCBpbiBvbmUgcGxhY2UuCgpgYGB7ciBjbGltYXRlLXByb2dyZXNzaW9uLCBlY2hvPUZBTFNFLCByZXN1bHRzPSdhc2lzJ30KY2xpbWF0ZV9wcm9ncmVzc2lvbiA8LSB0aWJibGUoCiAgTW9kZWwgPSBwYXN0ZSgibG0iLCAxOjYpLAogIGBBZGRlZCB0ZXJtYCA9IGMoIkxhdCIsICJIZWlnaHQiLCAiU2VhIiwgIk5vcnRoSXNsYW5kIiwgIlJhaW4iLCAiTG9uZyIpLAogIGBBZGp1c3RlZCBSMmAgPSBzYXBwbHkobGlzdChsbTEsIGxtMiwgbG0zLCBsbTQsIGxtNSwgbG02KSwgZnVuY3Rpb24obW9kZWwpIHN1bW1hcnkobW9kZWwpJGFkai5yLnNxdWFyZWQpLAogIFJTRSA9IHNhcHBseShsaXN0KGxtMSwgbG0yLCBsbTMsIGxtNCwgbG01LCBsbTYpLCBzaWdtYSksCiAgQUlDID0gc2FwcGx5KGxpc3QobG0xLCBsbTIsIGxtMywgbG00LCBsbTUsIGxtNiksIEFJQyksCiAgYEFkZGVkLXRlcm0gZXZpZGVuY2VgID0gYygiPCAwLjAwMSIsICI8IDAuMDAxIiwgIjwgMC4wMDEiLCAiPCAwLjAwMSIsICIwLjEwIiwgIj4gMC41IikKKQoKa25pdHI6OmthYmxlKGNsaW1hdGVfcHJvZ3Jlc3Npb24sIGRpZ2l0cyA9IDMsCiAgICAgICAgICAgICBjYXB0aW9uID0gIkNsaW1hdGUgY2FuZGlkYXRlIG1vZGVscyBhbmQgaW4tc2FtcGxlIGNvbXBhcmlzb24gbWVhc3VyZXMiKQpgYGAKClRoZSB0YWJsZSBpcyB1c2VmdWwgYmVjYXVzZSBpdCBzaG93cyB3aGF0IGVhY2ggZXh0cmEgdGVybSBidXlzIHVzLiBJdCBzdGlsbCBkb2VzIG5vdCByZXBsYWNlIHRoZSBtb2RlbGxpbmcgZ29hbCwgc3ViamVjdC1tYXR0ZXIga25vd2xlZGdlLCBvciBwcm9wZXIgdmFsaWRhdGlvbiBpZiBwcmVkaWN0aW9uIGlzIHdoYXQgd2UgY2FyZSBhYm91dC4KCiMjIEZpbmFsIE1vZGVsIERpYWdub3N0aWMgUGxvdHMKCiMjIyBBIGZpbmFsIG1vZGVsIGNoZWNrCgpPbmNlIHdlIGhhdmUgYSBwbGF1c2libGUgbWVhbiBzdHJ1Y3R1cmUsIHdlIHN0aWxsIG5lZWQgdG8gY2hlY2sgd2hldGhlciB0aGUgZml0dGVkIG1vZGVsIHNob3dzIG9idmlvdXMgcHJvYmxlbXMgd2l0aCB0aGUgTElORSBhc3N1bXB0aW9ucy4KCmBsbShNbkpseVRlbXAgfiBMYXQgKyBIZWlnaHQgKyBTZWEgKyBOb3J0aElzbGFuZCArIFJhaW4sIGRhdGEgPSBjbGltYXRlKWAuCgoKYGBge3IgZGlhZ25vc3RpYyBwbG90cywgZWNobz1GQUxTRX0KIyBHZW5lcmF0ZSB0aGUgNCBzdGFuZGFyZCBkaWFnbm9zdGljIHBsb3RzCnBhcihtZnJvdyA9IGMoMiwgMikpCnBsb3QobG01KQpgYGAKCiMjIyBJbnRlcnByZXRhdGlvbiBvZiBkaWFnbm9zdGljIHBsb3RzCgotIFJlc2lkdWFscyB2cy4gRml0dGVkOiB0aGUgcG9pbnRzIHNob3cgbm8gc3Ryb25nIHN5c3RlbWF0aWMgcGF0dGVybiwgc28gdGhlcmUgaXMgbm90IHN0cm9uZyBldmlkZW5jZSBvZiBhIHByb2JsZW0gd2l0aCB0aGUgbGluZWFyIG1lYW4gc3RydWN0dXJlLgotIE5vcm1hbCBRLVE6IHRoZSBwb2ludHMgYXJlIHJlYXNvbmFibHkgY2xvc2UgdG8gdGhlIHJlZmVyZW5jZSBsaW5lLCB3aXRoIG9uZSBub3RpY2VhYmxlIGRlcGFydHVyZS4gVGhpcyBpcyBub3QgcHJvb2Ygb2Ygbm9ybWFsaXR5LCBidXQgaXQgZ2l2ZXMgbm8gc3Ryb25nIHdhcm5pbmcgYXQgdGhpcyBzY2FsZS4KLSBTY2FsZS1Mb2NhdGlvbjogdGhlIHNwcmVhZCBkb2VzIG5vdCBzaG93IGEgc3Ryb25nIHRyZW5kIGFjcm9zcyBmaXR0ZWQgdmFsdWVzLCBzbyB0aGVyZSBpcyBub3Qgc3Ryb25nIGV2aWRlbmNlIG9mIGNoYW5naW5nIHZhcmlhbmNlLgotIFJlc2lkdWFscyB2cy4gTGV2ZXJhZ2U6IG5vIG9ic2VydmF0aW9uIHN0YW5kcyBvdXQgYXMgb2J2aW91c2x5IGluZmx1ZW50aWFsIGZyb20gdGhpcyBwbG90IGFsb25lLgoKVGhlc2UgcGxvdHMgZG8gbm90IGNlcnRpZnkgdGhlIG1vZGVsLiBUaGV5IHRlbGwgdXMgd2hldGhlciB0aGVyZSBhcmUgb2J2aW91cyBkZXBhcnR1cmVzIHRoYXQgbmVlZCBtb3JlIGF0dGVudGlvbi4KCiMjIEEgbW9yZSByZWFsaXN0aWMgcnVubmluZyBleGFtcGxlCgpGcm9tIHRoaXMgcG9pbnQgaW4gdGhlIGNvdXJzZSwgd2UgYXJlIGdvaW5nIHRvIGtlZXAgY29taW5nIGJhY2sgdG8gb25lIG1vcmUgY29tcGxleCBkYXRhc2V0IHJhdGhlciB0aGFuIHN0YXJ0aW5nIGZyb20gc2NyYXRjaCB3aXRoIGEgbmV3IHRveSBleGFtcGxlIGVhY2ggdGltZS4gVGhlIGRhdGEgYXJlIGhvdXJseSBiaWtlIHJlbnRhbHMgaW4gU2VvdWwsIFNvdXRoIEtvcmVhLCB0b2dldGhlciB3aXRoIHdlYXRoZXIgYW5kIGNhbGVuZGFyIHZhcmlhYmxlcyB0aGF0IG1pZ2h0IGhlbHAgdXMgZXhwbGFpbiBvciBwcmVkaWN0IGRlbWFuZC4KCldlIHdpbGwgYnVpbGQgdGhpcyBtb2RlbCB1cCBhcyB0aGUgY291cnNlIGdvZXMgb24uIEhlcmUgd2Ugc3RhcnQgd2l0aCBzZXZlcmFsIHBsYXVzaWJsZSBwcmVkaWN0b3JzLiBMYXRlciB3ZSB3aWxsIGNvbWUgYmFjayB0byB0aGUgc2FtZSBkYXRhIGZvciBhdXRvbWF0ZWQgdmFyaWFibGUgc2VsZWN0aW9uLCBwZW5hbGlzZWQgcmVncmVzc2lvbiwgbXVsdGljb2xsaW5lYXJpdHkgYW5kIHRoZSBvdGhlciBwcm9ibGVtcyB0aGF0IGFwcGVhciBvbmNlIGEgbW9kZWwgc3RhcnRzIGxvb2tpbmcgbW9yZSBsaWtlIHNvbWV0aGluZyB3ZSB3b3VsZCBmaXQgaW4gcHJhY3RpY2UuCgpgciB4ZnVuOjplbWJlZF9maWxlKCIuLi9kYXRhL3Nlb3VsX2Jpa2VfaG91cmx5LmNzdiIpYAoKYGBge3IgcmVhZCBzZW91bCBob3VybHksIG1lc3NhZ2U9RkFMU0V9CnNlb3VsX2hvdXJseSA9IHJlYWRfY3N2KCIuLi9kYXRhL3Nlb3VsX2Jpa2VfaG91cmx5LmNzdiIpCnNlb3VsX2hvdXJseQpgYGAKClRoZSByZXNwb25zZSBpcyBgcmVudGVkX2Jpa2VfY291bnRgLiBQb3RlbnRpYWwgZXhwbGFuYXRvcnkgdmFyaWFibGVzIGluY2x1ZGUgd2VhdGhlciBtZWFzdXJlbWVudHMgc3VjaCBhcyB0ZW1wZXJhdHVyZSwgaHVtaWRpdHksIHdpbmQgc3BlZWQgYW5kIHJhaW5mYWxsLCBhcyB3ZWxsIGFzIGNhbGVuZGFyIGluZm9ybWF0aW9uIHN1Y2ggYXMgaG91ciwgc2Vhc29uIGFuZCB3aGV0aGVyIHRoZSBkYXkgaXMgYSBob2xpZGF5LgoKVGhlcmUgaXMgbm8gb2J2aW91cyBzaW5nbGUgbW9kZWwgaGVyZS4gU2V2ZXJhbCBwcmVkaWN0b3JzIGFyZSBwbGF1c2libGUsIHNvbWUgYXJlIGNsZWFybHkgcmVsYXRlZCB0byBvbmUgYW5vdGhlciwgYW5kIHRoZSBudW1iZXIgb2YgcG9zc2libGUgbW9kZWxzIGdldHMgbGFyZ2UgdmVyeSBxdWlja2x5LiBUaGF0IGlzIGV4YWN0bHkgd2h5IHRoaXMgaXMgdXNlZnVsIGFzIGEgcnVubmluZyBleGFtcGxlLgoKYGBge3Igc2VvdWwgY2FuZGlkYXRlIG1vZGVscywgbWVzc2FnZT1GQUxTRX0Kc2VvdWxfYmFzaWMgPSBsbSgKICByZW50ZWRfYmlrZV9jb3VudCB+IHRlbXBlcmF0dXJlICsgaHVtaWRpdHkgKyB3aW5kX3NwZWVkLAogIGRhdGEgPSBzZW91bF9ob3VybHkKKQoKc2VvdWxfcmljaGVyID0gbG0oCiAgcmVudGVkX2Jpa2VfY291bnQgfiB0ZW1wZXJhdHVyZSArIGh1bWlkaXR5ICsgd2luZF9zcGVlZCArCiAgICB2aXNpYmlsaXR5ICsgZGV3X3BvaW50X3RlbXBlcmF0dXJlICsgc29sYXJfcmFkaWF0aW9uICsKICAgIHJhaW5mYWxsICsgc25vd2ZhbGwgKyBob3VyICsgc2Vhc29uICsgaG9saWRheSArIGZ1bmN0aW9uaW5nX2RheSwKICBkYXRhID0gc2VvdWxfaG91cmx5CikKCnRpYmJsZSgKICBtb2RlbCA9IGMoImJhc2ljIHdlYXRoZXIgbW9kZWwiLCAicmljaGVyIHdlYXRoZXIgKyBjYWxlbmRhciBtb2RlbCIpLAogIGFkal9yX3NxdWFyZWQgPSBjKAogICAgc3VtbWFyeShzZW91bF9iYXNpYykkYWRqLnIuc3F1YXJlZCwKICAgIHN1bW1hcnkoc2VvdWxfcmljaGVyKSRhZGouci5zcXVhcmVkCiAgKSwKICBzaWdtYSA9IGMoc3VtbWFyeShzZW91bF9iYXNpYykkc2lnbWEsIHN1bW1hcnkoc2VvdWxfcmljaGVyKSRzaWdtYSksCiAgYWljID0gYyhBSUMoc2VvdWxfYmFzaWMpLCBBSUMoc2VvdWxfcmljaGVyKSkKKQpgYGAKClRoZSByaWNoZXIgbW9kZWwgbG9va3MgYmV0dGVyIG9uIGFsbCB0aHJlZSBpbi1zYW1wbGUgY3JpdGVyaWE6IGFkanVzdGVkICRSXjIkIGlzIGhpZ2hlciwgd2hpbGUgcmVzaWR1YWwgc3RhbmRhcmQgZXJyb3IgYW5kIEFJQyBhcmUgbG93ZXIuIFRoYXQgdGVsbHMgdXMgaXQgZml0cyB0aGVzZSBkYXRhIGJldHRlciBhZnRlciBhY2NvdW50aW5nLCB0byBzb21lIGV4dGVudCwgZm9yIHRoZSBleHRyYSBjb21wbGV4aXR5LiBJdCBkb2VzIG5vdCB5ZXQgdGVsbCB1cyB0aGF0IGl0IHdpbGwgcHJlZGljdCBuZXcgYmlrZSBkZW1hbmQgYmV0dGVyLgoKV2l0aCBvbmx5IGEgZmV3IGNhbmRpZGF0ZSBtb2RlbHMgd2UgY2FuIHN0aWxsIGRvIHRoaXMgYnkgaGFuZC4gT25jZSB0aGUgcHJlZGljdG9yIGxpc3QgZ2V0cyBsb25nZXIsIHRoYXQgYmVjb21lcyBwYWluZnVsLiBJbiB0aGUgbmV4dCBsZWN0dXJlIHdlIGxvb2sgYXQgdHdvIHdheXMgZm9yd2FyZDogYXV0b21hdGUgdGhlIHNlYXJjaCB0aHJvdWdoIGNhbmRpZGF0ZSBtb2RlbHMsIG9yIGtlZXAgYSBsYXJnZXIgbW9kZWwgYW5kIHNocmluayB0aGUgY29lZmZpY2llbnRzIGluc3RlYWQgb2YgcmVwZWF0ZWRseSBhZGRpbmcgYW5kIHJlbW92aW5nIHRlcm1zLgo=