Though a variety of R packages provide good performance of Bayesian Linear Regression, I feel a strong urge to implement it by plain code from scatch. This is an example of reproduction bayes.regress() from BayesSummaryStatLM package. One should fully understand how the math behand works after the practice.

Theory

Conditional posterior distributions regarding \(\beta\) prior

Conditional posterior distributions regarding \(σ^2\) prior

Implementation in R

Data preparation

set.seed(1234)
num.samp  <- 100 # number of data values to simulate
# The first value of the beta vector is the y-intercept:
beta <- c(-0.35, 0.88, -0.46, 0.43, -1.71)
# Calculate the number of predictor variables:
num.pred <- length(beta)-1
rho         <- 0.0  # correlation between predictors
mean.vec    <- rep(0,num.pred)
sigma.mat   <- matrix(rho,num.pred,num.pred) + diag(1-rho,num.pred)
sigmasq.sim <- 0.05
# Simulate predictor variables:
# library(MASS)
x.pre       <- mvrnorm(num.samp, mu=mean.vec, Sigma=sigma.mat)
# Add leading column of 1's to x.pre for y-intercept:
x <- cbind(rep(1,num.samp),x.pre)
epsilon <- rnorm(num.samp, mean=0, sd=sqrt(sigmasq.sim))
y  <- as.numeric( x %*% as.matrix(beta) +  epsilon)
# See the data
plot(y)
for(i in 2:5){
  points(x[,i],col=i)
}

Set up

# Set the number of MCMC samples
Tsamp.out <- 100
Tsamp.out <- Tsamp.out+1
## Compute summary statistics
xtx <- t(x)%*%x 
xty <- t(x)%*%y 
yty <- t(y)%*%y 
ytx<-t(xty)
xtx.inv = chol2inv(chol(xtx)) # compute (X'X)^(-1)
# num.predictors <- dim(xtx)[1]
betahat <- matrix(NA, nrow=Tsamp.out, ncol = dim(xtx)[1]) # betahat = 101 \times 5
sigmasqhat <- rep(NA,Tsamp.out) # sigmasqhat = 101

Priors

\(\hat{\beta}\): flat; \(\hat{\sigma}^2\): sigmasq.inverse

Start

# set starting value for sigmasqhat
sigmasq.init = 0.1; 
sigmasqhat[1] <- sigmasq.init
# posterior mean of betahat
betahat.mean <- xtx.inv%*%(xty)

Posterior variance of betahat \(\hat{\beta}\)

# posterior variance of betahat
for (i in 2:Tsamp.out){
# simulate betahat
betahat[i,] <- mvrnorm(n=1, mu=betahat.mean, Sigma=sigmasqhat[i-1]*xtx.inv)
# simulate sigmasqhat
sigmasqscale.pre <- (yty - t(betahat[i,]) %*% xty - 
ytx %*% betahat[i,] + t(betahat[i,]) %*% xtx %*% betahat[i,])
sigmasqhat[i] <- 1/rgamma(1,shape=num.samp/2,scale=(sigmasqscale.pre/2)^(-1))
} # end i

Remove starting value for sigmasqhat and NA for starting value of betahat

betahat    <- betahat[-1,]
sigmasqhat <- sigmasqhat[-1]
# Compare the true value and the simulated one
paste("beta =", paste(beta,collapse = " "))
[1] "beta = -0.35 0.88 -0.46 0.43 -1.71"
paste("beta.mean =", paste(round(betahat.mean,2),collapse = " "))
[1] "beta.mean = -0.36 0.86 -0.45 0.43 -1.73"
paste("betahat =", paste(round(betahat[num.samp,],2),collapse = " "))
[1] "betahat = -0.35 0.87 -0.41 0.46 -1.76"

\(\hat{\beta}\): mvnorm.known; \(\hat{\sigma}^2\): sigmasq.inverse

Start

# num.predictors <- dim(xtx)[1]
betahat <- matrix(NA, nrow=Tsamp.out, ncol = dim(xtx)[1]) # betahat = 101 \times 5
sigmasqhat <- rep(NA,Tsamp.out) # sigmasqhat = 101
# set starting value for sigmasqhat
sigmasq.init = 1.0; 
beta.prior.mean=rep(0,dim(xtx)[1])
beta.prior.var=diag(dim(xtx)[1])
beta.prior.var.inv=chol2inv(chol(beta.prior.var))
sigmasqhat[1] <- sigmasq.init
betahat.pre1 <- beta.prior.var.inv  # C^{-1}
betahat.pre2 <- betahat.pre1 %*% beta.prior.mean # C^{-1} %*% μ

