Metropolis algorithm + Rhat (PSRF) demonstration

ggplot2 and gridExtra are used for plotting, tidyr for manipulating data frames

library(ggplot2)
theme_set(theme_minimal())
library(gridExtra)
library(tidyverse)
library(gganimate)
library(MASS)
library(rstan)
library(rprojroot)
root<-has_file(".BDA_R_demos_root")$make_fix_file()

Parameters of a Normal distribution used as a toy target distribution

y1 <- 0
y2 <- 0
r <- 0.8
S <- diag(2)
S[1, 2] <- r
S[2, 1] <- r

Sample from the toy distribution to visualize 90% HPD interval with ggplot’s stat_ellipse()

dft <- data.frame(mvrnorm(100000, c(0, 0), S))

Load pre-run Metropolis chains.

Since, implementation of the Metropolis algorithm is one of the exercises, we load here pre-computed chains and Rhat-values

The proposal distribution was intentionally selected to be slightly too small, to better illustrate convergence diagonstics

tts' contains draws,p1’ and ‘p2’ contain Rhat values for t1 and t2 using 50% warm-up, `pp1’ and ‘pp2’ contain Rhat values for t1 and t2 using 10% warm-up, Rhat-values have been computed for each time-step.

load(root("demos_ch11","demo11_4.RData"))

Transform the first s1 rows of the data into a ‘tidy’ format for plotting

s1 <- 50
dfs1 <- data.frame(iter = 1:s1, tts[1:s1, 1, ]) %>%
  gather(chain, th1, -iter) %>%
  within({th2 <- c(tts[1:s1, 2, ])         # xl and yl specify the previous
          th2l <- c(th2[1], th2[-length(th2)])   # draw in the chain for
          th1l <- c(th1[1], th1[-length(th1)])}) # plotting

Fix the incorrect lagged values, the lagged value of the first draw in the chain (for plotting) is the value itself (instead of the last value of the previous chain)

sind <- 0:9*s1+1
dfs1[sind, c('th1l','th2l')] <- dfs1[sind, c('th1','th2')]

Another data frame with all draws

inds2 <- 1:10000
dfs2 <- data.frame(iter = inds2, tts[inds2, 1, ]) %>%
  gather(chain, theta1, -iter) %>%
  within(theta2 <- c(tts[inds2, 2, ])) %>%
  gather(var, val, -iter, -chain)

Third data frame with Rhat values

indsp <- seq(10, length(p1), 10)
dfp <- data.frame(iter = indsp,
                  theta1 = p1[indsp],
                  theta2 = p2[indsp]) %>%
  gather(var,rhat,-iter) 

Construct a 2d-plot of the 50 first iterations of the chains

frame = rep(1:s1, 10)
chains1 <- ggplot(data = dfs1) +
  geom_segment(aes(x = th1, xend = th1l, y = th2, yend = th2l, color = chain,
                   group = chain)) +
  geom_point(data = dfs1[sind, ], aes(x = th1, y = th2, color = chain)) +
  stat_ellipse(data = dft, aes(x = X1, y = X2), level = 0.9, color = 'black') +
  coord_cartesian(xlim = c(-4, 4), ylim = c(-4, 4)) +
  labs(x = 'theta1', y = 'theta2') +
  scale_color_discrete(guide = "none")

Animate s1 first iterations of the chains. At some points some of the chains seem to halt for

#  a moment. What really happens at that point is that

they draw possibly a few points that are rejected (rejected points not shown) and thus the chain is not moving

anim <- animate(chains1 + 
                  transition_reveal(along=iter) + 
                  shadow_trail(1/s1))

Show the animation

anim

Plot the result, no convergence yet

chains1 + labs(title = 'No convergence')

Plot trends of the 50 first iterations

ggplot(data = filter(dfs2, iter<=50)) +
  geom_line(aes(iter, val, color = chain)) +
  facet_grid(var~.) +
  labs(title = 'Not converged', y = '') +
  scale_color_discrete(guide = "none")

