Note: Some results may differ from the hard copy book due to the changing of sampling procedures introduced in R 3.6.0. See http://bit.ly/35D1SW7 for more details. Access and run the source code for this notebook here. Do to output size, most of this chapter’s code chunks should not be ran on RStudio Cloud.

Hidden chapter requirements used in the book to set the plotting theme and load packages used in hidden code chunks:

library(ggplot2)
Find out what's changed in ggplot2 at https://github.com/tidyverse/ggplot2/releases.
library(tidyr)
library(stringr)
library(purrr)

# Set the graphical theme
ggplot2::theme_set(ggplot2::theme_light())

# Set global knitr chunk options
knitr::opts_chunk$set(
  cache = FALSE,
  warning = FALSE, 
  message = FALSE
)

Prerequisites

Note: you may need to install tensorflow if you have not done so before. You can do so by executing keras::install_keras(). See https://keras.rstudio.com/articles/getting_started.html for more details.

# Helper packages
library(dplyr)         # for basic data wrangling

# Modeling packages
library(keras)         # for fitting DNNs
library(tfruns)        # for additional grid search & model training functions

# Modeling helper package - not necessary for reproducibility
library(tfestimators)  # provides grid search & model training interface

We’ll use the MNIST data to illustrate various DNN concepts.

# Import MNIST training data
mnist <- dslabs::read_mnist()
mnist_x <- mnist$train$images
mnist_y <- mnist$train$labels

# Rename columns and standardize feature values
colnames(mnist_x) <- paste0("V", 1:ncol(mnist_x))
mnist_x <- mnist_x / 255

# One-hot encode response
mnist_y <- to_categorical(mnist_y, 10)

Why deep learning

Figure 13.1:

knitr::include_graphics("images/digits.png")

Figure 13.2:

knitr::include_graphics("images/basic-neural-net.png")

Figure 13.3:

knitr::include_graphics("images/deep_neural-net.png")

Feedforward DNNs

Figure 13.4:

knitr::include_graphics("images/mlp_network.png")

Network architecture

Implementation

The keras package allows us to develop our network with a layering approach. First, we initiate our sequential feedforward DNN architecture with keras_model_sequential() and then add some dense layers. This example creates two hidden layers, the first with 128 nodes and the second with 64, followed by an output layer with 10 nodes. One thing to point out is that the first layer needs the input_shape argument to equal the number of features in your data; however, the successive layers are able to dynamically interpret the number of expected inputs based on the previous layer.

model <- keras_model_sequential() %>%
  layer_dense(units = 128, input_shape = ncol(mnist_x)) %>%
  layer_dense(units = 64) %>%
  layer_dense(units = 10)

Activation functions

Figure 13.5:

knitr::include_graphics("images/perceptron_node.png")

Implementation

model <- keras_model_sequential() %>%
  layer_dense(units = 128, activation = "relu", input_shape = ncol(mnist_x)) %>%
  layer_dense(units = 64, activation = "relu") %>%
  layer_dense(units = 10, activation = "softmax")

Backpropagation

model <- keras_model_sequential() %>%
  
  # Network architecture
  layer_dense(units = 128, activation = "relu", input_shape = ncol(mnist_x)) %>%
  layer_dense(units = 64, activation = "relu") %>%
  layer_dense(units = 10, activation = "softmax") %>%
  
  # Backpropagation
  compile(
    loss = 'categorical_crossentropy',
    optimizer = optimizer_rmsprop(),
    metrics = c('accuracy')
  )

Model training

# Train the model
fit1 <- model %>%
  fit(
    x = mnist_x,
    y = mnist_y,
    epochs = 25,
    batch_size = 128,
    validation_split = 0.2,
    verbose = FALSE
  )

# Display output
fit1
Trained on 48,000 samples (batch_size=128, epochs=25)
Final epoch (plot to see history):
        loss: 0.001994
    accuracy: 0.9995
    val_loss: 0.1561
val_accuracy: 0.9776 
plot(fit1)

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)

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==