Normal model with unknown mean and variance (BDA3 section 3.2 on p. 64).
Multivariate joint distribution, conditional distribution, marginal distribution, marginalization and posterior predictive distribution
ggplot2, grid, and gridExtra are used for plotting, tidyr for manipulating data frames
library(ggplot2)
theme_set(theme_minimal())
library(grid)
library(gridExtra)
library(tidyr)
Generic part common for Demos 3.1-3.4
Data
y <- c(93, 112, 122, 135, 122, 150, 118, 90, 124, 114)
Sufficient statistics
n <- length(y)
s2 <- var(y)
my <- mean(y)
Factorize the joint posterior p(mu,sigma2|y) to p(sigma2|y)p(mu|sigma2,y) Sample from the joint posterior using this factorization
rsinvchisq <- function(n, nu, s2, ...) nu*s2 / rchisq(n , nu, ...)
dsinvchisq <- function(x, nu, s2){
exp(log(nu/2)*nu/2 - lgamma(nu/2) + log(s2)/2*nu - log(x)*(nu/2+1) - (nu*s2/2)/x)
}
Sample 1000 random numbers from p(sigma2|y)
ns <- 1000
sigma2 <- rsinvchisq(ns, n-1, s2)
Sample from p(mu|sigma2,y)
mu <- my + sqrt(sigma2/n)*rnorm(length(sigma2))
Create a variable sigma and sample from predictive distribution p(ynew|y) for each draw of (mu, sigma)
sigma <- sqrt(sigma2)
ynew <- rnorm(ns, mu, sigma)
For mu, sigma and ynew compute the density in a grid ranges for the grids
t1l <- c(90, 150)
t2l <- c(10, 60)
nl <- c(50, 185)
t1 <- seq(t1l[1], t1l[2], length.out = ns)
t2 <- seq(t2l[1], t2l[2], length.out = ns)
xynew <- seq(nl[1], nl[2], length.out = ns)
Compute the exact marginal density of mu
pm <- dt((t1-my) / sqrt(s2/n), n-1) / sqrt(s2/n)
Estimate the marginal density using samples and ad hoc Gaussian kernel approximation
pmk <- density(mu, adjust = 2, n = ns, from = t1l[1], to = t1l[2])$y
Compute the exact marginal density of sigma
ps <- dsinvchisq(t2^2, n-1, s2) * 2*t2
Estimate the marginal density using samples and ad hoc Gaussian kernel approximation
psk <- density(sigma, n = ns, from = t2l[1], to = t2l[2])$y
Compute the exact predictive density
p_new <- dt((xynew-my) / sqrt(s2*(1+1/n)), n-1) / sqrt(s2*(1+1/n))
Evaluate the joint density in a grid. Note that the following is not normalized, but for plotting contours it does not matter.
dfj <- data.frame(t1 = rep(t1, each = length(t2)),
t2 = rep(t2, length(t1)))
dfj$z <- dsinvchisq(dfj$t2^2, n-1, s2) * 2*dfj$t2 * dnorm(dfj$t1, my, dfj$t2/sqrt(n))
cl <- seq(1e-5, max(dfj$z), length.out = 6)
Demo 3.1 Visualise the joint and marginal densities
Visualise the joint density and marginal densities of the posterior of normal distribution with unknown mean and variance.
Create a plot of the marginal density of mu
dfm <- data.frame(t1, Exact = pm, Empirical = pmk) %>%
pivot_longer(cols = !t1, names_to="grp", values_to="p")
margmu <- ggplot(dfm) +
geom_line(aes(t1, p, color = grp)) +
coord_cartesian(xlim = t1l) +
labs(title = 'Marginal of mu', x = '', y = '') +
scale_y_continuous(breaks = NULL) +
theme(legend.background = element_blank(),
legend.position = c(0.75, 0.8),
legend.title = element_blank())
Create a plot of the marginal density of sigma
dfs <- data.frame(t2, Exact = ps, Empirical = psk) %>%
pivot_longer(cols = !t2, names_to="grp", values_to="p")
margsig <- ggplot(dfs) +
geom_line(aes(t2, p, color = grp)) +
coord_cartesian(xlim = t2l) +
coord_flip() +
labs(title = 'Marginal of sigma', x = '', y = '') +
scale_y_continuous(breaks = NULL) +
theme(legend.background = element_blank(),
legend.position = c(0.75, 0.8),
legend.title = element_blank())
## Coordinate system already present. Adding new coordinate system, which will replace the existing one.
Create a plot of the joint density
joint1labs <- c('Samples','Exact contour')
joint1 <- ggplot() +
geom_point(data = data.frame(mu,sigma), aes(mu, sigma, col = '1'), size = 0.1) +
geom_contour(data = dfj, aes(t1, t2, z = z, col = '2'), breaks = cl) +
coord_cartesian(xlim = t1l,ylim = t2l) +
labs(title = 'Joint posterior', x = '', y = '') +
scale_y_continuous(labels = NULL) +
scale_x_continuous(labels = NULL) +
scale_color_manual(values=c('blue', 'black'), labels = joint1labs) +
guides(color = guide_legend(nrow = 1, override.aes = list(
shape = c(16, NA), linetype = c(0, 1), size = c(2, 1)))) +
theme(legend.background = element_blank(),
legend.position = c(0.5, 0.9),
legend.title = element_blank())
Combine the plots
bp <- grid.rect(gp = gpar(col = 'white'))
grid.arrange(joint1, margsig, margmu, bp, nrow = 2)

