An implementation of the “Simulating Variance Estimates” by ProfessorParris on youTube
Population variance is calculated:
\(\sigma = \frac{SS}{n}\)
But for sample standard deviations this formula systematically underestimates the variance and we use the sample standard deviation:
\(s = \frac{SS}{n - 1}\)
The following analysis will simulate repeated sampling from a normal distribution with a known mean (\(\mu\)) and standard deviation (\(\sigma\)) at different sample sizes.
We will then calculate the estimated variance and standard deviation using the population variance formula and the sample variance formula, and compare the results.
library(tidyr)
library(purrr)
library(dplyr)
library(knitr)
sample_data <- function(reps = 10000, mu = 100, sigma = 15, nsamples){
df <- data.frame("rep" = seq(1:reps), "mu" = mu, "sigma"= sigma, "n" = nsamples)
df %>%
group_by(rep) %>%
do(data = data.frame(value= rnorm(nsamples, mu, sigma))) %>%
unnest(data) %>%
mutate(nsamples = nsamples)
}
df <- bind_rows(sample_data(nsamples = 2),
sample_data(nsamples = 5),
sample_data(nsamples = 60),
sample_data(nsamples = 100))
Then we calcuates the sample mean (\(\bar{X}\)), Sum of squares (SS), variance and standard deviation using the population estimates (n in denominator) and using the sample estimates (n-1 in denominator).
stats <- df %>%
group_by(nsamples, rep) %>%
summarise(nreps = n(),
xbar = mean(value),
SS = sum((value - xbar)^2),
popvariance = SS / nreps,
samvariance = SS / (nreps - 1),
sigma_est = sqrt(popvariance),
s_est = sqrt(samvariance))
stats
Reformat the variances to show the difference between the average variance calculated using the population and the sample formula.
vars <- stats %>%
select(nsamples, rep, popvariance, samvariance) %>%
rename(population = popvariance,
sample = samvariance) %>%
gather("vartype", "variance", 3:4)
meanvars <- vars %>%
group_by(nsamples, vartype) %>%
summarise(mean = mean(variance))
meanvars %>%
spread(vartype, mean) %>%
kable()
| 2 |
112 |
223 |
| 5 |
180 |
225 |
| 60 |
222 |
225 |
| 100 |
223 |
225 |
Despite the small sample size the sample variance is on average a good estimate of the actual variance (\(\sigma^2 = 15^2 = 225\)).
When we plot the distribution it is clear that the individual estimates of the standard deviation have a large distribution which is highly skewed (especially as smalle sample sizes). The sample variance corrects well (on average) for the underestimation.
plot_variance_histogram <- function(n) {
means <- meanvars %>%
filter(nsamples == n)
vars %>%
filter(nsamples == n) %>%
ggplot(aes(variance)) +
geom_histogram(binwidth = 25) +
facet_grid(vartype~.) +
scale_x_continuous(breaks = seq(-200, 1400, 200) ) +
geom_vline(data = means %>% filter(nsamples == n),
aes(xintercept = mean), color= "red", size = 1.1) +
geom_vline(xintercept = 225, color= "blue") +
labs(title = paste0("simulation of estimating variance (nsamples = ", n, ")"),
subtitle= "comparing sample (n-1) and population (n) variance")
}
plot_variance_histogram(n= 5)

plot_variance_histogram(n= 60)

plot_variance_histogram(n= 2)

The sample standard deviation is however still systematially underestimated compared to the actual \(\sigma\) = 15.
stddevs <- stats %>%
select(nsamples, rep, sigma_est, s_est) %>%
rename(population = sigma_est,
sample = s_est) %>%
gather("vartype", "stddev", 3:4)
meansds <- stddevs %>%
group_by(nsamples, vartype) %>%
summarise(mean = mean(stddev))
meansds %>%
kable()
| 2 |
population |
8.47 |
| 2 |
sample |
11.98 |
| 5 |
population |
12.60 |
| 5 |
sample |
14.09 |
| 60 |
population |
14.83 |
| 60 |
sample |
14.95 |
| 100 |
population |
14.89 |
| 100 |
sample |
14.96 |
n = 100
means <- means %>%
filter(nsamples == n)
stddevs %>%
filter(nsamples == n) %>%
ggplot(aes(stddev)) +
geom_histogram(binwidth = 0.5) +
facet_grid(vartype~.) +
scale_x_continuous(breaks = seq(10, 20, 1) ) +
geom_vline(data = means %>% filter(nsamples == n),
aes(xintercept = mean), color= "red", size = 1.1) +
geom_vline(xintercept = 15, color= "blue") +
labs(title = paste0("simulation of estimating std. dev (nsamples = ", n, ")"),
subtitle= "comparing sample (n-1) and population (n) sd")