Plot trends of the all draws

ggplot(data = filter(dfs2, iter>500)) +
  geom_line(aes(iter, val, color = chain)) +
  facet_grid(var~.) +
  labs(title = 'Visually converged', y = '') +
  scale_color_discrete(guide = "none")

Function for plotting components of Rhat

plotRhatcomps <- function(df = NULL, niter = NULL) {
  msdf <- df %>% filter(var=='theta1', iter<niter, iter>=niter/2) %>%
    group_by(chain) %>%
    summarise(mean=mean(val), sd=sd(val))
  ndf <-  msdf %>%
    pmap_df(~ data_frame(x = seq(-4, 4, length.out = 201), chain=..1,
                           density = dnorm(x, ..2, ..3)))
  # plot normal distribution approaximation for each chain to illustrate
  # means and variances
  p1 <- ggplot(ndf, aes(group = chain, x = x, y = density)) + 
    geom_line(linetype=1, color="blue") +
    labs(x='theta1', y='',
       title=paste(niter/2, ' warmup, ', niter/2, 'post warmup iterations')) +
    scale_y_continuous(breaks=NULL)
  # compute Rhat
  n <- niter/2
  B <- n*var(msdf$mean)
  W <- mean(msdf$sd^2)
  varp <- (n-1)/n*W + B/n
  Rhat <- sqrt(varp/W)
  # plot normal approximations to illustrate W, marginal variance estimate
  # using within variances, and var_hat_plus, marginal variance estimate
  # combining W and between chain variance estimate
  
  p2 <- ggplot(data.frame(x=c(-4, 4)), aes(x)) +
    stat_function(fun = dnorm, args = list(mean=0, sd=sqrt(varp)), color='red') +
    stat_function(fun = dnorm, args = list(mean=0, sd=sqrt(W)), color='blue',
                  linetype=2) +
    scale_y_continuous(breaks=NULL) +
    labs(x = 'theta1', y='', title = paste('Rhat = ', round(Rhat,2))) +
    annotate('text', x= 0, y = 0.1, label = paste('var_hat_plus = ', round(varp,2)), color='red') +
    annotate('text', x= 0, y = 0.2, label = paste('W = ', round(W,2)), color='blue')
  grid.arrange(p1, p2)
}

Plot normal approximation of each chain and Rhat components with niter = 100, 1000, 10000 with 50% warmup

niter <- 100
plotRhatcomps(dfs2, niter)
## Warning: `data_frame()` was deprecated in tibble 1.1.0.
## Please use `tibble()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

niter <- 1000
plotRhatcomps(dfs2, niter)

niter <- 10000
plotRhatcomps(dfs2, niter)

Plot Rhat with 50% warm-up and increasing number of iterations

ggplot(data = dfp) +
  geom_line(aes(iter, rhat, color = var)) +
  geom_hline(aes(yintercept = 1), linetype = 'dashed') +
  labs(title = 'Running Rhat with 50% warmp-up length', y = 'Rhat',
       x='Total number of iterations') +
  scale_x_log10(breaks = 10^(2:4), lim = c(100, 10000)) +
  scale_color_discrete(labels = c('theta1','theta2')) +
  theme(legend.position = 'bottom', legend.title = element_blank()) +
  ylim(c(0.95, 2.7))
## Warning: Removed 18 row(s) containing missing values (geom_path).

Demonstrate how to compute Rhat (PSRF) using RStan monitor. We need to reorder our array

