This demonstration uses a small data set to define a context for repeated sampling from a “population” with known slope and intercept to develope the sampling distributions of regression estimates. The data relate to predicting outcome in therapy (Y) from scores on a personality test (X).

We treat the X values as fixed as in the standard regression model. For each X value, generate sample Y’s, according to the model, \[ Y = 14 + 1.2 * X + sigma * N(0,1) \] where

Load packages

library(purrr)
library(broom)
library(dplyr)
library(ggplot2) 
library(car)

Read the data

therapy <- read.table(header=TRUE, text ='
name   sex perstest therapy
John    M 26 32
Susan   F 24 40
Mary    F 22 44
Paul    M 33 44
Jenny   F 27 48
Rick    M 36 52
Cathy   F 30 56
Robert  M 38 56
Lisa    F 30 60
Tina    F 34 68
')

Treat the therapy data as defining a ‘true model’

true_mod <- lm(therapy ~ perstest, data=therapy)

get tidy coefficients

tmod <- tidy(true_mod)
tmod
# A tibble: 2 x 5
  term        estimate std.error statistic p.value
  <chr>          <dbl>     <dbl>     <dbl>   <dbl>
1 (Intercept)    14.      17.2       0.814  0.439 
2 perstest        1.20     0.566     2.12   0.0667

Add lower/upper 95% intervals

upper <- tmod$estimate +  1.96 * tmod$std.error
lower <- tmod$estimate -  1.96 * tmod$std.error
tmod <- cbind(tmod, lower, upper)
tmod
         term estimate std.error statistic p.value     lower  upper
1 (Intercept)     14.0   17.2047    0.8137 0.43934 -19.72112 47.721
2    perstest      1.2    0.5657    2.1213 0.06669   0.09126  2.309

Set up the simulation parameters

x <- therapy$perstest
n <- length(x)

samples <- 1000

# "true" parameters
b0 <- coef(true_mod)[1]   # 14
b1 <- coef(true_mod)[2]   # 1.2
sigma <- summary(true_mod)$sigma  # sqrt(80)
set.seed(1234)       # reproducibility

Generate one sample from the bivariate distribution of (x, y)

For the simulation, it is useful to define some simple functions to do a calculation for a single sample.

Then, one can use replicate(samples, fun()) to repeat over many samples.

one_sample <- function(x, b0=14, b1=1.2, sigma=sqrt(80)) {
    n <- length(x)
    y <- b0 + b1 * x + rnorm(n, mean=0, sd=sigma)
    data.frame(x, y)
}
one_sample(x, b0, b1, sigma)
    x     y
1  26 34.40
2  24 45.28
3  22 50.10
4  33 32.62
5  27 50.24
6  36 61.73
7  30 44.86
8  38 54.71
9  30 44.95
10 34 46.84

Do a regression for one sample

one_reg <- function(x) {
    simdata <- one_sample(x)
    lm(y ~ x, data=simdata)
}
one_reg(x)

Call:
lm(formula = y ~ x, data = simdata)

Coefficients:
(Intercept)            x  
      -1.16         1.67  

Generate the coefficients for all samples

Note that replicate() binds the results as columns of a matrix, but I want them as rows, of a data frame

all_coefs <- function(x, samples=5) {
    res <- replicate(n=samples, coef(one_reg(x)))
    rownames(res) <- c("b0", "b1")
    as.data.frame(t(res))
}

Apply this for all samples

coefs <- all_coefs(x, samples=samples)

head(coefs)   # show first 6
      b0     b1
1 22.584 0.7982
2 20.127 0.7673
3 17.365 0.9060
4  1.213 1.5431
5 15.949 1.3189
6 20.712 0.9637

Plot the sampling distribution of coefs

Plot the density of b1 estimates together with the theoretical normal density with true parameters. Note the use of stat_function() for the normal density (dnorm())

ggplot(coefs,  aes(x = b1) ) +
    geom_density(color="blue", fill = "blue", alpha = .2, size=1.5) +
    geom_vline( xintercept = b1, color="red", size=1.5) + 
    geom_vline(xintercept = lower[2]) +
    geom_vline(xintercept = upper[2]) +
    annotate("text", x=b1, y=0.05, label=paste("beta[1] ==", b1), 
             size=8, parse=TRUE) +
    stat_function(fun = dnorm, args = list(mean = b1, sd = .565)) +
        xlab("Slope (b1)") 

Plot the joint distribution of b0, b1

ggplot(coefs, aes(x=b1, y=b0)) +
            geom_point(color="blue", size=0.9) +
            stat_ellipse(level = 0.68, size=1.2) + 
            stat_ellipse(level = 0.95, size=1.05, linetype=1) +
            geom_point(x=b1, y=b0, color="red", size=4) +
            theme_minimal()

The very strong negative correlation between slope & intercept is perhaps a bit surprising. Here it is in numbers:

cor(coefs)          
        b0      b1
b0  1.0000 -0.9873
b1 -0.9873  1.0000

Plot the slopes & their individual CIs (Fig 4.2)

Add se, CI info to the slope estimate in on sample

one_slope_info <- function(x) {
    simdata <- one_sample(x)
    reg <- lm(y ~ x, data=simdata)
    treg <- tidy(reg)
    b1 <- as.numeric(treg[2,"estimate"])
    se <- as.numeric(treg[2,"std.error"])
    conf <- b1 + c(-2, -.67, .67, 2) * se
    c(b1=b1, se=se, conf)
}
one_slope_info(x)
     b1      se                                 
 0.2750  0.3320 -0.3890  0.0526  0.4975  0.9390 

Do these for all samples

all_slope_info <- function(x, samples=5) {
    res <- replicate(n=samples, one_slope_info(x))
    rownames(res) <- c("b1", "se", "lo95", "lo50", "up50", "up95")
    as.data.frame(t(res))
}
slopes <- all_slope_info(x, samples=samples)
head(slopes)
       b1     se    lo95     lo50   up50   up95
1  0.4860 0.8664 -1.2467 -0.09448 1.0664 2.2187
2  1.5155 0.8138 -0.1121  0.97022 2.0607 3.1430
3  0.8219 0.5789 -0.3359  0.43403 1.2098 1.9797
4  0.9374 0.3625  0.2123  0.69450 1.1803 1.6625
5  0.4011 0.4327 -0.4643  0.11118 0.6909 1.2664
6 -0.2435 0.5364 -1.3162 -0.60290 0.1158 0.8292

Make the plot in the style of Figure 4.2

plot(c(-2, samples+2), range(slopes[,c("lo95", "up95")]), bty="l", 
        xlab="Simulation", ylab="Estimate, 50% & 95% confidence interval", 
        type="n")
abline(b1, 0, col="gray")
for (i in seq(1, samples, by=2)){
  lines(c(i,i), slopes[i,c("lo95", "up95")], lwd=.8, col=gray(.3, alpha=.5))
  lines(c(i,i), slopes[i,c("lo50", "up50")], lwd=2,  col="red")
}
points(1:samples, slopes[,"b1"], pch=20)

Plot individual regression lines to confirm the confidence band

Here I plot the lm regression line for the data with 68 & 95% confidence intervals. Overlaid here are the individual regression lines for the samples.

A crafty ggplot trick allows to use the fitted coefficients as a new data frame input to geom_abline

ggplot(data=therapy, aes(x=perstest, y=therapy)) +
    geom_smooth(method="lm", formula= y~x, level=0.68, alpha=.2) +
    geom_smooth(method="lm", formula= y~x, level=0.95, alpha=.3, size=2) +
    geom_abline(data=coefs, aes(intercept = b0, slope = b1), color=adjustcolor( "red", alpha.f = 0.1)) +
    geom_point(size=3, color="blue")  +
    theme_bw()

Other explorations

What happens if we use 2 confidence levels? Surprise!

ggplot(data=therapy, aes(x=perstest, y=therapy)) +
    geom_point(size=3, color="blue") + 
    geom_smooth(method="lm", formula= y~x, level=c(0.68, .95))

Show three confidence levels

ggplot(data=therapy, aes(x=perstest, y=therapy)) +
            geom_point(size=3, color="blue") + 
            geom_smooth(method="lm", formula= y~x, alpha=.2, level=0.68) +
            geom_smooth(method="lm", formula= y~x, alpha=.2, level=0.90) +
            geom_smooth(method="lm", formula= y~x, alpha=.2, level=0.95) +
            theme_bw()

Different way to do this

In the examples above, I essentially re-ran all of the simulations for each of the the functions used with replicate(). This is somewhat wasteful.

Another way is to generate all the model objects at once, store them in a list.
Can then use tidy functions to extract summaries, do plots, etc.

sims <- replicate(n=samples, one_reg(x), simplify=FALSE)

plot the residual standard errors

sims %>%
     map_dbl(~summary(.x)$sigma) %>%
     data.frame(sigma = .) %>%
     ggplot( aes(x = sigma) ) +
          geom_density(fill = "blue", alpha = .2) +
          geom_vline(xintercept = sqrt(80))

References

IycgLS0tDQojJyB0aXRsZTogIkdlbmVyYXRlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiByZWdyZXNzaW9uIGVzdGltYXRlcyBiMCwgYjEiDQojJyBhdXRob3I6ICJNaWNoYWVsIEZyaWVuZGx5Ig0KIycgZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpKWAiDQojJyBvdXRwdXQ6DQojJyAgIGh0bWxfZG9jdW1lbnQ6DQojJyAgICAgdGhlbWU6IHJlYWRhYmxlDQojJyAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KIycgLS0tDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIycgVGhpcyBkZW1vbnN0cmF0aW9uIHVzZXMgYSBzbWFsbCBkYXRhIHNldCB0byBkZWZpbmUgYSBjb250ZXh0IGZvciByZXBlYXRlZA0KIycgc2FtcGxpbmcgZnJvbSBhICJwb3B1bGF0aW9uIiB3aXRoIGtub3duIHNsb3BlIGFuZCBpbnRlcmNlcHQgdG8gZGV2ZWxvcGUgdGhlDQojJyBzYW1wbGluZyBkaXN0cmlidXRpb25zIG9mIHJlZ3Jlc3Npb24gZXN0aW1hdGVzLiBUaGUgZGF0YSByZWxhdGUgdG8gcHJlZGljdGluZw0KIycgb3V0Y29tZSBpbiB0aGVyYXB5IChZKSBmcm9tIHNjb3JlcyBvbiBhIHBlcnNvbmFsaXR5IHRlc3QgKFgpLg0KIycNCiMnIFdlIHRyZWF0IHRoZSBYIHZhbHVlcyBhcyBmaXhlZCBhcyBpbiB0aGUgc3RhbmRhcmQgcmVncmVzc2lvbiBtb2RlbC4NCiMnICBGb3IgZWFjaCBYIHZhbHVlLCBnZW5lcmF0ZSBzYW1wbGUgWSdzLCBhY2NvcmRpbmcNCiMnICB0byB0aGUgbW9kZWwsDQojJyAgJCQNCiMnICAgICAgICAgICBZICA9ICAxNCArIDEuMiAqIFggKyBzaWdtYSAqIE4oMCwxKQ0KIycgICQkDQojJyAgd2hlcmUNCiMnICANCiMnICogJFxiZXRhXzAgPSAxNCQsICAkXGJldGFfMSA9IDEuMiQgKGNvZWZmaWNpZW50cyBpbiB0aGUgdGhlcmFweSBkYXRhKSANCiMnICogc2lnbWEgPSBzcXJ0KE1TRSkgPSBzcXJ0KDgwKQ0KIycgKiBOKDAsMSk9IGEgcmFuZG9tIG5vcm1hbCBkZXZpYXRlDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIysgc2V0dXAsIGluY2x1ZGU9RkFMU0UNCmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRSwgd2FybmluZz1GQUxTRSwgY29tbWVudD1OQSkNCm9wdGlvbnMoZGlnaXRzPTQpDQoNCiMnICMjIyBMb2FkIHBhY2thZ2VzDQpsaWJyYXJ5KHB1cnJyKQ0KbGlicmFyeShicm9vbSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpIA0KbGlicmFyeShjYXIpDQoNCiMnICMjIyBSZWFkIHRoZSBkYXRhDQp0aGVyYXB5IDwtIHJlYWQudGFibGUoaGVhZGVyPVRSVUUsIHRleHQgPScNCm5hbWUgICBzZXggcGVyc3Rlc3QgdGhlcmFweQ0KSm9obiAgICBNIDI2IDMyDQpTdXNhbiAgIEYgMjQgNDANCk1hcnkgICAgRiAyMiA0NA0KUGF1bCAgICBNIDMzIDQ0DQpKZW5ueSAgIEYgMjcgNDgNClJpY2sgICAgTSAzNiA1Mg0KQ2F0aHkgICBGIDMwIDU2DQpSb2JlcnQgIE0gMzggNTYNCkxpc2EgICAgRiAzMCA2MA0KVGluYSAgICBGIDM0IDY4DQonKQ0KDQojJyAjIyBUcmVhdCB0aGUgdGhlcmFweSBkYXRhIGFzIGRlZmluaW5nIGEgJ3RydWUgbW9kZWwnDQoNCnRydWVfbW9kIDwtIGxtKHRoZXJhcHkgfiBwZXJzdGVzdCwgZGF0YT10aGVyYXB5KQ0KDQojJyAjIyBnZXQgdGlkeSBjb2VmZmljaWVudHMNCnRtb2QgPC0gdGlkeSh0cnVlX21vZCkNCnRtb2QNCg0KIycgQWRkIGxvd2VyL3VwcGVyIDk1JSBpbnRlcnZhbHMNCnVwcGVyIDwtIHRtb2QkZXN0aW1hdGUgKyAgMS45NiAqIHRtb2Qkc3RkLmVycm9yDQpsb3dlciA8LSB0bW9kJGVzdGltYXRlIC0gIDEuOTYgKiB0bW9kJHN0ZC5lcnJvcg0KdG1vZCA8LSBjYmluZCh0bW9kLCBsb3dlciwgdXBwZXIpDQp0bW9kDQoNCiMnICMjIFNldCB1cCB0aGUgc2ltdWxhdGlvbiBwYXJhbWV0ZXJzDQp4IDwtIHRoZXJhcHkkcGVyc3Rlc3QNCm4gPC0gbGVuZ3RoKHgpDQoNCnNhbXBsZXMgPC0gMTAwMA0KDQojICJ0cnVlIiBwYXJhbWV0ZXJzDQpiMCA8LSBjb2VmKHRydWVfbW9kKVsxXSAgICMgMTQNCmIxIDwtIGNvZWYodHJ1ZV9tb2QpWzJdICAgIyAxLjINCnNpZ21hIDwtIHN1bW1hcnkodHJ1ZV9tb2QpJHNpZ21hICAjIHNxcnQoODApDQpzZXQuc2VlZCgxMjM0KSAgICAgICAjIHJlcHJvZHVjaWJpbGl0eQ0KDQojJyAjIyMgR2VuZXJhdGUgb25lIHNhbXBsZSBmcm9tIHRoZSBiaXZhcmlhdGUgZGlzdHJpYnV0aW9uIG9mICh4LCB5KQ0KIycNCiMnIEZvciB0aGUgc2ltdWxhdGlvbiwgaXQgaXMgdXNlZnVsIHRvIGRlZmluZSBzb21lIHNpbXBsZSBmdW5jdGlvbnMgdG8gZG8gYQ0KIycgY2FsY3VsYXRpb24gZm9yIGEgc2luZ2xlIHNhbXBsZS4NCiMnIA0KIycgVGhlbiwgb25lIGNhbiB1c2UgYHJlcGxpY2F0ZShzYW1wbGVzLCBmdW4oKSlgIHRvIHJlcGVhdCBvdmVyIG1hbnkgc2FtcGxlcy4NCg0Kb25lX3NhbXBsZSA8LSBmdW5jdGlvbih4LCBiMD0xNCwgYjE9MS4yLCBzaWdtYT1zcXJ0KDgwKSkgew0KCW4gPC0gbGVuZ3RoKHgpDQoJeSA8LSBiMCArIGIxICogeCArIHJub3JtKG4sIG1lYW49MCwgc2Q9c2lnbWEpDQoJZGF0YS5mcmFtZSh4LCB5KQ0KfQ0Kb25lX3NhbXBsZSh4LCBiMCwgYjEsIHNpZ21hKQ0KDQojJyBEbyBhIHJlZ3Jlc3Npb24gZm9yIG9uZSBzYW1wbGUNCm9uZV9yZWcgPC0gZnVuY3Rpb24oeCkgew0KCXNpbWRhdGEgPC0gb25lX3NhbXBsZSh4KQ0KCWxtKHkgfiB4LCBkYXRhPXNpbWRhdGEpDQp9DQpvbmVfcmVnKHgpDQoNCiMnICMjIyBHZW5lcmF0ZSB0aGUgY29lZmZpY2llbnRzIGZvciAqKmFsbCoqIHNhbXBsZXMNCiMnIA0KIycgTm90ZSB0aGF0IGByZXBsaWNhdGUoKWAgYmluZHMgdGhlIHJlc3VsdHMgYXMgY29sdW1ucyBvZiBhIG1hdHJpeCwgYnV0IEkgd2FudCB0aGVtDQojJyBhcyByb3dzLCBvZiBhIGRhdGEgZnJhbWUNCmFsbF9jb2VmcyA8LSBmdW5jdGlvbih4LCBzYW1wbGVzPTUpIHsNCglyZXMgPC0gcmVwbGljYXRlKG49c2FtcGxlcywgY29lZihvbmVfcmVnKHgpKSkNCglyb3duYW1lcyhyZXMpIDwtIGMoImIwIiwgImIxIikNCglhcy5kYXRhLmZyYW1lKHQocmVzKSkNCn0NCg0KIycgQXBwbHkgdGhpcyBmb3IgYWxsIHNhbXBsZXMNCmNvZWZzIDwtIGFsbF9jb2Vmcyh4LCBzYW1wbGVzPXNhbXBsZXMpDQoNCmhlYWQoY29lZnMpICAgIyBzaG93IGZpcnN0IDYNCg0KIycgIyMgUGxvdCB0aGUgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIG9mIGNvZWZzDQojJyANCiMnIFBsb3QgdGhlIGRlbnNpdHkgb2YgYjEgZXN0aW1hdGVzIHRvZ2V0aGVyIHdpdGggdGhlIHRoZW9yZXRpY2FsDQojJyBub3JtYWwgZGVuc2l0eSB3aXRoIHRydWUgcGFyYW1ldGVycy4gTm90ZSB0aGUgdXNlIG9mIGBzdGF0X2Z1bmN0aW9uKClgDQojJyBmb3IgdGhlIG5vcm1hbCBkZW5zaXR5IChgZG5vcm0oKWApDQojJyANCmdncGxvdChjb2VmcywgIGFlcyh4ID0gYjEpICkgKw0KICAgIGdlb21fZGVuc2l0eShjb2xvcj0iYmx1ZSIsIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gLjIsIHNpemU9MS41KSArDQogICAgZ2VvbV92bGluZSggeGludGVyY2VwdCA9IGIxLCBjb2xvcj0icmVkIiwgc2l6ZT0xLjUpICsgDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbG93ZXJbMl0pICsNCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSB1cHBlclsyXSkgKw0KICAgIGFubm90YXRlKCJ0ZXh0IiwgeD1iMSwgeT0wLjA1LCBsYWJlbD1wYXN0ZSgiYmV0YVsxXSA9PSIsIGIxKSwgDQogICAgICAgICAgICAgc2l6ZT04LCBwYXJzZT1UUlVFKSArDQogICAgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QobWVhbiA9IGIxLCBzZCA9IC41NjUpKSArDQogICAgICAgIHhsYWIoIlNsb3BlIChiMSkiKSANCiAgICAgICAgDQoNCiMnICMjIFBsb3QgdGhlIGpvaW50IGRpc3RyaWJ1dGlvbiBvZiBiMCwgYjENCmdncGxvdChjb2VmcywgYWVzKHg9YjEsIHk9YjApKSArDQoJCQlnZW9tX3BvaW50KGNvbG9yPSJibHVlIiwgc2l6ZT0wLjkpICsNCgkJCXN0YXRfZWxsaXBzZShsZXZlbCA9IDAuNjgsIHNpemU9MS4yKSArIA0KCQkJc3RhdF9lbGxpcHNlKGxldmVsID0gMC45NSwgc2l6ZT0xLjA1LCBsaW5ldHlwZT0xKSArDQoJCQlnZW9tX3BvaW50KHg9YjEsIHk9YjAsIGNvbG9yPSJyZWQiLCBzaXplPTQpICsNCgkJCXRoZW1lX21pbmltYWwoKQ0KDQojJyBUaGUgdmVyeSBzdHJvbmcgbmVnYXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiBzbG9wZSAmIGludGVyY2VwdCBpcyBwZXJoYXBzDQojJyBhIGJpdCBzdXJwcmlzaW5nLiAgSGVyZSBpdCBpcyBpbiBudW1iZXJzOg0KIycgDQpjb3IoY29lZnMpCQkJDQoNCiMnICMjIFBsb3QgdGhlIHNsb3BlcyAmIHRoZWlyIGluZGl2aWR1YWwgQ0lzIChGaWcgNC4yKQ0KDQojJyBBZGQgc2UsIENJIGluZm8gdG8gdGhlIHNsb3BlIGVzdGltYXRlIGluIG9uIHNhbXBsZQ0KDQpvbmVfc2xvcGVfaW5mbyA8LSBmdW5jdGlvbih4KSB7DQoJc2ltZGF0YSA8LSBvbmVfc2FtcGxlKHgpDQoJcmVnIDwtIGxtKHkgfiB4LCBkYXRhPXNpbWRhdGEpDQoJdHJlZyA8LSB0aWR5KHJlZykNCgliMSA8LSBhcy5udW1lcmljKHRyZWdbMiwiZXN0aW1hdGUiXSkNCglzZSA8LSBhcy5udW1lcmljKHRyZWdbMiwic3RkLmVycm9yIl0pDQoJY29uZiA8LSBiMSArIGMoLTIsIC0uNjcsIC42NywgMikgKiBzZQ0KCWMoYjE9YjEsIHNlPXNlLCBjb25mKQ0KfQ0Kb25lX3Nsb3BlX2luZm8oeCkNCg0KIycgRG8gdGhlc2UgZm9yIGFsbCBzYW1wbGVzDQphbGxfc2xvcGVfaW5mbyA8LSBmdW5jdGlvbih4LCBzYW1wbGVzPTUpIHsNCglyZXMgPC0gcmVwbGljYXRlKG49c2FtcGxlcywgb25lX3Nsb3BlX2luZm8oeCkpDQoJcm93bmFtZXMocmVzKSA8LSBjKCJiMSIsICJzZSIsICJsbzk1IiwgImxvNTAiLCAidXA1MCIsICJ1cDk1IikNCglhcy5kYXRhLmZyYW1lKHQocmVzKSkNCn0NCnNsb3BlcyA8LSBhbGxfc2xvcGVfaW5mbyh4LCBzYW1wbGVzPXNhbXBsZXMpDQpoZWFkKHNsb3BlcykNCg0KIycgTWFrZSB0aGUgcGxvdCBpbiB0aGUgc3R5bGUgb2YgRmlndXJlIDQuMg0KIycNCiMrIGZpZy53aWR0aD03DQpwbG90KGMoLTIsIHNhbXBsZXMrMiksIHJhbmdlKHNsb3Blc1ssYygibG85NSIsICJ1cDk1IildKSwgYnR5PSJsIiwgDQoJCXhsYWI9IlNpbXVsYXRpb24iLCB5bGFiPSJFc3RpbWF0ZSwgNTAlICYgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwiLCANCgkJdHlwZT0ibiIpDQphYmxpbmUoYjEsIDAsIGNvbD0iZ3JheSIpDQpmb3IgKGkgaW4gc2VxKDEsIHNhbXBsZXMsIGJ5PTIpKXsNCiAgbGluZXMoYyhpLGkpLCBzbG9wZXNbaSxjKCJsbzk1IiwgInVwOTUiKV0sIGx3ZD0uOCwgY29sPWdyYXkoLjMsIGFscGhhPS41KSkNCiAgbGluZXMoYyhpLGkpLCBzbG9wZXNbaSxjKCJsbzUwIiwgInVwNTAiKV0sIGx3ZD0yLCAgY29sPSJyZWQiKQ0KfQ0KcG9pbnRzKDE6c2FtcGxlcywgc2xvcGVzWywiYjEiXSwgcGNoPTIwKQ0KDQoNCiMnICMjIFBsb3QgaW5kaXZpZHVhbCByZWdyZXNzaW9uIGxpbmVzIHRvIGNvbmZpcm0gdGhlIGNvbmZpZGVuY2UgYmFuZA0KDQojJyBIZXJlIEkgcGxvdCB0aGUgYGxtYCByZWdyZXNzaW9uIGxpbmUgZm9yIHRoZSBkYXRhIHdpdGggNjggJiA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbHMuDQojJyBPdmVybGFpZCBoZXJlIGFyZSB0aGUgKmluZGl2aWR1YWwqIHJlZ3Jlc3Npb24gbGluZXMgZm9yIHRoZSBzYW1wbGVzLiANCiMnIA0KIycgQSBjcmFmdHkgYGdncGxvdGAgdHJpY2sNCiMnIGFsbG93cyB0byB1c2UgdGhlIGZpdHRlZCBjb2VmZmljaWVudHMgYXMgYSBuZXcgZGF0YSBmcmFtZSBpbnB1dCB0byBgZ2VvbV9hYmxpbmVgDQoNCg0KZ2dwbG90KGRhdGE9dGhlcmFweSwgYWVzKHg9cGVyc3Rlc3QsIHk9dGhlcmFweSkpICsNCglnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgZm9ybXVsYT0geX54LCBsZXZlbD0wLjY4LCBhbHBoYT0uMikgKw0KCWdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBmb3JtdWxhPSB5fngsIGxldmVsPTAuOTUsIGFscGhhPS4zLCBzaXplPTIpICsNCglnZW9tX2FibGluZShkYXRhPWNvZWZzLCBhZXMoaW50ZXJjZXB0ID0gYjAsIHNsb3BlID0gYjEpLCBjb2xvcj1hZGp1c3Rjb2xvciggInJlZCIsIGFscGhhLmYgPSAwLjEpKSArDQoJZ2VvbV9wb2ludChzaXplPTMsIGNvbG9yPSJibHVlIikgICsNCgl0aGVtZV9idygpDQoNCg0KIycgIyMjIE90aGVyIGV4cGxvcmF0aW9ucw0KIycgV2hhdCBoYXBwZW5zIGlmIHdlIHVzZSAyIGNvbmZpZGVuY2UgbGV2ZWxzPyAgU3VycHJpc2UhDQpnZ3Bsb3QoZGF0YT10aGVyYXB5LCBhZXMoeD1wZXJzdGVzdCwgeT10aGVyYXB5KSkgKw0KCWdlb21fcG9pbnQoc2l6ZT0zLCBjb2xvcj0iYmx1ZSIpICsgDQoJZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGZvcm11bGE9IHl+eCwgbGV2ZWw9YygwLjY4LCAuOTUpKQ0KDQojJyBTaG93IHRocmVlIGNvbmZpZGVuY2UgbGV2ZWxzDQpnZ3Bsb3QoZGF0YT10aGVyYXB5LCBhZXMoeD1wZXJzdGVzdCwgeT10aGVyYXB5KSkgKw0KCQkJZ2VvbV9wb2ludChzaXplPTMsIGNvbG9yPSJibHVlIikgKyANCgkJCWdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBmb3JtdWxhPSB5fngsIGFscGhhPS4yLCBsZXZlbD0wLjY4KSArDQoJCQlnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgZm9ybXVsYT0geX54LCBhbHBoYT0uMiwgbGV2ZWw9MC45MCkgKw0KCQkJZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGZvcm11bGE9IHl+eCwgYWxwaGE9LjIsIGxldmVsPTAuOTUpICsNCgkJCXRoZW1lX2J3KCkNCgkNCg0KDQojJyAjIyBEaWZmZXJlbnQgd2F5IHRvIGRvIHRoaXMNCg0KIycgSW4gdGhlIGV4YW1wbGVzIGFib3ZlLCBJIGVzc2VudGlhbGx5IHJlLXJhbiBhbGwgb2YgdGhlIHNpbXVsYXRpb25zIGZvciBlYWNoIG9mDQojJyB0aGUgdGhlIGZ1bmN0aW9ucyB1c2VkIHdpdGggYHJlcGxpY2F0ZSgpYC4gVGhpcyBpcyBzb21ld2hhdCB3YXN0ZWZ1bC4NCiMnDQojJyBBbm90aGVyIHdheSBpcyB0byBnZW5lcmF0ZSBhbGwgdGhlIG1vZGVsIG9iamVjdHMgYXQgb25jZSwgc3RvcmUgdGhlbSBpbiBhIGxpc3QuICANCiMnIENhbiB0aGVuIHVzZSB0aWR5IGZ1bmN0aW9ucyB0byBleHRyYWN0DQojJyBzdW1tYXJpZXMsIGRvIHBsb3RzLCBldGMuDQoNCnNpbXMgPC0gcmVwbGljYXRlKG49c2FtcGxlcywgb25lX3JlZyh4KSwgc2ltcGxpZnk9RkFMU0UpDQoNCiMnICMjIHBsb3QgdGhlIHJlc2lkdWFsIHN0YW5kYXJkIGVycm9ycw0KDQpzaW1zICU+JQ0KICAgICBtYXBfZGJsKH5zdW1tYXJ5KC54KSRzaWdtYSkgJT4lDQogICAgIGRhdGEuZnJhbWUoc2lnbWEgPSAuKSAlPiUNCiAgICAgZ2dwbG90KCBhZXMoeCA9IHNpZ21hKSApICsNCiAgICAgICAgICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJibHVlIiwgYWxwaGEgPSAuMikgKw0KICAgICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHNxcnQoODApKQ0KDQojJyAjIyBSZWZlcmVuY2VzDQojJyANCg0KIycgKiBTb21lIHNpbWlsYXIgbWV0aG9kcyBmb3Igc2ltdWxhdGlvbiBhcmUgZGVzY3JpYmVkIGluIA0KIycgW1NpbXVsYXRlISBTaW11bGF0ZSEgLSBQYXJ0IDE6IEEgbGluZWFyIG1vZGVsXShodHRwczovL2Fvc21pdGgucmJpbmQuaW8vMjAxOC8wMS8wOS9zaW11bGF0ZS1zaW11bGF0ZS1wYXJ0MS8pDQojJyANCiMnICogQ2hhcHRlciAyMCwgW1NpbXVsYXRpb25dKGh0dHBzOi8vYm9va2Rvd24ub3JnL3JkcGVuZy9ycHJvZ2RhdGFzY2llbmNlL3NpbXVsYXRpb24uaHRtbCkgZnJvbQ0KIycgUm9nZXIgUGVuZywgW1IgUHJvZ3JhbW1pbmcgZm9yIERhdGEgU2NpZW5jZV0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmRwZW5nL3Jwcm9nZGF0YXNjaWVuY2UvKS4NCiMnIA==