Local level model set
up
The following exercise is inspired in Gamerman, Reis and Salazar
(2006) Comparison of sampling schemes for dynamic linear models.
International Statistical Review, 74, 203-214.
The purpose of the exercise is to compare two MCMC schemes to sample
states and fixed parameters in the first order dynamic linear model,
also known as the local level model that we have already discussed in
class. More specifically, the local level model assumes that the time
series data, \(\mbox{data}=\{y_1,\ldots,y_n\}\), can be
described as follows, for \(t=1,\ldots,n\) \[\begin{eqnarray*}
y_t &=& \beta_t + \nu_t \qquad \nu_t \sim N(0,V)\\
\beta_t &=& \beta_{t-1} + \omega_t \qquad \omega_t \sim N(0,W),
\end{eqnarray*}\] with \(\beta_0 \sim
N(m_0,C_0)\) and where \(W/V\)
is commonly known as the signal to noise ratio (SNR). Here \(m_0\) and \(C_0\) are known hyperparameters.
The model is completed with prior distributions for the error
variances, \(V\) and \(W\). Here, we will assume that \[\begin{eqnarray*}
V &\sim& IG(a_\nu,b_\nu)\\
W &\sim& IG(a_\omega,b_\omega),
\end{eqnarray*}\] \(a_\nu,b_\nu,a_\omega,b_\omega\) known
hyperparameters.
Simulating one
dataset
For simplication, we created the data simulation function
sample.data.
sample.data = function(n,W,V,beta0){
sdW = sqrt(W)
sdV = sqrt(V)
beta = rep(0,n)
y = rep(0,n)
beta[1] = rnorm(1,beta0,sdW)
y[1] = rnorm(1,beta[1],sdV)
for (t in 2:n){
beta[t] = rnorm(1,beta[t-1],sdW)
y[t] = rnorm(1,beta[t],sdV)
}
return(y)
}
We simulated \(n=100\) observations,
starting with \(\beta_0=0\) and fixing
\(V=1.0\) and \(W=0.25\) so the SNR is 0.25.
set.seed(17389)
n = 100
V = 1.0
W = 0.25
beta0 = 0.0
y = sample.data(n,W,V,beta0)
ts.plot(y)

Block-move
The following routine mcmc.joint samples \((\beta_1,\ldots,\beta_n)\), \(V\) and \(W\) iteratively from their full conditional
posterior distributions, ie. \[\begin{eqnarray*}
p(\beta_1,\ldots,\beta_n|V,W,\mbox{data})\\
p(V|\beta_1,\ldots,\beta_n,W,\mbox{data})\\
p(W|\beta_1,\ldots,\beta_n,V,\mbox{data}),
\end{eqnarray*}\] and returns an \(M
\times (n+2)\) matrix with \(M\)
draws of the \((n+2)\)-dimensional
vector \((\beta_1,\ldots,\beta_n,V,W)\).
The full conditional distributions of \(V\) and \(W\) are both Inverse-Gamma distributions,
while the full conditional distribution of \((\beta_1,\ldots,\beta_n)\) is \(n\)-variate normal distribution and its
joint sampling is facilitated by the kalman recursions, both filtering
and smoothing.
Below, \(a_1=m_0\) and \(R_1=C_0+W\), while \(M_0\) and \(M\) are MCMC draws, with the first \(M_0\) draws discarded (burn-in or warm-up)
and the following \(M\) draws stores
for posterior inference. Initial values for \(V\) and \(W\) are the two last arguments of the
function mcmc.joint.
mcmc.joint = function(y,a1,R1,av,bv,aw,bw,M0,M,V,W){
n = length(y)
V.draw = V
W.draw = W
niter = M0+M
draws = matrix(0,niter,n+2)
for (iter in 1:niter){
beta.draw = ffbs(y,rep(1,n),0.0,V.draw,1.0,0.0,W.draw,a1,R1,nd=1)
par2v = bv+sum((y-beta.draw)^2)/2
par2w = bw+sum((beta.draw[2:n]-beta.draw[1:(n-1)])^2)/2
V.draw = 1/rgamma(1,av+n/2,par2v)
W.draw = 1/rgamma(1,aw+(n-1)/2,par2w)
draws[iter,] = c(beta.draw,V.draw,W.draw)
}
return(draws[(M0+1):niter,])
}
Sampling from \(p(\beta_1,\ldots,\beta_n|V,W,\mbox{data})\)
The following routine ffbs was actually written to
be able to sample the states jointly via the forward filtering backward
sampling (FFBS) scheme for a more general class of normal dynamic linear
model (NDLM):
\[\begin{eqnarray*}
y_t &=& F_t^T \theta_t + \nu_t \qquad \nu_t \sim N(0,V)\\
\theta_t &=& \gamma + G \theta_{t-1} + \omega_t \qquad \omega_t
\sim N(0,W),
\end{eqnarray*}\] where \(F_t\)
is a vector of observed components, with \(\theta_t\) a state vector of the same
dimensions. If \(F_t\) are the usual
definition of regressors in linear regression models, then \(\theta_t\) are the time-varying regression
coefficients. For simple local level model, we have that \(F_t=1, \ \ \forall t\), \(\gamma=0\), \(G=1\) and \(\theta_t=\beta_t\). Another special case,
that we have studied in this class, is the AR(1) plus noise model: \[\begin{eqnarray*}
y_t &=& \theta_t + \nu_t \qquad\qquad \nu_t \sim N(0,V)\\
\theta_t &=& \gamma + \phi \theta_{t-1} + \omega_t \qquad
\omega_t \sim N(0,W).
\end{eqnarray*}\]
We use the function ffbs to draw \(\beta_1,\ldots,\beta_n\) in the MCMC
function mcmc.joint.
ffbs = function(y,Ft,alpha,V,G,gamma,W,a1,R1,nd=1){
n = length(y)
if (length(Ft)==1){Ft=rep(Ft,n)}
if (length(alpha)==1){alpha=rep(alpha,n)}
if (length(V)==1){V=rep(V,n)}
a = rep(0,n)
R = rep(0,n)
m = rep(0,n)
C = rep(0,n)
B = rep(0,n-1)
H = rep(0,n-1)
# time t=1
a[1] = a1
R[1] = R1
f = alpha[1]+Ft[1]*a[1]
Q = R[1]*Ft[1]**2+V[1]
A = R[1]*Ft[1]/Q
m[1] = a[1]+A*(y[1]-f)
C[1] = R[1]-Q*A**2
# forward filtering
for (t in 2:n){
a[t] = gamma + G*m[t-1]
R[t] = C[t-1]*G**2 + W
f = alpha[t]+Ft[t]*a[t]
Q = R[t]*Ft[t]**2+V[t]
A = R[t]*Ft[t]/Q
m[t] = a[t]+A*(y[t]-f)
C[t] = R[t]-Q*A**2
B[t-1] = C[t-1]*G/R[t]
H[t-1] = sqrt(C[t-1]-R[t]*B[t-1]**2)
}
# backward sampling
theta = matrix(0,nd,n)
theta[,n] = rnorm(nd,m[n],sqrt(C[n]))
for (t in (n-1):1)
theta[,t] = rnorm(nd,m[t]+B[t]*(theta[,t+1]-a[t+1]),H[t])
if (nd==1){
theta[1,]
}
else{
theta
}
}
Posterior
inference
# Prior for beta(0) ~ N(m0,C0)
# Prior for beta(1) ~ N(a1,R1), a1=m0,R1=C0+W
a1 = 0
R1 = 10
av = 2.01
bv = 1.01
aw = 2.01
bw = 1.01*W
m0 = 0
C0 = 10-W
# MCMC initual values
V0 = 1
W0 = 0.5
M0 = 10000
M = 10000
set.seed(65432)
draws1 = mcmc.joint(y,a1,R1,av,bv,aw,bw,M0,M,V0,W0)
# states MCMC autocorrelations and posterior quantiles
q1 = t(apply(draws1[,1:n],2,quantile,c(0.05,0.5,0.95)))
lagmax = 30
acfs = array(0,c(2,n,lagmax))
for (i in 1:n)
acfs[1,i,] = acf(draws1[,i],plot=FALSE,lag.max=lagmax)$acf[2:(lagmax+1)]
Posterior inference
for \(V\) and \(W\)
par(mfrow=c(2,3),mar = c(2, 2, 1, 1))
ts.plot(draws1[,n+1],ylab="",xlab="Iteration",main="V")
acf(draws1[,n+1],main="")
hist(draws1[,n+1],prob=TRUE,main="",xlab="")
ts.plot(draws1[,n+2],ylab="",xlab="Iteration",main="W")
acf(draws1[,n+2],main="")
hist(draws1[,n+2],prob=TRUE,main="",xlab="")