samp <- aperm(tts, c(1, 3, 2))
monitor(samp, probs = c(0.25, 0.5, 0.75), digits_summary = 2)
## Inference for the input samples (10 chains: each with iter = 20000; warmup = 10000):
## 
##      Q5 Q50 Q95 Mean SD  Rhat Bulk_ESS Tail_ESS
## V1 -1.6   0 1.7    0  1  1.01      957     2403
## V2 -1.6   0 1.7    0  1  1.01      945     2517
## 
## For each parameter, Bulk_ESS and Tail_ESS are crude measures of 
## effective sample size for bulk and tail quantities respectively (an ESS > 100 
## per chain is considered good), and Rhat is the potential scale reduction 
## factor on rank normalized split chains (at convergence, Rhat <= 1.05).
LS0tCnRpdGxlOiAiQmF5ZXNpYW4gZGF0YSBhbmFseXNpcyBkZW1vcyAxMS4zLCBhbmQgMTEuNCIKYXV0aG9yOiAiQWtpIFZlaHRhcmksIE1hcmt1cyBQYWFzaW5pZW1pIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCkpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogcmVhZGFibGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCiMjIE1ldHJvcG9saXMgYWxnb3JpdGhtICsgUmhhdCAoUFNSRikgZGVtb25zdHJhdGlvbgpnZ3Bsb3QyIGFuZCBncmlkRXh0cmEgYXJlIHVzZWQgZm9yIHBsb3R0aW5nLCB0aWR5ciBmb3IgbWFuaXB1bGF0aW5nCmRhdGEgZnJhbWVzCgpgYGB7ciBzZXR1cCwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZ2dwbG90MikKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdnYW5pbWF0ZSkKbGlicmFyeShNQVNTKQpsaWJyYXJ5KHJzdGFuKQpsaWJyYXJ5KHJwcm9qcm9vdCkKcm9vdDwtaGFzX2ZpbGUoIi5CREFfUl9kZW1vc19yb290IikkbWFrZV9maXhfZmlsZSgpCmBgYAoKUGFyYW1ldGVycyBvZiBhIE5vcm1hbCBkaXN0cmlidXRpb24gdXNlZCBhcyBhIHRveSB0YXJnZXQgZGlzdHJpYnV0aW9uCgpgYGB7ciB9CnkxIDwtIDAKeTIgPC0gMApyIDwtIDAuOApTIDwtIGRpYWcoMikKU1sxLCAyXSA8LSByClNbMiwgMV0gPC0gcgpgYGAKClNhbXBsZSBmcm9tIHRoZSB0b3kgZGlzdHJpYnV0aW9uIHRvIHZpc3VhbGl6ZSA5MCUgSFBECmludGVydmFsIHdpdGggZ2dwbG90J3Mgc3RhdF9lbGxpcHNlKCkKCmBgYHtyIH0KZGZ0IDwtIGRhdGEuZnJhbWUobXZybm9ybSgxMDAwMDAsIGMoMCwgMCksIFMpKQpgYGAKCiMjIyBMb2FkIHByZS1ydW4gTWV0cm9wb2xpcyBjaGFpbnMuClNpbmNlLCBpbXBsZW1lbnRhdGlvbiBvZiB0aGUgTWV0cm9wb2xpcyBhbGdvcml0aG0gaXMgb25lIG9mIHRoZQpleGVyY2lzZXMsIHdlIGxvYWQgaGVyZSBwcmUtY29tcHV0ZWQgY2hhaW5zIGFuZCBSaGF0LXZhbHVlcwoKVGhlIHByb3Bvc2FsIGRpc3RyaWJ1dGlvbiB3YXMgaW50ZW50aW9uYWxseSBzZWxlY3RlZCB0byBiZSBzbGlnaHRseQp0b28gc21hbGwsIHRvIGJldHRlciBpbGx1c3RyYXRlIGNvbnZlcmdlbmNlIGRpYWdvbnN0aWNzCgpgdHRzJyBjb250YWlucyBkcmF3cywgYHAxJyBhbmQgJ3AyJyBjb250YWluIFJoYXQgdmFsdWVzIGZvciB0MQphbmQgdDIgdXNpbmcgNTAlIHdhcm0tdXAsIGBwcDEnIGFuZCAncHAyJyBjb250YWluIFJoYXQgdmFsdWVzIGZvcgp0MSBhbmQgdDIgdXNpbmcgMTAlIHdhcm0tdXAsIFJoYXQtdmFsdWVzIGhhdmUgYmVlbiBjb21wdXRlZCBmb3IKZWFjaCB0aW1lLXN0ZXAuCgpgYGB7ciB9CmxvYWQocm9vdCgiZGVtb3NfY2gxMSIsImRlbW8xMV80LlJEYXRhIikpCmBgYAoKVHJhbnNmb3JtIHRoZSBmaXJzdCBzMSByb3dzIG9mIHRoZQpkYXRhIGludG8gYSAndGlkeScgZm9ybWF0IGZvciBwbG90dGluZwoKYGBge3IgfQpzMSA8LSA1MApkZnMxIDwtIGRhdGEuZnJhbWUoaXRlciA9IDE6czEsIHR0c1sxOnMxLCAxLCBdKSAlPiUKICBnYXRoZXIoY2hhaW4sIHRoMSwgLWl0ZXIpICU+JQogIHdpdGhpbih7dGgyIDwtIGModHRzWzE6czEsIDIsIF0pICAgICAgICAgIyB4bCBhbmQgeWwgc3BlY2lmeSB0aGUgcHJldmlvdXMKICAgICAgICAgIHRoMmwgPC0gYyh0aDJbMV0sIHRoMlstbGVuZ3RoKHRoMildKSAgICMgZHJhdyBpbiB0aGUgY2hhaW4gZm9yCiAgICAgICAgICB0aDFsIDwtIGModGgxWzFdLCB0aDFbLWxlbmd0aCh0aDEpXSl9KSAjIHBsb3R0aW5nCmBgYAoKRml4IHRoZSBpbmNvcnJlY3QgbGFnZ2VkIHZhbHVlcywgdGhlIGxhZ2dlZCB2YWx1ZSBvZiB0aGUKZmlyc3QgZHJhdyBpbiB0aGUgY2hhaW4gKGZvciBwbG90dGluZykgaXMgIHRoZSB2YWx1ZQppdHNlbGYgKGluc3RlYWQgb2YgdGhlIGxhc3QgdmFsdWUgb2YgdGhlIHByZXZpb3VzIGNoYWluKQoKYGBge3IgfQpzaW5kIDwtIDA6OSpzMSsxCmRmczFbc2luZCwgYygndGgxbCcsJ3RoMmwnKV0gPC0gZGZzMVtzaW5kLCBjKCd0aDEnLCd0aDInKV0KYGBgCgpBbm90aGVyIGRhdGEgZnJhbWUgd2l0aCBhbGwgZHJhd3MKCmBgYHtyIH0KaW5kczIgPC0gMToxMDAwMApkZnMyIDwtIGRhdGEuZnJhbWUoaXRlciA9IGluZHMyLCB0dHNbaW5kczIsIDEsIF0pICU+JQogIGdhdGhlcihjaGFpbiwgdGhldGExLCAtaXRlcikgJT4lCiAgd2l0aGluKHRoZXRhMiA8LSBjKHR0c1tpbmRzMiwgMiwgXSkpICU+JQogIGdhdGhlcih2YXIsIHZhbCwgLWl0ZXIsIC1jaGFpbikKYGBgCgpUaGlyZCBkYXRhIGZyYW1lIHdpdGggUmhhdCB2YWx1ZXMKCmBgYHtyIH0KaW5kc3AgPC0gc2VxKDEwLCBsZW5ndGgocDEpLCAxMCkKZGZwIDwtIGRhdGEuZnJhbWUoaXRlciA9IGluZHNwLAogICAgICAgICAgICAgICAgICB0aGV0YTEgPSBwMVtpbmRzcF0sCiAgICAgICAgICAgICAgICAgIHRoZXRhMiA9IHAyW2luZHNwXSkgJT4lCiAgZ2F0aGVyKHZhcixyaGF0LC1pdGVyKSAKYGBgCgpDb25zdHJ1Y3QgYSAyZC1wbG90IG9mIHRoZSA1MCBmaXJzdCBpdGVyYXRpb25zIG9mIHRoZSBjaGFpbnMKCmBgYHtyIH0KZnJhbWUgPSByZXAoMTpzMSwgMTApCmNoYWluczEgPC0gZ2dwbG90KGRhdGEgPSBkZnMxKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gdGgxLCB4ZW5kID0gdGgxbCwgeSA9IHRoMiwgeWVuZCA9IHRoMmwsIGNvbG9yID0gY2hhaW4sCiAgICAgICAgICAgICAgICAgICBncm91cCA9IGNoYWluKSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGRmczFbc2luZCwgXSwgYWVzKHggPSB0aDEsIHkgPSB0aDIsIGNvbG9yID0gY2hhaW4pKSArCiAgc3RhdF9lbGxpcHNlKGRhdGEgPSBkZnQsIGFlcyh4ID0gWDEsIHkgPSBYMiksIGxldmVsID0gMC45LCBjb2xvciA9ICdibGFjaycpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTQsIDQpLCB5bGltID0gYygtNCwgNCkpICsKICBsYWJzKHggPSAndGhldGExJywgeSA9ICd0aGV0YTInKSArCiAgc2NhbGVfY29sb3JfZGlzY3JldGUoZ3VpZGUgPSAibm9uZSIpCmBgYAoKQW5pbWF0ZSBzMSBmaXJzdCBpdGVyYXRpb25zIG9mIHRoZSBjaGFpbnMuIApBdCBzb21lIHBvaW50cyBzb21lIG9mIHRoZSBjaGFpbnMgc2VlbSB0byBoYWx0IGZvcgoKYGBge3IgfQojICBhIG1vbWVudC4gV2hhdCByZWFsbHkgaGFwcGVucyBhdCB0aGF0IHBvaW50IGlzIHRoYXQKYGBgCgp0aGV5IGRyYXcgcG9zc2libHkgYSBmZXcgcG9pbnRzIHRoYXQgYXJlIHJlamVjdGVkCihyZWplY3RlZCBwb2ludHMgbm90IHNob3duKSBhbmQgdGh1cyB0aGUgY2hhaW4gaXMgbm90IG1vdmluZwoKYGBge3IgTWV0cm9wb2xpcywgcmVzdWx0cz0naGlkZScsIG1lc3NhZ2U9RkFMU0V9CmFuaW0gPC0gYW5pbWF0ZShjaGFpbnMxICsgCiAgICAgICAgICAgICAgICAgIHRyYW5zaXRpb25fcmV2ZWFsKGFsb25nPWl0ZXIpICsgCiAgICAgICAgICAgICAgICAgIHNoYWRvd190cmFpbCgxL3MxKSkKYGBgCgpTaG93IHRoZSBhbmltYXRpb24KCmBgYHtyIH0KYW5pbQpgYGAKClBsb3QgdGhlIHJlc3VsdCwgbm8gY29udmVyZ2VuY2UgeWV0CgpgYGB7ciB9CmNoYWluczEgKyBsYWJzKHRpdGxlID0gJ05vIGNvbnZlcmdlbmNlJykKYGBgCgpQbG90IHRyZW5kcyBvZiB0aGUgNTAgZmlyc3QgaXRlcmF0aW9ucwoKYGBge3IgfQpnZ3Bsb3QoZGF0YSA9IGZpbHRlcihkZnMyLCBpdGVyPD01MCkpICsKICBnZW9tX2xpbmUoYWVzKGl0ZXIsIHZhbCwgY29sb3IgPSBjaGFpbikpICsKICBmYWNldF9ncmlkKHZhcn4uKSArCiAgbGFicyh0aXRsZSA9ICdOb3QgY29udmVyZ2VkJywgeSA9ICcnKSArCiAgc2NhbGVfY29sb3JfZGlzY3JldGUoZ3VpZGUgPSAibm9uZSIpCmBgYAoKUGxvdCB0cmVuZHMgb2YgdGhlIGFsbCBkcmF3cwoKYGBge3IgfQpnZ3Bsb3QoZGF0YSA9IGZpbHRlcihkZnMyLCBpdGVyPjUwMCkpICsKICBnZW9tX2xpbmUoYWVzKGl0ZXIsIHZhbCwgY29sb3IgPSBjaGFpbikpICsKICBmYWNldF9ncmlkKHZhcn4uKSArCiAgbGFicyh0aXRsZSA9ICdWaXN1YWxseSBjb252ZXJnZWQnLCB5ID0gJycpICsKICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShndWlkZSA9ICJub25lIikKYGBgCgpGdW5jdGlvbiBmb3IgcGxvdHRpbmcgY29tcG9uZW50cyBvZiBSaGF0CgpgYGB7ciB9CnBsb3RSaGF0Y29tcHMgPC0gZnVuY3Rpb24oZGYgPSBOVUxMLCBuaXRlciA9IE5VTEwpIHsKICBtc2RmIDwtIGRmICU+JSBmaWx0ZXIodmFyPT0ndGhldGExJywgaXRlcjxuaXRlciwgaXRlcj49bml0ZXIvMikgJT4lCiAgICBncm91cF9ieShjaGFpbikgJT4lCiAgICBzdW1tYXJpc2UobWVhbj1tZWFuKHZhbCksIHNkPXNkKHZhbCkpCiAgbmRmIDwtICBtc2RmICU+JQogICAgcG1hcF9kZih+IGRhdGFfZnJhbWUoeCA9IHNlcSgtNCwgNCwgbGVuZ3RoLm91dCA9IDIwMSksIGNoYWluPS4uMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVuc2l0eSA9IGRub3JtKHgsIC4uMiwgLi4zKSkpCiAgIyBwbG90IG5vcm1hbCBkaXN0cmlidXRpb24gYXBwcm9heGltYXRpb24gZm9yIGVhY2ggY2hhaW4gdG8gaWxsdXN0cmF0ZQogICMgbWVhbnMgYW5kIHZhcmlhbmNlcwogIHAxIDwtIGdncGxvdChuZGYsIGFlcyhncm91cCA9IGNoYWluLCB4ID0geCwgeSA9IGRlbnNpdHkpKSArIAogICAgZ2VvbV9saW5lKGxpbmV0eXBlPTEsIGNvbG9yPSJibHVlIikgKwogICAgbGFicyh4PSd0aGV0YTEnLCB5PScnLAogICAgICAgdGl0bGU9cGFzdGUobml0ZXIvMiwgJyB3YXJtdXAsICcsIG5pdGVyLzIsICdwb3N0IHdhcm11cCBpdGVyYXRpb25zJykpICsKICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3M9TlVMTCkKICAjIGNvbXB1dGUgUmhhdAogIG4gPC0gbml0ZXIvMgogIEIgPC0gbip2YXIobXNkZiRtZWFuKQogIFcgPC0gbWVhbihtc2RmJHNkXjIpCiAgdmFycCA8LSAobi0xKS9uKlcgKyBCL24KICBSaGF0IDwtIHNxcnQodmFycC9XKQogICMgcGxvdCBub3JtYWwgYXBwcm94aW1hdGlvbnMgdG8gaWxsdXN0cmF0ZSBXLCBtYXJnaW5hbCB2YXJpYW5jZSBlc3RpbWF0ZQogICMgdXNpbmcgd2l0aGluIHZhcmlhbmNlcywgYW5kIHZhcl9oYXRfcGx1cywgbWFyZ2luYWwgdmFyaWFuY2UgZXN0aW1hdGUKICAjIGNvbWJpbmluZyBXIGFuZCBiZXR3ZWVuIGNoYWluIHZhcmlhbmNlIGVzdGltYXRlCiAgCiAgcDIgPC0gZ2dwbG90KGRhdGEuZnJhbWUoeD1jKC00LCA0KSksIGFlcyh4KSkgKwogICAgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QobWVhbj0wLCBzZD1zcXJ0KHZhcnApKSwgY29sb3I9J3JlZCcpICsKICAgIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGFyZ3MgPSBsaXN0KG1lYW49MCwgc2Q9c3FydChXKSksIGNvbG9yPSdibHVlJywKICAgICAgICAgICAgICAgICAgbGluZXR5cGU9MikgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1OVUxMKSArCiAgICBsYWJzKHggPSAndGhldGExJywgeT0nJywgdGl0bGUgPSBwYXN0ZSgnUmhhdCA9ICcsIHJvdW5kKFJoYXQsMikpKSArCiAgICBhbm5vdGF0ZSgndGV4dCcsIHg9IDAsIHkgPSAwLjEsIGxhYmVsID0gcGFzdGUoJ3Zhcl9oYXRfcGx1cyA9ICcsIHJvdW5kKHZhcnAsMikpLCBjb2xvcj0ncmVkJykgKwogICAgYW5ub3RhdGUoJ3RleHQnLCB4PSAwLCB5ID0gMC4yLCBsYWJlbCA9IHBhc3RlKCdXID0gJywgcm91bmQoVywyKSksIGNvbG9yPSdibHVlJykKICBncmlkLmFycmFuZ2UocDEsIHAyKQp9CmBgYAoKUGxvdCBub3JtYWwgYXBwcm94aW1hdGlvbiBvZiBlYWNoIGNoYWluIGFuZCBSaGF0IGNvbXBvbmVudHMgd2l0aApuaXRlciA9IDEwMCwgMTAwMCwgMTAwMDAgd2l0aCA1MCUgd2FybXVwCgpgYGB7ciB9Cm5pdGVyIDwtIDEwMApwbG90UmhhdGNvbXBzKGRmczIsIG5pdGVyKQoKbml0ZXIgPC0gMTAwMApwbG90UmhhdGNvbXBzKGRmczIsIG5pdGVyKQoKbml0ZXIgPC0gMTAwMDAKcGxvdFJoYXRjb21wcyhkZnMyLCBuaXRlcikKYGBgCgpQbG90IFJoYXQgd2l0aCA1MCUgd2FybS11cCBhbmQgaW5jcmVhc2luZyBudW1iZXIgb2YgaXRlcmF0aW9ucwoKYGBge3IgfQpnZ3Bsb3QoZGF0YSA9IGRmcCkgKwogIGdlb21fbGluZShhZXMoaXRlciwgcmhhdCwgY29sb3IgPSB2YXIpKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDEpLCBsaW5ldHlwZSA9ICdkYXNoZWQnKSArCiAgbGFicyh0aXRsZSA9ICdSdW5uaW5nIFJoYXQgd2l0aCA1MCUgd2FybXAtdXAgbGVuZ3RoJywgeSA9ICdSaGF0JywKICAgICAgIHg9J1RvdGFsIG51bWJlciBvZiBpdGVyYXRpb25zJykgKwogIHNjYWxlX3hfbG9nMTAoYnJlYWtzID0gMTBeKDI6NCksIGxpbSA9IGMoMTAwLCAxMDAwMCkpICsKICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShsYWJlbHMgPSBjKCd0aGV0YTEnLCd0aGV0YTInKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICB5bGltKGMoMC45NSwgMi43KSkKYGBgCgpEZW1vbnN0cmF0ZSBob3cgdG8gY29tcHV0ZSBSaGF0IChQU1JGKSB1c2luZyBSU3RhbiBtb25pdG9yLgpXZSBuZWVkIHRvIHJlb3JkZXIgb3VyIGFycmF5CgpgYGB7ciB9CnNhbXAgPC0gYXBlcm0odHRzLCBjKDEsIDMsIDIpKQptb25pdG9yKHNhbXAsIHByb2JzID0gYygwLjI1LCAwLjUsIDAuNzUpLCBkaWdpdHNfc3VtbWFyeSA9IDIpCmBgYAoK