Forward selection

Start with the intercept-only model. At each step, ask which single addition gives the best reduction in AIC. We will follow each step and examine how the candidate models are considered by the algorithm.

climate_forward <- step(climate_null, scope = climate_scope, direction = "forward",
    trace = 1)
Start:  AIC=388.08
Sun ~ 1

              Df Sum of Sq     RSS    AIC
+ MnJanTemp    1    506057 1130350 376.76
+ Long         1    344588 1291820 381.57
+ Rain         1    316258 1320150 382.35
+ Lat          1    220053 1416355 384.88
+ MnJlyTemp    1    182734 1453674 385.82
+ Sea          1    148252 1488156 386.66
+ NorthIsland  1     95788 1540620 387.91
+ Height       1     90941 1545467 388.02
<none>                     1636408 388.08

Step:  AIC=376.76
Sun ~ MnJanTemp

              Df Sum of Sq     RSS    AIC
+ Rain         1    132170  998181 374.29
+ NorthIsland  1     76944 1053407 376.22
<none>                     1130350 376.76
+ Sea          1     50284 1080066 377.12
+ Lat          1     43712 1086639 377.34
+ MnJlyTemp    1     22898 1107452 378.03
+ Long         1     14102 1116248 378.31
+ Height       1        40 1130310 378.76

Step:  AIC=374.29
Sun ~ MnJanTemp + Rain

              Df Sum of Sq    RSS    AIC
+ Sea          1     87195 910985 373.00
<none>                     998181 374.29
+ NorthIsland  1     53140 945040 374.32
+ Long         1     12081 986099 375.85
+ Height       1      1193 996988 376.24
+ Lat          1       330 997850 376.27
+ MnJlyTemp    1        16 998165 376.29

Step:  AIC=373
Sun ~ MnJanTemp + Rain + Sea

              Df Sum of Sq    RSS    AIC
+ Height       1     94788 816197 371.04
+ MnJlyTemp    1     52902 858083 372.84
<none>                     910985 373.00
+ Long         1     38986 871999 373.42
+ NorthIsland  1     16393 894592 374.34
+ Lat          1      1945 909040 374.92

Step:  AIC=371.04
Sun ~ MnJanTemp + Rain + Sea + Height

              Df Sum of Sq    RSS    AIC
<none>                     816197 371.04
+ NorthIsland  1     28556 787641 371.76
+ MnJlyTemp    1     22773 793424 372.02
+ Long         1     19420 796777 372.17
+ Lat          1     10788 805409 372.56

. . .

Finally stopped.Let’s check what we ended up with again.

. . .

formula(climate_forward)
Sun ~ MnJanTemp + Rain + Sea + Height
AIC(climate_forward)
[1] 475.2037

Backward elimination

This is the same overall idea as forward selection, however we start with all terms in the model and kick terms out based on the best reduction in AIC.

climate_backward <- step(climate_full, scope = climate_scope, direction = "backward",
    trace = 1)
Start:  AIC=372.48
Sun ~ Lat + Long + MnJanTemp + MnJlyTemp + Rain + Height + Sea + 
    NorthIsland

              Df Sum of Sq    RSS    AIC
- Lat          1       760 680941 370.52
- MnJlyTemp    1      9027 689208 370.95
- Height       1     21370 701551 371.59
<none>                     680181 372.48
- NorthIsland  1     47272 727453 372.90
- MnJanTemp    1     62239 742420 373.63
- Rain         1     79398 759579 374.45
- Long         1    104127 784308 375.61
- Sea          1    112082 792263 375.97

Step:  AIC=370.52
Sun ~ Long + MnJanTemp + MnJlyTemp + Rain + Height + Sea + NorthIsland

              Df Sum of Sq    RSS    AIC
- MnJlyTemp    1      8381 689322 368.96
<none>                     680941 370.52
- Height       1     39668 720609 370.56
- NorthIsland  1     48846 729788 371.01
- Rain         1     86487 767428 372.82
- Long         1    104518 785460 373.66
- Sea          1    114410 795351 374.11
- MnJanTemp    1    147165 828106 375.56

Step:  AIC=368.96
Sun ~ Long + MnJanTemp + Rain + Height + Sea + NorthIsland

              Df Sum of Sq    RSS    AIC
<none>                     689322 368.96
- Height       1     76044 765366 370.73
- Long         1     98319 787641 371.76
- NorthIsland  1    107455 796777 372.17
- Sea          1    125566 814888 372.98
- MnJanTemp    1    140484 829807 373.64
- Rain         1    142964 832286 373.74

. . .

Done.

formula(climate_backward)
Sun ~ Long + MnJanTemp + Rain + Height + Sea + NorthIsland
AIC(climate_backward)
[1] 473.1216

. . .

Now where did we end up? Not the same model as the forward selection. Now let’s try stepwise!?

Stepwise selection

Stepwise selection allows both kinds of move. We start small, but after adding a term the algorithm can reconsider terms already in the model.

climate_both <- step(climate_null, scope = climate_scope, direction = "both", trace = 1)
Start:  AIC=388.08
Sun ~ 1

              Df Sum of Sq     RSS    AIC
+ MnJanTemp    1    506057 1130350 376.76
+ Long         1    344588 1291820 381.57
+ Rain         1    316258 1320150 382.35
+ Lat          1    220053 1416355 384.88
+ MnJlyTemp    1    182734 1453674 385.82
+ Sea          1    148252 1488156 386.66
+ NorthIsland  1     95788 1540620 387.91
+ Height       1     90941 1545467 388.02
<none>                     1636408 388.08

Step:  AIC=376.76
Sun ~ MnJanTemp

              Df Sum of Sq     RSS    AIC
+ Rain         1    132170  998181 374.29
+ NorthIsland  1     76944 1053407 376.22
<none>                     1130350 376.76
+ Sea          1     50284 1080066 377.12
+ Lat          1     43712 1086639 377.34
+ MnJlyTemp    1     22898 1107452 378.03
+ Long         1     14102 1116248 378.31
+ Height       1        40 1130310 378.76
- MnJanTemp    1    506057 1636408 388.08

Step:  AIC=374.29
Sun ~ MnJanTemp + Rain

              Df Sum of Sq     RSS    AIC
+ Sea          1     87195  910985 373.00
<none>                      998181 374.29
+ NorthIsland  1     53140  945040 374.32
+ Long         1     12081  986099 375.85
+ Height       1      1193  996988 376.24
+ Lat          1       330  997850 376.27
+ MnJlyTemp    1        16  998165 376.29
- Rain         1    132170 1130350 376.76
- MnJanTemp    1    321969 1320150 382.35

Step:  AIC=373
Sun ~ MnJanTemp + Rain + Sea

              Df Sum of Sq     RSS    AIC
+ Height       1     94788  816197 371.04
+ MnJlyTemp    1     52902  858083 372.84
<none>                      910985 373.00
+ Long         1     38986  871999 373.42
- Sea          1     87195  998181 374.29
+ NorthIsland  1     16393  894592 374.34
+ Lat          1      1945  909040 374.92
- Rain         1    169081 1080066 377.12
- MnJanTemp    1    214016 1125001 378.59

Step:  AIC=371.04
Sun ~ MnJanTemp + Rain + Sea + Height

              Df Sum of Sq     RSS    AIC
<none>                      816197 371.04
+ NorthIsland  1     28556  787641 371.76
+ MnJlyTemp    1     22773  793424 372.02
+ Long         1     19420  796777 372.17
+ Lat          1     10788  805409 372.56
- Height       1     94788  910985 373.00
- Sea          1    180791  996988 376.24
- Rain         1    226572 1042769 377.86
- MnJanTemp    1    284136 1100333 379.79

. . .

Done.

. . .

formula(climate_both)
Sun ~ MnJanTemp + Rain + Sea + Height
AIC(climate_both)
[1] 475.2037

Does the bidirectional search finish at the forward model, the backward model, or somewhere else?

Comparing the climate searches

Search Final formula AIC
Forward Sun ~ MnJanTemp + Rain + Sea + Height 475.20
Backward Sun ~ Long + MnJanTemp + Rain + Height + Sea + NorthIsland 473.12
Stepwise: both Sun ~ MnJanTemp + Rain + Sea + Height 475.20
The lower AIC is preferred among the models actually reached by the search. Different starting points can finish at different local solutions. Agreement between searches is reassuring for this dataset, but it does not make the selected formula uniquely correct.

A larger problem: Seoul bike demand

The Seoul data contain hourly bike rentals together with weather and calendar variables. We have already watched forward, backward and bidirectional search on the smaller climate problem, so here we will use one bidirectional search and focus on defining the model space.

seoul_hourly <- read_csv("../data/seoul_bike_hourly.csv", show_col_types = FALSE) |>
    mutate(hour = factor(hour))

seoul_subset <- seoul_hourly |>
    select(rented_bike_count, hour, season, holiday, functioning_day, temperature,
        humidity, wind_speed, visibility, dew_point_temperature, solar_radiation,
        rainfall, snowfall) |>
    drop_na()

Note that we are treating hour records as a factor rather than forcing bike demand to change linearly from midnight to 11 pm.

. . .

seoul_null <- lm(rented_bike_count ~ 1, data = seoul_subset)

seoul_full <- lm(rented_bike_count ~ hour + season + holiday + functioning_day +
    temperature + humidity + wind_speed + visibility + dew_point_temperature + solar_radiation +
    rainfall + snowfall, data = seoul_subset)

seoul_scope <- list(lower = formula(seoul_null), upper = formula(seoul_full))

seoul_scope
$lower
rented_bike_count ~ 1

$upper
rented_bike_count ~ hour + season + holiday + functioning_day + 
    temperature + humidity + wind_speed + visibility + dew_point_temperature + 
    solar_radiation + rainfall + snowfall

The R term scope tells step() the smallest and largest formulas it is allowed to consider. This is handy if you always want a certain term in the model.

Stepwise search on the Seoul data

seoul_step <- step(seoul_null, scope = seoul_scope, direction = "both", trace = 1)
Start:  AIC=113342.2
rented_bike_count ~ 1

                        Df  Sum of Sq        RSS    AIC
+ temperature            1 1056904520 2587029843 110343
+ hour                  23 1064201100 2579733263 110363
+ season                 3  765709006 2878225356 111282
+ dew_point_temperature  1  525597511 3118336852 111980
+ solar_radiation        1  249823064 3394111299 112722
+ functioning_day        1  151560828 3492373534 112972
+ humidity               1  145437128 3498497235 112987
+ visibility             1  144710241 3499224121 112989
+ snowfall               1   73273235 3570661128 113166
+ rainfall               1   55195401 3588738962 113210
+ wind_speed             1   53446519 3590487844 113215
+ holiday                1   19067701 3624866661 113298
<none>                                3643934363 113342

