1 Local level model

In the local levem model, the observation and system equations are given by \[\begin{eqnarray*} y_t|x_t,\theta &\sim& N(x_t,\sigma^2)\\ x_t|x_{t-1},\theta &\sim& N(x_{t-1},\tau^2), \end{eqnarray*}\] for \(t=1,\ldots,n\), with \(x_0 \sim N(m_0,C_0)\) and \(\theta=(\sigma^2,\tau^2)\). We assume that \(\theta\) is known in what follows.

1.1 Simulating some data

set.seed(123456)
sig2 = 1.0
tau2 = 1.0
n    = 100
x0   = 0.0
tau  = sqrt(tau2)
sig  = sqrt(sig2)
w    = rnorm(n,0,tau)
v    = rnorm(n,0,sig)
x    = rep(0,n)
y    = rep(0,n)
x[1] = x0 + w[1]
y[1] = x[1] + v[1]
for (t in 2:n){
  x[t] = x[t-1] + w[t]
  y[t] = x[t] + v[t]
}
plot(y,xlab="Time",ylab="",pch=16,ylim=range(x,y))
lines(x,col=2,lwd=2,type="b")
legend("topleft",legend=c("Data","State"),bty="n",pch=16,col=1:2)

1.2 Kalman filter and Kalman smoother

The function DLM returns forward filtering (Kalman filter) and backward smoothing (Kalman smoother) means and variances.

DLM = function(y,sig2,tau2,m0,C0,back){
  n  = length(y)
  m  = m0
  C  = C0
  l  = 0.0
  mf = rep(0,n)
  Cf = rep(0,n)
  for (t in 1:n){
    R = C + tau2
    Q = R + sig2
    l = l + dnorm(y[t],m,sqrt(Q),log=TRUE)
    A = R/Q
    m = (1-A)*m+A*y[t]
    C = A*sig2
    mf[t] = m
    Cf[t] = C
  }
  if(back){
    mb = rep(0,n)
    Cb = rep(0,n)
    mb[n] = mf[n]
    Cb[n] = Cf[n]
    for (t in (n-1):1){
      B = Cf[t]/(Cf[t]+tau2)
      mb[t] = (1-B)*mf[t]+B*mb[t+1]
      Cb[t] = (1-B)*Cf[t]+B*B*Cb[t+1]   
    }
    return(list(l=l,mf=mf,Cf=Cf,mb=mb,Cb=Cb))
  }else{
    return(list(l=l,mf=mf,Cf=Cf))
  }
} 

1.3 Filtered and smoothed densities

m0     = 0
C0     = 4
dlm    = DLM(y,sig2,tau2,m0,C0,TRUE)
mf     = dlm$mf
mb     = dlm$mb
Cf     = dlm$Cf
Cb     = dlm$Cb
quantf = cbind(mf+qnorm(0.025)*sqrt(Cf),mf,mf+qnorm(0.975)*sqrt(Cf))
quantb = cbind(mb+qnorm(0.025)*sqrt(Cb),mb,mb+qnorm(0.975)*sqrt(Cb))

par(mfrow=c(1,1))
plot(quantf[,1],col=2,xlab="Time",ylab="",pch=16,ylim=range(x,quantf,quantb),type="l",lwd=2)
for (i in 2:3) lines(quantf[,i],col=2,lwd=2)
for (i in 1:3) lines(quantb[,i],col=4,lwd=2)
legend("topright",legend=c("Filtered density","Smoothed density"),bty="n",lty=1,lwd=3,col=c(2,4))

2 Multivariate normal approach

It is worth noticing that the normal local level model can be rewritten as a multivariate normal model with multivariate normal prior for the states: \[\begin{eqnarray*} y | x,\theta &\sim& N(x,\sigma_2 I_n)\\ x |x_0,\theta &\sim& N(x_0 1_n, \tau^2\Omega)\\ x_0 &\sim& N(m_0,C_0) \end{eqnarray*}\] where \(x=(x_1,\ldots,x_n)'\), \(y=(y_1,\ldots,y_n)'\), and \[ \Omega = \left( \begin{array}{cccccccc} 1 & 1 & 1 & 1 & \ldots & 1 & 1 & 1 \\ 1 & 2 & 2 & 2 & \ldots & 2 & 2 & 2 \\ 1 & 2 & 3 & 3 & \ldots & 3 & 3 & 3 \\ 1 & 2 & 3 & 4 & \ldots & 4 & 4 & 4 \\ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots & \vdots & \vdots \\ 1 & 2 & 3 & 4 & \ldots & n-2 & n-2 & n-2 \\ 1 & 2 & 3 & 4 & \ldots & n-1 & n-1 & n-1 \\ 1 & 2 & 3 & 4 & \ldots & n-2 & n-1 & n \end{array} \right) \]

2.1 Covariance and precision matrices

