Introduction

In this note, we will perform Bayesian inference in a simple linear dynamic regression model. More specificaly, for \(t=1,\ldots,n\), real-valued dependent variable, \(y_t\), is linearly related to regressor/predictor, \(x_t\), as \[ y_t|x_t,\sigma^2 \sim N(x_t \beta_t, \sigma^2), \] where \(\sigma \in \Re^+\) is the observational standard deviationand \(\beta_t \in \Re\) the time-varying slope. The dynamics of the slope is a simple random walk: \[ \beta_t | \beta_{t-1},\tau^2 \sim N(\beta_{t-1},\tau^2), \] with \(\tau \in \Re^+\) the evolutional standard deviation. The specification is completed with the posterior of \(\beta_0\) at time \(t=0\), \[ \beta_0|D_0 \sim N(m_0,C_0), \] where \(D_0\) is the accumulated knowledge at time \(t=0\) and \(D_t=\{D_{t-1},y_t\}\), for \(t=1,\ldots,n\).

Simulating the data

The data we simulate below are basically three static linear regressions for each third of the data, i.e. \[ y_t|x_t \sim \left\{ \begin{array}{cc} N(\gamma_1 x_t,\sigma^2) & t=1,\ldots,n/3, \\ N(\gamma_2 x_t,\sigma^2) & t=n/3+1,\ldots,2n/3, \\ N(\gamma_3 x_t,\sigma^2) & t=2n/3+1,\ldots,n, \end{array} \right. \]

set.seed(12345)
n      = 300
gamma1 = 4
gamma2 = 1
gamma3 = -1
sig2   = 4
x      = rnorm(n)
y      = rep(0,n)
sig    = sqrt(sig2)
y[1:(n/3)]         = gamma1*x[1:(n/3)]         + rnorm(n/3,0,sig)
y[(n/3+1):(2*n/3)] = gamma2*x[(n/3+1):(2*n/3)] + rnorm(n/3,0,sig)
y[(2*n/3+1):n]     = gamma3*x[(2*n/3+1):n]     + rnorm(n/3,0,sig)

par(mfrow=c(2,2))
plot(x,y,col=c(rep(1,n/3),rep(2,n/3),rep(3,n/3)),pch=16)
plot(x,y,pch=16)
ts.plot(y)
ts.plot(x)

Computing marginal likelihood for \((\sigma^2,\tau^2)\)

As we learned in class, the Kalman filter recursions produce the evaluation of \[ p(y_1,\ldots,y_n|\sigma^2,\tau^2) = \prod_{t=1}^n p(y_t|D_{t-1},\sigma^2,\tau^2), \] where \((y_t|D_{t-1},\sigma^2,\tau^2) \sim N(f_t,Q_t)\), with \(f_t\) and \(Q_t\) from the following recursion. For \(t=1,\ldots,n\), \[\begin{eqnarray*} \beta_{t-1}|D_{t-1},\sigma^2,\tau^2 &\sim& N(m_{t-1},C_{t-1})\\ \beta_t|D_{t-1},\sigma^2,\tau^2 &\sim& N(a_t,R_t)\\ y_t|D_{t-1},\sigma^2,\tau^2 &\sim& N(f_t,Q_t)\\ \beta_t|D_t,\sigma^2,\tau^2 &\sim& N(m_t,C_t), \end{eqnarray*}\] where \(a_t=m_{t-1}\), \(R_t=C_{t-1}+\tau^2\), \(f_t=x_t a_t\), \(Q_t=x_t^2 R_t+\sigma^2\), and \(m_t = a_t + A_t(y_t-f_t)\) and \(C_t=R_t-A_t^2Q_t\), for \(A_t=R_tx_t/Q_t\).

Therefore, the likelihood function of \((\sigma^2,\tau^2)\), integrating out \(\beta_1,\ldots,\beta_n\), is easily obtained \[ L(\sigma^2,\tau^2|D_n) = \prod_{t=1}^n p_N(y_t|f_t,Q_t). \] Therefore, \[ ({\hat \sigma}^2,{\hat \tau}^2) = \mbox{arg} \max_{(\sigma^2,\tau^2) \in \Re^{+2}} L(\sigma^2,\tau^2|D_n). \]

m0    = 0
C0    = 1
N     = 50
sig2s = seq(3,5,length=N)
tau2s = seq(0.01,0.2,length=N)
loglikelihood = matrix(0,N,N)
for (i in 1:N){
  sig2 = sig2s[i]
  for (j in 1:N){
    m    = m0
    C    = C0
    tau2 = tau2s[j]
    loglike = 0
    for (t in 1:n){
      a     = m
      R     = C + tau2
      ft    = x[t]*a
      Qt    = x[t]^2*R + sig2
      loglike = loglike + dnorm(y[t],ft,sqrt(Qt),log=TRUE)
      At    = R*x[t]/Qt
      m     = a + At*(y[t]-ft)
      C     = R - At^2*Qt
    }
    loglikelihood[i,j] = loglike
  }
}

sig2.hat = sig2s[apply(loglikelihood==max(loglikelihood),1,sum)==1]
tau2.hat = tau2s[apply(loglikelihood==max(loglikelihood),2,sum)==1]

par(mfrow=c(1,1))
contour(sig2s,tau2s,exp(loglikelihood),
        xlab=expression(sigma^2),ylab=expression(tau^2),drawlabels=FALSE)
title("Likelihood function")
points(sig2.hat,tau2.hat,col=2,pch=16)

Sequentially learning \(\beta_1,\ldots,\beta_n\) given \(({\hat \sigma}^2,{\hat \tau}^2)\)

We now rerun the kalman filter recursions conditionally on \(({\hat \sigma}^2,{\hat \tau}^2)\). More precisely, we can derive the sequential posteriors \[ p(\beta_t|D_t,{\hat \sigma}^2,{\hat \tau}^2) \equiv N(m_t,C_t), \] where \(m_t\) and \(C_t\) are intricate functions of \(({\hat \sigma}^2,{\hat \tau}^2)\). However, it is worth remembering that we are actually using the data twice. First, we run the Kalman recursions several times and for the whole data, i.e. \(\{y_1,\ldots,y_n\}\), in order to obtain \(({\hat \sigma}^2,{\hat \tau}^2)\). Second, we rerun the Kalman recursions ``pretending’’ that \(\sigma={\hat \sigma}\) and \(\tau={\hat \tau}\).

m0    = 0
C0    = 1
sig2  = sig2.hat
tau2  = tau2.hat
mf    = rep(0,n)
Cf    = rep(0,n)
for (t in 1:n){
  a     = m
  R     = C + tau2
  ft    = x[t]*a
  Qt    = x[t]^2*R + sig2
  loglike = loglike + dnorm(y[t],ft,sqrt(Qt),log=TRUE)
  At    = R*x[t]/Qt
  m     = a + At*(y[t]-ft)
  C     = R - At^2*Qt
  mf[t] = m
  Cf[t] = C
}

qf = cbind(mf-2*sqrt(Cf),mf+2*sqrt(Cf))
ts.plot(mf,ylim=range(qf),ylab="States")
lines(qf[,1],col=2)
lines(qf[,2],col=2)
segments(1,gamma1,n/3,gamma1,col=4)
segments(n/3+1,gamma2,2*n/3,gamma2,col=4)
segments(2*n/3+1,gamma3,n,gamma3,col=4)
abline(v=n/3,lty=2,col=6)
abline(v=2*n/3,lty=2,col=6)

Jointly learning \(\beta_1,\ldots,\beta_n,\sigma^2,\tau^2\)

At this point, we still will not be able to fully describe \(p(\beta_t|D_t)\), unless we repeat the previous two parts for each time \(t\). If the interest is in learning about \(\beta_1,\ldots,\beta_n\) and \((\sigma^2,\tau^2)\) jointly and conditionally on \(D_n\), i.e. the whole data, then we can easily implement an MCMC scheme that cycles throught the full conditionals: \(p(\beta_t|\beta_{-t},\sigma^2,\tau^2,y_{1:n},x_{1:n})\),for \(t=1,\ldots,n\), \(p(\sigma^2|\beta_{1:n},\tau^2,y_{1:n},x_{1:n})\),and \(p(\tau^2|\beta_{1:n},\sigma^2,y_{1:n},x_{1:n})\).

Full conditional of \(\sigma^2\)

Let us assume that \(\sigma^2 \sim IG(a_0,b_0)\), so \[ \sigma^2|\beta_{1:n},y_{1:n},x_{1:n} \sim IG\left(a_0+\frac{n}{2},b_0+\frac{\sum_{t=1}^n (y_t-\beta_t x_t)^2}{2}\right). \]

Full conditional of \(\tau^2\)

Let us assume that \(\tau^2 \sim IG(c_0,d_0)\), so \[ \tau^2|\beta_{1:n} \sim IG\left(c_0+\frac{n-1}{2},d_0+\frac{\sum_{t=2}^n (\beta_t-\beta_{t-1})^2}{2}\right). \]

Full conditional of \(\beta_t\)

For \(t=1\) \[ p(\beta_1|\beta_{-1},y_{1:n},x_{1:n},\sigma^2,\tau^2) = p(\beta_1)p(\beta_2|\beta_1,\tau^2) p(y_1|\beta_1,x_1,\sigma^2) \equiv N(b_1,V_1), \] where \(V_1 = 1/(1/(C_0+\tau^2)+1/\tau^2+x_1^2/\sigma^2)\) and \(b_1 = V_1 (m_0/(C_0+\tau^2)+\beta_2/\tau^2+x_1y_1/\sigma^2)\). Recall that \(\beta_0 \sim N(m_0,C_0)\), so the prior \(\beta_1 \sim N(m_0,C_0+\tau^2)\).

It is straighforward to see that, for \(t=2,\ldots,n-1\), \[ p(\beta_t|\beta_{-t},y_{1:n},x_{1:n},\sigma^2,\tau^2) = p(\beta_t|\beta_{t-1},\tau^2) p(\beta_{t+1}|\beta_t,\tau^2) p(y_t|\beta_t,x_t,\sigma^2) \equiv N(b_t,V_t), \] where \(V_t = 1/(2/\tau^2+x_t^2/\sigma^2)\) and \(b_t=V_t((\beta_{t-1}+\beta_{t+1})/\tau^2+x_ty_t/\sigma^2)\).

For \(t=n\) \[ p(\beta_n|\beta_{-n},y_{1:n},x_{1:n},\sigma^2,\tau^2) = p(\beta_n|\beta_{n-1},\tau^2) p(y_n|\beta_n,x_n,\sigma^2) \equiv N(b_n,V_n), \] where \(V_n = 1/(1/\tau^2+x_n^2/\sigma^2)\) and \(b_n = V_n (\beta_{n-1}/\tau^2+x_ny_n/\sigma^2)\).

a0=2;b0=1;c0=2;d0=1
a0=0.01;b0=0.01;c0=0.01;d0=0.01

# Initial values
beta  = mf
sig2  = sig2.hat
tau2  = tau2.hat
M0    = 20000
M     = 5000
niter = M0 + M
draws = matrix(0,niter,n+2)
for (iter in 1:niter){
  sig2 = 1/rgamma(1,a0+n/2,b0+sum((y-beta*x)^2)/2)
  tau2 = 1/rgamma(1,c0+(n-1)/2,d0+sum((beta[2:n]-beta[1:(n-1)])^2)/2)
  V1 = 1/(1/(C0+tau2)+1/tau2+x[1]^2/sig2)
  b1 = V1*(m0/(C0+tau2)+beta[2]/tau2+x[1]*y[1]/sig2)
  beta[1] = rnorm(1,b1,sqrt(V1))
  for (t in 2:(n-1)){
    Vt = 1/(2/tau2+x[t]^2/sig2)         
    bt = Vt*((beta[t-1]+beta[t+1])/tau2+x[t]*y[t]/sig2)
    beta[t] = rnorm(1,bt,sqrt(Vt))
  }  
  Vn = 1/(1/tau2+x[n]^2/sig2)
  bn = Vn*(beta[n-1]/tau2+x[n]*y[n]/sig2)
  beta[n] = rnorm(1,bn,sqrt(Vn))
  draws[iter,] = c(beta,sig2,tau2)
}
draws = draws[(M0+1):niter,]

Trace plots

par(mfrow=c(2,3))
ts.plot(draws[,1],main=expression(beta[1]),xlab="iterations",ylab="")
ts.plot(draws[,100],main=expression(beta[100]),xlab="iterations",ylab="")
ts.plot(draws[,200],main=expression(beta[200]),xlab="iterations",ylab="")
ts.plot(draws[,n],main=expression(beta[n]),xlab="iterations",ylab="")
ts.plot(draws[,n+1],main=expression(sigma^2),xlab="iterations",ylab="")
ts.plot(draws[,n+2],main=expression(tau^2),xlab="iterations",ylab="")

Markov chain autocorrelations

par(mfrow=c(1,3))
acf(draws[,n+1],main=expression(sigma^2),ylab="")
acf(draws[,n+2],main=expression(tau^2),ylab="")
acfs = matrix(0,n,50)
for (i in 1:n){
  acf = acf(draws[,i],lag.max=50,plot=FALSE)
  acfs[i,] = acf$acf[2:51]
}
plot(acfs[1,],ylim=range(acfs),ylab="ACF",type="l",xlab="Lag")
for (i in 2:n)
  lines(acfs[i,])

Marginal posterior densities

Below we plot histogram approximations of marginal posterior densities for various parameters, such as \(p(\beta_1|D_n)\), \(p(\beta_200|D_n)\), \(p(\sigma^2|D_n)\) and \(p(\tau^2|D_n)\).

par(mfrow=c(2,3))
hist(draws[,1],main=expression(beta[1]),prob=TRUE,xlab="")
hist(draws[,100],main=expression(beta[100]),prob=TRUE,xlab="")
hist(draws[,200],main=expression(beta[200]),prob=TRUE,xlab="")
hist(draws[,n],main=expression(beta[n]),prob=TRUE,xlab="")
hist(draws[,n+1],main=expression(sigma^2),prob=TRUE,xlab="")
hist(draws[,n+2],main=expression(tau^2),prob=TRUE,xlab="")

Joint posterior \(p(\sigma^2,\tau^2|D_n)\)

The picture above shows the contours of \(p(\sigma^2,\tau^2|D_n)\) evaluated on a fine grid. The underneath image is a 2D-histogram reconstruction this joint distribution based on the posterior draws of \(\sigma^2\) and \(\tau^2\).

ldigamma=function(x,a,b){dgamma(1/x,a,b,log=TRUE)-2*log(x)}
logprior = matrix(0,N,N)
for (i in 1:N)
  for (j in 1:N)
    logprior[i,j] = ldigamma(sig2s[i],a0,b0)+ldigamma(tau2s[j],c0,d0)
logposterior = logprior+loglikelihood

library(MASS)
library(RColorBrewer)

rf = colorRampPalette(rev(brewer.pal(11,'Spectral')))
r  = rf(32)
xx = draws[,n+1]
yy = draws[,n+2]
df = data.frame(xx,yy)
k = kde2d(df$xx, df$yy, n=200)

sig2.mode = k$x[apply(k$z==max(k$z),1,sum)==1]
tau2.mode = k$y[apply(k$z==max(k$z),2,sum)==1]
image(k, col=r,xlab=expression(sigma^2),ylab=expression(tau^2))
points(sig2.mode,tau2.mode,col=4,pch=16)
points(sig2.hat,tau2.hat,col=3,pch=16)
contour(sig2s,tau2s,exp(logposterior),add=TRUE,drawlabels=FALSE)

Below we show the values of \(\sigma^2\) and \(\tau^2\) that maximize the likelihood funtion and the joint posterior, respectively. Because we are using a prior that is fairly uninformative, both estimators are particularly similar.

c(sig2.hat,sig2.mode)
## [1] 3.897959 3.852122
c(tau2.hat,tau2.mode)
## [1] 0.04877551 0.04217180

Smoothed states marginal posterior densities

Below we plot the 2.5th, 50th and 97.5th percentiles of \[ p(\beta_t|D_n) \ \ \ \mbox{and} \ \ \ p(\beta_t|D_t,{\hat \sigma}^2,{\hat \tau}^2). \] We ares still a bit short of producing \(p(\beta_t|D_t)\), i.e. the online distribution of the states unconditionally on \((\sigma^2,\tau^2)\). We will be able to do that once we talk about sequential Monte Carlo tools, also known as particle filters.

qbeta = t(apply(draws[,1:n],2,quantile,c(0.025,0.5,0.975)))
range = range(qbeta,qf)
par(mfrow=c(1,1))
ts.plot(qbeta,lty=c(2,1,2),ylim=range,xlab="Time",ylab=expression(beta),main="")
lines(qf[,1],col=2,lty=2)
lines(mf,col=2,lty=1)
lines(qf[,2],col=2,lty=2)
segments(1,gamma1,n/3,gamma1,col=4)
segments(n/3+1,gamma2,2*n/3,gamma2,col=4)
segments(2*n/3+1,gamma3,n,gamma3,col=4)
abline(v=n/3,lty=2,col=6)
abline(v=2*n/3,lty=2,col=6)
legend("topright",legend=c("Smoothed densities (MCMC)","Filtered densities (given sig2,tau2)"),col=1:2,lty=1)

Final improvement: sampling \(\beta_1,\ldots,\beta_n\) jointly

Instead of sampling \(\beta_t\) from its full conditional, the forward filtering, backward sampling scheme samples \(\beta_1,\ldots,\beta_n\) jointly. More specifically, FFBS uses the following results: \[ p(\beta_1,\ldots,\beta_n|\sigma^2,\tau^2,D_n) = p(\beta_n|\sigma^2,\tau^2,D_n) \prod_{t=2}^n p(\beta_t|\beta_{t+1},\sigma^2,\tau^2,D_n), \] where \((\beta_n|\sigma^2,\tau^2,D_n) \sim N({\tilde m}_n,{\tilde C}_n)\), with \({\tilde m}_n=m_n\) and \({\tilde C}_n)=C_n\). Also, \((\beta_t|\beta_{t+1},\sigma^2,\tau^2,D_n) \sim N({\tilde m}_n,{\tilde C}_n)\), where \[ {\tilde C}_t = 1/(1/\tau^2+1/C_t) \ \ \mbox{and} \ \ {\tilde m}_t = C_t(\beta_{t+1}/\tau^2 + m_t/C_t), \] for \(t=n-1,\ldots,1\).