Demo 3.2 Visualise factored distribution
Visualise factored sampling and the corresponding marginal and conditional densities.
Create another plot of the joint posterior
dfc <- data.frame(mu = t1, marg = rep(sigma[1], length(t1)),
cond = sigma[1] + dnorm(t1 ,my, sqrt(sigma2[1]/n)) * 100) %>%
pivot_longer(cols = c(marg,cond), names_to="grp", values_to="p")
joint2labs <- c('Exact contour plot', 'Sample from joint post.',
'Cond. distribution of mu', 'Sample from the marg. of sigma')
joint2 <- ggplot() +
geom_contour(data = dfj, aes(t1, t2, z = z, col = '1'), breaks = cl) +
geom_point(data = data.frame(m = mu[1], s = sigma[1]), aes(m , s, color = '2')) +
geom_line(data = dfc, aes(mu, p, color = grp), linetype = 'dashed') +
coord_cartesian(xlim = t1l,ylim = t2l) +
labs(title = 'Joint posterior', x = '', y = '') +
scale_x_continuous(labels = NULL) +
scale_color_manual(values=c('blue', 'darkgreen','darkgreen','black'), labels = joint2labs) +
guides(color = guide_legend(nrow = 2, override.aes = list(
shape = c(NA, 16, NA, NA), linetype = c(1, 0, 1, 1)))) +
theme(legend.background = element_blank(),
legend.position = c(0.5, 0.85),
legend.title = element_blank())
Create another plot of the marginal density of sigma
margsig2 <- ggplot(data = data.frame(t2, ps)) +
geom_line(aes(t2, ps), color = 'blue') +
coord_cartesian(xlim = t2l) +
coord_flip() +
labs(title = 'Marginal of sigma', x = '', y = '') +
scale_y_continuous(labels = NULL)
## Coordinate system already present. Adding new coordinate system, which will replace the existing one.
Combine the plots
grid.arrange(joint2, margsig2, ncol = 2)

Demo 3.3 Visualise the marginal distribution of mu
Visualise the marginal distribution of mu as a mixture of normals.
Calculate conditional pdfs for each sample
condpdfs <- sapply(t1, function(x) dnorm(x, my, sqrt(sigma2/n)))
Create a plot of some of them
dfm25 <- data.frame(t1, t(condpdfs[1:25,])) %>%
pivot_longer(cols = !t1, names_to="grp", values_to="p")
condmu <- ggplot(data = dfm25) +
geom_line(aes(t1, p, group = grp), linetype = 'dashed') +
labs(title = 'Conditional distribution of mu for first 25 samples', y = '', x = '') +
scale_y_continuous(breaks = NULL)
create a plot of their mean
dfsam <- data.frame(t1, colMeans(condpdfs), pm) %>%
pivot_longer(cols = !t1, names_to="grp", values_to="p")
mulabs <- c('avg of sampled conds', 'exact marginal of mu')
meanmu <- ggplot(data = dfsam) +
geom_line(aes(t1, p, size = grp, color = grp)) +
labs(y = '', x = 'mu') +
scale_y_continuous(breaks = NULL) +
scale_size_manual(values = c(2, 0.8), labels = mulabs) +
scale_color_manual(values = c('orange', 'black'), labels = mulabs) +
theme(legend.position = c(0.8, 0.8),
legend.background = element_blank(),
legend.title = element_blank())
Combine the plots
grid.arrange(condmu, meanmu, ncol = 1)

