The No-U-Turn Sampler (NUTS) / Dynamic Hamiltonian Monte Carlo
The Hamiltonian Monte Carlo, henceforth HMC, enables efficient
exploration of complex and confined target distributions, but its
performance is largely governed by the step size \(\epsilon\) and number of steps \(L\) used in simulating the Hamiltonian
trajectories. Depending on how large \(L\) is, too small a step size may unduly
increase the computation time or lead to exploration of the target
distribution that resembles pure random walk, which we want to avoid
when using HMC. Too large a step size makes the error explode in
simulating the Hamiltonian trajectory, which results in low acceptance
rates and poor exploration. Assuming that we have found a stable value
for \(\epsilon\), finding a good value
for \(L\) is not easy in general. We
would like \(L\) to be large enough so
that the trajectory travels long enough through the target distribution
yielding almost uncorrelated draws. However, if \(L\) is too large, then due to Hamiltonian
dynamics, the trajectory starts to rotate towards the starting point and
eventually makes a full cycle resulting in wasted computation time, and
even worse, the trajectory can stop near the starting point. Too few
steps can also dilute the exploration to random walk.
In the static version of HMC the user needs to hand-tune the
parameters either by visualizing the trajectories, performing several
preliminary runs, assessing the convergence of the chains or, more
preferably, a combination of these. For many distributions, however,
optimal parameter values in one region may be suboptimal in some other
region. For example, if the target distribution is constrained in some
directions, consider e.g. heavily correlated random variables, then we
would like to use long trajectories in the less constrained directions.
Also, when the number of parameters grows, visualizing and interpreting
the trajectories becomes more and more difficult. Fortunately, setting
the parameters via trial and error can be avoided for both \(\epsilon\) and \(L\), and they can be adapted dynamically
instead. In this demo, however, we are only considering the dynamic
adaptation of the number of steps \(L\).
We would like to find an algebraic representation for the criterion
of having simulated the trajectory long enough. The trajectory is
considered to be “long enough” when the distance between the starting
point and the proposal is maximized. Let \(\theta\) be the starting point of the
simulation. Now a natural option would be to simulate deterministically
as in the static HMC (BDA3 p. 300) until we are at a position-momentum
pair \((\tilde{\theta}, \tilde{\phi})\)
for which it holds that
\[
(\tilde{\theta} - \theta)^T \tilde{\phi} < 0
\]
That is, we should stop the simulation when the angle between the
current momentum \(\tilde{\phi}\) and
the vector from the starting point \(\theta\) to the current point \(\tilde{\theta}\) is less than 90 degrees.
This criterion corresponds to stopping when the trajectory starts to
rotate towards the starting point and the distance between \(\theta\) and \(\tilde{\theta}\) starts to decrease.
Unfortunately, the above scheme does not guarantee reversibility of the
Markov chain, and thus it is not guaranteed to converge to the desired
target distribution. The reversibility can be remediated by making the
simulation process random by allowing steps both forward and backward in
time. This is incorporated in the NUTS algorithm (Hoffman and Gelman 2014) which we will present
next. Further variants of dynamic HMC are described by Betancourt (2018). The specific algorithm and
implementation details of dynamic HMC in Stan slightly differ from the
ones described by Hoffman and Gelman
(2014) and Betancourt (2018), and
unfortunately there is no detailed description of the differences
available except in the C++ code. The algorithm presented here follows
the original NUTS algorithm by Hoffman and Gelman
(2014).
Assume that \(p(\theta | y)\) is the
target distribution and \(\theta^{t}\)
is the current draw. Let \(\epsilon >
0\) be the step size for the leapfrog steps. The NUTS algorithm
for drawing \(\theta^{t+1}\) consists
of the following steps.
Sample a momentum vector \(\phi \sim
N(0, M)\), where \(M\) is a
symmetric positive-definite mass matrix. This step is identical to the
first step in static HMC.
Sample a slice variable \(u | \theta^t,
\phi \sim \text{Uniform}([0, e^{-H(\theta^t, \phi)}])\), where
\(H(\theta, \phi) = -\log(p(\theta | y)) +
\frac{1}{2} \phi^T M^{-1} \phi\) is the Hamiltonian function that
specifies the “total energy” at the position-momentum pair \((\theta, \phi)\).
Generate a set \(C\) of proposal
points as follows. Set \(C = \{ \theta^t
\}\). Starting the Hamiltonian simulation from \((\theta^t, \phi)\), for \(j=0,1,2,...\),
Sample \(v_j \sim \text{Uniform}(\{ -1,
1 \})\) and perform \(2^j\)
leapfrog steps with the step size \(v_j
\epsilon\), always appending to the trajectory from the previous
iteration.
The newly generated \(2^j\)
points are equivalent to the leaf nodes of a perfect, ordered binary
tree with depth \(j\), see Figure 1
below. Now consider each of the \(2^j-1\) subtrees that have depth greater
than \(0\), and for a given subtree
denote the points associated with the leftmost and rightmost leaves by
\((\theta^-, \phi^-)\) and \((\theta^+, \phi^+)\), respectively. If for
some subtree it holds that
\[
(\theta^+ - \theta^-)^T \phi^- < 0
\quad \text{or} \quad
(\theta^+ - \theta^-)^T \phi^+ < 0,
\]
then terminate the simulation loop. If for some point \((\theta, \phi)\) from the new \(2^j\) points it holds that
\[
H(\theta, \phi) + \log u > \Delta_{\text{max}}
\]
for some nonnegative \(\Delta_{\text{max}}\), then also terminate
the simulation loop. We will use the value \(\Delta_{\text{max}} = 1000\).
If the former stopping criterion above holds for the leftmost and
rightmost points of the full simulated trajectory, then for all \((\theta, \phi)\) of the newly simulated
\(2^j\) points, add \(\theta\) to \(C\) if \(e^{-H(\theta, \phi)} \geq u\). Finally,
terminate the simulation loop.
If no stopping criterion was met, then for all \((\theta, \phi)\) of the new points, add
\(\theta\) to \(C\) if \(e^{-H(\theta, \phi)} \geq u\), and move on
to the next iteration.
Sample a value for \(\theta^{t+1}\) uniformly from \(C\).
In summary, after each iteration the number of leapfrog steps is
doubled, and the simulation is stopped whenever any subtrajectory
equivalent to a binary subtree satisfies one of the above stopping
criteria. The slice variable \(u\) is
chosen so that \(\theta^{t+1}\) can be
drawn uniformly from \(C\). Note that
if we were able to simulate the Hamiltonian trajectory exactly, \(H(\theta, \phi)\) would remain constant
over the whole trajectory, and \(e^{-H(\theta,
\phi)} \geq u\) would hold for all points on the trajectory.
Thus, the condition \(e^{-H(\theta, \phi)}
\geq u\) can be interpreted as discarding all points for which
the simulation error is too large. The stopping condition \(H(\theta, \phi) + \log u >
\Delta_{\text{max}}\) in step 3 is also related to terminating
the simulation if the simulation error becomes too large. Note that the
number of leapfrog steps increases exponentially, but as long as \(\epsilon\) is small enough and the target
distribution \(p(\theta | y)\) is
sufficiently well-behaved, the simulation error shouldn’t increase in
the number of leapfrog steps, which makes the above dynamic scheme
attractive. As usual with HMC, the leapfrog steps require the gradient
of the log-density, i.e. \(\frac{d \log
p(\theta | y)}{d\theta}\).
Implementation and visualization of NUTS
library(latex2exp)
library(ggplot2)
theme_set(theme_minimal())
library(tidyr)
library(MASS)
library(gganimate)
library(posterior)
Next we will implement the NUTS algorithm and visualize how the
trajectories look like. The following subroutines will be useful in the
implementation.
# Perform a single leapfrog step.
leapfrog <- function(theta, phi, epsilon, grad_log_p, M_inv) {
phi_tilde <- phi + 0.5 * epsilon * grad_log_p(theta)
theta_tilde <- theta + epsilon * M_inv %*% phi_tilde
phi_tilde <- phi_tilde + 0.5 * epsilon * grad_log_p(theta_tilde)
list(theta=c(theta_tilde), phi=c(phi_tilde))
}
# Evaluate the Hamiltonian.
hamiltonian <- function(theta, phi, log_p, M_inv) {
-log_p(theta) + 0.5 * sum(phi * (M_inv %*% phi))
}
The step 3 of the NUTS algorithm above is the trickiest to implement.
In practice, instead of considering all the subtrees only after
simulating the \(2^j\) leapfrog steps,
it’s easier to perform the steps and construct the trees recursively at
the same time.
# Perform the leapfrog steps and construct the subtrees recursively.
build_tree <- function(theta, phi, u, v, j, epsilon, log_p, grad_log_p, M_inv, trajectory) {
if (j == 0) {
# Base case. Take just one leapfrog step.
step <- leapfrog(theta, phi, v*epsilon, grad_log_p, M_inv)
theta <- step$theta
phi <- step$phi
# Color the new theta red if it's certainly not a valid proposal, and
# color it yellow otherwise.
if (u <= exp(-hamiltonian(theta, phi, log_p, M_inv))) {
color <- "yellow"
} else {
color <- "red"
}
# Update the trajectory.
if (v == -1) {
trajectory$thetas <- rbind(theta, trajectory$thetas)
trajectory$colors <- c(color, trajectory$colors)
} else {
trajectory$thetas <- rbind(trajectory$thetas, theta)
trajectory$colors <- c(trajectory$colors, color)
}
# Check the second stopping condition in step 3. We use Delta_max=1000.
terminate <- hamiltonian(theta, phi, log_p, M_inv) + log(u) > 1000
return(list(theta_minus=theta, phi_minus=phi, theta_plus=theta, phi_plus=phi,
trajectory=trajectory, terminate=terminate))
} else {
# Build the left and right subtrees recursively.
tree1 <- build_tree(theta, phi, u, v, j-1, epsilon,
log_p, grad_log_p, M_inv, trajectory)
theta_minus <- tree1$theta_minus
phi_minus <- tree1$phi_minus
theta_plus <- tree1$theta_plus
phi_plus <- tree1$phi_plus
if (v == -1) {
tree2 <- build_tree(theta_minus, phi_minus, u, v, j-1, epsilon,
log_p, grad_log_p, M_inv, tree1$trajectory)
theta_minus <- tree2$theta_minus
phi_minus <- tree2$phi_minus
} else {
tree2 <- build_tree(theta_plus, phi_plus, u, v, j-1, epsilon,
log_p, grad_log_p, M_inv, tree1$trajectory)
theta_plus <- tree2$theta_plus
phi_plus <- tree2$phi_plus
}
# Check the U-turn stopping condition and also take into account
# the stopping flags from building the subtrees.
condition1 <- sum((theta_plus - theta_minus) * phi_minus) < 0
condition2 <- sum((theta_plus - theta_minus) * phi_plus) < 0
terminate <- condition1 || condition2 || tree1$terminate || tree2$terminate
return(list(theta_minus=theta_minus, phi_minus=phi_minus,
theta_plus=theta_plus, phi_plus=phi_plus,
trajectory=tree2$trajectory, terminate=terminate))
}
}
We are now ready to implement the sampling function.
# Draw the given number of samples from the target distribution using NUTS.
#
# Parameters:
# - theta Starting point of the chain.
# - log_p Log target distribution function.
# - grad_log_p Gradient of the log target distribution function.
# - n_iter Number of draws.
# - M Symmetric positive definite mass matrix. Default: identity matrix.
# - epsilon Step size for the leapfrog steps. Default: 0.1
# - store_paths Whether to store and return the individual trajectories
# for visualization purposes. Default: TRUE
#
# If store_paths=TRUE, returns a list consisting of the n_iter draws and their
# corresponding trajectories.
# If store_paths=FALSE, returns just the draws.
NUTS <- function(theta, log_p, grad_log_p, n_iter, M=NULL, epsilon=0.1, store_paths=TRUE) {
if (is.null(M)) {
M <- diag(length(theta))
}
M_inv <- solve(M)
chain <- list(draws=matrix(nrow=n_iter+1, ncol=length(theta)),
trajectories=list())
chain$draws[1,] <- theta
for (i in 1:n_iter) {
# Step 1. Draw a momentum vector from N(0, M).
phi <- mvrnorm(n=1, rep(0, length(theta)), M)
# Step 2. Draw a slice variable u.
u <- runif(1, 0, exp(-hamiltonian(chain$draws[i,], phi, log_p, M_inv)))
# Step 3. Perform the leapfrog steps. Color the starting point black.
theta_minus <- chain$draws[i,]
phi_minus <- phi
theta_plus <- chain$draws[i,]
phi_plus <- phi
trajectory <- list(thetas=matrix(theta_minus, nrow=1), colors=c("black"))
j <- 0
terminate <- FALSE
while (!terminate) {
# Draw a direction.
v <- sample(c(-1, 1), 1)
if (v == -1) {
tree <- build_tree(theta_minus, phi_minus, u, v, j, epsilon,
log_p, grad_log_p, M_inv, trajectory)
theta_minus <- tree$theta_minus
phi_minus <- tree$phi_minus
} else {
tree <- build_tree(theta_plus, phi_plus, u, v, j, epsilon,
log_p, grad_log_p, M_inv, trajectory)
theta_plus <- tree$theta_plus
phi_plus <- tree$phi_plus
}
trajectory$thetas <- tree$trajectory$thetas
trajectory$colors <- tree$trajectory$colors
# If a stopping condition was met, the newly generated subtrajectory
# cannot be used in the sampling and is thus colored red.
if (tree$terminate) {
for (k in 1:2^j) {
if (v == 1) {
trajectory$colors[length(trajectory$colors)+1-k] <- "red"
} else {
trajectory$colors[k] <- "red"
}
}
}
# Check the stopping conditions.
condition1 <- sum((theta_plus - theta_minus) * phi_minus) < 0
condition2 <- sum((theta_plus - theta_minus) * phi_plus) < 0
terminate <- tree$terminate || condition1 || condition2
j <- j + 1
}
# Step 4. Draw the next sample uniformly amongst the starting point and
# the yellow-colored points. The chosen point is colored green.
valid_indices <- 1:length(trajectory$colors)
valid_indices <- valid_indices[(trajectory$colors == "yellow") |
(trajectory$colors == "black")]
draw_id <- sample(valid_indices, 1)
trajectory$colors[draw_id] <- "green"
chain$draws[i+1,] <- trajectory$thetas[draw_id,]
if (store_paths) {
chain$trajectories[[length(chain$trajectories)+1]] <- trajectory
}
}
if (store_paths) {
return(chain)
} else {
return(chain$draws)
}
}
Let’s then visualize the NUTS algorithm. As the target distribution
we use a bivariate normal distribution \(p(\theta | y) = N(\theta | 0, \Sigma)\),
where
\[
\Sigma =
\begin{bmatrix}
1 & 0.8 \\
0.8 & 1
\end{bmatrix}
\]
Since \(p(\theta | y) \propto
\exp(-\frac{1}{2} \theta^T \Sigma^{-1} \theta)\), the log-density
is given, up to an additive constant, by \(\log p(\theta | y) = -\frac{1}{2} \theta^T
\Sigma^{-1} \theta\), and the gradient of the log-density is
given by \(\frac{d \log p(\theta |
y)}{d\theta} = -\Sigma^{-1} \theta\).
Sigma <- matrix(c(1, 0.8, 0.8, 1), nrow=2)
Sigma_inv <- solve(Sigma)
log_p <- function(theta) {
-0.5 * sum(theta * (Sigma_inv %*% theta))
}
grad_log_p <- function(theta) {
-Sigma_inv %*% theta
}
Let \(\theta^0 = (-2.5, 2.5)\) be
the starting point. Simulate 2000 draws.
# Draw samples from the target distribution to visualize its 90%
# HPD region with ggplot's stat_ellipse function.
target_draws <- data.frame(mvrnorm(100000, c(0, 0), Sigma))
theta_0 <- c(-2.5, 2.5)
n_iter <- 2000
nuts_chain <- NUTS(theta_0, log_p, grad_log_p, n_iter)
The following example shows how to plot a single trajectory
# Try to find a trajectory with at least 32 points.
t <- nuts_chain$trajectories[[100]]
for (i in 1:length(nuts_chain$trajectories)) {
if (length(nuts_chain$trajectories[[i]]$colors) >= 32) {
t <- nuts_chain$trajectories[[i]]
break
}
}
df <- data.frame(theta1 = t$thetas[,1],
theta2 = t$thetas[,2],
color = t$colors,
theta1_end = c(t$thetas[1,1], t$theta[-nrow(t$thetas), 1]),
theta2_end = c(t$thetas[1,2], t$theta[-nrow(t$thetas), 2]))
ggplot() +
stat_ellipse(data = target_draws, aes(x = X1, y = X2, color = "HPD"), level = 0.9) +
geom_segment(data = df, aes(x = theta1, xend = theta1_end, color = "gray",
y = theta2, yend = theta2_end), alpha = 0.5) +
geom_point(data = df, aes(theta1, theta2, color = color), size = 2) +
coord_cartesian(xlim = c(-4, 4), ylim = c(-4, 4)) +
labs(x = TeX("$\\theta_1$"), y = TeX("$\\theta_2$")) +
scale_color_manual(values = c("black" = "black",
"red" = "red",
"yellow" = "yellow3",
"green" = "green4",
"HPD" = "blue"),
labels = c("black" = "Starting point",
"red" = "Outside of the slice",
"yellow" = "Within the slice",
"green" = "Draw from the slice",
"HPD" = "90% HPD")) +
guides(color = guide_legend(override.aes = list(shape = c(16, 16, 16, 16, NA),
linetype = c(0,0,0,0,1)))) +
theme(legend.position = "bottom", legend.title = element_blank())
The following example displays how to animate the trajectories. Let’s
consider the first 50 draws.
nframes <- 50 # Set this to the number of iterations you wish to visualize.
df_trajectories <- data.frame()
df_draws <- data.frame()
for (i in 1:nframes) {
t <- nuts_chain$trajectories[[i]]
df_trajectories <- rbind(df_trajectories,
list(rep(i, nrow(t$thetas)),
t$thetas[,1],
t$thetas[,2],
t$colors,
c(t$thetas[1,1], t$theta[-nrow(t$thetas), 1]),
c(t$thetas[1,2], t$theta[-nrow(t$thetas), 2])))
df_draws <- rbind(df_draws, list(rep(i, i),
nuts_chain$draws[1:i, 1],
nuts_chain$draws[1:i, 2]))
}
names(df_trajectories) <- c("iter", "theta1", "theta2",
"color", "theta1_end", "theta2_end")
names(df_draws) <- c("iter", "theta1", "theta2")
p <- ggplot() +
stat_ellipse(data = target_draws, aes(x = X1, y = X2, color = "HPD"), level = 0.9) +
geom_point(data = df_draws, aes(x = theta1, y = theta2, color = "black"),
size = 1, alpha = 0.5) +
geom_segment(data = df_trajectories, aes(x = theta1, xend = theta1_end,
y = theta2, yend = theta2_end,
color = "gray"), alpha = 0.5) +
geom_point(data = df_trajectories, aes(theta1, theta2, color = color), size = 2) +
coord_cartesian(xlim = c(-4, 4), ylim = c(-4, 4)) +
labs(x = TeX("$\\theta_1$"), y = TeX("$\\theta_2$")) +
scale_color_manual(values = c("black" = "black",
"red" = "red",
"yellow" = "yellow3",
"green" = "green4",
"HPD" = "blue"),
labels = c("black" = "Starting point",
"red" = "Outside of the slice",
"yellow" = "Within the slice",
"green" = "Draw from the slice",
"HPD" = "90% HPD")) +
guides(color = guide_legend(override.aes = list(shape = c(16, 16, 16, 16, NA),
linetype = c(0,0,0,0,1)))) +
theme(legend.position = "bottom", legend.title = element_blank())
# Make sure that nframes is equal to the number of iterations that you wish to visualize.
anim <- animate(p + transition_time(iter), nframes = nframes, fps = 1)
Show the animation
anim
Plot all the draws after removing a warmup of 50 draws.
warmup <- 50
df <- data.frame(theta1 = nuts_chain$draws[(warmup+1):nrow(nuts_chain$draws), 1],
theta2 = nuts_chain$draws[(warmup+1):nrow(nuts_chain$draws), 2])
ggplot() +
geom_point(data = df, aes(theta1, theta2, color = "1"), alpha = 0.3) +
stat_ellipse(data = target_draws, aes(x = X1, y = X2, color = "2"), level = 0.9) +
coord_cartesian(xlim = c(-4, 4), ylim = c(-4, 4)) +
labs(x = TeX("$\\theta_1$"), y = TeX("$\\theta_2$")) +
scale_color_manual(values = c("steelblue", "blue"),
labels = c("Draws", "90% HPD")) +
guides(color = guide_legend(override.aes = list(
shape = c(16, NA), linetype = c(0, 1), alpha = c(1, 1)))) +
theme(legend.position = "bottom", legend.title = element_blank())
Convergence diagnostics
Pure random-walk MCMC algorithms, such as Gibbs and Metropolis, tend
to struggle in the presence of high correlations between the parameters.
Let us demonstrate how NUTS is able to alleviate this. Consider again a
normal target distribution but with the covariance matrix
\[
\Sigma =
\begin{bmatrix}
1 & 0.99 \\
0.99 & 1
\end{bmatrix}
\]
Sigma <- matrix(c(1, 0.99, 0.99, 1), nrow=2)
Sigma_inv <- solve(Sigma)
log_p <- function(theta) {
-0.5 * sum(theta * (Sigma_inv %*% theta))
}
grad_log_p <- function(theta) {
-Sigma_inv %*% theta
}
Simulate a total of 4 chains, each consisting of 2000 draws. The
starting points are \(\{ (-2.5, 2.5), (2.5,
2.5), (2.5, -2.5), (-2.5, -2.5) \}\).
# For HPD visualization.
target_draws <- data.frame(mvrnorm(100000, c(0, 0), Sigma))
n_iter <- 2000
nuts_chain1 <- NUTS(c(-2.5, 2.5), log_p, grad_log_p, n_iter, store_paths=FALSE)
nuts_chain2 <- NUTS(c( 2.5, 2.5), log_p, grad_log_p, n_iter, store_paths=FALSE)
nuts_chain3 <- NUTS(c( 2.5, -2.5), log_p, grad_log_p, n_iter, store_paths=FALSE)
nuts_chain4 <- NUTS(c(-2.5, -2.5), log_p, grad_log_p, n_iter, store_paths=FALSE)
Plot the draws of the first chain with a warmup of 50 draws.
warmup <- 50
df <- data.frame(theta1 = nuts_chain1[(warmup+1):nrow(nuts_chain1), 1],
theta2 = nuts_chain1[(warmup+1):nrow(nuts_chain1), 2])
ggplot() +
geom_point(data = df, aes(theta1, theta2, color = "1"), alpha = 0.3) +
stat_ellipse(data = target_draws, aes(x = X1, y = X2, color = "2"), level = 0.9) +
coord_cartesian(xlim = c(-4, 4), ylim = c(-4, 4)) +
labs(x = TeX("$\\theta_1$"), y = TeX("$\\theta_2$")) +
scale_color_manual(values = c("steelblue", "blue"),
labels = c("Draws", "90% HPD")) +
guides(color = guide_legend(override.aes = list(
shape = c(16, NA), linetype = c(0, 1), alpha = c(1, 1)))) +
theme(legend.position = "bottom", legend.title = element_blank())
We see that NUTS is able to explore the relevant probability
mass.
Let’s then look at some of the MCMC-specific convergence metrics by
using the \(\texttt{summarise_draws}\)
function from \(\texttt{posterior}\).
draws <- array(data = c(nuts_chain1, nuts_chain2, nuts_chain3, nuts_chain4),
dim = c(n_iter+1, 2, 4),
dimnames = list(NULL, c("theta1", "theta2"), NULL))
draws <- aperm(draws, c(1, 3, 2))
summarise_draws(draws, "mean", "sd", ~quantile(.x, probs = c(0.05, 0.95)), default_convergence_measures())
## # A tibble: 2 × 8
## variable mean sd `5%` `95%` rhat ess_bulk ess_tail
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 theta1 0.0139 1.03 -1.75 1.67 1.00 610. 761.
## 2 theta2 0.0179 1.03 -1.75 1.66 1.00 605. 753.
Based on the \(\widehat{R}\)-values,
it seems that the chains have converged.
Plot a line plot of the first chain using a warmup of 50 draws.
warmup <- 50
df_chain <- data.frame(iter = 1:(nrow(nuts_chain1)-warmup),
theta1 = nuts_chain1[-(1:warmup), 1],
theta2 = nuts_chain1[-(1:warmup), 2]) %>%
pivot_longer(cols = !iter, names_to = "grp", values_to = "value")
ggplot() +
geom_line(data = df_chain, aes(iter, value, color = grp)) +
labs(title = "Trends") +
scale_color_discrete(labels = c("theta1", "theta2")) +
theme(legend.position = 'bottom', legend.title = element_blank())
Finally, visualize the autocorrelation function of the first
chain.
warmup <- 50
nlags <- 50
df_chain <- data.frame(theta1 = nuts_chain1[-(1:warmup), 1],
theta2 = nuts_chain2[-(1:warmup), 2])
df_acf <- sapply(df_chain, function(x) acf(x, lag.max = nlags, plot = F)$acf) %>%
data.frame(iter = 0:(nlags)) %>%
pivot_longer(cols = !iter, names_to = "grp", values_to = "value")
ggplot() +
geom_line(data = df_acf, aes(iter, value, color = grp)) +
geom_hline(aes(yintercept = 0)) +
labs(title = "Autocorrelation function", x = "Iteration") +
scale_color_discrete(labels = c('theta1', 'theta2')) +
theme(legend.position = 'bottom', legend.title = element_blank())
The draws don’t seem to be independent. As we saw in the
visualization of NUTS, picking the next draw uniformly at random may
take us very close to the starting point of the trajectory, which can
partly explain the correlation between the draws. A better drawing
scheme would be to add a larger weight to points far away from the
starting point.
LS0tCnRpdGxlOiAiQmF5ZXNpYW4gZGF0YSBhbmFseXNpcyBkZW1vIDEyLjEiCmF1dGhvcjogIkpvb25hcyBMYWFrc29uZW4sIEFraSBWZWh0YXJpIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCkpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogcmVhZGFibGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKYmlibGlvZ3JhcGh5OiBudXRzLmJpYgpub2NpdGU6IHwKICBAQkRBMwotLS0KCiMjIFRoZSBOby1VLVR1cm4gU2FtcGxlciAoTlVUUykgLyBEeW5hbWljIEhhbWlsdG9uaWFuIE1vbnRlIENhcmxvCgpUaGUgSGFtaWx0b25pYW4gTW9udGUgQ2FybG8sIGhlbmNlZm9ydGggSE1DLCBlbmFibGVzIGVmZmljaWVudCBleHBsb3JhdGlvbiBvZiBjb21wbGV4IGFuZCBjb25maW5lZCB0YXJnZXQgZGlzdHJpYnV0aW9ucywgYnV0IGl0cyBwZXJmb3JtYW5jZSBpcyBsYXJnZWx5IGdvdmVybmVkIGJ5IHRoZSBzdGVwIHNpemUgJFxlcHNpbG9uJCBhbmQgbnVtYmVyIG9mIHN0ZXBzICRMJCB1c2VkIGluIHNpbXVsYXRpbmcgdGhlIEhhbWlsdG9uaWFuIHRyYWplY3Rvcmllcy4gRGVwZW5kaW5nIG9uIGhvdyBsYXJnZSAkTCQgaXMsIHRvbyBzbWFsbCBhIHN0ZXAgc2l6ZSBtYXkgdW5kdWx5IGluY3JlYXNlIHRoZSBjb21wdXRhdGlvbiB0aW1lIG9yIGxlYWQgdG8gZXhwbG9yYXRpb24gb2YgdGhlIHRhcmdldCBkaXN0cmlidXRpb24gdGhhdCByZXNlbWJsZXMgcHVyZSByYW5kb20gd2Fsaywgd2hpY2ggd2Ugd2FudCB0byBhdm9pZCB3aGVuIHVzaW5nIEhNQy4gVG9vIGxhcmdlIGEgc3RlcCBzaXplIG1ha2VzIHRoZSBlcnJvciBleHBsb2RlIGluIHNpbXVsYXRpbmcgdGhlIEhhbWlsdG9uaWFuIHRyYWplY3RvcnksIHdoaWNoIHJlc3VsdHMgaW4gbG93IGFjY2VwdGFuY2UgcmF0ZXMgYW5kIHBvb3IgZXhwbG9yYXRpb24uIEFzc3VtaW5nIHRoYXQgd2UgaGF2ZSBmb3VuZCBhIHN0YWJsZSB2YWx1ZSBmb3IgJFxlcHNpbG9uJCwgZmluZGluZyBhIGdvb2QgdmFsdWUgZm9yICRMJCBpcyBub3QgZWFzeSBpbiBnZW5lcmFsLiBXZSB3b3VsZCBsaWtlICRMJCB0byBiZSBsYXJnZSBlbm91Z2ggc28gdGhhdCB0aGUgdHJhamVjdG9yeSB0cmF2ZWxzIGxvbmcgZW5vdWdoIHRocm91Z2ggdGhlIHRhcmdldCBkaXN0cmlidXRpb24geWllbGRpbmcgYWxtb3N0IHVuY29ycmVsYXRlZCBkcmF3cy4gSG93ZXZlciwgaWYgJEwkIGlzIHRvbyBsYXJnZSwgdGhlbiBkdWUgdG8gSGFtaWx0b25pYW4gZHluYW1pY3MsIHRoZSB0cmFqZWN0b3J5IHN0YXJ0cyB0byByb3RhdGUgdG93YXJkcyB0aGUgc3RhcnRpbmcgcG9pbnQgYW5kIGV2ZW50dWFsbHkgbWFrZXMgYSBmdWxsIGN5Y2xlIHJlc3VsdGluZyBpbiB3YXN0ZWQgY29tcHV0YXRpb24gdGltZSwgYW5kIGV2ZW4gd29yc2UsIHRoZSB0cmFqZWN0b3J5IGNhbiBzdG9wIG5lYXIgdGhlIHN0YXJ0aW5nIHBvaW50LiBUb28gZmV3IHN0ZXBzIGNhbiBhbHNvIGRpbHV0ZSB0aGUgZXhwbG9yYXRpb24gdG8gcmFuZG9tIHdhbGsuCgpJbiB0aGUgc3RhdGljIHZlcnNpb24gb2YgSE1DIHRoZSB1c2VyIG5lZWRzIHRvIGhhbmQtdHVuZSB0aGUgcGFyYW1ldGVycyBlaXRoZXIgYnkgdmlzdWFsaXppbmcgdGhlIHRyYWplY3RvcmllcywgcGVyZm9ybWluZyBzZXZlcmFsIHByZWxpbWluYXJ5IHJ1bnMsIGFzc2Vzc2luZyB0aGUgY29udmVyZ2VuY2Ugb2YgdGhlIGNoYWlucyBvciwgbW9yZSBwcmVmZXJhYmx5LCBhIGNvbWJpbmF0aW9uIG9mIHRoZXNlLiBGb3IgbWFueSBkaXN0cmlidXRpb25zLCBob3dldmVyLCBvcHRpbWFsIHBhcmFtZXRlciB2YWx1ZXMgaW4gb25lIHJlZ2lvbiBtYXkgYmUgc3Vib3B0aW1hbCBpbiBzb21lIG90aGVyIHJlZ2lvbi4gRm9yIGV4YW1wbGUsIGlmIHRoZSB0YXJnZXQgZGlzdHJpYnV0aW9uIGlzIGNvbnN0cmFpbmVkIGluIHNvbWUgZGlyZWN0aW9ucywgY29uc2lkZXIgZS5nLiBoZWF2aWx5IGNvcnJlbGF0ZWQgcmFuZG9tIHZhcmlhYmxlcywgdGhlbiB3ZSB3b3VsZCBsaWtlIHRvIHVzZSBsb25nIHRyYWplY3RvcmllcyBpbiB0aGUgbGVzcyBjb25zdHJhaW5lZCBkaXJlY3Rpb25zLiBBbHNvLCB3aGVuIHRoZSBudW1iZXIgb2YgcGFyYW1ldGVycyBncm93cywgdmlzdWFsaXppbmcgYW5kIGludGVycHJldGluZyB0aGUgdHJhamVjdG9yaWVzIGJlY29tZXMgbW9yZSBhbmQgbW9yZSBkaWZmaWN1bHQuIEZvcnR1bmF0ZWx5LCBzZXR0aW5nIHRoZSBwYXJhbWV0ZXJzIHZpYSB0cmlhbCBhbmQgZXJyb3IgY2FuIGJlIGF2b2lkZWQgZm9yIGJvdGggJFxlcHNpbG9uJCBhbmQgJEwkLCBhbmQgdGhleSBjYW4gYmUgYWRhcHRlZCBkeW5hbWljYWxseSBpbnN0ZWFkLiBJbiB0aGlzIGRlbW8sIGhvd2V2ZXIsIHdlIGFyZSBvbmx5IGNvbnNpZGVyaW5nIHRoZSBkeW5hbWljIGFkYXB0YXRpb24gb2YgdGhlIG51bWJlciBvZiBzdGVwcyAkTCQuCgpXZSB3b3VsZCBsaWtlIHRvIGZpbmQgYW4gYWxnZWJyYWljIHJlcHJlc2VudGF0aW9uIGZvciB0aGUgY3JpdGVyaW9uIG9mIGhhdmluZyBzaW11bGF0ZWQgdGhlIHRyYWplY3RvcnkgbG9uZyBlbm91Z2guIFRoZSB0cmFqZWN0b3J5IGlzIGNvbnNpZGVyZWQgdG8gYmUgImxvbmcgZW5vdWdoIiB3aGVuIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIHRoZSBzdGFydGluZyBwb2ludCBhbmQgdGhlIHByb3Bvc2FsIGlzIG1heGltaXplZC4gTGV0ICRcdGhldGEkIGJlIHRoZSBzdGFydGluZyBwb2ludCBvZiB0aGUgc2ltdWxhdGlvbi4gTm93IGEgbmF0dXJhbCBvcHRpb24gd291bGQgYmUgdG8gc2ltdWxhdGUgZGV0ZXJtaW5pc3RpY2FsbHkgYXMgaW4gdGhlIHN0YXRpYyBITUMgKEJEQTMgcC4gMzAwKSB1bnRpbCB3ZSBhcmUgYXQgYSBwb3NpdGlvbi1tb21lbnR1bSBwYWlyICQoXHRpbGRle1x0aGV0YX0sIFx0aWxkZXtccGhpfSkkIGZvciB3aGljaCBpdCBob2xkcyB0aGF0CgokJAooXHRpbGRle1x0aGV0YX0gLSBcdGhldGEpXlQgXHRpbGRle1xwaGl9IDwgMAokJAoKVGhhdCBpcywgd2Ugc2hvdWxkIHN0b3AgdGhlIHNpbXVsYXRpb24gd2hlbiB0aGUgYW5nbGUgYmV0d2VlbiB0aGUgY3VycmVudCBtb21lbnR1bSAkXHRpbGRle1xwaGl9JCBhbmQgdGhlIHZlY3RvciBmcm9tIHRoZSBzdGFydGluZyBwb2ludCAkXHRoZXRhJCB0byB0aGUgY3VycmVudCBwb2ludCAkXHRpbGRle1x0aGV0YX0kIGlzIGxlc3MgdGhhbiA5MCBkZWdyZWVzLiBUaGlzIGNyaXRlcmlvbiBjb3JyZXNwb25kcyB0byBzdG9wcGluZyB3aGVuIHRoZSB0cmFqZWN0b3J5IHN0YXJ0cyB0byByb3RhdGUgdG93YXJkcyB0aGUgc3RhcnRpbmcgcG9pbnQgYW5kIHRoZSBkaXN0YW5jZSBiZXR3ZWVuICRcdGhldGEkIGFuZCAkXHRpbGRle1x0aGV0YX0kIHN0YXJ0cyB0byBkZWNyZWFzZS4gVW5mb3J0dW5hdGVseSwgdGhlIGFib3ZlIHNjaGVtZSBkb2VzIG5vdCBndWFyYW50ZWUgcmV2ZXJzaWJpbGl0eSBvZiB0aGUgTWFya292IGNoYWluLCBhbmQgdGh1cyBpdCBpcyBub3QgZ3VhcmFudGVlZCB0byBjb252ZXJnZSB0byB0aGUgZGVzaXJlZCB0YXJnZXQgZGlzdHJpYnV0aW9uLiBUaGUgcmV2ZXJzaWJpbGl0eSBjYW4gYmUgcmVtZWRpYXRlZCBieSBtYWtpbmcgdGhlIHNpbXVsYXRpb24gcHJvY2VzcyByYW5kb20gYnkgYWxsb3dpbmcgc3RlcHMgYm90aCBmb3J3YXJkIGFuZCBiYWNrd2FyZCBpbiB0aW1lLiBUaGlzIGlzIGluY29ycG9yYXRlZCBpbiB0aGUgTlVUUyBhbGdvcml0aG0gW0Bob2ZmbWFuXSB3aGljaCB3ZSB3aWxsIHByZXNlbnQgbmV4dC4gRnVydGhlciB2YXJpYW50cyBvZiBkeW5hbWljIEhNQyBhcmUgZGVzY3JpYmVkIGJ5IEBiZXRhbmNvdXJ0LiBUaGUgc3BlY2lmaWMgYWxnb3JpdGhtIGFuZCBpbXBsZW1lbnRhdGlvbiBkZXRhaWxzIG9mIGR5bmFtaWMgSE1DIGluIFN0YW4gc2xpZ2h0bHkgZGlmZmVyIGZyb20gdGhlIG9uZXMgZGVzY3JpYmVkIGJ5IEBob2ZmbWFuIGFuZCBAYmV0YW5jb3VydCwgYW5kIHVuZm9ydHVuYXRlbHkgdGhlcmUgaXMgbm8gZGV0YWlsZWQgZGVzY3JpcHRpb24gb2YgdGhlIGRpZmZlcmVuY2VzIGF2YWlsYWJsZSBleGNlcHQgaW4gdGhlIEMrKyBjb2RlLiBUaGUgYWxnb3JpdGhtIHByZXNlbnRlZCBoZXJlIGZvbGxvd3MgdGhlIG9yaWdpbmFsIE5VVFMgYWxnb3JpdGhtIGJ5IEBob2ZmbWFuLgoKQXNzdW1lIHRoYXQgJHAoXHRoZXRhIHwgeSkkIGlzIHRoZSB0YXJnZXQgZGlzdHJpYnV0aW9uIGFuZCAkXHRoZXRhXnt0fSQgaXMgdGhlIGN1cnJlbnQgZHJhdy4gTGV0ICRcZXBzaWxvbiA+IDAkIGJlIHRoZSBzdGVwIHNpemUgZm9yIHRoZSBsZWFwZnJvZyBzdGVwcy4gVGhlIE5VVFMgYWxnb3JpdGhtIGZvciBkcmF3aW5nICRcdGhldGFee3QrMX0kIGNvbnNpc3RzIG9mIHRoZSBmb2xsb3dpbmcgc3RlcHMuCgoxLiAgU2FtcGxlIGEgbW9tZW50dW0gdmVjdG9yICRccGhpIFxzaW0gTigwLCBNKSQsIHdoZXJlICRNJCBpcyBhIHN5bW1ldHJpYyBwb3NpdGl2ZS1kZWZpbml0ZSBtYXNzIG1hdHJpeC4gVGhpcyBzdGVwIGlzIGlkZW50aWNhbCB0byB0aGUgZmlyc3Qgc3RlcCBpbiBzdGF0aWMgSE1DLgoKMi4gIFNhbXBsZSBhIHNsaWNlIHZhcmlhYmxlICR1IHwgXHRoZXRhXnQsIFxwaGkgXHNpbSBcdGV4dHtVbmlmb3JtfShbMCwgZV57LUgoXHRoZXRhXnQsIFxwaGkpfV0pJCwgd2hlcmUgJEgoXHRoZXRhLCBccGhpKSA9IC1cbG9nKHAoXHRoZXRhIHwgeSkpICsgXGZyYWN7MX17Mn0gXHBoaV5UIE1eey0xfSBccGhpJCBpcyB0aGUgSGFtaWx0b25pYW4gZnVuY3Rpb24gdGhhdCBzcGVjaWZpZXMgdGhlICJ0b3RhbCBlbmVyZ3kiIGF0IHRoZSBwb3NpdGlvbi1tb21lbnR1bSBwYWlyICQoXHRoZXRhLCBccGhpKSQuCgozLiAgR2VuZXJhdGUgYSBzZXQgJEMkIG9mIHByb3Bvc2FsIHBvaW50cyBhcyBmb2xsb3dzLiBTZXQgJEMgPSBceyBcdGhldGFedCBcfSQuIFN0YXJ0aW5nIHRoZSBIYW1pbHRvbmlhbiBzaW11bGF0aW9uIGZyb20gJChcdGhldGFedCwgXHBoaSkkLCBmb3IgJGo9MCwxLDIsLi4uJCwKCiAgICAtICAgU2FtcGxlICR2X2ogXHNpbSBcdGV4dHtVbmlmb3JtfShceyAtMSwgMSBcfSkkIGFuZCBwZXJmb3JtICQyXmokIGxlYXBmcm9nIHN0ZXBzIHdpdGggdGhlIHN0ZXAgc2l6ZSAkdl9qIFxlcHNpbG9uJCwgYWx3YXlzIGFwcGVuZGluZyB0byB0aGUgdHJhamVjdG9yeSBmcm9tIHRoZSBwcmV2aW91cyBpdGVyYXRpb24uCgogICAgLSAgIFRoZSBuZXdseSBnZW5lcmF0ZWQgJDJeaiQgcG9pbnRzIGFyZSBlcXVpdmFsZW50IHRvIHRoZSBsZWFmIG5vZGVzIG9mIGEgcGVyZmVjdCwgb3JkZXJlZCBiaW5hcnkgdHJlZSB3aXRoIGRlcHRoICRqJCwgc2VlIEZpZ3VyZSAxIGJlbG93LiBOb3cgY29uc2lkZXIgZWFjaCBvZiB0aGUgJDJeai0xJCBzdWJ0cmVlcyB0aGF0IGhhdmUgZGVwdGggZ3JlYXRlciB0aGFuICQwJCwgYW5kIGZvciBhIGdpdmVuIHN1YnRyZWUgZGVub3RlIHRoZSBwb2ludHMgYXNzb2NpYXRlZCB3aXRoIHRoZSBsZWZ0bW9zdCBhbmQgcmlnaHRtb3N0IGxlYXZlcyBieSAkKFx0aGV0YV4tLCBccGhpXi0pJCBhbmQgJChcdGhldGFeKywgXHBoaV4rKSQsIHJlc3BlY3RpdmVseS4gSWYgZm9yIHNvbWUgc3VidHJlZSBpdCBob2xkcyB0aGF0CgogICAgICAgICQkCiAgICAgICAgKFx0aGV0YV4rIC0gXHRoZXRhXi0pXlQgXHBoaV4tIDwgMAogICAgICAgIFxxdWFkIFx0ZXh0e29yfSBccXVhZAogICAgICAgIChcdGhldGFeKyAtIFx0aGV0YV4tKV5UIFxwaGleKyA8IDAsCiAgICAgICAgJCQKCiAgICAgICAgdGhlbiB0ZXJtaW5hdGUgdGhlIHNpbXVsYXRpb24gbG9vcC4gSWYgZm9yIHNvbWUgcG9pbnQgJChcdGhldGEsIFxwaGkpJCBmcm9tIHRoZSBuZXcgJDJeaiQgcG9pbnRzIGl0IGhvbGRzIHRoYXQKCiAgICAgICAgJCQKICAgICAgICBIKFx0aGV0YSwgXHBoaSkgKyBcbG9nIHUgPiBcRGVsdGFfe1x0ZXh0e21heH19CiAgICAgICAgJCQKCiAgICAgICAgZm9yIHNvbWUgbm9ubmVnYXRpdmUgJFxEZWx0YV97XHRleHR7bWF4fX0kLCB0aGVuIGFsc28gdGVybWluYXRlIHRoZSBzaW11bGF0aW9uIGxvb3AuIFdlIHdpbGwgdXNlIHRoZSB2YWx1ZSAkXERlbHRhX3tcdGV4dHttYXh9fSA9IDEwMDAkLgoKICAgIC0gICBJZiB0aGUgZm9ybWVyIHN0b3BwaW5nIGNyaXRlcmlvbiBhYm92ZSBob2xkcyBmb3IgdGhlIGxlZnRtb3N0IGFuZCByaWdodG1vc3QgcG9pbnRzIG9mIHRoZSBmdWxsIHNpbXVsYXRlZCB0cmFqZWN0b3J5LCB0aGVuIGZvciBhbGwgJChcdGhldGEsIFxwaGkpJCBvZiB0aGUgbmV3bHkgc2ltdWxhdGVkICQyXmokIHBvaW50cywgYWRkICRcdGhldGEkIHRvICRDJCBpZiAkZV57LUgoXHRoZXRhLCBccGhpKX0gXGdlcSB1JC4gRmluYWxseSwgdGVybWluYXRlIHRoZSBzaW11bGF0aW9uIGxvb3AuCgogICAgLSAgIElmIG5vIHN0b3BwaW5nIGNyaXRlcmlvbiB3YXMgbWV0LCB0aGVuIGZvciBhbGwgJChcdGhldGEsIFxwaGkpJCBvZiB0aGUgbmV3IHBvaW50cywgYWRkICRcdGhldGEkIHRvICRDJCBpZiAkZV57LUgoXHRoZXRhLCBccGhpKX0gXGdlcSB1JCwgYW5kIG1vdmUgb24gdG8gdGhlIG5leHQgaXRlcmF0aW9uLgoKNC4gIFNhbXBsZSBhIHZhbHVlIGZvciAkXHRoZXRhXnt0KzF9JCB1bmlmb3JtbHkgZnJvbSAkQyQuCgpgYGB7dGlreiwgZmlnLmNhcD0iRmlndXJlIDE6IFRoZSBuZXcgcG9pbnRzIHJlc3VsdGluZyBmcm9tIHRoZSAkMl5qJCAoaGVyZSAkaj0yJCkgbGVhcGZyb2cgc3RlcHMgYXJlIGVxdWl2YWxlbnQgdG8gdGhlIGxlYWYgbm9kZXMgb2YgYSBwZXJmZWN0LCBvcmRlcmVkIGJpbmFyeSB0cmVlIHdpdGggZGVwdGggJGokLiBUaGUgc3RvcHBpbmcgY29uZGl0aW9ucyBhcmUgY2hlY2tlZCBmb3IgYWxsIHRoZSBsZWF2ZXMgYW5kIGFsbCB0aGUgJDJeai0xJCBzdWJ0cmVlcyB3aXRoIGRlcHRoIGdyZWF0ZXIgdGhhbiAkMCQuIE5vdGUgdGhhdCB0aGUgaW50ZXJpb3Igbm9kZXMgYXJlIG5vdCBpbXBvcnRhbnQuIiwgZmlnLmFsaWduPSJjZW50ZXIiLCBvdXQud2lkdGg9IjQwMHB4IiwgZmlnLmV4dCA9ICdzdmcnLCBlY2hvPUZBTFNFfQolIHJlcXVpcmVzIHRpbnl0ZXgsIG1hZ2ljaywgYW5kIHBkZnRvb2xzIHBhY2thZ2VzClxiZWdpbnt0aWt6cGljdHVyZX1bCiAgaW50Ly5zdHlsZT17Y2lyY2xlLCBmaWxsPWdyYXksIG1pbmltdW0gc2l6ZT0xMHB0fSwKICBsZWFmLy5zdHlsZT17Y2lyY2xlLCBmaWxsPWJsdWUsIG1pbmltdW0gc2l6ZT0xMHB0fSwKICBsZXZlbCAxLy5zdHlsZT17c2libGluZyBkaXN0YW5jZT03NXB0fSwKICBsZXZlbCAyLy5zdHlsZT17c2libGluZyBkaXN0YW5jZT0zOHB0fQpdClxub2RlW2ludF0ge30KICBjaGlsZHtub2RlW2ludF0ge30KICAgIGNoaWxke25vZGVbbGVhZl0ge319CiAgICBjaGlsZHtub2RlW2xlYWZdIHt9fQogIH0KICBjaGlsZHtub2RlW2ludF0ge30KICAgIGNoaWxke25vZGVbbGVhZl0ge319CiAgICBjaGlsZHtub2RlW2xlYWZdIHt9fQogIH07ClxlbmR7dGlrenBpY3R1cmV9CmBgYAoKSW4gc3VtbWFyeSwgYWZ0ZXIgZWFjaCBpdGVyYXRpb24gdGhlIG51bWJlciBvZiBsZWFwZnJvZyBzdGVwcyBpcyBkb3VibGVkLCBhbmQgdGhlIHNpbXVsYXRpb24gaXMgc3RvcHBlZCB3aGVuZXZlciBhbnkgc3VidHJhamVjdG9yeSBlcXVpdmFsZW50IHRvIGEgYmluYXJ5IHN1YnRyZWUgc2F0aXNmaWVzIG9uZSBvZiB0aGUgYWJvdmUgc3RvcHBpbmcgY3JpdGVyaWEuIFRoZSBzbGljZSB2YXJpYWJsZSAkdSQgaXMgY2hvc2VuIHNvIHRoYXQgJFx0aGV0YV57dCsxfSQgY2FuIGJlIGRyYXduIHVuaWZvcm1seSBmcm9tICRDJC4gTm90ZSB0aGF0IGlmIHdlIHdlcmUgYWJsZSB0byBzaW11bGF0ZSB0aGUgSGFtaWx0b25pYW4gdHJhamVjdG9yeSBleGFjdGx5LCAkSChcdGhldGEsIFxwaGkpJCB3b3VsZCByZW1haW4gY29uc3RhbnQgb3ZlciB0aGUgd2hvbGUgdHJhamVjdG9yeSwgYW5kICRlXnstSChcdGhldGEsIFxwaGkpfSBcZ2VxIHUkIHdvdWxkIGhvbGQgZm9yIGFsbCBwb2ludHMgb24gdGhlIHRyYWplY3RvcnkuIFRodXMsIHRoZSBjb25kaXRpb24gJGVeey1IKFx0aGV0YSwgXHBoaSl9IFxnZXEgdSQgY2FuIGJlIGludGVycHJldGVkIGFzIGRpc2NhcmRpbmcgYWxsIHBvaW50cyBmb3Igd2hpY2ggdGhlIHNpbXVsYXRpb24gZXJyb3IgaXMgdG9vIGxhcmdlLiBUaGUgc3RvcHBpbmcgY29uZGl0aW9uICRIKFx0aGV0YSwgXHBoaSkgKyBcbG9nIHUgPiBcRGVsdGFfe1x0ZXh0e21heH19JCBpbiBzdGVwIDMgaXMgYWxzbyByZWxhdGVkIHRvIHRlcm1pbmF0aW5nIHRoZSBzaW11bGF0aW9uIGlmIHRoZSBzaW11bGF0aW9uIGVycm9yIGJlY29tZXMgdG9vIGxhcmdlLiBOb3RlIHRoYXQgdGhlIG51bWJlciBvZiBsZWFwZnJvZyBzdGVwcyBpbmNyZWFzZXMgZXhwb25lbnRpYWxseSwgYnV0IGFzIGxvbmcgYXMgJFxlcHNpbG9uJCBpcyBzbWFsbCBlbm91Z2ggYW5kIHRoZSB0YXJnZXQgZGlzdHJpYnV0aW9uICRwKFx0aGV0YSB8IHkpJCBpcyBzdWZmaWNpZW50bHkgd2VsbC1iZWhhdmVkLCB0aGUgc2ltdWxhdGlvbiBlcnJvciBzaG91bGRuJ3QgaW5jcmVhc2UgaW4gdGhlIG51bWJlciBvZiBsZWFwZnJvZyBzdGVwcywgd2hpY2ggbWFrZXMgdGhlIGFib3ZlIGR5bmFtaWMgc2NoZW1lIGF0dHJhY3RpdmUuIEFzIHVzdWFsIHdpdGggSE1DLCB0aGUgbGVhcGZyb2cgc3RlcHMgcmVxdWlyZSB0aGUgZ3JhZGllbnQgb2YgdGhlIGxvZy1kZW5zaXR5LCBpLmUuICRcZnJhY3tkIFxsb2cgcChcdGhldGEgfCB5KX17ZFx0aGV0YX0kLgoKIyMgSW1wbGVtZW50YXRpb24gYW5kIHZpc3VhbGl6YXRpb24gb2YgTlVUUwoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGxhdGV4MmV4cCkKbGlicmFyeShnZ3Bsb3QyKQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KE1BU1MpCmxpYnJhcnkoZ2dhbmltYXRlKQpsaWJyYXJ5KHBvc3RlcmlvcikKYGBgCgpOZXh0IHdlIHdpbGwgaW1wbGVtZW50IHRoZSBOVVRTIGFsZ29yaXRobSBhbmQgdmlzdWFsaXplIGhvdyB0aGUgdHJhamVjdG9yaWVzIGxvb2sgbGlrZS4gVGhlIGZvbGxvd2luZyBzdWJyb3V0aW5lcyB3aWxsIGJlIHVzZWZ1bCBpbiB0aGUgaW1wbGVtZW50YXRpb24uCgpgYGB7cn0KIyBQZXJmb3JtIGEgc2luZ2xlIGxlYXBmcm9nIHN0ZXAuCmxlYXBmcm9nIDwtIGZ1bmN0aW9uKHRoZXRhLCBwaGksIGVwc2lsb24sIGdyYWRfbG9nX3AsIE1faW52KSB7CiAgcGhpX3RpbGRlIDwtIHBoaSArIDAuNSAqIGVwc2lsb24gKiBncmFkX2xvZ19wKHRoZXRhKQogIHRoZXRhX3RpbGRlIDwtIHRoZXRhICsgZXBzaWxvbiAqIE1faW52ICUqJSBwaGlfdGlsZGUKICBwaGlfdGlsZGUgPC0gcGhpX3RpbGRlICsgMC41ICogZXBzaWxvbiAqIGdyYWRfbG9nX3AodGhldGFfdGlsZGUpCiAgbGlzdCh0aGV0YT1jKHRoZXRhX3RpbGRlKSwgcGhpPWMocGhpX3RpbGRlKSkKfQoKIyBFdmFsdWF0ZSB0aGUgSGFtaWx0b25pYW4uCmhhbWlsdG9uaWFuIDwtIGZ1bmN0aW9uKHRoZXRhLCBwaGksIGxvZ19wLCBNX2ludikgewogIC1sb2dfcCh0aGV0YSkgKyAwLjUgKiBzdW0ocGhpICogKE1faW52ICUqJSBwaGkpKQp9CmBgYAoKVGhlIHN0ZXAgMyBvZiB0aGUgTlVUUyBhbGdvcml0aG0gYWJvdmUgaXMgdGhlIHRyaWNraWVzdCB0byBpbXBsZW1lbnQuIEluIHByYWN0aWNlLCBpbnN0ZWFkIG9mIGNvbnNpZGVyaW5nIGFsbCB0aGUgc3VidHJlZXMgb25seSBhZnRlciBzaW11bGF0aW5nIHRoZSAkMl5qJCBsZWFwZnJvZyBzdGVwcywgaXQncyBlYXNpZXIgdG8gcGVyZm9ybSB0aGUgc3RlcHMgYW5kIGNvbnN0cnVjdCB0aGUgdHJlZXMgcmVjdXJzaXZlbHkgYXQgdGhlIHNhbWUgdGltZS4KCmBgYHtyfQojIFBlcmZvcm0gdGhlIGxlYXBmcm9nIHN0ZXBzIGFuZCBjb25zdHJ1Y3QgdGhlIHN1YnRyZWVzIHJlY3Vyc2l2ZWx5LgpidWlsZF90cmVlIDwtIGZ1bmN0aW9uKHRoZXRhLCBwaGksIHUsIHYsIGosIGVwc2lsb24sIGxvZ19wLCBncmFkX2xvZ19wLCBNX2ludiwgdHJhamVjdG9yeSkgewogIGlmIChqID09IDApIHsKICAgICMgQmFzZSBjYXNlLiBUYWtlIGp1c3Qgb25lIGxlYXBmcm9nIHN0ZXAuCiAgICBzdGVwIDwtIGxlYXBmcm9nKHRoZXRhLCBwaGksIHYqZXBzaWxvbiwgZ3JhZF9sb2dfcCwgTV9pbnYpCiAgICB0aGV0YSA8LSBzdGVwJHRoZXRhCiAgICBwaGkgPC0gc3RlcCRwaGkKICAgIAogICAgIyBDb2xvciB0aGUgbmV3IHRoZXRhIHJlZCBpZiBpdCdzIGNlcnRhaW5seSBub3QgYSB2YWxpZCBwcm9wb3NhbCwgYW5kCiAgICAjIGNvbG9yIGl0IHllbGxvdyBvdGhlcndpc2UuCiAgICBpZiAodSA8PSBleHAoLWhhbWlsdG9uaWFuKHRoZXRhLCBwaGksIGxvZ19wLCBNX2ludikpKSB7CiAgICAgIGNvbG9yIDwtICJ5ZWxsb3ciCiAgICB9IGVsc2UgewogICAgICBjb2xvciA8LSAicmVkIgogICAgfQogICAgCiAgICAjIFVwZGF0ZSB0aGUgdHJhamVjdG9yeS4KICAgIGlmICh2ID09IC0xKSB7CiAgICAgIHRyYWplY3RvcnkkdGhldGFzIDwtIHJiaW5kKHRoZXRhLCB0cmFqZWN0b3J5JHRoZXRhcykKICAgICAgdHJhamVjdG9yeSRjb2xvcnMgPC0gYyhjb2xvciwgdHJhamVjdG9yeSRjb2xvcnMpCiAgICB9IGVsc2UgewogICAgICB0cmFqZWN0b3J5JHRoZXRhcyA8LSByYmluZCh0cmFqZWN0b3J5JHRoZXRhcywgdGhldGEpCiAgICAgIHRyYWplY3RvcnkkY29sb3JzIDwtIGModHJhamVjdG9yeSRjb2xvcnMsIGNvbG9yKQogICAgfQogICAgCiAgICAjIENoZWNrIHRoZSBzZWNvbmQgc3RvcHBpbmcgY29uZGl0aW9uIGluIHN0ZXAgMy4gV2UgdXNlIERlbHRhX21heD0xMDAwLgogICAgdGVybWluYXRlIDwtIGhhbWlsdG9uaWFuKHRoZXRhLCBwaGksIGxvZ19wLCBNX2ludikgKyBsb2codSkgPiAxMDAwCiAgICAKICAgIHJldHVybihsaXN0KHRoZXRhX21pbnVzPXRoZXRhLCBwaGlfbWludXM9cGhpLCB0aGV0YV9wbHVzPXRoZXRhLCBwaGlfcGx1cz1waGksCiAgICAgICAgICAgICAgICB0cmFqZWN0b3J5PXRyYWplY3RvcnksIHRlcm1pbmF0ZT10ZXJtaW5hdGUpKQogIH0gZWxzZSB7CiAgICAjIEJ1aWxkIHRoZSBsZWZ0IGFuZCByaWdodCBzdWJ0cmVlcyByZWN1cnNpdmVseS4KICAgIHRyZWUxIDwtIGJ1aWxkX3RyZWUodGhldGEsIHBoaSwgdSwgdiwgai0xLCBlcHNpbG9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgbG9nX3AsIGdyYWRfbG9nX3AsIE1faW52LCB0cmFqZWN0b3J5KQogICAgdGhldGFfbWludXMgPC0gdHJlZTEkdGhldGFfbWludXMKICAgIHBoaV9taW51cyA8LSB0cmVlMSRwaGlfbWludXMKICAgIHRoZXRhX3BsdXMgPC0gdHJlZTEkdGhldGFfcGx1cwogICAgcGhpX3BsdXMgPC0gdHJlZTEkcGhpX3BsdXMKICAgIGlmICh2ID09IC0xKSB7CiAgICAgIHRyZWUyIDwtIGJ1aWxkX3RyZWUodGhldGFfbWludXMsIHBoaV9taW51cywgdSwgdiwgai0xLCBlcHNpbG9uLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ19wLCBncmFkX2xvZ19wLCBNX2ludiwgdHJlZTEkdHJhamVjdG9yeSkKICAgICAgdGhldGFfbWludXMgPC0gdHJlZTIkdGhldGFfbWludXMKICAgICAgcGhpX21pbnVzIDwtIHRyZWUyJHBoaV9taW51cwogICAgfSBlbHNlIHsKICAgICAgdHJlZTIgPC0gYnVpbGRfdHJlZSh0aGV0YV9wbHVzLCBwaGlfcGx1cywgdSwgdiwgai0xLCBlcHNpbG9uLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ19wLCBncmFkX2xvZ19wLCBNX2ludiwgdHJlZTEkdHJhamVjdG9yeSkKICAgICAgdGhldGFfcGx1cyA8LSB0cmVlMiR0aGV0YV9wbHVzCiAgICAgIHBoaV9wbHVzIDwtIHRyZWUyJHBoaV9wbHVzCiAgICB9CiAgICAKICAgICMgQ2hlY2sgdGhlIFUtdHVybiBzdG9wcGluZyBjb25kaXRpb24gYW5kIGFsc28gdGFrZSBpbnRvIGFjY291bnQKICAgICMgdGhlIHN0b3BwaW5nIGZsYWdzIGZyb20gYnVpbGRpbmcgdGhlIHN1YnRyZWVzLgogICAgY29uZGl0aW9uMSA8LSBzdW0oKHRoZXRhX3BsdXMgLSB0aGV0YV9taW51cykgKiBwaGlfbWludXMpIDwgMAogICAgY29uZGl0aW9uMiA8LSBzdW0oKHRoZXRhX3BsdXMgLSB0aGV0YV9taW51cykgKiBwaGlfcGx1cykgPCAwCiAgICB0ZXJtaW5hdGUgPC0gY29uZGl0aW9uMSB8fCBjb25kaXRpb24yIHx8IHRyZWUxJHRlcm1pbmF0ZSB8fCB0cmVlMiR0ZXJtaW5hdGUKICAgIAogICAgcmV0dXJuKGxpc3QodGhldGFfbWludXM9dGhldGFfbWludXMsIHBoaV9taW51cz1waGlfbWludXMsIAogICAgICAgICAgICAgICAgdGhldGFfcGx1cz10aGV0YV9wbHVzLCBwaGlfcGx1cz1waGlfcGx1cywKICAgICAgICAgICAgICAgIHRyYWplY3Rvcnk9dHJlZTIkdHJhamVjdG9yeSwgdGVybWluYXRlPXRlcm1pbmF0ZSkpCiAgfQp9CmBgYAoKV2UgYXJlIG5vdyByZWFkeSB0byBpbXBsZW1lbnQgdGhlIHNhbXBsaW5nIGZ1bmN0aW9uLgoKYGBge3J9CiMgRHJhdyB0aGUgZ2l2ZW4gbnVtYmVyIG9mIHNhbXBsZXMgZnJvbSB0aGUgdGFyZ2V0IGRpc3RyaWJ1dGlvbiB1c2luZyBOVVRTLgojCiMgUGFyYW1ldGVyczoKIyAgIC0gdGhldGEgICAgICAgICBTdGFydGluZyBwb2ludCBvZiB0aGUgY2hhaW4uCiMgICAtIGxvZ19wICAgICAgICAgTG9nIHRhcmdldCBkaXN0cmlidXRpb24gZnVuY3Rpb24uCiMgICAtIGdyYWRfbG9nX3AgICAgR3JhZGllbnQgb2YgdGhlIGxvZyB0YXJnZXQgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uLgojICAgLSBuX2l0ZXIgICAgICAgIE51bWJlciBvZiBkcmF3cy4KIyAgIC0gTSAgICAgICAgICAgICBTeW1tZXRyaWMgcG9zaXRpdmUgZGVmaW5pdGUgbWFzcyBtYXRyaXguIERlZmF1bHQ6IGlkZW50aXR5IG1hdHJpeC4KIyAgIC0gZXBzaWxvbiAgICAgICBTdGVwIHNpemUgZm9yIHRoZSBsZWFwZnJvZyBzdGVwcy4gRGVmYXVsdDogMC4xCiMgICAtIHN0b3JlX3BhdGhzICAgV2hldGhlciB0byBzdG9yZSBhbmQgcmV0dXJuIHRoZSBpbmRpdmlkdWFsIHRyYWplY3RvcmllcwojICAgICAgICAgICAgICAgICAgIGZvciB2aXN1YWxpemF0aW9uIHB1cnBvc2VzLiBEZWZhdWx0OiBUUlVFCiMKIyBJZiBzdG9yZV9wYXRocz1UUlVFLCByZXR1cm5zIGEgbGlzdCBjb25zaXN0aW5nIG9mIHRoZSBuX2l0ZXIgZHJhd3MgYW5kIHRoZWlyCiMgY29ycmVzcG9uZGluZyB0cmFqZWN0b3JpZXMuCiMgSWYgc3RvcmVfcGF0aHM9RkFMU0UsIHJldHVybnMganVzdCB0aGUgZHJhd3MuCk5VVFMgPC0gZnVuY3Rpb24odGhldGEsIGxvZ19wLCBncmFkX2xvZ19wLCBuX2l0ZXIsIE09TlVMTCwgZXBzaWxvbj0wLjEsIHN0b3JlX3BhdGhzPVRSVUUpIHsKICBpZiAoaXMubnVsbChNKSkgewogICAgTSA8LSBkaWFnKGxlbmd0aCh0aGV0YSkpCiAgfQogIE1faW52IDwtIHNvbHZlKE0pCiAgCiAgY2hhaW4gPC0gbGlzdChkcmF3cz1tYXRyaXgobnJvdz1uX2l0ZXIrMSwgbmNvbD1sZW5ndGgodGhldGEpKSwgCiAgICAgICAgICAgICAgICB0cmFqZWN0b3JpZXM9bGlzdCgpKQogIGNoYWluJGRyYXdzWzEsXSA8LSB0aGV0YQogIAogIGZvciAoaSBpbiAxOm5faXRlcikgewogICAgIyBTdGVwIDEuIERyYXcgYSBtb21lbnR1bSB2ZWN0b3IgZnJvbSBOKDAsIE0pLgogICAgcGhpIDwtIG12cm5vcm0obj0xLCByZXAoMCwgbGVuZ3RoKHRoZXRhKSksIE0pCiAgCiAgICAjIFN0ZXAgMi4gRHJhdyBhIHNsaWNlIHZhcmlhYmxlIHUuCiAgICB1IDwtIHJ1bmlmKDEsIDAsIGV4cCgtaGFtaWx0b25pYW4oY2hhaW4kZHJhd3NbaSxdLCBwaGksIGxvZ19wLCBNX2ludikpKQogICAgCiAgICAjIFN0ZXAgMy4gUGVyZm9ybSB0aGUgbGVhcGZyb2cgc3RlcHMuIENvbG9yIHRoZSBzdGFydGluZyBwb2ludCBibGFjay4KICAgIHRoZXRhX21pbnVzIDwtIGNoYWluJGRyYXdzW2ksXQogICAgcGhpX21pbnVzIDwtIHBoaQogICAgdGhldGFfcGx1cyA8LSBjaGFpbiRkcmF3c1tpLF0KICAgIHBoaV9wbHVzIDwtIHBoaQogICAgdHJhamVjdG9yeSA8LSBsaXN0KHRoZXRhcz1tYXRyaXgodGhldGFfbWludXMsIG5yb3c9MSksIGNvbG9ycz1jKCJibGFjayIpKQogICAgaiA8LSAwCiAgICB0ZXJtaW5hdGUgPC0gRkFMU0UKICAgIHdoaWxlICghdGVybWluYXRlKSB7CiAgICAgICMgRHJhdyBhIGRpcmVjdGlvbi4KICAgICAgdiA8LSBzYW1wbGUoYygtMSwgMSksIDEpCiAgICAgIGlmICh2ID09IC0xKSB7CiAgICAgICAgdHJlZSA8LSBidWlsZF90cmVlKHRoZXRhX21pbnVzLCBwaGlfbWludXMsIHUsIHYsIGosIGVwc2lsb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ19wLCBncmFkX2xvZ19wLCBNX2ludiwgdHJhamVjdG9yeSkKICAgICAgICB0aGV0YV9taW51cyA8LSB0cmVlJHRoZXRhX21pbnVzCiAgICAgICAgcGhpX21pbnVzIDwtIHRyZWUkcGhpX21pbnVzCiAgICAgIH0gZWxzZSB7CiAgICAgICAgdHJlZSA8LSBidWlsZF90cmVlKHRoZXRhX3BsdXMsIHBoaV9wbHVzLCB1LCB2LCBqLCBlcHNpbG9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsb2dfcCwgZ3JhZF9sb2dfcCwgTV9pbnYsIHRyYWplY3RvcnkpCiAgICAgICAgdGhldGFfcGx1cyA8LSB0cmVlJHRoZXRhX3BsdXMKICAgICAgICBwaGlfcGx1cyA8LSB0cmVlJHBoaV9wbHVzCiAgICAgIH0KICAgICAgdHJhamVjdG9yeSR0aGV0YXMgPC0gdHJlZSR0cmFqZWN0b3J5JHRoZXRhcwogICAgICB0cmFqZWN0b3J5JGNvbG9ycyA8LSB0cmVlJHRyYWplY3RvcnkkY29sb3JzCiAgICAgIAogICAgICAjIElmIGEgc3RvcHBpbmcgY29uZGl0aW9uIHdhcyBtZXQsIHRoZSBuZXdseSBnZW5lcmF0ZWQgc3VidHJhamVjdG9yeQogICAgICAjIGNhbm5vdCBiZSB1c2VkIGluIHRoZSBzYW1wbGluZyBhbmQgaXMgdGh1cyBjb2xvcmVkIHJlZC4KICAgICAgaWYgKHRyZWUkdGVybWluYXRlKSB7CiAgICAgICAgZm9yIChrIGluIDE6Ml5qKSB7CiAgICAgICAgICBpZiAodiA9PSAxKSB7CiAgICAgICAgICAgIHRyYWplY3RvcnkkY29sb3JzW2xlbmd0aCh0cmFqZWN0b3J5JGNvbG9ycykrMS1rXSA8LSAicmVkIgogICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgdHJhamVjdG9yeSRjb2xvcnNba10gPC0gInJlZCIKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0KICAgICAgCiAgICAgICMgQ2hlY2sgdGhlIHN0b3BwaW5nIGNvbmRpdGlvbnMuCiAgICAgIGNvbmRpdGlvbjEgPC0gc3VtKCh0aGV0YV9wbHVzIC0gdGhldGFfbWludXMpICogcGhpX21pbnVzKSA8IDAKICAgICAgY29uZGl0aW9uMiA8LSBzdW0oKHRoZXRhX3BsdXMgLSB0aGV0YV9taW51cykgKiBwaGlfcGx1cykgPCAwCiAgICAgIHRlcm1pbmF0ZSA8LSB0cmVlJHRlcm1pbmF0ZSB8fCBjb25kaXRpb24xIHx8IGNvbmRpdGlvbjIKICAgICAgCiAgICAgIGogPC0gaiArIDEKICAgIH0KICAgIAogICAgIyBTdGVwIDQuIERyYXcgdGhlIG5leHQgc2FtcGxlIHVuaWZvcm1seSBhbW9uZ3N0IHRoZSBzdGFydGluZyBwb2ludCBhbmQKICAgICMgdGhlIHllbGxvdy1jb2xvcmVkIHBvaW50cy4gVGhlIGNob3NlbiBwb2ludCBpcyBjb2xvcmVkIGdyZWVuLgogICAgdmFsaWRfaW5kaWNlcyA8LSAxOmxlbmd0aCh0cmFqZWN0b3J5JGNvbG9ycykKICAgIHZhbGlkX2luZGljZXMgPC0gdmFsaWRfaW5kaWNlc1sodHJhamVjdG9yeSRjb2xvcnMgPT0gInllbGxvdyIpIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAodHJhamVjdG9yeSRjb2xvcnMgPT0gImJsYWNrIildCiAgICBkcmF3X2lkIDwtIHNhbXBsZSh2YWxpZF9pbmRpY2VzLCAxKQogICAgdHJhamVjdG9yeSRjb2xvcnNbZHJhd19pZF0gPC0gImdyZWVuIgogICAgY2hhaW4kZHJhd3NbaSsxLF0gPC0gdHJhamVjdG9yeSR0aGV0YXNbZHJhd19pZCxdCiAgICAKICAgIGlmIChzdG9yZV9wYXRocykgewogICAgICBjaGFpbiR0cmFqZWN0b3JpZXNbW2xlbmd0aChjaGFpbiR0cmFqZWN0b3JpZXMpKzFdXSA8LSB0cmFqZWN0b3J5CiAgICB9CiAgfQogIAogIGlmIChzdG9yZV9wYXRocykgewogICAgcmV0dXJuKGNoYWluKQogIH0gZWxzZSB7CiAgICByZXR1cm4oY2hhaW4kZHJhd3MpCiAgfQp9CmBgYAoKTGV0J3MgdGhlbiB2aXN1YWxpemUgdGhlIE5VVFMgYWxnb3JpdGhtLiBBcyB0aGUgdGFyZ2V0IGRpc3RyaWJ1dGlvbiB3ZSB1c2UgYSBiaXZhcmlhdGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiAkcChcdGhldGEgfCB5KSA9IE4oXHRoZXRhIHwgMCwgXFNpZ21hKSQsIHdoZXJlCgokJApcU2lnbWEgPSAKXGJlZ2lue2JtYXRyaXh9CjEgJiAwLjggXFwKMC44ICYgMQpcZW5ke2JtYXRyaXh9CiQkCgpTaW5jZSAkcChcdGhldGEgfCB5KSBccHJvcHRvIFxleHAoLVxmcmFjezF9ezJ9IFx0aGV0YV5UIFxTaWdtYV57LTF9IFx0aGV0YSkkLCB0aGUgbG9nLWRlbnNpdHkgaXMgZ2l2ZW4sIHVwIHRvIGFuIGFkZGl0aXZlIGNvbnN0YW50LCBieSAkXGxvZyBwKFx0aGV0YSB8IHkpID0gLVxmcmFjezF9ezJ9IFx0aGV0YV5UIFxTaWdtYV57LTF9IFx0aGV0YSQsIGFuZCB0aGUgZ3JhZGllbnQgb2YgdGhlIGxvZy1kZW5zaXR5IGlzIGdpdmVuIGJ5ICRcZnJhY3tkIFxsb2cgcChcdGhldGEgfCB5KX17ZFx0aGV0YX0gPSAtXFNpZ21hXnstMX0gXHRoZXRhJC4KCmBgYHtyfQpTaWdtYSA8LSBtYXRyaXgoYygxLCAwLjgsIDAuOCwgMSksIG5yb3c9MikKU2lnbWFfaW52IDwtIHNvbHZlKFNpZ21hKQoKbG9nX3AgPC0gZnVuY3Rpb24odGhldGEpIHsKICAtMC41ICogc3VtKHRoZXRhICogKFNpZ21hX2ludiAlKiUgdGhldGEpKQp9CgpncmFkX2xvZ19wIDwtIGZ1bmN0aW9uKHRoZXRhKSB7CiAgLVNpZ21hX2ludiAlKiUgdGhldGEKfQpgYGAKCkxldCAkXHRoZXRhXjAgPSAoLTIuNSwgMi41KSQgYmUgdGhlIHN0YXJ0aW5nIHBvaW50LiBTaW11bGF0ZSAyMDAwIGRyYXdzLgoKYGBge3J9CiMgRHJhdyBzYW1wbGVzIGZyb20gdGhlIHRhcmdldCBkaXN0cmlidXRpb24gdG8gdmlzdWFsaXplIGl0cyA5MCUKIyBIUEQgcmVnaW9uIHdpdGggZ2dwbG90J3Mgc3RhdF9lbGxpcHNlIGZ1bmN0aW9uLgp0YXJnZXRfZHJhd3MgPC0gZGF0YS5mcmFtZShtdnJub3JtKDEwMDAwMCwgYygwLCAwKSwgU2lnbWEpKQoKdGhldGFfMCA8LSBjKC0yLjUsIDIuNSkKbl9pdGVyIDwtIDIwMDAKbnV0c19jaGFpbiA8LSBOVVRTKHRoZXRhXzAsIGxvZ19wLCBncmFkX2xvZ19wLCBuX2l0ZXIpCmBgYAoKVGhlIGZvbGxvd2luZyBleGFtcGxlIHNob3dzIGhvdyB0byBwbG90IGEgc2luZ2xlIHRyYWplY3RvcnkKCmBgYHtyLCBmaWcuYWxpZ249ImNlbnRlciJ9CiMgVHJ5IHRvIGZpbmQgYSB0cmFqZWN0b3J5IHdpdGggYXQgbGVhc3QgMzIgcG9pbnRzLgp0IDwtIG51dHNfY2hhaW4kdHJhamVjdG9yaWVzW1sxMDBdXQpmb3IgKGkgaW4gMTpsZW5ndGgobnV0c19jaGFpbiR0cmFqZWN0b3JpZXMpKSB7CiAgaWYgKGxlbmd0aChudXRzX2NoYWluJHRyYWplY3Rvcmllc1tbaV1dJGNvbG9ycykgPj0gMzIpIHsKICAgIHQgPC0gbnV0c19jaGFpbiR0cmFqZWN0b3JpZXNbW2ldXQogICAgYnJlYWsKICB9Cn0KZGYgPC0gZGF0YS5mcmFtZSh0aGV0YTEgICAgID0gdCR0aGV0YXNbLDFdLAogICAgICAgICAgICAgICAgIHRoZXRhMiAgICAgPSB0JHRoZXRhc1ssMl0sCiAgICAgICAgICAgICAgICAgY29sb3IgICAgICA9IHQkY29sb3JzLAogICAgICAgICAgICAgICAgIHRoZXRhMV9lbmQgPSBjKHQkdGhldGFzWzEsMV0sIHQkdGhldGFbLW5yb3codCR0aGV0YXMpLCAxXSksCiAgICAgICAgICAgICAgICAgdGhldGEyX2VuZCA9IGModCR0aGV0YXNbMSwyXSwgdCR0aGV0YVstbnJvdyh0JHRoZXRhcyksIDJdKSkKZ2dwbG90KCkgKwogIHN0YXRfZWxsaXBzZShkYXRhID0gdGFyZ2V0X2RyYXdzLCBhZXMoeCA9IFgxLCB5ID0gWDIsIGNvbG9yID0gIkhQRCIpLCBsZXZlbCA9IDAuOSkgKwogIGdlb21fc2VnbWVudChkYXRhID0gZGYsIGFlcyh4ID0gdGhldGExLCB4ZW5kID0gdGhldGExX2VuZCwgY29sb3IgPSAiZ3JheSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSB0aGV0YTIsIHllbmQgPSB0aGV0YTJfZW5kKSwgYWxwaGEgPSAwLjUpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkZiwgYWVzKHRoZXRhMSwgdGhldGEyLCBjb2xvciA9IGNvbG9yKSwgc2l6ZSA9IDIpICsgCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC00LCA0KSwgeWxpbSA9IGMoLTQsIDQpKSArIAogIGxhYnMoeCA9IFRlWCgiJFxcdGhldGFfMSQiKSwgeSA9IFRlWCgiJFxcdGhldGFfMiQiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIgPSAiYmxhY2siLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicmVkIiA9ICJyZWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAieWVsbG93IiA9ICJ5ZWxsb3czIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdyZWVuIiA9ICJncmVlbjQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSFBEIiA9ICJibHVlIiksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoImJsYWNrIiA9ICJTdGFydGluZyBwb2ludCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInJlZCIgPSAiT3V0c2lkZSBvZiB0aGUgc2xpY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ5ZWxsb3ciID0gIldpdGhpbiB0aGUgc2xpY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJncmVlbiIgPSAiRHJhdyBmcm9tIHRoZSBzbGljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkhQRCIgPSAiOTAlIEhQRCIpKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2hhcGUgPSBjKDE2LCAxNiwgMTYsIDE2LCBOQSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSBjKDAsMCwwLDAsMSkpKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKVGhlIGZvbGxvd2luZyBleGFtcGxlIGRpc3BsYXlzIGhvdyB0byBhbmltYXRlIHRoZSB0cmFqZWN0b3JpZXMuIExldCdzIGNvbnNpZGVyIHRoZSBmaXJzdCA1MCBkcmF3cy4KCmBgYHtyLCBmaWcuYWxpZ249ImNlbnRlciIsIG1lc3NhZ2U9RkFMU0V9Cm5mcmFtZXMgPC0gNTAgIyBTZXQgdGhpcyB0byB0aGUgbnVtYmVyIG9mIGl0ZXJhdGlvbnMgeW91IHdpc2ggdG8gdmlzdWFsaXplLgpkZl90cmFqZWN0b3JpZXMgPC0gZGF0YS5mcmFtZSgpCmRmX2RyYXdzIDwtIGRhdGEuZnJhbWUoKQpmb3IgKGkgaW4gMTpuZnJhbWVzKSB7CiAgdCA8LSBudXRzX2NoYWluJHRyYWplY3Rvcmllc1tbaV1dCiAgZGZfdHJhamVjdG9yaWVzIDwtIHJiaW5kKGRmX3RyYWplY3RvcmllcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QocmVwKGksIG5yb3codCR0aGV0YXMpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0JHRoZXRhc1ssMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdCR0aGV0YXNbLDJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHQkY29sb3JzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGModCR0aGV0YXNbMSwxXSwgdCR0aGV0YVstbnJvdyh0JHRoZXRhcyksIDFdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKHQkdGhldGFzWzEsMl0sIHQkdGhldGFbLW5yb3codCR0aGV0YXMpLCAyXSkpKQogIGRmX2RyYXdzIDwtIHJiaW5kKGRmX2RyYXdzLCBsaXN0KHJlcChpLCBpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudXRzX2NoYWluJGRyYXdzWzE6aSwgMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnV0c19jaGFpbiRkcmF3c1sxOmksIDJdKSkKfQpuYW1lcyhkZl90cmFqZWN0b3JpZXMpIDwtIGMoIml0ZXIiLCAidGhldGExIiwgInRoZXRhMiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvbG9yIiwgInRoZXRhMV9lbmQiLCAidGhldGEyX2VuZCIpCm5hbWVzKGRmX2RyYXdzKSA8LSBjKCJpdGVyIiwgInRoZXRhMSIsICJ0aGV0YTIiKQoKcCA8LSBnZ3Bsb3QoKSArCiAgc3RhdF9lbGxpcHNlKGRhdGEgPSB0YXJnZXRfZHJhd3MsIGFlcyh4ID0gWDEsIHkgPSBYMiwgY29sb3IgPSAiSFBEIiksIGxldmVsID0gMC45KSArCiAgZ2VvbV9wb2ludChkYXRhID0gZGZfZHJhd3MsIGFlcyh4ID0gdGhldGExLCB5ID0gdGhldGEyLCBjb2xvciA9ICJibGFjayIpLAogICAgICAgICAgICAgc2l6ZSA9IDEsIGFscGhhID0gMC41KSArCiAgZ2VvbV9zZWdtZW50KGRhdGEgPSBkZl90cmFqZWN0b3JpZXMsIGFlcyh4ID0gdGhldGExLCB4ZW5kID0gdGhldGExX2VuZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSB0aGV0YTIsIHllbmQgPSB0aGV0YTJfZW5kLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZ3JheSIpLCBhbHBoYSA9IDAuNSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGRmX3RyYWplY3RvcmllcywgYWVzKHRoZXRhMSwgdGhldGEyLCBjb2xvciA9IGNvbG9yKSwgc2l6ZSA9IDIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTQsIDQpLCB5bGltID0gYygtNCwgNCkpICsKICBsYWJzKHggPSBUZVgoIiRcXHRoZXRhXzEkIiksIHkgPSBUZVgoIiRcXHRoZXRhXzIkIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siID0gImJsYWNrIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInJlZCIgPSAicmVkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInllbGxvdyIgPSAieWVsbG93MyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJncmVlbiIgPSAiZ3JlZW40IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkhQRCIgPSAiYmx1ZSIpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJibGFjayIgPSAiU3RhcnRpbmcgcG9pbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyZWQiID0gIk91dHNpZGUgb2YgdGhlIHNsaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAieWVsbG93IiA9ICJXaXRoaW4gdGhlIHNsaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZ3JlZW4iID0gIkRyYXcgZnJvbSB0aGUgc2xpY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJIUEQiID0gIjkwJSBIUEQiKSkgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNoYXBlID0gYygxNiwgMTYsIDE2LCAxNiwgTkEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gYygwLDAsMCwwLDEpKSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSAKICAKIyBNYWtlIHN1cmUgdGhhdCBuZnJhbWVzIGlzIGVxdWFsIHRvIHRoZSBudW1iZXIgb2YgaXRlcmF0aW9ucyB0aGF0IHlvdSB3aXNoIHRvIHZpc3VhbGl6ZS4KYW5pbSA8LSBhbmltYXRlKHAgKyB0cmFuc2l0aW9uX3RpbWUoaXRlciksIG5mcmFtZXMgPSBuZnJhbWVzLCBmcHMgPSAxKQpgYGAKClNob3cgdGhlIGFuaW1hdGlvbgpgYGB7cn0KYW5pbQpgYGAKCgpQbG90IGFsbCB0aGUgZHJhd3MgYWZ0ZXIgcmVtb3ZpbmcgYSB3YXJtdXAgb2YgNTAgZHJhd3MuCgpgYGB7ciwgZmlnLmFsaWduPSJjZW50ZXIifQp3YXJtdXAgPC0gNTAKZGYgPC0gZGF0YS5mcmFtZSh0aGV0YTEgPSBudXRzX2NoYWluJGRyYXdzWyh3YXJtdXArMSk6bnJvdyhudXRzX2NoYWluJGRyYXdzKSwgMV0sCiAgICAgICAgICAgICAgICAgdGhldGEyID0gbnV0c19jaGFpbiRkcmF3c1sod2FybXVwKzEpOm5yb3cobnV0c19jaGFpbiRkcmF3cyksIDJdKQpnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZGYsIGFlcyh0aGV0YTEsIHRoZXRhMiwgY29sb3IgPSAiMSIpLCBhbHBoYSA9IDAuMykgKwogIHN0YXRfZWxsaXBzZShkYXRhID0gdGFyZ2V0X2RyYXdzLCBhZXMoeCA9IFgxLCB5ID0gWDIsIGNvbG9yID0gIjIiKSwgbGV2ZWwgPSAwLjkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTQsIDQpLCB5bGltID0gYygtNCwgNCkpICsKICBsYWJzKHggPSBUZVgoIiRcXHRoZXRhXzEkIiksIHkgPSBUZVgoIiRcXHRoZXRhXzIkIikpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInN0ZWVsYmx1ZSIsICJibHVlIiksIAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJEcmF3cyIsICI5MCUgSFBEIikpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdCgKICAgIHNoYXBlID0gYygxNiwgTkEpLCBsaW5ldHlwZSA9IGMoMCwgMSksIGFscGhhID0gYygxLCAxKSkpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIyBDb252ZXJnZW5jZSBkaWFnbm9zdGljcwoKUHVyZSByYW5kb20td2FsayBNQ01DIGFsZ29yaXRobXMsIHN1Y2ggYXMgR2liYnMgYW5kIE1ldHJvcG9saXMsIHRlbmQgdG8gc3RydWdnbGUgaW4gdGhlIHByZXNlbmNlIG9mIGhpZ2ggY29ycmVsYXRpb25zIGJldHdlZW4gdGhlIHBhcmFtZXRlcnMuIExldCB1cyBkZW1vbnN0cmF0ZSBob3cgTlVUUyBpcyBhYmxlIHRvIGFsbGV2aWF0ZSB0aGlzLiBDb25zaWRlciBhZ2FpbiBhIG5vcm1hbCB0YXJnZXQgZGlzdHJpYnV0aW9uIGJ1dCB3aXRoIHRoZSBjb3ZhcmlhbmNlIG1hdHJpeAoKJCQKXFNpZ21hID0KXGJlZ2lue2JtYXRyaXh9CjEgJiAwLjk5IFxcCjAuOTkgJiAxClxlbmR7Ym1hdHJpeH0KJCQKCmBgYHtyfQpTaWdtYSA8LSBtYXRyaXgoYygxLCAwLjk5LCAwLjk5LCAxKSwgbnJvdz0yKQpTaWdtYV9pbnYgPC0gc29sdmUoU2lnbWEpCgpsb2dfcCA8LSBmdW5jdGlvbih0aGV0YSkgewogIC0wLjUgKiBzdW0odGhldGEgKiAoU2lnbWFfaW52ICUqJSB0aGV0YSkpCn0KCmdyYWRfbG9nX3AgPC0gZnVuY3Rpb24odGhldGEpIHsKICAtU2lnbWFfaW52ICUqJSB0aGV0YQp9CmBgYAoKU2ltdWxhdGUgYSB0b3RhbCBvZiA0IGNoYWlucywgZWFjaCBjb25zaXN0aW5nIG9mIDIwMDAgZHJhd3MuIFRoZSBzdGFydGluZyBwb2ludHMgYXJlICRceyAoLTIuNSwgMi41KSwgKDIuNSwgMi41KSwgKDIuNSwgLTIuNSksICgtMi41LCAtMi41KSBcfSQuCgpgYGB7cn0KIyBGb3IgSFBEIHZpc3VhbGl6YXRpb24uCnRhcmdldF9kcmF3cyA8LSBkYXRhLmZyYW1lKG12cm5vcm0oMTAwMDAwLCBjKDAsIDApLCBTaWdtYSkpCgpuX2l0ZXIgPC0gMjAwMApudXRzX2NoYWluMSA8LSBOVVRTKGMoLTIuNSwgIDIuNSksIGxvZ19wLCBncmFkX2xvZ19wLCBuX2l0ZXIsIHN0b3JlX3BhdGhzPUZBTFNFKQpudXRzX2NoYWluMiA8LSBOVVRTKGMoIDIuNSwgIDIuNSksIGxvZ19wLCBncmFkX2xvZ19wLCBuX2l0ZXIsIHN0b3JlX3BhdGhzPUZBTFNFKQpudXRzX2NoYWluMyA8LSBOVVRTKGMoIDIuNSwgLTIuNSksIGxvZ19wLCBncmFkX2xvZ19wLCBuX2l0ZXIsIHN0b3JlX3BhdGhzPUZBTFNFKQpudXRzX2NoYWluNCA8LSBOVVRTKGMoLTIuNSwgLTIuNSksIGxvZ19wLCBncmFkX2xvZ19wLCBuX2l0ZXIsIHN0b3JlX3BhdGhzPUZBTFNFKQpgYGAKClBsb3QgdGhlIGRyYXdzIG9mIHRoZSBmaXJzdCBjaGFpbiB3aXRoIGEgd2FybXVwIG9mIDUwIGRyYXdzLgoKYGBge3IsIGZpZy5hbGlnbj0iY2VudGVyIn0Kd2FybXVwIDwtIDUwCmRmIDwtIGRhdGEuZnJhbWUodGhldGExID0gbnV0c19jaGFpbjFbKHdhcm11cCsxKTpucm93KG51dHNfY2hhaW4xKSwgMV0sCiAgICAgICAgICAgICAgICAgdGhldGEyID0gbnV0c19jaGFpbjFbKHdhcm11cCsxKTpucm93KG51dHNfY2hhaW4xKSwgMl0pCmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkZiwgYWVzKHRoZXRhMSwgdGhldGEyLCBjb2xvciA9ICIxIiksIGFscGhhID0gMC4zKSArCiAgc3RhdF9lbGxpcHNlKGRhdGEgPSB0YXJnZXRfZHJhd3MsIGFlcyh4ID0gWDEsIHkgPSBYMiwgY29sb3IgPSAiMiIpLCBsZXZlbCA9IDAuOSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtNCwgNCksIHlsaW0gPSBjKC00LCA0KSkgKwogIGxhYnMoeCA9IFRlWCgiJFxcdGhldGFfMSQiKSwgeSA9IFRlWCgiJFxcdGhldGFfMiQiKSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygic3RlZWxibHVlIiwgImJsdWUiKSwgCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkRyYXdzIiwgIjkwJSBIUEQiKSkgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KAogICAgc2hhcGUgPSBjKDE2LCBOQSksIGxpbmV0eXBlID0gYygwLCAxKSwgYWxwaGEgPSBjKDEsIDEpKSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCldlIHNlZSB0aGF0IE5VVFMgaXMgYWJsZSB0byBleHBsb3JlIHRoZSByZWxldmFudCBwcm9iYWJpbGl0eSBtYXNzLgoKTGV0J3MgdGhlbiBsb29rIGF0IHNvbWUgb2YgdGhlIE1DTUMtc3BlY2lmaWMgY29udmVyZ2VuY2UgbWV0cmljcyBieSB1c2luZyB0aGUgJFx0ZXh0dHR7c3VtbWFyaXNlX2RyYXdzfSQgZnVuY3Rpb24gZnJvbSAkXHRleHR0dHtwb3N0ZXJpb3J9JC4KCmBgYHtyfQpkcmF3cyA8LSBhcnJheShkYXRhID0gYyhudXRzX2NoYWluMSwgbnV0c19jaGFpbjIsIG51dHNfY2hhaW4zLCBudXRzX2NoYWluNCksIAogICAgICAgICAgICAgICBkaW0gPSBjKG5faXRlcisxLCAyLCA0KSwKICAgICAgICAgICAgICAgZGltbmFtZXMgPSBsaXN0KE5VTEwsIGMoInRoZXRhMSIsICJ0aGV0YTIiKSwgTlVMTCkpCmRyYXdzIDwtIGFwZXJtKGRyYXdzLCBjKDEsIDMsIDIpKQpzdW1tYXJpc2VfZHJhd3MoZHJhd3MsICJtZWFuIiwgInNkIiwgfnF1YW50aWxlKC54LCBwcm9icyA9IGMoMC4wNSwgMC45NSkpLCBkZWZhdWx0X2NvbnZlcmdlbmNlX21lYXN1cmVzKCkpCmBgYAoKQmFzZWQgb24gdGhlICRcd2lkZWhhdHtSfSQtdmFsdWVzLCBpdCBzZWVtcyB0aGF0IHRoZSBjaGFpbnMgaGF2ZSBjb252ZXJnZWQuCgpQbG90IGEgbGluZSBwbG90IG9mIHRoZSBmaXJzdCBjaGFpbiB1c2luZyBhIHdhcm11cCBvZiA1MCBkcmF3cy4KCmBgYHtyLCBmaWcuYWxpZ249ImNlbnRlciJ9Cndhcm11cCA8LSA1MApkZl9jaGFpbiA8LSBkYXRhLmZyYW1lKGl0ZXIgICA9IDE6KG5yb3cobnV0c19jaGFpbjEpLXdhcm11cCksCiAgICAgICAgICAgICAgICAgICAgICAgdGhldGExID0gbnV0c19jaGFpbjFbLSgxOndhcm11cCksIDFdLAogICAgICAgICAgICAgICAgICAgICAgIHRoZXRhMiA9IG51dHNfY2hhaW4xWy0oMTp3YXJtdXApLCAyXSkgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gIWl0ZXIsIG5hbWVzX3RvID0gImdycCIsIHZhbHVlc190byA9ICJ2YWx1ZSIpCgpnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBkZl9jaGFpbiwgYWVzKGl0ZXIsIHZhbHVlLCBjb2xvciA9IGdycCkpICsKICBsYWJzKHRpdGxlID0gIlRyZW5kcyIpICsKICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShsYWJlbHMgPSBjKCJ0aGV0YTEiLCAidGhldGEyIikpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCkZpbmFsbHksIHZpc3VhbGl6ZSB0aGUgYXV0b2NvcnJlbGF0aW9uIGZ1bmN0aW9uIG9mIHRoZSBmaXJzdCBjaGFpbi4KCmBgYHtyLCBmaWcuYWxpZ249ImNlbnRlciJ9Cndhcm11cCA8LSA1MApubGFncyA8LSA1MApkZl9jaGFpbiA8LSBkYXRhLmZyYW1lKHRoZXRhMSA9IG51dHNfY2hhaW4xWy0oMTp3YXJtdXApLCAxXSwKICAgICAgICAgICAgICAgICAgICAgICB0aGV0YTIgPSBudXRzX2NoYWluMlstKDE6d2FybXVwKSwgMl0pCmRmX2FjZiA8LSBzYXBwbHkoZGZfY2hhaW4sIGZ1bmN0aW9uKHgpIGFjZih4LCBsYWcubWF4ID0gbmxhZ3MsIHBsb3QgPSBGKSRhY2YpICU+JQogIGRhdGEuZnJhbWUoaXRlciA9IDA6KG5sYWdzKSkgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAhaXRlciwgbmFtZXNfdG8gPSAiZ3JwIiwgdmFsdWVzX3RvID0gInZhbHVlIikKCmdncGxvdCgpICsKICBnZW9tX2xpbmUoZGF0YSA9IGRmX2FjZiwgYWVzKGl0ZXIsIHZhbHVlLCBjb2xvciA9IGdycCkpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMCkpICsKICBsYWJzKHRpdGxlID0gIkF1dG9jb3JyZWxhdGlvbiBmdW5jdGlvbiIsIHggPSAiSXRlcmF0aW9uIikgKwogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKGxhYmVscyA9IGMoJ3RoZXRhMScsICd0aGV0YTInKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKVGhlIGRyYXdzIGRvbid0IHNlZW0gdG8gYmUgaW5kZXBlbmRlbnQuIEFzIHdlIHNhdyBpbiB0aGUgdmlzdWFsaXphdGlvbiBvZiBOVVRTLCBwaWNraW5nIHRoZSBuZXh0IGRyYXcgdW5pZm9ybWx5IGF0IHJhbmRvbSBtYXkgdGFrZSB1cyB2ZXJ5IGNsb3NlIHRvIHRoZSBzdGFydGluZyBwb2ludCBvZiB0aGUgdHJhamVjdG9yeSwgd2hpY2ggY2FuIHBhcnRseSBleHBsYWluIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBkcmF3cy4gQSBiZXR0ZXIgZHJhd2luZyBzY2hlbWUgd291bGQgYmUgdG8gYWRkIGEgbGFyZ2VyIHdlaWdodCB0byBwb2ludHMgZmFyIGF3YXkgZnJvbSB0aGUgc3RhcnRpbmcgcG9pbnQuCgojIyBSZWZlcmVuY2VzCg==