# MCMC scheme
beta   = mf
sig2   = sig2.hat
tau2   = tau2.hat
M0     = 20000
M      = 5000
niter  = M0 + M
ms     = rep(0,n)
Cs     = rep(0,n)
m      = m0
C      = C0
draws1 = matrix(0,niter,n+2)
for (iter in 1:niter){
  for (t in 1:n){
    at    = m
    Rt    = C+tau2
    ft    = x[t]*at
    Qt    = x[t]^2*Rt+sig2
    At    = Rt*x[t]/Qt
    m     = at + At*(y[t]-ft)
    C     = Rt - At^2*Qt
    ms[t] = m
    Cs[t] = C
  }
  betas = rep(0,n)
  betas[n] = rnorm(1,ms[n],sqrt(Cs[n]))
  for (t in (n-1):1){
    var = 1/(1/tau2+1/Cs[t])
    mean = var*(betas[t+1]/tau2+ms[t]/Cs[t])
    betas[t] = rnorm(1,mean,sqrt(var))  
  }

  # sampling sig2
  sig2 = 1/rgamma(1,a0+n/2,b0+sum((y-x*betas)^2)/2)

  # sampling tau2
  tau2 = 1/rgamma(1,c0+(n-1)/2,d0+sum((betas[2:n]-betas[1:(n-1)])^2)/2)

  # storing draws
  draws1[iter,] = c(betas,sig2,tau2)
}
draws1 = draws1[(M0+1):niter,]
qbeta1 = t(apply(draws1[,1:n],2,quantile,c(0.025,0.5,0.975)))
par(mfrow=c(2,3))
plot(density(draws[,1]),main=expression(beta[1]),xlab="")
lines(density(draws1[,1]),col=2)
plot(density(draws[,100]),main=expression(beta[100]),xlab="")
lines(density(draws1[,100]),col=2)
plot(density(draws[,200]),main=expression(beta[200]),xlab="")
lines(density(draws1[,200]),col=2)
plot(density(draws[,n]),main=expression(beta[n]),xlab="")
lines(density(draws1[,n]),col=2)
plot(density(draws[,n+1]),main=expression(sigma^2),xlab="")
lines(density(draws1[,n+1]),col=2)
plot(density(draws[,n+2]),main=expression(tau^2),xlab="")
lines(density(draws1[,n+2]),col=2)