MCMC
autocorrelation for states
par(mfrow=c(1,1),mar=c(5, 4, 4, 2))
ts.plot(t(acfs[1,,]),xlab="Lag",ylab="ACF",main="",ylim=range(acfs))

Posterior quantiles
for states
par(mfrow=c(1,1))
ts.plot(q1,col=c(3,2,3),ylim=range(y,q1),ylab="States")
points(y)
title("5th, 50th and 95th percentiles")

Single-move
The routine mcmc.single samples \(\beta_1,\ldots,\beta_n\), \(V\) and \(W\) iteratively from their univariate full
conditional posterior distributions, ie. \[\begin{eqnarray*}
p(V|\beta_1,\ldots,\beta_n,W,\mbox{data})\\
p(W|\beta_1,\ldots,\beta_n,V,\mbox{data}),
\end{eqnarray*}\] and \[
p(\beta_t|\beta_{-t},V,W,\mbox{data}) \qquad t=1,\ldots,n,
\] where \(\beta_{-t}\) is the
vector \((\beta_1,\ldots,\beta_n)\)
excluding the \(\beta_t\) state.
mcmc.single returns an \(M
\times (n+2)\) matrix with \(M\)
draws of the \((n+2)\)-dimensional
vector \((\beta_1,\ldots,\beta_n,V,W)\).
mcmc.single = function(y,a1,R1,av,bv,aw,bw,M0,M,beta,V,W){
n = length(y)
beta.draw = beta
V.draw = V
W.draw = W
niter = M0+M
draws = matrix(0,niter,n+2)
for (iter in 1:niter){
beta.draw = single.sampling(y,a1,R1,av,bv,aw,bw,M0,M,beta.draw,V.draw,W.draw)
par2v = bv+sum((y-beta.draw)^2)/2
par2w = bw+sum((beta.draw[2:n]-beta.draw[1:(n-1)])^2)/2
V.draw = 1/rgamma(1,av+n/2,par2v)
W.draw = 1/rgamma(1,aw+(n-1)/2,par2w)
draws[iter,] = c(beta.draw,V.draw,W.draw)
}
return(draws[(M0+1):niter,])
}
Sampling from \(p(\beta_t|\beta_{-t},V,W,\mbox{data})\)
The routine single.sampling samples, for \(t=1,\ldots,n\), from the univariate full
conditional distributons \[
p(\beta_t|\beta_{-t},V,W,\mbox{data}).
\]
single.sampling = function(y,a1,R1,av,bv,aw,bw,M0,M,beta,V,W){
V1 = 1/(1/R1+1/W+1/V)
b1 = V1*(a1/R1+beta[2]/W+y[1]/V)
beta[1] = rnorm(1,b1,sqrt(V1))
for (t in 2:(n-1)){
Vt = 1/(2/W+1/V)
bt = Vt*((beta[t-1]+beta[t+1])/W+y[t]/V)
beta[t] = rnorm(1,bt,sqrt(Vt))
}
Vn = 1/(1/W+1/V)
bn = Vn*(beta[n-1]/W+y[n]/V)
beta[n] = rnorm(1,bn,sqrt(Vn))
return(beta)
}
Posterior
inference
set.seed(65432)
beta0v = y
draws2 = mcmc.single(y,a1,R1,av,bv,aw,bw,M0,M,beta0v,V0,W0)
q2 = t(apply(draws2[,1:n],2,quantile,c(0.05,0.5,0.95)))
for (i in 1:n)
acfs[2,i,] = acf(draws2[,i],plot=FALSE,lag.max=lagmax)$acf[2:(lagmax+1)]
Posterior inference
for \(V\) and \(W\)
par(mfrow=c(2,3),mar = c(2, 2, 1, 1))
ts.plot(draws2[,n+1],ylab="",xlab="Iteration",main="V")
acf(draws2[,n+1],main="")
hist(draws2[,n+1],prob=TRUE,main="",xlab="")
ts.plot(draws2[,n+2],ylab="",xlab="Iteration",main="W")
acf(draws2[,n+2],main="")
hist(draws2[,n+2],prob=TRUE,main="",xlab="")

MCMC
autocorrelation for states
par(mfrow=c(1,1),mar=c(5, 4, 4, 2))
ts.plot(t(acfs[2,,]),xlab="Lag",ylab="ACF",main="",ylim=range(acfs))

Posterior quantiles
for states
par(mfrow=c(1,1))
ts.plot(q2,col=c(3,2,3),ylim=range(y,q2),ylab="States")
points(y)
title("5th, 50th and 95th percentiles")

Comparing MCMC
schemes
Comparing ACFs
par(mfrow=c(1,1),mar=c(5, 4, 4, 2))
ts.plot(t(acfs[2,,]),xlab="Lag",ylab="ACF",main="",ylim=range(acfs))
for (i in 1:n)
lines(acfs[1,i,],col=2)
legend("topright",legend=c("Single-move","Block-move"),col=1:2,lwd=2,bty="n")

