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

# helper functions to sample from and evaluate
# scaled inverse chi-squared distribution
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

# multiplication by 1./sqrt(s2/n) is due to the transformation of
# variable z=(x-mean(y))/sqrt(s2/n), see BDA3 p. 21
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

# the multiplication by 2*t2 is due to the transformation of
# variable z=t2^2, see BDA3 p. 21
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

# multiplication by 1./sqrt(s2/n) is due to the transformation of variable
# see BDA3 p. 21
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.

# Combine grid points into another data frame
# with all pairwise combinations
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))
# breaks for plotting the contours
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

# blank plot for combining 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

# data frame for the conditional of mu and marginal of sigma
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")
# legend labels for the following plot
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

# data frame of 25 first samples
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")
# labels
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

# data frame of dirst draw from sample the predictive along with the exact value for plotting
dfp <- data.frame(xynew, draw = ynewdists[1,], exact = p_new)
# data frame for plotting the samples
dfy <- data.frame(ynew, p = 0.02*max(ynewdists))
# legend labels
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