\(\beta_t\) autocorrelations

par(mfrow=c(1,2))
acfs1 = matrix(0,n,50)
for (i in 1:n){
  acf = acf(draws1[,i],lag.max=50,plot=FALSE)
  acfs1[i,] = acf$acf[2:51]
}
plot(acfs[1,],ylim=range(acfs,acfs1),ylab="ACF",type="l",xlab="Lag")
for (i in 2:n)
  lines(acfs[i,])
plot(acfs1[1,],ylim=range(acfs,acfs1),ylab="ACF",type="l",xlab="Lag")
for (i in 2:n)
  lines(acfs1[i,])

range = range(qbeta,qbeta1)
par(mfrow=c(1,1))
ts.plot(qbeta,lty=c(2,1,2),ylim=range,xlab="Time",ylab=expression(beta),main="")
lines(qbeta1[,1],col=2,lty=2)
lines(qbeta1[,2],col=2,lty=1)
lines(qbeta1[,3],col=2,lty=2)
segments(1,gamma1,n/3,gamma1,col=4)
segments(n/3+1,gamma2,2*n/3,gamma2,col=4)
segments(2*n/3+1,gamma3,n,gamma3,col=4)
abline(v=n/3,lty=2,col=6)
abline(v=2*n/3,lty=2,col=6)
legend("topright",legend=c("Block sampling","Individual sampling"),col=1:2,lty=1)