Comparing state
posterior quantiles
par(mfrow=c(1,1),mar=c(5, 4, 4, 2))
ts.plot(q2)
lines(q1[,1],col=2)
lines(q1[,2],col=2)
lines(q1[,3],col=2)
legend("top",legend=c("Single-move","Block-move"),col=1:2,lwd=2,bty="n")

Comparing marginal
posterior for \(V\) and \(W\)
par(mfrow=c(1,2),mar = c(2, 2, 1, 1))
plot(density(draws2[,n+1]),main="V")
lines(density(draws1[,n+1]),col=2)
plot(density(draws2[,n+2]),main="W")
lines(density(draws1[,n+2]),col=2)
legend("topright",legend=c("Single-move","Block-move"),col=1:2,lwd=2,bty="n")

Replications
We replicate the above exercise for 20 sets of time series data and
run both MCMC schemes. We assume that for \(n=100\) and $W {0.01,0.25,1.0}.
set.seed(65432123)
nrep = 20
n = 100
V = 1.0
M0 = 1000
M = 1000
beta0 = 0.0
V0 = 1.0
W0 = 0.5
a1 = 0.0
R1 = 10.0
av = 2.01
bv = 1.01
aw = 2.01
m0 = 0.0
ws = c(0.01,0.25,1.0)
nws = length(ws)
neffs = array(0,c(nrep,nws,2))
for (rep in 1:nrep){
for (i in 1:3){
y = sample.data(n,ws[i],V,beta0)
beta0v = y
C0 = 10-W
bw = 1.01*W
draws1 = mcmc.joint(y,a1,R1,av,bv,aw,bw,M0,M,V0,W0)
acf1 = acf(draws1[,n+1]/draws1[,n+2],lag.max=31,plot=FALSE)
neff1 = M/(1+2*sum(acf1$acf[2:31]))
draws2 = mcmc.single(y,a1,R1,av,bv,aw,bw,M0,M,beta0v,V0,W0)
acf2 = acf(draws2[,n+1]/draws2[,n+2],lag.max=31,plot=FALSE)
neff2 = M/(1+2*sum(acf2$acf[2:31]))
neffs[rep,i,] = trunc(c(neff1,neff2))
}
}
Effective sample size
(ESS)
comb = c("(100,0.01)","(100,0.25)","(100,1)")
par(mfrow=c(1,1),mar=c(5, 4, 4, 2))
boxplot(cbind(neffs[,,1],neffs[,,2]),outline=FALSE,names=c(comb,comb),
xlab=paste("(n,W) for V=",V,sep=""))
abline(v=3.5)
legend("topleft",legend=c("Block-move"),bty="n")
legend("topright",legend=c("Single-move"),bty="n")
title(paste("MCMC posterior draws = ",M,sep=""))

Relative ESS
par(mfrow=c(1,1),mar=c(5, 4, 4, 2))
boxplot(neffs[,,2]/neffs[,,1],outline=FALSE,names=comb,ylab="Relative ESS",
xlab=paste("(n,W) for V=",V,sep=""))
abline(h=1,lty=2)
title("Ratio single-move to block-move")

