Exploratory Data Analysis
We’ll make two ggplots and combine them using cowplot::plot_grid(). Note that for the zoomed in plot, we make use of tibbletime::time_filter(), which is an easy way to perform time-based filtering.
p1 <- sun_spots %>%
ggplot(aes(index, value)) +
geom_point(alpha = 0.5) +
labs(
title = "From 1749 to 2013 (Full Data Set)"
)
p2 <- sun_spots %>%
filter(index < "1800-01-01") %>%
#filter_time("start" ~ "1800") %>%
ggplot(aes(index, value)) +
geom_line(color = palette_light()[[1]], alpha = 0.5) +
geom_point(color = palette_light()[[1]]) +
geom_smooth(method = "loess", span = 0.2, se = FALSE) +
labs(
title = "1749 to 1759 (Zoomed In To Show Changes over the Year)",
caption = "datasets::sunspot.month"
)
p_title <- cowplot::ggdraw() +
draw_label("Sunspots", size = 18, fontface = "bold")
cowplot::plot_grid(p_title,
p1, p2,
ncol = 1,
rel_heights = c(0.1, 1, 1))

Backtesting: time series cross validation
Can be done with rsample package which includes facilities for backtesting time series. The rolling_origin() function is used to created samples deigned for time series cross validation.
Developing a backtesting strategy
The sampling strategy: we use 50 years (initial = 12 x 50 samples) for training set and 10 years (assess = 12 x 10 samples) for testing (validation) set. The skip span of about 20 years (skip = 12 x 20 - 1) to approximately evenly distribute the samples into 6 sets that span the entire 265 years of sunspot history. And finally we select cumulative = FALSE to allow the origin to shift which ensures that models on more resetl data are not given an unfair advantage (more observations) over those operation on less recent data.
periods_train <- 12 * 100
periods_test <- 12 * 50
skip_span = 12 * 22 - 1
rolling_origin_resamples <- rsample::rolling_origin(
data = sun_spots,
initial = periods_train,
assess = periods_test,
cumulative = FALSE,
skip = skip_span
)
rolling_origin_resamples %>% head()
[38;5;246m# A tibble: 6 x 2[39m
splits id
[38;5;250m*[39m [3m[38;5;246m<list>[39m[23m [3m[38;5;246m<chr>[39m[23m
[38;5;250m1[39m [38;5;246m<S3: rsplit>[39m Slice1
[38;5;250m2[39m [38;5;246m<S3: rsplit>[39m Slice2
[38;5;250m3[39m [38;5;246m<S3: rsplit>[39m Slice3
[38;5;250m4[39m [38;5;246m<S3: rsplit>[39m Slice4
[38;5;250m5[39m [38;5;246m<S3: rsplit>[39m Slice5
[38;5;250m6[39m [38;5;246m<S3: rsplit>[39m Slice6
Visualizing the backtesting strategy
The resamples can be visualized with two custom functions:
plot_split() plots one of the resampling splits using ggplot2. The expand_y_axis argument is added to expand the date range to the full sun_spots dataset date range.
#plotting function for a single split
plot_split <- function(split, expand_y_axis = TRUE, alpha = 1, size = 1, base_size = 14) {
#manipulate data
train_tbl <- rsample::training(split) %>%
tibble::add_column(key = "training")
test_tbl <- rsample::testing(split) %>%
tibble::add_column(key = "testing")
data_manipulated <- bind_rows(train_tbl, test_tbl) %>%
tibbletime::as_tbl_time(index = index) %>%
mutate(key = forcats::fct_relevel(key, "training", "testing"))
#collect attributes
train_time_summary <- train_tbl %>%
timetk::tk_index() %>%
timetk::tk_get_timeseries_summary()
test_time_summary <- test_tbl %>%
timetk::tk_index() %>%
timetk::tk_get_timeseries_summary()
#visualize
g <- data_manipulated %>%
ggplot(aes(x = index, y = value, color = key)) +
geom_line(size = size, alpha = alpha) +
tidyquant::theme_tq(base_size = base_size) +
tidyquant::scale_color_tq() +
labs(
title = glue::glue("Split: {split$id}"),
subtitle = glue::glue("{train_time_summary$start} to {test_time_summary$end}"),
y = "", x = ""
) +
theme(legend.position = "none")
if(expand_y_axis) {
sun_spots_time_summary <- sun_spots %>%
timetk::tk_index() %>%
timetk::tk_get_timeseries_summary()
g <- g +
scale_x_date(limits = c(sun_spots_time_summary$start,
sun_spots_time_summary$end))
}
return(g)
}
The plot_split() takes one split and return a visual of the sampling strategy.
rolling_origin_resamples$splits[[1]] %>%
plot_split(expand_y_axis = TRUE) +
theme(legend.position = "bottom")

plot_sampling_plan() scales the plot_split() to all the samples via purrr and cowplot.
#plotting function that scales to all splits
plot_sampling_plan <- function(sampling_tbl, expand_y_axis = TRUE,
ncol = 3, alpha = 1, size = 1, base_size = 14,
title = "Sampling Plan") {
#map plot_slit() to sampling tbl
sampling_tbl_with_plots <- sampling_tbl %>%
mutate(gg_plots = map(splits, plot_split,
expand_y_axis = expand_y_axis,
alpha = alpha, base_size = base_size))
#make plots with cowplot
plot_list <- sampling_tbl_with_plots$gg_plots
p_temp <- plot_list[[1]] +
theme(legend.position = "bottom")
legend <- cowplot::get_legend(p_temp)
p_body <- cowplot::plot_grid(plotlist = plot_list, ncol = ncol)
p_title <- cowplot::ggdraw() +
draw_label(title, size = 14, colour = palette_light()[[1]], fontface = "bold")
g <- cowplot::plot_grid(p_title, p_body, ncol = 1, rel_heights = c(0.05, 1, 0.05))
return(g)
}
Now we can visualize the entire backtesting strategy with plot_sampling_plan().
rolling_origin_resamples %>%
plot_sampling_plan(expand_y_axis = TRUE, ncol = 3, alpha = 1, size = 1, base_size = 10,
title = "Backtesting Strategy: Rolling Origin Sampling Plan")

We can zoom in by setting expand_y_axis = FALSE:
rolling_origin_resamples %>%
plot_sampling_plan(expand_y_axis = FALSE, ncol = 3, alpha = 1, size = 1, base_size = 10,
title = "Backtesting Strategy: Zoomed In")

The LSTM Model
To begin, we’ll develop and LSTM model on a single sample from backtesting strategy (the recent one) and then apply the model to all samples to investigate modeling performance.
example_split <- rolling_origin_resamples$splits[[6]]
example_split_id <- rolling_origin_resamples$id[[6]]
Let’s reuse the plot_split() to visualize the split.
plot_split(example_split, expand_y_axis = FALSE, size = 0.5) +
theme(legend.position = "bottom") +
ggtitle(glue::glue("Split: {example_split_id}"))

Data Setup
To aid hyperparameter tuning, we need to create a validation set as well. So we’ll dedicate 2/3 of training set to training, and remaining 1/3 to validation.
df_trn <- rsample::analysis(example_split)[1:800, , drop = FALSE]
df_val <- rsample::analysis(example_split)[801:1200, , drop = FALSE]
df_tst <- rsample::assessment(example_split)
First, let’s combine the training and testing sets into a single dataset with a column key that specifies where they came from (train or test).
df <- bind_rows(
df_trn %>% add_column(key = "training"),
df_val %>% add_column(key = "validation"),
df_tst %>% add_column(key = "testing")
) %>%
tibbletime::as_tbl_time(index = index)
Preprocessing with recipes
The LSTM works better if the input data has been centered and scaled. This (and many other useful transformations) can be done with recipes package.
rec_obj <- recipes::recipe(value ~ ., df) %>%
recipes::step_sqrt(value) %>% #to reduce variane and remove outliers
recipes::step_center(value) %>%
recipes::step_scale(value) %>%
recipes::prep()
#bake the recipe
df_processed_tlb <- recipes::bake(rec_obj, df)
Now, let’s capture the original center and scale so we can invert the steps after modeling. The square root step can then simply be undone by squaring the back transformed data.
center_history <- rec_obj$steps[[2]]$means["value"]
scale_history <- rec_obj$steps[[3]]$sds["value"]
c("center" = center_history, "scale" = scale_history)
center.value scale.value
6.758380 3.285035
Reshaping the data
Keras LSTM expects the input as well as the target data to be in a specific shape. The input has to be a 3D array of size num_samples, num_timestamps, num_features.
num_samples - the number of observations in the set. This will get fed to the model in portions of batch_size
num_timesteps - the lenght of the hidden state
num_features - number of predictions we’re using. For univariate time series, this is 1.
Suppose we wanted to forecaset 12 months ahead. The way we can do this, with Keras, is by wiring the LSTM hidden states to sets of consecutive outputs of the same lenght. Thus, if we want to produce predictions for 12 months, our LSTM should have a hidden state length of 12.
These 12 time steps will then get wired to 12 linear predictor unites using keras::time_districuted() wrapper. That wrapper’s task is to apply the same calculation (i.e., the same weight matrix) ti every state input it receives.
The target array’s format should be 3-dimensional:
- Dim 1 - batch dimension
- Dim 2 - corresponds to the number of timestamps (the forecasted ones)
- Dim 3 - the size of the wrapped layer. In our case, the wrapped layer is a
layer_dense() of a single unit, as we want exactly one prediction per point in time.
So let’s reshape the data. The main action here is creating the sliding windows of 12 steps of input, followed by 12 steps of output each.
n_timesteps <- 12
n_predictions <- n_timesteps
batch_size <- 10
#function used
build_matrix <- function(tseries, overall_timesteps) {
t(sapply(1:(length(tseries) - overall_timesteps + 1),
function(x) tseries[x:(x + overall_timesteps - 1)]))
}
reshape_X_3d <- function(X) {
dim(X) <- c(dim(X)[1], dim(X)[2], 1)
X
}
#extract values from data frame
train_vals <- df_processed_tlb %>%
filter(key == "training") %>%
select(value) %>%
dplyr::pull() #converts to a vector
valid_vals <- df_processed_tlb %>%
filter(key == "validation") %>%
select(value) %>%
dplyr::pull()
test_vals <- df_processed_tlb %>%
filter(key == "testing") %>%
select(value) %>%
dplyr::pull()
#build the windowed matrices
train_matrix <- build_matrix(train_vals, n_timesteps + n_predictions)
valid_matrix <- build_matrix(valid_vals, n_timesteps + n_predictions)
test_matrix <- build_matrix(test_vals, n_timesteps + n_predictions)
#separate matrices into training and testing parts
#discard last batch, if there fewer than batch_size samples
X_train <- train_matrix[, 1:n_timesteps]
y_train <- train_matrix[, (n_timesteps + 1):(n_timesteps * 2)]
X_train <- X_train[1:(nrow(X_train) %/% batch_size * batch_size), ]
y_train <- X_train[1:(nrow(y_train) %/% batch_size * batch_size), ]
X_valid <- valid_matrix[, 1:n_timesteps]
y_valid <- valid_matrix[, (n_timesteps + 1):(n_timesteps * 2)]
X_valid <- X_valid[1:(nrow(X_valid) %/% batch_size * batch_size), ]
y_valid <- X_valid[1:(nrow(y_valid) %/% batch_size * batch_size), ]
X_test <- test_matrix[, 1:n_timesteps]
y_test <- test_matrix[, (n_timesteps + 1):(n_timesteps * 2)]
X_test <- X_test[1:(nrow(X_test) %/% batch_size * batch_size), ]
y_test <- X_test[1:(nrow(y_test) %/% batch_size * batch_size), ]
#add on the required third axis
X_train <- reshape_X_3d(X_train)
X_valid <- reshape_X_3d(X_valid)
X_test <- reshape_X_3d(X_test)
y_train <- reshape_X_3d(y_train)
y_valid <- reshape_X_3d(y_valid)
y_test <- reshape_X_3d(y_test)
Building LSTM model
Instead of coding the hyperparameters, we’ll use tfruns to set up an environment where we could easily perform a grid search.
FLAGS <- tfruns::flags(
flag_boolean("stateful", FALSE),
flag_boolean("stack_layers", FALSE), # should be use several layers of LSTM
flag_integer("batch_size", 10), # number of samples fed to model in one go
flag_integer("n_timesteps", 12), #size of the hidden state, equals size of predictions
flag_integer("n_epochs", 100), #how many epochs to train for
flag_numeric("dropout", 0.2),
flag_numeric("recurrent_dropout", 0.2),
flag_string("loss", "logcosh"), #this particular method worked better on the task
flag_string("optimizer_type", "sgd"),
flag_integer("n_units", 128),
flag_numeric("lr", 0.003),
flag_numeric("momentum", 0.9),
flag_integer("patience", 10) #parameter to the early stopping callback
)
n_predictions <- FLAGS$n_timesteps
n_features <- 1
optimizer <- switch(FLAGS$optimizer_type,
sgd = optimizer_sgd(lr = FLAGS$lr,
momentum = FLAGS$momentum))
Using TensorFlow backend.
callbacks <- list(
callback_early_stopping(patience = FLAGS$patience))
model <- keras_model_sequential()
model %>%
layer_lstm(
units <- FLAGS$n_units,
batch_input_shape = c(FLAGS$batch_size, FLAGS$n_timesteps, n_features),
dropout = FLAGS$dropout,
recurrent_dropout = FLAGS$recurrent_dropout,
return_sequences = TRUE
) %>% time_distributed(layer_dense(units = 1))
model %>%
compile(
loss = FLAGS$loss,
optimizer = optimizer,
metrics = list("mean_squared_error")
)
history <- model %>% fit(
x = X_train,
y = y_train,
validation_data = list(X_valid, y_valid),
batch_size = FLAGS$batch_size,
epochs = FLAGS$n_epochs,
callbacks = callbacks
)
plot(history, metrics = "loss")

Now let’s see how well the model was able to capute the characterics of the training set.
We compute the evarage RSME over all sequences of predictions.
rmse_train
[1] 22.93601
How do there predictions really look? Let’s took some random start points at regular intervals:
compare_train %>%
ggplot(aes(x = index, y = value)) +
geom_line() +
geom_line(aes(y = pred_train1), color = "cyan") +
geom_line(aes(y = pred_train50), color = "red") +
geom_line(aes(y = pred_train100), color = "green") +
geom_line(aes(y = pred_train150), color = "violet") +
geom_line(aes(y = pred_train200), color = "cyan") +
geom_line(aes(y = pred_train250), color = "red") +
geom_line(aes(y = pred_train300), color = "green") +
geom_line(aes(y = pred_train350), color = "violet") +
geom_line(aes(y = pred_train400), color = "cyan") +
geom_line(aes(y = pred_train450), color = "red") +
geom_line(aes(y = pred_train500), color = "green") +
geom_line(aes(y = pred_train550), color = "violet") +
geom_line(aes(y = pred_train600), color = "cyan") +
geom_line(aes(y = pred_train650), color = "red") +
geom_line(aes(y = pred_train700), color = "green") +
geom_line(aes(y = pred_train750), color = "violet") +
ggtitle("Predictions on the training set")

Let’s do the same with test set:
rmse_test
[1] 31.36336
compare_test %>%
ggplot(aes(x = index, y = value)) +
geom_line() +
geom_line(aes(y = pred_test1), color = "cyan") +
geom_line(aes(y = pred_test50), color = "red") +
geom_line(aes(y = pred_test100), color = "green") +
geom_line(aes(y = pred_test150), color = "violet") +
geom_line(aes(y = pred_test200), color = "cyan") +
geom_line(aes(y = pred_test250), color = "red") +
geom_line(aes(y = pred_test300), color = "green") +
geom_line(aes(y = pred_test350), color = "violet") +
geom_line(aes(y = pred_test400), color = "cyan") +
geom_line(aes(y = pred_test450), color = "red") +
geom_line(aes(y = pred_test500), color = "green") +
geom_line(aes(y = pred_test550), color = "violet") +
ggtitle("Predictions on the testing set")

Backtesting the model on all splits
To obtain predictions on all splits, we move the code into a function and apply it to all splits.
obtain_predictions <- function(split) {
df_trn <- rsample::analysis(split)[1:800, , drop = FALSE]
df_val <- rsample::analysis(split)[801:1200, , drop = FALSE]
df_tst <- rsample::assessment(split)
df <- bind_rows(
df_trn %>% add_column(key = "training"),
df_val %>% add_column(key = "validation"),
df_tst %>% add_column(key = "testing")
) %>%
tibbletime::as_tbl_time(index = index)
rec_obj <- recipes::recipe(value ~ ., df) %>%
recipes::step_sqrt(value) %>% #to reduce variane and remove outliers
recipes::step_center(value) %>%
recipes::step_scale(value) %>%
recipes::prep()
#bake the recipe
df_processed_tlb <- recipes::bake(rec_obj, df)
center_history <- rec_obj$steps[[2]]$means["value"]
scale_history <- rec_obj$steps[[3]]$sds["value"]
FLAGS <- tfruns::flags(
flag_boolean("stateful", FALSE),
flag_boolean("stack_layers", FALSE),
# should be use several layers of LSTM
flag_integer("batch_size", 10),
# number of samples fed to model in one go
flag_integer("n_timesteps", 12),
#size of the hidden state, equals size of predictions
flag_integer("n_epochs", 100),
#how many epochs to train for
flag_numeric("dropout", 0.2),
flag_numeric("recurrent_dropout", 0.2),
flag_string("loss", "logcosh"),
#this particular method worked better on the task
flag_string("optimizer_type", "sgd"),
flag_integer("n_units", 128),
flag_numeric("lr", 0.003),
flag_numeric("momentum", 0.9),
flag_integer("patience", 10) #parameter to the early stopping callback
)
n_predictions <- FLAGS$n_timesteps
n_features <- 1
optimizer <- switch(FLAGS$optimizer_type,
sgd = optimizer_sgd(lr = FLAGS$lr,
momentum = FLAGS$momentum))
callbacks <- list(callback_early_stopping(patience = FLAGS$patience))
train_vals <- df_processed_tlb %>%
filter()
#extract values from data frame
train_vals <- df_processed_tlb %>%
filter(key == "training") %>%
select(value) %>%
dplyr::pull() #converts to a vector
valid_vals <- df_processed_tlb %>%
filter(key == "validation") %>%
select(value) %>%
dplyr::pull()
test_vals <- df_processed_tlb %>%
filter(key == "testing") %>%
select(value) %>%
dplyr::pull()
#build the windowed matrices
train_matrix <-
build_matrix(train_vals, n_timesteps + n_predictions)
valid_matrix <-
build_matrix(valid_vals, n_timesteps + n_predictions)
test_matrix <- build_matrix(test_vals, n_timesteps + n_predictions)
#separate matrices into training and testing parts
#discard last batch, if there fewer than batch_size samples
X_train <- train_matrix[, 1:n_timesteps]
y_train <- train_matrix[, (n_timesteps + 1):(n_timesteps * 2)]
X_train <- X_train[1:(nrow(X_train) %/% batch_size * batch_size),]
y_train <- X_train[1:(nrow(y_train) %/% batch_size * batch_size),]
X_valid <- valid_matrix[, 1:n_timesteps]
y_valid <- valid_matrix[, (n_timesteps + 1):(n_timesteps * 2)]
X_valid <- X_valid[1:(nrow(X_valid) %/% batch_size * batch_size),]
y_valid <- X_valid[1:(nrow(y_valid) %/% batch_size * batch_size),]
X_test <- test_matrix[, 1:n_timesteps]
y_test <- test_matrix[, (n_timesteps + 1):(n_timesteps * 2)]
X_test <- X_test[1:(nrow(X_test) %/% batch_size * batch_size),]
y_test <- X_test[1:(nrow(y_test) %/% batch_size * batch_size),]
#add on the required third axis
X_train <- reshape_X_3d(X_train)
X_valid <- reshape_X_3d(X_valid)
X_test <- reshape_X_3d(X_test)
y_train <- reshape_X_3d(y_train)
y_valid <- reshape_X_3d(y_valid)
y_test <- reshape_X_3d(y_test)
model <- keras_model_sequential()
model %>%
layer_lstm(
units <- FLAGS$n_units,
batch_input_shape = c(FLAGS$batch_size, FLAGS$n_timesteps, n_features),
dropout = FLAGS$dropout,
recurrent_dropout = FLAGS$recurrent_dropout,
return_sequences = TRUE
) %>% time_distributed(layer_dense(units = 1))
model %>%
compile(
loss = FLAGS$loss,
optimizer = optimizer,
metrics = list("mean_squared_error")
)
model %>% fit(
x = X_train,
y = y_train,
validation_data = list(X_valid, y_valid),
batch_size = FLAGS$batch_size,
epochs = FLAGS$n_epochs,
callbacks = callbacks
)
pred_train <- model %>%
predict(X_train, batch_size = FLAGS$batch_size) %>%
.[, , 1]
#retransform values to original scale
pred_train <- (pred_train * scale_history + center_history) ^ 2
compare_train <- df %>%
filter(key == "training")
#build a dataframe that has both actual and predicted values
for(i in 1:nrow(pred_train)) {
varname <- paste0("pred_train", i)
compare_train <-
mutate(compare_train, !!varname := c(
rep(NA, FLAGS$n_timesteps + i - 1),
pred_train[i, ],
rep(NA, nrow(compare_train) - FLAGS$n_timesteps * 2 - i + 1)
))
}
pred_test <- model %>%
predict(X_test, batch_size = FLAGS$batch_size) %>%
.[, , 1]
#retransform values to original scale
pred_test <- (pred_test * scale_history + center_history) ^ 2
compare_test <- df %>%
filter(key == "testing")
#build a dataframe that has both actual and predicted values
for(i in 1:nrow(pred_test)) {
varname <- paste0("pred_test", i)
compare_test <-
mutate(compare_test, !!varname := c(
rep(NA, FLAGS$n_timesteps + i - 1),
pred_test[i, ],
rep(NA, nrow(compare_test) - FLAGS$n_timesteps * 2 - i + 1)
))
}
list(train = compare_train, test = compare_test)
}
Mapping the function over all splits yields a list of all predictions:
Calculate RMSE on all splits:
all_split_rmses_train <- all_slits_preds_train %>%
mutate(rmse = map_dbl(predict, calc_rmse)) %>%
select(id, rmse)
all_split_rmses_train <- all_slits_preds_train %>%
mutate(rmse = map_dbl(predict, calc_rmse)) %>%
select(id, rmse)
all_split_rmses_test <- all_split_rmses_test %>%
mutate(rmse = map_dbl(predict, calc_rmse)) %>%
select(id, rmse)
Error in eval(lhs, parent, parent) :
object 'all_split_rmses_test' not found
Here how it looks - RMSE on the training set for the 6 splits:
all_split_rmses_train
all_split_rmses_test
Let’s post all predictions on training sets:


LS0tCnRpdGxlOiAiTFRTTSB0aW1lIHNlcmllcyBmb3JlY2FzdGluZyB3aXRoIEtlcmFzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KIyBDb3JlIFRpZHl2ZXJzZQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnbHVlKQpsaWJyYXJ5KGZvcmNhdHMpCgojIFRpbWUgU2VyaWVzCmxpYnJhcnkodGltZXRrKQpsaWJyYXJ5KHRpZHlxdWFudCkKbGlicmFyeSh0aWJibGV0aW1lKQoKIyBWaXN1YWxpemF0aW9uCmxpYnJhcnkoY293cGxvdCkKCiMgUHJlcHJvY2Vzc2luZwpsaWJyYXJ5KHJlY2lwZXMpCgojIFNhbXBsaW5nIC8gQWNjdXJhY3kKbGlicmFyeShyc2FtcGxlKQpsaWJyYXJ5KHlhcmRzdGljaykgCgojIE1vZGVsaW5nCmxpYnJhcnkoa2VyYXMpCmxpYnJhcnkodGZydW5zKQpgYGAKCiMgUHJlcGFyZSBEYXRhCgpgYGB7cn0Kc3VuX3Nwb3RzIDwtIGRhdGFzZXRzOjpzdW5zcG90Lm1vbnRoICU+JSAKICB0aW1ldGs6OnRrX3RibCgpICU+JSAKICBtdXRhdGUoaW5kZXggPSBhc19kYXRlKGluZGV4KSkgJT4lIAogIHRpYmJsZXRpbWU6OmFzX3RibF90aW1lKGluZGV4ID0gaW5kZXgpCmBgYAoKCiMgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcwoKV2XigJlsbCBtYWtlIHR3byBnZ3Bsb3RzIGFuZCBjb21iaW5lIHRoZW0gdXNpbmcgYGNvd3Bsb3Q6OnBsb3RfZ3JpZCgpYC4gTm90ZSB0aGF0IGZvciB0aGUgem9vbWVkIGluIHBsb3QsIHdlIG1ha2UgdXNlIG9mIGB0aWJibGV0aW1lOjp0aW1lX2ZpbHRlcigpYCwgd2hpY2ggaXMgYW4gZWFzeSB3YXkgdG8gcGVyZm9ybSB0aW1lLWJhc2VkIGZpbHRlcmluZy4KCmBgYHtyfQpwMSA8LSBzdW5fc3BvdHMgJT4lIAogIGdncGxvdChhZXMoaW5kZXgsIHZhbHVlKSkgKyAKICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArIAogIGxhYnMoCiAgICAgICAgdGl0bGUgPSAiRnJvbSAxNzQ5IHRvIDIwMTMgKEZ1bGwgRGF0YSBTZXQpIgogICAgKQoKcDIgPC0gc3VuX3Nwb3RzICU+JSAKICBmaWx0ZXIoaW5kZXggPCAiMTgwMC0wMS0wMSIpICU+JSAKICAjZmlsdGVyX3RpbWUoInN0YXJ0IiB+ICIxODAwIikgJT4lCiAgZ2dwbG90KGFlcyhpbmRleCwgdmFsdWUpKSArIAogIGdlb21fbGluZShjb2xvciA9IHBhbGV0dGVfbGlnaHQoKVtbMV1dLCBhbHBoYSA9IDAuNSkgKwogICAgZ2VvbV9wb2ludChjb2xvciA9IHBhbGV0dGVfbGlnaHQoKVtbMV1dKSArCiAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzcGFuID0gMC4yLCBzZSA9IEZBTFNFKSArIAogIGxhYnMoCiAgICAgICAgdGl0bGUgPSAiMTc0OSB0byAxNzU5IChab29tZWQgSW4gVG8gU2hvdyBDaGFuZ2VzIG92ZXIgdGhlIFllYXIpIiwKICAgICAgICBjYXB0aW9uID0gImRhdGFzZXRzOjpzdW5zcG90Lm1vbnRoIgogICAgKQoKcF90aXRsZSA8LSBjb3dwbG90OjpnZ2RyYXcoKSArIAogIGRyYXdfbGFiZWwoIlN1bnNwb3RzIiwgc2l6ZSA9IDE4LCBmb250ZmFjZSA9ICJib2xkIikKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwX3RpdGxlLCAKICAgICAgICAgICAgICAgICAgIHAxLCBwMiwgCiAgICAgICAgICAgICAgICAgICBuY29sID0gMSwgCiAgICAgICAgICAgICAgICAgICByZWxfaGVpZ2h0cyA9IGMoMC4xLCAxLCAxKSkKYGBgCgojIEJhY2t0ZXN0aW5nOiB0aW1lIHNlcmllcyBjcm9zcyB2YWxpZGF0aW9uCgpDYW4gYmUgZG9uZSB3aXRoIGByc2FtcGxlYCBwYWNrYWdlIHdoaWNoIGluY2x1ZGVzIGZhY2lsaXRpZXMgZm9yIGJhY2t0ZXN0aW5nIHRpbWUgc2VyaWVzLiBUaGUgYHJvbGxpbmdfb3JpZ2luKClgIGZ1bmN0aW9uIGlzIHVzZWQgdG8gY3JlYXRlZCBzYW1wbGVzIGRlaWduZWQgZm9yIHRpbWUgc2VyaWVzIGNyb3NzIHZhbGlkYXRpb24uCgojIyBEZXZlbG9waW5nIGEgYmFja3Rlc3Rpbmcgc3RyYXRlZ3kKClRoZSBzYW1wbGluZyBzdHJhdGVneTogd2UgdXNlIDUwIHllYXJzIChgaW5pdGlhbCA9IDEyIHggNTBgIHNhbXBsZXMpIGZvciB0cmFpbmluZyBzZXQgYW5kIDEwIHllYXJzIChgYXNzZXNzID0gMTIgeCAxMGAgc2FtcGxlcykgZm9yIHRlc3RpbmcgKHZhbGlkYXRpb24pIHNldC4gVGhlIGBza2lwYCBzcGFuIG9mIGFib3V0IDIwIHllYXJzIChgc2tpcCA9IDEyIHggMjAgLSAxYCkgdG8gYXBwcm94aW1hdGVseSBldmVubHkgZGlzdHJpYnV0ZSB0aGUgc2FtcGxlcyBpbnRvIDYgc2V0cyB0aGF0IHNwYW4gdGhlIGVudGlyZSAyNjUgeWVhcnMgb2Ygc3Vuc3BvdCBoaXN0b3J5LiBBbmQgZmluYWxseSB3ZSBzZWxlY3QgYGN1bXVsYXRpdmUgPSBGQUxTRWAgdG8gYWxsb3cgdGhlIG9yaWdpbiB0byBzaGlmdCB3aGljaCBlbnN1cmVzIHRoYXQgbW9kZWxzIG9uIG1vcmUgcmVzZXRsIGRhdGEgYXJlIG5vdCBnaXZlbiBhbiB1bmZhaXIgYWR2YW50YWdlIChtb3JlIG9ic2VydmF0aW9ucykgb3ZlciB0aG9zZSBvcGVyYXRpb24gb24gbGVzcyByZWNlbnQgZGF0YS4KCgpgYGB7cn0KcGVyaW9kc190cmFpbiA8LSAxMiAqIDEwMApwZXJpb2RzX3Rlc3QgPC0gMTIgKiA1MApza2lwX3NwYW4gPSAxMiAqIDIyIC0gMQoKcm9sbGluZ19vcmlnaW5fcmVzYW1wbGVzIDwtIHJzYW1wbGU6OnJvbGxpbmdfb3JpZ2luKAogIGRhdGEgPSBzdW5fc3BvdHMsCiAgaW5pdGlhbCA9IHBlcmlvZHNfdHJhaW4sCiAgYXNzZXNzID0gcGVyaW9kc190ZXN0LAogIGN1bXVsYXRpdmUgPSBGQUxTRSwKICBza2lwID0gc2tpcF9zcGFuCikKCnJvbGxpbmdfb3JpZ2luX3Jlc2FtcGxlcyAlPiUgaGVhZCgpCmBgYAoKIyMgVmlzdWFsaXppbmcgdGhlIGJhY2t0ZXN0aW5nIHN0cmF0ZWd5CgpUaGUgcmVzYW1wbGVzIGNhbiBiZSB2aXN1YWxpemVkIHdpdGggdHdvIGN1c3RvbSBmdW5jdGlvbnM6IAoKKiBgcGxvdF9zcGxpdCgpYCBwbG90cyBvbmUgb2YgdGhlIHJlc2FtcGxpbmcgc3BsaXRzIHVzaW5nIGBnZ3Bsb3QyYC4gVGhlIGBleHBhbmRfeV9heGlzYCBhcmd1bWVudCBpcyBhZGRlZCB0byBleHBhbmQgdGhlIGRhdGUgcmFuZ2UgdG8gdGhlIGZ1bGwgYHN1bl9zcG90c2AgZGF0YXNldCBkYXRlIHJhbmdlLgoKYGBge3J9CiNwbG90dGluZyBmdW5jdGlvbiBmb3IgYSBzaW5nbGUgc3BsaXQKcGxvdF9zcGxpdCA8LSBmdW5jdGlvbihzcGxpdCwgZXhwYW5kX3lfYXhpcyA9IFRSVUUsIGFscGhhID0gMSwgc2l6ZSA9IDEsIGJhc2Vfc2l6ZSA9IDE0KSB7CiAgCiAgI21hbmlwdWxhdGUgZGF0YQogIHRyYWluX3RibCA8LSByc2FtcGxlOjp0cmFpbmluZyhzcGxpdCkgJT4lIAogICAgdGliYmxlOjphZGRfY29sdW1uKGtleSA9ICJ0cmFpbmluZyIpCiAgCiAgIHRlc3RfdGJsIDwtIHJzYW1wbGU6OnRlc3Rpbmcoc3BsaXQpICU+JSAKICAgIHRpYmJsZTo6YWRkX2NvbHVtbihrZXkgPSAidGVzdGluZyIpCiAgIAogICBkYXRhX21hbmlwdWxhdGVkIDwtIGJpbmRfcm93cyh0cmFpbl90YmwsIHRlc3RfdGJsKSAlPiUgCiAgICAgdGliYmxldGltZTo6YXNfdGJsX3RpbWUoaW5kZXggPSBpbmRleCkgJT4lIAogICAgIG11dGF0ZShrZXkgPSBmb3JjYXRzOjpmY3RfcmVsZXZlbChrZXksICJ0cmFpbmluZyIsICJ0ZXN0aW5nIikpCiAgIAogICAjY29sbGVjdCBhdHRyaWJ1dGVzCiAgIHRyYWluX3RpbWVfc3VtbWFyeSA8LSB0cmFpbl90YmwgJT4lIAogICAgIHRpbWV0azo6dGtfaW5kZXgoKSAlPiUgCiAgICAgdGltZXRrOjp0a19nZXRfdGltZXNlcmllc19zdW1tYXJ5KCkKICAgCiAgIHRlc3RfdGltZV9zdW1tYXJ5IDwtIHRlc3RfdGJsICU+JSAKICAgICB0aW1ldGs6OnRrX2luZGV4KCkgJT4lIAogICAgIHRpbWV0azo6dGtfZ2V0X3RpbWVzZXJpZXNfc3VtbWFyeSgpCiAgIAogICAjdmlzdWFsaXplCiAgIGcgPC0gZGF0YV9tYW5pcHVsYXRlZCAlPiUgCiAgICAgZ2dwbG90KGFlcyh4ID0gaW5kZXgsIHkgPSB2YWx1ZSwgY29sb3IgPSBrZXkpKSArIAogICAgIGdlb21fbGluZShzaXplID0gc2l6ZSwgYWxwaGEgPSBhbHBoYSkgKyAKICAgICB0aWR5cXVhbnQ6OnRoZW1lX3RxKGJhc2Vfc2l6ZSA9IGJhc2Vfc2l6ZSkgKyAKICAgICB0aWR5cXVhbnQ6OnNjYWxlX2NvbG9yX3RxKCkgKyAKICAgICBsYWJzKAogICAgICAgdGl0bGUgPSBnbHVlOjpnbHVlKCJTcGxpdDoge3NwbGl0JGlkfSIpLAogICAgICAgc3VidGl0bGUgPSBnbHVlOjpnbHVlKCJ7dHJhaW5fdGltZV9zdW1tYXJ5JHN0YXJ0fSB0byB7dGVzdF90aW1lX3N1bW1hcnkkZW5kfSIpLAogICAgICAgeSA9ICIiLCB4ID0gIiIKICAgICApICsgCiAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAKICAgCiAgIGlmKGV4cGFuZF95X2F4aXMpIHsKICAgICBzdW5fc3BvdHNfdGltZV9zdW1tYXJ5IDwtIHN1bl9zcG90cyAlPiUgCiAgICAgICB0aW1ldGs6OnRrX2luZGV4KCkgJT4lIAogICAgICAgdGltZXRrOjp0a19nZXRfdGltZXNlcmllc19zdW1tYXJ5KCkKICAgICAKICAgICBnIDwtIGcgKwogICAgICAgc2NhbGVfeF9kYXRlKGxpbWl0cyA9IGMoc3VuX3Nwb3RzX3RpbWVfc3VtbWFyeSRzdGFydCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bl9zcG90c190aW1lX3N1bW1hcnkkZW5kKSkKICAgfQogICAKICAgcmV0dXJuKGcpCn0KYGBgCgpUaGUgYHBsb3Rfc3BsaXQoKWAgdGFrZXMgb25lIHNwbGl0IGFuZCByZXR1cm4gYSB2aXN1YWwgb2YgdGhlIHNhbXBsaW5nIHN0cmF0ZWd5LiAKCmBgYHtyfQpyb2xsaW5nX29yaWdpbl9yZXNhbXBsZXMkc3BsaXRzW1sxXV0gJT4lIAogIHBsb3Rfc3BsaXQoZXhwYW5kX3lfYXhpcyA9IFRSVUUpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCmBgYAoKKiBgcGxvdF9zYW1wbGluZ19wbGFuKClgIHNjYWxlcyB0aGUgYHBsb3Rfc3BsaXQoKWAgdG8gYWxsIHRoZSBzYW1wbGVzIHZpYSBgcHVycnJgIGFuZCBgY293cGxvdGAuCgpgYGB7cn0KI3Bsb3R0aW5nIGZ1bmN0aW9uIHRoYXQgc2NhbGVzIHRvIGFsbCBzcGxpdHMKcGxvdF9zYW1wbGluZ19wbGFuIDwtIGZ1bmN0aW9uKHNhbXBsaW5nX3RibCwgZXhwYW5kX3lfYXhpcyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDMsIGFscGhhID0gMSwgc2l6ZSA9IDEsIGJhc2Vfc2l6ZSA9IDE0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiU2FtcGxpbmcgUGxhbiIpIHsKICAKICAjbWFwIHBsb3Rfc2xpdCgpIHRvIHNhbXBsaW5nIHRibAogIHNhbXBsaW5nX3RibF93aXRoX3Bsb3RzIDwtIHNhbXBsaW5nX3RibCAlPiUgCiAgICBtdXRhdGUoZ2dfcGxvdHMgPSBtYXAoc3BsaXRzLCBwbG90X3NwbGl0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwYW5kX3lfYXhpcyA9IGV4cGFuZF95X2F4aXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IGFscGhhLCBiYXNlX3NpemUgPSBiYXNlX3NpemUpKQogIAogICNtYWtlIHBsb3RzIHdpdGggY293cGxvdAogIHBsb3RfbGlzdCA8LSBzYW1wbGluZ190Ymxfd2l0aF9wbG90cyRnZ19wbG90cwogIAogIHBfdGVtcCA8LSBwbG90X2xpc3RbWzFdXSArIAogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCiAgCiAgbGVnZW5kIDwtIGNvd3Bsb3Q6OmdldF9sZWdlbmQocF90ZW1wKQogIAogIHBfYm9keSA8LSBjb3dwbG90OjpwbG90X2dyaWQocGxvdGxpc3QgPSBwbG90X2xpc3QsIG5jb2wgPSBuY29sKQoKICBwX3RpdGxlIDwtIGNvd3Bsb3Q6OmdnZHJhdygpICsgCiAgICBkcmF3X2xhYmVsKHRpdGxlLCBzaXplID0gMTQsIGNvbG91ciA9IHBhbGV0dGVfbGlnaHQoKVtbMV1dLCBmb250ZmFjZSA9ICJib2xkIikKICAKICAKICBnIDwtIGNvd3Bsb3Q6OnBsb3RfZ3JpZChwX3RpdGxlLCBwX2JvZHksIG5jb2wgPSAxLCByZWxfaGVpZ2h0cyA9IGMoMC4wNSwgMSwgMC4wNSkpCiAgCiAgcmV0dXJuKGcpCiAgfQpgYGAKCgpOb3cgd2UgY2FuIHZpc3VhbGl6ZSB0aGUgZW50aXJlIGJhY2t0ZXN0aW5nIHN0cmF0ZWd5IHdpdGggYHBsb3Rfc2FtcGxpbmdfcGxhbigpYC4gCgpgYGB7cn0Kcm9sbGluZ19vcmlnaW5fcmVzYW1wbGVzICU+JSAKICBwbG90X3NhbXBsaW5nX3BsYW4oZXhwYW5kX3lfYXhpcyA9IFRSVUUsIG5jb2wgPSAzLCBhbHBoYSA9IDEsIHNpemUgPSAxLCAgYmFzZV9zaXplID0gMTAsIAogICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICJCYWNrdGVzdGluZyBTdHJhdGVneTogUm9sbGluZyBPcmlnaW4gU2FtcGxpbmcgUGxhbiIpIApgYGAKCldlIGNhbiB6b29tIGluIGJ5IHNldHRpbmcgYGV4cGFuZF95X2F4aXMgPSBGQUxTRWA6CgpgYGB7cn0Kcm9sbGluZ19vcmlnaW5fcmVzYW1wbGVzICU+JSAKICBwbG90X3NhbXBsaW5nX3BsYW4oZXhwYW5kX3lfYXhpcyA9IEZBTFNFLCBuY29sID0gMywgYWxwaGEgPSAxLCBzaXplID0gMSwgIGJhc2Vfc2l6ZSA9IDEwLCAKICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiQmFja3Rlc3RpbmcgU3RyYXRlZ3k6IFpvb21lZCBJbiIpCmBgYAoKCiMgVGhlIExTVE0gTW9kZWwKClRvIGJlZ2luLCB3ZSdsbCBkZXZlbG9wIGFuZCBMU1RNIG1vZGVsIG9uIGEgc2luZ2xlIHNhbXBsZSBmcm9tIGJhY2t0ZXN0aW5nIHN0cmF0ZWd5ICh0aGUgcmVjZW50IG9uZSkgYW5kIHRoZW4gYXBwbHkgdGhlIG1vZGVsIHRvIGFsbCBzYW1wbGVzIHRvIGludmVzdGlnYXRlIG1vZGVsaW5nIHBlcmZvcm1hbmNlLgoKYGBge3J9CmV4YW1wbGVfc3BsaXQgPC0gcm9sbGluZ19vcmlnaW5fcmVzYW1wbGVzJHNwbGl0c1tbNl1dCmV4YW1wbGVfc3BsaXRfaWQgPC0gcm9sbGluZ19vcmlnaW5fcmVzYW1wbGVzJGlkW1s2XV0KYGBgCgpMZXQncyByZXVzZSB0aGUgYHBsb3Rfc3BsaXQoKWAgdG8gdmlzdWFsaXplIHRoZSBzcGxpdC4KCmBgYHtyfQpwbG90X3NwbGl0KGV4YW1wbGVfc3BsaXQsIGV4cGFuZF95X2F4aXMgPSBGQUxTRSwgc2l6ZSA9IDAuNSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKyAKICBnZ3RpdGxlKGdsdWU6OmdsdWUoIlNwbGl0OiB7ZXhhbXBsZV9zcGxpdF9pZH0iKSkKYGBgCgojIyBEYXRhIFNldHVwCgpUbyBhaWQgaHlwZXJwYXJhbWV0ZXIgdHVuaW5nLCB3ZSBuZWVkIHRvIGNyZWF0ZSBhIHZhbGlkYXRpb24gc2V0IGFzIHdlbGwuIApTbyB3ZSdsbCBkZWRpY2F0ZSAyLzMgb2YgdHJhaW5pbmcgc2V0IHRvIHRyYWluaW5nLCBhbmQgcmVtYWluaW5nIDEvMyB0byB2YWxpZGF0aW9uLgoKYGBge3J9CmRmX3RybiA8LSByc2FtcGxlOjphbmFseXNpcyhleGFtcGxlX3NwbGl0KVsxOjgwMCwgLCBkcm9wID0gRkFMU0VdCmRmX3ZhbCA8LSByc2FtcGxlOjphbmFseXNpcyhleGFtcGxlX3NwbGl0KVs4MDE6MTIwMCwgLCBkcm9wID0gRkFMU0VdCmRmX3RzdCA8LSByc2FtcGxlOjphc3Nlc3NtZW50KGV4YW1wbGVfc3BsaXQpCmBgYAoKRmlyc3QsIGxldCdzIGNvbWJpbmUgdGhlIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMgaW50byBhIHNpbmdsZSBkYXRhc2V0IHdpdGggYSBjb2x1bW4gYGtleWAgdGhhdCBzcGVjaWZpZXMgd2hlcmUgdGhleSBjYW1lIGZyb20gKHRyYWluIG9yIHRlc3QpLiAKCmBgYHtyfQpkZiA8LSBiaW5kX3Jvd3MoCiAgZGZfdHJuICU+JSBhZGRfY29sdW1uKGtleSA9ICJ0cmFpbmluZyIpLCAKICBkZl92YWwgJT4lIGFkZF9jb2x1bW4oa2V5ID0gInZhbGlkYXRpb24iKSwgCiAgZGZfdHN0ICU+JSBhZGRfY29sdW1uKGtleSA9ICJ0ZXN0aW5nIikKKSAlPiUgCiAgdGliYmxldGltZTo6YXNfdGJsX3RpbWUoaW5kZXggPSBpbmRleCkKYGBgCgojIyBQcmVwcm9jZXNzaW5nIHdpdGggcmVjaXBlcwoKVGhlIExTVE0gd29ya3MgYmV0dGVyIGlmIHRoZSBpbnB1dCBkYXRhIGhhcyBiZWVuIGNlbnRlcmVkIGFuZCBzY2FsZWQuIFRoaXMgKGFuZCBtYW55IG90aGVyIHVzZWZ1bCB0cmFuc2Zvcm1hdGlvbnMpIGNhbiBiZSBkb25lIHdpdGggYHJlY2lwZXNgIHBhY2thZ2UuCgpgYGB7cn0KcmVjX29iaiA8LSByZWNpcGVzOjpyZWNpcGUodmFsdWUgfiAuLCBkZikgJT4lIAogIHJlY2lwZXM6OnN0ZXBfc3FydCh2YWx1ZSkgJT4lICN0byByZWR1Y2UgdmFyaWFuZSBhbmQgcmVtb3ZlIG91dGxpZXJzCiAgcmVjaXBlczo6c3RlcF9jZW50ZXIodmFsdWUpICU+JSAKICByZWNpcGVzOjpzdGVwX3NjYWxlKHZhbHVlKSAlPiUgCiAgcmVjaXBlczo6cHJlcCgpCgojYmFrZSB0aGUgcmVjaXBlCgpkZl9wcm9jZXNzZWRfdGxiIDwtIHJlY2lwZXM6OmJha2UocmVjX29iaiwgZGYpCmBgYAoKTm93LCBsZXQncyBjYXB0dXJlIHRoZSBvcmlnaW5hbCBjZW50ZXIgYW5kIHNjYWxlIHNvIHdlIGNhbiBpbnZlcnQgdGhlIHN0ZXBzIGFmdGVyIG1vZGVsaW5nLiBUaGUgc3F1YXJlIHJvb3Qgc3RlcCBjYW4gdGhlbiBzaW1wbHkgYmUgdW5kb25lIGJ5IHNxdWFyaW5nIHRoZSBiYWNrIHRyYW5zZm9ybWVkIGRhdGEuCgpgYGB7cn0KY2VudGVyX2hpc3RvcnkgPC0gcmVjX29iaiRzdGVwc1tbMl1dJG1lYW5zWyJ2YWx1ZSJdCnNjYWxlX2hpc3RvcnkgPC0gcmVjX29iaiRzdGVwc1tbM11dJHNkc1sidmFsdWUiXQpjKCJjZW50ZXIiID0gY2VudGVyX2hpc3RvcnksICJzY2FsZSIgPSBzY2FsZV9oaXN0b3J5KQpgYGAKCiMjIFJlc2hhcGluZyB0aGUgZGF0YQoKS2VyYXMgTFNUTSBleHBlY3RzIHRoZSBpbnB1dCBhcyB3ZWxsIGFzIHRoZSB0YXJnZXQgZGF0YSB0byBiZSBpbiBhIHNwZWNpZmljIHNoYXBlLiBUaGUgaW5wdXQgaGFzIHRvIGJlIGEgM0QgYXJyYXkgb2Ygc2l6ZSBgbnVtX3NhbXBsZXNgLCBgbnVtX3RpbWVzdGFtcHNgLCBgbnVtX2ZlYXR1cmVzYC4KCiogYG51bV9zYW1wbGVzYCAtIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIHRoZSBzZXQuIFRoaXMgd2lsbCBnZXQgZmVkIHRvIHRoZSBtb2RlbCBpbiBwb3J0aW9ucyBvZiBgYmF0Y2hfc2l6ZWAKKiBgbnVtX3RpbWVzdGVwc2AgLSB0aGUgbGVuZ2h0IG9mIHRoZSBoaWRkZW4gc3RhdGUKKiBgbnVtX2ZlYXR1cmVzYCAtIG51bWJlciBvZiBwcmVkaWN0aW9ucyB3ZSdyZSB1c2luZy4gRm9yIHVuaXZhcmlhdGUgdGltZSBzZXJpZXMsIHRoaXMgaXMgMS4KClN1cHBvc2Ugd2Ugd2FudGVkIHRvIGZvcmVjYXNldCAxMiBtb250aHMgYWhlYWQuIFRoZSB3YXkgd2UgY2FuIGRvIHRoaXMsIHdpdGggS2VyYXMsIGlzIGJ5IHdpcmluZyB0aGUgTFNUTSBoaWRkZW4gc3RhdGVzIHRvIHNldHMgb2YgY29uc2VjdXRpdmUgb3V0cHV0cyBvZiB0aGUgc2FtZSBsZW5naHQuIFRodXMsIGlmIHdlIHdhbnQgdG8gcHJvZHVjZSBwcmVkaWN0aW9ucyBmb3IgMTIgbW9udGhzLCBvdXIgTFNUTSBzaG91bGQgaGF2ZSBhIGhpZGRlbiBzdGF0ZSBsZW5ndGggb2YgMTIuIAoKVGhlc2UgMTIgdGltZSBzdGVwcyB3aWxsIHRoZW4gZ2V0IHdpcmVkIHRvIDEyIGxpbmVhciBwcmVkaWN0b3IgdW5pdGVzIHVzaW5nIGBrZXJhczo6dGltZV9kaXN0cmljdXRlZCgpYCB3cmFwcGVyLgpUaGF0IHdyYXBwZXIncyB0YXNrIGlzIHRvIGFwcGx5IHRoZSBzYW1lIGNhbGN1bGF0aW9uIChpLmUuLCB0aGUgc2FtZSB3ZWlnaHQgbWF0cml4KSB0aSBldmVyeSBzdGF0ZSBpbnB1dCBpdCByZWNlaXZlcy4KClRoZSB0YXJnZXQgYXJyYXkncyBmb3JtYXQgc2hvdWxkIGJlIDMtZGltZW5zaW9uYWw6IAoKMS4gKipEaW0gMSoqIC0gYmF0Y2ggZGltZW5zaW9uCjIuICoqRGltIDIqKiAtIGNvcnJlc3BvbmRzIHRvIHRoZSBudW1iZXIgb2YgdGltZXN0YW1wcyAodGhlIGZvcmVjYXN0ZWQgb25lcykKMy4gKipEaW0gMyoqIC0gdGhlIHNpemUgb2YgdGhlIHdyYXBwZWQgbGF5ZXIuIEluIG91ciBjYXNlLCB0aGUgd3JhcHBlZCBsYXllciBpcyBhIGBsYXllcl9kZW5zZSgpYCBvZiBhIHNpbmdsZSB1bml0LCBhcyB3ZSB3YW50IGV4YWN0bHkgb25lIHByZWRpY3Rpb24gcGVyIHBvaW50IGluIHRpbWUuIAoKU28gbGV0J3MgcmVzaGFwZSB0aGUgZGF0YS4gVGhlIG1haW4gYWN0aW9uIGhlcmUgaXMgY3JlYXRpbmcgdGhlIHNsaWRpbmcgd2luZG93cyBvZiAxMiBzdGVwcyBvZiBpbnB1dCwgZm9sbG93ZWQgYnkgMTIgc3RlcHMgb2Ygb3V0cHV0IGVhY2guIAoKYGBge3J9Cm5fdGltZXN0ZXBzIDwtIDEyCm5fcHJlZGljdGlvbnMgPC0gbl90aW1lc3RlcHMKYmF0Y2hfc2l6ZSA8LSAxMAoKI2Z1bmN0aW9uIHVzZWQKYnVpbGRfbWF0cml4IDwtIGZ1bmN0aW9uKHRzZXJpZXMsIG92ZXJhbGxfdGltZXN0ZXBzKSB7CiAgCiAgdChzYXBwbHkoMToobGVuZ3RoKHRzZXJpZXMpIC0gb3ZlcmFsbF90aW1lc3RlcHMgKyAxKSwgCiAgICBmdW5jdGlvbih4KSB0c2VyaWVzW3g6KHggKyBvdmVyYWxsX3RpbWVzdGVwcyAtIDEpXSkpCgp9CgpyZXNoYXBlX1hfM2QgPC0gZnVuY3Rpb24oWCkgewogIGRpbShYKSA8LSBjKGRpbShYKVsxXSwgZGltKFgpWzJdLCAxKQogIFgKfQoKI2V4dHJhY3QgdmFsdWVzIGZyb20gZGF0YSBmcmFtZQp0cmFpbl92YWxzIDwtIGRmX3Byb2Nlc3NlZF90bGIgJT4lIAogIGZpbHRlcihrZXkgPT0gInRyYWluaW5nIikgJT4lIAogIHNlbGVjdCh2YWx1ZSkgJT4lIAogIGRwbHlyOjpwdWxsKCkgI2NvbnZlcnRzIHRvIGEgdmVjdG9yCgp2YWxpZF92YWxzIDwtIGRmX3Byb2Nlc3NlZF90bGIgJT4lIAogIGZpbHRlcihrZXkgPT0gInZhbGlkYXRpb24iKSAlPiUgCiAgc2VsZWN0KHZhbHVlKSAlPiUgCiAgZHBseXI6OnB1bGwoKQoKdGVzdF92YWxzIDwtIGRmX3Byb2Nlc3NlZF90bGIgJT4lIAogIGZpbHRlcihrZXkgPT0gInRlc3RpbmciKSAlPiUgCiAgc2VsZWN0KHZhbHVlKSAlPiUgCiAgZHBseXI6OnB1bGwoKQoKI2J1aWxkIHRoZSB3aW5kb3dlZCBtYXRyaWNlcwp0cmFpbl9tYXRyaXggPC0gYnVpbGRfbWF0cml4KHRyYWluX3ZhbHMsIG5fdGltZXN0ZXBzICsgbl9wcmVkaWN0aW9ucykKdmFsaWRfbWF0cml4IDwtIGJ1aWxkX21hdHJpeCh2YWxpZF92YWxzLCBuX3RpbWVzdGVwcyArIG5fcHJlZGljdGlvbnMpCnRlc3RfbWF0cml4IDwtIGJ1aWxkX21hdHJpeCh0ZXN0X3ZhbHMsIG5fdGltZXN0ZXBzICsgbl9wcmVkaWN0aW9ucykKCiNzZXBhcmF0ZSBtYXRyaWNlcyBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHBhcnRzCiNkaXNjYXJkIGxhc3QgYmF0Y2gsIGlmIHRoZXJlIGZld2VyIHRoYW4gYmF0Y2hfc2l6ZSBzYW1wbGVzCgpYX3RyYWluIDwtIHRyYWluX21hdHJpeFssIDE6bl90aW1lc3RlcHNdCnlfdHJhaW4gPC0gdHJhaW5fbWF0cml4WywgKG5fdGltZXN0ZXBzICsgMSk6KG5fdGltZXN0ZXBzICogMildClhfdHJhaW4gPC0gWF90cmFpblsxOihucm93KFhfdHJhaW4pICUvJSBiYXRjaF9zaXplICogYmF0Y2hfc2l6ZSksIF0KeV90cmFpbiA8LSBYX3RyYWluWzE6KG5yb3coeV90cmFpbikgJS8lIGJhdGNoX3NpemUgKiBiYXRjaF9zaXplKSwgXQoKWF92YWxpZCA8LSB2YWxpZF9tYXRyaXhbLCAxOm5fdGltZXN0ZXBzXQp5X3ZhbGlkIDwtIHZhbGlkX21hdHJpeFssIChuX3RpbWVzdGVwcyArIDEpOihuX3RpbWVzdGVwcyAqIDIpXQpYX3ZhbGlkIDwtIFhfdmFsaWRbMToobnJvdyhYX3ZhbGlkKSAlLyUgYmF0Y2hfc2l6ZSAqIGJhdGNoX3NpemUpLCBdCnlfdmFsaWQgPC0gWF92YWxpZFsxOihucm93KHlfdmFsaWQpICUvJSBiYXRjaF9zaXplICogYmF0Y2hfc2l6ZSksIF0KCgpYX3Rlc3QgPC0gdGVzdF9tYXRyaXhbLCAxOm5fdGltZXN0ZXBzXQp5X3Rlc3QgPC0gdGVzdF9tYXRyaXhbLCAobl90aW1lc3RlcHMgKyAxKToobl90aW1lc3RlcHMgKiAyKV0KWF90ZXN0IDwtIFhfdGVzdFsxOihucm93KFhfdGVzdCkgJS8lIGJhdGNoX3NpemUgKiBiYXRjaF9zaXplKSwgXQp5X3Rlc3QgPC0gWF90ZXN0WzE6KG5yb3coeV90ZXN0KSAlLyUgYmF0Y2hfc2l6ZSAqIGJhdGNoX3NpemUpLCBdCgojYWRkIG9uIHRoZSByZXF1aXJlZCB0aGlyZCBheGlzClhfdHJhaW4gPC0gcmVzaGFwZV9YXzNkKFhfdHJhaW4pClhfdmFsaWQgPC0gcmVzaGFwZV9YXzNkKFhfdmFsaWQpClhfdGVzdCA8LSByZXNoYXBlX1hfM2QoWF90ZXN0KQoKeV90cmFpbiA8LSByZXNoYXBlX1hfM2QoeV90cmFpbikKeV92YWxpZCA8LSByZXNoYXBlX1hfM2QoeV92YWxpZCkKeV90ZXN0IDwtIHJlc2hhcGVfWF8zZCh5X3Rlc3QpCmBgYAoKIyMgQnVpbGRpbmcgTFNUTSBtb2RlbAoKSW5zdGVhZCBvZiBjb2RpbmcgdGhlIGh5cGVycGFyYW1ldGVycywgd2UnbGwgdXNlIGB0ZnJ1bnNgIHRvIHNldCB1cCBhbiBlbnZpcm9ubWVudCB3aGVyZSB3ZSBjb3VsZCBlYXNpbHkgcGVyZm9ybSBhIGdyaWQgc2VhcmNoLgoKYGBge3J9CkZMQUdTIDwtIHRmcnVuczo6ZmxhZ3MoCiAgZmxhZ19ib29sZWFuKCJzdGF0ZWZ1bCIsIEZBTFNFKSwKICBmbGFnX2Jvb2xlYW4oInN0YWNrX2xheWVycyIsIEZBTFNFKSwgIyBzaG91bGQgYmUgdXNlIHNldmVyYWwgbGF5ZXJzIG9mIExTVE0KICBmbGFnX2ludGVnZXIoImJhdGNoX3NpemUiLCAxMCksICMgbnVtYmVyIG9mIHNhbXBsZXMgZmVkIHRvIG1vZGVsIGluIG9uZSBnbwogIGZsYWdfaW50ZWdlcigibl90aW1lc3RlcHMiLCAxMiksICNzaXplIG9mIHRoZSBoaWRkZW4gc3RhdGUsIGVxdWFscyBzaXplIG9mIHByZWRpY3Rpb25zCiAgZmxhZ19pbnRlZ2VyKCJuX2Vwb2NocyIsIDEwMCksICNob3cgbWFueSBlcG9jaHMgdG8gdHJhaW4gZm9yCiAgZmxhZ19udW1lcmljKCJkcm9wb3V0IiwgMC4yKSwgCiAgZmxhZ19udW1lcmljKCJyZWN1cnJlbnRfZHJvcG91dCIsIDAuMiksIAogIGZsYWdfc3RyaW5nKCJsb3NzIiwgImxvZ2Nvc2giKSwgI3RoaXMgcGFydGljdWxhciBtZXRob2Qgd29ya2VkIGJldHRlciBvbiB0aGUgdGFzawogIGZsYWdfc3RyaW5nKCJvcHRpbWl6ZXJfdHlwZSIsICJzZ2QiKSwgCiAgZmxhZ19pbnRlZ2VyKCJuX3VuaXRzIiwgMTI4KSwgCiAgZmxhZ19udW1lcmljKCJsciIsIDAuMDAzKSwgCiAgZmxhZ19udW1lcmljKCJtb21lbnR1bSIsIDAuOSksCiAgZmxhZ19pbnRlZ2VyKCJwYXRpZW5jZSIsIDEwKSAjcGFyYW1ldGVyIHRvIHRoZSBlYXJseSBzdG9wcGluZyBjYWxsYmFjawogIAopCgpuX3ByZWRpY3Rpb25zIDwtIEZMQUdTJG5fdGltZXN0ZXBzCm5fZmVhdHVyZXMgPC0gMQpvcHRpbWl6ZXIgPC0gc3dpdGNoKEZMQUdTJG9wdGltaXplcl90eXBlLCAKICAgICAgICAgICAgICAgICAgICBzZ2QgPSBvcHRpbWl6ZXJfc2dkKGxyID0gRkxBR1MkbHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb21lbnR1bSA9IEZMQUdTJG1vbWVudHVtKSkKY2FsbGJhY2tzIDwtIGxpc3QoCiAgY2FsbGJhY2tfZWFybHlfc3RvcHBpbmcocGF0aWVuY2UgPSBGTEFHUyRwYXRpZW5jZSkpCmBgYAoKCmBgYHtyfQptb2RlbCA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkKCm1vZGVsICU+JSAKICBsYXllcl9sc3RtKAogICAgdW5pdHMgPC0gRkxBR1Mkbl91bml0cywKICAgIGJhdGNoX2lucHV0X3NoYXBlID0gYyhGTEFHUyRiYXRjaF9zaXplLCBGTEFHUyRuX3RpbWVzdGVwcywgbl9mZWF0dXJlcyksCiAgICBkcm9wb3V0ID0gRkxBR1MkZHJvcG91dCwKICAgIHJlY3VycmVudF9kcm9wb3V0ID0gRkxBR1MkcmVjdXJyZW50X2Ryb3BvdXQsIAogICAgcmV0dXJuX3NlcXVlbmNlcyA9IFRSVUUKICApICU+JSB0aW1lX2Rpc3RyaWJ1dGVkKGxheWVyX2RlbnNlKHVuaXRzID0gMSkpCgoKbW9kZWwgJT4lIAogIGNvbXBpbGUoCiAgICBsb3NzID0gRkxBR1MkbG9zcywKICAgIG9wdGltaXplciA9IG9wdGltaXplciwgCiAgICBtZXRyaWNzID0gbGlzdCgibWVhbl9zcXVhcmVkX2Vycm9yIikKICApCgpoaXN0b3J5IDwtIG1vZGVsICU+JSBmaXQoCiAgeCA9IFhfdHJhaW4sIAogIHkgPSB5X3RyYWluLCAKICB2YWxpZGF0aW9uX2RhdGEgPSBsaXN0KFhfdmFsaWQsIHlfdmFsaWQpLCAKICBiYXRjaF9zaXplID0gRkxBR1MkYmF0Y2hfc2l6ZSwKICBlcG9jaHMgICAgID0gRkxBR1Mkbl9lcG9jaHMsCiAgY2FsbGJhY2tzID0gY2FsbGJhY2tzCikKYGBgCgpgYGB7cn0KcGxvdChoaXN0b3J5LCBtZXRyaWNzID0gImxvc3MiKQpgYGAKCk5vdyBsZXQncyBzZWUgaG93IHdlbGwgdGhlIG1vZGVsIHdhcyBhYmxlIHRvIGNhcHV0ZSB0aGUgY2hhcmFjdGVyaWNzIG9mIHRoZSB0cmFpbmluZyBzZXQuCgpgYGB7cn0KcHJlZF90cmFpbiA8LSBtb2RlbCAlPiUgCiAgcHJlZGljdChYX3RyYWluLCBiYXRjaF9zaXplID0gRkxBR1MkYmF0Y2hfc2l6ZSkgJT4lIAogIC5bLCAsIDFdCgojcmV0cmFuc2Zvcm0gdmFsdWVzIHRvIG9yaWdpbmFsIHNjYWxlCnByZWRfdHJhaW4gPC0gKHByZWRfdHJhaW4gKiBzY2FsZV9oaXN0b3J5ICsgY2VudGVyX2hpc3RvcnkpIF4gMgpjb21wYXJlX3RyYWluIDwtIGRmICU+JSAKICBmaWx0ZXIoa2V5ID09ICJ0cmFpbmluZyIpCgojYnVpbGQgYSBkYXRhZnJhbWUgdGhhdCBoYXMgYm90aCBhY3R1YWwgYW5kIHByZWRpY3RlZCB2YWx1ZXMKZm9yKGkgaW4gMTpucm93KHByZWRfdHJhaW4pKSB7CiAgdmFybmFtZSA8LSBwYXN0ZTAoInByZWRfdHJhaW4iLCBpKQogIGNvbXBhcmVfdHJhaW4gPC0gCiAgICBtdXRhdGUoY29tcGFyZV90cmFpbiwgISF2YXJuYW1lIDo9IGMoCiAgICAgIHJlcChOQSwgRkxBR1Mkbl90aW1lc3RlcHMgKyBpIC0gMSksIAogICAgICBwcmVkX3RyYWluW2ksIF0sIAogICAgICByZXAoTkEsIG5yb3coY29tcGFyZV90cmFpbikgLSBGTEFHUyRuX3RpbWVzdGVwcyAqIDIgLSBpICsgMSkKICAgICkpCn0KYGBgCgpXZSBjb21wdXRlIHRoZSBldmFyYWdlIFJTTUUgb3ZlciBhbGwgc2VxdWVuY2VzIG9mIHByZWRpY3Rpb25zLgoKYGBge3J9CmNvbG4gPC0gY29sbmFtZXMoY29tcGFyZV90cmFpbilbNDpuY29sKGNvbXBhcmVfdHJhaW4pXQpjb2xzIDwtIG1hcChjb2xuLCBkcGx5cjo6cXVvKGRwbHlyOjpzeW0oLikpKQoKcm1zZV90cmFpbiA8LSAKICBtYXBfZGJsKGNvbHMsIGZ1bmN0aW9uKGNvbCkKICAgIHlhcmRzdGljazo6cm1zZSgKICAgIGNvbXBhcmVfdHJhaW4sIAogICAgdHJ1dGggPSB2YWx1ZSwgCiAgICBlc3RpbWF0ZSA9ICEhY29sLAogICAgbmEucm0gPSBUUlVFCiAgKSkgJT4lIG1lYW4oKQoKcm1zZV90cmFpbgpgYGAKCkhvdyBkbyB0aGVyZSBwcmVkaWN0aW9ucyByZWFsbHkgbG9vaz8gTGV0J3MgdG9vayBzb21lIHJhbmRvbSBzdGFydCBwb2ludHMgYXQgcmVndWxhciBpbnRlcnZhbHM6CgpgYGB7ciwgd2FybmluZz1GQUxTRX0KY29tcGFyZV90cmFpbiAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gaW5kZXgsIHkgPSB2YWx1ZSkpICsgCiAgZ2VvbV9saW5lKCkgKyAKICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3RyYWluMSksIGNvbG9yID0gImN5YW4iKSArIAogIGdlb21fbGluZShhZXMoeSA9IHByZWRfdHJhaW41MCksIGNvbG9yID0gInJlZCIpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90cmFpbjEwMCksIGNvbG9yID0gImdyZWVuIikgKyAKICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3RyYWluMTUwKSwgY29sb3IgPSAidmlvbGV0IikgKyAKICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3RyYWluMjAwKSwgY29sb3IgPSAiY3lhbiIpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90cmFpbjI1MCksIGNvbG9yID0gInJlZCIpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90cmFpbjMwMCksIGNvbG9yID0gImdyZWVuIikgKyAKICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3RyYWluMzUwKSwgY29sb3IgPSAidmlvbGV0IikgKyAKICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3RyYWluNDAwKSwgY29sb3IgPSAiY3lhbiIpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90cmFpbjQ1MCksIGNvbG9yID0gInJlZCIpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90cmFpbjUwMCksIGNvbG9yID0gImdyZWVuIikgKyAKICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3RyYWluNTUwKSwgY29sb3IgPSAidmlvbGV0IikgKyAKICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3RyYWluNjAwKSwgY29sb3IgPSAiY3lhbiIpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90cmFpbjY1MCksIGNvbG9yID0gInJlZCIpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90cmFpbjcwMCksIGNvbG9yID0gImdyZWVuIikgKyAKICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3RyYWluNzUwKSwgY29sb3IgPSAidmlvbGV0IikgKwogIGdndGl0bGUoIlByZWRpY3Rpb25zIG9uIHRoZSB0cmFpbmluZyBzZXQiKQoKYGBgCgpMZXQncyBkbyB0aGUgc2FtZSB3aXRoIHRlc3Qgc2V0OgoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CnByZWRfdGVzdCA8LSBtb2RlbCAlPiUgCiAgcHJlZGljdChYX3Rlc3QsIGJhdGNoX3NpemUgPSBGTEFHUyRiYXRjaF9zaXplKSAlPiUgCiAgLlssICwgMV0KCiNyZXRyYW5zZm9ybSB2YWx1ZXMgdG8gb3JpZ2luYWwgc2NhbGUKcHJlZF90ZXN0IDwtIChwcmVkX3Rlc3QgKiBzY2FsZV9oaXN0b3J5ICsgY2VudGVyX2hpc3RvcnkpIF4gMgpjb21wYXJlX3Rlc3QgPC0gZGYgJT4lIAogIGZpbHRlcihrZXkgPT0gInRlc3RpbmciKQoKI2J1aWxkIGEgZGF0YWZyYW1lIHRoYXQgaGFzIGJvdGggYWN0dWFsIGFuZCBwcmVkaWN0ZWQgdmFsdWVzCmZvcihpIGluIDE6bnJvdyhwcmVkX3Rlc3QpKSB7CiAgdmFybmFtZSA8LSBwYXN0ZTAoInByZWRfdGVzdCIsIGkpCiAgY29tcGFyZV90ZXN0IDwtIAogICAgbXV0YXRlKGNvbXBhcmVfdGVzdCwgISF2YXJuYW1lIDo9IGMoCiAgICAgIHJlcChOQSwgRkxBR1Mkbl90aW1lc3RlcHMgKyBpIC0gMSksIAogICAgICBwcmVkX3Rlc3RbaSwgXSwgCiAgICAgIHJlcChOQSwgbnJvdyhjb21wYXJlX3Rlc3QpIC0gRkxBR1Mkbl90aW1lc3RlcHMgKiAyIC0gaSArIDEpCiAgICApKQp9CgoKIyBjb21wYXJlX3Rlc3QgJT4lIAojICAgd3JpdGVfY3N2KHN0cl9yZXBsYWNlKG1vZGVsX3BhdGgsICIuaGRmNSIsICIudGVzdC5jc3YiKSkKCmNvbXBhcmVfdGVzdFtGTEFHUyRuX3RpbWVzdGVwczooRkxBR1Mkbl90aW1lc3RlcHMgKyAxMCksIGMoMiwgNDo4KV0gJT4lIHByaW50KCkKCmNvbG4gPC0gY29sbmFtZXMoY29tcGFyZV90ZXN0KVs0Om5jb2woY29tcGFyZV90ZXN0KV0KY29scyA8LSBtYXAoY29sbiwgZHBseXI6OnF1byhkcGx5cjo6c3ltKC4pKSkKCnJtc2VfdGVzdCA8LSAKICBtYXBfZGJsKGNvbHMsIGZ1bmN0aW9uKGNvbCkKICAgIHlhcmRzdGljazo6cm1zZSgKICAgIGNvbXBhcmVfdGVzdCwgCiAgICB0cnV0aCA9IHZhbHVlLCAKICAgIGVzdGltYXRlID0gISFjb2wsCiAgICBuYS5ybSA9IFRSVUUKICApKSAlPiUgbWVhbigpCgpybXNlX3Rlc3QKCmNvbXBhcmVfdGVzdCAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gaW5kZXgsIHkgPSB2YWx1ZSkpICsgCiAgZ2VvbV9saW5lKCkgKyAKICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3Rlc3QxKSwgY29sb3IgPSAiY3lhbiIpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90ZXN0NTApLCBjb2xvciA9ICJyZWQiKSArIAogIGdlb21fbGluZShhZXMoeSA9IHByZWRfdGVzdDEwMCksIGNvbG9yID0gImdyZWVuIikgKyAKICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3Rlc3QxNTApLCBjb2xvciA9ICJ2aW9sZXQiKSArIAogIGdlb21fbGluZShhZXMoeSA9IHByZWRfdGVzdDIwMCksIGNvbG9yID0gImN5YW4iKSArIAogIGdlb21fbGluZShhZXMoeSA9IHByZWRfdGVzdDI1MCksIGNvbG9yID0gInJlZCIpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90ZXN0MzAwKSwgY29sb3IgPSAiZ3JlZW4iKSArIAogIGdlb21fbGluZShhZXMoeSA9IHByZWRfdGVzdDM1MCksIGNvbG9yID0gInZpb2xldCIpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90ZXN0NDAwKSwgY29sb3IgPSAiY3lhbiIpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90ZXN0NDUwKSwgY29sb3IgPSAicmVkIikgKyAKICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3Rlc3Q1MDApLCBjb2xvciA9ICJncmVlbiIpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90ZXN0NTUwKSwgY29sb3IgPSAidmlvbGV0IikgKyAKICBnZ3RpdGxlKCJQcmVkaWN0aW9ucyBvbiB0aGUgdGVzdGluZyBzZXQiKQpgYGAKCiMgQmFja3Rlc3RpbmcgdGhlIG1vZGVsIG9uIGFsbCBzcGxpdHMKClRvIG9idGFpbiBwcmVkaWN0aW9ucyBvbiBhbGwgc3BsaXRzLCB3ZSBtb3ZlIHRoZSBjb2RlIGludG8gYSBmdW5jdGlvbiBhbmQgYXBwbHkgaXQgdG8gYWxsIHNwbGl0cy4KCmBgYHtyfQpvYnRhaW5fcHJlZGljdGlvbnMgPC0gZnVuY3Rpb24oc3BsaXQpIHsKICBkZl90cm4gPC0gcnNhbXBsZTo6YW5hbHlzaXMoc3BsaXQpWzE6ODAwLCAsIGRyb3AgPSBGQUxTRV0KICBkZl92YWwgPC0gcnNhbXBsZTo6YW5hbHlzaXMoc3BsaXQpWzgwMToxMjAwLCAsIGRyb3AgPSBGQUxTRV0KICBkZl90c3QgPC0gcnNhbXBsZTo6YXNzZXNzbWVudChzcGxpdCkKICAKICBkZiA8LSBiaW5kX3Jvd3MoCiAgICBkZl90cm4gJT4lIGFkZF9jb2x1bW4oa2V5ID0gInRyYWluaW5nIiksCiAgICBkZl92YWwgJT4lIGFkZF9jb2x1bW4oa2V5ID0gInZhbGlkYXRpb24iKSwKICAgIGRmX3RzdCAlPiUgYWRkX2NvbHVtbihrZXkgPSAidGVzdGluZyIpCiAgICApICU+JQogICAgdGliYmxldGltZTo6YXNfdGJsX3RpbWUoaW5kZXggPSBpbmRleCkKICAKICByZWNfb2JqIDwtIHJlY2lwZXM6OnJlY2lwZSh2YWx1ZSB+IC4sIGRmKSAlPiUKICAgIHJlY2lwZXM6OnN0ZXBfc3FydCh2YWx1ZSkgJT4lICN0byByZWR1Y2UgdmFyaWFuZSBhbmQgcmVtb3ZlIG91dGxpZXJzCiAgICByZWNpcGVzOjpzdGVwX2NlbnRlcih2YWx1ZSkgJT4lCiAgICByZWNpcGVzOjpzdGVwX3NjYWxlKHZhbHVlKSAlPiUKICAgIHJlY2lwZXM6OnByZXAoKQogICAgCiAgICAjYmFrZSB0aGUgcmVjaXBlCiAgICAKICBkZl9wcm9jZXNzZWRfdGxiIDwtIHJlY2lwZXM6OmJha2UocmVjX29iaiwgZGYpCiAgCiAgY2VudGVyX2hpc3RvcnkgPC0gcmVjX29iaiRzdGVwc1tbMl1dJG1lYW5zWyJ2YWx1ZSJdCiAgc2NhbGVfaGlzdG9yeSA8LSByZWNfb2JqJHN0ZXBzW1szXV0kc2RzWyJ2YWx1ZSJdCiAgCiAgRkxBR1MgPC0gdGZydW5zOjpmbGFncygKICAgIGZsYWdfYm9vbGVhbigic3RhdGVmdWwiLCBGQUxTRSksCiAgICBmbGFnX2Jvb2xlYW4oInN0YWNrX2xheWVycyIsIEZBTFNFKSwKICAgICMgc2hvdWxkIGJlIHVzZSBzZXZlcmFsIGxheWVycyBvZiBMU1RNCiAgICBmbGFnX2ludGVnZXIoImJhdGNoX3NpemUiLCAxMCksCiAgICAjIG51bWJlciBvZiBzYW1wbGVzIGZlZCB0byBtb2RlbCBpbiBvbmUgZ28KICAgIGZsYWdfaW50ZWdlcigibl90aW1lc3RlcHMiLCAxMiksCiAgICAjc2l6ZSBvZiB0aGUgaGlkZGVuIHN0YXRlLCBlcXVhbHMgc2l6ZSBvZiBwcmVkaWN0aW9ucwogICAgZmxhZ19pbnRlZ2VyKCJuX2Vwb2NocyIsIDEwMCksCiAgICAjaG93IG1hbnkgZXBvY2hzIHRvIHRyYWluIGZvcgogICAgZmxhZ19udW1lcmljKCJkcm9wb3V0IiwgMC4yKSwKICAgIGZsYWdfbnVtZXJpYygicmVjdXJyZW50X2Ryb3BvdXQiLCAwLjIpLAogICAgZmxhZ19zdHJpbmcoImxvc3MiLCAibG9nY29zaCIpLAogICAgI3RoaXMgcGFydGljdWxhciBtZXRob2Qgd29ya2VkIGJldHRlciBvbiB0aGUgdGFzawogICAgZmxhZ19zdHJpbmcoIm9wdGltaXplcl90eXBlIiwgInNnZCIpLAogICAgZmxhZ19pbnRlZ2VyKCJuX3VuaXRzIiwgMTI4KSwKICAgIGZsYWdfbnVtZXJpYygibHIiLCAwLjAwMyksCiAgICBmbGFnX251bWVyaWMoIm1vbWVudHVtIiwgMC45KSwKICAgIGZsYWdfaW50ZWdlcigicGF0aWVuY2UiLCAxMCkgI3BhcmFtZXRlciB0byB0aGUgZWFybHkgc3RvcHBpbmcgY2FsbGJhY2sKICAgIAogICAgKQogIAogIG5fcHJlZGljdGlvbnMgPC0gRkxBR1Mkbl90aW1lc3RlcHMKICBuX2ZlYXR1cmVzIDwtIDEKICBvcHRpbWl6ZXIgPC0gc3dpdGNoKEZMQUdTJG9wdGltaXplcl90eXBlLCAKICAgICAgICAgICAgICAgICAgICBzZ2QgPSBvcHRpbWl6ZXJfc2dkKGxyID0gRkxBR1MkbHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb21lbnR1bSA9IEZMQUdTJG1vbWVudHVtKSkKICBjYWxsYmFja3MgPC0gbGlzdChjYWxsYmFja19lYXJseV9zdG9wcGluZyhwYXRpZW5jZSA9IEZMQUdTJHBhdGllbmNlKSkKICAKICB0cmFpbl92YWxzIDwtIGRmX3Byb2Nlc3NlZF90bGIgJT4lCiAgZmlsdGVyKCkKICAKICAjZXh0cmFjdCB2YWx1ZXMgZnJvbSBkYXRhIGZyYW1lCiAgdHJhaW5fdmFscyA8LSBkZl9wcm9jZXNzZWRfdGxiICU+JQogIGZpbHRlcihrZXkgPT0gInRyYWluaW5nIikgJT4lCiAgc2VsZWN0KHZhbHVlKSAlPiUKICBkcGx5cjo6cHVsbCgpICNjb252ZXJ0cyB0byBhIHZlY3RvcgogIAogIHZhbGlkX3ZhbHMgPC0gZGZfcHJvY2Vzc2VkX3RsYiAlPiUKICBmaWx0ZXIoa2V5ID09ICJ2YWxpZGF0aW9uIikgJT4lCiAgc2VsZWN0KHZhbHVlKSAlPiUKICBkcGx5cjo6cHVsbCgpCiAgCiAgdGVzdF92YWxzIDwtIGRmX3Byb2Nlc3NlZF90bGIgJT4lCiAgZmlsdGVyKGtleSA9PSAidGVzdGluZyIpICU+JQogIHNlbGVjdCh2YWx1ZSkgJT4lCiAgZHBseXI6OnB1bGwoKQogIAogICNidWlsZCB0aGUgd2luZG93ZWQgbWF0cmljZXMKICB0cmFpbl9tYXRyaXggPC0KICBidWlsZF9tYXRyaXgodHJhaW5fdmFscywgbl90aW1lc3RlcHMgKyBuX3ByZWRpY3Rpb25zKQogIHZhbGlkX21hdHJpeCA8LQogIGJ1aWxkX21hdHJpeCh2YWxpZF92YWxzLCBuX3RpbWVzdGVwcyArIG5fcHJlZGljdGlvbnMpCiAgdGVzdF9tYXRyaXggPC0gYnVpbGRfbWF0cml4KHRlc3RfdmFscywgbl90aW1lc3RlcHMgKyBuX3ByZWRpY3Rpb25zKQogIAogICNzZXBhcmF0ZSBtYXRyaWNlcyBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHBhcnRzCiAgI2Rpc2NhcmQgbGFzdCBiYXRjaCwgaWYgdGhlcmUgZmV3ZXIgdGhhbiBiYXRjaF9zaXplIHNhbXBsZXMKICAKICBYX3RyYWluIDwtIHRyYWluX21hdHJpeFssIDE6bl90aW1lc3RlcHNdCiAgeV90cmFpbiA8LSB0cmFpbl9tYXRyaXhbLCAobl90aW1lc3RlcHMgKyAxKToobl90aW1lc3RlcHMgKiAyKV0KICBYX3RyYWluIDwtIFhfdHJhaW5bMToobnJvdyhYX3RyYWluKSAlLyUgYmF0Y2hfc2l6ZSAqIGJhdGNoX3NpemUpLF0KICB5X3RyYWluIDwtIFhfdHJhaW5bMToobnJvdyh5X3RyYWluKSAlLyUgYmF0Y2hfc2l6ZSAqIGJhdGNoX3NpemUpLF0KICAKICBYX3ZhbGlkIDwtIHZhbGlkX21hdHJpeFssIDE6bl90aW1lc3RlcHNdCiAgeV92YWxpZCA8LSB2YWxpZF9tYXRyaXhbLCAobl90aW1lc3RlcHMgKyAxKToobl90aW1lc3RlcHMgKiAyKV0KICBYX3ZhbGlkIDwtIFhfdmFsaWRbMToobnJvdyhYX3ZhbGlkKSAlLyUgYmF0Y2hfc2l6ZSAqIGJhdGNoX3NpemUpLF0KICB5X3ZhbGlkIDwtIFhfdmFsaWRbMToobnJvdyh5X3ZhbGlkKSAlLyUgYmF0Y2hfc2l6ZSAqIGJhdGNoX3NpemUpLF0KICAKICAKICBYX3Rlc3QgPC0gdGVzdF9tYXRyaXhbLCAxOm5fdGltZXN0ZXBzXQogIHlfdGVzdCA8LSB0ZXN0X21hdHJpeFssIChuX3RpbWVzdGVwcyArIDEpOihuX3RpbWVzdGVwcyAqIDIpXQogIFhfdGVzdCA8LSBYX3Rlc3RbMToobnJvdyhYX3Rlc3QpICUvJSBiYXRjaF9zaXplICogYmF0Y2hfc2l6ZSksXQogIHlfdGVzdCA8LSBYX3Rlc3RbMToobnJvdyh5X3Rlc3QpICUvJSBiYXRjaF9zaXplICogYmF0Y2hfc2l6ZSksXQogIAogICNhZGQgb24gdGhlIHJlcXVpcmVkIHRoaXJkIGF4aXMKICBYX3RyYWluIDwtIHJlc2hhcGVfWF8zZChYX3RyYWluKQogIFhfdmFsaWQgPC0gcmVzaGFwZV9YXzNkKFhfdmFsaWQpCiAgWF90ZXN0IDwtIHJlc2hhcGVfWF8zZChYX3Rlc3QpCiAgCiAgeV90cmFpbiA8LSByZXNoYXBlX1hfM2QoeV90cmFpbikKICB5X3ZhbGlkIDwtIHJlc2hhcGVfWF8zZCh5X3ZhbGlkKQogIHlfdGVzdCA8LSByZXNoYXBlX1hfM2QoeV90ZXN0KQogIAogIG1vZGVsIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKQoKICBtb2RlbCAlPiUgCiAgICBsYXllcl9sc3RtKAogICAgICB1bml0cyA8LSBGTEFHUyRuX3VuaXRzLAogICAgICBiYXRjaF9pbnB1dF9zaGFwZSA9IGMoRkxBR1MkYmF0Y2hfc2l6ZSwgRkxBR1Mkbl90aW1lc3RlcHMsIG5fZmVhdHVyZXMpLAogICAgICBkcm9wb3V0ID0gRkxBR1MkZHJvcG91dCwKICAgICAgcmVjdXJyZW50X2Ryb3BvdXQgPSBGTEFHUyRyZWN1cnJlbnRfZHJvcG91dCwgCiAgICAgIHJldHVybl9zZXF1ZW5jZXMgPSBUUlVFCiAgICApICU+JSB0aW1lX2Rpc3RyaWJ1dGVkKGxheWVyX2RlbnNlKHVuaXRzID0gMSkpCiAgCiAgCiAgbW9kZWwgJT4lIAogICAgY29tcGlsZSgKICAgICAgbG9zcyA9IEZMQUdTJGxvc3MsCiAgICAgIG9wdGltaXplciA9IG9wdGltaXplciwgCiAgICAgIG1ldHJpY3MgPSBsaXN0KCJtZWFuX3NxdWFyZWRfZXJyb3IiKQogICAgKQogIAogIG1vZGVsICU+JSBmaXQoCiAgICB4ID0gWF90cmFpbiwgCiAgICB5ID0geV90cmFpbiwgCiAgICB2YWxpZGF0aW9uX2RhdGEgPSBsaXN0KFhfdmFsaWQsIHlfdmFsaWQpLCAKICAgIGJhdGNoX3NpemUgPSBGTEFHUyRiYXRjaF9zaXplLAogICAgZXBvY2hzICAgICA9IEZMQUdTJG5fZXBvY2hzLAogICAgY2FsbGJhY2tzID0gY2FsbGJhY2tzCiAgKQogIAogIHByZWRfdHJhaW4gPC0gbW9kZWwgJT4lIAogIHByZWRpY3QoWF90cmFpbiwgYmF0Y2hfc2l6ZSA9IEZMQUdTJGJhdGNoX3NpemUpICU+JSAKICAuWywgLCAxXQoKICAjcmV0cmFuc2Zvcm0gdmFsdWVzIHRvIG9yaWdpbmFsIHNjYWxlCiAgcHJlZF90cmFpbiA8LSAocHJlZF90cmFpbiAqIHNjYWxlX2hpc3RvcnkgKyBjZW50ZXJfaGlzdG9yeSkgXiAyCiAgY29tcGFyZV90cmFpbiA8LSBkZiAlPiUgCiAgICBmaWx0ZXIoa2V5ID09ICJ0cmFpbmluZyIpCiAgCiAgI2J1aWxkIGEgZGF0YWZyYW1lIHRoYXQgaGFzIGJvdGggYWN0dWFsIGFuZCBwcmVkaWN0ZWQgdmFsdWVzCiAgZm9yKGkgaW4gMTpucm93KHByZWRfdHJhaW4pKSB7CiAgICB2YXJuYW1lIDwtIHBhc3RlMCgicHJlZF90cmFpbiIsIGkpCiAgICBjb21wYXJlX3RyYWluIDwtIAogICAgICBtdXRhdGUoY29tcGFyZV90cmFpbiwgISF2YXJuYW1lIDo9IGMoCiAgICAgICAgcmVwKE5BLCBGTEFHUyRuX3RpbWVzdGVwcyArIGkgLSAxKSwgCiAgICAgICAgcHJlZF90cmFpbltpLCBdLCAKICAgICAgICByZXAoTkEsIG5yb3coY29tcGFyZV90cmFpbikgLSBGTEFHUyRuX3RpbWVzdGVwcyAqIDIgLSBpICsgMSkKICAgICAgKSkKICB9CiAgCiAgcHJlZF90ZXN0IDwtIG1vZGVsICU+JSAKICBwcmVkaWN0KFhfdGVzdCwgYmF0Y2hfc2l6ZSA9IEZMQUdTJGJhdGNoX3NpemUpICU+JSAKICAuWywgLCAxXQoKI3JldHJhbnNmb3JtIHZhbHVlcyB0byBvcmlnaW5hbCBzY2FsZQpwcmVkX3Rlc3QgPC0gKHByZWRfdGVzdCAqIHNjYWxlX2hpc3RvcnkgKyBjZW50ZXJfaGlzdG9yeSkgXiAyCmNvbXBhcmVfdGVzdCA8LSBkZiAlPiUgCiAgZmlsdGVyKGtleSA9PSAidGVzdGluZyIpCgojYnVpbGQgYSBkYXRhZnJhbWUgdGhhdCBoYXMgYm90aCBhY3R1YWwgYW5kIHByZWRpY3RlZCB2YWx1ZXMKZm9yKGkgaW4gMTpucm93KHByZWRfdGVzdCkpIHsKICB2YXJuYW1lIDwtIHBhc3RlMCgicHJlZF90ZXN0IiwgaSkKICBjb21wYXJlX3Rlc3QgPC0gCiAgICBtdXRhdGUoY29tcGFyZV90ZXN0LCAhIXZhcm5hbWUgOj0gYygKICAgICAgcmVwKE5BLCBGTEFHUyRuX3RpbWVzdGVwcyArIGkgLSAxKSwgCiAgICAgIHByZWRfdGVzdFtpLCBdLCAKICAgICAgcmVwKE5BLCBucm93KGNvbXBhcmVfdGVzdCkgLSBGTEFHUyRuX3RpbWVzdGVwcyAqIDIgLSBpICsgMSkKICAgICkpCn0KbGlzdCh0cmFpbiA9IGNvbXBhcmVfdHJhaW4sIHRlc3QgPSBjb21wYXJlX3Rlc3QpCiAgCn0KYGBgCgoKTWFwcGluZyB0aGUgZnVuY3Rpb24gb3ZlciBhbGwgc3BsaXRzIHlpZWxkcyBhIGxpc3Qgb2YgYWxsIHByZWRpY3Rpb25zOgoKYGBge3IsIGVjaG8gPSBUUlVFLCByZXN1bHRzPSJoaWRlIn0KYWxsX3NwbGl0X3ByZWRzIDwtIHJvbGxpbmdfb3JpZ2luX3Jlc2FtcGxlcyAlPiUgCiAgbXV0YXRlKHByZWRpY3QgPSBtYXAoc3BsaXRzLCBvYnRhaW5fcHJlZGljdGlvbnMpKQpgYGAKCkNhbGN1bGF0ZSBSTVNFIG9uIGFsbCBzcGxpdHM6CgpgYGB7cn0KY2FsY19ybXNlIDwtIGZ1bmN0aW9uKGRmKSB7CiAgY29sbiA8LSBjb2xuYW1lcyhkZilbNDpuY29sKGRmKV0KICBjb2xzIDwtIG1hcChjb2xuLCBkcGx5cjo6cXVvKGRwbHlyOjpzeW0oLikpKQoKICBtYXBfZGJsKGNvbHMsIGZ1bmN0aW9uKGNvbCkKICAgIHlhcmRzdGljazo6cm1zZSgKICAgIGRmLAogICAgdHJ1dGggPSB2YWx1ZSwKICAgIGVzdGltYXRlID0gISFjb2wsCiAgICBuYS5ybSA9IFRSVUUKICAgICkpICU+JSBtZWFuKCkKfQoKYWxsX3NwbGl0X3ByZWRzIDwtIGFsbF9zcGxpdF9wcmVkcyAlPiUgdW5uZXN0KHByZWRpY3QpCmFsbF9zbGl0c19wcmVkc190cmFpbiA8LSBhbGxfc3BsaXRfcHJlZHNbc2VxKDEsIDExLCBieSA9IDIpLCBdCmFsbF9zbGl0c19wcmVkc190ZXN0IDwtIGFsbF9zcGxpdF9wcmVkc1tzZXEoMiwgMTIsIGJ5ID0gMiksIF0KCmFsbF9zcGxpdF9ybXNlc190cmFpbiA8LSBhbGxfc2xpdHNfcHJlZHNfdHJhaW4gJT4lIAogIG11dGF0ZShybXNlID0gbWFwX2RibChwcmVkaWN0LCBjYWxjX3Jtc2UpKSAlPiUgCiAgc2VsZWN0KGlkLCBybXNlKQoKYWxsX3NwbGl0X3Jtc2VzX3Rlc3QgPC0gYWxsX3NsaXRzX3ByZWRzX3Rlc3QgJT4lIAogIG11dGF0ZShybXNlID0gbWFwX2RibChwcmVkaWN0LCBjYWxjX3Jtc2UpKSAlPiUgCiAgc2VsZWN0KGlkLCBybXNlKQpgYGAKCkhlcmUgaG93IGl0IGxvb2tzIC0gUk1TRSBvbiB0aGUgdHJhaW5pbmcgc2V0IGZvciB0aGUgNiBzcGxpdHM6CgpgYGB7cn0KYWxsX3NwbGl0X3Jtc2VzX3RyYWluCmFsbF9zcGxpdF9ybXNlc190ZXN0CmBgYAoKTGV0J3MgcG9zdCBhbGwgcHJlZGljdGlvbnMgb24gdHJhaW5pbmcgc2V0czoKCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQojY29uc3RydWN0IGZ1bmN0aW9ucwpwbG90X3RyYWluIDwtIGZ1bmN0aW9uKHNsaWNlLCBuYW1lKSB7CiAgc2xpY2UgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBpbmRleCwgeSA9IHZhbHVlKSkgKwogICAgZ2VvbV9saW5lKCkgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90cmFpbjEpLCBjb2xvciA9ICJjeWFuIikgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90cmFpbjUwKSwgY29sb3IgPSAicmVkIikgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90cmFpbjEwMCksIGNvbG9yID0gImdyZWVuIikgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90cmFpbjE1MCksIGNvbG9yID0gInZpb2xldCIpICsKICAgIGdlb21fbGluZShhZXMoeSA9IHByZWRfdHJhaW4yMDApLCBjb2xvciA9ICJjeWFuIikgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90cmFpbjI1MCksIGNvbG9yID0gInJlZCIpICsKICAgIGdlb21fbGluZShhZXMoeSA9IHByZWRfdHJhaW4zMDApLCBjb2xvciA9ICJncmVlbiIpICsKICAgIGdlb21fbGluZShhZXMoeSA9IHByZWRfdHJhaW4zNTApLCBjb2xvciA9ICJ2aW9sZXQiKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3RyYWluNDAwKSwgY29sb3IgPSAiY3lhbiIpICsKICAgIGdlb21fbGluZShhZXMoeSA9IHByZWRfdHJhaW40NTApLCBjb2xvciA9ICJyZWQiKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3RyYWluNTAwKSwgY29sb3IgPSAiZ3JlZW4iKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3RyYWluNTUwKSwgY29sb3IgPSAidmlvbGV0IikgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90cmFpbjYwMCksIGNvbG9yID0gImN5YW4iKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3RyYWluNjUwKSwgY29sb3IgPSAicmVkIikgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90cmFpbjcwMCksIGNvbG9yID0gImdyZWVuIikgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90cmFpbjc1MCksIGNvbG9yID0gInZpb2xldCIpICsKICAgIGdndGl0bGUobmFtZSkKfSAKCnBsb3RfdGVzdCA8LSBmdW5jdGlvbihzbGljZSwgbmFtZSkgewogIHNsaWNlICU+JQogICAgZ2dwbG90KGFlcyh4ID0gaW5kZXgsIHkgPSB2YWx1ZSkpICsKICAgIGdlb21fbGluZSgpICsKICAgIGdlb21fbGluZShhZXMoeSA9IHByZWRfdGVzdDEpLCBjb2xvciA9ICJjeWFuIikgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gcHJlZF90ZXN0NTApLCBjb2xvciA9ICJyZWQiKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3Rlc3QxMDApLCBjb2xvciA9ICJncmVlbiIpICsKICAgIGdlb21fbGluZShhZXMoeSA9IHByZWRfdGVzdDE1MCksIGNvbG9yID0gInZpb2xldCIpICsKICAgIGdlb21fbGluZShhZXMoeSA9IHByZWRfdGVzdDIwMCksIGNvbG9yID0gImN5YW4iKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3Rlc3QyNTApLCBjb2xvciA9ICJyZWQiKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3Rlc3QzMDApLCBjb2xvciA9ICJncmVlbiIpICsKICAgIGdlb21fbGluZShhZXMoeSA9IHByZWRfdGVzdDM1MCksIGNvbG9yID0gInZpb2xldCIpICsKICAgIGdlb21fbGluZShhZXMoeSA9IHByZWRfdGVzdDQwMCksIGNvbG9yID0gImN5YW4iKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3Rlc3Q0NTApLCBjb2xvciA9ICJyZWQiKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkX3Rlc3Q1MDApLCBjb2xvciA9ICJncmVlbiIpICsKICAgIGdlb21fbGluZShhZXMoeSA9IHByZWRfdGVzdDU1MCksIGNvbG9yID0gInZpb2xldCIpICsKICAgIGdndGl0bGUobmFtZSkKfQoKdHJhaW5fcGxvdHMgPC0gbWFwMihhbGxfc2xpdHNfcHJlZHNfdHJhaW4kcHJlZGljdCwgYWxsX3NwbGl0X3Jtc2VzX3RyYWluJGlkLCBwbG90X3RyYWluKQpwX2JvZHlfdHJhaW4gPC0gY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gdHJhaW5fcGxvdHMsIG5jb2wgPSAzKQpwX3RpdGxlX3RyYWluIDwtIGNvd3Bsb3Q6OmdnZHJhdygpICsgCiAgZHJhd19sYWJlbCgiQmFja3Rlc3RlZCBQcmVkaWN0aW9uczogVHJhaW5pbmcgU2V0cyIsIHNpemUgPSAxOCwgZm9udGZhY2UgPSAiYm9sZCIpCgpwbG90X2dyaWQocF90aXRsZV90cmFpbiwgcF9ib2R5X3RyYWluLCBuY29sID0gMSwgcmVsX2hlaWdodHMgPSBjKDAuMDUsIDEsIDAuMDUpKQpgYGAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQp0ZXN0X3Bsb3RzIDwtIG1hcDIoYWxsX3NsaXRzX3ByZWRzX3Rlc3QkcHJlZGljdCwgYWxsX3NwbGl0X3Jtc2VzX3Rlc3QkaWQsIHBsb3RfdGVzdCkKcF9ib2R5X3Rlc3QgPC0gY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gdGVzdF9wbG90cywgbmNvbCA9IDMpCnBfdGl0bGVfdGVzdCA8LSBjb3dwbG90OjpnZ2RyYXcoKSArIAogIGRyYXdfbGFiZWwoIkJhY2t0ZXN0ZWQgUHJlZGljdGlvbnM6IFRlc3QgU2V0cyIsIHNpemUgPSAxOCwgZm9udGZhY2UgPSAiYm9sZCIpCgoKcGxvdF9ncmlkKHBfdGl0bGVfdGVzdCwgcF9ib2R5X3Rlc3QsIG5jb2wgPSAxLCByZWxfaGVpZ2h0cyA9IGMoMC4wNSwgMSwgMC4wNSkpCiAKYGBgCgo=