Demo 3.4 Visualise posterior predictive distribution.
Visualise sampling from the posterior predictive distribution. Calculate each predictive pdf with given mu and sigma
ynewdists <- sapply(xynew, function(x) dnorm(x, mu, sigma))
Create plot of the joint posterior with a draw from the posterior predictive distribution, highlight the first sample create a plot of the joint density
dfp <- data.frame(xynew, draw = ynewdists[1,], exact = p_new)
dfy <- data.frame(ynew, p = 0.02*max(ynewdists))
pred1labs <- c('Sample from the predictive distribution', 'Predictive distribution given the posterior sample')
pred2labs <- c('Samples from the predictive distribution', 'Exact predictive distribution')
joint3labs <- c('Samples', 'Exact contour')
joint3 <- ggplot() +
geom_point(data = data.frame(mu, sigma), aes(mu, sigma, col = '1'), size = 0.1) +
geom_contour(data = dfj, aes(t1, t2, z = z, col = '2'), breaks = cl) +
geom_point(data = data.frame(x = mu[1], y = sigma[1]), aes(x, y), color = 'red') +
coord_cartesian(xlim = t1l,ylim = t2l) +
labs(title = 'Joint posterior', x = 'mu', y = 'sigma') +
scale_color_manual(values=c('blue', 'black'), labels = joint3labs) +
guides(color = guide_legend(nrow = 1, override.aes = list(
shape = c(16, NA), linetype = c(0, 1), size = c(2, 1)))) +
theme(legend.background = element_blank(),
legend.position=c(0.5 ,0.9),
legend.title = element_blank())
Create a plot of the predicitive distribution and the respective sample
pred1 <- ggplot() +
geom_line(data = dfp, aes(xynew, draw, color = '2')) +
geom_point(data = dfy, aes(ynew[1], p, color = '1')) +
coord_cartesian(xlim = nl, ylim = c(0,0.04)) +
labs(title = 'Posterior predictive distribution', x = 'ytilde', y = '') +
scale_y_continuous(breaks = NULL) +
scale_color_manual(values = c('red', 'blue'), labels = pred1labs) +
guides(color = guide_legend(nrow = 2, override.aes = list(
linetype = c(0, 1), shape=c(16, NA), labels = pred1labs))) +
theme(legend.background = element_blank(),
legend.position = c(0.5 ,0.9),
legend.title = element_blank())
Create a plot for all ynews
pred2 <- ggplot() +
geom_line(data = dfp, aes(xynew, draw, color = '2')) +
geom_point(data = dfy, aes(ynew, p, color = '1'), alpha = 0.05) +
coord_cartesian(xlim = nl, ylim = c(0,0.04)) +
labs(x = 'ytilde', y = '') +
scale_y_continuous(breaks=NULL) +
scale_color_manual(values=c('darkgreen','blue'),labels=pred2labs) +
guides(color = guide_legend(nrow = 2, override.aes=list(
linetype = c(0, 1), shape = c(16, NA), alpha = c(1, 1) ,labels = pred2labs))) +
theme(legend.background = element_blank(),
legend.position = c(0.5 ,0.9),
legend.title = element_blank())
Combine the plots
grid.arrange(joint3, pred1, bp, pred2, nrow = 2)