The covariance matrix \(\Omega\) has a tridiagonal inverse \(\Omega^{-1}\): \[ \Omega^{-1} = \begin{pmatrix} 2 & -1 & 0 & \dots & 0 \\ -1 & 2 & -1 & \dots & 0 \\ 0 & -1 & 2 & \dots & 0 \\ \vdots & \vdots & \vdots & \ddots & -1 \\ 0 & 0 & 0 & -1 & 1 \end{pmatrix} \] This sparsity is a key computational advantage in Bayesian state-space modeling, as we illustrate in what follows.

2.2 States posterior: \(O(n^3)\) operations

The joint prior of \(x\) given \(\theta\) (integrating out \(x_0\)) is given by \[ x|\theta \sim N(m_0 1_n; C_0 1_n 1_n' + \tau^2 \Omega), \] while its full conditional posterior distribution is \[ x | y, \theta \sim N(m_1,C_1) \] where \[\begin{eqnarray*} C_1^{-1} &=& (C_0 1_n 1_n' + \tau^2 \Omega)^{-1} + \sigma^{-2}I_n\\ C_1^{-1}m_1 &=& (C_0 1_n 1_n' + \tau^2 \Omega)^{-1}m_01_n + \sigma^{-2}y \end{eqnarray*}\]

ones      = matrix(1,n,1)
In        = diag(1,n)
Omega     = matrix(0,n,n)
Omega[n,] = 1:n
for (i in 1:(n-1))
  Omega[i,] = c(1:i,rep(i,n-i))
iC1     = solve(C0*ones%*%t(ones)+Omega/tau2) + In/sig2
C1      = solve(iC1)
m1      = C1%*%((C0*ones%*%t(ones)+Omega/tau2)%*%ones*m0+y/sig2)
quantb1 = cbind(m1+qnorm(0.025)*sqrt(diag(C1)),m1,m1+qnorm(0.975)*sqrt(diag(C1)))

par(mfrow=c(1,1))
plot(quantb[,1],col=2,xlab="Time",ylab="",pch=16,ylim=range(x,quantf,quantb),type="l",lwd=1.5)
for (i in 2:3) lines(quantb[,i],col=2,lwd=1.5)
for (i in 1:3) lines(quantb1[,i],col=4,lwd=1.5)
legend("topright",legend=c("Kalman filter/smoother","Multivariate normal"),bty="n",lty=1,lwd=1.5,col=c(2,4))

2.3 States posterior: \(O(n)\) operations

The tridiagonal structure of \(\Phi=\Omega^{-1}\) allows for drastic reduction of the number of operations when inverting \(C_1\), computing the Cholesky of \(C_1\) or sampling from the multivariate normal \(N(m_1,C_1)\).

A more efficient way to obtain \(C_1^{-1}\) takes advantage of the fact that \(\Omega^{-1}\) is tridiagonal \[ C_1^{-1} = \sigma^{-2}I_n + \tau^{-2}\Omega^{-1} - \frac{e_1 e_1'}{\tau^2\left(\frac{\tau^2}{C_0}+1\right)}, \] where \(e_1=(1,0,0,\ldots,0)'\).

iOmega = matrix(0,n,n)
for (i in 2:(n-1))
  iOmega[i,(i-1):(i+1)] = c(-1,2,-1)
iOmega[1,1:2] = c(2,-1)
iOmega[n,(n-1):n] = c(-1,1)
e1   = matrix(c(1,rep(0,n-1)),n,1)
iC11 = In/sig2+iOmega/tau2-e1%*%t(e1)/(tau2*(tau2/C0+1))

Similarly, \(m_1\) can be computed without the need of inverting \(C_1^{-1}\), which is a dense matrix. The R function solve.tridiagonal solves the system \[ A z = d, \] where \(A\) ia a tridiagonal matrix with the vector \(b\) in the diagonal, the vector \(a\) in the lower diagonal and the vector \(c\) in the upper diagonal. Notice that \(a\) and \(c\) are vectors of length \(n-1\). In our case, \[\begin{eqnarray*} A &=& C_1^{-1},\\ z &=& m_1\\ d &=& (C_1^{-1} - \sigma^{-2}I_n)m_0 1_n + \sigma^{-1}y. \end{eqnarray*}\]

Once we collect the vectors \(a, b, c\) and \(d\), we can easily compute \(m_1\).

# a: (0,vector of lower diagonal)
# b: vector of diagonal
# c: (vector of upper diagonal,0)
solve.tridiagonal = function(a,b,c,d){
  n  = length(d)
  cp = numeric(n)
  dp = numeric(n)
  x  = numeric(n)

  # Forward sweep
  cp[1] = c[1]/b[1]
  dp[1] = d[1]/b[1]
  for (i in 2:(n-1))
    cp[i] = c[i]/(b[i]-a[i]*cp[i-1])
  for (i in 2:n)
    dp[i] = (d[i]-a[i]*dp[i-1])/(b[i]-a[i]*cp[i-1])

  # Back substitution
  x[n] = dp[n]
  for (i in (n-1):1) {
    x[i] = dp[i] - cp[i]*x[i+1]
  }
  return(x)
}

We are now ready to compute \(m_1\):

ind = matrix(1,n,1)
d   = as.vector(m0*(iC1-In/sig2)%*%ind + y/sig2)
a   = rep(0,n)
b   = rep(0,n)
c   = rep(0,n)
for (i in 2:(n-1)){
  a[i] = iC11[i,i-1]
  b[i] = iC11[i,i]
  c[i] = iC11[i,i+1]
}
a[n] = iC11[n,n-1]
b[1] = iC11[1,1]
b[n] = iC11[n,n]
c[1] = iC11[1,2]
m11  = solve.tridiagonal(a, b, c, d)

par(mfrow=c(1,2))
ts.plot(m1,ylab="Posterior mean")
lines(m11,col=2)
ts.plot(round(m1-m11,10),ylab="Difference")

2.4 Computing Cholesky of \(C_1^{-1}\)

We will only need the vectors \(a\) (lower diagonal) and \(b\) (main diagonal) from \(C_1^{-1}\) in order to use the function trichol from the R package mgcv.

library(mgcv)
## Loading required package: nlme
## This is mgcv 1.9-3. For overview type 'help("mgcv-package")'.
L1  = t(chol(C1))
res = trichol(ld = b, sd = a[1:(n-1)])
L11 = matrix(0,n,n)
for (i in 1:n)
  L11[i,i] = res$ld[i]
for (i in 2:n)
  L11[i,i-1] = res$sd[i-1]

2.5 Sampling from the multivariate normal

set.seed(2718282)
M      = 10000
draws  = matrix(0,M,n)
draws1 = matrix(0,M,n)
for (i in 1:M){
  z          = rnorm(n)
  draws[i,]  = m1  + as.vector(L1%*%z)
  draws1[i,] = as.vector(solve(L11,z,system="Lt"))
}
draws1 = sweep(draws1, 2, m11, "+")
qdraw  = t(apply(draws,2,quantile,c(0.025,0.5,0.975)))
qdraw1 = t(apply(draws1,2,quantile,c(0.025,0.5,0.975)))

par(mfrow=c(1,2))
plot(qdraw[,2],ylim=range(qdraw,qdraw1),type="l",xlab="Time",ylab="",col=2)
lines(qdraw[,1],col=2)
lines(qdraw[,3],col=2)
lines(mb+qnorm(0.025)*sqrt(Cb),col=4)
lines(mb,col=4)
lines(mb+qnorm(0.975)*sqrt(Cb),col=4)
title(expression(O(n^3)))

plot(qdraw1[,2],ylim=range(qdraw,qdraw1),type="l",xlab="Time",ylab="",col=2)
lines(qdraw1[,1],col=2)
lines(qdraw1[,3],col=2)
lines(mb+qnorm(0.025)*sqrt(Cb),col=4)
lines(mb,col=4)
lines(mb+qnorm(0.975)*sqrt(Cb),col=4)
title(expression(O(n)))

LS0tCnRpdGxlOiAiRmlyc3Qgb3JkZXIgRExNIgpzdWJ0aXRsZTogIkNvdmFyaWFuY2UsIFByZWNpc2lvbiwgQ2hvbGVza3kgYW5kIEludmVyc2UgTWF0cmljZXMiCmF1dGhvcjogIkhlZGliZXJ0IEZyZWl0YXMgTG9wZXMiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogcGFwZXIKICAgIGhpZ2hsaWdodDogcHlnbWVudHMKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfY29sbGFwc2VkOiB0cnVlCiMgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKLS0tCiAgCiMgTG9jYWwgbGV2ZWwgbW9kZWwKCkluIHRoZSBsb2NhbCBsZXZlbSBtb2RlbCwgdGhlIG9ic2VydmF0aW9uIGFuZCBzeXN0ZW0gZXF1YXRpb25zIGFyZSBnaXZlbiBieQpcYmVnaW57ZXFuYXJyYXkqfQp5X3R8eF90LFx0aGV0YSAmXHNpbSYgTih4X3QsXHNpZ21hXjIpXFwKeF90fHhfe3QtMX0sXHRoZXRhICZcc2ltJiAgTih4X3t0LTF9LFx0YXVeMiksClxlbmR7ZXFuYXJyYXkqfQpmb3IgJHQ9MSxcbGRvdHMsbiQsIHdpdGggJHhfMCBcc2ltIE4obV8wLENfMCkkIGFuZCAkXHRoZXRhPShcc2lnbWFeMixcdGF1XjIpJC4gIFdlIGFzc3VtZSB0aGF0ICRcdGhldGEkIGlzIGtub3duIGluIHdoYXQgZm9sbG93cy4gIAoKIyMgU2ltdWxhdGluZyBzb21lIGRhdGEKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NX0Kc2V0LnNlZWQoMTIzNDU2KQpzaWcyID0gMS4wCnRhdTIgPSAxLjAKbiAgICA9IDEwMAp4MCAgID0gMC4wCnRhdSAgPSBzcXJ0KHRhdTIpCnNpZyAgPSBzcXJ0KHNpZzIpCncgICAgPSBybm9ybShuLDAsdGF1KQp2ICAgID0gcm5vcm0obiwwLHNpZykKeCAgICA9IHJlcCgwLG4pCnkgICAgPSByZXAoMCxuKQp4WzFdID0geDAgKyB3WzFdCnlbMV0gPSB4WzFdICsgdlsxXQpmb3IgKHQgaW4gMjpuKXsKICB4W3RdID0geFt0LTFdICsgd1t0XQogIHlbdF0gPSB4W3RdICsgdlt0XQp9CnBsb3QoeSx4bGFiPSJUaW1lIix5bGFiPSIiLHBjaD0xNix5bGltPXJhbmdlKHgseSkpCmxpbmVzKHgsY29sPTIsbHdkPTIsdHlwZT0iYiIpCmxlZ2VuZCgidG9wbGVmdCIsbGVnZW5kPWMoIkRhdGEiLCJTdGF0ZSIpLGJ0eT0ibiIscGNoPTE2LGNvbD0xOjIpCmBgYAoKIyMgS2FsbWFuIGZpbHRlciBhbmQgS2FsbWFuIHNtb290aGVyCgpUaGUgZnVuY3Rpb24gKipETE0qKiByZXR1cm5zIGZvcndhcmQgZmlsdGVyaW5nIChLYWxtYW4gZmlsdGVyKSBhbmQgYmFja3dhcmQgc21vb3RoaW5nIChLYWxtYW4gc21vb3RoZXIpIG1lYW5zIGFuZCB2YXJpYW5jZXMuCmBgYHtyfQpETE0gPSBmdW5jdGlvbih5LHNpZzIsdGF1MixtMCxDMCxiYWNrKXsKICBuICA9IGxlbmd0aCh5KQogIG0gID0gbTAKICBDICA9IEMwCiAgbCAgPSAwLjAKICBtZiA9IHJlcCgwLG4pCiAgQ2YgPSByZXAoMCxuKQogIGZvciAodCBpbiAxOm4pewogICAgUiA9IEMgKyB0YXUyCiAgICBRID0gUiArIHNpZzIKICAgIGwgPSBsICsgZG5vcm0oeVt0XSxtLHNxcnQoUSksbG9nPVRSVUUpCiAgICBBID0gUi9RCiAgICBtID0gKDEtQSkqbStBKnlbdF0KICAgIEMgPSBBKnNpZzIKICAgIG1mW3RdID0gbQogICAgQ2ZbdF0gPSBDCiAgfQogIGlmKGJhY2spewogICAgbWIgPSByZXAoMCxuKQogICAgQ2IgPSByZXAoMCxuKQogICAgbWJbbl0gPSBtZltuXQogICAgQ2Jbbl0gPSBDZltuXQogICAgZm9yICh0IGluIChuLTEpOjEpewogICAgICBCID0gQ2ZbdF0vKENmW3RdK3RhdTIpCiAgICAgIG1iW3RdID0gKDEtQikqbWZbdF0rQiptYlt0KzFdCiAgICAgIENiW3RdID0gKDEtQikqQ2ZbdF0rQipCKkNiW3QrMV0gICAKICAgIH0KICAgIHJldHVybihsaXN0KGw9bCxtZj1tZixDZj1DZixtYj1tYixDYj1DYikpCiAgfWVsc2V7CiAgICByZXR1cm4obGlzdChsPWwsbWY9bWYsQ2Y9Q2YpKQogIH0KfSAKYGBgCgojIyBGaWx0ZXJlZCBhbmQgc21vb3RoZWQgZGVuc2l0aWVzCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTV9Cm0wICAgICA9IDAKQzAgICAgID0gNApkbG0gICAgPSBETE0oeSxzaWcyLHRhdTIsbTAsQzAsVFJVRSkKbWYgICAgID0gZGxtJG1mCm1iICAgICA9IGRsbSRtYgpDZiAgICAgPSBkbG0kQ2YKQ2IgICAgID0gZGxtJENiCnF1YW50ZiA9IGNiaW5kKG1mK3Fub3JtKDAuMDI1KSpzcXJ0KENmKSxtZixtZitxbm9ybSgwLjk3NSkqc3FydChDZikpCnF1YW50YiA9IGNiaW5kKG1iK3Fub3JtKDAuMDI1KSpzcXJ0KENiKSxtYixtYitxbm9ybSgwLjk3NSkqc3FydChDYikpCgpwYXIobWZyb3c9YygxLDEpKQpwbG90KHF1YW50ZlssMV0sY29sPTIseGxhYj0iVGltZSIseWxhYj0iIixwY2g9MTYseWxpbT1yYW5nZSh4LHF1YW50ZixxdWFudGIpLHR5cGU9ImwiLGx3ZD0yKQpmb3IgKGkgaW4gMjozKSBsaW5lcyhxdWFudGZbLGldLGNvbD0yLGx3ZD0yKQpmb3IgKGkgaW4gMTozKSBsaW5lcyhxdWFudGJbLGldLGNvbD00LGx3ZD0yKQpsZWdlbmQoInRvcHJpZ2h0IixsZWdlbmQ9YygiRmlsdGVyZWQgZGVuc2l0eSIsIlNtb290aGVkIGRlbnNpdHkiKSxidHk9Im4iLGx0eT0xLGx3ZD0zLGNvbD1jKDIsNCkpCmBgYAoKIyBNdWx0aXZhcmlhdGUgbm9ybWFsIGFwcHJvYWNoCgpJdCBpcyB3b3J0aCBub3RpY2luZyB0aGF0IHRoZSBub3JtYWwgbG9jYWwgbGV2ZWwgbW9kZWwgY2FuIGJlIHJld3JpdHRlbiBhcyBhIG11bHRpdmFyaWF0ZSBub3JtYWwgbW9kZWwgd2l0aCBtdWx0aXZhcmlhdGUgbm9ybWFsIHByaW9yIGZvciB0aGUgc3RhdGVzOgpcYmVnaW57ZXFuYXJyYXkqfQp5IHwgeCxcdGhldGEgJlxzaW0mIE4oeCxcc2lnbWFfMiBJX24pXFwKeCB8eF8wLFx0aGV0YSAmXHNpbSYgTih4XzAgMV9uLCBcdGF1XjJcT21lZ2EpXFwKeF8wICZcc2ltJiBOKG1fMCxDXzApClxlbmR7ZXFuYXJyYXkqfQp3aGVyZSAkeD0oeF8xLFxsZG90cyx4X24pJyQsICR5PSh5XzEsXGxkb3RzLHlfbiknJCwgYW5kIAokJApcT21lZ2EgPSBcbGVmdCgKXGJlZ2lue2FycmF5fXtjY2NjY2NjY30KMSAmIDEgJiAxICYgMSAmIFxsZG90cyAmIDEgJiAxICYgMSBcXAoxICYgMiAmIDIgJiAyICYgXGxkb3RzICYgMiAmIDIgJiAyIFxcCjEgJiAyICYgMyAmIDMgJiBcbGRvdHMgJiAzICYgMyAmIDMgXFwKMSAmIDIgJiAzICYgNCAmIFxsZG90cyAmIDQgJiA0ICYgNCBcXApcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgJiBcZGRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgXFwKMSAmIDIgJiAzICYgNCAmIFxsZG90cyAmIG4tMiAmIG4tMiAmIG4tMiBcXAoxICYgMiAmIDMgJiA0ICYgXGxkb3RzICYgbi0xICYgbi0xICYgbi0xIFxcCjEgJiAyICYgMyAmIDQgJiBcbGRvdHMgJiBuLTIgJiBuLTEgJiBuClxlbmR7YXJyYXl9ClxyaWdodCkKJCQKCiMjIENvdmFyaWFuY2UgYW5kIHByZWNpc2lvbiBtYXRyaWNlcwoKVGhlIGNvdmFyaWFuY2UgbWF0cml4ICRcT21lZ2EkIGhhcyBhIHRyaWRpYWdvbmFsIGludmVyc2UgJFxPbWVnYV57LTF9JDoKJCQKXE9tZWdhXnstMX0gPSAKXGJlZ2lue3BtYXRyaXh9CiAyICYgLTEgJiAgMCAmIFxkb3RzICYgMCBcXAotMSAmICAyICYgLTEgJiBcZG90cyAmIDAgXFwKIDAgJiAtMSAmICAyICYgXGRvdHMgJiAwIFxcCiBcdmRvdHMgJiBcdmRvdHMgJiBcdmRvdHMgJiBcZGRvdHMgJiAtMSBcXAogMCAmIDAgJiAwICYgLTEgJiAxClxlbmR7cG1hdHJpeH0KJCQKVGhpcyBzcGFyc2l0eSBpcyBhIGtleSBjb21wdXRhdGlvbmFsIGFkdmFudGFnZSBpbiBCYXllc2lhbiBzdGF0ZS1zcGFjZSBtb2RlbGluZywgYXMgd2UgaWxsdXN0cmF0ZSBpbiB3aGF0IGZvbGxvd3MuCgoKIyMgU3RhdGVzIHBvc3RlcmlvcjogJE8obl4zKSQgb3BlcmF0aW9ucwoKVGhlIGpvaW50IHByaW9yIG9mICR4JCBnaXZlbiAkXHRoZXRhJCAoaW50ZWdyYXRpbmcgb3V0ICR4XzAkKSBpcyBnaXZlbiBieQokJAp4fFx0aGV0YSBcc2ltIE4obV8wIDFfbjsgQ18wIDFfbiAxX24nICsgXHRhdV4yIFxPbWVnYSksCiQkCndoaWxlIGl0cyBmdWxsIGNvbmRpdGlvbmFsIHBvc3RlcmlvciBkaXN0cmlidXRpb24gaXMKJCQKeCB8IHksIFx0aGV0YSBcc2ltIE4obV8xLENfMSkKJCQKd2hlcmUgClxiZWdpbntlcW5hcnJheSp9CkNfMV57LTF9ICY9JiAoQ18wIDFfbiAxX24nICsgXHRhdV4yIFxPbWVnYSleey0xfSArIFxzaWdtYV57LTJ9SV9uXFwKQ18xXnstMX1tXzEgJj0mIChDXzAgMV9uIDFfbicgKyBcdGF1XjIgXE9tZWdhKV57LTF9bV8wMV9uICsgXHNpZ21hXnstMn15ClxlbmR7ZXFuYXJyYXkqfQoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01fQpvbmVzICAgICAgPSBtYXRyaXgoMSxuLDEpCkluICAgICAgICA9IGRpYWcoMSxuKQpPbWVnYSAgICAgPSBtYXRyaXgoMCxuLG4pCk9tZWdhW24sXSA9IDE6bgpmb3IgKGkgaW4gMToobi0xKSkKICBPbWVnYVtpLF0gPSBjKDE6aSxyZXAoaSxuLWkpKQppQzEgICAgID0gc29sdmUoQzAqb25lcyUqJXQob25lcykrT21lZ2EvdGF1MikgKyBJbi9zaWcyCkMxICAgICAgPSBzb2x2ZShpQzEpCm0xICAgICAgPSBDMSUqJSgoQzAqb25lcyUqJXQob25lcykrT21lZ2EvdGF1MiklKiVvbmVzKm0wK3kvc2lnMikKcXVhbnRiMSA9IGNiaW5kKG0xK3Fub3JtKDAuMDI1KSpzcXJ0KGRpYWcoQzEpKSxtMSxtMStxbm9ybSgwLjk3NSkqc3FydChkaWFnKEMxKSkpCgpwYXIobWZyb3c9YygxLDEpKQpwbG90KHF1YW50YlssMV0sY29sPTIseGxhYj0iVGltZSIseWxhYj0iIixwY2g9MTYseWxpbT1yYW5nZSh4LHF1YW50ZixxdWFudGIpLHR5cGU9ImwiLGx3ZD0xLjUpCmZvciAoaSBpbiAyOjMpIGxpbmVzKHF1YW50YlssaV0sY29sPTIsbHdkPTEuNSkKZm9yIChpIGluIDE6MykgbGluZXMocXVhbnRiMVssaV0sY29sPTQsbHdkPTEuNSkKbGVnZW5kKCJ0b3ByaWdodCIsbGVnZW5kPWMoIkthbG1hbiBmaWx0ZXIvc21vb3RoZXIiLCJNdWx0aXZhcmlhdGUgbm9ybWFsIiksYnR5PSJuIixsdHk9MSxsd2Q9MS41LGNvbD1jKDIsNCkpCmBgYAoKIyMgU3RhdGVzIHBvc3RlcmlvcjogJE8obikkIG9wZXJhdGlvbnMKClRoZSB0cmlkaWFnb25hbCBzdHJ1Y3R1cmUgb2YgJFxQaGk9XE9tZWdhXnstMX0kIGFsbG93cyBmb3IgZHJhc3RpYyByZWR1Y3Rpb24gb2YgdGhlIG51bWJlciBvZiBvcGVyYXRpb25zCndoZW4gaW52ZXJ0aW5nICRDXzEkLCBjb21wdXRpbmcgdGhlIENob2xlc2t5IG9mICRDXzEkIG9yIHNhbXBsaW5nIGZyb20gdGhlIG11bHRpdmFyaWF0ZSBub3JtYWwgJE4obV8xLENfMSkkLgoKQSBtb3JlIGVmZmljaWVudCB3YXkgdG8gb2J0YWluICRDXzFeey0xfSQgdGFrZXMgYWR2YW50YWdlIG9mIHRoZSBmYWN0IHRoYXQgJFxPbWVnYV57LTF9JCBpcyB0cmlkaWFnb25hbAokJApDXzFeey0xfSA9IFxzaWdtYV57LTJ9SV9uICsgXHRhdV57LTJ9XE9tZWdhXnstMX0gLSBcZnJhY3tlXzEgZV8xJ317XHRhdV4yXGxlZnQoXGZyYWN7XHRhdV4yfXtDXzB9KzFccmlnaHQpfSwKJCQKd2hlcmUgJGVfMT0oMSwwLDAsXGxkb3RzLDApJyQuCgpgYGB7cn0KaU9tZWdhID0gbWF0cml4KDAsbixuKQpmb3IgKGkgaW4gMjoobi0xKSkKICBpT21lZ2FbaSwoaS0xKTooaSsxKV0gPSBjKC0xLDIsLTEpCmlPbWVnYVsxLDE6Ml0gPSBjKDIsLTEpCmlPbWVnYVtuLChuLTEpOm5dID0gYygtMSwxKQplMSAgID0gbWF0cml4KGMoMSxyZXAoMCxuLTEpKSxuLDEpCmlDMTEgPSBJbi9zaWcyK2lPbWVnYS90YXUyLWUxJSoldChlMSkvKHRhdTIqKHRhdTIvQzArMSkpCmBgYAoKU2ltaWxhcmx5LCAkbV8xJCBjYW4gYmUgY29tcHV0ZWQgd2l0aG91dCB0aGUgbmVlZCBvZiBpbnZlcnRpbmcgJENfMV57LTF9JCwgd2hpY2ggaXMgYSBkZW5zZSBtYXRyaXguClRoZSBSIGZ1bmN0aW9uICoqc29sdmUudHJpZGlhZ29uYWwqKiBzb2x2ZXMgdGhlIHN5c3RlbQokJApBIHogPSBkLAokJAp3aGVyZSAkQSQgaWEgYSB0cmlkaWFnb25hbCBtYXRyaXggd2l0aCB0aGUgdmVjdG9yICRiJCBpbiB0aGUgZGlhZ29uYWwsIHRoZSB2ZWN0b3IgJGEkIGluIHRoZSBsb3dlciBkaWFnb25hbCBhbmQgdGhlIHZlY3RvciAkYyQgaW4gdGhlIHVwcGVyIGRpYWdvbmFsLiAgTm90aWNlIHRoYXQgJGEkIGFuZCAkYyQgYXJlIHZlY3RvcnMgb2YgbGVuZ3RoICRuLTEkLiAgSW4gb3VyIGNhc2UsIApcYmVnaW57ZXFuYXJyYXkqfQpBICY9JiBDXzFeey0xfSxcXAp6ICY9JiBtXzFcXApkICY9JiAoQ18xXnstMX0gLSBcc2lnbWFeey0yfUlfbiltXzAgMV9uICsgXHNpZ21hXnstMX15LgpcZW5ke2VxbmFycmF5Kn0KCk9uY2Ugd2UgY29sbGVjdCB0aGUgdmVjdG9ycyAkYSwgYiwgYyQgYW5kICRkJCwgd2UgY2FuIGVhc2lseSBjb21wdXRlICRtXzEkLgoKYGBge3J9CiMgYTogKDAsdmVjdG9yIG9mIGxvd2VyIGRpYWdvbmFsKQojIGI6IHZlY3RvciBvZiBkaWFnb25hbAojIGM6ICh2ZWN0b3Igb2YgdXBwZXIgZGlhZ29uYWwsMCkKc29sdmUudHJpZGlhZ29uYWwgPSBmdW5jdGlvbihhLGIsYyxkKXsKICBuICA9IGxlbmd0aChkKQogIGNwID0gbnVtZXJpYyhuKQogIGRwID0gbnVtZXJpYyhuKQogIHggID0gbnVtZXJpYyhuKQoKICAjIEZvcndhcmQgc3dlZXAKICBjcFsxXSA9IGNbMV0vYlsxXQogIGRwWzFdID0gZFsxXS9iWzFdCiAgZm9yIChpIGluIDI6KG4tMSkpCiAgICBjcFtpXSA9IGNbaV0vKGJbaV0tYVtpXSpjcFtpLTFdKQogIGZvciAoaSBpbiAyOm4pCiAgICBkcFtpXSA9IChkW2ldLWFbaV0qZHBbaS0xXSkvKGJbaV0tYVtpXSpjcFtpLTFdKQoKICAjIEJhY2sgc3Vic3RpdHV0aW9uCiAgeFtuXSA9IGRwW25dCiAgZm9yIChpIGluIChuLTEpOjEpIHsKICAgIHhbaV0gPSBkcFtpXSAtIGNwW2ldKnhbaSsxXQogIH0KICByZXR1cm4oeCkKfQpgYGAKCldlIGFyZSBub3cgcmVhZHkgdG8gY29tcHV0ZSAkbV8xJDoKICAKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NH0KaW5kID0gbWF0cml4KDEsbiwxKQpkICAgPSBhcy52ZWN0b3IobTAqKGlDMS1Jbi9zaWcyKSUqJWluZCArIHkvc2lnMikKYSAgID0gcmVwKDAsbikKYiAgID0gcmVwKDAsbikKYyAgID0gcmVwKDAsbikKZm9yIChpIGluIDI6KG4tMSkpewogIGFbaV0gPSBpQzExW2ksaS0xXQogIGJbaV0gPSBpQzExW2ksaV0KICBjW2ldID0gaUMxMVtpLGkrMV0KfQphW25dID0gaUMxMVtuLG4tMV0KYlsxXSA9IGlDMTFbMSwxXQpiW25dID0gaUMxMVtuLG5dCmNbMV0gPSBpQzExWzEsMl0KbTExICA9IHNvbHZlLnRyaWRpYWdvbmFsKGEsIGIsIGMsIGQpCgpwYXIobWZyb3c9YygxLDIpKQp0cy5wbG90KG0xLHlsYWI9IlBvc3RlcmlvciBtZWFuIikKbGluZXMobTExLGNvbD0yKQp0cy5wbG90KHJvdW5kKG0xLW0xMSwxMCkseWxhYj0iRGlmZmVyZW5jZSIpCmBgYAoKIyMgQ29tcHV0aW5nIENob2xlc2t5IG9mICRDXzFeey0xfSQKCldlIHdpbGwgb25seSBuZWVkIHRoZSB2ZWN0b3JzICRhJCAobG93ZXIgZGlhZ29uYWwpIGFuZCAkYiQgKG1haW4gZGlhZ29uYWwpIGZyb20gJENfMV57LTF9JCBpbiBvcmRlciB0byB1c2UgdGhlIGZ1bmN0aW9uICoqdHJpY2hvbCoqIGZyb20gdGhlIFIgcGFja2FnZSAqKm1nY3YqKi4KCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTR9CmxpYnJhcnkobWdjdikKTDEgID0gdChjaG9sKEMxKSkKcmVzID0gdHJpY2hvbChsZCA9IGIsIHNkID0gYVsxOihuLTEpXSkKTDExID0gbWF0cml4KDAsbixuKQpmb3IgKGkgaW4gMTpuKQogIEwxMVtpLGldID0gcmVzJGxkW2ldCmZvciAoaSBpbiAyOm4pCiAgTDExW2ksaS0xXSA9IHJlcyRzZFtpLTFdCmBgYAoKIyMgU2FtcGxpbmcgZnJvbSB0aGUgbXVsdGl2YXJpYXRlIG5vcm1hbAoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NH0Kc2V0LnNlZWQoMjcxODI4MikKTSAgICAgID0gMTAwMDAKZHJhd3MgID0gbWF0cml4KDAsTSxuKQpkcmF3czEgPSBtYXRyaXgoMCxNLG4pCmZvciAoaSBpbiAxOk0pewogIHogICAgICAgICAgPSBybm9ybShuKQogIGRyYXdzW2ksXSAgPSBtMSAgKyBhcy52ZWN0b3IoTDElKiV6KQogIGRyYXdzMVtpLF0gPSBhcy52ZWN0b3Ioc29sdmUoTDExLHosc3lzdGVtPSJMdCIpKQp9CmRyYXdzMSA9IHN3ZWVwKGRyYXdzMSwgMiwgbTExLCAiKyIpCnFkcmF3ICA9IHQoYXBwbHkoZHJhd3MsMixxdWFudGlsZSxjKDAuMDI1LDAuNSwwLjk3NSkpKQpxZHJhdzEgPSB0KGFwcGx5KGRyYXdzMSwyLHF1YW50aWxlLGMoMC4wMjUsMC41LDAuOTc1KSkpCgpwYXIobWZyb3c9YygxLDIpKQpwbG90KHFkcmF3WywyXSx5bGltPXJhbmdlKHFkcmF3LHFkcmF3MSksdHlwZT0ibCIseGxhYj0iVGltZSIseWxhYj0iIixjb2w9MikKbGluZXMocWRyYXdbLDFdLGNvbD0yKQpsaW5lcyhxZHJhd1ssM10sY29sPTIpCmxpbmVzKG1iK3Fub3JtKDAuMDI1KSpzcXJ0KENiKSxjb2w9NCkKbGluZXMobWIsY29sPTQpCmxpbmVzKG1iK3Fub3JtKDAuOTc1KSpzcXJ0KENiKSxjb2w9NCkKdGl0bGUoZXhwcmVzc2lvbihPKG5eMykpKQoKcGxvdChxZHJhdzFbLDJdLHlsaW09cmFuZ2UocWRyYXcscWRyYXcxKSx0eXBlPSJsIix4bGFiPSJUaW1lIix5bGFiPSIiLGNvbD0yKQpsaW5lcyhxZHJhdzFbLDFdLGNvbD0yKQpsaW5lcyhxZHJhdzFbLDNdLGNvbD0yKQpsaW5lcyhtYitxbm9ybSgwLjAyNSkqc3FydChDYiksY29sPTQpCmxpbmVzKG1iLGNvbD00KQpsaW5lcyhtYitxbm9ybSgwLjk3NSkqc3FydChDYiksY29sPTQpCnRpdGxlKGV4cHJlc3Npb24oTyhuKSkpCmBgYAo=