Step:  AIC=110343.4
rented_bike_count ~ temperature

                        Df  Sum of Sq        RSS    AIC
+ hour                  23  843729089 1743300754 106932
+ humidity               1  304994771 2282035071 109247
+ dew_point_temperature  1  273094201 2313935642 109368
+ functioning_day        1  194870595 2392159247 109659
+ visibility             1  118918824 2468111018 109933
+ rainfall               1   82364833 2504665010 110062
+ wind_speed             1   72162334 2514867509 110098
+ season                 3   29334094 2557695748 110250
+ solar_radiation        1   21261579 2565768264 110273
+ holiday                1    6514428 2580515414 110323
+ snowfall               1    2237212 2584792630 110338
<none>                                2587029843 110343
- temperature            1 1056904520 3643934363 113342

Step:  AIC=106931.6
rented_bike_count ~ temperature + hour

                        Df Sum of Sq        RSS    AIC
+ functioning_day        1 186915633 1556385121 105940
+ humidity               1 173760455 1569540299 106014
+ dew_point_temperature  1 153236015 1590064739 106128
+ rainfall               1  90363923 1652936831 106467
+ solar_radiation        1  75740488 1667560266 106544
+ visibility             1  71178026 1672122728 106568
+ season                 3  38391455 1704909298 106743
+ holiday                1   7370701 1735930053 106896
+ snowfall               1   2460256 1740840498 106921
+ wind_speed             1   1623298 1741677456 106925
<none>                               1743300754 106932
- hour                  23 843729089 2587029843 110343
- temperature            1 836432509 2579733263 110363

Step:  AIC=105940.1
rented_bike_count ~ temperature + hour + functioning_day

                        Df Sum of Sq        RSS    AIC
+ humidity               1 171653389 1384731732 104918
+ dew_point_temperature  1 149699208 1406685913 105056
+ rainfall               1  91573998 1464811123 105411
+ season                 3  83935134 1472449987 105460
+ visibility             1  77443817 1478941304 105495
+ solar_radiation        1  75199277 1481185844 105508
+ holiday                1   5271935 1551113186 105912
+ snowfall               1   3453574 1552931547 105923
+ wind_speed             1   1795566 1554589555 105932
<none>                               1556385121 105940
- functioning_day        1 186915633 1743300754 106932
- hour                  23 835774126 2392159247 109659
- temperature            1 875980432 2432365553 109849

Step:  AIC=104918.4
rented_bike_count ~ temperature + hour + functioning_day + humidity

                        Df  Sum of Sq        RSS    AIC
+ season                 3   95589314 1289142418 104298
+ rainfall               1   39929433 1344802299 104664
+ solar_radiation        1    9463826 1375267907 104860
+ holiday                1    8093992 1376637740 104869
+ visibility             1    1888005 1382843728 104908
+ dew_point_temperature  1    1752158 1382979575 104909
+ snowfall               1     479728 1384252004 104917
<none>                                1384731732 104918
+ wind_speed             1     153460 1384578273 104919
- humidity               1  171653389 1556385121 105940
- functioning_day        1  184808566 1569540299 106014
- hour                  23  708681498 2093413230 108493
- temperature            1 1032873206 2417604939 109798

Step:  AIC=104297.8
rented_bike_count ~ temperature + hour + functioning_day + humidity + 
    season

                        Df Sum of Sq        RSS    AIC
+ rainfall               1  39434247 1249708172 104028
+ solar_radiation        1  10944435 1278197983 104225
+ holiday                1   5717923 1283424496 104261
+ dew_point_temperature  1   2599798 1286542620 104282
+ snowfall               1   1875639 1287266780 104287
<none>                               1289142418 104298
+ wind_speed             1    209094 1288933324 104298
+ visibility             1    126700 1289015718 104299
- season                 3  95589314 1384731732 104918
- temperature            1 155591977 1444734395 105294
- humidity               1 183307569 1472449987 105460
- functioning_day        1 229470047 1518612465 105731
- hour                  23 717078111 2006220529 108126

Step:  AIC=104027.6
rented_bike_count ~ temperature + hour + functioning_day + humidity + 
    season + rainfall

                        Df Sum of Sq        RSS    AIC
+ solar_radiation        1   8812374 1240895798 103968
+ holiday                1   5603476 1244104696 103990
+ snowfall               1   1362942 1248345230 104020
+ dew_point_temperature  1    936585 1248771587 104023
+ wind_speed             1    303290 1249404882 104028
<none>                               1249708172 104028
+ visibility             1     55499 1249652673 104029
- rainfall               1  39434247 1289142418 104298
- season                 3  95094127 1344802299 104664
- humidity               1 130345225 1380053397 104895
- temperature            1 149274881 1398983053 105014
- functioning_day        1 229378123 1479086294 105502
- hour                  23 730246773 1979954945 108013

Step:  AIC=103967.7
rented_bike_count ~ temperature + hour + functioning_day + humidity + 
    season + rainfall + solar_radiation

                        Df Sum of Sq        RSS    AIC
+ holiday                1   5630805 1235264993 103930
+ dew_point_temperature  1   2347099 1238548700 103953
+ snowfall               1   1054360 1239841439 103962
<none>                               1240895798 103968
+ visibility             1    172237 1240723561 103968
+ wind_speed             1      7361 1240888437 103970
- solar_radiation        1   8812374 1249708172 104028
- rainfall               1  37302185 1278197983 104225
- humidity               1  80806547 1321702345 104518
- season                 3  96310930 1337206728 104616
- temperature            1 118326884 1359222682 104764
- functioning_day        1 230699299 1471595097 105459
- hour                  23 703948892 1944844690 107858

Step:  AIC=103929.8
rented_bike_count ~ temperature + hour + functioning_day + humidity + 
    season + rainfall + solar_radiation + holiday

                        Df Sum of Sq        RSS    AIC
+ dew_point_temperature  1   2409125 1232855868 103915
+ snowfall               1    924749 1234340244 103925
<none>                               1235264993 103930
+ visibility             1    208411 1235056582 103930
+ wind_speed             1     18006 1235246987 103932
- holiday                1   5630805 1240895798 103968
- solar_radiation        1   8839703 1244104696 103990
- rainfall               1  37187683 1272452676 104188
- humidity               1  81886272 1317151266 104490
- season                 3  93917308 1329182302 104566
- temperature            1 121768802 1357033795 104751
- functioning_day        1 229113857 1464378850 105418
- hour                  23 701986875 1937251868 107826

Step:  AIC=103914.7
rented_bike_count ~ temperature + hour + functioning_day + humidity + 
    season + rainfall + solar_radiation + holiday + dew_point_temperature

                        Df Sum of Sq        RSS    AIC
+ snowfall               1   1172965 1231682903 103908
<none>                               1232855868 103915
+ visibility             1    141721 1232714147 103916
+ wind_speed             1     15752 1232840116 103917
- temperature            1   1252858 1234108726 103922
- dew_point_temperature  1   2409125 1235264993 103930
- holiday                1   5692831 1238548700 103953
- solar_radiation        1  10275359 1243131227 103985
- humidity               1  18314836 1251170704 104042
- rainfall               1  34564709 1267420577 104155
- season                 3  94736908 1327592776 104557
- functioning_day        1 230297464 1463153332 105413
- hour                  23 704315728 1937171597 107827

Step:  AIC=103908.4
rented_bike_count ~ temperature + hour + functioning_day + humidity + 
    season + rainfall + solar_radiation + holiday + dew_point_temperature + 
    snowfall

                        Df Sum of Sq        RSS    AIC
<none>                               1231682903 103908
+ visibility             1    154303 1231528601 103909
+ wind_speed             1     16316 1231666587 103910
- temperature            1   1134633 1232817536 103914
- snowfall               1   1172965 1232855868 103915
- dew_point_temperature  1   2657341 1234340244 103925
- holiday                1   5549734 1237232637 103946
- solar_radiation        1  10033817 1241716721 103977
- humidity               1  19243708 1250926611 104042
- rainfall               1  34029648 1265712551 104145
- season                 3  95724751 1327407655 104558
- functioning_day        1 229762878 1461445781 105405
- hour                  23 701524297 1933207200 107811

. . .

Done.

. . .

formula(seoul_step)
rented_bike_count ~ temperature + hour + functioning_day + humidity + 
    season + rainfall + solar_radiation + holiday + dew_point_temperature + 
    snowfall
seoul_step$anova
                      Step  Df   Deviance Resid. Df Resid. Dev      AIC
1                           NA         NA      8759 3643934363 113342.2
2            + temperature  -1 1056904520      8758 2587029843 110343.4
3                   + hour -23  843729089      8735 1743300754 106931.6
4        + functioning_day  -1  186915633      8734 1556385121 105940.1
5               + humidity  -1  171653389      8733 1384731732 104918.4
6                 + season  -3   95589314      8730 1289142418 104297.8
7               + rainfall  -1   39434247      8729 1249708172 104027.6
8        + solar_radiation  -1    8812374      8728 1240895798 103967.7
9                + holiday  -1    5630805      8727 1235264993 103929.8
10 + dew_point_temperature  -1    2409125      8726 1232855868 103914.7
11              + snowfall  -1    1172965      8725 1231682903 103908.4

This is where automated search becomes convenient. We do not want to fit every plausible formula ourselves. But the procedure still makes a consistent decision at each step: a term is either in the model or out.

Penalized Regression Models

Until now, we have taken discrete steps when selecting terms: include a term, or leave it out. Just as moving from categories to numbers gave us a smoother way to represent a variable, there is a smoother approach to building multivariable models: shrinkage.

When shrinkage is applied to regression, we call it a penalised regression model. Rather than immediately removing terms, we can keep the larger model but discourage large coefficient values. Coefficients are pulled towards zero, with the amount of shrinkage controlled by a penalty.

Different penalties let us express different kinds of model structure. We might expect the response to reflect many small influences, or instead expect that only a smaller number of predictors have substantial effects.

Remember that Ordinary Least Squares (OLS) minimises

\[ RSS = \sum_{i=1}^{n}(y_i-\hat y_i)^2. \]

Penalised regression minimises

\[ RSS + \lambda \times \text{penalty}. \]

The two penalties we will use are L2 for ridge and L1 for lasso.

  • \(\lambda = 0\) gives ordinary least squares.
  • Larger \(\lambda\) means a stronger penalty.
  • The intercept is normally left unpenalised.

Ridge regression: L2 penalty

Ridge regression minimises

\[ RSS + \lambda\sum_{j=1}^{p}\beta_j^2. \]

  • Large coefficients are penalised more strongly than small coefficients.
  • As \(\lambda\) increases, the fitted coefficient vector is pulled towards zero.
  • Ridge generally keeps all predictors rather than setting coefficients exactly to zero.

. . .

In matrix form,

