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.
unlabelled

The bias-variance trade-off as model flexibility changes.

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.

How should we judge extra complexity?

Adding variables will always make RSS smaller, so that alone cannot tell us whether the extra complexity was worth it. We need criteria that put the improvement in fit into context.

Fit measures

The coefficient of determination

\[R^2 = 1 - \frac{\sum(y_i-\hat y_i)^2}{\sum(y_i-\bar y)^2}\]

measures the proportion of sample response variation accounted for by the fitted model. It does not penalise complexity, so \(R^2\) cannot decrease when variables are added.

Adjusted \(R^2\) accounts for residual degrees of freedom:

\[R^2_{\mathrm{Adj}} = 1 - \frac{\sum(y_i-\hat y_i)^2/(n-p)}{\sum(y_i-\bar y)^2/(n-1)}.\]

It can decrease when an added term does not reduce RSS enough to justify its degrees of freedom. A decrease is evidence against that particular added structure, not proof that the variable should never be used.

The residual standard error is the residual scale estimate:

\[S = \sqrt{\frac{RSS}{df_{\mathrm{residual}}}}.\]

It measures remaining unexplained variation on the response scale. It is useful for comparing fitted models to the extent that they have the same response and observations.

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:

unlabelled

The basic cross-validation cycle.

unlabelled

Five-fold cross-validation in action.

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)

unlabelled

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

unlabelled

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
Model Added term Adjusted R2 RSE AIC Added-term evidence
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).

unlabelled

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=