Posterior variance of betahat \(\hat\beta\)

for (i in 2:Tsamp.out){
betahat.var <- chol2inv(chol((betahat.pre1+(1/sigmasqhat[i-1]) * xtx))) # covariance=(C^{-1}+σ^{-2}(X'X)^{-1})
betahat.mean <- betahat.var %*% (betahat.pre2 + (1/sigmasqhat[i-1]) * xty) # mean=(C^{-1}+σ^{-2}(X'X))^{-1}(C^{-1}μ + σ^{-2}X'Y)
betahat[i,] <- mvrnorm(n=1, mu = betahat.mean,Sigma = betahat.var)
# simulate sigmasqhat
sigmasqscale.pre <- (yty - t(betahat[i,]) %*% xty - 
ytx %*% betahat[i,] + t(betahat[i,]) %*% xtx %*% betahat[i,]) # (Y'Y-β'X'Y-Y'Xβ+β'X'Xβ)
sigmasqhat[i] <- 1/rgamma(1,shape=num.samp/2,scale=(sigmasqscale.pre/2)^(-1)) # Inverse Gamma (numsamp.data/2, (0.5(Y'Y-β'X'Y-Y'Xβ+β'X'Xβ))^{-1})
}  # end

Remove starting value for sigmasqhat and NA for starting value of betahat

