Metropolis algorithm
ggplot2 is used for plotting, tidyr for manipulating data frames
library(ggplot2)
theme_set(theme_minimal())
library(tidyr)
library(gganimate)
library(ggforce)
library(MASS)
library(posterior)
library(rprojroot)
root<-has_file(".BDA_R_demos_root")$make_fix_file()
Parameters of a normal distribution used as a toy target distribution
y1 <- 0
y2 <- 0
r <- 0.8
Sigma <- diag(2)
Sigma[1, 2] <- r
Sigma[2, 1] <- r
Metropolis proposal distribution scale
sp <- 0.3
Sample from the toy distribution to visualize 90% HPD interval with ggplot’s stat_ellipse()
dft <- data.frame(mvrnorm(100000, c(0, 0), Sigma))
see BDA3 p. 85 for how to compute HPD for multivariate normal in 2d-case contour for 90% HPD is an ellipse, whose semimajor axes can be computed from the eigenvalues of the covariance matrix scaled by a value selected to get ellipse match the density at the edge of 90% HPD. Angle of the ellipse could be computed from the eigenvectors, but since the marginals are same we know that angle is pi/4 Starting value of the chain
t1 <- -2.5
t2 <- 2.5
Number of iterations.
M <- 5000
Insert your own Metropolis sampling here
# Allocate memory for the sample
tt <- matrix(rep(0, 2*M), ncol = 2)
tt[1,] <- c(t1, t2) # Save starting point
# For demonstration load pre-computed values
# Replace this with your algorithm!
# tt is a M x 2 array, with M draws of both theta_1 and theta_2
load(root("demos_ch11","demo11_2a.RData"))
The rest is for illustration Take the first 200 draws to illustrate how the sampler works
df100 <- data.frame(id=rep(1,100),
iter=1:100,
th1 = tt[1:100, 1],
th2 = tt[1:100, 2],
th1l = c(tt[1, 1], tt[1:(100-1), 1]),
th2l = c(tt[1, 2], tt[1:(100-1), 2]))
Take the first 5000 observations after warmup of 50
S <- 5000
warm <- 500
dfs <- data.frame(th1 = tt[(warm+1):S, 1], th2 = tt[(warm+1):S, 2])
Remove warm-up period of 50 first draws later
# labels and frame indices for the plot
labs1 <- c('Draws', 'Steps of the sampler', '90% HPD')
p1 <- ggplot() +
geom_jitter(data = df100, width=0.05, height=0.05,
aes(th1, th2, group=id, color ='1'), alpha=0.3) +
geom_segment(data = df100, aes(x = th1, xend = th1l, color = '2',
y = th2, yend = th2l)) +
stat_ellipse(data = dft, aes(x = X1, y = X2, color = '3'), level = 0.9) +
coord_cartesian(xlim = c(-4, 4), ylim = c(-4, 4)) +
labs(x = 'theta1', y = 'theta2') +
scale_color_manual(values = c('red', 'forestgreen','blue'), labels = labs1) +
guides(color = guide_legend(override.aes = list(
shape = c(16, NA, NA), linetype = c(0, 1, 1)))) +
theme(legend.position = 'bottom', legend.title = element_blank())
The following generates a gif animation of the steps of the sampler (might take 10 seconds).
anim <- animate(p1 +
transition_reveal(along=iter) +
shadow_trail(0.01))
Show the animation
anim
Plot the final frame
p1
show 1000 draws after the warm-up
labs2 <- c('Draws', '90% HPD')
ggplot() +
geom_point(data = dfs[1:1000,],
aes(th1, th2, color = '1'), alpha = 0.3) +
stat_ellipse(data = dft, aes(x = X1, y = X2, color = '2'), level = 0.9) +
coord_cartesian(xlim = c(-4, 4), ylim = c(-4, 4)) +
labs(x = 'theta1', y = 'theta2') +
scale_color_manual(values = c('steelblue', 'blue'), labels = labs2) +
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())
show 4500 draws after the warm-up
labs2 <- c('Draws', '90% HPD')
ggplot() +
geom_point(data = dfs,
aes(th1, th2, color = '1'), alpha = 0.3) +
stat_ellipse(data = dft, aes(x = X1, y = X2, color = '2'), level = 0.9) +
coord_cartesian(xlim = c(-4, 4), ylim = c(-4, 4)) +
labs(x = 'theta1', y = 'theta2') +
scale_color_manual(values = c('steelblue', 'blue'), labels = labs2) +
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
summarise_draws(dfs, Rhat=rhat_basic, ESS=ess_basic)
## # A tibble: 2 × 3
## variable Rhat ESS
## <chr> <num> <num>
## 1 th1 1.00 223.
## 2 th2 1.00 191.
neff <- apply(dfs, 2, ess_basic)
# both theta have own neff, but for plotting these are so close to each
# other, so that single relative efficiency value is used
reff <- mean(neff/S)
Visual convergence diagnostics
Collapse the data frame with row numbers augmented into key-value pairs for visualizing the chains
dfb <- dfs
Sb <- S-warm
dfch <- within(dfb, iter <- 1:Sb) %>%
pivot_longer(cols = !iter, names_to = "grp", values_to = "value")
Another data frame for visualizing the estimate of the autocorrelation function
nlags <- 50
dfa <- sapply(dfb, 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")
A third data frame to visualize the cumulative averages and the 95% intervals
dfca <- (cumsum(dfb) / (1:Sb)) %>%
within({iter <- 1:Sb
uppi <- 1.96/sqrt(1:Sb)
upp <- 1.96/(sqrt(1:Sb*reff))}) %>%
pivot_longer(cols = !iter, names_to = "grp", values_to = "value")
Visualize the chains
ggplot(data = dfch) +
geom_line(aes(iter, value, color = grp)) +
labs(title = 'Trends') +
scale_color_discrete(labels = c('theta1','theta2')) +
theme(legend.position = 'bottom', legend.title = element_blank())
Visualize the estimate of the autocorrelation function
ggplot(data = dfa) +
geom_line(aes(iter, value, color = grp)) +
geom_hline(aes(yintercept = 0)) +
labs(title = 'Autocorrelation function') +
scale_color_discrete(labels = c('theta1', 'theta2')) +
theme(legend.position = 'bottom', legend.title = element_blank())
Visualize the estimate of the Monte Carlo error estimates
# labels
labs3 <- c('theta1', 'theta2',
'95% interval for MCMC error',
'95% interval for independent MC')
ggplot() +
geom_line(data = dfca, aes(iter, value, color = grp, linetype = grp)) +
geom_line(aes(1:Sb, -1.96/sqrt(1:Sb*reff)), linetype = 2) +
geom_line(aes(1:Sb, -1.96/sqrt(1:Sb)), linetype = 3) +
geom_hline(aes(yintercept = 0)) +
coord_cartesian(ylim = c(-1.5, 1.5), xlim = c(0,4000)) +
labs(title = 'Cumulative averages') +
scale_color_manual(values = c('red','blue',rep('black', 2)), labels = labs3) +
scale_linetype_manual(values = c(1, 1, 2, 3), labels = labs3) +
theme(legend.position = 'bottom', legend.title = element_blank())
Same again with r=0.99 Parameters of a normal distribution used as a toy target distribution
y1 <- 0
y2 <- 0
r <- 0.99
Sigma <- diag(2)
Sigma[1, 2] <- r
Sigma[2, 1] <- r
Metropolis proposal distribution scale
sp <- 0.3
Sample from the toy distribution to visualize 90% HPD interval with ggplot’s stat_ellipse()
dft <- data.frame(mvrnorm(100000, c(0, 0), Sigma))
see BDA3 p. 85 for how to compute HPD for multivariate normal in 2d-case contour for 90% HPD is an ellipse, whose semimajor axes can be computed from the eigenvalues of the covariance matrix scaled by a value selected to get ellipse match the density at the edge of 90% HPD. Angle of the ellipse could be computed from the eigenvectors, but since the marginals are same we know that angle is pi/4 Starting value of the chain
t1 <- -2.5
t2 <- 2.5
Number of iterations.
M <- 5000
Insert your own Metropolis sampling here
# Allocate memory for the sample
tt <- matrix(rep(0, 2*M), ncol = 2)
tt[1,] <- c(t1, t2) # Save starting point
# For demonstration load pre-computed values
# Replace this with your algorithm!
# tt is a M x 2 array, with M draws of both theta_1 and theta_2
load(root("demos_ch11","demo11_2b.RData"))
The rest is for illustration Take the first 200 draws to illustrate how the sampler works
df100 <- data.frame(id=rep(1,100),
iter=1:100,
th1 = tt[1:100, 1],
th2 = tt[1:100, 2],
th1l = c(tt[1, 1], tt[1:(100-1), 1]),
th2l = c(tt[1, 2], tt[1:(100-1), 2]))
Take the first 5000 observations after warmup of 50
S <- 5000
warm <- 500
dfs <- data.frame(th1 = tt[(warm+1):S, 1], th2 = tt[(warm+1):S, 2])
Remove warm-up period of 50 first draws later
# labels and frame indices for the plot
labs1 <- c('Draws', 'Steps of the sampler', '90% HPD')
p1 <- ggplot() +
geom_jitter(data = df100, width=0.05, height=0.05,
aes(th1, th2, group=id, color ='1'), alpha=0.3) +
geom_segment(data = df100, aes(x = th1, xend = th1l, color = '2',
y = th2, yend = th2l)) +
stat_ellipse(data = dft, aes(x = X1, y = X2, color = '3'), level = 0.9) +
coord_cartesian(xlim = c(-4, 4), ylim = c(-4, 4)) +
labs(x = 'theta1', y = 'theta2') +
scale_color_manual(values = c('red', 'forestgreen','blue'), labels = labs1) +
guides(color = guide_legend(override.aes = list(
shape = c(16, NA, NA), linetype = c(0, 1, 1)))) +
theme(legend.position = 'bottom', legend.title = element_blank())
The following generates a gif animation of the steps of the sampler (might take 10 seconds).
anim <- animate(p1 +
transition_reveal(along=iter) +
shadow_trail(0.01))
Show the animation
anim
Plot the final frame
p1
show 1000 draws after the warm-up
labs2 <- c('Draws', '90% HPD')
ggplot() +
geom_point(data = dfs[1:1000,],
aes(th1, th2, color = '1'), alpha = 0.3) +
stat_ellipse(data = dft, aes(x = X1, y = X2, color = '2'), level = 0.9) +
coord_cartesian(xlim = c(-4, 4), ylim = c(-4, 4)) +
labs(x = 'theta1', y = 'theta2') +
scale_color_manual(values = c('steelblue', 'blue'), labels = labs2) +
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())
show 4500 draws after the warm-up
labs2 <- c('Draws', '90% HPD')
ggplot() +
geom_point(data = dfs,
aes(th1, th2, color = '1'), alpha = 0.3) +
stat_ellipse(data = dft, aes(x = X1, y = X2, color = '2'), level = 0.9) +
coord_cartesian(xlim = c(-4, 4), ylim = c(-4, 4)) +
labs(x = 'theta1', y = 'theta2') +
scale_color_manual(values = c('steelblue', 'blue'), labels = labs2) +
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
summarise_draws(dfs, Rhat=rhat_basic, ESS=ess_basic)
## # A tibble: 2 × 3
## variable Rhat ESS
## <chr> <num> <num>
## 1 th1 1.02 38.9
## 2 th2 1.02 39.1
neff <- apply(dfs, 2, ess_basic)
# both theta have own neff, but for plotting these are so close to each
# other, so that single relative efficiency value is used
reff <- mean(neff/S)
Visual convergence diagnostics
Collapse the data frame with row numbers augmented into key-value pairs for visualizing the chains
dfb <- dfs
Sb <- S-warm
dfch <- within(dfb, iter <- 1:Sb) %>%
pivot_longer(cols = !iter, names_to = "grp", values_to = "value")
Another data frame for visualizing the estimate of the autocorrelation function
nlags <- 100
dfa <- sapply(dfb, 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")
A third data frame to visualize the cumulative averages and the 95% intervals
dfca <- (cumsum(dfb) / (1:Sb)) %>%
within({iter <- 1:Sb
uppi <- 1.96/sqrt(1:Sb)
upp <- 1.96/(sqrt(1:Sb*reff))}) %>%
pivot_longer(cols = !iter, names_to = "grp", values_to = "value")
Visualize the chains
ggplot(data = dfch) +
geom_line(aes(iter, value, color = grp)) +
labs(title = 'Trends') +
scale_color_discrete(labels = c('theta1','theta2')) +
theme(legend.position = 'bottom', legend.title = element_blank())
Visualize the estimate of the autocorrelation function
ggplot(data = dfa) +
geom_line(aes(iter, value, color = grp)) +
geom_hline(aes(yintercept = 0)) +
labs(title = 'Autocorrelation function') +
scale_color_discrete(labels = c('theta1', 'theta2')) +
theme(legend.position = 'bottom', legend.title = element_blank())
Visualize the estimate of the Monte Carlo error estimates
# labels
labs3 <- c('theta1', 'theta2',
'95% interval for MCMC error',
'95% interval for independent MC')
ggplot() +
geom_line(data = dfca, aes(iter, value, color = grp, linetype = grp)) +
geom_line(aes(1:Sb, -1.96/sqrt(1:Sb*reff)), linetype = 2) +
geom_line(aes(1:Sb, -1.96/sqrt(1:Sb)), linetype = 3) +
geom_hline(aes(yintercept = 0)) +
coord_cartesian(ylim = c(-1.5, 1.5), xlim = c(0,4000)) +
labs(title = 'Cumulative averages') +
scale_color_manual(values = c('red','blue',rep('black', 2)), labels = labs3) +
scale_linetype_manual(values = c(1, 1, 2, 3), labels = labs3) +
theme(legend.position = 'bottom', legend.title = element_blank())
Same again with sp = 1.5
sp = 1.5
Insert your own Metropolis sampling here
# Allocate memory for the sample
tt <- matrix(rep(0, 2*M), ncol = 2)
tt[1,] <- c(t1, t2) # Save starting point
# For demonstration load pre-computed values
# Replace this with your algorithm!
# tt is a M x 2 array, with M draws of both theta_1 and theta_2
# See in the end how to manipulate R arrays for easy convergence diagnostics
load(root("demos_ch11","demo11_2c.RData"))
The rest is for illustration Take the first 200 draws to illustrate how the sampler works
df100 <- data.frame(id=rep(1,100),
iter=1:100,
th1 = tt[1:100, 1],
th2 = tt[1:100, 2],
th1l = c(tt[1, 1], tt[1:(100-1), 1]),
th2l = c(tt[1, 2], tt[1:(100-1), 2]))
Take the first 5000 observations after warmup of 50
S <- 5000
warm <- 500
dfs <- data.frame(th1 = tt[(warm+1):S, 1], th2 = tt[(warm+1):S, 2])
Remove warm-up period of 50 first draws later
# labels and frame indices for the plot
labs1 <- c('Draws', 'Steps of the sampler', '90% HPD')
p1 <- ggplot() +
geom_jitter(data = df100, width=0.05, height=0.05,
aes(th1, th2, group=id, color ='1'), alpha=0.3) +
geom_segment(data = df100, aes(x = th1, xend = th1l, color = '2',
y = th2, yend = th2l)) +
stat_ellipse(data = dft, aes(x = X1, y = X2, color = '3'), level = 0.9) +
coord_cartesian(xlim = c(-4, 4), ylim = c(-4, 4)) +
labs(x = 'theta1', y = 'theta2') +
scale_color_manual(values = c('red', 'forestgreen','blue'), labels = labs1) +
guides(color = guide_legend(override.aes = list(
shape = c(16, NA, NA), linetype = c(0, 1, 1)))) +
theme(legend.position = 'bottom', legend.title = element_blank())
The following generates a gif animation of the steps of the sampler (might take 10 seconds).
anim <- animate(p1 +
transition_reveal(along=iter) +
shadow_trail(0.01))
Show the animation
anim
show 1000 draws after the warm-up
labs2 <- c('Draws', '90% HPD')
ggplot() +
geom_point(data = dfs[1:1000,],
aes(th1, th2, color = '1'), alpha = 0.3) +
stat_ellipse(data = dft, aes(x = X1, y = X2, color = '2'), level = 0.9) +
coord_cartesian(xlim = c(-4, 4), ylim = c(-4, 4)) +
labs(x = 'theta1', y = 'theta2') +
scale_color_manual(values = c('steelblue', 'blue'), labels = labs2) +
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())
show 4500 draws after the warm-up
labs2 <- c('Draws', '90% HPD')
ggplot() +
geom_point(data = dfs,
aes(th1, th2, color = '1'), alpha = 0.3) +
stat_ellipse(data = dft, aes(x = X1, y = X2, color = '2'), level = 0.9) +
coord_cartesian(xlim = c(-4, 4), ylim = c(-4, 4)) +
labs(x = 'theta1', y = 'theta2') +
scale_color_manual(values = c('steelblue', 'blue'), labels = labs2) +
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
summarise_draws(dfs)
## # A tibble: 2 × 10
## variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
## <chr> <num> <num> <num> <num> <num> <num> <num> <num> <num>
## 1 th1 -0.294 -0.339 0.855 0.921 -1.60 1.11 1.03 62.9 81.4
## 2 th2 -0.293 -0.343 0.857 0.919 -1.47 1.24 1.03 64.2 74.3
neff <- apply(dfs, 2, ess_basic)
# both theta have own neff, but for plotting these are so close to each
# other, so that single relative efficiency value is used
reff <- mean(neff/S)
Visual convergence diagnostics
Collapse the data frame with row numbers augmented into key-value pairs for visualizing the chains
dfb <- dfs
Sb <- S-warm
dfch <- within(dfb, iter <- 1:Sb) %>%
pivot_longer(cols = !iter, names_to = "grp", values_to = "value")
Another data frame for visualizing the estimate of the autocorrelation function
nlags <- 100
dfa <- sapply(dfb, 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")
A third data frame to visualize the cumulative averages and the 95% intervals
dfca <- (cumsum(dfb) / (1:Sb)) %>%
within({iter <- 1:Sb
uppi <- 1.96/sqrt(1:Sb)
upp <- 1.96/(sqrt(1:Sb*reff))}) %>%
pivot_longer(cols = !iter, names_to = "grp", values_to = "value")
Visualize the chains
ggplot(data = dfch) +
geom_line(aes(iter, value, color = grp)) +
labs(title = 'Trends') +
scale_color_discrete(labels = c('theta1','theta2')) +
theme(legend.position = 'bottom', legend.title = element_blank())
Visualize the estimate of the autocorrelation function
ggplot(data = dfa) +
geom_line(aes(iter, value, color = grp)) +
geom_hline(aes(yintercept = 0)) +
labs(title = 'Autocorrelation function') +
scale_color_discrete(labels = c('theta1', 'theta2')) +
theme(legend.position = 'bottom', legend.title = element_blank())
Visualize the estimate of the Monte Carlo error estimates
# labels
labs3 <- c('theta1', 'theta2',
'95% interval for MCMC error',
'95% interval for independent MC')
ggplot() +
geom_line(data = dfca, aes(iter, value, color = grp, linetype = grp)) +
geom_line(aes(1:Sb, -1.96/sqrt(1:Sb*reff)), linetype = 2) +
geom_line(aes(1:Sb, -1.96/sqrt(1:Sb)), linetype = 3) +
geom_hline(aes(yintercept = 0)) +
coord_cartesian(ylim = c(-1.5, 1.5), xlim = c(0,4000)) +
labs(title = 'Cumulative averages') +
scale_color_manual(values = c('red','blue',rep('black', 2)), labels = labs3) +
scale_linetype_manual(values = c(1, 1, 2, 3), labels = labs3) +
theme(legend.position = 'bottom', legend.title = element_blank())
LS0tCnRpdGxlOiAiQmF5ZXNpYW4gZGF0YSBhbmFseXNpcyBkZW1vIDExLjIiCmF1dGhvcjogIkFraSBWZWh0YXJpLCBNYXJrdXMgUGFhc2luaWVtaSIKZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IHJlYWRhYmxlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQojIyBNZXRyb3BvbGlzIGFsZ29yaXRobQoKZ2dwbG90MiBpcyB1c2VkIGZvciBwbG90dGluZywgdGlkeXIgZm9yIG1hbmlwdWxhdGluZyBkYXRhIGZyYW1lcwoKYGBge3Igc2V0dXAsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZ2dhbmltYXRlKQpsaWJyYXJ5KGdnZm9yY2UpCmxpYnJhcnkoTUFTUykKbGlicmFyeShwb3N0ZXJpb3IpCmxpYnJhcnkocnByb2pyb290KQpyb290PC1oYXNfZmlsZSgiLkJEQV9SX2RlbW9zX3Jvb3QiKSRtYWtlX2ZpeF9maWxlKCkKYGBgCgpQYXJhbWV0ZXJzIG9mIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiB1c2VkIGFzIGEgdG95IHRhcmdldCBkaXN0cmlidXRpb24KCmBgYHtyIH0KeTEgPC0gMAp5MiA8LSAwCnIgPC0gMC44ClNpZ21hIDwtIGRpYWcoMikKU2lnbWFbMSwgMl0gPC0gcgpTaWdtYVsyLCAxXSA8LSByCmBgYAoKTWV0cm9wb2xpcyBwcm9wb3NhbCBkaXN0cmlidXRpb24gc2NhbGUKCmBgYHtyIH0Kc3AgPC0gMC4zCmBgYAoKU2FtcGxlIGZyb20gdGhlIHRveSBkaXN0cmlidXRpb24gdG8gdmlzdWFsaXplIDkwJSBIUEQKaW50ZXJ2YWwgd2l0aCBnZ3Bsb3QncyBzdGF0X2VsbGlwc2UoKQoKYGBge3IgfQpkZnQgPC0gZGF0YS5mcmFtZShtdnJub3JtKDEwMDAwMCwgYygwLCAwKSwgU2lnbWEpKQpgYGAKCnNlZSBCREEzIHAuIDg1IGZvciBob3cgdG8gY29tcHV0ZSBIUEQgZm9yIG11bHRpdmFyaWF0ZSBub3JtYWwKaW4gMmQtY2FzZSBjb250b3VyIGZvciA5MCUgSFBEIGlzIGFuIGVsbGlwc2UsIHdob3NlIHNlbWltYWpvcgpheGVzIGNhbiBiZSBjb21wdXRlZCBmcm9tIHRoZSBlaWdlbnZhbHVlcyBvZiB0aGUgY292YXJpYW5jZQptYXRyaXggc2NhbGVkIGJ5IGEgdmFsdWUgc2VsZWN0ZWQgdG8gZ2V0IGVsbGlwc2UgbWF0Y2ggdGhlCmRlbnNpdHkgYXQgdGhlIGVkZ2Ugb2YgOTAlIEhQRC4gQW5nbGUgb2YgdGhlIGVsbGlwc2UgY291bGQgYmUKY29tcHV0ZWQgZnJvbSB0aGUgZWlnZW52ZWN0b3JzLCBidXQgc2luY2UgdGhlIG1hcmdpbmFscyBhcmUgc2FtZQp3ZSBrbm93IHRoYXQgYW5nbGUgaXMgcGkvNApTdGFydGluZyB2YWx1ZSBvZiB0aGUgY2hhaW4KCmBgYHtyIH0KdDEgPC0gLTIuNQp0MiA8LSAyLjUKYGBgCgpOdW1iZXIgb2YgaXRlcmF0aW9ucy4KCmBgYHtyIH0KTSA8LSA1MDAwCmBgYAoKSW5zZXJ0IHlvdXIgb3duIE1ldHJvcG9saXMgc2FtcGxpbmcgaGVyZQoKYGBge3IgfQojIEFsbG9jYXRlIG1lbW9yeSBmb3IgdGhlIHNhbXBsZQp0dCA8LSBtYXRyaXgocmVwKDAsIDIqTSksIG5jb2wgPSAyKQp0dFsxLF0gPC0gYyh0MSwgdDIpICAgICMgU2F2ZSBzdGFydGluZyBwb2ludAojIEZvciBkZW1vbnN0cmF0aW9uIGxvYWQgcHJlLWNvbXB1dGVkIHZhbHVlcwojIFJlcGxhY2UgdGhpcyB3aXRoIHlvdXIgYWxnb3JpdGhtIQojIHR0IGlzIGEgTSB4IDIgYXJyYXksIHdpdGggTSBkcmF3cyBvZiBib3RoIHRoZXRhXzEgYW5kIHRoZXRhXzIKbG9hZChyb290KCJkZW1vc19jaDExIiwiZGVtbzExXzJhLlJEYXRhIikpCmBgYAoKVGhlIHJlc3QgaXMgZm9yIGlsbHVzdHJhdGlvbgpUYWtlIHRoZSBmaXJzdCAyMDAgZHJhd3MKdG8gaWxsdXN0cmF0ZSBob3cgdGhlIHNhbXBsZXIgd29ya3MKCmBgYHtyIH0KZGYxMDAgPC0gZGF0YS5mcmFtZShpZD1yZXAoMSwxMDApLAogICAgICAgICAgICAgICAgICAgIGl0ZXI9MToxMDAsIAogICAgICAgICAgICAgICAgICAgIHRoMSA9IHR0WzE6MTAwLCAxXSwKICAgICAgICAgICAgICAgICAgICB0aDIgPSB0dFsxOjEwMCwgMl0sCiAgICAgICAgICAgICAgICAgICAgdGgxbCA9IGModHRbMSwgMV0sIHR0WzE6KDEwMC0xKSwgMV0pLAogICAgICAgICAgICAgICAgICAgIHRoMmwgPSBjKHR0WzEsIDJdLCB0dFsxOigxMDAtMSksIDJdKSkKYGBgCgpUYWtlIHRoZSBmaXJzdCA1MDAwIG9ic2VydmF0aW9ucyBhZnRlciB3YXJtdXAgb2YgNTAKCmBgYHtyIH0KUyA8LSA1MDAwCndhcm0gPC0gNTAwCmRmcyA8LSBkYXRhLmZyYW1lKHRoMSA9IHR0Wyh3YXJtKzEpOlMsIDFdLCB0aDIgPSB0dFsod2FybSsxKTpTLCAyXSkKYGBgCgpSZW1vdmUgd2FybS11cCBwZXJpb2Qgb2YgNTAgZmlyc3QgZHJhd3MgbGF0ZXIKCmBgYHtyIH0KIyBsYWJlbHMgYW5kIGZyYW1lIGluZGljZXMgZm9yIHRoZSBwbG90CmxhYnMxIDwtIGMoJ0RyYXdzJywgJ1N0ZXBzIG9mIHRoZSBzYW1wbGVyJywgJzkwJSBIUEQnKQpwMSA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9qaXR0ZXIoZGF0YSA9IGRmMTAwLCB3aWR0aD0wLjA1LCBoZWlnaHQ9MC4wNSwKICAgICAgICAgICAgICBhZXModGgxLCB0aDIsIGdyb3VwPWlkLCBjb2xvciA9JzEnKSwgYWxwaGE9MC4zKSArCiAgZ2VvbV9zZWdtZW50KGRhdGEgPSBkZjEwMCwgYWVzKHggPSB0aDEsIHhlbmQgPSB0aDFsLCBjb2xvciA9ICcyJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHRoMiwgeWVuZCA9IHRoMmwpKSArCiAgc3RhdF9lbGxpcHNlKGRhdGEgPSBkZnQsIGFlcyh4ID0gWDEsIHkgPSBYMiwgY29sb3IgPSAnMycpLCBsZXZlbCA9IDAuOSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtNCwgNCksIHlsaW0gPSBjKC00LCA0KSkgKwogIGxhYnMoeCA9ICd0aGV0YTEnLCB5ID0gJ3RoZXRhMicpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygncmVkJywgJ2ZvcmVzdGdyZWVuJywnYmx1ZScpLCBsYWJlbHMgPSBsYWJzMSkgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KAogICAgc2hhcGUgPSBjKDE2LCBOQSwgTkEpLCBsaW5ldHlwZSA9IGMoMCwgMSwgMSkpKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKVGhlIGZvbGxvd2luZyBnZW5lcmF0ZXMgYSBnaWYgYW5pbWF0aW9uCm9mIHRoZSBzdGVwcyBvZiB0aGUgc2FtcGxlciAobWlnaHQgdGFrZSAxMCBzZWNvbmRzKS4KCmBgYHtyIE1ldHJvcG9saXMgKDEpLCByZXN1bHRzPSdoaWRlJywgbWVzc2FnZT1GQUxTRX0KYW5pbSA8LSBhbmltYXRlKHAxICsgICAKICAgICAgICAgICAgICAgICAgdHJhbnNpdGlvbl9yZXZlYWwoYWxvbmc9aXRlcikgKyAKICAgICAgICAgICAgICAgICAgc2hhZG93X3RyYWlsKDAuMDEpKQpgYGAKClNob3cgdGhlIGFuaW1hdGlvbgoKYGBge3IgfQphbmltCmBgYAoKUGxvdCB0aGUgZmluYWwgZnJhbWUKCmBgYHtyIH0KcDEKYGBgCgpzaG93IDEwMDAgZHJhd3MgYWZ0ZXIgdGhlIHdhcm0tdXAKCmBgYHtyIH0KbGFiczIgPC0gYygnRHJhd3MnLCAnOTAlIEhQRCcpCmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkZnNbMToxMDAwLF0sCiAgICAgICAgICAgICBhZXModGgxLCB0aDIsIGNvbG9yID0gJzEnKSwgYWxwaGEgPSAwLjMpICsKICBzdGF0X2VsbGlwc2UoZGF0YSA9IGRmdCwgYWVzKHggPSBYMSwgeSA9IFgyLCBjb2xvciA9ICcyJyksIGxldmVsID0gMC45KSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC00LCA0KSwgeWxpbSA9IGMoLTQsIDQpKSArCiAgbGFicyh4ID0gJ3RoZXRhMScsIHkgPSAndGhldGEyJykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCdzdGVlbGJsdWUnLCAnYmx1ZScpLCBsYWJlbHMgPSBsYWJzMikgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KAogICAgc2hhcGUgPSBjKDE2LCBOQSksIGxpbmV0eXBlID0gYygwLCAxKSwgYWxwaGEgPSBjKDEsIDEpKSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCnNob3cgNDUwMCBkcmF3cyBhZnRlciB0aGUgd2FybS11cAoKYGBge3IgfQpsYWJzMiA8LSBjKCdEcmF3cycsICc5MCUgSFBEJykKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGRmcywKICAgICAgICAgICAgIGFlcyh0aDEsIHRoMiwgY29sb3IgPSAnMScpLCBhbHBoYSA9IDAuMykgKwogIHN0YXRfZWxsaXBzZShkYXRhID0gZGZ0LCBhZXMoeCA9IFgxLCB5ID0gWDIsIGNvbG9yID0gJzInKSwgbGV2ZWwgPSAwLjkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTQsIDQpLCB5bGltID0gYygtNCwgNCkpICsKICBsYWJzKHggPSAndGhldGExJywgeSA9ICd0aGV0YTInKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoJ3N0ZWVsYmx1ZScsICdibHVlJyksIGxhYmVscyA9IGxhYnMyKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoCiAgICBzaGFwZSA9IGMoMTYsIE5BKSwgbGluZXR5cGUgPSBjKDAsIDEpLCBhbHBoYSA9IGMoMSwgMSkpKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMjIENvbnZlcmdlbmNlIGRpYWdub3N0aWNzCgpgYGB7ciB9CnN1bW1hcmlzZV9kcmF3cyhkZnMsIFJoYXQ9cmhhdF9iYXNpYywgRVNTPWVzc19iYXNpYykKbmVmZiA8LSBhcHBseShkZnMsIDIsIGVzc19iYXNpYykKIyBib3RoIHRoZXRhIGhhdmUgb3duIG5lZmYsIGJ1dCBmb3IgcGxvdHRpbmcgdGhlc2UgYXJlIHNvIGNsb3NlIHRvIGVhY2gKIyBvdGhlciwgc28gdGhhdCBzaW5nbGUgcmVsYXRpdmUgZWZmaWNpZW5jeSB2YWx1ZSBpcyB1c2VkCnJlZmYgPC0gbWVhbihuZWZmL1MpCmBgYAoKIyMjIFZpc3VhbCBjb252ZXJnZW5jZSBkaWFnbm9zdGljcwpDb2xsYXBzZSB0aGUgZGF0YSBmcmFtZSB3aXRoIHJvdyBudW1iZXJzIGF1Z21lbnRlZAppbnRvIGtleS12YWx1ZSBwYWlycyBmb3IgdmlzdWFsaXppbmcgdGhlIGNoYWlucwoKYGBge3IgfQpkZmIgPC0gZGZzClNiIDwtIFMtd2FybQpkZmNoIDwtIHdpdGhpbihkZmIsIGl0ZXIgPC0gMTpTYikgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gIWl0ZXIsIG5hbWVzX3RvID0gImdycCIsIHZhbHVlc190byA9ICJ2YWx1ZSIpCmBgYAoKQW5vdGhlciBkYXRhIGZyYW1lIGZvciB2aXN1YWxpemluZyB0aGUgZXN0aW1hdGUgb2YKdGhlIGF1dG9jb3JyZWxhdGlvbiBmdW5jdGlvbgoKYGBge3IgfQpubGFncyA8LSA1MApkZmEgPC0gc2FwcGx5KGRmYiwgZnVuY3Rpb24oeCkgYWNmKHgsIGxhZy5tYXggPSBubGFncywgcGxvdCA9IEYpJGFjZikgJT4lCiAgZGF0YS5mcmFtZShpdGVyID0gMDoobmxhZ3MpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAhaXRlciwgbmFtZXNfdG8gPSAiZ3JwIiwgdmFsdWVzX3RvID0gInZhbHVlIikKYGBgCgpBIHRoaXJkIGRhdGEgZnJhbWUgdG8gdmlzdWFsaXplIHRoZSBjdW11bGF0aXZlIGF2ZXJhZ2VzCmFuZCB0aGUgOTUlIGludGVydmFscwoKYGBge3IgfQpkZmNhIDwtIChjdW1zdW0oZGZiKSAvICgxOlNiKSkgJT4lCiAgd2l0aGluKHtpdGVyIDwtIDE6U2IKICB1cHBpIDwtICAxLjk2L3NxcnQoMTpTYikKICB1cHAgPC0gMS45Ni8oc3FydCgxOlNiKnJlZmYpKX0pICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gIWl0ZXIsIG5hbWVzX3RvID0gImdycCIsIHZhbHVlc190byA9ICJ2YWx1ZSIpCmBgYAoKVmlzdWFsaXplIHRoZSBjaGFpbnMKCmBgYHtyIH0KZ2dwbG90KGRhdGEgPSBkZmNoKSArCiAgZ2VvbV9saW5lKGFlcyhpdGVyLCB2YWx1ZSwgY29sb3IgPSBncnApKSArCiAgbGFicyh0aXRsZSA9ICdUcmVuZHMnKSArCiAgc2NhbGVfY29sb3JfZGlzY3JldGUobGFiZWxzID0gYygndGhldGExJywndGhldGEyJykpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKClZpc3VhbGl6ZSB0aGUgZXN0aW1hdGUgb2YgdGhlIGF1dG9jb3JyZWxhdGlvbiBmdW5jdGlvbgoKYGBge3IgfQpnZ3Bsb3QoZGF0YSA9IGRmYSkgKwogIGdlb21fbGluZShhZXMoaXRlciwgdmFsdWUsIGNvbG9yID0gZ3JwKSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAwKSkgKwogIGxhYnModGl0bGUgPSAnQXV0b2NvcnJlbGF0aW9uIGZ1bmN0aW9uJykgKwogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKGxhYmVscyA9IGMoJ3RoZXRhMScsICd0aGV0YTInKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKVmlzdWFsaXplIHRoZSBlc3RpbWF0ZSBvZiB0aGUgTW9udGUgQ2FybG8gZXJyb3IgZXN0aW1hdGVzCgpgYGB7ciB9CiMgbGFiZWxzCmxhYnMzIDwtIGMoJ3RoZXRhMScsICd0aGV0YTInLAogICAgICAgICAgICc5NSUgaW50ZXJ2YWwgZm9yIE1DTUMgZXJyb3InLAogICAgICAgICAgICc5NSUgaW50ZXJ2YWwgZm9yIGluZGVwZW5kZW50IE1DJykKZ2dwbG90KCkgKwogIGdlb21fbGluZShkYXRhID0gZGZjYSwgYWVzKGl0ZXIsIHZhbHVlLCBjb2xvciA9IGdycCwgbGluZXR5cGUgPSBncnApKSArCiAgZ2VvbV9saW5lKGFlcygxOlNiLCAtMS45Ni9zcXJ0KDE6U2IqcmVmZikpLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX2xpbmUoYWVzKDE6U2IsIC0xLjk2L3NxcnQoMTpTYikpLCBsaW5ldHlwZSA9IDMpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLTEuNSwgMS41KSwgeGxpbSA9IGMoMCw0MDAwKSkgKwogIGxhYnModGl0bGUgPSAnQ3VtdWxhdGl2ZSBhdmVyYWdlcycpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygncmVkJywnYmx1ZScscmVwKCdibGFjaycsIDIpKSwgbGFiZWxzID0gbGFiczMpICsKICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYygxLCAxLCAyLCAzKSwgbGFiZWxzID0gbGFiczMpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKClNhbWUgYWdhaW4gd2l0aCByPTAuOTkKUGFyYW1ldGVycyBvZiBhIG5vcm1hbCBkaXN0cmlidXRpb24gdXNlZCBhcyBhIHRveSB0YXJnZXQgZGlzdHJpYnV0aW9uCgpgYGB7ciB9CnkxIDwtIDAKeTIgPC0gMApyIDwtIDAuOTkKU2lnbWEgPC0gZGlhZygyKQpTaWdtYVsxLCAyXSA8LSByClNpZ21hWzIsIDFdIDwtIHIKYGBgCgpNZXRyb3BvbGlzIHByb3Bvc2FsIGRpc3RyaWJ1dGlvbiBzY2FsZQoKYGBge3IgfQpzcCA8LSAwLjMKYGBgCgpTYW1wbGUgZnJvbSB0aGUgdG95IGRpc3RyaWJ1dGlvbiB0byB2aXN1YWxpemUgOTAlIEhQRAppbnRlcnZhbCB3aXRoIGdncGxvdCdzIHN0YXRfZWxsaXBzZSgpCgpgYGB7ciB9CmRmdCA8LSBkYXRhLmZyYW1lKG12cm5vcm0oMTAwMDAwLCBjKDAsIDApLCBTaWdtYSkpCmBgYAoKc2VlIEJEQTMgcC4gODUgZm9yIGhvdyB0byBjb21wdXRlIEhQRCBmb3IgbXVsdGl2YXJpYXRlIG5vcm1hbAppbiAyZC1jYXNlIGNvbnRvdXIgZm9yIDkwJSBIUEQgaXMgYW4gZWxsaXBzZSwgd2hvc2Ugc2VtaW1ham9yCmF4ZXMgY2FuIGJlIGNvbXB1dGVkIGZyb20gdGhlIGVpZ2VudmFsdWVzIG9mIHRoZSBjb3ZhcmlhbmNlCm1hdHJpeCBzY2FsZWQgYnkgYSB2YWx1ZSBzZWxlY3RlZCB0byBnZXQgZWxsaXBzZSBtYXRjaCB0aGUKZGVuc2l0eSBhdCB0aGUgZWRnZSBvZiA5MCUgSFBELiBBbmdsZSBvZiB0aGUgZWxsaXBzZSBjb3VsZCBiZQpjb21wdXRlZCBmcm9tIHRoZSBlaWdlbnZlY3RvcnMsIGJ1dCBzaW5jZSB0aGUgbWFyZ2luYWxzIGFyZSBzYW1lCndlIGtub3cgdGhhdCBhbmdsZSBpcyBwaS80ClN0YXJ0aW5nIHZhbHVlIG9mIHRoZSBjaGFpbgoKYGBge3IgfQp0MSA8LSAtMi41CnQyIDwtIDIuNQpgYGAKCk51bWJlciBvZiBpdGVyYXRpb25zLgoKYGBge3IgfQpNIDwtIDUwMDAKYGBgCgpJbnNlcnQgeW91ciBvd24gTWV0cm9wb2xpcyBzYW1wbGluZyBoZXJlCgpgYGB7ciB9CiMgQWxsb2NhdGUgbWVtb3J5IGZvciB0aGUgc2FtcGxlCnR0IDwtIG1hdHJpeChyZXAoMCwgMipNKSwgbmNvbCA9IDIpCnR0WzEsXSA8LSBjKHQxLCB0MikgICAgIyBTYXZlIHN0YXJ0aW5nIHBvaW50CiMgRm9yIGRlbW9uc3RyYXRpb24gbG9hZCBwcmUtY29tcHV0ZWQgdmFsdWVzCiMgUmVwbGFjZSB0aGlzIHdpdGggeW91ciBhbGdvcml0aG0hCiMgdHQgaXMgYSBNIHggMiBhcnJheSwgd2l0aCBNIGRyYXdzIG9mIGJvdGggdGhldGFfMSBhbmQgdGhldGFfMgpsb2FkKHJvb3QoImRlbW9zX2NoMTEiLCJkZW1vMTFfMmIuUkRhdGEiKSkKYGBgCgpUaGUgcmVzdCBpcyBmb3IgaWxsdXN0cmF0aW9uClRha2UgdGhlIGZpcnN0IDIwMCBkcmF3cwp0byBpbGx1c3RyYXRlIGhvdyB0aGUgc2FtcGxlciB3b3JrcwoKYGBge3IgfQpkZjEwMCA8LSBkYXRhLmZyYW1lKGlkPXJlcCgxLDEwMCksCiAgICAgICAgICAgICAgICAgICAgaXRlcj0xOjEwMCwgCiAgICAgICAgICAgICAgICAgICAgdGgxID0gdHRbMToxMDAsIDFdLAogICAgICAgICAgICAgICAgICAgIHRoMiA9IHR0WzE6MTAwLCAyXSwKICAgICAgICAgICAgICAgICAgICB0aDFsID0gYyh0dFsxLCAxXSwgdHRbMTooMTAwLTEpLCAxXSksCiAgICAgICAgICAgICAgICAgICAgdGgybCA9IGModHRbMSwgMl0sIHR0WzE6KDEwMC0xKSwgMl0pKQpgYGAKClRha2UgdGhlIGZpcnN0IDUwMDAgb2JzZXJ2YXRpb25zIGFmdGVyIHdhcm11cCBvZiA1MAoKYGBge3IgfQpTIDwtIDUwMDAKd2FybSA8LSA1MDAKZGZzIDwtIGRhdGEuZnJhbWUodGgxID0gdHRbKHdhcm0rMSk6UywgMV0sIHRoMiA9IHR0Wyh3YXJtKzEpOlMsIDJdKQpgYGAKClJlbW92ZSB3YXJtLXVwIHBlcmlvZCBvZiA1MCBmaXJzdCBkcmF3cyBsYXRlcgoKYGBge3IgfQojIGxhYmVscyBhbmQgZnJhbWUgaW5kaWNlcyBmb3IgdGhlIHBsb3QKbGFiczEgPC0gYygnRHJhd3MnLCAnU3RlcHMgb2YgdGhlIHNhbXBsZXInLCAnOTAlIEhQRCcpCnAxIDwtIGdncGxvdCgpICsKICBnZW9tX2ppdHRlcihkYXRhID0gZGYxMDAsIHdpZHRoPTAuMDUsIGhlaWdodD0wLjA1LAogICAgICAgICAgICAgYWVzKHRoMSwgdGgyLCBncm91cD1pZCwgY29sb3IgPScxJyksIGFscGhhPTAuMykgKwogIGdlb21fc2VnbWVudChkYXRhID0gZGYxMDAsIGFlcyh4ID0gdGgxLCB4ZW5kID0gdGgxbCwgY29sb3IgPSAnMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSB0aDIsIHllbmQgPSB0aDJsKSkgKwogIHN0YXRfZWxsaXBzZShkYXRhID0gZGZ0LCBhZXMoeCA9IFgxLCB5ID0gWDIsIGNvbG9yID0gJzMnKSwgbGV2ZWwgPSAwLjkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTQsIDQpLCB5bGltID0gYygtNCwgNCkpICsKICBsYWJzKHggPSAndGhldGExJywgeSA9ICd0aGV0YTInKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoJ3JlZCcsICdmb3Jlc3RncmVlbicsJ2JsdWUnKSwgbGFiZWxzID0gbGFiczEpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdCgKICAgIHNoYXBlID0gYygxNiwgTkEsIE5BKSwgbGluZXR5cGUgPSBjKDAsIDEsIDEpKSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKClRoZSBmb2xsb3dpbmcgZ2VuZXJhdGVzIGEgZ2lmIGFuaW1hdGlvbgpvZiB0aGUgc3RlcHMgb2YgdGhlIHNhbXBsZXIgKG1pZ2h0IHRha2UgMTAgc2Vjb25kcykuCgpgYGB7ciBNZXRyb3BvbGlzICgyKSwgcmVzdWx0cz0naGlkZScsIG1lc3NhZ2U9RkFMU0V9CmFuaW0gPC0gYW5pbWF0ZShwMSArICAgCiAgICAgICAgICAgICAgICAgIHRyYW5zaXRpb25fcmV2ZWFsKGFsb25nPWl0ZXIpICsgCiAgICAgICAgICAgICAgICAgIHNoYWRvd190cmFpbCgwLjAxKSkKYGBgCgpTaG93IHRoZSBhbmltYXRpb24KCmBgYHtyIH0KYW5pbQpgYGAKClBsb3QgdGhlIGZpbmFsIGZyYW1lCgpgYGB7ciB9CnAxCmBgYAoKc2hvdyAxMDAwIGRyYXdzIGFmdGVyIHRoZSB3YXJtLXVwCgpgYGB7ciB9CmxhYnMyIDwtIGMoJ0RyYXdzJywgJzkwJSBIUEQnKQpnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZGZzWzE6MTAwMCxdLAogICAgICAgICAgICAgYWVzKHRoMSwgdGgyLCBjb2xvciA9ICcxJyksIGFscGhhID0gMC4zKSArCiAgc3RhdF9lbGxpcHNlKGRhdGEgPSBkZnQsIGFlcyh4ID0gWDEsIHkgPSBYMiwgY29sb3IgPSAnMicpLCBsZXZlbCA9IDAuOSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtNCwgNCksIHlsaW0gPSBjKC00LCA0KSkgKwogIGxhYnMoeCA9ICd0aGV0YTEnLCB5ID0gJ3RoZXRhMicpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygnc3RlZWxibHVlJywgJ2JsdWUnKSwgbGFiZWxzID0gbGFiczIpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdCgKICAgIHNoYXBlID0gYygxNiwgTkEpLCBsaW5ldHlwZSA9IGMoMCwgMSksIGFscGhhID0gYygxLCAxKSkpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScsIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpzaG93IDQ1MDAgZHJhd3MgYWZ0ZXIgdGhlIHdhcm0tdXAKCmBgYHtyIH0KbGFiczIgPC0gYygnRHJhd3MnLCAnOTAlIEhQRCcpCmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkZnMsCiAgICAgICAgICAgICBhZXModGgxLCB0aDIsIGNvbG9yID0gJzEnKSwgYWxwaGEgPSAwLjMpICsKICBzdGF0X2VsbGlwc2UoZGF0YSA9IGRmdCwgYWVzKHggPSBYMSwgeSA9IFgyLCBjb2xvciA9ICcyJyksIGxldmVsID0gMC45KSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC00LCA0KSwgeWxpbSA9IGMoLTQsIDQpKSArCiAgbGFicyh4ID0gJ3RoZXRhMScsIHkgPSAndGhldGEyJykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCdzdGVlbGJsdWUnLCAnYmx1ZScpLCBsYWJlbHMgPSBsYWJzMikgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KAogICAgc2hhcGUgPSBjKDE2LCBOQSksIGxpbmV0eXBlID0gYygwLCAxKSwgYWxwaGEgPSBjKDEsIDEpKSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMjIyBDb252ZXJnZW5jZSBkaWFnbm9zdGljcwoKYGBge3IgfQpzdW1tYXJpc2VfZHJhd3MoZGZzLCBSaGF0PXJoYXRfYmFzaWMsIEVTUz1lc3NfYmFzaWMpCm5lZmYgPC0gYXBwbHkoZGZzLCAyLCBlc3NfYmFzaWMpCiMgYm90aCB0aGV0YSBoYXZlIG93biBuZWZmLCBidXQgZm9yIHBsb3R0aW5nIHRoZXNlIGFyZSBzbyBjbG9zZSB0byBlYWNoCiMgb3RoZXIsIHNvIHRoYXQgc2luZ2xlIHJlbGF0aXZlIGVmZmljaWVuY3kgdmFsdWUgaXMgdXNlZApyZWZmIDwtIG1lYW4obmVmZi9TKQpgYGAKCiMjIyBWaXN1YWwgY29udmVyZ2VuY2UgZGlhZ25vc3RpY3MKQ29sbGFwc2UgdGhlIGRhdGEgZnJhbWUgd2l0aCByb3cgbnVtYmVycyBhdWdtZW50ZWQKaW50byBrZXktdmFsdWUgcGFpcnMgZm9yIHZpc3VhbGl6aW5nIHRoZSBjaGFpbnMKCmBgYHtyIH0KZGZiIDwtIGRmcwpTYiA8LSBTLXdhcm0KZGZjaCA8LSB3aXRoaW4oZGZiLCBpdGVyIDwtIDE6U2IpICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9ICFpdGVyLCBuYW1lc190byA9ICJncnAiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKQpgYGAKCkFub3RoZXIgZGF0YSBmcmFtZSBmb3IgdmlzdWFsaXppbmcgdGhlIGVzdGltYXRlIG9mCnRoZSBhdXRvY29ycmVsYXRpb24gZnVuY3Rpb24KCmBgYHtyIH0KbmxhZ3MgPC0gMTAwCmRmYSA8LSBzYXBwbHkoZGZiLCBmdW5jdGlvbih4KSBhY2YoeCwgbGFnLm1heCA9IG5sYWdzLCBwbG90ID0gRikkYWNmKSAlPiUKICBkYXRhLmZyYW1lKGl0ZXIgPSAwOihubGFncykpICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9ICFpdGVyLCBuYW1lc190byA9ICJncnAiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKQpgYGAKCkEgdGhpcmQgZGF0YSBmcmFtZSB0byB2aXN1YWxpemUgdGhlIGN1bXVsYXRpdmUgYXZlcmFnZXMKYW5kIHRoZSA5NSUgaW50ZXJ2YWxzCgpgYGB7ciB9CmRmY2EgPC0gKGN1bXN1bShkZmIpIC8gKDE6U2IpKSAlPiUKICB3aXRoaW4oe2l0ZXIgPC0gMTpTYgogICAgICAgICAgdXBwaSA8LSAgMS45Ni9zcXJ0KDE6U2IpCiAgICAgICAgICB1cHAgPC0gMS45Ni8oc3FydCgxOlNiKnJlZmYpKX0pICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gIWl0ZXIsIG5hbWVzX3RvID0gImdycCIsIHZhbHVlc190byA9ICJ2YWx1ZSIpCmBgYAoKVmlzdWFsaXplIHRoZSBjaGFpbnMKCmBgYHtyIH0KZ2dwbG90KGRhdGEgPSBkZmNoKSArCiAgZ2VvbV9saW5lKGFlcyhpdGVyLCB2YWx1ZSwgY29sb3IgPSBncnApKSArCiAgbGFicyh0aXRsZSA9ICdUcmVuZHMnKSArCiAgc2NhbGVfY29sb3JfZGlzY3JldGUobGFiZWxzID0gYygndGhldGExJywndGhldGEyJykpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKClZpc3VhbGl6ZSB0aGUgZXN0aW1hdGUgb2YgdGhlIGF1dG9jb3JyZWxhdGlvbiBmdW5jdGlvbgoKYGBge3IgfQpnZ3Bsb3QoZGF0YSA9IGRmYSkgKwogIGdlb21fbGluZShhZXMoaXRlciwgdmFsdWUsIGNvbG9yID0gZ3JwKSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAwKSkgKwogIGxhYnModGl0bGUgPSAnQXV0b2NvcnJlbGF0aW9uIGZ1bmN0aW9uJykgKwogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKGxhYmVscyA9IGMoJ3RoZXRhMScsICd0aGV0YTInKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKVmlzdWFsaXplIHRoZSBlc3RpbWF0ZSBvZiB0aGUgTW9udGUgQ2FybG8gZXJyb3IgZXN0aW1hdGVzCgpgYGB7ciB9CiMgbGFiZWxzCmxhYnMzIDwtIGMoJ3RoZXRhMScsICd0aGV0YTInLAogICAgICAgICAgICc5NSUgaW50ZXJ2YWwgZm9yIE1DTUMgZXJyb3InLAogICAgICAgICAgICc5NSUgaW50ZXJ2YWwgZm9yIGluZGVwZW5kZW50IE1DJykKZ2dwbG90KCkgKwogIGdlb21fbGluZShkYXRhID0gZGZjYSwgYWVzKGl0ZXIsIHZhbHVlLCBjb2xvciA9IGdycCwgbGluZXR5cGUgPSBncnApKSArCiAgZ2VvbV9saW5lKGFlcygxOlNiLCAtMS45Ni9zcXJ0KDE6U2IqcmVmZikpLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX2xpbmUoYWVzKDE6U2IsIC0xLjk2L3NxcnQoMTpTYikpLCBsaW5ldHlwZSA9IDMpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLTEuNSwgMS41KSwgeGxpbSA9IGMoMCw0MDAwKSkgKwogIGxhYnModGl0bGUgPSAnQ3VtdWxhdGl2ZSBhdmVyYWdlcycpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygncmVkJywnYmx1ZScscmVwKCdibGFjaycsIDIpKSwgbGFiZWxzID0gbGFiczMpICsKICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYygxLCAxLCAyLCAzKSwgbGFiZWxzID0gbGFiczMpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKClNhbWUgYWdhaW4gd2l0aCBzcCA9IDEuNQoKYGBge3IgfQpzcCA9IDEuNQpgYGAKCkluc2VydCB5b3VyIG93biBNZXRyb3BvbGlzIHNhbXBsaW5nIGhlcmUKCmBgYHtyIH0KIyBBbGxvY2F0ZSBtZW1vcnkgZm9yIHRoZSBzYW1wbGUKdHQgPC0gbWF0cml4KHJlcCgwLCAyKk0pLCBuY29sID0gMikKdHRbMSxdIDwtIGModDEsIHQyKSAgICAjIFNhdmUgc3RhcnRpbmcgcG9pbnQKIyBGb3IgZGVtb25zdHJhdGlvbiBsb2FkIHByZS1jb21wdXRlZCB2YWx1ZXMKIyBSZXBsYWNlIHRoaXMgd2l0aCB5b3VyIGFsZ29yaXRobSEKIyB0dCBpcyBhIE0geCAyIGFycmF5LCB3aXRoIE0gZHJhd3Mgb2YgYm90aCB0aGV0YV8xIGFuZCB0aGV0YV8yCiMgU2VlIGluIHRoZSBlbmQgaG93IHRvIG1hbmlwdWxhdGUgUiBhcnJheXMgZm9yIGVhc3kgY29udmVyZ2VuY2UgZGlhZ25vc3RpY3MKbG9hZChyb290KCJkZW1vc19jaDExIiwiZGVtbzExXzJjLlJEYXRhIikpCmBgYAoKVGhlIHJlc3QgaXMgZm9yIGlsbHVzdHJhdGlvbgpUYWtlIHRoZSBmaXJzdCAyMDAgZHJhd3MKdG8gaWxsdXN0cmF0ZSBob3cgdGhlIHNhbXBsZXIgd29ya3MKCmBgYHtyIH0KZGYxMDAgPC0gZGF0YS5mcmFtZShpZD1yZXAoMSwxMDApLAogICAgICAgICAgICAgICAgICAgIGl0ZXI9MToxMDAsIAogICAgICAgICAgICAgICAgICAgIHRoMSA9IHR0WzE6MTAwLCAxXSwKICAgICAgICAgICAgICAgICAgICB0aDIgPSB0dFsxOjEwMCwgMl0sCiAgICAgICAgICAgICAgICAgICAgdGgxbCA9IGModHRbMSwgMV0sIHR0WzE6KDEwMC0xKSwgMV0pLAogICAgICAgICAgICAgICAgICAgIHRoMmwgPSBjKHR0WzEsIDJdLCB0dFsxOigxMDAtMSksIDJdKSkKYGBgCgpUYWtlIHRoZSBmaXJzdCA1MDAwIG9ic2VydmF0aW9ucyBhZnRlciB3YXJtdXAgb2YgNTAKCmBgYHtyIH0KUyA8LSA1MDAwCndhcm0gPC0gNTAwCmRmcyA8LSBkYXRhLmZyYW1lKHRoMSA9IHR0Wyh3YXJtKzEpOlMsIDFdLCB0aDIgPSB0dFsod2FybSsxKTpTLCAyXSkKYGBgCgpSZW1vdmUgd2FybS11cCBwZXJpb2Qgb2YgNTAgZmlyc3QgZHJhd3MgbGF0ZXIKCmBgYHtyIH0KIyBsYWJlbHMgYW5kIGZyYW1lIGluZGljZXMgZm9yIHRoZSBwbG90CmxhYnMxIDwtIGMoJ0RyYXdzJywgJ1N0ZXBzIG9mIHRoZSBzYW1wbGVyJywgJzkwJSBIUEQnKQpwMSA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9qaXR0ZXIoZGF0YSA9IGRmMTAwLCB3aWR0aD0wLjA1LCBoZWlnaHQ9MC4wNSwKICAgICAgICAgICAgIGFlcyh0aDEsIHRoMiwgZ3JvdXA9aWQsIGNvbG9yID0nMScpLCBhbHBoYT0wLjMpICsKICBnZW9tX3NlZ21lbnQoZGF0YSA9IGRmMTAwLCBhZXMoeCA9IHRoMSwgeGVuZCA9IHRoMWwsIGNvbG9yID0gJzInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gdGgyLCB5ZW5kID0gdGgybCkpICsKICBzdGF0X2VsbGlwc2UoZGF0YSA9IGRmdCwgYWVzKHggPSBYMSwgeSA9IFgyLCBjb2xvciA9ICczJyksIGxldmVsID0gMC45KSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC00LCA0KSwgeWxpbSA9IGMoLTQsIDQpKSArCiAgbGFicyh4ID0gJ3RoZXRhMScsIHkgPSAndGhldGEyJykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCdyZWQnLCAnZm9yZXN0Z3JlZW4nLCdibHVlJyksIGxhYmVscyA9IGxhYnMxKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoCiAgICBzaGFwZSA9IGMoMTYsIE5BLCBOQSksIGxpbmV0eXBlID0gYygwLCAxLCAxKSkpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScsIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpUaGUgZm9sbG93aW5nIGdlbmVyYXRlcyBhIGdpZiBhbmltYXRpb24Kb2YgdGhlIHN0ZXBzIG9mIHRoZSBzYW1wbGVyIChtaWdodCB0YWtlIDEwIHNlY29uZHMpLgoKYGBge3IgTWV0cm9wb2xpcyAoMyksIHJlc3VsdHM9J2hpZGUnLCBtZXNzYWdlPUZBTFNFfQphbmltIDwtIGFuaW1hdGUocDEgKyAgIAogICAgICAgICAgICAgICAgICB0cmFuc2l0aW9uX3JldmVhbChhbG9uZz1pdGVyKSArIAogICAgICAgICAgICAgICAgICBzaGFkb3dfdHJhaWwoMC4wMSkpCmBgYAoKU2hvdyB0aGUgYW5pbWF0aW9uCgpgYGB7ciB9CmFuaW0KYGBgCgpzaG93IDEwMDAgZHJhd3MgYWZ0ZXIgdGhlIHdhcm0tdXAKCmBgYHtyIH0KbGFiczIgPC0gYygnRHJhd3MnLCAnOTAlIEhQRCcpCmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkZnNbMToxMDAwLF0sCiAgICAgICAgICAgICBhZXModGgxLCB0aDIsIGNvbG9yID0gJzEnKSwgYWxwaGEgPSAwLjMpICsKICBzdGF0X2VsbGlwc2UoZGF0YSA9IGRmdCwgYWVzKHggPSBYMSwgeSA9IFgyLCBjb2xvciA9ICcyJyksIGxldmVsID0gMC45KSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC00LCA0KSwgeWxpbSA9IGMoLTQsIDQpKSArCiAgbGFicyh4ID0gJ3RoZXRhMScsIHkgPSAndGhldGEyJykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCdzdGVlbGJsdWUnLCAnYmx1ZScpLCBsYWJlbHMgPSBsYWJzMikgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KAogICAgc2hhcGUgPSBjKDE2LCBOQSksIGxpbmV0eXBlID0gYygwLCAxKSwgYWxwaGEgPSBjKDEsIDEpKSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCnNob3cgNDUwMCBkcmF3cyBhZnRlciB0aGUgd2FybS11cAoKYGBge3IgfQpsYWJzMiA8LSBjKCdEcmF3cycsICc5MCUgSFBEJykKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGRmcywKICAgICAgICAgICAgIGFlcyh0aDEsIHRoMiwgY29sb3IgPSAnMScpLCBhbHBoYSA9IDAuMykgKwogIHN0YXRfZWxsaXBzZShkYXRhID0gZGZ0LCBhZXMoeCA9IFgxLCB5ID0gWDIsIGNvbG9yID0gJzInKSwgbGV2ZWwgPSAwLjkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTQsIDQpLCB5bGltID0gYygtNCwgNCkpICsKICBsYWJzKHggPSAndGhldGExJywgeSA9ICd0aGV0YTInKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoJ3N0ZWVsYmx1ZScsICdibHVlJyksIGxhYmVscyA9IGxhYnMyKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoCiAgICBzaGFwZSA9IGMoMTYsIE5BKSwgbGluZXR5cGUgPSBjKDAsIDEpLCBhbHBoYSA9IGMoMSwgMSkpKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMjIENvbnZlcmdlbmNlIGRpYWdub3N0aWNzCgpgYGB7ciB9CnN1bW1hcmlzZV9kcmF3cyhkZnMpCm5lZmYgPC0gYXBwbHkoZGZzLCAyLCBlc3NfYmFzaWMpCiMgYm90aCB0aGV0YSBoYXZlIG93biBuZWZmLCBidXQgZm9yIHBsb3R0aW5nIHRoZXNlIGFyZSBzbyBjbG9zZSB0byBlYWNoCiMgb3RoZXIsIHNvIHRoYXQgc2luZ2xlIHJlbGF0aXZlIGVmZmljaWVuY3kgdmFsdWUgaXMgdXNlZApyZWZmIDwtIG1lYW4obmVmZi9TKQpgYGAKCiMjIyBWaXN1YWwgY29udmVyZ2VuY2UgZGlhZ25vc3RpY3MKQ29sbGFwc2UgdGhlIGRhdGEgZnJhbWUgd2l0aCByb3cgbnVtYmVycyBhdWdtZW50ZWQKaW50byBrZXktdmFsdWUgcGFpcnMgZm9yIHZpc3VhbGl6aW5nIHRoZSBjaGFpbnMKCmBgYHtyIH0KZGZiIDwtIGRmcwpTYiA8LSBTLXdhcm0KZGZjaCA8LSB3aXRoaW4oZGZiLCBpdGVyIDwtIDE6U2IpICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9ICFpdGVyLCBuYW1lc190byA9ICJncnAiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKQpgYGAKCkFub3RoZXIgZGF0YSBmcmFtZSBmb3IgdmlzdWFsaXppbmcgdGhlIGVzdGltYXRlIG9mCnRoZSBhdXRvY29ycmVsYXRpb24gZnVuY3Rpb24KCmBgYHtyIH0KbmxhZ3MgPC0gMTAwCmRmYSA8LSBzYXBwbHkoZGZiLCBmdW5jdGlvbih4KSBhY2YoeCwgbGFnLm1heCA9IG5sYWdzLCBwbG90ID0gRikkYWNmKSAlPiUKICBkYXRhLmZyYW1lKGl0ZXIgPSAwOihubGFncykpICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9ICFpdGVyLCBuYW1lc190byA9ICJncnAiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKQpgYGAKCkEgdGhpcmQgZGF0YSBmcmFtZSB0byB2aXN1YWxpemUgdGhlIGN1bXVsYXRpdmUgYXZlcmFnZXMKYW5kIHRoZSA5NSUgaW50ZXJ2YWxzCgpgYGB7ciB9CmRmY2EgPC0gKGN1bXN1bShkZmIpIC8gKDE6U2IpKSAlPiUKICB3aXRoaW4oe2l0ZXIgPC0gMTpTYgogICAgICAgICAgdXBwaSA8LSAgMS45Ni9zcXJ0KDE6U2IpCiAgICAgICAgICB1cHAgPC0gMS45Ni8oc3FydCgxOlNiKnJlZmYpKX0pICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gIWl0ZXIsIG5hbWVzX3RvID0gImdycCIsIHZhbHVlc190byA9ICJ2YWx1ZSIpCmBgYAoKVmlzdWFsaXplIHRoZSBjaGFpbnMKCmBgYHtyIH0KZ2dwbG90KGRhdGEgPSBkZmNoKSArCiAgZ2VvbV9saW5lKGFlcyhpdGVyLCB2YWx1ZSwgY29sb3IgPSBncnApKSArCiAgbGFicyh0aXRsZSA9ICdUcmVuZHMnKSArCiAgc2NhbGVfY29sb3JfZGlzY3JldGUobGFiZWxzID0gYygndGhldGExJywndGhldGEyJykpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKClZpc3VhbGl6ZSB0aGUgZXN0aW1hdGUgb2YgdGhlIGF1dG9jb3JyZWxhdGlvbiBmdW5jdGlvbgoKYGBge3IgfQpnZ3Bsb3QoZGF0YSA9IGRmYSkgKwogIGdlb21fbGluZShhZXMoaXRlciwgdmFsdWUsIGNvbG9yID0gZ3JwKSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAwKSkgKwogIGxhYnModGl0bGUgPSAnQXV0b2NvcnJlbGF0aW9uIGZ1bmN0aW9uJykgKwogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKGxhYmVscyA9IGMoJ3RoZXRhMScsICd0aGV0YTInKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKVmlzdWFsaXplIHRoZSBlc3RpbWF0ZSBvZiB0aGUgTW9udGUgQ2FybG8gZXJyb3IgZXN0aW1hdGVzCgpgYGB7ciB9CiMgbGFiZWxzCmxhYnMzIDwtIGMoJ3RoZXRhMScsICd0aGV0YTInLAogICAgICAgICAgICc5NSUgaW50ZXJ2YWwgZm9yIE1DTUMgZXJyb3InLAogICAgICAgICAgICc5NSUgaW50ZXJ2YWwgZm9yIGluZGVwZW5kZW50IE1DJykKZ2dwbG90KCkgKwogIGdlb21fbGluZShkYXRhID0gZGZjYSwgYWVzKGl0ZXIsIHZhbHVlLCBjb2xvciA9IGdycCwgbGluZXR5cGUgPSBncnApKSArCiAgZ2VvbV9saW5lKGFlcygxOlNiLCAtMS45Ni9zcXJ0KDE6U2IqcmVmZikpLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX2xpbmUoYWVzKDE6U2IsIC0xLjk2L3NxcnQoMTpTYikpLCBsaW5ldHlwZSA9IDMpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLTEuNSwgMS41KSwgeGxpbSA9IGMoMCw0MDAwKSkgKwogIGxhYnModGl0bGUgPSAnQ3VtdWxhdGl2ZSBhdmVyYWdlcycpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygncmVkJywnYmx1ZScscmVwKCdibGFjaycsIDIpKSwgbGFiZWxzID0gbGFiczMpICsKICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYygxLCAxLCAyLCAzKSwgbGFiZWxzID0gbGFiczMpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCg==