Gaussian model with variance switching
We will assume that observations \(\{y_1,\ldots,y_n\}\) are conditionally independent given a hidden binary state variable \(\{s_1,\ldots,s_n\}\) as follows: \[
y_t|\sigma_1^2,\sigma_2^2,s_t \sim N(0,\sigma_t^2)
\] where \[
\sigma_t^2 = \sigma_1^2(1-s_t)+\sigma_2^2s_t,
\] more explicitly, \[
\sigma_t^2= \left\{
\begin{array}{ll}
\sigma_1^2 & s_t=0\\
\sigma_2^2 & s_t=1
\end{array}
\right.
\] Finally, the binary latent state \(s_t\) depends on the previous latent states \(s_1,\ldots,s_{t-1}\) only via the most recent one, i.e. \(s_{t-1}\), according to a first order Markov chain: \[
s_t|s_{t-1},\xi,\pi \sim \left\{
\begin{array}{ll}
Ber(1-\xi) & s_{t-1}=0\\
Ber(\pi) & s_{t-1}=1
\end{array}
\right..
\] For now, we will assume that \(\sigma_1^2\), \(\sigma_2^2\), \(\xi\) and \(\pi\) are all known.
Simulating the dataset
Let us simulated \(n=800\) observations with parameters \((\sigma_1^2,\sigma_2^2)=(1,4)\), \((\pi,\xi)=(0.99,0.99)\) and \(s_1 \sim Ber(0.5)\).
set.seed(3452345)
n = 800
sig12 = 1
sig22 = 4
pi = 0.99
xi = 0.99
S = rep(0,n)
S[1] = rbinom(1,1,0.5)
for (t in 2:n)
S[t] = rbinom(1,1,pi*S[t-1]+(1-xi)*(1-S[t-1]))
y = rnorm(n,0,sqrt(sig12*(1-S)+sig22*S))
xi.true = xi
pi.true = pi
sig12.true = sig12
sig22.true = sig22
Let us take a look at the time series of \(s_t\) and \(y_t\).
par(mfrow=c(2,1))
plot(S,xlab="Time",ylab="S(t)",pch=16)
plot(y,xlab="Time",ylab="y(t)",pch=16)
Forward and backward probabilities
A common inferential goal, it is to use the data \(y_1,\ldots,y_n\) to compute the following (online or filtered) marginal probabilities: \[
\pi_t = Pr(s_t=1|y_{1:t}) \ \ \ \mbox{and} \ \ \ {\tilde \pi}_t = Pr(s_t=1|y_{1:n}).
\] assuming, for instance, that \(\pi_0=0.5\).
Another important goal is jointly sampling the latent/hidden states \(\{s_1,\ldots,s_n\}\) from \(p(s_1,\ldots,s_n|y_{1:n})\) recursively and backwards (see the backward recursions below).
Forward probabilities
We want to be able to start at time \(t-1\) with \(\pi_{t-1}=Pr(s_{t-1}=1|y_{1:(t-1)})\) and finish at time \(t\) with \(\pi_t=Pr(s_t=1|y_{1:t})\):
\[\begin{eqnarray*}
Pr(s_t=1|y_{1:t}) &\propto& Pr(s_t=1|y_{1:(t-1)}) p_N(y_t;0,\sigma_1^2),\\
Pr(s_t=0|y_{1:t}) &\propto& Pr(s_t=0|y_{1:(t-1)}) p_N(y_t;0,\sigma_2^2)
\end{eqnarray*}\] with \[\begin{eqnarray*}
Pr(s_t=1|y_{1:(t-1)}) &=& Pr(s_t=1|s_{t-1}=0,y_{1:(t-1)})Pr(s_{t-1}=0|y_{1:(t-1)})\\
&+& Pr(s_t=1|s_{t-1}=1,y_{1:(t-1)})Pr(s_{t-1}=1|y_{1:(t-1)})\\
&=& (1-\xi)(1-\pi_{t-1})+\pi\pi_{t-1}\\
&=& \alpha_t,\\
Pr(s_t=0|y_{1:(t-1)}) &=& Pr(s_t=0|s_{t-1}=0,y_{1:(t-1)})Pr(s_{t-1}=0|y_{1:(t-1)})\\
&+& Pr(s_t=0|s_{t-1}=1,y_{1:(t-1)})Pr(s_{t-1}=1|y_{1:(t-1)})\\
&=& \xi(1-\pi_{t-1})+(1-\pi)\pi_{t-1}\\
&=& 1-\alpha_t.
\end{eqnarray*}\] Therefore, \[\begin{eqnarray*}
Pr(s_t=1|y_{1:t}) &\propto& \alpha_t p_N(y_t;0,\sigma_1^2),\\
Pr(s_t=0|y_{1:t}) &\propto& (1-\alpha_t) p_N(y_t;0,\sigma_2^2)
\end{eqnarray*}\] and \[
\pi_t=Pr(s_t=1|y_{1:t})=\frac{\alpha_tp_N(y_t;0,\sigma_1^2)}{\alpha_tp_N(y_t;0,\sigma_1^2)+(1-\alpha_t)p_N(y_t;0,\sigma_2^2)}.
\]
# Forward probabilities
pi0 = 0.5
pit = rep(0,n)
# t=1
A = (1-xi)*(1-pi0)+pi*pi0
term1 = dnorm(y[1],0,sqrt(sig22))*A
term2 = dnorm(y[1],0,sqrt(sig12))*(1-A)
pit[1] = term1/(term1+term2)
# t=2,...,n
for (t in 2:n){
A = (1-xi)*(1-pit[t-1])+pi*pit[t-1]
term1 = dnorm(y[t],0,sqrt(sig22))*A
term2 = dnorm(y[t],0,sqrt(sig12))*(1-A)
pit[t] = term1/(term1+term2)
}
Let us take a look at these forward probabilities.
par(mfrow=c(1,1))
plot(S,xlab="Time",ylab="S(t)",pch=16)
points(pit,col=2)
Backward probabilities
Similarly, for \(t=n,\ldots,2\), \[\begin{eqnarray*}
{\tilde \pi}_{t-1} =
Pr(s_{t-1}=1|y_{1:n}) &=& Pr(s_{t-1}=1|s_t=0,y_{1:n})Pr(s_t=0|y_{1:n})
+ Pr(s_{t-1}=1|s_t=1,y_{1:n})Pr(s_t=1|y_{1:n})\\
&=& Pr(s_{t-1}=1|s_t=0,y_{1:t})(1-{\tilde \pi}_t)
+ Pr(s_{t-1}=1|s_t=1,y_{1:t}){\tilde \pi}_t.
\end{eqnarray*}\] Therefore, we need to compute \[
Pr(s_{t-1}=1|s_t=0,y_{1:t}) \ \ \ \mbox{and}\ \ \ Pr(s_{t-1}=1|s_t=1,y_{1:t}),
\] which are obtained via \[\begin{eqnarray*}
Pr(s_{t-1}=1|s_t=0,y_{1:t}) &\propto& Pr(s_t=0|s_{t-1}=1,y_{1:t})Pr(s_{t-1}=1|y_{1:(t-1)})=(1-\pi) \pi_{t-1},\\
Pr(s_{t-1}=0|s_t=0,y_{1:t}) &\propto&
Pr(s_t=0|s_{t-1}=0,y_{1:t})Pr(s_{t-1}=0|y_{1:(t-1)})=\xi (1-\pi_{t-1}),\\
\end{eqnarray*}\] and \[\begin{eqnarray*}
Pr(s_{t-1}=1|s_t=1,y_{1:t}) &\propto&
Pr(s_t=1|s_{t-1}=1,y_{1:t})Pr(s_{t-1}=1|y_{1:(t-1)})=\pi\pi_{t-1}\\
Pr(s_{t-1}=0|s_t=1,y_{1:t}) &\propto&
Pr(s_t=1|s_{t-1}=0,y_{1:t})Pr(s_{t-1}=0|y_{1:(t-1)})=(1-xi)(1-\pi_{t-1}).
\end{eqnarray*}\]
Therefore, \[\begin{eqnarray*}
\beta_t &=& Pr(s_{t-1}=1|s_t=0,y_{1:t})=\frac{(1-\pi) \pi_{t-1}}{(1-\pi) \pi_{t-1}+\xi (1-\pi_{t-1})}\\
\alpha_t &=& Pr(s_{t-1}=1|s_t=1,y_{1:t})= \frac{\pi\pi_{t-1}}{\pi\pi_{t-1}+(1-\xi)(1-\pi_{t-1})}.
\end{eqnarray*}\] The backward recursions are \[
s_{t-1}|s_t=0,y_{1:t} \sim Ber(\beta_t) \ \ \ \mbox{and} \ \ \
s_{t-1}|s_t=1,y_{1:t} \sim Ber(\alpha_t),
\], which are very important when jointly sampling the latent/hidden states \(s_1,\ldots,s_n\) from \(p(s_1,\ldots,s_n|y_{1:n})\) recursively and backwards: \[
p(s_1,\ldots,s_n|y_{1:n}) = \prod_{t=2}^n p(s_{t-1}|s_t,y_{1:t})p(s_n|y_{1:n}).
\] Finally, the marginal backward probabilities are \[
{\tilde \pi}_{t-1}=Pr(s_{t-1}=1|y_{1:n})=\beta_t (1-{\tilde \pi}_t) + \alpha_t{\tilde \pi}_t.
\]
# Backward probabilities
pitil = rep(0,n)
pitil[n] = pit[n]
for (t in n:2){
alpha = pit[t-1]*pi/(pit[t-1]*pi+(1-pit[t-1])*(1-xi))
beta = pit[t-1]*(1-pi)/(pit[t-1]*(1-pi)+(1-pit[t-1])*xi)
pitil[t-1] = alpha*pitil[t]+beta*(1-pitil[t])
}
# storing forward and backward probabilities for later use
pis.ff = pit
pis.bs = pitil
par(mfrow=c(2,1))
plot(pit,xlab="Time",ylab="Pr(S(t)=1|D(t))",pch=16,main="Forward probabilities")
lines(S,col=2,lwd=2)
plot(pitil,xlab="Time",ylab="Pr(S(t)=1|D(n))",pch=16,main="Backward probabilities")
lines(S,col=2,lwd=2)
par(mfrow=c(1,1))
plot(S,xlab="Time",ylab="Probabilities",type="l",main="FFBS for HMM",lwd=2)
lines(pit,col=2,lwd=2)
lines(pitil,col=3,lwd=3)
legend("topright",legend=c("S(t)","Pr(S(t)=1|D(t))","Pr(S(t)=1|D(n))"),col=1:3,lwd=2)
abline(h=0.5,lty=2)
Full-blown MCMC
Let us now include the parameters \(\theta=(\pi,\xi,\sigma_1^2,\sigma_2^2)\) into the analysis. We assume conditionally conjugate and independent priors for the components of \(\theta\), i.e. \[
p(\theta) = p(\pi)p(\xi)p(\sigma_1^2)p(\sigma_2^2),
\] with \(\pi\) and \(\xi\) following \(U(0,1)\) and \(\sigma_1^2\) and \(\sigma_2^2\) following \(IG(3/2,3/2)\), so \(E(\sigma_1^2)=E(\sigma_2^2)=3\) and 95% probability interval approximately equal to \((0.4,8.5)\).
Full conditionals
Full posterior inference is facilitated by a customized Gibbs sampler that cycles through the full conditionals: \[\begin{eqnarray*}
p(s_{1:n}|y_{1:n},\theta)\\
p(\pi|y_{1:n},s_{1:n},\xi,\sigma_1^2,\sigma_2^2)\\
p(\xi|y_{1:n},s_{1:n},\pi,\sigma_1^2,\sigma_2^2)\\
p(\sigma_1^2|y_{1:n},s_{1:n},\pi,\xi,\sigma_2^2)\\
p(\sigma_2^2|y_{1:n},s_{1:n},\pi,\xi,\sigma_1^2),
\end{eqnarray*}\] with the first one, \(p(s_{1:n}|y_{1:n},\theta)\) already derived via the forward filtering, backward sampling scheme introduced above. The other three full conditionals are easily derived and are gives below as follows.
Given \(s_1,\ldots,s_n\), lets compute the observed jumps between regimes \(0\) and \(1\): \[\begin{eqnarray*}
n_1 &=& \sum_{i=1}^n \delta_1(s_{n-1})\delta_1(s_n)\\
n_2 &=& \sum_{i=1}^n \delta_1(s_{n-1})\delta_0(s_n)\\
n_3 &=& \sum_{i=1}^n \delta_0(s_{n-1})\delta_0(s_n)\\
n_4 &=& \sum_{i=1}^n \delta_0(s_{n-1})\delta_1(s_n).
\end{eqnarray*}\]
Similarly, let \({\tilde y}_0^2=\sum_{i:s_i=0} y_i^2\) and \({\tilde y}_1^2=\sum_{i:s_i=1} y_i^2\).
Therefore, \[\begin{eqnarray*}
(\pi|s_{1:n}) &\sim& \mbox{Beta}(n_1+1,n_2+1)\\
(\xi|s_{1:n}) &\sim& \mbox{Beta}(n_3+1,n_4+1)\\
(\sigma_1^2|y_{1:n},s_{1:n}) &\sim& IG((n_2+n_3+3)/2,(3+{\tilde y}_0^2)/2)\\
(\sigma_2^2|y_{1:n},s_{1:n}) &\sim& IG((n_1+n_4+3)/2,(3+{\tilde y}_1^2)/2).
\end{eqnarray*}\]
# Initial values
xi = 0.9
pi = 0.9
sig12 = 1
sig22 = 10
# MCMC setup
set.seed(321456)
pi0 = 0.5
pit = rep(0,n)
burnin = 1000
M = 1000
thin = 1
niter = burnin+M*thin
draws.par = matrix(0,niter,4)
draws.sta = matrix(0,niter,n)
s = rep(0,n)
# MCMC in action
for (iter in 1:niter){
# Drawing the latent states s(1),...,s(n)
A = (1-xi)*(1-pi0)+pi*pi0
term1 = dnorm(y[1],0,sqrt(sig22))*A
term2 = dnorm(y[1],0,sqrt(sig12))*(1-A)
pit[1] = term1/(term1+term2)
for (t in 2:n){
A = (1-xi)*(1-pit[t-1])+pi*pit[t-1]
term1 = dnorm(y[t],0,sqrt(sig22))*A
term2 = dnorm(y[t],0,sqrt(sig12))*(1-A)
pit[t] = term1/(term1+term2)
}
s[n] = rbinom(1,1,pit[n])
for (t in n:2){
alpha = pit[t-1]*pi/(pit[t-1]*pi+(1-pit[t-1])*(1-xi))
beta = pit[t-1]*(1-pi)/(pit[t-1]*(1-pi)+(1-pit[t-1])*xi)
s[t-1] = s[t]*rbinom(1,1,alpha)+(1-s[t])*rbinom(1,1,beta)
}
# Drawing the fixed parameters (pi,xi,sigma12,sigma22)
n1 = sum((s[1:(n-1)]==1)&(s[2:n]==1))
n2 = sum((s[1:(n-1)]==1)&(s[2:n]==0))
n3 = sum((s[1:(n-1)]==0)&(s[2:n]==0))
n4 = sum((s[1:(n-1)]==0)&(s[2:n]==1))
pi = rbeta(1,n1+1,n2+1)
xi = rbeta(1,n3+1,n4+1)
sig12 = 1/rgamma(1,(n2+n3+3)/2,(3+sum(y[s==0]^2))/2)
sig22 = 1/rgamma(1,(n1+n4+3)/2,(3+sum(y[s==1]^2))/2)
# Storing MCMC draws
draws.par[iter,] = c(sig12,sig22,pi,xi)
draws.sta[iter,] = s
}
pis.full = apply(draws.sta[(burnin+1):niter,],2,mean)
Posterior trace plots and marginal posterior histograms
names = c("sigma12","sigma22","pi","xi")
true = c(sig12.true,sig22.true,pi.true,xi.true)
par(mfrow=c(2,4))
for (i in 1:4){
ts.plot(draws.par[,i],xlab="Iterations",ylab="",main=names[i])
abline(h=true[i],col=2,lwd=2)
}
for (i in 1:4){
hist(draws.par[(burnin+1):niter,i],prob=TRUE,xlab="",main="")
abline(v=true[i],col=2,lwd=2)
}
Comparing probabilities
Let us compare \(Pr(s_t=1|y_{1:t},\theta)\), \(Pr(s_t=1|y_{1:n},\theta)\) and \(Pr(s_t=1|y_{1:n})\).
par(mfrow=c(1,1))
plot(S,xlab="Time",ylab="Probabilities",type="l",main="FFBS for HMM")
lines(pis.ff,col=2)
lines(pis.bs,col=3)
lines(pis.full,col=4)
legend("topright",legend=c("S(t)","Pr(S(t)=1|D(t),theta)","Pr(S(t)=1|D(n),theta)","Pr(S(t)=1|D(n))"),col=1:4,lwd=2)
abline(h=0.5,lty=2)
LS0tCnRpdGxlOiAiSGlkZGVuIE1hcmtvdiBNb2RlbDogdmFyaWFuY2Utc3dpdGNoaW5nIgphdXRob3I6ICJIZWRpYmVydCBGcmVpdGFzIExvcGVzIgpkYXRlOiAiNC8xMS8yMDIyIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCiMgR2F1c3NpYW4gbW9kZWwgd2l0aCB2YXJpYW5jZSBzd2l0Y2hpbmcKV2Ugd2lsbCBhc3N1bWUgdGhhdCBvYnNlcnZhdGlvbnMgJFx7eV8xLFxsZG90cyx5X25cfSQgYXJlIGNvbmRpdGlvbmFsbHkgaW5kZXBlbmRlbnQgZ2l2ZW4gYSBoaWRkZW4gYmluYXJ5IHN0YXRlIHZhcmlhYmxlICRce3NfMSxcbGRvdHMsc19uXH0kIGFzIGZvbGxvd3M6CiQkCnlfdHxcc2lnbWFfMV4yLFxzaWdtYV8yXjIsc190IFxzaW0gTigwLFxzaWdtYV90XjIpCiQkCndoZXJlIAokJApcc2lnbWFfdF4yID0gXHNpZ21hXzFeMigxLXNfdCkrXHNpZ21hXzJeMnNfdCwKJCQKbW9yZSBleHBsaWNpdGx5LAokJApcc2lnbWFfdF4yPSBcbGVmdFx7ClxiZWdpbnthcnJheX17bGx9ClxzaWdtYV8xXjIgJiBzX3Q9MFxcClxzaWdtYV8yXjIgJiBzX3Q9MQpcZW5ke2FycmF5fQpccmlnaHQuCiQkCkZpbmFsbHksIHRoZSBiaW5hcnkgbGF0ZW50IHN0YXRlICRzX3QkIGRlcGVuZHMgb24gdGhlIHByZXZpb3VzIGxhdGVudCBzdGF0ZXMgJHNfMSxcbGRvdHMsc197dC0xfSQgb25seSB2aWEgdGhlIG1vc3QgcmVjZW50IG9uZSwgaS5lLiAkc197dC0xfSQsIGFjY29yZGluZyB0byBhIGZpcnN0IG9yZGVyIE1hcmtvdiBjaGFpbjoKJCQKc190fHNfe3QtMX0sXHhpLFxwaSBcc2ltIFxsZWZ0XHsKXGJlZ2lue2FycmF5fXtsbH0KQmVyKDEtXHhpKSAmIHNfe3QtMX09MFxcCkJlcihccGkpICYgc197dC0xfT0xClxlbmR7YXJyYXl9ClxyaWdodC4uCiQkCkZvciBub3csIHdlIHdpbGwgYXNzdW1lIHRoYXQgJFxzaWdtYV8xXjIkLCAkXHNpZ21hXzJeMiQsICRceGkkIGFuZCAkXHBpJCBhcmUgYWxsIGtub3duLgoKIyAgU2ltdWxhdGluZyB0aGUgZGF0YXNldApMZXQgdXMgc2ltdWxhdGVkICRuPTgwMCQgb2JzZXJ2YXRpb25zIHdpdGggcGFyYW1ldGVycyAkKFxzaWdtYV8xXjIsXHNpZ21hXzJeMik9KDEsNCkkLCAkKFxwaSxceGkpPSgwLjk5LDAuOTkpJCBhbmQgJHNfMSBcc2ltIEJlcigwLjUpJC4KCmBgYHtyfQpzZXQuc2VlZCgzNDUyMzQ1KQpuICAgICA9IDgwMApzaWcxMiA9IDEKc2lnMjIgPSA0CnBpICAgID0gMC45OQp4aSAgICA9IDAuOTkKUyAgICAgPSByZXAoMCxuKQpTWzFdICA9IHJiaW5vbSgxLDEsMC41KQpmb3IgKHQgaW4gMjpuKQogIFNbdF0gPSByYmlub20oMSwxLHBpKlNbdC0xXSsoMS14aSkqKDEtU1t0LTFdKSkKeSA9IHJub3JtKG4sMCxzcXJ0KHNpZzEyKigxLVMpK3NpZzIyKlMpKQp4aS50cnVlID0geGkKcGkudHJ1ZSA9IHBpCnNpZzEyLnRydWUgPSBzaWcxMgpzaWcyMi50cnVlID0gc2lnMjIKYGBgCgpMZXQgdXMgdGFrZSBhIGxvb2sgYXQgdGhlIHRpbWUgc2VyaWVzIG9mICRzX3QkIGFuZCAkeV90JC4KCmBgYHtyIGZpZy53aWR0aD0xMixmaWcuaGVpZ2h0PTEyfQpwYXIobWZyb3c9YygyLDEpKQpwbG90KFMseGxhYj0iVGltZSIseWxhYj0iUyh0KSIscGNoPTE2KQpwbG90KHkseGxhYj0iVGltZSIseWxhYj0ieSh0KSIscGNoPTE2KQpgYGAKCiMgRm9yd2FyZCBhbmQgYmFja3dhcmQgcHJvYmFiaWxpdGllcwoKQSBjb21tb24gaW5mZXJlbnRpYWwgZ29hbCwgaXQgaXMgdG8gdXNlIHRoZSBkYXRhICR5XzEsXGxkb3RzLHlfbiQgdG8gY29tcHV0ZSB0aGUgZm9sbG93aW5nIChvbmxpbmUgb3IgZmlsdGVyZWQpIG1hcmdpbmFsIHByb2JhYmlsaXRpZXM6CiQkClxwaV90ID0gUHIoc190PTF8eV97MTp0fSkgXCBcIFwgXG1ib3h7YW5kfSBcIFwgXCB7XHRpbGRlIFxwaX1fdCA9IFByKHNfdD0xfHlfezE6bn0pLgokJAphc3N1bWluZywgZm9yIGluc3RhbmNlLCB0aGF0ICRccGlfMD0wLjUkLgoKQW5vdGhlciBpbXBvcnRhbnQgZ29hbCBpcyBqb2ludGx5IHNhbXBsaW5nIHRoZSBsYXRlbnQvaGlkZGVuIHN0YXRlcyAkXHtzXzEsXGxkb3RzLHNfblx9JCBmcm9tICRwKHNfMSxcbGRvdHMsc19ufHlfezE6bn0pJCByZWN1cnNpdmVseSBhbmQgYmFja3dhcmRzIChzZWUgdGhlIGJhY2t3YXJkIHJlY3Vyc2lvbnMgYmVsb3cpLgoKIyMgRm9yd2FyZCBwcm9iYWJpbGl0aWVzCgpXZSB3YW50IHRvIGJlIGFibGUgdG8gc3RhcnQgYXQgdGltZSAkdC0xJCB3aXRoICRccGlfe3QtMX09UHIoc197dC0xfT0xfHlfezE6KHQtMSl9KSQgYW5kIGZpbmlzaCBhdCB0aW1lICR0JCB3aXRoICRccGlfdD1QcihzX3Q9MXx5X3sxOnR9KSQ6CgpcYmVnaW57ZXFuYXJyYXkqfQpQcihzX3Q9MXx5X3sxOnR9KSAmXHByb3B0byYgUHIoc190PTF8eV97MToodC0xKX0pIHBfTih5X3Q7MCxcc2lnbWFfMV4yKSxcXApQcihzX3Q9MHx5X3sxOnR9KSAmXHByb3B0byYgUHIoc190PTB8eV97MToodC0xKX0pIHBfTih5X3Q7MCxcc2lnbWFfMl4yKQpcZW5ke2VxbmFycmF5Kn0Kd2l0aApcYmVnaW57ZXFuYXJyYXkqfQpQcihzX3Q9MXx5X3sxOih0LTEpfSkgJj0mIFByKHNfdD0xfHNfe3QtMX09MCx5X3sxOih0LTEpfSlQcihzX3t0LTF9PTB8eV97MToodC0xKX0pXFwKICAgICAgICAgICAgICAgICAgICAgICYrJiBQcihzX3Q9MXxzX3t0LTF9PTEseV97MToodC0xKX0pUHIoc197dC0xfT0xfHlfezE6KHQtMSl9KVxcCiAgICAgICAgICAgICAgICAgICAgICAmPSYgKDEtXHhpKSgxLVxwaV97dC0xfSkrXHBpXHBpX3t0LTF9XFwKICAgICAgICAgICAgICAgICAgICAgICY9JiBcYWxwaGFfdCxcXApQcihzX3Q9MHx5X3sxOih0LTEpfSkgJj0mIFByKHNfdD0wfHNfe3QtMX09MCx5X3sxOih0LTEpfSlQcihzX3t0LTF9PTB8eV97MToodC0xKX0pXFwKICAgICAgICAgICAgICAgICAgICAgICYrJiBQcihzX3Q9MHxzX3t0LTF9PTEseV97MToodC0xKX0pUHIoc197dC0xfT0xfHlfezE6KHQtMSl9KVxcCiAgICAgICAgICAgICAgICAgICAgICAmPSYgXHhpKDEtXHBpX3t0LTF9KSsoMS1ccGkpXHBpX3t0LTF9XFwKICAgICAgICAgICAgICAgICAgICAgICY9JiAxLVxhbHBoYV90LgpcZW5ke2VxbmFycmF5Kn0KVGhlcmVmb3JlLApcYmVnaW57ZXFuYXJyYXkqfQpQcihzX3Q9MXx5X3sxOnR9KSAmXHByb3B0byYgXGFscGhhX3QgcF9OKHlfdDswLFxzaWdtYV8xXjIpLFxcClByKHNfdD0wfHlfezE6dH0pICZccHJvcHRvJiAoMS1cYWxwaGFfdCkgcF9OKHlfdDswLFxzaWdtYV8yXjIpClxlbmR7ZXFuYXJyYXkqfQphbmQKJCQKXHBpX3Q9UHIoc190PTF8eV97MTp0fSk9XGZyYWN7XGFscGhhX3RwX04oeV90OzAsXHNpZ21hXzFeMil9e1xhbHBoYV90cF9OKHlfdDswLFxzaWdtYV8xXjIpKygxLVxhbHBoYV90KXBfTih5X3Q7MCxcc2lnbWFfMl4yKX0uCiQkCgpgYGB7cn0KIyBGb3J3YXJkIHByb2JhYmlsaXRpZXMKcGkwICAgID0gMC41CnBpdCAgICA9IHJlcCgwLG4pCiMgdD0xCkEgICAgICA9ICgxLXhpKSooMS1waTApK3BpKnBpMAp0ZXJtMSAgPSBkbm9ybSh5WzFdLDAsc3FydChzaWcyMikpKkEKdGVybTIgID0gZG5vcm0oeVsxXSwwLHNxcnQoc2lnMTIpKSooMS1BKQpwaXRbMV0gPSB0ZXJtMS8odGVybTErdGVybTIpCgojIHQ9MiwuLi4sbgpmb3IgKHQgaW4gMjpuKXsKICBBID0gKDEteGkpKigxLXBpdFt0LTFdKStwaSpwaXRbdC0xXQogIHRlcm0xID0gZG5vcm0oeVt0XSwwLHNxcnQoc2lnMjIpKSpBCiAgdGVybTIgPSBkbm9ybSh5W3RdLDAsc3FydChzaWcxMikpKigxLUEpCiAgcGl0W3RdID0gdGVybTEvKHRlcm0xK3Rlcm0yKQp9CmBgYAoKTGV0IHVzIHRha2UgYSBsb29rIGF0IHRoZXNlIGZvcndhcmQgcHJvYmFiaWxpdGllcy4KCmBgYHtyIGZpZy53aWR0aD0xMixmaWcuaGVpZ2h0PTZ9CnBhcihtZnJvdz1jKDEsMSkpCnBsb3QoUyx4bGFiPSJUaW1lIix5bGFiPSJTKHQpIixwY2g9MTYpCnBvaW50cyhwaXQsY29sPTIpCmBgYAoKCiMjIEJhY2t3YXJkIHByb2JhYmlsaXRpZXMKClNpbWlsYXJseSwgZm9yICR0PW4sXGxkb3RzLDIkLCAKXGJlZ2lue2VxbmFycmF5Kn0Ke1x0aWxkZSBccGl9X3t0LTF9ID0gClByKHNfe3QtMX09MXx5X3sxOm59KSAmPSYgUHIoc197dC0xfT0xfHNfdD0wLHlfezE6bn0pUHIoc190PTB8eV97MTpufSkKICAgICAgICAgICAgICAgICAgICAgICsgUHIoc197dC0xfT0xfHNfdD0xLHlfezE6bn0pUHIoc190PTF8eV97MTpufSlcXAogICAgICAgICAgICAgICAgICAgICAgJj0mIFByKHNfe3QtMX09MXxzX3Q9MCx5X3sxOnR9KSgxLXtcdGlsZGUgXHBpfV90KQogICAgICAgICAgICAgICAgICAgICAgKyBQcihzX3t0LTF9PTF8c190PTEseV97MTp0fSl7XHRpbGRlIFxwaX1fdC4KXGVuZHtlcW5hcnJheSp9ClRoZXJlZm9yZSwgd2UgbmVlZCB0byBjb21wdXRlIAokJApQcihzX3t0LTF9PTF8c190PTAseV97MTp0fSkgXCBcIFwgXG1ib3h7YW5kfVwgXCBcIFByKHNfe3QtMX09MXxzX3Q9MSx5X3sxOnR9KSwKJCQKd2hpY2ggYXJlIG9idGFpbmVkIHZpYQpcYmVnaW57ZXFuYXJyYXkqfQpQcihzX3t0LTF9PTF8c190PTAseV97MTp0fSkgJlxwcm9wdG8mIFByKHNfdD0wfHNfe3QtMX09MSx5X3sxOnR9KVByKHNfe3QtMX09MXx5X3sxOih0LTEpfSk9KDEtXHBpKSBccGlfe3QtMX0sXFwKUHIoc197dC0xfT0wfHNfdD0wLHlfezE6dH0pICZccHJvcHRvJiAKUHIoc190PTB8c197dC0xfT0wLHlfezE6dH0pUHIoc197dC0xfT0wfHlfezE6KHQtMSl9KT1ceGkgKDEtXHBpX3t0LTF9KSxcXApcZW5ke2VxbmFycmF5Kn0KYW5kClxiZWdpbntlcW5hcnJheSp9ClByKHNfe3QtMX09MXxzX3Q9MSx5X3sxOnR9KSAmXHByb3B0byYKUHIoc190PTF8c197dC0xfT0xLHlfezE6dH0pUHIoc197dC0xfT0xfHlfezE6KHQtMSl9KT1ccGlccGlfe3QtMX1cXApQcihzX3t0LTF9PTB8c190PTEseV97MTp0fSkgJlxwcm9wdG8mClByKHNfdD0xfHNfe3QtMX09MCx5X3sxOnR9KVByKHNfe3QtMX09MHx5X3sxOih0LTEpfSk9KDEteGkpKDEtXHBpX3t0LTF9KS4KXGVuZHtlcW5hcnJheSp9CgpUaGVyZWZvcmUsClxiZWdpbntlcW5hcnJheSp9ClxiZXRhX3QgJj0mIFByKHNfe3QtMX09MXxzX3Q9MCx5X3sxOnR9KT1cZnJhY3soMS1ccGkpIFxwaV97dC0xfX17KDEtXHBpKSBccGlfe3QtMX0rXHhpICgxLVxwaV97dC0xfSl9XFwKXGFscGhhX3QgJj0mIFByKHNfe3QtMX09MXxzX3Q9MSx5X3sxOnR9KT0gXGZyYWN7XHBpXHBpX3t0LTF9fXtccGlccGlfe3QtMX0rKDEtXHhpKSgxLVxwaV97dC0xfSl9LgpcZW5ke2VxbmFycmF5Kn0KVGhlIGJhY2t3YXJkIHJlY3Vyc2lvbnMgYXJlCiQkCnNfe3QtMX18c190PTAseV97MTp0fSBcc2ltIEJlcihcYmV0YV90KSBcIFwgXCBcbWJveHthbmR9IFwgXCBcIApzX3t0LTF9fHNfdD0xLHlfezE6dH0gXHNpbSBCZXIoXGFscGhhX3QpLAokJCwKd2hpY2ggYXJlIHZlcnkgaW1wb3J0YW50IHdoZW4gam9pbnRseSBzYW1wbGluZyB0aGUgbGF0ZW50L2hpZGRlbiBzdGF0ZXMgJHNfMSxcbGRvdHMsc19uJCBmcm9tICRwKHNfMSxcbGRvdHMsc19ufHlfezE6bn0pJCByZWN1cnNpdmVseSBhbmQgYmFja3dhcmRzOgokJApwKHNfMSxcbGRvdHMsc19ufHlfezE6bn0pID0gXHByb2Rfe3Q9Mn1ebiBwKHNfe3QtMX18c190LHlfezE6dH0pcChzX258eV97MTpufSkuCiQkCkZpbmFsbHksIHRoZSBtYXJnaW5hbCBiYWNrd2FyZCBwcm9iYWJpbGl0aWVzIGFyZQokJAp7XHRpbGRlIFxwaX1fe3QtMX09UHIoc197dC0xfT0xfHlfezE6bn0pPVxiZXRhX3QgKDEte1x0aWxkZSBccGl9X3QpICsgXGFscGhhX3R7XHRpbGRlIFxwaX1fdC4KJCQKCmBgYHtyfQojIEJhY2t3YXJkIHByb2JhYmlsaXRpZXMKcGl0aWwgPSByZXAoMCxuKQpwaXRpbFtuXSA9IHBpdFtuXQpmb3IgKHQgaW4gbjoyKXsKICBhbHBoYSA9IHBpdFt0LTFdKnBpLyhwaXRbdC0xXSpwaSsoMS1waXRbdC0xXSkqKDEteGkpKQogIGJldGEgID0gcGl0W3QtMV0qKDEtcGkpLyhwaXRbdC0xXSooMS1waSkrKDEtcGl0W3QtMV0pKnhpKQogIHBpdGlsW3QtMV0gPSBhbHBoYSpwaXRpbFt0XStiZXRhKigxLXBpdGlsW3RdKQp9CgojIHN0b3JpbmcgZm9yd2FyZCBhbmQgYmFja3dhcmQgcHJvYmFiaWxpdGllcyBmb3IgbGF0ZXIgdXNlCnBpcy5mZiA9IHBpdApwaXMuYnMgPSBwaXRpbApgYGAKCmBgYHtyIGZpZy53aWR0aD0xMixmaWcuaGVpZ2h0PTEyfQpwYXIobWZyb3c9YygyLDEpKQpwbG90KHBpdCx4bGFiPSJUaW1lIix5bGFiPSJQcihTKHQpPTF8RCh0KSkiLHBjaD0xNixtYWluPSJGb3J3YXJkIHByb2JhYmlsaXRpZXMiKQpsaW5lcyhTLGNvbD0yLGx3ZD0yKQpwbG90KHBpdGlsLHhsYWI9IlRpbWUiLHlsYWI9IlByKFModCk9MXxEKG4pKSIscGNoPTE2LG1haW49IkJhY2t3YXJkIHByb2JhYmlsaXRpZXMiKQpsaW5lcyhTLGNvbD0yLGx3ZD0yKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xMixmaWcuaGVpZ2h0PTh9CnBhcihtZnJvdz1jKDEsMSkpCnBsb3QoUyx4bGFiPSJUaW1lIix5bGFiPSJQcm9iYWJpbGl0aWVzIix0eXBlPSJsIixtYWluPSJGRkJTIGZvciBITU0iLGx3ZD0yKQpsaW5lcyhwaXQsY29sPTIsbHdkPTIpCmxpbmVzKHBpdGlsLGNvbD0zLGx3ZD0zKQpsZWdlbmQoInRvcHJpZ2h0IixsZWdlbmQ9YygiUyh0KSIsIlByKFModCk9MXxEKHQpKSIsIlByKFModCk9MXxEKG4pKSIpLGNvbD0xOjMsbHdkPTIpCmFibGluZShoPTAuNSxsdHk9MikKYGBgCgojIEZ1bGwtYmxvd24gTUNNQyAKCkxldCB1cyBub3cgaW5jbHVkZSB0aGUgcGFyYW1ldGVycyAkXHRoZXRhPShccGksXHhpLFxzaWdtYV8xXjIsXHNpZ21hXzJeMikkIGludG8gdGhlIGFuYWx5c2lzLgpXZSBhc3N1bWUgY29uZGl0aW9uYWxseSBjb25qdWdhdGUgYW5kIGluZGVwZW5kZW50IHByaW9ycyBmb3IgdGhlIGNvbXBvbmVudHMgb2YgJFx0aGV0YSQsIGkuZS4KJCQKcChcdGhldGEpID0gcChccGkpcChceGkpcChcc2lnbWFfMV4yKXAoXHNpZ21hXzJeMiksCiQkCndpdGggJFxwaSQgYW5kICRceGkkIGZvbGxvd2luZyAkVSgwLDEpJCBhbmQgJFxzaWdtYV8xXjIkIGFuZCAkXHNpZ21hXzJeMiQgZm9sbG93aW5nICRJRygzLzIsMy8yKSQsIHNvIAokRShcc2lnbWFfMV4yKT1FKFxzaWdtYV8yXjIpPTMkIGFuZCA5NVwlIHByb2JhYmlsaXR5IGludGVydmFsIGFwcHJveGltYXRlbHkgZXF1YWwgdG8gJCgwLjQsOC41KSQuICAKCiMjIEZ1bGwgY29uZGl0aW9uYWxzCgpGdWxsIHBvc3RlcmlvciBpbmZlcmVuY2UgaXMgZmFjaWxpdGF0ZWQgYnkgYSBjdXN0b21pemVkIEdpYmJzIHNhbXBsZXIgdGhhdCBjeWNsZXMgdGhyb3VnaCB0aGUgZnVsbCBjb25kaXRpb25hbHM6ClxiZWdpbntlcW5hcnJheSp9CnAoc197MTpufXx5X3sxOm59LFx0aGV0YSlcXApwKFxwaXx5X3sxOm59LHNfezE6bn0sXHhpLFxzaWdtYV8xXjIsXHNpZ21hXzJeMilcXApwKFx4aXx5X3sxOm59LHNfezE6bn0sXHBpLFxzaWdtYV8xXjIsXHNpZ21hXzJeMilcXApwKFxzaWdtYV8xXjJ8eV97MTpufSxzX3sxOm59LFxwaSxceGksXHNpZ21hXzJeMilcXApwKFxzaWdtYV8yXjJ8eV97MTpufSxzX3sxOm59LFxwaSxceGksXHNpZ21hXzFeMiksClxlbmR7ZXFuYXJyYXkqfQp3aXRoIHRoZSBmaXJzdCBvbmUsICRwKHNfezE6bn18eV97MTpufSxcdGhldGEpJCBhbHJlYWR5IGRlcml2ZWQgdmlhIHRoZSAqZm9yd2FyZCBmaWx0ZXJpbmcsIGJhY2t3YXJkIHNhbXBsaW5nKiBzY2hlbWUgaW50cm9kdWNlZCBhYm92ZS4KVGhlIG90aGVyIHRocmVlIGZ1bGwgY29uZGl0aW9uYWxzIGFyZSBlYXNpbHkgZGVyaXZlZCBhbmQgYXJlIGdpdmVzIGJlbG93IGFzIGZvbGxvd3MuICAKCkdpdmVuICRzXzEsXGxkb3RzLHNfbiQsIGxldHMgY29tcHV0ZSB0aGUgb2JzZXJ2ZWQganVtcHMgYmV0d2VlbiByZWdpbWVzICQwJCBhbmQgJDEkOgpcYmVnaW57ZXFuYXJyYXkqfQpuXzEgJj0mIFxzdW1fe2k9MX1ebiBcZGVsdGFfMShzX3tuLTF9KVxkZWx0YV8xKHNfbilcXApuXzIgJj0mIFxzdW1fe2k9MX1ebiBcZGVsdGFfMShzX3tuLTF9KVxkZWx0YV8wKHNfbilcXApuXzMgJj0mIFxzdW1fe2k9MX1ebiBcZGVsdGFfMChzX3tuLTF9KVxkZWx0YV8wKHNfbilcXApuXzQgJj0mIFxzdW1fe2k9MX1ebiBcZGVsdGFfMChzX3tuLTF9KVxkZWx0YV8xKHNfbikuClxlbmR7ZXFuYXJyYXkqfQoKU2ltaWxhcmx5LCBsZXQgJHtcdGlsZGUgeX1fMF4yPVxzdW1fe2k6c19pPTB9IHlfaV4yJCBhbmQgJHtcdGlsZGUgeX1fMV4yPVxzdW1fe2k6c19pPTF9IHlfaV4yJC4gIAoKVGhlcmVmb3JlLApcYmVnaW57ZXFuYXJyYXkqfQooXHBpfHNfezE6bn0pICZcc2ltJiBcbWJveHtCZXRhfShuXzErMSxuXzIrMSlcXAooXHhpfHNfezE6bn0pICZcc2ltJiBcbWJveHtCZXRhfShuXzMrMSxuXzQrMSlcXAooXHNpZ21hXzFeMnx5X3sxOm59LHNfezE6bn0pICZcc2ltJiBJRygobl8yK25fMyszKS8yLCgzK3tcdGlsZGUgeX1fMF4yKS8yKVxcCihcc2lnbWFfMl4yfHlfezE6bn0sc197MTpufSkgJlxzaW0mIElHKChuXzErbl80KzMpLzIsKDMre1x0aWxkZSB5fV8xXjIpLzIpLgpcZW5ke2VxbmFycmF5Kn0KCmBgYHtyfQojIEluaXRpYWwgdmFsdWVzCnhpID0gMC45CnBpID0gMC45CnNpZzEyID0gMQpzaWcyMiA9IDEwCgojIE1DTUMgc2V0dXAKc2V0LnNlZWQoMzIxNDU2KQpwaTAgICAgPSAwLjUKcGl0ICAgID0gcmVwKDAsbikKYnVybmluID0gMTAwMApNICAgICAgPSAxMDAwCnRoaW4gICA9IDEKbml0ZXIgPSBidXJuaW4rTSp0aGluCmRyYXdzLnBhciA9IG1hdHJpeCgwLG5pdGVyLDQpCmRyYXdzLnN0YSA9IG1hdHJpeCgwLG5pdGVyLG4pCnMgPSByZXAoMCxuKQoKIyBNQ01DIGluIGFjdGlvbgpmb3IgKGl0ZXIgaW4gMTpuaXRlcil7CiAgIyBEcmF3aW5nIHRoZSBsYXRlbnQgc3RhdGVzIHMoMSksLi4uLHMobikKICBBICAgICAgPSAoMS14aSkqKDEtcGkwKStwaSpwaTAKICB0ZXJtMSAgPSBkbm9ybSh5WzFdLDAsc3FydChzaWcyMikpKkEKICB0ZXJtMiAgPSBkbm9ybSh5WzFdLDAsc3FydChzaWcxMikpKigxLUEpCiAgcGl0WzFdID0gdGVybTEvKHRlcm0xK3Rlcm0yKQogIGZvciAodCBpbiAyOm4pewogICAgQSA9ICgxLXhpKSooMS1waXRbdC0xXSkrcGkqcGl0W3QtMV0KICAgIHRlcm0xID0gZG5vcm0oeVt0XSwwLHNxcnQoc2lnMjIpKSpBCiAgICB0ZXJtMiA9IGRub3JtKHlbdF0sMCxzcXJ0KHNpZzEyKSkqKDEtQSkKICAgIHBpdFt0XSA9IHRlcm0xLyh0ZXJtMSt0ZXJtMikKICB9CiAgc1tuXSA9IHJiaW5vbSgxLDEscGl0W25dKQogIGZvciAodCBpbiBuOjIpewogICAgYWxwaGEgPSBwaXRbdC0xXSpwaS8ocGl0W3QtMV0qcGkrKDEtcGl0W3QtMV0pKigxLXhpKSkKICAgIGJldGEgID0gcGl0W3QtMV0qKDEtcGkpLyhwaXRbdC0xXSooMS1waSkrKDEtcGl0W3QtMV0pKnhpKQogICAgc1t0LTFdID0gc1t0XSpyYmlub20oMSwxLGFscGhhKSsoMS1zW3RdKSpyYmlub20oMSwxLGJldGEpCiAgfQogIAogICMgRHJhd2luZyB0aGUgZml4ZWQgcGFyYW1ldGVycyAocGkseGksc2lnbWExMixzaWdtYTIyKQogIG4xICAgID0gc3VtKChzWzE6KG4tMSldPT0xKSYoc1syOm5dPT0xKSkKICBuMiAgICA9IHN1bSgoc1sxOihuLTEpXT09MSkmKHNbMjpuXT09MCkpCiAgbjMgICAgPSBzdW0oKHNbMToobi0xKV09PTApJihzWzI6bl09PTApKQogIG40ICAgID0gc3VtKChzWzE6KG4tMSldPT0wKSYoc1syOm5dPT0xKSkKICBwaSAgICA9IHJiZXRhKDEsbjErMSxuMisxKQogIHhpICAgID0gcmJldGEoMSxuMysxLG40KzEpCiAgc2lnMTIgPSAxL3JnYW1tYSgxLChuMituMyszKS8yLCgzK3N1bSh5W3M9PTBdXjIpKS8yKQogIHNpZzIyID0gMS9yZ2FtbWEoMSwobjErbjQrMykvMiwoMytzdW0oeVtzPT0xXV4yKSkvMikKCiAgIyBTdG9yaW5nIE1DTUMgZHJhd3MKICBkcmF3cy5wYXJbaXRlcixdID0gYyhzaWcxMixzaWcyMixwaSx4aSkKICBkcmF3cy5zdGFbaXRlcixdID0gcwp9CnBpcy5mdWxsID0gYXBwbHkoZHJhd3Muc3RhWyhidXJuaW4rMSk6bml0ZXIsXSwyLG1lYW4pCmBgYAoKIyAgUG9zdGVyaW9yIHRyYWNlIHBsb3RzIGFuZCBtYXJnaW5hbCBwb3N0ZXJpb3IgaGlzdG9ncmFtcwoKYGBge3IgZmlnLndpZHRoPTEyLGZpZy5oZWlnaHQ9OH0KbmFtZXMgPSBjKCJzaWdtYTEyIiwic2lnbWEyMiIsInBpIiwieGkiKQp0cnVlID0gYyhzaWcxMi50cnVlLHNpZzIyLnRydWUscGkudHJ1ZSx4aS50cnVlKQpwYXIobWZyb3c9YygyLDQpKQpmb3IgKGkgaW4gMTo0KXsKICB0cy5wbG90KGRyYXdzLnBhclssaV0seGxhYj0iSXRlcmF0aW9ucyIseWxhYj0iIixtYWluPW5hbWVzW2ldKQogIGFibGluZShoPXRydWVbaV0sY29sPTIsbHdkPTIpCn0KZm9yIChpIGluIDE6NCl7CiAgaGlzdChkcmF3cy5wYXJbKGJ1cm5pbisxKTpuaXRlcixpXSxwcm9iPVRSVUUseGxhYj0iIixtYWluPSIiKQogIGFibGluZSh2PXRydWVbaV0sY29sPTIsbHdkPTIpCn0KYGBgCgojIENvbXBhcmluZyBwcm9iYWJpbGl0aWVzCgpMZXQgdXMgY29tcGFyZSAkUHIoc190PTF8eV97MTp0fSxcdGhldGEpJCwgJFByKHNfdD0xfHlfezE6bn0sXHRoZXRhKSQgYW5kICRQcihzX3Q9MXx5X3sxOm59KSQuCgpgYGB7ciBmaWcud2lkdGg9MTIsZmlnLmhlaWdodD04fQpwYXIobWZyb3c9YygxLDEpKQpwbG90KFMseGxhYj0iVGltZSIseWxhYj0iUHJvYmFiaWxpdGllcyIsdHlwZT0ibCIsbWFpbj0iRkZCUyBmb3IgSE1NIikKbGluZXMocGlzLmZmLGNvbD0yKQpsaW5lcyhwaXMuYnMsY29sPTMpCmxpbmVzKHBpcy5mdWxsLGNvbD00KQpsZWdlbmQoInRvcHJpZ2h0IixsZWdlbmQ9YygiUyh0KSIsIlByKFModCk9MXxEKHQpLHRoZXRhKSIsIlByKFModCk9MXxEKG4pLHRoZXRhKSIsIlByKFModCk9MXxEKG4pKSIpLGNvbD0xOjQsbHdkPTIpCmFibGluZShoPTAuNSxsdHk9MikKYGBgCgoK