betahat    <- betahat[-1,]
sigmasqhat <- sigmasqhat[-1]
# Compare the true value and the simulated one
paste("beta =", paste(beta,collapse = " "))
[1] "beta = -0.35 0.88 -0.46 0.43 -1.71"
paste("beta.mean =", paste(round(betahat.mean,2),collapse = " "))
[1] "beta.mean = -0.36 0.86 -0.45 0.43 -1.72"
paste("betahat =", paste(round(betahat[num.samp,],2),collapse = " "))
[1] "betahat = -0.41 0.89 -0.44 0.43 -1.76"
LS0tCnRpdGxlOiAiQmF5ZXNpYW4gcmVncmVzc2lvbiB3aXRoIE1DIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tClRob3VnaCBhIHZhcmlldHkgb2YgUiBwYWNrYWdlcyBwcm92aWRlIGdvb2QgcGVyZm9ybWFuY2Ugb2YgQmF5ZXNpYW4gTGluZWFyIFJlZ3Jlc3Npb24sIEkgZmVlbCBhIHN0cm9uZyB1cmdlIHRvIGltcGxlbWVudCBpdCBieSBwbGFpbiBjb2RlIGZyb20gc2NhdGNoLiBUaGlzIGlzIGFuIGV4YW1wbGUgb2YgcmVwcm9kdWN0aW9uIFtiYXllcy5yZWdyZXNzKCldKGh0dHBzOi8vcmRyci5pby9jcmFuL0JheWVzU3VtbWFyeVN0YXRMTS9tYW4vYmF5ZXMucmVncmVzcy5odG1sKSBmcm9tICpCYXllc1N1bW1hcnlTdGF0TE0gKiBwYWNrYWdlLiBPbmUgc2hvdWxkIGZ1bGx5IHVuZGVyc3RhbmQgaG93IHRoZSBtYXRoIGJlaGFuZCB3b3JrcyBhZnRlciB0aGUgcHJhY3RpY2UuCgojIFRoZW9yeQoKPiA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWU7Ij5Db25kaXRpb25hbCBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9ucyByZWdhcmRpbmcgJFxiZXRhJCBwcmlvcjwvc3Bhbj4KCi0gPHNwYW4gc3R5bGU9ImNvbG9yOiBncmVlbjsiPmJldGEucHJpb3I9bGlzdCh0eXBlID0gImZsYXQiKTwvc3Bhbj4uIEEgVW5pZm9ybSBwcmlvciBkaXN0cmlidXRpb24gZm9yICRcYmV0YSQuCgogICAgLSAkXGJldGEgfCBcc2lnbWFeMiwgWCwgWSBcc2ltIE5vcm1hbF97aysxfSAobWVhbj0oKFgnWCleey0xfShYJ1kpLCBjb3ZhcmlhbmNlPShcc2lnbWFeMihYJ1gpXnstMX0pKSkkCgotIDxzcGFuIHN0eWxlPSJjb2xvcjogZ3JlZW47Ij5iZXRhLnByaW9yPWxpc3QodHlwZSA9ICJtdm5vcm0ua25vd24iKTwvc3Bhbj4uIE11bHRpdmFyaWF0ZSBOb3JtYWwgd2l0aCBrbm93biBtZWFuIHZlY3RvciAkXG11JCBhbmQga25vd24gY292YXJpYW5jZSBtYXRyaXggJEMkLgoKICAgIC0gJFxiZXRhIHwgXHNpZ21hXjIsIFgsIFkgXHNpbSBOb3JtYWxfe2srMX0gKG1lYW49KENeey0xfSvPg157LTJ9KFgnWCkpXnstMX0oQ157LTF9XG11ICsgXHNpZ21hXnstMn1YJ1kpLGNvdmFyaWFuY2U9KENeey0xfStcc2lnbWFeey0yfShYJ1gpXnstMX0pKSQKCi0gPHNwYW4gc3R5bGU9ImNvbG9yOiBncmVlbjsiPmJldGEucHJpb3I9bGlzdCh0eXBlID0gIm12bm9ybS51bmtub3duIik8L3NwYW4+LiBNdWx0aXZhcmlhdGUgTm9ybWFsIHdpdGggdW5rbm93biBtZWFuIHZlY3RvciDOvCBhbmQgdW5rbm93biBjb3ZhcmlhbmNlIG1hdHJpeCBDLiBUaGlzIHByaW9yIGFsc28gaW5jbHVkZXMgdGhlIGh5cGVycHJpb3JzIGZvciAkXG11JCBhbmQgJEMkLCB3aGVyZSAkXG11IFxzaW0gTXVsdGl2YXJpYXRlIE5vcm1hbCAoXGV0YSwgRCkkLCBhbmQgJENeey0xfSBcc2ltIFdpc2hhcnQoZC5mLiA9IFxsYW1iZGEsIHNjYWxlIG1hdHJpeCA9IFYpJDsgJFxldGEsIEQsIFxsYW1iZGEsIFYkIGFzc3VtZWQga25vd24uCgogICAgLSAkXGJldGEgfCBcc2lnbWFeMiwgXG11LCBDXnstMX0sIFgsIFkgXHNpbSBOb3JtYWxfe2srMX0obWVhbj0oQ157LTF9K1xzaWdtYV57LTJ9KFgnWCkpXnstMX0oQ157LTF9XG11ICtcc2lnbWFeey0yfVgnWSksY292YXJpYW5jZT0oQ157LTF9K1xzaWdtYV57LTJ9KFgnWCleey0xfSkpJAoKICAgIC0gJFxtdSB8IFxiZXRhLCBcc2lnbWFeMiwgQ157LTF9LCBYLCBZIFxzaW0gTm9ybWFsX3trKzF9IChtZWFuPShEXnstMX0rQ157LTF9KV57LTF9KENeey0xfVxiZXRhK0Reey0xfVxldGEpLCBjb3ZhcmlhbmNlPShEXnstMX0rQ157LTF9KV57LTF9KSQKCiAgICAtICRDXnstMX0gfCBcYmV0YSwgXHNpZ21hXjIsIFxtdSwgWCwgWSBcc2ltIFdpc2hhcnRfe2srMX0gKGQuZi4gPSAoMStcbGFtYmRhKSwgc2NhbGUgbWF0cml4ID0gKFZeey0xfSsgKFxiZXRhIC0gXG11KShcYmV0YSAtIFxtdSknKV57LTF9KSQKCj4gPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+Q29uZGl0aW9uYWwgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbnMgcmVnYXJkaW5nICTPg14yJCBwcmlvcjwvc3Bhbj4KCi0gPHNwYW4gc3R5bGU9ImNvbG9yOiBncmVlbjsiPnNpZ21hc3EucHJpb3I9bGlzdCh0eXBlID0gImludmVyc2UuZ2FtbWEiKTwvc3Bhbj4uIEludmVyc2UgR2FtbWEgZGlzdHJpYnV0aW9uIHdpdGgga25vd24gc2hhcGUgYW5kIHNjYWxlIHBhcmFtZXRlcnMgJGEkIGFuZCAkYiQsIHJlc3BlY3RpdmVseS4KCiAgICAtICRcc2lnbWFeMiB8IFxiZXRhLCBYLCBZIH4gSW52ZXJzZSBHYW1tYSAobnVtLnNhbXAvMithLCAoMC41KFknWS1cYmV0YSdYJ1ktWSdYXGJldGErXGJldGEnWCdYXGJldGEpK1xmcmFjezF9e2J9KV57LTF9KSQKCi0gPHNwYW4gc3R5bGU9ImNvbG9yOiBncmVlbjsiPnNpZ21hc3EucHJpb3I9bGlzdCh0eXBlID0gInNpZ21hc3EuaW52ZXJzZSIpPC9zcGFuPi4gSW52ZXJzZSBzaWdtYS1zcXVhcmVkIGRpc3RyaWJ1dGlvbi4KCiAgICAtICRcc2lnbWFeMiB8IFxiZXRhLCBYLCBZIFxzaW0gSW52ZXJzZSBHYW1tYSAobnVtLnNhbXAvMiwgKDAuNShZJ1ktXGJldGEnWCdZLVknWFxiZXRhK1xiZXRhJ1gnWFxiZXRhKSleey0xfSkkCgojIEltcGxlbWVudGF0aW9uIGluIFIKCiMjIERhdGEgcHJlcGFyYXRpb24KYGBge3J9CnNldC5zZWVkKDEyMzQpCgpudW0uc2FtcCAgPC0gMTAwICMgbnVtYmVyIG9mIGRhdGEgdmFsdWVzIHRvIHNpbXVsYXRlCgojIFRoZSBmaXJzdCB2YWx1ZSBvZiB0aGUgYmV0YSB2ZWN0b3IgaXMgdGhlIHktaW50ZXJjZXB0OgpiZXRhIDwtIGMoLTAuMzUsIDAuODgsIC0wLjQ2LCAwLjQzLCAtMS43MSkKCiMgQ2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgcHJlZGljdG9yIHZhcmlhYmxlczoKbnVtLnByZWQgPC0gbGVuZ3RoKGJldGEpLTEKCnJobyAgICAgICAgIDwtIDAuMCAgIyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHByZWRpY3RvcnMKbWVhbi52ZWMgICAgPC0gcmVwKDAsbnVtLnByZWQpCnNpZ21hLm1hdCAgIDwtIG1hdHJpeChyaG8sbnVtLnByZWQsbnVtLnByZWQpICsgZGlhZygxLXJobyxudW0ucHJlZCkKc2lnbWFzcS5zaW0gPC0gMC4wNQoKIyBTaW11bGF0ZSBwcmVkaWN0b3IgdmFyaWFibGVzOgojIGxpYnJhcnkoTUFTUykKeC5wcmUgICAgICAgPC0gbXZybm9ybShudW0uc2FtcCwgbXU9bWVhbi52ZWMsIFNpZ21hPXNpZ21hLm1hdCkKCiMgQWRkIGxlYWRpbmcgY29sdW1uIG9mIDEncyB0byB4LnByZSBmb3IgeS1pbnRlcmNlcHQ6CnggPC0gY2JpbmQocmVwKDEsbnVtLnNhbXApLHgucHJlKQoKZXBzaWxvbiA8LSBybm9ybShudW0uc2FtcCwgbWVhbj0wLCBzZD1zcXJ0KHNpZ21hc3Euc2ltKSkKCnkgIDwtIGFzLm51bWVyaWMoIHggJSolIGFzLm1hdHJpeChiZXRhKSArICBlcHNpbG9uKQoKIyBTZWUgdGhlIGRhdGEKcGxvdCh5KQpmb3IoaSBpbiAyOjUpewogIHBvaW50cyh4WyxpXSxjb2w9aSkKfQpgYGAKCiMjIFNldCB1cApgYGB7cn0KIyBTZXQgdGhlIG51bWJlciBvZiBNQ01DIHNhbXBsZXMKVHNhbXAub3V0IDwtIDEwMApUc2FtcC5vdXQgPC0gVHNhbXAub3V0KzEKCiMjIENvbXB1dGUgc3VtbWFyeSBzdGF0aXN0aWNzCnh0eCA8LSB0KHgpJSoleCAKeHR5IDwtIHQoeCklKiV5IAp5dHkgPC0gdCh5KSUqJXkgCnl0eDwtdCh4dHkpCnh0eC5pbnYgPSBjaG9sMmludihjaG9sKHh0eCkpICMgY29tcHV0ZSAoWCdYKV4oLTEpCmBgYAoKIyMgUHJpb3JzCgojIyMgJFxoYXR7XGJldGF9JDogZmxhdDsgJFxoYXR7XHNpZ21hfV4yJDogc2lnbWFzcS5pbnZlcnNlCgpTdGFydApgYGB7cn0KIyBudW0ucHJlZGljdG9ycyA8LSBkaW0oeHR4KVsxXQpiZXRhaGF0IDwtIG1hdHJpeChOQSwgbnJvdz1Uc2FtcC5vdXQsIG5jb2wgPSBkaW0oeHR4KVsxXSkgIyBiZXRhaGF0ID0gMTAxIFx0aW1lcyA1CnNpZ21hc3FoYXQgPC0gcmVwKE5BLFRzYW1wLm91dCkgIyBzaWdtYXNxaGF0ID0gMTAxCgojIHNldCBzdGFydGluZyB2YWx1ZSBmb3Igc2lnbWFzcWhhdApzaWdtYXNxLmluaXQgPSAwLjE7IApzaWdtYXNxaGF0WzFdIDwtIHNpZ21hc3EuaW5pdAoKIyBwb3N0ZXJpb3IgbWVhbiBvZiBiZXRhaGF0CmJldGFoYXQubWVhbiA8LSB4dHguaW52JSolKHh0eSkKYGBgCgpQb3N0ZXJpb3IgdmFyaWFuY2Ugb2YgYmV0YWhhdCAkXGhhdHtcYmV0YX0kCgpgYGB7cn0KIyBwb3N0ZXJpb3IgdmFyaWFuY2Ugb2YgYmV0YWhhdAoKZm9yIChpIGluIDI6VHNhbXAub3V0KXsKCiMgc2ltdWxhdGUgYmV0YWhhdApiZXRhaGF0W2ksXSA8LSBtdnJub3JtKG49MSwgbXU9YmV0YWhhdC5tZWFuLCBTaWdtYT1zaWdtYXNxaGF0W2ktMV0qeHR4LmludikKCiMgc2ltdWxhdGUgc2lnbWFzcWhhdApzaWdtYXNxc2NhbGUucHJlIDwtICh5dHkgLSB0KGJldGFoYXRbaSxdKSAlKiUgeHR5IC0gCnl0eCAlKiUgYmV0YWhhdFtpLF0gKyB0KGJldGFoYXRbaSxdKSAlKiUgeHR4ICUqJSBiZXRhaGF0W2ksXSkKCnNpZ21hc3FoYXRbaV0gPC0gMS9yZ2FtbWEoMSxzaGFwZT1udW0uc2FtcC8yLHNjYWxlPShzaWdtYXNxc2NhbGUucHJlLzIpXigtMSkpCgp9ICMgZW5kIGkKYGBgClJlbW92ZSBzdGFydGluZyB2YWx1ZSBmb3Igc2lnbWFzcWhhdCBhbmQgTkEgZm9yIHN0YXJ0aW5nIHZhbHVlIG9mIGJldGFoYXQKYGBge3J9CmJldGFoYXQgICAgPC0gYmV0YWhhdFstMSxdCnNpZ21hc3FoYXQgPC0gc2lnbWFzcWhhdFstMV0KIyBDb21wYXJlIHRoZSB0cnVlIHZhbHVlIGFuZCB0aGUgc2ltdWxhdGVkIG9uZQpwYXN0ZSgiYmV0YSA9IiwgcGFzdGUoYmV0YSxjb2xsYXBzZSA9ICIgIikpCnBhc3RlKCJiZXRhLm1lYW4gPSIsIHBhc3RlKHJvdW5kKGJldGFoYXQubWVhbiwyKSxjb2xsYXBzZSA9ICIgIikpCnBhc3RlKCJiZXRhaGF0ID0iLCBwYXN0ZShyb3VuZChiZXRhaGF0W251bS5zYW1wLF0sMiksY29sbGFwc2UgPSAiICIpKQpgYGAKCgojIyMgJFxoYXR7XGJldGF9JDogbXZub3JtLmtub3duOyAkXGhhdHtcc2lnbWF9XjIkOiBzaWdtYXNxLmludmVyc2UKClN0YXJ0CmBgYHtyfQojIG51bS5wcmVkaWN0b3JzIDwtIGRpbSh4dHgpWzFdCmJldGFoYXQgPC0gbWF0cml4KE5BLCBucm93PVRzYW1wLm91dCwgbmNvbCA9IGRpbSh4dHgpWzFdKSAjIGJldGFoYXQgPSAxMDEgXHRpbWVzIDUKc2lnbWFzcWhhdCA8LSByZXAoTkEsVHNhbXAub3V0KSAjIHNpZ21hc3FoYXQgPSAxMDEKCiMgc2V0IHN0YXJ0aW5nIHZhbHVlIGZvciBzaWdtYXNxaGF0CnNpZ21hc3EuaW5pdCA9IDEuMDsgCmJldGEucHJpb3IubWVhbj1yZXAoMCxkaW0oeHR4KVsxXSkKYmV0YS5wcmlvci52YXI9ZGlhZyhkaW0oeHR4KVsxXSkKYmV0YS5wcmlvci52YXIuaW52PWNob2wyaW52KGNob2woYmV0YS5wcmlvci52YXIpKQoKc2lnbWFzcWhhdFsxXSA8LSBzaWdtYXNxLmluaXQKYmV0YWhhdC5wcmUxIDwtIGJldGEucHJpb3IudmFyLmludiAgIyBDXnstMX0KYmV0YWhhdC5wcmUyIDwtIGJldGFoYXQucHJlMSAlKiUgYmV0YS5wcmlvci5tZWFuICMgQ157LTF9ICUqJSDOvApgYGAKClBvc3RlcmlvciB2YXJpYW5jZSBvZiBiZXRhaGF0ICRcaGF0XGJldGEkCmBgYHtyfQpmb3IgKGkgaW4gMjpUc2FtcC5vdXQpewoKYmV0YWhhdC52YXIgPC0gY2hvbDJpbnYoY2hvbCgoYmV0YWhhdC5wcmUxKygxL3NpZ21hc3FoYXRbaS0xXSkgKiB4dHgpKSkgIyBjb3ZhcmlhbmNlPShDXnstMX0rz4Neey0yfShYJ1gpXnstMX0pCgpiZXRhaGF0Lm1lYW4gPC0gYmV0YWhhdC52YXIgJSolIChiZXRhaGF0LnByZTIgKyAoMS9zaWdtYXNxaGF0W2ktMV0pICogeHR5KSAjIG1lYW49KENeey0xfSvPg157LTJ9KFgnWCkpXnstMX0oQ157LTF9zrwgKyDPg157LTJ9WCdZKQoKYmV0YWhhdFtpLF0gPC0gbXZybm9ybShuPTEsIG11ID0gYmV0YWhhdC5tZWFuLFNpZ21hID0gYmV0YWhhdC52YXIpCgojIHNpbXVsYXRlIHNpZ21hc3FoYXQKCnNpZ21hc3FzY2FsZS5wcmUgPC0gKHl0eSAtIHQoYmV0YWhhdFtpLF0pICUqJSB4dHkgLSAKeXR4ICUqJSBiZXRhaGF0W2ksXSArIHQoYmV0YWhhdFtpLF0pICUqJSB4dHggJSolIGJldGFoYXRbaSxdKSAjIChZJ1ktzrInWCdZLVknWM6yK86yJ1gnWM6yKQoKc2lnbWFzcWhhdFtpXSA8LSAxL3JnYW1tYSgxLHNoYXBlPW51bS5zYW1wLzIsc2NhbGU9KHNpZ21hc3FzY2FsZS5wcmUvMileKC0xKSkgIyBJbnZlcnNlIEdhbW1hIChudW1zYW1wLmRhdGEvMiwgKDAuNShZJ1ktzrInWCdZLVknWM6yK86yJ1gnWM6yKSleey0xfSkKCn0gICMgZW5kCmBgYAoKUmVtb3ZlIHN0YXJ0aW5nIHZhbHVlIGZvciBzaWdtYXNxaGF0IGFuZCBOQSBmb3Igc3RhcnRpbmcgdmFsdWUgb2YgYmV0YWhhdApgYGB7cn0KYmV0YWhhdCAgICA8LSBiZXRhaGF0Wy0xLF0Kc2lnbWFzcWhhdCA8LSBzaWdtYXNxaGF0Wy0xXQoKIyBDb21wYXJlIHRoZSB0cnVlIHZhbHVlIGFuZCB0aGUgc2ltdWxhdGVkIG9uZQpwYXN0ZSgiYmV0YSA9IiwgcGFzdGUoYmV0YSxjb2xsYXBzZSA9ICIgIikpCnBhc3RlKCJiZXRhLm1lYW4gPSIsIHBhc3RlKHJvdW5kKGJldGFoYXQubWVhbiwyKSxjb2xsYXBzZSA9ICIgIikpCnBhc3RlKCJiZXRhaGF0ID0iLCBwYXN0ZShyb3VuZChiZXRhaGF0W251bS5zYW1wLF0sMiksY29sbGFwc2UgPSAiICIpKQpgYGAKCgoKCgoKCgoK