\[ \hat{\boldsymbol\beta}_{\text{ridge}} = (X^\top X+\lambda I)^{-1}X^\top y. \]

A useful way to read this is that ridge adds \(\lambda\) to the diagonal of \(X^\top X\). That stabilises the inversion when predictors contain overlapping information.

Lasso regression: L1 penalty

Lasso minimises

\[ RSS + \lambda\sum_{j=1}^{p}|\beta_j|. \]

  • Lasso also shrinks coefficients as \(\lambda\) increases.
  • Unlike ridge, some coefficients can become exactly zero.
  • Lasso can therefore perform shrinkage and variable selection at the same time.

. . .

Ridge and lasso both control coefficient size. The difference is what the penalty allows to happen at zero.

Why standardisation matters

The penalty acts on coefficient size, and coefficient size depends on the units used for the predictor.

Suppose the same rainfall variable is measured in millimetres or metres.

Rainfall units Equivalent coefficient Contribution to an L2 penalty
millimetres 0.002 4e-06
metres 2.000 4e+00

The fitted relationship is identical, but the raw coefficient penalty is not. Without standardisation, changing units can change how strongly a predictor is penalised.

Using glmnet

For a Gaussian response variable, glmnet minimises

\[ \text{Objective}(\beta_0,\boldsymbol\beta) = \frac{RSS}{2n} + \lambda\left[ \frac{1-\alpha}{2}\sum_j\beta_j^2 + \alpha\sum_j|\beta_j| \right]. \]

The first term measures fit, with RSS divided by \(2n\). The bracketed term penalises coefficient size.

Here \(\alpha\) is the mixing parameter, with \(0 \leq \alpha \leq 1\). We choose its value to set the balance between the L2 and L1 parts of the penalty.

  • alpha = 0: the L1 term disappears, giving ridge (L2).
  • alpha = 1: the L2 term disappears, giving lasso (L1).
  • \(0 < \alpha < 1\): elastic net, a mixture of the two. We will not develop elastic net further here.

. . .

Note: glmnet standardises predictors by default when standardize = TRUE.

. . .

The remaining tuning parameter is \(\lambda\), which we choose by cross-validation.

library(glmnet)

X <- model.matrix(rented_bike_count ~ hour + season + holiday + functioning_day +
    temperature + humidity + wind_speed + visibility + dew_point_temperature + solar_radiation +
    rainfall + snowfall, data = seoul_subset)[, -1]

y <- seoul_subset$rented_bike_count

set.seed(42)
foldid <- sample(rep(seq_len(10), length.out = nrow(X)))

Ridge: choosing \(\lambda\)

unlabelled

lambda.min gives the lowest mean cross-validation error.

lambda.1se is the largest \(\lambda\) whose estimated error is within one standard error of that minimum. It gives stronger regularisation while remaining close to the best estimated cross-validation performance.

Note that the intercept is not penalised, but the slopes are. The penalty is applied to the coefficients of the model matrix, so it can cause the intercept to change as the slopes are shrunk.

Ridge: lambda.min versus lambda.1se

Let’s look at both the min and 1se in an example dataset.

ridge_coef_compare <- cbind(lambda.min = as.matrix(coef(seoul_ridge, s = "lambda.min"))[,
    1], lambda.1se = as.matrix(coef(seoul_ridge, s = "lambda.1se"))[, 1])

round(ridge_coef_compare, 3)
                      lambda.min lambda.1se
(Intercept)               22.494     25.913
hour1                   -127.853   -137.617
hour2                   -230.654   -234.233
hour3                   -310.709   -309.547
hour4                   -368.618   -364.096
hour5                   -357.351   -354.308
hour6                   -203.497   -211.891
hour7                     84.164     57.467
hour8                    429.401    383.211
hour9                      0.562     -6.609
hour10                  -210.398   -193.483
hour11                  -214.825   -190.558
hour12                  -175.048   -148.785
hour13                  -172.031   -143.176
hour14                  -166.132   -136.794
hour15                   -86.583    -64.576
hour16                    37.001     47.004
hour17                   292.668    278.585
hour18                   709.671    659.393
hour19                   465.680    426.805
hour20                   394.720    356.573
hour21                   385.548    345.673
hour22                   294.332    259.289
hour23                    73.516     52.597
seasonSpring            -120.309    -83.020
seasonSummer             -98.679    -44.672
seasonWinter            -342.588   -314.342
holidayNo Holiday        110.174    102.283
functioning_dayYes       859.762    773.532
temperature               14.155     12.924
humidity                  -7.285     -6.203
wind_speed                 1.095      2.601
visibility                 0.028      0.044
dew_point_temperature      7.330      6.563
solar_radiation           72.552     64.888
rainfall                 -57.972    -55.675
snowfall                  16.581      5.211

The lambda.1se column uses the stronger penalty, so the coefficient vector is more strongly regularised.

For the rest of this lecture we will use lambda.1se.

For ridge that means stronger shrinkage. For lasso it usually also means fewer non-zero coefficients.

Lasso coefficient paths

unlabelled

Each line is one model-matrix coefficient. The colours group related predictors; several lines can share a colour, especially the hour indicators. Moving to the right means increasing \(\lambda\).

For lasso, coefficients do not just get smaller. Some paths hit zero and stay there. At lambda.1se we use the stronger of the two marked penalties.

coef(seoul_lasso, s = "lambda.1se")
37 x 1 sparse Matrix of class "dgCMatrix"
                         lambda.1se
(Intercept)            -55.05261698
hour1                  -67.04615252
hour2                 -174.41218658
hour3                 -257.76351662
hour4                 -318.12606332
hour5                 -305.09850207
hour6                 -142.94326049
hour7                   90.73816413
hour8                  465.81340699
hour9                   33.12534552
hour10                 -94.56642398
hour11                 -84.28396219
hour12                 -33.43775151
hour13                 -30.79494042
hour14                 -33.63804606
hour15                   .         
hour16                  73.99601783
hour17                 323.42845839
hour18                 744.22563094
hour19                 478.45963889
hour20                 401.08296136
hour21                 393.26739265
hour22                 299.67977064
hour23                  68.59146328
seasonSpring           -90.28970236
seasonSummer           -57.83894556
seasonWinter          -335.82286493
holidayNo Holiday       76.37915944
functioning_dayYes     845.74464940
temperature             21.64353137
humidity                -6.52268074
wind_speed               .         
visibility               0.01082108
dew_point_temperature    .         
solar_radiation         13.14976824
rainfall               -56.58813318
snowfall                 .         

Stepwise regression versus shrinkage

Method Decision being made Typical result
Stepwise search Add or remove whole model terms A smaller selected formula
Ridge, L2 Penalise squared coefficient size Keep predictors but shrink/stabilise the coefficient vector
Lasso, L1 Penalise absolute coefficient size Shrink coefficients and set some exactly to zero
Stepwise and lasso can both produce sparse models, but they do not select the same object. Stepwise works with model terms. glmnet works with columns of the model matrix, so a multi-level factor can contribute several penalised coefficients.

These procedures are also data-dependent. Change the sample slightly and the selected stepwise model or lasso set can change.

Looking ahead

Automated search and shrinkage give us useful ways to work with a larger predictor set, but they expose another problem.

When predictors carry nearly the same information, ordinary least-squares coefficients can become unstable. A small change in the data or model can produce a large change in the separate coefficient estimates.

That is the multicollinearity problem we turn to next.

