Hidden chapter requirements used in the book to set the plotting theme and load packages used in hidden code chunks:
Model tuning
Model capacity
Table 13.1:
library(knitr)
library(kableExtra)
data_frame(
Size = c("small", "medium", "large"),
`1` = c("16", "64", "256"),
`2` = c("16, 8", "64, 32", "256, 128"),
`3` = c("16, 8, 4", "64, 32, 16", "256, 128, 64")
) %>%
kable(align = "c", caption = "Model capacities assessed represented as number of layers and nodes per layer.") %>%
add_header_above(c(" ", "Hidden Layers" = 3)) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)
Model capacities assessed represented as number of layers and nodes per layer.
|
Hidden Layers |
Size |
1 |
2 |
3 |
small |
16 |
16, 8 |
16, 8, 4 |
medium |
64 |
64, 32 |
64, 32, 16 |
large |
256 |
256, 128 |
256, 128, 64 |
Figure 13.7:
Batch normalization
model_w_norm <- keras_model_sequential() %>%
# Network architecture with batch normalization
layer_dense(units = 256, activation = "relu", input_shape = ncol(mnist_x)) %>%
layer_batch_normalization() %>%
layer_dense(units = 128, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dense(units = 64, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dense(units = 10, activation = "softmax") %>%
# Backpropagation
compile(
loss = "categorical_crossentropy",
optimizer = optimizer_rmsprop(),
metrics = c("accuracy")
)
Figure 13.8:
# One layer models -------------------------------------------------------------
# small capacity model
`1 layer_small` <- keras_model_sequential() %>%
layer_dense(units = 16, activation = "relu", input_shape = ncol(mnist_x)) %>%
layer_batch_normalization() %>%
layer_dense(units = 10, activation = "softmax") %>%
compiler() %>%
trainer()
# medium
`1 layer_medium` <- keras_model_sequential() %>%
layer_dense(units = 64, activation = "relu", input_shape = ncol(mnist_x)) %>%
layer_batch_normalization() %>%
layer_dense(units = 10, activation = "softmax") %>%
compiler() %>%
trainer()
# large
`1 layer_large` <- keras_model_sequential() %>%
layer_dense(units = 256, activation = "relu", input_shape = ncol(mnist_x)) %>%
layer_batch_normalization() %>%
layer_dense(units = 10, activation = "softmax") %>%
compiler() %>%
trainer()
# Two layer models -------------------------------------------------------------
# small capacity model
`2 layer_small` <- keras_model_sequential() %>%
layer_dense(units = 16, activation = "relu", input_shape = ncol(mnist_x)) %>%
layer_batch_normalization() %>%
layer_dense(units = 8, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dense(units = 10, activation = "softmax") %>%
compiler() %>%
trainer()
# medium
`2 layer_medium` <- keras_model_sequential() %>%
layer_dense(units = 64, activation = "relu", input_shape = ncol(mnist_x)) %>%
layer_batch_normalization() %>%
layer_dense(units = 32, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dense(units = 10, activation = "softmax") %>%
compiler() %>%
trainer()
# large
`2 layer_large` <- keras_model_sequential() %>%
layer_dense(units = 256, activation = "relu", input_shape = ncol(mnist_x)) %>%
layer_batch_normalization() %>%
layer_dense(units = 128, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dense(units = 10, activation = "softmax") %>%
compiler() %>%
trainer()
# Three layer models -------------------------------------------------------------
# small capacity model
`3 layer_small` <- keras_model_sequential() %>%
layer_dense(units = 16, activation = "relu", input_shape = ncol(mnist_x)) %>%
layer_batch_normalization() %>%
layer_dense(units = 8, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dense(units = 4, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dense(units = 10, activation = "softmax") %>%
compiler() %>%
trainer()
# medium
`3 layer_medium` <- keras_model_sequential() %>%
layer_dense(units = 64, activation = "relu", input_shape = ncol(mnist_x)) %>%
layer_batch_normalization() %>%
layer_dense(units = 32, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dense(units = 16, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dense(units = 10, activation = "softmax") %>%
compiler() %>%
trainer()
# large
`3 layer_large` <- keras_model_sequential() %>%
layer_dense(units = 256, activation = "relu", input_shape = ncol(mnist_x)) %>%
layer_batch_normalization() %>%
layer_dense(units = 128, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dense(units = 64, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dense(units = 10, activation = "softmax") %>%
compiler() %>%
trainer()
models <- ls(pattern = "layer_")
df_batch <- models %>%
map(get) %>%
map(~ data.frame(
`Validation error` = .$metrics$val_loss,
`Training error` = .$metrics$loss,
epoch = seq_len(.$params$epoch)
)) %>%
map2_df(models, ~ mutate(.x, model = .y)) %>%
separate(model, into = c("Middle layers", "Number of nodes"), sep = "_") %>%
gather(Validation, Loss, Validation.error:Training.error) %>%
mutate(
Validation = str_replace_all(Validation, "\\.", " "),
`Number of nodes` = factor(`Number of nodes`, levels = c("small", "medium", "large")),
`Batch normalization` = TRUE
)
Figure 13.8 (continued):
df2 <- df %>%
mutate(`Batch normalization` = FALSE) %>%
bind_rows(df_batch) %>%
filter(Validation == "Validation error")
best <- df2 %>%
filter(Validation == "Validation error") %>%
group_by(`Middle layers`, `Number of nodes`) %>%
filter(Loss == min(Loss)) %>%
mutate(label = paste("Min validation error:", round(Loss, 4)))
ggplot(df2, aes(epoch, Loss, color = `Batch normalization`)) +
geom_text(data = best, aes(x = 25, y = 0.95, label = label), size = 4, hjust = 1, vjust = 1) +
geom_point() +
geom_line() +
facet_grid(`Number of nodes` ~ `Middle layers`, scales = "free_y") +
scale_y_continuous(limits = c(0, 1)) +
xlab("Epoch") +
scale_color_discrete("Batch normalization") +
theme(legend.position = "top")
Regularization
model_w_reg <- keras_model_sequential() %>%
# Network architecture with L1 regularization and batch normalization
layer_dense(units = 256, activation = "relu", input_shape = ncol(mnist_x),
kernel_regularizer = regularizer_l2(0.001)) %>%
layer_batch_normalization() %>%
layer_dense(units = 128, activation = "relu",
kernel_regularizer = regularizer_l2(0.001)) %>%
layer_batch_normalization() %>%
layer_dense(units = 64, activation = "relu",
kernel_regularizer = regularizer_l2(0.001)) %>%
layer_batch_normalization() %>%
layer_dense(units = 10, activation = "softmax") %>%
# Backpropagation
compile(
loss = "categorical_crossentropy",
optimizer = optimizer_rmsprop(),
metrics = c("accuracy")
)
model_w_drop <- keras_model_sequential() %>%
# Network architecture with 20% dropout
layer_dense(units = 256, activation = "relu", input_shape = ncol(mnist_x)) %>%
layer_dropout(rate = 0.2) %>%
layer_dense(units = 128, activation = "relu") %>%
layer_dropout(rate = 0.2) %>%
layer_dense(units = 64, activation = "relu") %>%
layer_dropout(rate = 0.2) %>%
layer_dense(units = 10, activation = "softmax") %>%
# Backpropagation
compile(
loss = "categorical_crossentropy",
optimizer = optimizer_rmsprop(),
metrics = c("accuracy")
)
Figure 13.9:
fit_baseline <- keras_model_sequential() %>%
layer_dense(units = 256, activation = "relu", input_shape = ncol(mnist_x)) %>%
layer_dense(units = 128, activation = "relu") %>%
layer_dense(units = 64, activation = "relu") %>%
layer_dense(units = 10, activation = "softmax") %>%
compiler() %>%
fit(
x = mnist_x,
y = mnist_y,
epochs = 35,
batch_size = 128,
validation_split = 0.2,
verbose = FALSE
)
fit_norm <- keras_model_sequential() %>%
layer_dense(units = 256, activation = "relu", input_shape = ncol(mnist_x)) %>%
layer_batch_normalization() %>%
layer_dense(units = 128, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dense(units = 64, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dense(units = 10, activation = "softmax") %>%
compiler() %>%
fit(
x = mnist_x,
y = mnist_y,
epochs = 35,
batch_size = 128,
validation_split = 0.2,
verbose = FALSE
)
fit_reg <- keras_model_sequential() %>%
layer_dense(units = 256, activation = "relu", input_shape = ncol(mnist_x)) %>%
layer_batch_normalization() %>%
layer_dropout(rate = 0.4) %>%
layer_dense(units = 128, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dropout(rate = 0.3) %>%
layer_dense(units = 64, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dropout(rate = 0.2) %>%
layer_dense(units = 10, activation = "softmax") %>%
compiler() %>%
fit(
x = mnist_x,
y = mnist_y,
epochs = 35,
batch_size = 128,
validation_split = 0.2,
verbose = FALSE
)
models <- ls(pattern = "fit_")
df_reg <- models %>%
map(get) %>%
map(~ data.frame(
`Validation error` = .$metrics$val_loss,
`Training error` = .$metrics$loss,
epoch = seq_len(.$params$epoch)
)) %>%
map2_df(models, ~ mutate(.x, model = .y)) %>%
mutate(Model = case_when(
model == "fit_baseline" ~ "Baseline",
model == "fit_norm" ~ "Baseline + batch normalization",
model == "fit_reg" ~ "Baseline + batch normalization + dropout"
)) %>%
gather(Validation, Loss, Validation.error:Training.error)
Figure 13.9 (continued):
baseline <- df %>%
filter(`Middle layers` == "3 layer", `Number of nodes` == "large") %>%
mutate(Model = "Baseline") %>%
select(epoch, Model, Validation, Loss)
batch <- df_batch %>%
filter(`Middle layers` == "3 layer", `Number of nodes` == "large") %>%
mutate(Model = "Baseline + batch normalization") %>%
select(epoch, Model, Validation, Loss)
df_reg <- df_reg %>%
select(-model) %>%
filter(Model == "Baseline + batch normalization + dropout") %>%
mutate(Validation = stringr::str_replace_all(Validation, "\\.", " ")) %>%
bind_rows(batch, baseline)
best <- df_reg %>%
filter(Validation == "Validation error") %>%
group_by(Model) %>%
filter(Loss == min(Loss)) %>%
mutate(label = paste("Min validation error:", round(Loss, 4)))
ggplot(df_reg, aes(epoch, Loss)) +
geom_text(data = best, aes(x = 35, y = 0.49, label = label), size = 4, hjust = 1, vjust = 1) +
geom_point(aes(color = Validation)) +
geom_line(aes(color = Validation)) +
facet_wrap(~ Model) +
xlab("Epoch") +
theme(legend.title = element_blank(),
legend.position = "top")
Adjust learning rate
Figure 13.10:
set.seed(123) # for reproducibility
x <- seq(from = 0, to = 4.25 * pi, length = 100)
y <- sin(x) - x*.2
df <- data.frame(x, y)
global <- filter(df, y == min(y))
local <- df %>%
filter(x < 6) %>%
filter(y == min(y))
ggplot(df, aes(x, y)) +
geom_line(size = 1.5, alpha = 0.5) +
scale_y_continuous("Loss function", expand = c(0, 0.08)) +
scale_x_continuous("Parameter value", expand = c(0.08, 0)) +
theme(
axis.ticks = element_blank(),
axis.text = element_blank(),
panel.grid = element_blank(),
panel.border = element_blank()
) +
geom_segment(aes(x = min(x) - 0.5, xend = max(x) , y = min(y) - 0.5, yend = min(y) - 0.5),
arrow = arrow(length = unit(0.2, "cm")), color = "grey30") +
geom_segment(aes(x = min(x) - 0.5, xend = min(x) - 0.5 , y = min(y) - 0.5, yend = max(y)),
arrow = arrow(length = unit(0.2, "cm")), color = "grey30") +
geom_point(data = global, aes(x, y), size = 4, shape = 21, fill = "yellow") +
geom_point(data = local, aes(x, y), size = 4, shape = 21, fill = "blue") +
annotate("text", x = global$x, y = global$y, label = "Global minimum", vjust = 2, size = 3) +
annotate("text", x = local$x, y = local$y, label = "Local minimum", vjust = 2, size = 3)
model_w_adj_lrn <- keras_model_sequential() %>%
layer_dense(units = 256, activation = "relu", input_shape = ncol(mnist_x)) %>%
layer_batch_normalization() %>%
layer_dropout(rate = 0.4) %>%
layer_dense(units = 128, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dropout(rate = 0.3) %>%
layer_dense(units = 64, activation = "relu") %>%
layer_batch_normalization() %>%
layer_dropout(rate = 0.2) %>%
layer_dense(units = 10, activation = "softmax") %>%
compile(
loss = 'categorical_crossentropy',
optimizer = optimizer_adam(),
metrics = c('accuracy')
) %>%
fit(
x = mnist_x,
y = mnist_y,
epochs = 35,
batch_size = 128,
validation_split = 0.2,
callbacks = list(
callback_early_stopping(patience = 5),
callback_reduce_lr_on_plateau(factor = 0.05)
),
verbose = FALSE
)
model_w_adj_lrn
Trained on 48,000 samples (batch_size=128, epochs=25)
Final epoch (plot to see history):
loss: 0.05384
accuracy: 0.9827
val_loss: 0.07691
val_accuracy: 0.9806
lr: 0.001
# Optimal
min(model_w_adj_lrn$metrics$val_loss)
[1] 0.07305577
max(model_w_adj_lrn$metrics$val_acc)
[1] 0.9805833
# Learning rate
plot(model_w_adj_lrn)
Grid Search
This grid search requires the mnist-grid-search.R file to be located a /scrips
directory within your current working directory.
WARNING: The following grid search took us over 1.5 hours to run!
# Run various combinations of dropout1 and dropout2
runs <- tuning_run("scripts/mnist-grid-search.R",
flags = list(
nodes1 = c(64, 128, 256),
nodes2 = c(64, 128, 256),
nodes3 = c(64, 128, 256),
dropout1 = c(0.2, 0.3, 0.4),
dropout2 = c(0.2, 0.3, 0.4),
dropout3 = c(0.2, 0.3, 0.4),
optimizer = c("rmsprop", "adam"),
lr_annealing = c(0.1, 0.05)
),
sample = 0.05
)
runs %>%
filter(metric_val_loss == min(metric_val_loss)) %>%
glimpse()
LS0tCnRpdGxlOiAiQ2hhcHRlciAxMzogRGVlcCBMZWFybmluZyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKX19Ob3RlX186IFNvbWUgcmVzdWx0cyBtYXkgZGlmZmVyIGZyb20gdGhlIGhhcmQgY29weSBib29rIGR1ZSB0byB0aGUgY2hhbmdpbmcgb2Ygc2FtcGxpbmcgcHJvY2VkdXJlcyBpbnRyb2R1Y2VkIGluIFIgMy42LjAuIFNlZSBodHRwOi8vYml0Lmx5LzM1RDFTVzcgZm9yIG1vcmUgZGV0YWlscy4gQWNjZXNzIGFuZCBydW4gdGhlIHNvdXJjZSBjb2RlIGZvciB0aGlzIG5vdGVib29rIFtoZXJlXShodHRwczovL3JzdHVkaW8uY2xvdWQvcHJvamVjdC84MDExODUpLiBEbyB0byBvdXRwdXQgc2l6ZSwgbW9zdCBvZiB0aGlzCmNoYXB0ZXIncyBjb2RlIGNodW5rcyBzaG91bGQgbm90IGJlIHJhbiBvbiBSU3R1ZGlvIENsb3VkLgoKSGlkZGVuIGNoYXB0ZXIgcmVxdWlyZW1lbnRzIHVzZWQgaW4gdGhlIGJvb2sgdG8gc2V0IHRoZSBwbG90dGluZyB0aGVtZSBhbmQgbG9hZCBwYWNrYWdlcyB1c2VkIGluIGhpZGRlbiBjb2RlIGNodW5rczoKCmBgYHtyIHNldHVwfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShwdXJycikKCiMgU2V0IHRoZSBncmFwaGljYWwgdGhlbWUKZ2dwbG90Mjo6dGhlbWVfc2V0KGdncGxvdDI6OnRoZW1lX2xpZ2h0KCkpCgojIFNldCBnbG9iYWwga25pdHIgY2h1bmsgb3B0aW9ucwprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgY2FjaGUgPSBGQUxTRSwKICB3YXJuaW5nID0gRkFMU0UsIAogIG1lc3NhZ2UgPSBGQUxTRQopCmBgYAoKIyMgUHJlcmVxdWlzaXRlcwoKX19Ob3RlOl9fIHlvdSBtYXkgbmVlZCB0byBpbnN0YWxsIHRlbnNvcmZsb3cgaWYgeW91IGhhdmUgbm90IGRvbmUgc28gYmVmb3JlLiBZb3UKY2FuIGRvIHNvIGJ5IGV4ZWN1dGluZyBga2VyYXM6Omluc3RhbGxfa2VyYXMoKWAuIFNlZSBodHRwczovL2tlcmFzLnJzdHVkaW8uY29tL2FydGljbGVzL2dldHRpbmdfc3RhcnRlZC5odG1sCmZvciBtb3JlIGRldGFpbHMuCgpgYGB7ciBwa2ctcmVxLCBjYWNoZT1GQUxTRX0KIyBIZWxwZXIgcGFja2FnZXMKbGlicmFyeShkcGx5cikgICAgICAgICAjIGZvciBiYXNpYyBkYXRhIHdyYW5nbGluZwoKIyBNb2RlbGluZyBwYWNrYWdlcwpsaWJyYXJ5KGtlcmFzKSAgICAgICAgICMgZm9yIGZpdHRpbmcgRE5OcwpsaWJyYXJ5KHRmcnVucykgICAgICAgICMgZm9yIGFkZGl0aW9uYWwgZ3JpZCBzZWFyY2ggJiBtb2RlbCB0cmFpbmluZyBmdW5jdGlvbnMKCiMgTW9kZWxpbmcgaGVscGVyIHBhY2thZ2UgLSBub3QgbmVjZXNzYXJ5IGZvciByZXByb2R1Y2liaWxpdHkKbGlicmFyeSh0ZmVzdGltYXRvcnMpICAjIHByb3ZpZGVzIGdyaWQgc2VhcmNoICYgbW9kZWwgdHJhaW5pbmcgaW50ZXJmYWNlCmBgYAoKV2UnbGwgdXNlIHRoZSBNTklTVCBkYXRhIHRvIGlsbHVzdHJhdGUgdmFyaW91cyBETk4gY29uY2VwdHMuCgpgYGB7ciBETC1wcmVwLW1uaXN0LWRhdGF9CiMgSW1wb3J0IE1OSVNUIHRyYWluaW5nIGRhdGEKbW5pc3QgPC0gZHNsYWJzOjpyZWFkX21uaXN0KCkKbW5pc3RfeCA8LSBtbmlzdCR0cmFpbiRpbWFnZXMKbW5pc3RfeSA8LSBtbmlzdCR0cmFpbiRsYWJlbHMKCiMgUmVuYW1lIGNvbHVtbnMgYW5kIHN0YW5kYXJkaXplIGZlYXR1cmUgdmFsdWVzCmNvbG5hbWVzKG1uaXN0X3gpIDwtIHBhc3RlMCgiViIsIDE6bmNvbChtbmlzdF94KSkKbW5pc3RfeCA8LSBtbmlzdF94IC8gMjU1CgojIE9uZS1ob3QgZW5jb2RlIHJlc3BvbnNlCm1uaXN0X3kgPC0gdG9fY2F0ZWdvcmljYWwobW5pc3RfeSwgMTApCmBgYAoKIyMgV2h5IGRlZXAgbGVhcm5pbmcKCkZpZ3VyZSAxMy4xOgoKYGBge3IgbW5pc3QtbnVtYmVycywgZWNobz1UUlVFLCBvdXQuaGVpZ2h0PSI2MCUiLCBvdXQud2lkdGg9IjYwJSIsIGZpZy5jYXA9IlNhbXBsZSBpbWFnZXMgZnJvbSBNTklTVCB0ZXN0IGRhdGFzZXQgXFxjaXRlcHt3aWtpTU5JU1R9LiJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJpbWFnZXMvZGlnaXRzLnBuZyIpCmBgYAoKRmlndXJlIDEzLjI6CgpgYGB7ciBiYXNpYy1uZXVyYWwtbmV0LCBvdXQuaGVpZ2h0PSI2MCUiLCBvdXQud2lkdGg9IjYwJSIsIGVjaG89VFJVRSwgZmlnLmNhcD0iUmVwcmVzZW50YXRpb24gb2YgYSBzaW1wbGUgZmVlZGZvcndhcmQgbmV1cmFsIG5ldHdvcmsuIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImltYWdlcy9iYXNpYy1uZXVyYWwtbmV0LnBuZyIpCmBgYAoKRmlndXJlIDEzLjM6CgpgYGB7ciBkZWVwLW5ldXJhbC1uZXQsIGVjaG89VFJVRSwgb3V0LmhlaWdodD0iNjAlIiwgb3V0LndpZHRoPSI2MCUiLCBmaWcuY2FwPSJSZXByZXNlbnRhdGlvbiBvZiBhIGRlZXAgZmVlZGZvcndhcmQgbmV1cmFsIG5ldHdvcmsuIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImltYWdlcy9kZWVwX25ldXJhbC1uZXQucG5nIikKYGBgCgojIyBGZWVkZm9yd2FyZCBETk5zCgpGaWd1cmUgMTMuNDoKCmBgYHtyIG1scC1uZXR3b3JrLCBlY2hvPVRSVUUsIG91dC5oZWlnaHQ9IjYwJSIsIG91dC53aWR0aD0iNjAlIiwgZmlnLmNhcD0iRmVlZGZvcndhcmQgbmV1cmFsIG5ldHdvcmsuIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImltYWdlcy9tbHBfbmV0d29yay5wbmciKQpgYGAKCiMjIE5ldHdvcmsgYXJjaGl0ZWN0dXJlCgojIyMjIEltcGxlbWVudGF0aW9uCgpUaGUgX19rZXJhc19fIHBhY2thZ2UgYWxsb3dzIHVzIHRvIGRldmVsb3Agb3VyIG5ldHdvcmsgd2l0aCBhIGxheWVyaW5nIGFwcHJvYWNoLiAgRmlyc3QsIHdlIGluaXRpYXRlIG91ciBzZXF1ZW50aWFsIGZlZWRmb3J3YXJkIEROTiBhcmNoaXRlY3R1cmUgd2l0aCBga2VyYXNfbW9kZWxfc2VxdWVudGlhbCgpYCBhbmQgdGhlbiBhZGQgc29tZSBkZW5zZSBsYXllcnMuICBUaGlzIGV4YW1wbGUgY3JlYXRlcyB0d28gaGlkZGVuIGxheWVycywgdGhlIGZpcnN0IHdpdGggMTI4IG5vZGVzIGFuZCB0aGUgc2Vjb25kIHdpdGggNjQsIGZvbGxvd2VkIGJ5IGFuIG91dHB1dCBsYXllciB3aXRoIDEwIG5vZGVzLiAgT25lIHRoaW5nIHRvIHBvaW50IG91dCBpcyB0aGF0IHRoZSBmaXJzdCBsYXllciBuZWVkcyB0aGUgYGlucHV0X3NoYXBlYCBhcmd1bWVudCB0byBlcXVhbCB0aGUgbnVtYmVyIG9mIGZlYXR1cmVzIGluIHlvdXIgZGF0YTsgaG93ZXZlciwgdGhlIHN1Y2Nlc3NpdmUgbGF5ZXJzIGFyZSBhYmxlIHRvIGR5bmFtaWNhbGx5IGludGVycHJldCB0aGUgbnVtYmVyIG9mIGV4cGVjdGVkIGlucHV0cyBiYXNlZCBvbiB0aGUgcHJldmlvdXMgbGF5ZXIuCgpgYGB7ciBhcmNoaXRlY3R1cmV9Cm1vZGVsIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDEyOCwgaW5wdXRfc2hhcGUgPSBuY29sKG1uaXN0X3gpKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDY0KSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDEwKQpgYGAKCiMjIyMgQWN0aXZhdGlvbiBmdW5jdGlvbnMKCkZpZ3VyZSAxMy41OiAgCgpgYGB7ciBwZXJjZXB0cm9uLW5vZGUsIGVjaG89VFJVRSwgb3V0LmhlaWdodD0iNjAlIiwgb3V0LndpZHRoPSI2MCUiLCBmaWcuY2FwPSJGbG93IG9mIGluZm9ybWF0aW9uIGluIGFuIGFydGlmaWNpYWwgbmV1cm9uLiJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJpbWFnZXMvcGVyY2VwdHJvbl9ub2RlLnBuZyIpCmBgYAoKIyMjIyBJbXBsZW1lbnRhdGlvbgoKYGBge3IgYWN0aXZhdGlvbi1hcmd1bWVudHN9Cm1vZGVsIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDEyOCwgYWN0aXZhdGlvbiA9ICJyZWx1IiwgaW5wdXRfc2hhcGUgPSBuY29sKG1uaXN0X3gpKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDY0LCBhY3RpdmF0aW9uID0gInJlbHUiKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDEwLCBhY3RpdmF0aW9uID0gInNvZnRtYXgiKQpgYGAKCgojIyBCYWNrcHJvcGFnYXRpb24KCmBgYHtyIGJhY2twcm9wYWdhdGlvbn0KbW9kZWwgPC0ga2VyYXNfbW9kZWxfc2VxdWVudGlhbCgpICU+JQogIAogICMgTmV0d29yayBhcmNoaXRlY3R1cmUKICBsYXllcl9kZW5zZSh1bml0cyA9IDEyOCwgYWN0aXZhdGlvbiA9ICJyZWx1IiwgaW5wdXRfc2hhcGUgPSBuY29sKG1uaXN0X3gpKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDY0LCBhY3RpdmF0aW9uID0gInJlbHUiKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDEwLCBhY3RpdmF0aW9uID0gInNvZnRtYXgiKSAlPiUKICAKICAjIEJhY2twcm9wYWdhdGlvbgogIGNvbXBpbGUoCiAgICBsb3NzID0gJ2NhdGVnb3JpY2FsX2Nyb3NzZW50cm9weScsCiAgICBvcHRpbWl6ZXIgPSBvcHRpbWl6ZXJfcm1zcHJvcCgpLAogICAgbWV0cmljcyA9IGMoJ2FjY3VyYWN5JykKICApCmBgYAoKCiMjIE1vZGVsIHRyYWluaW5nCgpgYGB7ciBtb2RlbC10cmFpbiwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9NywgZmlnLmNhcD0iVHJhaW5pbmcgYW5kIHZhbGlkYXRpb24gcGVyZm9ybWFuY2Ugb3ZlciAyNSBlcG9jaHMuIn0KIyBUcmFpbiB0aGUgbW9kZWwKZml0MSA8LSBtb2RlbCAlPiUKICBmaXQoCiAgICB4ID0gbW5pc3RfeCwKICAgIHkgPSBtbmlzdF95LAogICAgZXBvY2hzID0gMjUsCiAgICBiYXRjaF9zaXplID0gMTI4LAogICAgdmFsaWRhdGlvbl9zcGxpdCA9IDAuMiwKICAgIHZlcmJvc2UgPSBGQUxTRQogICkKCiMgRGlzcGxheSBvdXRwdXQKZml0MQpwbG90KGZpdDEpCmBgYAoKCiMjIE1vZGVsIHR1bmluZwoKIyMjIE1vZGVsIGNhcGFjaXR5CgpUYWJsZSAxMy4xOgoKYGBge3Igb25lLWhvdCwgZWNobz1UUlVFfQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCgpkYXRhX2ZyYW1lKAogIFNpemUgPSBjKCJzbWFsbCIsICJtZWRpdW0iLCAibGFyZ2UiKSwgCiAgYDFgID0gYygiMTYiLCAiNjQiLCAiMjU2IiksCiAgYDJgID0gYygiMTYsIDgiLCAiNjQsIDMyIiwgIjI1NiwgMTI4IiksCiAgYDNgID0gYygiMTYsIDgsIDQiLCAiNjQsIDMyLCAxNiIsICIyNTYsIDEyOCwgNjQiKQopICU+JQogIGthYmxlKGFsaWduID0gImMiLCBjYXB0aW9uID0gIk1vZGVsIGNhcGFjaXRpZXMgYXNzZXNzZWQgcmVwcmVzZW50ZWQgYXMgbnVtYmVyIG9mIGxheWVycyBhbmQgbm9kZXMgcGVyIGxheWVyLiIpICU+JQogIGFkZF9oZWFkZXJfYWJvdmUoYygiICIsICJIaWRkZW4gTGF5ZXJzIiA9IDMpKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gRkFMU0UpCmBgYAoKRmlndXJlIDEzLjc6CgpgYGB7ciBtb2RlbC1jYXBhY2l0eX0KY29tcGlsZXIgPC0gZnVuY3Rpb24ob2JqZWN0KSB7CiAgY29tcGlsZSgKICAgIG9iamVjdCwKICAgIGxvc3MgPSAnY2F0ZWdvcmljYWxfY3Jvc3NlbnRyb3B5JywKICAgIG9wdGltaXplciA9IG9wdGltaXplcl9ybXNwcm9wKCksCiAgICBtZXRyaWNzID0gYygnYWNjdXJhY3knKQogICkKfQoKdHJhaW5lciA8LSBmdW5jdGlvbihvYmplY3QpIHsKICBmaXQoCiAgICBvYmplY3QsCiAgICB4ID0gbW5pc3RfeCwKICAgIHkgPSBtbmlzdF95LAogICAgZXBvY2hzID0gMjUsCiAgICBiYXRjaF9zaXplID0gMTI4LAogICAgdmFsaWRhdGlvbl9zcGxpdCA9IC4yLAogICAgdmVyYm9zZSA9IEZBTFNFCiAgICApCn0KCiMgT25lIGxheWVyIG1vZGVscyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgc21hbGwgY2FwYWNpdHkgbW9kZWwKYDEgbGF5ZXJfc21hbGxgIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDE2LCBhY3RpdmF0aW9uID0gInJlbHUiLCBpbnB1dF9zaGFwZSA9IG5jb2wobW5pc3RfeCkpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpICU+JQogIGNvbXBpbGVyKCkgJT4lCiAgdHJhaW5lcigpCgojIG1lZGl1bQpgMSBsYXllcl9tZWRpdW1gIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDY0LCBhY3RpdmF0aW9uID0gInJlbHUiLCBpbnB1dF9zaGFwZSA9IG5jb2wobW5pc3RfeCkpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpICU+JQogIGNvbXBpbGVyKCkgJT4lCiAgdHJhaW5lcigpCgojIGxhcmdlCmAxIGxheWVyX2xhcmdlYCA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAyNTYsIGFjdGl2YXRpb24gPSAicmVsdSIsIGlucHV0X3NoYXBlID0gbmNvbChtbmlzdF94KSkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMCwgYWN0aXZhdGlvbiA9ICJzb2Z0bWF4IikgJT4lCiAgY29tcGlsZXIoKSAlPiUKICB0cmFpbmVyKCkKCiMgVHdvIGxheWVyIG1vZGVscyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgc21hbGwgY2FwYWNpdHkgbW9kZWwKYDIgbGF5ZXJfc21hbGxgIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDE2LCBhY3RpdmF0aW9uID0gInJlbHUiLCBpbnB1dF9zaGFwZSA9IG5jb2wobW5pc3RfeCkpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gOCwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMCwgYWN0aXZhdGlvbiA9ICJzb2Z0bWF4IikgJT4lCiAgY29tcGlsZXIoKSAlPiUKICB0cmFpbmVyKCkKCiMgbWVkaXVtCmAyIGxheWVyX21lZGl1bWAgPC0ga2VyYXNfbW9kZWxfc2VxdWVudGlhbCgpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gNjQsIGFjdGl2YXRpb24gPSAicmVsdSIsIGlucHV0X3NoYXBlID0gbmNvbChtbmlzdF94KSkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAzMiwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMCwgYWN0aXZhdGlvbiA9ICJzb2Z0bWF4IikgJT4lCiAgY29tcGlsZXIoKSAlPiUKICB0cmFpbmVyKCkKCiMgbGFyZ2UKYDIgbGF5ZXJfbGFyZ2VgIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDI1NiwgYWN0aXZhdGlvbiA9ICJyZWx1IiwgaW5wdXRfc2hhcGUgPSBuY29sKG1uaXN0X3gpKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDEyOCwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMCwgYWN0aXZhdGlvbiA9ICJzb2Z0bWF4IikgJT4lCiAgY29tcGlsZXIoKSAlPiUKICB0cmFpbmVyKCkKCiMgVGhyZWUgbGF5ZXIgbW9kZWxzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBzbWFsbCBjYXBhY2l0eSBtb2RlbApgMyBsYXllcl9zbWFsbGAgPC0ga2VyYXNfbW9kZWxfc2VxdWVudGlhbCgpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTYsIGFjdGl2YXRpb24gPSAicmVsdSIsIGlucHV0X3NoYXBlID0gbmNvbChtbmlzdF94KSkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSA4LCBhY3RpdmF0aW9uID0gInJlbHUiKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDQsIGFjdGl2YXRpb24gPSAicmVsdSIpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpICU+JQogIGNvbXBpbGVyKCkgJT4lCiAgdHJhaW5lcigpCgojIG1lZGl1bQpgMyBsYXllcl9tZWRpdW1gIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDY0LCBhY3RpdmF0aW9uID0gInJlbHUiLCBpbnB1dF9zaGFwZSA9IG5jb2wobW5pc3RfeCkpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMzIsIGFjdGl2YXRpb24gPSAicmVsdSIpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTYsIGFjdGl2YXRpb24gPSAicmVsdSIpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpICU+JQogIGNvbXBpbGVyKCkgJT4lCiAgdHJhaW5lcigpCgojIGxhcmdlCmAzIGxheWVyX2xhcmdlYCA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAyNTYsIGFjdGl2YXRpb24gPSAicmVsdSIsIGlucHV0X3NoYXBlID0gbmNvbChtbmlzdF94KSkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMjgsIGFjdGl2YXRpb24gPSAicmVsdSIpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gNjQsIGFjdGl2YXRpb24gPSAicmVsdSIpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpICU+JQogIGNvbXBpbGVyKCkgJT4lCiAgdHJhaW5lcigpCgptb2RlbHMgPC0gbHMocGF0dGVybiA9ICJsYXllcl8iKSAKZGYgPC0gbW9kZWxzICU+JQogIG1hcChnZXQpICU+JQogIG1hcCh+IGRhdGEuZnJhbWUoCiAgICBgVmFsaWRhdGlvbiBlcnJvcmAgPSAuJG1ldHJpY3MkdmFsX2xvc3MsCiAgICBgVHJhaW5pbmcgZXJyb3JgICAgPSAuJG1ldHJpY3MkbG9zcywKICAgIGVwb2NoID0gc2VxX2xlbiguJHBhcmFtcyRlcG9jaCkKICAgICkpICU+JQogIG1hcDJfZGYobW9kZWxzLCB+IG11dGF0ZSgueCwgbW9kZWwgPSAueSkpICU+JQogIHNlcGFyYXRlKG1vZGVsLCBpbnRvID0gYygiTWlkZGxlIGxheWVycyIsICJOdW1iZXIgb2Ygbm9kZXMiKSwgc2VwID0gIl8iKSAlPiUKICBnYXRoZXIoVmFsaWRhdGlvbiwgTG9zcywgVmFsaWRhdGlvbi5lcnJvcjpUcmFpbmluZy5lcnJvcikgJT4lCiAgbXV0YXRlKAogICAgVmFsaWRhdGlvbiA9IHN0cl9yZXBsYWNlX2FsbChWYWxpZGF0aW9uLCAiXFwuIiwgIiAiKSwKICAgIGBOdW1iZXIgb2Ygbm9kZXNgID0gZmFjdG9yKGBOdW1iZXIgb2Ygbm9kZXNgLCBsZXZlbHMgPSBjKCJzbWFsbCIsICJtZWRpdW0iLCAibGFyZ2UiKSkKICAgICkKCmJlc3QgPC0gZGYgJT4lIAogIGZpbHRlcihWYWxpZGF0aW9uID09ICJWYWxpZGF0aW9uIGVycm9yIikgJT4lCiAgZ3JvdXBfYnkoYE1pZGRsZSBsYXllcnNgLCBgTnVtYmVyIG9mIG5vZGVzYCkgJT4lIAogIGZpbHRlcihMb3NzID09IG1pbihMb3NzKSkgJT4lCiAgbXV0YXRlKGxhYmVsID0gcGFzdGUoIk1pbiB2YWxpZGF0aW9uIGVycm9yOiIsIHJvdW5kKExvc3MsIDQpKSkKCmdncGxvdChkZiwgYWVzKGVwb2NoLCBMb3NzKSkgKwogIGdlb21faGxpbmUoZGF0YSA9IGJlc3QsIGFlcyh5aW50ZXJjZXB0ID0gTG9zcyksIGx0eSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmV5NTAiKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBiZXN0LCBhZXMoeCA9IDI1LCB5ID0gMC45NSwgbGFiZWwgPSBsYWJlbCksIHNpemUgPSA0LCBoanVzdCA9IDEsIHZqdXN0ID0gMSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IFZhbGlkYXRpb24pKSArCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IFZhbGlkYXRpb24pKSArCiAgZmFjZXRfZ3JpZChgTnVtYmVyIG9mIG5vZGVzYCB+IGBNaWRkbGUgbGF5ZXJzYCwgc2NhbGVzID0gImZyZWVfeSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAxKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIikgKwogIHhsYWIoIkVwb2NoIikKYGBgCgoKIyMjIEJhdGNoIG5vcm1hbGl6YXRpb24KCmBgYHtyIGJhdGNoLW5vcm19Cm1vZGVsX3dfbm9ybSA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkgJT4lCiAgCiAgIyBOZXR3b3JrIGFyY2hpdGVjdHVyZSB3aXRoIGJhdGNoIG5vcm1hbGl6YXRpb24KICBsYXllcl9kZW5zZSh1bml0cyA9IDI1NiwgYWN0aXZhdGlvbiA9ICJyZWx1IiwgaW5wdXRfc2hhcGUgPSBuY29sKG1uaXN0X3gpKSAlPiUKICBsYXllcl9iYXRjaF9ub3JtYWxpemF0aW9uKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMjgsIGFjdGl2YXRpb24gPSAicmVsdSIpICU+JQogIGxheWVyX2JhdGNoX25vcm1hbGl6YXRpb24oKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDY0LCBhY3RpdmF0aW9uID0gInJlbHUiKSAlPiUKICBsYXllcl9iYXRjaF9ub3JtYWxpemF0aW9uKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMCwgYWN0aXZhdGlvbiA9ICJzb2Z0bWF4IikgJT4lCgogICMgQmFja3Byb3BhZ2F0aW9uCiAgY29tcGlsZSgKICAgIGxvc3MgPSAiY2F0ZWdvcmljYWxfY3Jvc3NlbnRyb3B5IiwKICAgIG9wdGltaXplciA9IG9wdGltaXplcl9ybXNwcm9wKCksCiAgICBtZXRyaWNzID0gYygiYWNjdXJhY3kiKQogICkKYGBgCgpGaWd1cmUgMTMuODoKCmBgYHtyIG1vZGVsLWNhcGFjaXR5LXdpdGgtYmF0Y2gtbm9ybSwgZWNobz1UUlVFLCBldmFsPVRSVUV9CiMgT25lIGxheWVyIG1vZGVscyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgc21hbGwgY2FwYWNpdHkgbW9kZWwKYDEgbGF5ZXJfc21hbGxgIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDE2LCBhY3RpdmF0aW9uID0gInJlbHUiLCBpbnB1dF9zaGFwZSA9IG5jb2wobW5pc3RfeCkpICU+JQogIGxheWVyX2JhdGNoX25vcm1hbGl6YXRpb24oKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDEwLCBhY3RpdmF0aW9uID0gInNvZnRtYXgiKSAlPiUKICBjb21waWxlcigpICU+JQogIHRyYWluZXIoKQoKIyBtZWRpdW0KYDEgbGF5ZXJfbWVkaXVtYCA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSA2NCwgYWN0aXZhdGlvbiA9ICJyZWx1IiwgaW5wdXRfc2hhcGUgPSBuY29sKG1uaXN0X3gpKSAlPiUKICBsYXllcl9iYXRjaF9ub3JtYWxpemF0aW9uKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMCwgYWN0aXZhdGlvbiA9ICJzb2Z0bWF4IikgJT4lCiAgY29tcGlsZXIoKSAlPiUKICB0cmFpbmVyKCkKCiMgbGFyZ2UKYDEgbGF5ZXJfbGFyZ2VgIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDI1NiwgYWN0aXZhdGlvbiA9ICJyZWx1IiwgaW5wdXRfc2hhcGUgPSBuY29sKG1uaXN0X3gpKSAlPiUKICBsYXllcl9iYXRjaF9ub3JtYWxpemF0aW9uKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMCwgYWN0aXZhdGlvbiA9ICJzb2Z0bWF4IikgJT4lCiAgY29tcGlsZXIoKSAlPiUKICB0cmFpbmVyKCkKCiMgVHdvIGxheWVyIG1vZGVscyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgc21hbGwgY2FwYWNpdHkgbW9kZWwKYDIgbGF5ZXJfc21hbGxgIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDE2LCBhY3RpdmF0aW9uID0gInJlbHUiLCBpbnB1dF9zaGFwZSA9IG5jb2wobW5pc3RfeCkpICU+JQogIGxheWVyX2JhdGNoX25vcm1hbGl6YXRpb24oKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDgsIGFjdGl2YXRpb24gPSAicmVsdSIpICU+JQogIGxheWVyX2JhdGNoX25vcm1hbGl6YXRpb24oKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDEwLCBhY3RpdmF0aW9uID0gInNvZnRtYXgiKSAlPiUKICBjb21waWxlcigpICU+JQogIHRyYWluZXIoKQoKIyBtZWRpdW0KYDIgbGF5ZXJfbWVkaXVtYCA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSA2NCwgYWN0aXZhdGlvbiA9ICJyZWx1IiwgaW5wdXRfc2hhcGUgPSBuY29sKG1uaXN0X3gpKSAlPiUKICBsYXllcl9iYXRjaF9ub3JtYWxpemF0aW9uKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAzMiwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpICU+JQogIGNvbXBpbGVyKCkgJT4lCiAgdHJhaW5lcigpCgojIGxhcmdlCmAyIGxheWVyX2xhcmdlYCA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAyNTYsIGFjdGl2YXRpb24gPSAicmVsdSIsIGlucHV0X3NoYXBlID0gbmNvbChtbmlzdF94KSkgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTI4LCBhY3RpdmF0aW9uID0gInJlbHUiKSAlPiUKICBsYXllcl9iYXRjaF9ub3JtYWxpemF0aW9uKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMCwgYWN0aXZhdGlvbiA9ICJzb2Z0bWF4IikgJT4lCiAgY29tcGlsZXIoKSAlPiUKICB0cmFpbmVyKCkKCiMgVGhyZWUgbGF5ZXIgbW9kZWxzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBzbWFsbCBjYXBhY2l0eSBtb2RlbApgMyBsYXllcl9zbWFsbGAgPC0ga2VyYXNfbW9kZWxfc2VxdWVudGlhbCgpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTYsIGFjdGl2YXRpb24gPSAicmVsdSIsIGlucHV0X3NoYXBlID0gbmNvbChtbmlzdF94KSkgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gOCwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gNCwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpICU+JQogIGNvbXBpbGVyKCkgJT4lCiAgdHJhaW5lcigpCgojIG1lZGl1bQpgMyBsYXllcl9tZWRpdW1gIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDY0LCBhY3RpdmF0aW9uID0gInJlbHUiLCBpbnB1dF9zaGFwZSA9IG5jb2wobW5pc3RfeCkpICU+JQogIGxheWVyX2JhdGNoX25vcm1hbGl6YXRpb24oKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDMyLCBhY3RpdmF0aW9uID0gInJlbHUiKSAlPiUKICBsYXllcl9iYXRjaF9ub3JtYWxpemF0aW9uKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxNiwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpICU+JQogIGNvbXBpbGVyKCkgJT4lCiAgdHJhaW5lcigpCgojIGxhcmdlCmAzIGxheWVyX2xhcmdlYCA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAyNTYsIGFjdGl2YXRpb24gPSAicmVsdSIsIGlucHV0X3NoYXBlID0gbmNvbChtbmlzdF94KSkgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTI4LCBhY3RpdmF0aW9uID0gInJlbHUiKSAlPiUKICBsYXllcl9iYXRjaF9ub3JtYWxpemF0aW9uKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSA2NCwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpICU+JQogIGNvbXBpbGVyKCkgJT4lCiAgdHJhaW5lcigpCgptb2RlbHMgPC0gbHMocGF0dGVybiA9ICJsYXllcl8iKSAKZGZfYmF0Y2ggPC0gbW9kZWxzICU+JQogIG1hcChnZXQpICU+JQogIG1hcCh+IGRhdGEuZnJhbWUoCiAgICBgVmFsaWRhdGlvbiBlcnJvcmAgPSAuJG1ldHJpY3MkdmFsX2xvc3MsCiAgICBgVHJhaW5pbmcgZXJyb3JgICAgPSAuJG1ldHJpY3MkbG9zcywKICAgIGVwb2NoID0gc2VxX2xlbiguJHBhcmFtcyRlcG9jaCkKICAgICkpICU+JQogIG1hcDJfZGYobW9kZWxzLCB+IG11dGF0ZSgueCwgbW9kZWwgPSAueSkpICU+JQogIHNlcGFyYXRlKG1vZGVsLCBpbnRvID0gYygiTWlkZGxlIGxheWVycyIsICJOdW1iZXIgb2Ygbm9kZXMiKSwgc2VwID0gIl8iKSAlPiUKICBnYXRoZXIoVmFsaWRhdGlvbiwgTG9zcywgVmFsaWRhdGlvbi5lcnJvcjpUcmFpbmluZy5lcnJvcikgJT4lCiAgbXV0YXRlKAogICAgVmFsaWRhdGlvbiA9IHN0cl9yZXBsYWNlX2FsbChWYWxpZGF0aW9uLCAiXFwuIiwgIiAiKSwKICAgIGBOdW1iZXIgb2Ygbm9kZXNgID0gZmFjdG9yKGBOdW1iZXIgb2Ygbm9kZXNgLCBsZXZlbHMgPSBjKCJzbWFsbCIsICJtZWRpdW0iLCAibGFyZ2UiKSksCiAgICBgQmF0Y2ggbm9ybWFsaXphdGlvbmAgPSBUUlVFCiAgICApCmBgYAoKRmlndXJlIDEzLjggKGNvbnRpbnVlZCk6CgpgYGB7ciBtb2RlbC1jYXBhY2l0eS13aXRoLWJhdGNoLW5vcm0tcGxvdCwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9OCwgZmlnLmNhcD0iVGhlIGVmZmVjdCBvZiBiYXRjaCBub3JtYWxpemF0aW9uIG9uIHZhbGlkYXRpb24gbG9zcyBmb3IgdmFyaW91cyBtb2RlbCBjYXBhY2l0aWVzLiJ9CmRmMiA8LSBkZiAlPiUKICBtdXRhdGUoYEJhdGNoIG5vcm1hbGl6YXRpb25gID0gRkFMU0UpICU+JQogIGJpbmRfcm93cyhkZl9iYXRjaCkgJT4lIAogIGZpbHRlcihWYWxpZGF0aW9uID09ICJWYWxpZGF0aW9uIGVycm9yIikKCmJlc3QgPC0gZGYyICU+JSAKICBmaWx0ZXIoVmFsaWRhdGlvbiA9PSAiVmFsaWRhdGlvbiBlcnJvciIpICU+JQogIGdyb3VwX2J5KGBNaWRkbGUgbGF5ZXJzYCwgYE51bWJlciBvZiBub2Rlc2ApICU+JSAKICBmaWx0ZXIoTG9zcyA9PSBtaW4oTG9zcykpICU+JQogIG11dGF0ZShsYWJlbCA9IHBhc3RlKCJNaW4gdmFsaWRhdGlvbiBlcnJvcjoiLCByb3VuZChMb3NzLCA0KSkpCgpnZ3Bsb3QoZGYyLCBhZXMoZXBvY2gsIExvc3MsIGNvbG9yID0gYEJhdGNoIG5vcm1hbGl6YXRpb25gKSkgKyAKICBnZW9tX3RleHQoZGF0YSA9IGJlc3QsIGFlcyh4ID0gMjUsIHkgPSAwLjk1LCBsYWJlbCA9IGxhYmVsKSwgc2l6ZSA9IDQsIGhqdXN0ID0gMSwgdmp1c3QgPSAxKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgKwogIGZhY2V0X2dyaWQoYE51bWJlciBvZiBub2Rlc2AgfiBgTWlkZGxlIGxheWVyc2AsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMSkpICsKICB4bGFiKCJFcG9jaCIpICsKICBzY2FsZV9jb2xvcl9kaXNjcmV0ZSgiQmF0Y2ggbm9ybWFsaXphdGlvbiIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgoKIyMjIFJlZ3VsYXJpemF0aW9uCgpgYGB7ciByZWd1bGFyaXphdGlvbi13aXRoLXBlbmFsdHksIGV2YWw9VFJVRX0KbW9kZWxfd19yZWcgPC0ga2VyYXNfbW9kZWxfc2VxdWVudGlhbCgpICU+JQogIAogICMgTmV0d29yayBhcmNoaXRlY3R1cmUgd2l0aCBMMSByZWd1bGFyaXphdGlvbiBhbmQgYmF0Y2ggbm9ybWFsaXphdGlvbgogIGxheWVyX2RlbnNlKHVuaXRzID0gMjU2LCBhY3RpdmF0aW9uID0gInJlbHUiLCBpbnB1dF9zaGFwZSA9IG5jb2wobW5pc3RfeCksCiAgICAgICAgICAgICAga2VybmVsX3JlZ3VsYXJpemVyID0gcmVndWxhcml6ZXJfbDIoMC4wMDEpKSAlPiUKICBsYXllcl9iYXRjaF9ub3JtYWxpemF0aW9uKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMjgsIGFjdGl2YXRpb24gPSAicmVsdSIsIAogICAgICAgICAgICAgIGtlcm5lbF9yZWd1bGFyaXplciA9IHJlZ3VsYXJpemVyX2wyKDAuMDAxKSkgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gNjQsIGFjdGl2YXRpb24gPSAicmVsdSIsIAogICAgICAgICAgICAgIGtlcm5lbF9yZWd1bGFyaXplciA9IHJlZ3VsYXJpemVyX2wyKDAuMDAxKSkgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpICU+JQoKICAjIEJhY2twcm9wYWdhdGlvbgogIGNvbXBpbGUoCiAgICBsb3NzID0gImNhdGVnb3JpY2FsX2Nyb3NzZW50cm9weSIsCiAgICBvcHRpbWl6ZXIgPSBvcHRpbWl6ZXJfcm1zcHJvcCgpLAogICAgbWV0cmljcyA9IGMoImFjY3VyYWN5IikKICApCmBgYAoKYGBge3IgZHJvcG91dCwgZXZhbD1UUlVFfQptb2RlbF93X2Ryb3AgPC0ga2VyYXNfbW9kZWxfc2VxdWVudGlhbCgpICU+JQogIAogICMgTmV0d29yayBhcmNoaXRlY3R1cmUgd2l0aCAyMCUgZHJvcG91dAogIGxheWVyX2RlbnNlKHVuaXRzID0gMjU2LCBhY3RpdmF0aW9uID0gInJlbHUiLCBpbnB1dF9zaGFwZSA9IG5jb2wobW5pc3RfeCkpICU+JQogIGxheWVyX2Ryb3BvdXQocmF0ZSA9IDAuMikgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMjgsIGFjdGl2YXRpb24gPSAicmVsdSIpICU+JQogIGxheWVyX2Ryb3BvdXQocmF0ZSA9IDAuMikgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSA2NCwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfZHJvcG91dChyYXRlID0gMC4yKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDEwLCBhY3RpdmF0aW9uID0gInNvZnRtYXgiKSAlPiUKCiAgIyBCYWNrcHJvcGFnYXRpb24KICBjb21waWxlKAogICAgbG9zcyA9ICJjYXRlZ29yaWNhbF9jcm9zc2VudHJvcHkiLAogICAgb3B0aW1pemVyID0gb3B0aW1pemVyX3Jtc3Byb3AoKSwKICAgIG1ldHJpY3MgPSBjKCJhY2N1cmFjeSIpCiAgKQpgYGAKCkZpZ3VyZSAxMy45OgoKYGBge3IgbW9kZWwtd2l0aC1yZWd1bGFyaXphdGlvbiwgZWNobz1UUlVFLCBldmFsPVRSVUV9CmZpdF9iYXNlbGluZSA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAyNTYsIGFjdGl2YXRpb24gPSAicmVsdSIsIGlucHV0X3NoYXBlID0gbmNvbChtbmlzdF94KSkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMjgsIGFjdGl2YXRpb24gPSAicmVsdSIpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gNjQsIGFjdGl2YXRpb24gPSAicmVsdSIpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpICU+JQogIGNvbXBpbGVyKCkgJT4lCiAgZml0KAogICAgeCA9IG1uaXN0X3gsCiAgICB5ID0gbW5pc3RfeSwKICAgIGVwb2NocyA9IDM1LAogICAgYmF0Y2hfc2l6ZSA9IDEyOCwKICAgIHZhbGlkYXRpb25fc3BsaXQgPSAwLjIsCiAgICB2ZXJib3NlID0gRkFMU0UKICApCgpmaXRfbm9ybSA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAyNTYsIGFjdGl2YXRpb24gPSAicmVsdSIsIGlucHV0X3NoYXBlID0gbmNvbChtbmlzdF94KSkgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTI4LCBhY3RpdmF0aW9uID0gInJlbHUiKSAlPiUKICBsYXllcl9iYXRjaF9ub3JtYWxpemF0aW9uKCkgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSA2NCwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpICU+JQogIGNvbXBpbGVyKCkgJT4lCiAgZml0KAogICAgeCA9IG1uaXN0X3gsCiAgICB5ID0gbW5pc3RfeSwKICAgIGVwb2NocyA9IDM1LAogICAgYmF0Y2hfc2l6ZSA9IDEyOCwKICAgIHZhbGlkYXRpb25fc3BsaXQgPSAwLjIsCiAgICB2ZXJib3NlID0gRkFMU0UKICApCgpmaXRfcmVnIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDI1NiwgYWN0aXZhdGlvbiA9ICJyZWx1IiwgaW5wdXRfc2hhcGUgPSBuY29sKG1uaXN0X3gpKSAlPiUKICBsYXllcl9iYXRjaF9ub3JtYWxpemF0aW9uKCkgJT4lCiAgbGF5ZXJfZHJvcG91dChyYXRlID0gMC40KSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDEyOCwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2Ryb3BvdXQocmF0ZSA9IDAuMykgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSA2NCwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2Ryb3BvdXQocmF0ZSA9IDAuMikgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMCwgYWN0aXZhdGlvbiA9ICJzb2Z0bWF4IikgJT4lCiAgY29tcGlsZXIoKSAlPiUKICBmaXQoCiAgICB4ID0gbW5pc3RfeCwKICAgIHkgPSBtbmlzdF95LAogICAgZXBvY2hzID0gMzUsCiAgICBiYXRjaF9zaXplID0gMTI4LAogICAgdmFsaWRhdGlvbl9zcGxpdCA9IDAuMiwKICAgIHZlcmJvc2UgPSBGQUxTRQogICkKCm1vZGVscyA8LSBscyhwYXR0ZXJuID0gImZpdF8iKSAKZGZfcmVnIDwtIG1vZGVscyAlPiUKICBtYXAoZ2V0KSAlPiUKICBtYXAofiBkYXRhLmZyYW1lKAogICAgYFZhbGlkYXRpb24gZXJyb3JgID0gLiRtZXRyaWNzJHZhbF9sb3NzLAogICAgYFRyYWluaW5nIGVycm9yYCAgID0gLiRtZXRyaWNzJGxvc3MsCiAgICBlcG9jaCA9IHNlcV9sZW4oLiRwYXJhbXMkZXBvY2gpCiAgICApKSAlPiUKICBtYXAyX2RmKG1vZGVscywgfiBtdXRhdGUoLngsIG1vZGVsID0gLnkpKSAlPiUKICBtdXRhdGUoTW9kZWwgPSBjYXNlX3doZW4oCiAgICBtb2RlbCA9PSAiZml0X2Jhc2VsaW5lIiB+ICJCYXNlbGluZSIsCiAgICBtb2RlbCA9PSAiZml0X25vcm0iICAgICB+ICJCYXNlbGluZSArIGJhdGNoIG5vcm1hbGl6YXRpb24iLAogICAgbW9kZWwgPT0gImZpdF9yZWciICAgICAgfiAiQmFzZWxpbmUgKyBiYXRjaCBub3JtYWxpemF0aW9uICsgZHJvcG91dCIKICApKSAlPiUKICBnYXRoZXIoVmFsaWRhdGlvbiwgTG9zcywgVmFsaWRhdGlvbi5lcnJvcjpUcmFpbmluZy5lcnJvcikKYGBgCgpGaWd1cmUgMTMuOSAoY29udGludWVkKToKCmBgYHtyIG1vZGVsLXdpdGgtcmVndWxhcml6YXRpb24tcGxvdCwgZWNobz1UUlVFLCBmaWcuY2FwPSJUaGUgZWZmZWN0IG9mIHJlZ3VsYXJpemF0aW9uIHdpdGggZHJvcG91dCBvbiB2YWxpZGF0aW9uIGxvc3MuIn0KYmFzZWxpbmUgPC0gZGYgJT4lCiAgZmlsdGVyKGBNaWRkbGUgbGF5ZXJzYCA9PSAiMyBsYXllciIsIGBOdW1iZXIgb2Ygbm9kZXNgID09ICJsYXJnZSIpICU+JQogIG11dGF0ZShNb2RlbCA9ICJCYXNlbGluZSIpICU+JQogIHNlbGVjdChlcG9jaCwgTW9kZWwsIFZhbGlkYXRpb24sIExvc3MpCmJhdGNoIDwtIGRmX2JhdGNoICU+JQogIGZpbHRlcihgTWlkZGxlIGxheWVyc2AgPT0gIjMgbGF5ZXIiLCBgTnVtYmVyIG9mIG5vZGVzYCA9PSAibGFyZ2UiKSAlPiUKICBtdXRhdGUoTW9kZWwgPSAiQmFzZWxpbmUgKyBiYXRjaCBub3JtYWxpemF0aW9uIikgJT4lCiAgc2VsZWN0KGVwb2NoLCBNb2RlbCwgVmFsaWRhdGlvbiwgTG9zcykKZGZfcmVnIDwtIGRmX3JlZyAlPiUKICBzZWxlY3QoLW1vZGVsKSAlPiUKICBmaWx0ZXIoTW9kZWwgPT0gIkJhc2VsaW5lICsgYmF0Y2ggbm9ybWFsaXphdGlvbiArIGRyb3BvdXQiKSAlPiUKICBtdXRhdGUoVmFsaWRhdGlvbiA9IHN0cmluZ3I6OnN0cl9yZXBsYWNlX2FsbChWYWxpZGF0aW9uLCAiXFwuIiwgIiAiKSkgJT4lCiAgYmluZF9yb3dzKGJhdGNoLCBiYXNlbGluZSkKCmJlc3QgPC0gZGZfcmVnICU+JSAKICBmaWx0ZXIoVmFsaWRhdGlvbiA9PSAiVmFsaWRhdGlvbiBlcnJvciIpICU+JQogIGdyb3VwX2J5KE1vZGVsKSAlPiUgCiAgZmlsdGVyKExvc3MgPT0gbWluKExvc3MpKSAlPiUKICBtdXRhdGUobGFiZWwgPSBwYXN0ZSgiTWluIHZhbGlkYXRpb24gZXJyb3I6Iiwgcm91bmQoTG9zcywgNCkpKQoKZ2dwbG90KGRmX3JlZywgYWVzKGVwb2NoLCBMb3NzKSkgKyAKICBnZW9tX3RleHQoZGF0YSA9IGJlc3QsIGFlcyh4ID0gMzUsIHkgPSAwLjQ5LCBsYWJlbCA9IGxhYmVsKSwgc2l6ZSA9IDQsIGhqdXN0ID0gMSwgdmp1c3QgPSAxKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBWYWxpZGF0aW9uKSkgKwogIGdlb21fbGluZShhZXMoY29sb3IgPSBWYWxpZGF0aW9uKSkgKwogIGZhY2V0X3dyYXAofiBNb2RlbCkgKwogIHhsYWIoIkVwb2NoIikgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgoKIyMjIEFkanVzdCBsZWFybmluZyByYXRlCgpGaWd1cmUgMTMuMTA6CgpgYGB7ciBsb2NhbC12cy1nbG9iYWwsIGVjaG89VFJVRSwgZmlnLndpZHRoPTQuNSwgZmlnLmhlaWdodD0yLjUsIGZpZy5jYXA9IkEgbG9jYWwgbWluaW11bSBhbmQgYSBnbG9iYWwgbWluaW11bS4ifQpzZXQuc2VlZCgxMjMpICAjIGZvciByZXByb2R1Y2liaWxpdHkKeCA8LSBzZXEoZnJvbSA9IDAsIHRvID0gNC4yNSAqIHBpLCBsZW5ndGggPSAxMDApCnkgPC0gc2luKHgpIC0geCouMgpkZiA8LSBkYXRhLmZyYW1lKHgsIHkpCgpnbG9iYWwgPC0gZmlsdGVyKGRmLCB5ID09IG1pbih5KSkKbG9jYWwgIDwtIGRmICU+JSAKICBmaWx0ZXIoeCA8IDYpICU+JSAKICBmaWx0ZXIoeSA9PSBtaW4oeSkpCgpnZ3Bsb3QoZGYsIGFlcyh4LCB5KSkgKwogIGdlb21fbGluZShzaXplID0gMS41LCBhbHBoYSA9IDAuNSkgKwogIHNjYWxlX3lfY29udGludW91cygiTG9zcyBmdW5jdGlvbiIsIGV4cGFuZCA9IGMoMCwgMC4wOCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoIlBhcmFtZXRlciB2YWx1ZSIsIGV4cGFuZCA9IGMoMC4wOCwgMCkpICsKICB0aGVtZSgKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpCiAgKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gbWluKHgpIC0gMC41LCB4ZW5kID0gbWF4KHgpICwgeSA9IG1pbih5KSAtIDAuNSwgeWVuZCA9IG1pbih5KSAtIDAuNSksIAogICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiY20iKSksIGNvbG9yID0gImdyZXkzMCIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSBtaW4oeCkgLSAwLjUsIHhlbmQgPSBtaW4oeCkgLSAwLjUgLCB5ID0gbWluKHkpIC0gMC41LCB5ZW5kID0gbWF4KHkpKSwgCiAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjIsICJjbSIpKSwgY29sb3IgPSAiZ3JleTMwIikgKwogIGdlb21fcG9pbnQoZGF0YSA9IGdsb2JhbCwgYWVzKHgsIHkpLCBzaXplID0gNCwgc2hhcGUgPSAyMSwgZmlsbCA9ICJ5ZWxsb3ciKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbG9jYWwsIGFlcyh4LCB5KSwgc2l6ZSA9IDQsIHNoYXBlID0gMjEsIGZpbGwgPSAiYmx1ZSIpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBnbG9iYWwkeCwgeSA9IGdsb2JhbCR5LCBsYWJlbCA9ICJHbG9iYWwgbWluaW11bSIsIHZqdXN0ID0gMiwgc2l6ZSA9IDMpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBsb2NhbCR4LCB5ID0gbG9jYWwkeSwgbGFiZWwgPSAiTG9jYWwgbWluaW11bSIsIHZqdXN0ID0gMiwgc2l6ZSA9IDMpCmBgYAoKYGBge3IgYWRqLWxybi1yYXRlLCBmaWcuY2FwPSJUcmFpbmluZyBhbmQgdmFsaWRhdGlvbiBwZXJmb3JtYW5jZSBvbiBvdXIgMy1sYXllciBsYXJnZSBuZXR3b3JrIHdpdGggZHJvcG91dCwgYWRqdXN0YWJsZSBsZWFybmluZyByYXRlLCBhbmQgdXNpbmcgYW4gQWRhbSBtaW5pLWJhdGNoIFNHRCBvcHRpbWl6ZXIuIn0KbW9kZWxfd19hZGpfbHJuIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDI1NiwgYWN0aXZhdGlvbiA9ICJyZWx1IiwgaW5wdXRfc2hhcGUgPSBuY29sKG1uaXN0X3gpKSAlPiUKICBsYXllcl9iYXRjaF9ub3JtYWxpemF0aW9uKCkgJT4lCiAgbGF5ZXJfZHJvcG91dChyYXRlID0gMC40KSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDEyOCwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2Ryb3BvdXQocmF0ZSA9IDAuMykgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSA2NCwgYWN0aXZhdGlvbiA9ICJyZWx1IikgJT4lCiAgbGF5ZXJfYmF0Y2hfbm9ybWFsaXphdGlvbigpICU+JQogIGxheWVyX2Ryb3BvdXQocmF0ZSA9IDAuMikgJT4lCiAgbGF5ZXJfZGVuc2UodW5pdHMgPSAxMCwgYWN0aXZhdGlvbiA9ICJzb2Z0bWF4IikgJT4lCiAgY29tcGlsZSgKICAgIGxvc3MgPSAnY2F0ZWdvcmljYWxfY3Jvc3NlbnRyb3B5JywKICAgIG9wdGltaXplciA9IG9wdGltaXplcl9hZGFtKCksCiAgICBtZXRyaWNzID0gYygnYWNjdXJhY3knKQogICkgJT4lCiAgZml0KAogICAgeCA9IG1uaXN0X3gsCiAgICB5ID0gbW5pc3RfeSwKICAgIGVwb2NocyA9IDM1LAogICAgYmF0Y2hfc2l6ZSA9IDEyOCwKICAgIHZhbGlkYXRpb25fc3BsaXQgPSAwLjIsCiAgICBjYWxsYmFja3MgPSBsaXN0KAogICAgICBjYWxsYmFja19lYXJseV9zdG9wcGluZyhwYXRpZW5jZSA9IDUpLAogICAgICBjYWxsYmFja19yZWR1Y2VfbHJfb25fcGxhdGVhdShmYWN0b3IgPSAwLjA1KQogICAgICApLAogICAgdmVyYm9zZSA9IEZBTFNFCiAgKQoKbW9kZWxfd19hZGpfbHJuCgojIE9wdGltYWwKbWluKG1vZGVsX3dfYWRqX2xybiRtZXRyaWNzJHZhbF9sb3NzKQptYXgobW9kZWxfd19hZGpfbHJuJG1ldHJpY3MkdmFsX2FjYykKCiMgTGVhcm5pbmcgcmF0ZQpwbG90KG1vZGVsX3dfYWRqX2xybikKYGBgCgoKIyMgR3JpZCBTZWFyY2gKClRoaXMgZ3JpZCBzZWFyY2ggcmVxdWlyZXMgdGhlIFttbmlzdC1ncmlkLXNlYXJjaC5SXShodHRwOi8vYml0Lmx5L21uaXN0LWdyaWQtc2VhcmNoKQpmaWxlIHRvIGJlIGxvY2F0ZWQgYSBgL3Njcmlwc2AgZGlyZWN0b3J5IHdpdGhpbiB5b3VyIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkuCgpfX1dBUk5JTkc6IFRoZSBmb2xsb3dpbmcgZ3JpZCBzZWFyY2ggdG9vayB1cyBvdmVyIDEuNSBob3VycyB0byBydW4hX18KCmBgYHtyfQojIFJ1biB2YXJpb3VzIGNvbWJpbmF0aW9ucyBvZiBkcm9wb3V0MSBhbmQgZHJvcG91dDIKcnVucyA8LSB0dW5pbmdfcnVuKCJzY3JpcHRzL21uaXN0LWdyaWQtc2VhcmNoLlIiLCAKICBmbGFncyA9IGxpc3QoCiAgICBub2RlczEgPSBjKDY0LCAxMjgsIDI1NiksCiAgICBub2RlczIgPSBjKDY0LCAxMjgsIDI1NiksCiAgICBub2RlczMgPSBjKDY0LCAxMjgsIDI1NiksCiAgICBkcm9wb3V0MSA9IGMoMC4yLCAwLjMsIDAuNCksCiAgICBkcm9wb3V0MiA9IGMoMC4yLCAwLjMsIDAuNCksCiAgICBkcm9wb3V0MyA9IGMoMC4yLCAwLjMsIDAuNCksCiAgICBvcHRpbWl6ZXIgPSBjKCJybXNwcm9wIiwgImFkYW0iKSwKICAgIGxyX2FubmVhbGluZyA9IGMoMC4xLCAwLjA1KQogICksCiAgc2FtcGxlID0gMC4wNQopCgpydW5zICU+JSAKICBmaWx0ZXIobWV0cmljX3ZhbF9sb3NzID09IG1pbihtZXRyaWNfdmFsX2xvc3MpKSAlPiUgCiAgZ2xpbXBzZSgpCmBgYA==