n = 100
stddevs %>%
filter(nsamples == n) %>%
ggplot(aes(stddev)) +
geom_histogram(binwidth = 0.05) +
facet_grid(vartype~.) +
scale_x_continuous(breaks = seq(10, 20, 1) ) +
coord_cartesian(xlim = c(14, 16))+
geom_vline(data = means %>% filter(nsamples == n),
aes(xintercept = mean), color= "red", size = 1.1) +
geom_vline(xintercept = 15, color= "blue") +
labs(title = paste0("simulation of estimating std. dev (nsamples = ", n, ")"),
subtitle= "comparing sample (n-1) and population (n) sd")

This demonstrates that while the estimate of the sample mean and sample variance is an unbiased estimator of the population mean (\(\mu\)) population variance (\(\sigma\)). However, sample standard deviation is not an unbiased estimator of the population standard deviation and systematically underestimates the standard deviation.
How well does the standard error estimate the error in the population mean?
As I had all this simulation data I thought that I would have a look at the difference between the errors in the xbar confidence interval (based on the SE mean), using the z distribution and the t-distribution at different sample sizes.
stats <- stats %>%
mutate(SEmean = s_est / sqrt(nreps),
ZciLL = (xbar - 1.96 * SEmean),
ZciUL = (xbar + 1.96 * SEmean),
ZmeanOK = ifelse( (100 > ZciLL) & (100 < ZciUL),
yes = TRUE, no = FALSE ),
tciLL = (xbar - qt(p = 0.975, df= (nreps - 1)) * SEmean),
tciUL = (xbar + qt(p = 0.975, df= (nreps - 1)) * SEmean),
tmeanOK = ifelse( (100 > tciLL) & (100 < tciUL),
yes = TRUE, no = FALSE ) )
stats %>%
group_by(nsamples) %>%
summarise(zmean = sum(ZmeanOK) / 10000,
avgZciRange = mean(ZciUL - ZciLL),
tmean = sum(tmeanOK) / 10000,
avgtciRange = mean(tciUL - tciLL) ) %>%
kable()
| 2 |
0.702 |
33.20 |
0.948 |
215.21 |
| 5 |
0.872 |
24.69 |
0.949 |
34.98 |
| 60 |
0.944 |
7.57 |
0.949 |
7.72 |
| 100 |
0.948 |
5.87 |
0.951 |
5.94 |
stats %>%
ggplot(aes(x= xbar)) +
geom_histogram(bins = 100) +
geom_vline(xintercept = 100, color= "red") +
facet_grid(nsamples~., scales = "free") +
labs(title= "Distribution of sample means",
subtitle= "with different sample sizes")