LS0tCnRpdGxlOiAiQmF5ZXNpYW4gZGF0YSBhbmFseXNpcyBkZW1vcyAzLjEsIDMuMiwgMy4zLCAzLjQiCmF1dGhvcjogIkFraSBWZWh0YXJpLCBNYXJrdXMgUGFhc2luaWVtaSIKZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IHJlYWRhYmxlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQojIyBOb3JtYWwgbW9kZWwgd2l0aCB1bmtub3duIG1lYW4gYW5kIHZhcmlhbmNlIChCREEzIHNlY3Rpb24gMy4yIG9uIHAuIDY0KS4KCk11bHRpdmFyaWF0ZSBqb2ludCBkaXN0cmlidXRpb24sIGNvbmRpdGlvbmFsIGRpc3RyaWJ1dGlvbiwgbWFyZ2luYWwKZGlzdHJpYnV0aW9uLCBtYXJnaW5hbGl6YXRpb24gYW5kIHBvc3RlcmlvciBwcmVkaWN0aXZlIGRpc3RyaWJ1dGlvbgoKZ2dwbG90MiwgZ3JpZCwgYW5kIGdyaWRFeHRyYSBhcmUgdXNlZCBmb3IgcGxvdHRpbmcsIHRpZHlyIGZvciBtYW5pcHVsYXRpbmcgZGF0YSBmcmFtZXMKCmBgYHtyIHNldHVwLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpKQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KHRpZHlyKQpgYGAKCiMjIyMgR2VuZXJpYyBwYXJ0IGNvbW1vbiBmb3IgRGVtb3MgMy4xLTMuNApEYXRhCgpgYGB7ciB9CnkgPC0gYyg5MywgMTEyLCAxMjIsIDEzNSwgMTIyLCAxNTAsIDExOCwgOTAsIDEyNCwgMTE0KQpgYGAKClN1ZmZpY2llbnQgc3RhdGlzdGljcwoKYGBge3IgfQpuIDwtIGxlbmd0aCh5KQpzMiA8LSB2YXIoeSkKbXkgPC0gbWVhbih5KQpgYGAKCkZhY3Rvcml6ZSB0aGUgam9pbnQgcG9zdGVyaW9yIHAobXUsc2lnbWEyfHkpIHRvIHAoc2lnbWEyfHkpcChtdXxzaWdtYTIseSkKU2FtcGxlIGZyb20gdGhlIGpvaW50IHBvc3RlcmlvciB1c2luZyB0aGlzIGZhY3Rvcml6YXRpb24KCmBgYHtyIH0KIyBoZWxwZXIgZnVuY3Rpb25zIHRvIHNhbXBsZSBmcm9tIGFuZCBldmFsdWF0ZQojIHNjYWxlZCBpbnZlcnNlIGNoaS1zcXVhcmVkIGRpc3RyaWJ1dGlvbgpyc2ludmNoaXNxIDwtIGZ1bmN0aW9uKG4sIG51LCBzMiwgLi4uKSBudSpzMiAvIHJjaGlzcShuICwgbnUsIC4uLikKZHNpbnZjaGlzcSA8LSBmdW5jdGlvbih4LCBudSwgczIpewogIGV4cChsb2cobnUvMikqbnUvMiAtIGxnYW1tYShudS8yKSArIGxvZyhzMikvMipudSAtIGxvZyh4KSoobnUvMisxKSAtIChudSpzMi8yKS94KQp9CmBgYAoKU2FtcGxlIDEwMDAgcmFuZG9tIG51bWJlcnMgZnJvbSBwKHNpZ21hMnx5KQoKYGBge3IgfQpucyA8LSAxMDAwCnNpZ21hMiAgPC0gcnNpbnZjaGlzcShucywgbi0xLCBzMikKYGBgCgpTYW1wbGUgZnJvbSBwKG11fHNpZ21hMix5KQoKYGBge3IgfQptdSA8LSBteSArIHNxcnQoc2lnbWEyL24pKnJub3JtKGxlbmd0aChzaWdtYTIpKQpgYGAKCkNyZWF0ZSBhIHZhcmlhYmxlIHNpZ21hIGFuZApzYW1wbGUgZnJvbSBwcmVkaWN0aXZlIGRpc3RyaWJ1dGlvbiBwKHluZXd8eSkgZm9yIGVhY2ggZHJhdyBvZiAobXUsIHNpZ21hKQoKYGBge3IgfQpzaWdtYSA8LSBzcXJ0KHNpZ21hMikKeW5ldyA8LSBybm9ybShucywgbXUsIHNpZ21hKQpgYGAKCkZvciBtdSwgc2lnbWEgYW5kIHluZXcgY29tcHV0ZSB0aGUgZGVuc2l0eSBpbiBhIGdyaWQKcmFuZ2VzIGZvciB0aGUgZ3JpZHMKCmBgYHtyIH0KdDFsIDwtIGMoOTAsIDE1MCkKdDJsIDwtIGMoMTAsIDYwKQpubCA8LSBjKDUwLCAxODUpCnQxIDwtIHNlcSh0MWxbMV0sIHQxbFsyXSwgbGVuZ3RoLm91dCA9IG5zKQp0MiA8LSBzZXEodDJsWzFdLCB0MmxbMl0sIGxlbmd0aC5vdXQgPSBucykKeHluZXcgPC0gc2VxKG5sWzFdLCBubFsyXSwgbGVuZ3RoLm91dCA9IG5zKQpgYGAKCkNvbXB1dGUgdGhlIGV4YWN0IG1hcmdpbmFsIGRlbnNpdHkgb2YgbXUKCmBgYHtyIH0KIyBtdWx0aXBsaWNhdGlvbiBieSAxLi9zcXJ0KHMyL24pIGlzIGR1ZSB0byB0aGUgdHJhbnNmb3JtYXRpb24gb2YKIyB2YXJpYWJsZSB6PSh4LW1lYW4oeSkpL3NxcnQoczIvbiksIHNlZSBCREEzIHAuIDIxCnBtIDwtIGR0KCh0MS1teSkgLyBzcXJ0KHMyL24pLCBuLTEpIC8gc3FydChzMi9uKQpgYGAKCkVzdGltYXRlIHRoZSBtYXJnaW5hbCBkZW5zaXR5IHVzaW5nIHNhbXBsZXMKYW5kIGFkIGhvYyBHYXVzc2lhbiBrZXJuZWwgYXBwcm94aW1hdGlvbgoKYGBge3IgfQpwbWsgPC0gZGVuc2l0eShtdSwgYWRqdXN0ID0gMiwgbiA9IG5zLCBmcm9tID0gdDFsWzFdLCB0byA9IHQxbFsyXSkkeQpgYGAKCkNvbXB1dGUgdGhlIGV4YWN0IG1hcmdpbmFsIGRlbnNpdHkgb2Ygc2lnbWEKCmBgYHtyIH0KIyB0aGUgbXVsdGlwbGljYXRpb24gYnkgMip0MiBpcyBkdWUgdG8gdGhlIHRyYW5zZm9ybWF0aW9uIG9mCiMgdmFyaWFibGUgej10Ml4yLCBzZWUgQkRBMyBwLiAyMQpwcyA8LSBkc2ludmNoaXNxKHQyXjIsIG4tMSwgczIpICogMip0MgpgYGAKCkVzdGltYXRlIHRoZSBtYXJnaW5hbCBkZW5zaXR5IHVzaW5nIHNhbXBsZXMKYW5kIGFkIGhvYyBHYXVzc2lhbiBrZXJuZWwgYXBwcm94aW1hdGlvbgoKYGBge3IgfQpwc2sgPC0gZGVuc2l0eShzaWdtYSwgbiA9IG5zLCBmcm9tID0gdDJsWzFdLCB0byA9IHQybFsyXSkkeQpgYGAKCkNvbXB1dGUgdGhlIGV4YWN0IHByZWRpY3RpdmUgZGVuc2l0eQoKYGBge3IgfQojIG11bHRpcGxpY2F0aW9uIGJ5IDEuL3NxcnQoczIvbikgaXMgZHVlIHRvIHRoZSB0cmFuc2Zvcm1hdGlvbiBvZiB2YXJpYWJsZQojIHNlZSBCREEzIHAuIDIxCnBfbmV3IDwtIGR0KCh4eW5ldy1teSkgLyBzcXJ0KHMyKigxKzEvbikpLCBuLTEpIC8gc3FydChzMiooMSsxL24pKQpgYGAKCkV2YWx1YXRlIHRoZSBqb2ludCBkZW5zaXR5IGluIGEgZ3JpZC4KTm90ZSB0aGF0IHRoZSBmb2xsb3dpbmcgaXMgbm90IG5vcm1hbGl6ZWQsIGJ1dCBmb3IgcGxvdHRpbmcKY29udG91cnMgaXQgZG9lcyBub3QgbWF0dGVyLgoKYGBge3IgfQojIENvbWJpbmUgZ3JpZCBwb2ludHMgaW50byBhbm90aGVyIGRhdGEgZnJhbWUKIyB3aXRoIGFsbCBwYWlyd2lzZSBjb21iaW5hdGlvbnMKZGZqIDwtIGRhdGEuZnJhbWUodDEgPSByZXAodDEsIGVhY2ggPSBsZW5ndGgodDIpKSwKICAgICAgICAgICAgICAgICAgdDIgPSByZXAodDIsIGxlbmd0aCh0MSkpKQpkZmokeiA8LSBkc2ludmNoaXNxKGRmaiR0Ml4yLCBuLTEsIHMyKSAqIDIqZGZqJHQyICogZG5vcm0oZGZqJHQxLCBteSwgZGZqJHQyL3NxcnQobikpCiMgYnJlYWtzIGZvciBwbG90dGluZyB0aGUgY29udG91cnMKY2wgPC0gc2VxKDFlLTUsIG1heChkZmokeiksIGxlbmd0aC5vdXQgPSA2KQpgYGAKCiMjIyBEZW1vIDMuMSBWaXN1YWxpc2UgdGhlIGpvaW50IGFuZCBtYXJnaW5hbCBkZW5zaXRpZXMKVmlzdWFsaXNlIHRoZSBqb2ludCBkZW5zaXR5IGFuZCBtYXJnaW5hbCBkZW5zaXRpZXMgb2YgdGhlIHBvc3RlcmlvcgpvZiBub3JtYWwgZGlzdHJpYnV0aW9uIHdpdGggdW5rbm93biBtZWFuIGFuZCB2YXJpYW5jZS4KCkNyZWF0ZSBhIHBsb3Qgb2YgdGhlIG1hcmdpbmFsIGRlbnNpdHkgb2YgbXUKCmBgYHtyIH0KZGZtIDwtIGRhdGEuZnJhbWUodDEsIEV4YWN0ID0gcG0sIEVtcGlyaWNhbCA9IHBtaykgJT4lCiAgIHBpdm90X2xvbmdlcihjb2xzID0gIXQxLCBuYW1lc190bz0iZ3JwIiwgdmFsdWVzX3RvPSJwIikKbWFyZ211IDwtIGdncGxvdChkZm0pICsKICBnZW9tX2xpbmUoYWVzKHQxLCBwLCBjb2xvciA9IGdycCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IHQxbCkgKwogIGxhYnModGl0bGUgPSAnTWFyZ2luYWwgb2YgbXUnLCB4ID0gJycsIHkgPSAnJykgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBOVUxMKSArCiAgdGhlbWUobGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjc1LCAwLjgpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpDcmVhdGUgYSBwbG90IG9mIHRoZSBtYXJnaW5hbCBkZW5zaXR5IG9mIHNpZ21hCgpgYGB7ciB9CmRmcyA8LSBkYXRhLmZyYW1lKHQyLCBFeGFjdCA9IHBzLCBFbXBpcmljYWwgPSBwc2spICU+JSAKICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAhdDIsIG5hbWVzX3RvPSJncnAiLCB2YWx1ZXNfdG89InAiKQptYXJnc2lnIDwtIGdncGxvdChkZnMpICsKICBnZW9tX2xpbmUoYWVzKHQyLCBwLCBjb2xvciA9IGdycCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IHQybCkgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicyh0aXRsZSA9ICdNYXJnaW5hbCBvZiBzaWdtYScsIHggPSAnJywgeSA9ICcnKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IE5VTEwpICsKICB0aGVtZShsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuNzUsIDAuOCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCkNyZWF0ZSBhIHBsb3Qgb2YgdGhlIGpvaW50IGRlbnNpdHkKCmBgYHtyIH0Kam9pbnQxbGFicyA8LSBjKCdTYW1wbGVzJywnRXhhY3QgY29udG91cicpCmpvaW50MSA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZGF0YS5mcmFtZShtdSxzaWdtYSksIGFlcyhtdSwgc2lnbWEsIGNvbCA9ICcxJyksIHNpemUgPSAwLjEpICsKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGRmaiwgYWVzKHQxLCB0MiwgeiA9IHosIGNvbCA9ICcyJyksIGJyZWFrcyA9IGNsKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSB0MWwseWxpbSA9IHQybCkgKwogIGxhYnModGl0bGUgPSAnSm9pbnQgcG9zdGVyaW9yJywgeCA9ICcnLCB5ID0gJycpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gTlVMTCkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBOVUxMKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCdibHVlJywgJ2JsYWNrJyksIGxhYmVscyA9IGpvaW50MWxhYnMpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyAgPSAxLCBvdmVycmlkZS5hZXMgPSBsaXN0KAogICAgc2hhcGUgPSBjKDE2LCBOQSksIGxpbmV0eXBlID0gYygwLCAxKSwgc2l6ZSA9IGMoMiwgMSkpKSkgKwogIHRoZW1lKGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC41LCAwLjkpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpDb21iaW5lIHRoZSBwbG90cwoKYGBge3IgYmxhbmssIGZpZy5zaG93PSdoaWRlJ30KIyBibGFuayBwbG90IGZvciBjb21iaW5pbmcgdGhlIHBsb3RzCmJwIDwtIGdyaWQucmVjdChncCA9IGdwYXIoY29sID0gJ3doaXRlJykpCmBgYApgYGB7ciBjb21iaW5lZH0KZ3JpZC5hcnJhbmdlKGpvaW50MSwgbWFyZ3NpZywgbWFyZ211LCBicCwgbnJvdyA9IDIpCmBgYAoKIyMjIERlbW8gMy4yIFZpc3VhbGlzZSBmYWN0b3JlZCBkaXN0cmlidXRpb24KVmlzdWFsaXNlIGZhY3RvcmVkIHNhbXBsaW5nIGFuZCB0aGUgY29ycmVzcG9uZGluZwptYXJnaW5hbCBhbmQgY29uZGl0aW9uYWwgZGVuc2l0aWVzLgoKQ3JlYXRlIGFub3RoZXIgcGxvdCBvZiB0aGUgam9pbnQgcG9zdGVyaW9yCgpgYGB7ciB9CiMgZGF0YSBmcmFtZSBmb3IgdGhlIGNvbmRpdGlvbmFsIG9mIG11IGFuZCBtYXJnaW5hbCBvZiBzaWdtYQpkZmMgPC0gZGF0YS5mcmFtZShtdSA9IHQxLCBtYXJnID0gcmVwKHNpZ21hWzFdLCBsZW5ndGgodDEpKSwKICAgICAgICAgICAgICAgICAgY29uZCA9IHNpZ21hWzFdICsgZG5vcm0odDEgLG15LCBzcXJ0KHNpZ21hMlsxXS9uKSkgKiAxMDApICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhtYXJnLGNvbmQpLCBuYW1lc190bz0iZ3JwIiwgdmFsdWVzX3RvPSJwIikKIyBsZWdlbmQgbGFiZWxzIGZvciB0aGUgZm9sbG93aW5nIHBsb3QKam9pbnQybGFicyA8LSBjKCdFeGFjdCBjb250b3VyIHBsb3QnLCAnU2FtcGxlIGZyb20gam9pbnQgcG9zdC4nLAogICAgICAgICAgICAgICAnQ29uZC4gZGlzdHJpYnV0aW9uIG9mIG11JywgJ1NhbXBsZSBmcm9tIHRoZSBtYXJnLiBvZiBzaWdtYScpCmpvaW50MiA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9jb250b3VyKGRhdGEgPSBkZmosIGFlcyh0MSwgdDIsIHogPSB6LCBjb2wgPSAnMScpLCBicmVha3MgPSBjbCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGRhdGEuZnJhbWUobSA9IG11WzFdLCBzID0gc2lnbWFbMV0pLCBhZXMobSAsIHMsIGNvbG9yID0gJzInKSkgKwogIGdlb21fbGluZShkYXRhID0gZGZjLCBhZXMobXUsIHAsIGNvbG9yID0gZ3JwKSwgbGluZXR5cGUgPSAnZGFzaGVkJykgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gdDFsLHlsaW0gPSB0MmwpICsKICBsYWJzKHRpdGxlID0gJ0pvaW50IHBvc3RlcmlvcicsIHggPSAnJywgeSA9ICcnKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IE5VTEwpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ2JsdWUnLCAnZGFya2dyZWVuJywnZGFya2dyZWVuJywnYmxhY2snKSwgbGFiZWxzID0gam9pbnQybGFicykgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChucm93ICA9IDIsIG92ZXJyaWRlLmFlcyA9IGxpc3QoCiAgICBzaGFwZSA9IGMoTkEsIDE2LCBOQSwgTkEpLCBsaW5ldHlwZSA9IGMoMSwgMCwgMSwgMSkpKSkgKwogIHRoZW1lKGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC41LCAwLjg1KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKQ3JlYXRlIGFub3RoZXIgcGxvdCBvZiB0aGUgbWFyZ2luYWwgZGVuc2l0eSBvZiBzaWdtYQoKYGBge3IgfQptYXJnc2lnMiA8LSBnZ3Bsb3QoZGF0YSA9IGRhdGEuZnJhbWUodDIsIHBzKSkgKwogIGdlb21fbGluZShhZXModDIsIHBzKSwgY29sb3IgPSAnYmx1ZScpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IHQybCkgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicyh0aXRsZSA9ICdNYXJnaW5hbCBvZiBzaWdtYScsIHggPSAnJywgeSA9ICcnKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IE5VTEwpCmBgYAoKQ29tYmluZSB0aGUgcGxvdHMKCmBgYHtyIH0KZ3JpZC5hcnJhbmdlKGpvaW50MiwgbWFyZ3NpZzIsIG5jb2wgPSAyKQpgYGAKCiMjIyBEZW1vIDMuMyBWaXN1YWxpc2UgdGhlIG1hcmdpbmFsIGRpc3RyaWJ1dGlvbiBvZiBtdQpWaXN1YWxpc2UgdGhlIG1hcmdpbmFsIGRpc3RyaWJ1dGlvbiBvZiBtdSBhcyBhIG1peHR1cmUgb2Ygbm9ybWFscy4KCkNhbGN1bGF0ZSBjb25kaXRpb25hbCBwZGZzIGZvciBlYWNoIHNhbXBsZQoKYGBge3IgfQpjb25kcGRmcyA8LSBzYXBwbHkodDEsIGZ1bmN0aW9uKHgpIGRub3JtKHgsIG15LCBzcXJ0KHNpZ21hMi9uKSkpCmBgYAoKQ3JlYXRlIGEgcGxvdCBvZiBzb21lIG9mIHRoZW0KCmBgYHtyIH0KIyBkYXRhIGZyYW1lIG9mIDI1IGZpcnN0IHNhbXBsZXMKZGZtMjUgPC0gZGF0YS5mcmFtZSh0MSwgdChjb25kcGRmc1sxOjI1LF0pKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9ICF0MSwgbmFtZXNfdG89ImdycCIsIHZhbHVlc190bz0icCIpCmNvbmRtdSA8LSBnZ3Bsb3QoZGF0YSA9IGRmbTI1KSArCiAgZ2VvbV9saW5lKGFlcyh0MSwgcCwgZ3JvdXAgPSBncnApLCBsaW5ldHlwZSA9ICdkYXNoZWQnKSArCiAgbGFicyh0aXRsZSA9ICdDb25kaXRpb25hbCBkaXN0cmlidXRpb24gb2YgbXUgZm9yIGZpcnN0IDI1IHNhbXBsZXMnLCB5ID0gJycsIHggPSAnJykgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBOVUxMKQpgYGAKCmNyZWF0ZSBhIHBsb3Qgb2YgdGhlaXIgbWVhbgoKYGBge3IgfQpkZnNhbSA8LSBkYXRhLmZyYW1lKHQxLCBjb2xNZWFucyhjb25kcGRmcyksIHBtKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9ICF0MSwgbmFtZXNfdG89ImdycCIsIHZhbHVlc190bz0icCIpCiMgbGFiZWxzCm11bGFicyA8LSBjKCdhdmcgb2Ygc2FtcGxlZCBjb25kcycsICdleGFjdCBtYXJnaW5hbCBvZiBtdScpCm1lYW5tdSA8LSBnZ3Bsb3QoZGF0YSA9IGRmc2FtKSArCiAgZ2VvbV9saW5lKGFlcyh0MSwgcCwgc2l6ZSA9IGdycCwgY29sb3IgPSBncnApKSArCiAgbGFicyh5ID0gJycsIHggPSAnbXUnKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IE5VTEwpICsKICBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXMgPSBjKDIsIDAuOCksIGxhYmVscyA9IG11bGFicykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCdvcmFuZ2UnLCAnYmxhY2snKSwgbGFiZWxzID0gbXVsYWJzKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjgsIDAuOCksCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCkNvbWJpbmUgdGhlIHBsb3RzCgpgYGB7ciB9CmdyaWQuYXJyYW5nZShjb25kbXUsIG1lYW5tdSwgbmNvbCA9IDEpCmBgYAoKIyMjIERlbW8gMy40IFZpc3VhbGlzZSBwb3N0ZXJpb3IgcHJlZGljdGl2ZSBkaXN0cmlidXRpb24uClZpc3VhbGlzZSBzYW1wbGluZyBmcm9tIHRoZSBwb3N0ZXJpb3IgcHJlZGljdGl2ZSBkaXN0cmlidXRpb24uCkNhbGN1bGF0ZSBlYWNoIHByZWRpY3RpdmUgcGRmIHdpdGggZ2l2ZW4gbXUgYW5kIHNpZ21hCgpgYGB7ciB9CnluZXdkaXN0cyA8LSBzYXBwbHkoeHluZXcsIGZ1bmN0aW9uKHgpIGRub3JtKHgsIG11LCBzaWdtYSkpCmBgYAoKQ3JlYXRlIHBsb3Qgb2YgdGhlIGpvaW50IHBvc3RlcmlvciB3aXRoIGEgZHJhdwpmcm9tIHRoZSBwb3N0ZXJpb3IgcHJlZGljdGl2ZSBkaXN0cmlidXRpb24sIGhpZ2hsaWdodCB0aGUgZmlyc3Qgc2FtcGxlCmNyZWF0ZSBhIHBsb3Qgb2YgdGhlIGpvaW50IGRlbnNpdHkKCmBgYHtyIH0KIyBkYXRhIGZyYW1lIG9mIGRpcnN0IGRyYXcgZnJvbSBzYW1wbGUgdGhlIHByZWRpY3RpdmUgYWxvbmcgd2l0aCB0aGUgZXhhY3QgdmFsdWUgZm9yIHBsb3R0aW5nCmRmcCA8LSBkYXRhLmZyYW1lKHh5bmV3LCBkcmF3ID0geW5ld2Rpc3RzWzEsXSwgZXhhY3QgPSBwX25ldykKIyBkYXRhIGZyYW1lIGZvciBwbG90dGluZyB0aGUgc2FtcGxlcwpkZnkgPC0gZGF0YS5mcmFtZSh5bmV3LCBwID0gMC4wMiptYXgoeW5ld2Rpc3RzKSkKIyBsZWdlbmQgbGFiZWxzCnByZWQxbGFicyA8LSBjKCdTYW1wbGUgZnJvbSB0aGUgcHJlZGljdGl2ZSBkaXN0cmlidXRpb24nLCAnUHJlZGljdGl2ZSBkaXN0cmlidXRpb24gZ2l2ZW4gdGhlIHBvc3RlcmlvciBzYW1wbGUnKQpwcmVkMmxhYnMgPC0gYygnU2FtcGxlcyBmcm9tIHRoZSBwcmVkaWN0aXZlIGRpc3RyaWJ1dGlvbicsICdFeGFjdCBwcmVkaWN0aXZlIGRpc3RyaWJ1dGlvbicpCmpvaW50M2xhYnMgPC0gYygnU2FtcGxlcycsICdFeGFjdCBjb250b3VyJykKam9pbnQzIDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkYXRhLmZyYW1lKG11LCBzaWdtYSksIGFlcyhtdSwgc2lnbWEsIGNvbCA9ICcxJyksIHNpemUgPSAwLjEpICsKICBnZW9tX2NvbnRvdXIoZGF0YSA9IGRmaiwgYWVzKHQxLCB0MiwgeiA9IHosIGNvbCA9ICcyJyksIGJyZWFrcyA9IGNsKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZGF0YS5mcmFtZSh4ID0gbXVbMV0sIHkgPSBzaWdtYVsxXSksIGFlcyh4LCB5KSwgY29sb3IgPSAncmVkJykgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gdDFsLHlsaW0gPSB0MmwpICsKICBsYWJzKHRpdGxlID0gJ0pvaW50IHBvc3RlcmlvcicsIHggPSAnbXUnLCB5ID0gJ3NpZ21hJykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygnYmx1ZScsICdibGFjaycpLCBsYWJlbHMgPSBqb2ludDNsYWJzKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5yb3cgID0gMSwgb3ZlcnJpZGUuYWVzID0gbGlzdCgKICAgIHNoYXBlID0gYygxNiwgTkEpLCBsaW5ldHlwZSA9IGMoMCwgMSksIHNpemUgPSBjKDIsIDEpKSkpICsKICB0aGVtZShsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb249YygwLjUgLDAuOSksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCkNyZWF0ZSBhIHBsb3Qgb2YgdGhlIHByZWRpY2l0aXZlIGRpc3RyaWJ1dGlvbiBhbmQgdGhlIHJlc3BlY3RpdmUgc2FtcGxlCgpgYGB7ciB9CnByZWQxIDwtIGdncGxvdCgpICsKICBnZW9tX2xpbmUoZGF0YSA9IGRmcCwgYWVzKHh5bmV3LCBkcmF3LCBjb2xvciA9ICcyJykpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkZnksIGFlcyh5bmV3WzFdLCBwLCBjb2xvciA9ICcxJykpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IG5sLCB5bGltID0gYygwLDAuMDQpKSArCiAgbGFicyh0aXRsZSA9ICdQb3N0ZXJpb3IgcHJlZGljdGl2ZSBkaXN0cmlidXRpb24nLCB4ID0gJ3l0aWxkZScsIHkgPSAnJykgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBOVUxMKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoJ3JlZCcsICdibHVlJyksIGxhYmVscyA9IHByZWQxbGFicykgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChucm93ID0gMiwgb3ZlcnJpZGUuYWVzID0gbGlzdCgKICAgIGxpbmV0eXBlID0gYygwLCAxKSwgc2hhcGU9YygxNiwgTkEpLCBsYWJlbHMgPSBwcmVkMWxhYnMpKSkgKwogIHRoZW1lKGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC41ICwwLjkpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpDcmVhdGUgYSBwbG90IGZvciBhbGwgeW5ld3MKCmBgYHtyIH0KcHJlZDIgPC0gZ2dwbG90KCkgKwogIGdlb21fbGluZShkYXRhID0gZGZwLCBhZXMoeHluZXcsIGRyYXcsIGNvbG9yID0gJzInKSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGRmeSwgYWVzKHluZXcsIHAsIGNvbG9yID0gJzEnKSwgYWxwaGEgPSAwLjA1KSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBubCwgeWxpbSA9IGMoMCwwLjA0KSkgKwogIGxhYnMoeCA9ICd5dGlsZGUnLCB5ID0gJycpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPU5VTEwpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ2RhcmtncmVlbicsJ2JsdWUnKSxsYWJlbHM9cHJlZDJsYWJzKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5yb3cgPSAyLCBvdmVycmlkZS5hZXM9bGlzdCgKICAgIGxpbmV0eXBlID0gYygwLCAxKSwgc2hhcGUgPSBjKDE2LCBOQSksIGFscGhhID0gYygxLCAxKSAsbGFiZWxzID0gcHJlZDJsYWJzKSkpICsKICB0aGVtZShsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuNSAsMC45KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKQ29tYmluZSB0aGUgcGxvdHMKCmBgYHtyIH0KZ3JpZC5hcnJhbmdlKGpvaW50MywgcHJlZDEsIGJwLCBwcmVkMiwgbnJvdyA9IDIpCmBgYAoK