LS0tDQp0aXRsZTogIkxlY3R1cmUgMjg6IFN0ZXB3aXNlIGFuZCBQZW5hbGlzZWQgUmVncmVzc2lvbiINCnN1YnRpdGxlOiAxNjEuMjUxIFJlZ3Jlc3Npb24gTW9kZWxsaW5nDQphdXRob3I6ICJQcmVzZW50ZWQgYnkgTmljayBLbm93bHRvbiA8Ti5Lbm93bHRvbkBtYXNzZXkuYWMubno+IiAgDQpkYXRlOiAiV2VlayAxMCBvZiBTZW1lc3RlciAyLCBgciBsdWJyaWRhdGU6OnllYXIobHVicmlkYXRlOjpub3coKSlgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogeWV0aQ0KICAgIGhpZ2hsaWdodF9zdHlsZTogcHlnbWVudHMNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHRfc3R5bGU6IHB5Z21lbnRzDQogIGlvc2xpZGVzX3ByZXNlbnRhdGlvbjoNCiAgICB3aWRlc2NyZWVuOiB0cnVlDQogICAgc21hbGxlcjogdHJ1ZQ0KICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0DQogIHNsaWR5X3ByZXNlbnRhdGlvbjogDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHRfc3R5bGU6IHB5Z21lbnRzDQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KLS0tDQoNCg0KDQoNCjwhLS0tIERhdGEgaXMgb24NCmh0dHBzOi8vci1yZXNvdXJjZXMubWFzc2V5LmFjLm56L2RhdGEvMTYxMjUxLw0KLS0tPg0KDQpgYGB7ciBzZXR1cCwgcHVybD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkoa25pdHIpDQpvcHRzX2NodW5rJHNldChkZXY9YygicG5nIiwgInBkZiIpKQ0Kb3B0c19jaHVuayRzZXQoZmlnLmhlaWdodD02LCBmaWcud2lkdGg9NywgZmlnLnBhdGg9IkZpZ3VyZXMvIiwgZmlnLmFsdD0idW5sYWJlbGxlZCIpDQpvcHRzX2NodW5rJHNldChjb21tZW50PSIiLCBmaWcuYWxpZ249ImNlbnRlciIsIHRpZHk9VFJVRSkNCm9wdGlvbnMoa25pdHIua2FibGUuTkEgPSAnJykNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShicm9vbSkNCmBgYA0KDQoNCjwhLS0tIERvIG5vdCBlZGl0IGFueXRoaW5nIGFib3ZlIHRoaXMgbGluZS4gLS0tPg0KIyMgRnJvbSBtYW51YWwgbW9kZWwgYnVpbGRpbmcgdG8gYXV0b21hdGVkIHNlYXJjaA0KDQpJbiBMZWN0dXJlIDI3LCB3ZSBzdGlsbCBjaG9zZSB0aGUgY2FuZGlkYXRlIG1vZGVscyBvdXJzZWx2ZXMuIE9uY2UgdGhlIG51bWJlciBvZiBwbGF1c2libGUgcHJlZGljdG9ycyBncm93cywgdGhlcmUgYXJlIHR3byBicm9hZCB3YXlzIHRvIHN0b3AgZG9pbmcgYWxsIG9mIHRoYXQgYnkgaGFuZC4NCg0KIVtTdGVwd2lzZSBzZWFyY2hdKGByIGlmIChrbml0cjo6aXNfbGF0ZXhfb3V0cHV0KCkpICIuLi9yZXNvdXJjZXMvbGVjdHVyZTI4X3N0ZXB3aXNlLnBuZyIgZWxzZSAiLi4vZ3JhcGhpY3Mvc3RlcHdpc2Vfc2VhcmNoLnN2ZyJgKXsuaW5jcmVtZW50YWwgd2lkdGg9IjQzJSJ9ICFbUmlkZ2UgYW5kIGxhc3NvIHNocmlua2FnZV0oYHIgaWYgKGtuaXRyOjppc19sYXRleF9vdXRwdXQoKSkgIi4uL3Jlc291cmNlcy9sZWN0dXJlMjhfcGVuYWxpc2VkLnBuZyIgZWxzZSAiLi4vZ3JhcGhpY3MvcGVuYWxpc2VkX3JlZ3Jlc3Npb24uc3ZnImApey5pbmNyZW1lbnRhbCB3aWR0aD0iNDMlIn0NCg0KV2Ugd2lsbCB1c2UgYm90aCBhcHByb2FjaGVzIGluIHRoaXMgbGVjdHVyZS4NCg0KIyMgRm9yd2FyZCwgYmFja3dhcmQsIGFuZCBzdGVwd2lzZSBzZWFyY2gNCg0KVGhlIGJhc2ljIGlkZWEgaXMgc2ltcGxlLg0KDQo6OjogaW5jcmVtZW50YWwNCi0gKipGb3J3YXJkIHNlbGVjdGlvbioqIHN0YXJ0cyBzbWFsbCBhbmQgYWRkcyBvbmUgdGVybSBhdCBhIHRpbWUuDQotICoqQmFja3dhcmQgZWxpbWluYXRpb24qKiBzdGFydHMgbGFyZ2UgYW5kIHJlbW92ZXMgb25lIHRlcm0gYXQgYSB0aW1lLg0KLSAqKlN0ZXB3aXNlIHNlbGVjdGlvbioqIGFsbG93cyBtb3ZlcyBpbiBlaXRoZXIgZGlyZWN0aW9uLg0KOjo6DQoNCi4gLiAuDQoNClRoZSB1bml0IGJlaW5nIGFkZGVkIG9yIHJlbW92ZWQgaXMgYSAqKm1vZGVsIHRlcm0qKiwgbm90IGFuIGFyYml0cmFyeSBjb2VmZmljaWVudC4gQSBmYWN0b3IgY2FuIGdlbmVyYXRlIHNldmVyYWwgY29lZmZpY2llbnRzIGFuZCBzdGlsbCBjb3VudCBhcyBvbmUgdGVybS4NCg0KRm9yIGEgcHJlc3BlY2lmaWVkIG5lc3RlZCBjb21wYXJpc29uLCBhIHBhcnRpYWwgJEYkIHRlc3QgaXMgc3RpbGwgdXNlZnVsLiBGb3IgYSBsYXJnZXIgYXV0b21hdGVkIHNlYXJjaCwgd2UgbmVlZCBhIGNyaXRlcmlvbiB0aGF0IGNhbiBiZSBldmFsdWF0ZWQgcmVwZWF0ZWRseS4gSGVyZSB3ZSB3aWxsIHVzZSAqKkFJQyoqLCBpbnRyb2R1Y2VkIGluIExlY3R1cmUgMjcuDQoNCkluIHByYWN0aWNlLCBgc3RlcCgpYCBsb29rcyBhdCBtb2RlbHMgb25lIG1vdmUgYXdheSBmcm9tIHRoZSBjdXJyZW50IG1vZGVsIGFuZCB0YWtlcyBhIG1vdmUgdGhhdCBsb3dlcnMgQUlDLiBUaGF0IG1ha2VzIHRoZSBzZWFyY2ggbG9jYWwsIHNvIHRoZSBwYXRoIGFuZCBzdGFydGluZyBtb2RlbCBjYW4gbWF0dGVyLg0KDQoNCiMjIENsaW1hdGUgZGF0YTogc21hbGwgZW5vdWdoIHRvIHdhdGNoIHRoZSBzZWFyY2gNCg0KVGhlIGNsaW1hdGUgZGF0YSBhcmUgc21hbGwgZW5vdWdoIHRoYXQgd2UgY2FuIGFjdHVhbGx5IHdhdGNoIHRoZSBzZWFyY2ggcHJvY2VzcyBtb3ZlIGZyb20gb25lIG1vZGVsIHRvIHRoZSBuZXh0Lg0KDQpgYGB7ciByZWFkLUNsaW1hdGUtZGF0YS0zMCwgbWVzc2FnZT1GQUxTRX0NCmNsaW1hdGUgPC0gcmVhZC5jc3YoIi4uL2RhdGEvY2xpbWF0ZS5jc3YiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSB8Pg0KICBkcm9wX25hKFN1biwgTGF0LCBMb25nLCBNbkphblRlbXAsIE1uSmx5VGVtcCwgUmFpbiwgSGVpZ2h0LCBTZWEsIE5vcnRoSXNsYW5kKQ0KDQpjbGltYXRlX251bGwgPC0gbG0oU3VuIH4gMSwgZGF0YSA9IGNsaW1hdGUpDQpjbGltYXRlX2Z1bGwgPC0gbG0oDQogIFN1biB+IExhdCArIExvbmcgKyBNbkphblRlbXAgKyBNbkpseVRlbXAgKyBSYWluICsgSGVpZ2h0ICsgU2VhICsgTm9ydGhJc2xhbmQsDQogIGRhdGEgPSBjbGltYXRlDQopDQpjbGltYXRlX3Njb3BlIDwtIGxpc3QoDQogIGxvd2VyID0gZm9ybXVsYShjbGltYXRlX251bGwpLA0KICB1cHBlciA9IGZvcm11bGEoY2xpbWF0ZV9mdWxsKQ0KKQ0KYGBgDQoNCi4gLiAuDQoNClRoZSBmdWxsIG1vZGVsIGZvciBhbm51YWwgc3Vuc2hpbmUgaXMNCg0KLiAuIC4NCg0KJCQNClx0ZXh0e1N1bn0gXHNpbSBcdGV4dHtMYXR9ICsgXHRleHR7TG9uZ30gKyBcdGV4dHtNbkphblRlbXB9ICsNClx0ZXh0e01uSmx5VGVtcH0gKyBcdGV4dHtSYWlufSArIFx0ZXh0e0hlaWdodH0gKyBcdGV4dHtTZWF9ICsNClx0ZXh0e05vcnRoSXNsYW5kfS4NCiQkDQoNCiMjIEZvcndhcmQgc2VsZWN0aW9uDQoNClN0YXJ0IHdpdGggdGhlIGludGVyY2VwdC1vbmx5IG1vZGVsLiBBdCBlYWNoIHN0ZXAsIGFzayB3aGljaCBzaW5nbGUgYWRkaXRpb24gZ2l2ZXMgdGhlIGJlc3QgcmVkdWN0aW9uIGluIEFJQy4gV2Ugd2lsbCBmb2xsb3cgZWFjaCBzdGVwIGFuZCBleGFtaW5lIGhvdyB0aGUgY2FuZGlkYXRlIG1vZGVscyBhcmUgY29uc2lkZXJlZCBieSB0aGUgYWxnb3JpdGhtLg0KDQoNCmBgYHtyIGNsaW1hdGUtZm9yd2FyZC0yOH0NCmNsaW1hdGVfZm9yd2FyZCA8LSBzdGVwKA0KICBjbGltYXRlX251bGwsDQogIHNjb3BlID0gY2xpbWF0ZV9zY29wZSwNCiAgZGlyZWN0aW9uID0gImZvcndhcmQiLA0KICB0cmFjZSA9IDENCikNCmBgYA0KDQouIC4gLg0KDQpGaW5hbGx5IHN0b3BwZWQuTGV0J3MgY2hlY2sgd2hhdCB3ZSBlbmRlZCB1cCB3aXRoIGFnYWluLg0KDQouIC4gLg0KDQpgYGB7ciBjbGltYXRlLWZvcndhcmQtcmVzdWx0LTI4fQ0KZm9ybXVsYShjbGltYXRlX2ZvcndhcmQpDQpBSUMoY2xpbWF0ZV9mb3J3YXJkKQ0KYGBgDQoNCiMjIEJhY2t3YXJkIGVsaW1pbmF0aW9uDQoNClRoaXMgaXMgdGhlIHNhbWUgb3ZlcmFsbCBpZGVhIGFzIGZvcndhcmQgc2VsZWN0aW9uLCBob3dldmVyIHdlIHN0YXJ0IHdpdGggYWxsIHRlcm1zIGluIHRoZSBtb2RlbCBhbmQga2ljayB0ZXJtcyBvdXQgYmFzZWQgb24gdGhlIGJlc3QgcmVkdWN0aW9uIGluIEFJQy4NCg0KDQpgYGB7ciBjbGltYXRlLWJhY2t3YXJkLTI4fQ0KY2xpbWF0ZV9iYWNrd2FyZCA8LSBzdGVwKA0KICBjbGltYXRlX2Z1bGwsDQogIHNjb3BlID0gY2xpbWF0ZV9zY29wZSwNCiAgZGlyZWN0aW9uID0gImJhY2t3YXJkIiwNCiAgdHJhY2UgPSAxDQopDQpgYGANCg0KLiAuIC4NCg0KRG9uZS4NCg0KYGBge3IgY2xpbWF0ZS1iYWNrd2FyZC1yZXN1bHQtMjh9DQpmb3JtdWxhKGNsaW1hdGVfYmFja3dhcmQpDQpBSUMoY2xpbWF0ZV9iYWNrd2FyZCkNCmBgYA0KDQouIC4gLg0KDQpOb3cgd2hlcmUgZGlkIHdlIGVuZCB1cD8gTm90IHRoZSBzYW1lIG1vZGVsIGFzIHRoZSBmb3J3YXJkIHNlbGVjdGlvbi4gTm93IGxldCdzIHRyeSBzdGVwd2lzZSE/DQoNCiMjIFN0ZXB3aXNlIHNlbGVjdGlvbg0KDQpTdGVwd2lzZSBzZWxlY3Rpb24gYWxsb3dzIGJvdGgga2luZHMgb2YgbW92ZS4gV2Ugc3RhcnQgc21hbGwsIGJ1dCBhZnRlciBhZGRpbmcgYSB0ZXJtIHRoZSBhbGdvcml0aG0gY2FuIHJlY29uc2lkZXIgdGVybXMgYWxyZWFkeSBpbiB0aGUgbW9kZWwuDQoNCmBgYHtyIGNsaW1hdGUtYm90aC0yOH0NCmNsaW1hdGVfYm90aCA8LSBzdGVwKA0KICBjbGltYXRlX251bGwsDQogIHNjb3BlID0gY2xpbWF0ZV9zY29wZSwNCiAgZGlyZWN0aW9uID0gImJvdGgiLA0KICB0cmFjZSA9IDENCikNCmBgYA0KDQouIC4gLg0KDQpEb25lLg0KDQouIC4gLg0KDQpgYGB7ciBjbGltYXRlLWJvdGgtcmVzdWx0LTI4fQ0KZm9ybXVsYShjbGltYXRlX2JvdGgpDQpBSUMoY2xpbWF0ZV9ib3RoKQ0KYGBgDQoNCkRvZXMgdGhlIGJpZGlyZWN0aW9uYWwgc2VhcmNoIGZpbmlzaCBhdCB0aGUgZm9yd2FyZCBtb2RlbCwgdGhlIGJhY2t3YXJkIG1vZGVsLCBvciBzb21ld2hlcmUgZWxzZT8NCg0KIyMgQ29tcGFyaW5nIHRoZSBjbGltYXRlIHNlYXJjaGVzDQoNCmBgYHtyIGNsaW1hdGUtc2VhcmNoLWNvbXBhcmlzb24tMjgsIGVjaG89RkFMU0V9DQptb2RlbF9mb3JtdWxhIDwtIGZ1bmN0aW9uKG1vZGVsKSBwYXN0ZShkZXBhcnNlKGZvcm11bGEobW9kZWwpKSwgY29sbGFwc2UgPSAiICIpDQoNCmNsaW1hdGVfc2VhcmNoZXMgPC0gdGliYmxlKA0KICBTZWFyY2ggPSBjKCJGb3J3YXJkIiwgIkJhY2t3YXJkIiwgIlN0ZXB3aXNlOiBib3RoIiksDQogIGBGaW5hbCBmb3JtdWxhYCA9IGMoDQogICAgbW9kZWxfZm9ybXVsYShjbGltYXRlX2ZvcndhcmQpLA0KICAgIG1vZGVsX2Zvcm11bGEoY2xpbWF0ZV9iYWNrd2FyZCksDQogICAgbW9kZWxfZm9ybXVsYShjbGltYXRlX2JvdGgpDQogICksDQogIEFJQyA9IGMoQUlDKGNsaW1hdGVfZm9yd2FyZCksIEFJQyhjbGltYXRlX2JhY2t3YXJkKSwgQUlDKGNsaW1hdGVfYm90aCkpDQopDQoNCmtuaXRyOjprYWJsZShjbGltYXRlX3NlYXJjaGVzLCBkaWdpdHMgPSAyKQ0KYGBgDQoNCnwgIHwgIHwNCnw6LS0tfDotLS18DQp8ICFbXShgciBpZiAoa25pdHI6OmlzX2xhdGV4X291dHB1dCgpKSAiLi4vcmVzb3VyY2VzL2xlY3R1cmUyOF9zdGVwd2lzZS5wbmciIGVsc2UgIi4uL2dyYXBoaWNzL3N0ZXB3aXNlX3NlYXJjaC5zdmciYCl7d2lkdGg9IjEwMCUifSB8IFRoZSBsb3dlciBBSUMgaXMgcHJlZmVycmVkIGFtb25nIHRoZSBtb2RlbHMgYWN0dWFsbHkgcmVhY2hlZCBieSB0aGUgc2VhcmNoLiBEaWZmZXJlbnQgc3RhcnRpbmcgcG9pbnRzIGNhbiBmaW5pc2ggYXQgZGlmZmVyZW50IGxvY2FsIHNvbHV0aW9ucy4gQWdyZWVtZW50IGJldHdlZW4gc2VhcmNoZXMgaXMgcmVhc3N1cmluZyBmb3IgdGhpcyBkYXRhc2V0LCBidXQgaXQgZG9lcyBub3QgbWFrZSB0aGUgc2VsZWN0ZWQgZm9ybXVsYSB1bmlxdWVseSBjb3JyZWN0LiB8DQoNCiMjIEEgbGFyZ2VyIHByb2JsZW06IFNlb3VsIGJpa2UgZGVtYW5kDQoNClRoZSBTZW91bCBkYXRhIGNvbnRhaW4gaG91cmx5IGJpa2UgcmVudGFscyB0b2dldGhlciB3aXRoIHdlYXRoZXIgYW5kIGNhbGVuZGFyIHZhcmlhYmxlcy4gV2UgaGF2ZSBhbHJlYWR5IHdhdGNoZWQgZm9yd2FyZCwgYmFja3dhcmQgYW5kIGJpZGlyZWN0aW9uYWwgc2VhcmNoIG9uIHRoZSBzbWFsbGVyIGNsaW1hdGUgcHJvYmxlbSwgc28gaGVyZSB3ZSB3aWxsIHVzZSBvbmUgYmlkaXJlY3Rpb25hbCBzZWFyY2ggYW5kIGZvY3VzIG9uIGRlZmluaW5nIHRoZSBtb2RlbCBzcGFjZS4NCg0KYGBge3Igc2VvdWwtc3RlcHdpc2UtcmVhZCwgbWVzc2FnZT1GQUxTRX0NCnNlb3VsX2hvdXJseSA8LSByZWFkX2NzdigiLi4vZGF0YS9zZW91bF9iaWtlX2hvdXJseS5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKSB8Pg0KICBtdXRhdGUoaG91ciA9IGZhY3Rvcihob3VyKSkNCg0Kc2VvdWxfc3Vic2V0IDwtIHNlb3VsX2hvdXJseSB8Pg0KICBzZWxlY3QoDQogICAgcmVudGVkX2Jpa2VfY291bnQsIGhvdXIsIHNlYXNvbiwgaG9saWRheSwgZnVuY3Rpb25pbmdfZGF5LA0KICAgIHRlbXBlcmF0dXJlLCBodW1pZGl0eSwgd2luZF9zcGVlZCwgdmlzaWJpbGl0eSwgZGV3X3BvaW50X3RlbXBlcmF0dXJlLA0KICAgIHNvbGFyX3JhZGlhdGlvbiwgcmFpbmZhbGwsIHNub3dmYWxsDQogICkgfD4NCiAgZHJvcF9uYSgpDQpgYGANCg0KTm90ZSB0aGF0IHdlIGFyZSB0cmVhdGluZyBgaG91cmAgcmVjb3JkcyBhcyBhIGZhY3RvciByYXRoZXIgdGhhbiBmb3JjaW5nIGJpa2UgZGVtYW5kIHRvIGNoYW5nZSBsaW5lYXJseSBmcm9tIG1pZG5pZ2h0IHRvIDExIHBtLg0KDQouIC4gLg0KDQpgYGB7ciBzZW91bC1zdGVwd2lzZS1zZXR1cH0NCnNlb3VsX251bGwgPC0gbG0ocmVudGVkX2Jpa2VfY291bnQgfiAxLCBkYXRhID0gc2VvdWxfc3Vic2V0KQ0KDQpzZW91bF9mdWxsIDwtIGxtKA0KICByZW50ZWRfYmlrZV9jb3VudCB+IGhvdXIgKyBzZWFzb24gKyBob2xpZGF5ICsgZnVuY3Rpb25pbmdfZGF5ICsNCiAgICB0ZW1wZXJhdHVyZSArIGh1bWlkaXR5ICsgd2luZF9zcGVlZCArIHZpc2liaWxpdHkgKw0KICAgIGRld19wb2ludF90ZW1wZXJhdHVyZSArIHNvbGFyX3JhZGlhdGlvbiArIHJhaW5mYWxsICsgc25vd2ZhbGwsDQogIGRhdGEgPSBzZW91bF9zdWJzZXQNCikNCg0Kc2VvdWxfc2NvcGUgPC0gbGlzdCgNCiAgbG93ZXIgPSBmb3JtdWxhKHNlb3VsX251bGwpLA0KICB1cHBlciA9IGZvcm11bGEoc2VvdWxfZnVsbCkNCikNCg0Kc2VvdWxfc2NvcGUNCmBgYA0KDQpUaGUgUiB0ZXJtIGBzY29wZWAgdGVsbHMgYHN0ZXAoKWAgdGhlIHNtYWxsZXN0IGFuZCBsYXJnZXN0IGZvcm11bGFzIGl0IGlzIGFsbG93ZWQgdG8gY29uc2lkZXIuIFRoaXMgaXMgaGFuZHkgaWYgeW91IGFsd2F5cyB3YW50IGEgY2VydGFpbiB0ZXJtIGluIHRoZSBtb2RlbC4NCg0KIyMgU3RlcHdpc2Ugc2VhcmNoIG9uIHRoZSBTZW91bCBkYXRhDQoNCg0KYGBge3Igc2VvdWwtc3RlcC1wcmV2aWV3fQ0Kc2VvdWxfc3RlcCA8LSBzdGVwKA0KICBzZW91bF9udWxsLA0KICBzY29wZSA9IHNlb3VsX3Njb3BlLA0KICBkaXJlY3Rpb24gPSAiYm90aCIsDQogIHRyYWNlID0gMQ0KKQ0KYGBgDQoNCi4gLiAuDQoNCkRvbmUuDQoNCi4gLiAuDQoNCmBgYHtyIHNlb3VsLXN0ZXAtcmVzdWx0LTI4fQ0KZm9ybXVsYShzZW91bF9zdGVwKQ0Kc2VvdWxfc3RlcCRhbm92YQ0KYGBgDQoNClRoaXMgaXMgd2hlcmUgYXV0b21hdGVkIHNlYXJjaCBiZWNvbWVzIGNvbnZlbmllbnQuIFdlIGRvIG5vdCB3YW50IHRvIGZpdCBldmVyeSBwbGF1c2libGUgZm9ybXVsYSBvdXJzZWx2ZXMuIEJ1dCB0aGUgcHJvY2VkdXJlIHN0aWxsIG1ha2VzIGEgY29uc2lzdGVudCBkZWNpc2lvbiBhdCBlYWNoIHN0ZXA6IGEgdGVybSBpcyBlaXRoZXIgaW4gdGhlIG1vZGVsIG9yIG91dC4NCg0KIyMgUGVuYWxpemVkIFJlZ3Jlc3Npb24gTW9kZWxzDQoNClVudGlsIG5vdywgd2UgaGF2ZSB0YWtlbiBkaXNjcmV0ZSBzdGVwcyB3aGVuIHNlbGVjdGluZyB0ZXJtczogaW5jbHVkZSBhIHRlcm0sIG9yIGxlYXZlIGl0IG91dC4gSnVzdCBhcyBtb3ZpbmcgZnJvbSBjYXRlZ29yaWVzIHRvIG51bWJlcnMgZ2F2ZSB1cyBhIHNtb290aGVyIHdheSB0byByZXByZXNlbnQgYSB2YXJpYWJsZSwgdGhlcmUgaXMgYSBzbW9vdGhlciBhcHByb2FjaCB0byBidWlsZGluZyBtdWx0aXZhcmlhYmxlIG1vZGVsczogc2hyaW5rYWdlLg0KDQpXaGVuIHNocmlua2FnZSBpcyBhcHBsaWVkIHRvIHJlZ3Jlc3Npb24sIHdlIGNhbGwgaXQgYSBwZW5hbGlzZWQgcmVncmVzc2lvbiBtb2RlbC4gUmF0aGVyIHRoYW4gaW1tZWRpYXRlbHkgcmVtb3ZpbmcgdGVybXMsIHdlIGNhbiBrZWVwIHRoZSBsYXJnZXIgbW9kZWwgYnV0IGRpc2NvdXJhZ2UgbGFyZ2UgY29lZmZpY2llbnQgdmFsdWVzLiBDb2VmZmljaWVudHMgYXJlIHB1bGxlZCB0b3dhcmRzIHplcm8sIHdpdGggdGhlIGFtb3VudCBvZiBzaHJpbmthZ2UgY29udHJvbGxlZCBieSBhIHBlbmFsdHkuDQoNCkRpZmZlcmVudCBwZW5hbHRpZXMgbGV0IHVzIGV4cHJlc3MgZGlmZmVyZW50IGtpbmRzIG9mIG1vZGVsIHN0cnVjdHVyZS4gV2UgbWlnaHQgZXhwZWN0IHRoZSByZXNwb25zZSB0byByZWZsZWN0IG1hbnkgc21hbGwgaW5mbHVlbmNlcywgb3IgaW5zdGVhZCBleHBlY3QgdGhhdCBvbmx5IGEgc21hbGxlciBudW1iZXIgb2YgcHJlZGljdG9ycyBoYXZlIHN1YnN0YW50aWFsIGVmZmVjdHMuDQoNClJlbWVtYmVyIHRoYXQgT3JkaW5hcnkgTGVhc3QgU3F1YXJlcyAoT0xTKSBtaW5pbWlzZXMNCg0KJCQNClJTUyA9IFxzdW1fe2k9MX1ee259KHlfaS1caGF0IHlfaSleMi4NCiQkDQoNClBlbmFsaXNlZCByZWdyZXNzaW9uIG1pbmltaXNlcw0KDQokJA0KUlNTICsgXGxhbWJkYSBcdGltZXMgXHRleHR7cGVuYWx0eX0uDQokJA0KDQoNClRoZSB0d28gcGVuYWx0aWVzIHdlIHdpbGwgdXNlIGFyZSAqKkwyKiogZm9yIHJpZGdlIGFuZCAqKkwxKiogZm9yIGxhc3NvLg0KDQo6OjogaW5jcmVtZW50YWwNCi0gJFxsYW1iZGEgPSAwJCBnaXZlcyBvcmRpbmFyeSBsZWFzdCBzcXVhcmVzLg0KLSBMYXJnZXIgJFxsYW1iZGEkIG1lYW5zIGEgc3Ryb25nZXIgcGVuYWx0eS4NCi0gVGhlIGludGVyY2VwdCBpcyBub3JtYWxseSBsZWZ0IHVucGVuYWxpc2VkLg0KOjo6DQoNCiMjIFJpZGdlIHJlZ3Jlc3Npb246IEwyIHBlbmFsdHkNCg0KUmlkZ2UgcmVncmVzc2lvbiBtaW5pbWlzZXMNCg0KJCQNClJTUyArIFxsYW1iZGFcc3VtX3tqPTF9XntwfVxiZXRhX2peMi4NCiQkDQoNCjo6OiBpbmNyZW1lbnRhbA0KLSBMYXJnZSBjb2VmZmljaWVudHMgYXJlIHBlbmFsaXNlZCBtb3JlIHN0cm9uZ2x5IHRoYW4gc21hbGwgY29lZmZpY2llbnRzLg0KLSBBcyAkXGxhbWJkYSQgaW5jcmVhc2VzLCB0aGUgZml0dGVkIGNvZWZmaWNpZW50IHZlY3RvciBpcyBwdWxsZWQgdG93YXJkcyB6ZXJvLg0KLSBSaWRnZSBnZW5lcmFsbHkga2VlcHMgYWxsIHByZWRpY3RvcnMgcmF0aGVyIHRoYW4gc2V0dGluZyBjb2VmZmljaWVudHMgZXhhY3RseSB0byB6ZXJvLg0KOjo6DQoNCi4gLiAuDQoNCkluIG1hdHJpeCBmb3JtLA0KDQokJA0KXGhhdHtcYm9sZHN5bWJvbFxiZXRhfV97XHRleHR7cmlkZ2V9fQ0KPQ0KKFheXHRvcCBYK1xsYW1iZGEgSSleey0xfVheXHRvcCB5Lg0KJCQNCg0KQSB1c2VmdWwgd2F5IHRvIHJlYWQgdGhpcyBpcyB0aGF0IHJpZGdlIGFkZHMgJFxsYW1iZGEkIHRvIHRoZSBkaWFnb25hbCBvZiAkWF5cdG9wIFgkLiBUaGF0IHN0YWJpbGlzZXMgdGhlIGludmVyc2lvbiB3aGVuIHByZWRpY3RvcnMgY29udGFpbiBvdmVybGFwcGluZyBpbmZvcm1hdGlvbi4NCg0KIyMgTGFzc28gcmVncmVzc2lvbjogTDEgcGVuYWx0eQ0KDQpMYXNzbyBtaW5pbWlzZXMNCg0KJCQNClJTUyArIFxsYW1iZGFcc3VtX3tqPTF9XntwfXxcYmV0YV9qfC4NCiQkDQoNCjo6OiBpbmNyZW1lbnRhbA0KLSBMYXNzbyBhbHNvIHNocmlua3MgY29lZmZpY2llbnRzIGFzICRcbGFtYmRhJCBpbmNyZWFzZXMuDQotIFVubGlrZSByaWRnZSwgc29tZSBjb2VmZmljaWVudHMgY2FuIGJlY29tZSAqKmV4YWN0bHkgemVybyoqLg0KLSBMYXNzbyBjYW4gdGhlcmVmb3JlIHBlcmZvcm0gc2hyaW5rYWdlIGFuZCB2YXJpYWJsZSBzZWxlY3Rpb24gYXQgdGhlIHNhbWUgdGltZS4NCjo6Og0KDQouIC4gLg0KDQpSaWRnZSBhbmQgbGFzc28gYm90aCBjb250cm9sIGNvZWZmaWNpZW50IHNpemUuIFRoZSBkaWZmZXJlbmNlIGlzIHdoYXQgdGhlIHBlbmFsdHkgYWxsb3dzIHRvIGhhcHBlbiBhdCB6ZXJvLg0KDQojIyBXaHkgc3RhbmRhcmRpc2F0aW9uIG1hdHRlcnMNCg0KVGhlIHBlbmFsdHkgYWN0cyBvbiBjb2VmZmljaWVudCBzaXplLCBhbmQgY29lZmZpY2llbnQgc2l6ZSBkZXBlbmRzIG9uIHRoZSB1bml0cyB1c2VkIGZvciB0aGUgcHJlZGljdG9yLg0KDQpTdXBwb3NlIHRoZSBzYW1lIHJhaW5mYWxsIHZhcmlhYmxlIGlzIG1lYXN1cmVkIGluIG1pbGxpbWV0cmVzIG9yIG1ldHJlcy4NCg0KYGBge3Igc3RhbmRhcmRpc2F0aW9uLXVuaXRzLTI4LCBlY2hvPUZBTFNFfQ0KdW5pdF9leGFtcGxlIDwtIHRpYmJsZSgNCiAgVW5pdHMgPSBjKCJtaWxsaW1ldHJlcyIsICJtZXRyZXMiKSwNCiAgQ29lZmZpY2llbnQgPSBjKDAuMDAyLCAyKSwNCiAgYFNxdWFyZWQgY29lZmZpY2llbnRgID0gYygwLjAwMl4yLCAyXjIpDQopDQoNCmtuaXRyOjprYWJsZSgNCiAgdW5pdF9leGFtcGxlLA0KICBkaWdpdHMgPSBjKDAsIDMsIDYpLA0KICBjb2wubmFtZXMgPSBjKA0KICAgICJSYWluZmFsbCB1bml0cyIsDQogICAgIkVxdWl2YWxlbnQgY29lZmZpY2llbnQiLA0KICAgICJDb250cmlidXRpb24gdG8gYW4gTDIgcGVuYWx0eSINCiAgKQ0KKQ0KYGBgDQoNClRoZSBmaXR0ZWQgcmVsYXRpb25zaGlwIGlzIGlkZW50aWNhbCwgYnV0IHRoZSByYXcgY29lZmZpY2llbnQgcGVuYWx0eSBpcyBub3QuIFdpdGhvdXQgc3RhbmRhcmRpc2F0aW9uLCBjaGFuZ2luZyB1bml0cyBjYW4gY2hhbmdlIGhvdyBzdHJvbmdseSBhIHByZWRpY3RvciBpcyBwZW5hbGlzZWQuDQoNCg0KIyMgVXNpbmcgYGdsbW5ldGANCg0KRm9yIGEgR2F1c3NpYW4gcmVzcG9uc2UgdmFyaWFibGUsIGBnbG1uZXRgIG1pbmltaXNlcw0KDQokJA0KXHRleHR7T2JqZWN0aXZlfShcYmV0YV8wLFxib2xkc3ltYm9sXGJldGEpDQo9IFxmcmFje1JTU317Mm59ICsgXGxhbWJkYVxsZWZ0Ww0KXGZyYWN7MS1cYWxwaGF9ezJ9XHN1bV9qXGJldGFfal4yDQorDQpcYWxwaGFcc3VtX2p8XGJldGFfanwNClxyaWdodF0uDQokJA0KDQpUaGUgZmlyc3QgdGVybSBtZWFzdXJlcyBmaXQsIHdpdGggUlNTIGRpdmlkZWQgYnkgJDJuJC4gVGhlIGJyYWNrZXRlZCB0ZXJtIHBlbmFsaXNlcyBjb2VmZmljaWVudCBzaXplLg0KDQpIZXJlICRcYWxwaGEkIGlzIHRoZSBtaXhpbmcgcGFyYW1ldGVyLCB3aXRoICQwIFxsZXEgXGFscGhhIFxsZXEgMSQuIFdlIGNob29zZSBpdHMgdmFsdWUgdG8gc2V0IHRoZSBiYWxhbmNlIGJldHdlZW4gdGhlIEwyIGFuZCBMMSBwYXJ0cyBvZiB0aGUgcGVuYWx0eS4NCg0KOjo6IGluY3JlbWVudGFsDQotIGBhbHBoYSA9IDBgOiB0aGUgTDEgdGVybSBkaXNhcHBlYXJzLCBnaXZpbmcgcmlkZ2UgKCoqTDIqKikuDQotIGBhbHBoYSA9IDFgOiB0aGUgTDIgdGVybSBkaXNhcHBlYXJzLCBnaXZpbmcgbGFzc28gKCoqTDEqKikuDQotICQwIDwgXGFscGhhIDwgMSQ6IGVsYXN0aWMgbmV0LCBhIG1peHR1cmUgb2YgdGhlIHR3by4gV2Ugd2lsbCBub3QgZGV2ZWxvcCBlbGFzdGljIG5ldCBmdXJ0aGVyIGhlcmUuDQo6OjoNCg0KLiAuIC4NCg0KTm90ZTogYGdsbW5ldGAgc3RhbmRhcmRpc2VzIHByZWRpY3RvcnMgYnkgZGVmYXVsdCB3aGVuIGBzdGFuZGFyZGl6ZSA9IFRSVUVgLg0KDQouIC4gLg0KDQpUaGUgcmVtYWluaW5nIHR1bmluZyBwYXJhbWV0ZXIgaXMgJFxsYW1iZGEkLCB3aGljaCB3ZSBjaG9vc2UgYnkgY3Jvc3MtdmFsaWRhdGlvbi4NCg0KYGBge3Igc2VvdWwtcGVuYWxpc2VkLXNldHVwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShnbG1uZXQpDQoNClggPC0gbW9kZWwubWF0cml4KA0KICByZW50ZWRfYmlrZV9jb3VudCB+IGhvdXIgKyBzZWFzb24gKyBob2xpZGF5ICsgZnVuY3Rpb25pbmdfZGF5ICsNCiAgICB0ZW1wZXJhdHVyZSArIGh1bWlkaXR5ICsgd2luZF9zcGVlZCArIHZpc2liaWxpdHkgKw0KICAgIGRld19wb2ludF90ZW1wZXJhdHVyZSArIHNvbGFyX3JhZGlhdGlvbiArIHJhaW5mYWxsICsgc25vd2ZhbGwsDQogIGRhdGEgPSBzZW91bF9zdWJzZXQNCilbLCAtMV0NCg0KeSA8LSBzZW91bF9zdWJzZXQkcmVudGVkX2Jpa2VfY291bnQNCg0Kc2V0LnNlZWQoNDIpDQpmb2xkaWQgPC0gc2FtcGxlKHJlcChzZXFfbGVuKDEwKSwgbGVuZ3RoLm91dCA9IG5yb3coWCkpKQ0KYGBgDQoNCiMjIFJpZGdlOiBjaG9vc2luZyAkXGxhbWJkYSQNCg0KYGBge3Igc2VvdWwtcmlkZ2UtZml0LTI4LCBpbmNsdWRlPUZBTFNFfQ0Kc2VvdWxfcmlkZ2UgPC0gY3YuZ2xtbmV0KA0KICB4ID0gWCwNCiAgeSA9IHksDQogIGFscGhhID0gMCwNCiAgZm9sZGlkID0gZm9sZGlkLA0KICBzdGFuZGFyZGl6ZSA9IFRSVUUNCikNCg0KcmlkZ2VfY3YgPC0gdGliYmxlKA0KICBsb2dfbGFtYmRhID0gbG9nKHNlb3VsX3JpZGdlJGxhbWJkYSksDQogIGN2X2Vycm9yID0gc2VvdWxfcmlkZ2UkY3ZtLA0KICBjdl9zZSA9IHNlb3VsX3JpZGdlJGN2c2QNCikNCmBgYA0KDQpgYGB7ciBzZW91bC1yaWRnZS1jdi1wbG90LTI4LCBlY2hvPUZBTFNFLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD01fQ0KZ2dwbG90KHJpZGdlX2N2LCBhZXMoeCA9IGxvZ19sYW1iZGEsIHkgPSBjdl9lcnJvcikpICsNCiAgZ2VvbV9lcnJvcmJhcigNCiAgICBhZXMoeW1pbiA9IGN2X2Vycm9yIC0gY3Zfc2UsIHltYXggPSBjdl9lcnJvciArIGN2X3NlKSwNCiAgICB3aWR0aCA9IDAsIGFscGhhID0gMC4zNQ0KICApICsNCiAgZ2VvbV9saW5lKCkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAxLjQpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbG9nKHNlb3VsX3JpZGdlJGxhbWJkYS5taW4pLCBsaW5ldHlwZSA9IDIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbG9nKHNlb3VsX3JpZGdlJGxhbWJkYS4xc2UpLCBsaW5ldHlwZSA9IDMpICsNCiAgYW5ub3RhdGUoDQogICAgInRleHQiLCB4ID0gbG9nKHNlb3VsX3JpZGdlJGxhbWJkYS5taW4pLA0KICAgIHkgPSBtYXgocmlkZ2VfY3YkY3ZfZXJyb3IpLCBsYWJlbCA9ICJsYW1iZGEubWluIiwNCiAgICBhbmdsZSA9IDkwLCB2anVzdCA9IC0wLjQsIGhqdXN0ID0gMQ0KICApICsNCiAgYW5ub3RhdGUoDQogICAgInRleHQiLCB4ID0gbG9nKHNlb3VsX3JpZGdlJGxhbWJkYS4xc2UpLA0KICAgIHkgPSBtYXgocmlkZ2VfY3YkY3ZfZXJyb3IpLCBsYWJlbCA9ICJsYW1iZGEuMXNlIiwNCiAgICBhbmdsZSA9IDkwLCB2anVzdCA9IDEuMiwgaGp1c3QgPSAxDQogICkgKw0KICBsYWJzKA0KICAgIHggPSBleHByZXNzaW9uKGxvZyhsYW1iZGEpKSwNCiAgICB5ID0gIkNyb3NzLXZhbGlkYXRlZCBtZWFuIHNxdWFyZWQgZXJyb3IiDQogICkgKw0KICB0aGVtZV9idyhiYXNlX3NpemUgPSAxMykNCmBgYA0KDQpgbGFtYmRhLm1pbmAgZ2l2ZXMgdGhlIGxvd2VzdCBtZWFuIGNyb3NzLXZhbGlkYXRpb24gZXJyb3IuDQoNCmBsYW1iZGEuMXNlYCBpcyB0aGUgKipsYXJnZXN0KiogJFxsYW1iZGEkIHdob3NlIGVzdGltYXRlZCBlcnJvciBpcyB3aXRoaW4gb25lIHN0YW5kYXJkIGVycm9yIG9mIHRoYXQgbWluaW11bS4gSXQgZ2l2ZXMgc3Ryb25nZXIgcmVndWxhcmlzYXRpb24gd2hpbGUgcmVtYWluaW5nIGNsb3NlIHRvIHRoZSBiZXN0IGVzdGltYXRlZCBjcm9zcy12YWxpZGF0aW9uIHBlcmZvcm1hbmNlLg0KDQpOb3RlIHRoYXQgdGhlIGludGVyY2VwdCBpcyBub3QgcGVuYWxpc2VkLCBidXQgdGhlIHNsb3BlcyBhcmUuIFRoZSBwZW5hbHR5IGlzIGFwcGxpZWQgdG8gdGhlIGNvZWZmaWNpZW50cyBvZiB0aGUgbW9kZWwgbWF0cml4LCBzbyBpdCBjYW4gY2F1c2UgdGhlIGludGVyY2VwdCB0byBjaGFuZ2UgYXMgdGhlIHNsb3BlcyBhcmUgc2hydW5rLg0KDQojIyBSaWRnZTogYGxhbWJkYS5taW5gIHZlcnN1cyBgbGFtYmRhLjFzZWANCg0KTGV0J3MgbG9vayBhdCBib3RoIHRoZSBtaW4gYW5kIDFzZSBpbiBhbiBleGFtcGxlIGRhdGFzZXQuDQoNCmBgYHtyIHNlb3VsLXJpZGdlLXN1bW1hcnktMjh9DQpyaWRnZV9jb2VmX2NvbXBhcmUgPC0gY2JpbmQoDQogIGBsYW1iZGEubWluYCA9IGFzLm1hdHJpeChjb2VmKHNlb3VsX3JpZGdlLCBzID0gImxhbWJkYS5taW4iKSlbLCAxXSwNCiAgYGxhbWJkYS4xc2VgID0gYXMubWF0cml4KGNvZWYoc2VvdWxfcmlkZ2UsIHMgPSAibGFtYmRhLjFzZSIpKVssIDFdDQopDQoNCnJvdW5kKHJpZGdlX2NvZWZfY29tcGFyZSwgMykNCmBgYA0KDQpUaGUgYGxhbWJkYS4xc2VgIGNvbHVtbiB1c2VzIHRoZSBzdHJvbmdlciBwZW5hbHR5LCBzbyB0aGUgY29lZmZpY2llbnQgdmVjdG9yIGlzIG1vcmUgc3Ryb25nbHkgcmVndWxhcmlzZWQuDQoNCioqRm9yIHRoZSByZXN0IG9mIHRoaXMgbGVjdHVyZSB3ZSB3aWxsIHVzZSBgbGFtYmRhLjFzZWAuKioNCg0KRm9yIHJpZGdlIHRoYXQgbWVhbnMgc3Ryb25nZXIgc2hyaW5rYWdlLiBGb3IgbGFzc28gaXQgdXN1YWxseSBhbHNvIG1lYW5zIGZld2VyIG5vbi16ZXJvIGNvZWZmaWNpZW50cy4NCg0KIyMgTGFzc28gY29lZmZpY2llbnQgcGF0aHMNCg0KYGBge3Igc2VvdWwtbGFzc28tZml0LTI4LCBpbmNsdWRlPUZBTFNFfQ0Kc2VvdWxfbGFzc28gPC0gY3YuZ2xtbmV0KA0KICB4ID0gWCwNCiAgeSA9IHksDQogIGFscGhhID0gMSwNCiAgZm9sZGlkID0gZm9sZGlkLA0KICBzdGFuZGFyZGl6ZSA9IFRSVUUNCikNCg0KbGFzc29fYmV0YSA8LSBhcy5kYXRhLmZyYW1lKHQoYXMubWF0cml4KHNlb3VsX2xhc3NvJGdsbW5ldC5maXQkYmV0YSkpKQ0KbGFzc29fYmV0YSRsb2dfbGFtYmRhIDwtIGxvZyhzZW91bF9sYXNzbyRnbG1uZXQuZml0JGxhbWJkYSkNCg0KbGFzc29fcGF0aCA8LSBsYXNzb19iZXRhIHw+DQogIHBpdm90X2xvbmdlcigNCiAgICBjb2xzID0gLWxvZ19sYW1iZGEsDQogICAgbmFtZXNfdG8gPSAidGVybSIsDQogICAgdmFsdWVzX3RvID0gImNvZWZmaWNpZW50Ig0KICApIHw+DQogIG11dGF0ZSgNCiAgICBncm91cCA9IGNhc2Vfd2hlbigNCiAgICAgIGdyZXBsKCJeaG91ciIsIHRlcm0pIHwgdGVybSAlaW4lIGMoImhvbGlkYXlObyBIb2xpZGF5IiwgImZ1bmN0aW9uaW5nX2RheVllcyIpIH4gIlRpbWUgLyBjYWxlbmRhciIsDQogICAgICBncmVwbCgiXnNlYXNvbiIsIHRlcm0pIH4gIlNlYXNvbiIsDQogICAgICB0ZXJtICVpbiUgYygidGVtcGVyYXR1cmUiLCAiaHVtaWRpdHkiLCAid2luZF9zcGVlZCIsICJ2aXNpYmlsaXR5IikgfiAiVGVtcGVyYXR1cmUgLyBhaXIiLA0KICAgICAgdGVybSAlaW4lIGMoInJhaW5mYWxsIiwgInNub3dmYWxsIiwgInNvbGFyX3JhZGlhdGlvbiIsICJkZXdfcG9pbnRfdGVtcGVyYXR1cmUiKSB+ICJSYWluIC8gc25vdyAvIHNvbGFyIC8gZGV3IiwNCiAgICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfDQogICAgKSwNCiAgICBncm91cCA9IGZhY3RvcigNCiAgICAgIGdyb3VwLA0KICAgICAgbGV2ZWxzID0gYygiVGltZSAvIGNhbGVuZGFyIiwgIlNlYXNvbiIsICJUZW1wZXJhdHVyZSAvIGFpciIsICJSYWluIC8gc25vdyAvIHNvbGFyIC8gZGV3IikNCiAgICApDQogICkNCg0Kc3RvcGlmbm90KCFhbnlOQShsYXNzb19wYXRoJGdyb3VwKSkNCmBgYA0KDQpgYGB7ciBzZW91bC1sYXNzby1wYXRoLTI4LCBlY2hvPUZBTFNFLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Niwgb3V0LndpZHRoPSIxMDAlIn0NCmdncGxvdChsYXNzb19wYXRoLCBhZXMoeCA9IGxvZ19sYW1iZGEsIHkgPSBjb2VmZmljaWVudCwgZ3JvdXAgPSB0ZXJtLCBjb2xvdXIgPSBncm91cCkpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAzLCBjb2xvdXIgPSAiZ3JleTU1IikgKw0KICBnZW9tX2xpbmUoYWxwaGEgPSAwLjcyLCBsaW5ld2lkdGggPSAwLjc1KSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGxvZyhzZW91bF9sYXNzbyRsYW1iZGEubWluKSwgbGluZXR5cGUgPSAyLCBjb2xvdXIgPSAiZ3JleTMwIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBsb2coc2VvdWxfbGFzc28kbGFtYmRhLjFzZSksIGxpbmV0eXBlID0gMywgY29sb3VyID0gImdyZXkzMCIpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCgNCiAgICBuYW1lID0gIlByZWRpY3RvciBncm91cCIsDQogICAgdmFsdWVzID0gYygNCiAgICAgICJUaW1lIC8gY2FsZW5kYXIiID0gIiMwMDcyQjIiLA0KICAgICAgIlNlYXNvbiIgPSAiI0Q1NUUwMCIsDQogICAgICAiVGVtcGVyYXR1cmUgLyBhaXIiID0gIiMwMDlFNzMiLA0KICAgICAgIlJhaW4gLyBzbm93IC8gc29sYXIgLyBkZXciID0gIiNDQzc5QTciDQogICAgKSwNCiAgICBsYWJlbHMgPSBjKA0KICAgICAgIkhvdXIsIGhvbGlkYXksIGZ1bmN0aW9uaW5nIGRheSIsDQogICAgICAiU2Vhc29uIGluZGljYXRvcnMiLA0KICAgICAgIlRlbXBlcmF0dXJlLCBodW1pZGl0eSwgd2luZCwgdmlzaWJpbGl0eSIsDQogICAgICAiUmFpbmZhbGwsIHNub3dmYWxsLCBzb2xhciwgZGV3IHBvaW50Ig0KICAgICkNCiAgKSArDQogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChhbHBoYSA9IDEsIGxpbmV3aWR0aCA9IDIpKSkgKw0KICBsYWJzKA0KICAgIHggPSBleHByZXNzaW9uKGxvZyhsYW1iZGEpfiIoc3Ryb25nZXIgcGVuYWx0eSB0byB0aGUgcmlnaHQpIiksDQogICAgeSA9ICJDb2VmZmljaWVudCIsDQogICAgY2FwdGlvbiA9ICJEYXNoZWQgbGluZTogbGFtYmRhLm1pbiAgICBEb3R0ZWQgbGluZTogbGFtYmRhLjFzZSINCiAgKSArDQogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDEzKSArDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsDQogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIpLA0KICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksDQogICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMCkNCiAgKQ0KYGBgDQoNCkVhY2ggbGluZSBpcyBvbmUgbW9kZWwtbWF0cml4IGNvZWZmaWNpZW50LiBUaGUgY29sb3VycyBncm91cCByZWxhdGVkIHByZWRpY3RvcnM7IHNldmVyYWwgbGluZXMgY2FuIHNoYXJlIGEgY29sb3VyLCBlc3BlY2lhbGx5IHRoZSBob3VyIGluZGljYXRvcnMuIE1vdmluZyB0byB0aGUgcmlnaHQgbWVhbnMgaW5jcmVhc2luZyAkXGxhbWJkYSQuDQoNCkZvciBsYXNzbywgY29lZmZpY2llbnRzIGRvIG5vdCBqdXN0IGdldCBzbWFsbGVyLiBTb21lIHBhdGhzIGhpdCB6ZXJvIGFuZCBzdGF5IHRoZXJlLiBBdCBgbGFtYmRhLjFzZWAgd2UgdXNlIHRoZSBzdHJvbmdlciBvZiB0aGUgdHdvIG1hcmtlZCBwZW5hbHRpZXMuDQoNCmBgYHtyIHNlb3VsLWxhc3NvLTFzZS0yOH0NCmNvZWYoc2VvdWxfbGFzc28sIHMgPSAibGFtYmRhLjFzZSIpDQpgYGANCg0KIyMgU3RlcHdpc2UgcmVncmVzc2lvbiB2ZXJzdXMgc2hyaW5rYWdlDQoNCnwgTWV0aG9kIHwgRGVjaXNpb24gYmVpbmcgbWFkZSB8IFR5cGljYWwgcmVzdWx0IHwNCnwtLS18LS0tfC0tLXwNCnwgU3RlcHdpc2Ugc2VhcmNoIHwgQWRkIG9yIHJlbW92ZSB3aG9sZSBtb2RlbCB0ZXJtcyB8IEEgc21hbGxlciBzZWxlY3RlZCBmb3JtdWxhIHwNCnwgUmlkZ2UsIEwyIHwgUGVuYWxpc2Ugc3F1YXJlZCBjb2VmZmljaWVudCBzaXplIHwgS2VlcCBwcmVkaWN0b3JzIGJ1dCBzaHJpbmsvc3RhYmlsaXNlIHRoZSBjb2VmZmljaWVudCB2ZWN0b3IgfA0KfCBMYXNzbywgTDEgfCBQZW5hbGlzZSBhYnNvbHV0ZSBjb2VmZmljaWVudCBzaXplIHwgU2hyaW5rIGNvZWZmaWNpZW50cyBhbmQgc2V0IHNvbWUgZXhhY3RseSB0byB6ZXJvIHwNCg0KfCAgfCAgfA0KfDotLS18Oi0tLXwNCnwgIVtdKGByIGlmIChrbml0cjo6aXNfbGF0ZXhfb3V0cHV0KCkpICIuLi9yZXNvdXJjZXMvbGVjdHVyZTI4X3BlbmFsaXNlZC5wbmciIGVsc2UgIi4uL2dyYXBoaWNzL3BlbmFsaXNlZF9yZWdyZXNzaW9uLnN2ZyJgKXt3aWR0aD0iMTAwJSJ9IHwgU3RlcHdpc2UgYW5kIGxhc3NvIGNhbiBib3RoIHByb2R1Y2Ugc3BhcnNlIG1vZGVscywgYnV0IHRoZXkgZG8gbm90IHNlbGVjdCB0aGUgc2FtZSBvYmplY3QuIFN0ZXB3aXNlIHdvcmtzIHdpdGggKiptb2RlbCB0ZXJtcyoqLiBgZ2xtbmV0YCB3b3JrcyB3aXRoICoqY29sdW1ucyBvZiB0aGUgbW9kZWwgbWF0cml4KiosIHNvIGEgbXVsdGktbGV2ZWwgZmFjdG9yIGNhbiBjb250cmlidXRlIHNldmVyYWwgcGVuYWxpc2VkIGNvZWZmaWNpZW50cy48YnI+PGJyPlRoZXNlIHByb2NlZHVyZXMgYXJlIGFsc28gZGF0YS1kZXBlbmRlbnQuIENoYW5nZSB0aGUgc2FtcGxlIHNsaWdodGx5IGFuZCB0aGUgc2VsZWN0ZWQgc3RlcHdpc2UgbW9kZWwgb3IgbGFzc28gc2V0IGNhbiBjaGFuZ2UuIHwNCg0KIyMgTG9va2luZyBhaGVhZA0KDQpBdXRvbWF0ZWQgc2VhcmNoIGFuZCBzaHJpbmthZ2UgZ2l2ZSB1cyB1c2VmdWwgd2F5cyB0byB3b3JrIHdpdGggYSBsYXJnZXIgcHJlZGljdG9yIHNldCwgYnV0IHRoZXkgZXhwb3NlIGFub3RoZXIgcHJvYmxlbS4NCg0KV2hlbiBwcmVkaWN0b3JzIGNhcnJ5IG5lYXJseSB0aGUgc2FtZSBpbmZvcm1hdGlvbiwgb3JkaW5hcnkgbGVhc3Qtc3F1YXJlcyBjb2VmZmljaWVudHMgY2FuIGJlY29tZSB1bnN0YWJsZS4gQSBzbWFsbCBjaGFuZ2UgaW4gdGhlIGRhdGEgb3IgbW9kZWwgY2FuIHByb2R1Y2UgYSBsYXJnZSBjaGFuZ2UgaW4gdGhlIHNlcGFyYXRlIGNvZWZmaWNpZW50IGVzdGltYXRlcy4NCg0KVGhhdCBpcyB0aGUgKiptdWx0aWNvbGxpbmVhcml0eSoqIHByb2JsZW0gd2UgdHVybiB0byBuZXh0Lg0K