LS0tCnRpdGxlOiAiU2ltdWxhdGluZyBWYXJpYW5jZSBFc3RpbWF0ZXMiCmF1dGhvcjogIkFhcm9uIE0uIFNhdW5kZXJzIgpkYXRlOiAiRGVjZW1iZXIgMjEsIDIwMTYiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKCkFuIGltcGxlbWVudGF0aW9uIG9mIHRoZSAiU2ltdWxhdGluZyBWYXJpYW5jZSBFc3RpbWF0ZXMiIGJ5IFByb2Zlc3NvclBhcnJpcyBvbiBbeW91VHViZV0oaHR0cHM6Ly95b3V0dS5iZS9KZ2R0NnFLQjVDdykKCgpQb3B1bGF0aW9uIHZhcmlhbmNlIGlzIGNhbGN1bGF0ZWQ6CgokXHNpZ21hID0gXGZyYWN7U1N9e259JAogCkJ1dCBmb3Igc2FtcGxlIHN0YW5kYXJkIGRldmlhdGlvbnMgdGhpcyBmb3JtdWxhIHN5c3RlbWF0aWNhbGx5IHVuZGVyZXN0aW1hdGVzIHRoZSB2YXJpYW5jZSBhbmQgd2UgdXNlIHRoZSBzYW1wbGUgc3RhbmRhcmQgZGV2aWF0aW9uOgoKJHMgPSBcZnJhY3tTU317biAtIDF9JAoKVGhlIGZvbGxvd2luZyBhbmFseXNpcyB3aWxsIHNpbXVsYXRlIHJlcGVhdGVkIHNhbXBsaW5nIGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uIHdpdGggYSBrbm93biBtZWFuICgkXG11JCkgYW5kIHN0YW5kYXJkIGRldmlhdGlvbiAoJFxzaWdtYSQpIGF0IGRpZmZlcmVudCBzYW1wbGUgc2l6ZXMuIAoKV2Ugd2lsbCB0aGVuIGNhbGN1bGF0ZSB0aGUgZXN0aW1hdGVkIHZhcmlhbmNlIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gdXNpbmcgdGhlIHBvcHVsYXRpb24gdmFyaWFuY2UgZm9ybXVsYSBhbmQgdGhlIHNhbXBsZSB2YXJpYW5jZSBmb3JtdWxhLCBhbmQgY29tcGFyZSB0aGUgcmVzdWx0cy4KCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KbGlicmFyeSh0aWR5cikKbGlicmFyeShwdXJycikKbGlicmFyeShkcGx5cikKbGlicmFyeShrbml0cikKCnNhbXBsZV9kYXRhIDwtIGZ1bmN0aW9uKHJlcHMgPSAxMDAwMCwgbXUgPSAxMDAsIHNpZ21hID0gMTUsIG5zYW1wbGVzKXsKICBkZiA8LSBkYXRhLmZyYW1lKCJyZXAiID0gc2VxKDE6cmVwcyksICJtdSIgPSBtdSwgInNpZ21hIj0gc2lnbWEsICJuIiA9IG5zYW1wbGVzKSAKICBkZiAlPiUgCiAgICBncm91cF9ieShyZXApICU+JSAKICAgIGRvKGRhdGEgPSBkYXRhLmZyYW1lKHZhbHVlPSBybm9ybShuc2FtcGxlcywgbXUsIHNpZ21hKSkpICU+JQogICAgdW5uZXN0KGRhdGEpICU+JSAKICAgIG11dGF0ZShuc2FtcGxlcyA9IG5zYW1wbGVzKQp9CgpkZiA8LSBiaW5kX3Jvd3Moc2FtcGxlX2RhdGEobnNhbXBsZXMgPSAyKSwKICAgICAgICAgICAgICAgIHNhbXBsZV9kYXRhKG5zYW1wbGVzID0gNSksCiAgICAgICAgICAgICAgICBzYW1wbGVfZGF0YShuc2FtcGxlcyA9IDYwKSwKICAgICAgICAgICAgICAgIHNhbXBsZV9kYXRhKG5zYW1wbGVzID0gMTAwKSkKYGBgCgoKVGhlbiB3ZSBjYWxjdWF0ZXMgdGhlIHNhbXBsZSBtZWFuICgkXGJhcntYfSQpLCBTdW0gb2Ygc3F1YXJlcyAoU1MpLCB2YXJpYW5jZSBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIHVzaW5nIHRoZSBwb3B1bGF0aW9uIGVzdGltYXRlcyAobiBpbiBkZW5vbWluYXRvcikgYW5kIHVzaW5nIHRoZSBzYW1wbGUgZXN0aW1hdGVzIChuLTEgaW4gZGVub21pbmF0b3IpLgoKYGBge3J9CnN0YXRzIDwtIGRmICU+JQogIGdyb3VwX2J5KG5zYW1wbGVzLCByZXApICU+JSAKICBzdW1tYXJpc2UobnJlcHMgPSBuKCksCiAgICAgICAgICAgICAgeGJhciAgPSBtZWFuKHZhbHVlKSwKICAgICAgICAgICAgICBTUyAgICA9IHN1bSgodmFsdWUgLSB4YmFyKV4yKSwKICAgICAgICAgICAgICBwb3B2YXJpYW5jZSA9IFNTIC8gbnJlcHMsCiAgICAgICAgICAgICAgc2FtdmFyaWFuY2UgPSBTUyAvIChucmVwcyAtIDEpLAogICAgICAgICAgICAgIHNpZ21hX2VzdCA9IHNxcnQocG9wdmFyaWFuY2UpLAogICAgICAgICAgICAgIHNfZXN0ICAgICA9IHNxcnQoc2FtdmFyaWFuY2UpKQpzdGF0cwoKYGBgCgpSZWZvcm1hdCB0aGUgdmFyaWFuY2VzIHRvIHNob3cgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgYXZlcmFnZSB2YXJpYW5jZSBjYWxjdWxhdGVkIHVzaW5nIHRoZSBwb3B1bGF0aW9uIGFuZCB0aGUgc2FtcGxlIGZvcm11bGEuCgpgYGB7cn0KdmFycyA8LSBzdGF0cyAlPiUKICBzZWxlY3QobnNhbXBsZXMsIHJlcCwgcG9wdmFyaWFuY2UsIHNhbXZhcmlhbmNlKSAlPiUKICByZW5hbWUocG9wdWxhdGlvbiA9IHBvcHZhcmlhbmNlLAogICAgICAgICBzYW1wbGUgICAgID0gc2FtdmFyaWFuY2UpICU+JQogIGdhdGhlcigidmFydHlwZSIsICJ2YXJpYW5jZSIsIDM6NCkgCgptZWFudmFycyA8LSB2YXJzICU+JSAKICBncm91cF9ieShuc2FtcGxlcywgdmFydHlwZSkgJT4lIAogIHN1bW1hcmlzZShtZWFuID0gbWVhbih2YXJpYW5jZSkpCgptZWFudmFycyAlPiUgCiAgc3ByZWFkKHZhcnR5cGUsIG1lYW4pICU+JSAKICBrYWJsZSgpIAoKYGBgCgpEZXNwaXRlIHRoZSBzbWFsbCBzYW1wbGUgc2l6ZSB0aGUgc2FtcGxlIHZhcmlhbmNlIGlzIG9uIGF2ZXJhZ2UgYSBnb29kIGVzdGltYXRlIG9mIHRoZSBhY3R1YWwgdmFyaWFuY2UgKCRcc2lnbWFeMiA9IDE1XjIgPSAyMjUkKS4KCldoZW4gd2UgcGxvdCB0aGUgZGlzdHJpYnV0aW9uIGl0IGlzIGNsZWFyIHRoYXQgdGhlIGluZGl2aWR1YWwgZXN0aW1hdGVzIG9mIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gaGF2ZSBhIGxhcmdlIGRpc3RyaWJ1dGlvbiB3aGljaCBpcyBoaWdobHkgc2tld2VkIChlc3BlY2lhbGx5IGFzIHNtYWxsZSBzYW1wbGUgc2l6ZXMpLiBUaGUgc2FtcGxlIHZhcmlhbmNlIGNvcnJlY3RzIHdlbGwgKG9uIGF2ZXJhZ2UpIGZvciB0aGUgdW5kZXJlc3RpbWF0aW9uLgoKCmBgYHtyfQpwbG90X3ZhcmlhbmNlX2hpc3RvZ3JhbSA8LSBmdW5jdGlvbihuKSB7CiAgbWVhbnMgPC0gbWVhbnZhcnMgJT4lIAogICAgZmlsdGVyKG5zYW1wbGVzID09IG4pCiAgCiAgdmFycyAlPiUKICAgIGZpbHRlcihuc2FtcGxlcyA9PSBuKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHZhcmlhbmNlKSkgKyAKICAgICAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAyNSkgKwogICAgICBmYWNldF9ncmlkKHZhcnR5cGV+LikgKwogICAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKC0yMDAsIDE0MDAsIDIwMCkgKSArCiAgICAgIGdlb21fdmxpbmUoZGF0YSA9IG1lYW5zICU+JSBmaWx0ZXIobnNhbXBsZXMgPT0gbiksIAogICAgICAgICAgICAgICAgIGFlcyh4aW50ZXJjZXB0ID0gbWVhbiksIGNvbG9yPSAicmVkIiwgc2l6ZSA9IDEuMSkgKwogICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAyMjUsIGNvbG9yPSAiYmx1ZSIpICsKICAgICAgbGFicyh0aXRsZSA9IHBhc3RlMCgic2ltdWxhdGlvbiBvZiBlc3RpbWF0aW5nIHZhcmlhbmNlIChuc2FtcGxlcyA9ICIsIG4sICIpIiksCiAgICAgICAgICAgc3VidGl0bGU9ICJjb21wYXJpbmcgc2FtcGxlIChuLTEpIGFuZCBwb3B1bGF0aW9uIChuKSB2YXJpYW5jZSIpCn0KCnBsb3RfdmFyaWFuY2VfaGlzdG9ncmFtKG49IDUpCmBgYAoKYGBge3J9CnBsb3RfdmFyaWFuY2VfaGlzdG9ncmFtKG49IDYwKQpgYGAKCmBgYHtyfQpwbG90X3ZhcmlhbmNlX2hpc3RvZ3JhbShuPSAyKQpgYGAKClRoZSBzYW1wbGUgc3RhbmRhcmQgZGV2aWF0aW9uIGlzIGhvd2V2ZXIgc3RpbGwgc3lzdGVtYXRpYWxseSB1bmRlcmVzdGltYXRlZCBjb21wYXJlZCB0byB0aGUgYWN0dWFsICRcc2lnbWEkID0gMTUuIAoKCmBgYHtyfQpzdGRkZXZzIDwtIHN0YXRzICU+JQogIHNlbGVjdChuc2FtcGxlcywgcmVwLCBzaWdtYV9lc3QsIHNfZXN0KSAlPiUKICByZW5hbWUocG9wdWxhdGlvbiA9IHNpZ21hX2VzdCwKICAgICAgICAgc2FtcGxlICAgICA9IHNfZXN0KSAlPiUKICBnYXRoZXIoInZhcnR5cGUiLCAic3RkZGV2IiwgMzo0KSAKCm1lYW5zZHMgPC0gc3RkZGV2cyAlPiUgCiAgZ3JvdXBfYnkobnNhbXBsZXMsIHZhcnR5cGUpICU+JSAKICBzdW1tYXJpc2UobWVhbiA9IG1lYW4oc3RkZGV2KSkKCm1lYW5zZHMgJT4lIAogIGthYmxlKCkKYGBgCgoKYGBge3J9Cm4gPSAxMDAKbWVhbnMgPC0gbWVhbnMgJT4lCiAgZmlsdGVyKG5zYW1wbGVzID09IG4pCgpzdGRkZXZzICU+JQogICAgZmlsdGVyKG5zYW1wbGVzID09IG4pICU+JSAKICAgIGdncGxvdChhZXMoc3RkZGV2KSkgKyAKICAgICAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjUpICsKICAgICAgZmFjZXRfZ3JpZCh2YXJ0eXBlfi4pICsKICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxMCwgMjAsIDEpICkgKwogICAgICBnZW9tX3ZsaW5lKGRhdGEgPSBtZWFucyAlPiUgZmlsdGVyKG5zYW1wbGVzID09IG4pLCAKICAgICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IG1lYW4pLCBjb2xvcj0gInJlZCIsIHNpemUgPSAxLjEpICsKICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMTUsIGNvbG9yPSAiYmx1ZSIpICsKICAgICAgbGFicyh0aXRsZSA9IHBhc3RlMCgic2ltdWxhdGlvbiBvZiBlc3RpbWF0aW5nIHN0ZC4gZGV2IChuc2FtcGxlcyA9ICIsIG4sICIpIiksCiAgICAgICAgICAgc3VidGl0bGU9ICJjb21wYXJpbmcgc2FtcGxlIChuLTEpIGFuZCBwb3B1bGF0aW9uIChuKSBzZCIpCmBgYAoKYGBge3J9Cm4gPSAxMDAKc3RkZGV2cyAlPiUKICBmaWx0ZXIobnNhbXBsZXMgPT0gbikgJT4lIAogIGdncGxvdChhZXMoc3RkZGV2KSkgKyAKICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4wNSkgKwogICAgZmFjZXRfZ3JpZCh2YXJ0eXBlfi4pICsKICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMTAsIDIwLCAxKSApICsKICAgIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygxNCwgMTYpKSsKICAgIGdlb21fdmxpbmUoZGF0YSA9IG1lYW5zICU+JSBmaWx0ZXIobnNhbXBsZXMgPT0gbiksIAogICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IG1lYW4pLCBjb2xvcj0gInJlZCIsIHNpemUgPSAxLjEpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDE1LCBjb2xvcj0gImJsdWUiKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUwKCJzaW11bGF0aW9uIG9mIGVzdGltYXRpbmcgc3RkLiBkZXYgKG5zYW1wbGVzID0gIiwgbiwgIikiKSwKICAgICAgICAgc3VidGl0bGU9ICJjb21wYXJpbmcgc2FtcGxlIChuLTEpIGFuZCBwb3B1bGF0aW9uIChuKSBzZCIpCmBgYAoKVGhpcyBkZW1vbnN0cmF0ZXMgdGhhdCB3aGlsZSB0aGUgZXN0aW1hdGUgb2YgdGhlIHNhbXBsZSBtZWFuIGFuZCBzYW1wbGUgdmFyaWFuY2UgaXMgYW4gdW5iaWFzZWQgZXN0aW1hdG9yIG9mIHRoZSBwb3B1bGF0aW9uIG1lYW4gKCRcbXUkKSBwb3B1bGF0aW9uIHZhcmlhbmNlICgkXHNpZ21hJCkuIEhvd2V2ZXIsIHNhbXBsZSBzdGFuZGFyZCBkZXZpYXRpb24gaXMgbm90IGFuIHVuYmlhc2VkIGVzdGltYXRvciBvZiB0aGUgcG9wdWxhdGlvbiBzdGFuZGFyZCBkZXZpYXRpb24gYW5kIHN5c3RlbWF0aWNhbGx5IHVuZGVyZXN0aW1hdGVzIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24uCgojIyBIb3cgd2VsbCBkb2VzIHRoZSBzdGFuZGFyZCBlcnJvciBlc3RpbWF0ZSB0aGUgZXJyb3IgaW4gdGhlIHBvcHVsYXRpb24gbWVhbj8KCkFzIEkgaGFkIGFsbCB0aGlzIHNpbXVsYXRpb24gZGF0YSBJIHRob3VnaHQgdGhhdCBJIHdvdWxkIGhhdmUgYSBsb29rIGF0IHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGVycm9ycyBpbiB0aGUgeGJhciBjb25maWRlbmNlIGludGVydmFsIChiYXNlZCBvbiB0aGUgU0UgbWVhbiksIHVzaW5nIHRoZSB6IGRpc3RyaWJ1dGlvbiBhbmQgdGhlIHQtZGlzdHJpYnV0aW9uIGF0IGRpZmZlcmVudCBzYW1wbGUgc2l6ZXMuCgpgYGB7cn0Kc3RhdHMgPC0gc3RhdHMgJT4lCiAgbXV0YXRlKFNFbWVhbiA9IHNfZXN0IC8gc3FydChucmVwcyksCiAgICAgICAgIFpjaUxMICAgPSAoeGJhciAtIDEuOTYgKiBTRW1lYW4pLAogICAgICAgICBaY2lVTCAgID0gKHhiYXIgKyAxLjk2ICogU0VtZWFuKSwKICAgICAgICAgWm1lYW5PSyA9IGlmZWxzZSggKDEwMCA+IFpjaUxMKSAmICgxMDAgPCBaY2lVTCksIAogICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9IFRSVUUsIG5vID0gRkFMU0UgKSwKICAgICAgICAgdGNpTEwgICA9ICh4YmFyIC0gcXQocCA9IDAuOTc1LCBkZj0gKG5yZXBzIC0gMSkpICogU0VtZWFuKSwKICAgICAgICAgdGNpVUwgICA9ICh4YmFyICsgcXQocCA9IDAuOTc1LCBkZj0gKG5yZXBzIC0gMSkpICogU0VtZWFuKSwKICAgICAgICAgdG1lYW5PSyA9IGlmZWxzZSggKDEwMCA+IHRjaUxMKSAmICgxMDAgPCB0Y2lVTCksIAogICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9IFRSVUUsIG5vID0gRkFMU0UgKSAgKQoKc3RhdHMgJT4lCiAgZ3JvdXBfYnkobnNhbXBsZXMpICU+JSAKICBzdW1tYXJpc2Uoem1lYW4gPSBzdW0oWm1lYW5PSykgLyAxMDAwMCwKICAgICAgICAgICAgYXZnWmNpUmFuZ2UgPSBtZWFuKFpjaVVMIC0gWmNpTEwpLCAKICAgICAgICAgICAgdG1lYW4gPSBzdW0odG1lYW5PSykgLyAxMDAwMCwKICAgICAgICAgICAgYXZndGNpUmFuZ2UgPSBtZWFuKHRjaVVMIC0gdGNpTEwpICkgJT4lIAogIGthYmxlKCkKCmBgYAoKCmBgYHtyfQpzdGF0cyAlPiUgICAKICBnZ3Bsb3QoYWVzKHg9IHhiYXIpKSArIAogICAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwMCkgKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMTAwLCBjb2xvcj0gInJlZCIpICsKICAgIGZhY2V0X2dyaWQobnNhbXBsZXN+Liwgc2NhbGVzID0gImZyZWUiKSArCiAgICBsYWJzKHRpdGxlPSAiRGlzdHJpYnV0aW9uIG9mIHNhbXBsZSBtZWFucyIsCiAgICAgICAgIHN1YnRpdGxlPSAid2l0aCBkaWZmZXJlbnQgc2FtcGxlIHNpemVzIikKYGBgCgo=