LS0tCnRpdGxlOiAiTm9ybWFsIER5bmFtaWMgTGluZWFyIHJlZ3Jlc3Npb24iCnN1YnRpdGxlOiAiQmF5ZXNpYW4gaW5mZXJlbmNlIgphdXRob3I6ICJIZWRpYmVydCBGcmVpdGFzIExvcGVzIgpkYXRlOiAiMy8zMS8yMDIyIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCiMgSW50cm9kdWN0aW9uCkluIHRoaXMgbm90ZSwgd2Ugd2lsbCBwZXJmb3JtIEJheWVzaWFuIGluZmVyZW5jZSBpbiBhIHNpbXBsZSBsaW5lYXIgZHluYW1pYyByZWdyZXNzaW9uIG1vZGVsLiAgTW9yZSBzcGVjaWZpY2FseSwgIGZvciAkdD0xLFxsZG90cyxuJCwgcmVhbC12YWx1ZWQgZGVwZW5kZW50IHZhcmlhYmxlLCAkeV90JCwgaXMgbGluZWFybHkgcmVsYXRlZCB0byByZWdyZXNzb3IvcHJlZGljdG9yLCAkeF90JCwgYXMKJCQKeV90fHhfdCxcc2lnbWFeMiBcc2ltIE4oeF90IFxiZXRhX3QsIFxzaWdtYV4yKSwKJCQKd2hlcmUgJFxzaWdtYSBcaW4gXFJlXiskIGlzIHRoZSBvYnNlcnZhdGlvbmFsIHN0YW5kYXJkIGRldmlhdGlvbmFuZCAkXGJldGFfdCBcaW4gXFJlJCB0aGUgdGltZS12YXJ5aW5nIHNsb3BlLiAgVGhlIGR5bmFtaWNzIG9mIHRoZSBzbG9wZSBpcyBhIHNpbXBsZSByYW5kb20gd2FsazoKJCQKXGJldGFfdCB8IFxiZXRhX3t0LTF9LFx0YXVeMiBcc2ltIE4oXGJldGFfe3QtMX0sXHRhdV4yKSwKJCQKd2l0aCAkXHRhdSBcaW4gXFJlXiskIHRoZSBldm9sdXRpb25hbCBzdGFuZGFyZCBkZXZpYXRpb24uICBUaGUgc3BlY2lmaWNhdGlvbiBpcyBjb21wbGV0ZWQgd2l0aCB0aGUgcG9zdGVyaW9yIG9mICRcYmV0YV8wJCBhdCB0aW1lICR0PTAkLCAKJCQKXGJldGFfMHxEXzAgXHNpbSBOKG1fMCxDXzApLAokJAp3aGVyZSAkRF8wJCBpcyB0aGUgYWNjdW11bGF0ZWQga25vd2xlZGdlIGF0IHRpbWUgJHQ9MCQgYW5kICREX3Q9XHtEX3t0LTF9LHlfdFx9JCwgZm9yICR0PTEsXGxkb3RzLG4kLgoKIyBTaW11bGF0aW5nIHRoZSBkYXRhClRoZSBkYXRhIHdlIHNpbXVsYXRlIGJlbG93IGFyZSBiYXNpY2FsbHkgdGhyZWUgc3RhdGljIGxpbmVhciByZWdyZXNzaW9ucyBmb3IgZWFjaCB0aGlyZCBvZiB0aGUgZGF0YSwgaS5lLgokJAp5X3R8eF90IFxzaW0gClxsZWZ0XHsKXGJlZ2lue2FycmF5fXtjY30KTihcZ2FtbWFfMSB4X3QsXHNpZ21hXjIpICYgdD0xLFxsZG90cyxuLzMsIFxcCk4oXGdhbW1hXzIgeF90LFxzaWdtYV4yKSAmIHQ9bi8zKzEsXGxkb3RzLDJuLzMsIFxcCk4oXGdhbW1hXzMgeF90LFxzaWdtYV4yKSAmIHQ9Mm4vMysxLFxsZG90cyxuLApcZW5ke2FycmF5fQpccmlnaHQuCiQkCmBgYHtyIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0Kc2V0LnNlZWQoMTIzNDUpCm4gICAgICA9IDMwMApnYW1tYTEgPSA0CmdhbW1hMiA9IDEKZ2FtbWEzID0gLTEKc2lnMiAgID0gNAp4ICAgICAgPSBybm9ybShuKQp5ICAgICAgPSByZXAoMCxuKQpzaWcgICAgPSBzcXJ0KHNpZzIpCnlbMToobi8zKV0gICAgICAgICA9IGdhbW1hMSp4WzE6KG4vMyldICAgICAgICAgKyBybm9ybShuLzMsMCxzaWcpCnlbKG4vMysxKTooMipuLzMpXSA9IGdhbW1hMip4WyhuLzMrMSk6KDIqbi8zKV0gKyBybm9ybShuLzMsMCxzaWcpCnlbKDIqbi8zKzEpOm5dICAgICA9IGdhbW1hMyp4WygyKm4vMysxKTpuXSAgICAgKyBybm9ybShuLzMsMCxzaWcpCgpwYXIobWZyb3c9YygyLDIpKQpwbG90KHgseSxjb2w9YyhyZXAoMSxuLzMpLHJlcCgyLG4vMykscmVwKDMsbi8zKSkscGNoPTE2KQpwbG90KHgseSxwY2g9MTYpCnRzLnBsb3QoeSkKdHMucGxvdCh4KQpgYGAKCiMgQ29tcHV0aW5nIG1hcmdpbmFsIGxpa2VsaWhvb2QgZm9yICQoXHNpZ21hXjIsXHRhdV4yKSQKQXMgd2UgbGVhcm5lZCBpbiBjbGFzcywgdGhlIEthbG1hbiBmaWx0ZXIgcmVjdXJzaW9ucyBwcm9kdWNlIHRoZSBldmFsdWF0aW9uIG9mIAokJApwKHlfMSxcbGRvdHMseV9ufFxzaWdtYV4yLFx0YXVeMikgPSBccHJvZF97dD0xfV5uIHAoeV90fERfe3QtMX0sXHNpZ21hXjIsXHRhdV4yKSwKJCQKd2hlcmUgJCh5X3R8RF97dC0xfSxcc2lnbWFeMixcdGF1XjIpIFxzaW0gTihmX3QsUV90KSQsIHdpdGggJGZfdCQgYW5kICRRX3QkIGZyb20gdGhlIGZvbGxvd2luZyByZWN1cnNpb24uICBGb3IgJHQ9MSxcbGRvdHMsbiQsClxiZWdpbntlcW5hcnJheSp9ClxiZXRhX3t0LTF9fERfe3QtMX0sXHNpZ21hXjIsXHRhdV4yICZcc2ltJiBOKG1fe3QtMX0sQ197dC0xfSlcXApcYmV0YV90fERfe3QtMX0sXHNpZ21hXjIsXHRhdV4yICZcc2ltJiBOKGFfdCxSX3QpXFwKeV90fERfe3QtMX0sXHNpZ21hXjIsXHRhdV4yICZcc2ltJiBOKGZfdCxRX3QpXFwKXGJldGFfdHxEX3QsXHNpZ21hXjIsXHRhdV4yICZcc2ltJiBOKG1fdCxDX3QpLApcZW5ke2VxbmFycmF5Kn0Kd2hlcmUgJGFfdD1tX3t0LTF9JCwgJFJfdD1DX3t0LTF9K1x0YXVeMiQsICRmX3Q9eF90IGFfdCQsICRRX3Q9eF90XjIgUl90K1xzaWdtYV4yJCwgYW5kIAokbV90ID0gYV90ICsgQV90KHlfdC1mX3QpJCBhbmQgJENfdD1SX3QtQV90XjJRX3QkLCBmb3IgJEFfdD1SX3R4X3QvUV90JC4gIAoKVGhlcmVmb3JlLCB0aGUgbGlrZWxpaG9vZCBmdW5jdGlvbiBvZiAkKFxzaWdtYV4yLFx0YXVeMikkLCBpbnRlZ3JhdGluZyBvdXQgJFxiZXRhXzEsXGxkb3RzLFxiZXRhX24kLCBpcyBlYXNpbHkgb2J0YWluZWQKJCQKTChcc2lnbWFeMixcdGF1XjJ8RF9uKSA9IFxwcm9kX3t0PTF9Xm4gcF9OKHlfdHxmX3QsUV90KS4KJCQKVGhlcmVmb3JlLAokJAooe1xoYXQgXHNpZ21hfV4yLHtcaGF0IFx0YXV9XjIpID0gXG1ib3h7YXJnfSBcbWF4X3soXHNpZ21hXjIsXHRhdV4yKSBcaW4gXFJlXnsrMn19IEwoXHNpZ21hXjIsXHRhdV4yfERfbikuCiQkCgpgYGB7ciBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9OH0KbTAgICAgPSAwCkMwICAgID0gMQpOICAgICA9IDUwCnNpZzJzID0gc2VxKDMsNSxsZW5ndGg9TikKdGF1MnMgPSBzZXEoMC4wMSwwLjIsbGVuZ3RoPU4pCmxvZ2xpa2VsaWhvb2QgPSBtYXRyaXgoMCxOLE4pCmZvciAoaSBpbiAxOk4pewogIHNpZzIgPSBzaWcyc1tpXQogIGZvciAoaiBpbiAxOk4pewogICAgbSAgICA9IG0wCiAgICBDICAgID0gQzAKICAgIHRhdTIgPSB0YXUyc1tqXQogICAgbG9nbGlrZSA9IDAKICAgIGZvciAodCBpbiAxOm4pewogICAgICBhICAgICA9IG0KICAgICAgUiAgICAgPSBDICsgdGF1MgogICAgICBmdCAgICA9IHhbdF0qYQogICAgICBRdCAgICA9IHhbdF1eMipSICsgc2lnMgogICAgICBsb2dsaWtlID0gbG9nbGlrZSArIGRub3JtKHlbdF0sZnQsc3FydChRdCksbG9nPVRSVUUpCiAgICAgIEF0ICAgID0gUip4W3RdL1F0CiAgICAgIG0gICAgID0gYSArIEF0Kih5W3RdLWZ0KQogICAgICBDICAgICA9IFIgLSBBdF4yKlF0CiAgICB9CiAgICBsb2dsaWtlbGlob29kW2ksal0gPSBsb2dsaWtlCiAgfQp9CgpzaWcyLmhhdCA9IHNpZzJzW2FwcGx5KGxvZ2xpa2VsaWhvb2Q9PW1heChsb2dsaWtlbGlob29kKSwxLHN1bSk9PTFdCnRhdTIuaGF0ID0gdGF1MnNbYXBwbHkobG9nbGlrZWxpaG9vZD09bWF4KGxvZ2xpa2VsaWhvb2QpLDIsc3VtKT09MV0KCnBhcihtZnJvdz1jKDEsMSkpCmNvbnRvdXIoc2lnMnMsdGF1MnMsZXhwKGxvZ2xpa2VsaWhvb2QpLAogICAgICAgIHhsYWI9ZXhwcmVzc2lvbihzaWdtYV4yKSx5bGFiPWV4cHJlc3Npb24odGF1XjIpLGRyYXdsYWJlbHM9RkFMU0UpCnRpdGxlKCJMaWtlbGlob29kIGZ1bmN0aW9uIikKcG9pbnRzKHNpZzIuaGF0LHRhdTIuaGF0LGNvbD0yLHBjaD0xNikKYGBgCgojIFNlcXVlbnRpYWxseSBsZWFybmluZyAkXGJldGFfMSxcbGRvdHMsXGJldGFfbiQgZ2l2ZW4gJCh7XGhhdCBcc2lnbWF9XjIse1xoYXQgXHRhdX1eMikkCgoKV2Ugbm93IHJlcnVuIHRoZSBrYWxtYW4gZmlsdGVyIHJlY3Vyc2lvbnMgY29uZGl0aW9uYWxseSBvbiAkKHtcaGF0IFxzaWdtYX1eMix7XGhhdCBcdGF1fV4yKSQuICBNb3JlIHByZWNpc2VseSwgd2UgY2FuIGRlcml2ZSB0aGUgc2VxdWVudGlhbCBwb3N0ZXJpb3JzCiQkCnAoXGJldGFfdHxEX3Qse1xoYXQgXHNpZ21hfV4yLHtcaGF0IFx0YXV9XjIpIFxlcXVpdiBOKG1fdCxDX3QpLAokJAp3aGVyZSAkbV90JCBhbmQgJENfdCQgYXJlIGludHJpY2F0ZSBmdW5jdGlvbnMgb2YgJCh7XGhhdCBcc2lnbWF9XjIse1xoYXQgXHRhdX1eMikkLiAgSG93ZXZlciwgaXQgaXMgd29ydGggcmVtZW1iZXJpbmcgdGhhdCB3ZSBhcmUgYWN0dWFsbHkgdXNpbmcgdGhlIGRhdGEgdHdpY2UuICBGaXJzdCwgd2UgcnVuIHRoZSBLYWxtYW4gcmVjdXJzaW9ucyBzZXZlcmFsIHRpbWVzIGFuZCBmb3IgdGhlIHdob2xlIGRhdGEsIGkuZS4gJFx7eV8xLFxsZG90cyx5X25cfSQsIGluIG9yZGVyIHRvIG9idGFpbiAkKHtcaGF0IFxzaWdtYX1eMix7XGhhdCBcdGF1fV4yKSQuICBTZWNvbmQsIHdlIHJlcnVuIHRoZSBLYWxtYW4gcmVjdXJzaW9ucyBgYHByZXRlbmRpbmcnJyB0aGF0ICRcc2lnbWE9e1xoYXQgXHNpZ21hfSQgYW5kIAokXHRhdT17XGhhdCBcdGF1fSQuCgpgYGB7ciBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9OH0KbTAgICAgPSAwCkMwICAgID0gMQpzaWcyICA9IHNpZzIuaGF0CnRhdTIgID0gdGF1Mi5oYXQKbWYgICAgPSByZXAoMCxuKQpDZiAgICA9IHJlcCgwLG4pCmZvciAodCBpbiAxOm4pewogIGEgICAgID0gbQogIFIgICAgID0gQyArIHRhdTIKICBmdCAgICA9IHhbdF0qYQogIFF0ICAgID0geFt0XV4yKlIgKyBzaWcyCiAgbG9nbGlrZSA9IGxvZ2xpa2UgKyBkbm9ybSh5W3RdLGZ0LHNxcnQoUXQpLGxvZz1UUlVFKQogIEF0ICAgID0gUip4W3RdL1F0CiAgbSAgICAgPSBhICsgQXQqKHlbdF0tZnQpCiAgQyAgICAgPSBSIC0gQXReMipRdAogIG1mW3RdID0gbQogIENmW3RdID0gQwp9CgpxZiA9IGNiaW5kKG1mLTIqc3FydChDZiksbWYrMipzcXJ0KENmKSkKdHMucGxvdChtZix5bGltPXJhbmdlKHFmKSx5bGFiPSJTdGF0ZXMiKQpsaW5lcyhxZlssMV0sY29sPTIpCmxpbmVzKHFmWywyXSxjb2w9MikKc2VnbWVudHMoMSxnYW1tYTEsbi8zLGdhbW1hMSxjb2w9NCkKc2VnbWVudHMobi8zKzEsZ2FtbWEyLDIqbi8zLGdhbW1hMixjb2w9NCkKc2VnbWVudHMoMipuLzMrMSxnYW1tYTMsbixnYW1tYTMsY29sPTQpCmFibGluZSh2PW4vMyxsdHk9Mixjb2w9NikKYWJsaW5lKHY9MipuLzMsbHR5PTIsY29sPTYpCmBgYAoKIyBKb2ludGx5IGxlYXJuaW5nICRcYmV0YV8xLFxsZG90cyxcYmV0YV9uLFxzaWdtYV4yLFx0YXVeMiQgCgpBdCB0aGlzIHBvaW50LCB3ZSBzdGlsbCB3aWxsIG5vdCBiZSBhYmxlIHRvIGZ1bGx5IGRlc2NyaWJlICRwKFxiZXRhX3R8RF90KSQsIHVubGVzcyB3ZSByZXBlYXQgdGhlIHByZXZpb3VzIHR3byBwYXJ0cyBmb3IgZWFjaCB0aW1lICR0JC4gIElmIHRoZSBpbnRlcmVzdCBpcyBpbiBsZWFybmluZyBhYm91dCAkXGJldGFfMSxcbGRvdHMsXGJldGFfbiQgYW5kICQoXHNpZ21hXjIsXHRhdV4yKSQgam9pbnRseSBhbmQgY29uZGl0aW9uYWxseSBvbiAkRF9uJCwgaS5lLiB0aGUgd2hvbGUgZGF0YSwgdGhlbiB3ZSBjYW4gZWFzaWx5IGltcGxlbWVudCBhbiBNQ01DIHNjaGVtZSB0aGF0IGN5Y2xlcyB0aHJvdWdodCB0aGUgZnVsbCBjb25kaXRpb25hbHM6CiRwKFxiZXRhX3R8XGJldGFfey10fSxcc2lnbWFeMixcdGF1XjIseV97MTpufSx4X3sxOm59KSQsZm9yICR0PTEsXGxkb3RzLG4kLCAkcChcc2lnbWFeMnxcYmV0YV97MTpufSxcdGF1XjIseV97MTpufSx4X3sxOm59KSQsYW5kIAokcChcdGF1XjJ8XGJldGFfezE6bn0sXHNpZ21hXjIseV97MTpufSx4X3sxOm59KSQuCgoKIyMgRnVsbCBjb25kaXRpb25hbCBvZiAkXHNpZ21hXjIkCgpMZXQgdXMgYXNzdW1lIHRoYXQgJFxzaWdtYV4yIFxzaW0gSUcoYV8wLGJfMCkkLCBzbwokJApcc2lnbWFeMnxcYmV0YV97MTpufSx5X3sxOm59LHhfezE6bn0gXHNpbSBJR1xsZWZ0KGFfMCtcZnJhY3tufXsyfSxiXzArXGZyYWN7XHN1bV97dD0xfV5uICh5X3QtXGJldGFfdCB4X3QpXjJ9ezJ9XHJpZ2h0KS4KJCQKCiMjIEZ1bGwgY29uZGl0aW9uYWwgb2YgJFx0YXVeMiQKCkxldCB1cyBhc3N1bWUgdGhhdCAkXHRhdV4yIFxzaW0gSUcoY18wLGRfMCkkLCBzbwokJApcdGF1XjJ8XGJldGFfezE6bn0gXHNpbSBJR1xsZWZ0KGNfMCtcZnJhY3tuLTF9ezJ9LGRfMCtcZnJhY3tcc3VtX3t0PTJ9Xm4gKFxiZXRhX3QtXGJldGFfe3QtMX0pXjJ9ezJ9XHJpZ2h0KS4KJCQKCiMjIEZ1bGwgY29uZGl0aW9uYWwgb2YgJFxiZXRhX3QkCgpGb3IgJHQ9MSQKJCQKcChcYmV0YV8xfFxiZXRhX3stMX0seV97MTpufSx4X3sxOm59LFxzaWdtYV4yLFx0YXVeMikgPSAKcChcYmV0YV8xKXAoXGJldGFfMnxcYmV0YV8xLFx0YXVeMikKcCh5XzF8XGJldGFfMSx4XzEsXHNpZ21hXjIpIFxlcXVpdiBOKGJfMSxWXzEpLAokJAp3aGVyZSAkVl8xID0gMS8oMS8oQ18wK1x0YXVeMikrMS9cdGF1XjIreF8xXjIvXHNpZ21hXjIpJCBhbmQgJGJfMSA9IFZfMSAobV8wLyhDXzArXHRhdV4yKStcYmV0YV8yL1x0YXVeMit4XzF5XzEvXHNpZ21hXjIpJC4gIFJlY2FsbCB0aGF0ICRcYmV0YV8wIFxzaW0gTihtXzAsQ18wKSQsIHNvIHRoZSBwcmlvciAkXGJldGFfMSBcc2ltIE4obV8wLENfMCtcdGF1XjIpJC4KCkl0IGlzIHN0cmFpZ2hmb3J3YXJkIHRvIHNlZSB0aGF0LCBmb3IgJHQ9MixcbGRvdHMsbi0xJCwgCiQkCnAoXGJldGFfdHxcYmV0YV97LXR9LHlfezE6bn0seF97MTpufSxcc2lnbWFeMixcdGF1XjIpID0gCnAoXGJldGFfdHxcYmV0YV97dC0xfSxcdGF1XjIpCnAoXGJldGFfe3QrMX18XGJldGFfdCxcdGF1XjIpCnAoeV90fFxiZXRhX3QseF90LFxzaWdtYV4yKSBcZXF1aXYgTihiX3QsVl90KSwKJCQKd2hlcmUgJFZfdCA9IDEvKDIvXHRhdV4yK3hfdF4yL1xzaWdtYV4yKSQgYW5kICRiX3Q9Vl90KChcYmV0YV97dC0xfStcYmV0YV97dCsxfSkvXHRhdV4yK3hfdHlfdC9cc2lnbWFeMikkLgoKCkZvciAkdD1uJAokJApwKFxiZXRhX258XGJldGFfey1ufSx5X3sxOm59LHhfezE6bn0sXHNpZ21hXjIsXHRhdV4yKSA9IApwKFxiZXRhX258XGJldGFfe24tMX0sXHRhdV4yKQpwKHlfbnxcYmV0YV9uLHhfbixcc2lnbWFeMikgXGVxdWl2IE4oYl9uLFZfbiksCiQkCndoZXJlICRWX24gPSAxLygxL1x0YXVeMit4X25eMi9cc2lnbWFeMikkIGFuZCAkYl9uID0gVl9uIChcYmV0YV97bi0xfS9cdGF1XjIreF9ueV9uL1xzaWdtYV4yKSQuCgpgYGB7cn0KYTA9MjtiMD0xO2MwPTI7ZDA9MQphMD0wLjAxO2IwPTAuMDE7YzA9MC4wMTtkMD0wLjAxCgojIEluaXRpYWwgdmFsdWVzCmJldGEgID0gbWYKc2lnMiAgPSBzaWcyLmhhdAp0YXUyICA9IHRhdTIuaGF0Ck0wICAgID0gMjAwMDAKTSAgICAgPSA1MDAwCm5pdGVyID0gTTAgKyBNCmRyYXdzID0gbWF0cml4KDAsbml0ZXIsbisyKQpmb3IgKGl0ZXIgaW4gMTpuaXRlcil7CiAgc2lnMiA9IDEvcmdhbW1hKDEsYTArbi8yLGIwK3N1bSgoeS1iZXRhKngpXjIpLzIpCiAgdGF1MiA9IDEvcmdhbW1hKDEsYzArKG4tMSkvMixkMCtzdW0oKGJldGFbMjpuXS1iZXRhWzE6KG4tMSldKV4yKS8yKQogIFYxID0gMS8oMS8oQzArdGF1MikrMS90YXUyK3hbMV1eMi9zaWcyKQogIGIxID0gVjEqKG0wLyhDMCt0YXUyKStiZXRhWzJdL3RhdTIreFsxXSp5WzFdL3NpZzIpCiAgYmV0YVsxXSA9IHJub3JtKDEsYjEsc3FydChWMSkpCiAgZm9yICh0IGluIDI6KG4tMSkpewogICAgVnQgPSAxLygyL3RhdTIreFt0XV4yL3NpZzIpICAgICAgICAgCiAgICBidCA9IFZ0KigoYmV0YVt0LTFdK2JldGFbdCsxXSkvdGF1Mit4W3RdKnlbdF0vc2lnMikKICAgIGJldGFbdF0gPSBybm9ybSgxLGJ0LHNxcnQoVnQpKQogIH0gIAogIFZuID0gMS8oMS90YXUyK3hbbl1eMi9zaWcyKQogIGJuID0gVm4qKGJldGFbbi0xXS90YXUyK3hbbl0qeVtuXS9zaWcyKQogIGJldGFbbl0gPSBybm9ybSgxLGJuLHNxcnQoVm4pKQogIGRyYXdzW2l0ZXIsXSA9IGMoYmV0YSxzaWcyLHRhdTIpCn0KZHJhd3MgPSBkcmF3c1soTTArMSk6bml0ZXIsXQpgYGAKCiMjIFRyYWNlIHBsb3RzCgpgYGB7ciBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9OH0KcGFyKG1mcm93PWMoMiwzKSkKdHMucGxvdChkcmF3c1ssMV0sbWFpbj1leHByZXNzaW9uKGJldGFbMV0pLHhsYWI9Iml0ZXJhdGlvbnMiLHlsYWI9IiIpCnRzLnBsb3QoZHJhd3NbLDEwMF0sbWFpbj1leHByZXNzaW9uKGJldGFbMTAwXSkseGxhYj0iaXRlcmF0aW9ucyIseWxhYj0iIikKdHMucGxvdChkcmF3c1ssMjAwXSxtYWluPWV4cHJlc3Npb24oYmV0YVsyMDBdKSx4bGFiPSJpdGVyYXRpb25zIix5bGFiPSIiKQp0cy5wbG90KGRyYXdzWyxuXSxtYWluPWV4cHJlc3Npb24oYmV0YVtuXSkseGxhYj0iaXRlcmF0aW9ucyIseWxhYj0iIikKdHMucGxvdChkcmF3c1ssbisxXSxtYWluPWV4cHJlc3Npb24oc2lnbWFeMikseGxhYj0iaXRlcmF0aW9ucyIseWxhYj0iIikKdHMucGxvdChkcmF3c1ssbisyXSxtYWluPWV4cHJlc3Npb24odGF1XjIpLHhsYWI9Iml0ZXJhdGlvbnMiLHlsYWI9IiIpCmBgYAoKIyMgTWFya292IGNoYWluIGF1dG9jb3JyZWxhdGlvbnMKCmBgYHtyIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD04fQpwYXIobWZyb3c9YygxLDMpKQphY2YoZHJhd3NbLG4rMV0sbWFpbj1leHByZXNzaW9uKHNpZ21hXjIpLHlsYWI9IiIpCmFjZihkcmF3c1ssbisyXSxtYWluPWV4cHJlc3Npb24odGF1XjIpLHlsYWI9IiIpCmFjZnMgPSBtYXRyaXgoMCxuLDUwKQpmb3IgKGkgaW4gMTpuKXsKICBhY2YgPSBhY2YoZHJhd3NbLGldLGxhZy5tYXg9NTAscGxvdD1GQUxTRSkKICBhY2ZzW2ksXSA9IGFjZiRhY2ZbMjo1MV0KfQpwbG90KGFjZnNbMSxdLHlsaW09cmFuZ2UoYWNmcykseWxhYj0iQUNGIix0eXBlPSJsIix4bGFiPSJMYWciKQpmb3IgKGkgaW4gMjpuKQogIGxpbmVzKGFjZnNbaSxdKQpgYGAKCiMjIE1hcmdpbmFsIHBvc3RlcmlvciBkZW5zaXRpZXMKCkJlbG93IHdlIHBsb3QgaGlzdG9ncmFtIGFwcHJveGltYXRpb25zIG9mIG1hcmdpbmFsIHBvc3RlcmlvciBkZW5zaXRpZXMgZm9yIHZhcmlvdXMgcGFyYW1ldGVycywgc3VjaCBhcyAkcChcYmV0YV8xfERfbikkLCAkcChcYmV0YV8yMDB8RF9uKSQsICRwKFxzaWdtYV4yfERfbikkIGFuZCAkcChcdGF1XjJ8RF9uKSQuCgpgYGB7ciBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9OH0KcGFyKG1mcm93PWMoMiwzKSkKaGlzdChkcmF3c1ssMV0sbWFpbj1leHByZXNzaW9uKGJldGFbMV0pLHByb2I9VFJVRSx4bGFiPSIiKQpoaXN0KGRyYXdzWywxMDBdLG1haW49ZXhwcmVzc2lvbihiZXRhWzEwMF0pLHByb2I9VFJVRSx4bGFiPSIiKQpoaXN0KGRyYXdzWywyMDBdLG1haW49ZXhwcmVzc2lvbihiZXRhWzIwMF0pLHByb2I9VFJVRSx4bGFiPSIiKQpoaXN0KGRyYXdzWyxuXSxtYWluPWV4cHJlc3Npb24oYmV0YVtuXSkscHJvYj1UUlVFLHhsYWI9IiIpCmhpc3QoZHJhd3NbLG4rMV0sbWFpbj1leHByZXNzaW9uKHNpZ21hXjIpLHByb2I9VFJVRSx4bGFiPSIiKQpoaXN0KGRyYXdzWyxuKzJdLG1haW49ZXhwcmVzc2lvbih0YXVeMikscHJvYj1UUlVFLHhsYWI9IiIpCmBgYAoKCiMjIEpvaW50IHBvc3RlcmlvciAkcChcc2lnbWFeMixcdGF1XjJ8RF9uKSQKClRoZSBwaWN0dXJlIGFib3ZlIHNob3dzIHRoZSBjb250b3VycyBvZiAkcChcc2lnbWFeMixcdGF1XjJ8RF9uKSQgZXZhbHVhdGVkIG9uIGEgZmluZSBncmlkLiAgVGhlIHVuZGVybmVhdGggaW1hZ2UgaXMgYSAyRC1oaXN0b2dyYW0gcmVjb25zdHJ1Y3Rpb24gdGhpcyBqb2ludCBkaXN0cmlidXRpb24gYmFzZWQgb24gdGhlIHBvc3RlcmlvciBkcmF3cyBvZiAkXHNpZ21hXjIkIGFuZCAkXHRhdV4yJC4gIAoKYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTZ9CmxkaWdhbW1hPWZ1bmN0aW9uKHgsYSxiKXtkZ2FtbWEoMS94LGEsYixsb2c9VFJVRSktMipsb2coeCl9CmxvZ3ByaW9yID0gbWF0cml4KDAsTixOKQpmb3IgKGkgaW4gMTpOKQogIGZvciAoaiBpbiAxOk4pCiAgICBsb2dwcmlvcltpLGpdID0gbGRpZ2FtbWEoc2lnMnNbaV0sYTAsYjApK2xkaWdhbW1hKHRhdTJzW2pdLGMwLGQwKQpsb2dwb3N0ZXJpb3IgPSBsb2dwcmlvcitsb2dsaWtlbGlob29kCgpsaWJyYXJ5KE1BU1MpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQoKcmYgPSBjb2xvclJhbXBQYWxldHRlKHJldihicmV3ZXIucGFsKDExLCdTcGVjdHJhbCcpKSkKciAgPSByZigzMikKeHggPSBkcmF3c1ssbisxXQp5eSA9IGRyYXdzWyxuKzJdCmRmID0gZGF0YS5mcmFtZSh4eCx5eSkKayA9IGtkZTJkKGRmJHh4LCBkZiR5eSwgbj0yMDApCgpzaWcyLm1vZGUgPSBrJHhbYXBwbHkoayR6PT1tYXgoayR6KSwxLHN1bSk9PTFdCnRhdTIubW9kZSA9IGskeVthcHBseShrJHo9PW1heChrJHopLDIsc3VtKT09MV0KaW1hZ2UoaywgY29sPXIseGxhYj1leHByZXNzaW9uKHNpZ21hXjIpLHlsYWI9ZXhwcmVzc2lvbih0YXVeMikpCnBvaW50cyhzaWcyLm1vZGUsdGF1Mi5tb2RlLGNvbD00LHBjaD0xNikKcG9pbnRzKHNpZzIuaGF0LHRhdTIuaGF0LGNvbD0zLHBjaD0xNikKY29udG91cihzaWcycyx0YXUycyxleHAobG9ncG9zdGVyaW9yKSxhZGQ9VFJVRSxkcmF3bGFiZWxzPUZBTFNFKQpgYGAKCkJlbG93IHdlIHNob3cgdGhlIHZhbHVlcyBvZiAkXHNpZ21hXjIkIGFuZCAkXHRhdV4yJCB0aGF0IG1heGltaXplIHRoZSBsaWtlbGlob29kIGZ1bnRpb24gYW5kIHRoZSBqb2ludCBwb3N0ZXJpb3IsIHJlc3BlY3RpdmVseS4gIEJlY2F1c2Ugd2UgYXJlIHVzaW5nIGEgcHJpb3IgdGhhdCBpcyBmYWlybHkgdW5pbmZvcm1hdGl2ZSwgYm90aCBlc3RpbWF0b3JzIGFyZSBwYXJ0aWN1bGFybHkgc2ltaWxhci4KCgpgYGB7cn0KYyhzaWcyLmhhdCxzaWcyLm1vZGUpCmModGF1Mi5oYXQsdGF1Mi5tb2RlKQpgYGAKCgojIyBTbW9vdGhlZCBzdGF0ZXMgbWFyZ2luYWwgcG9zdGVyaW9yIGRlbnNpdGllcwoKQmVsb3cgd2UgcGxvdCB0aGUgMi41dGgsIDUwdGggYW5kIDk3LjV0aCBwZXJjZW50aWxlcyBvZgokJApwKFxiZXRhX3R8RF9uKSBcIFwgXCBcbWJveHthbmR9IFwgXCBcIHAoXGJldGFfdHxEX3Qse1xoYXQgXHNpZ21hfV4yLHtcaGF0IFx0YXV9XjIpLgokJApXZSBhcmVzIHN0aWxsIGEgYml0IHNob3J0IG9mIHByb2R1Y2luZyAkcChcYmV0YV90fERfdCkkLCBpLmUuIHRoZSBvbmxpbmUgZGlzdHJpYnV0aW9uIG9mIHRoZSBzdGF0ZXMgdW5jb25kaXRpb25hbGx5IG9uICQoXHNpZ21hXjIsXHRhdV4yKSQuICBXZSB3aWxsIGJlIGFibGUgdG8gZG8gdGhhdCBvbmNlIHdlIHRhbGsgYWJvdXQgc2VxdWVudGlhbCBNb250ZSBDYXJsbyB0b29scywgYWxzbyBrbm93biBhcyAqcGFydGljbGUgZmlsdGVycyouCgpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0KcWJldGEgPSB0KGFwcGx5KGRyYXdzWywxOm5dLDIscXVhbnRpbGUsYygwLjAyNSwwLjUsMC45NzUpKSkKcmFuZ2UgPSByYW5nZShxYmV0YSxxZikKcGFyKG1mcm93PWMoMSwxKSkKdHMucGxvdChxYmV0YSxsdHk9YygyLDEsMikseWxpbT1yYW5nZSx4bGFiPSJUaW1lIix5bGFiPWV4cHJlc3Npb24oYmV0YSksbWFpbj0iIikKbGluZXMocWZbLDFdLGNvbD0yLGx0eT0yKQpsaW5lcyhtZixjb2w9MixsdHk9MSkKbGluZXMocWZbLDJdLGNvbD0yLGx0eT0yKQpzZWdtZW50cygxLGdhbW1hMSxuLzMsZ2FtbWExLGNvbD00KQpzZWdtZW50cyhuLzMrMSxnYW1tYTIsMipuLzMsZ2FtbWEyLGNvbD00KQpzZWdtZW50cygyKm4vMysxLGdhbW1hMyxuLGdhbW1hMyxjb2w9NCkKYWJsaW5lKHY9bi8zLGx0eT0yLGNvbD02KQphYmxpbmUodj0yKm4vMyxsdHk9Mixjb2w9NikKbGVnZW5kKCJ0b3ByaWdodCIsbGVnZW5kPWMoIlNtb290aGVkIGRlbnNpdGllcyAoTUNNQykiLCJGaWx0ZXJlZCBkZW5zaXRpZXMgKGdpdmVuIHNpZzIsdGF1MikiKSxjb2w9MToyLGx0eT0xKQpgYGAKCgojIEZpbmFsIGltcHJvdmVtZW50OiBzYW1wbGluZyAkXGJldGFfMSxcbGRvdHMsXGJldGFfbiQgam9pbnRseQoKSW5zdGVhZCBvZiBzYW1wbGluZyAkXGJldGFfdCQgZnJvbSBpdHMgZnVsbCBjb25kaXRpb25hbCwgdGhlICpmb3J3YXJkIGZpbHRlcmluZywgYmFja3dhcmQgc2FtcGxpbmcqIHNjaGVtZSBzYW1wbGVzICRcYmV0YV8xLFxsZG90cyxcYmV0YV9uJCBqb2ludGx5LiAgTW9yZSBzcGVjaWZpY2FsbHksIEZGQlMgdXNlcyB0aGUgZm9sbG93aW5nIHJlc3VsdHM6CiQkCnAoXGJldGFfMSxcbGRvdHMsXGJldGFfbnxcc2lnbWFeMixcdGF1XjIsRF9uKSA9IHAoXGJldGFfbnxcc2lnbWFeMixcdGF1XjIsRF9uKQpccHJvZF97dD0yfV5uIHAoXGJldGFfdHxcYmV0YV97dCsxfSxcc2lnbWFeMixcdGF1XjIsRF9uKSwKJCQKd2hlcmUgJChcYmV0YV9ufFxzaWdtYV4yLFx0YXVeMixEX24pIFxzaW0gTih7XHRpbGRlIG19X24se1x0aWxkZSBDfV9uKSQsIHdpdGggCiR7XHRpbGRlIG19X249bV9uJCBhbmQgJHtcdGlsZGUgQ31fbik9Q19uJC4gIEFsc28sCiQoXGJldGFfdHxcYmV0YV97dCsxfSxcc2lnbWFeMixcdGF1XjIsRF9uKSBcc2ltIE4oe1x0aWxkZSBtfV9uLHtcdGlsZGUgQ31fbikkLCB3aGVyZQokJAp7XHRpbGRlIEN9X3QgPSAxLygxL1x0YXVeMisxL0NfdCkgXCBcIFxtYm94e2FuZH0gXCBcIAp7XHRpbGRlIG19X3QgPSAgQ190KFxiZXRhX3t0KzF9L1x0YXVeMiArIG1fdC9DX3QpLAokJApmb3IgJHQ9bi0xLFxsZG90cywxJC4KCmBgYHtyfQojIE1DTUMgc2NoZW1lCmJldGEgICA9IG1mCnNpZzIgICA9IHNpZzIuaGF0CnRhdTIgICA9IHRhdTIuaGF0Ck0wICAgICA9IDIwMDAwCk0gICAgICA9IDUwMDAKbml0ZXIgID0gTTAgKyBNCm1zICAgICA9IHJlcCgwLG4pCkNzICAgICA9IHJlcCgwLG4pCm0gICAgICA9IG0wCkMgICAgICA9IEMwCmRyYXdzMSA9IG1hdHJpeCgwLG5pdGVyLG4rMikKZm9yIChpdGVyIGluIDE6bml0ZXIpewogIGZvciAodCBpbiAxOm4pewogICAgYXQgICAgPSBtCiAgICBSdCAgICA9IEMrdGF1MgogICAgZnQgICAgPSB4W3RdKmF0CiAgICBRdCAgICA9IHhbdF1eMipSdCtzaWcyCiAgICBBdCAgICA9IFJ0KnhbdF0vUXQKICAgIG0gICAgID0gYXQgKyBBdCooeVt0XS1mdCkKICAgIEMgICAgID0gUnQgLSBBdF4yKlF0CiAgICBtc1t0XSA9IG0KICAgIENzW3RdID0gQwogIH0KICBiZXRhcyA9IHJlcCgwLG4pCiAgYmV0YXNbbl0gPSBybm9ybSgxLG1zW25dLHNxcnQoQ3Nbbl0pKQogIGZvciAodCBpbiAobi0xKToxKXsKICAgIHZhciA9IDEvKDEvdGF1MisxL0NzW3RdKQogICAgbWVhbiA9IHZhciooYmV0YXNbdCsxXS90YXUyK21zW3RdL0NzW3RdKQogICAgYmV0YXNbdF0gPSBybm9ybSgxLG1lYW4sc3FydCh2YXIpKQkKICB9CgogICMgc2FtcGxpbmcgc2lnMgogIHNpZzIgPSAxL3JnYW1tYSgxLGEwK24vMixiMCtzdW0oKHkteCpiZXRhcyleMikvMikKCiAgIyBzYW1wbGluZyB0YXUyCiAgdGF1MiA9IDEvcmdhbW1hKDEsYzArKG4tMSkvMixkMCtzdW0oKGJldGFzWzI6bl0tYmV0YXNbMToobi0xKV0pXjIpLzIpCgogICMgc3RvcmluZyBkcmF3cwogIGRyYXdzMVtpdGVyLF0gPSBjKGJldGFzLHNpZzIsdGF1MikKfQpkcmF3czEgPSBkcmF3czFbKE0wKzEpOm5pdGVyLF0KcWJldGExID0gdChhcHBseShkcmF3czFbLDE6bl0sMixxdWFudGlsZSxjKDAuMDI1LDAuNSwwLjk3NSkpKQpgYGAKCgpgYGB7ciBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9OH0KcGFyKG1mcm93PWMoMiwzKSkKcGxvdChkZW5zaXR5KGRyYXdzWywxXSksbWFpbj1leHByZXNzaW9uKGJldGFbMV0pLHhsYWI9IiIpCmxpbmVzKGRlbnNpdHkoZHJhd3MxWywxXSksY29sPTIpCnBsb3QoZGVuc2l0eShkcmF3c1ssMTAwXSksbWFpbj1leHByZXNzaW9uKGJldGFbMTAwXSkseGxhYj0iIikKbGluZXMoZGVuc2l0eShkcmF3czFbLDEwMF0pLGNvbD0yKQpwbG90KGRlbnNpdHkoZHJhd3NbLDIwMF0pLG1haW49ZXhwcmVzc2lvbihiZXRhWzIwMF0pLHhsYWI9IiIpCmxpbmVzKGRlbnNpdHkoZHJhd3MxWywyMDBdKSxjb2w9MikKcGxvdChkZW5zaXR5KGRyYXdzWyxuXSksbWFpbj1leHByZXNzaW9uKGJldGFbbl0pLHhsYWI9IiIpCmxpbmVzKGRlbnNpdHkoZHJhd3MxWyxuXSksY29sPTIpCnBsb3QoZGVuc2l0eShkcmF3c1ssbisxXSksbWFpbj1leHByZXNzaW9uKHNpZ21hXjIpLHhsYWI9IiIpCmxpbmVzKGRlbnNpdHkoZHJhd3MxWyxuKzFdKSxjb2w9MikKcGxvdChkZW5zaXR5KGRyYXdzWyxuKzJdKSxtYWluPWV4cHJlc3Npb24odGF1XjIpLHhsYWI9IiIpCmxpbmVzKGRlbnNpdHkoZHJhd3MxWyxuKzJdKSxjb2w9MikKYGBgCgoKIyMgJFxiZXRhX3QkIGF1dG9jb3JyZWxhdGlvbnMKCmBgYHtyIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD04fQpwYXIobWZyb3c9YygxLDIpKQphY2ZzMSA9IG1hdHJpeCgwLG4sNTApCmZvciAoaSBpbiAxOm4pewogIGFjZiA9IGFjZihkcmF3czFbLGldLGxhZy5tYXg9NTAscGxvdD1GQUxTRSkKICBhY2ZzMVtpLF0gPSBhY2YkYWNmWzI6NTFdCn0KcGxvdChhY2ZzWzEsXSx5bGltPXJhbmdlKGFjZnMsYWNmczEpLHlsYWI9IkFDRiIsdHlwZT0ibCIseGxhYj0iTGFnIikKZm9yIChpIGluIDI6bikKICBsaW5lcyhhY2ZzW2ksXSkKcGxvdChhY2ZzMVsxLF0seWxpbT1yYW5nZShhY2ZzLGFjZnMxKSx5bGFiPSJBQ0YiLHR5cGU9ImwiLHhsYWI9IkxhZyIpCmZvciAoaSBpbiAyOm4pCiAgbGluZXMoYWNmczFbaSxdKQpgYGAKCgoKYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTZ9CnJhbmdlID0gcmFuZ2UocWJldGEscWJldGExKQpwYXIobWZyb3c9YygxLDEpKQp0cy5wbG90KHFiZXRhLGx0eT1jKDIsMSwyKSx5bGltPXJhbmdlLHhsYWI9IlRpbWUiLHlsYWI9ZXhwcmVzc2lvbihiZXRhKSxtYWluPSIiKQpsaW5lcyhxYmV0YTFbLDFdLGNvbD0yLGx0eT0yKQpsaW5lcyhxYmV0YTFbLDJdLGNvbD0yLGx0eT0xKQpsaW5lcyhxYmV0YTFbLDNdLGNvbD0yLGx0eT0yKQpzZWdtZW50cygxLGdhbW1hMSxuLzMsZ2FtbWExLGNvbD00KQpzZWdtZW50cyhuLzMrMSxnYW1tYTIsMipuLzMsZ2FtbWEyLGNvbD00KQpzZWdtZW50cygyKm4vMysxLGdhbW1hMyxuLGdhbW1hMyxjb2w9NCkKYWJsaW5lKHY9bi8zLGx0eT0yLGNvbD02KQphYmxpbmUodj0yKm4vMyxsdHk9Mixjb2w9NikKbGVnZW5kKCJ0b3ByaWdodCIsbGVnZW5kPWMoIkJsb2NrIHNhbXBsaW5nIiwiSW5kaXZpZHVhbCBzYW1wbGluZyIpLGNvbD0xOjIsbHR5PTEpCmBgYAo=