LS0tCnRpdGxlOiAiRmlyc3Qgb3JkZXIgRExNIgpzdWJ0aXRsZTogIlNpbmdsZS1tb3ZlIHZzIGJsb2NrLW1vdmUiCmF1dGhvcjogIkhlZGliZXJ0IEYuIExvcGVzIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IHBhcGVyCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgdG9jX2NvbGxhcHNlZDogdHJ1ZQojICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCi0tLQogIAoKIyBMb2NhbCBsZXZlbCBtb2RlbCBzZXQgdXAKClRoZSBmb2xsb3dpbmcgZXhlcmNpc2UgaXMgaW5zcGlyZWQgaW4gR2FtZXJtYW4sIFJlaXMgYW5kIFNhbGF6YXIgKDIwMDYpIENvbXBhcmlzb24gb2Ygc2FtcGxpbmcgc2NoZW1lcyBmb3IgZHluYW1pYyBsaW5lYXIgbW9kZWxzLiAqSW50ZXJuYXRpb25hbCBTdGF0aXN0aWNhbCBSZXZpZXcqLCA3NCwgMjAzLTIxNC4KClRoZSBwdXJwb3NlIG9mIHRoZSBleGVyY2lzZSBpcyB0byBjb21wYXJlIHR3byBNQ01DIHNjaGVtZXMgdG8gc2FtcGxlIHN0YXRlcyBhbmQgZml4ZWQgcGFyYW1ldGVycyBpbiB0aGUgZmlyc3Qgb3JkZXIgZHluYW1pYyBsaW5lYXIgbW9kZWwsIGFsc28ga25vd24gYXMgdGhlIGxvY2FsIGxldmVsIG1vZGVsIHRoYXQgd2UgaGF2ZSBhbHJlYWR5IGRpc2N1c3NlZCBpbiBjbGFzcy4gIE1vcmUgc3BlY2lmaWNhbGx5LCB0aGUgbG9jYWwgbGV2ZWwgbW9kZWwgYXNzdW1lcyB0aGF0IHRoZSB0aW1lIHNlcmllcyBkYXRhLCAkXG1ib3h7ZGF0YX09XHt5XzEsXGxkb3RzLHlfblx9JCwgY2FuIGJlIGRlc2NyaWJlZCBhcyBmb2xsb3dzLCBmb3IgJHQ9MSxcbGRvdHMsbiQKXGJlZ2lue2VxbmFycmF5Kn0KeV90ICY9JiBcYmV0YV90ICsgXG51X3QgXHFxdWFkIFxudV90IFxzaW0gTigwLFYpXFwKXGJldGFfdCAmPSYgXGJldGFfe3QtMX0gKyBcb21lZ2FfdCBccXF1YWQgXG9tZWdhX3QgXHNpbSBOKDAsVyksClxlbmR7ZXFuYXJyYXkqfQp3aXRoICRcYmV0YV8wIFxzaW0gTihtXzAsQ18wKSQgYW5kIHdoZXJlICRXL1YkIGlzIGNvbW1vbmx5IGtub3duIGFzIHRoZSBzaWduYWwgdG8gbm9pc2UgcmF0aW8gKFNOUikuICBIZXJlICRtXzAkIGFuZCAkQ18wJCBhcmUga25vd24gaHlwZXJwYXJhbWV0ZXJzLgoKVGhlIG1vZGVsIGlzIGNvbXBsZXRlZCB3aXRoIHByaW9yIGRpc3RyaWJ1dGlvbnMgZm9yIHRoZSBlcnJvciB2YXJpYW5jZXMsICRWJCBhbmQgJFckLiAgSGVyZSwgd2Ugd2lsbCBhc3N1bWUgdGhhdApcYmVnaW57ZXFuYXJyYXkqfQpWICZcc2ltJiBJRyhhX1xudSxiX1xudSlcXApXICZcc2ltJiBJRyhhX1xvbWVnYSxiX1xvbWVnYSksClxlbmR7ZXFuYXJyYXkqfQokYV9cbnUsYl9cbnUsYV9cb21lZ2EsYl9cb21lZ2EkIGtub3duIGh5cGVycGFyYW1ldGVycy4KCgojIFNpbXVsYXRpbmcgb25lIGRhdGFzZXQKCkZvciBzaW1wbGljYXRpb24sIHdlIGNyZWF0ZWQgdGhlIGRhdGEgc2ltdWxhdGlvbiBmdW5jdGlvbiAqKnNhbXBsZS5kYXRhKiouCgpgYGB7cn0Kc2FtcGxlLmRhdGEgPSBmdW5jdGlvbihuLFcsVixiZXRhMCl7CiAgc2RXICAgICA9IHNxcnQoVykKICBzZFYgICAgID0gc3FydChWKQogIGJldGEgICAgPSByZXAoMCxuKQogIHkgICAgICAgPSByZXAoMCxuKQogIGJldGFbMV0gPSBybm9ybSgxLGJldGEwLHNkVykKICB5WzFdICAgID0gcm5vcm0oMSxiZXRhWzFdLHNkVikKICBmb3IgKHQgaW4gMjpuKXsKICAgIGJldGFbdF0gPSBybm9ybSgxLGJldGFbdC0xXSxzZFcpICAKICAgIHlbdF0gICAgPSBybm9ybSgxLGJldGFbdF0sc2RWKQogIH0KICByZXR1cm4oeSkKfQpgYGAKCldlIHNpbXVsYXRlZCAkbj0xMDAkIG9ic2VydmF0aW9ucywgc3RhcnRpbmcgd2l0aCAkXGJldGFfMD0wJCBhbmQgZml4aW5nICRWPTEuMCQgYW5kICRXPTAuMjUkIHNvIHRoZSBTTlIgaXMgMC4yNS4KCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NX0Kc2V0LnNlZWQoMTczODkpCm4gICAgID0gMTAwClYgICAgID0gMS4wClcgICAgID0gMC4yNQpiZXRhMCA9IDAuMAp5ICAgICA9IHNhbXBsZS5kYXRhKG4sVyxWLGJldGEwKQp0cy5wbG90KHkpCmBgYAoKIyBCbG9jay1tb3ZlCgpUaGUgZm9sbG93aW5nIHJvdXRpbmUgKiptY21jLmpvaW50Kiogc2FtcGxlcyAkKFxiZXRhXzEsXGxkb3RzLFxiZXRhX24pJCwgJFYkIGFuZCAkVyQgaXRlcmF0aXZlbHkgZnJvbSB0aGVpciBmdWxsIGNvbmRpdGlvbmFsIHBvc3RlcmlvciBkaXN0cmlidXRpb25zLCBpZS4KXGJlZ2lue2VxbmFycmF5Kn0KcChcYmV0YV8xLFxsZG90cyxcYmV0YV9ufFYsVyxcbWJveHtkYXRhfSlcXApwKFZ8XGJldGFfMSxcbGRvdHMsXGJldGFfbixXLFxtYm94e2RhdGF9KVxcCnAoV3xcYmV0YV8xLFxsZG90cyxcYmV0YV9uLFYsXG1ib3h7ZGF0YX0pLApcZW5ke2VxbmFycmF5Kn0KYW5kIHJldHVybnMgYW4gJE0gXHRpbWVzIChuKzIpJCBtYXRyaXggd2l0aCAkTSQgZHJhd3Mgb2YgdGhlICQobisyKSQtZGltZW5zaW9uYWwgdmVjdG9yICQoXGJldGFfMSxcbGRvdHMsXGJldGFfbixWLFcpJC4KClRoZSBmdWxsIGNvbmRpdGlvbmFsIGRpc3RyaWJ1dGlvbnMgb2YgJFYkIGFuZCAkVyQgYXJlIGJvdGggSW52ZXJzZS1HYW1tYSBkaXN0cmlidXRpb25zLCB3aGlsZSB0aGUgZnVsbCBjb25kaXRpb25hbCBkaXN0cmlidXRpb24gb2YKJChcYmV0YV8xLFxsZG90cyxcYmV0YV9uKSQgaXMgJG4kLXZhcmlhdGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiBhbmQgaXRzIGpvaW50IHNhbXBsaW5nIGlzIGZhY2lsaXRhdGVkIGJ5IHRoZSBrYWxtYW4gcmVjdXJzaW9ucywgYm90aCBmaWx0ZXJpbmcgYW5kIHNtb290aGluZy4gCgpCZWxvdywgJGFfMT1tXzAkIGFuZCAkUl8xPUNfMCtXJCwgd2hpbGUgJE1fMCQgYW5kICRNJCBhcmUgTUNNQyBkcmF3cywgd2l0aCB0aGUgZmlyc3QgJE1fMCQgZHJhd3MgZGlzY2FyZGVkIChidXJuLWluIG9yIHdhcm0tdXApIGFuZCB0aGUgZm9sbG93aW5nICRNJCBkcmF3cyBzdG9yZXMgZm9yIHBvc3RlcmlvciBpbmZlcmVuY2UuICBJbml0aWFsIHZhbHVlcyBmb3IgJFYkIGFuZCAkVyQgYXJlIHRoZSB0d28gbGFzdCBhcmd1bWVudHMgb2YgdGhlIGZ1bmN0aW9uICoqbWNtYy5qb2ludCoqLiAKCmBgYHtyfQptY21jLmpvaW50ID0gZnVuY3Rpb24oeSxhMSxSMSxhdixidixhdyxidyxNMCxNLFYsVyl7CiAgbiAgICAgID0gbGVuZ3RoKHkpCiAgVi5kcmF3ID0gVgogIFcuZHJhdyA9IFcKICBuaXRlciAgPSBNMCtNCiAgZHJhd3MgID0gbWF0cml4KDAsbml0ZXIsbisyKQogIGZvciAoaXRlciBpbiAxOm5pdGVyKXsKICAgIGJldGEuZHJhdyA9IGZmYnMoeSxyZXAoMSxuKSwwLjAsVi5kcmF3LDEuMCwwLjAsVy5kcmF3LGExLFIxLG5kPTEpCiAgICBwYXIydiAgICAgPSBiditzdW0oKHktYmV0YS5kcmF3KV4yKS8yCiAgICBwYXIydyAgICAgPSBidytzdW0oKGJldGEuZHJhd1syOm5dLWJldGEuZHJhd1sxOihuLTEpXSleMikvMgogICAgVi5kcmF3ICAgID0gMS9yZ2FtbWEoMSxhdituLzIscGFyMnYpCiAgICBXLmRyYXcgICAgPSAxL3JnYW1tYSgxLGF3KyhuLTEpLzIscGFyMncpCiAgICBkcmF3c1tpdGVyLF0gPSBjKGJldGEuZHJhdyxWLmRyYXcsVy5kcmF3KQogIH0KICByZXR1cm4oZHJhd3NbKE0wKzEpOm5pdGVyLF0pCn0KYGBgCgojIyBTYW1wbGluZyBmcm9tICRwKFxiZXRhXzEsXGxkb3RzLFxiZXRhX258VixXLFxtYm94e2RhdGF9KSQKClRoZSBmb2xsb3dpbmcgcm91dGluZSAqKmZmYnMqKiB3YXMgYWN0dWFsbHkgd3JpdHRlbiB0byBiZSBhYmxlIHRvIHNhbXBsZSB0aGUgc3RhdGVzIGpvaW50bHkgdmlhIHRoZSBmb3J3YXJkIGZpbHRlcmluZyBiYWNrd2FyZCBzYW1wbGluZyAoRkZCUykgc2NoZW1lIGZvciBhIG1vcmUgZ2VuZXJhbCBjbGFzcyBvZiBub3JtYWwgZHluYW1pYyBsaW5lYXIgbW9kZWwgKE5ETE0pOgoKXGJlZ2lue2VxbmFycmF5Kn0KeV90ICY9JiBGX3ReVCBcdGhldGFfdCArIFxudV90IFxxcXVhZCBcbnVfdCBcc2ltIE4oMCxWKVxcClx0aGV0YV90ICY9JiBcZ2FtbWEgKyBHIFx0aGV0YV97dC0xfSArIFxvbWVnYV90IFxxcXVhZCBcb21lZ2FfdCBcc2ltIE4oMCxXKSwKXGVuZHtlcW5hcnJheSp9CndoZXJlICRGX3QkIGlzIGEgdmVjdG9yIG9mIG9ic2VydmVkIGNvbXBvbmVudHMsIHdpdGggJFx0aGV0YV90JCBhIHN0YXRlIHZlY3RvciBvZiB0aGUgc2FtZSBkaW1lbnNpb25zLiAgSWYgJEZfdCQgYXJlIHRoZSB1c3VhbCBkZWZpbml0aW9uIG9mIHJlZ3Jlc3NvcnMgaW4gbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWxzLCB0aGVuICRcdGhldGFfdCQgYXJlIHRoZSB0aW1lLXZhcnlpbmcgcmVncmVzc2lvbiBjb2VmZmljaWVudHMuICBGb3Igc2ltcGxlIGxvY2FsIGxldmVsIG1vZGVsLCAKd2UgaGF2ZSB0aGF0ICRGX3Q9MSwgXCBcIFxmb3JhbGwgdCQsICRcZ2FtbWE9MCQsICRHPTEkIGFuZCAkXHRoZXRhX3Q9XGJldGFfdCQuICBBbm90aGVyIHNwZWNpYWwgY2FzZSwgdGhhdCB3ZSBoYXZlIHN0dWRpZWQgaW4gdGhpcyBjbGFzcywgaXMgdGhlIEFSKDEpIHBsdXMgbm9pc2UgbW9kZWw6ClxiZWdpbntlcW5hcnJheSp9CnlfdCAmPSYgXHRoZXRhX3QgKyBcbnVfdCBccXF1YWRccXF1YWQgXG51X3QgXHNpbSBOKDAsVilcXApcdGhldGFfdCAmPSYgXGdhbW1hICsgXHBoaSBcdGhldGFfe3QtMX0gKyBcb21lZ2FfdCBccXF1YWQgXG9tZWdhX3QgXHNpbSBOKDAsVykuClxlbmR7ZXFuYXJyYXkqfQoKV2UgdXNlIHRoZSBmdW5jdGlvbiAqKmZmYnMqKiB0byBkcmF3ICRcYmV0YV8xLFxsZG90cyxcYmV0YV9uJCBpbiB0aGUgTUNNQyBmdW5jdGlvbiAqKm1jbWMuam9pbnQqKi4KCmBgYHtyfQpmZmJzID0gZnVuY3Rpb24oeSxGdCxhbHBoYSxWLEcsZ2FtbWEsVyxhMSxSMSxuZD0xKXsKICBuID0gbGVuZ3RoKHkpCiAgaWYgKGxlbmd0aChGdCk9PTEpe0Z0PXJlcChGdCxuKX0KICBpZiAobGVuZ3RoKGFscGhhKT09MSl7YWxwaGE9cmVwKGFscGhhLG4pfQogIGlmIChsZW5ndGgoVik9PTEpe1Y9cmVwKFYsbil9CiAgYSA9IHJlcCgwLG4pCiAgUiA9IHJlcCgwLG4pCiAgbSA9IHJlcCgwLG4pCiAgQyA9IHJlcCgwLG4pCiAgQiA9IHJlcCgwLG4tMSkKICBIID0gcmVwKDAsbi0xKQogICMgdGltZSB0PTEKICBhWzFdID0gYTEKICBSWzFdID0gUjEKICBmICAgID0gYWxwaGFbMV0rRnRbMV0qYVsxXQogIFEgICAgPSBSWzFdKkZ0WzFdKioyK1ZbMV0KICBBICAgID0gUlsxXSpGdFsxXS9RCiAgbVsxXSA9IGFbMV0rQSooeVsxXS1mKQogIENbMV0gPSBSWzFdLVEqQSoqMgogICMgZm9yd2FyZCBmaWx0ZXJpbmcKICBmb3IgKHQgaW4gMjpuKXsKICAgIGFbdF0gPSBnYW1tYSArIEcqbVt0LTFdCiAgICBSW3RdID0gQ1t0LTFdKkcqKjIgKyBXCiAgICBmICAgID0gYWxwaGFbdF0rRnRbdF0qYVt0XQogICAgUSAgICA9IFJbdF0qRnRbdF0qKjIrVlt0XQogICAgQSAgICA9IFJbdF0qRnRbdF0vUQogICAgbVt0XSA9IGFbdF0rQSooeVt0XS1mKQogICAgQ1t0XSA9IFJbdF0tUSpBKioyCiAgICBCW3QtMV0gPSBDW3QtMV0qRy9SW3RdCiAgICBIW3QtMV0gPSBzcXJ0KENbdC0xXS1SW3RdKkJbdC0xXSoqMikKICB9CiAgIyBiYWNrd2FyZCBzYW1wbGluZwogIHRoZXRhID0gbWF0cml4KDAsbmQsbikKICB0aGV0YVssbl0gPSBybm9ybShuZCxtW25dLHNxcnQoQ1tuXSkpCiAgZm9yICh0IGluIChuLTEpOjEpCiAgICB0aGV0YVssdF0gPSBybm9ybShuZCxtW3RdK0JbdF0qKHRoZXRhWyx0KzFdLWFbdCsxXSksSFt0XSkKICBpZiAobmQ9PTEpewogICAgdGhldGFbMSxdCiAgfQogIGVsc2V7CiAgICB0aGV0YQogIH0KfQpgYGAgICAgICAgICAgICAgICAgICAgICAgICAgIAoKIyMgUG9zdGVyaW9yIGluZmVyZW5jZQoKYGBge3J9CiMgUHJpb3IgZm9yIGJldGEoMCkgfiBOKG0wLEMwKQojIFByaW9yIGZvciBiZXRhKDEpIH4gTihhMSxSMSksIGExPW0wLFIxPUMwK1cKYTEgICAgID0gMApSMSAgICAgPSAxMAphdiAgICAgPSAyLjAxCmJ2ICAgICA9IDEuMDEKYXcgICAgID0gMi4wMQpidyAgICAgPSAxLjAxKlcgICAgCm0wICAgICA9IDAKQzAgICAgID0gMTAtVwoKIyBNQ01DIGluaXR1YWwgdmFsdWVzClYwICAgICA9IDEKVzAgICAgID0gMC41Ck0wICAgICA9IDEwMDAwCk0gICAgICA9IDEwMDAwCgpzZXQuc2VlZCg2NTQzMikKZHJhd3MxID0gbWNtYy5qb2ludCh5LGExLFIxLGF2LGJ2LGF3LGJ3LE0wLE0sVjAsVzApCgojIHN0YXRlcyBNQ01DIGF1dG9jb3JyZWxhdGlvbnMgYW5kIHBvc3RlcmlvciBxdWFudGlsZXMKcTEgPSB0KGFwcGx5KGRyYXdzMVssMTpuXSwyLHF1YW50aWxlLGMoMC4wNSwwLjUsMC45NSkpKQpsYWdtYXggPSAzMAphY2ZzID0gYXJyYXkoMCxjKDIsbixsYWdtYXgpKQpmb3IgKGkgaW4gMTpuKQogIGFjZnNbMSxpLF0gPSBhY2YoZHJhd3MxWyxpXSxwbG90PUZBTFNFLGxhZy5tYXg9bGFnbWF4KSRhY2ZbMjoobGFnbWF4KzEpXQpgYGAKCgojIyMgUG9zdGVyaW9yIGluZmVyZW5jZSBmb3IgJFYkIGFuZCAkVyQKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01fQpwYXIobWZyb3c9YygyLDMpLG1hciA9IGMoMiwgMiwgMSwgMSkpCnRzLnBsb3QoZHJhd3MxWyxuKzFdLHlsYWI9IiIseGxhYj0iSXRlcmF0aW9uIixtYWluPSJWIikKYWNmKGRyYXdzMVssbisxXSxtYWluPSIiKQpoaXN0KGRyYXdzMVssbisxXSxwcm9iPVRSVUUsbWFpbj0iIix4bGFiPSIiKQp0cy5wbG90KGRyYXdzMVssbisyXSx5bGFiPSIiLHhsYWI9Ikl0ZXJhdGlvbiIsbWFpbj0iVyIpCmFjZihkcmF3czFbLG4rMl0sbWFpbj0iIikKaGlzdChkcmF3czFbLG4rMl0scHJvYj1UUlVFLG1haW49IiIseGxhYj0iIikKYGBgCgojIyMgTUNNQyBhdXRvY29ycmVsYXRpb24gZm9yIHN0YXRlcwpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTV9CnBhcihtZnJvdz1jKDEsMSksbWFyPWMoNSwgNCwgNCwgMikpCnRzLnBsb3QodChhY2ZzWzEsLF0pLHhsYWI9IkxhZyIseWxhYj0iQUNGIixtYWluPSIiLHlsaW09cmFuZ2UoYWNmcykpCmBgYAoKIyMjIFBvc3RlcmlvciBxdWFudGlsZXMgZm9yIHN0YXRlcwpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTV9CnBhcihtZnJvdz1jKDEsMSkpCnRzLnBsb3QocTEsY29sPWMoMywyLDMpLHlsaW09cmFuZ2UoeSxxMSkseWxhYj0iU3RhdGVzIikKcG9pbnRzKHkpCnRpdGxlKCI1dGgsIDUwdGggYW5kIDk1dGggcGVyY2VudGlsZXMiKQpgYGAKCgoKCiMgU2luZ2xlLW1vdmUKClRoZSByb3V0aW5lICoqbWNtYy5zaW5nbGUqKiBzYW1wbGVzICRcYmV0YV8xLFxsZG90cyxcYmV0YV9uJCwgJFYkIGFuZCAkVyQgaXRlcmF0aXZlbHkgZnJvbSB0aGVpciB1bml2YXJpYXRlIGZ1bGwgY29uZGl0aW9uYWwgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbnMsIGllLgpcYmVnaW57ZXFuYXJyYXkqfQpwKFZ8XGJldGFfMSxcbGRvdHMsXGJldGFfbixXLFxtYm94e2RhdGF9KVxcCnAoV3xcYmV0YV8xLFxsZG90cyxcYmV0YV9uLFYsXG1ib3h7ZGF0YX0pLApcZW5ke2VxbmFycmF5Kn0KYW5kCiQkCnAoXGJldGFfdHxcYmV0YV97LXR9LFYsVyxcbWJveHtkYXRhfSkgXHFxdWFkIHQ9MSxcbGRvdHMsbiwKJCQKd2hlcmUgJFxiZXRhX3stdH0kIGlzIHRoZSB2ZWN0b3IgJChcYmV0YV8xLFxsZG90cyxcYmV0YV9uKSQgZXhjbHVkaW5nIHRoZSAkXGJldGFfdCQgc3RhdGUuCgoqKm1jbWMuc2luZ2xlKiogcmV0dXJucyBhbiAkTSBcdGltZXMgKG4rMikkIG1hdHJpeCB3aXRoICRNJCBkcmF3cyBvZiB0aGUgJChuKzIpJC1kaW1lbnNpb25hbCB2ZWN0b3IgJChcYmV0YV8xLFxsZG90cyxcYmV0YV9uLFYsVykkLgoKYGBge3J9Cm1jbWMuc2luZ2xlID0gZnVuY3Rpb24oeSxhMSxSMSxhdixidixhdyxidyxNMCxNLGJldGEsVixXKXsKICBuICAgICAgICAgPSBsZW5ndGgoeSkKICBiZXRhLmRyYXcgPSBiZXRhCiAgVi5kcmF3ICAgID0gVgogIFcuZHJhdyAgICA9IFcKICBuaXRlciAgICAgPSBNMCtNCiAgZHJhd3MgICAgID0gbWF0cml4KDAsbml0ZXIsbisyKQogIGZvciAoaXRlciBpbiAxOm5pdGVyKXsKICAgIGJldGEuZHJhdyA9IHNpbmdsZS5zYW1wbGluZyh5LGExLFIxLGF2LGJ2LGF3LGJ3LE0wLE0sYmV0YS5kcmF3LFYuZHJhdyxXLmRyYXcpCiAgICBwYXIydiAgICAgPSBiditzdW0oKHktYmV0YS5kcmF3KV4yKS8yCiAgICBwYXIydyAgICAgPSBidytzdW0oKGJldGEuZHJhd1syOm5dLWJldGEuZHJhd1sxOihuLTEpXSleMikvMgogICAgVi5kcmF3ID0gMS9yZ2FtbWEoMSxhdituLzIscGFyMnYpCiAgICBXLmRyYXcgPSAxL3JnYW1tYSgxLGF3KyhuLTEpLzIscGFyMncpCiAgICBkcmF3c1tpdGVyLF0gPSBjKGJldGEuZHJhdyxWLmRyYXcsVy5kcmF3KQogIH0KICByZXR1cm4oZHJhd3NbKE0wKzEpOm5pdGVyLF0pCn0KYGBgCgojIyBTYW1wbGluZyBmcm9tICRwKFxiZXRhX3R8XGJldGFfey10fSxWLFcsXG1ib3h7ZGF0YX0pJAoKVGhlIHJvdXRpbmUgKipzaW5nbGUuc2FtcGxpbmcqKiBzYW1wbGVzLCBmb3IgJHQ9MSxcbGRvdHMsbiQsIGZyb20gdGhlIHVuaXZhcmlhdGUgZnVsbCBjb25kaXRpb25hbCBkaXN0cmlidXRvbnMKJCQKcChcYmV0YV90fFxiZXRhX3stdH0sVixXLFxtYm94e2RhdGF9KS4KJCQKCmBgYHtyfQpzaW5nbGUuc2FtcGxpbmcgPSBmdW5jdGlvbih5LGExLFIxLGF2LGJ2LGF3LGJ3LE0wLE0sYmV0YSxWLFcpewogIFYxID0gMS8oMS9SMSsxL1crMS9WKQogIGIxID0gVjEqKGExL1IxK2JldGFbMl0vVyt5WzFdL1YpCiAgYmV0YVsxXSA9IHJub3JtKDEsYjEsc3FydChWMSkpCiAgZm9yICh0IGluIDI6KG4tMSkpewogICAgVnQgPSAxLygyL1crMS9WKSAgICAgICAgIAogICAgYnQgPSBWdCooKGJldGFbdC0xXStiZXRhW3QrMV0pL1creVt0XS9WKQogICAgYmV0YVt0XSA9IHJub3JtKDEsYnQsc3FydChWdCkpCiAgfSAgCiAgVm4gPSAxLygxL1crMS9WKQogIGJuID0gVm4qKGJldGFbbi0xXS9XK3lbbl0vVikKICBiZXRhW25dID0gcm5vcm0oMSxibixzcXJ0KFZuKSkKICByZXR1cm4oYmV0YSkKfQpgYGAKCiMjIFBvc3RlcmlvciBpbmZlcmVuY2UKYGBge3J9CnNldC5zZWVkKDY1NDMyKQpiZXRhMHYgPSB5CmRyYXdzMiA9IG1jbWMuc2luZ2xlKHksYTEsUjEsYXYsYnYsYXcsYncsTTAsTSxiZXRhMHYsVjAsVzApCnEyICAgICA9IHQoYXBwbHkoZHJhd3MyWywxOm5dLDIscXVhbnRpbGUsYygwLjA1LDAuNSwwLjk1KSkpCmZvciAoaSBpbiAxOm4pCiAgYWNmc1syLGksXSA9IGFjZihkcmF3czJbLGldLHBsb3Q9RkFMU0UsbGFnLm1heD1sYWdtYXgpJGFjZlsyOihsYWdtYXgrMSldCmBgYAoKIyMjIFBvc3RlcmlvciBpbmZlcmVuY2UgZm9yICRWJCBhbmQgJFckCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTV9CnBhcihtZnJvdz1jKDIsMyksbWFyID0gYygyLCAyLCAxLCAxKSkKdHMucGxvdChkcmF3czJbLG4rMV0seWxhYj0iIix4bGFiPSJJdGVyYXRpb24iLG1haW49IlYiKQphY2YoZHJhd3MyWyxuKzFdLG1haW49IiIpCmhpc3QoZHJhd3MyWyxuKzFdLHByb2I9VFJVRSxtYWluPSIiLHhsYWI9IiIpCnRzLnBsb3QoZHJhd3MyWyxuKzJdLHlsYWI9IiIseGxhYj0iSXRlcmF0aW9uIixtYWluPSJXIikKYWNmKGRyYXdzMlssbisyXSxtYWluPSIiKQpoaXN0KGRyYXdzMlssbisyXSxwcm9iPVRSVUUsbWFpbj0iIix4bGFiPSIiKQpgYGAKCiMjIyBNQ01DIGF1dG9jb3JyZWxhdGlvbiBmb3Igc3RhdGVzCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NX0KcGFyKG1mcm93PWMoMSwxKSxtYXI9Yyg1LCA0LCA0LCAyKSkKdHMucGxvdCh0KGFjZnNbMiwsXSkseGxhYj0iTGFnIix5bGFiPSJBQ0YiLG1haW49IiIseWxpbT1yYW5nZShhY2ZzKSkKYGBgCgojIyMgUG9zdGVyaW9yIHF1YW50aWxlcyBmb3Igc3RhdGVzCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NX0KcGFyKG1mcm93PWMoMSwxKSkKdHMucGxvdChxMixjb2w9YygzLDIsMykseWxpbT1yYW5nZSh5LHEyKSx5bGFiPSJTdGF0ZXMiKQpwb2ludHMoeSkKdGl0bGUoIjV0aCwgNTB0aCBhbmQgOTV0aCBwZXJjZW50aWxlcyIpCmBgYAoKIyBDb21wYXJpbmcgTUNNQyBzY2hlbWVzCgojIyBDb21wYXJpbmcgQUNGcwoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01fQpwYXIobWZyb3c9YygxLDEpLG1hcj1jKDUsIDQsIDQsIDIpKQp0cy5wbG90KHQoYWNmc1syLCxdKSx4bGFiPSJMYWciLHlsYWI9IkFDRiIsbWFpbj0iIix5bGltPXJhbmdlKGFjZnMpKQpmb3IgKGkgaW4gMTpuKQogIGxpbmVzKGFjZnNbMSxpLF0sY29sPTIpCmxlZ2VuZCgidG9wcmlnaHQiLGxlZ2VuZD1jKCJTaW5nbGUtbW92ZSIsIkJsb2NrLW1vdmUiKSxjb2w9MToyLGx3ZD0yLGJ0eT0ibiIpCmBgYAoKIyMgQ29tcGFyaW5nIHN0YXRlIHBvc3RlcmlvciBxdWFudGlsZXMKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NX0KcGFyKG1mcm93PWMoMSwxKSxtYXI9Yyg1LCA0LCA0LCAyKSkKdHMucGxvdChxMikKbGluZXMocTFbLDFdLGNvbD0yKQpsaW5lcyhxMVssMl0sY29sPTIpCmxpbmVzKHExWywzXSxjb2w9MikKbGVnZW5kKCJ0b3AiLGxlZ2VuZD1jKCJTaW5nbGUtbW92ZSIsIkJsb2NrLW1vdmUiKSxjb2w9MToyLGx3ZD0yLGJ0eT0ibiIpCmBgYAoKIyMgQ29tcGFyaW5nIG1hcmdpbmFsIHBvc3RlcmlvciBmb3IgJFYkIGFuZCAkVyQKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NH0KcGFyKG1mcm93PWMoMSwyKSxtYXIgPSBjKDIsIDIsIDEsIDEpKQpwbG90KGRlbnNpdHkoZHJhd3MyWyxuKzFdKSxtYWluPSJWIikKbGluZXMoZGVuc2l0eShkcmF3czFbLG4rMV0pLGNvbD0yKQpwbG90KGRlbnNpdHkoZHJhd3MyWyxuKzJdKSxtYWluPSJXIikKbGluZXMoZGVuc2l0eShkcmF3czFbLG4rMl0pLGNvbD0yKQpsZWdlbmQoInRvcHJpZ2h0IixsZWdlbmQ9YygiU2luZ2xlLW1vdmUiLCJCbG9jay1tb3ZlIiksY29sPTE6Mixsd2Q9MixidHk9Im4iKQpgYGAKCgojIFJlcGxpY2F0aW9ucwoKV2UgcmVwbGljYXRlIHRoZSBhYm92ZSBleGVyY2lzZSBmb3IgMjAgc2V0cyBvZiB0aW1lIHNlcmllcyBkYXRhIGFuZCBydW4gYm90aCBNQ01DIHNjaGVtZXMuICBXZSBhc3N1bWUgdGhhdCBmb3IgJG49MTAwJCBhbmQgJFcgXGluIFx7MC4wMSwwLjI1LDEuMFx9LgoKYGBge3J9CnNldC5zZWVkKDY1NDMyMTIzKQpucmVwICA9IDIwCm4gICAgID0gMTAwClYgICAgID0gMS4wCk0wICAgID0gMTAwMApNICAgICA9IDEwMDAKYmV0YTAgPSAwLjAKVjAgICAgPSAxLjAKVzAgICAgPSAwLjUKYTEgICAgPSAwLjAKUjEgICAgPSAxMC4wCmF2ICAgID0gMi4wMQpidiAgICA9IDEuMDEKYXcgICAgPSAyLjAxCm0wICAgID0gMC4wCndzICAgID0gYygwLjAxLDAuMjUsMS4wKQpud3MgICA9IGxlbmd0aCh3cykKbmVmZnMgPSBhcnJheSgwLGMobnJlcCxud3MsMikpCmZvciAocmVwIGluIDE6bnJlcCl7CiAgZm9yIChpIGluIDE6Myl7CiAgICB5ICAgICAgPSBzYW1wbGUuZGF0YShuLHdzW2ldLFYsYmV0YTApCiAgICBiZXRhMHYgPSB5CiAgICBDMCAgICAgPSAxMC1XCiAgICBidyAgICAgPSAxLjAxKlcgICAgCiAgICBkcmF3czEgPSBtY21jLmpvaW50KHksYTEsUjEsYXYsYnYsYXcsYncsTTAsTSxWMCxXMCkKICAgIGFjZjEgID0gYWNmKGRyYXdzMVssbisxXS9kcmF3czFbLG4rMl0sbGFnLm1heD0zMSxwbG90PUZBTFNFKQogICAgbmVmZjEgPSBNLygxKzIqc3VtKGFjZjEkYWNmWzI6MzFdKSkKICAgIGRyYXdzMiA9IG1jbWMuc2luZ2xlKHksYTEsUjEsYXYsYnYsYXcsYncsTTAsTSxiZXRhMHYsVjAsVzApCiAgICBhY2YyICA9IGFjZihkcmF3czJbLG4rMV0vZHJhd3MyWyxuKzJdLGxhZy5tYXg9MzEscGxvdD1GQUxTRSkKICAgIG5lZmYyID0gTS8oMSsyKnN1bShhY2YyJGFjZlsyOjMxXSkpCiAgICBuZWZmc1tyZXAsaSxdID0gdHJ1bmMoYyhuZWZmMSxuZWZmMikpCiAgfQp9CmBgYAoKIyMgRWZmZWN0aXZlIHNhbXBsZSBzaXplIChFU1MpCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTV9CmNvbWIgPSBjKCIoMTAwLDAuMDEpIiwiKDEwMCwwLjI1KSIsIigxMDAsMSkiKQpwYXIobWZyb3c9YygxLDEpLG1hcj1jKDUsIDQsIDQsIDIpKQpib3hwbG90KGNiaW5kKG5lZmZzWywsMV0sbmVmZnNbLCwyXSksb3V0bGluZT1GQUxTRSxuYW1lcz1jKGNvbWIsY29tYiksCiAgICAgICAgeGxhYj1wYXN0ZSgiKG4sVykgZm9yIFY9IixWLHNlcD0iIikpCmFibGluZSh2PTMuNSkKbGVnZW5kKCJ0b3BsZWZ0IixsZWdlbmQ9YygiQmxvY2stbW92ZSIpLGJ0eT0ibiIpCmxlZ2VuZCgidG9wcmlnaHQiLGxlZ2VuZD1jKCJTaW5nbGUtbW92ZSIpLGJ0eT0ibiIpCnRpdGxlKHBhc3RlKCJNQ01DIHBvc3RlcmlvciBkcmF3cyA9ICIsTSxzZXA9IiIpKQpgYGAKCiMjIFJlbGF0aXZlIEVTUwpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTV9CnBhcihtZnJvdz1jKDEsMSksbWFyPWMoNSwgNCwgNCwgMikpCmJveHBsb3QobmVmZnNbLCwyXS9uZWZmc1ssLDFdLG91dGxpbmU9RkFMU0UsbmFtZXM9Y29tYix5bGFiPSJSZWxhdGl2ZSBFU1MiLAogICAgICAgIHhsYWI9cGFzdGUoIihuLFcpIGZvciBWPSIsVixzZXA9IiIpKQphYmxpbmUoaD0xLGx0eT0yKQp0aXRsZSgiUmF0aW8gc2luZ2xlLW1vdmUgdG8gYmxvY2stbW92ZSIpCmBgYA==